From be8c6d86d558860bbc493ad3fbc51fc96ab0c2e4 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Thu, 19 May 2022 17:05:06 +0200 Subject: [PATCH 0001/2002] net: mac802154: Rename the synchronous xmit worker There are currently two driver hooks: one is synchronous, the other is not. We cannot rely on driver implementations to provide a synchronous API (which is related to the bus medium more than a wish to have a synchronized implementation) so we are going to introduce a sync API above any kind of driver transmit function. In order to clarify what this worker is for (synchronous driver implementation), let's rename it so that people don't get bothered by the fact that their driver does not make use of the "xmit worker" which is a too generic name. Signed-off-by: Miquel Raynal Acked-by: Alexander Aring Link: https://lore.kernel.org/r/20220519150516.443078-2-miquel.raynal@bootlin.com Signed-off-by: Stefan Schmidt --- net/mac802154/ieee802154_i.h | 2 +- net/mac802154/main.c | 2 +- net/mac802154/tx.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/net/mac802154/ieee802154_i.h b/net/mac802154/ieee802154_i.h index 1381e6a5e180..d7632c6d225f 100644 --- a/net/mac802154/ieee802154_i.h +++ b/net/mac802154/ieee802154_i.h @@ -123,7 +123,7 @@ ieee802154_sdata_running(struct ieee802154_sub_if_data *sdata) extern struct ieee802154_mlme_ops mac802154_mlme_wpan; void ieee802154_rx(struct ieee802154_local *local, struct sk_buff *skb); -void ieee802154_xmit_worker(struct work_struct *work); +void ieee802154_xmit_sync_worker(struct work_struct *work); netdev_tx_t ieee802154_monitor_start_xmit(struct sk_buff *skb, struct net_device *dev); netdev_tx_t diff --git a/net/mac802154/main.c b/net/mac802154/main.c index bd7bdb1219dd..392771bba9dd 100644 --- a/net/mac802154/main.c +++ b/net/mac802154/main.c @@ -95,7 +95,7 @@ ieee802154_alloc_hw(size_t priv_data_len, const struct ieee802154_ops *ops) skb_queue_head_init(&local->skb_queue); - INIT_WORK(&local->tx_work, ieee802154_xmit_worker); + INIT_WORK(&local->tx_work, ieee802154_xmit_sync_worker); /* init supported flags with 802.15.4 default ranges */ phy->supported.max_minbe = 8; diff --git a/net/mac802154/tx.c b/net/mac802154/tx.c index c829e4a75325..97df5985b830 100644 --- a/net/mac802154/tx.c +++ b/net/mac802154/tx.c @@ -22,7 +22,7 @@ #include "ieee802154_i.h" #include "driver-ops.h" -void ieee802154_xmit_worker(struct work_struct *work) +void ieee802154_xmit_sync_worker(struct work_struct *work) { struct ieee802154_local *local = container_of(work, struct ieee802154_local, tx_work); From 983a974b40f66d202b075ab8ac584c698a3fc141 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Thu, 19 May 2022 17:05:07 +0200 Subject: [PATCH 0002/2002] net: mac802154: Rename the main tx_work struct This entry is dedicated to synchronous transmissions done by drivers without async hook. Make this clearer that this is not a work that any driver can use by at least prefixing it with "sync_". While at it, let's enhance the comment explaining why we choose one or the other. Signed-off-by: Miquel Raynal Acked-by: Alexander Aring Link: https://lore.kernel.org/r/20220519150516.443078-3-miquel.raynal@bootlin.com Signed-off-by: Stefan Schmidt --- net/mac802154/ieee802154_i.h | 2 +- net/mac802154/main.c | 2 +- net/mac802154/tx.c | 9 ++++++--- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/net/mac802154/ieee802154_i.h b/net/mac802154/ieee802154_i.h index d7632c6d225f..a8b7b9049f14 100644 --- a/net/mac802154/ieee802154_i.h +++ b/net/mac802154/ieee802154_i.h @@ -55,7 +55,7 @@ struct ieee802154_local { struct sk_buff_head skb_queue; struct sk_buff *tx_skb; - struct work_struct tx_work; + struct work_struct sync_tx_work; /* A negative Linux error code or a null/positive MLME error status */ int tx_result; }; diff --git a/net/mac802154/main.c b/net/mac802154/main.c index 392771bba9dd..40fab08df24b 100644 --- a/net/mac802154/main.c +++ b/net/mac802154/main.c @@ -95,7 +95,7 @@ ieee802154_alloc_hw(size_t priv_data_len, const struct ieee802154_ops *ops) skb_queue_head_init(&local->skb_queue); - INIT_WORK(&local->tx_work, ieee802154_xmit_sync_worker); + INIT_WORK(&local->sync_tx_work, ieee802154_xmit_sync_worker); /* init supported flags with 802.15.4 default ranges */ phy->supported.max_minbe = 8; diff --git a/net/mac802154/tx.c b/net/mac802154/tx.c index 97df5985b830..a01689ddd547 100644 --- a/net/mac802154/tx.c +++ b/net/mac802154/tx.c @@ -25,7 +25,7 @@ void ieee802154_xmit_sync_worker(struct work_struct *work) { struct ieee802154_local *local = - container_of(work, struct ieee802154_local, tx_work); + container_of(work, struct ieee802154_local, sync_tx_work); struct sk_buff *skb = local->tx_skb; struct net_device *dev = skb->dev; int res; @@ -76,7 +76,10 @@ ieee802154_tx(struct ieee802154_local *local, struct sk_buff *skb) /* Stop the netif queue on each sub_if_data object. */ ieee802154_stop_queue(&local->hw); - /* async is priority, otherwise sync is fallback */ + /* Drivers should preferably implement the async callback. In some rare + * cases they only provide a sync callback which we will use as a + * fallback. + */ if (local->ops->xmit_async) { unsigned int len = skb->len; @@ -90,7 +93,7 @@ ieee802154_tx(struct ieee802154_local *local, struct sk_buff *skb) dev->stats.tx_bytes += len; } else { local->tx_skb = skb; - queue_work(local->workqueue, &local->tx_work); + queue_work(local->workqueue, &local->sync_tx_work); } return NETDEV_TX_OK; From d08d951a9ae7e30fc0421a1af2b4193ce4aa6b3e Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Thu, 19 May 2022 17:05:08 +0200 Subject: [PATCH 0003/2002] net: mac802154: Enhance the error path in the main tx helper Before adding more logic in the error path, let's move the wake queue call there, rename the default label and create an additional one. There is no functional change. Signed-off-by: Miquel Raynal Acked-by: Alexander Aring Link: https://lore.kernel.org/r/20220519150516.443078-4-miquel.raynal@bootlin.com Signed-off-by: Stefan Schmidt --- net/mac802154/tx.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/net/mac802154/tx.c b/net/mac802154/tx.c index a01689ddd547..4a46ce8d2ac8 100644 --- a/net/mac802154/tx.c +++ b/net/mac802154/tx.c @@ -65,7 +65,7 @@ ieee802154_tx(struct ieee802154_local *local, struct sk_buff *skb) consume_skb(skb); skb = nskb; } else { - goto err_tx; + goto err_free_skb; } } @@ -84,10 +84,8 @@ ieee802154_tx(struct ieee802154_local *local, struct sk_buff *skb) unsigned int len = skb->len; ret = drv_xmit_async(local, skb); - if (ret) { - ieee802154_wake_queue(&local->hw); - goto err_tx; - } + if (ret) + goto err_wake_netif_queue; dev->stats.tx_packets++; dev->stats.tx_bytes += len; @@ -98,7 +96,9 @@ ieee802154_tx(struct ieee802154_local *local, struct sk_buff *skb) return NETDEV_TX_OK; -err_tx: +err_wake_netif_queue: + ieee802154_wake_queue(&local->hw); +err_free_skb: kfree_skb(skb); return NETDEV_TX_OK; } From bde000ae459f2829ed88e967f7fa7665b4e3afaf Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Thu, 19 May 2022 17:05:09 +0200 Subject: [PATCH 0004/2002] net: mac802154: Follow the count of ongoing transmissions In order to create a synchronous API for MLME command purposes, we need to be able to track the end of the ongoing transmissions. Let's introduce an atomic variable which is incremented when a transmission starts and decremented when relevant so that we know at any moment whether there is an ongoing transmission. The counter gets decremented in the following situations: - The operation is asynchronous and there was a failure during the offloading process. - The operation is synchronous and the synchronous operation failed. - The operation finished, either successfully or not. Signed-off-by: Miquel Raynal Acked-by: Alexander Aring Link: https://lore.kernel.org/r/20220519150516.443078-5-miquel.raynal@bootlin.com Signed-off-by: Stefan Schmidt --- include/net/cfg802154.h | 3 +++ net/mac802154/tx.c | 3 +++ net/mac802154/util.c | 2 ++ 3 files changed, 8 insertions(+) diff --git a/include/net/cfg802154.h b/include/net/cfg802154.h index d8d8719315fd..678ff00c7d70 100644 --- a/include/net/cfg802154.h +++ b/include/net/cfg802154.h @@ -214,6 +214,9 @@ struct wpan_phy { /* the network namespace this phy lives in currently */ possible_net_t _net; + /* Transmission monitoring */ + atomic_t ongoing_txs; + char priv[] __aligned(NETDEV_ALIGN); }; diff --git a/net/mac802154/tx.c b/net/mac802154/tx.c index 4a46ce8d2ac8..33f64ecd96c7 100644 --- a/net/mac802154/tx.c +++ b/net/mac802154/tx.c @@ -44,6 +44,7 @@ void ieee802154_xmit_sync_worker(struct work_struct *work) err_tx: /* Restart the netif queue on each sub_if_data object. */ ieee802154_wake_queue(&local->hw); + atomic_dec(&local->phy->ongoing_txs); kfree_skb(skb); netdev_dbg(dev, "transmission failed\n"); } @@ -75,6 +76,7 @@ ieee802154_tx(struct ieee802154_local *local, struct sk_buff *skb) /* Stop the netif queue on each sub_if_data object. */ ieee802154_stop_queue(&local->hw); + atomic_inc(&local->phy->ongoing_txs); /* Drivers should preferably implement the async callback. In some rare * cases they only provide a sync callback which we will use as a @@ -98,6 +100,7 @@ ieee802154_tx(struct ieee802154_local *local, struct sk_buff *skb) err_wake_netif_queue: ieee802154_wake_queue(&local->hw); + atomic_dec(&local->phy->ongoing_txs); err_free_skb: kfree_skb(skb); return NETDEV_TX_OK; diff --git a/net/mac802154/util.c b/net/mac802154/util.c index 9f024d85563b..76dc663e2af4 100644 --- a/net/mac802154/util.c +++ b/net/mac802154/util.c @@ -88,6 +88,7 @@ void ieee802154_xmit_complete(struct ieee802154_hw *hw, struct sk_buff *skb, } dev_consume_skb_any(skb); + atomic_dec(&hw->phy->ongoing_txs); } EXPORT_SYMBOL(ieee802154_xmit_complete); @@ -99,6 +100,7 @@ void ieee802154_xmit_error(struct ieee802154_hw *hw, struct sk_buff *skb, local->tx_result = reason; ieee802154_wake_queue(hw); dev_kfree_skb_any(skb); + atomic_dec(&hw->phy->ongoing_txs); } EXPORT_SYMBOL(ieee802154_xmit_error); From 20a19d1df3e4079cbaa045ec89bbefb831d4705d Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Thu, 19 May 2022 17:05:10 +0200 Subject: [PATCH 0005/2002] net: mac802154: Bring the ability to hold the transmit queue Create a hold_txs atomic variable and increment/decrement it when relevant, ie. when we want to hold the queue or release it: currently all the "stopped" situations are suitable, but very soon we will more extensively use this feature for MLME purposes. Upon release, the atomic counter is decremented and checked. If it is back to 0, then the netif queue gets woken up. This makes the whole process fully transparent, provided that all the users of ieee802154_wake/stop_queue() now call ieee802154_hold/release_queue() instead. In no situation individual drivers should call any of these helpers manually in order to avoid messing with the counters. There are other functions more suited for this purpose which have been introduced, such as the _xmit_complete() and _xmit_error() helpers which will handle all that for them. One advantage is that, as no more drivers call the stop/wake helpers directly, we can safely stop exporting them and only declare the hold/release ones in a header only accessible to the core. Signed-off-by: Miquel Raynal Acked-by: Alexander Aring Link: https://lore.kernel.org/r/20220519150516.443078-6-miquel.raynal@bootlin.com Signed-off-by: Stefan Schmidt --- include/net/cfg802154.h | 6 +++-- include/net/mac802154.h | 27 ------------------- net/ieee802154/core.c | 2 ++ net/mac802154/cfg.c | 4 +-- net/mac802154/ieee802154_i.h | 19 +++++++++++++ net/mac802154/tx.c | 6 ++--- net/mac802154/util.c | 52 +++++++++++++++++++++++++++++++----- 7 files changed, 75 insertions(+), 41 deletions(-) diff --git a/include/net/cfg802154.h b/include/net/cfg802154.h index 678ff00c7d70..e87f1b07f20f 100644 --- a/include/net/cfg802154.h +++ b/include/net/cfg802154.h @@ -11,7 +11,7 @@ #include #include -#include +#include #include #include @@ -214,8 +214,10 @@ struct wpan_phy { /* the network namespace this phy lives in currently */ possible_net_t _net; - /* Transmission monitoring */ + /* Transmission monitoring and control */ + spinlock_t queue_lock; atomic_t ongoing_txs; + atomic_t hold_txs; char priv[] __aligned(NETDEV_ALIGN); }; diff --git a/include/net/mac802154.h b/include/net/mac802154.h index bdac0ddbdcdb..357d25ef627a 100644 --- a/include/net/mac802154.h +++ b/include/net/mac802154.h @@ -460,33 +460,6 @@ void ieee802154_unregister_hw(struct ieee802154_hw *hw); */ void ieee802154_rx_irqsafe(struct ieee802154_hw *hw, struct sk_buff *skb, u8 lqi); -/** - * ieee802154_wake_queue - wake ieee802154 queue - * @hw: pointer as obtained from ieee802154_alloc_hw(). - * - * Tranceivers usually have either one transmit framebuffer or one framebuffer - * for both transmitting and receiving. Hence, the core currently only handles - * one frame at a time for each phy, which means we had to stop the queue to - * avoid new skb to come during the transmission. The queue then needs to be - * woken up after the operation. - * - * Drivers should use this function instead of netif_wake_queue. - */ -void ieee802154_wake_queue(struct ieee802154_hw *hw); - -/** - * ieee802154_stop_queue - stop ieee802154 queue - * @hw: pointer as obtained from ieee802154_alloc_hw(). - * - * Tranceivers usually have either one transmit framebuffer or one framebuffer - * for both transmitting and receiving. Hence, the core currently only handles - * one frame at a time for each phy, which means we need to tell upper layers to - * stop giving us new skbs while we are busy with the transmitted one. The queue - * must then be stopped before transmitting. - * - * Drivers should use this function instead of netif_stop_queue. - */ -void ieee802154_stop_queue(struct ieee802154_hw *hw); /** * ieee802154_xmit_complete - frame transmission complete diff --git a/net/ieee802154/core.c b/net/ieee802154/core.c index de259b5170ab..47a4de6df88b 100644 --- a/net/ieee802154/core.c +++ b/net/ieee802154/core.c @@ -130,6 +130,8 @@ wpan_phy_new(const struct cfg802154_ops *ops, size_t priv_size) init_waitqueue_head(&rdev->dev_wait); + spin_lock_init(&rdev->wpan_phy.queue_lock); + return &rdev->wpan_phy; } EXPORT_SYMBOL(wpan_phy_new); diff --git a/net/mac802154/cfg.c b/net/mac802154/cfg.c index 1e4a9f74ed43..b51100fd9e3f 100644 --- a/net/mac802154/cfg.c +++ b/net/mac802154/cfg.c @@ -46,7 +46,7 @@ static int ieee802154_suspend(struct wpan_phy *wpan_phy) if (!local->open_count) goto suspend; - ieee802154_stop_queue(&local->hw); + ieee802154_hold_queue(local); synchronize_net(); /* stop hardware - this must stop RX */ @@ -72,7 +72,7 @@ static int ieee802154_resume(struct wpan_phy *wpan_phy) return ret; wake_up: - ieee802154_wake_queue(&local->hw); + ieee802154_release_queue(local); local->suspended = false; return 0; } diff --git a/net/mac802154/ieee802154_i.h b/net/mac802154/ieee802154_i.h index a8b7b9049f14..0c7ff9e0b632 100644 --- a/net/mac802154/ieee802154_i.h +++ b/net/mac802154/ieee802154_i.h @@ -130,6 +130,25 @@ netdev_tx_t ieee802154_subif_start_xmit(struct sk_buff *skb, struct net_device *dev); enum hrtimer_restart ieee802154_xmit_ifs_timer(struct hrtimer *timer); +/** + * ieee802154_hold_queue - hold ieee802154 queue + * @local: main mac object + * + * Hold a queue by incrementing an atomic counter and requesting the netif + * queues to be stopped. The queues cannot be woken up while the counter has not + * been reset with as any ieee802154_release_queue() calls as needed. + */ +void ieee802154_hold_queue(struct ieee802154_local *local); + +/** + * ieee802154_release_queue - release ieee802154 queue + * @local: main mac object + * + * Release a queue which is held by decrementing an atomic counter and wake it + * up only if the counter reaches 0. + */ +void ieee802154_release_queue(struct ieee802154_local *local); + /* MIB callbacks */ void mac802154_dev_set_page_channel(struct net_device *dev, u8 page, u8 chan); diff --git a/net/mac802154/tx.c b/net/mac802154/tx.c index 33f64ecd96c7..6a53c83cf039 100644 --- a/net/mac802154/tx.c +++ b/net/mac802154/tx.c @@ -43,7 +43,7 @@ void ieee802154_xmit_sync_worker(struct work_struct *work) err_tx: /* Restart the netif queue on each sub_if_data object. */ - ieee802154_wake_queue(&local->hw); + ieee802154_release_queue(local); atomic_dec(&local->phy->ongoing_txs); kfree_skb(skb); netdev_dbg(dev, "transmission failed\n"); @@ -75,7 +75,7 @@ ieee802154_tx(struct ieee802154_local *local, struct sk_buff *skb) } /* Stop the netif queue on each sub_if_data object. */ - ieee802154_stop_queue(&local->hw); + ieee802154_hold_queue(local); atomic_inc(&local->phy->ongoing_txs); /* Drivers should preferably implement the async callback. In some rare @@ -99,7 +99,7 @@ ieee802154_tx(struct ieee802154_local *local, struct sk_buff *skb) return NETDEV_TX_OK; err_wake_netif_queue: - ieee802154_wake_queue(&local->hw); + ieee802154_release_queue(local); atomic_dec(&local->phy->ongoing_txs); err_free_skb: kfree_skb(skb); diff --git a/net/mac802154/util.c b/net/mac802154/util.c index 76dc663e2af4..0ed8b5bcbe8a 100644 --- a/net/mac802154/util.c +++ b/net/mac802154/util.c @@ -13,7 +13,17 @@ /* privid for wpan_phys to determine whether they belong to us or not */ const void *const mac802154_wpan_phy_privid = &mac802154_wpan_phy_privid; -void ieee802154_wake_queue(struct ieee802154_hw *hw) +/** + * ieee802154_wake_queue - wake ieee802154 queue + * @local: main mac object + * + * Tranceivers usually have either one transmit framebuffer or one framebuffer + * for both transmitting and receiving. Hence, the core currently only handles + * one frame at a time for each phy, which means we had to stop the queue to + * avoid new skb to come during the transmission. The queue then needs to be + * woken up after the operation. + */ +static void ieee802154_wake_queue(struct ieee802154_hw *hw) { struct ieee802154_local *local = hw_to_local(hw); struct ieee802154_sub_if_data *sdata; @@ -27,9 +37,18 @@ void ieee802154_wake_queue(struct ieee802154_hw *hw) } rcu_read_unlock(); } -EXPORT_SYMBOL(ieee802154_wake_queue); -void ieee802154_stop_queue(struct ieee802154_hw *hw) +/** + * ieee802154_stop_queue - stop ieee802154 queue + * @local: main mac object + * + * Tranceivers usually have either one transmit framebuffer or one framebuffer + * for both transmitting and receiving. Hence, the core currently only handles + * one frame at a time for each phy, which means we need to tell upper layers to + * stop giving us new skbs while we are busy with the transmitted one. The queue + * must then be stopped before transmitting. + */ +static void ieee802154_stop_queue(struct ieee802154_hw *hw) { struct ieee802154_local *local = hw_to_local(hw); struct ieee802154_sub_if_data *sdata; @@ -43,14 +62,33 @@ void ieee802154_stop_queue(struct ieee802154_hw *hw) } rcu_read_unlock(); } -EXPORT_SYMBOL(ieee802154_stop_queue); + +void ieee802154_hold_queue(struct ieee802154_local *local) +{ + unsigned long flags; + + spin_lock_irqsave(&local->phy->queue_lock, flags); + if (!atomic_fetch_inc(&local->phy->hold_txs)) + ieee802154_stop_queue(&local->hw); + spin_unlock_irqrestore(&local->phy->queue_lock, flags); +} + +void ieee802154_release_queue(struct ieee802154_local *local) +{ + unsigned long flags; + + spin_lock_irqsave(&local->phy->queue_lock, flags); + if (!atomic_dec_and_test(&local->phy->hold_txs)) + ieee802154_wake_queue(&local->hw); + spin_unlock_irqrestore(&local->phy->queue_lock, flags); +} enum hrtimer_restart ieee802154_xmit_ifs_timer(struct hrtimer *timer) { struct ieee802154_local *local = container_of(timer, struct ieee802154_local, ifs_timer); - ieee802154_wake_queue(&local->hw); + ieee802154_release_queue(local); return HRTIMER_NORESTART; } @@ -84,7 +122,7 @@ void ieee802154_xmit_complete(struct ieee802154_hw *hw, struct sk_buff *skb, hw->phy->sifs_period * NSEC_PER_USEC, HRTIMER_MODE_REL); } else { - ieee802154_wake_queue(hw); + ieee802154_release_queue(local); } dev_consume_skb_any(skb); @@ -98,7 +136,7 @@ void ieee802154_xmit_error(struct ieee802154_hw *hw, struct sk_buff *skb, struct ieee802154_local *local = hw_to_local(hw); local->tx_result = reason; - ieee802154_wake_queue(hw); + ieee802154_release_queue(local); dev_kfree_skb_any(skb); atomic_dec(&hw->phy->ongoing_txs); } From 226730e1aa2844b6e5499a6fe6bea4db17a894a8 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Thu, 19 May 2022 17:05:11 +0200 Subject: [PATCH 0006/2002] net: mac802154: Create a hot tx path Let's rename the current Tx path to show that this is the "hot" Tx path. We will soon introduce a slower Tx path for MLME commands. Signed-off-by: Miquel Raynal Acked-by: Alexander Aring Link: https://lore.kernel.org/r/20220519150516.443078-7-miquel.raynal@bootlin.com Signed-off-by: Stefan Schmidt --- net/mac802154/tx.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/net/mac802154/tx.c b/net/mac802154/tx.c index 6a53c83cf039..607019b8f8ab 100644 --- a/net/mac802154/tx.c +++ b/net/mac802154/tx.c @@ -106,6 +106,12 @@ err_free_skb: return NETDEV_TX_OK; } +static netdev_tx_t +ieee802154_hot_tx(struct ieee802154_local *local, struct sk_buff *skb) +{ + return ieee802154_tx(local, skb); +} + netdev_tx_t ieee802154_monitor_start_xmit(struct sk_buff *skb, struct net_device *dev) { @@ -113,7 +119,7 @@ ieee802154_monitor_start_xmit(struct sk_buff *skb, struct net_device *dev) skb->skb_iif = dev->ifindex; - return ieee802154_tx(sdata->local, skb); + return ieee802154_hot_tx(sdata->local, skb); } netdev_tx_t @@ -135,5 +141,5 @@ ieee802154_subif_start_xmit(struct sk_buff *skb, struct net_device *dev) skb->skb_iif = dev->ifindex; - return ieee802154_tx(sdata->local, skb); + return ieee802154_hot_tx(sdata->local, skb); } From a40612f399eabe23424acf5985643a35d4f2832e Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Thu, 19 May 2022 17:05:12 +0200 Subject: [PATCH 0007/2002] net: mac802154: Introduce a helper to disable the queue Sometimes calling the stop queue helper is not enough because it does not hold any lock. In order to be safe and avoid racy situations when trying to (soon) sync the Tx queue, for instance before sending an MLME frame, let's now introduce an helper which actually hold the necessary locks when doing so. Suggested-by: Alexander Aring Signed-off-by: Miquel Raynal Acked-by: Alexander Aring Link: https://lore.kernel.org/r/20220519150516.443078-8-miquel.raynal@bootlin.com Signed-off-by: Stefan Schmidt --- net/mac802154/ieee802154_i.h | 12 ++++++++++++ net/mac802154/util.c | 14 ++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/net/mac802154/ieee802154_i.h b/net/mac802154/ieee802154_i.h index 0c7ff9e0b632..e34db1d49ef4 100644 --- a/net/mac802154/ieee802154_i.h +++ b/net/mac802154/ieee802154_i.h @@ -149,6 +149,18 @@ void ieee802154_hold_queue(struct ieee802154_local *local); */ void ieee802154_release_queue(struct ieee802154_local *local); +/** + * ieee802154_disable_queue - disable ieee802154 queue + * @local: main mac object + * + * When trying to sync the Tx queue, we cannot just stop the queue + * (which is basically a bit being set without proper lock handling) + * because it would be racy. We actually need to call netif_tx_disable() + * instead, which is done by this helper. Restarting the queue can + * however still be done with a regular wake call. + */ +void ieee802154_disable_queue(struct ieee802154_local *local); + /* MIB callbacks */ void mac802154_dev_set_page_channel(struct net_device *dev, u8 page, u8 chan); diff --git a/net/mac802154/util.c b/net/mac802154/util.c index 0ed8b5bcbe8a..999534f64485 100644 --- a/net/mac802154/util.c +++ b/net/mac802154/util.c @@ -83,6 +83,20 @@ void ieee802154_release_queue(struct ieee802154_local *local) spin_unlock_irqrestore(&local->phy->queue_lock, flags); } +void ieee802154_disable_queue(struct ieee802154_local *local) +{ + struct ieee802154_sub_if_data *sdata; + + rcu_read_lock(); + list_for_each_entry_rcu(sdata, &local->interfaces, list) { + if (!sdata->dev) + continue; + + netif_tx_disable(sdata->dev); + } + rcu_read_unlock(); +} + enum hrtimer_restart ieee802154_xmit_ifs_timer(struct hrtimer *timer) { struct ieee802154_local *local = From f0feb34904735ffa21fe7b0c50f9f9527ec74b7a Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Thu, 19 May 2022 17:05:13 +0200 Subject: [PATCH 0008/2002] net: mac802154: Introduce a tx queue flushing mechanism Right now we are able to stop a queue but we have no indication if a transmission is ongoing or not. Thanks to recent additions, we can track the number of ongoing transmissions so we know if the last transmission is over. Adding on top of it an internal wait queue also allows to be woken up asynchronously when this happens. If, beforehands, we marked the queue to be held and stopped it, we end up flushing and stopping the tx queue. Thanks to this feature, we will soon be able to introduce a synchronous transmit API. Signed-off-by: Miquel Raynal Acked-by: Alexander Aring Link: https://lore.kernel.org/r/20220519150516.443078-9-miquel.raynal@bootlin.com Signed-off-by: Stefan Schmidt --- include/net/cfg802154.h | 1 + net/ieee802154/core.c | 1 + net/mac802154/cfg.c | 2 +- net/mac802154/ieee802154_i.h | 1 + net/mac802154/tx.c | 26 ++++++++++++++++++++++++-- net/mac802154/util.c | 6 ++++-- 6 files changed, 32 insertions(+), 5 deletions(-) diff --git a/include/net/cfg802154.h b/include/net/cfg802154.h index e87f1b07f20f..0804d79669a4 100644 --- a/include/net/cfg802154.h +++ b/include/net/cfg802154.h @@ -218,6 +218,7 @@ struct wpan_phy { spinlock_t queue_lock; atomic_t ongoing_txs; atomic_t hold_txs; + wait_queue_head_t sync_txq; char priv[] __aligned(NETDEV_ALIGN); }; diff --git a/net/ieee802154/core.c b/net/ieee802154/core.c index 47a4de6df88b..57546e07e06a 100644 --- a/net/ieee802154/core.c +++ b/net/ieee802154/core.c @@ -129,6 +129,7 @@ wpan_phy_new(const struct cfg802154_ops *ops, size_t priv_size) wpan_phy_net_set(&rdev->wpan_phy, &init_net); init_waitqueue_head(&rdev->dev_wait); + init_waitqueue_head(&rdev->wpan_phy.sync_txq); spin_lock_init(&rdev->wpan_phy.queue_lock); diff --git a/net/mac802154/cfg.c b/net/mac802154/cfg.c index b51100fd9e3f..93df24f75572 100644 --- a/net/mac802154/cfg.c +++ b/net/mac802154/cfg.c @@ -46,7 +46,7 @@ static int ieee802154_suspend(struct wpan_phy *wpan_phy) if (!local->open_count) goto suspend; - ieee802154_hold_queue(local); + ieee802154_sync_and_hold_queue(local); synchronize_net(); /* stop hardware - this must stop RX */ diff --git a/net/mac802154/ieee802154_i.h b/net/mac802154/ieee802154_i.h index e34db1d49ef4..a057827fc48a 100644 --- a/net/mac802154/ieee802154_i.h +++ b/net/mac802154/ieee802154_i.h @@ -124,6 +124,7 @@ extern struct ieee802154_mlme_ops mac802154_mlme_wpan; void ieee802154_rx(struct ieee802154_local *local, struct sk_buff *skb); void ieee802154_xmit_sync_worker(struct work_struct *work); +int ieee802154_sync_and_hold_queue(struct ieee802154_local *local); netdev_tx_t ieee802154_monitor_start_xmit(struct sk_buff *skb, struct net_device *dev); netdev_tx_t diff --git a/net/mac802154/tx.c b/net/mac802154/tx.c index 607019b8f8ab..38f74b8b6740 100644 --- a/net/mac802154/tx.c +++ b/net/mac802154/tx.c @@ -44,7 +44,8 @@ void ieee802154_xmit_sync_worker(struct work_struct *work) err_tx: /* Restart the netif queue on each sub_if_data object. */ ieee802154_release_queue(local); - atomic_dec(&local->phy->ongoing_txs); + if (!atomic_dec_and_test(&local->phy->ongoing_txs)) + wake_up(&local->phy->sync_txq); kfree_skb(skb); netdev_dbg(dev, "transmission failed\n"); } @@ -100,12 +101,33 @@ ieee802154_tx(struct ieee802154_local *local, struct sk_buff *skb) err_wake_netif_queue: ieee802154_release_queue(local); - atomic_dec(&local->phy->ongoing_txs); + if (!atomic_dec_and_test(&local->phy->ongoing_txs)) + wake_up(&local->phy->sync_txq); err_free_skb: kfree_skb(skb); return NETDEV_TX_OK; } +static int ieee802154_sync_queue(struct ieee802154_local *local) +{ + int ret; + + ieee802154_hold_queue(local); + ieee802154_disable_queue(local); + wait_event(local->phy->sync_txq, !atomic_read(&local->phy->ongoing_txs)); + ret = local->tx_result; + ieee802154_release_queue(local); + + return ret; +} + +int ieee802154_sync_and_hold_queue(struct ieee802154_local *local) +{ + ieee802154_hold_queue(local); + + return ieee802154_sync_queue(local); +} + static netdev_tx_t ieee802154_hot_tx(struct ieee802154_local *local, struct sk_buff *skb) { diff --git a/net/mac802154/util.c b/net/mac802154/util.c index 999534f64485..5e1fcc7b0123 100644 --- a/net/mac802154/util.c +++ b/net/mac802154/util.c @@ -140,7 +140,8 @@ void ieee802154_xmit_complete(struct ieee802154_hw *hw, struct sk_buff *skb, } dev_consume_skb_any(skb); - atomic_dec(&hw->phy->ongoing_txs); + if (!atomic_dec_and_test(&hw->phy->ongoing_txs)) + wake_up(&hw->phy->sync_txq); } EXPORT_SYMBOL(ieee802154_xmit_complete); @@ -152,7 +153,8 @@ void ieee802154_xmit_error(struct ieee802154_hw *hw, struct sk_buff *skb, local->tx_result = reason; ieee802154_release_queue(local); dev_kfree_skb_any(skb); - atomic_dec(&hw->phy->ongoing_txs); + if (!atomic_dec_and_test(&hw->phy->ongoing_txs)) + wake_up(&hw->phy->sync_txq); } EXPORT_SYMBOL(ieee802154_xmit_error); From ddd9ee7cda122ac55571b8b11b51ef1e71918b63 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Thu, 19 May 2022 17:05:14 +0200 Subject: [PATCH 0009/2002] net: mac802154: Introduce a synchronous API for MLME commands This is the slow path, we need to wait for each command to be processed before continuing so let's introduce an helper which does the transmission and blocks until it gets notified of its asynchronous completion. This helper is going to be used when introducing scan support. Signed-off-by: Miquel Raynal Acked-by: Alexander Aring Link: https://lore.kernel.org/r/20220519150516.443078-10-miquel.raynal@bootlin.com Signed-off-by: Stefan Schmidt --- net/mac802154/ieee802154_i.h | 4 ++++ net/mac802154/tx.c | 44 ++++++++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+) diff --git a/net/mac802154/ieee802154_i.h b/net/mac802154/ieee802154_i.h index a057827fc48a..8a4816ae71e7 100644 --- a/net/mac802154/ieee802154_i.h +++ b/net/mac802154/ieee802154_i.h @@ -125,6 +125,10 @@ extern struct ieee802154_mlme_ops mac802154_mlme_wpan; void ieee802154_rx(struct ieee802154_local *local, struct sk_buff *skb); void ieee802154_xmit_sync_worker(struct work_struct *work); int ieee802154_sync_and_hold_queue(struct ieee802154_local *local); +int ieee802154_mlme_op_pre(struct ieee802154_local *local); +int ieee802154_mlme_tx(struct ieee802154_local *local, struct sk_buff *skb); +void ieee802154_mlme_op_post(struct ieee802154_local *local); +int ieee802154_mlme_tx_one(struct ieee802154_local *local, struct sk_buff *skb); netdev_tx_t ieee802154_monitor_start_xmit(struct sk_buff *skb, struct net_device *dev); netdev_tx_t diff --git a/net/mac802154/tx.c b/net/mac802154/tx.c index 38f74b8b6740..4827391600f6 100644 --- a/net/mac802154/tx.c +++ b/net/mac802154/tx.c @@ -128,6 +128,50 @@ int ieee802154_sync_and_hold_queue(struct ieee802154_local *local) return ieee802154_sync_queue(local); } +int ieee802154_mlme_op_pre(struct ieee802154_local *local) +{ + return ieee802154_sync_and_hold_queue(local); +} + +int ieee802154_mlme_tx(struct ieee802154_local *local, struct sk_buff *skb) +{ + int ret; + + /* Avoid possible calls to ->ndo_stop() when we asynchronously perform + * MLME transmissions. + */ + rtnl_lock(); + + /* Ensure the device was not stopped, otherwise error out */ + if (!local->open_count) { + rtnl_unlock(); + return -ENETDOWN; + } + + ieee802154_tx(local, skb); + ret = ieee802154_sync_queue(local); + + rtnl_unlock(); + + return ret; +} + +void ieee802154_mlme_op_post(struct ieee802154_local *local) +{ + ieee802154_release_queue(local); +} + +int ieee802154_mlme_tx_one(struct ieee802154_local *local, struct sk_buff *skb) +{ + int ret; + + ieee802154_mlme_op_pre(local); + ret = ieee802154_mlme_tx(local, skb); + ieee802154_mlme_op_post(local); + + return ret; +} + static netdev_tx_t ieee802154_hot_tx(struct ieee802154_local *local, struct sk_buff *skb) { From 2b13db13af50a5dcdb944723c828915a50f0c3b2 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Thu, 19 May 2022 17:05:15 +0200 Subject: [PATCH 0010/2002] net: mac802154: Add a warning in the hot path We should never start a transmission after the queue has been stopped. But because it might work we don't kill the function here but rather warn loudly the user that something is wrong. Set a flag when the queue should remain stopped. Reset this flag when the queue actually gets restarded. Just check this value to know if a transmission is legitimate, warn if it is not. Turn the flags variable into an unsigned long to allow the use of atomic helpers on it. Signed-off-by: Miquel Raynal Acked-by: Alexander Aring Link: https://lore.kernel.org/r/20220519150516.443078-11-miquel.raynal@bootlin.com Signed-off-by: Stefan Schmidt --- include/net/cfg802154.h | 5 ++++- net/mac802154/tx.c | 18 ++++++++++++++++-- net/mac802154/util.c | 1 + 3 files changed, 21 insertions(+), 3 deletions(-) diff --git a/include/net/cfg802154.h b/include/net/cfg802154.h index 0804d79669a4..428cece22205 100644 --- a/include/net/cfg802154.h +++ b/include/net/cfg802154.h @@ -166,11 +166,14 @@ wpan_phy_cca_cmp(const struct wpan_phy_cca *a, const struct wpan_phy_cca *b) * level setting. * @WPAN_PHY_FLAG_CCA_MODE: Indicates that transceiver will support cca mode * setting. + * @WPAN_PHY_FLAG_STATE_QUEUE_STOPPED: Indicates that the transmit queue was + * temporarily stopped. */ enum wpan_phy_flags { WPAN_PHY_FLAG_TXPOWER = BIT(1), WPAN_PHY_FLAG_CCA_ED_LEVEL = BIT(2), WPAN_PHY_FLAG_CCA_MODE = BIT(3), + WPAN_PHY_FLAG_STATE_QUEUE_STOPPED = BIT(4), }; struct wpan_phy { @@ -182,7 +185,7 @@ struct wpan_phy { */ const void *privid; - u32 flags; + unsigned long flags; /* * This is a PIB according to 802.15.4-2011. diff --git a/net/mac802154/tx.c b/net/mac802154/tx.c index 4827391600f6..6188f42276e7 100644 --- a/net/mac802154/tx.c +++ b/net/mac802154/tx.c @@ -123,9 +123,13 @@ static int ieee802154_sync_queue(struct ieee802154_local *local) int ieee802154_sync_and_hold_queue(struct ieee802154_local *local) { - ieee802154_hold_queue(local); + int ret; - return ieee802154_sync_queue(local); + ieee802154_hold_queue(local); + ret = ieee802154_sync_queue(local); + set_bit(WPAN_PHY_FLAG_STATE_QUEUE_STOPPED, &local->phy->flags); + + return ret; } int ieee802154_mlme_op_pre(struct ieee802154_local *local) @@ -172,9 +176,19 @@ int ieee802154_mlme_tx_one(struct ieee802154_local *local, struct sk_buff *skb) return ret; } +static bool ieee802154_queue_is_stopped(struct ieee802154_local *local) +{ + return test_bit(WPAN_PHY_FLAG_STATE_QUEUE_STOPPED, &local->phy->flags); +} + static netdev_tx_t ieee802154_hot_tx(struct ieee802154_local *local, struct sk_buff *skb) { + /* Warn if the net interface tries to transmit frames while the + * ieee802154 core assumes the queue is stopped. + */ + WARN_ON_ONCE(ieee802154_queue_is_stopped(local)); + return ieee802154_tx(local, skb); } diff --git a/net/mac802154/util.c b/net/mac802154/util.c index 5e1fcc7b0123..60eb7bd3bfc1 100644 --- a/net/mac802154/util.c +++ b/net/mac802154/util.c @@ -29,6 +29,7 @@ static void ieee802154_wake_queue(struct ieee802154_hw *hw) struct ieee802154_sub_if_data *sdata; rcu_read_lock(); + clear_bit(WPAN_PHY_FLAG_STATE_QUEUE_STOPPED, &local->phy->flags); list_for_each_entry_rcu(sdata, &local->interfaces, list) { if (!sdata->dev) continue; From 4f790184139beb8b35a1f4f9a4b6731c6eef1763 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Thu, 19 May 2022 17:05:16 +0200 Subject: [PATCH 0011/2002] net: mac802154: Add a warning in the slow path In order to be able to detect possible conflicts between the net interface core and the ieee802154 core, let's add a warning in the slow path: we want to be sure that whenever we start an asynchronous MLME transmission (which can be fully asynchronous) the net core somehow agrees that this transmission is possible, ie. the device was not stopped. Warning in this case would allow us to track down more easily possible issues with the MLME logic if we ever get reports. Unlike in the hot path, such a situation cannot be handled. Signed-off-by: Miquel Raynal Acked-by: Alexander Aring Link: https://lore.kernel.org/r/20220519150516.443078-12-miquel.raynal@bootlin.com Signed-off-by: Stefan Schmidt --- net/mac802154/tx.c | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/net/mac802154/tx.c b/net/mac802154/tx.c index 6188f42276e7..5b471e932271 100644 --- a/net/mac802154/tx.c +++ b/net/mac802154/tx.c @@ -132,6 +132,25 @@ int ieee802154_sync_and_hold_queue(struct ieee802154_local *local) return ret; } +static bool ieee802154_netif_is_down(struct ieee802154_local *local) +{ + struct ieee802154_sub_if_data *sdata; + bool is_down = true; + + rcu_read_lock(); + list_for_each_entry_rcu(sdata, &local->interfaces, list) { + if (!sdata->dev) + continue; + + is_down = !netif_running(sdata->dev); + if (is_down) + break; + } + rcu_read_unlock(); + + return is_down; +} + int ieee802154_mlme_op_pre(struct ieee802154_local *local) { return ieee802154_sync_and_hold_queue(local); @@ -152,6 +171,14 @@ int ieee802154_mlme_tx(struct ieee802154_local *local, struct sk_buff *skb) return -ENETDOWN; } + /* Warn if the ieee802154 core thinks MLME frames can be sent while the + * net interface expects this cannot happen. + */ + if (WARN_ON_ONCE(ieee802154_netif_is_down(local))) { + rtnl_unlock(); + return -ENETDOWN; + } + ieee802154_tx(local, skb); ret = ieee802154_sync_queue(local); From 2ec2f6bed4d12a7ed74ed179a5d2879b117105d1 Mon Sep 17 00:00:00 2001 From: Alexander Aring Date: Mon, 13 Jun 2022 00:37:34 -0400 Subject: [PATCH 0012/2002] mac802154: util: fix release queue handling The semantic of atomic_dec_and_test() is to return true if zero is reached and we need call ieee802154_wake_queue() when zero is reached. Fixes: 20a19d1df3e4 ("net: mac802154: Bring the ability to hold the transmit queue") Signed-off-by: Alexander Aring Reviewed-by: Miquel Raynal Link: https://lore.kernel.org/r/20220613043735.1039895-2-aahringo@redhat.com Signed-off-by: Stefan Schmidt --- net/mac802154/util.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/mac802154/util.c b/net/mac802154/util.c index 60eb7bd3bfc1..60f6c0f10641 100644 --- a/net/mac802154/util.c +++ b/net/mac802154/util.c @@ -79,7 +79,7 @@ void ieee802154_release_queue(struct ieee802154_local *local) unsigned long flags; spin_lock_irqsave(&local->phy->queue_lock, flags); - if (!atomic_dec_and_test(&local->phy->hold_txs)) + if (atomic_dec_and_test(&local->phy->hold_txs)) ieee802154_wake_queue(&local->hw); spin_unlock_irqrestore(&local->phy->queue_lock, flags); } From 6c1c78d0182fcbfe953bf5a0c3e71204d176b887 Mon Sep 17 00:00:00 2001 From: Alexander Aring Date: Mon, 13 Jun 2022 00:37:35 -0400 Subject: [PATCH 0013/2002] mac802154: fix atomic_dec_and_test checks We need to call wake_up() when hold_txs reaches zero. The semantic of atomic_dec_and_test() is that it returns true when it's zero. Fixes: f0feb3490473 ("net: mac802154: Introduce a tx queue flushing mechanism") Signed-off-by: Alexander Aring Reviewed-by: Miquel Raynal Link: https://lore.kernel.org/r/20220613043735.1039895-3-aahringo@redhat.com Signed-off-by: Stefan Schmidt --- net/mac802154/tx.c | 4 ++-- net/mac802154/util.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/net/mac802154/tx.c b/net/mac802154/tx.c index 5b471e932271..8ddcd2e841ca 100644 --- a/net/mac802154/tx.c +++ b/net/mac802154/tx.c @@ -44,7 +44,7 @@ void ieee802154_xmit_sync_worker(struct work_struct *work) err_tx: /* Restart the netif queue on each sub_if_data object. */ ieee802154_release_queue(local); - if (!atomic_dec_and_test(&local->phy->ongoing_txs)) + if (atomic_dec_and_test(&local->phy->ongoing_txs)) wake_up(&local->phy->sync_txq); kfree_skb(skb); netdev_dbg(dev, "transmission failed\n"); @@ -101,7 +101,7 @@ ieee802154_tx(struct ieee802154_local *local, struct sk_buff *skb) err_wake_netif_queue: ieee802154_release_queue(local); - if (!atomic_dec_and_test(&local->phy->ongoing_txs)) + if (atomic_dec_and_test(&local->phy->ongoing_txs)) wake_up(&local->phy->sync_txq); err_free_skb: kfree_skb(skb); diff --git a/net/mac802154/util.c b/net/mac802154/util.c index 60f6c0f10641..f08605f59b60 100644 --- a/net/mac802154/util.c +++ b/net/mac802154/util.c @@ -141,7 +141,7 @@ void ieee802154_xmit_complete(struct ieee802154_hw *hw, struct sk_buff *skb, } dev_consume_skb_any(skb); - if (!atomic_dec_and_test(&hw->phy->ongoing_txs)) + if (atomic_dec_and_test(&hw->phy->ongoing_txs)) wake_up(&hw->phy->sync_txq); } EXPORT_SYMBOL(ieee802154_xmit_complete); @@ -154,7 +154,7 @@ void ieee802154_xmit_error(struct ieee802154_hw *hw, struct sk_buff *skb, local->tx_result = reason; ieee802154_release_queue(local); dev_kfree_skb_any(skb); - if (!atomic_dec_and_test(&hw->phy->ongoing_txs)) + if (atomic_dec_and_test(&hw->phy->ongoing_txs)) wake_up(&hw->phy->sync_txq); } EXPORT_SYMBOL(ieee802154_xmit_error); From fbdaa5ba6bd6955f7e7f9228e4d815cc5e43fe5b Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Fri, 17 Jun 2022 21:29:14 +0200 Subject: [PATCH 0014/2002] net: mac802154: Fix a Tx warning check The purpose of the netif_is_down() helper was to ensure that the network interface used was still up when performing the transmission. What it actually did was to check if _all_ interfaces were up. This was not noticed at that time because I did not use interfaces at all before discussing with Alexander Aring about how to handle coordinators properly. Drop the helper and call netif_running() on the right sub interface object directly. Fixes: 4f790184139b ("net: mac802154: Add a warning in the slow path") Signed-off-by: Miquel Raynal Acked-by: Alexander Aring Link: https://lore.kernel.org/r/20220617192914.1275611-1-miquel.raynal@bootlin.com Signed-off-by: Stefan Schmidt --- net/mac802154/ieee802154_i.h | 8 ++++++-- net/mac802154/tx.c | 31 ++++++++----------------------- 2 files changed, 14 insertions(+), 25 deletions(-) diff --git a/net/mac802154/ieee802154_i.h b/net/mac802154/ieee802154_i.h index 8a4816ae71e7..010365a6364e 100644 --- a/net/mac802154/ieee802154_i.h +++ b/net/mac802154/ieee802154_i.h @@ -126,9 +126,13 @@ void ieee802154_rx(struct ieee802154_local *local, struct sk_buff *skb); void ieee802154_xmit_sync_worker(struct work_struct *work); int ieee802154_sync_and_hold_queue(struct ieee802154_local *local); int ieee802154_mlme_op_pre(struct ieee802154_local *local); -int ieee802154_mlme_tx(struct ieee802154_local *local, struct sk_buff *skb); +int ieee802154_mlme_tx(struct ieee802154_local *local, + struct ieee802154_sub_if_data *sdata, + struct sk_buff *skb); void ieee802154_mlme_op_post(struct ieee802154_local *local); -int ieee802154_mlme_tx_one(struct ieee802154_local *local, struct sk_buff *skb); +int ieee802154_mlme_tx_one(struct ieee802154_local *local, + struct ieee802154_sub_if_data *sdata, + struct sk_buff *skb); netdev_tx_t ieee802154_monitor_start_xmit(struct sk_buff *skb, struct net_device *dev); netdev_tx_t diff --git a/net/mac802154/tx.c b/net/mac802154/tx.c index 8ddcd2e841ca..9d8d43cf1e64 100644 --- a/net/mac802154/tx.c +++ b/net/mac802154/tx.c @@ -132,31 +132,14 @@ int ieee802154_sync_and_hold_queue(struct ieee802154_local *local) return ret; } -static bool ieee802154_netif_is_down(struct ieee802154_local *local) -{ - struct ieee802154_sub_if_data *sdata; - bool is_down = true; - - rcu_read_lock(); - list_for_each_entry_rcu(sdata, &local->interfaces, list) { - if (!sdata->dev) - continue; - - is_down = !netif_running(sdata->dev); - if (is_down) - break; - } - rcu_read_unlock(); - - return is_down; -} - int ieee802154_mlme_op_pre(struct ieee802154_local *local) { return ieee802154_sync_and_hold_queue(local); } -int ieee802154_mlme_tx(struct ieee802154_local *local, struct sk_buff *skb) +int ieee802154_mlme_tx(struct ieee802154_local *local, + struct ieee802154_sub_if_data *sdata, + struct sk_buff *skb) { int ret; @@ -174,7 +157,7 @@ int ieee802154_mlme_tx(struct ieee802154_local *local, struct sk_buff *skb) /* Warn if the ieee802154 core thinks MLME frames can be sent while the * net interface expects this cannot happen. */ - if (WARN_ON_ONCE(ieee802154_netif_is_down(local))) { + if (WARN_ON_ONCE(!netif_running(sdata->dev))) { rtnl_unlock(); return -ENETDOWN; } @@ -192,12 +175,14 @@ void ieee802154_mlme_op_post(struct ieee802154_local *local) ieee802154_release_queue(local); } -int ieee802154_mlme_tx_one(struct ieee802154_local *local, struct sk_buff *skb) +int ieee802154_mlme_tx_one(struct ieee802154_local *local, + struct ieee802154_sub_if_data *sdata, + struct sk_buff *skb) { int ret; ieee802154_mlme_op_pre(local); - ret = ieee802154_mlme_tx(local, skb); + ret = ieee802154_mlme_tx(local, sdata, skb); ieee802154_mlme_op_post(local); return ret; From d90fdb9138262e2c68e4305965348dc065f5c7a3 Mon Sep 17 00:00:00 2001 From: Jilin Yuan Date: Fri, 8 Jul 2022 23:15:38 +0800 Subject: [PATCH 0015/2002] net/ieee802154: fix repeated words in comments Delete the redundant word 'was'. Signed-off-by: Jilin Yuan Link: https://lore.kernel.org/r/20220708151538.51483-1-yuanjilin@cdjrlc.com Signed-off-by: Stefan Schmidt --- drivers/net/ieee802154/ca8210.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ieee802154/ca8210.c b/drivers/net/ieee802154/ca8210.c index 42c0b451088d..450b16ad40a4 100644 --- a/drivers/net/ieee802154/ca8210.c +++ b/drivers/net/ieee802154/ca8210.c @@ -2293,7 +2293,7 @@ static int ca8210_set_csma_params( * @retries: Number of retries * * Sets the number of times to retry a transmission if no acknowledgment was - * was received from the other end when one was requested. + * received from the other end when one was requested. * * Return: 0 or linux error code */ From 81e60b2dfb2744ab6642c4aa62534b4f711fdc5d Mon Sep 17 00:00:00 2001 From: Aditya Kumar Singh Date: Tue, 27 Sep 2022 09:18:54 +0300 Subject: [PATCH 0016/2002] wifi: ath11k: stop tx queues immediately upon firmware exit Currently, recovery flag is set immediately upon firmware exit but tx queues are stopped once firmware arrives back and is ready which is during ath11k_core_restart. Once ieee80211 hw restart is completed, tx queues are resumed. If during the time delta between firmware exit and firmware ready, mac80211 send packets, currently ath11k will drop it since recovery flag will be set. But warning prints will come - "ath11k c000000.wifi: failed to transmit frame -108" If more tx packets are there, this could lead to flooding of above print. However, actually tx queues should be stopped immediately when firmware leaves. This will prevent packets to get dropped when firmware is recovering. Add fix to stop tx queues immediately after firmware exit. Tested-on: IPQ8074 hw2.0 AHB WLAN.HK.2.5.0.1-01100-QCAHKSWPL_SILICONZ-1 Signed-off-by: Aditya Kumar Singh Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20220923170235.18873-1-quic_adisi@quicinc.com --- drivers/net/wireless/ath/ath11k/core.c | 5 +---- drivers/net/wireless/ath/ath11k/core.h | 1 + drivers/net/wireless/ath/ath11k/qmi.c | 3 +++ 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/ath/ath11k/core.c b/drivers/net/wireless/ath/ath11k/core.c index b99180bc8172..35a5de3a0a8a 100644 --- a/drivers/net/wireless/ath/ath11k/core.c +++ b/drivers/net/wireless/ath/ath11k/core.c @@ -1641,7 +1641,7 @@ static void ath11k_update_11d(struct work_struct *work) } } -static void ath11k_core_pre_reconfigure_recovery(struct ath11k_base *ab) +void ath11k_core_pre_reconfigure_recovery(struct ath11k_base *ab) { struct ath11k *ar; struct ath11k_pdev *pdev; @@ -1730,9 +1730,6 @@ static void ath11k_core_restart(struct work_struct *work) struct ath11k_base *ab = container_of(work, struct ath11k_base, restart_work); int ret; - if (!ab->is_reset) - ath11k_core_pre_reconfigure_recovery(ab); - ret = ath11k_core_reconfigure_on_crash(ab); if (ret) { ath11k_err(ab, "failed to reconfigure driver on crash recovery\n"); diff --git a/drivers/net/wireless/ath/ath11k/core.h b/drivers/net/wireless/ath/ath11k/core.h index cf2f52cc4e30..bbcef120c8f6 100644 --- a/drivers/net/wireless/ath/ath11k/core.h +++ b/drivers/net/wireless/ath/ath11k/core.h @@ -1157,6 +1157,7 @@ int ath11k_core_check_smbios(struct ath11k_base *ab); void ath11k_core_halt(struct ath11k *ar); int ath11k_core_resume(struct ath11k_base *ab); int ath11k_core_suspend(struct ath11k_base *ab); +void ath11k_core_pre_reconfigure_recovery(struct ath11k_base *ab); const struct firmware *ath11k_core_firmware_request(struct ath11k_base *ab, const char *filename); diff --git a/drivers/net/wireless/ath/ath11k/qmi.c b/drivers/net/wireless/ath/ath11k/qmi.c index 51de2208b789..775dfb5eb1a7 100644 --- a/drivers/net/wireless/ath/ath11k/qmi.c +++ b/drivers/net/wireless/ath/ath11k/qmi.c @@ -3158,6 +3158,9 @@ static void ath11k_qmi_driver_event_work(struct work_struct *work) case ATH11K_QMI_EVENT_SERVER_EXIT: set_bit(ATH11K_FLAG_CRASH_FLUSH, &ab->dev_flags); set_bit(ATH11K_FLAG_RECOVERY, &ab->dev_flags); + + if (!ab->is_reset) + ath11k_core_pre_reconfigure_recovery(ab); break; case ATH11K_QMI_EVENT_REQUEST_MEM: ret = ath11k_qmi_event_mem_request(qmi); From e5398f92d1ca4aa4244616519fbf6dc155302b33 Mon Sep 17 00:00:00 2001 From: Liu Shixin Date: Tue, 27 Sep 2022 09:18:54 +0300 Subject: [PATCH 0017/2002] wifi: wil6210: debugfs: use DEFINE_SHOW_ATTRIBUTE to simplify fw_capabilities/fw_version Use DEFINE_SHOW_ATTRIBUTE helper macro to simplify the code. No functional change. Signed-off-by: Liu Shixin Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20220922142858.3250469-1-liushixin2@huawei.com --- drivers/net/wireless/ath/wil6210/debugfs.c | 36 ++++------------------ 1 file changed, 6 insertions(+), 30 deletions(-) diff --git a/drivers/net/wireless/ath/wil6210/debugfs.c b/drivers/net/wireless/ath/wil6210/debugfs.c index 04d1aa0e2d35..c021ebcddee7 100644 --- a/drivers/net/wireless/ath/wil6210/debugfs.c +++ b/drivers/net/wireless/ath/wil6210/debugfs.c @@ -2154,7 +2154,7 @@ static const struct file_operations fops_led_blink_time = { }; /*---------FW capabilities------------*/ -static int wil_fw_capabilities_debugfs_show(struct seq_file *s, void *data) +static int fw_capabilities_show(struct seq_file *s, void *data) { struct wil6210_priv *wil = s->private; @@ -2163,22 +2163,10 @@ static int wil_fw_capabilities_debugfs_show(struct seq_file *s, void *data) return 0; } - -static int wil_fw_capabilities_seq_open(struct inode *inode, struct file *file) -{ - return single_open(file, wil_fw_capabilities_debugfs_show, - inode->i_private); -} - -static const struct file_operations fops_fw_capabilities = { - .open = wil_fw_capabilities_seq_open, - .release = single_release, - .read = seq_read, - .llseek = seq_lseek, -}; +DEFINE_SHOW_ATTRIBUTE(fw_capabilities); /*---------FW version------------*/ -static int wil_fw_version_debugfs_show(struct seq_file *s, void *data) +static int fw_version_show(struct seq_file *s, void *data) { struct wil6210_priv *wil = s->private; @@ -2189,19 +2177,7 @@ static int wil_fw_version_debugfs_show(struct seq_file *s, void *data) return 0; } - -static int wil_fw_version_seq_open(struct inode *inode, struct file *file) -{ - return single_open(file, wil_fw_version_debugfs_show, - inode->i_private); -} - -static const struct file_operations fops_fw_version = { - .open = wil_fw_version_seq_open, - .release = single_release, - .read = seq_read, - .llseek = seq_lseek, -}; +DEFINE_SHOW_ATTRIBUTE(fw_version); /*---------suspend_stats---------*/ static ssize_t wil_write_suspend_stats(struct file *file, @@ -2366,8 +2342,8 @@ static const struct { {"recovery", 0644, &fops_recovery}, {"led_cfg", 0644, &fops_led_cfg}, {"led_blink_time", 0644, &fops_led_blink_time}, - {"fw_capabilities", 0444, &fops_fw_capabilities}, - {"fw_version", 0444, &fops_fw_version}, + {"fw_capabilities", 0444, &fw_capabilities_fops}, + {"fw_version", 0444, &fw_version_fops}, {"suspend_stats", 0644, &fops_suspend_stats}, {"compressed_rx_status", 0644, &fops_compressed_rx_status}, {"srings", 0444, &srings_fops}, From 00d942e779c24581932125a58d8b9a2938e9e7ce Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Tue, 27 Sep 2022 09:18:55 +0300 Subject: [PATCH 0018/2002] wifi: ath10k: Replace zero-length arrays with DECLARE_FLEX_ARRAY() helper Zero-length arrays are deprecated and we are moving towards adopting C99 flexible-array members, instead. So, replace zero-length arrays declarations in anonymous union with the new DECLARE_FLEX_ARRAY() helper macro. This helper allows for flexible-array members in unions. Link: https://github.com/KSPP/linux/issues/193 Link: https://github.com/KSPP/linux/issues/212 Link: https://gcc.gnu.org/onlinedocs/gcc/Zero-Length.html Signed-off-by: Gustavo A. R. Silva Reviewed-by: Kees Cook Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/YzIcoloIQBDqGlgc@work --- drivers/net/wireless/ath/ath10k/htt.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/ath/ath10k/htt.h b/drivers/net/wireless/ath/ath10k/htt.h index f06cf39204e2..c051a22fce14 100644 --- a/drivers/net/wireless/ath/ath10k/htt.h +++ b/drivers/net/wireless/ath/ath10k/htt.h @@ -1108,8 +1108,10 @@ struct htt_rx_in_ord_ind { u8 reserved; __le16 msdu_count; union { - struct htt_rx_in_ord_msdu_desc msdu_descs32[0]; - struct htt_rx_in_ord_msdu_desc_ext msdu_descs64[0]; + DECLARE_FLEX_ARRAY(struct htt_rx_in_ord_msdu_desc, + msdu_descs32); + DECLARE_FLEX_ARRAY(struct htt_rx_in_ord_msdu_desc_ext, + msdu_descs64); } __packed; } __packed; From 9ec6e20776abda3ca03ab97b1caa8310074427c1 Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Mon, 26 Sep 2022 16:44:57 -0500 Subject: [PATCH 0019/2002] carl9170: Replace zero-length array with DECLARE_FLEX_ARRAY() helper Zero-length arrays are deprecated and we are moving towards adopting C99 flexible-array members, instead. So, replace zero-length arrays declarations in anonymous union with the new DECLARE_FLEX_ARRAY() helper macro. This helper allows for flexible-array members in unions. Link: https://github.com/KSPP/linux/issues/193 Link: https://github.com/KSPP/linux/issues/215 Link: https://gcc.gnu.org/onlinedocs/gcc/Zero-Length.html Signed-off-by: Gustavo A. R. Silva Reviewed-by: Kees Cook Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/YzIdWc8QSdZFHBYg@work --- drivers/net/wireless/ath/carl9170/wlan.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/carl9170/wlan.h b/drivers/net/wireless/ath/carl9170/wlan.h index 0a4e42e806b9..ded2c6d0a759 100644 --- a/drivers/net/wireless/ath/carl9170/wlan.h +++ b/drivers/net/wireless/ath/carl9170/wlan.h @@ -271,7 +271,7 @@ struct ar9170_tx_frame { union { struct ieee80211_hdr i3e; - u8 payload[0]; + DECLARE_FLEX_ARRAY(u8, payload); } data; } __packed; From 45d2e268369b0c768d5a644f319758bcfd370521 Mon Sep 17 00:00:00 2001 From: Baochen Qiang Date: Wed, 28 Sep 2022 09:51:40 +0800 Subject: [PATCH 0020/2002] wifi: ath11k: Don't exit on wakeup failure Currently, ath11k_pcic_read() returns an error if wakeup() fails, this makes firmware crash debug quite hard because we can get nothing. Change to go ahead on wakeup failure, in that case we still may get something valid to check. There should be no mislead due to incorrect content because we are aware of the failure with the log printed. Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-01720.1-QCAHSPSWPL_V1_V2_SILICONZ_LITE-1 Signed-off-by: Baochen Qiang Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20220928015140.5431-1-quic_bqiang@quicinc.com --- drivers/net/wireless/ath/ath11k/pcic.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/ath/ath11k/pcic.c b/drivers/net/wireless/ath/ath11k/pcic.c index 3fa1958f8c82..d2f0112e8df0 100644 --- a/drivers/net/wireless/ath/ath11k/pcic.c +++ b/drivers/net/wireless/ath/ath11k/pcic.c @@ -218,9 +218,16 @@ int ath11k_pcic_read(struct ath11k_base *ab, void *buf, u32 start, u32 end) if (wakeup_required && ab->pci.ops->wakeup) { ret = ab->pci.ops->wakeup(ab); if (ret) { - ath11k_warn(ab, "failed to wakeup for read from 0x%x: %d\n", - start, ret); - return ret; + ath11k_warn(ab, + "wakeup failed, data may be invalid: %d", + ret); + /* Even though wakeup() failed, continue processing rather + * than returning because some parts of the data may still + * be valid and useful in some cases, e.g. could give us + * some clues on firmware crash. + * Mislead due to invalid data could be avoided because we + * are aware of the wakeup failure. + */ } } From f74878433d5ade360447da5d92e9c2e535780d80 Mon Sep 17 00:00:00 2001 From: Wen Gong Date: Wed, 28 Sep 2022 03:38:32 -0400 Subject: [PATCH 0021/2002] wifi: ath11k: fix warning in dma_free_coherent() of memory chunks while recovery Commit 26f3a021b37c ("ath11k: allocate smaller chunks of memory for firmware") and commit f6f92968e1e5 ("ath11k: qmi: try to allocate a big block of DMA memory first") change ath11k to allocate the memory chunks for target twice while wlan load. It fails for the 1st time because of large memory and then changed to allocate many small chunks for the 2nd time sometimes as below log. 1st time failed: [10411.640620] ath11k_pci 0000:05:00.0: qmi firmware request memory request [10411.640625] ath11k_pci 0000:05:00.0: qmi mem seg type 1 size 6881280 [10411.640630] ath11k_pci 0000:05:00.0: qmi mem seg type 4 size 3784704 [10411.640658] ath11k_pci 0000:05:00.0: qmi dma allocation failed (6881280 B type 1), will try later with small size [10411.640671] ath11k_pci 0000:05:00.0: qmi delays mem_request 2 [10411.640677] ath11k_pci 0000:05:00.0: qmi respond memory request delayed 1 2nd time success: [10411.642004] ath11k_pci 0000:05:00.0: qmi firmware request memory request [10411.642008] ath11k_pci 0000:05:00.0: qmi mem seg type 1 size 524288 [10411.642012] ath11k_pci 0000:05:00.0: qmi mem seg type 1 size 524288 [10411.642014] ath11k_pci 0000:05:00.0: qmi mem seg type 1 size 524288 [10411.642016] ath11k_pci 0000:05:00.0: qmi mem seg type 1 size 524288 [10411.642018] ath11k_pci 0000:05:00.0: qmi mem seg type 1 size 524288 [10411.642020] ath11k_pci 0000:05:00.0: qmi mem seg type 1 size 524288 [10411.642022] ath11k_pci 0000:05:00.0: qmi mem seg type 1 size 524288 [10411.642024] ath11k_pci 0000:05:00.0: qmi mem seg type 1 size 524288 [10411.642027] ath11k_pci 0000:05:00.0: qmi mem seg type 1 size 524288 [10411.642029] ath11k_pci 0000:05:00.0: qmi mem seg type 1 size 524288 [10411.642031] ath11k_pci 0000:05:00.0: qmi mem seg type 1 size 458752 [10411.642033] ath11k_pci 0000:05:00.0: qmi mem seg type 1 size 131072 [10411.642035] ath11k_pci 0000:05:00.0: qmi mem seg type 4 size 524288 [10411.642037] ath11k_pci 0000:05:00.0: qmi mem seg type 4 size 524288 [10411.642039] ath11k_pci 0000:05:00.0: qmi mem seg type 4 size 524288 [10411.642041] ath11k_pci 0000:05:00.0: qmi mem seg type 4 size 524288 [10411.642043] ath11k_pci 0000:05:00.0: qmi mem seg type 4 size 524288 [10411.642045] ath11k_pci 0000:05:00.0: qmi mem seg type 4 size 524288 [10411.642047] ath11k_pci 0000:05:00.0: qmi mem seg type 4 size 491520 [10411.642049] ath11k_pci 0000:05:00.0: qmi mem seg type 1 size 524288 And then commit 5962f370ce41 ("ath11k: Reuse the available memory after firmware reload") skip the ath11k_qmi_free_resource() which frees the memory chunks while recovery, after that, when run recovery test on WCN6855, a warning happened every time as below and finally leads fail for recovery. [ 159.570318] BUG: Bad page state in process kworker/u16:5 pfn:33300 [ 159.570320] page:0000000096ffdbb9 refcount:1 mapcount:0 mapping:0000000000000000 index:0x0 pfn:0x33300 [ 159.570324] flags: 0xfffffc0000000(node=0|zone=1|lastcpupid=0x1fffff) [ 159.570329] raw: 000fffffc0000000 0000000000000000 dead000000000122 0000000000000000 [ 159.570332] raw: 0000000000000000 0000000000000000 00000001ffffffff 0000000000000000 [ 159.570334] page dumped because: nonzero _refcount [ 159.570440] firewire_ohci syscopyarea sysfillrect psmouse sdhci_pci ahci sysimgblt firewire_core fb_sys_fops libahci crc_itu_t cqhci drm sdhci e1000e wmi video [ 159.570460] CPU: 2 PID: 217 Comm: kworker/u16:5 Kdump: loaded Tainted: G B 5.19.0-rc1-wt-ath+ #3 [ 159.570465] Hardware name: LENOVO 418065C/418065C, BIOS 83ET63WW (1.33 ) 07/29/2011 [ 159.570467] Workqueue: qmi_msg_handler qmi_data_ready_work [qmi_helpers] [ 159.570475] Call Trace: [ 159.570476] [ 159.570478] dump_stack_lvl+0x49/0x5f [ 159.570486] dump_stack+0x10/0x12 [ 159.570493] bad_page+0xab/0xf0 [ 159.570502] check_free_page_bad+0x66/0x70 [ 159.570511] __free_pages_ok+0x530/0x9a0 [ 159.570517] ? __dev_printk+0x58/0x6b [ 159.570525] ? _dev_printk+0x56/0x72 [ 159.570534] ? qmi_decode+0x119/0x470 [qmi_helpers] [ 159.570543] __free_pages+0x91/0xd0 [ 159.570548] dma_free_contiguous+0x50/0x60 [ 159.570556] dma_direct_free+0xe5/0x140 [ 159.570564] dma_free_attrs+0x35/0x50 [ 159.570570] ath11k_qmi_msg_mem_request_cb+0x2ae/0x3c0 [ath11k] [ 159.570620] qmi_invoke_handler+0xac/0xe0 [qmi_helpers] [ 159.570630] qmi_handle_message+0x6d/0x180 [qmi_helpers] [ 159.570643] qmi_data_ready_work+0x2ca/0x440 [qmi_helpers] [ 159.570656] process_one_work+0x227/0x440 [ 159.570667] worker_thread+0x31/0x3d0 [ 159.570676] ? process_one_work+0x440/0x440 [ 159.570685] kthread+0xfe/0x130 [ 159.570692] ? kthread_complete_and_exit+0x20/0x20 [ 159.570701] ret_from_fork+0x22/0x30 [ 159.570712] The reason is because when wlan start to recovery, the type, size and count is not same for the 1st and 2nd QMI_WLFW_REQUEST_MEM_IND message, Then it leads the parameter size is not correct for the dma_free_coherent(). For the chunk[1], the actual dma size is 524288 which allocate in the 2nd time of the initial wlan load phase, and the size which pass to dma_free_coherent() is 3784704 which is got in the 1st time of recovery phase, then warning above happened. Change to use prev_size of struct target_mem_chunk for the paramter of dma_free_coherent() since prev_size is the real size of last load/recovery. Also change to check both type and size of struct target_mem_chunk to reuse the memory to avoid mismatch buffer size for target. Then the warning disappear and recovery success. When the 1st QMI_WLFW_REQUEST_MEM_IND for recovery arrived, the trunk[0] is freed in ath11k_qmi_alloc_target_mem_chunk() and then dma_alloc_coherent() failed caused by large size, and then trunk[1] is freed in ath11k_qmi_free_target_mem_chunk(), the left 18 trunks will be reuse for the 2nd QMI_WLFW_REQUEST_MEM_IND message. Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-03125-QCAHSPSWPL_V1_V2_SILICONZ_LITE-3 Fixes: 5962f370ce41 ("ath11k: Reuse the available memory after firmware reload") Signed-off-by: Wen Gong Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20220928073832.16251-1-quic_wgong@quicinc.com --- drivers/net/wireless/ath/ath11k/qmi.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/ath/ath11k/qmi.c b/drivers/net/wireless/ath/ath11k/qmi.c index 775dfb5eb1a7..84e3abecbdac 100644 --- a/drivers/net/wireless/ath/ath11k/qmi.c +++ b/drivers/net/wireless/ath/ath11k/qmi.c @@ -1961,7 +1961,7 @@ static void ath11k_qmi_free_target_mem_chunk(struct ath11k_base *ab) continue; dma_free_coherent(ab->dev, - ab->qmi.target_mem[i].size, + ab->qmi.target_mem[i].prev_size, ab->qmi.target_mem[i].vaddr, ab->qmi.target_mem[i].paddr); ab->qmi.target_mem[i].vaddr = NULL; @@ -1982,12 +1982,12 @@ static int ath11k_qmi_alloc_target_mem_chunk(struct ath11k_base *ab) * in such case, no need to allocate memory for FW again. */ if (chunk->vaddr) { - if (chunk->prev_type == chunk->type || + if (chunk->prev_type == chunk->type && chunk->prev_size == chunk->size) continue; /* cannot reuse the existing chunk */ - dma_free_coherent(ab->dev, chunk->size, + dma_free_coherent(ab->dev, chunk->prev_size, chunk->vaddr, chunk->paddr); chunk->vaddr = NULL; } From c8b5fc2e1d2f6a11fe2ba82da8b0c39bb379b529 Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Wed, 28 Sep 2022 16:43:28 +0800 Subject: [PATCH 0022/2002] wifi: rtw89: 8852b: add BB and RF tables (1 of 2) These tables contain BB and RF parameters that driver will load them into registers. It also contains TX power according to country, band, rate and so on. Increasing thermal can cause TX power degraded, so power tracking tables are defined to compensate TX power. Internal version of these tables: - HALRF_029_00_014 (R32) - HALBB_027_046_05 Signed-off-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20220928084336.34981-2-pkshih@realtek.com --- .../wireless/realtek/rtw89/rtw8852b_table.c | 13249 ++++++++++++++++ .../wireless/realtek/rtw89/rtw8852b_table.h | 30 + 2 files changed, 13279 insertions(+) create mode 100644 drivers/net/wireless/realtek/rtw89/rtw8852b_table.c create mode 100644 drivers/net/wireless/realtek/rtw89/rtw8852b_table.h diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852b_table.c b/drivers/net/wireless/realtek/rtw89/rtw8852b_table.c new file mode 100644 index 000000000000..f29bc5d8d576 --- /dev/null +++ b/drivers/net/wireless/realtek/rtw89/rtw8852b_table.c @@ -0,0 +1,13249 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* Copyright(c) 2019-2020 Realtek Corporation + */ + +#include "phy.h" +#include "reg.h" +#include "rtw8852b_table.h" + +static const struct rtw89_reg2_def rtw89_8852b_phy_bb_regs[] = { + {0x704, 0x601E0100}, + {0x4000, 0x00000000}, + {0x4004, 0xCA014000}, + {0x4008, 0xC751D4F0}, + {0x400C, 0x44511475}, + {0x4010, 0x00000000}, + {0x4014, 0x00000000}, + {0x4018, 0x4F4C084B}, + {0x401C, 0x084A4E52}, + {0x4020, 0x4D504E4B}, + {0x4024, 0x4F4C0849}, + {0x4028, 0x08484C50}, + {0x402C, 0x4C50504C}, + {0x4030, 0x5454084A}, + {0x4034, 0x084B5654}, + {0x4038, 0x6A6C605A}, + {0x403C, 0x4C4C084C}, + {0x4040, 0x084B4E4D}, + {0x4044, 0x4E4C4B4B}, + {0x4048, 0x4B4B084A}, + {0x404C, 0x084A4E4C}, + {0x4050, 0x514F4C4A}, + {0x4054, 0x524E084A}, + {0x4058, 0x084A5154}, + {0x405C, 0x53555554}, + {0x4060, 0x45450845}, + {0x4064, 0x08454144}, + {0x4068, 0x40434445}, + {0x406C, 0x44450845}, + {0x4070, 0x08444043}, + {0x4074, 0x42434444}, + {0x4078, 0x46450844}, + {0x407C, 0x08444843}, + {0x4080, 0x4B4E4A47}, + {0x4084, 0x4F4C084B}, + {0x4088, 0x084A4E52}, + {0x408C, 0x4D504E4B}, + {0x4090, 0x4F4C0849}, + {0x4094, 0x08484C50}, + {0x4098, 0x4C50504C}, + {0x409C, 0x5454084A}, + {0x40A0, 0x084B5654}, + {0x40A4, 0x6A6C605A}, + {0x40A8, 0x4C4C084C}, + {0x40AC, 0x084B4E4D}, + {0x40B0, 0x4E4C4B4B}, + {0x40B4, 0x4B4B084A}, + {0x40B8, 0x084A4E4C}, + {0x40BC, 0x514F4C4A}, + {0x40C0, 0x524E084A}, + {0x40C4, 0x084A5154}, + {0x40C8, 0x53555554}, + {0x40CC, 0x45450845}, + {0x40D0, 0x08454144}, + {0x40D4, 0x40434445}, + {0x40D8, 0x44450845}, + {0x40DC, 0x08444043}, + {0x40E0, 0x42434444}, + {0x40E4, 0x46450844}, + {0x40E8, 0x08444843}, + {0x40EC, 0x4B4E4A47}, + {0x40F0, 0x00000000}, + {0x40F4, 0x00000006}, + {0x40F8, 0x00000000}, + {0x40FC, 0x8C30C30C}, + {0x4100, 0x4C30C30C}, + {0x4104, 0x0C30C30C}, + {0x4108, 0x0C30C30C}, + {0x410C, 0x0C30C30C}, + {0x4110, 0x0C30C30C}, + {0x4114, 0x28A28A28}, + {0x4118, 0x28A28A28}, + {0x411C, 0x28A28A28}, + {0x4120, 0x28A28A28}, + {0x4124, 0x28A28A28}, + {0x4128, 0x28A28A28}, + {0x412C, 0x06666666}, + {0x4130, 0x33333333}, + {0x4134, 0x33333333}, + {0x4138, 0x33333333}, + {0x413C, 0x00000031}, + {0x4140, 0x5100600A}, + {0x4144, 0x18363113}, + {0x4148, 0x1D976DDC}, + {0x414C, 0x1C072DD7}, + {0x4150, 0x1127CDF4}, + {0x4154, 0x1E37BDF1}, + {0x4158, 0x1FB7F1D6}, + {0x415C, 0x1EA7DDF9}, + {0x4160, 0x1FE445DD}, + {0x4164, 0x1F97F1FE}, + {0x4168, 0x1FF781ED}, + {0x416C, 0x1FA7F5FE}, + {0x4170, 0x1E07B913}, + {0x4174, 0x1FD7FDFF}, + {0x4178, 0x1E17B9FA}, + {0x417C, 0x19A66914}, + {0x4180, 0x10F65598}, + {0x4184, 0x14A5A111}, + {0x4188, 0x1D3765DB}, + {0x418C, 0x17C685CA}, + {0x4190, 0x1107C5F3}, + {0x4194, 0x1B5785EB}, + {0x4198, 0x1F97ED8F}, + {0x419C, 0x1BC7A5F3}, + {0x41A0, 0x1FE43595}, + {0x41A4, 0x1EB7D9FC}, + {0x41A8, 0x1FE65DBE}, + {0x41AC, 0x1EC7D9FC}, + {0x41B0, 0x1976FCFF}, + {0x41B4, 0x1F77F5FF}, + {0x41B8, 0x1976FDEC}, + {0x41BC, 0x198664EF}, + {0x41C0, 0x11062D93}, + {0x41C4, 0x10C4E910}, + {0x41C8, 0x1CA759DB}, + {0x41CC, 0x1335A9B5}, + {0x41D0, 0x1097B9F3}, + {0x41D4, 0x17B72DE1}, + {0x41D8, 0x1F67ED42}, + {0x41DC, 0x18074DE9}, + {0x41E0, 0x1FD40547}, + {0x41E4, 0x1D57ADF9}, + {0x41E8, 0x1FE52182}, + {0x41EC, 0x1D67B1F9}, + {0x41F0, 0x14860CE1}, + {0x41F4, 0x1EC7E9FE}, + {0x41F8, 0x14860DD6}, + {0x41FC, 0x195664C7}, + {0x4200, 0x0005E58A}, + {0x4204, 0x00000000}, + {0x4208, 0x00000000}, + {0x420C, 0x7A000000}, + {0x4210, 0x0F9F3D7A}, + {0x4214, 0x0040817C}, + {0x4218, 0x00E10204}, + {0x421C, 0x227D94CD}, + {0x4220, 0x08028A28}, + {0x4224, 0x00000210}, + {0x4228, 0x04688000}, + {0x4A48, 0x00000002}, + {0x422C, 0x0060B002}, + {0x4230, 0x9A8249A8}, + {0x4234, 0x26A1469E}, + {0x4238, 0x2099A824}, + {0x423C, 0x2359461C}, + {0x4240, 0x1631A675}, + {0x4244, 0x2C6B1D63}, + {0x4248, 0x0000000E}, + {0x424C, 0x00000001}, + {0x4250, 0x00000001}, + {0x4254, 0x00000000}, + {0x4258, 0x00000000}, + {0x425C, 0x00000000}, + {0x4260, 0x0020000C}, + {0x4264, 0x00000000}, + {0x4268, 0x00000000}, + {0x426C, 0x0418317C}, + {0x4270, 0x2B33135C}, + {0x4274, 0x00000002}, + {0x4278, 0x00000000}, + {0x427C, 0x00000000}, + {0x4280, 0x00000000}, + {0x4284, 0x00000000}, + {0x4288, 0x00000000}, + {0x428C, 0x00000000}, + {0x4290, 0x00000000}, + {0x4294, 0x00000000}, + {0x4298, 0x00000000}, + {0x429C, 0x84026000}, + {0x42A0, 0x0051AC20}, + {0x4A24, 0x0010C040}, + {0x42A4, 0x02024008}, + {0x42A8, 0x00000000}, + {0x42AC, 0x00000000}, + {0x42B0, 0x22CE803C}, + {0x42B4, 0x32000000}, + {0x42B8, 0x996FD67D}, + {0x42BC, 0xBD67D67D}, + {0x42C0, 0x7D67D65B}, + {0x42C4, 0x28029F59}, + {0x42C8, 0x00280280}, + {0x42CC, 0x00000000}, + {0x42D0, 0x00000000}, + {0x42D4, 0x00000003}, + {0x42D8, 0x00000001}, + {0x42DC, 0x61861800}, + {0x42E0, 0x830C30C3}, + {0x42E4, 0xC30C30C3}, + {0x42E8, 0x830C30C3}, + {0x42EC, 0x451450C3}, + {0x42F0, 0x05145145}, + {0x42F4, 0x05145145}, + {0x42F8, 0x05145145}, + {0x42FC, 0x0F0C3145}, + {0x4300, 0x030C30CF}, + {0x4304, 0x030C30C3}, + {0x4308, 0x030CF3C3}, + {0x430C, 0x030C30C3}, + {0x4310, 0x0F3CF3C3}, + {0x4314, 0x0F3CF3CF}, + {0x4318, 0x0F3CF3CF}, + {0x431C, 0x0F3CF3CF}, + {0x4320, 0x0F3CF3CF}, + {0x4324, 0x030C10C3}, + {0x4328, 0x051430C3}, + {0x432C, 0x051490CB}, + {0x4330, 0x030CD151}, + {0x4334, 0x050C50C7}, + {0x4338, 0x051492CB}, + {0x433C, 0x05145145}, + {0x4340, 0x05145145}, + {0x4344, 0x05145145}, + {0x4348, 0x05145145}, + {0x434C, 0x090CD3CF}, + {0x4350, 0x071491C5}, + {0x4354, 0x073CF143}, + {0x4358, 0x071431C3}, + {0x435C, 0x0F3CF1C5}, + {0x4360, 0x0F3CF3CF}, + {0x4364, 0x0F3CF3CF}, + {0x4368, 0x0F3CF3CF}, + {0x436C, 0x0F3CF3CF}, + {0x4370, 0x090C91CF}, + {0x4374, 0x11243143}, + {0x4378, 0x9777A777}, + {0x437C, 0xBB7BAC95}, + {0x4380, 0xB667B889}, + {0x4384, 0x7B9B8899}, + {0x4388, 0x7A5567C8}, + {0x438C, 0x2278CCCC}, + {0x4390, 0x7C222222}, + {0x4394, 0x0000069B}, + {0x4398, 0x001CCCCC}, + {0x4AAC, 0xCCCCC88C}, + {0x4AB0, 0x0000AACC}, + {0x439C, 0x00000000}, + {0x43A0, 0x00000008}, + {0x43A4, 0x00000000}, + {0x43A8, 0x00000000}, + {0x43AC, 0x00000000}, + {0x43B0, 0x10000000}, + {0x43B4, 0x00401001}, + {0x43B8, 0x00061003}, + {0x43BC, 0x000024D8}, + {0x43C0, 0x00000000}, + {0x43C4, 0x10000020}, + {0x43C8, 0x20000200}, + {0x43CC, 0x00000000}, + {0x43D0, 0x04000000}, + {0x43D4, 0x44000100}, + {0x43D8, 0x60804060}, + {0x43DC, 0x44204210}, + {0x43E0, 0x82108082}, + {0x43E4, 0x82108402}, + {0x43E8, 0xC8082108}, + {0x43EC, 0xC8202084}, + {0x43F0, 0x44208208}, + {0x43F4, 0x84108204}, + {0x43F8, 0xD0108104}, + {0x43FC, 0xF8210108}, + {0x4400, 0x6431E930}, + {0x4404, 0x02309468}, + {0x4408, 0x10C61C22}, + {0x440C, 0x02109469}, + {0x4410, 0x10C61C22}, + {0x4414, 0x00041049}, + {0x4A4C, 0x00060581}, + {0x4418, 0x00000000}, + {0x441C, 0x00000000}, + {0x4420, 0x6C000000}, + {0x4424, 0xB0200020}, + {0x4428, 0x00001FF0}, + {0x442C, 0x00000000}, + {0x4430, 0x00000000}, + {0x4434, 0x00000000}, + {0x4438, 0x00000000}, + {0x443C, 0x190642D0}, + {0x4440, 0xA80668A0}, + {0x4444, 0x60900820}, + {0x4448, 0x9F28518C}, + {0x444C, 0x32488A62}, + {0x4450, 0x9C6E36DC}, + {0x4454, 0x0000F52B}, + {0x4458, 0x00000000}, + {0x445C, 0x4801442E}, + {0x4460, 0x0051A0B8}, + {0x4464, 0x00000000}, + {0x4468, 0x00000000}, + {0x446C, 0x00000000}, + {0x4470, 0x00000000}, + {0x4474, 0x00000000}, + {0x4478, 0x00000000}, + {0x447C, 0x00000000}, + {0x4480, 0x2A0A6040}, + {0x4484, 0x0A0A6829}, + {0x4488, 0x00000004}, + {0x448C, 0x00000000}, + {0x4490, 0x80000000}, + {0x4494, 0x10000000}, + {0x4498, 0xE0000000}, + {0x4AB4, 0x00000000}, + {0x449C, 0x0000001E}, + {0x44A0, 0x02B2C3A6}, + {0x44A4, 0x00000400}, + {0x44A8, 0x00000001}, + {0x44AC, 0x000190C0}, + {0x44B0, 0x00000000}, + {0x44B4, 0x00000000}, + {0x44B8, 0x00000000}, + {0x44BC, 0x00000000}, + {0x44C0, 0x00000000}, + {0x44C4, 0x00000000}, + {0x44C8, 0x00000000}, + {0x44CC, 0x00000000}, + {0x44D0, 0x00000000}, + {0x44D4, 0x00000000}, + {0x44D8, 0x00000000}, + {0x44DC, 0x00000000}, + {0x44E0, 0x00000000}, + {0x44E4, 0x00000000}, + {0x44E8, 0x00000000}, + {0x44EC, 0x00000000}, + {0x44F0, 0x00000000}, + {0x44F4, 0x00000000}, + {0x44F8, 0x00000000}, + {0x44FC, 0x00000000}, + {0x4500, 0x00000000}, + {0x4504, 0x00000000}, + {0x4508, 0x00000000}, + {0x450C, 0x00000000}, + {0x4510, 0x00000000}, + {0x4514, 0x00000000}, + {0x4518, 0x00000000}, + {0x451C, 0x00000000}, + {0x4520, 0x00000000}, + {0x4524, 0x00000000}, + {0x4528, 0x00000000}, + {0x452C, 0x00000000}, + {0x4530, 0x4E830171}, + {0x4534, 0x00000870}, + {0x4538, 0x000000FF}, + {0x453C, 0x00000000}, + {0x4540, 0x00000000}, + {0x4544, 0x00000000}, + {0x4548, 0x00000000}, + {0x454C, 0x00000000}, + {0x4550, 0x00000000}, + {0x4554, 0x00000000}, + {0x4558, 0x00000000}, + {0x455C, 0x00000000}, + {0x4560, 0x40000000}, + {0x4564, 0x40000000}, + {0x4568, 0x00000000}, + {0x456C, 0x20000000}, + {0x4570, 0x04F040BB}, + {0x4574, 0x000E53FF}, + {0x4578, 0x000205CB}, + {0x457C, 0x00200000}, + {0x4580, 0x00000040}, + {0x4584, 0x00000000}, + {0x4588, 0x00000017}, + {0x458C, 0x30000000}, + {0x4590, 0x00000000}, + {0x4594, 0x00000000}, + {0x4598, 0x00000001}, + {0x459C, 0x0003FE00}, + {0x45A0, 0x00000086}, + {0x45A4, 0x00000000}, + {0x45A8, 0xC00001C0}, + {0x45AC, 0x78038000}, + {0x45B0, 0x8000004A}, + {0x45B4, 0x04094800}, + {0x45B8, 0x00280002}, + {0x45BC, 0x06748790}, + {0x45C0, 0x80000000}, + {0x45C4, 0x00000000}, + {0x45C8, 0x00000000}, + {0x45CC, 0x00558670}, + {0x45D0, 0x002883F0}, + {0x45D4, 0x00090120}, + {0x45D8, 0x00000000}, + {0x45E0, 0xA3A6D3C4}, + {0x45E4, 0xAB27B126}, + {0x45E8, 0x00006778}, + {0x45F4, 0x000001B5}, + {0x45EC, 0x11110F0A}, + {0x45F0, 0x00000003}, + {0x4A0C, 0x0000000A}, + {0x45F8, 0x0058BC3F}, + {0x45FC, 0x00000003}, + {0x462C, 0x00000020}, + {0x4600, 0x000003D9}, + {0x45F0, 0x00000004}, + {0x4604, 0x002B1CB0}, + {0x4A50, 0xC0000000}, + {0x4A54, 0x00001000}, + {0x4A58, 0x00000000}, + {0x4A18, 0x00000024}, + {0x4608, 0x00000001}, + {0x460C, 0x00000000}, + {0x4A10, 0x00000001}, + {0x4610, 0x00000001}, + {0x4614, 0x16E5298F}, + {0x4618, 0x18C6294A}, + {0x461C, 0x0E06318A}, + {0x4620, 0x0E539CE5}, + {0x4624, 0x00019287}, + {0x4A14, 0x000000BF}, + {0x4628, 0x00000001}, + {0x4630, 0x000001AA}, + {0x4A18, 0x00001900}, + {0x4A1C, 0x000002A6}, + {0x4634, 0x000000A3}, + {0x4A20, 0x00000086}, + {0x4638, 0x01986456}, + {0x49F8, 0x00000000}, + {0x463C, 0x00000000}, + {0x4640, 0x00000000}, + {0x4644, 0x00C8CC00}, + {0x4648, 0xC400B6B6}, + {0x464C, 0xDC400FC0}, + {0x4A8C, 0x00000110}, + {0x4650, 0x08882550}, + {0x4654, 0x08CC2660}, + {0x4658, 0x09102660}, + {0x465C, 0x00000154}, + {0x45DC, 0xC39E38E8}, + {0x4660, 0x452607E6}, + {0x4664, 0x6750DC65}, + {0x4668, 0xF3F0F1ED}, + {0x466C, 0x30141506}, + {0x4670, 0x2C2B2B2B}, + {0x4674, 0x2C2C2C2C}, + {0x4678, 0xDDB738E8}, + {0x467C, 0x543618FB}, + {0x4680, 0x4F31DC6F}, + {0x4684, 0xFBEBDA00}, + {0x4688, 0x1A10FF04}, + {0x468C, 0x282A3000}, + {0x4690, 0x2A29292A}, + {0x4694, 0x04FA2A2A}, + {0x4698, 0xEE0F04D1}, + {0x469C, 0x99E91436}, + {0x46A0, 0x0701E79E}, + {0x46A4, 0x08D77CFF}, + {0x46A8, 0x2212FF14}, + {0x46AC, 0x60322437}, + {0x46B0, 0x63666666}, + {0x46B4, 0x35374425}, + {0x46B8, 0x35883042}, + {0x46BC, 0x5177C252}, + {0x4720, 0x7FFFFD63}, + {0x4724, 0xB58D11FF}, + {0x4728, 0x07FFFFFF}, + {0x472C, 0x0E7893B6}, + {0x4730, 0xE0391201}, + {0x4734, 0x00000020}, + {0x4738, 0x8325C500}, + {0x473C, 0x00000B7F}, + {0x46C0, 0x00000000}, + {0x46C4, 0x00000000}, + {0x46C8, 0x00000219}, + {0x46CC, 0x00000000}, + {0x46D0, 0x00000000}, + {0x46D4, 0x00000001}, + {0x46D8, 0x00000001}, + {0x46DC, 0x00000000}, + {0x46E0, 0x00000000}, + {0x46E4, 0x00000151}, + {0x46E8, 0x00000498}, + {0x46EC, 0x00000498}, + {0x46F0, 0x00000000}, + {0x46F4, 0x00000000}, + {0x46F8, 0x00001146}, + {0x46FC, 0x00000000}, + {0x4700, 0x00000000}, + {0x4704, 0x00C8CC00}, + {0x4708, 0xC400B6B6}, + {0x470C, 0xDC400FC0}, + {0x4A90, 0x00000110}, + {0x4710, 0x08882550}, + {0x4714, 0x08CC2660}, + {0x4718, 0x09102660}, + {0x471C, 0x00000154}, + {0x4740, 0xC69F38E8}, + {0x4744, 0x462709E9}, + {0x4748, 0x6750DC67}, + {0x474C, 0xF3F0F1ED}, + {0x4750, 0x30141506}, + {0x4754, 0x2C2B2B2B}, + {0x4758, 0x2C2C2C2C}, + {0x475C, 0xE0B738E8}, + {0x4760, 0x52381BFE}, + {0x4764, 0x5031DC6C}, + {0x4768, 0xFBEBDA00}, + {0x476C, 0x1A10FF04}, + {0x4770, 0x282A3000}, + {0x4774, 0x2A29292A}, + {0x4778, 0x04FA2A2A}, + {0x477C, 0xEE0F04D1}, + {0x49F0, 0x99E91436}, + {0x49F4, 0x0701E79E}, + {0x49FC, 0x08D77CFF}, + {0x4A5C, 0x2212FF14}, + {0x4A60, 0x60322437}, + {0x4A64, 0x63666666}, + {0x4A68, 0x35374425}, + {0x4A6C, 0x35883042}, + {0x4A70, 0x5177C252}, + {0x4A74, 0x7FFFFD63}, + {0x4A78, 0xB58D11FF}, + {0x4A7C, 0x07FFFFFF}, + {0x4A80, 0x0E7893B6}, + {0x4A9C, 0xE0391201}, + {0x4AA0, 0x00000020}, + {0x4AA4, 0x8325C500}, + {0x4AA8, 0x00000B7F}, + {0x4780, 0x00000000}, + {0x4784, 0x00000000}, + {0x4788, 0x00000219}, + {0x478C, 0x00000000}, + {0x4790, 0x00000000}, + {0x4794, 0x00000001}, + {0x4798, 0x00000001}, + {0x479C, 0x00000000}, + {0x47A0, 0x00000000}, + {0x47A4, 0x00000151}, + {0x47A8, 0x00000498}, + {0x47AC, 0x00000498}, + {0x47B0, 0x00000000}, + {0x47B4, 0x00000000}, + {0x47B8, 0x00001146}, + {0x47BC, 0x00000002}, + {0x47C0, 0x00000002}, + {0x47C4, 0x00000000}, + {0x47C8, 0xA32103FE}, + {0x47CC, 0xB20A5328}, + {0x47D0, 0xC686314F}, + {0x47D4, 0x000005D7}, + {0x47D8, 0x009B902A}, + {0x47DC, 0x009B902A}, + {0x47E0, 0x98682C18}, + {0x47E4, 0x6308C4C1}, + {0x47E8, 0x6248C631}, + {0x47EC, 0x922A8253}, + {0x47F0, 0x00000005}, + {0x47F4, 0x00001759}, + {0x47F8, 0x4BB02000}, + {0x47FC, 0x831408BE}, + {0x4A84, 0x000000E9}, + {0x4800, 0x9ABBCACB}, + {0x4804, 0x56767578}, + {0x4808, 0xBCCBBB13}, + {0x480C, 0x7889989B}, + {0x4810, 0xBBB0F455}, + {0x4814, 0x777BBBBB}, + {0x4818, 0x15277777}, + {0x481C, 0x27039CE9}, + {0x4820, 0x42424432}, + {0x4824, 0x36058342}, + {0x4828, 0x00000006}, + {0x482C, 0x00000005}, + {0x4830, 0x00000005}, + {0x4834, 0xC7013016}, + {0x4838, 0x84413016}, + {0x483C, 0x84413016}, + {0x4840, 0x8C413016}, + {0x4844, 0x8C40B028}, + {0x4848, 0x3140B028}, + {0x484C, 0x2940B028}, + {0x4850, 0x8440B028}, + {0x4854, 0x2318C610}, + {0x4858, 0x45344753}, + {0x485C, 0x236A6A88}, + {0x4860, 0xAC8DF814}, + {0x4864, 0x08877ACB}, + {0x4868, 0x000107AA}, + {0x4A94, 0x00000000}, + {0x486C, 0xBCEB4A14}, + {0x4870, 0x000A3A4A}, + {0x4874, 0xBCEB4A14}, + {0x4878, 0x000A3A4A}, + {0x487C, 0xBCBDBD85}, + {0x4880, 0x0CABB99A}, + {0x4884, 0x38384242}, + {0x4888, 0x0086102E}, + {0x488C, 0xCA24C82A}, + {0x4890, 0x00008A62}, + {0x4894, 0x00000008}, + {0x4898, 0x009B902A}, + {0x489C, 0x009B902A}, + {0x48A0, 0x98682C18}, + {0x48A4, 0x6308C4C1}, + {0x48A8, 0x6248C631}, + {0x48AC, 0x922A8253}, + {0x48B0, 0x00000005}, + {0x48B4, 0x00001759}, + {0x48B8, 0x4BA02000}, + {0x48BC, 0x831408BE}, + {0x4A88, 0x000000E9}, + {0x48C0, 0x9898A8BB}, + {0x48C4, 0x54535368}, + {0x48C8, 0x99999B13}, + {0x48CC, 0x55555899}, + {0x48D0, 0xBBB07453}, + {0x48D4, 0x777BBBBB}, + {0x48D8, 0x15277777}, + {0x48DC, 0x27039CE9}, + {0x48E0, 0x31413432}, + {0x48E4, 0x36058342}, + {0x48E8, 0x00000006}, + {0x48EC, 0x00000005}, + {0x48F0, 0x00000005}, + {0x48F4, 0xC7013016}, + {0x48F8, 0x84413016}, + {0x48FC, 0x84413016}, + {0x4900, 0x8C413016}, + {0x4904, 0x8C40B028}, + {0x4908, 0x3140B028}, + {0x490C, 0x2940B028}, + {0x4910, 0x8440B028}, + {0x4914, 0x2318C610}, + {0x4918, 0x45334753}, + {0x491C, 0x236A6A88}, + {0x4920, 0xAC8DF814}, + {0x4924, 0x08877ACB}, + {0x4928, 0x000007AA}, + {0x4A98, 0x00000000}, + {0x492C, 0xBCEB4A14}, + {0x4930, 0x000A3A4A}, + {0x4934, 0xBCEB4A14}, + {0x4938, 0x000A3A4A}, + {0x493C, 0x9A8A8A85}, + {0x4940, 0x0CA3B99A}, + {0x4944, 0x38384242}, + {0x4948, 0x8086102E}, + {0x494C, 0xCA24C82A}, + {0x4950, 0x00008A62}, + {0x4954, 0x00000008}, + {0x4958, 0x80040000}, + {0x495C, 0x80040000}, + {0x4960, 0xFE800000}, + {0x4964, 0x834C0000}, + {0x4968, 0x00000000}, + {0x496C, 0x00000000}, + {0x4970, 0x00000000}, + {0x4974, 0x00000000}, + {0x4978, 0x00000000}, + {0x497C, 0x00000000}, + {0x4980, 0x40000000}, + {0x4984, 0x00000000}, + {0x4988, 0x00000000}, + {0x498C, 0x00000000}, + {0x4990, 0x00000000}, + {0x4994, 0x04065800}, + {0x4998, 0x02004080}, + {0x499C, 0x0E1E3E05}, + {0x49A0, 0x0A163068}, + {0x49A4, 0x00206040}, + {0x49A8, 0x02020202}, + {0x49AC, 0x00002020}, + {0x49B0, 0xF8F8F418}, + {0x49B4, 0xF8E8F8F8}, + {0x49B8, 0xF80808E8}, + {0x4A00, 0xF8F8FA00}, + {0x4A04, 0xFAFAFAF8}, + {0x4A08, 0xFAFAFAFA}, + {0x4A28, 0xFAFAFAFA}, + {0x4A2C, 0xFAFAFAFA}, + {0x4A30, 0xFAFAFAFA}, + {0x4A34, 0xFAFAFAFA}, + {0x4A38, 0xFAFAFAFA}, + {0x4A3C, 0xFAFAFAFA}, + {0x4A40, 0xFAFAFAFA}, + {0x4A44, 0x0000FAFA}, + {0x49BC, 0x00000000}, + {0x49C0, 0x800CD62D}, + {0x49C4, 0x00000103}, + {0x49C8, 0x00000000}, + {0x49CC, 0x00000000}, + {0x49D0, 0x00000000}, + {0x49D4, 0x00000000}, + {0x49D8, 0x00000000}, + {0x49DC, 0x00000000}, + {0x49E0, 0x00000000}, + {0x49E4, 0x00000000}, + {0x49E8, 0x00000000}, + {0x49EC, 0x00000000}, + {0x994, 0x00000010}, + {0x904, 0x00000005}, + {0xC3C, 0x2840E1BF}, + {0xC40, 0x00000000}, + {0xC44, 0x00000007}, + {0xC48, 0x410E4000}, + {0xC54, 0x1EE14368}, + {0xC58, 0x41000000}, + {0x730, 0x00000002}, + {0xC60, 0x017FFFF2}, + {0xC64, 0x0010A130}, + {0xC68, 0x10000050}, + {0xC6C, 0x10001021}, + {0x708, 0x00000000}, + {0x884, 0x0043F01D}, + {0x704, 0x601E0100}, + {0x710, 0xEF810000}, + {0x704, 0x601E0100}, + {0xD40, 0xF64FA0F7}, + {0xD44, 0x0400063F}, + {0xD48, 0x0003FF7F}, + {0xD4C, 0x00000000}, + {0xD50, 0xF64FA0F7}, + {0xD54, 0x04100437}, + {0xD58, 0x0000FF7F}, + {0xD5C, 0x00000000}, + {0xD60, 0x00000000}, + {0xD64, 0x00000000}, + {0xD70, 0x00000015}, + {0xD90, 0x000003FF}, + {0xD94, 0x00000000}, + {0xD98, 0x0000003F}, + {0xD9C, 0x00000000}, + {0xDA0, 0x000003FE}, + {0xDA4, 0x00000000}, + {0xDA8, 0x0000003F}, + {0xDAC, 0x00000000}, + {0xD00, 0x77777777}, + {0xD04, 0xBBBBBBBB}, + {0xD08, 0xBBBBBBBB}, + {0xD0C, 0x00000070}, + {0xD10, 0x20110900}, + {0xD10, 0x20110FFF}, + {0xD78, 0x00000001}, + {0xD7C, 0x001D050E}, + {0xD84, 0x00004207}, + {0xD18, 0x50209900}, + {0xD80, 0x00804100}, + {0x718, 0x1333233F}, + {0x604, 0x041E1E1E}, + {0x714, 0x00010000}, + {0x586C, 0x000000F0}, + {0x586C, 0x000000E0}, + {0x586C, 0x000000D0}, + {0x586C, 0x000000C0}, + {0x586C, 0x000000B0}, + {0x586C, 0x000000A0}, + {0x586C, 0x00000090}, + {0x586C, 0x00000080}, + {0x586C, 0x00000070}, + {0x586C, 0x00000060}, + {0x586C, 0x00000050}, + {0x586C, 0x00000040}, + {0x586C, 0x00000030}, + {0x586C, 0x00000020}, + {0x586C, 0x00000010}, + {0x586C, 0x00000000}, + {0x786C, 0x000000F0}, + {0x786C, 0x000000E0}, + {0x786C, 0x000000D0}, + {0x786C, 0x000000C0}, + {0x786C, 0x000000B0}, + {0x786C, 0x000000A0}, + {0x786C, 0x00000090}, + {0x786C, 0x00000080}, + {0x786C, 0x00000070}, + {0x786C, 0x00000060}, + {0x786C, 0x00000050}, + {0x786C, 0x00000040}, + {0x786C, 0x00000030}, + {0x786C, 0x00000020}, + {0x786C, 0x00000010}, + {0x786C, 0x00000000}, + {0xC0D4, 0x4486888C}, + {0xC0D8, 0xC6BA10E1}, + {0xC0DC, 0x30C52868}, + {0xC0E0, 0x05008128}, + {0xC0E4, 0x0000A72B}, + {0xC1D4, 0x4486888C}, + {0xC1D8, 0xC6BA10E1}, + {0xC1DC, 0x30C52868}, + {0xC1E0, 0x05008128}, + {0xC1E4, 0x0000A72B}, + {0xC0EC, 0x00000000}, + {0xC0E4, 0x0000272B}, + {0xC1EC, 0x00000000}, + {0xC1E4, 0x0000272B}, + {0x334, 0xFFFFFFFF}, + {0x33C, 0x55000000}, + {0x340, 0x00005555}, + {0x724, 0x00111200}, + {0x5868, 0xA9550000}, + {0x5870, 0x33221100}, + {0x5874, 0x77665544}, + {0x5878, 0xBBAA9988}, + {0x587C, 0xFFEEDDCC}, + {0x5880, 0x76543210}, + {0x5884, 0xFEDCBA98}, + {0x5888, 0x00000000}, + {0x588C, 0x00000000}, + {0x5894, 0x00000008}, + {0x7868, 0xA9550000}, + {0x7870, 0x33221100}, + {0x7874, 0x77665544}, + {0x7878, 0xBBAA9988}, + {0x787C, 0xFFEEDDCC}, + {0x7880, 0x76543210}, + {0x7884, 0xFEDCBA98}, + {0x7888, 0x00000000}, + {0x788C, 0x00000000}, + {0x7894, 0x00000008}, + {0x650, 0x00200888}, + {0x710, 0xF3810000}, + {0x020, 0x0000F381}, + {0x024, 0x0000F381}, + {0x000, 0xC580801E}, + {0xC70, 0x00000400}, + {0x980, 0x10002250}, + {0x988, 0x3C3C4107}, + {0x994, 0x00000010}, + {0x2994, 0x00000010}, + {0x000, 0x0580801F}, + {0x240C, 0x00000000}, + {0x640, 0x140A141E}, + {0x640, 0x1414141E}, + {0x640, 0x1414141E}, + {0x644, 0x3414283C}, + {0x644, 0x3425283C}, + {0x644, 0x3426283C}, + {0x2640, 0x140A141E}, + {0x2640, 0x1414141E}, + {0x2640, 0x1414141E}, + {0x2644, 0x3414283C}, + {0x2644, 0x3425283C}, + {0x2644, 0x3425183C}, + {0x2300, 0x02748790}, + {0x2304, 0x00558670}, + {0x2308, 0x002883F0}, + {0x230C, 0x00090120}, + {0x2310, 0x00000000}, + {0x2314, 0x06000000}, + {0x2318, 0x00000000}, + {0x231C, 0x00000000}, + {0x2320, 0x03020100}, + {0x2324, 0x07060504}, + {0x2328, 0x0B0A0908}, + {0x232C, 0x0F0E0D0C}, + {0x2330, 0x13121110}, + {0x2334, 0x17161514}, + {0x2338, 0x0C700022}, + {0x233C, 0x0A0529D0}, + {0x2340, 0x000529D0}, + {0x2344, 0x0006318A}, + {0x2348, 0xB7E6318A}, + {0x234C, 0x80039C00}, + {0x2350, 0x80039C00}, + {0x2354, 0x0005298F}, + {0x2358, 0x0015296E}, + {0x235C, 0x0C07FC31}, + {0x2360, 0x0219AAAE}, + {0x2364, 0xE4F624C3}, + {0x2368, 0x53626F15}, + {0x236C, 0x48000000}, + {0x2370, 0x48000000}, + {0x2374, 0x07540000}, + {0x2378, 0x202401B9}, + {0x237C, 0x00F7000E}, + {0x2380, 0x0F0A1111}, + {0x2384, 0x30D9000F}, + {0x2388, 0x0200EA02}, + {0x238C, 0x003CB061}, + {0x2390, 0x69C00000}, + {0x2394, 0x00000000}, + {0x2398, 0x000000F0}, + {0x239C, 0x0001FFFF}, + {0x23A0, 0x00C80064}, + {0x23A4, 0x0190012C}, + {0x23A8, 0x001917BE}, + {0x23AC, 0x0B30880C}, + {0x23B0, 0x9281CE00}, + {0x23B4, 0x7F027C00}, + {0x704, 0x601E0102}, + {0x704, 0x601E0102}, + {0x5864, 0x080801FF}, + {0x7864, 0x080801FF}, + {0xC60, 0x017FFFF3}, + {0x58AC, 0x08000000}, + {0x78AC, 0x08000000}, + {0x8088, 0x007F0000}, + {0x81A4, 0x003F3A00}, + {0x81B4, 0x0100007F}, + {0x81C0, 0x0060010B}, + {0x81A0, 0x00000010}, + {0x8138, 0x00000002}, + {0x82A4, 0x003F3A00}, + {0x82B4, 0x0100007F}, + {0x82C0, 0x0060010B}, + {0x82A0, 0x00000010}, + {0x81A0, 0x00000010}, + {0x8238, 0x00000002}, + {0x8088, 0x00000000}, + {0x8020, 0x00000000}, + {0x8120, 0x00000000}, + {0x8220, 0x00000000}, + {0x8124, 0x00000F0F}, + {0x8224, 0x00000F0F}, + {0x5864, 0x180801FF}, + {0x7864, 0x180801FF}, + {0xC60, 0x017FFFF3}, + {0xC70, 0x00000600}, + {0xC70, 0x00000660}, + {0x58AC, 0x08000000}, + {0x78AC, 0x08000000}, + {0x8120, 0x10000000}, + {0x8120, 0x10030000}, + {0x8124, 0x00000F0F}, + {0x8124, 0x00000F0F}, + {0x8224, 0x00000F0F}, + {0x8224, 0x00000F0F}, + {0x8220, 0x10000000}, + {0x8220, 0x10030000}, + {0x704, 0x601E0100}, + {0x5864, 0x100801FF}, + {0x7864, 0x100801FF}, + {0x5864, 0x180801FF}, + {0x7864, 0x180801FF}, + {0x58D4, 0x7401FE00}, + {0x78D4, 0x7401FE00}, + {0x58F0, 0x400401FF}, + {0x78F0, 0x400401FF}, + {0x58F0, 0x400401FF}, + {0x78F0, 0x400401FF}, + {0x704, 0x601E0102}, + {0xC7C, 0x0020BFE0}, + {0x58C0, 0x00FE0000}, + {0x58FC, 0x00000000}, + {0x566C, 0x00000005}, + {0x566C, 0x00001005}, + {0x78C0, 0x00FE0000}, + {0x78FC, 0x00000000}, + {0x700, 0x00000030}, + {0x704, 0x601E0102}, + {0x704, 0x601E0100}, + {0x704, 0x601E0502}, + {0x20FC, 0x00000000}, + {0x20F8, 0x00000000}, + {0x20F0, 0x00000000}, + {0x9C0, 0x00000001}, + {0x9C0, 0x00000000}, + {0x9C0, 0x00000001}, + {0x9C0, 0x00000000}, + {0x4AE8, 0x00000744}, + {0x4AF0, 0x00000744}, + {0x1010, 0x00000010}, + {0x3010, 0x00000010}, + {0x4AD4, 0x00000040}, + {0x4AE0, 0x00000040}, + {0x4AE4, 0x0079E99E}, + {0x4AEC, 0x0079E99E}, + {0x300, 0xF30CE31C}, + {0x304, 0x13EF1F19}, + {0x308, 0x0C0CF3F3}, + {0x30C, 0x0C0C0C0C}, + {0x310, 0x80496000}, + {0x314, 0x0041E000}, + {0x318, 0x20022042}, + {0x31C, 0x20448009}, + {0x320, 0x00010031}, + {0x324, 0xE000E000}, + {0x328, 0xE000E000}, + {0x32C, 0xE000E000}, + {0x12BC, 0x10104041}, + {0x12C0, 0x14411111}, + {0x32BC, 0x10104041}, + {0x32C0, 0x14411111}, + {0x010, 0x0005FFFF}, + {0x028, 0x0000F381}, + {0x02C, 0x0000F381}, + {0x620, 0x00141230}, + {0x704, 0x601C05FF}, + {0x720, 0x20000000}, + {0x738, 0x004100CC}, + {0x12A0, 0x24903056}, + {0x12AC, 0x12333121}, + {0x12B8, 0x30020000}, + {0x12E4, 0x30D52A68}, + {0x2000, 0x50BBBF04}, + {0x32A0, 0x24903056}, + {0x32AC, 0x12333121}, + {0x32B8, 0x30020000}, + {0x32E4, 0x30D52A68}, + {0x5800, 0x03FF807F}, + {0x5804, 0x04237040}, + {0x5808, 0x04237040}, + {0x7800, 0x03FF807F}, + {0x7804, 0x04237040}, + {0x7808, 0x04237040}, + {0x73C, 0x00000002}, + {0x74C, 0x00000001}, + {0x748, 0x00000002}, + {0x5818, 0x082C1800}, + {0x7818, 0x082C1800}, + {0x624, 0x0101030A}, + {0xC14, 0x85010000}, + {0xDD4, 0x00000001}, + {0x241C, 0x00000001}, + {0x1200, 0x00010142}, + {0x3200, 0x00010142}, + {0xC0F8, 0x00000001}, + {0xC1F8, 0x00000001}, + {0x35C, 0x000004C4}, + {0x0F0, 0x00000002}, + {0x0F4, 0x00000028}, + {0x0F8, 0x20220408}, +}; + +static const struct rtw89_reg2_def rtw89_8852b_phy_bb_reg_gain[] = { + {0x000, 0x18FBDDB7}, + {0x001, 0x006F5436}, + {0x002, 0x00004F31}, + {0x100, 0x1BFEE0B7}, + {0x101, 0x006C5238}, + {0x102, 0x00005031}, + {0x10000, 0x07E6C39E}, + {0x10001, 0x00654526}, + {0x10002, 0x00006750}, + {0x10100, 0x09E9C69F}, + {0x10101, 0x00674627}, + {0x10102, 0x00006750}, + {0x20000, 0x06E8C49F}, + {0x20001, 0x00654526}, + {0x20002, 0x00006750}, + {0x20100, 0x07E9C6A0}, + {0x20101, 0x00674728}, + {0x20102, 0x00006850}, + {0x30000, 0x04E5C39D}, + {0x30001, 0x00634325}, + {0x30002, 0x00006750}, + {0x30100, 0x06E9C69F}, + {0x30101, 0x00654527}, + {0x30102, 0x00006750}, + {0x1000000, 0x000000F4}, + {0x1000010, 0x000000F8}, + {0x1000011, 0x0000F8F8}, + {0x1000100, 0x000000F8}, + {0x1000110, 0x00000000}, + {0x1000111, 0x00000000}, + {0x1010000, 0x000000F4}, + {0x1010010, 0x000000F8}, + {0x1010011, 0x0000F8F8}, + {0x1010020, 0x000000F8}, + {0x1010021, 0x0808E8E8}, + {0x1010029, 0x0000F8F8}, + {0x1010100, 0x000000F4}, + {0x1010110, 0x000000F8}, + {0x1010111, 0x0000F8F8}, + {0x1010120, 0x000000F8}, + {0x1010121, 0x0808E8E8}, + {0x1010129, 0x0000F8F8}, + {0x1020000, 0x000000F4}, + {0x1020010, 0x000000F8}, + {0x1020011, 0x0000F8F8}, + {0x1020020, 0x000000F8}, + {0x1020021, 0x0808E8E8}, + {0x1020029, 0x0000F8F8}, + {0x1020100, 0x000000F4}, + {0x1020110, 0x000000F8}, + {0x1020111, 0x0000F8F8}, + {0x1020120, 0x000000F8}, + {0x1020121, 0x0808E8E8}, + {0x1020129, 0x0000F8F8}, + {0x1030000, 0x000000F4}, + {0x1030010, 0x000000F8}, + {0x1030011, 0x0000F8F8}, + {0x1030020, 0x000000F8}, + {0x1030021, 0x0808E8E8}, + {0x1030029, 0x0000F8F8}, + {0x1030100, 0x000000F4}, + {0x1030110, 0x000000F8}, + {0x1030111, 0x0000F8F8}, + {0x1030120, 0x000000F8}, + {0x1030121, 0x0808E8E8}, + {0x1030129, 0x0000F8F8}, +}; + +static const struct rtw89_reg2_def rtw89_8852b_phy_radioa_regs[] = { + {0xF0010000, 0x00000000}, + {0xF0020000, 0x00000001}, + {0xF0010001, 0x00000002}, + {0xF0020001, 0x00000003}, + {0xF0030001, 0x00000004}, + {0xF0040001, 0x00000005}, + {0xF0050001, 0x00000006}, + {0xF0060001, 0x00000007}, + {0xF0070001, 0x00000008}, + {0xF0080001, 0x00000009}, + {0xF0290001, 0x0000000A}, + {0xF02B0001, 0x0000000B}, + {0x005, 0x00000000}, + {0x000, 0x00030000}, + {0x10000, 0x00030000}, + {0x018, 0x00011124}, + {0x10018, 0x00011124}, + {0x000, 0x00033C00}, + {0x10000, 0x00033C00}, + {0x01A, 0x00040004}, + {0x011, 0x00014073}, + {0x067, 0x00000070}, + {0x059, 0x000A0000}, + {0x066, 0x00000100}, + {0x057, 0x0000D589}, + {0x05A, 0x0007FFFF}, + {0x0A4, 0x0006FF12}, + {0x043, 0x00005000}, + {0x0E1, 0x00000001}, + {0x0DD, 0x000001A0}, + {0x0CA, 0x00002000}, + {0x0D3, 0x00000003}, + {0x0B3, 0x0004EFE0}, + {0x0B4, 0x0007C07E}, + {0x0B5, 0x0003A701}, + {0x0B6, 0x000581E0}, + {0x0B7, 0x00001A0A}, + {0x0BB, 0x000C7000}, + {0x0ED, 0x00000400}, + {0x033, 0x00000000}, + {0x03F, 0x00000543}, + {0x033, 0x00000001}, + {0x03F, 0x00000542}, + {0x033, 0x00000002}, + {0x03F, 0x00000541}, + {0x033, 0x00000003}, + {0x03F, 0x00000521}, + {0x033, 0x00000004}, + {0x03F, 0x00000343}, + {0x033, 0x00000005}, + {0x03F, 0x00000342}, + {0x033, 0x00000006}, + {0x03F, 0x00000341}, + {0x033, 0x00000007}, + {0x03F, 0x00000321}, + {0x033, 0x00000008}, + {0x03F, 0x000005C3}, + {0x033, 0x00000009}, + {0x03F, 0x000005C2}, + {0x033, 0x0000000A}, + {0x03F, 0x000005C1}, + {0x033, 0x0000000B}, + {0x03F, 0x000005A1}, + {0x033, 0x0000000C}, + {0x03F, 0x000002C3}, + {0x033, 0x0000000D}, + {0x03F, 0x000002C2}, + {0x033, 0x0000000E}, + {0x03F, 0x000002C1}, + {0x033, 0x0000000F}, + {0x03F, 0x000002A1}, + {0x0ED, 0x00000000}, + {0x0ED, 0x00002000}, + {0x033, 0x00000002}, + {0x03D, 0x0004A883}, + {0x03E, 0x00000000}, + {0x03F, 0x00000001}, + {0x033, 0x00000006}, + {0x03D, 0x0004A883}, + {0x03E, 0x00000000}, + {0x03F, 0x00000001}, + {0x0ED, 0x00000000}, + {0x018, 0x00001001}, + {0x10018, 0x00001001}, + {0x002, 0x0000000D}, + {0x10002, 0x0000000D}, + {0x0EE, 0x00000004}, + {0x033, 0x0000000B}, + {0x03F, 0x0000000B}, + {0x033, 0x0000000C}, + {0x03F, 0x00000012}, + {0x033, 0x0000000D}, + {0x03F, 0x00000019}, + {0x0EE, 0x00000000}, + {0x08F, 0x000D0F7A}, + {0x0EF, 0x00080000}, + {0x033, 0x00000008}, + {0x03E, 0x000000C4}, + {0x03F, 0x000034C0}, + {0x033, 0x0000000A}, + {0x03E, 0x000000C4}, + {0x03F, 0x000035D0}, + {0x033, 0x0000000B}, + {0x03E, 0x000000C4}, + {0x03F, 0x000035C8}, + {0x033, 0x0000008A}, + {0x03E, 0x000000C4}, + {0x03F, 0x000035F7}, + {0x0EF, 0x00000000}, + {0x08D, 0x000CC800}, + {0x0EF, 0x00004000}, + {0x033, 0x00000006}, + {0x03F, 0x00000700}, + {0x033, 0x00000005}, + {0x03F, 0x00090600}, + {0x033, 0x00000004}, + {0x03F, 0x000A3500}, + {0x033, 0x00000003}, + {0x03F, 0x000A3400}, + {0x033, 0x00000002}, + {0x03F, 0x00008B00}, + {0x033, 0x00000001}, + {0x03F, 0x00001B00}, + {0x033, 0x00000000}, + {0x03F, 0x00003A00}, + {0x033, 0x0000000F}, + {0x03F, 0x00000700}, + {0x033, 0x0000000E}, + {0x03F, 0x00000700}, + {0x033, 0x0000000D}, + {0x03F, 0x00090600}, + {0x033, 0x0000000C}, + {0x03F, 0x000A3500}, + {0x033, 0x0000000B}, + {0x03F, 0x000A3400}, + {0x033, 0x0000000A}, + {0x03F, 0x00008B00}, + {0x033, 0x00000009}, + {0x03F, 0x00001B00}, + {0x033, 0x00000008}, + {0x03F, 0x00003A00}, + {0x0EF, 0x00000000}, + {0x0EE, 0x00000010}, + {0x033, 0x00000006}, + {0x03F, 0x00000003}, + {0x033, 0x00000007}, + {0x03F, 0x00000003}, + {0x033, 0x00000008}, + {0x03F, 0x00000001}, + {0x0EE, 0x00000000}, + {0x0EF, 0x00001000}, + {0x033, 0x00000000}, + {0x03F, 0x00000015}, + {0x033, 0x00000001}, + {0x03F, 0x00000017}, + {0x0EF, 0x00000000}, + {0x0EF, 0x00008000}, + {0x033, 0x00000000}, + {0x03E, 0x00004FC0}, + {0x03F, 0x00000087}, + {0x033, 0x00000001}, + {0x03E, 0x000046C0}, + {0x03F, 0x00000087}, + {0x033, 0x00000002}, + {0x03E, 0x00004240}, + {0x03F, 0x00000087}, + {0x033, 0x00000003}, + {0x03E, 0x00008010}, + {0x03F, 0x00000147}, + {0x033, 0x00000004}, + {0x03E, 0x0000A048}, + {0x03F, 0x0000004F}, + {0x033, 0x00000005}, + {0x03E, 0x0000A030}, + {0x03F, 0x0000005F}, + {0x033, 0x00000006}, + {0x03E, 0x0000A000}, + {0x03F, 0x0000009F}, + {0x033, 0x00000008}, + {0x03E, 0x00004FC0}, + {0x03F, 0x00000087}, + {0x033, 0x00000009}, + {0x03E, 0x000046C0}, + {0x03F, 0x00000087}, + {0x033, 0x0000000A}, + {0x03E, 0x00004240}, + {0x03F, 0x00000087}, + {0x033, 0x0000000B}, + {0x03E, 0x00008010}, + {0x03F, 0x00000147}, + {0x033, 0x0000000C}, + {0x03E, 0x0000A048}, + {0x03F, 0x0000004F}, + {0x033, 0x0000000D}, + {0x03E, 0x0000A030}, + {0x03F, 0x0000005F}, + {0x033, 0x0000000E}, + {0x03E, 0x0000A000}, + {0x03F, 0x0000009F}, + {0x033, 0x00000010}, + {0x03E, 0x00004FC0}, + {0x03F, 0x00000087}, + {0x033, 0x00000011}, + {0x03E, 0x000046C0}, + {0x03F, 0x00000087}, + {0x033, 0x00000012}, + {0x03E, 0x00004240}, + {0x03F, 0x00000087}, + {0x033, 0x00000013}, + {0x03E, 0x00008010}, + {0x03F, 0x00000147}, + {0x033, 0x00000014}, + {0x03E, 0x0000A048}, + {0x03F, 0x0000004F}, + {0x033, 0x00000015}, + {0x03E, 0x0000A030}, + {0x03F, 0x0000005F}, + {0x033, 0x00000016}, + {0x03E, 0x0000A000}, + {0x03F, 0x0000009F}, + {0x033, 0x00000020}, + {0x03E, 0x00004FC0}, + {0x03F, 0x00000087}, + {0x033, 0x00000021}, + {0x03E, 0x000046C0}, + {0x03F, 0x00000087}, + {0x033, 0x00000022}, + {0x03E, 0x00004240}, + {0x03F, 0x00000087}, + {0x033, 0x00000023}, + {0x03E, 0x00008010}, + {0x03F, 0x00000147}, + {0x033, 0x00000024}, + {0x03E, 0x0000A048}, + {0x03F, 0x0000004F}, + {0x033, 0x00000025}, + {0x03E, 0x0000A030}, + {0x03F, 0x0000005F}, + {0x033, 0x00000026}, + {0x03E, 0x0000A000}, + {0x03F, 0x0000009F}, + {0x033, 0x00000028}, + {0x03E, 0x00004FC0}, + {0x03F, 0x00000087}, + {0x033, 0x00000029}, + {0x03E, 0x000046C0}, + {0x03F, 0x00000087}, + {0x033, 0x0000002A}, + {0x03E, 0x00004240}, + {0x03F, 0x00000087}, + {0x033, 0x0000002B}, + {0x03E, 0x00008010}, + {0x03F, 0x00000147}, + {0x033, 0x0000002C}, + {0x03E, 0x0000A048}, + {0x03F, 0x0000004F}, + {0x033, 0x0000002D}, + {0x03E, 0x0000A030}, + {0x03F, 0x0000005F}, + {0x033, 0x0000002E}, + {0x03E, 0x0000A000}, + {0x03F, 0x0000009F}, + {0x033, 0x00000030}, + {0x03E, 0x00004FC0}, + {0x03F, 0x00000087}, + {0x033, 0x00000031}, + {0x03E, 0x000046C0}, + {0x03F, 0x00000087}, + {0x033, 0x00000032}, + {0x03E, 0x00004240}, + {0x03F, 0x00000087}, + {0x033, 0x00000033}, + {0x03E, 0x00008010}, + {0x03F, 0x00000147}, + {0x033, 0x00000034}, + {0x03E, 0x0000A048}, + {0x03F, 0x0000004F}, + {0x033, 0x00000035}, + {0x03E, 0x0000A030}, + {0x03F, 0x0000005F}, + {0x033, 0x00000036}, + {0x03E, 0x0000A000}, + {0x03F, 0x0000009F}, + {0x0EF, 0x00000000}, + {0x0EF, 0x00000100}, + {0x033, 0x00000000}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004376}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004376}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0xA0000000, 0x00000000}, + {0x03F, 0x00004376}, + {0xB0000000, 0x00000000}, + {0x033, 0x00000001}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004376}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004376}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0xA0000000, 0x00000000}, + {0x03F, 0x00004376}, + {0xB0000000, 0x00000000}, + {0x033, 0x00000002}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004376}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004376}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0xA0000000, 0x00000000}, + {0x03F, 0x00004376}, + {0xB0000000, 0x00000000}, + {0x033, 0x00000003}, + {0x03F, 0x00004376}, + {0x033, 0x00000004}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004376}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004376}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0xA0000000, 0x00000000}, + {0x03F, 0x00004376}, + {0xB0000000, 0x00000000}, + {0x033, 0x00000005}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004376}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004376}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004317}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004317}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004317}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004317}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004317}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004317}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004317}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004317}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004317}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004317}, + {0xA0000000, 0x00000000}, + {0x03F, 0x00004376}, + {0xB0000000, 0x00000000}, + {0x033, 0x00000006}, + {0x03F, 0x00004376}, + {0x033, 0x00000007}, + {0x03F, 0x00004376}, + {0x033, 0x00000008}, + {0x03F, 0x00004376}, + {0x033, 0x00000009}, + {0x03F, 0x00004376}, + {0x033, 0x0000000A}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004376}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004376}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x000043A6}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x000043A6}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x000043A6}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x000043A6}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x000043A6}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x000043A6}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x000043A6}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x000043A6}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x000043A6}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x000043A6}, + {0xA0000000, 0x00000000}, + {0x03F, 0x00004376}, + {0xB0000000, 0x00000000}, + {0x033, 0x0000000B}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004376}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004376}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x000043A6}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x000043A6}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x000043A6}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x000043A6}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x000043A6}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x000043A6}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x000043A6}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x000043A6}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x000043A6}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x000043A6}, + {0xA0000000, 0x00000000}, + {0x03F, 0x00004376}, + {0xB0000000, 0x00000000}, + {0x033, 0x0000000C}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004376}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004376}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x000043A6}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x000043A6}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x000043A6}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x000043A6}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x000043A6}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x000043A6}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x000043A6}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x000043A6}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x000043A6}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x000043A6}, + {0xA0000000, 0x00000000}, + {0x03F, 0x00004376}, + {0xB0000000, 0x00000000}, + {0x033, 0x0000000D}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004376}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004376}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x000043A6}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x000043A6}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x000043A6}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x000043A6}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x000043A6}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x000043A6}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x000043A6}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x000043A6}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x000043A6}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x000043A6}, + {0xA0000000, 0x00000000}, + {0x03F, 0x00004376}, + {0xB0000000, 0x00000000}, + {0x033, 0x0000000E}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004376}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004376}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0xA0000000, 0x00000000}, + {0x03F, 0x00004376}, + {0xB0000000, 0x00000000}, + {0x033, 0x0000000F}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004376}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004376}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0xA0000000, 0x00000000}, + {0x03F, 0x00004376}, + {0xB0000000, 0x00000000}, + {0x033, 0x00000010}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004376}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004376}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0xA0000000, 0x00000000}, + {0x03F, 0x00004376}, + {0xB0000000, 0x00000000}, + {0x033, 0x00000011}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004376}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004376}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0xA0000000, 0x00000000}, + {0x03F, 0x00004376}, + {0xB0000000, 0x00000000}, + {0x033, 0x00000012}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004376}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004376}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0xA0000000, 0x00000000}, + {0x03F, 0x00004376}, + {0xB0000000, 0x00000000}, + {0x033, 0x00000013}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004376}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004376}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004347}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004347}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004347}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004347}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004347}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004347}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004347}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004347}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004347}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004347}, + {0xA0000000, 0x00000000}, + {0x03F, 0x00004376}, + {0xB0000000, 0x00000000}, + {0x033, 0x00000014}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004376}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004376}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0xA0000000, 0x00000000}, + {0x03F, 0x00004376}, + {0xB0000000, 0x00000000}, + {0x033, 0x00000015}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004376}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004376}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0xA0000000, 0x00000000}, + {0x03F, 0x00004376}, + {0xB0000000, 0x00000000}, + {0x033, 0x00000016}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004376}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004376}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0xA0000000, 0x00000000}, + {0x03F, 0x00004376}, + {0xB0000000, 0x00000000}, + {0x033, 0x00000017}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004376}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004376}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004347}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004347}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004347}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004347}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004347}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004347}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004347}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004347}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004347}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004347}, + {0xA0000000, 0x00000000}, + {0x03F, 0x00004376}, + {0xB0000000, 0x00000000}, + {0x033, 0x00000020}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004376}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004376}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0xA0000000, 0x00000000}, + {0x03F, 0x00004376}, + {0xB0000000, 0x00000000}, + {0x033, 0x00000021}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004376}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004376}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004347}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004347}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004347}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004347}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004347}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004347}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004347}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004347}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004347}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004347}, + {0xA0000000, 0x00000000}, + {0x03F, 0x00004376}, + {0xB0000000, 0x00000000}, + {0x033, 0x00000022}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004376}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004376}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0xA0000000, 0x00000000}, + {0x03F, 0x00004376}, + {0xB0000000, 0x00000000}, + {0x033, 0x00000023}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004396}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004396}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004386}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004386}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004386}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004386}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004386}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004386}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004386}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004386}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004386}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004386}, + {0xA0000000, 0x00000000}, + {0x03F, 0x00004396}, + {0xB0000000, 0x00000000}, + {0x033, 0x00000024}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004396}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004396}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004386}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004386}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004386}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004386}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004386}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004386}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004386}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004386}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004386}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004386}, + {0xA0000000, 0x00000000}, + {0x03F, 0x00004396}, + {0xB0000000, 0x00000000}, + {0x033, 0x00000025}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004396}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004396}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004386}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004386}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004386}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004386}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004386}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004386}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004386}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004386}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004386}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004386}, + {0xA0000000, 0x00000000}, + {0x03F, 0x00004396}, + {0xB0000000, 0x00000000}, + {0x033, 0x00000026}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004396}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004396}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004386}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004386}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004386}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004386}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004386}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004386}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004386}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004386}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004386}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004386}, + {0xA0000000, 0x00000000}, + {0x03F, 0x00004396}, + {0xB0000000, 0x00000000}, + {0x033, 0x00000027}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004396}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004396}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004386}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004386}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004386}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004386}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004386}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004386}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004386}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004386}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004386}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004386}, + {0xA0000000, 0x00000000}, + {0x03F, 0x00004396}, + {0xB0000000, 0x00000000}, + {0x0EF, 0x00000000}, + {0x067, 0x00008072}, + {0x0EF, 0x00000010}, + {0x033, 0x00000001}, + {0x03F, 0x00000ED5}, + {0x033, 0x00000002}, + {0x03F, 0x00000FC7}, + {0x033, 0x00000003}, + {0x03F, 0x00000783}, + {0x033, 0x00000004}, + {0x03F, 0x00000973}, + {0x033, 0x00000005}, + {0x03F, 0x00000762}, + {0x033, 0x00000006}, + {0x03F, 0x00000762}, + {0x0EF, 0x00000000}, + {0x0EF, 0x00000080}, + {0x033, 0x00000000}, + {0x03E, 0x0000000B}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00026858}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00026858}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0xA0000000, 0x00000000}, + {0x03F, 0x00026858}, + {0xB0000000, 0x00000000}, + {0x033, 0x00000001}, + {0x03E, 0x0000000B}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00026858}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00026858}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0xA0000000, 0x00000000}, + {0x03F, 0x00026858}, + {0xB0000000, 0x00000000}, + {0x033, 0x00000002}, + {0x03E, 0x0000000C}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x0002C758}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x0002C758}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023958}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023958}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023958}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023958}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023958}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023958}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023958}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023958}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023958}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023958}, + {0xA0000000, 0x00000000}, + {0x03F, 0x0002C758}, + {0xB0000000, 0x00000000}, + {0x033, 0x00000003}, + {0x03E, 0x0000000C}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x0002C758}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x0002C758}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023958}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023958}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023958}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023958}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023958}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023958}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023958}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023958}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023958}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023958}, + {0xA0000000, 0x00000000}, + {0x03F, 0x0002C758}, + {0xB0000000, 0x00000000}, + {0x033, 0x00000004}, + {0x03E, 0x0000000B}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00026858}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00026858}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0xA0000000, 0x00000000}, + {0x03F, 0x00026858}, + {0xB0000000, 0x00000000}, + {0x033, 0x00000005}, + {0x03E, 0x0000000B}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00026858}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00026858}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0xA0000000, 0x00000000}, + {0x03F, 0x00026858}, + {0xB0000000, 0x00000000}, + {0x033, 0x00000006}, + {0x03E, 0x0000000C}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x0002C758}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x0002C758}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023958}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023958}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023958}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023958}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023958}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023958}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023958}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023958}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023958}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023958}, + {0xA0000000, 0x00000000}, + {0x03F, 0x0002C758}, + {0xB0000000, 0x00000000}, + {0x033, 0x00000007}, + {0x03E, 0x0000000C}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x0002C758}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x0002C758}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023958}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023958}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023958}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023958}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023958}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023958}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023958}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023958}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023958}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023958}, + {0xA0000000, 0x00000000}, + {0x03F, 0x0002C758}, + {0xB0000000, 0x00000000}, + {0x033, 0x00000008}, + {0x03E, 0x0000000B}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00026858}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00026858}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0xA0000000, 0x00000000}, + {0x03F, 0x00026858}, + {0xB0000000, 0x00000000}, + {0x033, 0x00000009}, + {0x03E, 0x0000000B}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00026858}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00026858}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0xA0000000, 0x00000000}, + {0x03F, 0x00026858}, + {0xB0000000, 0x00000000}, + {0x033, 0x0000000A}, + {0x03E, 0x0000000C}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x0002C758}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x0002C758}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023958}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023958}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023958}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023958}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023958}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023958}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023958}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023958}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023958}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023958}, + {0xA0000000, 0x00000000}, + {0x03F, 0x0002C758}, + {0xB0000000, 0x00000000}, + {0x033, 0x0000000B}, + {0x03E, 0x0000000C}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x0002C758}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x0002C758}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023958}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023958}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023958}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023958}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023958}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023958}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023958}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023958}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023958}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023958}, + {0xA0000000, 0x00000000}, + {0x03F, 0x0002C758}, + {0xB0000000, 0x00000000}, + {0x033, 0x0000000C}, + {0x03E, 0x0000000B}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00026858}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00026858}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0xA0000000, 0x00000000}, + {0x03F, 0x00026858}, + {0xB0000000, 0x00000000}, + {0x033, 0x0000000D}, + {0x03E, 0x0000000B}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00026858}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00026858}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0xA0000000, 0x00000000}, + {0x03F, 0x00026858}, + {0xB0000000, 0x00000000}, + {0x033, 0x0000000E}, + {0x03E, 0x0000000C}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x0002C758}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x0002C758}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023958}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023958}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023958}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023958}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023958}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023958}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023958}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023958}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023958}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023958}, + {0xA0000000, 0x00000000}, + {0x03F, 0x0002C758}, + {0xB0000000, 0x00000000}, + {0x033, 0x0000000F}, + {0x03E, 0x0000000C}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x0002C758}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x0002C758}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023958}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023958}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023958}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023958}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023958}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023958}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023958}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023958}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023958}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023958}, + {0xA0000000, 0x00000000}, + {0x03F, 0x0002C758}, + {0xB0000000, 0x00000000}, + {0x033, 0x00000010}, + {0x03E, 0x0000000B}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00026858}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00026858}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0xA0000000, 0x00000000}, + {0x03F, 0x00026858}, + {0xB0000000, 0x00000000}, + {0x033, 0x00000011}, + {0x03E, 0x0000000B}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00026858}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00026858}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0xA0000000, 0x00000000}, + {0x03F, 0x00026858}, + {0xB0000000, 0x00000000}, + {0x033, 0x00000012}, + {0x03E, 0x0000000C}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x0002C758}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x0002C758}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023958}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023958}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023958}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023958}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023958}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023958}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023958}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023958}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023958}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023958}, + {0xA0000000, 0x00000000}, + {0x03F, 0x0002C758}, + {0xB0000000, 0x00000000}, + {0x033, 0x00000013}, + {0x03E, 0x0000000C}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x0002C758}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x0002C758}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023958}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023958}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023958}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023958}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023958}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023958}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023958}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023958}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023958}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023958}, + {0xA0000000, 0x00000000}, + {0x03F, 0x0002C758}, + {0xB0000000, 0x00000000}, + {0x033, 0x00000014}, + {0x03E, 0x0000000B}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00026858}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00026858}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0xA0000000, 0x00000000}, + {0x03F, 0x00026858}, + {0xB0000000, 0x00000000}, + {0x033, 0x00000015}, + {0x03E, 0x0000000B}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00026858}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00026858}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0xA0000000, 0x00000000}, + {0x03F, 0x00026858}, + {0xB0000000, 0x00000000}, + {0x033, 0x00000016}, + {0x03E, 0x0000000C}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x0002C758}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x0002C758}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0xA0000000, 0x00000000}, + {0x03F, 0x0002C758}, + {0xB0000000, 0x00000000}, + {0x033, 0x00000017}, + {0x03E, 0x0000000C}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x0002C758}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x0002C758}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023A58}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023A58}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023A58}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023A58}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023A58}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023A58}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023A58}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023A58}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023A58}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023A58}, + {0xA0000000, 0x00000000}, + {0x03F, 0x0002C758}, + {0xB0000000, 0x00000000}, + {0x033, 0x00000018}, + {0x03E, 0x0000000B}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00026858}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00026858}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0xA0000000, 0x00000000}, + {0x03F, 0x00026858}, + {0xB0000000, 0x00000000}, + {0x033, 0x00000019}, + {0x03E, 0x0000000B}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00026858}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00026858}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0xA0000000, 0x00000000}, + {0x03F, 0x00026858}, + {0xB0000000, 0x00000000}, + {0x033, 0x0000001A}, + {0x03E, 0x0000000B}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x0002C758}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x0002C758}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0xA0000000, 0x00000000}, + {0x03F, 0x0002C758}, + {0xB0000000, 0x00000000}, + {0x033, 0x0000001B}, + {0x03E, 0x0000000B}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x0002C758}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x0002C758}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023A58}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023A58}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023A58}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023A58}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023A58}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023A58}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023A58}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023A58}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023A58}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023A58}, + {0xA0000000, 0x00000000}, + {0x03F, 0x0002C758}, + {0xB0000000, 0x00000000}, + {0x033, 0x0000001C}, + {0x03E, 0x0000000B}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00026858}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00026858}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0xA0000000, 0x00000000}, + {0x03F, 0x00026858}, + {0xB0000000, 0x00000000}, + {0x033, 0x0000001D}, + {0x03E, 0x0000000B}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00026858}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00026858}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0xA0000000, 0x00000000}, + {0x03F, 0x00026858}, + {0xB0000000, 0x00000000}, + {0x033, 0x0000001E}, + {0x03E, 0x0000000C}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x0002C758}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x0002C758}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0xA0000000, 0x00000000}, + {0x03F, 0x0002C758}, + {0xB0000000, 0x00000000}, + {0x033, 0x0000001F}, + {0x03E, 0x0000000C}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x0002C758}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x0002C758}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023A58}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023A58}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023A58}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023A58}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023A58}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023A58}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023A58}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023A58}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023A58}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023A58}, + {0xA0000000, 0x00000000}, + {0x03F, 0x0002C758}, + {0xB0000000, 0x00000000}, + {0x033, 0x00000020}, + {0x03E, 0x0000000C}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00026858}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00026858}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0xA0000000, 0x00000000}, + {0x03F, 0x00026858}, + {0xB0000000, 0x00000000}, + {0x033, 0x00000021}, + {0x03E, 0x0000000C}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00026858}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00026858}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0xA0000000, 0x00000000}, + {0x03F, 0x00026858}, + {0xB0000000, 0x00000000}, + {0x033, 0x00000022}, + {0x03E, 0x0000000C}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x0002C758}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x0002C758}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0xA0000000, 0x00000000}, + {0x03F, 0x0002C758}, + {0xB0000000, 0x00000000}, + {0x033, 0x00000023}, + {0x03E, 0x0000000C}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x0002C758}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x0002C758}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023A58}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023A58}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023A58}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023A58}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023A58}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023A58}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023A58}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023A58}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023A58}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023A58}, + {0xA0000000, 0x00000000}, + {0x03F, 0x0002C758}, + {0xB0000000, 0x00000000}, + {0x033, 0x00000024}, + {0x03E, 0x0000000C}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00026858}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00026858}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0xA0000000, 0x00000000}, + {0x03F, 0x00026858}, + {0xB0000000, 0x00000000}, + {0x033, 0x00000025}, + {0x03E, 0x0000000C}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00026858}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00026858}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0xA0000000, 0x00000000}, + {0x03F, 0x00026858}, + {0xB0000000, 0x00000000}, + {0x033, 0x00000026}, + {0x03E, 0x0000000C}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x0002C758}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x0002C758}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0xA0000000, 0x00000000}, + {0x03F, 0x0002C758}, + {0xB0000000, 0x00000000}, + {0x033, 0x00000027}, + {0x03E, 0x0000000C}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x0002C758}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x0002C758}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023A58}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023A58}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023A58}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023A58}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023A58}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023A58}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023A58}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023A58}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023A58}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023A58}, + {0xA0000000, 0x00000000}, + {0x03F, 0x0002C758}, + {0xB0000000, 0x00000000}, + {0x033, 0x00000028}, + {0x03E, 0x0000000C}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00026858}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00026858}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0xA0000000, 0x00000000}, + {0x03F, 0x00026858}, + {0xB0000000, 0x00000000}, + {0x033, 0x00000029}, + {0x03E, 0x0000000C}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00026858}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00026858}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0xA0000000, 0x00000000}, + {0x03F, 0x00026858}, + {0xB0000000, 0x00000000}, + {0x033, 0x0000002A}, + {0x03E, 0x0000000C}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x0002C758}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x0002C758}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0xA0000000, 0x00000000}, + {0x03F, 0x0002C758}, + {0xB0000000, 0x00000000}, + {0x033, 0x0000002B}, + {0x03E, 0x0000000C}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x0002C758}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x0002C758}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023A58}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023A58}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023A58}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023A58}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023A58}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023A58}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023A58}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023A58}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023A58}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023A58}, + {0xA0000000, 0x00000000}, + {0x03F, 0x0002C758}, + {0xB0000000, 0x00000000}, + {0x033, 0x0000002C}, + {0x03E, 0x0000000C}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00026858}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00026858}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0xA0000000, 0x00000000}, + {0x03F, 0x00026858}, + {0xB0000000, 0x00000000}, + {0x033, 0x0000002D}, + {0x03E, 0x0000000C}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00026858}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00026858}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0xA0000000, 0x00000000}, + {0x03F, 0x00026858}, + {0xB0000000, 0x00000000}, + {0x033, 0x0000002E}, + {0x03E, 0x0000000C}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x0002C758}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x0002C758}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0xA0000000, 0x00000000}, + {0x03F, 0x0002C758}, + {0xB0000000, 0x00000000}, + {0x033, 0x0000002F}, + {0x03E, 0x0000000C}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x0002C758}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x0002C758}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023A58}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023A58}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023A58}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023A58}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023A58}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023A58}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023A58}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023A58}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023A58}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023A58}, + {0xA0000000, 0x00000000}, + {0x03F, 0x0002C758}, + {0xB0000000, 0x00000000}, + {0x033, 0x00000030}, + {0x03E, 0x0000000C}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00026858}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00026858}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0xA0000000, 0x00000000}, + {0x03F, 0x00026858}, + {0xB0000000, 0x00000000}, + {0x033, 0x00000031}, + {0x03E, 0x0000000C}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00026858}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00026858}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0xA0000000, 0x00000000}, + {0x03F, 0x00026858}, + {0xB0000000, 0x00000000}, + {0x033, 0x00000032}, + {0x03E, 0x0000000C}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x0002C758}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x0002C758}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0xA0000000, 0x00000000}, + {0x03F, 0x0002C758}, + {0xB0000000, 0x00000000}, + {0x033, 0x00000033}, + {0x03E, 0x0000000C}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x0002C758}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x0002C758}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023A58}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023A58}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023A58}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023A58}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023A58}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023A58}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023A58}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023A58}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023A58}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023A58}, + {0xA0000000, 0x00000000}, + {0x03F, 0x0002C758}, + {0xB0000000, 0x00000000}, + {0x033, 0x00000034}, + {0x03E, 0x0000000C}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00026858}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00026858}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0xA0000000, 0x00000000}, + {0x03F, 0x00026858}, + {0xB0000000, 0x00000000}, + {0x033, 0x00000035}, + {0x03E, 0x0000000C}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00026858}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00026858}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0xA0000000, 0x00000000}, + {0x03F, 0x00026858}, + {0xB0000000, 0x00000000}, + {0x033, 0x00000036}, + {0x03E, 0x0000000C}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x0002C758}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x0002C758}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0xA0000000, 0x00000000}, + {0x03F, 0x0002C758}, + {0xB0000000, 0x00000000}, + {0x033, 0x00000037}, + {0x03E, 0x0000000C}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x0002C758}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x0002C758}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023A58}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023A58}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023A58}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023A58}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023A58}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023A58}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023A58}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023A58}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023A58}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023A58}, + {0xA0000000, 0x00000000}, + {0x03F, 0x0002C758}, + {0xB0000000, 0x00000000}, + {0x033, 0x00000038}, + {0x03E, 0x0000000C}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00026858}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00026858}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0xA0000000, 0x00000000}, + {0x03F, 0x00026858}, + {0xB0000000, 0x00000000}, + {0x033, 0x00000039}, + {0x03E, 0x0000000C}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00026858}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00026858}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0xA0000000, 0x00000000}, + {0x03F, 0x00026858}, + {0xB0000000, 0x00000000}, + {0x033, 0x0000003A}, + {0x03E, 0x0000000C}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x0002C758}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x0002C758}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022A58}, + {0xA0000000, 0x00000000}, + {0x03F, 0x0002C758}, + {0xB0000000, 0x00000000}, + {0x033, 0x0000003B}, + {0x03E, 0x0000000C}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x0002C758}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x0002C758}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023A58}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023A58}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023A58}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023A58}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023A58}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023A58}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023A58}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023A58}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023A58}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00023A58}, + {0xA0000000, 0x00000000}, + {0x03F, 0x0002C758}, + {0xB0000000, 0x00000000}, + {0x0EF, 0x00000000}, + {0x0EE, 0x00000800}, + {0x033, 0x00000000}, + {0x03F, 0x00000001}, + {0x033, 0x00000001}, + {0x03F, 0x00000003}, + {0x033, 0x00000002}, + {0x03F, 0x00000005}, + {0x033, 0x00000003}, + {0x03F, 0x00000007}, + {0x033, 0x00000004}, + {0x03F, 0x00000001}, + {0x033, 0x00000005}, + {0x03F, 0x00000003}, + {0x033, 0x00000006}, + {0x03F, 0x00000006}, + {0x033, 0x00000007}, + {0x03F, 0x00000007}, + {0x0EE, 0x00000000}, + {0x0EE, 0x00001000}, + {0x033, 0x00000000}, + {0x03F, 0x00003000}, + {0x033, 0x00000001}, + {0x03F, 0x00003001}, + {0x033, 0x00000002}, + {0x03F, 0x00003003}, + {0x033, 0x00000003}, + {0x03F, 0x00003007}, + {0x033, 0x00000004}, + {0x03F, 0x0000300F}, + {0x033, 0x00000005}, + {0x03F, 0x0000310F}, + {0x033, 0x00000006}, + {0x03F, 0x0000330F}, + {0x033, 0x00000007}, + {0x03F, 0x0000330F}, + {0x033, 0x00000008}, + {0x03F, 0x00003000}, + {0x033, 0x00000009}, + {0x03F, 0x00003001}, + {0x033, 0x0000000A}, + {0x03F, 0x00003003}, + {0x033, 0x0000000B}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00003007}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00003007}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00003007}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00003007}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00003007}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00003007}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00003007}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00003007}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00003007}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00003007}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00003007}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00003007}, + {0xA0000000, 0x00000000}, + {0x03F, 0x00003103}, + {0xB0000000, 0x00000000}, + {0x033, 0x0000000C}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00003107}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00003107}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00003107}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00003107}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00003107}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00003107}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00003107}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00003107}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00003107}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00003107}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00003107}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00003107}, + {0xA0000000, 0x00000000}, + {0x03F, 0x00003307}, + {0xB0000000, 0x00000000}, + {0x033, 0x0000000D}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00003307}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00003307}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00003307}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00003307}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00003307}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00003307}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00003307}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00003307}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00003307}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00003307}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00003307}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00003307}, + {0xA0000000, 0x00000000}, + {0x03F, 0x00002307}, + {0xB0000000, 0x00000000}, + {0x033, 0x0000000E}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00001307}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00001307}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00001307}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00001307}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00001307}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00001307}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00001307}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00001307}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00001307}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00001307}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00001307}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00001307}, + {0xA0000000, 0x00000000}, + {0x03F, 0x00000307}, + {0xB0000000, 0x00000000}, + {0x033, 0x0000000F}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00000307}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00000307}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00000307}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00000307}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00000307}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00000307}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00000307}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00000307}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00000307}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00000307}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00000307}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00000307}, + {0xA0000000, 0x00000000}, + {0x03F, 0x00000307}, + {0xB0000000, 0x00000000}, + {0x0EE, 0x00000000}, + {0x0EE, 0x00000200}, + {0x033, 0x00000000}, + {0x03F, 0x00000001}, + {0x033, 0x00000001}, + {0x03F, 0x00000003}, + {0x033, 0x00000002}, + {0x03F, 0x00000005}, + {0x033, 0x00000003}, + {0x03F, 0x00000007}, + {0x0EE, 0x00000000}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x0EC, 0x00000100}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x0EC, 0x00000100}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x0EC, 0x00000000}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x0EC, 0x00000000}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x0EC, 0x00000000}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x0EC, 0x00000000}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x0EC, 0x00000000}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x0EC, 0x00000000}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x0EC, 0x00000000}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x0EC, 0x00000000}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x0EC, 0x00000000}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x0EC, 0x00000000}, + {0xA0000000, 0x00000000}, + {0x0EC, 0x00000100}, + {0xB0000000, 0x00000000}, + {0x033, 0x00000004}, + {0x03D, 0x00000078}, + {0x03E, 0x00080000}, + {0x03F, 0x00000000}, + {0x033, 0x00000005}, + {0x03D, 0x0000007B}, + {0x03E, 0x00020000}, + {0x03F, 0x00000000}, + {0x0EC, 0x00000000}, + {0x0DE, 0x00000000}, + {0x0EF, 0x00000000}, + {0x033, 0x00000000}, + {0x008, 0x00060280}, + {0x009, 0x00030400}, + {0x0EF, 0x00000000}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x0EF, 0x00000400}, + {0x033, 0x00000000}, + {0x03F, 0x000001FF}, + {0x033, 0x00000001}, + {0x03F, 0x000001FF}, + {0x033, 0x00000002}, + {0x03F, 0x000001F7}, + {0x033, 0x00000003}, + {0x03F, 0x000000FF}, + {0x033, 0x00000004}, + {0x03F, 0x000000FF}, + {0x033, 0x00000005}, + {0x03F, 0x000000FF}, + {0x033, 0x00000006}, + {0x03F, 0x000000FF}, + {0x033, 0x00000007}, + {0x03F, 0x000000FF}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x0EF, 0x00000400}, + {0x033, 0x00000000}, + {0x03F, 0x000001FF}, + {0x033, 0x00000001}, + {0x03F, 0x000001FF}, + {0x033, 0x00000002}, + {0x03F, 0x000001F7}, + {0x033, 0x00000003}, + {0x03F, 0x000000FF}, + {0x033, 0x00000004}, + {0x03F, 0x000000FF}, + {0x033, 0x00000005}, + {0x03F, 0x000000FF}, + {0x033, 0x00000006}, + {0x03F, 0x000000FF}, + {0x033, 0x00000007}, + {0x03F, 0x000000FF}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x0EF, 0x00000400}, + {0x033, 0x00000000}, + {0x03F, 0x000001FF}, + {0x033, 0x00000001}, + {0x03F, 0x000001FF}, + {0x033, 0x00000002}, + {0x03F, 0x0000013F}, + {0x033, 0x00000003}, + {0x03F, 0x000000FB}, + {0x033, 0x00000004}, + {0x03F, 0x000000FB}, + {0x033, 0x00000005}, + {0x03F, 0x000000FB}, + {0x033, 0x00000006}, + {0x03F, 0x000000FB}, + {0x033, 0x00000007}, + {0x03F, 0x000000FB}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x0EF, 0x00000400}, + {0x033, 0x00000000}, + {0x03F, 0x000001FF}, + {0x033, 0x00000001}, + {0x03F, 0x000001FF}, + {0x033, 0x00000002}, + {0x03F, 0x0000013F}, + {0x033, 0x00000003}, + {0x03F, 0x000000FB}, + {0x033, 0x00000004}, + {0x03F, 0x000000FB}, + {0x033, 0x00000005}, + {0x03F, 0x000000FB}, + {0x033, 0x00000006}, + {0x03F, 0x000000FB}, + {0x033, 0x00000007}, + {0x03F, 0x000000FB}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x0EF, 0x00000400}, + {0x033, 0x00000000}, + {0x03F, 0x000001FF}, + {0x033, 0x00000001}, + {0x03F, 0x000001FF}, + {0x033, 0x00000002}, + {0x03F, 0x0000013F}, + {0x033, 0x00000003}, + {0x03F, 0x000000FB}, + {0x033, 0x00000004}, + {0x03F, 0x000000FB}, + {0x033, 0x00000005}, + {0x03F, 0x000000FB}, + {0x033, 0x00000006}, + {0x03F, 0x000000FB}, + {0x033, 0x00000007}, + {0x03F, 0x000000FB}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x0EF, 0x00000400}, + {0x033, 0x00000000}, + {0x03F, 0x000001FF}, + {0x033, 0x00000001}, + {0x03F, 0x000001FF}, + {0x033, 0x00000002}, + {0x03F, 0x0000013F}, + {0x033, 0x00000003}, + {0x03F, 0x000000FB}, + {0x033, 0x00000004}, + {0x03F, 0x000000FB}, + {0x033, 0x00000005}, + {0x03F, 0x000000FB}, + {0x033, 0x00000006}, + {0x03F, 0x000000FB}, + {0x033, 0x00000007}, + {0x03F, 0x000000FB}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x0EF, 0x00000400}, + {0x033, 0x00000000}, + {0x03F, 0x000001FF}, + {0x033, 0x00000001}, + {0x03F, 0x000001FF}, + {0x033, 0x00000002}, + {0x03F, 0x0000013F}, + {0x033, 0x00000003}, + {0x03F, 0x000000FB}, + {0x033, 0x00000004}, + {0x03F, 0x000000FB}, + {0x033, 0x00000005}, + {0x03F, 0x000000FB}, + {0x033, 0x00000006}, + {0x03F, 0x000000FB}, + {0x033, 0x00000007}, + {0x03F, 0x000000FB}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x0EF, 0x00000400}, + {0x033, 0x00000000}, + {0x03F, 0x000001FF}, + {0x033, 0x00000001}, + {0x03F, 0x000001FF}, + {0x033, 0x00000002}, + {0x03F, 0x0000013F}, + {0x033, 0x00000003}, + {0x03F, 0x000000FB}, + {0x033, 0x00000004}, + {0x03F, 0x000000FB}, + {0x033, 0x00000005}, + {0x03F, 0x000000FB}, + {0x033, 0x00000006}, + {0x03F, 0x000000FB}, + {0x033, 0x00000007}, + {0x03F, 0x000000FB}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x0EF, 0x00000400}, + {0x033, 0x00000000}, + {0x03F, 0x000001FF}, + {0x033, 0x00000001}, + {0x03F, 0x000001FF}, + {0x033, 0x00000002}, + {0x03F, 0x0000013F}, + {0x033, 0x00000003}, + {0x03F, 0x000000FB}, + {0x033, 0x00000004}, + {0x03F, 0x000000FB}, + {0x033, 0x00000005}, + {0x03F, 0x000000FB}, + {0x033, 0x00000006}, + {0x03F, 0x000000FB}, + {0x033, 0x00000007}, + {0x03F, 0x000000FB}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x0EF, 0x00000400}, + {0x033, 0x00000000}, + {0x03F, 0x000001FF}, + {0x033, 0x00000001}, + {0x03F, 0x000001FF}, + {0x033, 0x00000002}, + {0x03F, 0x0000013F}, + {0x033, 0x00000003}, + {0x03F, 0x000000FB}, + {0x033, 0x00000004}, + {0x03F, 0x000000FB}, + {0x033, 0x00000005}, + {0x03F, 0x000000FB}, + {0x033, 0x00000006}, + {0x03F, 0x000000FB}, + {0x033, 0x00000007}, + {0x03F, 0x000000FB}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x0EF, 0x00000400}, + {0x033, 0x00000000}, + {0x03F, 0x000001FF}, + {0x033, 0x00000001}, + {0x03F, 0x000001FF}, + {0x033, 0x00000002}, + {0x03F, 0x0000013F}, + {0x033, 0x00000003}, + {0x03F, 0x000000FB}, + {0x033, 0x00000004}, + {0x03F, 0x000000FB}, + {0x033, 0x00000005}, + {0x03F, 0x000000FB}, + {0x033, 0x00000006}, + {0x03F, 0x000000FB}, + {0x033, 0x00000007}, + {0x03F, 0x000000FB}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x0EF, 0x00000400}, + {0x033, 0x00000000}, + {0x03F, 0x000001FF}, + {0x033, 0x00000001}, + {0x03F, 0x000001FF}, + {0x033, 0x00000002}, + {0x03F, 0x0000013F}, + {0x033, 0x00000003}, + {0x03F, 0x000000FB}, + {0x033, 0x00000004}, + {0x03F, 0x000000FB}, + {0x033, 0x00000005}, + {0x03F, 0x000000FB}, + {0x033, 0x00000006}, + {0x03F, 0x000000FB}, + {0x033, 0x00000007}, + {0x03F, 0x000000FB}, + {0xA0000000, 0x00000000}, + {0x0EF, 0x00000400}, + {0x033, 0x00000000}, + {0x03F, 0x000001FF}, + {0x033, 0x00000001}, + {0x03F, 0x000001FF}, + {0x033, 0x00000002}, + {0x03F, 0x000001F7}, + {0x033, 0x00000003}, + {0x03F, 0x000000FF}, + {0x033, 0x00000004}, + {0x03F, 0x000000FF}, + {0x033, 0x00000005}, + {0x03F, 0x000000FF}, + {0x033, 0x00000006}, + {0x03F, 0x000000FF}, + {0x033, 0x00000007}, + {0x03F, 0x000000FF}, + {0xB0000000, 0x00000000}, + {0x0EF, 0x00000200}, + {0x033, 0x00000000}, + {0x03F, 0x0000017F}, + {0x033, 0x00000001}, + {0x03F, 0x0000017F}, + {0x033, 0x00000002}, + {0x03F, 0x0000017F}, + {0x033, 0x00000003}, + {0x03F, 0x0000007F}, + {0x033, 0x00000004}, + {0x03F, 0x0000007F}, + {0x033, 0x00000005}, + {0x03F, 0x0000007F}, + {0x033, 0x00000006}, + {0x03F, 0x0000007F}, + {0x033, 0x00000007}, + {0x03F, 0x0000007F}, + {0x0EF, 0x00000000}, + {0x06E, 0x00077A18}, + {0x06F, 0x00077A18}, + {0x06D, 0x00000C31}, + {0x0EF, 0x00020000}, + {0x033, 0x00000000}, + {0x03F, 0x000005FF}, + {0x0EF, 0x00000000}, + {0x005, 0x00000001}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x094, 0x000000FC}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x094, 0x000000FC}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x094, 0x000000FC}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x094, 0x000000FC}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x094, 0x000000FC}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x094, 0x000000FC}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x094, 0x000000FC}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x094, 0x000000FC}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x094, 0x000000FC}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x094, 0x000000FC}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x094, 0x000000FC}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x094, 0x000000FC}, + {0xA0000000, 0x00000000}, + {0x094, 0x000001FC}, + {0xB0000000, 0x00000000}, + {0x100EE, 0x00002000}, + {0x10033, 0x00000080}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000FB}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000FB}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000FB}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000FB}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000FB}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000FB}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000FB}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000FB}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000FB}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000FB}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000FB}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000FB}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x000000F6}, + {0xB0000000, 0x00000000}, + {0x10033, 0x00000081}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F8}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F8}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F8}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F8}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F8}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F8}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F8}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F8}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F8}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F8}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F8}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F8}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x000000F3}, + {0xB0000000, 0x00000000}, + {0x10033, 0x00000082}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F5}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F5}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F5}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F5}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F5}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F5}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F5}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F5}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F5}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F5}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F5}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F5}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x000000F0}, + {0xB0000000, 0x00000000}, + {0x10033, 0x00000083}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F2}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F2}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F2}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F2}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F2}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F2}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F2}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F2}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F2}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F2}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F2}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F2}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x000000ED}, + {0xB0000000, 0x00000000}, + {0x10033, 0x00000084}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000EF}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000EF}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000EF}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000EF}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000EF}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000EF}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000EF}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000EF}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000EF}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000EF}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000EF}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000EF}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x000000EA}, + {0xB0000000, 0x00000000}, + {0x10033, 0x00000085}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000EC}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000EC}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000EC}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000EC}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000EC}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000EC}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000EC}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000EC}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000EC}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000EC}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000EC}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000EC}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x000000E7}, + {0xB0000000, 0x00000000}, + {0x10033, 0x00000086}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000AB}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000AB}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000AB}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000AB}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000AB}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000AB}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000AB}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000AB}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000AB}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000AB}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000AB}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000AB}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x000000A6}, + {0xB0000000, 0x00000000}, + {0x10033, 0x00000087}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000A8}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000A8}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000A8}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000A8}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000A8}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000A8}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000A8}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000A8}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000A8}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000A8}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000A8}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000A8}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x000000A3}, + {0xB0000000, 0x00000000}, + {0x10033, 0x00000088}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000068}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000068}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000068}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000068}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000068}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000068}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000068}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000068}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000068}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000068}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000068}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000068}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x00000063}, + {0xB0000000, 0x00000000}, + {0x10033, 0x00000089}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000065}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000065}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000065}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000065}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000065}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000065}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000065}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000065}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000065}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000065}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000065}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000065}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x00000060}, + {0xB0000000, 0x00000000}, + {0x10033, 0x0000008A}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000002B}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000002B}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000002B}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000002B}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000002B}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000002B}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000002B}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000002B}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000002B}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000002B}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000002B}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000002B}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x00000026}, + {0xB0000000, 0x00000000}, + {0x10033, 0x0000008B}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000028}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000028}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000028}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000028}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000028}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000028}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000028}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000028}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000028}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000028}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000028}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000028}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x00000023}, + {0xB0000000, 0x00000000}, + {0x10033, 0x0000008C}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000025}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000025}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000025}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000025}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000025}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000025}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000025}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000025}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000025}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000025}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000025}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000025}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x00000020}, + {0xB0000000, 0x00000000}, + {0x10033, 0x0000008D}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000022}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000022}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000022}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000022}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000022}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000022}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000022}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000022}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000022}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000022}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000022}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000022}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x0000001D}, + {0xB0000000, 0x00000000}, + {0x10033, 0x0000008E}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001F}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001F}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001F}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001F}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001F}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001F}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001F}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001F}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001F}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001F}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001F}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001F}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x0000001A}, + {0xB0000000, 0x00000000}, + {0x10033, 0x0000008F}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001C}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001C}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001C}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001C}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001C}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001C}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001C}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001C}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001C}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001C}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001C}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001C}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x00000017}, + {0xB0000000, 0x00000000}, + {0x10033, 0x00000090}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000019}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000019}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000019}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000019}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000019}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000019}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000019}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000019}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000019}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000019}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000019}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000019}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x00000014}, + {0xB0000000, 0x00000000}, + {0x10033, 0x000000A0}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000FB}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000FB}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000FB}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000FB}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000FB}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000FB}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000FB}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000FB}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000FB}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000FB}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000FB}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000FB}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x000000F6}, + {0xB0000000, 0x00000000}, + {0x10033, 0x000000A1}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F8}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F8}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F8}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F8}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F8}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F8}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F8}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F8}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F8}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F8}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F8}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F8}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x000000F3}, + {0xB0000000, 0x00000000}, + {0x10033, 0x000000A2}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F5}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F5}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F5}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F5}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F5}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F5}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F5}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F5}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F5}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F5}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F5}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F5}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x000000F0}, + {0xB0000000, 0x00000000}, + {0x10033, 0x000000A3}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F2}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F2}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F2}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F2}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F2}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F2}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F2}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F2}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F2}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F2}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F2}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F2}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x000000ED}, + {0xB0000000, 0x00000000}, + {0x10033, 0x000000A4}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000EF}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000EF}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000EF}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000EF}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000EF}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000EF}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000EF}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000EF}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000EF}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000EF}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000EF}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000EF}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x000000EA}, + {0xB0000000, 0x00000000}, + {0x10033, 0x000000A5}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000EC}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000EC}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000EC}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000EC}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000EC}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000EC}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000EC}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000EC}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000EC}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000EC}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000EC}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000EC}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x000000E7}, + {0xB0000000, 0x00000000}, + {0x10033, 0x000000A6}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000AB}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000AB}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000AB}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000AB}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000AB}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000AB}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000AB}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000AB}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000AB}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000AB}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000AB}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000AB}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x000000A6}, + {0xB0000000, 0x00000000}, + {0x10033, 0x000000A7}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000A8}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000A8}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000A8}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000A8}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000A8}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000A8}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000A8}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000A8}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000A8}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000A8}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000A8}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000A8}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x000000A3}, + {0xB0000000, 0x00000000}, + {0x10033, 0x000000A8}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000068}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000068}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000068}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000068}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000068}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000068}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000068}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000068}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000068}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000068}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000068}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000068}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x00000063}, + {0xB0000000, 0x00000000}, + {0x10033, 0x000000A9}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000065}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000065}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000065}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000065}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000065}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000065}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000065}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000065}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000065}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000065}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000065}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000065}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x00000060}, + {0xB0000000, 0x00000000}, + {0x10033, 0x000000AA}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000002B}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000002B}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000002B}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000002B}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000002B}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000002B}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000002B}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000002B}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000002B}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000002B}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000002B}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000002B}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x00000026}, + {0xB0000000, 0x00000000}, + {0x10033, 0x000000AB}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000028}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000028}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000028}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000028}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000028}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000028}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000028}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000028}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000028}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000028}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000028}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000028}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x00000023}, + {0xB0000000, 0x00000000}, + {0x10033, 0x000000AC}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000025}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000025}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000025}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000025}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000025}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000025}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000025}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000025}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000025}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000025}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000025}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000025}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x00000020}, + {0xB0000000, 0x00000000}, + {0x10033, 0x000000AD}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000022}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000022}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000022}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000022}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000022}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000022}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000022}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000022}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000022}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000022}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000022}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000022}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x0000001D}, + {0xB0000000, 0x00000000}, + {0x10033, 0x000000AE}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001F}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001F}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001F}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001F}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001F}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001F}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001F}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001F}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001F}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001F}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001F}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001F}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x0000001A}, + {0xB0000000, 0x00000000}, + {0x10033, 0x000000AF}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001C}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001C}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001C}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001C}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001C}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001C}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001C}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001C}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001C}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001C}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001C}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001C}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x00000017}, + {0xB0000000, 0x00000000}, + {0x10033, 0x000000B0}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000019}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000019}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000019}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000019}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000019}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000019}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000019}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000019}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000019}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000019}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000019}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000019}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x00000014}, + {0xB0000000, 0x00000000}, + {0x10033, 0x000000C0}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000FB}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000FB}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000FB}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000FB}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000FB}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000FB}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000FB}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000FB}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000FB}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000FB}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000FB}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000FB}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x000000F6}, + {0xB0000000, 0x00000000}, + {0x10033, 0x000000C1}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F8}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F8}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F8}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F8}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F8}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F8}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F8}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F8}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F8}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F8}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F8}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F8}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x000000F3}, + {0xB0000000, 0x00000000}, + {0x10033, 0x000000C2}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F5}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F5}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F5}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F5}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F5}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F5}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F5}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F5}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F5}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F5}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F5}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F5}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x000000F0}, + {0xB0000000, 0x00000000}, + {0x10033, 0x000000C3}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F2}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F2}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F2}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F2}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F2}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F2}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F2}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F2}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F2}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F2}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F2}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F2}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x000000ED}, + {0xB0000000, 0x00000000}, + {0x10033, 0x000000C4}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000EF}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000EF}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000EF}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000EF}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000EF}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000EF}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000EF}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000EF}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000EF}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000EF}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000EF}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000EF}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x000000EA}, + {0xB0000000, 0x00000000}, + {0x10033, 0x000000C5}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000EC}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000EC}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000EC}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000EC}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000EC}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000EC}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000EC}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000EC}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000EC}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000EC}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000EC}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000EC}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x000000E7}, + {0xB0000000, 0x00000000}, + {0x10033, 0x000000C6}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000AB}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000AB}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000AB}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000AB}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000AB}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000AB}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000AB}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000AB}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000AB}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000AB}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000AB}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000AB}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x000000A6}, + {0xB0000000, 0x00000000}, + {0x10033, 0x000000C7}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000A8}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000A8}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000A8}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000A8}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000A8}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000A8}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000A8}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000A8}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000A8}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000A8}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000A8}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000A8}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x000000A3}, + {0xB0000000, 0x00000000}, + {0x10033, 0x000000C8}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000068}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000068}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000068}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000068}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000068}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000068}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000068}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000068}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000068}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000068}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000068}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000068}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x00000063}, + {0xB0000000, 0x00000000}, + {0x10033, 0x000000C9}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000065}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000065}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000065}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000065}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000065}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000065}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000065}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000065}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000065}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000065}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000065}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000065}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x00000060}, + {0xB0000000, 0x00000000}, + {0x10033, 0x000000CA}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000002B}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000002B}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000002B}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000002B}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000002B}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000002B}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000002B}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000002B}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000002B}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000002B}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000002B}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000002B}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x00000026}, + {0xB0000000, 0x00000000}, + {0x10033, 0x000000CB}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000028}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000028}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000028}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000028}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000028}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000028}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000028}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000028}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000028}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000028}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000028}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000028}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x00000023}, + {0xB0000000, 0x00000000}, + {0x10033, 0x000000CC}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000025}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000025}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000025}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000025}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000025}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000025}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000025}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000025}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000025}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000025}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000025}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000025}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x00000020}, + {0xB0000000, 0x00000000}, + {0x10033, 0x000000CD}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000022}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000022}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000022}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000022}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000022}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000022}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000022}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000022}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000022}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000022}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000022}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000022}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x0000001D}, + {0xB0000000, 0x00000000}, + {0x10033, 0x000000CE}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001F}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001F}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001F}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001F}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001F}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001F}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001F}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001F}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001F}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001F}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001F}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001F}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x0000001A}, + {0xB0000000, 0x00000000}, + {0x10033, 0x000000CF}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001C}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001C}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001C}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001C}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001C}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001C}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001C}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001C}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001C}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001C}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001C}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001C}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x00000017}, + {0xB0000000, 0x00000000}, + {0x10033, 0x000000D0}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000019}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000019}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000019}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000019}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000019}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000019}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000019}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000019}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000019}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000019}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000019}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000019}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x00000014}, + {0xB0000000, 0x00000000}, + {0x100EE, 0x00000000}, + {0x100EE, 0x00004000}, + {0x10033, 0x00000080}, + {0x1003F, 0x000001A9}, + {0x10033, 0x00000081}, + {0x1003F, 0x000001A3}, + {0x10033, 0x00000082}, + {0x1003F, 0x0000019D}, + {0x10033, 0x00000083}, + {0x1003F, 0x00000197}, + {0x10033, 0x00000084}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000158}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000158}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000158}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000158}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000158}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000158}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000158}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000158}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000158}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000158}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000158}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000158}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x00000191}, + {0xB0000000, 0x00000000}, + {0x10033, 0x00000085}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000011F}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000011F}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000011F}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000011F}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000011F}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000011F}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000011F}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000011F}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000011F}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000011F}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000011F}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000011F}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x0000018B}, + {0xB0000000, 0x00000000}, + {0x10033, 0x00000086}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000119}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000119}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000119}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000119}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000119}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000119}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000119}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000119}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000119}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000119}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000119}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000119}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x0000014D}, + {0xB0000000, 0x00000000}, + {0x10033, 0x00000087}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000DF}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000DF}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000DF}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000DF}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000DF}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000DF}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000DF}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000DF}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000DF}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000DF}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000DF}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000DF}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x0000010B}, + {0xB0000000, 0x00000000}, + {0x10033, 0x00000088}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000D9}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000D9}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000D9}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000D9}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000D9}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000D9}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000D9}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000D9}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000D9}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000D9}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000D9}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000D9}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x000000DF}, + {0xB0000000, 0x00000000}, + {0x10033, 0x00000089}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000009F}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000009F}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000009F}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000009F}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000009F}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000009F}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000009F}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000009F}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000009F}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000009F}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000009F}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000009F}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x000000D9}, + {0xB0000000, 0x00000000}, + {0x10033, 0x0000008A}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000099}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000099}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000099}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000099}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000099}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000099}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000099}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000099}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000099}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000099}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000099}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000099}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x000000D3}, + {0xB0000000, 0x00000000}, + {0x10033, 0x0000008B}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000005F}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000005F}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000005F}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000005F}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000005F}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000005F}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000005F}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000005F}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000005F}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000005F}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000005F}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000005F}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x00000099}, + {0xB0000000, 0x00000000}, + {0x10033, 0x0000008C}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000059}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000059}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000059}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000059}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000059}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000059}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000059}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000059}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000059}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000059}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000059}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000059}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x00000093}, + {0xB0000000, 0x00000000}, + {0x10033, 0x0000008D}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001F}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001F}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001F}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001F}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001F}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001F}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001F}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001F}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001F}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001F}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001F}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001F}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x00000059}, + {0xB0000000, 0x00000000}, + {0x10033, 0x0000008E}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000019}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000019}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000019}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000019}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000019}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000019}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000019}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000019}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000019}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000019}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000019}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000019}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x00000053}, + {0xB0000000, 0x00000000}, + {0x10033, 0x0000008F}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000013}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000013}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000013}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000013}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000013}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000013}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000013}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000013}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000013}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000013}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000013}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000013}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x00000019}, + {0xB0000000, 0x00000000}, + {0x10033, 0x00000090}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000000D}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000000D}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000000D}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000000D}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000000D}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000000D}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000000D}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000000D}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000000D}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000000D}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000000D}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000000D}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x00000013}, + {0xB0000000, 0x00000000}, + {0x10033, 0x00000091}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000007}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000007}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000007}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000007}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000007}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000007}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000007}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000007}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000007}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000007}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000007}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000007}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x0000000D}, + {0xB0000000, 0x00000000}, + {0x10033, 0x000000A0}, + {0x1003F, 0x000001A9}, + {0x10033, 0x000000A1}, + {0x1003F, 0x000001A3}, + {0x10033, 0x000000A2}, + {0x1003F, 0x0000019D}, + {0x10033, 0x000000A3}, + {0x1003F, 0x00000197}, + {0x10033, 0x000000A4}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000158}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000158}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000158}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000158}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000158}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000158}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000158}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000158}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000158}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000158}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000158}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000158}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x00000191}, + {0xB0000000, 0x00000000}, + {0x10033, 0x000000A5}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000011F}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000011F}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000011F}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000011F}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000011F}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000011F}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000011F}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000011F}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000011F}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000011F}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000011F}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000011F}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x0000018B}, + {0xB0000000, 0x00000000}, + {0x10033, 0x000000A6}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000119}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000119}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000119}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000119}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000119}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000119}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000119}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000119}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000119}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000119}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000119}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000119}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x0000014D}, + {0xB0000000, 0x00000000}, + {0x10033, 0x000000A7}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000DF}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000DF}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000DF}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000DF}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000DF}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000DF}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000DF}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000DF}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000DF}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000DF}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000DF}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000DF}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x0000010B}, + {0xB0000000, 0x00000000}, + {0x10033, 0x000000A8}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000D9}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000D9}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000D9}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000D9}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000D9}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000D9}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000D9}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000D9}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000D9}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000D9}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000D9}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000D9}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x000000DF}, + {0xB0000000, 0x00000000}, + {0x10033, 0x000000A9}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000009F}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000009F}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000009F}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000009F}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000009F}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000009F}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000009F}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000009F}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000009F}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000009F}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000009F}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000009F}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x000000D9}, + {0xB0000000, 0x00000000}, + {0x10033, 0x000000AA}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000099}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000099}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000099}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000099}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000099}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000099}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000099}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000099}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000099}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000099}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000099}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000099}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x000000D3}, + {0xB0000000, 0x00000000}, + {0x10033, 0x000000AB}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000005F}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000005F}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000005F}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000005F}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000005F}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000005F}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000005F}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000005F}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000005F}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000005F}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000005F}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000005F}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x00000099}, + {0xB0000000, 0x00000000}, + {0x10033, 0x000000AC}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000059}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000059}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000059}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000059}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000059}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000059}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000059}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000059}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000059}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000059}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000059}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000059}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x00000093}, + {0xB0000000, 0x00000000}, + {0x10033, 0x000000AD}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001F}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001F}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001F}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001F}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001F}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001F}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001F}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001F}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001F}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001F}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001F}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001F}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x00000059}, + {0xB0000000, 0x00000000}, + {0x10033, 0x000000AE}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000019}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000019}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000019}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000019}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000019}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000019}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000019}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000019}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000019}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000019}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000019}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000019}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x00000053}, + {0xB0000000, 0x00000000}, + {0x10033, 0x000000AF}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000013}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000013}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000013}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000013}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000013}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000013}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000013}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000013}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000013}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000013}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000013}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000013}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x00000019}, + {0xB0000000, 0x00000000}, + {0x10033, 0x000000B0}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000000D}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000000D}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000000D}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000000D}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000000D}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000000D}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000000D}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000000D}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000000D}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000000D}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000000D}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000000D}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x00000013}, + {0xB0000000, 0x00000000}, + {0x10033, 0x000000B1}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000007}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000007}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000007}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000007}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000007}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000007}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000007}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000007}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000007}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000007}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000007}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000007}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x0000000D}, + {0xB0000000, 0x00000000}, + {0x10033, 0x000000C0}, + {0x1003F, 0x000001A9}, + {0x10033, 0x000000C1}, + {0x1003F, 0x000001A3}, + {0x10033, 0x000000C2}, + {0x1003F, 0x0000019D}, + {0x10033, 0x000000C3}, + {0x1003F, 0x00000197}, + {0x10033, 0x000000C4}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000158}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000158}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000158}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000158}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000158}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000158}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000158}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000158}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000158}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000158}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000158}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000158}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x00000191}, + {0xB0000000, 0x00000000}, + {0x10033, 0x000000C5}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000011F}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000011F}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000011F}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000011F}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000011F}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000011F}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000011F}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000011F}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000011F}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000011F}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000011F}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000011F}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x0000018B}, + {0xB0000000, 0x00000000}, + {0x10033, 0x000000C6}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000119}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000119}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000119}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000119}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000119}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000119}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000119}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000119}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000119}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000119}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000119}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000119}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x0000014D}, + {0xB0000000, 0x00000000}, + {0x10033, 0x000000C7}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000DF}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000DF}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000DF}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000DF}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000DF}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000DF}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000DF}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000DF}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000DF}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000DF}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000DF}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000DF}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x0000010B}, + {0xB0000000, 0x00000000}, + {0x10033, 0x000000C8}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000D9}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000D9}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000D9}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000D9}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000D9}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000D9}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000D9}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000D9}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000D9}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000D9}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000D9}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000D9}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x000000DF}, + {0xB0000000, 0x00000000}, + {0x10033, 0x000000C9}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000009F}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000009F}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000009F}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000009F}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000009F}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000009F}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000009F}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000009F}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000009F}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000009F}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000009F}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000009F}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x000000D9}, + {0xB0000000, 0x00000000}, + {0x10033, 0x000000CA}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000099}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000099}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000099}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000099}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000099}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000099}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000099}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000099}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000099}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000099}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000099}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000099}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x000000D3}, + {0xB0000000, 0x00000000}, + {0x10033, 0x000000CB}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000005F}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000005F}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000005F}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000005F}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000005F}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000005F}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000005F}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000005F}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000005F}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000005F}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000005F}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000005F}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x00000099}, + {0xB0000000, 0x00000000}, + {0x10033, 0x000000CC}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000059}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000059}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000059}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000059}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000059}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000059}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000059}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000059}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000059}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000059}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000059}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000059}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x00000093}, + {0xB0000000, 0x00000000}, + {0x10033, 0x000000CD}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001F}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001F}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001F}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001F}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001F}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001F}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001F}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001F}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001F}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001F}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001F}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001F}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x00000059}, + {0xB0000000, 0x00000000}, + {0x10033, 0x000000CE}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000019}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000019}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000019}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000019}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000019}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000019}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000019}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000019}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000019}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000019}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000019}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000019}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x00000053}, + {0xB0000000, 0x00000000}, + {0x10033, 0x000000CF}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000013}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000013}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000013}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000013}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000013}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000013}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000013}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000013}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000013}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000013}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000013}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000013}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x00000019}, + {0xB0000000, 0x00000000}, + {0x10033, 0x000000D0}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000000D}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000000D}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000000D}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000000D}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000000D}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000000D}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000000D}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000000D}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000000D}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000000D}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000000D}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000000D}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x00000013}, + {0xB0000000, 0x00000000}, + {0x10033, 0x000000D1}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000007}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000007}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000007}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000007}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000007}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000007}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000007}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000007}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000007}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000007}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000007}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000007}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x0000000D}, + {0xB0000000, 0x00000000}, + {0x100EE, 0x00000000}, + {0x100EE, 0x00002000}, + {0x10033, 0x00000000}, + {0x1003F, 0x000000F6}, + {0x10033, 0x00000001}, + {0x1003F, 0x000000F3}, + {0x10033, 0x00000002}, + {0x1003F, 0x000000F0}, + {0x10033, 0x00000003}, + {0x1003F, 0x000000ED}, + {0x10033, 0x00000004}, + {0x1003F, 0x000000EA}, + {0x10033, 0x00000005}, + {0x1003F, 0x000000E7}, + {0x10033, 0x00000006}, + {0x1003F, 0x000000A6}, + {0x10033, 0x00000007}, + {0x1003F, 0x000000A3}, + {0x10033, 0x00000008}, + {0x1003F, 0x00000063}, + {0x10033, 0x00000009}, + {0x1003F, 0x00000060}, + {0x10033, 0x0000000A}, + {0x1003F, 0x00000023}, + {0x10033, 0x0000000B}, + {0x1003F, 0x00000020}, + {0x10033, 0x0000000C}, + {0x1003F, 0x0000001D}, + {0x10033, 0x0000000D}, + {0x1003F, 0x0000001A}, + {0x10033, 0x0000000E}, + {0x1003F, 0x00000017}, + {0x10033, 0x0000000F}, + {0x1003F, 0x00000014}, + {0x10033, 0x00000010}, + {0x1003F, 0x00000011}, + {0x100EE, 0x00000000}, + {0x100EE, 0x00004000}, + {0x10033, 0x00000000}, + {0x1003F, 0x000001AF}, + {0x10033, 0x00000001}, + {0x1003F, 0x000001A9}, + {0x10033, 0x00000002}, + {0x1003F, 0x000001A3}, + {0x10033, 0x00000003}, + {0x1003F, 0x0000019D}, + {0x10033, 0x00000004}, + {0x1003F, 0x00000197}, + {0x10033, 0x00000005}, + {0x1003F, 0x0000015F}, + {0x10033, 0x00000006}, + {0x1003F, 0x00000159}, + {0x10033, 0x00000007}, + {0x1003F, 0x0000011F}, + {0x10033, 0x00000008}, + {0x1003F, 0x00000119}, + {0x10033, 0x00000009}, + {0x1003F, 0x000000DF}, + {0x10033, 0x0000000A}, + {0x1003F, 0x000000D9}, + {0x10033, 0x0000000B}, + {0x1003F, 0x0000009F}, + {0x10033, 0x0000000C}, + {0x1003F, 0x00000099}, + {0x10033, 0x0000000D}, + {0x1003F, 0x0000005F}, + {0x10033, 0x0000000E}, + {0x1003F, 0x00000059}, + {0x10033, 0x0000000F}, + {0x1003F, 0x0000001F}, + {0x10033, 0x00000010}, + {0x1003F, 0x00000019}, + {0x10033, 0x00000011}, + {0x1003F, 0x00000013}, + {0x100EE, 0x00000000}, + {0x10005, 0x00000001}, + {0x09F, 0x00000032}, +}; + +static const struct rtw89_reg2_def rtw89_8852b_phy_radiob_regs[] = { + {0xF0010000, 0x00000000}, + {0xF0020000, 0x00000001}, + {0xF0010001, 0x00000002}, + {0xF0020001, 0x00000003}, + {0xF0030001, 0x00000004}, + {0xF0040001, 0x00000005}, + {0xF0050001, 0x00000006}, + {0xF0060001, 0x00000007}, + {0xF0070001, 0x00000008}, + {0xF0080001, 0x00000009}, + {0xF0290001, 0x0000000A}, + {0xF02B0001, 0x0000000B}, + {0x005, 0x00000000}, + {0x000, 0x00030000}, + {0x10000, 0x00030000}, + {0x018, 0x00011124}, + {0x10018, 0x00011124}, + {0x000, 0x00033C00}, + {0x10000, 0x00033C00}, + {0x01A, 0x00040004}, + {0x011, 0x00014073}, + {0x067, 0x00000070}, + {0x059, 0x000A0000}, + {0x066, 0x00000100}, + {0x05A, 0x0007F000}, + {0x0A4, 0x0006FF12}, + {0x043, 0x00005000}, + {0x0E1, 0x00000001}, + {0x0DD, 0x000001A0}, + {0x0CA, 0x00002000}, + {0x0D3, 0x00000003}, + {0x0B3, 0x0004EFE0}, + {0x0B4, 0x0007C03E}, + {0x0B5, 0x0003A201}, + {0x0BB, 0x000C7000}, + {0x0ED, 0x00002000}, + {0x033, 0x00000002}, + {0x03D, 0x0004A883}, + {0x03E, 0x00000000}, + {0x03F, 0x00000001}, + {0x033, 0x00000006}, + {0x03D, 0x0004A883}, + {0x03E, 0x00000000}, + {0x03F, 0x00000001}, + {0x0ED, 0x00000000}, + {0x018, 0x00001001}, + {0x10018, 0x00001001}, + {0x002, 0x0000000D}, + {0x10002, 0x0000000D}, + {0x0EE, 0x00000004}, + {0x033, 0x0000000B}, + {0x03F, 0x0000000B}, + {0x033, 0x0000000C}, + {0x03F, 0x00000012}, + {0x033, 0x0000000D}, + {0x03F, 0x00000019}, + {0x0EE, 0x00000000}, + {0x08F, 0x000D0F7A}, + {0x0EF, 0x00080000}, + {0x033, 0x00000008}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03E, 0x00000031}, + {0x03F, 0x00000D30}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03E, 0x00000031}, + {0x03F, 0x00000D30}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03E, 0x00000031}, + {0x03F, 0x00000D30}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03E, 0x00000031}, + {0x03F, 0x00000D30}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03E, 0x00000031}, + {0x03F, 0x00000D30}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03E, 0x00000031}, + {0x03F, 0x00000D30}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03E, 0x00000031}, + {0x03F, 0x00000D30}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03E, 0x00000031}, + {0x03F, 0x00000D30}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03E, 0x00000031}, + {0x03F, 0x00000D30}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03E, 0x00000031}, + {0x03F, 0x00000D30}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03E, 0x00000031}, + {0x03F, 0x00000D30}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03E, 0x00000031}, + {0x03F, 0x00000D30}, + {0xA0000000, 0x00000000}, + {0x03E, 0x000000C4}, + {0x03F, 0x000034C0}, + {0xB0000000, 0x00000000}, + {0x033, 0x0000000A}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03E, 0x00000031}, + {0x03F, 0x00000D74}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03E, 0x00000031}, + {0x03F, 0x00000D74}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03E, 0x00000031}, + {0x03F, 0x00000D74}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03E, 0x00000031}, + {0x03F, 0x00000D74}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03E, 0x00000031}, + {0x03F, 0x00000D74}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03E, 0x00000031}, + {0x03F, 0x00000D74}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03E, 0x00000031}, + {0x03F, 0x00000D74}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03E, 0x00000031}, + {0x03F, 0x00000D74}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03E, 0x00000031}, + {0x03F, 0x00000D74}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03E, 0x00000031}, + {0x03F, 0x00000D74}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03E, 0x00000031}, + {0x03F, 0x00000D74}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03E, 0x00000031}, + {0x03F, 0x00000D74}, + {0xA0000000, 0x00000000}, + {0x03E, 0x000000C4}, + {0x03F, 0x000035D0}, + {0xB0000000, 0x00000000}, + {0x033, 0x0000000B}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03E, 0x00000031}, + {0x03F, 0x00000D72}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03E, 0x00000031}, + {0x03F, 0x00000D72}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03E, 0x00000031}, + {0x03F, 0x00000D72}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03E, 0x00000031}, + {0x03F, 0x00000D72}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03E, 0x00000031}, + {0x03F, 0x00000D72}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03E, 0x00000031}, + {0x03F, 0x00000D72}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03E, 0x00000031}, + {0x03F, 0x00000D72}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03E, 0x00000031}, + {0x03F, 0x00000D72}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03E, 0x00000031}, + {0x03F, 0x00000D72}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03E, 0x00000031}, + {0x03F, 0x00000D72}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03E, 0x00000031}, + {0x03F, 0x00000D72}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03E, 0x00000031}, + {0x03F, 0x00000D72}, + {0xA0000000, 0x00000000}, + {0x03E, 0x000000C4}, + {0x03F, 0x000035C8}, + {0xB0000000, 0x00000000}, + {0x033, 0x0000008A}, + {0x03E, 0x00000031}, + {0x03F, 0x00000D7D}, + {0x0EF, 0x00000000}, + {0x08D, 0x000CC800}, + {0x0EF, 0x00004000}, + {0x033, 0x00000007}, + {0x03F, 0x00000700}, + {0x033, 0x00000006}, + {0x03F, 0x00000700}, + {0x033, 0x00000005}, + {0x03F, 0x00090600}, + {0x033, 0x00000004}, + {0x03F, 0x000A3500}, + {0x033, 0x00000003}, + {0x03F, 0x000A3400}, + {0x033, 0x00000002}, + {0x03F, 0x00008B00}, + {0x033, 0x00000001}, + {0x03F, 0x00001B00}, + {0x033, 0x00000000}, + {0x03F, 0x00003A00}, + {0x033, 0x0000000F}, + {0x03F, 0x00000700}, + {0x033, 0x0000000E}, + {0x03F, 0x00000700}, + {0x033, 0x0000000D}, + {0x03F, 0x00090600}, + {0x033, 0x0000000C}, + {0x03F, 0x000A3500}, + {0x033, 0x0000000B}, + {0x03F, 0x000A3400}, + {0x033, 0x0000000A}, + {0x03F, 0x00008B00}, + {0x033, 0x00000009}, + {0x03F, 0x00001B00}, + {0x033, 0x00000008}, + {0x03F, 0x00003A00}, + {0x033, 0x00000017}, + {0x03F, 0x00000705}, + {0x033, 0x00000016}, + {0x03F, 0x00000705}, + {0x033, 0x00000015}, + {0x03F, 0x00090605}, + {0x033, 0x00000014}, + {0x03F, 0x000A3505}, + {0x033, 0x00000013}, + {0x03F, 0x000A3405}, + {0x033, 0x00000012}, + {0x03F, 0x00008B05}, + {0x033, 0x00000011}, + {0x03F, 0x00001B05}, + {0x033, 0x00000010}, + {0x03F, 0x00003A05}, + {0x0EF, 0x00000000}, + {0x0EE, 0x00000010}, + {0x033, 0x00000006}, + {0x03F, 0x00000003}, + {0x033, 0x00000007}, + {0x03F, 0x00000003}, + {0x033, 0x00000008}, + {0x03F, 0x00000001}, + {0x0EE, 0x00000000}, + {0x0EF, 0x00001000}, + {0x033, 0x00000000}, + {0x03F, 0x00000015}, + {0x033, 0x00000001}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00000005}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00000005}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00000017}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00000017}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00000017}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00000017}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00000017}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00000017}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00000017}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00000017}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00000017}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00000017}, + {0xA0000000, 0x00000000}, + {0x03F, 0x00000005}, + {0xB0000000, 0x00000000}, + {0x033, 0x00000002}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00000017}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00000017}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00000015}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00000015}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00000015}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00000015}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00000015}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00000015}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00000015}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00000015}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00000015}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00000015}, + {0xA0000000, 0x00000000}, + {0x03F, 0x00000017}, + {0xB0000000, 0x00000000}, + {0x033, 0x00000003}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00000007}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00000007}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00000005}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00000005}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00000005}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00000005}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00000005}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00000005}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00000005}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00000005}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00000005}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00000005}, + {0xA0000000, 0x00000000}, + {0x03F, 0x00000007}, + {0xB0000000, 0x00000000}, + {0x0EF, 0x00000000}, + {0x0EF, 0x00008000}, + {0x033, 0x00000000}, + {0x03E, 0x00004FC0}, + {0x03F, 0x00000087}, + {0x033, 0x00000001}, + {0x03E, 0x000046C0}, + {0x03F, 0x00000087}, + {0x033, 0x00000002}, + {0x03E, 0x00004240}, + {0x03F, 0x00000087}, + {0x033, 0x00000003}, + {0x03E, 0x00008010}, + {0x03F, 0x00000147}, + {0x033, 0x00000004}, + {0x03E, 0x0000A048}, + {0x03F, 0x0000004F}, + {0x033, 0x00000005}, + {0x03E, 0x0000A030}, + {0x03F, 0x0000005F}, + {0x033, 0x00000006}, + {0x03E, 0x0000A000}, + {0x03F, 0x0000009F}, + {0x033, 0x00000008}, + {0x03E, 0x00004FC0}, + {0x03F, 0x00000087}, + {0x033, 0x00000009}, + {0x03E, 0x000046C0}, + {0x03F, 0x00000087}, + {0x033, 0x0000000A}, + {0x03E, 0x00004240}, + {0x03F, 0x00000087}, + {0x033, 0x0000000B}, + {0x03E, 0x00008010}, + {0x03F, 0x00000147}, + {0x033, 0x0000000C}, + {0x03E, 0x0000A048}, + {0x03F, 0x0000004F}, + {0x033, 0x0000000D}, + {0x03E, 0x0000A030}, + {0x03F, 0x0000005F}, + {0x033, 0x0000000E}, + {0x03E, 0x0000A000}, + {0x03F, 0x0000009F}, + {0x033, 0x00000010}, + {0x03E, 0x00004FC0}, + {0x03F, 0x00000087}, + {0x033, 0x00000011}, + {0x03E, 0x000046C0}, + {0x03F, 0x00000087}, + {0x033, 0x00000012}, + {0x03E, 0x00004240}, + {0x03F, 0x00000087}, + {0x033, 0x00000013}, + {0x03E, 0x00008010}, + {0x03F, 0x00000147}, + {0x033, 0x00000014}, + {0x03E, 0x0000A048}, + {0x03F, 0x0000004F}, + {0x033, 0x00000015}, + {0x03E, 0x0000A030}, + {0x03F, 0x0000005F}, + {0x033, 0x00000016}, + {0x03E, 0x0000A000}, + {0x03F, 0x0000009F}, + {0x033, 0x00000020}, + {0x03E, 0x00004FC0}, + {0x03F, 0x00000087}, + {0x033, 0x00000021}, + {0x03E, 0x000046C0}, + {0x03F, 0x00000087}, + {0x033, 0x00000022}, + {0x03E, 0x00004240}, + {0x03F, 0x00000087}, + {0x033, 0x00000023}, + {0x03E, 0x00008010}, + {0x03F, 0x00000147}, + {0x033, 0x00000024}, + {0x03E, 0x0000A048}, + {0x03F, 0x0000004F}, + {0x033, 0x00000025}, + {0x03E, 0x0000A030}, + {0x03F, 0x0000005F}, + {0x033, 0x00000026}, + {0x03E, 0x0000A000}, + {0x03F, 0x0000009F}, + {0x033, 0x00000028}, + {0x03E, 0x00004FC0}, + {0x03F, 0x00000087}, + {0x033, 0x00000029}, + {0x03E, 0x000046C0}, + {0x03F, 0x00000087}, + {0x033, 0x0000002A}, + {0x03E, 0x00004240}, + {0x03F, 0x00000087}, + {0x033, 0x0000002B}, + {0x03E, 0x00008010}, + {0x03F, 0x00000147}, + {0x033, 0x0000002C}, + {0x03E, 0x0000A048}, + {0x03F, 0x0000004F}, + {0x033, 0x0000002D}, + {0x03E, 0x0000A030}, + {0x03F, 0x0000005F}, + {0x033, 0x0000002E}, + {0x03E, 0x0000A000}, + {0x03F, 0x0000009F}, + {0x033, 0x00000030}, + {0x03E, 0x00004FC0}, + {0x03F, 0x00000087}, + {0x033, 0x00000031}, + {0x03E, 0x000046C0}, + {0x03F, 0x00000087}, + {0x033, 0x00000032}, + {0x03E, 0x00004240}, + {0x03F, 0x00000087}, + {0x033, 0x00000033}, + {0x03E, 0x00008010}, + {0x03F, 0x00000147}, + {0x033, 0x00000034}, + {0x03E, 0x0000A048}, + {0x03F, 0x0000004F}, + {0x033, 0x00000035}, + {0x03E, 0x0000A030}, + {0x03F, 0x0000005F}, + {0x033, 0x00000036}, + {0x03E, 0x0000A000}, + {0x03F, 0x0000009F}, + {0x0EF, 0x00000000}, + {0x0EF, 0x00000100}, + {0x033, 0x00000000}, + {0x03F, 0x00004346}, + {0x033, 0x00000001}, + {0x03F, 0x00004346}, + {0x033, 0x00000002}, + {0x03F, 0x00004346}, + {0x033, 0x00000003}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004376}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004376}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004376}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004376}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004376}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004376}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004376}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004376}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004376}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004376}, + {0xA0000000, 0x00000000}, + {0x03F, 0x00004346}, + {0xB0000000, 0x00000000}, + {0x033, 0x00000004}, + {0x03F, 0x00004346}, + {0x033, 0x00000005}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004317}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004317}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004317}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004317}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004317}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004317}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004317}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004317}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004317}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004317}, + {0xA0000000, 0x00000000}, + {0x03F, 0x00004346}, + {0xB0000000, 0x00000000}, + {0x033, 0x00000006}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004376}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004376}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004376}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004376}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004376}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004376}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004376}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004376}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004376}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004376}, + {0xA0000000, 0x00000000}, + {0x03F, 0x00004346}, + {0xB0000000, 0x00000000}, + {0x033, 0x00000007}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004376}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004376}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004376}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004376}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004376}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004376}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004376}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004376}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004376}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004376}, + {0xA0000000, 0x00000000}, + {0x03F, 0x00004346}, + {0xB0000000, 0x00000000}, + {0x033, 0x00000008}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004376}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004376}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004376}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004376}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004376}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004376}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004376}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004376}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004376}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004376}, + {0xA0000000, 0x00000000}, + {0x03F, 0x00004346}, + {0xB0000000, 0x00000000}, + {0x033, 0x00000009}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004376}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004376}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004376}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004376}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004376}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004376}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004376}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004376}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004376}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004376}, + {0xA0000000, 0x00000000}, + {0x03F, 0x00004346}, + {0xB0000000, 0x00000000}, + {0x033, 0x0000000A}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x000043A6}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x000043A6}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x000043A6}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x000043A6}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x000043A6}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x000043A6}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x000043A6}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x000043A6}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x000043A6}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x000043A6}, + {0xA0000000, 0x00000000}, + {0x03F, 0x00004346}, + {0xB0000000, 0x00000000}, + {0x033, 0x0000000B}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x000043A6}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x000043A6}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x000043A6}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x000043A6}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x000043A6}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x000043A6}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x000043A6}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x000043A6}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x000043A6}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x000043A6}, + {0xA0000000, 0x00000000}, + {0x03F, 0x00004346}, + {0xB0000000, 0x00000000}, + {0x033, 0x0000000C}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x000043A6}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x000043A6}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x000043A6}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x000043A6}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x000043A6}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x000043A6}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x000043A6}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x000043A6}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x000043A6}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x000043A6}, + {0xA0000000, 0x00000000}, + {0x03F, 0x00004346}, + {0xB0000000, 0x00000000}, + {0x033, 0x0000000D}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x000043A6}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x000043A6}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x000043A6}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x000043A6}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x000043A6}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x000043A6}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x000043A6}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x000043A6}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x000043A6}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x000043A6}, + {0xA0000000, 0x00000000}, + {0x03F, 0x00004346}, + {0xB0000000, 0x00000000}, + {0x033, 0x0000000E}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004366}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004366}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0xA0000000, 0x00000000}, + {0x03F, 0x00004366}, + {0xB0000000, 0x00000000}, + {0x033, 0x0000000F}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004366}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004366}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0xA0000000, 0x00000000}, + {0x03F, 0x00004366}, + {0xB0000000, 0x00000000}, + {0x033, 0x00000010}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004366}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004366}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0xA0000000, 0x00000000}, + {0x03F, 0x00004366}, + {0xB0000000, 0x00000000}, + {0x033, 0x00000011}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004366}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004366}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0xA0000000, 0x00000000}, + {0x03F, 0x00004366}, + {0xB0000000, 0x00000000}, + {0x033, 0x00000012}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004366}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004366}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0xA0000000, 0x00000000}, + {0x03F, 0x00004366}, + {0xB0000000, 0x00000000}, + {0x033, 0x00000013}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004366}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004366}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004347}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004347}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004347}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004347}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004347}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004347}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004347}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004347}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004347}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004347}, + {0xA0000000, 0x00000000}, + {0x03F, 0x00004366}, + {0xB0000000, 0x00000000}, + {0x033, 0x00000014}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004366}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004366}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0xA0000000, 0x00000000}, + {0x03F, 0x00004366}, + {0xB0000000, 0x00000000}, + {0x033, 0x00000015}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004366}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004366}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0xA0000000, 0x00000000}, + {0x03F, 0x00004366}, + {0xB0000000, 0x00000000}, + {0x033, 0x00000016}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004366}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004366}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0xA0000000, 0x00000000}, + {0x03F, 0x00004366}, + {0xB0000000, 0x00000000}, + {0x033, 0x00000017}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004366}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004366}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004347}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004347}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004347}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004347}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004347}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004347}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004347}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004347}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004347}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004347}, + {0xA0000000, 0x00000000}, + {0x03F, 0x00004366}, + {0xB0000000, 0x00000000}, + {0x033, 0x00000020}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004366}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004366}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0xA0000000, 0x00000000}, + {0x03F, 0x00004366}, + {0xB0000000, 0x00000000}, + {0x033, 0x00000021}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004366}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004366}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004347}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004347}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004347}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004347}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004347}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004347}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004347}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004347}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004347}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004347}, + {0xA0000000, 0x00000000}, + {0x03F, 0x00004366}, + {0xB0000000, 0x00000000}, + {0x033, 0x00000022}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004366}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004366}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00004346}, + {0xA0000000, 0x00000000}, + {0x03F, 0x00004366}, + {0xB0000000, 0x00000000}, + {0x033, 0x00000023}, + {0x03F, 0x00004386}, + {0x033, 0x00000024}, + {0x03F, 0x00004386}, + {0x033, 0x00000025}, + {0x03F, 0x00004386}, + {0x033, 0x00000026}, + {0x03F, 0x00004386}, + {0x033, 0x00000027}, + {0x03F, 0x00004386}, + {0x0EF, 0x00000000}, + {0x067, 0x00008072}, + {0x0EF, 0x00000010}, + {0x033, 0x00000001}, + {0x03F, 0x00000ED5}, + {0x033, 0x00000002}, + {0x03F, 0x00000FC5}, + {0x033, 0x00000003}, + {0x03F, 0x00000A93}, + {0x033, 0x00000004}, + {0x03F, 0x00000973}, + {0x033, 0x00000005}, + {0x03F, 0x00000761}, + {0x033, 0x00000006}, + {0x03F, 0x00000761}, + {0x0EF, 0x00000000}, + {0x0EF, 0x00000080}, + {0x033, 0x00000000}, + {0x03E, 0x0000000B}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00026558}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00026558}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022758}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022758}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022758}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022758}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022758}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022758}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022758}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022758}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022758}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022758}, + {0xA0000000, 0x00000000}, + {0x03F, 0x00026558}, + {0xB0000000, 0x00000000}, + {0x033, 0x00000001}, + {0x03E, 0x0000000B}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00026558}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00026558}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022758}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022758}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022758}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022758}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022758}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022758}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022758}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022758}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022758}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022758}, + {0xA0000000, 0x00000000}, + {0x03F, 0x00026558}, + {0xB0000000, 0x00000000}, + {0x033, 0x00000002}, + {0x03E, 0x0000000C}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00027558}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00027558}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020758}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020758}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020758}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020758}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020758}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020758}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020758}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020758}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020758}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020758}, + {0xA0000000, 0x00000000}, + {0x03F, 0x00027558}, + {0xB0000000, 0x00000000}, + {0x033, 0x00000003}, + {0x03E, 0x0000000C}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00027558}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00027558}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020758}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020758}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020758}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020758}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020758}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020758}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020758}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020758}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020758}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020758}, + {0xA0000000, 0x00000000}, + {0x03F, 0x00027558}, + {0xB0000000, 0x00000000}, + {0x033, 0x00000004}, + {0x03E, 0x0000000B}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00026558}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00026558}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022758}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022758}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022758}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022758}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022758}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022758}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022758}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022758}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022758}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022758}, + {0xA0000000, 0x00000000}, + {0x03F, 0x00026558}, + {0xB0000000, 0x00000000}, + {0x033, 0x00000005}, + {0x03E, 0x0000000B}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00026558}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00026558}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022758}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022758}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022758}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022758}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022758}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022758}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022758}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022758}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022758}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022758}, + {0xA0000000, 0x00000000}, + {0x03F, 0x00026558}, + {0xB0000000, 0x00000000}, + {0x033, 0x00000006}, + {0x03E, 0x0000000C}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00027558}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00027558}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020758}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020758}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020758}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020758}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020758}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020758}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020758}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020758}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020758}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020758}, + {0xA0000000, 0x00000000}, + {0x03F, 0x00027558}, + {0xB0000000, 0x00000000}, + {0x033, 0x00000007}, + {0x03E, 0x0000000C}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00027558}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00027558}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020758}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020758}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020758}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020758}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020758}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020758}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020758}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020758}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020758}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020758}, + {0xA0000000, 0x00000000}, + {0x03F, 0x00027558}, + {0xB0000000, 0x00000000}, + {0x033, 0x00000008}, + {0x03E, 0x0000000B}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00026558}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00026558}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022758}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022758}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022758}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022758}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022758}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022758}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022758}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022758}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022758}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022758}, + {0xA0000000, 0x00000000}, + {0x03F, 0x00026558}, + {0xB0000000, 0x00000000}, + {0x033, 0x00000009}, + {0x03E, 0x0000000B}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00026558}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00026558}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022758}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022758}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022758}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022758}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022758}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022758}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022758}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022758}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022758}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022758}, + {0xA0000000, 0x00000000}, + {0x03F, 0x00026558}, + {0xB0000000, 0x00000000}, + {0x033, 0x0000000A}, + {0x03E, 0x0000000C}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00027558}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00027558}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020758}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020758}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020758}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020758}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020758}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020758}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020758}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020758}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020758}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020758}, + {0xA0000000, 0x00000000}, + {0x03F, 0x00027558}, + {0xB0000000, 0x00000000}, + {0x033, 0x0000000B}, + {0x03E, 0x0000000C}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00027558}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00027558}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020758}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020758}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020758}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020758}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020758}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020758}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020758}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020758}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020758}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020758}, + {0xA0000000, 0x00000000}, + {0x03F, 0x00027558}, + {0xB0000000, 0x00000000}, + {0x033, 0x0000000C}, + {0x03E, 0x0000000B}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00026558}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00026558}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022758}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022758}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022758}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022758}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022758}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022758}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022758}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022758}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022758}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022758}, + {0xA0000000, 0x00000000}, + {0x03F, 0x00026558}, + {0xB0000000, 0x00000000}, + {0x033, 0x0000000D}, + {0x03E, 0x0000000B}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00026558}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00026558}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022758}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022758}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022758}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022758}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022758}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022758}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022758}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022758}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022758}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022758}, + {0xA0000000, 0x00000000}, + {0x03F, 0x00026558}, + {0xB0000000, 0x00000000}, + {0x033, 0x0000000E}, + {0x03E, 0x0000000C}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00027558}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00027558}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020758}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020758}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020758}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020758}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020758}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020758}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020758}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020758}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020758}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020758}, + {0xA0000000, 0x00000000}, + {0x03F, 0x00027558}, + {0xB0000000, 0x00000000}, + {0x033, 0x0000000F}, + {0x03E, 0x0000000C}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00027558}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00027558}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020758}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020758}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020758}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020758}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020758}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020758}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020758}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020758}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020758}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020758}, + {0xA0000000, 0x00000000}, + {0x03F, 0x00027558}, + {0xB0000000, 0x00000000}, + {0x033, 0x00000010}, + {0x03E, 0x0000000B}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00026558}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00026558}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022758}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022758}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022758}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022758}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022758}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022758}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022758}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022758}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022758}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022758}, + {0xA0000000, 0x00000000}, + {0x03F, 0x00026558}, + {0xB0000000, 0x00000000}, + {0x033, 0x00000011}, + {0x03E, 0x0000000B}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00026558}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00026558}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022758}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022758}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022758}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022758}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022758}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022758}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022758}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022758}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022758}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022758}, + {0xA0000000, 0x00000000}, + {0x03F, 0x00026558}, + {0xB0000000, 0x00000000}, + {0x033, 0x00000012}, + {0x03E, 0x0000000C}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00027558}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00027558}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020758}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020758}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020758}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020758}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020758}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020758}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020758}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020758}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020758}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020758}, + {0xA0000000, 0x00000000}, + {0x03F, 0x00027558}, + {0xB0000000, 0x00000000}, + {0x033, 0x00000013}, + {0x03E, 0x0000000C}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00027558}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00027558}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020758}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020758}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020758}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020758}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020758}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020758}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020758}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020758}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020758}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020758}, + {0xA0000000, 0x00000000}, + {0x03F, 0x00027558}, + {0xB0000000, 0x00000000}, + {0x033, 0x00000014}, + {0x03E, 0x0000000B}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00026558}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00026558}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0xA0000000, 0x00000000}, + {0x03F, 0x00026558}, + {0xB0000000, 0x00000000}, + {0x033, 0x00000015}, + {0x03E, 0x0000000B}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00026558}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00026558}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0xA0000000, 0x00000000}, + {0x03F, 0x00026558}, + {0xB0000000, 0x00000000}, + {0x033, 0x00000016}, + {0x03E, 0x0000000C}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00027558}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00027558}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022858}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022858}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022858}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022858}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022858}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022858}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022858}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022858}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022858}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022858}, + {0xA0000000, 0x00000000}, + {0x03F, 0x00027558}, + {0xB0000000, 0x00000000}, + {0x033, 0x00000017}, + {0x03E, 0x0000000C}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00027558}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00027558}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020858}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020858}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020858}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020858}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020858}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020858}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020858}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020858}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020858}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020858}, + {0xA0000000, 0x00000000}, + {0x03F, 0x00027558}, + {0xB0000000, 0x00000000}, + {0x033, 0x00000018}, + {0x03E, 0x0000000B}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00026458}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00026458}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0xA0000000, 0x00000000}, + {0x03F, 0x00026458}, + {0xB0000000, 0x00000000}, + {0x033, 0x00000019}, + {0x03E, 0x0000000B}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00026458}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00026458}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0xA0000000, 0x00000000}, + {0x03F, 0x00026458}, + {0xB0000000, 0x00000000}, + {0x033, 0x0000001A}, + {0x03E, 0x0000000B}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00027558}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00027558}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022858}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022858}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022858}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022858}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022858}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022858}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022858}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022858}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022858}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022858}, + {0xA0000000, 0x00000000}, + {0x03F, 0x00027558}, + {0xB0000000, 0x00000000}, + {0x033, 0x0000001B}, + {0x03E, 0x0000000B}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00027558}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00027558}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020858}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020858}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020858}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020858}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020858}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020858}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020858}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020858}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020858}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020858}, + {0xA0000000, 0x00000000}, + {0x03F, 0x00027558}, + {0xB0000000, 0x00000000}, + {0x033, 0x0000001C}, + {0x03E, 0x0000000B}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00026458}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00026458}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0xA0000000, 0x00000000}, + {0x03F, 0x00026458}, + {0xB0000000, 0x00000000}, + {0x033, 0x0000001D}, + {0x03E, 0x0000000B}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00026458}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00026458}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0xA0000000, 0x00000000}, + {0x03F, 0x00026458}, + {0xB0000000, 0x00000000}, + {0x033, 0x0000001E}, + {0x03E, 0x0000000C}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00027558}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00027558}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022858}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022858}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022858}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022858}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022858}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022858}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022858}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022858}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022858}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022858}, + {0xA0000000, 0x00000000}, + {0x03F, 0x00027558}, + {0xB0000000, 0x00000000}, + {0x033, 0x0000001F}, + {0x03E, 0x0000000C}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00027558}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00027558}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020858}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020858}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020858}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020858}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020858}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020858}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020858}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020858}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020858}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020858}, + {0xA0000000, 0x00000000}, + {0x03F, 0x00027558}, + {0xB0000000, 0x00000000}, + {0x033, 0x00000020}, + {0x03E, 0x0000000C}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00026458}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00026458}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0xA0000000, 0x00000000}, + {0x03F, 0x00026458}, + {0xB0000000, 0x00000000}, + {0x033, 0x00000021}, + {0x03E, 0x0000000C}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00026458}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00026458}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0xA0000000, 0x00000000}, + {0x03F, 0x00026458}, + {0xB0000000, 0x00000000}, + {0x033, 0x00000022}, + {0x03E, 0x0000000C}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00027558}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00027558}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022858}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022858}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022858}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022858}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022858}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022858}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022858}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022858}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022858}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022858}, + {0xA0000000, 0x00000000}, + {0x03F, 0x00027558}, + {0xB0000000, 0x00000000}, + {0x033, 0x00000023}, + {0x03E, 0x0000000C}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00027558}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00027558}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020858}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020858}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020858}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020858}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020858}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020858}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020858}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020858}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020858}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020858}, + {0xA0000000, 0x00000000}, + {0x03F, 0x00027558}, + {0xB0000000, 0x00000000}, + {0x033, 0x00000024}, + {0x03E, 0x0000000C}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00026458}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00026458}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0xA0000000, 0x00000000}, + {0x03F, 0x00026458}, + {0xB0000000, 0x00000000}, + {0x033, 0x00000025}, + {0x03E, 0x0000000C}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00026458}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00026458}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0xA0000000, 0x00000000}, + {0x03F, 0x00026458}, + {0xB0000000, 0x00000000}, + {0x033, 0x00000026}, + {0x03E, 0x0000000C}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00027558}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00027558}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022858}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022858}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022858}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022858}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022858}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022858}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022858}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022858}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022858}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022858}, + {0xA0000000, 0x00000000}, + {0x03F, 0x00027558}, + {0xB0000000, 0x00000000}, + {0x033, 0x00000027}, + {0x03E, 0x0000000C}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00027558}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00027558}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020858}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020858}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020858}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020858}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020858}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020858}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020858}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020858}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020858}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020858}, + {0xA0000000, 0x00000000}, + {0x03F, 0x00027558}, + {0xB0000000, 0x00000000}, + {0x033, 0x00000028}, + {0x03E, 0x0000000C}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00026458}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00026458}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0xA0000000, 0x00000000}, + {0x03F, 0x00026458}, + {0xB0000000, 0x00000000}, + {0x033, 0x00000029}, + {0x03E, 0x0000000C}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00026458}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00026458}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0xA0000000, 0x00000000}, + {0x03F, 0x00026458}, + {0xB0000000, 0x00000000}, + {0x033, 0x0000002A}, + {0x03E, 0x0000000C}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00027558}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00027558}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022858}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022858}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022858}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022858}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022858}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022858}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022858}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022858}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022858}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022858}, + {0xA0000000, 0x00000000}, + {0x03F, 0x00027558}, + {0xB0000000, 0x00000000}, + {0x033, 0x0000002B}, + {0x03E, 0x0000000C}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00027558}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00027558}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020858}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020858}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020858}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020858}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020858}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020858}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020858}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020858}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020858}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020858}, + {0xA0000000, 0x00000000}, + {0x03F, 0x00027558}, + {0xB0000000, 0x00000000}, + {0x033, 0x0000002C}, + {0x03E, 0x0000000C}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00026458}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00026458}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0xA0000000, 0x00000000}, + {0x03F, 0x00026458}, + {0xB0000000, 0x00000000}, + {0x033, 0x0000002D}, + {0x03E, 0x0000000C}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00026458}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00026458}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0xA0000000, 0x00000000}, + {0x03F, 0x00026458}, + {0xB0000000, 0x00000000}, + {0x033, 0x0000002E}, + {0x03E, 0x0000000C}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00027558}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00027558}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022858}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022858}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022858}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022858}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022858}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022858}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022858}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022858}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022858}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022858}, + {0xA0000000, 0x00000000}, + {0x03F, 0x00027558}, + {0xB0000000, 0x00000000}, + {0x033, 0x0000002F}, + {0x03E, 0x0000000C}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00027558}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00027558}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020858}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020858}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020858}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020858}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020858}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020858}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020858}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020858}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020858}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020858}, + {0xA0000000, 0x00000000}, + {0x03F, 0x00027558}, + {0xB0000000, 0x00000000}, + {0x033, 0x00000030}, + {0x03E, 0x0000000C}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00026458}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00026458}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0xA0000000, 0x00000000}, + {0x03F, 0x00026458}, + {0xB0000000, 0x00000000}, + {0x033, 0x00000031}, + {0x03E, 0x0000000C}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00026458}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00026458}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0xA0000000, 0x00000000}, + {0x03F, 0x00026458}, + {0xB0000000, 0x00000000}, + {0x033, 0x00000032}, + {0x03E, 0x0000000C}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00027558}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00027558}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022858}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022858}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022858}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022858}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022858}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022858}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022858}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022858}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022858}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022858}, + {0xA0000000, 0x00000000}, + {0x03F, 0x00027558}, + {0xB0000000, 0x00000000}, + {0x033, 0x00000033}, + {0x03E, 0x0000000C}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00027558}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00027558}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020858}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020858}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020858}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020858}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020858}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020858}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020858}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020858}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020858}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020858}, + {0xA0000000, 0x00000000}, + {0x03F, 0x00027558}, + {0xB0000000, 0x00000000}, + {0x033, 0x00000034}, + {0x03E, 0x0000000C}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00026458}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00026458}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0xA0000000, 0x00000000}, + {0x03F, 0x00026458}, + {0xB0000000, 0x00000000}, + {0x033, 0x00000035}, + {0x03E, 0x0000000C}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00026458}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00026458}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0xA0000000, 0x00000000}, + {0x03F, 0x00026458}, + {0xB0000000, 0x00000000}, + {0x033, 0x00000036}, + {0x03E, 0x0000000C}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00027558}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00027558}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022858}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022858}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022858}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022858}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022858}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022858}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022858}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022858}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022858}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022858}, + {0xA0000000, 0x00000000}, + {0x03F, 0x00027558}, + {0xB0000000, 0x00000000}, + {0x033, 0x00000037}, + {0x03E, 0x0000000C}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00027558}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00027558}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020858}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020858}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020858}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020858}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020858}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020858}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020858}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020858}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020858}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020858}, + {0xA0000000, 0x00000000}, + {0x03F, 0x00027558}, + {0xB0000000, 0x00000000}, + {0x033, 0x00000038}, + {0x03E, 0x0000000C}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00026458}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00026458}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0xA0000000, 0x00000000}, + {0x03F, 0x00026458}, + {0xB0000000, 0x00000000}, + {0x033, 0x00000039}, + {0x03E, 0x0000000C}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00026458}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00026458}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022658}, + {0xA0000000, 0x00000000}, + {0x03F, 0x00026458}, + {0xB0000000, 0x00000000}, + {0x033, 0x0000003A}, + {0x03E, 0x0000000C}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00027558}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00027558}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022858}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022858}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022858}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022858}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022858}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022858}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022858}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022858}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022858}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00022858}, + {0xA0000000, 0x00000000}, + {0x03F, 0x00027558}, + {0xB0000000, 0x00000000}, + {0x033, 0x0000003B}, + {0x03E, 0x0000000C}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00027558}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00027558}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020858}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020858}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020858}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020858}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020858}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020858}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020858}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020858}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020858}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00020858}, + {0xA0000000, 0x00000000}, + {0x03F, 0x00027558}, + {0xB0000000, 0x00000000}, + {0x0EF, 0x00000000}, + {0x0EE, 0x00000800}, + {0x033, 0x00000000}, + {0x03F, 0x00000001}, + {0x033, 0x00000001}, + {0x03F, 0x00000003}, + {0x033, 0x00000002}, + {0x03F, 0x00000005}, + {0x033, 0x00000003}, + {0x03F, 0x00000007}, + {0x033, 0x00000004}, + {0x03F, 0x00000001}, + {0x033, 0x00000005}, + {0x03F, 0x00000003}, + {0x033, 0x00000006}, + {0x03F, 0x00000006}, + {0x033, 0x00000007}, + {0x03F, 0x00000007}, + {0x0EE, 0x00000000}, + {0x0EE, 0x00001000}, + {0x033, 0x00000000}, + {0x03F, 0x00003000}, + {0x033, 0x00000001}, + {0x03F, 0x00003001}, + {0x033, 0x00000002}, + {0x03F, 0x00003003}, + {0x033, 0x00000003}, + {0x03F, 0x00003007}, + {0x033, 0x00000004}, + {0x03F, 0x0000300F}, + {0x033, 0x00000005}, + {0x03F, 0x0000310F}, + {0x033, 0x00000006}, + {0x03F, 0x0000330F}, + {0x033, 0x00000007}, + {0x03F, 0x0000330F}, + {0x033, 0x00000008}, + {0x03F, 0x00003000}, + {0x033, 0x00000009}, + {0x03F, 0x00003001}, + {0x033, 0x0000000A}, + {0x03F, 0x00003003}, + {0x033, 0x0000000B}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00003007}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00003007}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00003007}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00003007}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00003007}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00003007}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00003007}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00003007}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00003007}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00003007}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00003007}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00003007}, + {0xA0000000, 0x00000000}, + {0x03F, 0x00003103}, + {0xB0000000, 0x00000000}, + {0x033, 0x0000000C}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00003107}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00003107}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00003107}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00003107}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00003107}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00003107}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00003107}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00003107}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00003107}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00003107}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00003107}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00003107}, + {0xA0000000, 0x00000000}, + {0x03F, 0x00003307}, + {0xB0000000, 0x00000000}, + {0x033, 0x0000000D}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00003307}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00003307}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00003307}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00003307}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00003307}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00003307}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00003307}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00003307}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00003307}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00003307}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00003307}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00003307}, + {0xA0000000, 0x00000000}, + {0x03F, 0x00002307}, + {0xB0000000, 0x00000000}, + {0x033, 0x0000000E}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00001307}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00001307}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00001307}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00001307}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00001307}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00001307}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00001307}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00001307}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00001307}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00001307}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00001307}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00001307}, + {0xA0000000, 0x00000000}, + {0x03F, 0x00000307}, + {0xB0000000, 0x00000000}, + {0x033, 0x0000000F}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00000307}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00000307}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00000307}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00000307}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00000307}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00000307}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00000307}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00000307}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00000307}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00000307}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00000307}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x03F, 0x00000307}, + {0xA0000000, 0x00000000}, + {0x03F, 0x00000307}, + {0xB0000000, 0x00000000}, + {0x0EE, 0x00000000}, + {0x0EE, 0x00000200}, + {0x033, 0x00000000}, + {0x03F, 0x00000001}, + {0x033, 0x00000001}, + {0x03F, 0x00000003}, + {0x033, 0x00000002}, + {0x03F, 0x00000005}, + {0x033, 0x00000003}, + {0x03F, 0x00000007}, + {0x0EE, 0x00000000}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x0EC, 0x00000100}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x0EC, 0x00000100}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x0EC, 0x00000000}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x0EC, 0x00000000}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x0EC, 0x00000000}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x0EC, 0x00000000}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x0EC, 0x00000000}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x0EC, 0x00000000}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x0EC, 0x00000000}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x0EC, 0x00000000}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x0EC, 0x00000000}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x0EC, 0x00000000}, + {0xA0000000, 0x00000000}, + {0x0EC, 0x00000100}, + {0xB0000000, 0x00000000}, + {0x033, 0x00000004}, + {0x03D, 0x00000078}, + {0x03E, 0x00080000}, + {0x03F, 0x00000000}, + {0x033, 0x00000005}, + {0x03D, 0x0000007B}, + {0x03E, 0x00020000}, + {0x03F, 0x00000000}, + {0x0EC, 0x00000000}, + {0x0DE, 0x00000000}, + {0x0EF, 0x00000000}, + {0x033, 0x00000000}, + {0x008, 0x00060280}, + {0x009, 0x00030400}, + {0x0EF, 0x00000000}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x0EF, 0x00000400}, + {0x033, 0x00000000}, + {0x03F, 0x000001FF}, + {0x033, 0x00000001}, + {0x03F, 0x000001FF}, + {0x033, 0x00000002}, + {0x03F, 0x000001F7}, + {0x033, 0x00000003}, + {0x03F, 0x000000FF}, + {0x033, 0x00000004}, + {0x03F, 0x000000FF}, + {0x033, 0x00000005}, + {0x03F, 0x000000FF}, + {0x033, 0x00000006}, + {0x03F, 0x000000FF}, + {0x033, 0x00000007}, + {0x03F, 0x000000FF}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x0EF, 0x00000400}, + {0x033, 0x00000000}, + {0x03F, 0x000001FF}, + {0x033, 0x00000001}, + {0x03F, 0x000001FF}, + {0x033, 0x00000002}, + {0x03F, 0x000001F7}, + {0x033, 0x00000003}, + {0x03F, 0x000000FF}, + {0x033, 0x00000004}, + {0x03F, 0x000000FF}, + {0x033, 0x00000005}, + {0x03F, 0x000000FF}, + {0x033, 0x00000006}, + {0x03F, 0x000000FF}, + {0x033, 0x00000007}, + {0x03F, 0x000000FF}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x0EF, 0x00000400}, + {0x033, 0x00000000}, + {0x03F, 0x000001FF}, + {0x033, 0x00000001}, + {0x03F, 0x000001FF}, + {0x033, 0x00000002}, + {0x03F, 0x0000013F}, + {0x033, 0x00000003}, + {0x03F, 0x000000FB}, + {0x033, 0x00000004}, + {0x03F, 0x000000FB}, + {0x033, 0x00000005}, + {0x03F, 0x000000FB}, + {0x033, 0x00000006}, + {0x03F, 0x000000FB}, + {0x033, 0x00000007}, + {0x03F, 0x000000FB}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x0EF, 0x00000400}, + {0x033, 0x00000000}, + {0x03F, 0x000001FF}, + {0x033, 0x00000001}, + {0x03F, 0x000001FF}, + {0x033, 0x00000002}, + {0x03F, 0x0000013F}, + {0x033, 0x00000003}, + {0x03F, 0x000000FB}, + {0x033, 0x00000004}, + {0x03F, 0x000000FB}, + {0x033, 0x00000005}, + {0x03F, 0x000000FB}, + {0x033, 0x00000006}, + {0x03F, 0x000000FB}, + {0x033, 0x00000007}, + {0x03F, 0x000000FB}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x0EF, 0x00000400}, + {0x033, 0x00000000}, + {0x03F, 0x000001FF}, + {0x033, 0x00000001}, + {0x03F, 0x000001FF}, + {0x033, 0x00000002}, + {0x03F, 0x0000013F}, + {0x033, 0x00000003}, + {0x03F, 0x000000FB}, + {0x033, 0x00000004}, + {0x03F, 0x000000FB}, + {0x033, 0x00000005}, + {0x03F, 0x000000FB}, + {0x033, 0x00000006}, + {0x03F, 0x000000FB}, + {0x033, 0x00000007}, + {0x03F, 0x000000FB}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x0EF, 0x00000400}, + {0x033, 0x00000000}, + {0x03F, 0x000001FF}, + {0x033, 0x00000001}, + {0x03F, 0x000001FF}, + {0x033, 0x00000002}, + {0x03F, 0x0000013F}, + {0x033, 0x00000003}, + {0x03F, 0x000000FB}, + {0x033, 0x00000004}, + {0x03F, 0x000000FB}, + {0x033, 0x00000005}, + {0x03F, 0x000000FB}, + {0x033, 0x00000006}, + {0x03F, 0x000000FB}, + {0x033, 0x00000007}, + {0x03F, 0x000000FB}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x0EF, 0x00000400}, + {0x033, 0x00000000}, + {0x03F, 0x000001FF}, + {0x033, 0x00000001}, + {0x03F, 0x000001FF}, + {0x033, 0x00000002}, + {0x03F, 0x0000013F}, + {0x033, 0x00000003}, + {0x03F, 0x000000FB}, + {0x033, 0x00000004}, + {0x03F, 0x000000FB}, + {0x033, 0x00000005}, + {0x03F, 0x000000FB}, + {0x033, 0x00000006}, + {0x03F, 0x000000FB}, + {0x033, 0x00000007}, + {0x03F, 0x000000FB}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x0EF, 0x00000400}, + {0x033, 0x00000000}, + {0x03F, 0x000001FF}, + {0x033, 0x00000001}, + {0x03F, 0x000001FF}, + {0x033, 0x00000002}, + {0x03F, 0x0000013F}, + {0x033, 0x00000003}, + {0x03F, 0x000000FB}, + {0x033, 0x00000004}, + {0x03F, 0x000000FB}, + {0x033, 0x00000005}, + {0x03F, 0x000000FB}, + {0x033, 0x00000006}, + {0x03F, 0x000000FB}, + {0x033, 0x00000007}, + {0x03F, 0x000000FB}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x0EF, 0x00000400}, + {0x033, 0x00000000}, + {0x03F, 0x000001FF}, + {0x033, 0x00000001}, + {0x03F, 0x000001FF}, + {0x033, 0x00000002}, + {0x03F, 0x0000013F}, + {0x033, 0x00000003}, + {0x03F, 0x000000FB}, + {0x033, 0x00000004}, + {0x03F, 0x000000FB}, + {0x033, 0x00000005}, + {0x03F, 0x000000FB}, + {0x033, 0x00000006}, + {0x03F, 0x000000FB}, + {0x033, 0x00000007}, + {0x03F, 0x000000FB}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x0EF, 0x00000400}, + {0x033, 0x00000000}, + {0x03F, 0x000001FF}, + {0x033, 0x00000001}, + {0x03F, 0x000001FF}, + {0x033, 0x00000002}, + {0x03F, 0x0000013F}, + {0x033, 0x00000003}, + {0x03F, 0x000000FB}, + {0x033, 0x00000004}, + {0x03F, 0x000000FB}, + {0x033, 0x00000005}, + {0x03F, 0x000000FB}, + {0x033, 0x00000006}, + {0x03F, 0x000000FB}, + {0x033, 0x00000007}, + {0x03F, 0x000000FB}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x0EF, 0x00000400}, + {0x033, 0x00000000}, + {0x03F, 0x000001FF}, + {0x033, 0x00000001}, + {0x03F, 0x000001FF}, + {0x033, 0x00000002}, + {0x03F, 0x0000013F}, + {0x033, 0x00000003}, + {0x03F, 0x000000FB}, + {0x033, 0x00000004}, + {0x03F, 0x000000FB}, + {0x033, 0x00000005}, + {0x03F, 0x000000FB}, + {0x033, 0x00000006}, + {0x03F, 0x000000FB}, + {0x033, 0x00000007}, + {0x03F, 0x000000FB}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x0EF, 0x00000400}, + {0x033, 0x00000000}, + {0x03F, 0x000001FF}, + {0x033, 0x00000001}, + {0x03F, 0x000001FF}, + {0x033, 0x00000002}, + {0x03F, 0x0000013F}, + {0x033, 0x00000003}, + {0x03F, 0x000000FB}, + {0x033, 0x00000004}, + {0x03F, 0x000000FB}, + {0x033, 0x00000005}, + {0x03F, 0x000000FB}, + {0x033, 0x00000006}, + {0x03F, 0x000000FB}, + {0x033, 0x00000007}, + {0x03F, 0x000000FB}, + {0xA0000000, 0x00000000}, + {0x0EF, 0x00000400}, + {0x033, 0x00000000}, + {0x03F, 0x000001FF}, + {0x033, 0x00000001}, + {0x03F, 0x000001FF}, + {0x033, 0x00000002}, + {0x03F, 0x000001F7}, + {0x033, 0x00000003}, + {0x03F, 0x000000FF}, + {0x033, 0x00000004}, + {0x03F, 0x000000FF}, + {0x033, 0x00000005}, + {0x03F, 0x000000FF}, + {0x033, 0x00000006}, + {0x03F, 0x000000FF}, + {0x033, 0x00000007}, + {0x03F, 0x000000FF}, + {0xB0000000, 0x00000000}, + {0x0EF, 0x00000200}, + {0x033, 0x00000000}, + {0x03F, 0x0000017F}, + {0x033, 0x00000001}, + {0x03F, 0x0000017F}, + {0x033, 0x00000002}, + {0x03F, 0x0000017F}, + {0x033, 0x00000003}, + {0x03F, 0x0000007F}, + {0x033, 0x00000004}, + {0x03F, 0x0000007F}, + {0x033, 0x00000005}, + {0x03F, 0x0000007F}, + {0x033, 0x00000006}, + {0x03F, 0x0000007F}, + {0x033, 0x00000007}, + {0x03F, 0x0000007F}, + {0x0EF, 0x00000000}, + {0x06E, 0x00077A18}, + {0x06F, 0x00077A18}, + {0x06D, 0x00000C31}, + {0x0EF, 0x00020000}, + {0x033, 0x00000000}, + {0x03F, 0x000005FF}, + {0x0EF, 0x00000000}, + {0x005, 0x00000001}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x094, 0x000000FC}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x094, 0x000000FC}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x094, 0x000000FC}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x094, 0x000000FC}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x094, 0x000000FC}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x094, 0x000000FC}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x094, 0x000000FC}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x094, 0x000000FC}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x094, 0x000000FC}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x094, 0x000000FC}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x094, 0x000000FC}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x094, 0x000000FC}, + {0xA0000000, 0x00000000}, + {0x094, 0x000001FC}, + {0xB0000000, 0x00000000}, + {0x100EE, 0x00002000}, + {0x10033, 0x00000080}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000FB}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000FB}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000FB}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000FB}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000FB}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000FB}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000FB}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000FB}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000FB}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000FB}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000FB}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000FB}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x000000F6}, + {0xB0000000, 0x00000000}, + {0x10033, 0x00000081}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F8}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F8}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F8}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F8}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F8}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F8}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F8}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F8}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F8}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F8}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F8}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F8}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x000000F3}, + {0xB0000000, 0x00000000}, + {0x10033, 0x00000082}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F5}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F5}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F5}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F5}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F5}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F5}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F5}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F5}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F5}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F5}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F5}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F5}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x000000F0}, + {0xB0000000, 0x00000000}, + {0x10033, 0x00000083}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F2}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F2}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F2}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F2}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F2}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F2}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F2}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F2}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F2}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F2}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F2}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F2}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x000000ED}, + {0xB0000000, 0x00000000}, + {0x10033, 0x00000084}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000EF}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000EF}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000EF}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000EF}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000EF}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000EF}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000EF}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000EF}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000EF}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000EF}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000EF}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000EF}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x000000EA}, + {0xB0000000, 0x00000000}, + {0x10033, 0x00000085}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000EC}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000EC}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000EC}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000EC}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000EC}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000EC}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000EC}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000EC}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000EC}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000EC}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000EC}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000EC}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x000000E7}, + {0xB0000000, 0x00000000}, + {0x10033, 0x00000086}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000AB}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000AB}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000AB}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000AB}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000AB}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000AB}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000AB}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000AB}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000AB}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000AB}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000AB}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000AB}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x000000A6}, + {0xB0000000, 0x00000000}, + {0x10033, 0x00000087}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000A8}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000A8}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000A8}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000A8}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000A8}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000A8}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000A8}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000A8}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000A8}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000A8}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000A8}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000A8}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x000000A3}, + {0xB0000000, 0x00000000}, + {0x10033, 0x00000088}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000068}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000068}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000068}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000068}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000068}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000068}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000068}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000068}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000068}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000068}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000068}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000068}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x00000063}, + {0xB0000000, 0x00000000}, + {0x10033, 0x00000089}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000065}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000065}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000065}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000065}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000065}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000065}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000065}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000065}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000065}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000065}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000065}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000065}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x00000060}, + {0xB0000000, 0x00000000}, + {0x10033, 0x0000008A}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000002B}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000002B}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000002B}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000002B}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000002B}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000002B}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000002B}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000002B}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000002B}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000002B}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000002B}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000002B}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x00000026}, + {0xB0000000, 0x00000000}, + {0x10033, 0x0000008B}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000028}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000028}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000028}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000028}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000028}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000028}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000028}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000028}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000028}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000028}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000028}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000028}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x00000023}, + {0xB0000000, 0x00000000}, + {0x10033, 0x0000008C}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000025}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000025}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000025}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000025}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000025}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000025}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000025}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000025}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000025}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000025}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000025}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000025}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x00000020}, + {0xB0000000, 0x00000000}, + {0x10033, 0x0000008D}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000022}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000022}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000022}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000022}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000022}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000022}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000022}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000022}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000022}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000022}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000022}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000022}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x0000001D}, + {0xB0000000, 0x00000000}, + {0x10033, 0x0000008E}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001F}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001F}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001F}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001F}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001F}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001F}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001F}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001F}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001F}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001F}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001F}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001F}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x0000001A}, + {0xB0000000, 0x00000000}, + {0x10033, 0x0000008F}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001C}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001C}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001C}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001C}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001C}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001C}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001C}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001C}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001C}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001C}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001C}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001C}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x00000017}, + {0xB0000000, 0x00000000}, + {0x10033, 0x00000090}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000019}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000019}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000019}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000019}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000019}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000019}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000019}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000019}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000019}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000019}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000019}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000019}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x00000014}, + {0xB0000000, 0x00000000}, + {0x10033, 0x000000A0}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000FB}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000FB}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000FB}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000FB}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000FB}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000FB}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000FB}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000FB}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000FB}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000FB}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000FB}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000FB}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x000000F6}, + {0xB0000000, 0x00000000}, + {0x10033, 0x000000A1}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F8}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F8}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F8}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F8}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F8}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F8}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F8}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F8}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F8}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F8}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F8}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F8}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x000000F3}, + {0xB0000000, 0x00000000}, + {0x10033, 0x000000A2}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F5}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F5}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F5}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F5}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F5}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F5}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F5}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F5}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F5}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F5}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F5}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F5}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x000000F0}, + {0xB0000000, 0x00000000}, + {0x10033, 0x000000A3}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F2}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F2}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F2}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F2}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F2}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F2}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F2}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F2}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F2}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F2}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F2}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F2}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x000000ED}, + {0xB0000000, 0x00000000}, + {0x10033, 0x000000A4}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000EF}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000EF}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000EF}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000EF}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000EF}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000EF}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000EF}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000EF}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000EF}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000EF}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000EF}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000EF}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x000000EA}, + {0xB0000000, 0x00000000}, + {0x10033, 0x000000A5}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000EC}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000EC}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000EC}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000EC}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000EC}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000EC}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000EC}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000EC}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000EC}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000EC}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000EC}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000EC}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x000000E7}, + {0xB0000000, 0x00000000}, + {0x10033, 0x000000A6}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000AB}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000AB}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000AB}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000AB}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000AB}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000AB}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000AB}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000AB}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000AB}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000AB}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000AB}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000AB}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x000000A6}, + {0xB0000000, 0x00000000}, + {0x10033, 0x000000A7}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000A8}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000A8}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000A8}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000A8}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000A8}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000A8}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000A8}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000A8}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000A8}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000A8}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000A8}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000A8}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x000000A3}, + {0xB0000000, 0x00000000}, + {0x10033, 0x000000A8}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000068}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000068}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000068}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000068}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000068}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000068}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000068}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000068}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000068}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000068}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000068}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000068}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x00000063}, + {0xB0000000, 0x00000000}, + {0x10033, 0x000000A9}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000065}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000065}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000065}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000065}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000065}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000065}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000065}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000065}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000065}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000065}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000065}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000065}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x00000060}, + {0xB0000000, 0x00000000}, + {0x10033, 0x000000AA}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000002B}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000002B}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000002B}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000002B}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000002B}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000002B}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000002B}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000002B}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000002B}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000002B}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000002B}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000002B}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x00000026}, + {0xB0000000, 0x00000000}, + {0x10033, 0x000000AB}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000028}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000028}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000028}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000028}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000028}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000028}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000028}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000028}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000028}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000028}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000028}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000028}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x00000023}, + {0xB0000000, 0x00000000}, + {0x10033, 0x000000AC}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000025}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000025}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000025}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000025}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000025}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000025}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000025}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000025}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000025}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000025}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000025}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000025}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x00000020}, + {0xB0000000, 0x00000000}, + {0x10033, 0x000000AD}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000022}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000022}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000022}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000022}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000022}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000022}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000022}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000022}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000022}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000022}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000022}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000022}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x0000001D}, + {0xB0000000, 0x00000000}, + {0x10033, 0x000000AE}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001F}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001F}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001F}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001F}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001F}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001F}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001F}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001F}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001F}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001F}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001F}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001F}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x0000001A}, + {0xB0000000, 0x00000000}, + {0x10033, 0x000000AF}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001C}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001C}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001C}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001C}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001C}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001C}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001C}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001C}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001C}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001C}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001C}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001C}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x00000017}, + {0xB0000000, 0x00000000}, + {0x10033, 0x000000B0}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000019}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000019}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000019}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000019}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000019}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000019}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000019}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000019}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000019}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000019}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000019}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000019}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x00000014}, + {0xB0000000, 0x00000000}, + {0x10033, 0x000000C0}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000FB}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000FB}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000FB}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000FB}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000FB}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000FB}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000FB}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000FB}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000FB}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000FB}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000FB}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000FB}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x000000F6}, + {0xB0000000, 0x00000000}, + {0x10033, 0x000000C1}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F8}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F8}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F8}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F8}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F8}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F8}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F8}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F8}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F8}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F8}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F8}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F8}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x000000F3}, + {0xB0000000, 0x00000000}, + {0x10033, 0x000000C2}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F5}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F5}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F5}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F5}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F5}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F5}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F5}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F5}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F5}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F5}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F5}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F5}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x000000F0}, + {0xB0000000, 0x00000000}, + {0x10033, 0x000000C3}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F2}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F2}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F2}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F2}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F2}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F2}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F2}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F2}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F2}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F2}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F2}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000F2}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x000000ED}, + {0xB0000000, 0x00000000}, + {0x10033, 0x000000C4}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000EF}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000EF}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000EF}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000EF}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000EF}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000EF}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000EF}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000EF}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000EF}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000EF}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000EF}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000EF}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x000000EA}, + {0xB0000000, 0x00000000}, + {0x10033, 0x000000C5}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000EC}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000EC}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000EC}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000EC}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000EC}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000EC}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000EC}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000EC}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000EC}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000EC}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000EC}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000EC}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x000000E7}, + {0xB0000000, 0x00000000}, + {0x10033, 0x000000C6}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000AB}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000AB}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000AB}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000AB}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000AB}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000AB}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000AB}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000AB}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000AB}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000AB}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000AB}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000AB}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x000000A6}, + {0xB0000000, 0x00000000}, + {0x10033, 0x000000C7}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000A8}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000A8}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000A8}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000A8}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000A8}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000A8}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000A8}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000A8}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000A8}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000A8}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000A8}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000A8}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x000000A3}, + {0xB0000000, 0x00000000}, + {0x10033, 0x000000C8}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000068}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000068}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000068}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000068}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000068}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000068}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000068}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000068}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000068}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000068}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000068}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000068}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x00000063}, + {0xB0000000, 0x00000000}, + {0x10033, 0x000000C9}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000065}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000065}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000065}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000065}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000065}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000065}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000065}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000065}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000065}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000065}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000065}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000065}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x00000060}, + {0xB0000000, 0x00000000}, + {0x10033, 0x000000CA}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000002B}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000002B}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000002B}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000002B}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000002B}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000002B}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000002B}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000002B}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000002B}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000002B}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000002B}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000002B}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x00000026}, + {0xB0000000, 0x00000000}, + {0x10033, 0x000000CB}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000028}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000028}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000028}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000028}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000028}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000028}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000028}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000028}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000028}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000028}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000028}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000028}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x00000023}, + {0xB0000000, 0x00000000}, + {0x10033, 0x000000CC}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000025}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000025}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000025}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000025}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000025}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000025}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000025}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000025}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000025}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000025}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000025}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000025}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x00000020}, + {0xB0000000, 0x00000000}, + {0x10033, 0x000000CD}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000022}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000022}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000022}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000022}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000022}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000022}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000022}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000022}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000022}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000022}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000022}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000022}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x0000001D}, + {0xB0000000, 0x00000000}, + {0x10033, 0x000000CE}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001F}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001F}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001F}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001F}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001F}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001F}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001F}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001F}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001F}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001F}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001F}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001F}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x0000001A}, + {0xB0000000, 0x00000000}, + {0x10033, 0x000000CF}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001C}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001C}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001C}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001C}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001C}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001C}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001C}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001C}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001C}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001C}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001C}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001C}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x00000017}, + {0xB0000000, 0x00000000}, + {0x10033, 0x000000D0}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000019}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000019}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000019}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000019}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000019}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000019}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000019}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000019}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000019}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000019}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000019}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000019}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x00000014}, + {0xB0000000, 0x00000000}, + {0x100EE, 0x00000000}, + {0x100EE, 0x00004000}, + {0x10033, 0x00000080}, + {0x1003F, 0x000001A9}, + {0x10033, 0x00000081}, + {0x1003F, 0x000001A3}, + {0x10033, 0x00000082}, + {0x1003F, 0x0000019D}, + {0x10033, 0x00000083}, + {0x1003F, 0x00000197}, + {0x10033, 0x00000084}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000158}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000158}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000158}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000158}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000158}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000158}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000158}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000158}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000158}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000158}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000158}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000158}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x00000191}, + {0xB0000000, 0x00000000}, + {0x10033, 0x00000085}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000011F}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000011F}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000011F}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000011F}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000011F}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000011F}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000011F}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000011F}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000011F}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000011F}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000011F}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000011F}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x0000018B}, + {0xB0000000, 0x00000000}, + {0x10033, 0x00000086}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000119}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000119}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000119}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000119}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000119}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000119}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000119}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000119}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000119}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000119}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000119}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000119}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x0000014D}, + {0xB0000000, 0x00000000}, + {0x10033, 0x00000087}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000DF}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000DF}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000DF}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000DF}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000DF}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000DF}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000DF}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000DF}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000DF}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000DF}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000DF}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000DF}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x0000010B}, + {0xB0000000, 0x00000000}, + {0x10033, 0x00000088}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000D9}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000D9}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000D9}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000D9}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000D9}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000D9}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000D9}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000D9}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000D9}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000D9}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000D9}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000D9}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x000000DF}, + {0xB0000000, 0x00000000}, + {0x10033, 0x00000089}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000009F}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000009F}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000009F}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000009F}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000009F}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000009F}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000009F}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000009F}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000009F}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000009F}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000009F}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000009F}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x000000D9}, + {0xB0000000, 0x00000000}, + {0x10033, 0x0000008A}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000099}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000099}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000099}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000099}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000099}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000099}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000099}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000099}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000099}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000099}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000099}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000099}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x000000D3}, + {0xB0000000, 0x00000000}, + {0x10033, 0x0000008B}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000005F}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000005F}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000005F}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000005F}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000005F}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000005F}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000005F}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000005F}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000005F}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000005F}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000005F}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000005F}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x00000099}, + {0xB0000000, 0x00000000}, + {0x10033, 0x0000008C}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000059}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000059}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000059}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000059}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000059}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000059}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000059}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000059}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000059}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000059}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000059}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000059}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x00000093}, + {0xB0000000, 0x00000000}, + {0x10033, 0x0000008D}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001F}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001F}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001F}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001F}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001F}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001F}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001F}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001F}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001F}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001F}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001F}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001F}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x00000059}, + {0xB0000000, 0x00000000}, + {0x10033, 0x0000008E}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000019}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000019}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000019}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000019}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000019}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000019}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000019}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000019}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000019}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000019}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000019}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000019}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x00000053}, + {0xB0000000, 0x00000000}, + {0x10033, 0x0000008F}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000013}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000013}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000013}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000013}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000013}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000013}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000013}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000013}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000013}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000013}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000013}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000013}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x00000019}, + {0xB0000000, 0x00000000}, + {0x10033, 0x00000090}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000000D}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000000D}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000000D}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000000D}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000000D}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000000D}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000000D}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000000D}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000000D}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000000D}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000000D}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000000D}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x00000013}, + {0xB0000000, 0x00000000}, + {0x10033, 0x00000091}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000007}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000007}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000007}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000007}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000007}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000007}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000007}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000007}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000007}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000007}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000007}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000007}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x0000000D}, + {0xB0000000, 0x00000000}, + {0x10033, 0x000000A0}, + {0x1003F, 0x000001A9}, + {0x10033, 0x000000A1}, + {0x1003F, 0x000001A3}, + {0x10033, 0x000000A2}, + {0x1003F, 0x0000019D}, + {0x10033, 0x000000A3}, + {0x1003F, 0x00000197}, + {0x10033, 0x000000A4}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000158}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000158}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000158}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000158}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000158}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000158}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000158}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000158}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000158}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000158}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000158}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000158}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x00000191}, + {0xB0000000, 0x00000000}, + {0x10033, 0x000000A5}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000011F}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000011F}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000011F}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000011F}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000011F}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000011F}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000011F}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000011F}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000011F}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000011F}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000011F}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000011F}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x0000018B}, + {0xB0000000, 0x00000000}, + {0x10033, 0x000000A6}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000119}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000119}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000119}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000119}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000119}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000119}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000119}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000119}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000119}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000119}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000119}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000119}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x0000014D}, + {0xB0000000, 0x00000000}, + {0x10033, 0x000000A7}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000DF}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000DF}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000DF}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000DF}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000DF}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000DF}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000DF}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000DF}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000DF}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000DF}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000DF}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000DF}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x0000010B}, + {0xB0000000, 0x00000000}, + {0x10033, 0x000000A8}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000D9}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000D9}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000D9}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000D9}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000D9}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000D9}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000D9}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000D9}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000D9}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000D9}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000D9}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000D9}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x000000DF}, + {0xB0000000, 0x00000000}, + {0x10033, 0x000000A9}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000009F}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000009F}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000009F}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000009F}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000009F}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000009F}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000009F}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000009F}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000009F}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000009F}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000009F}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000009F}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x000000D9}, + {0xB0000000, 0x00000000}, + {0x10033, 0x000000AA}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000099}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000099}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000099}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000099}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000099}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000099}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000099}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000099}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000099}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000099}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000099}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000099}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x000000D3}, + {0xB0000000, 0x00000000}, + {0x10033, 0x000000AB}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000005F}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000005F}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000005F}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000005F}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000005F}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000005F}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000005F}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000005F}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000005F}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000005F}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000005F}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000005F}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x00000099}, + {0xB0000000, 0x00000000}, + {0x10033, 0x000000AC}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000059}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000059}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000059}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000059}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000059}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000059}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000059}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000059}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000059}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000059}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000059}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000059}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x00000093}, + {0xB0000000, 0x00000000}, + {0x10033, 0x000000AD}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001F}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001F}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001F}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001F}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001F}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001F}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001F}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001F}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001F}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001F}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001F}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001F}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x00000059}, + {0xB0000000, 0x00000000}, + {0x10033, 0x000000AE}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000019}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000019}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000019}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000019}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000019}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000019}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000019}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000019}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000019}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000019}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000019}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000019}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x00000053}, + {0xB0000000, 0x00000000}, + {0x10033, 0x000000AF}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000013}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000013}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000013}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000013}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000013}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000013}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000013}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000013}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000013}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000013}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000013}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000013}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x00000019}, + {0xB0000000, 0x00000000}, + {0x10033, 0x000000B0}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000000D}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000000D}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000000D}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000000D}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000000D}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000000D}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000000D}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000000D}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000000D}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000000D}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000000D}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000000D}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x00000013}, + {0xB0000000, 0x00000000}, + {0x10033, 0x000000B1}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000007}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000007}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000007}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000007}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000007}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000007}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000007}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000007}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000007}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000007}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000007}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000007}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x0000000D}, + {0xB0000000, 0x00000000}, + {0x10033, 0x000000C0}, + {0x1003F, 0x000001A9}, + {0x10033, 0x000000C1}, + {0x1003F, 0x000001A3}, + {0x10033, 0x000000C2}, + {0x1003F, 0x0000019D}, + {0x10033, 0x000000C3}, + {0x1003F, 0x00000197}, + {0x10033, 0x000000C4}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000158}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000158}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000158}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000158}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000158}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000158}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000158}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000158}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000158}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000158}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000158}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000158}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x00000191}, + {0xB0000000, 0x00000000}, + {0x10033, 0x000000C5}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000011F}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000011F}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000011F}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000011F}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000011F}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000011F}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000011F}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000011F}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000011F}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000011F}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000011F}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000011F}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x0000018B}, + {0xB0000000, 0x00000000}, + {0x10033, 0x000000C6}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000119}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000119}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000119}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000119}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000119}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000119}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000119}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000119}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000119}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000119}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000119}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000119}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x0000014D}, + {0xB0000000, 0x00000000}, + {0x10033, 0x000000C7}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000DF}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000DF}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000DF}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000DF}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000DF}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000DF}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000DF}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000DF}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000DF}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000DF}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000DF}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000DF}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x0000010B}, + {0xB0000000, 0x00000000}, + {0x10033, 0x000000C8}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000D9}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000D9}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000D9}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000D9}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000D9}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000D9}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000D9}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000D9}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000D9}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000D9}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000D9}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x000000D9}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x000000DF}, + {0xB0000000, 0x00000000}, + {0x10033, 0x000000C9}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000009F}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000009F}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000009F}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000009F}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000009F}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000009F}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000009F}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000009F}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000009F}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000009F}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000009F}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000009F}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x000000D9}, + {0xB0000000, 0x00000000}, + {0x10033, 0x000000CA}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000099}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000099}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000099}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000099}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000099}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000099}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000099}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000099}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000099}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000099}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000099}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000099}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x000000D3}, + {0xB0000000, 0x00000000}, + {0x10033, 0x000000CB}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000005F}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000005F}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000005F}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000005F}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000005F}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000005F}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000005F}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000005F}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000005F}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000005F}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000005F}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000005F}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x00000099}, + {0xB0000000, 0x00000000}, + {0x10033, 0x000000CC}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000059}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000059}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000059}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000059}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000059}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000059}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000059}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000059}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000059}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000059}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000059}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000059}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x00000093}, + {0xB0000000, 0x00000000}, + {0x10033, 0x000000CD}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001F}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001F}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001F}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001F}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001F}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001F}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001F}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001F}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001F}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001F}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001F}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000001F}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x00000059}, + {0xB0000000, 0x00000000}, + {0x10033, 0x000000CE}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000019}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000019}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000019}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000019}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000019}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000019}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000019}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000019}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000019}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000019}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000019}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000019}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x00000053}, + {0xB0000000, 0x00000000}, + {0x10033, 0x000000CF}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000013}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000013}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000013}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000013}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000013}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000013}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000013}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000013}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000013}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000013}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000013}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000013}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x00000019}, + {0xB0000000, 0x00000000}, + {0x10033, 0x000000D0}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000000D}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000000D}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000000D}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000000D}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000000D}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000000D}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000000D}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000000D}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000000D}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000000D}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000000D}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x0000000D}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x00000013}, + {0xB0000000, 0x00000000}, + {0x10033, 0x000000D1}, + {0x80010000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000007}, + {0x90020000, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000007}, + {0x90010001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000007}, + {0x90020001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000007}, + {0x90030001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000007}, + {0x90040001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000007}, + {0x90050001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000007}, + {0x90060001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000007}, + {0x90070001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000007}, + {0x90080001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000007}, + {0x90290001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000007}, + {0x902b0001, 0x00000000}, {0x40000000, 0x00000000}, + {0x1003F, 0x00000007}, + {0xA0000000, 0x00000000}, + {0x1003F, 0x0000000D}, + {0xB0000000, 0x00000000}, + {0x100EE, 0x00000000}, + {0x100EE, 0x00002000}, + {0x10033, 0x00000000}, + {0x1003F, 0x000000F6}, + {0x10033, 0x00000001}, + {0x1003F, 0x000000F3}, + {0x10033, 0x00000002}, + {0x1003F, 0x000000F0}, + {0x10033, 0x00000003}, + {0x1003F, 0x000000ED}, + {0x10033, 0x00000004}, + {0x1003F, 0x000000EA}, + {0x10033, 0x00000005}, + {0x1003F, 0x000000E7}, + {0x10033, 0x00000006}, + {0x1003F, 0x000000A6}, + {0x10033, 0x00000007}, + {0x1003F, 0x000000A3}, + {0x10033, 0x00000008}, + {0x1003F, 0x00000063}, + {0x10033, 0x00000009}, + {0x1003F, 0x00000060}, + {0x10033, 0x0000000A}, + {0x1003F, 0x00000023}, + {0x10033, 0x0000000B}, + {0x1003F, 0x00000020}, + {0x10033, 0x0000000C}, + {0x1003F, 0x0000001D}, + {0x10033, 0x0000000D}, + {0x1003F, 0x0000001A}, + {0x10033, 0x0000000E}, + {0x1003F, 0x00000017}, + {0x10033, 0x0000000F}, + {0x1003F, 0x00000014}, + {0x10033, 0x00000010}, + {0x1003F, 0x00000011}, + {0x100EE, 0x00000000}, + {0x100EE, 0x00004000}, + {0x10033, 0x00000000}, + {0x1003F, 0x000001AF}, + {0x10033, 0x00000001}, + {0x1003F, 0x000001A9}, + {0x10033, 0x00000002}, + {0x1003F, 0x000001A3}, + {0x10033, 0x00000003}, + {0x1003F, 0x0000019D}, + {0x10033, 0x00000004}, + {0x1003F, 0x00000197}, + {0x10033, 0x00000005}, + {0x1003F, 0x0000015F}, + {0x10033, 0x00000006}, + {0x1003F, 0x00000159}, + {0x10033, 0x00000007}, + {0x1003F, 0x0000011F}, + {0x10033, 0x00000008}, + {0x1003F, 0x00000119}, + {0x10033, 0x00000009}, + {0x1003F, 0x000000DF}, + {0x10033, 0x0000000A}, + {0x1003F, 0x000000D9}, + {0x10033, 0x0000000B}, + {0x1003F, 0x0000009F}, + {0x10033, 0x0000000C}, + {0x1003F, 0x00000099}, + {0x10033, 0x0000000D}, + {0x1003F, 0x0000005F}, + {0x10033, 0x0000000E}, + {0x1003F, 0x00000059}, + {0x10033, 0x0000000F}, + {0x1003F, 0x0000001F}, + {0x10033, 0x00000010}, + {0x1003F, 0x00000019}, + {0x10033, 0x00000011}, + {0x1003F, 0x00000013}, + {0x100EE, 0x00000000}, + {0x10005, 0x00000001}, + {0x09F, 0x00000032}, +}; diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852b_table.h b/drivers/net/wireless/realtek/rtw89/rtw8852b_table.h new file mode 100644 index 000000000000..114337ac9fb0 --- /dev/null +++ b/drivers/net/wireless/realtek/rtw89/rtw8852b_table.h @@ -0,0 +1,30 @@ +/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ +/* Copyright(c) 2019-2020 Realtek Corporation + */ + +#ifndef __RTW89_8852B_TABLE_H__ +#define __RTW89_8852B_TABLE_H__ + +#include "core.h" + +extern const struct rtw89_phy_table rtw89_8852b_phy_bb_table; +extern const struct rtw89_phy_table rtw89_8852b_phy_bb_gain_table; +extern const struct rtw89_phy_table rtw89_8852b_phy_radioa_table; +extern const struct rtw89_phy_table rtw89_8852b_phy_radiob_table; +extern const struct rtw89_phy_table rtw89_8852b_phy_nctl_table; +extern const struct rtw89_txpwr_table rtw89_8852b_byr_table; +extern const struct rtw89_txpwr_track_cfg rtw89_8852b_trk_cfg; +extern const u8 rtw89_8852b_tx_shape[RTW89_BAND_MAX][RTW89_RS_TX_SHAPE_NUM] + [RTW89_REGD_NUM]; +extern const s8 rtw89_8852b_txpwr_lmt_2g[RTW89_2G_BW_NUM][RTW89_NTX_NUM] + [RTW89_RS_LMT_NUM][RTW89_BF_NUM] + [RTW89_REGD_NUM][RTW89_2G_CH_NUM]; +extern const s8 rtw89_8852b_txpwr_lmt_5g[RTW89_5G_BW_NUM][RTW89_NTX_NUM] + [RTW89_RS_LMT_NUM][RTW89_BF_NUM] + [RTW89_REGD_NUM][RTW89_5G_CH_NUM]; +extern const s8 rtw89_8852b_txpwr_lmt_ru_2g[RTW89_RU_NUM][RTW89_NTX_NUM] + [RTW89_REGD_NUM][RTW89_2G_CH_NUM]; +extern const s8 rtw89_8852b_txpwr_lmt_ru_5g[RTW89_RU_NUM][RTW89_NTX_NUM] + [RTW89_REGD_NUM][RTW89_5G_CH_NUM]; + +#endif From 3e65a0ae142a97bb9b2e0a988e1ddf55fe289cf0 Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Wed, 28 Sep 2022 16:43:29 +0800 Subject: [PATCH 0023/2002] wifi: rtw89: 8852b: add BB and RF tables (2 of 2) These tables contain BB and RF parameters that driver will load them into registers. It also contains TX power according to country, band, rate and so on. Increasing thermal can cause TX power degraded, so power tracking tables are defined to compensate TX power. Internal version of these tables: - HALRF_029_00_014 (R32) - HALBB_027_046_05 Signed-off-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20220928084336.34981-3-pkshih@realtek.com --- .../wireless/realtek/rtw89/rtw8852b_table.c | 9628 +++++++++++++++++ 1 file changed, 9628 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852b_table.c b/drivers/net/wireless/realtek/rtw89/rtw8852b_table.c index f29bc5d8d576..a6734965361f 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852b_table.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852b_table.c @@ -13247,3 +13247,9631 @@ static const struct rtw89_reg2_def rtw89_8852b_phy_radiob_regs[] = { {0x10005, 0x00000001}, {0x09F, 0x00000032}, }; + +static const struct rtw89_reg2_def rtw89_8852b_phy_nctl_regs[] = { + {0x8000, 0x00000008}, + {0x8008, 0x00000000}, + {0x8004, 0xf0862966}, + {0x800c, 0x78000000}, + {0x8010, 0x88015000}, + {0x8014, 0x80010100}, + {0x8018, 0x10010100}, + {0x801c, 0xa210bc00}, + {0x8020, 0x000403e0}, + {0x8024, 0x00072160}, + {0x8028, 0x00180e00}, + {0x8030, 0x400000c0}, + {0x8034, 0x11000830}, + {0x8038, 0x00000009}, + {0x803c, 0x00000008}, + {0x8040, 0x00000046}, + {0x8044, 0x0010001f}, + {0x8048, 0xf0000003}, + {0x804c, 0x62ac6162}, + {0x8050, 0xf2acf162}, + {0x8054, 0x62ac6162}, + {0x8058, 0xf2acf162}, + {0x805c, 0x150c0b02}, + {0x8060, 0x150c0b02}, + {0x8064, 0x2aa00047}, + {0x8074, 0x80000000}, + {0x807c, 0x000000ee}, + {0x8088, 0x80000000}, + {0x8098, 0x0000ff00}, + {0x809c, 0x0000001f}, + {0x80a0, 0x00010300}, + {0x80b8, 0x00001000}, + {0x80b0, 0x00000000}, + {0x80d0, 0x00000000}, + {0x80ec, 0x00000002}, + {0x810c, 0x33112200}, + {0x8110, 0x33112200}, + {0x8114, 0x00000000}, + {0x8120, 0x10010000}, + {0x8124, 0x00000000}, + {0x812c, 0x0000c000}, + {0x8138, 0x40000000}, + {0x813c, 0x40000000}, + {0x8140, 0x00000000}, + {0x8144, 0x0b040b03}, + {0x8148, 0x0a050b04}, + {0x814c, 0x0a050b04}, + {0x8150, 0xe4e40000}, + {0x8158, 0xffffffff}, + {0x815c, 0xffffffff}, + {0x8160, 0xffffffff}, + {0x8164, 0xffffffff}, + {0x8168, 0xffffffff}, + {0x816c, 0x1fffffff}, + {0x81a0, 0x00000000}, + {0x81ac, 0x003f2e2e}, + {0x81b0, 0x003f2e2e}, + {0x81bc, 0x005b5b5b}, + {0x81c0, 0x005b5b5b}, + {0x81b4, 0x00600060}, + {0x81b8, 0x00600060}, + {0x81cc, 0x00000000}, + {0x81dc, 0x00000002}, + {0x81e0, 0x00000000}, + {0x81e4, 0x00000001}, + {0x820c, 0x33112200}, + {0x8210, 0x33112200}, + {0x8214, 0x00000000}, + {0x8220, 0x10010000}, + {0x8224, 0x00000000}, + {0x822c, 0x0000d000}, + {0x8238, 0x40000000}, + {0x823c, 0x40000000}, + {0x8240, 0x00000000}, + {0x8244, 0x0b040b03}, + {0x8248, 0x0a050b04}, + {0x824c, 0x0a050b04}, + {0x8250, 0xe4e40000}, + {0x8258, 0xffffffff}, + {0x825c, 0xffffffff}, + {0x8260, 0xffffffff}, + {0x8264, 0xffffffff}, + {0x8268, 0xffffffff}, + {0x826c, 0x1fffffff}, + {0x82a0, 0x00000000}, + {0x82ac, 0x003f2e2e}, + {0x82b0, 0x003f2e2e}, + {0x82bc, 0x005b5b5b}, + {0x82c0, 0x005b5b5b}, + {0x82b4, 0x00600060}, + {0x82b8, 0x00600060}, + {0x82cc, 0x00000000}, + {0x82dc, 0x00000002}, + {0x82e0, 0x00100000}, + {0x82e4, 0x00000001}, + {0x81d8, 0x00000001}, + {0x82d8, 0x00000001}, + {0x8d00, 0x00000000}, + {0x8d04, 0x00000000}, + {0x8d08, 0x00000000}, + {0x8d0c, 0x00000000}, + {0x8d10, 0x00000000}, + {0x8d14, 0x00000000}, + {0x8d18, 0x00000000}, + {0x8d1c, 0x00000000}, + {0x8d20, 0x00000000}, + {0x8d24, 0x00000000}, + {0x8d28, 0x00000000}, + {0x8d2c, 0x00000000}, + {0x8d30, 0x00000000}, + {0x8d34, 0x00000000}, + {0x8d38, 0x00000000}, + {0x8d3c, 0x00000000}, + {0x8d40, 0x00000000}, + {0x8d44, 0x00000000}, + {0x8d48, 0x00000000}, + {0x8d4c, 0x00000000}, + {0x8d50, 0x00000000}, + {0x8d54, 0x00000000}, + {0x8d58, 0x00000000}, + {0x8d5c, 0x00000000}, + {0x8d60, 0x00000000}, + {0x8d64, 0x00000000}, + {0x8d68, 0x00000000}, + {0x8d6c, 0x00000000}, + {0x8d70, 0x00000000}, + {0x8d74, 0x00000000}, + {0x8d78, 0x00000000}, + {0x8d7c, 0x00000000}, + {0x8d80, 0x00000000}, + {0x8d84, 0x00000000}, + {0x8d88, 0x00000000}, + {0x8d8c, 0x00000000}, + {0x8d90, 0x00000000}, + {0x8d94, 0x00000000}, + {0x8d98, 0x00000000}, + {0x8d9c, 0x00000000}, + {0x8da0, 0x00000000}, + {0x8da4, 0x00000000}, + {0x8da8, 0x00000000}, + {0x8dac, 0x00000000}, + {0x8db0, 0x00000000}, + {0x8db4, 0x00000000}, + {0x8db8, 0x00000000}, + {0x8dbc, 0x00000000}, + {0x8dc0, 0x00000000}, + {0x8dc4, 0x00000000}, + {0x8dc8, 0x00000000}, + {0x8dcc, 0x00000000}, + {0x8dd0, 0x00000000}, + {0x8dd4, 0x00000000}, + {0x8dd8, 0x00000000}, + {0x8ddc, 0x00000000}, + {0x8de0, 0x00000000}, + {0x8de4, 0x00000000}, + {0x8de8, 0x00000000}, + {0x8dec, 0x00000000}, + {0x8df0, 0x00000000}, + {0x8df4, 0x00000000}, + {0x8df8, 0x00000000}, + {0x8dfc, 0x00000000}, + {0x8e00, 0x00000000}, + {0x8e04, 0x00000000}, + {0x8e08, 0x00000000}, + {0x8e0c, 0x00000000}, + {0x8e10, 0x00000000}, + {0x8e14, 0x00000000}, + {0x8e18, 0x00000000}, + {0x8e1c, 0x00000000}, + {0x8e20, 0x00000000}, + {0x8e24, 0x00000000}, + {0x8e28, 0x00000000}, + {0x8e2c, 0x00000000}, + {0x8e30, 0x00000000}, + {0x8e34, 0x00000000}, + {0x8e38, 0x00000000}, + {0x8e3c, 0x00000000}, + {0x8e40, 0x00000000}, + {0x8e44, 0x00000000}, + {0x8e48, 0x00000000}, + {0x8e4c, 0x00000000}, + {0x8e50, 0x00000000}, + {0x8e54, 0x00000000}, + {0x8e58, 0x00000000}, + {0x8e5c, 0x00000000}, + {0x8e60, 0x00000000}, + {0x8e64, 0x00000000}, + {0x8e68, 0x00000000}, + {0x8e6c, 0x00000000}, + {0x8e70, 0x00000000}, + {0x8e74, 0x00000000}, + {0x8e78, 0x00000000}, + {0x8e7c, 0x00000000}, + {0x8e80, 0x00000000}, + {0x8e84, 0x00000000}, + {0x8e88, 0x00000000}, + {0x8e8c, 0x00000000}, + {0x8e90, 0x00000000}, + {0x8e94, 0x00000000}, + {0x8e98, 0x00000000}, + {0x8e9c, 0x00000000}, + {0x8ea0, 0x00000000}, + {0x8ea4, 0x00000000}, + {0x8ea8, 0x00000000}, + {0x8eac, 0x00000000}, + {0x8eb0, 0x00000000}, + {0x8eb4, 0x00000000}, + {0x8eb8, 0x00000000}, + {0x8ebc, 0x00000000}, + {0x8ec0, 0x00000000}, + {0x8ec4, 0x00000000}, + {0x8ec8, 0x00000000}, + {0x8ecc, 0x00000000}, + {0x8ed0, 0x00000000}, + {0x8ed4, 0x00000000}, + {0x8ed8, 0x00000000}, + {0x8edc, 0x00000000}, + {0x8ee0, 0x00000000}, + {0x8ee4, 0x00000000}, + {0x8ee8, 0x00000000}, + {0x8eec, 0x00000000}, + {0x8ef0, 0x00000000}, + {0x8ef4, 0x00000000}, + {0x8ef8, 0x00000000}, + {0x8efc, 0x00000000}, + {0x8f00, 0x00000000}, + {0x8f04, 0x00000000}, + {0x8f08, 0x00000000}, + {0x8f0c, 0x00000000}, + {0x8f10, 0x00000000}, + {0x8f14, 0x00000000}, + {0x8f18, 0x00000000}, + {0x8f1c, 0x00000000}, + {0x8f20, 0x00000000}, + {0x8f24, 0x00000000}, + {0x8f28, 0x00000000}, + {0x8f2c, 0x00000000}, + {0x8f30, 0x00000000}, + {0x8f34, 0x00000000}, + {0x8f38, 0x00000000}, + {0x8f3c, 0x00000000}, + {0x8f40, 0x00000000}, + {0x8f44, 0x00000000}, + {0x8f48, 0x00000000}, + {0x8f4c, 0x00000000}, + {0x8f50, 0x00000000}, + {0x8f54, 0x00000000}, + {0x8f58, 0x00000000}, + {0x8f5c, 0x00000000}, + {0x8f60, 0x00000000}, + {0x8f64, 0x00000000}, + {0x8f68, 0x00000000}, + {0x8f6c, 0x00000000}, + {0x8f70, 0x00000000}, + {0x8f74, 0x00000000}, + {0x8f78, 0x00000000}, + {0x8f7c, 0x00000000}, + {0x8f80, 0x00000000}, + {0x8f84, 0x00000000}, + {0x8f88, 0x00000000}, + {0x8f8c, 0x00000000}, + {0x8f90, 0x00000000}, + {0x8f94, 0x00000000}, + {0x8f98, 0x00000000}, + {0x8f9c, 0x00000000}, + {0x8fa0, 0x00000000}, + {0x8fa4, 0x00000000}, + {0x8fa8, 0x00000000}, + {0x8fac, 0x00000000}, + {0x8fb0, 0x00000000}, + {0x8fb4, 0x00000000}, + {0x8fb8, 0x00000000}, + {0x8fbc, 0x00000000}, + {0x8fc0, 0x00000000}, + {0x8fc4, 0x00000000}, + {0x8fc8, 0x00000000}, + {0x8fcc, 0x00000000}, + {0x8fd0, 0x00000000}, + {0x8fd4, 0x00000000}, + {0x8fd8, 0x00000000}, + {0x8fdc, 0x00000000}, + {0x8fe0, 0x00000000}, + {0x8fe4, 0x00000000}, + {0x8fe8, 0x00000000}, + {0x8fec, 0x00000000}, + {0x8ff0, 0x00000000}, + {0x8ff4, 0x00000000}, + {0x8ff8, 0x00000000}, + {0x8ffc, 0x00000000}, + {0x9000, 0x00000000}, + {0x9004, 0x00000000}, + {0x9008, 0x00000000}, + {0x900c, 0x00000000}, + {0x9010, 0x00000000}, + {0x9014, 0x00000000}, + {0x9018, 0x00000000}, + {0x901c, 0x00000000}, + {0x9020, 0x00000000}, + {0x9024, 0x00000000}, + {0x9028, 0x00000000}, + {0x902c, 0x00000000}, + {0x9030, 0x00000000}, + {0x9034, 0x00000000}, + {0x9038, 0x00000000}, + {0x903c, 0x00000000}, + {0x9040, 0x00000000}, + {0x9044, 0x00000000}, + {0x9048, 0x00000000}, + {0x904c, 0x00000000}, + {0x9050, 0x00000000}, + {0x9054, 0x00000000}, + {0x9058, 0x00000000}, + {0x905c, 0x00000000}, + {0x9060, 0x00000000}, + {0x9064, 0x00000000}, + {0x9068, 0x00000000}, + {0x906c, 0x00000000}, + {0x9070, 0x00000000}, + {0x9074, 0x00000000}, + {0x9078, 0x00000000}, + {0x907c, 0x00000000}, + {0x9080, 0x00000000}, + {0x9084, 0x00000000}, + {0x9088, 0x00000000}, + {0x908c, 0x00000000}, + {0x9090, 0x00000000}, + {0x9094, 0x00000000}, + {0x9098, 0x00000000}, + {0x909c, 0x00000000}, + {0x90a0, 0x00000000}, + {0x90a4, 0x00000000}, + {0x90a8, 0x00000000}, + {0x90ac, 0x00000000}, + {0x90b0, 0x00000000}, + {0x90b4, 0x00000000}, + {0x90b8, 0x00000000}, + {0x90bc, 0x00000000}, + {0x9100, 0x00000000}, + {0x9104, 0x00000000}, + {0x9108, 0x00000000}, + {0x910c, 0x00000000}, + {0x9110, 0x00000000}, + {0x9114, 0x00000000}, + {0x9118, 0x00000000}, + {0x911c, 0x00000000}, + {0x9120, 0x00000000}, + {0x9124, 0x00000000}, + {0x9128, 0x00000000}, + {0x912c, 0x00000000}, + {0x9130, 0x00000000}, + {0x9134, 0x00000000}, + {0x9138, 0x00000000}, + {0x913c, 0x00000000}, + {0x9140, 0x00000000}, + {0x9144, 0x00000000}, + {0x9148, 0x00000000}, + {0x914c, 0x00000000}, + {0x9150, 0x00000000}, + {0x9154, 0x00000000}, + {0x9158, 0x00000000}, + {0x915c, 0x00000000}, + {0x9160, 0x00000000}, + {0x9164, 0x00000000}, + {0x9168, 0x00000000}, + {0x916c, 0x00000000}, + {0x9170, 0x00000000}, + {0x9174, 0x00000000}, + {0x9178, 0x00000000}, + {0x917c, 0x00000000}, + {0x9180, 0x00000000}, + {0x9184, 0x00000000}, + {0x9188, 0x00000000}, + {0x918c, 0x00000000}, + {0x9190, 0x00000000}, + {0x9194, 0x00000000}, + {0x9198, 0x00000000}, + {0x919c, 0x00000000}, + {0x91a0, 0x00000000}, + {0x91a4, 0x00000000}, + {0x91a8, 0x00000000}, + {0x91ac, 0x00000000}, + {0x91b0, 0x00000000}, + {0x91b4, 0x00000000}, + {0x91b8, 0x00000000}, + {0x91bc, 0x00000000}, + {0x91c0, 0x00000000}, + {0x91c4, 0x00000000}, + {0x91c8, 0x00000000}, + {0x91cc, 0x00000000}, + {0x91d0, 0x00000000}, + {0x91d4, 0x00000000}, + {0x91d8, 0x00000000}, + {0x91dc, 0x00000000}, + {0x91e0, 0x00000000}, + {0x91e4, 0x00000000}, + {0x91e8, 0x00000000}, + {0x91ec, 0x00000000}, + {0x91f0, 0x00000000}, + {0x91f4, 0x00000000}, + {0x91f8, 0x00000000}, + {0x91fc, 0x00000000}, + {0x9200, 0x00000000}, + {0x9204, 0x00000000}, + {0x9208, 0x00000000}, + {0x920c, 0x00000000}, + {0x9210, 0x00000000}, + {0x9214, 0x00000000}, + {0x9218, 0x00000000}, + {0x921c, 0x00000000}, + {0x9220, 0x00000000}, + {0x9224, 0x00000000}, + {0x9228, 0x00000000}, + {0x922c, 0x00000000}, + {0x9230, 0x00000000}, + {0x9234, 0x00000000}, + {0x9238, 0x00000000}, + {0x923c, 0x00000000}, + {0x9240, 0x00000000}, + {0x9244, 0x00000000}, + {0x9248, 0x00000000}, + {0x924c, 0x00000000}, + {0x9250, 0x00000000}, + {0x9254, 0x00000000}, + {0x9258, 0x00000000}, + {0x925c, 0x00000000}, + {0x9260, 0x00000000}, + {0x9264, 0x00000000}, + {0x9268, 0x00000000}, + {0x926c, 0x00000000}, + {0x9270, 0x00000000}, + {0x9274, 0x00000000}, + {0x9278, 0x00000000}, + {0x927c, 0x00000000}, + {0x9280, 0x00000000}, + {0x9284, 0x00000000}, + {0x9288, 0x00000000}, + {0x928c, 0x00000000}, + {0x9290, 0x00000000}, + {0x9294, 0x00000000}, + {0x9298, 0x00000000}, + {0x929c, 0x00000000}, + {0x92a0, 0x00000000}, + {0x92a4, 0x00000000}, + {0x92a8, 0x00000000}, + {0x92ac, 0x00000000}, + {0x92b0, 0x00000000}, + {0x92b4, 0x00000000}, + {0x92b8, 0x00000000}, + {0x92bc, 0x00000000}, + {0x92c0, 0x00000000}, + {0x92c4, 0x00000000}, + {0x92c8, 0x00000000}, + {0x92cc, 0x00000000}, + {0x92d0, 0x00000000}, + {0x92d4, 0x00000000}, + {0x92d8, 0x00000000}, + {0x92dc, 0x00000000}, + {0x92e0, 0x00000000}, + {0x92e4, 0x00000000}, + {0x92e8, 0x00000000}, + {0x92ec, 0x00000000}, + {0x92f0, 0x00000000}, + {0x92f4, 0x00000000}, + {0x92f8, 0x00000000}, + {0x92fc, 0x00000000}, + {0x9300, 0x00000000}, + {0x9304, 0x00000000}, + {0x9308, 0x00000000}, + {0x930c, 0x00000000}, + {0x9310, 0x00000000}, + {0x9314, 0x00000000}, + {0x9318, 0x00000000}, + {0x931c, 0x00000000}, + {0x9320, 0x00000000}, + {0x9324, 0x00000000}, + {0x9328, 0x00000000}, + {0x932c, 0x00000000}, + {0x9330, 0x00000000}, + {0x9334, 0x00000000}, + {0x9338, 0x00000000}, + {0x933c, 0x00000000}, + {0x9340, 0x00000000}, + {0x9344, 0x00000000}, + {0x9348, 0x00000000}, + {0x934c, 0x00000000}, + {0x9350, 0x00000000}, + {0x9354, 0x00000000}, + {0x9358, 0x00000000}, + {0x935c, 0x00000000}, + {0x9360, 0x00000000}, + {0x9364, 0x00000000}, + {0x9368, 0x00000000}, + {0x936c, 0x00000000}, + {0x9370, 0x00000000}, + {0x9374, 0x00000000}, + {0x9378, 0x00000000}, + {0x937c, 0x00000000}, + {0x9380, 0x00000000}, + {0x9384, 0x00000000}, + {0x9388, 0x00000000}, + {0x938c, 0x00000000}, + {0x9390, 0x00000000}, + {0x9394, 0x00000000}, + {0x9398, 0x00000000}, + {0x939c, 0x00000000}, + {0x93a0, 0x00000000}, + {0x93a4, 0x00000000}, + {0x93a8, 0x00000000}, + {0x93ac, 0x00000000}, + {0x93b0, 0x00000000}, + {0x93b4, 0x00000000}, + {0x93b8, 0x00000000}, + {0x93bc, 0x00000000}, + {0x93c0, 0x00000000}, + {0x93c4, 0x00000000}, + {0x93c8, 0x00000000}, + {0x93cc, 0x00000000}, + {0x93d0, 0x00000000}, + {0x93d4, 0x00000000}, + {0x93d8, 0x00000000}, + {0x93dc, 0x00000000}, + {0x93e0, 0x00000000}, + {0x93e4, 0x00000000}, + {0x93e8, 0x00000000}, + {0x93ec, 0x00000000}, + {0x93f0, 0x00000000}, + {0x93f4, 0x00000000}, + {0x93f8, 0x00000000}, + {0x93fc, 0x00000000}, + {0x9400, 0x00000000}, + {0x9404, 0x00000000}, + {0x9408, 0x00000000}, + {0x940c, 0x00000000}, + {0x9410, 0x00000000}, + {0x9414, 0x00000000}, + {0x9418, 0x00000000}, + {0x941c, 0x00000000}, + {0x9420, 0x00000000}, + {0x9424, 0x00000000}, + {0x9428, 0x00000000}, + {0x942c, 0x00000000}, + {0x9430, 0x00000000}, + {0x9434, 0x00000000}, + {0x9438, 0x00000000}, + {0x943c, 0x00000000}, + {0x9440, 0x00000000}, + {0x9444, 0x00000000}, + {0x9448, 0x00000000}, + {0x944c, 0x00000000}, + {0x9450, 0x00000000}, + {0x9454, 0x00000000}, + {0x9458, 0x00000000}, + {0x945c, 0x00000000}, + {0x9460, 0x00000000}, + {0x9464, 0x00000000}, + {0x9468, 0x00000000}, + {0x946c, 0x00000000}, + {0x9470, 0x00000000}, + {0x9474, 0x00000000}, + {0x9478, 0x00000000}, + {0x947c, 0x00000000}, + {0x9480, 0x00000000}, + {0x9484, 0x00000000}, + {0x9488, 0x00000000}, + {0x948c, 0x00000000}, + {0x9490, 0x00000000}, + {0x9494, 0x00000000}, + {0x9498, 0x00000000}, + {0x949c, 0x00000000}, + {0x94a0, 0x00000000}, + {0x94a4, 0x00000000}, + {0x94a8, 0x00000000}, + {0x94ac, 0x00000000}, + {0x94b0, 0x00000000}, + {0x94b4, 0x00000000}, + {0x94b8, 0x00000000}, + {0x94bc, 0x00000000}, + {0xa220, 0x00000000}, + {0xa224, 0x00000000}, + {0xa228, 0x00000000}, + {0xa22c, 0x00000000}, + {0xa230, 0x00000000}, + {0xa234, 0x00000000}, + {0xa238, 0x00000000}, + {0xa23c, 0x00000000}, + {0xa240, 0x00000000}, + {0xa244, 0x00000000}, + {0xa248, 0x00000000}, + {0xa24c, 0x00000000}, + {0xa250, 0x00000000}, + {0xa254, 0x00000000}, + {0xa258, 0x00000000}, + {0xa25c, 0x00000000}, + {0xa260, 0x00000000}, + {0xa264, 0x00000000}, + {0xa268, 0x00000000}, + {0xa26c, 0x00000000}, + {0xa270, 0x00000000}, + {0xa274, 0x00000000}, + {0xa278, 0x00000000}, + {0xa27c, 0x00000000}, + {0xa280, 0x00000000}, + {0xa284, 0x00000000}, + {0xa288, 0x00000000}, + {0xa28c, 0x00000000}, + {0xa290, 0x00000000}, + {0xa294, 0x00000000}, + {0xa298, 0x00000000}, + {0xa29c, 0x00000000}, + {0xa2a0, 0x00000000}, + {0xa2a4, 0x00000000}, + {0xa2a8, 0x00000000}, + {0xa2ac, 0x00000000}, + {0xa2b0, 0x00000000}, + {0xa2b4, 0x00000000}, + {0xa2b8, 0x00000000}, + {0xa2bc, 0x00000000}, + {0xa2c0, 0x00000000}, + {0xa2c4, 0x00000000}, + {0xa2c8, 0x00000000}, + {0xa2cc, 0x00000000}, + {0xa2d0, 0x00000000}, + {0xa2d4, 0x00000000}, + {0xa2d8, 0x00000000}, + {0xa2dc, 0x00000000}, + {0xa2e0, 0x00000000}, + {0xa2e4, 0x00000000}, + {0xa2e8, 0x00000000}, + {0xa2ec, 0x00000000}, + {0xa2f0, 0x00000000}, + {0xa2f4, 0x00000000}, + {0xa2f8, 0x00000000}, + {0xa2fc, 0x00000000}, + {0xa300, 0x00000000}, + {0xa304, 0x00000000}, + {0xa308, 0x00000000}, + {0xa30c, 0x00000000}, + {0xa310, 0x00000000}, + {0xa314, 0x00000000}, + {0xa318, 0x00000000}, + {0xa31c, 0x00000000}, + {0xa320, 0x00000000}, + {0xa324, 0x00000000}, + {0xa328, 0x00000000}, + {0xa32c, 0x00000000}, + {0xa330, 0x00000000}, + {0xa334, 0x00000000}, + {0xa338, 0x00000000}, + {0xa33c, 0x00000000}, + {0xa340, 0x00000000}, + {0xa344, 0x00000000}, + {0xa348, 0x00000000}, + {0xa34c, 0x00000000}, + {0xa350, 0x00000000}, + {0xa354, 0x00000000}, + {0xa358, 0x00000000}, + {0xa35c, 0x00000000}, + {0xa360, 0x00000000}, + {0xa364, 0x00000000}, + {0xa368, 0x00000000}, + {0xa36c, 0x00000000}, + {0xa370, 0x00000000}, + {0xa374, 0x00000000}, + {0xa378, 0x00000000}, + {0xa37c, 0x00000000}, + {0xa380, 0x00000000}, + {0xa384, 0x00000000}, + {0xa388, 0x00000000}, + {0xa38c, 0x00000000}, + {0xa390, 0x00000000}, + {0xa394, 0x00000000}, + {0xa398, 0x00000000}, + {0xa39c, 0x00000000}, + {0xa3a0, 0x00000000}, + {0xa3a4, 0x00000000}, + {0xa3a8, 0x00000000}, + {0xa3ac, 0x00000000}, + {0xa3b0, 0x00000000}, + {0xa3b4, 0x00000000}, + {0xa3b8, 0x00000000}, + {0xa3bc, 0x00000000}, + {0xa620, 0x00000000}, + {0xa624, 0x00000000}, + {0xa628, 0x00000000}, + {0xa62c, 0x00000000}, + {0xa630, 0x00000000}, + {0xa634, 0x00000000}, + {0xa638, 0x00000000}, + {0xa63c, 0x00000000}, + {0xa640, 0x00000000}, + {0xa644, 0x00000000}, + {0xa648, 0x00000000}, + {0xa64c, 0x00000000}, + {0xa650, 0x00000000}, + {0xa654, 0x00000000}, + {0xa658, 0x00000000}, + {0xa65c, 0x00000000}, + {0xa660, 0x00000000}, + {0xa664, 0x00000000}, + {0xa668, 0x00000000}, + {0xa66c, 0x00000000}, + {0xa670, 0x00000000}, + {0xa674, 0x00000000}, + {0xa678, 0x00000000}, + {0xa67c, 0x00000000}, + {0xa680, 0x00000000}, + {0xa684, 0x00000000}, + {0xa688, 0x00000000}, + {0xa68c, 0x00000000}, + {0xa690, 0x00000000}, + {0xa694, 0x00000000}, + {0xa698, 0x00000000}, + {0xa69c, 0x00000000}, + {0xa6a0, 0x00000000}, + {0xa6a4, 0x00000000}, + {0xa6a8, 0x00000000}, + {0xa6ac, 0x00000000}, + {0xa6b0, 0x00000000}, + {0xa6b4, 0x00000000}, + {0xa6b8, 0x00000000}, + {0xa6bc, 0x00000000}, + {0xa6c0, 0x00000000}, + {0xa6c4, 0x00000000}, + {0xa6c8, 0x00000000}, + {0xa6cc, 0x00000000}, + {0xa6d0, 0x00000000}, + {0xa6d4, 0x00000000}, + {0xa6d8, 0x00000000}, + {0xa6dc, 0x00000000}, + {0xa6e0, 0x00000000}, + {0xa6e4, 0x00000000}, + {0xa6e8, 0x00000000}, + {0xa6ec, 0x00000000}, + {0xa6f0, 0x00000000}, + {0xa6f4, 0x00000000}, + {0xa6f8, 0x00000000}, + {0xa6fc, 0x00000000}, + {0xa700, 0x00000000}, + {0xa704, 0x00000000}, + {0xa708, 0x00000000}, + {0xa70c, 0x00000000}, + {0xa710, 0x00000000}, + {0xa714, 0x00000000}, + {0xa718, 0x00000000}, + {0xa71c, 0x00000000}, + {0xa720, 0x00000000}, + {0xa724, 0x00000000}, + {0xa728, 0x00000000}, + {0xa72c, 0x00000000}, + {0xa730, 0x00000000}, + {0xa734, 0x00000000}, + {0xa738, 0x00000000}, + {0xa73c, 0x00000000}, + {0xa740, 0x00000000}, + {0xa744, 0x00000000}, + {0xa748, 0x00000000}, + {0xa74c, 0x00000000}, + {0xa750, 0x00000000}, + {0xa754, 0x00000000}, + {0xa758, 0x00000000}, + {0xa75c, 0x00000000}, + {0xa760, 0x00000000}, + {0xa764, 0x00000000}, + {0xa768, 0x00000000}, + {0xa76c, 0x00000000}, + {0xa770, 0x00000000}, + {0xa774, 0x00000000}, + {0xa778, 0x00000000}, + {0xa77c, 0x00000000}, + {0xa780, 0x00000000}, + {0xa784, 0x00000000}, + {0xa788, 0x00000000}, + {0xa78c, 0x00000000}, + {0xa790, 0x00000000}, + {0xa794, 0x00000000}, + {0xa798, 0x00000000}, + {0xa79c, 0x00000000}, + {0xa7a0, 0x00000000}, + {0xa7a4, 0x00000000}, + {0xa7a8, 0x00000000}, + {0xa7ac, 0x00000000}, + {0xa7b0, 0x00000000}, + {0xa7b4, 0x00000000}, + {0xa7b8, 0x00000000}, + {0xa7bc, 0x00000000}, + {0x81d8, 0x00000000}, + {0x82d8, 0x00000000}, + {0x9f04, 0x2b251f19}, + {0x9f08, 0x433d3731}, + {0x9f0c, 0x5b554f49}, + {0x9f10, 0x736d6761}, + {0x9f14, 0x7f7f7f79}, + {0x9f18, 0x120f7f7f}, + {0x9f1c, 0x1e1b1815}, + {0x9f20, 0x2a272421}, + {0x9f24, 0x3633302d}, + {0x9f28, 0x3f3f3c39}, + {0x9f2c, 0x3f3f3f3f}, + {0x8008, 0x00000080}, + {0x8088, 0x807f030a}, + {0x80c8, 0x708f0bf1}, + {0x80c8, 0x708e0aa5}, + {0x80c8, 0x708d097d}, + {0x80c8, 0x708c0875}, + {0x80c8, 0x708b0789}, + {0x80c8, 0x708a06b7}, + {0x80c8, 0x708905fc}, + {0x80c8, 0x70880556}, + {0x80c8, 0x708704c1}, + {0x80c8, 0x7086043d}, + {0x80c8, 0x708503c7}, + {0x80c8, 0x7084035e}, + {0x80c8, 0x708302ac}, + {0x80c8, 0x70820262}, + {0x80c8, 0x70810220}, + {0x80c8, 0x70800000}, + {0x80c8, 0x7090011f}, + {0x80c8, 0x7010011f}, + {0x8088, 0x80000000}, + {0x8008, 0x00000000}, + {0x8088, 0x00000110}, + {0x8000, 0x00000008}, + {0x8080, 0x00000005}, + {0x8500, 0x80000008}, + {0x8504, 0x43000004}, + {0x8508, 0x4b044a00}, + {0x850c, 0x40098604}, + {0x8510, 0x0004e020}, + {0x8514, 0x87044b05}, + {0x8518, 0xe020400b}, + {0x851c, 0x4b000004}, + {0x8520, 0x21e07410}, + {0x8524, 0x74300000}, + {0x8528, 0x43800004}, + {0x852c, 0x4c000007}, + {0x8530, 0x43000004}, + {0x8534, 0x42fe5700}, + {0x8538, 0x42004000}, + {0x853c, 0x30005055}, + {0x8540, 0xa50fb41a}, + {0x8544, 0xf11ce3c7}, + {0x8548, 0xf31cf21c}, + {0x854c, 0xf61cf41c}, + {0x8550, 0xf91cf81c}, + {0x8554, 0xfb1cfa1c}, + {0x8558, 0xfd1cfc1c}, + {0x855c, 0xff1cfe1c}, + {0x8560, 0xf11cf01c}, + {0x8564, 0xf31cf21c}, + {0x8568, 0xf51cf41c}, + {0x856c, 0xf71cf61c}, + {0x8570, 0xf91cf81c}, + {0x8574, 0xe3c7a504}, + {0x8578, 0xf11af01a}, + {0x857c, 0x30580001}, + {0x8580, 0x30b030c9}, + {0x8584, 0x30ff30fc}, + {0x8588, 0x310f3102}, + {0x858c, 0x3148311c}, + {0x8590, 0x31603158}, + {0x8594, 0x30c7320e}, + {0x8598, 0x32293225}, + {0x859c, 0x32433242}, + {0x85a0, 0x3286327a}, + {0x85a4, 0x329d328a}, + {0x85a8, 0x32aa32a8}, + {0x85ac, 0x320331c5}, + {0x85b0, 0x7410e2c1}, + {0x85b4, 0x020020a8}, + {0x85b8, 0x2098140f}, + {0x85bc, 0x140f0200}, + {0x85c0, 0x02002088}, + {0x85c4, 0x7430140f}, + {0x85c8, 0x5b10e31c}, + {0x85cc, 0x20a87410}, + {0x85d0, 0x140f0201}, + {0x85d4, 0x00002080}, + {0x85d8, 0x5507140f}, + {0x85dc, 0x5c065661}, + {0x85e0, 0x7410e308}, + {0x85e4, 0x02002088}, + {0x85e8, 0x5517140f}, + {0x85ec, 0x7410e308}, + {0x85f0, 0x020020a8}, + {0x85f4, 0x5517140f}, + {0x85f8, 0x5c025641}, + {0x85fc, 0x7410e308}, + {0x8600, 0x00002080}, + {0x8604, 0x1407140f}, + {0x8608, 0xe3085507}, + {0x860c, 0x7508e2b4}, + {0x8610, 0xe312468e}, + {0x8614, 0x5b10e0f4}, + {0x8618, 0x20a87410}, + {0x861c, 0x140f0201}, + {0x8620, 0x00002090}, + {0x8624, 0x5507140f}, + {0x8628, 0x5c065661}, + {0x862c, 0x7410e308}, + {0x8630, 0x02002098}, + {0x8634, 0x5517140f}, + {0x8638, 0x7410e308}, + {0x863c, 0x020020a8}, + {0x8640, 0x5517140f}, + {0x8644, 0x5c025641}, + {0x8648, 0x7410e308}, + {0x864c, 0x00002090}, + {0x8650, 0x5507140f}, + {0x8654, 0x7509e308}, + {0x8658, 0xe3124696}, + {0x865c, 0x0001e0f4}, + {0x8660, 0x74105b10}, + {0x8664, 0x000020a0}, + {0x8668, 0x5507140f}, + {0x866c, 0xe3085601}, + {0x8670, 0x20a87410}, + {0x8674, 0x140f0200}, + {0x8678, 0xe3085517}, + {0x867c, 0x750ae2b4}, + {0x8680, 0xe3124686}, + {0x8684, 0x5500e0f4}, + {0x8688, 0x5501e304}, + {0x868c, 0xe2c10001}, + {0x8690, 0x5b10e31c}, + {0x8694, 0x20807410}, + {0x8698, 0x140f0000}, + {0x869c, 0x02002098}, + {0x86a0, 0xf204140f}, + {0x86a4, 0x020020a8}, + {0x86a8, 0x5507140f}, + {0x86ac, 0xe3085601}, + {0x86b0, 0x20887410}, + {0x86b4, 0x140f0200}, + {0x86b8, 0xe3085517}, + {0x86bc, 0x7508e2b4}, + {0x86c0, 0xe312468e}, + {0x86c4, 0x7410e0f4}, + {0x86c8, 0x00002090}, + {0x86cc, 0x5507140f}, + {0x86d0, 0x7410e308}, + {0x86d4, 0x02002098}, + {0x86d8, 0x5517140f}, + {0x86dc, 0x7509e308}, + {0x86e0, 0xe3124696}, + {0x86e4, 0x0001e0f4}, + {0x86e8, 0x74207900}, + {0x86ec, 0x57005710}, + {0x86f0, 0x9700140f}, + {0x86f4, 0x00017430}, + {0x86f8, 0xe31ce2c1}, + {0x86fc, 0xe2ca0001}, + {0x8700, 0x0001e34b}, + {0x8704, 0x312ae2c1}, + {0x8708, 0xe3ba0023}, + {0x870c, 0x54ed0002}, + {0x8710, 0x00230baa}, + {0x8714, 0x0002e3ba}, + {0x8718, 0xe2b9e367}, + {0x871c, 0xe2c10001}, + {0x8720, 0x00223125}, + {0x8724, 0x0002e3ba}, + {0x8728, 0x0baa54ec}, + {0x872c, 0xe3ba0022}, + {0x8730, 0xe3670002}, + {0x8734, 0x0001e2b9}, + {0x8738, 0x0baae2c1}, + {0x873c, 0x6d0f6c67}, + {0x8740, 0xe3bae31c}, + {0x8744, 0xe31c6c8b}, + {0x8748, 0x0bace3ba}, + {0x874c, 0x6d0f6cb3}, + {0x8750, 0xe3bae31c}, + {0x8754, 0x6cdb0bad}, + {0x8758, 0xe31c6d0f}, + {0x875c, 0x6cf7e3ba}, + {0x8760, 0xe31c6d0f}, + {0x8764, 0x6c09e3ba}, + {0x8768, 0xe31c6d00}, + {0x876c, 0x6c25e3ba}, + {0x8770, 0xe3bae31c}, + {0x8774, 0x6c4df8ca}, + {0x8778, 0xe3bae31c}, + {0x877c, 0x6c75f9d3}, + {0x8780, 0xe3bae31c}, + {0x8784, 0xe31c6c99}, + {0x8788, 0xe367e3ba}, + {0x878c, 0x0001e2b9}, + {0x8790, 0x4380e2ca}, + {0x8794, 0x43006344}, + {0x8798, 0x00223188}, + {0x879c, 0x0002e3bf}, + {0x87a0, 0x0baa54ec}, + {0x87a4, 0xe3bf0022}, + {0x87a8, 0xe3670002}, + {0x87ac, 0x0001e2c5}, + {0x87b0, 0x4380e2ca}, + {0x87b4, 0x43006344}, + {0x87b8, 0xe367317b}, + {0x87bc, 0x0001e2c5}, + {0x87c0, 0x4380e2ca}, + {0x87c4, 0x4300634d}, + {0x87c8, 0x74100ba6}, + {0x87cc, 0x000921e8}, + {0x87d0, 0x6f0f6e67}, + {0x87d4, 0xe3bfe34b}, + {0x87d8, 0x000a21e8}, + {0x87dc, 0xe34b6e77}, + {0x87e0, 0x21e8e3bf}, + {0x87e4, 0x6e8b000b}, + {0x87e8, 0xe3bfe34b}, + {0x87ec, 0x000c21e8}, + {0x87f0, 0xe34b6e9f}, + {0x87f4, 0x0baae3bf}, + {0x87f8, 0x21e87410}, + {0x87fc, 0x6eb3000d}, + {0x8800, 0xe34b6f0f}, + {0x8804, 0x21e8e3bf}, + {0x8808, 0x6ec7000e}, + {0x880c, 0xe3bfe34b}, + {0x8810, 0x74100bac}, + {0x8814, 0x000f21e8}, + {0x8818, 0x6f0f6edb}, + {0x881c, 0xe3bfe34b}, + {0x8820, 0x001021e8}, + {0x8824, 0xe34b6eef}, + {0x8828, 0xe3bfe3bf}, + {0x882c, 0x001321e8}, + {0x8830, 0x6f006e11}, + {0x8834, 0xe3bfe34b}, + {0x8838, 0x21e8e3bf}, + {0x883c, 0x6e250014}, + {0x8840, 0xe3bfe34b}, + {0x8844, 0x21e8fbab}, + {0x8848, 0x6e390015}, + {0x884c, 0xe3bfe34b}, + {0x8850, 0x001621e8}, + {0x8854, 0xe34b6e4d}, + {0x8858, 0xfcb0e3bf}, + {0x885c, 0x001721e8}, + {0x8860, 0xe34b6e61}, + {0x8864, 0x21e8e3bf}, + {0x8868, 0x6e750018}, + {0x886c, 0xe3bfe34b}, + {0x8870, 0x001921e8}, + {0x8874, 0xe34b6e89}, + {0x8878, 0x21e8e3bf}, + {0x887c, 0x6e99001a}, + {0x8880, 0xe3bfe34b}, + {0x8884, 0xe2c5e367}, + {0x8888, 0x00040001}, + {0x888c, 0x42fc0004}, + {0x8890, 0x60010007}, + {0x8894, 0x42000004}, + {0x8898, 0x62200007}, + {0x889c, 0x00046200}, + {0x88a0, 0x5b005501}, + {0x88a4, 0x5b40e304}, + {0x88a8, 0x00076605}, + {0x88ac, 0x63006200}, + {0x88b0, 0x0004e388}, + {0x88b4, 0x0a010900}, + {0x88b8, 0x0d000b40}, + {0x88bc, 0x00320e01}, + {0x88c0, 0x95090004}, + {0x88c4, 0x790442fb}, + {0x88c8, 0x43804200}, + {0x88cc, 0x4d010007}, + {0x88d0, 0x43000004}, + {0x88d4, 0x05620007}, + {0x88d8, 0x961d05a3}, + {0x88dc, 0x0004e388}, + {0x88e0, 0x0007e304}, + {0x88e4, 0x07a306a2}, + {0x88e8, 0x0004e388}, + {0x88ec, 0xe378e304}, + {0x88f0, 0xe3800002}, + {0x88f4, 0x00074380}, + {0x88f8, 0x00044d00}, + {0x88fc, 0x42fe4300}, + {0x8900, 0x42007900}, + {0x8904, 0x00040001}, + {0x8908, 0x000742fc}, + {0x890c, 0x00046003}, + {0x8910, 0x31cc4200}, + {0x8914, 0x06a20007}, + {0x8918, 0x31f807a3}, + {0x891c, 0x77000005}, + {0x8920, 0x52000007}, + {0x8924, 0x42fe0004}, + {0x8928, 0x60000007}, + {0x892c, 0x42000004}, + {0x8930, 0x60004380}, + {0x8934, 0x62016100}, + {0x8938, 0x00056310}, + {0x893c, 0x55004100}, + {0x8940, 0x5c020007}, + {0x8944, 0x43000004}, + {0x8948, 0xe2d70001}, + {0x894c, 0x73000005}, + {0x8950, 0xe2d70001}, + {0x8954, 0x5d000006}, + {0x8958, 0x42f70004}, + {0x895c, 0x6c000005}, + {0x8960, 0x42000004}, + {0x8964, 0x0004e2de}, + {0x8968, 0x00074380}, + {0x896c, 0x4a004e00}, + {0x8970, 0x00064c00}, + {0x8974, 0x60007f00}, + {0x8978, 0x00046f00}, + {0x897c, 0x00054300}, + {0x8980, 0x00017300}, + {0x8984, 0xe2d70001}, + {0x8988, 0x5d010006}, + {0x898c, 0x61006002}, + {0x8990, 0x00055601}, + {0x8994, 0xe2e27710}, + {0x8998, 0x73000005}, + {0x899c, 0x43800004}, + {0x89a0, 0x5e010007}, + {0x89a4, 0x4d205e00}, + {0x89a8, 0x4a084e20}, + {0x89ac, 0x4c3f4960}, + {0x89b0, 0x00064301}, + {0x89b4, 0x63807f01}, + {0x89b8, 0x00046010}, + {0x89bc, 0x00064300}, + {0x89c0, 0x00077402}, + {0x89c4, 0x40004001}, + {0x89c8, 0x0006ab00}, + {0x89cc, 0x00077404}, + {0x89d0, 0x40004001}, + {0x89d4, 0x0004ab00}, + {0x89d8, 0x00074380}, + {0x89dc, 0x4e004d00}, + {0x89e0, 0x4c004a00}, + {0x89e4, 0x00064300}, + {0x89e8, 0x63007f00}, + {0x89ec, 0x00046000}, + {0x89f0, 0x00014300}, + {0x89f4, 0x73800005}, + {0x89f8, 0x42fe0004}, + {0x89fc, 0x6c010005}, + {0x8a00, 0x000514c8}, + {0x8a04, 0x00046c00}, + {0x8a08, 0x00014200}, + {0x8a0c, 0x0005e2ce}, + {0x8a10, 0x00017300}, + {0x8a14, 0x00040006}, + {0x8a18, 0x42fa4380}, + {0x8a1c, 0x42007c05}, + {0x8a20, 0x7c5b0006}, + {0x8a24, 0x7e5b7d5b}, + {0x8a28, 0x00077f00}, + {0x8a2c, 0x415b405b}, + {0x8a30, 0x4300425b}, + {0x8a34, 0x43000004}, + {0x8a38, 0x00040001}, + {0x8a3c, 0x60004380}, + {0x8a40, 0x62016100}, + {0x8a44, 0x42fa6310}, + {0x8a48, 0x42007c00}, + {0x8a4c, 0x00014300}, + {0x8a50, 0x0001e2e5}, + {0x8a54, 0x55000007}, + {0x8a58, 0x74200004}, + {0x8a5c, 0x79017711}, + {0x8a60, 0x57005710}, + {0x8a64, 0x00019700}, + {0x8a68, 0x4e004f02}, + {0x8a6c, 0x52015302}, + {0x8a70, 0x43800001}, + {0x8a74, 0x78006505}, + {0x8a78, 0x7a007900}, + {0x8a7c, 0x43007b00}, + {0x8a80, 0x43800001}, + {0x8a84, 0x43006500}, + {0x8a88, 0x43800001}, + {0x8a8c, 0x7c006405}, + {0x8a90, 0x00014300}, + {0x8a94, 0x64004380}, + {0x8a98, 0x00014300}, + {0x8a9c, 0x74200004}, + {0x8aa0, 0x0005e392}, + {0x8aa4, 0x73807388}, + {0x8aa8, 0xe3a08f00}, + {0x8aac, 0xe3920001}, + {0x8ab0, 0x73810005}, + {0x8ab4, 0x93007380}, + {0x8ab8, 0x0001e3a0}, + {0x8abc, 0xe2e5e3a7}, + {0x8ac0, 0x0001e3ae}, + {0x8ac4, 0xe3aee3a7}, + {0x8ac8, 0x00040001}, + {0x8acc, 0x24207410}, + {0x8ad0, 0x14c80000}, + {0x8ad4, 0x00002428}, + {0x8ad8, 0x1a4215f4}, + {0x8adc, 0x74300008}, + {0x8ae0, 0x43800001}, + {0x8ae4, 0x7a907b48}, + {0x8ae8, 0x78027900}, + {0x8aec, 0x55034300}, + {0x8af0, 0x43803308}, + {0x8af4, 0x7a807b38}, + {0x8af8, 0x55134300}, + {0x8afc, 0x43803308}, + {0x8b00, 0x7a007b40}, + {0x8b04, 0x55234300}, + {0x8b08, 0x74007401}, + {0x8b0c, 0x00018e00}, + {0x8b10, 0x52300007}, + {0x8b14, 0x74310004}, + {0x8b18, 0x8e007430}, + {0x8b1c, 0x52200007}, + {0x8b20, 0x00010004}, + {0x8b24, 0x57005702}, + {0x8b28, 0x00018e00}, + {0x8b2c, 0x561042ef}, + {0x8b30, 0x42005600}, + {0x8b34, 0x00018c00}, + {0x8b38, 0x4e004f78}, + {0x8b3c, 0x52015388}, + {0x8b40, 0xe32b5b20}, + {0x8b44, 0x54005480}, + {0x8b48, 0x54005481}, + {0x8b4c, 0x54005482}, + {0x8b50, 0xbf1de336}, + {0x8b54, 0xe2f13010}, + {0x8b58, 0xe2ffe2f9}, + {0x8b5c, 0xe3b3e312}, + {0x8b60, 0xe3085523}, + {0x8b64, 0xe3125525}, + {0x8b68, 0x0001e3b3}, + {0x8b6c, 0x54c054bf}, + {0x8b70, 0x54c154a3}, + {0x8b74, 0x4c1854a4}, + {0x8b78, 0x54c2bf07}, + {0x8b7c, 0xbf0454a4}, + {0x8b80, 0x54a354c1}, + {0x8b84, 0xe3c4bf01}, + {0x8b88, 0x000154df}, + {0x8b8c, 0x54e554bf}, + {0x8b90, 0x54df050a}, + {0x8b94, 0x16570001}, + {0x8b98, 0x74307b80}, + {0x8b9c, 0x7f404380}, + {0x8ba0, 0x7d007e00}, + {0x8ba4, 0x43007c02}, + {0x8ba8, 0x55015b40}, + {0x8bac, 0xe3165c01}, + {0x8bb0, 0x54005480}, + {0x8bb4, 0x54005481}, + {0x8bb8, 0x54005482}, + {0x8bbc, 0x74107b00}, + {0x8bc0, 0xbfe5e336}, + {0x8bc4, 0x56103010}, + {0x8bc8, 0x8c005600}, + {0x8bcc, 0x57040001}, + {0x8bd0, 0x8e005700}, + {0x8bd4, 0x57005708}, + {0x8bd8, 0x57818e00}, + {0x8bdc, 0x8e005780}, + {0x8be0, 0x00074380}, + {0x8be4, 0x5c005c01}, + {0x8be8, 0x00041403}, + {0x8bec, 0x00014300}, + {0x8bf0, 0x0007427f}, + {0x8bf4, 0x62006280}, + {0x8bf8, 0x00049200}, + {0x8bfc, 0x00014200}, + {0x8c00, 0x0007427f}, + {0x8c04, 0x63146394}, + {0x8c08, 0x00049200}, + {0x8c0c, 0x00014200}, + {0x8c10, 0x42fe0004}, + {0x8c14, 0x42007901}, + {0x8c18, 0x14037420}, + {0x8c1c, 0x57005710}, + {0x8c20, 0x0001140f}, + {0x8c24, 0x56010006}, + {0x8c28, 0x54005502}, + {0x8c2c, 0x7f000005}, + {0x8c30, 0x77107e12}, + {0x8c34, 0x75007600}, + {0x8c38, 0x00047400}, + {0x8c3c, 0x00014270}, + {0x8c40, 0x42000004}, + {0x8c44, 0x77000005}, + {0x8c48, 0x56000006}, + {0x8c4c, 0x00060001}, + {0x8c50, 0x5f005f80}, + {0x8c54, 0x00059900}, + {0x8c58, 0x00017300}, + {0x8c5c, 0x63800006}, + {0x8c60, 0x98006300}, + {0x8c64, 0x549f0001}, + {0x8c68, 0x5c015400}, + {0x8c6c, 0x540054df}, + {0x8c70, 0x00015c02}, + {0x8c74, 0x07145c01}, + {0x8c78, 0x5c025400}, + {0x8c7c, 0x5c020001}, + {0x8c80, 0x54000714}, + {0x8c84, 0x00015c01}, + {0x8c88, 0x4c184c98}, + {0x8c8c, 0x00040001}, + {0x8c90, 0x74305c02}, + {0x8c94, 0x0c010901}, + {0x8c98, 0x00050ba6}, + {0x8c9c, 0x00077780}, + {0x8ca0, 0x00045220}, + {0x8ca4, 0x60084380}, + {0x8ca8, 0x6200610a}, + {0x8cac, 0x000763ce}, + {0x8cb0, 0x00045c00}, + {0x8cb4, 0x00014300}, + {0x8080, 0x00000004}, + {0x8080, 0x00000000}, + {0x8088, 0x00000000}, +}; + +static const struct rtw89_txpwr_byrate_cfg rtw89_8852b_txpwr_byrate[] = { + { 0, 0, 0, 0, 4, 0x50505050, }, + { 0, 0, 1, 0, 4, 0x50505050, }, + { 0, 0, 1, 4, 4, 0x484c5050, }, + { 0, 0, 2, 0, 4, 0x50505050, }, + { 0, 0, 2, 4, 4, 0x44484c50, }, + { 0, 0, 2, 8, 4, 0x34383c40, }, + { 0, 0, 3, 0, 4, 0x50505050, }, + { 0, 1, 2, 0, 4, 0x50505050, }, + { 0, 1, 2, 4, 4, 0x44484c50, }, + { 0, 1, 2, 8, 4, 0x34383c40, }, + { 0, 1, 3, 0, 4, 0x50505050, }, + { 0, 0, 4, 1, 4, 0x00000000, }, + { 0, 0, 4, 0, 1, 0x00000000, }, + { 1, 0, 1, 0, 4, 0x50505050, }, + { 1, 0, 1, 4, 4, 0x484c5050, }, + { 1, 0, 2, 0, 4, 0x50505050, }, + { 1, 0, 2, 4, 4, 0x44484c50, }, + { 1, 0, 2, 8, 4, 0x34383c40, }, + { 1, 0, 3, 0, 4, 0x50505050, }, + { 1, 1, 2, 0, 4, 0x50505050, }, + { 1, 1, 2, 4, 4, 0x44484c50, }, + { 1, 1, 2, 8, 4, 0x34383c40, }, + { 1, 1, 3, 0, 4, 0x50505050, }, + { 1, 0, 4, 0, 4, 0x00000000, }, +}; + +static const s8 _txpwr_track_delta_swingidx_5gb_n[][DELTA_SWINGIDX_SIZE] = { + {0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, + 4, 5, 5, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 8}, + {0, 1, 1, 1, 1, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 5, + 5, 5, 5, 6, 6, 6, 6, 7, 7, 7, 8, 8, 8, 8}, + {0, 1, 1, 2, 2, 2, 3, 3, 4, 4, 4, 5, 5, 6, 6, 6, 7, + 7, 8, 8, 8, 9, 9, 10, 10, 10, 11, 11, 12, 12}, +}; + +static const s8 _txpwr_track_delta_swingidx_5gb_p[][DELTA_SWINGIDX_SIZE] = { + {0, 1, 1, 1, 1, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 5, + 5, 5, 5, 6, 6, 6, 6, 7, 7, 7, 8, 8, 8, 8}, + {0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, + 4, 5, 5, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 8}, + {0, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 5, + 5, 5, 5, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 9}, +}; + +static const s8 _txpwr_track_delta_swingidx_5ga_n[][DELTA_SWINGIDX_SIZE] = { + {0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, + 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4}, + {0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3}, + {0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3}, +}; + +static const s8 _txpwr_track_delta_swingidx_5ga_p[][DELTA_SWINGIDX_SIZE] = { + {0, 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, + 4, 4, 5, 5, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7}, + {0, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 4, 4, 4, 4, 5, + 5, 5, 5, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 9}, + {0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 5, 5, + 5, 6, 6, 6, 6, 7, 7, 7, 8, 8, 8, 9, 9, 9}, +}; + +static const s8 _txpwr_track_delta_swingidx_2gb_n[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -2, -2}; + +static const s8 _txpwr_track_delta_swingidx_2gb_p[] = { + 0, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, + 3, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 6, 6}; + +static const s8 _txpwr_track_delta_swingidx_2ga_n[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + +static const s8 _txpwr_track_delta_swingidx_2ga_p[] = { + 0, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 3, 3, + 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 5, 5, 5}; + +static const s8 _txpwr_track_delta_swingidx_2g_cck_b_n[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}; + +static const s8 _txpwr_track_delta_swingidx_2g_cck_b_p[] = { + 0, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, + 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 6, 6, 6}; + +static const s8 _txpwr_track_delta_swingidx_2g_cck_a_n[] = { + 0, 0, 0, 0, 0, 0, 0, 0, -1, -1, -1, -1, -1, -1, -1, -2, -2, + -2, -2, -2, -2, -2, -2, -3, -3, -3, -3, -3, -3, -3}; + +static const s8 _txpwr_track_delta_swingidx_2g_cck_a_p[] = { + 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}; + +const u8 rtw89_8852b_tx_shape[RTW89_BAND_MAX][RTW89_RS_TX_SHAPE_NUM] + [RTW89_REGD_NUM] = { + [0][0][RTW89_ACMA] = 0, + [0][0][RTW89_CHILE] = 0, + [0][0][RTW89_CN] = 0, + [0][0][RTW89_ETSI] = 0, + [0][0][RTW89_FCC] = 1, + [0][0][RTW89_IC] = 1, + [0][0][RTW89_KCC] = 0, + [0][0][RTW89_MEXICO] = 1, + [0][0][RTW89_MKK] = 0, + [0][0][RTW89_QATAR] = 0, + [0][0][RTW89_UK] = 0, + [0][0][RTW89_UKRAINE] = 0, + [0][1][RTW89_ACMA] = 0, + [0][1][RTW89_CHILE] = 0, + [0][1][RTW89_CN] = 0, + [0][1][RTW89_ETSI] = 0, + [0][1][RTW89_FCC] = 3, + [0][1][RTW89_IC] = 3, + [0][1][RTW89_KCC] = 0, + [0][1][RTW89_MEXICO] = 3, + [0][1][RTW89_MKK] = 0, + [0][1][RTW89_QATAR] = 0, + [0][1][RTW89_UK] = 0, + [0][1][RTW89_UKRAINE] = 0, + [1][1][RTW89_ACMA] = 0, + [1][1][RTW89_CHILE] = 0, + [1][1][RTW89_CN] = 0, + [1][1][RTW89_ETSI] = 0, + [1][1][RTW89_FCC] = 3, + [1][1][RTW89_IC] = 3, + [1][1][RTW89_KCC] = 0, + [1][1][RTW89_MEXICO] = 3, + [1][1][RTW89_MKK] = 0, + [1][1][RTW89_QATAR] = 0, + [1][1][RTW89_UK] = 0, + [1][1][RTW89_UKRAINE] = 0, +}; + +const s8 rtw89_8852b_txpwr_lmt_2g[RTW89_2G_BW_NUM][RTW89_NTX_NUM] + [RTW89_RS_LMT_NUM][RTW89_BF_NUM] + [RTW89_REGD_NUM][RTW89_2G_CH_NUM] = { + [0][0][0][0][RTW89_WW][0] = 58, + [0][0][0][0][RTW89_WW][1] = 58, + [0][0][0][0][RTW89_WW][2] = 58, + [0][0][0][0][RTW89_WW][3] = 58, + [0][0][0][0][RTW89_WW][4] = 58, + [0][0][0][0][RTW89_WW][5] = 58, + [0][0][0][0][RTW89_WW][6] = 58, + [0][0][0][0][RTW89_WW][7] = 58, + [0][0][0][0][RTW89_WW][8] = 58, + [0][0][0][0][RTW89_WW][9] = 58, + [0][0][0][0][RTW89_WW][10] = 58, + [0][0][0][0][RTW89_WW][11] = 58, + [0][0][0][0][RTW89_WW][12] = 56, + [0][0][0][0][RTW89_WW][13] = 76, + [0][1][0][0][RTW89_WW][0] = 46, + [0][1][0][0][RTW89_WW][1] = 46, + [0][1][0][0][RTW89_WW][2] = 46, + [0][1][0][0][RTW89_WW][3] = 46, + [0][1][0][0][RTW89_WW][4] = 46, + [0][1][0][0][RTW89_WW][5] = 46, + [0][1][0][0][RTW89_WW][6] = 46, + [0][1][0][0][RTW89_WW][7] = 46, + [0][1][0][0][RTW89_WW][8] = 46, + [0][1][0][0][RTW89_WW][9] = 46, + [0][1][0][0][RTW89_WW][10] = 46, + [0][1][0][0][RTW89_WW][11] = 46, + [0][1][0][0][RTW89_WW][12] = 42, + [0][1][0][0][RTW89_WW][13] = 64, + [1][0][0][0][RTW89_WW][0] = 0, + [1][0][0][0][RTW89_WW][1] = 0, + [1][0][0][0][RTW89_WW][2] = 50, + [1][0][0][0][RTW89_WW][3] = 50, + [1][0][0][0][RTW89_WW][4] = 50, + [1][0][0][0][RTW89_WW][5] = 58, + [1][0][0][0][RTW89_WW][6] = 50, + [1][0][0][0][RTW89_WW][7] = 50, + [1][0][0][0][RTW89_WW][8] = 50, + [1][0][0][0][RTW89_WW][9] = 42, + [1][0][0][0][RTW89_WW][10] = 30, + [1][0][0][0][RTW89_WW][11] = 0, + [1][0][0][0][RTW89_WW][12] = 0, + [1][0][0][0][RTW89_WW][13] = 0, + [1][1][0][0][RTW89_WW][0] = 0, + [1][1][0][0][RTW89_WW][1] = 0, + [1][1][0][0][RTW89_WW][2] = 46, + [1][1][0][0][RTW89_WW][3] = 46, + [1][1][0][0][RTW89_WW][4] = 46, + [1][1][0][0][RTW89_WW][5] = 46, + [1][1][0][0][RTW89_WW][6] = 34, + [1][1][0][0][RTW89_WW][7] = 34, + [1][1][0][0][RTW89_WW][8] = 34, + [1][1][0][0][RTW89_WW][9] = 30, + [1][1][0][0][RTW89_WW][10] = 30, + [1][1][0][0][RTW89_WW][11] = 0, + [1][1][0][0][RTW89_WW][12] = 0, + [1][1][0][0][RTW89_WW][13] = 0, + [0][0][1][0][RTW89_WW][0] = 58, + [0][0][1][0][RTW89_WW][1] = 58, + [0][0][1][0][RTW89_WW][2] = 58, + [0][0][1][0][RTW89_WW][3] = 58, + [0][0][1][0][RTW89_WW][4] = 58, + [0][0][1][0][RTW89_WW][5] = 58, + [0][0][1][0][RTW89_WW][6] = 58, + [0][0][1][0][RTW89_WW][7] = 58, + [0][0][1][0][RTW89_WW][8] = 58, + [0][0][1][0][RTW89_WW][9] = 58, + [0][0][1][0][RTW89_WW][10] = 58, + [0][0][1][0][RTW89_WW][11] = 54, + [0][0][1][0][RTW89_WW][12] = 50, + [0][0][1][0][RTW89_WW][13] = 0, + [0][1][1][0][RTW89_WW][0] = 46, + [0][1][1][0][RTW89_WW][1] = 46, + [0][1][1][0][RTW89_WW][2] = 46, + [0][1][1][0][RTW89_WW][3] = 46, + [0][1][1][0][RTW89_WW][4] = 46, + [0][1][1][0][RTW89_WW][5] = 46, + [0][1][1][0][RTW89_WW][6] = 46, + [0][1][1][0][RTW89_WW][7] = 46, + [0][1][1][0][RTW89_WW][8] = 46, + [0][1][1][0][RTW89_WW][9] = 46, + [0][1][1][0][RTW89_WW][10] = 46, + [0][1][1][0][RTW89_WW][11] = 46, + [0][1][1][0][RTW89_WW][12] = 42, + [0][1][1][0][RTW89_WW][13] = 0, + [0][0][2][0][RTW89_WW][0] = 58, + [0][0][2][0][RTW89_WW][1] = 58, + [0][0][2][0][RTW89_WW][2] = 58, + [0][0][2][0][RTW89_WW][3] = 58, + [0][0][2][0][RTW89_WW][4] = 58, + [0][0][2][0][RTW89_WW][5] = 58, + [0][0][2][0][RTW89_WW][6] = 58, + [0][0][2][0][RTW89_WW][7] = 58, + [0][0][2][0][RTW89_WW][8] = 58, + [0][0][2][0][RTW89_WW][9] = 58, + [0][0][2][0][RTW89_WW][10] = 58, + [0][0][2][0][RTW89_WW][11] = 54, + [0][0][2][0][RTW89_WW][12] = 50, + [0][0][2][0][RTW89_WW][13] = 0, + [0][1][2][0][RTW89_WW][0] = 46, + [0][1][2][0][RTW89_WW][1] = 46, + [0][1][2][0][RTW89_WW][2] = 46, + [0][1][2][0][RTW89_WW][3] = 46, + [0][1][2][0][RTW89_WW][4] = 46, + [0][1][2][0][RTW89_WW][5] = 46, + [0][1][2][0][RTW89_WW][6] = 46, + [0][1][2][0][RTW89_WW][7] = 46, + [0][1][2][0][RTW89_WW][8] = 46, + [0][1][2][0][RTW89_WW][9] = 46, + [0][1][2][0][RTW89_WW][10] = 46, + [0][1][2][0][RTW89_WW][11] = 46, + [0][1][2][0][RTW89_WW][12] = 42, + [0][1][2][0][RTW89_WW][13] = 0, + [0][1][2][1][RTW89_WW][0] = 34, + [0][1][2][1][RTW89_WW][1] = 34, + [0][1][2][1][RTW89_WW][2] = 34, + [0][1][2][1][RTW89_WW][3] = 34, + [0][1][2][1][RTW89_WW][4] = 34, + [0][1][2][1][RTW89_WW][5] = 34, + [0][1][2][1][RTW89_WW][6] = 34, + [0][1][2][1][RTW89_WW][7] = 34, + [0][1][2][1][RTW89_WW][8] = 34, + [0][1][2][1][RTW89_WW][9] = 34, + [0][1][2][1][RTW89_WW][10] = 34, + [0][1][2][1][RTW89_WW][11] = 34, + [0][1][2][1][RTW89_WW][12] = 34, + [0][1][2][1][RTW89_WW][13] = 0, + [1][0][2][0][RTW89_WW][0] = 0, + [1][0][2][0][RTW89_WW][1] = 0, + [1][0][2][0][RTW89_WW][2] = 58, + [1][0][2][0][RTW89_WW][3] = 58, + [1][0][2][0][RTW89_WW][4] = 58, + [1][0][2][0][RTW89_WW][5] = 58, + [1][0][2][0][RTW89_WW][6] = 58, + [1][0][2][0][RTW89_WW][7] = 58, + [1][0][2][0][RTW89_WW][8] = 58, + [1][0][2][0][RTW89_WW][9] = 58, + [1][0][2][0][RTW89_WW][10] = 58, + [1][0][2][0][RTW89_WW][11] = 0, + [1][0][2][0][RTW89_WW][12] = 0, + [1][0][2][0][RTW89_WW][13] = 0, + [1][1][2][0][RTW89_WW][0] = 0, + [1][1][2][0][RTW89_WW][1] = 0, + [1][1][2][0][RTW89_WW][2] = 46, + [1][1][2][0][RTW89_WW][3] = 46, + [1][1][2][0][RTW89_WW][4] = 46, + [1][1][2][0][RTW89_WW][5] = 46, + [1][1][2][0][RTW89_WW][6] = 46, + [1][1][2][0][RTW89_WW][7] = 46, + [1][1][2][0][RTW89_WW][8] = 46, + [1][1][2][0][RTW89_WW][9] = 42, + [1][1][2][0][RTW89_WW][10] = 38, + [1][1][2][0][RTW89_WW][11] = 0, + [1][1][2][0][RTW89_WW][12] = 0, + [1][1][2][0][RTW89_WW][13] = 0, + [1][1][2][1][RTW89_WW][0] = 0, + [1][1][2][1][RTW89_WW][1] = 0, + [1][1][2][1][RTW89_WW][2] = 34, + [1][1][2][1][RTW89_WW][3] = 34, + [1][1][2][1][RTW89_WW][4] = 34, + [1][1][2][1][RTW89_WW][5] = 34, + [1][1][2][1][RTW89_WW][6] = 34, + [1][1][2][1][RTW89_WW][7] = 34, + [1][1][2][1][RTW89_WW][8] = 34, + [1][1][2][1][RTW89_WW][9] = 34, + [1][1][2][1][RTW89_WW][10] = 34, + [1][1][2][1][RTW89_WW][11] = 0, + [1][1][2][1][RTW89_WW][12] = 0, + [1][1][2][1][RTW89_WW][13] = 0, + [0][0][0][0][RTW89_FCC][0] = 78, + [0][0][0][0][RTW89_ETSI][0] = 58, + [0][0][0][0][RTW89_MKK][0] = 68, + [0][0][0][0][RTW89_IC][0] = 78, + [0][0][0][0][RTW89_KCC][0] = 68, + [0][0][0][0][RTW89_ACMA][0] = 58, + [0][0][0][0][RTW89_CHILE][0] = 64, + [0][0][0][0][RTW89_UKRAINE][0] = 58, + [0][0][0][0][RTW89_MEXICO][0] = 78, + [0][0][0][0][RTW89_CN][0] = 58, + [0][0][0][0][RTW89_QATAR][0] = 58, + [0][0][0][0][RTW89_UK][0] = 58, + [0][0][0][0][RTW89_FCC][1] = 78, + [0][0][0][0][RTW89_ETSI][1] = 58, + [0][0][0][0][RTW89_MKK][1] = 68, + [0][0][0][0][RTW89_IC][1] = 78, + [0][0][0][0][RTW89_KCC][1] = 68, + [0][0][0][0][RTW89_ACMA][1] = 58, + [0][0][0][0][RTW89_CHILE][1] = 64, + [0][0][0][0][RTW89_UKRAINE][1] = 58, + [0][0][0][0][RTW89_MEXICO][1] = 78, + [0][0][0][0][RTW89_CN][1] = 58, + [0][0][0][0][RTW89_QATAR][1] = 58, + [0][0][0][0][RTW89_UK][1] = 58, + [0][0][0][0][RTW89_FCC][2] = 78, + [0][0][0][0][RTW89_ETSI][2] = 58, + [0][0][0][0][RTW89_MKK][2] = 68, + [0][0][0][0][RTW89_IC][2] = 78, + [0][0][0][0][RTW89_KCC][2] = 68, + [0][0][0][0][RTW89_ACMA][2] = 58, + [0][0][0][0][RTW89_CHILE][2] = 64, + [0][0][0][0][RTW89_UKRAINE][2] = 58, + [0][0][0][0][RTW89_MEXICO][2] = 78, + [0][0][0][0][RTW89_CN][2] = 58, + [0][0][0][0][RTW89_QATAR][2] = 58, + [0][0][0][0][RTW89_UK][2] = 58, + [0][0][0][0][RTW89_FCC][3] = 78, + [0][0][0][0][RTW89_ETSI][3] = 58, + [0][0][0][0][RTW89_MKK][3] = 68, + [0][0][0][0][RTW89_IC][3] = 78, + [0][0][0][0][RTW89_KCC][3] = 68, + [0][0][0][0][RTW89_ACMA][3] = 58, + [0][0][0][0][RTW89_CHILE][3] = 64, + [0][0][0][0][RTW89_UKRAINE][3] = 58, + [0][0][0][0][RTW89_MEXICO][3] = 78, + [0][0][0][0][RTW89_CN][3] = 58, + [0][0][0][0][RTW89_QATAR][3] = 58, + [0][0][0][0][RTW89_UK][3] = 58, + [0][0][0][0][RTW89_FCC][4] = 78, + [0][0][0][0][RTW89_ETSI][4] = 58, + [0][0][0][0][RTW89_MKK][4] = 68, + [0][0][0][0][RTW89_IC][4] = 78, + [0][0][0][0][RTW89_KCC][4] = 70, + [0][0][0][0][RTW89_ACMA][4] = 58, + [0][0][0][0][RTW89_CHILE][4] = 64, + [0][0][0][0][RTW89_UKRAINE][4] = 58, + [0][0][0][0][RTW89_MEXICO][4] = 78, + [0][0][0][0][RTW89_CN][4] = 58, + [0][0][0][0][RTW89_QATAR][4] = 58, + [0][0][0][0][RTW89_UK][4] = 58, + [0][0][0][0][RTW89_FCC][5] = 78, + [0][0][0][0][RTW89_ETSI][5] = 58, + [0][0][0][0][RTW89_MKK][5] = 68, + [0][0][0][0][RTW89_IC][5] = 78, + [0][0][0][0][RTW89_KCC][5] = 70, + [0][0][0][0][RTW89_ACMA][5] = 58, + [0][0][0][0][RTW89_CHILE][5] = 64, + [0][0][0][0][RTW89_UKRAINE][5] = 58, + [0][0][0][0][RTW89_MEXICO][5] = 78, + [0][0][0][0][RTW89_CN][5] = 58, + [0][0][0][0][RTW89_QATAR][5] = 58, + [0][0][0][0][RTW89_UK][5] = 58, + [0][0][0][0][RTW89_FCC][6] = 78, + [0][0][0][0][RTW89_ETSI][6] = 58, + [0][0][0][0][RTW89_MKK][6] = 68, + [0][0][0][0][RTW89_IC][6] = 78, + [0][0][0][0][RTW89_KCC][6] = 70, + [0][0][0][0][RTW89_ACMA][6] = 58, + [0][0][0][0][RTW89_CHILE][6] = 64, + [0][0][0][0][RTW89_UKRAINE][6] = 58, + [0][0][0][0][RTW89_MEXICO][6] = 78, + [0][0][0][0][RTW89_CN][6] = 58, + [0][0][0][0][RTW89_QATAR][6] = 58, + [0][0][0][0][RTW89_UK][6] = 58, + [0][0][0][0][RTW89_FCC][7] = 78, + [0][0][0][0][RTW89_ETSI][7] = 58, + [0][0][0][0][RTW89_MKK][7] = 68, + [0][0][0][0][RTW89_IC][7] = 78, + [0][0][0][0][RTW89_KCC][7] = 70, + [0][0][0][0][RTW89_ACMA][7] = 58, + [0][0][0][0][RTW89_CHILE][7] = 64, + [0][0][0][0][RTW89_UKRAINE][7] = 58, + [0][0][0][0][RTW89_MEXICO][7] = 78, + [0][0][0][0][RTW89_CN][7] = 58, + [0][0][0][0][RTW89_QATAR][7] = 58, + [0][0][0][0][RTW89_UK][7] = 58, + [0][0][0][0][RTW89_FCC][8] = 78, + [0][0][0][0][RTW89_ETSI][8] = 58, + [0][0][0][0][RTW89_MKK][8] = 68, + [0][0][0][0][RTW89_IC][8] = 78, + [0][0][0][0][RTW89_KCC][8] = 70, + [0][0][0][0][RTW89_ACMA][8] = 58, + [0][0][0][0][RTW89_CHILE][8] = 64, + [0][0][0][0][RTW89_UKRAINE][8] = 58, + [0][0][0][0][RTW89_MEXICO][8] = 78, + [0][0][0][0][RTW89_CN][8] = 58, + [0][0][0][0][RTW89_QATAR][8] = 58, + [0][0][0][0][RTW89_UK][8] = 58, + [0][0][0][0][RTW89_FCC][9] = 78, + [0][0][0][0][RTW89_ETSI][9] = 58, + [0][0][0][0][RTW89_MKK][9] = 68, + [0][0][0][0][RTW89_IC][9] = 78, + [0][0][0][0][RTW89_KCC][9] = 70, + [0][0][0][0][RTW89_ACMA][9] = 58, + [0][0][0][0][RTW89_CHILE][9] = 64, + [0][0][0][0][RTW89_UKRAINE][9] = 58, + [0][0][0][0][RTW89_MEXICO][9] = 78, + [0][0][0][0][RTW89_CN][9] = 58, + [0][0][0][0][RTW89_QATAR][9] = 58, + [0][0][0][0][RTW89_UK][9] = 58, + [0][0][0][0][RTW89_FCC][10] = 78, + [0][0][0][0][RTW89_ETSI][10] = 58, + [0][0][0][0][RTW89_MKK][10] = 68, + [0][0][0][0][RTW89_IC][10] = 78, + [0][0][0][0][RTW89_KCC][10] = 70, + [0][0][0][0][RTW89_ACMA][10] = 58, + [0][0][0][0][RTW89_CHILE][10] = 66, + [0][0][0][0][RTW89_UKRAINE][10] = 58, + [0][0][0][0][RTW89_MEXICO][10] = 78, + [0][0][0][0][RTW89_CN][10] = 58, + [0][0][0][0][RTW89_QATAR][10] = 58, + [0][0][0][0][RTW89_UK][10] = 58, + [0][0][0][0][RTW89_FCC][11] = 70, + [0][0][0][0][RTW89_ETSI][11] = 58, + [0][0][0][0][RTW89_MKK][11] = 68, + [0][0][0][0][RTW89_IC][11] = 70, + [0][0][0][0][RTW89_KCC][11] = 70, + [0][0][0][0][RTW89_ACMA][11] = 58, + [0][0][0][0][RTW89_CHILE][11] = 64, + [0][0][0][0][RTW89_UKRAINE][11] = 58, + [0][0][0][0][RTW89_MEXICO][11] = 70, + [0][0][0][0][RTW89_CN][11] = 58, + [0][0][0][0][RTW89_QATAR][11] = 58, + [0][0][0][0][RTW89_UK][11] = 58, + [0][0][0][0][RTW89_FCC][12] = 56, + [0][0][0][0][RTW89_ETSI][12] = 58, + [0][0][0][0][RTW89_MKK][12] = 68, + [0][0][0][0][RTW89_IC][12] = 56, + [0][0][0][0][RTW89_KCC][12] = 70, + [0][0][0][0][RTW89_ACMA][12] = 58, + [0][0][0][0][RTW89_CHILE][12] = 56, + [0][0][0][0][RTW89_UKRAINE][12] = 58, + [0][0][0][0][RTW89_MEXICO][12] = 56, + [0][0][0][0][RTW89_CN][12] = 58, + [0][0][0][0][RTW89_QATAR][12] = 58, + [0][0][0][0][RTW89_UK][12] = 58, + [0][0][0][0][RTW89_FCC][13] = 127, + [0][0][0][0][RTW89_ETSI][13] = 127, + [0][0][0][0][RTW89_MKK][13] = 76, + [0][0][0][0][RTW89_IC][13] = 127, + [0][0][0][0][RTW89_KCC][13] = 127, + [0][0][0][0][RTW89_ACMA][13] = 127, + [0][0][0][0][RTW89_CHILE][13] = 127, + [0][0][0][0][RTW89_UKRAINE][13] = 127, + [0][0][0][0][RTW89_MEXICO][13] = 127, + [0][0][0][0][RTW89_CN][13] = 127, + [0][0][0][0][RTW89_QATAR][13] = 127, + [0][0][0][0][RTW89_UK][13] = 127, + [0][1][0][0][RTW89_FCC][0] = 74, + [0][1][0][0][RTW89_ETSI][0] = 46, + [0][1][0][0][RTW89_MKK][0] = 56, + [0][1][0][0][RTW89_IC][0] = 74, + [0][1][0][0][RTW89_KCC][0] = 58, + [0][1][0][0][RTW89_ACMA][0] = 46, + [0][1][0][0][RTW89_CHILE][0] = 50, + [0][1][0][0][RTW89_UKRAINE][0] = 46, + [0][1][0][0][RTW89_MEXICO][0] = 74, + [0][1][0][0][RTW89_CN][0] = 46, + [0][1][0][0][RTW89_QATAR][0] = 46, + [0][1][0][0][RTW89_UK][0] = 46, + [0][1][0][0][RTW89_FCC][1] = 74, + [0][1][0][0][RTW89_ETSI][1] = 46, + [0][1][0][0][RTW89_MKK][1] = 56, + [0][1][0][0][RTW89_IC][1] = 74, + [0][1][0][0][RTW89_KCC][1] = 58, + [0][1][0][0][RTW89_ACMA][1] = 46, + [0][1][0][0][RTW89_CHILE][1] = 50, + [0][1][0][0][RTW89_UKRAINE][1] = 46, + [0][1][0][0][RTW89_MEXICO][1] = 74, + [0][1][0][0][RTW89_CN][1] = 46, + [0][1][0][0][RTW89_QATAR][1] = 46, + [0][1][0][0][RTW89_UK][1] = 46, + [0][1][0][0][RTW89_FCC][2] = 74, + [0][1][0][0][RTW89_ETSI][2] = 46, + [0][1][0][0][RTW89_MKK][2] = 56, + [0][1][0][0][RTW89_IC][2] = 74, + [0][1][0][0][RTW89_KCC][2] = 58, + [0][1][0][0][RTW89_ACMA][2] = 46, + [0][1][0][0][RTW89_CHILE][2] = 50, + [0][1][0][0][RTW89_UKRAINE][2] = 46, + [0][1][0][0][RTW89_MEXICO][2] = 74, + [0][1][0][0][RTW89_CN][2] = 46, + [0][1][0][0][RTW89_QATAR][2] = 46, + [0][1][0][0][RTW89_UK][2] = 46, + [0][1][0][0][RTW89_FCC][3] = 74, + [0][1][0][0][RTW89_ETSI][3] = 46, + [0][1][0][0][RTW89_MKK][3] = 56, + [0][1][0][0][RTW89_IC][3] = 74, + [0][1][0][0][RTW89_KCC][3] = 58, + [0][1][0][0][RTW89_ACMA][3] = 46, + [0][1][0][0][RTW89_CHILE][3] = 50, + [0][1][0][0][RTW89_UKRAINE][3] = 46, + [0][1][0][0][RTW89_MEXICO][3] = 74, + [0][1][0][0][RTW89_CN][3] = 46, + [0][1][0][0][RTW89_QATAR][3] = 46, + [0][1][0][0][RTW89_UK][3] = 46, + [0][1][0][0][RTW89_FCC][4] = 74, + [0][1][0][0][RTW89_ETSI][4] = 46, + [0][1][0][0][RTW89_MKK][4] = 56, + [0][1][0][0][RTW89_IC][4] = 74, + [0][1][0][0][RTW89_KCC][4] = 56, + [0][1][0][0][RTW89_ACMA][4] = 46, + [0][1][0][0][RTW89_CHILE][4] = 50, + [0][1][0][0][RTW89_UKRAINE][4] = 46, + [0][1][0][0][RTW89_MEXICO][4] = 74, + [0][1][0][0][RTW89_CN][4] = 46, + [0][1][0][0][RTW89_QATAR][4] = 46, + [0][1][0][0][RTW89_UK][4] = 46, + [0][1][0][0][RTW89_FCC][5] = 74, + [0][1][0][0][RTW89_ETSI][5] = 46, + [0][1][0][0][RTW89_MKK][5] = 56, + [0][1][0][0][RTW89_IC][5] = 74, + [0][1][0][0][RTW89_KCC][5] = 56, + [0][1][0][0][RTW89_ACMA][5] = 46, + [0][1][0][0][RTW89_CHILE][5] = 50, + [0][1][0][0][RTW89_UKRAINE][5] = 46, + [0][1][0][0][RTW89_MEXICO][5] = 74, + [0][1][0][0][RTW89_CN][5] = 46, + [0][1][0][0][RTW89_QATAR][5] = 46, + [0][1][0][0][RTW89_UK][5] = 46, + [0][1][0][0][RTW89_FCC][6] = 74, + [0][1][0][0][RTW89_ETSI][6] = 46, + [0][1][0][0][RTW89_MKK][6] = 56, + [0][1][0][0][RTW89_IC][6] = 74, + [0][1][0][0][RTW89_KCC][6] = 56, + [0][1][0][0][RTW89_ACMA][6] = 46, + [0][1][0][0][RTW89_CHILE][6] = 52, + [0][1][0][0][RTW89_UKRAINE][6] = 46, + [0][1][0][0][RTW89_MEXICO][6] = 74, + [0][1][0][0][RTW89_CN][6] = 46, + [0][1][0][0][RTW89_QATAR][6] = 46, + [0][1][0][0][RTW89_UK][6] = 46, + [0][1][0][0][RTW89_FCC][7] = 74, + [0][1][0][0][RTW89_ETSI][7] = 46, + [0][1][0][0][RTW89_MKK][7] = 56, + [0][1][0][0][RTW89_IC][7] = 74, + [0][1][0][0][RTW89_KCC][7] = 56, + [0][1][0][0][RTW89_ACMA][7] = 46, + [0][1][0][0][RTW89_CHILE][7] = 50, + [0][1][0][0][RTW89_UKRAINE][7] = 46, + [0][1][0][0][RTW89_MEXICO][7] = 74, + [0][1][0][0][RTW89_CN][7] = 46, + [0][1][0][0][RTW89_QATAR][7] = 46, + [0][1][0][0][RTW89_UK][7] = 46, + [0][1][0][0][RTW89_FCC][8] = 74, + [0][1][0][0][RTW89_ETSI][8] = 46, + [0][1][0][0][RTW89_MKK][8] = 56, + [0][1][0][0][RTW89_IC][8] = 74, + [0][1][0][0][RTW89_KCC][8] = 56, + [0][1][0][0][RTW89_ACMA][8] = 46, + [0][1][0][0][RTW89_CHILE][8] = 50, + [0][1][0][0][RTW89_UKRAINE][8] = 46, + [0][1][0][0][RTW89_MEXICO][8] = 74, + [0][1][0][0][RTW89_CN][8] = 46, + [0][1][0][0][RTW89_QATAR][8] = 46, + [0][1][0][0][RTW89_UK][8] = 46, + [0][1][0][0][RTW89_FCC][9] = 74, + [0][1][0][0][RTW89_ETSI][9] = 46, + [0][1][0][0][RTW89_MKK][9] = 56, + [0][1][0][0][RTW89_IC][9] = 74, + [0][1][0][0][RTW89_KCC][9] = 54, + [0][1][0][0][RTW89_ACMA][9] = 46, + [0][1][0][0][RTW89_CHILE][9] = 50, + [0][1][0][0][RTW89_UKRAINE][9] = 46, + [0][1][0][0][RTW89_MEXICO][9] = 74, + [0][1][0][0][RTW89_CN][9] = 46, + [0][1][0][0][RTW89_QATAR][9] = 46, + [0][1][0][0][RTW89_UK][9] = 46, + [0][1][0][0][RTW89_FCC][10] = 74, + [0][1][0][0][RTW89_ETSI][10] = 46, + [0][1][0][0][RTW89_MKK][10] = 56, + [0][1][0][0][RTW89_IC][10] = 74, + [0][1][0][0][RTW89_KCC][10] = 54, + [0][1][0][0][RTW89_ACMA][10] = 46, + [0][1][0][0][RTW89_CHILE][10] = 52, + [0][1][0][0][RTW89_UKRAINE][10] = 46, + [0][1][0][0][RTW89_MEXICO][10] = 74, + [0][1][0][0][RTW89_CN][10] = 46, + [0][1][0][0][RTW89_QATAR][10] = 46, + [0][1][0][0][RTW89_UK][10] = 46, + [0][1][0][0][RTW89_FCC][11] = 54, + [0][1][0][0][RTW89_ETSI][11] = 46, + [0][1][0][0][RTW89_MKK][11] = 56, + [0][1][0][0][RTW89_IC][11] = 54, + [0][1][0][0][RTW89_KCC][11] = 54, + [0][1][0][0][RTW89_ACMA][11] = 46, + [0][1][0][0][RTW89_CHILE][11] = 50, + [0][1][0][0][RTW89_UKRAINE][11] = 46, + [0][1][0][0][RTW89_MEXICO][11] = 54, + [0][1][0][0][RTW89_CN][11] = 46, + [0][1][0][0][RTW89_QATAR][11] = 46, + [0][1][0][0][RTW89_UK][11] = 46, + [0][1][0][0][RTW89_FCC][12] = 42, + [0][1][0][0][RTW89_ETSI][12] = 46, + [0][1][0][0][RTW89_MKK][12] = 56, + [0][1][0][0][RTW89_IC][12] = 42, + [0][1][0][0][RTW89_KCC][12] = 54, + [0][1][0][0][RTW89_ACMA][12] = 46, + [0][1][0][0][RTW89_CHILE][12] = 42, + [0][1][0][0][RTW89_UKRAINE][12] = 46, + [0][1][0][0][RTW89_MEXICO][12] = 42, + [0][1][0][0][RTW89_CN][12] = 46, + [0][1][0][0][RTW89_QATAR][12] = 46, + [0][1][0][0][RTW89_UK][12] = 46, + [0][1][0][0][RTW89_FCC][13] = 127, + [0][1][0][0][RTW89_ETSI][13] = 127, + [0][1][0][0][RTW89_MKK][13] = 64, + [0][1][0][0][RTW89_IC][13] = 127, + [0][1][0][0][RTW89_KCC][13] = 127, + [0][1][0][0][RTW89_ACMA][13] = 127, + [0][1][0][0][RTW89_CHILE][13] = 127, + [0][1][0][0][RTW89_UKRAINE][13] = 127, + [0][1][0][0][RTW89_MEXICO][13] = 127, + [0][1][0][0][RTW89_CN][13] = 127, + [0][1][0][0][RTW89_QATAR][13] = 127, + [0][1][0][0][RTW89_UK][13] = 127, + [1][0][0][0][RTW89_FCC][0] = 127, + [1][0][0][0][RTW89_ETSI][0] = 127, + [1][0][0][0][RTW89_MKK][0] = 127, + [1][0][0][0][RTW89_IC][0] = 127, + [1][0][0][0][RTW89_KCC][0] = 127, + [1][0][0][0][RTW89_ACMA][0] = 127, + [1][0][0][0][RTW89_CHILE][0] = 127, + [1][0][0][0][RTW89_UKRAINE][0] = 127, + [1][0][0][0][RTW89_MEXICO][0] = 127, + [1][0][0][0][RTW89_CN][0] = 127, + [1][0][0][0][RTW89_QATAR][0] = 127, + [1][0][0][0][RTW89_UK][0] = 127, + [1][0][0][0][RTW89_FCC][1] = 127, + [1][0][0][0][RTW89_ETSI][1] = 127, + [1][0][0][0][RTW89_MKK][1] = 127, + [1][0][0][0][RTW89_IC][1] = 127, + [1][0][0][0][RTW89_KCC][1] = 127, + [1][0][0][0][RTW89_ACMA][1] = 127, + [1][0][0][0][RTW89_CHILE][1] = 127, + [1][0][0][0][RTW89_UKRAINE][1] = 127, + [1][0][0][0][RTW89_MEXICO][1] = 127, + [1][0][0][0][RTW89_CN][1] = 127, + [1][0][0][0][RTW89_QATAR][1] = 127, + [1][0][0][0][RTW89_UK][1] = 127, + [1][0][0][0][RTW89_FCC][2] = 50, + [1][0][0][0][RTW89_ETSI][2] = 58, + [1][0][0][0][RTW89_MKK][2] = 76, + [1][0][0][0][RTW89_IC][2] = 50, + [1][0][0][0][RTW89_KCC][2] = 70, + [1][0][0][0][RTW89_ACMA][2] = 58, + [1][0][0][0][RTW89_CHILE][2] = 62, + [1][0][0][0][RTW89_UKRAINE][2] = 58, + [1][0][0][0][RTW89_MEXICO][2] = 50, + [1][0][0][0][RTW89_CN][2] = 58, + [1][0][0][0][RTW89_QATAR][2] = 58, + [1][0][0][0][RTW89_UK][2] = 58, + [1][0][0][0][RTW89_FCC][3] = 50, + [1][0][0][0][RTW89_ETSI][3] = 58, + [1][0][0][0][RTW89_MKK][3] = 76, + [1][0][0][0][RTW89_IC][3] = 50, + [1][0][0][0][RTW89_KCC][3] = 70, + [1][0][0][0][RTW89_ACMA][3] = 58, + [1][0][0][0][RTW89_CHILE][3] = 62, + [1][0][0][0][RTW89_UKRAINE][3] = 58, + [1][0][0][0][RTW89_MEXICO][3] = 50, + [1][0][0][0][RTW89_CN][3] = 58, + [1][0][0][0][RTW89_QATAR][3] = 58, + [1][0][0][0][RTW89_UK][3] = 58, + [1][0][0][0][RTW89_FCC][4] = 50, + [1][0][0][0][RTW89_ETSI][4] = 58, + [1][0][0][0][RTW89_MKK][4] = 76, + [1][0][0][0][RTW89_IC][4] = 50, + [1][0][0][0][RTW89_KCC][4] = 70, + [1][0][0][0][RTW89_ACMA][4] = 58, + [1][0][0][0][RTW89_CHILE][4] = 62, + [1][0][0][0][RTW89_UKRAINE][4] = 58, + [1][0][0][0][RTW89_MEXICO][4] = 50, + [1][0][0][0][RTW89_CN][4] = 58, + [1][0][0][0][RTW89_QATAR][4] = 58, + [1][0][0][0][RTW89_UK][4] = 58, + [1][0][0][0][RTW89_FCC][5] = 66, + [1][0][0][0][RTW89_ETSI][5] = 58, + [1][0][0][0][RTW89_MKK][5] = 76, + [1][0][0][0][RTW89_IC][5] = 66, + [1][0][0][0][RTW89_KCC][5] = 70, + [1][0][0][0][RTW89_ACMA][5] = 58, + [1][0][0][0][RTW89_CHILE][5] = 62, + [1][0][0][0][RTW89_UKRAINE][5] = 58, + [1][0][0][0][RTW89_MEXICO][5] = 66, + [1][0][0][0][RTW89_CN][5] = 58, + [1][0][0][0][RTW89_QATAR][5] = 58, + [1][0][0][0][RTW89_UK][5] = 58, + [1][0][0][0][RTW89_FCC][6] = 50, + [1][0][0][0][RTW89_ETSI][6] = 58, + [1][0][0][0][RTW89_MKK][6] = 76, + [1][0][0][0][RTW89_IC][6] = 50, + [1][0][0][0][RTW89_KCC][6] = 70, + [1][0][0][0][RTW89_ACMA][6] = 58, + [1][0][0][0][RTW89_CHILE][6] = 62, + [1][0][0][0][RTW89_UKRAINE][6] = 58, + [1][0][0][0][RTW89_MEXICO][6] = 50, + [1][0][0][0][RTW89_CN][6] = 58, + [1][0][0][0][RTW89_QATAR][6] = 58, + [1][0][0][0][RTW89_UK][6] = 58, + [1][0][0][0][RTW89_FCC][7] = 50, + [1][0][0][0][RTW89_ETSI][7] = 58, + [1][0][0][0][RTW89_MKK][7] = 76, + [1][0][0][0][RTW89_IC][7] = 50, + [1][0][0][0][RTW89_KCC][7] = 70, + [1][0][0][0][RTW89_ACMA][7] = 58, + [1][0][0][0][RTW89_CHILE][7] = 62, + [1][0][0][0][RTW89_UKRAINE][7] = 58, + [1][0][0][0][RTW89_MEXICO][7] = 50, + [1][0][0][0][RTW89_CN][7] = 58, + [1][0][0][0][RTW89_QATAR][7] = 58, + [1][0][0][0][RTW89_UK][7] = 58, + [1][0][0][0][RTW89_FCC][8] = 50, + [1][0][0][0][RTW89_ETSI][8] = 58, + [1][0][0][0][RTW89_MKK][8] = 76, + [1][0][0][0][RTW89_IC][8] = 50, + [1][0][0][0][RTW89_KCC][8] = 70, + [1][0][0][0][RTW89_ACMA][8] = 58, + [1][0][0][0][RTW89_CHILE][8] = 62, + [1][0][0][0][RTW89_UKRAINE][8] = 58, + [1][0][0][0][RTW89_MEXICO][8] = 50, + [1][0][0][0][RTW89_CN][8] = 58, + [1][0][0][0][RTW89_QATAR][8] = 58, + [1][0][0][0][RTW89_UK][8] = 58, + [1][0][0][0][RTW89_FCC][9] = 42, + [1][0][0][0][RTW89_ETSI][9] = 58, + [1][0][0][0][RTW89_MKK][9] = 76, + [1][0][0][0][RTW89_IC][9] = 42, + [1][0][0][0][RTW89_KCC][9] = 70, + [1][0][0][0][RTW89_ACMA][9] = 58, + [1][0][0][0][RTW89_CHILE][9] = 42, + [1][0][0][0][RTW89_UKRAINE][9] = 58, + [1][0][0][0][RTW89_MEXICO][9] = 42, + [1][0][0][0][RTW89_CN][9] = 58, + [1][0][0][0][RTW89_QATAR][9] = 58, + [1][0][0][0][RTW89_UK][9] = 58, + [1][0][0][0][RTW89_FCC][10] = 30, + [1][0][0][0][RTW89_ETSI][10] = 58, + [1][0][0][0][RTW89_MKK][10] = 72, + [1][0][0][0][RTW89_IC][10] = 30, + [1][0][0][0][RTW89_KCC][10] = 70, + [1][0][0][0][RTW89_ACMA][10] = 58, + [1][0][0][0][RTW89_CHILE][10] = 30, + [1][0][0][0][RTW89_UKRAINE][10] = 58, + [1][0][0][0][RTW89_MEXICO][10] = 30, + [1][0][0][0][RTW89_CN][10] = 58, + [1][0][0][0][RTW89_QATAR][10] = 58, + [1][0][0][0][RTW89_UK][10] = 58, + [1][0][0][0][RTW89_FCC][11] = 127, + [1][0][0][0][RTW89_ETSI][11] = 127, + [1][0][0][0][RTW89_MKK][11] = 127, + [1][0][0][0][RTW89_IC][11] = 127, + [1][0][0][0][RTW89_KCC][11] = 127, + [1][0][0][0][RTW89_ACMA][11] = 127, + [1][0][0][0][RTW89_CHILE][11] = 127, + [1][0][0][0][RTW89_UKRAINE][11] = 127, + [1][0][0][0][RTW89_MEXICO][11] = 127, + [1][0][0][0][RTW89_CN][11] = 127, + [1][0][0][0][RTW89_QATAR][11] = 127, + [1][0][0][0][RTW89_UK][11] = 127, + [1][0][0][0][RTW89_FCC][12] = 127, + [1][0][0][0][RTW89_ETSI][12] = 127, + [1][0][0][0][RTW89_MKK][12] = 127, + [1][0][0][0][RTW89_IC][12] = 127, + [1][0][0][0][RTW89_KCC][12] = 127, + [1][0][0][0][RTW89_ACMA][12] = 127, + [1][0][0][0][RTW89_CHILE][12] = 127, + [1][0][0][0][RTW89_UKRAINE][12] = 127, + [1][0][0][0][RTW89_MEXICO][12] = 127, + [1][0][0][0][RTW89_CN][12] = 127, + [1][0][0][0][RTW89_QATAR][12] = 127, + [1][0][0][0][RTW89_UK][12] = 127, + [1][0][0][0][RTW89_FCC][13] = 127, + [1][0][0][0][RTW89_ETSI][13] = 127, + [1][0][0][0][RTW89_MKK][13] = 127, + [1][0][0][0][RTW89_IC][13] = 127, + [1][0][0][0][RTW89_KCC][13] = 127, + [1][0][0][0][RTW89_ACMA][13] = 127, + [1][0][0][0][RTW89_CHILE][13] = 127, + [1][0][0][0][RTW89_UKRAINE][13] = 127, + [1][0][0][0][RTW89_MEXICO][13] = 127, + [1][0][0][0][RTW89_CN][13] = 127, + [1][0][0][0][RTW89_QATAR][13] = 127, + [1][0][0][0][RTW89_UK][13] = 127, + [1][1][0][0][RTW89_FCC][0] = 127, + [1][1][0][0][RTW89_ETSI][0] = 127, + [1][1][0][0][RTW89_MKK][0] = 127, + [1][1][0][0][RTW89_IC][0] = 127, + [1][1][0][0][RTW89_KCC][0] = 127, + [1][1][0][0][RTW89_ACMA][0] = 127, + [1][1][0][0][RTW89_CHILE][0] = 127, + [1][1][0][0][RTW89_UKRAINE][0] = 127, + [1][1][0][0][RTW89_MEXICO][0] = 127, + [1][1][0][0][RTW89_CN][0] = 127, + [1][1][0][0][RTW89_QATAR][0] = 127, + [1][1][0][0][RTW89_UK][0] = 127, + [1][1][0][0][RTW89_FCC][1] = 127, + [1][1][0][0][RTW89_ETSI][1] = 127, + [1][1][0][0][RTW89_MKK][1] = 127, + [1][1][0][0][RTW89_IC][1] = 127, + [1][1][0][0][RTW89_KCC][1] = 127, + [1][1][0][0][RTW89_ACMA][1] = 127, + [1][1][0][0][RTW89_CHILE][1] = 127, + [1][1][0][0][RTW89_UKRAINE][1] = 127, + [1][1][0][0][RTW89_MEXICO][1] = 127, + [1][1][0][0][RTW89_CN][1] = 127, + [1][1][0][0][RTW89_QATAR][1] = 127, + [1][1][0][0][RTW89_UK][1] = 127, + [1][1][0][0][RTW89_FCC][2] = 46, + [1][1][0][0][RTW89_ETSI][2] = 46, + [1][1][0][0][RTW89_MKK][2] = 64, + [1][1][0][0][RTW89_IC][2] = 46, + [1][1][0][0][RTW89_KCC][2] = 58, + [1][1][0][0][RTW89_ACMA][2] = 46, + [1][1][0][0][RTW89_CHILE][2] = 50, + [1][1][0][0][RTW89_UKRAINE][2] = 46, + [1][1][0][0][RTW89_MEXICO][2] = 46, + [1][1][0][0][RTW89_CN][2] = 46, + [1][1][0][0][RTW89_QATAR][2] = 46, + [1][1][0][0][RTW89_UK][2] = 46, + [1][1][0][0][RTW89_FCC][3] = 46, + [1][1][0][0][RTW89_ETSI][3] = 46, + [1][1][0][0][RTW89_MKK][3] = 64, + [1][1][0][0][RTW89_IC][3] = 46, + [1][1][0][0][RTW89_KCC][3] = 58, + [1][1][0][0][RTW89_ACMA][3] = 46, + [1][1][0][0][RTW89_CHILE][3] = 50, + [1][1][0][0][RTW89_UKRAINE][3] = 46, + [1][1][0][0][RTW89_MEXICO][3] = 46, + [1][1][0][0][RTW89_CN][3] = 46, + [1][1][0][0][RTW89_QATAR][3] = 46, + [1][1][0][0][RTW89_UK][3] = 46, + [1][1][0][0][RTW89_FCC][4] = 46, + [1][1][0][0][RTW89_ETSI][4] = 46, + [1][1][0][0][RTW89_MKK][4] = 64, + [1][1][0][0][RTW89_IC][4] = 46, + [1][1][0][0][RTW89_KCC][4] = 58, + [1][1][0][0][RTW89_ACMA][4] = 46, + [1][1][0][0][RTW89_CHILE][4] = 50, + [1][1][0][0][RTW89_UKRAINE][4] = 46, + [1][1][0][0][RTW89_MEXICO][4] = 46, + [1][1][0][0][RTW89_CN][4] = 46, + [1][1][0][0][RTW89_QATAR][4] = 46, + [1][1][0][0][RTW89_UK][4] = 46, + [1][1][0][0][RTW89_FCC][5] = 62, + [1][1][0][0][RTW89_ETSI][5] = 46, + [1][1][0][0][RTW89_MKK][5] = 64, + [1][1][0][0][RTW89_IC][5] = 62, + [1][1][0][0][RTW89_KCC][5] = 58, + [1][1][0][0][RTW89_ACMA][5] = 46, + [1][1][0][0][RTW89_CHILE][5] = 50, + [1][1][0][0][RTW89_UKRAINE][5] = 46, + [1][1][0][0][RTW89_MEXICO][5] = 62, + [1][1][0][0][RTW89_CN][5] = 46, + [1][1][0][0][RTW89_QATAR][5] = 46, + [1][1][0][0][RTW89_UK][5] = 46, + [1][1][0][0][RTW89_FCC][6] = 34, + [1][1][0][0][RTW89_ETSI][6] = 46, + [1][1][0][0][RTW89_MKK][6] = 64, + [1][1][0][0][RTW89_IC][6] = 34, + [1][1][0][0][RTW89_KCC][6] = 58, + [1][1][0][0][RTW89_ACMA][6] = 46, + [1][1][0][0][RTW89_CHILE][6] = 50, + [1][1][0][0][RTW89_UKRAINE][6] = 46, + [1][1][0][0][RTW89_MEXICO][6] = 34, + [1][1][0][0][RTW89_CN][6] = 46, + [1][1][0][0][RTW89_QATAR][6] = 46, + [1][1][0][0][RTW89_UK][6] = 46, + [1][1][0][0][RTW89_FCC][7] = 34, + [1][1][0][0][RTW89_ETSI][7] = 46, + [1][1][0][0][RTW89_MKK][7] = 64, + [1][1][0][0][RTW89_IC][7] = 34, + [1][1][0][0][RTW89_KCC][7] = 58, + [1][1][0][0][RTW89_ACMA][7] = 46, + [1][1][0][0][RTW89_CHILE][7] = 50, + [1][1][0][0][RTW89_UKRAINE][7] = 46, + [1][1][0][0][RTW89_MEXICO][7] = 34, + [1][1][0][0][RTW89_CN][7] = 46, + [1][1][0][0][RTW89_QATAR][7] = 46, + [1][1][0][0][RTW89_UK][7] = 46, + [1][1][0][0][RTW89_FCC][8] = 34, + [1][1][0][0][RTW89_ETSI][8] = 46, + [1][1][0][0][RTW89_MKK][8] = 64, + [1][1][0][0][RTW89_IC][8] = 34, + [1][1][0][0][RTW89_KCC][8] = 58, + [1][1][0][0][RTW89_ACMA][8] = 46, + [1][1][0][0][RTW89_CHILE][8] = 50, + [1][1][0][0][RTW89_UKRAINE][8] = 46, + [1][1][0][0][RTW89_MEXICO][8] = 34, + [1][1][0][0][RTW89_CN][8] = 46, + [1][1][0][0][RTW89_QATAR][8] = 46, + [1][1][0][0][RTW89_UK][8] = 46, + [1][1][0][0][RTW89_FCC][9] = 30, + [1][1][0][0][RTW89_ETSI][9] = 46, + [1][1][0][0][RTW89_MKK][9] = 64, + [1][1][0][0][RTW89_IC][9] = 30, + [1][1][0][0][RTW89_KCC][9] = 58, + [1][1][0][0][RTW89_ACMA][9] = 46, + [1][1][0][0][RTW89_CHILE][9] = 30, + [1][1][0][0][RTW89_UKRAINE][9] = 46, + [1][1][0][0][RTW89_MEXICO][9] = 30, + [1][1][0][0][RTW89_CN][9] = 46, + [1][1][0][0][RTW89_QATAR][9] = 46, + [1][1][0][0][RTW89_UK][9] = 46, + [1][1][0][0][RTW89_FCC][10] = 30, + [1][1][0][0][RTW89_ETSI][10] = 46, + [1][1][0][0][RTW89_MKK][10] = 64, + [1][1][0][0][RTW89_IC][10] = 30, + [1][1][0][0][RTW89_KCC][10] = 58, + [1][1][0][0][RTW89_ACMA][10] = 46, + [1][1][0][0][RTW89_CHILE][10] = 30, + [1][1][0][0][RTW89_UKRAINE][10] = 46, + [1][1][0][0][RTW89_MEXICO][10] = 30, + [1][1][0][0][RTW89_CN][10] = 46, + [1][1][0][0][RTW89_QATAR][10] = 46, + [1][1][0][0][RTW89_UK][10] = 46, + [1][1][0][0][RTW89_FCC][11] = 127, + [1][1][0][0][RTW89_ETSI][11] = 127, + [1][1][0][0][RTW89_MKK][11] = 127, + [1][1][0][0][RTW89_IC][11] = 127, + [1][1][0][0][RTW89_KCC][11] = 127, + [1][1][0][0][RTW89_ACMA][11] = 127, + [1][1][0][0][RTW89_CHILE][11] = 127, + [1][1][0][0][RTW89_UKRAINE][11] = 127, + [1][1][0][0][RTW89_MEXICO][11] = 127, + [1][1][0][0][RTW89_CN][11] = 127, + [1][1][0][0][RTW89_QATAR][11] = 127, + [1][1][0][0][RTW89_UK][11] = 127, + [1][1][0][0][RTW89_FCC][12] = 127, + [1][1][0][0][RTW89_ETSI][12] = 127, + [1][1][0][0][RTW89_MKK][12] = 127, + [1][1][0][0][RTW89_IC][12] = 127, + [1][1][0][0][RTW89_KCC][12] = 127, + [1][1][0][0][RTW89_ACMA][12] = 127, + [1][1][0][0][RTW89_CHILE][12] = 127, + [1][1][0][0][RTW89_UKRAINE][12] = 127, + [1][1][0][0][RTW89_MEXICO][12] = 127, + [1][1][0][0][RTW89_CN][12] = 127, + [1][1][0][0][RTW89_QATAR][12] = 127, + [1][1][0][0][RTW89_UK][12] = 127, + [1][1][0][0][RTW89_FCC][13] = 127, + [1][1][0][0][RTW89_ETSI][13] = 127, + [1][1][0][0][RTW89_MKK][13] = 127, + [1][1][0][0][RTW89_IC][13] = 127, + [1][1][0][0][RTW89_KCC][13] = 127, + [1][1][0][0][RTW89_ACMA][13] = 127, + [1][1][0][0][RTW89_CHILE][13] = 127, + [1][1][0][0][RTW89_UKRAINE][13] = 127, + [1][1][0][0][RTW89_MEXICO][13] = 127, + [1][1][0][0][RTW89_CN][13] = 127, + [1][1][0][0][RTW89_QATAR][13] = 127, + [1][1][0][0][RTW89_UK][13] = 127, + [0][0][1][0][RTW89_FCC][0] = 76, + [0][0][1][0][RTW89_ETSI][0] = 58, + [0][0][1][0][RTW89_MKK][0] = 74, + [0][0][1][0][RTW89_IC][0] = 76, + [0][0][1][0][RTW89_KCC][0] = 76, + [0][0][1][0][RTW89_ACMA][0] = 58, + [0][0][1][0][RTW89_CHILE][0] = 66, + [0][0][1][0][RTW89_UKRAINE][0] = 58, + [0][0][1][0][RTW89_MEXICO][0] = 76, + [0][0][1][0][RTW89_CN][0] = 58, + [0][0][1][0][RTW89_QATAR][0] = 58, + [0][0][1][0][RTW89_UK][0] = 58, + [0][0][1][0][RTW89_FCC][1] = 76, + [0][0][1][0][RTW89_ETSI][1] = 58, + [0][0][1][0][RTW89_MKK][1] = 76, + [0][0][1][0][RTW89_IC][1] = 76, + [0][0][1][0][RTW89_KCC][1] = 76, + [0][0][1][0][RTW89_ACMA][1] = 58, + [0][0][1][0][RTW89_CHILE][1] = 66, + [0][0][1][0][RTW89_UKRAINE][1] = 58, + [0][0][1][0][RTW89_MEXICO][1] = 76, + [0][0][1][0][RTW89_CN][1] = 58, + [0][0][1][0][RTW89_QATAR][1] = 58, + [0][0][1][0][RTW89_UK][1] = 58, + [0][0][1][0][RTW89_FCC][2] = 78, + [0][0][1][0][RTW89_ETSI][2] = 58, + [0][0][1][0][RTW89_MKK][2] = 76, + [0][0][1][0][RTW89_IC][2] = 78, + [0][0][1][0][RTW89_KCC][2] = 76, + [0][0][1][0][RTW89_ACMA][2] = 58, + [0][0][1][0][RTW89_CHILE][2] = 66, + [0][0][1][0][RTW89_UKRAINE][2] = 58, + [0][0][1][0][RTW89_MEXICO][2] = 78, + [0][0][1][0][RTW89_CN][2] = 58, + [0][0][1][0][RTW89_QATAR][2] = 58, + [0][0][1][0][RTW89_UK][2] = 58, + [0][0][1][0][RTW89_FCC][3] = 78, + [0][0][1][0][RTW89_ETSI][3] = 58, + [0][0][1][0][RTW89_MKK][3] = 76, + [0][0][1][0][RTW89_IC][3] = 78, + [0][0][1][0][RTW89_KCC][3] = 76, + [0][0][1][0][RTW89_ACMA][3] = 58, + [0][0][1][0][RTW89_CHILE][3] = 66, + [0][0][1][0][RTW89_UKRAINE][3] = 58, + [0][0][1][0][RTW89_MEXICO][3] = 78, + [0][0][1][0][RTW89_CN][3] = 58, + [0][0][1][0][RTW89_QATAR][3] = 58, + [0][0][1][0][RTW89_UK][3] = 58, + [0][0][1][0][RTW89_FCC][4] = 78, + [0][0][1][0][RTW89_ETSI][4] = 58, + [0][0][1][0][RTW89_MKK][4] = 76, + [0][0][1][0][RTW89_IC][4] = 78, + [0][0][1][0][RTW89_KCC][4] = 76, + [0][0][1][0][RTW89_ACMA][4] = 58, + [0][0][1][0][RTW89_CHILE][4] = 66, + [0][0][1][0][RTW89_UKRAINE][4] = 58, + [0][0][1][0][RTW89_MEXICO][4] = 78, + [0][0][1][0][RTW89_CN][4] = 58, + [0][0][1][0][RTW89_QATAR][4] = 58, + [0][0][1][0][RTW89_UK][4] = 58, + [0][0][1][0][RTW89_FCC][5] = 78, + [0][0][1][0][RTW89_ETSI][5] = 58, + [0][0][1][0][RTW89_MKK][5] = 76, + [0][0][1][0][RTW89_IC][5] = 78, + [0][0][1][0][RTW89_KCC][5] = 76, + [0][0][1][0][RTW89_ACMA][5] = 58, + [0][0][1][0][RTW89_CHILE][5] = 66, + [0][0][1][0][RTW89_UKRAINE][5] = 58, + [0][0][1][0][RTW89_MEXICO][5] = 78, + [0][0][1][0][RTW89_CN][5] = 58, + [0][0][1][0][RTW89_QATAR][5] = 58, + [0][0][1][0][RTW89_UK][5] = 58, + [0][0][1][0][RTW89_FCC][6] = 78, + [0][0][1][0][RTW89_ETSI][6] = 58, + [0][0][1][0][RTW89_MKK][6] = 76, + [0][0][1][0][RTW89_IC][6] = 78, + [0][0][1][0][RTW89_KCC][6] = 76, + [0][0][1][0][RTW89_ACMA][6] = 58, + [0][0][1][0][RTW89_CHILE][6] = 66, + [0][0][1][0][RTW89_UKRAINE][6] = 58, + [0][0][1][0][RTW89_MEXICO][6] = 78, + [0][0][1][0][RTW89_CN][6] = 58, + [0][0][1][0][RTW89_QATAR][6] = 58, + [0][0][1][0][RTW89_UK][6] = 58, + [0][0][1][0][RTW89_FCC][7] = 78, + [0][0][1][0][RTW89_ETSI][7] = 58, + [0][0][1][0][RTW89_MKK][7] = 76, + [0][0][1][0][RTW89_IC][7] = 78, + [0][0][1][0][RTW89_KCC][7] = 76, + [0][0][1][0][RTW89_ACMA][7] = 58, + [0][0][1][0][RTW89_CHILE][7] = 66, + [0][0][1][0][RTW89_UKRAINE][7] = 58, + [0][0][1][0][RTW89_MEXICO][7] = 78, + [0][0][1][0][RTW89_CN][7] = 58, + [0][0][1][0][RTW89_QATAR][7] = 58, + [0][0][1][0][RTW89_UK][7] = 58, + [0][0][1][0][RTW89_FCC][8] = 78, + [0][0][1][0][RTW89_ETSI][8] = 58, + [0][0][1][0][RTW89_MKK][8] = 76, + [0][0][1][0][RTW89_IC][8] = 78, + [0][0][1][0][RTW89_KCC][8] = 76, + [0][0][1][0][RTW89_ACMA][8] = 58, + [0][0][1][0][RTW89_CHILE][8] = 66, + [0][0][1][0][RTW89_UKRAINE][8] = 58, + [0][0][1][0][RTW89_MEXICO][8] = 78, + [0][0][1][0][RTW89_CN][8] = 58, + [0][0][1][0][RTW89_QATAR][8] = 58, + [0][0][1][0][RTW89_UK][8] = 58, + [0][0][1][0][RTW89_FCC][9] = 74, + [0][0][1][0][RTW89_ETSI][9] = 58, + [0][0][1][0][RTW89_MKK][9] = 76, + [0][0][1][0][RTW89_IC][9] = 74, + [0][0][1][0][RTW89_KCC][9] = 76, + [0][0][1][0][RTW89_ACMA][9] = 58, + [0][0][1][0][RTW89_CHILE][9] = 66, + [0][0][1][0][RTW89_UKRAINE][9] = 58, + [0][0][1][0][RTW89_MEXICO][9] = 74, + [0][0][1][0][RTW89_CN][9] = 58, + [0][0][1][0][RTW89_QATAR][9] = 58, + [0][0][1][0][RTW89_UK][9] = 58, + [0][0][1][0][RTW89_FCC][10] = 74, + [0][0][1][0][RTW89_ETSI][10] = 58, + [0][0][1][0][RTW89_MKK][10] = 76, + [0][0][1][0][RTW89_IC][10] = 74, + [0][0][1][0][RTW89_KCC][10] = 76, + [0][0][1][0][RTW89_ACMA][10] = 58, + [0][0][1][0][RTW89_CHILE][10] = 66, + [0][0][1][0][RTW89_UKRAINE][10] = 58, + [0][0][1][0][RTW89_MEXICO][10] = 74, + [0][0][1][0][RTW89_CN][10] = 58, + [0][0][1][0][RTW89_QATAR][10] = 58, + [0][0][1][0][RTW89_UK][10] = 58, + [0][0][1][0][RTW89_FCC][11] = 54, + [0][0][1][0][RTW89_ETSI][11] = 58, + [0][0][1][0][RTW89_MKK][11] = 76, + [0][0][1][0][RTW89_IC][11] = 54, + [0][0][1][0][RTW89_KCC][11] = 76, + [0][0][1][0][RTW89_ACMA][11] = 58, + [0][0][1][0][RTW89_CHILE][11] = 54, + [0][0][1][0][RTW89_UKRAINE][11] = 58, + [0][0][1][0][RTW89_MEXICO][11] = 54, + [0][0][1][0][RTW89_CN][11] = 58, + [0][0][1][0][RTW89_QATAR][11] = 58, + [0][0][1][0][RTW89_UK][11] = 58, + [0][0][1][0][RTW89_FCC][12] = 50, + [0][0][1][0][RTW89_ETSI][12] = 58, + [0][0][1][0][RTW89_MKK][12] = 76, + [0][0][1][0][RTW89_IC][12] = 50, + [0][0][1][0][RTW89_KCC][12] = 76, + [0][0][1][0][RTW89_ACMA][12] = 58, + [0][0][1][0][RTW89_CHILE][12] = 50, + [0][0][1][0][RTW89_UKRAINE][12] = 58, + [0][0][1][0][RTW89_MEXICO][12] = 50, + [0][0][1][0][RTW89_CN][12] = 58, + [0][0][1][0][RTW89_QATAR][12] = 58, + [0][0][1][0][RTW89_UK][12] = 58, + [0][0][1][0][RTW89_FCC][13] = 127, + [0][0][1][0][RTW89_ETSI][13] = 127, + [0][0][1][0][RTW89_MKK][13] = 127, + [0][0][1][0][RTW89_IC][13] = 127, + [0][0][1][0][RTW89_KCC][13] = 127, + [0][0][1][0][RTW89_ACMA][13] = 127, + [0][0][1][0][RTW89_CHILE][13] = 127, + [0][0][1][0][RTW89_UKRAINE][13] = 127, + [0][0][1][0][RTW89_MEXICO][13] = 127, + [0][0][1][0][RTW89_CN][13] = 127, + [0][0][1][0][RTW89_QATAR][13] = 127, + [0][0][1][0][RTW89_UK][13] = 127, + [0][1][1][0][RTW89_FCC][0] = 62, + [0][1][1][0][RTW89_ETSI][0] = 46, + [0][1][1][0][RTW89_MKK][0] = 64, + [0][1][1][0][RTW89_IC][0] = 62, + [0][1][1][0][RTW89_KCC][0] = 66, + [0][1][1][0][RTW89_ACMA][0] = 46, + [0][1][1][0][RTW89_CHILE][0] = 50, + [0][1][1][0][RTW89_UKRAINE][0] = 46, + [0][1][1][0][RTW89_MEXICO][0] = 62, + [0][1][1][0][RTW89_CN][0] = 46, + [0][1][1][0][RTW89_QATAR][0] = 46, + [0][1][1][0][RTW89_UK][0] = 46, + [0][1][1][0][RTW89_FCC][1] = 62, + [0][1][1][0][RTW89_ETSI][1] = 46, + [0][1][1][0][RTW89_MKK][1] = 64, + [0][1][1][0][RTW89_IC][1] = 62, + [0][1][1][0][RTW89_KCC][1] = 66, + [0][1][1][0][RTW89_ACMA][1] = 46, + [0][1][1][0][RTW89_CHILE][1] = 50, + [0][1][1][0][RTW89_UKRAINE][1] = 46, + [0][1][1][0][RTW89_MEXICO][1] = 62, + [0][1][1][0][RTW89_CN][1] = 46, + [0][1][1][0][RTW89_QATAR][1] = 46, + [0][1][1][0][RTW89_UK][1] = 46, + [0][1][1][0][RTW89_FCC][2] = 66, + [0][1][1][0][RTW89_ETSI][2] = 46, + [0][1][1][0][RTW89_MKK][2] = 64, + [0][1][1][0][RTW89_IC][2] = 66, + [0][1][1][0][RTW89_KCC][2] = 66, + [0][1][1][0][RTW89_ACMA][2] = 46, + [0][1][1][0][RTW89_CHILE][2] = 50, + [0][1][1][0][RTW89_UKRAINE][2] = 46, + [0][1][1][0][RTW89_MEXICO][2] = 66, + [0][1][1][0][RTW89_CN][2] = 46, + [0][1][1][0][RTW89_QATAR][2] = 46, + [0][1][1][0][RTW89_UK][2] = 46, + [0][1][1][0][RTW89_FCC][3] = 70, + [0][1][1][0][RTW89_ETSI][3] = 46, + [0][1][1][0][RTW89_MKK][3] = 64, + [0][1][1][0][RTW89_IC][3] = 70, + [0][1][1][0][RTW89_KCC][3] = 66, + [0][1][1][0][RTW89_ACMA][3] = 46, + [0][1][1][0][RTW89_CHILE][3] = 50, + [0][1][1][0][RTW89_UKRAINE][3] = 46, + [0][1][1][0][RTW89_MEXICO][3] = 70, + [0][1][1][0][RTW89_CN][3] = 46, + [0][1][1][0][RTW89_QATAR][3] = 46, + [0][1][1][0][RTW89_UK][3] = 46, + [0][1][1][0][RTW89_FCC][4] = 78, + [0][1][1][0][RTW89_ETSI][4] = 46, + [0][1][1][0][RTW89_MKK][4] = 64, + [0][1][1][0][RTW89_IC][4] = 78, + [0][1][1][0][RTW89_KCC][4] = 64, + [0][1][1][0][RTW89_ACMA][4] = 46, + [0][1][1][0][RTW89_CHILE][4] = 50, + [0][1][1][0][RTW89_UKRAINE][4] = 46, + [0][1][1][0][RTW89_MEXICO][4] = 78, + [0][1][1][0][RTW89_CN][4] = 46, + [0][1][1][0][RTW89_QATAR][4] = 46, + [0][1][1][0][RTW89_UK][4] = 46, + [0][1][1][0][RTW89_FCC][5] = 78, + [0][1][1][0][RTW89_ETSI][5] = 46, + [0][1][1][0][RTW89_MKK][5] = 64, + [0][1][1][0][RTW89_IC][5] = 78, + [0][1][1][0][RTW89_KCC][5] = 64, + [0][1][1][0][RTW89_ACMA][5] = 46, + [0][1][1][0][RTW89_CHILE][5] = 50, + [0][1][1][0][RTW89_UKRAINE][5] = 46, + [0][1][1][0][RTW89_MEXICO][5] = 78, + [0][1][1][0][RTW89_CN][5] = 46, + [0][1][1][0][RTW89_QATAR][5] = 46, + [0][1][1][0][RTW89_UK][5] = 46, + [0][1][1][0][RTW89_FCC][6] = 78, + [0][1][1][0][RTW89_ETSI][6] = 46, + [0][1][1][0][RTW89_MKK][6] = 64, + [0][1][1][0][RTW89_IC][6] = 78, + [0][1][1][0][RTW89_KCC][6] = 64, + [0][1][1][0][RTW89_ACMA][6] = 46, + [0][1][1][0][RTW89_CHILE][6] = 50, + [0][1][1][0][RTW89_UKRAINE][6] = 46, + [0][1][1][0][RTW89_MEXICO][6] = 78, + [0][1][1][0][RTW89_CN][6] = 46, + [0][1][1][0][RTW89_QATAR][6] = 46, + [0][1][1][0][RTW89_UK][6] = 46, + [0][1][1][0][RTW89_FCC][7] = 70, + [0][1][1][0][RTW89_ETSI][7] = 46, + [0][1][1][0][RTW89_MKK][7] = 64, + [0][1][1][0][RTW89_IC][7] = 70, + [0][1][1][0][RTW89_KCC][7] = 64, + [0][1][1][0][RTW89_ACMA][7] = 46, + [0][1][1][0][RTW89_CHILE][7] = 50, + [0][1][1][0][RTW89_UKRAINE][7] = 46, + [0][1][1][0][RTW89_MEXICO][7] = 70, + [0][1][1][0][RTW89_CN][7] = 46, + [0][1][1][0][RTW89_QATAR][7] = 46, + [0][1][1][0][RTW89_UK][7] = 46, + [0][1][1][0][RTW89_FCC][8] = 66, + [0][1][1][0][RTW89_ETSI][8] = 46, + [0][1][1][0][RTW89_MKK][8] = 64, + [0][1][1][0][RTW89_IC][8] = 66, + [0][1][1][0][RTW89_KCC][8] = 64, + [0][1][1][0][RTW89_ACMA][8] = 46, + [0][1][1][0][RTW89_CHILE][8] = 50, + [0][1][1][0][RTW89_UKRAINE][8] = 46, + [0][1][1][0][RTW89_MEXICO][8] = 66, + [0][1][1][0][RTW89_CN][8] = 46, + [0][1][1][0][RTW89_QATAR][8] = 46, + [0][1][1][0][RTW89_UK][8] = 46, + [0][1][1][0][RTW89_FCC][9] = 62, + [0][1][1][0][RTW89_ETSI][9] = 46, + [0][1][1][0][RTW89_MKK][9] = 64, + [0][1][1][0][RTW89_IC][9] = 62, + [0][1][1][0][RTW89_KCC][9] = 64, + [0][1][1][0][RTW89_ACMA][9] = 46, + [0][1][1][0][RTW89_CHILE][9] = 50, + [0][1][1][0][RTW89_UKRAINE][9] = 46, + [0][1][1][0][RTW89_MEXICO][9] = 62, + [0][1][1][0][RTW89_CN][9] = 46, + [0][1][1][0][RTW89_QATAR][9] = 46, + [0][1][1][0][RTW89_UK][9] = 46, + [0][1][1][0][RTW89_FCC][10] = 62, + [0][1][1][0][RTW89_ETSI][10] = 46, + [0][1][1][0][RTW89_MKK][10] = 64, + [0][1][1][0][RTW89_IC][10] = 62, + [0][1][1][0][RTW89_KCC][10] = 64, + [0][1][1][0][RTW89_ACMA][10] = 46, + [0][1][1][0][RTW89_CHILE][10] = 52, + [0][1][1][0][RTW89_UKRAINE][10] = 46, + [0][1][1][0][RTW89_MEXICO][10] = 62, + [0][1][1][0][RTW89_CN][10] = 46, + [0][1][1][0][RTW89_QATAR][10] = 46, + [0][1][1][0][RTW89_UK][10] = 46, + [0][1][1][0][RTW89_FCC][11] = 46, + [0][1][1][0][RTW89_ETSI][11] = 46, + [0][1][1][0][RTW89_MKK][11] = 64, + [0][1][1][0][RTW89_IC][11] = 46, + [0][1][1][0][RTW89_KCC][11] = 64, + [0][1][1][0][RTW89_ACMA][11] = 46, + [0][1][1][0][RTW89_CHILE][11] = 46, + [0][1][1][0][RTW89_UKRAINE][11] = 46, + [0][1][1][0][RTW89_MEXICO][11] = 46, + [0][1][1][0][RTW89_CN][11] = 46, + [0][1][1][0][RTW89_QATAR][11] = 46, + [0][1][1][0][RTW89_UK][11] = 46, + [0][1][1][0][RTW89_FCC][12] = 42, + [0][1][1][0][RTW89_ETSI][12] = 46, + [0][1][1][0][RTW89_MKK][12] = 64, + [0][1][1][0][RTW89_IC][12] = 42, + [0][1][1][0][RTW89_KCC][12] = 64, + [0][1][1][0][RTW89_ACMA][12] = 46, + [0][1][1][0][RTW89_CHILE][12] = 42, + [0][1][1][0][RTW89_UKRAINE][12] = 46, + [0][1][1][0][RTW89_MEXICO][12] = 42, + [0][1][1][0][RTW89_CN][12] = 46, + [0][1][1][0][RTW89_QATAR][12] = 46, + [0][1][1][0][RTW89_UK][12] = 46, + [0][1][1][0][RTW89_FCC][13] = 127, + [0][1][1][0][RTW89_ETSI][13] = 127, + [0][1][1][0][RTW89_MKK][13] = 127, + [0][1][1][0][RTW89_IC][13] = 127, + [0][1][1][0][RTW89_KCC][13] = 127, + [0][1][1][0][RTW89_ACMA][13] = 127, + [0][1][1][0][RTW89_CHILE][13] = 127, + [0][1][1][0][RTW89_UKRAINE][13] = 127, + [0][1][1][0][RTW89_MEXICO][13] = 127, + [0][1][1][0][RTW89_CN][13] = 127, + [0][1][1][0][RTW89_QATAR][13] = 127, + [0][1][1][0][RTW89_UK][13] = 127, + [0][0][2][0][RTW89_FCC][0] = 76, + [0][0][2][0][RTW89_ETSI][0] = 58, + [0][0][2][0][RTW89_MKK][0] = 76, + [0][0][2][0][RTW89_IC][0] = 76, + [0][0][2][0][RTW89_KCC][0] = 76, + [0][0][2][0][RTW89_ACMA][0] = 58, + [0][0][2][0][RTW89_CHILE][0] = 66, + [0][0][2][0][RTW89_UKRAINE][0] = 58, + [0][0][2][0][RTW89_MEXICO][0] = 76, + [0][0][2][0][RTW89_CN][0] = 58, + [0][0][2][0][RTW89_QATAR][0] = 58, + [0][0][2][0][RTW89_UK][0] = 58, + [0][0][2][0][RTW89_FCC][1] = 76, + [0][0][2][0][RTW89_ETSI][1] = 58, + [0][0][2][0][RTW89_MKK][1] = 76, + [0][0][2][0][RTW89_IC][1] = 76, + [0][0][2][0][RTW89_KCC][1] = 76, + [0][0][2][0][RTW89_ACMA][1] = 58, + [0][0][2][0][RTW89_CHILE][1] = 66, + [0][0][2][0][RTW89_UKRAINE][1] = 58, + [0][0][2][0][RTW89_MEXICO][1] = 76, + [0][0][2][0][RTW89_CN][1] = 58, + [0][0][2][0][RTW89_QATAR][1] = 58, + [0][0][2][0][RTW89_UK][1] = 58, + [0][0][2][0][RTW89_FCC][2] = 78, + [0][0][2][0][RTW89_ETSI][2] = 58, + [0][0][2][0][RTW89_MKK][2] = 76, + [0][0][2][0][RTW89_IC][2] = 78, + [0][0][2][0][RTW89_KCC][2] = 76, + [0][0][2][0][RTW89_ACMA][2] = 58, + [0][0][2][0][RTW89_CHILE][2] = 66, + [0][0][2][0][RTW89_UKRAINE][2] = 58, + [0][0][2][0][RTW89_MEXICO][2] = 78, + [0][0][2][0][RTW89_CN][2] = 58, + [0][0][2][0][RTW89_QATAR][2] = 58, + [0][0][2][0][RTW89_UK][2] = 58, + [0][0][2][0][RTW89_FCC][3] = 78, + [0][0][2][0][RTW89_ETSI][3] = 58, + [0][0][2][0][RTW89_MKK][3] = 76, + [0][0][2][0][RTW89_IC][3] = 78, + [0][0][2][0][RTW89_KCC][3] = 76, + [0][0][2][0][RTW89_ACMA][3] = 58, + [0][0][2][0][RTW89_CHILE][3] = 66, + [0][0][2][0][RTW89_UKRAINE][3] = 58, + [0][0][2][0][RTW89_MEXICO][3] = 78, + [0][0][2][0][RTW89_CN][3] = 58, + [0][0][2][0][RTW89_QATAR][3] = 58, + [0][0][2][0][RTW89_UK][3] = 58, + [0][0][2][0][RTW89_FCC][4] = 78, + [0][0][2][0][RTW89_ETSI][4] = 58, + [0][0][2][0][RTW89_MKK][4] = 76, + [0][0][2][0][RTW89_IC][4] = 78, + [0][0][2][0][RTW89_KCC][4] = 76, + [0][0][2][0][RTW89_ACMA][4] = 58, + [0][0][2][0][RTW89_CHILE][4] = 66, + [0][0][2][0][RTW89_UKRAINE][4] = 58, + [0][0][2][0][RTW89_MEXICO][4] = 78, + [0][0][2][0][RTW89_CN][4] = 58, + [0][0][2][0][RTW89_QATAR][4] = 58, + [0][0][2][0][RTW89_UK][4] = 58, + [0][0][2][0][RTW89_FCC][5] = 78, + [0][0][2][0][RTW89_ETSI][5] = 58, + [0][0][2][0][RTW89_MKK][5] = 76, + [0][0][2][0][RTW89_IC][5] = 78, + [0][0][2][0][RTW89_KCC][5] = 76, + [0][0][2][0][RTW89_ACMA][5] = 58, + [0][0][2][0][RTW89_CHILE][5] = 66, + [0][0][2][0][RTW89_UKRAINE][5] = 58, + [0][0][2][0][RTW89_MEXICO][5] = 78, + [0][0][2][0][RTW89_CN][5] = 58, + [0][0][2][0][RTW89_QATAR][5] = 58, + [0][0][2][0][RTW89_UK][5] = 58, + [0][0][2][0][RTW89_FCC][6] = 78, + [0][0][2][0][RTW89_ETSI][6] = 58, + [0][0][2][0][RTW89_MKK][6] = 76, + [0][0][2][0][RTW89_IC][6] = 78, + [0][0][2][0][RTW89_KCC][6] = 76, + [0][0][2][0][RTW89_ACMA][6] = 58, + [0][0][2][0][RTW89_CHILE][6] = 66, + [0][0][2][0][RTW89_UKRAINE][6] = 58, + [0][0][2][0][RTW89_MEXICO][6] = 78, + [0][0][2][0][RTW89_CN][6] = 58, + [0][0][2][0][RTW89_QATAR][6] = 58, + [0][0][2][0][RTW89_UK][6] = 58, + [0][0][2][0][RTW89_FCC][7] = 78, + [0][0][2][0][RTW89_ETSI][7] = 58, + [0][0][2][0][RTW89_MKK][7] = 76, + [0][0][2][0][RTW89_IC][7] = 78, + [0][0][2][0][RTW89_KCC][7] = 76, + [0][0][2][0][RTW89_ACMA][7] = 58, + [0][0][2][0][RTW89_CHILE][7] = 66, + [0][0][2][0][RTW89_UKRAINE][7] = 58, + [0][0][2][0][RTW89_MEXICO][7] = 78, + [0][0][2][0][RTW89_CN][7] = 58, + [0][0][2][0][RTW89_QATAR][7] = 58, + [0][0][2][0][RTW89_UK][7] = 58, + [0][0][2][0][RTW89_FCC][8] = 76, + [0][0][2][0][RTW89_ETSI][8] = 58, + [0][0][2][0][RTW89_MKK][8] = 76, + [0][0][2][0][RTW89_IC][8] = 76, + [0][0][2][0][RTW89_KCC][8] = 76, + [0][0][2][0][RTW89_ACMA][8] = 58, + [0][0][2][0][RTW89_CHILE][8] = 66, + [0][0][2][0][RTW89_UKRAINE][8] = 58, + [0][0][2][0][RTW89_MEXICO][8] = 76, + [0][0][2][0][RTW89_CN][8] = 58, + [0][0][2][0][RTW89_QATAR][8] = 58, + [0][0][2][0][RTW89_UK][8] = 58, + [0][0][2][0][RTW89_FCC][9] = 72, + [0][0][2][0][RTW89_ETSI][9] = 58, + [0][0][2][0][RTW89_MKK][9] = 76, + [0][0][2][0][RTW89_IC][9] = 72, + [0][0][2][0][RTW89_KCC][9] = 76, + [0][0][2][0][RTW89_ACMA][9] = 58, + [0][0][2][0][RTW89_CHILE][9] = 66, + [0][0][2][0][RTW89_UKRAINE][9] = 58, + [0][0][2][0][RTW89_MEXICO][9] = 72, + [0][0][2][0][RTW89_CN][9] = 58, + [0][0][2][0][RTW89_QATAR][9] = 58, + [0][0][2][0][RTW89_UK][9] = 58, + [0][0][2][0][RTW89_FCC][10] = 72, + [0][0][2][0][RTW89_ETSI][10] = 58, + [0][0][2][0][RTW89_MKK][10] = 76, + [0][0][2][0][RTW89_IC][10] = 72, + [0][0][2][0][RTW89_KCC][10] = 76, + [0][0][2][0][RTW89_ACMA][10] = 58, + [0][0][2][0][RTW89_CHILE][10] = 66, + [0][0][2][0][RTW89_UKRAINE][10] = 58, + [0][0][2][0][RTW89_MEXICO][10] = 72, + [0][0][2][0][RTW89_CN][10] = 58, + [0][0][2][0][RTW89_QATAR][10] = 58, + [0][0][2][0][RTW89_UK][10] = 58, + [0][0][2][0][RTW89_FCC][11] = 54, + [0][0][2][0][RTW89_ETSI][11] = 58, + [0][0][2][0][RTW89_MKK][11] = 76, + [0][0][2][0][RTW89_IC][11] = 54, + [0][0][2][0][RTW89_KCC][11] = 76, + [0][0][2][0][RTW89_ACMA][11] = 58, + [0][0][2][0][RTW89_CHILE][11] = 54, + [0][0][2][0][RTW89_UKRAINE][11] = 58, + [0][0][2][0][RTW89_MEXICO][11] = 54, + [0][0][2][0][RTW89_CN][11] = 58, + [0][0][2][0][RTW89_QATAR][11] = 58, + [0][0][2][0][RTW89_UK][11] = 58, + [0][0][2][0][RTW89_FCC][12] = 50, + [0][0][2][0][RTW89_ETSI][12] = 58, + [0][0][2][0][RTW89_MKK][12] = 76, + [0][0][2][0][RTW89_IC][12] = 50, + [0][0][2][0][RTW89_KCC][12] = 76, + [0][0][2][0][RTW89_ACMA][12] = 58, + [0][0][2][0][RTW89_CHILE][12] = 50, + [0][0][2][0][RTW89_UKRAINE][12] = 58, + [0][0][2][0][RTW89_MEXICO][12] = 50, + [0][0][2][0][RTW89_CN][12] = 58, + [0][0][2][0][RTW89_QATAR][12] = 58, + [0][0][2][0][RTW89_UK][12] = 58, + [0][0][2][0][RTW89_FCC][13] = 127, + [0][0][2][0][RTW89_ETSI][13] = 127, + [0][0][2][0][RTW89_MKK][13] = 127, + [0][0][2][0][RTW89_IC][13] = 127, + [0][0][2][0][RTW89_KCC][13] = 127, + [0][0][2][0][RTW89_ACMA][13] = 127, + [0][0][2][0][RTW89_CHILE][13] = 127, + [0][0][2][0][RTW89_UKRAINE][13] = 127, + [0][0][2][0][RTW89_MEXICO][13] = 127, + [0][0][2][0][RTW89_CN][13] = 127, + [0][0][2][0][RTW89_QATAR][13] = 127, + [0][0][2][0][RTW89_UK][13] = 127, + [0][1][2][0][RTW89_FCC][0] = 58, + [0][1][2][0][RTW89_ETSI][0] = 46, + [0][1][2][0][RTW89_MKK][0] = 66, + [0][1][2][0][RTW89_IC][0] = 58, + [0][1][2][0][RTW89_KCC][0] = 62, + [0][1][2][0][RTW89_ACMA][0] = 46, + [0][1][2][0][RTW89_CHILE][0] = 50, + [0][1][2][0][RTW89_UKRAINE][0] = 46, + [0][1][2][0][RTW89_MEXICO][0] = 58, + [0][1][2][0][RTW89_CN][0] = 46, + [0][1][2][0][RTW89_QATAR][0] = 46, + [0][1][2][0][RTW89_UK][0] = 46, + [0][1][2][0][RTW89_FCC][1] = 58, + [0][1][2][0][RTW89_ETSI][1] = 46, + [0][1][2][0][RTW89_MKK][1] = 66, + [0][1][2][0][RTW89_IC][1] = 58, + [0][1][2][0][RTW89_KCC][1] = 62, + [0][1][2][0][RTW89_ACMA][1] = 46, + [0][1][2][0][RTW89_CHILE][1] = 50, + [0][1][2][0][RTW89_UKRAINE][1] = 46, + [0][1][2][0][RTW89_MEXICO][1] = 58, + [0][1][2][0][RTW89_CN][1] = 46, + [0][1][2][0][RTW89_QATAR][1] = 46, + [0][1][2][0][RTW89_UK][1] = 46, + [0][1][2][0][RTW89_FCC][2] = 62, + [0][1][2][0][RTW89_ETSI][2] = 46, + [0][1][2][0][RTW89_MKK][2] = 66, + [0][1][2][0][RTW89_IC][2] = 62, + [0][1][2][0][RTW89_KCC][2] = 62, + [0][1][2][0][RTW89_ACMA][2] = 46, + [0][1][2][0][RTW89_CHILE][2] = 50, + [0][1][2][0][RTW89_UKRAINE][2] = 46, + [0][1][2][0][RTW89_MEXICO][2] = 62, + [0][1][2][0][RTW89_CN][2] = 46, + [0][1][2][0][RTW89_QATAR][2] = 46, + [0][1][2][0][RTW89_UK][2] = 46, + [0][1][2][0][RTW89_FCC][3] = 66, + [0][1][2][0][RTW89_ETSI][3] = 46, + [0][1][2][0][RTW89_MKK][3] = 66, + [0][1][2][0][RTW89_IC][3] = 66, + [0][1][2][0][RTW89_KCC][3] = 62, + [0][1][2][0][RTW89_ACMA][3] = 46, + [0][1][2][0][RTW89_CHILE][3] = 50, + [0][1][2][0][RTW89_UKRAINE][3] = 46, + [0][1][2][0][RTW89_MEXICO][3] = 66, + [0][1][2][0][RTW89_CN][3] = 46, + [0][1][2][0][RTW89_QATAR][3] = 46, + [0][1][2][0][RTW89_UK][3] = 46, + [0][1][2][0][RTW89_FCC][4] = 72, + [0][1][2][0][RTW89_ETSI][4] = 46, + [0][1][2][0][RTW89_MKK][4] = 66, + [0][1][2][0][RTW89_IC][4] = 72, + [0][1][2][0][RTW89_KCC][4] = 62, + [0][1][2][0][RTW89_ACMA][4] = 46, + [0][1][2][0][RTW89_CHILE][4] = 50, + [0][1][2][0][RTW89_UKRAINE][4] = 46, + [0][1][2][0][RTW89_MEXICO][4] = 72, + [0][1][2][0][RTW89_CN][4] = 46, + [0][1][2][0][RTW89_QATAR][4] = 46, + [0][1][2][0][RTW89_UK][4] = 46, + [0][1][2][0][RTW89_FCC][5] = 78, + [0][1][2][0][RTW89_ETSI][5] = 46, + [0][1][2][0][RTW89_MKK][5] = 66, + [0][1][2][0][RTW89_IC][5] = 78, + [0][1][2][0][RTW89_KCC][5] = 62, + [0][1][2][0][RTW89_ACMA][5] = 46, + [0][1][2][0][RTW89_CHILE][5] = 50, + [0][1][2][0][RTW89_UKRAINE][5] = 46, + [0][1][2][0][RTW89_MEXICO][5] = 78, + [0][1][2][0][RTW89_CN][5] = 46, + [0][1][2][0][RTW89_QATAR][5] = 46, + [0][1][2][0][RTW89_UK][5] = 46, + [0][1][2][0][RTW89_FCC][6] = 74, + [0][1][2][0][RTW89_ETSI][6] = 46, + [0][1][2][0][RTW89_MKK][6] = 66, + [0][1][2][0][RTW89_IC][6] = 74, + [0][1][2][0][RTW89_KCC][6] = 62, + [0][1][2][0][RTW89_ACMA][6] = 46, + [0][1][2][0][RTW89_CHILE][6] = 50, + [0][1][2][0][RTW89_UKRAINE][6] = 46, + [0][1][2][0][RTW89_MEXICO][6] = 74, + [0][1][2][0][RTW89_CN][6] = 46, + [0][1][2][0][RTW89_QATAR][6] = 46, + [0][1][2][0][RTW89_UK][6] = 46, + [0][1][2][0][RTW89_FCC][7] = 66, + [0][1][2][0][RTW89_ETSI][7] = 46, + [0][1][2][0][RTW89_MKK][7] = 66, + [0][1][2][0][RTW89_IC][7] = 66, + [0][1][2][0][RTW89_KCC][7] = 62, + [0][1][2][0][RTW89_ACMA][7] = 46, + [0][1][2][0][RTW89_CHILE][7] = 50, + [0][1][2][0][RTW89_UKRAINE][7] = 46, + [0][1][2][0][RTW89_MEXICO][7] = 66, + [0][1][2][0][RTW89_CN][7] = 46, + [0][1][2][0][RTW89_QATAR][7] = 46, + [0][1][2][0][RTW89_UK][7] = 46, + [0][1][2][0][RTW89_FCC][8] = 62, + [0][1][2][0][RTW89_ETSI][8] = 46, + [0][1][2][0][RTW89_MKK][8] = 66, + [0][1][2][0][RTW89_IC][8] = 62, + [0][1][2][0][RTW89_KCC][8] = 62, + [0][1][2][0][RTW89_ACMA][8] = 46, + [0][1][2][0][RTW89_CHILE][8] = 50, + [0][1][2][0][RTW89_UKRAINE][8] = 46, + [0][1][2][0][RTW89_MEXICO][8] = 62, + [0][1][2][0][RTW89_CN][8] = 46, + [0][1][2][0][RTW89_QATAR][8] = 46, + [0][1][2][0][RTW89_UK][8] = 46, + [0][1][2][0][RTW89_FCC][9] = 58, + [0][1][2][0][RTW89_ETSI][9] = 46, + [0][1][2][0][RTW89_MKK][9] = 66, + [0][1][2][0][RTW89_IC][9] = 58, + [0][1][2][0][RTW89_KCC][9] = 60, + [0][1][2][0][RTW89_ACMA][9] = 46, + [0][1][2][0][RTW89_CHILE][9] = 50, + [0][1][2][0][RTW89_UKRAINE][9] = 46, + [0][1][2][0][RTW89_MEXICO][9] = 58, + [0][1][2][0][RTW89_CN][9] = 46, + [0][1][2][0][RTW89_QATAR][9] = 46, + [0][1][2][0][RTW89_UK][9] = 46, + [0][1][2][0][RTW89_FCC][10] = 58, + [0][1][2][0][RTW89_ETSI][10] = 46, + [0][1][2][0][RTW89_MKK][10] = 66, + [0][1][2][0][RTW89_IC][10] = 58, + [0][1][2][0][RTW89_KCC][10] = 60, + [0][1][2][0][RTW89_ACMA][10] = 46, + [0][1][2][0][RTW89_CHILE][10] = 50, + [0][1][2][0][RTW89_UKRAINE][10] = 46, + [0][1][2][0][RTW89_MEXICO][10] = 58, + [0][1][2][0][RTW89_CN][10] = 46, + [0][1][2][0][RTW89_QATAR][10] = 46, + [0][1][2][0][RTW89_UK][10] = 46, + [0][1][2][0][RTW89_FCC][11] = 46, + [0][1][2][0][RTW89_ETSI][11] = 46, + [0][1][2][0][RTW89_MKK][11] = 66, + [0][1][2][0][RTW89_IC][11] = 46, + [0][1][2][0][RTW89_KCC][11] = 60, + [0][1][2][0][RTW89_ACMA][11] = 46, + [0][1][2][0][RTW89_CHILE][11] = 46, + [0][1][2][0][RTW89_UKRAINE][11] = 46, + [0][1][2][0][RTW89_MEXICO][11] = 46, + [0][1][2][0][RTW89_CN][11] = 46, + [0][1][2][0][RTW89_QATAR][11] = 46, + [0][1][2][0][RTW89_UK][11] = 46, + [0][1][2][0][RTW89_FCC][12] = 42, + [0][1][2][0][RTW89_ETSI][12] = 46, + [0][1][2][0][RTW89_MKK][12] = 66, + [0][1][2][0][RTW89_IC][12] = 42, + [0][1][2][0][RTW89_KCC][12] = 60, + [0][1][2][0][RTW89_ACMA][12] = 46, + [0][1][2][0][RTW89_CHILE][12] = 42, + [0][1][2][0][RTW89_UKRAINE][12] = 46, + [0][1][2][0][RTW89_MEXICO][12] = 42, + [0][1][2][0][RTW89_CN][12] = 46, + [0][1][2][0][RTW89_QATAR][12] = 46, + [0][1][2][0][RTW89_UK][12] = 46, + [0][1][2][0][RTW89_FCC][13] = 127, + [0][1][2][0][RTW89_ETSI][13] = 127, + [0][1][2][0][RTW89_MKK][13] = 127, + [0][1][2][0][RTW89_IC][13] = 127, + [0][1][2][0][RTW89_KCC][13] = 127, + [0][1][2][0][RTW89_ACMA][13] = 127, + [0][1][2][0][RTW89_CHILE][13] = 127, + [0][1][2][0][RTW89_UKRAINE][13] = 127, + [0][1][2][0][RTW89_MEXICO][13] = 127, + [0][1][2][0][RTW89_CN][13] = 127, + [0][1][2][0][RTW89_QATAR][13] = 127, + [0][1][2][0][RTW89_UK][13] = 127, + [0][1][2][1][RTW89_FCC][0] = 58, + [0][1][2][1][RTW89_ETSI][0] = 34, + [0][1][2][1][RTW89_MKK][0] = 66, + [0][1][2][1][RTW89_IC][0] = 58, + [0][1][2][1][RTW89_KCC][0] = 62, + [0][1][2][1][RTW89_ACMA][0] = 34, + [0][1][2][1][RTW89_CHILE][0] = 42, + [0][1][2][1][RTW89_UKRAINE][0] = 34, + [0][1][2][1][RTW89_MEXICO][0] = 58, + [0][1][2][1][RTW89_CN][0] = 34, + [0][1][2][1][RTW89_QATAR][0] = 34, + [0][1][2][1][RTW89_UK][0] = 34, + [0][1][2][1][RTW89_FCC][1] = 58, + [0][1][2][1][RTW89_ETSI][1] = 34, + [0][1][2][1][RTW89_MKK][1] = 66, + [0][1][2][1][RTW89_IC][1] = 58, + [0][1][2][1][RTW89_KCC][1] = 62, + [0][1][2][1][RTW89_ACMA][1] = 34, + [0][1][2][1][RTW89_CHILE][1] = 40, + [0][1][2][1][RTW89_UKRAINE][1] = 34, + [0][1][2][1][RTW89_MEXICO][1] = 58, + [0][1][2][1][RTW89_CN][1] = 34, + [0][1][2][1][RTW89_QATAR][1] = 34, + [0][1][2][1][RTW89_UK][1] = 34, + [0][1][2][1][RTW89_FCC][2] = 62, + [0][1][2][1][RTW89_ETSI][2] = 34, + [0][1][2][1][RTW89_MKK][2] = 66, + [0][1][2][1][RTW89_IC][2] = 62, + [0][1][2][1][RTW89_KCC][2] = 62, + [0][1][2][1][RTW89_ACMA][2] = 34, + [0][1][2][1][RTW89_CHILE][2] = 40, + [0][1][2][1][RTW89_UKRAINE][2] = 34, + [0][1][2][1][RTW89_MEXICO][2] = 62, + [0][1][2][1][RTW89_CN][2] = 34, + [0][1][2][1][RTW89_QATAR][2] = 34, + [0][1][2][1][RTW89_UK][2] = 34, + [0][1][2][1][RTW89_FCC][3] = 66, + [0][1][2][1][RTW89_ETSI][3] = 34, + [0][1][2][1][RTW89_MKK][3] = 66, + [0][1][2][1][RTW89_IC][3] = 66, + [0][1][2][1][RTW89_KCC][3] = 62, + [0][1][2][1][RTW89_ACMA][3] = 34, + [0][1][2][1][RTW89_CHILE][3] = 40, + [0][1][2][1][RTW89_UKRAINE][3] = 34, + [0][1][2][1][RTW89_MEXICO][3] = 66, + [0][1][2][1][RTW89_CN][3] = 34, + [0][1][2][1][RTW89_QATAR][3] = 34, + [0][1][2][1][RTW89_UK][3] = 34, + [0][1][2][1][RTW89_FCC][4] = 72, + [0][1][2][1][RTW89_ETSI][4] = 34, + [0][1][2][1][RTW89_MKK][4] = 66, + [0][1][2][1][RTW89_IC][4] = 72, + [0][1][2][1][RTW89_KCC][4] = 62, + [0][1][2][1][RTW89_ACMA][4] = 34, + [0][1][2][1][RTW89_CHILE][4] = 40, + [0][1][2][1][RTW89_UKRAINE][4] = 34, + [0][1][2][1][RTW89_MEXICO][4] = 72, + [0][1][2][1][RTW89_CN][4] = 34, + [0][1][2][1][RTW89_QATAR][4] = 34, + [0][1][2][1][RTW89_UK][4] = 34, + [0][1][2][1][RTW89_FCC][5] = 78, + [0][1][2][1][RTW89_ETSI][5] = 34, + [0][1][2][1][RTW89_MKK][5] = 66, + [0][1][2][1][RTW89_IC][5] = 78, + [0][1][2][1][RTW89_KCC][5] = 62, + [0][1][2][1][RTW89_ACMA][5] = 34, + [0][1][2][1][RTW89_CHILE][5] = 42, + [0][1][2][1][RTW89_UKRAINE][5] = 34, + [0][1][2][1][RTW89_MEXICO][5] = 78, + [0][1][2][1][RTW89_CN][5] = 34, + [0][1][2][1][RTW89_QATAR][5] = 34, + [0][1][2][1][RTW89_UK][5] = 34, + [0][1][2][1][RTW89_FCC][6] = 74, + [0][1][2][1][RTW89_ETSI][6] = 34, + [0][1][2][1][RTW89_MKK][6] = 66, + [0][1][2][1][RTW89_IC][6] = 74, + [0][1][2][1][RTW89_KCC][6] = 62, + [0][1][2][1][RTW89_ACMA][6] = 34, + [0][1][2][1][RTW89_CHILE][6] = 40, + [0][1][2][1][RTW89_UKRAINE][6] = 34, + [0][1][2][1][RTW89_MEXICO][6] = 74, + [0][1][2][1][RTW89_CN][6] = 34, + [0][1][2][1][RTW89_QATAR][6] = 34, + [0][1][2][1][RTW89_UK][6] = 34, + [0][1][2][1][RTW89_FCC][7] = 66, + [0][1][2][1][RTW89_ETSI][7] = 34, + [0][1][2][1][RTW89_MKK][7] = 66, + [0][1][2][1][RTW89_IC][7] = 66, + [0][1][2][1][RTW89_KCC][7] = 62, + [0][1][2][1][RTW89_ACMA][7] = 34, + [0][1][2][1][RTW89_CHILE][7] = 40, + [0][1][2][1][RTW89_UKRAINE][7] = 34, + [0][1][2][1][RTW89_MEXICO][7] = 66, + [0][1][2][1][RTW89_CN][7] = 34, + [0][1][2][1][RTW89_QATAR][7] = 34, + [0][1][2][1][RTW89_UK][7] = 34, + [0][1][2][1][RTW89_FCC][8] = 62, + [0][1][2][1][RTW89_ETSI][8] = 34, + [0][1][2][1][RTW89_MKK][8] = 66, + [0][1][2][1][RTW89_IC][8] = 62, + [0][1][2][1][RTW89_KCC][8] = 62, + [0][1][2][1][RTW89_ACMA][8] = 34, + [0][1][2][1][RTW89_CHILE][8] = 40, + [0][1][2][1][RTW89_UKRAINE][8] = 34, + [0][1][2][1][RTW89_MEXICO][8] = 62, + [0][1][2][1][RTW89_CN][8] = 34, + [0][1][2][1][RTW89_QATAR][8] = 34, + [0][1][2][1][RTW89_UK][8] = 34, + [0][1][2][1][RTW89_FCC][9] = 58, + [0][1][2][1][RTW89_ETSI][9] = 34, + [0][1][2][1][RTW89_MKK][9] = 66, + [0][1][2][1][RTW89_IC][9] = 58, + [0][1][2][1][RTW89_KCC][9] = 60, + [0][1][2][1][RTW89_ACMA][9] = 34, + [0][1][2][1][RTW89_CHILE][9] = 40, + [0][1][2][1][RTW89_UKRAINE][9] = 34, + [0][1][2][1][RTW89_MEXICO][9] = 58, + [0][1][2][1][RTW89_CN][9] = 34, + [0][1][2][1][RTW89_QATAR][9] = 34, + [0][1][2][1][RTW89_UK][9] = 34, + [0][1][2][1][RTW89_FCC][10] = 58, + [0][1][2][1][RTW89_ETSI][10] = 34, + [0][1][2][1][RTW89_MKK][10] = 66, + [0][1][2][1][RTW89_IC][10] = 58, + [0][1][2][1][RTW89_KCC][10] = 60, + [0][1][2][1][RTW89_ACMA][10] = 34, + [0][1][2][1][RTW89_CHILE][10] = 40, + [0][1][2][1][RTW89_UKRAINE][10] = 34, + [0][1][2][1][RTW89_MEXICO][10] = 58, + [0][1][2][1][RTW89_CN][10] = 34, + [0][1][2][1][RTW89_QATAR][10] = 34, + [0][1][2][1][RTW89_UK][10] = 34, + [0][1][2][1][RTW89_FCC][11] = 46, + [0][1][2][1][RTW89_ETSI][11] = 34, + [0][1][2][1][RTW89_MKK][11] = 66, + [0][1][2][1][RTW89_IC][11] = 46, + [0][1][2][1][RTW89_KCC][11] = 60, + [0][1][2][1][RTW89_ACMA][11] = 34, + [0][1][2][1][RTW89_CHILE][11] = 40, + [0][1][2][1][RTW89_UKRAINE][11] = 34, + [0][1][2][1][RTW89_MEXICO][11] = 46, + [0][1][2][1][RTW89_CN][11] = 34, + [0][1][2][1][RTW89_QATAR][11] = 34, + [0][1][2][1][RTW89_UK][11] = 34, + [0][1][2][1][RTW89_FCC][12] = 42, + [0][1][2][1][RTW89_ETSI][12] = 34, + [0][1][2][1][RTW89_MKK][12] = 66, + [0][1][2][1][RTW89_IC][12] = 42, + [0][1][2][1][RTW89_KCC][12] = 60, + [0][1][2][1][RTW89_ACMA][12] = 34, + [0][1][2][1][RTW89_CHILE][12] = 40, + [0][1][2][1][RTW89_UKRAINE][12] = 34, + [0][1][2][1][RTW89_MEXICO][12] = 42, + [0][1][2][1][RTW89_CN][12] = 34, + [0][1][2][1][RTW89_QATAR][12] = 34, + [0][1][2][1][RTW89_UK][12] = 34, + [0][1][2][1][RTW89_FCC][13] = 127, + [0][1][2][1][RTW89_ETSI][13] = 127, + [0][1][2][1][RTW89_MKK][13] = 127, + [0][1][2][1][RTW89_IC][13] = 127, + [0][1][2][1][RTW89_KCC][13] = 127, + [0][1][2][1][RTW89_ACMA][13] = 127, + [0][1][2][1][RTW89_CHILE][13] = 127, + [0][1][2][1][RTW89_UKRAINE][13] = 127, + [0][1][2][1][RTW89_MEXICO][13] = 127, + [0][1][2][1][RTW89_CN][13] = 127, + [0][1][2][1][RTW89_QATAR][13] = 127, + [0][1][2][1][RTW89_UK][13] = 127, + [1][0][2][0][RTW89_FCC][0] = 127, + [1][0][2][0][RTW89_ETSI][0] = 127, + [1][0][2][0][RTW89_MKK][0] = 127, + [1][0][2][0][RTW89_IC][0] = 127, + [1][0][2][0][RTW89_KCC][0] = 127, + [1][0][2][0][RTW89_ACMA][0] = 127, + [1][0][2][0][RTW89_CHILE][0] = 127, + [1][0][2][0][RTW89_UKRAINE][0] = 127, + [1][0][2][0][RTW89_MEXICO][0] = 127, + [1][0][2][0][RTW89_CN][0] = 127, + [1][0][2][0][RTW89_QATAR][0] = 127, + [1][0][2][0][RTW89_UK][0] = 127, + [1][0][2][0][RTW89_FCC][1] = 127, + [1][0][2][0][RTW89_ETSI][1] = 127, + [1][0][2][0][RTW89_MKK][1] = 127, + [1][0][2][0][RTW89_IC][1] = 127, + [1][0][2][0][RTW89_KCC][1] = 127, + [1][0][2][0][RTW89_ACMA][1] = 127, + [1][0][2][0][RTW89_CHILE][1] = 127, + [1][0][2][0][RTW89_UKRAINE][1] = 127, + [1][0][2][0][RTW89_MEXICO][1] = 127, + [1][0][2][0][RTW89_CN][1] = 127, + [1][0][2][0][RTW89_QATAR][1] = 127, + [1][0][2][0][RTW89_UK][1] = 127, + [1][0][2][0][RTW89_FCC][2] = 70, + [1][0][2][0][RTW89_ETSI][2] = 58, + [1][0][2][0][RTW89_MKK][2] = 74, + [1][0][2][0][RTW89_IC][2] = 70, + [1][0][2][0][RTW89_KCC][2] = 74, + [1][0][2][0][RTW89_ACMA][2] = 58, + [1][0][2][0][RTW89_CHILE][2] = 66, + [1][0][2][0][RTW89_UKRAINE][2] = 58, + [1][0][2][0][RTW89_MEXICO][2] = 70, + [1][0][2][0][RTW89_CN][2] = 58, + [1][0][2][0][RTW89_QATAR][2] = 58, + [1][0][2][0][RTW89_UK][2] = 58, + [1][0][2][0][RTW89_FCC][3] = 70, + [1][0][2][0][RTW89_ETSI][3] = 58, + [1][0][2][0][RTW89_MKK][3] = 74, + [1][0][2][0][RTW89_IC][3] = 70, + [1][0][2][0][RTW89_KCC][3] = 74, + [1][0][2][0][RTW89_ACMA][3] = 58, + [1][0][2][0][RTW89_CHILE][3] = 66, + [1][0][2][0][RTW89_UKRAINE][3] = 58, + [1][0][2][0][RTW89_MEXICO][3] = 70, + [1][0][2][0][RTW89_CN][3] = 58, + [1][0][2][0][RTW89_QATAR][3] = 58, + [1][0][2][0][RTW89_UK][3] = 58, + [1][0][2][0][RTW89_FCC][4] = 72, + [1][0][2][0][RTW89_ETSI][4] = 58, + [1][0][2][0][RTW89_MKK][4] = 74, + [1][0][2][0][RTW89_IC][4] = 72, + [1][0][2][0][RTW89_KCC][4] = 74, + [1][0][2][0][RTW89_ACMA][4] = 58, + [1][0][2][0][RTW89_CHILE][4] = 66, + [1][0][2][0][RTW89_UKRAINE][4] = 58, + [1][0][2][0][RTW89_MEXICO][4] = 72, + [1][0][2][0][RTW89_CN][4] = 58, + [1][0][2][0][RTW89_QATAR][4] = 58, + [1][0][2][0][RTW89_UK][4] = 58, + [1][0][2][0][RTW89_FCC][5] = 72, + [1][0][2][0][RTW89_ETSI][5] = 58, + [1][0][2][0][RTW89_MKK][5] = 74, + [1][0][2][0][RTW89_IC][5] = 72, + [1][0][2][0][RTW89_KCC][5] = 74, + [1][0][2][0][RTW89_ACMA][5] = 58, + [1][0][2][0][RTW89_CHILE][5] = 66, + [1][0][2][0][RTW89_UKRAINE][5] = 58, + [1][0][2][0][RTW89_MEXICO][5] = 72, + [1][0][2][0][RTW89_CN][5] = 58, + [1][0][2][0][RTW89_QATAR][5] = 58, + [1][0][2][0][RTW89_UK][5] = 58, + [1][0][2][0][RTW89_FCC][6] = 72, + [1][0][2][0][RTW89_ETSI][6] = 58, + [1][0][2][0][RTW89_MKK][6] = 74, + [1][0][2][0][RTW89_IC][6] = 72, + [1][0][2][0][RTW89_KCC][6] = 74, + [1][0][2][0][RTW89_ACMA][6] = 58, + [1][0][2][0][RTW89_CHILE][6] = 66, + [1][0][2][0][RTW89_UKRAINE][6] = 58, + [1][0][2][0][RTW89_MEXICO][6] = 72, + [1][0][2][0][RTW89_CN][6] = 58, + [1][0][2][0][RTW89_QATAR][6] = 58, + [1][0][2][0][RTW89_UK][6] = 58, + [1][0][2][0][RTW89_FCC][7] = 68, + [1][0][2][0][RTW89_ETSI][7] = 58, + [1][0][2][0][RTW89_MKK][7] = 74, + [1][0][2][0][RTW89_IC][7] = 68, + [1][0][2][0][RTW89_KCC][7] = 74, + [1][0][2][0][RTW89_ACMA][7] = 58, + [1][0][2][0][RTW89_CHILE][7] = 66, + [1][0][2][0][RTW89_UKRAINE][7] = 58, + [1][0][2][0][RTW89_MEXICO][7] = 68, + [1][0][2][0][RTW89_CN][7] = 58, + [1][0][2][0][RTW89_QATAR][7] = 58, + [1][0][2][0][RTW89_UK][7] = 58, + [1][0][2][0][RTW89_FCC][8] = 68, + [1][0][2][0][RTW89_ETSI][8] = 58, + [1][0][2][0][RTW89_MKK][8] = 74, + [1][0][2][0][RTW89_IC][8] = 68, + [1][0][2][0][RTW89_KCC][8] = 74, + [1][0][2][0][RTW89_ACMA][8] = 58, + [1][0][2][0][RTW89_CHILE][8] = 66, + [1][0][2][0][RTW89_UKRAINE][8] = 58, + [1][0][2][0][RTW89_MEXICO][8] = 68, + [1][0][2][0][RTW89_CN][8] = 58, + [1][0][2][0][RTW89_QATAR][8] = 58, + [1][0][2][0][RTW89_UK][8] = 58, + [1][0][2][0][RTW89_FCC][9] = 68, + [1][0][2][0][RTW89_ETSI][9] = 58, + [1][0][2][0][RTW89_MKK][9] = 74, + [1][0][2][0][RTW89_IC][9] = 68, + [1][0][2][0][RTW89_KCC][9] = 74, + [1][0][2][0][RTW89_ACMA][9] = 58, + [1][0][2][0][RTW89_CHILE][9] = 66, + [1][0][2][0][RTW89_UKRAINE][9] = 58, + [1][0][2][0][RTW89_MEXICO][9] = 68, + [1][0][2][0][RTW89_CN][9] = 58, + [1][0][2][0][RTW89_QATAR][9] = 58, + [1][0][2][0][RTW89_UK][9] = 58, + [1][0][2][0][RTW89_FCC][10] = 66, + [1][0][2][0][RTW89_ETSI][10] = 58, + [1][0][2][0][RTW89_MKK][10] = 74, + [1][0][2][0][RTW89_IC][10] = 66, + [1][0][2][0][RTW89_KCC][10] = 74, + [1][0][2][0][RTW89_ACMA][10] = 58, + [1][0][2][0][RTW89_CHILE][10] = 66, + [1][0][2][0][RTW89_UKRAINE][10] = 58, + [1][0][2][0][RTW89_MEXICO][10] = 66, + [1][0][2][0][RTW89_CN][10] = 58, + [1][0][2][0][RTW89_QATAR][10] = 58, + [1][0][2][0][RTW89_UK][10] = 58, + [1][0][2][0][RTW89_FCC][11] = 127, + [1][0][2][0][RTW89_ETSI][11] = 127, + [1][0][2][0][RTW89_MKK][11] = 127, + [1][0][2][0][RTW89_IC][11] = 127, + [1][0][2][0][RTW89_KCC][11] = 127, + [1][0][2][0][RTW89_ACMA][11] = 127, + [1][0][2][0][RTW89_CHILE][11] = 127, + [1][0][2][0][RTW89_UKRAINE][11] = 127, + [1][0][2][0][RTW89_MEXICO][11] = 127, + [1][0][2][0][RTW89_CN][11] = 127, + [1][0][2][0][RTW89_QATAR][11] = 127, + [1][0][2][0][RTW89_UK][11] = 127, + [1][0][2][0][RTW89_FCC][12] = 127, + [1][0][2][0][RTW89_ETSI][12] = 127, + [1][0][2][0][RTW89_MKK][12] = 127, + [1][0][2][0][RTW89_IC][12] = 127, + [1][0][2][0][RTW89_KCC][12] = 127, + [1][0][2][0][RTW89_ACMA][12] = 127, + [1][0][2][0][RTW89_CHILE][12] = 127, + [1][0][2][0][RTW89_UKRAINE][12] = 127, + [1][0][2][0][RTW89_MEXICO][12] = 127, + [1][0][2][0][RTW89_CN][12] = 127, + [1][0][2][0][RTW89_QATAR][12] = 127, + [1][0][2][0][RTW89_UK][12] = 127, + [1][0][2][0][RTW89_FCC][13] = 127, + [1][0][2][0][RTW89_ETSI][13] = 127, + [1][0][2][0][RTW89_MKK][13] = 127, + [1][0][2][0][RTW89_IC][13] = 127, + [1][0][2][0][RTW89_KCC][13] = 127, + [1][0][2][0][RTW89_ACMA][13] = 127, + [1][0][2][0][RTW89_CHILE][13] = 127, + [1][0][2][0][RTW89_UKRAINE][13] = 127, + [1][0][2][0][RTW89_MEXICO][13] = 127, + [1][0][2][0][RTW89_CN][13] = 127, + [1][0][2][0][RTW89_QATAR][13] = 127, + [1][0][2][0][RTW89_UK][13] = 127, + [1][1][2][0][RTW89_FCC][0] = 127, + [1][1][2][0][RTW89_ETSI][0] = 127, + [1][1][2][0][RTW89_MKK][0] = 127, + [1][1][2][0][RTW89_IC][0] = 127, + [1][1][2][0][RTW89_KCC][0] = 127, + [1][1][2][0][RTW89_ACMA][0] = 127, + [1][1][2][0][RTW89_CHILE][0] = 127, + [1][1][2][0][RTW89_UKRAINE][0] = 127, + [1][1][2][0][RTW89_MEXICO][0] = 127, + [1][1][2][0][RTW89_CN][0] = 127, + [1][1][2][0][RTW89_QATAR][0] = 127, + [1][1][2][0][RTW89_UK][0] = 127, + [1][1][2][0][RTW89_FCC][1] = 127, + [1][1][2][0][RTW89_ETSI][1] = 127, + [1][1][2][0][RTW89_MKK][1] = 127, + [1][1][2][0][RTW89_IC][1] = 127, + [1][1][2][0][RTW89_KCC][1] = 127, + [1][1][2][0][RTW89_ACMA][1] = 127, + [1][1][2][0][RTW89_CHILE][1] = 127, + [1][1][2][0][RTW89_UKRAINE][1] = 127, + [1][1][2][0][RTW89_MEXICO][1] = 127, + [1][1][2][0][RTW89_CN][1] = 127, + [1][1][2][0][RTW89_QATAR][1] = 127, + [1][1][2][0][RTW89_UK][1] = 127, + [1][1][2][0][RTW89_FCC][2] = 54, + [1][1][2][0][RTW89_ETSI][2] = 46, + [1][1][2][0][RTW89_MKK][2] = 66, + [1][1][2][0][RTW89_IC][2] = 54, + [1][1][2][0][RTW89_KCC][2] = 62, + [1][1][2][0][RTW89_ACMA][2] = 46, + [1][1][2][0][RTW89_CHILE][2] = 52, + [1][1][2][0][RTW89_UKRAINE][2] = 46, + [1][1][2][0][RTW89_MEXICO][2] = 54, + [1][1][2][0][RTW89_CN][2] = 46, + [1][1][2][0][RTW89_QATAR][2] = 46, + [1][1][2][0][RTW89_UK][2] = 46, + [1][1][2][0][RTW89_FCC][3] = 54, + [1][1][2][0][RTW89_ETSI][3] = 46, + [1][1][2][0][RTW89_MKK][3] = 66, + [1][1][2][0][RTW89_IC][3] = 54, + [1][1][2][0][RTW89_KCC][3] = 62, + [1][1][2][0][RTW89_ACMA][3] = 46, + [1][1][2][0][RTW89_CHILE][3] = 52, + [1][1][2][0][RTW89_UKRAINE][3] = 46, + [1][1][2][0][RTW89_MEXICO][3] = 54, + [1][1][2][0][RTW89_CN][3] = 46, + [1][1][2][0][RTW89_QATAR][3] = 46, + [1][1][2][0][RTW89_UK][3] = 46, + [1][1][2][0][RTW89_FCC][4] = 58, + [1][1][2][0][RTW89_ETSI][4] = 46, + [1][1][2][0][RTW89_MKK][4] = 66, + [1][1][2][0][RTW89_IC][4] = 58, + [1][1][2][0][RTW89_KCC][4] = 62, + [1][1][2][0][RTW89_ACMA][4] = 46, + [1][1][2][0][RTW89_CHILE][4] = 52, + [1][1][2][0][RTW89_UKRAINE][4] = 46, + [1][1][2][0][RTW89_MEXICO][4] = 58, + [1][1][2][0][RTW89_CN][4] = 46, + [1][1][2][0][RTW89_QATAR][4] = 46, + [1][1][2][0][RTW89_UK][4] = 46, + [1][1][2][0][RTW89_FCC][5] = 66, + [1][1][2][0][RTW89_ETSI][5] = 46, + [1][1][2][0][RTW89_MKK][5] = 66, + [1][1][2][0][RTW89_IC][5] = 66, + [1][1][2][0][RTW89_KCC][5] = 62, + [1][1][2][0][RTW89_ACMA][5] = 46, + [1][1][2][0][RTW89_CHILE][5] = 54, + [1][1][2][0][RTW89_UKRAINE][5] = 46, + [1][1][2][0][RTW89_MEXICO][5] = 66, + [1][1][2][0][RTW89_CN][5] = 46, + [1][1][2][0][RTW89_QATAR][5] = 46, + [1][1][2][0][RTW89_UK][5] = 46, + [1][1][2][0][RTW89_FCC][6] = 58, + [1][1][2][0][RTW89_ETSI][6] = 46, + [1][1][2][0][RTW89_MKK][6] = 66, + [1][1][2][0][RTW89_IC][6] = 58, + [1][1][2][0][RTW89_KCC][6] = 62, + [1][1][2][0][RTW89_ACMA][6] = 46, + [1][1][2][0][RTW89_CHILE][6] = 52, + [1][1][2][0][RTW89_UKRAINE][6] = 46, + [1][1][2][0][RTW89_MEXICO][6] = 58, + [1][1][2][0][RTW89_CN][6] = 46, + [1][1][2][0][RTW89_QATAR][6] = 46, + [1][1][2][0][RTW89_UK][6] = 46, + [1][1][2][0][RTW89_FCC][7] = 54, + [1][1][2][0][RTW89_ETSI][7] = 46, + [1][1][2][0][RTW89_MKK][7] = 66, + [1][1][2][0][RTW89_IC][7] = 54, + [1][1][2][0][RTW89_KCC][7] = 62, + [1][1][2][0][RTW89_ACMA][7] = 46, + [1][1][2][0][RTW89_CHILE][7] = 52, + [1][1][2][0][RTW89_UKRAINE][7] = 46, + [1][1][2][0][RTW89_MEXICO][7] = 54, + [1][1][2][0][RTW89_CN][7] = 46, + [1][1][2][0][RTW89_QATAR][7] = 46, + [1][1][2][0][RTW89_UK][7] = 46, + [1][1][2][0][RTW89_FCC][8] = 54, + [1][1][2][0][RTW89_ETSI][8] = 46, + [1][1][2][0][RTW89_MKK][8] = 66, + [1][1][2][0][RTW89_IC][8] = 54, + [1][1][2][0][RTW89_KCC][8] = 62, + [1][1][2][0][RTW89_ACMA][8] = 46, + [1][1][2][0][RTW89_CHILE][8] = 52, + [1][1][2][0][RTW89_UKRAINE][8] = 46, + [1][1][2][0][RTW89_MEXICO][8] = 54, + [1][1][2][0][RTW89_CN][8] = 46, + [1][1][2][0][RTW89_QATAR][8] = 46, + [1][1][2][0][RTW89_UK][8] = 46, + [1][1][2][0][RTW89_FCC][9] = 42, + [1][1][2][0][RTW89_ETSI][9] = 46, + [1][1][2][0][RTW89_MKK][9] = 66, + [1][1][2][0][RTW89_IC][9] = 42, + [1][1][2][0][RTW89_KCC][9] = 62, + [1][1][2][0][RTW89_ACMA][9] = 46, + [1][1][2][0][RTW89_CHILE][9] = 42, + [1][1][2][0][RTW89_UKRAINE][9] = 46, + [1][1][2][0][RTW89_MEXICO][9] = 42, + [1][1][2][0][RTW89_CN][9] = 46, + [1][1][2][0][RTW89_QATAR][9] = 46, + [1][1][2][0][RTW89_UK][9] = 46, + [1][1][2][0][RTW89_FCC][10] = 38, + [1][1][2][0][RTW89_ETSI][10] = 46, + [1][1][2][0][RTW89_MKK][10] = 66, + [1][1][2][0][RTW89_IC][10] = 38, + [1][1][2][0][RTW89_KCC][10] = 62, + [1][1][2][0][RTW89_ACMA][10] = 46, + [1][1][2][0][RTW89_CHILE][10] = 38, + [1][1][2][0][RTW89_UKRAINE][10] = 46, + [1][1][2][0][RTW89_MEXICO][10] = 38, + [1][1][2][0][RTW89_CN][10] = 46, + [1][1][2][0][RTW89_QATAR][10] = 46, + [1][1][2][0][RTW89_UK][10] = 46, + [1][1][2][0][RTW89_FCC][11] = 127, + [1][1][2][0][RTW89_ETSI][11] = 127, + [1][1][2][0][RTW89_MKK][11] = 127, + [1][1][2][0][RTW89_IC][11] = 127, + [1][1][2][0][RTW89_KCC][11] = 127, + [1][1][2][0][RTW89_ACMA][11] = 127, + [1][1][2][0][RTW89_CHILE][11] = 127, + [1][1][2][0][RTW89_UKRAINE][11] = 127, + [1][1][2][0][RTW89_MEXICO][11] = 127, + [1][1][2][0][RTW89_CN][11] = 127, + [1][1][2][0][RTW89_QATAR][11] = 127, + [1][1][2][0][RTW89_UK][11] = 127, + [1][1][2][0][RTW89_FCC][12] = 127, + [1][1][2][0][RTW89_ETSI][12] = 127, + [1][1][2][0][RTW89_MKK][12] = 127, + [1][1][2][0][RTW89_IC][12] = 127, + [1][1][2][0][RTW89_KCC][12] = 127, + [1][1][2][0][RTW89_ACMA][12] = 127, + [1][1][2][0][RTW89_CHILE][12] = 127, + [1][1][2][0][RTW89_UKRAINE][12] = 127, + [1][1][2][0][RTW89_MEXICO][12] = 127, + [1][1][2][0][RTW89_CN][12] = 127, + [1][1][2][0][RTW89_QATAR][12] = 127, + [1][1][2][0][RTW89_UK][12] = 127, + [1][1][2][0][RTW89_FCC][13] = 127, + [1][1][2][0][RTW89_ETSI][13] = 127, + [1][1][2][0][RTW89_MKK][13] = 127, + [1][1][2][0][RTW89_IC][13] = 127, + [1][1][2][0][RTW89_KCC][13] = 127, + [1][1][2][0][RTW89_ACMA][13] = 127, + [1][1][2][0][RTW89_CHILE][13] = 127, + [1][1][2][0][RTW89_UKRAINE][13] = 127, + [1][1][2][0][RTW89_MEXICO][13] = 127, + [1][1][2][0][RTW89_CN][13] = 127, + [1][1][2][0][RTW89_QATAR][13] = 127, + [1][1][2][0][RTW89_UK][13] = 127, + [1][1][2][1][RTW89_FCC][0] = 127, + [1][1][2][1][RTW89_ETSI][0] = 127, + [1][1][2][1][RTW89_MKK][0] = 127, + [1][1][2][1][RTW89_IC][0] = 127, + [1][1][2][1][RTW89_KCC][0] = 127, + [1][1][2][1][RTW89_ACMA][0] = 127, + [1][1][2][1][RTW89_CHILE][0] = 127, + [1][1][2][1][RTW89_UKRAINE][0] = 127, + [1][1][2][1][RTW89_MEXICO][0] = 127, + [1][1][2][1][RTW89_CN][0] = 127, + [1][1][2][1][RTW89_QATAR][0] = 127, + [1][1][2][1][RTW89_UK][0] = 127, + [1][1][2][1][RTW89_FCC][1] = 127, + [1][1][2][1][RTW89_ETSI][1] = 127, + [1][1][2][1][RTW89_MKK][1] = 127, + [1][1][2][1][RTW89_IC][1] = 127, + [1][1][2][1][RTW89_KCC][1] = 127, + [1][1][2][1][RTW89_ACMA][1] = 127, + [1][1][2][1][RTW89_CHILE][1] = 127, + [1][1][2][1][RTW89_UKRAINE][1] = 127, + [1][1][2][1][RTW89_MEXICO][1] = 127, + [1][1][2][1][RTW89_CN][1] = 127, + [1][1][2][1][RTW89_QATAR][1] = 127, + [1][1][2][1][RTW89_UK][1] = 127, + [1][1][2][1][RTW89_FCC][2] = 54, + [1][1][2][1][RTW89_ETSI][2] = 34, + [1][1][2][1][RTW89_MKK][2] = 66, + [1][1][2][1][RTW89_IC][2] = 54, + [1][1][2][1][RTW89_KCC][2] = 62, + [1][1][2][1][RTW89_ACMA][2] = 34, + [1][1][2][1][RTW89_CHILE][2] = 42, + [1][1][2][1][RTW89_UKRAINE][2] = 34, + [1][1][2][1][RTW89_MEXICO][2] = 54, + [1][1][2][1][RTW89_CN][2] = 34, + [1][1][2][1][RTW89_QATAR][2] = 34, + [1][1][2][1][RTW89_UK][2] = 34, + [1][1][2][1][RTW89_FCC][3] = 54, + [1][1][2][1][RTW89_ETSI][3] = 34, + [1][1][2][1][RTW89_MKK][3] = 66, + [1][1][2][1][RTW89_IC][3] = 54, + [1][1][2][1][RTW89_KCC][3] = 62, + [1][1][2][1][RTW89_ACMA][3] = 34, + [1][1][2][1][RTW89_CHILE][3] = 42, + [1][1][2][1][RTW89_UKRAINE][3] = 34, + [1][1][2][1][RTW89_MEXICO][3] = 54, + [1][1][2][1][RTW89_CN][3] = 34, + [1][1][2][1][RTW89_QATAR][3] = 34, + [1][1][2][1][RTW89_UK][3] = 34, + [1][1][2][1][RTW89_FCC][4] = 58, + [1][1][2][1][RTW89_ETSI][4] = 34, + [1][1][2][1][RTW89_MKK][4] = 66, + [1][1][2][1][RTW89_IC][4] = 58, + [1][1][2][1][RTW89_KCC][4] = 62, + [1][1][2][1][RTW89_ACMA][4] = 34, + [1][1][2][1][RTW89_CHILE][4] = 42, + [1][1][2][1][RTW89_UKRAINE][4] = 34, + [1][1][2][1][RTW89_MEXICO][4] = 58, + [1][1][2][1][RTW89_CN][4] = 34, + [1][1][2][1][RTW89_QATAR][4] = 34, + [1][1][2][1][RTW89_UK][4] = 34, + [1][1][2][1][RTW89_FCC][5] = 66, + [1][1][2][1][RTW89_ETSI][5] = 34, + [1][1][2][1][RTW89_MKK][5] = 66, + [1][1][2][1][RTW89_IC][5] = 66, + [1][1][2][1][RTW89_KCC][5] = 62, + [1][1][2][1][RTW89_ACMA][5] = 34, + [1][1][2][1][RTW89_CHILE][5] = 42, + [1][1][2][1][RTW89_UKRAINE][5] = 34, + [1][1][2][1][RTW89_MEXICO][5] = 66, + [1][1][2][1][RTW89_CN][5] = 34, + [1][1][2][1][RTW89_QATAR][5] = 34, + [1][1][2][1][RTW89_UK][5] = 34, + [1][1][2][1][RTW89_FCC][6] = 58, + [1][1][2][1][RTW89_ETSI][6] = 34, + [1][1][2][1][RTW89_MKK][6] = 66, + [1][1][2][1][RTW89_IC][6] = 58, + [1][1][2][1][RTW89_KCC][6] = 62, + [1][1][2][1][RTW89_ACMA][6] = 34, + [1][1][2][1][RTW89_CHILE][6] = 42, + [1][1][2][1][RTW89_UKRAINE][6] = 34, + [1][1][2][1][RTW89_MEXICO][6] = 58, + [1][1][2][1][RTW89_CN][6] = 34, + [1][1][2][1][RTW89_QATAR][6] = 34, + [1][1][2][1][RTW89_UK][6] = 34, + [1][1][2][1][RTW89_FCC][7] = 54, + [1][1][2][1][RTW89_ETSI][7] = 34, + [1][1][2][1][RTW89_MKK][7] = 66, + [1][1][2][1][RTW89_IC][7] = 54, + [1][1][2][1][RTW89_KCC][7] = 62, + [1][1][2][1][RTW89_ACMA][7] = 34, + [1][1][2][1][RTW89_CHILE][7] = 42, + [1][1][2][1][RTW89_UKRAINE][7] = 34, + [1][1][2][1][RTW89_MEXICO][7] = 54, + [1][1][2][1][RTW89_CN][7] = 34, + [1][1][2][1][RTW89_QATAR][7] = 34, + [1][1][2][1][RTW89_UK][7] = 34, + [1][1][2][1][RTW89_FCC][8] = 54, + [1][1][2][1][RTW89_ETSI][8] = 34, + [1][1][2][1][RTW89_MKK][8] = 66, + [1][1][2][1][RTW89_IC][8] = 54, + [1][1][2][1][RTW89_KCC][8] = 62, + [1][1][2][1][RTW89_ACMA][8] = 34, + [1][1][2][1][RTW89_CHILE][8] = 42, + [1][1][2][1][RTW89_UKRAINE][8] = 34, + [1][1][2][1][RTW89_MEXICO][8] = 54, + [1][1][2][1][RTW89_CN][8] = 34, + [1][1][2][1][RTW89_QATAR][8] = 34, + [1][1][2][1][RTW89_UK][8] = 34, + [1][1][2][1][RTW89_FCC][9] = 42, + [1][1][2][1][RTW89_ETSI][9] = 34, + [1][1][2][1][RTW89_MKK][9] = 66, + [1][1][2][1][RTW89_IC][9] = 42, + [1][1][2][1][RTW89_KCC][9] = 62, + [1][1][2][1][RTW89_ACMA][9] = 34, + [1][1][2][1][RTW89_CHILE][9] = 42, + [1][1][2][1][RTW89_UKRAINE][9] = 34, + [1][1][2][1][RTW89_MEXICO][9] = 42, + [1][1][2][1][RTW89_CN][9] = 34, + [1][1][2][1][RTW89_QATAR][9] = 34, + [1][1][2][1][RTW89_UK][9] = 34, + [1][1][2][1][RTW89_FCC][10] = 38, + [1][1][2][1][RTW89_ETSI][10] = 34, + [1][1][2][1][RTW89_MKK][10] = 66, + [1][1][2][1][RTW89_IC][10] = 38, + [1][1][2][1][RTW89_KCC][10] = 62, + [1][1][2][1][RTW89_ACMA][10] = 34, + [1][1][2][1][RTW89_CHILE][10] = 38, + [1][1][2][1][RTW89_UKRAINE][10] = 34, + [1][1][2][1][RTW89_MEXICO][10] = 38, + [1][1][2][1][RTW89_CN][10] = 34, + [1][1][2][1][RTW89_QATAR][10] = 34, + [1][1][2][1][RTW89_UK][10] = 34, + [1][1][2][1][RTW89_FCC][11] = 127, + [1][1][2][1][RTW89_ETSI][11] = 127, + [1][1][2][1][RTW89_MKK][11] = 127, + [1][1][2][1][RTW89_IC][11] = 127, + [1][1][2][1][RTW89_KCC][11] = 127, + [1][1][2][1][RTW89_ACMA][11] = 127, + [1][1][2][1][RTW89_CHILE][11] = 127, + [1][1][2][1][RTW89_UKRAINE][11] = 127, + [1][1][2][1][RTW89_MEXICO][11] = 127, + [1][1][2][1][RTW89_CN][11] = 127, + [1][1][2][1][RTW89_QATAR][11] = 127, + [1][1][2][1][RTW89_UK][11] = 127, + [1][1][2][1][RTW89_FCC][12] = 127, + [1][1][2][1][RTW89_ETSI][12] = 127, + [1][1][2][1][RTW89_MKK][12] = 127, + [1][1][2][1][RTW89_IC][12] = 127, + [1][1][2][1][RTW89_KCC][12] = 127, + [1][1][2][1][RTW89_ACMA][12] = 127, + [1][1][2][1][RTW89_CHILE][12] = 127, + [1][1][2][1][RTW89_UKRAINE][12] = 127, + [1][1][2][1][RTW89_MEXICO][12] = 127, + [1][1][2][1][RTW89_CN][12] = 127, + [1][1][2][1][RTW89_QATAR][12] = 127, + [1][1][2][1][RTW89_UK][12] = 127, + [1][1][2][1][RTW89_FCC][13] = 127, + [1][1][2][1][RTW89_ETSI][13] = 127, + [1][1][2][1][RTW89_MKK][13] = 127, + [1][1][2][1][RTW89_IC][13] = 127, + [1][1][2][1][RTW89_KCC][13] = 127, + [1][1][2][1][RTW89_ACMA][13] = 127, + [1][1][2][1][RTW89_CHILE][13] = 127, + [1][1][2][1][RTW89_UKRAINE][13] = 127, + [1][1][2][1][RTW89_MEXICO][13] = 127, + [1][1][2][1][RTW89_CN][13] = 127, + [1][1][2][1][RTW89_QATAR][13] = 127, + [1][1][2][1][RTW89_UK][13] = 127, +}; + +const s8 rtw89_8852b_txpwr_lmt_5g[RTW89_5G_BW_NUM][RTW89_NTX_NUM] + [RTW89_RS_LMT_NUM][RTW89_BF_NUM] + [RTW89_REGD_NUM][RTW89_5G_CH_NUM] = { + [0][0][1][0][RTW89_WW][0] = 42, + [0][0][1][0][RTW89_WW][2] = 42, + [0][0][1][0][RTW89_WW][4] = 42, + [0][0][1][0][RTW89_WW][6] = 42, + [0][0][1][0][RTW89_WW][8] = 52, + [0][0][1][0][RTW89_WW][10] = 52, + [0][0][1][0][RTW89_WW][12] = 52, + [0][0][1][0][RTW89_WW][14] = 52, + [0][0][1][0][RTW89_WW][15] = 52, + [0][0][1][0][RTW89_WW][17] = 52, + [0][0][1][0][RTW89_WW][19] = 52, + [0][0][1][0][RTW89_WW][21] = 52, + [0][0][1][0][RTW89_WW][23] = 52, + [0][0][1][0][RTW89_WW][25] = 52, + [0][0][1][0][RTW89_WW][27] = 52, + [0][0][1][0][RTW89_WW][29] = 52, + [0][0][1][0][RTW89_WW][31] = 52, + [0][0][1][0][RTW89_WW][33] = 52, + [0][0][1][0][RTW89_WW][35] = 52, + [0][0][1][0][RTW89_WW][37] = 68, + [0][0][1][0][RTW89_WW][38] = 28, + [0][0][1][0][RTW89_WW][40] = 28, + [0][0][1][0][RTW89_WW][42] = 28, + [0][0][1][0][RTW89_WW][44] = 28, + [0][0][1][0][RTW89_WW][46] = 28, + [0][0][1][0][RTW89_WW][48] = 78, + [0][0][1][0][RTW89_WW][50] = 78, + [0][0][1][0][RTW89_WW][52] = 78, + [0][1][1][0][RTW89_WW][0] = 30, + [0][1][1][0][RTW89_WW][2] = 32, + [0][1][1][0][RTW89_WW][4] = 30, + [0][1][1][0][RTW89_WW][6] = 30, + [0][1][1][0][RTW89_WW][8] = 40, + [0][1][1][0][RTW89_WW][10] = 40, + [0][1][1][0][RTW89_WW][12] = 40, + [0][1][1][0][RTW89_WW][14] = 40, + [0][1][1][0][RTW89_WW][15] = 40, + [0][1][1][0][RTW89_WW][17] = 40, + [0][1][1][0][RTW89_WW][19] = 40, + [0][1][1][0][RTW89_WW][21] = 40, + [0][1][1][0][RTW89_WW][23] = 40, + [0][1][1][0][RTW89_WW][25] = 40, + [0][1][1][0][RTW89_WW][27] = 40, + [0][1][1][0][RTW89_WW][29] = 40, + [0][1][1][0][RTW89_WW][31] = 40, + [0][1][1][0][RTW89_WW][33] = 40, + [0][1][1][0][RTW89_WW][35] = 40, + [0][1][1][0][RTW89_WW][37] = 50, + [0][1][1][0][RTW89_WW][38] = 16, + [0][1][1][0][RTW89_WW][40] = 16, + [0][1][1][0][RTW89_WW][42] = 16, + [0][1][1][0][RTW89_WW][44] = 16, + [0][1][1][0][RTW89_WW][46] = 16, + [0][1][1][0][RTW89_WW][48] = 56, + [0][1][1][0][RTW89_WW][50] = 56, + [0][1][1][0][RTW89_WW][52] = 56, + [0][0][2][0][RTW89_WW][0] = 42, + [0][0][2][0][RTW89_WW][2] = 42, + [0][0][2][0][RTW89_WW][4] = 42, + [0][0][2][0][RTW89_WW][6] = 42, + [0][0][2][0][RTW89_WW][8] = 52, + [0][0][2][0][RTW89_WW][10] = 52, + [0][0][2][0][RTW89_WW][12] = 52, + [0][0][2][0][RTW89_WW][14] = 52, + [0][0][2][0][RTW89_WW][15] = 52, + [0][0][2][0][RTW89_WW][17] = 52, + [0][0][2][0][RTW89_WW][19] = 52, + [0][0][2][0][RTW89_WW][21] = 52, + [0][0][2][0][RTW89_WW][23] = 52, + [0][0][2][0][RTW89_WW][25] = 52, + [0][0][2][0][RTW89_WW][27] = 52, + [0][0][2][0][RTW89_WW][29] = 52, + [0][0][2][0][RTW89_WW][31] = 52, + [0][0][2][0][RTW89_WW][33] = 52, + [0][0][2][0][RTW89_WW][35] = 52, + [0][0][2][0][RTW89_WW][37] = 64, + [0][0][2][0][RTW89_WW][38] = 28, + [0][0][2][0][RTW89_WW][40] = 28, + [0][0][2][0][RTW89_WW][42] = 28, + [0][0][2][0][RTW89_WW][44] = 28, + [0][0][2][0][RTW89_WW][46] = 28, + [0][0][2][0][RTW89_WW][48] = 78, + [0][0][2][0][RTW89_WW][50] = 78, + [0][0][2][0][RTW89_WW][52] = 78, + [0][1][2][0][RTW89_WW][0] = 30, + [0][1][2][0][RTW89_WW][2] = 30, + [0][1][2][0][RTW89_WW][4] = 30, + [0][1][2][0][RTW89_WW][6] = 30, + [0][1][2][0][RTW89_WW][8] = 40, + [0][1][2][0][RTW89_WW][10] = 40, + [0][1][2][0][RTW89_WW][12] = 40, + [0][1][2][0][RTW89_WW][14] = 40, + [0][1][2][0][RTW89_WW][15] = 40, + [0][1][2][0][RTW89_WW][17] = 40, + [0][1][2][0][RTW89_WW][19] = 40, + [0][1][2][0][RTW89_WW][21] = 40, + [0][1][2][0][RTW89_WW][23] = 40, + [0][1][2][0][RTW89_WW][25] = 40, + [0][1][2][0][RTW89_WW][27] = 40, + [0][1][2][0][RTW89_WW][29] = 40, + [0][1][2][0][RTW89_WW][31] = 40, + [0][1][2][0][RTW89_WW][33] = 40, + [0][1][2][0][RTW89_WW][35] = 40, + [0][1][2][0][RTW89_WW][37] = 50, + [0][1][2][0][RTW89_WW][38] = 16, + [0][1][2][0][RTW89_WW][40] = 16, + [0][1][2][0][RTW89_WW][42] = 16, + [0][1][2][0][RTW89_WW][44] = 16, + [0][1][2][0][RTW89_WW][46] = 16, + [0][1][2][0][RTW89_WW][48] = 58, + [0][1][2][0][RTW89_WW][50] = 58, + [0][1][2][0][RTW89_WW][52] = 58, + [0][1][2][1][RTW89_WW][0] = 14, + [0][1][2][1][RTW89_WW][2] = 14, + [0][1][2][1][RTW89_WW][4] = 14, + [0][1][2][1][RTW89_WW][6] = 14, + [0][1][2][1][RTW89_WW][8] = 28, + [0][1][2][1][RTW89_WW][10] = 28, + [0][1][2][1][RTW89_WW][12] = 28, + [0][1][2][1][RTW89_WW][14] = 28, + [0][1][2][1][RTW89_WW][15] = 28, + [0][1][2][1][RTW89_WW][17] = 28, + [0][1][2][1][RTW89_WW][19] = 28, + [0][1][2][1][RTW89_WW][21] = 28, + [0][1][2][1][RTW89_WW][23] = 28, + [0][1][2][1][RTW89_WW][25] = 28, + [0][1][2][1][RTW89_WW][27] = 28, + [0][1][2][1][RTW89_WW][29] = 28, + [0][1][2][1][RTW89_WW][31] = 28, + [0][1][2][1][RTW89_WW][33] = 28, + [0][1][2][1][RTW89_WW][35] = 28, + [0][1][2][1][RTW89_WW][37] = 36, + [0][1][2][1][RTW89_WW][38] = 4, + [0][1][2][1][RTW89_WW][40] = 4, + [0][1][2][1][RTW89_WW][42] = 4, + [0][1][2][1][RTW89_WW][44] = 4, + [0][1][2][1][RTW89_WW][46] = 4, + [0][1][2][1][RTW89_WW][48] = 58, + [0][1][2][1][RTW89_WW][50] = 58, + [0][1][2][1][RTW89_WW][52] = 58, + [1][0][2][0][RTW89_WW][1] = 42, + [1][0][2][0][RTW89_WW][5] = 42, + [1][0][2][0][RTW89_WW][9] = 52, + [1][0][2][0][RTW89_WW][13] = 52, + [1][0][2][0][RTW89_WW][16] = 52, + [1][0][2][0][RTW89_WW][20] = 52, + [1][0][2][0][RTW89_WW][24] = 52, + [1][0][2][0][RTW89_WW][28] = 52, + [1][0][2][0][RTW89_WW][32] = 52, + [1][0][2][0][RTW89_WW][36] = 64, + [1][0][2][0][RTW89_WW][39] = 28, + [1][0][2][0][RTW89_WW][43] = 28, + [1][0][2][0][RTW89_WW][47] = 78, + [1][0][2][0][RTW89_WW][51] = 70, + [1][1][2][0][RTW89_WW][1] = 30, + [1][1][2][0][RTW89_WW][5] = 30, + [1][1][2][0][RTW89_WW][9] = 40, + [1][1][2][0][RTW89_WW][13] = 40, + [1][1][2][0][RTW89_WW][16] = 40, + [1][1][2][0][RTW89_WW][20] = 40, + [1][1][2][0][RTW89_WW][24] = 40, + [1][1][2][0][RTW89_WW][28] = 40, + [1][1][2][0][RTW89_WW][32] = 40, + [1][1][2][0][RTW89_WW][36] = 50, + [1][1][2][0][RTW89_WW][39] = 16, + [1][1][2][0][RTW89_WW][43] = 16, + [1][1][2][0][RTW89_WW][47] = 68, + [1][1][2][0][RTW89_WW][51] = 66, + [1][1][2][1][RTW89_WW][1] = 16, + [1][1][2][1][RTW89_WW][5] = 16, + [1][1][2][1][RTW89_WW][9] = 28, + [1][1][2][1][RTW89_WW][13] = 28, + [1][1][2][1][RTW89_WW][16] = 28, + [1][1][2][1][RTW89_WW][20] = 28, + [1][1][2][1][RTW89_WW][24] = 28, + [1][1][2][1][RTW89_WW][28] = 28, + [1][1][2][1][RTW89_WW][32] = 28, + [1][1][2][1][RTW89_WW][36] = 36, + [1][1][2][1][RTW89_WW][39] = 4, + [1][1][2][1][RTW89_WW][43] = 4, + [1][1][2][1][RTW89_WW][47] = 68, + [1][1][2][1][RTW89_WW][51] = 66, + [2][0][2][0][RTW89_WW][3] = 42, + [2][0][2][0][RTW89_WW][11] = 52, + [2][0][2][0][RTW89_WW][18] = 52, + [2][0][2][0][RTW89_WW][26] = 52, + [2][0][2][0][RTW89_WW][34] = 64, + [2][0][2][0][RTW89_WW][41] = 28, + [2][0][2][0][RTW89_WW][49] = 64, + [2][1][2][0][RTW89_WW][3] = 28, + [2][1][2][0][RTW89_WW][11] = 40, + [2][1][2][0][RTW89_WW][18] = 40, + [2][1][2][0][RTW89_WW][26] = 40, + [2][1][2][0][RTW89_WW][34] = 50, + [2][1][2][0][RTW89_WW][41] = 16, + [2][1][2][0][RTW89_WW][49] = 58, + [2][1][2][1][RTW89_WW][3] = 16, + [2][1][2][1][RTW89_WW][11] = 28, + [2][1][2][1][RTW89_WW][18] = 28, + [2][1][2][1][RTW89_WW][26] = 28, + [2][1][2][1][RTW89_WW][34] = 34, + [2][1][2][1][RTW89_WW][41] = 4, + [2][1][2][1][RTW89_WW][49] = 58, + [0][0][1][0][RTW89_FCC][0] = 78, + [0][0][1][0][RTW89_ETSI][0] = 58, + [0][0][1][0][RTW89_MKK][0] = 60, + [0][0][1][0][RTW89_IC][0] = 60, + [0][0][1][0][RTW89_KCC][0] = 76, + [0][0][1][0][RTW89_ACMA][0] = 58, + [0][0][1][0][RTW89_CHILE][0] = 42, + [0][0][1][0][RTW89_UKRAINE][0] = 52, + [0][0][1][0][RTW89_MEXICO][0] = 62, + [0][0][1][0][RTW89_CN][0] = 58, + [0][0][1][0][RTW89_QATAR][0] = 58, + [0][0][1][0][RTW89_UK][0] = 58, + [0][0][1][0][RTW89_FCC][2] = 78, + [0][0][1][0][RTW89_ETSI][2] = 58, + [0][0][1][0][RTW89_MKK][2] = 60, + [0][0][1][0][RTW89_IC][2] = 60, + [0][0][1][0][RTW89_KCC][2] = 76, + [0][0][1][0][RTW89_ACMA][2] = 58, + [0][0][1][0][RTW89_CHILE][2] = 42, + [0][0][1][0][RTW89_UKRAINE][2] = 52, + [0][0][1][0][RTW89_MEXICO][2] = 62, + [0][0][1][0][RTW89_CN][2] = 58, + [0][0][1][0][RTW89_QATAR][2] = 58, + [0][0][1][0][RTW89_UK][2] = 58, + [0][0][1][0][RTW89_FCC][4] = 78, + [0][0][1][0][RTW89_ETSI][4] = 58, + [0][0][1][0][RTW89_MKK][4] = 60, + [0][0][1][0][RTW89_IC][4] = 60, + [0][0][1][0][RTW89_KCC][4] = 76, + [0][0][1][0][RTW89_ACMA][4] = 58, + [0][0][1][0][RTW89_CHILE][4] = 42, + [0][0][1][0][RTW89_UKRAINE][4] = 52, + [0][0][1][0][RTW89_MEXICO][4] = 62, + [0][0][1][0][RTW89_CN][4] = 58, + [0][0][1][0][RTW89_QATAR][4] = 58, + [0][0][1][0][RTW89_UK][4] = 58, + [0][0][1][0][RTW89_FCC][6] = 78, + [0][0][1][0][RTW89_ETSI][6] = 58, + [0][0][1][0][RTW89_MKK][6] = 60, + [0][0][1][0][RTW89_IC][6] = 60, + [0][0][1][0][RTW89_KCC][6] = 50, + [0][0][1][0][RTW89_ACMA][6] = 58, + [0][0][1][0][RTW89_CHILE][6] = 42, + [0][0][1][0][RTW89_UKRAINE][6] = 52, + [0][0][1][0][RTW89_MEXICO][6] = 62, + [0][0][1][0][RTW89_CN][6] = 58, + [0][0][1][0][RTW89_QATAR][6] = 58, + [0][0][1][0][RTW89_UK][6] = 58, + [0][0][1][0][RTW89_FCC][8] = 78, + [0][0][1][0][RTW89_ETSI][8] = 58, + [0][0][1][0][RTW89_MKK][8] = 62, + [0][0][1][0][RTW89_IC][8] = 64, + [0][0][1][0][RTW89_KCC][8] = 70, + [0][0][1][0][RTW89_ACMA][8] = 58, + [0][0][1][0][RTW89_CHILE][8] = 66, + [0][0][1][0][RTW89_UKRAINE][8] = 52, + [0][0][1][0][RTW89_MEXICO][8] = 78, + [0][0][1][0][RTW89_CN][8] = 58, + [0][0][1][0][RTW89_QATAR][8] = 58, + [0][0][1][0][RTW89_UK][8] = 58, + [0][0][1][0][RTW89_FCC][10] = 78, + [0][0][1][0][RTW89_ETSI][10] = 58, + [0][0][1][0][RTW89_MKK][10] = 62, + [0][0][1][0][RTW89_IC][10] = 64, + [0][0][1][0][RTW89_KCC][10] = 70, + [0][0][1][0][RTW89_ACMA][10] = 58, + [0][0][1][0][RTW89_CHILE][10] = 66, + [0][0][1][0][RTW89_UKRAINE][10] = 52, + [0][0][1][0][RTW89_MEXICO][10] = 78, + [0][0][1][0][RTW89_CN][10] = 58, + [0][0][1][0][RTW89_QATAR][10] = 58, + [0][0][1][0][RTW89_UK][10] = 58, + [0][0][1][0][RTW89_FCC][12] = 78, + [0][0][1][0][RTW89_ETSI][12] = 58, + [0][0][1][0][RTW89_MKK][12] = 62, + [0][0][1][0][RTW89_IC][12] = 64, + [0][0][1][0][RTW89_KCC][12] = 74, + [0][0][1][0][RTW89_ACMA][12] = 58, + [0][0][1][0][RTW89_CHILE][12] = 66, + [0][0][1][0][RTW89_UKRAINE][12] = 52, + [0][0][1][0][RTW89_MEXICO][12] = 78, + [0][0][1][0][RTW89_CN][12] = 58, + [0][0][1][0][RTW89_QATAR][12] = 58, + [0][0][1][0][RTW89_UK][12] = 58, + [0][0][1][0][RTW89_FCC][14] = 78, + [0][0][1][0][RTW89_ETSI][14] = 58, + [0][0][1][0][RTW89_MKK][14] = 60, + [0][0][1][0][RTW89_IC][14] = 64, + [0][0][1][0][RTW89_KCC][14] = 74, + [0][0][1][0][RTW89_ACMA][14] = 58, + [0][0][1][0][RTW89_CHILE][14] = 66, + [0][0][1][0][RTW89_UKRAINE][14] = 52, + [0][0][1][0][RTW89_MEXICO][14] = 78, + [0][0][1][0][RTW89_CN][14] = 58, + [0][0][1][0][RTW89_QATAR][14] = 58, + [0][0][1][0][RTW89_UK][14] = 58, + [0][0][1][0][RTW89_FCC][15] = 76, + [0][0][1][0][RTW89_ETSI][15] = 58, + [0][0][1][0][RTW89_MKK][15] = 76, + [0][0][1][0][RTW89_IC][15] = 76, + [0][0][1][0][RTW89_KCC][15] = 74, + [0][0][1][0][RTW89_ACMA][15] = 58, + [0][0][1][0][RTW89_CHILE][15] = 66, + [0][0][1][0][RTW89_UKRAINE][15] = 52, + [0][0][1][0][RTW89_MEXICO][15] = 76, + [0][0][1][0][RTW89_CN][15] = 127, + [0][0][1][0][RTW89_QATAR][15] = 58, + [0][0][1][0][RTW89_UK][15] = 58, + [0][0][1][0][RTW89_FCC][17] = 78, + [0][0][1][0][RTW89_ETSI][17] = 58, + [0][0][1][0][RTW89_MKK][17] = 76, + [0][0][1][0][RTW89_IC][17] = 78, + [0][0][1][0][RTW89_KCC][17] = 74, + [0][0][1][0][RTW89_ACMA][17] = 58, + [0][0][1][0][RTW89_CHILE][17] = 66, + [0][0][1][0][RTW89_UKRAINE][17] = 52, + [0][0][1][0][RTW89_MEXICO][17] = 78, + [0][0][1][0][RTW89_CN][17] = 127, + [0][0][1][0][RTW89_QATAR][17] = 58, + [0][0][1][0][RTW89_UK][17] = 58, + [0][0][1][0][RTW89_FCC][19] = 78, + [0][0][1][0][RTW89_ETSI][19] = 58, + [0][0][1][0][RTW89_MKK][19] = 76, + [0][0][1][0][RTW89_IC][19] = 78, + [0][0][1][0][RTW89_KCC][19] = 74, + [0][0][1][0][RTW89_ACMA][19] = 58, + [0][0][1][0][RTW89_CHILE][19] = 66, + [0][0][1][0][RTW89_UKRAINE][19] = 52, + [0][0][1][0][RTW89_MEXICO][19] = 78, + [0][0][1][0][RTW89_CN][19] = 127, + [0][0][1][0][RTW89_QATAR][19] = 58, + [0][0][1][0][RTW89_UK][19] = 58, + [0][0][1][0][RTW89_FCC][21] = 78, + [0][0][1][0][RTW89_ETSI][21] = 58, + [0][0][1][0][RTW89_MKK][21] = 76, + [0][0][1][0][RTW89_IC][21] = 78, + [0][0][1][0][RTW89_KCC][21] = 74, + [0][0][1][0][RTW89_ACMA][21] = 58, + [0][0][1][0][RTW89_CHILE][21] = 68, + [0][0][1][0][RTW89_UKRAINE][21] = 52, + [0][0][1][0][RTW89_MEXICO][21] = 78, + [0][0][1][0][RTW89_CN][21] = 127, + [0][0][1][0][RTW89_QATAR][21] = 58, + [0][0][1][0][RTW89_UK][21] = 58, + [0][0][1][0][RTW89_FCC][23] = 78, + [0][0][1][0][RTW89_ETSI][23] = 58, + [0][0][1][0][RTW89_MKK][23] = 76, + [0][0][1][0][RTW89_IC][23] = 78, + [0][0][1][0][RTW89_KCC][23] = 74, + [0][0][1][0][RTW89_ACMA][23] = 58, + [0][0][1][0][RTW89_CHILE][23] = 68, + [0][0][1][0][RTW89_UKRAINE][23] = 52, + [0][0][1][0][RTW89_MEXICO][23] = 78, + [0][0][1][0][RTW89_CN][23] = 127, + [0][0][1][0][RTW89_QATAR][23] = 58, + [0][0][1][0][RTW89_UK][23] = 58, + [0][0][1][0][RTW89_FCC][25] = 78, + [0][0][1][0][RTW89_ETSI][25] = 58, + [0][0][1][0][RTW89_MKK][25] = 76, + [0][0][1][0][RTW89_IC][25] = 127, + [0][0][1][0][RTW89_KCC][25] = 74, + [0][0][1][0][RTW89_ACMA][25] = 127, + [0][0][1][0][RTW89_CHILE][25] = 68, + [0][0][1][0][RTW89_UKRAINE][25] = 52, + [0][0][1][0][RTW89_MEXICO][25] = 78, + [0][0][1][0][RTW89_CN][25] = 127, + [0][0][1][0][RTW89_QATAR][25] = 58, + [0][0][1][0][RTW89_UK][25] = 58, + [0][0][1][0][RTW89_FCC][27] = 78, + [0][0][1][0][RTW89_ETSI][27] = 58, + [0][0][1][0][RTW89_MKK][27] = 76, + [0][0][1][0][RTW89_IC][27] = 127, + [0][0][1][0][RTW89_KCC][27] = 74, + [0][0][1][0][RTW89_ACMA][27] = 127, + [0][0][1][0][RTW89_CHILE][27] = 66, + [0][0][1][0][RTW89_UKRAINE][27] = 52, + [0][0][1][0][RTW89_MEXICO][27] = 78, + [0][0][1][0][RTW89_CN][27] = 127, + [0][0][1][0][RTW89_QATAR][27] = 58, + [0][0][1][0][RTW89_UK][27] = 58, + [0][0][1][0][RTW89_FCC][29] = 78, + [0][0][1][0][RTW89_ETSI][29] = 58, + [0][0][1][0][RTW89_MKK][29] = 76, + [0][0][1][0][RTW89_IC][29] = 127, + [0][0][1][0][RTW89_KCC][29] = 74, + [0][0][1][0][RTW89_ACMA][29] = 127, + [0][0][1][0][RTW89_CHILE][29] = 66, + [0][0][1][0][RTW89_UKRAINE][29] = 52, + [0][0][1][0][RTW89_MEXICO][29] = 78, + [0][0][1][0][RTW89_CN][29] = 127, + [0][0][1][0][RTW89_QATAR][29] = 58, + [0][0][1][0][RTW89_UK][29] = 58, + [0][0][1][0][RTW89_FCC][31] = 78, + [0][0][1][0][RTW89_ETSI][31] = 58, + [0][0][1][0][RTW89_MKK][31] = 76, + [0][0][1][0][RTW89_IC][31] = 78, + [0][0][1][0][RTW89_KCC][31] = 72, + [0][0][1][0][RTW89_ACMA][31] = 58, + [0][0][1][0][RTW89_CHILE][31] = 66, + [0][0][1][0][RTW89_UKRAINE][31] = 52, + [0][0][1][0][RTW89_MEXICO][31] = 78, + [0][0][1][0][RTW89_CN][31] = 127, + [0][0][1][0][RTW89_QATAR][31] = 58, + [0][0][1][0][RTW89_UK][31] = 58, + [0][0][1][0][RTW89_FCC][33] = 78, + [0][0][1][0][RTW89_ETSI][33] = 58, + [0][0][1][0][RTW89_MKK][33] = 76, + [0][0][1][0][RTW89_IC][33] = 78, + [0][0][1][0][RTW89_KCC][33] = 72, + [0][0][1][0][RTW89_ACMA][33] = 58, + [0][0][1][0][RTW89_CHILE][33] = 66, + [0][0][1][0][RTW89_UKRAINE][33] = 52, + [0][0][1][0][RTW89_MEXICO][33] = 78, + [0][0][1][0][RTW89_CN][33] = 127, + [0][0][1][0][RTW89_QATAR][33] = 58, + [0][0][1][0][RTW89_UK][33] = 58, + [0][0][1][0][RTW89_FCC][35] = 70, + [0][0][1][0][RTW89_ETSI][35] = 58, + [0][0][1][0][RTW89_MKK][35] = 76, + [0][0][1][0][RTW89_IC][35] = 70, + [0][0][1][0][RTW89_KCC][35] = 72, + [0][0][1][0][RTW89_ACMA][35] = 58, + [0][0][1][0][RTW89_CHILE][35] = 66, + [0][0][1][0][RTW89_UKRAINE][35] = 52, + [0][0][1][0][RTW89_MEXICO][35] = 70, + [0][0][1][0][RTW89_CN][35] = 127, + [0][0][1][0][RTW89_QATAR][35] = 58, + [0][0][1][0][RTW89_UK][35] = 58, + [0][0][1][0][RTW89_FCC][37] = 78, + [0][0][1][0][RTW89_ETSI][37] = 127, + [0][0][1][0][RTW89_MKK][37] = 76, + [0][0][1][0][RTW89_IC][37] = 78, + [0][0][1][0][RTW89_KCC][37] = 72, + [0][0][1][0][RTW89_ACMA][37] = 76, + [0][0][1][0][RTW89_CHILE][37] = 68, + [0][0][1][0][RTW89_UKRAINE][37] = 127, + [0][0][1][0][RTW89_MEXICO][37] = 78, + [0][0][1][0][RTW89_CN][37] = 127, + [0][0][1][0][RTW89_QATAR][37] = 127, + [0][0][1][0][RTW89_UK][37] = 76, + [0][0][1][0][RTW89_FCC][38] = 78, + [0][0][1][0][RTW89_ETSI][38] = 28, + [0][0][1][0][RTW89_MKK][38] = 127, + [0][0][1][0][RTW89_IC][38] = 78, + [0][0][1][0][RTW89_KCC][38] = 74, + [0][0][1][0][RTW89_ACMA][38] = 76, + [0][0][1][0][RTW89_CHILE][38] = 68, + [0][0][1][0][RTW89_UKRAINE][38] = 28, + [0][0][1][0][RTW89_MEXICO][38] = 78, + [0][0][1][0][RTW89_CN][38] = 76, + [0][0][1][0][RTW89_QATAR][38] = 28, + [0][0][1][0][RTW89_UK][38] = 58, + [0][0][1][0][RTW89_FCC][40] = 78, + [0][0][1][0][RTW89_ETSI][40] = 28, + [0][0][1][0][RTW89_MKK][40] = 127, + [0][0][1][0][RTW89_IC][40] = 78, + [0][0][1][0][RTW89_KCC][40] = 74, + [0][0][1][0][RTW89_ACMA][40] = 76, + [0][0][1][0][RTW89_CHILE][40] = 68, + [0][0][1][0][RTW89_UKRAINE][40] = 28, + [0][0][1][0][RTW89_MEXICO][40] = 78, + [0][0][1][0][RTW89_CN][40] = 76, + [0][0][1][0][RTW89_QATAR][40] = 28, + [0][0][1][0][RTW89_UK][40] = 58, + [0][0][1][0][RTW89_FCC][42] = 78, + [0][0][1][0][RTW89_ETSI][42] = 28, + [0][0][1][0][RTW89_MKK][42] = 127, + [0][0][1][0][RTW89_IC][42] = 78, + [0][0][1][0][RTW89_KCC][42] = 74, + [0][0][1][0][RTW89_ACMA][42] = 76, + [0][0][1][0][RTW89_CHILE][42] = 66, + [0][0][1][0][RTW89_UKRAINE][42] = 28, + [0][0][1][0][RTW89_MEXICO][42] = 78, + [0][0][1][0][RTW89_CN][42] = 76, + [0][0][1][0][RTW89_QATAR][42] = 28, + [0][0][1][0][RTW89_UK][42] = 58, + [0][0][1][0][RTW89_FCC][44] = 78, + [0][0][1][0][RTW89_ETSI][44] = 28, + [0][0][1][0][RTW89_MKK][44] = 127, + [0][0][1][0][RTW89_IC][44] = 78, + [0][0][1][0][RTW89_KCC][44] = 74, + [0][0][1][0][RTW89_ACMA][44] = 76, + [0][0][1][0][RTW89_CHILE][44] = 68, + [0][0][1][0][RTW89_UKRAINE][44] = 28, + [0][0][1][0][RTW89_MEXICO][44] = 78, + [0][0][1][0][RTW89_CN][44] = 76, + [0][0][1][0][RTW89_QATAR][44] = 28, + [0][0][1][0][RTW89_UK][44] = 58, + [0][0][1][0][RTW89_FCC][46] = 78, + [0][0][1][0][RTW89_ETSI][46] = 28, + [0][0][1][0][RTW89_MKK][46] = 127, + [0][0][1][0][RTW89_IC][46] = 78, + [0][0][1][0][RTW89_KCC][46] = 74, + [0][0][1][0][RTW89_ACMA][46] = 76, + [0][0][1][0][RTW89_CHILE][46] = 68, + [0][0][1][0][RTW89_UKRAINE][46] = 28, + [0][0][1][0][RTW89_MEXICO][46] = 78, + [0][0][1][0][RTW89_CN][46] = 76, + [0][0][1][0][RTW89_QATAR][46] = 28, + [0][0][1][0][RTW89_UK][46] = 58, + [0][0][1][0][RTW89_FCC][48] = 78, + [0][0][1][0][RTW89_ETSI][48] = 127, + [0][0][1][0][RTW89_MKK][48] = 127, + [0][0][1][0][RTW89_IC][48] = 127, + [0][0][1][0][RTW89_KCC][48] = 127, + [0][0][1][0][RTW89_ACMA][48] = 127, + [0][0][1][0][RTW89_CHILE][48] = 127, + [0][0][1][0][RTW89_UKRAINE][48] = 127, + [0][0][1][0][RTW89_MEXICO][48] = 127, + [0][0][1][0][RTW89_CN][48] = 127, + [0][0][1][0][RTW89_QATAR][48] = 127, + [0][0][1][0][RTW89_UK][48] = 127, + [0][0][1][0][RTW89_FCC][50] = 78, + [0][0][1][0][RTW89_ETSI][50] = 127, + [0][0][1][0][RTW89_MKK][50] = 127, + [0][0][1][0][RTW89_IC][50] = 127, + [0][0][1][0][RTW89_KCC][50] = 127, + [0][0][1][0][RTW89_ACMA][50] = 127, + [0][0][1][0][RTW89_CHILE][50] = 127, + [0][0][1][0][RTW89_UKRAINE][50] = 127, + [0][0][1][0][RTW89_MEXICO][50] = 127, + [0][0][1][0][RTW89_CN][50] = 127, + [0][0][1][0][RTW89_QATAR][50] = 127, + [0][0][1][0][RTW89_UK][50] = 127, + [0][0][1][0][RTW89_FCC][52] = 78, + [0][0][1][0][RTW89_ETSI][52] = 127, + [0][0][1][0][RTW89_MKK][52] = 127, + [0][0][1][0][RTW89_IC][52] = 127, + [0][0][1][0][RTW89_KCC][52] = 127, + [0][0][1][0][RTW89_ACMA][52] = 127, + [0][0][1][0][RTW89_CHILE][52] = 127, + [0][0][1][0][RTW89_UKRAINE][52] = 127, + [0][0][1][0][RTW89_MEXICO][52] = 127, + [0][0][1][0][RTW89_CN][52] = 127, + [0][0][1][0][RTW89_QATAR][52] = 127, + [0][0][1][0][RTW89_UK][52] = 127, + [0][1][1][0][RTW89_FCC][0] = 68, + [0][1][1][0][RTW89_ETSI][0] = 46, + [0][1][1][0][RTW89_MKK][0] = 48, + [0][1][1][0][RTW89_IC][0] = 40, + [0][1][1][0][RTW89_KCC][0] = 64, + [0][1][1][0][RTW89_ACMA][0] = 46, + [0][1][1][0][RTW89_CHILE][0] = 30, + [0][1][1][0][RTW89_UKRAINE][0] = 40, + [0][1][1][0][RTW89_MEXICO][0] = 50, + [0][1][1][0][RTW89_CN][0] = 46, + [0][1][1][0][RTW89_QATAR][0] = 46, + [0][1][1][0][RTW89_UK][0] = 46, + [0][1][1][0][RTW89_FCC][2] = 68, + [0][1][1][0][RTW89_ETSI][2] = 46, + [0][1][1][0][RTW89_MKK][2] = 48, + [0][1][1][0][RTW89_IC][2] = 40, + [0][1][1][0][RTW89_KCC][2] = 64, + [0][1][1][0][RTW89_ACMA][2] = 46, + [0][1][1][0][RTW89_CHILE][2] = 32, + [0][1][1][0][RTW89_UKRAINE][2] = 40, + [0][1][1][0][RTW89_MEXICO][2] = 50, + [0][1][1][0][RTW89_CN][2] = 46, + [0][1][1][0][RTW89_QATAR][2] = 46, + [0][1][1][0][RTW89_UK][2] = 46, + [0][1][1][0][RTW89_FCC][4] = 68, + [0][1][1][0][RTW89_ETSI][4] = 46, + [0][1][1][0][RTW89_MKK][4] = 48, + [0][1][1][0][RTW89_IC][4] = 40, + [0][1][1][0][RTW89_KCC][4] = 64, + [0][1][1][0][RTW89_ACMA][4] = 46, + [0][1][1][0][RTW89_CHILE][4] = 30, + [0][1][1][0][RTW89_UKRAINE][4] = 40, + [0][1][1][0][RTW89_MEXICO][4] = 50, + [0][1][1][0][RTW89_CN][4] = 46, + [0][1][1][0][RTW89_QATAR][4] = 46, + [0][1][1][0][RTW89_UK][4] = 46, + [0][1][1][0][RTW89_FCC][6] = 68, + [0][1][1][0][RTW89_ETSI][6] = 46, + [0][1][1][0][RTW89_MKK][6] = 48, + [0][1][1][0][RTW89_IC][6] = 40, + [0][1][1][0][RTW89_KCC][6] = 38, + [0][1][1][0][RTW89_ACMA][6] = 46, + [0][1][1][0][RTW89_CHILE][6] = 30, + [0][1][1][0][RTW89_UKRAINE][6] = 40, + [0][1][1][0][RTW89_MEXICO][6] = 50, + [0][1][1][0][RTW89_CN][6] = 46, + [0][1][1][0][RTW89_QATAR][6] = 46, + [0][1][1][0][RTW89_UK][6] = 46, + [0][1][1][0][RTW89_FCC][8] = 68, + [0][1][1][0][RTW89_ETSI][8] = 46, + [0][1][1][0][RTW89_MKK][8] = 48, + [0][1][1][0][RTW89_IC][8] = 52, + [0][1][1][0][RTW89_KCC][8] = 64, + [0][1][1][0][RTW89_ACMA][8] = 46, + [0][1][1][0][RTW89_CHILE][8] = 52, + [0][1][1][0][RTW89_UKRAINE][8] = 40, + [0][1][1][0][RTW89_MEXICO][8] = 68, + [0][1][1][0][RTW89_CN][8] = 46, + [0][1][1][0][RTW89_QATAR][8] = 46, + [0][1][1][0][RTW89_UK][8] = 46, + [0][1][1][0][RTW89_FCC][10] = 68, + [0][1][1][0][RTW89_ETSI][10] = 46, + [0][1][1][0][RTW89_MKK][10] = 48, + [0][1][1][0][RTW89_IC][10] = 52, + [0][1][1][0][RTW89_KCC][10] = 64, + [0][1][1][0][RTW89_ACMA][10] = 46, + [0][1][1][0][RTW89_CHILE][10] = 52, + [0][1][1][0][RTW89_UKRAINE][10] = 40, + [0][1][1][0][RTW89_MEXICO][10] = 68, + [0][1][1][0][RTW89_CN][10] = 46, + [0][1][1][0][RTW89_QATAR][10] = 46, + [0][1][1][0][RTW89_UK][10] = 46, + [0][1][1][0][RTW89_FCC][12] = 68, + [0][1][1][0][RTW89_ETSI][12] = 46, + [0][1][1][0][RTW89_MKK][12] = 48, + [0][1][1][0][RTW89_IC][12] = 52, + [0][1][1][0][RTW89_KCC][12] = 64, + [0][1][1][0][RTW89_ACMA][12] = 46, + [0][1][1][0][RTW89_CHILE][12] = 52, + [0][1][1][0][RTW89_UKRAINE][12] = 40, + [0][1][1][0][RTW89_MEXICO][12] = 68, + [0][1][1][0][RTW89_CN][12] = 46, + [0][1][1][0][RTW89_QATAR][12] = 46, + [0][1][1][0][RTW89_UK][12] = 46, + [0][1][1][0][RTW89_FCC][14] = 68, + [0][1][1][0][RTW89_ETSI][14] = 46, + [0][1][1][0][RTW89_MKK][14] = 48, + [0][1][1][0][RTW89_IC][14] = 52, + [0][1][1][0][RTW89_KCC][14] = 64, + [0][1][1][0][RTW89_ACMA][14] = 46, + [0][1][1][0][RTW89_CHILE][14] = 52, + [0][1][1][0][RTW89_UKRAINE][14] = 40, + [0][1][1][0][RTW89_MEXICO][14] = 68, + [0][1][1][0][RTW89_CN][14] = 46, + [0][1][1][0][RTW89_QATAR][14] = 46, + [0][1][1][0][RTW89_UK][14] = 46, + [0][1][1][0][RTW89_FCC][15] = 66, + [0][1][1][0][RTW89_ETSI][15] = 46, + [0][1][1][0][RTW89_MKK][15] = 68, + [0][1][1][0][RTW89_IC][15] = 66, + [0][1][1][0][RTW89_KCC][15] = 62, + [0][1][1][0][RTW89_ACMA][15] = 46, + [0][1][1][0][RTW89_CHILE][15] = 48, + [0][1][1][0][RTW89_UKRAINE][15] = 40, + [0][1][1][0][RTW89_MEXICO][15] = 66, + [0][1][1][0][RTW89_CN][15] = 127, + [0][1][1][0][RTW89_QATAR][15] = 46, + [0][1][1][0][RTW89_UK][15] = 46, + [0][1][1][0][RTW89_FCC][17] = 68, + [0][1][1][0][RTW89_ETSI][17] = 46, + [0][1][1][0][RTW89_MKK][17] = 70, + [0][1][1][0][RTW89_IC][17] = 68, + [0][1][1][0][RTW89_KCC][17] = 62, + [0][1][1][0][RTW89_ACMA][17] = 46, + [0][1][1][0][RTW89_CHILE][17] = 48, + [0][1][1][0][RTW89_UKRAINE][17] = 40, + [0][1][1][0][RTW89_MEXICO][17] = 68, + [0][1][1][0][RTW89_CN][17] = 127, + [0][1][1][0][RTW89_QATAR][17] = 46, + [0][1][1][0][RTW89_UK][17] = 46, + [0][1][1][0][RTW89_FCC][19] = 68, + [0][1][1][0][RTW89_ETSI][19] = 46, + [0][1][1][0][RTW89_MKK][19] = 70, + [0][1][1][0][RTW89_IC][19] = 68, + [0][1][1][0][RTW89_KCC][19] = 62, + [0][1][1][0][RTW89_ACMA][19] = 46, + [0][1][1][0][RTW89_CHILE][19] = 48, + [0][1][1][0][RTW89_UKRAINE][19] = 40, + [0][1][1][0][RTW89_MEXICO][19] = 68, + [0][1][1][0][RTW89_CN][19] = 127, + [0][1][1][0][RTW89_QATAR][19] = 46, + [0][1][1][0][RTW89_UK][19] = 46, + [0][1][1][0][RTW89_FCC][21] = 68, + [0][1][1][0][RTW89_ETSI][21] = 46, + [0][1][1][0][RTW89_MKK][21] = 70, + [0][1][1][0][RTW89_IC][21] = 68, + [0][1][1][0][RTW89_KCC][21] = 62, + [0][1][1][0][RTW89_ACMA][21] = 46, + [0][1][1][0][RTW89_CHILE][21] = 48, + [0][1][1][0][RTW89_UKRAINE][21] = 40, + [0][1][1][0][RTW89_MEXICO][21] = 68, + [0][1][1][0][RTW89_CN][21] = 127, + [0][1][1][0][RTW89_QATAR][21] = 46, + [0][1][1][0][RTW89_UK][21] = 46, + [0][1][1][0][RTW89_FCC][23] = 68, + [0][1][1][0][RTW89_ETSI][23] = 46, + [0][1][1][0][RTW89_MKK][23] = 70, + [0][1][1][0][RTW89_IC][23] = 68, + [0][1][1][0][RTW89_KCC][23] = 62, + [0][1][1][0][RTW89_ACMA][23] = 46, + [0][1][1][0][RTW89_CHILE][23] = 48, + [0][1][1][0][RTW89_UKRAINE][23] = 40, + [0][1][1][0][RTW89_MEXICO][23] = 68, + [0][1][1][0][RTW89_CN][23] = 127, + [0][1][1][0][RTW89_QATAR][23] = 46, + [0][1][1][0][RTW89_UK][23] = 46, + [0][1][1][0][RTW89_FCC][25] = 68, + [0][1][1][0][RTW89_ETSI][25] = 46, + [0][1][1][0][RTW89_MKK][25] = 68, + [0][1][1][0][RTW89_IC][25] = 127, + [0][1][1][0][RTW89_KCC][25] = 62, + [0][1][1][0][RTW89_ACMA][25] = 127, + [0][1][1][0][RTW89_CHILE][25] = 48, + [0][1][1][0][RTW89_UKRAINE][25] = 40, + [0][1][1][0][RTW89_MEXICO][25] = 68, + [0][1][1][0][RTW89_CN][25] = 127, + [0][1][1][0][RTW89_QATAR][25] = 46, + [0][1][1][0][RTW89_UK][25] = 46, + [0][1][1][0][RTW89_FCC][27] = 68, + [0][1][1][0][RTW89_ETSI][27] = 46, + [0][1][1][0][RTW89_MKK][27] = 70, + [0][1][1][0][RTW89_IC][27] = 127, + [0][1][1][0][RTW89_KCC][27] = 62, + [0][1][1][0][RTW89_ACMA][27] = 127, + [0][1][1][0][RTW89_CHILE][27] = 50, + [0][1][1][0][RTW89_UKRAINE][27] = 40, + [0][1][1][0][RTW89_MEXICO][27] = 68, + [0][1][1][0][RTW89_CN][27] = 127, + [0][1][1][0][RTW89_QATAR][27] = 46, + [0][1][1][0][RTW89_UK][27] = 46, + [0][1][1][0][RTW89_FCC][29] = 68, + [0][1][1][0][RTW89_ETSI][29] = 46, + [0][1][1][0][RTW89_MKK][29] = 70, + [0][1][1][0][RTW89_IC][29] = 127, + [0][1][1][0][RTW89_KCC][29] = 62, + [0][1][1][0][RTW89_ACMA][29] = 127, + [0][1][1][0][RTW89_CHILE][29] = 50, + [0][1][1][0][RTW89_UKRAINE][29] = 40, + [0][1][1][0][RTW89_MEXICO][29] = 68, + [0][1][1][0][RTW89_CN][29] = 127, + [0][1][1][0][RTW89_QATAR][29] = 46, + [0][1][1][0][RTW89_UK][29] = 46, + [0][1][1][0][RTW89_FCC][31] = 68, + [0][1][1][0][RTW89_ETSI][31] = 46, + [0][1][1][0][RTW89_MKK][31] = 70, + [0][1][1][0][RTW89_IC][31] = 68, + [0][1][1][0][RTW89_KCC][31] = 62, + [0][1][1][0][RTW89_ACMA][31] = 46, + [0][1][1][0][RTW89_CHILE][31] = 50, + [0][1][1][0][RTW89_UKRAINE][31] = 40, + [0][1][1][0][RTW89_MEXICO][31] = 68, + [0][1][1][0][RTW89_CN][31] = 127, + [0][1][1][0][RTW89_QATAR][31] = 46, + [0][1][1][0][RTW89_UK][31] = 46, + [0][1][1][0][RTW89_FCC][33] = 68, + [0][1][1][0][RTW89_ETSI][33] = 46, + [0][1][1][0][RTW89_MKK][33] = 70, + [0][1][1][0][RTW89_IC][33] = 68, + [0][1][1][0][RTW89_KCC][33] = 62, + [0][1][1][0][RTW89_ACMA][33] = 46, + [0][1][1][0][RTW89_CHILE][33] = 50, + [0][1][1][0][RTW89_UKRAINE][33] = 40, + [0][1][1][0][RTW89_MEXICO][33] = 68, + [0][1][1][0][RTW89_CN][33] = 127, + [0][1][1][0][RTW89_QATAR][33] = 46, + [0][1][1][0][RTW89_UK][33] = 46, + [0][1][1][0][RTW89_FCC][35] = 66, + [0][1][1][0][RTW89_ETSI][35] = 46, + [0][1][1][0][RTW89_MKK][35] = 70, + [0][1][1][0][RTW89_IC][35] = 66, + [0][1][1][0][RTW89_KCC][35] = 62, + [0][1][1][0][RTW89_ACMA][35] = 46, + [0][1][1][0][RTW89_CHILE][35] = 50, + [0][1][1][0][RTW89_UKRAINE][35] = 40, + [0][1][1][0][RTW89_MEXICO][35] = 66, + [0][1][1][0][RTW89_CN][35] = 127, + [0][1][1][0][RTW89_QATAR][35] = 46, + [0][1][1][0][RTW89_UK][35] = 46, + [0][1][1][0][RTW89_FCC][37] = 68, + [0][1][1][0][RTW89_ETSI][37] = 127, + [0][1][1][0][RTW89_MKK][37] = 70, + [0][1][1][0][RTW89_IC][37] = 68, + [0][1][1][0][RTW89_KCC][37] = 62, + [0][1][1][0][RTW89_ACMA][37] = 70, + [0][1][1][0][RTW89_CHILE][37] = 50, + [0][1][1][0][RTW89_UKRAINE][37] = 127, + [0][1][1][0][RTW89_MEXICO][37] = 68, + [0][1][1][0][RTW89_CN][37] = 127, + [0][1][1][0][RTW89_QATAR][37] = 127, + [0][1][1][0][RTW89_UK][37] = 76, + [0][1][1][0][RTW89_FCC][38] = 78, + [0][1][1][0][RTW89_ETSI][38] = 16, + [0][1][1][0][RTW89_MKK][38] = 127, + [0][1][1][0][RTW89_IC][38] = 78, + [0][1][1][0][RTW89_KCC][38] = 60, + [0][1][1][0][RTW89_ACMA][38] = 72, + [0][1][1][0][RTW89_CHILE][38] = 48, + [0][1][1][0][RTW89_UKRAINE][38] = 16, + [0][1][1][0][RTW89_MEXICO][38] = 78, + [0][1][1][0][RTW89_CN][38] = 76, + [0][1][1][0][RTW89_QATAR][38] = 16, + [0][1][1][0][RTW89_UK][38] = 46, + [0][1][1][0][RTW89_FCC][40] = 78, + [0][1][1][0][RTW89_ETSI][40] = 16, + [0][1][1][0][RTW89_MKK][40] = 127, + [0][1][1][0][RTW89_IC][40] = 78, + [0][1][1][0][RTW89_KCC][40] = 60, + [0][1][1][0][RTW89_ACMA][40] = 72, + [0][1][1][0][RTW89_CHILE][40] = 48, + [0][1][1][0][RTW89_UKRAINE][40] = 16, + [0][1][1][0][RTW89_MEXICO][40] = 78, + [0][1][1][0][RTW89_CN][40] = 76, + [0][1][1][0][RTW89_QATAR][40] = 16, + [0][1][1][0][RTW89_UK][40] = 46, + [0][1][1][0][RTW89_FCC][42] = 78, + [0][1][1][0][RTW89_ETSI][42] = 16, + [0][1][1][0][RTW89_MKK][42] = 127, + [0][1][1][0][RTW89_IC][42] = 78, + [0][1][1][0][RTW89_KCC][42] = 60, + [0][1][1][0][RTW89_ACMA][42] = 76, + [0][1][1][0][RTW89_CHILE][42] = 48, + [0][1][1][0][RTW89_UKRAINE][42] = 16, + [0][1][1][0][RTW89_MEXICO][42] = 78, + [0][1][1][0][RTW89_CN][42] = 76, + [0][1][1][0][RTW89_QATAR][42] = 16, + [0][1][1][0][RTW89_UK][42] = 46, + [0][1][1][0][RTW89_FCC][44] = 78, + [0][1][1][0][RTW89_ETSI][44] = 16, + [0][1][1][0][RTW89_MKK][44] = 127, + [0][1][1][0][RTW89_IC][44] = 78, + [0][1][1][0][RTW89_KCC][44] = 60, + [0][1][1][0][RTW89_ACMA][44] = 76, + [0][1][1][0][RTW89_CHILE][44] = 48, + [0][1][1][0][RTW89_UKRAINE][44] = 16, + [0][1][1][0][RTW89_MEXICO][44] = 78, + [0][1][1][0][RTW89_CN][44] = 76, + [0][1][1][0][RTW89_QATAR][44] = 16, + [0][1][1][0][RTW89_UK][44] = 46, + [0][1][1][0][RTW89_FCC][46] = 78, + [0][1][1][0][RTW89_ETSI][46] = 16, + [0][1][1][0][RTW89_MKK][46] = 127, + [0][1][1][0][RTW89_IC][46] = 78, + [0][1][1][0][RTW89_KCC][46] = 60, + [0][1][1][0][RTW89_ACMA][46] = 76, + [0][1][1][0][RTW89_CHILE][46] = 48, + [0][1][1][0][RTW89_UKRAINE][46] = 16, + [0][1][1][0][RTW89_MEXICO][46] = 78, + [0][1][1][0][RTW89_CN][46] = 76, + [0][1][1][0][RTW89_QATAR][46] = 16, + [0][1][1][0][RTW89_UK][46] = 46, + [0][1][1][0][RTW89_FCC][48] = 56, + [0][1][1][0][RTW89_ETSI][48] = 127, + [0][1][1][0][RTW89_MKK][48] = 127, + [0][1][1][0][RTW89_IC][48] = 127, + [0][1][1][0][RTW89_KCC][48] = 127, + [0][1][1][0][RTW89_ACMA][48] = 127, + [0][1][1][0][RTW89_CHILE][48] = 127, + [0][1][1][0][RTW89_UKRAINE][48] = 127, + [0][1][1][0][RTW89_MEXICO][48] = 127, + [0][1][1][0][RTW89_CN][48] = 127, + [0][1][1][0][RTW89_QATAR][48] = 127, + [0][1][1][0][RTW89_UK][48] = 127, + [0][1][1][0][RTW89_FCC][50] = 56, + [0][1][1][0][RTW89_ETSI][50] = 127, + [0][1][1][0][RTW89_MKK][50] = 127, + [0][1][1][0][RTW89_IC][50] = 127, + [0][1][1][0][RTW89_KCC][50] = 127, + [0][1][1][0][RTW89_ACMA][50] = 127, + [0][1][1][0][RTW89_CHILE][50] = 127, + [0][1][1][0][RTW89_UKRAINE][50] = 127, + [0][1][1][0][RTW89_MEXICO][50] = 127, + [0][1][1][0][RTW89_CN][50] = 127, + [0][1][1][0][RTW89_QATAR][50] = 127, + [0][1][1][0][RTW89_UK][50] = 127, + [0][1][1][0][RTW89_FCC][52] = 56, + [0][1][1][0][RTW89_ETSI][52] = 127, + [0][1][1][0][RTW89_MKK][52] = 127, + [0][1][1][0][RTW89_IC][52] = 127, + [0][1][1][0][RTW89_KCC][52] = 127, + [0][1][1][0][RTW89_ACMA][52] = 127, + [0][1][1][0][RTW89_CHILE][52] = 127, + [0][1][1][0][RTW89_UKRAINE][52] = 127, + [0][1][1][0][RTW89_MEXICO][52] = 127, + [0][1][1][0][RTW89_CN][52] = 127, + [0][1][1][0][RTW89_QATAR][52] = 127, + [0][1][1][0][RTW89_UK][52] = 127, + [0][0][2][0][RTW89_FCC][0] = 78, + [0][0][2][0][RTW89_ETSI][0] = 60, + [0][0][2][0][RTW89_MKK][0] = 62, + [0][0][2][0][RTW89_IC][0] = 64, + [0][0][2][0][RTW89_KCC][0] = 74, + [0][0][2][0][RTW89_ACMA][0] = 60, + [0][0][2][0][RTW89_CHILE][0] = 42, + [0][0][2][0][RTW89_UKRAINE][0] = 52, + [0][0][2][0][RTW89_MEXICO][0] = 62, + [0][0][2][0][RTW89_CN][0] = 60, + [0][0][2][0][RTW89_QATAR][0] = 60, + [0][0][2][0][RTW89_UK][0] = 60, + [0][0][2][0][RTW89_FCC][2] = 78, + [0][0][2][0][RTW89_ETSI][2] = 60, + [0][0][2][0][RTW89_MKK][2] = 62, + [0][0][2][0][RTW89_IC][2] = 64, + [0][0][2][0][RTW89_KCC][2] = 74, + [0][0][2][0][RTW89_ACMA][2] = 60, + [0][0][2][0][RTW89_CHILE][2] = 42, + [0][0][2][0][RTW89_UKRAINE][2] = 52, + [0][0][2][0][RTW89_MEXICO][2] = 62, + [0][0][2][0][RTW89_CN][2] = 60, + [0][0][2][0][RTW89_QATAR][2] = 60, + [0][0][2][0][RTW89_UK][2] = 60, + [0][0][2][0][RTW89_FCC][4] = 78, + [0][0][2][0][RTW89_ETSI][4] = 60, + [0][0][2][0][RTW89_MKK][4] = 62, + [0][0][2][0][RTW89_IC][4] = 64, + [0][0][2][0][RTW89_KCC][4] = 74, + [0][0][2][0][RTW89_ACMA][4] = 60, + [0][0][2][0][RTW89_CHILE][4] = 42, + [0][0][2][0][RTW89_UKRAINE][4] = 52, + [0][0][2][0][RTW89_MEXICO][4] = 62, + [0][0][2][0][RTW89_CN][4] = 60, + [0][0][2][0][RTW89_QATAR][4] = 60, + [0][0][2][0][RTW89_UK][4] = 60, + [0][0][2][0][RTW89_FCC][6] = 78, + [0][0][2][0][RTW89_ETSI][6] = 60, + [0][0][2][0][RTW89_MKK][6] = 62, + [0][0][2][0][RTW89_IC][6] = 64, + [0][0][2][0][RTW89_KCC][6] = 50, + [0][0][2][0][RTW89_ACMA][6] = 60, + [0][0][2][0][RTW89_CHILE][6] = 42, + [0][0][2][0][RTW89_UKRAINE][6] = 52, + [0][0][2][0][RTW89_MEXICO][6] = 62, + [0][0][2][0][RTW89_CN][6] = 60, + [0][0][2][0][RTW89_QATAR][6] = 60, + [0][0][2][0][RTW89_UK][6] = 60, + [0][0][2][0][RTW89_FCC][8] = 78, + [0][0][2][0][RTW89_ETSI][8] = 60, + [0][0][2][0][RTW89_MKK][8] = 62, + [0][0][2][0][RTW89_IC][8] = 64, + [0][0][2][0][RTW89_KCC][8] = 74, + [0][0][2][0][RTW89_ACMA][8] = 60, + [0][0][2][0][RTW89_CHILE][8] = 66, + [0][0][2][0][RTW89_UKRAINE][8] = 52, + [0][0][2][0][RTW89_MEXICO][8] = 78, + [0][0][2][0][RTW89_CN][8] = 60, + [0][0][2][0][RTW89_QATAR][8] = 60, + [0][0][2][0][RTW89_UK][8] = 60, + [0][0][2][0][RTW89_FCC][10] = 78, + [0][0][2][0][RTW89_ETSI][10] = 60, + [0][0][2][0][RTW89_MKK][10] = 62, + [0][0][2][0][RTW89_IC][10] = 64, + [0][0][2][0][RTW89_KCC][10] = 74, + [0][0][2][0][RTW89_ACMA][10] = 60, + [0][0][2][0][RTW89_CHILE][10] = 66, + [0][0][2][0][RTW89_UKRAINE][10] = 52, + [0][0][2][0][RTW89_MEXICO][10] = 78, + [0][0][2][0][RTW89_CN][10] = 60, + [0][0][2][0][RTW89_QATAR][10] = 60, + [0][0][2][0][RTW89_UK][10] = 60, + [0][0][2][0][RTW89_FCC][12] = 78, + [0][0][2][0][RTW89_ETSI][12] = 60, + [0][0][2][0][RTW89_MKK][12] = 62, + [0][0][2][0][RTW89_IC][12] = 64, + [0][0][2][0][RTW89_KCC][12] = 74, + [0][0][2][0][RTW89_ACMA][12] = 60, + [0][0][2][0][RTW89_CHILE][12] = 66, + [0][0][2][0][RTW89_UKRAINE][12] = 52, + [0][0][2][0][RTW89_MEXICO][12] = 78, + [0][0][2][0][RTW89_CN][12] = 60, + [0][0][2][0][RTW89_QATAR][12] = 60, + [0][0][2][0][RTW89_UK][12] = 60, + [0][0][2][0][RTW89_FCC][14] = 78, + [0][0][2][0][RTW89_ETSI][14] = 60, + [0][0][2][0][RTW89_MKK][14] = 62, + [0][0][2][0][RTW89_IC][14] = 64, + [0][0][2][0][RTW89_KCC][14] = 74, + [0][0][2][0][RTW89_ACMA][14] = 60, + [0][0][2][0][RTW89_CHILE][14] = 66, + [0][0][2][0][RTW89_UKRAINE][14] = 52, + [0][0][2][0][RTW89_MEXICO][14] = 78, + [0][0][2][0][RTW89_CN][14] = 60, + [0][0][2][0][RTW89_QATAR][14] = 60, + [0][0][2][0][RTW89_UK][14] = 60, + [0][0][2][0][RTW89_FCC][15] = 74, + [0][0][2][0][RTW89_ETSI][15] = 60, + [0][0][2][0][RTW89_MKK][15] = 76, + [0][0][2][0][RTW89_IC][15] = 74, + [0][0][2][0][RTW89_KCC][15] = 74, + [0][0][2][0][RTW89_ACMA][15] = 60, + [0][0][2][0][RTW89_CHILE][15] = 64, + [0][0][2][0][RTW89_UKRAINE][15] = 52, + [0][0][2][0][RTW89_MEXICO][15] = 74, + [0][0][2][0][RTW89_CN][15] = 127, + [0][0][2][0][RTW89_QATAR][15] = 60, + [0][0][2][0][RTW89_UK][15] = 60, + [0][0][2][0][RTW89_FCC][17] = 78, + [0][0][2][0][RTW89_ETSI][17] = 60, + [0][0][2][0][RTW89_MKK][17] = 76, + [0][0][2][0][RTW89_IC][17] = 78, + [0][0][2][0][RTW89_KCC][17] = 74, + [0][0][2][0][RTW89_ACMA][17] = 60, + [0][0][2][0][RTW89_CHILE][17] = 64, + [0][0][2][0][RTW89_UKRAINE][17] = 52, + [0][0][2][0][RTW89_MEXICO][17] = 78, + [0][0][2][0][RTW89_CN][17] = 127, + [0][0][2][0][RTW89_QATAR][17] = 60, + [0][0][2][0][RTW89_UK][17] = 60, + [0][0][2][0][RTW89_FCC][19] = 78, + [0][0][2][0][RTW89_ETSI][19] = 60, + [0][0][2][0][RTW89_MKK][19] = 76, + [0][0][2][0][RTW89_IC][19] = 78, + [0][0][2][0][RTW89_KCC][19] = 74, + [0][0][2][0][RTW89_ACMA][19] = 60, + [0][0][2][0][RTW89_CHILE][19] = 64, + [0][0][2][0][RTW89_UKRAINE][19] = 52, + [0][0][2][0][RTW89_MEXICO][19] = 78, + [0][0][2][0][RTW89_CN][19] = 127, + [0][0][2][0][RTW89_QATAR][19] = 60, + [0][0][2][0][RTW89_UK][19] = 60, + [0][0][2][0][RTW89_FCC][21] = 78, + [0][0][2][0][RTW89_ETSI][21] = 60, + [0][0][2][0][RTW89_MKK][21] = 76, + [0][0][2][0][RTW89_IC][21] = 78, + [0][0][2][0][RTW89_KCC][21] = 74, + [0][0][2][0][RTW89_ACMA][21] = 60, + [0][0][2][0][RTW89_CHILE][21] = 66, + [0][0][2][0][RTW89_UKRAINE][21] = 52, + [0][0][2][0][RTW89_MEXICO][21] = 78, + [0][0][2][0][RTW89_CN][21] = 127, + [0][0][2][0][RTW89_QATAR][21] = 60, + [0][0][2][0][RTW89_UK][21] = 60, + [0][0][2][0][RTW89_FCC][23] = 78, + [0][0][2][0][RTW89_ETSI][23] = 60, + [0][0][2][0][RTW89_MKK][23] = 76, + [0][0][2][0][RTW89_IC][23] = 78, + [0][0][2][0][RTW89_KCC][23] = 74, + [0][0][2][0][RTW89_ACMA][23] = 60, + [0][0][2][0][RTW89_CHILE][23] = 66, + [0][0][2][0][RTW89_UKRAINE][23] = 52, + [0][0][2][0][RTW89_MEXICO][23] = 78, + [0][0][2][0][RTW89_CN][23] = 127, + [0][0][2][0][RTW89_QATAR][23] = 60, + [0][0][2][0][RTW89_UK][23] = 60, + [0][0][2][0][RTW89_FCC][25] = 78, + [0][0][2][0][RTW89_ETSI][25] = 60, + [0][0][2][0][RTW89_MKK][25] = 76, + [0][0][2][0][RTW89_IC][25] = 127, + [0][0][2][0][RTW89_KCC][25] = 74, + [0][0][2][0][RTW89_ACMA][25] = 127, + [0][0][2][0][RTW89_CHILE][25] = 66, + [0][0][2][0][RTW89_UKRAINE][25] = 52, + [0][0][2][0][RTW89_MEXICO][25] = 78, + [0][0][2][0][RTW89_CN][25] = 127, + [0][0][2][0][RTW89_QATAR][25] = 60, + [0][0][2][0][RTW89_UK][25] = 60, + [0][0][2][0][RTW89_FCC][27] = 78, + [0][0][2][0][RTW89_ETSI][27] = 60, + [0][0][2][0][RTW89_MKK][27] = 76, + [0][0][2][0][RTW89_IC][27] = 127, + [0][0][2][0][RTW89_KCC][27] = 74, + [0][0][2][0][RTW89_ACMA][27] = 127, + [0][0][2][0][RTW89_CHILE][27] = 64, + [0][0][2][0][RTW89_UKRAINE][27] = 52, + [0][0][2][0][RTW89_MEXICO][27] = 78, + [0][0][2][0][RTW89_CN][27] = 127, + [0][0][2][0][RTW89_QATAR][27] = 60, + [0][0][2][0][RTW89_UK][27] = 60, + [0][0][2][0][RTW89_FCC][29] = 78, + [0][0][2][0][RTW89_ETSI][29] = 60, + [0][0][2][0][RTW89_MKK][29] = 76, + [0][0][2][0][RTW89_IC][29] = 127, + [0][0][2][0][RTW89_KCC][29] = 74, + [0][0][2][0][RTW89_ACMA][29] = 127, + [0][0][2][0][RTW89_CHILE][29] = 64, + [0][0][2][0][RTW89_UKRAINE][29] = 52, + [0][0][2][0][RTW89_MEXICO][29] = 78, + [0][0][2][0][RTW89_CN][29] = 127, + [0][0][2][0][RTW89_QATAR][29] = 60, + [0][0][2][0][RTW89_UK][29] = 60, + [0][0][2][0][RTW89_FCC][31] = 78, + [0][0][2][0][RTW89_ETSI][31] = 60, + [0][0][2][0][RTW89_MKK][31] = 76, + [0][0][2][0][RTW89_IC][31] = 78, + [0][0][2][0][RTW89_KCC][31] = 74, + [0][0][2][0][RTW89_ACMA][31] = 60, + [0][0][2][0][RTW89_CHILE][31] = 64, + [0][0][2][0][RTW89_UKRAINE][31] = 52, + [0][0][2][0][RTW89_MEXICO][31] = 78, + [0][0][2][0][RTW89_CN][31] = 127, + [0][0][2][0][RTW89_QATAR][31] = 60, + [0][0][2][0][RTW89_UK][31] = 60, + [0][0][2][0][RTW89_FCC][33] = 78, + [0][0][2][0][RTW89_ETSI][33] = 60, + [0][0][2][0][RTW89_MKK][33] = 76, + [0][0][2][0][RTW89_IC][33] = 78, + [0][0][2][0][RTW89_KCC][33] = 74, + [0][0][2][0][RTW89_ACMA][33] = 60, + [0][0][2][0][RTW89_CHILE][33] = 64, + [0][0][2][0][RTW89_UKRAINE][33] = 52, + [0][0][2][0][RTW89_MEXICO][33] = 78, + [0][0][2][0][RTW89_CN][33] = 127, + [0][0][2][0][RTW89_QATAR][33] = 60, + [0][0][2][0][RTW89_UK][33] = 60, + [0][0][2][0][RTW89_FCC][35] = 70, + [0][0][2][0][RTW89_ETSI][35] = 60, + [0][0][2][0][RTW89_MKK][35] = 76, + [0][0][2][0][RTW89_IC][35] = 70, + [0][0][2][0][RTW89_KCC][35] = 74, + [0][0][2][0][RTW89_ACMA][35] = 60, + [0][0][2][0][RTW89_CHILE][35] = 64, + [0][0][2][0][RTW89_UKRAINE][35] = 52, + [0][0][2][0][RTW89_MEXICO][35] = 70, + [0][0][2][0][RTW89_CN][35] = 127, + [0][0][2][0][RTW89_QATAR][35] = 60, + [0][0][2][0][RTW89_UK][35] = 60, + [0][0][2][0][RTW89_FCC][37] = 78, + [0][0][2][0][RTW89_ETSI][37] = 127, + [0][0][2][0][RTW89_MKK][37] = 76, + [0][0][2][0][RTW89_IC][37] = 78, + [0][0][2][0][RTW89_KCC][37] = 74, + [0][0][2][0][RTW89_ACMA][37] = 76, + [0][0][2][0][RTW89_CHILE][37] = 64, + [0][0][2][0][RTW89_UKRAINE][37] = 127, + [0][0][2][0][RTW89_MEXICO][37] = 78, + [0][0][2][0][RTW89_CN][37] = 127, + [0][0][2][0][RTW89_QATAR][37] = 127, + [0][0][2][0][RTW89_UK][37] = 74, + [0][0][2][0][RTW89_FCC][38] = 78, + [0][0][2][0][RTW89_ETSI][38] = 28, + [0][0][2][0][RTW89_MKK][38] = 127, + [0][0][2][0][RTW89_IC][38] = 78, + [0][0][2][0][RTW89_KCC][38] = 72, + [0][0][2][0][RTW89_ACMA][38] = 76, + [0][0][2][0][RTW89_CHILE][38] = 64, + [0][0][2][0][RTW89_UKRAINE][38] = 28, + [0][0][2][0][RTW89_MEXICO][38] = 78, + [0][0][2][0][RTW89_CN][38] = 76, + [0][0][2][0][RTW89_QATAR][38] = 28, + [0][0][2][0][RTW89_UK][38] = 60, + [0][0][2][0][RTW89_FCC][40] = 78, + [0][0][2][0][RTW89_ETSI][40] = 28, + [0][0][2][0][RTW89_MKK][40] = 127, + [0][0][2][0][RTW89_IC][40] = 78, + [0][0][2][0][RTW89_KCC][40] = 72, + [0][0][2][0][RTW89_ACMA][40] = 76, + [0][0][2][0][RTW89_CHILE][40] = 64, + [0][0][2][0][RTW89_UKRAINE][40] = 28, + [0][0][2][0][RTW89_MEXICO][40] = 78, + [0][0][2][0][RTW89_CN][40] = 76, + [0][0][2][0][RTW89_QATAR][40] = 28, + [0][0][2][0][RTW89_UK][40] = 60, + [0][0][2][0][RTW89_FCC][42] = 78, + [0][0][2][0][RTW89_ETSI][42] = 28, + [0][0][2][0][RTW89_MKK][42] = 127, + [0][0][2][0][RTW89_IC][42] = 78, + [0][0][2][0][RTW89_KCC][42] = 72, + [0][0][2][0][RTW89_ACMA][42] = 76, + [0][0][2][0][RTW89_CHILE][42] = 64, + [0][0][2][0][RTW89_UKRAINE][42] = 28, + [0][0][2][0][RTW89_MEXICO][42] = 78, + [0][0][2][0][RTW89_CN][42] = 76, + [0][0][2][0][RTW89_QATAR][42] = 28, + [0][0][2][0][RTW89_UK][42] = 60, + [0][0][2][0][RTW89_FCC][44] = 78, + [0][0][2][0][RTW89_ETSI][44] = 28, + [0][0][2][0][RTW89_MKK][44] = 127, + [0][0][2][0][RTW89_IC][44] = 78, + [0][0][2][0][RTW89_KCC][44] = 72, + [0][0][2][0][RTW89_ACMA][44] = 76, + [0][0][2][0][RTW89_CHILE][44] = 66, + [0][0][2][0][RTW89_UKRAINE][44] = 28, + [0][0][2][0][RTW89_MEXICO][44] = 78, + [0][0][2][0][RTW89_CN][44] = 76, + [0][0][2][0][RTW89_QATAR][44] = 28, + [0][0][2][0][RTW89_UK][44] = 60, + [0][0][2][0][RTW89_FCC][46] = 78, + [0][0][2][0][RTW89_ETSI][46] = 28, + [0][0][2][0][RTW89_MKK][46] = 127, + [0][0][2][0][RTW89_IC][46] = 78, + [0][0][2][0][RTW89_KCC][46] = 72, + [0][0][2][0][RTW89_ACMA][46] = 76, + [0][0][2][0][RTW89_CHILE][46] = 66, + [0][0][2][0][RTW89_UKRAINE][46] = 28, + [0][0][2][0][RTW89_MEXICO][46] = 78, + [0][0][2][0][RTW89_CN][46] = 76, + [0][0][2][0][RTW89_QATAR][46] = 28, + [0][0][2][0][RTW89_UK][46] = 60, + [0][0][2][0][RTW89_FCC][48] = 78, + [0][0][2][0][RTW89_ETSI][48] = 127, + [0][0][2][0][RTW89_MKK][48] = 127, + [0][0][2][0][RTW89_IC][48] = 127, + [0][0][2][0][RTW89_KCC][48] = 127, + [0][0][2][0][RTW89_ACMA][48] = 127, + [0][0][2][0][RTW89_CHILE][48] = 127, + [0][0][2][0][RTW89_UKRAINE][48] = 127, + [0][0][2][0][RTW89_MEXICO][48] = 127, + [0][0][2][0][RTW89_CN][48] = 127, + [0][0][2][0][RTW89_QATAR][48] = 127, + [0][0][2][0][RTW89_UK][48] = 127, + [0][0][2][0][RTW89_FCC][50] = 78, + [0][0][2][0][RTW89_ETSI][50] = 127, + [0][0][2][0][RTW89_MKK][50] = 127, + [0][0][2][0][RTW89_IC][50] = 127, + [0][0][2][0][RTW89_KCC][50] = 127, + [0][0][2][0][RTW89_ACMA][50] = 127, + [0][0][2][0][RTW89_CHILE][50] = 127, + [0][0][2][0][RTW89_UKRAINE][50] = 127, + [0][0][2][0][RTW89_MEXICO][50] = 127, + [0][0][2][0][RTW89_CN][50] = 127, + [0][0][2][0][RTW89_QATAR][50] = 127, + [0][0][2][0][RTW89_UK][50] = 127, + [0][0][2][0][RTW89_FCC][52] = 78, + [0][0][2][0][RTW89_ETSI][52] = 127, + [0][0][2][0][RTW89_MKK][52] = 127, + [0][0][2][0][RTW89_IC][52] = 127, + [0][0][2][0][RTW89_KCC][52] = 127, + [0][0][2][0][RTW89_ACMA][52] = 127, + [0][0][2][0][RTW89_CHILE][52] = 127, + [0][0][2][0][RTW89_UKRAINE][52] = 127, + [0][0][2][0][RTW89_MEXICO][52] = 127, + [0][0][2][0][RTW89_CN][52] = 127, + [0][0][2][0][RTW89_QATAR][52] = 127, + [0][0][2][0][RTW89_UK][52] = 127, + [0][1][2][0][RTW89_FCC][0] = 70, + [0][1][2][0][RTW89_ETSI][0] = 48, + [0][1][2][0][RTW89_MKK][0] = 50, + [0][1][2][0][RTW89_IC][0] = 42, + [0][1][2][0][RTW89_KCC][0] = 62, + [0][1][2][0][RTW89_ACMA][0] = 48, + [0][1][2][0][RTW89_CHILE][0] = 30, + [0][1][2][0][RTW89_UKRAINE][0] = 40, + [0][1][2][0][RTW89_MEXICO][0] = 50, + [0][1][2][0][RTW89_CN][0] = 48, + [0][1][2][0][RTW89_QATAR][0] = 48, + [0][1][2][0][RTW89_UK][0] = 48, + [0][1][2][0][RTW89_FCC][2] = 70, + [0][1][2][0][RTW89_ETSI][2] = 48, + [0][1][2][0][RTW89_MKK][2] = 50, + [0][1][2][0][RTW89_IC][2] = 42, + [0][1][2][0][RTW89_KCC][2] = 62, + [0][1][2][0][RTW89_ACMA][2] = 48, + [0][1][2][0][RTW89_CHILE][2] = 30, + [0][1][2][0][RTW89_UKRAINE][2] = 40, + [0][1][2][0][RTW89_MEXICO][2] = 50, + [0][1][2][0][RTW89_CN][2] = 48, + [0][1][2][0][RTW89_QATAR][2] = 48, + [0][1][2][0][RTW89_UK][2] = 48, + [0][1][2][0][RTW89_FCC][4] = 70, + [0][1][2][0][RTW89_ETSI][4] = 48, + [0][1][2][0][RTW89_MKK][4] = 50, + [0][1][2][0][RTW89_IC][4] = 42, + [0][1][2][0][RTW89_KCC][4] = 62, + [0][1][2][0][RTW89_ACMA][4] = 48, + [0][1][2][0][RTW89_CHILE][4] = 30, + [0][1][2][0][RTW89_UKRAINE][4] = 40, + [0][1][2][0][RTW89_MEXICO][4] = 50, + [0][1][2][0][RTW89_CN][4] = 48, + [0][1][2][0][RTW89_QATAR][4] = 48, + [0][1][2][0][RTW89_UK][4] = 48, + [0][1][2][0][RTW89_FCC][6] = 70, + [0][1][2][0][RTW89_ETSI][6] = 48, + [0][1][2][0][RTW89_MKK][6] = 50, + [0][1][2][0][RTW89_IC][6] = 42, + [0][1][2][0][RTW89_KCC][6] = 34, + [0][1][2][0][RTW89_ACMA][6] = 48, + [0][1][2][0][RTW89_CHILE][6] = 30, + [0][1][2][0][RTW89_UKRAINE][6] = 40, + [0][1][2][0][RTW89_MEXICO][6] = 50, + [0][1][2][0][RTW89_CN][6] = 48, + [0][1][2][0][RTW89_QATAR][6] = 48, + [0][1][2][0][RTW89_UK][6] = 48, + [0][1][2][0][RTW89_FCC][8] = 70, + [0][1][2][0][RTW89_ETSI][8] = 48, + [0][1][2][0][RTW89_MKK][8] = 50, + [0][1][2][0][RTW89_IC][8] = 52, + [0][1][2][0][RTW89_KCC][8] = 62, + [0][1][2][0][RTW89_ACMA][8] = 48, + [0][1][2][0][RTW89_CHILE][8] = 50, + [0][1][2][0][RTW89_UKRAINE][8] = 40, + [0][1][2][0][RTW89_MEXICO][8] = 70, + [0][1][2][0][RTW89_CN][8] = 48, + [0][1][2][0][RTW89_QATAR][8] = 48, + [0][1][2][0][RTW89_UK][8] = 48, + [0][1][2][0][RTW89_FCC][10] = 70, + [0][1][2][0][RTW89_ETSI][10] = 48, + [0][1][2][0][RTW89_MKK][10] = 50, + [0][1][2][0][RTW89_IC][10] = 52, + [0][1][2][0][RTW89_KCC][10] = 62, + [0][1][2][0][RTW89_ACMA][10] = 48, + [0][1][2][0][RTW89_CHILE][10] = 50, + [0][1][2][0][RTW89_UKRAINE][10] = 40, + [0][1][2][0][RTW89_MEXICO][10] = 70, + [0][1][2][0][RTW89_CN][10] = 48, + [0][1][2][0][RTW89_QATAR][10] = 48, + [0][1][2][0][RTW89_UK][10] = 48, + [0][1][2][0][RTW89_FCC][12] = 70, + [0][1][2][0][RTW89_ETSI][12] = 48, + [0][1][2][0][RTW89_MKK][12] = 50, + [0][1][2][0][RTW89_IC][12] = 52, + [0][1][2][0][RTW89_KCC][12] = 62, + [0][1][2][0][RTW89_ACMA][12] = 48, + [0][1][2][0][RTW89_CHILE][12] = 50, + [0][1][2][0][RTW89_UKRAINE][12] = 40, + [0][1][2][0][RTW89_MEXICO][12] = 70, + [0][1][2][0][RTW89_CN][12] = 48, + [0][1][2][0][RTW89_QATAR][12] = 48, + [0][1][2][0][RTW89_UK][12] = 48, + [0][1][2][0][RTW89_FCC][14] = 70, + [0][1][2][0][RTW89_ETSI][14] = 48, + [0][1][2][0][RTW89_MKK][14] = 50, + [0][1][2][0][RTW89_IC][14] = 52, + [0][1][2][0][RTW89_KCC][14] = 62, + [0][1][2][0][RTW89_ACMA][14] = 48, + [0][1][2][0][RTW89_CHILE][14] = 50, + [0][1][2][0][RTW89_UKRAINE][14] = 40, + [0][1][2][0][RTW89_MEXICO][14] = 70, + [0][1][2][0][RTW89_CN][14] = 48, + [0][1][2][0][RTW89_QATAR][14] = 48, + [0][1][2][0][RTW89_UK][14] = 48, + [0][1][2][0][RTW89_FCC][15] = 68, + [0][1][2][0][RTW89_ETSI][15] = 48, + [0][1][2][0][RTW89_MKK][15] = 70, + [0][1][2][0][RTW89_IC][15] = 68, + [0][1][2][0][RTW89_KCC][15] = 62, + [0][1][2][0][RTW89_ACMA][15] = 48, + [0][1][2][0][RTW89_CHILE][15] = 48, + [0][1][2][0][RTW89_UKRAINE][15] = 40, + [0][1][2][0][RTW89_MEXICO][15] = 68, + [0][1][2][0][RTW89_CN][15] = 127, + [0][1][2][0][RTW89_QATAR][15] = 48, + [0][1][2][0][RTW89_UK][15] = 48, + [0][1][2][0][RTW89_FCC][17] = 70, + [0][1][2][0][RTW89_ETSI][17] = 48, + [0][1][2][0][RTW89_MKK][17] = 70, + [0][1][2][0][RTW89_IC][17] = 70, + [0][1][2][0][RTW89_KCC][17] = 62, + [0][1][2][0][RTW89_ACMA][17] = 48, + [0][1][2][0][RTW89_CHILE][17] = 48, + [0][1][2][0][RTW89_UKRAINE][17] = 40, + [0][1][2][0][RTW89_MEXICO][17] = 70, + [0][1][2][0][RTW89_CN][17] = 127, + [0][1][2][0][RTW89_QATAR][17] = 48, + [0][1][2][0][RTW89_UK][17] = 48, + [0][1][2][0][RTW89_FCC][19] = 70, + [0][1][2][0][RTW89_ETSI][19] = 48, + [0][1][2][0][RTW89_MKK][19] = 70, + [0][1][2][0][RTW89_IC][19] = 70, + [0][1][2][0][RTW89_KCC][19] = 62, + [0][1][2][0][RTW89_ACMA][19] = 48, + [0][1][2][0][RTW89_CHILE][19] = 48, + [0][1][2][0][RTW89_UKRAINE][19] = 40, + [0][1][2][0][RTW89_MEXICO][19] = 70, + [0][1][2][0][RTW89_CN][19] = 127, + [0][1][2][0][RTW89_QATAR][19] = 48, + [0][1][2][0][RTW89_UK][19] = 48, + [0][1][2][0][RTW89_FCC][21] = 70, + [0][1][2][0][RTW89_ETSI][21] = 48, + [0][1][2][0][RTW89_MKK][21] = 70, + [0][1][2][0][RTW89_IC][21] = 70, + [0][1][2][0][RTW89_KCC][21] = 62, + [0][1][2][0][RTW89_ACMA][21] = 48, + [0][1][2][0][RTW89_CHILE][21] = 48, + [0][1][2][0][RTW89_UKRAINE][21] = 40, + [0][1][2][0][RTW89_MEXICO][21] = 70, + [0][1][2][0][RTW89_CN][21] = 127, + [0][1][2][0][RTW89_QATAR][21] = 48, + [0][1][2][0][RTW89_UK][21] = 48, + [0][1][2][0][RTW89_FCC][23] = 70, + [0][1][2][0][RTW89_ETSI][23] = 48, + [0][1][2][0][RTW89_MKK][23] = 70, + [0][1][2][0][RTW89_IC][23] = 70, + [0][1][2][0][RTW89_KCC][23] = 62, + [0][1][2][0][RTW89_ACMA][23] = 48, + [0][1][2][0][RTW89_CHILE][23] = 48, + [0][1][2][0][RTW89_UKRAINE][23] = 40, + [0][1][2][0][RTW89_MEXICO][23] = 70, + [0][1][2][0][RTW89_CN][23] = 127, + [0][1][2][0][RTW89_QATAR][23] = 48, + [0][1][2][0][RTW89_UK][23] = 48, + [0][1][2][0][RTW89_FCC][25] = 70, + [0][1][2][0][RTW89_ETSI][25] = 48, + [0][1][2][0][RTW89_MKK][25] = 70, + [0][1][2][0][RTW89_IC][25] = 127, + [0][1][2][0][RTW89_KCC][25] = 62, + [0][1][2][0][RTW89_ACMA][25] = 127, + [0][1][2][0][RTW89_CHILE][25] = 48, + [0][1][2][0][RTW89_UKRAINE][25] = 40, + [0][1][2][0][RTW89_MEXICO][25] = 70, + [0][1][2][0][RTW89_CN][25] = 127, + [0][1][2][0][RTW89_QATAR][25] = 48, + [0][1][2][0][RTW89_UK][25] = 48, + [0][1][2][0][RTW89_FCC][27] = 70, + [0][1][2][0][RTW89_ETSI][27] = 48, + [0][1][2][0][RTW89_MKK][27] = 70, + [0][1][2][0][RTW89_IC][27] = 127, + [0][1][2][0][RTW89_KCC][27] = 62, + [0][1][2][0][RTW89_ACMA][27] = 127, + [0][1][2][0][RTW89_CHILE][27] = 50, + [0][1][2][0][RTW89_UKRAINE][27] = 40, + [0][1][2][0][RTW89_MEXICO][27] = 70, + [0][1][2][0][RTW89_CN][27] = 127, + [0][1][2][0][RTW89_QATAR][27] = 48, + [0][1][2][0][RTW89_UK][27] = 48, + [0][1][2][0][RTW89_FCC][29] = 70, + [0][1][2][0][RTW89_ETSI][29] = 48, + [0][1][2][0][RTW89_MKK][29] = 70, + [0][1][2][0][RTW89_IC][29] = 127, + [0][1][2][0][RTW89_KCC][29] = 62, + [0][1][2][0][RTW89_ACMA][29] = 127, + [0][1][2][0][RTW89_CHILE][29] = 50, + [0][1][2][0][RTW89_UKRAINE][29] = 40, + [0][1][2][0][RTW89_MEXICO][29] = 70, + [0][1][2][0][RTW89_CN][29] = 127, + [0][1][2][0][RTW89_QATAR][29] = 48, + [0][1][2][0][RTW89_UK][29] = 48, + [0][1][2][0][RTW89_FCC][31] = 70, + [0][1][2][0][RTW89_ETSI][31] = 48, + [0][1][2][0][RTW89_MKK][31] = 70, + [0][1][2][0][RTW89_IC][31] = 70, + [0][1][2][0][RTW89_KCC][31] = 62, + [0][1][2][0][RTW89_ACMA][31] = 48, + [0][1][2][0][RTW89_CHILE][31] = 50, + [0][1][2][0][RTW89_UKRAINE][31] = 40, + [0][1][2][0][RTW89_MEXICO][31] = 70, + [0][1][2][0][RTW89_CN][31] = 127, + [0][1][2][0][RTW89_QATAR][31] = 48, + [0][1][2][0][RTW89_UK][31] = 48, + [0][1][2][0][RTW89_FCC][33] = 70, + [0][1][2][0][RTW89_ETSI][33] = 48, + [0][1][2][0][RTW89_MKK][33] = 70, + [0][1][2][0][RTW89_IC][33] = 70, + [0][1][2][0][RTW89_KCC][33] = 62, + [0][1][2][0][RTW89_ACMA][33] = 48, + [0][1][2][0][RTW89_CHILE][33] = 50, + [0][1][2][0][RTW89_UKRAINE][33] = 40, + [0][1][2][0][RTW89_MEXICO][33] = 70, + [0][1][2][0][RTW89_CN][33] = 127, + [0][1][2][0][RTW89_QATAR][33] = 48, + [0][1][2][0][RTW89_UK][33] = 48, + [0][1][2][0][RTW89_FCC][35] = 66, + [0][1][2][0][RTW89_ETSI][35] = 48, + [0][1][2][0][RTW89_MKK][35] = 70, + [0][1][2][0][RTW89_IC][35] = 66, + [0][1][2][0][RTW89_KCC][35] = 62, + [0][1][2][0][RTW89_ACMA][35] = 48, + [0][1][2][0][RTW89_CHILE][35] = 50, + [0][1][2][0][RTW89_UKRAINE][35] = 40, + [0][1][2][0][RTW89_MEXICO][35] = 66, + [0][1][2][0][RTW89_CN][35] = 127, + [0][1][2][0][RTW89_QATAR][35] = 48, + [0][1][2][0][RTW89_UK][35] = 48, + [0][1][2][0][RTW89_FCC][37] = 70, + [0][1][2][0][RTW89_ETSI][37] = 127, + [0][1][2][0][RTW89_MKK][37] = 70, + [0][1][2][0][RTW89_IC][37] = 70, + [0][1][2][0][RTW89_KCC][37] = 62, + [0][1][2][0][RTW89_ACMA][37] = 70, + [0][1][2][0][RTW89_CHILE][37] = 50, + [0][1][2][0][RTW89_UKRAINE][37] = 127, + [0][1][2][0][RTW89_MEXICO][37] = 70, + [0][1][2][0][RTW89_CN][37] = 127, + [0][1][2][0][RTW89_QATAR][37] = 127, + [0][1][2][0][RTW89_UK][37] = 76, + [0][1][2][0][RTW89_FCC][38] = 78, + [0][1][2][0][RTW89_ETSI][38] = 16, + [0][1][2][0][RTW89_MKK][38] = 127, + [0][1][2][0][RTW89_IC][38] = 78, + [0][1][2][0][RTW89_KCC][38] = 62, + [0][1][2][0][RTW89_ACMA][38] = 74, + [0][1][2][0][RTW89_CHILE][38] = 50, + [0][1][2][0][RTW89_UKRAINE][38] = 16, + [0][1][2][0][RTW89_MEXICO][38] = 78, + [0][1][2][0][RTW89_CN][38] = 76, + [0][1][2][0][RTW89_QATAR][38] = 16, + [0][1][2][0][RTW89_UK][38] = 48, + [0][1][2][0][RTW89_FCC][40] = 78, + [0][1][2][0][RTW89_ETSI][40] = 16, + [0][1][2][0][RTW89_MKK][40] = 127, + [0][1][2][0][RTW89_IC][40] = 78, + [0][1][2][0][RTW89_KCC][40] = 62, + [0][1][2][0][RTW89_ACMA][40] = 74, + [0][1][2][0][RTW89_CHILE][40] = 50, + [0][1][2][0][RTW89_UKRAINE][40] = 16, + [0][1][2][0][RTW89_MEXICO][40] = 78, + [0][1][2][0][RTW89_CN][40] = 76, + [0][1][2][0][RTW89_QATAR][40] = 16, + [0][1][2][0][RTW89_UK][40] = 48, + [0][1][2][0][RTW89_FCC][42] = 78, + [0][1][2][0][RTW89_ETSI][42] = 16, + [0][1][2][0][RTW89_MKK][42] = 127, + [0][1][2][0][RTW89_IC][42] = 78, + [0][1][2][0][RTW89_KCC][42] = 62, + [0][1][2][0][RTW89_ACMA][42] = 76, + [0][1][2][0][RTW89_CHILE][42] = 52, + [0][1][2][0][RTW89_UKRAINE][42] = 16, + [0][1][2][0][RTW89_MEXICO][42] = 78, + [0][1][2][0][RTW89_CN][42] = 76, + [0][1][2][0][RTW89_QATAR][42] = 16, + [0][1][2][0][RTW89_UK][42] = 48, + [0][1][2][0][RTW89_FCC][44] = 78, + [0][1][2][0][RTW89_ETSI][44] = 16, + [0][1][2][0][RTW89_MKK][44] = 127, + [0][1][2][0][RTW89_IC][44] = 78, + [0][1][2][0][RTW89_KCC][44] = 62, + [0][1][2][0][RTW89_ACMA][44] = 76, + [0][1][2][0][RTW89_CHILE][44] = 52, + [0][1][2][0][RTW89_UKRAINE][44] = 16, + [0][1][2][0][RTW89_MEXICO][44] = 78, + [0][1][2][0][RTW89_CN][44] = 76, + [0][1][2][0][RTW89_QATAR][44] = 16, + [0][1][2][0][RTW89_UK][44] = 48, + [0][1][2][0][RTW89_FCC][46] = 78, + [0][1][2][0][RTW89_ETSI][46] = 16, + [0][1][2][0][RTW89_MKK][46] = 127, + [0][1][2][0][RTW89_IC][46] = 78, + [0][1][2][0][RTW89_KCC][46] = 62, + [0][1][2][0][RTW89_ACMA][46] = 76, + [0][1][2][0][RTW89_CHILE][46] = 52, + [0][1][2][0][RTW89_UKRAINE][46] = 16, + [0][1][2][0][RTW89_MEXICO][46] = 78, + [0][1][2][0][RTW89_CN][46] = 76, + [0][1][2][0][RTW89_QATAR][46] = 16, + [0][1][2][0][RTW89_UK][46] = 48, + [0][1][2][0][RTW89_FCC][48] = 58, + [0][1][2][0][RTW89_ETSI][48] = 127, + [0][1][2][0][RTW89_MKK][48] = 127, + [0][1][2][0][RTW89_IC][48] = 127, + [0][1][2][0][RTW89_KCC][48] = 127, + [0][1][2][0][RTW89_ACMA][48] = 127, + [0][1][2][0][RTW89_CHILE][48] = 127, + [0][1][2][0][RTW89_UKRAINE][48] = 127, + [0][1][2][0][RTW89_MEXICO][48] = 127, + [0][1][2][0][RTW89_CN][48] = 127, + [0][1][2][0][RTW89_QATAR][48] = 127, + [0][1][2][0][RTW89_UK][48] = 127, + [0][1][2][0][RTW89_FCC][50] = 58, + [0][1][2][0][RTW89_ETSI][50] = 127, + [0][1][2][0][RTW89_MKK][50] = 127, + [0][1][2][0][RTW89_IC][50] = 127, + [0][1][2][0][RTW89_KCC][50] = 127, + [0][1][2][0][RTW89_ACMA][50] = 127, + [0][1][2][0][RTW89_CHILE][50] = 127, + [0][1][2][0][RTW89_UKRAINE][50] = 127, + [0][1][2][0][RTW89_MEXICO][50] = 127, + [0][1][2][0][RTW89_CN][50] = 127, + [0][1][2][0][RTW89_QATAR][50] = 127, + [0][1][2][0][RTW89_UK][50] = 127, + [0][1][2][0][RTW89_FCC][52] = 58, + [0][1][2][0][RTW89_ETSI][52] = 127, + [0][1][2][0][RTW89_MKK][52] = 127, + [0][1][2][0][RTW89_IC][52] = 127, + [0][1][2][0][RTW89_KCC][52] = 127, + [0][1][2][0][RTW89_ACMA][52] = 127, + [0][1][2][0][RTW89_CHILE][52] = 127, + [0][1][2][0][RTW89_UKRAINE][52] = 127, + [0][1][2][0][RTW89_MEXICO][52] = 127, + [0][1][2][0][RTW89_CN][52] = 127, + [0][1][2][0][RTW89_QATAR][52] = 127, + [0][1][2][0][RTW89_UK][52] = 127, + [0][1][2][1][RTW89_FCC][0] = 68, + [0][1][2][1][RTW89_ETSI][0] = 36, + [0][1][2][1][RTW89_MKK][0] = 50, + [0][1][2][1][RTW89_IC][0] = 40, + [0][1][2][1][RTW89_KCC][0] = 62, + [0][1][2][1][RTW89_ACMA][0] = 36, + [0][1][2][1][RTW89_CHILE][0] = 14, + [0][1][2][1][RTW89_UKRAINE][0] = 28, + [0][1][2][1][RTW89_MEXICO][0] = 50, + [0][1][2][1][RTW89_CN][0] = 36, + [0][1][2][1][RTW89_QATAR][0] = 36, + [0][1][2][1][RTW89_UK][0] = 36, + [0][1][2][1][RTW89_FCC][2] = 68, + [0][1][2][1][RTW89_ETSI][2] = 36, + [0][1][2][1][RTW89_MKK][2] = 50, + [0][1][2][1][RTW89_IC][2] = 40, + [0][1][2][1][RTW89_KCC][2] = 62, + [0][1][2][1][RTW89_ACMA][2] = 36, + [0][1][2][1][RTW89_CHILE][2] = 14, + [0][1][2][1][RTW89_UKRAINE][2] = 28, + [0][1][2][1][RTW89_MEXICO][2] = 50, + [0][1][2][1][RTW89_CN][2] = 36, + [0][1][2][1][RTW89_QATAR][2] = 36, + [0][1][2][1][RTW89_UK][2] = 36, + [0][1][2][1][RTW89_FCC][4] = 68, + [0][1][2][1][RTW89_ETSI][4] = 36, + [0][1][2][1][RTW89_MKK][4] = 50, + [0][1][2][1][RTW89_IC][4] = 40, + [0][1][2][1][RTW89_KCC][4] = 62, + [0][1][2][1][RTW89_ACMA][4] = 36, + [0][1][2][1][RTW89_CHILE][4] = 14, + [0][1][2][1][RTW89_UKRAINE][4] = 28, + [0][1][2][1][RTW89_MEXICO][4] = 50, + [0][1][2][1][RTW89_CN][4] = 36, + [0][1][2][1][RTW89_QATAR][4] = 36, + [0][1][2][1][RTW89_UK][4] = 36, + [0][1][2][1][RTW89_FCC][6] = 68, + [0][1][2][1][RTW89_ETSI][6] = 36, + [0][1][2][1][RTW89_MKK][6] = 50, + [0][1][2][1][RTW89_IC][6] = 40, + [0][1][2][1][RTW89_KCC][6] = 34, + [0][1][2][1][RTW89_ACMA][6] = 36, + [0][1][2][1][RTW89_CHILE][6] = 14, + [0][1][2][1][RTW89_UKRAINE][6] = 28, + [0][1][2][1][RTW89_MEXICO][6] = 50, + [0][1][2][1][RTW89_CN][6] = 36, + [0][1][2][1][RTW89_QATAR][6] = 36, + [0][1][2][1][RTW89_UK][6] = 36, + [0][1][2][1][RTW89_FCC][8] = 68, + [0][1][2][1][RTW89_ETSI][8] = 36, + [0][1][2][1][RTW89_MKK][8] = 50, + [0][1][2][1][RTW89_IC][8] = 40, + [0][1][2][1][RTW89_KCC][8] = 62, + [0][1][2][1][RTW89_ACMA][8] = 36, + [0][1][2][1][RTW89_CHILE][8] = 36, + [0][1][2][1][RTW89_UKRAINE][8] = 28, + [0][1][2][1][RTW89_MEXICO][8] = 68, + [0][1][2][1][RTW89_CN][8] = 36, + [0][1][2][1][RTW89_QATAR][8] = 36, + [0][1][2][1][RTW89_UK][8] = 36, + [0][1][2][1][RTW89_FCC][10] = 68, + [0][1][2][1][RTW89_ETSI][10] = 36, + [0][1][2][1][RTW89_MKK][10] = 50, + [0][1][2][1][RTW89_IC][10] = 40, + [0][1][2][1][RTW89_KCC][10] = 62, + [0][1][2][1][RTW89_ACMA][10] = 36, + [0][1][2][1][RTW89_CHILE][10] = 36, + [0][1][2][1][RTW89_UKRAINE][10] = 28, + [0][1][2][1][RTW89_MEXICO][10] = 68, + [0][1][2][1][RTW89_CN][10] = 36, + [0][1][2][1][RTW89_QATAR][10] = 36, + [0][1][2][1][RTW89_UK][10] = 36, + [0][1][2][1][RTW89_FCC][12] = 68, + [0][1][2][1][RTW89_ETSI][12] = 36, + [0][1][2][1][RTW89_MKK][12] = 50, + [0][1][2][1][RTW89_IC][12] = 40, + [0][1][2][1][RTW89_KCC][12] = 62, + [0][1][2][1][RTW89_ACMA][12] = 36, + [0][1][2][1][RTW89_CHILE][12] = 36, + [0][1][2][1][RTW89_UKRAINE][12] = 28, + [0][1][2][1][RTW89_MEXICO][12] = 68, + [0][1][2][1][RTW89_CN][12] = 36, + [0][1][2][1][RTW89_QATAR][12] = 36, + [0][1][2][1][RTW89_UK][12] = 36, + [0][1][2][1][RTW89_FCC][14] = 68, + [0][1][2][1][RTW89_ETSI][14] = 36, + [0][1][2][1][RTW89_MKK][14] = 50, + [0][1][2][1][RTW89_IC][14] = 40, + [0][1][2][1][RTW89_KCC][14] = 62, + [0][1][2][1][RTW89_ACMA][14] = 36, + [0][1][2][1][RTW89_CHILE][14] = 36, + [0][1][2][1][RTW89_UKRAINE][14] = 28, + [0][1][2][1][RTW89_MEXICO][14] = 68, + [0][1][2][1][RTW89_CN][14] = 36, + [0][1][2][1][RTW89_QATAR][14] = 36, + [0][1][2][1][RTW89_UK][14] = 36, + [0][1][2][1][RTW89_FCC][15] = 68, + [0][1][2][1][RTW89_ETSI][15] = 36, + [0][1][2][1][RTW89_MKK][15] = 70, + [0][1][2][1][RTW89_IC][15] = 68, + [0][1][2][1][RTW89_KCC][15] = 62, + [0][1][2][1][RTW89_ACMA][15] = 36, + [0][1][2][1][RTW89_CHILE][15] = 36, + [0][1][2][1][RTW89_UKRAINE][15] = 28, + [0][1][2][1][RTW89_MEXICO][15] = 68, + [0][1][2][1][RTW89_CN][15] = 127, + [0][1][2][1][RTW89_QATAR][15] = 36, + [0][1][2][1][RTW89_UK][15] = 36, + [0][1][2][1][RTW89_FCC][17] = 68, + [0][1][2][1][RTW89_ETSI][17] = 36, + [0][1][2][1][RTW89_MKK][17] = 70, + [0][1][2][1][RTW89_IC][17] = 68, + [0][1][2][1][RTW89_KCC][17] = 62, + [0][1][2][1][RTW89_ACMA][17] = 36, + [0][1][2][1][RTW89_CHILE][17] = 36, + [0][1][2][1][RTW89_UKRAINE][17] = 28, + [0][1][2][1][RTW89_MEXICO][17] = 68, + [0][1][2][1][RTW89_CN][17] = 127, + [0][1][2][1][RTW89_QATAR][17] = 36, + [0][1][2][1][RTW89_UK][17] = 36, + [0][1][2][1][RTW89_FCC][19] = 68, + [0][1][2][1][RTW89_ETSI][19] = 36, + [0][1][2][1][RTW89_MKK][19] = 70, + [0][1][2][1][RTW89_IC][19] = 68, + [0][1][2][1][RTW89_KCC][19] = 62, + [0][1][2][1][RTW89_ACMA][19] = 36, + [0][1][2][1][RTW89_CHILE][19] = 36, + [0][1][2][1][RTW89_UKRAINE][19] = 28, + [0][1][2][1][RTW89_MEXICO][19] = 68, + [0][1][2][1][RTW89_CN][19] = 127, + [0][1][2][1][RTW89_QATAR][19] = 36, + [0][1][2][1][RTW89_UK][19] = 36, + [0][1][2][1][RTW89_FCC][21] = 68, + [0][1][2][1][RTW89_ETSI][21] = 36, + [0][1][2][1][RTW89_MKK][21] = 70, + [0][1][2][1][RTW89_IC][21] = 68, + [0][1][2][1][RTW89_KCC][21] = 62, + [0][1][2][1][RTW89_ACMA][21] = 36, + [0][1][2][1][RTW89_CHILE][21] = 36, + [0][1][2][1][RTW89_UKRAINE][21] = 28, + [0][1][2][1][RTW89_MEXICO][21] = 68, + [0][1][2][1][RTW89_CN][21] = 127, + [0][1][2][1][RTW89_QATAR][21] = 36, + [0][1][2][1][RTW89_UK][21] = 36, + [0][1][2][1][RTW89_FCC][23] = 68, + [0][1][2][1][RTW89_ETSI][23] = 36, + [0][1][2][1][RTW89_MKK][23] = 70, + [0][1][2][1][RTW89_IC][23] = 68, + [0][1][2][1][RTW89_KCC][23] = 62, + [0][1][2][1][RTW89_ACMA][23] = 36, + [0][1][2][1][RTW89_CHILE][23] = 36, + [0][1][2][1][RTW89_UKRAINE][23] = 28, + [0][1][2][1][RTW89_MEXICO][23] = 68, + [0][1][2][1][RTW89_CN][23] = 127, + [0][1][2][1][RTW89_QATAR][23] = 36, + [0][1][2][1][RTW89_UK][23] = 36, + [0][1][2][1][RTW89_FCC][25] = 66, + [0][1][2][1][RTW89_ETSI][25] = 36, + [0][1][2][1][RTW89_MKK][25] = 70, + [0][1][2][1][RTW89_IC][25] = 127, + [0][1][2][1][RTW89_KCC][25] = 62, + [0][1][2][1][RTW89_ACMA][25] = 127, + [0][1][2][1][RTW89_CHILE][25] = 36, + [0][1][2][1][RTW89_UKRAINE][25] = 28, + [0][1][2][1][RTW89_MEXICO][25] = 66, + [0][1][2][1][RTW89_CN][25] = 127, + [0][1][2][1][RTW89_QATAR][25] = 36, + [0][1][2][1][RTW89_UK][25] = 36, + [0][1][2][1][RTW89_FCC][27] = 66, + [0][1][2][1][RTW89_ETSI][27] = 36, + [0][1][2][1][RTW89_MKK][27] = 70, + [0][1][2][1][RTW89_IC][27] = 127, + [0][1][2][1][RTW89_KCC][27] = 62, + [0][1][2][1][RTW89_ACMA][27] = 127, + [0][1][2][1][RTW89_CHILE][27] = 36, + [0][1][2][1][RTW89_UKRAINE][27] = 28, + [0][1][2][1][RTW89_MEXICO][27] = 66, + [0][1][2][1][RTW89_CN][27] = 127, + [0][1][2][1][RTW89_QATAR][27] = 36, + [0][1][2][1][RTW89_UK][27] = 36, + [0][1][2][1][RTW89_FCC][29] = 66, + [0][1][2][1][RTW89_ETSI][29] = 36, + [0][1][2][1][RTW89_MKK][29] = 70, + [0][1][2][1][RTW89_IC][29] = 127, + [0][1][2][1][RTW89_KCC][29] = 62, + [0][1][2][1][RTW89_ACMA][29] = 127, + [0][1][2][1][RTW89_CHILE][29] = 36, + [0][1][2][1][RTW89_UKRAINE][29] = 28, + [0][1][2][1][RTW89_MEXICO][29] = 66, + [0][1][2][1][RTW89_CN][29] = 127, + [0][1][2][1][RTW89_QATAR][29] = 36, + [0][1][2][1][RTW89_UK][29] = 36, + [0][1][2][1][RTW89_FCC][31] = 66, + [0][1][2][1][RTW89_ETSI][31] = 36, + [0][1][2][1][RTW89_MKK][31] = 70, + [0][1][2][1][RTW89_IC][31] = 66, + [0][1][2][1][RTW89_KCC][31] = 62, + [0][1][2][1][RTW89_ACMA][31] = 36, + [0][1][2][1][RTW89_CHILE][31] = 36, + [0][1][2][1][RTW89_UKRAINE][31] = 28, + [0][1][2][1][RTW89_MEXICO][31] = 66, + [0][1][2][1][RTW89_CN][31] = 127, + [0][1][2][1][RTW89_QATAR][31] = 36, + [0][1][2][1][RTW89_UK][31] = 36, + [0][1][2][1][RTW89_FCC][33] = 66, + [0][1][2][1][RTW89_ETSI][33] = 36, + [0][1][2][1][RTW89_MKK][33] = 70, + [0][1][2][1][RTW89_IC][33] = 66, + [0][1][2][1][RTW89_KCC][33] = 62, + [0][1][2][1][RTW89_ACMA][33] = 36, + [0][1][2][1][RTW89_CHILE][33] = 36, + [0][1][2][1][RTW89_UKRAINE][33] = 28, + [0][1][2][1][RTW89_MEXICO][33] = 66, + [0][1][2][1][RTW89_CN][33] = 127, + [0][1][2][1][RTW89_QATAR][33] = 36, + [0][1][2][1][RTW89_UK][33] = 36, + [0][1][2][1][RTW89_FCC][35] = 66, + [0][1][2][1][RTW89_ETSI][35] = 36, + [0][1][2][1][RTW89_MKK][35] = 70, + [0][1][2][1][RTW89_IC][35] = 66, + [0][1][2][1][RTW89_KCC][35] = 62, + [0][1][2][1][RTW89_ACMA][35] = 36, + [0][1][2][1][RTW89_CHILE][35] = 36, + [0][1][2][1][RTW89_UKRAINE][35] = 28, + [0][1][2][1][RTW89_MEXICO][35] = 66, + [0][1][2][1][RTW89_CN][35] = 127, + [0][1][2][1][RTW89_QATAR][35] = 36, + [0][1][2][1][RTW89_UK][35] = 36, + [0][1][2][1][RTW89_FCC][37] = 68, + [0][1][2][1][RTW89_ETSI][37] = 127, + [0][1][2][1][RTW89_MKK][37] = 70, + [0][1][2][1][RTW89_IC][37] = 68, + [0][1][2][1][RTW89_KCC][37] = 62, + [0][1][2][1][RTW89_ACMA][37] = 70, + [0][1][2][1][RTW89_CHILE][37] = 36, + [0][1][2][1][RTW89_UKRAINE][37] = 127, + [0][1][2][1][RTW89_MEXICO][37] = 68, + [0][1][2][1][RTW89_CN][37] = 127, + [0][1][2][1][RTW89_QATAR][37] = 127, + [0][1][2][1][RTW89_UK][37] = 62, + [0][1][2][1][RTW89_FCC][38] = 78, + [0][1][2][1][RTW89_ETSI][38] = 4, + [0][1][2][1][RTW89_MKK][38] = 127, + [0][1][2][1][RTW89_IC][38] = 78, + [0][1][2][1][RTW89_KCC][38] = 62, + [0][1][2][1][RTW89_ACMA][38] = 74, + [0][1][2][1][RTW89_CHILE][38] = 36, + [0][1][2][1][RTW89_UKRAINE][38] = 4, + [0][1][2][1][RTW89_MEXICO][38] = 78, + [0][1][2][1][RTW89_CN][38] = 72, + [0][1][2][1][RTW89_QATAR][38] = 4, + [0][1][2][1][RTW89_UK][38] = 36, + [0][1][2][1][RTW89_FCC][40] = 78, + [0][1][2][1][RTW89_ETSI][40] = 4, + [0][1][2][1][RTW89_MKK][40] = 127, + [0][1][2][1][RTW89_IC][40] = 78, + [0][1][2][1][RTW89_KCC][40] = 62, + [0][1][2][1][RTW89_ACMA][40] = 74, + [0][1][2][1][RTW89_CHILE][40] = 36, + [0][1][2][1][RTW89_UKRAINE][40] = 4, + [0][1][2][1][RTW89_MEXICO][40] = 78, + [0][1][2][1][RTW89_CN][40] = 72, + [0][1][2][1][RTW89_QATAR][40] = 4, + [0][1][2][1][RTW89_UK][40] = 36, + [0][1][2][1][RTW89_FCC][42] = 78, + [0][1][2][1][RTW89_ETSI][42] = 4, + [0][1][2][1][RTW89_MKK][42] = 127, + [0][1][2][1][RTW89_IC][42] = 78, + [0][1][2][1][RTW89_KCC][42] = 62, + [0][1][2][1][RTW89_ACMA][42] = 76, + [0][1][2][1][RTW89_CHILE][42] = 36, + [0][1][2][1][RTW89_UKRAINE][42] = 4, + [0][1][2][1][RTW89_MEXICO][42] = 78, + [0][1][2][1][RTW89_CN][42] = 72, + [0][1][2][1][RTW89_QATAR][42] = 4, + [0][1][2][1][RTW89_UK][42] = 36, + [0][1][2][1][RTW89_FCC][44] = 78, + [0][1][2][1][RTW89_ETSI][44] = 4, + [0][1][2][1][RTW89_MKK][44] = 127, + [0][1][2][1][RTW89_IC][44] = 78, + [0][1][2][1][RTW89_KCC][44] = 62, + [0][1][2][1][RTW89_ACMA][44] = 76, + [0][1][2][1][RTW89_CHILE][44] = 36, + [0][1][2][1][RTW89_UKRAINE][44] = 4, + [0][1][2][1][RTW89_MEXICO][44] = 78, + [0][1][2][1][RTW89_CN][44] = 76, + [0][1][2][1][RTW89_QATAR][44] = 4, + [0][1][2][1][RTW89_UK][44] = 36, + [0][1][2][1][RTW89_FCC][46] = 78, + [0][1][2][1][RTW89_ETSI][46] = 4, + [0][1][2][1][RTW89_MKK][46] = 127, + [0][1][2][1][RTW89_IC][46] = 78, + [0][1][2][1][RTW89_KCC][46] = 62, + [0][1][2][1][RTW89_ACMA][46] = 76, + [0][1][2][1][RTW89_CHILE][46] = 36, + [0][1][2][1][RTW89_UKRAINE][46] = 4, + [0][1][2][1][RTW89_MEXICO][46] = 78, + [0][1][2][1][RTW89_CN][46] = 76, + [0][1][2][1][RTW89_QATAR][46] = 4, + [0][1][2][1][RTW89_UK][46] = 36, + [0][1][2][1][RTW89_FCC][48] = 58, + [0][1][2][1][RTW89_ETSI][48] = 127, + [0][1][2][1][RTW89_MKK][48] = 127, + [0][1][2][1][RTW89_IC][48] = 127, + [0][1][2][1][RTW89_KCC][48] = 127, + [0][1][2][1][RTW89_ACMA][48] = 127, + [0][1][2][1][RTW89_CHILE][48] = 127, + [0][1][2][1][RTW89_UKRAINE][48] = 127, + [0][1][2][1][RTW89_MEXICO][48] = 127, + [0][1][2][1][RTW89_CN][48] = 127, + [0][1][2][1][RTW89_QATAR][48] = 127, + [0][1][2][1][RTW89_UK][48] = 127, + [0][1][2][1][RTW89_FCC][50] = 58, + [0][1][2][1][RTW89_ETSI][50] = 127, + [0][1][2][1][RTW89_MKK][50] = 127, + [0][1][2][1][RTW89_IC][50] = 127, + [0][1][2][1][RTW89_KCC][50] = 127, + [0][1][2][1][RTW89_ACMA][50] = 127, + [0][1][2][1][RTW89_CHILE][50] = 127, + [0][1][2][1][RTW89_UKRAINE][50] = 127, + [0][1][2][1][RTW89_MEXICO][50] = 127, + [0][1][2][1][RTW89_CN][50] = 127, + [0][1][2][1][RTW89_QATAR][50] = 127, + [0][1][2][1][RTW89_UK][50] = 127, + [0][1][2][1][RTW89_FCC][52] = 58, + [0][1][2][1][RTW89_ETSI][52] = 127, + [0][1][2][1][RTW89_MKK][52] = 127, + [0][1][2][1][RTW89_IC][52] = 127, + [0][1][2][1][RTW89_KCC][52] = 127, + [0][1][2][1][RTW89_ACMA][52] = 127, + [0][1][2][1][RTW89_CHILE][52] = 127, + [0][1][2][1][RTW89_UKRAINE][52] = 127, + [0][1][2][1][RTW89_MEXICO][52] = 127, + [0][1][2][1][RTW89_CN][52] = 127, + [0][1][2][1][RTW89_QATAR][52] = 127, + [0][1][2][1][RTW89_UK][52] = 127, + [1][0][2][0][RTW89_FCC][1] = 66, + [1][0][2][0][RTW89_ETSI][1] = 64, + [1][0][2][0][RTW89_MKK][1] = 62, + [1][0][2][0][RTW89_IC][1] = 64, + [1][0][2][0][RTW89_KCC][1] = 70, + [1][0][2][0][RTW89_ACMA][1] = 64, + [1][0][2][0][RTW89_CHILE][1] = 42, + [1][0][2][0][RTW89_UKRAINE][1] = 52, + [1][0][2][0][RTW89_MEXICO][1] = 62, + [1][0][2][0][RTW89_CN][1] = 62, + [1][0][2][0][RTW89_QATAR][1] = 64, + [1][0][2][0][RTW89_UK][1] = 64, + [1][0][2][0][RTW89_FCC][5] = 78, + [1][0][2][0][RTW89_ETSI][5] = 64, + [1][0][2][0][RTW89_MKK][5] = 62, + [1][0][2][0][RTW89_IC][5] = 64, + [1][0][2][0][RTW89_KCC][5] = 66, + [1][0][2][0][RTW89_ACMA][5] = 64, + [1][0][2][0][RTW89_CHILE][5] = 42, + [1][0][2][0][RTW89_UKRAINE][5] = 52, + [1][0][2][0][RTW89_MEXICO][5] = 62, + [1][0][2][0][RTW89_CN][5] = 62, + [1][0][2][0][RTW89_QATAR][5] = 64, + [1][0][2][0][RTW89_UK][5] = 64, + [1][0][2][0][RTW89_FCC][9] = 78, + [1][0][2][0][RTW89_ETSI][9] = 64, + [1][0][2][0][RTW89_MKK][9] = 62, + [1][0][2][0][RTW89_IC][9] = 64, + [1][0][2][0][RTW89_KCC][9] = 74, + [1][0][2][0][RTW89_ACMA][9] = 64, + [1][0][2][0][RTW89_CHILE][9] = 66, + [1][0][2][0][RTW89_UKRAINE][9] = 52, + [1][0][2][0][RTW89_MEXICO][9] = 78, + [1][0][2][0][RTW89_CN][9] = 62, + [1][0][2][0][RTW89_QATAR][9] = 64, + [1][0][2][0][RTW89_UK][9] = 64, + [1][0][2][0][RTW89_FCC][13] = 66, + [1][0][2][0][RTW89_ETSI][13] = 64, + [1][0][2][0][RTW89_MKK][13] = 62, + [1][0][2][0][RTW89_IC][13] = 64, + [1][0][2][0][RTW89_KCC][13] = 68, + [1][0][2][0][RTW89_ACMA][13] = 64, + [1][0][2][0][RTW89_CHILE][13] = 66, + [1][0][2][0][RTW89_UKRAINE][13] = 52, + [1][0][2][0][RTW89_MEXICO][13] = 66, + [1][0][2][0][RTW89_CN][13] = 62, + [1][0][2][0][RTW89_QATAR][13] = 64, + [1][0][2][0][RTW89_UK][13] = 64, + [1][0][2][0][RTW89_FCC][16] = 64, + [1][0][2][0][RTW89_ETSI][16] = 64, + [1][0][2][0][RTW89_MKK][16] = 74, + [1][0][2][0][RTW89_IC][16] = 64, + [1][0][2][0][RTW89_KCC][16] = 70, + [1][0][2][0][RTW89_ACMA][16] = 64, + [1][0][2][0][RTW89_CHILE][16] = 64, + [1][0][2][0][RTW89_UKRAINE][16] = 52, + [1][0][2][0][RTW89_MEXICO][16] = 64, + [1][0][2][0][RTW89_CN][16] = 127, + [1][0][2][0][RTW89_QATAR][16] = 64, + [1][0][2][0][RTW89_UK][16] = 64, + [1][0][2][0][RTW89_FCC][20] = 78, + [1][0][2][0][RTW89_ETSI][20] = 64, + [1][0][2][0][RTW89_MKK][20] = 74, + [1][0][2][0][RTW89_IC][20] = 78, + [1][0][2][0][RTW89_KCC][20] = 70, + [1][0][2][0][RTW89_ACMA][20] = 64, + [1][0][2][0][RTW89_CHILE][20] = 62, + [1][0][2][0][RTW89_UKRAINE][20] = 52, + [1][0][2][0][RTW89_MEXICO][20] = 78, + [1][0][2][0][RTW89_CN][20] = 127, + [1][0][2][0][RTW89_QATAR][20] = 64, + [1][0][2][0][RTW89_UK][20] = 64, + [1][0][2][0][RTW89_FCC][24] = 78, + [1][0][2][0][RTW89_ETSI][24] = 64, + [1][0][2][0][RTW89_MKK][24] = 74, + [1][0][2][0][RTW89_IC][24] = 127, + [1][0][2][0][RTW89_KCC][24] = 70, + [1][0][2][0][RTW89_ACMA][24] = 127, + [1][0][2][0][RTW89_CHILE][24] = 62, + [1][0][2][0][RTW89_UKRAINE][24] = 52, + [1][0][2][0][RTW89_MEXICO][24] = 78, + [1][0][2][0][RTW89_CN][24] = 127, + [1][0][2][0][RTW89_QATAR][24] = 64, + [1][0][2][0][RTW89_UK][24] = 64, + [1][0][2][0][RTW89_FCC][28] = 78, + [1][0][2][0][RTW89_ETSI][28] = 64, + [1][0][2][0][RTW89_MKK][28] = 74, + [1][0][2][0][RTW89_IC][28] = 127, + [1][0][2][0][RTW89_KCC][28] = 74, + [1][0][2][0][RTW89_ACMA][28] = 127, + [1][0][2][0][RTW89_CHILE][28] = 64, + [1][0][2][0][RTW89_UKRAINE][28] = 52, + [1][0][2][0][RTW89_MEXICO][28] = 78, + [1][0][2][0][RTW89_CN][28] = 127, + [1][0][2][0][RTW89_QATAR][28] = 64, + [1][0][2][0][RTW89_UK][28] = 64, + [1][0][2][0][RTW89_FCC][32] = 76, + [1][0][2][0][RTW89_ETSI][32] = 64, + [1][0][2][0][RTW89_MKK][32] = 74, + [1][0][2][0][RTW89_IC][32] = 76, + [1][0][2][0][RTW89_KCC][32] = 74, + [1][0][2][0][RTW89_ACMA][32] = 64, + [1][0][2][0][RTW89_CHILE][32] = 64, + [1][0][2][0][RTW89_UKRAINE][32] = 52, + [1][0][2][0][RTW89_MEXICO][32] = 76, + [1][0][2][0][RTW89_CN][32] = 127, + [1][0][2][0][RTW89_QATAR][32] = 64, + [1][0][2][0][RTW89_UK][32] = 64, + [1][0][2][0][RTW89_FCC][36] = 78, + [1][0][2][0][RTW89_ETSI][36] = 127, + [1][0][2][0][RTW89_MKK][36] = 74, + [1][0][2][0][RTW89_IC][36] = 78, + [1][0][2][0][RTW89_KCC][36] = 74, + [1][0][2][0][RTW89_ACMA][36] = 74, + [1][0][2][0][RTW89_CHILE][36] = 64, + [1][0][2][0][RTW89_UKRAINE][36] = 127, + [1][0][2][0][RTW89_MEXICO][36] = 78, + [1][0][2][0][RTW89_CN][36] = 127, + [1][0][2][0][RTW89_QATAR][36] = 127, + [1][0][2][0][RTW89_UK][36] = 74, + [1][0][2][0][RTW89_FCC][39] = 78, + [1][0][2][0][RTW89_ETSI][39] = 28, + [1][0][2][0][RTW89_MKK][39] = 127, + [1][0][2][0][RTW89_IC][39] = 78, + [1][0][2][0][RTW89_KCC][39] = 74, + [1][0][2][0][RTW89_ACMA][39] = 74, + [1][0][2][0][RTW89_CHILE][39] = 64, + [1][0][2][0][RTW89_UKRAINE][39] = 28, + [1][0][2][0][RTW89_MEXICO][39] = 78, + [1][0][2][0][RTW89_CN][39] = 70, + [1][0][2][0][RTW89_QATAR][39] = 28, + [1][0][2][0][RTW89_UK][39] = 64, + [1][0][2][0][RTW89_FCC][43] = 78, + [1][0][2][0][RTW89_ETSI][43] = 28, + [1][0][2][0][RTW89_MKK][43] = 127, + [1][0][2][0][RTW89_IC][43] = 78, + [1][0][2][0][RTW89_KCC][43] = 74, + [1][0][2][0][RTW89_ACMA][43] = 74, + [1][0][2][0][RTW89_CHILE][43] = 64, + [1][0][2][0][RTW89_UKRAINE][43] = 28, + [1][0][2][0][RTW89_MEXICO][43] = 78, + [1][0][2][0][RTW89_CN][43] = 74, + [1][0][2][0][RTW89_QATAR][43] = 28, + [1][0][2][0][RTW89_UK][43] = 62, + [1][0][2][0][RTW89_FCC][47] = 78, + [1][0][2][0][RTW89_ETSI][47] = 127, + [1][0][2][0][RTW89_MKK][47] = 127, + [1][0][2][0][RTW89_IC][47] = 127, + [1][0][2][0][RTW89_KCC][47] = 127, + [1][0][2][0][RTW89_ACMA][47] = 127, + [1][0][2][0][RTW89_CHILE][47] = 127, + [1][0][2][0][RTW89_UKRAINE][47] = 127, + [1][0][2][0][RTW89_MEXICO][47] = 127, + [1][0][2][0][RTW89_CN][47] = 127, + [1][0][2][0][RTW89_QATAR][47] = 127, + [1][0][2][0][RTW89_UK][47] = 127, + [1][0][2][0][RTW89_FCC][51] = 70, + [1][0][2][0][RTW89_ETSI][51] = 127, + [1][0][2][0][RTW89_MKK][51] = 127, + [1][0][2][0][RTW89_IC][51] = 127, + [1][0][2][0][RTW89_KCC][51] = 127, + [1][0][2][0][RTW89_ACMA][51] = 127, + [1][0][2][0][RTW89_CHILE][51] = 127, + [1][0][2][0][RTW89_UKRAINE][51] = 127, + [1][0][2][0][RTW89_MEXICO][51] = 127, + [1][0][2][0][RTW89_CN][51] = 127, + [1][0][2][0][RTW89_QATAR][51] = 127, + [1][0][2][0][RTW89_UK][51] = 127, + [1][1][2][0][RTW89_FCC][1] = 62, + [1][1][2][0][RTW89_ETSI][1] = 52, + [1][1][2][0][RTW89_MKK][1] = 50, + [1][1][2][0][RTW89_IC][1] = 52, + [1][1][2][0][RTW89_KCC][1] = 58, + [1][1][2][0][RTW89_ACMA][1] = 52, + [1][1][2][0][RTW89_CHILE][1] = 30, + [1][1][2][0][RTW89_UKRAINE][1] = 40, + [1][1][2][0][RTW89_MEXICO][1] = 50, + [1][1][2][0][RTW89_CN][1] = 50, + [1][1][2][0][RTW89_QATAR][1] = 52, + [1][1][2][0][RTW89_UK][1] = 52, + [1][1][2][0][RTW89_FCC][5] = 76, + [1][1][2][0][RTW89_ETSI][5] = 52, + [1][1][2][0][RTW89_MKK][5] = 50, + [1][1][2][0][RTW89_IC][5] = 52, + [1][1][2][0][RTW89_KCC][5] = 48, + [1][1][2][0][RTW89_ACMA][5] = 52, + [1][1][2][0][RTW89_CHILE][5] = 30, + [1][1][2][0][RTW89_UKRAINE][5] = 40, + [1][1][2][0][RTW89_MEXICO][5] = 50, + [1][1][2][0][RTW89_CN][5] = 50, + [1][1][2][0][RTW89_QATAR][5] = 52, + [1][1][2][0][RTW89_UK][5] = 52, + [1][1][2][0][RTW89_FCC][9] = 76, + [1][1][2][0][RTW89_ETSI][9] = 52, + [1][1][2][0][RTW89_MKK][9] = 50, + [1][1][2][0][RTW89_IC][9] = 52, + [1][1][2][0][RTW89_KCC][9] = 60, + [1][1][2][0][RTW89_ACMA][9] = 52, + [1][1][2][0][RTW89_CHILE][9] = 50, + [1][1][2][0][RTW89_UKRAINE][9] = 40, + [1][1][2][0][RTW89_MEXICO][9] = 76, + [1][1][2][0][RTW89_CN][9] = 50, + [1][1][2][0][RTW89_QATAR][9] = 52, + [1][1][2][0][RTW89_UK][9] = 52, + [1][1][2][0][RTW89_FCC][13] = 62, + [1][1][2][0][RTW89_ETSI][13] = 52, + [1][1][2][0][RTW89_MKK][13] = 50, + [1][1][2][0][RTW89_IC][13] = 52, + [1][1][2][0][RTW89_KCC][13] = 58, + [1][1][2][0][RTW89_ACMA][13] = 52, + [1][1][2][0][RTW89_CHILE][13] = 48, + [1][1][2][0][RTW89_UKRAINE][13] = 40, + [1][1][2][0][RTW89_MEXICO][13] = 62, + [1][1][2][0][RTW89_CN][13] = 50, + [1][1][2][0][RTW89_QATAR][13] = 52, + [1][1][2][0][RTW89_UK][13] = 52, + [1][1][2][0][RTW89_FCC][16] = 56, + [1][1][2][0][RTW89_ETSI][16] = 52, + [1][1][2][0][RTW89_MKK][16] = 70, + [1][1][2][0][RTW89_IC][16] = 56, + [1][1][2][0][RTW89_KCC][16] = 58, + [1][1][2][0][RTW89_ACMA][16] = 52, + [1][1][2][0][RTW89_CHILE][16] = 48, + [1][1][2][0][RTW89_UKRAINE][16] = 40, + [1][1][2][0][RTW89_MEXICO][16] = 56, + [1][1][2][0][RTW89_CN][16] = 127, + [1][1][2][0][RTW89_QATAR][16] = 52, + [1][1][2][0][RTW89_UK][16] = 52, + [1][1][2][0][RTW89_FCC][20] = 76, + [1][1][2][0][RTW89_ETSI][20] = 52, + [1][1][2][0][RTW89_MKK][20] = 70, + [1][1][2][0][RTW89_IC][20] = 76, + [1][1][2][0][RTW89_KCC][20] = 58, + [1][1][2][0][RTW89_ACMA][20] = 52, + [1][1][2][0][RTW89_CHILE][20] = 50, + [1][1][2][0][RTW89_UKRAINE][20] = 40, + [1][1][2][0][RTW89_MEXICO][20] = 76, + [1][1][2][0][RTW89_CN][20] = 127, + [1][1][2][0][RTW89_QATAR][20] = 52, + [1][1][2][0][RTW89_UK][20] = 52, + [1][1][2][0][RTW89_FCC][24] = 76, + [1][1][2][0][RTW89_ETSI][24] = 52, + [1][1][2][0][RTW89_MKK][24] = 70, + [1][1][2][0][RTW89_IC][24] = 127, + [1][1][2][0][RTW89_KCC][24] = 58, + [1][1][2][0][RTW89_ACMA][24] = 127, + [1][1][2][0][RTW89_CHILE][24] = 50, + [1][1][2][0][RTW89_UKRAINE][24] = 40, + [1][1][2][0][RTW89_MEXICO][24] = 76, + [1][1][2][0][RTW89_CN][24] = 127, + [1][1][2][0][RTW89_QATAR][24] = 52, + [1][1][2][0][RTW89_UK][24] = 52, + [1][1][2][0][RTW89_FCC][28] = 76, + [1][1][2][0][RTW89_ETSI][28] = 52, + [1][1][2][0][RTW89_MKK][28] = 70, + [1][1][2][0][RTW89_IC][28] = 127, + [1][1][2][0][RTW89_KCC][28] = 60, + [1][1][2][0][RTW89_ACMA][28] = 127, + [1][1][2][0][RTW89_CHILE][28] = 48, + [1][1][2][0][RTW89_UKRAINE][28] = 40, + [1][1][2][0][RTW89_MEXICO][28] = 76, + [1][1][2][0][RTW89_CN][28] = 127, + [1][1][2][0][RTW89_QATAR][28] = 52, + [1][1][2][0][RTW89_UK][28] = 52, + [1][1][2][0][RTW89_FCC][32] = 68, + [1][1][2][0][RTW89_ETSI][32] = 52, + [1][1][2][0][RTW89_MKK][32] = 70, + [1][1][2][0][RTW89_IC][32] = 68, + [1][1][2][0][RTW89_KCC][32] = 60, + [1][1][2][0][RTW89_ACMA][32] = 52, + [1][1][2][0][RTW89_CHILE][32] = 48, + [1][1][2][0][RTW89_UKRAINE][32] = 40, + [1][1][2][0][RTW89_MEXICO][32] = 68, + [1][1][2][0][RTW89_CN][32] = 127, + [1][1][2][0][RTW89_QATAR][32] = 52, + [1][1][2][0][RTW89_UK][32] = 52, + [1][1][2][0][RTW89_FCC][36] = 76, + [1][1][2][0][RTW89_ETSI][36] = 127, + [1][1][2][0][RTW89_MKK][36] = 70, + [1][1][2][0][RTW89_IC][36] = 76, + [1][1][2][0][RTW89_KCC][36] = 60, + [1][1][2][0][RTW89_ACMA][36] = 74, + [1][1][2][0][RTW89_CHILE][36] = 50, + [1][1][2][0][RTW89_UKRAINE][36] = 127, + [1][1][2][0][RTW89_MEXICO][36] = 76, + [1][1][2][0][RTW89_CN][36] = 127, + [1][1][2][0][RTW89_QATAR][36] = 127, + [1][1][2][0][RTW89_UK][36] = 74, + [1][1][2][0][RTW89_FCC][39] = 78, + [1][1][2][0][RTW89_ETSI][39] = 16, + [1][1][2][0][RTW89_MKK][39] = 127, + [1][1][2][0][RTW89_IC][39] = 78, + [1][1][2][0][RTW89_KCC][39] = 58, + [1][1][2][0][RTW89_ACMA][39] = 72, + [1][1][2][0][RTW89_CHILE][39] = 52, + [1][1][2][0][RTW89_UKRAINE][39] = 16, + [1][1][2][0][RTW89_MEXICO][39] = 78, + [1][1][2][0][RTW89_CN][39] = 70, + [1][1][2][0][RTW89_QATAR][39] = 16, + [1][1][2][0][RTW89_UK][39] = 52, + [1][1][2][0][RTW89_FCC][43] = 78, + [1][1][2][0][RTW89_ETSI][43] = 16, + [1][1][2][0][RTW89_MKK][43] = 127, + [1][1][2][0][RTW89_IC][43] = 78, + [1][1][2][0][RTW89_KCC][43] = 58, + [1][1][2][0][RTW89_ACMA][43] = 74, + [1][1][2][0][RTW89_CHILE][43] = 52, + [1][1][2][0][RTW89_UKRAINE][43] = 16, + [1][1][2][0][RTW89_MEXICO][43] = 78, + [1][1][2][0][RTW89_CN][43] = 74, + [1][1][2][0][RTW89_QATAR][43] = 16, + [1][1][2][0][RTW89_UK][43] = 52, + [1][1][2][0][RTW89_FCC][47] = 68, + [1][1][2][0][RTW89_ETSI][47] = 127, + [1][1][2][0][RTW89_MKK][47] = 127, + [1][1][2][0][RTW89_IC][47] = 127, + [1][1][2][0][RTW89_KCC][47] = 127, + [1][1][2][0][RTW89_ACMA][47] = 127, + [1][1][2][0][RTW89_CHILE][47] = 127, + [1][1][2][0][RTW89_UKRAINE][47] = 127, + [1][1][2][0][RTW89_MEXICO][47] = 127, + [1][1][2][0][RTW89_CN][47] = 127, + [1][1][2][0][RTW89_QATAR][47] = 127, + [1][1][2][0][RTW89_UK][47] = 127, + [1][1][2][0][RTW89_FCC][51] = 66, + [1][1][2][0][RTW89_ETSI][51] = 127, + [1][1][2][0][RTW89_MKK][51] = 127, + [1][1][2][0][RTW89_IC][51] = 127, + [1][1][2][0][RTW89_KCC][51] = 127, + [1][1][2][0][RTW89_ACMA][51] = 127, + [1][1][2][0][RTW89_CHILE][51] = 127, + [1][1][2][0][RTW89_UKRAINE][51] = 127, + [1][1][2][0][RTW89_MEXICO][51] = 127, + [1][1][2][0][RTW89_CN][51] = 127, + [1][1][2][0][RTW89_QATAR][51] = 127, + [1][1][2][0][RTW89_UK][51] = 127, + [1][1][2][1][RTW89_FCC][1] = 62, + [1][1][2][1][RTW89_ETSI][1] = 40, + [1][1][2][1][RTW89_MKK][1] = 50, + [1][1][2][1][RTW89_IC][1] = 40, + [1][1][2][1][RTW89_KCC][1] = 58, + [1][1][2][1][RTW89_ACMA][1] = 40, + [1][1][2][1][RTW89_CHILE][1] = 16, + [1][1][2][1][RTW89_UKRAINE][1] = 28, + [1][1][2][1][RTW89_MEXICO][1] = 50, + [1][1][2][1][RTW89_CN][1] = 38, + [1][1][2][1][RTW89_QATAR][1] = 40, + [1][1][2][1][RTW89_UK][1] = 40, + [1][1][2][1][RTW89_FCC][5] = 68, + [1][1][2][1][RTW89_ETSI][5] = 40, + [1][1][2][1][RTW89_MKK][5] = 50, + [1][1][2][1][RTW89_IC][5] = 40, + [1][1][2][1][RTW89_KCC][5] = 48, + [1][1][2][1][RTW89_ACMA][5] = 40, + [1][1][2][1][RTW89_CHILE][5] = 16, + [1][1][2][1][RTW89_UKRAINE][5] = 28, + [1][1][2][1][RTW89_MEXICO][5] = 50, + [1][1][2][1][RTW89_CN][5] = 38, + [1][1][2][1][RTW89_QATAR][5] = 40, + [1][1][2][1][RTW89_UK][5] = 40, + [1][1][2][1][RTW89_FCC][9] = 68, + [1][1][2][1][RTW89_ETSI][9] = 40, + [1][1][2][1][RTW89_MKK][9] = 50, + [1][1][2][1][RTW89_IC][9] = 40, + [1][1][2][1][RTW89_KCC][9] = 60, + [1][1][2][1][RTW89_ACMA][9] = 40, + [1][1][2][1][RTW89_CHILE][9] = 36, + [1][1][2][1][RTW89_UKRAINE][9] = 28, + [1][1][2][1][RTW89_MEXICO][9] = 68, + [1][1][2][1][RTW89_CN][9] = 38, + [1][1][2][1][RTW89_QATAR][9] = 40, + [1][1][2][1][RTW89_UK][9] = 40, + [1][1][2][1][RTW89_FCC][13] = 62, + [1][1][2][1][RTW89_ETSI][13] = 40, + [1][1][2][1][RTW89_MKK][13] = 50, + [1][1][2][1][RTW89_IC][13] = 40, + [1][1][2][1][RTW89_KCC][13] = 58, + [1][1][2][1][RTW89_ACMA][13] = 40, + [1][1][2][1][RTW89_CHILE][13] = 36, + [1][1][2][1][RTW89_UKRAINE][13] = 28, + [1][1][2][1][RTW89_MEXICO][13] = 62, + [1][1][2][1][RTW89_CN][13] = 38, + [1][1][2][1][RTW89_QATAR][13] = 40, + [1][1][2][1][RTW89_UK][13] = 40, + [1][1][2][1][RTW89_FCC][16] = 56, + [1][1][2][1][RTW89_ETSI][16] = 40, + [1][1][2][1][RTW89_MKK][16] = 70, + [1][1][2][1][RTW89_IC][16] = 56, + [1][1][2][1][RTW89_KCC][16] = 58, + [1][1][2][1][RTW89_ACMA][16] = 40, + [1][1][2][1][RTW89_CHILE][16] = 36, + [1][1][2][1][RTW89_UKRAINE][16] = 28, + [1][1][2][1][RTW89_MEXICO][16] = 56, + [1][1][2][1][RTW89_CN][16] = 127, + [1][1][2][1][RTW89_QATAR][16] = 40, + [1][1][2][1][RTW89_UK][16] = 40, + [1][1][2][1][RTW89_FCC][20] = 68, + [1][1][2][1][RTW89_ETSI][20] = 40, + [1][1][2][1][RTW89_MKK][20] = 70, + [1][1][2][1][RTW89_IC][20] = 68, + [1][1][2][1][RTW89_KCC][20] = 58, + [1][1][2][1][RTW89_ACMA][20] = 40, + [1][1][2][1][RTW89_CHILE][20] = 36, + [1][1][2][1][RTW89_UKRAINE][20] = 28, + [1][1][2][1][RTW89_MEXICO][20] = 68, + [1][1][2][1][RTW89_CN][20] = 127, + [1][1][2][1][RTW89_QATAR][20] = 40, + [1][1][2][1][RTW89_UK][20] = 40, + [1][1][2][1][RTW89_FCC][24] = 68, + [1][1][2][1][RTW89_ETSI][24] = 40, + [1][1][2][1][RTW89_MKK][24] = 70, + [1][1][2][1][RTW89_IC][24] = 127, + [1][1][2][1][RTW89_KCC][24] = 58, + [1][1][2][1][RTW89_ACMA][24] = 127, + [1][1][2][1][RTW89_CHILE][24] = 36, + [1][1][2][1][RTW89_UKRAINE][24] = 28, + [1][1][2][1][RTW89_MEXICO][24] = 68, + [1][1][2][1][RTW89_CN][24] = 127, + [1][1][2][1][RTW89_QATAR][24] = 40, + [1][1][2][1][RTW89_UK][24] = 40, + [1][1][2][1][RTW89_FCC][28] = 68, + [1][1][2][1][RTW89_ETSI][28] = 40, + [1][1][2][1][RTW89_MKK][28] = 70, + [1][1][2][1][RTW89_IC][28] = 127, + [1][1][2][1][RTW89_KCC][28] = 60, + [1][1][2][1][RTW89_ACMA][28] = 127, + [1][1][2][1][RTW89_CHILE][28] = 36, + [1][1][2][1][RTW89_UKRAINE][28] = 28, + [1][1][2][1][RTW89_MEXICO][28] = 68, + [1][1][2][1][RTW89_CN][28] = 127, + [1][1][2][1][RTW89_QATAR][28] = 40, + [1][1][2][1][RTW89_UK][28] = 40, + [1][1][2][1][RTW89_FCC][32] = 68, + [1][1][2][1][RTW89_ETSI][32] = 40, + [1][1][2][1][RTW89_MKK][32] = 70, + [1][1][2][1][RTW89_IC][32] = 68, + [1][1][2][1][RTW89_KCC][32] = 60, + [1][1][2][1][RTW89_ACMA][32] = 40, + [1][1][2][1][RTW89_CHILE][32] = 36, + [1][1][2][1][RTW89_UKRAINE][32] = 28, + [1][1][2][1][RTW89_MEXICO][32] = 68, + [1][1][2][1][RTW89_CN][32] = 127, + [1][1][2][1][RTW89_QATAR][32] = 40, + [1][1][2][1][RTW89_UK][32] = 40, + [1][1][2][1][RTW89_FCC][36] = 68, + [1][1][2][1][RTW89_ETSI][36] = 127, + [1][1][2][1][RTW89_MKK][36] = 70, + [1][1][2][1][RTW89_IC][36] = 68, + [1][1][2][1][RTW89_KCC][36] = 60, + [1][1][2][1][RTW89_ACMA][36] = 70, + [1][1][2][1][RTW89_CHILE][36] = 36, + [1][1][2][1][RTW89_UKRAINE][36] = 127, + [1][1][2][1][RTW89_MEXICO][36] = 68, + [1][1][2][1][RTW89_CN][36] = 127, + [1][1][2][1][RTW89_QATAR][36] = 127, + [1][1][2][1][RTW89_UK][36] = 62, + [1][1][2][1][RTW89_FCC][39] = 78, + [1][1][2][1][RTW89_ETSI][39] = 4, + [1][1][2][1][RTW89_MKK][39] = 127, + [1][1][2][1][RTW89_IC][39] = 78, + [1][1][2][1][RTW89_KCC][39] = 58, + [1][1][2][1][RTW89_ACMA][39] = 72, + [1][1][2][1][RTW89_CHILE][39] = 36, + [1][1][2][1][RTW89_UKRAINE][39] = 4, + [1][1][2][1][RTW89_MEXICO][39] = 78, + [1][1][2][1][RTW89_CN][39] = 70, + [1][1][2][1][RTW89_QATAR][39] = 4, + [1][1][2][1][RTW89_UK][39] = 40, + [1][1][2][1][RTW89_FCC][43] = 78, + [1][1][2][1][RTW89_ETSI][43] = 4, + [1][1][2][1][RTW89_MKK][43] = 127, + [1][1][2][1][RTW89_IC][43] = 78, + [1][1][2][1][RTW89_KCC][43] = 58, + [1][1][2][1][RTW89_ACMA][43] = 74, + [1][1][2][1][RTW89_CHILE][43] = 36, + [1][1][2][1][RTW89_UKRAINE][43] = 4, + [1][1][2][1][RTW89_MEXICO][43] = 78, + [1][1][2][1][RTW89_CN][43] = 74, + [1][1][2][1][RTW89_QATAR][43] = 4, + [1][1][2][1][RTW89_UK][43] = 40, + [1][1][2][1][RTW89_FCC][47] = 68, + [1][1][2][1][RTW89_ETSI][47] = 127, + [1][1][2][1][RTW89_MKK][47] = 127, + [1][1][2][1][RTW89_IC][47] = 127, + [1][1][2][1][RTW89_KCC][47] = 127, + [1][1][2][1][RTW89_ACMA][47] = 127, + [1][1][2][1][RTW89_CHILE][47] = 127, + [1][1][2][1][RTW89_UKRAINE][47] = 127, + [1][1][2][1][RTW89_MEXICO][47] = 127, + [1][1][2][1][RTW89_CN][47] = 127, + [1][1][2][1][RTW89_QATAR][47] = 127, + [1][1][2][1][RTW89_UK][47] = 127, + [1][1][2][1][RTW89_FCC][51] = 66, + [1][1][2][1][RTW89_ETSI][51] = 127, + [1][1][2][1][RTW89_MKK][51] = 127, + [1][1][2][1][RTW89_IC][51] = 127, + [1][1][2][1][RTW89_KCC][51] = 127, + [1][1][2][1][RTW89_ACMA][51] = 127, + [1][1][2][1][RTW89_CHILE][51] = 127, + [1][1][2][1][RTW89_UKRAINE][51] = 127, + [1][1][2][1][RTW89_MEXICO][51] = 127, + [1][1][2][1][RTW89_CN][51] = 127, + [1][1][2][1][RTW89_QATAR][51] = 127, + [1][1][2][1][RTW89_UK][51] = 127, + [2][0][2][0][RTW89_FCC][3] = 64, + [2][0][2][0][RTW89_ETSI][3] = 64, + [2][0][2][0][RTW89_MKK][3] = 64, + [2][0][2][0][RTW89_IC][3] = 62, + [2][0][2][0][RTW89_KCC][3] = 68, + [2][0][2][0][RTW89_ACMA][3] = 64, + [2][0][2][0][RTW89_CHILE][3] = 42, + [2][0][2][0][RTW89_UKRAINE][3] = 52, + [2][0][2][0][RTW89_MEXICO][3] = 62, + [2][0][2][0][RTW89_CN][3] = 62, + [2][0][2][0][RTW89_QATAR][3] = 64, + [2][0][2][0][RTW89_UK][3] = 64, + [2][0][2][0][RTW89_FCC][11] = 66, + [2][0][2][0][RTW89_ETSI][11] = 64, + [2][0][2][0][RTW89_MKK][11] = 64, + [2][0][2][0][RTW89_IC][11] = 64, + [2][0][2][0][RTW89_KCC][11] = 70, + [2][0][2][0][RTW89_ACMA][11] = 64, + [2][0][2][0][RTW89_CHILE][11] = 66, + [2][0][2][0][RTW89_UKRAINE][11] = 52, + [2][0][2][0][RTW89_MEXICO][11] = 66, + [2][0][2][0][RTW89_CN][11] = 62, + [2][0][2][0][RTW89_QATAR][11] = 64, + [2][0][2][0][RTW89_UK][11] = 64, + [2][0][2][0][RTW89_FCC][18] = 62, + [2][0][2][0][RTW89_ETSI][18] = 64, + [2][0][2][0][RTW89_MKK][18] = 70, + [2][0][2][0][RTW89_IC][18] = 62, + [2][0][2][0][RTW89_KCC][18] = 64, + [2][0][2][0][RTW89_ACMA][18] = 64, + [2][0][2][0][RTW89_CHILE][18] = 64, + [2][0][2][0][RTW89_UKRAINE][18] = 52, + [2][0][2][0][RTW89_MEXICO][18] = 62, + [2][0][2][0][RTW89_CN][18] = 127, + [2][0][2][0][RTW89_QATAR][18] = 64, + [2][0][2][0][RTW89_UK][18] = 64, + [2][0][2][0][RTW89_FCC][26] = 74, + [2][0][2][0][RTW89_ETSI][26] = 64, + [2][0][2][0][RTW89_MKK][26] = 70, + [2][0][2][0][RTW89_IC][26] = 127, + [2][0][2][0][RTW89_KCC][26] = 70, + [2][0][2][0][RTW89_ACMA][26] = 127, + [2][0][2][0][RTW89_CHILE][26] = 64, + [2][0][2][0][RTW89_UKRAINE][26] = 52, + [2][0][2][0][RTW89_MEXICO][26] = 74, + [2][0][2][0][RTW89_CN][26] = 127, + [2][0][2][0][RTW89_QATAR][26] = 64, + [2][0][2][0][RTW89_UK][26] = 64, + [2][0][2][0][RTW89_FCC][34] = 74, + [2][0][2][0][RTW89_ETSI][34] = 127, + [2][0][2][0][RTW89_MKK][34] = 70, + [2][0][2][0][RTW89_IC][34] = 74, + [2][0][2][0][RTW89_KCC][34] = 70, + [2][0][2][0][RTW89_ACMA][34] = 70, + [2][0][2][0][RTW89_CHILE][34] = 64, + [2][0][2][0][RTW89_UKRAINE][34] = 127, + [2][0][2][0][RTW89_MEXICO][34] = 74, + [2][0][2][0][RTW89_CN][34] = 127, + [2][0][2][0][RTW89_QATAR][34] = 127, + [2][0][2][0][RTW89_UK][34] = 70, + [2][0][2][0][RTW89_FCC][41] = 74, + [2][0][2][0][RTW89_ETSI][41] = 28, + [2][0][2][0][RTW89_MKK][41] = 127, + [2][0][2][0][RTW89_IC][41] = 74, + [2][0][2][0][RTW89_KCC][41] = 66, + [2][0][2][0][RTW89_ACMA][41] = 70, + [2][0][2][0][RTW89_CHILE][41] = 64, + [2][0][2][0][RTW89_UKRAINE][41] = 28, + [2][0][2][0][RTW89_MEXICO][41] = 74, + [2][0][2][0][RTW89_CN][41] = 70, + [2][0][2][0][RTW89_QATAR][41] = 28, + [2][0][2][0][RTW89_UK][41] = 64, + [2][0][2][0][RTW89_FCC][49] = 64, + [2][0][2][0][RTW89_ETSI][49] = 127, + [2][0][2][0][RTW89_MKK][49] = 127, + [2][0][2][0][RTW89_IC][49] = 127, + [2][0][2][0][RTW89_KCC][49] = 127, + [2][0][2][0][RTW89_ACMA][49] = 127, + [2][0][2][0][RTW89_CHILE][49] = 127, + [2][0][2][0][RTW89_UKRAINE][49] = 127, + [2][0][2][0][RTW89_MEXICO][49] = 127, + [2][0][2][0][RTW89_CN][49] = 127, + [2][0][2][0][RTW89_QATAR][49] = 127, + [2][0][2][0][RTW89_UK][49] = 127, + [2][1][2][0][RTW89_FCC][3] = 56, + [2][1][2][0][RTW89_ETSI][3] = 52, + [2][1][2][0][RTW89_MKK][3] = 52, + [2][1][2][0][RTW89_IC][3] = 52, + [2][1][2][0][RTW89_KCC][3] = 54, + [2][1][2][0][RTW89_ACMA][3] = 52, + [2][1][2][0][RTW89_CHILE][3] = 28, + [2][1][2][0][RTW89_UKRAINE][3] = 40, + [2][1][2][0][RTW89_MEXICO][3] = 50, + [2][1][2][0][RTW89_CN][3] = 50, + [2][1][2][0][RTW89_QATAR][3] = 52, + [2][1][2][0][RTW89_UK][3] = 52, + [2][1][2][0][RTW89_FCC][11] = 62, + [2][1][2][0][RTW89_ETSI][11] = 52, + [2][1][2][0][RTW89_MKK][11] = 52, + [2][1][2][0][RTW89_IC][11] = 52, + [2][1][2][0][RTW89_KCC][11] = 56, + [2][1][2][0][RTW89_ACMA][11] = 52, + [2][1][2][0][RTW89_CHILE][11] = 52, + [2][1][2][0][RTW89_UKRAINE][11] = 40, + [2][1][2][0][RTW89_MEXICO][11] = 62, + [2][1][2][0][RTW89_CN][11] = 50, + [2][1][2][0][RTW89_QATAR][11] = 52, + [2][1][2][0][RTW89_UK][11] = 52, + [2][1][2][0][RTW89_FCC][18] = 56, + [2][1][2][0][RTW89_ETSI][18] = 52, + [2][1][2][0][RTW89_MKK][18] = 70, + [2][1][2][0][RTW89_IC][18] = 56, + [2][1][2][0][RTW89_KCC][18] = 58, + [2][1][2][0][RTW89_ACMA][18] = 52, + [2][1][2][0][RTW89_CHILE][18] = 48, + [2][1][2][0][RTW89_UKRAINE][18] = 40, + [2][1][2][0][RTW89_MEXICO][18] = 56, + [2][1][2][0][RTW89_CN][18] = 127, + [2][1][2][0][RTW89_QATAR][18] = 52, + [2][1][2][0][RTW89_UK][18] = 52, + [2][1][2][0][RTW89_FCC][26] = 70, + [2][1][2][0][RTW89_ETSI][26] = 52, + [2][1][2][0][RTW89_MKK][26] = 70, + [2][1][2][0][RTW89_IC][26] = 127, + [2][1][2][0][RTW89_KCC][26] = 56, + [2][1][2][0][RTW89_ACMA][26] = 127, + [2][1][2][0][RTW89_CHILE][26] = 50, + [2][1][2][0][RTW89_UKRAINE][26] = 40, + [2][1][2][0][RTW89_MEXICO][26] = 70, + [2][1][2][0][RTW89_CN][26] = 127, + [2][1][2][0][RTW89_QATAR][26] = 52, + [2][1][2][0][RTW89_UK][26] = 52, + [2][1][2][0][RTW89_FCC][34] = 74, + [2][1][2][0][RTW89_ETSI][34] = 127, + [2][1][2][0][RTW89_MKK][34] = 70, + [2][1][2][0][RTW89_IC][34] = 74, + [2][1][2][0][RTW89_KCC][34] = 56, + [2][1][2][0][RTW89_ACMA][34] = 70, + [2][1][2][0][RTW89_CHILE][34] = 50, + [2][1][2][0][RTW89_UKRAINE][34] = 127, + [2][1][2][0][RTW89_MEXICO][34] = 74, + [2][1][2][0][RTW89_CN][34] = 127, + [2][1][2][0][RTW89_QATAR][34] = 127, + [2][1][2][0][RTW89_UK][34] = 68, + [2][1][2][0][RTW89_FCC][41] = 74, + [2][1][2][0][RTW89_ETSI][41] = 16, + [2][1][2][0][RTW89_MKK][41] = 127, + [2][1][2][0][RTW89_IC][41] = 74, + [2][1][2][0][RTW89_KCC][41] = 56, + [2][1][2][0][RTW89_ACMA][41] = 70, + [2][1][2][0][RTW89_CHILE][41] = 50, + [2][1][2][0][RTW89_UKRAINE][41] = 16, + [2][1][2][0][RTW89_MEXICO][41] = 74, + [2][1][2][0][RTW89_CN][41] = 70, + [2][1][2][0][RTW89_QATAR][41] = 16, + [2][1][2][0][RTW89_UK][41] = 52, + [2][1][2][0][RTW89_FCC][49] = 58, + [2][1][2][0][RTW89_ETSI][49] = 127, + [2][1][2][0][RTW89_MKK][49] = 127, + [2][1][2][0][RTW89_IC][49] = 127, + [2][1][2][0][RTW89_KCC][49] = 127, + [2][1][2][0][RTW89_ACMA][49] = 127, + [2][1][2][0][RTW89_CHILE][49] = 127, + [2][1][2][0][RTW89_UKRAINE][49] = 127, + [2][1][2][0][RTW89_MEXICO][49] = 127, + [2][1][2][0][RTW89_CN][49] = 127, + [2][1][2][0][RTW89_QATAR][49] = 127, + [2][1][2][0][RTW89_UK][49] = 127, + [2][1][2][1][RTW89_FCC][3] = 56, + [2][1][2][1][RTW89_ETSI][3] = 40, + [2][1][2][1][RTW89_MKK][3] = 52, + [2][1][2][1][RTW89_IC][3] = 40, + [2][1][2][1][RTW89_KCC][3] = 54, + [2][1][2][1][RTW89_ACMA][3] = 40, + [2][1][2][1][RTW89_CHILE][3] = 16, + [2][1][2][1][RTW89_UKRAINE][3] = 28, + [2][1][2][1][RTW89_MEXICO][3] = 50, + [2][1][2][1][RTW89_CN][3] = 38, + [2][1][2][1][RTW89_QATAR][3] = 40, + [2][1][2][1][RTW89_UK][3] = 40, + [2][1][2][1][RTW89_FCC][11] = 62, + [2][1][2][1][RTW89_ETSI][11] = 40, + [2][1][2][1][RTW89_MKK][11] = 52, + [2][1][2][1][RTW89_IC][11] = 40, + [2][1][2][1][RTW89_KCC][11] = 56, + [2][1][2][1][RTW89_ACMA][11] = 40, + [2][1][2][1][RTW89_CHILE][11] = 34, + [2][1][2][1][RTW89_UKRAINE][11] = 28, + [2][1][2][1][RTW89_MEXICO][11] = 62, + [2][1][2][1][RTW89_CN][11] = 38, + [2][1][2][1][RTW89_QATAR][11] = 40, + [2][1][2][1][RTW89_UK][11] = 40, + [2][1][2][1][RTW89_FCC][18] = 56, + [2][1][2][1][RTW89_ETSI][18] = 40, + [2][1][2][1][RTW89_MKK][18] = 70, + [2][1][2][1][RTW89_IC][18] = 56, + [2][1][2][1][RTW89_KCC][18] = 58, + [2][1][2][1][RTW89_ACMA][18] = 40, + [2][1][2][1][RTW89_CHILE][18] = 34, + [2][1][2][1][RTW89_UKRAINE][18] = 28, + [2][1][2][1][RTW89_MEXICO][18] = 56, + [2][1][2][1][RTW89_CN][18] = 127, + [2][1][2][1][RTW89_QATAR][18] = 40, + [2][1][2][1][RTW89_UK][18] = 40, + [2][1][2][1][RTW89_FCC][26] = 68, + [2][1][2][1][RTW89_ETSI][26] = 40, + [2][1][2][1][RTW89_MKK][26] = 70, + [2][1][2][1][RTW89_IC][26] = 127, + [2][1][2][1][RTW89_KCC][26] = 56, + [2][1][2][1][RTW89_ACMA][26] = 127, + [2][1][2][1][RTW89_CHILE][26] = 34, + [2][1][2][1][RTW89_UKRAINE][26] = 28, + [2][1][2][1][RTW89_MEXICO][26] = 68, + [2][1][2][1][RTW89_CN][26] = 127, + [2][1][2][1][RTW89_QATAR][26] = 40, + [2][1][2][1][RTW89_UK][26] = 40, + [2][1][2][1][RTW89_FCC][34] = 68, + [2][1][2][1][RTW89_ETSI][34] = 127, + [2][1][2][1][RTW89_MKK][34] = 70, + [2][1][2][1][RTW89_IC][34] = 68, + [2][1][2][1][RTW89_KCC][34] = 56, + [2][1][2][1][RTW89_ACMA][34] = 70, + [2][1][2][1][RTW89_CHILE][34] = 34, + [2][1][2][1][RTW89_UKRAINE][34] = 127, + [2][1][2][1][RTW89_MEXICO][34] = 68, + [2][1][2][1][RTW89_CN][34] = 127, + [2][1][2][1][RTW89_QATAR][34] = 127, + [2][1][2][1][RTW89_UK][34] = 56, + [2][1][2][1][RTW89_FCC][41] = 74, + [2][1][2][1][RTW89_ETSI][41] = 4, + [2][1][2][1][RTW89_MKK][41] = 127, + [2][1][2][1][RTW89_IC][41] = 74, + [2][1][2][1][RTW89_KCC][41] = 56, + [2][1][2][1][RTW89_ACMA][41] = 70, + [2][1][2][1][RTW89_CHILE][41] = 36, + [2][1][2][1][RTW89_UKRAINE][41] = 4, + [2][1][2][1][RTW89_MEXICO][41] = 74, + [2][1][2][1][RTW89_CN][41] = 70, + [2][1][2][1][RTW89_QATAR][41] = 4, + [2][1][2][1][RTW89_UK][41] = 38, + [2][1][2][1][RTW89_FCC][49] = 58, + [2][1][2][1][RTW89_ETSI][49] = 127, + [2][1][2][1][RTW89_MKK][49] = 127, + [2][1][2][1][RTW89_IC][49] = 127, + [2][1][2][1][RTW89_KCC][49] = 127, + [2][1][2][1][RTW89_ACMA][49] = 127, + [2][1][2][1][RTW89_CHILE][49] = 127, + [2][1][2][1][RTW89_UKRAINE][49] = 127, + [2][1][2][1][RTW89_MEXICO][49] = 127, + [2][1][2][1][RTW89_CN][49] = 127, + [2][1][2][1][RTW89_QATAR][49] = 127, + [2][1][2][1][RTW89_UK][49] = 127, +}; + +const s8 rtw89_8852b_txpwr_lmt_ru_2g[RTW89_RU_NUM][RTW89_NTX_NUM] + [RTW89_REGD_NUM][RTW89_2G_CH_NUM] = { + [0][0][RTW89_WW][0] = 32, + [0][0][RTW89_WW][1] = 32, + [0][0][RTW89_WW][2] = 32, + [0][0][RTW89_WW][3] = 32, + [0][0][RTW89_WW][4] = 32, + [0][0][RTW89_WW][5] = 32, + [0][0][RTW89_WW][6] = 32, + [0][0][RTW89_WW][7] = 32, + [0][0][RTW89_WW][8] = 32, + [0][0][RTW89_WW][9] = 32, + [0][0][RTW89_WW][10] = 32, + [0][0][RTW89_WW][11] = 32, + [0][0][RTW89_WW][12] = 32, + [0][0][RTW89_WW][13] = 0, + [0][1][RTW89_WW][0] = 20, + [0][1][RTW89_WW][1] = 22, + [0][1][RTW89_WW][2] = 22, + [0][1][RTW89_WW][3] = 22, + [0][1][RTW89_WW][4] = 22, + [0][1][RTW89_WW][5] = 22, + [0][1][RTW89_WW][6] = 22, + [0][1][RTW89_WW][7] = 22, + [0][1][RTW89_WW][8] = 22, + [0][1][RTW89_WW][9] = 22, + [0][1][RTW89_WW][10] = 22, + [0][1][RTW89_WW][11] = 22, + [0][1][RTW89_WW][12] = 20, + [0][1][RTW89_WW][13] = 0, + [1][0][RTW89_WW][0] = 42, + [1][0][RTW89_WW][1] = 44, + [1][0][RTW89_WW][2] = 44, + [1][0][RTW89_WW][3] = 44, + [1][0][RTW89_WW][4] = 44, + [1][0][RTW89_WW][5] = 44, + [1][0][RTW89_WW][6] = 44, + [1][0][RTW89_WW][7] = 44, + [1][0][RTW89_WW][8] = 44, + [1][0][RTW89_WW][9] = 44, + [1][0][RTW89_WW][10] = 44, + [1][0][RTW89_WW][11] = 44, + [1][0][RTW89_WW][12] = 38, + [1][0][RTW89_WW][13] = 0, + [1][1][RTW89_WW][0] = 32, + [1][1][RTW89_WW][1] = 32, + [1][1][RTW89_WW][2] = 32, + [1][1][RTW89_WW][3] = 32, + [1][1][RTW89_WW][4] = 32, + [1][1][RTW89_WW][5] = 32, + [1][1][RTW89_WW][6] = 32, + [1][1][RTW89_WW][7] = 32, + [1][1][RTW89_WW][8] = 32, + [1][1][RTW89_WW][9] = 32, + [1][1][RTW89_WW][10] = 32, + [1][1][RTW89_WW][11] = 32, + [1][1][RTW89_WW][12] = 32, + [1][1][RTW89_WW][13] = 0, + [2][0][RTW89_WW][0] = 56, + [2][0][RTW89_WW][1] = 56, + [2][0][RTW89_WW][2] = 56, + [2][0][RTW89_WW][3] = 56, + [2][0][RTW89_WW][4] = 56, + [2][0][RTW89_WW][5] = 56, + [2][0][RTW89_WW][6] = 56, + [2][0][RTW89_WW][7] = 56, + [2][0][RTW89_WW][8] = 56, + [2][0][RTW89_WW][9] = 56, + [2][0][RTW89_WW][10] = 56, + [2][0][RTW89_WW][11] = 50, + [2][0][RTW89_WW][12] = 46, + [2][0][RTW89_WW][13] = 0, + [2][1][RTW89_WW][0] = 44, + [2][1][RTW89_WW][1] = 44, + [2][1][RTW89_WW][2] = 44, + [2][1][RTW89_WW][3] = 44, + [2][1][RTW89_WW][4] = 44, + [2][1][RTW89_WW][5] = 44, + [2][1][RTW89_WW][6] = 44, + [2][1][RTW89_WW][7] = 44, + [2][1][RTW89_WW][8] = 44, + [2][1][RTW89_WW][9] = 44, + [2][1][RTW89_WW][10] = 44, + [2][1][RTW89_WW][11] = 38, + [2][1][RTW89_WW][12] = 34, + [2][1][RTW89_WW][13] = 0, + [0][0][RTW89_FCC][0] = 68, + [0][0][RTW89_ETSI][0] = 32, + [0][0][RTW89_MKK][0] = 42, + [0][0][RTW89_IC][0] = 68, + [0][0][RTW89_KCC][0] = 44, + [0][0][RTW89_ACMA][0] = 32, + [0][0][RTW89_CHILE][0] = 66, + [0][0][RTW89_UKRAINE][0] = 32, + [0][0][RTW89_MEXICO][0] = 68, + [0][0][RTW89_CN][0] = 32, + [0][0][RTW89_QATAR][0] = 32, + [0][0][RTW89_UK][0] = 32, + [0][0][RTW89_FCC][1] = 68, + [0][0][RTW89_ETSI][1] = 32, + [0][0][RTW89_MKK][1] = 42, + [0][0][RTW89_IC][1] = 68, + [0][0][RTW89_KCC][1] = 44, + [0][0][RTW89_ACMA][1] = 32, + [0][0][RTW89_CHILE][1] = 64, + [0][0][RTW89_UKRAINE][1] = 32, + [0][0][RTW89_MEXICO][1] = 68, + [0][0][RTW89_CN][1] = 32, + [0][0][RTW89_QATAR][1] = 32, + [0][0][RTW89_UK][1] = 32, + [0][0][RTW89_FCC][2] = 72, + [0][0][RTW89_ETSI][2] = 32, + [0][0][RTW89_MKK][2] = 42, + [0][0][RTW89_IC][2] = 72, + [0][0][RTW89_KCC][2] = 44, + [0][0][RTW89_ACMA][2] = 32, + [0][0][RTW89_CHILE][2] = 64, + [0][0][RTW89_UKRAINE][2] = 32, + [0][0][RTW89_MEXICO][2] = 72, + [0][0][RTW89_CN][2] = 32, + [0][0][RTW89_QATAR][2] = 32, + [0][0][RTW89_UK][2] = 32, + [0][0][RTW89_FCC][3] = 76, + [0][0][RTW89_ETSI][3] = 32, + [0][0][RTW89_MKK][3] = 42, + [0][0][RTW89_IC][3] = 76, + [0][0][RTW89_KCC][3] = 44, + [0][0][RTW89_ACMA][3] = 32, + [0][0][RTW89_CHILE][3] = 64, + [0][0][RTW89_UKRAINE][3] = 32, + [0][0][RTW89_MEXICO][3] = 76, + [0][0][RTW89_CN][3] = 32, + [0][0][RTW89_QATAR][3] = 32, + [0][0][RTW89_UK][3] = 32, + [0][0][RTW89_FCC][4] = 76, + [0][0][RTW89_ETSI][4] = 32, + [0][0][RTW89_MKK][4] = 42, + [0][0][RTW89_IC][4] = 76, + [0][0][RTW89_KCC][4] = 44, + [0][0][RTW89_ACMA][4] = 32, + [0][0][RTW89_CHILE][4] = 64, + [0][0][RTW89_UKRAINE][4] = 32, + [0][0][RTW89_MEXICO][4] = 76, + [0][0][RTW89_CN][4] = 32, + [0][0][RTW89_QATAR][4] = 32, + [0][0][RTW89_UK][4] = 32, + [0][0][RTW89_FCC][5] = 84, + [0][0][RTW89_ETSI][5] = 32, + [0][0][RTW89_MKK][5] = 42, + [0][0][RTW89_IC][5] = 84, + [0][0][RTW89_KCC][5] = 44, + [0][0][RTW89_ACMA][5] = 32, + [0][0][RTW89_CHILE][5] = 64, + [0][0][RTW89_UKRAINE][5] = 32, + [0][0][RTW89_MEXICO][5] = 84, + [0][0][RTW89_CN][5] = 32, + [0][0][RTW89_QATAR][5] = 32, + [0][0][RTW89_UK][5] = 32, + [0][0][RTW89_FCC][6] = 74, + [0][0][RTW89_ETSI][6] = 32, + [0][0][RTW89_MKK][6] = 42, + [0][0][RTW89_IC][6] = 74, + [0][0][RTW89_KCC][6] = 44, + [0][0][RTW89_ACMA][6] = 32, + [0][0][RTW89_CHILE][6] = 64, + [0][0][RTW89_UKRAINE][6] = 32, + [0][0][RTW89_MEXICO][6] = 74, + [0][0][RTW89_CN][6] = 32, + [0][0][RTW89_QATAR][6] = 32, + [0][0][RTW89_UK][6] = 32, + [0][0][RTW89_FCC][7] = 74, + [0][0][RTW89_ETSI][7] = 32, + [0][0][RTW89_MKK][7] = 42, + [0][0][RTW89_IC][7] = 74, + [0][0][RTW89_KCC][7] = 44, + [0][0][RTW89_ACMA][7] = 32, + [0][0][RTW89_CHILE][7] = 64, + [0][0][RTW89_UKRAINE][7] = 32, + [0][0][RTW89_MEXICO][7] = 74, + [0][0][RTW89_CN][7] = 32, + [0][0][RTW89_QATAR][7] = 32, + [0][0][RTW89_UK][7] = 32, + [0][0][RTW89_FCC][8] = 70, + [0][0][RTW89_ETSI][8] = 32, + [0][0][RTW89_MKK][8] = 42, + [0][0][RTW89_IC][8] = 70, + [0][0][RTW89_KCC][8] = 44, + [0][0][RTW89_ACMA][8] = 32, + [0][0][RTW89_CHILE][8] = 64, + [0][0][RTW89_UKRAINE][8] = 32, + [0][0][RTW89_MEXICO][8] = 70, + [0][0][RTW89_CN][8] = 32, + [0][0][RTW89_QATAR][8] = 32, + [0][0][RTW89_UK][8] = 32, + [0][0][RTW89_FCC][9] = 66, + [0][0][RTW89_ETSI][9] = 32, + [0][0][RTW89_MKK][9] = 42, + [0][0][RTW89_IC][9] = 66, + [0][0][RTW89_KCC][9] = 42, + [0][0][RTW89_ACMA][9] = 32, + [0][0][RTW89_CHILE][9] = 64, + [0][0][RTW89_UKRAINE][9] = 32, + [0][0][RTW89_MEXICO][9] = 66, + [0][0][RTW89_CN][9] = 32, + [0][0][RTW89_QATAR][9] = 32, + [0][0][RTW89_UK][9] = 32, + [0][0][RTW89_FCC][10] = 66, + [0][0][RTW89_ETSI][10] = 32, + [0][0][RTW89_MKK][10] = 42, + [0][0][RTW89_IC][10] = 66, + [0][0][RTW89_KCC][10] = 42, + [0][0][RTW89_ACMA][10] = 32, + [0][0][RTW89_CHILE][10] = 66, + [0][0][RTW89_UKRAINE][10] = 32, + [0][0][RTW89_MEXICO][10] = 66, + [0][0][RTW89_CN][10] = 32, + [0][0][RTW89_QATAR][10] = 32, + [0][0][RTW89_UK][10] = 32, + [0][0][RTW89_FCC][11] = 50, + [0][0][RTW89_ETSI][11] = 32, + [0][0][RTW89_MKK][11] = 42, + [0][0][RTW89_IC][11] = 50, + [0][0][RTW89_KCC][11] = 42, + [0][0][RTW89_ACMA][11] = 32, + [0][0][RTW89_CHILE][11] = 64, + [0][0][RTW89_UKRAINE][11] = 32, + [0][0][RTW89_MEXICO][11] = 50, + [0][0][RTW89_CN][11] = 32, + [0][0][RTW89_QATAR][11] = 32, + [0][0][RTW89_UK][11] = 32, + [0][0][RTW89_FCC][12] = 32, + [0][0][RTW89_ETSI][12] = 32, + [0][0][RTW89_MKK][12] = 42, + [0][0][RTW89_IC][12] = 32, + [0][0][RTW89_KCC][12] = 42, + [0][0][RTW89_ACMA][12] = 32, + [0][0][RTW89_CHILE][12] = 64, + [0][0][RTW89_UKRAINE][12] = 32, + [0][0][RTW89_MEXICO][12] = 32, + [0][0][RTW89_CN][12] = 32, + [0][0][RTW89_QATAR][12] = 32, + [0][0][RTW89_UK][12] = 32, + [0][0][RTW89_FCC][13] = 127, + [0][0][RTW89_ETSI][13] = 127, + [0][0][RTW89_MKK][13] = 127, + [0][0][RTW89_IC][13] = 127, + [0][0][RTW89_KCC][13] = 127, + [0][0][RTW89_ACMA][13] = 127, + [0][0][RTW89_CHILE][13] = 127, + [0][0][RTW89_UKRAINE][13] = 127, + [0][0][RTW89_MEXICO][13] = 127, + [0][0][RTW89_CN][13] = 127, + [0][0][RTW89_QATAR][13] = 127, + [0][0][RTW89_UK][13] = 127, + [0][1][RTW89_FCC][0] = 54, + [0][1][RTW89_ETSI][0] = 20, + [0][1][RTW89_MKK][0] = 32, + [0][1][RTW89_IC][0] = 54, + [0][1][RTW89_KCC][0] = 32, + [0][1][RTW89_ACMA][0] = 20, + [0][1][RTW89_CHILE][0] = 50, + [0][1][RTW89_UKRAINE][0] = 20, + [0][1][RTW89_MEXICO][0] = 54, + [0][1][RTW89_CN][0] = 20, + [0][1][RTW89_QATAR][0] = 20, + [0][1][RTW89_UK][0] = 20, + [0][1][RTW89_FCC][1] = 54, + [0][1][RTW89_ETSI][1] = 22, + [0][1][RTW89_MKK][1] = 32, + [0][1][RTW89_IC][1] = 54, + [0][1][RTW89_KCC][1] = 32, + [0][1][RTW89_ACMA][1] = 22, + [0][1][RTW89_CHILE][1] = 50, + [0][1][RTW89_UKRAINE][1] = 22, + [0][1][RTW89_MEXICO][1] = 54, + [0][1][RTW89_CN][1] = 22, + [0][1][RTW89_QATAR][1] = 22, + [0][1][RTW89_UK][1] = 22, + [0][1][RTW89_FCC][2] = 58, + [0][1][RTW89_ETSI][2] = 22, + [0][1][RTW89_MKK][2] = 32, + [0][1][RTW89_IC][2] = 58, + [0][1][RTW89_KCC][2] = 32, + [0][1][RTW89_ACMA][2] = 22, + [0][1][RTW89_CHILE][2] = 50, + [0][1][RTW89_UKRAINE][2] = 22, + [0][1][RTW89_MEXICO][2] = 58, + [0][1][RTW89_CN][2] = 22, + [0][1][RTW89_QATAR][2] = 22, + [0][1][RTW89_UK][2] = 22, + [0][1][RTW89_FCC][3] = 62, + [0][1][RTW89_ETSI][3] = 22, + [0][1][RTW89_MKK][3] = 32, + [0][1][RTW89_IC][3] = 62, + [0][1][RTW89_KCC][3] = 32, + [0][1][RTW89_ACMA][3] = 22, + [0][1][RTW89_CHILE][3] = 50, + [0][1][RTW89_UKRAINE][3] = 22, + [0][1][RTW89_MEXICO][3] = 62, + [0][1][RTW89_CN][3] = 22, + [0][1][RTW89_QATAR][3] = 22, + [0][1][RTW89_UK][3] = 22, + [0][1][RTW89_FCC][4] = 66, + [0][1][RTW89_ETSI][4] = 22, + [0][1][RTW89_MKK][4] = 32, + [0][1][RTW89_IC][4] = 66, + [0][1][RTW89_KCC][4] = 30, + [0][1][RTW89_ACMA][4] = 22, + [0][1][RTW89_CHILE][4] = 50, + [0][1][RTW89_UKRAINE][4] = 22, + [0][1][RTW89_MEXICO][4] = 66, + [0][1][RTW89_CN][4] = 22, + [0][1][RTW89_QATAR][4] = 22, + [0][1][RTW89_UK][4] = 22, + [0][1][RTW89_FCC][5] = 74, + [0][1][RTW89_ETSI][5] = 22, + [0][1][RTW89_MKK][5] = 32, + [0][1][RTW89_IC][5] = 74, + [0][1][RTW89_KCC][5] = 30, + [0][1][RTW89_ACMA][5] = 22, + [0][1][RTW89_CHILE][5] = 52, + [0][1][RTW89_UKRAINE][5] = 22, + [0][1][RTW89_MEXICO][5] = 74, + [0][1][RTW89_CN][5] = 22, + [0][1][RTW89_QATAR][5] = 22, + [0][1][RTW89_UK][5] = 22, + [0][1][RTW89_FCC][6] = 66, + [0][1][RTW89_ETSI][6] = 22, + [0][1][RTW89_MKK][6] = 30, + [0][1][RTW89_IC][6] = 66, + [0][1][RTW89_KCC][6] = 30, + [0][1][RTW89_ACMA][6] = 22, + [0][1][RTW89_CHILE][6] = 50, + [0][1][RTW89_UKRAINE][6] = 22, + [0][1][RTW89_MEXICO][6] = 66, + [0][1][RTW89_CN][6] = 22, + [0][1][RTW89_QATAR][6] = 22, + [0][1][RTW89_UK][6] = 22, + [0][1][RTW89_FCC][7] = 62, + [0][1][RTW89_ETSI][7] = 22, + [0][1][RTW89_MKK][7] = 32, + [0][1][RTW89_IC][7] = 62, + [0][1][RTW89_KCC][7] = 30, + [0][1][RTW89_ACMA][7] = 22, + [0][1][RTW89_CHILE][7] = 50, + [0][1][RTW89_UKRAINE][7] = 22, + [0][1][RTW89_MEXICO][7] = 62, + [0][1][RTW89_CN][7] = 22, + [0][1][RTW89_QATAR][7] = 22, + [0][1][RTW89_UK][7] = 22, + [0][1][RTW89_FCC][8] = 58, + [0][1][RTW89_ETSI][8] = 22, + [0][1][RTW89_MKK][8] = 32, + [0][1][RTW89_IC][8] = 58, + [0][1][RTW89_KCC][8] = 30, + [0][1][RTW89_ACMA][8] = 22, + [0][1][RTW89_CHILE][8] = 50, + [0][1][RTW89_UKRAINE][8] = 22, + [0][1][RTW89_MEXICO][8] = 58, + [0][1][RTW89_CN][8] = 22, + [0][1][RTW89_QATAR][8] = 22, + [0][1][RTW89_UK][8] = 22, + [0][1][RTW89_FCC][9] = 54, + [0][1][RTW89_ETSI][9] = 22, + [0][1][RTW89_MKK][9] = 32, + [0][1][RTW89_IC][9] = 54, + [0][1][RTW89_KCC][9] = 30, + [0][1][RTW89_ACMA][9] = 22, + [0][1][RTW89_CHILE][9] = 50, + [0][1][RTW89_UKRAINE][9] = 22, + [0][1][RTW89_MEXICO][9] = 54, + [0][1][RTW89_CN][9] = 22, + [0][1][RTW89_QATAR][9] = 22, + [0][1][RTW89_UK][9] = 22, + [0][1][RTW89_FCC][10] = 54, + [0][1][RTW89_ETSI][10] = 22, + [0][1][RTW89_MKK][10] = 32, + [0][1][RTW89_IC][10] = 54, + [0][1][RTW89_KCC][10] = 30, + [0][1][RTW89_ACMA][10] = 22, + [0][1][RTW89_CHILE][10] = 50, + [0][1][RTW89_UKRAINE][10] = 22, + [0][1][RTW89_MEXICO][10] = 54, + [0][1][RTW89_CN][10] = 22, + [0][1][RTW89_QATAR][10] = 22, + [0][1][RTW89_UK][10] = 22, + [0][1][RTW89_FCC][11] = 38, + [0][1][RTW89_ETSI][11] = 22, + [0][1][RTW89_MKK][11] = 32, + [0][1][RTW89_IC][11] = 38, + [0][1][RTW89_KCC][11] = 30, + [0][1][RTW89_ACMA][11] = 22, + [0][1][RTW89_CHILE][11] = 50, + [0][1][RTW89_UKRAINE][11] = 22, + [0][1][RTW89_MEXICO][11] = 38, + [0][1][RTW89_CN][11] = 22, + [0][1][RTW89_QATAR][11] = 22, + [0][1][RTW89_UK][11] = 22, + [0][1][RTW89_FCC][12] = 30, + [0][1][RTW89_ETSI][12] = 20, + [0][1][RTW89_MKK][12] = 30, + [0][1][RTW89_IC][12] = 30, + [0][1][RTW89_KCC][12] = 30, + [0][1][RTW89_ACMA][12] = 20, + [0][1][RTW89_CHILE][12] = 50, + [0][1][RTW89_UKRAINE][12] = 20, + [0][1][RTW89_MEXICO][12] = 30, + [0][1][RTW89_CN][12] = 20, + [0][1][RTW89_QATAR][12] = 20, + [0][1][RTW89_UK][12] = 20, + [0][1][RTW89_FCC][13] = 127, + [0][1][RTW89_ETSI][13] = 127, + [0][1][RTW89_MKK][13] = 127, + [0][1][RTW89_IC][13] = 127, + [0][1][RTW89_KCC][13] = 127, + [0][1][RTW89_ACMA][13] = 127, + [0][1][RTW89_CHILE][13] = 127, + [0][1][RTW89_UKRAINE][13] = 127, + [0][1][RTW89_MEXICO][13] = 127, + [0][1][RTW89_CN][13] = 127, + [0][1][RTW89_QATAR][13] = 127, + [0][1][RTW89_UK][13] = 127, + [1][0][RTW89_FCC][0] = 72, + [1][0][RTW89_ETSI][0] = 42, + [1][0][RTW89_MKK][0] = 52, + [1][0][RTW89_IC][0] = 72, + [1][0][RTW89_KCC][0] = 52, + [1][0][RTW89_ACMA][0] = 42, + [1][0][RTW89_CHILE][0] = 68, + [1][0][RTW89_UKRAINE][0] = 42, + [1][0][RTW89_MEXICO][0] = 72, + [1][0][RTW89_CN][0] = 42, + [1][0][RTW89_QATAR][0] = 42, + [1][0][RTW89_UK][0] = 42, + [1][0][RTW89_FCC][1] = 72, + [1][0][RTW89_ETSI][1] = 44, + [1][0][RTW89_MKK][1] = 52, + [1][0][RTW89_IC][1] = 72, + [1][0][RTW89_KCC][1] = 52, + [1][0][RTW89_ACMA][1] = 44, + [1][0][RTW89_CHILE][1] = 68, + [1][0][RTW89_UKRAINE][1] = 44, + [1][0][RTW89_MEXICO][1] = 72, + [1][0][RTW89_CN][1] = 44, + [1][0][RTW89_QATAR][1] = 44, + [1][0][RTW89_UK][1] = 44, + [1][0][RTW89_FCC][2] = 76, + [1][0][RTW89_ETSI][2] = 44, + [1][0][RTW89_MKK][2] = 52, + [1][0][RTW89_IC][2] = 76, + [1][0][RTW89_KCC][2] = 52, + [1][0][RTW89_ACMA][2] = 44, + [1][0][RTW89_CHILE][2] = 68, + [1][0][RTW89_UKRAINE][2] = 44, + [1][0][RTW89_MEXICO][2] = 76, + [1][0][RTW89_CN][2] = 44, + [1][0][RTW89_QATAR][2] = 44, + [1][0][RTW89_UK][2] = 44, + [1][0][RTW89_FCC][3] = 78, + [1][0][RTW89_ETSI][3] = 44, + [1][0][RTW89_MKK][3] = 52, + [1][0][RTW89_IC][3] = 78, + [1][0][RTW89_KCC][3] = 52, + [1][0][RTW89_ACMA][3] = 44, + [1][0][RTW89_CHILE][3] = 68, + [1][0][RTW89_UKRAINE][3] = 44, + [1][0][RTW89_MEXICO][3] = 78, + [1][0][RTW89_CN][3] = 44, + [1][0][RTW89_QATAR][3] = 44, + [1][0][RTW89_UK][3] = 44, + [1][0][RTW89_FCC][4] = 78, + [1][0][RTW89_ETSI][4] = 44, + [1][0][RTW89_MKK][4] = 52, + [1][0][RTW89_IC][4] = 78, + [1][0][RTW89_KCC][4] = 52, + [1][0][RTW89_ACMA][4] = 44, + [1][0][RTW89_CHILE][4] = 68, + [1][0][RTW89_UKRAINE][4] = 44, + [1][0][RTW89_MEXICO][4] = 78, + [1][0][RTW89_CN][4] = 44, + [1][0][RTW89_QATAR][4] = 44, + [1][0][RTW89_UK][4] = 44, + [1][0][RTW89_FCC][5] = 84, + [1][0][RTW89_ETSI][5] = 44, + [1][0][RTW89_MKK][5] = 52, + [1][0][RTW89_IC][5] = 84, + [1][0][RTW89_KCC][5] = 52, + [1][0][RTW89_ACMA][5] = 44, + [1][0][RTW89_CHILE][5] = 68, + [1][0][RTW89_UKRAINE][5] = 44, + [1][0][RTW89_MEXICO][5] = 84, + [1][0][RTW89_CN][5] = 44, + [1][0][RTW89_QATAR][5] = 44, + [1][0][RTW89_UK][5] = 44, + [1][0][RTW89_FCC][6] = 72, + [1][0][RTW89_ETSI][6] = 44, + [1][0][RTW89_MKK][6] = 52, + [1][0][RTW89_IC][6] = 72, + [1][0][RTW89_KCC][6] = 52, + [1][0][RTW89_ACMA][6] = 44, + [1][0][RTW89_CHILE][6] = 68, + [1][0][RTW89_UKRAINE][6] = 44, + [1][0][RTW89_MEXICO][6] = 72, + [1][0][RTW89_CN][6] = 44, + [1][0][RTW89_QATAR][6] = 44, + [1][0][RTW89_UK][6] = 44, + [1][0][RTW89_FCC][7] = 72, + [1][0][RTW89_ETSI][7] = 44, + [1][0][RTW89_MKK][7] = 52, + [1][0][RTW89_IC][7] = 72, + [1][0][RTW89_KCC][7] = 52, + [1][0][RTW89_ACMA][7] = 44, + [1][0][RTW89_CHILE][7] = 68, + [1][0][RTW89_UKRAINE][7] = 44, + [1][0][RTW89_MEXICO][7] = 72, + [1][0][RTW89_CN][7] = 44, + [1][0][RTW89_QATAR][7] = 44, + [1][0][RTW89_UK][7] = 44, + [1][0][RTW89_FCC][8] = 72, + [1][0][RTW89_ETSI][8] = 44, + [1][0][RTW89_MKK][8] = 52, + [1][0][RTW89_IC][8] = 72, + [1][0][RTW89_KCC][8] = 52, + [1][0][RTW89_ACMA][8] = 44, + [1][0][RTW89_CHILE][8] = 68, + [1][0][RTW89_UKRAINE][8] = 44, + [1][0][RTW89_MEXICO][8] = 72, + [1][0][RTW89_CN][8] = 44, + [1][0][RTW89_QATAR][8] = 44, + [1][0][RTW89_UK][8] = 44, + [1][0][RTW89_FCC][9] = 68, + [1][0][RTW89_ETSI][9] = 44, + [1][0][RTW89_MKK][9] = 52, + [1][0][RTW89_IC][9] = 68, + [1][0][RTW89_KCC][9] = 52, + [1][0][RTW89_ACMA][9] = 44, + [1][0][RTW89_CHILE][9] = 68, + [1][0][RTW89_UKRAINE][9] = 44, + [1][0][RTW89_MEXICO][9] = 68, + [1][0][RTW89_CN][9] = 44, + [1][0][RTW89_QATAR][9] = 44, + [1][0][RTW89_UK][9] = 44, + [1][0][RTW89_FCC][10] = 68, + [1][0][RTW89_ETSI][10] = 44, + [1][0][RTW89_MKK][10] = 52, + [1][0][RTW89_IC][10] = 68, + [1][0][RTW89_KCC][10] = 52, + [1][0][RTW89_ACMA][10] = 44, + [1][0][RTW89_CHILE][10] = 70, + [1][0][RTW89_UKRAINE][10] = 44, + [1][0][RTW89_MEXICO][10] = 68, + [1][0][RTW89_CN][10] = 44, + [1][0][RTW89_QATAR][10] = 44, + [1][0][RTW89_UK][10] = 44, + [1][0][RTW89_FCC][11] = 50, + [1][0][RTW89_ETSI][11] = 44, + [1][0][RTW89_MKK][11] = 52, + [1][0][RTW89_IC][11] = 50, + [1][0][RTW89_KCC][11] = 52, + [1][0][RTW89_ACMA][11] = 44, + [1][0][RTW89_CHILE][11] = 68, + [1][0][RTW89_UKRAINE][11] = 44, + [1][0][RTW89_MEXICO][11] = 50, + [1][0][RTW89_CN][11] = 44, + [1][0][RTW89_QATAR][11] = 44, + [1][0][RTW89_UK][11] = 44, + [1][0][RTW89_FCC][12] = 38, + [1][0][RTW89_ETSI][12] = 42, + [1][0][RTW89_MKK][12] = 52, + [1][0][RTW89_IC][12] = 38, + [1][0][RTW89_KCC][12] = 52, + [1][0][RTW89_ACMA][12] = 42, + [1][0][RTW89_CHILE][12] = 68, + [1][0][RTW89_UKRAINE][12] = 42, + [1][0][RTW89_MEXICO][12] = 38, + [1][0][RTW89_CN][12] = 42, + [1][0][RTW89_QATAR][12] = 42, + [1][0][RTW89_UK][12] = 42, + [1][0][RTW89_FCC][13] = 127, + [1][0][RTW89_ETSI][13] = 127, + [1][0][RTW89_MKK][13] = 127, + [1][0][RTW89_IC][13] = 127, + [1][0][RTW89_KCC][13] = 127, + [1][0][RTW89_ACMA][13] = 127, + [1][0][RTW89_CHILE][13] = 127, + [1][0][RTW89_UKRAINE][13] = 127, + [1][0][RTW89_MEXICO][13] = 127, + [1][0][RTW89_CN][13] = 127, + [1][0][RTW89_QATAR][13] = 127, + [1][0][RTW89_UK][13] = 127, + [1][1][RTW89_FCC][0] = 54, + [1][1][RTW89_ETSI][0] = 32, + [1][1][RTW89_MKK][0] = 40, + [1][1][RTW89_IC][0] = 54, + [1][1][RTW89_KCC][0] = 40, + [1][1][RTW89_ACMA][0] = 32, + [1][1][RTW89_CHILE][0] = 54, + [1][1][RTW89_UKRAINE][0] = 32, + [1][1][RTW89_MEXICO][0] = 54, + [1][1][RTW89_CN][0] = 32, + [1][1][RTW89_QATAR][0] = 32, + [1][1][RTW89_UK][0] = 32, + [1][1][RTW89_FCC][1] = 54, + [1][1][RTW89_ETSI][1] = 32, + [1][1][RTW89_MKK][1] = 40, + [1][1][RTW89_IC][1] = 54, + [1][1][RTW89_KCC][1] = 40, + [1][1][RTW89_ACMA][1] = 32, + [1][1][RTW89_CHILE][1] = 54, + [1][1][RTW89_UKRAINE][1] = 32, + [1][1][RTW89_MEXICO][1] = 54, + [1][1][RTW89_CN][1] = 32, + [1][1][RTW89_QATAR][1] = 32, + [1][1][RTW89_UK][1] = 32, + [1][1][RTW89_FCC][2] = 58, + [1][1][RTW89_ETSI][2] = 32, + [1][1][RTW89_MKK][2] = 40, + [1][1][RTW89_IC][2] = 58, + [1][1][RTW89_KCC][2] = 40, + [1][1][RTW89_ACMA][2] = 32, + [1][1][RTW89_CHILE][2] = 54, + [1][1][RTW89_UKRAINE][2] = 32, + [1][1][RTW89_MEXICO][2] = 58, + [1][1][RTW89_CN][2] = 32, + [1][1][RTW89_QATAR][2] = 32, + [1][1][RTW89_UK][2] = 32, + [1][1][RTW89_FCC][3] = 62, + [1][1][RTW89_ETSI][3] = 32, + [1][1][RTW89_MKK][3] = 40, + [1][1][RTW89_IC][3] = 62, + [1][1][RTW89_KCC][3] = 40, + [1][1][RTW89_ACMA][3] = 32, + [1][1][RTW89_CHILE][3] = 54, + [1][1][RTW89_UKRAINE][3] = 32, + [1][1][RTW89_MEXICO][3] = 62, + [1][1][RTW89_CN][3] = 32, + [1][1][RTW89_QATAR][3] = 32, + [1][1][RTW89_UK][3] = 32, + [1][1][RTW89_FCC][4] = 66, + [1][1][RTW89_ETSI][4] = 32, + [1][1][RTW89_MKK][4] = 40, + [1][1][RTW89_IC][4] = 66, + [1][1][RTW89_KCC][4] = 40, + [1][1][RTW89_ACMA][4] = 32, + [1][1][RTW89_CHILE][4] = 54, + [1][1][RTW89_UKRAINE][4] = 32, + [1][1][RTW89_MEXICO][4] = 66, + [1][1][RTW89_CN][4] = 32, + [1][1][RTW89_QATAR][4] = 32, + [1][1][RTW89_UK][4] = 32, + [1][1][RTW89_FCC][5] = 74, + [1][1][RTW89_ETSI][5] = 32, + [1][1][RTW89_MKK][5] = 40, + [1][1][RTW89_IC][5] = 74, + [1][1][RTW89_KCC][5] = 40, + [1][1][RTW89_ACMA][5] = 32, + [1][1][RTW89_CHILE][5] = 54, + [1][1][RTW89_UKRAINE][5] = 32, + [1][1][RTW89_MEXICO][5] = 74, + [1][1][RTW89_CN][5] = 32, + [1][1][RTW89_QATAR][5] = 32, + [1][1][RTW89_UK][5] = 32, + [1][1][RTW89_FCC][6] = 66, + [1][1][RTW89_ETSI][6] = 32, + [1][1][RTW89_MKK][6] = 40, + [1][1][RTW89_IC][6] = 66, + [1][1][RTW89_KCC][6] = 40, + [1][1][RTW89_ACMA][6] = 32, + [1][1][RTW89_CHILE][6] = 54, + [1][1][RTW89_UKRAINE][6] = 32, + [1][1][RTW89_MEXICO][6] = 66, + [1][1][RTW89_CN][6] = 32, + [1][1][RTW89_QATAR][6] = 32, + [1][1][RTW89_UK][6] = 32, + [1][1][RTW89_FCC][7] = 62, + [1][1][RTW89_ETSI][7] = 32, + [1][1][RTW89_MKK][7] = 40, + [1][1][RTW89_IC][7] = 62, + [1][1][RTW89_KCC][7] = 40, + [1][1][RTW89_ACMA][7] = 32, + [1][1][RTW89_CHILE][7] = 54, + [1][1][RTW89_UKRAINE][7] = 32, + [1][1][RTW89_MEXICO][7] = 62, + [1][1][RTW89_CN][7] = 32, + [1][1][RTW89_QATAR][7] = 32, + [1][1][RTW89_UK][7] = 32, + [1][1][RTW89_FCC][8] = 58, + [1][1][RTW89_ETSI][8] = 32, + [1][1][RTW89_MKK][8] = 40, + [1][1][RTW89_IC][8] = 58, + [1][1][RTW89_KCC][8] = 40, + [1][1][RTW89_ACMA][8] = 32, + [1][1][RTW89_CHILE][8] = 54, + [1][1][RTW89_UKRAINE][8] = 32, + [1][1][RTW89_MEXICO][8] = 58, + [1][1][RTW89_CN][8] = 32, + [1][1][RTW89_QATAR][8] = 32, + [1][1][RTW89_UK][8] = 32, + [1][1][RTW89_FCC][9] = 54, + [1][1][RTW89_ETSI][9] = 32, + [1][1][RTW89_MKK][9] = 40, + [1][1][RTW89_IC][9] = 54, + [1][1][RTW89_KCC][9] = 40, + [1][1][RTW89_ACMA][9] = 32, + [1][1][RTW89_CHILE][9] = 54, + [1][1][RTW89_UKRAINE][9] = 32, + [1][1][RTW89_MEXICO][9] = 54, + [1][1][RTW89_CN][9] = 32, + [1][1][RTW89_QATAR][9] = 32, + [1][1][RTW89_UK][9] = 32, + [1][1][RTW89_FCC][10] = 54, + [1][1][RTW89_ETSI][10] = 32, + [1][1][RTW89_MKK][10] = 40, + [1][1][RTW89_IC][10] = 54, + [1][1][RTW89_KCC][10] = 40, + [1][1][RTW89_ACMA][10] = 32, + [1][1][RTW89_CHILE][10] = 54, + [1][1][RTW89_UKRAINE][10] = 32, + [1][1][RTW89_MEXICO][10] = 54, + [1][1][RTW89_CN][10] = 32, + [1][1][RTW89_QATAR][10] = 32, + [1][1][RTW89_UK][10] = 32, + [1][1][RTW89_FCC][11] = 38, + [1][1][RTW89_ETSI][11] = 32, + [1][1][RTW89_MKK][11] = 40, + [1][1][RTW89_IC][11] = 38, + [1][1][RTW89_KCC][11] = 40, + [1][1][RTW89_ACMA][11] = 32, + [1][1][RTW89_CHILE][11] = 54, + [1][1][RTW89_UKRAINE][11] = 32, + [1][1][RTW89_MEXICO][11] = 38, + [1][1][RTW89_CN][11] = 32, + [1][1][RTW89_QATAR][11] = 32, + [1][1][RTW89_UK][11] = 32, + [1][1][RTW89_FCC][12] = 32, + [1][1][RTW89_ETSI][12] = 32, + [1][1][RTW89_MKK][12] = 40, + [1][1][RTW89_IC][12] = 32, + [1][1][RTW89_KCC][12] = 40, + [1][1][RTW89_ACMA][12] = 32, + [1][1][RTW89_CHILE][12] = 54, + [1][1][RTW89_UKRAINE][12] = 32, + [1][1][RTW89_MEXICO][12] = 32, + [1][1][RTW89_CN][12] = 32, + [1][1][RTW89_QATAR][12] = 32, + [1][1][RTW89_UK][12] = 32, + [1][1][RTW89_FCC][13] = 127, + [1][1][RTW89_ETSI][13] = 127, + [1][1][RTW89_MKK][13] = 127, + [1][1][RTW89_IC][13] = 127, + [1][1][RTW89_KCC][13] = 127, + [1][1][RTW89_ACMA][13] = 127, + [1][1][RTW89_CHILE][13] = 127, + [1][1][RTW89_UKRAINE][13] = 127, + [1][1][RTW89_MEXICO][13] = 127, + [1][1][RTW89_CN][13] = 127, + [1][1][RTW89_QATAR][13] = 127, + [1][1][RTW89_UK][13] = 127, + [2][0][RTW89_FCC][0] = 72, + [2][0][RTW89_ETSI][0] = 56, + [2][0][RTW89_MKK][0] = 64, + [2][0][RTW89_IC][0] = 72, + [2][0][RTW89_KCC][0] = 66, + [2][0][RTW89_ACMA][0] = 56, + [2][0][RTW89_CHILE][0] = 68, + [2][0][RTW89_UKRAINE][0] = 56, + [2][0][RTW89_MEXICO][0] = 72, + [2][0][RTW89_CN][0] = 56, + [2][0][RTW89_QATAR][0] = 56, + [2][0][RTW89_UK][0] = 56, + [2][0][RTW89_FCC][1] = 72, + [2][0][RTW89_ETSI][1] = 56, + [2][0][RTW89_MKK][1] = 64, + [2][0][RTW89_IC][1] = 72, + [2][0][RTW89_KCC][1] = 66, + [2][0][RTW89_ACMA][1] = 56, + [2][0][RTW89_CHILE][1] = 68, + [2][0][RTW89_UKRAINE][1] = 56, + [2][0][RTW89_MEXICO][1] = 72, + [2][0][RTW89_CN][1] = 56, + [2][0][RTW89_QATAR][1] = 56, + [2][0][RTW89_UK][1] = 56, + [2][0][RTW89_FCC][2] = 74, + [2][0][RTW89_ETSI][2] = 56, + [2][0][RTW89_MKK][2] = 64, + [2][0][RTW89_IC][2] = 74, + [2][0][RTW89_KCC][2] = 66, + [2][0][RTW89_ACMA][2] = 56, + [2][0][RTW89_CHILE][2] = 68, + [2][0][RTW89_UKRAINE][2] = 56, + [2][0][RTW89_MEXICO][2] = 74, + [2][0][RTW89_CN][2] = 56, + [2][0][RTW89_QATAR][2] = 56, + [2][0][RTW89_UK][2] = 56, + [2][0][RTW89_FCC][3] = 74, + [2][0][RTW89_ETSI][3] = 56, + [2][0][RTW89_MKK][3] = 64, + [2][0][RTW89_IC][3] = 74, + [2][0][RTW89_KCC][3] = 66, + [2][0][RTW89_ACMA][3] = 56, + [2][0][RTW89_CHILE][3] = 68, + [2][0][RTW89_UKRAINE][3] = 56, + [2][0][RTW89_MEXICO][3] = 74, + [2][0][RTW89_CN][3] = 56, + [2][0][RTW89_QATAR][3] = 56, + [2][0][RTW89_UK][3] = 56, + [2][0][RTW89_FCC][4] = 74, + [2][0][RTW89_ETSI][4] = 56, + [2][0][RTW89_MKK][4] = 64, + [2][0][RTW89_IC][4] = 74, + [2][0][RTW89_KCC][4] = 66, + [2][0][RTW89_ACMA][4] = 56, + [2][0][RTW89_CHILE][4] = 68, + [2][0][RTW89_UKRAINE][4] = 56, + [2][0][RTW89_MEXICO][4] = 74, + [2][0][RTW89_CN][4] = 56, + [2][0][RTW89_QATAR][4] = 56, + [2][0][RTW89_UK][4] = 56, + [2][0][RTW89_FCC][5] = 84, + [2][0][RTW89_ETSI][5] = 56, + [2][0][RTW89_MKK][5] = 64, + [2][0][RTW89_IC][5] = 84, + [2][0][RTW89_KCC][5] = 66, + [2][0][RTW89_ACMA][5] = 56, + [2][0][RTW89_CHILE][5] = 70, + [2][0][RTW89_UKRAINE][5] = 56, + [2][0][RTW89_MEXICO][5] = 84, + [2][0][RTW89_CN][5] = 56, + [2][0][RTW89_QATAR][5] = 56, + [2][0][RTW89_UK][5] = 56, + [2][0][RTW89_FCC][6] = 70, + [2][0][RTW89_ETSI][6] = 56, + [2][0][RTW89_MKK][6] = 64, + [2][0][RTW89_IC][6] = 70, + [2][0][RTW89_KCC][6] = 66, + [2][0][RTW89_ACMA][6] = 56, + [2][0][RTW89_CHILE][6] = 68, + [2][0][RTW89_UKRAINE][6] = 56, + [2][0][RTW89_MEXICO][6] = 70, + [2][0][RTW89_CN][6] = 56, + [2][0][RTW89_QATAR][6] = 56, + [2][0][RTW89_UK][6] = 56, + [2][0][RTW89_FCC][7] = 70, + [2][0][RTW89_ETSI][7] = 56, + [2][0][RTW89_MKK][7] = 64, + [2][0][RTW89_IC][7] = 70, + [2][0][RTW89_KCC][7] = 66, + [2][0][RTW89_ACMA][7] = 56, + [2][0][RTW89_CHILE][7] = 68, + [2][0][RTW89_UKRAINE][7] = 56, + [2][0][RTW89_MEXICO][7] = 70, + [2][0][RTW89_CN][7] = 56, + [2][0][RTW89_QATAR][7] = 56, + [2][0][RTW89_UK][7] = 56, + [2][0][RTW89_FCC][8] = 70, + [2][0][RTW89_ETSI][8] = 56, + [2][0][RTW89_MKK][8] = 64, + [2][0][RTW89_IC][8] = 70, + [2][0][RTW89_KCC][8] = 66, + [2][0][RTW89_ACMA][8] = 56, + [2][0][RTW89_CHILE][8] = 68, + [2][0][RTW89_UKRAINE][8] = 56, + [2][0][RTW89_MEXICO][8] = 70, + [2][0][RTW89_CN][8] = 56, + [2][0][RTW89_QATAR][8] = 56, + [2][0][RTW89_UK][8] = 56, + [2][0][RTW89_FCC][9] = 68, + [2][0][RTW89_ETSI][9] = 56, + [2][0][RTW89_MKK][9] = 64, + [2][0][RTW89_IC][9] = 68, + [2][0][RTW89_KCC][9] = 66, + [2][0][RTW89_ACMA][9] = 56, + [2][0][RTW89_CHILE][9] = 68, + [2][0][RTW89_UKRAINE][9] = 56, + [2][0][RTW89_MEXICO][9] = 68, + [2][0][RTW89_CN][9] = 56, + [2][0][RTW89_QATAR][9] = 56, + [2][0][RTW89_UK][9] = 56, + [2][0][RTW89_FCC][10] = 68, + [2][0][RTW89_ETSI][10] = 56, + [2][0][RTW89_MKK][10] = 64, + [2][0][RTW89_IC][10] = 68, + [2][0][RTW89_KCC][10] = 66, + [2][0][RTW89_ACMA][10] = 56, + [2][0][RTW89_CHILE][10] = 68, + [2][0][RTW89_UKRAINE][10] = 56, + [2][0][RTW89_MEXICO][10] = 68, + [2][0][RTW89_CN][10] = 56, + [2][0][RTW89_QATAR][10] = 56, + [2][0][RTW89_UK][10] = 56, + [2][0][RTW89_FCC][11] = 50, + [2][0][RTW89_ETSI][11] = 56, + [2][0][RTW89_MKK][11] = 64, + [2][0][RTW89_IC][11] = 50, + [2][0][RTW89_KCC][11] = 66, + [2][0][RTW89_ACMA][11] = 56, + [2][0][RTW89_CHILE][11] = 68, + [2][0][RTW89_UKRAINE][11] = 56, + [2][0][RTW89_MEXICO][11] = 50, + [2][0][RTW89_CN][11] = 56, + [2][0][RTW89_QATAR][11] = 56, + [2][0][RTW89_UK][11] = 56, + [2][0][RTW89_FCC][12] = 46, + [2][0][RTW89_ETSI][12] = 56, + [2][0][RTW89_MKK][12] = 64, + [2][0][RTW89_IC][12] = 46, + [2][0][RTW89_KCC][12] = 66, + [2][0][RTW89_ACMA][12] = 56, + [2][0][RTW89_CHILE][12] = 68, + [2][0][RTW89_UKRAINE][12] = 56, + [2][0][RTW89_MEXICO][12] = 46, + [2][0][RTW89_CN][12] = 56, + [2][0][RTW89_QATAR][12] = 56, + [2][0][RTW89_UK][12] = 56, + [2][0][RTW89_FCC][13] = 127, + [2][0][RTW89_ETSI][13] = 127, + [2][0][RTW89_MKK][13] = 127, + [2][0][RTW89_IC][13] = 127, + [2][0][RTW89_KCC][13] = 127, + [2][0][RTW89_ACMA][13] = 127, + [2][0][RTW89_CHILE][13] = 127, + [2][0][RTW89_UKRAINE][13] = 127, + [2][0][RTW89_MEXICO][13] = 127, + [2][0][RTW89_CN][13] = 127, + [2][0][RTW89_QATAR][13] = 127, + [2][0][RTW89_UK][13] = 127, + [2][1][RTW89_FCC][0] = 54, + [2][1][RTW89_ETSI][0] = 44, + [2][1][RTW89_MKK][0] = 52, + [2][1][RTW89_IC][0] = 54, + [2][1][RTW89_KCC][0] = 54, + [2][1][RTW89_ACMA][0] = 44, + [2][1][RTW89_CHILE][0] = 58, + [2][1][RTW89_UKRAINE][0] = 44, + [2][1][RTW89_MEXICO][0] = 54, + [2][1][RTW89_CN][0] = 44, + [2][1][RTW89_QATAR][0] = 44, + [2][1][RTW89_UK][0] = 44, + [2][1][RTW89_FCC][1] = 54, + [2][1][RTW89_ETSI][1] = 44, + [2][1][RTW89_MKK][1] = 52, + [2][1][RTW89_IC][1] = 54, + [2][1][RTW89_KCC][1] = 54, + [2][1][RTW89_ACMA][1] = 44, + [2][1][RTW89_CHILE][1] = 56, + [2][1][RTW89_UKRAINE][1] = 44, + [2][1][RTW89_MEXICO][1] = 54, + [2][1][RTW89_CN][1] = 44, + [2][1][RTW89_QATAR][1] = 44, + [2][1][RTW89_UK][1] = 44, + [2][1][RTW89_FCC][2] = 58, + [2][1][RTW89_ETSI][2] = 44, + [2][1][RTW89_MKK][2] = 52, + [2][1][RTW89_IC][2] = 58, + [2][1][RTW89_KCC][2] = 54, + [2][1][RTW89_ACMA][2] = 44, + [2][1][RTW89_CHILE][2] = 56, + [2][1][RTW89_UKRAINE][2] = 44, + [2][1][RTW89_MEXICO][2] = 58, + [2][1][RTW89_CN][2] = 44, + [2][1][RTW89_QATAR][2] = 44, + [2][1][RTW89_UK][2] = 44, + [2][1][RTW89_FCC][3] = 62, + [2][1][RTW89_ETSI][3] = 44, + [2][1][RTW89_MKK][3] = 52, + [2][1][RTW89_IC][3] = 62, + [2][1][RTW89_KCC][3] = 54, + [2][1][RTW89_ACMA][3] = 44, + [2][1][RTW89_CHILE][3] = 56, + [2][1][RTW89_UKRAINE][3] = 44, + [2][1][RTW89_MEXICO][3] = 62, + [2][1][RTW89_CN][3] = 44, + [2][1][RTW89_QATAR][3] = 44, + [2][1][RTW89_UK][3] = 44, + [2][1][RTW89_FCC][4] = 64, + [2][1][RTW89_ETSI][4] = 44, + [2][1][RTW89_MKK][4] = 52, + [2][1][RTW89_IC][4] = 64, + [2][1][RTW89_KCC][4] = 52, + [2][1][RTW89_ACMA][4] = 44, + [2][1][RTW89_CHILE][4] = 56, + [2][1][RTW89_UKRAINE][4] = 44, + [2][1][RTW89_MEXICO][4] = 64, + [2][1][RTW89_CN][4] = 44, + [2][1][RTW89_QATAR][4] = 44, + [2][1][RTW89_UK][4] = 44, + [2][1][RTW89_FCC][5] = 80, + [2][1][RTW89_ETSI][5] = 44, + [2][1][RTW89_MKK][5] = 52, + [2][1][RTW89_IC][5] = 80, + [2][1][RTW89_KCC][5] = 52, + [2][1][RTW89_ACMA][5] = 44, + [2][1][RTW89_CHILE][5] = 56, + [2][1][RTW89_UKRAINE][5] = 44, + [2][1][RTW89_MEXICO][5] = 80, + [2][1][RTW89_CN][5] = 44, + [2][1][RTW89_QATAR][5] = 44, + [2][1][RTW89_UK][5] = 44, + [2][1][RTW89_FCC][6] = 62, + [2][1][RTW89_ETSI][6] = 44, + [2][1][RTW89_MKK][6] = 52, + [2][1][RTW89_IC][6] = 62, + [2][1][RTW89_KCC][6] = 52, + [2][1][RTW89_ACMA][6] = 44, + [2][1][RTW89_CHILE][6] = 56, + [2][1][RTW89_UKRAINE][6] = 44, + [2][1][RTW89_MEXICO][6] = 62, + [2][1][RTW89_CN][6] = 44, + [2][1][RTW89_QATAR][6] = 44, + [2][1][RTW89_UK][6] = 44, + [2][1][RTW89_FCC][7] = 62, + [2][1][RTW89_ETSI][7] = 44, + [2][1][RTW89_MKK][7] = 52, + [2][1][RTW89_IC][7] = 62, + [2][1][RTW89_KCC][7] = 52, + [2][1][RTW89_ACMA][7] = 44, + [2][1][RTW89_CHILE][7] = 56, + [2][1][RTW89_UKRAINE][7] = 44, + [2][1][RTW89_MEXICO][7] = 62, + [2][1][RTW89_CN][7] = 44, + [2][1][RTW89_QATAR][7] = 44, + [2][1][RTW89_UK][7] = 44, + [2][1][RTW89_FCC][8] = 58, + [2][1][RTW89_ETSI][8] = 44, + [2][1][RTW89_MKK][8] = 52, + [2][1][RTW89_IC][8] = 58, + [2][1][RTW89_KCC][8] = 52, + [2][1][RTW89_ACMA][8] = 44, + [2][1][RTW89_CHILE][8] = 56, + [2][1][RTW89_UKRAINE][8] = 44, + [2][1][RTW89_MEXICO][8] = 58, + [2][1][RTW89_CN][8] = 44, + [2][1][RTW89_QATAR][8] = 44, + [2][1][RTW89_UK][8] = 44, + [2][1][RTW89_FCC][9] = 54, + [2][1][RTW89_ETSI][9] = 44, + [2][1][RTW89_MKK][9] = 52, + [2][1][RTW89_IC][9] = 54, + [2][1][RTW89_KCC][9] = 54, + [2][1][RTW89_ACMA][9] = 44, + [2][1][RTW89_CHILE][9] = 56, + [2][1][RTW89_UKRAINE][9] = 44, + [2][1][RTW89_MEXICO][9] = 54, + [2][1][RTW89_CN][9] = 44, + [2][1][RTW89_QATAR][9] = 44, + [2][1][RTW89_UK][9] = 44, + [2][1][RTW89_FCC][10] = 54, + [2][1][RTW89_ETSI][10] = 44, + [2][1][RTW89_MKK][10] = 52, + [2][1][RTW89_IC][10] = 54, + [2][1][RTW89_KCC][10] = 54, + [2][1][RTW89_ACMA][10] = 44, + [2][1][RTW89_CHILE][10] = 56, + [2][1][RTW89_UKRAINE][10] = 44, + [2][1][RTW89_MEXICO][10] = 54, + [2][1][RTW89_CN][10] = 44, + [2][1][RTW89_QATAR][10] = 44, + [2][1][RTW89_UK][10] = 44, + [2][1][RTW89_FCC][11] = 38, + [2][1][RTW89_ETSI][11] = 44, + [2][1][RTW89_MKK][11] = 52, + [2][1][RTW89_IC][11] = 38, + [2][1][RTW89_KCC][11] = 54, + [2][1][RTW89_ACMA][11] = 44, + [2][1][RTW89_CHILE][11] = 56, + [2][1][RTW89_UKRAINE][11] = 44, + [2][1][RTW89_MEXICO][11] = 38, + [2][1][RTW89_CN][11] = 44, + [2][1][RTW89_QATAR][11] = 44, + [2][1][RTW89_UK][11] = 44, + [2][1][RTW89_FCC][12] = 34, + [2][1][RTW89_ETSI][12] = 42, + [2][1][RTW89_MKK][12] = 52, + [2][1][RTW89_IC][12] = 34, + [2][1][RTW89_KCC][12] = 54, + [2][1][RTW89_ACMA][12] = 42, + [2][1][RTW89_CHILE][12] = 56, + [2][1][RTW89_UKRAINE][12] = 42, + [2][1][RTW89_MEXICO][12] = 34, + [2][1][RTW89_CN][12] = 42, + [2][1][RTW89_QATAR][12] = 42, + [2][1][RTW89_UK][12] = 42, + [2][1][RTW89_FCC][13] = 127, + [2][1][RTW89_ETSI][13] = 127, + [2][1][RTW89_MKK][13] = 127, + [2][1][RTW89_IC][13] = 127, + [2][1][RTW89_KCC][13] = 127, + [2][1][RTW89_ACMA][13] = 127, + [2][1][RTW89_CHILE][13] = 127, + [2][1][RTW89_UKRAINE][13] = 127, + [2][1][RTW89_MEXICO][13] = 127, + [2][1][RTW89_CN][13] = 127, + [2][1][RTW89_QATAR][13] = 127, + [2][1][RTW89_UK][13] = 127, +}; + +const s8 rtw89_8852b_txpwr_lmt_ru_5g[RTW89_RU_NUM][RTW89_NTX_NUM] + [RTW89_REGD_NUM][RTW89_5G_CH_NUM] = { + [0][0][RTW89_WW][0] = 24, + [0][0][RTW89_WW][2] = 24, + [0][0][RTW89_WW][4] = 24, + [0][0][RTW89_WW][6] = 12, + [0][0][RTW89_WW][8] = 24, + [0][0][RTW89_WW][10] = 24, + [0][0][RTW89_WW][12] = 24, + [0][0][RTW89_WW][14] = 24, + [0][0][RTW89_WW][15] = 24, + [0][0][RTW89_WW][17] = 24, + [0][0][RTW89_WW][19] = 24, + [0][0][RTW89_WW][21] = 24, + [0][0][RTW89_WW][23] = 24, + [0][0][RTW89_WW][25] = 24, + [0][0][RTW89_WW][27] = 24, + [0][0][RTW89_WW][29] = 24, + [0][0][RTW89_WW][31] = 24, + [0][0][RTW89_WW][33] = 24, + [0][0][RTW89_WW][35] = 24, + [0][0][RTW89_WW][37] = 44, + [0][0][RTW89_WW][38] = 26, + [0][0][RTW89_WW][40] = 26, + [0][0][RTW89_WW][42] = 26, + [0][0][RTW89_WW][44] = 26, + [0][0][RTW89_WW][46] = 26, + [0][0][RTW89_WW][48] = 32, + [0][0][RTW89_WW][50] = 32, + [0][0][RTW89_WW][52] = 32, + [0][1][RTW89_WW][0] = 0, + [0][1][RTW89_WW][2] = 4, + [0][1][RTW89_WW][4] = 0, + [0][1][RTW89_WW][6] = 0, + [0][1][RTW89_WW][8] = 12, + [0][1][RTW89_WW][10] = 12, + [0][1][RTW89_WW][12] = 12, + [0][1][RTW89_WW][14] = 12, + [0][1][RTW89_WW][15] = 12, + [0][1][RTW89_WW][17] = 12, + [0][1][RTW89_WW][19] = 12, + [0][1][RTW89_WW][21] = 12, + [0][1][RTW89_WW][23] = 12, + [0][1][RTW89_WW][25] = 12, + [0][1][RTW89_WW][27] = 12, + [0][1][RTW89_WW][29] = 12, + [0][1][RTW89_WW][31] = 12, + [0][1][RTW89_WW][33] = 12, + [0][1][RTW89_WW][35] = 12, + [0][1][RTW89_WW][37] = 30, + [0][1][RTW89_WW][38] = 14, + [0][1][RTW89_WW][40] = 14, + [0][1][RTW89_WW][42] = 14, + [0][1][RTW89_WW][44] = 14, + [0][1][RTW89_WW][46] = 14, + [0][1][RTW89_WW][48] = 20, + [0][1][RTW89_WW][50] = 20, + [0][1][RTW89_WW][52] = 20, + [1][0][RTW89_WW][0] = 34, + [1][0][RTW89_WW][2] = 34, + [1][0][RTW89_WW][4] = 34, + [1][0][RTW89_WW][6] = 26, + [1][0][RTW89_WW][8] = 34, + [1][0][RTW89_WW][10] = 34, + [1][0][RTW89_WW][12] = 34, + [1][0][RTW89_WW][14] = 34, + [1][0][RTW89_WW][15] = 34, + [1][0][RTW89_WW][17] = 34, + [1][0][RTW89_WW][19] = 34, + [1][0][RTW89_WW][21] = 34, + [1][0][RTW89_WW][23] = 34, + [1][0][RTW89_WW][25] = 34, + [1][0][RTW89_WW][27] = 34, + [1][0][RTW89_WW][29] = 34, + [1][0][RTW89_WW][31] = 34, + [1][0][RTW89_WW][33] = 34, + [1][0][RTW89_WW][35] = 34, + [1][0][RTW89_WW][37] = 52, + [1][0][RTW89_WW][38] = 28, + [1][0][RTW89_WW][40] = 28, + [1][0][RTW89_WW][42] = 28, + [1][0][RTW89_WW][44] = 28, + [1][0][RTW89_WW][46] = 28, + [1][0][RTW89_WW][48] = 44, + [1][0][RTW89_WW][50] = 44, + [1][0][RTW89_WW][52] = 44, + [1][1][RTW89_WW][0] = 10, + [1][1][RTW89_WW][2] = 14, + [1][1][RTW89_WW][4] = 10, + [1][1][RTW89_WW][6] = 10, + [1][1][RTW89_WW][8] = 20, + [1][1][RTW89_WW][10] = 20, + [1][1][RTW89_WW][12] = 22, + [1][1][RTW89_WW][14] = 22, + [1][1][RTW89_WW][15] = 22, + [1][1][RTW89_WW][17] = 22, + [1][1][RTW89_WW][19] = 22, + [1][1][RTW89_WW][21] = 22, + [1][1][RTW89_WW][23] = 22, + [1][1][RTW89_WW][25] = 22, + [1][1][RTW89_WW][27] = 22, + [1][1][RTW89_WW][29] = 22, + [1][1][RTW89_WW][31] = 22, + [1][1][RTW89_WW][33] = 22, + [1][1][RTW89_WW][35] = 22, + [1][1][RTW89_WW][37] = 38, + [1][1][RTW89_WW][38] = 16, + [1][1][RTW89_WW][40] = 16, + [1][1][RTW89_WW][42] = 16, + [1][1][RTW89_WW][44] = 16, + [1][1][RTW89_WW][46] = 16, + [1][1][RTW89_WW][48] = 32, + [1][1][RTW89_WW][50] = 32, + [1][1][RTW89_WW][52] = 32, + [2][0][RTW89_WW][0] = 44, + [2][0][RTW89_WW][2] = 44, + [2][0][RTW89_WW][4] = 44, + [2][0][RTW89_WW][6] = 38, + [2][0][RTW89_WW][8] = 48, + [2][0][RTW89_WW][10] = 48, + [2][0][RTW89_WW][12] = 46, + [2][0][RTW89_WW][14] = 46, + [2][0][RTW89_WW][15] = 48, + [2][0][RTW89_WW][17] = 48, + [2][0][RTW89_WW][19] = 48, + [2][0][RTW89_WW][21] = 48, + [2][0][RTW89_WW][23] = 48, + [2][0][RTW89_WW][25] = 48, + [2][0][RTW89_WW][27] = 48, + [2][0][RTW89_WW][29] = 48, + [2][0][RTW89_WW][31] = 48, + [2][0][RTW89_WW][33] = 48, + [2][0][RTW89_WW][35] = 48, + [2][0][RTW89_WW][37] = 64, + [2][0][RTW89_WW][38] = 28, + [2][0][RTW89_WW][40] = 28, + [2][0][RTW89_WW][42] = 28, + [2][0][RTW89_WW][44] = 28, + [2][0][RTW89_WW][46] = 28, + [2][0][RTW89_WW][48] = 56, + [2][0][RTW89_WW][50] = 56, + [2][0][RTW89_WW][52] = 56, + [2][1][RTW89_WW][0] = 20, + [2][1][RTW89_WW][2] = 18, + [2][1][RTW89_WW][4] = 22, + [2][1][RTW89_WW][6] = 22, + [2][1][RTW89_WW][8] = 34, + [2][1][RTW89_WW][10] = 34, + [2][1][RTW89_WW][12] = 36, + [2][1][RTW89_WW][14] = 36, + [2][1][RTW89_WW][15] = 36, + [2][1][RTW89_WW][17] = 36, + [2][1][RTW89_WW][19] = 36, + [2][1][RTW89_WW][21] = 36, + [2][1][RTW89_WW][23] = 36, + [2][1][RTW89_WW][25] = 36, + [2][1][RTW89_WW][27] = 36, + [2][1][RTW89_WW][29] = 36, + [2][1][RTW89_WW][31] = 36, + [2][1][RTW89_WW][33] = 36, + [2][1][RTW89_WW][35] = 36, + [2][1][RTW89_WW][37] = 48, + [2][1][RTW89_WW][38] = 16, + [2][1][RTW89_WW][40] = 16, + [2][1][RTW89_WW][42] = 16, + [2][1][RTW89_WW][44] = 16, + [2][1][RTW89_WW][46] = 16, + [2][1][RTW89_WW][48] = 44, + [2][1][RTW89_WW][50] = 44, + [2][1][RTW89_WW][52] = 44, + [0][0][RTW89_FCC][0] = 52, + [0][0][RTW89_ETSI][0] = 24, + [0][0][RTW89_MKK][0] = 26, + [0][0][RTW89_IC][0] = 24, + [0][0][RTW89_KCC][0] = 44, + [0][0][RTW89_ACMA][0] = 24, + [0][0][RTW89_CHILE][0] = 40, + [0][0][RTW89_UKRAINE][0] = 24, + [0][0][RTW89_MEXICO][0] = 52, + [0][0][RTW89_CN][0] = 24, + [0][0][RTW89_QATAR][0] = 24, + [0][0][RTW89_UK][0] = 24, + [0][0][RTW89_FCC][2] = 52, + [0][0][RTW89_ETSI][2] = 24, + [0][0][RTW89_MKK][2] = 26, + [0][0][RTW89_IC][2] = 24, + [0][0][RTW89_KCC][2] = 44, + [0][0][RTW89_ACMA][2] = 24, + [0][0][RTW89_CHILE][2] = 38, + [0][0][RTW89_UKRAINE][2] = 24, + [0][0][RTW89_MEXICO][2] = 52, + [0][0][RTW89_CN][2] = 24, + [0][0][RTW89_QATAR][2] = 24, + [0][0][RTW89_UK][2] = 24, + [0][0][RTW89_FCC][4] = 52, + [0][0][RTW89_ETSI][4] = 24, + [0][0][RTW89_MKK][4] = 26, + [0][0][RTW89_IC][4] = 24, + [0][0][RTW89_KCC][4] = 44, + [0][0][RTW89_ACMA][4] = 24, + [0][0][RTW89_CHILE][4] = 38, + [0][0][RTW89_UKRAINE][4] = 24, + [0][0][RTW89_MEXICO][4] = 52, + [0][0][RTW89_CN][4] = 24, + [0][0][RTW89_QATAR][4] = 24, + [0][0][RTW89_UK][4] = 24, + [0][0][RTW89_FCC][6] = 52, + [0][0][RTW89_ETSI][6] = 24, + [0][0][RTW89_MKK][6] = 26, + [0][0][RTW89_IC][6] = 24, + [0][0][RTW89_KCC][6] = 12, + [0][0][RTW89_ACMA][6] = 24, + [0][0][RTW89_CHILE][6] = 40, + [0][0][RTW89_UKRAINE][6] = 24, + [0][0][RTW89_MEXICO][6] = 52, + [0][0][RTW89_CN][6] = 24, + [0][0][RTW89_QATAR][6] = 24, + [0][0][RTW89_UK][6] = 24, + [0][0][RTW89_FCC][8] = 52, + [0][0][RTW89_ETSI][8] = 24, + [0][0][RTW89_MKK][8] = 26, + [0][0][RTW89_IC][8] = 52, + [0][0][RTW89_KCC][8] = 46, + [0][0][RTW89_ACMA][8] = 24, + [0][0][RTW89_CHILE][8] = 64, + [0][0][RTW89_UKRAINE][8] = 24, + [0][0][RTW89_MEXICO][8] = 52, + [0][0][RTW89_CN][8] = 24, + [0][0][RTW89_QATAR][8] = 24, + [0][0][RTW89_UK][8] = 24, + [0][0][RTW89_FCC][10] = 52, + [0][0][RTW89_ETSI][10] = 24, + [0][0][RTW89_MKK][10] = 26, + [0][0][RTW89_IC][10] = 52, + [0][0][RTW89_KCC][10] = 46, + [0][0][RTW89_ACMA][10] = 24, + [0][0][RTW89_CHILE][10] = 64, + [0][0][RTW89_UKRAINE][10] = 24, + [0][0][RTW89_MEXICO][10] = 52, + [0][0][RTW89_CN][10] = 24, + [0][0][RTW89_QATAR][10] = 24, + [0][0][RTW89_UK][10] = 24, + [0][0][RTW89_FCC][12] = 52, + [0][0][RTW89_ETSI][12] = 24, + [0][0][RTW89_MKK][12] = 24, + [0][0][RTW89_IC][12] = 52, + [0][0][RTW89_KCC][12] = 42, + [0][0][RTW89_ACMA][12] = 24, + [0][0][RTW89_CHILE][12] = 64, + [0][0][RTW89_UKRAINE][12] = 24, + [0][0][RTW89_MEXICO][12] = 52, + [0][0][RTW89_CN][12] = 24, + [0][0][RTW89_QATAR][12] = 24, + [0][0][RTW89_UK][12] = 24, + [0][0][RTW89_FCC][14] = 52, + [0][0][RTW89_ETSI][14] = 24, + [0][0][RTW89_MKK][14] = 24, + [0][0][RTW89_IC][14] = 52, + [0][0][RTW89_KCC][14] = 42, + [0][0][RTW89_ACMA][14] = 24, + [0][0][RTW89_CHILE][14] = 64, + [0][0][RTW89_UKRAINE][14] = 24, + [0][0][RTW89_MEXICO][14] = 52, + [0][0][RTW89_CN][14] = 24, + [0][0][RTW89_QATAR][14] = 24, + [0][0][RTW89_UK][14] = 24, + [0][0][RTW89_FCC][15] = 52, + [0][0][RTW89_ETSI][15] = 24, + [0][0][RTW89_MKK][15] = 46, + [0][0][RTW89_IC][15] = 52, + [0][0][RTW89_KCC][15] = 44, + [0][0][RTW89_ACMA][15] = 24, + [0][0][RTW89_CHILE][15] = 60, + [0][0][RTW89_UKRAINE][15] = 24, + [0][0][RTW89_MEXICO][15] = 52, + [0][0][RTW89_CN][15] = 127, + [0][0][RTW89_QATAR][15] = 24, + [0][0][RTW89_UK][15] = 24, + [0][0][RTW89_FCC][17] = 52, + [0][0][RTW89_ETSI][17] = 24, + [0][0][RTW89_MKK][17] = 48, + [0][0][RTW89_IC][17] = 52, + [0][0][RTW89_KCC][17] = 44, + [0][0][RTW89_ACMA][17] = 24, + [0][0][RTW89_CHILE][17] = 60, + [0][0][RTW89_UKRAINE][17] = 24, + [0][0][RTW89_MEXICO][17] = 52, + [0][0][RTW89_CN][17] = 127, + [0][0][RTW89_QATAR][17] = 24, + [0][0][RTW89_UK][17] = 24, + [0][0][RTW89_FCC][19] = 52, + [0][0][RTW89_ETSI][19] = 24, + [0][0][RTW89_MKK][19] = 48, + [0][0][RTW89_IC][19] = 52, + [0][0][RTW89_KCC][19] = 44, + [0][0][RTW89_ACMA][19] = 24, + [0][0][RTW89_CHILE][19] = 60, + [0][0][RTW89_UKRAINE][19] = 24, + [0][0][RTW89_MEXICO][19] = 52, + [0][0][RTW89_CN][19] = 127, + [0][0][RTW89_QATAR][19] = 24, + [0][0][RTW89_UK][19] = 24, + [0][0][RTW89_FCC][21] = 52, + [0][0][RTW89_ETSI][21] = 24, + [0][0][RTW89_MKK][21] = 48, + [0][0][RTW89_IC][21] = 52, + [0][0][RTW89_KCC][21] = 44, + [0][0][RTW89_ACMA][21] = 24, + [0][0][RTW89_CHILE][21] = 62, + [0][0][RTW89_UKRAINE][21] = 24, + [0][0][RTW89_MEXICO][21] = 52, + [0][0][RTW89_CN][21] = 127, + [0][0][RTW89_QATAR][21] = 24, + [0][0][RTW89_UK][21] = 24, + [0][0][RTW89_FCC][23] = 52, + [0][0][RTW89_ETSI][23] = 24, + [0][0][RTW89_MKK][23] = 48, + [0][0][RTW89_IC][23] = 52, + [0][0][RTW89_KCC][23] = 44, + [0][0][RTW89_ACMA][23] = 24, + [0][0][RTW89_CHILE][23] = 62, + [0][0][RTW89_UKRAINE][23] = 24, + [0][0][RTW89_MEXICO][23] = 52, + [0][0][RTW89_CN][23] = 127, + [0][0][RTW89_QATAR][23] = 24, + [0][0][RTW89_UK][23] = 24, + [0][0][RTW89_FCC][25] = 52, + [0][0][RTW89_ETSI][25] = 24, + [0][0][RTW89_MKK][25] = 48, + [0][0][RTW89_IC][25] = 127, + [0][0][RTW89_KCC][25] = 44, + [0][0][RTW89_ACMA][25] = 127, + [0][0][RTW89_CHILE][25] = 62, + [0][0][RTW89_UKRAINE][25] = 24, + [0][0][RTW89_MEXICO][25] = 52, + [0][0][RTW89_CN][25] = 127, + [0][0][RTW89_QATAR][25] = 24, + [0][0][RTW89_UK][25] = 24, + [0][0][RTW89_FCC][27] = 52, + [0][0][RTW89_ETSI][27] = 24, + [0][0][RTW89_MKK][27] = 48, + [0][0][RTW89_IC][27] = 127, + [0][0][RTW89_KCC][27] = 44, + [0][0][RTW89_ACMA][27] = 127, + [0][0][RTW89_CHILE][27] = 62, + [0][0][RTW89_UKRAINE][27] = 24, + [0][0][RTW89_MEXICO][27] = 52, + [0][0][RTW89_CN][27] = 127, + [0][0][RTW89_QATAR][27] = 24, + [0][0][RTW89_UK][27] = 24, + [0][0][RTW89_FCC][29] = 52, + [0][0][RTW89_ETSI][29] = 24, + [0][0][RTW89_MKK][29] = 48, + [0][0][RTW89_IC][29] = 127, + [0][0][RTW89_KCC][29] = 44, + [0][0][RTW89_ACMA][29] = 127, + [0][0][RTW89_CHILE][29] = 60, + [0][0][RTW89_UKRAINE][29] = 24, + [0][0][RTW89_MEXICO][29] = 52, + [0][0][RTW89_CN][29] = 127, + [0][0][RTW89_QATAR][29] = 24, + [0][0][RTW89_UK][29] = 24, + [0][0][RTW89_FCC][31] = 52, + [0][0][RTW89_ETSI][31] = 24, + [0][0][RTW89_MKK][31] = 48, + [0][0][RTW89_IC][31] = 52, + [0][0][RTW89_KCC][31] = 44, + [0][0][RTW89_ACMA][31] = 24, + [0][0][RTW89_CHILE][31] = 60, + [0][0][RTW89_UKRAINE][31] = 24, + [0][0][RTW89_MEXICO][31] = 52, + [0][0][RTW89_CN][31] = 127, + [0][0][RTW89_QATAR][31] = 24, + [0][0][RTW89_UK][31] = 24, + [0][0][RTW89_FCC][33] = 52, + [0][0][RTW89_ETSI][33] = 24, + [0][0][RTW89_MKK][33] = 48, + [0][0][RTW89_IC][33] = 52, + [0][0][RTW89_KCC][33] = 44, + [0][0][RTW89_ACMA][33] = 24, + [0][0][RTW89_CHILE][33] = 60, + [0][0][RTW89_UKRAINE][33] = 24, + [0][0][RTW89_MEXICO][33] = 52, + [0][0][RTW89_CN][33] = 127, + [0][0][RTW89_QATAR][33] = 24, + [0][0][RTW89_UK][33] = 24, + [0][0][RTW89_FCC][35] = 52, + [0][0][RTW89_ETSI][35] = 24, + [0][0][RTW89_MKK][35] = 48, + [0][0][RTW89_IC][35] = 52, + [0][0][RTW89_KCC][35] = 44, + [0][0][RTW89_ACMA][35] = 24, + [0][0][RTW89_CHILE][35] = 60, + [0][0][RTW89_UKRAINE][35] = 24, + [0][0][RTW89_MEXICO][35] = 52, + [0][0][RTW89_CN][35] = 127, + [0][0][RTW89_QATAR][35] = 24, + [0][0][RTW89_UK][35] = 24, + [0][0][RTW89_FCC][37] = 52, + [0][0][RTW89_ETSI][37] = 127, + [0][0][RTW89_MKK][37] = 44, + [0][0][RTW89_IC][37] = 52, + [0][0][RTW89_KCC][37] = 44, + [0][0][RTW89_ACMA][37] = 52, + [0][0][RTW89_CHILE][37] = 62, + [0][0][RTW89_UKRAINE][37] = 127, + [0][0][RTW89_MEXICO][37] = 52, + [0][0][RTW89_CN][37] = 127, + [0][0][RTW89_QATAR][37] = 127, + [0][0][RTW89_UK][37] = 56, + [0][0][RTW89_FCC][38] = 84, + [0][0][RTW89_ETSI][38] = 28, + [0][0][RTW89_MKK][38] = 127, + [0][0][RTW89_IC][38] = 84, + [0][0][RTW89_KCC][38] = 44, + [0][0][RTW89_ACMA][38] = 84, + [0][0][RTW89_CHILE][38] = 60, + [0][0][RTW89_UKRAINE][38] = 28, + [0][0][RTW89_MEXICO][38] = 84, + [0][0][RTW89_CN][38] = 62, + [0][0][RTW89_QATAR][38] = 28, + [0][0][RTW89_UK][38] = 26, + [0][0][RTW89_FCC][40] = 84, + [0][0][RTW89_ETSI][40] = 28, + [0][0][RTW89_MKK][40] = 127, + [0][0][RTW89_IC][40] = 84, + [0][0][RTW89_KCC][40] = 44, + [0][0][RTW89_ACMA][40] = 84, + [0][0][RTW89_CHILE][40] = 60, + [0][0][RTW89_UKRAINE][40] = 28, + [0][0][RTW89_MEXICO][40] = 84, + [0][0][RTW89_CN][40] = 62, + [0][0][RTW89_QATAR][40] = 28, + [0][0][RTW89_UK][40] = 26, + [0][0][RTW89_FCC][42] = 84, + [0][0][RTW89_ETSI][42] = 28, + [0][0][RTW89_MKK][42] = 127, + [0][0][RTW89_IC][42] = 84, + [0][0][RTW89_KCC][42] = 44, + [0][0][RTW89_ACMA][42] = 84, + [0][0][RTW89_CHILE][42] = 64, + [0][0][RTW89_UKRAINE][42] = 28, + [0][0][RTW89_MEXICO][42] = 84, + [0][0][RTW89_CN][42] = 62, + [0][0][RTW89_QATAR][42] = 28, + [0][0][RTW89_UK][42] = 26, + [0][0][RTW89_FCC][44] = 84, + [0][0][RTW89_ETSI][44] = 28, + [0][0][RTW89_MKK][44] = 127, + [0][0][RTW89_IC][44] = 84, + [0][0][RTW89_KCC][44] = 44, + [0][0][RTW89_ACMA][44] = 84, + [0][0][RTW89_CHILE][44] = 60, + [0][0][RTW89_UKRAINE][44] = 28, + [0][0][RTW89_MEXICO][44] = 84, + [0][0][RTW89_CN][44] = 62, + [0][0][RTW89_QATAR][44] = 28, + [0][0][RTW89_UK][44] = 26, + [0][0][RTW89_FCC][46] = 84, + [0][0][RTW89_ETSI][46] = 28, + [0][0][RTW89_MKK][46] = 127, + [0][0][RTW89_IC][46] = 84, + [0][0][RTW89_KCC][46] = 44, + [0][0][RTW89_ACMA][46] = 84, + [0][0][RTW89_CHILE][46] = 60, + [0][0][RTW89_UKRAINE][46] = 28, + [0][0][RTW89_MEXICO][46] = 84, + [0][0][RTW89_CN][46] = 62, + [0][0][RTW89_QATAR][46] = 28, + [0][0][RTW89_UK][46] = 26, + [0][0][RTW89_FCC][48] = 32, + [0][0][RTW89_ETSI][48] = 127, + [0][0][RTW89_MKK][48] = 127, + [0][0][RTW89_IC][48] = 127, + [0][0][RTW89_KCC][48] = 127, + [0][0][RTW89_ACMA][48] = 127, + [0][0][RTW89_CHILE][48] = 127, + [0][0][RTW89_UKRAINE][48] = 127, + [0][0][RTW89_MEXICO][48] = 127, + [0][0][RTW89_CN][48] = 127, + [0][0][RTW89_QATAR][48] = 127, + [0][0][RTW89_UK][48] = 127, + [0][0][RTW89_FCC][50] = 32, + [0][0][RTW89_ETSI][50] = 127, + [0][0][RTW89_MKK][50] = 127, + [0][0][RTW89_IC][50] = 127, + [0][0][RTW89_KCC][50] = 127, + [0][0][RTW89_ACMA][50] = 127, + [0][0][RTW89_CHILE][50] = 127, + [0][0][RTW89_UKRAINE][50] = 127, + [0][0][RTW89_MEXICO][50] = 127, + [0][0][RTW89_CN][50] = 127, + [0][0][RTW89_QATAR][50] = 127, + [0][0][RTW89_UK][50] = 127, + [0][0][RTW89_FCC][52] = 32, + [0][0][RTW89_ETSI][52] = 127, + [0][0][RTW89_MKK][52] = 127, + [0][0][RTW89_IC][52] = 127, + [0][0][RTW89_KCC][52] = 127, + [0][0][RTW89_ACMA][52] = 127, + [0][0][RTW89_CHILE][52] = 127, + [0][0][RTW89_UKRAINE][52] = 127, + [0][0][RTW89_MEXICO][52] = 127, + [0][0][RTW89_CN][52] = 127, + [0][0][RTW89_QATAR][52] = 127, + [0][0][RTW89_UK][52] = 127, + [0][1][RTW89_FCC][0] = 34, + [0][1][RTW89_ETSI][0] = 12, + [0][1][RTW89_MKK][0] = 12, + [0][1][RTW89_IC][0] = 0, + [0][1][RTW89_KCC][0] = 28, + [0][1][RTW89_ACMA][0] = 12, + [0][1][RTW89_CHILE][0] = 14, + [0][1][RTW89_UKRAINE][0] = 12, + [0][1][RTW89_MEXICO][0] = 34, + [0][1][RTW89_CN][0] = 12, + [0][1][RTW89_QATAR][0] = 12, + [0][1][RTW89_UK][0] = 12, + [0][1][RTW89_FCC][2] = 38, + [0][1][RTW89_ETSI][2] = 12, + [0][1][RTW89_MKK][2] = 12, + [0][1][RTW89_IC][2] = 4, + [0][1][RTW89_KCC][2] = 28, + [0][1][RTW89_ACMA][2] = 12, + [0][1][RTW89_CHILE][2] = 12, + [0][1][RTW89_UKRAINE][2] = 12, + [0][1][RTW89_MEXICO][2] = 38, + [0][1][RTW89_CN][2] = 12, + [0][1][RTW89_QATAR][2] = 12, + [0][1][RTW89_UK][2] = 12, + [0][1][RTW89_FCC][4] = 34, + [0][1][RTW89_ETSI][4] = 12, + [0][1][RTW89_MKK][4] = 14, + [0][1][RTW89_IC][4] = 0, + [0][1][RTW89_KCC][4] = 28, + [0][1][RTW89_ACMA][4] = 12, + [0][1][RTW89_CHILE][4] = 12, + [0][1][RTW89_UKRAINE][4] = 12, + [0][1][RTW89_MEXICO][4] = 34, + [0][1][RTW89_CN][4] = 12, + [0][1][RTW89_QATAR][4] = 12, + [0][1][RTW89_UK][4] = 12, + [0][1][RTW89_FCC][6] = 34, + [0][1][RTW89_ETSI][6] = 12, + [0][1][RTW89_MKK][6] = 14, + [0][1][RTW89_IC][6] = 0, + [0][1][RTW89_KCC][6] = 2, + [0][1][RTW89_ACMA][6] = 12, + [0][1][RTW89_CHILE][6] = 12, + [0][1][RTW89_UKRAINE][6] = 12, + [0][1][RTW89_MEXICO][6] = 34, + [0][1][RTW89_CN][6] = 12, + [0][1][RTW89_QATAR][6] = 12, + [0][1][RTW89_UK][6] = 12, + [0][1][RTW89_FCC][8] = 34, + [0][1][RTW89_ETSI][8] = 12, + [0][1][RTW89_MKK][8] = 14, + [0][1][RTW89_IC][8] = 34, + [0][1][RTW89_KCC][8] = 30, + [0][1][RTW89_ACMA][8] = 12, + [0][1][RTW89_CHILE][8] = 50, + [0][1][RTW89_UKRAINE][8] = 12, + [0][1][RTW89_MEXICO][8] = 34, + [0][1][RTW89_CN][8] = 12, + [0][1][RTW89_QATAR][8] = 12, + [0][1][RTW89_UK][8] = 12, + [0][1][RTW89_FCC][10] = 34, + [0][1][RTW89_ETSI][10] = 12, + [0][1][RTW89_MKK][10] = 14, + [0][1][RTW89_IC][10] = 34, + [0][1][RTW89_KCC][10] = 30, + [0][1][RTW89_ACMA][10] = 12, + [0][1][RTW89_CHILE][10] = 50, + [0][1][RTW89_UKRAINE][10] = 12, + [0][1][RTW89_MEXICO][10] = 34, + [0][1][RTW89_CN][10] = 12, + [0][1][RTW89_QATAR][10] = 12, + [0][1][RTW89_UK][10] = 12, + [0][1][RTW89_FCC][12] = 38, + [0][1][RTW89_ETSI][12] = 12, + [0][1][RTW89_MKK][12] = 12, + [0][1][RTW89_IC][12] = 38, + [0][1][RTW89_KCC][12] = 30, + [0][1][RTW89_ACMA][12] = 12, + [0][1][RTW89_CHILE][12] = 50, + [0][1][RTW89_UKRAINE][12] = 12, + [0][1][RTW89_MEXICO][12] = 38, + [0][1][RTW89_CN][12] = 12, + [0][1][RTW89_QATAR][12] = 12, + [0][1][RTW89_UK][12] = 12, + [0][1][RTW89_FCC][14] = 34, + [0][1][RTW89_ETSI][14] = 12, + [0][1][RTW89_MKK][14] = 12, + [0][1][RTW89_IC][14] = 34, + [0][1][RTW89_KCC][14] = 30, + [0][1][RTW89_ACMA][14] = 12, + [0][1][RTW89_CHILE][14] = 48, + [0][1][RTW89_UKRAINE][14] = 12, + [0][1][RTW89_MEXICO][14] = 34, + [0][1][RTW89_CN][14] = 12, + [0][1][RTW89_QATAR][14] = 12, + [0][1][RTW89_UK][14] = 12, + [0][1][RTW89_FCC][15] = 34, + [0][1][RTW89_ETSI][15] = 12, + [0][1][RTW89_MKK][15] = 32, + [0][1][RTW89_IC][15] = 34, + [0][1][RTW89_KCC][15] = 30, + [0][1][RTW89_ACMA][15] = 12, + [0][1][RTW89_CHILE][15] = 52, + [0][1][RTW89_UKRAINE][15] = 12, + [0][1][RTW89_MEXICO][15] = 34, + [0][1][RTW89_CN][15] = 127, + [0][1][RTW89_QATAR][15] = 12, + [0][1][RTW89_UK][15] = 12, + [0][1][RTW89_FCC][17] = 34, + [0][1][RTW89_ETSI][17] = 12, + [0][1][RTW89_MKK][17] = 34, + [0][1][RTW89_IC][17] = 34, + [0][1][RTW89_KCC][17] = 30, + [0][1][RTW89_ACMA][17] = 12, + [0][1][RTW89_CHILE][17] = 52, + [0][1][RTW89_UKRAINE][17] = 12, + [0][1][RTW89_MEXICO][17] = 34, + [0][1][RTW89_CN][17] = 127, + [0][1][RTW89_QATAR][17] = 12, + [0][1][RTW89_UK][17] = 12, + [0][1][RTW89_FCC][19] = 38, + [0][1][RTW89_ETSI][19] = 12, + [0][1][RTW89_MKK][19] = 34, + [0][1][RTW89_IC][19] = 38, + [0][1][RTW89_KCC][19] = 30, + [0][1][RTW89_ACMA][19] = 12, + [0][1][RTW89_CHILE][19] = 52, + [0][1][RTW89_UKRAINE][19] = 12, + [0][1][RTW89_MEXICO][19] = 38, + [0][1][RTW89_CN][19] = 127, + [0][1][RTW89_QATAR][19] = 12, + [0][1][RTW89_UK][19] = 12, + [0][1][RTW89_FCC][21] = 38, + [0][1][RTW89_ETSI][21] = 12, + [0][1][RTW89_MKK][21] = 34, + [0][1][RTW89_IC][21] = 38, + [0][1][RTW89_KCC][21] = 30, + [0][1][RTW89_ACMA][21] = 12, + [0][1][RTW89_CHILE][21] = 52, + [0][1][RTW89_UKRAINE][21] = 12, + [0][1][RTW89_MEXICO][21] = 38, + [0][1][RTW89_CN][21] = 127, + [0][1][RTW89_QATAR][21] = 12, + [0][1][RTW89_UK][21] = 12, + [0][1][RTW89_FCC][23] = 38, + [0][1][RTW89_ETSI][23] = 12, + [0][1][RTW89_MKK][23] = 34, + [0][1][RTW89_IC][23] = 38, + [0][1][RTW89_KCC][23] = 30, + [0][1][RTW89_ACMA][23] = 12, + [0][1][RTW89_CHILE][23] = 52, + [0][1][RTW89_UKRAINE][23] = 12, + [0][1][RTW89_MEXICO][23] = 38, + [0][1][RTW89_CN][23] = 127, + [0][1][RTW89_QATAR][23] = 12, + [0][1][RTW89_UK][23] = 12, + [0][1][RTW89_FCC][25] = 38, + [0][1][RTW89_ETSI][25] = 12, + [0][1][RTW89_MKK][25] = 34, + [0][1][RTW89_IC][25] = 127, + [0][1][RTW89_KCC][25] = 30, + [0][1][RTW89_ACMA][25] = 127, + [0][1][RTW89_CHILE][25] = 52, + [0][1][RTW89_UKRAINE][25] = 12, + [0][1][RTW89_MEXICO][25] = 38, + [0][1][RTW89_CN][25] = 127, + [0][1][RTW89_QATAR][25] = 12, + [0][1][RTW89_UK][25] = 12, + [0][1][RTW89_FCC][27] = 38, + [0][1][RTW89_ETSI][27] = 12, + [0][1][RTW89_MKK][27] = 34, + [0][1][RTW89_IC][27] = 127, + [0][1][RTW89_KCC][27] = 30, + [0][1][RTW89_ACMA][27] = 127, + [0][1][RTW89_CHILE][27] = 52, + [0][1][RTW89_UKRAINE][27] = 12, + [0][1][RTW89_MEXICO][27] = 38, + [0][1][RTW89_CN][27] = 127, + [0][1][RTW89_QATAR][27] = 12, + [0][1][RTW89_UK][27] = 12, + [0][1][RTW89_FCC][29] = 38, + [0][1][RTW89_ETSI][29] = 12, + [0][1][RTW89_MKK][29] = 34, + [0][1][RTW89_IC][29] = 127, + [0][1][RTW89_KCC][29] = 30, + [0][1][RTW89_ACMA][29] = 127, + [0][1][RTW89_CHILE][29] = 52, + [0][1][RTW89_UKRAINE][29] = 12, + [0][1][RTW89_MEXICO][29] = 38, + [0][1][RTW89_CN][29] = 127, + [0][1][RTW89_QATAR][29] = 12, + [0][1][RTW89_UK][29] = 12, + [0][1][RTW89_FCC][31] = 38, + [0][1][RTW89_ETSI][31] = 12, + [0][1][RTW89_MKK][31] = 34, + [0][1][RTW89_IC][31] = 34, + [0][1][RTW89_KCC][31] = 30, + [0][1][RTW89_ACMA][31] = 12, + [0][1][RTW89_CHILE][31] = 52, + [0][1][RTW89_UKRAINE][31] = 12, + [0][1][RTW89_MEXICO][31] = 38, + [0][1][RTW89_CN][31] = 127, + [0][1][RTW89_QATAR][31] = 12, + [0][1][RTW89_UK][31] = 12, + [0][1][RTW89_FCC][33] = 34, + [0][1][RTW89_ETSI][33] = 12, + [0][1][RTW89_MKK][33] = 34, + [0][1][RTW89_IC][33] = 34, + [0][1][RTW89_KCC][33] = 30, + [0][1][RTW89_ACMA][33] = 12, + [0][1][RTW89_CHILE][33] = 52, + [0][1][RTW89_UKRAINE][33] = 12, + [0][1][RTW89_MEXICO][33] = 34, + [0][1][RTW89_CN][33] = 127, + [0][1][RTW89_QATAR][33] = 12, + [0][1][RTW89_UK][33] = 12, + [0][1][RTW89_FCC][35] = 34, + [0][1][RTW89_ETSI][35] = 12, + [0][1][RTW89_MKK][35] = 34, + [0][1][RTW89_IC][35] = 34, + [0][1][RTW89_KCC][35] = 30, + [0][1][RTW89_ACMA][35] = 12, + [0][1][RTW89_CHILE][35] = 52, + [0][1][RTW89_UKRAINE][35] = 12, + [0][1][RTW89_MEXICO][35] = 34, + [0][1][RTW89_CN][35] = 127, + [0][1][RTW89_QATAR][35] = 12, + [0][1][RTW89_UK][35] = 12, + [0][1][RTW89_FCC][37] = 38, + [0][1][RTW89_ETSI][37] = 127, + [0][1][RTW89_MKK][37] = 34, + [0][1][RTW89_IC][37] = 38, + [0][1][RTW89_KCC][37] = 30, + [0][1][RTW89_ACMA][37] = 38, + [0][1][RTW89_CHILE][37] = 52, + [0][1][RTW89_UKRAINE][37] = 127, + [0][1][RTW89_MEXICO][37] = 38, + [0][1][RTW89_CN][37] = 127, + [0][1][RTW89_QATAR][37] = 127, + [0][1][RTW89_UK][37] = 44, + [0][1][RTW89_FCC][38] = 82, + [0][1][RTW89_ETSI][38] = 16, + [0][1][RTW89_MKK][38] = 127, + [0][1][RTW89_IC][38] = 82, + [0][1][RTW89_KCC][38] = 30, + [0][1][RTW89_ACMA][38] = 84, + [0][1][RTW89_CHILE][38] = 52, + [0][1][RTW89_UKRAINE][38] = 16, + [0][1][RTW89_MEXICO][38] = 82, + [0][1][RTW89_CN][38] = 50, + [0][1][RTW89_QATAR][38] = 16, + [0][1][RTW89_UK][38] = 14, + [0][1][RTW89_FCC][40] = 82, + [0][1][RTW89_ETSI][40] = 16, + [0][1][RTW89_MKK][40] = 127, + [0][1][RTW89_IC][40] = 82, + [0][1][RTW89_KCC][40] = 30, + [0][1][RTW89_ACMA][40] = 84, + [0][1][RTW89_CHILE][40] = 52, + [0][1][RTW89_UKRAINE][40] = 16, + [0][1][RTW89_MEXICO][40] = 82, + [0][1][RTW89_CN][40] = 50, + [0][1][RTW89_QATAR][40] = 16, + [0][1][RTW89_UK][40] = 14, + [0][1][RTW89_FCC][42] = 82, + [0][1][RTW89_ETSI][42] = 16, + [0][1][RTW89_MKK][42] = 127, + [0][1][RTW89_IC][42] = 82, + [0][1][RTW89_KCC][42] = 30, + [0][1][RTW89_ACMA][42] = 84, + [0][1][RTW89_CHILE][42] = 54, + [0][1][RTW89_UKRAINE][42] = 16, + [0][1][RTW89_MEXICO][42] = 82, + [0][1][RTW89_CN][42] = 50, + [0][1][RTW89_QATAR][42] = 16, + [0][1][RTW89_UK][42] = 14, + [0][1][RTW89_FCC][44] = 82, + [0][1][RTW89_ETSI][44] = 16, + [0][1][RTW89_MKK][44] = 127, + [0][1][RTW89_IC][44] = 82, + [0][1][RTW89_KCC][44] = 30, + [0][1][RTW89_ACMA][44] = 84, + [0][1][RTW89_CHILE][44] = 54, + [0][1][RTW89_UKRAINE][44] = 16, + [0][1][RTW89_MEXICO][44] = 82, + [0][1][RTW89_CN][44] = 50, + [0][1][RTW89_QATAR][44] = 16, + [0][1][RTW89_UK][44] = 14, + [0][1][RTW89_FCC][46] = 82, + [0][1][RTW89_ETSI][46] = 16, + [0][1][RTW89_MKK][46] = 127, + [0][1][RTW89_IC][46] = 82, + [0][1][RTW89_KCC][46] = 30, + [0][1][RTW89_ACMA][46] = 84, + [0][1][RTW89_CHILE][46] = 54, + [0][1][RTW89_UKRAINE][46] = 16, + [0][1][RTW89_MEXICO][46] = 82, + [0][1][RTW89_CN][46] = 50, + [0][1][RTW89_QATAR][46] = 16, + [0][1][RTW89_UK][46] = 14, + [0][1][RTW89_FCC][48] = 20, + [0][1][RTW89_ETSI][48] = 127, + [0][1][RTW89_MKK][48] = 127, + [0][1][RTW89_IC][48] = 127, + [0][1][RTW89_KCC][48] = 127, + [0][1][RTW89_ACMA][48] = 127, + [0][1][RTW89_CHILE][48] = 127, + [0][1][RTW89_UKRAINE][48] = 127, + [0][1][RTW89_MEXICO][48] = 127, + [0][1][RTW89_CN][48] = 127, + [0][1][RTW89_QATAR][48] = 127, + [0][1][RTW89_UK][48] = 127, + [0][1][RTW89_FCC][50] = 20, + [0][1][RTW89_ETSI][50] = 127, + [0][1][RTW89_MKK][50] = 127, + [0][1][RTW89_IC][50] = 127, + [0][1][RTW89_KCC][50] = 127, + [0][1][RTW89_ACMA][50] = 127, + [0][1][RTW89_CHILE][50] = 127, + [0][1][RTW89_UKRAINE][50] = 127, + [0][1][RTW89_MEXICO][50] = 127, + [0][1][RTW89_CN][50] = 127, + [0][1][RTW89_QATAR][50] = 127, + [0][1][RTW89_UK][50] = 127, + [0][1][RTW89_FCC][52] = 20, + [0][1][RTW89_ETSI][52] = 127, + [0][1][RTW89_MKK][52] = 127, + [0][1][RTW89_IC][52] = 127, + [0][1][RTW89_KCC][52] = 127, + [0][1][RTW89_ACMA][52] = 127, + [0][1][RTW89_CHILE][52] = 127, + [0][1][RTW89_UKRAINE][52] = 127, + [0][1][RTW89_MEXICO][52] = 127, + [0][1][RTW89_CN][52] = 127, + [0][1][RTW89_QATAR][52] = 127, + [0][1][RTW89_UK][52] = 127, + [1][0][RTW89_FCC][0] = 62, + [1][0][RTW89_ETSI][0] = 34, + [1][0][RTW89_MKK][0] = 36, + [1][0][RTW89_IC][0] = 36, + [1][0][RTW89_KCC][0] = 52, + [1][0][RTW89_ACMA][0] = 34, + [1][0][RTW89_CHILE][0] = 40, + [1][0][RTW89_UKRAINE][0] = 34, + [1][0][RTW89_MEXICO][0] = 62, + [1][0][RTW89_CN][0] = 34, + [1][0][RTW89_QATAR][0] = 34, + [1][0][RTW89_UK][0] = 34, + [1][0][RTW89_FCC][2] = 62, + [1][0][RTW89_ETSI][2] = 34, + [1][0][RTW89_MKK][2] = 36, + [1][0][RTW89_IC][2] = 36, + [1][0][RTW89_KCC][2] = 52, + [1][0][RTW89_ACMA][2] = 34, + [1][0][RTW89_CHILE][2] = 42, + [1][0][RTW89_UKRAINE][2] = 34, + [1][0][RTW89_MEXICO][2] = 62, + [1][0][RTW89_CN][2] = 34, + [1][0][RTW89_QATAR][2] = 34, + [1][0][RTW89_UK][2] = 34, + [1][0][RTW89_FCC][4] = 62, + [1][0][RTW89_ETSI][4] = 34, + [1][0][RTW89_MKK][4] = 34, + [1][0][RTW89_IC][4] = 36, + [1][0][RTW89_KCC][4] = 52, + [1][0][RTW89_ACMA][4] = 34, + [1][0][RTW89_CHILE][4] = 42, + [1][0][RTW89_UKRAINE][4] = 34, + [1][0][RTW89_MEXICO][4] = 62, + [1][0][RTW89_CN][4] = 34, + [1][0][RTW89_QATAR][4] = 34, + [1][0][RTW89_UK][4] = 34, + [1][0][RTW89_FCC][6] = 62, + [1][0][RTW89_ETSI][6] = 34, + [1][0][RTW89_MKK][6] = 34, + [1][0][RTW89_IC][6] = 36, + [1][0][RTW89_KCC][6] = 26, + [1][0][RTW89_ACMA][6] = 34, + [1][0][RTW89_CHILE][6] = 42, + [1][0][RTW89_UKRAINE][6] = 34, + [1][0][RTW89_MEXICO][6] = 62, + [1][0][RTW89_CN][6] = 34, + [1][0][RTW89_QATAR][6] = 34, + [1][0][RTW89_UK][6] = 34, + [1][0][RTW89_FCC][8] = 62, + [1][0][RTW89_ETSI][8] = 34, + [1][0][RTW89_MKK][8] = 36, + [1][0][RTW89_IC][8] = 62, + [1][0][RTW89_KCC][8] = 54, + [1][0][RTW89_ACMA][8] = 34, + [1][0][RTW89_CHILE][8] = 64, + [1][0][RTW89_UKRAINE][8] = 34, + [1][0][RTW89_MEXICO][8] = 62, + [1][0][RTW89_CN][8] = 34, + [1][0][RTW89_QATAR][8] = 34, + [1][0][RTW89_UK][8] = 34, + [1][0][RTW89_FCC][10] = 62, + [1][0][RTW89_ETSI][10] = 34, + [1][0][RTW89_MKK][10] = 36, + [1][0][RTW89_IC][10] = 62, + [1][0][RTW89_KCC][10] = 54, + [1][0][RTW89_ACMA][10] = 34, + [1][0][RTW89_CHILE][10] = 64, + [1][0][RTW89_UKRAINE][10] = 34, + [1][0][RTW89_MEXICO][10] = 62, + [1][0][RTW89_CN][10] = 34, + [1][0][RTW89_QATAR][10] = 34, + [1][0][RTW89_UK][10] = 34, + [1][0][RTW89_FCC][12] = 64, + [1][0][RTW89_ETSI][12] = 34, + [1][0][RTW89_MKK][12] = 36, + [1][0][RTW89_IC][12] = 64, + [1][0][RTW89_KCC][12] = 54, + [1][0][RTW89_ACMA][12] = 34, + [1][0][RTW89_CHILE][12] = 64, + [1][0][RTW89_UKRAINE][12] = 34, + [1][0][RTW89_MEXICO][12] = 64, + [1][0][RTW89_CN][12] = 34, + [1][0][RTW89_QATAR][12] = 34, + [1][0][RTW89_UK][12] = 34, + [1][0][RTW89_FCC][14] = 62, + [1][0][RTW89_ETSI][14] = 34, + [1][0][RTW89_MKK][14] = 36, + [1][0][RTW89_IC][14] = 62, + [1][0][RTW89_KCC][14] = 54, + [1][0][RTW89_ACMA][14] = 34, + [1][0][RTW89_CHILE][14] = 64, + [1][0][RTW89_UKRAINE][14] = 34, + [1][0][RTW89_MEXICO][14] = 62, + [1][0][RTW89_CN][14] = 34, + [1][0][RTW89_QATAR][14] = 34, + [1][0][RTW89_UK][14] = 34, + [1][0][RTW89_FCC][15] = 62, + [1][0][RTW89_ETSI][15] = 34, + [1][0][RTW89_MKK][15] = 54, + [1][0][RTW89_IC][15] = 62, + [1][0][RTW89_KCC][15] = 54, + [1][0][RTW89_ACMA][15] = 34, + [1][0][RTW89_CHILE][15] = 62, + [1][0][RTW89_UKRAINE][15] = 34, + [1][0][RTW89_MEXICO][15] = 62, + [1][0][RTW89_CN][15] = 127, + [1][0][RTW89_QATAR][15] = 34, + [1][0][RTW89_UK][15] = 34, + [1][0][RTW89_FCC][17] = 62, + [1][0][RTW89_ETSI][17] = 34, + [1][0][RTW89_MKK][17] = 58, + [1][0][RTW89_IC][17] = 62, + [1][0][RTW89_KCC][17] = 54, + [1][0][RTW89_ACMA][17] = 34, + [1][0][RTW89_CHILE][17] = 62, + [1][0][RTW89_UKRAINE][17] = 34, + [1][0][RTW89_MEXICO][17] = 62, + [1][0][RTW89_CN][17] = 127, + [1][0][RTW89_QATAR][17] = 34, + [1][0][RTW89_UK][17] = 34, + [1][0][RTW89_FCC][19] = 62, + [1][0][RTW89_ETSI][19] = 34, + [1][0][RTW89_MKK][19] = 58, + [1][0][RTW89_IC][19] = 62, + [1][0][RTW89_KCC][19] = 54, + [1][0][RTW89_ACMA][19] = 34, + [1][0][RTW89_CHILE][19] = 62, + [1][0][RTW89_UKRAINE][19] = 34, + [1][0][RTW89_MEXICO][19] = 62, + [1][0][RTW89_CN][19] = 127, + [1][0][RTW89_QATAR][19] = 34, + [1][0][RTW89_UK][19] = 34, + [1][0][RTW89_FCC][21] = 62, + [1][0][RTW89_ETSI][21] = 34, + [1][0][RTW89_MKK][21] = 58, + [1][0][RTW89_IC][21] = 62, + [1][0][RTW89_KCC][21] = 54, + [1][0][RTW89_ACMA][21] = 34, + [1][0][RTW89_CHILE][21] = 64, + [1][0][RTW89_UKRAINE][21] = 34, + [1][0][RTW89_MEXICO][21] = 62, + [1][0][RTW89_CN][21] = 127, + [1][0][RTW89_QATAR][21] = 34, + [1][0][RTW89_UK][21] = 34, + [1][0][RTW89_FCC][23] = 62, + [1][0][RTW89_ETSI][23] = 34, + [1][0][RTW89_MKK][23] = 58, + [1][0][RTW89_IC][23] = 62, + [1][0][RTW89_KCC][23] = 54, + [1][0][RTW89_ACMA][23] = 34, + [1][0][RTW89_CHILE][23] = 64, + [1][0][RTW89_UKRAINE][23] = 34, + [1][0][RTW89_MEXICO][23] = 62, + [1][0][RTW89_CN][23] = 127, + [1][0][RTW89_QATAR][23] = 34, + [1][0][RTW89_UK][23] = 34, + [1][0][RTW89_FCC][25] = 62, + [1][0][RTW89_ETSI][25] = 34, + [1][0][RTW89_MKK][25] = 58, + [1][0][RTW89_IC][25] = 127, + [1][0][RTW89_KCC][25] = 54, + [1][0][RTW89_ACMA][25] = 127, + [1][0][RTW89_CHILE][25] = 64, + [1][0][RTW89_UKRAINE][25] = 34, + [1][0][RTW89_MEXICO][25] = 62, + [1][0][RTW89_CN][25] = 127, + [1][0][RTW89_QATAR][25] = 34, + [1][0][RTW89_UK][25] = 34, + [1][0][RTW89_FCC][27] = 62, + [1][0][RTW89_ETSI][27] = 34, + [1][0][RTW89_MKK][27] = 58, + [1][0][RTW89_IC][27] = 127, + [1][0][RTW89_KCC][27] = 54, + [1][0][RTW89_ACMA][27] = 127, + [1][0][RTW89_CHILE][27] = 64, + [1][0][RTW89_UKRAINE][27] = 34, + [1][0][RTW89_MEXICO][27] = 62, + [1][0][RTW89_CN][27] = 127, + [1][0][RTW89_QATAR][27] = 34, + [1][0][RTW89_UK][27] = 34, + [1][0][RTW89_FCC][29] = 62, + [1][0][RTW89_ETSI][29] = 34, + [1][0][RTW89_MKK][29] = 58, + [1][0][RTW89_IC][29] = 127, + [1][0][RTW89_KCC][29] = 54, + [1][0][RTW89_ACMA][29] = 127, + [1][0][RTW89_CHILE][29] = 66, + [1][0][RTW89_UKRAINE][29] = 34, + [1][0][RTW89_MEXICO][29] = 62, + [1][0][RTW89_CN][29] = 127, + [1][0][RTW89_QATAR][29] = 34, + [1][0][RTW89_UK][29] = 34, + [1][0][RTW89_FCC][31] = 62, + [1][0][RTW89_ETSI][31] = 34, + [1][0][RTW89_MKK][31] = 58, + [1][0][RTW89_IC][31] = 62, + [1][0][RTW89_KCC][31] = 54, + [1][0][RTW89_ACMA][31] = 34, + [1][0][RTW89_CHILE][31] = 66, + [1][0][RTW89_UKRAINE][31] = 34, + [1][0][RTW89_MEXICO][31] = 62, + [1][0][RTW89_CN][31] = 127, + [1][0][RTW89_QATAR][31] = 34, + [1][0][RTW89_UK][31] = 34, + [1][0][RTW89_FCC][33] = 62, + [1][0][RTW89_ETSI][33] = 34, + [1][0][RTW89_MKK][33] = 58, + [1][0][RTW89_IC][33] = 62, + [1][0][RTW89_KCC][33] = 54, + [1][0][RTW89_ACMA][33] = 34, + [1][0][RTW89_CHILE][33] = 66, + [1][0][RTW89_UKRAINE][33] = 34, + [1][0][RTW89_MEXICO][33] = 62, + [1][0][RTW89_CN][33] = 127, + [1][0][RTW89_QATAR][33] = 34, + [1][0][RTW89_UK][33] = 34, + [1][0][RTW89_FCC][35] = 62, + [1][0][RTW89_ETSI][35] = 34, + [1][0][RTW89_MKK][35] = 58, + [1][0][RTW89_IC][35] = 62, + [1][0][RTW89_KCC][35] = 54, + [1][0][RTW89_ACMA][35] = 34, + [1][0][RTW89_CHILE][35] = 66, + [1][0][RTW89_UKRAINE][35] = 34, + [1][0][RTW89_MEXICO][35] = 62, + [1][0][RTW89_CN][35] = 127, + [1][0][RTW89_QATAR][35] = 34, + [1][0][RTW89_UK][35] = 34, + [1][0][RTW89_FCC][37] = 64, + [1][0][RTW89_ETSI][37] = 127, + [1][0][RTW89_MKK][37] = 52, + [1][0][RTW89_IC][37] = 64, + [1][0][RTW89_KCC][37] = 54, + [1][0][RTW89_ACMA][37] = 64, + [1][0][RTW89_CHILE][37] = 64, + [1][0][RTW89_UKRAINE][37] = 127, + [1][0][RTW89_MEXICO][37] = 64, + [1][0][RTW89_CN][37] = 127, + [1][0][RTW89_QATAR][37] = 127, + [1][0][RTW89_UK][37] = 66, + [1][0][RTW89_FCC][38] = 84, + [1][0][RTW89_ETSI][38] = 28, + [1][0][RTW89_MKK][38] = 127, + [1][0][RTW89_IC][38] = 84, + [1][0][RTW89_KCC][38] = 56, + [1][0][RTW89_ACMA][38] = 84, + [1][0][RTW89_CHILE][38] = 64, + [1][0][RTW89_UKRAINE][38] = 28, + [1][0][RTW89_MEXICO][38] = 84, + [1][0][RTW89_CN][38] = 74, + [1][0][RTW89_QATAR][38] = 28, + [1][0][RTW89_UK][38] = 38, + [1][0][RTW89_FCC][40] = 84, + [1][0][RTW89_ETSI][40] = 28, + [1][0][RTW89_MKK][40] = 127, + [1][0][RTW89_IC][40] = 84, + [1][0][RTW89_KCC][40] = 56, + [1][0][RTW89_ACMA][40] = 84, + [1][0][RTW89_CHILE][40] = 64, + [1][0][RTW89_UKRAINE][40] = 28, + [1][0][RTW89_MEXICO][40] = 84, + [1][0][RTW89_CN][40] = 74, + [1][0][RTW89_QATAR][40] = 28, + [1][0][RTW89_UK][40] = 38, + [1][0][RTW89_FCC][42] = 84, + [1][0][RTW89_ETSI][42] = 28, + [1][0][RTW89_MKK][42] = 127, + [1][0][RTW89_IC][42] = 84, + [1][0][RTW89_KCC][42] = 56, + [1][0][RTW89_ACMA][42] = 84, + [1][0][RTW89_CHILE][42] = 64, + [1][0][RTW89_UKRAINE][42] = 28, + [1][0][RTW89_MEXICO][42] = 84, + [1][0][RTW89_CN][42] = 74, + [1][0][RTW89_QATAR][42] = 28, + [1][0][RTW89_UK][42] = 38, + [1][0][RTW89_FCC][44] = 84, + [1][0][RTW89_ETSI][44] = 28, + [1][0][RTW89_MKK][44] = 127, + [1][0][RTW89_IC][44] = 84, + [1][0][RTW89_KCC][44] = 56, + [1][0][RTW89_ACMA][44] = 84, + [1][0][RTW89_CHILE][44] = 64, + [1][0][RTW89_UKRAINE][44] = 28, + [1][0][RTW89_MEXICO][44] = 84, + [1][0][RTW89_CN][44] = 74, + [1][0][RTW89_QATAR][44] = 28, + [1][0][RTW89_UK][44] = 38, + [1][0][RTW89_FCC][46] = 84, + [1][0][RTW89_ETSI][46] = 28, + [1][0][RTW89_MKK][46] = 127, + [1][0][RTW89_IC][46] = 84, + [1][0][RTW89_KCC][46] = 56, + [1][0][RTW89_ACMA][46] = 84, + [1][0][RTW89_CHILE][46] = 64, + [1][0][RTW89_UKRAINE][46] = 28, + [1][0][RTW89_MEXICO][46] = 84, + [1][0][RTW89_CN][46] = 74, + [1][0][RTW89_QATAR][46] = 28, + [1][0][RTW89_UK][46] = 38, + [1][0][RTW89_FCC][48] = 44, + [1][0][RTW89_ETSI][48] = 127, + [1][0][RTW89_MKK][48] = 127, + [1][0][RTW89_IC][48] = 127, + [1][0][RTW89_KCC][48] = 127, + [1][0][RTW89_ACMA][48] = 127, + [1][0][RTW89_CHILE][48] = 127, + [1][0][RTW89_UKRAINE][48] = 127, + [1][0][RTW89_MEXICO][48] = 127, + [1][0][RTW89_CN][48] = 127, + [1][0][RTW89_QATAR][48] = 127, + [1][0][RTW89_UK][48] = 127, + [1][0][RTW89_FCC][50] = 44, + [1][0][RTW89_ETSI][50] = 127, + [1][0][RTW89_MKK][50] = 127, + [1][0][RTW89_IC][50] = 127, + [1][0][RTW89_KCC][50] = 127, + [1][0][RTW89_ACMA][50] = 127, + [1][0][RTW89_CHILE][50] = 127, + [1][0][RTW89_UKRAINE][50] = 127, + [1][0][RTW89_MEXICO][50] = 127, + [1][0][RTW89_CN][50] = 127, + [1][0][RTW89_QATAR][50] = 127, + [1][0][RTW89_UK][50] = 127, + [1][0][RTW89_FCC][52] = 44, + [1][0][RTW89_ETSI][52] = 127, + [1][0][RTW89_MKK][52] = 127, + [1][0][RTW89_IC][52] = 127, + [1][0][RTW89_KCC][52] = 127, + [1][0][RTW89_ACMA][52] = 127, + [1][0][RTW89_CHILE][52] = 127, + [1][0][RTW89_UKRAINE][52] = 127, + [1][0][RTW89_MEXICO][52] = 127, + [1][0][RTW89_CN][52] = 127, + [1][0][RTW89_QATAR][52] = 127, + [1][0][RTW89_UK][52] = 127, + [1][1][RTW89_FCC][0] = 42, + [1][1][RTW89_ETSI][0] = 22, + [1][1][RTW89_MKK][0] = 22, + [1][1][RTW89_IC][0] = 10, + [1][1][RTW89_KCC][0] = 36, + [1][1][RTW89_ACMA][0] = 22, + [1][1][RTW89_CHILE][0] = 22, + [1][1][RTW89_UKRAINE][0] = 22, + [1][1][RTW89_MEXICO][0] = 42, + [1][1][RTW89_CN][0] = 22, + [1][1][RTW89_QATAR][0] = 22, + [1][1][RTW89_UK][0] = 22, + [1][1][RTW89_FCC][2] = 44, + [1][1][RTW89_ETSI][2] = 22, + [1][1][RTW89_MKK][2] = 22, + [1][1][RTW89_IC][2] = 14, + [1][1][RTW89_KCC][2] = 36, + [1][1][RTW89_ACMA][2] = 22, + [1][1][RTW89_CHILE][2] = 22, + [1][1][RTW89_UKRAINE][2] = 22, + [1][1][RTW89_MEXICO][2] = 44, + [1][1][RTW89_CN][2] = 22, + [1][1][RTW89_QATAR][2] = 22, + [1][1][RTW89_UK][2] = 22, + [1][1][RTW89_FCC][4] = 42, + [1][1][RTW89_ETSI][4] = 22, + [1][1][RTW89_MKK][4] = 20, + [1][1][RTW89_IC][4] = 10, + [1][1][RTW89_KCC][4] = 36, + [1][1][RTW89_ACMA][4] = 22, + [1][1][RTW89_CHILE][4] = 20, + [1][1][RTW89_UKRAINE][4] = 22, + [1][1][RTW89_MEXICO][4] = 42, + [1][1][RTW89_CN][4] = 22, + [1][1][RTW89_QATAR][4] = 22, + [1][1][RTW89_UK][4] = 22, + [1][1][RTW89_FCC][6] = 42, + [1][1][RTW89_ETSI][6] = 22, + [1][1][RTW89_MKK][6] = 20, + [1][1][RTW89_IC][6] = 10, + [1][1][RTW89_KCC][6] = 10, + [1][1][RTW89_ACMA][6] = 22, + [1][1][RTW89_CHILE][6] = 20, + [1][1][RTW89_UKRAINE][6] = 22, + [1][1][RTW89_MEXICO][6] = 42, + [1][1][RTW89_CN][6] = 22, + [1][1][RTW89_QATAR][6] = 22, + [1][1][RTW89_UK][6] = 22, + [1][1][RTW89_FCC][8] = 44, + [1][1][RTW89_ETSI][8] = 22, + [1][1][RTW89_MKK][8] = 20, + [1][1][RTW89_IC][8] = 44, + [1][1][RTW89_KCC][8] = 36, + [1][1][RTW89_ACMA][8] = 22, + [1][1][RTW89_CHILE][8] = 54, + [1][1][RTW89_UKRAINE][8] = 22, + [1][1][RTW89_MEXICO][8] = 44, + [1][1][RTW89_CN][8] = 22, + [1][1][RTW89_QATAR][8] = 22, + [1][1][RTW89_UK][8] = 22, + [1][1][RTW89_FCC][10] = 44, + [1][1][RTW89_ETSI][10] = 22, + [1][1][RTW89_MKK][10] = 20, + [1][1][RTW89_IC][10] = 44, + [1][1][RTW89_KCC][10] = 36, + [1][1][RTW89_ACMA][10] = 22, + [1][1][RTW89_CHILE][10] = 54, + [1][1][RTW89_UKRAINE][10] = 22, + [1][1][RTW89_MEXICO][10] = 44, + [1][1][RTW89_CN][10] = 22, + [1][1][RTW89_QATAR][10] = 22, + [1][1][RTW89_UK][10] = 22, + [1][1][RTW89_FCC][12] = 46, + [1][1][RTW89_ETSI][12] = 22, + [1][1][RTW89_MKK][12] = 22, + [1][1][RTW89_IC][12] = 46, + [1][1][RTW89_KCC][12] = 40, + [1][1][RTW89_ACMA][12] = 22, + [1][1][RTW89_CHILE][12] = 52, + [1][1][RTW89_UKRAINE][12] = 22, + [1][1][RTW89_MEXICO][12] = 46, + [1][1][RTW89_CN][12] = 22, + [1][1][RTW89_QATAR][12] = 22, + [1][1][RTW89_UK][12] = 22, + [1][1][RTW89_FCC][14] = 42, + [1][1][RTW89_ETSI][14] = 22, + [1][1][RTW89_MKK][14] = 22, + [1][1][RTW89_IC][14] = 40, + [1][1][RTW89_KCC][14] = 40, + [1][1][RTW89_ACMA][14] = 22, + [1][1][RTW89_CHILE][14] = 54, + [1][1][RTW89_UKRAINE][14] = 22, + [1][1][RTW89_MEXICO][14] = 42, + [1][1][RTW89_CN][14] = 22, + [1][1][RTW89_QATAR][14] = 22, + [1][1][RTW89_UK][14] = 22, + [1][1][RTW89_FCC][15] = 42, + [1][1][RTW89_ETSI][15] = 22, + [1][1][RTW89_MKK][15] = 42, + [1][1][RTW89_IC][15] = 42, + [1][1][RTW89_KCC][15] = 38, + [1][1][RTW89_ACMA][15] = 22, + [1][1][RTW89_CHILE][15] = 54, + [1][1][RTW89_UKRAINE][15] = 22, + [1][1][RTW89_MEXICO][15] = 42, + [1][1][RTW89_CN][15] = 127, + [1][1][RTW89_QATAR][15] = 22, + [1][1][RTW89_UK][15] = 22, + [1][1][RTW89_FCC][17] = 42, + [1][1][RTW89_ETSI][17] = 22, + [1][1][RTW89_MKK][17] = 44, + [1][1][RTW89_IC][17] = 42, + [1][1][RTW89_KCC][17] = 38, + [1][1][RTW89_ACMA][17] = 22, + [1][1][RTW89_CHILE][17] = 54, + [1][1][RTW89_UKRAINE][17] = 22, + [1][1][RTW89_MEXICO][17] = 42, + [1][1][RTW89_CN][17] = 127, + [1][1][RTW89_QATAR][17] = 22, + [1][1][RTW89_UK][17] = 22, + [1][1][RTW89_FCC][19] = 42, + [1][1][RTW89_ETSI][19] = 22, + [1][1][RTW89_MKK][19] = 44, + [1][1][RTW89_IC][19] = 42, + [1][1][RTW89_KCC][19] = 38, + [1][1][RTW89_ACMA][19] = 22, + [1][1][RTW89_CHILE][19] = 54, + [1][1][RTW89_UKRAINE][19] = 22, + [1][1][RTW89_MEXICO][19] = 42, + [1][1][RTW89_CN][19] = 127, + [1][1][RTW89_QATAR][19] = 22, + [1][1][RTW89_UK][19] = 22, + [1][1][RTW89_FCC][21] = 42, + [1][1][RTW89_ETSI][21] = 22, + [1][1][RTW89_MKK][21] = 44, + [1][1][RTW89_IC][21] = 42, + [1][1][RTW89_KCC][21] = 38, + [1][1][RTW89_ACMA][21] = 22, + [1][1][RTW89_CHILE][21] = 54, + [1][1][RTW89_UKRAINE][21] = 22, + [1][1][RTW89_MEXICO][21] = 42, + [1][1][RTW89_CN][21] = 127, + [1][1][RTW89_QATAR][21] = 22, + [1][1][RTW89_UK][21] = 22, + [1][1][RTW89_FCC][23] = 42, + [1][1][RTW89_ETSI][23] = 22, + [1][1][RTW89_MKK][23] = 44, + [1][1][RTW89_IC][23] = 42, + [1][1][RTW89_KCC][23] = 38, + [1][1][RTW89_ACMA][23] = 22, + [1][1][RTW89_CHILE][23] = 54, + [1][1][RTW89_UKRAINE][23] = 22, + [1][1][RTW89_MEXICO][23] = 42, + [1][1][RTW89_CN][23] = 127, + [1][1][RTW89_QATAR][23] = 22, + [1][1][RTW89_UK][23] = 22, + [1][1][RTW89_FCC][25] = 42, + [1][1][RTW89_ETSI][25] = 22, + [1][1][RTW89_MKK][25] = 44, + [1][1][RTW89_IC][25] = 127, + [1][1][RTW89_KCC][25] = 38, + [1][1][RTW89_ACMA][25] = 127, + [1][1][RTW89_CHILE][25] = 54, + [1][1][RTW89_UKRAINE][25] = 22, + [1][1][RTW89_MEXICO][25] = 42, + [1][1][RTW89_CN][25] = 127, + [1][1][RTW89_QATAR][25] = 22, + [1][1][RTW89_UK][25] = 22, + [1][1][RTW89_FCC][27] = 42, + [1][1][RTW89_ETSI][27] = 22, + [1][1][RTW89_MKK][27] = 44, + [1][1][RTW89_IC][27] = 127, + [1][1][RTW89_KCC][27] = 38, + [1][1][RTW89_ACMA][27] = 127, + [1][1][RTW89_CHILE][27] = 54, + [1][1][RTW89_UKRAINE][27] = 22, + [1][1][RTW89_MEXICO][27] = 42, + [1][1][RTW89_CN][27] = 127, + [1][1][RTW89_QATAR][27] = 22, + [1][1][RTW89_UK][27] = 22, + [1][1][RTW89_FCC][29] = 42, + [1][1][RTW89_ETSI][29] = 22, + [1][1][RTW89_MKK][29] = 44, + [1][1][RTW89_IC][29] = 127, + [1][1][RTW89_KCC][29] = 38, + [1][1][RTW89_ACMA][29] = 127, + [1][1][RTW89_CHILE][29] = 54, + [1][1][RTW89_UKRAINE][29] = 22, + [1][1][RTW89_MEXICO][29] = 42, + [1][1][RTW89_CN][29] = 127, + [1][1][RTW89_QATAR][29] = 22, + [1][1][RTW89_UK][29] = 22, + [1][1][RTW89_FCC][31] = 42, + [1][1][RTW89_ETSI][31] = 22, + [1][1][RTW89_MKK][31] = 44, + [1][1][RTW89_IC][31] = 38, + [1][1][RTW89_KCC][31] = 38, + [1][1][RTW89_ACMA][31] = 22, + [1][1][RTW89_CHILE][31] = 54, + [1][1][RTW89_UKRAINE][31] = 22, + [1][1][RTW89_MEXICO][31] = 42, + [1][1][RTW89_CN][31] = 127, + [1][1][RTW89_QATAR][31] = 22, + [1][1][RTW89_UK][31] = 22, + [1][1][RTW89_FCC][33] = 40, + [1][1][RTW89_ETSI][33] = 22, + [1][1][RTW89_MKK][33] = 44, + [1][1][RTW89_IC][33] = 38, + [1][1][RTW89_KCC][33] = 38, + [1][1][RTW89_ACMA][33] = 22, + [1][1][RTW89_CHILE][33] = 54, + [1][1][RTW89_UKRAINE][33] = 22, + [1][1][RTW89_MEXICO][33] = 40, + [1][1][RTW89_CN][33] = 127, + [1][1][RTW89_QATAR][33] = 22, + [1][1][RTW89_UK][33] = 22, + [1][1][RTW89_FCC][35] = 40, + [1][1][RTW89_ETSI][35] = 22, + [1][1][RTW89_MKK][35] = 44, + [1][1][RTW89_IC][35] = 38, + [1][1][RTW89_KCC][35] = 38, + [1][1][RTW89_ACMA][35] = 22, + [1][1][RTW89_CHILE][35] = 54, + [1][1][RTW89_UKRAINE][35] = 22, + [1][1][RTW89_MEXICO][35] = 40, + [1][1][RTW89_CN][35] = 127, + [1][1][RTW89_QATAR][35] = 22, + [1][1][RTW89_UK][35] = 22, + [1][1][RTW89_FCC][37] = 48, + [1][1][RTW89_ETSI][37] = 127, + [1][1][RTW89_MKK][37] = 42, + [1][1][RTW89_IC][37] = 48, + [1][1][RTW89_KCC][37] = 38, + [1][1][RTW89_ACMA][37] = 48, + [1][1][RTW89_CHILE][37] = 54, + [1][1][RTW89_UKRAINE][37] = 127, + [1][1][RTW89_MEXICO][37] = 48, + [1][1][RTW89_CN][37] = 127, + [1][1][RTW89_QATAR][37] = 127, + [1][1][RTW89_UK][37] = 54, + [1][1][RTW89_FCC][38] = 84, + [1][1][RTW89_ETSI][38] = 16, + [1][1][RTW89_MKK][38] = 127, + [1][1][RTW89_IC][38] = 84, + [1][1][RTW89_KCC][38] = 38, + [1][1][RTW89_ACMA][38] = 82, + [1][1][RTW89_CHILE][38] = 54, + [1][1][RTW89_UKRAINE][38] = 16, + [1][1][RTW89_MEXICO][38] = 84, + [1][1][RTW89_CN][38] = 62, + [1][1][RTW89_QATAR][38] = 16, + [1][1][RTW89_UK][38] = 26, + [1][1][RTW89_FCC][40] = 84, + [1][1][RTW89_ETSI][40] = 16, + [1][1][RTW89_MKK][40] = 127, + [1][1][RTW89_IC][40] = 84, + [1][1][RTW89_KCC][40] = 38, + [1][1][RTW89_ACMA][40] = 82, + [1][1][RTW89_CHILE][40] = 54, + [1][1][RTW89_UKRAINE][40] = 16, + [1][1][RTW89_MEXICO][40] = 84, + [1][1][RTW89_CN][40] = 62, + [1][1][RTW89_QATAR][40] = 16, + [1][1][RTW89_UK][40] = 26, + [1][1][RTW89_FCC][42] = 84, + [1][1][RTW89_ETSI][42] = 16, + [1][1][RTW89_MKK][42] = 127, + [1][1][RTW89_IC][42] = 84, + [1][1][RTW89_KCC][42] = 38, + [1][1][RTW89_ACMA][42] = 84, + [1][1][RTW89_CHILE][42] = 54, + [1][1][RTW89_UKRAINE][42] = 16, + [1][1][RTW89_MEXICO][42] = 84, + [1][1][RTW89_CN][42] = 62, + [1][1][RTW89_QATAR][42] = 16, + [1][1][RTW89_UK][42] = 26, + [1][1][RTW89_FCC][44] = 84, + [1][1][RTW89_ETSI][44] = 16, + [1][1][RTW89_MKK][44] = 127, + [1][1][RTW89_IC][44] = 84, + [1][1][RTW89_KCC][44] = 38, + [1][1][RTW89_ACMA][44] = 84, + [1][1][RTW89_CHILE][44] = 56, + [1][1][RTW89_UKRAINE][44] = 16, + [1][1][RTW89_MEXICO][44] = 84, + [1][1][RTW89_CN][44] = 62, + [1][1][RTW89_QATAR][44] = 16, + [1][1][RTW89_UK][44] = 26, + [1][1][RTW89_FCC][46] = 84, + [1][1][RTW89_ETSI][46] = 16, + [1][1][RTW89_MKK][46] = 127, + [1][1][RTW89_IC][46] = 84, + [1][1][RTW89_KCC][46] = 38, + [1][1][RTW89_ACMA][46] = 84, + [1][1][RTW89_CHILE][46] = 56, + [1][1][RTW89_UKRAINE][46] = 16, + [1][1][RTW89_MEXICO][46] = 84, + [1][1][RTW89_CN][46] = 62, + [1][1][RTW89_QATAR][46] = 16, + [1][1][RTW89_UK][46] = 26, + [1][1][RTW89_FCC][48] = 32, + [1][1][RTW89_ETSI][48] = 127, + [1][1][RTW89_MKK][48] = 127, + [1][1][RTW89_IC][48] = 127, + [1][1][RTW89_KCC][48] = 127, + [1][1][RTW89_ACMA][48] = 127, + [1][1][RTW89_CHILE][48] = 127, + [1][1][RTW89_UKRAINE][48] = 127, + [1][1][RTW89_MEXICO][48] = 127, + [1][1][RTW89_CN][48] = 127, + [1][1][RTW89_QATAR][48] = 127, + [1][1][RTW89_UK][48] = 127, + [1][1][RTW89_FCC][50] = 32, + [1][1][RTW89_ETSI][50] = 127, + [1][1][RTW89_MKK][50] = 127, + [1][1][RTW89_IC][50] = 127, + [1][1][RTW89_KCC][50] = 127, + [1][1][RTW89_ACMA][50] = 127, + [1][1][RTW89_CHILE][50] = 127, + [1][1][RTW89_UKRAINE][50] = 127, + [1][1][RTW89_MEXICO][50] = 127, + [1][1][RTW89_CN][50] = 127, + [1][1][RTW89_QATAR][50] = 127, + [1][1][RTW89_UK][50] = 127, + [1][1][RTW89_FCC][52] = 32, + [1][1][RTW89_ETSI][52] = 127, + [1][1][RTW89_MKK][52] = 127, + [1][1][RTW89_IC][52] = 127, + [1][1][RTW89_KCC][52] = 127, + [1][1][RTW89_ACMA][52] = 127, + [1][1][RTW89_CHILE][52] = 127, + [1][1][RTW89_UKRAINE][52] = 127, + [1][1][RTW89_MEXICO][52] = 127, + [1][1][RTW89_CN][52] = 127, + [1][1][RTW89_QATAR][52] = 127, + [1][1][RTW89_UK][52] = 127, + [2][0][RTW89_FCC][0] = 70, + [2][0][RTW89_ETSI][0] = 48, + [2][0][RTW89_MKK][0] = 48, + [2][0][RTW89_IC][0] = 46, + [2][0][RTW89_KCC][0] = 66, + [2][0][RTW89_ACMA][0] = 48, + [2][0][RTW89_CHILE][0] = 44, + [2][0][RTW89_UKRAINE][0] = 48, + [2][0][RTW89_MEXICO][0] = 64, + [2][0][RTW89_CN][0] = 48, + [2][0][RTW89_QATAR][0] = 48, + [2][0][RTW89_UK][0] = 48, + [2][0][RTW89_FCC][2] = 70, + [2][0][RTW89_ETSI][2] = 48, + [2][0][RTW89_MKK][2] = 48, + [2][0][RTW89_IC][2] = 46, + [2][0][RTW89_KCC][2] = 66, + [2][0][RTW89_ACMA][2] = 48, + [2][0][RTW89_CHILE][2] = 44, + [2][0][RTW89_UKRAINE][2] = 48, + [2][0][RTW89_MEXICO][2] = 64, + [2][0][RTW89_CN][2] = 48, + [2][0][RTW89_QATAR][2] = 48, + [2][0][RTW89_UK][2] = 48, + [2][0][RTW89_FCC][4] = 70, + [2][0][RTW89_ETSI][4] = 48, + [2][0][RTW89_MKK][4] = 48, + [2][0][RTW89_IC][4] = 46, + [2][0][RTW89_KCC][4] = 66, + [2][0][RTW89_ACMA][4] = 48, + [2][0][RTW89_CHILE][4] = 44, + [2][0][RTW89_UKRAINE][4] = 48, + [2][0][RTW89_MEXICO][4] = 64, + [2][0][RTW89_CN][4] = 48, + [2][0][RTW89_QATAR][4] = 48, + [2][0][RTW89_UK][4] = 48, + [2][0][RTW89_FCC][6] = 70, + [2][0][RTW89_ETSI][6] = 48, + [2][0][RTW89_MKK][6] = 48, + [2][0][RTW89_IC][6] = 46, + [2][0][RTW89_KCC][6] = 38, + [2][0][RTW89_ACMA][6] = 48, + [2][0][RTW89_CHILE][6] = 44, + [2][0][RTW89_UKRAINE][6] = 48, + [2][0][RTW89_MEXICO][6] = 64, + [2][0][RTW89_CN][6] = 48, + [2][0][RTW89_QATAR][6] = 48, + [2][0][RTW89_UK][6] = 48, + [2][0][RTW89_FCC][8] = 70, + [2][0][RTW89_ETSI][8] = 48, + [2][0][RTW89_MKK][8] = 48, + [2][0][RTW89_IC][8] = 66, + [2][0][RTW89_KCC][8] = 64, + [2][0][RTW89_ACMA][8] = 48, + [2][0][RTW89_CHILE][8] = 66, + [2][0][RTW89_UKRAINE][8] = 48, + [2][0][RTW89_MEXICO][8] = 70, + [2][0][RTW89_CN][8] = 48, + [2][0][RTW89_QATAR][8] = 48, + [2][0][RTW89_UK][8] = 48, + [2][0][RTW89_FCC][10] = 70, + [2][0][RTW89_ETSI][10] = 48, + [2][0][RTW89_MKK][10] = 48, + [2][0][RTW89_IC][10] = 66, + [2][0][RTW89_KCC][10] = 64, + [2][0][RTW89_ACMA][10] = 48, + [2][0][RTW89_CHILE][10] = 66, + [2][0][RTW89_UKRAINE][10] = 48, + [2][0][RTW89_MEXICO][10] = 70, + [2][0][RTW89_CN][10] = 48, + [2][0][RTW89_QATAR][10] = 48, + [2][0][RTW89_UK][10] = 48, + [2][0][RTW89_FCC][12] = 70, + [2][0][RTW89_ETSI][12] = 48, + [2][0][RTW89_MKK][12] = 46, + [2][0][RTW89_IC][12] = 66, + [2][0][RTW89_KCC][12] = 64, + [2][0][RTW89_ACMA][12] = 48, + [2][0][RTW89_CHILE][12] = 66, + [2][0][RTW89_UKRAINE][12] = 48, + [2][0][RTW89_MEXICO][12] = 70, + [2][0][RTW89_CN][12] = 48, + [2][0][RTW89_QATAR][12] = 48, + [2][0][RTW89_UK][12] = 48, + [2][0][RTW89_FCC][14] = 70, + [2][0][RTW89_ETSI][14] = 48, + [2][0][RTW89_MKK][14] = 46, + [2][0][RTW89_IC][14] = 66, + [2][0][RTW89_KCC][14] = 64, + [2][0][RTW89_ACMA][14] = 48, + [2][0][RTW89_CHILE][14] = 66, + [2][0][RTW89_UKRAINE][14] = 48, + [2][0][RTW89_MEXICO][14] = 70, + [2][0][RTW89_CN][14] = 48, + [2][0][RTW89_QATAR][14] = 48, + [2][0][RTW89_UK][14] = 48, + [2][0][RTW89_FCC][15] = 70, + [2][0][RTW89_ETSI][15] = 48, + [2][0][RTW89_MKK][15] = 68, + [2][0][RTW89_IC][15] = 70, + [2][0][RTW89_KCC][15] = 64, + [2][0][RTW89_ACMA][15] = 48, + [2][0][RTW89_CHILE][15] = 62, + [2][0][RTW89_UKRAINE][15] = 48, + [2][0][RTW89_MEXICO][15] = 70, + [2][0][RTW89_CN][15] = 127, + [2][0][RTW89_QATAR][15] = 48, + [2][0][RTW89_UK][15] = 48, + [2][0][RTW89_FCC][17] = 70, + [2][0][RTW89_ETSI][17] = 48, + [2][0][RTW89_MKK][17] = 70, + [2][0][RTW89_IC][17] = 70, + [2][0][RTW89_KCC][17] = 64, + [2][0][RTW89_ACMA][17] = 48, + [2][0][RTW89_CHILE][17] = 62, + [2][0][RTW89_UKRAINE][17] = 48, + [2][0][RTW89_MEXICO][17] = 70, + [2][0][RTW89_CN][17] = 127, + [2][0][RTW89_QATAR][17] = 48, + [2][0][RTW89_UK][17] = 48, + [2][0][RTW89_FCC][19] = 70, + [2][0][RTW89_ETSI][19] = 48, + [2][0][RTW89_MKK][19] = 70, + [2][0][RTW89_IC][19] = 70, + [2][0][RTW89_KCC][19] = 64, + [2][0][RTW89_ACMA][19] = 48, + [2][0][RTW89_CHILE][19] = 62, + [2][0][RTW89_UKRAINE][19] = 48, + [2][0][RTW89_MEXICO][19] = 70, + [2][0][RTW89_CN][19] = 127, + [2][0][RTW89_QATAR][19] = 48, + [2][0][RTW89_UK][19] = 48, + [2][0][RTW89_FCC][21] = 70, + [2][0][RTW89_ETSI][21] = 48, + [2][0][RTW89_MKK][21] = 70, + [2][0][RTW89_IC][21] = 70, + [2][0][RTW89_KCC][21] = 64, + [2][0][RTW89_ACMA][21] = 48, + [2][0][RTW89_CHILE][21] = 64, + [2][0][RTW89_UKRAINE][21] = 48, + [2][0][RTW89_MEXICO][21] = 70, + [2][0][RTW89_CN][21] = 127, + [2][0][RTW89_QATAR][21] = 48, + [2][0][RTW89_UK][21] = 48, + [2][0][RTW89_FCC][23] = 70, + [2][0][RTW89_ETSI][23] = 48, + [2][0][RTW89_MKK][23] = 70, + [2][0][RTW89_IC][23] = 70, + [2][0][RTW89_KCC][23] = 64, + [2][0][RTW89_ACMA][23] = 48, + [2][0][RTW89_CHILE][23] = 64, + [2][0][RTW89_UKRAINE][23] = 48, + [2][0][RTW89_MEXICO][23] = 70, + [2][0][RTW89_CN][23] = 127, + [2][0][RTW89_QATAR][23] = 48, + [2][0][RTW89_UK][23] = 48, + [2][0][RTW89_FCC][25] = 70, + [2][0][RTW89_ETSI][25] = 48, + [2][0][RTW89_MKK][25] = 70, + [2][0][RTW89_IC][25] = 127, + [2][0][RTW89_KCC][25] = 64, + [2][0][RTW89_ACMA][25] = 127, + [2][0][RTW89_CHILE][25] = 64, + [2][0][RTW89_UKRAINE][25] = 48, + [2][0][RTW89_MEXICO][25] = 70, + [2][0][RTW89_CN][25] = 127, + [2][0][RTW89_QATAR][25] = 48, + [2][0][RTW89_UK][25] = 48, + [2][0][RTW89_FCC][27] = 70, + [2][0][RTW89_ETSI][27] = 48, + [2][0][RTW89_MKK][27] = 70, + [2][0][RTW89_IC][27] = 127, + [2][0][RTW89_KCC][27] = 64, + [2][0][RTW89_ACMA][27] = 127, + [2][0][RTW89_CHILE][27] = 64, + [2][0][RTW89_UKRAINE][27] = 48, + [2][0][RTW89_MEXICO][27] = 70, + [2][0][RTW89_CN][27] = 127, + [2][0][RTW89_QATAR][27] = 48, + [2][0][RTW89_UK][27] = 48, + [2][0][RTW89_FCC][29] = 70, + [2][0][RTW89_ETSI][29] = 48, + [2][0][RTW89_MKK][29] = 70, + [2][0][RTW89_IC][29] = 127, + [2][0][RTW89_KCC][29] = 64, + [2][0][RTW89_ACMA][29] = 127, + [2][0][RTW89_CHILE][29] = 66, + [2][0][RTW89_UKRAINE][29] = 48, + [2][0][RTW89_MEXICO][29] = 70, + [2][0][RTW89_CN][29] = 127, + [2][0][RTW89_QATAR][29] = 48, + [2][0][RTW89_UK][29] = 48, + [2][0][RTW89_FCC][31] = 70, + [2][0][RTW89_ETSI][31] = 48, + [2][0][RTW89_MKK][31] = 70, + [2][0][RTW89_IC][31] = 72, + [2][0][RTW89_KCC][31] = 64, + [2][0][RTW89_ACMA][31] = 48, + [2][0][RTW89_CHILE][31] = 66, + [2][0][RTW89_UKRAINE][31] = 48, + [2][0][RTW89_MEXICO][31] = 70, + [2][0][RTW89_CN][31] = 127, + [2][0][RTW89_QATAR][31] = 48, + [2][0][RTW89_UK][31] = 48, + [2][0][RTW89_FCC][33] = 72, + [2][0][RTW89_ETSI][33] = 48, + [2][0][RTW89_MKK][33] = 70, + [2][0][RTW89_IC][33] = 72, + [2][0][RTW89_KCC][33] = 64, + [2][0][RTW89_ACMA][33] = 48, + [2][0][RTW89_CHILE][33] = 66, + [2][0][RTW89_UKRAINE][33] = 48, + [2][0][RTW89_MEXICO][33] = 72, + [2][0][RTW89_CN][33] = 127, + [2][0][RTW89_QATAR][33] = 48, + [2][0][RTW89_UK][33] = 48, + [2][0][RTW89_FCC][35] = 72, + [2][0][RTW89_ETSI][35] = 48, + [2][0][RTW89_MKK][35] = 70, + [2][0][RTW89_IC][35] = 72, + [2][0][RTW89_KCC][35] = 64, + [2][0][RTW89_ACMA][35] = 48, + [2][0][RTW89_CHILE][35] = 66, + [2][0][RTW89_UKRAINE][35] = 48, + [2][0][RTW89_MEXICO][35] = 72, + [2][0][RTW89_CN][35] = 127, + [2][0][RTW89_QATAR][35] = 48, + [2][0][RTW89_UK][35] = 48, + [2][0][RTW89_FCC][37] = 70, + [2][0][RTW89_ETSI][37] = 127, + [2][0][RTW89_MKK][37] = 66, + [2][0][RTW89_IC][37] = 70, + [2][0][RTW89_KCC][37] = 64, + [2][0][RTW89_ACMA][37] = 76, + [2][0][RTW89_CHILE][37] = 66, + [2][0][RTW89_UKRAINE][37] = 127, + [2][0][RTW89_MEXICO][37] = 70, + [2][0][RTW89_CN][37] = 127, + [2][0][RTW89_QATAR][37] = 127, + [2][0][RTW89_UK][37] = 76, + [2][0][RTW89_FCC][38] = 84, + [2][0][RTW89_ETSI][38] = 28, + [2][0][RTW89_MKK][38] = 127, + [2][0][RTW89_IC][38] = 84, + [2][0][RTW89_KCC][38] = 66, + [2][0][RTW89_ACMA][38] = 84, + [2][0][RTW89_CHILE][38] = 64, + [2][0][RTW89_UKRAINE][38] = 28, + [2][0][RTW89_MEXICO][38] = 84, + [2][0][RTW89_CN][38] = 76, + [2][0][RTW89_QATAR][38] = 28, + [2][0][RTW89_UK][38] = 50, + [2][0][RTW89_FCC][40] = 84, + [2][0][RTW89_ETSI][40] = 28, + [2][0][RTW89_MKK][40] = 127, + [2][0][RTW89_IC][40] = 84, + [2][0][RTW89_KCC][40] = 66, + [2][0][RTW89_ACMA][40] = 84, + [2][0][RTW89_CHILE][40] = 64, + [2][0][RTW89_UKRAINE][40] = 28, + [2][0][RTW89_MEXICO][40] = 84, + [2][0][RTW89_CN][40] = 76, + [2][0][RTW89_QATAR][40] = 28, + [2][0][RTW89_UK][40] = 50, + [2][0][RTW89_FCC][42] = 84, + [2][0][RTW89_ETSI][42] = 28, + [2][0][RTW89_MKK][42] = 127, + [2][0][RTW89_IC][42] = 84, + [2][0][RTW89_KCC][42] = 66, + [2][0][RTW89_ACMA][42] = 84, + [2][0][RTW89_CHILE][42] = 66, + [2][0][RTW89_UKRAINE][42] = 28, + [2][0][RTW89_MEXICO][42] = 84, + [2][0][RTW89_CN][42] = 76, + [2][0][RTW89_QATAR][42] = 28, + [2][0][RTW89_UK][42] = 50, + [2][0][RTW89_FCC][44] = 84, + [2][0][RTW89_ETSI][44] = 28, + [2][0][RTW89_MKK][44] = 127, + [2][0][RTW89_IC][44] = 84, + [2][0][RTW89_KCC][44] = 66, + [2][0][RTW89_ACMA][44] = 84, + [2][0][RTW89_CHILE][44] = 64, + [2][0][RTW89_UKRAINE][44] = 28, + [2][0][RTW89_MEXICO][44] = 84, + [2][0][RTW89_CN][44] = 76, + [2][0][RTW89_QATAR][44] = 28, + [2][0][RTW89_UK][44] = 50, + [2][0][RTW89_FCC][46] = 84, + [2][0][RTW89_ETSI][46] = 28, + [2][0][RTW89_MKK][46] = 127, + [2][0][RTW89_IC][46] = 84, + [2][0][RTW89_KCC][46] = 66, + [2][0][RTW89_ACMA][46] = 84, + [2][0][RTW89_CHILE][46] = 64, + [2][0][RTW89_UKRAINE][46] = 28, + [2][0][RTW89_MEXICO][46] = 84, + [2][0][RTW89_CN][46] = 76, + [2][0][RTW89_QATAR][46] = 28, + [2][0][RTW89_UK][46] = 50, + [2][0][RTW89_FCC][48] = 56, + [2][0][RTW89_ETSI][48] = 127, + [2][0][RTW89_MKK][48] = 127, + [2][0][RTW89_IC][48] = 127, + [2][0][RTW89_KCC][48] = 127, + [2][0][RTW89_ACMA][48] = 127, + [2][0][RTW89_CHILE][48] = 127, + [2][0][RTW89_UKRAINE][48] = 127, + [2][0][RTW89_MEXICO][48] = 127, + [2][0][RTW89_CN][48] = 127, + [2][0][RTW89_QATAR][48] = 127, + [2][0][RTW89_UK][48] = 127, + [2][0][RTW89_FCC][50] = 56, + [2][0][RTW89_ETSI][50] = 127, + [2][0][RTW89_MKK][50] = 127, + [2][0][RTW89_IC][50] = 127, + [2][0][RTW89_KCC][50] = 127, + [2][0][RTW89_ACMA][50] = 127, + [2][0][RTW89_CHILE][50] = 127, + [2][0][RTW89_UKRAINE][50] = 127, + [2][0][RTW89_MEXICO][50] = 127, + [2][0][RTW89_CN][50] = 127, + [2][0][RTW89_QATAR][50] = 127, + [2][0][RTW89_UK][50] = 127, + [2][0][RTW89_FCC][52] = 56, + [2][0][RTW89_ETSI][52] = 127, + [2][0][RTW89_MKK][52] = 127, + [2][0][RTW89_IC][52] = 127, + [2][0][RTW89_KCC][52] = 127, + [2][0][RTW89_ACMA][52] = 127, + [2][0][RTW89_CHILE][52] = 127, + [2][0][RTW89_UKRAINE][52] = 127, + [2][0][RTW89_MEXICO][52] = 127, + [2][0][RTW89_CN][52] = 127, + [2][0][RTW89_QATAR][52] = 127, + [2][0][RTW89_UK][52] = 127, + [2][1][RTW89_FCC][0] = 50, + [2][1][RTW89_ETSI][0] = 36, + [2][1][RTW89_MKK][0] = 36, + [2][1][RTW89_IC][0] = 20, + [2][1][RTW89_KCC][0] = 46, + [2][1][RTW89_ACMA][0] = 36, + [2][1][RTW89_CHILE][0] = 32, + [2][1][RTW89_UKRAINE][0] = 36, + [2][1][RTW89_MEXICO][0] = 52, + [2][1][RTW89_CN][0] = 36, + [2][1][RTW89_QATAR][0] = 36, + [2][1][RTW89_UK][0] = 36, + [2][1][RTW89_FCC][2] = 50, + [2][1][RTW89_ETSI][2] = 36, + [2][1][RTW89_MKK][2] = 36, + [2][1][RTW89_IC][2] = 18, + [2][1][RTW89_KCC][2] = 46, + [2][1][RTW89_ACMA][2] = 36, + [2][1][RTW89_CHILE][2] = 32, + [2][1][RTW89_UKRAINE][2] = 36, + [2][1][RTW89_MEXICO][2] = 52, + [2][1][RTW89_CN][2] = 36, + [2][1][RTW89_QATAR][2] = 36, + [2][1][RTW89_UK][2] = 36, + [2][1][RTW89_FCC][4] = 50, + [2][1][RTW89_ETSI][4] = 36, + [2][1][RTW89_MKK][4] = 36, + [2][1][RTW89_IC][4] = 22, + [2][1][RTW89_KCC][4] = 46, + [2][1][RTW89_ACMA][4] = 36, + [2][1][RTW89_CHILE][4] = 30, + [2][1][RTW89_UKRAINE][4] = 36, + [2][1][RTW89_MEXICO][4] = 52, + [2][1][RTW89_CN][4] = 36, + [2][1][RTW89_QATAR][4] = 36, + [2][1][RTW89_UK][4] = 36, + [2][1][RTW89_FCC][6] = 50, + [2][1][RTW89_ETSI][6] = 36, + [2][1][RTW89_MKK][6] = 36, + [2][1][RTW89_IC][6] = 22, + [2][1][RTW89_KCC][6] = 22, + [2][1][RTW89_ACMA][6] = 36, + [2][1][RTW89_CHILE][6] = 30, + [2][1][RTW89_UKRAINE][6] = 36, + [2][1][RTW89_MEXICO][6] = 52, + [2][1][RTW89_CN][6] = 36, + [2][1][RTW89_QATAR][6] = 36, + [2][1][RTW89_UK][6] = 36, + [2][1][RTW89_FCC][8] = 50, + [2][1][RTW89_ETSI][8] = 36, + [2][1][RTW89_MKK][8] = 34, + [2][1][RTW89_IC][8] = 50, + [2][1][RTW89_KCC][8] = 48, + [2][1][RTW89_ACMA][8] = 36, + [2][1][RTW89_CHILE][8] = 54, + [2][1][RTW89_UKRAINE][8] = 36, + [2][1][RTW89_MEXICO][8] = 50, + [2][1][RTW89_CN][8] = 36, + [2][1][RTW89_QATAR][8] = 36, + [2][1][RTW89_UK][8] = 36, + [2][1][RTW89_FCC][10] = 50, + [2][1][RTW89_ETSI][10] = 36, + [2][1][RTW89_MKK][10] = 34, + [2][1][RTW89_IC][10] = 50, + [2][1][RTW89_KCC][10] = 48, + [2][1][RTW89_ACMA][10] = 36, + [2][1][RTW89_CHILE][10] = 54, + [2][1][RTW89_UKRAINE][10] = 36, + [2][1][RTW89_MEXICO][10] = 50, + [2][1][RTW89_CN][10] = 36, + [2][1][RTW89_QATAR][10] = 36, + [2][1][RTW89_UK][10] = 36, + [2][1][RTW89_FCC][12] = 52, + [2][1][RTW89_ETSI][12] = 36, + [2][1][RTW89_MKK][12] = 36, + [2][1][RTW89_IC][12] = 52, + [2][1][RTW89_KCC][12] = 48, + [2][1][RTW89_ACMA][12] = 36, + [2][1][RTW89_CHILE][12] = 54, + [2][1][RTW89_UKRAINE][12] = 36, + [2][1][RTW89_MEXICO][12] = 52, + [2][1][RTW89_CN][12] = 36, + [2][1][RTW89_QATAR][12] = 36, + [2][1][RTW89_UK][12] = 36, + [2][1][RTW89_FCC][14] = 52, + [2][1][RTW89_ETSI][14] = 36, + [2][1][RTW89_MKK][14] = 36, + [2][1][RTW89_IC][14] = 52, + [2][1][RTW89_KCC][14] = 48, + [2][1][RTW89_ACMA][14] = 36, + [2][1][RTW89_CHILE][14] = 54, + [2][1][RTW89_UKRAINE][14] = 36, + [2][1][RTW89_MEXICO][14] = 52, + [2][1][RTW89_CN][14] = 36, + [2][1][RTW89_QATAR][14] = 36, + [2][1][RTW89_UK][14] = 36, + [2][1][RTW89_FCC][15] = 50, + [2][1][RTW89_ETSI][15] = 36, + [2][1][RTW89_MKK][15] = 54, + [2][1][RTW89_IC][15] = 50, + [2][1][RTW89_KCC][15] = 48, + [2][1][RTW89_ACMA][15] = 36, + [2][1][RTW89_CHILE][15] = 56, + [2][1][RTW89_UKRAINE][15] = 36, + [2][1][RTW89_MEXICO][15] = 50, + [2][1][RTW89_CN][15] = 127, + [2][1][RTW89_QATAR][15] = 36, + [2][1][RTW89_UK][15] = 36, + [2][1][RTW89_FCC][17] = 50, + [2][1][RTW89_ETSI][17] = 36, + [2][1][RTW89_MKK][17] = 56, + [2][1][RTW89_IC][17] = 50, + [2][1][RTW89_KCC][17] = 48, + [2][1][RTW89_ACMA][17] = 36, + [2][1][RTW89_CHILE][17] = 56, + [2][1][RTW89_UKRAINE][17] = 36, + [2][1][RTW89_MEXICO][17] = 50, + [2][1][RTW89_CN][17] = 127, + [2][1][RTW89_QATAR][17] = 36, + [2][1][RTW89_UK][17] = 36, + [2][1][RTW89_FCC][19] = 50, + [2][1][RTW89_ETSI][19] = 36, + [2][1][RTW89_MKK][19] = 56, + [2][1][RTW89_IC][19] = 50, + [2][1][RTW89_KCC][19] = 48, + [2][1][RTW89_ACMA][19] = 36, + [2][1][RTW89_CHILE][19] = 56, + [2][1][RTW89_UKRAINE][19] = 36, + [2][1][RTW89_MEXICO][19] = 50, + [2][1][RTW89_CN][19] = 127, + [2][1][RTW89_QATAR][19] = 36, + [2][1][RTW89_UK][19] = 36, + [2][1][RTW89_FCC][21] = 50, + [2][1][RTW89_ETSI][21] = 36, + [2][1][RTW89_MKK][21] = 56, + [2][1][RTW89_IC][21] = 50, + [2][1][RTW89_KCC][21] = 48, + [2][1][RTW89_ACMA][21] = 36, + [2][1][RTW89_CHILE][21] = 58, + [2][1][RTW89_UKRAINE][21] = 36, + [2][1][RTW89_MEXICO][21] = 50, + [2][1][RTW89_CN][21] = 127, + [2][1][RTW89_QATAR][21] = 36, + [2][1][RTW89_UK][21] = 36, + [2][1][RTW89_FCC][23] = 50, + [2][1][RTW89_ETSI][23] = 36, + [2][1][RTW89_MKK][23] = 56, + [2][1][RTW89_IC][23] = 50, + [2][1][RTW89_KCC][23] = 48, + [2][1][RTW89_ACMA][23] = 36, + [2][1][RTW89_CHILE][23] = 58, + [2][1][RTW89_UKRAINE][23] = 36, + [2][1][RTW89_MEXICO][23] = 50, + [2][1][RTW89_CN][23] = 127, + [2][1][RTW89_QATAR][23] = 36, + [2][1][RTW89_UK][23] = 36, + [2][1][RTW89_FCC][25] = 50, + [2][1][RTW89_ETSI][25] = 36, + [2][1][RTW89_MKK][25] = 56, + [2][1][RTW89_IC][25] = 127, + [2][1][RTW89_KCC][25] = 48, + [2][1][RTW89_ACMA][25] = 127, + [2][1][RTW89_CHILE][25] = 58, + [2][1][RTW89_UKRAINE][25] = 36, + [2][1][RTW89_MEXICO][25] = 50, + [2][1][RTW89_CN][25] = 127, + [2][1][RTW89_QATAR][25] = 36, + [2][1][RTW89_UK][25] = 36, + [2][1][RTW89_FCC][27] = 50, + [2][1][RTW89_ETSI][27] = 36, + [2][1][RTW89_MKK][27] = 56, + [2][1][RTW89_IC][27] = 127, + [2][1][RTW89_KCC][27] = 48, + [2][1][RTW89_ACMA][27] = 127, + [2][1][RTW89_CHILE][27] = 58, + [2][1][RTW89_UKRAINE][27] = 36, + [2][1][RTW89_MEXICO][27] = 50, + [2][1][RTW89_CN][27] = 127, + [2][1][RTW89_QATAR][27] = 36, + [2][1][RTW89_UK][27] = 36, + [2][1][RTW89_FCC][29] = 50, + [2][1][RTW89_ETSI][29] = 36, + [2][1][RTW89_MKK][29] = 56, + [2][1][RTW89_IC][29] = 127, + [2][1][RTW89_KCC][29] = 48, + [2][1][RTW89_ACMA][29] = 127, + [2][1][RTW89_CHILE][29] = 56, + [2][1][RTW89_UKRAINE][29] = 36, + [2][1][RTW89_MEXICO][29] = 50, + [2][1][RTW89_CN][29] = 127, + [2][1][RTW89_QATAR][29] = 36, + [2][1][RTW89_UK][29] = 36, + [2][1][RTW89_FCC][31] = 50, + [2][1][RTW89_ETSI][31] = 36, + [2][1][RTW89_MKK][31] = 56, + [2][1][RTW89_IC][31] = 50, + [2][1][RTW89_KCC][31] = 48, + [2][1][RTW89_ACMA][31] = 36, + [2][1][RTW89_CHILE][31] = 56, + [2][1][RTW89_UKRAINE][31] = 36, + [2][1][RTW89_MEXICO][31] = 50, + [2][1][RTW89_CN][31] = 127, + [2][1][RTW89_QATAR][31] = 36, + [2][1][RTW89_UK][31] = 36, + [2][1][RTW89_FCC][33] = 50, + [2][1][RTW89_ETSI][33] = 36, + [2][1][RTW89_MKK][33] = 56, + [2][1][RTW89_IC][33] = 50, + [2][1][RTW89_KCC][33] = 48, + [2][1][RTW89_ACMA][33] = 36, + [2][1][RTW89_CHILE][33] = 56, + [2][1][RTW89_UKRAINE][33] = 36, + [2][1][RTW89_MEXICO][33] = 50, + [2][1][RTW89_CN][33] = 127, + [2][1][RTW89_QATAR][33] = 36, + [2][1][RTW89_UK][33] = 36, + [2][1][RTW89_FCC][35] = 50, + [2][1][RTW89_ETSI][35] = 36, + [2][1][RTW89_MKK][35] = 56, + [2][1][RTW89_IC][35] = 50, + [2][1][RTW89_KCC][35] = 48, + [2][1][RTW89_ACMA][35] = 36, + [2][1][RTW89_CHILE][35] = 56, + [2][1][RTW89_UKRAINE][35] = 36, + [2][1][RTW89_MEXICO][35] = 50, + [2][1][RTW89_CN][35] = 127, + [2][1][RTW89_QATAR][35] = 36, + [2][1][RTW89_UK][35] = 36, + [2][1][RTW89_FCC][37] = 50, + [2][1][RTW89_ETSI][37] = 127, + [2][1][RTW89_MKK][37] = 54, + [2][1][RTW89_IC][37] = 50, + [2][1][RTW89_KCC][37] = 48, + [2][1][RTW89_ACMA][37] = 60, + [2][1][RTW89_CHILE][37] = 56, + [2][1][RTW89_UKRAINE][37] = 127, + [2][1][RTW89_MEXICO][37] = 50, + [2][1][RTW89_CN][37] = 127, + [2][1][RTW89_QATAR][37] = 127, + [2][1][RTW89_UK][37] = 66, + [2][1][RTW89_FCC][38] = 84, + [2][1][RTW89_ETSI][38] = 16, + [2][1][RTW89_MKK][38] = 127, + [2][1][RTW89_IC][38] = 84, + [2][1][RTW89_KCC][38] = 48, + [2][1][RTW89_ACMA][38] = 84, + [2][1][RTW89_CHILE][38] = 58, + [2][1][RTW89_UKRAINE][38] = 16, + [2][1][RTW89_MEXICO][38] = 84, + [2][1][RTW89_CN][38] = 64, + [2][1][RTW89_QATAR][38] = 16, + [2][1][RTW89_UK][38] = 38, + [2][1][RTW89_FCC][40] = 84, + [2][1][RTW89_ETSI][40] = 16, + [2][1][RTW89_MKK][40] = 127, + [2][1][RTW89_IC][40] = 84, + [2][1][RTW89_KCC][40] = 48, + [2][1][RTW89_ACMA][40] = 84, + [2][1][RTW89_CHILE][40] = 58, + [2][1][RTW89_UKRAINE][40] = 16, + [2][1][RTW89_MEXICO][40] = 84, + [2][1][RTW89_CN][40] = 64, + [2][1][RTW89_QATAR][40] = 16, + [2][1][RTW89_UK][40] = 38, + [2][1][RTW89_FCC][42] = 84, + [2][1][RTW89_ETSI][42] = 16, + [2][1][RTW89_MKK][42] = 127, + [2][1][RTW89_IC][42] = 84, + [2][1][RTW89_KCC][42] = 48, + [2][1][RTW89_ACMA][42] = 84, + [2][1][RTW89_CHILE][42] = 58, + [2][1][RTW89_UKRAINE][42] = 16, + [2][1][RTW89_MEXICO][42] = 84, + [2][1][RTW89_CN][42] = 64, + [2][1][RTW89_QATAR][42] = 16, + [2][1][RTW89_UK][42] = 38, + [2][1][RTW89_FCC][44] = 84, + [2][1][RTW89_ETSI][44] = 16, + [2][1][RTW89_MKK][44] = 127, + [2][1][RTW89_IC][44] = 84, + [2][1][RTW89_KCC][44] = 48, + [2][1][RTW89_ACMA][44] = 84, + [2][1][RTW89_CHILE][44] = 58, + [2][1][RTW89_UKRAINE][44] = 16, + [2][1][RTW89_MEXICO][44] = 84, + [2][1][RTW89_CN][44] = 64, + [2][1][RTW89_QATAR][44] = 16, + [2][1][RTW89_UK][44] = 38, + [2][1][RTW89_FCC][46] = 84, + [2][1][RTW89_ETSI][46] = 16, + [2][1][RTW89_MKK][46] = 127, + [2][1][RTW89_IC][46] = 84, + [2][1][RTW89_KCC][46] = 48, + [2][1][RTW89_ACMA][46] = 84, + [2][1][RTW89_CHILE][46] = 58, + [2][1][RTW89_UKRAINE][46] = 16, + [2][1][RTW89_MEXICO][46] = 84, + [2][1][RTW89_CN][46] = 64, + [2][1][RTW89_QATAR][46] = 16, + [2][1][RTW89_UK][46] = 38, + [2][1][RTW89_FCC][48] = 44, + [2][1][RTW89_ETSI][48] = 127, + [2][1][RTW89_MKK][48] = 127, + [2][1][RTW89_IC][48] = 127, + [2][1][RTW89_KCC][48] = 127, + [2][1][RTW89_ACMA][48] = 127, + [2][1][RTW89_CHILE][48] = 127, + [2][1][RTW89_UKRAINE][48] = 127, + [2][1][RTW89_MEXICO][48] = 127, + [2][1][RTW89_CN][48] = 127, + [2][1][RTW89_QATAR][48] = 127, + [2][1][RTW89_UK][48] = 127, + [2][1][RTW89_FCC][50] = 44, + [2][1][RTW89_ETSI][50] = 127, + [2][1][RTW89_MKK][50] = 127, + [2][1][RTW89_IC][50] = 127, + [2][1][RTW89_KCC][50] = 127, + [2][1][RTW89_ACMA][50] = 127, + [2][1][RTW89_CHILE][50] = 127, + [2][1][RTW89_UKRAINE][50] = 127, + [2][1][RTW89_MEXICO][50] = 127, + [2][1][RTW89_CN][50] = 127, + [2][1][RTW89_QATAR][50] = 127, + [2][1][RTW89_UK][50] = 127, + [2][1][RTW89_FCC][52] = 44, + [2][1][RTW89_ETSI][52] = 127, + [2][1][RTW89_MKK][52] = 127, + [2][1][RTW89_IC][52] = 127, + [2][1][RTW89_KCC][52] = 127, + [2][1][RTW89_ACMA][52] = 127, + [2][1][RTW89_CHILE][52] = 127, + [2][1][RTW89_UKRAINE][52] = 127, + [2][1][RTW89_MEXICO][52] = 127, + [2][1][RTW89_CN][52] = 127, + [2][1][RTW89_QATAR][52] = 127, + [2][1][RTW89_UK][52] = 127, +}; + +const struct rtw89_phy_table rtw89_8852b_phy_bb_table = { + .regs = rtw89_8852b_phy_bb_regs, + .n_regs = ARRAY_SIZE(rtw89_8852b_phy_bb_regs), + .rf_path = 0, /* don't care */ +}; + +const struct rtw89_phy_table rtw89_8852b_phy_bb_gain_table = { + .regs = rtw89_8852b_phy_bb_reg_gain, + .n_regs = ARRAY_SIZE(rtw89_8852b_phy_bb_reg_gain), + .rf_path = 0, /* don't care */ +}; + +const struct rtw89_phy_table rtw89_8852b_phy_radioa_table = { + .regs = rtw89_8852b_phy_radioa_regs, + .n_regs = ARRAY_SIZE(rtw89_8852b_phy_radioa_regs), + .rf_path = RF_PATH_A, + .config = rtw89_phy_config_rf_reg_v1, +}; + +const struct rtw89_phy_table rtw89_8852b_phy_radiob_table = { + .regs = rtw89_8852b_phy_radiob_regs, + .n_regs = ARRAY_SIZE(rtw89_8852b_phy_radiob_regs), + .rf_path = RF_PATH_B, + .config = rtw89_phy_config_rf_reg_v1, +}; + +const struct rtw89_phy_table rtw89_8852b_phy_nctl_table = { + .regs = rtw89_8852b_phy_nctl_regs, + .n_regs = ARRAY_SIZE(rtw89_8852b_phy_nctl_regs), + .rf_path = 0, /* don't care */ +}; + +const struct rtw89_txpwr_table rtw89_8852b_byr_table = { + .data = rtw89_8852b_txpwr_byrate, + .size = ARRAY_SIZE(rtw89_8852b_txpwr_byrate), + .load = rtw89_phy_load_txpwr_byrate, +}; + +const struct rtw89_txpwr_track_cfg rtw89_8852b_trk_cfg = { + .delta_swingidx_5gb_n = _txpwr_track_delta_swingidx_5gb_n, + .delta_swingidx_5gb_p = _txpwr_track_delta_swingidx_5gb_p, + .delta_swingidx_5ga_n = _txpwr_track_delta_swingidx_5ga_n, + .delta_swingidx_5ga_p = _txpwr_track_delta_swingidx_5ga_p, + .delta_swingidx_2gb_n = _txpwr_track_delta_swingidx_2gb_n, + .delta_swingidx_2gb_p = _txpwr_track_delta_swingidx_2gb_p, + .delta_swingidx_2ga_n = _txpwr_track_delta_swingidx_2ga_n, + .delta_swingidx_2ga_p = _txpwr_track_delta_swingidx_2ga_p, + .delta_swingidx_2g_cck_b_n = _txpwr_track_delta_swingidx_2g_cck_b_n, + .delta_swingidx_2g_cck_b_p = _txpwr_track_delta_swingidx_2g_cck_b_p, + .delta_swingidx_2g_cck_a_n = _txpwr_track_delta_swingidx_2g_cck_a_n, + .delta_swingidx_2g_cck_a_p = _txpwr_track_delta_swingidx_2g_cck_a_p, +}; From 2b379eb443e2a4bd6fb2cbd300e12aeff45cff57 Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Wed, 28 Sep 2022 16:43:30 +0800 Subject: [PATCH 0024/2002] wifi: rtw89: 8852b: add tables for RFK These tables are used by RFK to assist to configure PHY and RF registers. Signed-off-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20220928084336.34981-4-pkshih@realtek.com --- .../realtek/rtw89/rtw8852b_rfk_table.c | 794 ++++++++++++++++++ .../realtek/rtw89/rtw8852b_rfk_table.h | 62 ++ 2 files changed, 856 insertions(+) create mode 100644 drivers/net/wireless/realtek/rtw89/rtw8852b_rfk_table.c create mode 100644 drivers/net/wireless/realtek/rtw89/rtw8852b_rfk_table.h diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852b_rfk_table.c b/drivers/net/wireless/realtek/rtw89/rtw8852b_rfk_table.c new file mode 100644 index 000000000000..0b8a210bb10b --- /dev/null +++ b/drivers/net/wireless/realtek/rtw89/rtw8852b_rfk_table.c @@ -0,0 +1,794 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* Copyright(c) 2019-2020 Realtek Corporation + */ + +#include "rtw8852b_rfk_table.h" + +static const struct rtw89_reg5_def rtw8852b_afe_init_defs[] = { + RTW89_DECL_RFK_WM(0xC0D4, 0xffffffff, 0x4486888c), + RTW89_DECL_RFK_WM(0xC0D8, 0xffffffff, 0xc6ba10e0), + RTW89_DECL_RFK_WM(0xc0dc, 0xffffffff, 0x30c52868), + RTW89_DECL_RFK_WM(0xc0e0, 0xffffffff, 0x05008128), + RTW89_DECL_RFK_WM(0xc0e4, 0xffffffff, 0x0000272b), + RTW89_DECL_RFK_WM(0xC1D4, 0xffffffff, 0x4486888c), + RTW89_DECL_RFK_WM(0xC1D8, 0xffffffff, 0xc6ba10e0), + RTW89_DECL_RFK_WM(0xc1dc, 0xffffffff, 0x30c52868), + RTW89_DECL_RFK_WM(0xc1e0, 0xffffffff, 0x05008128), + RTW89_DECL_RFK_WM(0xc1e4, 0xffffffff, 0x0000272b), +}; + +RTW89_DECLARE_RFK_TBL(rtw8852b_afe_init_defs); + +static const struct rtw89_reg5_def rtw8852b_check_addc_defs_a[] = { + RTW89_DECL_RFK_WM(0x20f4, BIT(24), 0x0), + RTW89_DECL_RFK_WM(0x20f8, 0x80000000, 0x1), + RTW89_DECL_RFK_WM(0x20f0, 0xff0000, 0x1), + RTW89_DECL_RFK_WM(0x20f0, 0xf00, 0x2), + RTW89_DECL_RFK_WM(0x20f0, 0xf, 0x0), + RTW89_DECL_RFK_WM(0x20f0, 0xc0, 0x2), +}; + +RTW89_DECLARE_RFK_TBL(rtw8852b_check_addc_defs_a); + +static const struct rtw89_reg5_def rtw8852b_check_addc_defs_b[] = { + RTW89_DECL_RFK_WM(0x20f4, BIT(24), 0x0), + RTW89_DECL_RFK_WM(0x20f8, 0x80000000, 0x1), + RTW89_DECL_RFK_WM(0x20f0, 0xff0000, 0x1), + RTW89_DECL_RFK_WM(0x20f0, 0xf00, 0x2), + RTW89_DECL_RFK_WM(0x20f0, 0xf, 0x0), + RTW89_DECL_RFK_WM(0x20f0, 0xc0, 0x3), +}; + +RTW89_DECLARE_RFK_TBL(rtw8852b_check_addc_defs_b); + +static const struct rtw89_reg5_def rtw8852b_check_dadc_en_defs_a[] = { + RTW89_DECL_RFK_WM(0x032C, BIT(30), 0x0), + RTW89_DECL_RFK_WM(0x030C, 0x0f000000, 0xf), + RTW89_DECL_RFK_WM(0x030C, 0x0f000000, 0x3), + RTW89_DECL_RFK_WM(0x032C, BIT(16), 0x0), + RTW89_DECL_RFK_WM(0x12dc, BIT(0), 0x1), + RTW89_DECL_RFK_WM(0x12e8, BIT(2), 0x1), + RTW89_DECL_RFK_WRF(RF_PATH_A, 0x8f, BIT(13), 0x1), +}; + +RTW89_DECLARE_RFK_TBL(rtw8852b_check_dadc_en_defs_a); + +static const struct rtw89_reg5_def rtw8852b_check_dadc_en_defs_b[] = { + RTW89_DECL_RFK_WM(0x032C, BIT(30), 0x0), + RTW89_DECL_RFK_WM(0x030C, 0x0f000000, 0xf), + RTW89_DECL_RFK_WM(0x030C, 0x0f000000, 0x3), + RTW89_DECL_RFK_WM(0x032C, BIT(16), 0x0), + RTW89_DECL_RFK_WM(0x32dc, BIT(0), 0x1), + RTW89_DECL_RFK_WM(0x32e8, BIT(2), 0x1), + RTW89_DECL_RFK_WRF(RF_PATH_B, 0x8f, BIT(13), 0x1), +}; + +RTW89_DECLARE_RFK_TBL(rtw8852b_check_dadc_en_defs_b); + +static const struct rtw89_reg5_def rtw8852b_check_dadc_dis_defs_a[] = { + RTW89_DECL_RFK_WM(0x12dc, BIT(0), 0x0), + RTW89_DECL_RFK_WM(0x12e8, BIT(2), 0x0), + RTW89_DECL_RFK_WRF(RF_PATH_A, 0x8f, BIT(13), 0x0), + RTW89_DECL_RFK_WM(0x032C, BIT(16), 0x1), +}; + +RTW89_DECLARE_RFK_TBL(rtw8852b_check_dadc_dis_defs_a); + +static const struct rtw89_reg5_def rtw8852b_check_dadc_dis_defs_b[] = { + RTW89_DECL_RFK_WM(0x32dc, BIT(0), 0x0), + RTW89_DECL_RFK_WM(0x32e8, BIT(2), 0x0), + RTW89_DECL_RFK_WRF(RF_PATH_B, 0x8f, BIT(13), 0x0), + RTW89_DECL_RFK_WM(0x032C, BIT(16), 0x1), +}; + +RTW89_DECLARE_RFK_TBL(rtw8852b_check_dadc_dis_defs_b); + +static const struct rtw89_reg5_def rtw8852b_dack_s0_1_defs[] = { + RTW89_DECL_RFK_WM(0x12A0, BIT(15), 0x1), + RTW89_DECL_RFK_WM(0x12A0, 0x00007000, 0x3), + RTW89_DECL_RFK_WM(0x12B8, BIT(30), 0x1), + RTW89_DECL_RFK_WM(0x030C, BIT(28), 0x1), + RTW89_DECL_RFK_WM(0x032C, 0x80000000, 0x0), + RTW89_DECL_RFK_WM(0xC0D8, BIT(16), 0x1), + RTW89_DECL_RFK_WM(0xc0dc, 0x0c000000, 0x3), + RTW89_DECL_RFK_WM(0xC004, BIT(30), 0x0), + RTW89_DECL_RFK_WM(0xc024, BIT(30), 0x0), + RTW89_DECL_RFK_WM(0xC004, 0x3ff00000, 0x30), + RTW89_DECL_RFK_WM(0xC004, 0xc0000000, 0x0), + RTW89_DECL_RFK_WM(0xC004, BIT(17), 0x1), + RTW89_DECL_RFK_WM(0xc024, BIT(17), 0x1), + RTW89_DECL_RFK_WM(0xc00c, BIT(2), 0x0), + RTW89_DECL_RFK_WM(0xc02c, BIT(2), 0x0), + RTW89_DECL_RFK_WM(0xC004, BIT(0), 0x1), + RTW89_DECL_RFK_WM(0xc024, BIT(0), 0x1), + RTW89_DECL_RFK_DELAY(1), +}; + +RTW89_DECLARE_RFK_TBL(rtw8852b_dack_s0_1_defs); + +static const struct rtw89_reg5_def rtw8852b_dack_s0_2_defs[] = { + RTW89_DECL_RFK_WM(0xc0dc, 0x0c000000, 0x0), + RTW89_DECL_RFK_WM(0xc00c, BIT(2), 0x1), + RTW89_DECL_RFK_WM(0xc02c, BIT(2), 0x1), +}; + +RTW89_DECLARE_RFK_TBL(rtw8852b_dack_s0_2_defs); + +static const struct rtw89_reg5_def rtw8852b_dack_s0_3_defs[] = { + RTW89_DECL_RFK_WM(0xC004, BIT(0), 0x0), + RTW89_DECL_RFK_WM(0xc024, BIT(0), 0x0), + RTW89_DECL_RFK_WM(0xC0D8, BIT(16), 0x0), + RTW89_DECL_RFK_WM(0x12A0, BIT(15), 0x0), + RTW89_DECL_RFK_WM(0x12A0, 0x00007000, 0x7), +}; + +RTW89_DECLARE_RFK_TBL(rtw8852b_dack_s0_3_defs); + +static const struct rtw89_reg5_def rtw8852b_dack_s1_1_defs[] = { + RTW89_DECL_RFK_WM(0x32a0, BIT(15), 0x1), + RTW89_DECL_RFK_WM(0x32a0, 0x7000, 0x3), + RTW89_DECL_RFK_WM(0x32B8, BIT(30), 0x1), + RTW89_DECL_RFK_WM(0x030C, BIT(28), 0x1), + RTW89_DECL_RFK_WM(0x032C, 0x80000000, 0x0), + RTW89_DECL_RFK_WM(0xC1D8, BIT(16), 0x1), + RTW89_DECL_RFK_WM(0xc1dc, 0x0c000000, 0x3), + RTW89_DECL_RFK_WM(0xc104, BIT(30), 0x0), + RTW89_DECL_RFK_WM(0xc124, BIT(30), 0x0), + RTW89_DECL_RFK_WM(0xc104, 0x3ff00000, 0x30), + RTW89_DECL_RFK_WM(0xc104, 0xc0000000, 0x0), + RTW89_DECL_RFK_WM(0xc104, BIT(17), 0x1), + RTW89_DECL_RFK_WM(0xc124, BIT(17), 0x1), + RTW89_DECL_RFK_WM(0xc10c, BIT(2), 0x0), + RTW89_DECL_RFK_WM(0xc12c, BIT(2), 0x0), + RTW89_DECL_RFK_WM(0xc104, BIT(0), 0x1), + RTW89_DECL_RFK_WM(0xc124, BIT(0), 0x1), + RTW89_DECL_RFK_DELAY(1), +}; + +RTW89_DECLARE_RFK_TBL(rtw8852b_dack_s1_1_defs); + +static const struct rtw89_reg5_def rtw8852b_dack_s1_2_defs[] = { + RTW89_DECL_RFK_WM(0xc1dc, 0x0c000000, 0x0), + RTW89_DECL_RFK_WM(0xc10c, BIT(2), 0x1), + RTW89_DECL_RFK_WM(0xc12c, BIT(2), 0x1), + RTW89_DECL_RFK_DELAY(1), +}; + +RTW89_DECLARE_RFK_TBL(rtw8852b_dack_s1_2_defs); + +static const struct rtw89_reg5_def rtw8852b_dack_s1_3_defs[] = { + RTW89_DECL_RFK_WM(0xc104, BIT(0), 0x0), + RTW89_DECL_RFK_WM(0xc124, BIT(0), 0x0), + RTW89_DECL_RFK_WM(0xC1D8, BIT(16), 0x0), + RTW89_DECL_RFK_WM(0x32a0, BIT(15), 0x0), + RTW89_DECL_RFK_WM(0x32a0, 0x7000, 0x7), +}; + +RTW89_DECLARE_RFK_TBL(rtw8852b_dack_s1_3_defs); + +static const struct rtw89_reg5_def rtw8852b_dpk_afe_defs[] = { + RTW89_DECL_RFK_WM(0x20fc, 0xffff0000, 0x0303), + RTW89_DECL_RFK_WM(0x12b8, BIT(30), 0x1), + RTW89_DECL_RFK_WM(0x32b8, BIT(30), 0x1), + RTW89_DECL_RFK_WM(0x030c, 0xff000000, 0x13), + RTW89_DECL_RFK_WM(0x032c, 0xffff0000, 0x0041), + RTW89_DECL_RFK_WM(0x12b8, BIT(28), 0x1), + RTW89_DECL_RFK_WM(0x58c8, BIT(24), 0x1), + RTW89_DECL_RFK_WM(0x78c8, BIT(24), 0x1), + RTW89_DECL_RFK_WM(0x5864, 0xc0000000, 0x3), + RTW89_DECL_RFK_WM(0x7864, 0xc0000000, 0x3), + RTW89_DECL_RFK_WM(0x2008, 0x01FFFFFF, 0x1ffffff), + RTW89_DECL_RFK_WM(0x0c1c, BIT(2), 0x1), + RTW89_DECL_RFK_WM(0x0700, BIT(27), 0x1), + RTW89_DECL_RFK_WM(0x0c70, 0x000003FF, 0x3ff), + RTW89_DECL_RFK_WM(0x0c60, 0x00000003, 0x3), + RTW89_DECL_RFK_WM(0x0c6c, BIT(0), 0x1), + RTW89_DECL_RFK_WM(0x58ac, BIT(27), 0x1), + RTW89_DECL_RFK_WM(0x78ac, BIT(27), 0x1), + RTW89_DECL_RFK_WM(0x0c3c, BIT(9), 0x1), + RTW89_DECL_RFK_WM(0x2344, BIT(31), 0x1), + RTW89_DECL_RFK_WM(0x4490, BIT(31), 0x1), + RTW89_DECL_RFK_WM(0x12a0, 0x000ff000, 0xbf), + RTW89_DECL_RFK_WM(0x32a0, 0x000f0000, 0xb), + RTW89_DECL_RFK_WM(0x0700, 0x07000000, 0x5), + RTW89_DECL_RFK_WM(0x20fc, 0xffff0000, 0x3333), + RTW89_DECL_RFK_WM(0x580c, BIT(15), 0x1), + RTW89_DECL_RFK_WM(0x5800, 0x0000ffff, 0x0000), + RTW89_DECL_RFK_WM(0x780c, BIT(15), 0x1), + RTW89_DECL_RFK_WM(0x7800, 0x0000ffff, 0x0000), +}; + +RTW89_DECLARE_RFK_TBL(rtw8852b_dpk_afe_defs); + +static const struct rtw89_reg5_def rtw8852b_dpk_afe_restore_defs[] = { + RTW89_DECL_RFK_WM(0x20fc, 0xffff0000, 0x0303), + RTW89_DECL_RFK_WM(0x12b8, BIT(30), 0x0), + RTW89_DECL_RFK_WM(0x32b8, BIT(30), 0x0), + RTW89_DECL_RFK_WM(0x5864, 0xc0000000, 0x0), + RTW89_DECL_RFK_WM(0x7864, 0xc0000000, 0x0), + RTW89_DECL_RFK_WM(0x2008, 0x01FFFFFF, 0x0), + RTW89_DECL_RFK_WM(0x0c1c, BIT(2), 0x0), + RTW89_DECL_RFK_WM(0x0700, BIT(27), 0x0), + RTW89_DECL_RFK_WM(0x0c70, 0x000003FF, 0x63), + RTW89_DECL_RFK_WM(0x12a0, 0x000FF000, 0x00), + RTW89_DECL_RFK_WM(0x32a0, 0x000FF000, 0x00), + RTW89_DECL_RFK_WM(0x0700, 0x07000000, 0x0), + RTW89_DECL_RFK_WM(0x5864, BIT(29), 0x0), + RTW89_DECL_RFK_WM(0x7864, BIT(29), 0x0), + RTW89_DECL_RFK_WM(0x20fc, 0xffff0000, 0x0000), + RTW89_DECL_RFK_WM(0x58c8, BIT(24), 0x0), + RTW89_DECL_RFK_WM(0x78c8, BIT(24), 0x0), + RTW89_DECL_RFK_WM(0x0c3c, BIT(9), 0x0), + RTW89_DECL_RFK_WM(0x580c, BIT(15), 0x0), + RTW89_DECL_RFK_WM(0x58e4, 0x18000000, 0x1), + RTW89_DECL_RFK_WM(0x58e4, 0x18000000, 0x2), + RTW89_DECL_RFK_WM(0x780c, BIT(15), 0x0), + RTW89_DECL_RFK_WM(0x78e4, 0x18000000, 0x1), + RTW89_DECL_RFK_WM(0x78e4, 0x18000000, 0x2), +}; + +RTW89_DECLARE_RFK_TBL(rtw8852b_dpk_afe_restore_defs); + +static const struct rtw89_reg5_def rtw8852b_dpk_kip_defs[] = { + RTW89_DECL_RFK_WM(0x8008, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x8088, 0xffffffff, 0x80000000), +}; + +RTW89_DECLARE_RFK_TBL(rtw8852b_dpk_kip_defs); + +static const struct rtw89_reg5_def rtw8852b_tssi_sys_defs[] = { + RTW89_DECL_RFK_WM(0x12a8, 0x0000000f, 0x5), + RTW89_DECL_RFK_WM(0x32a8, 0x0000000f, 0x5), + RTW89_DECL_RFK_WM(0x12bc, 0x000ffff0, 0x5555), + RTW89_DECL_RFK_WM(0x32bc, 0x000ffff0, 0x5555), + RTW89_DECL_RFK_WM(0x0300, 0xff000000, 0x16), + RTW89_DECL_RFK_WM(0x0304, 0x000000ff, 0x19), + RTW89_DECL_RFK_WM(0x0314, 0xffff0000, 0x2041), + RTW89_DECL_RFK_WM(0x0318, 0xffffffff, 0x2041), + RTW89_DECL_RFK_WM(0x0318, 0xffffffff, 0x20012041), + RTW89_DECL_RFK_WM(0x0020, 0x00006000, 0x3), + RTW89_DECL_RFK_WM(0x0024, 0x00006000, 0x3), + RTW89_DECL_RFK_WM(0x0704, 0xffff0000, 0x601e), + RTW89_DECL_RFK_WM(0x2704, 0xffff0000, 0x601e), + RTW89_DECL_RFK_WM(0x0700, 0xf0000000, 0x4), + RTW89_DECL_RFK_WM(0x2700, 0xf0000000, 0x4), + RTW89_DECL_RFK_WM(0x0650, 0x3c000000, 0x0), + RTW89_DECL_RFK_WM(0x2650, 0x3c000000, 0x0), +}; + +RTW89_DECLARE_RFK_TBL(rtw8852b_tssi_sys_defs); + +static const struct rtw89_reg5_def rtw8852b_tssi_sys_a_defs_2g[] = { + RTW89_DECL_RFK_WM(0x120c, 0x000000ff, 0x33), + RTW89_DECL_RFK_WM(0x12c0, 0x0ff00000, 0x33), + RTW89_DECL_RFK_WM(0x58f8, 0x40000000, 0x1), + RTW89_DECL_RFK_WM(0x0304, 0x0000ff00, 0x1e), +}; + +RTW89_DECLARE_RFK_TBL(rtw8852b_tssi_sys_a_defs_2g); + +static const struct rtw89_reg5_def rtw8852b_tssi_sys_a_defs_5g[] = { + RTW89_DECL_RFK_WM(0x120c, 0x000000ff, 0x44), + RTW89_DECL_RFK_WM(0x12c0, 0x0ff00000, 0x44), + RTW89_DECL_RFK_WM(0x58f8, 0x40000000, 0x0), + RTW89_DECL_RFK_WM(0x0304, 0x0000ff00, 0x1d), +}; + +RTW89_DECLARE_RFK_TBL(rtw8852b_tssi_sys_a_defs_5g); + +static const struct rtw89_reg5_def rtw8852b_tssi_sys_b_defs_2g[] = { + RTW89_DECL_RFK_WM(0x32c0, 0x0ff00000, 0x33), + RTW89_DECL_RFK_WM(0x320c, 0x000000ff, 0x33), + RTW89_DECL_RFK_WM(0x78f8, 0x40000000, 0x1), + RTW89_DECL_RFK_WM(0x0304, 0x0000ff00, 0x1e), +}; + +RTW89_DECLARE_RFK_TBL(rtw8852b_tssi_sys_b_defs_2g); + +static const struct rtw89_reg5_def rtw8852b_tssi_sys_b_defs_5g[] = { + RTW89_DECL_RFK_WM(0x32c0, 0x0ff00000, 0x44), + RTW89_DECL_RFK_WM(0x320c, 0x000000ff, 0x44), + RTW89_DECL_RFK_WM(0x78f8, 0x40000000, 0x0), + RTW89_DECL_RFK_WM(0x0304, 0x0000ff00, 0x1d), +}; + +RTW89_DECLARE_RFK_TBL(rtw8852b_tssi_sys_b_defs_5g); + +static const struct rtw89_reg5_def rtw8852b_tssi_init_txpwr_defs_a[] = { + RTW89_DECL_RFK_WM(0x566c, 0x00001000, 0x0), + RTW89_DECL_RFK_WM(0x5800, 0xffffffff, 0x003f807f), + RTW89_DECL_RFK_WM(0x580c, 0x0000007f, 0x40), + RTW89_DECL_RFK_WM(0x580c, 0x0fffff00, 0x00040), + RTW89_DECL_RFK_WM(0x5810, 0xffffffff, 0x59010000), + RTW89_DECL_RFK_WM(0x5814, 0x01ffffff, 0x002d000), + RTW89_DECL_RFK_WM(0x5814, 0xf8000000, 0x00), + RTW89_DECL_RFK_WM(0x5818, 0xffffffff, 0x002c1800), + RTW89_DECL_RFK_WM(0x581c, 0x3fffffff, 0x1dc80280), + RTW89_DECL_RFK_WM(0x5820, 0xffffffff, 0x00002080), + RTW89_DECL_RFK_WM(0x580c, 0x10000000, 0x1), + RTW89_DECL_RFK_WM(0x580c, 0x40000000, 0x1), + RTW89_DECL_RFK_WM(0x5834, 0x3fffffff, 0x000115f2), + RTW89_DECL_RFK_WM(0x5838, 0x7fffffff, 0x0000121), + RTW89_DECL_RFK_WM(0x5854, 0x3fffffff, 0x000115f2), + RTW89_DECL_RFK_WM(0x5858, 0x7fffffff, 0x0000121), + RTW89_DECL_RFK_WM(0x5860, 0x80000000, 0x0), + RTW89_DECL_RFK_WM(0x5864, 0x07ffffff, 0x00801ff), + RTW89_DECL_RFK_WM(0x5898, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x589c, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x58a4, 0x000000ff, 0x16), + RTW89_DECL_RFK_WM(0x58b0, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x58b4, 0x7fffffff, 0x0a002000), + RTW89_DECL_RFK_WM(0x58b8, 0x7fffffff, 0x00007628), + RTW89_DECL_RFK_WM(0x58bc, 0x07ffffff, 0x7a7807f), + RTW89_DECL_RFK_WM(0x58c0, 0xfffe0000, 0x003f), + RTW89_DECL_RFK_WM(0x58c4, 0xffffffff, 0x0003ffff), + RTW89_DECL_RFK_WM(0x58c8, 0x00ffffff, 0x000000), + RTW89_DECL_RFK_WM(0x58c8, 0xf0000000, 0x0), + RTW89_DECL_RFK_WM(0x58cc, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x58d0, 0x07ffffff, 0x2008101), + RTW89_DECL_RFK_WM(0x58d4, 0x000000ff, 0x00), + RTW89_DECL_RFK_WM(0x58d4, 0x0003fe00, 0x0ff), + RTW89_DECL_RFK_WM(0x58d4, 0x07fc0000, 0x100), + RTW89_DECL_RFK_WM(0x58d8, 0xffffffff, 0x8008016c), + RTW89_DECL_RFK_WM(0x58dc, 0x0001ffff, 0x0807f), + RTW89_DECL_RFK_WM(0x58dc, 0xfff00000, 0x800), + RTW89_DECL_RFK_WM(0x58f0, 0x0003ffff, 0x001ff), + RTW89_DECL_RFK_WM(0x58f4, 0x000fffff, 0x000), +}; + +RTW89_DECLARE_RFK_TBL(rtw8852b_tssi_init_txpwr_defs_a); + +static const struct rtw89_reg5_def rtw8852b_tssi_init_txpwr_defs_b[] = { + RTW89_DECL_RFK_WM(0x566c, 0x00001000, 0x0), + RTW89_DECL_RFK_WM(0x7800, 0xffffffff, 0x003f807f), + RTW89_DECL_RFK_WM(0x780c, 0x0000007f, 0x40), + RTW89_DECL_RFK_WM(0x780c, 0x0fffff00, 0x00040), + RTW89_DECL_RFK_WM(0x7810, 0xffffffff, 0x59010000), + RTW89_DECL_RFK_WM(0x7814, 0x01ffffff, 0x002d000), + RTW89_DECL_RFK_WM(0x7814, 0xf8000000, 0x00), + RTW89_DECL_RFK_WM(0x7818, 0xffffffff, 0x002c1800), + RTW89_DECL_RFK_WM(0x781c, 0x3fffffff, 0x1dc80280), + RTW89_DECL_RFK_WM(0x7820, 0xffffffff, 0x00002080), + RTW89_DECL_RFK_WM(0x780c, 0x10000000, 0x1), + RTW89_DECL_RFK_WM(0x780c, 0x40000000, 0x1), + RTW89_DECL_RFK_WM(0x7834, 0x3fffffff, 0x000115f2), + RTW89_DECL_RFK_WM(0x7838, 0x7fffffff, 0x0000121), + RTW89_DECL_RFK_WM(0x7854, 0x3fffffff, 0x000115f2), + RTW89_DECL_RFK_WM(0x7858, 0x7fffffff, 0x0000121), + RTW89_DECL_RFK_WM(0x7860, 0x80000000, 0x0), + RTW89_DECL_RFK_WM(0x7864, 0x07ffffff, 0x00801ff), + RTW89_DECL_RFK_WM(0x7898, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x789c, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x78a4, 0x000000ff, 0x16), + RTW89_DECL_RFK_WM(0x78b0, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x78b4, 0x7fffffff, 0x0a002000), + RTW89_DECL_RFK_WM(0x78b8, 0x7fffffff, 0x00007628), + RTW89_DECL_RFK_WM(0x78bc, 0x07ffffff, 0x7a7807f), + RTW89_DECL_RFK_WM(0x78c0, 0xfffe0000, 0x003f), + RTW89_DECL_RFK_WM(0x78c4, 0xffffffff, 0x0003ffff), + RTW89_DECL_RFK_WM(0x78c8, 0x00ffffff, 0x000000), + RTW89_DECL_RFK_WM(0x78c8, 0xf0000000, 0x0), + RTW89_DECL_RFK_WM(0x78cc, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x78d0, 0x07ffffff, 0x2008101), + RTW89_DECL_RFK_WM(0x78d4, 0x000000ff, 0x00), + RTW89_DECL_RFK_WM(0x78d4, 0x0003fe00, 0x0ff), + RTW89_DECL_RFK_WM(0x78d4, 0x07fc0000, 0x100), + RTW89_DECL_RFK_WM(0x78d8, 0xffffffff, 0x8008016c), + RTW89_DECL_RFK_WM(0x78dc, 0x0001ffff, 0x0807f), + RTW89_DECL_RFK_WM(0x78dc, 0xfff00000, 0x800), + RTW89_DECL_RFK_WM(0x78f0, 0x0003ffff, 0x001ff), + RTW89_DECL_RFK_WM(0x78f4, 0x000fffff, 0x000), +}; + +RTW89_DECLARE_RFK_TBL(rtw8852b_tssi_init_txpwr_defs_b); + +static const struct rtw89_reg5_def rtw8852b_tssi_init_txpwr_he_tb_defs_a[] = { + RTW89_DECL_RFK_WM(0x58a0, 0xffffffff, 0x000000fe), + RTW89_DECL_RFK_WM(0x58e4, 0x0000007f, 0x1f), +}; + +RTW89_DECLARE_RFK_TBL(rtw8852b_tssi_init_txpwr_he_tb_defs_a); + +static const struct rtw89_reg5_def rtw8852b_tssi_init_txpwr_he_tb_defs_b[] = { + RTW89_DECL_RFK_WM(0x78a0, 0xffffffff, 0x000000fe), + RTW89_DECL_RFK_WM(0x78e4, 0x0000007f, 0x1f), +}; + +RTW89_DECLARE_RFK_TBL(rtw8852b_tssi_init_txpwr_he_tb_defs_b); + +static const struct rtw89_reg5_def rtw8852b_tssi_dck_defs_a[] = { + RTW89_DECL_RFK_WM(0x580c, 0x0fff0000, 0x000), + RTW89_DECL_RFK_WM(0x5814, 0x003ff000, 0x0ef), + RTW89_DECL_RFK_WM(0x5814, 0x18000000, 0x0), +}; + +RTW89_DECLARE_RFK_TBL(rtw8852b_tssi_dck_defs_a); + +static const struct rtw89_reg5_def rtw8852b_tssi_dck_defs_b[] = { + RTW89_DECL_RFK_WM(0x780c, 0x0fff0000, 0x000), + RTW89_DECL_RFK_WM(0x7814, 0x003ff000, 0x0ef), + RTW89_DECL_RFK_WM(0x7814, 0x18000000, 0x0), +}; + +RTW89_DECLARE_RFK_TBL(rtw8852b_tssi_dck_defs_b); + +static const struct rtw89_reg5_def rtw8852b_tssi_dac_gain_defs_a[] = { + RTW89_DECL_RFK_WM(0x58b0, 0x00000400, 0x1), + RTW89_DECL_RFK_WM(0x58b0, 0x00000fff, 0x000), + RTW89_DECL_RFK_WM(0x58b0, 0x00000800, 0x1), + RTW89_DECL_RFK_WM(0x5a00, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x5a04, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x5a08, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x5a0c, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x5a10, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x5a14, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x5a18, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x5a1c, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x5a20, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x5a24, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x5a28, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x5a2c, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x5a30, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x5a34, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x5a38, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x5a3c, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x5a40, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x5a44, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x5a48, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x5a4c, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x5a50, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x5a54, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x5a58, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x5a5c, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x5a60, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x5a64, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x5a68, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x5a6c, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x5a70, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x5a74, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x5a78, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x5a7c, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x5a80, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x5a84, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x5a88, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x5a8c, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x5a90, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x5a94, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x5a98, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x5a9c, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x5aa0, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x5aa4, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x5aa8, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x5aac, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x5ab0, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x5ab4, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x5ab8, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x5abc, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x5ac0, 0xffffffff, 0x00000000), +}; + +RTW89_DECLARE_RFK_TBL(rtw8852b_tssi_dac_gain_defs_a); + +static const struct rtw89_reg5_def rtw8852b_tssi_dac_gain_defs_b[] = { + RTW89_DECL_RFK_WM(0x78b0, 0x00000fff, 0x000), + RTW89_DECL_RFK_WM(0x78b0, 0x00000800, 0x1), + RTW89_DECL_RFK_WM(0x7a00, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x7a04, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x7a08, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x7a0c, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x7a10, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x7a14, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x7a18, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x7a1c, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x7a20, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x7a24, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x7a28, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x7a2c, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x7a30, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x7a34, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x7a38, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x7a3c, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x7a40, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x7a44, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x7a48, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x7a4c, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x7a50, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x7a54, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x7a58, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x7a5c, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x7a60, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x7a64, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x7a68, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x7a6c, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x7a70, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x7a74, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x7a78, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x7a7c, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x7a80, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x7a84, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x7a88, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x7a8c, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x7a90, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x7a94, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x7a98, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x7a9c, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x7aa0, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x7aa4, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x7aa8, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x7aac, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x7ab0, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x7ab4, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x7ab8, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x7abc, 0xffffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x7ac0, 0xffffffff, 0x00000000), +}; + +RTW89_DECLARE_RFK_TBL(rtw8852b_tssi_dac_gain_defs_b); + +static const struct rtw89_reg5_def rtw8852b_tssi_slope_a_defs_2g[] = { + RTW89_DECL_RFK_WM(0x5608, 0x07ffffff, 0x0801008), + RTW89_DECL_RFK_WM(0x560c, 0x07ffffff, 0x0201020), + RTW89_DECL_RFK_WM(0x5610, 0x07ffffff, 0x0201008), + RTW89_DECL_RFK_WM(0x5614, 0x07ffffff, 0x0804008), + RTW89_DECL_RFK_WM(0x5618, 0x07ffffff, 0x0201008), + RTW89_DECL_RFK_WM(0x561c, 0x000001ff, 0x008), + RTW89_DECL_RFK_WM(0x561c, 0xffff0000, 0x0808), + RTW89_DECL_RFK_WM(0x5620, 0xffffffff, 0x08081e28), + RTW89_DECL_RFK_WM(0x5624, 0xffffffff, 0x08080808), + RTW89_DECL_RFK_WM(0x5628, 0xffffffff, 0x08081e28), + RTW89_DECL_RFK_WM(0x562c, 0x0000ffff, 0x0808), + RTW89_DECL_RFK_WM(0x581c, 0x00100000, 0x1), +}; + +RTW89_DECLARE_RFK_TBL(rtw8852b_tssi_slope_a_defs_2g); + +static const struct rtw89_reg5_def rtw8852b_tssi_slope_a_defs_5g[] = { + RTW89_DECL_RFK_WM(0x5608, 0x07ffffff, 0x0201008), + RTW89_DECL_RFK_WM(0x560c, 0x07ffffff, 0x0201020), + RTW89_DECL_RFK_WM(0x5610, 0x07ffffff, 0x0201008), + RTW89_DECL_RFK_WM(0x5614, 0x07ffffff, 0x0201008), + RTW89_DECL_RFK_WM(0x5618, 0x07ffffff, 0x0201008), + RTW89_DECL_RFK_WM(0x561c, 0x000001ff, 0x008), + RTW89_DECL_RFK_WM(0x561c, 0xffff0000, 0x0808), + RTW89_DECL_RFK_WM(0x5620, 0xffffffff, 0x08081e08), + RTW89_DECL_RFK_WM(0x5624, 0xffffffff, 0x08080808), + RTW89_DECL_RFK_WM(0x5628, 0xffffffff, 0x08080808), + RTW89_DECL_RFK_WM(0x562c, 0x0000ffff, 0x0808), + RTW89_DECL_RFK_WM(0x581c, 0x00100000, 0x1), +}; + +RTW89_DECLARE_RFK_TBL(rtw8852b_tssi_slope_a_defs_5g); + +static const struct rtw89_reg5_def rtw8852b_tssi_slope_b_defs_2g[] = { + RTW89_DECL_RFK_WM(0x7608, 0x07ffffff, 0x0801008), + RTW89_DECL_RFK_WM(0x760c, 0x07ffffff, 0x0201020), + RTW89_DECL_RFK_WM(0x7610, 0x07ffffff, 0x0201008), + RTW89_DECL_RFK_WM(0x7614, 0x07ffffff, 0x0804008), + RTW89_DECL_RFK_WM(0x7618, 0x07ffffff, 0x0201008), + RTW89_DECL_RFK_WM(0x761c, 0x000001ff, 0x008), + RTW89_DECL_RFK_WM(0x761c, 0xffff0000, 0x0808), + RTW89_DECL_RFK_WM(0x7620, 0xffffffff, 0x08081e28), + RTW89_DECL_RFK_WM(0x7624, 0xffffffff, 0x08080808), + RTW89_DECL_RFK_WM(0x7628, 0xffffffff, 0x08081e28), + RTW89_DECL_RFK_WM(0x762c, 0x0000ffff, 0x0808), + RTW89_DECL_RFK_WM(0x781c, 0x00100000, 0x1), +}; + +RTW89_DECLARE_RFK_TBL(rtw8852b_tssi_slope_b_defs_2g); + +static const struct rtw89_reg5_def rtw8852b_tssi_slope_b_defs_5g[] = { + RTW89_DECL_RFK_WM(0x7608, 0x07ffffff, 0x0201008), + RTW89_DECL_RFK_WM(0x760c, 0x07ffffff, 0x0201020), + RTW89_DECL_RFK_WM(0x7610, 0x07ffffff, 0x0201008), + RTW89_DECL_RFK_WM(0x7614, 0x07ffffff, 0x0201008), + RTW89_DECL_RFK_WM(0x7618, 0x07ffffff, 0x0201008), + RTW89_DECL_RFK_WM(0x761c, 0x000001ff, 0x008), + RTW89_DECL_RFK_WM(0x761c, 0xffff0000, 0x0808), + RTW89_DECL_RFK_WM(0x7620, 0xffffffff, 0x08081e08), + RTW89_DECL_RFK_WM(0x7624, 0xffffffff, 0x08080808), + RTW89_DECL_RFK_WM(0x7628, 0xffffffff, 0x08080808), + RTW89_DECL_RFK_WM(0x762c, 0x0000ffff, 0x0808), + RTW89_DECL_RFK_WM(0x781c, 0x00100000, 0x1), +}; + +RTW89_DECLARE_RFK_TBL(rtw8852b_tssi_slope_b_defs_5g); + +static const struct rtw89_reg5_def rtw8852b_tssi_align_a_2g_all_defs[] = { + RTW89_DECL_RFK_WM(0x5604, 0x80000000, 0x1), + RTW89_DECL_RFK_WM(0x5600, 0x3fffffff, 0x3f2d2721), + RTW89_DECL_RFK_WM(0x5604, 0x003fffff, 0x010101), + RTW89_DECL_RFK_WM(0x5630, 0x3fffffff, 0x01ef27af), + RTW89_DECL_RFK_WM(0x5634, 0x3fffffff, 0x00000075), + RTW89_DECL_RFK_WM(0x5638, 0x000fffff, 0x00000), + RTW89_DECL_RFK_WM(0x563c, 0x3fffffff, 0x017f13ae), + RTW89_DECL_RFK_WM(0x5640, 0x3fffffff, 0x0000006e), + RTW89_DECL_RFK_WM(0x5644, 0x000fffff, 0x00000), +}; + +RTW89_DECLARE_RFK_TBL(rtw8852b_tssi_align_a_2g_all_defs); + +static const struct rtw89_reg5_def rtw8852b_tssi_align_a_2g_part_defs[] = { + RTW89_DECL_RFK_WM(0x5630, 0x3fffffff, 0x01ef27af), + RTW89_DECL_RFK_WM(0x5634, 0x3fffffff, 0x00000075), + RTW89_DECL_RFK_WM(0x563c, 0x3fffffff, 0x017f13ae), + RTW89_DECL_RFK_WM(0x5640, 0x3fffffff, 0x0000006e), +}; + +RTW89_DECLARE_RFK_TBL(rtw8852b_tssi_align_a_2g_part_defs); + +static const struct rtw89_reg5_def rtw8852b_tssi_align_a_5g1_all_defs[] = { + RTW89_DECL_RFK_WM(0x5604, 0x80000000, 0x1), + RTW89_DECL_RFK_WM(0x5600, 0x3fffffff, 0x3f2d2721), + RTW89_DECL_RFK_WM(0x5604, 0x003fffff, 0x010101), + RTW89_DECL_RFK_WM(0x5630, 0x3fffffff, 0x016037e7), + RTW89_DECL_RFK_WM(0x5634, 0x3fffffff, 0x0000006f), + RTW89_DECL_RFK_WM(0x5638, 0x000fffff, 0x00000), + RTW89_DECL_RFK_WM(0x563c, 0x3fffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x5640, 0x3fffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x5644, 0x000fffff, 0x00000), +}; + +RTW89_DECLARE_RFK_TBL(rtw8852b_tssi_align_a_5g1_all_defs); + +static const struct rtw89_reg5_def rtw8852b_tssi_align_a_5g1_part_defs[] = { + RTW89_DECL_RFK_WM(0x5630, 0x3fffffff, 0x016037e7), + RTW89_DECL_RFK_WM(0x5634, 0x3fffffff, 0x0000006f), + RTW89_DECL_RFK_WM(0x563c, 0x3fffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x5640, 0x3fffffff, 0x00000000), +}; + +RTW89_DECLARE_RFK_TBL(rtw8852b_tssi_align_a_5g1_part_defs); + +static const struct rtw89_reg5_def rtw8852b_tssi_align_a_5g2_all_defs[] = { + RTW89_DECL_RFK_WM(0x5604, 0x80000000, 0x1), + RTW89_DECL_RFK_WM(0x5600, 0x3fffffff, 0x3f2d2721), + RTW89_DECL_RFK_WM(0x5604, 0x003fffff, 0x010101), + RTW89_DECL_RFK_WM(0x5630, 0x3fffffff, 0x01f053f1), + RTW89_DECL_RFK_WM(0x5634, 0x3fffffff, 0x00000070), + RTW89_DECL_RFK_WM(0x5638, 0x000fffff, 0x00000), + RTW89_DECL_RFK_WM(0x563c, 0x3fffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x5640, 0x3fffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x5644, 0x000fffff, 0x00000), +}; + +RTW89_DECLARE_RFK_TBL(rtw8852b_tssi_align_a_5g2_all_defs); + +static const struct rtw89_reg5_def rtw8852b_tssi_align_a_5g2_part_defs[] = { + RTW89_DECL_RFK_WM(0x5630, 0x3fffffff, 0x01f053f1), + RTW89_DECL_RFK_WM(0x5634, 0x3fffffff, 0x00000070), + RTW89_DECL_RFK_WM(0x563c, 0x3fffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x5640, 0x3fffffff, 0x00000000), +}; + +RTW89_DECLARE_RFK_TBL(rtw8852b_tssi_align_a_5g2_part_defs); + +static const struct rtw89_reg5_def rtw8852b_tssi_align_a_5g3_all_defs[] = { + RTW89_DECL_RFK_WM(0x5604, 0x80000000, 0x1), + RTW89_DECL_RFK_WM(0x5600, 0x3fffffff, 0x3f2d2721), + RTW89_DECL_RFK_WM(0x5604, 0x003fffff, 0x010101), + RTW89_DECL_RFK_WM(0x5630, 0x3fffffff, 0x01c047ee), + RTW89_DECL_RFK_WM(0x5634, 0x3fffffff, 0x00000070), + RTW89_DECL_RFK_WM(0x5638, 0x000fffff, 0x00000), + RTW89_DECL_RFK_WM(0x563c, 0x3fffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x5640, 0x3fffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x5644, 0x000fffff, 0x00000), +}; + +RTW89_DECLARE_RFK_TBL(rtw8852b_tssi_align_a_5g3_all_defs); + +static const struct rtw89_reg5_def rtw8852b_tssi_align_a_5g3_part_defs[] = { + RTW89_DECL_RFK_WM(0x5630, 0x3fffffff, 0x01c047ee), + RTW89_DECL_RFK_WM(0x5634, 0x3fffffff, 0x00000070), + RTW89_DECL_RFK_WM(0x563c, 0x3fffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x5640, 0x3fffffff, 0x00000000), +}; + +RTW89_DECLARE_RFK_TBL(rtw8852b_tssi_align_a_5g3_part_defs); + +static const struct rtw89_reg5_def rtw8852b_tssi_align_b_2g_all_defs[] = { + RTW89_DECL_RFK_WM(0x7604, 0x80000000, 0x1), + RTW89_DECL_RFK_WM(0x7600, 0x3fffffff, 0x3f2d2721), + RTW89_DECL_RFK_WM(0x7604, 0x003fffff, 0x010101), + RTW89_DECL_RFK_WM(0x7630, 0x3fffffff, 0x01ff2bb5), + RTW89_DECL_RFK_WM(0x7634, 0x3fffffff, 0x00000078), + RTW89_DECL_RFK_WM(0x7638, 0x000fffff, 0x00000), + RTW89_DECL_RFK_WM(0x763c, 0x3fffffff, 0x018f2bb0), + RTW89_DECL_RFK_WM(0x7640, 0x3fffffff, 0x00000072), + RTW89_DECL_RFK_WM(0x7644, 0x000fffff, 0x00000), +}; + +RTW89_DECLARE_RFK_TBL(rtw8852b_tssi_align_b_2g_all_defs); + +static const struct rtw89_reg5_def rtw8852b_tssi_align_b_2g_part_defs[] = { + RTW89_DECL_RFK_WM(0x7630, 0x3fffffff, 0x01ff2bb5), + RTW89_DECL_RFK_WM(0x7634, 0x3fffffff, 0x00000078), + RTW89_DECL_RFK_WM(0x763c, 0x3fffffff, 0x018f2bb0), + RTW89_DECL_RFK_WM(0x7640, 0x3fffffff, 0x00000072), +}; + +RTW89_DECLARE_RFK_TBL(rtw8852b_tssi_align_b_2g_part_defs); + +static const struct rtw89_reg5_def rtw8852b_tssi_align_b_5g1_all_defs[] = { + RTW89_DECL_RFK_WM(0x7604, 0x80000000, 0x1), + RTW89_DECL_RFK_WM(0x7600, 0x3fffffff, 0x3f2d2721), + RTW89_DECL_RFK_WM(0x7604, 0x003fffff, 0x010101), + RTW89_DECL_RFK_WM(0x7630, 0x3fffffff, 0x009003da), + RTW89_DECL_RFK_WM(0x7634, 0x3fffffff, 0x00000069), + RTW89_DECL_RFK_WM(0x7638, 0x000fffff, 0x00000), + RTW89_DECL_RFK_WM(0x763c, 0x3fffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x7640, 0x3fffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x7644, 0x000fffff, 0x00000), +}; + +RTW89_DECLARE_RFK_TBL(rtw8852b_tssi_align_b_5g1_all_defs); + +static const struct rtw89_reg5_def rtw8852b_tssi_align_b_5g1_part_defs[] = { + RTW89_DECL_RFK_WM(0x7630, 0x3fffffff, 0x009003da), + RTW89_DECL_RFK_WM(0x7634, 0x3fffffff, 0x00000069), + RTW89_DECL_RFK_WM(0x763c, 0x3fffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x7640, 0x3fffffff, 0x00000000), +}; + +RTW89_DECLARE_RFK_TBL(rtw8852b_tssi_align_b_5g1_part_defs); + +static const struct rtw89_reg5_def rtw8852b_tssi_align_b_5g2_all_defs[] = { + RTW89_DECL_RFK_WM(0x7604, 0x80000000, 0x1), + RTW89_DECL_RFK_WM(0x7600, 0x3fffffff, 0x3f2d2721), + RTW89_DECL_RFK_WM(0x7604, 0x003fffff, 0x010101), + RTW89_DECL_RFK_WM(0x7630, 0x3fffffff, 0x013027e6), + RTW89_DECL_RFK_WM(0x7634, 0x3fffffff, 0x00000069), + RTW89_DECL_RFK_WM(0x7638, 0x000fffff, 0x00000), + RTW89_DECL_RFK_WM(0x763c, 0x3fffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x7640, 0x3fffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x7644, 0x000fffff, 0x00000), +}; + +RTW89_DECLARE_RFK_TBL(rtw8852b_tssi_align_b_5g2_all_defs); + +static const struct rtw89_reg5_def rtw8852b_tssi_align_b_5g2_part_defs[] = { + RTW89_DECL_RFK_WM(0x7630, 0x3fffffff, 0x013027e6), + RTW89_DECL_RFK_WM(0x7634, 0x3fffffff, 0x00000069), + RTW89_DECL_RFK_WM(0x763c, 0x3fffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x7640, 0x3fffffff, 0x00000000), +}; + +RTW89_DECLARE_RFK_TBL(rtw8852b_tssi_align_b_5g2_part_defs); + +static const struct rtw89_reg5_def rtw8852b_tssi_align_b_5g3_all_defs[] = { + RTW89_DECL_RFK_WM(0x7604, 0x80000000, 0x1), + RTW89_DECL_RFK_WM(0x7600, 0x3fffffff, 0x3f2d2721), + RTW89_DECL_RFK_WM(0x7604, 0x003fffff, 0x010101), + RTW89_DECL_RFK_WM(0x7630, 0x3fffffff, 0x009003da), + RTW89_DECL_RFK_WM(0x7634, 0x3fffffff, 0x00000069), + RTW89_DECL_RFK_WM(0x7638, 0x000fffff, 0x00000), + RTW89_DECL_RFK_WM(0x763c, 0x3fffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x7640, 0x3fffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x7644, 0x000fffff, 0x00000), +}; + +RTW89_DECLARE_RFK_TBL(rtw8852b_tssi_align_b_5g3_all_defs); + +static const struct rtw89_reg5_def rtw8852b_tssi_align_b_5g3_part_defs[] = { + RTW89_DECL_RFK_WM(0x7630, 0x3fffffff, 0x009003da), + RTW89_DECL_RFK_WM(0x7634, 0x3fffffff, 0x00000069), + RTW89_DECL_RFK_WM(0x763c, 0x3fffffff, 0x00000000), + RTW89_DECL_RFK_WM(0x7640, 0x3fffffff, 0x00000000), +}; + +RTW89_DECLARE_RFK_TBL(rtw8852b_tssi_align_b_5g3_part_defs); + +static const struct rtw89_reg5_def rtw8852b_tssi_slope_defs_a[] = { + RTW89_DECL_RFK_WM(0x5814, 0x00000800, 0x1), + RTW89_DECL_RFK_WM(0x581c, 0x20000000, 0x1), + RTW89_DECL_RFK_WM(0x5814, 0x20000000, 0x1), +}; + +RTW89_DECLARE_RFK_TBL(rtw8852b_tssi_slope_defs_a); + +static const struct rtw89_reg5_def rtw8852b_tssi_slope_defs_b[] = { + RTW89_DECL_RFK_WM(0x7814, 0x00000800, 0x1), + RTW89_DECL_RFK_WM(0x781c, 0x20000000, 0x1), + RTW89_DECL_RFK_WM(0x7814, 0x20000000, 0x1), +}; + +RTW89_DECLARE_RFK_TBL(rtw8852b_tssi_slope_defs_b); diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852b_rfk_table.h b/drivers/net/wireless/realtek/rtw89/rtw8852b_rfk_table.h new file mode 100644 index 000000000000..b4d6e9851ff9 --- /dev/null +++ b/drivers/net/wireless/realtek/rtw89/rtw8852b_rfk_table.h @@ -0,0 +1,62 @@ +/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ +/* Copyright(c) 2019-2020 Realtek Corporation + */ + +#ifndef __RTW89_8852B_RFK_TABLE_H__ +#define __RTW89_8852B_RFK_TABLE_H__ + +#include "phy.h" + +extern const struct rtw89_rfk_tbl rtw8852b_afe_init_defs_tbl; +extern const struct rtw89_rfk_tbl rtw8852b_check_addc_defs_a_tbl; +extern const struct rtw89_rfk_tbl rtw8852b_check_addc_defs_b_tbl; +extern const struct rtw89_rfk_tbl rtw8852b_check_dadc_en_defs_a_tbl; +extern const struct rtw89_rfk_tbl rtw8852b_check_dadc_en_defs_b_tbl; +extern const struct rtw89_rfk_tbl rtw8852b_check_dadc_dis_defs_a_tbl; +extern const struct rtw89_rfk_tbl rtw8852b_check_dadc_dis_defs_b_tbl; +extern const struct rtw89_rfk_tbl rtw8852b_dack_s0_1_defs_tbl; +extern const struct rtw89_rfk_tbl rtw8852b_dack_s0_2_defs_tbl; +extern const struct rtw89_rfk_tbl rtw8852b_dack_s0_3_defs_tbl; +extern const struct rtw89_rfk_tbl rtw8852b_dack_s1_1_defs_tbl; +extern const struct rtw89_rfk_tbl rtw8852b_dack_s1_2_defs_tbl; +extern const struct rtw89_rfk_tbl rtw8852b_dack_s1_3_defs_tbl; +extern const struct rtw89_rfk_tbl rtw8852b_dpk_afe_defs_tbl; +extern const struct rtw89_rfk_tbl rtw8852b_dpk_afe_restore_defs_tbl; +extern const struct rtw89_rfk_tbl rtw8852b_dpk_kip_defs_tbl; +extern const struct rtw89_rfk_tbl rtw8852b_tssi_sys_defs_tbl; +extern const struct rtw89_rfk_tbl rtw8852b_tssi_sys_a_defs_2g_tbl; +extern const struct rtw89_rfk_tbl rtw8852b_tssi_sys_a_defs_5g_tbl; +extern const struct rtw89_rfk_tbl rtw8852b_tssi_sys_b_defs_2g_tbl; +extern const struct rtw89_rfk_tbl rtw8852b_tssi_sys_b_defs_5g_tbl; +extern const struct rtw89_rfk_tbl rtw8852b_tssi_init_txpwr_defs_a_tbl; +extern const struct rtw89_rfk_tbl rtw8852b_tssi_init_txpwr_defs_b_tbl; +extern const struct rtw89_rfk_tbl rtw8852b_tssi_init_txpwr_he_tb_defs_a_tbl; +extern const struct rtw89_rfk_tbl rtw8852b_tssi_init_txpwr_he_tb_defs_b_tbl; +extern const struct rtw89_rfk_tbl rtw8852b_tssi_dck_defs_a_tbl; +extern const struct rtw89_rfk_tbl rtw8852b_tssi_dck_defs_b_tbl; +extern const struct rtw89_rfk_tbl rtw8852b_tssi_dac_gain_defs_a_tbl; +extern const struct rtw89_rfk_tbl rtw8852b_tssi_dac_gain_defs_b_tbl; +extern const struct rtw89_rfk_tbl rtw8852b_tssi_slope_a_defs_2g_tbl; +extern const struct rtw89_rfk_tbl rtw8852b_tssi_slope_a_defs_5g_tbl; +extern const struct rtw89_rfk_tbl rtw8852b_tssi_slope_b_defs_2g_tbl; +extern const struct rtw89_rfk_tbl rtw8852b_tssi_slope_b_defs_5g_tbl; +extern const struct rtw89_rfk_tbl rtw8852b_tssi_align_a_2g_all_defs_tbl; +extern const struct rtw89_rfk_tbl rtw8852b_tssi_align_a_2g_part_defs_tbl; +extern const struct rtw89_rfk_tbl rtw8852b_tssi_align_a_5g1_all_defs_tbl; +extern const struct rtw89_rfk_tbl rtw8852b_tssi_align_a_5g1_part_defs_tbl; +extern const struct rtw89_rfk_tbl rtw8852b_tssi_align_a_5g2_all_defs_tbl; +extern const struct rtw89_rfk_tbl rtw8852b_tssi_align_a_5g2_part_defs_tbl; +extern const struct rtw89_rfk_tbl rtw8852b_tssi_align_a_5g3_all_defs_tbl; +extern const struct rtw89_rfk_tbl rtw8852b_tssi_align_a_5g3_part_defs_tbl; +extern const struct rtw89_rfk_tbl rtw8852b_tssi_align_b_2g_all_defs_tbl; +extern const struct rtw89_rfk_tbl rtw8852b_tssi_align_b_2g_part_defs_tbl; +extern const struct rtw89_rfk_tbl rtw8852b_tssi_align_b_5g1_all_defs_tbl; +extern const struct rtw89_rfk_tbl rtw8852b_tssi_align_b_5g1_part_defs_tbl; +extern const struct rtw89_rfk_tbl rtw8852b_tssi_align_b_5g2_all_defs_tbl; +extern const struct rtw89_rfk_tbl rtw8852b_tssi_align_b_5g2_part_defs_tbl; +extern const struct rtw89_rfk_tbl rtw8852b_tssi_align_b_5g3_all_defs_tbl; +extern const struct rtw89_rfk_tbl rtw8852b_tssi_align_b_5g3_part_defs_tbl; +extern const struct rtw89_rfk_tbl rtw8852b_tssi_slope_defs_a_tbl; +extern const struct rtw89_rfk_tbl rtw8852b_tssi_slope_defs_b_tbl; + +#endif From 9b43bd1ac0a8e29b678768f93645cc1b39571278 Mon Sep 17 00:00:00 2001 From: Zong-Zhe Yang Date: Wed, 28 Sep 2022 16:43:31 +0800 Subject: [PATCH 0025/2002] wifi: rtw89: phy: make generic txpwr setting functions Previously, we thought control registers or setting things for TX power series may change according to chip. So, setting functions are implemented chip by chip. However, until now, the functions keep the same among chips, at least 8852A, 8852C, and 8852B. There is a sufficient number of chips to share generic setting functions. So, we now remake them including TX power by rate, TX power offset, TX power limit, and TX power limit RU as generic ones in phy.c. Besides, there are some code refinements in the generic ones, but almost all of the logic doesn't change. Signed-off-by: Zong-Zhe Yang Signed-off-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20220928084336.34981-5-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/core.h | 4 + drivers/net/wireless/realtek/rtw89/phy.c | 167 +++++++++++++++++- drivers/net/wireless/realtek/rtw89/phy.h | 25 ++- drivers/net/wireless/realtek/rtw89/rtw8852a.c | 145 +-------------- drivers/net/wireless/realtek/rtw89/rtw8852a.h | 1 - drivers/net/wireless/realtek/rtw89/rtw8852c.c | 145 +-------------- drivers/net/wireless/realtek/rtw89/rtw8852c.h | 1 - 7 files changed, 184 insertions(+), 304 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index db041b32a8c2..be39d2200e05 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -490,6 +490,8 @@ enum rtw89_bandwidth_section_num { RTW89_BW80_SEC_NUM = 2, }; +#define RTW89_TXPWR_LMT_PAGE_SIZE 40 + struct rtw89_txpwr_limit { s8 cck_20m[RTW89_BF_NUM]; s8 cck_40m[RTW89_BF_NUM]; @@ -504,6 +506,8 @@ struct rtw89_txpwr_limit { #define RTW89_RU_SEC_NUM 8 +#define RTW89_TXPWR_LMT_RU_PAGE_SIZE 24 + struct rtw89_txpwr_limit_ru { s8 ru26[RTW89_RU_SEC_NUM]; s8 ru52[RTW89_RU_SEC_NUM]; diff --git a/drivers/net/wireless/realtek/rtw89/phy.c b/drivers/net/wireless/realtek/rtw89/phy.c index 6a6bdc652e09..12a297f6bf50 100644 --- a/drivers/net/wireless/realtek/rtw89/phy.c +++ b/drivers/net/wireless/realtek/rtw89/phy.c @@ -1443,23 +1443,21 @@ void rtw89_phy_write_reg3_tbl(struct rtw89_dev *rtwdev, } EXPORT_SYMBOL(rtw89_phy_write_reg3_tbl); -const u8 rtw89_rs_idx_max[] = { +static const u8 rtw89_rs_idx_max[] = { [RTW89_RS_CCK] = RTW89_RATE_CCK_MAX, [RTW89_RS_OFDM] = RTW89_RATE_OFDM_MAX, [RTW89_RS_MCS] = RTW89_RATE_MCS_MAX, [RTW89_RS_HEDCM] = RTW89_RATE_HEDCM_MAX, [RTW89_RS_OFFSET] = RTW89_RATE_OFFSET_MAX, }; -EXPORT_SYMBOL(rtw89_rs_idx_max); -const u8 rtw89_rs_nss_max[] = { +static const u8 rtw89_rs_nss_max[] = { [RTW89_RS_CCK] = 1, [RTW89_RS_OFDM] = 1, [RTW89_RS_MCS] = RTW89_NSS_MAX, [RTW89_RS_HEDCM] = RTW89_NSS_HEDCM_MAX, [RTW89_RS_OFFSET] = 1, }; -EXPORT_SYMBOL(rtw89_rs_nss_max); static const u8 _byr_of_rs[] = { [RTW89_RS_CCK] = offsetof(struct rtw89_txpwr_byrate, cck), @@ -1501,6 +1499,7 @@ EXPORT_SYMBOL(rtw89_phy_load_txpwr_byrate); (txpwr_rf) >> (__c->txpwr_factor_rf - __c->txpwr_factor_mac); \ }) +static s8 rtw89_phy_read_txpwr_byrate(struct rtw89_dev *rtwdev, u8 band, const struct rtw89_rate_desc *rate_desc) { @@ -1523,7 +1522,6 @@ s8 rtw89_phy_read_txpwr_byrate(struct rtw89_dev *rtwdev, u8 band, return _phy_txpwr_rf_to_mac(rtwdev, byr[idx]); } -EXPORT_SYMBOL(rtw89_phy_read_txpwr_byrate); static u8 rtw89_channel_6g_to_idx(struct rtw89_dev *rtwdev, u8 channel_6g) { @@ -1783,6 +1781,7 @@ static void rtw89_phy_fill_txpwr_limit_160m(struct rtw89_dev *rtwdev, lmt->mcs_40m_2p5[i] = min_t(s8, val_2p5_n[i], val_2p5_p[i]); } +static void rtw89_phy_fill_txpwr_limit(struct rtw89_dev *rtwdev, const struct rtw89_chan *chan, struct rtw89_txpwr_limit *lmt, @@ -1813,7 +1812,6 @@ void rtw89_phy_fill_txpwr_limit(struct rtw89_dev *rtwdev, break; } } -EXPORT_SYMBOL(rtw89_phy_fill_txpwr_limit); static s8 rtw89_phy_read_txpwr_limit_ru(struct rtw89_dev *rtwdev, u8 band, u8 ru, u8 ntx, u8 ch) @@ -1962,6 +1960,7 @@ rtw89_phy_fill_txpwr_limit_ru_160m(struct rtw89_dev *rtwdev, } } +static void rtw89_phy_fill_txpwr_limit_ru(struct rtw89_dev *rtwdev, const struct rtw89_chan *chan, struct rtw89_txpwr_limit_ru *lmt_ru, @@ -1992,7 +1991,161 @@ void rtw89_phy_fill_txpwr_limit_ru(struct rtw89_dev *rtwdev, break; } } -EXPORT_SYMBOL(rtw89_phy_fill_txpwr_limit_ru); + +void rtw89_phy_set_txpwr_byrate(struct rtw89_dev *rtwdev, + const struct rtw89_chan *chan, + enum rtw89_phy_idx phy_idx) +{ + static const u8 rs[] = { + RTW89_RS_CCK, + RTW89_RS_OFDM, + RTW89_RS_MCS, + RTW89_RS_HEDCM, + }; + struct rtw89_rate_desc cur; + u8 band = chan->band_type; + u8 ch = chan->channel; + u32 addr, val; + s8 v[4] = {}; + u8 i; + + rtw89_debug(rtwdev, RTW89_DBG_TXPWR, + "[TXPWR] set txpwr byrate with ch=%d\n", ch); + + BUILD_BUG_ON(rtw89_rs_idx_max[RTW89_RS_CCK] % 4); + BUILD_BUG_ON(rtw89_rs_idx_max[RTW89_RS_OFDM] % 4); + BUILD_BUG_ON(rtw89_rs_idx_max[RTW89_RS_MCS] % 4); + BUILD_BUG_ON(rtw89_rs_idx_max[RTW89_RS_HEDCM] % 4); + + addr = R_AX_PWR_BY_RATE; + for (cur.nss = 0; cur.nss <= RTW89_NSS_2; cur.nss++) { + for (i = 0; i < ARRAY_SIZE(rs); i++) { + if (cur.nss >= rtw89_rs_nss_max[rs[i]]) + continue; + + cur.rs = rs[i]; + for (cur.idx = 0; cur.idx < rtw89_rs_idx_max[rs[i]]; + cur.idx++) { + v[cur.idx % 4] = + rtw89_phy_read_txpwr_byrate(rtwdev, + band, + &cur); + + if ((cur.idx + 1) % 4) + continue; + + val = FIELD_PREP(GENMASK(7, 0), v[0]) | + FIELD_PREP(GENMASK(15, 8), v[1]) | + FIELD_PREP(GENMASK(23, 16), v[2]) | + FIELD_PREP(GENMASK(31, 24), v[3]); + + rtw89_mac_txpwr_write32(rtwdev, phy_idx, addr, + val); + addr += 4; + } + } + } +} +EXPORT_SYMBOL(rtw89_phy_set_txpwr_byrate); + +void rtw89_phy_set_txpwr_offset(struct rtw89_dev *rtwdev, + const struct rtw89_chan *chan, + enum rtw89_phy_idx phy_idx) +{ + struct rtw89_rate_desc desc = { + .nss = RTW89_NSS_1, + .rs = RTW89_RS_OFFSET, + }; + u8 band = chan->band_type; + s8 v[RTW89_RATE_OFFSET_MAX] = {}; + u32 val; + + rtw89_debug(rtwdev, RTW89_DBG_TXPWR, "[TXPWR] set txpwr offset\n"); + + for (desc.idx = 0; desc.idx < RTW89_RATE_OFFSET_MAX; desc.idx++) + v[desc.idx] = rtw89_phy_read_txpwr_byrate(rtwdev, band, &desc); + + BUILD_BUG_ON(RTW89_RATE_OFFSET_MAX != 5); + val = FIELD_PREP(GENMASK(3, 0), v[0]) | + FIELD_PREP(GENMASK(7, 4), v[1]) | + FIELD_PREP(GENMASK(11, 8), v[2]) | + FIELD_PREP(GENMASK(15, 12), v[3]) | + FIELD_PREP(GENMASK(19, 16), v[4]); + + rtw89_mac_txpwr_write32_mask(rtwdev, phy_idx, R_AX_PWR_RATE_OFST_CTRL, + GENMASK(19, 0), val); +} +EXPORT_SYMBOL(rtw89_phy_set_txpwr_offset); + +void rtw89_phy_set_txpwr_limit(struct rtw89_dev *rtwdev, + const struct rtw89_chan *chan, + enum rtw89_phy_idx phy_idx) +{ + struct rtw89_txpwr_limit lmt; + u8 ch = chan->channel; + u8 bw = chan->band_width; + const s8 *ptr; + u32 addr, val; + u8 i, j; + + rtw89_debug(rtwdev, RTW89_DBG_TXPWR, + "[TXPWR] set txpwr limit with ch=%d bw=%d\n", ch, bw); + + BUILD_BUG_ON(sizeof(struct rtw89_txpwr_limit) != + RTW89_TXPWR_LMT_PAGE_SIZE); + + addr = R_AX_PWR_LMT; + for (i = 0; i < RTW89_NTX_NUM; i++) { + rtw89_phy_fill_txpwr_limit(rtwdev, chan, &lmt, i); + + ptr = (s8 *)&lmt; + for (j = 0; j < RTW89_TXPWR_LMT_PAGE_SIZE; + j += 4, addr += 4, ptr += 4) { + val = FIELD_PREP(GENMASK(7, 0), ptr[0]) | + FIELD_PREP(GENMASK(15, 8), ptr[1]) | + FIELD_PREP(GENMASK(23, 16), ptr[2]) | + FIELD_PREP(GENMASK(31, 24), ptr[3]); + + rtw89_mac_txpwr_write32(rtwdev, phy_idx, addr, val); + } + } +} +EXPORT_SYMBOL(rtw89_phy_set_txpwr_limit); + +void rtw89_phy_set_txpwr_limit_ru(struct rtw89_dev *rtwdev, + const struct rtw89_chan *chan, + enum rtw89_phy_idx phy_idx) +{ + struct rtw89_txpwr_limit_ru lmt_ru; + u8 ch = chan->channel; + u8 bw = chan->band_width; + const s8 *ptr; + u32 addr, val; + u8 i, j; + + rtw89_debug(rtwdev, RTW89_DBG_TXPWR, + "[TXPWR] set txpwr limit ru with ch=%d bw=%d\n", ch, bw); + + BUILD_BUG_ON(sizeof(struct rtw89_txpwr_limit_ru) != + RTW89_TXPWR_LMT_RU_PAGE_SIZE); + + addr = R_AX_PWR_RU_LMT; + for (i = 0; i < RTW89_NTX_NUM; i++) { + rtw89_phy_fill_txpwr_limit_ru(rtwdev, chan, &lmt_ru, i); + + ptr = (s8 *)&lmt_ru; + for (j = 0; j < RTW89_TXPWR_LMT_RU_PAGE_SIZE; + j += 4, addr += 4, ptr += 4) { + val = FIELD_PREP(GENMASK(7, 0), ptr[0]) | + FIELD_PREP(GENMASK(15, 8), ptr[1]) | + FIELD_PREP(GENMASK(23, 16), ptr[2]) | + FIELD_PREP(GENMASK(31, 24), ptr[3]); + + rtw89_mac_txpwr_write32(rtwdev, phy_idx, addr, val); + } + } +} +EXPORT_SYMBOL(rtw89_phy_set_txpwr_limit_ru); struct rtw89_phy_iter_ra_data { struct rtw89_dev *rtwdev; diff --git a/drivers/net/wireless/realtek/rtw89/phy.h b/drivers/net/wireless/realtek/rtw89/phy.h index ee3bc5e111e1..030a7c904a28 100644 --- a/drivers/net/wireless/realtek/rtw89/phy.h +++ b/drivers/net/wireless/realtek/rtw89/phy.h @@ -317,9 +317,6 @@ struct rtw89_nbi_reg_def { struct rtw89_reg_def notch2_en; }; -extern const u8 rtw89_rs_idx_max[RTW89_RS_MAX]; -extern const u8 rtw89_rs_nss_max[RTW89_RS_MAX]; - static inline void rtw89_phy_write8(struct rtw89_dev *rtwdev, u32 addr, u8 data) { @@ -460,18 +457,20 @@ void rtw89_phy_write32_idx(struct rtw89_dev *rtwdev, u32 addr, u32 mask, u32 data, enum rtw89_phy_idx phy_idx); void rtw89_phy_load_txpwr_byrate(struct rtw89_dev *rtwdev, const struct rtw89_txpwr_table *tbl); -s8 rtw89_phy_read_txpwr_byrate(struct rtw89_dev *rtwdev, u8 band, - const struct rtw89_rate_desc *rate_desc); -void rtw89_phy_fill_txpwr_limit(struct rtw89_dev *rtwdev, - const struct rtw89_chan *chan, - struct rtw89_txpwr_limit *lmt, - u8 ntx); -void rtw89_phy_fill_txpwr_limit_ru(struct rtw89_dev *rtwdev, - const struct rtw89_chan *chan, - struct rtw89_txpwr_limit_ru *lmt_ru, - u8 ntx); s8 rtw89_phy_read_txpwr_limit(struct rtw89_dev *rtwdev, u8 band, u8 bw, u8 ntx, u8 rs, u8 bf, u8 ch); +void rtw89_phy_set_txpwr_byrate(struct rtw89_dev *rtwdev, + const struct rtw89_chan *chan, + enum rtw89_phy_idx phy_idx); +void rtw89_phy_set_txpwr_offset(struct rtw89_dev *rtwdev, + const struct rtw89_chan *chan, + enum rtw89_phy_idx phy_idx); +void rtw89_phy_set_txpwr_limit(struct rtw89_dev *rtwdev, + const struct rtw89_chan *chan, + enum rtw89_phy_idx phy_idx); +void rtw89_phy_set_txpwr_limit_ru(struct rtw89_dev *rtwdev, + const struct rtw89_chan *chan, + enum rtw89_phy_idx phy_idx); void rtw89_phy_ra_assoc(struct rtw89_dev *rtwdev, struct ieee80211_sta *sta); void rtw89_phy_ra_update(struct rtw89_dev *rtwdev); void rtw89_phy_ra_updata_sta(struct rtw89_dev *rtwdev, struct ieee80211_sta *sta, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852a.c b/drivers/net/wireless/realtek/rtw89/rtw8852a.c index 784147680353..5678683ec02a 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852a.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852a.c @@ -1410,151 +1410,14 @@ static void rtw8852a_set_txpwr_ref(struct rtw89_dev *rtwdev, phy_idx); } -static void rtw8852a_set_txpwr_byrate(struct rtw89_dev *rtwdev, - const struct rtw89_chan *chan, - enum rtw89_phy_idx phy_idx) -{ - u8 band = chan->band_type; - u8 ch = chan->channel; - static const u8 rs[] = { - RTW89_RS_CCK, - RTW89_RS_OFDM, - RTW89_RS_MCS, - RTW89_RS_HEDCM, - }; - s8 tmp; - u8 i, j; - u32 val, shf, addr = R_AX_PWR_BY_RATE; - struct rtw89_rate_desc cur; - - rtw89_debug(rtwdev, RTW89_DBG_TXPWR, - "[TXPWR] set txpwr byrate with ch=%d\n", ch); - - for (cur.nss = 0; cur.nss <= RTW89_NSS_2; cur.nss++) { - for (i = 0; i < ARRAY_SIZE(rs); i++) { - if (cur.nss >= rtw89_rs_nss_max[rs[i]]) - continue; - - val = 0; - cur.rs = rs[i]; - - for (j = 0; j < rtw89_rs_idx_max[rs[i]]; j++) { - cur.idx = j; - shf = (j % 4) * 8; - tmp = rtw89_phy_read_txpwr_byrate(rtwdev, band, - &cur); - val |= (tmp << shf); - - if ((j + 1) % 4) - continue; - - rtw89_mac_txpwr_write32(rtwdev, phy_idx, addr, val); - val = 0; - addr += 4; - } - } - } -} - -static void rtw8852a_set_txpwr_offset(struct rtw89_dev *rtwdev, - const struct rtw89_chan *chan, - enum rtw89_phy_idx phy_idx) -{ - u8 band = chan->band_type; - struct rtw89_rate_desc desc = { - .nss = RTW89_NSS_1, - .rs = RTW89_RS_OFFSET, - }; - u32 val = 0; - s8 v; - - rtw89_debug(rtwdev, RTW89_DBG_TXPWR, "[TXPWR] set txpwr offset\n"); - - for (desc.idx = 0; desc.idx < RTW89_RATE_OFFSET_MAX; desc.idx++) { - v = rtw89_phy_read_txpwr_byrate(rtwdev, band, &desc); - val |= ((v & 0xf) << (4 * desc.idx)); - } - - rtw89_mac_txpwr_write32_mask(rtwdev, phy_idx, R_AX_PWR_RATE_OFST_CTRL, - GENMASK(19, 0), val); -} - -static void rtw8852a_set_txpwr_limit(struct rtw89_dev *rtwdev, - const struct rtw89_chan *chan, - enum rtw89_phy_idx phy_idx) -{ -#define __MAC_TXPWR_LMT_PAGE_SIZE 40 - u8 ch = chan->channel; - u8 bw = chan->band_width; - struct rtw89_txpwr_limit lmt[NTX_NUM_8852A]; - u32 addr, val; - const s8 *ptr; - u8 i, j; - - rtw89_debug(rtwdev, RTW89_DBG_TXPWR, - "[TXPWR] set txpwr limit with ch=%d bw=%d\n", ch, bw); - - for (i = 0; i < NTX_NUM_8852A; i++) { - rtw89_phy_fill_txpwr_limit(rtwdev, chan, &lmt[i], i); - - for (j = 0; j < __MAC_TXPWR_LMT_PAGE_SIZE; j += 4) { - addr = R_AX_PWR_LMT + j + __MAC_TXPWR_LMT_PAGE_SIZE * i; - ptr = (s8 *)&lmt[i] + j; - - val = FIELD_PREP(GENMASK(7, 0), ptr[0]) | - FIELD_PREP(GENMASK(15, 8), ptr[1]) | - FIELD_PREP(GENMASK(23, 16), ptr[2]) | - FIELD_PREP(GENMASK(31, 24), ptr[3]); - - rtw89_mac_txpwr_write32(rtwdev, phy_idx, addr, val); - } - } -#undef __MAC_TXPWR_LMT_PAGE_SIZE -} - -static void rtw8852a_set_txpwr_limit_ru(struct rtw89_dev *rtwdev, - const struct rtw89_chan *chan, - enum rtw89_phy_idx phy_idx) -{ -#define __MAC_TXPWR_LMT_RU_PAGE_SIZE 24 - u8 ch = chan->channel; - u8 bw = chan->band_width; - struct rtw89_txpwr_limit_ru lmt_ru[NTX_NUM_8852A]; - u32 addr, val; - const s8 *ptr; - u8 i, j; - - rtw89_debug(rtwdev, RTW89_DBG_TXPWR, - "[TXPWR] set txpwr limit ru with ch=%d bw=%d\n", ch, bw); - - for (i = 0; i < NTX_NUM_8852A; i++) { - rtw89_phy_fill_txpwr_limit_ru(rtwdev, chan, &lmt_ru[i], i); - - for (j = 0; j < __MAC_TXPWR_LMT_RU_PAGE_SIZE; j += 4) { - addr = R_AX_PWR_RU_LMT + j + - __MAC_TXPWR_LMT_RU_PAGE_SIZE * i; - ptr = (s8 *)&lmt_ru[i] + j; - - val = FIELD_PREP(GENMASK(7, 0), ptr[0]) | - FIELD_PREP(GENMASK(15, 8), ptr[1]) | - FIELD_PREP(GENMASK(23, 16), ptr[2]) | - FIELD_PREP(GENMASK(31, 24), ptr[3]); - - rtw89_mac_txpwr_write32(rtwdev, phy_idx, addr, val); - } - } - -#undef __MAC_TXPWR_LMT_RU_PAGE_SIZE -} - static void rtw8852a_set_txpwr(struct rtw89_dev *rtwdev, const struct rtw89_chan *chan, enum rtw89_phy_idx phy_idx) { - rtw8852a_set_txpwr_byrate(rtwdev, chan, phy_idx); - rtw8852a_set_txpwr_offset(rtwdev, chan, phy_idx); - rtw8852a_set_txpwr_limit(rtwdev, chan, phy_idx); - rtw8852a_set_txpwr_limit_ru(rtwdev, chan, phy_idx); + rtw89_phy_set_txpwr_byrate(rtwdev, chan, phy_idx); + rtw89_phy_set_txpwr_offset(rtwdev, chan, phy_idx); + rtw89_phy_set_txpwr_limit(rtwdev, chan, phy_idx); + rtw89_phy_set_txpwr_limit_ru(rtwdev, chan, phy_idx); } static void rtw8852a_set_txpwr_ctrl(struct rtw89_dev *rtwdev, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852a.h b/drivers/net/wireless/realtek/rtw89/rtw8852a.h index fcff1194c009..ea82fed7b7be 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852a.h +++ b/drivers/net/wireless/realtek/rtw89/rtw8852a.h @@ -8,7 +8,6 @@ #include "core.h" #define RF_PATH_NUM_8852A 2 -#define NTX_NUM_8852A 2 enum rtw8852a_pmac_mode { NONE_TEST, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852c.c b/drivers/net/wireless/realtek/rtw89/rtw8852c.c index 67653b3e1a35..a6a9fe3d0b56 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852c.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852c.c @@ -2006,75 +2006,6 @@ static void rtw8852c_set_txpwr_ref(struct rtw89_dev *rtwdev, phy_idx); } -static void rtw8852c_set_txpwr_byrate(struct rtw89_dev *rtwdev, - const struct rtw89_chan *chan, - enum rtw89_phy_idx phy_idx) -{ - u8 band = chan->band_type; - u8 ch = chan->channel; - static const u8 rs[] = { - RTW89_RS_CCK, - RTW89_RS_OFDM, - RTW89_RS_MCS, - RTW89_RS_HEDCM, - }; - s8 tmp; - u8 i, j; - u32 val, shf, addr = R_AX_PWR_BY_RATE; - struct rtw89_rate_desc cur; - - rtw89_debug(rtwdev, RTW89_DBG_TXPWR, - "[TXPWR] set txpwr byrate with ch=%d\n", ch); - - for (cur.nss = 0; cur.nss <= RTW89_NSS_2; cur.nss++) { - for (i = 0; i < ARRAY_SIZE(rs); i++) { - if (cur.nss >= rtw89_rs_nss_max[rs[i]]) - continue; - - val = 0; - cur.rs = rs[i]; - - for (j = 0; j < rtw89_rs_idx_max[rs[i]]; j++) { - cur.idx = j; - shf = (j % 4) * 8; - tmp = rtw89_phy_read_txpwr_byrate(rtwdev, band, - &cur); - val |= (tmp << shf); - - if ((j + 1) % 4) - continue; - - rtw89_mac_txpwr_write32(rtwdev, phy_idx, addr, val); - val = 0; - addr += 4; - } - } - } -} - -static void rtw8852c_set_txpwr_offset(struct rtw89_dev *rtwdev, - const struct rtw89_chan *chan, - enum rtw89_phy_idx phy_idx) -{ - u8 band = chan->band_type; - struct rtw89_rate_desc desc = { - .nss = RTW89_NSS_1, - .rs = RTW89_RS_OFFSET, - }; - u32 val = 0; - s8 v; - - rtw89_debug(rtwdev, RTW89_DBG_TXPWR, "[TXPWR] set txpwr offset\n"); - - for (desc.idx = 0; desc.idx < RTW89_RATE_OFFSET_MAX; desc.idx++) { - v = rtw89_phy_read_txpwr_byrate(rtwdev, band, &desc); - val |= ((v & 0xf) << (4 * desc.idx)); - } - - rtw89_mac_txpwr_write32_mask(rtwdev, phy_idx, R_AX_PWR_RATE_OFST_CTRL, - GENMASK(19, 0), val); -} - static void rtw8852c_bb_set_tx_shape_dfir(struct rtw89_dev *rtwdev, u8 tx_shape_idx, enum rtw89_phy_idx phy_idx) @@ -2147,83 +2078,15 @@ static void rtw8852c_set_tx_shape(struct rtw89_dev *rtwdev, tx_shape_ofdm); } -static void rtw8852c_set_txpwr_limit(struct rtw89_dev *rtwdev, - const struct rtw89_chan *chan, - enum rtw89_phy_idx phy_idx) -{ -#define __MAC_TXPWR_LMT_PAGE_SIZE 40 - u8 ch = chan->channel; - u8 bw = chan->band_width; - struct rtw89_txpwr_limit lmt[NTX_NUM_8852C]; - u32 addr, val; - const s8 *ptr; - u8 i, j; - - rtw89_debug(rtwdev, RTW89_DBG_TXPWR, - "[TXPWR] set txpwr limit with ch=%d bw=%d\n", ch, bw); - - for (i = 0; i < NTX_NUM_8852C; i++) { - rtw89_phy_fill_txpwr_limit(rtwdev, chan, &lmt[i], i); - - for (j = 0; j < __MAC_TXPWR_LMT_PAGE_SIZE; j += 4) { - addr = R_AX_PWR_LMT + j + __MAC_TXPWR_LMT_PAGE_SIZE * i; - ptr = (s8 *)&lmt[i] + j; - - val = FIELD_PREP(GENMASK(7, 0), ptr[0]) | - FIELD_PREP(GENMASK(15, 8), ptr[1]) | - FIELD_PREP(GENMASK(23, 16), ptr[2]) | - FIELD_PREP(GENMASK(31, 24), ptr[3]); - - rtw89_mac_txpwr_write32(rtwdev, phy_idx, addr, val); - } - } -#undef __MAC_TXPWR_LMT_PAGE_SIZE -} - -static void rtw8852c_set_txpwr_limit_ru(struct rtw89_dev *rtwdev, - const struct rtw89_chan *chan, - enum rtw89_phy_idx phy_idx) -{ -#define __MAC_TXPWR_LMT_RU_PAGE_SIZE 24 - u8 ch = chan->channel; - u8 bw = chan->band_width; - struct rtw89_txpwr_limit_ru lmt_ru[NTX_NUM_8852C]; - u32 addr, val; - const s8 *ptr; - u8 i, j; - - rtw89_debug(rtwdev, RTW89_DBG_TXPWR, - "[TXPWR] set txpwr limit ru with ch=%d bw=%d\n", ch, bw); - - for (i = 0; i < NTX_NUM_8852C; i++) { - rtw89_phy_fill_txpwr_limit_ru(rtwdev, chan, &lmt_ru[i], i); - - for (j = 0; j < __MAC_TXPWR_LMT_RU_PAGE_SIZE; j += 4) { - addr = R_AX_PWR_RU_LMT + j + - __MAC_TXPWR_LMT_RU_PAGE_SIZE * i; - ptr = (s8 *)&lmt_ru[i] + j; - - val = FIELD_PREP(GENMASK(7, 0), ptr[0]) | - FIELD_PREP(GENMASK(15, 8), ptr[1]) | - FIELD_PREP(GENMASK(23, 16), ptr[2]) | - FIELD_PREP(GENMASK(31, 24), ptr[3]); - - rtw89_mac_txpwr_write32(rtwdev, phy_idx, addr, val); - } - } - -#undef __MAC_TXPWR_LMT_RU_PAGE_SIZE -} - static void rtw8852c_set_txpwr(struct rtw89_dev *rtwdev, const struct rtw89_chan *chan, enum rtw89_phy_idx phy_idx) { - rtw8852c_set_txpwr_byrate(rtwdev, chan, phy_idx); - rtw8852c_set_txpwr_offset(rtwdev, chan, phy_idx); + rtw89_phy_set_txpwr_byrate(rtwdev, chan, phy_idx); + rtw89_phy_set_txpwr_offset(rtwdev, chan, phy_idx); rtw8852c_set_tx_shape(rtwdev, chan, phy_idx); - rtw8852c_set_txpwr_limit(rtwdev, chan, phy_idx); - rtw8852c_set_txpwr_limit_ru(rtwdev, chan, phy_idx); + rtw89_phy_set_txpwr_limit(rtwdev, chan, phy_idx); + rtw89_phy_set_txpwr_limit_ru(rtwdev, chan, phy_idx); } static void rtw8852c_set_txpwr_ctrl(struct rtw89_dev *rtwdev, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852c.h b/drivers/net/wireless/realtek/rtw89/rtw8852c.h index 558dd0f048f2..ac642808a81f 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852c.h +++ b/drivers/net/wireless/realtek/rtw89/rtw8852c.h @@ -9,7 +9,6 @@ #define RF_PATH_NUM_8852C 2 #define BB_PATH_NUM_8852C 2 -#define NTX_NUM_8852C 2 struct rtw8852c_u_efuse { u8 rsvd[0x38]; From b902161645879ac820dfbb561667cd08be569538 Mon Sep 17 00:00:00 2001 From: Zong-Zhe Yang Date: Wed, 28 Sep 2022 16:43:32 +0800 Subject: [PATCH 0026/2002] wifi: rtw89: debug: txpwr_table considers sign Previously, value of each field is just shown as unsigned. Now, we start to show them with sign to make things more intuitive during debugging. Signed-off-by: Zong-Zhe Yang Signed-off-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20220928084336.34981-6-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/debug.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/debug.c b/drivers/net/wireless/realtek/rtw89/debug.c index 730e83d54257..f584fa57c82f 100644 --- a/drivers/net/wireless/realtek/rtw89/debug.c +++ b/drivers/net/wireless/realtek/rtw89/debug.c @@ -464,7 +464,7 @@ static const struct txpwr_map __txpwr_map_lmt_ru = { }; static u8 __print_txpwr_ent(struct seq_file *m, const struct txpwr_ent *ent, - const u8 *buf, const u8 cur) + const s8 *buf, const u8 cur) { char *fmt; @@ -493,8 +493,9 @@ static int __print_txpwr_map(struct seq_file *m, struct rtw89_dev *rtwdev, const struct txpwr_map *map) { u8 fct = rtwdev->chip->txpwr_factor_mac; - u8 *buf, cur, i; u32 val, addr; + s8 *buf, tmp; + u8 cur, i; int ret; buf = vzalloc(map->addr_to - map->addr_from + 4); @@ -507,8 +508,11 @@ static int __print_txpwr_map(struct seq_file *m, struct rtw89_dev *rtwdev, val = MASKDWORD; cur = addr - map->addr_from; - for (i = 0; i < 4; i++, val >>= 8) - buf[cur + i] = FIELD_GET(MASKBYTE0, val) >> fct; + for (i = 0; i < 4; i++, val >>= 8) { + /* signed 7 bits, and reserved BIT(7) */ + tmp = sign_extend32(val, 6); + buf[cur + i] = tmp >> fct; + } } for (cur = 0, i = 0; i < map->size; i++) From 08484e1f6e6fd670c722756baea4833436ca8fb5 Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Wed, 28 Sep 2022 16:43:33 +0800 Subject: [PATCH 0027/2002] wifi: rtw89: 8852b: add chip_ops::set_txpwr This chip_ops is to set TX power according to country, channel, rate and so on. Since shared code is used to configure TX power, we only implement specific part in this patch. Signed-off-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20220928084336.34981-7-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/mac.c | 1 + drivers/net/wireless/realtek/rtw89/reg.h | 5 + drivers/net/wireless/realtek/rtw89/rtw8852b.c | 200 +++++++++++++++++- drivers/net/wireless/realtek/rtw89/rtw8852b.h | 13 ++ 4 files changed, 218 insertions(+), 1 deletion(-) create mode 100644 drivers/net/wireless/realtek/rtw89/rtw8852b.h diff --git a/drivers/net/wireless/realtek/rtw89/mac.c b/drivers/net/wireless/realtek/rtw89/mac.c index 0508dfca8edf..878c33fa30bd 100644 --- a/drivers/net/wireless/realtek/rtw89/mac.c +++ b/drivers/net/wireless/realtek/rtw89/mac.c @@ -4819,6 +4819,7 @@ int rtw89_mac_read_xtal_si(struct rtw89_dev *rtwdev, u8 offset, u8 *val) return 0; } +EXPORT_SYMBOL(rtw89_mac_read_xtal_si); static void rtw89_mac_pkt_drop_sta(struct rtw89_dev *rtwdev, struct rtw89_sta *rtwsta) diff --git a/drivers/net/wireless/realtek/rtw89/reg.h b/drivers/net/wireless/realtek/rtw89/reg.h index ca20bb024b40..6809ff812abb 100644 --- a/drivers/net/wireless/realtek/rtw89/reg.h +++ b/drivers/net/wireless/realtek/rtw89/reg.h @@ -2991,6 +2991,7 @@ #define R_AX_PWR_RATE_CTRL 0xD200 #define R_AX_PWR_RATE_CTRL_C1 0xF200 +#define B_AX_PWR_REF GENMASK(27, 10) #define B_AX_FORCE_PWR_BY_RATE_EN BIT(9) #define B_AX_FORCE_PWR_BY_RATE_VALUE_MASK GENMASK(8, 0) @@ -3770,6 +3771,7 @@ #define B_DCFO_WEIGHT_MSK GENMASK(27, 24) #define R_DCFO_OPT 0x4494 #define B_DCFO_OPT_EN BIT(29) +#define B_TXSHAPE_TRIANGULAR_CFG GENMASK(25, 24) #define R_BANDEDGE 0x4498 #define B_BANDEDGE_EN BIT(30) #define R_TXPATH_SEL 0x458C @@ -4003,6 +4005,9 @@ #define B_TXPWRB_VAL GENMASK(27, 19) #define R_DPD_OFT_EN 0x5800 #define B_DPD_OFT_EN BIT(28) +#define B_DPD_TSSI_CW GENMASK(26, 18) +#define B_DPD_PWR_CW GENMASK(17, 9) +#define B_DPD_REF GENMASK(8, 0) #define R_DPD_OFT_ADDR 0x5804 #define B_DPD_OFT_ADDR GENMASK(31, 27) #define R_TXPWRB_H 0x580c diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852b.c b/drivers/net/wireless/realtek/rtw89/rtw8852b.c index 9f9908418ee4..ec6833080b80 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852b.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852b.c @@ -2,9 +2,14 @@ /* Copyright(c) 2019-2022 Realtek Corporation */ -#include "core.h" +#include "coex.h" +#include "fw.h" #include "mac.h" +#include "phy.h" #include "reg.h" +#include "rtw8852b.h" +#include "rtw8852b_table.h" +#include "txrx.h" static const struct rtw89_dle_mem rtw8852b_dle_mem_pcie[] = { [RTW89_QTA_SCC] = {RTW89_QTA_SCC, &rtw89_mac_size.wde_size6, @@ -19,6 +24,195 @@ static const struct rtw89_dle_mem rtw8852b_dle_mem_pcie[] = { NULL}, }; +static u32 rtw8852b_bb_cal_txpwr_ref(struct rtw89_dev *rtwdev, + enum rtw89_phy_idx phy_idx, s16 ref) +{ + const u16 tssi_16dbm_cw = 0x12c; + const u8 base_cw_0db = 0x27; + const s8 ofst_int = 0; + s16 pwr_s10_3; + s16 rf_pwr_cw; + u16 bb_pwr_cw; + u32 pwr_cw; + u32 tssi_ofst_cw; + + pwr_s10_3 = (ref << 1) + (s16)(ofst_int) + (s16)(base_cw_0db << 3); + bb_pwr_cw = FIELD_GET(GENMASK(2, 0), pwr_s10_3); + rf_pwr_cw = FIELD_GET(GENMASK(8, 3), pwr_s10_3); + rf_pwr_cw = clamp_t(s16, rf_pwr_cw, 15, 63); + pwr_cw = (rf_pwr_cw << 3) | bb_pwr_cw; + + tssi_ofst_cw = (u32)((s16)tssi_16dbm_cw + (ref << 1) - (16 << 3)); + rtw89_debug(rtwdev, RTW89_DBG_TXPWR, + "[TXPWR] tssi_ofst_cw=%d rf_cw=0x%x bb_cw=0x%x\n", + tssi_ofst_cw, rf_pwr_cw, bb_pwr_cw); + + return FIELD_PREP(B_DPD_TSSI_CW, tssi_ofst_cw) | + FIELD_PREP(B_DPD_PWR_CW, pwr_cw) | + FIELD_PREP(B_DPD_REF, ref); +} + +static void rtw8852b_set_txpwr_ref(struct rtw89_dev *rtwdev, + enum rtw89_phy_idx phy_idx) +{ + static const u32 addr[RF_PATH_NUM_8852B] = {0x5800, 0x7800}; + const u32 mask = B_DPD_TSSI_CW | B_DPD_PWR_CW | B_DPD_REF; + const u8 ofst_ofdm = 0x4; + const u8 ofst_cck = 0x8; + const s16 ref_ofdm = 0; + const s16 ref_cck = 0; + u32 val; + u8 i; + + rtw89_debug(rtwdev, RTW89_DBG_TXPWR, "[TXPWR] set txpwr reference\n"); + + rtw89_mac_txpwr_write32_mask(rtwdev, phy_idx, R_AX_PWR_RATE_CTRL, + B_AX_PWR_REF, 0x0); + + rtw89_debug(rtwdev, RTW89_DBG_TXPWR, "[TXPWR] set bb ofdm txpwr ref\n"); + val = rtw8852b_bb_cal_txpwr_ref(rtwdev, phy_idx, ref_ofdm); + + for (i = 0; i < RF_PATH_NUM_8852B; i++) + rtw89_phy_write32_idx(rtwdev, addr[i] + ofst_ofdm, mask, val, + phy_idx); + + rtw89_debug(rtwdev, RTW89_DBG_TXPWR, "[TXPWR] set bb cck txpwr ref\n"); + val = rtw8852b_bb_cal_txpwr_ref(rtwdev, phy_idx, ref_cck); + + for (i = 0; i < RF_PATH_NUM_8852B; i++) + rtw89_phy_write32_idx(rtwdev, addr[i] + ofst_cck, mask, val, + phy_idx); +} + +static void rtw8852b_bb_set_tx_shape_dfir(struct rtw89_dev *rtwdev, + u8 tx_shape_idx, + enum rtw89_phy_idx phy_idx) +{ +#define __DFIR_CFG_ADDR(i) (R_TXFIR0 + ((i) << 2)) +#define __DFIR_CFG_MASK 0xffffffff +#define __DFIR_CFG_NR 8 +#define __DECL_DFIR_PARAM(_name, _val...) \ + static const u32 param_ ## _name[] = {_val}; \ + static_assert(ARRAY_SIZE(param_ ## _name) == __DFIR_CFG_NR) + + __DECL_DFIR_PARAM(flat, + 0x023D23FF, 0x0029B354, 0x000FC1C8, 0x00FDB053, + 0x00F86F9A, 0x06FAEF92, 0x00FE5FCC, 0x00FFDFF5); + __DECL_DFIR_PARAM(sharp, + 0x023D83FF, 0x002C636A, 0x0013F204, 0x00008090, + 0x00F87FB0, 0x06F99F83, 0x00FDBFBA, 0x00003FF5); + __DECL_DFIR_PARAM(sharp_14, + 0x023B13FF, 0x001C42DE, 0x00FDB0AD, 0x00F60F6E, + 0x00FD8F92, 0x0602D011, 0x0001C02C, 0x00FFF00A); + const struct rtw89_chan *chan = rtw89_chan_get(rtwdev, RTW89_SUB_ENTITY_0); + u8 ch = chan->channel; + const u32 *param; + u32 addr; + int i; + + if (ch > 14) { + rtw89_warn(rtwdev, + "set tx shape dfir by unknown ch: %d on 2G\n", ch); + return; + } + + if (ch == 14) + param = param_sharp_14; + else + param = tx_shape_idx == 0 ? param_flat : param_sharp; + + for (i = 0; i < __DFIR_CFG_NR; i++) { + addr = __DFIR_CFG_ADDR(i); + rtw89_debug(rtwdev, RTW89_DBG_TXPWR, + "set tx shape dfir: 0x%x: 0x%x\n", addr, param[i]); + rtw89_phy_write32_idx(rtwdev, addr, __DFIR_CFG_MASK, param[i], + phy_idx); + } + +#undef __DECL_DFIR_PARAM +#undef __DFIR_CFG_NR +#undef __DFIR_CFG_MASK +#undef __DECL_CFG_ADDR +} + +static void rtw8852b_set_tx_shape(struct rtw89_dev *rtwdev, + const struct rtw89_chan *chan, + enum rtw89_phy_idx phy_idx) +{ + u8 band = chan->band_type; + u8 regd = rtw89_regd_get(rtwdev, band); + u8 tx_shape_cck = rtw89_8852b_tx_shape[band][RTW89_RS_CCK][regd]; + u8 tx_shape_ofdm = rtw89_8852b_tx_shape[band][RTW89_RS_OFDM][regd]; + + if (band == RTW89_BAND_2G) + rtw8852b_bb_set_tx_shape_dfir(rtwdev, tx_shape_cck, phy_idx); + + rtw89_phy_write32_mask(rtwdev, R_DCFO_OPT, B_TXSHAPE_TRIANGULAR_CFG, + tx_shape_ofdm); +} + +static void rtw8852b_set_txpwr(struct rtw89_dev *rtwdev, + const struct rtw89_chan *chan, + enum rtw89_phy_idx phy_idx) +{ + rtw89_phy_set_txpwr_byrate(rtwdev, chan, phy_idx); + rtw89_phy_set_txpwr_offset(rtwdev, chan, phy_idx); + rtw8852b_set_tx_shape(rtwdev, chan, phy_idx); + rtw89_phy_set_txpwr_limit(rtwdev, chan, phy_idx); + rtw89_phy_set_txpwr_limit_ru(rtwdev, chan, phy_idx); +} + +static void rtw8852b_set_txpwr_ctrl(struct rtw89_dev *rtwdev, + enum rtw89_phy_idx phy_idx) +{ + rtw8852b_set_txpwr_ref(rtwdev, phy_idx); +} + +static +void rtw8852b_set_txpwr_ul_tb_offset(struct rtw89_dev *rtwdev, + s8 pw_ofst, enum rtw89_mac_idx mac_idx) +{ + u32 reg; + + if (pw_ofst < -16 || pw_ofst > 15) { + rtw89_warn(rtwdev, "[ULTB] Err pwr_offset=%d\n", pw_ofst); + return; + } + + reg = rtw89_mac_reg_by_idx(R_AX_PWR_UL_TB_CTRL, mac_idx); + rtw89_write32_set(rtwdev, reg, B_AX_PWR_UL_TB_CTRL_EN); + + reg = rtw89_mac_reg_by_idx(R_AX_PWR_UL_TB_1T, mac_idx); + rtw89_write32_mask(rtwdev, reg, B_AX_PWR_UL_TB_1T_MASK, pw_ofst); + + pw_ofst = max_t(s8, pw_ofst - 3, -16); + reg = rtw89_mac_reg_by_idx(R_AX_PWR_UL_TB_2T, mac_idx); + rtw89_write32_mask(rtwdev, reg, B_AX_PWR_UL_TB_1T_MASK, pw_ofst); +} + +static int +rtw8852b_init_txpwr_unit(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx) +{ + int ret; + + ret = rtw89_mac_txpwr_write32(rtwdev, phy_idx, R_AX_PWR_UL_CTRL2, 0x07763333); + if (ret) + return ret; + + ret = rtw89_mac_txpwr_write32(rtwdev, phy_idx, R_AX_PWR_COEXT_CTRL, 0x01ebf000); + if (ret) + return ret; + + ret = rtw89_mac_txpwr_write32(rtwdev, phy_idx, R_AX_PWR_UL_CTRL0, 0x0002f8ff); + if (ret) + return ret; + + rtw8852b_set_txpwr_ul_tb_offset(rtwdev, 0, phy_idx == RTW89_PHY_1 ? + RTW89_MAC_1 : RTW89_MAC_0); + + return 0; +} + static int rtw8852b_mac_enable_bb_rf(struct rtw89_dev *rtwdev) { int ret; @@ -75,10 +269,14 @@ static int rtw8852b_mac_disable_bb_rf(struct rtw89_dev *rtwdev) static const struct rtw89_chip_ops rtw8852b_chip_ops = { .enable_bb_rf = rtw8852b_mac_enable_bb_rf, .disable_bb_rf = rtw8852b_mac_disable_bb_rf, + .set_txpwr = rtw8852b_set_txpwr, + .set_txpwr_ctrl = rtw8852b_set_txpwr_ctrl, + .init_txpwr_unit = rtw8852b_init_txpwr_unit, }; const struct rtw89_chip_info rtw8852b_chip_info = { .chip_id = RTL8852B, + .ops = &rtw8852b_chip_ops, .fifo_size = 196608, .dle_scc_rsvd_size = 98304, .dle_mem = rtw8852b_dle_mem_pcie, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852b.h b/drivers/net/wireless/realtek/rtw89/rtw8852b.h new file mode 100644 index 000000000000..a5ff269752a3 --- /dev/null +++ b/drivers/net/wireless/realtek/rtw89/rtw8852b.h @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ +/* Copyright(c) 2019-2022 Realtek Corporation + */ + +#ifndef __RTW89_8852B_H__ +#define __RTW89_8852B_H__ + +#include "core.h" + +#define RF_PATH_NUM_8852B 2 +#define BB_PATH_NUM_8852B 2 + +#endif From 132dc4fe5b587c0a62fc90d78e7413944fa06669 Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Wed, 28 Sep 2022 16:43:34 +0800 Subject: [PATCH 0028/2002] wifi: rtw89: 8852b: add chip_ops to read efuse efuse stores individual data about a chip itself, such as MAC address, country code, RF and crystal calibration data, and so on. Define a struct to help access efuse content, and copy them into a common struct. Signed-off-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20220928084336.34981-8-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/rtw8852b.c | 106 ++++++++++++++++++ drivers/net/wireless/realtek/rtw89/rtw8852b.h | 75 +++++++++++++ 2 files changed, 181 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852b.c b/drivers/net/wireless/realtek/rtw89/rtw8852b.c index ec6833080b80..b80102b1dd7f 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852b.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852b.c @@ -24,6 +24,105 @@ static const struct rtw89_dle_mem rtw8852b_dle_mem_pcie[] = { NULL}, }; +static void rtw8852be_efuse_parsing(struct rtw89_efuse *efuse, + struct rtw8852b_efuse *map) +{ + ether_addr_copy(efuse->addr, map->e.mac_addr); + efuse->rfe_type = map->rfe_type; + efuse->xtal_cap = map->xtal_k; +} + +static void rtw8852b_efuse_parsing_tssi(struct rtw89_dev *rtwdev, + struct rtw8852b_efuse *map) +{ + struct rtw89_tssi_info *tssi = &rtwdev->tssi; + struct rtw8852b_tssi_offset *ofst[] = {&map->path_a_tssi, &map->path_b_tssi}; + u8 i, j; + + tssi->thermal[RF_PATH_A] = map->path_a_therm; + tssi->thermal[RF_PATH_B] = map->path_b_therm; + + for (i = 0; i < RF_PATH_NUM_8852B; i++) { + memcpy(tssi->tssi_cck[i], ofst[i]->cck_tssi, + sizeof(ofst[i]->cck_tssi)); + + for (j = 0; j < TSSI_CCK_CH_GROUP_NUM; j++) + rtw89_debug(rtwdev, RTW89_DBG_TSSI, + "[TSSI][EFUSE] path=%d cck[%d]=0x%x\n", + i, j, tssi->tssi_cck[i][j]); + + memcpy(tssi->tssi_mcs[i], ofst[i]->bw40_tssi, + sizeof(ofst[i]->bw40_tssi)); + memcpy(tssi->tssi_mcs[i] + TSSI_MCS_2G_CH_GROUP_NUM, + ofst[i]->bw40_1s_tssi_5g, sizeof(ofst[i]->bw40_1s_tssi_5g)); + + for (j = 0; j < TSSI_MCS_CH_GROUP_NUM; j++) + rtw89_debug(rtwdev, RTW89_DBG_TSSI, + "[TSSI][EFUSE] path=%d mcs[%d]=0x%x\n", + i, j, tssi->tssi_mcs[i][j]); + } +} + +static bool _decode_efuse_gain(u8 data, s8 *high, s8 *low) +{ + if (high) + *high = sign_extend32(FIELD_GET(GENMASK(7, 4), data), 3); + if (low) + *low = sign_extend32(FIELD_GET(GENMASK(3, 0), data), 3); + + return data != 0xff; +} + +static void rtw8852b_efuse_parsing_gain_offset(struct rtw89_dev *rtwdev, + struct rtw8852b_efuse *map) +{ + struct rtw89_phy_efuse_gain *gain = &rtwdev->efuse_gain; + bool valid = false; + + valid |= _decode_efuse_gain(map->rx_gain_2g_cck, + &gain->offset[RF_PATH_A][RTW89_GAIN_OFFSET_2G_CCK], + &gain->offset[RF_PATH_B][RTW89_GAIN_OFFSET_2G_CCK]); + valid |= _decode_efuse_gain(map->rx_gain_2g_ofdm, + &gain->offset[RF_PATH_A][RTW89_GAIN_OFFSET_2G_OFDM], + &gain->offset[RF_PATH_B][RTW89_GAIN_OFFSET_2G_OFDM]); + valid |= _decode_efuse_gain(map->rx_gain_5g_low, + &gain->offset[RF_PATH_A][RTW89_GAIN_OFFSET_5G_LOW], + &gain->offset[RF_PATH_B][RTW89_GAIN_OFFSET_5G_LOW]); + valid |= _decode_efuse_gain(map->rx_gain_5g_mid, + &gain->offset[RF_PATH_A][RTW89_GAIN_OFFSET_5G_MID], + &gain->offset[RF_PATH_B][RTW89_GAIN_OFFSET_5G_MID]); + valid |= _decode_efuse_gain(map->rx_gain_5g_high, + &gain->offset[RF_PATH_A][RTW89_GAIN_OFFSET_5G_HIGH], + &gain->offset[RF_PATH_B][RTW89_GAIN_OFFSET_5G_HIGH]); + + gain->offset_valid = valid; +} + +static int rtw8852b_read_efuse(struct rtw89_dev *rtwdev, u8 *log_map) +{ + struct rtw89_efuse *efuse = &rtwdev->efuse; + struct rtw8852b_efuse *map; + + map = (struct rtw8852b_efuse *)log_map; + + efuse->country_code[0] = map->country_code[0]; + efuse->country_code[1] = map->country_code[1]; + rtw8852b_efuse_parsing_tssi(rtwdev, map); + rtw8852b_efuse_parsing_gain_offset(rtwdev, map); + + switch (rtwdev->hci.type) { + case RTW89_HCI_TYPE_PCIE: + rtw8852be_efuse_parsing(efuse, map); + break; + default: + return -EOPNOTSUPP; + } + + rtw89_info(rtwdev, "chip rfe_type is %d\n", efuse->rfe_type); + + return 0; +} + static u32 rtw8852b_bb_cal_txpwr_ref(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx, s16 ref) { @@ -269,6 +368,7 @@ static int rtw8852b_mac_disable_bb_rf(struct rtw89_dev *rtwdev) static const struct rtw89_chip_ops rtw8852b_chip_ops = { .enable_bb_rf = rtw8852b_mac_enable_bb_rf, .disable_bb_rf = rtw8852b_mac_disable_bb_rf, + .read_efuse = rtw8852b_read_efuse, .set_txpwr = rtw8852b_set_txpwr, .set_txpwr_ctrl = rtw8852b_set_txpwr_ctrl, .init_txpwr_unit = rtw8852b_init_txpwr_unit, @@ -280,6 +380,12 @@ const struct rtw89_chip_info rtw8852b_chip_info = { .fifo_size = 196608, .dle_scc_rsvd_size = 98304, .dle_mem = rtw8852b_dle_mem_pcie, + .sec_ctrl_efuse_size = 4, + .physical_efuse_size = 1216, + .logical_efuse_size = 2048, + .limit_efuse_size = 1280, + .dav_phy_efuse_size = 96, + .dav_log_efuse_size = 16, .dma_ch_mask = BIT(RTW89_DMA_ACH4) | BIT(RTW89_DMA_ACH5) | BIT(RTW89_DMA_ACH6) | BIT(RTW89_DMA_ACH7) | BIT(RTW89_DMA_B1MG) | BIT(RTW89_DMA_B1HI), diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852b.h b/drivers/net/wireless/realtek/rtw89/rtw8852b.h index a5ff269752a3..578fe55b6695 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852b.h +++ b/drivers/net/wireless/realtek/rtw89/rtw8852b.h @@ -10,4 +10,79 @@ #define RF_PATH_NUM_8852B 2 #define BB_PATH_NUM_8852B 2 +struct rtw8852b_u_efuse { + u8 rsvd[0x88]; + u8 mac_addr[ETH_ALEN]; +}; + +struct rtw8852b_e_efuse { + u8 mac_addr[ETH_ALEN]; +}; + +struct rtw8852b_tssi_offset { + u8 cck_tssi[TSSI_CCK_CH_GROUP_NUM]; + u8 bw40_tssi[TSSI_MCS_2G_CH_GROUP_NUM]; + u8 rsvd[7]; + u8 bw40_1s_tssi_5g[TSSI_MCS_5G_CH_GROUP_NUM]; +} __packed; + +struct rtw8852b_efuse { + u8 rsvd[0x210]; + struct rtw8852b_tssi_offset path_a_tssi; + u8 rsvd1[10]; + struct rtw8852b_tssi_offset path_b_tssi; + u8 rsvd2[94]; + u8 channel_plan; + u8 xtal_k; + u8 rsvd3; + u8 iqk_lck; + u8 rsvd4[5]; + u8 reg_setting:2; + u8 tx_diversity:1; + u8 rx_diversity:2; + u8 ac_mode:1; + u8 module_type:2; + u8 rsvd5; + u8 shared_ant:1; + u8 coex_type:3; + u8 ant_iso:1; + u8 radio_on_off:1; + u8 rsvd6:2; + u8 eeprom_version; + u8 customer_id; + u8 tx_bb_swing_2g; + u8 tx_bb_swing_5g; + u8 tx_cali_pwr_trk_mode; + u8 trx_path_selection; + u8 rfe_type; + u8 country_code[2]; + u8 rsvd7[3]; + u8 path_a_therm; + u8 path_b_therm; + u8 rsvd8[2]; + u8 rx_gain_2g_ofdm; + u8 rsvd9; + u8 rx_gain_2g_cck; + u8 rsvd10; + u8 rx_gain_5g_low; + u8 rsvd11; + u8 rx_gain_5g_mid; + u8 rsvd12; + u8 rx_gain_5g_high; + u8 rsvd13[35]; + u8 path_a_cck_pwr_idx[6]; + u8 path_a_bw40_1tx_pwr_idx[5]; + u8 path_a_ofdm_1tx_pwr_idx_diff:4; + u8 path_a_bw20_1tx_pwr_idx_diff:4; + u8 path_a_bw20_2tx_pwr_idx_diff:4; + u8 path_a_bw40_2tx_pwr_idx_diff:4; + u8 path_a_cck_2tx_pwr_idx_diff:4; + u8 path_a_ofdm_2tx_pwr_idx_diff:4; + u8 rsvd14[0xf2]; + union { + struct rtw8852b_u_efuse u; + struct rtw8852b_e_efuse e; + }; +} __packed; + #endif From 134cf7c01517d5cfaab940cacbb41525659de5f6 Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Wed, 28 Sep 2022 16:43:35 +0800 Subject: [PATCH 0029/2002] wifi: rtw89: 8852b: add chip_ops to read phy cap This efuse region is to store PHY calibration, and it is a separated region from the region that stores MAC address. Then, use these data to configure via chip_ops::power_trim that is a calibration mechanism of TX power. Signed-off-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20220928084336.34981-9-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/core.h | 4 + drivers/net/wireless/realtek/rtw89/rtw8852b.c | 184 ++++++++++++++++++ 2 files changed, 188 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index be39d2200e05..51af91b30fc4 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -84,6 +84,7 @@ enum rtw89_subband { RTW89_CH_6G_BAND_IDX7, /* Ultra-high */ RTW89_SUBBAND_NR, + RTW89_SUBBAND_2GHZ_5GHZ_NR = RTW89_CH_5G_BAND_4 + 1, }; enum rtw89_gain_offset { @@ -2196,6 +2197,7 @@ struct rtw89_sta { struct rtw89_efuse { bool valid; + bool power_k_valid; u8 xtal_cap; u8 addr[ETH_ALEN]; u8 rfe_type; @@ -3425,8 +3427,10 @@ struct rtw89_phy_bb_gain_info { struct rtw89_phy_efuse_gain { bool offset_valid; + bool comp_valid; s8 offset[RF_PATH_MAX][RTW89_GAIN_OFFSET_NR]; /* S(8, 0) */ s8 offset_base[RTW89_PHY_MAX]; /* S(8, 4) */ + s8 comp[RF_PATH_MAX][RTW89_SUBBAND_NR]; /* S(8, 0) */ }; struct rtw89_dev { diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852b.c b/drivers/net/wireless/realtek/rtw89/rtw8852b.c index b80102b1dd7f..e9bcea35a72a 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852b.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852b.c @@ -123,6 +123,186 @@ static int rtw8852b_read_efuse(struct rtw89_dev *rtwdev, u8 *log_map) return 0; } +static void rtw8852b_phycap_parsing_power_cal(struct rtw89_dev *rtwdev, u8 *phycap_map) +{ +#define PWR_K_CHK_OFFSET 0x5E9 +#define PWR_K_CHK_VALUE 0xAA + u32 offset = PWR_K_CHK_OFFSET - rtwdev->chip->phycap_addr; + + if (phycap_map[offset] == PWR_K_CHK_VALUE) + rtwdev->efuse.power_k_valid = true; +} + +static void rtw8852b_phycap_parsing_tssi(struct rtw89_dev *rtwdev, u8 *phycap_map) +{ + struct rtw89_tssi_info *tssi = &rtwdev->tssi; + static const u32 tssi_trim_addr[RF_PATH_NUM_8852B] = {0x5D6, 0x5AB}; + u32 addr = rtwdev->chip->phycap_addr; + bool pg = false; + u32 ofst; + u8 i, j; + + for (i = 0; i < RF_PATH_NUM_8852B; i++) { + for (j = 0; j < TSSI_TRIM_CH_GROUP_NUM; j++) { + /* addrs are in decreasing order */ + ofst = tssi_trim_addr[i] - addr - j; + tssi->tssi_trim[i][j] = phycap_map[ofst]; + + if (phycap_map[ofst] != 0xff) + pg = true; + } + } + + if (!pg) { + memset(tssi->tssi_trim, 0, sizeof(tssi->tssi_trim)); + rtw89_debug(rtwdev, RTW89_DBG_TSSI, + "[TSSI][TRIM] no PG, set all trim info to 0\n"); + } + + for (i = 0; i < RF_PATH_NUM_8852B; i++) + for (j = 0; j < TSSI_TRIM_CH_GROUP_NUM; j++) + rtw89_debug(rtwdev, RTW89_DBG_TSSI, + "[TSSI] path=%d idx=%d trim=0x%x addr=0x%x\n", + i, j, tssi->tssi_trim[i][j], + tssi_trim_addr[i] - j); +} + +static void rtw8852b_phycap_parsing_thermal_trim(struct rtw89_dev *rtwdev, + u8 *phycap_map) +{ + struct rtw89_power_trim_info *info = &rtwdev->pwr_trim; + static const u32 thm_trim_addr[RF_PATH_NUM_8852B] = {0x5DF, 0x5DC}; + u32 addr = rtwdev->chip->phycap_addr; + u8 i; + + for (i = 0; i < RF_PATH_NUM_8852B; i++) { + info->thermal_trim[i] = phycap_map[thm_trim_addr[i] - addr]; + + rtw89_debug(rtwdev, RTW89_DBG_RFK, + "[THERMAL][TRIM] path=%d thermal_trim=0x%x\n", + i, info->thermal_trim[i]); + + if (info->thermal_trim[i] != 0xff) + info->pg_thermal_trim = true; + } +} + +static void rtw8852b_thermal_trim(struct rtw89_dev *rtwdev) +{ +#define __thm_setting(raw) \ +({ \ + u8 __v = (raw); \ + ((__v & 0x1) << 3) | ((__v & 0x1f) >> 1); \ +}) + struct rtw89_power_trim_info *info = &rtwdev->pwr_trim; + u8 i, val; + + if (!info->pg_thermal_trim) { + rtw89_debug(rtwdev, RTW89_DBG_RFK, + "[THERMAL][TRIM] no PG, do nothing\n"); + + return; + } + + for (i = 0; i < RF_PATH_NUM_8852B; i++) { + val = __thm_setting(info->thermal_trim[i]); + rtw89_write_rf(rtwdev, i, RR_TM2, RR_TM2_OFF, val); + + rtw89_debug(rtwdev, RTW89_DBG_RFK, + "[THERMAL][TRIM] path=%d thermal_setting=0x%x\n", + i, val); + } +#undef __thm_setting +} + +static void rtw8852b_phycap_parsing_pa_bias_trim(struct rtw89_dev *rtwdev, + u8 *phycap_map) +{ + struct rtw89_power_trim_info *info = &rtwdev->pwr_trim; + static const u32 pabias_trim_addr[RF_PATH_NUM_8852B] = {0x5DE, 0x5DB}; + u32 addr = rtwdev->chip->phycap_addr; + u8 i; + + for (i = 0; i < RF_PATH_NUM_8852B; i++) { + info->pa_bias_trim[i] = phycap_map[pabias_trim_addr[i] - addr]; + + rtw89_debug(rtwdev, RTW89_DBG_RFK, + "[PA_BIAS][TRIM] path=%d pa_bias_trim=0x%x\n", + i, info->pa_bias_trim[i]); + + if (info->pa_bias_trim[i] != 0xff) + info->pg_pa_bias_trim = true; + } +} + +static void rtw8852b_pa_bias_trim(struct rtw89_dev *rtwdev) +{ + struct rtw89_power_trim_info *info = &rtwdev->pwr_trim; + u8 pabias_2g, pabias_5g; + u8 i; + + if (!info->pg_pa_bias_trim) { + rtw89_debug(rtwdev, RTW89_DBG_RFK, + "[PA_BIAS][TRIM] no PG, do nothing\n"); + + return; + } + + for (i = 0; i < RF_PATH_NUM_8852B; i++) { + pabias_2g = FIELD_GET(GENMASK(3, 0), info->pa_bias_trim[i]); + pabias_5g = FIELD_GET(GENMASK(7, 4), info->pa_bias_trim[i]); + + rtw89_debug(rtwdev, RTW89_DBG_RFK, + "[PA_BIAS][TRIM] path=%d 2G=0x%x 5G=0x%x\n", + i, pabias_2g, pabias_5g); + + rtw89_write_rf(rtwdev, i, RR_BIASA, RR_BIASA_TXG, pabias_2g); + rtw89_write_rf(rtwdev, i, RR_BIASA, RR_BIASA_TXA, pabias_5g); + } +} + +static void rtw8852b_phycap_parsing_gain_comp(struct rtw89_dev *rtwdev, u8 *phycap_map) +{ + static const u32 comp_addrs[][RTW89_SUBBAND_2GHZ_5GHZ_NR] = { + {0x5BB, 0x5BA, 0, 0x5B9, 0x5B8}, + {0x590, 0x58F, 0, 0x58E, 0x58D}, + }; + struct rtw89_phy_efuse_gain *gain = &rtwdev->efuse_gain; + u32 phycap_addr = rtwdev->chip->phycap_addr; + bool valid = false; + int path, i; + u8 data; + + for (path = 0; path < 2; path++) + for (i = 0; i < RTW89_SUBBAND_2GHZ_5GHZ_NR; i++) { + if (comp_addrs[path][i] == 0) + continue; + + data = phycap_map[comp_addrs[path][i] - phycap_addr]; + valid |= _decode_efuse_gain(data, NULL, + &gain->comp[path][i]); + } + + gain->comp_valid = valid; +} + +static int rtw8852b_read_phycap(struct rtw89_dev *rtwdev, u8 *phycap_map) +{ + rtw8852b_phycap_parsing_power_cal(rtwdev, phycap_map); + rtw8852b_phycap_parsing_tssi(rtwdev, phycap_map); + rtw8852b_phycap_parsing_thermal_trim(rtwdev, phycap_map); + rtw8852b_phycap_parsing_pa_bias_trim(rtwdev, phycap_map); + rtw8852b_phycap_parsing_gain_comp(rtwdev, phycap_map); + + return 0; +} + +static void rtw8852b_power_trim(struct rtw89_dev *rtwdev) +{ + rtw8852b_thermal_trim(rtwdev); + rtw8852b_pa_bias_trim(rtwdev); +} + static u32 rtw8852b_bb_cal_txpwr_ref(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx, s16 ref) { @@ -369,6 +549,8 @@ static const struct rtw89_chip_ops rtw8852b_chip_ops = { .enable_bb_rf = rtw8852b_mac_enable_bb_rf, .disable_bb_rf = rtw8852b_mac_disable_bb_rf, .read_efuse = rtw8852b_read_efuse, + .read_phycap = rtw8852b_read_phycap, + .power_trim = rtw8852b_power_trim, .set_txpwr = rtw8852b_set_txpwr, .set_txpwr_ctrl = rtw8852b_set_txpwr_ctrl, .init_txpwr_unit = rtw8852b_init_txpwr_unit, @@ -386,6 +568,8 @@ const struct rtw89_chip_info rtw8852b_chip_info = { .limit_efuse_size = 1280, .dav_phy_efuse_size = 96, .dav_log_efuse_size = 16, + .phycap_addr = 0x580, + .phycap_size = 128, .dma_ch_mask = BIT(RTW89_DMA_ACH4) | BIT(RTW89_DMA_ACH5) | BIT(RTW89_DMA_ACH6) | BIT(RTW89_DMA_ACH7) | BIT(RTW89_DMA_B1MG) | BIT(RTW89_DMA_B1HI), From 9695dc2e4be90315471ea4c672836929f5c403fe Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Wed, 28 Sep 2022 16:43:36 +0800 Subject: [PATCH 0030/2002] wifi: rtw89: 8852be: add 8852BE PCI entry 8852BE has two variants with different ID. One is 10ec:b852 that is a main model with 2x2 antenna, and the other is 10ec:b85b that is a 1x1 model. Signed-off-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20220928084336.34981-10-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/rtw8852b.h | 2 + .../net/wireless/realtek/rtw89/rtw8852be.c | 64 +++++++++++++++++++ 2 files changed, 66 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852b.h b/drivers/net/wireless/realtek/rtw89/rtw8852b.h index 578fe55b6695..bc0a383c4a39 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852b.h +++ b/drivers/net/wireless/realtek/rtw89/rtw8852b.h @@ -85,4 +85,6 @@ struct rtw8852b_efuse { }; } __packed; +extern const struct rtw89_chip_info rtw8852b_chip_info; + #endif diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852be.c b/drivers/net/wireless/realtek/rtw89/rtw8852be.c index 7bf95c38d3eb..0ef2ca8efeb0 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852be.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852be.c @@ -7,19 +7,83 @@ #include "pci.h" #include "reg.h" +#include "rtw8852b.h" static const struct rtw89_pci_info rtw8852b_pci_info = { + .txbd_trunc_mode = MAC_AX_BD_TRUNC, + .rxbd_trunc_mode = MAC_AX_BD_TRUNC, + .rxbd_mode = MAC_AX_RXBD_PKT, + .tag_mode = MAC_AX_TAG_MULTI, + .tx_burst = MAC_AX_TX_BURST_2048B, + .rx_burst = MAC_AX_RX_BURST_128B, + .wd_dma_idle_intvl = MAC_AX_WD_DMA_INTVL_256NS, + .wd_dma_act_intvl = MAC_AX_WD_DMA_INTVL_256NS, + .multi_tag_num = MAC_AX_TAG_NUM_8, + .lbc_en = MAC_AX_PCIE_ENABLE, + .lbc_tmr = MAC_AX_LBC_TMR_2MS, + .autok_en = MAC_AX_PCIE_DISABLE, + .io_rcy_en = MAC_AX_PCIE_DISABLE, + .io_rcy_tmr = MAC_AX_IO_RCY_ANA_TMR_6MS, + + .init_cfg_reg = R_AX_PCIE_INIT_CFG1, + .txhci_en_bit = B_AX_TXHCI_EN, + .rxhci_en_bit = B_AX_RXHCI_EN, + .rxbd_mode_bit = B_AX_RXBD_MODE, + .exp_ctrl_reg = R_AX_PCIE_EXP_CTRL, + .max_tag_num_mask = B_AX_MAX_TAG_NUM, + .rxbd_rwptr_clr_reg = R_AX_RXBD_RWPTR_CLR, + .txbd_rwptr_clr2_reg = 0, .dma_stop1 = {R_AX_PCIE_DMA_STOP1, B_AX_TX_STOP1_MASK_V1}, .dma_stop2 = {0}, .dma_busy1 = {R_AX_PCIE_DMA_BUSY1, DMA_BUSY1_CHECK_V1}, .dma_busy2_reg = 0, .dma_busy3_reg = R_AX_PCIE_DMA_BUSY1, + .rpwm_addr = R_AX_PCIE_HRPWM, + .cpwm_addr = R_AX_CPWM, .tx_dma_ch_mask = BIT(RTW89_TXCH_ACH4) | BIT(RTW89_TXCH_ACH5) | BIT(RTW89_TXCH_ACH6) | BIT(RTW89_TXCH_ACH7) | BIT(RTW89_TXCH_CH10) | BIT(RTW89_TXCH_CH11), + .bd_idx_addr_low_power = NULL, + .dma_addr_set = &rtw89_pci_ch_dma_addr_set, + + .ltr_set = rtw89_pci_ltr_set, + .fill_txaddr_info = rtw89_pci_fill_txaddr_info, + .config_intr_mask = rtw89_pci_config_intr_mask, + .enable_intr = rtw89_pci_enable_intr, + .disable_intr = rtw89_pci_disable_intr, + .recognize_intrs = rtw89_pci_recognize_intrs, }; +static const struct rtw89_driver_info rtw89_8852be_info = { + .chip = &rtw8852b_chip_info, + .bus = { + .pci = &rtw8852b_pci_info, + }, +}; + +static const struct pci_device_id rtw89_8852be_id_table[] = { + { + PCI_DEVICE(PCI_VENDOR_ID_REALTEK, 0xb852), + .driver_data = (kernel_ulong_t)&rtw89_8852be_info, + }, + { + PCI_DEVICE(PCI_VENDOR_ID_REALTEK, 0xb85b), + .driver_data = (kernel_ulong_t)&rtw89_8852be_info, + }, + {}, +}; +MODULE_DEVICE_TABLE(pci, rtw89_8852be_id_table); + +static struct pci_driver rtw89_8852be_driver = { + .name = "rtw89_8852be", + .id_table = rtw89_8852be_id_table, + .probe = rtw89_pci_probe, + .remove = rtw89_pci_remove, + .driver.pm = &rtw89_pm_ops, +}; +module_pci_driver(rtw89_8852be_driver); + MODULE_AUTHOR("Realtek Corporation"); MODULE_DESCRIPTION("Realtek 802.11ax wireless 8852BE driver"); MODULE_LICENSE("Dual BSD/GPL"); From c888183b21f36a247bb166ca9365705611bea847 Mon Sep 17 00:00:00 2001 From: Bitterblue Smith Date: Thu, 29 Sep 2022 00:16:46 +0300 Subject: [PATCH 0031/2002] wifi: rtl8xxxu: Support new chip RTL8188FU This chip is found in the cheapest USB adapters, e.g. 1.17 USD with VAT and shipping from China included. It's a gen 2 chip, similar to the RTL8723BU, but without Bluetooth. Features: 2.4 GHz, b/g/n mode, 1T1R, 150 Mbps. The vendor driver rtl8188fu version 4.3.23.6_20964.20170110 [0] was used as reference. The CD shipped with the device includes a newer driver, version 5.11.5-1-g12f7cde4b.20201102, but that one couldn't complete the WPA2 key exchange thing for whatever reason. [0] https://github.com/kelebek333/rtl8188fu Signed-off-by: Bitterblue Smith Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/b14f299d-3248-98fe-eee1-ba50d2e76c74@gmail.com --- .../net/wireless/realtek/rtl8xxxu/Makefile | 2 +- .../net/wireless/realtek/rtl8xxxu/rtl8xxxu.h | 56 + .../realtek/rtl8xxxu/rtl8xxxu_8188f.c | 1679 +++++++++++++++++ .../realtek/rtl8xxxu/rtl8xxxu_8192c.c | 1 + .../realtek/rtl8xxxu/rtl8xxxu_8192e.c | 1 + .../realtek/rtl8xxxu/rtl8xxxu_8723a.c | 1 + .../realtek/rtl8xxxu/rtl8xxxu_8723b.c | 3 +- .../wireless/realtek/rtl8xxxu/rtl8xxxu_core.c | 135 +- .../wireless/realtek/rtl8xxxu/rtl8xxxu_regs.h | 19 + 9 files changed, 1873 insertions(+), 24 deletions(-) create mode 100644 drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8188f.c diff --git a/drivers/net/wireless/realtek/rtl8xxxu/Makefile b/drivers/net/wireless/realtek/rtl8xxxu/Makefile index b278f8697cc0..c4ad5325f5e7 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/Makefile +++ b/drivers/net/wireless/realtek/rtl8xxxu/Makefile @@ -2,4 +2,4 @@ obj-$(CONFIG_RTL8XXXU) += rtl8xxxu.o rtl8xxxu-y := rtl8xxxu_core.o rtl8xxxu_8192e.o rtl8xxxu_8723b.o \ - rtl8xxxu_8723a.o rtl8xxxu_8192c.o + rtl8xxxu_8723a.o rtl8xxxu_8192c.o rtl8xxxu_8188f.o diff --git a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h index 782b089a2e1b..9950a2ee00aa 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h +++ b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h @@ -35,6 +35,7 @@ #define REALTEK_USB_CMD_IDX 0x00 #define TX_TOTAL_PAGE_NUM 0xf8 +#define TX_TOTAL_PAGE_NUM_8188F 0xf7 #define TX_TOTAL_PAGE_NUM_8192E 0xf3 #define TX_TOTAL_PAGE_NUM_8723B 0xf7 /* (HPQ + LPQ + NPQ + PUBQ) = TX_TOTAL_PAGE_NUM */ @@ -43,6 +44,11 @@ #define TX_PAGE_NUM_LO_PQ 0x02 #define TX_PAGE_NUM_NORM_PQ 0x02 +#define TX_PAGE_NUM_PUBQ_8188F 0xe5 +#define TX_PAGE_NUM_HI_PQ_8188F 0x0c +#define TX_PAGE_NUM_LO_PQ_8188F 0x02 +#define TX_PAGE_NUM_NORM_PQ_8188F 0x02 + #define TX_PAGE_NUM_PUBQ_8192E 0xe7 #define TX_PAGE_NUM_HI_PQ_8192E 0x08 #define TX_PAGE_NUM_LO_PQ_8192E 0x0c @@ -859,6 +865,50 @@ struct rtl8192eu_efuse { u8 res12[0xc3]; }; +struct rtl8188fu_efuse_tx_power { + u8 cck_base[6]; + u8 ht40_base[5]; + /* a: ofdm; b: ht20 */ + struct rtl8723au_idx ht20_ofdm_1s_diff; +}; + +struct rtl8188fu_efuse { + __le16 rtl_id; + u8 res0[0x0e]; + struct rtl8188fu_efuse_tx_power tx_power_index_A; /* 0x10 */ + u8 res1[0x9c]; /* 0x1c */ + u8 channel_plan; /* 0xb8 */ + u8 xtal_k; + u8 thermal_meter; + u8 iqk_lck; + u8 res2[5]; + u8 rf_board_option; + u8 rf_feature_option; + u8 rf_bt_setting; + u8 eeprom_version; + u8 eeprom_customer_id; + u8 res3[2]; + u8 kfree_thermal_k_on; + u8 rf_antenna_option; /* 0xc9 */ + u8 rfe_option; + u8 country_code; + u8 res4[4]; + u8 vid; /* 0xd0 */ + u8 res5[1]; + u8 pid; /* 0xd2 */ + u8 res6[1]; + u8 usb_optional_function; + u8 res7[2]; + u8 mac_addr[ETH_ALEN]; /* 0xd7 */ + u8 res8[2]; + u8 vendor_name[7]; + u8 res9[2]; + u8 device_name[7]; /* 0xe8 */ + u8 res10[0x41]; + u8 unknown[0x0d]; /* 0x130 */ + u8 res11[0xc3]; +}; + struct rtl8xxxu_reg8val { u16 reg; u8 val; @@ -1368,6 +1418,7 @@ struct rtl8xxxu_priv { struct rtl8723bu_efuse efuse8723bu; struct rtl8192cu_efuse efuse8192; struct rtl8192eu_efuse efuse8192eu; + struct rtl8188fu_efuse efuse8188fu; } efuse_wifi; u32 adda_backup[RTL8XXXU_ADDA_REGS]; u32 mac_backup[RTL8XXXU_MAC_REGS]; @@ -1414,6 +1465,7 @@ struct rtl8xxxu_fileops { void (*init_phy_bb) (struct rtl8xxxu_priv *priv); int (*init_phy_rf) (struct rtl8xxxu_priv *priv); void (*phy_init_antenna_selection) (struct rtl8xxxu_priv *priv); + void (*phy_lc_calibrate) (struct rtl8xxxu_priv *priv); void (*phy_iq_calibrate) (struct rtl8xxxu_priv *priv); void (*config_channel) (struct ieee80211_hw *hw); int (*parse_rx_desc) (struct rtl8xxxu_priv *priv, struct sk_buff *skb); @@ -1493,9 +1545,11 @@ int rtl8xxxu_init_phy_regs(struct rtl8xxxu_priv *priv, int rtl8xxxu_load_firmware(struct rtl8xxxu_priv *priv, char *fw_name); void rtl8xxxu_firmware_self_reset(struct rtl8xxxu_priv *priv); void rtl8xxxu_power_off(struct rtl8xxxu_priv *priv); +int rtl8xxxu_read_efuse8(struct rtl8xxxu_priv *priv, u16 offset, u8 *data); void rtl8xxxu_reset_8051(struct rtl8xxxu_priv *priv); int rtl8xxxu_auto_llt_table(struct rtl8xxxu_priv *priv); void rtl8xxxu_gen2_prepare_calibrate(struct rtl8xxxu_priv *priv, u8 start); +void rtl8723a_phy_lc_calibrate(struct rtl8xxxu_priv *priv); int rtl8xxxu_flush_fifo(struct rtl8xxxu_priv *priv); int rtl8xxxu_gen2_h2c_cmd(struct rtl8xxxu_priv *priv, struct h2c_cmd *h2c, int len); @@ -1539,7 +1593,9 @@ void rtl8xxxu_fill_txdesc_v2(struct ieee80211_hw *hw, struct ieee80211_hdr *hdr, u32 rts_rate); void rtl8723bu_set_ps_tdma(struct rtl8xxxu_priv *priv, u8 arg1, u8 arg2, u8 arg3, u8 arg4, u8 arg5); +void rtl8723bu_phy_init_antenna_selection(struct rtl8xxxu_priv *priv); +extern struct rtl8xxxu_fileops rtl8188fu_fops; extern struct rtl8xxxu_fileops rtl8192cu_fops; extern struct rtl8xxxu_fileops rtl8192eu_fops; extern struct rtl8xxxu_fileops rtl8723au_fops; diff --git a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8188f.c b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8188f.c new file mode 100644 index 000000000000..4ede766104c8 --- /dev/null +++ b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8188f.c @@ -0,0 +1,1679 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * RTL8XXXU mac80211 USB driver - 8188f specific subdriver + * + * Copyright (c) 2022 Bitterblue Smith + * + * Portions copied from existing rtl8xxxu code: + * Copyright (c) 2014 - 2017 Jes Sorensen + * + * Portions, notably calibration code: + * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "rtl8xxxu.h" +#include "rtl8xxxu_regs.h" + +static struct rtl8xxxu_reg8val rtl8188f_mac_init_table[] = { + {0x024, 0xDF}, {0x025, 0x07}, {0x02B, 0x1C}, {0x283, 0x20}, + {0x421, 0x0F}, {0x428, 0x0A}, {0x429, 0x10}, {0x430, 0x00}, + {0x431, 0x00}, {0x432, 0x00}, {0x433, 0x01}, {0x434, 0x04}, + {0x435, 0x05}, {0x436, 0x07}, {0x437, 0x08}, {0x43C, 0x04}, + {0x43D, 0x05}, {0x43E, 0x07}, {0x43F, 0x08}, {0x440, 0x5D}, + {0x441, 0x01}, {0x442, 0x00}, {0x444, 0x10}, {0x445, 0x00}, + {0x446, 0x00}, {0x447, 0x00}, {0x448, 0x00}, {0x449, 0xF0}, + {0x44A, 0x0F}, {0x44B, 0x3E}, {0x44C, 0x10}, {0x44D, 0x00}, + {0x44E, 0x00}, {0x44F, 0x00}, {0x450, 0x00}, {0x451, 0xF0}, + {0x452, 0x0F}, {0x453, 0x00}, {0x456, 0x5E}, {0x460, 0x44}, + {0x461, 0x44}, {0x4BC, 0xC0}, {0x4C8, 0xFF}, {0x4C9, 0x08}, + {0x4CC, 0xFF}, {0x4CD, 0xFF}, {0x4CE, 0x01}, {0x500, 0x26}, + {0x501, 0xA2}, {0x502, 0x2F}, {0x503, 0x00}, {0x504, 0x28}, + {0x505, 0xA3}, {0x506, 0x5E}, {0x507, 0x00}, {0x508, 0x2B}, + {0x509, 0xA4}, {0x50A, 0x5E}, {0x50B, 0x00}, {0x50C, 0x4F}, + {0x50D, 0xA4}, {0x50E, 0x00}, {0x50F, 0x00}, {0x512, 0x1C}, + {0x514, 0x0A}, {0x516, 0x0A}, {0x525, 0x4F}, {0x550, 0x10}, + {0x551, 0x10}, {0x559, 0x02}, {0x55C, 0x28}, {0x55D, 0xFF}, + {0x605, 0x30}, {0x608, 0x0E}, {0x609, 0x2A}, {0x620, 0xFF}, + {0x621, 0xFF}, {0x622, 0xFF}, {0x623, 0xFF}, {0x624, 0xFF}, + {0x625, 0xFF}, {0x626, 0xFF}, {0x627, 0xFF}, {0x638, 0x28}, + {0x63C, 0x0A}, {0x63D, 0x0A}, {0x63E, 0x0E}, {0x63F, 0x0E}, + {0x640, 0x40}, {0x642, 0x40}, {0x643, 0x00}, {0x652, 0xC8}, + {0x66E, 0x05}, {0x700, 0x21}, {0x701, 0x43}, {0x702, 0x65}, + {0x703, 0x87}, {0x708, 0x21}, {0x709, 0x43}, {0x70A, 0x65}, + {0x70B, 0x87}, + {0xffff, 0xff}, +}; + +static struct rtl8xxxu_reg32val rtl8188fu_phy_init_table[] = { + {0x800, 0x80045700}, {0x804, 0x00000001}, + {0x808, 0x0000FC00}, {0x80C, 0x0000000A}, + {0x810, 0x10001331}, {0x814, 0x020C3D10}, + {0x818, 0x00200385}, {0x81C, 0x00000000}, + {0x820, 0x01000100}, {0x824, 0x00390204}, + {0x828, 0x00000000}, {0x82C, 0x00000000}, + {0x830, 0x00000000}, {0x834, 0x00000000}, + {0x838, 0x00000000}, {0x83C, 0x00000000}, + {0x840, 0x00010000}, {0x844, 0x00000000}, + {0x848, 0x00000000}, {0x84C, 0x00000000}, + {0x850, 0x00030000}, {0x854, 0x00000000}, + {0x858, 0x569A569A}, {0x85C, 0x569A569A}, + {0x860, 0x00000130}, {0x864, 0x00000000}, + {0x868, 0x00000000}, {0x86C, 0x27272700}, + {0x870, 0x00000000}, {0x874, 0x25004000}, + {0x878, 0x00000808}, {0x87C, 0x004F0201}, + {0x880, 0xB0000B1E}, {0x884, 0x00000007}, + {0x888, 0x00000000}, {0x88C, 0xCCC000C0}, + {0x890, 0x00000800}, {0x894, 0xFFFFFFFE}, + {0x898, 0x40302010}, {0x89C, 0x00706050}, + {0x900, 0x00000000}, {0x904, 0x00000023}, + {0x908, 0x00000000}, {0x90C, 0x81121111}, + {0x910, 0x00000002}, {0x914, 0x00000201}, + {0x948, 0x99000000}, {0x94C, 0x00000010}, + {0x950, 0x20003000}, {0x954, 0x4A880000}, + {0x958, 0x4BC5D87A}, {0x95C, 0x04EB9B79}, + {0x96C, 0x00000003}, {0xA00, 0x00D047C8}, + {0xA04, 0x80FF800C}, {0xA08, 0x8C898300}, + {0xA0C, 0x2E7F120F}, {0xA10, 0x9500BB78}, + {0xA14, 0x1114D028}, {0xA18, 0x00881117}, + {0xA1C, 0x89140F00}, {0xA20, 0xD1D80000}, + {0xA24, 0x5A7DA0BD}, {0xA28, 0x0000223B}, + {0xA2C, 0x00D30000}, {0xA70, 0x101FBF00}, + {0xA74, 0x00000007}, {0xA78, 0x00000900}, + {0xA7C, 0x225B0606}, {0xA80, 0x218075B1}, + {0xA84, 0x00120000}, {0xA88, 0x040C0000}, + {0xA8C, 0x12345678}, {0xA90, 0xABCDEF00}, + {0xA94, 0x001B1B89}, {0xA98, 0x05100000}, + {0xA9C, 0x3F000000}, {0xAA0, 0x00000000}, + {0xB2C, 0x00000000}, {0xC00, 0x48071D40}, + {0xC04, 0x03A05611}, {0xC08, 0x000000E4}, + {0xC0C, 0x6C6C6C6C}, {0xC10, 0x18800000}, + {0xC14, 0x40000100}, {0xC18, 0x08800000}, + {0xC1C, 0x40000100}, {0xC20, 0x00000000}, + {0xC24, 0x00000000}, {0xC28, 0x00000000}, + {0xC2C, 0x00000000}, {0xC30, 0x69E9CC4A}, + {0xC34, 0x31000040}, {0xC38, 0x21688080}, + {0xC3C, 0x00001714}, {0xC40, 0x1F78403F}, + {0xC44, 0x00010036}, {0xC48, 0xEC020107}, + {0xC4C, 0x007F037F}, {0xC50, 0x69553420}, + {0xC54, 0x43BC0094}, {0xC58, 0x00013169}, + {0xC5C, 0x00250492}, {0xC60, 0x00000000}, + {0xC64, 0x7112848B}, {0xC68, 0x47C07BFF}, + {0xC6C, 0x00000036}, {0xC70, 0x2C7F000D}, + {0xC74, 0x020600DB}, {0xC78, 0x0000001F}, + {0xC7C, 0x00B91612}, {0xC80, 0x390000E4}, + {0xC84, 0x11F60000}, + {0xC88, 0x40000100}, {0xC8C, 0x20200000}, + {0xC90, 0x00091521}, {0xC94, 0x00000000}, + {0xC98, 0x00121820}, {0xC9C, 0x00007F7F}, + {0xCA0, 0x00000000}, {0xCA4, 0x000300A0}, + {0xCA8, 0x00000000}, {0xCAC, 0x00000000}, + {0xCB0, 0x00000000}, {0xCB4, 0x00000000}, + {0xCB8, 0x00000000}, {0xCBC, 0x28000000}, + {0xCC0, 0x00000000}, {0xCC4, 0x00000000}, + {0xCC8, 0x00000000}, {0xCCC, 0x00000000}, + {0xCD0, 0x00000000}, {0xCD4, 0x00000000}, + {0xCD8, 0x64B22427}, {0xCDC, 0x00766932}, + {0xCE0, 0x00222222}, {0xCE4, 0x10000000}, + {0xCE8, 0x37644302}, {0xCEC, 0x2F97D40C}, + {0xD00, 0x04030740}, {0xD04, 0x40020401}, + {0xD08, 0x0000907F}, {0xD0C, 0x20010201}, + {0xD10, 0xA0633333}, {0xD14, 0x3333BC53}, + {0xD18, 0x7A8F5B6F}, {0xD2C, 0xCB979975}, + {0xD30, 0x00000000}, {0xD34, 0x80608000}, + {0xD38, 0x98000000}, {0xD3C, 0x40127353}, + {0xD40, 0x00000000}, {0xD44, 0x00000000}, + {0xD48, 0x00000000}, {0xD4C, 0x00000000}, + {0xD50, 0x6437140A}, {0xD54, 0x00000000}, + {0xD58, 0x00000282}, {0xD5C, 0x30032064}, + {0xD60, 0x4653DE68}, {0xD64, 0x04518A3C}, + {0xD68, 0x00002101}, {0xD6C, 0x2A201C16}, + {0xD70, 0x1812362E}, {0xD74, 0x322C2220}, + {0xD78, 0x000E3C24}, {0xE00, 0x2D2D2D2D}, + {0xE04, 0x2D2D2D2D}, {0xE08, 0x0390272D}, + {0xE10, 0x2D2D2D2D}, {0xE14, 0x2D2D2D2D}, + {0xE18, 0x2D2D2D2D}, {0xE1C, 0x2D2D2D2D}, + {0xE28, 0x00000000}, {0xE30, 0x1000DC1F}, + {0xE34, 0x10008C1F}, {0xE38, 0x02140102}, + {0xE3C, 0x681604C2}, {0xE40, 0x01007C00}, + {0xE44, 0x01004800}, {0xE48, 0xFB000000}, + {0xE4C, 0x000028D1}, {0xE50, 0x1000DC1F}, + {0xE54, 0x10008C1F}, {0xE58, 0x02140102}, + {0xE5C, 0x28160D05}, {0xE60, 0x00000008}, + {0xE60, 0x021400A0}, {0xE64, 0x281600A0}, + {0xE6C, 0x01C00010}, {0xE70, 0x01C00010}, + {0xE74, 0x02000010}, {0xE78, 0x02000010}, + {0xE7C, 0x02000010}, {0xE80, 0x02000010}, + {0xE84, 0x01C00010}, {0xE88, 0x02000010}, + {0xE8C, 0x01C00010}, {0xED0, 0x01C00010}, + {0xED4, 0x01C00010}, {0xED8, 0x01C00010}, + {0xEDC, 0x00000010}, {0xEE0, 0x00000010}, + {0xEEC, 0x03C00010}, {0xF14, 0x00000003}, + {0xF4C, 0x00000000}, {0xF00, 0x00000300}, + {0xffff, 0xffffffff}, +}; + +static struct rtl8xxxu_reg32val rtl8188f_agc_table[] = { + {0xC78, 0xFC000001}, {0xC78, 0xFB010001}, + {0xC78, 0xFA020001}, {0xC78, 0xF9030001}, + {0xC78, 0xF8040001}, {0xC78, 0xF7050001}, + {0xC78, 0xF6060001}, {0xC78, 0xF5070001}, + {0xC78, 0xF4080001}, {0xC78, 0xF3090001}, + {0xC78, 0xF20A0001}, {0xC78, 0xF10B0001}, + {0xC78, 0xF00C0001}, {0xC78, 0xEF0D0001}, + {0xC78, 0xEE0E0001}, {0xC78, 0xED0F0001}, + {0xC78, 0xEC100001}, {0xC78, 0xEB110001}, + {0xC78, 0xEA120001}, {0xC78, 0xE9130001}, + {0xC78, 0xE8140001}, {0xC78, 0xE7150001}, + {0xC78, 0xE6160001}, {0xC78, 0xE5170001}, + {0xC78, 0xE4180001}, {0xC78, 0xE3190001}, + {0xC78, 0xE21A0001}, {0xC78, 0xE11B0001}, + {0xC78, 0xE01C0001}, {0xC78, 0xC21D0001}, + {0xC78, 0xC11E0001}, {0xC78, 0xC01F0001}, + {0xC78, 0xA5200001}, {0xC78, 0xA4210001}, + {0xC78, 0xA3220001}, {0xC78, 0xA2230001}, + {0xC78, 0xA1240001}, {0xC78, 0xA0250001}, + {0xC78, 0x65260001}, {0xC78, 0x64270001}, + {0xC78, 0x63280001}, {0xC78, 0x62290001}, + {0xC78, 0x612A0001}, {0xC78, 0x442B0001}, + {0xC78, 0x432C0001}, {0xC78, 0x422D0001}, + {0xC78, 0x412E0001}, {0xC78, 0x402F0001}, + {0xC78, 0x21300001}, {0xC78, 0x20310001}, + {0xC78, 0x05320001}, {0xC78, 0x04330001}, + {0xC78, 0x03340001}, {0xC78, 0x02350001}, + {0xC78, 0x01360001}, {0xC78, 0x00370001}, + {0xC78, 0x00380001}, {0xC78, 0x00390001}, + {0xC78, 0x003A0001}, {0xC78, 0x003B0001}, + {0xC78, 0x003C0001}, {0xC78, 0x003D0001}, + {0xC78, 0x003E0001}, {0xC78, 0x003F0001}, + {0xC50, 0x69553422}, {0xC50, 0x69553420}, + {0xffff, 0xffffffff} +}; + +static struct rtl8xxxu_rfregval rtl8188fu_radioa_init_table[] = { + {0x00, 0x00030000}, {0x08, 0x00008400}, + {0x18, 0x00000407}, {0x19, 0x00000012}, + {0x1B, 0x00001C6C}, + {0x1E, 0x00080009}, {0x1F, 0x00000880}, + {0x2F, 0x0001A060}, {0x3F, 0x00028000}, + {0x42, 0x000060C0}, {0x57, 0x000D0000}, + {0x58, 0x000C0160}, {0x67, 0x00001552}, + {0x83, 0x00000000}, {0xB0, 0x000FF9F0}, + {0xB1, 0x00022218}, {0xB2, 0x00034C00}, + {0xB4, 0x0004484B}, {0xB5, 0x0000112A}, + {0xB6, 0x0000053E}, {0xB7, 0x00010408}, + {0xB8, 0x00010200}, {0xB9, 0x00080001}, + {0xBA, 0x00040001}, {0xBB, 0x00000400}, + {0xBF, 0x000C0000}, {0xC2, 0x00002400}, + {0xC3, 0x00000009}, {0xC4, 0x00040C91}, + {0xC5, 0x00099999}, {0xC6, 0x000000A3}, + {0xC7, 0x0008F820}, {0xC8, 0x00076C06}, + {0xC9, 0x00000000}, {0xCA, 0x00080000}, + {0xDF, 0x00000180}, {0xEF, 0x000001A0}, + {0x51, 0x000E8333}, {0x52, 0x000FAC2C}, + {0x53, 0x00000103}, {0x56, 0x000517F0}, + {0x35, 0x00000099}, {0x35, 0x00000199}, + {0x35, 0x00000299}, {0x36, 0x00000064}, + {0x36, 0x00008064}, {0x36, 0x00010064}, + {0x36, 0x00018064}, {0x18, 0x00000C07}, + {0x5A, 0x00048000}, {0x19, 0x000739D0}, + {0x34, 0x0000ADD6}, {0x34, 0x00009DD3}, + {0x34, 0x00008CF4}, {0x34, 0x00007CF1}, + {0x34, 0x00006CEE}, {0x34, 0x00005CEB}, + {0x34, 0x00004CCE}, {0x34, 0x00003CCB}, + {0x34, 0x00002CC8}, {0x34, 0x00001C4B}, + {0x34, 0x00000C48}, + {0x00, 0x00030159}, {0x84, 0x00048000}, + {0x86, 0x0000002A}, {0x87, 0x00000025}, + {0x8E, 0x00065540}, {0x8F, 0x00088000}, + {0xEF, 0x000020A0}, {0x3B, 0x000F0F00}, + {0x3B, 0x000E0B00}, {0x3B, 0x000D0900}, + {0x3B, 0x000C0700}, {0x3B, 0x000B0600}, + {0x3B, 0x000A0400}, {0x3B, 0x00090200}, + {0x3B, 0x00080000}, {0x3B, 0x0007BF00}, + {0x3B, 0x00060B00}, {0x3B, 0x0005C900}, + {0x3B, 0x00040700}, {0x3B, 0x00030600}, + {0x3B, 0x0002D500}, {0x3B, 0x00010200}, + {0x3B, 0x0000E000}, {0xEF, 0x000000A0}, + {0xEF, 0x00000010}, {0x3B, 0x0000C0A8}, + {0x3B, 0x00010400}, {0xEF, 0x00000000}, + {0xEF, 0x00080000}, {0x30, 0x00010000}, + {0x31, 0x0000000F}, {0x32, 0x00007EFE}, + {0xEF, 0x00000000}, {0x00, 0x00010159}, + {0x18, 0x0000FC07}, {0xFE, 0x00000000}, + {0xFE, 0x00000000}, {0x1F, 0x00080003}, + {0xFE, 0x00000000}, {0xFE, 0x00000000}, + {0x1E, 0x00000001}, {0x1F, 0x00080000}, + {0x00, 0x00033D95}, + {0xff, 0xffffffff} +}; + +static struct rtl8xxxu_rfregval rtl8188fu_cut_b_radioa_init_table[] = { + {0x00, 0x00030000}, {0x08, 0x00008400}, + {0x18, 0x00000407}, {0x19, 0x00000012}, + {0x1B, 0x00001C6C}, + {0x1E, 0x00080009}, {0x1F, 0x00000880}, + {0x2F, 0x0001A060}, {0x3F, 0x00028000}, + {0x42, 0x000060C0}, {0x57, 0x000D0000}, + {0x58, 0x000C0160}, {0x67, 0x00001552}, + {0x83, 0x00000000}, {0xB0, 0x000FF9F0}, + {0xB1, 0x00022218}, {0xB2, 0x00034C00}, + {0xB4, 0x0004484B}, {0xB5, 0x0000112A}, + {0xB6, 0x0000053E}, {0xB7, 0x00010408}, + {0xB8, 0x00010200}, {0xB9, 0x00080001}, + {0xBA, 0x00040001}, {0xBB, 0x00000400}, + {0xBF, 0x000C0000}, {0xC2, 0x00002400}, + {0xC3, 0x00000009}, {0xC4, 0x00040C91}, + {0xC5, 0x00099999}, {0xC6, 0x000000A3}, + {0xC7, 0x0008F820}, {0xC8, 0x00076C06}, + {0xC9, 0x00000000}, {0xCA, 0x00080000}, + {0xDF, 0x00000180}, {0xEF, 0x000001A0}, + {0x51, 0x000E8231}, {0x52, 0x000FAC2C}, + {0x53, 0x00000141}, {0x56, 0x000517F0}, + {0x35, 0x00000090}, {0x35, 0x00000190}, + {0x35, 0x00000290}, {0x36, 0x00001064}, + {0x36, 0x00009064}, {0x36, 0x00011064}, + {0x36, 0x00019064}, {0x18, 0x00000C07}, + {0x5A, 0x00048000}, {0x19, 0x000739D0}, + {0x34, 0x0000ADD2}, {0x34, 0x00009DD0}, + {0x34, 0x00008CF3}, {0x34, 0x00007CF0}, + {0x34, 0x00006CED}, {0x34, 0x00005CD2}, + {0x34, 0x00004CCF}, {0x34, 0x00003CCC}, + {0x34, 0x00002CC9}, {0x34, 0x00001C4C}, + {0x34, 0x00000C49}, + {0x00, 0x00030159}, {0x84, 0x00048000}, + {0x86, 0x0000002A}, {0x87, 0x00000025}, + {0x8E, 0x00065540}, {0x8F, 0x00088000}, + {0xEF, 0x000020A0}, {0x3B, 0x000F0F00}, + {0x3B, 0x000E0B00}, {0x3B, 0x000D0900}, + {0x3B, 0x000C0700}, {0x3B, 0x000B0600}, + {0x3B, 0x000A0400}, {0x3B, 0x00090200}, + {0x3B, 0x00080000}, {0x3B, 0x0007BF00}, + {0x3B, 0x00060B00}, {0x3B, 0x0005C900}, + {0x3B, 0x00040700}, {0x3B, 0x00030600}, + {0x3B, 0x0002D500}, {0x3B, 0x00010200}, + {0x3B, 0x0000E000}, {0xEF, 0x000000A0}, + {0xEF, 0x00000010}, {0x3B, 0x0000C0A8}, + {0x3B, 0x00010400}, {0xEF, 0x00000000}, + {0xEF, 0x00080000}, {0x30, 0x00010000}, + {0x31, 0x0000000F}, {0x32, 0x00007EFE}, + {0xEF, 0x00000000}, {0x00, 0x00010159}, + {0x18, 0x0000FC07}, {0xFE, 0x00000000}, + {0xFE, 0x00000000}, {0x1F, 0x00080003}, + {0xFE, 0x00000000}, {0xFE, 0x00000000}, + {0x1E, 0x00000001}, {0x1F, 0x00080000}, + {0x00, 0x00033D95}, + {0xff, 0xffffffff} +}; + +static void rtl8xxxu_8188f_channel_to_group(int channel, int *group, int *cck_group) +{ + if (channel < 3) + *group = 0; + else if (channel < 6) + *group = 1; + else if (channel < 9) + *group = 2; + else if (channel < 12) + *group = 3; + else + *group = 4; + + if (channel == 14) + *cck_group = 5; + else + *cck_group = *group; +} + +static void +rtl8188f_set_tx_power(struct rtl8xxxu_priv *priv, int channel, bool ht40) +{ + u32 val32, ofdm, mcs; + u8 cck, ofdmbase, mcsbase; + int group, cck_group; + + rtl8xxxu_8188f_channel_to_group(channel, &group, &cck_group); + + cck = priv->cck_tx_power_index_A[cck_group]; + + val32 = rtl8xxxu_read32(priv, REG_TX_AGC_A_CCK1_MCS32); + val32 &= 0xffff00ff; + val32 |= (cck << 8); + rtl8xxxu_write32(priv, REG_TX_AGC_A_CCK1_MCS32, val32); + + val32 = rtl8xxxu_read32(priv, REG_TX_AGC_B_CCK11_A_CCK2_11); + val32 &= 0xff; + val32 |= ((cck << 8) | (cck << 16) | (cck << 24)); + rtl8xxxu_write32(priv, REG_TX_AGC_B_CCK11_A_CCK2_11, val32); + + ofdmbase = priv->ht40_1s_tx_power_index_A[group]; + ofdmbase += priv->ofdm_tx_power_diff[0].a; + ofdm = ofdmbase | ofdmbase << 8 | ofdmbase << 16 | ofdmbase << 24; + + rtl8xxxu_write32(priv, REG_TX_AGC_A_RATE18_06, ofdm); + rtl8xxxu_write32(priv, REG_TX_AGC_A_RATE54_24, ofdm); + + mcsbase = priv->ht40_1s_tx_power_index_A[group]; + if (ht40) + /* This diff is always 0 - not used in 8188FU. */ + mcsbase += priv->ht40_tx_power_diff[0].a; + else + mcsbase += priv->ht20_tx_power_diff[0].a; + mcs = mcsbase | mcsbase << 8 | mcsbase << 16 | mcsbase << 24; + + rtl8xxxu_write32(priv, REG_TX_AGC_A_MCS03_MCS00, mcs); + rtl8xxxu_write32(priv, REG_TX_AGC_A_MCS07_MCS04, mcs); + rtl8xxxu_write32(priv, REG_TX_AGC_A_MCS11_MCS08, mcs); + rtl8xxxu_write32(priv, REG_TX_AGC_A_MCS15_MCS12, mcs); +} + +/* A workaround to eliminate the 2400MHz, 2440MHz, 2480MHz spur of 8188F. */ +static void rtl8188f_spur_calibration(struct rtl8xxxu_priv *priv, u8 channel) +{ + static const u32 frequencies[14 + 1] = { + [5] = 0xFCCD, + [6] = 0xFC4D, + [7] = 0xFFCD, + [8] = 0xFF4D, + [11] = 0xFDCD, + [13] = 0xFCCD, + [14] = 0xFF9A + }; + + static const u32 reg_d40[14 + 1] = { + [5] = 0x06000000, + [6] = 0x00000600, + [13] = 0x06000000 + }; + + static const u32 reg_d44[14 + 1] = { + [11] = 0x04000000 + }; + + static const u32 reg_d4c[14 + 1] = { + [7] = 0x06000000, + [8] = 0x00000380, + [14] = 0x00180000 + }; + + const u8 threshold = 0x16; + bool do_notch, hw_ctrl, sw_ctrl, hw_ctrl_s1, sw_ctrl_s1; + u32 val32, initial_gain, reg948; + + val32 = rtl8xxxu_read32(priv, REG_OFDM0_RX_D_SYNC_PATH); + val32 |= GENMASK(28, 24); + rtl8xxxu_write32(priv, REG_OFDM0_RX_D_SYNC_PATH, val32); + + /* enable notch filter */ + val32 = rtl8xxxu_read32(priv, REG_OFDM0_RX_D_SYNC_PATH); + val32 |= BIT(9); + rtl8xxxu_write32(priv, REG_OFDM0_RX_D_SYNC_PATH, val32); + + if (channel <= 14 && frequencies[channel] > 0) { + reg948 = rtl8xxxu_read32(priv, REG_S0S1_PATH_SWITCH); + hw_ctrl = reg948 & BIT(6); + sw_ctrl = !hw_ctrl; + + if (hw_ctrl) { + val32 = rtl8xxxu_read32(priv, REG_FPGA0_XB_RF_INT_OE); + val32 &= GENMASK(5, 3); + hw_ctrl_s1 = val32 == BIT(3); + } else if (sw_ctrl) { + sw_ctrl_s1 = !(reg948 & BIT(9)); + } + + if (hw_ctrl_s1 || sw_ctrl_s1) { + initial_gain = rtl8xxxu_read32(priv, REG_OFDM0_XA_AGC_CORE1); + + /* Disable CCK block */ + val32 = rtl8xxxu_read32(priv, REG_FPGA0_RF_MODE); + val32 &= ~FPGA_RF_MODE_CCK; + rtl8xxxu_write32(priv, REG_FPGA0_RF_MODE, val32); + + val32 = initial_gain & ~OFDM0_X_AGC_CORE1_IGI_MASK; + val32 |= 0x30; + rtl8xxxu_write32(priv, REG_OFDM0_XA_AGC_CORE1, val32); + + /* disable 3-wire */ + rtl8xxxu_write32(priv, REG_FPGA0_ANALOG4, 0xccf000c0); + + /* Setup PSD */ + rtl8xxxu_write32(priv, REG_FPGA0_PSD_FUNC, frequencies[channel]); + + /* Start PSD */ + rtl8xxxu_write32(priv, REG_FPGA0_PSD_FUNC, 0x400000 | frequencies[channel]); + + msleep(30); + + do_notch = rtl8xxxu_read32(priv, REG_FPGA0_PSD_REPORT) >= threshold; + + /* turn off PSD */ + rtl8xxxu_write32(priv, REG_FPGA0_PSD_FUNC, frequencies[channel]); + + /* enable 3-wire */ + rtl8xxxu_write32(priv, REG_FPGA0_ANALOG4, 0xccc000c0); + + /* Enable CCK block */ + val32 = rtl8xxxu_read32(priv, REG_FPGA0_RF_MODE); + val32 |= FPGA_RF_MODE_CCK; + rtl8xxxu_write32(priv, REG_FPGA0_RF_MODE, val32); + + rtl8xxxu_write32(priv, REG_OFDM0_XA_AGC_CORE1, initial_gain); + + if (do_notch) { + rtl8xxxu_write32(priv, REG_OFDM1_CSI_FIX_MASK1, reg_d40[channel]); + rtl8xxxu_write32(priv, REG_OFDM1_CSI_FIX_MASK2, reg_d44[channel]); + rtl8xxxu_write32(priv, 0xd48, 0x0); + rtl8xxxu_write32(priv, 0xd4c, reg_d4c[channel]); + + /* enable CSI mask */ + val32 = rtl8xxxu_read32(priv, REG_OFDM1_CFO_TRACKING); + val32 |= BIT(28); + rtl8xxxu_write32(priv, REG_OFDM1_CFO_TRACKING, val32); + + return; + } + } + } + + /* disable CSI mask function */ + val32 = rtl8xxxu_read32(priv, REG_OFDM1_CFO_TRACKING); + val32 &= ~BIT(28); + rtl8xxxu_write32(priv, REG_OFDM1_CFO_TRACKING, val32); +} + +static void rtl8188fu_config_channel(struct ieee80211_hw *hw) +{ + struct rtl8xxxu_priv *priv = hw->priv; + u32 val32; + u8 channel, subchannel; + bool sec_ch_above; + + channel = (u8)hw->conf.chandef.chan->hw_value; + + /* Set channel */ + val32 = rtl8xxxu_read_rfreg(priv, RF_A, RF6052_REG_MODE_AG); + val32 &= ~MODE_AG_CHANNEL_MASK; + val32 |= channel; + rtl8xxxu_write_rfreg(priv, RF_A, RF6052_REG_MODE_AG, val32); + + /* Spur calibration */ + rtl8188f_spur_calibration(priv, channel); + + /* Set bandwidth mode */ + val32 = rtl8xxxu_read32(priv, REG_FPGA0_RF_MODE); + val32 &= ~FPGA_RF_MODE; + val32 |= hw->conf.chandef.width == NL80211_CHAN_WIDTH_40; + rtl8xxxu_write32(priv, REG_FPGA0_RF_MODE, val32); + + val32 = rtl8xxxu_read32(priv, REG_FPGA1_RF_MODE); + val32 &= ~FPGA_RF_MODE; + val32 |= hw->conf.chandef.width == NL80211_CHAN_WIDTH_40; + rtl8xxxu_write32(priv, REG_FPGA1_RF_MODE, val32); + + /* RXADC CLK */ + val32 = rtl8xxxu_read32(priv, REG_FPGA0_RF_MODE); + val32 |= GENMASK(10, 8); + rtl8xxxu_write32(priv, REG_FPGA0_RF_MODE, val32); + + /* TXDAC CLK */ + val32 = rtl8xxxu_read32(priv, REG_FPGA0_RF_MODE); + val32 |= BIT(14) | BIT(12); + val32 &= ~BIT(13); + rtl8xxxu_write32(priv, REG_FPGA0_RF_MODE, val32); + + /* small BW */ + val32 = rtl8xxxu_read32(priv, REG_OFDM0_TX_PSDO_NOISE_WEIGHT); + val32 &= ~GENMASK(31, 30); + rtl8xxxu_write32(priv, REG_OFDM0_TX_PSDO_NOISE_WEIGHT, val32); + + /* adc buffer clk */ + val32 = rtl8xxxu_read32(priv, REG_OFDM0_TX_PSDO_NOISE_WEIGHT); + val32 &= ~BIT(29); + val32 |= BIT(28); + rtl8xxxu_write32(priv, REG_OFDM0_TX_PSDO_NOISE_WEIGHT, val32); + + /* adc buffer clk */ + val32 = rtl8xxxu_read32(priv, REG_OFDM0_XA_RX_AFE); + val32 &= ~BIT(29); + val32 |= BIT(28); + rtl8xxxu_write32(priv, REG_OFDM0_XA_RX_AFE, val32); + + val32 = rtl8xxxu_read32(priv, REG_OFDM_RX_DFIR); + val32 &= ~BIT(19); + rtl8xxxu_write32(priv, REG_OFDM_RX_DFIR, val32); + + val32 = rtl8xxxu_read32(priv, REG_OFDM_RX_DFIR); + val32 &= ~GENMASK(23, 20); + val32 |= BIT(21); + if (hw->conf.chandef.width == NL80211_CHAN_WIDTH_20 || + hw->conf.chandef.width == NL80211_CHAN_WIDTH_20_NOHT) + val32 |= BIT(20); + else if (hw->conf.chandef.width == NL80211_CHAN_WIDTH_40) + val32 |= BIT(22); + rtl8xxxu_write32(priv, REG_OFDM_RX_DFIR, val32); + + if (hw->conf.chandef.width == NL80211_CHAN_WIDTH_40) { + if (hw->conf.chandef.center_freq1 > + hw->conf.chandef.chan->center_freq) { + sec_ch_above = 1; + channel += 2; + } else { + sec_ch_above = 0; + channel -= 2; + } + + /* Set Control channel to upper or lower. */ + val32 = rtl8xxxu_read32(priv, REG_CCK0_SYSTEM); + val32 &= ~CCK0_SIDEBAND; + if (!sec_ch_above) + val32 |= CCK0_SIDEBAND; + rtl8xxxu_write32(priv, REG_CCK0_SYSTEM, val32); + + val32 = rtl8xxxu_read32(priv, REG_DATA_SUBCHANNEL); + val32 &= ~GENMASK(3, 0); + if (sec_ch_above) + subchannel = 2; + else + subchannel = 1; + val32 |= subchannel; + rtl8xxxu_write32(priv, REG_DATA_SUBCHANNEL, val32); + + val32 = rtl8xxxu_read32(priv, REG_RESPONSE_RATE_SET); + val32 &= ~RSR_RSC_BANDWIDTH_40M; + rtl8xxxu_write32(priv, REG_RESPONSE_RATE_SET, val32); + } + + /* RF TRX_BW */ + val32 = channel; + if (hw->conf.chandef.width == NL80211_CHAN_WIDTH_20 || + hw->conf.chandef.width == NL80211_CHAN_WIDTH_20_NOHT) + val32 |= MODE_AG_BW_20MHZ_8723B; + else if (hw->conf.chandef.width == NL80211_CHAN_WIDTH_40) + val32 |= MODE_AG_BW_40MHZ_8723B; + rtl8xxxu_write_rfreg(priv, RF_A, RF6052_REG_MODE_AG, val32); + + /* FILTER BW&RC Corner (ACPR) */ + if (hw->conf.chandef.width == NL80211_CHAN_WIDTH_20 || + hw->conf.chandef.width == NL80211_CHAN_WIDTH_20_NOHT) + val32 = 0x00065; + else if (hw->conf.chandef.width == NL80211_CHAN_WIDTH_40) + val32 = 0x00025; + rtl8xxxu_write_rfreg(priv, RF_A, RF6052_REG_RXG_MIX_SWBW, val32); + + if (hw->conf.chandef.width == NL80211_CHAN_WIDTH_20 || + hw->conf.chandef.width == NL80211_CHAN_WIDTH_20_NOHT) + val32 = 0x0; + else if (hw->conf.chandef.width == NL80211_CHAN_WIDTH_40) + val32 = 0x01000; + rtl8xxxu_write_rfreg(priv, RF_A, RF6052_REG_RX_BB2, val32); + + /* RC Corner */ + rtl8xxxu_write_rfreg(priv, RF_A, RF6052_REG_UNKNOWN_DF, 0x00140); + rtl8xxxu_write_rfreg(priv, RF_A, RF6052_REG_RX_G2, 0x01c6c); +} + +static void rtl8188fu_init_aggregation(struct rtl8xxxu_priv *priv) +{ + u8 agg_ctrl, rxdma_mode, usb_tx_agg_desc_num = 6; + u32 agg_rx, val32; + + /* TX aggregation */ + val32 = rtl8xxxu_read32(priv, REG_DWBCN0_CTRL_8188F); + val32 &= ~(0xf << 4); + val32 |= usb_tx_agg_desc_num << 4; + rtl8xxxu_write32(priv, REG_DWBCN0_CTRL_8188F, val32); + rtl8xxxu_write8(priv, REG_DWBCN1_CTRL_8723B, usb_tx_agg_desc_num << 1); + + /* RX aggregation */ + agg_ctrl = rtl8xxxu_read8(priv, REG_TRXDMA_CTRL); + agg_ctrl &= ~TRXDMA_CTRL_RXDMA_AGG_EN; + + agg_rx = rtl8xxxu_read32(priv, REG_RXDMA_AGG_PG_TH); + agg_rx &= ~RXDMA_USB_AGG_ENABLE; + agg_rx &= ~0xFF0F; /* reset agg size and timeout */ + + rxdma_mode = rtl8xxxu_read8(priv, REG_RXDMA_PRO_8723B); + rxdma_mode &= ~BIT(1); + + rtl8xxxu_write8(priv, REG_TRXDMA_CTRL, agg_ctrl); + rtl8xxxu_write32(priv, REG_RXDMA_AGG_PG_TH, agg_rx); + rtl8xxxu_write8(priv, REG_RXDMA_PRO_8723B, rxdma_mode); +} + +static void rtl8188fu_init_statistics(struct rtl8xxxu_priv *priv) +{ + u32 val32; + + /* Time duration for NHM unit: 4us, 0xc350=200ms */ + rtl8xxxu_write16(priv, REG_NHM_TIMER_8723B + 2, 0xc350); + rtl8xxxu_write16(priv, REG_NHM_TH9_TH10_8723B + 2, 0xffff); + rtl8xxxu_write32(priv, REG_NHM_TH3_TO_TH0_8723B, 0xffffff50); + rtl8xxxu_write32(priv, REG_NHM_TH7_TO_TH4_8723B, 0xffffffff); + + /* TH8 */ + val32 = rtl8xxxu_read32(priv, REG_FPGA0_IQK); + val32 |= 0xff; + rtl8xxxu_write32(priv, REG_FPGA0_IQK, val32); + + /* Enable CCK */ + val32 = rtl8xxxu_read32(priv, REG_NHM_TH9_TH10_8723B); + val32 &= ~(BIT(8) | BIT(9) | BIT(10)); + val32 |= BIT(8); + rtl8xxxu_write32(priv, REG_NHM_TH9_TH10_8723B, val32); + + /* Max power amongst all RX antennas */ + val32 = rtl8xxxu_read32(priv, REG_OFDM0_FA_RSTC); + val32 |= BIT(7); + rtl8xxxu_write32(priv, REG_OFDM0_FA_RSTC, val32); +} + +static int rtl8188fu_parse_efuse(struct rtl8xxxu_priv *priv) +{ + struct rtl8188fu_efuse *efuse = &priv->efuse_wifi.efuse8188fu; + int i; + + if (efuse->rtl_id != cpu_to_le16(0x8129)) + return -EINVAL; + + ether_addr_copy(priv->mac_addr, efuse->mac_addr); + + memcpy(priv->cck_tx_power_index_A, efuse->tx_power_index_A.cck_base, + sizeof(efuse->tx_power_index_A.cck_base)); + + memcpy(priv->ht40_1s_tx_power_index_A, + efuse->tx_power_index_A.ht40_base, + sizeof(efuse->tx_power_index_A.ht40_base)); + + priv->ofdm_tx_power_diff[0].a = efuse->tx_power_index_A.ht20_ofdm_1s_diff.a; + priv->ht20_tx_power_diff[0].a = efuse->tx_power_index_A.ht20_ofdm_1s_diff.b; + + priv->xtalk = efuse->xtal_k & 0x3f; + + dev_info(&priv->udev->dev, "Vendor: %.7s\n", efuse->vendor_name); + dev_info(&priv->udev->dev, "Product: %.7s\n", efuse->device_name); + + if (rtl8xxxu_debug & RTL8XXXU_DEBUG_EFUSE) { + unsigned char *raw = priv->efuse_wifi.raw; + + dev_info(&priv->udev->dev, + "%s: dumping efuse (0x%02zx bytes):\n", + __func__, sizeof(struct rtl8188fu_efuse)); + for (i = 0; i < sizeof(struct rtl8188fu_efuse); i += 8) + dev_info(&priv->udev->dev, "%02x: %8ph\n", i, &raw[i]); + } + + return 0; +} + +static int rtl8188fu_load_firmware(struct rtl8xxxu_priv *priv) +{ + char *fw_name; + int ret; + + fw_name = "rtlwifi/rtl8188fufw.bin"; + + ret = rtl8xxxu_load_firmware(priv, fw_name); + + return ret; +} + +static void rtl8188fu_init_phy_bb(struct rtl8xxxu_priv *priv) +{ + u8 val8; + u16 val16; + u32 val32; + + /* Enable BB and RF */ + val16 = rtl8xxxu_read16(priv, REG_SYS_FUNC); + val16 |= SYS_FUNC_BB_GLB_RSTN | SYS_FUNC_BBRSTB | SYS_FUNC_DIO_RF; + rtl8xxxu_write16(priv, REG_SYS_FUNC, val16); + + /* + * Per vendor driver, run power sequence before init of RF + */ + val8 = RF_ENABLE | RF_RSTB | RF_SDMRSTB; + rtl8xxxu_write8(priv, REG_RF_CTRL, val8); + + usleep_range(10, 20); + + rtl8xxxu_write_rfreg(priv, RF_A, RF6052_REG_IQADJ_G1, 0x780); + + val8 = SYS_FUNC_BB_GLB_RSTN | SYS_FUNC_BBRSTB | SYS_FUNC_USBA | SYS_FUNC_USBD; + rtl8xxxu_write8(priv, REG_SYS_FUNC, val8); + + rtl8xxxu_init_phy_regs(priv, rtl8188fu_phy_init_table); + rtl8xxxu_init_phy_regs(priv, rtl8188f_agc_table); + + val32 = rtl8xxxu_read32(priv, REG_AFE_XTAL_CTRL); + val8 = priv->xtalk; + val32 &= ~0x007FF800; + val32 |= ((val8 | (val8 << 6)) << 11); + rtl8xxxu_write32(priv, REG_AFE_XTAL_CTRL, val32); +} + +static int rtl8188fu_init_phy_rf(struct rtl8xxxu_priv *priv) +{ + int ret; + + if (priv->chip_cut == 1) + ret = rtl8xxxu_init_phy_rf(priv, rtl8188fu_cut_b_radioa_init_table, RF_A); + else + ret = rtl8xxxu_init_phy_rf(priv, rtl8188fu_radioa_init_table, RF_A); + + return ret; +} + +static void rtl8188f_phy_lc_calibrate(struct rtl8xxxu_priv *priv) +{ + u32 val32; + u32 rf_amode, lstf; + int i; + + /* Check continuous TX and Packet TX */ + lstf = rtl8xxxu_read32(priv, REG_OFDM1_LSTF); + + if (lstf & OFDM_LSTF_MASK) { + /* Disable all continuous TX */ + val32 = lstf & ~OFDM_LSTF_MASK; + rtl8xxxu_write32(priv, REG_OFDM1_LSTF, val32); + } else { + /* Deal with Packet TX case */ + /* block all queues */ + rtl8xxxu_write8(priv, REG_TXPAUSE, 0xff); + } + + /* Read original RF mode Path A */ + rf_amode = rtl8xxxu_read_rfreg(priv, RF_A, RF6052_REG_MODE_AG); + + /* Start LC calibration */ + rtl8xxxu_write_rfreg(priv, RF_A, RF6052_REG_MODE_AG, rf_amode | 0x08000); + + for (i = 0; i < 100; i++) { + if ((rtl8xxxu_read_rfreg(priv, RF_A, RF6052_REG_MODE_AG) & 0x08000) == 0) + break; + msleep(10); + } + + if (i == 100) + dev_warn(&priv->udev->dev, "LC calibration timed out.\n"); + + rtl8xxxu_write_rfreg(priv, RF_A, RF6052_REG_MODE_AG, rf_amode); + + /* Restore original parameters */ + if (lstf & OFDM_LSTF_MASK) + rtl8xxxu_write32(priv, REG_OFDM1_LSTF, lstf); + else /* Deal with Packet TX case */ + rtl8xxxu_write8(priv, REG_TXPAUSE, 0x00); +} + +static int rtl8188fu_iqk_path_a(struct rtl8xxxu_priv *priv, u32 *lok_result) +{ + u32 reg_eac, reg_e94, reg_e9c, val32; + int result = 0; + + /* + * Leave IQK mode + */ + val32 = rtl8xxxu_read32(priv, REG_FPGA0_IQK); + val32 &= 0x000000ff; + rtl8xxxu_write32(priv, REG_FPGA0_IQK, val32); + + /* + * Enable path A PA in TX IQK mode + */ + val32 = rtl8xxxu_read_rfreg(priv, RF_A, RF6052_REG_WE_LUT); + val32 |= 0x80000; + rtl8xxxu_write_rfreg(priv, RF_A, RF6052_REG_WE_LUT, val32); + rtl8xxxu_write_rfreg(priv, RF_A, RF6052_REG_RCK_OS, 0x20000); + rtl8xxxu_write_rfreg(priv, RF_A, RF6052_REG_TXPA_G1, 0x0000f); + rtl8xxxu_write_rfreg(priv, RF_A, RF6052_REG_TXPA_G2, 0x07ff7); + + /* PA,PAD gain adjust */ + rtl8xxxu_write_rfreg(priv, RF_A, RF6052_REG_UNKNOWN_DF, 0x980); + rtl8xxxu_write_rfreg(priv, RF_A, RF6052_REG_UNKNOWN_56, 0x5102a); + + /* enter IQK mode */ + val32 = rtl8xxxu_read32(priv, REG_FPGA0_IQK); + val32 &= 0x000000ff; + val32 |= 0x80800000; + rtl8xxxu_write32(priv, REG_FPGA0_IQK, val32); + + /* path-A IQK setting */ + rtl8xxxu_write32(priv, REG_TX_IQK_TONE_A, 0x18008c1c); + rtl8xxxu_write32(priv, REG_RX_IQK_TONE_A, 0x38008c1c); + + rtl8xxxu_write32(priv, REG_TX_IQK_PI_A, 0x821403ff); + rtl8xxxu_write32(priv, REG_RX_IQK_PI_A, 0x28160000); + + /* LO calibration setting */ + rtl8xxxu_write32(priv, REG_IQK_AGC_RSP, 0x00462911); + + /* One shot, path A LOK & IQK */ + rtl8xxxu_write32(priv, REG_IQK_AGC_PTS, 0xf9000000); + rtl8xxxu_write32(priv, REG_IQK_AGC_PTS, 0xf8000000); + + mdelay(25); + + /* + * Leave IQK mode + */ + val32 = rtl8xxxu_read32(priv, REG_FPGA0_IQK); + val32 &= 0x000000ff; + rtl8xxxu_write32(priv, REG_FPGA0_IQK, val32); + + rtl8xxxu_write_rfreg(priv, RF_A, RF6052_REG_UNKNOWN_DF, 0x180); + + /* save LOK result */ + *lok_result = rtl8xxxu_read_rfreg(priv, RF_A, RF6052_REG_TXM_IDAC); + + /* Check failed */ + reg_eac = rtl8xxxu_read32(priv, REG_RX_POWER_AFTER_IQK_A_2); + reg_e94 = rtl8xxxu_read32(priv, REG_TX_POWER_BEFORE_IQK_A); + reg_e9c = rtl8xxxu_read32(priv, REG_TX_POWER_AFTER_IQK_A); + + if (!(reg_eac & BIT(28)) && + ((reg_e94 & 0x03ff0000) != 0x01420000) && + ((reg_e9c & 0x03ff0000) != 0x00420000)) + result |= 0x01; + + return result; +} + +static int rtl8188fu_rx_iqk_path_a(struct rtl8xxxu_priv *priv, u32 lok_result) +{ + u32 reg_ea4, reg_eac, reg_e94, reg_e9c, val32; + int result = 0; + + /* + * Leave IQK mode + */ + val32 = rtl8xxxu_read32(priv, REG_FPGA0_IQK); + val32 &= 0x000000ff; + rtl8xxxu_write32(priv, REG_FPGA0_IQK, val32); + + /* + * Enable path A PA in TX IQK mode + */ + val32 = rtl8xxxu_read_rfreg(priv, RF_A, RF6052_REG_WE_LUT); + val32 |= 0x80000; + rtl8xxxu_write_rfreg(priv, RF_A, RF6052_REG_WE_LUT, val32); + rtl8xxxu_write_rfreg(priv, RF_A, RF6052_REG_RCK_OS, 0x30000); + rtl8xxxu_write_rfreg(priv, RF_A, RF6052_REG_TXPA_G1, 0x0000f); + rtl8xxxu_write_rfreg(priv, RF_A, RF6052_REG_TXPA_G2, 0xf1173); + + /* PA,PAD gain adjust */ + rtl8xxxu_write_rfreg(priv, RF_A, RF6052_REG_UNKNOWN_DF, 0x980); + rtl8xxxu_write_rfreg(priv, RF_A, RF6052_REG_UNKNOWN_56, 0x5102a); + + /* + * Enter IQK mode + */ + val32 = rtl8xxxu_read32(priv, REG_FPGA0_IQK); + val32 &= 0x000000ff; + val32 |= 0x80800000; + rtl8xxxu_write32(priv, REG_FPGA0_IQK, val32); + + /* + * Tx IQK setting + */ + rtl8xxxu_write32(priv, REG_TX_IQK, 0x01007c00); + rtl8xxxu_write32(priv, REG_RX_IQK, 0x01004800); + + /* path-A IQK setting */ + rtl8xxxu_write32(priv, REG_TX_IQK_TONE_A, 0x10008c1c); + rtl8xxxu_write32(priv, REG_RX_IQK_TONE_A, 0x30008c1c); + + rtl8xxxu_write32(priv, REG_TX_IQK_PI_A, 0x82160fff); + rtl8xxxu_write32(priv, REG_RX_IQK_PI_A, 0x28160000); + + /* LO calibration setting */ + rtl8xxxu_write32(priv, REG_IQK_AGC_RSP, 0x00462911); + + /* One shot, path A LOK & IQK */ + rtl8xxxu_write32(priv, REG_IQK_AGC_PTS, 0xf9000000); + rtl8xxxu_write32(priv, REG_IQK_AGC_PTS, 0xf8000000); + + mdelay(25); + + /* + * Leave IQK mode + */ + val32 = rtl8xxxu_read32(priv, REG_FPGA0_IQK); + val32 &= 0x000000ff; + rtl8xxxu_write32(priv, REG_FPGA0_IQK, val32); + + rtl8xxxu_write_rfreg(priv, RF_A, RF6052_REG_UNKNOWN_DF, 0x180); + + /* Check failed */ + reg_eac = rtl8xxxu_read32(priv, REG_RX_POWER_AFTER_IQK_A_2); + reg_e94 = rtl8xxxu_read32(priv, REG_TX_POWER_BEFORE_IQK_A); + reg_e9c = rtl8xxxu_read32(priv, REG_TX_POWER_AFTER_IQK_A); + + if (!(reg_eac & BIT(28)) && + ((reg_e94 & 0x03ff0000) != 0x01420000) && + ((reg_e9c & 0x03ff0000) != 0x00420000)) + result |= 0x01; + else /* If TX not OK, ignore RX */ + goto out; + + val32 = 0x80007c00 | (reg_e94 & 0x3ff0000) | + ((reg_e9c & 0x3ff0000) >> 16); + rtl8xxxu_write32(priv, REG_TX_IQK, val32); + + /* + * Modify RX IQK mode table + */ + val32 = rtl8xxxu_read32(priv, REG_FPGA0_IQK); + val32 &= 0x000000ff; + rtl8xxxu_write32(priv, REG_FPGA0_IQK, val32); + + val32 = rtl8xxxu_read_rfreg(priv, RF_A, RF6052_REG_WE_LUT); + val32 |= 0x80000; + rtl8xxxu_write_rfreg(priv, RF_A, RF6052_REG_WE_LUT, val32); + rtl8xxxu_write_rfreg(priv, RF_A, RF6052_REG_RCK_OS, 0x30000); + rtl8xxxu_write_rfreg(priv, RF_A, RF6052_REG_TXPA_G1, 0x0000f); + rtl8xxxu_write_rfreg(priv, RF_A, RF6052_REG_TXPA_G2, 0xf7ff2); + + /* + * PA, PAD setting + */ + rtl8xxxu_write_rfreg(priv, RF_A, RF6052_REG_UNKNOWN_DF, 0x980); + rtl8xxxu_write_rfreg(priv, RF_A, RF6052_REG_UNKNOWN_56, 0x51000); + + /* + * Enter IQK mode + */ + val32 = rtl8xxxu_read32(priv, REG_FPGA0_IQK); + val32 &= 0x000000ff; + val32 |= 0x80800000; + rtl8xxxu_write32(priv, REG_FPGA0_IQK, val32); + + /* + * RX IQK setting + */ + rtl8xxxu_write32(priv, REG_RX_IQK, 0x01004800); + + /* path-A IQK setting */ + rtl8xxxu_write32(priv, REG_TX_IQK_TONE_A, 0x30008c1c); + rtl8xxxu_write32(priv, REG_RX_IQK_TONE_A, 0x10008c1c); + + rtl8xxxu_write32(priv, REG_TX_IQK_PI_A, 0x82160000); + rtl8xxxu_write32(priv, REG_RX_IQK_PI_A, 0x281613ff); + + /* LO calibration setting */ + rtl8xxxu_write32(priv, REG_IQK_AGC_RSP, 0x0046a911); + + /* One shot, path A LOK & IQK */ + rtl8xxxu_write32(priv, REG_IQK_AGC_PTS, 0xf9000000); + rtl8xxxu_write32(priv, REG_IQK_AGC_PTS, 0xf8000000); + + mdelay(25); + + /* + * Leave IQK mode + */ + val32 = rtl8xxxu_read32(priv, REG_FPGA0_IQK); + val32 &= 0x000000ff; + rtl8xxxu_write32(priv, REG_FPGA0_IQK, val32); + + rtl8xxxu_write_rfreg(priv, RF_A, RF6052_REG_UNKNOWN_DF, 0x180); + + /* reload LOK value */ + rtl8xxxu_write_rfreg(priv, RF_A, RF6052_REG_TXM_IDAC, lok_result); + + /* Check failed */ + reg_eac = rtl8xxxu_read32(priv, REG_RX_POWER_AFTER_IQK_A_2); + reg_ea4 = rtl8xxxu_read32(priv, REG_RX_POWER_BEFORE_IQK_A_2); + + if (!(reg_eac & BIT(27)) && + ((reg_ea4 & 0x03ff0000) != 0x01320000) && + ((reg_eac & 0x03ff0000) != 0x00360000)) + result |= 0x02; + +out: + return result; +} + +static void rtl8188fu_phy_iqcalibrate(struct rtl8xxxu_priv *priv, + int result[][8], int t) +{ + struct device *dev = &priv->udev->dev; + u32 i, val32, rx_initial_gain, lok_result; + u32 path_sel_bb, path_sel_rf; + int path_a_ok; + int retry = 2; + static const u32 adda_regs[RTL8XXXU_ADDA_REGS] = { + REG_FPGA0_XCD_SWITCH_CTRL, REG_BLUETOOTH, + REG_RX_WAIT_CCA, REG_TX_CCK_RFON, + REG_TX_CCK_BBON, REG_TX_OFDM_RFON, + REG_TX_OFDM_BBON, REG_TX_TO_RX, + REG_TX_TO_TX, REG_RX_CCK, + REG_RX_OFDM, REG_RX_WAIT_RIFS, + REG_RX_TO_RX, REG_STANDBY, + REG_SLEEP, REG_PMPD_ANAEN + }; + static const u32 iqk_mac_regs[RTL8XXXU_MAC_REGS] = { + REG_TXPAUSE, REG_BEACON_CTRL, + REG_BEACON_CTRL_1, REG_GPIO_MUXCFG + }; + static const u32 iqk_bb_regs[RTL8XXXU_BB_REGS] = { + REG_OFDM0_TRX_PATH_ENABLE, REG_OFDM0_TR_MUX_PAR, + REG_FPGA0_XCD_RF_SW_CTRL, REG_CONFIG_ANT_A, REG_CONFIG_ANT_B, + REG_FPGA0_XAB_RF_SW_CTRL, REG_FPGA0_XA_RF_INT_OE, + REG_FPGA0_XB_RF_INT_OE, REG_FPGA0_RF_MODE + }; + + /* + * Note: IQ calibration must be performed after loading + * PHY_REG.txt , and radio_a, radio_b.txt + */ + + rx_initial_gain = rtl8xxxu_read32(priv, REG_OFDM0_XA_AGC_CORE1); + + if (t == 0) { + /* Save ADDA parameters, turn Path A ADDA on */ + rtl8xxxu_save_regs(priv, adda_regs, priv->adda_backup, + RTL8XXXU_ADDA_REGS); + rtl8xxxu_save_mac_regs(priv, iqk_mac_regs, priv->mac_backup); + rtl8xxxu_save_regs(priv, iqk_bb_regs, + priv->bb_backup, RTL8XXXU_BB_REGS); + } + + rtl8xxxu_path_adda_on(priv, adda_regs, true); + + if (t == 0) { + val32 = rtl8xxxu_read32(priv, REG_FPGA0_XA_HSSI_PARM1); + priv->pi_enabled = val32 & FPGA0_HSSI_PARM1_PI; + } + + /* save RF path */ + path_sel_bb = rtl8xxxu_read32(priv, REG_S0S1_PATH_SWITCH); + path_sel_rf = rtl8xxxu_read_rfreg(priv, RF_A, RF6052_REG_S0S1); + + /* BB setting */ + rtl8xxxu_write32(priv, REG_OFDM0_TRX_PATH_ENABLE, 0x03a05600); + rtl8xxxu_write32(priv, REG_OFDM0_TR_MUX_PAR, 0x000800e4); + rtl8xxxu_write32(priv, REG_FPGA0_XCD_RF_SW_CTRL, 0x25204000); + + /* MAC settings */ + val32 = rtl8xxxu_read32(priv, REG_TX_PTCL_CTRL); + val32 |= 0x00ff0000; + rtl8xxxu_write32(priv, REG_TX_PTCL_CTRL, val32); + + /* IQ calibration setting */ + val32 = rtl8xxxu_read32(priv, REG_FPGA0_IQK); + val32 &= 0xff; + val32 |= 0x80800000; + rtl8xxxu_write32(priv, REG_FPGA0_IQK, val32); + rtl8xxxu_write32(priv, REG_TX_IQK, 0x01007c00); + rtl8xxxu_write32(priv, REG_RX_IQK, 0x01004800); + + for (i = 0; i < retry; i++) { + path_a_ok = rtl8188fu_iqk_path_a(priv, &lok_result); + if (path_a_ok == 0x01) { + val32 = rtl8xxxu_read32(priv, REG_FPGA0_IQK); + val32 &= 0xff; + rtl8xxxu_write32(priv, REG_FPGA0_IQK, val32); + + val32 = rtl8xxxu_read32(priv, + REG_TX_POWER_BEFORE_IQK_A); + result[t][0] = (val32 >> 16) & 0x3ff; + + val32 = rtl8xxxu_read32(priv, + REG_TX_POWER_AFTER_IQK_A); + result[t][1] = (val32 >> 16) & 0x3ff; + break; + } + } + + for (i = 0; i < retry; i++) { + path_a_ok = rtl8188fu_rx_iqk_path_a(priv, lok_result); + if (path_a_ok == 0x03) { + val32 = rtl8xxxu_read32(priv, + REG_RX_POWER_BEFORE_IQK_A_2); + result[t][2] = (val32 >> 16) & 0x3ff; + + val32 = rtl8xxxu_read32(priv, + REG_RX_POWER_AFTER_IQK_A_2); + result[t][3] = (val32 >> 16) & 0x3ff; + break; + } + } + + if (!path_a_ok) + dev_dbg(dev, "%s: Path A IQK failed!\n", __func__); + + /* Back to BB mode, load original value */ + val32 = rtl8xxxu_read32(priv, REG_FPGA0_IQK); + val32 &= 0xff; + rtl8xxxu_write32(priv, REG_FPGA0_IQK, val32); + + if (t == 0) + return; + + if (!priv->pi_enabled) { + /* + * Switch back BB to SI mode after finishing + * IQ Calibration + */ + val32 = 0x01000000; + rtl8xxxu_write32(priv, REG_FPGA0_XA_HSSI_PARM1, val32); + rtl8xxxu_write32(priv, REG_FPGA0_XB_HSSI_PARM1, val32); + } + + /* Reload ADDA power saving parameters */ + rtl8xxxu_restore_regs(priv, adda_regs, priv->adda_backup, + RTL8XXXU_ADDA_REGS); + + /* Reload MAC parameters */ + rtl8xxxu_restore_mac_regs(priv, iqk_mac_regs, priv->mac_backup); + + /* Reload BB parameters */ + rtl8xxxu_restore_regs(priv, iqk_bb_regs, + priv->bb_backup, RTL8XXXU_BB_REGS); + + /* Reload RF path */ + rtl8xxxu_write32(priv, REG_S0S1_PATH_SWITCH, path_sel_bb); + rtl8xxxu_write_rfreg(priv, RF_A, RF6052_REG_S0S1, path_sel_rf); + + /* Restore RX initial gain */ + val32 = rtl8xxxu_read32(priv, REG_OFDM0_XA_AGC_CORE1); + val32 &= 0xffffff00; + val32 |= 0x50; + rtl8xxxu_write32(priv, REG_OFDM0_XA_AGC_CORE1, val32); + val32 = rtl8xxxu_read32(priv, REG_OFDM0_XA_AGC_CORE1); + val32 &= 0xffffff00; + val32 |= rx_initial_gain & 0xff; + rtl8xxxu_write32(priv, REG_OFDM0_XA_AGC_CORE1, val32); + + /* Load 0xe30 IQC default value */ + rtl8xxxu_write32(priv, REG_TX_IQK_TONE_A, 0x01008c00); + rtl8xxxu_write32(priv, REG_RX_IQK_TONE_A, 0x01008c00); +} + +static void rtl8188fu_phy_iq_calibrate(struct rtl8xxxu_priv *priv) +{ + struct device *dev = &priv->udev->dev; + int result[4][8]; /* last is final result */ + int i, candidate; + bool path_a_ok; + u32 reg_e94, reg_e9c, reg_ea4, reg_eac; + u32 reg_eb4, reg_ebc, reg_ec4, reg_ecc; + s32 reg_tmp = 0; + bool simu; + u32 path_sel_bb, path_sel_rf; + + /* Save RF path */ + path_sel_bb = rtl8xxxu_read32(priv, REG_S0S1_PATH_SWITCH); + path_sel_rf = rtl8xxxu_read_rfreg(priv, RF_A, RF6052_REG_S0S1); + + memset(result, 0, sizeof(result)); + candidate = -1; + + path_a_ok = false; + + for (i = 0; i < 3; i++) { + rtl8188fu_phy_iqcalibrate(priv, result, i); + + if (i == 1) { + simu = rtl8xxxu_gen2_simularity_compare(priv, result, 0, 1); + if (simu) { + candidate = 0; + break; + } + } + + if (i == 2) { + simu = rtl8xxxu_gen2_simularity_compare(priv, result, 0, 2); + if (simu) { + candidate = 0; + break; + } + + simu = rtl8xxxu_gen2_simularity_compare(priv, result, 1, 2); + if (simu) { + candidate = 1; + } else { + for (i = 0; i < 8; i++) + reg_tmp += result[3][i]; + + if (reg_tmp) + candidate = 3; + else + candidate = -1; + } + } + } + + for (i = 0; i < 4; i++) { + reg_e94 = result[i][0]; + reg_e9c = result[i][1]; + reg_ea4 = result[i][2]; + reg_eac = result[i][3]; + reg_eb4 = result[i][4]; + reg_ebc = result[i][5]; + reg_ec4 = result[i][6]; + reg_ecc = result[i][7]; + } + + if (candidate >= 0) { + reg_e94 = result[candidate][0]; + priv->rege94 = reg_e94; + reg_e9c = result[candidate][1]; + priv->rege9c = reg_e9c; + reg_ea4 = result[candidate][2]; + reg_eac = result[candidate][3]; + reg_eb4 = result[candidate][4]; + priv->regeb4 = reg_eb4; + reg_ebc = result[candidate][5]; + priv->regebc = reg_ebc; + reg_ec4 = result[candidate][6]; + reg_ecc = result[candidate][7]; + dev_dbg(dev, "%s: candidate is %x\n", __func__, candidate); + dev_dbg(dev, + "%s: e94 =%x e9c=%x ea4=%x eac=%x eb4=%x ebc=%x ec4=%x ecc=%x\n", + __func__, reg_e94, reg_e9c, + reg_ea4, reg_eac, reg_eb4, reg_ebc, reg_ec4, reg_ecc); + path_a_ok = true; + } else { + reg_e94 = reg_eb4 = priv->rege94 = priv->regeb4 = 0x100; + reg_e9c = reg_ebc = priv->rege9c = priv->regebc = 0x0; + } + + if (reg_e94 && candidate >= 0) + rtl8xxxu_fill_iqk_matrix_a(priv, path_a_ok, result, + candidate, (reg_ea4 == 0)); + + rtl8xxxu_save_regs(priv, rtl8xxxu_iqk_phy_iq_bb_reg, + priv->bb_recovery_backup, RTL8XXXU_BB_REGS); + + rtl8xxxu_write32(priv, REG_S0S1_PATH_SWITCH, path_sel_bb); + rtl8xxxu_write_rfreg(priv, RF_A, RF6052_REG_S0S1, path_sel_rf); +} + +static void rtl8188f_disabled_to_emu(struct rtl8xxxu_priv *priv) +{ + u16 val8; + + /* 0x04[12:11] = 2b'01enable WL suspend */ + val8 = rtl8xxxu_read8(priv, REG_APS_FSMCO + 1); + val8 &= ~((APS_FSMCO_PCIE | APS_FSMCO_HW_SUSPEND) >> 8); + rtl8xxxu_write8(priv, REG_APS_FSMCO + 1, val8); + + /* 0xC4[4] <= 1, turn off USB APHY LDO under suspend mode */ + val8 = rtl8xxxu_read8(priv, 0xc4); + val8 &= ~BIT(4); + rtl8xxxu_write8(priv, 0xc4, val8); +} + +static int rtl8188f_emu_to_active(struct rtl8xxxu_priv *priv) +{ + u8 val8; + u32 val32; + int count, ret = 0; + + /* Disable SW LPS */ + val8 = rtl8xxxu_read8(priv, REG_APS_FSMCO + 1); + val8 &= ~(APS_FSMCO_SW_LPS >> 8); + rtl8xxxu_write8(priv, REG_APS_FSMCO + 1, val8); + + /* wait till 0x04[17] = 1 power ready */ + for (count = RTL8XXXU_MAX_REG_POLL; count; count--) { + val32 = rtl8xxxu_read32(priv, REG_APS_FSMCO); + if (val32 & BIT(17)) + break; + + udelay(10); + } + + if (!count) { + ret = -EBUSY; + goto exit; + } + + /* Disable HWPDN */ + val8 = rtl8xxxu_read8(priv, REG_APS_FSMCO + 1); + val8 &= ~(APS_FSMCO_HW_POWERDOWN >> 8); + rtl8xxxu_write8(priv, REG_APS_FSMCO + 1, val8); + + /* Disable WL suspend */ + val8 = rtl8xxxu_read8(priv, REG_APS_FSMCO + 1); + val8 &= ~(APS_FSMCO_HW_SUSPEND >> 8); + rtl8xxxu_write8(priv, REG_APS_FSMCO + 1, val8); + + /* set, then poll until 0 */ + val8 = rtl8xxxu_read8(priv, REG_APS_FSMCO + 1); + val8 |= APS_FSMCO_MAC_ENABLE >> 8; + rtl8xxxu_write8(priv, REG_APS_FSMCO + 1, val8); + + for (count = RTL8XXXU_MAX_REG_POLL; count; count--) { + val32 = rtl8xxxu_read32(priv, REG_APS_FSMCO); + if ((val32 & APS_FSMCO_MAC_ENABLE) == 0) { + ret = 0; + break; + } + udelay(10); + } + + if (!count) { + ret = -EBUSY; + goto exit; + } + + /* 0x27<=35 to reduce RF noise */ + val8 = rtl8xxxu_write8(priv, 0x27, 0x35); +exit: + return ret; +} + +static int rtl8188fu_active_to_emu(struct rtl8xxxu_priv *priv) +{ + u8 val8; + u32 val32; + int count, ret = 0; + + /* Turn off RF */ + rtl8xxxu_write8(priv, REG_RF_CTRL, 0); + + /* 0x4C[23] = 0x4E[7] = 0, switch DPDT_SEL_P output from register 0x65[2] */ + val8 = rtl8xxxu_read8(priv, 0x4e); + val8 &= ~BIT(7); + rtl8xxxu_write8(priv, 0x4e, val8); + + /* 0x27 <= 34, xtal_qsel = 0 to xtal bring up */ + rtl8xxxu_write8(priv, 0x27, 0x34); + + /* 0x04[9] = 1 turn off MAC by HW state machine */ + val8 = rtl8xxxu_read8(priv, REG_APS_FSMCO + 1); + val8 |= APS_FSMCO_MAC_OFF >> 8; + rtl8xxxu_write8(priv, REG_APS_FSMCO + 1, val8); + + for (count = RTL8XXXU_MAX_REG_POLL; count; count--) { + val32 = rtl8xxxu_read32(priv, REG_APS_FSMCO); + if ((val32 & APS_FSMCO_MAC_OFF) == 0) { + ret = 0; + break; + } + udelay(10); + } + + if (!count) { + ret = -EBUSY; + goto exit; + } + +exit: + return ret; +} + +static int rtl8188fu_emu_to_disabled(struct rtl8xxxu_priv *priv) +{ + u8 val8; + + /* 0x04[12:11] = 2b'01 enable WL suspend */ + val8 = rtl8xxxu_read8(priv, REG_APS_FSMCO + 1); + val8 &= ~((APS_FSMCO_PCIE | APS_FSMCO_HW_SUSPEND) >> 8); + val8 |= APS_FSMCO_HW_SUSPEND >> 8; + rtl8xxxu_write8(priv, REG_APS_FSMCO + 1, val8); + + /* 0xC4[4] <= 1, turn off USB APHY LDO under suspend mode */ + val8 = rtl8xxxu_read8(priv, 0xc4); + val8 |= BIT(4); + rtl8xxxu_write8(priv, 0xc4, val8); + + return 0; +} + +static int rtl8188fu_active_to_lps(struct rtl8xxxu_priv *priv) +{ + struct device *dev = &priv->udev->dev; + u8 val8; + u16 val16; + u32 val32; + int retry, retval; + + /* set RPWM IMR */ + val8 = rtl8xxxu_read8(priv, REG_FTIMR + 1); + val8 |= IMR0_CPWM >> 8; + rtl8xxxu_write8(priv, REG_FTIMR + 1, val8); + + /* Tx Pause */ + rtl8xxxu_write8(priv, REG_TXPAUSE, 0xff); + + retry = 100; + retval = -EBUSY; + + /* + * Poll 32 bit wide REG_SCH_TX_CMD for 0x00000000 to ensure no TX is pending. + */ + do { + val32 = rtl8xxxu_read32(priv, REG_SCH_TX_CMD); + if (!val32) { + retval = 0; + break; + } + } while (retry--); + + if (!retry) { + dev_warn(dev, "Failed to flush TX queue\n"); + retval = -EBUSY; + goto out; + } + + /* Disable CCK and OFDM, clock gated */ + val8 = rtl8xxxu_read8(priv, REG_SYS_FUNC); + val8 &= ~SYS_FUNC_BBRSTB; + rtl8xxxu_write8(priv, REG_SYS_FUNC, val8); + + udelay(2); + + /* Whole BB is reset */ + val8 = rtl8xxxu_read8(priv, REG_SYS_FUNC); + val8 &= ~SYS_FUNC_BB_GLB_RSTN; + rtl8xxxu_write8(priv, REG_SYS_FUNC, val8); + + /* Reset MAC TRX */ + val16 = rtl8xxxu_read16(priv, REG_CR); + val16 |= 0x3f; + val16 &= ~(CR_MAC_TX_ENABLE | CR_MAC_RX_ENABLE | CR_SECURITY_ENABLE); + rtl8xxxu_write16(priv, REG_CR, val16); + + /* Respond TxOK to scheduler */ + val8 = rtl8xxxu_read8(priv, REG_DUAL_TSF_RST); + val8 |= DUAL_TSF_TX_OK; + rtl8xxxu_write8(priv, REG_DUAL_TSF_RST, val8); + +out: + return retval; +} + +static int rtl8188fu_power_on(struct rtl8xxxu_priv *priv) +{ + u16 val16; + int ret; + + rtl8188f_disabled_to_emu(priv); + + ret = rtl8188f_emu_to_active(priv); + if (ret) + goto exit; + + rtl8xxxu_write8(priv, REG_CR, 0); + + val16 = rtl8xxxu_read16(priv, REG_CR); + + val16 |= (CR_HCI_TXDMA_ENABLE | CR_HCI_RXDMA_ENABLE | + CR_TXDMA_ENABLE | CR_RXDMA_ENABLE | + CR_PROTOCOL_ENABLE | CR_SCHEDULE_ENABLE | + CR_SECURITY_ENABLE | CR_CALTIMER_ENABLE); + rtl8xxxu_write16(priv, REG_CR, val16); + +exit: + return ret; +} + +static void rtl8188fu_power_off(struct rtl8xxxu_priv *priv) +{ + u8 val8; + u16 val16; + + rtl8xxxu_flush_fifo(priv); + + val16 = rtl8xxxu_read16(priv, REG_GPIO_MUXCFG); + val16 &= ~BIT(12); + rtl8xxxu_write16(priv, REG_GPIO_MUXCFG, val16); + + rtl8xxxu_write32(priv, REG_HISR0, 0xFFFFFFFF); + rtl8xxxu_write32(priv, REG_HISR1, 0xFFFFFFFF); + + /* Stop Tx Report Timer. 0x4EC[Bit1]=b'0 */ + val8 = rtl8xxxu_read8(priv, REG_TX_REPORT_CTRL); + val8 &= ~TX_REPORT_CTRL_TIMER_ENABLE; + rtl8xxxu_write8(priv, REG_TX_REPORT_CTRL, val8); + + /* Turn off RF */ + rtl8xxxu_write8(priv, REG_RF_CTRL, 0x00); + + /* Reset Firmware if running in RAM */ + if (rtl8xxxu_read8(priv, REG_MCU_FW_DL) & MCU_FW_RAM_SEL) + rtl8xxxu_firmware_self_reset(priv); + + rtl8188fu_active_to_lps(priv); + + /* Reset MCU */ + val16 = rtl8xxxu_read16(priv, REG_SYS_FUNC); + val16 &= ~SYS_FUNC_CPU_ENABLE; + rtl8xxxu_write16(priv, REG_SYS_FUNC, val16); + + /* Reset MCU ready status */ + rtl8xxxu_write8(priv, REG_MCU_FW_DL, 0x00); + + rtl8188fu_active_to_emu(priv); + rtl8188fu_emu_to_disabled(priv); +} + +#define PPG_BB_GAIN_2G_TXA_OFFSET_8188F 0xee +#define PPG_BB_GAIN_2G_TX_OFFSET_MASK 0x0f + +static void rtl8188f_enable_rf(struct rtl8xxxu_priv *priv) +{ + u32 val32; + u8 pg_pwrtrim = 0xff, val8; + s8 bb_gain; + + /* Somehow this is not found in the efuse we read earlier. */ + rtl8xxxu_read_efuse8(priv, PPG_BB_GAIN_2G_TXA_OFFSET_8188F, &pg_pwrtrim); + + if (pg_pwrtrim != 0xff) { + bb_gain = pg_pwrtrim & PPG_BB_GAIN_2G_TX_OFFSET_MASK; + + if (bb_gain == PPG_BB_GAIN_2G_TX_OFFSET_MASK) + bb_gain = 0; + else if (bb_gain & 1) + bb_gain = bb_gain >> 1; + else + bb_gain = -(bb_gain >> 1); + + val8 = abs(bb_gain); + if (bb_gain > 0) + val8 |= BIT(5); + + val32 = rtl8xxxu_read_rfreg(priv, RF_A, RF6052_REG_UNKNOWN_55); + val32 &= ~0xfc000; + val32 |= val8 << 14; + rtl8xxxu_write_rfreg(priv, RF_A, RF6052_REG_UNKNOWN_55, val32); + } + + rtl8xxxu_write8(priv, REG_RF_CTRL, RF_ENABLE | RF_RSTB | RF_SDMRSTB); + + val32 = rtl8xxxu_read32(priv, REG_OFDM0_TRX_PATH_ENABLE); + val32 &= ~(OFDM_RF_PATH_RX_MASK | OFDM_RF_PATH_TX_MASK); + val32 |= OFDM_RF_PATH_RX_A | OFDM_RF_PATH_TX_A; + rtl8xxxu_write32(priv, REG_OFDM0_TRX_PATH_ENABLE, val32); + + rtl8xxxu_write8(priv, REG_TXPAUSE, 0x00); +} + +static void rtl8188f_disable_rf(struct rtl8xxxu_priv *priv) +{ + u32 val32; + + val32 = rtl8xxxu_read32(priv, REG_OFDM0_TRX_PATH_ENABLE); + val32 &= ~OFDM_RF_PATH_TX_MASK; + rtl8xxxu_write32(priv, REG_OFDM0_TRX_PATH_ENABLE, val32); + + /* Power down RF module */ + rtl8xxxu_write_rfreg(priv, RF_A, RF6052_REG_AC, 0); +} + +static void rtl8188f_usb_quirks(struct rtl8xxxu_priv *priv) +{ + u16 val16; + u32 val32; + + val16 = rtl8xxxu_read16(priv, REG_CR); + val16 |= (CR_MAC_TX_ENABLE | CR_MAC_RX_ENABLE); + rtl8xxxu_write16(priv, REG_CR, val16); + + val32 = rtl8xxxu_read32(priv, REG_TXDMA_OFFSET_CHK); + val32 |= TXDMA_OFFSET_DROP_DATA_EN; + rtl8xxxu_write32(priv, REG_TXDMA_OFFSET_CHK, val32); +} + +struct rtl8xxxu_fileops rtl8188fu_fops = { + .parse_efuse = rtl8188fu_parse_efuse, + .load_firmware = rtl8188fu_load_firmware, + .power_on = rtl8188fu_power_on, + .power_off = rtl8188fu_power_off, + .reset_8051 = rtl8xxxu_reset_8051, + .llt_init = rtl8xxxu_auto_llt_table, + .init_phy_bb = rtl8188fu_init_phy_bb, + .init_phy_rf = rtl8188fu_init_phy_rf, + .phy_init_antenna_selection = rtl8723bu_phy_init_antenna_selection, + .phy_lc_calibrate = rtl8188f_phy_lc_calibrate, + .phy_iq_calibrate = rtl8188fu_phy_iq_calibrate, + .config_channel = rtl8188fu_config_channel, + .parse_rx_desc = rtl8xxxu_parse_rxdesc24, + .init_aggregation = rtl8188fu_init_aggregation, + .init_statistics = rtl8188fu_init_statistics, + .enable_rf = rtl8188f_enable_rf, + .disable_rf = rtl8188f_disable_rf, + .usb_quirks = rtl8188f_usb_quirks, + .set_tx_power = rtl8188f_set_tx_power, + .update_rate_mask = rtl8xxxu_gen2_update_rate_mask, + .report_connect = rtl8xxxu_gen2_report_connect, + .fill_txdesc = rtl8xxxu_fill_txdesc_v2, + .writeN_block_size = 128, + .rx_desc_size = sizeof(struct rtl8xxxu_rxdesc24), + .tx_desc_size = sizeof(struct rtl8xxxu_txdesc40), + .has_s0s1 = 1, + .has_tx_report = 1, + .gen2_thermal_meter = 1, + .needs_full_init = 1, + .adda_1t_init = 0x03c00014, + .adda_1t_path_on = 0x03c00014, + .trxff_boundary = 0x3f7f, + .pbp_rx = PBP_PAGE_SIZE_256, + .pbp_tx = PBP_PAGE_SIZE_256, + .mactable = rtl8188f_mac_init_table, + .total_page_num = TX_TOTAL_PAGE_NUM_8188F, + .page_num_hi = TX_PAGE_NUM_HI_PQ_8188F, + .page_num_lo = TX_PAGE_NUM_LO_PQ_8188F, + .page_num_norm = TX_PAGE_NUM_NORM_PQ_8188F, +}; diff --git a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8192c.c b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8192c.c index 27c4cb688be4..dd84da51686c 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8192c.c +++ b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8192c.c @@ -549,6 +549,7 @@ struct rtl8xxxu_fileops rtl8192cu_fops = { .llt_init = rtl8xxxu_init_llt_table, .init_phy_bb = rtl8xxxu_gen1_init_phy_bb, .init_phy_rf = rtl8192cu_init_phy_rf, + .phy_lc_calibrate = rtl8723a_phy_lc_calibrate, .phy_iq_calibrate = rtl8xxxu_gen1_phy_iq_calibrate, .config_channel = rtl8xxxu_gen1_config_channel, .parse_rx_desc = rtl8xxxu_parse_rxdesc16, diff --git a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8192e.c b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8192e.c index b06508d0cdf8..c37bae079cf6 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8192e.c +++ b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8192e.c @@ -1680,6 +1680,7 @@ struct rtl8xxxu_fileops rtl8192eu_fops = { .llt_init = rtl8xxxu_auto_llt_table, .init_phy_bb = rtl8192eu_init_phy_bb, .init_phy_rf = rtl8192eu_init_phy_rf, + .phy_lc_calibrate = rtl8723a_phy_lc_calibrate, .phy_iq_calibrate = rtl8192eu_phy_iq_calibrate, .config_channel = rtl8xxxu_gen2_config_channel, .parse_rx_desc = rtl8xxxu_parse_rxdesc24, diff --git a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8723a.c b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8723a.c index 4f93f88716a9..e18ddd441728 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8723a.c +++ b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8723a.c @@ -366,6 +366,7 @@ struct rtl8xxxu_fileops rtl8723au_fops = { .llt_init = rtl8xxxu_init_llt_table, .init_phy_bb = rtl8xxxu_gen1_init_phy_bb, .init_phy_rf = rtl8723au_init_phy_rf, + .phy_lc_calibrate = rtl8723a_phy_lc_calibrate, .phy_iq_calibrate = rtl8xxxu_gen1_phy_iq_calibrate, .config_channel = rtl8xxxu_gen1_config_channel, .parse_rx_desc = rtl8xxxu_parse_rxdesc16, diff --git a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8723b.c b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8723b.c index a71e1816e632..7c9b8432a4e9 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8723b.c +++ b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8723b.c @@ -518,7 +518,7 @@ static int rtl8723bu_init_phy_rf(struct rtl8xxxu_priv *priv) return ret; } -static void rtl8723bu_phy_init_antenna_selection(struct rtl8xxxu_priv *priv) +void rtl8723bu_phy_init_antenna_selection(struct rtl8xxxu_priv *priv) { u32 val32; @@ -1650,6 +1650,7 @@ struct rtl8xxxu_fileops rtl8723bu_fops = { .init_phy_bb = rtl8723bu_init_phy_bb, .init_phy_rf = rtl8723bu_init_phy_rf, .phy_init_antenna_selection = rtl8723bu_phy_init_antenna_selection, + .phy_lc_calibrate = rtl8723a_phy_lc_calibrate, .phy_iq_calibrate = rtl8723bu_phy_iq_calibrate, .config_channel = rtl8xxxu_gen2_config_channel, .parse_rx_desc = rtl8xxxu_parse_rxdesc24, diff --git a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c index ac641a56efb0..80084e839ec2 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c +++ b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c @@ -52,6 +52,7 @@ MODULE_FIRMWARE("rtlwifi/rtl8192cufw_TMSC.bin"); MODULE_FIRMWARE("rtlwifi/rtl8192eu_nic.bin"); MODULE_FIRMWARE("rtlwifi/rtl8723bu_nic.bin"); MODULE_FIRMWARE("rtlwifi/rtl8723bu_bt.bin"); +MODULE_FIRMWARE("rtlwifi/rtl8188fufw.bin"); module_param_named(debug, rtl8xxxu_debug, int, 0600); MODULE_PARM_DESC(debug, "Set debug mask"); @@ -1606,6 +1607,7 @@ static void rtl8xxxu_print_chipinfo(struct rtl8xxxu_priv *priv) static int rtl8xxxu_identify_chip(struct rtl8xxxu_priv *priv) { + const struct usb_device_descriptor *descriptor = &priv->udev->descriptor; struct device *dev = &priv->udev->dev; struct ieee80211_hw *hw = priv->hw; u32 val32, bonding; @@ -1619,6 +1621,17 @@ static int rtl8xxxu_identify_chip(struct rtl8xxxu_priv *priv) return -ENOTSUPP; } + if (descriptor->idVendor == USB_VENDOR_ID_REALTEK && + descriptor->idProduct == 0xf179) { + sprintf(priv->chip_name, "8188FU"); + priv->rtl_chip = RTL8188F; + priv->rf_paths = 1; + priv->rx_paths = 1; + priv->tx_paths = 1; + priv->has_wifi = 1; + goto skip_complicated_chip_detection; + } + if (val32 & SYS_CFG_BT_FUNC) { if (priv->chip_cut >= 3) { sprintf(priv->chip_name, "8723BU"); @@ -1685,11 +1698,14 @@ static int rtl8xxxu_identify_chip(struct rtl8xxxu_priv *priv) priv->has_wifi = 1; } +skip_complicated_chip_detection: + hw->wiphy->available_antennas_tx = BIT(priv->tx_paths) - 1; hw->wiphy->available_antennas_rx = BIT(priv->rx_paths) - 1; switch (priv->rtl_chip) { case RTL8188E: + case RTL8188F: case RTL8192E: case RTL8723B: switch (val32 & SYS_CFG_VENDOR_EXT_MASK) { @@ -1720,7 +1736,18 @@ static int rtl8xxxu_identify_chip(struct rtl8xxxu_priv *priv) val32 = rtl8xxxu_read32(priv, REG_GPIO_OUTSTS); priv->rom_rev = (val32 & GPIO_RF_RL_ID) >> 28; - val16 = rtl8xxxu_read16(priv, REG_NORMAL_SIE_EP_TX); + /* + * 8188FU vendor driver doesn't use REG_NORMAL_SIE_EP_TX, + * it just decides the queue mapping based on nr_out_eps. + * However, reading the register returns "0x321" which + * results in a wrong ep_tx_count of 3 and most frames + * not being transmitted. + */ + if (priv->rtl_chip == RTL8188F) + val16 = 0; + else + val16 = rtl8xxxu_read16(priv, REG_NORMAL_SIE_EP_TX); + if (val16 & NORMAL_SIE_EP_TX_HIGH_MASK) { priv->ep_tx_high_queue = 1; priv->ep_tx_count++; @@ -1763,7 +1790,7 @@ static int rtl8xxxu_identify_chip(struct rtl8xxxu_priv *priv) return 0; } -static int +int rtl8xxxu_read_efuse8(struct rtl8xxxu_priv *priv, u16 offset, u8 *data) { int i; @@ -1979,7 +2006,7 @@ static int rtl8xxxu_start_firmware(struct rtl8xxxu_priv *priv) /* * Init H2C command */ - if (priv->rtl_chip == RTL8723B) + if (priv->rtl_chip == RTL8723B || priv->rtl_chip == RTL8188F) rtl8xxxu_write8(priv, REG_HMTFR, 0x0f); exit: return ret; @@ -2099,6 +2126,7 @@ int rtl8xxxu_load_firmware(struct rtl8xxxu_priv *priv, char *fw_name) case 0x88c0: case 0x5300: case 0x2300: + case 0x88f0: break; default: ret = -EINVAL; @@ -2166,7 +2194,9 @@ rtl8xxxu_init_mac(struct rtl8xxxu_priv *priv) } } - if (priv->rtl_chip != RTL8723B && priv->rtl_chip != RTL8192E) + if (priv->rtl_chip != RTL8723B && + priv->rtl_chip != RTL8192E && + priv->rtl_chip != RTL8188F) rtl8xxxu_write8(priv, REG_MAX_AGGR_NUM, 0x0a); return 0; @@ -3427,7 +3457,7 @@ void rtl8xxxu_gen1_phy_iq_calibrate(struct rtl8xxxu_priv *priv) priv->bb_recovery_backup, RTL8XXXU_BB_REGS); } -static void rtl8723a_phy_lc_calibrate(struct rtl8xxxu_priv *priv) +void rtl8723a_phy_lc_calibrate(struct rtl8xxxu_priv *priv) { u32 val32; u32 rf_amode, rf_bmode = 0, lstf; @@ -4031,6 +4061,9 @@ static int rtl8xxxu_init_device(struct ieee80211_hw *hw) if (priv->rtl_chip == RTL8192E) { rtl8xxxu_write32(priv, REG_HIMR0, 0x00); rtl8xxxu_write32(priv, REG_HIMR1, 0x00); + } else if (priv->rtl_chip == RTL8188F) { + rtl8xxxu_write32(priv, REG_HISR0, 0xffffffff); + rtl8xxxu_write32(priv, REG_HISR1, 0xffffffff); } else { /* * Enable all interrupts - not obvious USB needs to do this @@ -4050,11 +4083,25 @@ static int rtl8xxxu_init_device(struct ieee80211_hw *hw) RCR_APPEND_PHYSTAT | RCR_APPEND_ICV | RCR_APPEND_MIC; rtl8xxxu_write32(priv, REG_RCR, val32); - /* - * Accept all multicast - */ - rtl8xxxu_write32(priv, REG_MAR, 0xffffffff); - rtl8xxxu_write32(priv, REG_MAR + 4, 0xffffffff); + if (priv->rtl_chip == RTL8188F) { + /* Accept all data frames */ + rtl8xxxu_write16(priv, REG_RXFLTMAP2, 0xffff); + + /* + * Since ADF is removed from RCR, ps-poll will not be indicate to driver, + * RxFilterMap should mask ps-poll to gurantee AP mode can rx ps-poll. + */ + rtl8xxxu_write16(priv, REG_RXFLTMAP1, 0x400); + + /* Accept all management frames */ + rtl8xxxu_write16(priv, REG_RXFLTMAP0, 0xffff); + } else { + /* + * Accept all multicast + */ + rtl8xxxu_write32(priv, REG_MAR, 0xffffffff); + rtl8xxxu_write32(priv, REG_MAR + 4, 0xffffffff); + } /* * Init adaptive controls @@ -4105,14 +4152,17 @@ static int rtl8xxxu_init_device(struct ieee80211_hw *hw) val16 = BEACON_DISABLE_TSF_UPDATE | (BEACON_DISABLE_TSF_UPDATE << 8); rtl8xxxu_write16(priv, REG_BEACON_CTRL, val16); rtl8xxxu_write16(priv, REG_TBTT_PROHIBIT, 0x6404); - rtl8xxxu_write8(priv, REG_DRIVER_EARLY_INT, DRIVER_EARLY_INT_TIME); + if (priv->rtl_chip != RTL8188F) + /* Firmware will control REG_DRVERLYINT when power saving is enable, */ + /* so don't set this register on STA mode. */ + rtl8xxxu_write8(priv, REG_DRIVER_EARLY_INT, DRIVER_EARLY_INT_TIME); rtl8xxxu_write8(priv, REG_BEACON_DMA_TIME, BEACON_DMA_ATIME_INT_TIME); rtl8xxxu_write16(priv, REG_BEACON_TCFG, 0x660F); /* * Initialize burst parameters */ - if (priv->rtl_chip == RTL8723B) { + if (priv->rtl_chip == RTL8723B || priv->rtl_chip == RTL8188F) { /* * For USB high speed set 512B packets */ @@ -4130,13 +4180,26 @@ static int rtl8xxxu_init_device(struct ieee80211_hw *hw) rtl8xxxu_write8(priv, REG_HT_SINGLE_AMPDU_8723B, val8); rtl8xxxu_write16(priv, REG_MAX_AGGR_NUM, 0x0c14); - rtl8xxxu_write8(priv, REG_AMPDU_MAX_TIME_8723B, 0x5e); + if (priv->rtl_chip == RTL8723B) + val8 = 0x5e; + else if (priv->rtl_chip == RTL8188F) + val8 = 0x70; /* 0x5e would make it very slow */ + rtl8xxxu_write8(priv, REG_AMPDU_MAX_TIME_8723B, val8); rtl8xxxu_write32(priv, REG_AGGLEN_LMT, 0xffffffff); rtl8xxxu_write8(priv, REG_RX_PKT_LIMIT, 0x18); rtl8xxxu_write8(priv, REG_PIFS, 0x00); - rtl8xxxu_write8(priv, REG_USTIME_TSF_8723B, 0x50); - rtl8xxxu_write8(priv, REG_USTIME_EDCA, 0x50); + if (priv->rtl_chip == RTL8188F) { + rtl8xxxu_write8(priv, REG_FWHW_TXQ_CTRL, FWHW_TXQ_CTRL_AMPDU_RETRY); + rtl8xxxu_write32(priv, REG_FAST_EDCA_CTRL, 0x03086666); + } + if (priv->rtl_chip == RTL8723B) + val8 = 0x50; + else if (priv->rtl_chip == RTL8188F) + val8 = 0x28; /* 0x50 would make the upload slow */ + rtl8xxxu_write8(priv, REG_USTIME_TSF_8723B, val8); + rtl8xxxu_write8(priv, REG_USTIME_EDCA, val8); + /* to prevent mac is reseted by bus. */ val8 = rtl8xxxu_read8(priv, REG_RSV_CTRL); val8 |= BIT(5) | BIT(6); rtl8xxxu_write8(priv, REG_RSV_CTRL, val8); @@ -4145,6 +4208,11 @@ static int rtl8xxxu_init_device(struct ieee80211_hw *hw) if (fops->init_aggregation) fops->init_aggregation(priv); + if (priv->rtl_chip == RTL8188F) { + rtl8xxxu_write16(priv, REG_PKT_VO_VI_LIFE_TIME, 0x0400); /* unit: 256us. 256ms */ + rtl8xxxu_write16(priv, REG_PKT_BE_BK_LIFE_TIME, 0x0400); /* unit: 256us. 256ms */ + } + /* * Enable CCK and OFDM block */ @@ -4163,7 +4231,7 @@ static int rtl8xxxu_init_device(struct ieee80211_hw *hw) fops->set_tx_power(priv, 1, false); /* Let the 8051 take control of antenna setting */ - if (priv->rtl_chip != RTL8192E) { + if (priv->rtl_chip != RTL8192E && priv->rtl_chip != RTL8188F) { val8 = rtl8xxxu_read8(priv, REG_LEDCFG2); val8 |= LEDCFG2_DPDT_SELECT; rtl8xxxu_write8(priv, REG_LEDCFG2, val8); @@ -4174,7 +4242,8 @@ static int rtl8xxxu_init_device(struct ieee80211_hw *hw) /* Disable BAR - not sure if this has any effect on USB */ rtl8xxxu_write32(priv, REG_BAR_MODE_CTRL, 0x0201ffff); - rtl8xxxu_write16(priv, REG_FAST_EDCA_CTRL, 0); + if (priv->rtl_chip != RTL8188F) + rtl8xxxu_write16(priv, REG_FAST_EDCA_CTRL, 0); if (fops->init_statistics) fops->init_statistics(priv); @@ -4191,20 +4260,38 @@ static int rtl8xxxu_init_device(struct ieee80211_hw *hw) * Reset USB mode switch setting */ rtl8xxxu_write8(priv, REG_ACLK_MON, 0x00); + } else if (priv->rtl_chip == RTL8188F) { + /* + * Init GPIO settings for 8188f + */ + val8 = rtl8xxxu_read8(priv, REG_GPIO_MUXCFG); + val8 &= ~GPIO_MUXCFG_IO_SEL_ENBT; + rtl8xxxu_write8(priv, REG_GPIO_MUXCFG, val8); } - rtl8723a_phy_lc_calibrate(priv); + if (priv->rtl_chip == RTL8188F) + /* CCK PD */ + rtl8xxxu_write8(priv, REG_CCK_PD_THRESH, CCK_PD_TYPE1_LV1_TH); + + fops->phy_lc_calibrate(priv); fops->phy_iq_calibrate(priv); /* * This should enable thermal meter */ - if (fops->gen2_thermal_meter) - rtl8xxxu_write_rfreg(priv, - RF_A, RF6052_REG_T_METER_8723B, 0x37cf8); - else + if (fops->gen2_thermal_meter) { + if (priv->rtl_chip == RTL8188F) { + val32 = rtl8xxxu_read_rfreg(priv, RF_A, RF6052_REG_T_METER_8723B); + val32 |= 0x30000; + rtl8xxxu_write_rfreg(priv, RF_A, RF6052_REG_T_METER_8723B, val32); + } else { + rtl8xxxu_write_rfreg(priv, + RF_A, RF6052_REG_T_METER_8723B, 0x37cf8); + } + } else { rtl8xxxu_write_rfreg(priv, RF_A, RF6052_REG_T_METER, 0x60); + } /* Set NAV_UPPER to 30000us */ val8 = ((30000 + NAV_UPPER_UNIT - 1) / NAV_UPPER_UNIT); @@ -6674,6 +6761,7 @@ static int rtl8xxxu_probe(struct usb_interface *interface, case 0x8178: case 0x817f: case 0x818b: + case 0xf179: untested = 0; break; } @@ -6886,6 +6974,9 @@ static const struct usb_device_id dev_table[] = { .driver_info = (unsigned long)&rtl8723bu_fops}, {USB_DEVICE_AND_INTERFACE_INFO(0x7392, 0xa611, 0xff, 0xff, 0xff), .driver_info = (unsigned long)&rtl8723bu_fops}, +/* RTL8188FU */ +{USB_DEVICE_AND_INTERFACE_INFO(USB_VENDOR_ID_REALTEK, 0xf179, 0xff, 0xff, 0xff), + .driver_info = (unsigned long)&rtl8188fu_fops}, #ifdef CONFIG_RTL8XXXU_UNTESTED /* Still supported by rtlwifi */ {USB_DEVICE_AND_INTERFACE_INFO(USB_VENDOR_ID_REALTEK, 0x8176, 0xff, 0xff, 0xff), diff --git a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_regs.h b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_regs.h index 438b65ba9640..35bde1404793 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_regs.h +++ b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_regs.h @@ -135,6 +135,7 @@ #define REG_CAL_TIMER 0x003c #define REG_ACLK_MON 0x003e #define REG_GPIO_MUXCFG 0x0040 +#define GPIO_MUXCFG_IO_SEL_ENBT BIT(5) #define REG_GPIO_IO_SEL 0x0042 #define REG_MAC_PINMUX_CFG 0x0043 #define REG_GPIO_PIN_CTRL 0x0044 @@ -391,6 +392,7 @@ #define REG_CPWM 0x012f #define REG_FWIMR 0x0130 #define REG_FWISR 0x0134 +#define REG_FTIMR 0x0138 #define REG_PKTBUF_DBG_CTRL 0x0140 #define REG_PKTBUF_DBG_DATA_L 0x0144 #define REG_PKTBUF_DBG_DATA_H 0x0148 @@ -440,6 +442,9 @@ #define REG_FIFOPAGE 0x0204 #define REG_TDECTRL 0x0208 + +#define REG_DWBCN0_CTRL_8188F REG_TDECTRL + #define REG_TXDMA_OFFSET_CHK 0x020c #define TXDMA_OFFSET_DROP_DATA_EN BIT(9) #define REG_TXDMA_STATUS 0x0210 @@ -925,6 +930,7 @@ #define REG_FPGA0_XA_LSSI_READBACK 0x08a0 /* Tranceiver LSSI Readback */ #define REG_FPGA0_XB_LSSI_READBACK 0x08a4 +#define REG_FPGA0_PSD_REPORT 0x08b4 #define REG_HSPI_XA_READBACK 0x08b8 /* Transceiver A HSPI read */ #define REG_HSPI_XB_READBACK 0x08bc /* Transceiver B HSPI read */ @@ -936,6 +942,7 @@ #define REG_RFE_PATH_SELECT 0x0940 /* 8723BU */ #define REG_RFE_BUFFER 0x0944 /* 8723BU */ #define REG_S0S1_PATH_SWITCH 0x0948 /* 8723BU */ +#define REG_OFDM_RX_DFIR 0x954 #define REG_CCK0_SYSTEM 0x0a00 #define CCK0_SIDEBAND BIT(4) @@ -946,6 +953,13 @@ #define CCK0_AFE_RX_ANT_A 0 #define CCK0_AFE_RX_ANT_B (BIT(24) | BIT(26)) +#define REG_CCK_PD_THRESH 0x0a0a +#define CCK_PD_TYPE1_LV0_TH 0x40 +#define CCK_PD_TYPE1_LV1_TH 0x83 +#define CCK_PD_TYPE1_LV2_TH 0xcd +#define CCK_PD_TYPE1_LV3_TH 0xdd +#define CCK_PD_TYPE1_LV4_TH 0xed + #define REG_CONFIG_ANT_A 0x0b68 #define REG_CONFIG_ANT_B 0x0b6c @@ -965,6 +979,7 @@ #define REG_OFDM0_FA_RSTC 0x0c0c +#define REG_OFDM0_XA_RX_AFE 0x0c10 #define REG_OFDM0_XA_RX_IQ_IMBALANCE 0x0c14 #define REG_OFDM0_XB_RX_IQ_IMBALANCE 0x0c1c @@ -1011,6 +1026,9 @@ #define OFDM_LSTF_MASK 0x70000000 #define REG_OFDM1_TRX_PATH_ENABLE 0x0d04 +#define REG_OFDM1_CFO_TRACKING 0x0d2c +#define REG_OFDM1_CSI_FIX_MASK1 0x0d40 +#define REG_OFDM1_CSI_FIX_MASK2 0x0d44 #define REG_TX_AGC_A_RATE18_06 0x0e00 #define REG_TX_AGC_A_RATE54_24 0x0e04 @@ -1202,6 +1220,7 @@ #define RF6052_REG_UNKNOWN_43 0x43 #define RF6052_REG_UNKNOWN_55 0x55 #define RF6052_REG_UNKNOWN_56 0x56 +#define RF6052_REG_RXG_MIX_SWBW 0x87 #define RF6052_REG_S0S1 0xb0 #define RF6052_REG_UNKNOWN_DF 0xdf #define RF6052_REG_UNKNOWN_ED 0xed From 791082ec0ab843e0be07c8ce3678e4c2afd2e33d Mon Sep 17 00:00:00 2001 From: Bitterblue Smith Date: Wed, 28 Sep 2022 23:36:51 +0300 Subject: [PATCH 0032/2002] wifi: rtl8xxxu: gen2: Turn on the rate control Re-enable the function rtl8xxxu_gen2_report_connect. It informs the firmware when connecting to a network. This makes the firmware enable the rate control, which makes the upload faster. It also informs the firmware when disconnecting from a network. In the past this made reconnecting impossible because it was sending the auth on queue 0x7 (TXDESC_QUEUE_VO) instead of queue 0x12 (TXDESC_QUEUE_MGNT): wlp0s20f0u3: send auth to 90:55:de:__:__:__ (try 1/3) wlp0s20f0u3: send auth to 90:55:de:__:__:__ (try 2/3) wlp0s20f0u3: send auth to 90:55:de:__:__:__ (try 3/3) wlp0s20f0u3: authentication with 90:55:de:__:__:__ timed out Probably the firmware disables the unnecessary TX queues when it knows it's disconnected. However, this was fixed in commit edd5747aa12e ("wifi: rtl8xxxu: Fix skb misuse in TX queue selection"). Fixes: c59f13bbead4 ("rtl8xxxu: Work around issue with 8192eu and 8723bu devices not reconnecting") Signed-off-by: Bitterblue Smith Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/43200afc-0c65-ee72-48f8-231edd1df493@gmail.com --- drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c index 80084e839ec2..71a391bba9e2 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c +++ b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c @@ -4476,12 +4476,9 @@ void rtl8xxxu_gen1_report_connect(struct rtl8xxxu_priv *priv, void rtl8xxxu_gen2_report_connect(struct rtl8xxxu_priv *priv, u8 macid, bool connect) { -#ifdef RTL8XXXU_GEN2_REPORT_CONNECT /* - * Barry Day reports this causes issues with 8192eu and 8723bu - * devices reconnecting. The reason for this is unclear, but - * until it is better understood, leave the code in place but - * disabled, so it is not lost. + * The firmware turns on the rate control when it knows it's + * connected to a network. */ struct h2c_cmd h2c; @@ -4494,7 +4491,6 @@ void rtl8xxxu_gen2_report_connect(struct rtl8xxxu_priv *priv, h2c.media_status_rpt.parm &= ~BIT(0); rtl8xxxu_gen2_h2c_cmd(priv, &h2c, sizeof(h2c.media_status_rpt)); -#endif } void rtl8xxxu_gen1_init_aggregation(struct rtl8xxxu_priv *priv) From 6c04deae1438e5df59fc4848795248fc34961f51 Mon Sep 17 00:00:00 2001 From: Wright Feng Date: Wed, 28 Sep 2022 20:25:24 -0500 Subject: [PATCH 0033/2002] brcmfmac: Add dump_survey cfg80211 ops for HostApd AutoChannelSelection To enable ACS feature in Hostap daemon, dump_survey cfg80211 ops and dump obss survey command in firmware side are needed. This patch is for adding dump_survey feature and adding DUMP_OBSS feature flag to check if firmware supports dump_obss iovar. Signed-off-by: Wright Feng Signed-off-by: Chi-hsien Lin Signed-off-by: Ian Lin Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20220929012527.4152-2-ian.lin@infineon.com --- .../broadcom/brcm80211/brcmfmac/cfg80211.c | 256 ++++++++++++++++++ .../broadcom/brcm80211/brcmfmac/feature.c | 3 +- .../broadcom/brcm80211/brcmfmac/feature.h | 4 +- 3 files changed, 261 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c index dfcfb3333369..bb3380c057d8 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c @@ -88,9 +88,39 @@ #define BRCMF_PS_MAX_TIMEOUT_MS 2000 +/* Dump obss definitions */ +#define ACS_MSRMNT_DELAY 100 +#define CHAN_NOISE_DUMMY (-80) +#define OBSS_TOKEN_IDX 15 +#define IBSS_TOKEN_IDX 15 +#define TX_TOKEN_IDX 14 +#define CTG_TOKEN_IDX 13 +#define PKT_TOKEN_IDX 15 +#define IDLE_TOKEN_IDX 12 + #define BRCMF_ASSOC_PARAMS_FIXED_SIZE \ (sizeof(struct brcmf_assoc_params_le) - sizeof(u16)) +struct brcmf_dump_survey { + u32 obss; + u32 ibss; + u32 no_ctg; + u32 no_pckt; + u32 tx; + u32 idle; +}; + +struct cca_stats_n_flags { + u32 msrmnt_time; /* Time for Measurement (msec) */ + u32 msrmnt_done; /* flag set when measurement complete */ + char buf[1]; +}; + +struct cca_msrmnt_query { + u32 msrmnt_query; + u32 time_req; +}; + static bool check_vif_up(struct brcmf_cfg80211_vif *vif) { if (!test_bit(BRCMF_VIF_STATUS_READY, &vif->sme_state)) { @@ -7525,6 +7555,229 @@ static s32 brcmf_translate_country_code(struct brcmf_pub *drvr, char alpha2[2], return 0; } +static int +brcmf_parse_dump_obss(char *buf, struct brcmf_dump_survey *survey) +{ + int i; + char *token; + char delim[] = "\n "; + unsigned long val; + int err = 0; + + token = strsep(&buf, delim); + while (token) { + if (!strcmp(token, "OBSS")) { + for (i = 0; i < OBSS_TOKEN_IDX; i++) + token = strsep(&buf, delim); + err = kstrtoul(token, 10, &val); + if (err) + break; + survey->obss = val; + } + + if (!strcmp(token, "IBSS")) { + for (i = 0; i < IBSS_TOKEN_IDX; i++) + token = strsep(&buf, delim); + err = kstrtoul(token, 10, &val); + if (err) + break; + survey->ibss = val; + } + + if (!strcmp(token, "TXDur")) { + for (i = 0; i < TX_TOKEN_IDX; i++) + token = strsep(&buf, delim); + err = kstrtoul(token, 10, &val); + if (err) + break; + survey->tx = val; + } + + if (!strcmp(token, "Category")) { + for (i = 0; i < CTG_TOKEN_IDX; i++) + token = strsep(&buf, delim); + err = kstrtoul(token, 10, &val); + if (err) + break; + survey->no_ctg = val; + } + + if (!strcmp(token, "Packet")) { + for (i = 0; i < PKT_TOKEN_IDX; i++) + token = strsep(&buf, delim); + err = kstrtoul(token, 10, &val); + if (err) + break; + survey->no_pckt = val; + } + + if (!strcmp(token, "Opp(time):")) { + for (i = 0; i < IDLE_TOKEN_IDX; i++) + token = strsep(&buf, delim); + err = kstrtoul(token, 10, &val); + if (err) + break; + survey->idle = val; + } + + token = strsep(&buf, delim); + } + + return err; +} + +static int +brcmf_dump_obss(struct brcmf_if *ifp, struct cca_msrmnt_query req, + struct brcmf_dump_survey *survey) +{ + struct cca_stats_n_flags *results; + char *buf; + int err; + + buf = kzalloc(sizeof(char) * BRCMF_DCMD_MEDLEN, GFP_KERNEL); + if (unlikely(!buf)) { + brcmf_err("%s: buf alloc failed\n", __func__); + return -ENOMEM; + } + + memcpy(buf, &req, sizeof(struct cca_msrmnt_query)); + err = brcmf_fil_iovar_data_get(ifp, "dump_obss", + buf, BRCMF_DCMD_MEDLEN); + if (err < 0) { + brcmf_err("dump_obss error (%d)\n", err); + goto exit; + } + results = (struct cca_stats_n_flags *)(buf); + + if (req.msrmnt_query) + brcmf_parse_dump_obss(results->buf, survey); + + kfree(buf); + return 0; +exit: + kfree(buf); + return -EINVAL; +} + +static s32 +cfg80211_set_channel(struct wiphy *wiphy, struct net_device *dev, + struct ieee80211_channel *chan, + enum nl80211_channel_type channel_type) +{ + u16 chspec = 0; + int err = 0; + struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); + struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg)); + + /* set_channel */ + chspec = channel_to_chanspec(&cfg->d11inf, chan); + if (chspec != INVCHANSPEC) { + err = brcmf_fil_iovar_int_set(ifp, "chanspec", chspec); + if (err) { + brcmf_err("set chanspec 0x%04x fail, reason %d\n", chspec, err); + err = -EINVAL; + } + } else { + brcmf_err("failed to convert host chanspec to fw chanspec\n"); + err = -EINVAL; + } + + return err; +} + +static int +brcmf_cfg80211_dump_survey(struct wiphy *wiphy, struct net_device *ndev, + int idx, struct survey_info *info) +{ + struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); + struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg)); + struct brcmf_dump_survey survey = {}; + struct ieee80211_supported_band *band; + struct ieee80211_channel *chan; + struct cca_msrmnt_query req; + u32 val, noise; + int err; + + brcmf_dbg(TRACE, "Enter: channel idx=%d\n", idx); + + band = wiphy->bands[NL80211_BAND_2GHZ]; + if (band && idx >= band->n_channels) { + idx -= band->n_channels; + band = NULL; + } + + if (!band || idx >= band->n_channels) { + band = wiphy->bands[NL80211_BAND_5GHZ]; + if (idx >= band->n_channels) + return -ENOENT; + } + + /* Setting current channel to the requested channel */ + chan = &band->channels[idx]; + err = cfg80211_set_channel(wiphy, ndev, chan, NL80211_CHAN_HT20); + if (err) { + info->channel = chan; + info->filled = 0; + return 0; + } + + if (!idx) { + /* Disable mpc */ + val = 0; + brcmf_set_mpc(ifp, val); + /* Set interface up, explicitly. */ + val = 1; + err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_UP, val); + if (err) { + brcmf_err("BRCMF_C_UP error (%d)\n", err); + return -EIO; + } + } + + /* Get noise value */ + err = brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_PHY_NOISE, &noise); + if (err) { + brcmf_err("Get Phy Noise failed, error = %d\n", err); + noise = CHAN_NOISE_DUMMY; + } + + /* Start Measurement for obss stats on current channel */ + req.msrmnt_query = 0; + req.time_req = ACS_MSRMNT_DELAY; + err = brcmf_dump_obss(ifp, req, &survey); + if (err) + goto exit; + + /* Add 10 ms for IOVAR completion */ + msleep(ACS_MSRMNT_DELAY + 10); + + /* Issue IOVAR to collect measurement results */ + req.msrmnt_query = 1; + err = brcmf_dump_obss(ifp, req, &survey); + if (err < 0) + goto exit; + + info->channel = chan; + info->noise = noise; + info->time = ACS_MSRMNT_DELAY; + info->time_busy = ACS_MSRMNT_DELAY - survey.idle; + info->time_rx = survey.obss + survey.ibss + survey.no_ctg + + survey.no_pckt; + info->time_tx = survey.tx; + info->filled = SURVEY_INFO_NOISE_DBM | SURVEY_INFO_TIME | + SURVEY_INFO_TIME_BUSY | SURVEY_INFO_TIME_RX | + SURVEY_INFO_TIME_TX; + + brcmf_dbg(INFO, "OBSS dump: channel %d: survey duration %d\n", + ieee80211_frequency_to_channel(chan->center_freq), + ACS_MSRMNT_DELAY); + brcmf_dbg(INFO, "noise(%d) busy(%llu) rx(%llu) tx(%llu)\n", + info->noise, info->time_busy, info->time_rx, info->time_tx); + +exit: + return err; +} + static void brcmf_cfg80211_reg_notifier(struct wiphy *wiphy, struct regulatory_request *req) { @@ -7676,6 +7929,9 @@ struct brcmf_cfg80211_info *brcmf_cfg80211_attach(struct brcmf_pub *drvr, if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_WOWL_GTK)) ops->set_rekey_data = brcmf_cfg80211_set_rekey_data; #endif + if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_DUMP_OBSS)) + ops->dump_survey = brcmf_cfg80211_dump_survey; + err = wiphy_register(wiphy); if (err < 0) { bphy_err(drvr, "Could not register wiphy device (%d)\n", err); diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.c index 2c2f3e026c13..10bac865d724 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.c @@ -143,7 +143,7 @@ static void brcmf_feat_iovar_int_get(struct brcmf_if *ifp, ifp->fwil_fwerr = true; err = brcmf_fil_iovar_int_get(ifp, name, &data); - if (err == 0) { + if (err != -BRCMF_FW_UNSUPPORTED) { brcmf_dbg(INFO, "enabling feature: %s\n", brcmf_feat_names[id]); ifp->drvr->feat_flags |= BIT(id); } else { @@ -281,6 +281,7 @@ void brcmf_feat_attach(struct brcmf_pub *drvr) brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_RSDB, "rsdb_mode"); brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_TDLS, "tdls_enable"); brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_MFP, "mfp"); + brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_DUMP_OBSS, "dump_obss"); pfn_mac.version = BRCMF_PFN_MACADDR_CFG_VER; err = brcmf_fil_iovar_data_get(ifp, "pfn_macaddr", &pfn_mac, diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.h index d1f4257af696..f1b086a69d73 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.h @@ -29,6 +29,7 @@ * DOT11H: firmware supports 802.11h * SAE: simultaneous authentication of equals * FWAUTH: Firmware authenticator + * DUMP_OBSS: Firmware has capable to dump obss info to support ACS */ #define BRCMF_FEAT_LIST \ BRCMF_FEAT_DEF(MBSS) \ @@ -51,7 +52,8 @@ BRCMF_FEAT_DEF(MONITOR_FMT_HW_RX_HDR) \ BRCMF_FEAT_DEF(DOT11H) \ BRCMF_FEAT_DEF(SAE) \ - BRCMF_FEAT_DEF(FWAUTH) + BRCMF_FEAT_DEF(FWAUTH) \ + BRCMF_FEAT_DEF(DUMP_OBSS) /* * Quirks: From 216647e6aaaf14773d85b6681eca1344bcd14757 Mon Sep 17 00:00:00 2001 From: Wright Feng Date: Wed, 28 Sep 2022 20:25:25 -0500 Subject: [PATCH 0034/2002] brcmfmac: fix firmware trap while dumping obss stats When doing dump_survey, host will call "dump_obss" iovar to firmware side. Host need to make sure the HW clock in dongle is on, or there is high probability that firmware gets trap because register or shared memory access failed. To fix this, we disable mpc when doing dump obss and set it back after that. [28350.512799] brcmfmac: brcmf_dump_obss: dump_obss error (-52) [28743.402314] ieee80211 phy0: brcmf_fw_crashed: Firmware has halted or crashed [28745.869430] brcmfmac: brcmf_sdio_bus_rxctl: resumed on timeout [28745.877546] brcmfmac: brcmf_sdio_checkdied: firmware trap in dongle Signed-off-by: Wright Feng Signed-off-by: Chi-hsien Lin Signed-off-by: Ian Lin Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20220929012527.4152-3-ian.lin@infineon.com --- .../broadcom/brcm80211/brcmfmac/cfg80211.c | 38 +++++++++---------- 1 file changed, 17 insertions(+), 21 deletions(-) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c index bb3380c057d8..db39e730096a 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c @@ -7635,16 +7635,15 @@ brcmf_dump_obss(struct brcmf_if *ifp, struct cca_msrmnt_query req, int err; buf = kzalloc(sizeof(char) * BRCMF_DCMD_MEDLEN, GFP_KERNEL); - if (unlikely(!buf)) { - brcmf_err("%s: buf alloc failed\n", __func__); + if (!buf) return -ENOMEM; - } memcpy(buf, &req, sizeof(struct cca_msrmnt_query)); err = brcmf_fil_iovar_data_get(ifp, "dump_obss", buf, BRCMF_DCMD_MEDLEN); - if (err < 0) { + if (err) { brcmf_err("dump_obss error (%d)\n", err); + err = -EINVAL; goto exit; } results = (struct cca_stats_n_flags *)(buf); @@ -7652,11 +7651,9 @@ brcmf_dump_obss(struct brcmf_if *ifp, struct cca_msrmnt_query req, if (req.msrmnt_query) brcmf_parse_dump_obss(results->buf, survey); - kfree(buf); - return 0; exit: kfree(buf); - return -EINVAL; + return err; } static s32 @@ -7695,7 +7692,7 @@ brcmf_cfg80211_dump_survey(struct wiphy *wiphy, struct net_device *ndev, struct ieee80211_supported_band *band; struct ieee80211_channel *chan; struct cca_msrmnt_query req; - u32 val, noise; + u32 noise; int err; brcmf_dbg(TRACE, "Enter: channel idx=%d\n", idx); @@ -7721,23 +7718,20 @@ brcmf_cfg80211_dump_survey(struct wiphy *wiphy, struct net_device *ndev, return 0; } - if (!idx) { - /* Disable mpc */ - val = 0; - brcmf_set_mpc(ifp, val); - /* Set interface up, explicitly. */ - val = 1; - err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_UP, val); - if (err) { - brcmf_err("BRCMF_C_UP error (%d)\n", err); - return -EIO; - } + /* Disable mpc */ + brcmf_set_mpc(ifp, 0); + + /* Set interface up, explicitly. */ + err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_UP, 1); + if (err) { + brcmf_err("set interface up failed, err = %d\n", err); + goto exit; } /* Get noise value */ err = brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_PHY_NOISE, &noise); if (err) { - brcmf_err("Get Phy Noise failed, error = %d\n", err); + brcmf_err("Get Phy Noise failed, use dummy value\n"); noise = CHAN_NOISE_DUMMY; } @@ -7754,7 +7748,7 @@ brcmf_cfg80211_dump_survey(struct wiphy *wiphy, struct net_device *ndev, /* Issue IOVAR to collect measurement results */ req.msrmnt_query = 1; err = brcmf_dump_obss(ifp, req, &survey); - if (err < 0) + if (err) goto exit; info->channel = chan; @@ -7775,6 +7769,8 @@ brcmf_cfg80211_dump_survey(struct wiphy *wiphy, struct net_device *ndev, info->noise, info->time_busy, info->time_rx, info->time_tx); exit: + if (!brcmf_is_apmode(ifp->vif)) + brcmf_set_mpc(ifp, 1); return err; } From 25076fe2a6024bcac009af1b737b184f65826476 Mon Sep 17 00:00:00 2001 From: Double Lo Date: Wed, 28 Sep 2022 20:25:26 -0500 Subject: [PATCH 0035/2002] brcmfmac: fix CERT-P2P:5.1.10 failure This patch fix CERT-P2P:5.1.10 failure at step 18 Group formation failed due to chip is under dump survey. Decrease the dump survery duration to pass this certification case. Signed-off-by: Double Lo Signed-off-by: Chi-hsien Lin Signed-off-by: Ian Lin Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20220929012527.4152-4-ian.lin@infineon.com --- drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c index db39e730096a..c13c4c559e6e 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c @@ -89,7 +89,7 @@ #define BRCMF_PS_MAX_TIMEOUT_MS 2000 /* Dump obss definitions */ -#define ACS_MSRMNT_DELAY 100 +#define ACS_MSRMNT_DELAY 80 #define CHAN_NOISE_DUMMY (-80) #define OBSS_TOKEN_IDX 15 #define IBSS_TOKEN_IDX 15 From 62ccb2e6f208ea754f62883be5a775f00bd559ab Mon Sep 17 00:00:00 2001 From: Ramesh Rangavittal Date: Wed, 28 Sep 2022 20:25:27 -0500 Subject: [PATCH 0036/2002] brcmfmac: Fix authentication latency caused by OBSS stats survey Auto Channel Select feature of HostAP uses dump_survey to fetch OBSS statistics. When the device is in the middle of an authentication sequence or just at the end of authentication completion, running dump_survey would trigger a channel change. The channel change in-turn can cause packet loss, resulting in authentication delay. With this change, dump_survey won't be run when authentication or association is in progress, hence resolving the issue. Signed-off-by: Ramesh Rangavittal Signed-off-by: Chung-Hsien Hsu Signed-off-by: Chi-hsien Lin Signed-off-by: Ian Lin Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20220929012527.4152-5-ian.lin@infineon.com --- drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c index c13c4c559e6e..362d8fd0af4e 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c @@ -7697,6 +7697,12 @@ brcmf_cfg80211_dump_survey(struct wiphy *wiphy, struct net_device *ndev, brcmf_dbg(TRACE, "Enter: channel idx=%d\n", idx); + /* Do not run survey when VIF in CONNECTING / CONNECTED states */ + if ((test_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state)) || + (test_bit(BRCMF_VIF_STATUS_CONNECTED, &ifp->vif->sme_state))) { + return -EBUSY; + } + band = wiphy->bands[NL80211_BAND_2GHZ]; if (band && idx >= band->n_channels) { idx -= band->n_channels; From dcb485dfc83bfa407e6dbdfb99f02be1446692ce Mon Sep 17 00:00:00 2001 From: Wright Feng Date: Wed, 28 Sep 2022 22:09:59 -0500 Subject: [PATCH 0037/2002] brcmfmac: add a timer to read console periodically in PCIE bus Currently, host only reads console buffer when receiving mailbox data or hit crash with PCIE bus. Therefore, we add timer in PCIE code to read console buffer periodically to help developer and user check firmware message when there is no data transmission between host and dongle. Signed-off-by: Wright Feng Signed-off-by: Chi-hsien Lin Signed-off-by: Ian Lin Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20220929031001.9962-2-ian.lin@infineon.com --- .../broadcom/brcm80211/brcmfmac/bus.h | 6 + .../broadcom/brcm80211/brcmfmac/pcie.c | 125 ++++++++++++++++++ .../broadcom/brcm80211/brcmfmac/sdio.c | 2 - 3 files changed, 131 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bus.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bus.h index 2208ab3aa795..60f5645aead3 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bus.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bus.h @@ -24,6 +24,12 @@ #define BRCMF_NROF_COMMON_MSGRINGS (BRCMF_NROF_H2D_COMMON_MSGRINGS + \ BRCMF_NROF_D2H_COMMON_MSGRINGS) +/* The interval to poll console */ +#define BRCMF_CONSOLE 10 + +/* The maximum console interval value (5 mins) */ +#define MAX_CONSOLE_INTERVAL (5 * 60) + /* The level of bus communication with the dongle */ enum brcmf_bus_state { BRCMF_BUS_DOWN, /* Not ready for frame transfers */ diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c index 80083f9ea311..2b7ebbd7b5b4 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c @@ -12,6 +12,8 @@ #include #include #include +#include +#include #include #include @@ -340,6 +342,11 @@ struct brcmf_pciedev_info { u16 value); struct brcmf_mp_device *settings; struct brcmf_otp_params otp; +#ifdef DEBUG + u32 console_interval; + bool console_active; + struct timer_list timer; +#endif }; struct brcmf_pcie_ringbuf { @@ -440,6 +447,9 @@ static void brcmf_pcie_setup(struct device *dev, int ret, struct brcmf_fw_request *fwreq); static struct brcmf_fw_request * brcmf_pcie_prepare_fw_request(struct brcmf_pciedev_info *devinfo); +static void +brcmf_pcie_fwcon_timer(struct brcmf_pciedev_info *devinfo, bool active); +static void brcmf_pcie_debugfs_create(struct device *dev); static u16 brcmf_pcie_read_reg16(struct brcmf_pciedev_info *devinfo, u32 reg_offset) @@ -1413,6 +1423,11 @@ fail: static void brcmf_pcie_down(struct device *dev) { + struct brcmf_bus *bus_if = dev_get_drvdata(dev); + struct brcmf_pciedev *pcie_bus_dev = bus_if->bus_priv.pcie; + struct brcmf_pciedev_info *devinfo = pcie_bus_dev->devinfo; + + brcmf_pcie_fwcon_timer(devinfo, false); } static int brcmf_pcie_preinit(struct device *dev) @@ -1547,6 +1562,7 @@ static const struct brcmf_bus_ops brcmf_pcie_bus_ops = { .get_memdump = brcmf_pcie_get_memdump, .get_blob = brcmf_pcie_get_blob, .reset = brcmf_pcie_reset, + .debugfs_create = brcmf_pcie_debugfs_create, }; @@ -2123,6 +2139,8 @@ static void brcmf_pcie_setup(struct device *dev, int ret, brcmf_pcie_bus_console_read(devinfo, false); + brcmf_pcie_fwcon_timer(devinfo, true); + return; fail: @@ -2197,6 +2215,105 @@ brcmf_pcie_prepare_fw_request(struct brcmf_pciedev_info *devinfo) return fwreq; } +#ifdef DEBUG +static void +brcmf_pcie_fwcon_timer(struct brcmf_pciedev_info *devinfo, bool active) +{ + if (!active) { + if (devinfo->console_active) { + del_timer_sync(&devinfo->timer); + devinfo->console_active = false; + } + return; + } + + /* don't start the timer */ + if (devinfo->state != BRCMFMAC_PCIE_STATE_UP || + !devinfo->console_interval || !BRCMF_FWCON_ON()) + return; + + if (!devinfo->console_active) { + devinfo->timer.expires = jiffies + devinfo->console_interval; + add_timer(&devinfo->timer); + devinfo->console_active = true; + } else { + /* Reschedule the timer */ + mod_timer(&devinfo->timer, jiffies + devinfo->console_interval); + } +} + +static void +brcmf_pcie_fwcon(struct timer_list *t) +{ + struct brcmf_pciedev_info *devinfo = from_timer(devinfo, t, timer); + + if (!devinfo->console_active) + return; + + brcmf_pcie_bus_console_read(devinfo, false); + + /* Reschedule the timer if console interval is not zero */ + mod_timer(&devinfo->timer, jiffies + devinfo->console_interval); +} + +static int brcmf_pcie_console_interval_get(void *data, u64 *val) +{ + struct brcmf_pciedev_info *devinfo = data; + + *val = devinfo->console_interval; + + return 0; +} + +static int brcmf_pcie_console_interval_set(void *data, u64 val) +{ + struct brcmf_pciedev_info *devinfo = data; + + if (val > MAX_CONSOLE_INTERVAL) + return -EINVAL; + + devinfo->console_interval = val; + + if (!val && devinfo->console_active) + brcmf_pcie_fwcon_timer(devinfo, false); + else if (val) + brcmf_pcie_fwcon_timer(devinfo, true); + + return 0; +} + +DEFINE_SIMPLE_ATTRIBUTE(brcmf_pcie_console_interval_fops, + brcmf_pcie_console_interval_get, + brcmf_pcie_console_interval_set, + "%llu\n"); + +static void brcmf_pcie_debugfs_create(struct device *dev) +{ + struct brcmf_bus *bus_if = dev_get_drvdata(dev); + struct brcmf_pub *drvr = bus_if->drvr; + struct brcmf_pciedev *pcie_bus_dev = bus_if->bus_priv.pcie; + struct brcmf_pciedev_info *devinfo = pcie_bus_dev->devinfo; + struct dentry *dentry = brcmf_debugfs_get_devdir(drvr); + + if (IS_ERR_OR_NULL(dentry)) + return; + + devinfo->console_interval = BRCMF_CONSOLE; + + debugfs_create_file("console_interval", 0644, dentry, devinfo, + &brcmf_pcie_console_interval_fops); +} + +#else +void brcmf_pcie_fwcon_timer(struct brcmf_pciedev_info *devinfo, bool active) +{ +} + +static void brcmf_pcie_debugfs_create(struct device *dev) +{ +} +#endif + static int brcmf_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id) { @@ -2278,6 +2395,11 @@ brcmf_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id) goto fail_brcmf; } +#ifdef DEBUG + /* Set up the fwcon timer */ + timer_setup(&devinfo->timer, brcmf_pcie_fwcon, 0); +#endif + fwreq = brcmf_pcie_prepare_fw_request(devinfo); if (!fwreq) { ret = -ENOMEM; @@ -2323,6 +2445,7 @@ brcmf_pcie_remove(struct pci_dev *pdev) devinfo = bus->bus_priv.pcie->devinfo; brcmf_pcie_bus_console_read(devinfo, false); + brcmf_pcie_fwcon_timer(devinfo, false); devinfo->state = BRCMFMAC_PCIE_STATE_DOWN; if (devinfo->ci) @@ -2366,6 +2489,7 @@ static int brcmf_pcie_pm_enter_D3(struct device *dev) bus = dev_get_drvdata(dev); devinfo = bus->bus_priv.pcie->devinfo; + brcmf_pcie_fwcon_timer(devinfo, false); brcmf_bus_change_state(bus, BRCMF_BUS_DOWN); devinfo->mbdata_completed = false; @@ -2409,6 +2533,7 @@ static int brcmf_pcie_pm_leave_D3(struct device *dev) brcmf_bus_change_state(bus, BRCMF_BUS_UP); brcmf_pcie_intr_enable(devinfo); brcmf_pcie_hostready(devinfo); + brcmf_pcie_fwcon_timer(devinfo, true); return 0; } diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c index 465d95d83759..deb0199fcf8c 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c @@ -135,8 +135,6 @@ struct rte_console { #define BRCMF_FIRSTREAD (1 << 6) -#define BRCMF_CONSOLE 10 /* watchdog interval to poll console */ - /* SBSDIO_DEVICE_CTL */ /* 1: device will assert busy signal when receiving CMD53 */ From 2aca4f3734bd717e04943ddf340d49ab62299a00 Mon Sep 17 00:00:00 2001 From: Wright Feng Date: Wed, 28 Sep 2022 22:10:00 -0500 Subject: [PATCH 0038/2002] brcmfmac: return error when getting invalid max_flowrings from dongle When firmware hit trap at initialization, host will read abnormal max_flowrings number from dongle, and it will cause kernel panic when doing iowrite to initialize dongle ring. To detect this error at early stage, we directly return error when getting invalid max_flowrings(>256). Signed-off-by: Wright Feng Signed-off-by: Chi-hsien Lin Signed-off-by: Ian Lin Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20220929031001.9962-3-ian.lin@infineon.com --- drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c index 2b7ebbd7b5b4..1becd50038ab 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c @@ -1228,6 +1228,10 @@ static int brcmf_pcie_init_ringbuffers(struct brcmf_pciedev_info *devinfo) BRCMF_NROF_H2D_COMMON_MSGRINGS; max_completionrings = BRCMF_NROF_D2H_COMMON_MSGRINGS; } + if (max_flowrings > 256) { + brcmf_err(bus, "invalid max_flowrings(%d)\n", max_flowrings); + return -EIO; + } if (devinfo->dma_idx_sz != 0) { bufsz = (max_submissionrings + max_completionrings) * From 5671c8b56c320e17c9d61e8578dc7e6fa4a704ed Mon Sep 17 00:00:00 2001 From: Wright Feng Date: Wed, 28 Sep 2022 22:10:01 -0500 Subject: [PATCH 0039/2002] brcmfmac: dump dongle memory when attaching failed To enhance FW debugging, we add dongle memory dump when hitting attaching failure with PCIE bus. It can help developer to get more information about dongle trap reason and root cause. Signed-off-by: Wright Feng Signed-off-by: Chi-hsien Lin Signed-off-by: Ian Lin Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20220929031001.9962-4-ian.lin@infineon.com --- .../net/wireless/broadcom/brcm80211/brcmfmac/core.c | 3 ++- .../net/wireless/broadcom/brcm80211/brcmfmac/pcie.c | 10 +++++++--- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c index 595ae3ae561e..d354f79fd0ac 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c @@ -1399,7 +1399,8 @@ void brcmf_fw_crashed(struct device *dev) brcmf_dev_coredump(dev); - schedule_work(&drvr->bus_reset); + if (drvr->bus_reset.func) + schedule_work(&drvr->bus_reset); } void brcmf_detach(struct device *dev) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c index 1becd50038ab..cf564adc612a 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c @@ -2068,13 +2068,14 @@ static void brcmf_pcie_setup(struct device *dev, int ret, struct brcmf_commonring **flowrings; u32 i, nvram_len; + bus = dev_get_drvdata(dev); + pcie_bus_dev = bus->bus_priv.pcie; + devinfo = pcie_bus_dev->devinfo; + /* check firmware loading result */ if (ret) goto fail; - bus = dev_get_drvdata(dev); - pcie_bus_dev = bus->bus_priv.pcie; - devinfo = pcie_bus_dev->devinfo; brcmf_pcie_attach(devinfo); fw = fwreq->items[BRCMF_PCIE_FW_CODE].binary; @@ -2148,6 +2149,9 @@ static void brcmf_pcie_setup(struct device *dev, int ret, return; fail: + brcmf_err(bus, "Dongle setup failed\n"); + brcmf_pcie_bus_console_read(devinfo, true); + brcmf_fw_crashed(dev); device_release_driver(dev); } From 2b5fb30f8ff55c7d0e8cd6a06c9be1de6129f5a7 Mon Sep 17 00:00:00 2001 From: Wright Feng Date: Thu, 29 Sep 2022 00:06:11 -0500 Subject: [PATCH 0040/2002] brcmfmac: add creating station interface support With RSDB device, it is able to control two station interfaces concurrently. So we add creating station interface support and allow user to create it via cfg80211. Signed-off-by: Wright Feng Signed-off-by: Chi-hsien Lin Signed-off-by: Ian Lin Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20220929050614.31518-2-ian.lin@infineon.com --- .../broadcom/brcm80211/brcmfmac/cfg80211.c | 81 ++++++++++++++++--- .../broadcom/brcm80211/brcmfmac/core.h | 1 + 2 files changed, 70 insertions(+), 12 deletions(-) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c index 362d8fd0af4e..6817e745434a 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c @@ -264,6 +264,19 @@ struct parsed_vndr_ies { struct parsed_vndr_ie_info ie_info[VNDR_IE_PARSE_LIMIT]; }; +#define WL_INTERFACE_MAC_DONT_USE 0x0 +#define WL_INTERFACE_MAC_USE 0x2 + +#define WL_INTERFACE_CREATE_STA 0x0 +#define WL_INTERFACE_CREATE_AP 0x1 + +struct wl_interface_create { + u16 ver; + u32 flags; + u8 mac_addr[ETH_ALEN]; + u8 pad[13]; +}; + static u8 nl80211_band_to_fwil(enum nl80211_band band) { switch (band) { @@ -551,6 +564,42 @@ static int brcmf_get_first_free_bsscfgidx(struct brcmf_pub *drvr) return -ENOMEM; } +static void brcmf_set_sta_iface_macaddr(struct brcmf_if *ifp, + struct wl_interface_create *iface) +{ + u8 mac_idx = ifp->drvr->sta_mac_idx; + + /* set difference MAC address with locally administered bit */ + iface->flags |= WL_INTERFACE_MAC_USE; + memcpy(iface->mac_addr, ifp->mac_addr, ETH_ALEN); + iface->mac_addr[0] |= 0x02; + iface->mac_addr[3] ^= mac_idx ? 0xC0 : 0xA0; + mac_idx++; + mac_idx = mac_idx % 2; + ifp->drvr->sta_mac_idx = mac_idx; +} + +static int brcmf_cfg80211_request_sta_if(struct brcmf_if *ifp, u8 *macaddr) +{ + struct wl_interface_create iface; + int err; + + memset(&iface, 0, sizeof(iface)); + + iface.ver = 0; + iface.flags = WL_INTERFACE_CREATE_STA; + if (!is_zero_ether_addr(macaddr)) { + /* set MAC address in cfg80211 params */ + memcpy(iface.mac_addr, macaddr, ETH_ALEN); + } else { + brcmf_set_sta_iface_macaddr(ifp, &iface); + } + + err = brcmf_fil_iovar_data_get(ifp, "interface_create", &iface, + sizeof(iface)); + return err; +} + static int brcmf_cfg80211_request_ap_if(struct brcmf_if *ifp) { struct brcmf_pub *drvr = ifp->drvr; @@ -576,15 +625,17 @@ static int brcmf_cfg80211_request_ap_if(struct brcmf_if *ifp) } /** - * brcmf_ap_add_vif() - create a new AP virtual interface for multiple BSS + * brcmf_apsta_add_vif() - create a new AP or STA virtual interface * * @wiphy: wiphy device of new interface. * @name: name of the new interface. - * @params: contains mac address for AP device. + * @params: contains mac address for AP or STA device. + * @type: interface type. */ static -struct wireless_dev *brcmf_ap_add_vif(struct wiphy *wiphy, const char *name, - struct vif_params *params) +struct wireless_dev *brcmf_apsta_add_vif(struct wiphy *wiphy, const char *name, + struct vif_params *params, + enum nl80211_iftype type) { struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg)); @@ -592,18 +643,24 @@ struct wireless_dev *brcmf_ap_add_vif(struct wiphy *wiphy, const char *name, struct brcmf_cfg80211_vif *vif; int err; + if (type != NL80211_IFTYPE_STATION && type != NL80211_IFTYPE_AP) + return ERR_PTR(-EINVAL); + if (brcmf_cfg80211_vif_event_armed(cfg)) return ERR_PTR(-EBUSY); brcmf_dbg(INFO, "Adding vif \"%s\"\n", name); - vif = brcmf_alloc_vif(cfg, NL80211_IFTYPE_AP); + vif = brcmf_alloc_vif(cfg, type); if (IS_ERR(vif)) return (struct wireless_dev *)vif; brcmf_cfg80211_arm_vif_event(cfg, vif); - err = brcmf_cfg80211_request_ap_if(ifp); + if (type == NL80211_IFTYPE_STATION) + err = brcmf_cfg80211_request_sta_if(ifp, params->macaddr); + else + err = brcmf_cfg80211_request_ap_if(ifp); if (err) { brcmf_cfg80211_arm_vif_event(cfg, NULL); goto fail; @@ -750,15 +807,15 @@ static struct wireless_dev *brcmf_cfg80211_add_iface(struct wiphy *wiphy, } switch (type) { case NL80211_IFTYPE_ADHOC: - case NL80211_IFTYPE_STATION: case NL80211_IFTYPE_AP_VLAN: case NL80211_IFTYPE_WDS: case NL80211_IFTYPE_MESH_POINT: return ERR_PTR(-EOPNOTSUPP); case NL80211_IFTYPE_MONITOR: return brcmf_mon_add_vif(wiphy, name); + case NL80211_IFTYPE_STATION: case NL80211_IFTYPE_AP: - wdev = brcmf_ap_add_vif(wiphy, name, params); + wdev = brcmf_apsta_add_vif(wiphy, name, params, type); break; case NL80211_IFTYPE_P2P_CLIENT: case NL80211_IFTYPE_P2P_GO: @@ -878,8 +935,8 @@ s32 brcmf_notify_escan_complete(struct brcmf_cfg80211_info *cfg, return err; } -static int brcmf_cfg80211_del_ap_iface(struct wiphy *wiphy, - struct wireless_dev *wdev) +static int brcmf_cfg80211_del_apsta_iface(struct wiphy *wiphy, + struct wireless_dev *wdev) { struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); struct net_device *ndev = wdev->netdev; @@ -936,15 +993,15 @@ int brcmf_cfg80211_del_iface(struct wiphy *wiphy, struct wireless_dev *wdev) switch (wdev->iftype) { case NL80211_IFTYPE_ADHOC: - case NL80211_IFTYPE_STATION: case NL80211_IFTYPE_AP_VLAN: case NL80211_IFTYPE_WDS: case NL80211_IFTYPE_MESH_POINT: return -EOPNOTSUPP; case NL80211_IFTYPE_MONITOR: return brcmf_mon_del_vif(wiphy, wdev); + case NL80211_IFTYPE_STATION: case NL80211_IFTYPE_AP: - return brcmf_cfg80211_del_ap_iface(wiphy, wdev); + return brcmf_cfg80211_del_apsta_iface(wiphy, wdev); case NL80211_IFTYPE_P2P_CLIENT: case NL80211_IFTYPE_P2P_GO: case NL80211_IFTYPE_P2P_DEVICE: diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.h index 340346c122d3..2e71b5c2a975 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.h @@ -136,6 +136,7 @@ struct brcmf_pub { struct work_struct bus_reset; u8 clmver[BRCMF_DCMD_SMLEN]; + u8 sta_mac_idx; }; /* forward declarations */ From 4388827b87d849e51053e098c4e12c707b9fef34 Mon Sep 17 00:00:00 2001 From: Wright Feng Date: Thu, 29 Sep 2022 00:06:12 -0500 Subject: [PATCH 0041/2002] brcmfmac: support station interface creation version 1, 2 and 3 To create virtual station interface for RSDB and VSDB, we add interface creation version 1, 2 and 3 supports The structures of each version are different and only version 3 and later version are able to get interface creating version from firmware side. The patch has been verified two concurrent stations pings test with interface create version 1: 89342(4359b1)-PCIE: 9.40.100 interface create version 2: 4373a0-sdio: 13.10.271 interface create version 3: 4373a0-sdio: 13.35.48 Signed-off-by: Wright Feng Signed-off-by: Chi-hsien Lin Signed-off-by: Ian Lin Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20220929050614.31518-3-ian.lin@infineon.com --- .../broadcom/brcm80211/brcmfmac/cfg80211.c | 148 +++++++++++++++--- 1 file changed, 124 insertions(+), 24 deletions(-) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c index 6817e745434a..e5f0a9ef2d0f 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c @@ -264,17 +264,46 @@ struct parsed_vndr_ies { struct parsed_vndr_ie_info ie_info[VNDR_IE_PARSE_LIMIT]; }; +#define WL_INTERFACE_CREATE_VER_1 1 +#define WL_INTERFACE_CREATE_VER_2 2 +#define WL_INTERFACE_CREATE_VER_3 3 +#define WL_INTERFACE_CREATE_VER_MAX WL_INTERFACE_CREATE_VER_3 + #define WL_INTERFACE_MAC_DONT_USE 0x0 #define WL_INTERFACE_MAC_USE 0x2 #define WL_INTERFACE_CREATE_STA 0x0 #define WL_INTERFACE_CREATE_AP 0x1 -struct wl_interface_create { - u16 ver; - u32 flags; - u8 mac_addr[ETH_ALEN]; - u8 pad[13]; +struct wl_interface_create_v1 { + u16 ver; /* structure version */ + u32 flags; /* flags for operation */ + u8 mac_addr[ETH_ALEN]; /* MAC address */ + u32 wlc_index; /* optional for wlc index */ +}; + +struct wl_interface_create_v2 { + u16 ver; /* structure version */ + u8 pad1[2]; + u32 flags; /* flags for operation */ + u8 mac_addr[ETH_ALEN]; /* MAC address */ + u8 iftype; /* type of interface created */ + u8 pad2; + u32 wlc_index; /* optional for wlc index */ +}; + +struct wl_interface_create_v3 { + u16 ver; /* structure version */ + u16 len; /* length of structure + data */ + u16 fixed_len; /* length of structure */ + u8 iftype; /* type of interface created */ + u8 wlc_index; /* optional for wlc index */ + u32 flags; /* flags for operation */ + u8 mac_addr[ETH_ALEN]; /* MAC address */ + u8 bssid[ETH_ALEN]; /* optional for BSSID */ + u8 if_index; /* interface index request */ + u8 pad[3]; + u8 data[]; /* Optional for specific data */ }; static u8 nl80211_band_to_fwil(enum nl80211_band band) @@ -564,16 +593,14 @@ static int brcmf_get_first_free_bsscfgidx(struct brcmf_pub *drvr) return -ENOMEM; } -static void brcmf_set_sta_iface_macaddr(struct brcmf_if *ifp, - struct wl_interface_create *iface) +static void brcmf_set_vif_sta_macaddr(struct brcmf_if *ifp, u8 *mac_addr) { u8 mac_idx = ifp->drvr->sta_mac_idx; /* set difference MAC address with locally administered bit */ - iface->flags |= WL_INTERFACE_MAC_USE; - memcpy(iface->mac_addr, ifp->mac_addr, ETH_ALEN); - iface->mac_addr[0] |= 0x02; - iface->mac_addr[3] ^= mac_idx ? 0xC0 : 0xA0; + memcpy(mac_addr, ifp->mac_addr, ETH_ALEN); + mac_addr[0] |= 0x02; + mac_addr[3] ^= mac_idx ? 0xC0 : 0xA0; mac_idx++; mac_idx = mac_idx % 2; ifp->drvr->sta_mac_idx = mac_idx; @@ -581,23 +608,96 @@ static void brcmf_set_sta_iface_macaddr(struct brcmf_if *ifp, static int brcmf_cfg80211_request_sta_if(struct brcmf_if *ifp, u8 *macaddr) { - struct wl_interface_create iface; + struct wl_interface_create_v1 iface_v1; + struct wl_interface_create_v2 iface_v2; + struct wl_interface_create_v3 iface_v3; + u32 iface_create_ver; int err; - memset(&iface, 0, sizeof(iface)); + /* interface_create version 1 */ + memset(&iface_v1, 0, sizeof(iface_v1)); + iface_v1.ver = WL_INTERFACE_CREATE_VER_1; + iface_v1.flags = WL_INTERFACE_CREATE_STA | + WL_INTERFACE_MAC_USE; + if (!is_zero_ether_addr(macaddr)) + memcpy(iface_v1.mac_addr, macaddr, ETH_ALEN); + else + brcmf_set_vif_sta_macaddr(ifp, iface_v1.mac_addr); - iface.ver = 0; - iface.flags = WL_INTERFACE_CREATE_STA; - if (!is_zero_ether_addr(macaddr)) { - /* set MAC address in cfg80211 params */ - memcpy(iface.mac_addr, macaddr, ETH_ALEN); + err = brcmf_fil_iovar_data_get(ifp, "interface_create", + &iface_v1, + sizeof(iface_v1)); + if (err) { + brcmf_info("failed to create interface(v1), err=%d\n", + err); } else { - brcmf_set_sta_iface_macaddr(ifp, &iface); + brcmf_dbg(INFO, "interface created(v1)\n"); + return 0; } - err = brcmf_fil_iovar_data_get(ifp, "interface_create", &iface, - sizeof(iface)); - return err; + /* interface_create version 2 */ + memset(&iface_v2, 0, sizeof(iface_v2)); + iface_v2.ver = WL_INTERFACE_CREATE_VER_2; + iface_v2.flags = WL_INTERFACE_MAC_USE; + iface_v2.iftype = WL_INTERFACE_CREATE_STA; + if (!is_zero_ether_addr(macaddr)) + memcpy(iface_v2.mac_addr, macaddr, ETH_ALEN); + else + brcmf_set_vif_sta_macaddr(ifp, iface_v2.mac_addr); + + err = brcmf_fil_iovar_data_get(ifp, "interface_create", + &iface_v2, + sizeof(iface_v2)); + if (err) { + brcmf_info("failed to create interface(v2), err=%d\n", + err); + } else { + brcmf_dbg(INFO, "interface created(v2)\n"); + return 0; + } + + /* interface_create version 3+ */ + /* get supported version from firmware side */ + iface_create_ver = 0; + err = brcmf_fil_bsscfg_int_get(ifp, "interface_create", + &iface_create_ver); + if (err) { + brcmf_err("fail to get supported version, err=%d\n", err); + return -EOPNOTSUPP; + } + + switch (iface_create_ver) { + case WL_INTERFACE_CREATE_VER_3: + memset(&iface_v3, 0, sizeof(iface_v3)); + iface_v3.ver = WL_INTERFACE_CREATE_VER_3; + iface_v3.flags = WL_INTERFACE_MAC_USE; + iface_v3.iftype = WL_INTERFACE_CREATE_STA; + if (!is_zero_ether_addr(macaddr)) + memcpy(iface_v3.mac_addr, macaddr, ETH_ALEN); + else + brcmf_set_vif_sta_macaddr(ifp, iface_v3.mac_addr); + + err = brcmf_fil_iovar_data_get(ifp, "interface_create", + &iface_v3, + sizeof(iface_v3)); + + if (!err) + brcmf_dbg(INFO, "interface created(v3)\n"); + break; + default: + brcmf_err("not support interface create(v%d)\n", + iface_create_ver); + err = -EOPNOTSUPP; + break; + } + + if (err) { + brcmf_info("station interface creation failed (%d)\n", + err); + return -EIO; + } + + return 0; } static int brcmf_cfg80211_request_ap_if(struct brcmf_if *ifp) @@ -7060,7 +7160,7 @@ brcmf_txrx_stypes[NUM_NL80211_IFTYPES] = { * * p2p, mchan, and mbss: * - * #STA <= 1, #P2P-DEV <= 1, #{P2P-CL, P2P-GO} <= 1, channels = 2, 3 total + * #STA <= 2, #P2P-DEV <= 1, #{P2P-CL, P2P-GO} <= 1, channels = 2, 3 total * #STA <= 1, #P2P-DEV <= 1, #AP <= 1, #P2P-CL <= 1, channels = 1, 4 total * #AP <= 4, matching BI, channels = 1, 4 total * @@ -7106,7 +7206,7 @@ static int brcmf_setup_ifmodes(struct wiphy *wiphy, struct brcmf_if *ifp) goto err; combo[c].num_different_channels = 1 + (rsdb || (p2p && mchan)); - c0_limits[i].max = 1; + c0_limits[i].max = 1 + (p2p && mchan); c0_limits[i++].types = BIT(NL80211_IFTYPE_STATION); if (mon_flag) { c0_limits[i].max = 1; From 1562bdef925117dcd5a5678417c01f703be99589 Mon Sep 17 00:00:00 2001 From: Prasanna Kerekoppa Date: Thu, 29 Sep 2022 00:06:13 -0500 Subject: [PATCH 0042/2002] brcmfmac: Fix AP interface delete issue Fixes the ap interface delete issue. Fix is to make sure interface is created with supported version. Patch has been verified by creating and deleting AP interface. Signed-off-by: Prasanna Kerekoppa Signed-off-by: Ian Lin Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20220929050614.31518-4-ian.lin@infineon.com --- .../broadcom/brcm80211/brcmfmac/cfg80211.c | 101 ++++++++++++++++-- 1 file changed, 90 insertions(+), 11 deletions(-) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c index e5f0a9ef2d0f..bf184c0e64cb 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c @@ -702,24 +702,103 @@ static int brcmf_cfg80211_request_sta_if(struct brcmf_if *ifp, u8 *macaddr) static int brcmf_cfg80211_request_ap_if(struct brcmf_if *ifp) { + struct wl_interface_create_v1 iface_v1; + struct wl_interface_create_v2 iface_v2; + struct wl_interface_create_v3 iface_v3; + u32 iface_create_ver; struct brcmf_pub *drvr = ifp->drvr; struct brcmf_mbss_ssid_le mbss_ssid_le; int bsscfgidx; int err; - memset(&mbss_ssid_le, 0, sizeof(mbss_ssid_le)); - bsscfgidx = brcmf_get_first_free_bsscfgidx(ifp->drvr); - if (bsscfgidx < 0) - return bsscfgidx; + /* interface_create version 1 */ + memset(&iface_v1, 0, sizeof(iface_v1)); + iface_v1.ver = WL_INTERFACE_CREATE_VER_1; + iface_v1.flags = WL_INTERFACE_CREATE_AP | + WL_INTERFACE_MAC_USE; - mbss_ssid_le.bsscfgidx = cpu_to_le32(bsscfgidx); - mbss_ssid_le.SSID_len = cpu_to_le32(5); - sprintf(mbss_ssid_le.SSID, "ssid%d" , bsscfgidx); + brcmf_set_vif_sta_macaddr(ifp, iface_v1.mac_addr); - err = brcmf_fil_bsscfg_data_set(ifp, "bsscfg:ssid", &mbss_ssid_le, - sizeof(mbss_ssid_le)); - if (err < 0) - bphy_err(drvr, "setting ssid failed %d\n", err); + err = brcmf_fil_iovar_data_get(ifp, "interface_create", + &iface_v1, + sizeof(iface_v1)); + if (err) { + brcmf_info("failed to create interface(v1), err=%d\n", + err); + } else { + brcmf_dbg(INFO, "interface created(v1)\n"); + return 0; + } + + /* interface_create version 2 */ + memset(&iface_v2, 0, sizeof(iface_v2)); + iface_v2.ver = WL_INTERFACE_CREATE_VER_2; + iface_v2.flags = WL_INTERFACE_MAC_USE; + iface_v2.iftype = WL_INTERFACE_CREATE_AP; + + brcmf_set_vif_sta_macaddr(ifp, iface_v2.mac_addr); + + err = brcmf_fil_iovar_data_get(ifp, "interface_create", + &iface_v2, + sizeof(iface_v2)); + if (err) { + brcmf_info("failed to create interface(v2), err=%d\n", + err); + } else { + brcmf_dbg(INFO, "interface created(v2)\n"); + return 0; + } + + /* interface_create version 3+ */ + /* get supported version from firmware side */ + iface_create_ver = 0; + err = brcmf_fil_bsscfg_int_get(ifp, "interface_create", + &iface_create_ver); + if (err) { + brcmf_err("fail to get supported version, err=%d\n", err); + return -EOPNOTSUPP; + } + + switch (iface_create_ver) { + case WL_INTERFACE_CREATE_VER_3: + memset(&iface_v3, 0, sizeof(iface_v3)); + iface_v3.ver = WL_INTERFACE_CREATE_VER_3; + iface_v3.flags = WL_INTERFACE_MAC_USE; + iface_v3.iftype = WL_INTERFACE_CREATE_AP; + brcmf_set_vif_sta_macaddr(ifp, iface_v3.mac_addr); + + err = brcmf_fil_iovar_data_get(ifp, "interface_create", + &iface_v3, + sizeof(iface_v3)); + + if (!err) + brcmf_dbg(INFO, "interface created(v3)\n"); + break; + default: + brcmf_err("not support interface create(v%d)\n", + iface_create_ver); + err = -EOPNOTSUPP; + break; + } + + if (err) { + brcmf_info("Does not support interface_create (%d)\n", + err); + memset(&mbss_ssid_le, 0, sizeof(mbss_ssid_le)); + bsscfgidx = brcmf_get_first_free_bsscfgidx(ifp->drvr); + if (bsscfgidx < 0) + return bsscfgidx; + + mbss_ssid_le.bsscfgidx = cpu_to_le32(bsscfgidx); + mbss_ssid_le.SSID_len = cpu_to_le32(5); + sprintf(mbss_ssid_le.SSID, "ssid%d", bsscfgidx); + + err = brcmf_fil_bsscfg_data_set(ifp, "bsscfg:ssid", &mbss_ssid_le, + sizeof(mbss_ssid_le)); + + if (err < 0) + bphy_err(drvr, "setting ssid failed %d\n", err); + } return err; } From 68b0ce5bb4002cd657963cb876f0ff51729f9bfc Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Fri, 30 Sep 2022 21:33:17 +0800 Subject: [PATCH 0043/2002] wifi: rtw89: 8852c: correct set of IQK backup registers IQK can change the values of this register set, so need to backup and restore the values. During we rewrite IQK, the policy is changed. Some values are controlled and filled by IQK, and don't need to restore after IQK. Therefore, remove this kind of registers from this array. Signed-off-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20220930133318.6335-1-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/rtw8852c_rfk.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852c_rfk.c b/drivers/net/wireless/realtek/rtw89/rtw8852c_rfk.c index 006c2cf93111..1e67a565a9e0 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852c_rfk.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852c_rfk.c @@ -22,8 +22,7 @@ static const u32 _tssi_de_mcs_5m[RF_PATH_NUM_8852C] = {0x5828, 0x7828}; static const u32 _tssi_de_mcs_10m[RF_PATH_NUM_8852C] = {0x5830, 0x7830}; static const u32 rtw8852c_backup_bb_regs[] = { - 0x813c, 0x8124, 0x8120, 0xc0d4, 0xc0d8, 0xc0e8, 0x823c, 0x8224, 0x8220, - 0xc1d4, 0xc1d8, 0xc1e8 + 0x8120, 0xc0d4, 0xc0d8, 0xc0e8, 0x8220, 0xc1d4, 0xc1d8, 0xc1e8 }; static const u32 rtw8852c_backup_rf_regs[] = { From 3be11416204a7aecbdba8c843849f204c631b8e6 Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Fri, 30 Sep 2022 21:33:18 +0800 Subject: [PATCH 0044/2002] wifi: rtw89: 8852c: rfk: correct miscoding delay of DPK Using mdelay() can work well, but calibration causes too much time. Use proper udelay() to get shorter time and the same result. Signed-off-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20220930133318.6335-2-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/rtw8852c_rfk.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852c_rfk.c b/drivers/net/wireless/realtek/rtw89/rtw8852c_rfk.c index 1e67a565a9e0..b0672b906e7b 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852c_rfk.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852c_rfk.c @@ -1666,7 +1666,7 @@ static u8 _dpk_one_shot(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy, ret = read_poll_timeout_atomic(rtw89_phy_read32_mask, val, val == 0x55, 10, 20000, false, rtwdev, 0xbff8, MASKBYTE0); - mdelay(10); + udelay(10); rtw89_phy_write32_clr(rtwdev, R_NCTL_N1, MASKBYTE0); rtw89_debug(rtwdev, RTW89_DBG_RFK, From a9ee25c32fd4569f63cd34def9a013fb3dad8e01 Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Fri, 30 Sep 2022 21:36:58 +0800 Subject: [PATCH 0045/2002] wifi: rtw89: 8852c: update BB parameters to v28 Update BB parameters along with internal tag HALBB_027_067_07. Signed-off-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20220930133659.7789-1-pkshih@realtek.com --- .../wireless/realtek/rtw89/rtw8852c_table.c | 988 ++++++++++++++++-- 1 file changed, 879 insertions(+), 109 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852c_table.c b/drivers/net/wireless/realtek/rtw89/rtw8852c_table.c index 11f35e7a7f0e..96c264a057ff 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852c_table.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852c_table.c @@ -10,6 +10,8 @@ static const struct rtw89_reg2_def rtw89_8852c_phy_bb_regs[] = { {0xF0FF0000, 0x00000000}, {0xF03300FF, 0x00000001}, {0xF03400FF, 0x00000002}, + {0xF03500FF, 0x00000003}, + {0xF03600FF, 0x00000004}, {0x70C, 0x00000020}, {0x704, 0x601E0100}, {0x4000, 0x00000000}, @@ -200,7 +202,7 @@ static const struct rtw89_reg2_def rtw89_8852c_phy_bb_regs[] = { {0x4264, 0x00000000}, {0x4268, 0x00000000}, {0x426C, 0x0418317C}, - {0x46C0, 0x00000001}, + {0x46C0, 0x00000000}, {0x4270, 0x00D6135C}, {0x46C4, 0x00000033}, {0x4274, 0x00000000}, @@ -342,7 +344,7 @@ static const struct rtw89_reg2_def rtw89_8852c_phy_bb_regs[] = { {0x442C, 0x00000000}, {0x4430, 0x00000000}, {0x4434, 0x00000000}, - {0x4438, 0x590642D0}, + {0x4438, 0x59096398}, {0x443C, 0x398668A0}, {0x4440, 0x6C100808}, {0x4444, 0x4A145344}, @@ -566,9 +568,9 @@ static const struct rtw89_reg2_def rtw89_8852c_phy_bb_regs[] = { {0x4BA8, 0x002B6456}, {0x45E0, 0x00000000}, {0x45E4, 0x00000000}, - {0x45E8, 0x00E2E1E1}, + {0x45E8, 0x00C8E1E1}, {0x45EC, 0xCBCBB6B6}, - {0x45F0, 0x59100FCA}, + {0x45F0, 0x5F900FCA}, {0x4BAC, 0x12CAB6DE}, {0x4BB0, 0x00001110}, {0x45F4, 0x08882550}, @@ -584,9 +586,17 @@ static const struct rtw89_reg2_def rtw89_8852c_phy_bb_regs[] = { {0x4660, 0x41250EF4}, {0x4664, 0x6750E458}, {0x903400ff, 0x00000000}, {0x40000000, 0x00000000}, - {0x45DC, 0xE1CB38E8}, - {0x4660, 0x4A2E1800}, - {0x4664, 0x6750E462}, + {0x45DC, 0xD1B942F4}, + {0x4660, 0x41250EF4}, + {0x4664, 0x6750E458}, + {0x903500ff, 0x00000000}, {0x40000000, 0x00000000}, + {0x45DC, 0xD1B942F4}, + {0x4660, 0x41250EF4}, + {0x4664, 0x6750E458}, + {0x903600ff, 0x00000000}, {0x40000000, 0x00000000}, + {0x45DC, 0xD1B942F4}, + {0x4660, 0x41250EF4}, + {0x4664, 0x6750E458}, {0xA0000000, 0x00000000}, {0x45DC, 0xE1CB38E8}, {0x4660, 0x4A2E1800}, @@ -603,7 +613,19 @@ static const struct rtw89_reg2_def rtw89_8852c_phy_bb_regs[] = { {0x4688, 0x1A10FF04}, {0x468C, 0x282A3000}, {0x4690, 0x2A29292A}, + {0x80ff0000, 0x00000000}, {0x40000000, 0x00000000}, {0x4694, 0x04FA2A2A}, + {0x903300ff, 0x00000000}, {0x40000000, 0x00000000}, + {0x4694, 0x04FA2A2A}, + {0x903400ff, 0x00000000}, {0x40000000, 0x00000000}, + {0x4694, 0x06FA2A2A}, + {0x903500ff, 0x00000000}, {0x40000000, 0x00000000}, + {0x4694, 0x04FA2A2A}, + {0x903600ff, 0x00000000}, {0x40000000, 0x00000000}, + {0x4694, 0x04FA2A2A}, + {0xA0000000, 0x00000000}, + {0x4694, 0x04FA2A2A}, + {0xB0000000, 0x00000000}, {0x4698, 0xEE0F04D1}, {0x469C, 0x89291436}, {0x80ff0000, 0x00000000}, {0x40000000, 0x00000000}, @@ -612,6 +634,10 @@ static const struct rtw89_reg2_def rtw89_8852c_phy_bb_regs[] = { {0x46A0, 0x0701E79E}, {0x903400ff, 0x00000000}, {0x40000000, 0x00000000}, {0x46A0, 0x0701E79E}, + {0x903500ff, 0x00000000}, {0x40000000, 0x00000000}, + {0x46A0, 0x0701E79E}, + {0x903600ff, 0x00000000}, {0x40000000, 0x00000000}, + {0x46A0, 0x0701E79E}, {0xA0000000, 0x00000000}, {0x46A0, 0x0701E79E}, {0xB0000000, 0x00000000}, @@ -620,11 +646,17 @@ static const struct rtw89_reg2_def rtw89_8852c_phy_bb_regs[] = { {0x46A8, 0x2212FF14}, {0x46AC, 0x60423537}, {0x903300ff, 0x00000000}, {0x40000000, 0x00000000}, + {0x46A8, 0x649EFF14}, + {0x46AC, 0xA1B37C4E}, + {0x903400ff, 0x00000000}, {0x40000000, 0x00000000}, {0x46A8, 0x4D1E7F14}, {0x46AC, 0x60B37C4E}, - {0x903400ff, 0x00000000}, {0x40000000, 0x00000000}, - {0x46A8, 0x2212FF14}, - {0x46AC, 0x60423537}, + {0x903500ff, 0x00000000}, {0x40000000, 0x00000000}, + {0x46A8, 0x649EFF14}, + {0x46AC, 0xA1B37C4E}, + {0x903600ff, 0x00000000}, {0x40000000, 0x00000000}, + {0x46A8, 0x649EFF14}, + {0x46AC, 0xA1B37C4E}, {0xA0000000, 0x00000000}, {0x46A8, 0x2212FF14}, {0x46AC, 0x60423537}, @@ -637,11 +669,19 @@ static const struct rtw89_reg2_def rtw89_8852c_phy_bb_regs[] = { {0x4720, 0x3FFFFD63}, {0x4724, 0xB58D11FF}, {0x903300ff, 0x00000000}, {0x40000000, 0x00000000}, - {0x46BC, 0x5107C252}, - {0x4720, 0x27795843}, + {0x46BC, 0x510FC252}, + {0x4720, 0x27795303}, {0x4724, 0xB58D11F5}, {0x903400ff, 0x00000000}, {0x40000000, 0x00000000}, - {0x46BC, 0x5107C252}, + {0x46BC, 0x510FC252}, + {0x4720, 0x27795843}, + {0x4724, 0xB58D11F5}, + {0x903500ff, 0x00000000}, {0x40000000, 0x00000000}, + {0x46BC, 0x510FC252}, + {0x4720, 0x27795303}, + {0x4724, 0xB58D11F5}, + {0x903600ff, 0x00000000}, {0x40000000, 0x00000000}, + {0x46BC, 0x510FC252}, {0x4720, 0x27795303}, {0x4724, 0xB58D11F5}, {0xA0000000, 0x00000000}, @@ -656,11 +696,17 @@ static const struct rtw89_reg2_def rtw89_8852c_phy_bb_regs[] = { {0x4734, 0x00000020}, {0x4738, 0x8325C500}, {0x903300ff, 0x00000000}, {0x40000000, 0x00000000}, - {0x4734, 0x003D4C20}, + {0x4734, 0x003D5420}, {0x4738, 0x8F25C500}, {0x903400ff, 0x00000000}, {0x40000000, 0x00000000}, + {0x4734, 0x003D4C20}, + {0x4738, 0x8F25C500}, + {0x903500ff, 0x00000000}, {0x40000000, 0x00000000}, {0x4734, 0x003D5420}, - {0x4738, 0x8725C500}, + {0x4738, 0x8F25C500}, + {0x903600ff, 0x00000000}, {0x40000000, 0x00000000}, + {0x4734, 0x003D5420}, + {0x4738, 0x8F25C500}, {0xA0000000, 0x00000000}, {0x4734, 0x00000020}, {0x4738, 0x8325C500}, @@ -678,8 +724,14 @@ static const struct rtw89_reg2_def rtw89_8852c_phy_bb_regs[] = { {0x4BB4, 0x05EBC8AF}, {0x4BB8, 0x99543D24}, {0x903400ff, 0x00000000}, {0x40000000, 0x00000000}, - {0x4BB4, 0xFBD5B89F}, - {0x4BB8, 0x99563918}, + {0x4BB4, 0x05EBC8AF}, + {0x4BB8, 0x99543D24}, + {0x903500ff, 0x00000000}, {0x40000000, 0x00000000}, + {0x4BB4, 0x05EBC8AF}, + {0x4BB8, 0x99543D24}, + {0x903600ff, 0x00000000}, {0x40000000, 0x00000000}, + {0x4BB4, 0x05EBC8AF}, + {0x4BB8, 0x99543D24}, {0xA0000000, 0x00000000}, {0x4BB4, 0xFBD5B89F}, {0x4BB8, 0x99563918}, @@ -729,10 +781,10 @@ static const struct rtw89_reg2_def rtw89_8852c_phy_bb_regs[] = { {0x4C58, 0x00001146}, {0x4C5C, 0x00000000}, {0x4C60, 0x00000000}, - {0x4C64, 0xE2E1E1DE}, + {0x4C64, 0xC8E1E1DE}, {0x4C68, 0xB6B600B6}, {0x4C6C, 0xCACBCBCA}, - {0x4C70, 0x8091010F}, + {0x4C70, 0x80F9010F}, {0x4C74, 0x00000B11}, {0x46C8, 0x08882550}, {0x46CC, 0x08CC2660}, @@ -747,9 +799,17 @@ static const struct rtw89_reg2_def rtw89_8852c_phy_bb_regs[] = { {0x4744, 0x412504E8}, {0x4748, 0x6850E459}, {0x903400ff, 0x00000000}, {0x40000000, 0x00000000}, - {0x4740, 0xE4CD38E8}, - {0x4744, 0x4C321B04}, - {0x4748, 0x6750E466}, + {0x4740, 0xC5AD42F4}, + {0x4744, 0x412504E8}, + {0x4748, 0x6850E459}, + {0x903500ff, 0x00000000}, {0x40000000, 0x00000000}, + {0x4740, 0xC5AD42F4}, + {0x4744, 0x412504E8}, + {0x4748, 0x6850E459}, + {0x903600ff, 0x00000000}, {0x40000000, 0x00000000}, + {0x4740, 0xC5AD42F4}, + {0x4744, 0x412504E8}, + {0x4748, 0x6850E459}, {0xA0000000, 0x00000000}, {0x4740, 0xE4CD38E8}, {0x4744, 0x4C321B04}, @@ -766,7 +826,19 @@ static const struct rtw89_reg2_def rtw89_8852c_phy_bb_regs[] = { {0x476C, 0x1A10FF04}, {0x4770, 0x282A3000}, {0x4774, 0x2A29292A}, + {0x80ff0000, 0x00000000}, {0x40000000, 0x00000000}, {0x4778, 0x04FA2A2A}, + {0x903300ff, 0x00000000}, {0x40000000, 0x00000000}, + {0x4778, 0x04FA2A2A}, + {0x903400ff, 0x00000000}, {0x40000000, 0x00000000}, + {0x4778, 0x06FA2A2A}, + {0x903500ff, 0x00000000}, {0x40000000, 0x00000000}, + {0x4778, 0x04FA2A2A}, + {0x903600ff, 0x00000000}, {0x40000000, 0x00000000}, + {0x4778, 0x04FA2A2A}, + {0xA0000000, 0x00000000}, + {0x4778, 0x04FA2A2A}, + {0xB0000000, 0x00000000}, {0x477C, 0xEE0F04D1}, {0x49F0, 0x89291436}, {0x80ff0000, 0x00000000}, {0x40000000, 0x00000000}, @@ -775,6 +847,10 @@ static const struct rtw89_reg2_def rtw89_8852c_phy_bb_regs[] = { {0x49F4, 0x0701E79E}, {0x903400ff, 0x00000000}, {0x40000000, 0x00000000}, {0x49F4, 0x0701E79E}, + {0x903500ff, 0x00000000}, {0x40000000, 0x00000000}, + {0x49F4, 0x0701E79E}, + {0x903600ff, 0x00000000}, {0x40000000, 0x00000000}, + {0x49F4, 0x0701E79E}, {0xA0000000, 0x00000000}, {0x49F4, 0x0701E79E}, {0xB0000000, 0x00000000}, @@ -783,11 +859,17 @@ static const struct rtw89_reg2_def rtw89_8852c_phy_bb_regs[] = { {0x4A5C, 0x2212FF14}, {0x4A60, 0x60423537}, {0x903300ff, 0x00000000}, {0x40000000, 0x00000000}, + {0x4A5C, 0x649EFF14}, + {0x4A60, 0xA1B37C4E}, + {0x903400ff, 0x00000000}, {0x40000000, 0x00000000}, {0x4A5C, 0x4D1E7F14}, {0x4A60, 0x60B37C4E}, - {0x903400ff, 0x00000000}, {0x40000000, 0x00000000}, - {0x4A5C, 0x2212FF14}, - {0x4A60, 0x60423537}, + {0x903500ff, 0x00000000}, {0x40000000, 0x00000000}, + {0x4A5C, 0x649EFF14}, + {0x4A60, 0xA1B37C4E}, + {0x903600ff, 0x00000000}, {0x40000000, 0x00000000}, + {0x4A5C, 0x649EFF14}, + {0x4A60, 0xA1B37C4E}, {0xA0000000, 0x00000000}, {0x4A5C, 0x2212FF14}, {0x4A60, 0x60423537}, @@ -800,11 +882,19 @@ static const struct rtw89_reg2_def rtw89_8852c_phy_bb_regs[] = { {0x4A74, 0x3FFFFD63}, {0x4A78, 0xB58D11FF}, {0x903300ff, 0x00000000}, {0x40000000, 0x00000000}, - {0x4A70, 0x5107C252}, - {0x4A74, 0x27795843}, + {0x4A70, 0x510FC252}, + {0x4A74, 0x27795303}, {0x4A78, 0xB58D11F5}, {0x903400ff, 0x00000000}, {0x40000000, 0x00000000}, - {0x4A70, 0x5107C252}, + {0x4A70, 0x510FC252}, + {0x4A74, 0x27795843}, + {0x4A78, 0xB58D11F5}, + {0x903500ff, 0x00000000}, {0x40000000, 0x00000000}, + {0x4A70, 0x510FC252}, + {0x4A74, 0x27795303}, + {0x4A78, 0xB58D11F5}, + {0x903600ff, 0x00000000}, {0x40000000, 0x00000000}, + {0x4A70, 0x510FC252}, {0x4A74, 0x27795303}, {0x4A78, 0xB58D11F5}, {0xA0000000, 0x00000000}, @@ -819,11 +909,17 @@ static const struct rtw89_reg2_def rtw89_8852c_phy_bb_regs[] = { {0x4AA0, 0x00000020}, {0x4AA4, 0x8325C500}, {0x903300ff, 0x00000000}, {0x40000000, 0x00000000}, - {0x4AA0, 0x003D4C20}, + {0x4AA0, 0x003D5420}, {0x4AA4, 0x8F25C500}, {0x903400ff, 0x00000000}, {0x40000000, 0x00000000}, + {0x4AA0, 0x003D4C20}, + {0x4AA4, 0x8F25C500}, + {0x903500ff, 0x00000000}, {0x40000000, 0x00000000}, {0x4AA0, 0x003D5420}, - {0x4AA4, 0x8725C500}, + {0x4AA4, 0x8F25C500}, + {0x903600ff, 0x00000000}, {0x40000000, 0x00000000}, + {0x4AA0, 0x003D5420}, + {0x4AA4, 0x8F25C500}, {0xA0000000, 0x00000000}, {0x4AA0, 0x00000020}, {0x4AA4, 0x8325C500}, @@ -841,8 +937,14 @@ static const struct rtw89_reg2_def rtw89_8852c_phy_bb_regs[] = { {0x4C78, 0x07ECC9B0}, {0x4C7C, 0x995B4126}, {0x903400ff, 0x00000000}, {0x40000000, 0x00000000}, - {0x4C78, 0xFBD5B89F}, - {0x4C7C, 0x99563918}, + {0x4C78, 0x07ECC9B0}, + {0x4C7C, 0x995B4126}, + {0x903500ff, 0x00000000}, {0x40000000, 0x00000000}, + {0x4C78, 0x07ECC9B0}, + {0x4C7C, 0x995B4126}, + {0x903600ff, 0x00000000}, {0x40000000, 0x00000000}, + {0x4C78, 0x07ECC9B0}, + {0x4C7C, 0x995B4126}, {0xA0000000, 0x00000000}, {0x4C78, 0xFBD5B89F}, {0x4C7C, 0x99563918}, @@ -907,17 +1009,46 @@ static const struct rtw89_reg2_def rtw89_8852c_phy_bb_regs[] = { {0x47B4, 0x00000005}, {0x4D2C, 0x0008C0C1}, {0x47B8, 0x00001759}, - {0x47BC, 0x4B702400}, - {0x47C0, 0x831508BA}, + {0x47BC, 0x4B002402}, + {0x47C0, 0x831508BC}, {0x4A14, 0x000000E9}, - {0x4D30, 0x00000001}, + {0x4D30, 0x00000000}, {0x4E94, 0x000000FC}, + {0x80ff0000, 0x00000000}, {0x40000000, 0x00000000}, {0x47C4, 0x9ABBCACB}, {0x47C8, 0x56767578}, + {0x903300ff, 0x00000000}, {0x40000000, 0x00000000}, + {0x47C4, 0x9ABBCACB}, + {0x47C8, 0x56767578}, + {0x903400ff, 0x00000000}, {0x40000000, 0x00000000}, + {0x47C4, 0x9ABBCACB}, + {0x47C8, 0x56767578}, + {0x903500ff, 0x00000000}, {0x40000000, 0x00000000}, + {0x47C4, 0x9ABBCACB}, + {0x47C8, 0x56767578}, + {0x903600ff, 0x00000000}, {0x40000000, 0x00000000}, + {0x47C4, 0x9ABBCACB}, + {0x47C8, 0x56767578}, + {0xA0000000, 0x00000000}, + {0x47C4, 0x9ABBCACB}, + {0x47C8, 0x56767578}, + {0xB0000000, 0x00000000}, {0x47CC, 0xBBCCBBB3}, {0x47D0, 0x57889989}, {0x47D4, 0x00000F45}, + {0x80ff0000, 0x00000000}, {0x40000000, 0x00000000}, {0x4D34, 0x7BB167AB}, + {0x903300ff, 0x00000000}, {0x40000000, 0x00000000}, + {0x4D34, 0x7BB1579A}, + {0x903400ff, 0x00000000}, {0x40000000, 0x00000000}, + {0x4D34, 0x7BB167AB}, + {0x903500ff, 0x00000000}, {0x40000000, 0x00000000}, + {0x4D34, 0x7BB1579A}, + {0x903600ff, 0x00000000}, {0x40000000, 0x00000000}, + {0x4D34, 0x7BB1579A}, + {0xA0000000, 0x00000000}, + {0x4D34, 0x7BB167AB}, + {0xB0000000, 0x00000000}, {0x4D38, 0xBBBBBB05}, {0x4D3C, 0x777777BB}, {0x4D40, 0x00015277}, @@ -942,7 +1073,19 @@ static const struct rtw89_reg2_def rtw89_8852c_phy_bb_regs[] = { {0x4D48, 0x8C413016}, {0x4D4C, 0xA140B028}, {0x4D50, 0x00150A31}, + {0x80ff0000, 0x00000000}, {0x40000000, 0x00000000}, {0x481C, 0x576DF814}, + {0x903300ff, 0x00000000}, {0x40000000, 0x00000000}, + {0x481C, 0x576DF814}, + {0x903400ff, 0x00000000}, {0x40000000, 0x00000000}, + {0x481C, 0x576BF814}, + {0x903500ff, 0x00000000}, {0x40000000, 0x00000000}, + {0x481C, 0x576DF814}, + {0x903600ff, 0x00000000}, {0x40000000, 0x00000000}, + {0x481C, 0x576DF814}, + {0xA0000000, 0x00000000}, + {0x481C, 0x576DF814}, + {0xB0000000, 0x00000000}, {0x4820, 0xA08877AC}, {0x4824, 0x0000007A}, {0x4D54, 0x00001184}, @@ -967,7 +1110,19 @@ static const struct rtw89_reg2_def rtw89_8852c_phy_bb_regs[] = { {0x4D78, 0x994C1502}, {0x4D7C, 0x00017912}, {0x4EDC, 0x00000001}, + {0x80ff0000, 0x00000000}, {0x40000000, 0x00000000}, + {0x484C, 0x0000CA62}, + {0x903300ff, 0x00000000}, {0x40000000, 0x00000000}, {0x484C, 0x00008A62}, + {0x903400ff, 0x00000000}, {0x40000000, 0x00000000}, + {0x484C, 0x0000CA62}, + {0x903500ff, 0x00000000}, {0x40000000, 0x00000000}, + {0x484C, 0x00008A62}, + {0x903600ff, 0x00000000}, {0x40000000, 0x00000000}, + {0x484C, 0x00008A62}, + {0xA0000000, 0x00000000}, + {0x484C, 0x0000CA62}, + {0xB0000000, 0x00000000}, {0x4D80, 0x00000002}, {0x4850, 0x00000008}, {0x4854, 0x009B902A}, @@ -1014,7 +1169,19 @@ static const struct rtw89_reg2_def rtw89_8852c_phy_bb_regs[] = { {0x4DA0, 0x8C413016}, {0x4DA4, 0xA140B028}, {0x4DA8, 0x00150A31}, + {0x80ff0000, 0x00000000}, {0x40000000, 0x00000000}, {0x48D4, 0x576DF814}, + {0x903300ff, 0x00000000}, {0x40000000, 0x00000000}, + {0x48D4, 0x576BF814}, + {0x903400ff, 0x00000000}, {0x40000000, 0x00000000}, + {0x48D4, 0x576BF814}, + {0x903500ff, 0x00000000}, {0x40000000, 0x00000000}, + {0x48D4, 0x576BF814}, + {0x903600ff, 0x00000000}, {0x40000000, 0x00000000}, + {0x48D4, 0x576BF814}, + {0xA0000000, 0x00000000}, + {0x48D4, 0x576DF814}, + {0xB0000000, 0x00000000}, {0x48D8, 0xA08877AC}, {0x48DC, 0x0000007A}, {0x4DAC, 0x00001184}, @@ -1039,7 +1206,19 @@ static const struct rtw89_reg2_def rtw89_8852c_phy_bb_regs[] = { {0x4DD0, 0x994C1502}, {0x4DD4, 0x00017912}, {0x4EE4, 0x00000001}, - {0x4904, 0x00008A62}, + {0x80ff0000, 0x00000000}, {0x40000000, 0x00000000}, + {0x4904, 0x0000CA62}, + {0x903300ff, 0x00000000}, {0x40000000, 0x00000000}, + {0x4904, 0x0000CA62}, + {0x903400ff, 0x00000000}, {0x40000000, 0x00000000}, + {0x4904, 0x0000CA62}, + {0x903500ff, 0x00000000}, {0x40000000, 0x00000000}, + {0x4904, 0x0000CA62}, + {0x903600ff, 0x00000000}, {0x40000000, 0x00000000}, + {0x4904, 0x0000CA62}, + {0xA0000000, 0x00000000}, + {0x4904, 0x0000CA62}, + {0xB0000000, 0x00000000}, {0x4DD8, 0x00000002}, {0x4908, 0x00000008}, {0x490C, 0x80040000}, @@ -1096,8 +1275,8 @@ static const struct rtw89_reg2_def rtw89_8852c_phy_bb_regs[] = { {0x4988, 0x00000000}, {0x498C, 0x00000000}, {0x4E34, 0x00FC0000}, - {0x4E38, 0x0000F800}, - {0x4E3C, 0x00000001}, + {0x4E38, 0x00000000}, + {0x4E3C, 0x00000003}, {0x4990, 0x00000000}, {0x4994, 0x00000000}, {0x4998, 0x00000000}, @@ -1134,7 +1313,7 @@ static const struct rtw89_reg2_def rtw89_8852c_phy_bb_regs[] = { {0x710, 0xEF810000}, {0xC54, 0x1AE1436A}, {0xC58, 0x41000000}, - {0xC68, 0x10000050}, + {0xC68, 0x90000050}, {0xC6C, 0x20061020}, {0x704, 0x601E0100}, {0xC74, 0x00000000}, @@ -1225,12 +1404,12 @@ static const struct rtw89_reg2_def rtw89_8852c_phy_bb_regs[] = { {0x328, 0xE000E000}, {0x32C, 0x0041E000}, {0x35C, 0x000004C4}, - {0xC0D4, 0xA7C41460}, + {0xC0D4, 0xA7441460}, {0xC0D8, 0xC6BA7F67}, {0xC0DC, 0x30C52868}, {0xC0E0, 0x75008128}, {0xC0E4, 0x0000272B}, - {0xC1D4, 0xA7C41460}, + {0xC1D4, 0xA7441460}, {0xC1D8, 0xC6BA7F67}, {0xC1DC, 0x30C52868}, {0xC1E0, 0x75008128}, @@ -1290,7 +1469,7 @@ static const struct rtw89_reg2_def rtw89_8852c_phy_bb_regs[] = { {0xC8C, 0x02F2FC08}, {0xC70, 0x071BFC00}, {0x980, 0x10002251}, - {0x988, 0x3C3C4107}, + {0x988, 0x3C3C8107}, {0x904, 0x00000005}, {0x994, 0x00000010}, {0x000, 0x0580801F}, @@ -1359,7 +1538,19 @@ static const struct rtw89_reg2_def rtw89_8852c_phy_bb_regs[] = { {0x2310, 0xBC80536C}, {0x2314, 0x0363A0F3}, {0x2318, 0x000000BB}, + {0x80ff0000, 0x00000000}, {0x40000000, 0x00000000}, {0x724, 0x00111200}, + {0x903300ff, 0x00000000}, {0x40000000, 0x00000000}, + {0x724, 0x20111100}, + {0x903400ff, 0x00000000}, {0x40000000, 0x00000000}, + {0x724, 0x20111100}, + {0x903500ff, 0x00000000}, {0x40000000, 0x00000000}, + {0x724, 0x01100100}, + {0x903600ff, 0x00000000}, {0x40000000, 0x00000000}, + {0x724, 0x01100100}, + {0xA0000000, 0x00000000}, + {0x724, 0x00111200}, + {0xB0000000, 0x00000000}, {0x704, 0x601E0D00}, {0xC78, 0xBFFFFFFF}, {0x704, 0x601E0D02}, @@ -1393,7 +1584,7 @@ static const struct rtw89_reg2_def rtw89_8852c_phy_bb_regs[] = { {0xC60, 0x017FFFF3}, {0xC70, 0x071BFE00}, {0xC70, 0x071BFE60}, - {0xC6C, 0x20061021}, + {0xC6C, 0x26061021}, {0x58AC, 0x08000000}, {0x78AC, 0x08000000}, {0x8120, 0x10000000}, @@ -1452,7 +1643,7 @@ static const struct rtw89_reg2_def rtw89_8852c_phy_bb_regs[] = { {0x12A0, 0x24903056}, {0x12AC, 0x12333121}, {0x12B8, 0x30020000}, - {0x2000, 0x18BBBF84}, + {0x2000, 0x20BBBF04}, {0x2C14, 0x85000005}, {0x3200, 0x00010142}, {0x32A0, 0x24903056}, @@ -1469,7 +1660,21 @@ static const struct rtw89_reg2_def rtw89_8852c_phy_bb_regs[] = { {0x76C8, 0x0E800400}, {0x984, 0x000000E0}, {0x2008, 0x000FFFFF}, + {0x1210, 0x8049E304}, + {0x3210, 0x8049E304}, + {0x80ff0000, 0x00000000}, {0x40000000, 0x00000000}, {0x58B0, 0x00000800}, + {0x903300ff, 0x00000000}, {0x40000000, 0x00000000}, + {0x58B0, 0x00000000}, + {0x903400ff, 0x00000000}, {0x40000000, 0x00000000}, + {0x58B0, 0x00000000}, + {0x903500ff, 0x00000000}, {0x40000000, 0x00000000}, + {0x58B0, 0x00000000}, + {0x903600ff, 0x00000000}, {0x40000000, 0x00000000}, + {0x58B0, 0x00000000}, + {0xA0000000, 0x00000000}, + {0x58B0, 0x00000800}, + {0xB0000000, 0x00000000}, {0x5A00, 0x00000000}, {0x5A04, 0x00000000}, {0x5A08, 0x00000000}, @@ -1479,7 +1684,19 @@ static const struct rtw89_reg2_def rtw89_8852c_phy_bb_regs[] = { {0x5A18, 0x00000000}, {0x5A1C, 0x00000000}, {0x5A20, 0x00000000}, + {0x80ff0000, 0x00000000}, {0x40000000, 0x00000000}, {0x5A24, 0x00050000}, + {0x903300ff, 0x00000000}, {0x40000000, 0x00000000}, + {0x5A24, 0x00000000}, + {0x903400ff, 0x00000000}, {0x40000000, 0x00000000}, + {0x5A24, 0x00000000}, + {0x903500ff, 0x00000000}, {0x40000000, 0x00000000}, + {0x5A24, 0x00000000}, + {0x903600ff, 0x00000000}, {0x40000000, 0x00000000}, + {0x5A24, 0x00000000}, + {0xA0000000, 0x00000000}, + {0x5A24, 0x00050000}, + {0xB0000000, 0x00000000}, {0x5A28, 0x00000000}, {0x5A2C, 0x00000000}, {0x5A30, 0x00000000}, @@ -1487,14 +1704,38 @@ static const struct rtw89_reg2_def rtw89_8852c_phy_bb_regs[] = { {0x5A38, 0x00000000}, {0x5A3C, 0x00000000}, {0x5A40, 0x00000000}, + {0x80ff0000, 0x00000000}, {0x40000000, 0x00000000}, {0x5A44, 0x00000005}, + {0x903300ff, 0x00000000}, {0x40000000, 0x00000000}, + {0x5A44, 0x00000000}, + {0x903400ff, 0x00000000}, {0x40000000, 0x00000000}, + {0x5A44, 0x00000000}, + {0x903500ff, 0x00000000}, {0x40000000, 0x00000000}, + {0x5A44, 0x00000000}, + {0x903600ff, 0x00000000}, {0x40000000, 0x00000000}, + {0x5A44, 0x00000000}, + {0xA0000000, 0x00000000}, + {0x5A44, 0x00000005}, + {0xB0000000, 0x00000000}, {0x5A48, 0x00000000}, {0x5A4C, 0x00000000}, {0x5A50, 0x00000000}, {0x5A54, 0x00000000}, {0x5A58, 0x00000000}, {0x5A5C, 0x00000000}, + {0x80ff0000, 0x00000000}, {0x40000000, 0x00000000}, {0x5A60, 0x00050000}, + {0x903300ff, 0x00000000}, {0x40000000, 0x00000000}, + {0x5A60, 0x00000000}, + {0x903400ff, 0x00000000}, {0x40000000, 0x00000000}, + {0x5A60, 0x00000000}, + {0x903500ff, 0x00000000}, {0x40000000, 0x00000000}, + {0x5A60, 0x00000000}, + {0x903600ff, 0x00000000}, {0x40000000, 0x00000000}, + {0x5A60, 0x00000000}, + {0xA0000000, 0x00000000}, + {0x5A60, 0x00050000}, + {0xB0000000, 0x00000000}, {0x5A64, 0x00000000}, {0x5A68, 0x00000000}, {0x5A6C, 0x00000000}, @@ -1514,12 +1755,49 @@ static const struct rtw89_reg2_def rtw89_8852c_phy_bb_regs[] = { {0x5AA4, 0x00000000}, {0x5AA8, 0x00000000}, {0x5AAC, 0x00000000}, + {0x80ff0000, 0x00000000}, {0x40000000, 0x00000000}, {0x5AB0, 0x00050005}, {0x5AB4, 0x00050005}, {0x5AB8, 0x00050005}, {0x5ABC, 0x00050005}, {0x5AC0, 0x00000005}, {0x78B0, 0x00000800}, + {0x903300ff, 0x00000000}, {0x40000000, 0x00000000}, + {0x5AB0, 0x00000000}, + {0x5AB4, 0x00000000}, + {0x5AB8, 0x00000000}, + {0x5ABC, 0x00000000}, + {0x5AC0, 0x00000000}, + {0x78B0, 0x00000000}, + {0x903400ff, 0x00000000}, {0x40000000, 0x00000000}, + {0x5AB0, 0x00000000}, + {0x5AB4, 0x00000000}, + {0x5AB8, 0x00000000}, + {0x5ABC, 0x00000000}, + {0x5AC0, 0x00000000}, + {0x78B0, 0x00000000}, + {0x903500ff, 0x00000000}, {0x40000000, 0x00000000}, + {0x5AB0, 0x00000000}, + {0x5AB4, 0x00000000}, + {0x5AB8, 0x00000000}, + {0x5ABC, 0x00000000}, + {0x5AC0, 0x00000000}, + {0x78B0, 0x00000000}, + {0x903600ff, 0x00000000}, {0x40000000, 0x00000000}, + {0x5AB0, 0x00000000}, + {0x5AB4, 0x00000000}, + {0x5AB8, 0x00000000}, + {0x5ABC, 0x00000000}, + {0x5AC0, 0x00000000}, + {0x78B0, 0x00000000}, + {0xA0000000, 0x00000000}, + {0x5AB0, 0x00050005}, + {0x5AB4, 0x00050005}, + {0x5AB8, 0x00050005}, + {0x5ABC, 0x00050005}, + {0x5AC0, 0x00000005}, + {0x78B0, 0x00000800}, + {0xB0000000, 0x00000000}, {0x7A00, 0x00000000}, {0x7A04, 0x00000000}, {0x7A08, 0x00000000}, @@ -1529,7 +1807,19 @@ static const struct rtw89_reg2_def rtw89_8852c_phy_bb_regs[] = { {0x7A18, 0x00000000}, {0x7A1C, 0x00000000}, {0x7A20, 0x00000000}, + {0x80ff0000, 0x00000000}, {0x40000000, 0x00000000}, {0x7A24, 0x00050000}, + {0x903300ff, 0x00000000}, {0x40000000, 0x00000000}, + {0x7A24, 0x00000000}, + {0x903400ff, 0x00000000}, {0x40000000, 0x00000000}, + {0x7A24, 0x00000000}, + {0x903500ff, 0x00000000}, {0x40000000, 0x00000000}, + {0x7A24, 0x00000000}, + {0x903600ff, 0x00000000}, {0x40000000, 0x00000000}, + {0x7A24, 0x00000000}, + {0xA0000000, 0x00000000}, + {0x7A24, 0x00050000}, + {0xB0000000, 0x00000000}, {0x7A28, 0x00000000}, {0x7A2C, 0x00000000}, {0x7A30, 0x00000000}, @@ -1537,14 +1827,38 @@ static const struct rtw89_reg2_def rtw89_8852c_phy_bb_regs[] = { {0x7A38, 0x00000000}, {0x7A3C, 0x00000000}, {0x7A40, 0x00000000}, + {0x80ff0000, 0x00000000}, {0x40000000, 0x00000000}, {0x7A44, 0x00000005}, + {0x903300ff, 0x00000000}, {0x40000000, 0x00000000}, + {0x7A44, 0x00000000}, + {0x903400ff, 0x00000000}, {0x40000000, 0x00000000}, + {0x7A44, 0x00000000}, + {0x903500ff, 0x00000000}, {0x40000000, 0x00000000}, + {0x7A44, 0x00000000}, + {0x903600ff, 0x00000000}, {0x40000000, 0x00000000}, + {0x7A44, 0x00000000}, + {0xA0000000, 0x00000000}, + {0x7A44, 0x00000005}, + {0xB0000000, 0x00000000}, {0x7A48, 0x00000000}, {0x7A4C, 0x00000000}, {0x7A50, 0x00000000}, {0x7A54, 0x00000000}, {0x7A58, 0x00000000}, {0x7A5C, 0x00000000}, + {0x80ff0000, 0x00000000}, {0x40000000, 0x00000000}, {0x7A60, 0x00050000}, + {0x903300ff, 0x00000000}, {0x40000000, 0x00000000}, + {0x7A60, 0x00000000}, + {0x903400ff, 0x00000000}, {0x40000000, 0x00000000}, + {0x7A60, 0x00000000}, + {0x903500ff, 0x00000000}, {0x40000000, 0x00000000}, + {0x7A60, 0x00000000}, + {0x903600ff, 0x00000000}, {0x40000000, 0x00000000}, + {0x7A60, 0x00000000}, + {0xA0000000, 0x00000000}, + {0x7A60, 0x00050000}, + {0xB0000000, 0x00000000}, {0x7A64, 0x00000000}, {0x7A68, 0x00000000}, {0x7A6C, 0x00000000}, @@ -1564,143 +1878,223 @@ static const struct rtw89_reg2_def rtw89_8852c_phy_bb_regs[] = { {0x7AA4, 0x00000000}, {0x7AA8, 0x00000000}, {0x7AAC, 0x00000000}, + {0x80ff0000, 0x00000000}, {0x40000000, 0x00000000}, {0x7AB0, 0x00050005}, {0x7AB4, 0x00050005}, {0x7AB8, 0x00050005}, {0x7ABC, 0x00050005}, {0x7AC0, 0x00000005}, + {0x903300ff, 0x00000000}, {0x40000000, 0x00000000}, + {0x7AB0, 0x00000000}, + {0x7AB4, 0x00000000}, + {0x7AB8, 0x00000000}, + {0x7ABC, 0x00000000}, + {0x7AC0, 0x00000000}, + {0x903400ff, 0x00000000}, {0x40000000, 0x00000000}, + {0x7AB0, 0x00000000}, + {0x7AB4, 0x00000000}, + {0x7AB8, 0x00000000}, + {0x7ABC, 0x00000000}, + {0x7AC0, 0x00000000}, + {0x903500ff, 0x00000000}, {0x40000000, 0x00000000}, + {0x7AB0, 0x00000000}, + {0x7AB4, 0x00000000}, + {0x7AB8, 0x00000000}, + {0x7ABC, 0x00000000}, + {0x7AC0, 0x00000000}, + {0x903600ff, 0x00000000}, {0x40000000, 0x00000000}, + {0x7AB0, 0x00000000}, + {0x7AB4, 0x00000000}, + {0x7AB8, 0x00000000}, + {0x7ABC, 0x00000000}, + {0x7AC0, 0x00000000}, + {0xA0000000, 0x00000000}, + {0x7AB0, 0x00050005}, + {0x7AB4, 0x00050005}, + {0x7AB8, 0x00050005}, + {0x7ABC, 0x00050005}, + {0x7AC0, 0x00000005}, + {0xB0000000, 0x00000000}, {0x0F0, 0x00010000}, - {0x0F4, 0x00000018}, - {0x0F8, 0x20220120}, + {0x0F4, 0x00000028}, + {0x0F8, 0x20220610}, }; static const struct rtw89_reg2_def rtw89_8852c_phy_bb_reg_gain[] = { {0xF0FF0000, 0x00000000}, {0xF03300FF, 0x00000001}, - {0x000, 0x01E3C39F}, - {0x001, 0x00694727}, - {0x002, 0x00005536}, - {0x100, 0x02E3C39F}, - {0x101, 0x0069472A}, + {0x000, 0x0EEECAA6}, + {0x001, 0x006C4B2C}, + {0x002, 0x00005636}, + {0x100, 0x0DEFCAA9}, + {0x101, 0x00694B2C}, {0x102, 0x00005536}, {0x80ff0000, 0x00000000}, {0x40000000, 0x00000000}, {0x10000, 0x1A02E1C9}, {0x10001, 0x00644A30}, {0x10002, 0x00006750}, {0x903300ff, 0x00000000}, {0x40000000, 0x00000000}, - {0x10000, 0x0EF4D1B9}, - {0x10001, 0x00584125}, - {0x10002, 0x00006750}, + {0x10000, 0x0BF1CEB6}, + {0x10001, 0x00434328}, + {0x10002, 0x00005050}, {0xA0000000, 0x00000000}, - {0x10000, 0x1A02E1C9}, - {0x10001, 0x00644A30}, - {0x10002, 0x00006750}, + {0x10000, 0x1D08E8D0}, + {0x10001, 0x00644C32}, + {0x10002, 0x00006650}, {0xB0000000, 0x00000000}, {0x80ff0000, 0x00000000}, {0x40000000, 0x00000000}, {0x10100, 0x1901E1C8}, {0x10101, 0x0061482D}, {0x10102, 0x00006750}, {0x903300ff, 0x00000000}, {0x40000000, 0x00000000}, - {0x10100, 0x04E8C5AD}, - {0x10101, 0x00594125}, - {0x10102, 0x00006850}, + {0x10100, 0x0BF0CEB8}, + {0x10101, 0x00424227}, + {0x10102, 0x00005050}, {0xA0000000, 0x00000000}, - {0x10100, 0x1901E1C8}, - {0x10101, 0x0061482D}, - {0x10102, 0x00006750}, + {0x10100, 0x1F0AECD5}, + {0x10101, 0x00634B31}, + {0x10102, 0x00006550}, {0xB0000000, 0x00000000}, {0x80ff0000, 0x00000000}, {0x40000000, 0x00000000}, {0x20000, 0x1601E2CA}, {0x20001, 0x005D452A}, {0x20002, 0x00006750}, {0x903300ff, 0x00000000}, {0x40000000, 0x00000000}, - {0x20000, 0x0EF4D3BB}, - {0x20001, 0x00563F25}, - {0x20002, 0x00006850}, + {0x20000, 0x0EF5D3BB}, + {0x20001, 0x00454529}, + {0x20002, 0x00005050}, {0xA0000000, 0x00000000}, - {0x20000, 0x1601E2CA}, - {0x20001, 0x005D452A}, - {0x20002, 0x00006750}, + {0x20000, 0x1904E6CE}, + {0x20001, 0x0060482D}, + {0x20002, 0x00006650}, {0xB0000000, 0x00000000}, {0x80ff0000, 0x00000000}, {0x40000000, 0x00000000}, {0x20100, 0x1901E1C8}, {0x20101, 0x0061482D}, {0x20102, 0x00006750}, {0x903300ff, 0x00000000}, {0x40000000, 0x00000000}, - {0x20100, 0x0BF1CFB7}, - {0x20101, 0x00574025}, - {0x20102, 0x00006750}, + {0x20100, 0x12F8D7C1}, + {0x20101, 0x004A4A2E}, + {0x20102, 0x00005050}, {0xA0000000, 0x00000000}, - {0x20100, 0x1901E1C8}, + {0x20100, 0x1F0AECD5}, {0x20101, 0x0061482D}, - {0x20102, 0x00006750}, + {0x20102, 0x00006550}, {0xB0000000, 0x00000000}, {0x80ff0000, 0x00000000}, {0x40000000, 0x00000000}, {0x30000, 0x1700E1CA}, {0x30001, 0x005E472B}, {0x30002, 0x00006750}, {0x903300ff, 0x00000000}, {0x40000000, 0x00000000}, - {0x30000, 0x05EFCEB7}, - {0x30001, 0x004B351A}, - {0x30002, 0x00006850}, + {0x30000, 0x0DF6D5BE}, + {0x30001, 0x00414126}, + {0x30002, 0x00005050}, {0xA0000000, 0x00000000}, - {0x30000, 0x1700E1CA}, - {0x30001, 0x005E472B}, - {0x30002, 0x00006750}, + {0x30000, 0x14FEE0CA}, + {0x30001, 0x005C4328}, + {0x30002, 0x00006650}, {0xB0000000, 0x00000000}, {0x80ff0000, 0x00000000}, {0x40000000, 0x00000000}, {0x30100, 0x14FEE0C9}, {0x30101, 0x00594428}, {0x30102, 0x00006650}, {0x903300ff, 0x00000000}, {0x40000000, 0x00000000}, - {0x30100, 0x0CF2D1B9}, - {0x30101, 0x00563F24}, - {0x30102, 0x00006750}, + {0x30100, 0x0EF5D5C0}, + {0x30101, 0x0045452A}, + {0x30102, 0x00005050}, {0xA0000000, 0x00000000}, - {0x30100, 0x14FEE0C9}, - {0x30101, 0x00594428}, + {0x30100, 0x1F0AECD8}, + {0x30101, 0x00654C31}, {0x30102, 0x00006650}, {0xB0000000, 0x00000000}, - {0x40000, 0x13FCDDC8}, - {0x40001, 0x005D4328}, - {0x40002, 0x00006850}, - {0x40100, 0x14FEE3CF}, - {0x40101, 0x00583E24}, - {0x40102, 0x00006850}, - {0x50000, 0x0DF4D6C6}, - {0x50001, 0x00604227}, - {0x50002, 0x00006850}, - {0x50100, 0x1903E7D5}, - {0x50101, 0x0061462B}, - {0x50102, 0x00006850}, - {0x60000, 0x0FF5D7C6}, - {0x60001, 0x005D4429}, - {0x60002, 0x00006850}, - {0x60100, 0x12FADECF}, - {0x60101, 0x005B4126}, - {0x60102, 0x00006850}, - {0x70000, 0x09F1D2C3}, - {0x70001, 0x00554026}, - {0x70002, 0x00006750}, - {0x70100, 0x0CF5DACC}, - {0x70101, 0x00563E25}, - {0x70102, 0x00006750}, + {0x40000, 0x15FEE0CB}, + {0x40001, 0x0060462B}, + {0x40002, 0x00006450}, + {0x40100, 0x1902E5D2}, + {0x40101, 0x0063482E}, + {0x40102, 0x00006450}, + {0x50000, 0x1C04E6D3}, + {0x50001, 0x006B5034}, + {0x50002, 0x00006450}, + {0x50100, 0x2009EDDB}, + {0x50101, 0x006B5035}, + {0x50102, 0x00006450}, + {0x60000, 0x16FEE1CF}, + {0x60001, 0x00634A2E}, + {0x60002, 0x00006550}, + {0x60100, 0x14FDE2D2}, + {0x60101, 0x005E4429}, + {0x60102, 0x00006450}, + {0x70000, 0x0BF3D6C6}, + {0x70001, 0x00573F24}, + {0x70002, 0x00006550}, + {0x70100, 0x08F0D6C7}, + {0x70101, 0x0052391E}, + {0x70102, 0x00006450}, {0x2000000, 0x02E4C4A0}, {0x2000001, 0x006A4828}, {0x2000100, 0x02E4C5A1}, {0x2000101, 0x00664629}, + {0x80ff0000, 0x00000000}, {0x40000000, 0x00000000}, {0x2010000, 0x05EBC8AF}, {0x2010001, 0x00543D24}, + {0x903300ff, 0x00000000}, {0x40000000, 0x00000000}, + {0x2010000, 0x08EDCAB2}, + {0x2010001, 0x00434327}, + {0xA0000000, 0x00000000}, + {0x2010000, 0x05EBC8AF}, + {0x2010001, 0x00543D24}, + {0xB0000000, 0x00000000}, + {0x80ff0000, 0x00000000}, {0x40000000, 0x00000000}, {0x2010100, 0x07ECC9B0}, {0x2010101, 0x005B4126}, + {0x903300ff, 0x00000000}, {0x40000000, 0x00000000}, + {0x2010100, 0x08ECCBB2}, + {0x2010101, 0x003C3C20}, + {0xA0000000, 0x00000000}, + {0x2010100, 0x07ECC9B0}, + {0x2010101, 0x005B4126}, + {0xB0000000, 0x00000000}, + {0x80ff0000, 0x00000000}, {0x40000000, 0x00000000}, {0x2020000, 0x05EDCCB2}, {0x2020001, 0x004D361C}, + {0x903300ff, 0x00000000}, {0x40000000, 0x00000000}, + {0x2020000, 0x0CF4D2BA}, + {0x2020001, 0x00404025}, + {0xA0000000, 0x00000000}, + {0x2020000, 0x05EDCCB2}, + {0x2020001, 0x004D361C}, + {0xB0000000, 0x00000000}, + {0x80ff0000, 0x00000000}, {0x40000000, 0x00000000}, {0x2020100, 0x06ECCBB2}, {0x2020101, 0x00553D22}, + {0x903300ff, 0x00000000}, {0x40000000, 0x00000000}, + {0x2020100, 0x09EECDB8}, + {0x2020101, 0x00444428}, + {0xA0000000, 0x00000000}, + {0x2020100, 0x06ECCBB2}, + {0x2020101, 0x00553D22}, + {0xB0000000, 0x00000000}, + {0x80ff0000, 0x00000000}, {0x40000000, 0x00000000}, {0x2030000, 0x02ECCCB3}, {0x2030001, 0x00483118}, + {0x903300ff, 0x00000000}, {0x40000000, 0x00000000}, + {0x2030000, 0x0DF8D6BF}, + {0x2030001, 0x003F3F24}, + {0xA0000000, 0x00000000}, + {0x2030000, 0x02ECCCB3}, + {0x2030001, 0x00483118}, + {0xB0000000, 0x00000000}, + {0x80ff0000, 0x00000000}, {0x40000000, 0x00000000}, {0x2030100, 0x04ECCCB2}, {0x2030101, 0x004F381C}, + {0x903300ff, 0x00000000}, {0x40000000, 0x00000000}, + {0x2030100, 0x08EFCDBA}, + {0x2030101, 0x00414126}, + {0xA0000000, 0x00000000}, + {0x2030100, 0x04ECCCB2}, + {0x2030101, 0x004F381C}, + {0xB0000000, 0x00000000}, {0x3000000, 0x00000000}, {0x3000001, 0x00000000}, {0x3000002, 0x00000000}, @@ -1709,30 +2103,102 @@ static const struct rtw89_reg2_def rtw89_8852c_phy_bb_reg_gain[] = { {0x3000101, 0x00000000}, {0x3000102, 0x00000000}, {0x3000103, 0x00000000}, + {0x80ff0000, 0x00000000}, {0x40000000, 0x00000000}, {0x3010000, 0x0E0CFB0A}, {0x3010001, 0x00100F06}, {0x3010002, 0x34333333}, {0x3010003, 0x3434343C}, + {0x903300ff, 0x00000000}, {0x40000000, 0x00000000}, + {0x3010000, 0x0E0CFB0A}, + {0x3010001, 0x00100F06}, + {0x3010002, 0x34333327}, + {0x3010003, 0x3434343C}, + {0xA0000000, 0x00000000}, + {0x3010000, 0x0E0CFB0A}, + {0x3010001, 0x00100F06}, + {0x3010002, 0x34333333}, + {0x3010003, 0x3434343C}, + {0xB0000000, 0x00000000}, + {0x80ff0000, 0x00000000}, {0x40000000, 0x00000000}, {0x3010100, 0x0E0CFB0A}, {0x3010101, 0x00100F06}, {0x3010102, 0x34333333}, {0x3010103, 0x3434343C}, + {0x903300ff, 0x00000000}, {0x40000000, 0x00000000}, + {0x3010100, 0x0E0CFB0A}, + {0x3010101, 0x00100F06}, + {0x3010102, 0x34333327}, + {0x3010103, 0x3434343C}, + {0xA0000000, 0x00000000}, + {0x3010100, 0x0E0CFB0A}, + {0x3010101, 0x00100F06}, + {0x3010102, 0x34333333}, + {0x3010103, 0x3434343C}, + {0xB0000000, 0x00000000}, + {0x80ff0000, 0x00000000}, {0x40000000, 0x00000000}, {0x3020000, 0x0E0CFB0A}, {0x3020001, 0x00100F06}, {0x3020002, 0x34333333}, {0x3020003, 0x3434343C}, + {0x903300ff, 0x00000000}, {0x40000000, 0x00000000}, + {0x3020000, 0x0E0CFB0A}, + {0x3020001, 0x00100F06}, + {0x3020002, 0x34333327}, + {0x3020003, 0x3434343C}, + {0xA0000000, 0x00000000}, + {0x3020000, 0x0E0CFB0A}, + {0x3020001, 0x00100F06}, + {0x3020002, 0x34333333}, + {0x3020003, 0x3434343C}, + {0xB0000000, 0x00000000}, + {0x80ff0000, 0x00000000}, {0x40000000, 0x00000000}, {0x3020100, 0x0E0CFB0A}, {0x3020101, 0x00100F06}, {0x3020102, 0x34333333}, {0x3020103, 0x3434343C}, + {0x903300ff, 0x00000000}, {0x40000000, 0x00000000}, + {0x3020100, 0x0E0CFB0A}, + {0x3020101, 0x00100F06}, + {0x3020102, 0x34333327}, + {0x3020103, 0x3434343C}, + {0xA0000000, 0x00000000}, + {0x3020100, 0x0E0CFB0A}, + {0x3020101, 0x00100F06}, + {0x3020102, 0x34333333}, + {0x3020103, 0x3434343C}, + {0xB0000000, 0x00000000}, + {0x80ff0000, 0x00000000}, {0x40000000, 0x00000000}, {0x3030000, 0x0E0CFB0A}, {0x3030001, 0x00100F06}, {0x3030002, 0x34333333}, {0x3030003, 0x3434343C}, + {0x903300ff, 0x00000000}, {0x40000000, 0x00000000}, + {0x3030000, 0x0E0CFB0A}, + {0x3030001, 0x00100F06}, + {0x3030002, 0x34333327}, + {0x3030003, 0x3434343C}, + {0xA0000000, 0x00000000}, + {0x3030000, 0x0E0CFB0A}, + {0x3030001, 0x00100F06}, + {0x3030002, 0x34333333}, + {0x3030003, 0x3434343C}, + {0xB0000000, 0x00000000}, + {0x80ff0000, 0x00000000}, {0x40000000, 0x00000000}, {0x3030100, 0x0E0CFB0A}, {0x3030101, 0x00100F06}, {0x3030102, 0x34333333}, {0x3030103, 0x3434343C}, + {0x903300ff, 0x00000000}, {0x40000000, 0x00000000}, + {0x3030100, 0x0E0CFB0A}, + {0x3030101, 0x00100F06}, + {0x3030102, 0x34333327}, + {0x3030103, 0x3434343C}, + {0xA0000000, 0x00000000}, + {0x3030100, 0x0E0CFB0A}, + {0x3030101, 0x00100F06}, + {0x3030102, 0x34333333}, + {0x3030103, 0x3434343C}, + {0xB0000000, 0x00000000}, {0x3040000, 0x0E0CFB0A}, {0x3040001, 0x00100F06}, {0x3040002, 0x343B3333}, @@ -1765,6 +2231,310 @@ static const struct rtw89_reg2_def rtw89_8852c_phy_bb_reg_gain[] = { {0x3070101, 0x00100F06}, {0x3070102, 0x3C3B3333}, {0x3070103, 0x34343C3C}, + {0x4000000, 0x00000000}, + {0x4000001, 0x76543210}, + {0x4000002, 0x77777777}, + {0x4000003, 0x35374425}, + {0x4000004, 0x00000043}, + {0x4000005, 0x000038E8}, + {0x4000100, 0x00000000}, + {0x4000101, 0x76543210}, + {0x4000102, 0x77777777}, + {0x4000103, 0x35374425}, + {0x4000104, 0x00000043}, + {0x4000105, 0x000038E8}, + {0x80ff0000, 0x00000000}, {0x40000000, 0x00000000}, + {0x4010000, 0x00000000}, + {0x4010001, 0x76543210}, + {0x4010002, 0x77777777}, + {0x4010003, 0x35374425}, + {0x4010004, 0x00000042}, + {0x4010005, 0x000038E8}, + {0x903300ff, 0x00000000}, {0x40000000, 0x00000000}, + {0x4010000, 0x0000FC50}, + {0x4010001, 0x51403210}, + {0x4010002, 0x76543276}, + {0x4010003, 0x3A4DAA3C}, + {0x4010004, 0x00000093}, + {0x4010005, 0x000040E4}, + {0xA0000000, 0x00000000}, + {0x4010000, 0x00000000}, + {0x4010001, 0x76543210}, + {0x4010002, 0x77777777}, + {0x4010003, 0x35374425}, + {0x4010004, 0x00000042}, + {0x4010005, 0x000038E8}, + {0xB0000000, 0x00000000}, + {0x80ff0000, 0x00000000}, {0x40000000, 0x00000000}, + {0x4010100, 0x00000000}, + {0x4010101, 0x76543210}, + {0x4010102, 0x77777777}, + {0x4010103, 0x35374425}, + {0x4010104, 0x00000042}, + {0x4010105, 0x000038E8}, + {0x903300ff, 0x00000000}, {0x40000000, 0x00000000}, + {0x4010100, 0x0000FC50}, + {0x4010101, 0x51403210}, + {0x4010102, 0x76543276}, + {0x4010103, 0x3A4DAA3C}, + {0x4010104, 0x00000093}, + {0x4010105, 0x000040E4}, + {0xA0000000, 0x00000000}, + {0x4010100, 0x00000000}, + {0x4010101, 0x76543210}, + {0x4010102, 0x77777777}, + {0x4010103, 0x35374425}, + {0x4010104, 0x00000042}, + {0x4010105, 0x000038E8}, + {0xB0000000, 0x00000000}, + {0x80ff0000, 0x00000000}, {0x40000000, 0x00000000}, + {0x4020000, 0x00000000}, + {0x4020001, 0x76543210}, + {0x4020002, 0x77777777}, + {0x4020003, 0x35374425}, + {0x4020004, 0x00000042}, + {0x4020005, 0x000038E8}, + {0x903300ff, 0x00000000}, {0x40000000, 0x00000000}, + {0x4020000, 0x0000FC50}, + {0x4020001, 0x51403210}, + {0x4020002, 0x76543276}, + {0x4020003, 0x4B4DAA3C}, + {0x4020004, 0x000000A3}, + {0x4020005, 0x000040E4}, + {0xA0000000, 0x00000000}, + {0x4020000, 0x00000000}, + {0x4020001, 0x76543210}, + {0x4020002, 0x77777777}, + {0x4020003, 0x35374425}, + {0x4020004, 0x00000042}, + {0x4020005, 0x000038E8}, + {0xB0000000, 0x00000000}, + {0x80ff0000, 0x00000000}, {0x40000000, 0x00000000}, + {0x4020100, 0x00000000}, + {0x4020101, 0x76543210}, + {0x4020102, 0x77777777}, + {0x4020103, 0x35374425}, + {0x4020104, 0x00000042}, + {0x4020105, 0x000038E8}, + {0x903300ff, 0x00000000}, {0x40000000, 0x00000000}, + {0x4020100, 0x0000FC50}, + {0x4020101, 0x51403210}, + {0x4020102, 0x76543276}, + {0x4020103, 0x3A4DAA3C}, + {0x4020104, 0x00000093}, + {0x4020105, 0x000040E4}, + {0xA0000000, 0x00000000}, + {0x4020100, 0x00000000}, + {0x4020101, 0x76543210}, + {0x4020102, 0x77777777}, + {0x4020103, 0x35374425}, + {0x4020104, 0x00000042}, + {0x4020105, 0x000038E8}, + {0xB0000000, 0x00000000}, + {0x80ff0000, 0x00000000}, {0x40000000, 0x00000000}, + {0x4030000, 0x00000000}, + {0x4030001, 0x76543210}, + {0x4030002, 0x77777777}, + {0x4030003, 0x35374425}, + {0x4030004, 0x00000042}, + {0x4030005, 0x000038E8}, + {0x903300ff, 0x00000000}, {0x40000000, 0x00000000}, + {0x4030000, 0x0000FC50}, + {0x4030001, 0x51403210}, + {0x4030002, 0x76543276}, + {0x4030003, 0x3A4DAA3C}, + {0x4030004, 0x00000093}, + {0x4030005, 0x000040E4}, + {0xA0000000, 0x00000000}, + {0x4030000, 0x00000000}, + {0x4030001, 0x76543210}, + {0x4030002, 0x77777777}, + {0x4030003, 0x35374425}, + {0x4030004, 0x00000042}, + {0x4030005, 0x000038E8}, + {0xB0000000, 0x00000000}, + {0x80ff0000, 0x00000000}, {0x40000000, 0x00000000}, + {0x4030100, 0x00000000}, + {0x4030101, 0x76543210}, + {0x4030102, 0x77777777}, + {0x4030103, 0x35374425}, + {0x4030104, 0x00000042}, + {0x4030105, 0x000038E8}, + {0x903300ff, 0x00000000}, {0x40000000, 0x00000000}, + {0x4030100, 0x0000FC50}, + {0x4030101, 0x51403210}, + {0x4030102, 0x76543276}, + {0x4030103, 0x3A4DAA3C}, + {0x4030104, 0x00000093}, + {0x4030105, 0x000040E4}, + {0xA0000000, 0x00000000}, + {0x4030100, 0x00000000}, + {0x4030101, 0x76543210}, + {0x4030102, 0x77777777}, + {0x4030103, 0x35374425}, + {0x4030104, 0x00000042}, + {0x4030105, 0x000038E8}, + {0xB0000000, 0x00000000}, + {0x1000000, 0x00000008}, + {0x1000010, 0x00000008}, + {0x1000011, 0x00000000}, + {0x1000100, 0x00000004}, + {0x1000110, 0x00000004}, + {0x1000111, 0x00000000}, + {0x1010000, 0x00000004}, + {0x1010010, 0x00000004}, + {0x1010011, 0x00000000}, + {0x1010020, 0x00000004}, + {0x1010021, 0x00000000}, + {0x1010029, 0x00000000}, + {0x1010030, 0x00000000}, + {0x1010031, 0x00000000}, + {0x1010035, 0x00000000}, + {0x1010039, 0x00000000}, + {0x101003D, 0x00000000}, + {0x1010100, 0x00000010}, + {0x1010110, 0x00000010}, + {0x1010111, 0x00000000}, + {0x1010120, 0x00000010}, + {0x1010121, 0x00000000}, + {0x1010129, 0x00000000}, + {0x1010030, 0x00000000}, + {0x1010031, 0x00000000}, + {0x1010035, 0x00000000}, + {0x1010039, 0x00000000}, + {0x101003D, 0x00000000}, + {0x1020000, 0x000000FA}, + {0x1020010, 0x000000FA}, + {0x1020011, 0x00000000}, + {0x1020020, 0x000000FA}, + {0x1020021, 0x00000000}, + {0x1020029, 0x00000000}, + {0x1020030, 0x00000000}, + {0x1020031, 0x00000000}, + {0x1020035, 0x00000000}, + {0x1020039, 0x00000000}, + {0x102003D, 0x00000000}, + {0x1020100, 0x0000000D}, + {0x1020110, 0x0000000D}, + {0x1020111, 0x00000000}, + {0x1020120, 0x0000000D}, + {0x1020121, 0x00000000}, + {0x1020129, 0x00000000}, + {0x1020030, 0x00000000}, + {0x1020031, 0x00000000}, + {0x1020035, 0x00000000}, + {0x1020039, 0x00000000}, + {0x102003D, 0x00000000}, + {0x1030000, 0x000000E4}, + {0x1030010, 0x000000E4}, + {0x1030011, 0x00000000}, + {0x1030020, 0x0000E8E8}, + {0x1030021, 0x00000000}, + {0x1030029, 0x00000000}, + {0x1030030, 0x00000000}, + {0x1030031, 0x00000000}, + {0x1030035, 0x00000000}, + {0x1030039, 0x00000000}, + {0x103003D, 0x00000000}, + {0x1030100, 0x00000018}, + {0x1030110, 0x00000018}, + {0x1030111, 0x00000000}, + {0x1030120, 0x00000018}, + {0x1030121, 0x00000000}, + {0x1030129, 0x00000000}, + {0x1030030, 0x00000000}, + {0x1030031, 0x00000000}, + {0x1030035, 0x00000000}, + {0x1030039, 0x00000000}, + {0x103003D, 0x00000000}, + {0x1040000, 0x000000EE}, + {0x1040010, 0x000000EE}, + {0x1040011, 0x00000000}, + {0x1040020, 0x000000EE}, + {0x1040021, 0x00000000}, + {0x1040029, 0x00000000}, + {0x1040030, 0x000000EE}, + {0x1040031, 0x00000000}, + {0x1040035, 0x00000000}, + {0x1040039, 0x00000000}, + {0x104003D, 0x00000000}, + {0x1040100, 0x00000000}, + {0x1040110, 0x00000005}, + {0x1040111, 0x00000000}, + {0x1040120, 0x00000008}, + {0x1040121, 0x00000000}, + {0x1040129, 0x00000000}, + {0x1040030, 0x00000008}, + {0x1040031, 0x00000000}, + {0x1040035, 0x00000000}, + {0x1040039, 0x00000000}, + {0x104003D, 0x00000000}, + {0x1050000, 0x00000008}, + {0x1050010, 0x0000000B}, + {0x1050011, 0x00000000}, + {0x1050020, 0x00000015}, + {0x1050021, 0x00000000}, + {0x1050029, 0x00000000}, + {0x1050030, 0x00000010}, + {0x1050031, 0x00000000}, + {0x1050035, 0x00000000}, + {0x1050039, 0x00000000}, + {0x105003D, 0x00000000}, + {0x1050100, 0x00000016}, + {0x1050110, 0x00000016}, + {0x1050111, 0x0000F8F8}, + {0x1050120, 0x0000001A}, + {0x1050121, 0x00000000}, + {0x1050129, 0x00000000}, + {0x1050030, 0x0000001A}, + {0x1050031, 0x00000000}, + {0x1050035, 0x00000000}, + {0x1050039, 0x00000000}, + {0x105003D, 0x00000000}, + {0x1060000, 0x000000F8}, + {0x1060010, 0x000000F8}, + {0x1060011, 0x00000000}, + {0x1060020, 0x00000000}, + {0x1060021, 0x00000000}, + {0x1060029, 0x00000000}, + {0x1060030, 0x00000000}, + {0x1060031, 0x00000000}, + {0x1060035, 0x00000000}, + {0x1060039, 0x00000000}, + {0x106003D, 0x00000000}, + {0x1060100, 0x000000F6}, + {0x1060110, 0x000000F6}, + {0x1060111, 0x00000000}, + {0x1060120, 0x000000F6}, + {0x1060121, 0x00000000}, + {0x1060129, 0x00000000}, + {0x1060030, 0x00000000}, + {0x1060031, 0x00000000}, + {0x1060035, 0x00000000}, + {0x1060039, 0x00000000}, + {0x106003D, 0x00000000}, + {0x1070000, 0x000000E8}, + {0x1070010, 0x000000E8}, + {0x1070011, 0x00000000}, + {0x1070020, 0x000000E8}, + {0x1070021, 0x00000000}, + {0x1070029, 0x00000000}, + {0x1070030, 0x000000F0}, + {0x1070031, 0x00000000}, + {0x1070035, 0x00000000}, + {0x1070039, 0x00000000}, + {0x107003D, 0x00000000}, + {0x1070100, 0x000000E4}, + {0x1070110, 0x000000E4}, + {0x1070111, 0x00000000}, + {0x1070120, 0x000000E4}, + {0x1070121, 0x00000000}, + {0x1070129, 0x00000000}, + {0x1070030, 0x000000F0}, + {0x1070031, 0x00000000}, + {0x1070035, 0x00000000}, + {0x1070039, 0x00000000}, + {0x107003D, 0x00000000}, }; static const struct rtw89_reg2_def rtw89_8852c_phy_radioa_regs[] = { From c6a9d360874a41dc972c44c0949916da55199f85 Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Fri, 30 Sep 2022 21:36:59 +0800 Subject: [PATCH 0046/2002] wifi: rtw89: phy: ignore warning of bb gain cfg_type 4 The new BB parameters add new cfg_tpe 4 to improve performance of eFEM modules (rfe_type >= 50), but we are using iFEM modules for now, so this warning can be ignored. Signed-off-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20220930133659.7789-2-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/phy.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/phy.c b/drivers/net/wireless/realtek/rtw89/phy.c index 12a297f6bf50..d9c4debecc62 100644 --- a/drivers/net/wireless/realtek/rtw89/phy.c +++ b/drivers/net/wireless/realtek/rtw89/phy.c @@ -1036,6 +1036,7 @@ static void rtw89_phy_config_bb_gain(struct rtw89_dev *rtwdev, { const struct rtw89_chip_info *chip = rtwdev->chip; union rtw89_phy_bb_gain_arg arg = { .addr = reg->addr }; + struct rtw89_efuse *efuse = &rtwdev->efuse; if (arg.gain_band >= RTW89_BB_GAIN_BAND_NR) return; @@ -1061,6 +1062,11 @@ static void rtw89_phy_config_bb_gain(struct rtw89_dev *rtwdev, case 3: rtw89_phy_cfg_bb_gain_op1db(rtwdev, arg, reg->data); break; + case 4: + /* This cfg_type is only used by rfe_type >= 50 with eFEM */ + if (efuse->rfe_type < 50) + break; + fallthrough; default: rtw89_warn(rtwdev, "bb gain {0x%x:0x%x} with unknown cfg type: %d\n", From d187691ab63f53f199a07904422e0911bc6f9390 Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Fri, 30 Sep 2022 21:44:16 +0800 Subject: [PATCH 0047/2002] wifi: rtw89: 8852c: set pin MUX to enable BT firmware log 8852CE is a combo chip, and WiFi driver controls pin MUX. To output BT firmware log to specific hardware pin, set pin MUX to achieve. Signed-off-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20220930134417.10282-1-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/reg.h | 4 ++++ drivers/net/wireless/realtek/rtw89/rtw8852c.c | 3 +++ 2 files changed, 7 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/reg.h b/drivers/net/wireless/realtek/rtw89/reg.h index 6809ff812abb..874cca85eaad 100644 --- a/drivers/net/wireless/realtek/rtw89/reg.h +++ b/drivers/net/wireless/realtek/rtw89/reg.h @@ -229,6 +229,10 @@ #define R_AX_GPIO0_7_FUNC_SEL 0x02D0 +#define R_AX_LED1_FUNC_SEL 0x02DC +#define B_AX_PINMUX_EESK_FUNC_SEL_V1_MASK GENMASK(27, 24) +#define PINMUX_EESK_FUNC_SEL_BT_LOG 0x1 + #define R_AX_GPIO0_15_EECS_EESK_LED1_PULL_LOW_EN 0x02E4 #define B_AX_LED1_PULL_LOW_EN BIT(18) #define B_AX_EESK_PULL_LOW_EN BIT(17) diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852c.c b/drivers/net/wireless/realtek/rtw89/rtw8852c.c index a6a9fe3d0b56..a5bcd3fcecb7 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852c.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852c.c @@ -273,6 +273,9 @@ static int rtw8852c_pwr_on_func(struct rtw89_dev *rtwdev) B_AX_CMAC_DMA_EN | B_AX_PTCLTOP_EN | B_AX_SCHEDULER_EN | B_AX_TMAC_EN | B_AX_RMAC_EN); + rtw89_write32_mask(rtwdev, R_AX_LED1_FUNC_SEL, B_AX_PINMUX_EESK_FUNC_SEL_V1_MASK, + PINMUX_EESK_FUNC_SEL_BT_LOG); + return 0; } From 732dd91db3d3a1b7a767598549ffed358c9fbb89 Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Fri, 30 Sep 2022 21:44:17 +0800 Subject: [PATCH 0048/2002] wifi: rtw89: add to dump TX FIFO 0/1 for 8852C MAC maintains TX FIFO to transmit packets with meta data to BB layer. To debug abnormal transmission, we need to dump the content to dig problem. Since FIFO of 8852C locates on different address with different size and need additional switch to enable read operation, this patch adds the changes accordingly. Signed-off-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20220930134417.10282-2-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/debug.c | 21 +++++++++++++++++++++ drivers/net/wireless/realtek/rtw89/mac.c | 2 ++ drivers/net/wireless/realtek/rtw89/mac.h | 4 ++++ 3 files changed, 27 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/debug.c b/drivers/net/wireless/realtek/rtw89/debug.c index f584fa57c82f..8f27c883eeab 100644 --- a/drivers/net/wireless/realtek/rtw89/debug.c +++ b/drivers/net/wireless/realtek/rtw89/debug.c @@ -774,13 +774,34 @@ rtw89_debug_priv_mac_mem_dump_get(struct seq_file *m, void *v) { struct rtw89_debugfs_priv *debugfs_priv = m->private; struct rtw89_dev *rtwdev = debugfs_priv->rtwdev; + bool grant_read = false; + + if (debugfs_priv->mac_mem.sel >= RTW89_MAC_MEM_NUM) + return -ENOENT; + + if (rtwdev->chip->chip_id == RTL8852C) { + switch (debugfs_priv->mac_mem.sel) { + case RTW89_MAC_MEM_TXD_FIFO_0_V1: + case RTW89_MAC_MEM_TXD_FIFO_1_V1: + case RTW89_MAC_MEM_TXDATA_FIFO_0: + case RTW89_MAC_MEM_TXDATA_FIFO_1: + grant_read = true; + break; + default: + break; + } + } mutex_lock(&rtwdev->mutex); rtw89_leave_ps_mode(rtwdev); + if (grant_read) + rtw89_write32_set(rtwdev, R_AX_TCR1, B_AX_TCR_FORCE_READ_TXDFIFO); rtw89_debug_dump_mac_mem(m, rtwdev, debugfs_priv->mac_mem.sel, debugfs_priv->mac_mem.start, debugfs_priv->mac_mem.len); + if (grant_read) + rtw89_write32_clr(rtwdev, R_AX_TCR1, B_AX_TCR_FORCE_READ_TXDFIFO); mutex_unlock(&rtwdev->mutex); return 0; diff --git a/drivers/net/wireless/realtek/rtw89/mac.c b/drivers/net/wireless/realtek/rtw89/mac.c index 878c33fa30bd..3531a859d6c8 100644 --- a/drivers/net/wireless/realtek/rtw89/mac.c +++ b/drivers/net/wireless/realtek/rtw89/mac.c @@ -31,6 +31,8 @@ const u32 rtw89_mac_mem_base_addrs[RTW89_MAC_MEM_NUM] = { [RTW89_MAC_MEM_TXDATA_FIFO_1] = TXDATA_FIFO_1_BASE_ADDR, [RTW89_MAC_MEM_CPU_LOCAL] = CPU_LOCAL_BASE_ADDR, [RTW89_MAC_MEM_BSSID_CAM] = BSSID_CAM_BASE_ADDR, + [RTW89_MAC_MEM_TXD_FIFO_0_V1] = TXD_FIFO_0_BASE_ADDR_V1, + [RTW89_MAC_MEM_TXD_FIFO_1_V1] = TXD_FIFO_1_BASE_ADDR_V1, }; static void rtw89_mac_mem_write(struct rtw89_dev *rtwdev, u32 offset, diff --git a/drivers/net/wireless/realtek/rtw89/mac.h b/drivers/net/wireless/realtek/rtw89/mac.h index 6f4ada1869a1..a9867ac351da 100644 --- a/drivers/net/wireless/realtek/rtw89/mac.h +++ b/drivers/net/wireless/realtek/rtw89/mac.h @@ -245,6 +245,8 @@ enum rtw89_mac_dbg_port_sel { #define BCN_IE_CAM1_BASE_ADDR 0x188A0000 #define TXD_FIFO_0_BASE_ADDR 0x18856200 #define TXD_FIFO_1_BASE_ADDR 0x188A1080 +#define TXD_FIFO_0_BASE_ADDR_V1 0x18856400 /* for 8852C */ +#define TXD_FIFO_1_BASE_ADDR_V1 0x188A1080 /* for 8852C */ #define TXDATA_FIFO_0_BASE_ADDR 0x18856000 #define TXDATA_FIFO_1_BASE_ADDR 0x188A1000 #define CPU_LOCAL_BASE_ADDR 0x18003000 @@ -271,6 +273,8 @@ enum rtw89_mac_mem_sel { RTW89_MAC_MEM_TXDATA_FIFO_1, RTW89_MAC_MEM_CPU_LOCAL, RTW89_MAC_MEM_BSSID_CAM, + RTW89_MAC_MEM_TXD_FIFO_0_V1, + RTW89_MAC_MEM_TXD_FIFO_1_V1, /* keep last */ RTW89_MAC_MEM_NUM, From 8af9d4068e86dd5c3221ee495ec09537c7fb458b Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Sat, 1 Oct 2022 20:24:28 -0700 Subject: [PATCH 0049/2002] wifi: atmel: Avoid clashing function prototypes When built with Control Flow Integrity, function prototypes between caller and function declaration must match. These mismatches are visible at compile time with the new -Wcast-function-type-strict in Clang[1]. Of the 1549 warnings found, 188 come from the atmel driver. For example: drivers/net/wireless/atmel/atmel.c:2518:2: warning: cast from 'int (*)(struct net_device *, struct iw_request_info *, void *, char *)' to 'iw_handler' (aka 'int (*)(struct net_device *, struct iw_request_info *, union iwreq_data *, char *)') converts to incompatible function type [-Wcast-function-type-strict] (iw_handler) atmel_config_commit, /* SIOCSIWCOMMIT */ ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The atmel Wireless Extension handler callbacks (iw_handler) use a union for the data argument. Actually use the union and perform explicit member selection in the function body instead of having a function prototype mismatch. There are no resulting binary differences. This patch is a cleanup based on Brad Spengler/PaX Team's modifications to the atmel driver in their last public patch of grsecurity/PaX based on my understanding of the code. Changes or omissions from the original code are mine and don't reflect the original grsecurity/PaX code. [1] https://reviews.llvm.org/D134831 Cc: Simon Kelley Cc: Kalle Valo Cc: "David S. Miller" Cc: Eric Dumazet Cc: Jakub Kicinski Cc: Paolo Abeni Cc: linux-wireless@vger.kernel.org Cc: netdev@vger.kernel.org Signed-off-by: Kees Cook Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20221002032428.4091540-1-keescook@chromium.org --- drivers/net/wireless/atmel/atmel.c | 164 ++++++++++++++--------------- 1 file changed, 80 insertions(+), 84 deletions(-) diff --git a/drivers/net/wireless/atmel/atmel.c b/drivers/net/wireless/atmel/atmel.c index 45d079b93384..ed430b91e2aa 100644 --- a/drivers/net/wireless/atmel/atmel.c +++ b/drivers/net/wireless/atmel/atmel.c @@ -1643,9 +1643,10 @@ EXPORT_SYMBOL(stop_atmel_card); static int atmel_set_essid(struct net_device *dev, struct iw_request_info *info, - struct iw_point *dwrq, + union iwreq_data *wrqu, char *extra) { + struct iw_point *dwrq = &wrqu->essid; struct atmel_private *priv = netdev_priv(dev); /* Check if we asked for `any' */ @@ -1671,9 +1672,10 @@ static int atmel_set_essid(struct net_device *dev, static int atmel_get_essid(struct net_device *dev, struct iw_request_info *info, - struct iw_point *dwrq, + union iwreq_data *wrqu, char *extra) { + struct iw_point *dwrq = &wrqu->essid; struct atmel_private *priv = netdev_priv(dev); /* Get the current SSID */ @@ -1692,9 +1694,10 @@ static int atmel_get_essid(struct net_device *dev, static int atmel_get_wap(struct net_device *dev, struct iw_request_info *info, - struct sockaddr *awrq, + union iwreq_data *wrqu, char *extra) { + struct sockaddr *awrq = &wrqu->ap_addr; struct atmel_private *priv = netdev_priv(dev); memcpy(awrq->sa_data, priv->CurrentBSSID, ETH_ALEN); awrq->sa_family = ARPHRD_ETHER; @@ -1704,9 +1707,10 @@ static int atmel_get_wap(struct net_device *dev, static int atmel_set_encode(struct net_device *dev, struct iw_request_info *info, - struct iw_point *dwrq, + union iwreq_data *wrqu, char *extra) { + struct iw_point *dwrq = &wrqu->encoding; struct atmel_private *priv = netdev_priv(dev); /* Basic checking: do we have a key to set ? @@ -1793,9 +1797,10 @@ static int atmel_set_encode(struct net_device *dev, static int atmel_get_encode(struct net_device *dev, struct iw_request_info *info, - struct iw_point *dwrq, + union iwreq_data *wrqu, char *extra) { + struct iw_point *dwrq = &wrqu->encoding; struct atmel_private *priv = netdev_priv(dev); int index = (dwrq->flags & IW_ENCODE_INDEX) - 1; @@ -2003,18 +2008,19 @@ static int atmel_get_auth(struct net_device *dev, static int atmel_get_name(struct net_device *dev, struct iw_request_info *info, - char *cwrq, + union iwreq_data *wrqu, char *extra) { - strcpy(cwrq, "IEEE 802.11-DS"); + strcpy(wrqu->name, "IEEE 802.11-DS"); return 0; } static int atmel_set_rate(struct net_device *dev, struct iw_request_info *info, - struct iw_param *vwrq, + union iwreq_data *wrqu, char *extra) { + struct iw_param *vwrq = &wrqu->bitrate; struct atmel_private *priv = netdev_priv(dev); if (vwrq->fixed == 0) { @@ -2053,9 +2059,10 @@ static int atmel_set_rate(struct net_device *dev, static int atmel_set_mode(struct net_device *dev, struct iw_request_info *info, - __u32 *uwrq, + union iwreq_data *wrqu, char *extra) { + __u32 *uwrq = &wrqu->mode; struct atmel_private *priv = netdev_priv(dev); if (*uwrq != IW_MODE_ADHOC && *uwrq != IW_MODE_INFRA) @@ -2067,9 +2074,10 @@ static int atmel_set_mode(struct net_device *dev, static int atmel_get_mode(struct net_device *dev, struct iw_request_info *info, - __u32 *uwrq, + union iwreq_data *wrqu, char *extra) { + __u32 *uwrq = &wrqu->mode; struct atmel_private *priv = netdev_priv(dev); *uwrq = priv->operating_mode; @@ -2078,9 +2086,10 @@ static int atmel_get_mode(struct net_device *dev, static int atmel_get_rate(struct net_device *dev, struct iw_request_info *info, - struct iw_param *vwrq, + union iwreq_data *wrqu, char *extra) { + struct iw_param *vwrq = &wrqu->bitrate; struct atmel_private *priv = netdev_priv(dev); if (priv->auto_tx_rate) { @@ -2108,9 +2117,10 @@ static int atmel_get_rate(struct net_device *dev, static int atmel_set_power(struct net_device *dev, struct iw_request_info *info, - struct iw_param *vwrq, + union iwreq_data *wrqu, char *extra) { + struct iw_param *vwrq = &wrqu->power; struct atmel_private *priv = netdev_priv(dev); priv->power_mode = vwrq->disabled ? 0 : 1; return -EINPROGRESS; @@ -2118,9 +2128,10 @@ static int atmel_set_power(struct net_device *dev, static int atmel_get_power(struct net_device *dev, struct iw_request_info *info, - struct iw_param *vwrq, + union iwreq_data *wrqu, char *extra) { + struct iw_param *vwrq = &wrqu->power; struct atmel_private *priv = netdev_priv(dev); vwrq->disabled = priv->power_mode ? 0 : 1; vwrq->flags = IW_POWER_ON; @@ -2129,9 +2140,10 @@ static int atmel_get_power(struct net_device *dev, static int atmel_set_retry(struct net_device *dev, struct iw_request_info *info, - struct iw_param *vwrq, + union iwreq_data *wrqu, char *extra) { + struct iw_param *vwrq = &wrqu->retry; struct atmel_private *priv = netdev_priv(dev); if (!vwrq->disabled && (vwrq->flags & IW_RETRY_LIMIT)) { @@ -2152,9 +2164,10 @@ static int atmel_set_retry(struct net_device *dev, static int atmel_get_retry(struct net_device *dev, struct iw_request_info *info, - struct iw_param *vwrq, + union iwreq_data *wrqu, char *extra) { + struct iw_param *vwrq = &wrqu->retry; struct atmel_private *priv = netdev_priv(dev); vwrq->disabled = 0; /* Can't be disabled */ @@ -2175,9 +2188,10 @@ static int atmel_get_retry(struct net_device *dev, static int atmel_set_rts(struct net_device *dev, struct iw_request_info *info, - struct iw_param *vwrq, + union iwreq_data *wrqu, char *extra) { + struct iw_param *vwrq = &wrqu->rts; struct atmel_private *priv = netdev_priv(dev); int rthr = vwrq->value; @@ -2193,9 +2207,10 @@ static int atmel_set_rts(struct net_device *dev, static int atmel_get_rts(struct net_device *dev, struct iw_request_info *info, - struct iw_param *vwrq, + union iwreq_data *wrqu, char *extra) { + struct iw_param *vwrq = &wrqu->rts; struct atmel_private *priv = netdev_priv(dev); vwrq->value = priv->rts_threshold; @@ -2207,9 +2222,10 @@ static int atmel_get_rts(struct net_device *dev, static int atmel_set_frag(struct net_device *dev, struct iw_request_info *info, - struct iw_param *vwrq, + union iwreq_data *wrqu, char *extra) { + struct iw_param *vwrq = &wrqu->frag; struct atmel_private *priv = netdev_priv(dev); int fthr = vwrq->value; @@ -2226,9 +2242,10 @@ static int atmel_set_frag(struct net_device *dev, static int atmel_get_frag(struct net_device *dev, struct iw_request_info *info, - struct iw_param *vwrq, + union iwreq_data *wrqu, char *extra) { + struct iw_param *vwrq = &wrqu->frag; struct atmel_private *priv = netdev_priv(dev); vwrq->value = priv->frag_threshold; @@ -2240,9 +2257,10 @@ static int atmel_get_frag(struct net_device *dev, static int atmel_set_freq(struct net_device *dev, struct iw_request_info *info, - struct iw_freq *fwrq, + union iwreq_data *wrqu, char *extra) { + struct iw_freq *fwrq = &wrqu->freq; struct atmel_private *priv = netdev_priv(dev); int rc = -EINPROGRESS; /* Call commit handler */ @@ -2270,9 +2288,10 @@ static int atmel_set_freq(struct net_device *dev, static int atmel_get_freq(struct net_device *dev, struct iw_request_info *info, - struct iw_freq *fwrq, + union iwreq_data *wrqu, char *extra) { + struct iw_freq *fwrq = &wrqu->freq; struct atmel_private *priv = netdev_priv(dev); fwrq->m = priv->channel; @@ -2282,7 +2301,7 @@ static int atmel_get_freq(struct net_device *dev, static int atmel_set_scan(struct net_device *dev, struct iw_request_info *info, - struct iw_point *dwrq, + union iwreq_data *dwrq, char *extra) { struct atmel_private *priv = netdev_priv(dev); @@ -2320,9 +2339,10 @@ static int atmel_set_scan(struct net_device *dev, static int atmel_get_scan(struct net_device *dev, struct iw_request_info *info, - struct iw_point *dwrq, + union iwreq_data *wrqu, char *extra) { + struct iw_point *dwrq = &wrqu->data; struct atmel_private *priv = netdev_priv(dev); int i; char *current_ev = extra; @@ -2391,9 +2411,10 @@ static int atmel_get_scan(struct net_device *dev, static int atmel_get_range(struct net_device *dev, struct iw_request_info *info, - struct iw_point *dwrq, + union iwreq_data *wrqu, char *extra) { + struct iw_point *dwrq = &wrqu->data; struct atmel_private *priv = netdev_priv(dev); struct iw_range *range = (struct iw_range *) extra; int k, i, j; @@ -2465,9 +2486,10 @@ static int atmel_get_range(struct net_device *dev, static int atmel_set_wap(struct net_device *dev, struct iw_request_info *info, - struct sockaddr *awrq, + union iwreq_data *wrqu, char *extra) { + struct sockaddr *awrq = &wrqu->ap_addr; struct atmel_private *priv = netdev_priv(dev); int i; static const u8 any[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; @@ -2507,7 +2529,7 @@ static int atmel_set_wap(struct net_device *dev, static int atmel_config_commit(struct net_device *dev, struct iw_request_info *info, /* NULL */ - void *zwrq, /* NULL */ + union iwreq_data *zwrq, /* NULL */ char *extra) /* NULL */ { return atmel_open(dev); @@ -2515,66 +2537,40 @@ static int atmel_config_commit(struct net_device *dev, static const iw_handler atmel_handler[] = { - (iw_handler) atmel_config_commit, /* SIOCSIWCOMMIT */ - (iw_handler) atmel_get_name, /* SIOCGIWNAME */ - (iw_handler) NULL, /* SIOCSIWNWID */ - (iw_handler) NULL, /* SIOCGIWNWID */ - (iw_handler) atmel_set_freq, /* SIOCSIWFREQ */ - (iw_handler) atmel_get_freq, /* SIOCGIWFREQ */ - (iw_handler) atmel_set_mode, /* SIOCSIWMODE */ - (iw_handler) atmel_get_mode, /* SIOCGIWMODE */ - (iw_handler) NULL, /* SIOCSIWSENS */ - (iw_handler) NULL, /* SIOCGIWSENS */ - (iw_handler) NULL, /* SIOCSIWRANGE */ - (iw_handler) atmel_get_range, /* SIOCGIWRANGE */ - (iw_handler) NULL, /* SIOCSIWPRIV */ - (iw_handler) NULL, /* SIOCGIWPRIV */ - (iw_handler) NULL, /* SIOCSIWSTATS */ - (iw_handler) NULL, /* SIOCGIWSTATS */ - (iw_handler) NULL, /* SIOCSIWSPY */ - (iw_handler) NULL, /* SIOCGIWSPY */ - (iw_handler) NULL, /* -- hole -- */ - (iw_handler) NULL, /* -- hole -- */ - (iw_handler) atmel_set_wap, /* SIOCSIWAP */ - (iw_handler) atmel_get_wap, /* SIOCGIWAP */ - (iw_handler) NULL, /* -- hole -- */ - (iw_handler) NULL, /* SIOCGIWAPLIST */ - (iw_handler) atmel_set_scan, /* SIOCSIWSCAN */ - (iw_handler) atmel_get_scan, /* SIOCGIWSCAN */ - (iw_handler) atmel_set_essid, /* SIOCSIWESSID */ - (iw_handler) atmel_get_essid, /* SIOCGIWESSID */ - (iw_handler) NULL, /* SIOCSIWNICKN */ - (iw_handler) NULL, /* SIOCGIWNICKN */ - (iw_handler) NULL, /* -- hole -- */ - (iw_handler) NULL, /* -- hole -- */ - (iw_handler) atmel_set_rate, /* SIOCSIWRATE */ - (iw_handler) atmel_get_rate, /* SIOCGIWRATE */ - (iw_handler) atmel_set_rts, /* SIOCSIWRTS */ - (iw_handler) atmel_get_rts, /* SIOCGIWRTS */ - (iw_handler) atmel_set_frag, /* SIOCSIWFRAG */ - (iw_handler) atmel_get_frag, /* SIOCGIWFRAG */ - (iw_handler) NULL, /* SIOCSIWTXPOW */ - (iw_handler) NULL, /* SIOCGIWTXPOW */ - (iw_handler) atmel_set_retry, /* SIOCSIWRETRY */ - (iw_handler) atmel_get_retry, /* SIOCGIWRETRY */ - (iw_handler) atmel_set_encode, /* SIOCSIWENCODE */ - (iw_handler) atmel_get_encode, /* SIOCGIWENCODE */ - (iw_handler) atmel_set_power, /* SIOCSIWPOWER */ - (iw_handler) atmel_get_power, /* SIOCGIWPOWER */ - (iw_handler) NULL, /* -- hole -- */ - (iw_handler) NULL, /* -- hole -- */ - (iw_handler) NULL, /* SIOCSIWGENIE */ - (iw_handler) NULL, /* SIOCGIWGENIE */ - (iw_handler) atmel_set_auth, /* SIOCSIWAUTH */ - (iw_handler) atmel_get_auth, /* SIOCGIWAUTH */ - (iw_handler) atmel_set_encodeext, /* SIOCSIWENCODEEXT */ - (iw_handler) atmel_get_encodeext, /* SIOCGIWENCODEEXT */ - (iw_handler) NULL, /* SIOCSIWPMKSA */ + IW_HANDLER(SIOCSIWCOMMIT, atmel_config_commit), + IW_HANDLER(SIOCGIWNAME, atmel_get_name), + IW_HANDLER(SIOCSIWFREQ, atmel_set_freq), + IW_HANDLER(SIOCGIWFREQ, atmel_get_freq), + IW_HANDLER(SIOCSIWMODE, atmel_set_mode), + IW_HANDLER(SIOCGIWMODE, atmel_get_mode), + IW_HANDLER(SIOCGIWRANGE, atmel_get_range), + IW_HANDLER(SIOCSIWAP, atmel_set_wap), + IW_HANDLER(SIOCGIWAP, atmel_get_wap), + IW_HANDLER(SIOCSIWSCAN, atmel_set_scan), + IW_HANDLER(SIOCGIWSCAN, atmel_get_scan), + IW_HANDLER(SIOCSIWESSID, atmel_set_essid), + IW_HANDLER(SIOCGIWESSID, atmel_get_essid), + IW_HANDLER(SIOCSIWRATE, atmel_set_rate), + IW_HANDLER(SIOCGIWRATE, atmel_get_rate), + IW_HANDLER(SIOCSIWRTS, atmel_set_rts), + IW_HANDLER(SIOCGIWRTS, atmel_get_rts), + IW_HANDLER(SIOCSIWFRAG, atmel_set_frag), + IW_HANDLER(SIOCGIWFRAG, atmel_get_frag), + IW_HANDLER(SIOCSIWRETRY, atmel_set_retry), + IW_HANDLER(SIOCGIWRETRY, atmel_get_retry), + IW_HANDLER(SIOCSIWENCODE, atmel_set_encode), + IW_HANDLER(SIOCGIWENCODE, atmel_get_encode), + IW_HANDLER(SIOCSIWPOWER, atmel_set_power), + IW_HANDLER(SIOCGIWPOWER, atmel_get_power), + IW_HANDLER(SIOCSIWAUTH, atmel_set_auth), + IW_HANDLER(SIOCGIWAUTH, atmel_get_auth), + IW_HANDLER(SIOCSIWENCODEEXT, atmel_set_encodeext), + IW_HANDLER(SIOCGIWENCODEEXT, atmel_get_encodeext), }; static const iw_handler atmel_private_handler[] = { - NULL, /* SIOCIWFIRSTPRIV */ + IW_HANDLER(SIOCIWFIRSTPRIV, NULL), }; struct atmel_priv_ioctl { @@ -2614,8 +2610,8 @@ static const struct iw_handler_def atmel_handler_def = { .num_standard = ARRAY_SIZE(atmel_handler), .num_private = ARRAY_SIZE(atmel_private_handler), .num_private_args = ARRAY_SIZE(atmel_private_args), - .standard = (iw_handler *) atmel_handler, - .private = (iw_handler *) atmel_private_handler, + .standard = atmel_handler, + .private = atmel_private_handler, .private_args = (struct iw_priv_args *) atmel_private_args, .get_wireless_stats = atmel_get_wireless_stats }; From 44a726c3f23cf762ef4ce3c1709aefbcbe97f62c Mon Sep 17 00:00:00 2001 From: Eduard Zingerman Date: Sat, 1 Oct 2022 13:44:24 +0300 Subject: [PATCH 0050/2002] bpftool: Print newline before '}' for struct with padding only fields btf_dump_emit_struct_def attempts to print empty structures at a single line, e.g. `struct empty {}`. However, it has to account for a case when there are no regular but some padding fields in the struct. In such case `vlen` would be zero, but size would be non-zero. E.g. here is struct bpf_timer from vmlinux.h before this patch: struct bpf_timer { long: 64; long: 64;}; And after this patch: struct bpf_dynptr { long: 64; long: 64; }; Signed-off-by: Eduard Zingerman Signed-off-by: Andrii Nakryiko Link: https://lore.kernel.org/bpf/20221001104425.415768-1-eddyz87@gmail.com --- tools/lib/bpf/btf_dump.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tools/lib/bpf/btf_dump.c b/tools/lib/bpf/btf_dump.c index 4221f73a74d0..e4da6de68d8f 100644 --- a/tools/lib/bpf/btf_dump.c +++ b/tools/lib/bpf/btf_dump.c @@ -944,7 +944,11 @@ static void btf_dump_emit_struct_def(struct btf_dump *d, lvl + 1); } - if (vlen) + /* + * Keep `struct empty {}` on a single line, + * only print newline when there are regular or padding fields. + */ + if (vlen || t->size) btf_dump_printf(d, "\n"); btf_dump_printf(d, "%s}", pfx(lvl)); if (packed) From d503f1176b14f722a40ea5110312614982f9a80b Mon Sep 17 00:00:00 2001 From: Eduard Zingerman Date: Sat, 1 Oct 2022 13:44:25 +0300 Subject: [PATCH 0051/2002] selftests/bpf: Test btf dump for struct with padding only fields Structures with zero regular fields but some padding constitute a special case in btf_dump.c:btf_dump_emit_struct_def with regards to newline before closing '}'. Signed-off-by: Eduard Zingerman Signed-off-by: Andrii Nakryiko Link: https://lore.kernel.org/bpf/20221001104425.415768-2-eddyz87@gmail.com --- .../selftests/bpf/progs/btf_dump_test_case_padding.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/tools/testing/selftests/bpf/progs/btf_dump_test_case_padding.c b/tools/testing/selftests/bpf/progs/btf_dump_test_case_padding.c index f2661c8d2d90..7cb522d22a66 100644 --- a/tools/testing/selftests/bpf/progs/btf_dump_test_case_padding.c +++ b/tools/testing/selftests/bpf/progs/btf_dump_test_case_padding.c @@ -102,12 +102,21 @@ struct zone { struct zone_padding __pad__; }; +/* ----- START-EXPECTED-OUTPUT ----- */ +struct padding_wo_named_members { + long: 64; + long: 64; +}; + +/* ------ END-EXPECTED-OUTPUT ------ */ + int f(struct { struct padded_implicitly _1; struct padded_explicitly _2; struct padded_a_lot _3; struct padded_cache_line _4; struct zone _5; + struct padding_wo_named_members _6; } *_) { return 0; From 2a72f5951ac6d613216a93ae3e172cabb04aaefc Mon Sep 17 00:00:00 2001 From: Andrii Nakryiko Date: Wed, 5 Oct 2022 09:14:48 -0700 Subject: [PATCH 0052/2002] selftests/bpf: allow requesting log level 2 in test_verifier Log level 1 on successfully verified programs are basically equivalent to log level 4 (stats-only), so it's useful to be able to request more verbose logs at log level 2. Teach test_verifier to recognize -vv as "very verbose" mode switch and use log level 2 in such mode. Also force verifier stats regradless of -v or -vv, they are very minimal and useful to be always emitted in verbose mode. Signed-off-by: Andrii Nakryiko Link: https://lore.kernel.org/r/20221005161450.1064469-1-andrii@kernel.org Signed-off-by: Alexei Starovoitov --- tools/testing/selftests/bpf/test_verifier.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/tools/testing/selftests/bpf/test_verifier.c b/tools/testing/selftests/bpf/test_verifier.c index 2dbcbf363c18..9c7091100b7f 100644 --- a/tools/testing/selftests/bpf/test_verifier.c +++ b/tools/testing/selftests/bpf/test_verifier.c @@ -68,7 +68,6 @@ #define SKIP_INSNS() BPF_RAW_INSN(0xde, 0xa, 0xd, 0xbeef, 0xdeadbeef) #define DEFAULT_LIBBPF_LOG_LEVEL 4 -#define VERBOSE_LIBBPF_LOG_LEVEL 1 #define F_NEEDS_EFFICIENT_UNALIGNED_ACCESS (1 << 0) #define F_LOAD_WITH_STRICT_ALIGNMENT (1 << 1) @@ -81,6 +80,7 @@ static bool unpriv_disabled = false; static int skips; static bool verbose = false; +static int verif_log_level = 0; struct kfunc_btf_id_pair { const char *kfunc; @@ -759,7 +759,7 @@ static int load_btf_spec(__u32 *types, int types_len, .log_buf = bpf_vlog, .log_size = sizeof(bpf_vlog), .log_level = (verbose - ? VERBOSE_LIBBPF_LOG_LEVEL + ? verif_log_level : DEFAULT_LIBBPF_LOG_LEVEL), ); @@ -1491,7 +1491,7 @@ static void do_test_single(struct bpf_test *test, bool unpriv, opts.expected_attach_type = test->expected_attach_type; if (verbose) - opts.log_level = VERBOSE_LIBBPF_LOG_LEVEL; + opts.log_level = verif_log_level | 4; /* force stats */ else if (expected_ret == VERBOSE_ACCEPT) opts.log_level = 2; else @@ -1746,6 +1746,13 @@ int main(int argc, char **argv) if (argc > 1 && strcmp(argv[1], "-v") == 0) { arg++; verbose = true; + verif_log_level = 1; + argc--; + } + if (argc > 1 && strcmp(argv[1], "-vv") == 0) { + arg++; + verbose = true; + verif_log_level = 2; argc--; } From 6df2eb45e378f38ca42776276e7bb5b5078c12cf Mon Sep 17 00:00:00 2001 From: Andrii Nakryiko Date: Wed, 5 Oct 2022 09:14:49 -0700 Subject: [PATCH 0053/2002] selftests/bpf: avoid reporting +100% difference in veristat for actual 0% In special case when both base and comparison values are 0, veristat currently reports "+0 (+100%)" difference, which is quite confusing. Fix it up to be "+0 (+0%)". Signed-off-by: Andrii Nakryiko Link: https://lore.kernel.org/r/20221005161450.1064469-2-andrii@kernel.org Signed-off-by: Alexei Starovoitov --- tools/testing/selftests/bpf/veristat.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/tools/testing/selftests/bpf/veristat.c b/tools/testing/selftests/bpf/veristat.c index b0d83a28e348..38f678122a7d 100644 --- a/tools/testing/selftests/bpf/veristat.c +++ b/tools/testing/selftests/bpf/veristat.c @@ -1104,17 +1104,21 @@ static void output_comp_stats(const struct verif_stats *base, const struct verif else snprintf(diff_buf, sizeof(diff_buf), "%s", "MISMATCH"); } else { + double p = 0.0; + snprintf(base_buf, sizeof(base_buf), "%ld", base_val); snprintf(comp_buf, sizeof(comp_buf), "%ld", comp_val); diff_val = comp_val - base_val; if (base == &fallback_stats || comp == &fallback_stats || base_val == 0) { - snprintf(diff_buf, sizeof(diff_buf), "%+ld (%+.2lf%%)", - diff_val, comp_val < base_val ? -100.0 : 100.0); + if (comp_val == base_val) + p = 0.0; /* avoid +0 (+100%) case */ + else + p = comp_val < base_val ? -100.0 : 100.0; } else { - snprintf(diff_buf, sizeof(diff_buf), "%+ld (%+.2lf%%)", - diff_val, diff_val * 100.0 / base_val); + p = diff_val * 100.0 / base_val; } + snprintf(diff_buf, sizeof(diff_buf), "%+ld (%+.2lf%%)", diff_val, p); } switch (fmt) { From 60df8c4d32d47fa5e91d02811dadcb97a0bd097e Mon Sep 17 00:00:00 2001 From: Andrii Nakryiko Date: Wed, 5 Oct 2022 09:14:50 -0700 Subject: [PATCH 0054/2002] selftests/bpf: add BPF object fixup step to veristat Add a step to attempt to "fix up" BPF object file to make it possible to successfully load it. E.g., set non-zero size for BPF maps that expect max_entries set, but BPF object file itself doesn't have declarative max_entries values specified. Another issue was with automatic map pinning. Pinning has no effect on BPF verification process itself but can interfere when validating multiple related programs and object files, so veristat disabled all the pinning explicitly. In the future more such fix up heuristics could be added to accommodate common patterns encountered in practice. Signed-off-by: Andrii Nakryiko Link: https://lore.kernel.org/r/20221005161450.1064469-3-andrii@kernel.org Signed-off-by: Alexei Starovoitov --- tools/testing/selftests/bpf/veristat.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/tools/testing/selftests/bpf/veristat.c b/tools/testing/selftests/bpf/veristat.c index 38f678122a7d..973cbf6af323 100644 --- a/tools/testing/selftests/bpf/veristat.c +++ b/tools/testing/selftests/bpf/veristat.c @@ -509,6 +509,28 @@ static int parse_verif_log(char * const buf, size_t buf_sz, struct verif_stats * return 0; } +static void fixup_obj(struct bpf_object *obj) +{ + struct bpf_map *map; + + bpf_object__for_each_map(map, obj) { + /* disable pinning */ + bpf_map__set_pin_path(map, NULL); + + /* fix up map size, if necessary */ + switch (bpf_map__type(map)) { + case BPF_MAP_TYPE_SK_STORAGE: + case BPF_MAP_TYPE_TASK_STORAGE: + case BPF_MAP_TYPE_INODE_STORAGE: + case BPF_MAP_TYPE_CGROUP_STORAGE: + break; + default: + if (bpf_map__max_entries(map) == 0) + bpf_map__set_max_entries(map, 1); + } + } +} + static int process_prog(const char *filename, struct bpf_object *obj, struct bpf_program *prog) { const char *prog_name = bpf_program__name(prog); @@ -543,6 +565,9 @@ static int process_prog(const char *filename, struct bpf_object *obj, struct bpf } verif_log_buf[0] = '\0'; + /* increase chances of successful BPF object loading */ + fixup_obj(obj); + err = bpf_object__load(obj); env.progs_processed++; From 8a76145a2ec2a81dfe34d7ac42e8c242f095e8c8 Mon Sep 17 00:00:00 2001 From: Andrii Nakryiko Date: Wed, 5 Oct 2022 21:24:51 -0700 Subject: [PATCH 0055/2002] bpf: explicitly define BPF_FUNC_xxx integer values MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Historically enum bpf_func_id's BPF_FUNC_xxx enumerators relied on implicit sequential values being assigned by compiler. This is convenient, as new BPF helpers are always added at the very end, but it also has its downsides, some of them being: - with over 200 helpers now it's very hard to know what's each helper's ID, which is often important to know when working with BPF assembly (e.g., by dumping raw bpf assembly instructions with llvm-objdump -d command). it's possible to work around this by looking into vmlinux.h, dumping /sys/btf/kernel/vmlinux, looking at libbpf-provided bpf_helper_defs.h, etc. But it always feels like an unnecessary step and one should be able to quickly figure this out from UAPI header. - when backporting and cherry-picking only some BPF helpers onto older kernels it's important to be able to skip some enum values for helpers that weren't backported, but preserve absolute integer IDs to keep BPF helper IDs stable so that BPF programs stay portable across upstream and backported kernels. While neither problem is insurmountable, they come up frequently enough and are annoying enough to warrant improving the situation. And for the backporting the problem can easily go unnoticed for a while, especially if backport is done with people not very familiar with BPF subsystem overall. Anyways, it's easy to fix this by making sure that __BPF_FUNC_MAPPER macro provides explicit helper IDs. Unfortunately that would potentially break existing users that use UAPI-exposed __BPF_FUNC_MAPPER and are expected to pass macro that accepts only symbolic helper identifier (e.g., map_lookup_elem for bpf_map_lookup_elem() helper). As such, we need to introduce a new macro (___BPF_FUNC_MAPPER) which would specify both identifier and integer ID, but in such a way as to allow existing __BPF_FUNC_MAPPER be expressed in terms of new ___BPF_FUNC_MAPPER macro. And that's what this patch is doing. To avoid duplication and allow __BPF_FUNC_MAPPER stay *exactly* the same, ___BPF_FUNC_MAPPER accepts arbitrary "context" arguments, which can be used to pass any extra macros, arguments, and whatnot. In our case we use this to pass original user-provided macro that expects single argument and __BPF_FUNC_MAPPER is using it's own three-argument __BPF_FUNC_MAPPER_APPLY intermediate macro to impedance-match new and old "callback" macros. Once we resolve this, we use new ___BPF_FUNC_MAPPER to define enum bpf_func_id with explicit values. The other users of __BPF_FUNC_MAPPER in kernel (namely in kernel/bpf/disasm.c) are kept exactly the same both as demonstration that backwards compat works, but also to avoid unnecessary code churn. Note that new ___BPF_FUNC_MAPPER() doesn't forcefully insert comma between values, as that might not be appropriate in all possible cases where ___BPF_FUNC_MAPPER might be used by users. This doesn't reduce usability, as it's trivial to insert that comma inside "callback" macro. To validate all the manually specified IDs are exactly right, we used BTF to compare before and after values: $ bpftool btf dump file ~/linux-build/default/vmlinux | rg bpf_func_id -A 211 > after.txt $ git stash # stach UAPI changes $ make -j90 ... re-building kernel without UAPI changes ... $ bpftool btf dump file ~/linux-build/default/vmlinux | rg bpf_func_id -A 211 > before.txt $ diff -u before.txt after.txt --- before.txt 2022-10-05 10:48:18.119195916 -0700 +++ after.txt 2022-10-05 10:46:49.446615025 -0700 @@ -1,4 +1,4 @@ -[14576] ENUM 'bpf_func_id' encoding=UNSIGNED size=4 vlen=211 +[9560] ENUM 'bpf_func_id' encoding=UNSIGNED size=4 vlen=211 'BPF_FUNC_unspec' val=0 'BPF_FUNC_map_lookup_elem' val=1 'BPF_FUNC_map_update_elem' val=2 As can be seen from diff above, the only thing that changed was resulting BTF type ID of ENUM bpf_func_id, not any of the enumerators, their names or integer values. The only other place that needed fixing was scripts/bpf_doc.py used to generate man pages and bpf_helper_defs.h header for libbpf and selftests. That script is tightly-coupled to exact shape of ___BPF_FUNC_MAPPER macro definition, so had to be trivially adapted. Cc: Quentin Monnet Reported-by: Andrea Terzolo Signed-off-by: Andrii Nakryiko Reviewed-by: Quentin Monnet Acked-by: Jiri Olsa Acked-by: Toke Høiland-Jørgensen Link: https://lore.kernel.org/r/20221006042452.2089843-1-andrii@kernel.org Signed-off-by: Alexei Starovoitov --- include/uapi/linux/bpf.h | 432 +++++++++++++++++---------------- scripts/bpf_doc.py | 19 +- tools/include/uapi/linux/bpf.h | 432 +++++++++++++++++---------------- 3 files changed, 447 insertions(+), 436 deletions(-) diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h index 51b9aa640ad2..17f61338f8f8 100644 --- a/include/uapi/linux/bpf.h +++ b/include/uapi/linux/bpf.h @@ -5436,225 +5436,231 @@ union bpf_attr { * larger than the size of the ring buffer, or which cannot fit * within a struct bpf_dynptr. */ -#define __BPF_FUNC_MAPPER(FN) \ - FN(unspec), \ - FN(map_lookup_elem), \ - FN(map_update_elem), \ - FN(map_delete_elem), \ - FN(probe_read), \ - FN(ktime_get_ns), \ - FN(trace_printk), \ - FN(get_prandom_u32), \ - FN(get_smp_processor_id), \ - FN(skb_store_bytes), \ - FN(l3_csum_replace), \ - FN(l4_csum_replace), \ - FN(tail_call), \ - FN(clone_redirect), \ - FN(get_current_pid_tgid), \ - FN(get_current_uid_gid), \ - FN(get_current_comm), \ - FN(get_cgroup_classid), \ - FN(skb_vlan_push), \ - FN(skb_vlan_pop), \ - FN(skb_get_tunnel_key), \ - FN(skb_set_tunnel_key), \ - FN(perf_event_read), \ - FN(redirect), \ - FN(get_route_realm), \ - FN(perf_event_output), \ - FN(skb_load_bytes), \ - FN(get_stackid), \ - FN(csum_diff), \ - FN(skb_get_tunnel_opt), \ - FN(skb_set_tunnel_opt), \ - FN(skb_change_proto), \ - FN(skb_change_type), \ - FN(skb_under_cgroup), \ - FN(get_hash_recalc), \ - FN(get_current_task), \ - FN(probe_write_user), \ - FN(current_task_under_cgroup), \ - FN(skb_change_tail), \ - FN(skb_pull_data), \ - FN(csum_update), \ - FN(set_hash_invalid), \ - FN(get_numa_node_id), \ - FN(skb_change_head), \ - FN(xdp_adjust_head), \ - FN(probe_read_str), \ - FN(get_socket_cookie), \ - FN(get_socket_uid), \ - FN(set_hash), \ - FN(setsockopt), \ - FN(skb_adjust_room), \ - FN(redirect_map), \ - FN(sk_redirect_map), \ - FN(sock_map_update), \ - FN(xdp_adjust_meta), \ - FN(perf_event_read_value), \ - FN(perf_prog_read_value), \ - FN(getsockopt), \ - FN(override_return), \ - FN(sock_ops_cb_flags_set), \ - FN(msg_redirect_map), \ - FN(msg_apply_bytes), \ - FN(msg_cork_bytes), \ - FN(msg_pull_data), \ - FN(bind), \ - FN(xdp_adjust_tail), \ - FN(skb_get_xfrm_state), \ - FN(get_stack), \ - FN(skb_load_bytes_relative), \ - FN(fib_lookup), \ - FN(sock_hash_update), \ - FN(msg_redirect_hash), \ - FN(sk_redirect_hash), \ - FN(lwt_push_encap), \ - FN(lwt_seg6_store_bytes), \ - FN(lwt_seg6_adjust_srh), \ - FN(lwt_seg6_action), \ - FN(rc_repeat), \ - FN(rc_keydown), \ - FN(skb_cgroup_id), \ - FN(get_current_cgroup_id), \ - FN(get_local_storage), \ - FN(sk_select_reuseport), \ - FN(skb_ancestor_cgroup_id), \ - FN(sk_lookup_tcp), \ - FN(sk_lookup_udp), \ - FN(sk_release), \ - FN(map_push_elem), \ - FN(map_pop_elem), \ - FN(map_peek_elem), \ - FN(msg_push_data), \ - FN(msg_pop_data), \ - FN(rc_pointer_rel), \ - FN(spin_lock), \ - FN(spin_unlock), \ - FN(sk_fullsock), \ - FN(tcp_sock), \ - FN(skb_ecn_set_ce), \ - FN(get_listener_sock), \ - FN(skc_lookup_tcp), \ - FN(tcp_check_syncookie), \ - FN(sysctl_get_name), \ - FN(sysctl_get_current_value), \ - FN(sysctl_get_new_value), \ - FN(sysctl_set_new_value), \ - FN(strtol), \ - FN(strtoul), \ - FN(sk_storage_get), \ - FN(sk_storage_delete), \ - FN(send_signal), \ - FN(tcp_gen_syncookie), \ - FN(skb_output), \ - FN(probe_read_user), \ - FN(probe_read_kernel), \ - FN(probe_read_user_str), \ - FN(probe_read_kernel_str), \ - FN(tcp_send_ack), \ - FN(send_signal_thread), \ - FN(jiffies64), \ - FN(read_branch_records), \ - FN(get_ns_current_pid_tgid), \ - FN(xdp_output), \ - FN(get_netns_cookie), \ - FN(get_current_ancestor_cgroup_id), \ - FN(sk_assign), \ - FN(ktime_get_boot_ns), \ - FN(seq_printf), \ - FN(seq_write), \ - FN(sk_cgroup_id), \ - FN(sk_ancestor_cgroup_id), \ - FN(ringbuf_output), \ - FN(ringbuf_reserve), \ - FN(ringbuf_submit), \ - FN(ringbuf_discard), \ - FN(ringbuf_query), \ - FN(csum_level), \ - FN(skc_to_tcp6_sock), \ - FN(skc_to_tcp_sock), \ - FN(skc_to_tcp_timewait_sock), \ - FN(skc_to_tcp_request_sock), \ - FN(skc_to_udp6_sock), \ - FN(get_task_stack), \ - FN(load_hdr_opt), \ - FN(store_hdr_opt), \ - FN(reserve_hdr_opt), \ - FN(inode_storage_get), \ - FN(inode_storage_delete), \ - FN(d_path), \ - FN(copy_from_user), \ - FN(snprintf_btf), \ - FN(seq_printf_btf), \ - FN(skb_cgroup_classid), \ - FN(redirect_neigh), \ - FN(per_cpu_ptr), \ - FN(this_cpu_ptr), \ - FN(redirect_peer), \ - FN(task_storage_get), \ - FN(task_storage_delete), \ - FN(get_current_task_btf), \ - FN(bprm_opts_set), \ - FN(ktime_get_coarse_ns), \ - FN(ima_inode_hash), \ - FN(sock_from_file), \ - FN(check_mtu), \ - FN(for_each_map_elem), \ - FN(snprintf), \ - FN(sys_bpf), \ - FN(btf_find_by_name_kind), \ - FN(sys_close), \ - FN(timer_init), \ - FN(timer_set_callback), \ - FN(timer_start), \ - FN(timer_cancel), \ - FN(get_func_ip), \ - FN(get_attach_cookie), \ - FN(task_pt_regs), \ - FN(get_branch_snapshot), \ - FN(trace_vprintk), \ - FN(skc_to_unix_sock), \ - FN(kallsyms_lookup_name), \ - FN(find_vma), \ - FN(loop), \ - FN(strncmp), \ - FN(get_func_arg), \ - FN(get_func_ret), \ - FN(get_func_arg_cnt), \ - FN(get_retval), \ - FN(set_retval), \ - FN(xdp_get_buff_len), \ - FN(xdp_load_bytes), \ - FN(xdp_store_bytes), \ - FN(copy_from_user_task), \ - FN(skb_set_tstamp), \ - FN(ima_file_hash), \ - FN(kptr_xchg), \ - FN(map_lookup_percpu_elem), \ - FN(skc_to_mptcp_sock), \ - FN(dynptr_from_mem), \ - FN(ringbuf_reserve_dynptr), \ - FN(ringbuf_submit_dynptr), \ - FN(ringbuf_discard_dynptr), \ - FN(dynptr_read), \ - FN(dynptr_write), \ - FN(dynptr_data), \ - FN(tcp_raw_gen_syncookie_ipv4), \ - FN(tcp_raw_gen_syncookie_ipv6), \ - FN(tcp_raw_check_syncookie_ipv4), \ - FN(tcp_raw_check_syncookie_ipv6), \ - FN(ktime_get_tai_ns), \ - FN(user_ringbuf_drain), \ +#define ___BPF_FUNC_MAPPER(FN, ctx...) \ + FN(unspec, 0, ##ctx) \ + FN(map_lookup_elem, 1, ##ctx) \ + FN(map_update_elem, 2, ##ctx) \ + FN(map_delete_elem, 3, ##ctx) \ + FN(probe_read, 4, ##ctx) \ + FN(ktime_get_ns, 5, ##ctx) \ + FN(trace_printk, 6, ##ctx) \ + FN(get_prandom_u32, 7, ##ctx) \ + FN(get_smp_processor_id, 8, ##ctx) \ + FN(skb_store_bytes, 9, ##ctx) \ + FN(l3_csum_replace, 10, ##ctx) \ + FN(l4_csum_replace, 11, ##ctx) \ + FN(tail_call, 12, ##ctx) \ + FN(clone_redirect, 13, ##ctx) \ + FN(get_current_pid_tgid, 14, ##ctx) \ + FN(get_current_uid_gid, 15, ##ctx) \ + FN(get_current_comm, 16, ##ctx) \ + FN(get_cgroup_classid, 17, ##ctx) \ + FN(skb_vlan_push, 18, ##ctx) \ + FN(skb_vlan_pop, 19, ##ctx) \ + FN(skb_get_tunnel_key, 20, ##ctx) \ + FN(skb_set_tunnel_key, 21, ##ctx) \ + FN(perf_event_read, 22, ##ctx) \ + FN(redirect, 23, ##ctx) \ + FN(get_route_realm, 24, ##ctx) \ + FN(perf_event_output, 25, ##ctx) \ + FN(skb_load_bytes, 26, ##ctx) \ + FN(get_stackid, 27, ##ctx) \ + FN(csum_diff, 28, ##ctx) \ + FN(skb_get_tunnel_opt, 29, ##ctx) \ + FN(skb_set_tunnel_opt, 30, ##ctx) \ + FN(skb_change_proto, 31, ##ctx) \ + FN(skb_change_type, 32, ##ctx) \ + FN(skb_under_cgroup, 33, ##ctx) \ + FN(get_hash_recalc, 34, ##ctx) \ + FN(get_current_task, 35, ##ctx) \ + FN(probe_write_user, 36, ##ctx) \ + FN(current_task_under_cgroup, 37, ##ctx) \ + FN(skb_change_tail, 38, ##ctx) \ + FN(skb_pull_data, 39, ##ctx) \ + FN(csum_update, 40, ##ctx) \ + FN(set_hash_invalid, 41, ##ctx) \ + FN(get_numa_node_id, 42, ##ctx) \ + FN(skb_change_head, 43, ##ctx) \ + FN(xdp_adjust_head, 44, ##ctx) \ + FN(probe_read_str, 45, ##ctx) \ + FN(get_socket_cookie, 46, ##ctx) \ + FN(get_socket_uid, 47, ##ctx) \ + FN(set_hash, 48, ##ctx) \ + FN(setsockopt, 49, ##ctx) \ + FN(skb_adjust_room, 50, ##ctx) \ + FN(redirect_map, 51, ##ctx) \ + FN(sk_redirect_map, 52, ##ctx) \ + FN(sock_map_update, 53, ##ctx) \ + FN(xdp_adjust_meta, 54, ##ctx) \ + FN(perf_event_read_value, 55, ##ctx) \ + FN(perf_prog_read_value, 56, ##ctx) \ + FN(getsockopt, 57, ##ctx) \ + FN(override_return, 58, ##ctx) \ + FN(sock_ops_cb_flags_set, 59, ##ctx) \ + FN(msg_redirect_map, 60, ##ctx) \ + FN(msg_apply_bytes, 61, ##ctx) \ + FN(msg_cork_bytes, 62, ##ctx) \ + FN(msg_pull_data, 63, ##ctx) \ + FN(bind, 64, ##ctx) \ + FN(xdp_adjust_tail, 65, ##ctx) \ + FN(skb_get_xfrm_state, 66, ##ctx) \ + FN(get_stack, 67, ##ctx) \ + FN(skb_load_bytes_relative, 68, ##ctx) \ + FN(fib_lookup, 69, ##ctx) \ + FN(sock_hash_update, 70, ##ctx) \ + FN(msg_redirect_hash, 71, ##ctx) \ + FN(sk_redirect_hash, 72, ##ctx) \ + FN(lwt_push_encap, 73, ##ctx) \ + FN(lwt_seg6_store_bytes, 74, ##ctx) \ + FN(lwt_seg6_adjust_srh, 75, ##ctx) \ + FN(lwt_seg6_action, 76, ##ctx) \ + FN(rc_repeat, 77, ##ctx) \ + FN(rc_keydown, 78, ##ctx) \ + FN(skb_cgroup_id, 79, ##ctx) \ + FN(get_current_cgroup_id, 80, ##ctx) \ + FN(get_local_storage, 81, ##ctx) \ + FN(sk_select_reuseport, 82, ##ctx) \ + FN(skb_ancestor_cgroup_id, 83, ##ctx) \ + FN(sk_lookup_tcp, 84, ##ctx) \ + FN(sk_lookup_udp, 85, ##ctx) \ + FN(sk_release, 86, ##ctx) \ + FN(map_push_elem, 87, ##ctx) \ + FN(map_pop_elem, 88, ##ctx) \ + FN(map_peek_elem, 89, ##ctx) \ + FN(msg_push_data, 90, ##ctx) \ + FN(msg_pop_data, 91, ##ctx) \ + FN(rc_pointer_rel, 92, ##ctx) \ + FN(spin_lock, 93, ##ctx) \ + FN(spin_unlock, 94, ##ctx) \ + FN(sk_fullsock, 95, ##ctx) \ + FN(tcp_sock, 96, ##ctx) \ + FN(skb_ecn_set_ce, 97, ##ctx) \ + FN(get_listener_sock, 98, ##ctx) \ + FN(skc_lookup_tcp, 99, ##ctx) \ + FN(tcp_check_syncookie, 100, ##ctx) \ + FN(sysctl_get_name, 101, ##ctx) \ + FN(sysctl_get_current_value, 102, ##ctx) \ + FN(sysctl_get_new_value, 103, ##ctx) \ + FN(sysctl_set_new_value, 104, ##ctx) \ + FN(strtol, 105, ##ctx) \ + FN(strtoul, 106, ##ctx) \ + FN(sk_storage_get, 107, ##ctx) \ + FN(sk_storage_delete, 108, ##ctx) \ + FN(send_signal, 109, ##ctx) \ + FN(tcp_gen_syncookie, 110, ##ctx) \ + FN(skb_output, 111, ##ctx) \ + FN(probe_read_user, 112, ##ctx) \ + FN(probe_read_kernel, 113, ##ctx) \ + FN(probe_read_user_str, 114, ##ctx) \ + FN(probe_read_kernel_str, 115, ##ctx) \ + FN(tcp_send_ack, 116, ##ctx) \ + FN(send_signal_thread, 117, ##ctx) \ + FN(jiffies64, 118, ##ctx) \ + FN(read_branch_records, 119, ##ctx) \ + FN(get_ns_current_pid_tgid, 120, ##ctx) \ + FN(xdp_output, 121, ##ctx) \ + FN(get_netns_cookie, 122, ##ctx) \ + FN(get_current_ancestor_cgroup_id, 123, ##ctx) \ + FN(sk_assign, 124, ##ctx) \ + FN(ktime_get_boot_ns, 125, ##ctx) \ + FN(seq_printf, 126, ##ctx) \ + FN(seq_write, 127, ##ctx) \ + FN(sk_cgroup_id, 128, ##ctx) \ + FN(sk_ancestor_cgroup_id, 129, ##ctx) \ + FN(ringbuf_output, 130, ##ctx) \ + FN(ringbuf_reserve, 131, ##ctx) \ + FN(ringbuf_submit, 132, ##ctx) \ + FN(ringbuf_discard, 133, ##ctx) \ + FN(ringbuf_query, 134, ##ctx) \ + FN(csum_level, 135, ##ctx) \ + FN(skc_to_tcp6_sock, 136, ##ctx) \ + FN(skc_to_tcp_sock, 137, ##ctx) \ + FN(skc_to_tcp_timewait_sock, 138, ##ctx) \ + FN(skc_to_tcp_request_sock, 139, ##ctx) \ + FN(skc_to_udp6_sock, 140, ##ctx) \ + FN(get_task_stack, 141, ##ctx) \ + FN(load_hdr_opt, 142, ##ctx) \ + FN(store_hdr_opt, 143, ##ctx) \ + FN(reserve_hdr_opt, 144, ##ctx) \ + FN(inode_storage_get, 145, ##ctx) \ + FN(inode_storage_delete, 146, ##ctx) \ + FN(d_path, 147, ##ctx) \ + FN(copy_from_user, 148, ##ctx) \ + FN(snprintf_btf, 149, ##ctx) \ + FN(seq_printf_btf, 150, ##ctx) \ + FN(skb_cgroup_classid, 151, ##ctx) \ + FN(redirect_neigh, 152, ##ctx) \ + FN(per_cpu_ptr, 153, ##ctx) \ + FN(this_cpu_ptr, 154, ##ctx) \ + FN(redirect_peer, 155, ##ctx) \ + FN(task_storage_get, 156, ##ctx) \ + FN(task_storage_delete, 157, ##ctx) \ + FN(get_current_task_btf, 158, ##ctx) \ + FN(bprm_opts_set, 159, ##ctx) \ + FN(ktime_get_coarse_ns, 160, ##ctx) \ + FN(ima_inode_hash, 161, ##ctx) \ + FN(sock_from_file, 162, ##ctx) \ + FN(check_mtu, 163, ##ctx) \ + FN(for_each_map_elem, 164, ##ctx) \ + FN(snprintf, 165, ##ctx) \ + FN(sys_bpf, 166, ##ctx) \ + FN(btf_find_by_name_kind, 167, ##ctx) \ + FN(sys_close, 168, ##ctx) \ + FN(timer_init, 169, ##ctx) \ + FN(timer_set_callback, 170, ##ctx) \ + FN(timer_start, 171, ##ctx) \ + FN(timer_cancel, 172, ##ctx) \ + FN(get_func_ip, 173, ##ctx) \ + FN(get_attach_cookie, 174, ##ctx) \ + FN(task_pt_regs, 175, ##ctx) \ + FN(get_branch_snapshot, 176, ##ctx) \ + FN(trace_vprintk, 177, ##ctx) \ + FN(skc_to_unix_sock, 178, ##ctx) \ + FN(kallsyms_lookup_name, 179, ##ctx) \ + FN(find_vma, 180, ##ctx) \ + FN(loop, 181, ##ctx) \ + FN(strncmp, 182, ##ctx) \ + FN(get_func_arg, 183, ##ctx) \ + FN(get_func_ret, 184, ##ctx) \ + FN(get_func_arg_cnt, 185, ##ctx) \ + FN(get_retval, 186, ##ctx) \ + FN(set_retval, 187, ##ctx) \ + FN(xdp_get_buff_len, 188, ##ctx) \ + FN(xdp_load_bytes, 189, ##ctx) \ + FN(xdp_store_bytes, 190, ##ctx) \ + FN(copy_from_user_task, 191, ##ctx) \ + FN(skb_set_tstamp, 192, ##ctx) \ + FN(ima_file_hash, 193, ##ctx) \ + FN(kptr_xchg, 194, ##ctx) \ + FN(map_lookup_percpu_elem, 195, ##ctx) \ + FN(skc_to_mptcp_sock, 196, ##ctx) \ + FN(dynptr_from_mem, 197, ##ctx) \ + FN(ringbuf_reserve_dynptr, 198, ##ctx) \ + FN(ringbuf_submit_dynptr, 199, ##ctx) \ + FN(ringbuf_discard_dynptr, 200, ##ctx) \ + FN(dynptr_read, 201, ##ctx) \ + FN(dynptr_write, 202, ##ctx) \ + FN(dynptr_data, 203, ##ctx) \ + FN(tcp_raw_gen_syncookie_ipv4, 204, ##ctx) \ + FN(tcp_raw_gen_syncookie_ipv6, 205, ##ctx) \ + FN(tcp_raw_check_syncookie_ipv4, 206, ##ctx) \ + FN(tcp_raw_check_syncookie_ipv6, 207, ##ctx) \ + FN(ktime_get_tai_ns, 208, ##ctx) \ + FN(user_ringbuf_drain, 209, ##ctx) \ /* */ +/* backwards-compatibility macros for users of __BPF_FUNC_MAPPER that don't + * know or care about integer value that is now passed as second argument + */ +#define __BPF_FUNC_MAPPER_APPLY(name, value, FN) FN(name), +#define __BPF_FUNC_MAPPER(FN) ___BPF_FUNC_MAPPER(__BPF_FUNC_MAPPER_APPLY, FN) + /* integer value in 'imm' field of BPF_CALL instruction selects which helper * function eBPF program intends to call */ -#define __BPF_ENUM_FN(x) BPF_FUNC_ ## x +#define __BPF_ENUM_FN(x, y) BPF_FUNC_ ## x = y, enum bpf_func_id { - __BPF_FUNC_MAPPER(__BPF_ENUM_FN) + ___BPF_FUNC_MAPPER(__BPF_ENUM_FN) __BPF_FUNC_MAX_ID, }; #undef __BPF_ENUM_FN diff --git a/scripts/bpf_doc.py b/scripts/bpf_doc.py index d5c389df6045..2fe07c9e3fe0 100755 --- a/scripts/bpf_doc.py +++ b/scripts/bpf_doc.py @@ -253,28 +253,27 @@ class HeaderParser(object): break def parse_define_helpers(self): - # Parse FN(...) in #define __BPF_FUNC_MAPPER to compare later with the + # Parse FN(...) in #define ___BPF_FUNC_MAPPER to compare later with the # number of unique function names present in description and use the # correct enumeration value. # Note: seek_to(..) discards the first line below the target search text, - # resulting in FN(unspec) being skipped and not added to self.define_unique_helpers. - self.seek_to('#define __BPF_FUNC_MAPPER(FN)', + # resulting in FN(unspec, 0, ##ctx) being skipped and not added to + # self.define_unique_helpers. + self.seek_to('#define ___BPF_FUNC_MAPPER(FN, ctx...)', 'Could not find start of eBPF helper definition list') # Searches for one FN(\w+) define or a backslash for newline - p = re.compile('\s*FN\((\w+)\)|\\\\') + p = re.compile('\s*FN\((\w+), (\d+), ##ctx\)|\\\\') fn_defines_str = '' - i = 1 # 'unspec' is skipped as mentioned above while True: capture = p.match(self.line) if capture: fn_defines_str += self.line - self.helper_enum_vals[capture.expand(r'bpf_\1')] = i - i += 1 + self.helper_enum_vals[capture.expand(r'bpf_\1')] = int(capture[2]) else: break self.line = self.reader.readline() # Find the number of occurences of FN(\w+) - self.define_unique_helpers = re.findall('FN\(\w+\)', fn_defines_str) + self.define_unique_helpers = re.findall('FN\(\w+, \d+, ##ctx\)', fn_defines_str) def assign_helper_values(self): seen_helpers = set() @@ -423,7 +422,7 @@ class PrinterHelpersRST(PrinterRST): """ def __init__(self, parser): self.elements = parser.helpers - self.elem_number_check(parser.desc_unique_helpers, parser.define_unique_helpers, 'helper', '__BPF_FUNC_MAPPER') + self.elem_number_check(parser.desc_unique_helpers, parser.define_unique_helpers, 'helper', '___BPF_FUNC_MAPPER') def print_header(self): header = '''\ @@ -636,7 +635,7 @@ class PrinterHelpers(Printer): """ def __init__(self, parser): self.elements = parser.helpers - self.elem_number_check(parser.desc_unique_helpers, parser.define_unique_helpers, 'helper', '__BPF_FUNC_MAPPER') + self.elem_number_check(parser.desc_unique_helpers, parser.define_unique_helpers, 'helper', '___BPF_FUNC_MAPPER') type_fwds = [ 'struct bpf_fib_lookup', diff --git a/tools/include/uapi/linux/bpf.h b/tools/include/uapi/linux/bpf.h index 51b9aa640ad2..17f61338f8f8 100644 --- a/tools/include/uapi/linux/bpf.h +++ b/tools/include/uapi/linux/bpf.h @@ -5436,225 +5436,231 @@ union bpf_attr { * larger than the size of the ring buffer, or which cannot fit * within a struct bpf_dynptr. */ -#define __BPF_FUNC_MAPPER(FN) \ - FN(unspec), \ - FN(map_lookup_elem), \ - FN(map_update_elem), \ - FN(map_delete_elem), \ - FN(probe_read), \ - FN(ktime_get_ns), \ - FN(trace_printk), \ - FN(get_prandom_u32), \ - FN(get_smp_processor_id), \ - FN(skb_store_bytes), \ - FN(l3_csum_replace), \ - FN(l4_csum_replace), \ - FN(tail_call), \ - FN(clone_redirect), \ - FN(get_current_pid_tgid), \ - FN(get_current_uid_gid), \ - FN(get_current_comm), \ - FN(get_cgroup_classid), \ - FN(skb_vlan_push), \ - FN(skb_vlan_pop), \ - FN(skb_get_tunnel_key), \ - FN(skb_set_tunnel_key), \ - FN(perf_event_read), \ - FN(redirect), \ - FN(get_route_realm), \ - FN(perf_event_output), \ - FN(skb_load_bytes), \ - FN(get_stackid), \ - FN(csum_diff), \ - FN(skb_get_tunnel_opt), \ - FN(skb_set_tunnel_opt), \ - FN(skb_change_proto), \ - FN(skb_change_type), \ - FN(skb_under_cgroup), \ - FN(get_hash_recalc), \ - FN(get_current_task), \ - FN(probe_write_user), \ - FN(current_task_under_cgroup), \ - FN(skb_change_tail), \ - FN(skb_pull_data), \ - FN(csum_update), \ - FN(set_hash_invalid), \ - FN(get_numa_node_id), \ - FN(skb_change_head), \ - FN(xdp_adjust_head), \ - FN(probe_read_str), \ - FN(get_socket_cookie), \ - FN(get_socket_uid), \ - FN(set_hash), \ - FN(setsockopt), \ - FN(skb_adjust_room), \ - FN(redirect_map), \ - FN(sk_redirect_map), \ - FN(sock_map_update), \ - FN(xdp_adjust_meta), \ - FN(perf_event_read_value), \ - FN(perf_prog_read_value), \ - FN(getsockopt), \ - FN(override_return), \ - FN(sock_ops_cb_flags_set), \ - FN(msg_redirect_map), \ - FN(msg_apply_bytes), \ - FN(msg_cork_bytes), \ - FN(msg_pull_data), \ - FN(bind), \ - FN(xdp_adjust_tail), \ - FN(skb_get_xfrm_state), \ - FN(get_stack), \ - FN(skb_load_bytes_relative), \ - FN(fib_lookup), \ - FN(sock_hash_update), \ - FN(msg_redirect_hash), \ - FN(sk_redirect_hash), \ - FN(lwt_push_encap), \ - FN(lwt_seg6_store_bytes), \ - FN(lwt_seg6_adjust_srh), \ - FN(lwt_seg6_action), \ - FN(rc_repeat), \ - FN(rc_keydown), \ - FN(skb_cgroup_id), \ - FN(get_current_cgroup_id), \ - FN(get_local_storage), \ - FN(sk_select_reuseport), \ - FN(skb_ancestor_cgroup_id), \ - FN(sk_lookup_tcp), \ - FN(sk_lookup_udp), \ - FN(sk_release), \ - FN(map_push_elem), \ - FN(map_pop_elem), \ - FN(map_peek_elem), \ - FN(msg_push_data), \ - FN(msg_pop_data), \ - FN(rc_pointer_rel), \ - FN(spin_lock), \ - FN(spin_unlock), \ - FN(sk_fullsock), \ - FN(tcp_sock), \ - FN(skb_ecn_set_ce), \ - FN(get_listener_sock), \ - FN(skc_lookup_tcp), \ - FN(tcp_check_syncookie), \ - FN(sysctl_get_name), \ - FN(sysctl_get_current_value), \ - FN(sysctl_get_new_value), \ - FN(sysctl_set_new_value), \ - FN(strtol), \ - FN(strtoul), \ - FN(sk_storage_get), \ - FN(sk_storage_delete), \ - FN(send_signal), \ - FN(tcp_gen_syncookie), \ - FN(skb_output), \ - FN(probe_read_user), \ - FN(probe_read_kernel), \ - FN(probe_read_user_str), \ - FN(probe_read_kernel_str), \ - FN(tcp_send_ack), \ - FN(send_signal_thread), \ - FN(jiffies64), \ - FN(read_branch_records), \ - FN(get_ns_current_pid_tgid), \ - FN(xdp_output), \ - FN(get_netns_cookie), \ - FN(get_current_ancestor_cgroup_id), \ - FN(sk_assign), \ - FN(ktime_get_boot_ns), \ - FN(seq_printf), \ - FN(seq_write), \ - FN(sk_cgroup_id), \ - FN(sk_ancestor_cgroup_id), \ - FN(ringbuf_output), \ - FN(ringbuf_reserve), \ - FN(ringbuf_submit), \ - FN(ringbuf_discard), \ - FN(ringbuf_query), \ - FN(csum_level), \ - FN(skc_to_tcp6_sock), \ - FN(skc_to_tcp_sock), \ - FN(skc_to_tcp_timewait_sock), \ - FN(skc_to_tcp_request_sock), \ - FN(skc_to_udp6_sock), \ - FN(get_task_stack), \ - FN(load_hdr_opt), \ - FN(store_hdr_opt), \ - FN(reserve_hdr_opt), \ - FN(inode_storage_get), \ - FN(inode_storage_delete), \ - FN(d_path), \ - FN(copy_from_user), \ - FN(snprintf_btf), \ - FN(seq_printf_btf), \ - FN(skb_cgroup_classid), \ - FN(redirect_neigh), \ - FN(per_cpu_ptr), \ - FN(this_cpu_ptr), \ - FN(redirect_peer), \ - FN(task_storage_get), \ - FN(task_storage_delete), \ - FN(get_current_task_btf), \ - FN(bprm_opts_set), \ - FN(ktime_get_coarse_ns), \ - FN(ima_inode_hash), \ - FN(sock_from_file), \ - FN(check_mtu), \ - FN(for_each_map_elem), \ - FN(snprintf), \ - FN(sys_bpf), \ - FN(btf_find_by_name_kind), \ - FN(sys_close), \ - FN(timer_init), \ - FN(timer_set_callback), \ - FN(timer_start), \ - FN(timer_cancel), \ - FN(get_func_ip), \ - FN(get_attach_cookie), \ - FN(task_pt_regs), \ - FN(get_branch_snapshot), \ - FN(trace_vprintk), \ - FN(skc_to_unix_sock), \ - FN(kallsyms_lookup_name), \ - FN(find_vma), \ - FN(loop), \ - FN(strncmp), \ - FN(get_func_arg), \ - FN(get_func_ret), \ - FN(get_func_arg_cnt), \ - FN(get_retval), \ - FN(set_retval), \ - FN(xdp_get_buff_len), \ - FN(xdp_load_bytes), \ - FN(xdp_store_bytes), \ - FN(copy_from_user_task), \ - FN(skb_set_tstamp), \ - FN(ima_file_hash), \ - FN(kptr_xchg), \ - FN(map_lookup_percpu_elem), \ - FN(skc_to_mptcp_sock), \ - FN(dynptr_from_mem), \ - FN(ringbuf_reserve_dynptr), \ - FN(ringbuf_submit_dynptr), \ - FN(ringbuf_discard_dynptr), \ - FN(dynptr_read), \ - FN(dynptr_write), \ - FN(dynptr_data), \ - FN(tcp_raw_gen_syncookie_ipv4), \ - FN(tcp_raw_gen_syncookie_ipv6), \ - FN(tcp_raw_check_syncookie_ipv4), \ - FN(tcp_raw_check_syncookie_ipv6), \ - FN(ktime_get_tai_ns), \ - FN(user_ringbuf_drain), \ +#define ___BPF_FUNC_MAPPER(FN, ctx...) \ + FN(unspec, 0, ##ctx) \ + FN(map_lookup_elem, 1, ##ctx) \ + FN(map_update_elem, 2, ##ctx) \ + FN(map_delete_elem, 3, ##ctx) \ + FN(probe_read, 4, ##ctx) \ + FN(ktime_get_ns, 5, ##ctx) \ + FN(trace_printk, 6, ##ctx) \ + FN(get_prandom_u32, 7, ##ctx) \ + FN(get_smp_processor_id, 8, ##ctx) \ + FN(skb_store_bytes, 9, ##ctx) \ + FN(l3_csum_replace, 10, ##ctx) \ + FN(l4_csum_replace, 11, ##ctx) \ + FN(tail_call, 12, ##ctx) \ + FN(clone_redirect, 13, ##ctx) \ + FN(get_current_pid_tgid, 14, ##ctx) \ + FN(get_current_uid_gid, 15, ##ctx) \ + FN(get_current_comm, 16, ##ctx) \ + FN(get_cgroup_classid, 17, ##ctx) \ + FN(skb_vlan_push, 18, ##ctx) \ + FN(skb_vlan_pop, 19, ##ctx) \ + FN(skb_get_tunnel_key, 20, ##ctx) \ + FN(skb_set_tunnel_key, 21, ##ctx) \ + FN(perf_event_read, 22, ##ctx) \ + FN(redirect, 23, ##ctx) \ + FN(get_route_realm, 24, ##ctx) \ + FN(perf_event_output, 25, ##ctx) \ + FN(skb_load_bytes, 26, ##ctx) \ + FN(get_stackid, 27, ##ctx) \ + FN(csum_diff, 28, ##ctx) \ + FN(skb_get_tunnel_opt, 29, ##ctx) \ + FN(skb_set_tunnel_opt, 30, ##ctx) \ + FN(skb_change_proto, 31, ##ctx) \ + FN(skb_change_type, 32, ##ctx) \ + FN(skb_under_cgroup, 33, ##ctx) \ + FN(get_hash_recalc, 34, ##ctx) \ + FN(get_current_task, 35, ##ctx) \ + FN(probe_write_user, 36, ##ctx) \ + FN(current_task_under_cgroup, 37, ##ctx) \ + FN(skb_change_tail, 38, ##ctx) \ + FN(skb_pull_data, 39, ##ctx) \ + FN(csum_update, 40, ##ctx) \ + FN(set_hash_invalid, 41, ##ctx) \ + FN(get_numa_node_id, 42, ##ctx) \ + FN(skb_change_head, 43, ##ctx) \ + FN(xdp_adjust_head, 44, ##ctx) \ + FN(probe_read_str, 45, ##ctx) \ + FN(get_socket_cookie, 46, ##ctx) \ + FN(get_socket_uid, 47, ##ctx) \ + FN(set_hash, 48, ##ctx) \ + FN(setsockopt, 49, ##ctx) \ + FN(skb_adjust_room, 50, ##ctx) \ + FN(redirect_map, 51, ##ctx) \ + FN(sk_redirect_map, 52, ##ctx) \ + FN(sock_map_update, 53, ##ctx) \ + FN(xdp_adjust_meta, 54, ##ctx) \ + FN(perf_event_read_value, 55, ##ctx) \ + FN(perf_prog_read_value, 56, ##ctx) \ + FN(getsockopt, 57, ##ctx) \ + FN(override_return, 58, ##ctx) \ + FN(sock_ops_cb_flags_set, 59, ##ctx) \ + FN(msg_redirect_map, 60, ##ctx) \ + FN(msg_apply_bytes, 61, ##ctx) \ + FN(msg_cork_bytes, 62, ##ctx) \ + FN(msg_pull_data, 63, ##ctx) \ + FN(bind, 64, ##ctx) \ + FN(xdp_adjust_tail, 65, ##ctx) \ + FN(skb_get_xfrm_state, 66, ##ctx) \ + FN(get_stack, 67, ##ctx) \ + FN(skb_load_bytes_relative, 68, ##ctx) \ + FN(fib_lookup, 69, ##ctx) \ + FN(sock_hash_update, 70, ##ctx) \ + FN(msg_redirect_hash, 71, ##ctx) \ + FN(sk_redirect_hash, 72, ##ctx) \ + FN(lwt_push_encap, 73, ##ctx) \ + FN(lwt_seg6_store_bytes, 74, ##ctx) \ + FN(lwt_seg6_adjust_srh, 75, ##ctx) \ + FN(lwt_seg6_action, 76, ##ctx) \ + FN(rc_repeat, 77, ##ctx) \ + FN(rc_keydown, 78, ##ctx) \ + FN(skb_cgroup_id, 79, ##ctx) \ + FN(get_current_cgroup_id, 80, ##ctx) \ + FN(get_local_storage, 81, ##ctx) \ + FN(sk_select_reuseport, 82, ##ctx) \ + FN(skb_ancestor_cgroup_id, 83, ##ctx) \ + FN(sk_lookup_tcp, 84, ##ctx) \ + FN(sk_lookup_udp, 85, ##ctx) \ + FN(sk_release, 86, ##ctx) \ + FN(map_push_elem, 87, ##ctx) \ + FN(map_pop_elem, 88, ##ctx) \ + FN(map_peek_elem, 89, ##ctx) \ + FN(msg_push_data, 90, ##ctx) \ + FN(msg_pop_data, 91, ##ctx) \ + FN(rc_pointer_rel, 92, ##ctx) \ + FN(spin_lock, 93, ##ctx) \ + FN(spin_unlock, 94, ##ctx) \ + FN(sk_fullsock, 95, ##ctx) \ + FN(tcp_sock, 96, ##ctx) \ + FN(skb_ecn_set_ce, 97, ##ctx) \ + FN(get_listener_sock, 98, ##ctx) \ + FN(skc_lookup_tcp, 99, ##ctx) \ + FN(tcp_check_syncookie, 100, ##ctx) \ + FN(sysctl_get_name, 101, ##ctx) \ + FN(sysctl_get_current_value, 102, ##ctx) \ + FN(sysctl_get_new_value, 103, ##ctx) \ + FN(sysctl_set_new_value, 104, ##ctx) \ + FN(strtol, 105, ##ctx) \ + FN(strtoul, 106, ##ctx) \ + FN(sk_storage_get, 107, ##ctx) \ + FN(sk_storage_delete, 108, ##ctx) \ + FN(send_signal, 109, ##ctx) \ + FN(tcp_gen_syncookie, 110, ##ctx) \ + FN(skb_output, 111, ##ctx) \ + FN(probe_read_user, 112, ##ctx) \ + FN(probe_read_kernel, 113, ##ctx) \ + FN(probe_read_user_str, 114, ##ctx) \ + FN(probe_read_kernel_str, 115, ##ctx) \ + FN(tcp_send_ack, 116, ##ctx) \ + FN(send_signal_thread, 117, ##ctx) \ + FN(jiffies64, 118, ##ctx) \ + FN(read_branch_records, 119, ##ctx) \ + FN(get_ns_current_pid_tgid, 120, ##ctx) \ + FN(xdp_output, 121, ##ctx) \ + FN(get_netns_cookie, 122, ##ctx) \ + FN(get_current_ancestor_cgroup_id, 123, ##ctx) \ + FN(sk_assign, 124, ##ctx) \ + FN(ktime_get_boot_ns, 125, ##ctx) \ + FN(seq_printf, 126, ##ctx) \ + FN(seq_write, 127, ##ctx) \ + FN(sk_cgroup_id, 128, ##ctx) \ + FN(sk_ancestor_cgroup_id, 129, ##ctx) \ + FN(ringbuf_output, 130, ##ctx) \ + FN(ringbuf_reserve, 131, ##ctx) \ + FN(ringbuf_submit, 132, ##ctx) \ + FN(ringbuf_discard, 133, ##ctx) \ + FN(ringbuf_query, 134, ##ctx) \ + FN(csum_level, 135, ##ctx) \ + FN(skc_to_tcp6_sock, 136, ##ctx) \ + FN(skc_to_tcp_sock, 137, ##ctx) \ + FN(skc_to_tcp_timewait_sock, 138, ##ctx) \ + FN(skc_to_tcp_request_sock, 139, ##ctx) \ + FN(skc_to_udp6_sock, 140, ##ctx) \ + FN(get_task_stack, 141, ##ctx) \ + FN(load_hdr_opt, 142, ##ctx) \ + FN(store_hdr_opt, 143, ##ctx) \ + FN(reserve_hdr_opt, 144, ##ctx) \ + FN(inode_storage_get, 145, ##ctx) \ + FN(inode_storage_delete, 146, ##ctx) \ + FN(d_path, 147, ##ctx) \ + FN(copy_from_user, 148, ##ctx) \ + FN(snprintf_btf, 149, ##ctx) \ + FN(seq_printf_btf, 150, ##ctx) \ + FN(skb_cgroup_classid, 151, ##ctx) \ + FN(redirect_neigh, 152, ##ctx) \ + FN(per_cpu_ptr, 153, ##ctx) \ + FN(this_cpu_ptr, 154, ##ctx) \ + FN(redirect_peer, 155, ##ctx) \ + FN(task_storage_get, 156, ##ctx) \ + FN(task_storage_delete, 157, ##ctx) \ + FN(get_current_task_btf, 158, ##ctx) \ + FN(bprm_opts_set, 159, ##ctx) \ + FN(ktime_get_coarse_ns, 160, ##ctx) \ + FN(ima_inode_hash, 161, ##ctx) \ + FN(sock_from_file, 162, ##ctx) \ + FN(check_mtu, 163, ##ctx) \ + FN(for_each_map_elem, 164, ##ctx) \ + FN(snprintf, 165, ##ctx) \ + FN(sys_bpf, 166, ##ctx) \ + FN(btf_find_by_name_kind, 167, ##ctx) \ + FN(sys_close, 168, ##ctx) \ + FN(timer_init, 169, ##ctx) \ + FN(timer_set_callback, 170, ##ctx) \ + FN(timer_start, 171, ##ctx) \ + FN(timer_cancel, 172, ##ctx) \ + FN(get_func_ip, 173, ##ctx) \ + FN(get_attach_cookie, 174, ##ctx) \ + FN(task_pt_regs, 175, ##ctx) \ + FN(get_branch_snapshot, 176, ##ctx) \ + FN(trace_vprintk, 177, ##ctx) \ + FN(skc_to_unix_sock, 178, ##ctx) \ + FN(kallsyms_lookup_name, 179, ##ctx) \ + FN(find_vma, 180, ##ctx) \ + FN(loop, 181, ##ctx) \ + FN(strncmp, 182, ##ctx) \ + FN(get_func_arg, 183, ##ctx) \ + FN(get_func_ret, 184, ##ctx) \ + FN(get_func_arg_cnt, 185, ##ctx) \ + FN(get_retval, 186, ##ctx) \ + FN(set_retval, 187, ##ctx) \ + FN(xdp_get_buff_len, 188, ##ctx) \ + FN(xdp_load_bytes, 189, ##ctx) \ + FN(xdp_store_bytes, 190, ##ctx) \ + FN(copy_from_user_task, 191, ##ctx) \ + FN(skb_set_tstamp, 192, ##ctx) \ + FN(ima_file_hash, 193, ##ctx) \ + FN(kptr_xchg, 194, ##ctx) \ + FN(map_lookup_percpu_elem, 195, ##ctx) \ + FN(skc_to_mptcp_sock, 196, ##ctx) \ + FN(dynptr_from_mem, 197, ##ctx) \ + FN(ringbuf_reserve_dynptr, 198, ##ctx) \ + FN(ringbuf_submit_dynptr, 199, ##ctx) \ + FN(ringbuf_discard_dynptr, 200, ##ctx) \ + FN(dynptr_read, 201, ##ctx) \ + FN(dynptr_write, 202, ##ctx) \ + FN(dynptr_data, 203, ##ctx) \ + FN(tcp_raw_gen_syncookie_ipv4, 204, ##ctx) \ + FN(tcp_raw_gen_syncookie_ipv6, 205, ##ctx) \ + FN(tcp_raw_check_syncookie_ipv4, 206, ##ctx) \ + FN(tcp_raw_check_syncookie_ipv6, 207, ##ctx) \ + FN(ktime_get_tai_ns, 208, ##ctx) \ + FN(user_ringbuf_drain, 209, ##ctx) \ /* */ +/* backwards-compatibility macros for users of __BPF_FUNC_MAPPER that don't + * know or care about integer value that is now passed as second argument + */ +#define __BPF_FUNC_MAPPER_APPLY(name, value, FN) FN(name), +#define __BPF_FUNC_MAPPER(FN) ___BPF_FUNC_MAPPER(__BPF_FUNC_MAPPER_APPLY, FN) + /* integer value in 'imm' field of BPF_CALL instruction selects which helper * function eBPF program intends to call */ -#define __BPF_ENUM_FN(x) BPF_FUNC_ ## x +#define __BPF_ENUM_FN(x, y) BPF_FUNC_ ## x = y, enum bpf_func_id { - __BPF_FUNC_MAPPER(__BPF_ENUM_FN) + ___BPF_FUNC_MAPPER(__BPF_ENUM_FN) __BPF_FUNC_MAX_ID, }; #undef __BPF_ENUM_FN From ce3e44a09dce74ca68fa56c23333378d936969b0 Mon Sep 17 00:00:00 2001 From: Andrii Nakryiko Date: Wed, 5 Oct 2022 21:24:52 -0700 Subject: [PATCH 0056/2002] scripts/bpf_doc.py: update logic to not assume sequential enum values Relax bpf_doc.py's expectation of all BPF_FUNC_xxx enumerators having sequential values increasing by one. Instead, only make sure that relative order of BPF helper descriptions in comments matches enumerators definitions order. Also additionally make sure that helper IDs are not duplicated. And also make sure that for cases when we have multiple descriptions for the same BPF helper (e.g., for bpf_get_socket_cookie()), all such descriptions are grouped together. Such checks should capture all the same (and more) issues in upstream UAPI headers, but also handle backported kernels correctly. Reported-by: Alexei Starovoitov Signed-off-by: Andrii Nakryiko Reviewed-by: Quentin Monnet Link: https://lore.kernel.org/r/20221006042452.2089843-2-andrii@kernel.org Signed-off-by: Alexei Starovoitov --- scripts/bpf_doc.py | 31 +++++++++++++++++++++++++------ 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/scripts/bpf_doc.py b/scripts/bpf_doc.py index 2fe07c9e3fe0..c0e6690be82a 100755 --- a/scripts/bpf_doc.py +++ b/scripts/bpf_doc.py @@ -97,6 +97,7 @@ class HeaderParser(object): self.desc_unique_helpers = set() self.define_unique_helpers = [] self.helper_enum_vals = {} + self.helper_enum_pos = {} self.desc_syscalls = [] self.enum_syscalls = [] @@ -264,42 +265,60 @@ class HeaderParser(object): # Searches for one FN(\w+) define or a backslash for newline p = re.compile('\s*FN\((\w+), (\d+), ##ctx\)|\\\\') fn_defines_str = '' + i = 0 while True: capture = p.match(self.line) if capture: fn_defines_str += self.line - self.helper_enum_vals[capture.expand(r'bpf_\1')] = int(capture[2]) + helper_name = capture.expand(r'bpf_\1') + self.helper_enum_vals[helper_name] = int(capture[2]) + self.helper_enum_pos[helper_name] = i + i += 1 else: break self.line = self.reader.readline() # Find the number of occurences of FN(\w+) self.define_unique_helpers = re.findall('FN\(\w+, \d+, ##ctx\)', fn_defines_str) - def assign_helper_values(self): + def validate_helpers(self): + last_helper = '' seen_helpers = set() + seen_enum_vals = set() + i = 0 for helper in self.helpers: proto = helper.proto_break_down() name = proto['name'] try: enum_val = self.helper_enum_vals[name] + enum_pos = self.helper_enum_pos[name] except KeyError: raise Exception("Helper %s is missing from enum bpf_func_id" % name) + if name in seen_helpers: + if last_helper != name: + raise Exception("Helper %s has multiple descriptions which are not grouped together" % name) + continue + # Enforce current practice of having the descriptions ordered # by enum value. + if enum_pos != i: + raise Exception("Helper %s (ID %d) comment order (#%d) must be aligned with its position (#%d) in enum bpf_func_id" % (name, enum_val, i + 1, enum_pos + 1)) + if enum_val in seen_enum_vals: + raise Exception("Helper %s has duplicated value %d" % (name, enum_val)) + seen_helpers.add(name) - desc_val = len(seen_helpers) - if desc_val != enum_val: - raise Exception("Helper %s comment order (#%d) must be aligned with its position (#%d) in enum bpf_func_id" % (name, desc_val, enum_val)) + last_helper = name + seen_enum_vals.add(enum_val) helper.enum_val = enum_val + i += 1 def run(self): self.parse_desc_syscall() self.parse_enum_syscall() self.parse_desc_helpers() self.parse_define_helpers() - self.assign_helper_values() + self.validate_helpers() self.reader.close() ############################################################################### From 8206e4e95230daeeba43c59fc7c39656883ecd62 Mon Sep 17 00:00:00 2001 From: Yonghong Song Date: Wed, 5 Oct 2022 22:34:29 -0700 Subject: [PATCH 0057/2002] selftests/bpf: Add selftest deny_namespace to s390x deny list BPF CI reported that selftest deny_namespace failed with s390x. test_unpriv_userns_create_no_bpf:PASS:no-bpf unpriv new user ns 0 nsec test_deny_namespace:PASS:skel load 0 nsec libbpf: prog 'test_userns_create': failed to attach: ERROR: strerror_r(-524)=22 libbpf: prog 'test_userns_create': failed to auto-attach: -524 test_deny_namespace:FAIL:attach unexpected error: -524 (errno 524) #57/1 deny_namespace/unpriv_userns_create_no_bpf:FAIL #57 deny_namespace:FAIL BPF program test_userns_create is a BPF LSM type program which is based on trampoline and s390x does not support s390x. Let add the test to x390x deny list to avoid this failure in BPF CI. Signed-off-by: Yonghong Song Acked-by: Jiri Olsa Link: https://lore.kernel.org/r/20221006053429.3549165-1-yhs@fb.com Signed-off-by: Alexei Starovoitov --- tools/testing/selftests/bpf/DENYLIST.s390x | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/testing/selftests/bpf/DENYLIST.s390x b/tools/testing/selftests/bpf/DENYLIST.s390x index 17e074eb42b8..0fb03b8047d5 100644 --- a/tools/testing/selftests/bpf/DENYLIST.s390x +++ b/tools/testing/selftests/bpf/DENYLIST.s390x @@ -75,3 +75,4 @@ user_ringbuf # failed to find kernel BTF type ID of lookup_key # JIT does not support calling kernel function (kfunc) verify_pkcs7_sig # JIT does not support calling kernel function (kfunc) kfunc_dynptr_param # JIT does not support calling kernel function (kfunc) +deny_namespace # failed to attach: ERROR: strerror_r(-524)=22 (trampoline) From 1d2d941bc140b34587b4c889699fb0f89d29937f Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Thu, 6 Oct 2022 10:31:06 +0200 Subject: [PATCH 0058/2002] selftests/bpf: Add missing bpf_iter_vma_offset__destroy call Adding missing bpf_iter_vma_offset__destroy call and using in-skeletin link pointer so we don't need extra bpf_link__destroy call. Fixes: b3e1331eb925 ("selftests/bpf: Test parameterized task BPF iterators.") Cc: Kui-Feng Lee Signed-off-by: Jiri Olsa Link: https://lore.kernel.org/r/20221006083106.117987-1-jolsa@kernel.org Signed-off-by: Martin KaFai Lau --- tools/testing/selftests/bpf/prog_tests/bpf_iter.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/tools/testing/selftests/bpf/prog_tests/bpf_iter.c b/tools/testing/selftests/bpf/prog_tests/bpf_iter.c index 3369c5ec3a17..ecde236047fe 100644 --- a/tools/testing/selftests/bpf/prog_tests/bpf_iter.c +++ b/tools/testing/selftests/bpf/prog_tests/bpf_iter.c @@ -1498,7 +1498,6 @@ static noinline int trigger_func(int arg) static void test_task_vma_offset_common(struct bpf_iter_attach_opts *opts, bool one_proc) { struct bpf_iter_vma_offset *skel; - struct bpf_link *link; char buf[16] = {}; int iter_fd, len; int pgsz, shift; @@ -1513,11 +1512,11 @@ static void test_task_vma_offset_common(struct bpf_iter_attach_opts *opts, bool ; skel->bss->page_shift = shift; - link = bpf_program__attach_iter(skel->progs.get_vma_offset, opts); - if (!ASSERT_OK_PTR(link, "attach_iter")) - return; + skel->links.get_vma_offset = bpf_program__attach_iter(skel->progs.get_vma_offset, opts); + if (!ASSERT_OK_PTR(skel->links.get_vma_offset, "attach_iter")) + goto exit; - iter_fd = bpf_iter_create(bpf_link__fd(link)); + iter_fd = bpf_iter_create(bpf_link__fd(skel->links.get_vma_offset)); if (!ASSERT_GT(iter_fd, 0, "create_iter")) goto exit; @@ -1535,7 +1534,7 @@ static void test_task_vma_offset_common(struct bpf_iter_attach_opts *opts, bool close(iter_fd); exit: - bpf_link__destroy(link); + bpf_iter_vma_offset__destroy(skel); } static void test_task_vma_offset(void) From 1d9e4c91db17c9bf6f94ac234a4d4f2bffd52b97 Mon Sep 17 00:00:00 2001 From: Benjamin Berg Date: Tue, 6 Sep 2022 19:02:04 +0200 Subject: [PATCH 0059/2002] wifi: mac80211: add pointer from link STA to STA While often not needed, this considerably simplifies going from a link to the STA. This helps in cases such as debugfs where a single pointer should allow accessing a specific link and the STA. Signed-off-by: Benjamin Berg Signed-off-by: Johannes Berg --- include/net/mac80211.h | 3 +++ net/mac80211/sta_info.c | 1 + 2 files changed, 4 insertions(+) diff --git a/include/net/mac80211.h b/include/net/mac80211.h index ac2bad57933f..7778a92d9582 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -2176,6 +2176,7 @@ struct ieee80211_sta_aggregates { * All link specific info for a STA link for a non MLD STA(single) * or a MLD STA(multiple entries) are stored here. * + * @sta: reference to owning STA * @addr: MAC address of the Link STA. For non-MLO STA this is same as the addr * in ieee80211_sta. For MLO Link STA this addr can be same or different * from addr in ieee80211_sta (representing MLD STA addr) @@ -2196,6 +2197,8 @@ struct ieee80211_sta_aggregates { * */ struct ieee80211_link_sta { + struct ieee80211_sta *sta; + u8 addr[ETH_ALEN]; u8 link_id; enum ieee80211_smps_mode smps_mode; diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index cebfd148bb40..71b1488bd390 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -511,6 +511,7 @@ static void sta_info_add_link(struct sta_info *sta, link_info->sta = sta; link_info->link_id = link_id; link_info->pub = link_sta; + link_info->pub->sta = &sta->sta; link_sta->link_id = link_id; rcu_assign_pointer(sta->link[link_id], link_info); rcu_assign_pointer(sta->sta.link[link_id], link_sta); From d2caad527c191563116809990081ab4fc0dafdb6 Mon Sep 17 00:00:00 2001 From: Benjamin Berg Date: Tue, 6 Sep 2022 14:26:52 +0200 Subject: [PATCH 0060/2002] wifi: mac80211: add API to show the link STAs in debugfs Create debugfs data per-link. For drivers, there is a new operation link_sta_add_debugfs which will always be called. For non-MLO, the station directory will be used directly rather than creating a corresponding subdirectory. As such, non-MLO drivers can simply continue to create the data from sta_debugfs_add. Signed-off-by: Benjamin Berg [add missing inlines if !CONFIG_MAC80211_DEBUGFS] Signed-off-by: Johannes Berg --- include/net/mac80211.h | 11 ++++ net/mac80211/debugfs_sta.c | 127 ++++++++++++++++++++++++++++++------- net/mac80211/debugfs_sta.h | 12 ++++ net/mac80211/driver-ops.c | 27 +++++++- net/mac80211/driver-ops.h | 16 +++++ net/mac80211/sta_info.c | 25 ++++++++ net/mac80211/sta_info.h | 5 ++ 7 files changed, 200 insertions(+), 23 deletions(-) diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 7778a92d9582..c413050ec8dd 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -3790,6 +3790,13 @@ struct ieee80211_prep_tx_info { * should be within a CONFIG_MAC80211_DEBUGFS conditional. This * callback can sleep. * + * @link_sta_add_debugfs: Drivers can use this callback to add debugfs files + * when a link is added to a mac80211 station. This callback + * should be within a CPTCFG_MAC80211_DEBUGFS conditional. This + * callback can sleep. + * For non-MLO the callback will be called once for the deflink with the + * station's directory rather than a separate subdirectory. + * * @sta_notify: Notifies low level driver about power state transition of an * associated station, AP, IBSS/WDS/mesh peer etc. For a VIF operating * in AP mode, this callback will not be called when the flag @@ -4260,6 +4267,10 @@ struct ieee80211_ops { struct ieee80211_vif *vif, struct ieee80211_sta *sta, struct dentry *dir); + void (*link_sta_add_debugfs)(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_link_sta *link_sta, + struct dentry *dir); #endif void (*sta_notify)(struct ieee80211_hw *hw, struct ieee80211_vif *vif, enum sta_notify_cmd, struct ieee80211_sta *sta); diff --git a/net/mac80211/debugfs_sta.c b/net/mac80211/debugfs_sta.c index d3397c1248d3..68c07d4b95a5 100644 --- a/net/mac80211/debugfs_sta.c +++ b/net/mac80211/debugfs_sta.c @@ -5,7 +5,7 @@ * Copyright 2007 Johannes Berg * Copyright 2013-2014 Intel Mobile Communications GmbH * Copyright(c) 2016 Intel Deutschland GmbH - * Copyright (C) 2018 - 2021 Intel Corporation + * Copyright (C) 2018 - 2022 Intel Corporation */ #include @@ -435,8 +435,16 @@ static ssize_t sta_agg_status_write(struct file *file, const char __user *userbu } STA_OPS_RW(agg_status); -static ssize_t sta_ht_capa_read(struct file *file, char __user *userbuf, - size_t count, loff_t *ppos) +/* link sta attributes */ +#define LINK_STA_OPS(name) \ +static const struct file_operations link_sta_ ##name## _ops = { \ + .read = link_sta_##name##_read, \ + .open = simple_open, \ + .llseek = generic_file_llseek, \ +} + +static ssize_t link_sta_ht_capa_read(struct file *file, char __user *userbuf, + size_t count, loff_t *ppos) { #define PRINT_HT_CAP(_cond, _str) \ do { \ @@ -446,8 +454,8 @@ static ssize_t sta_ht_capa_read(struct file *file, char __user *userbuf, char *buf, *p; int i; ssize_t bufsz = 512; - struct sta_info *sta = file->private_data; - struct ieee80211_sta_ht_cap *htc = &sta->sta.deflink.ht_cap; + struct link_sta_info *link_sta = file->private_data; + struct ieee80211_sta_ht_cap *htc = &link_sta->pub->ht_cap; ssize_t ret; buf = kzalloc(bufsz, GFP_KERNEL); @@ -524,14 +532,14 @@ static ssize_t sta_ht_capa_read(struct file *file, char __user *userbuf, kfree(buf); return ret; } -STA_OPS(ht_capa); +LINK_STA_OPS(ht_capa); -static ssize_t sta_vht_capa_read(struct file *file, char __user *userbuf, - size_t count, loff_t *ppos) +static ssize_t link_sta_vht_capa_read(struct file *file, char __user *userbuf, + size_t count, loff_t *ppos) { char *buf, *p; - struct sta_info *sta = file->private_data; - struct ieee80211_sta_vht_cap *vhtc = &sta->sta.deflink.vht_cap; + struct link_sta_info *link_sta = file->private_data; + struct ieee80211_sta_vht_cap *vhtc = &link_sta->pub->vht_cap; ssize_t ret; ssize_t bufsz = 512; @@ -638,15 +646,15 @@ static ssize_t sta_vht_capa_read(struct file *file, char __user *userbuf, kfree(buf); return ret; } -STA_OPS(vht_capa); +LINK_STA_OPS(vht_capa); -static ssize_t sta_he_capa_read(struct file *file, char __user *userbuf, - size_t count, loff_t *ppos) +static ssize_t link_sta_he_capa_read(struct file *file, char __user *userbuf, + size_t count, loff_t *ppos) { char *buf, *p; size_t buf_sz = PAGE_SIZE; - struct sta_info *sta = file->private_data; - struct ieee80211_sta_he_cap *hec = &sta->sta.deflink.he_cap; + struct link_sta_info *link_sta = file->private_data; + struct ieee80211_sta_he_cap *hec = &link_sta->pub->he_cap; struct ieee80211_he_mcs_nss_supp *nss = &hec->he_mcs_nss_supp; u8 ppe_size; u8 *cap; @@ -1011,7 +1019,7 @@ out: kfree(buf); return ret; } -STA_OPS(he_capa); +LINK_STA_OPS(he_capa); #define DEBUGFS_ADD(name) \ debugfs_create_file(#name, 0400, \ @@ -1048,12 +1056,7 @@ void ieee80211_sta_debugfs_add(struct sta_info *sta) DEBUGFS_ADD(num_ps_buf_frames); DEBUGFS_ADD(last_seq_ctrl); DEBUGFS_ADD(agg_status); - DEBUGFS_ADD(ht_capa); - DEBUGFS_ADD(vht_capa); - DEBUGFS_ADD(he_capa); - - DEBUGFS_ADD_COUNTER(rx_duplicates, deflink.rx_stats.num_duplicates); - DEBUGFS_ADD_COUNTER(rx_fragments, deflink.rx_stats.fragments); + /* FIXME: Kept here as the statistics are only done on the deflink */ DEBUGFS_ADD_COUNTER(tx_filtered, deflink.status_stats.filtered); if (local->ops->wake_tx_queue) { @@ -1076,3 +1079,83 @@ void ieee80211_sta_debugfs_remove(struct sta_info *sta) debugfs_remove_recursive(sta->debugfs_dir); sta->debugfs_dir = NULL; } + +#undef DEBUGFS_ADD +#undef DEBUGFS_ADD_COUNTER + +#define DEBUGFS_ADD(name) \ + debugfs_create_file(#name, 0400, \ + link_sta->debugfs_dir, link_sta, &link_sta_ ##name## _ops) +#define DEBUGFS_ADD_COUNTER(name, field) \ + debugfs_create_ulong(#name, 0400, link_sta->debugfs_dir, &link_sta->field) + +void ieee80211_link_sta_debugfs_add(struct link_sta_info *link_sta) +{ + if (WARN_ON(!link_sta->sta->debugfs_dir)) + return; + + /* For non-MLO, leave the files in the main directory. */ + if (link_sta->sta->sta.valid_links) { + char link_dir_name[10]; + + snprintf(link_dir_name, sizeof(link_dir_name), + "link-%d", link_sta->link_id); + + link_sta->debugfs_dir = + debugfs_create_dir(link_dir_name, + link_sta->sta->debugfs_dir); + } else { + if (WARN_ON(link_sta != &link_sta->sta->deflink)) + return; + + link_sta->debugfs_dir = link_sta->sta->debugfs_dir; + } + + DEBUGFS_ADD(ht_capa); + DEBUGFS_ADD(vht_capa); + DEBUGFS_ADD(he_capa); + + DEBUGFS_ADD_COUNTER(rx_duplicates, rx_stats.num_duplicates); + DEBUGFS_ADD_COUNTER(rx_fragments, rx_stats.fragments); +} + +void ieee80211_link_sta_debugfs_remove(struct link_sta_info *link_sta) +{ + if (!link_sta->debugfs_dir || !link_sta->sta->debugfs_dir) { + link_sta->debugfs_dir = NULL; + return; + } + + if (link_sta->debugfs_dir == link_sta->sta->debugfs_dir) { + WARN_ON(link_sta != &link_sta->sta->deflink); + link_sta->sta->debugfs_dir = NULL; + return; + } + + debugfs_remove_recursive(link_sta->debugfs_dir); + link_sta->debugfs_dir = NULL; +} + +void ieee80211_link_sta_debugfs_drv_add(struct link_sta_info *link_sta) +{ + if (WARN_ON(!link_sta->debugfs_dir)) + return; + + drv_link_sta_add_debugfs(link_sta->sta->local, link_sta->sta->sdata, + link_sta->pub, link_sta->debugfs_dir); +} + +void ieee80211_link_sta_debugfs_drv_remove(struct link_sta_info *link_sta) +{ + if (!link_sta->debugfs_dir) + return; + + if (WARN_ON(link_sta->debugfs_dir == link_sta->sta->debugfs_dir)) + return; + + /* Recreate the directory excluding the driver data */ + debugfs_remove_recursive(link_sta->debugfs_dir); + link_sta->debugfs_dir = NULL; + + ieee80211_link_sta_debugfs_add(link_sta); +} diff --git a/net/mac80211/debugfs_sta.h b/net/mac80211/debugfs_sta.h index d2e7c27ad6d1..cde8148bdb18 100644 --- a/net/mac80211/debugfs_sta.h +++ b/net/mac80211/debugfs_sta.h @@ -7,9 +7,21 @@ #ifdef CONFIG_MAC80211_DEBUGFS void ieee80211_sta_debugfs_add(struct sta_info *sta); void ieee80211_sta_debugfs_remove(struct sta_info *sta); + +void ieee80211_link_sta_debugfs_add(struct link_sta_info *link_sta); +void ieee80211_link_sta_debugfs_remove(struct link_sta_info *link_sta); + +void ieee80211_link_sta_debugfs_drv_add(struct link_sta_info *link_sta); +void ieee80211_link_sta_debugfs_drv_remove(struct link_sta_info *link_sta); #else static inline void ieee80211_sta_debugfs_add(struct sta_info *sta) {} static inline void ieee80211_sta_debugfs_remove(struct sta_info *sta) {} + +static inline void ieee80211_link_sta_debugfs_add(struct link_sta_info *link_sta) {} +static inline void ieee80211_link_sta_debugfs_remove(struct link_sta_info *link_sta) {} + +static inline void ieee80211_link_sta_debugfs_drv_add(struct link_sta_info *link_sta) {} +static inline void ieee80211_link_sta_debugfs_drv_remove(struct link_sta_info *link_sta) {} #endif #endif /* __MAC80211_DEBUGFS_STA_H */ diff --git a/net/mac80211/driver-ops.c b/net/mac80211/driver-ops.c index 5392ffa18270..d737db4e07e2 100644 --- a/net/mac80211/driver-ops.c +++ b/net/mac80211/driver-ops.c @@ -7,6 +7,7 @@ #include "ieee80211_i.h" #include "trace.h" #include "driver-ops.h" +#include "debugfs_sta.h" int drv_start(struct ieee80211_local *local) { @@ -497,6 +498,11 @@ int drv_change_sta_links(struct ieee80211_local *local, struct ieee80211_sta *sta, u16 old_links, u16 new_links) { + struct sta_info *info = container_of(sta, struct sta_info, sta); + struct link_sta_info *link_sta; + unsigned long links_to_add; + unsigned long links_to_rem; + unsigned int link_id; int ret = -EOPNOTSUPP; might_sleep(); @@ -510,11 +516,30 @@ int drv_change_sta_links(struct ieee80211_local *local, if (old_links == new_links) return 0; + links_to_add = ~old_links & new_links; + links_to_rem = old_links & ~new_links; + + for_each_set_bit(link_id, &links_to_rem, IEEE80211_MLD_MAX_NUM_LINKS) { + link_sta = rcu_dereference_protected(info->link[link_id], + lockdep_is_held(&local->sta_mtx)); + + ieee80211_link_sta_debugfs_drv_remove(link_sta); + } + trace_drv_change_sta_links(local, sdata, sta, old_links, new_links); if (local->ops->change_sta_links) ret = local->ops->change_sta_links(&local->hw, &sdata->vif, sta, old_links, new_links); trace_drv_return_int(local, ret); - return ret; + if (ret) + return ret; + + for_each_set_bit(link_id, &links_to_add, IEEE80211_MLD_MAX_NUM_LINKS) { + link_sta = rcu_dereference_protected(info->link[link_id], + lockdep_is_held(&local->sta_mtx)); + ieee80211_link_sta_debugfs_drv_add(link_sta); + } + + return 0; } diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h index 81e40b0a3b16..809bad53e15b 100644 --- a/net/mac80211/driver-ops.h +++ b/net/mac80211/driver-ops.h @@ -480,6 +480,22 @@ static inline void drv_sta_add_debugfs(struct ieee80211_local *local, local->ops->sta_add_debugfs(&local->hw, &sdata->vif, sta, dir); } + +static inline void drv_link_sta_add_debugfs(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata, + struct ieee80211_link_sta *link_sta, + struct dentry *dir) +{ + might_sleep(); + + sdata = get_bss_sdata(sdata); + if (!check_sdata_in_driver(sdata)) + return; + + if (local->ops->link_sta_add_debugfs) + local->ops->link_sta_add_debugfs(&local->hw, &sdata->vif, + link_sta, dir); +} #endif static inline void drv_sta_pre_rcu_remove(struct ieee80211_local *local, diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index 71b1488bd390..e6beaea4075e 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -366,6 +366,9 @@ static void sta_remove_link(struct sta_info *sta, unsigned int link_id, if (unhash) link_sta_info_hash_del(sta->local, link_sta); + if (test_sta_flag(sta, WLAN_STA_INSERTED)) + ieee80211_link_sta_debugfs_remove(link_sta); + if (link_sta != &sta->deflink) alloc = container_of(link_sta, typeof(*alloc), info); @@ -875,6 +878,26 @@ static int sta_info_insert_finish(struct sta_info *sta) __acquires(RCU) ieee80211_sta_debugfs_add(sta); rate_control_add_sta_debugfs(sta); + if (sta->sta.valid_links) { + int i; + + for (i = 0; i < ARRAY_SIZE(sta->link); i++) { + struct link_sta_info *link_sta; + + link_sta = rcu_dereference_protected(sta->link[i], + lockdep_is_held(&local->sta_mtx)); + + if (!link_sta) + continue; + + ieee80211_link_sta_debugfs_add(link_sta); + if (sdata->vif.active_links & BIT(i)) + ieee80211_link_sta_debugfs_drv_add(link_sta); + } + } else { + ieee80211_link_sta_debugfs_add(&sta->deflink); + ieee80211_link_sta_debugfs_drv_add(&sta->deflink); + } sinfo->generation = local->sta_generation; cfg80211_new_sta(sdata->dev, sta->sta.addr, sinfo, GFP_KERNEL); @@ -2824,6 +2847,8 @@ int ieee80211_sta_allocate_link(struct sta_info *sta, unsigned int link_id) sta_info_add_link(sta, link_id, &alloc->info, &alloc->sta); + ieee80211_link_sta_debugfs_add(&alloc->info); + return 0; } diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h index 2517ea714dc4..6e672bf9c79d 100644 --- a/net/mac80211/sta_info.h +++ b/net/mac80211/sta_info.h @@ -513,6 +513,7 @@ struct ieee80211_fragment_cache { * @status_stats.avg_ack_signal: average ACK signal * @cur_max_bandwidth: maximum bandwidth to use for TX to the station, * taken from HT/VHT capabilities or VHT operating mode notification + * @debugfs_dir: debug filesystem directory dentry * @pub: public (driver visible) link STA data * TODO Move other link params from sta_info as required for MLD operation */ @@ -560,6 +561,10 @@ struct link_sta_info { enum ieee80211_sta_rx_bandwidth cur_max_bandwidth; +#ifdef CONFIG_MAC80211_DEBUGFS + struct dentry *debugfs_dir; +#endif + struct ieee80211_link_sta *pub; }; From c2b6b1c13e17cdef76e01349f4aa6f035cfad063 Mon Sep 17 00:00:00 2001 From: Benjamin Berg Date: Tue, 6 Sep 2022 17:24:10 +0200 Subject: [PATCH 0061/2002] wifi: mac80211: include link address in debugfs Add the link address to the per-link information, but only if we are using MLO. Signed-off-by: Benjamin Berg Signed-off-by: Johannes Berg --- net/mac80211/debugfs_sta.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/net/mac80211/debugfs_sta.c b/net/mac80211/debugfs_sta.c index 68c07d4b95a5..0d8ff0303028 100644 --- a/net/mac80211/debugfs_sta.c +++ b/net/mac80211/debugfs_sta.c @@ -443,6 +443,19 @@ static const struct file_operations link_sta_ ##name## _ops = { \ .llseek = generic_file_llseek, \ } +static ssize_t link_sta_addr_read(struct file *file, char __user *userbuf, + size_t count, loff_t *ppos) +{ + struct link_sta_info *link_sta = file->private_data; + u8 mac[3 * ETH_ALEN + 1]; + + snprintf(mac, sizeof(mac), "%pM\n", link_sta->pub->addr); + + return simple_read_from_buffer(userbuf, count, ppos, mac, 3 * ETH_ALEN); +} + +LINK_STA_OPS(addr); + static ssize_t link_sta_ht_capa_read(struct file *file, char __user *userbuf, size_t count, loff_t *ppos) { @@ -1104,6 +1117,8 @@ void ieee80211_link_sta_debugfs_add(struct link_sta_info *link_sta) link_sta->debugfs_dir = debugfs_create_dir(link_dir_name, link_sta->sta->debugfs_dir); + + DEBUGFS_ADD(addr); } else { if (WARN_ON(link_sta != &link_sta->sta->deflink)) return; From 9b41a9d7dca0159723172a47097b3f2352e37e44 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 5 Oct 2022 13:52:26 +0200 Subject: [PATCH 0062/2002] wifi: mac80211: recalc station aggregate data during link switch During link switching, the active links change, so we need to recalculate the aggregate data in the stations. Signed-off-by: Johannes Berg --- net/mac80211/link.c | 17 +++++++++++++++++ net/mac80211/sta_info.c | 33 ++++++++++++++++++++++++--------- net/mac80211/sta_info.h | 2 ++ 3 files changed, 43 insertions(+), 9 deletions(-) diff --git a/net/mac80211/link.c b/net/mac80211/link.c index e309708abae8..d1f5a9f7c647 100644 --- a/net/mac80211/link.c +++ b/net/mac80211/link.c @@ -357,6 +357,11 @@ static int _ieee80211_set_active_links(struct ieee80211_sub_if_data *sdata, list_for_each_entry(sta, &local->sta_list, list) { if (sdata != sta->sdata) continue; + + /* this is very temporary, but do it anyway */ + __ieee80211_sta_recalc_aggregates(sta, + old_active | active_links); + ret = drv_change_sta_links(local, sdata, &sta->sta, old_active, old_active | active_links); @@ -369,10 +374,22 @@ static int _ieee80211_set_active_links(struct ieee80211_sub_if_data *sdata, list_for_each_entry(sta, &local->sta_list, list) { if (sdata != sta->sdata) continue; + + __ieee80211_sta_recalc_aggregates(sta, active_links); + ret = drv_change_sta_links(local, sdata, &sta->sta, old_active | active_links, active_links); WARN_ON_ONCE(ret); + + /* + * Do it again, just in case - the driver might very + * well have called ieee80211_sta_recalc_aggregates() + * from there when filling in the new links, which + * would set it wrong since the vif's active links are + * not switched yet... + */ + __ieee80211_sta_recalc_aggregates(sta, active_links); } for_each_set_bit(link_id, &add, IEEE80211_MLD_MAX_NUM_LINKS) { diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index e6beaea4075e..2bb6a71c72ef 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -2151,22 +2151,30 @@ void ieee80211_sta_register_airtime(struct ieee80211_sta *pubsta, u8 tid, } EXPORT_SYMBOL(ieee80211_sta_register_airtime); -void ieee80211_sta_recalc_aggregates(struct ieee80211_sta *pubsta) +void __ieee80211_sta_recalc_aggregates(struct sta_info *sta, u16 active_links) { - struct sta_info *sta = container_of(pubsta, struct sta_info, sta); - struct ieee80211_link_sta *link_sta; - int link_id, i; bool first = true; + int link_id; - if (!pubsta->valid_links || !pubsta->mlo) { - pubsta->cur = &pubsta->deflink.agg; + if (!sta->sta.valid_links || !sta->sta.mlo) { + sta->sta.cur = &sta->sta.deflink.agg; return; } rcu_read_lock(); - for_each_sta_active_link(&sta->sdata->vif, pubsta, link_sta, link_id) { + for (link_id = 0; link_id < ARRAY_SIZE((sta)->link); link_id++) { + struct ieee80211_link_sta *link_sta; + int i; + + if (!(active_links & BIT(link_id))) + continue; + + link_sta = rcu_dereference(sta->sta.link[link_id]); + if (!link_sta) + continue; + if (first) { - sta->cur = pubsta->deflink.agg; + sta->cur = sta->sta.deflink.agg; first = false; continue; } @@ -2185,7 +2193,14 @@ void ieee80211_sta_recalc_aggregates(struct ieee80211_sta *pubsta) } rcu_read_unlock(); - pubsta->cur = &sta->cur; + sta->sta.cur = &sta->cur; +} + +void ieee80211_sta_recalc_aggregates(struct ieee80211_sta *pubsta) +{ + struct sta_info *sta = container_of(pubsta, struct sta_info, sta); + + __ieee80211_sta_recalc_aggregates(sta, sta->sdata->vif.active_links); } EXPORT_SYMBOL(ieee80211_sta_recalc_aggregates); diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h index 6e672bf9c79d..69820b551668 100644 --- a/net/mac80211/sta_info.h +++ b/net/mac80211/sta_info.h @@ -927,6 +927,8 @@ void ieee80211_sta_set_max_amsdu_subframes(struct sta_info *sta, const u8 *ext_capab, unsigned int ext_capab_len); +void __ieee80211_sta_recalc_aggregates(struct sta_info *sta, u16 active_links); + enum sta_stats_type { STA_STATS_RATE_TYPE_INVALID = 0, STA_STATS_RATE_TYPE_LEGACY, From 53ad07e9823bca10c26e71d662b58c3e80e8ff2a Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 6 Sep 2022 11:27:57 +0200 Subject: [PATCH 0063/2002] wifi: cfg80211: support reporting failed links For assoc and connect result APIs, support reporting failed links; they should still come with the BSS pointer in the case of assoc, so they're released correctly. In the case of connect result, this is optional. Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 7 +++++++ net/wireless/mlme.c | 4 ++++ net/wireless/nl80211.c | 5 ++++- net/wireless/sme.c | 14 ++++++++++++++ 4 files changed, 29 insertions(+), 1 deletion(-) diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index e09ff87146c1..4d35a4234417 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -6933,6 +6933,8 @@ void cfg80211_auth_timeout(struct net_device *dev, const u8 *addr); * @ap_mld_addr: AP MLD address (in case of MLO) * @links: per-link information indexed by link ID, use links[0] for * non-MLO connections + * @links.status: Set this (along with a BSS pointer) for links that + * were rejected by the AP. */ struct cfg80211_rx_assoc_resp { const u8 *buf; @@ -6944,6 +6946,7 @@ struct cfg80211_rx_assoc_resp { struct { const u8 *addr; struct cfg80211_bss *bss; + u16 status; } links[IEEE80211_MLD_MAX_NUM_LINKS]; }; @@ -7454,6 +7457,9 @@ struct cfg80211_fils_resp_params { * if the bss is expired during the connection, esp. for those drivers * implementing connect op. Only one parameter among @bssid and @bss needs * to be specified. + * @links.status: per-link status code, to report a status code that's not + * %WLAN_STATUS_SUCCESS for a given link, it must also be in the + * @valid_links bitmap and may have a BSS pointer (which is then released) */ struct cfg80211_connect_resp_params { int status; @@ -7470,6 +7476,7 @@ struct cfg80211_connect_resp_params { const u8 *addr; const u8 *bssid; struct cfg80211_bss *bss; + u16 status; } links[IEEE80211_MLD_MAX_NUM_LINKS]; }; diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c index 581df7f4c524..58e1fb18f85a 100644 --- a/net/wireless/mlme.c +++ b/net/wireless/mlme.c @@ -42,6 +42,10 @@ void cfg80211_rx_assoc_resp(struct net_device *dev, unsigned int link_id; for (link_id = 0; link_id < ARRAY_SIZE(data->links); link_id++) { + cr.links[link_id].status = data->links[link_id].status; + WARN_ON_ONCE(cr.links[link_id].status != WLAN_STATUS_SUCCESS && + (!cr.ap_mld_addr || !cr.links[link_id].bss)); + cr.links[link_id].bss = data->links[link_id].bss; if (!cr.links[link_id].bss) continue; diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 8ff8b1c040f0..ad7393cd3d18 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -17745,6 +17745,7 @@ void nl80211_send_connect_result(struct cfg80211_registered_device *rdev, link_info_size += (cr->links[link].bssid || cr->links[link].bss) ? nla_total_size(ETH_ALEN) : 0; + link_info_size += nla_total_size(sizeof(u16)); } } @@ -17813,7 +17814,9 @@ void nl80211_send_connect_result(struct cfg80211_registered_device *rdev, nla_put(msg, NL80211_ATTR_BSSID, ETH_ALEN, bssid)) || (cr->links[link].addr && nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, - cr->links[link].addr))) + cr->links[link].addr)) || + nla_put_u16(msg, NL80211_ATTR_STATUS_CODE, + cr->links[link].status)) goto nla_put_failure; nla_nest_end(msg, nested_mlo_links); diff --git a/net/wireless/sme.c b/net/wireless/sme.c index d513536617bd..f94497e9db43 100644 --- a/net/wireless/sme.c +++ b/net/wireless/sme.c @@ -793,6 +793,10 @@ void __cfg80211_connect_result(struct net_device *dev, } for_each_valid_link(cr, link) { + /* don't do extra lookups for failures */ + if (cr->links[link].status != WLAN_STATUS_SUCCESS) + continue; + if (cr->links[link].bss) continue; @@ -829,6 +833,16 @@ void __cfg80211_connect_result(struct net_device *dev, } memset(wdev->links, 0, sizeof(wdev->links)); + for_each_valid_link(cr, link) { + if (cr->links[link].status == WLAN_STATUS_SUCCESS) + continue; + cr->valid_links &= ~BIT(link); + /* don't require bss pointer for failed links */ + if (!cr->links[link].bss) + continue; + cfg80211_unhold_bss(bss_from_pub(cr->links[link].bss)); + cfg80211_put_bss(wdev->wiphy, cr->links[link].bss); + } wdev->valid_links = cr->valid_links; for_each_valid_link(cr, link) wdev->links[link].client.current_bss = From 7b6f08771bf6045c32a2a1e18201c1aeeb78d72a Mon Sep 17 00:00:00 2001 From: Ilan Peer Date: Wed, 7 Sep 2022 10:34:30 +0300 Subject: [PATCH 0064/2002] wifi: ieee80211: Support validating ML station profile length Add a function to validate EHT Multi-Link per station profile length. Signed-off-by: Ilan Peer Signed-off-by: Johannes Berg --- include/linux/ieee80211.h | 40 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h index 79690938d9a2..bdf668f9dace 100644 --- a/include/linux/ieee80211.h +++ b/include/linux/ieee80211.h @@ -4685,6 +4685,46 @@ struct ieee80211_mle_per_sta_profile { u8 variable[]; } __packed; +/** + * ieee80211_mle_sta_prof_size_ok - validate multi-link element sta profile size + * @data: pointer to the sub element data + * @len: length of the containing sub element + */ +static inline bool ieee80211_mle_sta_prof_size_ok(const u8 *data, size_t len) +{ + const struct ieee80211_mle_per_sta_profile *prof = (const void *)data; + u16 control; + u8 fixed = sizeof(*prof); + u8 info_len = 1; + + if (len < fixed) + return false; + + control = le16_to_cpu(prof->control); + + if (control & IEEE80211_MLE_STA_CONTROL_STA_MAC_ADDR_PRESENT) + info_len += 6; + if (control & IEEE80211_MLE_STA_CONTROL_BEACON_INT_PRESENT) + info_len += 2; + if (control & IEEE80211_MLE_STA_CONTROL_TSF_OFFS_PRESENT) + info_len += 8; + if (control & IEEE80211_MLE_STA_CONTROL_DTIM_INFO_PRESENT) + info_len += 2; + if (control & IEEE80211_MLE_STA_CONTROL_BSS_PARAM_CHANGE_CNT_PRESENT) + info_len += 1; + + if (control & IEEE80211_MLE_STA_CONTROL_COMPLETE_PROFILE && + control & IEEE80211_MLE_STA_CONTROL_NSTR_BITMAP_SIZE) { + if (control & IEEE80211_MLE_STA_CONTROL_NSTR_BITMAP_SIZE) + info_len += 2; + else + info_len += 1; + } + + return prof->sta_info_len >= info_len && + fixed + prof->sta_info_len <= len; +} + #define for_each_mle_subelement(_elem, _data, _len) \ if (ieee80211_mle_size_ok(_data, _len)) \ for_each_element(_elem, \ From 1403b109c9a5244dc6ab79154f04eecc209ef3d2 Mon Sep 17 00:00:00 2001 From: Ilan Peer Date: Wed, 7 Sep 2022 17:23:09 +0300 Subject: [PATCH 0065/2002] wifi: cfg80211/mac80211: Fix ML element common size calculation The common size is part of the length in the data so don't add it again. Signed-off-by: Ilan Peer Signed-off-by: Johannes Berg --- include/linux/ieee80211.h | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h index bdf668f9dace..442b13333da8 100644 --- a/include/linux/ieee80211.h +++ b/include/linux/ieee80211.h @@ -4573,18 +4573,17 @@ static inline u8 ieee80211_mle_common_size(const u8 *data) switch (u16_get_bits(control, IEEE80211_ML_CONTROL_TYPE)) { case IEEE80211_ML_CONTROL_TYPE_BASIC: - common += sizeof(struct ieee80211_mle_basic_common_info); - break; case IEEE80211_ML_CONTROL_TYPE_PREQ: - common += sizeof(struct ieee80211_mle_preq_common_info); + case IEEE80211_ML_CONTROL_TYPE_TDLS: + /* + * The length is the first octet pointed by mle->variable so no + * need to add anything + */ break; case IEEE80211_ML_CONTROL_TYPE_RECONF: if (control & IEEE80211_MLC_RECONF_PRES_MLD_MAC_ADDR) common += ETH_ALEN; return common; - case IEEE80211_ML_CONTROL_TYPE_TDLS: - common += sizeof(struct ieee80211_mle_tdls_common_info); - break; case IEEE80211_ML_CONTROL_TYPE_PRIO_ACCESS: if (control & IEEE80211_MLC_PRIO_ACCESS_PRES_AP_MLD_MAC_ADDR) common += ETH_ALEN; From fb99c7d4d6d0fb4fe5a953e0c5f6c37a5b796b98 Mon Sep 17 00:00:00 2001 From: Ilan Peer Date: Sun, 11 Sep 2022 16:55:12 +0300 Subject: [PATCH 0066/2002] wifi: cfg80211/mac80211: Fix ML element common size validation The Multi-Link element can be fragmented, thus its size can exceed 254. Thus, modify ieee80211_mle_size_ok() to use 'size_t len' instead of 'u8 len'. Signed-off-by: Ilan Peer Signed-off-by: Johannes Berg --- include/linux/ieee80211.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h index 442b13333da8..b935a85b2f44 100644 --- a/include/linux/ieee80211.h +++ b/include/linux/ieee80211.h @@ -4601,7 +4601,7 @@ static inline u8 ieee80211_mle_common_size(const u8 *data) * @data: pointer to the element data * @len: length of the containing element */ -static inline bool ieee80211_mle_size_ok(const u8 *data, u8 len) +static inline bool ieee80211_mle_size_ok(const u8 *data, size_t len) { const struct ieee80211_multi_link_elem *mle = (const void *)data; u8 fixed = sizeof(*mle); From 45ebac4f059b92906e7e86dd1a780739f883857c Mon Sep 17 00:00:00 2001 From: Ilan Peer Date: Tue, 6 Sep 2022 11:48:56 +0300 Subject: [PATCH 0067/2002] wifi: mac80211: Parse station profile from association response When processing an association response frame for a Multi-Link connection, extract the per station profile for each additional link, and use it for parsing the link elements. As the Multi-Link element might be fragmented, add support for reassembling a fragmented element. To simplify memory management logic, extend 'struct ieee802_11_elems' to hold a scratch buffer, which is used for the defragmentation. Once an element is reconstructed in the scratch area, point the corresponding element pointer to it. Currently only defragmentation of Multi-Link element and the contained per-STA profile subelement is supported. Signed-off-by: Ilan Peer Signed-off-by: Johannes Berg --- include/linux/ieee80211.h | 1 + net/mac80211/ieee80211_i.h | 26 ++++++- net/mac80211/mlme.c | 17 ++++- net/mac80211/util.c | 150 ++++++++++++++++++++++++++++++++++++- 4 files changed, 188 insertions(+), 6 deletions(-) diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h index b935a85b2f44..f51e939f28f2 100644 --- a/include/linux/ieee80211.h +++ b/include/linux/ieee80211.h @@ -4666,6 +4666,7 @@ static inline bool ieee80211_mle_size_ok(const u8 *data, size_t len) enum ieee80211_mle_subelems { IEEE80211_MLE_SUBELEM_PER_STA_PROFILE = 0, + IEEE80211_MLE_SUBELEM_FRAGMENT = 254, }; #define IEEE80211_MLE_STA_CONTROL_LINK_ID 0x000f diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 4e1d4c339f2d..5dcbc8de53fd 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -1707,8 +1707,27 @@ struct ieee802_11_elems { u8 tx_pwr_env_num; u8 eht_cap_len; + /* mult-link element can be de-fragmented and thus u8 is not sufficient */ + size_t multi_link_len; + + /* + * store the per station profile pointer and length in case that the + * parsing also handled Multi-Link element parsing for a specific link + * ID. + */ + struct ieee80211_mle_per_sta_profile *prof; + size_t sta_prof_len; + /* whether a parse error occurred while retrieving these elements */ bool parse_error; + + /* + * scratch buffer that can be used for various element parsing related + * tasks, e.g., element de-fragmentation etc. + */ + size_t scratch_len; + u8 *scratch_pos; + u8 scratch[]; }; static inline struct ieee80211_local *hw_to_local( @@ -2197,9 +2216,13 @@ static inline void ieee80211_tx_skb(struct ieee80211_sub_if_data *sdata, * represent a non-transmitting BSS in which case the data * for that non-transmitting BSS is returned * @link_id: the link ID to parse elements for, if a STA profile - * is present in the multi-link element, or -1 to ignore + * is present in the multi-link element, or -1 to ignore; + * note that the code currently assumes parsing an association + * (or re-association) response frame if this is given * @from_ap: frame is received from an AP (currently used only * for EHT capabilities parsing) + * @scratch_len: if non zero, specifies the requested length of the scratch + * buffer; otherwise, 'len' is used. */ struct ieee80211_elems_parse_params { const u8 *start; @@ -2210,6 +2233,7 @@ struct ieee80211_elems_parse_params { struct cfg80211_bss *bss; int link_id; bool from_ap; + size_t scratch_len; }; struct ieee802_11_elems * diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 54b8d5065bbd..a7e06c8ddaf3 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -3923,11 +3923,12 @@ static bool ieee80211_assoc_config_link(struct ieee80211_link_data *link, struct ieee80211_mgd_assoc_data *assoc_data = sdata->u.mgd.assoc_data; struct ieee80211_bss_conf *bss_conf = link->conf; struct ieee80211_local *local = sdata->local; + unsigned int link_id = link->link_id; struct ieee80211_elems_parse_params parse_params = { .start = elem_start, .len = elem_len, .bss = cbss, - .link_id = link == &sdata->deflink ? -1 : link->link_id, + .link_id = link_id == assoc_data->assoc_link_id ? -1 : link_id, .from_ap = true, }; bool is_6ghz = cbss->channel->band == NL80211_BAND_6GHZ; @@ -3942,8 +3943,18 @@ static bool ieee80211_assoc_config_link(struct ieee80211_link_data *link, if (!elems) return false; - /* FIXME: use from STA profile element after parsing that */ - capab_info = le16_to_cpu(mgmt->u.assoc_resp.capab_info); + if (link_id == assoc_data->assoc_link_id) { + capab_info = le16_to_cpu(mgmt->u.assoc_resp.capab_info); + } else if (!elems->prof) { + ret = false; + goto out; + } else { + const u8 *ptr = elems->prof->variable + + elems->prof->sta_info_len - 1; + + /* FIXME: need to also handle the status code */ + capab_info = get_unaligned_le16(ptr); + } if (!is_s1g && !elems->supp_rates) { sdata_info(sdata, "no SuppRates element in AssocResp\n"); diff --git a/net/mac80211/util.c b/net/mac80211/util.c index bf7461c41bef..40b75fa82b15 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -1026,8 +1026,10 @@ ieee80211_parse_extension_element(u32 *crc, elems->eht_operation = data; break; case WLAN_EID_EXT_EHT_MULTI_LINK: - if (ieee80211_mle_size_ok(data, len)) + if (ieee80211_mle_size_ok(data, len)) { elems->multi_link = (void *)data; + elems->multi_link_len = len; + } break; } } @@ -1497,6 +1499,145 @@ static size_t ieee802_11_find_bssid_profile(const u8 *start, size_t len, return found ? profile_len : 0; } +static void ieee80211_defragment_element(struct ieee802_11_elems *elems, + void **elem_ptr, size_t *len, + size_t total_len, u8 frag_id) +{ + u8 *data = *elem_ptr, *pos, *start; + const struct element *elem; + + /* + * Since 'data' points to the data of the element, not the element + * itself, allow 254 in case it was an extended element where the + * extended ID isn't part of the data we see here and thus not part of + * 'len' either. + */ + if (!data || (*len != 254 && *len != 255)) + return; + + start = elems->scratch_pos; + + if (WARN_ON(*len > (elems->scratch + elems->scratch_len - + elems->scratch_pos))) + return; + + memcpy(elems->scratch_pos, data, *len); + elems->scratch_pos += *len; + + pos = data + *len; + total_len -= *len; + for_each_element(elem, pos, total_len) { + if (elem->id != frag_id) + break; + + if (WARN_ON(elem->datalen > + (elems->scratch + elems->scratch_len - + elems->scratch_pos))) + return; + + memcpy(elems->scratch_pos, elem->data, elem->datalen); + elems->scratch_pos += elem->datalen; + + *len += elem->datalen; + } + + *elem_ptr = start; +} + +static void ieee80211_mle_get_sta_prof(struct ieee802_11_elems *elems, + u8 link_id) +{ + const struct ieee80211_multi_link_elem *ml = elems->multi_link; + size_t ml_len = elems->multi_link_len; + const struct element *sub; + + if (!ml || !ml_len) + return; + + if (le16_get_bits(ml->control, IEEE80211_ML_CONTROL_TYPE) != + IEEE80211_ML_CONTROL_TYPE_BASIC) + return; + + for_each_mle_subelement(sub, (u8 *)ml, ml_len) { + struct ieee80211_mle_per_sta_profile *prof = (void *)sub->data; + u16 control; + + if (sub->id != IEEE80211_MLE_SUBELEM_PER_STA_PROFILE) + continue; + + if (!ieee80211_mle_sta_prof_size_ok(sub->data, sub->datalen)) + return; + + control = le16_to_cpu(prof->control); + + if (link_id != u16_get_bits(control, + IEEE80211_MLE_STA_CONTROL_LINK_ID)) + continue; + + if (!(control & IEEE80211_MLE_STA_CONTROL_COMPLETE_PROFILE)) + return; + + elems->prof = prof; + elems->sta_prof_len = sub->datalen; + + /* the sub element can be fragmented */ + ieee80211_defragment_element(elems, (void **)&elems->prof, + &elems->sta_prof_len, + ml_len - (sub->data - (u8 *)ml), + IEEE80211_MLE_SUBELEM_FRAGMENT); + return; + } +} + +static void ieee80211_mle_parse_link(struct ieee802_11_elems *elems, + struct ieee80211_elems_parse_params *params) +{ + struct ieee80211_mle_per_sta_profile *prof; + struct ieee80211_elems_parse_params sub = { + .action = params->action, + .from_ap = params->from_ap, + .link_id = -1, + }; + const struct element *non_inherit = NULL; + const u8 *end; + + if (params->link_id == -1) + return; + + ieee80211_defragment_element(elems, (void **)&elems->multi_link, + &elems->multi_link_len, + elems->total_len - ((u8 *)elems->multi_link - + elems->ie_start), + WLAN_EID_FRAGMENT); + + ieee80211_mle_get_sta_prof(elems, params->link_id); + prof = elems->prof; + + if (!prof) + return; + + /* check if we have the 4 bytes for the fixed part in assoc response */ + if (elems->sta_prof_len < sizeof(*prof) + prof->sta_info_len - 1 + 4) { + elems->prof = NULL; + elems->sta_prof_len = 0; + return; + } + + /* + * Skip the capability information and the status code that are expected + * as part of the station profile in association response frames. Note + * the -1 is because the 'sta_info_len' is accounted to as part of the + * per-STA profile, but not part of the 'u8 variable[]' portion. + */ + sub.start = prof->variable + prof->sta_info_len - 1 + 4; + end = (const u8 *)prof + elems->sta_prof_len; + sub.len = end - sub.start; + + non_inherit = cfg80211_find_ext_elem(WLAN_EID_EXT_NON_INHERITANCE, + sub.start, sub.len); + _ieee802_11_parse_elems_full(&sub, elems, non_inherit); +} + struct ieee802_11_elems * ieee802_11_parse_elems_full(struct ieee80211_elems_parse_params *params) { @@ -1504,12 +1645,15 @@ ieee802_11_parse_elems_full(struct ieee80211_elems_parse_params *params) const struct element *non_inherit = NULL; u8 *nontransmitted_profile; int nontransmitted_profile_len = 0; + size_t scratch_len = params->scratch_len ?: 2 * params->len; - elems = kzalloc(sizeof(*elems), GFP_ATOMIC); + elems = kzalloc(sizeof(*elems) + scratch_len, GFP_ATOMIC); if (!elems) return NULL; elems->ie_start = params->start; elems->total_len = params->len; + elems->scratch_len = scratch_len; + elems->scratch_pos = elems->scratch; nontransmitted_profile = kmalloc(params->len, GFP_ATOMIC); if (nontransmitted_profile) { @@ -1537,6 +1681,8 @@ ieee802_11_parse_elems_full(struct ieee80211_elems_parse_params *params) _ieee802_11_parse_elems_full(&sub, elems, NULL); } + ieee80211_mle_parse_link(elems, params); + if (elems->tim && !elems->parse_error) { const struct ieee80211_tim_ie *tim_ie = elems->tim; From c2d052a3c41aa7815714a18911819efde0d7d384 Mon Sep 17 00:00:00 2001 From: Ilan Peer Date: Thu, 8 Sep 2022 11:31:10 +0300 Subject: [PATCH 0068/2002] wifi: mac80211: Process association status for affiliated links In case the AP returned a non success status for one of the links, do not activate the link. Signed-off-by: Ilan Peer Signed-off-by: Johannes Berg --- net/mac80211/ieee80211_i.h | 2 ++ net/mac80211/mlme.c | 40 +++++++++++++++++++++++++++++++++----- 2 files changed, 37 insertions(+), 5 deletions(-) diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 5dcbc8de53fd..517a50abdb09 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -412,6 +412,8 @@ struct ieee80211_mgd_assoc_data { u8 *elems; /* pointing to inside ie[] below */ ieee80211_conn_flags_t conn_flags; + + u16 status; } link[IEEE80211_MLD_MAX_NUM_LINKS]; u8 ap_addr[ETH_ALEN] __aligned(2); diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index a7e06c8ddaf3..2e4bb75c68c0 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -2754,7 +2754,8 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata, struct cfg80211_bss *cbss = assoc_data->link[link_id].bss; struct ieee80211_link_data *link; - if (!cbss) + if (!cbss || + assoc_data->link[link_id].status != WLAN_STATUS_SUCCESS) continue; link = sdata_dereference(sdata->link[link_id], sdata); @@ -2782,7 +2783,8 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata, struct ieee80211_link_data *link; struct cfg80211_bss *cbss = assoc_data->link[link_id].bss; - if (!cbss) + if (!cbss || + assoc_data->link[link_id].status != WLAN_STATUS_SUCCESS) continue; link = sdata_dereference(sdata->link[link_id], sdata); @@ -3945,6 +3947,12 @@ static bool ieee80211_assoc_config_link(struct ieee80211_link_data *link, if (link_id == assoc_data->assoc_link_id) { capab_info = le16_to_cpu(mgmt->u.assoc_resp.capab_info); + + /* + * we should not get to this flow unless the association was + * successful, so set the status directly to success + */ + assoc_data->link[link_id].status = WLAN_STATUS_SUCCESS; } else if (!elems->prof) { ret = false; goto out; @@ -3952,8 +3960,19 @@ static bool ieee80211_assoc_config_link(struct ieee80211_link_data *link, const u8 *ptr = elems->prof->variable + elems->prof->sta_info_len - 1; - /* FIXME: need to also handle the status code */ + /* + * During parsing, we validated that these fields exist, + * otherwise elems->prof would have been set to NULL. + */ capab_info = get_unaligned_le16(ptr); + assoc_data->link[link_id].status = get_unaligned_le16(ptr + 2); + + if (assoc_data->link[link_id].status != WLAN_STATUS_SUCCESS) { + link_info(link, "association response status code=%u\n", + assoc_data->link[link_id].status); + ret = true; + goto out; + } } if (!is_s1g && !elems->supp_rates) { @@ -4874,6 +4893,7 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata, unsigned int link_id; struct sta_info *sta; u64 changed[IEEE80211_MLD_MAX_NUM_LINKS] = {}; + u16 valid_links = 0; int err; mutex_lock(&sdata->local->sta_mtx); @@ -4886,8 +4906,6 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata, goto out_err; if (sdata->vif.valid_links) { - u16 valid_links = 0; - for (link_id = 0; link_id < IEEE80211_MLD_MAX_NUM_LINKS; link_id++) { if (!assoc_data->link[link_id].bss) continue; @@ -4957,6 +4975,12 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata, &changed[link_id])) goto out_err; + if (assoc_data->link[link_id].status != WLAN_STATUS_SUCCESS) { + valid_links &= ~BIT(link_id); + ieee80211_sta_remove_link(sta, link_id); + continue; + } + if (link_id != assoc_data->assoc_link_id) { err = ieee80211_sta_activate_link(sta, link_id); if (err) @@ -4964,6 +4988,9 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata, } } + /* links might have changed due to rejected ones, set them again */ + ieee80211_vif_set_links(sdata, valid_links); + rate_control_rate_init(sta); if (ifmgd->flags & IEEE80211_STA_MFP_ENABLED) { @@ -5197,10 +5224,13 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata, link = sdata_dereference(sdata->link[link_id], sdata); if (!link) continue; + if (!assoc_data->link[link_id].bss) continue; + resp.links[link_id].bss = assoc_data->link[link_id].bss; resp.links[link_id].addr = link->conf->addr; + resp.links[link_id].status = assoc_data->link[link_id].status; /* get uapsd queues configuration - same for all links */ resp.uapsd_queues = 0; From 2d5e6171493695e962592657430a749f9f0d0728 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 6 Sep 2022 22:18:19 +0200 Subject: [PATCH 0069/2002] wifi: mac80211: wme: use ap_addr instead of deflink BSSID We use this to look up the destination station, so it needs to be the MLD address of the AP for an MLO; use ap_addr instead of the BSSID. Signed-off-by: Johannes Berg --- net/mac80211/wme.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/mac80211/wme.c b/net/mac80211/wme.c index ecc1de2e68a5..9fab97f6fbea 100644 --- a/net/mac80211/wme.c +++ b/net/mac80211/wme.c @@ -211,7 +211,7 @@ u16 ieee80211_select_queue(struct ieee80211_sub_if_data *sdata, if (sta) break; - ra = sdata->deflink.u.mgd.bssid; + ra = sdata->vif.cfg.ap_addr; break; case NL80211_IFTYPE_ADHOC: ra = skb->data; From f7ee304111584b1822c960b0b5e7ebbe3d4e406b Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 6 Sep 2022 22:25:50 +0200 Subject: [PATCH 0070/2002] wifi: mac80211: transmit AddBA with MLD address This management frame is intended for the MLD so we treat it in mac80211 as MLD addressed as well, and should therefore use the MLD address of the AP for the BSSID field in the frame, address translation applies. Signed-off-by: Johannes Berg --- net/mac80211/agg-tx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/mac80211/agg-tx.c b/net/mac80211/agg-tx.c index 07c892aa8c73..9c40f8d3bce8 100644 --- a/net/mac80211/agg-tx.c +++ b/net/mac80211/agg-tx.c @@ -82,7 +82,7 @@ static void ieee80211_send_addba_request(struct ieee80211_sub_if_data *sdata, sdata->vif.type == NL80211_IFTYPE_MESH_POINT) memcpy(mgmt->bssid, sdata->vif.addr, ETH_ALEN); else if (sdata->vif.type == NL80211_IFTYPE_STATION) - memcpy(mgmt->bssid, sdata->deflink.u.mgd.bssid, ETH_ALEN); + memcpy(mgmt->bssid, sdata->vif.cfg.ap_addr, ETH_ALEN); else if (sdata->vif.type == NL80211_IFTYPE_ADHOC) memcpy(mgmt->bssid, sdata->u.ibss.bssid, ETH_ALEN); From 1e0f8cc96b7162075d2e3b6bef856497884a3ae8 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 6 Sep 2022 22:37:03 +0200 Subject: [PATCH 0071/2002] wifi: nl80211: use link ID in NL80211_CMD_SET_BSS We clearly need the link ID here, to know the right BSS to configure. Use/require it. Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 2 ++ net/wireless/nl80211.c | 4 +++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 4d35a4234417..659dd1bee70f 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -2105,6 +2105,7 @@ struct mpath_info { * * Used to change BSS parameters (mainly for AP mode). * + * @link_id: link_id or -1 for non-MLD * @use_cts_prot: Whether to use CTS protection * (0 = no, 1 = yes, -1 = do not change) * @use_short_preamble: Whether the use of short preambles is allowed @@ -2122,6 +2123,7 @@ struct mpath_info { * @p2p_opp_ps: P2P opportunistic PS (-1 = no change) */ struct bss_parameters { + int link_id; int use_cts_prot; int use_short_preamble; int use_short_slot_time; diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index ad7393cd3d18..1d0277758d0e 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -7780,6 +7780,7 @@ static int nl80211_set_bss(struct sk_buff *skb, struct genl_info *info) int err; memset(¶ms, 0, sizeof(params)); + params.link_id = nl80211_link_id_or_invalid(info->attrs); /* default to not changing parameters */ params.use_cts_prot = -1; params.use_short_preamble = -1; @@ -16564,7 +16565,8 @@ static const struct genl_small_ops nl80211_small_ops[] = { .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, .doit = nl80211_set_bss, .flags = GENL_UNS_ADMIN_PERM, - .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV_UP), + .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV_UP | + NL80211_FLAG_MLO_VALID_LINK_ID), }, { .cmd = NL80211_CMD_GET_REG, From 9a886df0c36928039fc760eb8129c1d7f4bc418e Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 6 Sep 2022 22:42:25 +0200 Subject: [PATCH 0072/2002] wifi: mac80211: use link_id in ieee80211_change_bss() We should set the parameters here per link, except unfortunately ap_isolate, but we can't really change that anymore so it'll remain a quirk in the API in that you need to change it on one of the valid links and it'll apply to all. Signed-off-by: Johannes Berg --- net/mac80211/cfg.c | 40 +++++++++++++++++++++------------------- 1 file changed, 21 insertions(+), 19 deletions(-) diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 687b4c878d4a..ef5700eac05c 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -2554,47 +2554,50 @@ static int ieee80211_change_bss(struct wiphy *wiphy, struct bss_parameters *params) { struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + struct ieee80211_link_data *link; struct ieee80211_supported_band *sband; u32 changed = 0; - if (!sdata_dereference(sdata->deflink.u.ap.beacon, sdata)) + link = ieee80211_link_or_deflink(sdata, params->link_id, true); + if (IS_ERR(link)) + return PTR_ERR(link); + + if (!sdata_dereference(link->u.ap.beacon, sdata)) return -ENOENT; - sband = ieee80211_get_sband(sdata); + sband = ieee80211_get_link_sband(link); if (!sband) return -EINVAL; if (params->use_cts_prot >= 0) { - sdata->vif.bss_conf.use_cts_prot = params->use_cts_prot; + link->conf->use_cts_prot = params->use_cts_prot; changed |= BSS_CHANGED_ERP_CTS_PROT; } if (params->use_short_preamble >= 0) { - sdata->vif.bss_conf.use_short_preamble = - params->use_short_preamble; + link->conf->use_short_preamble = params->use_short_preamble; changed |= BSS_CHANGED_ERP_PREAMBLE; } - if (!sdata->vif.bss_conf.use_short_slot && + if (!link->conf->use_short_slot && (sband->band == NL80211_BAND_5GHZ || sband->band == NL80211_BAND_6GHZ)) { - sdata->vif.bss_conf.use_short_slot = true; + link->conf->use_short_slot = true; changed |= BSS_CHANGED_ERP_SLOT; } if (params->use_short_slot_time >= 0) { - sdata->vif.bss_conf.use_short_slot = - params->use_short_slot_time; + link->conf->use_short_slot = params->use_short_slot_time; changed |= BSS_CHANGED_ERP_SLOT; } if (params->basic_rates) { - ieee80211_parse_bitrates(sdata->vif.bss_conf.chandef.width, + ieee80211_parse_bitrates(link->conf->chandef.width, wiphy->bands[sband->band], params->basic_rates, params->basic_rates_len, - &sdata->vif.bss_conf.basic_rates); + &link->conf->basic_rates); changed |= BSS_CHANGED_BASIC_RATES; - ieee80211_check_rate_mask(&sdata->deflink); + ieee80211_check_rate_mask(link); } if (params->ap_isolate >= 0) { @@ -2606,30 +2609,29 @@ static int ieee80211_change_bss(struct wiphy *wiphy, } if (params->ht_opmode >= 0) { - sdata->vif.bss_conf.ht_operation_mode = - (u16) params->ht_opmode; + link->conf->ht_operation_mode = (u16)params->ht_opmode; changed |= BSS_CHANGED_HT; } if (params->p2p_ctwindow >= 0) { - sdata->vif.bss_conf.p2p_noa_attr.oppps_ctwindow &= + link->conf->p2p_noa_attr.oppps_ctwindow &= ~IEEE80211_P2P_OPPPS_CTWINDOW_MASK; - sdata->vif.bss_conf.p2p_noa_attr.oppps_ctwindow |= + link->conf->p2p_noa_attr.oppps_ctwindow |= params->p2p_ctwindow & IEEE80211_P2P_OPPPS_CTWINDOW_MASK; changed |= BSS_CHANGED_P2P_PS; } if (params->p2p_opp_ps > 0) { - sdata->vif.bss_conf.p2p_noa_attr.oppps_ctwindow |= + link->conf->p2p_noa_attr.oppps_ctwindow |= IEEE80211_P2P_OPPPS_ENABLE_BIT; changed |= BSS_CHANGED_P2P_PS; } else if (params->p2p_opp_ps == 0) { - sdata->vif.bss_conf.p2p_noa_attr.oppps_ctwindow &= + link->conf->p2p_noa_attr.oppps_ctwindow &= ~IEEE80211_P2P_OPPPS_ENABLE_BIT; changed |= BSS_CHANGED_P2P_PS; } - ieee80211_link_info_change_notify(sdata, &sdata->deflink, changed); + ieee80211_link_info_change_notify(sdata, link, changed); return 0; } From 0143ea09b63d58ed3e19ffdfc444cc23d8d877b6 Mon Sep 17 00:00:00 2001 From: Haim Dreyfuss Date: Sun, 11 Sep 2022 22:01:42 +0300 Subject: [PATCH 0073/2002] wifi: mac80211: advertise TWT requester only with HW support Currently, we rely only on the AP capability. If the AP supports TWT responder we will advertise TWT requester even if the driver or HW doesn't support it. Fix this by checking the HW capability. Signed-off-by: Haim Dreyfuss Signed-off-by: Johannes Berg --- net/mac80211/mlme.c | 30 ++++++++++++++++++++++++------ 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 2e4bb75c68c0..b6f378e7edea 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -3870,9 +3870,15 @@ static void ieee80211_get_rates(struct ieee80211_supported_band *sband, } } -static bool ieee80211_twt_req_supported(const struct link_sta_info *link_sta, +static bool ieee80211_twt_req_supported(struct ieee80211_sub_if_data *sdata, + struct ieee80211_supported_band *sband, + const struct link_sta_info *link_sta, const struct ieee802_11_elems *elems) { + const struct ieee80211_sta_he_cap *own_he_cap = + ieee80211_get_he_iftype_cap(sband, + ieee80211_vif_type_p2p(&sdata->vif)); + if (elems->ext_capab_len < 10) return false; @@ -3880,14 +3886,19 @@ static bool ieee80211_twt_req_supported(const struct link_sta_info *link_sta, return false; return link_sta->pub->he_cap.he_cap_elem.mac_cap_info[0] & - IEEE80211_HE_MAC_CAP0_TWT_RES; + IEEE80211_HE_MAC_CAP0_TWT_RES && + own_he_cap && + (own_he_cap->he_cap_elem.mac_cap_info[0] & + IEEE80211_HE_MAC_CAP0_TWT_REQ); } -static int ieee80211_recalc_twt_req(struct ieee80211_link_data *link, +static int ieee80211_recalc_twt_req(struct ieee80211_sub_if_data *sdata, + struct ieee80211_supported_band *sband, + struct ieee80211_link_data *link, struct link_sta_info *link_sta, struct ieee802_11_elems *elems) { - bool twt = ieee80211_twt_req_supported(link_sta, elems); + bool twt = ieee80211_twt_req_supported(sdata, sband, link_sta, elems); if (link->conf->twt_requester != twt) { link->conf->twt_requester = twt; @@ -4129,7 +4140,8 @@ static bool ieee80211_assoc_config_link(struct ieee80211_link_data *link, else bss_conf->twt_protected = false; - *changed |= ieee80211_recalc_twt_req(link, link_sta, elems); + *changed |= ieee80211_recalc_twt_req(sdata, sband, link, + link_sta, elems); if (elems->eht_operation && elems->eht_cap && !(link->u.mgd.conn_flags & IEEE80211_CONN_DISABLE_EHT)) { @@ -5472,6 +5484,7 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_link_data *link, struct ieee802_11_elems *elems; struct ieee80211_local *local = sdata->local; struct ieee80211_chanctx_conf *chanctx_conf; + struct ieee80211_supported_band *sband; struct ieee80211_channel *chan; struct link_sta_info *link_sta; struct sta_info *sta; @@ -5734,7 +5747,12 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_link_data *link, goto free; } - changed |= ieee80211_recalc_twt_req(link, link_sta, elems); + if (WARN_ON(!link->conf->chandef.chan)) + goto free; + + sband = local->hw.wiphy->bands[link->conf->chandef.chan->band]; + + changed |= ieee80211_recalc_twt_req(sdata, sband, link, link_sta, elems); if (ieee80211_config_bw(link, elems->ht_cap_elem, elems->vht_cap_elem, elems->ht_operation, From 9beed8de806644b59729d862e66e8a4eef894fa8 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 26 Sep 2022 15:45:04 +0200 Subject: [PATCH 0074/2002] wifi: mac80211: set internal scan request BSSID If any driver relies entirely on the scan request BSSID, then that would be wrong for internal scans. Initialize it to the broadcast address since we don't otherwise use the field. Signed-off-by: Johannes Berg --- net/mac80211/main.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/net/mac80211/main.c b/net/mac80211/main.c index 46f3eddc2388..25e72812000e 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -1155,6 +1155,8 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) if (!local->int_scan_req) return -ENOMEM; + eth_broadcast_addr(local->int_scan_req->bssid); + for (band = 0; band < NUM_NL80211_BANDS; band++) { if (!local->hw.wiphy->bands[band]) continue; From 3903963ed93dda7923144fd8b25fdfe0c3fd7437 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 16 Sep 2022 19:15:52 +0200 Subject: [PATCH 0075/2002] wifi: mac80211: fix AddBA response addressing Since this frame is addressed from/to an MLD, it should be built with the correct AP MLD address (in station mode) to be encrypted properly. Signed-off-by: Johannes Berg --- net/mac80211/agg-rx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/mac80211/agg-rx.c b/net/mac80211/agg-rx.c index 9414d3bbd65f..eaef88a603e5 100644 --- a/net/mac80211/agg-rx.c +++ b/net/mac80211/agg-rx.c @@ -242,7 +242,7 @@ static void ieee80211_send_addba_resp(struct sta_info *sta, u8 *da, u16 tid, sdata->vif.type == NL80211_IFTYPE_MESH_POINT) memcpy(mgmt->bssid, sdata->vif.addr, ETH_ALEN); else if (sdata->vif.type == NL80211_IFTYPE_STATION) - memcpy(mgmt->bssid, sdata->deflink.u.mgd.bssid, ETH_ALEN); + memcpy(mgmt->bssid, sdata->vif.cfg.ap_addr, ETH_ALEN); else if (sdata->vif.type == NL80211_IFTYPE_ADHOC) memcpy(mgmt->bssid, sdata->u.ibss.bssid, ETH_ALEN); From f3630c4f82ae43682bf84e6ddcbd7e97285d4699 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 27 Sep 2022 11:39:23 +0200 Subject: [PATCH 0076/2002] wifi: mac80211: add RCU _check() link access variants We might sometimes need to use RCU and locking in the same code path, so add the two variants link_conf_dereference_check() and link_sta_dereference_check(). Signed-off-by: Johannes Berg --- include/net/mac80211.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/include/net/mac80211.h b/include/net/mac80211.h index c413050ec8dd..cda4584dfd51 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -1915,6 +1915,10 @@ static inline bool lockdep_vif_mutex_held(struct ieee80211_vif *vif) rcu_dereference_protected((vif)->link_conf[link_id], \ lockdep_vif_mutex_held(vif)) +#define link_conf_dereference_check(vif, link_id) \ + rcu_dereference_check((vif)->link_conf[link_id], \ + lockdep_vif_mutex_held(vif)) + /** * enum ieee80211_key_flags - key flags * @@ -2311,6 +2315,10 @@ static inline bool lockdep_sta_mutex_held(struct ieee80211_sta *pubsta) rcu_dereference_protected((sta)->link[link_id], \ lockdep_sta_mutex_held(sta)) +#define link_sta_dereference_check(sta, link_id) \ + rcu_dereference_check((sta)->link[link_id], \ + lockdep_sta_mutex_held(sta)) + #define for_each_sta_active_link(vif, sta, link_sta, link_id) \ for (link_id = 0; link_id < ARRAY_SIZE((sta)->link); link_id++) \ if ((!(vif)->active_links || \ From 1177aaa7fe9373c762cd5bf5f5de8517bac989d5 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Sat, 17 Sep 2022 03:14:53 +0200 Subject: [PATCH 0077/2002] wifi: fix multi-link element subelement iteration The subelements obviously start after the common data, including the common multi-link element structure definition itself. This bug was possibly just hidden by the higher bits of the control being set to 0, so the iteration just found one bogus element and most of the code could continue anyway. Fixes: 0f48b8b88aa9 ("wifi: ieee80211: add definitions for multi-link element") Signed-off-by: Johannes Berg --- include/linux/ieee80211.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h index f51e939f28f2..6252f02f38b7 100644 --- a/include/linux/ieee80211.h +++ b/include/linux/ieee80211.h @@ -4593,7 +4593,7 @@ static inline u8 ieee80211_mle_common_size(const u8 *data) return 0; } - return common + mle->variable[0]; + return sizeof(*mle) + common + mle->variable[0]; } /** From 78a6a43aaf87180ec7425a2a90468e1b4d09a1ec Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 21 Sep 2022 02:44:58 +0200 Subject: [PATCH 0078/2002] wifi: mac80211: mlme: fix null-ptr deref on failed assoc If association to an AP without a link 0 fails, then we crash in tracing because it assumes that either ap_mld_addr or link 0 BSS is valid, since we clear sdata->vif.valid_links and then don't add the ap_mld_addr to the struct. Since we clear also sdata->vif.cfg.ap_addr, keep a local copy of it and assign it earlier, before clearing valid_links, to fix this. Fixes: 81151ce462e5 ("wifi: mac80211: support MLO authentication/association with one link") Signed-off-by: Johannes Berg --- net/mac80211/mlme.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index b6f378e7edea..1ad0bf3bfcae 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -5082,6 +5082,7 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata, struct cfg80211_rx_assoc_resp resp = { .uapsd_queues = -1, }; + u8 ap_mld_addr[ETH_ALEN] __aligned(2); unsigned int link_id; sdata_assert_lock(sdata); @@ -5251,6 +5252,11 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata, resp.uapsd_queues |= ieee80211_ac_to_qos_mask[ac]; } + if (sdata->vif.valid_links) { + ether_addr_copy(ap_mld_addr, sdata->vif.cfg.ap_addr); + resp.ap_mld_addr = ap_mld_addr; + } + ieee80211_destroy_assoc_data(sdata, status_code == WLAN_STATUS_SUCCESS ? ASSOC_SUCCESS : @@ -5260,8 +5266,6 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata, resp.len = len; resp.req_ies = ifmgd->assoc_req_ies; resp.req_ies_len = ifmgd->assoc_req_ies_len; - if (sdata->vif.valid_links) - resp.ap_mld_addr = sdata->vif.cfg.ap_addr; cfg80211_rx_assoc_resp(sdata->dev, &resp); notify_driver: drv_mgd_complete_tx(sdata->local, sdata, &info); From 85176a3fcd9748558cff72d4cdff5465b8732282 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 21 Sep 2022 23:01:46 +0200 Subject: [PATCH 0079/2002] wifi: mac80211: check link ID in auth/assoc continuation Ensure that the link ID matches in auth/assoc continuation, otherwise we need to reset all the data. Fixes: 81151ce462e5 ("wifi: mac80211: support MLO authentication/association with one link") Signed-off-by: Johannes Berg --- net/mac80211/ieee80211_i.h | 1 + net/mac80211/mlme.c | 7 +++++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 517a50abdb09..b704656027db 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -390,6 +390,7 @@ struct ieee80211_mgd_auth_data { bool done, waiting; bool peer_confirmed; bool timeout_started; + int link_id; u8 ap_addr[ETH_ALEN] __aligned(2); diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 1ad0bf3bfcae..c46f355265ee 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -6702,6 +6702,7 @@ int ieee80211_mgd_auth(struct ieee80211_sub_if_data *sdata, req->ap_mld_addr ?: req->bss->bssid, ETH_ALEN); auth_data->bss = req->bss; + auth_data->link_id = req->link_id; if (req->auth_data_len >= 4) { if (req->auth_type == NL80211_AUTHTYPE_SAE) { @@ -6720,7 +6721,8 @@ int ieee80211_mgd_auth(struct ieee80211_sub_if_data *sdata, * removal and re-addition of the STA entry in * ieee80211_prep_connection(). */ - cont_auth = ifmgd->auth_data && req->bss == ifmgd->auth_data->bss; + cont_auth = ifmgd->auth_data && req->bss == ifmgd->auth_data->bss && + ifmgd->auth_data->link_id == req->link_id; if (req->ie && req->ie_len) { memcpy(&auth_data->data[auth_data->data_len], @@ -7044,7 +7046,8 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata, /* keep sta info, bssid if matching */ match = ether_addr_equal(ifmgd->auth_data->ap_addr, - assoc_data->ap_addr); + assoc_data->ap_addr) && + ifmgd->auth_data->link_id == req->link_id; ieee80211_destroy_auth_data(sdata, match); } From 7a693ce0033707a49e951769cc230ef870ff76a8 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 20 Sep 2022 22:43:53 +0200 Subject: [PATCH 0080/2002] wifi: mac80211: mlme: mark assoc link in output It's useful to know which link was used for the association, mark it when printing the links. Signed-off-by: Johannes Berg --- net/mac80211/mlme.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index c46f355265ee..50a376f86ec2 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -4946,9 +4946,11 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata, if (sdata->vif.valid_links) link_info(link, - "local address %pM, AP link address %pM\n", + "local address %pM, AP link address %pM%s\n", link->conf->addr, - assoc_data->link[link_id].bss->bssid); + assoc_data->link[link_id].bss->bssid, + link_id == assoc_data->assoc_link_id ? + " (assoc)" : ""); link_sta = rcu_dereference_protected(sta->link[link_id], lockdep_is_held(&local->sta_mtx)); From e406121e1860e7789ebfa0e1b6853e48dde8b879 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 19 Sep 2022 19:19:30 +0200 Subject: [PATCH 0081/2002] wifi: mac80211: change AddBA deny error message If the station has no HT, we deny the aggregation session but the error message talks about QoS; change it to say HT instead. Signed-off-by: Johannes Berg --- net/mac80211/agg-rx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/mac80211/agg-rx.c b/net/mac80211/agg-rx.c index eaef88a603e5..f8bec5ee5b44 100644 --- a/net/mac80211/agg-rx.c +++ b/net/mac80211/agg-rx.c @@ -299,7 +299,7 @@ void ___ieee80211_start_rx_ba_session(struct sta_info *sta, if (!sta->sta.deflink.ht_cap.ht_supported && sta->sdata->vif.bss_conf.chandef.chan->band != NL80211_BAND_6GHZ) { ht_dbg(sta->sdata, - "STA %pM erroneously requests BA session on tid %d w/o QoS\n", + "STA %pM erroneously requests BA session on tid %d w/o HT\n", sta->sta.addr, tid); /* send a response anyway, it's an error case if we get here */ goto end; From cb04b5ef855c210a6e8e3e64fae9e2da9100f72e Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 15 Sep 2022 14:57:47 +0200 Subject: [PATCH 0082/2002] wifi: mac80211: don't clear DTIM period after setting it Fix the code that sets the DTIM period to always propagate it into link->conf->dtim_period and not overwrite it, while still preferring to set it from the beacon data if available. Signed-off-by: Johannes Berg --- net/mac80211/mlme.c | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 50a376f86ec2..a651d3ffd8e6 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -2717,18 +2717,10 @@ static u32 ieee80211_link_set_associated(struct ieee80211_link_data *link, } if (link->u.mgd.have_beacon) { - /* - * If the AP is buggy we may get here with no DTIM period - * known, so assume it's 1 which is the only safe assumption - * in that case, although if the TIM IE is broken powersave - * probably just won't work at all. - */ - bss_conf->dtim_period = link->u.mgd.dtim_period ?: 1; bss_conf->beacon_rate = bss->beacon_rate; changed |= BSS_CHANGED_BEACON_INFO; } else { bss_conf->beacon_rate = NULL; - bss_conf->dtim_period = 0; } /* Tell the driver to monitor connection quality (if supported) */ @@ -4934,10 +4926,11 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata, } for (link_id = 0; link_id < IEEE80211_MLD_MAX_NUM_LINKS; link_id++) { + struct cfg80211_bss *cbss = assoc_data->link[link_id].bss; struct ieee80211_link_data *link; struct link_sta_info *link_sta; - if (!assoc_data->link[link_id].bss) + if (!cbss) continue; link = sdata_dereference(sdata->link[link_id], sdata); @@ -4957,19 +4950,25 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata, if (WARN_ON(!link_sta)) goto out_err; - if (link_id != assoc_data->assoc_link_id) { - struct cfg80211_bss *cbss = assoc_data->link[link_id].bss; + if (!link->u.mgd.have_beacon) { const struct cfg80211_bss_ies *ies; rcu_read_lock(); - ies = rcu_dereference(cbss->ies); + ies = rcu_dereference(cbss->beacon_ies); + if (ies) + link->u.mgd.have_beacon = true; + else + ies = rcu_dereference(cbss->ies); ieee80211_get_dtim(ies, &link->conf->sync_dtim_count, &link->u.mgd.dtim_period); - link->conf->dtim_period = link->u.mgd.dtim_period ?: 1; link->conf->beacon_int = cbss->beacon_interval; rcu_read_unlock(); + } + link->conf->dtim_period = link->u.mgd.dtim_period ?: 1; + + if (link_id != assoc_data->assoc_link_id) { err = ieee80211_prep_channel(sdata, link, cbss, &link->u.mgd.conn_flags); if (err) { From 69e0d04e2b0b3a6ffdd2794eb65b3daf6ae5cbf7 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 30 Sep 2022 12:54:30 +0200 Subject: [PATCH 0083/2002] wifi: mac80211: prohibit IEEE80211_HT_CAP_DELAY_BA with MLO This won't work right at least with the code as it is, so at least for now just assume it's never set for MLO. It may very well never change, almost no drivers support it. Signed-off-by: Johannes Berg --- net/mac80211/main.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/net/mac80211/main.c b/net/mac80211/main.c index 25e72812000e..425793dd7c9c 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -1087,6 +1087,16 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) channels += sband->n_channels; + /* + * Due to the way the aggregation code handles this and it + * being an HT capability, we can't really support delayed + * BA in MLO (yet). + */ + if (WARN_ON(sband->ht_cap.ht_supported && + (sband->ht_cap.cap & IEEE80211_HT_CAP_DELAY_BA) && + hw->wiphy->flags & WIPHY_FLAG_SUPPORTS_MLO)) + return -EINVAL; + if (max_bitrates < sband->n_bitrates) max_bitrates = sband->n_bitrates; supp_ht = supp_ht || sband->ht_cap.ht_supported; From 2e82be13c6a33cb8befd557f168c92fb363107df Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 19 Sep 2022 19:01:19 +0200 Subject: [PATCH 0084/2002] wifi: mac80211: agg-rx: avoid band check If the deflink of the station is on 6 GHz, then it won't have HT. If at the same time we're using MLO, then vif.bss_conf isn't used, and thus vif.bss_conf.chandef.chan is NULL, causing the code to crash. Fix this by just checking for both HT and HE, and refusing the aggregation session if both are not present. This might be a bit wrong since it would accept an aggregation session from a peer that has HE but no HT on 2.4 or 5 GHz, but such a peer shouldn't exist in the first place, and it probably supports aggregation if it has HE support. Signed-off-by: Johannes Berg --- net/mac80211/agg-rx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/mac80211/agg-rx.c b/net/mac80211/agg-rx.c index f8bec5ee5b44..bd41022f4894 100644 --- a/net/mac80211/agg-rx.c +++ b/net/mac80211/agg-rx.c @@ -297,7 +297,7 @@ void ___ieee80211_start_rx_ba_session(struct sta_info *sta, } if (!sta->sta.deflink.ht_cap.ht_supported && - sta->sdata->vif.bss_conf.chandef.chan->band != NL80211_BAND_6GHZ) { + !sta->sta.deflink.he_cap.has_he) { ht_dbg(sta->sdata, "STA %pM erroneously requests BA session on tid %d w/o HT\n", sta->sta.addr, tid); From e8d0b807b4a223ae499d43e4baa6c45f946f42d5 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 30 Sep 2022 13:24:11 +0200 Subject: [PATCH 0085/2002] wifi: mac80211: remove support for AddBA with fragmentation HE added support for dynamic fragmentation inside aggregation sessions, but no existing driver ever advertises it. Thus, remove the code for now, it cannot work as-is in MLO. For it to properly work in MLO, we'd need to validate that the frag level is identical across all the link bands/iftypes, which is a good amount of complex code that's just not worth it as long as no driver has support for it. Signed-off-by: Johannes Berg --- net/mac80211/agg-rx.c | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/net/mac80211/agg-rx.c b/net/mac80211/agg-rx.c index bd41022f4894..c6fa53230450 100644 --- a/net/mac80211/agg-rx.c +++ b/net/mac80211/agg-rx.c @@ -183,34 +183,15 @@ static void ieee80211_add_addbaext(struct ieee80211_sub_if_data *sdata, const struct ieee80211_addba_ext_ie *req, u16 buf_size) { - struct ieee80211_supported_band *sband; struct ieee80211_addba_ext_ie *resp; - const struct ieee80211_sta_he_cap *he_cap; - u8 frag_level, cap_frag_level; u8 *pos; - sband = ieee80211_get_sband(sdata); - if (!sband) - return; - he_cap = ieee80211_get_he_iftype_cap(sband, - ieee80211_vif_type_p2p(&sdata->vif)); - if (!he_cap) - return; - pos = skb_put_zero(skb, 2 + sizeof(struct ieee80211_addba_ext_ie)); *pos++ = WLAN_EID_ADDBA_EXT; *pos++ = sizeof(struct ieee80211_addba_ext_ie); resp = (struct ieee80211_addba_ext_ie *)pos; resp->data = req->data & IEEE80211_ADDBA_EXT_NO_FRAG; - frag_level = u32_get_bits(req->data, - IEEE80211_ADDBA_EXT_FRAG_LEVEL_MASK); - cap_frag_level = u32_get_bits(he_cap->he_cap_elem.mac_cap_info[0], - IEEE80211_HE_MAC_CAP0_DYNAMIC_FRAG_MASK); - if (frag_level > cap_frag_level) - frag_level = cap_frag_level; - resp->data |= u8_encode_bits(frag_level, - IEEE80211_ADDBA_EXT_FRAG_LEVEL_MASK); resp->data |= u8_encode_bits(buf_size >> IEEE80211_ADDBA_EXT_BUF_SIZE_SHIFT, IEEE80211_ADDBA_EXT_BUF_SIZE_MASK); } From 9d13aff91ecd3f077b432df35291c945bde585be Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 5 Oct 2022 14:08:41 +0200 Subject: [PATCH 0086/2002] wifi: mac80211: fix ifdef symbol name This should of course be CONFIG_, not CPTCFG_, which is an artifact from working with backports. Fixes: 9dd1953846c7 ("wifi: nl80211/mac80211: clarify link ID in control port TX") Signed-off-by: Johannes Berg --- net/mac80211/tx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 27c964be102e..d9e7a2ed5d2c 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -2971,7 +2971,7 @@ static struct sk_buff *ieee80211_build_hdr(struct ieee80211_sub_if_data *sdata, if (pre_conf_link_id != link_id && link_id != IEEE80211_LINK_UNSPECIFIED) { -#ifdef CPTCFG_MAC80211_VERBOSE_DEBUG +#ifdef CONFIG_MAC80211_VERBOSE_DEBUG net_info_ratelimited("%s: dropped frame to %pM with bad link ID request (%d vs. %d)\n", sdata->name, hdr.addr1, pre_conf_link_id, link_id); From 4857ed9385fbd25060051cc42a93c3074de2f4dd Mon Sep 17 00:00:00 2001 From: Peter Seiderer Date: Thu, 15 Sep 2022 21:52:43 +0200 Subject: [PATCH 0087/2002] wifi: mac80211: minstrel_ht: remove unused has_mrr member from struct minstrel_priv Remove unused has_mrr (has multi-rate retry capabilities) member from struct minstrel_priv (only set once in minstrel_ht_alloc, never used again). Signed-off-by: Peter Seiderer Signed-off-by: Johannes Berg --- net/mac80211/rc80211_minstrel_ht.c | 3 --- net/mac80211/rc80211_minstrel_ht.h | 1 - 2 files changed, 4 deletions(-) diff --git a/net/mac80211/rc80211_minstrel_ht.c b/net/mac80211/rc80211_minstrel_ht.c index 7f3f5f51081d..f936d6b3f093 100644 --- a/net/mac80211/rc80211_minstrel_ht.c +++ b/net/mac80211/rc80211_minstrel_ht.c @@ -1963,9 +1963,6 @@ minstrel_ht_alloc(struct ieee80211_hw *hw) /* safe default, does not necessarily have to match hw properties */ mp->max_retry = 7; - if (hw->max_rates >= 4) - mp->has_mrr = true; - mp->hw = hw; mp->update_interval = HZ / 20; diff --git a/net/mac80211/rc80211_minstrel_ht.h b/net/mac80211/rc80211_minstrel_ht.h index 1766ff0c78d3..4be0401f7721 100644 --- a/net/mac80211/rc80211_minstrel_ht.h +++ b/net/mac80211/rc80211_minstrel_ht.h @@ -74,7 +74,6 @@ struct minstrel_priv { struct ieee80211_hw *hw; - bool has_mrr; unsigned int cw_min; unsigned int cw_max; unsigned int max_retry; From 0ff57171d6d225558c81a69439d5323e35b40549 Mon Sep 17 00:00:00 2001 From: Vinayak Yadawad Date: Wed, 7 Sep 2022 18:14:48 +0530 Subject: [PATCH 0088/2002] cfg80211: Update Transition Disable policy during port authorization In case of 4way handshake offload, transition disable policy updated by the AP during EAPOL 3/4 is not updated to the upper layer. This results in mismatch between transition disable policy between the upper layer and the driver. This patch addresses this issue by updating transition disable policy as part of port authorization indication. Signed-off-by: Vinayak Yadawad Signed-off-by: Johannes Berg --- .../wireless/broadcom/brcm80211/brcmfmac/cfg80211.c | 2 +- include/net/cfg80211.h | 4 +++- include/uapi/linux/nl80211.h | 3 +++ net/wireless/core.h | 5 ++++- net/wireless/nl80211.c | 8 +++++++- net/wireless/nl80211.h | 3 ++- net/wireless/sme.c | 12 ++++++++---- net/wireless/util.c | 4 +++- 8 files changed, 31 insertions(+), 10 deletions(-) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c index bf184c0e64cb..3f2336062217 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c @@ -6268,7 +6268,7 @@ done: brcmf_dbg(CONN, "Report roaming result\n"); if (profile->use_fwsup == BRCMF_PROFILE_FWSUP_1X && profile->is_ft) { - cfg80211_port_authorized(ndev, profile->bssid, GFP_KERNEL); + cfg80211_port_authorized(ndev, profile->bssid, NULL, 0, GFP_KERNEL); brcmf_dbg(CONN, "Report port authorized\n"); } diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 659dd1bee70f..11a370e64143 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -7683,6 +7683,8 @@ void cfg80211_roamed(struct net_device *dev, struct cfg80211_roam_info *info, * * @dev: network device * @bssid: the BSSID of the AP + * @td_bitmap: transition disable policy + * @td_bitmap_len: Length of transition disable policy * @gfp: allocation flags * * This function should be called by a driver that supports 4 way handshake @@ -7693,7 +7695,7 @@ void cfg80211_roamed(struct net_device *dev, struct cfg80211_roam_info *info, * indicate the 802.11 association. */ void cfg80211_port_authorized(struct net_device *dev, const u8 *bssid, - gfp_t gfp); + const u8* td_bitmap, u8 td_bitmap_len, gfp_t gfp); /** * cfg80211_disconnected - notify cfg80211 that connection was dropped diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index c32e7616a366..c14a91bbca7c 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -2749,6 +2749,8 @@ enum nl80211_commands { * When used with %NL80211_CMD_FRAME_TX_STATUS, indicates the ack RX * timestamp. When used with %NL80211_CMD_FRAME RX notification, indicates * the incoming frame RX timestamp. + * @NL80211_ATTR_TD_BITMAP: Transition Disable bitmap, for subsequent + * (re)associations. * @NUM_NL80211_ATTR: total number of nl80211_attrs available * @NL80211_ATTR_MAX: highest attribute number currently defined * @__NL80211_ATTR_AFTER_LAST: internal use @@ -3276,6 +3278,7 @@ enum nl80211_attrs { NL80211_ATTR_TX_HW_TIMESTAMP, NL80211_ATTR_RX_HW_TIMESTAMP, + NL80211_ATTR_TD_BITMAP, /* add attributes here, update the policy in nl80211.c */ diff --git a/net/wireless/core.h b/net/wireless/core.h index 775e16cb99ed..af85d8909935 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -271,6 +271,8 @@ struct cfg80211_event { } ij; struct { u8 bssid[ETH_ALEN]; + const u8 *td_bitmap; + u8 td_bitmap_len; } pa; }; }; @@ -409,7 +411,8 @@ int cfg80211_disconnect(struct cfg80211_registered_device *rdev, bool wextev); void __cfg80211_roamed(struct wireless_dev *wdev, struct cfg80211_roam_info *info); -void __cfg80211_port_authorized(struct wireless_dev *wdev, const u8 *bssid); +void __cfg80211_port_authorized(struct wireless_dev *wdev, const u8 *bssid, + const u8 *td_bitmap, u8 td_bitmap_len); int cfg80211_mgd_wext_connect(struct cfg80211_registered_device *rdev, struct wireless_dev *wdev); void cfg80211_autodisconnect_wk(struct work_struct *work); diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 1d0277758d0e..fe368af39554 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -17942,7 +17942,8 @@ void nl80211_send_roamed(struct cfg80211_registered_device *rdev, } void nl80211_send_port_authorized(struct cfg80211_registered_device *rdev, - struct net_device *netdev, const u8 *bssid) + struct net_device *netdev, const u8 *bssid, + const u8 *td_bitmap, u8 td_bitmap_len) { struct sk_buff *msg; void *hdr; @@ -17962,6 +17963,11 @@ void nl80211_send_port_authorized(struct cfg80211_registered_device *rdev, nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, bssid)) goto nla_put_failure; + if ((td_bitmap_len > 0) && td_bitmap) + if (nla_put(msg, NL80211_ATTR_TD_BITMAP, + td_bitmap_len, td_bitmap)) + goto nla_put_failure; + genlmsg_end(msg, hdr); genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0, diff --git a/net/wireless/nl80211.h b/net/wireless/nl80211.h index 855d540ddfb9..ba9457e94c43 100644 --- a/net/wireless/nl80211.h +++ b/net/wireless/nl80211.h @@ -83,7 +83,8 @@ void nl80211_send_roamed(struct cfg80211_registered_device *rdev, struct net_device *netdev, struct cfg80211_roam_info *info, gfp_t gfp); void nl80211_send_port_authorized(struct cfg80211_registered_device *rdev, - struct net_device *netdev, const u8 *bssid); + struct net_device *netdev, const u8 *bssid, + const u8 *td_bitmap, u8 td_bitmap_len); void nl80211_send_disconnected(struct cfg80211_registered_device *rdev, struct net_device *netdev, u16 reason, const u8 *ie, size_t ie_len, bool from_ap); diff --git a/net/wireless/sme.c b/net/wireless/sme.c index f94497e9db43..4b5b6ee0fe01 100644 --- a/net/wireless/sme.c +++ b/net/wireless/sme.c @@ -1251,7 +1251,8 @@ out: } EXPORT_SYMBOL(cfg80211_roamed); -void __cfg80211_port_authorized(struct wireless_dev *wdev, const u8 *bssid) +void __cfg80211_port_authorized(struct wireless_dev *wdev, const u8 *bssid, + const u8 *td_bitmap, u8 td_bitmap_len) { ASSERT_WDEV_LOCK(wdev); @@ -1264,11 +1265,11 @@ void __cfg80211_port_authorized(struct wireless_dev *wdev, const u8 *bssid) return; nl80211_send_port_authorized(wiphy_to_rdev(wdev->wiphy), wdev->netdev, - bssid); + bssid, td_bitmap, td_bitmap_len); } void cfg80211_port_authorized(struct net_device *dev, const u8 *bssid, - gfp_t gfp) + const u8 *td_bitmap, u8 td_bitmap_len, gfp_t gfp) { struct wireless_dev *wdev = dev->ieee80211_ptr; struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy); @@ -1278,12 +1279,15 @@ void cfg80211_port_authorized(struct net_device *dev, const u8 *bssid, if (WARN_ON(!bssid)) return; - ev = kzalloc(sizeof(*ev), gfp); + ev = kzalloc(sizeof(*ev) + td_bitmap_len, gfp); if (!ev) return; ev->type = EVENT_PORT_AUTHORIZED; memcpy(ev->pa.bssid, bssid, ETH_ALEN); + ev->pa.td_bitmap = ((u8 *)ev) + sizeof(*ev); + ev->pa.td_bitmap_len = td_bitmap_len; + memcpy((void *)ev->pa.td_bitmap, td_bitmap, td_bitmap_len); /* * Use the wdev event list so that if there are pending diff --git a/net/wireless/util.c b/net/wireless/util.c index 01493568a21d..f09d528e5199 100644 --- a/net/wireless/util.c +++ b/net/wireless/util.c @@ -988,7 +988,9 @@ void cfg80211_process_wdev_events(struct wireless_dev *wdev) __cfg80211_leave(wiphy_to_rdev(wdev->wiphy), wdev); break; case EVENT_PORT_AUTHORIZED: - __cfg80211_port_authorized(wdev, ev->pa.bssid); + __cfg80211_port_authorized(wdev, ev->pa.bssid, + ev->pa.td_bitmap, + ev->pa.td_bitmap_len); break; } wdev_unlock(wdev); From 2e30960097f6bc1a0dff89c0bcbc4138e3506d7c Mon Sep 17 00:00:00 2001 From: Jie Meng Date: Wed, 5 Oct 2022 10:00:39 -0700 Subject: [PATCH 0089/2002] bpf, x64: Remove unnecessary check on existence of SSE2 SSE2 and hence lfence are architectural in x86-64 and no need to check whether they're supported in CPU. SSE2's CPUID flag is still set to maintain backward compatibility with older code or code shared with x86, but bpf_jit_comp.c is compiled under x86-64 exclusively so the check is redundant. Signed-off-by: Jie Meng Signed-off-by: Daniel Borkmann Acked-by: KP Singh Link: https://lore.kernel.org/bpf/20221005170039.3936894-1-jmeng@fb.com --- arch/x86/net/bpf_jit_comp.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/arch/x86/net/bpf_jit_comp.c b/arch/x86/net/bpf_jit_comp.c index 99620428ad78..0abd082786e7 100644 --- a/arch/x86/net/bpf_jit_comp.c +++ b/arch/x86/net/bpf_jit_comp.c @@ -1226,8 +1226,7 @@ static int do_jit(struct bpf_prog *bpf_prog, int *addrs, u8 *image, u8 *rw_image /* speculation barrier */ case BPF_ST | BPF_NOSPEC: - if (boot_cpu_has(X86_FEATURE_XMM2)) - EMIT_LFENCE(); + EMIT_LFENCE(); break; /* ST: *(u8*)(dst_reg + off) = imm */ From c850e31f79f049af5022f07cd9961605b4470d0b Mon Sep 17 00:00:00 2001 From: Alexander Wetzel Date: Sun, 9 Oct 2022 18:30:38 +0200 Subject: [PATCH 0090/2002] wifi: mac80211: add internal handler for wake_tx_queue Start to align the TX handling to only use internal TX queues (iTXQs): Provide a handler for drivers not having a custom wake_tx_queue callback and update the documentation. Signed-off-by: Alexander Wetzel Signed-off-by: Johannes Berg --- include/net/mac80211.h | 51 ++++++++++++++++++++++++++---------------- net/mac80211/util.c | 46 +++++++++++++++++++++++++++++++++++++ 2 files changed, 78 insertions(+), 19 deletions(-) diff --git a/include/net/mac80211.h b/include/net/mac80211.h index cda4584dfd51..721c450a9ccd 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -89,15 +89,13 @@ /** * DOC: mac80211 software tx queueing * - * mac80211 provides an optional intermediate queueing implementation designed - * to allow the driver to keep hardware queues short and provide some fairness - * between different stations/interfaces. - * In this model, the driver pulls data frames from the mac80211 queue instead - * of letting mac80211 push them via drv_tx(). - * Other frames (e.g. control or management) are still pushed using drv_tx(). + * mac80211 uses an intermediate queueing implementation, designed to allow the + * driver to keep hardware queues short and to provide some fairness between + * different stations/interfaces. * - * Drivers indicate that they use this model by implementing the .wake_tx_queue - * driver operation. + * Drivers must provide the .wake_tx_queue driver operation by either + * linking it to ieee80211_handle_wake_tx_queue() or implementing a custom + * handler. * * Intermediate queues (struct ieee80211_txq) are kept per-sta per-tid, with * another per-sta for non-data/non-mgmt and bufferable management frames, and @@ -106,9 +104,12 @@ * The driver is expected to initialize its private per-queue data for stations * and interfaces in the .add_interface and .sta_add ops. * - * The driver can't access the queue directly. To dequeue a frame from a - * txq, it calls ieee80211_tx_dequeue(). Whenever mac80211 adds a new frame to a - * queue, it calls the .wake_tx_queue driver op. + * The driver can't access the internal TX queues (iTXQs) directly. + * Whenever mac80211 adds a new frame to a queue, it calls the .wake_tx_queue + * driver op. + * Drivers implementing a custom .wake_tx_queue op can get them by calling + * ieee80211_tx_dequeue(). Drivers using ieee80211_handle_wake_tx_queue() will + * simply get the individual frames pushed via the .tx driver operation. * * Drivers can optionally delegate responsibility for scheduling queues to * mac80211, to take advantage of airtime fairness accounting. In this case, to @@ -1826,7 +1827,7 @@ struct ieee80211_vif_cfg { * for this interface. * @drv_priv: data area for driver use, will always be aligned to * sizeof(void \*). - * @txq: the multicast data TX queue (if driver uses the TXQ abstraction) + * @txq: the multicast data TX queue * @txqs_stopped: per AC flag to indicate that intermediate TXQs are stopped, * protected by fq->lock. * @offload_flags: 802.3 -> 802.11 enapsulation offload flags, see @@ -2259,8 +2260,8 @@ struct ieee80211_link_sta { * For non MLO STA it will point to the deflink data. For MLO STA * ieee80211_sta_recalc_aggregates() must be called to update it. * @support_p2p_ps: indicates whether the STA supports P2P PS mechanism or not. - * @txq: per-TID data TX queues (if driver uses the TXQ abstraction); note that - * the last entry (%IEEE80211_NUM_TIDS) is used for non-data frames + * @txq: per-TID data TX queues; note that the last entry (%IEEE80211_NUM_TIDS) + * is used for non-data frames * @deflink: This holds the default link STA information, for non MLO STA all link * specific STA information is accessed through @deflink or through * link[0] which points to address of @deflink. For MLO Link STA @@ -5713,7 +5714,7 @@ void ieee80211_key_replay(struct ieee80211_key_conf *keyconf); * @hw: pointer as obtained from ieee80211_alloc_hw(). * @queue: queue number (counted from zero). * - * Drivers should use this function instead of netif_wake_queue. + * Drivers must use this function instead of netif_wake_queue. */ void ieee80211_wake_queue(struct ieee80211_hw *hw, int queue); @@ -5722,7 +5723,7 @@ void ieee80211_wake_queue(struct ieee80211_hw *hw, int queue); * @hw: pointer as obtained from ieee80211_alloc_hw(). * @queue: queue number (counted from zero). * - * Drivers should use this function instead of netif_stop_queue. + * Drivers must use this function instead of netif_stop_queue. */ void ieee80211_stop_queue(struct ieee80211_hw *hw, int queue); @@ -5731,7 +5732,7 @@ void ieee80211_stop_queue(struct ieee80211_hw *hw, int queue); * @hw: pointer as obtained from ieee80211_alloc_hw(). * @queue: queue number (counted from zero). * - * Drivers should use this function instead of netif_stop_queue. + * Drivers must use this function instead of netif_queue_stopped. * * Return: %true if the queue is stopped. %false otherwise. */ @@ -5742,7 +5743,7 @@ int ieee80211_queue_stopped(struct ieee80211_hw *hw, int queue); * ieee80211_stop_queues - stop all queues * @hw: pointer as obtained from ieee80211_alloc_hw(). * - * Drivers should use this function instead of netif_stop_queue. + * Drivers must use this function instead of netif_tx_stop_all_queues. */ void ieee80211_stop_queues(struct ieee80211_hw *hw); @@ -5750,7 +5751,7 @@ void ieee80211_stop_queues(struct ieee80211_hw *hw); * ieee80211_wake_queues - wake all queues * @hw: pointer as obtained from ieee80211_alloc_hw(). * - * Drivers should use this function instead of netif_wake_queue. + * Drivers must use this function instead of netif_tx_wake_all_queues. */ void ieee80211_wake_queues(struct ieee80211_hw *hw); @@ -6971,6 +6972,18 @@ static inline struct sk_buff *ieee80211_tx_dequeue_ni(struct ieee80211_hw *hw, return skb; } +/** + * ieee80211_handle_wake_tx_queue - mac80211 handler for wake_tx_queue callback + * + * @hw: pointer as obtained from wake_tx_queue() callback(). + * @txq: pointer as obtained from wake_tx_queue() callback(). + * + * Drivers can use this function for the mandatory mac80211 wake_tx_queue + * callback in struct ieee80211_ops. They should not call this function. + */ +void ieee80211_handle_wake_tx_queue(struct ieee80211_hw *hw, + struct ieee80211_txq *txq); + /** * ieee80211_next_txq - get next tx queue to pull packets from * diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 40b75fa82b15..a4bf86f17c39 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -288,6 +288,52 @@ __le16 ieee80211_ctstoself_duration(struct ieee80211_hw *hw, } EXPORT_SYMBOL(ieee80211_ctstoself_duration); +static void wake_tx_push_queue(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata, + struct ieee80211_txq *queue) +{ + int q = sdata->vif.hw_queue[queue->ac]; + struct ieee80211_tx_control control = { + .sta = queue->sta, + }; + struct sk_buff *skb; + unsigned long flags; + bool q_stopped; + + while (1) { + spin_lock_irqsave(&local->queue_stop_reason_lock, flags); + q_stopped = local->queue_stop_reasons[q]; + spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); + + if (q_stopped) + break; + + skb = ieee80211_tx_dequeue(&local->hw, queue); + if (!skb) + break; + + drv_tx(local, &control, skb); + } +} + +/* wake_tx_queue handler for driver not implementing a custom one*/ +void ieee80211_handle_wake_tx_queue(struct ieee80211_hw *hw, + struct ieee80211_txq *txq) +{ + struct ieee80211_local *local = hw_to_local(hw); + struct ieee80211_sub_if_data *sdata = vif_to_sdata(txq->vif); + struct ieee80211_txq *queue; + + /* Use ieee80211_next_txq() for airtime fairness accounting */ + ieee80211_txq_schedule_start(hw, txq->ac); + while ((queue = ieee80211_next_txq(hw, txq->ac))) { + wake_tx_push_queue(local, sdata, queue); + ieee80211_return_txq(hw, queue, false); + } + ieee80211_txq_schedule_end(hw, txq->ac); +} +EXPORT_SYMBOL(ieee80211_handle_wake_tx_queue); + static void __ieee80211_wake_txqs(struct ieee80211_sub_if_data *sdata, int ac) { struct ieee80211_local *local = sdata->local; From a790cc3a4fad75048295571a350b95b87e022a5a Mon Sep 17 00:00:00 2001 From: Alexander Wetzel Date: Sun, 9 Oct 2022 18:30:39 +0200 Subject: [PATCH 0091/2002] wifi: mac80211: add wake_tx_queue callback to drivers mac80211 is fully switching over to the internal TX queue (iTXQ) implementation. Update all drivers not yet providing the now mandatory wake_tx_queue() callback. As an side effect the netdev interfaces of all updated drivers will switch to the noqueue qdisc. Signed-off-by: Alexander Wetzel [add staging drivers] Signed-off-by: Johannes Berg --- drivers/net/wireless/admtek/adm8211.c | 1 + drivers/net/wireless/ath/ar5523/ar5523.c | 1 + drivers/net/wireless/ath/ath11k/mac.c | 1 + drivers/net/wireless/ath/ath5k/mac80211-ops.c | 1 + drivers/net/wireless/ath/ath9k/htc_drv_main.c | 1 + drivers/net/wireless/ath/carl9170/main.c | 1 + drivers/net/wireless/ath/wcn36xx/main.c | 1 + drivers/net/wireless/atmel/at76c50x-usb.c | 1 + drivers/net/wireless/broadcom/b43/main.c | 1 + drivers/net/wireless/broadcom/b43legacy/main.c | 1 + drivers/net/wireless/broadcom/brcm80211/brcmsmac/mac80211_if.c | 1 + drivers/net/wireless/intel/iwlegacy/3945-mac.c | 1 + drivers/net/wireless/intel/iwlegacy/4965-mac.c | 1 + drivers/net/wireless/intel/iwlwifi/dvm/mac80211.c | 1 + drivers/net/wireless/intersil/p54/main.c | 1 + drivers/net/wireless/mac80211_hwsim.c | 1 + drivers/net/wireless/marvell/libertas_tf/main.c | 1 + drivers/net/wireless/marvell/mwl8k.c | 1 + drivers/net/wireless/mediatek/mt7601u/main.c | 1 + drivers/net/wireless/purelifi/plfxlc/mac.c | 1 + drivers/net/wireless/ralink/rt2x00/rt2400pci.c | 1 + drivers/net/wireless/ralink/rt2x00/rt2500pci.c | 1 + drivers/net/wireless/ralink/rt2x00/rt2500usb.c | 1 + drivers/net/wireless/ralink/rt2x00/rt2800pci.c | 1 + drivers/net/wireless/ralink/rt2x00/rt2800soc.c | 1 + drivers/net/wireless/ralink/rt2x00/rt2800usb.c | 1 + drivers/net/wireless/ralink/rt2x00/rt61pci.c | 1 + drivers/net/wireless/ralink/rt2x00/rt73usb.c | 1 + drivers/net/wireless/realtek/rtl818x/rtl8180/dev.c | 1 + drivers/net/wireless/realtek/rtl818x/rtl8187/dev.c | 1 + drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c | 1 + drivers/net/wireless/realtek/rtlwifi/core.c | 1 + drivers/net/wireless/realtek/rtw88/mac80211.c | 1 + drivers/net/wireless/realtek/rtw89/mac80211.c | 1 + drivers/net/wireless/rsi/rsi_91x_mac80211.c | 1 + drivers/net/wireless/silabs/wfx/main.c | 1 + drivers/net/wireless/st/cw1200/main.c | 1 + drivers/net/wireless/ti/wl1251/main.c | 1 + drivers/net/wireless/ti/wlcore/main.c | 1 + drivers/net/wireless/zydas/zd1211rw/zd_mac.c | 1 + drivers/staging/vt6655/device_main.c | 1 + drivers/staging/vt6656/main_usb.c | 1 + 42 files changed, 42 insertions(+) diff --git a/drivers/net/wireless/admtek/adm8211.c b/drivers/net/wireless/admtek/adm8211.c index 6bee16b207d1..2fceea9f6550 100644 --- a/drivers/net/wireless/admtek/adm8211.c +++ b/drivers/net/wireless/admtek/adm8211.c @@ -1760,6 +1760,7 @@ static int adm8211_alloc_rings(struct ieee80211_hw *dev) static const struct ieee80211_ops adm8211_ops = { .tx = adm8211_tx, + .wake_tx_queue = ieee80211_handle_wake_tx_queue, .start = adm8211_start, .stop = adm8211_stop, .add_interface = adm8211_add_interface, diff --git a/drivers/net/wireless/ath/ar5523/ar5523.c b/drivers/net/wireless/ath/ar5523/ar5523.c index 6f937d2cc126..a184c09d2f0f 100644 --- a/drivers/net/wireless/ath/ar5523/ar5523.c +++ b/drivers/net/wireless/ath/ar5523/ar5523.c @@ -1355,6 +1355,7 @@ static const struct ieee80211_ops ar5523_ops = { .start = ar5523_start, .stop = ar5523_stop, .tx = ar5523_tx, + .wake_tx_queue = ieee80211_handle_wake_tx_queue, .set_rts_threshold = ar5523_set_rts_threshold, .add_interface = ar5523_add_interface, .remove_interface = ar5523_remove_interface, diff --git a/drivers/net/wireless/ath/ath11k/mac.c b/drivers/net/wireless/ath/ath11k/mac.c index 84d956ad4093..8094384b8616 100644 --- a/drivers/net/wireless/ath/ath11k/mac.c +++ b/drivers/net/wireless/ath/ath11k/mac.c @@ -8538,6 +8538,7 @@ err_fallback: static const struct ieee80211_ops ath11k_ops = { .tx = ath11k_mac_op_tx, + .wake_tx_queue = ieee80211_handle_wake_tx_queue, .start = ath11k_mac_op_start, .stop = ath11k_mac_op_stop, .reconfig_complete = ath11k_mac_op_reconfig_complete, diff --git a/drivers/net/wireless/ath/ath5k/mac80211-ops.c b/drivers/net/wireless/ath/ath5k/mac80211-ops.c index ed5d2160a72a..11ed30d6b595 100644 --- a/drivers/net/wireless/ath/ath5k/mac80211-ops.c +++ b/drivers/net/wireless/ath/ath5k/mac80211-ops.c @@ -781,6 +781,7 @@ static int ath5k_set_ringparam(struct ieee80211_hw *hw, u32 tx, u32 rx) const struct ieee80211_ops ath5k_hw_ops = { .tx = ath5k_tx, + .wake_tx_queue = ieee80211_handle_wake_tx_queue, .start = ath5k_start, .stop = ath5k_stop, .add_interface = ath5k_add_interface, diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_main.c b/drivers/net/wireless/ath/ath9k/htc_drv_main.c index 61875c45366b..51766de5ec3b 100644 --- a/drivers/net/wireless/ath/ath9k/htc_drv_main.c +++ b/drivers/net/wireless/ath/ath9k/htc_drv_main.c @@ -1870,6 +1870,7 @@ static void ath9k_htc_channel_switch_beacon(struct ieee80211_hw *hw, struct ieee80211_ops ath9k_htc_ops = { .tx = ath9k_htc_tx, + .wake_tx_queue = ieee80211_handle_wake_tx_queue, .start = ath9k_htc_start, .stop = ath9k_htc_stop, .add_interface = ath9k_htc_add_interface, diff --git a/drivers/net/wireless/ath/carl9170/main.c b/drivers/net/wireless/ath/carl9170/main.c index 1540e9827f48..524327d24964 100644 --- a/drivers/net/wireless/ath/carl9170/main.c +++ b/drivers/net/wireless/ath/carl9170/main.c @@ -1715,6 +1715,7 @@ static const struct ieee80211_ops carl9170_ops = { .start = carl9170_op_start, .stop = carl9170_op_stop, .tx = carl9170_op_tx, + .wake_tx_queue = ieee80211_handle_wake_tx_queue, .flush = carl9170_op_flush, .add_interface = carl9170_op_add_interface, .remove_interface = carl9170_op_remove_interface, diff --git a/drivers/net/wireless/ath/wcn36xx/main.c b/drivers/net/wireless/ath/wcn36xx/main.c index 6b8d2889d73f..3b79cc1c7c5b 100644 --- a/drivers/net/wireless/ath/wcn36xx/main.c +++ b/drivers/net/wireless/ath/wcn36xx/main.c @@ -1361,6 +1361,7 @@ static const struct ieee80211_ops wcn36xx_ops = { .prepare_multicast = wcn36xx_prepare_multicast, .configure_filter = wcn36xx_configure_filter, .tx = wcn36xx_tx, + .wake_tx_queue = ieee80211_handle_wake_tx_queue, .set_key = wcn36xx_set_key, .hw_scan = wcn36xx_hw_scan, .cancel_hw_scan = wcn36xx_cancel_hw_scan, diff --git a/drivers/net/wireless/atmel/at76c50x-usb.c b/drivers/net/wireless/atmel/at76c50x-usb.c index 24e609c1f523..009bca34ece3 100644 --- a/drivers/net/wireless/atmel/at76c50x-usb.c +++ b/drivers/net/wireless/atmel/at76c50x-usb.c @@ -2179,6 +2179,7 @@ static int at76_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, static const struct ieee80211_ops at76_ops = { .tx = at76_mac80211_tx, + .wake_tx_queue = ieee80211_handle_wake_tx_queue, .add_interface = at76_add_interface, .remove_interface = at76_remove_interface, .config = at76_config, diff --git a/drivers/net/wireless/broadcom/b43/main.c b/drivers/net/wireless/broadcom/b43/main.c index b2539a916fd0..298febbdffc0 100644 --- a/drivers/net/wireless/broadcom/b43/main.c +++ b/drivers/net/wireless/broadcom/b43/main.c @@ -5171,6 +5171,7 @@ static int b43_op_get_survey(struct ieee80211_hw *hw, int idx, static const struct ieee80211_ops b43_hw_ops = { .tx = b43_op_tx, + .wake_tx_queue = ieee80211_handle_wake_tx_queue, .conf_tx = b43_op_conf_tx, .add_interface = b43_op_add_interface, .remove_interface = b43_op_remove_interface, diff --git a/drivers/net/wireless/broadcom/b43legacy/main.c b/drivers/net/wireless/broadcom/b43legacy/main.c index 4022c544aefe..760136638a95 100644 --- a/drivers/net/wireless/broadcom/b43legacy/main.c +++ b/drivers/net/wireless/broadcom/b43legacy/main.c @@ -3532,6 +3532,7 @@ static int b43legacy_op_get_survey(struct ieee80211_hw *hw, int idx, static const struct ieee80211_ops b43legacy_hw_ops = { .tx = b43legacy_op_tx, + .wake_tx_queue = ieee80211_handle_wake_tx_queue, .conf_tx = b43legacy_op_conf_tx, .add_interface = b43legacy_op_add_interface, .remove_interface = b43legacy_op_remove_interface, diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/mac80211_if.c b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/mac80211_if.c index a4034d44609b..a8333e6adbda 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/mac80211_if.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/mac80211_if.c @@ -962,6 +962,7 @@ static int brcms_ops_beacon_set_tim(struct ieee80211_hw *hw, static const struct ieee80211_ops brcms_ops = { .tx = brcms_ops_tx, + .wake_tx_queue = ieee80211_handle_wake_tx_queue, .start = brcms_ops_start, .stop = brcms_ops_stop, .add_interface = brcms_ops_add_interface, diff --git a/drivers/net/wireless/intel/iwlegacy/3945-mac.c b/drivers/net/wireless/intel/iwlegacy/3945-mac.c index 7352d5b2095f..c4503308f3b0 100644 --- a/drivers/net/wireless/intel/iwlegacy/3945-mac.c +++ b/drivers/net/wireless/intel/iwlegacy/3945-mac.c @@ -3435,6 +3435,7 @@ static const struct attribute_group il3945_attribute_group = { static struct ieee80211_ops il3945_mac_ops __ro_after_init = { .tx = il3945_mac_tx, + .wake_tx_queue = ieee80211_handle_wake_tx_queue, .start = il3945_mac_start, .stop = il3945_mac_stop, .add_interface = il_mac_add_interface, diff --git a/drivers/net/wireless/intel/iwlegacy/4965-mac.c b/drivers/net/wireless/intel/iwlegacy/4965-mac.c index 943de47170c7..721b4042b4bf 100644 --- a/drivers/net/wireless/intel/iwlegacy/4965-mac.c +++ b/drivers/net/wireless/intel/iwlegacy/4965-mac.c @@ -6304,6 +6304,7 @@ il4965_tx_queue_set_status(struct il_priv *il, struct il_tx_queue *txq, static const struct ieee80211_ops il4965_mac_ops = { .tx = il4965_mac_tx, + .wake_tx_queue = ieee80211_handle_wake_tx_queue, .start = il4965_mac_start, .stop = il4965_mac_stop, .add_interface = il_mac_add_interface, diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/dvm/mac80211.c index f4070fddc8c7..b1939ff275b5 100644 --- a/drivers/net/wireless/intel/iwlwifi/dvm/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/dvm/mac80211.c @@ -1571,6 +1571,7 @@ static void iwlagn_mac_sta_notify(struct ieee80211_hw *hw, const struct ieee80211_ops iwlagn_hw_ops = { .tx = iwlagn_mac_tx, + .wake_tx_queue = ieee80211_handle_wake_tx_queue, .start = iwlagn_mac_start, .stop = iwlagn_mac_stop, #ifdef CONFIG_PM_SLEEP diff --git a/drivers/net/wireless/intersil/p54/main.c b/drivers/net/wireless/intersil/p54/main.c index e127453ab51a..c6084683aedd 100644 --- a/drivers/net/wireless/intersil/p54/main.c +++ b/drivers/net/wireless/intersil/p54/main.c @@ -705,6 +705,7 @@ static void p54_set_coverage_class(struct ieee80211_hw *dev, static const struct ieee80211_ops p54_ops = { .tx = p54_tx_80211, + .wake_tx_queue = ieee80211_handle_wake_tx_queue, .start = p54_start, .stop = p54_stop, .add_interface = p54_add_interface, diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c index df51b5b1f171..19c7fa29ec78 100644 --- a/drivers/net/wireless/mac80211_hwsim.c +++ b/drivers/net/wireless/mac80211_hwsim.c @@ -3104,6 +3104,7 @@ static int mac80211_hwsim_change_sta_links(struct ieee80211_hw *hw, #define HWSIM_COMMON_OPS \ .tx = mac80211_hwsim_tx, \ + .wake_tx_queue = ieee80211_handle_wake_tx_queue, \ .start = mac80211_hwsim_start, \ .stop = mac80211_hwsim_stop, \ .add_interface = mac80211_hwsim_add_interface, \ diff --git a/drivers/net/wireless/marvell/libertas_tf/main.c b/drivers/net/wireless/marvell/libertas_tf/main.c index 74c4942b9a5a..199d33ed3bb9 100644 --- a/drivers/net/wireless/marvell/libertas_tf/main.c +++ b/drivers/net/wireless/marvell/libertas_tf/main.c @@ -474,6 +474,7 @@ static int lbtf_op_get_survey(struct ieee80211_hw *hw, int idx, static const struct ieee80211_ops lbtf_ops = { .tx = lbtf_op_tx, + .wake_tx_queue = ieee80211_handle_wake_tx_queue, .start = lbtf_op_start, .stop = lbtf_op_stop, .add_interface = lbtf_op_add_interface, diff --git a/drivers/net/wireless/marvell/mwl8k.c b/drivers/net/wireless/marvell/mwl8k.c index 4dc7e2e53b81..13bcb123d122 100644 --- a/drivers/net/wireless/marvell/mwl8k.c +++ b/drivers/net/wireless/marvell/mwl8k.c @@ -5611,6 +5611,7 @@ static void mwl8k_sw_scan_complete(struct ieee80211_hw *hw, static const struct ieee80211_ops mwl8k_ops = { .tx = mwl8k_tx, + .wake_tx_queue = ieee80211_handle_wake_tx_queue, .start = mwl8k_start, .stop = mwl8k_stop, .add_interface = mwl8k_add_interface, diff --git a/drivers/net/wireless/mediatek/mt7601u/main.c b/drivers/net/wireless/mediatek/mt7601u/main.c index 6c9c7a61c5c9..c8d332456a6b 100644 --- a/drivers/net/wireless/mediatek/mt7601u/main.c +++ b/drivers/net/wireless/mediatek/mt7601u/main.c @@ -406,6 +406,7 @@ out: const struct ieee80211_ops mt7601u_ops = { .tx = mt7601u_tx, + .wake_tx_queue = ieee80211_handle_wake_tx_queue, .start = mt7601u_start, .stop = mt7601u_stop, .add_interface = mt7601u_add_interface, diff --git a/drivers/net/wireless/purelifi/plfxlc/mac.c b/drivers/net/wireless/purelifi/plfxlc/mac.c index d3cdffbded69..94ee831b5de3 100644 --- a/drivers/net/wireless/purelifi/plfxlc/mac.c +++ b/drivers/net/wireless/purelifi/plfxlc/mac.c @@ -686,6 +686,7 @@ static int plfxlc_set_rts_threshold(struct ieee80211_hw *hw, u32 value) static const struct ieee80211_ops plfxlc_ops = { .tx = plfxlc_op_tx, + .wake_tx_queue = ieee80211_handle_wake_tx_queue, .start = plfxlc_op_start, .stop = plfxlc_op_stop, .add_interface = plfxlc_op_add_interface, diff --git a/drivers/net/wireless/ralink/rt2x00/rt2400pci.c b/drivers/net/wireless/ralink/rt2x00/rt2400pci.c index 273c5eac3362..bb5685826c18 100644 --- a/drivers/net/wireless/ralink/rt2x00/rt2400pci.c +++ b/drivers/net/wireless/ralink/rt2x00/rt2400pci.c @@ -1706,6 +1706,7 @@ static int rt2400pci_tx_last_beacon(struct ieee80211_hw *hw) static const struct ieee80211_ops rt2400pci_mac80211_ops = { .tx = rt2x00mac_tx, + .wake_tx_queue = ieee80211_handle_wake_tx_queue, .start = rt2x00mac_start, .stop = rt2x00mac_stop, .add_interface = rt2x00mac_add_interface, diff --git a/drivers/net/wireless/ralink/rt2x00/rt2500pci.c b/drivers/net/wireless/ralink/rt2x00/rt2500pci.c index 8faa0a80e73a..43d2c1931d2b 100644 --- a/drivers/net/wireless/ralink/rt2x00/rt2500pci.c +++ b/drivers/net/wireless/ralink/rt2x00/rt2500pci.c @@ -2004,6 +2004,7 @@ static int rt2500pci_tx_last_beacon(struct ieee80211_hw *hw) static const struct ieee80211_ops rt2500pci_mac80211_ops = { .tx = rt2x00mac_tx, + .wake_tx_queue = ieee80211_handle_wake_tx_queue, .start = rt2x00mac_start, .stop = rt2x00mac_stop, .add_interface = rt2x00mac_add_interface, diff --git a/drivers/net/wireless/ralink/rt2x00/rt2500usb.c b/drivers/net/wireless/ralink/rt2x00/rt2500usb.c index bb5ed6630645..a2696e24ae2b 100644 --- a/drivers/net/wireless/ralink/rt2x00/rt2500usb.c +++ b/drivers/net/wireless/ralink/rt2x00/rt2500usb.c @@ -1795,6 +1795,7 @@ static int rt2500usb_probe_hw(struct rt2x00_dev *rt2x00dev) static const struct ieee80211_ops rt2500usb_mac80211_ops = { .tx = rt2x00mac_tx, + .wake_tx_queue = ieee80211_handle_wake_tx_queue, .start = rt2x00mac_start, .stop = rt2x00mac_stop, .add_interface = rt2x00mac_add_interface, diff --git a/drivers/net/wireless/ralink/rt2x00/rt2800pci.c b/drivers/net/wireless/ralink/rt2x00/rt2800pci.c index 1fde0e767ce3..dcb56f708a5f 100644 --- a/drivers/net/wireless/ralink/rt2x00/rt2800pci.c +++ b/drivers/net/wireless/ralink/rt2x00/rt2800pci.c @@ -288,6 +288,7 @@ static int rt2800pci_read_eeprom(struct rt2x00_dev *rt2x00dev) static const struct ieee80211_ops rt2800pci_mac80211_ops = { .tx = rt2x00mac_tx, + .wake_tx_queue = ieee80211_handle_wake_tx_queue, .start = rt2x00mac_start, .stop = rt2x00mac_stop, .add_interface = rt2x00mac_add_interface, diff --git a/drivers/net/wireless/ralink/rt2x00/rt2800soc.c b/drivers/net/wireless/ralink/rt2x00/rt2800soc.c index bbfe1425c0ee..7118d4f9038d 100644 --- a/drivers/net/wireless/ralink/rt2x00/rt2800soc.c +++ b/drivers/net/wireless/ralink/rt2x00/rt2800soc.c @@ -133,6 +133,7 @@ static int rt2800soc_write_firmware(struct rt2x00_dev *rt2x00dev, static const struct ieee80211_ops rt2800soc_mac80211_ops = { .tx = rt2x00mac_tx, + .wake_tx_queue = ieee80211_handle_wake_tx_queue, .start = rt2x00mac_start, .stop = rt2x00mac_stop, .add_interface = rt2x00mac_add_interface, diff --git a/drivers/net/wireless/ralink/rt2x00/rt2800usb.c b/drivers/net/wireless/ralink/rt2x00/rt2800usb.c index a3ffd1b0c9bc..b2a8e75a901b 100644 --- a/drivers/net/wireless/ralink/rt2x00/rt2800usb.c +++ b/drivers/net/wireless/ralink/rt2x00/rt2800usb.c @@ -630,6 +630,7 @@ static int rt2800usb_probe_hw(struct rt2x00_dev *rt2x00dev) static const struct ieee80211_ops rt2800usb_mac80211_ops = { .tx = rt2x00mac_tx, + .wake_tx_queue = ieee80211_handle_wake_tx_queue, .start = rt2x00mac_start, .stop = rt2x00mac_stop, .add_interface = rt2x00mac_add_interface, diff --git a/drivers/net/wireless/ralink/rt2x00/rt61pci.c b/drivers/net/wireless/ralink/rt2x00/rt61pci.c index d92f9eb07dc9..b1679a8e4610 100644 --- a/drivers/net/wireless/ralink/rt2x00/rt61pci.c +++ b/drivers/net/wireless/ralink/rt2x00/rt61pci.c @@ -2873,6 +2873,7 @@ static u64 rt61pci_get_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif) static const struct ieee80211_ops rt61pci_mac80211_ops = { .tx = rt2x00mac_tx, + .wake_tx_queue = ieee80211_handle_wake_tx_queue, .start = rt2x00mac_start, .stop = rt2x00mac_stop, .add_interface = rt2x00mac_add_interface, diff --git a/drivers/net/wireless/ralink/rt2x00/rt73usb.c b/drivers/net/wireless/ralink/rt2x00/rt73usb.c index e3269fd7c59e..e2f4f40cb7ee 100644 --- a/drivers/net/wireless/ralink/rt2x00/rt73usb.c +++ b/drivers/net/wireless/ralink/rt2x00/rt73usb.c @@ -2292,6 +2292,7 @@ static u64 rt73usb_get_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif) static const struct ieee80211_ops rt73usb_mac80211_ops = { .tx = rt2x00mac_tx, + .wake_tx_queue = ieee80211_handle_wake_tx_queue, .start = rt2x00mac_start, .stop = rt2x00mac_stop, .add_interface = rt2x00mac_add_interface, diff --git a/drivers/net/wireless/realtek/rtl818x/rtl8180/dev.c b/drivers/net/wireless/realtek/rtl818x/rtl8180/dev.c index cdfe08078c57..f6c25a52b69a 100644 --- a/drivers/net/wireless/realtek/rtl818x/rtl8180/dev.c +++ b/drivers/net/wireless/realtek/rtl818x/rtl8180/dev.c @@ -1608,6 +1608,7 @@ static void rtl8180_configure_filter(struct ieee80211_hw *dev, static const struct ieee80211_ops rtl8180_ops = { .tx = rtl8180_tx, + .wake_tx_queue = ieee80211_handle_wake_tx_queue, .start = rtl8180_start, .stop = rtl8180_stop, .add_interface = rtl8180_add_interface, diff --git a/drivers/net/wireless/realtek/rtl818x/rtl8187/dev.c b/drivers/net/wireless/realtek/rtl818x/rtl8187/dev.c index c0f6e9c6d03e..04945f905d6d 100644 --- a/drivers/net/wireless/realtek/rtl818x/rtl8187/dev.c +++ b/drivers/net/wireless/realtek/rtl818x/rtl8187/dev.c @@ -1378,6 +1378,7 @@ static int rtl8187_conf_tx(struct ieee80211_hw *dev, static const struct ieee80211_ops rtl8187_ops = { .tx = rtl8187_tx, + .wake_tx_queue = ieee80211_handle_wake_tx_queue, .start = rtl8187_start, .stop = rtl8187_stop, .add_interface = rtl8187_add_interface, diff --git a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c index 71a391bba9e2..9ee1a5bb1195 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c +++ b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c @@ -6644,6 +6644,7 @@ static void rtl8xxxu_stop(struct ieee80211_hw *hw) static const struct ieee80211_ops rtl8xxxu_ops = { .tx = rtl8xxxu_tx, + .wake_tx_queue = ieee80211_handle_wake_tx_queue, .add_interface = rtl8xxxu_add_interface, .remove_interface = rtl8xxxu_remove_interface, .config = rtl8xxxu_config, diff --git a/drivers/net/wireless/realtek/rtlwifi/core.c b/drivers/net/wireless/realtek/rtlwifi/core.c index ca01270944fe..6f10727cdb94 100644 --- a/drivers/net/wireless/realtek/rtlwifi/core.c +++ b/drivers/net/wireless/realtek/rtlwifi/core.c @@ -1912,6 +1912,7 @@ const struct ieee80211_ops rtl_ops = { .start = rtl_op_start, .stop = rtl_op_stop, .tx = rtl_op_tx, + .wake_tx_queue = ieee80211_handle_wake_tx_queue, .add_interface = rtl_op_add_interface, .remove_interface = rtl_op_remove_interface, .change_interface = rtl_op_change_interface, diff --git a/drivers/net/wireless/realtek/rtw88/mac80211.c b/drivers/net/wireless/realtek/rtw88/mac80211.c index 07578ccc4bab..bf917ef20e3b 100644 --- a/drivers/net/wireless/realtek/rtw88/mac80211.c +++ b/drivers/net/wireless/realtek/rtw88/mac80211.c @@ -896,6 +896,7 @@ static void rtw_ops_sta_rc_update(struct ieee80211_hw *hw, const struct ieee80211_ops rtw_ops = { .tx = rtw_ops_tx, + .wake_tx_queue = ieee80211_handle_wake_tx_queue, .wake_tx_queue = rtw_ops_wake_tx_queue, .start = rtw_ops_start, .stop = rtw_ops_stop, diff --git a/drivers/net/wireless/realtek/rtw89/mac80211.c b/drivers/net/wireless/realtek/rtw89/mac80211.c index a296bfa8188f..91674c63b9f9 100644 --- a/drivers/net/wireless/realtek/rtw89/mac80211.c +++ b/drivers/net/wireless/realtek/rtw89/mac80211.c @@ -918,6 +918,7 @@ static int rtw89_ops_set_tid_config(struct ieee80211_hw *hw, const struct ieee80211_ops rtw89_ops = { .tx = rtw89_ops_tx, + .wake_tx_queue = ieee80211_handle_wake_tx_queue, .wake_tx_queue = rtw89_ops_wake_tx_queue, .start = rtw89_ops_start, .stop = rtw89_ops_stop, diff --git a/drivers/net/wireless/rsi/rsi_91x_mac80211.c b/drivers/net/wireless/rsi/rsi_91x_mac80211.c index 2fbec51c8f94..bc1f038d1655 100644 --- a/drivers/net/wireless/rsi/rsi_91x_mac80211.c +++ b/drivers/net/wireless/rsi/rsi_91x_mac80211.c @@ -1958,6 +1958,7 @@ static int rsi_mac80211_resume(struct ieee80211_hw *hw) static const struct ieee80211_ops mac80211_ops = { .tx = rsi_mac80211_tx, + .wake_tx_queue = ieee80211_handle_wake_tx_queue, .start = rsi_mac80211_start, .stop = rsi_mac80211_stop, .add_interface = rsi_mac80211_add_interface, diff --git a/drivers/net/wireless/silabs/wfx/main.c b/drivers/net/wireless/silabs/wfx/main.c index 84d82ddded56..6b9864e478ac 100644 --- a/drivers/net/wireless/silabs/wfx/main.c +++ b/drivers/net/wireless/silabs/wfx/main.c @@ -128,6 +128,7 @@ static const struct ieee80211_ops wfx_ops = { .remove_interface = wfx_remove_interface, .config = wfx_config, .tx = wfx_tx, + .wake_tx_queue = ieee80211_handle_wake_tx_queue, .join_ibss = wfx_join_ibss, .leave_ibss = wfx_leave_ibss, .conf_tx = wfx_conf_tx, diff --git a/drivers/net/wireless/st/cw1200/main.c b/drivers/net/wireless/st/cw1200/main.c index 326b1cc1d2bc..381013e0db63 100644 --- a/drivers/net/wireless/st/cw1200/main.c +++ b/drivers/net/wireless/st/cw1200/main.c @@ -209,6 +209,7 @@ static const struct ieee80211_ops cw1200_ops = { .remove_interface = cw1200_remove_interface, .change_interface = cw1200_change_interface, .tx = cw1200_tx, + .wake_tx_queue = ieee80211_handle_wake_tx_queue, .hw_scan = cw1200_hw_scan, .set_tim = cw1200_set_tim, .sta_notify = cw1200_sta_notify, diff --git a/drivers/net/wireless/ti/wl1251/main.c b/drivers/net/wireless/ti/wl1251/main.c index 289371689a8d..eded284af600 100644 --- a/drivers/net/wireless/ti/wl1251/main.c +++ b/drivers/net/wireless/ti/wl1251/main.c @@ -1359,6 +1359,7 @@ static const struct ieee80211_ops wl1251_ops = { .prepare_multicast = wl1251_op_prepare_multicast, .configure_filter = wl1251_op_configure_filter, .tx = wl1251_op_tx, + .wake_tx_queue = ieee80211_handle_wake_tx_queue, .set_key = wl1251_op_set_key, .hw_scan = wl1251_op_hw_scan, .bss_info_changed = wl1251_op_bss_info_changed, diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index 3e3922d4c788..f1f1b3542b6c 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -5942,6 +5942,7 @@ static const struct ieee80211_ops wl1271_ops = { .prepare_multicast = wl1271_op_prepare_multicast, .configure_filter = wl1271_op_configure_filter, .tx = wl1271_op_tx, + .wake_tx_queue = ieee80211_handle_wake_tx_queue, .set_key = wlcore_op_set_key, .hw_scan = wl1271_op_hw_scan, .cancel_hw_scan = wl1271_op_cancel_hw_scan, diff --git a/drivers/net/wireless/zydas/zd1211rw/zd_mac.c b/drivers/net/wireless/zydas/zd1211rw/zd_mac.c index 80b905d49954..5d534e15a844 100644 --- a/drivers/net/wireless/zydas/zd1211rw/zd_mac.c +++ b/drivers/net/wireless/zydas/zd1211rw/zd_mac.c @@ -1344,6 +1344,7 @@ static u64 zd_op_get_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif) static const struct ieee80211_ops zd_ops = { .tx = zd_op_tx, + .wake_tx_queue = ieee80211_handle_wake_tx_queue, .start = zd_op_start, .stop = zd_op_stop, .add_interface = zd_op_add_interface, diff --git a/drivers/staging/vt6655/device_main.c b/drivers/staging/vt6655/device_main.c index bab08a40fe66..f6f5da4c55d1 100644 --- a/drivers/staging/vt6655/device_main.c +++ b/drivers/staging/vt6655/device_main.c @@ -1637,6 +1637,7 @@ static void vnt_reset_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif) static const struct ieee80211_ops vnt_mac_ops = { .tx = vnt_tx_80211, + .wake_tx_queue = ieee80211_handle_wake_tx_queue, .start = vnt_start, .stop = vnt_stop, .add_interface = vnt_add_interface, diff --git a/drivers/staging/vt6656/main_usb.c b/drivers/staging/vt6656/main_usb.c index 897ee0f7fc6b..2abae90f3f52 100644 --- a/drivers/staging/vt6656/main_usb.c +++ b/drivers/staging/vt6656/main_usb.c @@ -957,6 +957,7 @@ static void vnt_reset_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif) static const struct ieee80211_ops vnt_mac_ops = { .tx = vnt_tx_80211, + .wake_tx_queue = ieee80211_handle_wake_tx_queue, .start = vnt_start, .stop = vnt_stop, .add_interface = vnt_add_interface, From 107395f9cf4419fa29b553a00872a27e579ae304 Mon Sep 17 00:00:00 2001 From: Alexander Wetzel Date: Sun, 9 Oct 2022 18:30:40 +0200 Subject: [PATCH 0092/2002] wifi: mac80211: Drop support for TX push path All drivers are now using mac80211 internal queues (iTXQs). Drop mac80211 internal support for the old push path. Signed-off-by: Alexander Wetzel Signed-off-by: Johannes Berg --- net/mac80211/cfg.c | 3 -- net/mac80211/debugfs.c | 4 +- net/mac80211/debugfs_netdev.c | 3 +- net/mac80211/debugfs_sta.c | 6 +-- net/mac80211/ieee80211_i.h | 1 - net/mac80211/iface.c | 69 ++--------------------------------- net/mac80211/main.c | 11 ++---- net/mac80211/rx.c | 3 -- net/mac80211/sta_info.c | 51 ++++++++++---------------- net/mac80211/tdls.c | 1 - net/mac80211/tx.c | 28 ++------------ net/mac80211/util.c | 53 +++------------------------ net/mac80211/wme.c | 63 ++++---------------------------- net/mac80211/wme.h | 4 +- 14 files changed, 48 insertions(+), 252 deletions(-) diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index ef5700eac05c..c848fe04dd44 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -4340,9 +4340,6 @@ static int ieee80211_get_txq_stats(struct wiphy *wiphy, struct ieee80211_sub_if_data *sdata; int ret = 0; - if (!local->ops->wake_tx_queue) - return 1; - spin_lock_bh(&local->fq.lock); rcu_read_lock(); diff --git a/net/mac80211/debugfs.c b/net/mac80211/debugfs.c index 78c7d60e8667..dfb9f55e2685 100644 --- a/net/mac80211/debugfs.c +++ b/net/mac80211/debugfs.c @@ -663,9 +663,7 @@ void debugfs_hw_add(struct ieee80211_local *local) DEBUGFS_ADD_MODE(force_tx_status, 0600); DEBUGFS_ADD_MODE(aql_enable, 0600); DEBUGFS_ADD(aql_pending); - - if (local->ops->wake_tx_queue) - DEBUGFS_ADD_MODE(aqm, 0600); + DEBUGFS_ADD_MODE(aqm, 0600); DEBUGFS_ADD_MODE(airtime_flags, 0600); diff --git a/net/mac80211/debugfs_netdev.c b/net/mac80211/debugfs_netdev.c index 5b014786fd2d..c87e1137e5da 100644 --- a/net/mac80211/debugfs_netdev.c +++ b/net/mac80211/debugfs_netdev.c @@ -677,8 +677,7 @@ static void add_common_files(struct ieee80211_sub_if_data *sdata) DEBUGFS_ADD(rc_rateidx_vht_mcs_mask_5ghz); DEBUGFS_ADD(hw_queues); - if (sdata->local->ops->wake_tx_queue && - sdata->vif.type != NL80211_IFTYPE_P2P_DEVICE && + if (sdata->vif.type != NL80211_IFTYPE_P2P_DEVICE && sdata->vif.type != NL80211_IFTYPE_NAN) DEBUGFS_ADD(aqm); } diff --git a/net/mac80211/debugfs_sta.c b/net/mac80211/debugfs_sta.c index 0d8ff0303028..7a3d7893e19d 100644 --- a/net/mac80211/debugfs_sta.c +++ b/net/mac80211/debugfs_sta.c @@ -1072,10 +1072,8 @@ void ieee80211_sta_debugfs_add(struct sta_info *sta) /* FIXME: Kept here as the statistics are only done on the deflink */ DEBUGFS_ADD_COUNTER(tx_filtered, deflink.status_stats.filtered); - if (local->ops->wake_tx_queue) { - DEBUGFS_ADD(aqm); - DEBUGFS_ADD(airtime); - } + DEBUGFS_ADD(aqm); + DEBUGFS_ADD(airtime); if (wiphy_ext_feature_isset(local->hw.wiphy, NL80211_EXT_FEATURE_AQL)) diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index b704656027db..63ff0d2524b6 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -2307,7 +2307,6 @@ void ieee80211_wake_queue_by_reason(struct ieee80211_hw *hw, int queue, void ieee80211_stop_queue_by_reason(struct ieee80211_hw *hw, int queue, enum queue_stop_reason reason, bool refcounted); -void ieee80211_propagate_queue_wake(struct ieee80211_local *local, int queue); void ieee80211_add_pending_skb(struct ieee80211_local *local, struct sk_buff *skb); void ieee80211_add_pending_skbs(struct ieee80211_local *local, diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index dd9ac1f7d2ea..7c4ce716c939 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -458,12 +458,6 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, bool going_do if (cancel_scan) ieee80211_scan_cancel(local); - /* - * Stop TX on this interface first. - */ - if (!local->ops->wake_tx_queue && sdata->dev) - netif_tx_stop_all_queues(sdata->dev); - ieee80211_roc_purge(local, sdata); switch (sdata->vif.type) { @@ -811,13 +805,6 @@ static void ieee80211_uninit(struct net_device *dev) ieee80211_teardown_sdata(IEEE80211_DEV_TO_SUB_IF(dev)); } -static u16 ieee80211_netdev_select_queue(struct net_device *dev, - struct sk_buff *skb, - struct net_device *sb_dev) -{ - return ieee80211_select_queue(IEEE80211_DEV_TO_SUB_IF(dev), skb); -} - static void ieee80211_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats) { @@ -831,7 +818,6 @@ static const struct net_device_ops ieee80211_dataif_ops = { .ndo_start_xmit = ieee80211_subif_start_xmit, .ndo_set_rx_mode = ieee80211_set_multicast_list, .ndo_set_mac_address = ieee80211_change_mac, - .ndo_select_queue = ieee80211_netdev_select_queue, .ndo_get_stats64 = ieee80211_get_stats64, }; @@ -939,7 +925,6 @@ static const struct net_device_ops ieee80211_dataif_8023_ops = { .ndo_start_xmit = ieee80211_subif_start_xmit_8023, .ndo_set_rx_mode = ieee80211_set_multicast_list, .ndo_set_mac_address = ieee80211_change_mac, - .ndo_select_queue = ieee80211_netdev_select_queue, .ndo_get_stats64 = ieee80211_get_stats64, .ndo_fill_forward_path = ieee80211_netdev_fill_forward_path, }; @@ -1441,35 +1426,6 @@ int ieee80211_do_open(struct wireless_dev *wdev, bool coming_up) ieee80211_recalc_ps(local); - if (sdata->vif.type == NL80211_IFTYPE_MONITOR || - sdata->vif.type == NL80211_IFTYPE_AP_VLAN || - local->ops->wake_tx_queue) { - /* XXX: for AP_VLAN, actually track AP queues */ - if (dev) - netif_tx_start_all_queues(dev); - } else if (dev) { - unsigned long flags; - int n_acs = IEEE80211_NUM_ACS; - int ac; - - if (local->hw.queues < IEEE80211_NUM_ACS) - n_acs = 1; - - spin_lock_irqsave(&local->queue_stop_reason_lock, flags); - if (sdata->vif.cab_queue == IEEE80211_INVAL_HW_QUEUE || - (local->queue_stop_reasons[sdata->vif.cab_queue] == 0 && - skb_queue_empty(&local->pending[sdata->vif.cab_queue]))) { - for (ac = 0; ac < n_acs; ac++) { - int ac_queue = sdata->vif.hw_queue[ac]; - - if (local->queue_stop_reasons[ac_queue] == 0 && - skb_queue_empty(&local->pending[ac_queue])) - netif_start_subqueue(dev, ac); - } - } - spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); - } - set_bit(SDATA_STATE_RUNNING, &sdata->state); return 0; @@ -1499,17 +1455,12 @@ static void ieee80211_if_setup(struct net_device *dev) { ether_setup(dev); dev->priv_flags &= ~IFF_TX_SKB_SHARING; + dev->priv_flags |= IFF_NO_QUEUE; dev->netdev_ops = &ieee80211_dataif_ops; dev->needs_free_netdev = true; dev->priv_destructor = ieee80211_if_free; } -static void ieee80211_if_setup_no_queue(struct net_device *dev) -{ - ieee80211_if_setup(dev); - dev->priv_flags |= IFF_NO_QUEUE; -} - static void ieee80211_iface_process_skb(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata, struct sk_buff *skb) @@ -2094,9 +2045,7 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name, struct net_device *ndev = NULL; struct ieee80211_sub_if_data *sdata = NULL; struct txq_info *txqi; - void (*if_setup)(struct net_device *dev); int ret, i; - int txqs = 1; ASSERT_RTNL(); @@ -2119,30 +2068,18 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name, sizeof(void *)); int txq_size = 0; - if (local->ops->wake_tx_queue && - type != NL80211_IFTYPE_AP_VLAN && + if (type != NL80211_IFTYPE_AP_VLAN && (type != NL80211_IFTYPE_MONITOR || (params->flags & MONITOR_FLAG_ACTIVE))) txq_size += sizeof(struct txq_info) + local->hw.txq_data_size; - if (local->ops->wake_tx_queue) { - if_setup = ieee80211_if_setup_no_queue; - } else { - if_setup = ieee80211_if_setup; - if (local->hw.queues >= IEEE80211_NUM_ACS) - txqs = IEEE80211_NUM_ACS; - } - ndev = alloc_netdev_mqs(size + txq_size, name, name_assign_type, - if_setup, txqs, 1); + ieee80211_if_setup, 1, 1); if (!ndev) return -ENOMEM; - if (!local->ops->wake_tx_queue && local->hw.wiphy->tx_queue_len) - ndev->tx_queue_len = local->hw.wiphy->tx_queue_len; - dev_net_set(ndev, wiphy_net(local->hw.wiphy)); ndev->tstats = netdev_alloc_pcpu_stats(struct pcpu_sw_netstats); diff --git a/net/mac80211/main.c b/net/mac80211/main.c index 425793dd7c9c..b7279d88cddb 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -630,7 +630,7 @@ struct ieee80211_hw *ieee80211_alloc_hw_nm(size_t priv_data_len, if (WARN_ON(!ops->tx || !ops->start || !ops->stop || !ops->config || !ops->add_interface || !ops->remove_interface || - !ops->configure_filter)) + !ops->configure_filter || !ops->wake_tx_queue)) return NULL; if (WARN_ON(ops->sta_state && (ops->sta_add || ops->sta_remove))) @@ -719,9 +719,7 @@ struct ieee80211_hw *ieee80211_alloc_hw_nm(size_t priv_data_len, if (!ops->set_key) wiphy->flags |= WIPHY_FLAG_IBSS_RSN; - if (ops->wake_tx_queue) - wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_TXQS); - + wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_TXQS); wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_RRM); wiphy->bss_priv_size = sizeof(struct ieee80211_bss); @@ -834,10 +832,7 @@ struct ieee80211_hw *ieee80211_alloc_hw_nm(size_t priv_data_len, atomic_set(&local->agg_queue_stop[i], 0); } tasklet_setup(&local->tx_pending_tasklet, ieee80211_tx_pending); - - if (ops->wake_tx_queue) - tasklet_setup(&local->wake_txqs_tasklet, ieee80211_wake_txqs); - + tasklet_setup(&local->wake_txqs_tasklet, ieee80211_wake_txqs); tasklet_setup(&local->tasklet, ieee80211_tasklet_handler); skb_queue_head_init(&local->skb_queue); diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index 589521717c35..ac8691026903 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -1571,9 +1571,6 @@ static void sta_ps_start(struct sta_info *sta) ieee80211_clear_fast_xmit(sta); - if (!sta->sta.txq[0]) - return; - for (tid = 0; tid < IEEE80211_NUM_TIDS; tid++) { struct ieee80211_txq *txq = sta->sta.txq[tid]; struct txq_info *txqi = to_txq_info(txq); diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index 2bb6a71c72ef..6e0fd82855ae 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -140,17 +140,15 @@ static void __cleanup_single_sta(struct sta_info *sta) atomic_dec(&ps->num_sta_ps); } - if (sta->sta.txq[0]) { - for (i = 0; i < ARRAY_SIZE(sta->sta.txq); i++) { - struct txq_info *txqi; + for (i = 0; i < ARRAY_SIZE(sta->sta.txq); i++) { + struct txq_info *txqi; - if (!sta->sta.txq[i]) - continue; + if (!sta->sta.txq[i]) + continue; - txqi = to_txq_info(sta->sta.txq[i]); + txqi = to_txq_info(sta->sta.txq[i]); - ieee80211_txq_purge(local, txqi); - } + ieee80211_txq_purge(local, txqi); } for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) { @@ -428,8 +426,7 @@ void sta_info_free(struct ieee80211_local *local, struct sta_info *sta) sta_dbg(sta->sdata, "Destroyed STA %pM\n", sta->sta.addr); - if (sta->sta.txq[0]) - kfree(to_txq_info(sta->sta.txq[0])); + kfree(to_txq_info(sta->sta.txq[0])); kfree(rcu_dereference_raw(sta->sta.rates)); #ifdef CONFIG_MAC80211_MESH kfree(sta->mesh); @@ -531,6 +528,8 @@ __sta_info_alloc(struct ieee80211_sub_if_data *sdata, struct ieee80211_local *local = sdata->local; struct ieee80211_hw *hw = &local->hw; struct sta_info *sta; + void *txq_data; + int size; int i; sta = kzalloc(sizeof(*sta) + hw->sta_data_size, gfp); @@ -600,21 +599,18 @@ __sta_info_alloc(struct ieee80211_sub_if_data *sdata, sta->last_connected = ktime_get_seconds(); - if (local->ops->wake_tx_queue) { - void *txq_data; - int size = sizeof(struct txq_info) + - ALIGN(hw->txq_data_size, sizeof(void *)); + size = sizeof(struct txq_info) + + ALIGN(hw->txq_data_size, sizeof(void *)); - txq_data = kcalloc(ARRAY_SIZE(sta->sta.txq), size, gfp); - if (!txq_data) - goto free; + txq_data = kcalloc(ARRAY_SIZE(sta->sta.txq), size, gfp); + if (!txq_data) + goto free; - for (i = 0; i < ARRAY_SIZE(sta->sta.txq); i++) { - struct txq_info *txq = txq_data + i * size; + for (i = 0; i < ARRAY_SIZE(sta->sta.txq); i++) { + struct txq_info *txq = txq_data + i * size; - /* might not do anything for the bufferable MMPDU TXQ */ - ieee80211_txq_init(sdata, sta, txq, i); - } + /* might not do anything for the (bufferable) MMPDU TXQ */ + ieee80211_txq_init(sdata, sta, txq, i); } if (sta_prepare_rate_control(local, sta, gfp)) @@ -688,8 +684,7 @@ __sta_info_alloc(struct ieee80211_sub_if_data *sdata, return sta; free_txq: - if (sta->sta.txq[0]) - kfree(to_txq_info(sta->sta.txq[0])); + kfree(to_txq_info(sta->sta.txq[0])); free: sta_info_free_link(&sta->deflink); #ifdef CONFIG_MAC80211_MESH @@ -1982,9 +1977,6 @@ ieee80211_sta_ps_deliver_response(struct sta_info *sta, * TIM recalculation. */ - if (!sta->sta.txq[0]) - return; - for (tid = 0; tid < ARRAY_SIZE(sta->sta.txq); tid++) { if (!sta->sta.txq[tid] || !(driver_release_tids & BIT(tid)) || @@ -2484,7 +2476,7 @@ static void sta_set_tidstats(struct sta_info *sta, tidstats->tx_msdu_failed = sta->deflink.status_stats.msdu_failed[tid]; } - if (local->ops->wake_tx_queue && tid < IEEE80211_NUM_TIDS) { + if (tid < IEEE80211_NUM_TIDS) { spin_lock_bh(&local->fq.lock); rcu_read_lock(); @@ -2812,9 +2804,6 @@ unsigned long ieee80211_sta_last_active(struct sta_info *sta) static void sta_update_codel_params(struct sta_info *sta, u32 thr) { - if (!sta->sdata->local->ops->wake_tx_queue) - return; - if (thr && thr < STA_SLOW_THRESHOLD * sta->local->num_sta) { sta->cparams.target = MS2TIME(50); sta->cparams.interval = MS2TIME(300); diff --git a/net/mac80211/tdls.c b/net/mac80211/tdls.c index f4b4d25eef95..b255f3b5bf01 100644 --- a/net/mac80211/tdls.c +++ b/net/mac80211/tdls.c @@ -1016,7 +1016,6 @@ ieee80211_tdls_prep_mgmt_packet(struct wiphy *wiphy, struct net_device *dev, skb->priority = 256 + 5; break; } - skb_set_queue_mapping(skb, ieee80211_select_queue(sdata, skb)); /* * Set the WLAN_TDLS_TEARDOWN flag to indicate a teardown in progress. diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index e137355bea67..bb2e54610101 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -1599,9 +1599,6 @@ int ieee80211_txq_setup_flows(struct ieee80211_local *local) bool supp_vht = false; enum nl80211_band band; - if (!local->ops->wake_tx_queue) - return 0; - ret = fq_init(fq, 4096); if (ret) return ret; @@ -1649,9 +1646,6 @@ void ieee80211_txq_teardown_flows(struct ieee80211_local *local) { struct fq *fq = &local->fq; - if (!local->ops->wake_tx_queue) - return; - kfree(local->cvars); local->cvars = NULL; @@ -1668,8 +1662,7 @@ static bool ieee80211_queue_skb(struct ieee80211_local *local, struct ieee80211_vif *vif; struct txq_info *txqi; - if (!local->ops->wake_tx_queue || - sdata->vif.type == NL80211_IFTYPE_MONITOR) + if (sdata->vif.type == NL80211_IFTYPE_MONITOR) return false; if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) @@ -4184,12 +4177,7 @@ void __ieee80211_subif_start_xmit(struct sk_buff *skb, if (IS_ERR(sta)) sta = NULL; - if (local->ops->wake_tx_queue) { - u16 queue = __ieee80211_select_queue(sdata, sta, skb); - skb_set_queue_mapping(skb, queue); - skb_get_hash(skb); - } - + skb_set_queue_mapping(skb, ieee80211_select_queue(sdata, sta, skb)); ieee80211_aggr_check(sdata, sta, skb); sk_pacing_shift_update(skb->sk, sdata->local->hw.tx_sk_pacing_shift); @@ -4495,11 +4483,7 @@ static void ieee80211_8023_xmit(struct ieee80211_sub_if_data *sdata, struct tid_ampdu_tx *tid_tx; u8 tid; - if (local->ops->wake_tx_queue) { - u16 queue = __ieee80211_select_queue(sdata, sta, skb); - skb_set_queue_mapping(skb, queue); - skb_get_hash(skb); - } + skb_set_queue_mapping(skb, ieee80211_select_queue(sdata, sta, skb)); if (unlikely(test_bit(SCAN_SW_SCANNING, &local->scanning)) && test_bit(SDATA_STATE_OFFCHANNEL, &sdata->state)) @@ -4753,9 +4737,6 @@ void ieee80211_tx_pending(struct tasklet_struct *t) if (!txok) break; } - - if (skb_queue_empty(&local->pending[i])) - ieee80211_propagate_queue_wake(local, i); } spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); @@ -5948,10 +5929,9 @@ int ieee80211_tx_control_port(struct wiphy *wiphy, struct net_device *dev, } if (!IS_ERR(sta)) { - u16 queue = __ieee80211_select_queue(sdata, sta, skb); + u16 queue = ieee80211_select_queue(sdata, sta, skb); skb_set_queue_mapping(skb, queue); - skb_get_hash(skb); /* * for MLO STA, the SA should be the AP MLD address, but diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 7a80843f1a0a..53217dc3f932 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -446,39 +446,6 @@ void ieee80211_wake_txqs(struct tasklet_struct *t) spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); } -void ieee80211_propagate_queue_wake(struct ieee80211_local *local, int queue) -{ - struct ieee80211_sub_if_data *sdata; - int n_acs = IEEE80211_NUM_ACS; - - if (local->ops->wake_tx_queue) - return; - - if (local->hw.queues < IEEE80211_NUM_ACS) - n_acs = 1; - - list_for_each_entry_rcu(sdata, &local->interfaces, list) { - int ac; - - if (!sdata->dev) - continue; - - if (sdata->vif.cab_queue != IEEE80211_INVAL_HW_QUEUE && - local->queue_stop_reasons[sdata->vif.cab_queue] != 0) - continue; - - for (ac = 0; ac < n_acs; ac++) { - int ac_queue = sdata->vif.hw_queue[ac]; - - if (ac_queue == queue || - (sdata->vif.cab_queue == queue && - local->queue_stop_reasons[ac_queue] == 0 && - skb_queue_empty(&local->pending[ac_queue]))) - netif_wake_subqueue(sdata->dev, ac); - } - } -} - static void __ieee80211_wake_queue(struct ieee80211_hw *hw, int queue, enum queue_stop_reason reason, bool refcounted, @@ -509,11 +476,7 @@ static void __ieee80211_wake_queue(struct ieee80211_hw *hw, int queue, /* someone still has this queue stopped */ return; - if (skb_queue_empty(&local->pending[queue])) { - rcu_read_lock(); - ieee80211_propagate_queue_wake(local, queue); - rcu_read_unlock(); - } else + if (!skb_queue_empty(&local->pending[queue])) tasklet_schedule(&local->tx_pending_tasklet); /* @@ -523,12 +486,10 @@ static void __ieee80211_wake_queue(struct ieee80211_hw *hw, int queue, * release someone's lock, but it is fine because all the callers of * __ieee80211_wake_queue call it right before releasing the lock. */ - if (local->ops->wake_tx_queue) { - if (reason == IEEE80211_QUEUE_STOP_REASON_DRIVER) - tasklet_schedule(&local->wake_txqs_tasklet); - else - _ieee80211_wake_txqs(local, flags); - } + if (reason == IEEE80211_QUEUE_STOP_REASON_DRIVER) + tasklet_schedule(&local->wake_txqs_tasklet); + else + _ieee80211_wake_txqs(local, flags); } void ieee80211_wake_queue_by_reason(struct ieee80211_hw *hw, int queue, @@ -585,10 +546,6 @@ static void __ieee80211_stop_queue(struct ieee80211_hw *hw, int queue, for (ac = 0; ac < n_acs; ac++) { if (sdata->vif.hw_queue[ac] == queue || sdata->vif.cab_queue == queue) { - if (!local->ops->wake_tx_queue) { - netif_stop_subqueue(sdata->dev, ac); - continue; - } spin_lock(&local->fq.lock); sdata->vif.txqs_stopped[ac] = true; spin_unlock(&local->fq.lock); diff --git a/net/mac80211/wme.c b/net/mac80211/wme.c index 9fab97f6fbea..a12c63638680 100644 --- a/net/mac80211/wme.c +++ b/net/mac80211/wme.c @@ -122,6 +122,9 @@ u16 ieee80211_select_queue_80211(struct ieee80211_sub_if_data *sdata, struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); u8 *p; + /* Ensure hash is set prior to potential SW encryption */ + skb_get_hash(skb); + if ((info->control.flags & IEEE80211_TX_CTRL_DONT_REORDER) || local->hw.queues < IEEE80211_NUM_ACS) return 0; @@ -141,12 +144,15 @@ u16 ieee80211_select_queue_80211(struct ieee80211_sub_if_data *sdata, return ieee80211_downgrade_queue(sdata, NULL, skb); } -u16 __ieee80211_select_queue(struct ieee80211_sub_if_data *sdata, - struct sta_info *sta, struct sk_buff *skb) +u16 ieee80211_select_queue(struct ieee80211_sub_if_data *sdata, + struct sta_info *sta, struct sk_buff *skb) { struct mac80211_qos_map *qos_map; bool qos; + /* Ensure hash is set prior to potential SW encryption */ + skb_get_hash(skb); + /* all mesh/ocb stations are required to support WME */ if (sta && (sdata->vif.type == NL80211_IFTYPE_MESH_POINT || sdata->vif.type == NL80211_IFTYPE_OCB)) @@ -176,59 +182,6 @@ u16 __ieee80211_select_queue(struct ieee80211_sub_if_data *sdata, return ieee80211_downgrade_queue(sdata, sta, skb); } - -/* Indicate which queue to use. */ -u16 ieee80211_select_queue(struct ieee80211_sub_if_data *sdata, - struct sk_buff *skb) -{ - struct ieee80211_local *local = sdata->local; - struct sta_info *sta = NULL; - const u8 *ra = NULL; - u16 ret; - - /* when using iTXQ, we can do this later */ - if (local->ops->wake_tx_queue) - return 0; - - if (local->hw.queues < IEEE80211_NUM_ACS || skb->len < 6) { - skb->priority = 0; /* required for correct WPA/11i MIC */ - return 0; - } - - rcu_read_lock(); - switch (sdata->vif.type) { - case NL80211_IFTYPE_AP_VLAN: - sta = rcu_dereference(sdata->u.vlan.sta); - if (sta) - break; - fallthrough; - case NL80211_IFTYPE_AP: - ra = skb->data; - break; - case NL80211_IFTYPE_STATION: - /* might be a TDLS station */ - sta = sta_info_get(sdata, skb->data); - if (sta) - break; - - ra = sdata->vif.cfg.ap_addr; - break; - case NL80211_IFTYPE_ADHOC: - ra = skb->data; - break; - default: - break; - } - - if (!sta && ra && !is_multicast_ether_addr(ra)) - sta = sta_info_get(sdata, ra); - - ret = __ieee80211_select_queue(sdata, sta, skb); - - rcu_read_unlock(); - return ret; -} - /** * ieee80211_set_qos_hdr - Fill in the QoS header if there is one. * diff --git a/net/mac80211/wme.h b/net/mac80211/wme.h index 2e3dec0b6087..81f0039527a9 100644 --- a/net/mac80211/wme.h +++ b/net/mac80211/wme.h @@ -13,10 +13,8 @@ u16 ieee80211_select_queue_80211(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb, struct ieee80211_hdr *hdr); -u16 __ieee80211_select_queue(struct ieee80211_sub_if_data *sdata, - struct sta_info *sta, struct sk_buff *skb); u16 ieee80211_select_queue(struct ieee80211_sub_if_data *sdata, - struct sk_buff *skb); + struct sta_info *sta, struct sk_buff *skb); void ieee80211_set_qos_hdr(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb); From d99884ad9e3673a12879bc2830f6e5a66cccbd78 Mon Sep 17 00:00:00 2001 From: Wen Gong Date: Thu, 6 Oct 2022 18:17:47 +0300 Subject: [PATCH 0093/2002] wifi: ath11k: avoid deadlock during regulatory update in ath11k_regd_update() Running this test in a loop it is easy to reproduce an rtnl deadlock: iw reg set FI ifconfig wlan0 down What happens is that thread A (workqueue) tries to update the regulatory: try to acquire the rtnl_lock of ar->regd_update_work rtnl_lock+0x17/0x20 ath11k_regd_update+0x15a/0x260 [ath11k] ath11k_regd_update_work+0x15/0x20 [ath11k] process_one_work+0x228/0x670 worker_thread+0x4d/0x440 kthread+0x16d/0x1b0 ret_from_fork+0x22/0x30 And thread B (ifconfig) tries to stop the interface: try to cancel_work_sync(&ar->regd_update_work) in ath11k_mac_op_stop(). ifconfig 3109 [003] 2414.232506: probe: ath11k_mac_op_stop: (ffffffffc14187a0) drv_stop+0x30 ([mac80211]) ieee80211_do_stop+0x5d2 ([mac80211]) ieee80211_stop+0x3e ([mac80211]) __dev_close_many+0x9e ([kernel.kallsyms]) __dev_change_flags+0xbe ([kernel.kallsyms]) dev_change_flags+0x23 ([kernel.kallsyms]) devinet_ioctl+0x5e3 ([kernel.kallsyms]) inet_ioctl+0x197 ([kernel.kallsyms]) sock_do_ioctl+0x4d ([kernel.kallsyms]) sock_ioctl+0x264 ([kernel.kallsyms]) __x64_sys_ioctl+0x92 ([kernel.kallsyms]) do_syscall_64+0x3a ([kernel.kallsyms]) entry_SYSCALL_64_after_hwframe+0x63 ([kernel.kallsyms]) __GI___ioctl+0x7 (/lib/x86_64-linux-gnu/libc-2.23.so) The sequence of deadlock is: 1. Thread B calls rtnl_lock(). 2. Thread A starts to run and calls rtnl_lock() from within ath11k_regd_update_work(), then enters wait state because the lock is owned by thread B. 3. Thread B continues to run and tries to call cancel_work_sync(&ar->regd_update_work), but thread A is in ath11k_regd_update_work() waiting for rtnl_lock(). So cancel_work_sync() forever waits for ath11k_regd_update_work() to finish and we have a deadlock. Fix this by switching from using regulatory_set_wiphy_regd_sync() to regulatory_set_wiphy_regd(). Now cfg80211 will schedule another workqueue which handles the locking on it's own. So the ath11k workqueue can simply exit without taking any locks, avoiding the deadlock. Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-03125-QCAHSPSWPL_V1_V2_SILICONZ_LITE-3 Signed-off-by: Wen Gong [kvalo: improve commit log] Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20221006151747.13757-1-kvalo@kernel.org --- drivers/net/wireless/ath/ath11k/reg.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/drivers/net/wireless/ath/ath11k/reg.c b/drivers/net/wireless/ath/ath11k/reg.c index 7ee3ff69dfc8..6fae4e61ede7 100644 --- a/drivers/net/wireless/ath/ath11k/reg.c +++ b/drivers/net/wireless/ath/ath11k/reg.c @@ -287,11 +287,7 @@ int ath11k_regd_update(struct ath11k *ar) goto err; } - rtnl_lock(); - wiphy_lock(ar->hw->wiphy); - ret = regulatory_set_wiphy_regd_sync(ar->hw->wiphy, regd_copy); - wiphy_unlock(ar->hw->wiphy); - rtnl_unlock(); + ret = regulatory_set_wiphy_regd(ar->hw->wiphy, regd_copy); kfree(regd_copy); From a797f479bf3e02c6d179c2e6aeace7f9b22b0acd Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Wed, 28 Sep 2022 15:38:34 +0100 Subject: [PATCH 0094/2002] wifi: ath11k: Fix spelling mistake "chnange" -> "change" There is a spelling mistake in an ath11k_dbg debug message. Fix it. Signed-off-by: Colin Ian King Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20220928143834.35189-1-colin.i.king@gmail.com --- drivers/net/wireless/ath/ath11k/wmi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath11k/wmi.c b/drivers/net/wireless/ath/ath11k/wmi.c index fad9f8d308a2..2a8a3e3dcff6 100644 --- a/drivers/net/wireless/ath/ath11k/wmi.c +++ b/drivers/net/wireless/ath/ath11k/wmi.c @@ -6829,7 +6829,7 @@ static void ath11k_wmi_event_peer_sta_ps_state_chg(struct ath11k_base *ab, } ath11k_dbg(ab, ATH11K_DBG_WMI, - "peer sta ps chnange ev addr %pM state %u sup_bitmap %x ps_valid %u ts %u\n", + "peer sta ps change ev addr %pM state %u sup_bitmap %x ps_valid %u ts %u\n", ev->peer_macaddr.addr, ev->peer_ps_state, ev->ps_supported_bitmap, ev->peer_ps_valid, ev->peer_ps_timestamp); From e161d4b60ae3a5356e07202e0bfedb5fad82c6aa Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Wed, 5 Oct 2022 16:55:58 +0100 Subject: [PATCH 0095/2002] wifi: ath9k: Make arrays prof_prio and channelmap static const MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Don't populate the read-only arrays prof_prio and channelmap on the stack but instead make them static const. Also makes the object code a little smaller. Signed-off-by: Colin Ian King Acked-by: Toke Høiland-Jørgensen Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20221005155558.320556-1-colin.i.king@gmail.com --- drivers/net/wireless/ath/ath9k/mci.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/ath/ath9k/mci.c b/drivers/net/wireless/ath/ath9k/mci.c index 039bf0c35fbe..3363fc4e8966 100644 --- a/drivers/net/wireless/ath/ath9k/mci.c +++ b/drivers/net/wireless/ath/ath9k/mci.c @@ -266,7 +266,9 @@ static void ath_mci_set_concur_txprio(struct ath_softc *sc) stomp_txprio[ATH_BTCOEX_STOMP_NONE] = ATH_MCI_INQUIRY_PRIO; } else { - u8 prof_prio[] = { 50, 90, 94, 52 };/* RFCOMM, A2DP, HID, PAN */ + static const u8 prof_prio[] = { + 50, 90, 94, 52 + }; /* RFCOMM, A2DP, HID, PAN */ stomp_txprio[ATH_BTCOEX_STOMP_LOW] = stomp_txprio[ATH_BTCOEX_STOMP_NONE] = 0xff; @@ -644,7 +646,9 @@ void ath9k_mci_update_wlan_channels(struct ath_softc *sc, bool allow_all) struct ath_hw *ah = sc->sc_ah; struct ath9k_hw_mci *mci = &ah->btcoex_hw.mci; struct ath9k_channel *chan = ah->curchan; - u32 channelmap[] = {0x00000000, 0xffff0000, 0xffffffff, 0x7fffffff}; + static const u32 channelmap[] = { + 0x00000000, 0xffff0000, 0xffffffff, 0x7fffffff + }; int i; s16 chan_start, chan_end; u16 wlan_chan; From 53d0ce0c56d9330f137936b8850d4a5f2f70907d Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 10 Oct 2022 19:17:46 +0200 Subject: [PATCH 0096/2002] wifi: realtek: remove duplicated wake_tx_queue By accident, the previous patch duplicated the initialization of the wake_tx_queue callback. Fix that by removing the new initializations. Fixes: a790cc3a4fad ("wifi: mac80211: add wake_tx_queue callback to drivers") Signed-off-by: Johannes Berg --- drivers/net/wireless/realtek/rtw88/mac80211.c | 1 - drivers/net/wireless/realtek/rtw89/mac80211.c | 1 - 2 files changed, 2 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw88/mac80211.c b/drivers/net/wireless/realtek/rtw88/mac80211.c index bf917ef20e3b..07578ccc4bab 100644 --- a/drivers/net/wireless/realtek/rtw88/mac80211.c +++ b/drivers/net/wireless/realtek/rtw88/mac80211.c @@ -896,7 +896,6 @@ static void rtw_ops_sta_rc_update(struct ieee80211_hw *hw, const struct ieee80211_ops rtw_ops = { .tx = rtw_ops_tx, - .wake_tx_queue = ieee80211_handle_wake_tx_queue, .wake_tx_queue = rtw_ops_wake_tx_queue, .start = rtw_ops_start, .stop = rtw_ops_stop, diff --git a/drivers/net/wireless/realtek/rtw89/mac80211.c b/drivers/net/wireless/realtek/rtw89/mac80211.c index 91674c63b9f9..a296bfa8188f 100644 --- a/drivers/net/wireless/realtek/rtw89/mac80211.c +++ b/drivers/net/wireless/realtek/rtw89/mac80211.c @@ -918,7 +918,6 @@ static int rtw89_ops_set_tid_config(struct ieee80211_hw *hw, const struct ieee80211_ops rtw89_ops = { .tx = rtw89_ops_tx, - .wake_tx_queue = ieee80211_handle_wake_tx_queue, .wake_tx_queue = rtw89_ops_wake_tx_queue, .start = rtw89_ops_start, .stop = rtw89_ops_stop, From 7a366da2d2ba86316d3ec408f70d19b63916f9ce Mon Sep 17 00:00:00 2001 From: Roberto Sassu Date: Thu, 6 Oct 2022 13:07:31 +0200 Subject: [PATCH 0097/2002] libbpf: Fix LIBBPF_1.0.0 declaration in libbpf.map Add the missing LIBBPF_0.8.0 at the end of the LIBBPF_1.0.0 declaration, similarly to other version declarations. Signed-off-by: Roberto Sassu Signed-off-by: Andrii Nakryiko Link: https://lore.kernel.org/bpf/20221006110736.84253-2-roberto.sassu@huaweicloud.com --- tools/lib/bpf/libbpf.map | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/lib/bpf/libbpf.map b/tools/lib/bpf/libbpf.map index c1d6aa7c82b6..04fab9f1fdd7 100644 --- a/tools/lib/bpf/libbpf.map +++ b/tools/lib/bpf/libbpf.map @@ -367,7 +367,7 @@ LIBBPF_1.0.0 { libbpf_bpf_map_type_str; libbpf_bpf_prog_type_str; perf_buffer__buffer; -}; +} LIBBPF_0.8.0; LIBBPF_1.1.0 { global: From 243e300563b1b39ac669c7698742931427699184 Mon Sep 17 00:00:00 2001 From: Roberto Sassu Date: Thu, 6 Oct 2022 13:07:32 +0200 Subject: [PATCH 0098/2002] libbpf: Introduce bpf_get_fd_by_id_opts and bpf_map_get_fd_by_id_opts() Define a new data structure called bpf_get_fd_by_id_opts, with the member open_flags, to be used by callers of the _opts variants of bpf_*_get_fd_by_id() to specify the permissions needed for the file descriptor to be obtained. Also, introduce bpf_map_get_fd_by_id_opts(), to let the caller pass a bpf_get_fd_by_id_opts structure. Finally, keep the existing bpf_map_get_fd_by_id(), and call bpf_map_get_fd_by_id_opts() with NULL as opts argument, to request read-write permissions (current behavior). Signed-off-by: Roberto Sassu Signed-off-by: Andrii Nakryiko Link: https://lore.kernel.org/bpf/20221006110736.84253-3-roberto.sassu@huaweicloud.com --- tools/lib/bpf/bpf.c | 12 +++++++++++- tools/lib/bpf/bpf.h | 10 ++++++++++ tools/lib/bpf/libbpf.map | 1 + 3 files changed, 22 insertions(+), 1 deletion(-) diff --git a/tools/lib/bpf/bpf.c b/tools/lib/bpf/bpf.c index 1d49a0352836..c08d7509553d 100644 --- a/tools/lib/bpf/bpf.c +++ b/tools/lib/bpf/bpf.c @@ -948,19 +948,29 @@ int bpf_prog_get_fd_by_id(__u32 id) return libbpf_err_errno(fd); } -int bpf_map_get_fd_by_id(__u32 id) +int bpf_map_get_fd_by_id_opts(__u32 id, + const struct bpf_get_fd_by_id_opts *opts) { const size_t attr_sz = offsetofend(union bpf_attr, open_flags); union bpf_attr attr; int fd; + if (!OPTS_VALID(opts, bpf_get_fd_by_id_opts)) + return libbpf_err(-EINVAL); + memset(&attr, 0, attr_sz); attr.map_id = id; + attr.open_flags = OPTS_GET(opts, open_flags, 0); fd = sys_bpf_fd(BPF_MAP_GET_FD_BY_ID, &attr, attr_sz); return libbpf_err_errno(fd); } +int bpf_map_get_fd_by_id(__u32 id) +{ + return bpf_map_get_fd_by_id_opts(id, NULL); +} + int bpf_btf_get_fd_by_id(__u32 id) { const size_t attr_sz = offsetofend(union bpf_attr, open_flags); diff --git a/tools/lib/bpf/bpf.h b/tools/lib/bpf/bpf.h index 9c50beabdd14..10ce38f0a9ef 100644 --- a/tools/lib/bpf/bpf.h +++ b/tools/lib/bpf/bpf.h @@ -365,8 +365,18 @@ LIBBPF_API int bpf_prog_get_next_id(__u32 start_id, __u32 *next_id); LIBBPF_API int bpf_map_get_next_id(__u32 start_id, __u32 *next_id); LIBBPF_API int bpf_btf_get_next_id(__u32 start_id, __u32 *next_id); LIBBPF_API int bpf_link_get_next_id(__u32 start_id, __u32 *next_id); + +struct bpf_get_fd_by_id_opts { + size_t sz; /* size of this struct for forward/backward compatibility */ + __u32 open_flags; /* permissions requested for the operation on fd */ + size_t :0; +}; +#define bpf_get_fd_by_id_opts__last_field open_flags + LIBBPF_API int bpf_prog_get_fd_by_id(__u32 id); LIBBPF_API int bpf_map_get_fd_by_id(__u32 id); +LIBBPF_API int bpf_map_get_fd_by_id_opts(__u32 id, + const struct bpf_get_fd_by_id_opts *opts); LIBBPF_API int bpf_btf_get_fd_by_id(__u32 id); LIBBPF_API int bpf_link_get_fd_by_id(__u32 id); LIBBPF_API int bpf_obj_get_info_by_fd(int bpf_fd, void *info, __u32 *info_len); diff --git a/tools/lib/bpf/libbpf.map b/tools/lib/bpf/libbpf.map index 04fab9f1fdd7..2e665b21d84f 100644 --- a/tools/lib/bpf/libbpf.map +++ b/tools/lib/bpf/libbpf.map @@ -371,6 +371,7 @@ LIBBPF_1.0.0 { LIBBPF_1.1.0 { global: + bpf_map_get_fd_by_id_opts; user_ring_buffer__discard; user_ring_buffer__free; user_ring_buffer__new; From 8f13f168ea14333ac971e5404800b7fb3658e782 Mon Sep 17 00:00:00 2001 From: Roberto Sassu Date: Thu, 6 Oct 2022 13:07:33 +0200 Subject: [PATCH 0099/2002] libbpf: Introduce bpf_prog_get_fd_by_id_opts() Introduce bpf_prog_get_fd_by_id_opts(), for symmetry with bpf_map_get_fd_by_id_opts(), to let the caller pass the newly introduced data structure bpf_get_fd_by_id_opts. Keep the existing bpf_prog_get_fd_by_id(), and call bpf_prog_get_fd_by_id_opts() with NULL as opts argument, to prevent setting open_flags. Currently, the kernel does not support non-zero open_flags for bpf_prog_get_fd_by_id_opts(), and a call with them will result in an error returned by the bpf() system call. The caller should always pass zero open_flags. Signed-off-by: Roberto Sassu Signed-off-by: Andrii Nakryiko Link: https://lore.kernel.org/bpf/20221006110736.84253-4-roberto.sassu@huaweicloud.com --- tools/lib/bpf/bpf.c | 12 +++++++++++- tools/lib/bpf/bpf.h | 2 ++ tools/lib/bpf/libbpf.map | 1 + 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/tools/lib/bpf/bpf.c b/tools/lib/bpf/bpf.c index c08d7509553d..03be8c96bbac 100644 --- a/tools/lib/bpf/bpf.c +++ b/tools/lib/bpf/bpf.c @@ -935,19 +935,29 @@ int bpf_link_get_next_id(__u32 start_id, __u32 *next_id) return bpf_obj_get_next_id(start_id, next_id, BPF_LINK_GET_NEXT_ID); } -int bpf_prog_get_fd_by_id(__u32 id) +int bpf_prog_get_fd_by_id_opts(__u32 id, + const struct bpf_get_fd_by_id_opts *opts) { const size_t attr_sz = offsetofend(union bpf_attr, open_flags); union bpf_attr attr; int fd; + if (!OPTS_VALID(opts, bpf_get_fd_by_id_opts)) + return libbpf_err(-EINVAL); + memset(&attr, 0, attr_sz); attr.prog_id = id; + attr.open_flags = OPTS_GET(opts, open_flags, 0); fd = sys_bpf_fd(BPF_PROG_GET_FD_BY_ID, &attr, attr_sz); return libbpf_err_errno(fd); } +int bpf_prog_get_fd_by_id(__u32 id) +{ + return bpf_prog_get_fd_by_id_opts(id, NULL); +} + int bpf_map_get_fd_by_id_opts(__u32 id, const struct bpf_get_fd_by_id_opts *opts) { diff --git a/tools/lib/bpf/bpf.h b/tools/lib/bpf/bpf.h index 10ce38f0a9ef..4558bafbce14 100644 --- a/tools/lib/bpf/bpf.h +++ b/tools/lib/bpf/bpf.h @@ -374,6 +374,8 @@ struct bpf_get_fd_by_id_opts { #define bpf_get_fd_by_id_opts__last_field open_flags LIBBPF_API int bpf_prog_get_fd_by_id(__u32 id); +LIBBPF_API int bpf_prog_get_fd_by_id_opts(__u32 id, + const struct bpf_get_fd_by_id_opts *opts); LIBBPF_API int bpf_map_get_fd_by_id(__u32 id); LIBBPF_API int bpf_map_get_fd_by_id_opts(__u32 id, const struct bpf_get_fd_by_id_opts *opts); diff --git a/tools/lib/bpf/libbpf.map b/tools/lib/bpf/libbpf.map index 2e665b21d84f..c3604eaa220d 100644 --- a/tools/lib/bpf/libbpf.map +++ b/tools/lib/bpf/libbpf.map @@ -372,6 +372,7 @@ LIBBPF_1.0.0 { LIBBPF_1.1.0 { global: bpf_map_get_fd_by_id_opts; + bpf_prog_get_fd_by_id_opts; user_ring_buffer__discard; user_ring_buffer__free; user_ring_buffer__new; From 2ce7cbf2ba71bb03542af739af80e86b6855ae48 Mon Sep 17 00:00:00 2001 From: Roberto Sassu Date: Thu, 6 Oct 2022 13:07:34 +0200 Subject: [PATCH 0100/2002] libbpf: Introduce bpf_btf_get_fd_by_id_opts() Introduce bpf_btf_get_fd_by_id_opts(), for symmetry with bpf_map_get_fd_by_id_opts(), to let the caller pass the newly introduced data structure bpf_get_fd_by_id_opts. Keep the existing bpf_btf_get_fd_by_id(), and call bpf_btf_get_fd_by_id_opts() with NULL as opts argument, to prevent setting open_flags. Currently, the kernel does not support non-zero open_flags for bpf_btf_get_fd_by_id_opts(), and a call with them will result in an error returned by the bpf() system call. The caller should always pass zero open_flags. Signed-off-by: Roberto Sassu Signed-off-by: Andrii Nakryiko Link: https://lore.kernel.org/bpf/20221006110736.84253-5-roberto.sassu@huaweicloud.com --- tools/lib/bpf/bpf.c | 12 +++++++++++- tools/lib/bpf/bpf.h | 2 ++ tools/lib/bpf/libbpf.map | 1 + 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/tools/lib/bpf/bpf.c b/tools/lib/bpf/bpf.c index 03be8c96bbac..b95fed0c1644 100644 --- a/tools/lib/bpf/bpf.c +++ b/tools/lib/bpf/bpf.c @@ -981,19 +981,29 @@ int bpf_map_get_fd_by_id(__u32 id) return bpf_map_get_fd_by_id_opts(id, NULL); } -int bpf_btf_get_fd_by_id(__u32 id) +int bpf_btf_get_fd_by_id_opts(__u32 id, + const struct bpf_get_fd_by_id_opts *opts) { const size_t attr_sz = offsetofend(union bpf_attr, open_flags); union bpf_attr attr; int fd; + if (!OPTS_VALID(opts, bpf_get_fd_by_id_opts)) + return libbpf_err(-EINVAL); + memset(&attr, 0, attr_sz); attr.btf_id = id; + attr.open_flags = OPTS_GET(opts, open_flags, 0); fd = sys_bpf_fd(BPF_BTF_GET_FD_BY_ID, &attr, attr_sz); return libbpf_err_errno(fd); } +int bpf_btf_get_fd_by_id(__u32 id) +{ + return bpf_btf_get_fd_by_id_opts(id, NULL); +} + int bpf_link_get_fd_by_id(__u32 id) { const size_t attr_sz = offsetofend(union bpf_attr, open_flags); diff --git a/tools/lib/bpf/bpf.h b/tools/lib/bpf/bpf.h index 4558bafbce14..4b487305eeb8 100644 --- a/tools/lib/bpf/bpf.h +++ b/tools/lib/bpf/bpf.h @@ -380,6 +380,8 @@ LIBBPF_API int bpf_map_get_fd_by_id(__u32 id); LIBBPF_API int bpf_map_get_fd_by_id_opts(__u32 id, const struct bpf_get_fd_by_id_opts *opts); LIBBPF_API int bpf_btf_get_fd_by_id(__u32 id); +LIBBPF_API int bpf_btf_get_fd_by_id_opts(__u32 id, + const struct bpf_get_fd_by_id_opts *opts); LIBBPF_API int bpf_link_get_fd_by_id(__u32 id); LIBBPF_API int bpf_obj_get_info_by_fd(int bpf_fd, void *info, __u32 *info_len); diff --git a/tools/lib/bpf/libbpf.map b/tools/lib/bpf/libbpf.map index c3604eaa220d..7011d5eec67b 100644 --- a/tools/lib/bpf/libbpf.map +++ b/tools/lib/bpf/libbpf.map @@ -371,6 +371,7 @@ LIBBPF_1.0.0 { LIBBPF_1.1.0 { global: + bpf_btf_get_fd_by_id_opts; bpf_map_get_fd_by_id_opts; bpf_prog_get_fd_by_id_opts; user_ring_buffer__discard; From 97c8f9dd5db839f2387785ee936d0a5b257b31d3 Mon Sep 17 00:00:00 2001 From: Roberto Sassu Date: Thu, 6 Oct 2022 13:07:35 +0200 Subject: [PATCH 0101/2002] libbpf: Introduce bpf_link_get_fd_by_id_opts() Introduce bpf_link_get_fd_by_id_opts(), for symmetry with bpf_map_get_fd_by_id_opts(), to let the caller pass the newly introduced data structure bpf_get_fd_by_id_opts. Keep the existing bpf_link_get_fd_by_id(), and call bpf_link_get_fd_by_id_opts() with NULL as opts argument, to prevent setting open_flags. Currently, the kernel does not support non-zero open_flags for bpf_link_get_fd_by_id_opts(), and a call with them will result in an error returned by the bpf() system call. The caller should always pass zero open_flags. Signed-off-by: Roberto Sassu Signed-off-by: Andrii Nakryiko Link: https://lore.kernel.org/bpf/20221006110736.84253-6-roberto.sassu@huaweicloud.com --- tools/lib/bpf/bpf.c | 12 +++++++++++- tools/lib/bpf/bpf.h | 2 ++ tools/lib/bpf/libbpf.map | 1 + 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/tools/lib/bpf/bpf.c b/tools/lib/bpf/bpf.c index b95fed0c1644..9aff98f42a3d 100644 --- a/tools/lib/bpf/bpf.c +++ b/tools/lib/bpf/bpf.c @@ -1004,19 +1004,29 @@ int bpf_btf_get_fd_by_id(__u32 id) return bpf_btf_get_fd_by_id_opts(id, NULL); } -int bpf_link_get_fd_by_id(__u32 id) +int bpf_link_get_fd_by_id_opts(__u32 id, + const struct bpf_get_fd_by_id_opts *opts) { const size_t attr_sz = offsetofend(union bpf_attr, open_flags); union bpf_attr attr; int fd; + if (!OPTS_VALID(opts, bpf_get_fd_by_id_opts)) + return libbpf_err(-EINVAL); + memset(&attr, 0, attr_sz); attr.link_id = id; + attr.open_flags = OPTS_GET(opts, open_flags, 0); fd = sys_bpf_fd(BPF_LINK_GET_FD_BY_ID, &attr, attr_sz); return libbpf_err_errno(fd); } +int bpf_link_get_fd_by_id(__u32 id) +{ + return bpf_link_get_fd_by_id_opts(id, NULL); +} + int bpf_obj_get_info_by_fd(int bpf_fd, void *info, __u32 *info_len) { const size_t attr_sz = offsetofend(union bpf_attr, info); diff --git a/tools/lib/bpf/bpf.h b/tools/lib/bpf/bpf.h index 4b487305eeb8..a112e0ed1b19 100644 --- a/tools/lib/bpf/bpf.h +++ b/tools/lib/bpf/bpf.h @@ -383,6 +383,8 @@ LIBBPF_API int bpf_btf_get_fd_by_id(__u32 id); LIBBPF_API int bpf_btf_get_fd_by_id_opts(__u32 id, const struct bpf_get_fd_by_id_opts *opts); LIBBPF_API int bpf_link_get_fd_by_id(__u32 id); +LIBBPF_API int bpf_link_get_fd_by_id_opts(__u32 id, + const struct bpf_get_fd_by_id_opts *opts); LIBBPF_API int bpf_obj_get_info_by_fd(int bpf_fd, void *info, __u32 *info_len); struct bpf_prog_query_opts { diff --git a/tools/lib/bpf/libbpf.map b/tools/lib/bpf/libbpf.map index 7011d5eec67b..71bf5691a689 100644 --- a/tools/lib/bpf/libbpf.map +++ b/tools/lib/bpf/libbpf.map @@ -372,6 +372,7 @@ LIBBPF_1.0.0 { LIBBPF_1.1.0 { global: bpf_btf_get_fd_by_id_opts; + bpf_link_get_fd_by_id_opts; bpf_map_get_fd_by_id_opts; bpf_prog_get_fd_by_id_opts; user_ring_buffer__discard; From a9c7c18b57594c72a63fad749021b743c65a098b Mon Sep 17 00:00:00 2001 From: Roberto Sassu Date: Thu, 6 Oct 2022 13:07:36 +0200 Subject: [PATCH 0102/2002] selftests/bpf: Add tests for _opts variants of bpf_*_get_fd_by_id() Introduce the data_input map, write-protected with a small eBPF program implementing the lsm/bpf_map hook. Then, ensure that bpf_map_get_fd_by_id() and bpf_map_get_fd_by_id_opts() with NULL opts don't succeed due to requesting read-write access to the write-protected map. Also, ensure that bpf_map_get_fd_by_id_opts() with open_flags in opts set to BPF_F_RDONLY instead succeeds. After obtaining a read-only fd, ensure that only map lookup succeeds and not update. Ensure that update works only with the read-write fd obtained at program loading time, when the write protection was not yet enabled. Finally, ensure that the other _opts variants of bpf_*_get_fd_by_id() don't work if the BPF_F_RDONLY flag is set in opts (due to the kernel not handling the open_flags member of bpf_attr). Signed-off-by: Roberto Sassu Signed-off-by: Andrii Nakryiko Link: https://lore.kernel.org/bpf/20221006110736.84253-7-roberto.sassu@huaweicloud.com --- tools/testing/selftests/bpf/DENYLIST.s390x | 1 + .../bpf/prog_tests/libbpf_get_fd_by_id_opts.c | 87 +++++++++++++++++++ .../bpf/progs/test_libbpf_get_fd_by_id_opts.c | 36 ++++++++ 3 files changed, 124 insertions(+) create mode 100644 tools/testing/selftests/bpf/prog_tests/libbpf_get_fd_by_id_opts.c create mode 100644 tools/testing/selftests/bpf/progs/test_libbpf_get_fd_by_id_opts.c diff --git a/tools/testing/selftests/bpf/DENYLIST.s390x b/tools/testing/selftests/bpf/DENYLIST.s390x index 0fb03b8047d5..beef1232a47a 100644 --- a/tools/testing/selftests/bpf/DENYLIST.s390x +++ b/tools/testing/selftests/bpf/DENYLIST.s390x @@ -76,3 +76,4 @@ lookup_key # JIT does not support calling kernel f verify_pkcs7_sig # JIT does not support calling kernel function (kfunc) kfunc_dynptr_param # JIT does not support calling kernel function (kfunc) deny_namespace # failed to attach: ERROR: strerror_r(-524)=22 (trampoline) +libbpf_get_fd_by_id_opts # failed to attach: ERROR: strerror_r(-524)=22 (trampoline) diff --git a/tools/testing/selftests/bpf/prog_tests/libbpf_get_fd_by_id_opts.c b/tools/testing/selftests/bpf/prog_tests/libbpf_get_fd_by_id_opts.c new file mode 100644 index 000000000000..25e5dfa9c315 --- /dev/null +++ b/tools/testing/selftests/bpf/prog_tests/libbpf_get_fd_by_id_opts.c @@ -0,0 +1,87 @@ +// SPDX-License-Identifier: GPL-2.0 + +/* + * Copyright (C) 2022 Huawei Technologies Duesseldorf GmbH + * + * Author: Roberto Sassu + */ + +#include + +#include "test_libbpf_get_fd_by_id_opts.skel.h" + +void test_libbpf_get_fd_by_id_opts(void) +{ + struct test_libbpf_get_fd_by_id_opts *skel; + struct bpf_map_info info_m = {}; + __u32 len = sizeof(info_m), value; + int ret, zero = 0, fd = -1; + LIBBPF_OPTS(bpf_get_fd_by_id_opts, fd_opts_rdonly, + .open_flags = BPF_F_RDONLY, + ); + + skel = test_libbpf_get_fd_by_id_opts__open_and_load(); + if (!ASSERT_OK_PTR(skel, + "test_libbpf_get_fd_by_id_opts__open_and_load")) + return; + + ret = test_libbpf_get_fd_by_id_opts__attach(skel); + if (!ASSERT_OK(ret, "test_libbpf_get_fd_by_id_opts__attach")) + goto close_prog; + + ret = bpf_obj_get_info_by_fd(bpf_map__fd(skel->maps.data_input), + &info_m, &len); + if (!ASSERT_OK(ret, "bpf_obj_get_info_by_fd")) + goto close_prog; + + fd = bpf_map_get_fd_by_id(info_m.id); + if (!ASSERT_LT(fd, 0, "bpf_map_get_fd_by_id")) + goto close_prog; + + fd = bpf_map_get_fd_by_id_opts(info_m.id, NULL); + if (!ASSERT_LT(fd, 0, "bpf_map_get_fd_by_id_opts")) + goto close_prog; + + fd = bpf_map_get_fd_by_id_opts(info_m.id, &fd_opts_rdonly); + if (!ASSERT_GE(fd, 0, "bpf_map_get_fd_by_id_opts")) + goto close_prog; + + /* Map lookup should work with read-only fd. */ + ret = bpf_map_lookup_elem(fd, &zero, &value); + if (!ASSERT_OK(ret, "bpf_map_lookup_elem")) + goto close_prog; + + if (!ASSERT_EQ(value, 0, "map value mismatch")) + goto close_prog; + + /* Map update should not work with read-only fd. */ + ret = bpf_map_update_elem(fd, &zero, &len, BPF_ANY); + if (!ASSERT_LT(ret, 0, "bpf_map_update_elem")) + goto close_prog; + + /* Map update should work with read-write fd. */ + ret = bpf_map_update_elem(bpf_map__fd(skel->maps.data_input), &zero, + &len, BPF_ANY); + if (!ASSERT_OK(ret, "bpf_map_update_elem")) + goto close_prog; + + /* Prog get fd with opts set should not work (no kernel support). */ + ret = bpf_prog_get_fd_by_id_opts(0, &fd_opts_rdonly); + if (!ASSERT_EQ(ret, -EINVAL, "bpf_prog_get_fd_by_id_opts")) + goto close_prog; + + /* Link get fd with opts set should not work (no kernel support). */ + ret = bpf_link_get_fd_by_id_opts(0, &fd_opts_rdonly); + if (!ASSERT_EQ(ret, -EINVAL, "bpf_link_get_fd_by_id_opts")) + goto close_prog; + + /* BTF get fd with opts set should not work (no kernel support). */ + ret = bpf_btf_get_fd_by_id_opts(0, &fd_opts_rdonly); + ASSERT_EQ(ret, -EINVAL, "bpf_btf_get_fd_by_id_opts"); + +close_prog: + if (fd >= 0) + close(fd); + + test_libbpf_get_fd_by_id_opts__destroy(skel); +} diff --git a/tools/testing/selftests/bpf/progs/test_libbpf_get_fd_by_id_opts.c b/tools/testing/selftests/bpf/progs/test_libbpf_get_fd_by_id_opts.c new file mode 100644 index 000000000000..f5ac5f3e8919 --- /dev/null +++ b/tools/testing/selftests/bpf/progs/test_libbpf_get_fd_by_id_opts.c @@ -0,0 +1,36 @@ +// SPDX-License-Identifier: GPL-2.0 + +/* + * Copyright (C) 2022 Huawei Technologies Duesseldorf GmbH + * + * Author: Roberto Sassu + */ + +#include "vmlinux.h" +#include +#include +#include + +/* From include/linux/mm.h. */ +#define FMODE_WRITE 0x2 + +struct { + __uint(type, BPF_MAP_TYPE_ARRAY); + __uint(max_entries, 1); + __type(key, __u32); + __type(value, __u32); +} data_input SEC(".maps"); + +char _license[] SEC("license") = "GPL"; + +SEC("lsm/bpf_map") +int BPF_PROG(check_access, struct bpf_map *map, fmode_t fmode) +{ + if (map != (struct bpf_map *)&data_input) + return 0; + + if (fmode & FMODE_WRITE) + return -EACCES; + + return 0; +} From c2a94de38c74e86f49124ac14f093d6a5c377a90 Mon Sep 17 00:00:00 2001 From: Fedor Pchelkin Date: Mon, 25 Jul 2022 18:13:59 +0300 Subject: [PATCH 0103/2002] wifi: ath9k: hif_usb: fix memory leak of urbs in ath9k_hif_usb_dealloc_tx_urbs() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Syzkaller reports a long-known leak of urbs in ath9k_hif_usb_dealloc_tx_urbs(). The cause of the leak is that usb_get_urb() is called but usb_free_urb() (or usb_put_urb()) is not called inside usb_kill_urb() as urb->dev or urb->ep fields have not been initialized and usb_kill_urb() returns immediately. The patch removes trying to kill urbs located in hif_dev->tx.tx_buf because hif_dev->tx.tx_buf is not supposed to contain urbs which are in pending state (the pending urbs are stored in hif_dev->tx.tx_pending). The tx.tx_lock is acquired so there should not be any changes in the list. Found by Linux Verification Center (linuxtesting.org) with Syzkaller. Fixes: 03fb92a432ea ("ath9k: hif_usb: fix race condition between usb_get_urb() and usb_kill_anchored_urbs()") Signed-off-by: Fedor Pchelkin Signed-off-by: Alexey Khoroshilov Acked-by: Toke Høiland-Jørgensen Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20220725151359.283704-1-pchelkin@ispras.ru --- drivers/net/wireless/ath/ath9k/hif_usb.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/net/wireless/ath/ath9k/hif_usb.c b/drivers/net/wireless/ath/ath9k/hif_usb.c index 4d9002a9d082..b6b5ce9b9b68 100644 --- a/drivers/net/wireless/ath/ath9k/hif_usb.c +++ b/drivers/net/wireless/ath/ath9k/hif_usb.c @@ -780,14 +780,10 @@ static void ath9k_hif_usb_dealloc_tx_urbs(struct hif_device_usb *hif_dev) spin_lock_irqsave(&hif_dev->tx.tx_lock, flags); list_for_each_entry_safe(tx_buf, tx_buf_tmp, &hif_dev->tx.tx_buf, list) { - usb_get_urb(tx_buf->urb); - spin_unlock_irqrestore(&hif_dev->tx.tx_lock, flags); - usb_kill_urb(tx_buf->urb); list_del(&tx_buf->list); usb_free_urb(tx_buf->urb); kfree(tx_buf->buf); kfree(tx_buf); - spin_lock_irqsave(&hif_dev->tx.tx_lock, flags); } spin_unlock_irqrestore(&hif_dev->tx.tx_lock, flags); From d174768932a89c20f2ef6dee2602221143995c29 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Thu, 6 Oct 2022 12:20:54 -0700 Subject: [PATCH 0104/2002] wifi: ath9k: Remove -Warray-bounds exception MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit GCC-12 emits false positive -Warray-bounds warnings with CONFIG_UBSAN_SHIFT (-fsanitize=shift). This is fixed in GCC 13[1], and there is top-level Makefile logic to remove -Warray-bounds for known-bad GCC versions staring with commit f0be87c42cbd ("gcc-12: disable '-Warray-bounds' universally for now"). Remove the local work-around. [1] https://gcc.gnu.org/bugzilla/show_bug.cgi?id=105679 Signed-off-by: Kees Cook Acked-by: Toke Høiland-Jørgensen Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20221006192054.1742982-1-keescook@chromium.org --- drivers/net/wireless/ath/ath9k/Makefile | 5 ----- 1 file changed, 5 deletions(-) diff --git a/drivers/net/wireless/ath/ath9k/Makefile b/drivers/net/wireless/ath/ath9k/Makefile index 9bdfcee2f448..eff94bcd1f0a 100644 --- a/drivers/net/wireless/ath/ath9k/Makefile +++ b/drivers/net/wireless/ath/ath9k/Makefile @@ -45,11 +45,6 @@ ath9k_hw-y:= \ ar9003_eeprom.o \ ar9003_paprd.o -# FIXME: temporarily silence -Warray-bounds on non W=1+ builds -ifndef KBUILD_EXTRA_WARN -CFLAGS_mac.o += -Wno-array-bounds -endif - ath9k_hw-$(CONFIG_ATH9K_WOW) += ar9003_wow.o ath9k_hw-$(CONFIG_ATH9K_BTCOEX_SUPPORT) += btcoex.o \ From dd95f2239fc846795fc926787c3ae0ca701c9840 Mon Sep 17 00:00:00 2001 From: Fedor Pchelkin Date: Sat, 8 Oct 2022 14:49:17 +0300 Subject: [PATCH 0105/2002] wifi: ath9k: hif_usb: Fix use-after-free in ath9k_hif_usb_reg_in_cb() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It is possible that skb is freed in ath9k_htc_rx_msg(), then usb_submit_urb() fails and we try to free skb again. It causes use-after-free bug. Moreover, if alloc_skb() fails, urb->context becomes NULL but rx_buf is not freed and there can be a memory leak. The patch removes unnecessary nskb and makes skb processing more clear: it is supposed that ath9k_htc_rx_msg() either frees old skb or passes its managing to another callback function. Found by Linux Verification Center (linuxtesting.org) with Syzkaller. Fixes: 3deff76095c4 ("ath9k_htc: Increase URB count for REG_IN pipe") Signed-off-by: Fedor Pchelkin Signed-off-by: Alexey Khoroshilov Acked-by: Toke Høiland-Jørgensen Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20221008114917.21404-1-pchelkin@ispras.ru --- drivers/net/wireless/ath/ath9k/hif_usb.c | 28 +++++++++++++----------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/drivers/net/wireless/ath/ath9k/hif_usb.c b/drivers/net/wireless/ath/ath9k/hif_usb.c index b6b5ce9b9b68..6c653d46136e 100644 --- a/drivers/net/wireless/ath/ath9k/hif_usb.c +++ b/drivers/net/wireless/ath/ath9k/hif_usb.c @@ -708,14 +708,13 @@ static void ath9k_hif_usb_reg_in_cb(struct urb *urb) struct rx_buf *rx_buf = (struct rx_buf *)urb->context; struct hif_device_usb *hif_dev = rx_buf->hif_dev; struct sk_buff *skb = rx_buf->skb; - struct sk_buff *nskb; int ret; if (!skb) return; if (!hif_dev) - goto free; + goto free_skb; switch (urb->status) { case 0: @@ -724,7 +723,7 @@ static void ath9k_hif_usb_reg_in_cb(struct urb *urb) case -ECONNRESET: case -ENODEV: case -ESHUTDOWN: - goto free; + goto free_skb; default: skb_reset_tail_pointer(skb); skb_trim(skb, 0); @@ -735,25 +734,27 @@ static void ath9k_hif_usb_reg_in_cb(struct urb *urb) if (likely(urb->actual_length != 0)) { skb_put(skb, urb->actual_length); - /* Process the command first */ + /* + * Process the command first. + * skb is either freed here or passed to be + * managed to another callback function. + */ ath9k_htc_rx_msg(hif_dev->htc_handle, skb, skb->len, USB_REG_IN_PIPE); - - nskb = alloc_skb(MAX_REG_IN_BUF_SIZE, GFP_ATOMIC); - if (!nskb) { + skb = alloc_skb(MAX_REG_IN_BUF_SIZE, GFP_ATOMIC); + if (!skb) { dev_err(&hif_dev->udev->dev, "ath9k_htc: REG_IN memory allocation failure\n"); - urb->context = NULL; - return; + goto free_rx_buf; } - rx_buf->skb = nskb; + rx_buf->skb = skb; usb_fill_int_urb(urb, hif_dev->udev, usb_rcvintpipe(hif_dev->udev, USB_REG_IN_PIPE), - nskb->data, MAX_REG_IN_BUF_SIZE, + skb->data, MAX_REG_IN_BUF_SIZE, ath9k_hif_usb_reg_in_cb, rx_buf, 1); } @@ -762,12 +763,13 @@ resubmit: ret = usb_submit_urb(urb, GFP_ATOMIC); if (ret) { usb_unanchor_urb(urb); - goto free; + goto free_skb; } return; -free: +free_skb: kfree_skb(skb); +free_rx_buf: kfree(rx_buf); urb->context = NULL; } From 16ef02bad239f11f322df8425d302be62f0443ce Mon Sep 17 00:00:00 2001 From: Fedor Pchelkin Date: Sun, 9 Oct 2022 00:15:32 +0300 Subject: [PATCH 0106/2002] wifi: ath9k: verify the expected usb_endpoints are present MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The bug arises when a USB device claims to be an ATH9K but doesn't have the expected endpoints. (In this case there was an interrupt endpoint where the driver expected a bulk endpoint.) The kernel needs to be able to handle such devices without getting an internal error. usb 1-1: BOGUS urb xfer, pipe 3 != type 1 WARNING: CPU: 3 PID: 500 at drivers/usb/core/urb.c:493 usb_submit_urb+0xce2/0x1430 drivers/usb/core/urb.c:493 Modules linked in: CPU: 3 PID: 500 Comm: kworker/3:2 Not tainted 5.10.135-syzkaller #0 Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.12.0-1 04/01/2014 Workqueue: events request_firmware_work_func RIP: 0010:usb_submit_urb+0xce2/0x1430 drivers/usb/core/urb.c:493 Call Trace: ath9k_hif_usb_alloc_rx_urbs drivers/net/wireless/ath/ath9k/hif_usb.c:908 [inline] ath9k_hif_usb_alloc_urbs+0x75e/0x1010 drivers/net/wireless/ath/ath9k/hif_usb.c:1019 ath9k_hif_usb_dev_init drivers/net/wireless/ath/ath9k/hif_usb.c:1109 [inline] ath9k_hif_usb_firmware_cb+0x142/0x530 drivers/net/wireless/ath/ath9k/hif_usb.c:1242 request_firmware_work_func+0x12e/0x240 drivers/base/firmware_loader/main.c:1097 process_one_work+0x9af/0x1600 kernel/workqueue.c:2279 worker_thread+0x61d/0x12f0 kernel/workqueue.c:2425 kthread+0x3b4/0x4a0 kernel/kthread.c:313 ret_from_fork+0x22/0x30 arch/x86/entry/entry_64.S:299 Found by Linux Verification Center (linuxtesting.org) with Syzkaller. Suggested-by: Alan Stern Signed-off-by: Fedor Pchelkin Signed-off-by: Alexey Khoroshilov Acked-by: Toke Høiland-Jørgensen Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20221008211532.74583-1-pchelkin@ispras.ru --- drivers/net/wireless/ath/ath9k/hif_usb.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/drivers/net/wireless/ath/ath9k/hif_usb.c b/drivers/net/wireless/ath/ath9k/hif_usb.c index 6c653d46136e..1a2e0c7eeb02 100644 --- a/drivers/net/wireless/ath/ath9k/hif_usb.c +++ b/drivers/net/wireless/ath/ath9k/hif_usb.c @@ -1327,10 +1327,24 @@ static int send_eject_command(struct usb_interface *interface) static int ath9k_hif_usb_probe(struct usb_interface *interface, const struct usb_device_id *id) { + struct usb_endpoint_descriptor *bulk_in, *bulk_out, *int_in, *int_out; struct usb_device *udev = interface_to_usbdev(interface); + struct usb_host_interface *alt; struct hif_device_usb *hif_dev; int ret = 0; + /* Verify the expected endpoints are present */ + alt = interface->cur_altsetting; + if (usb_find_common_endpoints(alt, &bulk_in, &bulk_out, &int_in, &int_out) < 0 || + usb_endpoint_num(bulk_in) != USB_WLAN_RX_PIPE || + usb_endpoint_num(bulk_out) != USB_WLAN_TX_PIPE || + usb_endpoint_num(int_in) != USB_REG_IN_PIPE || + usb_endpoint_num(int_out) != USB_REG_OUT_PIPE) { + dev_err(&udev->dev, + "ath9k_htc: Device endpoint numbers are not the expected ones\n"); + return -ENODEV; + } + if (id->driver_info == STORAGE_DEVICE) return send_eject_command(interface); From 127da1aa61859c1eb27d7fc2d5b936e9e528815d Mon Sep 17 00:00:00 2001 From: Ching-Te Ku Date: Wed, 5 Oct 2022 16:32:07 +0800 Subject: [PATCH 0107/2002] wifi: rtw89: coex: move chip_ops::btc_bt_aci_imp to a generic code This chunk is to set fixed BT LNA2 at level5 when WiFi/BT shared BTG RFC to improve BT anti-interference ability from adjacent channel. Since all chips use the same setting, remove chip_ops::btc_bt_aci_imp. Signed-off-by: Ching-Te Ku Signed-off-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20221005083212.45683-2-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/coex.c | 9 +++++++-- drivers/net/wireless/realtek/rtw89/core.h | 1 - drivers/net/wireless/realtek/rtw89/rtw8852a.c | 14 -------------- drivers/net/wireless/realtek/rtw89/rtw8852c.c | 14 -------------- 4 files changed, 7 insertions(+), 31 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/coex.c b/drivers/net/wireless/realtek/rtw89/coex.c index bbdfa9ac203c..f21c73310fdb 100644 --- a/drivers/net/wireless/realtek/rtw89/coex.c +++ b/drivers/net/wireless/realtek/rtw89/coex.c @@ -1809,13 +1809,18 @@ static void _set_rf_trx_para(struct rtw89_dev *rtwdev) struct rtw89_btc_dm *dm = &btc->dm; struct rtw89_btc_wl_info *wl = &btc->cx.wl; struct rtw89_btc_bt_info *bt = &btc->cx.bt; + struct rtw89_btc_bt_link_info *b = &bt->link_info; struct rtw89_btc_rf_trx_para para; u32 wl_stb_chg = 0; u8 level_id = 0; if (!dm->freerun) { - dm->trx_para_level = 0; - chip->ops->btc_bt_aci_imp(rtwdev); + /* fix LNA2 = level-5 for BT ACI issue at BTG */ + if ((btc->dm.wl_btg_rx && b->profile_cnt.now != 0) || + dm->bt_only == 1) + dm->trx_para_level = 1; + else + dm->trx_para_level = 0; } level_id = (u8)dm->trx_para_level; diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index 51af91b30fc4..b04e7a54b1e0 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -2363,7 +2363,6 @@ struct rtw89_chip_ops { void (*btc_set_wl_pri)(struct rtw89_dev *rtwdev, u8 map, bool state); void (*btc_set_wl_txpwr_ctrl)(struct rtw89_dev *rtwdev, u32 txpwr_val); s8 (*btc_get_bt_rssi)(struct rtw89_dev *rtwdev, s8 val); - void (*btc_bt_aci_imp)(struct rtw89_dev *rtwdev); void (*btc_update_bt_cnt)(struct rtw89_dev *rtwdev); void (*btc_wl_s1_standby)(struct rtw89_dev *rtwdev, bool state); void (*btc_set_policy)(struct rtw89_dev *rtwdev, u16 policy_type); diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852a.c b/drivers/net/wireless/realtek/rtw89/rtw8852a.c index 5678683ec02a..b5aa8697a098 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852a.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852a.c @@ -1870,19 +1870,6 @@ static struct rtw89_btc_fbtc_mreg rtw89_btc_8852a_mon_reg[] = { RTW89_DEF_FBTC_MREG(REG_BT_MODEM, 4, 0x178), }; -static -void rtw8852a_btc_bt_aci_imp(struct rtw89_dev *rtwdev) -{ - struct rtw89_btc *btc = &rtwdev->btc; - struct rtw89_btc_dm *dm = &btc->dm; - struct rtw89_btc_bt_info *bt = &btc->cx.bt; - struct rtw89_btc_bt_link_info *b = &bt->link_info; - - /* fix LNA2 = level-5 for BT ACI issue at BTG */ - if (btc->dm.wl_btg_rx && b->profile_cnt.now != 0) - dm->trx_para_level = 1; -} - static void rtw8852a_btc_update_bt_cnt(struct rtw89_dev *rtwdev) { @@ -2041,7 +2028,6 @@ static const struct rtw89_chip_ops rtw8852a_chip_ops = { .btc_set_wl_pri = rtw8852a_btc_set_wl_pri, .btc_set_wl_txpwr_ctrl = rtw8852a_btc_set_wl_txpwr_ctrl, .btc_get_bt_rssi = rtw8852a_btc_get_bt_rssi, - .btc_bt_aci_imp = rtw8852a_btc_bt_aci_imp, .btc_update_bt_cnt = rtw8852a_btc_update_bt_cnt, .btc_wl_s1_standby = rtw8852a_btc_wl_s1_standby, .btc_set_wl_rx_gain = rtw8852a_btc_set_wl_rx_gain, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852c.c b/drivers/net/wireless/realtek/rtw89/rtw8852c.c index a5bcd3fcecb7..00f564be29e8 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852c.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852c.c @@ -2684,19 +2684,6 @@ static const struct rtw89_btc_fbtc_mreg rtw89_btc_8852c_mon_reg[] = { RTW89_DEF_FBTC_MREG(REG_BB, 4, 0x980), }; -static -void rtw8852c_btc_bt_aci_imp(struct rtw89_dev *rtwdev) -{ - struct rtw89_btc *btc = &rtwdev->btc; - struct rtw89_btc_dm *dm = &btc->dm; - struct rtw89_btc_bt_info *bt = &btc->cx.bt; - struct rtw89_btc_bt_link_info *b = &bt->link_info; - - /* fix LNA2 = level-5 for BT ACI issue at BTG */ - if (btc->dm.wl_btg_rx && b->profile_cnt.now != 0) - dm->trx_para_level = 1; -} - static void rtw8852c_btc_update_bt_cnt(struct rtw89_dev *rtwdev) { @@ -2893,7 +2880,6 @@ static const struct rtw89_chip_ops rtw8852c_chip_ops = { .btc_set_wl_pri = rtw8852c_btc_set_wl_pri, .btc_set_wl_txpwr_ctrl = rtw8852c_btc_set_wl_txpwr_ctrl, .btc_get_bt_rssi = rtw8852c_btc_get_bt_rssi, - .btc_bt_aci_imp = rtw8852c_btc_bt_aci_imp, .btc_update_bt_cnt = rtw8852c_btc_update_bt_cnt, .btc_wl_s1_standby = rtw8852c_btc_wl_s1_standby, .btc_set_wl_rx_gain = rtw8852c_btc_set_wl_rx_gain, From 0935bb1527d711b1af8e89d4ba200c302fb5ab2b Mon Sep 17 00:00:00 2001 From: Eric Huang Date: Wed, 5 Oct 2022 16:32:08 +0800 Subject: [PATCH 0108/2002] wifi: rtw89: parse PHY status only when PPDU is to_self Without this fix, some non-self packets are used to count CFO (center frequency offset), and average CFO has unstable variation. Then, it causes unexpected performance. Signed-off-by: Eric Huang Signed-off-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20221005083212.45683-3-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/core.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c index bc2994865372..a0fa9639b509 100644 --- a/drivers/net/wireless/realtek/rtw89/core.c +++ b/drivers/net/wireless/realtek/rtw89/core.c @@ -1255,6 +1255,9 @@ static int rtw89_core_rx_parse_phy_sts(struct rtw89_dev *rtwdev, if (phy_ppdu->ie < RTW89_CCK_PKT) return -EINVAL; + if (!phy_ppdu->to_self) + return 0; + pos = (u8 *)phy_ppdu->buf + PHY_STS_HDR_LEN; end = (u8 *)phy_ppdu->buf + phy_ppdu->len; while (pos < end) { From d0c820cc5bcf768598ca3e9f6e29f3e4e5589827 Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Wed, 5 Oct 2022 16:32:09 +0800 Subject: [PATCH 0109/2002] wifi: rtw89: 8852b: set proper configuration before loading NCTL Before loading RF NCTL table, we need to configure IQK/DPK clock and reset them, and then polling NCTL state ready. Since 8852BE needs additional one setting, add it by this patch. Also, give them proper names. Signed-off-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20221005083212.45683-4-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/phy.c | 12 +++++++----- drivers/net/wireless/realtek/rtw89/reg.h | 2 ++ 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/phy.c b/drivers/net/wireless/realtek/rtw89/phy.c index d9c4debecc62..fb7872225ee3 100644 --- a/drivers/net/wireless/realtek/rtw89/phy.c +++ b/drivers/net/wireless/realtek/rtw89/phy.c @@ -1368,13 +1368,15 @@ static void rtw89_phy_init_rf_nctl(struct rtw89_dev *rtwdev) int ret; /* IQK/DPK clock & reset */ - rtw89_phy_write32_set(rtwdev, 0x0c60, 0x3); - rtw89_phy_write32_set(rtwdev, 0x0c6c, 0x1); - rtw89_phy_write32_set(rtwdev, 0x58ac, 0x8000000); - rtw89_phy_write32_set(rtwdev, 0x78ac, 0x8000000); + rtw89_phy_write32_set(rtwdev, R_IOQ_IQK_DPK, 0x3); + rtw89_phy_write32_set(rtwdev, R_GNT_BT_WGT_EN, 0x1); + rtw89_phy_write32_set(rtwdev, R_P0_PATH_RST, 0x8000000); + rtw89_phy_write32_set(rtwdev, R_P1_PATH_RST, 0x8000000); + if (chip->chip_id == RTL8852B) + rtw89_phy_write32_set(rtwdev, R_IOQ_IQK_DPK, 0x2); /* check 0x8080 */ - rtw89_phy_write32(rtwdev, 0x8000, 0x8); + rtw89_phy_write32(rtwdev, R_NCTL_CFG, 0x8); ret = read_poll_timeout(rtw89_phy_nctl_poll, val, val == 0x4, 10, 1000, false, rtwdev); diff --git a/drivers/net/wireless/realtek/rtw89/reg.h b/drivers/net/wireless/realtek/rtw89/reg.h index 874cca85eaad..82af17d7d8d3 100644 --- a/drivers/net/wireless/realtek/rtw89/reg.h +++ b/drivers/net/wireless/realtek/rtw89/reg.h @@ -4039,6 +4039,7 @@ #define B_P0_RFM_TX_OPT BIT(6) #define B_P0_RFM_BT_EN BIT(5) #define B_P0_RFM_OUT GENMASK(4, 0) +#define R_P0_PATH_RST 0x58AC #define R_P0_TXDPD 0x58D4 #define B_P0_TXDPD GENMASK(31, 28) #define R_P0_TXPW_RSTB 0x58DC @@ -4083,6 +4084,7 @@ #define R_P1_RFCTM 0x7864 #define R_P1_RFCTM_RDY BIT(26) #define B_P1_RFCTM_VAL GENMASK(25, 20) +#define R_P1_PATH_RST 0x78AC #define R_P1_TXPW_RSTB 0x78DC #define B_P1_TXPW_RSTB_MANON BIT(30) #define B_P1_TXPW_RSTB_TSSI BIT(31) From 3e870b4817333a1a4b01805073da3e00449acf43 Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Wed, 5 Oct 2022 16:32:10 +0800 Subject: [PATCH 0110/2002] wifi: rtw89: 8852b: add HFC quota arrays HFC is short for HCI flow control. These arrays are used to set quota according to operating modes, which are SCC or download firmware. Signed-off-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20221005083212.45683-5-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/rtw8852b.c | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852b.c b/drivers/net/wireless/realtek/rtw89/rtw8852b.c index e9bcea35a72a..8424038d5338 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852b.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852b.c @@ -11,6 +11,37 @@ #include "rtw8852b_table.h" #include "txrx.h" +static const struct rtw89_hfc_ch_cfg rtw8852b_hfc_chcfg_pcie[] = { + {5, 343, grp_0}, /* ACH 0 */ + {5, 343, grp_0}, /* ACH 1 */ + {5, 343, grp_0}, /* ACH 2 */ + {5, 343, grp_0}, /* ACH 3 */ + {0, 0, grp_0}, /* ACH 4 */ + {0, 0, grp_0}, /* ACH 5 */ + {0, 0, grp_0}, /* ACH 6 */ + {0, 0, grp_0}, /* ACH 7 */ + {4, 344, grp_0}, /* B0MGQ */ + {4, 344, grp_0}, /* B0HIQ */ + {0, 0, grp_0}, /* B1MGQ */ + {0, 0, grp_0}, /* B1HIQ */ + {40, 0, 0} /* FWCMDQ */ +}; + +static const struct rtw89_hfc_pub_cfg rtw8852b_hfc_pubcfg_pcie = { + 448, /* Group 0 */ + 0, /* Group 1 */ + 448, /* Public Max */ + 0 /* WP threshold */ +}; + +static const struct rtw89_hfc_param_ini rtw8852b_hfc_param_ini_pcie[] = { + [RTW89_QTA_SCC] = {rtw8852b_hfc_chcfg_pcie, &rtw8852b_hfc_pubcfg_pcie, + &rtw89_mac_size.hfc_preccfg_pcie, RTW89_HCIFC_POH}, + [RTW89_QTA_DLFW] = {NULL, NULL, &rtw89_mac_size.hfc_preccfg_pcie, + RTW89_HCIFC_POH}, + [RTW89_QTA_INVALID] = {NULL}, +}; + static const struct rtw89_dle_mem rtw8852b_dle_mem_pcie[] = { [RTW89_QTA_SCC] = {RTW89_QTA_SCC, &rtw89_mac_size.wde_size6, &rtw89_mac_size.ple_size6, &rtw89_mac_size.wde_qt6, @@ -561,6 +592,7 @@ const struct rtw89_chip_info rtw8852b_chip_info = { .ops = &rtw8852b_chip_ops, .fifo_size = 196608, .dle_scc_rsvd_size = 98304, + .hfc_param_ini = rtw8852b_hfc_param_ini_pcie, .dle_mem = rtw8852b_dle_mem_pcie, .sec_ctrl_efuse_size = 4, .physical_efuse_size = 1216, From 6e5125bcbaf810520969c121c7f12f20b8f3987d Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Wed, 5 Oct 2022 16:32:11 +0800 Subject: [PATCH 0111/2002] wifi: rtw89: make generic functions to convert subband gain index The gain tables use different domain index, so we need to convert the index from subband of chandef. Since these conversion functions can share with 8852b, make generic functions for further use. Signed-off-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20221005083212.45683-6-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/phy.h | 44 ++++++++++++++++++ drivers/net/wireless/realtek/rtw89/rtw8852c.c | 46 +------------------ 2 files changed, 46 insertions(+), 44 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/phy.h b/drivers/net/wireless/realtek/rtw89/phy.h index 030a7c904a28..1129d462bfbd 100644 --- a/drivers/net/wireless/realtek/rtw89/phy.h +++ b/drivers/net/wireless/realtek/rtw89/phy.h @@ -374,6 +374,50 @@ static inline u32 rtw89_phy_read32_mask(struct rtw89_dev *rtwdev, return rtw89_read32_mask(rtwdev, addr | RTW89_PHY_ADDR_OFFSET, mask); } +static inline +enum rtw89_gain_offset rtw89_subband_to_gain_offset_band_of_ofdm(enum rtw89_subband subband) +{ + switch (subband) { + default: + case RTW89_CH_2G: + return RTW89_GAIN_OFFSET_2G_OFDM; + case RTW89_CH_5G_BAND_1: + return RTW89_GAIN_OFFSET_5G_LOW; + case RTW89_CH_5G_BAND_3: + return RTW89_GAIN_OFFSET_5G_MID; + case RTW89_CH_5G_BAND_4: + return RTW89_GAIN_OFFSET_5G_HIGH; + } +} + +static inline +enum rtw89_phy_bb_gain_band rtw89_subband_to_bb_gain_band(enum rtw89_subband subband) +{ + switch (subband) { + default: + case RTW89_CH_2G: + return RTW89_BB_GAIN_BAND_2G; + case RTW89_CH_5G_BAND_1: + return RTW89_BB_GAIN_BAND_5G_L; + case RTW89_CH_5G_BAND_3: + return RTW89_BB_GAIN_BAND_5G_M; + case RTW89_CH_5G_BAND_4: + return RTW89_BB_GAIN_BAND_5G_H; + case RTW89_CH_6G_BAND_IDX0: + case RTW89_CH_6G_BAND_IDX1: + return RTW89_BB_GAIN_BAND_6G_L; + case RTW89_CH_6G_BAND_IDX2: + case RTW89_CH_6G_BAND_IDX3: + return RTW89_BB_GAIN_BAND_6G_M; + case RTW89_CH_6G_BAND_IDX4: + case RTW89_CH_6G_BAND_IDX5: + return RTW89_BB_GAIN_BAND_6G_H; + case RTW89_CH_6G_BAND_IDX6: + case RTW89_CH_6G_BAND_IDX7: + return RTW89_BB_GAIN_BAND_6G_UH; + } +} + enum rtw89_rfk_flag { RTW89_RFK_F_WRF = 0, RTW89_RFK_F_WM = 1, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852c.c b/drivers/net/wireless/realtek/rtw89/rtw8852c.c index 00f564be29e8..f6bcac826816 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852c.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852c.c @@ -788,40 +788,12 @@ static const struct rtw8852c_bb_gain_op1db bb_gain_op1db_a = { .mask_tia0_lna6 = 0xff000000, }; -static enum rtw89_phy_bb_gain_band -rtw8852c_mapping_gain_band(enum rtw89_subband subband) -{ - switch (subband) { - default: - case RTW89_CH_2G: - return RTW89_BB_GAIN_BAND_2G; - case RTW89_CH_5G_BAND_1: - return RTW89_BB_GAIN_BAND_5G_L; - case RTW89_CH_5G_BAND_3: - return RTW89_BB_GAIN_BAND_5G_M; - case RTW89_CH_5G_BAND_4: - return RTW89_BB_GAIN_BAND_5G_H; - case RTW89_CH_6G_BAND_IDX0: - case RTW89_CH_6G_BAND_IDX1: - return RTW89_BB_GAIN_BAND_6G_L; - case RTW89_CH_6G_BAND_IDX2: - case RTW89_CH_6G_BAND_IDX3: - return RTW89_BB_GAIN_BAND_6G_M; - case RTW89_CH_6G_BAND_IDX4: - case RTW89_CH_6G_BAND_IDX5: - return RTW89_BB_GAIN_BAND_6G_H; - case RTW89_CH_6G_BAND_IDX6: - case RTW89_CH_6G_BAND_IDX7: - return RTW89_BB_GAIN_BAND_6G_UH; - } -} - static void rtw8852c_set_gain_error(struct rtw89_dev *rtwdev, enum rtw89_subband subband, enum rtw89_rf_path path) { const struct rtw89_phy_bb_gain_info *gain = &rtwdev->bb_gain; - u8 gain_band = rtw8852c_mapping_gain_band(subband); + u8 gain_band = rtw89_subband_to_bb_gain_band(subband); s32 val; u32 reg; u32 mask; @@ -979,21 +951,7 @@ static void rtw8852c_set_gain_offset(struct rtw89_dev *rtwdev, rtw89_phy_write32_mask(rtwdev, R_RPL_OFST, B_RPL_OFST_MASK, tmp & 0x7f); } - switch (chan->subband_type) { - default: - case RTW89_CH_2G: - gain_band = RTW89_GAIN_OFFSET_2G_OFDM; - break; - case RTW89_CH_5G_BAND_1: - gain_band = RTW89_GAIN_OFFSET_5G_LOW; - break; - case RTW89_CH_5G_BAND_3: - gain_band = RTW89_GAIN_OFFSET_5G_MID; - break; - case RTW89_CH_5G_BAND_4: - gain_band = RTW89_GAIN_OFFSET_5G_HIGH; - break; - } + gain_band = rtw89_subband_to_gain_offset_band_of_ofdm(chan->subband_type); offset_q0 = -efuse_gain->offset[path][gain_band]; offset_base_q4 = efuse_gain->offset_base[phy_idx]; From 6b0698984eb02f3e0dfd3c152df69f87c903e07f Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Wed, 5 Oct 2022 16:32:12 +0800 Subject: [PATCH 0112/2002] wifi: rtw89: 8852b: add chip_ops::set_channel set_channel is main function to configure channel and bandwidth for all layers, namely MAC, BB and RF. Additionally, MAC layer enables CCK rate checking to avoid wrong rate from driver. BB layer configures SCO (Sample Clock Offset) for CCK, TX gain error/offset, and reset baseband hardware circuit after all configurations done. Signed-off-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20221005083212.45683-7-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/core.h | 1 + drivers/net/wireless/realtek/rtw89/phy.c | 9 + drivers/net/wireless/realtek/rtw89/phy.h | 2 + drivers/net/wireless/realtek/rtw89/reg.h | 58 +- drivers/net/wireless/realtek/rtw89/rtw8852b.c | 564 ++++++++++++++++++ .../net/wireless/realtek/rtw89/rtw8852b_rfk.c | 256 ++++++++ .../net/wireless/realtek/rtw89/rtw8852b_rfk.h | 14 + 7 files changed, 903 insertions(+), 1 deletion(-) create mode 100644 drivers/net/wireless/realtek/rtw89/rtw8852b_rfk.c create mode 100644 drivers/net/wireless/realtek/rtw89/rtw8852b_rfk.h diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index b04e7a54b1e0..0a0343608ba6 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -3429,6 +3429,7 @@ struct rtw89_phy_efuse_gain { bool comp_valid; s8 offset[RF_PATH_MAX][RTW89_GAIN_OFFSET_NR]; /* S(8, 0) */ s8 offset_base[RTW89_PHY_MAX]; /* S(8, 4) */ + s8 rssi_base[RTW89_PHY_MAX]; /* S(8, 4) */ s8 comp[RF_PATH_MAX][RTW89_SUBBAND_NR]; /* S(8, 0) */ }; diff --git a/drivers/net/wireless/realtek/rtw89/phy.c b/drivers/net/wireless/realtek/rtw89/phy.c index fb7872225ee3..d3f47379e443 100644 --- a/drivers/net/wireless/realtek/rtw89/phy.c +++ b/drivers/net/wireless/realtek/rtw89/phy.c @@ -1427,6 +1427,15 @@ void rtw89_phy_write32_idx(struct rtw89_dev *rtwdev, u32 addr, u32 mask, } EXPORT_SYMBOL(rtw89_phy_write32_idx); +u32 rtw89_phy_read32_idx(struct rtw89_dev *rtwdev, u32 addr, u32 mask, + enum rtw89_phy_idx phy_idx) +{ + if (rtwdev->dbcc_en && phy_idx == RTW89_PHY_1) + addr += rtw89_phy0_phy1_offset(rtwdev, addr); + return rtw89_phy_read32_mask(rtwdev, addr, mask); +} +EXPORT_SYMBOL(rtw89_phy_read32_idx); + void rtw89_phy_set_phy_regs(struct rtw89_dev *rtwdev, u32 addr, u32 mask, u32 val) { diff --git a/drivers/net/wireless/realtek/rtw89/phy.h b/drivers/net/wireless/realtek/rtw89/phy.h index 1129d462bfbd..1e122b1498ba 100644 --- a/drivers/net/wireless/realtek/rtw89/phy.h +++ b/drivers/net/wireless/realtek/rtw89/phy.h @@ -499,6 +499,8 @@ void rtw89_phy_config_rf_reg_v1(struct rtw89_dev *rtwdev, void rtw89_phy_dm_init(struct rtw89_dev *rtwdev); void rtw89_phy_write32_idx(struct rtw89_dev *rtwdev, u32 addr, u32 mask, u32 data, enum rtw89_phy_idx phy_idx); +u32 rtw89_phy_read32_idx(struct rtw89_dev *rtwdev, u32 addr, u32 mask, + enum rtw89_phy_idx phy_idx); void rtw89_phy_load_txpwr_byrate(struct rtw89_dev *rtwdev, const struct rtw89_txpwr_table *tbl); s8 rtw89_phy_read_txpwr_limit(struct rtw89_dev *rtwdev, u8 band, diff --git a/drivers/net/wireless/realtek/rtw89/reg.h b/drivers/net/wireless/realtek/rtw89/reg.h index 82af17d7d8d3..1539973296cd 100644 --- a/drivers/net/wireless/realtek/rtw89/reg.h +++ b/drivers/net/wireless/realtek/rtw89/reg.h @@ -3313,6 +3313,10 @@ #define CFGCH_BAND1_2G 0 #define CFGCH_BAND1_5G 1 #define CFGCH_BAND1_6G 3 +#define RR_CFGCH_POW_LCK BIT(15) +#define RR_CFGCH_TRX_AH BIT(14) +#define RR_CFGCH_BCN BIT(13) +#define RR_CFGCH_BW2 BIT(12) #define RR_CFGCH_BAND0 GENMASK(9, 8) #define CFGCH_BAND0_2G 0 #define CFGCH_BAND0_5G 1 @@ -3448,14 +3452,31 @@ #define RR_TIA_N6 BIT(8) #define RR_MIXER 0x9f #define RR_MIXER_GN GENMASK(4, 3) +#define RR_POW 0xa0 +#define RR_POW_SYN GENMASK(3, 2) #define RR_LOGEN 0xa3 #define RR_LOGEN_RPT GENMASK(19, 16) +#define RR_SX 0xaf +#define RR_LDO 0xb1 +#define RR_LDO_SEL GENMASK(8, 6) +#define RR_VCO 0xb2 +#define RR_LPF 0xb7 +#define RR_LPF_BUSY BIT(8) #define RR_XTALX2 0xb8 #define RR_MALSEL 0xbe +#define RR_SYNFB 0xc5 +#define RR_SYNFB_LK BIT(15) +#define RR_LCKST 0xcf +#define RR_LCKST_BIN BIT(0) #define RR_LCK_TRG 0xd3 #define RR_LCK_TRGSEL BIT(8) +#define RR_MMD 0xd5 +#define RR_MMD_RST_EN BIT(8) +#define RR_MMD_RST_SYN BIT(6) #define RR_IQKPLL 0xdc #define RR_IQKPLL_MOD GENMASK(9, 8) +#define RR_SYNLUT 0xdd +#define RR_SYNLUT_MOD BIT(4) #define RR_RCKD 0xde #define RR_RCKD_POW GENMASK(19, 13) #define RR_RCKD_BW BIT(2) @@ -3624,6 +3645,8 @@ #define R_P0_RFMODE 0x12AC #define B_P0_RFMODE_ORI_TXRX_FTM_TX GENMASK(31, 4) #define B_P0_RFMODE_MUX GENMASK(11, 4) +#define R_P0_RFMODE_ORI_RX 0x12AC +#define B_P0_RFMODE_ORI_RX_ALL GENMASK(23, 12) #define R_P0_NRBW 0x12B8 #define B_P0_NRBW_DBG BIT(30) #define R_S0_RXDC 0x12D4 @@ -3717,6 +3740,8 @@ #define B_RXCCA_DIS_V1 BIT(0) #define R_RXSC 0x237C #define B_RXSC_EN BIT(0) +#define R_RX_RPL_OFST 0x23AC +#define B_RX_RPL_OFST_CCK_MASK GENMASK(6, 0) #define R_RXSCOBC 0x23B0 #define B_RXSCOBC_TH GENMASK(18, 0) #define R_RXSCOCCK 0x23B4 @@ -3733,6 +3758,8 @@ #define R_P1_RFMODE 0x32AC #define B_P1_RFMODE_ORI_TXRX_FTM_TX GENMASK(31, 4) #define B_P1_RFMODE_MUX GENMASK(11, 4) +#define R_P1_RFMODE_ORI_RX 0x32AC +#define B_P1_RFMODE_ORI_RX_ALL GENMASK(23, 12) #define R_P1_DBGMOD 0x32B8 #define B_P1_DBGMOD_ON BIT(30) #define R_S1_RXDC 0x32D4 @@ -3766,7 +3793,10 @@ #define R_T2F_GI_COMB 0x4424 #define B_T2F_GI_COMB_EN BIT(2) #define R_BT_DYN_DC_EST_EN 0x441C +#define R_BT_DYN_DC_EST_EN_V1 0x4420 #define B_BT_DYN_DC_EST_EN_MSK BIT(31) +#define R_ASSIGN_SBD_OPT_V1 0x4440 +#define B_ASSIGN_SBD_OPT_EN_V1 BIT(31) #define R_ASSIGN_SBD_OPT 0x4450 #define B_ASSIGN_SBD_OPT_EN BIT(24) #define R_DCFO_COMP_S0 0x448C @@ -3916,20 +3946,42 @@ #define R_2P4G_BAND 0x4970 #define B_2P4G_BAND_SEL BIT(1) #define R_FC0_BW 0x4974 -#define B_FC0_BW_INV GENMASK(6, 0) +#define R_FC0_BW_V1 0x49C0 #define B_FC0_BW_SET GENMASK(31, 30) #define B_ANT_RX_BT_SEG0 GENMASK(25, 22) #define B_ANT_RX_1RCCA_SEG1 GENMASK(21, 18) #define B_ANT_RX_1RCCA_SEG0 GENMASK(17, 14) +#define B_FC0_BW_INV GENMASK(6, 0) #define R_CHBW_MOD 0x4978 +#define R_CHBW_MOD_V1 0x49C4 #define B_BT_SHARE BIT(14) #define B_CHBW_MOD_SBW GENMASK(13, 12) #define B_CHBW_MOD_PRICH GENMASK(11, 8) #define B_ANT_RX_SEG0 GENMASK(3, 0) +#define R_P0_RPL1 0x49B0 +#define B_P0_RPL1_41_MASK GENMASK(31, 24) +#define B_P0_RPL1_40_MASK GENMASK(23, 16) +#define B_P0_RPL1_20_MASK GENMASK(15, 8) +#define B_P0_RPL1_MASK (B_P0_RPL1_41_MASK | B_P0_RPL1_40_MASK | B_P0_RPL1_20_MASK) +#define B_P0_RPL1_SHIFT 8 +#define B_P0_RPL1_BIAS_MASK GENMASK(7, 0) +#define R_P0_RPL2 0x49B4 +#define B_P0_RTL2_8A_MASK GENMASK(31, 24) +#define B_P0_RTL2_81_MASK GENMASK(23, 16) +#define B_P0_RTL2_80_MASK GENMASK(15, 8) +#define B_P0_RTL2_42_MASK GENMASK(7, 0) +#define R_P0_RPL3 0x49B8 +#define B_P0_RTL3_89_MASK GENMASK(31, 24) +#define B_P0_RTL3_84_MASK GENMASK(23, 16) +#define B_P0_RTL3_83_MASK GENMASK(15, 8) +#define B_P0_RTL3_82_MASK GENMASK(7, 0) #define R_PD_BOOST_EN 0x49E8 #define B_PD_BOOST_EN BIT(7) #define R_P1_BACKOFF_IBADC_V1 0x49F0 #define B_P1_BACKOFF_IBADC_V1 GENMASK(31, 26) +#define R_P1_RPL1 0x4A00 +#define R_P1_RPL2 0x4A04 +#define R_P1_RPL3 0x4A08 #define R_BK_FC0_INV_V1 0x4A1C #define B_BK_FC0_INV_MSK_V1 GENMASK(18, 0) #define R_CCK_FC0_INV_V1 0x4A20 @@ -3940,8 +3992,10 @@ #define B_P1_AGC_EN BIT(31) #define R_PATH1_TIA_INIT_V1 0x4AA8 #define B_PATH1_TIA_INIT_IDX_MSK_V1 BIT(9) +#define R_P0_AGC_RSVD 0x4ACC #define R_PATH0_RXBB_V1 0x4AD4 #define B_PATH0_RXBB_MSK_V1 GENMASK(31, 0) +#define R_P1_AGC_RSVD 0x4AD8 #define R_PATH1_RXBB_V1 0x4AE0 #define B_PATH1_RXBB_MSK_V1 GENMASK(31, 0) #define R_PATH0_BT_BACKOFF_V1 0x4AE4 @@ -3957,6 +4011,7 @@ #define B_PATH0_NOTCH2_EN BIT(12) #define B_PATH0_NOTCH2_VAL GENMASK(11, 0) #define R_PATH0_5MDET 0x4C4C +#define R_PATH0_5MDET_V1 0x46F8 #define B_PATH0_5MDET_EN BIT(12) #define B_PATH0_5MDET_SB2 BIT(8) #define B_PATH0_5MDET_SB0 BIT(6) @@ -3970,6 +4025,7 @@ #define B_PATH1_NOTCH2_EN BIT(12) #define B_PATH1_NOTCH2_VAL GENMASK(11, 0) #define R_PATH1_5MDET 0x4D10 +#define R_PATH1_5MDET_V1 0x47B8 #define B_PATH1_5MDET_EN BIT(12) #define B_PATH1_5MDET_SB2 BIT(8) #define B_PATH1_5MDET_SB0 BIT(6) diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852b.c b/drivers/net/wireless/realtek/rtw89/rtw8852b.c index 8424038d5338..b50fff00b139 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852b.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852b.c @@ -8,6 +8,7 @@ #include "phy.h" #include "reg.h" #include "rtw8852b.h" +#include "rtw8852b_rfk.h" #include "rtw8852b_table.h" #include "txrx.h" @@ -334,6 +335,568 @@ static void rtw8852b_power_trim(struct rtw89_dev *rtwdev) rtw8852b_pa_bias_trim(rtwdev); } +static void rtw8852b_set_channel_mac(struct rtw89_dev *rtwdev, + const struct rtw89_chan *chan, + u8 mac_idx) +{ + u32 rf_mod = rtw89_mac_reg_by_idx(R_AX_WMAC_RFMOD, mac_idx); + u32 sub_carr = rtw89_mac_reg_by_idx(R_AX_TX_SUB_CARRIER_VALUE, mac_idx); + u32 chk_rate = rtw89_mac_reg_by_idx(R_AX_TXRATE_CHK, mac_idx); + u8 txsc20 = 0, txsc40 = 0; + + switch (chan->band_width) { + case RTW89_CHANNEL_WIDTH_80: + txsc40 = rtw89_phy_get_txsc(rtwdev, chan, RTW89_CHANNEL_WIDTH_40); + fallthrough; + case RTW89_CHANNEL_WIDTH_40: + txsc20 = rtw89_phy_get_txsc(rtwdev, chan, RTW89_CHANNEL_WIDTH_20); + break; + default: + break; + } + + switch (chan->band_width) { + case RTW89_CHANNEL_WIDTH_80: + rtw89_write8_mask(rtwdev, rf_mod, B_AX_WMAC_RFMOD_MASK, BIT(1)); + rtw89_write32(rtwdev, sub_carr, txsc20 | (txsc40 << 4)); + break; + case RTW89_CHANNEL_WIDTH_40: + rtw89_write8_mask(rtwdev, rf_mod, B_AX_WMAC_RFMOD_MASK, BIT(0)); + rtw89_write32(rtwdev, sub_carr, txsc20); + break; + case RTW89_CHANNEL_WIDTH_20: + rtw89_write8_clr(rtwdev, rf_mod, B_AX_WMAC_RFMOD_MASK); + rtw89_write32(rtwdev, sub_carr, 0); + break; + default: + break; + } + + if (chan->channel > 14) { + rtw89_write8_clr(rtwdev, chk_rate, B_AX_BAND_MODE); + rtw89_write8_set(rtwdev, chk_rate, + B_AX_CHECK_CCK_EN | B_AX_RTS_LIMIT_IN_OFDM6); + } else { + rtw89_write8_set(rtwdev, chk_rate, B_AX_BAND_MODE); + rtw89_write8_clr(rtwdev, chk_rate, + B_AX_CHECK_CCK_EN | B_AX_RTS_LIMIT_IN_OFDM6); + } +} + +static const u32 rtw8852b_sco_barker_threshold[14] = { + 0x1cfea, 0x1d0e1, 0x1d1d7, 0x1d2cd, 0x1d3c3, 0x1d4b9, 0x1d5b0, 0x1d6a6, + 0x1d79c, 0x1d892, 0x1d988, 0x1da7f, 0x1db75, 0x1ddc4 +}; + +static const u32 rtw8852b_sco_cck_threshold[14] = { + 0x27de3, 0x27f35, 0x28088, 0x281da, 0x2832d, 0x2847f, 0x285d2, 0x28724, + 0x28877, 0x289c9, 0x28b1c, 0x28c6e, 0x28dc1, 0x290ed +}; + +static void rtw8852b_ctrl_sco_cck(struct rtw89_dev *rtwdev, u8 primary_ch) +{ + u8 ch_element = primary_ch - 1; + + rtw89_phy_write32_mask(rtwdev, R_RXSCOBC, B_RXSCOBC_TH, + rtw8852b_sco_barker_threshold[ch_element]); + rtw89_phy_write32_mask(rtwdev, R_RXSCOCCK, B_RXSCOCCK_TH, + rtw8852b_sco_cck_threshold[ch_element]); +} + +static u8 rtw8852b_sco_mapping(u8 central_ch) +{ + if (central_ch == 1) + return 109; + else if (central_ch >= 2 && central_ch <= 6) + return 108; + else if (central_ch >= 7 && central_ch <= 10) + return 107; + else if (central_ch >= 11 && central_ch <= 14) + return 106; + else if (central_ch == 36 || central_ch == 38) + return 51; + else if (central_ch >= 40 && central_ch <= 58) + return 50; + else if (central_ch >= 60 && central_ch <= 64) + return 49; + else if (central_ch == 100 || central_ch == 102) + return 48; + else if (central_ch >= 104 && central_ch <= 126) + return 47; + else if (central_ch >= 128 && central_ch <= 151) + return 46; + else if (central_ch >= 153 && central_ch <= 177) + return 45; + else + return 0; +} + +struct rtw8852b_bb_gain { + u32 gain_g[BB_PATH_NUM_8852B]; + u32 gain_a[BB_PATH_NUM_8852B]; + u32 gain_mask; +}; + +static const struct rtw8852b_bb_gain bb_gain_lna[LNA_GAIN_NUM] = { + { .gain_g = {0x4678, 0x475C}, .gain_a = {0x45DC, 0x4740}, + .gain_mask = 0x00ff0000 }, + { .gain_g = {0x4678, 0x475C}, .gain_a = {0x45DC, 0x4740}, + .gain_mask = 0xff000000 }, + { .gain_g = {0x467C, 0x4760}, .gain_a = {0x4660, 0x4744}, + .gain_mask = 0x000000ff }, + { .gain_g = {0x467C, 0x4760}, .gain_a = {0x4660, 0x4744}, + .gain_mask = 0x0000ff00 }, + { .gain_g = {0x467C, 0x4760}, .gain_a = {0x4660, 0x4744}, + .gain_mask = 0x00ff0000 }, + { .gain_g = {0x467C, 0x4760}, .gain_a = {0x4660, 0x4744}, + .gain_mask = 0xff000000 }, + { .gain_g = {0x4680, 0x4764}, .gain_a = {0x4664, 0x4748}, + .gain_mask = 0x000000ff }, +}; + +static const struct rtw8852b_bb_gain bb_gain_tia[TIA_GAIN_NUM] = { + { .gain_g = {0x4680, 0x4764}, .gain_a = {0x4664, 0x4748}, + .gain_mask = 0x00ff0000 }, + { .gain_g = {0x4680, 0x4764}, .gain_a = {0x4664, 0x4748}, + .gain_mask = 0xff000000 }, +}; + +static void rtw8852b_set_gain_error(struct rtw89_dev *rtwdev, + enum rtw89_subband subband, + enum rtw89_rf_path path) +{ + const struct rtw89_phy_bb_gain_info *gain = &rtwdev->bb_gain; + u8 gain_band = rtw89_subband_to_bb_gain_band(subband); + s32 val; + u32 reg; + u32 mask; + int i; + + for (i = 0; i < LNA_GAIN_NUM; i++) { + if (subband == RTW89_CH_2G) + reg = bb_gain_lna[i].gain_g[path]; + else + reg = bb_gain_lna[i].gain_a[path]; + + mask = bb_gain_lna[i].gain_mask; + val = gain->lna_gain[gain_band][path][i]; + rtw89_phy_write32_mask(rtwdev, reg, mask, val); + } + + for (i = 0; i < TIA_GAIN_NUM; i++) { + if (subband == RTW89_CH_2G) + reg = bb_gain_tia[i].gain_g[path]; + else + reg = bb_gain_tia[i].gain_a[path]; + + mask = bb_gain_tia[i].gain_mask; + val = gain->tia_gain[gain_band][path][i]; + rtw89_phy_write32_mask(rtwdev, reg, mask, val); + } +} + +static void rtw8852b_set_gain_offset(struct rtw89_dev *rtwdev, + enum rtw89_subband subband, + enum rtw89_phy_idx phy_idx) +{ + static const u32 gain_err_addr[2] = {R_P0_AGC_RSVD, R_P1_AGC_RSVD}; + static const u32 rssi_ofst_addr[2] = {R_PATH0_G_TIA1_LNA6_OP1DB_V1, + R_PATH1_G_TIA1_LNA6_OP1DB_V1}; + struct rtw89_hal *hal = &rtwdev->hal; + struct rtw89_phy_efuse_gain *efuse_gain = &rtwdev->efuse_gain; + enum rtw89_gain_offset gain_ofdm_band; + s32 offset_a, offset_b; + s32 offset_ofdm, offset_cck; + s32 tmp; + u8 path; + + if (!efuse_gain->comp_valid) + goto next; + + for (path = RF_PATH_A; path < BB_PATH_NUM_8852B; path++) { + tmp = efuse_gain->comp[path][subband]; + tmp = clamp_t(s32, tmp << 2, S8_MIN, S8_MAX); + rtw89_phy_write32_mask(rtwdev, gain_err_addr[path], MASKBYTE0, tmp); + } + +next: + if (!efuse_gain->offset_valid) + return; + + gain_ofdm_band = rtw89_subband_to_gain_offset_band_of_ofdm(subband); + + offset_a = -efuse_gain->offset[RF_PATH_A][gain_ofdm_band]; + offset_b = -efuse_gain->offset[RF_PATH_B][gain_ofdm_band]; + + tmp = -((offset_a << 2) + (efuse_gain->offset_base[RTW89_PHY_0] >> 2)); + tmp = clamp_t(s32, tmp, S8_MIN, S8_MAX); + rtw89_phy_write32_mask(rtwdev, rssi_ofst_addr[RF_PATH_A], B_PATH0_R_G_OFST_MASK, tmp); + + tmp = -((offset_b << 2) + (efuse_gain->offset_base[RTW89_PHY_0] >> 2)); + tmp = clamp_t(s32, tmp, S8_MIN, S8_MAX); + rtw89_phy_write32_mask(rtwdev, rssi_ofst_addr[RF_PATH_B], B_PATH0_R_G_OFST_MASK, tmp); + + if (hal->antenna_rx == RF_B) { + offset_ofdm = -efuse_gain->offset[RF_PATH_B][gain_ofdm_band]; + offset_cck = -efuse_gain->offset[RF_PATH_B][0]; + } else { + offset_ofdm = -efuse_gain->offset[RF_PATH_A][gain_ofdm_band]; + offset_cck = -efuse_gain->offset[RF_PATH_A][0]; + } + + tmp = (offset_ofdm << 4) + efuse_gain->offset_base[RTW89_PHY_0]; + tmp = clamp_t(s32, tmp, S8_MIN, S8_MAX); + rtw89_phy_write32_idx(rtwdev, R_P0_RPL1, B_P0_RPL1_BIAS_MASK, tmp, phy_idx); + + tmp = (offset_ofdm << 4) + efuse_gain->rssi_base[RTW89_PHY_0]; + tmp = clamp_t(s32, tmp, S8_MIN, S8_MAX); + rtw89_phy_write32_idx(rtwdev, R_P1_RPL1, B_P0_RPL1_BIAS_MASK, tmp, phy_idx); + + if (subband == RTW89_CH_2G) { + tmp = (offset_cck << 3) + (efuse_gain->offset_base[RTW89_PHY_0] >> 1); + tmp = clamp_t(s32, tmp, S8_MIN >> 1, S8_MAX >> 1); + rtw89_phy_write32_mask(rtwdev, R_RX_RPL_OFST, + B_RX_RPL_OFST_CCK_MASK, tmp); + } +} + +static +void rtw8852b_set_rxsc_rpl_comp(struct rtw89_dev *rtwdev, enum rtw89_subband subband) +{ + const struct rtw89_phy_bb_gain_info *gain = &rtwdev->bb_gain; + u8 band = rtw89_subband_to_bb_gain_band(subband); + u32 val; + + val = FIELD_PREP(B_P0_RPL1_20_MASK, (gain->rpl_ofst_20[band][RF_PATH_A] + + gain->rpl_ofst_20[band][RF_PATH_B]) / 2) | + FIELD_PREP(B_P0_RPL1_40_MASK, (gain->rpl_ofst_40[band][RF_PATH_A][0] + + gain->rpl_ofst_40[band][RF_PATH_B][0]) / 2) | + FIELD_PREP(B_P0_RPL1_41_MASK, (gain->rpl_ofst_40[band][RF_PATH_A][1] + + gain->rpl_ofst_40[band][RF_PATH_B][1]) / 2); + val >>= B_P0_RPL1_SHIFT; + rtw89_phy_write32_mask(rtwdev, R_P0_RPL1, B_P0_RPL1_MASK, val); + rtw89_phy_write32_mask(rtwdev, R_P1_RPL1, B_P0_RPL1_MASK, val); + + val = FIELD_PREP(B_P0_RTL2_42_MASK, (gain->rpl_ofst_40[band][RF_PATH_A][2] + + gain->rpl_ofst_40[band][RF_PATH_B][2]) / 2) | + FIELD_PREP(B_P0_RTL2_80_MASK, (gain->rpl_ofst_80[band][RF_PATH_A][0] + + gain->rpl_ofst_80[band][RF_PATH_B][0]) / 2) | + FIELD_PREP(B_P0_RTL2_81_MASK, (gain->rpl_ofst_80[band][RF_PATH_A][1] + + gain->rpl_ofst_80[band][RF_PATH_B][1]) / 2) | + FIELD_PREP(B_P0_RTL2_8A_MASK, (gain->rpl_ofst_80[band][RF_PATH_A][10] + + gain->rpl_ofst_80[band][RF_PATH_B][10]) / 2); + rtw89_phy_write32(rtwdev, R_P0_RPL2, val); + rtw89_phy_write32(rtwdev, R_P1_RPL2, val); + + val = FIELD_PREP(B_P0_RTL3_82_MASK, (gain->rpl_ofst_80[band][RF_PATH_A][2] + + gain->rpl_ofst_80[band][RF_PATH_B][2]) / 2) | + FIELD_PREP(B_P0_RTL3_83_MASK, (gain->rpl_ofst_80[band][RF_PATH_A][3] + + gain->rpl_ofst_80[band][RF_PATH_B][3]) / 2) | + FIELD_PREP(B_P0_RTL3_84_MASK, (gain->rpl_ofst_80[band][RF_PATH_A][4] + + gain->rpl_ofst_80[band][RF_PATH_B][4]) / 2) | + FIELD_PREP(B_P0_RTL3_89_MASK, (gain->rpl_ofst_80[band][RF_PATH_A][9] + + gain->rpl_ofst_80[band][RF_PATH_B][9]) / 2); + rtw89_phy_write32(rtwdev, R_P0_RPL3, val); + rtw89_phy_write32(rtwdev, R_P1_RPL3, val); +} + +static void rtw8852b_ctrl_ch(struct rtw89_dev *rtwdev, + const struct rtw89_chan *chan, + enum rtw89_phy_idx phy_idx) +{ + u8 central_ch = chan->channel; + u8 subband = chan->subband_type; + u8 sco_comp; + bool is_2g = central_ch <= 14; + + /* Path A */ + if (is_2g) + rtw89_phy_write32_idx(rtwdev, R_PATH0_BAND_SEL_V1, + B_PATH0_BAND_SEL_MSK_V1, 1, phy_idx); + else + rtw89_phy_write32_idx(rtwdev, R_PATH0_BAND_SEL_V1, + B_PATH0_BAND_SEL_MSK_V1, 0, phy_idx); + + /* Path B */ + if (is_2g) + rtw89_phy_write32_idx(rtwdev, R_PATH1_BAND_SEL_V1, + B_PATH1_BAND_SEL_MSK_V1, 1, phy_idx); + else + rtw89_phy_write32_idx(rtwdev, R_PATH1_BAND_SEL_V1, + B_PATH1_BAND_SEL_MSK_V1, 0, phy_idx); + + /* SCO compensate FC setting */ + sco_comp = rtw8852b_sco_mapping(central_ch); + rtw89_phy_write32_idx(rtwdev, R_FC0_BW_V1, B_FC0_BW_INV, sco_comp, phy_idx); + + if (chan->band_type == RTW89_BAND_6G) + return; + + /* CCK parameters */ + if (central_ch == 14) { + rtw89_phy_write32_mask(rtwdev, R_TXFIR0, B_TXFIR_C01, 0x3b13ff); + rtw89_phy_write32_mask(rtwdev, R_TXFIR2, B_TXFIR_C23, 0x1c42de); + rtw89_phy_write32_mask(rtwdev, R_TXFIR4, B_TXFIR_C45, 0xfdb0ad); + rtw89_phy_write32_mask(rtwdev, R_TXFIR6, B_TXFIR_C67, 0xf60f6e); + rtw89_phy_write32_mask(rtwdev, R_TXFIR8, B_TXFIR_C89, 0xfd8f92); + rtw89_phy_write32_mask(rtwdev, R_TXFIRA, B_TXFIR_CAB, 0x2d011); + rtw89_phy_write32_mask(rtwdev, R_TXFIRC, B_TXFIR_CCD, 0x1c02c); + rtw89_phy_write32_mask(rtwdev, R_TXFIRE, B_TXFIR_CEF, 0xfff00a); + } else { + rtw89_phy_write32_mask(rtwdev, R_TXFIR0, B_TXFIR_C01, 0x3d23ff); + rtw89_phy_write32_mask(rtwdev, R_TXFIR2, B_TXFIR_C23, 0x29b354); + rtw89_phy_write32_mask(rtwdev, R_TXFIR4, B_TXFIR_C45, 0xfc1c8); + rtw89_phy_write32_mask(rtwdev, R_TXFIR6, B_TXFIR_C67, 0xfdb053); + rtw89_phy_write32_mask(rtwdev, R_TXFIR8, B_TXFIR_C89, 0xf86f9a); + rtw89_phy_write32_mask(rtwdev, R_TXFIRA, B_TXFIR_CAB, 0xfaef92); + rtw89_phy_write32_mask(rtwdev, R_TXFIRC, B_TXFIR_CCD, 0xfe5fcc); + rtw89_phy_write32_mask(rtwdev, R_TXFIRE, B_TXFIR_CEF, 0xffdff5); + } + + rtw8852b_set_gain_error(rtwdev, subband, RF_PATH_A); + rtw8852b_set_gain_error(rtwdev, subband, RF_PATH_B); + rtw8852b_set_gain_offset(rtwdev, subband, phy_idx); + rtw8852b_set_rxsc_rpl_comp(rtwdev, subband); +} + +static void rtw8852b_bw_setting(struct rtw89_dev *rtwdev, u8 bw, u8 path) +{ + static const u32 adc_sel[2] = {0xC0EC, 0xC1EC}; + static const u32 wbadc_sel[2] = {0xC0E4, 0xC1E4}; + + switch (bw) { + case RTW89_CHANNEL_WIDTH_5: + rtw89_phy_write32_mask(rtwdev, adc_sel[path], 0x6000, 0x1); + rtw89_phy_write32_mask(rtwdev, wbadc_sel[path], 0x30, 0x0); + break; + case RTW89_CHANNEL_WIDTH_10: + rtw89_phy_write32_mask(rtwdev, adc_sel[path], 0x6000, 0x2); + rtw89_phy_write32_mask(rtwdev, wbadc_sel[path], 0x30, 0x1); + break; + case RTW89_CHANNEL_WIDTH_20: + rtw89_phy_write32_mask(rtwdev, adc_sel[path], 0x6000, 0x0); + rtw89_phy_write32_mask(rtwdev, wbadc_sel[path], 0x30, 0x2); + break; + case RTW89_CHANNEL_WIDTH_40: + rtw89_phy_write32_mask(rtwdev, adc_sel[path], 0x6000, 0x0); + rtw89_phy_write32_mask(rtwdev, wbadc_sel[path], 0x30, 0x2); + break; + case RTW89_CHANNEL_WIDTH_80: + rtw89_phy_write32_mask(rtwdev, adc_sel[path], 0x6000, 0x0); + rtw89_phy_write32_mask(rtwdev, wbadc_sel[path], 0x30, 0x2); + break; + default: + rtw89_warn(rtwdev, "Fail to set ADC\n"); + } +} + +static void rtw8852b_ctrl_bw(struct rtw89_dev *rtwdev, u8 pri_ch, u8 bw, + enum rtw89_phy_idx phy_idx) +{ + u32 rx_path_0; + + switch (bw) { + case RTW89_CHANNEL_WIDTH_5: + rtw89_phy_write32_idx(rtwdev, R_FC0_BW_V1, B_FC0_BW_SET, 0x0, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_CHBW_MOD_V1, B_CHBW_MOD_SBW, 0x1, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_CHBW_MOD_V1, B_CHBW_MOD_PRICH, 0x0, phy_idx); + + /*Set RF mode at 3 */ + rtw89_phy_write32_idx(rtwdev, R_P0_RFMODE_ORI_RX, + B_P0_RFMODE_ORI_RX_ALL, 0x333, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_P1_RFMODE_ORI_RX, + B_P1_RFMODE_ORI_RX_ALL, 0x333, phy_idx); + break; + case RTW89_CHANNEL_WIDTH_10: + rtw89_phy_write32_idx(rtwdev, R_FC0_BW_V1, B_FC0_BW_SET, 0x0, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_CHBW_MOD_V1, B_CHBW_MOD_SBW, 0x2, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_CHBW_MOD_V1, B_CHBW_MOD_PRICH, 0x0, phy_idx); + + /*Set RF mode at 3 */ + rtw89_phy_write32_idx(rtwdev, R_P0_RFMODE_ORI_RX, + B_P0_RFMODE_ORI_RX_ALL, 0x333, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_P1_RFMODE_ORI_RX, + B_P1_RFMODE_ORI_RX_ALL, 0x333, phy_idx); + break; + case RTW89_CHANNEL_WIDTH_20: + rtw89_phy_write32_idx(rtwdev, R_FC0_BW_V1, B_FC0_BW_SET, 0x0, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_CHBW_MOD_V1, B_CHBW_MOD_SBW, 0x0, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_CHBW_MOD_V1, B_CHBW_MOD_PRICH, 0x0, phy_idx); + + /*Set RF mode at 3 */ + rtw89_phy_write32_idx(rtwdev, R_P0_RFMODE_ORI_RX, + B_P0_RFMODE_ORI_RX_ALL, 0x333, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_P1_RFMODE_ORI_RX, + B_P1_RFMODE_ORI_RX_ALL, 0x333, phy_idx); + break; + case RTW89_CHANNEL_WIDTH_40: + rtw89_phy_write32_idx(rtwdev, R_FC0_BW_V1, B_FC0_BW_SET, 0x1, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_CHBW_MOD_V1, B_CHBW_MOD_SBW, 0x0, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_CHBW_MOD_V1, B_CHBW_MOD_PRICH, + pri_ch, phy_idx); + + /*Set RF mode at 3 */ + rtw89_phy_write32_idx(rtwdev, R_P0_RFMODE_ORI_RX, + B_P0_RFMODE_ORI_RX_ALL, 0x333, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_P1_RFMODE_ORI_RX, + B_P1_RFMODE_ORI_RX_ALL, 0x333, phy_idx); + /*CCK primary channel */ + if (pri_ch == RTW89_SC_20_UPPER) + rtw89_phy_write32_mask(rtwdev, R_RXSC, B_RXSC_EN, 1); + else + rtw89_phy_write32_mask(rtwdev, R_RXSC, B_RXSC_EN, 0); + + break; + case RTW89_CHANNEL_WIDTH_80: + rtw89_phy_write32_idx(rtwdev, R_FC0_BW_V1, B_FC0_BW_SET, 0x2, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_CHBW_MOD_V1, B_CHBW_MOD_SBW, 0x0, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_CHBW_MOD_V1, B_CHBW_MOD_PRICH, + pri_ch, phy_idx); + + /*Set RF mode at A */ + rtw89_phy_write32_idx(rtwdev, R_P0_RFMODE_ORI_RX, + B_P0_RFMODE_ORI_RX_ALL, 0xaaa, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_P1_RFMODE_ORI_RX, + B_P1_RFMODE_ORI_RX_ALL, 0xaaa, phy_idx); + break; + default: + rtw89_warn(rtwdev, "Fail to switch bw (bw:%d, pri ch:%d)\n", bw, + pri_ch); + } + + rtw8852b_bw_setting(rtwdev, bw, RF_PATH_A); + rtw8852b_bw_setting(rtwdev, bw, RF_PATH_B); + + rx_path_0 = rtw89_phy_read32_idx(rtwdev, R_CHBW_MOD_V1, B_ANT_RX_SEG0, + phy_idx); + if (rx_path_0 == 0x1) + rtw89_phy_write32_idx(rtwdev, R_P1_RFMODE_ORI_RX, + B_P1_RFMODE_ORI_RX_ALL, 0x111, phy_idx); + else if (rx_path_0 == 0x2) + rtw89_phy_write32_idx(rtwdev, R_P0_RFMODE_ORI_RX, + B_P0_RFMODE_ORI_RX_ALL, 0x111, phy_idx); +} + +static void rtw8852b_ctrl_cck_en(struct rtw89_dev *rtwdev, bool cck_en) +{ + if (cck_en) { + rtw89_phy_write32_mask(rtwdev, R_UPD_CLK_ADC, B_ENABLE_CCK, 1); + rtw89_phy_write32_mask(rtwdev, R_RXCCA, B_RXCCA_DIS, 0); + } else { + rtw89_phy_write32_mask(rtwdev, R_UPD_CLK_ADC, B_ENABLE_CCK, 0); + rtw89_phy_write32_mask(rtwdev, R_RXCCA, B_RXCCA_DIS, 1); + } +} + +static void rtw8852b_5m_mask(struct rtw89_dev *rtwdev, const struct rtw89_chan *chan, + enum rtw89_phy_idx phy_idx) +{ + u8 pri_ch = chan->primary_channel; + bool mask_5m_low; + bool mask_5m_en; + + switch (chan->band_width) { + case RTW89_CHANNEL_WIDTH_40: + /* Prich=1: Mask 5M High, Prich=2: Mask 5M Low */ + mask_5m_en = true; + mask_5m_low = pri_ch == 2; + break; + case RTW89_CHANNEL_WIDTH_80: + /* Prich=3: Mask 5M High, Prich=4: Mask 5M Low, Else: Disable */ + mask_5m_en = pri_ch == 3 || pri_ch == 4; + mask_5m_low = pri_ch == 4; + break; + default: + mask_5m_en = false; + break; + } + + if (!mask_5m_en) { + rtw89_phy_write32_mask(rtwdev, R_PATH0_5MDET_V1, B_PATH0_5MDET_EN, 0x0); + rtw89_phy_write32_mask(rtwdev, R_PATH1_5MDET_V1, B_PATH1_5MDET_EN, 0x0); + rtw89_phy_write32_idx(rtwdev, R_ASSIGN_SBD_OPT_V1, + B_ASSIGN_SBD_OPT_EN_V1, 0x0, phy_idx); + return; + } + + if (mask_5m_low) { + rtw89_phy_write32_mask(rtwdev, R_PATH0_5MDET_V1, B_PATH0_5MDET_TH, 0x4); + rtw89_phy_write32_mask(rtwdev, R_PATH0_5MDET_V1, B_PATH0_5MDET_EN, 0x1); + rtw89_phy_write32_mask(rtwdev, R_PATH0_5MDET_V1, B_PATH0_5MDET_SB2, 0x0); + rtw89_phy_write32_mask(rtwdev, R_PATH0_5MDET_V1, B_PATH0_5MDET_SB0, 0x1); + rtw89_phy_write32_mask(rtwdev, R_PATH1_5MDET_V1, B_PATH1_5MDET_TH, 0x4); + rtw89_phy_write32_mask(rtwdev, R_PATH1_5MDET_V1, B_PATH1_5MDET_EN, 0x1); + rtw89_phy_write32_mask(rtwdev, R_PATH1_5MDET_V1, B_PATH1_5MDET_SB2, 0x0); + rtw89_phy_write32_mask(rtwdev, R_PATH1_5MDET_V1, B_PATH1_5MDET_SB0, 0x1); + } else { + rtw89_phy_write32_mask(rtwdev, R_PATH0_5MDET_V1, B_PATH0_5MDET_TH, 0x4); + rtw89_phy_write32_mask(rtwdev, R_PATH0_5MDET_V1, B_PATH0_5MDET_EN, 0x1); + rtw89_phy_write32_mask(rtwdev, R_PATH0_5MDET_V1, B_PATH0_5MDET_SB2, 0x1); + rtw89_phy_write32_mask(rtwdev, R_PATH0_5MDET_V1, B_PATH0_5MDET_SB0, 0x0); + rtw89_phy_write32_mask(rtwdev, R_PATH1_5MDET_V1, B_PATH1_5MDET_TH, 0x4); + rtw89_phy_write32_mask(rtwdev, R_PATH1_5MDET_V1, B_PATH1_5MDET_EN, 0x1); + rtw89_phy_write32_mask(rtwdev, R_PATH1_5MDET_V1, B_PATH1_5MDET_SB2, 0x1); + rtw89_phy_write32_mask(rtwdev, R_PATH1_5MDET_V1, B_PATH1_5MDET_SB0, 0x0); + } + rtw89_phy_write32_idx(rtwdev, R_ASSIGN_SBD_OPT_V1, + B_ASSIGN_SBD_OPT_EN_V1, 0x1, phy_idx); +} + +static void rtw8852b_bb_reset_all(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx) +{ + rtw89_phy_write32_idx(rtwdev, R_S0_HW_SI_DIS, B_S0_HW_SI_DIS_W_R_TRIG, 0x7, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_S1_HW_SI_DIS, B_S1_HW_SI_DIS_W_R_TRIG, 0x7, phy_idx); + fsleep(1); + rtw89_phy_write32_idx(rtwdev, R_RSTB_ASYNC, B_RSTB_ASYNC_ALL, 1, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_RSTB_ASYNC, B_RSTB_ASYNC_ALL, 0, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_S0_HW_SI_DIS, B_S0_HW_SI_DIS_W_R_TRIG, 0x0, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_S1_HW_SI_DIS, B_S1_HW_SI_DIS_W_R_TRIG, 0x0, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_RSTB_ASYNC, B_RSTB_ASYNC_ALL, 1, phy_idx); +} + +static void rtw8852b_set_channel_bb(struct rtw89_dev *rtwdev, const struct rtw89_chan *chan, + enum rtw89_phy_idx phy_idx) +{ + bool cck_en = chan->channel <= 14; + u8 pri_ch_idx = chan->pri_ch_idx; + + if (cck_en) + rtw8852b_ctrl_sco_cck(rtwdev, chan->primary_channel); + + rtw8852b_ctrl_ch(rtwdev, chan, phy_idx); + rtw8852b_ctrl_bw(rtwdev, pri_ch_idx, chan->band_width, phy_idx); + rtw8852b_ctrl_cck_en(rtwdev, cck_en); + if (chan->band_type == RTW89_BAND_5G) { + rtw89_phy_write32_mask(rtwdev, R_PATH0_BT_SHARE_V1, + B_PATH0_BT_SHARE_V1, 0x0); + rtw89_phy_write32_mask(rtwdev, R_PATH0_BTG_PATH_V1, + B_PATH0_BTG_PATH_V1, 0x0); + rtw89_phy_write32_mask(rtwdev, R_PATH1_BT_SHARE_V1, + B_PATH1_BT_SHARE_V1, 0x0); + rtw89_phy_write32_mask(rtwdev, R_PATH1_BTG_PATH_V1, + B_PATH1_BTG_PATH_V1, 0x0); + rtw89_phy_write32_mask(rtwdev, R_CHBW_MOD_V1, B_BT_SHARE, 0x0); + rtw89_phy_write32_mask(rtwdev, R_FC0_BW_V1, B_ANT_RX_BT_SEG0, 0x0); + rtw89_phy_write32_mask(rtwdev, R_BT_DYN_DC_EST_EN_V1, + B_BT_DYN_DC_EST_EN_MSK, 0x0); + rtw89_phy_write32_mask(rtwdev, R_GNT_BT_WGT_EN, B_GNT_BT_WGT_EN, 0x0); + } + rtw89_phy_write32_mask(rtwdev, R_MAC_PIN_SEL, B_CH_IDX_SEG0, + chan->primary_channel); + rtw8852b_5m_mask(rtwdev, chan, phy_idx); + rtw8852b_bb_reset_all(rtwdev, phy_idx); +} + +static void rtw8852b_set_channel(struct rtw89_dev *rtwdev, + const struct rtw89_chan *chan, + enum rtw89_mac_idx mac_idx, + enum rtw89_phy_idx phy_idx) +{ + rtw8852b_set_channel_mac(rtwdev, chan, mac_idx); + rtw8852b_set_channel_bb(rtwdev, chan, phy_idx); + rtw8852b_set_channel_rf(rtwdev, chan, phy_idx); +} + static u32 rtw8852b_bb_cal_txpwr_ref(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx, s16 ref) { @@ -579,6 +1142,7 @@ static int rtw8852b_mac_disable_bb_rf(struct rtw89_dev *rtwdev) static const struct rtw89_chip_ops rtw8852b_chip_ops = { .enable_bb_rf = rtw8852b_mac_enable_bb_rf, .disable_bb_rf = rtw8852b_mac_disable_bb_rf, + .set_channel = rtw8852b_set_channel, .read_efuse = rtw8852b_read_efuse, .read_phycap = rtw8852b_read_phycap, .power_trim = rtw8852b_power_trim, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852b_rfk.c b/drivers/net/wireless/realtek/rtw89/rtw8852b_rfk.c new file mode 100644 index 000000000000..761544b0dcca --- /dev/null +++ b/drivers/net/wireless/realtek/rtw89/rtw8852b_rfk.c @@ -0,0 +1,256 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* Copyright(c) 2019-2022 Realtek Corporation + */ + +#include "coex.h" +#include "debug.h" +#include "mac.h" +#include "phy.h" +#include "reg.h" +#include "rtw8852b.h" +#include "rtw8852b_rfk.h" +#include "rtw8852b_rfk_table.h" +#include "rtw8852b_table.h" + +static u8 _kpath(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx) +{ + u8 val; + + rtw89_debug(rtwdev, RTW89_DBG_RFK, "[RFK]dbcc_en: %x,PHY%d\n", + rtwdev->dbcc_en, phy_idx); + + if (!rtwdev->dbcc_en) { + val = RF_AB; + } else { + if (phy_idx == RTW89_PHY_0) + val = RF_A; + else + val = RF_B; + } + return val; +} + +static void _bw_setting(struct rtw89_dev *rtwdev, enum rtw89_rf_path path, + enum rtw89_bandwidth bw, bool dav) +{ + u32 rf_reg18; + u32 reg18_addr = dav ? RR_CFGCH : RR_CFGCH_V1; + + rtw89_debug(rtwdev, RTW89_DBG_RFK, "[RFK]===> %s\n", __func__); + + rf_reg18 = rtw89_read_rf(rtwdev, path, reg18_addr, RFREG_MASK); + if (rf_reg18 == INV_RF_DATA) { + rtw89_debug(rtwdev, RTW89_DBG_RFK, + "[RFK]Invalid RF_0x18 for Path-%d\n", path); + return; + } + rf_reg18 &= ~RR_CFGCH_BW; + + switch (bw) { + case RTW89_CHANNEL_WIDTH_5: + case RTW89_CHANNEL_WIDTH_10: + case RTW89_CHANNEL_WIDTH_20: + rf_reg18 |= FIELD_PREP(RR_CFGCH_BW, CFGCH_BW_20M); + break; + case RTW89_CHANNEL_WIDTH_40: + rf_reg18 |= FIELD_PREP(RR_CFGCH_BW, CFGCH_BW_40M); + break; + case RTW89_CHANNEL_WIDTH_80: + rf_reg18 |= FIELD_PREP(RR_CFGCH_BW, CFGCH_BW_80M); + break; + default: + rtw89_debug(rtwdev, RTW89_DBG_RFK, "[RFK]Fail to set CH\n"); + } + + rf_reg18 &= ~(RR_CFGCH_POW_LCK | RR_CFGCH_TRX_AH | RR_CFGCH_BCN | + RR_CFGCH_BW2) & RFREG_MASK; + rf_reg18 |= RR_CFGCH_BW2; + rtw89_write_rf(rtwdev, path, reg18_addr, RFREG_MASK, rf_reg18); + + rtw89_debug(rtwdev, RTW89_DBG_RFK, "[RFK] set %x at path%d, %x =0x%x\n", + bw, path, reg18_addr, + rtw89_read_rf(rtwdev, path, reg18_addr, RFREG_MASK)); +} + +static void _ctrl_bw(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy, + enum rtw89_bandwidth bw) +{ + _bw_setting(rtwdev, RF_PATH_A, bw, true); + _bw_setting(rtwdev, RF_PATH_B, bw, true); + _bw_setting(rtwdev, RF_PATH_A, bw, false); + _bw_setting(rtwdev, RF_PATH_B, bw, false); +} + +static bool _set_s0_arfc18(struct rtw89_dev *rtwdev, u32 val) +{ + u32 bak; + u32 tmp; + int ret; + + bak = rtw89_read_rf(rtwdev, RF_PATH_A, RR_LDO, RFREG_MASK); + rtw89_write_rf(rtwdev, RF_PATH_A, RR_LDO, RR_LDO_SEL, 0x1); + rtw89_write_rf(rtwdev, RF_PATH_A, RR_CFGCH, RFREG_MASK, val); + + ret = read_poll_timeout_atomic(rtw89_read_rf, tmp, tmp == 0, 1, 1000, + false, rtwdev, RF_PATH_A, RR_LPF, RR_LPF_BUSY); + if (ret) + rtw89_debug(rtwdev, RTW89_DBG_RFK, "[LCK]LCK timeout\n"); + + rtw89_write_rf(rtwdev, RF_PATH_A, RR_LDO, RFREG_MASK, bak); + + return !!ret; +} + +static void _lck_check(struct rtw89_dev *rtwdev) +{ + u32 tmp; + + if (rtw89_read_rf(rtwdev, RF_PATH_A, RR_SYNFB, RR_SYNFB_LK) == 0) { + rtw89_debug(rtwdev, RTW89_DBG_RFK, "[LCK]SYN MMD reset\n"); + + rtw89_write_rf(rtwdev, RF_PATH_A, RR_MMD, RR_MMD_RST_EN, 0x1); + rtw89_write_rf(rtwdev, RF_PATH_A, RR_MMD, RR_MMD_RST_SYN, 0x0); + rtw89_write_rf(rtwdev, RF_PATH_A, RR_MMD, RR_MMD_RST_SYN, 0x1); + rtw89_write_rf(rtwdev, RF_PATH_A, RR_MMD, RR_MMD_RST_EN, 0x0); + } + + udelay(10); + + if (rtw89_read_rf(rtwdev, RF_PATH_A, RR_SYNFB, RR_SYNFB_LK) == 0) { + rtw89_debug(rtwdev, RTW89_DBG_RFK, "[LCK]re-set RF 0x18\n"); + + rtw89_write_rf(rtwdev, RF_PATH_A, RR_LCK_TRG, RR_LCK_TRGSEL, 0x1); + tmp = rtw89_read_rf(rtwdev, RF_PATH_A, RR_CFGCH, RFREG_MASK); + _set_s0_arfc18(rtwdev, tmp); + rtw89_write_rf(rtwdev, RF_PATH_A, RR_LCK_TRG, RR_LCK_TRGSEL, 0x0); + } + + if (rtw89_read_rf(rtwdev, RF_PATH_A, RR_SYNFB, RR_SYNFB_LK) == 0) { + rtw89_debug(rtwdev, RTW89_DBG_RFK, "[LCK]SYN off/on\n"); + + tmp = rtw89_read_rf(rtwdev, RF_PATH_A, RR_POW, RFREG_MASK); + rtw89_write_rf(rtwdev, RF_PATH_A, RR_POW, RFREG_MASK, tmp); + tmp = rtw89_read_rf(rtwdev, RF_PATH_A, RR_SX, RFREG_MASK); + rtw89_write_rf(rtwdev, RF_PATH_A, RR_SX, RFREG_MASK, tmp); + + rtw89_write_rf(rtwdev, RF_PATH_A, RR_SYNLUT, RR_SYNLUT_MOD, 0x1); + rtw89_write_rf(rtwdev, RF_PATH_A, RR_POW, RR_POW_SYN, 0x0); + rtw89_write_rf(rtwdev, RF_PATH_A, RR_POW, RR_POW_SYN, 0x3); + rtw89_write_rf(rtwdev, RF_PATH_A, RR_SYNLUT, RR_SYNLUT_MOD, 0x0); + + rtw89_write_rf(rtwdev, RF_PATH_A, RR_LCK_TRG, RR_LCK_TRGSEL, 0x1); + tmp = rtw89_read_rf(rtwdev, RF_PATH_A, RR_CFGCH, RFREG_MASK); + _set_s0_arfc18(rtwdev, tmp); + rtw89_write_rf(rtwdev, RF_PATH_A, RR_LCK_TRG, RR_LCK_TRGSEL, 0x0); + + rtw89_debug(rtwdev, RTW89_DBG_RFK, "[LCK]0xb2=%x, 0xc5=%x\n", + rtw89_read_rf(rtwdev, RF_PATH_A, RR_VCO, RFREG_MASK), + rtw89_read_rf(rtwdev, RF_PATH_A, RR_SYNFB, RFREG_MASK)); + } +} + +static void _set_ch(struct rtw89_dev *rtwdev, u32 val) +{ + bool timeout; + + timeout = _set_s0_arfc18(rtwdev, val); + if (!timeout) + _lck_check(rtwdev); +} + +static void _ch_setting(struct rtw89_dev *rtwdev, enum rtw89_rf_path path, + u8 central_ch, bool dav) +{ + u32 reg18_addr = dav ? RR_CFGCH : RR_CFGCH_V1; + bool is_2g_ch = central_ch <= 14; + u32 rf_reg18; + + rtw89_debug(rtwdev, RTW89_DBG_RFK, "[RFK]===> %s\n", __func__); + + rf_reg18 = rtw89_read_rf(rtwdev, path, reg18_addr, RFREG_MASK); + rf_reg18 &= ~(RR_CFGCH_BAND1 | RR_CFGCH_POW_LCK | RR_CFGCH_TRX_AH | + RR_CFGCH_BCN | RR_CFGCH_BAND0 | RR_CFGCH_CH); + rf_reg18 |= FIELD_PREP(RR_CFGCH_CH, central_ch); + + if (!is_2g_ch) + rf_reg18 |= FIELD_PREP(RR_CFGCH_BAND1, CFGCH_BAND1_5G) | + FIELD_PREP(RR_CFGCH_BAND0, CFGCH_BAND0_5G); + + rf_reg18 &= ~(RR_CFGCH_POW_LCK | RR_CFGCH_TRX_AH | RR_CFGCH_BCN | + RR_CFGCH_BW2) & RFREG_MASK; + rf_reg18 |= RR_CFGCH_BW2; + + if (path == RF_PATH_A && dav) + _set_ch(rtwdev, rf_reg18); + else + rtw89_write_rf(rtwdev, path, reg18_addr, RFREG_MASK, rf_reg18); + + rtw89_write_rf(rtwdev, path, RR_LCKST, RR_LCKST_BIN, 0); + rtw89_write_rf(rtwdev, path, RR_LCKST, RR_LCKST_BIN, 1); + + rtw89_debug(rtwdev, RTW89_DBG_RFK, + "[RFK]CH: %d for Path-%d, reg0x%x = 0x%x\n", + central_ch, path, reg18_addr, + rtw89_read_rf(rtwdev, path, reg18_addr, RFREG_MASK)); +} + +static void _ctrl_ch(struct rtw89_dev *rtwdev, u8 central_ch) +{ + _ch_setting(rtwdev, RF_PATH_A, central_ch, true); + _ch_setting(rtwdev, RF_PATH_B, central_ch, true); + _ch_setting(rtwdev, RF_PATH_A, central_ch, false); + _ch_setting(rtwdev, RF_PATH_B, central_ch, false); +} + +static void _set_rxbb_bw(struct rtw89_dev *rtwdev, enum rtw89_bandwidth bw, + enum rtw89_rf_path path) +{ + rtw89_write_rf(rtwdev, path, RR_LUTWE2, RR_LUTWE2_RTXBW, 0x1); + rtw89_write_rf(rtwdev, path, RR_LUTWA, RR_LUTWA_M2, 0x12); + + if (bw == RTW89_CHANNEL_WIDTH_20) + rtw89_write_rf(rtwdev, path, RR_LUTWD0, RR_LUTWD0_LB, 0x1b); + else if (bw == RTW89_CHANNEL_WIDTH_40) + rtw89_write_rf(rtwdev, path, RR_LUTWD0, RR_LUTWD0_LB, 0x13); + else if (bw == RTW89_CHANNEL_WIDTH_80) + rtw89_write_rf(rtwdev, path, RR_LUTWD0, RR_LUTWD0_LB, 0xb); + else + rtw89_write_rf(rtwdev, path, RR_LUTWD0, RR_LUTWD0_LB, 0x3); + + rtw89_debug(rtwdev, RTW89_DBG_RFK, "[RFK] set S%d RXBB BW 0x3F = 0x%x\n", path, + rtw89_read_rf(rtwdev, path, RR_LUTWD0, RR_LUTWD0_LB)); + + rtw89_write_rf(rtwdev, path, RR_LUTWE2, RR_LUTWE2_RTXBW, 0x0); +} + +static void _rxbb_bw(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy, + enum rtw89_bandwidth bw) +{ + u8 kpath, path; + + kpath = _kpath(rtwdev, phy); + + for (path = 0; path < RF_PATH_NUM_8852B; path++) { + if (!(kpath & BIT(path))) + continue; + + _set_rxbb_bw(rtwdev, bw, path); + } +} + +static void rtw8852b_ctrl_bw_ch(struct rtw89_dev *rtwdev, + enum rtw89_phy_idx phy, u8 central_ch, + enum rtw89_band band, enum rtw89_bandwidth bw) +{ + _ctrl_ch(rtwdev, central_ch); + _ctrl_bw(rtwdev, phy, bw); + _rxbb_bw(rtwdev, phy, bw); +} + +void rtw8852b_set_channel_rf(struct rtw89_dev *rtwdev, + const struct rtw89_chan *chan, + enum rtw89_phy_idx phy_idx) +{ + rtw8852b_ctrl_bw_ch(rtwdev, phy_idx, chan->channel, chan->band_type, + chan->band_width); +} diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852b_rfk.h b/drivers/net/wireless/realtek/rtw89/rtw8852b_rfk.h new file mode 100644 index 000000000000..d54256bbc9a0 --- /dev/null +++ b/drivers/net/wireless/realtek/rtw89/rtw8852b_rfk.h @@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ +/* Copyright(c) 2019-2022 Realtek Corporation + */ + +#ifndef __RTW89_8852B_RFK_H__ +#define __RTW89_8852B_RFK_H__ + +#include "core.h" + +void rtw8852b_set_channel_rf(struct rtw89_dev *rtwdev, + const struct rtw89_chan *chan, + enum rtw89_phy_idx phy_idx); + +#endif From 54997c24767b51bd762ff942431e8d37b535dc2e Mon Sep 17 00:00:00 2001 From: Po-Hao Huang Date: Fri, 7 Oct 2022 12:58:59 +0800 Subject: [PATCH 0113/2002] wifi: rtw89: correct 6 GHz scan behavior Change active scan behavior to fit 6GHz requirements. There are many different rules on active scan between 6GHz and 2GHz/5GHz, so if the SSID is not specified, do fast passive scanning and limit number of probe requests we send for now until new firmware can support all rules. Signed-off-by: Po-Hao Huang Signed-off-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20221007045900.10823-1-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/fw.c | 28 +++++++++++++++++++------ drivers/net/wireless/realtek/rtw89/fw.h | 1 + 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/fw.c b/drivers/net/wireless/realtek/rtw89/fw.c index d57e3610fb88..bdcc7af737ea 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.c +++ b/drivers/net/wireless/realtek/rtw89/fw.c @@ -2565,6 +2565,9 @@ static void rtw89_hw_scan_add_chan(struct rtw89_dev *rtwdev, int chan_type, struct rtw89_mac_chinfo *ch_info) { struct rtw89_hw_scan_info *scan_info = &rtwdev->scan_info; + struct ieee80211_vif *vif = rtwdev->scan_info.scanning_vif; + struct rtw89_vif *rtwvif = (struct rtw89_vif *)vif->drv_priv; + struct cfg80211_scan_request *req = rtwvif->scan_req; struct rtw89_pktofld_info *info; u8 band, probe_count = 0; @@ -2576,13 +2579,13 @@ static void rtw89_hw_scan_add_chan(struct rtw89_dev *rtwdev, int chan_type, ch_info->tx_pwr_idx = 0; ch_info->tx_null = false; ch_info->pause_data = false; + ch_info->probe_id = RTW89_SCANOFLD_PKT_NONE; if (ssid_num) { ch_info->num_pkt = ssid_num; band = rtw89_hw_to_nl80211_band(ch_info->ch_band); list_for_each_entry(info, &scan_info->pkt_list[band], list) { - ch_info->probe_id = info->id; ch_info->pkt_id[probe_count] = info->id; if (++probe_count >= ssid_num) break; @@ -2591,9 +2594,16 @@ static void rtw89_hw_scan_add_chan(struct rtw89_dev *rtwdev, int chan_type, rtw89_err(rtwdev, "SSID num differs from list len\n"); } + if (ch_info->ch_band == RTW89_BAND_6G) { + if (ssid_num == 1 && req->ssids[0].ssid_len == 0) { + ch_info->tx_pkt = false; + if (!req->duration_mandatory) + ch_info->period -= RTW89_DWELL_TIME; + } + } + switch (chan_type) { case RTW89_CHAN_OPERATE: - ch_info->probe_id = RTW89_SCANOFLD_PKT_NONE; ch_info->central_ch = scan_info->op_chan; ch_info->pri_ch = scan_info->op_pri_ch; ch_info->ch_band = scan_info->op_band; @@ -2602,8 +2612,9 @@ static void rtw89_hw_scan_add_chan(struct rtw89_dev *rtwdev, int chan_type, ch_info->num_pkt = 0; break; case RTW89_CHAN_DFS: - ch_info->period = max_t(u8, ch_info->period, - RTW89_DFS_CHAN_TIME); + if (ch_info->ch_band != RTW89_BAND_6G) + ch_info->period = max_t(u8, ch_info->period, + RTW89_DFS_CHAN_TIME); ch_info->dwell_time = RTW89_DWELL_TIME; break; case RTW89_CHAN_ACTIVE: @@ -2637,8 +2648,13 @@ static int rtw89_hw_scan_add_chan_list(struct rtw89_dev *rtwdev, goto out; } - ch_info->period = req->duration_mandatory ? - req->duration : RTW89_CHANNEL_TIME; + if (req->duration_mandatory) + ch_info->period = req->duration; + else if (channel->band == NL80211_BAND_6GHZ) + ch_info->period = RTW89_CHANNEL_TIME_6G + RTW89_DWELL_TIME; + else + ch_info->period = RTW89_CHANNEL_TIME; + ch_info->ch_band = rtw89_nl80211_to_hw_band(channel->band); ch_info->central_ch = channel->hw_value; ch_info->pri_ch = channel->hw_value; diff --git a/drivers/net/wireless/realtek/rtw89/fw.h b/drivers/net/wireless/realtek/rtw89/fw.h index 0047d5d0e9b1..6ef392ef9c6f 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.h +++ b/drivers/net/wireless/realtek/rtw89/fw.h @@ -197,6 +197,7 @@ struct rtw89_h2creg_sch_tx_en { #define RTW89_H2C_MAX_SIZE 2048 #define RTW89_CHANNEL_TIME 45 +#define RTW89_CHANNEL_TIME_6G 20 #define RTW89_DFS_CHAN_TIME 105 #define RTW89_OFF_CHAN_TIME 100 #define RTW89_DWELL_TIME 20 From 478132050360a5cf19cc1b3fc9fcf1e56a483481 Mon Sep 17 00:00:00 2001 From: Po-Hao Huang Date: Fri, 7 Oct 2022 12:59:00 +0800 Subject: [PATCH 0114/2002] wifi: rtw89: fix wrong bandwidth settings after scan Set channel in driver side to configure Tx power and channel index correctly after scan. Before this, beacons with bandwidth larger than 20M bandwidth will be dropped by mac80211 due to frequency mismatch. Signed-off-by: Po-Hao Huang Signed-off-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20221007045900.10823-2-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/fw.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/wireless/realtek/rtw89/fw.c b/drivers/net/wireless/realtek/rtw89/fw.c index bdcc7af737ea..d21f87e25ae1 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.c +++ b/drivers/net/wireless/realtek/rtw89/fw.c @@ -2773,6 +2773,7 @@ void rtw89_hw_scan_complete(struct rtw89_dev *rtwdev, struct ieee80211_vif *vif, if (rtwvif->net_type != RTW89_NET_TYPE_NO_LINK) rtw89_store_op_chan(rtwdev, false); + rtw89_set_channel(rtwdev); } void rtw89_hw_scan_abort(struct rtw89_dev *rtwdev, struct ieee80211_vif *vif) From 79cac25e51a6add887151916e96622871328f1e8 Mon Sep 17 00:00:00 2001 From: Bitterblue Smith Date: Sat, 8 Oct 2022 13:54:22 +0300 Subject: [PATCH 0115/2002] wifi: rtl8xxxu: Make some arrays const All the initialisation tables, plus rtl8xxxu_rfregs. Most of them were already static. Signed-off-by: Bitterblue Smith Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/9407f219-a7ba-676e-3d99-154d67b312d2@gmail.com --- .../net/wireless/realtek/rtl8xxxu/rtl8xxxu.h | 8 +++---- .../realtek/rtl8xxxu/rtl8xxxu_8188f.c | 10 ++++----- .../realtek/rtl8xxxu/rtl8xxxu_8192c.c | 10 ++++----- .../realtek/rtl8xxxu/rtl8xxxu_8192e.c | 12 +++++----- .../realtek/rtl8xxxu/rtl8xxxu_8723a.c | 2 +- .../realtek/rtl8xxxu/rtl8xxxu_8723b.c | 8 +++---- .../wireless/realtek/rtl8xxxu/rtl8xxxu_core.c | 22 +++++++++---------- 7 files changed, 36 insertions(+), 36 deletions(-) diff --git a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h index 9950a2ee00aa..ad3f32f4c6e7 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h +++ b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h @@ -1500,7 +1500,7 @@ struct rtl8xxxu_fileops { u16 trxff_boundary; u8 pbp_rx; u8 pbp_tx; - struct rtl8xxxu_reg8val *mactable; + const struct rtl8xxxu_reg8val *mactable; u8 total_page_num; u8 page_num_hi; u8 page_num_lo; @@ -1509,7 +1509,7 @@ struct rtl8xxxu_fileops { extern int rtl8xxxu_debug; -extern struct rtl8xxxu_reg8val rtl8xxxu_gen1_mac_init_table[]; +extern const struct rtl8xxxu_reg8val rtl8xxxu_gen1_mac_init_table[]; extern const u32 rtl8xxxu_iqk_phy_iq_bb_reg[]; u8 rtl8xxxu_read8(struct rtl8xxxu_priv *priv, u16 addr); u16 rtl8xxxu_read16(struct rtl8xxxu_priv *priv, u16 addr); @@ -1538,10 +1538,10 @@ void rtl8xxxu_fill_iqk_matrix_a(struct rtl8xxxu_priv *priv, bool iqk_ok, void rtl8xxxu_fill_iqk_matrix_b(struct rtl8xxxu_priv *priv, bool iqk_ok, int result[][8], int candidate, bool tx_only); int rtl8xxxu_init_phy_rf(struct rtl8xxxu_priv *priv, - struct rtl8xxxu_rfregval *table, + const struct rtl8xxxu_rfregval *table, enum rtl8xxxu_rfpath path); int rtl8xxxu_init_phy_regs(struct rtl8xxxu_priv *priv, - struct rtl8xxxu_reg32val *array); + const struct rtl8xxxu_reg32val *array); int rtl8xxxu_load_firmware(struct rtl8xxxu_priv *priv, char *fw_name); void rtl8xxxu_firmware_self_reset(struct rtl8xxxu_priv *priv); void rtl8xxxu_power_off(struct rtl8xxxu_priv *priv); diff --git a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8188f.c b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8188f.c index 4ede766104c8..99610bb2afd5 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8188f.c +++ b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8188f.c @@ -30,7 +30,7 @@ #include "rtl8xxxu.h" #include "rtl8xxxu_regs.h" -static struct rtl8xxxu_reg8val rtl8188f_mac_init_table[] = { +static const struct rtl8xxxu_reg8val rtl8188f_mac_init_table[] = { {0x024, 0xDF}, {0x025, 0x07}, {0x02B, 0x1C}, {0x283, 0x20}, {0x421, 0x0F}, {0x428, 0x0A}, {0x429, 0x10}, {0x430, 0x00}, {0x431, 0x00}, {0x432, 0x00}, {0x433, 0x01}, {0x434, 0x04}, @@ -60,7 +60,7 @@ static struct rtl8xxxu_reg8val rtl8188f_mac_init_table[] = { {0xffff, 0xff}, }; -static struct rtl8xxxu_reg32val rtl8188fu_phy_init_table[] = { +static const struct rtl8xxxu_reg32val rtl8188fu_phy_init_table[] = { {0x800, 0x80045700}, {0x804, 0x00000001}, {0x808, 0x0000FC00}, {0x80C, 0x0000000A}, {0x810, 0x10001331}, {0x814, 0x020C3D10}, @@ -168,7 +168,7 @@ static struct rtl8xxxu_reg32val rtl8188fu_phy_init_table[] = { {0xffff, 0xffffffff}, }; -static struct rtl8xxxu_reg32val rtl8188f_agc_table[] = { +static const struct rtl8xxxu_reg32val rtl8188f_agc_table[] = { {0xC78, 0xFC000001}, {0xC78, 0xFB010001}, {0xC78, 0xFA020001}, {0xC78, 0xF9030001}, {0xC78, 0xF8040001}, {0xC78, 0xF7050001}, @@ -205,7 +205,7 @@ static struct rtl8xxxu_reg32val rtl8188f_agc_table[] = { {0xffff, 0xffffffff} }; -static struct rtl8xxxu_rfregval rtl8188fu_radioa_init_table[] = { +static const struct rtl8xxxu_rfregval rtl8188fu_radioa_init_table[] = { {0x00, 0x00030000}, {0x08, 0x00008400}, {0x18, 0x00000407}, {0x19, 0x00000012}, {0x1B, 0x00001C6C}, @@ -263,7 +263,7 @@ static struct rtl8xxxu_rfregval rtl8188fu_radioa_init_table[] = { {0xff, 0xffffffff} }; -static struct rtl8xxxu_rfregval rtl8188fu_cut_b_radioa_init_table[] = { +static const struct rtl8xxxu_rfregval rtl8188fu_cut_b_radioa_init_table[] = { {0x00, 0x00030000}, {0x08, 0x00008400}, {0x18, 0x00000407}, {0x19, 0x00000012}, {0x1B, 0x00001C6C}, diff --git a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8192c.c b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8192c.c index dd84da51686c..e9bc94edfa79 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8192c.c +++ b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8192c.c @@ -77,7 +77,7 @@ static struct rtl8xxxu_power_base rtl8188r_power_base = { .reg_0868 = 0x00020204, }; -static struct rtl8xxxu_rfregval rtl8192cu_radioa_2t_init_table[] = { +static const struct rtl8xxxu_rfregval rtl8192cu_radioa_2t_init_table[] = { {0x00, 0x00030159}, {0x01, 0x00031284}, {0x02, 0x00098000}, {0x03, 0x00018c63}, {0x04, 0x000210e7}, {0x09, 0x0002044f}, @@ -152,7 +152,7 @@ static struct rtl8xxxu_rfregval rtl8192cu_radioa_2t_init_table[] = { {0xff, 0xffffffff} }; -static struct rtl8xxxu_rfregval rtl8192cu_radiob_2t_init_table[] = { +static const struct rtl8xxxu_rfregval rtl8192cu_radiob_2t_init_table[] = { {0x00, 0x00030159}, {0x01, 0x00031284}, {0x02, 0x00098000}, {0x03, 0x00018c63}, {0x04, 0x000210e7}, {0x09, 0x0002044f}, @@ -176,7 +176,7 @@ static struct rtl8xxxu_rfregval rtl8192cu_radiob_2t_init_table[] = { {0xff, 0xffffffff} }; -static struct rtl8xxxu_rfregval rtl8192cu_radioa_1t_init_table[] = { +static const struct rtl8xxxu_rfregval rtl8192cu_radioa_1t_init_table[] = { {0x00, 0x00030159}, {0x01, 0x00031284}, {0x02, 0x00098000}, {0x03, 0x00018c63}, {0x04, 0x000210e7}, {0x09, 0x0002044f}, @@ -251,7 +251,7 @@ static struct rtl8xxxu_rfregval rtl8192cu_radioa_1t_init_table[] = { {0xff, 0xffffffff} }; -static struct rtl8xxxu_rfregval rtl8188ru_radioa_1t_highpa_table[] = { +static const struct rtl8xxxu_rfregval rtl8188ru_radioa_1t_highpa_table[] = { {0x00, 0x00030159}, {0x01, 0x00031284}, {0x02, 0x00098000}, {0x03, 0x00018c63}, {0x04, 0x000210e7}, {0x09, 0x0002044f}, @@ -413,7 +413,7 @@ static int rtl8192cu_parse_efuse(struct rtl8xxxu_priv *priv) static int rtl8192cu_init_phy_rf(struct rtl8xxxu_priv *priv) { - struct rtl8xxxu_rfregval *rftable; + const struct rtl8xxxu_rfregval *rftable; int ret; if (priv->rtl_chip == RTL8188R) { diff --git a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8192e.c b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8192e.c index c37bae079cf6..550290ae9e17 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8192e.c +++ b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8192e.c @@ -32,7 +32,7 @@ #include "rtl8xxxu.h" #include "rtl8xxxu_regs.h" -static struct rtl8xxxu_reg8val rtl8192e_mac_init_table[] = { +static const struct rtl8xxxu_reg8val rtl8192e_mac_init_table[] = { {0x011, 0xeb}, {0x012, 0x07}, {0x014, 0x75}, {0x303, 0xa7}, {0x428, 0x0a}, {0x429, 0x10}, {0x430, 0x00}, {0x431, 0x00}, {0x432, 0x00}, {0x433, 0x01}, {0x434, 0x04}, {0x435, 0x05}, @@ -62,7 +62,7 @@ static struct rtl8xxxu_reg8val rtl8192e_mac_init_table[] = { {0xffff, 0xff}, }; -static struct rtl8xxxu_reg32val rtl8192eu_phy_init_table[] = { +static const struct rtl8xxxu_reg32val rtl8192eu_phy_init_table[] = { {0x800, 0x80040000}, {0x804, 0x00000003}, {0x808, 0x0000fc00}, {0x80c, 0x0000000a}, {0x810, 0x10001331}, {0x814, 0x020c3d10}, @@ -194,7 +194,7 @@ static struct rtl8xxxu_reg32val rtl8192eu_phy_init_table[] = { {0xffff, 0xffffffff}, }; -static struct rtl8xxxu_reg32val rtl8xxx_agc_8192eu_std_table[] = { +static const struct rtl8xxxu_reg32val rtl8xxx_agc_8192eu_std_table[] = { {0xc78, 0xfb000001}, {0xc78, 0xfb010001}, {0xc78, 0xfb020001}, {0xc78, 0xfb030001}, {0xc78, 0xfb040001}, {0xc78, 0xfb050001}, @@ -263,7 +263,7 @@ static struct rtl8xxxu_reg32val rtl8xxx_agc_8192eu_std_table[] = { {0xffff, 0xffffffff} }; -static struct rtl8xxxu_reg32val rtl8xxx_agc_8192eu_highpa_table[] = { +static const struct rtl8xxxu_reg32val rtl8xxx_agc_8192eu_highpa_table[] = { {0xc78, 0xfa000001}, {0xc78, 0xf9010001}, {0xc78, 0xf8020001}, {0xc78, 0xf7030001}, {0xc78, 0xf6040001}, {0xc78, 0xf5050001}, @@ -332,7 +332,7 @@ static struct rtl8xxxu_reg32val rtl8xxx_agc_8192eu_highpa_table[] = { {0xffff, 0xffffffff} }; -static struct rtl8xxxu_rfregval rtl8192eu_radioa_init_table[] = { +static const struct rtl8xxxu_rfregval rtl8192eu_radioa_init_table[] = { {0x7f, 0x00000082}, {0x81, 0x0003fc00}, {0x00, 0x00030000}, {0x08, 0x00008400}, {0x18, 0x00000407}, {0x19, 0x00000012}, @@ -412,7 +412,7 @@ static struct rtl8xxxu_rfregval rtl8192eu_radioa_init_table[] = { {0xff, 0xffffffff} }; -static struct rtl8xxxu_rfregval rtl8192eu_radiob_init_table[] = { +static const struct rtl8xxxu_rfregval rtl8192eu_radiob_init_table[] = { {0x7f, 0x00000082}, {0x81, 0x0003fc00}, {0x00, 0x00030000}, {0x08, 0x00008400}, {0x18, 0x00000407}, {0x19, 0x00000012}, diff --git a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8723a.c b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8723a.c index e18ddd441728..44565bf4c1c2 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8723a.c +++ b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8723a.c @@ -54,7 +54,7 @@ static struct rtl8xxxu_power_base rtl8723a_power_base = { .reg_0868 = 0x02040608, }; -static struct rtl8xxxu_rfregval rtl8723au_radioa_1t_init_table[] = { +static const struct rtl8xxxu_rfregval rtl8723au_radioa_1t_init_table[] = { {0x00, 0x00030159}, {0x01, 0x00031284}, {0x02, 0x00098000}, {0x03, 0x00039c63}, {0x04, 0x000210e7}, {0x09, 0x0002044f}, diff --git a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8723b.c b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8723b.c index 7c9b8432a4e9..a27fe06d6f77 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8723b.c +++ b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8723b.c @@ -32,7 +32,7 @@ #include "rtl8xxxu.h" #include "rtl8xxxu_regs.h" -static struct rtl8xxxu_reg8val rtl8723b_mac_init_table[] = { +static const struct rtl8xxxu_reg8val rtl8723b_mac_init_table[] = { {0x02f, 0x30}, {0x035, 0x00}, {0x039, 0x08}, {0x04e, 0xe0}, {0x064, 0x00}, {0x067, 0x20}, {0x428, 0x0a}, {0x429, 0x10}, {0x430, 0x00}, {0x431, 0x00}, @@ -63,7 +63,7 @@ static struct rtl8xxxu_reg8val rtl8723b_mac_init_table[] = { {0xffff, 0xff}, }; -static struct rtl8xxxu_reg32val rtl8723b_phy_1t_init_table[] = { +static const struct rtl8xxxu_reg32val rtl8723b_phy_1t_init_table[] = { {0x800, 0x80040000}, {0x804, 0x00000003}, {0x808, 0x0000fc00}, {0x80c, 0x0000000a}, {0x810, 0x10001331}, {0x814, 0x020c3d10}, @@ -164,7 +164,7 @@ static struct rtl8xxxu_reg32val rtl8723b_phy_1t_init_table[] = { {0xffff, 0xffffffff}, }; -static struct rtl8xxxu_reg32val rtl8xxx_agc_8723bu_table[] = { +static const struct rtl8xxxu_reg32val rtl8xxx_agc_8723bu_table[] = { {0xc78, 0xfd000001}, {0xc78, 0xfc010001}, {0xc78, 0xfb020001}, {0xc78, 0xfa030001}, {0xc78, 0xf9040001}, {0xc78, 0xf8050001}, @@ -235,7 +235,7 @@ static struct rtl8xxxu_reg32val rtl8xxx_agc_8723bu_table[] = { {0xffff, 0xffffffff} }; -static struct rtl8xxxu_rfregval rtl8723bu_radioa_1t_init_table[] = { +static const struct rtl8xxxu_rfregval rtl8723bu_radioa_1t_init_table[] = { {0x00, 0x00010000}, {0xb0, 0x000dffe0}, {0xfe, 0x00000000}, {0xfe, 0x00000000}, {0xfe, 0x00000000}, {0xb1, 0x00000018}, diff --git a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c index 9ee1a5bb1195..3abc088b59d0 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c +++ b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c @@ -128,7 +128,7 @@ static struct ieee80211_supported_band rtl8xxxu_supported_band = { .n_bitrates = ARRAY_SIZE(rtl8xxxu_rates), }; -struct rtl8xxxu_reg8val rtl8xxxu_gen1_mac_init_table[] = { +const struct rtl8xxxu_reg8val rtl8xxxu_gen1_mac_init_table[] = { {0x420, 0x80}, {0x423, 0x00}, {0x430, 0x00}, {0x431, 0x00}, {0x432, 0x00}, {0x433, 0x01}, {0x434, 0x04}, {0x435, 0x05}, {0x436, 0x06}, {0x437, 0x07}, {0x438, 0x00}, {0x439, 0x00}, @@ -153,7 +153,7 @@ struct rtl8xxxu_reg8val rtl8xxxu_gen1_mac_init_table[] = { {0x70a, 0x65}, {0x70b, 0x87}, {0xffff, 0xff}, }; -static struct rtl8xxxu_reg32val rtl8723a_phy_1t_init_table[] = { +static const struct rtl8xxxu_reg32val rtl8723a_phy_1t_init_table[] = { {0x800, 0x80040000}, {0x804, 0x00000003}, {0x808, 0x0000fc00}, {0x80c, 0x0000000a}, {0x810, 0x10001331}, {0x814, 0x020c3d10}, @@ -251,7 +251,7 @@ static struct rtl8xxxu_reg32val rtl8723a_phy_1t_init_table[] = { {0xffff, 0xffffffff}, }; -static struct rtl8xxxu_reg32val rtl8192cu_phy_2t_init_table[] = { +static const struct rtl8xxxu_reg32val rtl8192cu_phy_2t_init_table[] = { {0x024, 0x0011800f}, {0x028, 0x00ffdb83}, {0x800, 0x80040002}, {0x804, 0x00000003}, {0x808, 0x0000fc00}, {0x80c, 0x0000000a}, @@ -349,7 +349,7 @@ static struct rtl8xxxu_reg32val rtl8192cu_phy_2t_init_table[] = { {0xffff, 0xffffffff}, }; -static struct rtl8xxxu_reg32val rtl8188ru_phy_1t_highpa_table[] = { +static const struct rtl8xxxu_reg32val rtl8188ru_phy_1t_highpa_table[] = { {0x024, 0x0011800f}, {0x028, 0x00ffdb83}, {0x040, 0x000c0004}, {0x800, 0x80040000}, {0x804, 0x00000001}, {0x808, 0x0000fc00}, @@ -448,7 +448,7 @@ static struct rtl8xxxu_reg32val rtl8188ru_phy_1t_highpa_table[] = { {0xffff, 0xffffffff}, }; -static struct rtl8xxxu_reg32val rtl8xxx_agc_standard_table[] = { +static const struct rtl8xxxu_reg32val rtl8xxx_agc_standard_table[] = { {0xc78, 0x7b000001}, {0xc78, 0x7b010001}, {0xc78, 0x7b020001}, {0xc78, 0x7b030001}, {0xc78, 0x7b040001}, {0xc78, 0x7b050001}, @@ -532,7 +532,7 @@ static struct rtl8xxxu_reg32val rtl8xxx_agc_standard_table[] = { {0xffff, 0xffffffff} }; -static struct rtl8xxxu_reg32val rtl8xxx_agc_highpa_table[] = { +static const struct rtl8xxxu_reg32val rtl8xxx_agc_highpa_table[] = { {0xc78, 0x7b000001}, {0xc78, 0x7b010001}, {0xc78, 0x7b020001}, {0xc78, 0x7b030001}, {0xc78, 0x7b040001}, {0xc78, 0x7b050001}, @@ -616,7 +616,7 @@ static struct rtl8xxxu_reg32val rtl8xxx_agc_highpa_table[] = { {0xffff, 0xffffffff} }; -static struct rtl8xxxu_rfregs rtl8xxxu_rfregs[] = { +static const struct rtl8xxxu_rfregs rtl8xxxu_rfregs[] = { { /* RF_A */ .hssiparm1 = REG_FPGA0_XA_HSSI_PARM1, .hssiparm2 = REG_FPGA0_XA_HSSI_PARM2, @@ -2173,7 +2173,7 @@ void rtl8xxxu_firmware_self_reset(struct rtl8xxxu_priv *priv) static int rtl8xxxu_init_mac(struct rtl8xxxu_priv *priv) { - struct rtl8xxxu_reg8val *array = priv->fops->mactable; + const struct rtl8xxxu_reg8val *array = priv->fops->mactable; int i, ret; u16 reg; u8 val; @@ -2203,7 +2203,7 @@ rtl8xxxu_init_mac(struct rtl8xxxu_priv *priv) } int rtl8xxxu_init_phy_regs(struct rtl8xxxu_priv *priv, - struct rtl8xxxu_reg32val *array) + const struct rtl8xxxu_reg32val *array) { int i, ret; u16 reg; @@ -2368,7 +2368,7 @@ static int rtl8xxxu_init_phy_bb(struct rtl8xxxu_priv *priv) } static int rtl8xxxu_init_rf_regs(struct rtl8xxxu_priv *priv, - struct rtl8xxxu_rfregval *array, + const struct rtl8xxxu_rfregval *array, enum rtl8xxxu_rfpath path) { int i, ret; @@ -2416,7 +2416,7 @@ static int rtl8xxxu_init_rf_regs(struct rtl8xxxu_priv *priv, } int rtl8xxxu_init_phy_rf(struct rtl8xxxu_priv *priv, - struct rtl8xxxu_rfregval *table, + const struct rtl8xxxu_rfregval *table, enum rtl8xxxu_rfpath path) { u32 val32; From 6f103aeb5e985ac08f3a4a049a2c17294f40cff9 Mon Sep 17 00:00:00 2001 From: Bitterblue Smith Date: Sat, 8 Oct 2022 13:56:09 +0300 Subject: [PATCH 0116/2002] wifi: rtl8xxxu: Fix reading the vendor of combo chips The wifi + bluetooth combo chips (RTL8723AU and RTL8723BU) read the chip vendor from the wrong register because the val32 variable gets overwritten. Add one more variable to avoid this. This had no real effect on RTL8723BU. It may have had an effect on RTL8723AU. Fixes: 26f1fad29ad9 ("New driver: rtl8xxxu (mac80211)") Signed-off-by: Bitterblue Smith Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/24af8024-2f07-552b-93d8-38823d8e3cb0@gmail.com --- .../wireless/realtek/rtl8xxxu/rtl8xxxu_core.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c index 3abc088b59d0..65bb4dc8f555 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c +++ b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c @@ -1610,13 +1610,13 @@ static int rtl8xxxu_identify_chip(struct rtl8xxxu_priv *priv) const struct usb_device_descriptor *descriptor = &priv->udev->descriptor; struct device *dev = &priv->udev->dev; struct ieee80211_hw *hw = priv->hw; - u32 val32, bonding; + u32 val32, bonding, sys_cfg; u16 val16; - val32 = rtl8xxxu_read32(priv, REG_SYS_CFG); - priv->chip_cut = (val32 & SYS_CFG_CHIP_VERSION_MASK) >> + sys_cfg = rtl8xxxu_read32(priv, REG_SYS_CFG); + priv->chip_cut = (sys_cfg & SYS_CFG_CHIP_VERSION_MASK) >> SYS_CFG_CHIP_VERSION_SHIFT; - if (val32 & SYS_CFG_TRP_VAUX_EN) { + if (sys_cfg & SYS_CFG_TRP_VAUX_EN) { dev_info(dev, "Unsupported test chip\n"); return -ENOTSUPP; } @@ -1632,7 +1632,7 @@ static int rtl8xxxu_identify_chip(struct rtl8xxxu_priv *priv) goto skip_complicated_chip_detection; } - if (val32 & SYS_CFG_BT_FUNC) { + if (sys_cfg & SYS_CFG_BT_FUNC) { if (priv->chip_cut >= 3) { sprintf(priv->chip_name, "8723BU"); priv->rtl_chip = RTL8723B; @@ -1654,7 +1654,7 @@ static int rtl8xxxu_identify_chip(struct rtl8xxxu_priv *priv) if (val32 & MULTI_GPS_FUNC_EN) priv->has_gps = 1; priv->is_multi_func = 1; - } else if (val32 & SYS_CFG_TYPE_ID) { + } else if (sys_cfg & SYS_CFG_TYPE_ID) { bonding = rtl8xxxu_read32(priv, REG_HPON_FSM); bonding &= HPON_FSM_BONDING_MASK; if (priv->fops->tx_desc_size == @@ -1708,7 +1708,7 @@ skip_complicated_chip_detection: case RTL8188F: case RTL8192E: case RTL8723B: - switch (val32 & SYS_CFG_VENDOR_EXT_MASK) { + switch (sys_cfg & SYS_CFG_VENDOR_EXT_MASK) { case SYS_CFG_VENDOR_ID_TSMC: sprintf(priv->chip_vendor, "TSMC"); break; @@ -1725,7 +1725,7 @@ skip_complicated_chip_detection: } break; default: - if (val32 & SYS_CFG_VENDOR_ID) { + if (sys_cfg & SYS_CFG_VENDOR_ID) { sprintf(priv->chip_vendor, "UMC"); priv->vendor_umc = 1; } else { From 6e5971503e7dc5e496fb357268242d69abb73f45 Mon Sep 17 00:00:00 2001 From: Bitterblue Smith Date: Sat, 8 Oct 2022 13:58:36 +0300 Subject: [PATCH 0117/2002] wifi: rtl8xxxu: Update module description Make the title more future-proof, using rtw88 as a model. Mention support for RTL8723BU, RTL8192EU, and RTL8188FU. Correct the module name. Signed-off-by: Bitterblue Smith Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/ecb7512a-07e8-112d-c815-1494582de5f8@gmail.com --- drivers/net/wireless/realtek/rtl8xxxu/Kconfig | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/realtek/rtl8xxxu/Kconfig b/drivers/net/wireless/realtek/rtl8xxxu/Kconfig index a263507a77a6..631d078278be 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/Kconfig +++ b/drivers/net/wireless/realtek/rtl8xxxu/Kconfig @@ -3,13 +3,14 @@ # RTL8XXXU Wireless LAN device configuration # config RTL8XXXU - tristate "RTL8723AU/RTL8188[CR]U/RTL819[12]CU (mac80211) support" + tristate "Realtek 802.11n USB wireless chips support" depends on MAC80211 && USB help This is an alternative driver for various Realtek RTL8XXX parts written to utilize the Linux mac80211 stack. The driver is known to work with a number of RTL8723AU, - RL8188CU, RTL8188RU, RTL8191CU, and RTL8192CU devices + RL8188CU, RTL8188RU, RTL8191CU, RTL8192CU, RTL8723BU, RTL8192EU, + and RTL8188FU devices. This driver is under development and has a limited feature set. In particular it does not yet support 40MHz channels @@ -22,7 +23,7 @@ config RTL8XXXU but you will need to control which module you wish to load. To compile this driver as a module, choose M here: the module will - be called r8xxxu. If unsure, say N. + be called rtl8xxxu. If unsure, say N. config RTL8XXXU_UNTESTED bool "Include support for untested Realtek 8xxx USB devices (EXPERIMENTAL)" From d0a95ef3ed86762d2356fd5443bebe7a6ef140c7 Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Sun, 9 Oct 2022 20:53:55 +0800 Subject: [PATCH 0118/2002] wifi: rtw89: 8852b: add chip_ops::set_channel_help This chip_ops is to assist set_channel, because we need setup and restore hardware before and after set_channel. Before set_channel, we stop transmitting, reset PPDU status, disable TSSI, and disable ADC. After set_channel, do opposite things in reverse order. Signed-off-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20221009125403.19662-2-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/rtw8852b.c | 84 +++++++++++++++++++ 1 file changed, 84 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852b.c b/drivers/net/wireless/realtek/rtw89/rtw8852b.c index b50fff00b139..af04a0284b56 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852b.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852b.c @@ -854,6 +854,30 @@ static void rtw8852b_bb_reset_all(struct rtw89_dev *rtwdev, enum rtw89_phy_idx p rtw89_phy_write32_idx(rtwdev, R_RSTB_ASYNC, B_RSTB_ASYNC_ALL, 1, phy_idx); } +static void rtw8852b_bb_reset_en(struct rtw89_dev *rtwdev, enum rtw89_band band, + enum rtw89_phy_idx phy_idx, bool en) +{ + if (en) { + rtw89_phy_write32_idx(rtwdev, R_S0_HW_SI_DIS, + B_S0_HW_SI_DIS_W_R_TRIG, 0x0, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_S1_HW_SI_DIS, + B_S1_HW_SI_DIS_W_R_TRIG, 0x0, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_RSTB_ASYNC, B_RSTB_ASYNC_ALL, 1, phy_idx); + if (band == RTW89_BAND_2G) + rtw89_phy_write32_mask(rtwdev, R_RXCCA, B_RXCCA_DIS, 0x0); + rtw89_phy_write32_mask(rtwdev, R_PD_CTRL, B_PD_HIT_DIS, 0x0); + } else { + rtw89_phy_write32_mask(rtwdev, R_RXCCA, B_RXCCA_DIS, 0x1); + rtw89_phy_write32_mask(rtwdev, R_PD_CTRL, B_PD_HIT_DIS, 0x1); + rtw89_phy_write32_idx(rtwdev, R_S0_HW_SI_DIS, + B_S0_HW_SI_DIS_W_R_TRIG, 0x7, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_S1_HW_SI_DIS, + B_S1_HW_SI_DIS_W_R_TRIG, 0x7, phy_idx); + fsleep(1); + rtw89_phy_write32_idx(rtwdev, R_RSTB_ASYNC, B_RSTB_ASYNC_ALL, 0, phy_idx); + } +} + static void rtw8852b_set_channel_bb(struct rtw89_dev *rtwdev, const struct rtw89_chan *chan, enum rtw89_phy_idx phy_idx) { @@ -897,6 +921,65 @@ static void rtw8852b_set_channel(struct rtw89_dev *rtwdev, rtw8852b_set_channel_rf(rtwdev, chan, phy_idx); } +static void rtw8852b_tssi_cont_en(struct rtw89_dev *rtwdev, bool en, + enum rtw89_rf_path path) +{ + static const u32 tssi_trk[2] = {R_P0_TSSI_TRK, R_P1_TSSI_TRK}; + static const u32 ctrl_bbrst[2] = {R_P0_TXPW_RSTB, R_P1_TXPW_RSTB}; + + if (en) { + rtw89_phy_write32_mask(rtwdev, ctrl_bbrst[path], B_P0_TXPW_RSTB_MANON, 0x0); + rtw89_phy_write32_mask(rtwdev, tssi_trk[path], B_P0_TSSI_TRK_EN, 0x0); + } else { + rtw89_phy_write32_mask(rtwdev, ctrl_bbrst[path], B_P0_TXPW_RSTB_MANON, 0x1); + rtw89_phy_write32_mask(rtwdev, tssi_trk[path], B_P0_TSSI_TRK_EN, 0x1); + } +} + +static void rtw8852b_tssi_cont_en_phyidx(struct rtw89_dev *rtwdev, bool en, + u8 phy_idx) +{ + if (!rtwdev->dbcc_en) { + rtw8852b_tssi_cont_en(rtwdev, en, RF_PATH_A); + rtw8852b_tssi_cont_en(rtwdev, en, RF_PATH_B); + } else { + if (phy_idx == RTW89_PHY_0) + rtw8852b_tssi_cont_en(rtwdev, en, RF_PATH_A); + else + rtw8852b_tssi_cont_en(rtwdev, en, RF_PATH_B); + } +} + +static void rtw8852b_adc_en(struct rtw89_dev *rtwdev, bool en) +{ + if (en) + rtw89_phy_write32_mask(rtwdev, R_ADC_FIFO, B_ADC_FIFO_RST, 0x0); + else + rtw89_phy_write32_mask(rtwdev, R_ADC_FIFO, B_ADC_FIFO_RST, 0xf); +} + +static void rtw8852b_set_channel_help(struct rtw89_dev *rtwdev, bool enter, + struct rtw89_channel_help_params *p, + const struct rtw89_chan *chan, + enum rtw89_mac_idx mac_idx, + enum rtw89_phy_idx phy_idx) +{ + if (enter) { + rtw89_chip_stop_sch_tx(rtwdev, RTW89_MAC_0, &p->tx_en, RTW89_SCH_TX_SEL_ALL); + rtw89_mac_cfg_ppdu_status(rtwdev, RTW89_MAC_0, false); + rtw8852b_tssi_cont_en_phyidx(rtwdev, false, RTW89_PHY_0); + rtw8852b_adc_en(rtwdev, false); + fsleep(40); + rtw8852b_bb_reset_en(rtwdev, chan->band_type, phy_idx, false); + } else { + rtw89_mac_cfg_ppdu_status(rtwdev, RTW89_MAC_0, true); + rtw8852b_adc_en(rtwdev, true); + rtw8852b_tssi_cont_en_phyidx(rtwdev, true, RTW89_PHY_0); + rtw8852b_bb_reset_en(rtwdev, chan->band_type, phy_idx, true); + rtw89_chip_resume_sch_tx(rtwdev, RTW89_MAC_0, p->tx_en); + } +} + static u32 rtw8852b_bb_cal_txpwr_ref(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx, s16 ref) { @@ -1143,6 +1226,7 @@ static const struct rtw89_chip_ops rtw8852b_chip_ops = { .enable_bb_rf = rtw8852b_mac_enable_bb_rf, .disable_bb_rf = rtw8852b_mac_disable_bb_rf, .set_channel = rtw8852b_set_channel, + .set_channel_help = rtw8852b_set_channel_help, .read_efuse = rtw8852b_read_efuse, .read_phycap = rtw8852b_read_phycap, .power_trim = rtw8852b_power_trim, From b23b36efbdac603c491197dae1d27a3c5ac4b01c Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Sun, 9 Oct 2022 20:53:56 +0800 Subject: [PATCH 0119/2002] wifi: rtw89: 8852b: add power on/off functions We need power on function to enable hardware circuits of MAC/BB/RF, and then download firmware and load PHY parameters. After more settings, it starts to work. When it enters idle, use power off function to have the lowest power consumption. Signed-off-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20221009125403.19662-3-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/mac.h | 1 + drivers/net/wireless/realtek/rtw89/reg.h | 18 ++ drivers/net/wireless/realtek/rtw89/rtw8852b.c | 192 ++++++++++++++++++ 3 files changed, 211 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/mac.h b/drivers/net/wireless/realtek/rtw89/mac.h index a9867ac351da..a6cbafb75a2b 100644 --- a/drivers/net/wireless/realtek/rtw89/mac.h +++ b/drivers/net/wireless/realtek/rtw89/mac.h @@ -1014,6 +1014,7 @@ enum rtw89_mac_xtal_si_offset { #define XTAL_SI_PON_EI BIT(1) #define XTAL_SI_PON_WEI BIT(0) XTAL_SI_SRAM_CTRL = 0xA1, +#define XTAL_SI_SRAM_DIS BIT(1) #define FULL_BIT_MASK GENMASK(7, 0) }; diff --git a/drivers/net/wireless/realtek/rtw89/reg.h b/drivers/net/wireless/realtek/rtw89/reg.h index 1539973296cd..376ce7135b38 100644 --- a/drivers/net/wireless/realtek/rtw89/reg.h +++ b/drivers/net/wireless/realtek/rtw89/reg.h @@ -34,6 +34,9 @@ #define R_AX_SYS_CLK_CTRL 0x0008 #define B_AX_CPU_CLK_EN BIT(14) +#define R_AX_SYS_SWR_CTRL1 0x0010 +#define B_AX_SYM_CTRL_SPS_PWMFREQ BIT(10) + #define R_AX_SYS_ADIE_PAD_PWR_CTRL 0x0018 #define B_AX_SYM_PADPDN_WL_PTA_1P3 BIT(6) #define B_AX_SYM_PADPDN_WL_RFC_1P3 BIT(5) @@ -42,6 +45,9 @@ #define B_AX_R_DIS_PRST BIT(6) #define B_AX_WLOCK_1C_BIT6 BIT(5) +#define R_AX_AFE_LDO_CTRL 0x0020 +#define B_AX_AON_OFF_PC_EN BIT(23) + #define R_AX_EFUSE_CTRL_1 0x0038 #define B_AX_EF_PGPD_MASK GENMASK(30, 28) #define B_AX_EF_RDT BIT(27) @@ -118,6 +124,9 @@ #define B_AX_R_AX_BG_LPF BIT(2) #define B_AX_R_AX_BG GENMASK(1, 0) +#define R_AX_HCI_LDO_CTRL 0x007A +#define B_AX_R_AX_VADJ_MASK GENMASK(3, 0) + #define R_AX_PLATFORM_ENABLE 0x0088 #define B_AX_AXIDMA_EN BIT(3) #define B_AX_WCPU_EN BIT(1) @@ -125,6 +134,7 @@ #define R_AX_WLLPS_CTRL 0x0090 #define B_AX_DIS_WLBT_LPSEN_LOPC BIT(1) +#define SW_LPS_OPTION 0x0001A0B2 #define R_AX_SCOREBOARD 0x00AC #define B_AX_TOGGLE BIT(31) @@ -229,6 +239,9 @@ #define R_AX_GPIO0_7_FUNC_SEL 0x02D0 +#define R_AX_EECS_EESK_FUNC_SEL 0x02D8 +#define B_AX_PINMUX_EESK_FUNC_SEL_MASK GENMASK(7, 4) + #define R_AX_LED1_FUNC_SEL 0x02DC #define B_AX_PINMUX_EESK_FUNC_SEL_V1_MASK GENMASK(27, 24) #define PINMUX_EESK_FUNC_SEL_BT_LOG 0x1 @@ -253,6 +266,10 @@ #define B_AX_USB_HCISYS_PWR_STE_MASK GENMASK(3, 2) #define B_AX_PCIE_HCISYS_PWR_STE_MASK GENMASK(1, 0) +#define R_AX_SPS_DIG_OFF_CTRL0 0x0400 +#define B_AX_C3_L1_MASK GENMASK(5, 4) +#define B_AX_C1_L1_MASK GENMASK(1, 0) + #define R_AX_AFE_OFF_CTRL1 0x0444 #define B_AX_S1_LDO_VSEL_F_MASK GENMASK(25, 24) #define B_AX_S1_LDO2PWRCUT_F BIT(23) @@ -449,6 +466,7 @@ #define B_AX_DISPATCHER_EN BIT(18) #define B_AX_BBRPT_EN BIT(17) #define B_AX_MAC_SEC_EN BIT(16) +#define B_AX_DMACREG_GCKEN BIT(15) #define B_AX_MAC_UN_EN BIT(15) #define B_AX_H_AXIDMA_EN BIT(14) diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852b.c b/drivers/net/wireless/realtek/rtw89/rtw8852b.c index af04a0284b56..f54a4ea3c6b5 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852b.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852b.c @@ -56,6 +56,194 @@ static const struct rtw89_dle_mem rtw8852b_dle_mem_pcie[] = { NULL}, }; +static int rtw8852b_pwr_on_func(struct rtw89_dev *rtwdev) +{ + u32 val32; + u32 ret; + + rtw89_write32_clr(rtwdev, R_AX_SYS_PW_CTRL, B_AX_AFSM_WLSUS_EN | + B_AX_AFSM_PCIE_SUS_EN); + rtw89_write32_set(rtwdev, R_AX_SYS_PW_CTRL, B_AX_DIS_WLBT_PDNSUSEN_SOPC); + rtw89_write32_set(rtwdev, R_AX_WLLPS_CTRL, B_AX_DIS_WLBT_LPSEN_LOPC); + rtw89_write32_clr(rtwdev, R_AX_SYS_PW_CTRL, B_AX_APDM_HPDN); + rtw89_write32_clr(rtwdev, R_AX_SYS_PW_CTRL, B_AX_APFM_SWLPS); + + ret = read_poll_timeout(rtw89_read32, val32, val32 & B_AX_RDY_SYSPWR, + 1000, 20000, false, rtwdev, R_AX_SYS_PW_CTRL); + if (ret) + return ret; + + rtw89_write32_set(rtwdev, R_AX_AFE_LDO_CTRL, B_AX_AON_OFF_PC_EN); + ret = read_poll_timeout(rtw89_read32, val32, val32 & B_AX_AON_OFF_PC_EN, + 1000, 20000, false, rtwdev, R_AX_AFE_LDO_CTRL); + if (ret) + return ret; + + rtw89_write32_mask(rtwdev, R_AX_SPS_DIG_OFF_CTRL0, B_AX_C1_L1_MASK, 0x1); + rtw89_write32_mask(rtwdev, R_AX_SPS_DIG_OFF_CTRL0, B_AX_C3_L1_MASK, 0x3); + rtw89_write32_set(rtwdev, R_AX_SYS_PW_CTRL, B_AX_EN_WLON); + rtw89_write32_set(rtwdev, R_AX_SYS_PW_CTRL, B_AX_APFN_ONMAC); + + ret = read_poll_timeout(rtw89_read32, val32, !(val32 & B_AX_APFN_ONMAC), + 1000, 20000, false, rtwdev, R_AX_SYS_PW_CTRL); + if (ret) + return ret; + + rtw89_write8_set(rtwdev, R_AX_PLATFORM_ENABLE, B_AX_PLATFORM_EN); + rtw89_write8_clr(rtwdev, R_AX_PLATFORM_ENABLE, B_AX_PLATFORM_EN); + rtw89_write8_set(rtwdev, R_AX_PLATFORM_ENABLE, B_AX_PLATFORM_EN); + rtw89_write8_clr(rtwdev, R_AX_PLATFORM_ENABLE, B_AX_PLATFORM_EN); + + rtw89_write8_set(rtwdev, R_AX_PLATFORM_ENABLE, B_AX_PLATFORM_EN); + rtw89_write32_clr(rtwdev, R_AX_SYS_SDIO_CTRL, B_AX_PCIE_CALIB_EN_V1); + + rtw89_write32_set(rtwdev, R_AX_SYS_ADIE_PAD_PWR_CTRL, B_AX_SYM_PADPDN_WL_PTA_1P3); + + ret = rtw89_mac_write_xtal_si(rtwdev, XTAL_SI_ANAPAR_WL, + XTAL_SI_GND_SHDN_WL, XTAL_SI_GND_SHDN_WL); + if (ret) + return ret; + + rtw89_write32_set(rtwdev, R_AX_SYS_ADIE_PAD_PWR_CTRL, B_AX_SYM_PADPDN_WL_RFC_1P3); + + ret = rtw89_mac_write_xtal_si(rtwdev, XTAL_SI_ANAPAR_WL, + XTAL_SI_SHDN_WL, XTAL_SI_SHDN_WL); + if (ret) + return ret; + ret = rtw89_mac_write_xtal_si(rtwdev, XTAL_SI_ANAPAR_WL, XTAL_SI_OFF_WEI, + XTAL_SI_OFF_WEI); + if (ret) + return ret; + ret = rtw89_mac_write_xtal_si(rtwdev, XTAL_SI_ANAPAR_WL, XTAL_SI_OFF_EI, + XTAL_SI_OFF_EI); + if (ret) + return ret; + ret = rtw89_mac_write_xtal_si(rtwdev, XTAL_SI_ANAPAR_WL, 0, XTAL_SI_RFC2RF); + if (ret) + return ret; + ret = rtw89_mac_write_xtal_si(rtwdev, XTAL_SI_ANAPAR_WL, XTAL_SI_PON_WEI, + XTAL_SI_PON_WEI); + if (ret) + return ret; + ret = rtw89_mac_write_xtal_si(rtwdev, XTAL_SI_ANAPAR_WL, XTAL_SI_PON_EI, + XTAL_SI_PON_EI); + if (ret) + return ret; + ret = rtw89_mac_write_xtal_si(rtwdev, XTAL_SI_ANAPAR_WL, 0, XTAL_SI_SRAM2RFC); + if (ret) + return ret; + ret = rtw89_mac_write_xtal_si(rtwdev, XTAL_SI_SRAM_CTRL, 0, XTAL_SI_SRAM_DIS); + if (ret) + return ret; + ret = rtw89_mac_write_xtal_si(rtwdev, XTAL_SI_XTAL_XMD_2, 0, XTAL_SI_LDO_LPS); + if (ret) + return ret; + ret = rtw89_mac_write_xtal_si(rtwdev, XTAL_SI_XTAL_XMD_4, 0, XTAL_SI_LPS_CAP); + if (ret) + return ret; + + rtw89_write32_set(rtwdev, R_AX_PMC_DBG_CTRL2, B_AX_SYSON_DIS_PMCR_AX_WRMSK); + rtw89_write32_set(rtwdev, R_AX_SYS_ISO_CTRL, B_AX_ISO_EB2CORE); + rtw89_write32_clr(rtwdev, R_AX_SYS_ISO_CTRL, B_AX_PWC_EV2EF_B15); + + fsleep(1000); + + rtw89_write32_clr(rtwdev, R_AX_SYS_ISO_CTRL, B_AX_PWC_EV2EF_B14); + rtw89_write32_clr(rtwdev, R_AX_PMC_DBG_CTRL2, B_AX_SYSON_DIS_PMCR_AX_WRMSK); + + if (!rtwdev->efuse.valid || rtwdev->efuse.power_k_valid) + goto func_en; + + rtw89_write32_mask(rtwdev, R_AX_SPS_DIG_ON_CTRL0, B_AX_VOL_L1_MASK, 0x9); + rtw89_write32_mask(rtwdev, R_AX_SPS_DIG_ON_CTRL0, B_AX_VREFPFM_L_MASK, 0xA); + + if (rtwdev->hal.cv == CHIP_CBV) { + rtw89_write32_set(rtwdev, R_AX_PMC_DBG_CTRL2, B_AX_SYSON_DIS_PMCR_AX_WRMSK); + rtw89_write16_mask(rtwdev, R_AX_HCI_LDO_CTRL, B_AX_R_AX_VADJ_MASK, 0xA); + rtw89_write32_clr(rtwdev, R_AX_PMC_DBG_CTRL2, B_AX_SYSON_DIS_PMCR_AX_WRMSK); + } + +func_en: + rtw89_write32_set(rtwdev, R_AX_DMAC_FUNC_EN, + B_AX_MAC_FUNC_EN | B_AX_DMAC_FUNC_EN | B_AX_MPDU_PROC_EN | + B_AX_WD_RLS_EN | B_AX_DLE_WDE_EN | B_AX_TXPKT_CTRL_EN | + B_AX_STA_SCH_EN | B_AX_DLE_PLE_EN | B_AX_PKT_BUF_EN | + B_AX_DMAC_TBL_EN | B_AX_PKT_IN_EN | B_AX_DLE_CPUIO_EN | + B_AX_DISPATCHER_EN | B_AX_BBRPT_EN | B_AX_MAC_SEC_EN | + B_AX_DMACREG_GCKEN); + rtw89_write32_set(rtwdev, R_AX_CMAC_FUNC_EN, + B_AX_CMAC_EN | B_AX_CMAC_TXEN | B_AX_CMAC_RXEN | + B_AX_FORCE_CMACREG_GCKEN | B_AX_PHYINTF_EN | B_AX_CMAC_DMA_EN | + B_AX_PTCLTOP_EN | B_AX_SCHEDULER_EN | B_AX_TMAC_EN | + B_AX_RMAC_EN); + + rtw89_write32_mask(rtwdev, R_AX_EECS_EESK_FUNC_SEL, B_AX_PINMUX_EESK_FUNC_SEL_MASK, + PINMUX_EESK_FUNC_SEL_BT_LOG); + + return 0; +} + +static int rtw8852b_pwr_off_func(struct rtw89_dev *rtwdev) +{ + u32 val32; + u32 ret; + + ret = rtw89_mac_write_xtal_si(rtwdev, XTAL_SI_ANAPAR_WL, XTAL_SI_RFC2RF, + XTAL_SI_RFC2RF); + if (ret) + return ret; + ret = rtw89_mac_write_xtal_si(rtwdev, XTAL_SI_ANAPAR_WL, 0, XTAL_SI_OFF_EI); + if (ret) + return ret; + ret = rtw89_mac_write_xtal_si(rtwdev, XTAL_SI_ANAPAR_WL, 0, XTAL_SI_OFF_WEI); + if (ret) + return ret; + ret = rtw89_mac_write_xtal_si(rtwdev, XTAL_SI_WL_RFC_S0, 0, XTAL_SI_RF00); + if (ret) + return ret; + ret = rtw89_mac_write_xtal_si(rtwdev, XTAL_SI_WL_RFC_S1, 0, XTAL_SI_RF10); + if (ret) + return ret; + ret = rtw89_mac_write_xtal_si(rtwdev, XTAL_SI_ANAPAR_WL, XTAL_SI_SRAM2RFC, + XTAL_SI_SRAM2RFC); + if (ret) + return ret; + ret = rtw89_mac_write_xtal_si(rtwdev, XTAL_SI_ANAPAR_WL, 0, XTAL_SI_PON_EI); + if (ret) + return ret; + ret = rtw89_mac_write_xtal_si(rtwdev, XTAL_SI_ANAPAR_WL, 0, XTAL_SI_PON_WEI); + if (ret) + return ret; + + rtw89_write32_set(rtwdev, R_AX_SYS_PW_CTRL, B_AX_EN_WLON); + rtw89_write8_clr(rtwdev, R_AX_SYS_FUNC_EN, B_AX_FEN_BB_GLB_RSTN | B_AX_FEN_BBRSTB); + rtw89_write32_clr(rtwdev, R_AX_SYS_ADIE_PAD_PWR_CTRL, B_AX_SYM_PADPDN_WL_RFC_1P3); + + ret = rtw89_mac_write_xtal_si(rtwdev, XTAL_SI_ANAPAR_WL, 0, XTAL_SI_SHDN_WL); + if (ret) + return ret; + + rtw89_write32_clr(rtwdev, R_AX_SYS_ADIE_PAD_PWR_CTRL, B_AX_SYM_PADPDN_WL_PTA_1P3); + + ret = rtw89_mac_write_xtal_si(rtwdev, XTAL_SI_ANAPAR_WL, 0, XTAL_SI_GND_SHDN_WL); + if (ret) + return ret; + + rtw89_write32_set(rtwdev, R_AX_SYS_PW_CTRL, B_AX_APFM_OFFMAC); + + ret = read_poll_timeout(rtw89_read32, val32, !(val32 & B_AX_APFM_OFFMAC), + 1000, 20000, false, rtwdev, R_AX_SYS_PW_CTRL); + if (ret) + return ret; + + rtw89_write32(rtwdev, R_AX_WLLPS_CTRL, SW_LPS_OPTION); + rtw89_write32_set(rtwdev, R_AX_SYS_SWR_CTRL1, B_AX_SYM_CTRL_SPS_PWMFREQ); + rtw89_write32_mask(rtwdev, R_AX_SPS_DIG_ON_CTRL0, B_AX_REG_ZCDC_H_MASK, 0x3); + rtw89_write32_set(rtwdev, R_AX_SYS_PW_CTRL, B_AX_APFM_SWLPS); + + return 0; +} + static void rtw8852be_efuse_parsing(struct rtw89_efuse *efuse, struct rtw8852b_efuse *map) { @@ -1233,6 +1421,8 @@ static const struct rtw89_chip_ops rtw8852b_chip_ops = { .set_txpwr = rtw8852b_set_txpwr, .set_txpwr_ctrl = rtw8852b_set_txpwr_ctrl, .init_txpwr_unit = rtw8852b_init_txpwr_unit, + .pwr_on_func = rtw8852b_pwr_on_func, + .pwr_off_func = rtw8852b_pwr_off_func, }; const struct rtw89_chip_info rtw8852b_chip_info = { @@ -1242,6 +1432,8 @@ const struct rtw89_chip_info rtw8852b_chip_info = { .dle_scc_rsvd_size = 98304, .hfc_param_ini = rtw8852b_hfc_param_ini_pcie, .dle_mem = rtw8852b_dle_mem_pcie, + .pwr_on_seq = NULL, + .pwr_off_seq = NULL, .sec_ctrl_efuse_size = 4, .physical_efuse_size = 1216, .logical_efuse_size = 2048, From a804479839e1cf502a76c407f3e07135ddbe5032 Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Sun, 9 Oct 2022 20:53:57 +0800 Subject: [PATCH 0120/2002] wifi: rtw89: 8852b: add basic baseband chip_ops chip_ops::bb_reset is to reset baseband state after loading parameters, because its state could be unpredictable at that moment. The other is chip_ops::bb_sethw that is to set some baseband settings not including in parameter tables. Signed-off-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20221009125403.19662-4-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/rtw8852b.c | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852b.c b/drivers/net/wireless/realtek/rtw89/rtw8852b.c index f54a4ea3c6b5..f13c657f4d68 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852b.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852b.c @@ -1066,6 +1066,46 @@ static void rtw8852b_bb_reset_en(struct rtw89_dev *rtwdev, enum rtw89_band band, } } +static void rtw8852b_bb_reset(struct rtw89_dev *rtwdev, + enum rtw89_phy_idx phy_idx) +{ + rtw89_phy_write32_set(rtwdev, R_P0_TXPW_RSTB, B_P0_TXPW_RSTB_MANON); + rtw89_phy_write32_set(rtwdev, R_P0_TSSI_TRK, B_P0_TSSI_TRK_EN); + rtw89_phy_write32_set(rtwdev, R_P1_TXPW_RSTB, B_P1_TXPW_RSTB_MANON); + rtw89_phy_write32_set(rtwdev, R_P1_TSSI_TRK, B_P1_TSSI_TRK_EN); + rtw8852b_bb_reset_all(rtwdev, phy_idx); + rtw89_phy_write32_clr(rtwdev, R_P0_TXPW_RSTB, B_P0_TXPW_RSTB_MANON); + rtw89_phy_write32_clr(rtwdev, R_P0_TSSI_TRK, B_P0_TSSI_TRK_EN); + rtw89_phy_write32_clr(rtwdev, R_P1_TXPW_RSTB, B_P1_TXPW_RSTB_MANON); + rtw89_phy_write32_clr(rtwdev, R_P1_TSSI_TRK, B_P1_TSSI_TRK_EN); +} + +static void rtw8852b_bb_macid_ctrl_init(struct rtw89_dev *rtwdev, + enum rtw89_phy_idx phy_idx) +{ + u32 addr; + + for (addr = R_AX_PWR_MACID_LMT_TABLE0; + addr <= R_AX_PWR_MACID_LMT_TABLE127; addr += 4) + rtw89_mac_txpwr_write32(rtwdev, phy_idx, addr, 0); +} + +static void rtw8852b_bb_sethw(struct rtw89_dev *rtwdev) +{ + struct rtw89_phy_efuse_gain *gain = &rtwdev->efuse_gain; + + rtw89_phy_write32_clr(rtwdev, R_P0_EN_SOUND_WO_NDP, B_P0_EN_SOUND_WO_NDP); + rtw89_phy_write32_clr(rtwdev, R_P1_EN_SOUND_WO_NDP, B_P1_EN_SOUND_WO_NDP); + + rtw8852b_bb_macid_ctrl_init(rtwdev, RTW89_PHY_0); + + /* read these registers after loading BB parameters */ + gain->offset_base[RTW89_PHY_0] = + rtw89_phy_read32_mask(rtwdev, R_P0_RPL1, B_P0_RPL1_BIAS_MASK); + gain->rssi_base[RTW89_PHY_0] = + rtw89_phy_read32_mask(rtwdev, R_P1_RPL1, B_P0_RPL1_BIAS_MASK); +} + static void rtw8852b_set_channel_bb(struct rtw89_dev *rtwdev, const struct rtw89_chan *chan, enum rtw89_phy_idx phy_idx) { @@ -1413,6 +1453,8 @@ static int rtw8852b_mac_disable_bb_rf(struct rtw89_dev *rtwdev) static const struct rtw89_chip_ops rtw8852b_chip_ops = { .enable_bb_rf = rtw8852b_mac_enable_bb_rf, .disable_bb_rf = rtw8852b_mac_disable_bb_rf, + .bb_reset = rtw8852b_bb_reset, + .bb_sethw = rtw8852b_bb_sethw, .set_channel = rtw8852b_set_channel, .set_channel_help = rtw8852b_set_channel_help, .read_efuse = rtw8852b_read_efuse, From 8f88474ce3eca2dd8fb4e08d4b6ab71e76312e3e Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Sun, 9 Oct 2022 20:53:58 +0800 Subject: [PATCH 0121/2002] wifi: rtw89: 8852b: add chip_ops to get thermal Thermal value reflects temperature that will affect RF performance, so we re-calibrate RF characteristics if delta of thermal over a threshold. Signed-off-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20221009125403.19662-5-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/rtw8852b.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852b.c b/drivers/net/wireless/realtek/rtw89/rtw8852b.c index f13c657f4d68..09374b92f661 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852b.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852b.c @@ -1397,6 +1397,23 @@ rtw8852b_init_txpwr_unit(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx) return 0; } +static u8 rtw8852b_get_thermal(struct rtw89_dev *rtwdev, enum rtw89_rf_path rf_path) +{ + if (rtwdev->is_tssi_mode[rf_path]) { + u32 addr = 0x1c10 + (rf_path << 13); + + return rtw89_phy_read32_mask(rtwdev, addr, 0x3F000000); + } + + rtw89_write_rf(rtwdev, rf_path, RR_TM, RR_TM_TRI, 0x1); + rtw89_write_rf(rtwdev, rf_path, RR_TM, RR_TM_TRI, 0x0); + rtw89_write_rf(rtwdev, rf_path, RR_TM, RR_TM_TRI, 0x1); + + fsleep(200); + + return rtw89_read_rf(rtwdev, rf_path, RR_TM, RR_TM_VAL); +} + static int rtw8852b_mac_enable_bb_rf(struct rtw89_dev *rtwdev) { int ret; @@ -1463,6 +1480,7 @@ static const struct rtw89_chip_ops rtw8852b_chip_ops = { .set_txpwr = rtw8852b_set_txpwr, .set_txpwr_ctrl = rtw8852b_set_txpwr_ctrl, .init_txpwr_unit = rtw8852b_init_txpwr_unit, + .get_thermal = rtw8852b_get_thermal, .pwr_on_func = rtw8852b_pwr_on_func, .pwr_off_func = rtw8852b_pwr_off_func, }; From 98bf0ddf20fc2d70d11f1af6e041ee4fad1392ac Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Sun, 9 Oct 2022 20:53:59 +0800 Subject: [PATCH 0122/2002] wifi: rtw89: 8852b: add chip_ops related to BT coexistence These chip_ops are used to assist BT coexistence module to control chip specific operations, such as initial, pre-AGC, BT grant, set wifi priority and tx power, RX gain, and get BT counter. Signed-off-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20221009125403.19662-6-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/reg.h | 1 + drivers/net/wireless/realtek/rtw89/rtw8852b.c | 305 ++++++++++++++++++ 2 files changed, 306 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/reg.h b/drivers/net/wireless/realtek/rtw89/reg.h index 376ce7135b38..570fb1aee80c 100644 --- a/drivers/net/wireless/realtek/rtw89/reg.h +++ b/drivers/net/wireless/realtek/rtw89/reg.h @@ -3151,6 +3151,7 @@ #define BTC_BREAK_PARAM 0xf0ffffff #define R_BTC_BT_COEX_MSK_TABLE 0xDA30 +#define B_BTC_PRI_MASK_RXCCK_V1 BIT(28) #define B_BTC_PRI_MASK_TX_RESP_V1 BIT(3) #define R_AX_BT_COEX_CFG_2 0xDA34 diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852b.c b/drivers/net/wireless/realtek/rtw89/rtw8852b.c index 09374b92f661..ee5a29f35db2 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852b.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852b.c @@ -56,6 +56,44 @@ static const struct rtw89_dle_mem rtw8852b_dle_mem_pcie[] = { NULL}, }; +static const struct rtw89_reg3_def rtw8852b_btc_preagc_en_defs[] = { + {0x46D0, GENMASK(1, 0), 0x3}, + {0x4790, GENMASK(1, 0), 0x3}, + {0x4AD4, GENMASK(31, 0), 0xf}, + {0x4AE0, GENMASK(31, 0), 0xf}, + {0x4688, GENMASK(31, 24), 0x80}, + {0x476C, GENMASK(31, 24), 0x80}, + {0x4694, GENMASK(7, 0), 0x80}, + {0x4694, GENMASK(15, 8), 0x80}, + {0x4778, GENMASK(7, 0), 0x80}, + {0x4778, GENMASK(15, 8), 0x80}, + {0x4AE4, GENMASK(23, 0), 0x780D1E}, + {0x4AEC, GENMASK(23, 0), 0x780D1E}, + {0x469C, GENMASK(31, 26), 0x34}, + {0x49F0, GENMASK(31, 26), 0x34}, +}; + +static DECLARE_PHY_REG3_TBL(rtw8852b_btc_preagc_en_defs); + +static const struct rtw89_reg3_def rtw8852b_btc_preagc_dis_defs[] = { + {0x46D0, GENMASK(1, 0), 0x0}, + {0x4790, GENMASK(1, 0), 0x0}, + {0x4AD4, GENMASK(31, 0), 0x60}, + {0x4AE0, GENMASK(31, 0), 0x60}, + {0x4688, GENMASK(31, 24), 0x1a}, + {0x476C, GENMASK(31, 24), 0x1a}, + {0x4694, GENMASK(7, 0), 0x2a}, + {0x4694, GENMASK(15, 8), 0x2a}, + {0x4778, GENMASK(7, 0), 0x2a}, + {0x4778, GENMASK(15, 8), 0x2a}, + {0x4AE4, GENMASK(23, 0), 0x79E99E}, + {0x4AEC, GENMASK(23, 0), 0x79E99E}, + {0x469C, GENMASK(31, 26), 0x26}, + {0x49F0, GENMASK(31, 26), 0x26}, +}; + +static DECLARE_PHY_REG3_TBL(rtw8852b_btc_preagc_dis_defs); + static int rtw8852b_pwr_on_func(struct rtw89_dev *rtwdev) { u32 val32; @@ -1397,6 +1435,55 @@ rtw8852b_init_txpwr_unit(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx) return 0; } +static void rtw8852b_bb_ctrl_btc_preagc(struct rtw89_dev *rtwdev, bool bt_en) +{ + rtw89_phy_write_reg3_tbl(rtwdev, bt_en ? &rtw8852b_btc_preagc_en_defs_tbl : + &rtw8852b_btc_preagc_dis_defs_tbl); +} + +static void rtw8852b_ctrl_btg(struct rtw89_dev *rtwdev, bool btg) +{ + if (btg) { + rtw89_phy_write32_mask(rtwdev, R_PATH0_BT_SHARE_V1, + B_PATH0_BT_SHARE_V1, 0x1); + rtw89_phy_write32_mask(rtwdev, R_PATH0_BTG_PATH_V1, + B_PATH0_BTG_PATH_V1, 0x0); + rtw89_phy_write32_mask(rtwdev, R_PATH1_G_LNA6_OP1DB_V1, + B_PATH1_G_LNA6_OP1DB_V1, 0x20); + rtw89_phy_write32_mask(rtwdev, R_PATH1_G_TIA0_LNA6_OP1DB_V1, + B_PATH1_G_TIA0_LNA6_OP1DB_V1, 0x30); + rtw89_phy_write32_mask(rtwdev, R_PATH1_BT_SHARE_V1, + B_PATH1_BT_SHARE_V1, 0x1); + rtw89_phy_write32_mask(rtwdev, R_PATH1_BTG_PATH_V1, + B_PATH1_BTG_PATH_V1, 0x1); + rtw89_phy_write32_mask(rtwdev, R_PMAC_GNT, B_PMAC_GNT_P1, 0x0); + rtw89_phy_write32_mask(rtwdev, R_CHBW_MOD_V1, B_BT_SHARE, 0x1); + rtw89_phy_write32_mask(rtwdev, R_FC0_BW_V1, B_ANT_RX_BT_SEG0, 0x2); + rtw89_phy_write32_mask(rtwdev, R_BT_DYN_DC_EST_EN_V1, + B_BT_DYN_DC_EST_EN_MSK, 0x1); + rtw89_phy_write32_mask(rtwdev, R_GNT_BT_WGT_EN, B_GNT_BT_WGT_EN, 0x1); + } else { + rtw89_phy_write32_mask(rtwdev, R_PATH0_BT_SHARE_V1, + B_PATH0_BT_SHARE_V1, 0x0); + rtw89_phy_write32_mask(rtwdev, R_PATH0_BTG_PATH_V1, + B_PATH0_BTG_PATH_V1, 0x0); + rtw89_phy_write32_mask(rtwdev, R_PATH1_G_LNA6_OP1DB_V1, + B_PATH1_G_LNA6_OP1DB_V1, 0x1a); + rtw89_phy_write32_mask(rtwdev, R_PATH1_G_TIA0_LNA6_OP1DB_V1, + B_PATH1_G_TIA0_LNA6_OP1DB_V1, 0x2a); + rtw89_phy_write32_mask(rtwdev, R_PATH1_BT_SHARE_V1, + B_PATH1_BT_SHARE_V1, 0x0); + rtw89_phy_write32_mask(rtwdev, R_PATH1_BTG_PATH_V1, + B_PATH1_BTG_PATH_V1, 0x0); + rtw89_phy_write32_mask(rtwdev, R_PMAC_GNT, B_PMAC_GNT_P1, 0xc); + rtw89_phy_write32_mask(rtwdev, R_CHBW_MOD_V1, B_BT_SHARE, 0x0); + rtw89_phy_write32_mask(rtwdev, R_FC0_BW_V1, B_ANT_RX_BT_SEG0, 0x0); + rtw89_phy_write32_mask(rtwdev, R_BT_DYN_DC_EST_EN_V1, + B_BT_DYN_DC_EST_EN_MSK, 0x1); + rtw89_phy_write32_mask(rtwdev, R_GNT_BT_WGT_EN, B_GNT_BT_WGT_EN, 0x0); + } +} + static u8 rtw8852b_get_thermal(struct rtw89_dev *rtwdev, enum rtw89_rf_path rf_path) { if (rtwdev->is_tssi_mode[rf_path]) { @@ -1414,6 +1501,212 @@ static u8 rtw8852b_get_thermal(struct rtw89_dev *rtwdev, enum rtw89_rf_path rf_p return rtw89_read_rf(rtwdev, rf_path, RR_TM, RR_TM_VAL); } +static void rtw8852b_btc_set_rfe(struct rtw89_dev *rtwdev) +{ + struct rtw89_btc *btc = &rtwdev->btc; + struct rtw89_btc_module *module = &btc->mdinfo; + + module->rfe_type = rtwdev->efuse.rfe_type; + module->cv = rtwdev->hal.cv; + module->bt_solo = 0; + module->switch_type = BTC_SWITCH_INTERNAL; + + if (module->rfe_type > 0) + module->ant.num = module->rfe_type % 2 ? 2 : 3; + else + module->ant.num = 2; + + module->ant.diversity = 0; + module->ant.isolation = 10; + + if (module->ant.num == 3) { + module->ant.type = BTC_ANT_DEDICATED; + module->bt_pos = BTC_BT_ALONE; + } else { + module->ant.type = BTC_ANT_SHARED; + module->bt_pos = BTC_BT_BTG; + } +} + +static +void rtw8852b_set_trx_mask(struct rtw89_dev *rtwdev, u8 path, u8 group, u32 val) +{ + rtw89_write_rf(rtwdev, path, RR_LUTWE, RFREG_MASK, 0x20000); + rtw89_write_rf(rtwdev, path, RR_LUTWA, RFREG_MASK, group); + rtw89_write_rf(rtwdev, path, RR_LUTWD0, RFREG_MASK, val); + rtw89_write_rf(rtwdev, path, RR_LUTWE, RFREG_MASK, 0x0); +} + +static void rtw8852b_btc_init_cfg(struct rtw89_dev *rtwdev) +{ + struct rtw89_btc *btc = &rtwdev->btc; + struct rtw89_btc_module *module = &btc->mdinfo; + const struct rtw89_chip_info *chip = rtwdev->chip; + const struct rtw89_mac_ax_coex coex_params = { + .pta_mode = RTW89_MAC_AX_COEX_RTK_MODE, + .direction = RTW89_MAC_AX_COEX_INNER, + }; + + /* PTA init */ + rtw89_mac_coex_init(rtwdev, &coex_params); + + /* set WL Tx response = Hi-Pri */ + chip->ops->btc_set_wl_pri(rtwdev, BTC_PRI_MASK_TX_RESP, true); + chip->ops->btc_set_wl_pri(rtwdev, BTC_PRI_MASK_BEACON, true); + + /* set rf gnt debug off */ + rtw89_write_rf(rtwdev, RF_PATH_A, RR_WLSEL, RFREG_MASK, 0x0); + rtw89_write_rf(rtwdev, RF_PATH_B, RR_WLSEL, RFREG_MASK, 0x0); + + /* set WL Tx thru in TRX mask table if GNT_WL = 0 && BT_S1 = ss group */ + if (module->ant.type == BTC_ANT_SHARED) { + rtw8852b_set_trx_mask(rtwdev, RF_PATH_A, BTC_BT_SS_GROUP, 0x5ff); + rtw8852b_set_trx_mask(rtwdev, RF_PATH_B, BTC_BT_SS_GROUP, 0x5ff); + /* set path-A(S0) Tx/Rx no-mask if GNT_WL=0 && BT_S1=tx group */ + rtw8852b_set_trx_mask(rtwdev, RF_PATH_A, BTC_BT_TX_GROUP, 0x5ff); + rtw8852b_set_trx_mask(rtwdev, RF_PATH_B, BTC_BT_TX_GROUP, 0x55f); + } else { /* set WL Tx stb if GNT_WL = 0 && BT_S1 = ss group for 3-ant */ + rtw8852b_set_trx_mask(rtwdev, RF_PATH_A, BTC_BT_SS_GROUP, 0x5df); + rtw8852b_set_trx_mask(rtwdev, RF_PATH_B, BTC_BT_SS_GROUP, 0x5df); + rtw8852b_set_trx_mask(rtwdev, RF_PATH_A, BTC_BT_TX_GROUP, 0x5ff); + rtw8852b_set_trx_mask(rtwdev, RF_PATH_B, BTC_BT_TX_GROUP, 0x5ff); + } + + /* set PTA break table */ + rtw89_write32(rtwdev, R_BTC_BREAK_TABLE, BTC_BREAK_PARAM); + + /* enable BT counter 0xda40[16,2] = 2b'11 */ + rtw89_write32_set(rtwdev, R_AX_CSR_MODE, B_AX_BT_CNT_RST | B_AX_STATIS_BT_EN); + btc->cx.wl.status.map.init_ok = true; +} + +static +void rtw8852b_btc_set_wl_pri(struct rtw89_dev *rtwdev, u8 map, bool state) +{ + u32 bitmap; + u32 reg; + + switch (map) { + case BTC_PRI_MASK_TX_RESP: + reg = R_BTC_BT_COEX_MSK_TABLE; + bitmap = B_BTC_PRI_MASK_TX_RESP_V1; + break; + case BTC_PRI_MASK_BEACON: + reg = R_AX_WL_PRI_MSK; + bitmap = B_AX_PTA_WL_PRI_MASK_BCNQ; + break; + case BTC_PRI_MASK_RX_CCK: + reg = R_BTC_BT_COEX_MSK_TABLE; + bitmap = B_BTC_PRI_MASK_RXCCK_V1; + break; + default: + return; + } + + if (state) + rtw89_write32_set(rtwdev, reg, bitmap); + else + rtw89_write32_clr(rtwdev, reg, bitmap); +} + +union rtw8852b_btc_wl_txpwr_ctrl { + u32 txpwr_val; + struct { + union { + u16 ctrl_all_time; + struct { + s16 data:9; + u16 rsvd:6; + u16 flag:1; + } all_time; + }; + union { + u16 ctrl_gnt_bt; + struct { + s16 data:9; + u16 rsvd:7; + } gnt_bt; + }; + }; +} __packed; + +static void +rtw8852b_btc_set_wl_txpwr_ctrl(struct rtw89_dev *rtwdev, u32 txpwr_val) +{ + union rtw8852b_btc_wl_txpwr_ctrl arg = { .txpwr_val = txpwr_val }; + s32 val; + +#define __write_ctrl(_reg, _msk, _val, _en, _cond) \ +do { \ + u32 _wrt = FIELD_PREP(_msk, _val); \ + BUILD_BUG_ON(!!(_msk & _en)); \ + if (_cond) \ + _wrt |= _en; \ + else \ + _wrt &= ~_en; \ + rtw89_mac_txpwr_write32_mask(rtwdev, RTW89_PHY_0, _reg, \ + _msk | _en, _wrt); \ +} while (0) + + switch (arg.ctrl_all_time) { + case 0xffff: + val = 0; + break; + default: + val = arg.all_time.data; + break; + } + + __write_ctrl(R_AX_PWR_RATE_CTRL, B_AX_FORCE_PWR_BY_RATE_VALUE_MASK, + val, B_AX_FORCE_PWR_BY_RATE_EN, + arg.ctrl_all_time != 0xffff); + + switch (arg.ctrl_gnt_bt) { + case 0xffff: + val = 0; + break; + default: + val = arg.gnt_bt.data; + break; + } + + __write_ctrl(R_AX_PWR_COEXT_CTRL, B_AX_TXAGC_BT_MASK, val, + B_AX_TXAGC_BT_EN, arg.ctrl_gnt_bt != 0xffff); + +#undef __write_ctrl +} + +static +s8 rtw8852b_btc_get_bt_rssi(struct rtw89_dev *rtwdev, s8 val) +{ + return clamp_t(s8, val, -100, 0) + 100; +} + +static +void rtw8852b_btc_update_bt_cnt(struct rtw89_dev *rtwdev) +{ + /* Feature move to firmware */ +} + +static void rtw8852b_btc_wl_s1_standby(struct rtw89_dev *rtwdev, bool state) +{ + rtw89_write_rf(rtwdev, RF_PATH_B, RR_LUTWE, RFREG_MASK, 0x80000); + rtw89_write_rf(rtwdev, RF_PATH_B, RR_LUTWA, RFREG_MASK, 0x1); + rtw89_write_rf(rtwdev, RF_PATH_B, RR_LUTWD1, RFREG_MASK, 0x31); + + /* set WL standby = Rx for GNT_BT_Tx = 1->0 settle issue */ + if (state) + rtw89_write_rf(rtwdev, RF_PATH_B, RR_LUTWD0, RFREG_MASK, 0x579); + else + rtw89_write_rf(rtwdev, RF_PATH_B, RR_LUTWD0, RFREG_MASK, 0x20); + + rtw89_write_rf(rtwdev, RF_PATH_B, RR_LUTWE, RFREG_MASK, 0x0); +} + +static void rtw8852b_btc_set_wl_rx_gain(struct rtw89_dev *rtwdev, u32 level) +{ +} + static int rtw8852b_mac_enable_bb_rf(struct rtw89_dev *rtwdev) { int ret; @@ -1481,8 +1774,20 @@ static const struct rtw89_chip_ops rtw8852b_chip_ops = { .set_txpwr_ctrl = rtw8852b_set_txpwr_ctrl, .init_txpwr_unit = rtw8852b_init_txpwr_unit, .get_thermal = rtw8852b_get_thermal, + .ctrl_btg = rtw8852b_ctrl_btg, + .bb_ctrl_btc_preagc = rtw8852b_bb_ctrl_btc_preagc, .pwr_on_func = rtw8852b_pwr_on_func, .pwr_off_func = rtw8852b_pwr_off_func, + + .btc_set_rfe = rtw8852b_btc_set_rfe, + .btc_init_cfg = rtw8852b_btc_init_cfg, + .btc_set_wl_pri = rtw8852b_btc_set_wl_pri, + .btc_set_wl_txpwr_ctrl = rtw8852b_btc_set_wl_txpwr_ctrl, + .btc_get_bt_rssi = rtw8852b_btc_get_bt_rssi, + .btc_update_bt_cnt = rtw8852b_btc_update_bt_cnt, + .btc_wl_s1_standby = rtw8852b_btc_wl_s1_standby, + .btc_set_wl_rx_gain = rtw8852b_btc_set_wl_rx_gain, + .btc_set_policy = rtw89_btc_set_policy, }; const struct rtw89_chip_info rtw8852b_chip_info = { From bf958f76cf97663d79b4f90a08d38c5b9bb56082 Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Sun, 9 Oct 2022 20:54:00 +0800 Subject: [PATCH 0123/2002] wifi: rtw89: 8852b: add chip_ops to query PPDU Add to parse PPDU to get frequency and RSSI of received packets. Signed-off-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20221009125403.19662-7-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/rtw8852b.c | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852b.c b/drivers/net/wireless/realtek/rtw89/rtw8852b.c index ee5a29f35db2..abb35553a2b0 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852b.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852b.c @@ -1707,6 +1707,37 @@ static void rtw8852b_btc_set_wl_rx_gain(struct rtw89_dev *rtwdev, u32 level) { } +static void rtw8852b_fill_freq_with_ppdu(struct rtw89_dev *rtwdev, + struct rtw89_rx_phy_ppdu *phy_ppdu, + struct ieee80211_rx_status *status) +{ + u16 chan = phy_ppdu->chan_idx; + u8 band; + + if (chan == 0) + return; + + band = chan <= 14 ? NL80211_BAND_2GHZ : NL80211_BAND_5GHZ; + status->freq = ieee80211_channel_to_frequency(chan, band); + status->band = band; +} + +static void rtw8852b_query_ppdu(struct rtw89_dev *rtwdev, + struct rtw89_rx_phy_ppdu *phy_ppdu, + struct ieee80211_rx_status *status) +{ + u8 path; + u8 *rx_power = phy_ppdu->rssi; + + status->signal = RTW89_RSSI_RAW_TO_DBM(max(rx_power[RF_PATH_A], rx_power[RF_PATH_B])); + for (path = 0; path < rtwdev->chip->rf_path_num; path++) { + status->chains |= BIT(path); + status->chain_signal[path] = RTW89_RSSI_RAW_TO_DBM(rx_power[path]); + } + if (phy_ppdu->valid) + rtw8852b_fill_freq_with_ppdu(rtwdev, phy_ppdu, status); +} + static int rtw8852b_mac_enable_bb_rf(struct rtw89_dev *rtwdev) { int ret; @@ -1775,6 +1806,7 @@ static const struct rtw89_chip_ops rtw8852b_chip_ops = { .init_txpwr_unit = rtw8852b_init_txpwr_unit, .get_thermal = rtw8852b_get_thermal, .ctrl_btg = rtw8852b_ctrl_btg, + .query_ppdu = rtw8852b_query_ppdu, .bb_ctrl_btc_preagc = rtw8852b_bb_ctrl_btc_preagc, .pwr_on_func = rtw8852b_pwr_on_func, .pwr_off_func = rtw8852b_pwr_off_func, From 8915a256538d0e81fe02c5f68368e5787df261d5 Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Sun, 9 Oct 2022 20:54:01 +0800 Subject: [PATCH 0124/2002] wifi: rtw89: 8852b: add chip_ops to configure TX/RX path To support variant models, such as 1x1 or 1T2R, we need this chip_ops to change the path accordingly. Signed-off-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20221009125403.19662-8-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/reg.h | 4 + drivers/net/wireless/realtek/rtw89/rtw8852b.c | 112 ++++++++++++++++++ drivers/net/wireless/realtek/rtw89/rtw8852b.h | 3 + 3 files changed, 119 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/reg.h b/drivers/net/wireless/realtek/rtw89/reg.h index 570fb1aee80c..5482e32a72d5 100644 --- a/drivers/net/wireless/realtek/rtw89/reg.h +++ b/drivers/net/wireless/realtek/rtw89/reg.h @@ -3666,6 +3666,8 @@ #define B_P0_RFMODE_MUX GENMASK(11, 4) #define R_P0_RFMODE_ORI_RX 0x12AC #define B_P0_RFMODE_ORI_RX_ALL GENMASK(23, 12) +#define R_P0_RFMODE_FTM_RX 0x12B0 +#define B_P0_RFMODE_FTM_RX GENMASK(11, 0) #define R_P0_NRBW 0x12B8 #define B_P0_NRBW_DBG BIT(30) #define R_S0_RXDC 0x12D4 @@ -3779,6 +3781,8 @@ #define B_P1_RFMODE_MUX GENMASK(11, 4) #define R_P1_RFMODE_ORI_RX 0x32AC #define B_P1_RFMODE_ORI_RX_ALL GENMASK(23, 12) +#define R_P1_RFMODE_FTM_RX 0x32B0 +#define B_P1_RFMODE_FTM_RX GENMASK(11, 0) #define R_P1_DBGMOD 0x32B8 #define B_P1_DBGMOD_ON BIT(30) #define R_S1_RXDC 0x32D4 diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852b.c b/drivers/net/wireless/realtek/rtw89/rtw8852b.c index abb35553a2b0..a5156d7aca5b 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852b.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852b.c @@ -1484,6 +1484,117 @@ static void rtw8852b_ctrl_btg(struct rtw89_dev *rtwdev, bool btg) } } +void rtw8852b_bb_ctrl_rx_path(struct rtw89_dev *rtwdev, + enum rtw89_rf_path_bit rx_path) +{ + const struct rtw89_chan *chan = rtw89_chan_get(rtwdev, RTW89_SUB_ENTITY_0); + u32 rst_mask0; + u32 rst_mask1; + + if (rx_path == RF_A) { + rtw89_phy_write32_mask(rtwdev, R_CHBW_MOD_V1, B_ANT_RX_SEG0, 1); + rtw89_phy_write32_mask(rtwdev, R_FC0_BW_V1, B_ANT_RX_1RCCA_SEG0, 1); + rtw89_phy_write32_mask(rtwdev, R_FC0_BW_V1, B_ANT_RX_1RCCA_SEG1, 1); + rtw89_phy_write32_mask(rtwdev, R_RXHT_MCS_LIMIT, B_RXHT_MCS_LIMIT, 0); + rtw89_phy_write32_mask(rtwdev, R_RXVHT_MCS_LIMIT, B_RXVHT_MCS_LIMIT, 0); + rtw89_phy_write32_mask(rtwdev, R_RXHE, B_RXHE_USER_MAX, 4); + rtw89_phy_write32_mask(rtwdev, R_RXHE, B_RXHE_MAX_NSS, 0); + rtw89_phy_write32_mask(rtwdev, R_RXHE, B_RXHETB_MAX_NSS, 0); + } else if (rx_path == RF_B) { + rtw89_phy_write32_mask(rtwdev, R_CHBW_MOD_V1, B_ANT_RX_SEG0, 2); + rtw89_phy_write32_mask(rtwdev, R_FC0_BW_V1, B_ANT_RX_1RCCA_SEG0, 2); + rtw89_phy_write32_mask(rtwdev, R_FC0_BW_V1, B_ANT_RX_1RCCA_SEG1, 2); + rtw89_phy_write32_mask(rtwdev, R_RXHT_MCS_LIMIT, B_RXHT_MCS_LIMIT, 0); + rtw89_phy_write32_mask(rtwdev, R_RXVHT_MCS_LIMIT, B_RXVHT_MCS_LIMIT, 0); + rtw89_phy_write32_mask(rtwdev, R_RXHE, B_RXHE_USER_MAX, 4); + rtw89_phy_write32_mask(rtwdev, R_RXHE, B_RXHE_MAX_NSS, 0); + rtw89_phy_write32_mask(rtwdev, R_RXHE, B_RXHETB_MAX_NSS, 0); + } else if (rx_path == RF_AB) { + rtw89_phy_write32_mask(rtwdev, R_CHBW_MOD_V1, B_ANT_RX_SEG0, 3); + rtw89_phy_write32_mask(rtwdev, R_FC0_BW_V1, B_ANT_RX_1RCCA_SEG0, 3); + rtw89_phy_write32_mask(rtwdev, R_FC0_BW_V1, B_ANT_RX_1RCCA_SEG1, 3); + rtw89_phy_write32_mask(rtwdev, R_RXHT_MCS_LIMIT, B_RXHT_MCS_LIMIT, 1); + rtw89_phy_write32_mask(rtwdev, R_RXVHT_MCS_LIMIT, B_RXVHT_MCS_LIMIT, 1); + rtw89_phy_write32_mask(rtwdev, R_RXHE, B_RXHE_USER_MAX, 4); + rtw89_phy_write32_mask(rtwdev, R_RXHE, B_RXHE_MAX_NSS, 1); + rtw89_phy_write32_mask(rtwdev, R_RXHE, B_RXHETB_MAX_NSS, 1); + } + + rtw8852b_set_gain_offset(rtwdev, chan->subband_type, RTW89_PHY_0); + + if (chan->band_type == RTW89_BAND_2G && + (rx_path == RF_B || rx_path == RF_AB)) + rtw8852b_ctrl_btg(rtwdev, true); + else + rtw8852b_ctrl_btg(rtwdev, false); + + rst_mask0 = B_P0_TXPW_RSTB_MANON | B_P0_TXPW_RSTB_TSSI; + rst_mask1 = B_P1_TXPW_RSTB_MANON | B_P1_TXPW_RSTB_TSSI; + if (rx_path == RF_A) { + rtw89_phy_write32_mask(rtwdev, R_P0_TXPW_RSTB, rst_mask0, 1); + rtw89_phy_write32_mask(rtwdev, R_P0_TXPW_RSTB, rst_mask0, 3); + } else { + rtw89_phy_write32_mask(rtwdev, R_P1_TXPW_RSTB, rst_mask1, 1); + rtw89_phy_write32_mask(rtwdev, R_P1_TXPW_RSTB, rst_mask1, 3); + } +} + +static void rtw8852b_bb_ctrl_rf_mode_rx_path(struct rtw89_dev *rtwdev, + enum rtw89_rf_path_bit rx_path) +{ + if (rx_path == RF_A) { + rtw89_phy_write32_mask(rtwdev, R_P0_RFMODE, + B_P0_RFMODE_ORI_TXRX_FTM_TX, 0x1233312); + rtw89_phy_write32_mask(rtwdev, R_P0_RFMODE_FTM_RX, + B_P0_RFMODE_FTM_RX, 0x333); + rtw89_phy_write32_mask(rtwdev, R_P1_RFMODE, + B_P1_RFMODE_ORI_TXRX_FTM_TX, 0x1111111); + rtw89_phy_write32_mask(rtwdev, R_P1_RFMODE_FTM_RX, + B_P1_RFMODE_FTM_RX, 0x111); + } else if (rx_path == RF_B) { + rtw89_phy_write32_mask(rtwdev, R_P0_RFMODE, + B_P0_RFMODE_ORI_TXRX_FTM_TX, 0x1111111); + rtw89_phy_write32_mask(rtwdev, R_P0_RFMODE_FTM_RX, + B_P0_RFMODE_FTM_RX, 0x111); + rtw89_phy_write32_mask(rtwdev, R_P1_RFMODE, + B_P1_RFMODE_ORI_TXRX_FTM_TX, 0x1233312); + rtw89_phy_write32_mask(rtwdev, R_P1_RFMODE_FTM_RX, + B_P1_RFMODE_FTM_RX, 0x333); + } else if (rx_path == RF_AB) { + rtw89_phy_write32_mask(rtwdev, R_P0_RFMODE, + B_P0_RFMODE_ORI_TXRX_FTM_TX, 0x1233312); + rtw89_phy_write32_mask(rtwdev, R_P0_RFMODE_FTM_RX, + B_P0_RFMODE_FTM_RX, 0x333); + rtw89_phy_write32_mask(rtwdev, R_P1_RFMODE, + B_P1_RFMODE_ORI_TXRX_FTM_TX, 0x1233312); + rtw89_phy_write32_mask(rtwdev, R_P1_RFMODE_FTM_RX, + B_P1_RFMODE_FTM_RX, 0x333); + } +} + +static void rtw8852b_bb_cfg_txrx_path(struct rtw89_dev *rtwdev) +{ + struct rtw89_hal *hal = &rtwdev->hal; + enum rtw89_rf_path_bit rx_path = hal->antenna_rx ? hal->antenna_rx : RF_AB; + + rtw8852b_bb_ctrl_rx_path(rtwdev, rx_path); + rtw8852b_bb_ctrl_rf_mode_rx_path(rtwdev, rx_path); + + if (rtwdev->hal.rx_nss == 1) { + rtw89_phy_write32_mask(rtwdev, R_RXHT_MCS_LIMIT, B_RXHT_MCS_LIMIT, 0); + rtw89_phy_write32_mask(rtwdev, R_RXVHT_MCS_LIMIT, B_RXVHT_MCS_LIMIT, 0); + rtw89_phy_write32_mask(rtwdev, R_RXHE, B_RXHE_MAX_NSS, 0); + rtw89_phy_write32_mask(rtwdev, R_RXHE, B_RXHETB_MAX_NSS, 0); + } else { + rtw89_phy_write32_mask(rtwdev, R_RXHT_MCS_LIMIT, B_RXHT_MCS_LIMIT, 1); + rtw89_phy_write32_mask(rtwdev, R_RXVHT_MCS_LIMIT, B_RXVHT_MCS_LIMIT, 1); + rtw89_phy_write32_mask(rtwdev, R_RXHE, B_RXHE_MAX_NSS, 1); + rtw89_phy_write32_mask(rtwdev, R_RXHE, B_RXHETB_MAX_NSS, 1); + } + + rtw89_phy_write32_idx(rtwdev, R_MAC_SEL, B_MAC_SEL_MOD, 0x0, RTW89_PHY_0); +} + static u8 rtw8852b_get_thermal(struct rtw89_dev *rtwdev, enum rtw89_rf_path rf_path) { if (rtwdev->is_tssi_mode[rf_path]) { @@ -1808,6 +1919,7 @@ static const struct rtw89_chip_ops rtw8852b_chip_ops = { .ctrl_btg = rtw8852b_ctrl_btg, .query_ppdu = rtw8852b_query_ppdu, .bb_ctrl_btc_preagc = rtw8852b_bb_ctrl_btc_preagc, + .cfg_txrx_path = rtw8852b_bb_cfg_txrx_path, .pwr_on_func = rtw8852b_pwr_on_func, .pwr_off_func = rtw8852b_pwr_off_func, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852b.h b/drivers/net/wireless/realtek/rtw89/rtw8852b.h index bc0a383c4a39..33f621014e49 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852b.h +++ b/drivers/net/wireless/realtek/rtw89/rtw8852b.h @@ -87,4 +87,7 @@ struct rtw8852b_efuse { extern const struct rtw89_chip_info rtw8852b_chip_info; +void rtw8852b_bb_ctrl_rx_path(struct rtw89_dev *rtwdev, + enum rtw89_rf_path_bit rx_path); + #endif From 572fd2ab377b123e52b442d015f9605409a21ad9 Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Sun, 9 Oct 2022 20:54:02 +0800 Subject: [PATCH 0125/2002] wifi: rtw89: 8852b: add functions to control BB to assist RF calibrations When we are going to do RF calibrations, they need BB helpers to control TX PLCP, power, path and mode. Also, it they need helpers to backup and restore some registers before and after RF calibrations. Then, use flow of RF calibrations will be like backup registers, configure calibration, configure TX parameters, measure calibration result, and finally restore registers. Signed-off-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20221009125403.19662-9-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/rtw8852b.c | 288 ++++++++++++++++++ drivers/net/wireless/realtek/rtw89/rtw8852b.h | 44 +++ 2 files changed, 332 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852b.c b/drivers/net/wireless/realtek/rtw89/rtw8852b.c index a5156d7aca5b..290d83cb83d5 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852b.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852b.c @@ -56,6 +56,129 @@ static const struct rtw89_dle_mem rtw8852b_dle_mem_pcie[] = { NULL}, }; +static const struct rtw89_reg3_def rtw8852b_pmac_ht20_mcs7_tbl[] = { + {0x4580, 0x0000ffff, 0x0}, + {0x4580, 0xffff0000, 0x0}, + {0x4584, 0x0000ffff, 0x0}, + {0x4584, 0xffff0000, 0x0}, + {0x4580, 0x0000ffff, 0x1}, + {0x4578, 0x00ffffff, 0x2018b}, + {0x4570, 0x03ffffff, 0x7}, + {0x4574, 0x03ffffff, 0x32407}, + {0x45b8, 0x00000010, 0x0}, + {0x45b8, 0x00000100, 0x0}, + {0x45b8, 0x00000080, 0x0}, + {0x45b8, 0x00000008, 0x0}, + {0x45a0, 0x0000ff00, 0x0}, + {0x45a0, 0xff000000, 0x1}, + {0x45a4, 0x0000ff00, 0x2}, + {0x45a4, 0xff000000, 0x3}, + {0x45b8, 0x00000020, 0x0}, + {0x4568, 0xe0000000, 0x0}, + {0x45b8, 0x00000002, 0x1}, + {0x456c, 0xe0000000, 0x0}, + {0x45b4, 0x00006000, 0x0}, + {0x45b4, 0x00001800, 0x1}, + {0x45b8, 0x00000040, 0x0}, + {0x45b8, 0x00000004, 0x0}, + {0x45b8, 0x00000200, 0x0}, + {0x4598, 0xf8000000, 0x0}, + {0x45b8, 0x00100000, 0x0}, + {0x45a8, 0x00000fc0, 0x0}, + {0x45b8, 0x00200000, 0x0}, + {0x45b0, 0x00000038, 0x0}, + {0x45b0, 0x000001c0, 0x0}, + {0x45a0, 0x000000ff, 0x0}, + {0x45b8, 0x00400000, 0x0}, + {0x4590, 0x000007ff, 0x0}, + {0x45b0, 0x00000e00, 0x0}, + {0x45ac, 0x0000001f, 0x0}, + {0x45b8, 0x00800000, 0x0}, + {0x45a8, 0x0003f000, 0x0}, + {0x45b8, 0x01000000, 0x0}, + {0x45b0, 0x00007000, 0x0}, + {0x45b0, 0x00038000, 0x0}, + {0x45a0, 0x00ff0000, 0x0}, + {0x45b8, 0x02000000, 0x0}, + {0x4590, 0x003ff800, 0x0}, + {0x45b0, 0x001c0000, 0x0}, + {0x45ac, 0x000003e0, 0x0}, + {0x45b8, 0x04000000, 0x0}, + {0x45a8, 0x00fc0000, 0x0}, + {0x45b8, 0x08000000, 0x0}, + {0x45b0, 0x00e00000, 0x0}, + {0x45b0, 0x07000000, 0x0}, + {0x45a4, 0x000000ff, 0x0}, + {0x45b8, 0x10000000, 0x0}, + {0x4594, 0x000007ff, 0x0}, + {0x45b0, 0x38000000, 0x0}, + {0x45ac, 0x00007c00, 0x0}, + {0x45b8, 0x20000000, 0x0}, + {0x45a8, 0x3f000000, 0x0}, + {0x45b8, 0x40000000, 0x0}, + {0x45b4, 0x00000007, 0x0}, + {0x45b4, 0x00000038, 0x0}, + {0x45a4, 0x00ff0000, 0x0}, + {0x45b8, 0x80000000, 0x0}, + {0x4594, 0x003ff800, 0x0}, + {0x45b4, 0x000001c0, 0x0}, + {0x4598, 0xf8000000, 0x0}, + {0x45b8, 0x00100000, 0x0}, + {0x45a8, 0x00000fc0, 0x7}, + {0x45b8, 0x00200000, 0x0}, + {0x45b0, 0x00000038, 0x0}, + {0x45b0, 0x000001c0, 0x0}, + {0x45a0, 0x000000ff, 0x0}, + {0x45b4, 0x06000000, 0x0}, + {0x45b0, 0x00000007, 0x0}, + {0x45b8, 0x00080000, 0x0}, + {0x45a8, 0x0000003f, 0x0}, + {0x457c, 0xffe00000, 0x1}, + {0x4530, 0xffffffff, 0x0}, + {0x4588, 0x00003fff, 0x0}, + {0x4598, 0x000001ff, 0x0}, + {0x4534, 0xffffffff, 0x0}, + {0x4538, 0xffffffff, 0x0}, + {0x453c, 0xffffffff, 0x0}, + {0x4588, 0x0fffc000, 0x0}, + {0x4598, 0x0003fe00, 0x0}, + {0x4540, 0xffffffff, 0x0}, + {0x4544, 0xffffffff, 0x0}, + {0x4548, 0xffffffff, 0x0}, + {0x458c, 0x00003fff, 0x0}, + {0x4598, 0x07fc0000, 0x0}, + {0x454c, 0xffffffff, 0x0}, + {0x4550, 0xffffffff, 0x0}, + {0x4554, 0xffffffff, 0x0}, + {0x458c, 0x0fffc000, 0x0}, + {0x459c, 0x000001ff, 0x0}, + {0x4558, 0xffffffff, 0x0}, + {0x455c, 0xffffffff, 0x0}, + {0x4530, 0xffffffff, 0x4e790001}, + {0x4588, 0x00003fff, 0x0}, + {0x4598, 0x000001ff, 0x1}, + {0x4534, 0xffffffff, 0x0}, + {0x4538, 0xffffffff, 0x4b}, + {0x45ac, 0x38000000, 0x7}, + {0x4588, 0xf0000000, 0x0}, + {0x459c, 0x7e000000, 0x0}, + {0x45b8, 0x00040000, 0x0}, + {0x45b8, 0x00020000, 0x0}, + {0x4590, 0xffc00000, 0x0}, + {0x45b8, 0x00004000, 0x0}, + {0x4578, 0xff000000, 0x0}, + {0x45b8, 0x00000400, 0x0}, + {0x45b8, 0x00000800, 0x0}, + {0x45b8, 0x00001000, 0x0}, + {0x45b8, 0x00002000, 0x0}, + {0x45b4, 0x00018000, 0x0}, + {0x45ac, 0x07800000, 0x0}, + {0x45b4, 0x00000600, 0x2}, + {0x459c, 0x0001fe00, 0x80}, + {0x45ac, 0x00078000, 0x3}, + {0x459c, 0x01fe0000, 0x1}, +}; + static const struct rtw89_reg3_def rtw8852b_btc_preagc_en_defs[] = { {0x46D0, GENMASK(1, 0), 0x3}, {0x4790, GENMASK(1, 0), 0x3}, @@ -1435,6 +1558,171 @@ rtw8852b_init_txpwr_unit(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx) return 0; } +void rtw8852b_bb_set_plcp_tx(struct rtw89_dev *rtwdev) +{ + const struct rtw89_reg3_def *def = rtw8852b_pmac_ht20_mcs7_tbl; + u8 i; + + for (i = 0; i < ARRAY_SIZE(rtw8852b_pmac_ht20_mcs7_tbl); i++, def++) + rtw89_phy_write32_mask(rtwdev, def->addr, def->mask, def->data); +} + +static void rtw8852b_stop_pmac_tx(struct rtw89_dev *rtwdev, + struct rtw8852b_bb_pmac_info *tx_info, + enum rtw89_phy_idx idx) +{ + rtw89_debug(rtwdev, RTW89_DBG_TSSI, "PMAC Stop Tx"); + if (tx_info->mode == CONT_TX) + rtw89_phy_write32_idx(rtwdev, R_PMAC_TX_PRD, B_PMAC_CTX_EN, 0, idx); + else if (tx_info->mode == PKTS_TX) + rtw89_phy_write32_idx(rtwdev, R_PMAC_TX_PRD, B_PMAC_PTX_EN, 0, idx); +} + +static void rtw8852b_start_pmac_tx(struct rtw89_dev *rtwdev, + struct rtw8852b_bb_pmac_info *tx_info, + enum rtw89_phy_idx idx) +{ + enum rtw8852b_pmac_mode mode = tx_info->mode; + u32 pkt_cnt = tx_info->tx_cnt; + u16 period = tx_info->period; + + if (mode == CONT_TX && !tx_info->is_cck) { + rtw89_phy_write32_idx(rtwdev, R_PMAC_TX_PRD, B_PMAC_CTX_EN, 1, idx); + rtw89_debug(rtwdev, RTW89_DBG_TSSI, "PMAC CTx Start"); + } else if (mode == PKTS_TX) { + rtw89_phy_write32_idx(rtwdev, R_PMAC_TX_PRD, B_PMAC_PTX_EN, 1, idx); + rtw89_phy_write32_idx(rtwdev, R_PMAC_TX_PRD, + B_PMAC_TX_PRD_MSK, period, idx); + rtw89_phy_write32_idx(rtwdev, R_PMAC_TX_CNT, B_PMAC_TX_CNT_MSK, + pkt_cnt, idx); + rtw89_debug(rtwdev, RTW89_DBG_TSSI, "PMAC PTx Start"); + } + + rtw89_phy_write32_idx(rtwdev, R_PMAC_TX_CTRL, B_PMAC_TXEN_DIS, 1, idx); + rtw89_phy_write32_idx(rtwdev, R_PMAC_TX_CTRL, B_PMAC_TXEN_DIS, 0, idx); +} + +void rtw8852b_bb_set_pmac_tx(struct rtw89_dev *rtwdev, + struct rtw8852b_bb_pmac_info *tx_info, + enum rtw89_phy_idx idx) +{ + const struct rtw89_chan *chan = rtw89_chan_get(rtwdev, RTW89_SUB_ENTITY_0); + + if (!tx_info->en_pmac_tx) { + rtw8852b_stop_pmac_tx(rtwdev, tx_info, idx); + rtw89_phy_write32_idx(rtwdev, R_PD_CTRL, B_PD_HIT_DIS, 0, idx); + if (chan->band_type == RTW89_BAND_2G) + rtw89_phy_write32_clr(rtwdev, R_RXCCA, B_RXCCA_DIS); + return; + } + + rtw89_debug(rtwdev, RTW89_DBG_TSSI, "PMAC Tx Enable"); + + rtw89_phy_write32_idx(rtwdev, R_PMAC_GNT, B_PMAC_GNT_TXEN, 1, idx); + rtw89_phy_write32_idx(rtwdev, R_PMAC_GNT, B_PMAC_GNT_RXEN, 1, idx); + rtw89_phy_write32_idx(rtwdev, R_PMAC_RX_CFG1, B_PMAC_OPT1_MSK, 0x3f, idx); + rtw89_phy_write32_idx(rtwdev, R_RSTB_ASYNC, B_RSTB_ASYNC_ALL, 0, idx); + rtw89_phy_write32_idx(rtwdev, R_PD_CTRL, B_PD_HIT_DIS, 1, idx); + rtw89_phy_write32_set(rtwdev, R_RXCCA, B_RXCCA_DIS); + rtw89_phy_write32_idx(rtwdev, R_RSTB_ASYNC, B_RSTB_ASYNC_ALL, 1, idx); + + rtw8852b_start_pmac_tx(rtwdev, tx_info, idx); +} + +void rtw8852b_bb_set_pmac_pkt_tx(struct rtw89_dev *rtwdev, u8 enable, + u16 tx_cnt, u16 period, u16 tx_time, + enum rtw89_phy_idx idx) +{ + struct rtw8852b_bb_pmac_info tx_info = {0}; + + tx_info.en_pmac_tx = enable; + tx_info.is_cck = 0; + tx_info.mode = PKTS_TX; + tx_info.tx_cnt = tx_cnt; + tx_info.period = period; + tx_info.tx_time = tx_time; + + rtw8852b_bb_set_pmac_tx(rtwdev, &tx_info, idx); +} + +void rtw8852b_bb_set_power(struct rtw89_dev *rtwdev, s16 pwr_dbm, + enum rtw89_phy_idx idx) +{ + rtw89_debug(rtwdev, RTW89_DBG_TSSI, "PMAC CFG Tx PWR = %d", pwr_dbm); + + rtw89_phy_write32_idx(rtwdev, R_MAC_SEL, B_MAC_SEL_PWR_EN, 1, idx); + rtw89_phy_write32_idx(rtwdev, R_TXPWR, B_TXPWR_MSK, pwr_dbm, idx); +} + +void rtw8852b_bb_cfg_tx_path(struct rtw89_dev *rtwdev, u8 tx_path) +{ + rtw89_phy_write32_idx(rtwdev, R_MAC_SEL, B_MAC_SEL_MOD, 7, RTW89_PHY_0); + + rtw89_debug(rtwdev, RTW89_DBG_TSSI, "PMAC CFG Tx Path = %d", tx_path); + + if (tx_path == RF_PATH_A) { + rtw89_phy_write32_mask(rtwdev, R_TXPATH_SEL, B_TXPATH_SEL_MSK, 1); + rtw89_phy_write32_mask(rtwdev, R_TXNSS_MAP, B_TXNSS_MAP_MSK, 0); + } else if (tx_path == RF_PATH_B) { + rtw89_phy_write32_mask(rtwdev, R_TXPATH_SEL, B_TXPATH_SEL_MSK, 2); + rtw89_phy_write32_mask(rtwdev, R_TXNSS_MAP, B_TXNSS_MAP_MSK, 0); + } else if (tx_path == RF_PATH_AB) { + rtw89_phy_write32_mask(rtwdev, R_TXPATH_SEL, B_TXPATH_SEL_MSK, 3); + rtw89_phy_write32_mask(rtwdev, R_TXNSS_MAP, B_TXNSS_MAP_MSK, 4); + } else { + rtw89_debug(rtwdev, RTW89_DBG_TSSI, "Error Tx Path"); + } +} + +void rtw8852b_bb_tx_mode_switch(struct rtw89_dev *rtwdev, + enum rtw89_phy_idx idx, u8 mode) +{ + if (mode != 0) + return; + + rtw89_debug(rtwdev, RTW89_DBG_TSSI, "Tx mode switch"); + + rtw89_phy_write32_idx(rtwdev, R_PMAC_GNT, B_PMAC_GNT_TXEN, 0, idx); + rtw89_phy_write32_idx(rtwdev, R_PMAC_GNT, B_PMAC_GNT_RXEN, 0, idx); + rtw89_phy_write32_idx(rtwdev, R_PMAC_RX_CFG1, B_PMAC_OPT1_MSK, 0, idx); + rtw89_phy_write32_idx(rtwdev, R_PMAC_RXMOD, B_PMAC_RXMOD_MSK, 0, idx); + rtw89_phy_write32_idx(rtwdev, R_MAC_SEL, B_MAC_SEL_DPD_EN, 0, idx); + rtw89_phy_write32_idx(rtwdev, R_MAC_SEL, B_MAC_SEL_MOD, 0, idx); + rtw89_phy_write32_idx(rtwdev, R_MAC_SEL, B_MAC_SEL_PWR_EN, 0, idx); +} + +void rtw8852b_bb_backup_tssi(struct rtw89_dev *rtwdev, enum rtw89_phy_idx idx, + struct rtw8852b_bb_tssi_bak *bak) +{ + s32 tmp; + + bak->tx_path = rtw89_phy_read32_idx(rtwdev, R_TXPATH_SEL, B_TXPATH_SEL_MSK, idx); + bak->rx_path = rtw89_phy_read32_idx(rtwdev, R_CHBW_MOD_V1, B_ANT_RX_SEG0, idx); + bak->p0_rfmode = rtw89_phy_read32_idx(rtwdev, R_P0_RFMODE, MASKDWORD, idx); + bak->p0_rfmode_ftm = rtw89_phy_read32_idx(rtwdev, R_P0_RFMODE_FTM_RX, MASKDWORD, idx); + bak->p1_rfmode = rtw89_phy_read32_idx(rtwdev, R_P1_RFMODE, MASKDWORD, idx); + bak->p1_rfmode_ftm = rtw89_phy_read32_idx(rtwdev, R_P1_RFMODE_FTM_RX, MASKDWORD, idx); + tmp = rtw89_phy_read32_idx(rtwdev, R_TXPWR, B_TXPWR_MSK, idx); + bak->tx_pwr = sign_extend32(tmp, 8); +} + +void rtw8852b_bb_restore_tssi(struct rtw89_dev *rtwdev, enum rtw89_phy_idx idx, + const struct rtw8852b_bb_tssi_bak *bak) +{ + rtw89_phy_write32_idx(rtwdev, R_TXPATH_SEL, B_TXPATH_SEL_MSK, bak->tx_path, idx); + if (bak->tx_path == RF_AB) + rtw89_phy_write32_mask(rtwdev, R_TXNSS_MAP, B_TXNSS_MAP_MSK, 0x4); + else + rtw89_phy_write32_mask(rtwdev, R_TXNSS_MAP, B_TXNSS_MAP_MSK, 0x0); + rtw89_phy_write32_idx(rtwdev, R_CHBW_MOD_V1, B_ANT_RX_SEG0, bak->rx_path, idx); + rtw89_phy_write32_idx(rtwdev, R_MAC_SEL, B_MAC_SEL_PWR_EN, 1, idx); + rtw89_phy_write32_idx(rtwdev, R_P0_RFMODE, MASKDWORD, bak->p0_rfmode, idx); + rtw89_phy_write32_idx(rtwdev, R_P0_RFMODE_FTM_RX, MASKDWORD, bak->p0_rfmode_ftm, idx); + rtw89_phy_write32_idx(rtwdev, R_P1_RFMODE, MASKDWORD, bak->p1_rfmode, idx); + rtw89_phy_write32_idx(rtwdev, R_P1_RFMODE_FTM_RX, MASKDWORD, bak->p1_rfmode_ftm, idx); + rtw89_phy_write32_idx(rtwdev, R_TXPWR, B_TXPWR_MSK, bak->tx_pwr, idx); +} + static void rtw8852b_bb_ctrl_btc_preagc(struct rtw89_dev *rtwdev, bool bt_en) { rtw89_phy_write_reg3_tbl(rtwdev, bt_en ? &rtw8852b_btc_preagc_en_defs_tbl : diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852b.h b/drivers/net/wireless/realtek/rtw89/rtw8852b.h index 33f621014e49..4f9b3d476879 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852b.h +++ b/drivers/net/wireless/realtek/rtw89/rtw8852b.h @@ -10,6 +10,13 @@ #define RF_PATH_NUM_8852B 2 #define BB_PATH_NUM_8852B 2 +enum rtw8852b_pmac_mode { + NONE_TEST, + PKTS_TX, + PKTS_RX, + CONT_TX +}; + struct rtw8852b_u_efuse { u8 rsvd[0x88]; u8 mac_addr[ETH_ALEN]; @@ -85,9 +92,46 @@ struct rtw8852b_efuse { }; } __packed; +struct rtw8852b_bb_pmac_info { + u8 en_pmac_tx:1; + u8 is_cck:1; + u8 mode:3; + u8 rsvd:3; + u16 tx_cnt; + u16 period; + u16 tx_time; + u8 duty_cycle; +}; + +struct rtw8852b_bb_tssi_bak { + u8 tx_path; + u8 rx_path; + u32 p0_rfmode; + u32 p0_rfmode_ftm; + u32 p1_rfmode; + u32 p1_rfmode_ftm; + s16 tx_pwr; /* S9 */ +}; + extern const struct rtw89_chip_info rtw8852b_chip_info; +void rtw8852b_bb_set_plcp_tx(struct rtw89_dev *rtwdev); +void rtw8852b_bb_set_pmac_tx(struct rtw89_dev *rtwdev, + struct rtw8852b_bb_pmac_info *tx_info, + enum rtw89_phy_idx idx); +void rtw8852b_bb_set_pmac_pkt_tx(struct rtw89_dev *rtwdev, u8 enable, + u16 tx_cnt, u16 period, u16 tx_time, + enum rtw89_phy_idx idx); +void rtw8852b_bb_set_power(struct rtw89_dev *rtwdev, s16 pwr_dbm, + enum rtw89_phy_idx idx); +void rtw8852b_bb_cfg_tx_path(struct rtw89_dev *rtwdev, u8 tx_path); void rtw8852b_bb_ctrl_rx_path(struct rtw89_dev *rtwdev, enum rtw89_rf_path_bit rx_path); +void rtw8852b_bb_tx_mode_switch(struct rtw89_dev *rtwdev, + enum rtw89_phy_idx idx, u8 mode); +void rtw8852b_bb_backup_tssi(struct rtw89_dev *rtwdev, enum rtw89_phy_idx idx, + struct rtw8852b_bb_tssi_bak *bak); +void rtw8852b_bb_restore_tssi(struct rtw89_dev *rtwdev, enum rtw89_phy_idx idx, + const struct rtw8852b_bb_tssi_bak *bak); #endif From b8fe87b816851d08a31c7c9589855c8535672299 Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Sun, 9 Oct 2022 20:54:03 +0800 Subject: [PATCH 0126/2002] wifi: rtw89: 8852b: add basic attributes of chip_info Add 8852b specific constant tables and basic attributes containing common chip_ops, firmware name, supported TX/RX NSS, number of CAM, coexistence version, control register set, and so on. Signed-off-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20221009125403.19662-10-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/rtw8852b.c | 235 ++++++++++++++++++ 1 file changed, 235 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852b.c b/drivers/net/wireless/realtek/rtw89/rtw8852b.c index 290d83cb83d5..0918b75ab1d9 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852b.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852b.c @@ -217,6 +217,150 @@ static const struct rtw89_reg3_def rtw8852b_btc_preagc_dis_defs[] = { static DECLARE_PHY_REG3_TBL(rtw8852b_btc_preagc_dis_defs); +static const u32 rtw8852b_h2c_regs[RTW89_H2CREG_MAX] = { + R_AX_H2CREG_DATA0, R_AX_H2CREG_DATA1, R_AX_H2CREG_DATA2, + R_AX_H2CREG_DATA3 +}; + +static const u32 rtw8852b_c2h_regs[RTW89_C2HREG_MAX] = { + R_AX_C2HREG_DATA0, R_AX_C2HREG_DATA1, R_AX_C2HREG_DATA2, + R_AX_C2HREG_DATA3 +}; + +static const struct rtw89_page_regs rtw8852b_page_regs = { + .hci_fc_ctrl = R_AX_HCI_FC_CTRL, + .ch_page_ctrl = R_AX_CH_PAGE_CTRL, + .ach_page_ctrl = R_AX_ACH0_PAGE_CTRL, + .ach_page_info = R_AX_ACH0_PAGE_INFO, + .pub_page_info3 = R_AX_PUB_PAGE_INFO3, + .pub_page_ctrl1 = R_AX_PUB_PAGE_CTRL1, + .pub_page_ctrl2 = R_AX_PUB_PAGE_CTRL2, + .pub_page_info1 = R_AX_PUB_PAGE_INFO1, + .pub_page_info2 = R_AX_PUB_PAGE_INFO2, + .wp_page_ctrl1 = R_AX_WP_PAGE_CTRL1, + .wp_page_ctrl2 = R_AX_WP_PAGE_CTRL2, + .wp_page_info1 = R_AX_WP_PAGE_INFO1, +}; + +static const struct rtw89_reg_def rtw8852b_dcfo_comp = { + R_DCFO_COMP_S0, B_DCFO_COMP_S0_MSK +}; + +static const struct rtw89_imr_info rtw8852b_imr_info = { + .wdrls_imr_set = B_AX_WDRLS_IMR_SET, + .wsec_imr_reg = R_AX_SEC_DEBUG, + .wsec_imr_set = B_AX_IMR_ERROR, + .mpdu_tx_imr_set = 0, + .mpdu_rx_imr_set = 0, + .sta_sch_imr_set = B_AX_STA_SCHEDULER_IMR_SET, + .txpktctl_imr_b0_reg = R_AX_TXPKTCTL_ERR_IMR_ISR, + .txpktctl_imr_b0_clr = B_AX_TXPKTCTL_IMR_B0_CLR, + .txpktctl_imr_b0_set = B_AX_TXPKTCTL_IMR_B0_SET, + .txpktctl_imr_b1_reg = R_AX_TXPKTCTL_ERR_IMR_ISR_B1, + .txpktctl_imr_b1_clr = B_AX_TXPKTCTL_IMR_B1_CLR, + .txpktctl_imr_b1_set = B_AX_TXPKTCTL_IMR_B1_SET, + .wde_imr_clr = B_AX_WDE_IMR_CLR, + .wde_imr_set = B_AX_WDE_IMR_SET, + .ple_imr_clr = B_AX_PLE_IMR_CLR, + .ple_imr_set = B_AX_PLE_IMR_SET, + .host_disp_imr_clr = B_AX_HOST_DISP_IMR_CLR, + .host_disp_imr_set = B_AX_HOST_DISP_IMR_SET, + .cpu_disp_imr_clr = B_AX_CPU_DISP_IMR_CLR, + .cpu_disp_imr_set = B_AX_CPU_DISP_IMR_SET, + .other_disp_imr_clr = B_AX_OTHER_DISP_IMR_CLR, + .other_disp_imr_set = 0, + .bbrpt_com_err_imr_reg = R_AX_BBRPT_COM_ERR_IMR_ISR, + .bbrpt_chinfo_err_imr_reg = R_AX_BBRPT_CHINFO_ERR_IMR_ISR, + .bbrpt_err_imr_set = 0, + .bbrpt_dfs_err_imr_reg = R_AX_BBRPT_DFS_ERR_IMR_ISR, + .ptcl_imr_clr = B_AX_PTCL_IMR_CLR_ALL, + .ptcl_imr_set = B_AX_PTCL_IMR_SET, + .cdma_imr_0_reg = R_AX_DLE_CTRL, + .cdma_imr_0_clr = B_AX_DLE_IMR_CLR, + .cdma_imr_0_set = B_AX_DLE_IMR_SET, + .cdma_imr_1_reg = 0, + .cdma_imr_1_clr = 0, + .cdma_imr_1_set = 0, + .phy_intf_imr_reg = R_AX_PHYINFO_ERR_IMR, + .phy_intf_imr_clr = 0, + .phy_intf_imr_set = 0, + .rmac_imr_reg = R_AX_RMAC_ERR_ISR, + .rmac_imr_clr = B_AX_RMAC_IMR_CLR, + .rmac_imr_set = B_AX_RMAC_IMR_SET, + .tmac_imr_reg = R_AX_TMAC_ERR_IMR_ISR, + .tmac_imr_clr = B_AX_TMAC_IMR_CLR, + .tmac_imr_set = B_AX_TMAC_IMR_SET, +}; + +static const struct rtw89_rrsr_cfgs rtw8852b_rrsr_cfgs = { + .ref_rate = {R_AX_TRXPTCL_RRSR_CTL_0, B_AX_WMAC_RESP_REF_RATE_SEL, 0}, + .rsc = {R_AX_TRXPTCL_RRSR_CTL_0, B_AX_WMAC_RESP_RSC_MASK, 2}, +}; + +static const struct rtw89_dig_regs rtw8852b_dig_regs = { + .seg0_pd_reg = R_SEG0R_PD_V1, + .pd_lower_bound_mask = B_SEG0R_PD_LOWER_BOUND_MSK, + .pd_spatial_reuse_en = B_SEG0R_PD_SPATIAL_REUSE_EN_MSK_V1, + .p0_lna_init = {R_PATH0_LNA_INIT_V1, B_PATH0_LNA_INIT_IDX_MSK}, + .p1_lna_init = {R_PATH1_LNA_INIT_V1, B_PATH1_LNA_INIT_IDX_MSK}, + .p0_tia_init = {R_PATH0_TIA_INIT_V1, B_PATH0_TIA_INIT_IDX_MSK_V1}, + .p1_tia_init = {R_PATH1_TIA_INIT_V1, B_PATH1_TIA_INIT_IDX_MSK_V1}, + .p0_rxb_init = {R_PATH0_RXB_INIT_V1, B_PATH0_RXB_INIT_IDX_MSK_V1}, + .p1_rxb_init = {R_PATH1_RXB_INIT_V1, B_PATH1_RXB_INIT_IDX_MSK_V1}, + .p0_p20_pagcugc_en = {R_PATH0_P20_FOLLOW_BY_PAGCUGC_V2, + B_PATH0_P20_FOLLOW_BY_PAGCUGC_EN_MSK}, + .p0_s20_pagcugc_en = {R_PATH0_S20_FOLLOW_BY_PAGCUGC_V2, + B_PATH0_S20_FOLLOW_BY_PAGCUGC_EN_MSK}, + .p1_p20_pagcugc_en = {R_PATH1_P20_FOLLOW_BY_PAGCUGC_V2, + B_PATH1_P20_FOLLOW_BY_PAGCUGC_EN_MSK}, + .p1_s20_pagcugc_en = {R_PATH1_S20_FOLLOW_BY_PAGCUGC_V2, + B_PATH1_S20_FOLLOW_BY_PAGCUGC_EN_MSK}, +}; + +static const struct rtw89_btc_rf_trx_para rtw89_btc_8852b_rf_ul[] = { + {15, 0, 0, 7}, /* 0 -> original */ + {15, 2, 0, 7}, /* 1 -> for BT-connected ACI issue && BTG co-rx */ + {15, 0, 0, 7}, /* 2 ->reserved for shared-antenna */ + {15, 0, 0, 7}, /* 3- >reserved for shared-antenna */ + {15, 0, 0, 7}, /* 4 ->reserved for shared-antenna */ + {15, 0, 0, 7}, /* the below id is for non-shared-antenna free-run */ + {6, 1, 0, 7}, + {13, 1, 0, 7}, + {13, 1, 0, 7} +}; + +static const struct rtw89_btc_rf_trx_para rtw89_btc_8852b_rf_dl[] = { + {15, 0, 0, 7}, /* 0 -> original */ + {15, 2, 0, 7}, /* 1 -> reserved for shared-antenna */ + {15, 0, 0, 7}, /* 2 ->reserved for shared-antenna */ + {15, 0, 0, 7}, /* 3- >reserved for shared-antenna */ + {15, 0, 0, 7}, /* 4 ->reserved for shared-antenna */ + {15, 0, 0, 7}, /* the below id is for non-shared-antenna free-run */ + {15, 1, 0, 7}, + {15, 1, 0, 7}, + {15, 1, 0, 7} +}; + +static const struct rtw89_btc_fbtc_mreg rtw89_btc_8852b_mon_reg[] = { + RTW89_DEF_FBTC_MREG(REG_MAC, 4, 0xda24), + RTW89_DEF_FBTC_MREG(REG_MAC, 4, 0xda28), + RTW89_DEF_FBTC_MREG(REG_MAC, 4, 0xda2c), + RTW89_DEF_FBTC_MREG(REG_MAC, 4, 0xda30), + RTW89_DEF_FBTC_MREG(REG_MAC, 4, 0xda4c), + RTW89_DEF_FBTC_MREG(REG_MAC, 4, 0xda10), + RTW89_DEF_FBTC_MREG(REG_MAC, 4, 0xda20), + RTW89_DEF_FBTC_MREG(REG_MAC, 4, 0xda34), + RTW89_DEF_FBTC_MREG(REG_MAC, 4, 0xcef4), + RTW89_DEF_FBTC_MREG(REG_MAC, 4, 0x8424), + RTW89_DEF_FBTC_MREG(REG_MAC, 4, 0xd200), + RTW89_DEF_FBTC_MREG(REG_MAC, 4, 0xd220), + RTW89_DEF_FBTC_MREG(REG_BB, 4, 0x980), + RTW89_DEF_FBTC_MREG(REG_BT_MODEM, 4, 0x178), +}; + +static const u8 rtw89_btc_8852b_wl_rssi_thres[BTC_WL_RSSI_THMAX] = {70, 60, 50, 40}; +static const u8 rtw89_btc_8852b_bt_rssi_thres[BTC_BT_RSSI_THMAX] = {50, 40, 30, 20}; + static int rtw8852b_pwr_on_func(struct rtw89_dev *rtwdev) { u32 val32; @@ -2195,10 +2339,13 @@ static const struct rtw89_chip_ops rtw8852b_chip_ops = { .disable_bb_rf = rtw8852b_mac_disable_bb_rf, .bb_reset = rtw8852b_bb_reset, .bb_sethw = rtw8852b_bb_sethw, + .read_rf = rtw89_phy_read_rf_v1, + .write_rf = rtw89_phy_write_rf_v1, .set_channel = rtw8852b_set_channel, .set_channel_help = rtw8852b_set_channel_help, .read_efuse = rtw8852b_read_efuse, .read_phycap = rtw8852b_read_phycap, + .fem_setup = NULL, .power_trim = rtw8852b_power_trim, .set_txpwr = rtw8852b_set_txpwr, .set_txpwr_ctrl = rtw8852b_set_txpwr_ctrl, @@ -2208,8 +2355,16 @@ static const struct rtw89_chip_ops rtw8852b_chip_ops = { .query_ppdu = rtw8852b_query_ppdu, .bb_ctrl_btc_preagc = rtw8852b_bb_ctrl_btc_preagc, .cfg_txrx_path = rtw8852b_bb_cfg_txrx_path, + .set_txpwr_ul_tb_offset = rtw8852b_set_txpwr_ul_tb_offset, .pwr_on_func = rtw8852b_pwr_on_func, .pwr_off_func = rtw8852b_pwr_off_func, + .fill_txdesc = rtw89_core_fill_txdesc, + .fill_txdesc_fwcmd = rtw89_core_fill_txdesc, + .cfg_ctrl_path = rtw89_mac_cfg_ctrl_path, + .mac_cfg_gnt = rtw89_mac_cfg_gnt, + .stop_sch_tx = rtw89_mac_stop_sch_tx, + .resume_sch_tx = rtw89_mac_resume_sch_tx, + .h2c_dctl_sec_cam = NULL, .btc_set_rfe = rtw8852b_btc_set_rfe, .btc_init_cfg = rtw8852b_btc_init_cfg, @@ -2225,12 +2380,46 @@ static const struct rtw89_chip_ops rtw8852b_chip_ops = { const struct rtw89_chip_info rtw8852b_chip_info = { .chip_id = RTL8852B, .ops = &rtw8852b_chip_ops, + .fw_name = "rtw89/rtw8852b_fw.bin", .fifo_size = 196608, .dle_scc_rsvd_size = 98304, + .max_amsdu_limit = 3500, + .dis_2g_40m_ul_ofdma = true, + .rsvd_ple_ofst = 0x2f800, .hfc_param_ini = rtw8852b_hfc_param_ini_pcie, .dle_mem = rtw8852b_dle_mem_pcie, + .rf_base_addr = {0xe000, 0xf000}, .pwr_on_seq = NULL, .pwr_off_seq = NULL, + .bb_table = &rtw89_8852b_phy_bb_table, + .bb_gain_table = &rtw89_8852b_phy_bb_gain_table, + .rf_table = {&rtw89_8852b_phy_radioa_table, + &rtw89_8852b_phy_radiob_table,}, + .nctl_table = &rtw89_8852b_phy_nctl_table, + .byr_table = &rtw89_8852b_byr_table, + .txpwr_lmt_2g = &rtw89_8852b_txpwr_lmt_2g, + .txpwr_lmt_5g = &rtw89_8852b_txpwr_lmt_5g, + .txpwr_lmt_ru_2g = &rtw89_8852b_txpwr_lmt_ru_2g, + .txpwr_lmt_ru_5g = &rtw89_8852b_txpwr_lmt_ru_5g, + .txpwr_factor_rf = 2, + .txpwr_factor_mac = 1, + .dig_table = NULL, + .dig_regs = &rtw8852b_dig_regs, + .tssi_dbw_table = NULL, + .support_chanctx_num = 0, + .support_bands = BIT(NL80211_BAND_2GHZ) | + BIT(NL80211_BAND_5GHZ), + .support_bw160 = false, + .hw_sec_hdr = false, + .rf_path_num = 2, + .tx_nss = 2, + .rx_nss = 2, + .acam_num = 128, + .bcam_num = 10, + .scam_num = 128, + .bacam_num = 2, + .bacam_dynamic_num = 4, + .bacam_v1 = false, .sec_ctrl_efuse_size = 4, .physical_efuse_size = 1216, .logical_efuse_size = 2048, @@ -2239,6 +2428,52 @@ const struct rtw89_chip_info rtw8852b_chip_info = { .dav_log_efuse_size = 16, .phycap_addr = 0x580, .phycap_size = 128, + .para_ver = 0, + .wlcx_desired = 0x05050000, + .btcx_desired = 0x5, + .scbd = 0x1, + .mailbox = 0x1, + .btc_fwinfo_buf = 1024, + + .fcxbtcrpt_ver = 1, + .fcxtdma_ver = 1, + .fcxslots_ver = 1, + .fcxcysta_ver = 2, + .fcxstep_ver = 2, + .fcxnullsta_ver = 1, + .fcxmreg_ver = 1, + .fcxgpiodbg_ver = 1, + .fcxbtver_ver = 1, + .fcxbtscan_ver = 1, + .fcxbtafh_ver = 1, + .fcxbtdevinfo_ver = 1, + .afh_guard_ch = 6, + .wl_rssi_thres = rtw89_btc_8852b_wl_rssi_thres, + .bt_rssi_thres = rtw89_btc_8852b_bt_rssi_thres, + .rssi_tol = 2, + .mon_reg_num = ARRAY_SIZE(rtw89_btc_8852b_mon_reg), + .mon_reg = rtw89_btc_8852b_mon_reg, + .rf_para_ulink_num = ARRAY_SIZE(rtw89_btc_8852b_rf_ul), + .rf_para_ulink = rtw89_btc_8852b_rf_ul, + .rf_para_dlink_num = ARRAY_SIZE(rtw89_btc_8852b_rf_dl), + .rf_para_dlink = rtw89_btc_8852b_rf_dl, + .ps_mode_supported = BIT(RTW89_PS_MODE_RFOFF) | + BIT(RTW89_PS_MODE_CLK_GATED) | + BIT(RTW89_PS_MODE_PWR_GATED), + .low_power_hci_modes = 0, + .h2c_cctl_func_id = H2C_FUNC_MAC_CCTLINFO_UD, + .hci_func_en_addr = R_AX_HCI_FUNC_EN, + .h2c_desc_size = sizeof(struct rtw89_txwd_body), + .txwd_body_size = sizeof(struct rtw89_txwd_body), + .h2c_ctrl_reg = R_AX_H2CREG_CTRL, + .h2c_regs = rtw8852b_h2c_regs, + .c2h_ctrl_reg = R_AX_C2HREG_CTRL, + .c2h_regs = rtw8852b_c2h_regs, + .page_regs = &rtw8852b_page_regs, + .dcfo_comp = &rtw8852b_dcfo_comp, + .dcfo_comp_sft = 3, + .imr_info = &rtw8852b_imr_info, + .rrsr_cfgs = &rtw8852b_rrsr_cfgs, .dma_ch_mask = BIT(RTW89_DMA_ACH4) | BIT(RTW89_DMA_ACH5) | BIT(RTW89_DMA_ACH6) | BIT(RTW89_DMA_ACH7) | BIT(RTW89_DMA_B1MG) | BIT(RTW89_DMA_B1HI), From d31ada3b511141f4b78cae5a05cc2dad887c40b7 Mon Sep 17 00:00:00 2001 From: David Vernet Date: Tue, 11 Oct 2022 11:52:55 -0500 Subject: [PATCH 0127/2002] selftests/bpf: Alphabetize DENYLISTs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The DENYLIST and DENYLIST.s390x files are used to specify testcases which should not be run on CI. Currently, testcases are appended to the end of these files as needed. This can make it a pain to resolve merge conflicts. This patch alphabetizes the DENYLIST files to ease this burden. Signed-off-by: David Vernet Acked-by: Daniel Müller Link: https://lore.kernel.org/r/20221011165255.774014-1-void@manifault.com Signed-off-by: Martin KaFai Lau --- tools/testing/selftests/bpf/DENYLIST | 3 +- tools/testing/selftests/bpf/DENYLIST.s390x | 41 +++++++++++----------- 2 files changed, 23 insertions(+), 21 deletions(-) diff --git a/tools/testing/selftests/bpf/DENYLIST b/tools/testing/selftests/bpf/DENYLIST index 939de574fc7f..f748f2c33b22 100644 --- a/tools/testing/selftests/bpf/DENYLIST +++ b/tools/testing/selftests/bpf/DENYLIST @@ -1,6 +1,7 @@ # TEMPORARY +# Alphabetical order get_stack_raw_tp # spams with kernel warnings until next bpf -> bpf-next merge -stacktrace_build_id_nmi stacktrace_build_id +stacktrace_build_id_nmi task_fd_query_rawtp varlen diff --git a/tools/testing/selftests/bpf/DENYLIST.s390x b/tools/testing/selftests/bpf/DENYLIST.s390x index beef1232a47a..520f12229b98 100644 --- a/tools/testing/selftests/bpf/DENYLIST.s390x +++ b/tools/testing/selftests/bpf/DENYLIST.s390x @@ -1,13 +1,18 @@ # TEMPORARY +# Alphabetical order atomics # attach(add): actual -524 <= expected 0 (trampoline) -bpf_iter_setsockopt # JIT does not support calling kernel function (kfunc) bloom_filter_map # failed to find kernel BTF type ID of '__x64_sys_getpgid': -3 (?) -bpf_tcp_ca # JIT does not support calling kernel function (kfunc) +bpf_cookie # failed to open_and_load program: -524 (trampoline) +bpf_iter_setsockopt # JIT does not support calling kernel function (kfunc) bpf_loop # attaches to __x64_sys_nanosleep bpf_mod_race # BPF trampoline bpf_nf # JIT does not support calling kernel function +bpf_tcp_ca # JIT does not support calling kernel function (kfunc) +cb_refs # expected error message unexpected error: -524 (trampoline) +cgroup_hierarchical_stats # JIT does not support calling kernel function (kfunc) core_read_macros # unknown func bpf_probe_read#4 (overlapping) d_path # failed to auto-attach program 'prog_stat': -524 (trampoline) +deny_namespace # failed to attach: ERROR: strerror_r(-524)=22 (trampoline) dummy_st_ops # test_run unexpected error: -524 (errno 524) (trampoline) fentry_fexit # fentry attach failed: -524 (trampoline) fentry_test # fentry_first_attach unexpected error: -524 (trampoline) @@ -18,19 +23,28 @@ fexit_test # fexit_first_attach unexpected error: get_func_args_test # trampoline get_func_ip_test # get_func_ip_test__attach unexpected error: -524 (trampoline) get_stack_raw_tp # user_stack corrupted user stack (no backchain userspace) +htab_update # failed to attach: ERROR: strerror_r(-524)=22 (trampoline) kfree_skb # attach fentry unexpected error: -524 (trampoline) kfunc_call # 'bpf_prog_active': not found in kernel BTF (?) +kfunc_dynptr_param # JIT does not support calling kernel function (kfunc) +kprobe_multi_test # relies on fentry ksyms_module # test_ksyms_module__open_and_load unexpected error: -9 (?) ksyms_module_libbpf # JIT does not support calling kernel function (kfunc) ksyms_module_lskel # test_ksyms_module_lskel__open_and_load unexpected error: -9 (?) +libbpf_get_fd_by_id_opts # failed to attach: ERROR: strerror_r(-524)=22 (trampoline) +lookup_key # JIT does not support calling kernel function (kfunc) +lru_bug # prog 'printk': failed to auto-attach: -524 +map_kptr # failed to open_and_load program: -524 (trampoline) modify_return # modify_return attach failed: -524 (trampoline) module_attach # skel_attach skeleton attach failed: -524 (trampoline) mptcp -kprobe_multi_test # relies on fentry netcnt # failed to load BPF skeleton 'netcnt_prog': -7 (?) probe_user # check_kprobe_res wrong kprobe res from probe read (?) recursion # skel_attach unexpected error: -524 (trampoline) ringbuf # skel_load skeleton load failed (?) +select_reuseport # intermittently fails on new s390x setup +send_signal # intermittently fails to receive signal +setget_sockopt # attach unexpected error: -524 (trampoline) sk_assign # Can't read on server: Invalid argument (?) sk_lookup # endianness problem sk_storage_tracing # test_sk_storage_tracing__attach unexpected error: -524 (trampoline) @@ -52,28 +66,15 @@ timer_mim # failed to auto-attach program 'test1' trace_ext # failed to auto-attach program 'test_pkt_md_access_new': -524 (trampoline) trace_printk # trace_printk__load unexpected error: -2 (errno 2) (?) trace_vprintk # trace_vprintk__open_and_load unexpected error: -9 (?) +tracing_struct # failed to auto-attach: -524 (trampoline) trampoline_count # prog 'prog1': failed to attach: ERROR: strerror_r(-524)=22 (trampoline) +unpriv_bpf_disabled # fentry +user_ringbuf # failed to find kernel BTF type ID of '__s390x_sys_prctl': -3 (?) verif_stats # trace_vprintk__open_and_load unexpected error: -9 (?) +verify_pkcs7_sig # JIT does not support calling kernel function (kfunc) vmlinux # failed to auto-attach program 'handle__fentry': -524 (trampoline) xdp_adjust_tail # case-128 err 0 errno 28 retval 1 size 128 expect-size 3520 (?) xdp_bonding # failed to auto-attach program 'trace_on_entry': -524 (trampoline) xdp_bpf2bpf # failed to auto-attach program 'trace_on_entry': -524 (trampoline) -map_kptr # failed to open_and_load program: -524 (trampoline) -bpf_cookie # failed to open_and_load program: -524 (trampoline) xdp_do_redirect # prog_run_max_size unexpected error: -22 (errno 22) -send_signal # intermittently fails to receive signal -select_reuseport # intermittently fails on new s390x setup xdp_synproxy # JIT does not support calling kernel function (kfunc) -unpriv_bpf_disabled # fentry -lru_bug # prog 'printk': failed to auto-attach: -524 -setget_sockopt # attach unexpected error: -524 (trampoline) -cb_refs # expected error message unexpected error: -524 (trampoline) -cgroup_hierarchical_stats # JIT does not support calling kernel function (kfunc) -htab_update # failed to attach: ERROR: strerror_r(-524)=22 (trampoline) -tracing_struct # failed to auto-attach: -524 (trampoline) -user_ringbuf # failed to find kernel BTF type ID of '__s390x_sys_prctl': -3 (?) -lookup_key # JIT does not support calling kernel function (kfunc) -verify_pkcs7_sig # JIT does not support calling kernel function (kfunc) -kfunc_dynptr_param # JIT does not support calling kernel function (kfunc) -deny_namespace # failed to attach: ERROR: strerror_r(-524)=22 (trampoline) -libbpf_get_fd_by_id_opts # failed to attach: ERROR: strerror_r(-524)=22 (trampoline) From 2577a58df24417dcdd35a3ba5b9e4e419a7d1234 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Thu, 6 Oct 2022 12:20:51 -0700 Subject: [PATCH 0128/2002] wifi: carl9170: Remove -Warray-bounds exception GCC-12 emits false positive -Warray-bounds warnings with CONFIG_UBSAN_SHIFT (-fsanitize=shift). This is fixed in GCC 13[1], and there is top-level Makefile logic to remove -Warray-bounds for known-bad GCC versions staring with commit f0be87c42cbd ("gcc-12: disable '-Warray-bounds' universally for now"). Remove the local work-around. [1] https://gcc.gnu.org/bugzilla/show_bug.cgi?id=105679 Signed-off-by: Kees Cook Acked-by: Christian Lamparter Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20221006192051.1742930-1-keescook@chromium.org --- drivers/net/wireless/ath/carl9170/Makefile | 5 ----- 1 file changed, 5 deletions(-) diff --git a/drivers/net/wireless/ath/carl9170/Makefile b/drivers/net/wireless/ath/carl9170/Makefile index 7463baa62fa8..1a81868ce26d 100644 --- a/drivers/net/wireless/ath/carl9170/Makefile +++ b/drivers/net/wireless/ath/carl9170/Makefile @@ -3,8 +3,3 @@ carl9170-objs := main.o usb.o cmd.o mac.o phy.o led.o fw.o tx.o rx.o carl9170-$(CONFIG_CARL9170_DEBUGFS) += debug.o obj-$(CONFIG_CARL9170) += carl9170.o - -# FIXME: temporarily silence -Warray-bounds on non W=1+ builds -ifndef KBUILD_EXTRA_WARN -CFLAGS_cmd.o += -Wno-array-bounds -endif From b6702a942a069c2a975478d719e98d83cdae1797 Mon Sep 17 00:00:00 2001 From: Shigeru Yoshida Date: Mon, 10 Oct 2022 03:32:23 +0900 Subject: [PATCH 0129/2002] wifi: ar5523: Fix use-after-free on ar5523_cmd() timed out syzkaller reported use-after-free with the stack trace like below [1]: [ 38.960489][ C3] ================================================================== [ 38.963216][ C3] BUG: KASAN: use-after-free in ar5523_cmd_tx_cb+0x220/0x240 [ 38.964950][ C3] Read of size 8 at addr ffff888048e03450 by task swapper/3/0 [ 38.966363][ C3] [ 38.967053][ C3] CPU: 3 PID: 0 Comm: swapper/3 Not tainted 6.0.0-09039-ga6afa4199d3d-dirty #18 [ 38.968464][ C3] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.16.0-1.fc36 04/01/2014 [ 38.969959][ C3] Call Trace: [ 38.970841][ C3] [ 38.971663][ C3] dump_stack_lvl+0xfc/0x174 [ 38.972620][ C3] print_report.cold+0x2c3/0x752 [ 38.973626][ C3] ? ar5523_cmd_tx_cb+0x220/0x240 [ 38.974644][ C3] kasan_report+0xb1/0x1d0 [ 38.975720][ C3] ? ar5523_cmd_tx_cb+0x220/0x240 [ 38.976831][ C3] ar5523_cmd_tx_cb+0x220/0x240 [ 38.978412][ C3] __usb_hcd_giveback_urb+0x353/0x5b0 [ 38.979755][ C3] usb_hcd_giveback_urb+0x385/0x430 [ 38.981266][ C3] dummy_timer+0x140c/0x34e0 [ 38.982925][ C3] ? notifier_call_chain+0xb5/0x1e0 [ 38.984761][ C3] ? rcu_read_lock_sched_held+0xb/0x60 [ 38.986242][ C3] ? lock_release+0x51c/0x790 [ 38.987323][ C3] ? _raw_read_unlock_irqrestore+0x37/0x70 [ 38.988483][ C3] ? __wake_up_common_lock+0xde/0x130 [ 38.989621][ C3] ? reacquire_held_locks+0x4a0/0x4a0 [ 38.990777][ C3] ? lock_acquire+0x472/0x550 [ 38.991919][ C3] ? rcu_read_lock_sched_held+0xb/0x60 [ 38.993138][ C3] ? lock_acquire+0x472/0x550 [ 38.994890][ C3] ? dummy_urb_enqueue+0x860/0x860 [ 38.996266][ C3] ? do_raw_spin_unlock+0x16f/0x230 [ 38.997670][ C3] ? dummy_urb_enqueue+0x860/0x860 [ 38.999116][ C3] call_timer_fn+0x1a0/0x6a0 [ 39.000668][ C3] ? add_timer_on+0x4a0/0x4a0 [ 39.002137][ C3] ? reacquire_held_locks+0x4a0/0x4a0 [ 39.003809][ C3] ? __next_timer_interrupt+0x226/0x2a0 [ 39.005509][ C3] __run_timers.part.0+0x69a/0xac0 [ 39.007025][ C3] ? dummy_urb_enqueue+0x860/0x860 [ 39.008716][ C3] ? call_timer_fn+0x6a0/0x6a0 [ 39.010254][ C3] ? cpuacct_percpu_seq_show+0x10/0x10 [ 39.011795][ C3] ? kvm_sched_clock_read+0x14/0x40 [ 39.013277][ C3] ? sched_clock_cpu+0x69/0x2b0 [ 39.014724][ C3] run_timer_softirq+0xb6/0x1d0 [ 39.016196][ C3] __do_softirq+0x1d2/0x9be [ 39.017616][ C3] __irq_exit_rcu+0xeb/0x190 [ 39.019004][ C3] irq_exit_rcu+0x5/0x20 [ 39.020361][ C3] sysvec_apic_timer_interrupt+0x8f/0xb0 [ 39.021965][ C3] [ 39.023237][ C3] In ar5523_probe(), ar5523_host_available() calls ar5523_cmd() as below (there are other functions which finally call ar5523_cmd()): ar5523_probe() -> ar5523_host_available() -> ar5523_cmd_read() -> ar5523_cmd() If ar5523_cmd() timed out, then ar5523_host_available() failed and ar5523_probe() freed the device structure. So, ar5523_cmd_tx_cb() might touch the freed structure. This patch fixes this issue by canceling in-flight tx cmd if submitted urb timed out. Link: https://syzkaller.appspot.com/bug?id=9e12b2d54300842b71bdd18b54971385ff0d0d3a [1] Reported-by: syzbot+95001b1fd6dfcc716c29@syzkaller.appspotmail.com Signed-off-by: Shigeru Yoshida Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20221009183223.420015-1-syoshida@redhat.com --- drivers/net/wireless/ath/ar5523/ar5523.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/net/wireless/ath/ar5523/ar5523.c b/drivers/net/wireless/ath/ar5523/ar5523.c index 6f937d2cc126..ce3d613fa36c 100644 --- a/drivers/net/wireless/ath/ar5523/ar5523.c +++ b/drivers/net/wireless/ath/ar5523/ar5523.c @@ -241,6 +241,11 @@ static void ar5523_cmd_tx_cb(struct urb *urb) } } +static void ar5523_cancel_tx_cmd(struct ar5523 *ar) +{ + usb_kill_urb(ar->tx_cmd.urb_tx); +} + static int ar5523_cmd(struct ar5523 *ar, u32 code, const void *idata, int ilen, void *odata, int olen, int flags) { @@ -280,6 +285,7 @@ static int ar5523_cmd(struct ar5523 *ar, u32 code, const void *idata, } if (!wait_for_completion_timeout(&cmd->done, 2 * HZ)) { + ar5523_cancel_tx_cmd(ar); cmd->odata = NULL; ar5523_err(ar, "timeout waiting for command %02x reply\n", code); From 3ff51d7416ee1ea2d771051a0ffa1ec8be054768 Mon Sep 17 00:00:00 2001 From: Aditya Kumar Singh Date: Wed, 5 Oct 2022 15:24:30 +0530 Subject: [PATCH 0130/2002] wifi: ath11k: fix firmware assert during bandwidth change for peer sta Currently, ath11k sends peer assoc command for each peer to firmware when bandwidth changes. Peer assoc command is a bulky command and if many clients are connected, this could lead to firmware buffer getting overflowed leading to a firmware assert. However, during bandwidth change, only phymode and bandwidth also can be updated by WMI set peer param command. This makes the overall command light when compared to peer assoc and for multi-client cases, firmware buffer overflow also does not occur. Remove sending peer assoc command during sta bandwidth change and instead add sending WMI set peer param command for phymode and bandwidth. Tested-on: QCN9074 hw1.0 PCI WLAN.HK.2.5.0.1-01100-QCAHKSWPL_SILICONZ-1 Fixes: f187fe8e3bc65 ("ath11k: fix firmware crash during channel switch") Signed-off-by: Aditya Kumar Singh Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20221005095430.19890-1-quic_adisi@quicinc.com --- drivers/net/wireless/ath/ath11k/core.h | 2 + drivers/net/wireless/ath/ath11k/mac.c | 120 +++++++++++++++++-------- 2 files changed, 86 insertions(+), 36 deletions(-) diff --git a/drivers/net/wireless/ath/ath11k/core.h b/drivers/net/wireless/ath/ath11k/core.h index bbcef120c8f6..22460b0abf03 100644 --- a/drivers/net/wireless/ath/ath11k/core.h +++ b/drivers/net/wireless/ath/ath11k/core.h @@ -505,6 +505,8 @@ struct ath11k_sta { u64 ps_start_jiffies; u64 ps_total_duration; bool peer_current_ps_valid; + + u32 bw_prev; }; #define ATH11K_MIN_5G_FREQ 4150 diff --git a/drivers/net/wireless/ath/ath11k/mac.c b/drivers/net/wireless/ath/ath11k/mac.c index 84d956ad4093..4b86769cab8b 100644 --- a/drivers/net/wireless/ath/ath11k/mac.c +++ b/drivers/net/wireless/ath/ath11k/mac.c @@ -4214,10 +4214,11 @@ static void ath11k_sta_rc_update_wk(struct work_struct *wk) const u8 *ht_mcs_mask; const u16 *vht_mcs_mask; const u16 *he_mcs_mask; - u32 changed, bw, nss, smps; + u32 changed, bw, nss, smps, bw_prev; int err, num_vht_rates, num_he_rates; const struct cfg80211_bitrate_mask *mask; struct peer_assoc_params peer_arg; + enum wmi_phy_mode peer_phymode; arsta = container_of(wk, struct ath11k_sta, update_wk); sta = container_of((void *)arsta, struct ieee80211_sta, drv_priv); @@ -4238,6 +4239,7 @@ static void ath11k_sta_rc_update_wk(struct work_struct *wk) arsta->changed = 0; bw = arsta->bw; + bw_prev = arsta->bw_prev; nss = arsta->nss; smps = arsta->smps; @@ -4251,26 +4253,57 @@ static void ath11k_sta_rc_update_wk(struct work_struct *wk) ath11k_mac_max_he_nss(he_mcs_mask))); if (changed & IEEE80211_RC_BW_CHANGED) { - /* Send peer assoc command before set peer bandwidth param to - * avoid the mismatch between the peer phymode and the peer - * bandwidth. - */ - ath11k_peer_assoc_prepare(ar, arvif->vif, sta, &peer_arg, true); + /* Get the peer phymode */ + ath11k_peer_assoc_h_phymode(ar, arvif->vif, sta, &peer_arg); + peer_phymode = peer_arg.peer_phymode; + + ath11k_dbg(ar->ab, ATH11K_DBG_MAC, "mac update sta %pM peer bw %d phymode %d\n", + sta->addr, bw, peer_phymode); + + if (bw > bw_prev) { + /* BW is upgraded. In this case we send WMI_PEER_PHYMODE + * followed by WMI_PEER_CHWIDTH + */ + ath11k_dbg(ar->ab, ATH11K_DBG_MAC, "mac BW upgrade for sta %pM new BW %d, old BW %d\n", + sta->addr, bw, bw_prev); + + err = ath11k_wmi_set_peer_param(ar, sta->addr, arvif->vdev_id, + WMI_PEER_PHYMODE, peer_phymode); + + if (err) { + ath11k_warn(ar->ab, "failed to update STA %pM peer phymode %d: %d\n", + sta->addr, peer_phymode, err); + goto err_rc_bw_changed; + } - peer_arg.is_assoc = false; - err = ath11k_wmi_send_peer_assoc_cmd(ar, &peer_arg); - if (err) { - ath11k_warn(ar->ab, "failed to send peer assoc for STA %pM vdev %i: %d\n", - sta->addr, arvif->vdev_id, err); - } else if (wait_for_completion_timeout(&ar->peer_assoc_done, 1 * HZ)) { err = ath11k_wmi_set_peer_param(ar, sta->addr, arvif->vdev_id, WMI_PEER_CHWIDTH, bw); + if (err) ath11k_warn(ar->ab, "failed to update STA %pM peer bw %d: %d\n", sta->addr, bw, err); } else { - ath11k_warn(ar->ab, "failed to get peer assoc conf event for %pM vdev %i\n", - sta->addr, arvif->vdev_id); + /* BW is downgraded. In this case we send WMI_PEER_CHWIDTH + * followed by WMI_PEER_PHYMODE + */ + ath11k_dbg(ar->ab, ATH11K_DBG_MAC, "mac BW downgrade for sta %pM new BW %d,old BW %d\n", + sta->addr, bw, bw_prev); + + err = ath11k_wmi_set_peer_param(ar, sta->addr, arvif->vdev_id, + WMI_PEER_CHWIDTH, bw); + + if (err) { + ath11k_warn(ar->ab, "failed to update STA %pM peer bw %d: %d\n", + sta->addr, bw, err); + goto err_rc_bw_changed; + } + + err = ath11k_wmi_set_peer_param(ar, sta->addr, arvif->vdev_id, + WMI_PEER_PHYMODE, peer_phymode); + + if (err) + ath11k_warn(ar->ab, "failed to update STA %pM peer phymode %d: %d\n", + sta->addr, peer_phymode, err); } } @@ -4351,6 +4384,7 @@ static void ath11k_sta_rc_update_wk(struct work_struct *wk) } } +err_rc_bw_changed: mutex_unlock(&ar->conf_mutex); } @@ -4504,6 +4538,34 @@ exit: return ret; } +static u32 ath11k_mac_ieee80211_sta_bw_to_wmi(struct ath11k *ar, + struct ieee80211_sta *sta) +{ + u32 bw = WMI_PEER_CHWIDTH_20MHZ; + + switch (sta->deflink.bandwidth) { + case IEEE80211_STA_RX_BW_20: + bw = WMI_PEER_CHWIDTH_20MHZ; + break; + case IEEE80211_STA_RX_BW_40: + bw = WMI_PEER_CHWIDTH_40MHZ; + break; + case IEEE80211_STA_RX_BW_80: + bw = WMI_PEER_CHWIDTH_80MHZ; + break; + case IEEE80211_STA_RX_BW_160: + bw = WMI_PEER_CHWIDTH_160MHZ; + break; + default: + ath11k_warn(ar->ab, "Invalid bandwidth %d for %pM\n", + sta->deflink.bandwidth, sta->addr); + bw = WMI_PEER_CHWIDTH_20MHZ; + break; + } + + return bw; +} + static int ath11k_mac_op_sta_state(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_sta *sta, @@ -4589,6 +4651,12 @@ static int ath11k_mac_op_sta_state(struct ieee80211_hw *hw, if (ret) ath11k_warn(ar->ab, "Failed to associate station: %pM\n", sta->addr); + + spin_lock_bh(&ar->data_lock); + /* Set arsta bw and prev bw */ + arsta->bw = ath11k_mac_ieee80211_sta_bw_to_wmi(ar, sta); + arsta->bw_prev = arsta->bw; + spin_unlock_bh(&ar->data_lock); } else if (old_state == IEEE80211_STA_ASSOC && new_state == IEEE80211_STA_AUTHORIZED) { spin_lock_bh(&ar->ab->base_lock); @@ -4712,28 +4780,8 @@ static void ath11k_mac_op_sta_rc_update(struct ieee80211_hw *hw, spin_lock_bh(&ar->data_lock); if (changed & IEEE80211_RC_BW_CHANGED) { - bw = WMI_PEER_CHWIDTH_20MHZ; - - switch (sta->deflink.bandwidth) { - case IEEE80211_STA_RX_BW_20: - bw = WMI_PEER_CHWIDTH_20MHZ; - break; - case IEEE80211_STA_RX_BW_40: - bw = WMI_PEER_CHWIDTH_40MHZ; - break; - case IEEE80211_STA_RX_BW_80: - bw = WMI_PEER_CHWIDTH_80MHZ; - break; - case IEEE80211_STA_RX_BW_160: - bw = WMI_PEER_CHWIDTH_160MHZ; - break; - default: - ath11k_warn(ar->ab, "Invalid bandwidth %d in rc update for %pM\n", - sta->deflink.bandwidth, sta->addr); - bw = WMI_PEER_CHWIDTH_20MHZ; - break; - } - + bw = ath11k_mac_ieee80211_sta_bw_to_wmi(ar, sta); + arsta->bw_prev = arsta->bw; arsta->bw = bw; } From 638b26652b0438563a76ec90014c8cba34db982b Mon Sep 17 00:00:00 2001 From: Karthikeyan Periyasamy Date: Thu, 6 Oct 2022 06:28:42 +0530 Subject: [PATCH 0131/2002] wifi: ath11k: suppress add interface error In the VIF (other than monitor type) creation request, we should not throw the error code when the monitor VIF creation fails, since the actual VIF creation succeeds. If we throw the error code from driver then the actual VIF creation get fail. So suppress the monitor VIF creation error by throwing warning message instead of error code. Tested-on: QCN9074 hw1.0 PCI WLAN.HK.2.6.0.1-00760-QCAHKSWPL_SILICONZ-1 Signed-off-by: Karthikeyan Periyasamy Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20221006005842.8599-1-quic_periyasa@quicinc.com --- drivers/net/wireless/ath/ath11k/mac.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/drivers/net/wireless/ath/ath11k/mac.c b/drivers/net/wireless/ath/ath11k/mac.c index 4b86769cab8b..b37803d6c93f 100644 --- a/drivers/net/wireless/ath/ath11k/mac.c +++ b/drivers/net/wireless/ath/ath11k/mac.c @@ -6420,18 +6420,16 @@ static int ath11k_mac_op_add_interface(struct ieee80211_hw *hw, ath11k_dp_vdev_tx_attach(ar, arvif); + ath11k_debugfs_add_interface(arvif); + if (vif->type != NL80211_IFTYPE_MONITOR && test_bit(ATH11K_FLAG_MONITOR_CONF_ENABLED, &ar->monitor_flags)) { ret = ath11k_mac_monitor_vdev_create(ar); - if (ret) { + if (ret) ath11k_warn(ar->ab, "failed to create monitor vdev during add interface: %d", ret); - goto err_peer_del; - } } - ath11k_debugfs_add_interface(arvif); - mutex_unlock(&ar->conf_mutex); return 0; @@ -6456,7 +6454,6 @@ err_vdev_del: spin_unlock_bh(&ar->data_lock); err: - ath11k_debugfs_remove_interface(arvif); mutex_unlock(&ar->conf_mutex); return ret; From c362daa213cdeb0a9e7c2ed84849544c24505720 Mon Sep 17 00:00:00 2001 From: Manikanta Pubbisetty Date: Fri, 7 Oct 2022 10:41:30 +0530 Subject: [PATCH 0132/2002] wifi: ath11k: add support to configure channel dwell time Add support to configure channel dwell time during scan. Dwell time help to stay on the channel for a specified duration during scan and aid userspace in finding WiFi networks. Very useful in passive scans where longer dwell times are needed to find the WiFi networks. Configure channel dwell time from duration of the scan request received from mac80211 when the duration is non-zero. When the scan request does not have duration value, use the default ones, the current implementation. Advertise corresponding feature flag NL80211_EXT_FEATURE_SET_SCAN_DWELL to enable the feature. Change is applicable for all ath11k hardware. Tested-on: WCN6750 hw1.0 AHB WLAN.MSL.1.0.1-00887-QCAMSLSWPLZ-1 Signed-off-by: Manikanta Pubbisetty Reviewed-by: Jeff Johnson Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20221007051130.6067-1-quic_mpubbise@quicinc.com --- drivers/net/wireless/ath/ath11k/mac.c | 33 +++++++++++++++++++++++---- 1 file changed, 29 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/ath/ath11k/mac.c b/drivers/net/wireless/ath/ath11k/mac.c index b37803d6c93f..ce15693c1804 100644 --- a/drivers/net/wireless/ath/ath11k/mac.c +++ b/drivers/net/wireless/ath/ath11k/mac.c @@ -241,7 +241,10 @@ const struct htt_rx_ring_tlv_filter ath11k_mac_mon_status_filter_default = { #define ath11k_a_rates (ath11k_legacy_rates + 4) #define ath11k_a_rates_size (ARRAY_SIZE(ath11k_legacy_rates) - 4) -#define ATH11K_MAC_SCAN_TIMEOUT_MSECS 200 /* in msecs */ +#define ATH11K_MAC_SCAN_CMD_EVT_OVERHEAD 200 /* in msecs */ + +/* Overhead due to the processing of channel switch events from FW */ +#define ATH11K_SCAN_CHANNEL_SWITCH_WMI_EVT_OVERHEAD 10 /* in msecs */ static const u32 ath11k_smps_map[] = { [WLAN_HT_CAP_SM_PS_STATIC] = WMI_PEER_SMPS_STATIC, @@ -3611,6 +3614,7 @@ static int ath11k_mac_op_hw_scan(struct ieee80211_hw *hw, struct scan_req_params arg; int ret = 0; int i; + u32 scan_timeout; mutex_lock(&ar->conf_mutex); @@ -3680,6 +3684,26 @@ static int ath11k_mac_op_hw_scan(struct ieee80211_hw *hw, ether_addr_copy(arg.mac_mask.addr, req->mac_addr_mask); } + /* if duration is set, default dwell times will be overwritten */ + if (req->duration) { + arg.dwell_time_active = req->duration; + arg.dwell_time_active_2g = req->duration; + arg.dwell_time_active_6g = req->duration; + arg.dwell_time_passive = req->duration; + arg.dwell_time_passive_6g = req->duration; + arg.burst_duration = req->duration; + + scan_timeout = min_t(u32, arg.max_rest_time * + (arg.num_chan - 1) + (req->duration + + ATH11K_SCAN_CHANNEL_SWITCH_WMI_EVT_OVERHEAD) * + arg.num_chan, arg.max_scan_time); + } else { + scan_timeout = arg.max_scan_time; + } + + /* Add a margin to account for event/command processing */ + scan_timeout += ATH11K_MAC_SCAN_CMD_EVT_OVERHEAD; + ret = ath11k_start_scan(ar, &arg); if (ret) { ath11k_warn(ar->ab, "failed to start hw scan: %d\n", ret); @@ -3688,10 +3712,8 @@ static int ath11k_mac_op_hw_scan(struct ieee80211_hw *hw, spin_unlock_bh(&ar->data_lock); } - /* Add a 200ms margin to account for event/command processing */ ieee80211_queue_delayed_work(ar->hw, &ar->scan.timeout, - msecs_to_jiffies(arg.max_scan_time + - ATH11K_MAC_SCAN_TIMEOUT_MSECS)); + msecs_to_jiffies(scan_timeout)); exit: kfree(arg.chan_list); @@ -9058,6 +9080,9 @@ static int __ath11k_mac_register(struct ath11k *ar) NL80211_EXT_FEATURE_UNSOL_BCAST_PROBE_RESP); } + wiphy_ext_feature_set(ar->hw->wiphy, + NL80211_EXT_FEATURE_SET_SCAN_DWELL); + ath11k_reg_init(ar); if (!test_bit(ATH11K_FLAG_RAW_MODE, &ab->dev_flags)) { From 3f9b09ccf7d5f23066b02881a737bee42def9d1a Mon Sep 17 00:00:00 2001 From: Baochen Qiang Date: Mon, 10 Oct 2022 11:32:37 +0800 Subject: [PATCH 0133/2002] wifi: ath11k: Send PME message during wakeup from D3cold We are seeing system stuck on some specific platforms due to WLAN chip fails to wakeup from D3cold state. With this flag, firmware will send PME message during wakeup and this issue is gone. Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-03125-QCAHSPSWPL_V1_V2_SILICONZ_LITE-3 Signed-off-by: Baochen Qiang Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20221010033237.415478-1-quic_bqiang@quicinc.com --- drivers/net/wireless/ath/ath11k/qmi.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/wireless/ath/ath11k/qmi.c b/drivers/net/wireless/ath/ath11k/qmi.c index 84e3abecbdac..145f20a681bd 100644 --- a/drivers/net/wireless/ath/ath11k/qmi.c +++ b/drivers/net/wireless/ath/ath11k/qmi.c @@ -19,6 +19,7 @@ #define SLEEP_CLOCK_SELECT_INTERNAL_BIT 0x02 #define HOST_CSTATE_BIT 0x04 #define PLATFORM_CAP_PCIE_GLOBAL_RESET 0x08 +#define PLATFORM_CAP_PCIE_PME_D3COLD 0x10 #define FW_BUILD_ID_MASK "QC_IMAGE_VERSION_STRING=" @@ -1752,6 +1753,8 @@ static int ath11k_qmi_host_cap_send(struct ath11k_base *ab) if (ab->hw_params.global_reset) req.nm_modem |= PLATFORM_CAP_PCIE_GLOBAL_RESET; + req.nm_modem |= PLATFORM_CAP_PCIE_PME_D3COLD; + ath11k_dbg(ab, ATH11K_DBG_QMI, "qmi host cap request\n"); ret = qmi_txn_init(&ab->qmi.handle, &txn, From dfc3082da9c97112007b6d585e28c1aeb8618acb Mon Sep 17 00:00:00 2001 From: Yang Yingliang Date: Thu, 15 Sep 2022 15:12:58 +0800 Subject: [PATCH 0134/2002] net: ieee802154: mcr20a: Switch to use dev_err_probe() helper dev_err() can be replace with dev_err_probe() which will check if error code is -EPROBE_DEFER. Signed-off-by: Yang Yingliang Link: https://lore.kernel.org/r/20220915071258.678536-1-yangyingliang@huawei.com Signed-off-by: Stefan Schmidt --- drivers/net/ieee802154/mcr20a.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/drivers/net/ieee802154/mcr20a.c b/drivers/net/ieee802154/mcr20a.c index 2fe0e4a0a0c4..f53d185e0568 100644 --- a/drivers/net/ieee802154/mcr20a.c +++ b/drivers/net/ieee802154/mcr20a.c @@ -1233,12 +1233,9 @@ mcr20a_probe(struct spi_device *spi) } rst_b = devm_gpiod_get(&spi->dev, "rst_b", GPIOD_OUT_HIGH); - if (IS_ERR(rst_b)) { - ret = PTR_ERR(rst_b); - if (ret != -EPROBE_DEFER) - dev_err(&spi->dev, "Failed to get 'rst_b' gpio: %d", ret); - return ret; - } + if (IS_ERR(rst_b)) + return dev_err_probe(&spi->dev, PTR_ERR(rst_b), + "Failed to get 'rst_b' gpio"); /* reset mcr20a */ usleep_range(10, 20); From a6a6163b9a934df5965792d60af1f53aa51fdd39 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Fri, 7 Oct 2022 10:53:03 +0200 Subject: [PATCH 0135/2002] mac802154: Introduce filtering levels The 802154 specification details several filtering levels in which the PHY and the MAC could be. The amount of filtering will vary if they are in promiscuous mode or in scanning mode. Otherwise they are expected to do some very basic checks, such as enforcing the frame validity. Either the PHY is able to do so, and the MAC has nothing to do, or the PHY has a lower filtering level than expected and the MAC should take over. For now we just define these levels in an enumeration. In a second time, we will add a per-PHY parameter showing the expected filtering level as well as a per device current filtering level, and will initialize all these fields. In a third time, we will use them to apply more filtering by software when the PHY is limited. Indeed, if the drivers know they cannot reach the requested level of filtering, they will overwrite the "current filtering" parameter so that it reflects what they do. Then, in the core, the expected filtering level will be used to decide whether some additional software processing is needed or not. Signed-off-by: Miquel Raynal Acked-by: Alexander Aring Link: https://lore.kernel.org/r/20221007085310.503366-2-miquel.raynal@bootlin.com Signed-off-by: Stefan Schmidt --- include/linux/ieee802154.h | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/include/linux/ieee802154.h b/include/linux/ieee802154.h index f1f9412b6ac6..0303eb84d596 100644 --- a/include/linux/ieee802154.h +++ b/include/linux/ieee802154.h @@ -276,6 +276,30 @@ enum { IEEE802154_SYSTEM_ERROR = 0xff, }; +/** + * enum ieee802154_filtering_level - Filtering levels applicable to a PHY + * + * @IEEE802154_FILTERING_NONE: No filtering at all, what is received is + * forwarded to the softMAC + * @IEEE802154_FILTERING_1_FCS: First filtering level, frames with an invalid + * FCS should be dropped + * @IEEE802154_FILTERING_2_PROMISCUOUS: Second filtering level, promiscuous + * mode as described in the spec, identical in terms of filtering to the + * level one on PHY side, but at the MAC level the frame should be + * forwarded to the upper layer directly + * @IEEE802154_FILTERING_3_SCAN: Third filtering level, scan related, where + * only beacons must be processed, all remaining traffic gets dropped + * @IEEE802154_FILTERING_4_FRAME_FIELDS: Fourth filtering level actually + * enforcing the validity of the content of the frame with various checks + */ +enum ieee802154_filtering_level { + IEEE802154_FILTERING_NONE, + IEEE802154_FILTERING_1_FCS, + IEEE802154_FILTERING_2_PROMISCUOUS, + IEEE802154_FILTERING_3_SCAN, + IEEE802154_FILTERING_4_FRAME_FIELDS, +}; + /* frame control handling */ #define IEEE802154_FCTL_FTYPE 0x0003 #define IEEE802154_FCTL_ACKREQ 0x0020 From e9d8d9c4084dadfa5a68a2e6e4e4dda7a0a728ba Mon Sep 17 00:00:00 2001 From: Alexander Aring Date: Fri, 7 Oct 2022 10:53:04 +0200 Subject: [PATCH 0136/2002] mac802154: move receive parameters above start This patch moves all receive parameters above the drv_start() functionality to make it accessibile in the drv_start() function. Signed-off-by: Alexander Aring Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/r/20221007085310.503366-3-miquel.raynal@bootlin.com Signed-off-by: Stefan Schmidt --- net/mac802154/driver-ops.h | 210 ++++++++++++++++++------------------- 1 file changed, 105 insertions(+), 105 deletions(-) diff --git a/net/mac802154/driver-ops.h b/net/mac802154/driver-ops.h index d23f0db98015..c9d54088a567 100644 --- a/net/mac802154/driver-ops.h +++ b/net/mac802154/driver-ops.h @@ -24,6 +24,111 @@ drv_xmit_sync(struct ieee802154_local *local, struct sk_buff *skb) return local->ops->xmit_sync(&local->hw, skb); } +static inline int drv_set_pan_id(struct ieee802154_local *local, __le16 pan_id) +{ + struct ieee802154_hw_addr_filt filt; + int ret; + + might_sleep(); + + if (!local->ops->set_hw_addr_filt) { + WARN_ON(1); + return -EOPNOTSUPP; + } + + filt.pan_id = pan_id; + + trace_802154_drv_set_pan_id(local, pan_id); + ret = local->ops->set_hw_addr_filt(&local->hw, &filt, + IEEE802154_AFILT_PANID_CHANGED); + trace_802154_drv_return_int(local, ret); + return ret; +} + +static inline int +drv_set_extended_addr(struct ieee802154_local *local, __le64 extended_addr) +{ + struct ieee802154_hw_addr_filt filt; + int ret; + + might_sleep(); + + if (!local->ops->set_hw_addr_filt) { + WARN_ON(1); + return -EOPNOTSUPP; + } + + filt.ieee_addr = extended_addr; + + trace_802154_drv_set_extended_addr(local, extended_addr); + ret = local->ops->set_hw_addr_filt(&local->hw, &filt, + IEEE802154_AFILT_IEEEADDR_CHANGED); + trace_802154_drv_return_int(local, ret); + return ret; +} + +static inline int +drv_set_short_addr(struct ieee802154_local *local, __le16 short_addr) +{ + struct ieee802154_hw_addr_filt filt; + int ret; + + might_sleep(); + + if (!local->ops->set_hw_addr_filt) { + WARN_ON(1); + return -EOPNOTSUPP; + } + + filt.short_addr = short_addr; + + trace_802154_drv_set_short_addr(local, short_addr); + ret = local->ops->set_hw_addr_filt(&local->hw, &filt, + IEEE802154_AFILT_SADDR_CHANGED); + trace_802154_drv_return_int(local, ret); + return ret; +} + +static inline int +drv_set_pan_coord(struct ieee802154_local *local, bool is_coord) +{ + struct ieee802154_hw_addr_filt filt; + int ret; + + might_sleep(); + + if (!local->ops->set_hw_addr_filt) { + WARN_ON(1); + return -EOPNOTSUPP; + } + + filt.pan_coord = is_coord; + + trace_802154_drv_set_pan_coord(local, is_coord); + ret = local->ops->set_hw_addr_filt(&local->hw, &filt, + IEEE802154_AFILT_PANC_CHANGED); + trace_802154_drv_return_int(local, ret); + return ret; +} + +static inline int +drv_set_promiscuous_mode(struct ieee802154_local *local, bool on) +{ + int ret; + + might_sleep(); + + if (!local->ops->set_promiscuous_mode) { + WARN_ON(1); + return -EOPNOTSUPP; + } + + trace_802154_drv_set_promiscuous_mode(local, on); + ret = local->ops->set_promiscuous_mode(&local->hw, on); + trace_802154_drv_return_int(local, ret); + return ret; +} + static inline int drv_start(struct ieee802154_local *local) { int ret; @@ -138,93 +243,6 @@ drv_set_cca_ed_level(struct ieee802154_local *local, s32 mbm) return ret; } -static inline int drv_set_pan_id(struct ieee802154_local *local, __le16 pan_id) -{ - struct ieee802154_hw_addr_filt filt; - int ret; - - might_sleep(); - - if (!local->ops->set_hw_addr_filt) { - WARN_ON(1); - return -EOPNOTSUPP; - } - - filt.pan_id = pan_id; - - trace_802154_drv_set_pan_id(local, pan_id); - ret = local->ops->set_hw_addr_filt(&local->hw, &filt, - IEEE802154_AFILT_PANID_CHANGED); - trace_802154_drv_return_int(local, ret); - return ret; -} - -static inline int -drv_set_extended_addr(struct ieee802154_local *local, __le64 extended_addr) -{ - struct ieee802154_hw_addr_filt filt; - int ret; - - might_sleep(); - - if (!local->ops->set_hw_addr_filt) { - WARN_ON(1); - return -EOPNOTSUPP; - } - - filt.ieee_addr = extended_addr; - - trace_802154_drv_set_extended_addr(local, extended_addr); - ret = local->ops->set_hw_addr_filt(&local->hw, &filt, - IEEE802154_AFILT_IEEEADDR_CHANGED); - trace_802154_drv_return_int(local, ret); - return ret; -} - -static inline int -drv_set_short_addr(struct ieee802154_local *local, __le16 short_addr) -{ - struct ieee802154_hw_addr_filt filt; - int ret; - - might_sleep(); - - if (!local->ops->set_hw_addr_filt) { - WARN_ON(1); - return -EOPNOTSUPP; - } - - filt.short_addr = short_addr; - - trace_802154_drv_set_short_addr(local, short_addr); - ret = local->ops->set_hw_addr_filt(&local->hw, &filt, - IEEE802154_AFILT_SADDR_CHANGED); - trace_802154_drv_return_int(local, ret); - return ret; -} - -static inline int -drv_set_pan_coord(struct ieee802154_local *local, bool is_coord) -{ - struct ieee802154_hw_addr_filt filt; - int ret; - - might_sleep(); - - if (!local->ops->set_hw_addr_filt) { - WARN_ON(1); - return -EOPNOTSUPP; - } - - filt.pan_coord = is_coord; - - trace_802154_drv_set_pan_coord(local, is_coord); - ret = local->ops->set_hw_addr_filt(&local->hw, &filt, - IEEE802154_AFILT_PANC_CHANGED); - trace_802154_drv_return_int(local, ret); - return ret; -} - static inline int drv_set_csma_params(struct ieee802154_local *local, u8 min_be, u8 max_be, u8 max_csma_backoffs) @@ -264,22 +282,4 @@ drv_set_max_frame_retries(struct ieee802154_local *local, s8 max_frame_retries) return ret; } -static inline int -drv_set_promiscuous_mode(struct ieee802154_local *local, bool on) -{ - int ret; - - might_sleep(); - - if (!local->ops->set_promiscuous_mode) { - WARN_ON(1); - return -EOPNOTSUPP; - } - - trace_802154_drv_set_promiscuous_mode(local, on); - ret = local->ops->set_promiscuous_mode(&local->hw, on); - trace_802154_drv_return_int(local, ret); - return ret; -} - #endif /* __MAC802154_DRIVER_OPS */ From ac8037c35bd1fb1799d39f52205f813e6585b58b Mon Sep 17 00:00:00 2001 From: Alexander Aring Date: Fri, 7 Oct 2022 10:53:05 +0200 Subject: [PATCH 0137/2002] mac802154: set filter at drv_start() The current filtering level is set on the first interface up on a wpan phy. If we support scan functionality we need to change the filtering level on the fly on an operational phy and switching back again. This patch will move the receive mode parameter e.g. address filter and promiscuous mode to the drv_start() functionality to allow changing the receive mode on an operational phy not on first ifup only. In future this should be handled on driver layer because each hardware has it's own way to enter a specific filtering level. However this should offer to switch to mode IEEE802154_FILTERING_NONE and back to IEEE802154_FILTERING_4_FRAME_FIELDS. Only IEEE802154_FILTERING_4_FRAME_FIELDS and IEEE802154_FILTERING_NONE are somewhat supported by current hardware. All other filtering levels can be supported in future but will end in IEEE802154_FILTERING_NONE as the receive part can kind of "emulate" those receive paths by doing additional filtering routines. There are in total three filtering levels in the code: - the per-interface default level (should not be changed) - the required per-interface level (mac commands may play with it) - the actual per-PHY (hw) level that is currently in use Signed-off-by: Alexander Aring [ Link: https://lore.kernel.org/r/20221007085310.503366-4-miquel.raynal@bootlin.com Signed-off-by: Stefan Schmidt --- include/net/cfg802154.h | 7 +++- net/mac802154/cfg.c | 2 +- net/mac802154/driver-ops.h | 71 +++++++++++++++++++++++++++++++++++- net/mac802154/ieee802154_i.h | 12 ++++++ net/mac802154/iface.c | 44 ++++++++-------------- net/mac802154/rx.c | 12 +++++- 6 files changed, 115 insertions(+), 33 deletions(-) diff --git a/include/net/cfg802154.h b/include/net/cfg802154.h index 428cece22205..e1481f9cf049 100644 --- a/include/net/cfg802154.h +++ b/include/net/cfg802154.h @@ -223,6 +223,11 @@ struct wpan_phy { atomic_t hold_txs; wait_queue_head_t sync_txq; + /* Current filtering level on reception. + * Only allowed to be changed if phy is not operational. + */ + enum ieee802154_filtering_level filtering; + char priv[] __aligned(NETDEV_ALIGN); }; @@ -374,8 +379,6 @@ struct wpan_dev { bool lbt; - bool promiscuous_mode; - /* fallback for acknowledgment bit setting */ bool ackreq; }; diff --git a/net/mac802154/cfg.c b/net/mac802154/cfg.c index 93df24f75572..dc2d918fac68 100644 --- a/net/mac802154/cfg.c +++ b/net/mac802154/cfg.c @@ -67,7 +67,7 @@ static int ieee802154_resume(struct wpan_phy *wpan_phy) goto wake_up; /* restart hardware */ - ret = drv_start(local); + ret = drv_start(local, local->phy->filtering, &local->addr_filt); if (ret) return ret; diff --git a/net/mac802154/driver-ops.h b/net/mac802154/driver-ops.h index c9d54088a567..a7af3f0ddb3e 100644 --- a/net/mac802154/driver-ops.h +++ b/net/mac802154/driver-ops.h @@ -129,12 +129,81 @@ drv_set_promiscuous_mode(struct ieee802154_local *local, bool on) return ret; } -static inline int drv_start(struct ieee802154_local *local) +static inline int drv_start(struct ieee802154_local *local, + enum ieee802154_filtering_level level, + const struct ieee802154_hw_addr_filt *addr_filt) { int ret; might_sleep(); + /* setup receive mode parameters e.g. address mode */ + if (local->hw.flags & IEEE802154_HW_AFILT) { + ret = drv_set_pan_id(local, addr_filt->pan_id); + if (ret < 0) + return ret; + + ret = drv_set_short_addr(local, addr_filt->short_addr); + if (ret < 0) + return ret; + + ret = drv_set_extended_addr(local, addr_filt->ieee_addr); + if (ret < 0) + return ret; + } + + switch (level) { + case IEEE802154_FILTERING_NONE: + fallthrough; + case IEEE802154_FILTERING_1_FCS: + fallthrough; + case IEEE802154_FILTERING_2_PROMISCUOUS: + /* TODO: Requires a different receive mode setup e.g. + * at86rf233 hardware. + */ + fallthrough; + case IEEE802154_FILTERING_3_SCAN: + if (local->hw.flags & IEEE802154_HW_PROMISCUOUS) { + ret = drv_set_promiscuous_mode(local, true); + if (ret < 0) + return ret; + } else { + return -EOPNOTSUPP; + } + + /* In practice other filtering levels can be requested, but as + * for now most hardware/drivers only support + * IEEE802154_FILTERING_NONE, we fallback to this actual + * filtering level in hardware and make our own additional + * filtering in mac802154 receive path. + * + * TODO: Move this logic to the device drivers as hardware may + * support more higher level filters. Hardware may also require + * a different order how register are set, which could currently + * be buggy, so all received parameters need to be moved to the + * start() callback and let the driver go into the mode before + * it will turn on receive handling. + */ + local->phy->filtering = IEEE802154_FILTERING_NONE; + break; + case IEEE802154_FILTERING_4_FRAME_FIELDS: + /* Do not error out if IEEE802154_HW_PROMISCUOUS because we + * expect the hardware to operate at the level + * IEEE802154_FILTERING_4_FRAME_FIELDS anyway. + */ + if (local->hw.flags & IEEE802154_HW_PROMISCUOUS) { + ret = drv_set_promiscuous_mode(local, false); + if (ret < 0) + return ret; + } + + local->phy->filtering = IEEE802154_FILTERING_4_FRAME_FIELDS; + break; + default: + WARN_ON(1); + return -EINVAL; + } + trace_802154_drv_start(local); local->started = true; smp_mb(); diff --git a/net/mac802154/ieee802154_i.h b/net/mac802154/ieee802154_i.h index 010365a6364e..509e0172fe82 100644 --- a/net/mac802154/ieee802154_i.h +++ b/net/mac802154/ieee802154_i.h @@ -26,6 +26,8 @@ struct ieee802154_local { struct ieee802154_hw hw; const struct ieee802154_ops *ops; + /* hardware address filter */ + struct ieee802154_hw_addr_filt addr_filt; /* ieee802154 phy */ struct wpan_phy *phy; @@ -82,6 +84,16 @@ struct ieee802154_sub_if_data { struct ieee802154_local *local; struct net_device *dev; + /* Each interface starts and works in nominal state at a given filtering + * level given by iface_default_filtering, which is set once for all at + * the interface creation and should not evolve over time. For some MAC + * operations however, the filtering level may change temporarily, as + * reflected in the required_filtering field. The actual filtering at + * the PHY level may be different and is shown in struct wpan_phy. + */ + enum ieee802154_filtering_level iface_default_filtering; + enum ieee802154_filtering_level required_filtering; + unsigned long state; char name[IFNAMSIZ]; diff --git a/net/mac802154/iface.c b/net/mac802154/iface.c index 500ed1b81250..d9b50884d34e 100644 --- a/net/mac802154/iface.c +++ b/net/mac802154/iface.c @@ -147,25 +147,12 @@ static int ieee802154_setup_hw(struct ieee802154_sub_if_data *sdata) struct wpan_dev *wpan_dev = &sdata->wpan_dev; int ret; - if (local->hw.flags & IEEE802154_HW_PROMISCUOUS) { - ret = drv_set_promiscuous_mode(local, - wpan_dev->promiscuous_mode); - if (ret < 0) - return ret; - } + sdata->required_filtering = sdata->iface_default_filtering; if (local->hw.flags & IEEE802154_HW_AFILT) { - ret = drv_set_pan_id(local, wpan_dev->pan_id); - if (ret < 0) - return ret; - - ret = drv_set_extended_addr(local, wpan_dev->extended_addr); - if (ret < 0) - return ret; - - ret = drv_set_short_addr(local, wpan_dev->short_addr); - if (ret < 0) - return ret; + local->addr_filt.pan_id = wpan_dev->pan_id; + local->addr_filt.ieee_addr = wpan_dev->extended_addr; + local->addr_filt.short_addr = wpan_dev->short_addr; } if (local->hw.flags & IEEE802154_HW_LBT) { @@ -206,7 +193,8 @@ static int mac802154_slave_open(struct net_device *dev) if (res) goto err; - res = drv_start(local); + res = drv_start(local, sdata->required_filtering, + &local->addr_filt); if (res) goto err; } @@ -223,15 +211,16 @@ err: static int ieee802154_check_mac_settings(struct ieee802154_local *local, - struct wpan_dev *wpan_dev, - struct wpan_dev *nwpan_dev) + struct ieee802154_sub_if_data *sdata, + struct ieee802154_sub_if_data *nsdata) { + struct wpan_dev *nwpan_dev = &nsdata->wpan_dev; + struct wpan_dev *wpan_dev = &sdata->wpan_dev; + ASSERT_RTNL(); - if (local->hw.flags & IEEE802154_HW_PROMISCUOUS) { - if (wpan_dev->promiscuous_mode != nwpan_dev->promiscuous_mode) - return -EBUSY; - } + if (sdata->iface_default_filtering != nsdata->iface_default_filtering) + return -EBUSY; if (local->hw.flags & IEEE802154_HW_AFILT) { if (wpan_dev->pan_id != nwpan_dev->pan_id || @@ -285,8 +274,7 @@ ieee802154_check_concurrent_iface(struct ieee802154_sub_if_data *sdata, /* check all phy mac sublayer settings are the same. * We have only one phy, different values makes trouble. */ - ret = ieee802154_check_mac_settings(local, wpan_dev, - &nsdata->wpan_dev); + ret = ieee802154_check_mac_settings(local, sdata, nsdata); if (ret < 0) return ret; } @@ -586,7 +574,7 @@ ieee802154_setup_sdata(struct ieee802154_sub_if_data *sdata, sdata->dev->priv_destructor = mac802154_wpan_free; sdata->dev->netdev_ops = &mac802154_wpan_ops; sdata->dev->ml_priv = &mac802154_mlme_wpan; - wpan_dev->promiscuous_mode = false; + sdata->iface_default_filtering = IEEE802154_FILTERING_4_FRAME_FIELDS; wpan_dev->header_ops = &ieee802154_header_ops; mutex_init(&sdata->sec_mtx); @@ -600,7 +588,7 @@ ieee802154_setup_sdata(struct ieee802154_sub_if_data *sdata, case NL802154_IFTYPE_MONITOR: sdata->dev->needs_free_netdev = true; sdata->dev->netdev_ops = &mac802154_monitor_ops; - wpan_dev->promiscuous_mode = true; + sdata->iface_default_filtering = IEEE802154_FILTERING_NONE; break; default: BUG(); diff --git a/net/mac802154/rx.c b/net/mac802154/rx.c index b8ce84618a55..8543c28948a0 100644 --- a/net/mac802154/rx.c +++ b/net/mac802154/rx.c @@ -268,10 +268,20 @@ void ieee802154_rx(struct ieee802154_local *local, struct sk_buff *skb) ieee802154_monitors_rx(local, skb); + /* TODO: Avoid delivering frames received at the level + * IEEE802154_FILTERING_NONE on interfaces not expecting it because of + * the missing auto ACK handling feature. + */ + + /* TODO: Handle upcomming receive path where the PHY is at the + * IEEE802154_FILTERING_NONE level during a scan. + */ + /* Check if transceiver doesn't validate the checksum. * If not we validate the checksum here. */ - if (local->hw.flags & IEEE802154_HW_RX_DROP_BAD_CKSUM) { + if (local->hw.flags & IEEE802154_HW_RX_DROP_BAD_CKSUM || + local->phy->filtering == IEEE802154_FILTERING_NONE) { crc = crc_ccitt(0, skb->data, skb->len); if (crc) { rcu_read_unlock(); From a87815b7bbcd10d7e113c366eb6310c5898ff952 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Fri, 7 Oct 2022 10:53:06 +0200 Subject: [PATCH 0138/2002] ieee802154: hwsim: Record the address filter values As a first step, introduce a basic implementation for the ->set_hw_addr_filt() hook. In a second step, the values recorded here will be used to perform proper filtering during reception. Signed-off-by: Miquel Raynal Acked-by: Alexander Aring Link: https://lore.kernel.org/r/20221007085310.503366-5-miquel.raynal@bootlin.com Signed-off-by: Stefan Schmidt --- drivers/net/ieee802154/mac802154_hwsim.c | 36 ++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/drivers/net/ieee802154/mac802154_hwsim.c b/drivers/net/ieee802154/mac802154_hwsim.c index 38c217bd7c82..458be66b5195 100644 --- a/drivers/net/ieee802154/mac802154_hwsim.c +++ b/drivers/net/ieee802154/mac802154_hwsim.c @@ -47,6 +47,7 @@ static const struct genl_multicast_group hwsim_mcgrps[] = { struct hwsim_pib { u8 page; u8 channel; + struct ieee802154_hw_addr_filt filt; struct rcu_head rcu; }; @@ -101,6 +102,38 @@ static int hwsim_hw_channel(struct ieee802154_hw *hw, u8 page, u8 channel) pib->channel = channel; pib_old = rtnl_dereference(phy->pib); + + pib->filt.short_addr = pib_old->filt.short_addr; + pib->filt.pan_id = pib_old->filt.pan_id; + pib->filt.ieee_addr = pib_old->filt.ieee_addr; + pib->filt.pan_coord = pib_old->filt.pan_coord; + + rcu_assign_pointer(phy->pib, pib); + kfree_rcu(pib_old, rcu); + return 0; +} + +static int hwsim_hw_addr_filt(struct ieee802154_hw *hw, + struct ieee802154_hw_addr_filt *filt, + unsigned long changed) +{ + struct hwsim_phy *phy = hw->priv; + struct hwsim_pib *pib, *pib_old; + + pib = kzalloc(sizeof(*pib), GFP_KERNEL); + if (!pib) + return -ENOMEM; + + pib_old = rtnl_dereference(phy->pib); + + pib->page = pib_old->page; + pib->channel = pib_old->channel; + + pib->filt.short_addr = filt->short_addr; + pib->filt.pan_id = filt->pan_id; + pib->filt.ieee_addr = filt->ieee_addr; + pib->filt.pan_coord = filt->pan_coord; + rcu_assign_pointer(phy->pib, pib); kfree_rcu(pib_old, rcu); return 0; @@ -172,6 +205,7 @@ static const struct ieee802154_ops hwsim_ops = { .start = hwsim_hw_start, .stop = hwsim_hw_stop, .set_promiscuous_mode = hwsim_set_promiscuous_mode, + .set_hw_addr_filt = hwsim_hw_addr_filt, }; static int hwsim_new_radio_nl(struct sk_buff *msg, struct genl_info *info) @@ -787,6 +821,8 @@ static int hwsim_add_one(struct genl_info *info, struct device *dev, } pib->channel = 13; + pib->filt.short_addr = cpu_to_le16(IEEE802154_ADDR_BROADCAST); + pib->filt.pan_id = cpu_to_le16(IEEE802154_PANID_BROADCAST); rcu_assign_pointer(phy->pib, pib); phy->idx = idx; INIT_LIST_HEAD(&phy->edges); From ea562d8c486eebd2707bcd193974078a2a47affc Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Fri, 7 Oct 2022 10:53:07 +0200 Subject: [PATCH 0139/2002] ieee802154: hwsim: Implement address filtering We have access to the address filters being theoretically applied, we also have access to the actual filtering level applied, so let's add a proper frame validation sequence in hwsim. Signed-off-by: Miquel Raynal Acked-by: Alexander Aring Link: https://lore.kernel.org/r/20221007085310.503366-6-miquel.raynal@bootlin.com [stefan@datenfreihafen.org: fixup some checkpatch warnings] Signed-off-by: Stefan Schmidt --- drivers/net/ieee802154/mac802154_hwsim.c | 110 ++++++++++++++++++++++- include/net/ieee802154_netdev.h | 8 ++ 2 files changed, 116 insertions(+), 2 deletions(-) diff --git a/drivers/net/ieee802154/mac802154_hwsim.c b/drivers/net/ieee802154/mac802154_hwsim.c index 458be66b5195..75d802e0b685 100644 --- a/drivers/net/ieee802154/mac802154_hwsim.c +++ b/drivers/net/ieee802154/mac802154_hwsim.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -139,6 +140,112 @@ static int hwsim_hw_addr_filt(struct ieee802154_hw *hw, return 0; } +static void hwsim_hw_receive(struct ieee802154_hw *hw, struct sk_buff *skb, + u8 lqi) +{ + struct ieee802154_hdr hdr; + struct hwsim_phy *phy = hw->priv; + struct hwsim_pib *pib; + + rcu_read_lock(); + pib = rcu_dereference(phy->pib); + + if (!pskb_may_pull(skb, 3)) { + dev_dbg(hw->parent, "invalid frame\n"); + goto drop; + } + + memcpy(&hdr, skb->data, 3); + + /* Level 4 filtering: Frame fields validity */ + if (hw->phy->filtering == IEEE802154_FILTERING_4_FRAME_FIELDS) { + /* a) Drop reserved frame types */ + switch (mac_cb(skb)->type) { + case IEEE802154_FC_TYPE_BEACON: + case IEEE802154_FC_TYPE_DATA: + case IEEE802154_FC_TYPE_ACK: + case IEEE802154_FC_TYPE_MAC_CMD: + break; + default: + dev_dbg(hw->parent, "unrecognized frame type 0x%x\n", + mac_cb(skb)->type); + goto drop; + } + + /* b) Drop reserved frame versions */ + switch (hdr.fc.version) { + case IEEE802154_2003_STD: + case IEEE802154_2006_STD: + case IEEE802154_STD: + break; + default: + dev_dbg(hw->parent, + "unrecognized frame version 0x%x\n", + hdr.fc.version); + goto drop; + } + + /* c) PAN ID constraints */ + if ((mac_cb(skb)->dest.mode == IEEE802154_ADDR_LONG || + mac_cb(skb)->dest.mode == IEEE802154_ADDR_SHORT) && + mac_cb(skb)->dest.pan_id != pib->filt.pan_id && + mac_cb(skb)->dest.pan_id != cpu_to_le16(IEEE802154_PANID_BROADCAST)) { + dev_dbg(hw->parent, + "unrecognized PAN ID %04x\n", + le16_to_cpu(mac_cb(skb)->dest.pan_id)); + goto drop; + } + + /* d1) Short address constraints */ + if (mac_cb(skb)->dest.mode == IEEE802154_ADDR_SHORT && + mac_cb(skb)->dest.short_addr != pib->filt.short_addr && + mac_cb(skb)->dest.short_addr != cpu_to_le16(IEEE802154_ADDR_BROADCAST)) { + dev_dbg(hw->parent, + "unrecognized short address %04x\n", + le16_to_cpu(mac_cb(skb)->dest.short_addr)); + goto drop; + } + + /* d2) Extended address constraints */ + if (mac_cb(skb)->dest.mode == IEEE802154_ADDR_LONG && + mac_cb(skb)->dest.extended_addr != pib->filt.ieee_addr) { + dev_dbg(hw->parent, + "unrecognized long address 0x%016llx\n", + mac_cb(skb)->dest.extended_addr); + goto drop; + } + + /* d4) Specific PAN coordinator case (no parent) */ + if ((mac_cb(skb)->type == IEEE802154_FC_TYPE_DATA || + mac_cb(skb)->type == IEEE802154_FC_TYPE_MAC_CMD) && + mac_cb(skb)->dest.mode == IEEE802154_ADDR_NONE) { + dev_dbg(hw->parent, + "relaying is not supported\n"); + goto drop; + } + + /* e) Beacon frames follow specific PAN ID rules */ + if (mac_cb(skb)->type == IEEE802154_FC_TYPE_BEACON && + pib->filt.pan_id != cpu_to_le16(IEEE802154_PANID_BROADCAST) && + mac_cb(skb)->dest.pan_id != pib->filt.pan_id) { + dev_dbg(hw->parent, + "invalid beacon PAN ID %04x\n", + le16_to_cpu(mac_cb(skb)->dest.pan_id)); + goto drop; + } + } + + rcu_read_unlock(); + + ieee802154_rx_irqsafe(hw, skb, lqi); + + return; + +drop: + rcu_read_unlock(); + kfree_skb(skb); +} + static int hwsim_hw_xmit(struct ieee802154_hw *hw, struct sk_buff *skb) { struct hwsim_phy *current_phy = hw->priv; @@ -166,8 +273,7 @@ static int hwsim_hw_xmit(struct ieee802154_hw *hw, struct sk_buff *skb) einfo = rcu_dereference(e->info); if (newskb) - ieee802154_rx_irqsafe(e->endpoint->hw, newskb, - einfo->lqi); + hwsim_hw_receive(e->endpoint->hw, newskb, einfo->lqi); } } rcu_read_unlock(); diff --git a/include/net/ieee802154_netdev.h b/include/net/ieee802154_netdev.h index d0d188c3294b..1b82bbafe8c7 100644 --- a/include/net/ieee802154_netdev.h +++ b/include/net/ieee802154_netdev.h @@ -69,6 +69,14 @@ struct ieee802154_hdr_fc { #endif }; +enum ieee802154_frame_version { + IEEE802154_2003_STD, + IEEE802154_2006_STD, + IEEE802154_STD, + IEEE802154_RESERVED_STD, + IEEE802154_MULTIPURPOSE_STD = IEEE802154_2003_STD, +}; + struct ieee802154_hdr { struct ieee802154_hdr_fc fc; u8 seq; From a4b5b4c56dd8b1dd46b2f13cb09f5f8031978f86 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Fri, 7 Oct 2022 10:53:08 +0200 Subject: [PATCH 0140/2002] mac802154: Drop IEEE802154_HW_RX_DROP_BAD_CKSUM This IEEE802154_HW_RX_DROP_BAD_CKSUM flag was only used by hwsim to reflect the fact that it would not validate the checksum (FCS). So this was only useful while the only filtering level hwsim was capable of was "NONE". Now that the driver has been improved we no longer need this flag. Signed-off-by: Miquel Raynal Acked-by: Alexander Aring Link: https://lore.kernel.org/r/20221007085310.503366-7-miquel.raynal@bootlin.com Signed-off-by: Stefan Schmidt --- drivers/net/ieee802154/mac802154_hwsim.c | 3 ++- include/net/mac802154.h | 4 ---- net/mac802154/rx.c | 7 ++----- 3 files changed, 4 insertions(+), 10 deletions(-) diff --git a/drivers/net/ieee802154/mac802154_hwsim.c b/drivers/net/ieee802154/mac802154_hwsim.c index 75d802e0b685..1db7da3ccc1a 100644 --- a/drivers/net/ieee802154/mac802154_hwsim.c +++ b/drivers/net/ieee802154/mac802154_hwsim.c @@ -287,6 +287,7 @@ static int hwsim_hw_start(struct ieee802154_hw *hw) struct hwsim_phy *phy = hw->priv; phy->suspended = false; + return 0; } @@ -933,7 +934,7 @@ static int hwsim_add_one(struct genl_info *info, struct device *dev, phy->idx = idx; INIT_LIST_HEAD(&phy->edges); - hw->flags = IEEE802154_HW_PROMISCUOUS | IEEE802154_HW_RX_DROP_BAD_CKSUM; + hw->flags = IEEE802154_HW_PROMISCUOUS; hw->parent = dev; err = ieee802154_register_hw(hw); diff --git a/include/net/mac802154.h b/include/net/mac802154.h index 357d25ef627a..4a3a9de9da73 100644 --- a/include/net/mac802154.h +++ b/include/net/mac802154.h @@ -111,9 +111,6 @@ struct ieee802154_hw { * promiscuous mode setting. * * @IEEE802154_HW_RX_OMIT_CKSUM: Indicates that receiver omits FCS. - * - * @IEEE802154_HW_RX_DROP_BAD_CKSUM: Indicates that receiver will not filter - * frames with bad checksum. */ enum ieee802154_hw_flags { IEEE802154_HW_TX_OMIT_CKSUM = BIT(0), @@ -123,7 +120,6 @@ enum ieee802154_hw_flags { IEEE802154_HW_AFILT = BIT(4), IEEE802154_HW_PROMISCUOUS = BIT(5), IEEE802154_HW_RX_OMIT_CKSUM = BIT(6), - IEEE802154_HW_RX_DROP_BAD_CKSUM = BIT(7), }; /* Indicates that receiver omits FCS and xmitter will add FCS on it's own. */ diff --git a/net/mac802154/rx.c b/net/mac802154/rx.c index 8543c28948a0..80dd52bc6bf1 100644 --- a/net/mac802154/rx.c +++ b/net/mac802154/rx.c @@ -277,11 +277,8 @@ void ieee802154_rx(struct ieee802154_local *local, struct sk_buff *skb) * IEEE802154_FILTERING_NONE level during a scan. */ - /* Check if transceiver doesn't validate the checksum. - * If not we validate the checksum here. - */ - if (local->hw.flags & IEEE802154_HW_RX_DROP_BAD_CKSUM || - local->phy->filtering == IEEE802154_FILTERING_NONE) { + /* Level 1 filtering: Check the FCS by software when relevant */ + if (local->hw.phy->filtering == IEEE802154_FILTERING_NONE) { crc = crc_ccitt(0, skb->data, skb->len); if (crc) { rcu_read_unlock(); From 0218277df5a527c9e9474aa0e4e4e2f4bbc6f5cc Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Fri, 7 Oct 2022 10:53:09 +0200 Subject: [PATCH 0141/2002] mac802154: Avoid delivering frames received in a non satisfying filtering mode We must avoid the situation where one interface disables address filtering and AACK on the PHY while another interface expects to run with AACK and address filtering enabled. Just ignore the frames on the concerned interface if this happens. Signed-off-by: Miquel Raynal Acked-by: Alexander Aring Link: https://lore.kernel.org/r/20221007085310.503366-8-miquel.raynal@bootlin.com Signed-off-by: Stefan Schmidt --- net/mac802154/rx.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/net/mac802154/rx.c b/net/mac802154/rx.c index 80dd52bc6bf1..6640bd237352 100644 --- a/net/mac802154/rx.c +++ b/net/mac802154/rx.c @@ -209,6 +209,13 @@ __ieee802154_rx_handle_packet(struct ieee802154_local *local, if (!ieee802154_sdata_running(sdata)) continue; + /* Do not deliver packets received on interfaces expecting + * AACK=1 if the address filters where disabled. + */ + if (local->hw.phy->filtering < IEEE802154_FILTERING_4_FRAME_FIELDS && + sdata->required_filtering == IEEE802154_FILTERING_4_FRAME_FIELDS) + continue; + ieee802154_subif_frame(sdata, skb, &hdr); skb = NULL; break; @@ -268,11 +275,6 @@ void ieee802154_rx(struct ieee802154_local *local, struct sk_buff *skb) ieee802154_monitors_rx(local, skb); - /* TODO: Avoid delivering frames received at the level - * IEEE802154_FILTERING_NONE on interfaces not expecting it because of - * the missing auto ACK handling feature. - */ - /* TODO: Handle upcomming receive path where the PHY is at the * IEEE802154_FILTERING_NONE level during a scan. */ From 3a22550ab50a2bed1779c7d03c9f0239d33cc514 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Mon, 5 Sep 2022 22:27:24 +0200 Subject: [PATCH 0142/2002] net: mac802154: Avoid displaying misleading debug information With DEBUG defined, any frame received will see its MHR fields (fc and addresses, mainly) being printed in the kernel log buffer, unconditionally. In most cases this is fine, but in some specific cases (like Acknowledgment frames, where both the source and destination addressing fields are omitted), it displays garbage which is misleading. Only print the addressing fields when they are present, which clarifies the logs. Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/r/20220905202724.1322046-1-miquel.raynal@bootlin.com Signed-off-by: Stefan Schmidt --- net/mac802154/rx.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/net/mac802154/rx.c b/net/mac802154/rx.c index 6640bd237352..d1f7b8df41fe 100644 --- a/net/mac802154/rx.c +++ b/net/mac802154/rx.c @@ -114,8 +114,10 @@ fail: static void ieee802154_print_addr(const char *name, const struct ieee802154_addr *addr) { - if (addr->mode == IEEE802154_ADDR_NONE) + if (addr->mode == IEEE802154_ADDR_NONE) { pr_debug("%s not present\n", name); + return; + } pr_debug("%s PAN ID: %04x\n", name, le16_to_cpu(addr->pan_id)); if (addr->mode == IEEE802154_ADDR_SHORT) { From f8be91fbfc7dd2d69762d510cc29c3b52d3ef4db Mon Sep 17 00:00:00 2001 From: Alexander Aring Date: Mon, 5 Sep 2022 22:34:12 +0200 Subject: [PATCH 0143/2002] ieee802154: atusb: add support for trac feature This patch adds support for reading the trac register if atusb firmware reports tx done. There is currently a feature to compare a sequence number, if the payload is 1 it tells the driver only the sequence number is available if it's two there is additional the trac status register as payload. Currently the atusb_in_good() function determines if it's a tx done or rx done if according the payload length. This patch is doing the same and assumes this behaviour. Signed-off-by: Alexander Aring Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/r/20220905203412.1322947-10-miquel.raynal@bootlin.com Signed-off-by: Stefan Schmidt --- drivers/net/ieee802154/atusb.c | 33 ++++++++++++++++++++++++++++----- 1 file changed, 28 insertions(+), 5 deletions(-) diff --git a/drivers/net/ieee802154/atusb.c b/drivers/net/ieee802154/atusb.c index 2c338783893d..95a4a3cdc8a4 100644 --- a/drivers/net/ieee802154/atusb.c +++ b/drivers/net/ieee802154/atusb.c @@ -191,7 +191,7 @@ static void atusb_work_urbs(struct work_struct *work) /* ----- Asynchronous USB -------------------------------------------------- */ -static void atusb_tx_done(struct atusb *atusb, u8 seq) +static void atusb_tx_done(struct atusb *atusb, u8 seq, int reason) { struct usb_device *usb_dev = atusb->usb_dev; u8 expect = atusb->tx_ack_seq; @@ -199,7 +199,10 @@ static void atusb_tx_done(struct atusb *atusb, u8 seq) dev_dbg(&usb_dev->dev, "%s (0x%02x/0x%02x)\n", __func__, seq, expect); if (seq == expect) { /* TODO check for ifs handling in firmware */ - ieee802154_xmit_complete(atusb->hw, atusb->tx_skb, false); + if (reason == IEEE802154_SUCCESS) + ieee802154_xmit_complete(atusb->hw, atusb->tx_skb, false); + else + ieee802154_xmit_error(atusb->hw, atusb->tx_skb, reason); } else { /* TODO I experience this case when atusb has a tx complete * irq before probing, we should fix the firmware it's an @@ -215,7 +218,8 @@ static void atusb_in_good(struct urb *urb) struct usb_device *usb_dev = urb->dev; struct sk_buff *skb = urb->context; struct atusb *atusb = SKB_ATUSB(skb); - u8 len, lqi; + int result = IEEE802154_SUCCESS; + u8 len, lqi, trac; if (!urb->actual_length) { dev_dbg(&usb_dev->dev, "atusb_in: zero-sized URB ?\n"); @@ -224,8 +228,27 @@ static void atusb_in_good(struct urb *urb) len = *skb->data; - if (urb->actual_length == 1) { - atusb_tx_done(atusb, len); + switch (urb->actual_length) { + case 2: + trac = TRAC_MASK(*(skb->data + 1)); + switch (trac) { + case TRAC_SUCCESS: + case TRAC_SUCCESS_DATA_PENDING: + /* already IEEE802154_SUCCESS */ + break; + case TRAC_CHANNEL_ACCESS_FAILURE: + result = IEEE802154_CHANNEL_ACCESS_FAILURE; + break; + case TRAC_NO_ACK: + result = IEEE802154_NO_ACK; + break; + default: + result = IEEE802154_SYSTEM_ERROR; + } + + fallthrough; + case 1: + atusb_tx_done(atusb, len, result); return; } From 3811fa1f231f1a3e29759efef4992116604aab8b Mon Sep 17 00:00:00 2001 From: Sowmiya Sree Elavalagan Date: Tue, 11 Oct 2022 15:23:46 +0530 Subject: [PATCH 0144/2002] wifi: ath11k: Fix firmware crash on vdev delete race condition Current code does not wait for vdev delete completion on vdev create failures and tries to send another vdev create followed by vdev set param to firmware with same vdev id. This causes firmware crash. Fix this crash by waiting for vdev delete completion on vdev create failures. Tested-on: IPQ8074 hw2.0 AHB WLAN.HK.2.6.0.1-00905-QCAHKSWPL_SILICONZ-1 Signed-off-by: Sowmiya Sree Elavalagan Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20221011095346.3901-1-quic_ssreeela@quicinc.com --- drivers/net/wireless/ath/ath11k/mac.c | 60 +++++++++++++++++---------- 1 file changed, 37 insertions(+), 23 deletions(-) diff --git a/drivers/net/wireless/ath/ath11k/mac.c b/drivers/net/wireless/ath/ath11k/mac.c index ce15693c1804..6d83a178a891 100644 --- a/drivers/net/wireless/ath/ath11k/mac.c +++ b/drivers/net/wireless/ath/ath11k/mac.c @@ -6232,6 +6232,40 @@ void ath11k_mac_11d_scan_stop_all(struct ath11k_base *ab) } } +static int ath11k_mac_vdev_delete(struct ath11k *ar, struct ath11k_vif *arvif) +{ + unsigned long time_left; + struct ieee80211_vif *vif = arvif->vif; + int ret = 0; + + lockdep_assert_held(&ar->conf_mutex); + + reinit_completion(&ar->vdev_delete_done); + + ret = ath11k_wmi_vdev_delete(ar, arvif->vdev_id); + if (ret) { + ath11k_warn(ar->ab, "failed to delete WMI vdev %d: %d\n", + arvif->vdev_id, ret); + return ret; + } + + time_left = wait_for_completion_timeout(&ar->vdev_delete_done, + ATH11K_VDEV_DELETE_TIMEOUT_HZ); + if (time_left == 0) { + ath11k_warn(ar->ab, "Timeout in receiving vdev delete response\n"); + return -ETIMEDOUT; + } + + ar->ab->free_vdev_map |= 1LL << (arvif->vdev_id); + ar->allocated_vdev_map &= ~(1LL << arvif->vdev_id); + ar->num_created_vdevs--; + + ath11k_dbg(ar->ab, ATH11K_DBG_MAC, "vdev %pM deleted, vdev_id %d\n", + vif->addr, arvif->vdev_id); + + return ret; +} + static int ath11k_mac_op_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { @@ -6467,10 +6501,7 @@ err_peer_del: } err_vdev_del: - ath11k_wmi_vdev_delete(ar, arvif->vdev_id); - ar->num_created_vdevs--; - ar->allocated_vdev_map &= ~(1LL << arvif->vdev_id); - ab->free_vdev_map |= 1LL << arvif->vdev_id; + ath11k_mac_vdev_delete(ar, arvif); spin_lock_bh(&ar->data_lock); list_del(&arvif->list); spin_unlock_bh(&ar->data_lock); @@ -6498,7 +6529,6 @@ static void ath11k_mac_op_remove_interface(struct ieee80211_hw *hw, struct ath11k *ar = hw->priv; struct ath11k_vif *arvif = ath11k_vif_to_arvif(vif); struct ath11k_base *ab = ar->ab; - unsigned long time_left; int ret; int i; @@ -6519,29 +6549,13 @@ static void ath11k_mac_op_remove_interface(struct ieee80211_hw *hw, arvif->vdev_id, ret); } - reinit_completion(&ar->vdev_delete_done); - - ret = ath11k_wmi_vdev_delete(ar, arvif->vdev_id); + ret = ath11k_mac_vdev_delete(ar, arvif); if (ret) { - ath11k_warn(ab, "failed to delete WMI vdev %d: %d\n", + ath11k_warn(ab, "failed to delete vdev %d: %d\n", arvif->vdev_id, ret); goto err_vdev_del; } - time_left = wait_for_completion_timeout(&ar->vdev_delete_done, - ATH11K_VDEV_DELETE_TIMEOUT_HZ); - if (time_left == 0) { - ath11k_warn(ab, "Timeout in receiving vdev delete response\n"); - goto err_vdev_del; - } - - ab->free_vdev_map |= 1LL << (arvif->vdev_id); - ar->allocated_vdev_map &= ~(1LL << arvif->vdev_id); - ar->num_created_vdevs--; - - ath11k_dbg(ab, ATH11K_DBG_MAC, "vdev %pM deleted, vdev_id %d\n", - vif->addr, arvif->vdev_id); - if (arvif->vdev_type == WMI_VDEV_TYPE_MONITOR) { clear_bit(ATH11K_FLAG_MONITOR_VDEV_CREATED, &ar->monitor_flags); ar->monitor_vdev_id = -1; From 473118917cc33b98510880458c724bd833653db6 Mon Sep 17 00:00:00 2001 From: Peter Kosyh Date: Mon, 3 Oct 2022 12:12:17 +0300 Subject: [PATCH 0145/2002] wifi: ath10k: Check return value of ath10k_get_arvif() in ath10k_wmi_event_tdls_peer() Return value of a function ath10k_get_arvif() is dereferenced without checking for null in ath10k_wmi_event_tdls_peer(), but it is usually checked for this function. Make ath10k_wmi_event_tdls_peer() do check retval of ath10k_get_arvif(). Found by Linux Verification Center (linuxtesting.org) with SVACE. Signed-off-by: Peter Kosyh Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20221003091217.322598-1-pkosyh@yandex.ru --- drivers/net/wireless/ath/ath10k/wmi-tlv.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/net/wireless/ath/ath10k/wmi-tlv.c b/drivers/net/wireless/ath/ath10k/wmi-tlv.c index 876410a47d1d..6b6aa3c36744 100644 --- a/drivers/net/wireless/ath/ath10k/wmi-tlv.c +++ b/drivers/net/wireless/ath/ath10k/wmi-tlv.c @@ -584,7 +584,14 @@ static void ath10k_wmi_event_tdls_peer(struct ath10k *ar, struct sk_buff *skb) ath10k_warn(ar, "did not find station from tdls peer event"); goto exit; } + arvif = ath10k_get_arvif(ar, __le32_to_cpu(ev->vdev_id)); + if (!arvif) { + ath10k_warn(ar, "no vif for vdev_id %d found", + __le32_to_cpu(ev->vdev_id)); + goto exit; + } + ieee80211_tdls_oper_request( arvif->vif, station->addr, NL80211_TDLS_TEARDOWN, From de9c8d848d90cf2e53aced50b350827442ca5a4f Mon Sep 17 00:00:00 2001 From: Martin KaFai Lau Date: Wed, 12 Oct 2022 15:12:35 -0700 Subject: [PATCH 0146/2002] selftests/bpf: S/iptables/iptables-legacy/ in the bpf_nf and xdp_synproxy test The recent vm image in CI has reported error in selftests that use the iptables command. Manu Bretelle has pointed out the difference in the recent vm image that the iptables is sym-linked to the iptables-nft. With this knowledge, I can also reproduce the CI error by manually running with the 'iptables-nft'. This patch is to replace the iptables command with iptables-legacy to unblock the CI tests. Signed-off-by: Martin KaFai Lau Signed-off-by: Andrii Nakryiko Acked-by: David Vernet Link: https://lore.kernel.org/bpf/20221012221235.3529719-1-martin.lau@linux.dev --- tools/testing/selftests/bpf/prog_tests/bpf_nf.c | 6 +++--- tools/testing/selftests/bpf/prog_tests/xdp_synproxy.c | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/tools/testing/selftests/bpf/prog_tests/bpf_nf.c b/tools/testing/selftests/bpf/prog_tests/bpf_nf.c index 8a838ea8bdf3..c8ba4009e4ab 100644 --- a/tools/testing/selftests/bpf/prog_tests/bpf_nf.c +++ b/tools/testing/selftests/bpf/prog_tests/bpf_nf.c @@ -49,14 +49,14 @@ out: static void test_bpf_nf_ct(int mode) { - const char *iptables = "iptables -t raw %s PREROUTING -j CONNMARK --set-mark 42/0"; + const char *iptables = "iptables-legacy -t raw %s PREROUTING -j CONNMARK --set-mark 42/0"; int srv_fd = -1, client_fd = -1, srv_client_fd = -1; struct sockaddr_in peer_addr = {}; struct test_bpf_nf *skel; int prog_fd, err; socklen_t len; u16 srv_port; - char cmd[64]; + char cmd[128]; LIBBPF_OPTS(bpf_test_run_opts, topts, .data_in = &pkt_v4, .data_size_in = sizeof(pkt_v4), @@ -69,7 +69,7 @@ static void test_bpf_nf_ct(int mode) /* Enable connection tracking */ snprintf(cmd, sizeof(cmd), iptables, "-A"); - if (!ASSERT_OK(system(cmd), "iptables")) + if (!ASSERT_OK(system(cmd), cmd)) goto end; srv_port = (mode == TEST_XDP) ? 5005 : 5006; diff --git a/tools/testing/selftests/bpf/prog_tests/xdp_synproxy.c b/tools/testing/selftests/bpf/prog_tests/xdp_synproxy.c index 75550a40e029..c72083885b6d 100644 --- a/tools/testing/selftests/bpf/prog_tests/xdp_synproxy.c +++ b/tools/testing/selftests/bpf/prog_tests/xdp_synproxy.c @@ -94,12 +94,12 @@ static void test_synproxy(bool xdp) SYS("sysctl -w net.ipv4.tcp_syncookies=2"); SYS("sysctl -w net.ipv4.tcp_timestamps=1"); SYS("sysctl -w net.netfilter.nf_conntrack_tcp_loose=0"); - SYS("iptables -t raw -I PREROUTING \ + SYS("iptables-legacy -t raw -I PREROUTING \ -i tmp1 -p tcp -m tcp --syn --dport 8080 -j CT --notrack"); - SYS("iptables -t filter -A INPUT \ + SYS("iptables-legacy -t filter -A INPUT \ -i tmp1 -p tcp -m tcp --dport 8080 -m state --state INVALID,UNTRACKED \ -j SYNPROXY --sack-perm --timestamp --wscale 7 --mss 1460"); - SYS("iptables -t filter -A INPUT \ + SYS("iptables-legacy -t filter -A INPUT \ -i tmp1 -m state --state INVALID -j DROP"); ctrl_file = SYS_OUT("./xdp_synproxy --iface tmp1 --ports 8080 \ From 93c660ca40b5d2f7c1b1626e955a8e9fa30e0749 Mon Sep 17 00:00:00 2001 From: Xu Kuohai Date: Tue, 11 Oct 2022 08:01:03 -0400 Subject: [PATCH 0147/2002] libbpf: Fix use-after-free in btf_dump_name_dups ASAN reports an use-after-free in btf_dump_name_dups: ERROR: AddressSanitizer: heap-use-after-free on address 0xffff927006db at pc 0xaaaab5dfb618 bp 0xffffdd89b890 sp 0xffffdd89b928 READ of size 2 at 0xffff927006db thread T0 #0 0xaaaab5dfb614 in __interceptor_strcmp.part.0 (test_progs+0x21b614) #1 0xaaaab635f144 in str_equal_fn tools/lib/bpf/btf_dump.c:127 #2 0xaaaab635e3e0 in hashmap_find_entry tools/lib/bpf/hashmap.c:143 #3 0xaaaab635e72c in hashmap__find tools/lib/bpf/hashmap.c:212 #4 0xaaaab6362258 in btf_dump_name_dups tools/lib/bpf/btf_dump.c:1525 #5 0xaaaab636240c in btf_dump_resolve_name tools/lib/bpf/btf_dump.c:1552 #6 0xaaaab6362598 in btf_dump_type_name tools/lib/bpf/btf_dump.c:1567 #7 0xaaaab6360b48 in btf_dump_emit_struct_def tools/lib/bpf/btf_dump.c:912 #8 0xaaaab6360630 in btf_dump_emit_type tools/lib/bpf/btf_dump.c:798 #9 0xaaaab635f720 in btf_dump__dump_type tools/lib/bpf/btf_dump.c:282 #10 0xaaaab608523c in test_btf_dump_incremental tools/testing/selftests/bpf/prog_tests/btf_dump.c:236 #11 0xaaaab6097530 in test_btf_dump tools/testing/selftests/bpf/prog_tests/btf_dump.c:875 #12 0xaaaab6314ed0 in run_one_test tools/testing/selftests/bpf/test_progs.c:1062 #13 0xaaaab631a0a8 in main tools/testing/selftests/bpf/test_progs.c:1697 #14 0xffff9676d214 in __libc_start_main ../csu/libc-start.c:308 #15 0xaaaab5d65990 (test_progs+0x185990) 0xffff927006db is located 11 bytes inside of 16-byte region [0xffff927006d0,0xffff927006e0) freed by thread T0 here: #0 0xaaaab5e2c7c4 in realloc (test_progs+0x24c7c4) #1 0xaaaab634f4a0 in libbpf_reallocarray tools/lib/bpf/libbpf_internal.h:191 #2 0xaaaab634f840 in libbpf_add_mem tools/lib/bpf/btf.c:163 #3 0xaaaab636643c in strset_add_str_mem tools/lib/bpf/strset.c:106 #4 0xaaaab6366560 in strset__add_str tools/lib/bpf/strset.c:157 #5 0xaaaab6352d70 in btf__add_str tools/lib/bpf/btf.c:1519 #6 0xaaaab6353e10 in btf__add_field tools/lib/bpf/btf.c:2032 #7 0xaaaab6084fcc in test_btf_dump_incremental tools/testing/selftests/bpf/prog_tests/btf_dump.c:232 #8 0xaaaab6097530 in test_btf_dump tools/testing/selftests/bpf/prog_tests/btf_dump.c:875 #9 0xaaaab6314ed0 in run_one_test tools/testing/selftests/bpf/test_progs.c:1062 #10 0xaaaab631a0a8 in main tools/testing/selftests/bpf/test_progs.c:1697 #11 0xffff9676d214 in __libc_start_main ../csu/libc-start.c:308 #12 0xaaaab5d65990 (test_progs+0x185990) previously allocated by thread T0 here: #0 0xaaaab5e2c7c4 in realloc (test_progs+0x24c7c4) #1 0xaaaab634f4a0 in libbpf_reallocarray tools/lib/bpf/libbpf_internal.h:191 #2 0xaaaab634f840 in libbpf_add_mem tools/lib/bpf/btf.c:163 #3 0xaaaab636643c in strset_add_str_mem tools/lib/bpf/strset.c:106 #4 0xaaaab6366560 in strset__add_str tools/lib/bpf/strset.c:157 #5 0xaaaab6352d70 in btf__add_str tools/lib/bpf/btf.c:1519 #6 0xaaaab6353ff0 in btf_add_enum_common tools/lib/bpf/btf.c:2070 #7 0xaaaab6354080 in btf__add_enum tools/lib/bpf/btf.c:2102 #8 0xaaaab6082f50 in test_btf_dump_incremental tools/testing/selftests/bpf/prog_tests/btf_dump.c:162 #9 0xaaaab6097530 in test_btf_dump tools/testing/selftests/bpf/prog_tests/btf_dump.c:875 #10 0xaaaab6314ed0 in run_one_test tools/testing/selftests/bpf/test_progs.c:1062 #11 0xaaaab631a0a8 in main tools/testing/selftests/bpf/test_progs.c:1697 #12 0xffff9676d214 in __libc_start_main ../csu/libc-start.c:308 #13 0xaaaab5d65990 (test_progs+0x185990) The reason is that the key stored in hash table name_map is a string address, and the string memory is allocated by realloc() function, when the memory is resized by realloc() later, the old memory may be freed, so the address stored in name_map references to a freed memory, causing use-after-free. Fix it by storing duplicated string address in name_map. Fixes: 919d2b1dbb07 ("libbpf: Allow modification of BTF and add btf__add_str API") Signed-off-by: Xu Kuohai Signed-off-by: Andrii Nakryiko Acked-by: Martin KaFai Lau Link: https://lore.kernel.org/bpf/20221011120108.782373-2-xukuohai@huaweicloud.com --- tools/lib/bpf/btf_dump.c | 29 ++++++++++++++++++++++++++--- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/tools/lib/bpf/btf_dump.c b/tools/lib/bpf/btf_dump.c index e4da6de68d8f..bf0cc0e986dd 100644 --- a/tools/lib/bpf/btf_dump.c +++ b/tools/lib/bpf/btf_dump.c @@ -219,6 +219,17 @@ static int btf_dump_resize(struct btf_dump *d) return 0; } +static void btf_dump_free_names(struct hashmap *map) +{ + size_t bkt; + struct hashmap_entry *cur; + + hashmap__for_each_entry(map, cur, bkt) + free((void *)cur->key); + + hashmap__free(map); +} + void btf_dump__free(struct btf_dump *d) { int i; @@ -237,8 +248,8 @@ void btf_dump__free(struct btf_dump *d) free(d->cached_names); free(d->emit_queue); free(d->decl_stack); - hashmap__free(d->type_names); - hashmap__free(d->ident_names); + btf_dump_free_names(d->type_names); + btf_dump_free_names(d->ident_names); free(d); } @@ -1524,11 +1535,23 @@ static void btf_dump_emit_type_cast(struct btf_dump *d, __u32 id, static size_t btf_dump_name_dups(struct btf_dump *d, struct hashmap *name_map, const char *orig_name) { + char *old_name, *new_name; size_t dup_cnt = 0; + int err; + + new_name = strdup(orig_name); + if (!new_name) + return 1; hashmap__find(name_map, orig_name, (void **)&dup_cnt); dup_cnt++; - hashmap__set(name_map, orig_name, (void *)dup_cnt, NULL, NULL); + + err = hashmap__set(name_map, new_name, (void *)dup_cnt, + (const void **)&old_name, NULL); + if (err) + free(new_name); + + free(old_name); return dup_cnt; } From 0dc9254e03704c75f2ebc9cbef2ce4de83fba603 Mon Sep 17 00:00:00 2001 From: Xu Kuohai Date: Tue, 11 Oct 2022 08:01:04 -0400 Subject: [PATCH 0148/2002] libbpf: Fix memory leak in parse_usdt_arg() In the arm64 version of parse_usdt_arg(), when sscanf returns 2, reg_name is allocated but not freed. Fix it. Fixes: 0f8619929c57 ("libbpf: Usdt aarch64 arg parsing support") Signed-off-by: Xu Kuohai Signed-off-by: Andrii Nakryiko Acked-by: Martin KaFai Lau Link: https://lore.kernel.org/bpf/20221011120108.782373-3-xukuohai@huaweicloud.com --- tools/lib/bpf/usdt.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/tools/lib/bpf/usdt.c b/tools/lib/bpf/usdt.c index e83b497c2245..49f3c3b7f609 100644 --- a/tools/lib/bpf/usdt.c +++ b/tools/lib/bpf/usdt.c @@ -1348,25 +1348,23 @@ static int calc_pt_regs_off(const char *reg_name) static int parse_usdt_arg(const char *arg_str, int arg_num, struct usdt_arg_spec *arg) { - char *reg_name = NULL; + char reg_name[16]; int arg_sz, len, reg_off; long off; - if (sscanf(arg_str, " %d @ \[ %m[a-z0-9], %ld ] %n", &arg_sz, ®_name, &off, &len) == 3) { + if (sscanf(arg_str, " %d @ \[ %15[a-z0-9], %ld ] %n", &arg_sz, reg_name, &off, &len) == 3) { /* Memory dereference case, e.g., -4@[sp, 96] */ arg->arg_type = USDT_ARG_REG_DEREF; arg->val_off = off; reg_off = calc_pt_regs_off(reg_name); - free(reg_name); if (reg_off < 0) return reg_off; arg->reg_off = reg_off; - } else if (sscanf(arg_str, " %d @ \[ %m[a-z0-9] ] %n", &arg_sz, ®_name, &len) == 2) { + } else if (sscanf(arg_str, " %d @ \[ %15[a-z0-9] ] %n", &arg_sz, reg_name, &len) == 2) { /* Memory dereference case, e.g., -4@[sp] */ arg->arg_type = USDT_ARG_REG_DEREF; arg->val_off = 0; reg_off = calc_pt_regs_off(reg_name); - free(reg_name); if (reg_off < 0) return reg_off; arg->reg_off = reg_off; @@ -1375,12 +1373,11 @@ static int parse_usdt_arg(const char *arg_str, int arg_num, struct usdt_arg_spec arg->arg_type = USDT_ARG_CONST; arg->val_off = off; arg->reg_off = 0; - } else if (sscanf(arg_str, " %d @ %m[a-z0-9] %n", &arg_sz, ®_name, &len) == 2) { + } else if (sscanf(arg_str, " %d @ %15[a-z0-9] %n", &arg_sz, reg_name, &len) == 2) { /* Register read case, e.g., -8@x4 */ arg->arg_type = USDT_ARG_REG; arg->val_off = 0; reg_off = calc_pt_regs_off(reg_name); - free(reg_name); if (reg_off < 0) return reg_off; arg->reg_off = reg_off; From 6e8280b958c5d7edc514cf347a800b23b7732b2b Mon Sep 17 00:00:00 2001 From: Xu Kuohai Date: Tue, 11 Oct 2022 08:01:05 -0400 Subject: [PATCH 0149/2002] selftests/bpf: Fix memory leak caused by not destroying skeleton Some test cases does not destroy skeleton object correctly, causing ASAN to report memory leak warning. Fix it. Fixes: 0ef6740e9777 ("selftests/bpf: Add tests for kptr_ref refcounting") Fixes: 1642a3945e22 ("selftests/bpf: Add struct argument tests with fentry/fexit programs.") Signed-off-by: Xu Kuohai Signed-off-by: Andrii Nakryiko Acked-by: Martin KaFai Lau Link: https://lore.kernel.org/bpf/20221011120108.782373-4-xukuohai@huaweicloud.com --- tools/testing/selftests/bpf/prog_tests/map_kptr.c | 3 ++- tools/testing/selftests/bpf/prog_tests/tracing_struct.c | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/tools/testing/selftests/bpf/prog_tests/map_kptr.c b/tools/testing/selftests/bpf/prog_tests/map_kptr.c index fdcea7a61491..0d66b1524208 100644 --- a/tools/testing/selftests/bpf/prog_tests/map_kptr.c +++ b/tools/testing/selftests/bpf/prog_tests/map_kptr.c @@ -105,7 +105,7 @@ static void test_map_kptr_success(bool test_run) ASSERT_OK(opts.retval, "test_map_kptr_ref2 retval"); if (test_run) - return; + goto exit; ret = bpf_map__update_elem(skel->maps.array_map, &key, sizeof(key), buf, sizeof(buf), 0); @@ -132,6 +132,7 @@ static void test_map_kptr_success(bool test_run) ret = bpf_map__delete_elem(skel->maps.lru_hash_map, &key, sizeof(key), 0); ASSERT_OK(ret, "lru_hash_map delete"); +exit: map_kptr__destroy(skel); } diff --git a/tools/testing/selftests/bpf/prog_tests/tracing_struct.c b/tools/testing/selftests/bpf/prog_tests/tracing_struct.c index d5022b91d1e4..48dc9472e160 100644 --- a/tools/testing/selftests/bpf/prog_tests/tracing_struct.c +++ b/tools/testing/selftests/bpf/prog_tests/tracing_struct.c @@ -15,7 +15,7 @@ static void test_fentry(void) err = tracing_struct__attach(skel); if (!ASSERT_OK(err, "tracing_struct__attach")) - return; + goto destroy_skel; ASSERT_OK(trigger_module_test_read(256), "trigger_read"); @@ -54,6 +54,7 @@ static void test_fentry(void) ASSERT_EQ(skel->bss->t5_ret, 1, "t5 ret"); tracing_struct__detach(skel); +destroy_skel: tracing_struct__destroy(skel); } From 6d2e21dc4db3933db65293552ecc1ede26febeca Mon Sep 17 00:00:00 2001 From: Xu Kuohai Date: Tue, 11 Oct 2022 08:01:06 -0400 Subject: [PATCH 0150/2002] selftest/bpf: Fix memory leak in kprobe_multi_test The get_syms() function in kprobe_multi_test.c does not free the string memory allocated by sscanf correctly. Fix it. Fixes: 5b6c7e5c4434 ("selftests/bpf: Add attach bench test") Signed-off-by: Xu Kuohai Signed-off-by: Andrii Nakryiko Acked-by: Jiri Olsa Acked-by: Martin KaFai Lau Link: https://lore.kernel.org/bpf/20221011120108.782373-5-xukuohai@huaweicloud.com --- .../bpf/prog_tests/kprobe_multi_test.c | 26 ++++++++++--------- 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/tools/testing/selftests/bpf/prog_tests/kprobe_multi_test.c b/tools/testing/selftests/bpf/prog_tests/kprobe_multi_test.c index d457a55ff408..287b3ac40227 100644 --- a/tools/testing/selftests/bpf/prog_tests/kprobe_multi_test.c +++ b/tools/testing/selftests/bpf/prog_tests/kprobe_multi_test.c @@ -325,7 +325,7 @@ static bool symbol_equal(const void *key1, const void *key2, void *ctx __maybe_u static int get_syms(char ***symsp, size_t *cntp) { size_t cap = 0, cnt = 0, i; - char *name, **syms = NULL; + char *name = NULL, **syms = NULL; struct hashmap *map; char buf[256]; FILE *f; @@ -352,6 +352,8 @@ static int get_syms(char ***symsp, size_t *cntp) /* skip modules */ if (strchr(buf, '[')) continue; + + free(name); if (sscanf(buf, "%ms$*[^\n]\n", &name) != 1) continue; /* @@ -369,32 +371,32 @@ static int get_syms(char ***symsp, size_t *cntp) if (!strncmp(name, "__ftrace_invalid_address__", sizeof("__ftrace_invalid_address__") - 1)) continue; + err = hashmap__add(map, name, NULL); - if (err) { - free(name); - if (err == -EEXIST) - continue; + if (err == -EEXIST) + continue; + if (err) goto error; - } + err = libbpf_ensure_mem((void **) &syms, &cap, sizeof(*syms), cnt + 1); - if (err) { - free(name); + if (err) goto error; - } - syms[cnt] = name; - cnt++; + + syms[cnt++] = name; + name = NULL; } *symsp = syms; *cntp = cnt; error: + free(name); fclose(f); hashmap__free(map); if (err) { for (i = 0; i < cnt; i++) - free(syms[cnt]); + free(syms[i]); free(syms); } return err; From 4abdb1d5b250df4b5b3afd394d5e2fa516064c04 Mon Sep 17 00:00:00 2001 From: Xu Kuohai Date: Tue, 11 Oct 2022 08:01:07 -0400 Subject: [PATCH 0151/2002] selftests/bpf: Fix error failure of case test_xdp_adjust_tail_grow test_xdp_adjust_tail_grow failed with ipv6: test_xdp_adjust_tail_grow:FAIL:ipv6 unexpected error: -28 (errno 28) The reason is that this test case tests ipv4 before ipv6, and when ipv4 test finished, topts.data_size_out was set to 54, which is smaller than the ipv6 output data size 114, so ipv6 test fails with NOSPC error. Fix it by reset topts.data_size_out to sizeof(buf) before testing ipv6. Fixes: 04fcb5f9a104 ("selftests/bpf: Migrate from bpf_prog_test_run") Signed-off-by: Xu Kuohai Signed-off-by: Andrii Nakryiko Acked-by: Martin KaFai Lau Link: https://lore.kernel.org/bpf/20221011120108.782373-6-xukuohai@huaweicloud.com --- tools/testing/selftests/bpf/prog_tests/xdp_adjust_tail.c | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/testing/selftests/bpf/prog_tests/xdp_adjust_tail.c b/tools/testing/selftests/bpf/prog_tests/xdp_adjust_tail.c index 9b9cf8458adf..009ee37607df 100644 --- a/tools/testing/selftests/bpf/prog_tests/xdp_adjust_tail.c +++ b/tools/testing/selftests/bpf/prog_tests/xdp_adjust_tail.c @@ -63,6 +63,7 @@ static void test_xdp_adjust_tail_grow(void) expect_sz = sizeof(pkt_v6) + 40; /* Test grow with 40 bytes */ topts.data_in = &pkt_v6; topts.data_size_in = sizeof(pkt_v6); + topts.data_size_out = sizeof(buf); err = bpf_prog_test_run_opts(prog_fd, &topts); ASSERT_OK(err, "ipv6"); ASSERT_EQ(topts.retval, XDP_TX, "ipv6 retval"); From cbc1c998da59687e8bbc4667154a72eead2daf2d Mon Sep 17 00:00:00 2001 From: Xu Kuohai Date: Tue, 11 Oct 2022 08:01:08 -0400 Subject: [PATCH 0152/2002] selftest/bpf: Fix error usage of ASSERT_OK in xdp_adjust_tail.c xdp_adjust_tail.c calls ASSERT_OK() to check the return value of bpf_prog_test_load(), but the condition is not correct. Fix it. Fixes: 791cad025051 ("bpf: selftests: Get rid of CHECK macro in xdp_adjust_tail.c") Signed-off-by: Xu Kuohai Signed-off-by: Andrii Nakryiko Acked-by: Martin KaFai Lau Link: https://lore.kernel.org/bpf/20221011120108.782373-7-xukuohai@huaweicloud.com --- tools/testing/selftests/bpf/prog_tests/xdp_adjust_tail.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/testing/selftests/bpf/prog_tests/xdp_adjust_tail.c b/tools/testing/selftests/bpf/prog_tests/xdp_adjust_tail.c index 009ee37607df..39973ea1ce43 100644 --- a/tools/testing/selftests/bpf/prog_tests/xdp_adjust_tail.c +++ b/tools/testing/selftests/bpf/prog_tests/xdp_adjust_tail.c @@ -18,7 +18,7 @@ static void test_xdp_adjust_tail_shrink(void) ); err = bpf_prog_test_load(file, BPF_PROG_TYPE_XDP, &obj, &prog_fd); - if (ASSERT_OK(err, "test_xdp_adjust_tail_shrink")) + if (!ASSERT_OK(err, "test_xdp_adjust_tail_shrink")) return; err = bpf_prog_test_run_opts(prog_fd, &topts); @@ -53,7 +53,7 @@ static void test_xdp_adjust_tail_grow(void) ); err = bpf_prog_test_load(file, BPF_PROG_TYPE_XDP, &obj, &prog_fd); - if (ASSERT_OK(err, "test_xdp_adjust_tail_grow")) + if (!ASSERT_OK(err, "test_xdp_adjust_tail_grow")) return; err = bpf_prog_test_run_opts(prog_fd, &topts); @@ -90,7 +90,7 @@ static void test_xdp_adjust_tail_grow2(void) ); err = bpf_prog_test_load(file, BPF_PROG_TYPE_XDP, &obj, &prog_fd); - if (ASSERT_OK(err, "test_xdp_adjust_tail_grow")) + if (!ASSERT_OK(err, "test_xdp_adjust_tail_grow")) return; /* Test case-64 */ From 51deedc9b8680953437dfe359e5268120de10e30 Mon Sep 17 00:00:00 2001 From: Shung-Hsi Yu Date: Wed, 12 Oct 2022 10:23:51 +0800 Subject: [PATCH 0153/2002] libbpf: Use elf_getshdrnum() instead of e_shnum This commit replace e_shnum with the elf_getshdrnum() helper to fix two oss-fuzz-reported heap-buffer overflow in __bpf_object__open. Both reports are incorrectly marked as fixed and while still being reproducible in the latest libbpf. # clusterfuzz-testcase-minimized-bpf-object-fuzzer-5747922482888704 libbpf: loading object 'fuzz-object' from buffer libbpf: sec_cnt is 0 libbpf: elf: section(1) .data, size 0, link 538976288, flags 2020202020202020, type=2 libbpf: elf: section(2) .data, size 32, link 538976288, flags 202020202020ff20, type=1 ================================================================= ==13==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x6020000000c0 at pc 0x0000005a7b46 bp 0x7ffd12214af0 sp 0x7ffd12214ae8 WRITE of size 4 at 0x6020000000c0 thread T0 SCARINESS: 46 (4-byte-write-heap-buffer-overflow-far-from-bounds) #0 0x5a7b45 in bpf_object__elf_collect /src/libbpf/src/libbpf.c:3414:24 #1 0x5733c0 in bpf_object_open /src/libbpf/src/libbpf.c:7223:16 #2 0x5739fd in bpf_object__open_mem /src/libbpf/src/libbpf.c:7263:20 ... The issue lie in libbpf's direct use of e_shnum field in ELF header as the section header count. Where as libelf implemented an extra logic that, when e_shnum == 0 && e_shoff != 0, will use sh_size member of the initial section header as the real section header count (part of ELF spec to accommodate situation where section header counter is larger than SHN_LORESERVE). The above inconsistency lead to libbpf writing into a zero-entry calloc area. So intead of using e_shnum directly, use the elf_getshdrnum() helper provided by libelf to retrieve the section header counter into sec_cnt. Fixes: 0d6988e16a12 ("libbpf: Fix section counting logic") Fixes: 25bbbd7a444b ("libbpf: Remove assumptions about uniqueness of .rodata/.data/.bss maps") Signed-off-by: Shung-Hsi Yu Signed-off-by: Andrii Nakryiko Link: https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=40868 Link: https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=40957 Link: https://lore.kernel.org/bpf/20221012022353.7350-2-shung-hsi.yu@suse.com --- tools/lib/bpf/libbpf.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c index 184ce1684dcd..2e8ac13de6a0 100644 --- a/tools/lib/bpf/libbpf.c +++ b/tools/lib/bpf/libbpf.c @@ -597,7 +597,7 @@ struct elf_state { size_t shstrndx; /* section index for section name strings */ size_t strtabidx; struct elf_sec_desc *secs; - int sec_cnt; + size_t sec_cnt; int btf_maps_shndx; __u32 btf_maps_sec_btf_id; int text_shndx; @@ -3312,10 +3312,15 @@ static int bpf_object__elf_collect(struct bpf_object *obj) Elf64_Shdr *sh; /* ELF section indices are 0-based, but sec #0 is special "invalid" - * section. e_shnum does include sec #0, so e_shnum is the necessary - * size of an array to keep all the sections. + * section. Since section count retrieved by elf_getshdrnum() does + * include sec #0, it is already the necessary size of an array to keep + * all the sections. */ - obj->efile.sec_cnt = obj->efile.ehdr->e_shnum; + if (elf_getshdrnum(obj->efile.elf, &obj->efile.sec_cnt)) { + pr_warn("elf: failed to get the number of sections for %s: %s\n", + obj->path, elf_errmsg(-1)); + return -LIBBPF_ERRNO__FORMAT; + } obj->efile.secs = calloc(obj->efile.sec_cnt, sizeof(*obj->efile.secs)); if (!obj->efile.secs) return -ENOMEM; From 35a855509e6ee3442477c8ebc6827b5b5d32a7b5 Mon Sep 17 00:00:00 2001 From: Shung-Hsi Yu Date: Wed, 12 Oct 2022 10:23:52 +0800 Subject: [PATCH 0154/2002] libbpf: Deal with section with no data gracefully ELF section data pointer returned by libelf may be NULL (if section has SHT_NOBITS), so null check section data pointer before attempting to copy license and kversion section. Fixes: cb1e5e961991 ("bpf tools: Collect version and license from ELF sections") Signed-off-by: Shung-Hsi Yu Signed-off-by: Andrii Nakryiko Link: https://lore.kernel.org/bpf/20221012022353.7350-3-shung-hsi.yu@suse.com --- tools/lib/bpf/libbpf.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c index 2e8ac13de6a0..29e9df0c232b 100644 --- a/tools/lib/bpf/libbpf.c +++ b/tools/lib/bpf/libbpf.c @@ -1408,6 +1408,10 @@ static int bpf_object__check_endianness(struct bpf_object *obj) static int bpf_object__init_license(struct bpf_object *obj, void *data, size_t size) { + if (!data) { + pr_warn("invalid license section in %s\n", obj->path); + return -LIBBPF_ERRNO__FORMAT; + } /* libbpf_strlcpy() only copies first N - 1 bytes, so size + 1 won't * go over allowed ELF data section buffer */ @@ -1421,7 +1425,7 @@ bpf_object__init_kversion(struct bpf_object *obj, void *data, size_t size) { __u32 kver; - if (size != sizeof(kver)) { + if (!data || size != sizeof(kver)) { pr_warn("invalid kver section in %s\n", obj->path); return -LIBBPF_ERRNO__FORMAT; } From d0d382f95a9270dcf803539d6781d6bd67e3f5b2 Mon Sep 17 00:00:00 2001 From: Shung-Hsi Yu Date: Wed, 12 Oct 2022 10:23:53 +0800 Subject: [PATCH 0155/2002] libbpf: Fix null-pointer dereference in find_prog_by_sec_insn() When there are no program sections, obj->programs is left unallocated, and find_prog_by_sec_insn()'s search lands on &obj->programs[0] == NULL, and will cause null-pointer dereference in the following access to prog->sec_idx. Guard the search with obj->nr_programs similar to what's being done in __bpf_program__iter() to prevent null-pointer access from happening. Fixes: db2b8b06423c ("libbpf: Support CO-RE relocations for multi-prog sections") Signed-off-by: Shung-Hsi Yu Signed-off-by: Andrii Nakryiko Link: https://lore.kernel.org/bpf/20221012022353.7350-4-shung-hsi.yu@suse.com --- tools/lib/bpf/libbpf.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c index 29e9df0c232b..8c3f236c86e4 100644 --- a/tools/lib/bpf/libbpf.c +++ b/tools/lib/bpf/libbpf.c @@ -4115,6 +4115,9 @@ static struct bpf_program *find_prog_by_sec_insn(const struct bpf_object *obj, int l = 0, r = obj->nr_programs - 1, m; struct bpf_program *prog; + if (!obj->nr_programs) + return NULL; + while (l < r) { m = l + (r - l + 1) / 2; prog = &obj->programs[m]; From 62c69e89e81bfbdb9a87ae3e0599dcc6aacf786b Mon Sep 17 00:00:00 2001 From: Hou Tao Date: Tue, 11 Oct 2022 15:12:49 +0800 Subject: [PATCH 0156/2002] selftests/bpf: Use sys_pidfd_open() helper when possible SYS_pidfd_open may be undefined for old glibc, so using sys_pidfd_open() helper defined in task_local_storage_helpers.h instead to fix potential build failure. And according to commit 7615d9e1780e ("arch: wire-up pidfd_open()"), the syscall number of pidfd_open is always 434 except for alpha architure, so update the definition of __NR_pidfd_open accordingly. Signed-off-by: Hou Tao Signed-off-by: Andrii Nakryiko Link: https://lore.kernel.org/bpf/20221011071249.3471760-1-houtao@huaweicloud.com --- tools/testing/selftests/bpf/prog_tests/bpf_iter.c | 10 +++------- .../testing/selftests/bpf/task_local_storage_helpers.h | 4 ++++ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/tools/testing/selftests/bpf/prog_tests/bpf_iter.c b/tools/testing/selftests/bpf/prog_tests/bpf_iter.c index ecde236047fe..c39d40f4b268 100644 --- a/tools/testing/selftests/bpf/prog_tests/bpf_iter.c +++ b/tools/testing/selftests/bpf/prog_tests/bpf_iter.c @@ -3,6 +3,7 @@ #include #include #include +#include #include "bpf_iter_ipv6_route.skel.h" #include "bpf_iter_netlink.skel.h" #include "bpf_iter_bpf_map.skel.h" @@ -175,11 +176,6 @@ static void test_bpf_map(void) bpf_iter_bpf_map__destroy(skel); } -static int pidfd_open(pid_t pid, unsigned int flags) -{ - return syscall(SYS_pidfd_open, pid, flags); -} - static void check_bpf_link_info(const struct bpf_program *prog) { LIBBPF_OPTS(bpf_iter_attach_opts, opts); @@ -295,8 +291,8 @@ static void test_task_pidfd(void) union bpf_iter_link_info linfo; int pidfd; - pidfd = pidfd_open(getpid(), 0); - if (!ASSERT_GT(pidfd, 0, "pidfd_open")) + pidfd = sys_pidfd_open(getpid(), 0); + if (!ASSERT_GT(pidfd, 0, "sys_pidfd_open")) return; memset(&linfo, 0, sizeof(linfo)); diff --git a/tools/testing/selftests/bpf/task_local_storage_helpers.h b/tools/testing/selftests/bpf/task_local_storage_helpers.h index 711d5abb7d51..281f86132766 100644 --- a/tools/testing/selftests/bpf/task_local_storage_helpers.h +++ b/tools/testing/selftests/bpf/task_local_storage_helpers.h @@ -7,8 +7,12 @@ #include #ifndef __NR_pidfd_open +#ifdef __alpha__ +#define __NR_pidfd_open 544 +#else #define __NR_pidfd_open 434 #endif +#endif static inline int sys_pidfd_open(pid_t pid, unsigned int flags) { From f00909e2e6fe4ac6b2420e3863a0c533fe4f15e0 Mon Sep 17 00:00:00 2001 From: Jiapeng Chong Date: Mon, 17 Oct 2022 17:35:40 +0800 Subject: [PATCH 0157/2002] net: ip6_gre: Remove the unused function ip6gre_tnl_addr_conflict() The function ip6gre_tnl_addr_conflict() is defined in the ip6_gre.c file, but not called elsewhere, so delete this unused function. net/ipv6/ip6_gre.c:887:20: warning: unused function 'ip6gre_tnl_addr_conflict'. Link: https://bugzilla.openanolis.cn/show_bug.cgi?id=2419 Reported-by: Abaci Robot Signed-off-by: Jiapeng Chong Link: https://lore.kernel.org/r/20221017093540.26806-1-jiapeng.chong@linux.alibaba.com Signed-off-by: Paolo Abeni --- net/ipv6/ip6_gre.c | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/net/ipv6/ip6_gre.c b/net/ipv6/ip6_gre.c index 48b4ff0294f6..02b1b54165e8 100644 --- a/net/ipv6/ip6_gre.c +++ b/net/ipv6/ip6_gre.c @@ -870,26 +870,6 @@ static inline int ip6gre_xmit_ipv6(struct sk_buff *skb, struct net_device *dev) return 0; } -/** - * ip6gre_tnl_addr_conflict - compare packet addresses to tunnel's own - * @t: the outgoing tunnel device - * @hdr: IPv6 header from the incoming packet - * - * Description: - * Avoid trivial tunneling loop by checking that tunnel exit-point - * doesn't match source of incoming packet. - * - * Return: - * 1 if conflict, - * 0 else - **/ - -static inline bool ip6gre_tnl_addr_conflict(const struct ip6_tnl *t, - const struct ipv6hdr *hdr) -{ - return ipv6_addr_equal(&t->parms.raddr, &hdr->saddr); -} - static int ip6gre_xmit_other(struct sk_buff *skb, struct net_device *dev) { struct ip6_tnl *t = netdev_priv(dev); From e6c86c513f440bec5f1046539c7e3c6c653842da Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Fri, 14 Oct 2022 19:39:43 +0800 Subject: [PATCH 0158/2002] rcu-tasks: Provide rcu_trace_implies_rcu_gp() As an accident of implementation, an RCU Tasks Trace grace period also acts as an RCU grace period. However, this could change at any time. This commit therefore creates an rcu_trace_implies_rcu_gp() that currently returns true to codify this accident. Code relying on this accident must call this function to verify that this accident is still happening. Reported-by: Hou Tao Signed-off-by: Paul E. McKenney Cc: Alexei Starovoitov Cc: Martin KaFai Lau Link: https://lore.kernel.org/r/20221014113946.965131-2-houtao@huaweicloud.com Signed-off-by: Alexei Starovoitov --- include/linux/rcupdate.h | 12 ++++++++++++ kernel/rcu/tasks.h | 2 ++ 2 files changed, 14 insertions(+) diff --git a/include/linux/rcupdate.h b/include/linux/rcupdate.h index 08605ce7379d..8822f06e4b40 100644 --- a/include/linux/rcupdate.h +++ b/include/linux/rcupdate.h @@ -240,6 +240,18 @@ static inline void exit_tasks_rcu_start(void) { } static inline void exit_tasks_rcu_finish(void) { } #endif /* #else #ifdef CONFIG_TASKS_RCU_GENERIC */ +/** + * rcu_trace_implies_rcu_gp - does an RCU Tasks Trace grace period imply an RCU grace period? + * + * As an accident of implementation, an RCU Tasks Trace grace period also + * acts as an RCU grace period. However, this could change at any time. + * Code relying on this accident must call this function to verify that + * this accident is still happening. + * + * You have been warned! + */ +static inline bool rcu_trace_implies_rcu_gp(void) { return true; } + /** * cond_resched_tasks_rcu_qs - Report potential quiescent states to RCU * diff --git a/kernel/rcu/tasks.h b/kernel/rcu/tasks.h index f5bf6fb430da..9435e5a7b53e 100644 --- a/kernel/rcu/tasks.h +++ b/kernel/rcu/tasks.h @@ -1535,6 +1535,8 @@ static void rcu_tasks_trace_postscan(struct list_head *hop) { // Wait for late-stage exiting tasks to finish exiting. // These might have passed the call to exit_tasks_rcu_finish(). + + // If you remove the following line, update rcu_trace_implies_rcu_gp()!!! synchronize_rcu(); // Any tasks that exit after this point will set // TRC_NEED_QS_CHECKED in ->trc_reader_special.b.need_qs. From 59be91e5e70a1aa91dfee8088b071f6d05c8a1a3 Mon Sep 17 00:00:00 2001 From: Hou Tao Date: Fri, 14 Oct 2022 19:39:44 +0800 Subject: [PATCH 0159/2002] bpf: Use rcu_trace_implies_rcu_gp() in bpf memory allocator The memory free logic in bpf memory allocator chains a RCU Tasks Trace grace period and a normal RCU grace period one after the other, so it can ensure that both sleepable and non-sleepable programs have finished. With the introduction of rcu_trace_implies_rcu_gp(), __free_rcu_tasks_trace() can check whether or not a normal RCU grace period has also passed after a RCU Tasks Trace grace period has passed. If it is true, freeing these elements directly, else freeing through call_rcu(). Signed-off-by: Hou Tao Link: https://lore.kernel.org/r/20221014113946.965131-3-houtao@huaweicloud.com Signed-off-by: Alexei Starovoitov --- kernel/bpf/memalloc.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/kernel/bpf/memalloc.c b/kernel/bpf/memalloc.c index 5f83be1d2018..2433be58bb85 100644 --- a/kernel/bpf/memalloc.c +++ b/kernel/bpf/memalloc.c @@ -222,9 +222,13 @@ static void __free_rcu(struct rcu_head *head) static void __free_rcu_tasks_trace(struct rcu_head *head) { - struct bpf_mem_cache *c = container_of(head, struct bpf_mem_cache, rcu); - - call_rcu(&c->rcu, __free_rcu); + /* If RCU Tasks Trace grace period implies RCU grace period, + * there is no need to invoke call_rcu(). + */ + if (rcu_trace_implies_rcu_gp()) + __free_rcu(head); + else + call_rcu(head, __free_rcu); } static void enque_to_free(struct bpf_mem_cache *c, void *obj) @@ -253,8 +257,9 @@ static void do_call_rcu(struct bpf_mem_cache *c) */ __llist_add(llnode, &c->waiting_for_gp); /* Use call_rcu_tasks_trace() to wait for sleepable progs to finish. - * Then use call_rcu() to wait for normal progs to finish - * and finally do free_one() on each element. + * If RCU Tasks Trace grace period implies RCU grace period, free + * these elements directly, else use call_rcu() to wait for normal + * progs to finish and finally do free_one() on each element. */ call_rcu_tasks_trace(&c->rcu, __free_rcu_tasks_trace); } From d39d1445d37747032e2b26732fed6fe25161cd36 Mon Sep 17 00:00:00 2001 From: Hou Tao Date: Fri, 14 Oct 2022 19:39:45 +0800 Subject: [PATCH 0160/2002] bpf: Use rcu_trace_implies_rcu_gp() in local storage map Local storage map is accessible for both sleepable and non-sleepable bpf program, and its memory is freed by using both call_rcu_tasks_trace() and kfree_rcu() to wait for both RCU-tasks-trace grace period and RCU grace period to pass. With the introduction of rcu_trace_implies_rcu_gp(), both bpf_selem_free_rcu() and bpf_local_storage_free_rcu() can check whether or not a normal RCU grace period has also passed after a RCU-tasks-trace grace period has passed. If it is true, it is safe to call kfree() directly. Signed-off-by: Hou Tao Link: https://lore.kernel.org/r/20221014113946.965131-4-houtao@huaweicloud.com Signed-off-by: Alexei Starovoitov --- kernel/bpf/bpf_local_storage.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/kernel/bpf/bpf_local_storage.c b/kernel/bpf/bpf_local_storage.c index 802fc15b0d73..9dc6de1cf185 100644 --- a/kernel/bpf/bpf_local_storage.c +++ b/kernel/bpf/bpf_local_storage.c @@ -88,8 +88,14 @@ void bpf_local_storage_free_rcu(struct rcu_head *rcu) { struct bpf_local_storage *local_storage; + /* If RCU Tasks Trace grace period implies RCU grace period, do + * kfree(), else do kfree_rcu(). + */ local_storage = container_of(rcu, struct bpf_local_storage, rcu); - kfree_rcu(local_storage, rcu); + if (rcu_trace_implies_rcu_gp()) + kfree(local_storage); + else + kfree_rcu(local_storage, rcu); } static void bpf_selem_free_rcu(struct rcu_head *rcu) @@ -97,7 +103,10 @@ static void bpf_selem_free_rcu(struct rcu_head *rcu) struct bpf_local_storage_elem *selem; selem = container_of(rcu, struct bpf_local_storage_elem, rcu); - kfree_rcu(selem, rcu); + if (rcu_trace_implies_rcu_gp()) + kfree(selem); + else + kfree_rcu(selem, rcu); } /* local_storage->lock must be held and selem->local_storage == local_storage. From 4835f9ee980c1867584018e69cbf1f62d7844cb3 Mon Sep 17 00:00:00 2001 From: Hou Tao Date: Fri, 14 Oct 2022 19:39:46 +0800 Subject: [PATCH 0161/2002] bpf: Use rcu_trace_implies_rcu_gp() for program array freeing To support both sleepable and normal uprobe bpf program, the freeing of trace program array chains a RCU-tasks-trace grace period and a normal RCU grace period one after the other. With the introduction of rcu_trace_implies_rcu_gp(), __bpf_prog_array_free_sleepable_cb() can check whether or not a normal RCU grace period has also passed after a RCU-tasks-trace grace period has passed. If it is true, it is safe to invoke kfree() directly. Signed-off-by: Hou Tao Link: https://lore.kernel.org/r/20221014113946.965131-5-houtao@huaweicloud.com Signed-off-by: Alexei Starovoitov --- kernel/bpf/core.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/kernel/bpf/core.c b/kernel/bpf/core.c index 711fd293b6de..4bc5f46d7030 100644 --- a/kernel/bpf/core.c +++ b/kernel/bpf/core.c @@ -2251,8 +2251,14 @@ static void __bpf_prog_array_free_sleepable_cb(struct rcu_head *rcu) { struct bpf_prog_array *progs; + /* If RCU Tasks Trace grace period implies RCU grace period, there is + * no need to call kfree_rcu(), just call kfree() directly. + */ progs = container_of(rcu, struct bpf_prog_array, rcu); - kfree_rcu(progs, rcu); + if (rcu_trace_implies_rcu_gp()) + kfree(progs); + else + kfree_rcu(progs, rcu); } void bpf_prog_array_free_sleepable(struct bpf_prog_array *progs) From 6c4e777fbba6e7dd6a0757c0e7bba66cdbe611cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20M=C3=BCller?= Date: Mon, 17 Oct 2022 23:24:58 +0000 Subject: [PATCH 0162/2002] bpf/docs: Update README for most recent vmtest.sh MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since commit 40b09653b197 ("selftests/bpf: Adjust vmtest.sh to use local kernel configuration") the vmtest.sh script no longer downloads a kernel configuration but uses the local, in-repository one. This change updates the README, which still mentions the old behavior. Signed-off-by: Daniel Müller Signed-off-by: Daniel Borkmann Link: https://lore.kernel.org/bpf/20221017232458.1272762-1-deso@posteo.net --- tools/testing/selftests/bpf/README.rst | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/tools/testing/selftests/bpf/README.rst b/tools/testing/selftests/bpf/README.rst index d3c6b3da0bb1..822548d0f2ae 100644 --- a/tools/testing/selftests/bpf/README.rst +++ b/tools/testing/selftests/bpf/README.rst @@ -14,10 +14,11 @@ It's now possible to run the selftests using ``tools/testing/selftests/bpf/vmtes The script tries to ensure that the tests are run with the same environment as they would be run post-submit in the CI used by the Maintainers. -This script downloads a suitable Kconfig and VM userspace image from the system used by -the CI. It builds the kernel (without overwriting your existing Kconfig), recompiles the -bpf selftests, runs them (by default ``tools/testing/selftests/bpf/test_progs``) and -saves the resulting output (by default in ``~/.bpf_selftests``). +This script uses the in-tree kernel configuration and downloads a VM userspace +image from the system used by the CI. It builds the kernel (without overwriting +your existing Kconfig), recompiles the bpf selftests, runs them (by default +``tools/testing/selftests/bpf/test_progs``) and saves the resulting output (by +default in ``~/.bpf_selftests``). Script dependencies: - clang (preferably built from sources, https://github.com/llvm/llvm-project); @@ -26,7 +27,7 @@ Script dependencies: - docutils (for ``rst2man``); - libcap-devel. -For more information on about using the script, run: +For more information about using the script, run: .. code-block:: console From a2fd08448f2b7591fc7ec8dec2e5e025a43f0cee Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Mon, 17 Oct 2022 14:18:26 +0200 Subject: [PATCH 0163/2002] net: remove smc911x driver This driver was used on Arm and SH machines until 2009, when the last platforms moved to the smsc911x driver for the same hardware. Time to retire this version. Link: https://lore.kernel.org/netdev/1232010482-3744-1-git-send-email-steve.glendinning@smsc.com/ Signed-off-by: Arnd Bergmann Link: https://lore.kernel.org/r/20221017121900.3520108-1-arnd@kernel.org Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/smsc/Kconfig | 14 - drivers/net/ethernet/smsc/Makefile | 1 - drivers/net/ethernet/smsc/smc911x.c | 2198 --------------------------- drivers/net/ethernet/smsc/smc911x.h | 901 ----------- include/linux/smc911x.h | 14 - 5 files changed, 3128 deletions(-) delete mode 100644 drivers/net/ethernet/smsc/smc911x.c delete mode 100644 drivers/net/ethernet/smsc/smc911x.h delete mode 100644 include/linux/smc911x.h diff --git a/drivers/net/ethernet/smsc/Kconfig b/drivers/net/ethernet/smsc/Kconfig index 2524c907f386..5f22a8a4d27b 100644 --- a/drivers/net/ethernet/smsc/Kconfig +++ b/drivers/net/ethernet/smsc/Kconfig @@ -75,20 +75,6 @@ config EPIC100 More specific information and updates are available from . -config SMC911X - tristate "SMSC LAN911[5678] support" - select CRC32 - select MII - depends on (ARM || SUPERH || COMPILE_TEST) - help - This is a driver for SMSC's LAN911x series of Ethernet chipsets - including the new LAN9115, LAN9116, LAN9117, and LAN9118. - Say Y here if you want it compiled into the kernel. - - This driver is also available as a module. The module will be - called smc911x. If you want to compile it as a module, say M - here and read - config SMSC911X tristate "SMSC LAN911x/LAN921x families embedded ethernet support" depends on HAS_IOMEM diff --git a/drivers/net/ethernet/smsc/Makefile b/drivers/net/ethernet/smsc/Makefile index 4105912b1629..1501fa364c13 100644 --- a/drivers/net/ethernet/smsc/Makefile +++ b/drivers/net/ethernet/smsc/Makefile @@ -8,5 +8,4 @@ obj-$(CONFIG_SMC91X) += smc91x.o obj-$(CONFIG_PCMCIA_SMC91C92) += smc91c92_cs.o obj-$(CONFIG_EPIC100) += epic100.o obj-$(CONFIG_SMSC9420) += smsc9420.o -obj-$(CONFIG_SMC911X) += smc911x.o obj-$(CONFIG_SMSC911X) += smsc911x.o diff --git a/drivers/net/ethernet/smsc/smc911x.c b/drivers/net/ethernet/smsc/smc911x.c deleted file mode 100644 index 52ecfb461c41..000000000000 --- a/drivers/net/ethernet/smsc/smc911x.c +++ /dev/null @@ -1,2198 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * smc911x.c - * This is a driver for SMSC's LAN911{5,6,7,8} single-chip Ethernet devices. - * - * Copyright (C) 2005 Sensoria Corp - * Derived from the unified SMC91x driver by Nicolas Pitre - * and the smsc911x.c reference driver by SMSC - * - * Arguments: - * watchdog = TX watchdog timeout - * tx_fifo_kb = Size of TX FIFO in KB - * - * History: - * 04/16/05 Dustin McIntire Initial version - */ -static const char version[] = - "smc911x.c: v1.0 04-16-2005 by Dustin McIntire \n"; - -/* Debugging options */ -#define ENABLE_SMC_DEBUG_RX 0 -#define ENABLE_SMC_DEBUG_TX 0 -#define ENABLE_SMC_DEBUG_DMA 0 -#define ENABLE_SMC_DEBUG_PKTS 0 -#define ENABLE_SMC_DEBUG_MISC 0 -#define ENABLE_SMC_DEBUG_FUNC 0 - -#define SMC_DEBUG_RX ((ENABLE_SMC_DEBUG_RX ? 1 : 0) << 0) -#define SMC_DEBUG_TX ((ENABLE_SMC_DEBUG_TX ? 1 : 0) << 1) -#define SMC_DEBUG_DMA ((ENABLE_SMC_DEBUG_DMA ? 1 : 0) << 2) -#define SMC_DEBUG_PKTS ((ENABLE_SMC_DEBUG_PKTS ? 1 : 0) << 3) -#define SMC_DEBUG_MISC ((ENABLE_SMC_DEBUG_MISC ? 1 : 0) << 4) -#define SMC_DEBUG_FUNC ((ENABLE_SMC_DEBUG_FUNC ? 1 : 0) << 5) - -#ifndef SMC_DEBUG -#define SMC_DEBUG ( SMC_DEBUG_RX | \ - SMC_DEBUG_TX | \ - SMC_DEBUG_DMA | \ - SMC_DEBUG_PKTS | \ - SMC_DEBUG_MISC | \ - SMC_DEBUG_FUNC \ - ) -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include - -#include - -#include "smc911x.h" - -/* - * Transmit timeout, default 5 seconds. - */ -static int watchdog = 5000; -module_param(watchdog, int, 0400); -MODULE_PARM_DESC(watchdog, "transmit timeout in milliseconds"); - -static int tx_fifo_kb=8; -module_param(tx_fifo_kb, int, 0400); -MODULE_PARM_DESC(tx_fifo_kb,"transmit FIFO size in KB (1 0 -#define DBG(n, dev, args...) \ - do { \ - if (SMC_DEBUG & (n)) \ - netdev_dbg(dev, args); \ - } while (0) - -#define PRINTK(dev, args...) netdev_info(dev, args) -#else -#define DBG(n, dev, args...) \ - while (0) { \ - netdev_dbg(dev, args); \ - } -#define PRINTK(dev, args...) netdev_dbg(dev, args) -#endif - -#if SMC_DEBUG_PKTS > 0 -static void PRINT_PKT(u_char *buf, int length) -{ - int i; - int remainder; - int lines; - - lines = length / 16; - remainder = length % 16; - - for (i = 0; i < lines ; i ++) { - int cur; - printk(KERN_DEBUG); - for (cur = 0; cur < 8; cur++) { - u_char a, b; - a = *buf++; - b = *buf++; - pr_cont("%02x%02x ", a, b); - } - pr_cont("\n"); - } - printk(KERN_DEBUG); - for (i = 0; i < remainder/2 ; i++) { - u_char a, b; - a = *buf++; - b = *buf++; - pr_cont("%02x%02x ", a, b); - } - pr_cont("\n"); -} -#else -static inline void PRINT_PKT(u_char *buf, int length) { } -#endif - - -/* this enables an interrupt in the interrupt mask register */ -#define SMC_ENABLE_INT(lp, x) do { \ - unsigned int __mask; \ - __mask = SMC_GET_INT_EN((lp)); \ - __mask |= (x); \ - SMC_SET_INT_EN((lp), __mask); \ -} while (0) - -/* this disables an interrupt from the interrupt mask register */ -#define SMC_DISABLE_INT(lp, x) do { \ - unsigned int __mask; \ - __mask = SMC_GET_INT_EN((lp)); \ - __mask &= ~(x); \ - SMC_SET_INT_EN((lp), __mask); \ -} while (0) - -/* - * this does a soft reset on the device - */ -static void smc911x_reset(struct net_device *dev) -{ - struct smc911x_local *lp = netdev_priv(dev); - unsigned int reg, timeout=0, resets=1, irq_cfg; - unsigned long flags; - - DBG(SMC_DEBUG_FUNC, dev, "--> %s\n", __func__); - - /* Take out of PM setting first */ - if ((SMC_GET_PMT_CTRL(lp) & PMT_CTRL_READY_) == 0) { - /* Write to the bytetest will take out of powerdown */ - SMC_SET_BYTE_TEST(lp, 0); - timeout=10; - do { - udelay(10); - reg = SMC_GET_PMT_CTRL(lp) & PMT_CTRL_READY_; - } while (--timeout && !reg); - if (timeout == 0) { - PRINTK(dev, "smc911x_reset timeout waiting for PM restore\n"); - return; - } - } - - /* Disable all interrupts */ - spin_lock_irqsave(&lp->lock, flags); - SMC_SET_INT_EN(lp, 0); - spin_unlock_irqrestore(&lp->lock, flags); - - while (resets--) { - SMC_SET_HW_CFG(lp, HW_CFG_SRST_); - timeout=10; - do { - udelay(10); - reg = SMC_GET_HW_CFG(lp); - /* If chip indicates reset timeout then try again */ - if (reg & HW_CFG_SRST_TO_) { - PRINTK(dev, "chip reset timeout, retrying...\n"); - resets++; - break; - } - } while (--timeout && (reg & HW_CFG_SRST_)); - } - if (timeout == 0) { - PRINTK(dev, "smc911x_reset timeout waiting for reset\n"); - return; - } - - /* make sure EEPROM has finished loading before setting GPIO_CFG */ - timeout=1000; - while (--timeout && (SMC_GET_E2P_CMD(lp) & E2P_CMD_EPC_BUSY_)) - udelay(10); - - if (timeout == 0){ - PRINTK(dev, "smc911x_reset timeout waiting for EEPROM busy\n"); - return; - } - - /* Initialize interrupts */ - SMC_SET_INT_EN(lp, 0); - SMC_ACK_INT(lp, -1); - - /* Reset the FIFO level and flow control settings */ - SMC_SET_HW_CFG(lp, (lp->tx_fifo_kb & 0xF) << 16); -//TODO: Figure out what appropriate pause time is - SMC_SET_FLOW(lp, FLOW_FCPT_ | FLOW_FCEN_); - SMC_SET_AFC_CFG(lp, lp->afc_cfg); - - - /* Set to LED outputs */ - SMC_SET_GPIO_CFG(lp, 0x70070000); - - /* - * Deassert IRQ for 1*10us for edge type interrupts - * and drive IRQ pin push-pull - */ - irq_cfg = (1 << 24) | INT_CFG_IRQ_EN_ | INT_CFG_IRQ_TYPE_; -#ifdef SMC_DYNAMIC_BUS_CONFIG - if (lp->cfg.irq_polarity) - irq_cfg |= INT_CFG_IRQ_POL_; -#endif - SMC_SET_IRQ_CFG(lp, irq_cfg); - - /* clear anything saved */ - if (lp->pending_tx_skb != NULL) { - dev_kfree_skb (lp->pending_tx_skb); - lp->pending_tx_skb = NULL; - dev->stats.tx_errors++; - dev->stats.tx_aborted_errors++; - } -} - -/* - * Enable Interrupts, Receive, and Transmit - */ -static void smc911x_enable(struct net_device *dev) -{ - struct smc911x_local *lp = netdev_priv(dev); - unsigned mask, cfg, cr; - unsigned long flags; - - DBG(SMC_DEBUG_FUNC, dev, "--> %s\n", __func__); - - spin_lock_irqsave(&lp->lock, flags); - - SMC_SET_MAC_ADDR(lp, dev->dev_addr); - - /* Enable TX */ - cfg = SMC_GET_HW_CFG(lp); - cfg &= HW_CFG_TX_FIF_SZ_ | 0xFFF; - cfg |= HW_CFG_SF_; - SMC_SET_HW_CFG(lp, cfg); - SMC_SET_FIFO_TDA(lp, 0xFF); - /* Update TX stats on every 64 packets received or every 1 sec */ - SMC_SET_FIFO_TSL(lp, 64); - SMC_SET_GPT_CFG(lp, GPT_CFG_TIMER_EN_ | 10000); - - SMC_GET_MAC_CR(lp, cr); - cr |= MAC_CR_TXEN_ | MAC_CR_HBDIS_; - SMC_SET_MAC_CR(lp, cr); - SMC_SET_TX_CFG(lp, TX_CFG_TX_ON_); - - /* Add 2 byte padding to start of packets */ - SMC_SET_RX_CFG(lp, (2<<8) & RX_CFG_RXDOFF_); - - /* Turn on receiver and enable RX */ - if (cr & MAC_CR_RXEN_) - DBG(SMC_DEBUG_RX, dev, "Receiver already enabled\n"); - - SMC_SET_MAC_CR(lp, cr | MAC_CR_RXEN_); - - /* Interrupt on every received packet */ - SMC_SET_FIFO_RSA(lp, 0x01); - SMC_SET_FIFO_RSL(lp, 0x00); - - /* now, enable interrupts */ - mask = INT_EN_TDFA_EN_ | INT_EN_TSFL_EN_ | INT_EN_RSFL_EN_ | - INT_EN_GPT_INT_EN_ | INT_EN_RXDFH_INT_EN_ | INT_EN_RXE_EN_ | - INT_EN_PHY_INT_EN_; - if (IS_REV_A(lp->revision)) - mask|=INT_EN_RDFL_EN_; - else { - mask|=INT_EN_RDFO_EN_; - } - SMC_ENABLE_INT(lp, mask); - - spin_unlock_irqrestore(&lp->lock, flags); -} - -/* - * this puts the device in an inactive state - */ -static void smc911x_shutdown(struct net_device *dev) -{ - struct smc911x_local *lp = netdev_priv(dev); - unsigned cr; - unsigned long flags; - - DBG(SMC_DEBUG_FUNC, dev, "%s: --> %s\n", CARDNAME, __func__); - - /* Disable IRQ's */ - SMC_SET_INT_EN(lp, 0); - - /* Turn of Rx and TX */ - spin_lock_irqsave(&lp->lock, flags); - SMC_GET_MAC_CR(lp, cr); - cr &= ~(MAC_CR_TXEN_ | MAC_CR_RXEN_ | MAC_CR_HBDIS_); - SMC_SET_MAC_CR(lp, cr); - SMC_SET_TX_CFG(lp, TX_CFG_STOP_TX_); - spin_unlock_irqrestore(&lp->lock, flags); -} - -static inline void smc911x_drop_pkt(struct net_device *dev) -{ - struct smc911x_local *lp = netdev_priv(dev); - unsigned int fifo_count, timeout, reg; - - DBG(SMC_DEBUG_FUNC | SMC_DEBUG_RX, dev, "%s: --> %s\n", - CARDNAME, __func__); - fifo_count = SMC_GET_RX_FIFO_INF(lp) & 0xFFFF; - if (fifo_count <= 4) { - /* Manually dump the packet data */ - while (fifo_count--) - SMC_GET_RX_FIFO(lp); - } else { - /* Fast forward through the bad packet */ - SMC_SET_RX_DP_CTRL(lp, RX_DP_CTRL_FFWD_BUSY_); - timeout=50; - do { - udelay(10); - reg = SMC_GET_RX_DP_CTRL(lp) & RX_DP_CTRL_FFWD_BUSY_; - } while (--timeout && reg); - if (timeout == 0) { - PRINTK(dev, "timeout waiting for RX fast forward\n"); - } - } -} - -/* - * This is the procedure to handle the receipt of a packet. - * It should be called after checking for packet presence in - * the RX status FIFO. It must be called with the spin lock - * already held. - */ -static inline void smc911x_rcv(struct net_device *dev) -{ - struct smc911x_local *lp = netdev_priv(dev); - unsigned int pkt_len, status; - struct sk_buff *skb; - unsigned char *data; - - DBG(SMC_DEBUG_FUNC | SMC_DEBUG_RX, dev, "--> %s\n", - __func__); - status = SMC_GET_RX_STS_FIFO(lp); - DBG(SMC_DEBUG_RX, dev, "Rx pkt len %d status 0x%08x\n", - (status & 0x3fff0000) >> 16, status & 0xc000ffff); - pkt_len = (status & RX_STS_PKT_LEN_) >> 16; - if (status & RX_STS_ES_) { - /* Deal with a bad packet */ - dev->stats.rx_errors++; - if (status & RX_STS_CRC_ERR_) - dev->stats.rx_crc_errors++; - else { - if (status & RX_STS_LEN_ERR_) - dev->stats.rx_length_errors++; - if (status & RX_STS_MCAST_) - dev->stats.multicast++; - } - /* Remove the bad packet data from the RX FIFO */ - smc911x_drop_pkt(dev); - } else { - /* Receive a valid packet */ - /* Alloc a buffer with extra room for DMA alignment */ - skb = netdev_alloc_skb(dev, pkt_len+32); - if (unlikely(skb == NULL)) { - PRINTK(dev, "Low memory, rcvd packet dropped.\n"); - dev->stats.rx_dropped++; - smc911x_drop_pkt(dev); - return; - } - /* Align IP header to 32 bits - * Note that the device is configured to add a 2 - * byte padding to the packet start, so we really - * want to write to the orignal data pointer */ - data = skb->data; - skb_reserve(skb, 2); - skb_put(skb,pkt_len-4); -#ifdef SMC_USE_DMA - { - unsigned int fifo; - /* Lower the FIFO threshold if possible */ - fifo = SMC_GET_FIFO_INT(lp); - if (fifo & 0xFF) fifo--; - DBG(SMC_DEBUG_RX, dev, "Setting RX stat FIFO threshold to %d\n", - fifo & 0xff); - SMC_SET_FIFO_INT(lp, fifo); - /* Setup RX DMA */ - SMC_SET_RX_CFG(lp, RX_CFG_RX_END_ALGN16_ | ((2<<8) & RX_CFG_RXDOFF_)); - lp->rxdma_active = 1; - lp->current_rx_skb = skb; - SMC_PULL_DATA(lp, data, (pkt_len+2+15) & ~15); - /* Packet processing deferred to DMA RX interrupt */ - } -#else - SMC_SET_RX_CFG(lp, RX_CFG_RX_END_ALGN4_ | ((2<<8) & RX_CFG_RXDOFF_)); - SMC_PULL_DATA(lp, data, pkt_len+2+3); - - DBG(SMC_DEBUG_PKTS, dev, "Received packet\n"); - PRINT_PKT(data, min(pkt_len - 4, 64U)); - skb->protocol = eth_type_trans(skb, dev); - netif_rx(skb); - dev->stats.rx_packets++; - dev->stats.rx_bytes += pkt_len-4; -#endif - } -} - -/* - * This is called to actually send a packet to the chip. - */ -static void smc911x_hardware_send_pkt(struct net_device *dev) -{ - struct smc911x_local *lp = netdev_priv(dev); - struct sk_buff *skb; - unsigned int cmdA, cmdB, len; - unsigned char *buf; - - DBG(SMC_DEBUG_FUNC | SMC_DEBUG_TX, dev, "--> %s\n", __func__); - BUG_ON(lp->pending_tx_skb == NULL); - - skb = lp->pending_tx_skb; - lp->pending_tx_skb = NULL; - - /* cmdA {25:24] data alignment [20:16] start offset [10:0] buffer length */ - /* cmdB {31:16] pkt tag [10:0] length */ -#ifdef SMC_USE_DMA - /* 16 byte buffer alignment mode */ - buf = (char*)((u32)(skb->data) & ~0xF); - len = (skb->len + 0xF + ((u32)skb->data & 0xF)) & ~0xF; - cmdA = (1<<24) | (((u32)skb->data & 0xF)<<16) | - TX_CMD_A_INT_FIRST_SEG_ | TX_CMD_A_INT_LAST_SEG_ | - skb->len; -#else - buf = (char *)((uintptr_t)skb->data & ~0x3); - len = (skb->len + 3 + ((uintptr_t)skb->data & 3)) & ~0x3; - cmdA = (((uintptr_t)skb->data & 0x3) << 16) | - TX_CMD_A_INT_FIRST_SEG_ | TX_CMD_A_INT_LAST_SEG_ | - skb->len; -#endif - /* tag is packet length so we can use this in stats update later */ - cmdB = (skb->len << 16) | (skb->len & 0x7FF); - - DBG(SMC_DEBUG_TX, dev, "TX PKT LENGTH 0x%04x (%d) BUF 0x%p CMDA 0x%08x CMDB 0x%08x\n", - len, len, buf, cmdA, cmdB); - SMC_SET_TX_FIFO(lp, cmdA); - SMC_SET_TX_FIFO(lp, cmdB); - - DBG(SMC_DEBUG_PKTS, dev, "Transmitted packet\n"); - PRINT_PKT(buf, min(len, 64U)); - - /* Send pkt via PIO or DMA */ -#ifdef SMC_USE_DMA - lp->current_tx_skb = skb; - SMC_PUSH_DATA(lp, buf, len); - /* DMA complete IRQ will free buffer and set jiffies */ -#else - SMC_PUSH_DATA(lp, buf, len); - netif_trans_update(dev); - dev_kfree_skb_irq(skb); -#endif - if (!lp->tx_throttle) { - netif_wake_queue(dev); - } - SMC_ENABLE_INT(lp, INT_EN_TDFA_EN_ | INT_EN_TSFL_EN_); -} - -/* - * Since I am not sure if I will have enough room in the chip's ram - * to store the packet, I call this routine which either sends it - * now, or set the card to generates an interrupt when ready - * for the packet. - */ -static netdev_tx_t -smc911x_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) -{ - struct smc911x_local *lp = netdev_priv(dev); - unsigned int free; - unsigned long flags; - - DBG(SMC_DEBUG_FUNC | SMC_DEBUG_TX, dev, "--> %s\n", - __func__); - - spin_lock_irqsave(&lp->lock, flags); - - BUG_ON(lp->pending_tx_skb != NULL); - - free = SMC_GET_TX_FIFO_INF(lp) & TX_FIFO_INF_TDFREE_; - DBG(SMC_DEBUG_TX, dev, "TX free space %d\n", free); - - /* Turn off the flow when running out of space in FIFO */ - if (free <= SMC911X_TX_FIFO_LOW_THRESHOLD) { - DBG(SMC_DEBUG_TX, dev, "Disabling data flow due to low FIFO space (%d)\n", - free); - /* Reenable when at least 1 packet of size MTU present */ - SMC_SET_FIFO_TDA(lp, (SMC911X_TX_FIFO_LOW_THRESHOLD)/64); - lp->tx_throttle = 1; - netif_stop_queue(dev); - } - - /* Drop packets when we run out of space in TX FIFO - * Account for overhead required for: - * - * Tx command words 8 bytes - * Start offset 15 bytes - * End padding 15 bytes - */ - if (unlikely(free < (skb->len + 8 + 15 + 15))) { - netdev_warn(dev, "No Tx free space %d < %d\n", - free, skb->len); - lp->pending_tx_skb = NULL; - dev->stats.tx_errors++; - dev->stats.tx_dropped++; - spin_unlock_irqrestore(&lp->lock, flags); - dev_kfree_skb_any(skb); - return NETDEV_TX_OK; - } - -#ifdef SMC_USE_DMA - { - /* If the DMA is already running then defer this packet Tx until - * the DMA IRQ starts it - */ - if (lp->txdma_active) { - DBG(SMC_DEBUG_TX | SMC_DEBUG_DMA, dev, "Tx DMA running, deferring packet\n"); - lp->pending_tx_skb = skb; - netif_stop_queue(dev); - spin_unlock_irqrestore(&lp->lock, flags); - return NETDEV_TX_OK; - } else { - DBG(SMC_DEBUG_TX | SMC_DEBUG_DMA, dev, "Activating Tx DMA\n"); - lp->txdma_active = 1; - } - } -#endif - lp->pending_tx_skb = skb; - smc911x_hardware_send_pkt(dev); - spin_unlock_irqrestore(&lp->lock, flags); - - return NETDEV_TX_OK; -} - -/* - * This handles a TX status interrupt, which is only called when: - * - a TX error occurred, or - * - TX of a packet completed. - */ -static void smc911x_tx(struct net_device *dev) -{ - struct smc911x_local *lp = netdev_priv(dev); - unsigned int tx_status; - - DBG(SMC_DEBUG_FUNC | SMC_DEBUG_TX, dev, "--> %s\n", - __func__); - - /* Collect the TX status */ - while (((SMC_GET_TX_FIFO_INF(lp) & TX_FIFO_INF_TSUSED_) >> 16) != 0) { - DBG(SMC_DEBUG_TX, dev, "Tx stat FIFO used 0x%04x\n", - (SMC_GET_TX_FIFO_INF(lp) & TX_FIFO_INF_TSUSED_) >> 16); - tx_status = SMC_GET_TX_STS_FIFO(lp); - dev->stats.tx_packets++; - dev->stats.tx_bytes+=tx_status>>16; - DBG(SMC_DEBUG_TX, dev, "Tx FIFO tag 0x%04x status 0x%04x\n", - (tx_status & 0xffff0000) >> 16, - tx_status & 0x0000ffff); - /* count Tx errors, but ignore lost carrier errors when in - * full-duplex mode */ - if ((tx_status & TX_STS_ES_) && !(lp->ctl_rfduplx && - !(tx_status & 0x00000306))) { - dev->stats.tx_errors++; - } - if (tx_status & TX_STS_MANY_COLL_) { - dev->stats.collisions+=16; - dev->stats.tx_aborted_errors++; - } else { - dev->stats.collisions+=(tx_status & TX_STS_COLL_CNT_) >> 3; - } - /* carrier error only has meaning for half-duplex communication */ - if ((tx_status & (TX_STS_LOC_ | TX_STS_NO_CARR_)) && - !lp->ctl_rfduplx) { - dev->stats.tx_carrier_errors++; - } - if (tx_status & TX_STS_LATE_COLL_) { - dev->stats.collisions++; - dev->stats.tx_aborted_errors++; - } - } -} - - -/*---PHY CONTROL AND CONFIGURATION-----------------------------------------*/ -/* - * Reads a register from the MII Management serial interface - */ - -static int smc911x_phy_read(struct net_device *dev, int phyaddr, int phyreg) -{ - struct smc911x_local *lp = netdev_priv(dev); - unsigned int phydata; - - SMC_GET_MII(lp, phyreg, phyaddr, phydata); - - DBG(SMC_DEBUG_MISC, dev, "%s: phyaddr=0x%x, phyreg=0x%02x, phydata=0x%04x\n", - __func__, phyaddr, phyreg, phydata); - return phydata; -} - - -/* - * Writes a register to the MII Management serial interface - */ -static void smc911x_phy_write(struct net_device *dev, int phyaddr, int phyreg, - int phydata) -{ - struct smc911x_local *lp = netdev_priv(dev); - - DBG(SMC_DEBUG_MISC, dev, "%s: phyaddr=0x%x, phyreg=0x%x, phydata=0x%x\n", - __func__, phyaddr, phyreg, phydata); - - SMC_SET_MII(lp, phyreg, phyaddr, phydata); -} - -/* - * Finds and reports the PHY address (115 and 117 have external - * PHY interface 118 has internal only - */ -static void smc911x_phy_detect(struct net_device *dev) -{ - struct smc911x_local *lp = netdev_priv(dev); - int phyaddr; - unsigned int cfg, id1, id2; - - DBG(SMC_DEBUG_FUNC, dev, "--> %s\n", __func__); - - lp->phy_type = 0; - - /* - * Scan all 32 PHY addresses if necessary, starting at - * PHY#1 to PHY#31, and then PHY#0 last. - */ - switch(lp->version) { - case CHIP_9115: - case CHIP_9117: - case CHIP_9215: - case CHIP_9217: - cfg = SMC_GET_HW_CFG(lp); - if (cfg & HW_CFG_EXT_PHY_DET_) { - cfg &= ~HW_CFG_PHY_CLK_SEL_; - cfg |= HW_CFG_PHY_CLK_SEL_CLK_DIS_; - SMC_SET_HW_CFG(lp, cfg); - udelay(10); /* Wait for clocks to stop */ - - cfg |= HW_CFG_EXT_PHY_EN_; - SMC_SET_HW_CFG(lp, cfg); - udelay(10); /* Wait for clocks to stop */ - - cfg &= ~HW_CFG_PHY_CLK_SEL_; - cfg |= HW_CFG_PHY_CLK_SEL_EXT_PHY_; - SMC_SET_HW_CFG(lp, cfg); - udelay(10); /* Wait for clocks to stop */ - - cfg |= HW_CFG_SMI_SEL_; - SMC_SET_HW_CFG(lp, cfg); - - for (phyaddr = 1; phyaddr < 32; ++phyaddr) { - - /* Read the PHY identifiers */ - SMC_GET_PHY_ID1(lp, phyaddr & 31, id1); - SMC_GET_PHY_ID2(lp, phyaddr & 31, id2); - - /* Make sure it is a valid identifier */ - if (id1 != 0x0000 && id1 != 0xffff && - id1 != 0x8000 && id2 != 0x0000 && - id2 != 0xffff && id2 != 0x8000) { - /* Save the PHY's address */ - lp->mii.phy_id = phyaddr & 31; - lp->phy_type = id1 << 16 | id2; - break; - } - } - if (phyaddr < 32) - /* Found an external PHY */ - break; - } - fallthrough; - default: - /* Internal media only */ - SMC_GET_PHY_ID1(lp, 1, id1); - SMC_GET_PHY_ID2(lp, 1, id2); - /* Save the PHY's address */ - lp->mii.phy_id = 1; - lp->phy_type = id1 << 16 | id2; - } - - DBG(SMC_DEBUG_MISC, dev, "phy_id1=0x%x, phy_id2=0x%x phyaddr=0x%x\n", - id1, id2, lp->mii.phy_id); -} - -/* - * Sets the PHY to a configuration as determined by the user. - * Called with spin_lock held. - */ -static int smc911x_phy_fixed(struct net_device *dev) -{ - struct smc911x_local *lp = netdev_priv(dev); - int phyaddr = lp->mii.phy_id; - int bmcr; - - DBG(SMC_DEBUG_FUNC, dev, "--> %s\n", __func__); - - /* Enter Link Disable state */ - SMC_GET_PHY_BMCR(lp, phyaddr, bmcr); - bmcr |= BMCR_PDOWN; - SMC_SET_PHY_BMCR(lp, phyaddr, bmcr); - - /* - * Set our fixed capabilities - * Disable auto-negotiation - */ - bmcr &= ~BMCR_ANENABLE; - if (lp->ctl_rfduplx) - bmcr |= BMCR_FULLDPLX; - - if (lp->ctl_rspeed == 100) - bmcr |= BMCR_SPEED100; - - /* Write our capabilities to the phy control register */ - SMC_SET_PHY_BMCR(lp, phyaddr, bmcr); - - /* Re-Configure the Receive/Phy Control register */ - bmcr &= ~BMCR_PDOWN; - SMC_SET_PHY_BMCR(lp, phyaddr, bmcr); - - return 1; -} - -/** - * smc911x_phy_reset - reset the phy - * @dev: net device - * @phy: phy address - * - * Issue a software reset for the specified PHY and - * wait up to 100ms for the reset to complete. We should - * not access the PHY for 50ms after issuing the reset. - * - * The time to wait appears to be dependent on the PHY. - * - */ -static int smc911x_phy_reset(struct net_device *dev, int phy) -{ - struct smc911x_local *lp = netdev_priv(dev); - int timeout; - unsigned long flags; - unsigned int reg; - - DBG(SMC_DEBUG_FUNC, dev, "--> %s()\n", __func__); - - spin_lock_irqsave(&lp->lock, flags); - reg = SMC_GET_PMT_CTRL(lp); - reg &= ~0xfffff030; - reg |= PMT_CTRL_PHY_RST_; - SMC_SET_PMT_CTRL(lp, reg); - spin_unlock_irqrestore(&lp->lock, flags); - for (timeout = 2; timeout; timeout--) { - msleep(50); - spin_lock_irqsave(&lp->lock, flags); - reg = SMC_GET_PMT_CTRL(lp); - spin_unlock_irqrestore(&lp->lock, flags); - if (!(reg & PMT_CTRL_PHY_RST_)) { - /* extra delay required because the phy may - * not be completed with its reset - * when PHY_BCR_RESET_ is cleared. 256us - * should suffice, but use 500us to be safe - */ - udelay(500); - break; - } - } - - return reg & PMT_CTRL_PHY_RST_; -} - -/** - * smc911x_phy_powerdown - powerdown phy - * @dev: net device - * @phy: phy address - * - * Power down the specified PHY - */ -static void smc911x_phy_powerdown(struct net_device *dev, int phy) -{ - struct smc911x_local *lp = netdev_priv(dev); - unsigned int bmcr; - - /* Enter Link Disable state */ - SMC_GET_PHY_BMCR(lp, phy, bmcr); - bmcr |= BMCR_PDOWN; - SMC_SET_PHY_BMCR(lp, phy, bmcr); -} - -/** - * smc911x_phy_check_media - check the media status and adjust BMCR - * @dev: net device - * @init: set true for initialisation - * - * Select duplex mode depending on negotiation state. This - * also updates our carrier state. - */ -static void smc911x_phy_check_media(struct net_device *dev, int init) -{ - struct smc911x_local *lp = netdev_priv(dev); - int phyaddr = lp->mii.phy_id; - unsigned int bmcr, cr; - - DBG(SMC_DEBUG_FUNC, dev, "--> %s\n", __func__); - - if (mii_check_media(&lp->mii, netif_msg_link(lp), init)) { - /* duplex state has changed */ - SMC_GET_PHY_BMCR(lp, phyaddr, bmcr); - SMC_GET_MAC_CR(lp, cr); - if (lp->mii.full_duplex) { - DBG(SMC_DEBUG_MISC, dev, "Configuring for full-duplex mode\n"); - bmcr |= BMCR_FULLDPLX; - cr |= MAC_CR_RCVOWN_; - } else { - DBG(SMC_DEBUG_MISC, dev, "Configuring for half-duplex mode\n"); - bmcr &= ~BMCR_FULLDPLX; - cr &= ~MAC_CR_RCVOWN_; - } - SMC_SET_PHY_BMCR(lp, phyaddr, bmcr); - SMC_SET_MAC_CR(lp, cr); - } -} - -/* - * Configures the specified PHY through the MII management interface - * using Autonegotiation. - * Calls smc911x_phy_fixed() if the user has requested a certain config. - * If RPC ANEG bit is set, the media selection is dependent purely on - * the selection by the MII (either in the MII BMCR reg or the result - * of autonegotiation.) If the RPC ANEG bit is cleared, the selection - * is controlled by the RPC SPEED and RPC DPLX bits. - */ -static void smc911x_phy_configure(struct work_struct *work) -{ - struct smc911x_local *lp = container_of(work, struct smc911x_local, - phy_configure); - struct net_device *dev = lp->netdev; - int phyaddr = lp->mii.phy_id; - int my_phy_caps; /* My PHY capabilities */ - int my_ad_caps; /* My Advertised capabilities */ - int status __always_unused; - unsigned long flags; - - DBG(SMC_DEBUG_FUNC, dev, "--> %s()\n", __func__); - - /* - * We should not be called if phy_type is zero. - */ - if (lp->phy_type == 0) - return; - - if (smc911x_phy_reset(dev, phyaddr)) { - netdev_info(dev, "PHY reset timed out\n"); - return; - } - spin_lock_irqsave(&lp->lock, flags); - - /* - * Enable PHY Interrupts (for register 18) - * Interrupts listed here are enabled - */ - SMC_SET_PHY_INT_MASK(lp, phyaddr, PHY_INT_MASK_ENERGY_ON_ | - PHY_INT_MASK_ANEG_COMP_ | PHY_INT_MASK_REMOTE_FAULT_ | - PHY_INT_MASK_LINK_DOWN_); - - /* If the user requested no auto neg, then go set his request */ - if (lp->mii.force_media) { - smc911x_phy_fixed(dev); - goto smc911x_phy_configure_exit; - } - - /* Copy our capabilities from MII_BMSR to MII_ADVERTISE */ - SMC_GET_PHY_BMSR(lp, phyaddr, my_phy_caps); - if (!(my_phy_caps & BMSR_ANEGCAPABLE)) { - netdev_info(dev, "Auto negotiation NOT supported\n"); - smc911x_phy_fixed(dev); - goto smc911x_phy_configure_exit; - } - - /* CSMA capable w/ both pauses */ - my_ad_caps = ADVERTISE_CSMA | ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM; - - if (my_phy_caps & BMSR_100BASE4) - my_ad_caps |= ADVERTISE_100BASE4; - if (my_phy_caps & BMSR_100FULL) - my_ad_caps |= ADVERTISE_100FULL; - if (my_phy_caps & BMSR_100HALF) - my_ad_caps |= ADVERTISE_100HALF; - if (my_phy_caps & BMSR_10FULL) - my_ad_caps |= ADVERTISE_10FULL; - if (my_phy_caps & BMSR_10HALF) - my_ad_caps |= ADVERTISE_10HALF; - - /* Disable capabilities not selected by our user */ - if (lp->ctl_rspeed != 100) - my_ad_caps &= ~(ADVERTISE_100BASE4|ADVERTISE_100FULL|ADVERTISE_100HALF); - - if (!lp->ctl_rfduplx) - my_ad_caps &= ~(ADVERTISE_100FULL|ADVERTISE_10FULL); - - /* Update our Auto-Neg Advertisement Register */ - SMC_SET_PHY_MII_ADV(lp, phyaddr, my_ad_caps); - lp->mii.advertising = my_ad_caps; - - /* - * Read the register back. Without this, it appears that when - * auto-negotiation is restarted, sometimes it isn't ready and - * the link does not come up. - */ - udelay(10); - SMC_GET_PHY_MII_ADV(lp, phyaddr, status); - - DBG(SMC_DEBUG_MISC, dev, "phy caps=0x%04x\n", my_phy_caps); - DBG(SMC_DEBUG_MISC, dev, "phy advertised caps=0x%04x\n", my_ad_caps); - - /* Restart auto-negotiation process in order to advertise my caps */ - SMC_SET_PHY_BMCR(lp, phyaddr, BMCR_ANENABLE | BMCR_ANRESTART); - - smc911x_phy_check_media(dev, 1); - -smc911x_phy_configure_exit: - spin_unlock_irqrestore(&lp->lock, flags); -} - -/* - * smc911x_phy_interrupt - * - * Purpose: Handle interrupts relating to PHY register 18. This is - * called from the "hard" interrupt handler under our private spinlock. - */ -static void smc911x_phy_interrupt(struct net_device *dev) -{ - struct smc911x_local *lp = netdev_priv(dev); - int phyaddr = lp->mii.phy_id; - int status __always_unused; - - DBG(SMC_DEBUG_FUNC, dev, "--> %s\n", __func__); - - if (lp->phy_type == 0) - return; - - smc911x_phy_check_media(dev, 0); - /* read to clear status bits */ - SMC_GET_PHY_INT_SRC(lp, phyaddr,status); - DBG(SMC_DEBUG_MISC, dev, "PHY interrupt status 0x%04x\n", - status & 0xffff); - DBG(SMC_DEBUG_MISC, dev, "AFC_CFG 0x%08x\n", - SMC_GET_AFC_CFG(lp)); -} - -/*--- END PHY CONTROL AND CONFIGURATION-------------------------------------*/ - -/* - * This is the main routine of the driver, to handle the device when - * it needs some attention. - */ -static irqreturn_t smc911x_interrupt(int irq, void *dev_id) -{ - struct net_device *dev = dev_id; - struct smc911x_local *lp = netdev_priv(dev); - unsigned int status, mask, timeout; - unsigned int rx_overrun=0, cr, pkts; - unsigned long flags; - - DBG(SMC_DEBUG_FUNC, dev, "--> %s\n", __func__); - - spin_lock_irqsave(&lp->lock, flags); - - /* Spurious interrupt check */ - if ((SMC_GET_IRQ_CFG(lp) & (INT_CFG_IRQ_INT_ | INT_CFG_IRQ_EN_)) != - (INT_CFG_IRQ_INT_ | INT_CFG_IRQ_EN_)) { - spin_unlock_irqrestore(&lp->lock, flags); - return IRQ_NONE; - } - - mask = SMC_GET_INT_EN(lp); - SMC_SET_INT_EN(lp, 0); - - /* set a timeout value, so I don't stay here forever */ - timeout = 8; - - - do { - status = SMC_GET_INT(lp); - - DBG(SMC_DEBUG_MISC, dev, "INT 0x%08x MASK 0x%08x OUTSIDE MASK 0x%08x\n", - status, mask, status & ~mask); - - status &= mask; - if (!status) - break; - - /* Handle SW interrupt condition */ - if (status & INT_STS_SW_INT_) { - SMC_ACK_INT(lp, INT_STS_SW_INT_); - mask &= ~INT_EN_SW_INT_EN_; - } - /* Handle various error conditions */ - if (status & INT_STS_RXE_) { - SMC_ACK_INT(lp, INT_STS_RXE_); - dev->stats.rx_errors++; - } - if (status & INT_STS_RXDFH_INT_) { - SMC_ACK_INT(lp, INT_STS_RXDFH_INT_); - dev->stats.rx_dropped+=SMC_GET_RX_DROP(lp); - } - /* Undocumented interrupt-what is the right thing to do here? */ - if (status & INT_STS_RXDF_INT_) { - SMC_ACK_INT(lp, INT_STS_RXDF_INT_); - } - - /* Rx Data FIFO exceeds set level */ - if (status & INT_STS_RDFL_) { - if (IS_REV_A(lp->revision)) { - rx_overrun=1; - SMC_GET_MAC_CR(lp, cr); - cr &= ~MAC_CR_RXEN_; - SMC_SET_MAC_CR(lp, cr); - DBG(SMC_DEBUG_RX, dev, "RX overrun\n"); - dev->stats.rx_errors++; - dev->stats.rx_fifo_errors++; - } - SMC_ACK_INT(lp, INT_STS_RDFL_); - } - if (status & INT_STS_RDFO_) { - if (!IS_REV_A(lp->revision)) { - SMC_GET_MAC_CR(lp, cr); - cr &= ~MAC_CR_RXEN_; - SMC_SET_MAC_CR(lp, cr); - rx_overrun=1; - DBG(SMC_DEBUG_RX, dev, "RX overrun\n"); - dev->stats.rx_errors++; - dev->stats.rx_fifo_errors++; - } - SMC_ACK_INT(lp, INT_STS_RDFO_); - } - /* Handle receive condition */ - if ((status & INT_STS_RSFL_) || rx_overrun) { - unsigned int fifo; - DBG(SMC_DEBUG_RX, dev, "RX irq\n"); - fifo = SMC_GET_RX_FIFO_INF(lp); - pkts = (fifo & RX_FIFO_INF_RXSUSED_) >> 16; - DBG(SMC_DEBUG_RX, dev, "Rx FIFO pkts %d, bytes %d\n", - pkts, fifo & 0xFFFF); - if (pkts != 0) { -#ifdef SMC_USE_DMA - unsigned int fifo; - if (lp->rxdma_active){ - DBG(SMC_DEBUG_RX | SMC_DEBUG_DMA, dev, - "RX DMA active\n"); - /* The DMA is already running so up the IRQ threshold */ - fifo = SMC_GET_FIFO_INT(lp) & ~0xFF; - fifo |= pkts & 0xFF; - DBG(SMC_DEBUG_RX, dev, - "Setting RX stat FIFO threshold to %d\n", - fifo & 0xff); - SMC_SET_FIFO_INT(lp, fifo); - } else -#endif - smc911x_rcv(dev); - } - SMC_ACK_INT(lp, INT_STS_RSFL_); - } - /* Handle transmit FIFO available */ - if (status & INT_STS_TDFA_) { - DBG(SMC_DEBUG_TX, dev, "TX data FIFO space available irq\n"); - SMC_SET_FIFO_TDA(lp, 0xFF); - lp->tx_throttle = 0; -#ifdef SMC_USE_DMA - if (!lp->txdma_active) -#endif - netif_wake_queue(dev); - SMC_ACK_INT(lp, INT_STS_TDFA_); - } - /* Handle transmit done condition */ -#if 1 - if (status & (INT_STS_TSFL_ | INT_STS_GPT_INT_)) { - DBG(SMC_DEBUG_TX | SMC_DEBUG_MISC, dev, - "Tx stat FIFO limit (%d) /GPT irq\n", - (SMC_GET_FIFO_INT(lp) & 0x00ff0000) >> 16); - smc911x_tx(dev); - SMC_SET_GPT_CFG(lp, GPT_CFG_TIMER_EN_ | 10000); - SMC_ACK_INT(lp, INT_STS_TSFL_); - SMC_ACK_INT(lp, INT_STS_TSFL_ | INT_STS_GPT_INT_); - } -#else - if (status & INT_STS_TSFL_) { - DBG(SMC_DEBUG_TX, dev, "TX status FIFO limit (%d) irq\n", ?); - smc911x_tx(dev); - SMC_ACK_INT(lp, INT_STS_TSFL_); - } - - if (status & INT_STS_GPT_INT_) { - DBG(SMC_DEBUG_RX, dev, "IRQ_CFG 0x%08x FIFO_INT 0x%08x RX_CFG 0x%08x\n", - SMC_GET_IRQ_CFG(lp), - SMC_GET_FIFO_INT(lp), - SMC_GET_RX_CFG(lp)); - DBG(SMC_DEBUG_RX, dev, "Rx Stat FIFO Used 0x%02x Data FIFO Used 0x%04x Stat FIFO 0x%08x\n", - (SMC_GET_RX_FIFO_INF(lp) & 0x00ff0000) >> 16, - SMC_GET_RX_FIFO_INF(lp) & 0xffff, - SMC_GET_RX_STS_FIFO_PEEK(lp)); - SMC_SET_GPT_CFG(lp, GPT_CFG_TIMER_EN_ | 10000); - SMC_ACK_INT(lp, INT_STS_GPT_INT_); - } -#endif - - /* Handle PHY interrupt condition */ - if (status & INT_STS_PHY_INT_) { - DBG(SMC_DEBUG_MISC, dev, "PHY irq\n"); - smc911x_phy_interrupt(dev); - SMC_ACK_INT(lp, INT_STS_PHY_INT_); - } - } while (--timeout); - - /* restore mask state */ - SMC_SET_INT_EN(lp, mask); - - DBG(SMC_DEBUG_MISC, dev, "Interrupt done (%d loops)\n", - 8-timeout); - - spin_unlock_irqrestore(&lp->lock, flags); - - return IRQ_HANDLED; -} - -#ifdef SMC_USE_DMA -static void -smc911x_tx_dma_irq(void *data) -{ - struct smc911x_local *lp = data; - struct net_device *dev = lp->netdev; - struct sk_buff *skb = lp->current_tx_skb; - unsigned long flags; - - DBG(SMC_DEBUG_FUNC, dev, "--> %s\n", __func__); - - DBG(SMC_DEBUG_TX | SMC_DEBUG_DMA, dev, "TX DMA irq handler\n"); - BUG_ON(skb == NULL); - dma_unmap_single(lp->dev, tx_dmabuf, tx_dmalen, DMA_TO_DEVICE); - netif_trans_update(dev); - dev_kfree_skb_irq(skb); - lp->current_tx_skb = NULL; - if (lp->pending_tx_skb != NULL) - smc911x_hardware_send_pkt(dev); - else { - DBG(SMC_DEBUG_TX | SMC_DEBUG_DMA, dev, - "No pending Tx packets. DMA disabled\n"); - spin_lock_irqsave(&lp->lock, flags); - lp->txdma_active = 0; - if (!lp->tx_throttle) { - netif_wake_queue(dev); - } - spin_unlock_irqrestore(&lp->lock, flags); - } - - DBG(SMC_DEBUG_TX | SMC_DEBUG_DMA, dev, - "TX DMA irq completed\n"); -} -static void -smc911x_rx_dma_irq(void *data) -{ - struct smc911x_local *lp = data; - struct net_device *dev = lp->netdev; - struct sk_buff *skb = lp->current_rx_skb; - unsigned long flags; - unsigned int pkts; - - DBG(SMC_DEBUG_FUNC, dev, "--> %s\n", __func__); - DBG(SMC_DEBUG_RX | SMC_DEBUG_DMA, dev, "RX DMA irq handler\n"); - dma_unmap_single(lp->dev, rx_dmabuf, rx_dmalen, DMA_FROM_DEVICE); - BUG_ON(skb == NULL); - lp->current_rx_skb = NULL; - PRINT_PKT(skb->data, skb->len); - skb->protocol = eth_type_trans(skb, dev); - dev->stats.rx_packets++; - dev->stats.rx_bytes += skb->len; - netif_rx(skb); - - spin_lock_irqsave(&lp->lock, flags); - pkts = (SMC_GET_RX_FIFO_INF(lp) & RX_FIFO_INF_RXSUSED_) >> 16; - if (pkts != 0) { - smc911x_rcv(dev); - }else { - lp->rxdma_active = 0; - } - spin_unlock_irqrestore(&lp->lock, flags); - DBG(SMC_DEBUG_RX | SMC_DEBUG_DMA, dev, - "RX DMA irq completed. DMA RX FIFO PKTS %d\n", - pkts); -} -#endif /* SMC_USE_DMA */ - -#ifdef CONFIG_NET_POLL_CONTROLLER -/* - * Polling receive - used by netconsole and other diagnostic tools - * to allow network i/o with interrupts disabled. - */ -static void smc911x_poll_controller(struct net_device *dev) -{ - disable_irq(dev->irq); - smc911x_interrupt(dev->irq, dev); - enable_irq(dev->irq); -} -#endif - -/* Our watchdog timed out. Called by the networking layer */ -static void smc911x_timeout(struct net_device *dev, unsigned int txqueue) -{ - struct smc911x_local *lp = netdev_priv(dev); - int status, mask; - unsigned long flags; - - DBG(SMC_DEBUG_FUNC, dev, "--> %s\n", __func__); - - spin_lock_irqsave(&lp->lock, flags); - status = SMC_GET_INT(lp); - mask = SMC_GET_INT_EN(lp); - spin_unlock_irqrestore(&lp->lock, flags); - DBG(SMC_DEBUG_MISC, dev, "INT 0x%02x MASK 0x%02x\n", - status, mask); - - /* Dump the current TX FIFO contents and restart */ - mask = SMC_GET_TX_CFG(lp); - SMC_SET_TX_CFG(lp, mask | TX_CFG_TXS_DUMP_ | TX_CFG_TXD_DUMP_); - /* - * Reconfiguring the PHY doesn't seem like a bad idea here, but - * smc911x_phy_configure() calls msleep() which calls schedule_timeout() - * which calls schedule(). Hence we use a work queue. - */ - if (lp->phy_type != 0) - schedule_work(&lp->phy_configure); - - /* We can accept TX packets again */ - netif_trans_update(dev); /* prevent tx timeout */ - netif_wake_queue(dev); -} - -/* - * This routine will, depending on the values passed to it, - * either make it accept multicast packets, go into - * promiscuous mode (for TCPDUMP and cousins) or accept - * a select set of multicast packets - */ -static void smc911x_set_multicast_list(struct net_device *dev) -{ - struct smc911x_local *lp = netdev_priv(dev); - unsigned int multicast_table[2]; - unsigned int mcr, update_multicast = 0; - unsigned long flags; - - DBG(SMC_DEBUG_FUNC, dev, "--> %s\n", __func__); - - spin_lock_irqsave(&lp->lock, flags); - SMC_GET_MAC_CR(lp, mcr); - spin_unlock_irqrestore(&lp->lock, flags); - - if (dev->flags & IFF_PROMISC) { - - DBG(SMC_DEBUG_MISC, dev, "RCR_PRMS\n"); - mcr |= MAC_CR_PRMS_; - } - /* - * Here, I am setting this to accept all multicast packets. - * I don't need to zero the multicast table, because the flag is - * checked before the table is - */ - else if (dev->flags & IFF_ALLMULTI || netdev_mc_count(dev) > 16) { - DBG(SMC_DEBUG_MISC, dev, "RCR_ALMUL\n"); - mcr |= MAC_CR_MCPAS_; - } - - /* - * This sets the internal hardware table to filter out unwanted - * multicast packets before they take up memory. - * - * The SMC chip uses a hash table where the high 6 bits of the CRC of - * address are the offset into the table. If that bit is 1, then the - * multicast packet is accepted. Otherwise, it's dropped silently. - * - * To use the 6 bits as an offset into the table, the high 1 bit is - * the number of the 32 bit register, while the low 5 bits are the bit - * within that register. - */ - else if (!netdev_mc_empty(dev)) { - struct netdev_hw_addr *ha; - - /* Set the Hash perfec mode */ - mcr |= MAC_CR_HPFILT_; - - /* start with a table of all zeros: reject all */ - memset(multicast_table, 0, sizeof(multicast_table)); - - netdev_for_each_mc_addr(ha, dev) { - u32 position; - - /* upper 6 bits are used as hash index */ - position = ether_crc(ETH_ALEN, ha->addr)>>26; - - multicast_table[position>>5] |= 1 << (position&0x1f); - } - - /* be sure I get rid of flags I might have set */ - mcr &= ~(MAC_CR_PRMS_ | MAC_CR_MCPAS_); - - /* now, the table can be loaded into the chipset */ - update_multicast = 1; - } else { - DBG(SMC_DEBUG_MISC, dev, "~(MAC_CR_PRMS_|MAC_CR_MCPAS_)\n"); - mcr &= ~(MAC_CR_PRMS_ | MAC_CR_MCPAS_); - - /* - * since I'm disabling all multicast entirely, I need to - * clear the multicast list - */ - memset(multicast_table, 0, sizeof(multicast_table)); - update_multicast = 1; - } - - spin_lock_irqsave(&lp->lock, flags); - SMC_SET_MAC_CR(lp, mcr); - if (update_multicast) { - DBG(SMC_DEBUG_MISC, dev, - "update mcast hash table 0x%08x 0x%08x\n", - multicast_table[0], multicast_table[1]); - SMC_SET_HASHL(lp, multicast_table[0]); - SMC_SET_HASHH(lp, multicast_table[1]); - } - spin_unlock_irqrestore(&lp->lock, flags); -} - - -/* - * Open and Initialize the board - * - * Set up everything, reset the card, etc.. - */ -static int -smc911x_open(struct net_device *dev) -{ - struct smc911x_local *lp = netdev_priv(dev); - - DBG(SMC_DEBUG_FUNC, dev, "--> %s\n", __func__); - - /* reset the hardware */ - smc911x_reset(dev); - - /* Configure the PHY, initialize the link state */ - smc911x_phy_configure(&lp->phy_configure); - - /* Turn on Tx + Rx */ - smc911x_enable(dev); - - netif_start_queue(dev); - - return 0; -} - -/* - * smc911x_close - * - * this makes the board clean up everything that it can - * and not talk to the outside world. Caused by - * an 'ifconfig ethX down' - */ -static int smc911x_close(struct net_device *dev) -{ - struct smc911x_local *lp = netdev_priv(dev); - - DBG(SMC_DEBUG_FUNC, dev, "--> %s\n", __func__); - - netif_stop_queue(dev); - netif_carrier_off(dev); - - /* clear everything */ - smc911x_shutdown(dev); - - if (lp->phy_type != 0) { - /* We need to ensure that no calls to - * smc911x_phy_configure are pending. - */ - cancel_work_sync(&lp->phy_configure); - smc911x_phy_powerdown(dev, lp->mii.phy_id); - } - - if (lp->pending_tx_skb) { - dev_kfree_skb(lp->pending_tx_skb); - lp->pending_tx_skb = NULL; - } - - return 0; -} - -/* - * Ethtool support - */ -static int -smc911x_ethtool_get_link_ksettings(struct net_device *dev, - struct ethtool_link_ksettings *cmd) -{ - struct smc911x_local *lp = netdev_priv(dev); - int status; - unsigned long flags; - u32 supported; - - DBG(SMC_DEBUG_FUNC, dev, "--> %s\n", __func__); - - if (lp->phy_type != 0) { - spin_lock_irqsave(&lp->lock, flags); - mii_ethtool_get_link_ksettings(&lp->mii, cmd); - spin_unlock_irqrestore(&lp->lock, flags); - } else { - supported = SUPPORTED_10baseT_Half | - SUPPORTED_10baseT_Full | - SUPPORTED_TP | SUPPORTED_AUI; - - if (lp->ctl_rspeed == 10) - cmd->base.speed = SPEED_10; - else if (lp->ctl_rspeed == 100) - cmd->base.speed = SPEED_100; - - cmd->base.autoneg = AUTONEG_DISABLE; - cmd->base.port = 0; - SMC_GET_PHY_SPECIAL(lp, lp->mii.phy_id, status); - cmd->base.duplex = - (status & (PHY_SPECIAL_SPD_10FULL_ | PHY_SPECIAL_SPD_100FULL_)) ? - DUPLEX_FULL : DUPLEX_HALF; - - ethtool_convert_legacy_u32_to_link_mode( - cmd->link_modes.supported, supported); - - } - - return 0; -} - -static int -smc911x_ethtool_set_link_ksettings(struct net_device *dev, - const struct ethtool_link_ksettings *cmd) -{ - struct smc911x_local *lp = netdev_priv(dev); - int ret; - unsigned long flags; - - if (lp->phy_type != 0) { - spin_lock_irqsave(&lp->lock, flags); - ret = mii_ethtool_set_link_ksettings(&lp->mii, cmd); - spin_unlock_irqrestore(&lp->lock, flags); - } else { - if (cmd->base.autoneg != AUTONEG_DISABLE || - cmd->base.speed != SPEED_10 || - (cmd->base.duplex != DUPLEX_HALF && - cmd->base.duplex != DUPLEX_FULL) || - (cmd->base.port != PORT_TP && - cmd->base.port != PORT_AUI)) - return -EINVAL; - - lp->ctl_rfduplx = cmd->base.duplex == DUPLEX_FULL; - - ret = 0; - } - - return ret; -} - -static void -smc911x_ethtool_getdrvinfo(struct net_device *dev, struct ethtool_drvinfo *info) -{ - strscpy(info->driver, CARDNAME, sizeof(info->driver)); - strscpy(info->version, version, sizeof(info->version)); - strscpy(info->bus_info, dev_name(dev->dev.parent), - sizeof(info->bus_info)); -} - -static int smc911x_ethtool_nwayreset(struct net_device *dev) -{ - struct smc911x_local *lp = netdev_priv(dev); - int ret = -EINVAL; - unsigned long flags; - - if (lp->phy_type != 0) { - spin_lock_irqsave(&lp->lock, flags); - ret = mii_nway_restart(&lp->mii); - spin_unlock_irqrestore(&lp->lock, flags); - } - - return ret; -} - -static u32 smc911x_ethtool_getmsglevel(struct net_device *dev) -{ - struct smc911x_local *lp = netdev_priv(dev); - return lp->msg_enable; -} - -static void smc911x_ethtool_setmsglevel(struct net_device *dev, u32 level) -{ - struct smc911x_local *lp = netdev_priv(dev); - lp->msg_enable = level; -} - -static int smc911x_ethtool_getregslen(struct net_device *dev) -{ - /* System regs + MAC regs + PHY regs */ - return (((E2P_CMD - ID_REV)/4 + 1) + - (WUCSR - MAC_CR)+1 + 32) * sizeof(u32); -} - -static void smc911x_ethtool_getregs(struct net_device *dev, - struct ethtool_regs *regs, void *buf) -{ - struct smc911x_local *lp = netdev_priv(dev); - unsigned long flags; - u32 reg,i,j=0; - u32 *data = (u32*)buf; - - regs->version = lp->version; - for(i=ID_REV;i<=E2P_CMD;i+=4) { - data[j++] = SMC_inl(lp, i); - } - for(i=MAC_CR;i<=WUCSR;i++) { - spin_lock_irqsave(&lp->lock, flags); - SMC_GET_MAC_CSR(lp, i, reg); - spin_unlock_irqrestore(&lp->lock, flags); - data[j++] = reg; - } - for(i=0;i<=31;i++) { - spin_lock_irqsave(&lp->lock, flags); - SMC_GET_MII(lp, i, lp->mii.phy_id, reg); - spin_unlock_irqrestore(&lp->lock, flags); - data[j++] = reg & 0xFFFF; - } -} - -static int smc911x_ethtool_wait_eeprom_ready(struct net_device *dev) -{ - struct smc911x_local *lp = netdev_priv(dev); - unsigned int timeout; - int e2p_cmd; - - e2p_cmd = SMC_GET_E2P_CMD(lp); - for(timeout=10;(e2p_cmd & E2P_CMD_EPC_BUSY_) && timeout; timeout--) { - if (e2p_cmd & E2P_CMD_EPC_TIMEOUT_) { - PRINTK(dev, "%s timeout waiting for EEPROM to respond\n", - __func__); - return -EFAULT; - } - mdelay(1); - e2p_cmd = SMC_GET_E2P_CMD(lp); - } - if (timeout == 0) { - PRINTK(dev, "%s timeout waiting for EEPROM CMD not busy\n", - __func__); - return -ETIMEDOUT; - } - return 0; -} - -static inline int smc911x_ethtool_write_eeprom_cmd(struct net_device *dev, - int cmd, int addr) -{ - struct smc911x_local *lp = netdev_priv(dev); - int ret; - - if ((ret = smc911x_ethtool_wait_eeprom_ready(dev))!=0) - return ret; - SMC_SET_E2P_CMD(lp, E2P_CMD_EPC_BUSY_ | - ((cmd) & (0x7<<28)) | - ((addr) & 0xFF)); - return 0; -} - -static inline int smc911x_ethtool_read_eeprom_byte(struct net_device *dev, - u8 *data) -{ - struct smc911x_local *lp = netdev_priv(dev); - int ret; - - if ((ret = smc911x_ethtool_wait_eeprom_ready(dev))!=0) - return ret; - *data = SMC_GET_E2P_DATA(lp); - return 0; -} - -static inline int smc911x_ethtool_write_eeprom_byte(struct net_device *dev, - u8 data) -{ - struct smc911x_local *lp = netdev_priv(dev); - int ret; - - if ((ret = smc911x_ethtool_wait_eeprom_ready(dev))!=0) - return ret; - SMC_SET_E2P_DATA(lp, data); - return 0; -} - -static int smc911x_ethtool_geteeprom(struct net_device *dev, - struct ethtool_eeprom *eeprom, u8 *data) -{ - u8 eebuf[SMC911X_EEPROM_LEN]; - int i, ret; - - for(i=0;ioffset, eeprom->len); - return 0; -} - -static int smc911x_ethtool_seteeprom(struct net_device *dev, - struct ethtool_eeprom *eeprom, u8 *data) -{ - int i, ret; - - /* Enable erase */ - if ((ret=smc911x_ethtool_write_eeprom_cmd(dev, E2P_CMD_EPC_CMD_EWEN_, 0 ))!=0) - return ret; - for(i=eeprom->offset;i<(eeprom->offset+eeprom->len);i++) { - /* erase byte */ - if ((ret=smc911x_ethtool_write_eeprom_cmd(dev, E2P_CMD_EPC_CMD_ERASE_, i ))!=0) - return ret; - /* write byte */ - if ((ret=smc911x_ethtool_write_eeprom_byte(dev, *data))!=0) - return ret; - if ((ret=smc911x_ethtool_write_eeprom_cmd(dev, E2P_CMD_EPC_CMD_WRITE_, i ))!=0) - return ret; - } - return 0; -} - -static int smc911x_ethtool_geteeprom_len(struct net_device *dev) -{ - return SMC911X_EEPROM_LEN; -} - -static const struct ethtool_ops smc911x_ethtool_ops = { - .get_drvinfo = smc911x_ethtool_getdrvinfo, - .get_msglevel = smc911x_ethtool_getmsglevel, - .set_msglevel = smc911x_ethtool_setmsglevel, - .nway_reset = smc911x_ethtool_nwayreset, - .get_link = ethtool_op_get_link, - .get_regs_len = smc911x_ethtool_getregslen, - .get_regs = smc911x_ethtool_getregs, - .get_eeprom_len = smc911x_ethtool_geteeprom_len, - .get_eeprom = smc911x_ethtool_geteeprom, - .set_eeprom = smc911x_ethtool_seteeprom, - .get_link_ksettings = smc911x_ethtool_get_link_ksettings, - .set_link_ksettings = smc911x_ethtool_set_link_ksettings, -}; - -/* - * smc911x_findirq - * - * This routine has a simple purpose -- make the SMC chip generate an - * interrupt, so an auto-detect routine can detect it, and find the IRQ, - */ -static int smc911x_findirq(struct net_device *dev) -{ - struct smc911x_local *lp = netdev_priv(dev); - int timeout = 20; - unsigned long cookie; - - DBG(SMC_DEBUG_FUNC, dev, "--> %s\n", __func__); - - cookie = probe_irq_on(); - - /* - * Force a SW interrupt - */ - - SMC_SET_INT_EN(lp, INT_EN_SW_INT_EN_); - - /* - * Wait until positive that the interrupt has been generated - */ - do { - int int_status; - udelay(10); - int_status = SMC_GET_INT_EN(lp); - if (int_status & INT_EN_SW_INT_EN_) - break; /* got the interrupt */ - } while (--timeout); - - /* - * there is really nothing that I can do here if timeout fails, - * as autoirq_report will return a 0 anyway, which is what I - * want in this case. Plus, the clean up is needed in both - * cases. - */ - - /* and disable all interrupts again */ - SMC_SET_INT_EN(lp, 0); - - /* and return what I found */ - return probe_irq_off(cookie); -} - -static const struct net_device_ops smc911x_netdev_ops = { - .ndo_open = smc911x_open, - .ndo_stop = smc911x_close, - .ndo_start_xmit = smc911x_hard_start_xmit, - .ndo_tx_timeout = smc911x_timeout, - .ndo_set_rx_mode = smc911x_set_multicast_list, - .ndo_validate_addr = eth_validate_addr, - .ndo_set_mac_address = eth_mac_addr, -#ifdef CONFIG_NET_POLL_CONTROLLER - .ndo_poll_controller = smc911x_poll_controller, -#endif -}; - -/* - * Function: smc911x_probe(unsigned long ioaddr) - * - * Purpose: - * Tests to see if a given ioaddr points to an SMC911x chip. - * Returns a 0 on success - * - * Algorithm: - * (1) see if the endian word is OK - * (1) see if I recognize the chip ID in the appropriate register - * - * Here I do typical initialization tasks. - * - * o Initialize the structure if needed - * o print out my vanity message if not done so already - * o print out what type of hardware is detected - * o print out the ethernet address - * o find the IRQ - * o set up my private data - * o configure the dev structure with my subroutines - * o actually GRAB the irq. - * o GRAB the region - */ -static int smc911x_probe(struct net_device *dev) -{ - struct smc911x_local *lp = netdev_priv(dev); - int i, retval; - unsigned int val, chip_id, revision; - const char *version_string; - unsigned long irq_flags; -#ifdef SMC_USE_DMA - struct dma_slave_config config; - dma_cap_mask_t mask; -#endif - u8 addr[ETH_ALEN]; - - DBG(SMC_DEBUG_FUNC, dev, "--> %s\n", __func__); - - /* First, see if the endian word is recognized */ - val = SMC_GET_BYTE_TEST(lp); - DBG(SMC_DEBUG_MISC, dev, "%s: endian probe returned 0x%04x\n", - CARDNAME, val); - if (val != 0x87654321) { - netdev_err(dev, "Invalid chip endian 0x%08x\n", val); - retval = -ENODEV; - goto err_out; - } - - /* - * check if the revision register is something that I - * recognize. These might need to be added to later, - * as future revisions could be added. - */ - chip_id = SMC_GET_PN(lp); - DBG(SMC_DEBUG_MISC, dev, "%s: id probe returned 0x%04x\n", - CARDNAME, chip_id); - for(i=0;chip_ids[i].id != 0; i++) { - if (chip_ids[i].id == chip_id) break; - } - if (!chip_ids[i].id) { - netdev_err(dev, "Unknown chip ID %04x\n", chip_id); - retval = -ENODEV; - goto err_out; - } - version_string = chip_ids[i].name; - - revision = SMC_GET_REV(lp); - DBG(SMC_DEBUG_MISC, dev, "%s: revision = 0x%04x\n", CARDNAME, revision); - - /* At this point I'll assume that the chip is an SMC911x. */ - DBG(SMC_DEBUG_MISC, dev, "%s: Found a %s\n", - CARDNAME, chip_ids[i].name); - - /* Validate the TX FIFO size requested */ - if ((tx_fifo_kb < 2) || (tx_fifo_kb > 14)) { - netdev_err(dev, "Invalid TX FIFO size requested %d\n", - tx_fifo_kb); - retval = -EINVAL; - goto err_out; - } - - /* fill in some of the fields */ - lp->version = chip_ids[i].id; - lp->revision = revision; - lp->tx_fifo_kb = tx_fifo_kb; - /* Reverse calculate the RX FIFO size from the TX */ - lp->tx_fifo_size=(lp->tx_fifo_kb<<10) - 512; - lp->rx_fifo_size= ((0x4000 - 512 - lp->tx_fifo_size) / 16) * 15; - - /* Set the automatic flow control values */ - switch(lp->tx_fifo_kb) { - /* - * AFC_HI is about ((Rx Data Fifo Size)*2/3)/64 - * AFC_LO is AFC_HI/2 - * BACK_DUR is about 5uS*(AFC_LO) rounded down - */ - case 2:/* 13440 Rx Data Fifo Size */ - lp->afc_cfg=0x008C46AF;break; - case 3:/* 12480 Rx Data Fifo Size */ - lp->afc_cfg=0x0082419F;break; - case 4:/* 11520 Rx Data Fifo Size */ - lp->afc_cfg=0x00783C9F;break; - case 5:/* 10560 Rx Data Fifo Size */ - lp->afc_cfg=0x006E374F;break; - case 6:/* 9600 Rx Data Fifo Size */ - lp->afc_cfg=0x0064328F;break; - case 7:/* 8640 Rx Data Fifo Size */ - lp->afc_cfg=0x005A2D7F;break; - case 8:/* 7680 Rx Data Fifo Size */ - lp->afc_cfg=0x0050287F;break; - case 9:/* 6720 Rx Data Fifo Size */ - lp->afc_cfg=0x0046236F;break; - case 10:/* 5760 Rx Data Fifo Size */ - lp->afc_cfg=0x003C1E6F;break; - case 11:/* 4800 Rx Data Fifo Size */ - lp->afc_cfg=0x0032195F;break; - /* - * AFC_HI is ~1520 bytes less than RX Data Fifo Size - * AFC_LO is AFC_HI/2 - * BACK_DUR is about 5uS*(AFC_LO) rounded down - */ - case 12:/* 3840 Rx Data Fifo Size */ - lp->afc_cfg=0x0024124F;break; - case 13:/* 2880 Rx Data Fifo Size */ - lp->afc_cfg=0x0015073F;break; - case 14:/* 1920 Rx Data Fifo Size */ - lp->afc_cfg=0x0006032F;break; - default: - PRINTK(dev, "ERROR -- no AFC_CFG setting found"); - break; - } - - DBG(SMC_DEBUG_MISC | SMC_DEBUG_TX | SMC_DEBUG_RX, dev, - "%s: tx_fifo %d rx_fifo %d afc_cfg 0x%08x\n", CARDNAME, - lp->tx_fifo_size, lp->rx_fifo_size, lp->afc_cfg); - - spin_lock_init(&lp->lock); - - /* Get the MAC address */ - SMC_GET_MAC_ADDR(lp, addr); - eth_hw_addr_set(dev, addr); - - /* now, reset the chip, and put it into a known state */ - smc911x_reset(dev); - - /* - * If dev->irq is 0, then the device has to be banged on to see - * what the IRQ is. - * - * Specifying an IRQ is done with the assumption that the user knows - * what (s)he is doing. No checking is done!!!! - */ - if (dev->irq < 1) { - int trials; - - trials = 3; - while (trials--) { - dev->irq = smc911x_findirq(dev); - if (dev->irq) - break; - /* kick the card and try again */ - smc911x_reset(dev); - } - } - if (dev->irq == 0) { - netdev_warn(dev, "Couldn't autodetect your IRQ. Use irq=xx.\n"); - retval = -ENODEV; - goto err_out; - } - dev->irq = irq_canonicalize(dev->irq); - - dev->netdev_ops = &smc911x_netdev_ops; - dev->watchdog_timeo = msecs_to_jiffies(watchdog); - dev->ethtool_ops = &smc911x_ethtool_ops; - - INIT_WORK(&lp->phy_configure, smc911x_phy_configure); - lp->mii.phy_id_mask = 0x1f; - lp->mii.reg_num_mask = 0x1f; - lp->mii.force_media = 0; - lp->mii.full_duplex = 0; - lp->mii.dev = dev; - lp->mii.mdio_read = smc911x_phy_read; - lp->mii.mdio_write = smc911x_phy_write; - - /* - * Locate the phy, if any. - */ - smc911x_phy_detect(dev); - - /* Set default parameters */ - lp->msg_enable = NETIF_MSG_LINK; - lp->ctl_rfduplx = 1; - lp->ctl_rspeed = 100; - -#ifdef SMC_DYNAMIC_BUS_CONFIG - irq_flags = lp->cfg.irq_flags; -#else - irq_flags = IRQF_SHARED | SMC_IRQ_SENSE; -#endif - - /* Grab the IRQ */ - retval = request_irq(dev->irq, smc911x_interrupt, - irq_flags, dev->name, dev); - if (retval) - goto err_out; - -#ifdef SMC_USE_DMA - - dma_cap_zero(mask); - dma_cap_set(DMA_SLAVE, mask); - lp->rxdma = dma_request_channel(mask, NULL, NULL); - lp->txdma = dma_request_channel(mask, NULL, NULL); - lp->rxdma_active = 0; - lp->txdma_active = 0; - - memset(&config, 0, sizeof(config)); - config.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; - config.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; - config.src_addr = lp->physaddr + RX_DATA_FIFO; - config.dst_addr = lp->physaddr + TX_DATA_FIFO; - config.src_maxburst = 32; - config.dst_maxburst = 32; - retval = dmaengine_slave_config(lp->rxdma, &config); - if (retval) { - dev_err(lp->dev, "dma rx channel configuration failed: %d\n", - retval); - goto err_out; - } - retval = dmaengine_slave_config(lp->txdma, &config); - if (retval) { - dev_err(lp->dev, "dma tx channel configuration failed: %d\n", - retval); - goto err_out; - } -#endif - - retval = register_netdev(dev); - if (retval == 0) { - /* now, print out the card info, in a short format.. */ - netdev_info(dev, "%s (rev %d) at %#lx IRQ %d", - version_string, lp->revision, - dev->base_addr, dev->irq); - -#ifdef SMC_USE_DMA - if (lp->rxdma) - pr_cont(" RXDMA %p", lp->rxdma); - - if (lp->txdma) - pr_cont(" TXDMA %p", lp->txdma); -#endif - pr_cont("\n"); - if (!is_valid_ether_addr(dev->dev_addr)) { - netdev_warn(dev, "Invalid ethernet MAC address. Please set using ifconfig\n"); - } else { - /* Print the Ethernet address */ - netdev_info(dev, "Ethernet addr: %pM\n", - dev->dev_addr); - } - - if (lp->phy_type == 0) { - PRINTK(dev, "No PHY found\n"); - } else if ((lp->phy_type & ~0xff) == LAN911X_INTERNAL_PHY_ID) { - PRINTK(dev, "LAN911x Internal PHY\n"); - } else { - PRINTK(dev, "External PHY 0x%08x\n", lp->phy_type); - } - } - -err_out: -#ifdef SMC_USE_DMA - if (retval) { - if (lp->rxdma) - dma_release_channel(lp->rxdma); - if (lp->txdma) - dma_release_channel(lp->txdma); - } -#endif - return retval; -} - -/* - * smc911x_drv_probe(void) - * - * Output: - * 0 --> there is a device - * anything else, error - */ -static int smc911x_drv_probe(struct platform_device *pdev) -{ - struct net_device *ndev; - struct resource *res; - struct smc911x_local *lp; - void __iomem *addr; - int ret; - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - ret = -ENODEV; - goto out; - } - - /* - * Request the regions. - */ - if (!request_mem_region(res->start, SMC911X_IO_EXTENT, CARDNAME)) { - ret = -EBUSY; - goto out; - } - - ndev = alloc_etherdev(sizeof(struct smc911x_local)); - if (!ndev) { - ret = -ENOMEM; - goto release_1; - } - SET_NETDEV_DEV(ndev, &pdev->dev); - - ndev->dma = (unsigned char)-1; - ndev->irq = platform_get_irq(pdev, 0); - if (ndev->irq < 0) { - ret = ndev->irq; - goto release_both; - } - - lp = netdev_priv(ndev); - lp->netdev = ndev; -#ifdef SMC_DYNAMIC_BUS_CONFIG - { - struct smc911x_platdata *pd = dev_get_platdata(&pdev->dev); - if (!pd) { - ret = -EINVAL; - goto release_both; - } - memcpy(&lp->cfg, pd, sizeof(lp->cfg)); - } -#endif - - addr = ioremap(res->start, SMC911X_IO_EXTENT); - if (!addr) { - ret = -ENOMEM; - goto release_both; - } - - platform_set_drvdata(pdev, ndev); - lp->base = addr; - ndev->base_addr = res->start; - ret = smc911x_probe(ndev); - if (ret != 0) { - iounmap(addr); -release_both: - free_netdev(ndev); -release_1: - release_mem_region(res->start, SMC911X_IO_EXTENT); -out: - pr_info("%s: not found (%d).\n", CARDNAME, ret); - } -#ifdef SMC_USE_DMA - else { - lp->physaddr = res->start; - lp->dev = &pdev->dev; - } -#endif - - return ret; -} - -static int smc911x_drv_remove(struct platform_device *pdev) -{ - struct net_device *ndev = platform_get_drvdata(pdev); - struct smc911x_local *lp = netdev_priv(ndev); - struct resource *res; - - DBG(SMC_DEBUG_FUNC, ndev, "--> %s\n", __func__); - - unregister_netdev(ndev); - - free_irq(ndev->irq, ndev); - -#ifdef SMC_USE_DMA - { - if (lp->rxdma) - dma_release_channel(lp->rxdma); - if (lp->txdma) - dma_release_channel(lp->txdma); - } -#endif - iounmap(lp->base); - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - release_mem_region(res->start, SMC911X_IO_EXTENT); - - free_netdev(ndev); - return 0; -} - -static int smc911x_drv_suspend(struct platform_device *dev, pm_message_t state) -{ - struct net_device *ndev = platform_get_drvdata(dev); - struct smc911x_local *lp = netdev_priv(ndev); - - DBG(SMC_DEBUG_FUNC, ndev, "--> %s\n", __func__); - if (ndev) { - if (netif_running(ndev)) { - netif_device_detach(ndev); - smc911x_shutdown(ndev); -#if POWER_DOWN - /* Set D2 - Energy detect only setting */ - SMC_SET_PMT_CTRL(lp, 2<<12); -#endif - } - } - return 0; -} - -static int smc911x_drv_resume(struct platform_device *dev) -{ - struct net_device *ndev = platform_get_drvdata(dev); - - DBG(SMC_DEBUG_FUNC, ndev, "--> %s\n", __func__); - if (ndev) { - struct smc911x_local *lp = netdev_priv(ndev); - - if (netif_running(ndev)) { - smc911x_reset(ndev); - if (lp->phy_type != 0) - smc911x_phy_configure(&lp->phy_configure); - smc911x_enable(ndev); - netif_device_attach(ndev); - } - } - return 0; -} - -static struct platform_driver smc911x_driver = { - .probe = smc911x_drv_probe, - .remove = smc911x_drv_remove, - .suspend = smc911x_drv_suspend, - .resume = smc911x_drv_resume, - .driver = { - .name = CARDNAME, - }, -}; - -module_platform_driver(smc911x_driver); diff --git a/drivers/net/ethernet/smsc/smc911x.h b/drivers/net/ethernet/smsc/smc911x.h deleted file mode 100644 index d4edcc0da87c..000000000000 --- a/drivers/net/ethernet/smsc/smc911x.h +++ /dev/null @@ -1,901 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/*------------------------------------------------------------------------ - . smc911x.h - macros for SMSC's LAN911{5,6,7,8} single-chip Ethernet device. - . - . Copyright (C) 2005 Sensoria Corp. - . Derived from the unified SMC91x driver by Nicolas Pitre - . - . - . Information contained in this file was obtained from the LAN9118 - . manual from SMC. To get a copy, if you really want one, you can find - . information under www.smsc.com. - . - . Authors - . Dustin McIntire - . - ---------------------------------------------------------------------------*/ -#ifndef _SMC911X_H_ -#define _SMC911X_H_ - -#include -/* - * Use the DMA feature on PXA chips - */ -#ifdef CONFIG_ARCH_PXA - #define SMC_USE_PXA_DMA 1 - #define SMC_USE_16BIT 0 - #define SMC_USE_32BIT 1 - #define SMC_IRQ_SENSE IRQF_TRIGGER_FALLING -#elif defined(CONFIG_SH_MAGIC_PANEL_R2) - #define SMC_USE_16BIT 0 - #define SMC_USE_32BIT 1 - #define SMC_IRQ_SENSE IRQF_TRIGGER_LOW -#elif defined(CONFIG_ARCH_OMAP3) - #define SMC_USE_16BIT 0 - #define SMC_USE_32BIT 1 - #define SMC_IRQ_SENSE IRQF_TRIGGER_LOW - #define SMC_MEM_RESERVED 1 -#elif defined(CONFIG_ARCH_OMAP2) - #define SMC_USE_16BIT 0 - #define SMC_USE_32BIT 1 - #define SMC_IRQ_SENSE IRQF_TRIGGER_LOW - #define SMC_MEM_RESERVED 1 -#else -/* - * Default configuration - */ - -#define SMC_DYNAMIC_BUS_CONFIG -#endif - -#ifdef SMC_USE_PXA_DMA -#define SMC_USE_DMA -#endif - -/* store this information for the driver.. */ -struct smc911x_local { - /* - * If I have to wait until the DMA is finished and ready to reload a - * packet, I will store the skbuff here. Then, the DMA will send it - * out and free it. - */ - struct sk_buff *pending_tx_skb; - - /* version/revision of the SMC911x chip */ - u16 version; - u16 revision; - - /* FIFO sizes */ - int tx_fifo_kb; - int tx_fifo_size; - int rx_fifo_size; - int afc_cfg; - - /* Contains the current active receive/phy mode */ - int ctl_rfduplx; - int ctl_rspeed; - - u32 msg_enable; - u32 phy_type; - struct mii_if_info mii; - - /* work queue */ - struct work_struct phy_configure; - - int tx_throttle; - spinlock_t lock; - - struct net_device *netdev; - -#ifdef SMC_USE_DMA - /* DMA needs the physical address of the chip */ - u_long physaddr; - struct dma_chan *rxdma; - struct dma_chan *txdma; - int rxdma_active; - int txdma_active; - struct sk_buff *current_rx_skb; - struct sk_buff *current_tx_skb; - struct device *dev; -#endif - void __iomem *base; -#ifdef SMC_DYNAMIC_BUS_CONFIG - struct smc911x_platdata cfg; -#endif -}; - -/* - * Define the bus width specific IO macros - */ - -#ifdef SMC_DYNAMIC_BUS_CONFIG -static inline unsigned int SMC_inl(struct smc911x_local *lp, int reg) -{ - void __iomem *ioaddr = lp->base + reg; - - if (lp->cfg.flags & SMC911X_USE_32BIT) - return readl(ioaddr); - - if (lp->cfg.flags & SMC911X_USE_16BIT) - return readw(ioaddr) | (readw(ioaddr + 2) << 16); - - BUG(); -} - -static inline void SMC_outl(unsigned int value, struct smc911x_local *lp, - int reg) -{ - void __iomem *ioaddr = lp->base + reg; - - if (lp->cfg.flags & SMC911X_USE_32BIT) { - writel(value, ioaddr); - return; - } - - if (lp->cfg.flags & SMC911X_USE_16BIT) { - writew(value & 0xffff, ioaddr); - writew(value >> 16, ioaddr + 2); - return; - } - - BUG(); -} - -static inline void SMC_insl(struct smc911x_local *lp, int reg, - void *addr, unsigned int count) -{ - void __iomem *ioaddr = lp->base + reg; - - if (lp->cfg.flags & SMC911X_USE_32BIT) { - ioread32_rep(ioaddr, addr, count); - return; - } - - if (lp->cfg.flags & SMC911X_USE_16BIT) { - ioread16_rep(ioaddr, addr, count * 2); - return; - } - - BUG(); -} - -static inline void SMC_outsl(struct smc911x_local *lp, int reg, - void *addr, unsigned int count) -{ - void __iomem *ioaddr = lp->base + reg; - - if (lp->cfg.flags & SMC911X_USE_32BIT) { - iowrite32_rep(ioaddr, addr, count); - return; - } - - if (lp->cfg.flags & SMC911X_USE_16BIT) { - iowrite16_rep(ioaddr, addr, count * 2); - return; - } - - BUG(); -} -#else -#if SMC_USE_16BIT -#define SMC_inl(lp, r) ((readw((lp)->base + (r)) & 0xFFFF) + (readw((lp)->base + (r) + 2) << 16)) -#define SMC_outl(v, lp, r) \ - do{ \ - writew(v & 0xFFFF, (lp)->base + (r)); \ - writew(v >> 16, (lp)->base + (r) + 2); \ - } while (0) -#define SMC_insl(lp, r, p, l) ioread16_rep((short*)((lp)->base + (r)), p, l*2) -#define SMC_outsl(lp, r, p, l) iowrite16_rep((short*)((lp)->base + (r)), p, l*2) - -#elif SMC_USE_32BIT -#define SMC_inl(lp, r) readl((lp)->base + (r)) -#define SMC_outl(v, lp, r) writel(v, (lp)->base + (r)) -#define SMC_insl(lp, r, p, l) ioread32_rep((int*)((lp)->base + (r)), p, l) -#define SMC_outsl(lp, r, p, l) iowrite32_rep((int*)((lp)->base + (r)), p, l) - -#endif /* SMC_USE_16BIT */ -#endif /* SMC_DYNAMIC_BUS_CONFIG */ - - -#ifdef SMC_USE_PXA_DMA - -/* - * Use a DMA for RX and TX packets. - */ -#include - -static dma_addr_t rx_dmabuf, tx_dmabuf; -static int rx_dmalen, tx_dmalen; -static void smc911x_rx_dma_irq(void *data); -static void smc911x_tx_dma_irq(void *data); - -#ifdef SMC_insl -#undef SMC_insl -#define SMC_insl(lp, r, p, l) \ - smc_pxa_dma_insl(lp, lp->physaddr, r, lp->rxdma, p, l) - -static inline void -smc_pxa_dma_insl(struct smc911x_local *lp, u_long physaddr, - int reg, struct dma_chan *dma, u_char *buf, int len) -{ - struct dma_async_tx_descriptor *tx; - - /* 64 bit alignment is required for memory to memory DMA */ - if ((long)buf & 4) { - *((u32 *)buf) = SMC_inl(lp, reg); - buf += 4; - len--; - } - - len *= 4; - rx_dmabuf = dma_map_single(lp->dev, buf, len, DMA_FROM_DEVICE); - rx_dmalen = len; - tx = dmaengine_prep_slave_single(dma, rx_dmabuf, rx_dmalen, - DMA_DEV_TO_MEM, 0); - if (tx) { - tx->callback = smc911x_rx_dma_irq; - tx->callback_param = lp; - dmaengine_submit(tx); - dma_async_issue_pending(dma); - } -} -#endif - -#ifdef SMC_outsl -#undef SMC_outsl -#define SMC_outsl(lp, r, p, l) \ - smc_pxa_dma_outsl(lp, lp->physaddr, r, lp->txdma, p, l) - -static inline void -smc_pxa_dma_outsl(struct smc911x_local *lp, u_long physaddr, - int reg, struct dma_chan *dma, u_char *buf, int len) -{ - struct dma_async_tx_descriptor *tx; - - /* 64 bit alignment is required for memory to memory DMA */ - if ((long)buf & 4) { - SMC_outl(*((u32 *)buf), lp, reg); - buf += 4; - len--; - } - - len *= 4; - tx_dmabuf = dma_map_single(lp->dev, buf, len, DMA_TO_DEVICE); - tx_dmalen = len; - tx = dmaengine_prep_slave_single(dma, tx_dmabuf, tx_dmalen, - DMA_DEV_TO_MEM, 0); - if (tx) { - tx->callback = smc911x_tx_dma_irq; - tx->callback_param = lp; - dmaengine_submit(tx); - dma_async_issue_pending(dma); - } -} -#endif -#endif /* SMC_USE_PXA_DMA */ - - -/* Chip Parameters and Register Definitions */ - -#define SMC911X_TX_FIFO_LOW_THRESHOLD (1536*2) - -#define SMC911X_IO_EXTENT 0x100 - -#define SMC911X_EEPROM_LEN 7 - -/* Below are the register offsets and bit definitions - * of the Lan911x memory space - */ -#define RX_DATA_FIFO (0x00) - -#define TX_DATA_FIFO (0x20) -#define TX_CMD_A_INT_ON_COMP_ (0x80000000) -#define TX_CMD_A_INT_BUF_END_ALGN_ (0x03000000) -#define TX_CMD_A_INT_4_BYTE_ALGN_ (0x00000000) -#define TX_CMD_A_INT_16_BYTE_ALGN_ (0x01000000) -#define TX_CMD_A_INT_32_BYTE_ALGN_ (0x02000000) -#define TX_CMD_A_INT_DATA_OFFSET_ (0x001F0000) -#define TX_CMD_A_INT_FIRST_SEG_ (0x00002000) -#define TX_CMD_A_INT_LAST_SEG_ (0x00001000) -#define TX_CMD_A_BUF_SIZE_ (0x000007FF) -#define TX_CMD_B_PKT_TAG_ (0xFFFF0000) -#define TX_CMD_B_ADD_CRC_DISABLE_ (0x00002000) -#define TX_CMD_B_DISABLE_PADDING_ (0x00001000) -#define TX_CMD_B_PKT_BYTE_LENGTH_ (0x000007FF) - -#define RX_STATUS_FIFO (0x40) -#define RX_STS_PKT_LEN_ (0x3FFF0000) -#define RX_STS_ES_ (0x00008000) -#define RX_STS_BCST_ (0x00002000) -#define RX_STS_LEN_ERR_ (0x00001000) -#define RX_STS_RUNT_ERR_ (0x00000800) -#define RX_STS_MCAST_ (0x00000400) -#define RX_STS_TOO_LONG_ (0x00000080) -#define RX_STS_COLL_ (0x00000040) -#define RX_STS_ETH_TYPE_ (0x00000020) -#define RX_STS_WDOG_TMT_ (0x00000010) -#define RX_STS_MII_ERR_ (0x00000008) -#define RX_STS_DRIBBLING_ (0x00000004) -#define RX_STS_CRC_ERR_ (0x00000002) -#define RX_STATUS_FIFO_PEEK (0x44) -#define TX_STATUS_FIFO (0x48) -#define TX_STS_TAG_ (0xFFFF0000) -#define TX_STS_ES_ (0x00008000) -#define TX_STS_LOC_ (0x00000800) -#define TX_STS_NO_CARR_ (0x00000400) -#define TX_STS_LATE_COLL_ (0x00000200) -#define TX_STS_MANY_COLL_ (0x00000100) -#define TX_STS_COLL_CNT_ (0x00000078) -#define TX_STS_MANY_DEFER_ (0x00000004) -#define TX_STS_UNDERRUN_ (0x00000002) -#define TX_STS_DEFERRED_ (0x00000001) -#define TX_STATUS_FIFO_PEEK (0x4C) -#define ID_REV (0x50) -#define ID_REV_CHIP_ID_ (0xFFFF0000) /* RO */ -#define ID_REV_REV_ID_ (0x0000FFFF) /* RO */ - -#define INT_CFG (0x54) -#define INT_CFG_INT_DEAS_ (0xFF000000) /* R/W */ -#define INT_CFG_INT_DEAS_CLR_ (0x00004000) -#define INT_CFG_INT_DEAS_STS_ (0x00002000) -#define INT_CFG_IRQ_INT_ (0x00001000) /* RO */ -#define INT_CFG_IRQ_EN_ (0x00000100) /* R/W */ -#define INT_CFG_IRQ_POL_ (0x00000010) /* R/W Not Affected by SW Reset */ -#define INT_CFG_IRQ_TYPE_ (0x00000001) /* R/W Not Affected by SW Reset */ - -#define INT_STS (0x58) -#define INT_STS_SW_INT_ (0x80000000) /* R/WC */ -#define INT_STS_TXSTOP_INT_ (0x02000000) /* R/WC */ -#define INT_STS_RXSTOP_INT_ (0x01000000) /* R/WC */ -#define INT_STS_RXDFH_INT_ (0x00800000) /* R/WC */ -#define INT_STS_RXDF_INT_ (0x00400000) /* R/WC */ -#define INT_STS_TX_IOC_ (0x00200000) /* R/WC */ -#define INT_STS_RXD_INT_ (0x00100000) /* R/WC */ -#define INT_STS_GPT_INT_ (0x00080000) /* R/WC */ -#define INT_STS_PHY_INT_ (0x00040000) /* RO */ -#define INT_STS_PME_INT_ (0x00020000) /* R/WC */ -#define INT_STS_TXSO_ (0x00010000) /* R/WC */ -#define INT_STS_RWT_ (0x00008000) /* R/WC */ -#define INT_STS_RXE_ (0x00004000) /* R/WC */ -#define INT_STS_TXE_ (0x00002000) /* R/WC */ -//#define INT_STS_ERX_ (0x00001000) /* R/WC */ -#define INT_STS_TDFU_ (0x00000800) /* R/WC */ -#define INT_STS_TDFO_ (0x00000400) /* R/WC */ -#define INT_STS_TDFA_ (0x00000200) /* R/WC */ -#define INT_STS_TSFF_ (0x00000100) /* R/WC */ -#define INT_STS_TSFL_ (0x00000080) /* R/WC */ -//#define INT_STS_RXDF_ (0x00000040) /* R/WC */ -#define INT_STS_RDFO_ (0x00000040) /* R/WC */ -#define INT_STS_RDFL_ (0x00000020) /* R/WC */ -#define INT_STS_RSFF_ (0x00000010) /* R/WC */ -#define INT_STS_RSFL_ (0x00000008) /* R/WC */ -#define INT_STS_GPIO2_INT_ (0x00000004) /* R/WC */ -#define INT_STS_GPIO1_INT_ (0x00000002) /* R/WC */ -#define INT_STS_GPIO0_INT_ (0x00000001) /* R/WC */ - -#define INT_EN (0x5C) -#define INT_EN_SW_INT_EN_ (0x80000000) /* R/W */ -#define INT_EN_TXSTOP_INT_EN_ (0x02000000) /* R/W */ -#define INT_EN_RXSTOP_INT_EN_ (0x01000000) /* R/W */ -#define INT_EN_RXDFH_INT_EN_ (0x00800000) /* R/W */ -//#define INT_EN_RXDF_INT_EN_ (0x00400000) /* R/W */ -#define INT_EN_TIOC_INT_EN_ (0x00200000) /* R/W */ -#define INT_EN_RXD_INT_EN_ (0x00100000) /* R/W */ -#define INT_EN_GPT_INT_EN_ (0x00080000) /* R/W */ -#define INT_EN_PHY_INT_EN_ (0x00040000) /* R/W */ -#define INT_EN_PME_INT_EN_ (0x00020000) /* R/W */ -#define INT_EN_TXSO_EN_ (0x00010000) /* R/W */ -#define INT_EN_RWT_EN_ (0x00008000) /* R/W */ -#define INT_EN_RXE_EN_ (0x00004000) /* R/W */ -#define INT_EN_TXE_EN_ (0x00002000) /* R/W */ -//#define INT_EN_ERX_EN_ (0x00001000) /* R/W */ -#define INT_EN_TDFU_EN_ (0x00000800) /* R/W */ -#define INT_EN_TDFO_EN_ (0x00000400) /* R/W */ -#define INT_EN_TDFA_EN_ (0x00000200) /* R/W */ -#define INT_EN_TSFF_EN_ (0x00000100) /* R/W */ -#define INT_EN_TSFL_EN_ (0x00000080) /* R/W */ -//#define INT_EN_RXDF_EN_ (0x00000040) /* R/W */ -#define INT_EN_RDFO_EN_ (0x00000040) /* R/W */ -#define INT_EN_RDFL_EN_ (0x00000020) /* R/W */ -#define INT_EN_RSFF_EN_ (0x00000010) /* R/W */ -#define INT_EN_RSFL_EN_ (0x00000008) /* R/W */ -#define INT_EN_GPIO2_INT_ (0x00000004) /* R/W */ -#define INT_EN_GPIO1_INT_ (0x00000002) /* R/W */ -#define INT_EN_GPIO0_INT_ (0x00000001) /* R/W */ - -#define BYTE_TEST (0x64) -#define FIFO_INT (0x68) -#define FIFO_INT_TX_AVAIL_LEVEL_ (0xFF000000) /* R/W */ -#define FIFO_INT_TX_STS_LEVEL_ (0x00FF0000) /* R/W */ -#define FIFO_INT_RX_AVAIL_LEVEL_ (0x0000FF00) /* R/W */ -#define FIFO_INT_RX_STS_LEVEL_ (0x000000FF) /* R/W */ - -#define RX_CFG (0x6C) -#define RX_CFG_RX_END_ALGN_ (0xC0000000) /* R/W */ -#define RX_CFG_RX_END_ALGN4_ (0x00000000) /* R/W */ -#define RX_CFG_RX_END_ALGN16_ (0x40000000) /* R/W */ -#define RX_CFG_RX_END_ALGN32_ (0x80000000) /* R/W */ -#define RX_CFG_RX_DMA_CNT_ (0x0FFF0000) /* R/W */ -#define RX_CFG_RX_DUMP_ (0x00008000) /* R/W */ -#define RX_CFG_RXDOFF_ (0x00001F00) /* R/W */ -//#define RX_CFG_RXBAD_ (0x00000001) /* R/W */ - -#define TX_CFG (0x70) -//#define TX_CFG_TX_DMA_LVL_ (0xE0000000) /* R/W */ -//#define TX_CFG_TX_DMA_CNT_ (0x0FFF0000) /* R/W Self Clearing */ -#define TX_CFG_TXS_DUMP_ (0x00008000) /* Self Clearing */ -#define TX_CFG_TXD_DUMP_ (0x00004000) /* Self Clearing */ -#define TX_CFG_TXSAO_ (0x00000004) /* R/W */ -#define TX_CFG_TX_ON_ (0x00000002) /* R/W */ -#define TX_CFG_STOP_TX_ (0x00000001) /* Self Clearing */ - -#define HW_CFG (0x74) -#define HW_CFG_TTM_ (0x00200000) /* R/W */ -#define HW_CFG_SF_ (0x00100000) /* R/W */ -#define HW_CFG_TX_FIF_SZ_ (0x000F0000) /* R/W */ -#define HW_CFG_TR_ (0x00003000) /* R/W */ -#define HW_CFG_PHY_CLK_SEL_ (0x00000060) /* R/W */ -#define HW_CFG_PHY_CLK_SEL_INT_PHY_ (0x00000000) /* R/W */ -#define HW_CFG_PHY_CLK_SEL_EXT_PHY_ (0x00000020) /* R/W */ -#define HW_CFG_PHY_CLK_SEL_CLK_DIS_ (0x00000040) /* R/W */ -#define HW_CFG_SMI_SEL_ (0x00000010) /* R/W */ -#define HW_CFG_EXT_PHY_DET_ (0x00000008) /* RO */ -#define HW_CFG_EXT_PHY_EN_ (0x00000004) /* R/W */ -#define HW_CFG_32_16_BIT_MODE_ (0x00000004) /* RO */ -#define HW_CFG_SRST_TO_ (0x00000002) /* RO */ -#define HW_CFG_SRST_ (0x00000001) /* Self Clearing */ - -#define RX_DP_CTRL (0x78) -#define RX_DP_CTRL_RX_FFWD_ (0x80000000) /* R/W */ -#define RX_DP_CTRL_FFWD_BUSY_ (0x80000000) /* RO */ - -#define RX_FIFO_INF (0x7C) -#define RX_FIFO_INF_RXSUSED_ (0x00FF0000) /* RO */ -#define RX_FIFO_INF_RXDUSED_ (0x0000FFFF) /* RO */ - -#define TX_FIFO_INF (0x80) -#define TX_FIFO_INF_TSUSED_ (0x00FF0000) /* RO */ -#define TX_FIFO_INF_TDFREE_ (0x0000FFFF) /* RO */ - -#define PMT_CTRL (0x84) -#define PMT_CTRL_PM_MODE_ (0x00003000) /* Self Clearing */ -#define PMT_CTRL_PHY_RST_ (0x00000400) /* Self Clearing */ -#define PMT_CTRL_WOL_EN_ (0x00000200) /* R/W */ -#define PMT_CTRL_ED_EN_ (0x00000100) /* R/W */ -#define PMT_CTRL_PME_TYPE_ (0x00000040) /* R/W Not Affected by SW Reset */ -#define PMT_CTRL_WUPS_ (0x00000030) /* R/WC */ -#define PMT_CTRL_WUPS_NOWAKE_ (0x00000000) /* R/WC */ -#define PMT_CTRL_WUPS_ED_ (0x00000010) /* R/WC */ -#define PMT_CTRL_WUPS_WOL_ (0x00000020) /* R/WC */ -#define PMT_CTRL_WUPS_MULTI_ (0x00000030) /* R/WC */ -#define PMT_CTRL_PME_IND_ (0x00000008) /* R/W */ -#define PMT_CTRL_PME_POL_ (0x00000004) /* R/W */ -#define PMT_CTRL_PME_EN_ (0x00000002) /* R/W Not Affected by SW Reset */ -#define PMT_CTRL_READY_ (0x00000001) /* RO */ - -#define GPIO_CFG (0x88) -#define GPIO_CFG_LED3_EN_ (0x40000000) /* R/W */ -#define GPIO_CFG_LED2_EN_ (0x20000000) /* R/W */ -#define GPIO_CFG_LED1_EN_ (0x10000000) /* R/W */ -#define GPIO_CFG_GPIO2_INT_POL_ (0x04000000) /* R/W */ -#define GPIO_CFG_GPIO1_INT_POL_ (0x02000000) /* R/W */ -#define GPIO_CFG_GPIO0_INT_POL_ (0x01000000) /* R/W */ -#define GPIO_CFG_EEPR_EN_ (0x00700000) /* R/W */ -#define GPIO_CFG_GPIOBUF2_ (0x00040000) /* R/W */ -#define GPIO_CFG_GPIOBUF1_ (0x00020000) /* R/W */ -#define GPIO_CFG_GPIOBUF0_ (0x00010000) /* R/W */ -#define GPIO_CFG_GPIODIR2_ (0x00000400) /* R/W */ -#define GPIO_CFG_GPIODIR1_ (0x00000200) /* R/W */ -#define GPIO_CFG_GPIODIR0_ (0x00000100) /* R/W */ -#define GPIO_CFG_GPIOD4_ (0x00000010) /* R/W */ -#define GPIO_CFG_GPIOD3_ (0x00000008) /* R/W */ -#define GPIO_CFG_GPIOD2_ (0x00000004) /* R/W */ -#define GPIO_CFG_GPIOD1_ (0x00000002) /* R/W */ -#define GPIO_CFG_GPIOD0_ (0x00000001) /* R/W */ - -#define GPT_CFG (0x8C) -#define GPT_CFG_TIMER_EN_ (0x20000000) /* R/W */ -#define GPT_CFG_GPT_LOAD_ (0x0000FFFF) /* R/W */ - -#define GPT_CNT (0x90) -#define GPT_CNT_GPT_CNT_ (0x0000FFFF) /* RO */ - -#define ENDIAN (0x98) -#define FREE_RUN (0x9C) -#define RX_DROP (0xA0) -#define MAC_CSR_CMD (0xA4) -#define MAC_CSR_CMD_CSR_BUSY_ (0x80000000) /* Self Clearing */ -#define MAC_CSR_CMD_R_NOT_W_ (0x40000000) /* R/W */ -#define MAC_CSR_CMD_CSR_ADDR_ (0x000000FF) /* R/W */ - -#define MAC_CSR_DATA (0xA8) -#define AFC_CFG (0xAC) -#define AFC_CFG_AFC_HI_ (0x00FF0000) /* R/W */ -#define AFC_CFG_AFC_LO_ (0x0000FF00) /* R/W */ -#define AFC_CFG_BACK_DUR_ (0x000000F0) /* R/W */ -#define AFC_CFG_FCMULT_ (0x00000008) /* R/W */ -#define AFC_CFG_FCBRD_ (0x00000004) /* R/W */ -#define AFC_CFG_FCADD_ (0x00000002) /* R/W */ -#define AFC_CFG_FCANY_ (0x00000001) /* R/W */ - -#define E2P_CMD (0xB0) -#define E2P_CMD_EPC_BUSY_ (0x80000000) /* Self Clearing */ -#define E2P_CMD_EPC_CMD_ (0x70000000) /* R/W */ -#define E2P_CMD_EPC_CMD_READ_ (0x00000000) /* R/W */ -#define E2P_CMD_EPC_CMD_EWDS_ (0x10000000) /* R/W */ -#define E2P_CMD_EPC_CMD_EWEN_ (0x20000000) /* R/W */ -#define E2P_CMD_EPC_CMD_WRITE_ (0x30000000) /* R/W */ -#define E2P_CMD_EPC_CMD_WRAL_ (0x40000000) /* R/W */ -#define E2P_CMD_EPC_CMD_ERASE_ (0x50000000) /* R/W */ -#define E2P_CMD_EPC_CMD_ERAL_ (0x60000000) /* R/W */ -#define E2P_CMD_EPC_CMD_RELOAD_ (0x70000000) /* R/W */ -#define E2P_CMD_EPC_TIMEOUT_ (0x00000200) /* RO */ -#define E2P_CMD_MAC_ADDR_LOADED_ (0x00000100) /* RO */ -#define E2P_CMD_EPC_ADDR_ (0x000000FF) /* R/W */ - -#define E2P_DATA (0xB4) -#define E2P_DATA_EEPROM_DATA_ (0x000000FF) /* R/W */ -/* end of LAN register offsets and bit definitions */ - -/* - **************************************************************************** - **************************************************************************** - * MAC Control and Status Register (Indirect Address) - * Offset (through the MAC_CSR CMD and DATA port) - **************************************************************************** - **************************************************************************** - * - */ -#define MAC_CR (0x01) /* R/W */ - -/* MAC_CR - MAC Control Register */ -#define MAC_CR_RXALL_ (0x80000000) -// TODO: delete this bit? It is not described in the data sheet. -#define MAC_CR_HBDIS_ (0x10000000) -#define MAC_CR_RCVOWN_ (0x00800000) -#define MAC_CR_LOOPBK_ (0x00200000) -#define MAC_CR_FDPX_ (0x00100000) -#define MAC_CR_MCPAS_ (0x00080000) -#define MAC_CR_PRMS_ (0x00040000) -#define MAC_CR_INVFILT_ (0x00020000) -#define MAC_CR_PASSBAD_ (0x00010000) -#define MAC_CR_HFILT_ (0x00008000) -#define MAC_CR_HPFILT_ (0x00002000) -#define MAC_CR_LCOLL_ (0x00001000) -#define MAC_CR_BCAST_ (0x00000800) -#define MAC_CR_DISRTY_ (0x00000400) -#define MAC_CR_PADSTR_ (0x00000100) -#define MAC_CR_BOLMT_MASK_ (0x000000C0) -#define MAC_CR_DFCHK_ (0x00000020) -#define MAC_CR_TXEN_ (0x00000008) -#define MAC_CR_RXEN_ (0x00000004) - -#define ADDRH (0x02) /* R/W mask 0x0000FFFFUL */ -#define ADDRL (0x03) /* R/W mask 0xFFFFFFFFUL */ -#define HASHH (0x04) /* R/W */ -#define HASHL (0x05) /* R/W */ - -#define MII_ACC (0x06) /* R/W */ -#define MII_ACC_PHY_ADDR_ (0x0000F800) -#define MII_ACC_MIIRINDA_ (0x000007C0) -#define MII_ACC_MII_WRITE_ (0x00000002) -#define MII_ACC_MII_BUSY_ (0x00000001) - -#define MII_DATA (0x07) /* R/W mask 0x0000FFFFUL */ - -#define FLOW (0x08) /* R/W */ -#define FLOW_FCPT_ (0xFFFF0000) -#define FLOW_FCPASS_ (0x00000004) -#define FLOW_FCEN_ (0x00000002) -#define FLOW_FCBSY_ (0x00000001) - -#define VLAN1 (0x09) /* R/W mask 0x0000FFFFUL */ -#define VLAN1_VTI1_ (0x0000ffff) - -#define VLAN2 (0x0A) /* R/W mask 0x0000FFFFUL */ -#define VLAN2_VTI2_ (0x0000ffff) - -#define WUFF (0x0B) /* WO */ - -#define WUCSR (0x0C) /* R/W */ -#define WUCSR_GUE_ (0x00000200) -#define WUCSR_WUFR_ (0x00000040) -#define WUCSR_MPR_ (0x00000020) -#define WUCSR_WAKE_EN_ (0x00000004) -#define WUCSR_MPEN_ (0x00000002) - -/* - **************************************************************************** - * Chip Specific MII Defines - **************************************************************************** - * - * Phy register offsets and bit definitions - * - */ - -#define PHY_MODE_CTRL_STS ((u32)17) /* Mode Control/Status Register */ -//#define MODE_CTRL_STS_FASTRIP_ ((u16)0x4000) -#define MODE_CTRL_STS_EDPWRDOWN_ ((u16)0x2000) -//#define MODE_CTRL_STS_LOWSQEN_ ((u16)0x0800) -//#define MODE_CTRL_STS_MDPREBP_ ((u16)0x0400) -//#define MODE_CTRL_STS_FARLOOPBACK_ ((u16)0x0200) -//#define MODE_CTRL_STS_FASTEST_ ((u16)0x0100) -//#define MODE_CTRL_STS_REFCLKEN_ ((u16)0x0010) -//#define MODE_CTRL_STS_PHYADBP_ ((u16)0x0008) -//#define MODE_CTRL_STS_FORCE_G_LINK_ ((u16)0x0004) -#define MODE_CTRL_STS_ENERGYON_ ((u16)0x0002) - -#define PHY_INT_SRC ((u32)29) -#define PHY_INT_SRC_ENERGY_ON_ ((u16)0x0080) -#define PHY_INT_SRC_ANEG_COMP_ ((u16)0x0040) -#define PHY_INT_SRC_REMOTE_FAULT_ ((u16)0x0020) -#define PHY_INT_SRC_LINK_DOWN_ ((u16)0x0010) -#define PHY_INT_SRC_ANEG_LP_ACK_ ((u16)0x0008) -#define PHY_INT_SRC_PAR_DET_FAULT_ ((u16)0x0004) -#define PHY_INT_SRC_ANEG_PGRX_ ((u16)0x0002) - -#define PHY_INT_MASK ((u32)30) -#define PHY_INT_MASK_ENERGY_ON_ ((u16)0x0080) -#define PHY_INT_MASK_ANEG_COMP_ ((u16)0x0040) -#define PHY_INT_MASK_REMOTE_FAULT_ ((u16)0x0020) -#define PHY_INT_MASK_LINK_DOWN_ ((u16)0x0010) -#define PHY_INT_MASK_ANEG_LP_ACK_ ((u16)0x0008) -#define PHY_INT_MASK_PAR_DET_FAULT_ ((u16)0x0004) -#define PHY_INT_MASK_ANEG_PGRX_ ((u16)0x0002) - -#define PHY_SPECIAL ((u32)31) -#define PHY_SPECIAL_ANEG_DONE_ ((u16)0x1000) -#define PHY_SPECIAL_RES_ ((u16)0x0040) -#define PHY_SPECIAL_RES_MASK_ ((u16)0x0FE1) -#define PHY_SPECIAL_SPD_ ((u16)0x001C) -#define PHY_SPECIAL_SPD_10HALF_ ((u16)0x0004) -#define PHY_SPECIAL_SPD_10FULL_ ((u16)0x0014) -#define PHY_SPECIAL_SPD_100HALF_ ((u16)0x0008) -#define PHY_SPECIAL_SPD_100FULL_ ((u16)0x0018) - -#define LAN911X_INTERNAL_PHY_ID (0x0007C000) - -/* Chip ID values */ -#define CHIP_9115 0x0115 -#define CHIP_9116 0x0116 -#define CHIP_9117 0x0117 -#define CHIP_9118 0x0118 -#define CHIP_9211 0x9211 -#define CHIP_9215 0x115A -#define CHIP_9217 0x117A -#define CHIP_9218 0x118A - -struct chip_id { - u16 id; - char *name; -}; - -static const struct chip_id chip_ids[] = { - { CHIP_9115, "LAN9115" }, - { CHIP_9116, "LAN9116" }, - { CHIP_9117, "LAN9117" }, - { CHIP_9118, "LAN9118" }, - { CHIP_9211, "LAN9211" }, - { CHIP_9215, "LAN9215" }, - { CHIP_9217, "LAN9217" }, - { CHIP_9218, "LAN9218" }, - { 0, NULL }, -}; - -#define IS_REV_A(x) ((x & 0xFFFF)==0) - -/* - * Macros to abstract register access according to the data bus - * capabilities. Please use those and not the in/out primitives. - */ -/* FIFO read/write macros */ -#define SMC_PUSH_DATA(lp, p, l) SMC_outsl( lp, TX_DATA_FIFO, p, (l) >> 2 ) -#define SMC_PULL_DATA(lp, p, l) SMC_insl ( lp, RX_DATA_FIFO, p, (l) >> 2 ) -#define SMC_SET_TX_FIFO(lp, x) SMC_outl( x, lp, TX_DATA_FIFO ) -#define SMC_GET_RX_FIFO(lp) SMC_inl( lp, RX_DATA_FIFO ) - - -/* I/O mapped register read/write macros */ -#define SMC_GET_TX_STS_FIFO(lp) SMC_inl( lp, TX_STATUS_FIFO ) -#define SMC_GET_RX_STS_FIFO(lp) SMC_inl( lp, RX_STATUS_FIFO ) -#define SMC_GET_RX_STS_FIFO_PEEK(lp) SMC_inl( lp, RX_STATUS_FIFO_PEEK ) -#define SMC_GET_PN(lp) (SMC_inl( lp, ID_REV ) >> 16) -#define SMC_GET_REV(lp) (SMC_inl( lp, ID_REV ) & 0xFFFF) -#define SMC_GET_IRQ_CFG(lp) SMC_inl( lp, INT_CFG ) -#define SMC_SET_IRQ_CFG(lp, x) SMC_outl( x, lp, INT_CFG ) -#define SMC_GET_INT(lp) SMC_inl( lp, INT_STS ) -#define SMC_ACK_INT(lp, x) SMC_outl( x, lp, INT_STS ) -#define SMC_GET_INT_EN(lp) SMC_inl( lp, INT_EN ) -#define SMC_SET_INT_EN(lp, x) SMC_outl( x, lp, INT_EN ) -#define SMC_GET_BYTE_TEST(lp) SMC_inl( lp, BYTE_TEST ) -#define SMC_SET_BYTE_TEST(lp, x) SMC_outl( x, lp, BYTE_TEST ) -#define SMC_GET_FIFO_INT(lp) SMC_inl( lp, FIFO_INT ) -#define SMC_SET_FIFO_INT(lp, x) SMC_outl( x, lp, FIFO_INT ) -#define SMC_SET_FIFO_TDA(lp, x) \ - do { \ - unsigned long __flags; \ - int __mask; \ - local_irq_save(__flags); \ - __mask = SMC_GET_FIFO_INT((lp)) & ~(0xFF<<24); \ - SMC_SET_FIFO_INT( (lp), __mask | (x)<<24 ); \ - local_irq_restore(__flags); \ - } while (0) -#define SMC_SET_FIFO_TSL(lp, x) \ - do { \ - unsigned long __flags; \ - int __mask; \ - local_irq_save(__flags); \ - __mask = SMC_GET_FIFO_INT((lp)) & ~(0xFF<<16); \ - SMC_SET_FIFO_INT( (lp), __mask | (((x) & 0xFF)<<16)); \ - local_irq_restore(__flags); \ - } while (0) -#define SMC_SET_FIFO_RSA(lp, x) \ - do { \ - unsigned long __flags; \ - int __mask; \ - local_irq_save(__flags); \ - __mask = SMC_GET_FIFO_INT((lp)) & ~(0xFF<<8); \ - SMC_SET_FIFO_INT( (lp), __mask | (((x) & 0xFF)<<8)); \ - local_irq_restore(__flags); \ - } while (0) -#define SMC_SET_FIFO_RSL(lp, x) \ - do { \ - unsigned long __flags; \ - int __mask; \ - local_irq_save(__flags); \ - __mask = SMC_GET_FIFO_INT((lp)) & ~0xFF; \ - SMC_SET_FIFO_INT( (lp),__mask | ((x) & 0xFF)); \ - local_irq_restore(__flags); \ - } while (0) -#define SMC_GET_RX_CFG(lp) SMC_inl( lp, RX_CFG ) -#define SMC_SET_RX_CFG(lp, x) SMC_outl( x, lp, RX_CFG ) -#define SMC_GET_TX_CFG(lp) SMC_inl( lp, TX_CFG ) -#define SMC_SET_TX_CFG(lp, x) SMC_outl( x, lp, TX_CFG ) -#define SMC_GET_HW_CFG(lp) SMC_inl( lp, HW_CFG ) -#define SMC_SET_HW_CFG(lp, x) SMC_outl( x, lp, HW_CFG ) -#define SMC_GET_RX_DP_CTRL(lp) SMC_inl( lp, RX_DP_CTRL ) -#define SMC_SET_RX_DP_CTRL(lp, x) SMC_outl( x, lp, RX_DP_CTRL ) -#define SMC_GET_PMT_CTRL(lp) SMC_inl( lp, PMT_CTRL ) -#define SMC_SET_PMT_CTRL(lp, x) SMC_outl( x, lp, PMT_CTRL ) -#define SMC_GET_GPIO_CFG(lp) SMC_inl( lp, GPIO_CFG ) -#define SMC_SET_GPIO_CFG(lp, x) SMC_outl( x, lp, GPIO_CFG ) -#define SMC_GET_RX_FIFO_INF(lp) SMC_inl( lp, RX_FIFO_INF ) -#define SMC_SET_RX_FIFO_INF(lp, x) SMC_outl( x, lp, RX_FIFO_INF ) -#define SMC_GET_TX_FIFO_INF(lp) SMC_inl( lp, TX_FIFO_INF ) -#define SMC_SET_TX_FIFO_INF(lp, x) SMC_outl( x, lp, TX_FIFO_INF ) -#define SMC_GET_GPT_CFG(lp) SMC_inl( lp, GPT_CFG ) -#define SMC_SET_GPT_CFG(lp, x) SMC_outl( x, lp, GPT_CFG ) -#define SMC_GET_RX_DROP(lp) SMC_inl( lp, RX_DROP ) -#define SMC_SET_RX_DROP(lp, x) SMC_outl( x, lp, RX_DROP ) -#define SMC_GET_MAC_CMD(lp) SMC_inl( lp, MAC_CSR_CMD ) -#define SMC_SET_MAC_CMD(lp, x) SMC_outl( x, lp, MAC_CSR_CMD ) -#define SMC_GET_MAC_DATA(lp) SMC_inl( lp, MAC_CSR_DATA ) -#define SMC_SET_MAC_DATA(lp, x) SMC_outl( x, lp, MAC_CSR_DATA ) -#define SMC_GET_AFC_CFG(lp) SMC_inl( lp, AFC_CFG ) -#define SMC_SET_AFC_CFG(lp, x) SMC_outl( x, lp, AFC_CFG ) -#define SMC_GET_E2P_CMD(lp) SMC_inl( lp, E2P_CMD ) -#define SMC_SET_E2P_CMD(lp, x) SMC_outl( x, lp, E2P_CMD ) -#define SMC_GET_E2P_DATA(lp) SMC_inl( lp, E2P_DATA ) -#define SMC_SET_E2P_DATA(lp, x) SMC_outl( x, lp, E2P_DATA ) - -/* MAC register read/write macros */ -#define SMC_GET_MAC_CSR(lp,a,v) \ - do { \ - while (SMC_GET_MAC_CMD((lp)) & MAC_CSR_CMD_CSR_BUSY_); \ - SMC_SET_MAC_CMD((lp),MAC_CSR_CMD_CSR_BUSY_ | \ - MAC_CSR_CMD_R_NOT_W_ | (a) ); \ - while (SMC_GET_MAC_CMD((lp)) & MAC_CSR_CMD_CSR_BUSY_); \ - v = SMC_GET_MAC_DATA((lp)); \ - } while (0) -#define SMC_SET_MAC_CSR(lp,a,v) \ - do { \ - while (SMC_GET_MAC_CMD((lp)) & MAC_CSR_CMD_CSR_BUSY_); \ - SMC_SET_MAC_DATA((lp), v); \ - SMC_SET_MAC_CMD((lp), MAC_CSR_CMD_CSR_BUSY_ | (a) ); \ - while (SMC_GET_MAC_CMD((lp)) & MAC_CSR_CMD_CSR_BUSY_); \ - } while (0) -#define SMC_GET_MAC_CR(lp, x) SMC_GET_MAC_CSR( (lp), MAC_CR, x ) -#define SMC_SET_MAC_CR(lp, x) SMC_SET_MAC_CSR( (lp), MAC_CR, x ) -#define SMC_GET_ADDRH(lp, x) SMC_GET_MAC_CSR( (lp), ADDRH, x ) -#define SMC_SET_ADDRH(lp, x) SMC_SET_MAC_CSR( (lp), ADDRH, x ) -#define SMC_GET_ADDRL(lp, x) SMC_GET_MAC_CSR( (lp), ADDRL, x ) -#define SMC_SET_ADDRL(lp, x) SMC_SET_MAC_CSR( (lp), ADDRL, x ) -#define SMC_GET_HASHH(lp, x) SMC_GET_MAC_CSR( (lp), HASHH, x ) -#define SMC_SET_HASHH(lp, x) SMC_SET_MAC_CSR( (lp), HASHH, x ) -#define SMC_GET_HASHL(lp, x) SMC_GET_MAC_CSR( (lp), HASHL, x ) -#define SMC_SET_HASHL(lp, x) SMC_SET_MAC_CSR( (lp), HASHL, x ) -#define SMC_GET_MII_ACC(lp, x) SMC_GET_MAC_CSR( (lp), MII_ACC, x ) -#define SMC_SET_MII_ACC(lp, x) SMC_SET_MAC_CSR( (lp), MII_ACC, x ) -#define SMC_GET_MII_DATA(lp, x) SMC_GET_MAC_CSR( (lp), MII_DATA, x ) -#define SMC_SET_MII_DATA(lp, x) SMC_SET_MAC_CSR( (lp), MII_DATA, x ) -#define SMC_GET_FLOW(lp, x) SMC_GET_MAC_CSR( (lp), FLOW, x ) -#define SMC_SET_FLOW(lp, x) SMC_SET_MAC_CSR( (lp), FLOW, x ) -#define SMC_GET_VLAN1(lp, x) SMC_GET_MAC_CSR( (lp), VLAN1, x ) -#define SMC_SET_VLAN1(lp, x) SMC_SET_MAC_CSR( (lp), VLAN1, x ) -#define SMC_GET_VLAN2(lp, x) SMC_GET_MAC_CSR( (lp), VLAN2, x ) -#define SMC_SET_VLAN2(lp, x) SMC_SET_MAC_CSR( (lp), VLAN2, x ) -#define SMC_SET_WUFF(lp, x) SMC_SET_MAC_CSR( (lp), WUFF, x ) -#define SMC_GET_WUCSR(lp, x) SMC_GET_MAC_CSR( (lp), WUCSR, x ) -#define SMC_SET_WUCSR(lp, x) SMC_SET_MAC_CSR( (lp), WUCSR, x ) - -/* PHY register read/write macros */ -#define SMC_GET_MII(lp,a,phy,v) \ - do { \ - u32 __v; \ - do { \ - SMC_GET_MII_ACC((lp), __v); \ - } while ( __v & MII_ACC_MII_BUSY_ ); \ - SMC_SET_MII_ACC( (lp), ((phy)<<11) | ((a)<<6) | \ - MII_ACC_MII_BUSY_); \ - do { \ - SMC_GET_MII_ACC( (lp), __v); \ - } while ( __v & MII_ACC_MII_BUSY_ ); \ - SMC_GET_MII_DATA((lp), v); \ - } while (0) -#define SMC_SET_MII(lp,a,phy,v) \ - do { \ - u32 __v; \ - do { \ - SMC_GET_MII_ACC((lp), __v); \ - } while ( __v & MII_ACC_MII_BUSY_ ); \ - SMC_SET_MII_DATA((lp), v); \ - SMC_SET_MII_ACC( (lp), ((phy)<<11) | ((a)<<6) | \ - MII_ACC_MII_BUSY_ | \ - MII_ACC_MII_WRITE_ ); \ - do { \ - SMC_GET_MII_ACC((lp), __v); \ - } while ( __v & MII_ACC_MII_BUSY_ ); \ - } while (0) -#define SMC_GET_PHY_BMCR(lp,phy,x) SMC_GET_MII( (lp), MII_BMCR, phy, x ) -#define SMC_SET_PHY_BMCR(lp,phy,x) SMC_SET_MII( (lp), MII_BMCR, phy, x ) -#define SMC_GET_PHY_BMSR(lp,phy,x) SMC_GET_MII( (lp), MII_BMSR, phy, x ) -#define SMC_GET_PHY_ID1(lp,phy,x) SMC_GET_MII( (lp), MII_PHYSID1, phy, x ) -#define SMC_GET_PHY_ID2(lp,phy,x) SMC_GET_MII( (lp), MII_PHYSID2, phy, x ) -#define SMC_GET_PHY_MII_ADV(lp,phy,x) SMC_GET_MII( (lp), MII_ADVERTISE, phy, x ) -#define SMC_SET_PHY_MII_ADV(lp,phy,x) SMC_SET_MII( (lp), MII_ADVERTISE, phy, x ) -#define SMC_GET_PHY_MII_LPA(lp,phy,x) SMC_GET_MII( (lp), MII_LPA, phy, x ) -#define SMC_SET_PHY_MII_LPA(lp,phy,x) SMC_SET_MII( (lp), MII_LPA, phy, x ) -#define SMC_GET_PHY_CTRL_STS(lp,phy,x) SMC_GET_MII( (lp), PHY_MODE_CTRL_STS, phy, x ) -#define SMC_SET_PHY_CTRL_STS(lp,phy,x) SMC_SET_MII( (lp), PHY_MODE_CTRL_STS, phy, x ) -#define SMC_GET_PHY_INT_SRC(lp,phy,x) SMC_GET_MII( (lp), PHY_INT_SRC, phy, x ) -#define SMC_SET_PHY_INT_SRC(lp,phy,x) SMC_SET_MII( (lp), PHY_INT_SRC, phy, x ) -#define SMC_GET_PHY_INT_MASK(lp,phy,x) SMC_GET_MII( (lp), PHY_INT_MASK, phy, x ) -#define SMC_SET_PHY_INT_MASK(lp,phy,x) SMC_SET_MII( (lp), PHY_INT_MASK, phy, x ) -#define SMC_GET_PHY_SPECIAL(lp,phy,x) SMC_GET_MII( (lp), PHY_SPECIAL, phy, x ) - - - -/* Misc read/write macros */ - -#ifndef SMC_GET_MAC_ADDR -#define SMC_GET_MAC_ADDR(lp, addr) \ - do { \ - unsigned int __v; \ - \ - SMC_GET_MAC_CSR((lp), ADDRL, __v); \ - addr[0] = __v; addr[1] = __v >> 8; \ - addr[2] = __v >> 16; addr[3] = __v >> 24; \ - SMC_GET_MAC_CSR((lp), ADDRH, __v); \ - addr[4] = __v; addr[5] = __v >> 8; \ - } while (0) -#endif - -#define SMC_SET_MAC_ADDR(lp, addr) \ - do { \ - SMC_SET_MAC_CSR((lp), ADDRL, \ - addr[0] | \ - (addr[1] << 8) | \ - (addr[2] << 16) | \ - (addr[3] << 24)); \ - SMC_SET_MAC_CSR((lp), ADDRH, addr[4]|(addr[5] << 8));\ - } while (0) - - -#define SMC_WRITE_EEPROM_CMD(lp, cmd, addr) \ - do { \ - while (SMC_GET_E2P_CMD((lp)) & MAC_CSR_CMD_CSR_BUSY_); \ - SMC_SET_MAC_CMD((lp), MAC_CSR_CMD_R_NOT_W_ | a ); \ - while (SMC_GET_MAC_CMD((lp)) & MAC_CSR_CMD_CSR_BUSY_); \ - } while (0) - -#endif /* _SMC911X_H_ */ diff --git a/include/linux/smc911x.h b/include/linux/smc911x.h deleted file mode 100644 index 8cace8189e74..000000000000 --- a/include/linux/smc911x.h +++ /dev/null @@ -1,14 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -#ifndef __SMC911X_H__ -#define __SMC911X_H__ - -#define SMC911X_USE_16BIT (1 << 0) -#define SMC911X_USE_32BIT (1 << 1) - -struct smc911x_platdata { - unsigned long flags; - unsigned long irq_flags; /* IRQF_... */ - int irq_polarity; -}; - -#endif /* __SMC911X_H__ */ From f3d27ae079fa0da298f6786b0b2cc4d2a59b3539 Mon Sep 17 00:00:00 2001 From: Shenwei Wang Date: Mon, 17 Oct 2022 11:12:36 -0500 Subject: [PATCH 0164/2002] net: fec: remove the unused functions Removed those unused functions since we simplified the driver by using the page pool to manage RX buffers. Signed-off-by: Shenwei Wang Link: https://lore.kernel.org/r/20221017161236.1563975-1-shenwei.wang@nxp.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/freescale/fec_main.c | 57 ----------------------- 1 file changed, 57 deletions(-) diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c index 98d5cd313fdd..6986b74fb8af 100644 --- a/drivers/net/ethernet/freescale/fec_main.c +++ b/drivers/net/ethernet/freescale/fec_main.c @@ -365,16 +365,6 @@ static void swap_buffer(void *bufaddr, int len) swab32s(buf); } -static void swap_buffer2(void *dst_buf, void *src_buf, int len) -{ - int i; - unsigned int *src = src_buf; - unsigned int *dst = dst_buf; - - for (i = 0; i < len; i += 4, src++, dst++) - *dst = swab32p(src); -} - static void fec_dump(struct net_device *ndev) { struct fec_enet_private *fep = netdev_priv(ndev); @@ -1494,53 +1484,6 @@ static void fec_enet_tx(struct net_device *ndev) fec_enet_tx_queue(ndev, i); } -static int __maybe_unused -fec_enet_new_rxbdp(struct net_device *ndev, struct bufdesc *bdp, struct sk_buff *skb) -{ - struct fec_enet_private *fep = netdev_priv(ndev); - int off; - - off = ((unsigned long)skb->data) & fep->rx_align; - if (off) - skb_reserve(skb, fep->rx_align + 1 - off); - - bdp->cbd_bufaddr = cpu_to_fec32(dma_map_single(&fep->pdev->dev, skb->data, FEC_ENET_RX_FRSIZE - fep->rx_align, DMA_FROM_DEVICE)); - if (dma_mapping_error(&fep->pdev->dev, fec32_to_cpu(bdp->cbd_bufaddr))) { - if (net_ratelimit()) - netdev_err(ndev, "Rx DMA memory map failed\n"); - return -ENOMEM; - } - - return 0; -} - -static bool __maybe_unused -fec_enet_copybreak(struct net_device *ndev, struct sk_buff **skb, - struct bufdesc *bdp, u32 length, bool swap) -{ - struct fec_enet_private *fep = netdev_priv(ndev); - struct sk_buff *new_skb; - - if (length > fep->rx_copybreak) - return false; - - new_skb = netdev_alloc_skb(ndev, length); - if (!new_skb) - return false; - - dma_sync_single_for_cpu(&fep->pdev->dev, - fec32_to_cpu(bdp->cbd_bufaddr), - FEC_ENET_RX_FRSIZE - fep->rx_align, - DMA_FROM_DEVICE); - if (!swap) - memcpy(new_skb->data, (*skb)->data, length); - else - swap_buffer2(new_skb->data, (*skb)->data, length); - *skb = new_skb; - - return true; -} - static void fec_enet_update_cbd(struct fec_enet_priv_rx_q *rxq, struct bufdesc *bdp, int index) { From e2ac2a00dae10997b71870ecf26ca69f4d726537 Mon Sep 17 00:00:00 2001 From: Govindarajulu Varadarajan Date: Mon, 17 Oct 2022 17:58:04 -0700 Subject: [PATCH 0165/2002] enic: define constants for legacy interrupts offset Use macro instead of function calls. These values are constant and will not change. Signed-off-by: Govindarajulu Varadarajan Link: https://lore.kernel.org/r/20221018005804.188643-1-govind.varadar@gmail.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/cisco/enic/enic.h | 23 ++++++--------------- drivers/net/ethernet/cisco/enic/enic_main.c | 11 +++++----- 2 files changed, 11 insertions(+), 23 deletions(-) diff --git a/drivers/net/ethernet/cisco/enic/enic.h b/drivers/net/ethernet/cisco/enic/enic.h index a0964b629ffc..300ad05ee05b 100644 --- a/drivers/net/ethernet/cisco/enic/enic.h +++ b/drivers/net/ethernet/cisco/enic/enic.h @@ -226,21 +226,6 @@ static inline unsigned int enic_cq_wq(struct enic *enic, unsigned int wq) return enic->rq_count + wq; } -static inline unsigned int enic_legacy_io_intr(void) -{ - return 0; -} - -static inline unsigned int enic_legacy_err_intr(void) -{ - return 1; -} - -static inline unsigned int enic_legacy_notify_intr(void) -{ - return 2; -} - static inline unsigned int enic_msix_rq_intr(struct enic *enic, unsigned int rq) { @@ -258,6 +243,10 @@ static inline unsigned int enic_msix_err_intr(struct enic *enic) return enic->rq_count + enic->wq_count; } +#define ENIC_LEGACY_IO_INTR 0 +#define ENIC_LEGACY_ERR_INTR 1 +#define ENIC_LEGACY_NOTIFY_INTR 2 + static inline unsigned int enic_msix_notify_intr(struct enic *enic) { return enic->rq_count + enic->wq_count + 1; @@ -267,7 +256,7 @@ static inline bool enic_is_err_intr(struct enic *enic, int intr) { switch (vnic_dev_get_intr_mode(enic->vdev)) { case VNIC_DEV_INTR_MODE_INTX: - return intr == enic_legacy_err_intr(); + return intr == ENIC_LEGACY_ERR_INTR; case VNIC_DEV_INTR_MODE_MSIX: return intr == enic_msix_err_intr(enic); case VNIC_DEV_INTR_MODE_MSI: @@ -280,7 +269,7 @@ static inline bool enic_is_notify_intr(struct enic *enic, int intr) { switch (vnic_dev_get_intr_mode(enic->vdev)) { case VNIC_DEV_INTR_MODE_INTX: - return intr == enic_legacy_notify_intr(); + return intr == ENIC_LEGACY_NOTIFY_INTR; case VNIC_DEV_INTR_MODE_MSIX: return intr == enic_msix_notify_intr(enic); case VNIC_DEV_INTR_MODE_MSI: diff --git a/drivers/net/ethernet/cisco/enic/enic_main.c b/drivers/net/ethernet/cisco/enic/enic_main.c index 29500d32e362..37bd38d772e8 100644 --- a/drivers/net/ethernet/cisco/enic/enic_main.c +++ b/drivers/net/ethernet/cisco/enic/enic_main.c @@ -448,9 +448,9 @@ static irqreturn_t enic_isr_legacy(int irq, void *data) { struct net_device *netdev = data; struct enic *enic = netdev_priv(netdev); - unsigned int io_intr = enic_legacy_io_intr(); - unsigned int err_intr = enic_legacy_err_intr(); - unsigned int notify_intr = enic_legacy_notify_intr(); + unsigned int io_intr = ENIC_LEGACY_IO_INTR; + unsigned int err_intr = ENIC_LEGACY_ERR_INTR; + unsigned int notify_intr = ENIC_LEGACY_NOTIFY_INTR; u32 pba; vnic_intr_mask(&enic->intr[io_intr]); @@ -1507,7 +1507,7 @@ static int enic_poll(struct napi_struct *napi, int budget) struct enic *enic = netdev_priv(netdev); unsigned int cq_rq = enic_cq_rq(enic, 0); unsigned int cq_wq = enic_cq_wq(enic, 0); - unsigned int intr = enic_legacy_io_intr(); + unsigned int intr = ENIC_LEGACY_IO_INTR; unsigned int rq_work_to_do = budget; unsigned int wq_work_to_do = ENIC_WQ_NAPI_BUDGET; unsigned int work_done, rq_work_done = 0, wq_work_done; @@ -1856,8 +1856,7 @@ static int enic_dev_notify_set(struct enic *enic) spin_lock_bh(&enic->devcmd_lock); switch (vnic_dev_get_intr_mode(enic->vdev)) { case VNIC_DEV_INTR_MODE_INTX: - err = vnic_dev_notify_set(enic->vdev, - enic_legacy_notify_intr()); + err = vnic_dev_notify_set(enic->vdev, ENIC_LEGACY_NOTIFY_INTR); break; case VNIC_DEV_INTR_MODE_MSIX: err = vnic_dev_notify_set(enic->vdev, From acd4324e5f1f11351630234297f95076f0ac9a2f Mon Sep 17 00:00:00 2001 From: Youghandhar Chintala Date: Thu, 13 Oct 2022 09:34:59 +0300 Subject: [PATCH 0166/2002] wifi: ath10k: Delay the unmapping of the buffer On WCN3990, we are seeing a rare scenario where copy engine hardware is sending a copy complete interrupt to the host driver while still processing the buffer that the driver has sent, this is leading into an SMMU fault triggering kernel panic. This is happening on copy engine channel 3 (CE3) where the driver normally enqueues WMI commands to the firmware. Upon receiving a copy complete interrupt, host driver will immediately unmap and frees the buffer presuming that hardware has processed the buffer. In the issue case, upon receiving copy complete interrupt, host driver will unmap and free the buffer but since hardware is still accessing the buffer (which in this case got unmapped in parallel), SMMU hardware will trigger an SMMU fault resulting in a kernel panic. In order to avoid this, as a work around, add a delay before unmapping the copy engine source DMA buffer. This is conditionally done for WCN3990 and only for the CE3 channel where issue is seen. Below is the crash signature: wifi smmu error: kernel: [ 10.120965] arm-smmu 15000000.iommu: Unhandled context fault: fsr=0x402, iova=0x7fdfd8ac0, fsynr=0x500003,cbfrsynra=0xc1, cb=6 arm-smmu 15000000.iommu: Unhandled context fault:fsr=0x402, iova=0x7fe06fdc0, fsynr=0x710003, cbfrsynra=0xc1, cb=6 qcom-q6v5-mss 4080000.remoteproc: fatal error received: err_qdi.c:1040:EF:wlan_process:0x1:WLAN RT:0x2091: cmnos_thread.c:3998:Asserted in copy_engine.c:AXI_ERROR_DETECTED:2149 remoteproc remoteproc0: crash detected in 4080000.remoteproc: type fatal error <3> remoteproc remoteproc0: handling crash #1 in 4080000.remoteproc pc : __arm_lpae_unmap+0x500/0x514 lr : __arm_lpae_unmap+0x4bc/0x514 sp : ffffffc011ffb530 x29: ffffffc011ffb590 x28: 0000000000000000 x27: 0000000000000000 x26: 0000000000000004 x25: 0000000000000003 x24: ffffffc011ffb890 x23: ffffffa762ef9be0 x22: ffffffa77244ef00 x21: 0000000000000009 x20: 00000007fff7c000 x19: 0000000000000003 x18: 0000000000000000 x17: 0000000000000004 x16: ffffffd7a357d9f0 x15: 0000000000000000 x14: 00fd5d4fa7ffffff x13: 000000000000000e x12: 0000000000000000 x11: 00000000ffffffff x10: 00000000fffffe00 x9 : 000000000000017c x8 : 000000000000000c x7 : 0000000000000000 x6 : ffffffa762ef9000 x5 : 0000000000000003 x4 : 0000000000000004 x3 : 0000000000001000 x2 : 00000007fff7c000 x1 : ffffffc011ffb890 x0 : 0000000000000000 Call trace: __arm_lpae_unmap+0x500/0x514 __arm_lpae_unmap+0x4bc/0x514 __arm_lpae_unmap+0x4bc/0x514 arm_lpae_unmap_pages+0x78/0xa4 arm_smmu_unmap_pages+0x78/0x104 __iommu_unmap+0xc8/0x1e4 iommu_unmap_fast+0x38/0x48 __iommu_dma_unmap+0x84/0x104 iommu_dma_free+0x34/0x50 dma_free_attrs+0xa4/0xd0 ath10k_htt_rx_free+0xc4/0xf4 [ath10k_core] ath10k_core_stop+0x64/0x7c [ath10k_core] ath10k_halt+0x11c/0x180 [ath10k_core] ath10k_stop+0x54/0x94 [ath10k_core] drv_stop+0x48/0x1c8 [mac80211] ieee80211_do_open+0x638/0x77c [mac80211] ieee80211_open+0x48/0x5c [mac80211] __dev_open+0xb4/0x174 __dev_change_flags+0xc4/0x1dc dev_change_flags+0x3c/0x7c devinet_ioctl+0x2b4/0x580 inet_ioctl+0xb0/0x1b4 sock_do_ioctl+0x4c/0x16c compat_ifreq_ioctl+0x1cc/0x35c compat_sock_ioctl+0x110/0x2ac __arm64_compat_sys_ioctl+0xf4/0x3e0 el0_svc_common+0xb4/0x17c el0_svc_compat_handler+0x2c/0x58 el0_svc_compat+0x8/0x2c Tested-on: WCN3990 hw1.0 SNOC WLAN.HL.2.0-01387-QCAHLSWMTPLZ-1 Tested-by: Douglas Anderson Signed-off-by: Youghandhar Chintala Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20221012142733.32420-1-quic_youghand@quicinc.com --- drivers/net/wireless/ath/ath10k/core.c | 16 ++++++++++++++++ drivers/net/wireless/ath/ath10k/htc.c | 9 +++++++++ drivers/net/wireless/ath/ath10k/hw.h | 2 ++ 3 files changed, 27 insertions(+) diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c index 400f332a7ff0..5eb131ab916f 100644 --- a/drivers/net/wireless/ath/ath10k/core.c +++ b/drivers/net/wireless/ath/ath10k/core.c @@ -99,6 +99,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { .dynamic_sar_support = false, .hw_restart_disconnect = false, .use_fw_tx_credits = true, + .delay_unmap_buffer = false, }, { .id = QCA988X_HW_2_0_VERSION, @@ -138,6 +139,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { .dynamic_sar_support = false, .hw_restart_disconnect = false, .use_fw_tx_credits = true, + .delay_unmap_buffer = false, }, { .id = QCA9887_HW_1_0_VERSION, @@ -178,6 +180,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { .dynamic_sar_support = false, .hw_restart_disconnect = false, .use_fw_tx_credits = true, + .delay_unmap_buffer = false, }, { .id = QCA6174_HW_3_2_VERSION, @@ -213,6 +216,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { .dynamic_sar_support = true, .hw_restart_disconnect = false, .use_fw_tx_credits = true, + .delay_unmap_buffer = false, }, { .id = QCA6174_HW_2_1_VERSION, @@ -252,6 +256,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { .dynamic_sar_support = false, .hw_restart_disconnect = false, .use_fw_tx_credits = true, + .delay_unmap_buffer = false, }, { .id = QCA6174_HW_2_1_VERSION, @@ -291,6 +296,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { .dynamic_sar_support = false, .hw_restart_disconnect = false, .use_fw_tx_credits = true, + .delay_unmap_buffer = false, }, { .id = QCA6174_HW_3_0_VERSION, @@ -330,6 +336,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { .dynamic_sar_support = false, .hw_restart_disconnect = false, .use_fw_tx_credits = true, + .delay_unmap_buffer = false, }, { .id = QCA6174_HW_3_2_VERSION, @@ -373,6 +380,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { .dynamic_sar_support = true, .hw_restart_disconnect = false, .use_fw_tx_credits = true, + .delay_unmap_buffer = false, }, { .id = QCA99X0_HW_2_0_DEV_VERSION, @@ -418,6 +426,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { .dynamic_sar_support = false, .hw_restart_disconnect = false, .use_fw_tx_credits = true, + .delay_unmap_buffer = false, }, { .id = QCA9984_HW_1_0_DEV_VERSION, @@ -470,6 +479,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { .dynamic_sar_support = false, .hw_restart_disconnect = false, .use_fw_tx_credits = true, + .delay_unmap_buffer = false, }, { .id = QCA9888_HW_2_0_DEV_VERSION, @@ -519,6 +529,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { .dynamic_sar_support = false, .hw_restart_disconnect = false, .use_fw_tx_credits = true, + .delay_unmap_buffer = false, }, { .id = QCA9377_HW_1_0_DEV_VERSION, @@ -558,6 +569,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { .dynamic_sar_support = false, .hw_restart_disconnect = false, .use_fw_tx_credits = true, + .delay_unmap_buffer = false, }, { .id = QCA9377_HW_1_1_DEV_VERSION, @@ -599,6 +611,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { .dynamic_sar_support = false, .hw_restart_disconnect = false, .use_fw_tx_credits = true, + .delay_unmap_buffer = false, }, { .id = QCA9377_HW_1_1_DEV_VERSION, @@ -631,6 +644,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { .dynamic_sar_support = false, .hw_restart_disconnect = false, .use_fw_tx_credits = true, + .delay_unmap_buffer = false, }, { .id = QCA4019_HW_1_0_DEV_VERSION, @@ -677,6 +691,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { .dynamic_sar_support = false, .hw_restart_disconnect = false, .use_fw_tx_credits = true, + .delay_unmap_buffer = false, }, { .id = WCN3990_HW_1_0_DEV_VERSION, @@ -709,6 +724,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { .dynamic_sar_support = true, .hw_restart_disconnect = true, .use_fw_tx_credits = false, + .delay_unmap_buffer = true, }, }; diff --git a/drivers/net/wireless/ath/ath10k/htc.c b/drivers/net/wireless/ath/ath10k/htc.c index 6d1784f74bea..5bfeecb95fca 100644 --- a/drivers/net/wireless/ath/ath10k/htc.c +++ b/drivers/net/wireless/ath/ath10k/htc.c @@ -56,6 +56,15 @@ void ath10k_htc_notify_tx_completion(struct ath10k_htc_ep *ep, ath10k_dbg(ar, ATH10K_DBG_HTC, "%s: ep %d skb %pK\n", __func__, ep->eid, skb); + /* A corner case where the copy completion is reaching to host but still + * copy engine is processing it due to which host unmaps corresponding + * memory and causes SMMU fault, hence as workaround adding delay + * the unmapping memory to avoid SMMU faults. + */ + if (ar->hw_params.delay_unmap_buffer && + ep->ul_pipe_id == 3) + mdelay(2); + hdr = (struct ath10k_htc_hdr *)skb->data; ath10k_htc_restore_tx_skb(ep->htc, skb); diff --git a/drivers/net/wireless/ath/ath10k/hw.h b/drivers/net/wireless/ath/ath10k/hw.h index 1b99f3a39a11..9643031a4427 100644 --- a/drivers/net/wireless/ath/ath10k/hw.h +++ b/drivers/net/wireless/ath/ath10k/hw.h @@ -637,6 +637,8 @@ struct ath10k_hw_params { bool hw_restart_disconnect; bool use_fw_tx_credits; + + bool delay_unmap_buffer; }; struct htt_resp; From 16be5e3be0e5794b5e7227c63b440f3fdb9c22ce Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Wed, 12 Oct 2022 16:32:30 +0800 Subject: [PATCH 0167/2002] wifi: rtw89: 8852b: rfk: add DACK DACK (digital-to-analog converters calibration) is used to calibrate DAC to output good quality signals. Signed-off-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20221012083234.20224-2-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/reg.h | 21 + .../net/wireless/realtek/rtw89/rtw8852b_rfk.c | 432 ++++++++++++++++++ .../net/wireless/realtek/rtw89/rtw8852b_rfk.h | 1 + 3 files changed, 454 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/reg.h b/drivers/net/wireless/realtek/rtw89/reg.h index 5482e32a72d5..40b66c071b27 100644 --- a/drivers/net/wireless/realtek/rtw89/reg.h +++ b/drivers/net/wireless/realtek/rtw89/reg.h @@ -3524,6 +3524,7 @@ #define B_ANAPAR_ADCCLK BIT(30) #define B_ANAPAR_FLTRST BIT(22) #define B_ANAPAR_CRXBB GENMASK(18, 16) +#define B_ANAPAR_EN BIT(16) #define B_ANAPAR_14 GENMASK(15, 0) #define R_RFE_E_A2 0x0334 #define R_RFE_O_SEL_A2 0x0338 @@ -4378,6 +4379,8 @@ #define B_DACK_S0P3_OK BIT(2) #define R_DACK_DADCK01 0xC084 #define B_DACK_DADCK01 GENMASK(31, 24) +#define R_DRCK_FH 0xC094 +#define B_DRCK_LAT BIT(9) #define R_DRCK 0xC0C4 #define B_DRCK_IDLE BIT(9) #define B_DRCK_EN BIT(6) @@ -4385,15 +4388,28 @@ #define R_DRCK_RES 0xC0C8 #define B_DRCK_RES GENMASK(19, 15) #define B_DRCK_POL BIT(3) +#define R_DRCK_V1 0xC0CC +#define B_DRCK_V1_SEL BIT(9) +#define B_DRCK_V1_KICK BIT(6) +#define B_DRCK_V1_CV GENMASK(4, 0) +#define R_DRCK_RS 0xC0D0 +#define B_DRCK_RS_LPS GENMASK(19, 15) +#define B_DRCK_RS_DONE BIT(3) #define R_PATH0_SAMPL_DLY_T_V1 0xC0D4 #define B_PATH0_SAMPL_DLY_T_MSK_V1 GENMASK(27, 26) #define R_P0_CFCH_BW0 0xC0D4 #define B_P0_CFCH_BW0 GENMASK(27, 26) #define R_P0_CFCH_BW1 0xC0D8 #define B_P0_CFCH_BW1 GENMASK(8, 5) +#define R_ADDCK0D 0xC0F0 +#define B_ADDCK0D_VAL2 GENMASK(31, 26) +#define B_ADDCK0D_VAL GENMASK(25, 16) #define R_ADDCK0 0xC0F4 +#define B_ADDCK0_TRG BIT(11) #define B_ADDCK0 GENMASK(9, 8) +#define B_ADDCK0_MAN GENMASK(5, 4) #define B_ADDCK0_EN BIT(4) +#define B_ADDCK0_VAL GENMASK(3, 0) #define B_ADDCK0_RST BIT(2) #define R_ADDCK0_RL 0xC0F8 #define B_ADDCK0_RLS GENMASK(29, 28) @@ -4434,8 +4450,13 @@ #define B_PATH0_BW_SEL_MSK_V1 GENMASK(8, 5) #define R_PATH1_BW_SEL_V1 0xC1D8 #define B_PATH1_BW_SEL_MSK_V1 GENMASK(8, 5) +#define R_ADDCK1D 0xC1F0 +#define B_ADDCK1D_VAL2 GENMASK(31, 26) +#define B_ADDCK1D_VAL GENMASK(25, 16) #define R_ADDCK1 0xC1F4 +#define B_ADDCK1_TRG BIT(11) #define B_ADDCK1 GENMASK(9, 8) +#define B_ADDCK1_MAN GENMASK(5, 4) #define B_ADDCK1_EN BIT(4) #define B_ADDCK1_RST BIT(2) #define R_ADDCK1_RL 0xC1F8 diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852b_rfk.c b/drivers/net/wireless/realtek/rtw89/rtw8852b_rfk.c index 761544b0dcca..5c35f4fb38ff 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852b_rfk.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852b_rfk.c @@ -12,6 +12,8 @@ #include "rtw8852b_rfk_table.h" #include "rtw8852b_table.h" +#define ADDC_T_AVG 100 + static u8 _kpath(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx) { u8 val; @@ -30,6 +32,436 @@ static u8 _kpath(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx) return val; } +static void _afe_init(struct rtw89_dev *rtwdev) +{ + rtw89_write32(rtwdev, R_AX_PHYREG_SET, 0xf); + + rtw89_rfk_parser(rtwdev, &rtw8852b_afe_init_defs_tbl); +} + +static void _drck(struct rtw89_dev *rtwdev) +{ + u32 rck_d; + u32 val; + int ret; + + rtw89_debug(rtwdev, RTW89_DBG_RFK, "[DACK]Ddie RCK start!!!\n"); + rtw89_phy_write32_mask(rtwdev, R_DRCK_V1, B_DRCK_V1_KICK, 0x1); + + ret = read_poll_timeout_atomic(rtw89_phy_read32_mask, val, val, 1, 10000, + false, rtwdev, R_DRCK_RS, B_DRCK_RS_DONE); + if (ret) + rtw89_debug(rtwdev, RTW89_DBG_RFK, "[DACK]DRCK timeout\n"); + + rtw89_phy_write32_mask(rtwdev, R_DRCK_V1, B_DRCK_V1_KICK, 0x0); + rtw89_phy_write32_mask(rtwdev, R_DRCK_FH, B_DRCK_LAT, 0x1); + udelay(1); + rtw89_phy_write32_mask(rtwdev, R_DRCK_FH, B_DRCK_LAT, 0x0); + rck_d = rtw89_phy_read32_mask(rtwdev, R_DRCK_RS, B_DRCK_RS_LPS); + rtw89_phy_write32_mask(rtwdev, R_DRCK_V1, B_DRCK_V1_SEL, 0x0); + rtw89_phy_write32_mask(rtwdev, R_DRCK_V1, B_DRCK_V1_CV, rck_d); + + rtw89_debug(rtwdev, RTW89_DBG_RFK, "[DACK]0xc0cc = 0x%x\n", + rtw89_phy_read32_mask(rtwdev, R_DRCK_V1, MASKDWORD)); +} + +static void _addck_backup(struct rtw89_dev *rtwdev) +{ + struct rtw89_dack_info *dack = &rtwdev->dack; + + rtw89_phy_write32_mask(rtwdev, R_ADDCK0, B_ADDCK0, 0x0); + dack->addck_d[0][0] = rtw89_phy_read32_mask(rtwdev, R_ADDCKR0, B_ADDCKR0_A0); + dack->addck_d[0][1] = rtw89_phy_read32_mask(rtwdev, R_ADDCKR0, B_ADDCKR0_A1); + + rtw89_phy_write32_mask(rtwdev, R_ADDCK1, B_ADDCK1, 0x0); + dack->addck_d[1][0] = rtw89_phy_read32_mask(rtwdev, R_ADDCKR1, B_ADDCKR1_A0); + dack->addck_d[1][1] = rtw89_phy_read32_mask(rtwdev, R_ADDCKR1, B_ADDCKR1_A1); +} + +static void _addck_reload(struct rtw89_dev *rtwdev) +{ + struct rtw89_dack_info *dack = &rtwdev->dack; + + /* S0 */ + rtw89_phy_write32_mask(rtwdev, R_ADDCK0D, B_ADDCK0D_VAL, dack->addck_d[0][0]); + rtw89_phy_write32_mask(rtwdev, R_ADDCK0, B_ADDCK0_VAL, dack->addck_d[0][1] >> 6); + rtw89_phy_write32_mask(rtwdev, R_ADDCK0D, B_ADDCK0D_VAL2, dack->addck_d[0][1] & 0x3f); + rtw89_phy_write32_mask(rtwdev, R_ADDCK0, B_ADDCK0_MAN, 0x3); + + /* S1 */ + rtw89_phy_write32_mask(rtwdev, R_ADDCK1D, B_ADDCK1D_VAL, dack->addck_d[1][0]); + rtw89_phy_write32_mask(rtwdev, R_ADDCK1, B_ADDCK0_VAL, dack->addck_d[1][1] >> 6); + rtw89_phy_write32_mask(rtwdev, R_ADDCK1D, B_ADDCK1D_VAL2, dack->addck_d[1][1] & 0x3f); + rtw89_phy_write32_mask(rtwdev, R_ADDCK1, B_ADDCK1_MAN, 0x3); +} + +static void _dack_backup_s0(struct rtw89_dev *rtwdev) +{ + struct rtw89_dack_info *dack = &rtwdev->dack; + u8 i; + + rtw89_phy_write32_mask(rtwdev, R_P0_NRBW, B_P0_NRBW_DBG, 0x1); + + for (i = 0; i < RTW89_DACK_MSBK_NR; i++) { + rtw89_phy_write32_mask(rtwdev, R_DCOF0, B_DCOF0_V, i); + dack->msbk_d[0][0][i] = + rtw89_phy_read32_mask(rtwdev, R_DACK_S0P2, B_DACK_S0M0); + rtw89_phy_write32_mask(rtwdev, R_DCOF8, B_DCOF8_V, i); + dack->msbk_d[0][1][i] = + rtw89_phy_read32_mask(rtwdev, R_DACK_S0P3, B_DACK_S0M1); + } + + dack->biask_d[0][0] = + rtw89_phy_read32_mask(rtwdev, R_DACK_BIAS00, B_DACK_BIAS00); + dack->biask_d[0][1] = + rtw89_phy_read32_mask(rtwdev, R_DACK_BIAS01, B_DACK_BIAS01); + + dack->dadck_d[0][0] = + rtw89_phy_read32_mask(rtwdev, R_DACK_DADCK00, B_DACK_DADCK00); + dack->dadck_d[0][1] = + rtw89_phy_read32_mask(rtwdev, R_DACK_DADCK01, B_DACK_DADCK01); +} + +static void _dack_backup_s1(struct rtw89_dev *rtwdev) +{ + struct rtw89_dack_info *dack = &rtwdev->dack; + u8 i; + + rtw89_phy_write32_mask(rtwdev, R_P1_DBGMOD, B_P1_DBGMOD_ON, 0x1); + + for (i = 0; i < RTW89_DACK_MSBK_NR; i++) { + rtw89_phy_write32_mask(rtwdev, R_DACK10, B_DACK10, i); + dack->msbk_d[1][0][i] = + rtw89_phy_read32_mask(rtwdev, R_DACK10S, B_DACK10S); + rtw89_phy_write32_mask(rtwdev, R_DACK11, B_DACK11, i); + dack->msbk_d[1][1][i] = + rtw89_phy_read32_mask(rtwdev, R_DACK11S, B_DACK11S); + } + + dack->biask_d[1][0] = + rtw89_phy_read32_mask(rtwdev, R_DACK_BIAS10, B_DACK_BIAS10); + dack->biask_d[1][1] = + rtw89_phy_read32_mask(rtwdev, R_DACK_BIAS11, B_DACK_BIAS11); + + dack->dadck_d[1][0] = + rtw89_phy_read32_mask(rtwdev, R_DACK_DADCK10, B_DACK_DADCK10); + dack->dadck_d[1][1] = + rtw89_phy_read32_mask(rtwdev, R_DACK_DADCK11, B_DACK_DADCK11); +} + +static void _check_addc(struct rtw89_dev *rtwdev, enum rtw89_rf_path path) +{ + s32 dc_re = 0, dc_im = 0; + u32 tmp; + u32 i; + + rtw89_rfk_parser_by_cond(rtwdev, path == RF_PATH_A, + &rtw8852b_check_addc_defs_a_tbl, + &rtw8852b_check_addc_defs_b_tbl); + + for (i = 0; i < ADDC_T_AVG; i++) { + tmp = rtw89_phy_read32_mask(rtwdev, R_DBG32_D, MASKDWORD); + dc_re += sign_extend32(FIELD_GET(0xfff000, tmp), 11); + dc_im += sign_extend32(FIELD_GET(0xfff, tmp), 11); + } + + dc_re /= ADDC_T_AVG; + dc_im /= ADDC_T_AVG; + + rtw89_debug(rtwdev, RTW89_DBG_RFK, + "[DACK]S%d,dc_re = 0x%x,dc_im =0x%x\n", path, dc_re, dc_im); +} + +static void _addck(struct rtw89_dev *rtwdev) +{ + struct rtw89_dack_info *dack = &rtwdev->dack; + u32 val; + int ret; + + /* S0 */ + rtw89_phy_write32_mask(rtwdev, R_ADDCK0, B_ADDCK0_MAN, 0x0); + rtw89_phy_write32_mask(rtwdev, R_PATH1_SAMPL_DLY_T_V1, 0x30, 0x0); + rtw89_phy_write32_mask(rtwdev, R_P0_NRBW, B_P0_NRBW_DBG, 0x1); + rtw89_phy_write32_mask(rtwdev, R_ANAPAR, B_ANAPAR_ADCCLK, 0x0); + rtw89_phy_write32_mask(rtwdev, R_ANAPAR, B_ANAPAR_FLTRST, 0x0); + rtw89_phy_write32_mask(rtwdev, R_ANAPAR, B_ANAPAR_FLTRST, 0x1); + rtw89_phy_write32_mask(rtwdev, R_ANAPAR_PW15, B_ANAPAR_PW15_H, 0xf); + rtw89_phy_write32_mask(rtwdev, R_ANAPAR, B_ANAPAR_EN, 0x0); + rtw89_phy_write32_mask(rtwdev, R_PATH0_SAMPL_DLY_T_V1, BIT(1), 0x1); + rtw89_phy_write32_mask(rtwdev, R_ANAPAR_PW15, B_ANAPAR_PW15_H, 0x3); + + rtw89_debug(rtwdev, RTW89_DBG_RFK, "[DACK]before S0 ADDCK\n"); + _check_addc(rtwdev, RF_PATH_A); + + rtw89_phy_write32_mask(rtwdev, R_ADDCK0, B_ADDCK0_TRG, 0x1); + rtw89_phy_write32_mask(rtwdev, R_ADDCK0, B_ADDCK0_TRG, 0x0); + udelay(1); + rtw89_phy_write32_mask(rtwdev, R_ADDCK0, B_ADDCK0, 0x1); + + ret = read_poll_timeout_atomic(rtw89_phy_read32_mask, val, val, 1, 10000, + false, rtwdev, R_ADDCKR0, BIT(0)); + if (ret) { + rtw89_debug(rtwdev, RTW89_DBG_RFK, "[DACK]S0 ADDCK timeout\n"); + dack->addck_timeout[0] = true; + } + rtw89_debug(rtwdev, RTW89_DBG_RFK, "[DACK]ADDCK ret = %d\n", ret); + rtw89_debug(rtwdev, RTW89_DBG_RFK, "[DACK]after S0 ADDCK\n"); + _check_addc(rtwdev, RF_PATH_A); + + rtw89_phy_write32_mask(rtwdev, R_PATH0_SAMPL_DLY_T_V1, BIT(1), 0x0); + rtw89_phy_write32_mask(rtwdev, R_ANAPAR, B_ANAPAR_EN, 0x1); + rtw89_phy_write32_mask(rtwdev, R_ANAPAR_PW15, B_ANAPAR_PW15_H, 0xc); + rtw89_phy_write32_mask(rtwdev, R_ANAPAR, B_ANAPAR_ADCCLK, 0x1); + rtw89_phy_write32_mask(rtwdev, R_P0_NRBW, B_P0_NRBW_DBG, 0x0); + + /* S1 */ + rtw89_phy_write32_mask(rtwdev, R_P1_DBGMOD, B_P1_DBGMOD_ON, 0x1); + rtw89_phy_write32_mask(rtwdev, R_ANAPAR, B_ANAPAR_ADCCLK, 0x0); + rtw89_phy_write32_mask(rtwdev, R_ANAPAR, B_ANAPAR_FLTRST, 0x0); + rtw89_phy_write32_mask(rtwdev, R_ANAPAR, B_ANAPAR_FLTRST, 0x1); + rtw89_phy_write32_mask(rtwdev, R_ANAPAR_PW15, B_ANAPAR_PW15_H, 0xf); + rtw89_phy_write32_mask(rtwdev, R_ANAPAR, B_ANAPAR_EN, 0x0); + rtw89_phy_write32_mask(rtwdev, R_PATH1_SAMPL_DLY_T_V1, BIT(1), 0x1); + rtw89_phy_write32_mask(rtwdev, R_ANAPAR_PW15, B_ANAPAR_PW15_H, 0x3); + + rtw89_debug(rtwdev, RTW89_DBG_RFK, "[DACK]before S1 ADDCK\n"); + _check_addc(rtwdev, RF_PATH_B); + + rtw89_phy_write32_mask(rtwdev, R_ADDCK1, B_ADDCK1_TRG, 0x1); + rtw89_phy_write32_mask(rtwdev, R_ADDCK1, B_ADDCK1_TRG, 0x0); + udelay(1); + rtw89_phy_write32_mask(rtwdev, R_ADDCK1, B_ADDCK1, 0x1); + + ret = read_poll_timeout_atomic(rtw89_phy_read32_mask, val, val, 1, 10000, + false, rtwdev, R_ADDCKR1, BIT(0)); + if (ret) { + rtw89_debug(rtwdev, RTW89_DBG_RFK, "[DACK]S1 ADDCK timeout\n"); + dack->addck_timeout[1] = true; + } + rtw89_debug(rtwdev, RTW89_DBG_RFK, "[DACK]ADDCK ret = %d\n", ret); + rtw89_debug(rtwdev, RTW89_DBG_RFK, "[DACK]after S1 ADDCK\n"); + _check_addc(rtwdev, RF_PATH_B); + + rtw89_phy_write32_mask(rtwdev, R_PATH1_SAMPL_DLY_T_V1, BIT(1), 0x0); + rtw89_phy_write32_mask(rtwdev, R_ANAPAR, B_ANAPAR_EN, 0x1); + rtw89_phy_write32_mask(rtwdev, R_ANAPAR_PW15, B_ANAPAR_PW15_H, 0xc); + rtw89_phy_write32_mask(rtwdev, R_ANAPAR, B_ANAPAR_ADCCLK, 0x1); + rtw89_phy_write32_mask(rtwdev, R_P1_DBGMOD, B_P1_DBGMOD_ON, 0x0); +} + +static void _check_dadc(struct rtw89_dev *rtwdev, enum rtw89_rf_path path) +{ + rtw89_rfk_parser_by_cond(rtwdev, path == RF_PATH_A, + &rtw8852b_check_dadc_en_defs_a_tbl, + &rtw8852b_check_dadc_en_defs_b_tbl); + + _check_addc(rtwdev, path); + + rtw89_rfk_parser_by_cond(rtwdev, path == RF_PATH_A, + &rtw8852b_check_dadc_dis_defs_a_tbl, + &rtw8852b_check_dadc_dis_defs_b_tbl); +} + +static bool _dack_s0_check_done(struct rtw89_dev *rtwdev, bool part1) +{ + if (part1) { + if (rtw89_phy_read32_mask(rtwdev, R_DACK_S0P0, B_DACK_S0P0_OK) == 0 || + rtw89_phy_read32_mask(rtwdev, R_DACK_S0P1, B_DACK_S0P1_OK) == 0) + return false; + } else { + if (rtw89_phy_read32_mask(rtwdev, R_DACK_S0P2, B_DACK_S0P2_OK) == 0 || + rtw89_phy_read32_mask(rtwdev, R_DACK_S0P3, B_DACK_S0P3_OK) == 0) + return false; + } + + return true; +} + +static void _dack_s0(struct rtw89_dev *rtwdev) +{ + struct rtw89_dack_info *dack = &rtwdev->dack; + bool done; + int ret; + + rtw89_rfk_parser(rtwdev, &rtw8852b_dack_s0_1_defs_tbl); + + ret = read_poll_timeout_atomic(_dack_s0_check_done, done, done, 1, 10000, + false, rtwdev, true); + if (ret) { + rtw89_debug(rtwdev, RTW89_DBG_RFK, "[DACK]S0 MSBK timeout\n"); + dack->msbk_timeout[0] = true; + } + rtw89_debug(rtwdev, RTW89_DBG_RFK, "[DACK]DACK ret = %d\n", ret); + + rtw89_rfk_parser(rtwdev, &rtw8852b_dack_s0_2_defs_tbl); + + ret = read_poll_timeout_atomic(_dack_s0_check_done, done, done, 1, 10000, + false, rtwdev, false); + if (ret) { + rtw89_debug(rtwdev, RTW89_DBG_RFK, "[DACK]S0 DADCK timeout\n"); + dack->dadck_timeout[0] = true; + } + rtw89_debug(rtwdev, RTW89_DBG_RFK, "[DACK]DACK ret = %d\n", ret); + + rtw89_rfk_parser(rtwdev, &rtw8852b_dack_s0_3_defs_tbl); + + rtw89_debug(rtwdev, RTW89_DBG_RFK, "[DACK]after S0 DADCK\n"); + + _dack_backup_s0(rtwdev); + rtw89_phy_write32_mask(rtwdev, R_P0_NRBW, B_P0_NRBW_DBG, 0x0); +} + +static bool _dack_s1_check_done(struct rtw89_dev *rtwdev, bool part1) +{ + if (part1) { + if (rtw89_phy_read32_mask(rtwdev, R_DACK_S1P0, B_DACK_S1P0_OK) == 0 && + rtw89_phy_read32_mask(rtwdev, R_DACK_S1P1, B_DACK_S1P1_OK) == 0) + return false; + } else { + if (rtw89_phy_read32_mask(rtwdev, R_DACK10S, B_DACK_S1P2_OK) == 0 && + rtw89_phy_read32_mask(rtwdev, R_DACK11S, B_DACK_S1P3_OK) == 0) + return false; + } + + return true; +} + +static void _dack_s1(struct rtw89_dev *rtwdev) +{ + struct rtw89_dack_info *dack = &rtwdev->dack; + bool done; + int ret; + + rtw89_rfk_parser(rtwdev, &rtw8852b_dack_s1_1_defs_tbl); + + ret = read_poll_timeout_atomic(_dack_s1_check_done, done, done, 1, 10000, + false, rtwdev, true); + if (ret) { + rtw89_debug(rtwdev, RTW89_DBG_RFK, "[DACK]S1 MSBK timeout\n"); + dack->msbk_timeout[1] = true; + } + rtw89_debug(rtwdev, RTW89_DBG_RFK, "[DACK]DACK ret = %d\n", ret); + + rtw89_rfk_parser(rtwdev, &rtw8852b_dack_s1_2_defs_tbl); + + ret = read_poll_timeout_atomic(_dack_s1_check_done, done, done, 1, 10000, + false, rtwdev, false); + if (ret) { + rtw89_debug(rtwdev, RTW89_DBG_RFK, "[DACK]S1 DADCK timeout\n"); + dack->dadck_timeout[1] = true; + } + rtw89_debug(rtwdev, RTW89_DBG_RFK, "[DACK]DACK ret = %d\n", ret); + + rtw89_rfk_parser(rtwdev, &rtw8852b_dack_s1_3_defs_tbl); + + rtw89_debug(rtwdev, RTW89_DBG_RFK, "[DACK]after S1 DADCK\n"); + + _check_dadc(rtwdev, RF_PATH_B); + _dack_backup_s1(rtwdev); + rtw89_phy_write32_mask(rtwdev, R_P1_DBGMOD, B_P1_DBGMOD_ON, 0x0); +} + +static void _dack(struct rtw89_dev *rtwdev) +{ + _dack_s0(rtwdev); + _dack_s1(rtwdev); +} + +static void _dack_dump(struct rtw89_dev *rtwdev) +{ + struct rtw89_dack_info *dack = &rtwdev->dack; + u8 i; + u8 t; + + rtw89_debug(rtwdev, RTW89_DBG_RFK, + "[DACK]S0 ADC_DCK ic = 0x%x, qc = 0x%x\n", + dack->addck_d[0][0], dack->addck_d[0][1]); + rtw89_debug(rtwdev, RTW89_DBG_RFK, + "[DACK]S1 ADC_DCK ic = 0x%x, qc = 0x%x\n", + dack->addck_d[1][0], dack->addck_d[1][1]); + rtw89_debug(rtwdev, RTW89_DBG_RFK, + "[DACK]S0 DAC_DCK ic = 0x%x, qc = 0x%x\n", + dack->dadck_d[0][0], dack->dadck_d[0][1]); + rtw89_debug(rtwdev, RTW89_DBG_RFK, + "[DACK]S1 DAC_DCK ic = 0x%x, qc = 0x%x\n", + dack->dadck_d[1][0], dack->dadck_d[1][1]); + rtw89_debug(rtwdev, RTW89_DBG_RFK, + "[DACK]S0 biask ic = 0x%x, qc = 0x%x\n", + dack->biask_d[0][0], dack->biask_d[0][1]); + rtw89_debug(rtwdev, RTW89_DBG_RFK, + "[DACK]S1 biask ic = 0x%x, qc = 0x%x\n", + dack->biask_d[1][0], dack->biask_d[1][1]); + + rtw89_debug(rtwdev, RTW89_DBG_RFK, "[DACK]S0 MSBK ic:\n"); + for (i = 0; i < 0x10; i++) { + t = dack->msbk_d[0][0][i]; + rtw89_debug(rtwdev, RTW89_DBG_RFK, "[DACK]0x%x\n", t); + } + + rtw89_debug(rtwdev, RTW89_DBG_RFK, "[DACK]S0 MSBK qc:\n"); + for (i = 0; i < RTW89_DACK_MSBK_NR; i++) { + t = dack->msbk_d[0][1][i]; + rtw89_debug(rtwdev, RTW89_DBG_RFK, "[DACK]0x%x\n", t); + } + + rtw89_debug(rtwdev, RTW89_DBG_RFK, "[DACK]S1 MSBK ic:\n"); + for (i = 0; i < RTW89_DACK_MSBK_NR; i++) { + t = dack->msbk_d[1][0][i]; + rtw89_debug(rtwdev, RTW89_DBG_RFK, "[DACK]0x%x\n", t); + } + + rtw89_debug(rtwdev, RTW89_DBG_RFK, "[DACK]S1 MSBK qc:\n"); + for (i = 0; i < RTW89_DACK_MSBK_NR; i++) { + t = dack->msbk_d[1][1][i]; + rtw89_debug(rtwdev, RTW89_DBG_RFK, "[DACK]0x%x\n", t); + } +} + +static void _dac_cal(struct rtw89_dev *rtwdev, bool force) +{ + struct rtw89_dack_info *dack = &rtwdev->dack; + u32 rf0_0, rf1_0; + + dack->dack_done = false; + rtw89_debug(rtwdev, RTW89_DBG_RFK, "[DACK]DACK 0x1\n"); + rtw89_debug(rtwdev, RTW89_DBG_RFK, "[DACK]DACK start!!!\n"); + + rf0_0 = rtw89_read_rf(rtwdev, RF_PATH_A, RR_MOD, RFREG_MASK); + rf1_0 = rtw89_read_rf(rtwdev, RF_PATH_B, RR_MOD, RFREG_MASK); + _afe_init(rtwdev); + _drck(rtwdev); + + rtw89_write_rf(rtwdev, RF_PATH_A, RR_RSV1, RR_RSV1_RST, 0x0); + rtw89_write_rf(rtwdev, RF_PATH_B, RR_RSV1, RR_RSV1_RST, 0x0); + rtw89_write_rf(rtwdev, RF_PATH_A, RR_MOD, RFREG_MASK, 0x337e1); + rtw89_write_rf(rtwdev, RF_PATH_B, RR_MOD, RFREG_MASK, 0x337e1); + _addck(rtwdev); + _addck_backup(rtwdev); + _addck_reload(rtwdev); + + rtw89_write_rf(rtwdev, RF_PATH_A, RR_MODOPT, RFREG_MASK, 0x0); + rtw89_write_rf(rtwdev, RF_PATH_B, RR_MODOPT, RFREG_MASK, 0x0); + _dack(rtwdev); + _dack_dump(rtwdev); + dack->dack_done = true; + + rtw89_write_rf(rtwdev, RF_PATH_A, RR_MOD, RFREG_MASK, rf0_0); + rtw89_write_rf(rtwdev, RF_PATH_B, RR_MOD, RFREG_MASK, rf1_0); + rtw89_write_rf(rtwdev, RF_PATH_A, RR_RSV1, RR_RSV1_RST, 0x1); + rtw89_write_rf(rtwdev, RF_PATH_B, RR_RSV1, RR_RSV1_RST, 0x1); + dack->dack_cnt++; + rtw89_debug(rtwdev, RTW89_DBG_RFK, "[DACK]DACK finish!!!\n"); +} + +void rtw8852b_dack(struct rtw89_dev *rtwdev) +{ + u8 phy_map = rtw89_btc_phymap(rtwdev, RTW89_PHY_0, 0); + + rtw89_btc_ntfy_wl_rfk(rtwdev, phy_map, BTC_WRFKT_DACK, BTC_WRFK_START); + _dac_cal(rtwdev, false); + rtw89_btc_ntfy_wl_rfk(rtwdev, phy_map, BTC_WRFKT_DACK, BTC_WRFK_STOP); +} + static void _bw_setting(struct rtw89_dev *rtwdev, enum rtw89_rf_path path, enum rtw89_bandwidth bw, bool dav) { diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852b_rfk.h b/drivers/net/wireless/realtek/rtw89/rtw8852b_rfk.h index d54256bbc9a0..bc2ff7380b92 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852b_rfk.h +++ b/drivers/net/wireless/realtek/rtw89/rtw8852b_rfk.h @@ -7,6 +7,7 @@ #include "core.h" +void rtw8852b_dack(struct rtw89_dev *rtwdev); void rtw8852b_set_channel_rf(struct rtw89_dev *rtwdev, const struct rtw89_chan *chan, enum rtw89_phy_idx phy_idx); From 10298b53bff642e586e5b82616914c9fa3d9a906 Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Wed, 12 Oct 2022 16:32:31 +0800 Subject: [PATCH 0168/2002] wifi: rtw89: 8852b: rfk: add RCK RCK is synchronize RC calibration. Driver triggers this calibration and sets the result to register. This calibration is needed once when interface is going to up. Signed-off-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20221012083234.20224-3-pkshih@realtek.com --- .../net/wireless/realtek/rtw89/rtw8852b_rfk.c | 43 +++++++++++++++++++ .../net/wireless/realtek/rtw89/rtw8852b_rfk.h | 1 + 2 files changed, 44 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852b_rfk.c b/drivers/net/wireless/realtek/rtw89/rtw8852b_rfk.c index 5c35f4fb38ff..1dcb900ef700 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852b_rfk.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852b_rfk.c @@ -32,6 +32,41 @@ static u8 _kpath(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx) return val; } +static void _rck(struct rtw89_dev *rtwdev, enum rtw89_rf_path path) +{ + u32 rf_reg5; + u32 rck_val; + u32 val; + int ret; + + rtw89_debug(rtwdev, RTW89_DBG_RFK, "[RCK] ====== S%d RCK ======\n", path); + + rf_reg5 = rtw89_read_rf(rtwdev, path, RR_RSV1, RFREG_MASK); + + rtw89_write_rf(rtwdev, path, RR_RSV1, RR_RSV1_RST, 0x0); + rtw89_write_rf(rtwdev, path, RR_MOD, RR_MOD_MASK, RR_MOD_V_RX); + + rtw89_debug(rtwdev, RTW89_DBG_RFK, "[RCK] RF0x00 = 0x%05x\n", + rtw89_read_rf(rtwdev, path, RR_MOD, RFREG_MASK)); + + /* RCK trigger */ + rtw89_write_rf(rtwdev, path, RR_RCKC, RFREG_MASK, 0x00240); + + ret = read_poll_timeout_atomic(rtw89_read_rf, val, val, 2, 30, + false, rtwdev, path, RR_RCKS, BIT(3)); + + rck_val = rtw89_read_rf(rtwdev, path, RR_RCKC, RR_RCKC_CA); + + rtw89_debug(rtwdev, RTW89_DBG_RFK, "[RCK] rck_val = 0x%x, ret = %d\n", + rck_val, ret); + + rtw89_write_rf(rtwdev, path, RR_RCKC, RFREG_MASK, rck_val); + rtw89_write_rf(rtwdev, path, RR_RSV1, RFREG_MASK, rf_reg5); + + rtw89_debug(rtwdev, RTW89_DBG_RFK, "[RCK] RF 0x1b = 0x%x\n", + rtw89_read_rf(rtwdev, path, RR_RCKC, RFREG_MASK)); +} + static void _afe_init(struct rtw89_dev *rtwdev) { rtw89_write32(rtwdev, R_AX_PHYREG_SET, 0xf); @@ -453,6 +488,14 @@ static void _dac_cal(struct rtw89_dev *rtwdev, bool force) rtw89_debug(rtwdev, RTW89_DBG_RFK, "[DACK]DACK finish!!!\n"); } +void rtw8852b_rck(struct rtw89_dev *rtwdev) +{ + u8 path; + + for (path = 0; path < RF_PATH_NUM_8852B; path++) + _rck(rtwdev, path); +} + void rtw8852b_dack(struct rtw89_dev *rtwdev) { u8 phy_map = rtw89_btc_phymap(rtwdev, RTW89_PHY_0, 0); diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852b_rfk.h b/drivers/net/wireless/realtek/rtw89/rtw8852b_rfk.h index bc2ff7380b92..84325abc7f2c 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852b_rfk.h +++ b/drivers/net/wireless/realtek/rtw89/rtw8852b_rfk.h @@ -7,6 +7,7 @@ #include "core.h" +void rtw8852b_rck(struct rtw89_dev *rtwdev); void rtw8852b_dack(struct rtw89_dev *rtwdev); void rtw8852b_set_channel_rf(struct rtw89_dev *rtwdev, const struct rtw89_chan *chan, From 212671074ab2f3e33fd5e95392c7356410ad7f8d Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Wed, 12 Oct 2022 16:32:32 +0800 Subject: [PATCH 0169/2002] wifi: rtw89: 8852b: rfk: add RX DCK RX DCK is receiver DC calibration. With this calibration, we have proper DC offset to reflect correct received signal strength indicator. Do this calibration when bringing up interface and going to run on AP channel. Signed-off-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20221012083234.20224-4-pkshih@realtek.com --- .../net/wireless/realtek/rtw89/rtw8852b_rfk.c | 75 +++++++++++++++++++ .../net/wireless/realtek/rtw89/rtw8852b_rfk.h | 1 + 2 files changed, 76 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852b_rfk.c b/drivers/net/wireless/realtek/rtw89/rtw8852b_rfk.c index 1dcb900ef700..306f6a292c59 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852b_rfk.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852b_rfk.c @@ -12,6 +12,7 @@ #include "rtw8852b_rfk_table.h" #include "rtw8852b_table.h" +#define RTW8852B_RXDCK_VER 0x1 #define ADDC_T_AVG 100 static u8 _kpath(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx) @@ -32,6 +33,47 @@ static u8 _kpath(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx) return val; } +static void _set_rx_dck(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy, + enum rtw89_rf_path path) +{ + rtw89_write_rf(rtwdev, path, RR_DCK1, RR_DCK1_CLR, 0x0); + rtw89_write_rf(rtwdev, path, RR_DCK, RR_DCK_LV, 0x0); + rtw89_write_rf(rtwdev, path, RR_DCK, RR_DCK_LV, 0x1); + mdelay(1); +} + +static void _rx_dck(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy) +{ + u8 path, dck_tune; + u32 rf_reg5; + + rtw89_debug(rtwdev, RTW89_DBG_RFK, + "[RX_DCK] ****** RXDCK Start (Ver: 0x%x, CV : 0x%x) ******\n", + RTW8852B_RXDCK_VER, rtwdev->hal.cv); + + for (path = 0; path < RF_PATH_NUM_8852B; path++) { + rf_reg5 = rtw89_read_rf(rtwdev, path, RR_RSV1, RFREG_MASK); + dck_tune = rtw89_read_rf(rtwdev, path, RR_DCK, RR_DCK_FINE); + + if (rtwdev->is_tssi_mode[path]) + rtw89_phy_write32_mask(rtwdev, + R_P0_TSSI_TRK + (path << 13), + B_P0_TSSI_TRK_EN, 0x1); + + rtw89_write_rf(rtwdev, path, RR_RSV1, RR_RSV1_RST, 0x0); + rtw89_write_rf(rtwdev, path, RR_DCK, RR_DCK_FINE, 0x0); + rtw89_write_rf(rtwdev, path, RR_MOD, RR_MOD_MASK, RR_MOD_V_RX); + _set_rx_dck(rtwdev, phy, path); + rtw89_write_rf(rtwdev, path, RR_DCK, RR_DCK_FINE, dck_tune); + rtw89_write_rf(rtwdev, path, RR_RSV1, RFREG_MASK, rf_reg5); + + if (rtwdev->is_tssi_mode[path]) + rtw89_phy_write32_mask(rtwdev, + R_P0_TSSI_TRK + (path << 13), + B_P0_TSSI_TRK_EN, 0x0); + } +} + static void _rck(struct rtw89_dev *rtwdev, enum rtw89_rf_path path) { u32 rf_reg5; @@ -488,6 +530,24 @@ static void _dac_cal(struct rtw89_dev *rtwdev, bool force) rtw89_debug(rtwdev, RTW89_DBG_RFK, "[DACK]DACK finish!!!\n"); } +static void _wait_rx_mode(struct rtw89_dev *rtwdev, u8 kpath) +{ + u32 rf_mode; + u8 path; + int ret; + + for (path = 0; path < RF_PATH_MAX; path++) { + if (!(kpath & BIT(path))) + continue; + + ret = read_poll_timeout_atomic(rtw89_read_rf, rf_mode, + rf_mode != 2, 2, 5000, false, + rtwdev, path, RR_MOD, RR_MOD_MASK); + rtw89_debug(rtwdev, RTW89_DBG_RFK, + "[RFK] Wait S%d to Rx mode!! (ret = %d)\n", path, ret); + } +} + void rtw8852b_rck(struct rtw89_dev *rtwdev) { u8 path; @@ -505,6 +565,21 @@ void rtw8852b_dack(struct rtw89_dev *rtwdev) rtw89_btc_ntfy_wl_rfk(rtwdev, phy_map, BTC_WRFKT_DACK, BTC_WRFK_STOP); } +void rtw8852b_rx_dck(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx) +{ + u8 phy_map = rtw89_btc_phymap(rtwdev, phy_idx, 0); + u32 tx_en; + + rtw89_btc_ntfy_wl_rfk(rtwdev, phy_map, BTC_WRFKT_RXDCK, BTC_WRFK_START); + rtw89_chip_stop_sch_tx(rtwdev, phy_idx, &tx_en, RTW89_SCH_TX_SEL_ALL); + _wait_rx_mode(rtwdev, _kpath(rtwdev, phy_idx)); + + _rx_dck(rtwdev, phy_idx); + + rtw89_chip_resume_sch_tx(rtwdev, phy_idx, tx_en); + rtw89_btc_ntfy_wl_rfk(rtwdev, phy_map, BTC_WRFKT_RXDCK, BTC_WRFK_STOP); +} + static void _bw_setting(struct rtw89_dev *rtwdev, enum rtw89_rf_path path, enum rtw89_bandwidth bw, bool dav) { diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852b_rfk.h b/drivers/net/wireless/realtek/rtw89/rtw8852b_rfk.h index 84325abc7f2c..24e492484d27 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852b_rfk.h +++ b/drivers/net/wireless/realtek/rtw89/rtw8852b_rfk.h @@ -9,6 +9,7 @@ void rtw8852b_rck(struct rtw89_dev *rtwdev); void rtw8852b_dack(struct rtw89_dev *rtwdev); +void rtw8852b_rx_dck(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx); void rtw8852b_set_channel_rf(struct rtw89_dev *rtwdev, const struct rtw89_chan *chan, enum rtw89_phy_idx phy_idx); From f2abe804e8230ff20a834e204bde529935df5467 Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Wed, 12 Oct 2022 16:32:33 +0800 Subject: [PATCH 0170/2002] wifi: rtw89: 8852b: rfk: add IQK IQ signal calibration is a very important calibration to yield good RF performance. We do this calibration only if we are going to run on AP channel. During scanning phase, without this calibration RF performance is still acceptable because it transmits with low data rate at this phase. Signed-off-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20221012083234.20224-5-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/reg.h | 19 + .../net/wireless/realtek/rtw89/rtw8852b_rfk.c | 1046 +++++++++++++++++ .../net/wireless/realtek/rtw89/rtw8852b_rfk.h | 1 + 3 files changed, 1066 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/reg.h b/drivers/net/wireless/realtek/rtw89/reg.h index 40b66c071b27..6dc078969d58 100644 --- a/drivers/net/wireless/realtek/rtw89/reg.h +++ b/drivers/net/wireless/realtek/rtw89/reg.h @@ -3295,6 +3295,7 @@ #define RR_MOD_IQK GENMASK(19, 4) #define RR_MOD_DPK GENMASK(19, 5) #define RR_MOD_MASK GENMASK(19, 16) +#define RR_MOD_RGM GENMASK(13, 4) #define RR_MOD_V_DOWN 0x0 #define RR_MOD_V_STANDBY 0x1 #define RR_MOD_V_TX 0x2 @@ -3368,6 +3369,7 @@ #define RR_RXK_PLLEN BIT(5) #define RR_LUTWA 0x33 #define RR_LUTWA_MASK GENMASK(9, 0) +#define RR_LUTWA_M1 GENMASK(7, 0) #define RR_LUTWA_M2 GENMASK(4, 0) #define RR_LUTWD1 0x3e #define RR_LUTWD0 0x3f @@ -3415,6 +3417,8 @@ #define RR_TXA2_LDO GENMASK(19, 16) #define RR_TRXIQ 0x66 #define RR_RSV6 0x6d +#define RR_TXVBUF 0x7c +#define RR_TXVBUF_DACEN BIT(5) #define RR_TXPOW 0x7f #define RR_TXPOW_TXA BIT(8) #define RR_TXPOW_TXAS BIT(7) @@ -3438,7 +3442,9 @@ #define RR_RXA2 0x8c #define RR_RXA2_C1 GENMASK(12, 10) #define RR_RXA2_C2 GENMASK(9, 3) +#define RR_RXA2_CC2 GENMASK(8, 7) #define RR_RXA2_IATT GENMASK(7, 4) +#define RR_RXA2_HATT GENMASK(6, 0) #define RR_RXA2_ATT GENMASK(3, 0) #define RR_RXIQGEN 0x8d #define RR_RXIQGEN_ATTL GENMASK(12, 8) @@ -3450,6 +3456,7 @@ #define RR_RXBB2_IDAC GENMASK(11, 9) #define RR_RXBB2_EBW GENMASK(6, 5) #define RR_XALNA2 0x90 +#define RR_XALNA2_SW2 GENMASK(9, 8) #define RR_XALNA2_SW GENMASK(1, 0) #define RR_DCK 0x92 #define RR_DCK_DONE GENMASK(7, 5) @@ -3530,6 +3537,8 @@ #define R_RFE_O_SEL_A2 0x0338 #define R_RFE_SEL0_A2 0x033C #define R_RFE_SEL32_A2 0x0340 +#define R_CIRST 0x035c +#define B_CIRST_SYN GENMASK(11, 10) #define R_SWSI_DATA_V1 0x0370 #define B_SWSI_DATA_VAL_V1 GENMASK(19, 0) #define B_SWSI_DATA_ADDR_V1 GENMASK(27, 20) @@ -3777,6 +3786,11 @@ #define B_P1_EN_SOUND_WO_NDP BIT(1) #define R_S1_HW_SI_DIS 0x3200 #define B_S1_HW_SI_DIS_W_R_TRIG GENMASK(30, 28) +#define R_P1_RXCK 0x32A0 +#define B_P1_RXCK_BW3 BIT(30) +#define B_P1_TXCK_ALL GENMASK(19, 12) +#define B_P1_RXCK_ON BIT(19) +#define B_P1_RXCK_VAL GENMASK(18, 16) #define R_P1_RFMODE 0x32AC #define B_P1_RFMODE_ORI_TXRX_FTM_TX GENMASK(31, 4) #define B_P1_RFMODE_MUX GENMASK(11, 4) @@ -4107,6 +4121,7 @@ #define R_P0_TSSI_AVG 0x5820 #define B_P0_TSSI_AVG GENMASK(15, 12) #define R_P0_RFCTM 0x5864 +#define B_P0_RFCTM_EN BIT(29) #define B_P0_RFCTM_VAL GENMASK(25, 20) #define R_P0_RFCTM_RDY BIT(26) #define R_P0_TRSW 0x5868 @@ -4266,6 +4281,7 @@ #define B_COEF_SEL_MDPD BIT(8) #define R_CFIR_SYS 0x8120 #define R_IQK_RES 0x8124 +#define B_IQK_RES_K BIT(28) #define B_IQK_RES_TXCFIR GENMASK(11, 8) #define B_IQK_RES_RXCFIR GENMASK(3, 0) #define R_TXIQC 0x8138 @@ -4322,6 +4338,9 @@ #define B_RPT_PER_TSSI GENMASK(28, 16) #define B_RPT_PER_OF GENMASK(15, 8) #define B_RPT_PER_TH GENMASK(5, 0) +#define R_IQRSN 0x8220 +#define B_IQRSN_K1 BIT(28) +#define B_IQRSN_K2 BIT(16) #define R_RXCFIR_P0C0 0x8D40 #define R_RXCFIR_P0C1 0x8D84 #define R_RXCFIR_P0C2 0x8DC8 diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852b_rfk.c b/drivers/net/wireless/realtek/rtw89/rtw8852b_rfk.c index 306f6a292c59..466f908e0214 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852b_rfk.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852b_rfk.c @@ -13,8 +13,189 @@ #include "rtw8852b_table.h" #define RTW8852B_RXDCK_VER 0x1 +#define RTW8852B_IQK_VER 0x2a +#define RTW8852B_IQK_SS 2 +#define RTW8852B_RXK_GROUP_NR 4 #define ADDC_T_AVG 100 +enum rtw8852b_iqk_type { + ID_TXAGC = 0x0, + ID_FLOK_COARSE = 0x1, + ID_FLOK_FINE = 0x2, + ID_TXK = 0x3, + ID_RXAGC = 0x4, + ID_RXK = 0x5, + ID_NBTXK = 0x6, + ID_NBRXK = 0x7, + ID_FLOK_VBUFFER = 0x8, + ID_A_FLOK_COARSE = 0x9, + ID_G_FLOK_COARSE = 0xa, + ID_A_FLOK_FINE = 0xb, + ID_G_FLOK_FINE = 0xc, + ID_IQK_RESTORE = 0x10, +}; + +static const u32 _a_idxrxgain[RTW8852B_RXK_GROUP_NR] = {0x190, 0x198, 0x350, 0x352}; +static const u32 _a_idxattc2[RTW8852B_RXK_GROUP_NR] = {0x0f, 0x0f, 0x3f, 0x7f}; +static const u32 _a_idxattc1[RTW8852B_RXK_GROUP_NR] = {0x3, 0x1, 0x0, 0x0}; +static const u32 _g_idxrxgain[RTW8852B_RXK_GROUP_NR] = {0x212, 0x21c, 0x350, 0x360}; +static const u32 _g_idxattc2[RTW8852B_RXK_GROUP_NR] = {0x00, 0x00, 0x28, 0x5f}; +static const u32 _g_idxattc1[RTW8852B_RXK_GROUP_NR] = {0x3, 0x3, 0x2, 0x1}; +static const u32 _a_power_range[RTW8852B_RXK_GROUP_NR] = {0x0, 0x0, 0x0, 0x0}; +static const u32 _a_track_range[RTW8852B_RXK_GROUP_NR] = {0x3, 0x3, 0x6, 0x6}; +static const u32 _a_gain_bb[RTW8852B_RXK_GROUP_NR] = {0x08, 0x0e, 0x06, 0x0e}; +static const u32 _a_itqt[RTW8852B_RXK_GROUP_NR] = {0x12, 0x12, 0x12, 0x1b}; +static const u32 _g_power_range[RTW8852B_RXK_GROUP_NR] = {0x0, 0x0, 0x0, 0x0}; +static const u32 _g_track_range[RTW8852B_RXK_GROUP_NR] = {0x4, 0x4, 0x6, 0x6}; +static const u32 _g_gain_bb[RTW8852B_RXK_GROUP_NR] = {0x08, 0x0e, 0x06, 0x0e}; +static const u32 _g_itqt[RTW8852B_RXK_GROUP_NR] = {0x09, 0x12, 0x1b, 0x24}; + +static const u32 rtw8852b_backup_bb_regs[] = {0x2344, 0x5800, 0x7800}; +static const u32 rtw8852b_backup_rf_regs[] = { + 0xde, 0xdf, 0x8b, 0x90, 0x97, 0x85, 0x1e, 0x0, 0x2, 0x5, 0x10005 +}; + +#define BACKUP_BB_REGS_NR ARRAY_SIZE(rtw8852b_backup_bb_regs) +#define BACKUP_RF_REGS_NR ARRAY_SIZE(rtw8852b_backup_rf_regs) + +static const struct rtw89_reg3_def rtw8852b_set_nondbcc_path01[] = { + {0x20fc, 0xffff0000, 0x0303}, + {0x5864, 0x18000000, 0x3}, + {0x7864, 0x18000000, 0x3}, + {0x12b8, 0x40000000, 0x1}, + {0x32b8, 0x40000000, 0x1}, + {0x030c, 0xff000000, 0x13}, + {0x032c, 0xffff0000, 0x0041}, + {0x12b8, 0x10000000, 0x1}, + {0x58c8, 0x01000000, 0x1}, + {0x78c8, 0x01000000, 0x1}, + {0x5864, 0xc0000000, 0x3}, + {0x7864, 0xc0000000, 0x3}, + {0x2008, 0x01ffffff, 0x1ffffff}, + {0x0c1c, 0x00000004, 0x1}, + {0x0700, 0x08000000, 0x1}, + {0x0c70, 0x000003ff, 0x3ff}, + {0x0c60, 0x00000003, 0x3}, + {0x0c6c, 0x00000001, 0x1}, + {0x58ac, 0x08000000, 0x1}, + {0x78ac, 0x08000000, 0x1}, + {0x0c3c, 0x00000200, 0x1}, + {0x2344, 0x80000000, 0x1}, + {0x4490, 0x80000000, 0x1}, + {0x12a0, 0x00007000, 0x7}, + {0x12a0, 0x00008000, 0x1}, + {0x12a0, 0x00070000, 0x3}, + {0x12a0, 0x00080000, 0x1}, + {0x32a0, 0x00070000, 0x3}, + {0x32a0, 0x00080000, 0x1}, + {0x0700, 0x01000000, 0x1}, + {0x0700, 0x06000000, 0x2}, + {0x20fc, 0xffff0000, 0x3333}, +}; + +static const struct rtw89_reg3_def rtw8852b_restore_nondbcc_path01[] = { + {0x20fc, 0xffff0000, 0x0303}, + {0x12b8, 0x40000000, 0x0}, + {0x32b8, 0x40000000, 0x0}, + {0x5864, 0xc0000000, 0x0}, + {0x7864, 0xc0000000, 0x0}, + {0x2008, 0x01ffffff, 0x0000000}, + {0x0c1c, 0x00000004, 0x0}, + {0x0700, 0x08000000, 0x0}, + {0x0c70, 0x0000001f, 0x03}, + {0x0c70, 0x000003e0, 0x03}, + {0x12a0, 0x000ff000, 0x00}, + {0x32a0, 0x000ff000, 0x00}, + {0x0700, 0x07000000, 0x0}, + {0x20fc, 0xffff0000, 0x0000}, + {0x58c8, 0x01000000, 0x0}, + {0x78c8, 0x01000000, 0x0}, + {0x0c3c, 0x00000200, 0x0}, + {0x2344, 0x80000000, 0x0}, +}; + +static void _rfk_backup_bb_reg(struct rtw89_dev *rtwdev, u32 backup_bb_reg_val[]) +{ + u32 i; + + for (i = 0; i < BACKUP_BB_REGS_NR; i++) { + backup_bb_reg_val[i] = + rtw89_phy_read32_mask(rtwdev, rtw8852b_backup_bb_regs[i], + MASKDWORD); + rtw89_debug(rtwdev, RTW89_DBG_RFK, + "[RFK]backup bb reg : %x, value =%x\n", + rtw8852b_backup_bb_regs[i], backup_bb_reg_val[i]); + } +} + +static void _rfk_backup_rf_reg(struct rtw89_dev *rtwdev, u32 backup_rf_reg_val[], + u8 rf_path) +{ + u32 i; + + for (i = 0; i < BACKUP_RF_REGS_NR; i++) { + backup_rf_reg_val[i] = + rtw89_read_rf(rtwdev, rf_path, + rtw8852b_backup_rf_regs[i], RFREG_MASK); + rtw89_debug(rtwdev, RTW89_DBG_RFK, + "[RFK]backup rf S%d reg : %x, value =%x\n", rf_path, + rtw8852b_backup_rf_regs[i], backup_rf_reg_val[i]); + } +} + +static void _rfk_restore_bb_reg(struct rtw89_dev *rtwdev, + const u32 backup_bb_reg_val[]) +{ + u32 i; + + for (i = 0; i < BACKUP_BB_REGS_NR; i++) { + rtw89_phy_write32_mask(rtwdev, rtw8852b_backup_bb_regs[i], + MASKDWORD, backup_bb_reg_val[i]); + rtw89_debug(rtwdev, RTW89_DBG_RFK, + "[RFK]restore bb reg : %x, value =%x\n", + rtw8852b_backup_bb_regs[i], backup_bb_reg_val[i]); + } +} + +static void _rfk_restore_rf_reg(struct rtw89_dev *rtwdev, + const u32 backup_rf_reg_val[], u8 rf_path) +{ + u32 i; + + for (i = 0; i < BACKUP_RF_REGS_NR; i++) { + rtw89_write_rf(rtwdev, rf_path, rtw8852b_backup_rf_regs[i], + RFREG_MASK, backup_rf_reg_val[i]); + + rtw89_debug(rtwdev, RTW89_DBG_RFK, + "[RFK]restore rf S%d reg: %x, value =%x\n", rf_path, + rtw8852b_backup_rf_regs[i], backup_rf_reg_val[i]); + } +} + +static bool _iqk_check_cal(struct rtw89_dev *rtwdev, u8 path) +{ + bool fail = true; + u32 val; + int ret; + + ret = read_poll_timeout_atomic(rtw89_phy_read32_mask, val, val == 0x55, + 1, 8200, false, rtwdev, 0xbff8, MASKBYTE0); + if (ret) + rtw89_debug(rtwdev, RTW89_DBG_RFK, "[IQK]NCTL1 IQK timeout!!!\n"); + + udelay(200); + + if (!ret) + fail = rtw89_phy_read32_mask(rtwdev, R_NCTL_RPT, B_NCTL_RPT_FLG); + rtw89_phy_write32_mask(rtwdev, R_NCTL_N1, MASKBYTE0, 0x0); + + rtw89_debug(rtwdev, RTW89_DBG_RFK, "[IQK]S%x, ret=%d\n", path, ret); + val = rtw89_phy_read32_mask(rtwdev, R_NCTL_RPT, MASKDWORD); + rtw89_debug(rtwdev, RTW89_DBG_RFK, "[IQK]S%x, 0x8008 = 0x%x\n", path, val); + + return fail; +} + static u8 _kpath(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx) { u8 val; @@ -530,6 +711,803 @@ static void _dac_cal(struct rtw89_dev *rtwdev, bool force) rtw89_debug(rtwdev, RTW89_DBG_RFK, "[DACK]DACK finish!!!\n"); } +static void _iqk_rxk_setting(struct rtw89_dev *rtwdev, u8 path) +{ + struct rtw89_iqk_info *iqk_info = &rtwdev->iqk; + u32 tmp; + + switch (iqk_info->iqk_band[path]) { + case RTW89_BAND_2G: + rtw89_write_rf(rtwdev, path, RR_MOD, RR_MOD_MASK, 0xc); + rtw89_write_rf(rtwdev, path, RR_RXK, RR_RXK_SEL2G, 0x1); + tmp = rtw89_read_rf(rtwdev, path, RR_CFGCH, RFREG_MASK); + rtw89_write_rf(rtwdev, path, RR_RSV4, RFREG_MASK, tmp); + break; + case RTW89_BAND_5G: + rtw89_write_rf(rtwdev, path, RR_MOD, RR_MOD_MASK, 0xc); + rtw89_write_rf(rtwdev, path, RR_RXK, RR_RXK_SEL5G, 0x1); + tmp = rtw89_read_rf(rtwdev, path, RR_CFGCH, RFREG_MASK); + rtw89_write_rf(rtwdev, path, RR_RSV4, RFREG_MASK, tmp); + break; + default: + break; + } +} + +static bool _iqk_one_shot(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx, + u8 path, u8 ktype) +{ + struct rtw89_iqk_info *iqk_info = &rtwdev->iqk; + u32 iqk_cmd; + bool fail; + + switch (ktype) { + case ID_FLOK_COARSE: + rtw89_phy_write32_mask(rtwdev, R_P0_RFCTM, B_P0_RFCTM_EN, 0x1); + iqk_cmd = 0x108 | (1 << (4 + path)); + break; + case ID_FLOK_FINE: + rtw89_phy_write32_mask(rtwdev, R_P0_RFCTM, B_P0_RFCTM_EN, 0x1); + iqk_cmd = 0x208 | (1 << (4 + path)); + break; + case ID_FLOK_VBUFFER: + rtw89_phy_write32_mask(rtwdev, R_P0_RFCTM, B_P0_RFCTM_EN, 0x1); + iqk_cmd = 0x308 | (1 << (4 + path)); + break; + case ID_TXK: + rtw89_phy_write32_mask(rtwdev, R_P0_RFCTM, B_P0_RFCTM_EN, 0x0); + iqk_cmd = 0x008 | (1 << (path + 4)) | + (((0x8 + iqk_info->iqk_bw[path]) & 0xf) << 8); + break; + case ID_RXAGC: + iqk_cmd = 0x508 | (1 << (4 + path)) | (path << 1); + break; + case ID_RXK: + rtw89_phy_write32_mask(rtwdev, R_P0_RFCTM, B_P0_RFCTM_EN, 0x1); + iqk_cmd = 0x008 | (1 << (path + 4)) | + (((0xb + iqk_info->iqk_bw[path]) & 0xf) << 8); + break; + case ID_NBTXK: + rtw89_phy_write32_mask(rtwdev, R_P0_RFCTM, B_P0_RFCTM_EN, 0x0); + rtw89_phy_write32_mask(rtwdev, R_IQK_DIF4, B_IQK_DIF4_TXT, 0x011); + iqk_cmd = 0x308 | (1 << (4 + path)); + break; + case ID_NBRXK: + rtw89_phy_write32_mask(rtwdev, R_P0_RFCTM, B_P0_RFCTM_EN, 0x1); + rtw89_phy_write32_mask(rtwdev, R_IQK_DIF4, B_IQK_DIF4_RXT, 0x011); + iqk_cmd = 0x608 | (1 << (4 + path)); + break; + default: + return false; + } + + rtw89_phy_write32_mask(rtwdev, R_NCTL_CFG, MASKDWORD, iqk_cmd + 1); + udelay(1); + fail = _iqk_check_cal(rtwdev, path); + rtw89_phy_write32_mask(rtwdev, R_P0_RFCTM, B_P0_RFCTM_EN, 0x0); + + return fail; +} + +static bool _rxk_group_sel(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx, + u8 path) +{ + struct rtw89_iqk_info *iqk_info = &rtwdev->iqk; + bool kfail = false; + bool fail; + u8 gp; + + for (gp = 0; gp < RTW8852B_RXK_GROUP_NR; gp++) { + switch (iqk_info->iqk_band[path]) { + case RTW89_BAND_2G: + rtw89_write_rf(rtwdev, path, RR_MOD, RR_MOD_RGM, + _g_idxrxgain[gp]); + rtw89_write_rf(rtwdev, path, RR_RXBB, RR_RXBB_C2G, + _g_idxattc2[gp]); + rtw89_write_rf(rtwdev, path, RR_RXBB, RR_RXBB_C1G, + _g_idxattc1[gp]); + break; + case RTW89_BAND_5G: + rtw89_write_rf(rtwdev, path, RR_MOD, RR_MOD_RGM, + _a_idxrxgain[gp]); + rtw89_write_rf(rtwdev, path, RR_RXA2, RR_RXA2_HATT, + _a_idxattc2[gp]); + rtw89_write_rf(rtwdev, path, RR_RXA2, RR_RXA2_CC2, + _a_idxattc1[gp]); + break; + default: + break; + } + + rtw89_phy_write32_mask(rtwdev, R_CFIR_LUT + (path << 8), + B_CFIR_LUT_SEL, 0x1); + rtw89_phy_write32_mask(rtwdev, R_CFIR_LUT + (path << 8), + B_CFIR_LUT_SET, 0x0); + rtw89_phy_write32_mask(rtwdev, R_CFIR_LUT + (path << 8), + B_CFIR_LUT_GP_V1, gp); + fail = _iqk_one_shot(rtwdev, phy_idx, path, ID_RXK); + rtw89_phy_write32_mask(rtwdev, R_IQKINF, + BIT(16 + gp + path * 4), fail); + kfail |= fail; + } + rtw89_write_rf(rtwdev, path, RR_RXK, RR_RXK_SEL5G, 0x0); + + if (kfail) { + iqk_info->nb_rxcfir[path] = 0x40000002; + rtw89_phy_write32_mask(rtwdev, R_IQK_RES + (path << 8), + B_IQK_RES_RXCFIR, 0x0); + iqk_info->is_wb_rxiqk[path] = false; + } else { + iqk_info->nb_rxcfir[path] = 0x40000000; + rtw89_phy_write32_mask(rtwdev, R_IQK_RES + (path << 8), + B_IQK_RES_RXCFIR, 0x5); + iqk_info->is_wb_rxiqk[path] = true; + } + + return kfail; +} + +static bool _iqk_nbrxk(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx, + u8 path) +{ + struct rtw89_iqk_info *iqk_info = &rtwdev->iqk; + const u8 gp = 0x3; + bool kfail = false; + bool fail; + + switch (iqk_info->iqk_band[path]) { + case RTW89_BAND_2G: + rtw89_write_rf(rtwdev, path, RR_MOD, RR_MOD_RGM, + _g_idxrxgain[gp]); + rtw89_write_rf(rtwdev, path, RR_RXBB, RR_RXBB_C2G, + _g_idxattc2[gp]); + rtw89_write_rf(rtwdev, path, RR_RXBB, RR_RXBB_C1G, + _g_idxattc1[gp]); + break; + case RTW89_BAND_5G: + rtw89_write_rf(rtwdev, path, RR_MOD, RR_MOD_RGM, + _a_idxrxgain[gp]); + rtw89_write_rf(rtwdev, path, RR_RXA2, RR_RXA2_HATT, + _a_idxattc2[gp]); + rtw89_write_rf(rtwdev, path, RR_RXA2, RR_RXA2_CC2, + _a_idxattc1[gp]); + break; + default: + break; + } + + rtw89_phy_write32_mask(rtwdev, R_CFIR_LUT + (path << 8), B_CFIR_LUT_SEL, 0x1); + rtw89_phy_write32_mask(rtwdev, R_CFIR_LUT + (path << 8), B_CFIR_LUT_SET, 0x0); + rtw89_phy_write32_mask(rtwdev, R_CFIR_LUT + (path << 8), B_CFIR_LUT_GP_V1, gp); + rtw89_write_rf(rtwdev, path, RR_RXKPLL, RFREG_MASK, 0x80013); + udelay(1); + + fail = _iqk_one_shot(rtwdev, phy_idx, path, ID_NBRXK); + rtw89_phy_write32_mask(rtwdev, R_IQKINF, BIT(16 + gp + path * 4), fail); + kfail |= fail; + rtw89_write_rf(rtwdev, path, RR_RXK, RR_RXK_SEL5G, 0x0); + + if (!kfail) + iqk_info->nb_rxcfir[path] = + rtw89_phy_read32_mask(rtwdev, R_RXIQC + (path << 8), MASKDWORD) | 0x2; + else + iqk_info->nb_rxcfir[path] = 0x40000002; + + return kfail; +} + +static void _iqk_rxclk_setting(struct rtw89_dev *rtwdev, u8 path) +{ + struct rtw89_iqk_info *iqk_info = &rtwdev->iqk; + + if (iqk_info->iqk_bw[path] == RTW89_CHANNEL_WIDTH_80) { + rtw89_phy_write32_mask(rtwdev, R_P0_NRBW, B_P0_NRBW_DBG, 0x1); + rtw89_phy_write32_mask(rtwdev, R_P1_DBGMOD, B_P1_DBGMOD_ON, 0x1); + udelay(1); + rtw89_phy_write32_mask(rtwdev, R_ANAPAR_PW15, B_ANAPAR_PW15, 0x0f); + udelay(1); + rtw89_phy_write32_mask(rtwdev, R_ANAPAR_PW15, B_ANAPAR_PW15, 0x03); + rtw89_phy_write32_mask(rtwdev, R_ANAPAR, B_ANAPAR_15, 0xa001); + udelay(1); + rtw89_phy_write32_mask(rtwdev, R_ANAPAR, B_ANAPAR_15, 0xa041); + rtw89_phy_write32_mask(rtwdev, R_P0_RXCK, B_P0_RXCK_VAL, 0x2); + rtw89_phy_write32_mask(rtwdev, R_P0_RXCK, B_P0_RXCK_ON, 0x1); + rtw89_phy_write32_mask(rtwdev, R_P1_RXCK, B_P1_RXCK_VAL, 0x2); + rtw89_phy_write32_mask(rtwdev, R_P1_RXCK, B_P1_RXCK_ON, 0x1); + rtw89_phy_write32_mask(rtwdev, R_UPD_CLK_ADC, B_UPD_CLK_ADC_ON, 0x1); + rtw89_phy_write32_mask(rtwdev, R_UPD_CLK_ADC, B_UPD_CLK_ADC_VAL, 0x1); + } else { + rtw89_phy_write32_mask(rtwdev, R_P0_NRBW, B_P0_NRBW_DBG, 0x1); + rtw89_phy_write32_mask(rtwdev, R_P1_DBGMOD, B_P1_DBGMOD_ON, 0x1); + udelay(1); + rtw89_phy_write32_mask(rtwdev, R_ANAPAR_PW15, B_ANAPAR_PW15, 0x0f); + udelay(1); + rtw89_phy_write32_mask(rtwdev, R_ANAPAR_PW15, B_ANAPAR_PW15, 0x03); + rtw89_phy_write32_mask(rtwdev, R_ANAPAR, B_ANAPAR_15, 0xa001); + udelay(1); + rtw89_phy_write32_mask(rtwdev, R_ANAPAR, B_ANAPAR_15, 0xa041); + rtw89_phy_write32_mask(rtwdev, R_P0_RXCK, B_P0_RXCK_VAL, 0x1); + rtw89_phy_write32_mask(rtwdev, R_P0_RXCK, B_P0_RXCK_ON, 0x1); + rtw89_phy_write32_mask(rtwdev, R_P1_RXCK, B_P1_RXCK_VAL, 0x1); + rtw89_phy_write32_mask(rtwdev, R_P1_RXCK, B_P1_RXCK_ON, 0x1); + rtw89_phy_write32_mask(rtwdev, R_UPD_CLK_ADC, B_UPD_CLK_ADC_ON, 0x1); + rtw89_phy_write32_mask(rtwdev, R_UPD_CLK_ADC, B_UPD_CLK_ADC_VAL, 0x0); + } +} + +static bool _txk_group_sel(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx, u8 path) +{ + struct rtw89_iqk_info *iqk_info = &rtwdev->iqk; + bool kfail = false; + bool fail; + u8 gp; + + for (gp = 0x0; gp < RTW8852B_RXK_GROUP_NR; gp++) { + switch (iqk_info->iqk_band[path]) { + case RTW89_BAND_2G: + rtw89_write_rf(rtwdev, path, RR_TXIG, RR_TXIG_GR0, + _g_power_range[gp]); + rtw89_write_rf(rtwdev, path, RR_TXIG, RR_TXIG_GR1, + _g_track_range[gp]); + rtw89_write_rf(rtwdev, path, RR_TXIG, RR_TXIG_TG, + _g_gain_bb[gp]); + rtw89_phy_write32_mask(rtwdev, R_KIP_IQP + (path << 8), + MASKDWORD, _g_itqt[gp]); + break; + case RTW89_BAND_5G: + rtw89_write_rf(rtwdev, path, RR_TXIG, RR_TXIG_GR0, + _a_power_range[gp]); + rtw89_write_rf(rtwdev, path, RR_TXIG, RR_TXIG_GR1, + _a_track_range[gp]); + rtw89_write_rf(rtwdev, path, RR_TXIG, RR_TXIG_TG, + _a_gain_bb[gp]); + rtw89_phy_write32_mask(rtwdev, R_KIP_IQP + (path << 8), + MASKDWORD, _a_itqt[gp]); + break; + default: + break; + } + + rtw89_phy_write32_mask(rtwdev, R_CFIR_LUT + (path << 8), + B_CFIR_LUT_SEL, 0x1); + rtw89_phy_write32_mask(rtwdev, R_CFIR_LUT + (path << 8), + B_CFIR_LUT_SET, 0x1); + rtw89_phy_write32_mask(rtwdev, R_CFIR_LUT + (path << 8), + B_CFIR_LUT_G2, 0x0); + rtw89_phy_write32_mask(rtwdev, R_CFIR_LUT + (path << 8), + B_CFIR_LUT_GP, gp); + rtw89_phy_write32_mask(rtwdev, R_NCTL_N1, B_NCTL_N1_CIP, 0x00); + fail = _iqk_one_shot(rtwdev, phy_idx, path, ID_TXK); + rtw89_phy_write32_mask(rtwdev, R_IQKINF, + BIT(8 + gp + path * 4), fail); + kfail |= fail; + } + + if (kfail) { + iqk_info->nb_txcfir[path] = 0x40000002; + rtw89_phy_write32_mask(rtwdev, R_IQK_RES + (path << 8), + B_IQK_RES_TXCFIR, 0x0); + iqk_info->is_wb_txiqk[path] = false; + } else { + iqk_info->nb_txcfir[path] = 0x40000000; + rtw89_phy_write32_mask(rtwdev, R_IQK_RES + (path << 8), + B_IQK_RES_TXCFIR, 0x5); + iqk_info->is_wb_txiqk[path] = true; + } + + return kfail; +} + +static bool _iqk_nbtxk(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx, u8 path) +{ + struct rtw89_iqk_info *iqk_info = &rtwdev->iqk; + bool kfail; + u8 gp = 0x3; + + switch (iqk_info->iqk_band[path]) { + case RTW89_BAND_2G: + rtw89_write_rf(rtwdev, path, RR_TXIG, RR_TXIG_GR0, + _g_power_range[gp]); + rtw89_write_rf(rtwdev, path, RR_TXIG, RR_TXIG_GR1, + _g_track_range[gp]); + rtw89_write_rf(rtwdev, path, RR_TXIG, RR_TXIG_TG, + _g_gain_bb[gp]); + rtw89_phy_write32_mask(rtwdev, R_KIP_IQP + (path << 8), + MASKDWORD, _g_itqt[gp]); + break; + case RTW89_BAND_5G: + rtw89_write_rf(rtwdev, path, RR_TXIG, RR_TXIG_GR0, + _a_power_range[gp]); + rtw89_write_rf(rtwdev, path, RR_TXIG, RR_TXIG_GR1, + _a_track_range[gp]); + rtw89_write_rf(rtwdev, path, RR_TXIG, RR_TXIG_TG, + _a_gain_bb[gp]); + rtw89_phy_write32_mask(rtwdev, R_KIP_IQP + (path << 8), + MASKDWORD, _a_itqt[gp]); + break; + default: + break; + } + + rtw89_phy_write32_mask(rtwdev, R_CFIR_LUT + (path << 8), B_CFIR_LUT_SEL, 0x1); + rtw89_phy_write32_mask(rtwdev, R_CFIR_LUT + (path << 8), B_CFIR_LUT_SET, 0x1); + rtw89_phy_write32_mask(rtwdev, R_CFIR_LUT + (path << 8), B_CFIR_LUT_G2, 0x0); + rtw89_phy_write32_mask(rtwdev, R_CFIR_LUT + (path << 8), B_CFIR_LUT_GP, gp); + rtw89_phy_write32_mask(rtwdev, R_NCTL_N1, B_NCTL_N1_CIP, 0x00); + kfail = _iqk_one_shot(rtwdev, phy_idx, path, ID_NBTXK); + + if (!kfail) + iqk_info->nb_txcfir[path] = + rtw89_phy_read32_mask(rtwdev, R_TXIQC + (path << 8), + MASKDWORD) | 0x2; + else + iqk_info->nb_txcfir[path] = 0x40000002; + + return kfail; +} + +static void _lok_res_table(struct rtw89_dev *rtwdev, u8 path, u8 ibias) +{ + struct rtw89_iqk_info *iqk_info = &rtwdev->iqk; + + rtw89_debug(rtwdev, RTW89_DBG_RFK, "[IQK]===>%s\n", __func__); + rtw89_debug(rtwdev, RTW89_DBG_RFK, "[IQK]S%x, ibias = %x\n", path, ibias); + + rtw89_write_rf(rtwdev, path, RR_LUTWE, RFREG_MASK, 0x2); + if (iqk_info->iqk_band[path] == RTW89_BAND_2G) + rtw89_write_rf(rtwdev, path, RR_LUTWA, RFREG_MASK, 0x0); + else + rtw89_write_rf(rtwdev, path, RR_LUTWA, RFREG_MASK, 0x1); + rtw89_write_rf(rtwdev, path, RR_LUTWD0, RFREG_MASK, ibias); + rtw89_write_rf(rtwdev, path, RR_LUTWE, RFREG_MASK, 0x0); + rtw89_write_rf(rtwdev, path, RR_TXVBUF, RR_TXVBUF_DACEN, 0x1); + + rtw89_debug(rtwdev, RTW89_DBG_RFK, "[IQK]S%x, 0x7c = %x\n", path, + rtw89_read_rf(rtwdev, path, RR_TXVBUF, RFREG_MASK)); +} + +static bool _lok_finetune_check(struct rtw89_dev *rtwdev, u8 path) +{ + struct rtw89_iqk_info *iqk_info = &rtwdev->iqk; + bool is_fail1, is_fail2; + u32 vbuff_i; + u32 vbuff_q; + u32 core_i; + u32 core_q; + u32 tmp; + u8 ch; + + tmp = rtw89_read_rf(rtwdev, path, RR_TXMO, RFREG_MASK); + core_i = FIELD_GET(RR_TXMO_COI, tmp); + core_q = FIELD_GET(RR_TXMO_COQ, tmp); + ch = (iqk_info->iqk_times / 2) % RTW89_IQK_CHS_NR; + + if (core_i < 0x2 || core_i > 0x1d || core_q < 0x2 || core_q > 0x1d) + is_fail1 = true; + else + is_fail1 = false; + + iqk_info->lok_idac[ch][path] = tmp; + + tmp = rtw89_read_rf(rtwdev, path, RR_LOKVB, RFREG_MASK); + vbuff_i = FIELD_GET(RR_LOKVB_COI, tmp); + vbuff_q = FIELD_GET(RR_LOKVB_COQ, tmp); + + if (vbuff_i < 0x2 || vbuff_i > 0x3d || vbuff_q < 0x2 || vbuff_q > 0x3d) + is_fail2 = true; + else + is_fail2 = false; + + iqk_info->lok_vbuf[ch][path] = tmp; + + rtw89_debug(rtwdev, RTW89_DBG_RFK, + "[IQK]S%x, lok_idac[%x][%x] = 0x%x\n", path, ch, path, + iqk_info->lok_idac[ch][path]); + rtw89_debug(rtwdev, RTW89_DBG_RFK, + "[IQK]S%x, lok_vbuf[%x][%x] = 0x%x\n", path, ch, path, + iqk_info->lok_vbuf[ch][path]); + + return is_fail1 | is_fail2; +} + +static bool _iqk_lok(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx, u8 path) +{ + struct rtw89_iqk_info *iqk_info = &rtwdev->iqk; + bool tmp; + + rtw89_phy_write32_mask(rtwdev, R_IQK_DIF4, B_IQK_DIF4_TXT, 0x021); + + switch (iqk_info->iqk_band[path]) { + case RTW89_BAND_2G: + rtw89_write_rf(rtwdev, path, RR_TXIG, RR_TXIG_GR0, 0x0); + rtw89_write_rf(rtwdev, path, RR_TXIG, RR_TXIG_GR1, 0x6); + break; + case RTW89_BAND_5G: + rtw89_write_rf(rtwdev, path, RR_TXIG, RR_TXIG_GR0, 0x0); + rtw89_write_rf(rtwdev, path, RR_TXIG, RR_TXIG_GR1, 0x4); + break; + default: + break; + } + + switch (iqk_info->iqk_band[path]) { + case RTW89_BAND_2G: + rtw89_write_rf(rtwdev, path, RR_TXIG, RR_TXIG_TG, 0x0); + break; + case RTW89_BAND_5G: + rtw89_write_rf(rtwdev, path, RR_TXIG, RR_TXIG_TG, 0x0); + break; + default: + break; + } + + rtw89_phy_write32_mask(rtwdev, R_KIP_IQP + (path << 8), MASKDWORD, 0x9); + tmp = _iqk_one_shot(rtwdev, phy_idx, path, ID_FLOK_COARSE); + iqk_info->lok_cor_fail[0][path] = tmp; + + switch (iqk_info->iqk_band[path]) { + case RTW89_BAND_2G: + rtw89_write_rf(rtwdev, path, RR_TXIG, RR_TXIG_TG, 0x12); + break; + case RTW89_BAND_5G: + rtw89_write_rf(rtwdev, path, RR_TXIG, RR_TXIG_TG, 0x12); + break; + default: + break; + } + + rtw89_phy_write32_mask(rtwdev, R_KIP_IQP + (path << 8), MASKDWORD, 0x24); + tmp = _iqk_one_shot(rtwdev, phy_idx, path, ID_FLOK_VBUFFER); + + switch (iqk_info->iqk_band[path]) { + case RTW89_BAND_2G: + rtw89_write_rf(rtwdev, path, RR_TXIG, RR_TXIG_TG, 0x0); + break; + case RTW89_BAND_5G: + rtw89_write_rf(rtwdev, path, RR_TXIG, RR_TXIG_TG, 0x0); + break; + default: + break; + } + + rtw89_phy_write32_mask(rtwdev, R_KIP_IQP + (path << 8), MASKDWORD, 0x9); + rtw89_phy_write32_mask(rtwdev, R_IQK_DIF4, B_IQK_DIF4_TXT, 0x021); + tmp = _iqk_one_shot(rtwdev, phy_idx, path, ID_FLOK_FINE); + iqk_info->lok_fin_fail[0][path] = tmp; + + switch (iqk_info->iqk_band[path]) { + case RTW89_BAND_2G: + rtw89_write_rf(rtwdev, path, RR_TXIG, RR_TXIG_TG, 0x12); + break; + case RTW89_BAND_5G: + rtw89_write_rf(rtwdev, path, RR_TXIG, RR_TXIG_TG, 0x12); + break; + default: + break; + } + + rtw89_phy_write32_mask(rtwdev, R_KIP_IQP + (path << 8), MASKDWORD, 0x24); + _iqk_one_shot(rtwdev, phy_idx, path, ID_FLOK_VBUFFER); + + return _lok_finetune_check(rtwdev, path); +} + +static void _iqk_txk_setting(struct rtw89_dev *rtwdev, u8 path) +{ + struct rtw89_iqk_info *iqk_info = &rtwdev->iqk; + + switch (iqk_info->iqk_band[path]) { + case RTW89_BAND_2G: + rtw89_write_rf(rtwdev, path, RR_XALNA2, RR_XALNA2_SW2, 0x00); + rtw89_write_rf(rtwdev, path, RR_TXG1, RR_TXG1_ATT2, 0x0); + rtw89_write_rf(rtwdev, path, RR_TXG1, RR_TXG1_ATT1, 0x0); + rtw89_write_rf(rtwdev, path, RR_TXG2, RR_TXG2_ATT0, 0x1); + rtw89_write_rf(rtwdev, path, RR_TXGA, RR_TXGA_LOK_EXT, 0x0); + rtw89_write_rf(rtwdev, path, RR_LUTWE, RR_LUTWE_LOK, 0x1); + rtw89_write_rf(rtwdev, path, RR_LUTWA, RR_LUTWA_M1, 0x00); + rtw89_write_rf(rtwdev, path, RR_MOD, RR_MOD_IQK, 0x403e); + udelay(1); + break; + case RTW89_BAND_5G: + rtw89_write_rf(rtwdev, path, RR_XGLNA2, RR_XGLNA2_SW, 0x00); + rtw89_write_rf(rtwdev, path, RR_BIASA, RR_BIASA_A, 0x1); + rtw89_write_rf(rtwdev, path, RR_TXGA, RR_TXGA_LOK_EXT, 0x0); + rtw89_write_rf(rtwdev, path, RR_LUTWE, RR_LUTWE_LOK, 0x1); + rtw89_write_rf(rtwdev, path, RR_LUTWA, RR_LUTWA_M1, 0x80); + rtw89_write_rf(rtwdev, path, RR_MOD, RR_MOD_IQK, 0x403e); + udelay(1); + break; + default: + break; + } +} + +static void _iqk_txclk_setting(struct rtw89_dev *rtwdev, u8 path) +{ + rtw89_phy_write32_mask(rtwdev, R_P0_NRBW, B_P0_NRBW_DBG, 0x1); + rtw89_phy_write32_mask(rtwdev, R_P1_DBGMOD, B_P1_DBGMOD_ON, 0x1); + udelay(1); + rtw89_phy_write32_mask(rtwdev, R_ANAPAR_PW15, B_ANAPAR_PW15, 0x1f); + udelay(1); + rtw89_phy_write32_mask(rtwdev, R_ANAPAR_PW15, B_ANAPAR_PW15, 0x13); + rtw89_phy_write32_mask(rtwdev, R_ANAPAR, B_ANAPAR_15, 0x0001); + udelay(1); + rtw89_phy_write32_mask(rtwdev, R_ANAPAR, B_ANAPAR_15, 0x0041); +} + +static void _iqk_info_iqk(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx, u8 path) +{ + struct rtw89_iqk_info *iqk_info = &rtwdev->iqk; + u32 tmp; + bool flag; + + iqk_info->thermal[path] = + ewma_thermal_read(&rtwdev->phystat.avg_thermal[path]); + iqk_info->thermal_rek_en = false; + + flag = iqk_info->lok_cor_fail[0][path]; + rtw89_phy_write32_mask(rtwdev, R_IQKINF, B_IQKINF_FCOR << (path * 4), flag); + flag = iqk_info->lok_fin_fail[0][path]; + rtw89_phy_write32_mask(rtwdev, R_IQKINF, B_IQKINF_FFIN << (path * 4), flag); + flag = iqk_info->iqk_tx_fail[0][path]; + rtw89_phy_write32_mask(rtwdev, R_IQKINF, B_IQKINF_FTX << (path * 4), flag); + flag = iqk_info->iqk_rx_fail[0][path]; + rtw89_phy_write32_mask(rtwdev, R_IQKINF, B_IQKINF_F_RX << (path * 4), flag); + + tmp = rtw89_phy_read32_mask(rtwdev, R_IQK_RES + (path << 8), MASKDWORD); + iqk_info->bp_iqkenable[path] = tmp; + tmp = rtw89_phy_read32_mask(rtwdev, R_TXIQC + (path << 8), MASKDWORD); + iqk_info->bp_txkresult[path] = tmp; + tmp = rtw89_phy_read32_mask(rtwdev, R_RXIQC + (path << 8), MASKDWORD); + iqk_info->bp_rxkresult[path] = tmp; + + rtw89_phy_write32_mask(rtwdev, R_IQKINF2, B_IQKINF2_KCNT, iqk_info->iqk_times); + + tmp = rtw89_phy_read32_mask(rtwdev, R_IQKINF, B_IQKINF_FAIL << (path * 4)); + if (tmp) + iqk_info->iqk_fail_cnt++; + rtw89_phy_write32_mask(rtwdev, R_IQKINF2, B_IQKINF2_FCNT << (path * 4), + iqk_info->iqk_fail_cnt); +} + +static void _iqk_by_path(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx, u8 path) +{ + struct rtw89_iqk_info *iqk_info = &rtwdev->iqk; + bool lok_is_fail = false; + const int try = 3; + u8 ibias = 0x1; + u8 i; + + _iqk_txclk_setting(rtwdev, path); + + /* LOK */ + for (i = 0; i < try; i++) { + _lok_res_table(rtwdev, path, ibias++); + _iqk_txk_setting(rtwdev, path); + lok_is_fail = _iqk_lok(rtwdev, phy_idx, path); + if (!lok_is_fail) + break; + } + + if (lok_is_fail) + rtw89_debug(rtwdev, RTW89_DBG_RFK, "[IQK] LOK (%d) fail\n", path); + + /* TXK */ + if (iqk_info->is_nbiqk) + iqk_info->iqk_tx_fail[0][path] = _iqk_nbtxk(rtwdev, phy_idx, path); + else + iqk_info->iqk_tx_fail[0][path] = _txk_group_sel(rtwdev, phy_idx, path); + + /* RX */ + _iqk_rxclk_setting(rtwdev, path); + _iqk_rxk_setting(rtwdev, path); + if (iqk_info->is_nbiqk) + iqk_info->iqk_rx_fail[0][path] = _iqk_nbrxk(rtwdev, phy_idx, path); + else + iqk_info->iqk_rx_fail[0][path] = _rxk_group_sel(rtwdev, phy_idx, path); + + _iqk_info_iqk(rtwdev, phy_idx, path); +} + +static void _iqk_get_ch_info(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy, u8 path) +{ + const struct rtw89_chan *chan = rtw89_chan_get(rtwdev, RTW89_SUB_ENTITY_0); + struct rtw89_iqk_info *iqk_info = &rtwdev->iqk; + u32 reg_rf18; + u32 reg_35c; + u8 idx; + u8 get_empty_table = false; + + for (idx = 0; idx < RTW89_IQK_CHS_NR; idx++) { + if (iqk_info->iqk_mcc_ch[idx][path] == 0) { + get_empty_table = true; + break; + } + } + rtw89_debug(rtwdev, RTW89_DBG_RFK, "[IQK] (1)idx = %x\n", idx); + + if (!get_empty_table) { + idx = iqk_info->iqk_table_idx[path] + 1; + if (idx > 1) + idx = 0; + } + rtw89_debug(rtwdev, RTW89_DBG_RFK, "[IQK] (2)idx = %x\n", idx); + + reg_rf18 = rtw89_read_rf(rtwdev, path, RR_CFGCH, RFREG_MASK); + reg_35c = rtw89_phy_read32_mask(rtwdev, R_CIRST, B_CIRST_SYN); + + iqk_info->iqk_band[path] = chan->band_type; + iqk_info->iqk_bw[path] = chan->band_width; + iqk_info->iqk_ch[path] = chan->channel; + iqk_info->iqk_mcc_ch[idx][path] = chan->channel; + iqk_info->iqk_table_idx[path] = idx; + + rtw89_debug(rtwdev, RTW89_DBG_RFK, "[IQK]S%x, 0x18= 0x%x, idx = %x\n", + path, reg_rf18, idx); + rtw89_debug(rtwdev, RTW89_DBG_RFK, "[IQK]S%x, 0x18= 0x%x\n", + path, reg_rf18); + rtw89_debug(rtwdev, RTW89_DBG_RFK, "[IQK]times = 0x%x, ch =%x\n", + iqk_info->iqk_times, idx); + rtw89_debug(rtwdev, RTW89_DBG_RFK, "[IQK]iqk_mcc_ch[%x][%x] = 0x%x\n", + idx, path, iqk_info->iqk_mcc_ch[idx][path]); + + if (reg_35c == 0x01) + iqk_info->syn1to2 = 0x1; + else + iqk_info->syn1to2 = 0x0; + + rtw89_debug(rtwdev, RTW89_DBG_RFK, + "[IQK]S%x, iqk_info->syn1to2= 0x%x\n", path, + iqk_info->syn1to2); + + rtw89_phy_write32_mask(rtwdev, R_IQKINF, B_IQKINF_VER, RTW8852B_IQK_VER); + /* 2GHz/5GHz/6GHz = 0/1/2 */ + rtw89_phy_write32_mask(rtwdev, R_IQKCH, B_IQKCH_BAND << (path * 16), + iqk_info->iqk_band[path]); + /* 20/40/80 = 0/1/2 */ + rtw89_phy_write32_mask(rtwdev, R_IQKCH, B_IQKCH_BW << (path * 16), + iqk_info->iqk_bw[path]); + rtw89_phy_write32_mask(rtwdev, R_IQKCH, B_IQKCH_CH << (path * 16), + iqk_info->iqk_ch[path]); +} + +static void _iqk_start_iqk(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx, u8 path) +{ + _iqk_by_path(rtwdev, phy_idx, path); +} + +static void _iqk_restore(struct rtw89_dev *rtwdev, u8 path) +{ + struct rtw89_iqk_info *iqk_info = &rtwdev->iqk; + bool fail; + + rtw89_phy_write32_mask(rtwdev, R_TXIQC + (path << 8), MASKDWORD, + iqk_info->nb_txcfir[path]); + rtw89_phy_write32_mask(rtwdev, R_RXIQC + (path << 8), MASKDWORD, + iqk_info->nb_rxcfir[path]); + rtw89_phy_write32_mask(rtwdev, R_NCTL_CFG, MASKDWORD, + 0x00000e19 + (path << 4)); + fail = _iqk_check_cal(rtwdev, path); + + rtw89_debug(rtwdev, RTW89_DBG_RFK, "%s result =%x\n", __func__, fail); + + rtw89_phy_write32_mask(rtwdev, R_NCTL_N1, B_NCTL_N1_CIP, 0x00); + rtw89_phy_write32_mask(rtwdev, R_NCTL_RPT, MASKDWORD, 0x00000000); + rtw89_phy_write32_mask(rtwdev, R_KIP_SYSCFG, MASKDWORD, 0x80000000); + rtw89_phy_write32_mask(rtwdev, R_CFIR_SYS, B_IQK_RES_K, 0x0); + rtw89_phy_write32_mask(rtwdev, R_IQRSN, B_IQRSN_K1, 0x0); + rtw89_phy_write32_mask(rtwdev, R_IQRSN, B_IQRSN_K2, 0x0); + rtw89_write_rf(rtwdev, path, RR_LUTWE, RR_LUTWE_LOK, 0x0); + rtw89_write_rf(rtwdev, path, RR_LUTWE, RR_LUTWE_LOK, 0x0); + rtw89_write_rf(rtwdev, path, RR_MOD, RR_MOD_MASK, 0x3); + rtw89_write_rf(rtwdev, path, RR_RSV1, RR_RSV1_RST, 0x1); + rtw89_write_rf(rtwdev, path, RR_BBDC, RR_BBDC_SEL, 0x1); +} + +static void _iqk_afebb_restore(struct rtw89_dev *rtwdev, + enum rtw89_phy_idx phy_idx, u8 path) +{ + const struct rtw89_reg3_def *def; + int size; + u8 kpath; + int i; + + rtw89_debug(rtwdev, RTW89_DBG_RFK, "===> %s\n", __func__); + + kpath = _kpath(rtwdev, phy_idx); + + switch (kpath) { + case RF_A: + case RF_B: + return; + default: + size = ARRAY_SIZE(rtw8852b_restore_nondbcc_path01); + def = rtw8852b_restore_nondbcc_path01; + break; + } + + for (i = 0; i < size; i++, def++) + rtw89_phy_write32_mask(rtwdev, def->addr, def->mask, def->data); +} + +static void _iqk_preset(struct rtw89_dev *rtwdev, u8 path) +{ + struct rtw89_iqk_info *iqk_info = &rtwdev->iqk; + u8 idx; + + idx = iqk_info->iqk_table_idx[path]; + rtw89_debug(rtwdev, RTW89_DBG_RFK, "[IQK] (3)idx = %x\n", idx); + + rtw89_phy_write32_mask(rtwdev, R_COEF_SEL + (path << 8), B_COEF_SEL_IQC, idx); + rtw89_phy_write32_mask(rtwdev, R_CFIR_LUT + (path << 8), B_CFIR_LUT_G3, idx); + + rtw89_write_rf(rtwdev, path, RR_RSV1, RR_RSV1_RST, 0x0); + rtw89_write_rf(rtwdev, path, RR_BBDC, RR_BBDC_SEL, 0x0); + rtw89_phy_write32_mask(rtwdev, R_NCTL_RPT, MASKDWORD, 0x00000080); + rtw89_phy_write32_mask(rtwdev, R_KIP_SYSCFG, MASKDWORD, 0x81ff010a); + + rtw89_debug(rtwdev, RTW89_DBG_RFK, "[IQK](1)S%x, 0x8%x54 = 0x%x\n", path, 1 << path, + rtw89_phy_read32_mask(rtwdev, R_CFIR_LUT + (path << 8), MASKDWORD)); + rtw89_debug(rtwdev, RTW89_DBG_RFK, "[IQK](1)S%x, 0x8%x04 = 0x%x\n", path, 1 << path, + rtw89_phy_read32_mask(rtwdev, R_COEF_SEL + (path << 8), MASKDWORD)); +} + +static void _iqk_macbb_setting(struct rtw89_dev *rtwdev, + enum rtw89_phy_idx phy_idx, u8 path) +{ + const struct rtw89_reg3_def *def; + int size; + u8 kpath; + int i; + + kpath = _kpath(rtwdev, phy_idx); + + switch (kpath) { + case RF_A: + case RF_B: + return; + default: + size = ARRAY_SIZE(rtw8852b_set_nondbcc_path01); + def = rtw8852b_set_nondbcc_path01; + break; + } + + for (i = 0; i < size; i++, def++) + rtw89_phy_write32_mask(rtwdev, def->addr, def->mask, def->data); +} + +static void _iqk_init(struct rtw89_dev *rtwdev) +{ + struct rtw89_iqk_info *iqk_info = &rtwdev->iqk; + u8 idx, path; + + rtw89_phy_write32_mask(rtwdev, R_IQKINF, MASKDWORD, 0x0); + if (iqk_info->is_iqk_init) + return; + + rtw89_debug(rtwdev, RTW89_DBG_RFK, "[IQK]===>%s\n", __func__); + iqk_info->is_iqk_init = true; + iqk_info->is_nbiqk = false; + iqk_info->iqk_fft_en = false; + iqk_info->iqk_sram_en = false; + iqk_info->iqk_cfir_en = false; + iqk_info->iqk_xym_en = false; + iqk_info->thermal_rek_en = false; + iqk_info->iqk_times = 0x0; + + for (idx = 0; idx < RTW89_IQK_CHS_NR; idx++) { + iqk_info->iqk_channel[idx] = 0x0; + for (path = 0; path < RTW8852B_IQK_SS; path++) { + iqk_info->lok_cor_fail[idx][path] = false; + iqk_info->lok_fin_fail[idx][path] = false; + iqk_info->iqk_tx_fail[idx][path] = false; + iqk_info->iqk_rx_fail[idx][path] = false; + iqk_info->iqk_mcc_ch[idx][path] = 0x0; + iqk_info->iqk_table_idx[path] = 0x0; + } + } +} + static void _wait_rx_mode(struct rtw89_dev *rtwdev, u8 kpath) { u32 rf_mode; @@ -548,6 +1526,58 @@ static void _wait_rx_mode(struct rtw89_dev *rtwdev, u8 kpath) } } +static void _doiqk(struct rtw89_dev *rtwdev, bool force, + enum rtw89_phy_idx phy_idx, u8 path) +{ + struct rtw89_iqk_info *iqk_info = &rtwdev->iqk; + u32 backup_bb_val[BACKUP_BB_REGS_NR]; + u32 backup_rf_val[RTW8852B_IQK_SS][BACKUP_RF_REGS_NR]; + u8 phy_map = rtw89_btc_phymap(rtwdev, phy_idx, RF_AB); + + rtw89_btc_ntfy_wl_rfk(rtwdev, phy_map, BTC_WRFKT_IQK, BTC_WRFK_ONESHOT_START); + + rtw89_debug(rtwdev, RTW89_DBG_RFK, + "[IQK]==========IQK strat!!!!!==========\n"); + iqk_info->iqk_times++; + iqk_info->kcount = 0; + iqk_info->version = RTW8852B_IQK_VER; + + rtw89_debug(rtwdev, RTW89_DBG_RFK, "[IQK]Test Ver 0x%x\n", iqk_info->version); + _iqk_get_ch_info(rtwdev, phy_idx, path); + + _rfk_backup_bb_reg(rtwdev, &backup_bb_val[0]); + _rfk_backup_rf_reg(rtwdev, &backup_rf_val[path][0], path); + _iqk_macbb_setting(rtwdev, phy_idx, path); + _iqk_preset(rtwdev, path); + _iqk_start_iqk(rtwdev, phy_idx, path); + _iqk_restore(rtwdev, path); + _iqk_afebb_restore(rtwdev, phy_idx, path); + _rfk_restore_bb_reg(rtwdev, &backup_bb_val[0]); + _rfk_restore_rf_reg(rtwdev, &backup_rf_val[path][0], path); + + rtw89_btc_ntfy_wl_rfk(rtwdev, phy_map, BTC_WRFKT_IQK, BTC_WRFK_ONESHOT_STOP); +} + +static void _iqk(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx, bool force) +{ + u8 kpath = _kpath(rtwdev, phy_idx); + + switch (kpath) { + case RF_A: + _doiqk(rtwdev, force, phy_idx, RF_PATH_A); + break; + case RF_B: + _doiqk(rtwdev, force, phy_idx, RF_PATH_B); + break; + case RF_AB: + _doiqk(rtwdev, force, phy_idx, RF_PATH_A); + _doiqk(rtwdev, force, phy_idx, RF_PATH_B); + break; + default: + break; + } +} + void rtw8852b_rck(struct rtw89_dev *rtwdev) { u8 path; @@ -565,6 +1595,22 @@ void rtw8852b_dack(struct rtw89_dev *rtwdev) rtw89_btc_ntfy_wl_rfk(rtwdev, phy_map, BTC_WRFKT_DACK, BTC_WRFK_STOP); } +void rtw8852b_iqk(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx) +{ + u8 phy_map = rtw89_btc_phymap(rtwdev, phy_idx, 0); + u32 tx_en; + + rtw89_btc_ntfy_wl_rfk(rtwdev, phy_map, BTC_WRFKT_IQK, BTC_WRFK_START); + rtw89_chip_stop_sch_tx(rtwdev, phy_idx, &tx_en, RTW89_SCH_TX_SEL_ALL); + _wait_rx_mode(rtwdev, _kpath(rtwdev, phy_idx)); + + _iqk_init(rtwdev); + _iqk(rtwdev, phy_idx, false); + + rtw89_chip_resume_sch_tx(rtwdev, phy_idx, tx_en); + rtw89_btc_ntfy_wl_rfk(rtwdev, phy_map, BTC_WRFKT_IQK, BTC_WRFK_STOP); +} + void rtw8852b_rx_dck(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx) { u8 phy_map = rtw89_btc_phymap(rtwdev, phy_idx, 0); diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852b_rfk.h b/drivers/net/wireless/realtek/rtw89/rtw8852b_rfk.h index 24e492484d27..81eb9bddb6d7 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852b_rfk.h +++ b/drivers/net/wireless/realtek/rtw89/rtw8852b_rfk.h @@ -9,6 +9,7 @@ void rtw8852b_rck(struct rtw89_dev *rtwdev); void rtw8852b_dack(struct rtw89_dev *rtwdev); +void rtw8852b_iqk(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx); void rtw8852b_rx_dck(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx); void rtw8852b_set_channel_rf(struct rtw89_dev *rtwdev, const struct rtw89_chan *chan, From 7f18a70d7b4d2a0e54ce542c222aeffc464ed9f3 Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Wed, 12 Oct 2022 16:32:34 +0800 Subject: [PATCH 0171/2002] wifi: rtw89: 8852b: rfk: add TSSI TSSI is transmitter signal strength indication, which is a close-loop hardware circuit to feedback actual transmitting power as a reference for next transmission. When we setup channel to connect an AP, it does full calibration. When switching bands or channels, it needs to reset hardware status to prevent use wrong feedback of previous transmission. To do TX power compensation reflecting current temperature, it loads tables of compensation values into registers according to channel and band group. Signed-off-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20221012083234.20224-6-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/core.h | 15 + drivers/net/wireless/realtek/rtw89/reg.h | 43 + .../net/wireless/realtek/rtw89/rtw8852b_rfk.c | 1174 +++++++++++++++++ .../net/wireless/realtek/rtw89/rtw8852b_rfk.h | 4 + 4 files changed, 1236 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index 0a0343608ba6..856d9712f0ae 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -3164,6 +3164,14 @@ struct rtw89_cfo_tracking_info { u8 lock_cnt; }; +enum rtw89_tssi_alimk_band { + TSSI_ALIMK_2G = 0, + TSSI_ALIMK_5GL, + TSSI_ALIMK_5GM, + TSSI_ALIMK_5GH, + TSSI_ALIMK_MAX +}; + /* 2GL, 2GH, 5GL1, 5GH1, 5GM1, 5GM2, 5GH1, 5GH2 */ #define TSSI_TRIM_CH_GROUP_NUM 8 #define TSSI_TRIM_CH_GROUP_NUM_6G 16 @@ -3174,6 +3182,8 @@ struct rtw89_cfo_tracking_info { #define TSSI_MCS_6G_CH_GROUP_NUM 32 #define TSSI_MCS_CH_GROUP_NUM \ (TSSI_MCS_2G_CH_GROUP_NUM + TSSI_MCS_5G_CH_GROUP_NUM) +#define TSSI_MAX_CH_NUM 67 +#define TSSI_ALIMK_VALUE_NUM 8 struct rtw89_tssi_info { u8 thermal[RF_PATH_MAX]; @@ -3186,6 +3196,11 @@ struct rtw89_tssi_info { bool tssi_tracking_check[RF_PATH_MAX]; u8 default_txagc_offset[RF_PATH_MAX]; u32 base_thermal[RF_PATH_MAX]; + bool check_backup_aligmk[RF_PATH_MAX][TSSI_MAX_CH_NUM]; + u32 alignment_backup_by_ch[RF_PATH_MAX][TSSI_MAX_CH_NUM][TSSI_ALIMK_VALUE_NUM]; + u32 alignment_value[RF_PATH_MAX][TSSI_ALIMK_MAX][TSSI_ALIMK_VALUE_NUM]; + bool alignment_done[RF_PATH_MAX][TSSI_ALIMK_MAX]; + u32 tssi_alimk_time; }; struct rtw89_power_trim_info { diff --git a/drivers/net/wireless/realtek/rtw89/reg.h b/drivers/net/wireless/realtek/rtw89/reg.h index 6dc078969d58..4393b409cbe6 100644 --- a/drivers/net/wireless/realtek/rtw89/reg.h +++ b/drivers/net/wireless/realtek/rtw89/reg.h @@ -3389,6 +3389,8 @@ #define RR_TXGA_TRK_EN BIT(7) #define RR_TXGA_LOK_EXT GENMASK(4, 0) #define RR_TXGA_LOK_EN BIT(0) +#define RR_TXGA_V1 0x10055 +#define RR_TXGA_V1_TRK_EN BIT(7) #define RR_GAINTX 0x56 #define RR_GAINTX_ALL GENMASK(15, 0) #define RR_GAINTX_PAD GENMASK(9, 5) @@ -3730,6 +3732,9 @@ #define B_TXAGC_TP GENMASK(2, 0) #define R_TSSI_THER 0x1C10 #define B_TSSI_THER GENMASK(29, 24) +#define R_TSSI_CWRPT 0x1C18 +#define B_TSSI_CWRPT_RDY BIT(16) +#define B_TSSI_CWRPT GENMASK(8, 0) #define R_TXAGC_BTP 0x1CA0 #define B_TXAGC_BTP GENMASK(31, 24) #define R_TXAGC_BB 0x1C60 @@ -4092,6 +4097,20 @@ #define B_CFO_COMP_VALID_BIT BIT(29) #define B_CFO_COMP_WEIGHT_MSK GENMASK(27, 24) #define B_CFO_COMP_VAL_MSK GENMASK(11, 0) +#define R_TSSI_PA_K1 0x5600 +#define R_TSSI_PA_K2 0x5604 +#define R_P0_TSSI_ALIM1 0x5630 +#define B_P0_TSSI_ALIM1 GENMASK(29, 0) +#define B_P0_TSSI_ALIM11 GENMASK(29, 20) +#define B_P0_TSSI_ALIM12 GENMASK(19, 10) +#define B_P0_TSSI_ALIM13 GENMASK(9, 0) +#define R_P0_TSSI_ALIM3 0x5634 +#define B_P0_TSSI_ALIM31 GENMASK(9, 0) +#define R_TSSI_PA_K5 0x5638 +#define R_P0_TSSI_ALIM2 0x563c +#define B_P0_TSSI_ALIM2 GENMASK(29, 0) +#define R_P0_TSSI_ALIM4 0x5640 +#define R_TSSI_PA_K8 0x5644 #define R_UPD_CLK 0x5670 #define B_DAC_VAL BIT(31) #define B_ACK_VAL GENMASK(30, 29) @@ -4106,6 +4125,8 @@ #define B_DPD_TSSI_CW GENMASK(26, 18) #define B_DPD_PWR_CW GENMASK(17, 9) #define B_DPD_REF GENMASK(8, 0) +#define R_P0_TSSIC 0x5814 +#define B_P0_TSSIC_BYPASS BIT(11) #define R_DPD_OFT_ADDR 0x5804 #define B_DPD_OFT_ADDR GENMASK(31, 27) #define R_TXPWRB_H 0x580c @@ -4114,11 +4135,15 @@ #define B_P0_TMETER GENMASK(15, 10) #define B_P0_TMETER_DIS BIT(16) #define B_P0_TMETER_TRK BIT(24) +#define R_P1_TSSIC 0x7814 +#define B_P1_TSSIC_BYPASS BIT(11) #define R_P0_TSSI_TRK 0x5818 #define B_P0_TSSI_TRK_EN BIT(30) +#define B_P0_TSSI_RFC GENMASK(28, 27) #define B_P0_TSSI_OFT_EN BIT(28) #define B_P0_TSSI_OFT GENMASK(7, 0) #define R_P0_TSSI_AVG 0x5820 +#define B_P0_TSSI_EN BIT(31) #define B_P0_TSSI_AVG GENMASK(15, 12) #define R_P0_RFCTM 0x5864 #define B_P0_RFCTM_EN BIT(29) @@ -4141,7 +4166,9 @@ #define B_P0_TXPW_RSTB_MANON BIT(30) #define B_P0_TXPW_RSTB_TSSI BIT(31) #define R_P0_TSSI_MV_AVG 0x58E4 +#define B_P0_TSSI_MV_MIX GENMASK(19, 11) #define B_P0_TSSI_MV_AVG GENMASK(13, 11) +#define B_P0_TSSI_MV_CLR BIT(14) #define R_TXGAIN_SCALE 0x58F0 #define B_TXGAIN_SCALE_EN BIT(19) #define B_TXGAIN_SCALE_OFT GENMASK(31, 24) @@ -4166,25 +4193,41 @@ #define B_S0_DACKQ8_K GENMASK(15, 8) #define R_RPL_BIAS_COMP1 0x6DF0 #define B_RPL_BIAS_COMP1_MASK GENMASK(7, 0) +#define R_P1_TSSI_ALIM1 0x7630 +#define B_P1_TSSI_ALIM1 GENMASK(29, 0) +#define B_P1_TSSI_ALIM11 GENMASK(29, 20) +#define B_P1_TSSI_ALIM12 GENMASK(19, 10) +#define B_P1_TSSI_ALIM13 GENMASK(9, 0) +#define R_P1_TSSI_ALIM3 0x7634 +#define B_P1_TSSI_ALIM31 GENMASK(9, 0) +#define R_P1_TSSI_ALIM2 0x763c +#define B_P1_TSSI_ALIM2 GENMASK(29, 0) +#define R_P1_TSSIC 0x7814 +#define B_P1_TSSIC_BYPASS BIT(11) #define R_P1_TMETER 0x7810 #define B_P1_TMETER GENMASK(15, 10) #define B_P1_TMETER_DIS BIT(16) #define B_P1_TMETER_TRK BIT(24) #define R_P1_TSSI_TRK 0x7818 #define B_P1_TSSI_TRK_EN BIT(30) +#define B_P1_TSSI_RFC GENMASK(28, 27) #define B_P1_TSSI_OFT_EN BIT(28) #define B_P1_TSSI_OFT GENMASK(7, 0) #define R_P1_TSSI_AVG 0x7820 +#define B_P1_TSSI_EN BIT(31) #define B_P1_TSSI_AVG GENMASK(15, 12) #define R_P1_RFCTM 0x7864 #define R_P1_RFCTM_RDY BIT(26) #define B_P1_RFCTM_VAL GENMASK(25, 20) +#define B_P1_RFCTM_DEL GENMASK(19, 11) #define R_P1_PATH_RST 0x78AC #define R_P1_TXPW_RSTB 0x78DC #define B_P1_TXPW_RSTB_MANON BIT(30) #define B_P1_TXPW_RSTB_TSSI BIT(31) #define R_P1_TSSI_MV_AVG 0x78E4 +#define B_P1_TSSI_MV_MIX GENMASK(19, 11) #define B_P1_TSSI_MV_AVG GENMASK(13, 11) +#define B_P1_TSSI_MV_CLR BIT(14) #define R_TSSI_THOF 0x7C00 #define R_S1_DACKI 0x7E00 #define B_S1_DACKI_AR GENMASK(31, 28) diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852b_rfk.c b/drivers/net/wireless/realtek/rtw89/rtw8852b_rfk.c index 466f908e0214..d0ac883772c1 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852b_rfk.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852b_rfk.c @@ -16,6 +16,9 @@ #define RTW8852B_IQK_VER 0x2a #define RTW8852B_IQK_SS 2 #define RTW8852B_RXK_GROUP_NR 4 +#define RTW8852B_TSSI_PATH_NR 2 + +#define _TSSI_DE_MASK GENMASK(21, 12) #define ADDC_T_AVG 100 enum rtw8852b_iqk_type { @@ -35,6 +38,21 @@ enum rtw8852b_iqk_type { ID_IQK_RESTORE = 0x10, }; +static const u32 _tssi_trigger[RTW8852B_TSSI_PATH_NR] = {0x5820, 0x7820}; +static const u32 _tssi_cw_rpt_addr[RTW8852B_TSSI_PATH_NR] = {0x1c18, 0x3c18}; +static const u32 _tssi_cw_default_addr[RTW8852B_TSSI_PATH_NR][4] = { + {0x5634, 0x5630, 0x5630, 0x5630}, + {0x7634, 0x7630, 0x7630, 0x7630} }; +static const u32 _tssi_cw_default_mask[4] = { + 0x000003ff, 0x3ff00000, 0x000ffc00, 0x000003ff}; +static const u32 _tssi_de_cck_long[RF_PATH_NUM_8852B] = {0x5858, 0x7858}; +static const u32 _tssi_de_cck_short[RF_PATH_NUM_8852B] = {0x5860, 0x7860}; +static const u32 _tssi_de_mcs_20m[RF_PATH_NUM_8852B] = {0x5838, 0x7838}; +static const u32 _tssi_de_mcs_40m[RF_PATH_NUM_8852B] = {0x5840, 0x7840}; +static const u32 _tssi_de_mcs_80m[RF_PATH_NUM_8852B] = {0x5848, 0x7848}; +static const u32 _tssi_de_mcs_80m_80m[RF_PATH_NUM_8852B] = {0x5850, 0x7850}; +static const u32 _tssi_de_mcs_5m[RF_PATH_NUM_8852B] = {0x5828, 0x7828}; +static const u32 _tssi_de_mcs_10m[RF_PATH_NUM_8852B] = {0x5830, 0x7830}; static const u32 _a_idxrxgain[RTW8852B_RXK_GROUP_NR] = {0x190, 0x198, 0x350, 0x352}; static const u32 _a_idxattc2[RTW8852B_RXK_GROUP_NR] = {0x0f, 0x0f, 0x3f, 0x7f}; static const u32 _a_idxattc1[RTW8852B_RXK_GROUP_NR] = {0x3, 0x1, 0x0, 0x0}; @@ -1526,6 +1544,15 @@ static void _wait_rx_mode(struct rtw89_dev *rtwdev, u8 kpath) } } +static void _tmac_tx_pause(struct rtw89_dev *rtwdev, enum rtw89_phy_idx band_idx, + bool is_pause) +{ + if (!is_pause) + return; + + _wait_rx_mode(rtwdev, _kpath(rtwdev, band_idx)); +} + static void _doiqk(struct rtw89_dev *rtwdev, bool force, enum rtw89_phy_idx phy_idx, u8 path) { @@ -1578,6 +1605,1027 @@ static void _iqk(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx, bool forc } } +static void _tssi_rf_setting(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy, + enum rtw89_rf_path path) +{ + const struct rtw89_chan *chan = rtw89_chan_get(rtwdev, RTW89_SUB_ENTITY_0); + enum rtw89_band band = chan->band_type; + + if (band == RTW89_BAND_2G) + rtw89_write_rf(rtwdev, path, RR_TXPOW, RR_TXPOW_TXG, 0x1); + else + rtw89_write_rf(rtwdev, path, RR_TXPOW, RR_TXPOW_TXA, 0x1); +} + +static void _tssi_set_sys(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy, + enum rtw89_rf_path path) +{ + const struct rtw89_chan *chan = rtw89_chan_get(rtwdev, RTW89_SUB_ENTITY_0); + enum rtw89_band band = chan->band_type; + + rtw89_rfk_parser(rtwdev, &rtw8852b_tssi_sys_defs_tbl); + + if (path == RF_PATH_A) + rtw89_rfk_parser_by_cond(rtwdev, band == RTW89_BAND_2G, + &rtw8852b_tssi_sys_a_defs_2g_tbl, + &rtw8852b_tssi_sys_a_defs_5g_tbl); + else + rtw89_rfk_parser_by_cond(rtwdev, band == RTW89_BAND_2G, + &rtw8852b_tssi_sys_b_defs_2g_tbl, + &rtw8852b_tssi_sys_b_defs_5g_tbl); +} + +static void _tssi_ini_txpwr_ctrl_bb(struct rtw89_dev *rtwdev, + enum rtw89_phy_idx phy, + enum rtw89_rf_path path) +{ + rtw89_rfk_parser_by_cond(rtwdev, path == RF_PATH_A, + &rtw8852b_tssi_init_txpwr_defs_a_tbl, + &rtw8852b_tssi_init_txpwr_defs_b_tbl); +} + +static void _tssi_ini_txpwr_ctrl_bb_he_tb(struct rtw89_dev *rtwdev, + enum rtw89_phy_idx phy, + enum rtw89_rf_path path) +{ + rtw89_rfk_parser_by_cond(rtwdev, path == RF_PATH_A, + &rtw8852b_tssi_init_txpwr_he_tb_defs_a_tbl, + &rtw8852b_tssi_init_txpwr_he_tb_defs_b_tbl); +} + +static void _tssi_set_dck(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy, + enum rtw89_rf_path path) +{ + rtw89_rfk_parser_by_cond(rtwdev, path == RF_PATH_A, + &rtw8852b_tssi_dck_defs_a_tbl, + &rtw8852b_tssi_dck_defs_b_tbl); +} + +static void _tssi_set_tmeter_tbl(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy, + enum rtw89_rf_path path) +{ +#define RTW8852B_TSSI_GET_VAL(ptr, idx) \ +({ \ + s8 *__ptr = (ptr); \ + u8 __idx = (idx), __i, __v; \ + u32 __val = 0; \ + for (__i = 0; __i < 4; __i++) { \ + __v = (__ptr[__idx + __i]); \ + __val |= (__v << (8 * __i)); \ + } \ + __val; \ +}) + struct rtw89_tssi_info *tssi_info = &rtwdev->tssi; + const struct rtw89_chan *chan = rtw89_chan_get(rtwdev, RTW89_SUB_ENTITY_0); + u8 ch = chan->channel; + u8 subband = chan->subband_type; + const s8 *thm_up_a = NULL; + const s8 *thm_down_a = NULL; + const s8 *thm_up_b = NULL; + const s8 *thm_down_b = NULL; + u8 thermal = 0xff; + s8 thm_ofst[64] = {0}; + u32 tmp = 0; + u8 i, j; + + switch (subband) { + default: + case RTW89_CH_2G: + thm_up_a = rtw89_8852b_trk_cfg.delta_swingidx_2ga_p; + thm_down_a = rtw89_8852b_trk_cfg.delta_swingidx_2ga_n; + thm_up_b = rtw89_8852b_trk_cfg.delta_swingidx_2gb_p; + thm_down_b = rtw89_8852b_trk_cfg.delta_swingidx_2gb_n; + break; + case RTW89_CH_5G_BAND_1: + thm_up_a = rtw89_8852b_trk_cfg.delta_swingidx_5ga_p[0]; + thm_down_a = rtw89_8852b_trk_cfg.delta_swingidx_5ga_n[0]; + thm_up_b = rtw89_8852b_trk_cfg.delta_swingidx_5gb_p[0]; + thm_down_b = rtw89_8852b_trk_cfg.delta_swingidx_5gb_n[0]; + break; + case RTW89_CH_5G_BAND_3: + thm_up_a = rtw89_8852b_trk_cfg.delta_swingidx_5ga_p[1]; + thm_down_a = rtw89_8852b_trk_cfg.delta_swingidx_5ga_n[1]; + thm_up_b = rtw89_8852b_trk_cfg.delta_swingidx_5gb_p[1]; + thm_down_b = rtw89_8852b_trk_cfg.delta_swingidx_5gb_n[1]; + break; + case RTW89_CH_5G_BAND_4: + thm_up_a = rtw89_8852b_trk_cfg.delta_swingidx_5ga_p[2]; + thm_down_a = rtw89_8852b_trk_cfg.delta_swingidx_5ga_n[2]; + thm_up_b = rtw89_8852b_trk_cfg.delta_swingidx_5gb_p[2]; + thm_down_b = rtw89_8852b_trk_cfg.delta_swingidx_5gb_n[2]; + break; + } + + if (path == RF_PATH_A) { + thermal = tssi_info->thermal[RF_PATH_A]; + + rtw89_debug(rtwdev, RTW89_DBG_TSSI, + "[TSSI] ch=%d thermal_pathA=0x%x\n", ch, thermal); + + rtw89_phy_write32_mask(rtwdev, R_P0_TMETER, B_P0_TMETER_DIS, 0x0); + rtw89_phy_write32_mask(rtwdev, R_P0_TMETER, B_P0_TMETER_TRK, 0x1); + + if (thermal == 0xff) { + rtw89_phy_write32_mask(rtwdev, R_P0_TMETER, B_P0_TMETER, 32); + rtw89_phy_write32_mask(rtwdev, R_P0_RFCTM, B_P0_RFCTM_VAL, 32); + + for (i = 0; i < 64; i += 4) { + rtw89_phy_write32(rtwdev, R_P0_TSSI_BASE + i, 0x0); + + rtw89_debug(rtwdev, RTW89_DBG_TSSI, + "[TSSI] write 0x%x val=0x%08x\n", + R_P0_TSSI_BASE + i, 0x0); + } + + } else { + rtw89_phy_write32_mask(rtwdev, R_P0_TMETER, B_P0_TMETER, thermal); + rtw89_phy_write32_mask(rtwdev, R_P0_RFCTM, B_P0_RFCTM_VAL, + thermal); + + i = 0; + for (j = 0; j < 32; j++) + thm_ofst[j] = i < DELTA_SWINGIDX_SIZE ? + -thm_down_a[i++] : + -thm_down_a[DELTA_SWINGIDX_SIZE - 1]; + + i = 1; + for (j = 63; j >= 32; j--) + thm_ofst[j] = i < DELTA_SWINGIDX_SIZE ? + thm_up_a[i++] : + thm_up_a[DELTA_SWINGIDX_SIZE - 1]; + + for (i = 0; i < 64; i += 4) { + tmp = RTW8852B_TSSI_GET_VAL(thm_ofst, i); + rtw89_phy_write32(rtwdev, R_P0_TSSI_BASE + i, tmp); + + rtw89_debug(rtwdev, RTW89_DBG_TSSI, + "[TSSI] write 0x%x val=0x%08x\n", + 0x5c00 + i, tmp); + } + } + rtw89_phy_write32_mask(rtwdev, R_P0_RFCTM, R_P0_RFCTM_RDY, 0x1); + rtw89_phy_write32_mask(rtwdev, R_P0_RFCTM, R_P0_RFCTM_RDY, 0x0); + + } else { + thermal = tssi_info->thermal[RF_PATH_B]; + + rtw89_debug(rtwdev, RTW89_DBG_TSSI, + "[TSSI] ch=%d thermal_pathB=0x%x\n", ch, thermal); + + rtw89_phy_write32_mask(rtwdev, R_P1_TMETER, B_P1_TMETER_DIS, 0x0); + rtw89_phy_write32_mask(rtwdev, R_P1_TMETER, B_P1_TMETER_TRK, 0x1); + + if (thermal == 0xff) { + rtw89_phy_write32_mask(rtwdev, R_P1_TMETER, B_P1_TMETER, 32); + rtw89_phy_write32_mask(rtwdev, R_P1_RFCTM, B_P1_RFCTM_VAL, 32); + + for (i = 0; i < 64; i += 4) { + rtw89_phy_write32(rtwdev, R_TSSI_THOF + i, 0x0); + + rtw89_debug(rtwdev, RTW89_DBG_TSSI, + "[TSSI] write 0x%x val=0x%08x\n", + 0x7c00 + i, 0x0); + } + + } else { + rtw89_phy_write32_mask(rtwdev, R_P1_TMETER, B_P1_TMETER, thermal); + rtw89_phy_write32_mask(rtwdev, R_P1_RFCTM, B_P1_RFCTM_VAL, + thermal); + + i = 0; + for (j = 0; j < 32; j++) + thm_ofst[j] = i < DELTA_SWINGIDX_SIZE ? + -thm_down_b[i++] : + -thm_down_b[DELTA_SWINGIDX_SIZE - 1]; + + i = 1; + for (j = 63; j >= 32; j--) + thm_ofst[j] = i < DELTA_SWINGIDX_SIZE ? + thm_up_b[i++] : + thm_up_b[DELTA_SWINGIDX_SIZE - 1]; + + for (i = 0; i < 64; i += 4) { + tmp = RTW8852B_TSSI_GET_VAL(thm_ofst, i); + rtw89_phy_write32(rtwdev, R_TSSI_THOF + i, tmp); + + rtw89_debug(rtwdev, RTW89_DBG_TSSI, + "[TSSI] write 0x%x val=0x%08x\n", + 0x7c00 + i, tmp); + } + } + rtw89_phy_write32_mask(rtwdev, R_P1_RFCTM, R_P1_RFCTM_RDY, 0x1); + rtw89_phy_write32_mask(rtwdev, R_P1_RFCTM, R_P1_RFCTM_RDY, 0x0); + } +#undef RTW8852B_TSSI_GET_VAL +} + +static void _tssi_set_dac_gain_tbl(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy, + enum rtw89_rf_path path) +{ + rtw89_rfk_parser_by_cond(rtwdev, path == RF_PATH_A, + &rtw8852b_tssi_dac_gain_defs_a_tbl, + &rtw8852b_tssi_dac_gain_defs_b_tbl); +} + +static void _tssi_slope_cal_org(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy, + enum rtw89_rf_path path) +{ + const struct rtw89_chan *chan = rtw89_chan_get(rtwdev, RTW89_SUB_ENTITY_0); + enum rtw89_band band = chan->band_type; + + if (path == RF_PATH_A) + rtw89_rfk_parser_by_cond(rtwdev, band == RTW89_BAND_2G, + &rtw8852b_tssi_slope_a_defs_2g_tbl, + &rtw8852b_tssi_slope_a_defs_5g_tbl); + else + rtw89_rfk_parser_by_cond(rtwdev, band == RTW89_BAND_2G, + &rtw8852b_tssi_slope_b_defs_2g_tbl, + &rtw8852b_tssi_slope_b_defs_5g_tbl); +} + +static void _tssi_alignment_default(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy, + enum rtw89_rf_path path, bool all) +{ + const struct rtw89_chan *chan = rtw89_chan_get(rtwdev, RTW89_SUB_ENTITY_0); + enum rtw89_band band = chan->band_type; + const struct rtw89_rfk_tbl *tbl = NULL; + u8 ch = chan->channel; + + if (path == RF_PATH_A) { + if (band == RTW89_BAND_2G) { + if (all) + tbl = &rtw8852b_tssi_align_a_2g_all_defs_tbl; + else + tbl = &rtw8852b_tssi_align_a_2g_part_defs_tbl; + } else if (ch >= 36 && ch <= 64) { + if (all) + tbl = &rtw8852b_tssi_align_a_5g1_all_defs_tbl; + else + tbl = &rtw8852b_tssi_align_a_5g1_part_defs_tbl; + } else if (ch >= 100 && ch <= 144) { + if (all) + tbl = &rtw8852b_tssi_align_a_5g2_all_defs_tbl; + else + tbl = &rtw8852b_tssi_align_a_5g2_part_defs_tbl; + } else if (ch >= 149 && ch <= 177) { + if (all) + tbl = &rtw8852b_tssi_align_a_5g3_all_defs_tbl; + else + tbl = &rtw8852b_tssi_align_a_5g3_part_defs_tbl; + } + } else { + if (ch >= 1 && ch <= 14) { + if (all) + tbl = &rtw8852b_tssi_align_b_2g_all_defs_tbl; + else + tbl = &rtw8852b_tssi_align_b_2g_part_defs_tbl; + } else if (ch >= 36 && ch <= 64) { + if (all) + tbl = &rtw8852b_tssi_align_b_5g1_all_defs_tbl; + else + tbl = &rtw8852b_tssi_align_b_5g1_part_defs_tbl; + } else if (ch >= 100 && ch <= 144) { + if (all) + tbl = &rtw8852b_tssi_align_b_5g2_all_defs_tbl; + else + tbl = &rtw8852b_tssi_align_b_5g2_part_defs_tbl; + } else if (ch >= 149 && ch <= 177) { + if (all) + tbl = &rtw8852b_tssi_align_b_5g3_all_defs_tbl; + else + tbl = &rtw8852b_tssi_align_b_5g3_part_defs_tbl; + } + } + + if (tbl) + rtw89_rfk_parser(rtwdev, tbl); +} + +static void _tssi_set_tssi_slope(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy, + enum rtw89_rf_path path) +{ + rtw89_rfk_parser_by_cond(rtwdev, path == RF_PATH_A, + &rtw8852b_tssi_slope_defs_a_tbl, + &rtw8852b_tssi_slope_defs_b_tbl); +} + +static void _tssi_set_tssi_track(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy, + enum rtw89_rf_path path) +{ + if (path == RF_PATH_A) + rtw89_phy_write32_mask(rtwdev, R_P0_TSSIC, B_P0_TSSIC_BYPASS, 0x0); + else + rtw89_phy_write32_mask(rtwdev, R_P1_TSSIC, B_P1_TSSIC_BYPASS, 0x0); +} + +static void _tssi_set_txagc_offset_mv_avg(struct rtw89_dev *rtwdev, + enum rtw89_phy_idx phy, + enum rtw89_rf_path path) +{ + rtw89_debug(rtwdev, RTW89_DBG_TSSI, "======>%s path=%d\n", __func__, + path); + + if (path == RF_PATH_A) + rtw89_phy_write32_mask(rtwdev, R_P0_TSSI_MV_AVG, B_P0_TSSI_MV_MIX, 0x010); + else + rtw89_phy_write32_mask(rtwdev, R_P1_TSSI_MV_AVG, B_P1_RFCTM_DEL, 0x010); +} + +static void _tssi_enable(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy) +{ + u8 i; + + for (i = 0; i < RF_PATH_NUM_8852B; i++) { + _tssi_set_tssi_track(rtwdev, phy, i); + _tssi_set_txagc_offset_mv_avg(rtwdev, phy, i); + + if (i == RF_PATH_A) { + rtw89_phy_write32_mask(rtwdev, R_P0_TSSI_MV_AVG, + B_P0_TSSI_MV_CLR, 0x0); + rtw89_phy_write32_mask(rtwdev, R_P0_TSSI_AVG, + B_P0_TSSI_EN, 0x0); + rtw89_phy_write32_mask(rtwdev, R_P0_TSSI_AVG, + B_P0_TSSI_EN, 0x1); + rtw89_write_rf(rtwdev, i, RR_TXGA_V1, + RR_TXGA_V1_TRK_EN, 0x1); + rtw89_phy_write32_mask(rtwdev, R_P0_TSSI_TRK, + B_P0_TSSI_RFC, 0x3); + + rtw89_phy_write32_mask(rtwdev, R_P0_TSSI_TRK, + B_P0_TSSI_OFT, 0xc0); + rtw89_phy_write32_mask(rtwdev, R_P0_TSSI_TRK, + B_P0_TSSI_OFT_EN, 0x0); + rtw89_phy_write32_mask(rtwdev, R_P0_TSSI_TRK, + B_P0_TSSI_OFT_EN, 0x1); + + rtwdev->is_tssi_mode[RF_PATH_A] = true; + } else { + rtw89_phy_write32_mask(rtwdev, R_P1_TSSI_MV_AVG, + B_P1_TSSI_MV_CLR, 0x0); + rtw89_phy_write32_mask(rtwdev, R_P1_TSSI_AVG, + B_P1_TSSI_EN, 0x0); + rtw89_phy_write32_mask(rtwdev, R_P1_TSSI_AVG, + B_P1_TSSI_EN, 0x1); + rtw89_write_rf(rtwdev, i, RR_TXGA_V1, + RR_TXGA_V1_TRK_EN, 0x1); + rtw89_phy_write32_mask(rtwdev, R_P1_TSSI_TRK, + B_P1_TSSI_RFC, 0x3); + + rtw89_phy_write32_mask(rtwdev, R_P1_TSSI_TRK, + B_P1_TSSI_OFT, 0xc0); + rtw89_phy_write32_mask(rtwdev, R_P1_TSSI_TRK, + B_P1_TSSI_OFT_EN, 0x0); + rtw89_phy_write32_mask(rtwdev, R_P1_TSSI_TRK, + B_P1_TSSI_OFT_EN, 0x1); + + rtwdev->is_tssi_mode[RF_PATH_B] = true; + } + } +} + +static void _tssi_disable(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy) +{ + rtw89_phy_write32_mask(rtwdev, R_P0_TSSI_AVG, B_P0_TSSI_EN, 0x0); + rtw89_phy_write32_mask(rtwdev, R_P0_TSSI_TRK, B_P0_TSSI_RFC, 0x1); + rtw89_phy_write32_mask(rtwdev, R_P0_TSSI_MV_AVG, B_P0_TSSI_MV_CLR, 0x1); + rtw89_phy_write32_mask(rtwdev, R_P1_TSSI_AVG, B_P1_TSSI_EN, 0x0); + rtw89_phy_write32_mask(rtwdev, R_P1_TSSI_TRK, B_P1_TSSI_RFC, 0x1); + rtw89_phy_write32_mask(rtwdev, R_P1_TSSI_MV_AVG, B_P1_TSSI_MV_CLR, 0x1); + + rtwdev->is_tssi_mode[RF_PATH_A] = false; + rtwdev->is_tssi_mode[RF_PATH_B] = false; +} + +static u32 _tssi_get_cck_group(struct rtw89_dev *rtwdev, u8 ch) +{ + switch (ch) { + case 1 ... 2: + return 0; + case 3 ... 5: + return 1; + case 6 ... 8: + return 2; + case 9 ... 11: + return 3; + case 12 ... 13: + return 4; + case 14: + return 5; + } + + return 0; +} + +#define TSSI_EXTRA_GROUP_BIT (BIT(31)) +#define TSSI_EXTRA_GROUP(idx) (TSSI_EXTRA_GROUP_BIT | (idx)) +#define IS_TSSI_EXTRA_GROUP(group) ((group) & TSSI_EXTRA_GROUP_BIT) +#define TSSI_EXTRA_GET_GROUP_IDX1(group) ((group) & ~TSSI_EXTRA_GROUP_BIT) +#define TSSI_EXTRA_GET_GROUP_IDX2(group) (TSSI_EXTRA_GET_GROUP_IDX1(group) + 1) + +static u32 _tssi_get_ofdm_group(struct rtw89_dev *rtwdev, u8 ch) +{ + switch (ch) { + case 1 ... 2: + return 0; + case 3 ... 5: + return 1; + case 6 ... 8: + return 2; + case 9 ... 11: + return 3; + case 12 ... 14: + return 4; + case 36 ... 40: + return 5; + case 41 ... 43: + return TSSI_EXTRA_GROUP(5); + case 44 ... 48: + return 6; + case 49 ... 51: + return TSSI_EXTRA_GROUP(6); + case 52 ... 56: + return 7; + case 57 ... 59: + return TSSI_EXTRA_GROUP(7); + case 60 ... 64: + return 8; + case 100 ... 104: + return 9; + case 105 ... 107: + return TSSI_EXTRA_GROUP(9); + case 108 ... 112: + return 10; + case 113 ... 115: + return TSSI_EXTRA_GROUP(10); + case 116 ... 120: + return 11; + case 121 ... 123: + return TSSI_EXTRA_GROUP(11); + case 124 ... 128: + return 12; + case 129 ... 131: + return TSSI_EXTRA_GROUP(12); + case 132 ... 136: + return 13; + case 137 ... 139: + return TSSI_EXTRA_GROUP(13); + case 140 ... 144: + return 14; + case 149 ... 153: + return 15; + case 154 ... 156: + return TSSI_EXTRA_GROUP(15); + case 157 ... 161: + return 16; + case 162 ... 164: + return TSSI_EXTRA_GROUP(16); + case 165 ... 169: + return 17; + case 170 ... 172: + return TSSI_EXTRA_GROUP(17); + case 173 ... 177: + return 18; + } + + return 0; +} + +static u32 _tssi_get_trim_group(struct rtw89_dev *rtwdev, u8 ch) +{ + switch (ch) { + case 1 ... 8: + return 0; + case 9 ... 14: + return 1; + case 36 ... 48: + return 2; + case 52 ... 64: + return 3; + case 100 ... 112: + return 4; + case 116 ... 128: + return 5; + case 132 ... 144: + return 6; + case 149 ... 177: + return 7; + } + + return 0; +} + +static s8 _tssi_get_ofdm_de(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy, + enum rtw89_rf_path path) +{ + struct rtw89_tssi_info *tssi_info = &rtwdev->tssi; + const struct rtw89_chan *chan = rtw89_chan_get(rtwdev, RTW89_SUB_ENTITY_0); + u8 ch = chan->channel; + u32 gidx, gidx_1st, gidx_2nd; + s8 de_1st; + s8 de_2nd; + s8 val; + + gidx = _tssi_get_ofdm_group(rtwdev, ch); + + rtw89_debug(rtwdev, RTW89_DBG_TSSI, + "[TSSI][TRIM]: path=%d mcs group_idx=0x%x\n", path, gidx); + + if (IS_TSSI_EXTRA_GROUP(gidx)) { + gidx_1st = TSSI_EXTRA_GET_GROUP_IDX1(gidx); + gidx_2nd = TSSI_EXTRA_GET_GROUP_IDX2(gidx); + de_1st = tssi_info->tssi_mcs[path][gidx_1st]; + de_2nd = tssi_info->tssi_mcs[path][gidx_2nd]; + val = (de_1st + de_2nd) / 2; + + rtw89_debug(rtwdev, RTW89_DBG_TSSI, + "[TSSI][TRIM]: path=%d mcs de=%d 1st=%d 2nd=%d\n", + path, val, de_1st, de_2nd); + } else { + val = tssi_info->tssi_mcs[path][gidx]; + + rtw89_debug(rtwdev, RTW89_DBG_TSSI, + "[TSSI][TRIM]: path=%d mcs de=%d\n", path, val); + } + + return val; +} + +static s8 _tssi_get_ofdm_trim_de(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy, + enum rtw89_rf_path path) +{ + struct rtw89_tssi_info *tssi_info = &rtwdev->tssi; + const struct rtw89_chan *chan = rtw89_chan_get(rtwdev, RTW89_SUB_ENTITY_0); + u8 ch = chan->channel; + u32 tgidx, tgidx_1st, tgidx_2nd; + s8 tde_1st; + s8 tde_2nd; + s8 val; + + tgidx = _tssi_get_trim_group(rtwdev, ch); + + rtw89_debug(rtwdev, RTW89_DBG_TSSI, + "[TSSI][TRIM]: path=%d mcs trim_group_idx=0x%x\n", + path, tgidx); + + if (IS_TSSI_EXTRA_GROUP(tgidx)) { + tgidx_1st = TSSI_EXTRA_GET_GROUP_IDX1(tgidx); + tgidx_2nd = TSSI_EXTRA_GET_GROUP_IDX2(tgidx); + tde_1st = tssi_info->tssi_trim[path][tgidx_1st]; + tde_2nd = tssi_info->tssi_trim[path][tgidx_2nd]; + val = (tde_1st + tde_2nd) / 2; + + rtw89_debug(rtwdev, RTW89_DBG_TSSI, + "[TSSI][TRIM]: path=%d mcs trim_de=%d 1st=%d 2nd=%d\n", + path, val, tde_1st, tde_2nd); + } else { + val = tssi_info->tssi_trim[path][tgidx]; + + rtw89_debug(rtwdev, RTW89_DBG_TSSI, + "[TSSI][TRIM]: path=%d mcs trim_de=%d\n", + path, val); + } + + return val; +} + +static void _tssi_set_efuse_to_de(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy) +{ + struct rtw89_tssi_info *tssi_info = &rtwdev->tssi; + const struct rtw89_chan *chan = rtw89_chan_get(rtwdev, RTW89_SUB_ENTITY_0); + u8 ch = chan->channel; + u8 gidx; + s8 ofdm_de; + s8 trim_de; + s32 val; + u32 i; + + rtw89_debug(rtwdev, RTW89_DBG_TSSI, "[TSSI][TRIM]: phy=%d ch=%d\n", + phy, ch); + + for (i = RF_PATH_A; i < RF_PATH_NUM_8852B; i++) { + gidx = _tssi_get_cck_group(rtwdev, ch); + trim_de = _tssi_get_ofdm_trim_de(rtwdev, phy, i); + val = tssi_info->tssi_cck[i][gidx] + trim_de; + + rtw89_debug(rtwdev, RTW89_DBG_TSSI, + "[TSSI][TRIM]: path=%d cck[%d]=0x%x trim=0x%x\n", + i, gidx, tssi_info->tssi_cck[i][gidx], trim_de); + + rtw89_phy_write32_mask(rtwdev, _tssi_de_cck_long[i], _TSSI_DE_MASK, val); + rtw89_phy_write32_mask(rtwdev, _tssi_de_cck_short[i], _TSSI_DE_MASK, val); + + rtw89_debug(rtwdev, RTW89_DBG_TSSI, + "[TSSI] Set TSSI CCK DE 0x%x[21:12]=0x%x\n", + _tssi_de_cck_long[i], + rtw89_phy_read32_mask(rtwdev, _tssi_de_cck_long[i], + _TSSI_DE_MASK)); + + ofdm_de = _tssi_get_ofdm_de(rtwdev, phy, i); + trim_de = _tssi_get_ofdm_trim_de(rtwdev, phy, i); + val = ofdm_de + trim_de; + + rtw89_debug(rtwdev, RTW89_DBG_TSSI, + "[TSSI][TRIM]: path=%d mcs=0x%x trim=0x%x\n", + i, ofdm_de, trim_de); + + rtw89_phy_write32_mask(rtwdev, _tssi_de_mcs_20m[i], _TSSI_DE_MASK, val); + rtw89_phy_write32_mask(rtwdev, _tssi_de_mcs_40m[i], _TSSI_DE_MASK, val); + rtw89_phy_write32_mask(rtwdev, _tssi_de_mcs_80m[i], _TSSI_DE_MASK, val); + rtw89_phy_write32_mask(rtwdev, _tssi_de_mcs_80m_80m[i], _TSSI_DE_MASK, val); + rtw89_phy_write32_mask(rtwdev, _tssi_de_mcs_5m[i], _TSSI_DE_MASK, val); + rtw89_phy_write32_mask(rtwdev, _tssi_de_mcs_10m[i], _TSSI_DE_MASK, val); + + rtw89_debug(rtwdev, RTW89_DBG_TSSI, + "[TSSI] Set TSSI MCS DE 0x%x[21:12]=0x%x\n", + _tssi_de_mcs_20m[i], + rtw89_phy_read32_mask(rtwdev, _tssi_de_mcs_20m[i], + _TSSI_DE_MASK)); + } +} + +static void _tssi_alimentk_dump_result(struct rtw89_dev *rtwdev, enum rtw89_rf_path path) +{ + rtw89_debug(rtwdev, RTW89_DBG_RFK, + "[TSSI PA K]\n0x%x = 0x%08x\n0x%x = 0x%08x\n0x%x = 0x%08x\n0x%x = 0x%08x\n" + "0x%x = 0x%08x\n0x%x = 0x%08x\n0x%x = 0x%08x\n0x%x = 0x%08x\n", + R_TSSI_PA_K1 + (path << 13), + rtw89_phy_read32_mask(rtwdev, R_TSSI_PA_K1 + (path << 13), MASKDWORD), + R_TSSI_PA_K2 + (path << 13), + rtw89_phy_read32_mask(rtwdev, R_TSSI_PA_K2 + (path << 13), MASKDWORD), + R_P0_TSSI_ALIM1 + (path << 13), + rtw89_phy_read32_mask(rtwdev, R_P0_TSSI_ALIM1 + (path << 13), MASKDWORD), + R_P0_TSSI_ALIM3 + (path << 13), + rtw89_phy_read32_mask(rtwdev, R_P0_TSSI_ALIM3 + (path << 13), MASKDWORD), + R_TSSI_PA_K5 + (path << 13), + rtw89_phy_read32_mask(rtwdev, R_TSSI_PA_K5 + (path << 13), MASKDWORD), + R_P0_TSSI_ALIM2 + (path << 13), + rtw89_phy_read32_mask(rtwdev, R_P0_TSSI_ALIM2 + (path << 13), MASKDWORD), + R_P0_TSSI_ALIM4 + (path << 13), + rtw89_phy_read32_mask(rtwdev, R_P0_TSSI_ALIM4 + (path << 13), MASKDWORD), + R_TSSI_PA_K8 + (path << 13), + rtw89_phy_read32_mask(rtwdev, R_TSSI_PA_K8 + (path << 13), MASKDWORD)); +} + +static void _tssi_alimentk_done(struct rtw89_dev *rtwdev, + enum rtw89_phy_idx phy, enum rtw89_rf_path path) +{ + struct rtw89_tssi_info *tssi_info = &rtwdev->tssi; + const struct rtw89_chan *chan = rtw89_chan_get(rtwdev, RTW89_SUB_ENTITY_0); + u8 channel = chan->channel; + u8 band; + + rtw89_debug(rtwdev, RTW89_DBG_RFK, + "======>%s phy=%d path=%d\n", __func__, phy, path); + + if (channel >= 1 && channel <= 14) + band = TSSI_ALIMK_2G; + else if (channel >= 36 && channel <= 64) + band = TSSI_ALIMK_5GL; + else if (channel >= 100 && channel <= 144) + band = TSSI_ALIMK_5GM; + else if (channel >= 149 && channel <= 177) + band = TSSI_ALIMK_5GH; + else + band = TSSI_ALIMK_2G; + + if (tssi_info->alignment_done[path][band]) { + rtw89_phy_write32_mask(rtwdev, R_P0_TSSI_ALIM1 + (path << 13), MASKDWORD, + tssi_info->alignment_value[path][band][0]); + rtw89_phy_write32_mask(rtwdev, R_P0_TSSI_ALIM3 + (path << 13), MASKDWORD, + tssi_info->alignment_value[path][band][1]); + rtw89_phy_write32_mask(rtwdev, R_P0_TSSI_ALIM2 + (path << 13), MASKDWORD, + tssi_info->alignment_value[path][band][2]); + rtw89_phy_write32_mask(rtwdev, R_P0_TSSI_ALIM4 + (path << 13), MASKDWORD, + tssi_info->alignment_value[path][band][3]); + } + + _tssi_alimentk_dump_result(rtwdev, path); +} + +static void _tssi_hw_tx(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy, + enum rtw89_rf_path path, u16 cnt, u16 period, s16 pwr_dbm, + u8 enable) +{ + enum rtw89_rf_path_bit rx_path; + + if (path == RF_PATH_A) + rx_path = RF_A; + else if (path == RF_PATH_B) + rx_path = RF_B; + else if (path == RF_PATH_AB) + rx_path = RF_AB; + else + rx_path = RF_ABCD; /* don't change path, but still set others */ + + if (enable) { + rtw8852b_bb_set_plcp_tx(rtwdev); + rtw8852b_bb_cfg_tx_path(rtwdev, path); + rtw8852b_bb_ctrl_rx_path(rtwdev, rx_path); + rtw8852b_bb_set_power(rtwdev, pwr_dbm, phy); + } + + rtw8852b_bb_set_pmac_pkt_tx(rtwdev, enable, cnt, period, 20, phy); +} + +static void _tssi_backup_bb_registers(struct rtw89_dev *rtwdev, + enum rtw89_phy_idx phy, const u32 reg[], + u32 reg_backup[], u32 reg_num) +{ + u32 i; + + for (i = 0; i < reg_num; i++) { + reg_backup[i] = rtw89_phy_read32_mask(rtwdev, reg[i], MASKDWORD); + + rtw89_debug(rtwdev, RTW89_DBG_RFK, + "[TSSI] Backup BB 0x%x = 0x%x\n", reg[i], + reg_backup[i]); + } +} + +static void _tssi_reload_bb_registers(struct rtw89_dev *rtwdev, + enum rtw89_phy_idx phy, const u32 reg[], + u32 reg_backup[], u32 reg_num) + +{ + u32 i; + + for (i = 0; i < reg_num; i++) { + rtw89_phy_write32_mask(rtwdev, reg[i], MASKDWORD, reg_backup[i]); + + rtw89_debug(rtwdev, RTW89_DBG_RFK, + "[TSSI] Reload BB 0x%x = 0x%x\n", reg[i], + reg_backup[i]); + } +} + +static u8 _tssi_ch_to_idx(struct rtw89_dev *rtwdev, u8 channel) +{ + u8 channel_index; + + if (channel >= 1 && channel <= 14) + channel_index = channel - 1; + else if (channel >= 36 && channel <= 64) + channel_index = (channel - 36) / 2 + 14; + else if (channel >= 100 && channel <= 144) + channel_index = ((channel - 100) / 2) + 15 + 14; + else if (channel >= 149 && channel <= 177) + channel_index = ((channel - 149) / 2) + 38 + 14; + else + channel_index = 0; + + return channel_index; +} + +static bool _tssi_get_cw_report(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy, + enum rtw89_rf_path path, const s16 *power, + u32 *tssi_cw_rpt) +{ + u32 tx_counter, tx_counter_tmp; + const int retry = 100; + u32 tmp; + int j, k; + + for (j = 0; j < RTW8852B_TSSI_PATH_NR; j++) { + rtw89_phy_write32_mask(rtwdev, _tssi_trigger[path], B_P0_TSSI_EN, 0x0); + rtw89_phy_write32_mask(rtwdev, _tssi_trigger[path], B_P0_TSSI_EN, 0x1); + + tx_counter = rtw89_phy_read32_mask(rtwdev, R_TX_COUNTER, MASKLWORD); + + tmp = rtw89_phy_read32_mask(rtwdev, _tssi_trigger[path], MASKDWORD); + rtw89_debug(rtwdev, RTW89_DBG_RFK, + "[TSSI PA K] 0x%x = 0x%08x path=%d\n", + _tssi_trigger[path], tmp, path); + + if (j == 0) + _tssi_hw_tx(rtwdev, phy, path, 100, 5000, power[j], true); + else + _tssi_hw_tx(rtwdev, phy, RF_PATH_ABCD, 100, 5000, power[j], true); + + tx_counter_tmp = rtw89_phy_read32_mask(rtwdev, R_TX_COUNTER, MASKLWORD); + tx_counter_tmp -= tx_counter; + + rtw89_debug(rtwdev, RTW89_DBG_RFK, + "[TSSI PA K] First HWTXcounter=%d path=%d\n", + tx_counter_tmp, path); + + for (k = 0; k < retry; k++) { + tmp = rtw89_phy_read32_mask(rtwdev, _tssi_cw_rpt_addr[path], + B_TSSI_CWRPT_RDY); + if (tmp) + break; + + udelay(30); + + tx_counter_tmp = + rtw89_phy_read32_mask(rtwdev, R_TX_COUNTER, MASKLWORD); + tx_counter_tmp -= tx_counter; + + rtw89_debug(rtwdev, RTW89_DBG_RFK, + "[TSSI PA K] Flow k = %d HWTXcounter=%d path=%d\n", + k, tx_counter_tmp, path); + } + + if (k >= retry) { + rtw89_debug(rtwdev, RTW89_DBG_RFK, + "[TSSI PA K] TSSI finish bit k > %d mp:100ms normal:30us path=%d\n", + k, path); + + _tssi_hw_tx(rtwdev, phy, path, 100, 5000, power[j], false); + return false; + } + + tssi_cw_rpt[j] = + rtw89_phy_read32_mask(rtwdev, _tssi_cw_rpt_addr[path], B_TSSI_CWRPT); + + _tssi_hw_tx(rtwdev, phy, path, 100, 5000, power[j], false); + + tx_counter_tmp = rtw89_phy_read32_mask(rtwdev, R_TX_COUNTER, MASKLWORD); + tx_counter_tmp -= tx_counter; + + rtw89_debug(rtwdev, RTW89_DBG_RFK, + "[TSSI PA K] Final HWTXcounter=%d path=%d\n", + tx_counter_tmp, path); + } + + return true; +} + +static void _tssi_alimentk(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy, + enum rtw89_rf_path path) +{ + static const u32 bb_reg[8] = {0x5820, 0x7820, 0x4978, 0x58e4, + 0x78e4, 0x49c0, 0x0d18, 0x0d80}; + static const s16 power_2g[4] = {48, 20, 4, 4}; + static const s16 power_5g[4] = {48, 20, 4, 4}; + struct rtw89_tssi_info *tssi_info = &rtwdev->tssi; + const struct rtw89_chan *chan = rtw89_chan_get(rtwdev, RTW89_SUB_ENTITY_0); + s32 tssi_alim_offset_1, tssi_alim_offset_2, tssi_alim_offset_3; + u32 tssi_cw_rpt[RTW8852B_TSSI_PATH_NR] = {0}; + u8 channel = chan->channel; + u8 ch_idx = _tssi_ch_to_idx(rtwdev, channel); + struct rtw8852b_bb_tssi_bak tssi_bak; + s32 aliment_diff, tssi_cw_default; + u32 start_time, finish_time; + u32 bb_reg_backup[8] = {0}; + const s16 *power; + u8 band; + bool ok; + u32 tmp; + u8 j; + + rtw89_debug(rtwdev, RTW89_DBG_RFK, + "======> %s channel=%d path=%d\n", __func__, channel, + path); + + if (tssi_info->check_backup_aligmk[path][ch_idx]) { + rtw89_phy_write32_mask(rtwdev, R_P0_TSSI_ALIM1 + (path << 13), MASKDWORD, + tssi_info->alignment_backup_by_ch[path][ch_idx][0]); + rtw89_phy_write32_mask(rtwdev, R_P0_TSSI_ALIM3 + (path << 13), MASKDWORD, + tssi_info->alignment_backup_by_ch[path][ch_idx][1]); + rtw89_phy_write32_mask(rtwdev, R_P0_TSSI_ALIM2 + (path << 13), MASKDWORD, + tssi_info->alignment_backup_by_ch[path][ch_idx][2]); + rtw89_phy_write32_mask(rtwdev, R_P0_TSSI_ALIM4 + (path << 13), MASKDWORD, + tssi_info->alignment_backup_by_ch[path][ch_idx][3]); + + rtw89_debug(rtwdev, RTW89_DBG_RFK, + "======> %s Reload TSSI Alignment !!!\n", __func__); + _tssi_alimentk_dump_result(rtwdev, path); + return; + } + + start_time = ktime_get_ns(); + + if (chan->band_type == RTW89_BAND_2G) + power = power_2g; + else + power = power_5g; + + if (channel >= 1 && channel <= 14) + band = TSSI_ALIMK_2G; + else if (channel >= 36 && channel <= 64) + band = TSSI_ALIMK_5GL; + else if (channel >= 100 && channel <= 144) + band = TSSI_ALIMK_5GM; + else if (channel >= 149 && channel <= 177) + band = TSSI_ALIMK_5GH; + else + band = TSSI_ALIMK_2G; + + rtw8852b_bb_backup_tssi(rtwdev, phy, &tssi_bak); + _tssi_backup_bb_registers(rtwdev, phy, bb_reg, bb_reg_backup, ARRAY_SIZE(bb_reg_backup)); + + rtw89_phy_write32_mask(rtwdev, R_P0_TSSI_AVG, B_P0_TSSI_AVG, 0x8); + rtw89_phy_write32_mask(rtwdev, R_P1_TSSI_AVG, B_P1_TSSI_AVG, 0x8); + rtw89_phy_write32_mask(rtwdev, R_P0_TSSI_MV_AVG, B_P0_TSSI_MV_AVG, 0x2); + rtw89_phy_write32_mask(rtwdev, R_P1_TSSI_MV_AVG, B_P1_TSSI_MV_AVG, 0x2); + + ok = _tssi_get_cw_report(rtwdev, phy, path, power, tssi_cw_rpt); + if (!ok) + goto out; + + for (j = 0; j < RTW8852B_TSSI_PATH_NR; j++) { + rtw89_debug(rtwdev, RTW89_DBG_RFK, + "[TSSI PA K] power[%d]=%d tssi_cw_rpt[%d]=%d\n", j, + power[j], j, tssi_cw_rpt[j]); + } + + tmp = rtw89_phy_read32_mask(rtwdev, _tssi_cw_default_addr[path][1], + _tssi_cw_default_mask[1]); + tssi_cw_default = sign_extend32(tmp, 8); + tssi_alim_offset_1 = tssi_cw_rpt[0] - ((power[0] - power[1]) * 2) - + tssi_cw_rpt[1] + tssi_cw_default; + aliment_diff = tssi_alim_offset_1 - tssi_cw_default; + + tmp = rtw89_phy_read32_mask(rtwdev, _tssi_cw_default_addr[path][2], + _tssi_cw_default_mask[2]); + tssi_cw_default = sign_extend32(tmp, 8); + tssi_alim_offset_2 = tssi_cw_default + aliment_diff; + + tmp = rtw89_phy_read32_mask(rtwdev, _tssi_cw_default_addr[path][3], + _tssi_cw_default_mask[3]); + tssi_cw_default = sign_extend32(tmp, 8); + tssi_alim_offset_3 = tssi_cw_default + aliment_diff; + + if (path == RF_PATH_A) { + tmp = FIELD_PREP(B_P1_TSSI_ALIM11, tssi_alim_offset_1) | + FIELD_PREP(B_P1_TSSI_ALIM12, tssi_alim_offset_2) | + FIELD_PREP(B_P1_TSSI_ALIM13, tssi_alim_offset_3); + + rtw89_phy_write32_mask(rtwdev, R_P0_TSSI_ALIM1, B_P0_TSSI_ALIM1, tmp); + rtw89_phy_write32_mask(rtwdev, R_P0_TSSI_ALIM2, B_P0_TSSI_ALIM2, tmp); + + rtw89_debug(rtwdev, RTW89_DBG_RFK, + "[TSSI PA K] tssi_alim_offset = 0x%x 0x%x 0x%x 0x%x\n", + rtw89_phy_read32_mask(rtwdev, R_P0_TSSI_ALIM3, B_P0_TSSI_ALIM31), + rtw89_phy_read32_mask(rtwdev, R_P0_TSSI_ALIM1, B_P0_TSSI_ALIM11), + rtw89_phy_read32_mask(rtwdev, R_P0_TSSI_ALIM1, B_P0_TSSI_ALIM12), + rtw89_phy_read32_mask(rtwdev, R_P0_TSSI_ALIM1, B_P0_TSSI_ALIM13)); + } else { + tmp = FIELD_PREP(B_P1_TSSI_ALIM11, tssi_alim_offset_1) | + FIELD_PREP(B_P1_TSSI_ALIM12, tssi_alim_offset_2) | + FIELD_PREP(B_P1_TSSI_ALIM13, tssi_alim_offset_3); + + rtw89_phy_write32_mask(rtwdev, R_P1_TSSI_ALIM1, B_P1_TSSI_ALIM1, tmp); + rtw89_phy_write32_mask(rtwdev, R_P1_TSSI_ALIM2, B_P1_TSSI_ALIM2, tmp); + + rtw89_debug(rtwdev, RTW89_DBG_RFK, + "[TSSI PA K] tssi_alim_offset = 0x%x 0x%x 0x%x 0x%x\n", + rtw89_phy_read32_mask(rtwdev, R_P1_TSSI_ALIM3, B_P1_TSSI_ALIM31), + rtw89_phy_read32_mask(rtwdev, R_P1_TSSI_ALIM1, B_P1_TSSI_ALIM11), + rtw89_phy_read32_mask(rtwdev, R_P1_TSSI_ALIM1, B_P1_TSSI_ALIM12), + rtw89_phy_read32_mask(rtwdev, R_P1_TSSI_ALIM1, B_P1_TSSI_ALIM13)); + } + + tssi_info->alignment_done[path][band] = true; + tssi_info->alignment_value[path][band][0] = + rtw89_phy_read32_mask(rtwdev, R_P0_TSSI_ALIM1 + (path << 13), MASKDWORD); + tssi_info->alignment_value[path][band][1] = + rtw89_phy_read32_mask(rtwdev, R_P0_TSSI_ALIM3 + (path << 13), MASKDWORD); + tssi_info->alignment_value[path][band][2] = + rtw89_phy_read32_mask(rtwdev, R_P0_TSSI_ALIM2 + (path << 13), MASKDWORD); + tssi_info->alignment_value[path][band][3] = + rtw89_phy_read32_mask(rtwdev, R_P0_TSSI_ALIM4 + (path << 13), MASKDWORD); + + tssi_info->check_backup_aligmk[path][ch_idx] = true; + tssi_info->alignment_backup_by_ch[path][ch_idx][0] = + rtw89_phy_read32_mask(rtwdev, R_P0_TSSI_ALIM1 + (path << 13), MASKDWORD); + tssi_info->alignment_backup_by_ch[path][ch_idx][1] = + rtw89_phy_read32_mask(rtwdev, R_P0_TSSI_ALIM3 + (path << 13), MASKDWORD); + tssi_info->alignment_backup_by_ch[path][ch_idx][2] = + rtw89_phy_read32_mask(rtwdev, R_P0_TSSI_ALIM2 + (path << 13), MASKDWORD); + tssi_info->alignment_backup_by_ch[path][ch_idx][3] = + rtw89_phy_read32_mask(rtwdev, R_P0_TSSI_ALIM4 + (path << 13), MASKDWORD); + + rtw89_debug(rtwdev, RTW89_DBG_RFK, + "[TSSI PA K] tssi_info->alignment_value[path=%d][band=%d][0], 0x%x = 0x%08x\n", + path, band, R_P0_TSSI_ALIM1 + (path << 13), + tssi_info->alignment_value[path][band][0]); + rtw89_debug(rtwdev, RTW89_DBG_RFK, + "[TSSI PA K] tssi_info->alignment_value[path=%d][band=%d][1], 0x%x = 0x%08x\n", + path, band, R_P0_TSSI_ALIM3 + (path << 13), + tssi_info->alignment_value[path][band][1]); + rtw89_debug(rtwdev, RTW89_DBG_RFK, + "[TSSI PA K] tssi_info->alignment_value[path=%d][band=%d][2], 0x%x = 0x%08x\n", + path, band, R_P0_TSSI_ALIM2 + (path << 13), + tssi_info->alignment_value[path][band][2]); + rtw89_debug(rtwdev, RTW89_DBG_RFK, + "[TSSI PA K] tssi_info->alignment_value[path=%d][band=%d][3], 0x%x = 0x%08x\n", + path, band, R_P0_TSSI_ALIM4 + (path << 13), + tssi_info->alignment_value[path][band][3]); + +out: + _tssi_reload_bb_registers(rtwdev, phy, bb_reg, bb_reg_backup, ARRAY_SIZE(bb_reg_backup)); + rtw8852b_bb_restore_tssi(rtwdev, phy, &tssi_bak); + rtw8852b_bb_tx_mode_switch(rtwdev, phy, 0); + + finish_time = ktime_get_ns(); + tssi_info->tssi_alimk_time += finish_time - start_time; + + rtw89_debug(rtwdev, RTW89_DBG_RFK, + "[TSSI PA K] %s processing time = %d ms\n", __func__, + tssi_info->tssi_alimk_time); +} + void rtw8852b_rck(struct rtw89_dev *rtwdev) { u8 path; @@ -1626,6 +2674,132 @@ void rtw8852b_rx_dck(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx) rtw89_btc_ntfy_wl_rfk(rtwdev, phy_map, BTC_WRFKT_RXDCK, BTC_WRFK_STOP); } +void rtw8852b_tssi(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy, bool hwtx_en) +{ + u8 phy_map = rtw89_btc_phymap(rtwdev, phy, RF_AB); + u32 tx_en; + u8 i; + + rtw89_debug(rtwdev, RTW89_DBG_TSSI, "[TSSI] %s: phy=%d\n", __func__, phy); + rtw89_btc_ntfy_wl_rfk(rtwdev, phy_map, BTC_WRFKT_IQK, BTC_WRFK_ONESHOT_START); + + _tssi_disable(rtwdev, phy); + + for (i = RF_PATH_A; i < RF_PATH_NUM_8852B; i++) { + _tssi_rf_setting(rtwdev, phy, i); + _tssi_set_sys(rtwdev, phy, i); + _tssi_ini_txpwr_ctrl_bb(rtwdev, phy, i); + _tssi_ini_txpwr_ctrl_bb_he_tb(rtwdev, phy, i); + _tssi_set_dck(rtwdev, phy, i); + _tssi_set_tmeter_tbl(rtwdev, phy, i); + _tssi_set_dac_gain_tbl(rtwdev, phy, i); + _tssi_slope_cal_org(rtwdev, phy, i); + _tssi_alignment_default(rtwdev, phy, i, true); + _tssi_set_tssi_slope(rtwdev, phy, i); + + rtw89_chip_stop_sch_tx(rtwdev, phy, &tx_en, RTW89_SCH_TX_SEL_ALL); + _tmac_tx_pause(rtwdev, phy, true); + if (hwtx_en) + _tssi_alimentk(rtwdev, phy, i); + _tmac_tx_pause(rtwdev, phy, false); + rtw89_chip_resume_sch_tx(rtwdev, phy, tx_en); + } + + _tssi_enable(rtwdev, phy); + _tssi_set_efuse_to_de(rtwdev, phy); + + rtw89_btc_ntfy_wl_rfk(rtwdev, phy_map, BTC_WRFKT_IQK, BTC_WRFK_ONESHOT_STOP); +} + +void rtw8852b_tssi_scan(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy) +{ + const struct rtw89_chan *chan = rtw89_chan_get(rtwdev, RTW89_SUB_ENTITY_0); + struct rtw89_tssi_info *tssi_info = &rtwdev->tssi; + u8 channel = chan->channel; + u8 band; + u32 i; + + rtw89_debug(rtwdev, RTW89_DBG_RFK, + "======>%s phy=%d channel=%d\n", __func__, phy, channel); + + if (channel >= 1 && channel <= 14) + band = TSSI_ALIMK_2G; + else if (channel >= 36 && channel <= 64) + band = TSSI_ALIMK_5GL; + else if (channel >= 100 && channel <= 144) + band = TSSI_ALIMK_5GM; + else if (channel >= 149 && channel <= 177) + band = TSSI_ALIMK_5GH; + else + band = TSSI_ALIMK_2G; + + _tssi_disable(rtwdev, phy); + + for (i = RF_PATH_A; i < RTW8852B_TSSI_PATH_NR; i++) { + _tssi_rf_setting(rtwdev, phy, i); + _tssi_set_sys(rtwdev, phy, i); + _tssi_set_tmeter_tbl(rtwdev, phy, i); + + if (tssi_info->alignment_done[i][band]) + _tssi_alimentk_done(rtwdev, phy, i); + else + _tssi_alignment_default(rtwdev, phy, i, true); + } + + _tssi_enable(rtwdev, phy); + _tssi_set_efuse_to_de(rtwdev, phy); +} + +static void rtw8852b_tssi_default_txagc(struct rtw89_dev *rtwdev, + enum rtw89_phy_idx phy, bool enable) +{ + const struct rtw89_chan *chan = rtw89_chan_get(rtwdev, RTW89_SUB_ENTITY_0); + u8 channel = chan->channel; + + rtw89_debug(rtwdev, RTW89_DBG_RFK, "======> %s ch=%d\n", + __func__, channel); + + if (enable) { + if (!rtwdev->is_tssi_mode[RF_PATH_A] && !rtwdev->is_tssi_mode[RF_PATH_B]) + rtw8852b_tssi(rtwdev, phy, true); + return; + } + + rtw89_debug(rtwdev, RTW89_DBG_RFK, + "======>%s 1 SCAN_END Set 0x5818[7:0]=0x%x 0x7818[7:0]=0x%x\n", + __func__, + rtw89_phy_read32_mask(rtwdev, R_P0_TSSI_TRK, B_P0_TSSI_OFT), + rtw89_phy_read32_mask(rtwdev, R_P1_TSSI_TRK, B_P1_TSSI_OFT)); + + rtw89_phy_write32_mask(rtwdev, R_P0_TSSI_TRK, B_P0_TSSI_OFT, 0xc0); + rtw89_phy_write32_mask(rtwdev, R_P1_TSSI_TRK, B_P1_TSSI_OFT, 0xc0); + rtw89_phy_write32_mask(rtwdev, R_P0_TSSI_TRK, B_P0_TSSI_OFT_EN, 0x0); + rtw89_phy_write32_mask(rtwdev, R_P0_TSSI_TRK, B_P0_TSSI_OFT_EN, 0x1); + rtw89_phy_write32_mask(rtwdev, R_P1_TSSI_TRK, B_P1_TSSI_OFT_EN, 0x0); + rtw89_phy_write32_mask(rtwdev, R_P1_TSSI_TRK, B_P1_TSSI_OFT_EN, 0x1); + + _tssi_alimentk_done(rtwdev, phy, RF_PATH_A); + _tssi_alimentk_done(rtwdev, phy, RF_PATH_B); + + rtw89_debug(rtwdev, RTW89_DBG_RFK, + "======>%s 2 SCAN_END Set 0x5818[7:0]=0x%x 0x7818[7:0]=0x%x\n", + __func__, + rtw89_phy_read32_mask(rtwdev, R_P0_TSSI_TRK, B_P0_TSSI_OFT), + rtw89_phy_read32_mask(rtwdev, R_P1_TSSI_TRK, B_P1_TSSI_OFT)); + + rtw89_debug(rtwdev, RTW89_DBG_RFK, + "======> %s SCAN_END\n", __func__); +} + +void rtw8852b_wifi_scan_notify(struct rtw89_dev *rtwdev, bool scan_start, + enum rtw89_phy_idx phy_idx) +{ + if (scan_start) + rtw8852b_tssi_default_txagc(rtwdev, phy_idx, true); + else + rtw8852b_tssi_default_txagc(rtwdev, phy_idx, false); +} + static void _bw_setting(struct rtw89_dev *rtwdev, enum rtw89_rf_path path, enum rtw89_bandwidth bw, bool dav) { diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852b_rfk.h b/drivers/net/wireless/realtek/rtw89/rtw8852b_rfk.h index 81eb9bddb6d7..e7402733d848 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852b_rfk.h +++ b/drivers/net/wireless/realtek/rtw89/rtw8852b_rfk.h @@ -11,6 +11,10 @@ void rtw8852b_rck(struct rtw89_dev *rtwdev); void rtw8852b_dack(struct rtw89_dev *rtwdev); void rtw8852b_iqk(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx); void rtw8852b_rx_dck(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx); +void rtw8852b_tssi(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy, bool hwtx_en); +void rtw8852b_tssi_scan(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy); +void rtw8852b_wifi_scan_notify(struct rtw89_dev *rtwdev, bool scan_start, + enum rtw89_phy_idx phy_idx); void rtw8852b_set_channel_rf(struct rtw89_dev *rtwdev, const struct rtw89_chan *chan, enum rtw89_phy_idx phy_idx); From 5b8471ace5b1247b2eb6a824341e11a8e871080f Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Fri, 14 Oct 2022 14:02:34 +0800 Subject: [PATCH 0172/2002] wifi: rtw89: 8852b: rfk: add DPK DPK is short for digital pre-distortion calibration. It can adjusts digital waveform according to PA linear characteristics dynamically to enhance TX EVM. Do this calibration when we are going to run on AP channel. To prevent power offset out of boundary, it monitors thermal and set proper boundary to register. Signed-off-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20221014060237.29050-2-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/core.h | 1 + drivers/net/wireless/realtek/rtw89/reg.h | 17 + .../net/wireless/realtek/rtw89/rtw8852b_rfk.c | 1148 +++++++++++++++++ .../net/wireless/realtek/rtw89/rtw8852b_rfk.h | 3 + 4 files changed, 1169 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index 856d9712f0ae..90bf7bdb6062 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -3049,6 +3049,7 @@ struct rtw89_dpk_bkup_para { struct rtw89_dpk_info { bool is_dpk_enable; bool is_dpk_reload_en; + u8 dpk_gs[RTW89_PHY_MAX]; u16 dc_i[RTW89_DPK_RF_PATH][RTW89_DPK_BKUP_NUM]; u16 dc_q[RTW89_DPK_RF_PATH][RTW89_DPK_BKUP_NUM]; u8 corr_val[RTW89_DPK_RF_PATH][RTW89_DPK_BKUP_NUM]; diff --git a/drivers/net/wireless/realtek/rtw89/reg.h b/drivers/net/wireless/realtek/rtw89/reg.h index 4393b409cbe6..2b938d11d238 100644 --- a/drivers/net/wireless/realtek/rtw89/reg.h +++ b/drivers/net/wireless/realtek/rtw89/reg.h @@ -3298,6 +3298,7 @@ #define RR_MOD_RGM GENMASK(13, 4) #define RR_MOD_V_DOWN 0x0 #define RR_MOD_V_STANDBY 0x1 +#define RR_TXAGC 0x10001 #define RR_MOD_V_TX 0x2 #define RR_MOD_V_RX 0x3 #define RR_MOD_V_TXIQK 0x4 @@ -3431,6 +3432,7 @@ #define RR_RXBB_VOBUF GENMASK(15, 12) #define RR_RXBB_C2G GENMASK(16, 10) #define RR_RXBB_C1G GENMASK(9, 8) +#define RR_RXBB_FATT GENMASK(7, 0) #define RR_RXBB_ATTR GENMASK(7, 4) #define RR_RXBB_ATTC GENMASK(2, 0) #define RR_RXG 0x84 @@ -3441,7 +3443,9 @@ #define RR_RXAE_IQKMOD GENMASK(3, 0) #define RR_RXA 0x8a #define RR_RXA_DPK GENMASK(9, 8) +#define RR_RXA_LNA 0x8b #define RR_RXA2 0x8c +#define RR_RAA2_SWATT GENMASK(15, 9) #define RR_RXA2_C1 GENMASK(12, 10) #define RR_RXA2_C2 GENMASK(9, 3) #define RR_RXA2_CC2 GENMASK(8, 7) @@ -3476,6 +3480,7 @@ #define RR_IQGEN_BIAS GENMASK(11, 8) #define RR_TXIQK 0x98 #define RR_TXIQK_ATT2 GENMASK(15, 12) +#define RR_TXIQK_ATT1 GENMASK(6, 0) #define RR_TIA 0x9e #define RR_TIA_N6 BIT(8) #define RR_MIXER 0x9f @@ -3851,6 +3856,9 @@ #define B_TXSHAPE_TRIANGULAR_CFG GENMASK(25, 24) #define R_BANDEDGE 0x4498 #define B_BANDEDGE_EN BIT(30) +#define R_DPD_BF 0x44a0 +#define B_DPD_BF_OFDM GENMASK(16, 12) +#define B_DPD_BF_SCA GENMASK(6, 0) #define R_TXPATH_SEL 0x458C #define B_TXPATH_SEL_MSK GENMASK(31, 28) #define R_TXPWR 0x4594 @@ -4297,6 +4305,7 @@ #define B_KPATH_CFG_ED GENMASK(21, 20) #define R_KIP_RPT1 0x80D4 #define B_KIP_RPT1_SEL GENMASK(21, 16) +#define B_KIP_RPT1_SEL_V1 GENMASK(19, 16) #define R_SRAM_IQRX 0x80D8 #define R_GAPK 0x80E0 #define B_GAPK_ADR BIT(0) @@ -4318,6 +4327,7 @@ #define B_PRT_COM_GL GENMASK(7, 4) #define B_PRT_COM_CORI GENMASK(7, 0) #define B_PRT_COM_RXBB GENMASK(5, 0) +#define B_PRT_COM_RXBB_V1 GENMASK(4, 0) #define B_PRT_COM_DONE BIT(0) #define R_COEF_SEL 0x8104 #define B_COEF_SEL_IQC BIT(0) @@ -4356,13 +4366,18 @@ #define B_DPD_LBK BIT(7) #define R_DPD_CH0 0x81AC #define R_DPD_BND 0x81B4 +#define B_DPD_BND_1 GENMASK(24, 16) +#define B_DPD_BND_0 GENMASK(8, 0) #define R_DPD_CH0A 0x81BC #define B_DPD_MEN GENMASK(31, 28) #define B_DPD_ORDER GENMASK(26, 24) +#define B_DPD_ORDER_V1 GENMASK(26, 25) +#define B_DPD_CFG GENMASK(22, 0) #define B_DPD_SEL GENMASK(13, 8) #define R_TXAGC_RFK 0x81C4 #define B_TXAGC_RFK_CH0 GENMASK(5, 0) #define R_DPD_COM 0x81C8 +#define B_DPD_COM_OF BIT(15) #define R_KIP_IQP 0x81CC #define B_KIP_IQP_SW GENMASK(13, 12) #define B_KIP_IQP_IQSW GENMASK(5, 0) @@ -4462,6 +4477,7 @@ #define R_P0_CFCH_BW0 0xC0D4 #define B_P0_CFCH_BW0 GENMASK(27, 26) #define R_P0_CFCH_BW1 0xC0D8 +#define B_P0_CFCH_EX BIT(13) #define B_P0_CFCH_BW1 GENMASK(8, 5) #define R_ADDCK0D 0xC0F0 #define B_ADDCK0D_VAL2 GENMASK(31, 26) @@ -4511,6 +4527,7 @@ #define R_PATH0_BW_SEL_V1 0xC0D8 #define B_PATH0_BW_SEL_MSK_V1 GENMASK(8, 5) #define R_PATH1_BW_SEL_V1 0xC1D8 +#define B_PATH1_BW_SEL_EX BIT(13) #define B_PATH1_BW_SEL_MSK_V1 GENMASK(8, 5) #define R_ADDCK1D 0xC1F0 #define B_ADDCK1D_VAL2 GENMASK(31, 26) diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852b_rfk.c b/drivers/net/wireless/realtek/rtw89/rtw8852b_rfk.c index d0ac883772c1..8fd01502ac5b 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852b_rfk.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852b_rfk.c @@ -17,9 +17,49 @@ #define RTW8852B_IQK_SS 2 #define RTW8852B_RXK_GROUP_NR 4 #define RTW8852B_TSSI_PATH_NR 2 +#define RTW8852B_RF_REL_VERSION 34 +#define RTW8852B_DPK_VER 0x0d +#define RTW8852B_DPK_RF_PATH 2 +#define RTW8852B_DPK_KIP_REG_NUM 2 #define _TSSI_DE_MASK GENMASK(21, 12) #define ADDC_T_AVG 100 +#define DPK_TXAGC_LOWER 0x2e +#define DPK_TXAGC_UPPER 0x3f +#define DPK_TXAGC_INVAL 0xff +#define RFREG_MASKRXBB 0x003e0 +#define RFREG_MASKMODE 0xf0000 + +enum rtw8852b_dpk_id { + LBK_RXIQK = 0x06, + SYNC = 0x10, + MDPK_IDL = 0x11, + MDPK_MPA = 0x12, + GAIN_LOSS = 0x13, + GAIN_CAL = 0x14, + DPK_RXAGC = 0x15, + KIP_PRESET = 0x16, + KIP_RESTORE = 0x17, + DPK_TXAGC = 0x19, + D_KIP_PRESET = 0x28, + D_TXAGC = 0x29, + D_RXAGC = 0x2a, + D_SYNC = 0x2b, + D_GAIN_LOSS = 0x2c, + D_MDPK_IDL = 0x2d, + D_GAIN_NORM = 0x2f, + D_KIP_THERMAL = 0x30, + D_KIP_RESTORE = 0x31 +}; + +enum dpk_agc_step { + DPK_AGC_STEP_SYNC_DGAIN, + DPK_AGC_STEP_GAIN_ADJ, + DPK_AGC_STEP_GAIN_LOSS_IDX, + DPK_AGC_STEP_GL_GT_CRITERION, + DPK_AGC_STEP_GL_LT_CRITERION, + DPK_AGC_STEP_SET_TX_GAIN, +}; enum rtw8852b_iqk_type { ID_TXAGC = 0x0, @@ -190,6 +230,24 @@ static void _rfk_restore_rf_reg(struct rtw89_dev *rtwdev, } } +static void _rfk_rf_direct_cntrl(struct rtw89_dev *rtwdev, + enum rtw89_rf_path path, bool is_bybb) +{ + if (is_bybb) + rtw89_write_rf(rtwdev, path, RR_RSV1, RR_RSV1_RST, 0x1); + else + rtw89_write_rf(rtwdev, path, RR_RSV1, RR_RSV1_RST, 0x0); +} + +static void _rfk_drf_direct_cntrl(struct rtw89_dev *rtwdev, + enum rtw89_rf_path path, bool is_bybb) +{ + if (is_bybb) + rtw89_write_rf(rtwdev, path, RR_BBDC, RR_BBDC_SEL, 0x1); + else + rtw89_write_rf(rtwdev, path, RR_BBDC, RR_BBDC_SEL, 0x0); +} + static bool _iqk_check_cal(struct rtw89_dev *rtwdev, u8 path) { bool fail = true; @@ -1605,6 +1663,1069 @@ static void _iqk(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx, bool forc } } +static void _dpk_bkup_kip(struct rtw89_dev *rtwdev, const u32 reg[], + u32 reg_bkup[][RTW8852B_DPK_KIP_REG_NUM], u8 path) +{ + u8 i; + + for (i = 0; i < RTW8852B_DPK_KIP_REG_NUM; i++) { + reg_bkup[path][i] = + rtw89_phy_read32_mask(rtwdev, reg[i] + (path << 8), MASKDWORD); + rtw89_debug(rtwdev, RTW89_DBG_RFK, "[DPK] Backup 0x%x = %x\n", + reg[i] + (path << 8), reg_bkup[path][i]); + } +} + +static void _dpk_reload_kip(struct rtw89_dev *rtwdev, const u32 reg[], + const u32 reg_bkup[][RTW8852B_DPK_KIP_REG_NUM], u8 path) +{ + u8 i; + + for (i = 0; i < RTW8852B_DPK_KIP_REG_NUM; i++) { + rtw89_phy_write32_mask(rtwdev, reg[i] + (path << 8), MASKDWORD, + reg_bkup[path][i]); + rtw89_debug(rtwdev, RTW89_DBG_RFK, "[DPK] Reload 0x%x = %x\n", + reg[i] + (path << 8), reg_bkup[path][i]); + } +} + +static u8 _dpk_order_convert(struct rtw89_dev *rtwdev) +{ + u8 order; + u8 val; + + order = rtw89_phy_read32_mask(rtwdev, R_LDL_NORM, B_LDL_NORM_OP); + val = 0x3 >> order; + + rtw89_debug(rtwdev, RTW89_DBG_RFK, "[DPK] convert MDPD order to 0x%x\n", val); + + return val; +} + +static void _dpk_onoff(struct rtw89_dev *rtwdev, enum rtw89_rf_path path, bool off) +{ + struct rtw89_dpk_info *dpk = &rtwdev->dpk; + u8 val, kidx = dpk->cur_idx[path]; + + val = dpk->is_dpk_enable && !off && dpk->bp[path][kidx].path_ok; + + rtw89_phy_write32_mask(rtwdev, R_DPD_CH0A + (path << 8) + (kidx << 2), + MASKBYTE3, _dpk_order_convert(rtwdev) << 1 | val); + + rtw89_debug(rtwdev, RTW89_DBG_RFK, "[DPK] S%d[%d] DPK %s !!!\n", path, + kidx, dpk->is_dpk_enable && !off ? "enable" : "disable"); +} + +static void _dpk_one_shot(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy, + enum rtw89_rf_path path, enum rtw8852b_dpk_id id) +{ + u16 dpk_cmd; + u32 val; + int ret; + + dpk_cmd = (id << 8) | (0x19 + (path << 4)); + rtw89_phy_write32_mask(rtwdev, R_NCTL_CFG, MASKDWORD, dpk_cmd); + + ret = read_poll_timeout_atomic(rtw89_phy_read32_mask, val, val == 0x55, + 1, 20000, false, + rtwdev, 0xbff8, MASKBYTE0); + if (ret) + rtw89_debug(rtwdev, RTW89_DBG_RFK, "[DPK] one-shot over 20ms!!!!\n"); + + udelay(1); + + rtw89_phy_write32_mask(rtwdev, R_KIP_RPT1, MASKDWORD, 0x00030000); + + ret = read_poll_timeout_atomic(rtw89_phy_read32_mask, val, val == 0x8000, + 1, 2000, false, + rtwdev, 0x80fc, MASKLWORD); + if (ret) + rtw89_debug(rtwdev, RTW89_DBG_RFK, "[DPK] one-shot over 20ms!!!!\n"); + + rtw89_phy_write32_mask(rtwdev, R_NCTL_N1, MASKBYTE0, 0x0); + + rtw89_debug(rtwdev, RTW89_DBG_RFK, + "[DPK] one-shot for %s = 0x%x\n", + id == 0x06 ? "LBK_RXIQK" : + id == 0x10 ? "SYNC" : + id == 0x11 ? "MDPK_IDL" : + id == 0x12 ? "MDPK_MPA" : + id == 0x13 ? "GAIN_LOSS" : + id == 0x14 ? "PWR_CAL" : + id == 0x15 ? "DPK_RXAGC" : + id == 0x16 ? "KIP_PRESET" : + id == 0x17 ? "KIP_RESOTRE" : "DPK_TXAGC", + dpk_cmd); +} + +static void _dpk_rx_dck(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy, + enum rtw89_rf_path path) +{ + rtw89_write_rf(rtwdev, path, RR_RXBB2, RR_EN_TIA_IDA, 0x3); + _set_rx_dck(rtwdev, phy, path); +} + +static void _dpk_information(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy, + enum rtw89_rf_path path) +{ + const struct rtw89_chan *chan = rtw89_chan_get(rtwdev, RTW89_SUB_ENTITY_0); + struct rtw89_dpk_info *dpk = &rtwdev->dpk; + + u8 kidx = dpk->cur_idx[path]; + + dpk->bp[path][kidx].band = chan->band_type; + dpk->bp[path][kidx].ch = chan->channel; + dpk->bp[path][kidx].bw = chan->band_width; + + rtw89_debug(rtwdev, RTW89_DBG_RFK, + "[DPK] S%d[%d] (PHY%d): TSSI %s/ DBCC %s/ %s/ CH%d/ %s\n", + path, dpk->cur_idx[path], phy, + rtwdev->is_tssi_mode[path] ? "on" : "off", + rtwdev->dbcc_en ? "on" : "off", + dpk->bp[path][kidx].band == 0 ? "2G" : + dpk->bp[path][kidx].band == 1 ? "5G" : "6G", + dpk->bp[path][kidx].ch, + dpk->bp[path][kidx].bw == 0 ? "20M" : + dpk->bp[path][kidx].bw == 1 ? "40M" : "80M"); +} + +static void _dpk_bb_afe_setting(struct rtw89_dev *rtwdev, + enum rtw89_phy_idx phy, + enum rtw89_rf_path path, u8 kpath) +{ + const struct rtw89_chan *chan = rtw89_chan_get(rtwdev, RTW89_SUB_ENTITY_0); + + rtw89_rfk_parser(rtwdev, &rtw8852b_dpk_afe_defs_tbl); + + if (chan->band_width == RTW89_CHANNEL_WIDTH_80) { + rtw89_phy_write32_mask(rtwdev, R_P0_CFCH_BW1, B_P0_CFCH_EX, 0x1); + rtw89_phy_write32_mask(rtwdev, R_PATH1_BW_SEL_V1, B_PATH1_BW_SEL_EX, 0x1); + } + + rtw89_debug(rtwdev, RTW89_DBG_RFK, + "[DPK] Set BB/AFE for PHY%d (kpath=%d)\n", phy, kpath); +} + +static void _dpk_bb_afe_restore(struct rtw89_dev *rtwdev, + enum rtw89_phy_idx phy, + enum rtw89_rf_path path, u8 kpath) +{ + const struct rtw89_chan *chan = rtw89_chan_get(rtwdev, RTW89_SUB_ENTITY_0); + + rtw89_rfk_parser(rtwdev, &rtw8852b_dpk_afe_restore_defs_tbl); + + rtw89_debug(rtwdev, RTW89_DBG_RFK, + "[DPK] Restore BB/AFE for PHY%d (kpath=%d)\n", phy, kpath); + + if (chan->band_width == RTW89_CHANNEL_WIDTH_80) { + rtw89_phy_write32_mask(rtwdev, R_P0_CFCH_BW1, B_P0_CFCH_EX, 0x0); + rtw89_phy_write32_mask(rtwdev, R_PATH1_BW_SEL_V1, B_PATH1_BW_SEL_EX, 0x0); + } +} + +static void _dpk_tssi_pause(struct rtw89_dev *rtwdev, + enum rtw89_rf_path path, bool is_pause) +{ + rtw89_phy_write32_mask(rtwdev, R_P0_TSSI_TRK + (path << 13), + B_P0_TSSI_TRK_EN, is_pause); + + rtw89_debug(rtwdev, RTW89_DBG_RFK, "[DPK] S%d TSSI %s\n", path, + is_pause ? "pause" : "resume"); +} + +static void _dpk_kip_restore(struct rtw89_dev *rtwdev, + enum rtw89_rf_path path) +{ + rtw89_rfk_parser(rtwdev, &rtw8852b_dpk_kip_defs_tbl); + + if (rtwdev->hal.cv > CHIP_CAV) + rtw89_phy_write32_mask(rtwdev, R_DPD_COM + (path << 8), B_DPD_COM_OF, 0x1); + + rtw89_debug(rtwdev, RTW89_DBG_RFK, "[DPK] S%d restore KIP\n", path); +} + +static void _dpk_lbk_rxiqk(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy, + enum rtw89_rf_path path) +{ + u8 cur_rxbb; + u32 tmp; + + cur_rxbb = rtw89_read_rf(rtwdev, path, RR_MOD, RFREG_MASKRXBB); + + rtw89_phy_write32_mask(rtwdev, R_MDPK_RX_DCK, B_MDPK_RX_DCK_EN, 0x1); + rtw89_phy_write32_mask(rtwdev, R_IQK_RES + (path << 8), B_IQK_RES_RXCFIR, 0x0); + + tmp = rtw89_read_rf(rtwdev, path, RR_CFGCH, RFREG_MASK); + rtw89_write_rf(rtwdev, path, RR_RSV4, RFREG_MASK, tmp); + rtw89_write_rf(rtwdev, path, RR_MOD, RFREG_MASKMODE, 0xd); + rtw89_write_rf(rtwdev, path, RR_RXK, RR_RXK_PLLEN, 0x1); + + if (cur_rxbb >= 0x11) + rtw89_write_rf(rtwdev, path, RR_TXIQK, RR_TXIQK_ATT1, 0x13); + else if (cur_rxbb <= 0xa) + rtw89_write_rf(rtwdev, path, RR_TXIQK, RR_TXIQK_ATT1, 0x00); + else + rtw89_write_rf(rtwdev, path, RR_TXIQK, RR_TXIQK_ATT1, 0x05); + + rtw89_write_rf(rtwdev, path, RR_XGLNA2, RR_XGLNA2_SW, 0x0); + rtw89_write_rf(rtwdev, path, RR_RXKPLL, RR_RXKPLL_POW, 0x0); + rtw89_write_rf(rtwdev, path, RR_RXKPLL, RFREG_MASK, 0x80014); + udelay(70); + + rtw89_phy_write32_mask(rtwdev, R_P0_RFCTM, B_P0_RFCTM_EN, 0x1); + rtw89_phy_write32_mask(rtwdev, R_IQK_DIF4, B_IQK_DIF4_RXT, 0x025); + + _dpk_one_shot(rtwdev, phy, path, LBK_RXIQK); + + rtw89_debug(rtwdev, RTW89_DBG_RFK, "[DPK] S%d LBK RXIQC = 0x%x\n", path, + rtw89_phy_read32_mask(rtwdev, R_RXIQC, MASKDWORD)); + + rtw89_phy_write32_mask(rtwdev, R_P0_RFCTM, B_P0_RFCTM_EN, 0x0); + rtw89_write_rf(rtwdev, path, RR_RXK, RR_RXK_PLLEN, 0x0); + rtw89_phy_write32_mask(rtwdev, R_MDPK_RX_DCK, B_MDPK_RX_DCK_EN, 0x0); + rtw89_phy_write32_mask(rtwdev, R_KPATH_CFG, B_KPATH_CFG_ED, 0x0); + rtw89_phy_write32_mask(rtwdev, R_LOAD_COEF + (path << 8), B_LOAD_COEF_DI, 0x1); + rtw89_write_rf(rtwdev, path, RR_MOD, RFREG_MASKMODE, 0x5); +} + +static void _dpk_get_thermal(struct rtw89_dev *rtwdev, u8 kidx, enum rtw89_rf_path path) +{ + struct rtw89_dpk_info *dpk = &rtwdev->dpk; + + rtw89_write_rf(rtwdev, path, RR_TM, RR_TM_TRI, 0x1); + rtw89_write_rf(rtwdev, path, RR_TM, RR_TM_TRI, 0x0); + rtw89_write_rf(rtwdev, path, RR_TM, RR_TM_TRI, 0x1); + + udelay(200); + + dpk->bp[path][kidx].ther_dpk = rtw89_read_rf(rtwdev, path, RR_TM, RR_TM_VAL); + + rtw89_debug(rtwdev, RTW89_DBG_RFK, "[DPK] thermal@DPK = 0x%x\n", + dpk->bp[path][kidx].ther_dpk); +} + +static void _dpk_rf_setting(struct rtw89_dev *rtwdev, u8 gain, + enum rtw89_rf_path path, u8 kidx) +{ + struct rtw89_dpk_info *dpk = &rtwdev->dpk; + + if (dpk->bp[path][kidx].band == RTW89_BAND_2G) { + rtw89_write_rf(rtwdev, path, RR_MOD, RFREG_MASK, 0x50220); + rtw89_write_rf(rtwdev, path, RR_RXBB, RR_RXBB_FATT, 0xf2); + rtw89_write_rf(rtwdev, path, RR_LUTDBG, RR_LUTDBG_TIA, 0x1); + rtw89_write_rf(rtwdev, path, RR_TIA, RR_TIA_N6, 0x1); + } else { + rtw89_write_rf(rtwdev, path, RR_MOD, RFREG_MASK, 0x50220); + rtw89_write_rf(rtwdev, path, RR_RXA2, RR_RAA2_SWATT, 0x5); + rtw89_write_rf(rtwdev, path, RR_LUTDBG, RR_LUTDBG_TIA, 0x1); + rtw89_write_rf(rtwdev, path, RR_TIA, RR_TIA_N6, 0x1); + rtw89_write_rf(rtwdev, path, RR_RXA_LNA, RFREG_MASK, 0x920FC); + rtw89_write_rf(rtwdev, path, RR_XALNA2, RFREG_MASK, 0x002C0); + rtw89_write_rf(rtwdev, path, RR_IQGEN, RFREG_MASK, 0x38800); + } + + rtw89_write_rf(rtwdev, path, RR_RCKD, RR_RCKD_BW, 0x1); + rtw89_write_rf(rtwdev, path, RR_BTC, RR_BTC_TXBB, dpk->bp[path][kidx].bw + 1); + rtw89_write_rf(rtwdev, path, RR_BTC, RR_BTC_RXBB, 0x0); + + rtw89_debug(rtwdev, RTW89_DBG_RFK, + "[DPK] ARF 0x0/0x11/0x1a = 0x%x/ 0x%x/ 0x%x\n", + rtw89_read_rf(rtwdev, path, RR_MOD, RFREG_MASK), + rtw89_read_rf(rtwdev, path, RR_TXIG, RFREG_MASK), + rtw89_read_rf(rtwdev, path, RR_BTC, RFREG_MASK)); +} + +static void _dpk_bypass_rxcfir(struct rtw89_dev *rtwdev, + enum rtw89_rf_path path, bool is_bypass) +{ + if (is_bypass) { + rtw89_phy_write32_mask(rtwdev, R_RXIQC + (path << 8), + B_RXIQC_BYPASS2, 0x1); + rtw89_phy_write32_mask(rtwdev, R_RXIQC + (path << 8), + B_RXIQC_BYPASS, 0x1); + rtw89_debug(rtwdev, RTW89_DBG_RFK, + "[DPK] Bypass RXIQC (0x8%d3c = 0x%x)\n", 1 + path, + rtw89_phy_read32_mask(rtwdev, R_RXIQC + (path << 8), + MASKDWORD)); + } else { + rtw89_phy_write32_clr(rtwdev, R_RXIQC + (path << 8), B_RXIQC_BYPASS2); + rtw89_phy_write32_clr(rtwdev, R_RXIQC + (path << 8), B_RXIQC_BYPASS); + rtw89_debug(rtwdev, RTW89_DBG_RFK, + "[DPK] restore 0x8%d3c = 0x%x\n", 1 + path, + rtw89_phy_read32_mask(rtwdev, R_RXIQC + (path << 8), + MASKDWORD)); + } +} + +static +void _dpk_tpg_sel(struct rtw89_dev *rtwdev, enum rtw89_rf_path path, u8 kidx) +{ + struct rtw89_dpk_info *dpk = &rtwdev->dpk; + + if (dpk->bp[path][kidx].bw == RTW89_CHANNEL_WIDTH_80) + rtw89_phy_write32_clr(rtwdev, R_TPG_MOD, B_TPG_MOD_F); + else if (dpk->bp[path][kidx].bw == RTW89_CHANNEL_WIDTH_40) + rtw89_phy_write32_mask(rtwdev, R_TPG_MOD, B_TPG_MOD_F, 0x2); + else + rtw89_phy_write32_mask(rtwdev, R_TPG_MOD, B_TPG_MOD_F, 0x1); + + rtw89_debug(rtwdev, RTW89_DBG_RFK, "[DPK] TPG_Select for %s\n", + dpk->bp[path][kidx].bw == RTW89_CHANNEL_WIDTH_80 ? "80M" : + dpk->bp[path][kidx].bw == RTW89_CHANNEL_WIDTH_40 ? "40M" : "20M"); +} + +static void _dpk_table_select(struct rtw89_dev *rtwdev, + enum rtw89_rf_path path, u8 kidx, u8 gain) +{ + u8 val; + + val = 0x80 + kidx * 0x20 + gain * 0x10; + rtw89_phy_write32_mask(rtwdev, R_DPD_CH0 + (path << 8), MASKBYTE3, val); + rtw89_debug(rtwdev, RTW89_DBG_RFK, + "[DPK] table select for Kidx[%d], Gain[%d] (0x%x)\n", kidx, + gain, val); +} + +static bool _dpk_sync_check(struct rtw89_dev *rtwdev, enum rtw89_rf_path path, u8 kidx) +{ +#define DPK_SYNC_TH_DC_I 200 +#define DPK_SYNC_TH_DC_Q 200 +#define DPK_SYNC_TH_CORR 170 + struct rtw89_dpk_info *dpk = &rtwdev->dpk; + u16 dc_i, dc_q; + u8 corr_val, corr_idx; + + rtw89_phy_write32_clr(rtwdev, R_KIP_RPT1, B_KIP_RPT1_SEL); + + corr_idx = rtw89_phy_read32_mask(rtwdev, R_RPT_COM, B_PRT_COM_CORI); + corr_val = rtw89_phy_read32_mask(rtwdev, R_RPT_COM, B_PRT_COM_CORV); + + rtw89_debug(rtwdev, RTW89_DBG_RFK, + "[DPK] S%d Corr_idx / Corr_val = %d / %d\n", + path, corr_idx, corr_val); + + dpk->corr_idx[path][kidx] = corr_idx; + dpk->corr_val[path][kidx] = corr_val; + + rtw89_phy_write32_mask(rtwdev, R_KIP_RPT1, B_KIP_RPT1_SEL, 0x9); + + dc_i = rtw89_phy_read32_mask(rtwdev, R_RPT_COM, B_PRT_COM_DCI); + dc_q = rtw89_phy_read32_mask(rtwdev, R_RPT_COM, B_PRT_COM_DCQ); + + dc_i = abs(sign_extend32(dc_i, 11)); + dc_q = abs(sign_extend32(dc_q, 11)); + + rtw89_debug(rtwdev, RTW89_DBG_RFK, "[DPK] S%d DC I/Q, = %d / %d\n", + path, dc_i, dc_q); + + dpk->dc_i[path][kidx] = dc_i; + dpk->dc_q[path][kidx] = dc_q; + + if (dc_i > DPK_SYNC_TH_DC_I || dc_q > DPK_SYNC_TH_DC_Q || + corr_val < DPK_SYNC_TH_CORR) + return true; + else + return false; +} + +static bool _dpk_sync(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy, + enum rtw89_rf_path path, u8 kidx) +{ + _dpk_one_shot(rtwdev, phy, path, SYNC); + + return _dpk_sync_check(rtwdev, path, kidx); +} + +static u16 _dpk_dgain_read(struct rtw89_dev *rtwdev) +{ + u16 dgain; + + rtw89_phy_write32_mask(rtwdev, R_KIP_RPT1, B_KIP_RPT1_SEL, 0x0); + + dgain = rtw89_phy_read32_mask(rtwdev, R_RPT_COM, B_PRT_COM_DCI); + + rtw89_debug(rtwdev, RTW89_DBG_RFK, "[DPK] DGain = 0x%x\n", dgain); + + return dgain; +} + +static s8 _dpk_dgain_mapping(struct rtw89_dev *rtwdev, u16 dgain) +{ + static const u16 bnd[15] = { + 0xbf1, 0xaa5, 0x97d, 0x875, 0x789, 0x6b7, 0x5fc, 0x556, + 0x4c1, 0x43d, 0x3c7, 0x35e, 0x2ac, 0x262, 0x220 + }; + s8 offset; + + if (dgain >= bnd[0]) + offset = 0x6; + else if (bnd[0] > dgain && dgain >= bnd[1]) + offset = 0x6; + else if (bnd[1] > dgain && dgain >= bnd[2]) + offset = 0x5; + else if (bnd[2] > dgain && dgain >= bnd[3]) + offset = 0x4; + else if (bnd[3] > dgain && dgain >= bnd[4]) + offset = 0x3; + else if (bnd[4] > dgain && dgain >= bnd[5]) + offset = 0x2; + else if (bnd[5] > dgain && dgain >= bnd[6]) + offset = 0x1; + else if (bnd[6] > dgain && dgain >= bnd[7]) + offset = 0x0; + else if (bnd[7] > dgain && dgain >= bnd[8]) + offset = 0xff; + else if (bnd[8] > dgain && dgain >= bnd[9]) + offset = 0xfe; + else if (bnd[9] > dgain && dgain >= bnd[10]) + offset = 0xfd; + else if (bnd[10] > dgain && dgain >= bnd[11]) + offset = 0xfc; + else if (bnd[11] > dgain && dgain >= bnd[12]) + offset = 0xfb; + else if (bnd[12] > dgain && dgain >= bnd[13]) + offset = 0xfa; + else if (bnd[13] > dgain && dgain >= bnd[14]) + offset = 0xf9; + else if (bnd[14] > dgain) + offset = 0xf8; + else + offset = 0x0; + + rtw89_debug(rtwdev, RTW89_DBG_RFK, "[DPK] DGain offset = %d\n", offset); + + return offset; +} + +static u8 _dpk_gainloss_read(struct rtw89_dev *rtwdev) +{ + rtw89_phy_write32_mask(rtwdev, R_KIP_RPT1, B_KIP_RPT1_SEL, 0x6); + rtw89_phy_write32_mask(rtwdev, R_DPK_CFG2, B_DPK_CFG2_ST, 0x1); + + return rtw89_phy_read32_mask(rtwdev, R_RPT_COM, B_PRT_COM_GL); +} + +static void _dpk_gainloss(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy, + enum rtw89_rf_path path, u8 kidx) +{ + _dpk_table_select(rtwdev, path, kidx, 1); + _dpk_one_shot(rtwdev, phy, path, GAIN_LOSS); +} + +static void _dpk_kip_preset(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy, + enum rtw89_rf_path path, u8 kidx) +{ + _dpk_tpg_sel(rtwdev, path, kidx); + _dpk_one_shot(rtwdev, phy, path, KIP_PRESET); +} + +static void _dpk_kip_pwr_clk_on(struct rtw89_dev *rtwdev, + enum rtw89_rf_path path) +{ + rtw89_phy_write32_mask(rtwdev, R_NCTL_RPT, MASKDWORD, 0x00000080); + rtw89_phy_write32_mask(rtwdev, R_KIP_SYSCFG, MASKDWORD, 0x807f030a); + rtw89_phy_write32_mask(rtwdev, R_CFIR_SYS + (path << 8), MASKDWORD, 0xce000a08); + + rtw89_debug(rtwdev, RTW89_DBG_RFK, "[DPK] KIP Power/CLK on\n"); +} + +static void _dpk_kip_set_txagc(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy, + enum rtw89_rf_path path, u8 txagc) +{ + rtw89_write_rf(rtwdev, path, RR_TXAGC, RFREG_MASK, txagc); + rtw89_phy_write32_mask(rtwdev, R_P0_RFCTM, B_P0_RFCTM_EN, 0x1); + _dpk_one_shot(rtwdev, phy, path, DPK_TXAGC); + rtw89_phy_write32_mask(rtwdev, R_P0_RFCTM, B_P0_RFCTM_EN, 0x0); + + rtw89_debug(rtwdev, RTW89_DBG_RFK, "[DPK] set TXAGC = 0x%x\n", txagc); +} + +static void _dpk_kip_set_rxagc(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy, + enum rtw89_rf_path path) +{ + u32 tmp; + + tmp = rtw89_read_rf(rtwdev, path, RR_MOD, RFREG_MASK); + rtw89_phy_write32_mask(rtwdev, R_KIP_MOD, B_KIP_MOD, tmp); + rtw89_phy_write32_mask(rtwdev, R_P0_RFCTM, B_P0_RFCTM_EN, 0x1); + _dpk_one_shot(rtwdev, phy, path, DPK_RXAGC); + rtw89_phy_write32_mask(rtwdev, R_P0_RFCTM, B_P0_RFCTM_EN, 0x0); + rtw89_phy_write32_mask(rtwdev, R_KIP_RPT1, B_KIP_RPT1_SEL_V1, 0x8); + + rtw89_debug(rtwdev, RTW89_DBG_RFK, + "[DPK] set RXBB = 0x%x (RF0x0[9:5] = 0x%x)\n", + rtw89_phy_read32_mask(rtwdev, R_RPT_COM, B_PRT_COM_RXBB_V1), + rtw89_read_rf(rtwdev, path, RR_MOD, RFREG_MASKRXBB)); +} + +static u8 _dpk_set_offset(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy, + enum rtw89_rf_path path, s8 gain_offset) +{ + u8 txagc; + + txagc = rtw89_read_rf(rtwdev, path, RR_TXAGC, RFREG_MASK); + + if (txagc - gain_offset < DPK_TXAGC_LOWER) + txagc = DPK_TXAGC_LOWER; + else if (txagc - gain_offset > DPK_TXAGC_UPPER) + txagc = DPK_TXAGC_UPPER; + else + txagc = txagc - gain_offset; + + _dpk_kip_set_txagc(rtwdev, phy, path, txagc); + + rtw89_debug(rtwdev, RTW89_DBG_RFK, "[DPK] tmp_txagc (GL=%d) = 0x%x\n", + gain_offset, txagc); + return txagc; +} + +static bool _dpk_pas_read(struct rtw89_dev *rtwdev, bool is_check) +{ + u32 val1_i = 0, val1_q = 0, val2_i = 0, val2_q = 0; + u8 i; + + rtw89_phy_write32_mask(rtwdev, R_KIP_RPT1, MASKBYTE2, 0x06); + rtw89_phy_write32_mask(rtwdev, R_DPK_CFG2, B_DPK_CFG2_ST, 0x0); + rtw89_phy_write32_mask(rtwdev, R_DPK_CFG3, MASKBYTE2, 0x08); + + if (is_check) { + rtw89_phy_write32_mask(rtwdev, R_DPK_CFG3, MASKBYTE3, 0x00); + val1_i = rtw89_phy_read32_mask(rtwdev, R_RPT_COM, MASKHWORD); + val1_i = abs(sign_extend32(val1_i, 11)); + val1_q = rtw89_phy_read32_mask(rtwdev, R_RPT_COM, MASKLWORD); + val1_q = abs(sign_extend32(val1_q, 11)); + + rtw89_phy_write32_mask(rtwdev, R_DPK_CFG3, MASKBYTE3, 0x1f); + val2_i = rtw89_phy_read32_mask(rtwdev, R_RPT_COM, MASKHWORD); + val2_i = abs(sign_extend32(val2_i, 11)); + val2_q = rtw89_phy_read32_mask(rtwdev, R_RPT_COM, MASKLWORD); + val2_q = abs(sign_extend32(val2_q, 11)); + + rtw89_debug(rtwdev, RTW89_DBG_RFK, "[DPK] PAS_delta = 0x%x\n", + phy_div(val1_i * val1_i + val1_q * val1_q, + val2_i * val2_i + val2_q * val2_q)); + } else { + for (i = 0; i < 32; i++) { + rtw89_phy_write32_mask(rtwdev, R_DPK_CFG3, MASKBYTE3, i); + rtw89_debug(rtwdev, RTW89_DBG_RFK, + "[DPK] PAS_Read[%02d]= 0x%08x\n", i, + rtw89_phy_read32_mask(rtwdev, R_RPT_COM, MASKDWORD)); + } + } + + if (val1_i * val1_i + val1_q * val1_q >= + (val2_i * val2_i + val2_q * val2_q) * 8 / 5) + return true; + + return false; +} + +static u8 _dpk_agc(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy, + enum rtw89_rf_path path, u8 kidx, u8 init_txagc, + bool loss_only) +{ + const struct rtw89_chan *chan = rtw89_chan_get(rtwdev, RTW89_SUB_ENTITY_0); + u8 step = DPK_AGC_STEP_SYNC_DGAIN; + u8 tmp_txagc, tmp_rxbb = 0, tmp_gl_idx = 0; + u8 goout = 0, agc_cnt = 0, limited_rxbb = 0; + u16 dgain = 0; + s8 offset; + int limit = 200; + + tmp_txagc = init_txagc; + + do { + switch (step) { + case DPK_AGC_STEP_SYNC_DGAIN: + if (_dpk_sync(rtwdev, phy, path, kidx)) { + tmp_txagc = 0xff; + goout = 1; + break; + } + + dgain = _dpk_dgain_read(rtwdev); + + if (loss_only == 1 || limited_rxbb == 1) + step = DPK_AGC_STEP_GAIN_LOSS_IDX; + else + step = DPK_AGC_STEP_GAIN_ADJ; + break; + + case DPK_AGC_STEP_GAIN_ADJ: + tmp_rxbb = rtw89_read_rf(rtwdev, path, RR_MOD, + RFREG_MASKRXBB); + offset = _dpk_dgain_mapping(rtwdev, dgain); + + if (tmp_rxbb + offset > 0x1f) { + tmp_rxbb = 0x1f; + limited_rxbb = 1; + } else if (tmp_rxbb + offset < 0) { + tmp_rxbb = 0; + limited_rxbb = 1; + } else { + tmp_rxbb = tmp_rxbb + offset; + } + + rtw89_write_rf(rtwdev, path, RR_MOD, RFREG_MASKRXBB, + tmp_rxbb); + rtw89_debug(rtwdev, RTW89_DBG_RFK, + "[DPK] Adjust RXBB (%d) = 0x%x\n", offset, tmp_rxbb); + if (offset || agc_cnt == 0) { + if (chan->band_width < RTW89_CHANNEL_WIDTH_80) + _dpk_bypass_rxcfir(rtwdev, path, true); + else + _dpk_lbk_rxiqk(rtwdev, phy, path); + } + if (dgain > 1922 || dgain < 342) + step = DPK_AGC_STEP_SYNC_DGAIN; + else + step = DPK_AGC_STEP_GAIN_LOSS_IDX; + + agc_cnt++; + break; + + case DPK_AGC_STEP_GAIN_LOSS_IDX: + _dpk_gainloss(rtwdev, phy, path, kidx); + tmp_gl_idx = _dpk_gainloss_read(rtwdev); + + if ((tmp_gl_idx == 0 && _dpk_pas_read(rtwdev, true)) || + tmp_gl_idx >= 7) + step = DPK_AGC_STEP_GL_GT_CRITERION; + else if (tmp_gl_idx == 0) + step = DPK_AGC_STEP_GL_LT_CRITERION; + else + step = DPK_AGC_STEP_SET_TX_GAIN; + break; + + case DPK_AGC_STEP_GL_GT_CRITERION: + if (tmp_txagc == 0x2e) { + goout = 1; + rtw89_debug(rtwdev, RTW89_DBG_RFK, + "[DPK] Txagc@lower bound!!\n"); + } else { + tmp_txagc = _dpk_set_offset(rtwdev, phy, path, 0x3); + } + step = DPK_AGC_STEP_GAIN_LOSS_IDX; + agc_cnt++; + break; + + case DPK_AGC_STEP_GL_LT_CRITERION: + if (tmp_txagc == 0x3f) { + goout = 1; + rtw89_debug(rtwdev, RTW89_DBG_RFK, + "[DPK] Txagc@upper bound!!\n"); + } else { + tmp_txagc = _dpk_set_offset(rtwdev, phy, path, 0xfe); + } + step = DPK_AGC_STEP_GAIN_LOSS_IDX; + agc_cnt++; + break; + case DPK_AGC_STEP_SET_TX_GAIN: + tmp_txagc = _dpk_set_offset(rtwdev, phy, path, tmp_gl_idx); + goout = 1; + agc_cnt++; + break; + + default: + goout = 1; + break; + } + } while (!goout && agc_cnt < 6 && limit-- > 0); + + rtw89_debug(rtwdev, RTW89_DBG_RFK, + "[DPK] Txagc / RXBB for DPK = 0x%x / 0x%x\n", tmp_txagc, + tmp_rxbb); + + return tmp_txagc; +} + +static void _dpk_set_mdpd_para(struct rtw89_dev *rtwdev, u8 order) +{ + switch (order) { + case 0: + rtw89_phy_write32_mask(rtwdev, R_LDL_NORM, B_LDL_NORM_OP, order); + rtw89_phy_write32_mask(rtwdev, R_LDL_NORM, B_LDL_NORM_PN, 0x3); + rtw89_phy_write32_mask(rtwdev, R_MDPK_SYNC, B_MDPK_SYNC_MAN, 0x1); + break; + case 1: + rtw89_phy_write32_mask(rtwdev, R_LDL_NORM, B_LDL_NORM_OP, order); + rtw89_phy_write32_clr(rtwdev, R_LDL_NORM, B_LDL_NORM_PN); + rtw89_phy_write32_clr(rtwdev, R_MDPK_SYNC, B_MDPK_SYNC_MAN); + break; + case 2: + rtw89_phy_write32_mask(rtwdev, R_LDL_NORM, B_LDL_NORM_OP, order); + rtw89_phy_write32_clr(rtwdev, R_LDL_NORM, B_LDL_NORM_PN); + rtw89_phy_write32_clr(rtwdev, R_MDPK_SYNC, B_MDPK_SYNC_MAN); + break; + default: + rtw89_debug(rtwdev, RTW89_DBG_RFK, + "[DPK] Wrong MDPD order!!(0x%x)\n", order); + break; + } + + rtw89_debug(rtwdev, RTW89_DBG_RFK, + "[DPK] Set MDPD order to 0x%x for IDL\n", order); +} + +static void _dpk_idl_mpa(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy, + enum rtw89_rf_path path, u8 kidx, u8 gain) +{ + struct rtw89_dpk_info *dpk = &rtwdev->dpk; + + if (dpk->bp[path][kidx].bw < RTW89_CHANNEL_WIDTH_80 && + dpk->bp[path][kidx].band == RTW89_BAND_5G) + _dpk_set_mdpd_para(rtwdev, 0x2); + else + _dpk_set_mdpd_para(rtwdev, 0x0); + + _dpk_one_shot(rtwdev, phy, path, MDPK_IDL); +} + +static void _dpk_fill_result(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy, + enum rtw89_rf_path path, u8 kidx, u8 gain, u8 txagc) +{ + struct rtw89_dpk_info *dpk = &rtwdev->dpk; + const u16 pwsf = 0x78; + u8 gs = dpk->dpk_gs[phy]; + + rtw89_phy_write32_mask(rtwdev, R_COEF_SEL + (path << 8), + B_COEF_SEL_MDPD, kidx); + + rtw89_debug(rtwdev, RTW89_DBG_RFK, + "[DPK] Fill txagc/ pwsf/ gs = 0x%x/ 0x%x/ 0x%x\n", txagc, + pwsf, gs); + + dpk->bp[path][kidx].txagc_dpk = txagc; + rtw89_phy_write32_mask(rtwdev, R_TXAGC_RFK + (path << 8), + 0x3F << ((gain << 3) + (kidx << 4)), txagc); + + dpk->bp[path][kidx].pwsf = pwsf; + rtw89_phy_write32_mask(rtwdev, R_DPD_BND + (path << 8) + (kidx << 2), + 0x1FF << (gain << 4), pwsf); + + rtw89_phy_write32_mask(rtwdev, R_LOAD_COEF + (path << 8), B_LOAD_COEF_MDPD, 0x1); + rtw89_phy_write32_mask(rtwdev, R_LOAD_COEF + (path << 8), B_LOAD_COEF_MDPD, 0x0); + + dpk->bp[path][kidx].gs = gs; + if (dpk->dpk_gs[phy] == 0x7f) + rtw89_phy_write32_mask(rtwdev, R_DPD_CH0A + (path << 8) + (kidx << 2), + MASKDWORD, 0x007f7f7f); + else + rtw89_phy_write32_mask(rtwdev, R_DPD_CH0A + (path << 8) + (kidx << 2), + MASKDWORD, 0x005b5b5b); + + rtw89_phy_write32_mask(rtwdev, R_DPD_CH0A + (path << 8) + (kidx << 2), + B_DPD_ORDER_V1, _dpk_order_convert(rtwdev)); + rtw89_phy_write32_mask(rtwdev, R_DPD_V1 + (path << 8), MASKDWORD, 0x0); + rtw89_phy_write32_mask(rtwdev, R_MDPK_SYNC, B_MDPK_SYNC_SEL, 0x0); +} + +static bool _dpk_reload_check(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy, + enum rtw89_rf_path path) +{ + const struct rtw89_chan *chan = rtw89_chan_get(rtwdev, RTW89_SUB_ENTITY_0); + struct rtw89_dpk_info *dpk = &rtwdev->dpk; + bool is_reload = false; + u8 idx, cur_band, cur_ch; + + cur_band = chan->band_type; + cur_ch = chan->channel; + + for (idx = 0; idx < RTW89_DPK_BKUP_NUM; idx++) { + if (cur_band != dpk->bp[path][idx].band || + cur_ch != dpk->bp[path][idx].ch) + continue; + + rtw89_phy_write32_mask(rtwdev, R_COEF_SEL + (path << 8), + B_COEF_SEL_MDPD, idx); + dpk->cur_idx[path] = idx; + is_reload = true; + rtw89_debug(rtwdev, RTW89_DBG_RFK, + "[DPK] reload S%d[%d] success\n", path, idx); + } + + return is_reload; +} + +static bool _dpk_main(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy, + enum rtw89_rf_path path, u8 gain) +{ + struct rtw89_dpk_info *dpk = &rtwdev->dpk; + u8 txagc = 0x38, kidx = dpk->cur_idx[path]; + bool is_fail = false; + + rtw89_debug(rtwdev, RTW89_DBG_RFK, + "[DPK] ========= S%d[%d] DPK Start =========\n", path, kidx); + + _rfk_rf_direct_cntrl(rtwdev, path, false); + _rfk_drf_direct_cntrl(rtwdev, path, false); + + _dpk_kip_pwr_clk_on(rtwdev, path); + _dpk_kip_set_txagc(rtwdev, phy, path, txagc); + _dpk_rf_setting(rtwdev, gain, path, kidx); + _dpk_rx_dck(rtwdev, phy, path); + + _dpk_kip_preset(rtwdev, phy, path, kidx); + _dpk_kip_set_rxagc(rtwdev, phy, path); + _dpk_table_select(rtwdev, path, kidx, gain); + + txagc = _dpk_agc(rtwdev, phy, path, kidx, txagc, false); + rtw89_debug(rtwdev, RTW89_DBG_RFK, "[DPK] Adjust txagc = 0x%x\n", txagc); + + if (txagc == 0xff) { + is_fail = true; + } else { + _dpk_get_thermal(rtwdev, kidx, path); + + _dpk_idl_mpa(rtwdev, phy, path, kidx, gain); + + rtw89_write_rf(rtwdev, path, RR_MOD, RR_MOD_MASK, RR_MOD_V_RX); + + _dpk_fill_result(rtwdev, phy, path, kidx, gain, txagc); + } + + if (!is_fail) + dpk->bp[path][kidx].path_ok = true; + else + dpk->bp[path][kidx].path_ok = false; + + rtw89_debug(rtwdev, RTW89_DBG_RFK, "[DPK] S%d[%d] DPK %s\n", path, kidx, + is_fail ? "Check" : "Success"); + + return is_fail; +} + +static void _dpk_cal_select(struct rtw89_dev *rtwdev, bool force, + enum rtw89_phy_idx phy, u8 kpath) +{ + struct rtw89_dpk_info *dpk = &rtwdev->dpk; + static const u32 kip_reg[] = {0x813c, 0x8124, 0x8120}; + u32 kip_bkup[RTW8852B_DPK_RF_PATH][RTW8852B_DPK_KIP_REG_NUM] = {}; + u32 backup_rf_val[RTW8852B_DPK_RF_PATH][BACKUP_RF_REGS_NR]; + u32 backup_bb_val[BACKUP_BB_REGS_NR]; + bool is_fail = true, reloaded[RTW8852B_DPK_RF_PATH] = {}; + u8 path; + + if (dpk->is_dpk_reload_en) { + for (path = 0; path < RTW8852B_DPK_RF_PATH; path++) { + reloaded[path] = _dpk_reload_check(rtwdev, phy, path); + if (!reloaded[path] && dpk->bp[path][0].ch) + dpk->cur_idx[path] = !dpk->cur_idx[path]; + else + _dpk_onoff(rtwdev, path, false); + } + } else { + for (path = 0; path < RTW8852B_DPK_RF_PATH; path++) + dpk->cur_idx[path] = 0; + } + + _rfk_backup_bb_reg(rtwdev, &backup_bb_val[0]); + + for (path = 0; path < RTW8852B_DPK_RF_PATH; path++) { + _dpk_bkup_kip(rtwdev, kip_reg, kip_bkup, path); + _rfk_backup_rf_reg(rtwdev, &backup_rf_val[path][0], path); + _dpk_information(rtwdev, phy, path); + if (rtwdev->is_tssi_mode[path]) + _dpk_tssi_pause(rtwdev, path, true); + } + + _dpk_bb_afe_setting(rtwdev, phy, path, kpath); + + for (path = 0; path < RTW8852B_DPK_RF_PATH; path++) { + is_fail = _dpk_main(rtwdev, phy, path, 1); + _dpk_onoff(rtwdev, path, is_fail); + } + + _dpk_bb_afe_restore(rtwdev, phy, path, kpath); + _rfk_restore_bb_reg(rtwdev, &backup_bb_val[0]); + + for (path = 0; path < RTW8852B_DPK_RF_PATH; path++) { + _dpk_kip_restore(rtwdev, path); + _dpk_reload_kip(rtwdev, kip_reg, kip_bkup, path); + _rfk_restore_rf_reg(rtwdev, &backup_rf_val[path][0], path); + if (rtwdev->is_tssi_mode[path]) + _dpk_tssi_pause(rtwdev, path, false); + } +} + +static bool _dpk_bypass_check(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy) +{ + const struct rtw89_chan *chan = rtw89_chan_get(rtwdev, RTW89_SUB_ENTITY_0); + struct rtw89_fem_info *fem = &rtwdev->fem; + + if (fem->epa_2g && chan->band_type == RTW89_BAND_2G) { + rtw89_debug(rtwdev, RTW89_DBG_RFK, + "[DPK] Skip DPK due to 2G_ext_PA exist!!\n"); + return true; + } else if (fem->epa_5g && chan->band_type == RTW89_BAND_5G) { + rtw89_debug(rtwdev, RTW89_DBG_RFK, + "[DPK] Skip DPK due to 5G_ext_PA exist!!\n"); + return true; + } else if (fem->epa_6g && chan->band_type == RTW89_BAND_6G) { + rtw89_debug(rtwdev, RTW89_DBG_RFK, + "[DPK] Skip DPK due to 6G_ext_PA exist!!\n"); + return true; + } + + return false; +} + +static void _dpk_force_bypass(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy) +{ + u8 path, kpath; + + kpath = _kpath(rtwdev, phy); + + for (path = 0; path < RTW8852B_DPK_RF_PATH; path++) { + if (kpath & BIT(path)) + _dpk_onoff(rtwdev, path, true); + } +} + +static void _dpk(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy, bool force) +{ + rtw89_debug(rtwdev, RTW89_DBG_RFK, + "[DPK] ****** DPK Start (Ver: 0x%x, Cv: %d, RF_para: %d) ******\n", + RTW8852B_DPK_VER, rtwdev->hal.cv, + RTW8852B_RF_REL_VERSION); + + if (_dpk_bypass_check(rtwdev, phy)) + _dpk_force_bypass(rtwdev, phy); + else + _dpk_cal_select(rtwdev, force, phy, RF_AB); +} + +static void _dpk_track(struct rtw89_dev *rtwdev) +{ + struct rtw89_dpk_info *dpk = &rtwdev->dpk; + s8 txagc_bb, txagc_bb_tp, ini_diff = 0, txagc_ofst; + s8 delta_ther[2] = {}; + u8 trk_idx, txagc_rf; + u8 path, kidx; + u16 pwsf[2]; + u8 cur_ther; + u32 tmp; + + for (path = 0; path < RF_PATH_NUM_8852B; path++) { + kidx = dpk->cur_idx[path]; + + rtw89_debug(rtwdev, RTW89_DBG_RFK_TRACK, + "[DPK_TRK] ================[S%d[%d] (CH %d)]================\n", + path, kidx, dpk->bp[path][kidx].ch); + + cur_ther = ewma_thermal_read(&rtwdev->phystat.avg_thermal[path]); + + rtw89_debug(rtwdev, RTW89_DBG_RFK_TRACK, + "[DPK_TRK] thermal now = %d\n", cur_ther); + + if (dpk->bp[path][kidx].ch && cur_ther) + delta_ther[path] = dpk->bp[path][kidx].ther_dpk - cur_ther; + + if (dpk->bp[path][kidx].band == RTW89_BAND_2G) + delta_ther[path] = delta_ther[path] * 3 / 2; + else + delta_ther[path] = delta_ther[path] * 5 / 2; + + txagc_rf = rtw89_phy_read32_mask(rtwdev, R_TXAGC_BB + (path << 13), + 0x0000003f); + + if (rtwdev->is_tssi_mode[path]) { + trk_idx = rtw89_read_rf(rtwdev, path, RR_TXA, RR_TXA_TRK); + + rtw89_debug(rtwdev, RTW89_DBG_RFK_TRACK, + "[DPK_TRK] txagc_RF / track_idx = 0x%x / %d\n", + txagc_rf, trk_idx); + + txagc_bb = + rtw89_phy_read32_mask(rtwdev, R_TXAGC_BB + (path << 13), + MASKBYTE2); + txagc_bb_tp = + rtw89_phy_read32_mask(rtwdev, R_TXAGC_TP + (path << 13), + B_TXAGC_TP); + + rtw89_debug(rtwdev, RTW89_DBG_RFK_TRACK, + "[DPK_TRK] txagc_bb_tp / txagc_bb = 0x%x / 0x%x\n", + txagc_bb_tp, txagc_bb); + + txagc_ofst = + rtw89_phy_read32_mask(rtwdev, R_TXAGC_BB + (path << 13), + MASKBYTE3); + + rtw89_debug(rtwdev, RTW89_DBG_RFK_TRACK, + "[DPK_TRK] txagc_offset / delta_ther = %d / %d\n", + txagc_ofst, delta_ther[path]); + tmp = rtw89_phy_read32_mask(rtwdev, R_DPD_COM + (path << 8), + B_DPD_COM_OF); + if (tmp == 0x1) { + txagc_ofst = 0; + rtw89_debug(rtwdev, RTW89_DBG_RFK_TRACK, + "[DPK_TRK] HW txagc offset mode\n"); + } + + if (txagc_rf && cur_ther) + ini_diff = txagc_ofst + (delta_ther[path]); + + tmp = rtw89_phy_read32_mask(rtwdev, + R_P0_TXDPD + (path << 13), + B_P0_TXDPD); + if (tmp == 0x0) { + pwsf[0] = dpk->bp[path][kidx].pwsf + + txagc_bb_tp - txagc_bb + ini_diff; + pwsf[1] = dpk->bp[path][kidx].pwsf + + txagc_bb_tp - txagc_bb + ini_diff; + } else { + pwsf[0] = dpk->bp[path][kidx].pwsf + ini_diff; + pwsf[1] = dpk->bp[path][kidx].pwsf + ini_diff; + } + + } else { + pwsf[0] = (dpk->bp[path][kidx].pwsf + delta_ther[path]) & 0x1ff; + pwsf[1] = (dpk->bp[path][kidx].pwsf + delta_ther[path]) & 0x1ff; + } + + tmp = rtw89_phy_read32_mask(rtwdev, R_DPK_TRK, B_DPK_TRK_DIS); + if (!tmp && txagc_rf) { + rtw89_debug(rtwdev, RTW89_DBG_RFK_TRACK, + "[DPK_TRK] New pwsf[0] / pwsf[1] = 0x%x / 0x%x\n", + pwsf[0], pwsf[1]); + + rtw89_phy_write32_mask(rtwdev, + R_DPD_BND + (path << 8) + (kidx << 2), + B_DPD_BND_0, pwsf[0]); + rtw89_phy_write32_mask(rtwdev, + R_DPD_BND + (path << 8) + (kidx << 2), + B_DPD_BND_1, pwsf[1]); + } + } +} + +static void _set_dpd_backoff(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy) +{ + struct rtw89_dpk_info *dpk = &rtwdev->dpk; + u8 tx_scale, ofdm_bkof, path, kpath; + + kpath = _kpath(rtwdev, phy); + + ofdm_bkof = rtw89_phy_read32_mask(rtwdev, R_DPD_BF + (phy << 13), B_DPD_BF_OFDM); + tx_scale = rtw89_phy_read32_mask(rtwdev, R_DPD_BF + (phy << 13), B_DPD_BF_SCA); + + if (ofdm_bkof + tx_scale >= 44) { + /* move dpd backoff to bb, and set dpd backoff to 0 */ + dpk->dpk_gs[phy] = 0x7f; + for (path = 0; path < RF_PATH_NUM_8852B; path++) { + if (!(kpath & BIT(path))) + continue; + + rtw89_phy_write32_mask(rtwdev, R_DPD_CH0A + (path << 8), + B_DPD_CFG, 0x7f7f7f); + rtw89_debug(rtwdev, RTW89_DBG_RFK, + "[RFK] Set S%d DPD backoff to 0dB\n", path); + } + } else { + dpk->dpk_gs[phy] = 0x5b; + } +} + static void _tssi_rf_setting(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy, enum rtw89_rf_path path) { @@ -2626,6 +3747,11 @@ out: tssi_info->tssi_alimk_time); } +void rtw8852b_dpk_init(struct rtw89_dev *rtwdev) +{ + _set_dpd_backoff(rtwdev, RTW89_PHY_0); +} + void rtw8852b_rck(struct rtw89_dev *rtwdev) { u8 path; @@ -2674,6 +3800,28 @@ void rtw8852b_rx_dck(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx) rtw89_btc_ntfy_wl_rfk(rtwdev, phy_map, BTC_WRFKT_RXDCK, BTC_WRFK_STOP); } +void rtw8852b_dpk(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx) +{ + u8 phy_map = rtw89_btc_phymap(rtwdev, phy_idx, 0); + u32 tx_en; + + rtw89_btc_ntfy_wl_rfk(rtwdev, phy_map, BTC_WRFKT_DPK, BTC_WRFK_START); + rtw89_chip_stop_sch_tx(rtwdev, phy_idx, &tx_en, RTW89_SCH_TX_SEL_ALL); + _wait_rx_mode(rtwdev, _kpath(rtwdev, phy_idx)); + + rtwdev->dpk.is_dpk_enable = true; + rtwdev->dpk.is_dpk_reload_en = false; + _dpk(rtwdev, phy_idx, false); + + rtw89_chip_resume_sch_tx(rtwdev, phy_idx, tx_en); + rtw89_btc_ntfy_wl_rfk(rtwdev, phy_map, BTC_WRFKT_DPK, BTC_WRFK_STOP); +} + +void rtw8852b_dpk_track(struct rtw89_dev *rtwdev) +{ + _dpk_track(rtwdev); +} + void rtw8852b_tssi(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy, bool hwtx_en) { u8 phy_map = rtw89_btc_phymap(rtwdev, phy, RF_AB); diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852b_rfk.h b/drivers/net/wireless/realtek/rtw89/rtw8852b_rfk.h index e7402733d848..f52832065600 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852b_rfk.h +++ b/drivers/net/wireless/realtek/rtw89/rtw8852b_rfk.h @@ -11,6 +11,9 @@ void rtw8852b_rck(struct rtw89_dev *rtwdev); void rtw8852b_dack(struct rtw89_dev *rtwdev); void rtw8852b_iqk(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx); void rtw8852b_rx_dck(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx); +void rtw8852b_dpk_init(struct rtw89_dev *rtwdev); +void rtw8852b_dpk(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy); +void rtw8852b_dpk_track(struct rtw89_dev *rtwdev); void rtw8852b_tssi(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy, bool hwtx_en); void rtw8852b_tssi_scan(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy); void rtw8852b_wifi_scan_notify(struct rtw89_dev *rtwdev, bool scan_start, From ef8acbcac6816e4caf20934932b4881d775c6f37 Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Fri, 14 Oct 2022 14:02:35 +0800 Subject: [PATCH 0173/2002] wifi: rtw89: 8852b: add chip_ops related to RF calibration Since RF calibrations are added, add chip_ops to call them. These chip_ops include initial, full calibration, configuration when switching band and scanning, and track work in period of 2 seconds. Signed-off-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20221014060237.29050-3-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/rtw8852b.c | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852b.c b/drivers/net/wireless/realtek/rtw89/rtw8852b.c index 0918b75ab1d9..0df044b1c392 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852b.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852b.c @@ -1513,6 +1513,43 @@ static void rtw8852b_set_channel_help(struct rtw89_dev *rtwdev, bool enter, } } +static void rtw8852b_rfk_init(struct rtw89_dev *rtwdev) +{ + rtwdev->is_tssi_mode[RF_PATH_A] = false; + rtwdev->is_tssi_mode[RF_PATH_B] = false; + + rtw8852b_dpk_init(rtwdev); + rtw8852b_rck(rtwdev); + rtw8852b_dack(rtwdev); + rtw8852b_rx_dck(rtwdev, RTW89_PHY_0); +} + +static void rtw8852b_rfk_channel(struct rtw89_dev *rtwdev) +{ + enum rtw89_phy_idx phy_idx = RTW89_PHY_0; + + rtw8852b_rx_dck(rtwdev, phy_idx); + rtw8852b_iqk(rtwdev, phy_idx); + rtw8852b_tssi(rtwdev, phy_idx, true); + rtw8852b_dpk(rtwdev, phy_idx); +} + +static void rtw8852b_rfk_band_changed(struct rtw89_dev *rtwdev, + enum rtw89_phy_idx phy_idx) +{ + rtw8852b_tssi_scan(rtwdev, phy_idx); +} + +static void rtw8852b_rfk_scan(struct rtw89_dev *rtwdev, bool start) +{ + rtw8852b_wifi_scan_notify(rtwdev, start, RTW89_PHY_0); +} + +static void rtw8852b_rfk_track(struct rtw89_dev *rtwdev) +{ + rtw8852b_dpk_track(rtwdev); +} + static u32 rtw8852b_bb_cal_txpwr_ref(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx, s16 ref) { @@ -2346,6 +2383,11 @@ static const struct rtw89_chip_ops rtw8852b_chip_ops = { .read_efuse = rtw8852b_read_efuse, .read_phycap = rtw8852b_read_phycap, .fem_setup = NULL, + .rfk_init = rtw8852b_rfk_init, + .rfk_channel = rtw8852b_rfk_channel, + .rfk_band_changed = rtw8852b_rfk_band_changed, + .rfk_scan = rtw8852b_rfk_scan, + .rfk_track = rtw8852b_rfk_track, .power_trim = rtw8852b_power_trim, .set_txpwr = rtw8852b_set_txpwr, .set_txpwr_ctrl = rtw8852b_set_txpwr_ctrl, From 3b66519b023b9de3239576b938bbdf43f95bc862 Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Fri, 14 Oct 2022 14:02:36 +0800 Subject: [PATCH 0174/2002] wifi: rtw89: phy: add dummy C2H handler to avoid warning message The C2H class 2 function 3 is to report retry count of low rate, but driver doesn't implement yet, so add a dummy case to avoid message: rtw89_8852be 0000:03:00.0: c2h class 2 not support Signed-off-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20221014060237.29050-4-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/phy.c | 4 ++++ drivers/net/wireless/realtek/rtw89/phy.h | 9 +++++++++ 2 files changed, 13 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/phy.c b/drivers/net/wireless/realtek/rtw89/phy.c index d3f47379e443..35a0d190434a 100644 --- a/drivers/net/wireless/realtek/rtw89/phy.c +++ b/drivers/net/wireless/realtek/rtw89/phy.c @@ -2276,6 +2276,10 @@ void rtw89_phy_c2h_handle(struct rtw89_dev *rtwdev, struct sk_buff *skb, if (func < RTW89_PHY_C2H_FUNC_RA_MAX) handler = rtw89_phy_c2h_ra_handler[func]; break; + case RTW89_PHY_C2H_CLASS_DM: + if (func == RTW89_PHY_C2H_DM_FUNC_LOWRT_RTY) + return; + fallthrough; default: rtw89_info(rtwdev, "c2h class %d not support\n", class); return; diff --git a/drivers/net/wireless/realtek/rtw89/phy.h b/drivers/net/wireless/realtek/rtw89/phy.h index 1e122b1498ba..995c13f6f906 100644 --- a/drivers/net/wireless/realtek/rtw89/phy.h +++ b/drivers/net/wireless/realtek/rtw89/phy.h @@ -114,6 +114,15 @@ enum rtw89_phy_c2h_ra_func { RTW89_PHY_C2H_FUNC_RA_MAX, }; +enum rtw89_phy_c2h_dm_func { + RTW89_PHY_C2H_DM_FUNC_FW_TEST, + RTW89_PHY_C2H_DM_FUNC_FW_TRIG_TX_RPT, + RTW89_PHY_C2H_DM_FUNC_SIGB, + RTW89_PHY_C2H_DM_FUNC_LOWRT_RTY, + RTW89_PHY_C2H_DM_FUNC_MCC_DIG, + RTW89_PHY_C2H_DM_FUNC_NUM, +}; + enum rtw89_phy_c2h_class { RTW89_PHY_C2H_CLASS_RUA, RTW89_PHY_C2H_CLASS_RA, From b5db4ef38e21dd9b6b95ae96cea5032b00e04f24 Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Fri, 14 Oct 2022 14:02:37 +0800 Subject: [PATCH 0175/2002] wifi: rtw89: 8852b: add 8852be to Makefile and Kconfig Now, basic materials for 8852be are ready, so add 8852be to Kconfig and Makefile. Current version can support STA, AP and monitor modes. We still fine tune some features, such as BT coexistence, performance, and power consumption. Signed-off-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20221014060237.29050-5-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/Kconfig | 14 ++++++++++++++ drivers/net/wireless/realtek/rtw89/Makefile | 9 +++++++++ 2 files changed, 23 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/Kconfig b/drivers/net/wireless/realtek/rtw89/Kconfig index 93e09400aac4..2b20cf8bbf3a 100644 --- a/drivers/net/wireless/realtek/rtw89/Kconfig +++ b/drivers/net/wireless/realtek/rtw89/Kconfig @@ -19,6 +19,9 @@ config RTW89_PCI config RTW89_8852A tristate +config RTW89_8852B + tristate + config RTW89_8852C tristate @@ -33,6 +36,17 @@ config RTW89_8852AE 802.11ax PCIe wireless network (Wi-Fi 6) adapter +config RTW89_8852BE + tristate "Realtek 8852BE PCI wireless network (Wi-Fi 6) adapter" + depends on PCI + select RTW89_CORE + select RTW89_PCI + select RTW89_8852B + help + Select this option will enable support for 8852BE chipset + + 802.11ax PCIe wireless network (Wi-Fi 6) adapter + config RTW89_8852CE tristate "Realtek 8852CE PCI wireless network (Wi-Fi 6E) adapter" depends on PCI diff --git a/drivers/net/wireless/realtek/rtw89/Makefile b/drivers/net/wireless/realtek/rtw89/Makefile index a87f2aff4def..ec0f5da65d6a 100644 --- a/drivers/net/wireless/realtek/rtw89/Makefile +++ b/drivers/net/wireless/realtek/rtw89/Makefile @@ -24,6 +24,15 @@ rtw89_8852a-objs := rtw8852a.o \ obj-$(CONFIG_RTW89_8852AE) += rtw89_8852ae.o rtw89_8852ae-objs := rtw8852ae.o +obj-$(CONFIG_RTW89_8852B) += rtw89_8852b.o +rtw89_8852b-objs := rtw8852b.o \ + rtw8852b_table.o \ + rtw8852b_rfk.o \ + rtw8852b_rfk_table.o + +obj-$(CONFIG_RTW89_8852BE) += rtw89_8852be.o +rtw89_8852be-objs := rtw8852be.o + obj-$(CONFIG_RTW89_8852C) += rtw89_8852c.o rtw89_8852c-objs := rtw8852c.o \ rtw8852c_table.o \ From 80bc5ae9733c2ae71a13db819f9368191cc19d6f Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Tue, 11 Oct 2022 14:29:41 +0200 Subject: [PATCH 0176/2002] bcma: support SPROM rev 11 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rev 11 works fine for me to set the MAC address of gmac0 and gmac1 in the D-Link DWL-8610AP. Cc: Rafał Miłecki Signed-off-by: Linus Walleij Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20221011122941.2053705-1-linus.walleij@linaro.org --- drivers/bcma/sprom.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/bcma/sprom.c b/drivers/bcma/sprom.c index 3da01f173c63..e668ad7963fc 100644 --- a/drivers/bcma/sprom.c +++ b/drivers/bcma/sprom.c @@ -165,7 +165,7 @@ static int bcma_sprom_valid(struct bcma_bus *bus, const u16 *sprom, return err; revision = sprom[words - 1] & SSB_SPROM_REVISION_REV; - if (revision != 8 && revision != 9 && revision != 10) { + if (revision < 8 || revision > 11) { pr_err("Unsupported SPROM revision: %d\n", revision); return -ENOENT; } From 55549d6a3052990e65727bfec158e1b0e7cec4e4 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Wed, 12 Oct 2022 21:23:48 +0200 Subject: [PATCH 0177/2002] bcma: gpio: Convert to immutable gpio irqchip MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This switches the BCMA gpio irqchip to be immutable. The GPIO subsystem plays tricks on the irqchip structures, and we're fixing it one driver at a time. See merge commit 4bde53ab3370 ("Merge branch irq/gpio-immutable into irq/irqchip-next") for all the details. Tested on the D-Link DWL-6810AP. Cc: Marc Zyngier Cc: Bartosz Golaszewski Cc: Rafał Miłecki Signed-off-by: Linus Walleij Acked-by: Marc Zyngier Reviewed-by: Bartosz Golaszewski Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20221012192348.2234478-1-linus.walleij@linaro.org --- drivers/bcma/driver_gpio.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/bcma/driver_gpio.c b/drivers/bcma/driver_gpio.c index fac8ff983aec..eff6fc330632 100644 --- a/drivers/bcma/driver_gpio.c +++ b/drivers/bcma/driver_gpio.c @@ -82,6 +82,7 @@ static void bcma_gpio_irq_unmask(struct irq_data *d) int gpio = irqd_to_hwirq(d); u32 val = bcma_chipco_gpio_in(cc, BIT(gpio)); + gpiochip_enable_irq(gc, gpio); bcma_chipco_gpio_polarity(cc, BIT(gpio), val); bcma_chipco_gpio_intmask(cc, BIT(gpio), BIT(gpio)); } @@ -93,12 +94,15 @@ static void bcma_gpio_irq_mask(struct irq_data *d) int gpio = irqd_to_hwirq(d); bcma_chipco_gpio_intmask(cc, BIT(gpio), 0); + gpiochip_disable_irq(gc, gpio); } -static struct irq_chip bcma_gpio_irq_chip = { +static const struct irq_chip bcma_gpio_irq_chip = { .name = "BCMA-GPIO", .irq_mask = bcma_gpio_irq_mask, .irq_unmask = bcma_gpio_irq_unmask, + .flags = IRQCHIP_IMMUTABLE, + GPIOCHIP_IRQ_RESOURCE_HELPERS, }; static irqreturn_t bcma_gpio_irq_handler(int irq, void *dev_id) @@ -139,7 +143,7 @@ static int bcma_gpio_irq_init(struct bcma_drv_cc *cc) bcma_chipco_gpio_intmask(cc, ~0, 0); bcma_cc_set32(cc, BCMA_CC_IRQMASK, BCMA_CC_IRQ_GPIO); - girq->chip = &bcma_gpio_irq_chip; + gpio_irq_chip_set_chip(girq, &bcma_gpio_irq_chip); /* This will let us handle the parent IRQ in the driver */ girq->parent_handler = NULL; girq->num_parents = 0; From e91001bae0d1725d9a49b5bfb5f46f6d1ca6bf1d Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Mon, 17 Oct 2022 23:08:09 +0100 Subject: [PATCH 0178/2002] esp6: remove redundant variable err Variable err is being assigned a value that is not read, the assignment is redundant and so is the variable. Remove it. Cleans up clang scan warning: net/ipv6/esp6_offload.c:64:7: warning: Although the value stored to 'err' is used in the enclosing expression, the value is never actually read from 'err' [deadcode.DeadStores] Signed-off-by: Colin Ian King Signed-off-by: Steffen Klassert --- net/ipv6/esp6_offload.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/net/ipv6/esp6_offload.c b/net/ipv6/esp6_offload.c index 79d43548279c..97edf461bc72 100644 --- a/net/ipv6/esp6_offload.c +++ b/net/ipv6/esp6_offload.c @@ -56,12 +56,11 @@ static struct sk_buff *esp6_gro_receive(struct list_head *head, __be32 seq; __be32 spi; int nhoff; - int err; if (!pskb_pull(skb, offset)) return NULL; - if ((err = xfrm_parse_spi(skb, IPPROTO_ESP, &spi, &seq)) != 0) + if (xfrm_parse_spi(skb, IPPROTO_ESP, &spi, &seq) != 0) goto out; xo = xfrm_offload(skb); From c4d175c323e3751badf77766c79cb130b4a3eb5f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Grzelak?= Date: Fri, 14 Oct 2022 23:32:52 +0200 Subject: [PATCH 0179/2002] dt-bindings: net: marvell,pp2: convert to json-schema MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Convert the marvell,pp2 bindings from text to proper schema. Move 'marvell,system-controller' and 'dma-coherent' properties from port up to the controller node, to match what is actually done in DT. Rename all subnodes to match "^(ethernet-)?port@[0-2]$" and deprecate port-id in favour of 'reg'. Signed-off-by: Michał Grzelak Reviewed-by: Rob Herring Signed-off-by: David S. Miller --- .../devicetree/bindings/net/marvell,pp2.yaml | 305 ++++++++++++++++++ .../devicetree/bindings/net/marvell-pp2.txt | 141 -------- MAINTAINERS | 2 +- 3 files changed, 306 insertions(+), 142 deletions(-) create mode 100644 Documentation/devicetree/bindings/net/marvell,pp2.yaml delete mode 100644 Documentation/devicetree/bindings/net/marvell-pp2.txt diff --git a/Documentation/devicetree/bindings/net/marvell,pp2.yaml b/Documentation/devicetree/bindings/net/marvell,pp2.yaml new file mode 100644 index 000000000000..4eadafc43d4f --- /dev/null +++ b/Documentation/devicetree/bindings/net/marvell,pp2.yaml @@ -0,0 +1,305 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/net/marvell,pp2.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Marvell CN913X / Marvell Armada 375, 7K, 8K Ethernet Controller + +maintainers: + - Marcin Wojtas + - Russell King + +description: | + Marvell Armada 375 Ethernet Controller (PPv2.1) + Marvell Armada 7K/8K Ethernet Controller (PPv2.2) + Marvell CN913X Ethernet Controller (PPv2.3) + +properties: + compatible: + enum: + - marvell,armada-375-pp2 + - marvell,armada-7k-pp22 + + reg: + minItems: 3 + maxItems: 4 + + "#address-cells": + const: 1 + + "#size-cells": + const: 0 + + clocks: + minItems: 2 + items: + - description: main controller clock + - description: GOP clock + - description: MG clock + - description: MG Core clock + - description: AXI clock + + clock-names: + minItems: 2 + items: + - const: pp_clk + - const: gop_clk + - const: mg_clk + - const: mg_core_clk + - const: axi_clk + + dma-coherent: true + + marvell,system-controller: + $ref: /schemas/types.yaml#/definitions/phandle + description: a phandle to the system controller. + +patternProperties: + '^(ethernet-)?port@[0-2]$': + type: object + description: subnode for each ethernet port. + $ref: ethernet-controller.yaml# + unevaluatedProperties: false + + properties: + reg: + description: ID of the port from the MAC point of view. + maximum: 2 + + interrupts: + minItems: 1 + maxItems: 10 + description: interrupt(s) for the port + + interrupt-names: + minItems: 1 + items: + - const: hif0 + - const: hif1 + - const: hif2 + - const: hif3 + - const: hif4 + - const: hif5 + - const: hif6 + - const: hif7 + - const: hif8 + - const: link + + description: > + if more than a single interrupt for is given, must be the + name associated to the interrupts listed. Valid names are: + "hifX", with X in [0..8], and "link". The names "tx-cpu0", + "tx-cpu1", "tx-cpu2", "tx-cpu3" and "rx-shared" are supported + for backward compatibility but shouldn't be used for new + additions. + + phys: + minItems: 1 + maxItems: 2 + description: > + Generic PHY, providing SerDes connectivity. For most modes, + one lane is sufficient, but some (e.g. RXAUI) may require two. + + phy-mode: + enum: + - gmii + - sgmii + - rgmii-id + - 1000base-x + - 2500base-x + - 5gbase-r + - rxaui + - 10gbase-r + + port-id: + $ref: /schemas/types.yaml#/definitions/uint32 + deprecated: true + description: > + ID of the port from the MAC point of view. + Legacy binding for backward compatibility. + + marvell,loopback: + $ref: /schemas/types.yaml#/definitions/flag + description: port is loopback mode. + + gop-port-id: + $ref: /schemas/types.yaml#/definitions/uint32 + description: > + only for marvell,armada-7k-pp22, ID of the port from the + GOP (Group Of Ports) point of view. This ID is used to index the + per-port registers in the second register area. + + required: + - reg + - interrupts + - phy-mode + - port-id + +required: + - compatible + - reg + - clocks + - clock-names + +allOf: + - if: + properties: + compatible: + const: marvell,armada-7k-pp22 + then: + properties: + reg: + items: + - description: Packet Processor registers + - description: Networking interfaces registers + - description: CM3 address space used for TX Flow Control + + clocks: + minItems: 5 + + clock-names: + minItems: 5 + + patternProperties: + '^(ethernet-)?port@[0-2]$': + required: + - gop-port-id + + required: + - marvell,system-controller + else: + properties: + reg: + items: + - description: Packet Processor registers + - description: LMS registers + - description: Register area per eth0 + - description: Register area per eth1 + + clocks: + maxItems: 2 + + clock-names: + maxItems: 2 + + patternProperties: + '^(ethernet-)?port@[0-1]$': + properties: + reg: + maximum: 1 + + gop-port-id: false + +additionalProperties: false + +examples: + - | + // For Armada 375 variant + #include + #include + + ethernet@f0000 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "marvell,armada-375-pp2"; + reg = <0xf0000 0xa000>, + <0xc0000 0x3060>, + <0xc4000 0x100>, + <0xc5000 0x100>; + clocks = <&gateclk 3>, <&gateclk 19>; + clock-names = "pp_clk", "gop_clk"; + + ethernet-port@0 { + interrupts = ; + reg = <0>; + port-id = <0>; /* For backward compatibility. */ + phy = <&phy0>; + phy-mode = "rgmii-id"; + }; + + ethernet-port@1 { + interrupts = ; + reg = <1>; + port-id = <1>; /* For backward compatibility. */ + phy = <&phy3>; + phy-mode = "gmii"; + }; + }; + + - | + // For Armada 7k/8k and Cn913x variants + #include + #include + + ethernet@0 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "marvell,armada-7k-pp22"; + reg = <0x0 0x100000>, <0x129000 0xb000>, <0x220000 0x800>; + clocks = <&cp0_clk 1 3>, <&cp0_clk 1 9>, + <&cp0_clk 1 5>, <&cp0_clk 1 6>, <&cp0_clk 1 18>; + clock-names = "pp_clk", "gop_clk", "mg_clk", "mg_core_clk", "axi_clk"; + marvell,system-controller = <&cp0_syscon0>; + + ethernet-port@0 { + interrupts = , + , + , + , + , + , + , + , + , + ; + interrupt-names = "hif0", "hif1", "hif2", "hif3", "hif4", + "hif5", "hif6", "hif7", "hif8", "link"; + phy-mode = "10gbase-r"; + phys = <&cp0_comphy4 0>; + reg = <0>; + port-id = <0>; /* For backward compatibility. */ + gop-port-id = <0>; + }; + + ethernet-port@1 { + interrupts = , + , + , + , + , + , + , + , + , + ; + interrupt-names = "hif0", "hif1", "hif2", "hif3", "hif4", + "hif5", "hif6", "hif7", "hif8", "link"; + phy-mode = "rgmii-id"; + reg = <1>; + port-id = <1>; /* For backward compatibility. */ + gop-port-id = <2>; + }; + + ethernet-port@2 { + interrupts = , + , + , + , + , + , + , + , + , + ; + interrupt-names = "hif0", "hif1", "hif2", "hif3", "hif4", + "hif5", "hif6", "hif7", "hif8", "link"; + phy-mode = "2500base-x"; + managed = "in-band-status"; + phys = <&cp0_comphy5 2>; + sfp = <&sfp_eth3>; + reg = <2>; + port-id = <2>; /* For backward compatibility. */ + gop-port-id = <3>; + }; + }; diff --git a/Documentation/devicetree/bindings/net/marvell-pp2.txt b/Documentation/devicetree/bindings/net/marvell-pp2.txt deleted file mode 100644 index ce15c173f43f..000000000000 --- a/Documentation/devicetree/bindings/net/marvell-pp2.txt +++ /dev/null @@ -1,141 +0,0 @@ -* Marvell Armada 375 Ethernet Controller (PPv2.1) - Marvell Armada 7K/8K Ethernet Controller (PPv2.2) - Marvell CN913X Ethernet Controller (PPv2.3) - -Required properties: - -- compatible: should be one of: - "marvell,armada-375-pp2" - "marvell,armada-7k-pp2" -- reg: addresses and length of the register sets for the device. - For "marvell,armada-375-pp2", must contain the following register - sets: - - common controller registers - - LMS registers - - one register area per Ethernet port - For "marvell,armada-7k-pp2" used by 7K/8K and CN913X, must contain the following register - sets: - - packet processor registers - - networking interfaces registers - - CM3 address space used for TX Flow Control - -- clocks: pointers to the reference clocks for this device, consequently: - - main controller clock (for both armada-375-pp2 and armada-7k-pp2) - - GOP clock (for both armada-375-pp2 and armada-7k-pp2) - - MG clock (only for armada-7k-pp2) - - MG Core clock (only for armada-7k-pp2) - - AXI clock (only for armada-7k-pp2) -- clock-names: names of used clocks, must be "pp_clk", "gop_clk", "mg_clk", - "mg_core_clk" and "axi_clk" (the 3 latter only for armada-7k-pp2). - -The ethernet ports are represented by subnodes. At least one port is -required. - -Required properties (port): - -- interrupts: interrupt(s) for the port -- port-id: ID of the port from the MAC point of view -- gop-port-id: only for marvell,armada-7k-pp2, ID of the port from the - GOP (Group Of Ports) point of view. This ID is used to index the - per-port registers in the second register area. -- phy-mode: See ethernet.txt file in the same directory - -Optional properties (port): - -- marvell,loopback: port is loopback mode -- phy: a phandle to a phy node defining the PHY address (as the reg - property, a single integer). -- interrupt-names: if more than a single interrupt for is given, must be the - name associated to the interrupts listed. Valid names are: - "hifX", with X in [0..8], and "link". The names "tx-cpu0", - "tx-cpu1", "tx-cpu2", "tx-cpu3" and "rx-shared" are supported - for backward compatibility but shouldn't be used for new - additions. -- marvell,system-controller: a phandle to the system controller. - -Example for marvell,armada-375-pp2: - -ethernet@f0000 { - compatible = "marvell,armada-375-pp2"; - reg = <0xf0000 0xa000>, - <0xc0000 0x3060>, - <0xc4000 0x100>, - <0xc5000 0x100>; - clocks = <&gateclk 3>, <&gateclk 19>; - clock-names = "pp_clk", "gop_clk"; - - eth0: eth0@c4000 { - interrupts = ; - port-id = <0>; - phy = <&phy0>; - phy-mode = "gmii"; - }; - - eth1: eth1@c5000 { - interrupts = ; - port-id = <1>; - phy = <&phy3>; - phy-mode = "gmii"; - }; -}; - -Example for marvell,armada-7k-pp2: - -cpm_ethernet: ethernet@0 { - compatible = "marvell,armada-7k-pp22"; - reg = <0x0 0x100000>, <0x129000 0xb000>, <0x220000 0x800>; - clocks = <&cpm_syscon0 1 3>, <&cpm_syscon0 1 9>, - <&cpm_syscon0 1 5>, <&cpm_syscon0 1 6>, <&cpm_syscon0 1 18>; - clock-names = "pp_clk", "gop_clk", "mg_clk", "mg_core_clk", "axi_clk"; - - eth0: eth0 { - interrupts = , - , - , - , - , - , - , - , - , - ; - interrupt-names = "hif0", "hif1", "hif2", "hif3", "hif4", - "hif5", "hif6", "hif7", "hif8", "link"; - port-id = <0>; - gop-port-id = <0>; - }; - - eth1: eth1 { - interrupts = , - , - , - , - , - , - , - , - , - ; - interrupt-names = "hif0", "hif1", "hif2", "hif3", "hif4", - "hif5", "hif6", "hif7", "hif8", "link"; - port-id = <1>; - gop-port-id = <2>; - }; - - eth2: eth2 { - interrupts = , - , - , - , - , - , - , - , - , - ; - interrupt-names = "hif0", "hif1", "hif2", "hif3", "hif4", - "hif5", "hif6", "hif7", "hif8", "link"; - port-id = <2>; - gop-port-id = <3>; - }; -}; diff --git a/MAINTAINERS b/MAINTAINERS index a96c60c787af..5c6ce094e55e 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -12307,7 +12307,7 @@ M: Marcin Wojtas M: Russell King L: netdev@vger.kernel.org S: Maintained -F: Documentation/devicetree/bindings/net/marvell-pp2.txt +F: Documentation/devicetree/bindings/net/marvell,pp2.yaml F: drivers/net/ethernet/marvell/mvpp2/ MARVELL MWIFIEX WIRELESS DRIVER From 2994bf7705b4dca2b0e4634d79d3474868562216 Mon Sep 17 00:00:00 2001 From: Marcin Wojtas Date: Fri, 14 Oct 2022 23:32:53 +0200 Subject: [PATCH 0180/2002] arm64: dts: marvell: Update network description to match schema Update the PP2 ethernet ports subnodes' names to match schema enforced by the marvell,pp2.yaml contents. Add new required properties ('reg') which contains information about the port ID, keeping 'port-id' ones for backward compatibility. Signed-off-by: Marcin Wojtas Signed-off-by: David S. Miller --- arch/arm64/boot/dts/marvell/armada-cp11x.dtsi | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/arch/arm64/boot/dts/marvell/armada-cp11x.dtsi b/arch/arm64/boot/dts/marvell/armada-cp11x.dtsi index d6c0990a267d..7d0043824f2a 100644 --- a/arch/arm64/boot/dts/marvell/armada-cp11x.dtsi +++ b/arch/arm64/boot/dts/marvell/armada-cp11x.dtsi @@ -58,6 +58,8 @@ ranges = <0x0 0x0 ADDRESSIFY(CP11X_BASE) 0x2000000>; CP11X_LABEL(ethernet): ethernet@0 { + #address-cells = <1>; + #size-cells = <0>; compatible = "marvell,armada-7k-pp22"; reg = <0x0 0x100000>, <0x129000 0xb000>, <0x220000 0x800>; clocks = <&CP11X_LABEL(clk) 1 3>, <&CP11X_LABEL(clk) 1 9>, @@ -69,7 +71,7 @@ status = "disabled"; dma-coherent; - CP11X_LABEL(eth0): eth0 { + CP11X_LABEL(eth0): ethernet-port@0 { interrupts = <39 IRQ_TYPE_LEVEL_HIGH>, <43 IRQ_TYPE_LEVEL_HIGH>, <47 IRQ_TYPE_LEVEL_HIGH>, @@ -83,12 +85,13 @@ interrupt-names = "hif0", "hif1", "hif2", "hif3", "hif4", "hif5", "hif6", "hif7", "hif8", "link"; - port-id = <0>; + reg = <0>; + port-id = <0>; /* For backward compatibility. */ gop-port-id = <0>; status = "disabled"; }; - CP11X_LABEL(eth1): eth1 { + CP11X_LABEL(eth1): ethernet-port@1 { interrupts = <40 IRQ_TYPE_LEVEL_HIGH>, <44 IRQ_TYPE_LEVEL_HIGH>, <48 IRQ_TYPE_LEVEL_HIGH>, @@ -102,12 +105,13 @@ interrupt-names = "hif0", "hif1", "hif2", "hif3", "hif4", "hif5", "hif6", "hif7", "hif8", "link"; - port-id = <1>; + reg = <1>; + port-id = <1>; /* For backward compatibility. */ gop-port-id = <2>; status = "disabled"; }; - CP11X_LABEL(eth2): eth2 { + CP11X_LABEL(eth2): ethernet-port@2 { interrupts = <41 IRQ_TYPE_LEVEL_HIGH>, <45 IRQ_TYPE_LEVEL_HIGH>, <49 IRQ_TYPE_LEVEL_HIGH>, @@ -121,7 +125,8 @@ interrupt-names = "hif0", "hif1", "hif2", "hif3", "hif4", "hif5", "hif6", "hif7", "hif8", "link"; - port-id = <2>; + reg = <2>; + port-id = <2>; /* For backward compatibility. */ gop-port-id = <3>; status = "disabled"; }; From 844e44988fa8f747f1a730b1e1586a9f3ca23c0e Mon Sep 17 00:00:00 2001 From: Marcin Wojtas Date: Fri, 14 Oct 2022 23:32:54 +0200 Subject: [PATCH 0181/2002] ARM: dts: armada-375: Update network description to match schema Update the PP2 ethernet ports subnodes' names to match schema enforced by the marvell,pp2.yaml contents. Add new required properties ('reg') which contains information about the port ID, keeping 'port-id' ones for backward compatibility. Signed-off-by: Marcin Wojtas Signed-off-by: David S. Miller --- arch/arm/boot/dts/armada-375.dtsi | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/arch/arm/boot/dts/armada-375.dtsi b/arch/arm/boot/dts/armada-375.dtsi index 929deaf312a5..9fbe0cfec48f 100644 --- a/arch/arm/boot/dts/armada-375.dtsi +++ b/arch/arm/boot/dts/armada-375.dtsi @@ -178,6 +178,8 @@ /* Network controller */ ethernet: ethernet@f0000 { + #address-cells = <1>; + #size-cells = <0>; compatible = "marvell,armada-375-pp2"; reg = <0xf0000 0xa000>, /* Packet Processor regs */ <0xc0000 0x3060>, /* LMS regs */ @@ -187,15 +189,17 @@ clock-names = "pp_clk", "gop_clk"; status = "disabled"; - eth0: eth0 { + eth0: ethernet-port@0 { interrupts = ; - port-id = <0>; + reg = <0>; + port-id = <0>; /* For backward compatibility. */ status = "disabled"; }; - eth1: eth1 { + eth1: ethernet-port@1 { interrupts = ; - port-id = <1>; + reg = <1>; + port-id = <1>; /* For backward compatibility. */ status = "disabled"; }; }; From 76025ee53b7dc8dd9633231829b996d181bbcef2 Mon Sep 17 00:00:00 2001 From: Sean Anderson Date: Mon, 17 Oct 2022 16:22:32 -0400 Subject: [PATCH 0182/2002] dt-bindings: net: Expand pcs-handle to an array This allows multiple phandles to be specified for pcs-handle, such as when multiple PCSs are present for a single MAC. To differentiate between them, also add a pcs-handle-names property. Signed-off-by: Sean Anderson Reviewed-by: Rob Herring Signed-off-by: David S. Miller --- .../bindings/net/dsa/renesas,rzn1-a5psw.yaml | 2 +- .../devicetree/bindings/net/ethernet-controller.yaml | 11 ++++++++++- .../devicetree/bindings/net/fsl,qoriq-mc-dpmac.yaml | 2 +- 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/Documentation/devicetree/bindings/net/dsa/renesas,rzn1-a5psw.yaml b/Documentation/devicetree/bindings/net/dsa/renesas,rzn1-a5psw.yaml index 7ca9c19a157c..0a0d62b6c00e 100644 --- a/Documentation/devicetree/bindings/net/dsa/renesas,rzn1-a5psw.yaml +++ b/Documentation/devicetree/bindings/net/dsa/renesas,rzn1-a5psw.yaml @@ -74,10 +74,10 @@ properties: properties: pcs-handle: + maxItems: 1 description: phandle pointing to a PCS sub-node compatible with renesas,rzn1-miic.yaml# - $ref: /schemas/types.yaml#/definitions/phandle unevaluatedProperties: false diff --git a/Documentation/devicetree/bindings/net/ethernet-controller.yaml b/Documentation/devicetree/bindings/net/ethernet-controller.yaml index 4b3c590fcebf..3aef506fa158 100644 --- a/Documentation/devicetree/bindings/net/ethernet-controller.yaml +++ b/Documentation/devicetree/bindings/net/ethernet-controller.yaml @@ -108,11 +108,17 @@ properties: $ref: "#/properties/phy-connection-type" pcs-handle: - $ref: /schemas/types.yaml#/definitions/phandle + $ref: /schemas/types.yaml#/definitions/phandle-array + items: + maxItems: 1 description: Specifies a reference to a node representing a PCS PHY device on a MDIO bus to link with an external PHY (phy-handle) if exists. + pcs-handle-names: + description: + The name of each PCS in pcs-handle. + phy-handle: $ref: /schemas/types.yaml#/definitions/phandle description: @@ -216,6 +222,9 @@ properties: required: - speed +dependencies: + pcs-handle-names: [pcs-handle] + allOf: - if: properties: diff --git a/Documentation/devicetree/bindings/net/fsl,qoriq-mc-dpmac.yaml b/Documentation/devicetree/bindings/net/fsl,qoriq-mc-dpmac.yaml index 7f620a71a972..600240281e8c 100644 --- a/Documentation/devicetree/bindings/net/fsl,qoriq-mc-dpmac.yaml +++ b/Documentation/devicetree/bindings/net/fsl,qoriq-mc-dpmac.yaml @@ -31,7 +31,7 @@ properties: phy-mode: true pcs-handle: - $ref: /schemas/types.yaml#/definitions/phandle + maxItems: 1 description: A reference to a node representing a PCS PHY device found on the internal MDIO bus. From 00af103d06b35e0acb2213a8479fe7ef5dad1115 Mon Sep 17 00:00:00 2001 From: Sean Anderson Date: Mon, 17 Oct 2022 16:22:33 -0400 Subject: [PATCH 0183/2002] dt-bindings: net: Add Lynx PCS binding This binding is fairly bare-bones for now, since the Lynx driver doesn't parse any properties (or match based on the compatible). We just need it in order to prevent the PCS nodes from having phy devices attached to them. This is not really a problem, but it is a bit inefficient. This binding is really for three separate PCSs (SGMII, QSGMII, and XFI). However, the driver treats all of them the same. This works because the SGMII and XFI devices typically use the same address, and the SerDes driver (or RCW) muxes between them. The QSGMII PCSs have the same register layout as the SGMII PCSs. To do things properly, we'd probably do something like ethernet-pcs@0 { #pcs-cells = <1>; compatible = "fsl,lynx-pcs"; reg = <0>, <1>, <2>, <3>; }; but that would add complexity, and we can describe the hardware just fine using separate PCSs for now. Signed-off-by: Sean Anderson Reviewed-by: Rob Herring Signed-off-by: David S. Miller --- .../bindings/net/pcs/fsl,lynx-pcs.yaml | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 Documentation/devicetree/bindings/net/pcs/fsl,lynx-pcs.yaml diff --git a/Documentation/devicetree/bindings/net/pcs/fsl,lynx-pcs.yaml b/Documentation/devicetree/bindings/net/pcs/fsl,lynx-pcs.yaml new file mode 100644 index 000000000000..fbedf696c555 --- /dev/null +++ b/Documentation/devicetree/bindings/net/pcs/fsl,lynx-pcs.yaml @@ -0,0 +1,40 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/net/pcs/fsl,lynx-pcs.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: NXP Lynx PCS + +maintainers: + - Ioana Ciornei + +description: | + NXP Lynx 10G and 28G SerDes have Ethernet PCS devices which can be used as + protocol controllers. They are accessible over the Ethernet interface's MDIO + bus. + +properties: + compatible: + const: fsl,lynx-pcs + + reg: + maxItems: 1 + +required: + - compatible + - reg + +additionalProperties: false + +examples: + - | + mdio-bus { + #address-cells = <1>; + #size-cells = <0>; + + qsgmii_pcs1: ethernet-pcs@1 { + compatible = "fsl,lynx-pcs"; + reg = <1>; + }; + }; From 045d05018a2da6cb0944a40f3811fbecce6401c1 Mon Sep 17 00:00:00 2001 From: Sean Anderson Date: Mon, 17 Oct 2022 16:22:34 -0400 Subject: [PATCH 0184/2002] dt-bindings: net: fman: Add additional interface properties At the moment, mEMACs are configured almost completely based on the phy-connection-type. That is, if the phy interface is RGMII, it assumed that RGMII is supported. For some interfaces, it is assumed that the RCW/bootloader has set up the SerDes properly. This is generally OK, but restricts runtime reconfiguration. The actual link state is never reported. To address these shortcomings, the driver will need additional information. First, it needs to know how to access the PCS/PMAs (in order to configure them and get the link status). The SGMII PCS/PMA is the only currently-described PCS/PMA. Add the XFI and QSGMII PCS/PMAs as well. The XFI (and 10GBASE-KR) PCS/PMA is a c45 "phy" which sits on the same MDIO bus as SGMII PCS/PMA. By default they will have conflicting addresses, but they are also not enabled at the same time by default. Therefore, we can let the XFI PCS/PMA be the default when phy-connection-type is xgmii. This will allow for backwards-compatibility. QSGMII, however, cannot work with the current binding. This is because the QSGMII PCS/PMAs are only present on one MAC's MDIO bus. At the moment this is worked around by having every MAC write to the PCS/PMA addresses (without checking if they are present). This only works if each MAC has the same configuration, and only if we don't need to know the status. Because the QSGMII PCS/PMA will typically be located on a different MDIO bus than the MAC's SGMII PCS/PMA, there is no fallback for the QSGMII PCS/PMA. Signed-off-by: Sean Anderson Reviewed-by: Rob Herring Signed-off-by: David S. Miller --- .../bindings/net/fsl,fman-dtsec.yaml | 53 ++++++++++++++----- .../devicetree/bindings/net/fsl-fman.txt | 5 +- 2 files changed, 43 insertions(+), 15 deletions(-) diff --git a/Documentation/devicetree/bindings/net/fsl,fman-dtsec.yaml b/Documentation/devicetree/bindings/net/fsl,fman-dtsec.yaml index 3a35ac1c260d..c80c880a9dab 100644 --- a/Documentation/devicetree/bindings/net/fsl,fman-dtsec.yaml +++ b/Documentation/devicetree/bindings/net/fsl,fman-dtsec.yaml @@ -85,9 +85,39 @@ properties: $ref: /schemas/types.yaml#/definitions/phandle description: A reference to the IEEE1588 timer + phys: + description: A reference to the SerDes lane(s) + maxItems: 1 + + phy-names: + items: + - const: serdes + pcsphy-handle: - $ref: /schemas/types.yaml#/definitions/phandle - description: A reference to the PCS (typically found on the SerDes) + $ref: /schemas/types.yaml#/definitions/phandle-array + minItems: 1 + maxItems: 3 + deprecated: true + description: See pcs-handle. + + pcs-handle: + minItems: 1 + maxItems: 3 + description: | + A reference to the various PCSs (typically found on the SerDes). If + pcs-handle-names is absent, and phy-connection-type is "xgmii", then the first + reference will be assumed to be for "xfi". Otherwise, if pcs-handle-names is + absent, then the first reference will be assumed to be for "sgmii". + + pcs-handle-names: + minItems: 1 + maxItems: 3 + items: + enum: + - sgmii + - qsgmii + - xfi + description: The type of each PCS in pcsphy-handle. tbi-handle: $ref: /schemas/types.yaml#/definitions/phandle @@ -100,6 +130,10 @@ required: - fsl,fman-ports - ptp-timer +dependencies: + pcs-handle-names: + - pcs-handle + allOf: - $ref: ethernet-controller.yaml# - if: @@ -110,14 +144,6 @@ allOf: then: required: - tbi-handle - - if: - properties: - compatible: - contains: - const: fsl,fman-memac - then: - required: - - pcsphy-handle unevaluatedProperties: false @@ -138,8 +164,9 @@ examples: reg = <0xe8000 0x1000>; fsl,fman-ports = <&fman0_rx_0x0c &fman0_tx_0x2c>; ptp-timer = <&ptp_timer0>; - pcsphy-handle = <&pcsphy4>; - phy-handle = <&sgmii_phy1>; - phy-connection-type = "sgmii"; + pcs-handle = <&pcsphy4>, <&qsgmiib_pcs1>; + pcs-handle-names = "sgmii", "qsgmii"; + phys = <&serdes1 1>; + phy-names = "serdes"; }; ... diff --git a/Documentation/devicetree/bindings/net/fsl-fman.txt b/Documentation/devicetree/bindings/net/fsl-fman.txt index b9055335db3b..bda4b41af074 100644 --- a/Documentation/devicetree/bindings/net/fsl-fman.txt +++ b/Documentation/devicetree/bindings/net/fsl-fman.txt @@ -320,8 +320,9 @@ For internal PHY device on internal mdio bus, a PHY node should be created. See the definition of the PHY node in booting-without-of.txt for an example of how to define a PHY (Internal PHY has no interrupt line). - For "fsl,fman-mdio" compatible internal mdio bus, the PHY is TBI PHY. -- For "fsl,fman-memac-mdio" compatible internal mdio bus, the PHY is PCS PHY, - PCS PHY addr must be '0'. +- For "fsl,fman-memac-mdio" compatible internal mdio bus, the PHY is PCS PHY. + The PCS PHY address should correspond to the value of the appropriate + MDEV_PORT. EXAMPLE From f392a1846489720fc2e063d1210633b6cf4ec5a4 Mon Sep 17 00:00:00 2001 From: "Russell King (Oracle)" Date: Mon, 17 Oct 2022 16:22:35 -0400 Subject: [PATCH 0185/2002] net: phylink: provide phylink_validate_mask_caps() helper Provide a helper that restricts the link modes according to the phylink capabilities. Signed-off-by: Russell King (Oracle) [rebased on net-next/master and added documentation] Signed-off-by: Sean Anderson Signed-off-by: David S. Miller --- drivers/net/phy/phylink.c | 43 +++++++++++++++++++++++++++------------ include/linux/phylink.h | 3 +++ 2 files changed, 33 insertions(+), 13 deletions(-) diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c index 75464df191ef..ef10f5a70e5a 100644 --- a/drivers/net/phy/phylink.c +++ b/drivers/net/phy/phylink.c @@ -561,6 +561,35 @@ unsigned long phylink_get_capabilities(phy_interface_t interface, } EXPORT_SYMBOL_GPL(phylink_get_capabilities); +/** + * phylink_validate_mask_caps() - Restrict link modes based on caps + * @supported: ethtool bitmask for supported link modes. + * @state: an (optional) pointer to a &struct phylink_link_state. + * @mac_capabilities: bitmask of MAC capabilities + * + * Calculate the supported link modes based on @mac_capabilities, and restrict + * @supported and @state based on that. Use this function if your capabiliies + * aren't constant, such as if they vary depending on the interface. + */ +void phylink_validate_mask_caps(unsigned long *supported, + struct phylink_link_state *state, + unsigned long mac_capabilities) +{ + __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, }; + unsigned long caps; + + phylink_set_port_modes(mask); + phylink_set(mask, Autoneg); + caps = phylink_get_capabilities(state->interface, mac_capabilities, + state->rate_matching); + phylink_caps_to_linkmodes(mask, caps); + + linkmode_and(supported, supported, mask); + if (state) + linkmode_and(state->advertising, state->advertising, mask); +} +EXPORT_SYMBOL_GPL(phylink_validate_mask_caps); + /** * phylink_generic_validate() - generic validate() callback implementation * @config: a pointer to a &struct phylink_config. @@ -569,24 +598,12 @@ EXPORT_SYMBOL_GPL(phylink_get_capabilities); * * Generic implementation of the validate() callback that MAC drivers can * use when they pass the range of supported interfaces and MAC capabilities. - * This makes use of phylink_get_linkmodes(). */ void phylink_generic_validate(struct phylink_config *config, unsigned long *supported, struct phylink_link_state *state) { - __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, }; - unsigned long caps; - - phylink_set_port_modes(mask); - phylink_set(mask, Autoneg); - caps = phylink_get_capabilities(state->interface, - config->mac_capabilities, - state->rate_matching); - phylink_caps_to_linkmodes(mask, caps); - - linkmode_and(supported, supported, mask); - linkmode_and(state->advertising, state->advertising, mask); + phylink_validate_mask_caps(supported, state, config->mac_capabilities); } EXPORT_SYMBOL_GPL(phylink_generic_validate); diff --git a/include/linux/phylink.h b/include/linux/phylink.h index 664dd409feb9..c29c3f174972 100644 --- a/include/linux/phylink.h +++ b/include/linux/phylink.h @@ -556,6 +556,9 @@ void phylink_caps_to_linkmodes(unsigned long *linkmodes, unsigned long caps); unsigned long phylink_get_capabilities(phy_interface_t interface, unsigned long mac_capabilities, int rate_matching); +void phylink_validate_mask_caps(unsigned long *supported, + struct phylink_link_state *state, + unsigned long caps); void phylink_generic_validate(struct phylink_config *config, unsigned long *supported, struct phylink_link_state *state); From 0fc83bd79589b3e354535a4761a4b2a483f2f14c Mon Sep 17 00:00:00 2001 From: Sean Anderson Date: Mon, 17 Oct 2022 16:22:36 -0400 Subject: [PATCH 0186/2002] net: fman: memac: Add serdes support This adds support for using a serdes which has to be configured. This is primarly in preparation for phylink conversion, which will then change the serdes mode dynamically. Signed-off-by: Sean Anderson Signed-off-by: David S. Miller --- .../net/ethernet/freescale/fman/fman_memac.c | 49 ++++++++++++++++++- 1 file changed, 47 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/freescale/fman/fman_memac.c b/drivers/net/ethernet/freescale/fman/fman_memac.c index 32d26cf17843..56a29f505590 100644 --- a/drivers/net/ethernet/freescale/fman/fman_memac.c +++ b/drivers/net/ethernet/freescale/fman/fman_memac.c @@ -13,6 +13,7 @@ #include #include #include +#include #include /* PCS registers */ @@ -324,6 +325,7 @@ struct fman_mac { void *fm; struct fman_rev_info fm_rev_info; bool basex_if; + struct phy *serdes; struct phy_device *pcsphy; bool allmulti_enabled; }; @@ -1203,17 +1205,56 @@ int memac_initialization(struct mac_device *mac_dev, } } + memac->serdes = devm_of_phy_get(mac_dev->dev, mac_node, "serdes"); + err = PTR_ERR(memac->serdes); + if (err == -ENODEV || err == -ENOSYS) { + dev_dbg(mac_dev->dev, "could not get (optional) serdes\n"); + memac->serdes = NULL; + } else if (IS_ERR(memac->serdes)) { + dev_err_probe(mac_dev->dev, err, "could not get serdes\n"); + goto _return_fm_mac_free; + } else { + err = phy_init(memac->serdes); + if (err) { + dev_err_probe(mac_dev->dev, err, + "could not initialize serdes\n"); + goto _return_fm_mac_free; + } + + err = phy_power_on(memac->serdes); + if (err) { + dev_err_probe(mac_dev->dev, err, + "could not power on serdes\n"); + goto _return_phy_exit; + } + + if (memac->phy_if == PHY_INTERFACE_MODE_SGMII || + memac->phy_if == PHY_INTERFACE_MODE_1000BASEX || + memac->phy_if == PHY_INTERFACE_MODE_2500BASEX || + memac->phy_if == PHY_INTERFACE_MODE_QSGMII || + memac->phy_if == PHY_INTERFACE_MODE_XGMII) { + err = phy_set_mode_ext(memac->serdes, PHY_MODE_ETHERNET, + memac->phy_if); + if (err) { + dev_err_probe(mac_dev->dev, err, + "could not set serdes mode to %s\n", + phy_modes(memac->phy_if)); + goto _return_phy_power_off; + } + } + } + if (!mac_dev->phy_node && of_phy_is_fixed_link(mac_node)) { struct phy_device *phy; err = of_phy_register_fixed_link(mac_node); if (err) - goto _return_fm_mac_free; + goto _return_phy_power_off; fixed_link = kzalloc(sizeof(*fixed_link), GFP_KERNEL); if (!fixed_link) { err = -ENOMEM; - goto _return_fm_mac_free; + goto _return_phy_power_off; } mac_dev->phy_node = of_node_get(mac_node); @@ -1242,6 +1283,10 @@ int memac_initialization(struct mac_device *mac_dev, goto _return; +_return_phy_power_off: + phy_power_off(memac->serdes); +_return_phy_exit: + phy_exit(memac->serdes); _return_fixed_link_free: kfree(fixed_link); _return_fm_mac_free: From a7c2a32e7f22cf5648c52e05a21725a9d0675649 Mon Sep 17 00:00:00 2001 From: Sean Anderson Date: Mon, 17 Oct 2022 16:22:37 -0400 Subject: [PATCH 0187/2002] net: fman: memac: Use lynx pcs driver Although not stated in the datasheet, as far as I can tell PCS for mEMACs is a "Lynx." By reusing the existing driver, we can remove the PCS management code from the memac driver. This requires calling some PCS functions manually which phylink would usually do for us, but we will let it do that soon. One problem is that we don't actually have a PCS for QSGMII. We pretend that each mEMAC's MDIO bus has four QSGMII PCSs, but this is not the case. Only the "base" mEMAC's MDIO bus has the four QSGMII PCSs. This is not an issue yet, because we never get the PCS state. However, it will be once the conversion to phylink is complete, since the links will appear to never come up. To get around this, we allow specifying multiple PCSs in pcsphy. This breaks backwards compatibility with old device trees, but only for QSGMII. IMO this is the only reasonable way to figure out what the actual QSGMII PCS is. Additionally, we now also support a separate XFI PCS. This can allow the SerDes driver to set different addresses for the SGMII and XFI PCSs so they can be accessed at the same time. Signed-off-by: Sean Anderson Signed-off-by: David S. Miller --- drivers/net/ethernet/freescale/fman/Kconfig | 3 + .../net/ethernet/freescale/fman/fman_memac.c | 260 +++++++----------- 2 files changed, 106 insertions(+), 157 deletions(-) diff --git a/drivers/net/ethernet/freescale/fman/Kconfig b/drivers/net/ethernet/freescale/fman/Kconfig index 48bf8088795d..8f5637db41dd 100644 --- a/drivers/net/ethernet/freescale/fman/Kconfig +++ b/drivers/net/ethernet/freescale/fman/Kconfig @@ -4,6 +4,9 @@ config FSL_FMAN depends on FSL_SOC || ARCH_LAYERSCAPE || COMPILE_TEST select GENERIC_ALLOCATOR select PHYLIB + select PHYLINK + select PCS + select PCS_LYNX select CRC32 default n help diff --git a/drivers/net/ethernet/freescale/fman/fman_memac.c b/drivers/net/ethernet/freescale/fman/fman_memac.c index 56a29f505590..43584946cae2 100644 --- a/drivers/net/ethernet/freescale/fman/fman_memac.c +++ b/drivers/net/ethernet/freescale/fman/fman_memac.c @@ -11,43 +11,12 @@ #include #include +#include #include #include #include #include -/* PCS registers */ -#define MDIO_SGMII_CR 0x00 -#define MDIO_SGMII_DEV_ABIL_SGMII 0x04 -#define MDIO_SGMII_LINK_TMR_L 0x12 -#define MDIO_SGMII_LINK_TMR_H 0x13 -#define MDIO_SGMII_IF_MODE 0x14 - -/* SGMII Control defines */ -#define SGMII_CR_AN_EN 0x1000 -#define SGMII_CR_RESTART_AN 0x0200 -#define SGMII_CR_FD 0x0100 -#define SGMII_CR_SPEED_SEL1_1G 0x0040 -#define SGMII_CR_DEF_VAL (SGMII_CR_AN_EN | SGMII_CR_FD | \ - SGMII_CR_SPEED_SEL1_1G) - -/* SGMII Device Ability for SGMII defines */ -#define MDIO_SGMII_DEV_ABIL_SGMII_MODE 0x4001 -#define MDIO_SGMII_DEV_ABIL_BASEX_MODE 0x01A0 - -/* Link timer define */ -#define LINK_TMR_L 0xa120 -#define LINK_TMR_H 0x0007 -#define LINK_TMR_L_BASEX 0xaf08 -#define LINK_TMR_H_BASEX 0x002f - -/* SGMII IF Mode defines */ -#define IF_MODE_USE_SGMII_AN 0x0002 -#define IF_MODE_SGMII_EN 0x0001 -#define IF_MODE_SGMII_SPEED_100M 0x0004 -#define IF_MODE_SGMII_SPEED_1G 0x0008 -#define IF_MODE_SGMII_DUPLEX_HALF 0x0010 - /* Num of additional exact match MAC adr regs */ #define MEMAC_NUM_OF_PADDRS 7 @@ -326,7 +295,9 @@ struct fman_mac { struct fman_rev_info fm_rev_info; bool basex_if; struct phy *serdes; - struct phy_device *pcsphy; + struct phylink_pcs *sgmii_pcs; + struct phylink_pcs *qsgmii_pcs; + struct phylink_pcs *xfi_pcs; bool allmulti_enabled; }; @@ -487,91 +458,22 @@ static u32 get_mac_addr_hash_code(u64 eth_addr) return xor_val; } -static void setup_sgmii_internal_phy(struct fman_mac *memac, - struct fixed_phy_status *fixed_link) +static void setup_sgmii_internal(struct fman_mac *memac, + struct phylink_pcs *pcs, + struct fixed_phy_status *fixed_link) { - u16 tmp_reg16; + __ETHTOOL_DECLARE_LINK_MODE_MASK(advertising); + phy_interface_t iface = memac->basex_if ? PHY_INTERFACE_MODE_1000BASEX : + PHY_INTERFACE_MODE_SGMII; + unsigned int mode = fixed_link ? MLO_AN_FIXED : MLO_AN_INBAND; - if (WARN_ON(!memac->pcsphy)) - return; - - /* SGMII mode */ - tmp_reg16 = IF_MODE_SGMII_EN; - if (!fixed_link) - /* AN enable */ - tmp_reg16 |= IF_MODE_USE_SGMII_AN; - else { - switch (fixed_link->speed) { - case 10: - /* For 10M: IF_MODE[SPEED_10M] = 0 */ - break; - case 100: - tmp_reg16 |= IF_MODE_SGMII_SPEED_100M; - break; - case 1000: - default: - tmp_reg16 |= IF_MODE_SGMII_SPEED_1G; - break; - } - if (!fixed_link->duplex) - tmp_reg16 |= IF_MODE_SGMII_DUPLEX_HALF; - } - phy_write(memac->pcsphy, MDIO_SGMII_IF_MODE, tmp_reg16); - - /* Device ability according to SGMII specification */ - tmp_reg16 = MDIO_SGMII_DEV_ABIL_SGMII_MODE; - phy_write(memac->pcsphy, MDIO_SGMII_DEV_ABIL_SGMII, tmp_reg16); - - /* Adjust link timer for SGMII - - * According to Cisco SGMII specification the timer should be 1.6 ms. - * The link_timer register is configured in units of the clock. - * - When running as 1G SGMII, Serdes clock is 125 MHz, so - * unit = 1 / (125*10^6 Hz) = 8 ns. - * 1.6 ms in units of 8 ns = 1.6ms / 8ns = 2*10^5 = 0x30d40 - * - When running as 2.5G SGMII, Serdes clock is 312.5 MHz, so - * unit = 1 / (312.5*10^6 Hz) = 3.2 ns. - * 1.6 ms in units of 3.2 ns = 1.6ms / 3.2ns = 5*10^5 = 0x7a120. - * Since link_timer value of 1G SGMII will be too short for 2.5 SGMII, - * we always set up here a value of 2.5 SGMII. - */ - phy_write(memac->pcsphy, MDIO_SGMII_LINK_TMR_H, LINK_TMR_H); - phy_write(memac->pcsphy, MDIO_SGMII_LINK_TMR_L, LINK_TMR_L); - - if (!fixed_link) - /* Restart AN */ - tmp_reg16 = SGMII_CR_DEF_VAL | SGMII_CR_RESTART_AN; + linkmode_set_pause(advertising, true, true); + pcs->ops->pcs_config(pcs, mode, iface, advertising, true); + if (fixed_link) + pcs->ops->pcs_link_up(pcs, mode, iface, fixed_link->speed, + fixed_link->duplex); else - /* AN disabled */ - tmp_reg16 = SGMII_CR_DEF_VAL & ~SGMII_CR_AN_EN; - phy_write(memac->pcsphy, 0x0, tmp_reg16); -} - -static void setup_sgmii_internal_phy_base_x(struct fman_mac *memac) -{ - u16 tmp_reg16; - - /* AN Device capability */ - tmp_reg16 = MDIO_SGMII_DEV_ABIL_BASEX_MODE; - phy_write(memac->pcsphy, MDIO_SGMII_DEV_ABIL_SGMII, tmp_reg16); - - /* Adjust link timer for SGMII - - * For Serdes 1000BaseX auto-negotiation the timer should be 10 ms. - * The link_timer register is configured in units of the clock. - * - When running as 1G SGMII, Serdes clock is 125 MHz, so - * unit = 1 / (125*10^6 Hz) = 8 ns. - * 10 ms in units of 8 ns = 10ms / 8ns = 1250000 = 0x1312d0 - * - When running as 2.5G SGMII, Serdes clock is 312.5 MHz, so - * unit = 1 / (312.5*10^6 Hz) = 3.2 ns. - * 10 ms in units of 3.2 ns = 10ms / 3.2ns = 3125000 = 0x2faf08. - * Since link_timer value of 1G SGMII will be too short for 2.5 SGMII, - * we always set up here a value of 2.5 SGMII. - */ - phy_write(memac->pcsphy, MDIO_SGMII_LINK_TMR_H, LINK_TMR_H_BASEX); - phy_write(memac->pcsphy, MDIO_SGMII_LINK_TMR_L, LINK_TMR_L_BASEX); - - /* Restart AN */ - tmp_reg16 = SGMII_CR_DEF_VAL | SGMII_CR_RESTART_AN; - phy_write(memac->pcsphy, 0x0, tmp_reg16); + pcs->ops->pcs_an_restart(pcs); } static int check_init_parameters(struct fman_mac *memac) @@ -983,7 +885,6 @@ static int memac_set_exception(struct fman_mac *memac, static int memac_init(struct fman_mac *memac) { struct memac_cfg *memac_drv_param; - u8 i; enet_addr_t eth_addr; bool slow_10g_if = false; struct fixed_phy_status *fixed_link = NULL; @@ -1036,32 +937,10 @@ static int memac_init(struct fman_mac *memac) iowrite32be(reg32, &memac->regs->command_config); } - if (memac->phy_if == PHY_INTERFACE_MODE_SGMII) { - /* Configure internal SGMII PHY */ - if (memac->basex_if) - setup_sgmii_internal_phy_base_x(memac); - else - setup_sgmii_internal_phy(memac, fixed_link); - } else if (memac->phy_if == PHY_INTERFACE_MODE_QSGMII) { - /* Configure 4 internal SGMII PHYs */ - for (i = 0; i < 4; i++) { - u8 qsmgii_phy_addr, phy_addr; - /* QSGMII PHY address occupies 3 upper bits of 5-bit - * phy_address; the lower 2 bits are used to extend - * register address space and access each one of 4 - * ports inside QSGMII. - */ - phy_addr = memac->pcsphy->mdio.addr; - qsmgii_phy_addr = (u8)((phy_addr << 2) | i); - memac->pcsphy->mdio.addr = qsmgii_phy_addr; - if (memac->basex_if) - setup_sgmii_internal_phy_base_x(memac); - else - setup_sgmii_internal_phy(memac, fixed_link); - - memac->pcsphy->mdio.addr = phy_addr; - } - } + if (memac->phy_if == PHY_INTERFACE_MODE_SGMII) + setup_sgmii_internal(memac, memac->sgmii_pcs, fixed_link); + else if (memac->phy_if == PHY_INTERFACE_MODE_QSGMII) + setup_sgmii_internal(memac, memac->qsgmii_pcs, fixed_link); /* Max Frame Length */ err = fman_set_mac_max_frame(memac->fm, memac->mac_id, @@ -1097,12 +976,25 @@ static int memac_init(struct fman_mac *memac) return 0; } +static void pcs_put(struct phylink_pcs *pcs) +{ + struct mdio_device *mdiodev; + + if (IS_ERR_OR_NULL(pcs)) + return; + + mdiodev = lynx_get_mdio_device(pcs); + lynx_pcs_destroy(pcs); + mdio_device_free(mdiodev); +} + static int memac_free(struct fman_mac *memac) { free_init_resources(memac); - if (memac->pcsphy) - put_device(&memac->pcsphy->mdio.dev); + pcs_put(memac->sgmii_pcs); + pcs_put(memac->qsgmii_pcs); + pcs_put(memac->xfi_pcs); kfree(memac->memac_drv_param); kfree(memac); @@ -1153,12 +1045,31 @@ static struct fman_mac *memac_config(struct mac_device *mac_dev, return memac; } +static struct phylink_pcs *memac_pcs_create(struct device_node *mac_node, + int index) +{ + struct device_node *node; + struct mdio_device *mdiodev = NULL; + struct phylink_pcs *pcs; + + node = of_parse_phandle(mac_node, "pcsphy-handle", index); + if (node && of_device_is_available(node)) + mdiodev = of_mdio_find_device(node); + of_node_put(node); + + if (!mdiodev) + return ERR_PTR(-EPROBE_DEFER); + + pcs = lynx_pcs_create(mdiodev); + return pcs; +} + int memac_initialization(struct mac_device *mac_dev, struct device_node *mac_node, struct fman_mac_params *params) { int err; - struct device_node *phy_node; + struct phylink_pcs *pcs; struct fixed_phy_status *fixed_link; struct fman_mac *memac; @@ -1188,23 +1099,58 @@ int memac_initialization(struct mac_device *mac_dev, memac = mac_dev->fman_mac; memac->memac_drv_param->max_frame_length = fman_get_max_frm(); memac->memac_drv_param->reset_on_init = true; - if (memac->phy_if == PHY_INTERFACE_MODE_SGMII || - memac->phy_if == PHY_INTERFACE_MODE_QSGMII) { - phy_node = of_parse_phandle(mac_node, "pcsphy-handle", 0); - if (!phy_node) { - pr_err("PCS PHY node is not available\n"); - err = -EINVAL; - goto _return_fm_mac_free; - } - memac->pcsphy = of_phy_find_device(phy_node); - if (!memac->pcsphy) { - pr_err("of_phy_find_device (PCS PHY) failed\n"); - err = -EINVAL; + err = of_property_match_string(mac_node, "pcs-handle-names", "xfi"); + if (err >= 0) { + memac->xfi_pcs = memac_pcs_create(mac_node, err); + if (IS_ERR(memac->xfi_pcs)) { + err = PTR_ERR(memac->xfi_pcs); + dev_err_probe(mac_dev->dev, err, "missing xfi pcs\n"); goto _return_fm_mac_free; } + } else if (err != -EINVAL && err != -ENODATA) { + goto _return_fm_mac_free; } + err = of_property_match_string(mac_node, "pcs-handle-names", "qsgmii"); + if (err >= 0) { + memac->qsgmii_pcs = memac_pcs_create(mac_node, err); + if (IS_ERR(memac->qsgmii_pcs)) { + err = PTR_ERR(memac->qsgmii_pcs); + dev_err_probe(mac_dev->dev, err, + "missing qsgmii pcs\n"); + goto _return_fm_mac_free; + } + } else if (err != -EINVAL && err != -ENODATA) { + goto _return_fm_mac_free; + } + + /* For compatibility, if pcs-handle-names is missing, we assume this + * phy is the first one in pcsphy-handle + */ + err = of_property_match_string(mac_node, "pcs-handle-names", "sgmii"); + if (err == -EINVAL || err == -ENODATA) + pcs = memac_pcs_create(mac_node, 0); + else if (err < 0) + goto _return_fm_mac_free; + else + pcs = memac_pcs_create(mac_node, err); + + if (!pcs) { + dev_err(mac_dev->dev, "missing pcs\n"); + err = -ENOENT; + goto _return_fm_mac_free; + } + + /* If err is set here, it means that pcs-handle-names was missing above + * (and therefore that xfi_pcs cannot be set). If we are defaulting to + * XGMII, assume this is for XFI. Otherwise, assume it is for SGMII. + */ + if (err && mac_dev->phy_if == PHY_INTERFACE_MODE_XGMII) + memac->xfi_pcs = pcs; + else + memac->sgmii_pcs = pcs; + memac->serdes = devm_of_phy_get(mac_dev->dev, mac_node, "serdes"); err = PTR_ERR(memac->serdes); if (err == -ENODEV || err == -ENOSYS) { From 5d93cfcf7360eac9903774fe94f626c9ead2049d Mon Sep 17 00:00:00 2001 From: Sean Anderson Date: Mon, 17 Oct 2022 16:22:38 -0400 Subject: [PATCH 0188/2002] net: dpaa: Convert to phylink This converts DPAA to phylink. All macs are converted. This should work with no device tree modifications (including those made in this series), except for QSGMII (as noted previously). The mEMAC configuration is one of the tricker areas. I have tried to capture all the restrictions across the various models. Most of the time, we assume that if the serdes supports a mode or the phy-interface-mode specifies it, then we support it. The only place we can't do this is (RG)MII, since there's no serdes. In that case, we rely on a (new) devicetree property. There are also several cases where half-duplex is broken. Unfortunately, only a single compatible is used for the MAC, so we have to use the board compatible instead. The 10GEC conversion is very straightforward, since it only supports XAUI. There is generally nothing to configure. The dTSEC conversion is broadly similar to mEMAC, but is simpler because we don't support configuring the SerDes (though this can be easily added) and we don't have multiple PCSs. From what I can tell, there's nothing different in the driver or documentation between SGMII and 1000BASE-X except for the advertising. Similarly, I couldn't find anything about 2500BASE-X. In both cases, I treat them like SGMII. These modes aren't used by any in-tree boards. Similarly, despite being mentioned in the driver, I couldn't find any documented SoCs which supported QSGMII. I have left it unimplemented for now. Signed-off-by: Sean Anderson Signed-off-by: David S. Miller --- drivers/net/ethernet/freescale/dpaa/Kconfig | 4 +- .../net/ethernet/freescale/dpaa/dpaa_eth.c | 89 +-- .../ethernet/freescale/dpaa/dpaa_ethtool.c | 90 +-- drivers/net/ethernet/freescale/fman/Kconfig | 1 - .../net/ethernet/freescale/fman/fman_dtsec.c | 460 +++++++-------- .../net/ethernet/freescale/fman/fman_mac.h | 10 - .../net/ethernet/freescale/fman/fman_memac.c | 551 +++++++++--------- .../net/ethernet/freescale/fman/fman_tgec.c | 131 ++--- drivers/net/ethernet/freescale/fman/mac.c | 168 +----- drivers/net/ethernet/freescale/fman/mac.h | 23 +- 10 files changed, 615 insertions(+), 912 deletions(-) diff --git a/drivers/net/ethernet/freescale/dpaa/Kconfig b/drivers/net/ethernet/freescale/dpaa/Kconfig index 0e1439fd00bd..2b560661c82a 100644 --- a/drivers/net/ethernet/freescale/dpaa/Kconfig +++ b/drivers/net/ethernet/freescale/dpaa/Kconfig @@ -2,8 +2,8 @@ menuconfig FSL_DPAA_ETH tristate "DPAA Ethernet" depends on FSL_DPAA && FSL_FMAN - select PHYLIB - select FIXED_PHY + select PHYLINK + select PCS_LYNX help Data Path Acceleration Architecture Ethernet driver, supporting the Freescale QorIQ chips. diff --git a/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c b/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c index 31cfa121333d..021ba999d86d 100644 --- a/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c +++ b/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c @@ -264,8 +264,19 @@ static int dpaa_netdev_init(struct net_device *net_dev, net_dev->needed_headroom = priv->tx_headroom; net_dev->watchdog_timeo = msecs_to_jiffies(tx_timeout); - mac_dev->net_dev = net_dev; + /* The rest of the config is filled in by the mac device already */ + mac_dev->phylink_config.dev = &net_dev->dev; + mac_dev->phylink_config.type = PHYLINK_NETDEV; mac_dev->update_speed = dpaa_eth_cgr_set_speed; + mac_dev->phylink = phylink_create(&mac_dev->phylink_config, + dev_fwnode(mac_dev->dev), + mac_dev->phy_if, + mac_dev->phylink_ops); + if (IS_ERR(mac_dev->phylink)) { + err = PTR_ERR(mac_dev->phylink); + dev_err_probe(dev, err, "Could not create phylink\n"); + return err; + } /* start without the RUNNING flag, phylib controls it later */ netif_carrier_off(net_dev); @@ -273,6 +284,7 @@ static int dpaa_netdev_init(struct net_device *net_dev, err = register_netdev(net_dev); if (err < 0) { dev_err(dev, "register_netdev() = %d\n", err); + phylink_destroy(mac_dev->phylink); return err; } @@ -294,8 +306,7 @@ static int dpaa_stop(struct net_device *net_dev) */ msleep(200); - if (mac_dev->phy_dev) - phy_stop(mac_dev->phy_dev); + phylink_stop(mac_dev->phylink); mac_dev->disable(mac_dev->fman_mac); for (i = 0; i < ARRAY_SIZE(mac_dev->port); i++) { @@ -304,8 +315,7 @@ static int dpaa_stop(struct net_device *net_dev) err = error; } - if (net_dev->phydev) - phy_disconnect(net_dev->phydev); + phylink_disconnect_phy(mac_dev->phylink); net_dev->phydev = NULL; msleep(200); @@ -833,10 +843,10 @@ static int dpaa_eth_cgr_init(struct dpaa_priv *priv) /* Set different thresholds based on the configured MAC speed. * This may turn suboptimal if the MAC is reconfigured at another - * speed, so MACs must call dpaa_eth_cgr_set_speed in their adjust_link + * speed, so MACs must call dpaa_eth_cgr_set_speed in their link_up * callback. */ - if (priv->mac_dev->if_support & SUPPORTED_10000baseT_Full) + if (priv->mac_dev->phylink_config.mac_capabilities & MAC_10000FD) cs_th = DPAA_CS_THRESHOLD_10G; else cs_th = DPAA_CS_THRESHOLD_1G; @@ -865,7 +875,7 @@ out_error: static void dpaa_eth_cgr_set_speed(struct mac_device *mac_dev, int speed) { - struct net_device *net_dev = mac_dev->net_dev; + struct net_device *net_dev = to_net_dev(mac_dev->phylink_config.dev); struct dpaa_priv *priv = netdev_priv(net_dev); struct qm_mcc_initcgr opts = { }; u32 cs_th; @@ -2904,58 +2914,6 @@ static void dpaa_eth_napi_disable(struct dpaa_priv *priv) } } -static void dpaa_adjust_link(struct net_device *net_dev) -{ - struct mac_device *mac_dev; - struct dpaa_priv *priv; - - priv = netdev_priv(net_dev); - mac_dev = priv->mac_dev; - mac_dev->adjust_link(mac_dev); -} - -/* The Aquantia PHYs are capable of performing rate adaptation */ -#define PHY_VEND_AQUANTIA 0x03a1b400 -#define PHY_VEND_AQUANTIA2 0x31c31c00 - -static int dpaa_phy_init(struct net_device *net_dev) -{ - __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, }; - struct mac_device *mac_dev; - struct phy_device *phy_dev; - struct dpaa_priv *priv; - u32 phy_vendor; - - priv = netdev_priv(net_dev); - mac_dev = priv->mac_dev; - - phy_dev = of_phy_connect(net_dev, mac_dev->phy_node, - &dpaa_adjust_link, 0, - mac_dev->phy_if); - if (!phy_dev) { - netif_err(priv, ifup, net_dev, "init_phy() failed\n"); - return -ENODEV; - } - - phy_vendor = phy_dev->drv->phy_id & GENMASK(31, 10); - /* Unless the PHY is capable of rate adaptation */ - if (mac_dev->phy_if != PHY_INTERFACE_MODE_XGMII || - (phy_vendor != PHY_VEND_AQUANTIA && - phy_vendor != PHY_VEND_AQUANTIA2)) { - /* remove any features not supported by the controller */ - ethtool_convert_legacy_u32_to_link_mode(mask, - mac_dev->if_support); - linkmode_and(phy_dev->supported, phy_dev->supported, mask); - } - - phy_support_asym_pause(phy_dev); - - mac_dev->phy_dev = phy_dev; - net_dev->phydev = phy_dev; - - return 0; -} - static int dpaa_open(struct net_device *net_dev) { struct mac_device *mac_dev; @@ -2966,7 +2924,8 @@ static int dpaa_open(struct net_device *net_dev) mac_dev = priv->mac_dev; dpaa_eth_napi_enable(priv); - err = dpaa_phy_init(net_dev); + err = phylink_of_phy_connect(mac_dev->phylink, + mac_dev->dev->of_node, 0); if (err) goto phy_init_failed; @@ -2981,7 +2940,7 @@ static int dpaa_open(struct net_device *net_dev) netif_err(priv, ifup, net_dev, "mac_dev->enable() = %d\n", err); goto mac_start_failed; } - phy_start(priv->mac_dev->phy_dev); + phylink_start(mac_dev->phylink); netif_tx_start_all_queues(net_dev); @@ -2990,6 +2949,7 @@ static int dpaa_open(struct net_device *net_dev) mac_start_failed: for (i = 0; i < ARRAY_SIZE(mac_dev->port); i++) fman_port_disable(mac_dev->port[i]); + phylink_disconnect_phy(mac_dev->phylink); phy_init_failed: dpaa_eth_napi_disable(priv); @@ -3145,10 +3105,12 @@ static int dpaa_ts_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) static int dpaa_ioctl(struct net_device *net_dev, struct ifreq *rq, int cmd) { int ret = -EINVAL; + struct dpaa_priv *priv = netdev_priv(net_dev); if (cmd == SIOCGMIIREG) { if (net_dev->phydev) - return phy_mii_ioctl(net_dev->phydev, rq, cmd); + return phylink_mii_ioctl(priv->mac_dev->phylink, rq, + cmd); } if (cmd == SIOCSHWTSTAMP) @@ -3551,6 +3513,7 @@ static int dpaa_remove(struct platform_device *pdev) dev_set_drvdata(dev, NULL); unregister_netdev(net_dev); + phylink_destroy(priv->mac_dev->phylink); err = dpaa_fq_free(dev, &priv->dpaa_fq_list); diff --git a/drivers/net/ethernet/freescale/dpaa/dpaa_ethtool.c b/drivers/net/ethernet/freescale/dpaa/dpaa_ethtool.c index 769e936a263c..9c71cbbb13d8 100644 --- a/drivers/net/ethernet/freescale/dpaa/dpaa_ethtool.c +++ b/drivers/net/ethernet/freescale/dpaa/dpaa_ethtool.c @@ -54,27 +54,19 @@ static char dpaa_stats_global[][ETH_GSTRING_LEN] = { static int dpaa_get_link_ksettings(struct net_device *net_dev, struct ethtool_link_ksettings *cmd) { - if (!net_dev->phydev) - return 0; + struct dpaa_priv *priv = netdev_priv(net_dev); + struct mac_device *mac_dev = priv->mac_dev; - phy_ethtool_ksettings_get(net_dev->phydev, cmd); - - return 0; + return phylink_ethtool_ksettings_get(mac_dev->phylink, cmd); } static int dpaa_set_link_ksettings(struct net_device *net_dev, const struct ethtool_link_ksettings *cmd) { - int err; + struct dpaa_priv *priv = netdev_priv(net_dev); + struct mac_device *mac_dev = priv->mac_dev; - if (!net_dev->phydev) - return -ENODEV; - - err = phy_ethtool_ksettings_set(net_dev->phydev, cmd); - if (err < 0) - netdev_err(net_dev, "phy_ethtool_ksettings_set() = %d\n", err); - - return err; + return phylink_ethtool_ksettings_set(mac_dev->phylink, cmd); } static void dpaa_get_drvinfo(struct net_device *net_dev, @@ -99,80 +91,28 @@ static void dpaa_set_msglevel(struct net_device *net_dev, static int dpaa_nway_reset(struct net_device *net_dev) { - int err; + struct dpaa_priv *priv = netdev_priv(net_dev); + struct mac_device *mac_dev = priv->mac_dev; - if (!net_dev->phydev) - return -ENODEV; - - err = 0; - if (net_dev->phydev->autoneg) { - err = phy_start_aneg(net_dev->phydev); - if (err < 0) - netdev_err(net_dev, "phy_start_aneg() = %d\n", - err); - } - - return err; + return phylink_ethtool_nway_reset(mac_dev->phylink); } static void dpaa_get_pauseparam(struct net_device *net_dev, struct ethtool_pauseparam *epause) { - struct mac_device *mac_dev; - struct dpaa_priv *priv; + struct dpaa_priv *priv = netdev_priv(net_dev); + struct mac_device *mac_dev = priv->mac_dev; - priv = netdev_priv(net_dev); - mac_dev = priv->mac_dev; - - if (!net_dev->phydev) - return; - - epause->autoneg = mac_dev->autoneg_pause; - epause->rx_pause = mac_dev->rx_pause_active; - epause->tx_pause = mac_dev->tx_pause_active; + phylink_ethtool_get_pauseparam(mac_dev->phylink, epause); } static int dpaa_set_pauseparam(struct net_device *net_dev, struct ethtool_pauseparam *epause) { - struct mac_device *mac_dev; - struct phy_device *phydev; - bool rx_pause, tx_pause; - struct dpaa_priv *priv; - int err; + struct dpaa_priv *priv = netdev_priv(net_dev); + struct mac_device *mac_dev = priv->mac_dev; - priv = netdev_priv(net_dev); - mac_dev = priv->mac_dev; - - phydev = net_dev->phydev; - if (!phydev) { - netdev_err(net_dev, "phy device not initialized\n"); - return -ENODEV; - } - - if (!phy_validate_pause(phydev, epause)) - return -EINVAL; - - /* The MAC should know how to handle PAUSE frame autonegotiation before - * adjust_link is triggered by a forced renegotiation of sym/asym PAUSE - * settings. - */ - mac_dev->autoneg_pause = !!epause->autoneg; - mac_dev->rx_pause_req = !!epause->rx_pause; - mac_dev->tx_pause_req = !!epause->tx_pause; - - /* Determine the sym/asym advertised PAUSE capabilities from the desired - * rx/tx pause settings. - */ - - phy_set_asym_pause(phydev, epause->rx_pause, epause->tx_pause); - - fman_get_pause_cfg(mac_dev, &rx_pause, &tx_pause); - err = fman_set_mac_active_pause(mac_dev, rx_pause, tx_pause); - if (err < 0) - netdev_err(net_dev, "set_mac_active_pause() = %d\n", err); - - return err; + return phylink_ethtool_set_pauseparam(mac_dev->phylink, epause); } static int dpaa_get_sset_count(struct net_device *net_dev, int type) diff --git a/drivers/net/ethernet/freescale/fman/Kconfig b/drivers/net/ethernet/freescale/fman/Kconfig index 8f5637db41dd..e76a3d262b2b 100644 --- a/drivers/net/ethernet/freescale/fman/Kconfig +++ b/drivers/net/ethernet/freescale/fman/Kconfig @@ -3,7 +3,6 @@ config FSL_FMAN tristate "FMan support" depends on FSL_SOC || ARCH_LAYERSCAPE || COMPILE_TEST select GENERIC_ALLOCATOR - select PHYLIB select PHYLINK select PCS select PCS_LYNX diff --git a/drivers/net/ethernet/freescale/fman/fman_dtsec.c b/drivers/net/ethernet/freescale/fman/fman_dtsec.c index 6617932fd3fd..3c87820ca202 100644 --- a/drivers/net/ethernet/freescale/fman/fman_dtsec.c +++ b/drivers/net/ethernet/freescale/fman/fman_dtsec.c @@ -17,6 +17,7 @@ #include #include #include +#include /* TBI register addresses */ #define MII_TBICON 0x11 @@ -29,9 +30,6 @@ #define TBICON_CLK_SELECT 0x0020 /* Clock select */ #define TBICON_MI_MODE 0x0010 /* GMII mode (TBI if not set) */ -#define TBIANA_SGMII 0x4001 -#define TBIANA_1000X 0x01a0 - /* Interrupt Mask Register (IMASK) */ #define DTSEC_IMASK_BREN 0x80000000 #define DTSEC_IMASK_RXCEN 0x40000000 @@ -92,9 +90,10 @@ #define DTSEC_ECNTRL_GMIIM 0x00000040 #define DTSEC_ECNTRL_TBIM 0x00000020 -#define DTSEC_ECNTRL_SGMIIM 0x00000002 #define DTSEC_ECNTRL_RPM 0x00000010 #define DTSEC_ECNTRL_R100M 0x00000008 +#define DTSEC_ECNTRL_RMM 0x00000004 +#define DTSEC_ECNTRL_SGMIIM 0x00000002 #define DTSEC_ECNTRL_QSGMIIM 0x00000001 #define TCTRL_TTSE 0x00000040 @@ -318,7 +317,8 @@ struct fman_mac { void *fm; struct fman_rev_info fm_rev_info; bool basex_if; - struct phy_device *tbiphy; + struct mdio_device *tbidev; + struct phylink_pcs pcs; }; static void set_dflts(struct dtsec_cfg *cfg) @@ -356,56 +356,14 @@ static int init(struct dtsec_regs __iomem *regs, struct dtsec_cfg *cfg, phy_interface_t iface, u16 iface_speed, u64 addr, u32 exception_mask, u8 tbi_addr) { - bool is_rgmii, is_sgmii, is_qsgmii; enet_addr_t eth_addr; - u32 tmp; + u32 tmp = 0; int i; /* Soft reset */ iowrite32be(MACCFG1_SOFT_RESET, ®s->maccfg1); iowrite32be(0, ®s->maccfg1); - /* dtsec_id2 */ - tmp = ioread32be(®s->tsec_id2); - - /* check RGMII support */ - if (iface == PHY_INTERFACE_MODE_RGMII || - iface == PHY_INTERFACE_MODE_RGMII_ID || - iface == PHY_INTERFACE_MODE_RGMII_RXID || - iface == PHY_INTERFACE_MODE_RGMII_TXID || - iface == PHY_INTERFACE_MODE_RMII) - if (tmp & DTSEC_ID2_INT_REDUCED_OFF) - return -EINVAL; - - if (iface == PHY_INTERFACE_MODE_SGMII || - iface == PHY_INTERFACE_MODE_MII) - if (tmp & DTSEC_ID2_INT_REDUCED_OFF) - return -EINVAL; - - is_rgmii = iface == PHY_INTERFACE_MODE_RGMII || - iface == PHY_INTERFACE_MODE_RGMII_ID || - iface == PHY_INTERFACE_MODE_RGMII_RXID || - iface == PHY_INTERFACE_MODE_RGMII_TXID; - is_sgmii = iface == PHY_INTERFACE_MODE_SGMII; - is_qsgmii = iface == PHY_INTERFACE_MODE_QSGMII; - - tmp = 0; - if (is_rgmii || iface == PHY_INTERFACE_MODE_GMII) - tmp |= DTSEC_ECNTRL_GMIIM; - if (is_sgmii) - tmp |= (DTSEC_ECNTRL_SGMIIM | DTSEC_ECNTRL_TBIM); - if (is_qsgmii) - tmp |= (DTSEC_ECNTRL_SGMIIM | DTSEC_ECNTRL_TBIM | - DTSEC_ECNTRL_QSGMIIM); - if (is_rgmii) - tmp |= DTSEC_ECNTRL_RPM; - if (iface_speed == SPEED_100) - tmp |= DTSEC_ECNTRL_R100M; - - iowrite32be(tmp, ®s->ecntrl); - - tmp = 0; - if (cfg->tx_pause_time) tmp |= cfg->tx_pause_time; if (cfg->tx_pause_time_extd) @@ -446,17 +404,10 @@ static int init(struct dtsec_regs __iomem *regs, struct dtsec_cfg *cfg, tmp = 0; - if (iface_speed < SPEED_1000) - tmp |= MACCFG2_NIBBLE_MODE; - else if (iface_speed == SPEED_1000) - tmp |= MACCFG2_BYTE_MODE; - tmp |= (cfg->preamble_len << MACCFG2_PREAMBLE_LENGTH_SHIFT) & MACCFG2_PREAMBLE_LENGTH_MASK; if (cfg->tx_pad_crc) tmp |= MACCFG2_PAD_CRC_EN; - /* Full Duplex */ - tmp |= MACCFG2_FULL_DUPLEX; iowrite32be(tmp, ®s->maccfg2); tmp = (((cfg->non_back_to_back_ipg1 << @@ -525,10 +476,6 @@ static void set_bucket(struct dtsec_regs __iomem *regs, int bucket, static int check_init_parameters(struct fman_mac *dtsec) { - if (dtsec->max_speed >= SPEED_10000) { - pr_err("1G MAC driver supports 1G or lower speeds\n"); - return -EINVAL; - } if ((dtsec->dtsec_drv_param)->rx_prepend > MAX_PACKET_ALIGNMENT) { pr_err("packetAlignmentPadding can't be > than %d\n", @@ -630,22 +577,10 @@ static int get_exception_flag(enum fman_mac_exceptions exception) return bit_mask; } -static bool is_init_done(struct dtsec_cfg *dtsec_drv_params) -{ - /* Checks if dTSEC driver parameters were initialized */ - if (!dtsec_drv_params) - return true; - - return false; -} - static u16 dtsec_get_max_frame_length(struct fman_mac *dtsec) { struct dtsec_regs __iomem *regs = dtsec->regs; - if (is_init_done(dtsec->dtsec_drv_param)) - return 0; - return (u16)ioread32be(®s->maxfrm); } @@ -682,6 +617,7 @@ static void dtsec_isr(void *handle) dtsec->exception_cb(dtsec->dev_id, FM_MAC_EX_1G_COL_RET_LMT); if (event & DTSEC_IMASK_XFUNEN) { /* FM_TX_LOCKUP_ERRATA_DTSEC6 Errata workaround */ + /* FIXME: This races with the rest of the driver! */ if (dtsec->fm_rev_info.major == 2) { u32 tpkt1, tmp_reg1, tpkt2, tmp_reg2, i; /* a. Write 0x00E0_0C00 to DTSEC_ID @@ -814,6 +750,43 @@ static void free_init_resources(struct fman_mac *dtsec) dtsec->unicast_addr_hash = NULL; } +static struct fman_mac *pcs_to_dtsec(struct phylink_pcs *pcs) +{ + return container_of(pcs, struct fman_mac, pcs); +} + +static void dtsec_pcs_get_state(struct phylink_pcs *pcs, + struct phylink_link_state *state) +{ + struct fman_mac *dtsec = pcs_to_dtsec(pcs); + + phylink_mii_c22_pcs_get_state(dtsec->tbidev, state); +} + +static int dtsec_pcs_config(struct phylink_pcs *pcs, unsigned int mode, + phy_interface_t interface, + const unsigned long *advertising, + bool permit_pause_to_mac) +{ + struct fman_mac *dtsec = pcs_to_dtsec(pcs); + + return phylink_mii_c22_pcs_config(dtsec->tbidev, mode, interface, + advertising); +} + +static void dtsec_pcs_an_restart(struct phylink_pcs *pcs) +{ + struct fman_mac *dtsec = pcs_to_dtsec(pcs); + + phylink_mii_c22_pcs_an_restart(dtsec->tbidev); +} + +static const struct phylink_pcs_ops dtsec_pcs_ops = { + .pcs_get_state = dtsec_pcs_get_state, + .pcs_config = dtsec_pcs_config, + .pcs_an_restart = dtsec_pcs_an_restart, +}; + static void graceful_start(struct fman_mac *dtsec) { struct dtsec_regs __iomem *regs = dtsec->regs; @@ -854,36 +827,11 @@ static void graceful_stop(struct fman_mac *dtsec) static int dtsec_enable(struct fman_mac *dtsec) { - struct dtsec_regs __iomem *regs = dtsec->regs; - u32 tmp; - - if (!is_init_done(dtsec->dtsec_drv_param)) - return -EINVAL; - - /* Enable */ - tmp = ioread32be(®s->maccfg1); - tmp |= MACCFG1_RX_EN | MACCFG1_TX_EN; - iowrite32be(tmp, ®s->maccfg1); - - /* Graceful start - clear the graceful Rx/Tx stop bit */ - graceful_start(dtsec); - return 0; } static void dtsec_disable(struct fman_mac *dtsec) { - struct dtsec_regs __iomem *regs = dtsec->regs; - u32 tmp; - - WARN_ON_ONCE(!is_init_done(dtsec->dtsec_drv_param)); - - /* Graceful stop - Assert the graceful Rx/Tx stop bit */ - graceful_stop(dtsec); - - tmp = ioread32be(®s->maccfg1); - tmp &= ~(MACCFG1_RX_EN | MACCFG1_TX_EN); - iowrite32be(tmp, ®s->maccfg1); } static int dtsec_set_tx_pause_frames(struct fman_mac *dtsec, @@ -894,11 +842,6 @@ static int dtsec_set_tx_pause_frames(struct fman_mac *dtsec, struct dtsec_regs __iomem *regs = dtsec->regs; u32 ptv = 0; - if (!is_init_done(dtsec->dtsec_drv_param)) - return -EINVAL; - - graceful_stop(dtsec); - if (pause_time) { /* FM_BAD_TX_TS_IN_B_2_B_ERRATA_DTSEC_A003 Errata workaround */ if (dtsec->fm_rev_info.major == 2 && pause_time <= 320) { @@ -919,8 +862,6 @@ static int dtsec_set_tx_pause_frames(struct fman_mac *dtsec, iowrite32be(ioread32be(®s->maccfg1) & ~MACCFG1_TX_FLOW, ®s->maccfg1); - graceful_start(dtsec); - return 0; } @@ -929,11 +870,6 @@ static int dtsec_accept_rx_pause_frames(struct fman_mac *dtsec, bool en) struct dtsec_regs __iomem *regs = dtsec->regs; u32 tmp; - if (!is_init_done(dtsec->dtsec_drv_param)) - return -EINVAL; - - graceful_stop(dtsec); - tmp = ioread32be(®s->maccfg1); if (en) tmp |= MACCFG1_RX_FLOW; @@ -941,17 +877,125 @@ static int dtsec_accept_rx_pause_frames(struct fman_mac *dtsec, bool en) tmp &= ~MACCFG1_RX_FLOW; iowrite32be(tmp, ®s->maccfg1); - graceful_start(dtsec); - return 0; } +static struct phylink_pcs *dtsec_select_pcs(struct phylink_config *config, + phy_interface_t iface) +{ + struct fman_mac *dtsec = fman_config_to_mac(config)->fman_mac; + + switch (iface) { + case PHY_INTERFACE_MODE_SGMII: + case PHY_INTERFACE_MODE_1000BASEX: + case PHY_INTERFACE_MODE_2500BASEX: + return &dtsec->pcs; + default: + return NULL; + } +} + +static void dtsec_mac_config(struct phylink_config *config, unsigned int mode, + const struct phylink_link_state *state) +{ + struct mac_device *mac_dev = fman_config_to_mac(config); + struct dtsec_regs __iomem *regs = mac_dev->fman_mac->regs; + u32 tmp; + + switch (state->interface) { + case PHY_INTERFACE_MODE_RMII: + tmp = DTSEC_ECNTRL_RMM; + break; + case PHY_INTERFACE_MODE_RGMII: + case PHY_INTERFACE_MODE_RGMII_ID: + case PHY_INTERFACE_MODE_RGMII_RXID: + case PHY_INTERFACE_MODE_RGMII_TXID: + tmp = DTSEC_ECNTRL_GMIIM | DTSEC_ECNTRL_RPM; + break; + case PHY_INTERFACE_MODE_SGMII: + case PHY_INTERFACE_MODE_1000BASEX: + case PHY_INTERFACE_MODE_2500BASEX: + tmp = DTSEC_ECNTRL_TBIM | DTSEC_ECNTRL_SGMIIM; + break; + default: + dev_warn(mac_dev->dev, "cannot configure dTSEC for %s\n", + phy_modes(state->interface)); + return; + } + + iowrite32be(tmp, ®s->ecntrl); +} + +static void dtsec_link_up(struct phylink_config *config, struct phy_device *phy, + unsigned int mode, phy_interface_t interface, + int speed, int duplex, bool tx_pause, bool rx_pause) +{ + struct mac_device *mac_dev = fman_config_to_mac(config); + struct fman_mac *dtsec = mac_dev->fman_mac; + struct dtsec_regs __iomem *regs = dtsec->regs; + u16 pause_time = tx_pause ? FSL_FM_PAUSE_TIME_ENABLE : + FSL_FM_PAUSE_TIME_DISABLE; + u32 tmp; + + dtsec_set_tx_pause_frames(dtsec, 0, pause_time, 0); + dtsec_accept_rx_pause_frames(dtsec, rx_pause); + + tmp = ioread32be(®s->ecntrl); + if (speed == SPEED_100) + tmp |= DTSEC_ECNTRL_R100M; + else + tmp &= ~DTSEC_ECNTRL_R100M; + iowrite32be(tmp, ®s->ecntrl); + + tmp = ioread32be(®s->maccfg2); + tmp &= ~(MACCFG2_NIBBLE_MODE | MACCFG2_BYTE_MODE | MACCFG2_FULL_DUPLEX); + if (speed >= SPEED_1000) + tmp |= MACCFG2_BYTE_MODE; + else + tmp |= MACCFG2_NIBBLE_MODE; + + if (duplex == DUPLEX_FULL) + tmp |= MACCFG2_FULL_DUPLEX; + + iowrite32be(tmp, ®s->maccfg2); + + mac_dev->update_speed(mac_dev, speed); + + /* Enable */ + tmp = ioread32be(®s->maccfg1); + tmp |= MACCFG1_RX_EN | MACCFG1_TX_EN; + iowrite32be(tmp, ®s->maccfg1); + + /* Graceful start - clear the graceful Rx/Tx stop bit */ + graceful_start(dtsec); +} + +static void dtsec_link_down(struct phylink_config *config, unsigned int mode, + phy_interface_t interface) +{ + struct fman_mac *dtsec = fman_config_to_mac(config)->fman_mac; + struct dtsec_regs __iomem *regs = dtsec->regs; + u32 tmp; + + /* Graceful stop - Assert the graceful Rx/Tx stop bit */ + graceful_stop(dtsec); + + tmp = ioread32be(®s->maccfg1); + tmp &= ~(MACCFG1_RX_EN | MACCFG1_TX_EN); + iowrite32be(tmp, ®s->maccfg1); +} + +static const struct phylink_mac_ops dtsec_mac_ops = { + .validate = phylink_generic_validate, + .mac_select_pcs = dtsec_select_pcs, + .mac_config = dtsec_mac_config, + .mac_link_up = dtsec_link_up, + .mac_link_down = dtsec_link_down, +}; + static int dtsec_modify_mac_address(struct fman_mac *dtsec, const enet_addr_t *enet_addr) { - if (!is_init_done(dtsec->dtsec_drv_param)) - return -EINVAL; - graceful_stop(dtsec); /* Initialize MAC Station Address registers (1 & 2) @@ -975,9 +1019,6 @@ static int dtsec_add_hash_mac_address(struct fman_mac *dtsec, u32 crc = 0xFFFFFFFF; bool mcast, ghtx; - if (!is_init_done(dtsec->dtsec_drv_param)) - return -EINVAL; - addr = ENET_ADDR_TO_UINT64(*eth_addr); ghtx = (bool)((ioread32be(®s->rctrl) & RCTRL_GHTX) ? true : false); @@ -1037,9 +1078,6 @@ static int dtsec_set_allmulti(struct fman_mac *dtsec, bool enable) u32 tmp; struct dtsec_regs __iomem *regs = dtsec->regs; - if (!is_init_done(dtsec->dtsec_drv_param)) - return -EINVAL; - tmp = ioread32be(®s->rctrl); if (enable) tmp |= RCTRL_MPROM; @@ -1056,9 +1094,6 @@ static int dtsec_set_tstamp(struct fman_mac *dtsec, bool enable) struct dtsec_regs __iomem *regs = dtsec->regs; u32 rctrl, tctrl; - if (!is_init_done(dtsec->dtsec_drv_param)) - return -EINVAL; - rctrl = ioread32be(®s->rctrl); tctrl = ioread32be(®s->tctrl); @@ -1087,9 +1122,6 @@ static int dtsec_del_hash_mac_address(struct fman_mac *dtsec, u32 crc = 0xFFFFFFFF; bool mcast, ghtx; - if (!is_init_done(dtsec->dtsec_drv_param)) - return -EINVAL; - addr = ENET_ADDR_TO_UINT64(*eth_addr); ghtx = (bool)((ioread32be(®s->rctrl) & RCTRL_GHTX) ? true : false); @@ -1153,9 +1185,6 @@ static int dtsec_set_promiscuous(struct fman_mac *dtsec, bool new_val) struct dtsec_regs __iomem *regs = dtsec->regs; u32 tmp; - if (!is_init_done(dtsec->dtsec_drv_param)) - return -EINVAL; - /* Set unicast promiscuous */ tmp = ioread32be(®s->rctrl); if (new_val) @@ -1177,90 +1206,12 @@ static int dtsec_set_promiscuous(struct fman_mac *dtsec, bool new_val) return 0; } -static int dtsec_adjust_link(struct fman_mac *dtsec, u16 speed) -{ - struct dtsec_regs __iomem *regs = dtsec->regs; - u32 tmp; - - if (!is_init_done(dtsec->dtsec_drv_param)) - return -EINVAL; - - graceful_stop(dtsec); - - tmp = ioread32be(®s->maccfg2); - - /* Full Duplex */ - tmp |= MACCFG2_FULL_DUPLEX; - - tmp &= ~(MACCFG2_NIBBLE_MODE | MACCFG2_BYTE_MODE); - if (speed < SPEED_1000) - tmp |= MACCFG2_NIBBLE_MODE; - else if (speed == SPEED_1000) - tmp |= MACCFG2_BYTE_MODE; - iowrite32be(tmp, ®s->maccfg2); - - tmp = ioread32be(®s->ecntrl); - if (speed == SPEED_100) - tmp |= DTSEC_ECNTRL_R100M; - else - tmp &= ~DTSEC_ECNTRL_R100M; - iowrite32be(tmp, ®s->ecntrl); - - graceful_start(dtsec); - - return 0; -} - -static int dtsec_restart_autoneg(struct fman_mac *dtsec) -{ - u16 tmp_reg16; - - if (!is_init_done(dtsec->dtsec_drv_param)) - return -EINVAL; - - tmp_reg16 = phy_read(dtsec->tbiphy, MII_BMCR); - - tmp_reg16 &= ~(BMCR_SPEED100 | BMCR_SPEED1000); - tmp_reg16 |= (BMCR_ANENABLE | BMCR_ANRESTART | - BMCR_FULLDPLX | BMCR_SPEED1000); - - phy_write(dtsec->tbiphy, MII_BMCR, tmp_reg16); - - return 0; -} - -static void adjust_link_dtsec(struct mac_device *mac_dev) -{ - struct phy_device *phy_dev = mac_dev->phy_dev; - struct fman_mac *fman_mac; - bool rx_pause, tx_pause; - int err; - - fman_mac = mac_dev->fman_mac; - if (!phy_dev->link) { - dtsec_restart_autoneg(fman_mac); - - return; - } - - dtsec_adjust_link(fman_mac, phy_dev->speed); - mac_dev->update_speed(mac_dev, phy_dev->speed); - fman_get_pause_cfg(mac_dev, &rx_pause, &tx_pause); - err = fman_set_mac_active_pause(mac_dev, rx_pause, tx_pause); - if (err < 0) - dev_err(mac_dev->dev, "fman_set_mac_active_pause() = %d\n", - err); -} - static int dtsec_set_exception(struct fman_mac *dtsec, enum fman_mac_exceptions exception, bool enable) { struct dtsec_regs __iomem *regs = dtsec->regs; u32 bit_mask = 0; - if (!is_init_done(dtsec->dtsec_drv_param)) - return -EINVAL; - if (exception != FM_MAC_EX_1G_1588_TS_RX_ERR) { bit_mask = get_exception_flag(exception); if (bit_mask) { @@ -1310,12 +1261,9 @@ static int dtsec_init(struct fman_mac *dtsec) { struct dtsec_regs __iomem *regs = dtsec->regs; struct dtsec_cfg *dtsec_drv_param; - u16 max_frm_ln; + u16 max_frm_ln, tbicon; int err; - if (is_init_done(dtsec->dtsec_drv_param)) - return -EINVAL; - if (DEFAULT_RESET_ON_INIT && (fman_reset_mac(dtsec->fm, dtsec->mac_id) != 0)) { pr_err("Can't reset MAC!\n"); @@ -1330,38 +1278,19 @@ static int dtsec_init(struct fman_mac *dtsec) err = init(dtsec->regs, dtsec_drv_param, dtsec->phy_if, dtsec->max_speed, dtsec->addr, dtsec->exceptions, - dtsec->tbiphy->mdio.addr); + dtsec->tbidev->addr); if (err) { free_init_resources(dtsec); pr_err("DTSEC version doesn't support this i/f mode\n"); return err; } - if (dtsec->phy_if == PHY_INTERFACE_MODE_SGMII) { - u16 tmp_reg16; + /* Configure the TBI PHY Control Register */ + tbicon = TBICON_CLK_SELECT | TBICON_SOFT_RESET; + mdiodev_write(dtsec->tbidev, MII_TBICON, tbicon); - /* Configure the TBI PHY Control Register */ - tmp_reg16 = TBICON_CLK_SELECT | TBICON_SOFT_RESET; - phy_write(dtsec->tbiphy, MII_TBICON, tmp_reg16); - - tmp_reg16 = TBICON_CLK_SELECT; - phy_write(dtsec->tbiphy, MII_TBICON, tmp_reg16); - - tmp_reg16 = (BMCR_RESET | BMCR_ANENABLE | - BMCR_FULLDPLX | BMCR_SPEED1000); - phy_write(dtsec->tbiphy, MII_BMCR, tmp_reg16); - - if (dtsec->basex_if) - tmp_reg16 = TBIANA_1000X; - else - tmp_reg16 = TBIANA_SGMII; - phy_write(dtsec->tbiphy, MII_ADVERTISE, tmp_reg16); - - tmp_reg16 = (BMCR_ANENABLE | BMCR_ANRESTART | - BMCR_FULLDPLX | BMCR_SPEED1000); - - phy_write(dtsec->tbiphy, MII_BMCR, tmp_reg16); - } + tbicon = TBICON_CLK_SELECT; + mdiodev_write(dtsec->tbidev, MII_TBICON, tbicon); /* Max Frame Length */ max_frm_ln = (u16)ioread32be(®s->maxfrm); @@ -1406,6 +1335,8 @@ static int dtsec_free(struct fman_mac *dtsec) kfree(dtsec->dtsec_drv_param); dtsec->dtsec_drv_param = NULL; + if (!IS_ERR_OR_NULL(dtsec->tbidev)) + put_device(&dtsec->tbidev->dev); kfree(dtsec); return 0; @@ -1434,7 +1365,6 @@ static struct fman_mac *dtsec_config(struct mac_device *mac_dev, dtsec->regs = mac_dev->vaddr; dtsec->addr = ENET_ADDR_TO_UINT64(mac_dev->addr); - dtsec->max_speed = params->max_speed; dtsec->phy_if = mac_dev->phy_if; dtsec->mac_id = params->mac_id; dtsec->exceptions = (DTSEC_IMASK_BREN | @@ -1457,7 +1387,6 @@ static struct fman_mac *dtsec_config(struct mac_device *mac_dev, dtsec->en_tsu_err_exception = dtsec->dtsec_drv_param->ptp_exception_en; dtsec->fm = params->fm; - dtsec->basex_if = params->basex_if; /* Save FMan revision */ fman_get_revision(dtsec->fm, &dtsec->fm_rev_info); @@ -1476,18 +1405,18 @@ int dtsec_initialization(struct mac_device *mac_dev, int err; struct fman_mac *dtsec; struct device_node *phy_node; + unsigned long capabilities; + unsigned long *supported; + mac_dev->phylink_ops = &dtsec_mac_ops; mac_dev->set_promisc = dtsec_set_promiscuous; mac_dev->change_addr = dtsec_modify_mac_address; mac_dev->add_hash_mac_addr = dtsec_add_hash_mac_address; mac_dev->remove_hash_mac_addr = dtsec_del_hash_mac_address; - mac_dev->set_tx_pause = dtsec_set_tx_pause_frames; - mac_dev->set_rx_pause = dtsec_accept_rx_pause_frames; mac_dev->set_exception = dtsec_set_exception; mac_dev->set_allmulti = dtsec_set_allmulti; mac_dev->set_tstamp = dtsec_set_tstamp; mac_dev->set_multi = fman_set_multi; - mac_dev->adjust_link = adjust_link_dtsec; mac_dev->enable = dtsec_enable; mac_dev->disable = dtsec_disable; @@ -1502,19 +1431,56 @@ int dtsec_initialization(struct mac_device *mac_dev, dtsec->dtsec_drv_param->tx_pad_crc = true; phy_node = of_parse_phandle(mac_node, "tbi-handle", 0); - if (!phy_node) { - pr_err("TBI PHY node is not available\n"); + if (!phy_node || of_device_is_available(phy_node)) { + of_node_put(phy_node); err = -EINVAL; + dev_err_probe(mac_dev->dev, err, + "TBI PCS node is not available\n"); goto _return_fm_mac_free; } - dtsec->tbiphy = of_phy_find_device(phy_node); - if (!dtsec->tbiphy) { - pr_err("of_phy_find_device (TBI PHY) failed\n"); - err = -EINVAL; + dtsec->tbidev = of_mdio_find_device(phy_node); + of_node_put(phy_node); + if (!dtsec->tbidev) { + err = -EPROBE_DEFER; + dev_err_probe(mac_dev->dev, err, + "could not find mdiodev for PCS\n"); goto _return_fm_mac_free; } - put_device(&dtsec->tbiphy->mdio.dev); + dtsec->pcs.ops = &dtsec_pcs_ops; + dtsec->pcs.poll = true; + + supported = mac_dev->phylink_config.supported_interfaces; + + /* FIXME: Can we use DTSEC_ID2_INT_FULL_OFF to determine if these are + * supported? If not, we can determine support via the phy if SerDes + * support is added. + */ + if (mac_dev->phy_if == PHY_INTERFACE_MODE_SGMII || + mac_dev->phy_if == PHY_INTERFACE_MODE_1000BASEX) { + __set_bit(PHY_INTERFACE_MODE_SGMII, supported); + __set_bit(PHY_INTERFACE_MODE_1000BASEX, supported); + } else if (mac_dev->phy_if == PHY_INTERFACE_MODE_2500BASEX) { + __set_bit(PHY_INTERFACE_MODE_2500BASEX, supported); + } + + if (!(ioread32be(&dtsec->regs->tsec_id2) & DTSEC_ID2_INT_REDUCED_OFF)) { + phy_interface_set_rgmii(supported); + + /* DTSEC_ID2_INT_REDUCED_OFF indicates that the dTSEC supports + * RMII and RGMII. However, the only SoCs which support RMII + * are the P1017 and P1023. Avoid advertising this mode on + * other SoCs. This is a bit of a moot point, since there's no + * in-tree support for ethernet on these platforms... + */ + if (of_machine_is_compatible("fsl,P1023") || + of_machine_is_compatible("fsl,P1023RDB")) + __set_bit(PHY_INTERFACE_MODE_RMII, supported); + } + + capabilities = MAC_SYM_PAUSE | MAC_ASYM_PAUSE; + capabilities |= MAC_10 | MAC_100 | MAC_1000FD | MAC_2500FD; + mac_dev->phylink_config.mac_capabilities = capabilities; err = dtsec_init(dtsec); if (err < 0) diff --git a/drivers/net/ethernet/freescale/fman/fman_mac.h b/drivers/net/ethernet/freescale/fman/fman_mac.h index 65887a3160d7..e5d6cddea731 100644 --- a/drivers/net/ethernet/freescale/fman/fman_mac.h +++ b/drivers/net/ethernet/freescale/fman/fman_mac.h @@ -170,20 +170,10 @@ struct fman_mac_params { * 0 - FM_MAX_NUM_OF_10G_MACS */ u8 mac_id; - /* Note that the speed should indicate the maximum rate that - * this MAC should support rather than the actual speed; - */ - u16 max_speed; /* A handle to the FM object this port related to */ void *fm; fman_mac_exception_cb *event_cb; /* MDIO Events Callback Routine */ fman_mac_exception_cb *exception_cb;/* Exception Callback Routine */ - /* SGMII/QSGII interface with 1000BaseX auto-negotiation between MAC - * and phy or backplane; Note: 1000BaseX auto-negotiation relates only - * to interface between MAC and phy/backplane, SGMII phy can still - * synchronize with far-end phy at 10Mbps, 100Mbps or 1000Mbps - */ - bool basex_if; }; struct eth_hash_t { diff --git a/drivers/net/ethernet/freescale/fman/fman_memac.c b/drivers/net/ethernet/freescale/fman/fman_memac.c index 43584946cae2..9349f841bd06 100644 --- a/drivers/net/ethernet/freescale/fman/fman_memac.c +++ b/drivers/net/ethernet/freescale/fman/fman_memac.c @@ -278,9 +278,6 @@ struct fman_mac { struct memac_regs __iomem *regs; /* MAC address of device */ u64 addr; - /* Ethernet physical interface */ - phy_interface_t phy_if; - u16 max_speed; struct mac_device *dev_id; /* device cookie used by the exception cbs */ fman_mac_exception_cb *exception_cb; fman_mac_exception_cb *event_cb; @@ -293,12 +290,12 @@ struct fman_mac { struct memac_cfg *memac_drv_param; void *fm; struct fman_rev_info fm_rev_info; - bool basex_if; struct phy *serdes; struct phylink_pcs *sgmii_pcs; struct phylink_pcs *qsgmii_pcs; struct phylink_pcs *xfi_pcs; bool allmulti_enabled; + bool rgmii_no_half_duplex; }; static void add_addr_in_paddr(struct memac_regs __iomem *regs, const u8 *adr, @@ -356,7 +353,6 @@ static void set_exception(struct memac_regs __iomem *regs, u32 val, } static int init(struct memac_regs __iomem *regs, struct memac_cfg *cfg, - phy_interface_t phy_if, u16 speed, bool slow_10g_if, u32 exceptions) { u32 tmp; @@ -384,41 +380,6 @@ static int init(struct memac_regs __iomem *regs, struct memac_cfg *cfg, iowrite32be((u32)cfg->pause_quanta, ®s->pause_quanta[0]); iowrite32be((u32)0, ®s->pause_thresh[0]); - /* IF_MODE */ - tmp = 0; - switch (phy_if) { - case PHY_INTERFACE_MODE_XGMII: - tmp |= IF_MODE_10G; - break; - case PHY_INTERFACE_MODE_MII: - tmp |= IF_MODE_MII; - break; - default: - tmp |= IF_MODE_GMII; - if (phy_if == PHY_INTERFACE_MODE_RGMII || - phy_if == PHY_INTERFACE_MODE_RGMII_ID || - phy_if == PHY_INTERFACE_MODE_RGMII_RXID || - phy_if == PHY_INTERFACE_MODE_RGMII_TXID) - tmp |= IF_MODE_RGMII | IF_MODE_RGMII_AUTO; - } - iowrite32be(tmp, ®s->if_mode); - - /* TX_FIFO_SECTIONS */ - tmp = 0; - if (phy_if == PHY_INTERFACE_MODE_XGMII) { - if (slow_10g_if) { - tmp |= (TX_FIFO_SECTIONS_TX_AVAIL_SLOW_10G | - TX_FIFO_SECTIONS_TX_EMPTY_DEFAULT_10G); - } else { - tmp |= (TX_FIFO_SECTIONS_TX_AVAIL_10G | - TX_FIFO_SECTIONS_TX_EMPTY_DEFAULT_10G); - } - } else { - tmp |= (TX_FIFO_SECTIONS_TX_AVAIL_1G | - TX_FIFO_SECTIONS_TX_EMPTY_DEFAULT_1G); - } - iowrite32be(tmp, ®s->tx_fifo_sections); - /* clear all pending events and set-up interrupts */ iowrite32be(0xffffffff, ®s->ievent); set_exception(regs, exceptions, true); @@ -458,24 +419,6 @@ static u32 get_mac_addr_hash_code(u64 eth_addr) return xor_val; } -static void setup_sgmii_internal(struct fman_mac *memac, - struct phylink_pcs *pcs, - struct fixed_phy_status *fixed_link) -{ - __ETHTOOL_DECLARE_LINK_MODE_MASK(advertising); - phy_interface_t iface = memac->basex_if ? PHY_INTERFACE_MODE_1000BASEX : - PHY_INTERFACE_MODE_SGMII; - unsigned int mode = fixed_link ? MLO_AN_FIXED : MLO_AN_INBAND; - - linkmode_set_pause(advertising, true, true); - pcs->ops->pcs_config(pcs, mode, iface, advertising, true); - if (fixed_link) - pcs->ops->pcs_link_up(pcs, mode, iface, fixed_link->speed, - fixed_link->duplex); - else - pcs->ops->pcs_an_restart(pcs); -} - static int check_init_parameters(struct fman_mac *memac) { if (!memac->exception_cb) { @@ -581,41 +524,31 @@ static void free_init_resources(struct fman_mac *memac) memac->unicast_addr_hash = NULL; } -static bool is_init_done(struct memac_cfg *memac_drv_params) -{ - /* Checks if mEMAC driver parameters were initialized */ - if (!memac_drv_params) - return true; - - return false; -} - static int memac_enable(struct fman_mac *memac) { - struct memac_regs __iomem *regs = memac->regs; - u32 tmp; + int ret; - if (!is_init_done(memac->memac_drv_param)) - return -EINVAL; + ret = phy_init(memac->serdes); + if (ret) { + dev_err(memac->dev_id->dev, + "could not initialize serdes: %pe\n", ERR_PTR(ret)); + return ret; + } - tmp = ioread32be(®s->command_config); - tmp |= CMD_CFG_RX_EN | CMD_CFG_TX_EN; - iowrite32be(tmp, ®s->command_config); + ret = phy_power_on(memac->serdes); + if (ret) { + dev_err(memac->dev_id->dev, + "could not power on serdes: %pe\n", ERR_PTR(ret)); + phy_exit(memac->serdes); + } - return 0; + return ret; } static void memac_disable(struct fman_mac *memac) - { - struct memac_regs __iomem *regs = memac->regs; - u32 tmp; - - WARN_ON_ONCE(!is_init_done(memac->memac_drv_param)); - - tmp = ioread32be(®s->command_config); - tmp &= ~(CMD_CFG_RX_EN | CMD_CFG_TX_EN); - iowrite32be(tmp, ®s->command_config); + phy_power_off(memac->serdes); + phy_exit(memac->serdes); } static int memac_set_promiscuous(struct fman_mac *memac, bool new_val) @@ -623,9 +556,6 @@ static int memac_set_promiscuous(struct fman_mac *memac, bool new_val) struct memac_regs __iomem *regs = memac->regs; u32 tmp; - if (!is_init_done(memac->memac_drv_param)) - return -EINVAL; - tmp = ioread32be(®s->command_config); if (new_val) tmp |= CMD_CFG_PROMIS_EN; @@ -637,73 +567,12 @@ static int memac_set_promiscuous(struct fman_mac *memac, bool new_val) return 0; } -static int memac_adjust_link(struct fman_mac *memac, u16 speed) -{ - struct memac_regs __iomem *regs = memac->regs; - u32 tmp; - - if (!is_init_done(memac->memac_drv_param)) - return -EINVAL; - - tmp = ioread32be(®s->if_mode); - - /* Set full duplex */ - tmp &= ~IF_MODE_HD; - - if (phy_interface_mode_is_rgmii(memac->phy_if)) { - /* Configure RGMII in manual mode */ - tmp &= ~IF_MODE_RGMII_AUTO; - tmp &= ~IF_MODE_RGMII_SP_MASK; - /* Full duplex */ - tmp |= IF_MODE_RGMII_FD; - - switch (speed) { - case SPEED_1000: - tmp |= IF_MODE_RGMII_1000; - break; - case SPEED_100: - tmp |= IF_MODE_RGMII_100; - break; - case SPEED_10: - tmp |= IF_MODE_RGMII_10; - break; - default: - break; - } - } - - iowrite32be(tmp, ®s->if_mode); - - return 0; -} - -static void adjust_link_memac(struct mac_device *mac_dev) -{ - struct phy_device *phy_dev = mac_dev->phy_dev; - struct fman_mac *fman_mac; - bool rx_pause, tx_pause; - int err; - - fman_mac = mac_dev->fman_mac; - memac_adjust_link(fman_mac, phy_dev->speed); - mac_dev->update_speed(mac_dev, phy_dev->speed); - - fman_get_pause_cfg(mac_dev, &rx_pause, &tx_pause); - err = fman_set_mac_active_pause(mac_dev, rx_pause, tx_pause); - if (err < 0) - dev_err(mac_dev->dev, "fman_set_mac_active_pause() = %d\n", - err); -} - static int memac_set_tx_pause_frames(struct fman_mac *memac, u8 priority, u16 pause_time, u16 thresh_time) { struct memac_regs __iomem *regs = memac->regs; u32 tmp; - if (!is_init_done(memac->memac_drv_param)) - return -EINVAL; - tmp = ioread32be(®s->tx_fifo_sections); GET_TX_EMPTY_DEFAULT_VALUE(tmp); @@ -738,9 +607,6 @@ static int memac_accept_rx_pause_frames(struct fman_mac *memac, bool en) struct memac_regs __iomem *regs = memac->regs; u32 tmp; - if (!is_init_done(memac->memac_drv_param)) - return -EINVAL; - tmp = ioread32be(®s->command_config); if (en) tmp &= ~CMD_CFG_PAUSE_IGNORE; @@ -752,12 +618,175 @@ static int memac_accept_rx_pause_frames(struct fman_mac *memac, bool en) return 0; } +static void memac_validate(struct phylink_config *config, + unsigned long *supported, + struct phylink_link_state *state) +{ + struct fman_mac *memac = fman_config_to_mac(config)->fman_mac; + unsigned long caps = config->mac_capabilities; + + if (phy_interface_mode_is_rgmii(state->interface) && + memac->rgmii_no_half_duplex) + caps &= ~(MAC_10HD | MAC_100HD); + + phylink_validate_mask_caps(supported, state, caps); +} + +/** + * memac_if_mode() - Convert an interface mode into an IF_MODE config + * @interface: A phy interface mode + * + * Return: A configuration word, suitable for programming into the lower bits + * of %IF_MODE. + */ +static u32 memac_if_mode(phy_interface_t interface) +{ + switch (interface) { + case PHY_INTERFACE_MODE_MII: + return IF_MODE_MII; + case PHY_INTERFACE_MODE_RGMII: + case PHY_INTERFACE_MODE_RGMII_ID: + case PHY_INTERFACE_MODE_RGMII_RXID: + case PHY_INTERFACE_MODE_RGMII_TXID: + return IF_MODE_GMII | IF_MODE_RGMII; + case PHY_INTERFACE_MODE_SGMII: + case PHY_INTERFACE_MODE_1000BASEX: + case PHY_INTERFACE_MODE_QSGMII: + return IF_MODE_GMII; + case PHY_INTERFACE_MODE_10GBASER: + return IF_MODE_10G; + default: + WARN_ON_ONCE(1); + return 0; + } +} + +static struct phylink_pcs *memac_select_pcs(struct phylink_config *config, + phy_interface_t iface) +{ + struct fman_mac *memac = fman_config_to_mac(config)->fman_mac; + + switch (iface) { + case PHY_INTERFACE_MODE_SGMII: + case PHY_INTERFACE_MODE_1000BASEX: + return memac->sgmii_pcs; + case PHY_INTERFACE_MODE_QSGMII: + return memac->qsgmii_pcs; + case PHY_INTERFACE_MODE_10GBASER: + return memac->xfi_pcs; + default: + return NULL; + } +} + +static int memac_prepare(struct phylink_config *config, unsigned int mode, + phy_interface_t iface) +{ + struct fman_mac *memac = fman_config_to_mac(config)->fman_mac; + + switch (iface) { + case PHY_INTERFACE_MODE_SGMII: + case PHY_INTERFACE_MODE_1000BASEX: + case PHY_INTERFACE_MODE_QSGMII: + case PHY_INTERFACE_MODE_10GBASER: + return phy_set_mode_ext(memac->serdes, PHY_MODE_ETHERNET, + iface); + default: + return 0; + } +} + +static void memac_mac_config(struct phylink_config *config, unsigned int mode, + const struct phylink_link_state *state) +{ + struct mac_device *mac_dev = fman_config_to_mac(config); + struct memac_regs __iomem *regs = mac_dev->fman_mac->regs; + u32 tmp = ioread32be(®s->if_mode); + + tmp &= ~(IF_MODE_MASK | IF_MODE_RGMII); + tmp |= memac_if_mode(state->interface); + if (phylink_autoneg_inband(mode)) + tmp |= IF_MODE_RGMII_AUTO; + iowrite32be(tmp, ®s->if_mode); +} + +static void memac_link_up(struct phylink_config *config, struct phy_device *phy, + unsigned int mode, phy_interface_t interface, + int speed, int duplex, bool tx_pause, bool rx_pause) +{ + struct mac_device *mac_dev = fman_config_to_mac(config); + struct fman_mac *memac = mac_dev->fman_mac; + struct memac_regs __iomem *regs = memac->regs; + u32 tmp = memac_if_mode(interface); + u16 pause_time = tx_pause ? FSL_FM_PAUSE_TIME_ENABLE : + FSL_FM_PAUSE_TIME_DISABLE; + + memac_set_tx_pause_frames(memac, 0, pause_time, 0); + memac_accept_rx_pause_frames(memac, rx_pause); + + if (duplex == DUPLEX_HALF) + tmp |= IF_MODE_HD; + + switch (speed) { + case SPEED_1000: + tmp |= IF_MODE_RGMII_1000; + break; + case SPEED_100: + tmp |= IF_MODE_RGMII_100; + break; + case SPEED_10: + tmp |= IF_MODE_RGMII_10; + break; + } + iowrite32be(tmp, ®s->if_mode); + + /* TODO: EEE? */ + + if (speed == SPEED_10000) { + if (memac->fm_rev_info.major == 6 && + memac->fm_rev_info.minor == 4) + tmp = TX_FIFO_SECTIONS_TX_AVAIL_SLOW_10G; + else + tmp = TX_FIFO_SECTIONS_TX_AVAIL_10G; + tmp |= TX_FIFO_SECTIONS_TX_EMPTY_DEFAULT_10G; + } else { + tmp = TX_FIFO_SECTIONS_TX_AVAIL_1G | + TX_FIFO_SECTIONS_TX_EMPTY_DEFAULT_1G; + } + iowrite32be(tmp, ®s->tx_fifo_sections); + + mac_dev->update_speed(mac_dev, speed); + + tmp = ioread32be(®s->command_config); + tmp |= CMD_CFG_RX_EN | CMD_CFG_TX_EN; + iowrite32be(tmp, ®s->command_config); +} + +static void memac_link_down(struct phylink_config *config, unsigned int mode, + phy_interface_t interface) +{ + struct fman_mac *memac = fman_config_to_mac(config)->fman_mac; + struct memac_regs __iomem *regs = memac->regs; + u32 tmp; + + /* TODO: graceful */ + tmp = ioread32be(®s->command_config); + tmp &= ~(CMD_CFG_RX_EN | CMD_CFG_TX_EN); + iowrite32be(tmp, ®s->command_config); +} + +static const struct phylink_mac_ops memac_mac_ops = { + .validate = memac_validate, + .mac_select_pcs = memac_select_pcs, + .mac_prepare = memac_prepare, + .mac_config = memac_mac_config, + .mac_link_up = memac_link_up, + .mac_link_down = memac_link_down, +}; + static int memac_modify_mac_address(struct fman_mac *memac, const enet_addr_t *enet_addr) { - if (!is_init_done(memac->memac_drv_param)) - return -EINVAL; - add_addr_in_paddr(memac->regs, (const u8 *)(*enet_addr), 0); return 0; @@ -771,9 +800,6 @@ static int memac_add_hash_mac_address(struct fman_mac *memac, u32 hash; u64 addr; - if (!is_init_done(memac->memac_drv_param)) - return -EINVAL; - addr = ENET_ADDR_TO_UINT64(*eth_addr); if (!(addr & GROUP_ADDRESS)) { @@ -802,9 +828,6 @@ static int memac_set_allmulti(struct fman_mac *memac, bool enable) u32 entry; struct memac_regs __iomem *regs = memac->regs; - if (!is_init_done(memac->memac_drv_param)) - return -EINVAL; - if (enable) { for (entry = 0; entry < HASH_TABLE_SIZE; entry++) iowrite32be(entry | HASH_CTRL_MCAST_EN, @@ -834,9 +857,6 @@ static int memac_del_hash_mac_address(struct fman_mac *memac, u32 hash; u64 addr; - if (!is_init_done(memac->memac_drv_param)) - return -EINVAL; - addr = ENET_ADDR_TO_UINT64(*eth_addr); hash = get_mac_addr_hash_code(addr) & HASH_CTRL_ADDR_MASK; @@ -864,9 +884,6 @@ static int memac_set_exception(struct fman_mac *memac, { u32 bit_mask = 0; - if (!is_init_done(memac->memac_drv_param)) - return -EINVAL; - bit_mask = get_exception_flag(exception); if (bit_mask) { if (enable) @@ -886,23 +903,15 @@ static int memac_init(struct fman_mac *memac) { struct memac_cfg *memac_drv_param; enet_addr_t eth_addr; - bool slow_10g_if = false; - struct fixed_phy_status *fixed_link = NULL; int err; u32 reg32 = 0; - if (is_init_done(memac->memac_drv_param)) - return -EINVAL; - err = check_init_parameters(memac); if (err) return err; memac_drv_param = memac->memac_drv_param; - if (memac->fm_rev_info.major == 6 && memac->fm_rev_info.minor == 4) - slow_10g_if = true; - /* First, reset the MAC if desired. */ if (memac_drv_param->reset_on_init) { err = reset(memac->regs); @@ -918,10 +927,7 @@ static int memac_init(struct fman_mac *memac) add_addr_in_paddr(memac->regs, (const u8 *)eth_addr, 0); } - fixed_link = memac_drv_param->fixed_link; - - init(memac->regs, memac->memac_drv_param, memac->phy_if, - memac->max_speed, slow_10g_if, memac->exceptions); + init(memac->regs, memac->memac_drv_param, memac->exceptions); /* FM_RX_FIFO_CORRUPT_ERRATA_10GMAC_A006320 errata workaround * Exists only in FMan 6.0 and 6.3. @@ -937,11 +943,6 @@ static int memac_init(struct fman_mac *memac) iowrite32be(reg32, &memac->regs->command_config); } - if (memac->phy_if == PHY_INTERFACE_MODE_SGMII) - setup_sgmii_internal(memac, memac->sgmii_pcs, fixed_link); - else if (memac->phy_if == PHY_INTERFACE_MODE_QSGMII) - setup_sgmii_internal(memac, memac->qsgmii_pcs, fixed_link); - /* Max Frame Length */ err = fman_set_mac_max_frame(memac->fm, memac->mac_id, memac_drv_param->max_frame_length); @@ -970,9 +971,6 @@ static int memac_init(struct fman_mac *memac) fman_register_intr(memac->fm, FMAN_MOD_MAC, memac->mac_id, FMAN_INTR_TYPE_NORMAL, memac_exception, memac); - kfree(memac_drv_param); - memac->memac_drv_param = NULL; - return 0; } @@ -995,7 +993,6 @@ static int memac_free(struct fman_mac *memac) pcs_put(memac->sgmii_pcs); pcs_put(memac->qsgmii_pcs); pcs_put(memac->xfi_pcs); - kfree(memac->memac_drv_param); kfree(memac); @@ -1028,8 +1025,6 @@ static struct fman_mac *memac_config(struct mac_device *mac_dev, memac->addr = ENET_ADDR_TO_UINT64(mac_dev->addr); memac->regs = mac_dev->vaddr; - memac->max_speed = params->max_speed; - memac->phy_if = mac_dev->phy_if; memac->mac_id = params->mac_id; memac->exceptions = (MEMAC_IMASK_TSECC_ER | MEMAC_IMASK_TECC_ER | MEMAC_IMASK_RECC_ER | MEMAC_IMASK_MGI); @@ -1037,7 +1032,6 @@ static struct fman_mac *memac_config(struct mac_device *mac_dev, memac->event_cb = params->event_cb; memac->dev_id = mac_dev; memac->fm = params->fm; - memac->basex_if = params->basex_if; /* Save FMan revision */ fman_get_revision(memac->fm, &memac->fm_rev_info); @@ -1064,37 +1058,44 @@ static struct phylink_pcs *memac_pcs_create(struct device_node *mac_node, return pcs; } +static bool memac_supports(struct mac_device *mac_dev, phy_interface_t iface) +{ + /* If there's no serdes device, assume that it's been configured for + * whatever the default interface mode is. + */ + if (!mac_dev->fman_mac->serdes) + return mac_dev->phy_if == iface; + /* Otherwise, ask the serdes */ + return !phy_validate(mac_dev->fman_mac->serdes, PHY_MODE_ETHERNET, + iface, NULL); +} + int memac_initialization(struct mac_device *mac_dev, struct device_node *mac_node, struct fman_mac_params *params) { int err; + struct device_node *fixed; struct phylink_pcs *pcs; - struct fixed_phy_status *fixed_link; struct fman_mac *memac; + unsigned long capabilities; + unsigned long *supported; + mac_dev->phylink_ops = &memac_mac_ops; mac_dev->set_promisc = memac_set_promiscuous; mac_dev->change_addr = memac_modify_mac_address; mac_dev->add_hash_mac_addr = memac_add_hash_mac_address; mac_dev->remove_hash_mac_addr = memac_del_hash_mac_address; - mac_dev->set_tx_pause = memac_set_tx_pause_frames; - mac_dev->set_rx_pause = memac_accept_rx_pause_frames; mac_dev->set_exception = memac_set_exception; mac_dev->set_allmulti = memac_set_allmulti; mac_dev->set_tstamp = memac_set_tstamp; mac_dev->set_multi = fman_set_multi; - mac_dev->adjust_link = adjust_link_memac; mac_dev->enable = memac_enable; mac_dev->disable = memac_disable; - if (params->max_speed == SPEED_10000) - mac_dev->phy_if = PHY_INTERFACE_MODE_XGMII; - mac_dev->fman_mac = memac_config(mac_dev, params); - if (!mac_dev->fman_mac) { - err = -EINVAL; - goto _return; - } + if (!mac_dev->fman_mac) + return -EINVAL; memac = mac_dev->fman_mac; memac->memac_drv_param->max_frame_length = fman_get_max_frm(); @@ -1136,9 +1137,9 @@ int memac_initialization(struct mac_device *mac_dev, else pcs = memac_pcs_create(mac_node, err); - if (!pcs) { - dev_err(mac_dev->dev, "missing pcs\n"); - err = -ENOENT; + if (IS_ERR(pcs)) { + err = PTR_ERR(pcs); + dev_err_probe(mac_dev->dev, err, "missing pcs\n"); goto _return_fm_mac_free; } @@ -1159,84 +1160,100 @@ int memac_initialization(struct mac_device *mac_dev, } else if (IS_ERR(memac->serdes)) { dev_err_probe(mac_dev->dev, err, "could not get serdes\n"); goto _return_fm_mac_free; + } + + /* The internal connection to the serdes is XGMII, but this isn't + * really correct for the phy mode (which is the external connection). + * However, this is how all older device trees say that they want + * 10GBASE-R (aka XFI), so just convert it for them. + */ + if (mac_dev->phy_if == PHY_INTERFACE_MODE_XGMII) + mac_dev->phy_if = PHY_INTERFACE_MODE_10GBASER; + + /* TODO: The following interface modes are supported by (some) hardware + * but not by this driver: + * - 1000BASE-KX + * - 10GBASE-KR + * - XAUI/HiGig + */ + supported = mac_dev->phylink_config.supported_interfaces; + + /* Note that half duplex is only supported on 10/100M interfaces. */ + + if (memac->sgmii_pcs && + (memac_supports(mac_dev, PHY_INTERFACE_MODE_SGMII) || + memac_supports(mac_dev, PHY_INTERFACE_MODE_1000BASEX))) { + __set_bit(PHY_INTERFACE_MODE_SGMII, supported); + __set_bit(PHY_INTERFACE_MODE_1000BASEX, supported); + } + + if (memac->sgmii_pcs && + memac_supports(mac_dev, PHY_INTERFACE_MODE_2500BASEX)) + __set_bit(PHY_INTERFACE_MODE_2500BASEX, supported); + + if (memac->qsgmii_pcs && + memac_supports(mac_dev, PHY_INTERFACE_MODE_QSGMII)) + __set_bit(PHY_INTERFACE_MODE_QSGMII, supported); + else if (mac_dev->phy_if == PHY_INTERFACE_MODE_QSGMII) + dev_warn(mac_dev->dev, "no QSGMII pcs specified\n"); + + if (memac->xfi_pcs && + memac_supports(mac_dev, PHY_INTERFACE_MODE_10GBASER)) { + __set_bit(PHY_INTERFACE_MODE_10GBASER, supported); } else { - err = phy_init(memac->serdes); - if (err) { - dev_err_probe(mac_dev->dev, err, - "could not initialize serdes\n"); - goto _return_fm_mac_free; - } - - err = phy_power_on(memac->serdes); - if (err) { - dev_err_probe(mac_dev->dev, err, - "could not power on serdes\n"); - goto _return_phy_exit; - } - - if (memac->phy_if == PHY_INTERFACE_MODE_SGMII || - memac->phy_if == PHY_INTERFACE_MODE_1000BASEX || - memac->phy_if == PHY_INTERFACE_MODE_2500BASEX || - memac->phy_if == PHY_INTERFACE_MODE_QSGMII || - memac->phy_if == PHY_INTERFACE_MODE_XGMII) { - err = phy_set_mode_ext(memac->serdes, PHY_MODE_ETHERNET, - memac->phy_if); - if (err) { - dev_err_probe(mac_dev->dev, err, - "could not set serdes mode to %s\n", - phy_modes(memac->phy_if)); - goto _return_phy_power_off; - } - } + /* From what I can tell, no 10g macs support RGMII. */ + phy_interface_set_rgmii(supported); + __set_bit(PHY_INTERFACE_MODE_MII, supported); } - if (!mac_dev->phy_node && of_phy_is_fixed_link(mac_node)) { - struct phy_device *phy; + capabilities = MAC_SYM_PAUSE | MAC_ASYM_PAUSE | MAC_10 | MAC_100; + capabilities |= MAC_1000FD | MAC_2500FD | MAC_10000FD; - err = of_phy_register_fixed_link(mac_node); - if (err) - goto _return_phy_power_off; + /* These SoCs don't support half duplex at all; there's no different + * FMan version or compatible, so we just have to check the machine + * compatible instead + */ + if (of_machine_is_compatible("fsl,ls1043a") || + of_machine_is_compatible("fsl,ls1046a") || + of_machine_is_compatible("fsl,B4QDS")) + capabilities &= ~(MAC_10HD | MAC_100HD); - fixed_link = kzalloc(sizeof(*fixed_link), GFP_KERNEL); - if (!fixed_link) { - err = -ENOMEM; - goto _return_phy_power_off; - } + mac_dev->phylink_config.mac_capabilities = capabilities; - mac_dev->phy_node = of_node_get(mac_node); - phy = of_phy_find_device(mac_dev->phy_node); - if (!phy) { - err = -EINVAL; - of_node_put(mac_dev->phy_node); - goto _return_fixed_link_free; - } + /* The T2080 and T4240 don't support half duplex RGMII. There is no + * other way to identify these SoCs, so just use the machine + * compatible. + */ + if (of_machine_is_compatible("fsl,T2080QDS") || + of_machine_is_compatible("fsl,T2080RDB") || + of_machine_is_compatible("fsl,T2081QDS") || + of_machine_is_compatible("fsl,T4240QDS") || + of_machine_is_compatible("fsl,T4240RDB")) + memac->rgmii_no_half_duplex = true; - fixed_link->link = phy->link; - fixed_link->speed = phy->speed; - fixed_link->duplex = phy->duplex; - fixed_link->pause = phy->pause; - fixed_link->asym_pause = phy->asym_pause; - - put_device(&phy->mdio.dev); - memac->memac_drv_param->fixed_link = fixed_link; - } + /* Most boards should use MLO_AN_INBAND, but existing boards don't have + * a managed property. Default to MLO_AN_INBAND if nothing else is + * specified. We need to be careful and not enable this if we have a + * fixed link or if we are using MII or RGMII, since those + * configurations modes don't use in-band autonegotiation. + */ + fixed = of_get_child_by_name(mac_node, "fixed-link"); + if (!fixed && !of_property_read_bool(mac_node, "fixed-link") && + !of_property_read_bool(mac_node, "managed") && + mac_dev->phy_if != PHY_INTERFACE_MODE_MII && + !phy_interface_mode_is_rgmii(mac_dev->phy_if)) + mac_dev->phylink_config.ovr_an_inband = true; + of_node_put(fixed); err = memac_init(mac_dev->fman_mac); if (err < 0) - goto _return_fixed_link_free; + goto _return_fm_mac_free; dev_info(mac_dev->dev, "FMan MEMAC\n"); - goto _return; + return 0; -_return_phy_power_off: - phy_power_off(memac->serdes); -_return_phy_exit: - phy_exit(memac->serdes); -_return_fixed_link_free: - kfree(fixed_link); _return_fm_mac_free: memac_free(mac_dev->fman_mac); -_return: return err; } diff --git a/drivers/net/ethernet/freescale/fman/fman_tgec.c b/drivers/net/ethernet/freescale/fman/fman_tgec.c index 5a4be54ad459..c265b7f19a4d 100644 --- a/drivers/net/ethernet/freescale/fman/fman_tgec.c +++ b/drivers/net/ethernet/freescale/fman/fman_tgec.c @@ -13,6 +13,7 @@ #include #include #include +#include /* Transmit Inter-Packet Gap Length Register (TX_IPG_LENGTH) */ #define TGEC_TX_IPG_LENGTH_MASK 0x000003ff @@ -243,10 +244,6 @@ static int init(struct tgec_regs __iomem *regs, struct tgec_cfg *cfg, static int check_init_parameters(struct fman_mac *tgec) { - if (tgec->max_speed < SPEED_10000) { - pr_err("10G MAC driver only support 10G speed\n"); - return -EINVAL; - } if (!tgec->exception_cb) { pr_err("uninitialized exception_cb\n"); return -EINVAL; @@ -384,40 +381,13 @@ static void free_init_resources(struct fman_mac *tgec) tgec->unicast_addr_hash = NULL; } -static bool is_init_done(struct tgec_cfg *cfg) -{ - /* Checks if tGEC driver parameters were initialized */ - if (!cfg) - return true; - - return false; -} - static int tgec_enable(struct fman_mac *tgec) { - struct tgec_regs __iomem *regs = tgec->regs; - u32 tmp; - - if (!is_init_done(tgec->cfg)) - return -EINVAL; - - tmp = ioread32be(®s->command_config); - tmp |= CMD_CFG_RX_EN | CMD_CFG_TX_EN; - iowrite32be(tmp, ®s->command_config); - return 0; } static void tgec_disable(struct fman_mac *tgec) { - struct tgec_regs __iomem *regs = tgec->regs; - u32 tmp; - - WARN_ON_ONCE(!is_init_done(tgec->cfg)); - - tmp = ioread32be(®s->command_config); - tmp &= ~(CMD_CFG_RX_EN | CMD_CFG_TX_EN); - iowrite32be(tmp, ®s->command_config); } static int tgec_set_promiscuous(struct fman_mac *tgec, bool new_val) @@ -425,9 +395,6 @@ static int tgec_set_promiscuous(struct fman_mac *tgec, bool new_val) struct tgec_regs __iomem *regs = tgec->regs; u32 tmp; - if (!is_init_done(tgec->cfg)) - return -EINVAL; - tmp = ioread32be(®s->command_config); if (new_val) tmp |= CMD_CFG_PROMIS_EN; @@ -444,9 +411,6 @@ static int tgec_set_tx_pause_frames(struct fman_mac *tgec, { struct tgec_regs __iomem *regs = tgec->regs; - if (!is_init_done(tgec->cfg)) - return -EINVAL; - iowrite32be((u32)pause_time, ®s->pause_quant); return 0; @@ -457,9 +421,6 @@ static int tgec_accept_rx_pause_frames(struct fman_mac *tgec, bool en) struct tgec_regs __iomem *regs = tgec->regs; u32 tmp; - if (!is_init_done(tgec->cfg)) - return -EINVAL; - tmp = ioread32be(®s->command_config); if (!en) tmp |= CMD_CFG_PAUSE_IGNORE; @@ -470,12 +431,53 @@ static int tgec_accept_rx_pause_frames(struct fman_mac *tgec, bool en) return 0; } +static void tgec_mac_config(struct phylink_config *config, unsigned int mode, + const struct phylink_link_state *state) +{ +} + +static void tgec_link_up(struct phylink_config *config, struct phy_device *phy, + unsigned int mode, phy_interface_t interface, + int speed, int duplex, bool tx_pause, bool rx_pause) +{ + struct mac_device *mac_dev = fman_config_to_mac(config); + struct fman_mac *tgec = mac_dev->fman_mac; + struct tgec_regs __iomem *regs = tgec->regs; + u16 pause_time = tx_pause ? FSL_FM_PAUSE_TIME_ENABLE : + FSL_FM_PAUSE_TIME_DISABLE; + u32 tmp; + + tgec_set_tx_pause_frames(tgec, 0, pause_time, 0); + tgec_accept_rx_pause_frames(tgec, rx_pause); + mac_dev->update_speed(mac_dev, speed); + + tmp = ioread32be(®s->command_config); + tmp |= CMD_CFG_RX_EN | CMD_CFG_TX_EN; + iowrite32be(tmp, ®s->command_config); +} + +static void tgec_link_down(struct phylink_config *config, unsigned int mode, + phy_interface_t interface) +{ + struct fman_mac *tgec = fman_config_to_mac(config)->fman_mac; + struct tgec_regs __iomem *regs = tgec->regs; + u32 tmp; + + tmp = ioread32be(®s->command_config); + tmp &= ~(CMD_CFG_RX_EN | CMD_CFG_TX_EN); + iowrite32be(tmp, ®s->command_config); +} + +static const struct phylink_mac_ops tgec_mac_ops = { + .validate = phylink_generic_validate, + .mac_config = tgec_mac_config, + .mac_link_up = tgec_link_up, + .mac_link_down = tgec_link_down, +}; + static int tgec_modify_mac_address(struct fman_mac *tgec, const enet_addr_t *p_enet_addr) { - if (!is_init_done(tgec->cfg)) - return -EINVAL; - tgec->addr = ENET_ADDR_TO_UINT64(*p_enet_addr); set_mac_address(tgec->regs, (const u8 *)(*p_enet_addr)); @@ -490,9 +492,6 @@ static int tgec_add_hash_mac_address(struct fman_mac *tgec, u32 crc = 0xFFFFFFFF, hash; u64 addr; - if (!is_init_done(tgec->cfg)) - return -EINVAL; - addr = ENET_ADDR_TO_UINT64(*eth_addr); if (!(addr & GROUP_ADDRESS)) { @@ -525,9 +524,6 @@ static int tgec_set_allmulti(struct fman_mac *tgec, bool enable) u32 entry; struct tgec_regs __iomem *regs = tgec->regs; - if (!is_init_done(tgec->cfg)) - return -EINVAL; - if (enable) { for (entry = 0; entry < TGEC_HASH_TABLE_SIZE; entry++) iowrite32be(entry | TGEC_HASH_MCAST_EN, @@ -548,9 +544,6 @@ static int tgec_set_tstamp(struct fman_mac *tgec, bool enable) struct tgec_regs __iomem *regs = tgec->regs; u32 tmp; - if (!is_init_done(tgec->cfg)) - return -EINVAL; - tmp = ioread32be(®s->command_config); if (enable) @@ -572,9 +565,6 @@ static int tgec_del_hash_mac_address(struct fman_mac *tgec, u32 crc = 0xFFFFFFFF, hash; u64 addr; - if (!is_init_done(tgec->cfg)) - return -EINVAL; - addr = ((*(u64 *)eth_addr) >> 16); /* CRC calculation */ @@ -601,22 +591,12 @@ static int tgec_del_hash_mac_address(struct fman_mac *tgec, return 0; } -static void tgec_adjust_link(struct mac_device *mac_dev) -{ - struct phy_device *phy_dev = mac_dev->phy_dev; - - mac_dev->update_speed(mac_dev, phy_dev->speed); -} - static int tgec_set_exception(struct fman_mac *tgec, enum fman_mac_exceptions exception, bool enable) { struct tgec_regs __iomem *regs = tgec->regs; u32 bit_mask = 0; - if (!is_init_done(tgec->cfg)) - return -EINVAL; - bit_mask = get_exception_flag(exception); if (bit_mask) { if (enable) @@ -641,9 +621,6 @@ static int tgec_init(struct fman_mac *tgec) enet_addr_t eth_addr; int err; - if (is_init_done(tgec->cfg)) - return -EINVAL; - if (DEFAULT_RESET_ON_INIT && (fman_reset_mac(tgec->fm, tgec->mac_id) != 0)) { pr_err("Can't reset MAC!\n"); @@ -753,7 +730,6 @@ static struct fman_mac *tgec_config(struct mac_device *mac_dev, tgec->regs = mac_dev->vaddr; tgec->addr = ENET_ADDR_TO_UINT64(mac_dev->addr); - tgec->max_speed = params->max_speed; tgec->mac_id = params->mac_id; tgec->exceptions = (TGEC_IMASK_MDIO_SCAN_EVENT | TGEC_IMASK_REM_FAULT | @@ -788,17 +764,15 @@ int tgec_initialization(struct mac_device *mac_dev, int err; struct fman_mac *tgec; + mac_dev->phylink_ops = &tgec_mac_ops; mac_dev->set_promisc = tgec_set_promiscuous; mac_dev->change_addr = tgec_modify_mac_address; mac_dev->add_hash_mac_addr = tgec_add_hash_mac_address; mac_dev->remove_hash_mac_addr = tgec_del_hash_mac_address; - mac_dev->set_tx_pause = tgec_set_tx_pause_frames; - mac_dev->set_rx_pause = tgec_accept_rx_pause_frames; mac_dev->set_exception = tgec_set_exception; mac_dev->set_allmulti = tgec_set_allmulti; mac_dev->set_tstamp = tgec_set_tstamp; mac_dev->set_multi = fman_set_multi; - mac_dev->adjust_link = tgec_adjust_link; mac_dev->enable = tgec_enable; mac_dev->disable = tgec_disable; @@ -808,6 +782,19 @@ int tgec_initialization(struct mac_device *mac_dev, goto _return; } + /* The internal connection to the serdes is XGMII, but this isn't + * really correct for the phy mode (which is the external connection). + * However, this is how all older device trees say that they want + * XAUI, so just convert it for them. + */ + if (mac_dev->phy_if == PHY_INTERFACE_MODE_XGMII) + mac_dev->phy_if = PHY_INTERFACE_MODE_XAUI; + + __set_bit(PHY_INTERFACE_MODE_XAUI, + mac_dev->phylink_config.supported_interfaces); + mac_dev->phylink_config.mac_capabilities = + MAC_SYM_PAUSE | MAC_ASYM_PAUSE | MAC_10000FD; + tgec = mac_dev->fman_mac; tgec->cfg->max_frame_length = fman_get_max_frm(); err = tgec_init(tgec); diff --git a/drivers/net/ethernet/freescale/fman/mac.c b/drivers/net/ethernet/freescale/fman/mac.c index 7b7526fd7da3..2b0a30f69147 100644 --- a/drivers/net/ethernet/freescale/fman/mac.c +++ b/drivers/net/ethernet/freescale/fman/mac.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include @@ -93,130 +94,8 @@ int fman_set_multi(struct net_device *net_dev, struct mac_device *mac_dev) return 0; } -/** - * fman_set_mac_active_pause - * @mac_dev: A pointer to the MAC device - * @rx: Pause frame setting for RX - * @tx: Pause frame setting for TX - * - * Set the MAC RX/TX PAUSE frames settings - * - * Avoid redundant calls to FMD, if the MAC driver already contains the desired - * active PAUSE settings. Otherwise, the new active settings should be reflected - * in FMan. - * - * Return: 0 on success; Error code otherwise. - */ -int fman_set_mac_active_pause(struct mac_device *mac_dev, bool rx, bool tx) -{ - struct fman_mac *fman_mac = mac_dev->fman_mac; - int err = 0; - - if (rx != mac_dev->rx_pause_active) { - err = mac_dev->set_rx_pause(fman_mac, rx); - if (likely(err == 0)) - mac_dev->rx_pause_active = rx; - } - - if (tx != mac_dev->tx_pause_active) { - u16 pause_time = (tx ? FSL_FM_PAUSE_TIME_ENABLE : - FSL_FM_PAUSE_TIME_DISABLE); - - err = mac_dev->set_tx_pause(fman_mac, 0, pause_time, 0); - - if (likely(err == 0)) - mac_dev->tx_pause_active = tx; - } - - return err; -} -EXPORT_SYMBOL(fman_set_mac_active_pause); - -/** - * fman_get_pause_cfg - * @mac_dev: A pointer to the MAC device - * @rx_pause: Return value for RX setting - * @tx_pause: Return value for TX setting - * - * Determine the MAC RX/TX PAUSE frames settings based on PHY - * autonegotiation or values set by eththool. - * - * Return: Pointer to FMan device. - */ -void fman_get_pause_cfg(struct mac_device *mac_dev, bool *rx_pause, - bool *tx_pause) -{ - struct phy_device *phy_dev = mac_dev->phy_dev; - u16 lcl_adv, rmt_adv; - u8 flowctrl; - - *rx_pause = *tx_pause = false; - - if (!phy_dev->duplex) - return; - - /* If PAUSE autonegotiation is disabled, the TX/RX PAUSE settings - * are those set by ethtool. - */ - if (!mac_dev->autoneg_pause) { - *rx_pause = mac_dev->rx_pause_req; - *tx_pause = mac_dev->tx_pause_req; - return; - } - - /* Else if PAUSE autonegotiation is enabled, the TX/RX PAUSE - * settings depend on the result of the link negotiation. - */ - - /* get local capabilities */ - lcl_adv = linkmode_adv_to_lcl_adv_t(phy_dev->advertising); - - /* get link partner capabilities */ - rmt_adv = 0; - if (phy_dev->pause) - rmt_adv |= LPA_PAUSE_CAP; - if (phy_dev->asym_pause) - rmt_adv |= LPA_PAUSE_ASYM; - - /* Calculate TX/RX settings based on local and peer advertised - * symmetric/asymmetric PAUSE capabilities. - */ - flowctrl = mii_resolve_flowctrl_fdx(lcl_adv, rmt_adv); - if (flowctrl & FLOW_CTRL_RX) - *rx_pause = true; - if (flowctrl & FLOW_CTRL_TX) - *tx_pause = true; -} -EXPORT_SYMBOL(fman_get_pause_cfg); - -#define DTSEC_SUPPORTED \ - (SUPPORTED_10baseT_Half \ - | SUPPORTED_10baseT_Full \ - | SUPPORTED_100baseT_Half \ - | SUPPORTED_100baseT_Full \ - | SUPPORTED_Autoneg \ - | SUPPORTED_Pause \ - | SUPPORTED_Asym_Pause \ - | SUPPORTED_FIBRE \ - | SUPPORTED_MII) - static DEFINE_MUTEX(eth_lock); -static const u16 phy2speed[] = { - [PHY_INTERFACE_MODE_MII] = SPEED_100, - [PHY_INTERFACE_MODE_GMII] = SPEED_1000, - [PHY_INTERFACE_MODE_SGMII] = SPEED_1000, - [PHY_INTERFACE_MODE_TBI] = SPEED_1000, - [PHY_INTERFACE_MODE_RMII] = SPEED_100, - [PHY_INTERFACE_MODE_RGMII] = SPEED_1000, - [PHY_INTERFACE_MODE_RGMII_ID] = SPEED_1000, - [PHY_INTERFACE_MODE_RGMII_RXID] = SPEED_1000, - [PHY_INTERFACE_MODE_RGMII_TXID] = SPEED_1000, - [PHY_INTERFACE_MODE_RTBI] = SPEED_1000, - [PHY_INTERFACE_MODE_QSGMII] = SPEED_1000, - [PHY_INTERFACE_MODE_XGMII] = SPEED_10000 -}; - static struct platform_device *dpaa_eth_add_device(int fman_id, struct mac_device *mac_dev) { @@ -263,8 +142,8 @@ no_mem: } static const struct of_device_id mac_match[] = { - { .compatible = "fsl,fman-dtsec", .data = dtsec_initialization }, - { .compatible = "fsl,fman-xgec", .data = tgec_initialization }, + { .compatible = "fsl,fman-dtsec", .data = dtsec_initialization }, + { .compatible = "fsl,fman-xgec", .data = tgec_initialization }, { .compatible = "fsl,fman-memac", .data = memac_initialization }, {} }; @@ -296,6 +175,7 @@ static int mac_probe(struct platform_device *_of_dev) priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; + platform_set_drvdata(_of_dev, mac_dev); /* Save private information */ mac_dev->priv = priv; @@ -424,57 +304,21 @@ static int mac_probe(struct platform_device *_of_dev) } mac_dev->phy_if = phy_if; - priv->speed = phy2speed[mac_dev->phy_if]; - params.max_speed = priv->speed; - mac_dev->if_support = DTSEC_SUPPORTED; - /* We don't support half-duplex in SGMII mode */ - if (mac_dev->phy_if == PHY_INTERFACE_MODE_SGMII) - mac_dev->if_support &= ~(SUPPORTED_10baseT_Half | - SUPPORTED_100baseT_Half); - - /* Gigabit support (no half-duplex) */ - if (params.max_speed == 1000) - mac_dev->if_support |= SUPPORTED_1000baseT_Full; - - /* The 10G interface only supports one mode */ - if (mac_dev->phy_if == PHY_INTERFACE_MODE_XGMII) - mac_dev->if_support = SUPPORTED_10000baseT_Full; - - /* Get the rest of the PHY information */ - mac_dev->phy_node = of_parse_phandle(mac_node, "phy-handle", 0); - - params.basex_if = false; params.mac_id = priv->cell_index; params.fm = (void *)priv->fman; params.exception_cb = mac_exception; params.event_cb = mac_exception; err = init(mac_dev, mac_node, ¶ms); - if (err < 0) { - dev_err(dev, "mac_dev->init() = %d\n", err); - of_node_put(mac_dev->phy_node); - return err; - } - - /* pause frame autonegotiation enabled */ - mac_dev->autoneg_pause = true; - - /* By intializing the values to false, force FMD to enable PAUSE frames - * on RX and TX - */ - mac_dev->rx_pause_req = true; - mac_dev->tx_pause_req = true; - mac_dev->rx_pause_active = false; - mac_dev->tx_pause_active = false; - err = fman_set_mac_active_pause(mac_dev, true, true); if (err < 0) - dev_err(dev, "fman_set_mac_active_pause() = %d\n", err); + return err; if (!is_zero_ether_addr(mac_dev->addr)) dev_info(dev, "FMan MAC address: %pM\n", mac_dev->addr); priv->eth_dev = dpaa_eth_add_device(fman_id, mac_dev); if (IS_ERR(priv->eth_dev)) { + err = PTR_ERR(priv->eth_dev); dev_err(dev, "failed to add Ethernet platform device for MAC %d\n", priv->cell_index); priv->eth_dev = NULL; diff --git a/drivers/net/ethernet/freescale/fman/mac.h b/drivers/net/ethernet/freescale/fman/mac.h index b95d384271bd..5bf03e1e279a 100644 --- a/drivers/net/ethernet/freescale/fman/mac.h +++ b/drivers/net/ethernet/freescale/fman/mac.h @@ -9,6 +9,7 @@ #include #include #include +#include #include #include "fman_port.h" @@ -24,32 +25,22 @@ struct mac_device { struct device *dev; u8 addr[ETH_ALEN]; struct fman_port *port[2]; - u32 if_support; - struct phy_device *phy_dev; + struct phylink *phylink; + struct phylink_config phylink_config; phy_interface_t phy_if; - struct device_node *phy_node; - struct net_device *net_dev; - bool autoneg_pause; - bool rx_pause_req; - bool tx_pause_req; - bool rx_pause_active; - bool tx_pause_active; bool promisc; bool allmulti; + const struct phylink_mac_ops *phylink_ops; int (*enable)(struct fman_mac *mac_dev); void (*disable)(struct fman_mac *mac_dev); - void (*adjust_link)(struct mac_device *mac_dev); int (*set_promisc)(struct fman_mac *mac_dev, bool enable); int (*change_addr)(struct fman_mac *mac_dev, const enet_addr_t *enet_addr); int (*set_allmulti)(struct fman_mac *mac_dev, bool enable); int (*set_tstamp)(struct fman_mac *mac_dev, bool enable); int (*set_multi)(struct net_device *net_dev, struct mac_device *mac_dev); - int (*set_rx_pause)(struct fman_mac *mac_dev, bool en); - int (*set_tx_pause)(struct fman_mac *mac_dev, u8 priority, - u16 pause_time, u16 thresh_time); int (*set_exception)(struct fman_mac *mac_dev, enum fman_mac_exceptions exception, bool enable); int (*add_hash_mac_addr)(struct fman_mac *mac_dev, @@ -63,6 +54,12 @@ struct mac_device { struct mac_priv_s *priv; }; +static inline struct mac_device +*fman_config_to_mac(struct phylink_config *config) +{ + return container_of(config, struct mac_device, phylink_config); +} + struct dpaa_eth_data { struct mac_device *mac_dev; int mac_hw_id; From 36926a7d70c2d462fca1ed85bfee000d17fd8662 Mon Sep 17 00:00:00 2001 From: Sean Anderson Date: Mon, 17 Oct 2022 16:22:39 -0400 Subject: [PATCH 0189/2002] powerpc: dts: t208x: Mark MAC1 and MAC2 as 10G On the T208X SoCs, MAC1 and MAC2 support XGMII. Add some new MAC dtsi fragments, and mark the QMAN ports as 10G. Fixes: da414bb923d9 ("powerpc/mpc85xx: Add FSL QorIQ DPAA FMan support to the SoC device tree(s)") Signed-off-by: Sean Anderson Signed-off-by: David S. Miller --- .../boot/dts/fsl/qoriq-fman3-0-10g-2.dtsi | 44 +++++++++++++++++++ .../boot/dts/fsl/qoriq-fman3-0-10g-3.dtsi | 44 +++++++++++++++++++ arch/powerpc/boot/dts/fsl/t2081si-post.dtsi | 4 +- 3 files changed, 90 insertions(+), 2 deletions(-) create mode 100644 arch/powerpc/boot/dts/fsl/qoriq-fman3-0-10g-2.dtsi create mode 100644 arch/powerpc/boot/dts/fsl/qoriq-fman3-0-10g-3.dtsi diff --git a/arch/powerpc/boot/dts/fsl/qoriq-fman3-0-10g-2.dtsi b/arch/powerpc/boot/dts/fsl/qoriq-fman3-0-10g-2.dtsi new file mode 100644 index 000000000000..437dab3fc017 --- /dev/null +++ b/arch/powerpc/boot/dts/fsl/qoriq-fman3-0-10g-2.dtsi @@ -0,0 +1,44 @@ +// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-or-later +/* + * QorIQ FMan v3 10g port #2 device tree stub [ controller @ offset 0x400000 ] + * + * Copyright 2022 Sean Anderson + * Copyright 2012 - 2015 Freescale Semiconductor Inc. + */ + +fman@400000 { + fman0_rx_0x08: port@88000 { + cell-index = <0x8>; + compatible = "fsl,fman-v3-port-rx"; + reg = <0x88000 0x1000>; + fsl,fman-10g-port; + }; + + fman0_tx_0x28: port@a8000 { + cell-index = <0x28>; + compatible = "fsl,fman-v3-port-tx"; + reg = <0xa8000 0x1000>; + fsl,fman-10g-port; + }; + + ethernet@e0000 { + cell-index = <0>; + compatible = "fsl,fman-memac"; + reg = <0xe0000 0x1000>; + fsl,fman-ports = <&fman0_rx_0x08 &fman0_tx_0x28>; + ptp-timer = <&ptp_timer0>; + pcsphy-handle = <&pcsphy0>; + }; + + mdio@e1000 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "fsl,fman-memac-mdio", "fsl,fman-xmdio"; + reg = <0xe1000 0x1000>; + fsl,erratum-a011043; /* must ignore read errors */ + + pcsphy0: ethernet-phy@0 { + reg = <0x0>; + }; + }; +}; diff --git a/arch/powerpc/boot/dts/fsl/qoriq-fman3-0-10g-3.dtsi b/arch/powerpc/boot/dts/fsl/qoriq-fman3-0-10g-3.dtsi new file mode 100644 index 000000000000..ad116b17850a --- /dev/null +++ b/arch/powerpc/boot/dts/fsl/qoriq-fman3-0-10g-3.dtsi @@ -0,0 +1,44 @@ +// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-or-later +/* + * QorIQ FMan v3 10g port #3 device tree stub [ controller @ offset 0x400000 ] + * + * Copyright 2022 Sean Anderson + * Copyright 2012 - 2015 Freescale Semiconductor Inc. + */ + +fman@400000 { + fman0_rx_0x09: port@89000 { + cell-index = <0x9>; + compatible = "fsl,fman-v3-port-rx"; + reg = <0x89000 0x1000>; + fsl,fman-10g-port; + }; + + fman0_tx_0x29: port@a9000 { + cell-index = <0x29>; + compatible = "fsl,fman-v3-port-tx"; + reg = <0xa9000 0x1000>; + fsl,fman-10g-port; + }; + + ethernet@e2000 { + cell-index = <1>; + compatible = "fsl,fman-memac"; + reg = <0xe2000 0x1000>; + fsl,fman-ports = <&fman0_rx_0x09 &fman0_tx_0x29>; + ptp-timer = <&ptp_timer0>; + pcsphy-handle = <&pcsphy1>; + }; + + mdio@e3000 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "fsl,fman-memac-mdio", "fsl,fman-xmdio"; + reg = <0xe3000 0x1000>; + fsl,erratum-a011043; /* must ignore read errors */ + + pcsphy1: ethernet-phy@0 { + reg = <0x0>; + }; + }; +}; diff --git a/arch/powerpc/boot/dts/fsl/t2081si-post.dtsi b/arch/powerpc/boot/dts/fsl/t2081si-post.dtsi index ecbb447920bc..74e17e134387 100644 --- a/arch/powerpc/boot/dts/fsl/t2081si-post.dtsi +++ b/arch/powerpc/boot/dts/fsl/t2081si-post.dtsi @@ -609,8 +609,8 @@ /include/ "qoriq-bman1.dtsi" /include/ "qoriq-fman3-0.dtsi" -/include/ "qoriq-fman3-0-1g-0.dtsi" -/include/ "qoriq-fman3-0-1g-1.dtsi" +/include/ "qoriq-fman3-0-10g-2.dtsi" +/include/ "qoriq-fman3-0-10g-3.dtsi" /include/ "qoriq-fman3-0-1g-2.dtsi" /include/ "qoriq-fman3-0-1g-3.dtsi" /include/ "qoriq-fman3-0-1g-4.dtsi" From 4e31b808fad1f7c505f916e844000aa9dcbd2ccf Mon Sep 17 00:00:00 2001 From: Sean Anderson Date: Mon, 17 Oct 2022 16:22:40 -0400 Subject: [PATCH 0190/2002] powerpc: dts: qoriq: Add nodes for QSGMII PCSs Now that we actually read registers from QSGMII PCSs, it's important that we have the correct address (instead of hoping that we're the MAC with all the QSGMII PCSs on its bus). This adds nodes for the QSGMII PCSs. They have the same addresses on all SoCs (e.g. if QSGMIIA is present it's used for MACs 1 through 4). Since the first QSGMII PCSs share an address with the SGMII and XFI PCSs, we only add new nodes for PCSs 2-4. This avoids address conflicts on the bus. Signed-off-by: Sean Anderson Signed-off-by: David S. Miller --- .../boot/dts/fsl/qoriq-fman3-0-10g-0-best-effort.dtsi | 3 ++- arch/powerpc/boot/dts/fsl/qoriq-fman3-0-10g-0.dtsi | 10 +++++++++- .../boot/dts/fsl/qoriq-fman3-0-10g-1-best-effort.dtsi | 10 +++++++++- arch/powerpc/boot/dts/fsl/qoriq-fman3-0-10g-1.dtsi | 10 +++++++++- arch/powerpc/boot/dts/fsl/qoriq-fman3-0-10g-2.dtsi | 3 ++- arch/powerpc/boot/dts/fsl/qoriq-fman3-0-10g-3.dtsi | 3 ++- arch/powerpc/boot/dts/fsl/qoriq-fman3-0-1g-0.dtsi | 3 ++- arch/powerpc/boot/dts/fsl/qoriq-fman3-0-1g-1.dtsi | 10 +++++++++- arch/powerpc/boot/dts/fsl/qoriq-fman3-0-1g-2.dtsi | 10 +++++++++- arch/powerpc/boot/dts/fsl/qoriq-fman3-0-1g-3.dtsi | 10 +++++++++- arch/powerpc/boot/dts/fsl/qoriq-fman3-0-1g-4.dtsi | 3 ++- arch/powerpc/boot/dts/fsl/qoriq-fman3-0-1g-5.dtsi | 10 +++++++++- arch/powerpc/boot/dts/fsl/qoriq-fman3-1-10g-0.dtsi | 10 +++++++++- arch/powerpc/boot/dts/fsl/qoriq-fman3-1-10g-1.dtsi | 10 +++++++++- arch/powerpc/boot/dts/fsl/qoriq-fman3-1-1g-0.dtsi | 3 ++- arch/powerpc/boot/dts/fsl/qoriq-fman3-1-1g-1.dtsi | 10 +++++++++- arch/powerpc/boot/dts/fsl/qoriq-fman3-1-1g-2.dtsi | 10 +++++++++- arch/powerpc/boot/dts/fsl/qoriq-fman3-1-1g-3.dtsi | 10 +++++++++- arch/powerpc/boot/dts/fsl/qoriq-fman3-1-1g-4.dtsi | 3 ++- arch/powerpc/boot/dts/fsl/qoriq-fman3-1-1g-5.dtsi | 10 +++++++++- 20 files changed, 131 insertions(+), 20 deletions(-) diff --git a/arch/powerpc/boot/dts/fsl/qoriq-fman3-0-10g-0-best-effort.dtsi b/arch/powerpc/boot/dts/fsl/qoriq-fman3-0-10g-0-best-effort.dtsi index baa0c503e741..7e70977f282a 100644 --- a/arch/powerpc/boot/dts/fsl/qoriq-fman3-0-10g-0-best-effort.dtsi +++ b/arch/powerpc/boot/dts/fsl/qoriq-fman3-0-10g-0-best-effort.dtsi @@ -55,7 +55,8 @@ fman@400000 { reg = <0xe0000 0x1000>; fsl,fman-ports = <&fman0_rx_0x08 &fman0_tx_0x28>; ptp-timer = <&ptp_timer0>; - pcsphy-handle = <&pcsphy0>; + pcsphy-handle = <&pcsphy0>, <&pcsphy0>; + pcs-handle-names = "sgmii", "qsgmii"; }; mdio@e1000 { diff --git a/arch/powerpc/boot/dts/fsl/qoriq-fman3-0-10g-0.dtsi b/arch/powerpc/boot/dts/fsl/qoriq-fman3-0-10g-0.dtsi index 93095600e808..5f89f7c1761f 100644 --- a/arch/powerpc/boot/dts/fsl/qoriq-fman3-0-10g-0.dtsi +++ b/arch/powerpc/boot/dts/fsl/qoriq-fman3-0-10g-0.dtsi @@ -52,7 +52,15 @@ fman@400000 { compatible = "fsl,fman-memac"; reg = <0xf0000 0x1000>; fsl,fman-ports = <&fman0_rx_0x10 &fman0_tx_0x30>; - pcsphy-handle = <&pcsphy6>; + pcsphy-handle = <&pcsphy6>, <&qsgmiib_pcs2>, <&pcsphy6>; + pcs-handle-names = "sgmii", "qsgmii", "xfi"; + }; + + mdio@e9000 { + qsgmiib_pcs2: ethernet-pcs@2 { + compatible = "fsl,lynx-pcs"; + reg = <2>; + }; }; mdio@f1000 { diff --git a/arch/powerpc/boot/dts/fsl/qoriq-fman3-0-10g-1-best-effort.dtsi b/arch/powerpc/boot/dts/fsl/qoriq-fman3-0-10g-1-best-effort.dtsi index ff4bd38f0645..71eb75e82c2e 100644 --- a/arch/powerpc/boot/dts/fsl/qoriq-fman3-0-10g-1-best-effort.dtsi +++ b/arch/powerpc/boot/dts/fsl/qoriq-fman3-0-10g-1-best-effort.dtsi @@ -55,7 +55,15 @@ fman@400000 { reg = <0xe2000 0x1000>; fsl,fman-ports = <&fman0_rx_0x09 &fman0_tx_0x29>; ptp-timer = <&ptp_timer0>; - pcsphy-handle = <&pcsphy1>; + pcsphy-handle = <&pcsphy1>, <&qsgmiia_pcs1>; + pcs-handle-names = "sgmii", "qsgmii"; + }; + + mdio@e1000 { + qsgmiia_pcs1: ethernet-pcs@1 { + compatible = "fsl,lynx-pcs"; + reg = <1>; + }; }; mdio@e3000 { diff --git a/arch/powerpc/boot/dts/fsl/qoriq-fman3-0-10g-1.dtsi b/arch/powerpc/boot/dts/fsl/qoriq-fman3-0-10g-1.dtsi index 1fa38ed6f59e..fb7032ddb7fc 100644 --- a/arch/powerpc/boot/dts/fsl/qoriq-fman3-0-10g-1.dtsi +++ b/arch/powerpc/boot/dts/fsl/qoriq-fman3-0-10g-1.dtsi @@ -52,7 +52,15 @@ fman@400000 { compatible = "fsl,fman-memac"; reg = <0xf2000 0x1000>; fsl,fman-ports = <&fman0_rx_0x11 &fman0_tx_0x31>; - pcsphy-handle = <&pcsphy7>; + pcsphy-handle = <&pcsphy7>, <&qsgmiib_pcs3>, <&pcsphy7>; + pcs-handle-names = "sgmii", "qsgmii", "xfi"; + }; + + mdio@e9000 { + qsgmiib_pcs3: ethernet-pcs@3 { + compatible = "fsl,lynx-pcs"; + reg = <3>; + }; }; mdio@f3000 { diff --git a/arch/powerpc/boot/dts/fsl/qoriq-fman3-0-10g-2.dtsi b/arch/powerpc/boot/dts/fsl/qoriq-fman3-0-10g-2.dtsi index 437dab3fc017..6b3609574b0f 100644 --- a/arch/powerpc/boot/dts/fsl/qoriq-fman3-0-10g-2.dtsi +++ b/arch/powerpc/boot/dts/fsl/qoriq-fman3-0-10g-2.dtsi @@ -27,7 +27,8 @@ fman@400000 { reg = <0xe0000 0x1000>; fsl,fman-ports = <&fman0_rx_0x08 &fman0_tx_0x28>; ptp-timer = <&ptp_timer0>; - pcsphy-handle = <&pcsphy0>; + pcsphy-handle = <&pcsphy0>, <&pcsphy0>; + pcs-handle-names = "sgmii", "xfi"; }; mdio@e1000 { diff --git a/arch/powerpc/boot/dts/fsl/qoriq-fman3-0-10g-3.dtsi b/arch/powerpc/boot/dts/fsl/qoriq-fman3-0-10g-3.dtsi index ad116b17850a..28ed1a85a436 100644 --- a/arch/powerpc/boot/dts/fsl/qoriq-fman3-0-10g-3.dtsi +++ b/arch/powerpc/boot/dts/fsl/qoriq-fman3-0-10g-3.dtsi @@ -27,7 +27,8 @@ fman@400000 { reg = <0xe2000 0x1000>; fsl,fman-ports = <&fman0_rx_0x09 &fman0_tx_0x29>; ptp-timer = <&ptp_timer0>; - pcsphy-handle = <&pcsphy1>; + pcsphy-handle = <&pcsphy1>, <&pcsphy1>; + pcs-handle-names = "sgmii", "xfi"; }; mdio@e3000 { diff --git a/arch/powerpc/boot/dts/fsl/qoriq-fman3-0-1g-0.dtsi b/arch/powerpc/boot/dts/fsl/qoriq-fman3-0-1g-0.dtsi index a8cc9780c0c4..1089d6861bfb 100644 --- a/arch/powerpc/boot/dts/fsl/qoriq-fman3-0-1g-0.dtsi +++ b/arch/powerpc/boot/dts/fsl/qoriq-fman3-0-1g-0.dtsi @@ -51,7 +51,8 @@ fman@400000 { reg = <0xe0000 0x1000>; fsl,fman-ports = <&fman0_rx_0x08 &fman0_tx_0x28>; ptp-timer = <&ptp_timer0>; - pcsphy-handle = <&pcsphy0>; + pcsphy-handle = <&pcsphy0>, <&pcsphy0>; + pcs-handle-names = "sgmii", "qsgmii"; }; mdio@e1000 { diff --git a/arch/powerpc/boot/dts/fsl/qoriq-fman3-0-1g-1.dtsi b/arch/powerpc/boot/dts/fsl/qoriq-fman3-0-1g-1.dtsi index 8b8bd70c9382..a95bbb4fc827 100644 --- a/arch/powerpc/boot/dts/fsl/qoriq-fman3-0-1g-1.dtsi +++ b/arch/powerpc/boot/dts/fsl/qoriq-fman3-0-1g-1.dtsi @@ -51,7 +51,15 @@ fman@400000 { reg = <0xe2000 0x1000>; fsl,fman-ports = <&fman0_rx_0x09 &fman0_tx_0x29>; ptp-timer = <&ptp_timer0>; - pcsphy-handle = <&pcsphy1>; + pcsphy-handle = <&pcsphy1>, <&qsgmiia_pcs1>; + pcs-handle-names = "sgmii", "qsgmii"; + }; + + mdio@e1000 { + qsgmiia_pcs1: ethernet-pcs@1 { + compatible = "fsl,lynx-pcs"; + reg = <1>; + }; }; mdio@e3000 { diff --git a/arch/powerpc/boot/dts/fsl/qoriq-fman3-0-1g-2.dtsi b/arch/powerpc/boot/dts/fsl/qoriq-fman3-0-1g-2.dtsi index 619c880b54d8..7d5af0147a25 100644 --- a/arch/powerpc/boot/dts/fsl/qoriq-fman3-0-1g-2.dtsi +++ b/arch/powerpc/boot/dts/fsl/qoriq-fman3-0-1g-2.dtsi @@ -51,7 +51,15 @@ fman@400000 { reg = <0xe4000 0x1000>; fsl,fman-ports = <&fman0_rx_0x0a &fman0_tx_0x2a>; ptp-timer = <&ptp_timer0>; - pcsphy-handle = <&pcsphy2>; + pcsphy-handle = <&pcsphy2>, <&qsgmiia_pcs2>; + pcs-handle-names = "sgmii", "qsgmii"; + }; + + mdio@e1000 { + qsgmiia_pcs2: ethernet-pcs@2 { + compatible = "fsl,lynx-pcs"; + reg = <2>; + }; }; mdio@e5000 { diff --git a/arch/powerpc/boot/dts/fsl/qoriq-fman3-0-1g-3.dtsi b/arch/powerpc/boot/dts/fsl/qoriq-fman3-0-1g-3.dtsi index d7ebb73a400d..61e5466ec854 100644 --- a/arch/powerpc/boot/dts/fsl/qoriq-fman3-0-1g-3.dtsi +++ b/arch/powerpc/boot/dts/fsl/qoriq-fman3-0-1g-3.dtsi @@ -51,7 +51,15 @@ fman@400000 { reg = <0xe6000 0x1000>; fsl,fman-ports = <&fman0_rx_0x0b &fman0_tx_0x2b>; ptp-timer = <&ptp_timer0>; - pcsphy-handle = <&pcsphy3>; + pcsphy-handle = <&pcsphy3>, <&qsgmiia_pcs3>; + pcs-handle-names = "sgmii", "qsgmii"; + }; + + mdio@e1000 { + qsgmiia_pcs3: ethernet-pcs@3 { + compatible = "fsl,lynx-pcs"; + reg = <3>; + }; }; mdio@e7000 { diff --git a/arch/powerpc/boot/dts/fsl/qoriq-fman3-0-1g-4.dtsi b/arch/powerpc/boot/dts/fsl/qoriq-fman3-0-1g-4.dtsi index b151d696a069..3ba0cdafc069 100644 --- a/arch/powerpc/boot/dts/fsl/qoriq-fman3-0-1g-4.dtsi +++ b/arch/powerpc/boot/dts/fsl/qoriq-fman3-0-1g-4.dtsi @@ -51,7 +51,8 @@ fman@400000 { reg = <0xe8000 0x1000>; fsl,fman-ports = <&fman0_rx_0x0c &fman0_tx_0x2c>; ptp-timer = <&ptp_timer0>; - pcsphy-handle = <&pcsphy4>; + pcsphy-handle = <&pcsphy4>, <&pcsphy4>; + pcs-handle-names = "sgmii", "qsgmii"; }; mdio@e9000 { diff --git a/arch/powerpc/boot/dts/fsl/qoriq-fman3-0-1g-5.dtsi b/arch/powerpc/boot/dts/fsl/qoriq-fman3-0-1g-5.dtsi index adc0ae0013a3..51748de0a289 100644 --- a/arch/powerpc/boot/dts/fsl/qoriq-fman3-0-1g-5.dtsi +++ b/arch/powerpc/boot/dts/fsl/qoriq-fman3-0-1g-5.dtsi @@ -51,7 +51,15 @@ fman@400000 { reg = <0xea000 0x1000>; fsl,fman-ports = <&fman0_rx_0x0d &fman0_tx_0x2d>; ptp-timer = <&ptp_timer0>; - pcsphy-handle = <&pcsphy5>; + pcsphy-handle = <&pcsphy5>, <&qsgmiib_pcs1>; + pcs-handle-names = "sgmii", "qsgmii"; + }; + + mdio@e9000 { + qsgmiib_pcs1: ethernet-pcs@1 { + compatible = "fsl,lynx-pcs"; + reg = <1>; + }; }; mdio@eb000 { diff --git a/arch/powerpc/boot/dts/fsl/qoriq-fman3-1-10g-0.dtsi b/arch/powerpc/boot/dts/fsl/qoriq-fman3-1-10g-0.dtsi index 435047e0e250..ee4f5170f632 100644 --- a/arch/powerpc/boot/dts/fsl/qoriq-fman3-1-10g-0.dtsi +++ b/arch/powerpc/boot/dts/fsl/qoriq-fman3-1-10g-0.dtsi @@ -52,7 +52,15 @@ fman@500000 { compatible = "fsl,fman-memac"; reg = <0xf0000 0x1000>; fsl,fman-ports = <&fman1_rx_0x10 &fman1_tx_0x30>; - pcsphy-handle = <&pcsphy14>; + pcsphy-handle = <&pcsphy14>, <&qsgmiid_pcs2>, <&pcsphy14>; + pcs-handle-names = "sgmii", "qsgmii", "xfi"; + }; + + mdio@e9000 { + qsgmiid_pcs2: ethernet-pcs@2 { + compatible = "fsl,lynx-pcs"; + reg = <2>; + }; }; mdio@f1000 { diff --git a/arch/powerpc/boot/dts/fsl/qoriq-fman3-1-10g-1.dtsi b/arch/powerpc/boot/dts/fsl/qoriq-fman3-1-10g-1.dtsi index c098657cca0a..83d2e0ce8f7b 100644 --- a/arch/powerpc/boot/dts/fsl/qoriq-fman3-1-10g-1.dtsi +++ b/arch/powerpc/boot/dts/fsl/qoriq-fman3-1-10g-1.dtsi @@ -52,7 +52,15 @@ fman@500000 { compatible = "fsl,fman-memac"; reg = <0xf2000 0x1000>; fsl,fman-ports = <&fman1_rx_0x11 &fman1_tx_0x31>; - pcsphy-handle = <&pcsphy15>; + pcsphy-handle = <&pcsphy15>, <&qsgmiid_pcs3>, <&pcsphy15>; + pcs-handle-names = "sgmii", "qsgmii", "xfi"; + }; + + mdio@e9000 { + qsgmiid_pcs3: ethernet-pcs@3 { + compatible = "fsl,lynx-pcs"; + reg = <3>; + }; }; mdio@f3000 { diff --git a/arch/powerpc/boot/dts/fsl/qoriq-fman3-1-1g-0.dtsi b/arch/powerpc/boot/dts/fsl/qoriq-fman3-1-1g-0.dtsi index 9d06824815f3..3132fc73f133 100644 --- a/arch/powerpc/boot/dts/fsl/qoriq-fman3-1-1g-0.dtsi +++ b/arch/powerpc/boot/dts/fsl/qoriq-fman3-1-1g-0.dtsi @@ -51,7 +51,8 @@ fman@500000 { reg = <0xe0000 0x1000>; fsl,fman-ports = <&fman1_rx_0x08 &fman1_tx_0x28>; ptp-timer = <&ptp_timer1>; - pcsphy-handle = <&pcsphy8>; + pcsphy-handle = <&pcsphy8>, <&pcsphy8>; + pcs-handle-names = "sgmii", "qsgmii"; }; mdio@e1000 { diff --git a/arch/powerpc/boot/dts/fsl/qoriq-fman3-1-1g-1.dtsi b/arch/powerpc/boot/dts/fsl/qoriq-fman3-1-1g-1.dtsi index 70e947730c4b..75e904d96602 100644 --- a/arch/powerpc/boot/dts/fsl/qoriq-fman3-1-1g-1.dtsi +++ b/arch/powerpc/boot/dts/fsl/qoriq-fman3-1-1g-1.dtsi @@ -51,7 +51,15 @@ fman@500000 { reg = <0xe2000 0x1000>; fsl,fman-ports = <&fman1_rx_0x09 &fman1_tx_0x29>; ptp-timer = <&ptp_timer1>; - pcsphy-handle = <&pcsphy9>; + pcsphy-handle = <&pcsphy9>, <&qsgmiic_pcs1>; + pcs-handle-names = "sgmii", "qsgmii"; + }; + + mdio@e1000 { + qsgmiic_pcs1: ethernet-pcs@1 { + compatible = "fsl,lynx-pcs"; + reg = <1>; + }; }; mdio@e3000 { diff --git a/arch/powerpc/boot/dts/fsl/qoriq-fman3-1-1g-2.dtsi b/arch/powerpc/boot/dts/fsl/qoriq-fman3-1-1g-2.dtsi index ad96e6529595..69f2cc7b8f19 100644 --- a/arch/powerpc/boot/dts/fsl/qoriq-fman3-1-1g-2.dtsi +++ b/arch/powerpc/boot/dts/fsl/qoriq-fman3-1-1g-2.dtsi @@ -51,7 +51,15 @@ fman@500000 { reg = <0xe4000 0x1000>; fsl,fman-ports = <&fman1_rx_0x0a &fman1_tx_0x2a>; ptp-timer = <&ptp_timer1>; - pcsphy-handle = <&pcsphy10>; + pcsphy-handle = <&pcsphy10>, <&qsgmiic_pcs2>; + pcs-handle-names = "sgmii", "qsgmii"; + }; + + mdio@e1000 { + qsgmiic_pcs2: ethernet-pcs@2 { + compatible = "fsl,lynx-pcs"; + reg = <2>; + }; }; mdio@e5000 { diff --git a/arch/powerpc/boot/dts/fsl/qoriq-fman3-1-1g-3.dtsi b/arch/powerpc/boot/dts/fsl/qoriq-fman3-1-1g-3.dtsi index 034bc4b71f7a..b3aaf01d7da0 100644 --- a/arch/powerpc/boot/dts/fsl/qoriq-fman3-1-1g-3.dtsi +++ b/arch/powerpc/boot/dts/fsl/qoriq-fman3-1-1g-3.dtsi @@ -51,7 +51,15 @@ fman@500000 { reg = <0xe6000 0x1000>; fsl,fman-ports = <&fman1_rx_0x0b &fman1_tx_0x2b>; ptp-timer = <&ptp_timer1>; - pcsphy-handle = <&pcsphy11>; + pcsphy-handle = <&pcsphy11>, <&qsgmiic_pcs3>; + pcs-handle-names = "sgmii", "qsgmii"; + }; + + mdio@e1000 { + qsgmiic_pcs3: ethernet-pcs@3 { + compatible = "fsl,lynx-pcs"; + reg = <3>; + }; }; mdio@e7000 { diff --git a/arch/powerpc/boot/dts/fsl/qoriq-fman3-1-1g-4.dtsi b/arch/powerpc/boot/dts/fsl/qoriq-fman3-1-1g-4.dtsi index 93ca23d82b39..18e020432807 100644 --- a/arch/powerpc/boot/dts/fsl/qoriq-fman3-1-1g-4.dtsi +++ b/arch/powerpc/boot/dts/fsl/qoriq-fman3-1-1g-4.dtsi @@ -51,7 +51,8 @@ fman@500000 { reg = <0xe8000 0x1000>; fsl,fman-ports = <&fman1_rx_0x0c &fman1_tx_0x2c>; ptp-timer = <&ptp_timer1>; - pcsphy-handle = <&pcsphy12>; + pcsphy-handle = <&pcsphy12>, <&pcsphy12>; + pcs-handle-names = "sgmii", "qsgmii"; }; mdio@e9000 { diff --git a/arch/powerpc/boot/dts/fsl/qoriq-fman3-1-1g-5.dtsi b/arch/powerpc/boot/dts/fsl/qoriq-fman3-1-1g-5.dtsi index 23b3117a2fd2..55f329d13f19 100644 --- a/arch/powerpc/boot/dts/fsl/qoriq-fman3-1-1g-5.dtsi +++ b/arch/powerpc/boot/dts/fsl/qoriq-fman3-1-1g-5.dtsi @@ -51,7 +51,15 @@ fman@500000 { reg = <0xea000 0x1000>; fsl,fman-ports = <&fman1_rx_0x0d &fman1_tx_0x2d>; ptp-timer = <&ptp_timer1>; - pcsphy-handle = <&pcsphy13>; + pcsphy-handle = <&pcsphy13>, <&qsgmiid_pcs1>; + pcs-handle-names = "sgmii", "qsgmii"; + }; + + mdio@e9000 { + qsgmiid_pcs1: ethernet-pcs@1 { + compatible = "fsl,lynx-pcs"; + reg = <1>; + }; }; mdio@eb000 { From 4e748b1bd7c06c9f744b873939aef8982d81dcaf Mon Sep 17 00:00:00 2001 From: Sean Anderson Date: Mon, 17 Oct 2022 16:22:41 -0400 Subject: [PATCH 0191/2002] arm64: dts: layerscape: Add nodes for QSGMII PCSs Now that we actually read registers from QSGMII PCSs, it's important that we have the correct address (instead of hoping that we're the MAC with all the QSGMII PCSs on its bus). This adds nodes for the QSGMII PCSs. The exact mapping of QSGMII to MACs depends on the SoC. Since the first QSGMII PCSs share an address with the SGMII and XFI PCSs, we only add new nodes for PCSs 2-4. This avoids address conflicts on the bus. Signed-off-by: Sean Anderson Signed-off-by: David S. Miller --- .../boot/dts/freescale/fsl-ls1043-post.dtsi | 24 ++++++++++++++++++ .../boot/dts/freescale/fsl-ls1046-post.dtsi | 25 +++++++++++++++++++ 2 files changed, 49 insertions(+) diff --git a/arch/arm64/boot/dts/freescale/fsl-ls1043-post.dtsi b/arch/arm64/boot/dts/freescale/fsl-ls1043-post.dtsi index d237162a8744..5c4d7eef8b61 100644 --- a/arch/arm64/boot/dts/freescale/fsl-ls1043-post.dtsi +++ b/arch/arm64/boot/dts/freescale/fsl-ls1043-post.dtsi @@ -24,9 +24,12 @@ /* these aliases provide the FMan ports mapping */ enet0: ethernet@e0000 { + pcs-handle-names = "qsgmii"; }; enet1: ethernet@e2000 { + pcsphy-handle = <&pcsphy1>, <&qsgmiib_pcs1>; + pcs-handle-names = "sgmii", "qsgmii"; }; enet2: ethernet@e4000 { @@ -36,11 +39,32 @@ }; enet4: ethernet@e8000 { + pcsphy-handle = <&pcsphy4>, <&qsgmiib_pcs2>; + pcs-handle-names = "sgmii", "qsgmii"; }; enet5: ethernet@ea000 { + pcsphy-handle = <&pcsphy5>, <&qsgmiib_pcs3>; + pcs-handle-names = "sgmii", "qsgmii"; }; enet6: ethernet@f0000 { }; + + mdio@e1000 { + qsgmiib_pcs1: ethernet-pcs@1 { + compatible = "fsl,lynx-pcs"; + reg = <0x1>; + }; + + qsgmiib_pcs2: ethernet-pcs@2 { + compatible = "fsl,lynx-pcs"; + reg = <0x2>; + }; + + qsgmiib_pcs3: ethernet-pcs@3 { + compatible = "fsl,lynx-pcs"; + reg = <0x3>; + }; + }; }; diff --git a/arch/arm64/boot/dts/freescale/fsl-ls1046-post.dtsi b/arch/arm64/boot/dts/freescale/fsl-ls1046-post.dtsi index d6caaea57d90..4e3345093943 100644 --- a/arch/arm64/boot/dts/freescale/fsl-ls1046-post.dtsi +++ b/arch/arm64/boot/dts/freescale/fsl-ls1046-post.dtsi @@ -23,6 +23,8 @@ &fman0 { /* these aliases provide the FMan ports mapping */ enet0: ethernet@e0000 { + pcsphy-handle = <&qsgmiib_pcs3>; + pcs-handle-names = "qsgmii"; }; enet1: ethernet@e2000 { @@ -35,14 +37,37 @@ }; enet4: ethernet@e8000 { + pcsphy-handle = <&pcsphy4>, <&qsgmiib_pcs1>; + pcs-handle-names = "sgmii", "qsgmii"; }; enet5: ethernet@ea000 { + pcsphy-handle = <&pcsphy5>, <&pcsphy5>; + pcs-handle-names = "sgmii", "qsgmii"; }; enet6: ethernet@f0000 { }; enet7: ethernet@f2000 { + pcsphy-handle = <&pcsphy7>, <&qsgmiib_pcs2>, <&pcsphy7>; + pcs-handle-names = "sgmii", "qsgmii", "xfi"; + }; + + mdio@eb000 { + qsgmiib_pcs1: ethernet-pcs@1 { + compatible = "fsl,lynx-pcs"; + reg = <0x1>; + }; + + qsgmiib_pcs2: ethernet-pcs@2 { + compatible = "fsl,lynx-pcs"; + reg = <0x2>; + }; + + qsgmiib_pcs3: ethernet-pcs@3 { + compatible = "fsl,lynx-pcs"; + reg = <0x3>; + }; }; }; From 6fb1faa1b92b19e3b1fad70a800c87bee934d67b Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Tue, 18 Oct 2022 09:39:58 +0300 Subject: [PATCH 0192/2002] selftests: bridge_vlan_mcast: Delete qdiscs during cleanup The qdiscs are added during setup, but not deleted during cleanup, resulting in the following error messages: # ./bridge_vlan_mcast.sh [...] # ./bridge_vlan_mcast.sh Error: Exclusivity flag on, cannot modify. Error: Exclusivity flag on, cannot modify. Solve by deleting the qdiscs during cleanup. Signed-off-by: Ido Schimmel Acked-by: Nikolay Aleksandrov Signed-off-by: David S. Miller --- tools/testing/selftests/net/forwarding/bridge_vlan_mcast.sh | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tools/testing/selftests/net/forwarding/bridge_vlan_mcast.sh b/tools/testing/selftests/net/forwarding/bridge_vlan_mcast.sh index 8748d1b1d95b..72dfbeaf56b9 100755 --- a/tools/testing/selftests/net/forwarding/bridge_vlan_mcast.sh +++ b/tools/testing/selftests/net/forwarding/bridge_vlan_mcast.sh @@ -59,6 +59,9 @@ switch_create() switch_destroy() { + tc qdisc del dev $swp2 clsact + tc qdisc del dev $swp1 clsact + ip link set dev $swp2 down ip link set dev $swp1 down From b526b2ea1454d03b294cd10aa35695d5232b1106 Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Tue, 18 Oct 2022 09:39:59 +0300 Subject: [PATCH 0193/2002] selftests: bridge_igmp: Remove unnecessary address deletion The test group address is added and removed in v2reportleave_test(). There is no need to delete it again during cleanup as it results in the following error message: # bash -x ./bridge_igmp.sh [...] + cleanup + pre_cleanup [...] + ip address del dev swp4 239.10.10.10/32 RTNETLINK answers: Cannot assign requested address + h2_destroy Solve by removing the unnecessary address deletion. Signed-off-by: Ido Schimmel Acked-by: Nikolay Aleksandrov Signed-off-by: David S. Miller --- tools/testing/selftests/net/forwarding/bridge_igmp.sh | 3 --- 1 file changed, 3 deletions(-) diff --git a/tools/testing/selftests/net/forwarding/bridge_igmp.sh b/tools/testing/selftests/net/forwarding/bridge_igmp.sh index 1162836f8f32..2aa66d2a1702 100755 --- a/tools/testing/selftests/net/forwarding/bridge_igmp.sh +++ b/tools/testing/selftests/net/forwarding/bridge_igmp.sh @@ -96,9 +96,6 @@ cleanup() switch_destroy - # Always cleanup the mcast group - ip address del dev $h2 $TEST_GROUP/32 2>&1 1>/dev/null - h2_destroy h1_destroy From 262985fad1bd819d1323c6dbd72a8d9ed1c6090c Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Tue, 18 Oct 2022 09:40:00 +0300 Subject: [PATCH 0194/2002] bridge: mcast: Use spin_lock() instead of spin_lock_bh() IGMPv3 / MLDv2 Membership Reports are only processed from the data path with softIRQ disabled, so there is no need to call spin_lock_bh(). Use spin_lock() instead. This is consistent with how other IGMP / MLD packets are processed. Signed-off-by: Ido Schimmel Acked-by: Nikolay Aleksandrov Signed-off-by: David S. Miller --- net/bridge/br_multicast.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/net/bridge/br_multicast.c b/net/bridge/br_multicast.c index db4f2641d1cd..09140bc8c15e 100644 --- a/net/bridge/br_multicast.c +++ b/net/bridge/br_multicast.c @@ -2669,7 +2669,7 @@ static int br_ip4_multicast_igmp3_report(struct net_bridge_mcast *brmctx, if (!pmctx || igmpv2) continue; - spin_lock_bh(&brmctx->br->multicast_lock); + spin_lock(&brmctx->br->multicast_lock); if (!br_multicast_ctx_should_use(brmctx, pmctx)) goto unlock_continue; @@ -2717,7 +2717,7 @@ static int br_ip4_multicast_igmp3_report(struct net_bridge_mcast *brmctx, if (changed) br_mdb_notify(brmctx->br->dev, mdst, pg, RTM_NEWMDB); unlock_continue: - spin_unlock_bh(&brmctx->br->multicast_lock); + spin_unlock(&brmctx->br->multicast_lock); } return err; @@ -2807,7 +2807,7 @@ static int br_ip6_multicast_mld2_report(struct net_bridge_mcast *brmctx, if (!pmctx || mldv1) continue; - spin_lock_bh(&brmctx->br->multicast_lock); + spin_lock(&brmctx->br->multicast_lock); if (!br_multicast_ctx_should_use(brmctx, pmctx)) goto unlock_continue; @@ -2859,7 +2859,7 @@ static int br_ip6_multicast_mld2_report(struct net_bridge_mcast *brmctx, if (changed) br_mdb_notify(brmctx->br->dev, mdst, pg, RTM_NEWMDB); unlock_continue: - spin_unlock_bh(&brmctx->br->multicast_lock); + spin_unlock(&brmctx->br->multicast_lock); } return err; From d1942cd47dbdfb5a6187f660418a4e1be38d1312 Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Tue, 18 Oct 2022 09:40:01 +0300 Subject: [PATCH 0195/2002] bridge: mcast: Simplify MDB entry creation Before creating a new MDB entry, br_multicast_new_group() will call br_mdb_ip_get() to see if one exists and return it if so. Therefore, simply call br_multicast_new_group() and omit the call to br_mdb_ip_get(). Signed-off-by: Ido Schimmel Acked-by: Nikolay Aleksandrov Signed-off-by: David S. Miller --- net/bridge/br_mdb.c | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/net/bridge/br_mdb.c b/net/bridge/br_mdb.c index 589ff497d50c..321be94c445a 100644 --- a/net/bridge/br_mdb.c +++ b/net/bridge/br_mdb.c @@ -866,7 +866,6 @@ static int br_mdb_add_group(struct net_bridge *br, struct net_bridge_port *port, unsigned long now = jiffies; unsigned char flags = 0; u8 filter_mode; - int err; __mdb_entry_to_br_ip(entry, &group, mdb_attrs); @@ -892,13 +891,9 @@ static int br_mdb_add_group(struct net_bridge *br, struct net_bridge_port *port, return -EINVAL; } - mp = br_mdb_ip_get(br, &group); - if (!mp) { - mp = br_multicast_new_group(br, &group); - err = PTR_ERR_OR_ZERO(mp); - if (err) - return err; - } + mp = br_multicast_new_group(br, &group); + if (IS_ERR(mp)) + return PTR_ERR(mp); /* host join */ if (!port) { From a526a3cc9c8d426713f8bebc18ebbe39a8495d82 Mon Sep 17 00:00:00 2001 From: Alexandru Tachici Date: Mon, 17 Oct 2022 19:37:03 +0300 Subject: [PATCH 0196/2002] net: ethernet: adi: adin1110: Fix SPI transfers No need to use more than one SPI transfer for reads. Use only one from now as ADIN1110/2111 does not tolerate CS changes during reads. The BCM2711/2708 SPI controllers worked fine, but the NXP IMX8MM could not keep CS lowered during SPI bursts. This change aims to make the ADIN1110/2111 driver compatible with both SPI controllers, without any loss of bandwidth/other capabilities. Fixes: bc93e19d088b ("net: ethernet: adi: Add ADIN1110 support") Signed-off-by: Alexandru Tachici Signed-off-by: David S. Miller --- drivers/net/ethernet/adi/adin1110.c | 37 +++++++++++++---------------- 1 file changed, 16 insertions(+), 21 deletions(-) diff --git a/drivers/net/ethernet/adi/adin1110.c b/drivers/net/ethernet/adi/adin1110.c index 1744d623999d..086aa9c96b31 100644 --- a/drivers/net/ethernet/adi/adin1110.c +++ b/drivers/net/ethernet/adi/adin1110.c @@ -196,7 +196,7 @@ static int adin1110_read_reg(struct adin1110_priv *priv, u16 reg, u32 *val) { u32 header_len = ADIN1110_RD_HEADER_LEN; u32 read_len = ADIN1110_REG_LEN; - struct spi_transfer t[2] = {0}; + struct spi_transfer t = {0}; int ret; priv->data[0] = ADIN1110_CD | FIELD_GET(GENMASK(12, 8), reg); @@ -209,17 +209,15 @@ static int adin1110_read_reg(struct adin1110_priv *priv, u16 reg, u32 *val) header_len++; } - t[0].tx_buf = &priv->data[0]; - t[0].len = header_len; - if (priv->append_crc) read_len++; memset(&priv->data[header_len], 0, read_len); - t[1].rx_buf = &priv->data[header_len]; - t[1].len = read_len; + t.tx_buf = &priv->data[0]; + t.rx_buf = &priv->data[0]; + t.len = read_len + header_len; - ret = spi_sync_transfer(priv->spidev, t, 2); + ret = spi_sync_transfer(priv->spidev, &t, 1); if (ret) return ret; @@ -296,7 +294,7 @@ static int adin1110_read_fifo(struct adin1110_port_priv *port_priv) { struct adin1110_priv *priv = port_priv->priv; u32 header_len = ADIN1110_RD_HEADER_LEN; - struct spi_transfer t[2] = {0}; + struct spi_transfer t; u32 frame_size_no_fcs; struct sk_buff *rxb; u32 frame_size; @@ -327,12 +325,7 @@ static int adin1110_read_fifo(struct adin1110_port_priv *port_priv) return ret; frame_size_no_fcs = frame_size - ADIN1110_FRAME_HEADER_LEN - ADIN1110_FEC_LEN; - - rxb = netdev_alloc_skb(port_priv->netdev, round_len); - if (!rxb) - return -ENOMEM; - - memset(priv->data, 0, round_len + ADIN1110_RD_HEADER_LEN); + memset(priv->data, 0, ADIN1110_RD_HEADER_LEN); priv->data[0] = ADIN1110_CD | FIELD_GET(GENMASK(12, 8), reg); priv->data[1] = FIELD_GET(GENMASK(7, 0), reg); @@ -342,21 +335,23 @@ static int adin1110_read_fifo(struct adin1110_port_priv *port_priv) header_len++; } - skb_put(rxb, frame_size_no_fcs + ADIN1110_FRAME_HEADER_LEN); + rxb = netdev_alloc_skb(port_priv->netdev, round_len + header_len); + if (!rxb) + return -ENOMEM; - t[0].tx_buf = &priv->data[0]; - t[0].len = header_len; + skb_put(rxb, frame_size_no_fcs + header_len + ADIN1110_FRAME_HEADER_LEN); - t[1].rx_buf = &rxb->data[0]; - t[1].len = round_len; + t.tx_buf = &priv->data[0]; + t.rx_buf = &rxb->data[0]; + t.len = header_len + round_len; - ret = spi_sync_transfer(priv->spidev, t, 2); + ret = spi_sync_transfer(priv->spidev, &t, 1); if (ret) { kfree_skb(rxb); return ret; } - skb_pull(rxb, ADIN1110_FRAME_HEADER_LEN); + skb_pull(rxb, header_len + ADIN1110_FRAME_HEADER_LEN); rxb->protocol = eth_type_trans(rxb, port_priv->netdev); if ((port_priv->flags & IFF_ALLMULTI && rxb->pkt_type == PACKET_MULTICAST) || From 05ee658c654bacda03f7fecef367e62aaf8e1cfe Mon Sep 17 00:00:00 2001 From: Gerhard Engleder Date: Thu, 13 Oct 2022 22:09:22 +0200 Subject: [PATCH 0197/2002] samples/bpf: Fix map iteration in xdp1_user BPF map iteration in xdp1_user results in endless loop without any output, because the return value of bpf_map_get_next_key() is checked against the wrong value. Other call locations of bpf_map_get_next_key() check for equal 0 for continuing the iteration. xdp1_user checks against unequal -1. This is wrong for a function which can return arbitrary negative errno values, because a return value of e.g. -2 results in an endless loop. With this fix xdp1_user is printing statistics again: proto 0: 1 pkt/s proto 0: 1 pkt/s proto 17: 107383 pkt/s proto 17: 881655 pkt/s proto 17: 882083 pkt/s proto 17: 881758 pkt/s Fixes: bd054102a8c7 ("libbpf: enforce strict libbpf 1.0 behaviors") Signed-off-by: Gerhard Engleder Acked-by: Song Liu Link: https://lore.kernel.org/r/20221013200922.17167-1-gerhard@engleder-embedded.com Signed-off-by: Martin KaFai Lau --- samples/bpf/xdp1_user.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/bpf/xdp1_user.c b/samples/bpf/xdp1_user.c index ac370e638fa3..281dc964de8d 100644 --- a/samples/bpf/xdp1_user.c +++ b/samples/bpf/xdp1_user.c @@ -51,7 +51,7 @@ static void poll_stats(int map_fd, int interval) sleep(interval); - while (bpf_map_get_next_key(map_fd, &key, &key) != -1) { + while (bpf_map_get_next_key(map_fd, &key, &key) == 0) { __u64 sum = 0; assert(bpf_map_lookup_elem(map_fd, &key, values) == 0); From 7a698edf954cb3f8b6e8dacdb77615355170420c Mon Sep 17 00:00:00 2001 From: Gerhard Engleder Date: Sat, 15 Oct 2022 23:30:50 +0200 Subject: [PATCH 0198/2002] samples/bpf: Fix MAC address swapping in xdp2_kern xdp2_kern rewrites and forwards packets out on the same interface. Forwarding still works but rewrite got broken when xdp multibuffer support has been added. With xdp multibuffer a local copy of the packet has been introduced. The MAC address is now swapped in the local copy, but the local copy in not written back. Fix MAC address swapping be adding write back of modified packet. Fixes: 772251742262 ("samples/bpf: fixup some tools to be able to support xdp multibuffer") Signed-off-by: Gerhard Engleder Reviewed-by: Andy Gospodarek Link: https://lore.kernel.org/r/20221015213050.65222-1-gerhard@engleder-embedded.com Signed-off-by: Martin KaFai Lau --- samples/bpf/xdp2_kern.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/samples/bpf/xdp2_kern.c b/samples/bpf/xdp2_kern.c index 3332ba6bb95f..67804ecf7ce3 100644 --- a/samples/bpf/xdp2_kern.c +++ b/samples/bpf/xdp2_kern.c @@ -112,6 +112,10 @@ int xdp_prog1(struct xdp_md *ctx) if (ipproto == IPPROTO_UDP) { swap_src_dst_mac(data); + + if (bpf_xdp_store_bytes(ctx, 0, pkt, sizeof(pkt))) + return rc; + rc = XDP_TX; } From 01dea9548f6c88dc23dc5916eecd3555b82c8fec Mon Sep 17 00:00:00 2001 From: Shaomin Deng Date: Mon, 17 Oct 2022 10:23:03 -0400 Subject: [PATCH 0199/2002] samples/bpf: Fix double word in comments Remove the repeated word "by" in comments. Signed-off-by: Shaomin Deng Link: https://lore.kernel.org/r/20221017142303.8299-1-dengshaomin@cdjrlc.com Signed-off-by: Martin KaFai Lau --- samples/bpf/hbm_edt_kern.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/bpf/hbm_edt_kern.c b/samples/bpf/hbm_edt_kern.c index a65b677acdb0..6294f1d716c0 100644 --- a/samples/bpf/hbm_edt_kern.c +++ b/samples/bpf/hbm_edt_kern.c @@ -35,7 +35,7 @@ * * If the credit is below the drop threshold, the packet is dropped. If it * is a TCP packet, then it also calls tcp_cwr since packets dropped by - * by a cgroup skb BPF program do not automatically trigger a call to + * a cgroup skb BPF program do not automatically trigger a call to * tcp_cwr in the current kernel code. * * This BPF program actually uses 2 drop thresholds, one threshold From 2c4d72d66b548f1404ff43c01b7b81f4c4c6fafd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20M=C3=BCller?= Date: Tue, 18 Oct 2022 16:32:31 +0000 Subject: [PATCH 0200/2002] samples/bpf: Fix typos in README MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This change fixes some typos found in the BPF samples README file. Signed-off-by: Daniel Müller Acked-by: David Vernet Link: https://lore.kernel.org/r/20221018163231.1926462-1-deso@posteo.net Signed-off-by: Martin KaFai Lau --- samples/bpf/README.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/samples/bpf/README.rst b/samples/bpf/README.rst index 60c6494adb1b..6cfb74fb5ee6 100644 --- a/samples/bpf/README.rst +++ b/samples/bpf/README.rst @@ -37,8 +37,8 @@ user, simply call:: make headers_install -This will creates a local "usr/include" directory in the git/build top -level directory, that the make system automatically pickup first. +This will create a local "usr/include" directory in the git/build top +level directory, that the make system will automatically pick up first. Compiling ========= From 81bfcc3fcd2f99578eebc2f64248253b57fc5c76 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20M=C3=BCller?= Date: Tue, 18 Oct 2022 16:40:15 +0000 Subject: [PATCH 0201/2002] bpf/docs: Summarize CI system and deny lists MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This change adds a brief summary of the BPF continuous integration (CI) to the BPF selftest documentation. The summary focuses not so much on actual workings of the CI, as it is maintained outside of the repository, but aims to document the few bits of it that are sourced from this repository and that developers may want to adjust as part of patch submissions: the BPF kernel configuration and the deny list file(s). Changelog: - v1->v2: - use s390x instead of s390 for consistency Signed-off-by: Daniel Müller Acked-by: David Vernet Link: https://lore.kernel.org/r/20221018164015.1970862-1-deso@posteo.net Signed-off-by: Martin KaFai Lau --- tools/testing/selftests/bpf/README.rst | 42 +++++++++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) diff --git a/tools/testing/selftests/bpf/README.rst b/tools/testing/selftests/bpf/README.rst index 822548d0f2ae..cb9b95702ac6 100644 --- a/tools/testing/selftests/bpf/README.rst +++ b/tools/testing/selftests/bpf/README.rst @@ -6,13 +6,53 @@ General instructions on running selftests can be found in __ /Documentation/bpf/bpf_devel_QA.rst#q-how-to-run-bpf-selftests +============= +BPF CI System +============= + +BPF employs a continuous integration (CI) system to check patch submission in an +automated fashion. The system runs selftests for each patch in a series. Results +are propagated to patchwork, where failures are highlighted similar to +violations of other checks (such as additional warnings being emitted or a +``scripts/checkpatch.pl`` reported deficiency): + + https://patchwork.kernel.org/project/netdevbpf/list/?delegate=121173 + +The CI system executes tests on multiple architectures. It uses a kernel +configuration derived from both the generic and architecture specific config +file fragments below ``tools/testing/selftests/bpf/`` (e.g., ``config`` and +``config.x86_64``). + +Denylisting Tests +================= + +It is possible for some architectures to not have support for all BPF features. +In such a case tests in CI may fail. An example of such a shortcoming is BPF +trampoline support on IBM's s390x architecture. For cases like this, an in-tree +deny list file, located at ``tools/testing/selftests/bpf/DENYLIST.``, can +be used to prevent the test from running on such an architecture. + +In addition to that, the generic ``tools/testing/selftests/bpf/DENYLIST`` is +honored on every architecture running tests. + +These files are organized in three columns. The first column lists the test in +question. This can be the name of a test suite or of an individual test. The +remaining two columns provide additional meta data that helps identify and +classify the entry: column two is a copy and paste of the error being reported +when running the test in the setting in question. The third column, if +available, summarizes the underlying problem. A value of ``trampoline``, for +example, indicates that lack of trampoline support is causing the test to fail. +This last entry helps identify tests that can be re-enabled once such support is +added. + ========================= Running Selftests in a VM ========================= It's now possible to run the selftests using ``tools/testing/selftests/bpf/vmtest.sh``. The script tries to ensure that the tests are run with the same environment as they -would be run post-submit in the CI used by the Maintainers. +would be run post-submit in the CI used by the Maintainers, with the exception +that deny lists are not automatically honored. This script uses the in-tree kernel configuration and downloads a VM userspace image from the system used by the CI. It builds the kernel (without overwriting From 85700ac19aa19c5ff7267dc406c9bccb7a60b0e3 Mon Sep 17 00:00:00 2001 From: "Daniel S. Trevitz" Date: Fri, 7 Oct 2022 07:53:02 -0400 Subject: [PATCH 0202/2002] can: add termination resistor documentation Add documentation for how to use and setup the switchable termination resistor support for CAN controllers. Signed-off-by: Daniel Trevitz Link: https://lore.kernel.org/all/3441354.44csPzL39Z@daniel6430 Signed-off-by: Marc Kleine-Budde --- Documentation/networking/can.rst | 33 ++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/Documentation/networking/can.rst b/Documentation/networking/can.rst index ebc822e605f5..90121deef217 100644 --- a/Documentation/networking/can.rst +++ b/Documentation/networking/can.rst @@ -1148,6 +1148,39 @@ tuning on deep embedded systems'. The author is running a MPC603e load without any problems ... +Switchable Termination Resistors +-------------------------------- + +CAN bus requires a specific impedance across the differential pair, +typically provided by two 120Ohm resistors on the farthest nodes of +the bus. Some CAN controllers support activating / deactivating a +termination resistor(s) to provide the correct impedance. + +Query the available resistances:: + + $ ip -details link show can0 + ... + termination 120 [ 0, 120 ] + +Activate the terminating resistor:: + + $ ip link set dev can0 type can termination 120 + +Deactivate the terminating resistor:: + + $ ip link set dev can0 type can termination 0 + +To enable termination resistor support to a can-controller, either +implement in the controller's struct can-priv:: + + termination_const + termination_const_cnt + do_set_termination + +or add gpio control with the device tree entries from +Documentation/devicetree/bindings/net/can/can-controller.yaml + + The Virtual CAN Driver (vcan) ----------------------------- From de1deb156970bade8b877124c4142edac1696ee6 Mon Sep 17 00:00:00 2001 From: Zhang Changzhong Date: Fri, 30 Sep 2022 14:52:08 +0800 Subject: [PATCH 0203/2002] can: j1939: j1939_session_tx_eoma(): fix debug info Use "%s" instead of "%p" to print function name in debug info. Signed-off-by: Zhang Changzhong Acked-by: Oleksij Rempel Link: https://lore.kernel.org/all/1664520728-4644-1-git-send-email-zhangchangzhong@huawei.com Signed-off-by: Marc Kleine-Budde --- net/can/j1939/transport.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/can/j1939/transport.c b/net/can/j1939/transport.c index d7d86c944d76..6ec48a41988c 100644 --- a/net/can/j1939/transport.c +++ b/net/can/j1939/transport.c @@ -985,7 +985,7 @@ static int j1939_session_tx_eoma(struct j1939_session *session) /* wait for the EOMA packet to come in */ j1939_tp_set_rxtimeout(session, 1250); - netdev_dbg(session->priv->ndev, "%p: 0x%p\n", __func__, session); + netdev_dbg(session->priv->ndev, "%s: 0x%p\n", __func__, session); return 0; } From 1dd1b521be85417ec409062319520ca26c1c589e Mon Sep 17 00:00:00 2001 From: Oliver Hartkopp Date: Sat, 24 Sep 2022 19:44:24 +0200 Subject: [PATCH 0204/2002] can: remove obsolete PCH CAN driver The PCH CAN driver is a driver for a Bosch C_CAN controller IP core which is attached to the system via PCI. This code has been introduced in 2011 by Oki Semiconductors developers to support the Intel Atom E6xx series I/O Hub (aka EG20T IOH PCH CAN). Since 2012 the driver only has been maintained by the kernel community. As there is a well maintained and continously tested C_CAN/D_CAN driver which also supports the PCI configuration from the PCH CAN EG20T setup this driver became obsolete. Cc: Jacob Kroon Cc: Marc Kleine-Budde Cc: Dario Binacchi Cc: Wolfgang Grandegger Signed-off-by: Oliver Hartkopp Link: https://lore.kernel.org/all/20220924174424.86541-1-socketcan@hartkopp.net Acked-by: Jacob Kroon Signed-off-by: Marc Kleine-Budde --- drivers/net/can/Kconfig | 8 - drivers/net/can/Makefile | 1 - drivers/net/can/c_can/Kconfig | 3 +- drivers/net/can/pch_can.c | 1249 --------------------------------- 4 files changed, 2 insertions(+), 1259 deletions(-) delete mode 100644 drivers/net/can/pch_can.c diff --git a/drivers/net/can/Kconfig b/drivers/net/can/Kconfig index 3048ad77edb3..cd34e8dc9394 100644 --- a/drivers/net/can/Kconfig +++ b/drivers/net/can/Kconfig @@ -198,14 +198,6 @@ config CAN_XILINXCAN Xilinx CAN driver. This driver supports both soft AXI CAN IP and Zynq CANPS IP. -config PCH_CAN - tristate "Intel EG20T PCH CAN controller" - depends on PCI && (X86_32 || COMPILE_TEST) - help - This driver is for PCH CAN of Topcliff (Intel EG20T PCH) which - is an IOH for x86 embedded processor (Intel Atom E6xx series). - This driver can access CAN bus. - source "drivers/net/can/c_can/Kconfig" source "drivers/net/can/cc770/Kconfig" source "drivers/net/can/ctucanfd/Kconfig" diff --git a/drivers/net/can/Makefile b/drivers/net/can/Makefile index 61c75ce9d500..52b0f6e10668 100644 --- a/drivers/net/can/Makefile +++ b/drivers/net/can/Makefile @@ -30,6 +30,5 @@ obj-$(CONFIG_CAN_SJA1000) += sja1000/ obj-$(CONFIG_CAN_SUN4I) += sun4i_can.o obj-$(CONFIG_CAN_TI_HECC) += ti_hecc.o obj-$(CONFIG_CAN_XILINXCAN) += xilinx_can.o -obj-$(CONFIG_PCH_CAN) += pch_can.o subdir-ccflags-$(CONFIG_CAN_DEBUG_DEVICES) += -DDEBUG diff --git a/drivers/net/can/c_can/Kconfig b/drivers/net/can/c_can/Kconfig index 962725788b0a..1f0e9acb69ec 100644 --- a/drivers/net/can/c_can/Kconfig +++ b/drivers/net/can/c_can/Kconfig @@ -20,5 +20,6 @@ config CAN_C_CAN_PCI depends on PCI help This driver adds support for the C_CAN/D_CAN chips connected - to the PCI bus. + to the PCI bus. E.g. for the C_CAN controller IP inside the + Intel Atom E6xx series IOH (aka EG20T 'PCH CAN'). endif diff --git a/drivers/net/can/pch_can.c b/drivers/net/can/pch_can.c deleted file mode 100644 index 0558ff67ec6a..000000000000 --- a/drivers/net/can/pch_can.c +++ /dev/null @@ -1,1249 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Copyright (C) 1999 - 2010 Intel Corporation. - * Copyright (C) 2010 LAPIS SEMICONDUCTOR CO., LTD. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define PCH_CTRL_INIT BIT(0) /* The INIT bit of CANCONT register. */ -#define PCH_CTRL_IE BIT(1) /* The IE bit of CAN control register */ -#define PCH_CTRL_IE_SIE_EIE (BIT(3) | BIT(2) | BIT(1)) -#define PCH_CTRL_CCE BIT(6) -#define PCH_CTRL_OPT BIT(7) /* The OPT bit of CANCONT register. */ -#define PCH_OPT_SILENT BIT(3) /* The Silent bit of CANOPT reg. */ -#define PCH_OPT_LBACK BIT(4) /* The LoopBack bit of CANOPT reg. */ - -#define PCH_CMASK_RX_TX_SET 0x00f3 -#define PCH_CMASK_RX_TX_GET 0x0073 -#define PCH_CMASK_ALL 0xff -#define PCH_CMASK_NEWDAT BIT(2) -#define PCH_CMASK_CLRINTPND BIT(3) -#define PCH_CMASK_CTRL BIT(4) -#define PCH_CMASK_ARB BIT(5) -#define PCH_CMASK_MASK BIT(6) -#define PCH_CMASK_RDWR BIT(7) -#define PCH_IF_MCONT_NEWDAT BIT(15) -#define PCH_IF_MCONT_MSGLOST BIT(14) -#define PCH_IF_MCONT_INTPND BIT(13) -#define PCH_IF_MCONT_UMASK BIT(12) -#define PCH_IF_MCONT_TXIE BIT(11) -#define PCH_IF_MCONT_RXIE BIT(10) -#define PCH_IF_MCONT_RMTEN BIT(9) -#define PCH_IF_MCONT_TXRQXT BIT(8) -#define PCH_IF_MCONT_EOB BIT(7) -#define PCH_IF_MCONT_DLC (BIT(0) | BIT(1) | BIT(2) | BIT(3)) -#define PCH_MASK2_MDIR_MXTD (BIT(14) | BIT(15)) -#define PCH_ID2_DIR BIT(13) -#define PCH_ID2_XTD BIT(14) -#define PCH_ID_MSGVAL BIT(15) -#define PCH_IF_CREQ_BUSY BIT(15) - -#define PCH_STATUS_INT 0x8000 -#define PCH_RP 0x00008000 -#define PCH_REC 0x00007f00 -#define PCH_TEC 0x000000ff - -#define PCH_TX_OK BIT(3) -#define PCH_RX_OK BIT(4) -#define PCH_EPASSIV BIT(5) -#define PCH_EWARN BIT(6) -#define PCH_BUS_OFF BIT(7) - -/* bit position of certain controller bits. */ -#define PCH_BIT_BRP_SHIFT 0 -#define PCH_BIT_SJW_SHIFT 6 -#define PCH_BIT_TSEG1_SHIFT 8 -#define PCH_BIT_TSEG2_SHIFT 12 -#define PCH_BIT_BRPE_BRPE_SHIFT 6 - -#define PCH_MSK_BITT_BRP 0x3f -#define PCH_MSK_BRPE_BRPE 0x3c0 -#define PCH_MSK_CTRL_IE_SIE_EIE 0x07 -#define PCH_COUNTER_LIMIT 10 - -#define PCH_CAN_CLK 50000000 /* 50MHz */ - -/* - * Define the number of message object. - * PCH CAN communications are done via Message RAM. - * The Message RAM consists of 32 message objects. - */ -#define PCH_RX_OBJ_NUM 26 -#define PCH_TX_OBJ_NUM 6 -#define PCH_RX_OBJ_START 1 -#define PCH_RX_OBJ_END PCH_RX_OBJ_NUM -#define PCH_TX_OBJ_START (PCH_RX_OBJ_END + 1) -#define PCH_TX_OBJ_END (PCH_RX_OBJ_NUM + PCH_TX_OBJ_NUM) - -#define PCH_FIFO_THRESH 16 - -/* TxRqst2 show status of MsgObjNo.17~32 */ -#define PCH_TREQ2_TX_MASK (((1 << PCH_TX_OBJ_NUM) - 1) <<\ - (PCH_RX_OBJ_END - 16)) - -enum pch_ifreg { - PCH_RX_IFREG, - PCH_TX_IFREG, -}; - -enum pch_can_err { - PCH_STUF_ERR = 1, - PCH_FORM_ERR, - PCH_ACK_ERR, - PCH_BIT1_ERR, - PCH_BIT0_ERR, - PCH_CRC_ERR, - PCH_LEC_ALL, -}; - -enum pch_can_mode { - PCH_CAN_ENABLE, - PCH_CAN_DISABLE, - PCH_CAN_ALL, - PCH_CAN_NONE, - PCH_CAN_STOP, - PCH_CAN_RUN, -}; - -struct pch_can_if_regs { - u32 creq; - u32 cmask; - u32 mask1; - u32 mask2; - u32 id1; - u32 id2; - u32 mcont; - u32 data[4]; - u32 rsv[13]; -}; - -struct pch_can_regs { - u32 cont; - u32 stat; - u32 errc; - u32 bitt; - u32 intr; - u32 opt; - u32 brpe; - u32 reserve; - struct pch_can_if_regs ifregs[2]; /* [0]=if1 [1]=if2 */ - u32 reserve1[8]; - u32 treq1; - u32 treq2; - u32 reserve2[6]; - u32 data1; - u32 data2; - u32 reserve3[6]; - u32 canipend1; - u32 canipend2; - u32 reserve4[6]; - u32 canmval1; - u32 canmval2; - u32 reserve5[37]; - u32 srst; -}; - -struct pch_can_priv { - struct can_priv can; - struct pci_dev *dev; - u32 tx_enable[PCH_TX_OBJ_END]; - u32 rx_enable[PCH_TX_OBJ_END]; - u32 rx_link[PCH_TX_OBJ_END]; - u32 int_enables; - struct net_device *ndev; - struct pch_can_regs __iomem *regs; - struct napi_struct napi; - int tx_obj; /* Point next Tx Obj index */ - int use_msi; -}; - -static const struct can_bittiming_const pch_can_bittiming_const = { - .name = KBUILD_MODNAME, - .tseg1_min = 2, - .tseg1_max = 16, - .tseg2_min = 1, - .tseg2_max = 8, - .sjw_max = 4, - .brp_min = 1, - .brp_max = 1024, /* 6bit + extended 4bit */ - .brp_inc = 1, -}; - -static const struct pci_device_id pch_pci_tbl[] = { - {PCI_VENDOR_ID_INTEL, 0x8818, PCI_ANY_ID, PCI_ANY_ID,}, - {0,} -}; -MODULE_DEVICE_TABLE(pci, pch_pci_tbl); - -static inline void pch_can_bit_set(void __iomem *addr, u32 mask) -{ - iowrite32(ioread32(addr) | mask, addr); -} - -static inline void pch_can_bit_clear(void __iomem *addr, u32 mask) -{ - iowrite32(ioread32(addr) & ~mask, addr); -} - -static void pch_can_set_run_mode(struct pch_can_priv *priv, - enum pch_can_mode mode) -{ - switch (mode) { - case PCH_CAN_RUN: - pch_can_bit_clear(&priv->regs->cont, PCH_CTRL_INIT); - break; - - case PCH_CAN_STOP: - pch_can_bit_set(&priv->regs->cont, PCH_CTRL_INIT); - break; - - default: - netdev_err(priv->ndev, "%s -> Invalid Mode.\n", __func__); - break; - } -} - -static void pch_can_set_optmode(struct pch_can_priv *priv) -{ - u32 reg_val = ioread32(&priv->regs->opt); - - if (priv->can.ctrlmode & CAN_CTRLMODE_LISTENONLY) - reg_val |= PCH_OPT_SILENT; - - if (priv->can.ctrlmode & CAN_CTRLMODE_LOOPBACK) - reg_val |= PCH_OPT_LBACK; - - pch_can_bit_set(&priv->regs->cont, PCH_CTRL_OPT); - iowrite32(reg_val, &priv->regs->opt); -} - -static void pch_can_rw_msg_obj(void __iomem *creq_addr, u32 num) -{ - int counter = PCH_COUNTER_LIMIT; - u32 ifx_creq; - - iowrite32(num, creq_addr); - while (counter) { - ifx_creq = ioread32(creq_addr) & PCH_IF_CREQ_BUSY; - if (!ifx_creq) - break; - counter--; - udelay(1); - } - if (!counter) - pr_err("%s:IF1 BUSY Flag is set forever.\n", __func__); -} - -static void pch_can_set_int_enables(struct pch_can_priv *priv, - enum pch_can_mode interrupt_no) -{ - switch (interrupt_no) { - case PCH_CAN_DISABLE: - pch_can_bit_clear(&priv->regs->cont, PCH_CTRL_IE); - break; - - case PCH_CAN_ALL: - pch_can_bit_set(&priv->regs->cont, PCH_CTRL_IE_SIE_EIE); - break; - - case PCH_CAN_NONE: - pch_can_bit_clear(&priv->regs->cont, PCH_CTRL_IE_SIE_EIE); - break; - - default: - netdev_err(priv->ndev, "Invalid interrupt number.\n"); - break; - } -} - -static void pch_can_set_rxtx(struct pch_can_priv *priv, u32 buff_num, - int set, enum pch_ifreg dir) -{ - u32 ie; - - if (dir) - ie = PCH_IF_MCONT_TXIE; - else - ie = PCH_IF_MCONT_RXIE; - - /* Reading the Msg buffer from Message RAM to IF1/2 registers. */ - iowrite32(PCH_CMASK_RX_TX_GET, &priv->regs->ifregs[dir].cmask); - pch_can_rw_msg_obj(&priv->regs->ifregs[dir].creq, buff_num); - - /* Setting the IF1/2MASK1 register to access MsgVal and RxIE bits */ - iowrite32(PCH_CMASK_RDWR | PCH_CMASK_ARB | PCH_CMASK_CTRL, - &priv->regs->ifregs[dir].cmask); - - if (set) { - /* Setting the MsgVal and RxIE/TxIE bits */ - pch_can_bit_set(&priv->regs->ifregs[dir].mcont, ie); - pch_can_bit_set(&priv->regs->ifregs[dir].id2, PCH_ID_MSGVAL); - } else { - /* Clearing the MsgVal and RxIE/TxIE bits */ - pch_can_bit_clear(&priv->regs->ifregs[dir].mcont, ie); - pch_can_bit_clear(&priv->regs->ifregs[dir].id2, PCH_ID_MSGVAL); - } - - pch_can_rw_msg_obj(&priv->regs->ifregs[dir].creq, buff_num); -} - -static void pch_can_set_rx_all(struct pch_can_priv *priv, int set) -{ - int i; - - /* Traversing to obtain the object configured as receivers. */ - for (i = PCH_RX_OBJ_START; i <= PCH_RX_OBJ_END; i++) - pch_can_set_rxtx(priv, i, set, PCH_RX_IFREG); -} - -static void pch_can_set_tx_all(struct pch_can_priv *priv, int set) -{ - int i; - - /* Traversing to obtain the object configured as transmit object. */ - for (i = PCH_TX_OBJ_START; i <= PCH_TX_OBJ_END; i++) - pch_can_set_rxtx(priv, i, set, PCH_TX_IFREG); -} - -static u32 pch_can_int_pending(struct pch_can_priv *priv) -{ - return ioread32(&priv->regs->intr) & 0xffff; -} - -static void pch_can_clear_if_buffers(struct pch_can_priv *priv) -{ - int i; /* Msg Obj ID (1~32) */ - - for (i = PCH_RX_OBJ_START; i <= PCH_TX_OBJ_END; i++) { - iowrite32(PCH_CMASK_RX_TX_SET, &priv->regs->ifregs[0].cmask); - iowrite32(0xffff, &priv->regs->ifregs[0].mask1); - iowrite32(0xffff, &priv->regs->ifregs[0].mask2); - iowrite32(0x0, &priv->regs->ifregs[0].id1); - iowrite32(0x0, &priv->regs->ifregs[0].id2); - iowrite32(0x0, &priv->regs->ifregs[0].mcont); - iowrite32(0x0, &priv->regs->ifregs[0].data[0]); - iowrite32(0x0, &priv->regs->ifregs[0].data[1]); - iowrite32(0x0, &priv->regs->ifregs[0].data[2]); - iowrite32(0x0, &priv->regs->ifregs[0].data[3]); - iowrite32(PCH_CMASK_RDWR | PCH_CMASK_MASK | - PCH_CMASK_ARB | PCH_CMASK_CTRL, - &priv->regs->ifregs[0].cmask); - pch_can_rw_msg_obj(&priv->regs->ifregs[0].creq, i); - } -} - -static void pch_can_config_rx_tx_buffers(struct pch_can_priv *priv) -{ - int i; - - for (i = PCH_RX_OBJ_START; i <= PCH_RX_OBJ_END; i++) { - iowrite32(PCH_CMASK_RX_TX_GET, &priv->regs->ifregs[0].cmask); - pch_can_rw_msg_obj(&priv->regs->ifregs[0].creq, i); - - iowrite32(0x0, &priv->regs->ifregs[0].id1); - iowrite32(0x0, &priv->regs->ifregs[0].id2); - - pch_can_bit_set(&priv->regs->ifregs[0].mcont, - PCH_IF_MCONT_UMASK); - - /* In case FIFO mode, Last EoB of Rx Obj must be 1 */ - if (i == PCH_RX_OBJ_END) - pch_can_bit_set(&priv->regs->ifregs[0].mcont, - PCH_IF_MCONT_EOB); - else - pch_can_bit_clear(&priv->regs->ifregs[0].mcont, - PCH_IF_MCONT_EOB); - - iowrite32(0, &priv->regs->ifregs[0].mask1); - pch_can_bit_clear(&priv->regs->ifregs[0].mask2, - 0x1fff | PCH_MASK2_MDIR_MXTD); - - /* Setting CMASK for writing */ - iowrite32(PCH_CMASK_RDWR | PCH_CMASK_MASK | PCH_CMASK_ARB | - PCH_CMASK_CTRL, &priv->regs->ifregs[0].cmask); - - pch_can_rw_msg_obj(&priv->regs->ifregs[0].creq, i); - } - - for (i = PCH_TX_OBJ_START; i <= PCH_TX_OBJ_END; i++) { - iowrite32(PCH_CMASK_RX_TX_GET, &priv->regs->ifregs[1].cmask); - pch_can_rw_msg_obj(&priv->regs->ifregs[1].creq, i); - - /* Resetting DIR bit for reception */ - iowrite32(0x0, &priv->regs->ifregs[1].id1); - iowrite32(PCH_ID2_DIR, &priv->regs->ifregs[1].id2); - - /* Setting EOB bit for transmitter */ - iowrite32(PCH_IF_MCONT_EOB | PCH_IF_MCONT_UMASK, - &priv->regs->ifregs[1].mcont); - - iowrite32(0, &priv->regs->ifregs[1].mask1); - pch_can_bit_clear(&priv->regs->ifregs[1].mask2, 0x1fff); - - /* Setting CMASK for writing */ - iowrite32(PCH_CMASK_RDWR | PCH_CMASK_MASK | PCH_CMASK_ARB | - PCH_CMASK_CTRL, &priv->regs->ifregs[1].cmask); - - pch_can_rw_msg_obj(&priv->regs->ifregs[1].creq, i); - } -} - -static void pch_can_init(struct pch_can_priv *priv) -{ - /* Stopping the Can device. */ - pch_can_set_run_mode(priv, PCH_CAN_STOP); - - /* Clearing all the message object buffers. */ - pch_can_clear_if_buffers(priv); - - /* Configuring the respective message object as either rx/tx object. */ - pch_can_config_rx_tx_buffers(priv); - - /* Enabling the interrupts. */ - pch_can_set_int_enables(priv, PCH_CAN_ALL); -} - -static void pch_can_release(struct pch_can_priv *priv) -{ - /* Stooping the CAN device. */ - pch_can_set_run_mode(priv, PCH_CAN_STOP); - - /* Disabling the interrupts. */ - pch_can_set_int_enables(priv, PCH_CAN_NONE); - - /* Disabling all the receive object. */ - pch_can_set_rx_all(priv, 0); - - /* Disabling all the transmit object. */ - pch_can_set_tx_all(priv, 0); -} - -/* This function clears interrupt(s) from the CAN device. */ -static void pch_can_int_clr(struct pch_can_priv *priv, u32 mask) -{ - /* Clear interrupt for transmit object */ - if ((mask >= PCH_RX_OBJ_START) && (mask <= PCH_RX_OBJ_END)) { - /* Setting CMASK for clearing the reception interrupts. */ - iowrite32(PCH_CMASK_RDWR | PCH_CMASK_CTRL | PCH_CMASK_ARB, - &priv->regs->ifregs[0].cmask); - - /* Clearing the Dir bit. */ - pch_can_bit_clear(&priv->regs->ifregs[0].id2, PCH_ID2_DIR); - - /* Clearing NewDat & IntPnd */ - pch_can_bit_clear(&priv->regs->ifregs[0].mcont, - PCH_IF_MCONT_NEWDAT | PCH_IF_MCONT_INTPND); - - pch_can_rw_msg_obj(&priv->regs->ifregs[0].creq, mask); - } else if ((mask >= PCH_TX_OBJ_START) && (mask <= PCH_TX_OBJ_END)) { - /* - * Setting CMASK for clearing interrupts for frame transmission. - */ - iowrite32(PCH_CMASK_RDWR | PCH_CMASK_CTRL | PCH_CMASK_ARB, - &priv->regs->ifregs[1].cmask); - - /* Resetting the ID registers. */ - pch_can_bit_set(&priv->regs->ifregs[1].id2, - PCH_ID2_DIR | (0x7ff << 2)); - iowrite32(0x0, &priv->regs->ifregs[1].id1); - - /* Clearing NewDat, TxRqst & IntPnd */ - pch_can_bit_clear(&priv->regs->ifregs[1].mcont, - PCH_IF_MCONT_NEWDAT | PCH_IF_MCONT_INTPND | - PCH_IF_MCONT_TXRQXT); - pch_can_rw_msg_obj(&priv->regs->ifregs[1].creq, mask); - } -} - -static void pch_can_reset(struct pch_can_priv *priv) -{ - /* write to sw reset register */ - iowrite32(1, &priv->regs->srst); - iowrite32(0, &priv->regs->srst); -} - -static void pch_can_error(struct net_device *ndev, u32 status) -{ - struct sk_buff *skb; - struct pch_can_priv *priv = netdev_priv(ndev); - struct can_frame *cf; - u32 errc, lec; - struct net_device_stats *stats = &(priv->ndev->stats); - enum can_state state = priv->can.state; - - skb = alloc_can_err_skb(ndev, &cf); - if (!skb) - return; - - errc = ioread32(&priv->regs->errc); - if (status & PCH_BUS_OFF) { - pch_can_set_tx_all(priv, 0); - pch_can_set_rx_all(priv, 0); - state = CAN_STATE_BUS_OFF; - cf->can_id |= CAN_ERR_BUSOFF; - priv->can.can_stats.bus_off++; - can_bus_off(ndev); - } else { - cf->can_id |= CAN_ERR_CNT; - cf->data[6] = errc & PCH_TEC; - cf->data[7] = (errc & PCH_REC) >> 8; - } - - /* Warning interrupt. */ - if (status & PCH_EWARN) { - state = CAN_STATE_ERROR_WARNING; - priv->can.can_stats.error_warning++; - cf->can_id |= CAN_ERR_CRTL; - if (((errc & PCH_REC) >> 8) > 96) - cf->data[1] |= CAN_ERR_CRTL_RX_WARNING; - if ((errc & PCH_TEC) > 96) - cf->data[1] |= CAN_ERR_CRTL_TX_WARNING; - netdev_dbg(ndev, - "%s -> Error Counter is more than 96.\n", __func__); - } - /* Error passive interrupt. */ - if (status & PCH_EPASSIV) { - priv->can.can_stats.error_passive++; - state = CAN_STATE_ERROR_PASSIVE; - cf->can_id |= CAN_ERR_CRTL; - if (errc & PCH_RP) - cf->data[1] |= CAN_ERR_CRTL_RX_PASSIVE; - if ((errc & PCH_TEC) > 127) - cf->data[1] |= CAN_ERR_CRTL_TX_PASSIVE; - netdev_dbg(ndev, - "%s -> CAN controller is ERROR PASSIVE .\n", __func__); - } - - lec = status & PCH_LEC_ALL; - switch (lec) { - case PCH_STUF_ERR: - cf->data[2] |= CAN_ERR_PROT_STUFF; - priv->can.can_stats.bus_error++; - stats->rx_errors++; - break; - case PCH_FORM_ERR: - cf->data[2] |= CAN_ERR_PROT_FORM; - priv->can.can_stats.bus_error++; - stats->rx_errors++; - break; - case PCH_ACK_ERR: - cf->can_id |= CAN_ERR_ACK; - priv->can.can_stats.bus_error++; - stats->rx_errors++; - break; - case PCH_BIT1_ERR: - case PCH_BIT0_ERR: - cf->data[2] |= CAN_ERR_PROT_BIT; - priv->can.can_stats.bus_error++; - stats->rx_errors++; - break; - case PCH_CRC_ERR: - cf->data[3] = CAN_ERR_PROT_LOC_CRC_SEQ; - priv->can.can_stats.bus_error++; - stats->rx_errors++; - break; - case PCH_LEC_ALL: /* Written by CPU. No error status */ - break; - } - - priv->can.state = state; - netif_receive_skb(skb); -} - -static irqreturn_t pch_can_interrupt(int irq, void *dev_id) -{ - struct net_device *ndev = (struct net_device *)dev_id; - struct pch_can_priv *priv = netdev_priv(ndev); - - if (!pch_can_int_pending(priv)) - return IRQ_NONE; - - pch_can_set_int_enables(priv, PCH_CAN_NONE); - napi_schedule(&priv->napi); - return IRQ_HANDLED; -} - -static void pch_fifo_thresh(struct pch_can_priv *priv, int obj_id) -{ - if (obj_id < PCH_FIFO_THRESH) { - iowrite32(PCH_CMASK_RDWR | PCH_CMASK_CTRL | - PCH_CMASK_ARB, &priv->regs->ifregs[0].cmask); - - /* Clearing the Dir bit. */ - pch_can_bit_clear(&priv->regs->ifregs[0].id2, PCH_ID2_DIR); - - /* Clearing NewDat & IntPnd */ - pch_can_bit_clear(&priv->regs->ifregs[0].mcont, - PCH_IF_MCONT_INTPND); - pch_can_rw_msg_obj(&priv->regs->ifregs[0].creq, obj_id); - } else if (obj_id > PCH_FIFO_THRESH) { - pch_can_int_clr(priv, obj_id); - } else if (obj_id == PCH_FIFO_THRESH) { - int cnt; - for (cnt = 0; cnt < PCH_FIFO_THRESH; cnt++) - pch_can_int_clr(priv, cnt + 1); - } -} - -static void pch_can_rx_msg_lost(struct net_device *ndev, int obj_id) -{ - struct pch_can_priv *priv = netdev_priv(ndev); - struct net_device_stats *stats = &(priv->ndev->stats); - struct sk_buff *skb; - struct can_frame *cf; - - netdev_dbg(priv->ndev, "Msg Obj is overwritten.\n"); - pch_can_bit_clear(&priv->regs->ifregs[0].mcont, - PCH_IF_MCONT_MSGLOST); - iowrite32(PCH_CMASK_RDWR | PCH_CMASK_CTRL, - &priv->regs->ifregs[0].cmask); - pch_can_rw_msg_obj(&priv->regs->ifregs[0].creq, obj_id); - - skb = alloc_can_err_skb(ndev, &cf); - if (!skb) - return; - - cf->can_id |= CAN_ERR_CRTL; - cf->data[1] = CAN_ERR_CRTL_RX_OVERFLOW; - stats->rx_over_errors++; - stats->rx_errors++; - - netif_receive_skb(skb); -} - -static int pch_can_rx_normal(struct net_device *ndev, u32 obj_num, int quota) -{ - u32 reg; - canid_t id; - int rcv_pkts = 0; - struct sk_buff *skb; - struct can_frame *cf; - struct pch_can_priv *priv = netdev_priv(ndev); - struct net_device_stats *stats = &(priv->ndev->stats); - int i; - u32 id2; - u16 data_reg; - - do { - /* Reading the message object from the Message RAM */ - iowrite32(PCH_CMASK_RX_TX_GET, &priv->regs->ifregs[0].cmask); - pch_can_rw_msg_obj(&priv->regs->ifregs[0].creq, obj_num); - - /* Reading the MCONT register. */ - reg = ioread32(&priv->regs->ifregs[0].mcont); - - if (reg & PCH_IF_MCONT_EOB) - break; - - /* If MsgLost bit set. */ - if (reg & PCH_IF_MCONT_MSGLOST) { - pch_can_rx_msg_lost(ndev, obj_num); - rcv_pkts++; - quota--; - obj_num++; - continue; - } else if (!(reg & PCH_IF_MCONT_NEWDAT)) { - obj_num++; - continue; - } - - skb = alloc_can_skb(priv->ndev, &cf); - if (!skb) { - netdev_err(ndev, "alloc_can_skb Failed\n"); - return rcv_pkts; - } - - /* Get Received data */ - id2 = ioread32(&priv->regs->ifregs[0].id2); - if (id2 & PCH_ID2_XTD) { - id = (ioread32(&priv->regs->ifregs[0].id1) & 0xffff); - id |= (((id2) & 0x1fff) << 16); - cf->can_id = id | CAN_EFF_FLAG; - } else { - id = (id2 >> 2) & CAN_SFF_MASK; - cf->can_id = id; - } - - cf->len = can_cc_dlc2len((ioread32(&priv->regs-> - ifregs[0].mcont)) & 0xF); - - if (id2 & PCH_ID2_DIR) { - cf->can_id |= CAN_RTR_FLAG; - } else { - for (i = 0; i < cf->len; i += 2) { - data_reg = ioread16(&priv->regs->ifregs[0].data[i / 2]); - cf->data[i] = data_reg; - cf->data[i + 1] = data_reg >> 8; - } - - stats->rx_bytes += cf->len; - } - stats->rx_packets++; - rcv_pkts++; - quota--; - netif_receive_skb(skb); - - pch_fifo_thresh(priv, obj_num); - obj_num++; - } while (quota > 0); - - return rcv_pkts; -} - -static void pch_can_tx_complete(struct net_device *ndev, u32 int_stat) -{ - struct pch_can_priv *priv = netdev_priv(ndev); - struct net_device_stats *stats = &(priv->ndev->stats); - - stats->tx_bytes += can_get_echo_skb(ndev, int_stat - PCH_RX_OBJ_END - 1, - NULL); - stats->tx_packets++; - iowrite32(PCH_CMASK_RX_TX_GET | PCH_CMASK_CLRINTPND, - &priv->regs->ifregs[1].cmask); - pch_can_rw_msg_obj(&priv->regs->ifregs[1].creq, int_stat); - if (int_stat == PCH_TX_OBJ_END) - netif_wake_queue(ndev); -} - -static int pch_can_poll(struct napi_struct *napi, int quota) -{ - struct net_device *ndev = napi->dev; - struct pch_can_priv *priv = netdev_priv(ndev); - u32 int_stat; - u32 reg_stat; - int quota_save = quota; - - int_stat = pch_can_int_pending(priv); - if (!int_stat) - goto end; - - if (int_stat == PCH_STATUS_INT) { - reg_stat = ioread32(&priv->regs->stat); - - if ((reg_stat & (PCH_BUS_OFF | PCH_LEC_ALL)) && - ((reg_stat & PCH_LEC_ALL) != PCH_LEC_ALL)) { - pch_can_error(ndev, reg_stat); - quota--; - } - - if (reg_stat & (PCH_TX_OK | PCH_RX_OK)) - pch_can_bit_clear(&priv->regs->stat, - reg_stat & (PCH_TX_OK | PCH_RX_OK)); - - int_stat = pch_can_int_pending(priv); - } - - if (quota == 0) - goto end; - - if ((int_stat >= PCH_RX_OBJ_START) && (int_stat <= PCH_RX_OBJ_END)) { - quota -= pch_can_rx_normal(ndev, int_stat, quota); - } else if ((int_stat >= PCH_TX_OBJ_START) && - (int_stat <= PCH_TX_OBJ_END)) { - /* Handle transmission interrupt */ - pch_can_tx_complete(ndev, int_stat); - } - -end: - napi_complete(napi); - pch_can_set_int_enables(priv, PCH_CAN_ALL); - - return quota_save - quota; -} - -static int pch_set_bittiming(struct net_device *ndev) -{ - struct pch_can_priv *priv = netdev_priv(ndev); - const struct can_bittiming *bt = &priv->can.bittiming; - u32 canbit; - u32 bepe; - - /* Setting the CCE bit for accessing the Can Timing register. */ - pch_can_bit_set(&priv->regs->cont, PCH_CTRL_CCE); - - canbit = (bt->brp - 1) & PCH_MSK_BITT_BRP; - canbit |= (bt->sjw - 1) << PCH_BIT_SJW_SHIFT; - canbit |= (bt->phase_seg1 + bt->prop_seg - 1) << PCH_BIT_TSEG1_SHIFT; - canbit |= (bt->phase_seg2 - 1) << PCH_BIT_TSEG2_SHIFT; - bepe = ((bt->brp - 1) & PCH_MSK_BRPE_BRPE) >> PCH_BIT_BRPE_BRPE_SHIFT; - iowrite32(canbit, &priv->regs->bitt); - iowrite32(bepe, &priv->regs->brpe); - pch_can_bit_clear(&priv->regs->cont, PCH_CTRL_CCE); - - return 0; -} - -static void pch_can_start(struct net_device *ndev) -{ - struct pch_can_priv *priv = netdev_priv(ndev); - - if (priv->can.state != CAN_STATE_STOPPED) - pch_can_reset(priv); - - pch_set_bittiming(ndev); - pch_can_set_optmode(priv); - - pch_can_set_tx_all(priv, 1); - pch_can_set_rx_all(priv, 1); - - /* Setting the CAN to run mode. */ - pch_can_set_run_mode(priv, PCH_CAN_RUN); - - priv->can.state = CAN_STATE_ERROR_ACTIVE; - - return; -} - -static int pch_can_do_set_mode(struct net_device *ndev, enum can_mode mode) -{ - int ret = 0; - - switch (mode) { - case CAN_MODE_START: - pch_can_start(ndev); - netif_wake_queue(ndev); - break; - default: - ret = -EOPNOTSUPP; - break; - } - - return ret; -} - -static int pch_can_open(struct net_device *ndev) -{ - struct pch_can_priv *priv = netdev_priv(ndev); - int retval; - - /* Registering the interrupt. */ - retval = request_irq(priv->dev->irq, pch_can_interrupt, IRQF_SHARED, - ndev->name, ndev); - if (retval) { - netdev_err(ndev, "request_irq failed.\n"); - goto req_irq_err; - } - - /* Open common can device */ - retval = open_candev(ndev); - if (retval) { - netdev_err(ndev, "open_candev() failed %d\n", retval); - goto err_open_candev; - } - - pch_can_init(priv); - pch_can_start(ndev); - napi_enable(&priv->napi); - netif_start_queue(ndev); - - return 0; - -err_open_candev: - free_irq(priv->dev->irq, ndev); -req_irq_err: - pch_can_release(priv); - - return retval; -} - -static int pch_close(struct net_device *ndev) -{ - struct pch_can_priv *priv = netdev_priv(ndev); - - netif_stop_queue(ndev); - napi_disable(&priv->napi); - pch_can_release(priv); - free_irq(priv->dev->irq, ndev); - close_candev(ndev); - priv->can.state = CAN_STATE_STOPPED; - return 0; -} - -static netdev_tx_t pch_xmit(struct sk_buff *skb, struct net_device *ndev) -{ - struct pch_can_priv *priv = netdev_priv(ndev); - struct can_frame *cf = (struct can_frame *)skb->data; - int tx_obj_no; - int i; - u32 id2; - - if (can_dropped_invalid_skb(ndev, skb)) - return NETDEV_TX_OK; - - tx_obj_no = priv->tx_obj; - if (priv->tx_obj == PCH_TX_OBJ_END) { - if (ioread32(&priv->regs->treq2) & PCH_TREQ2_TX_MASK) - netif_stop_queue(ndev); - - priv->tx_obj = PCH_TX_OBJ_START; - } else { - priv->tx_obj++; - } - - /* Setting the CMASK register. */ - pch_can_bit_set(&priv->regs->ifregs[1].cmask, PCH_CMASK_ALL); - - /* If ID extended is set. */ - if (cf->can_id & CAN_EFF_FLAG) { - iowrite32(cf->can_id & 0xffff, &priv->regs->ifregs[1].id1); - id2 = ((cf->can_id >> 16) & 0x1fff) | PCH_ID2_XTD; - } else { - iowrite32(0, &priv->regs->ifregs[1].id1); - id2 = (cf->can_id & CAN_SFF_MASK) << 2; - } - - id2 |= PCH_ID_MSGVAL; - - /* If remote frame has to be transmitted.. */ - if (!(cf->can_id & CAN_RTR_FLAG)) - id2 |= PCH_ID2_DIR; - - iowrite32(id2, &priv->regs->ifregs[1].id2); - - /* Copy data to register */ - for (i = 0; i < cf->len; i += 2) { - iowrite16(cf->data[i] | (cf->data[i + 1] << 8), - &priv->regs->ifregs[1].data[i / 2]); - } - - can_put_echo_skb(skb, ndev, tx_obj_no - PCH_RX_OBJ_END - 1, 0); - - /* Set the size of the data. Update if2_mcont */ - iowrite32(cf->len | PCH_IF_MCONT_NEWDAT | PCH_IF_MCONT_TXRQXT | - PCH_IF_MCONT_TXIE, &priv->regs->ifregs[1].mcont); - - pch_can_rw_msg_obj(&priv->regs->ifregs[1].creq, tx_obj_no); - - return NETDEV_TX_OK; -} - -static const struct net_device_ops pch_can_netdev_ops = { - .ndo_open = pch_can_open, - .ndo_stop = pch_close, - .ndo_start_xmit = pch_xmit, - .ndo_change_mtu = can_change_mtu, -}; - -static const struct ethtool_ops pch_can_ethtool_ops = { - .get_ts_info = ethtool_op_get_ts_info, -}; - -static void pch_can_remove(struct pci_dev *pdev) -{ - struct net_device *ndev = pci_get_drvdata(pdev); - struct pch_can_priv *priv = netdev_priv(ndev); - - unregister_candev(priv->ndev); - if (priv->use_msi) - pci_disable_msi(priv->dev); - pci_release_regions(pdev); - pci_disable_device(pdev); - pch_can_reset(priv); - pci_iounmap(pdev, priv->regs); - free_candev(priv->ndev); -} - -static void __maybe_unused pch_can_set_int_custom(struct pch_can_priv *priv) -{ - /* Clearing the IE, SIE and EIE bits of Can control register. */ - pch_can_bit_clear(&priv->regs->cont, PCH_CTRL_IE_SIE_EIE); - - /* Appropriately setting them. */ - pch_can_bit_set(&priv->regs->cont, - ((priv->int_enables & PCH_MSK_CTRL_IE_SIE_EIE) << 1)); -} - -/* This function retrieves interrupt enabled for the CAN device. */ -static u32 __maybe_unused pch_can_get_int_enables(struct pch_can_priv *priv) -{ - /* Obtaining the status of IE, SIE and EIE interrupt bits. */ - return (ioread32(&priv->regs->cont) & PCH_CTRL_IE_SIE_EIE) >> 1; -} - -static u32 __maybe_unused pch_can_get_rxtx_ir(struct pch_can_priv *priv, - u32 buff_num, enum pch_ifreg dir) -{ - u32 ie, enable; - - if (dir) - ie = PCH_IF_MCONT_RXIE; - else - ie = PCH_IF_MCONT_TXIE; - - iowrite32(PCH_CMASK_RX_TX_GET, &priv->regs->ifregs[dir].cmask); - pch_can_rw_msg_obj(&priv->regs->ifregs[dir].creq, buff_num); - - if (((ioread32(&priv->regs->ifregs[dir].id2)) & PCH_ID_MSGVAL) && - ((ioread32(&priv->regs->ifregs[dir].mcont)) & ie)) - enable = 1; - else - enable = 0; - - return enable; -} - -static void __maybe_unused pch_can_set_rx_buffer_link(struct pch_can_priv *priv, - u32 buffer_num, int set) -{ - iowrite32(PCH_CMASK_RX_TX_GET, &priv->regs->ifregs[0].cmask); - pch_can_rw_msg_obj(&priv->regs->ifregs[0].creq, buffer_num); - iowrite32(PCH_CMASK_RDWR | PCH_CMASK_CTRL, - &priv->regs->ifregs[0].cmask); - if (set) - pch_can_bit_clear(&priv->regs->ifregs[0].mcont, - PCH_IF_MCONT_EOB); - else - pch_can_bit_set(&priv->regs->ifregs[0].mcont, PCH_IF_MCONT_EOB); - - pch_can_rw_msg_obj(&priv->regs->ifregs[0].creq, buffer_num); -} - -static u32 __maybe_unused pch_can_get_rx_buffer_link(struct pch_can_priv *priv, - u32 buffer_num) -{ - u32 link; - - iowrite32(PCH_CMASK_RX_TX_GET, &priv->regs->ifregs[0].cmask); - pch_can_rw_msg_obj(&priv->regs->ifregs[0].creq, buffer_num); - - if (ioread32(&priv->regs->ifregs[0].mcont) & PCH_IF_MCONT_EOB) - link = 0; - else - link = 1; - return link; -} - -static int __maybe_unused pch_can_get_buffer_status(struct pch_can_priv *priv) -{ - return (ioread32(&priv->regs->treq1) & 0xffff) | - (ioread32(&priv->regs->treq2) << 16); -} - -static int __maybe_unused pch_can_suspend(struct device *dev_d) -{ - int i; - u32 buf_stat; /* Variable for reading the transmit buffer status. */ - int counter = PCH_COUNTER_LIMIT; - - struct net_device *dev = dev_get_drvdata(dev_d); - struct pch_can_priv *priv = netdev_priv(dev); - - /* Stop the CAN controller */ - pch_can_set_run_mode(priv, PCH_CAN_STOP); - - /* Indicate that we are aboutto/in suspend */ - priv->can.state = CAN_STATE_STOPPED; - - /* Waiting for all transmission to complete. */ - while (counter) { - buf_stat = pch_can_get_buffer_status(priv); - if (!buf_stat) - break; - counter--; - udelay(1); - } - if (!counter) - dev_err(dev_d, "%s -> Transmission time out.\n", __func__); - - /* Save interrupt configuration and then disable them */ - priv->int_enables = pch_can_get_int_enables(priv); - pch_can_set_int_enables(priv, PCH_CAN_DISABLE); - - /* Save Tx buffer enable state */ - for (i = PCH_TX_OBJ_START; i <= PCH_TX_OBJ_END; i++) - priv->tx_enable[i - 1] = pch_can_get_rxtx_ir(priv, i, - PCH_TX_IFREG); - - /* Disable all Transmit buffers */ - pch_can_set_tx_all(priv, 0); - - /* Save Rx buffer enable state */ - for (i = PCH_RX_OBJ_START; i <= PCH_RX_OBJ_END; i++) { - priv->rx_enable[i - 1] = pch_can_get_rxtx_ir(priv, i, - PCH_RX_IFREG); - priv->rx_link[i - 1] = pch_can_get_rx_buffer_link(priv, i); - } - - /* Disable all Receive buffers */ - pch_can_set_rx_all(priv, 0); - - return 0; -} - -static int __maybe_unused pch_can_resume(struct device *dev_d) -{ - int i; - struct net_device *dev = dev_get_drvdata(dev_d); - struct pch_can_priv *priv = netdev_priv(dev); - - priv->can.state = CAN_STATE_ERROR_ACTIVE; - - /* Disabling all interrupts. */ - pch_can_set_int_enables(priv, PCH_CAN_DISABLE); - - /* Setting the CAN device in Stop Mode. */ - pch_can_set_run_mode(priv, PCH_CAN_STOP); - - /* Configuring the transmit and receive buffers. */ - pch_can_config_rx_tx_buffers(priv); - - /* Restore the CAN state */ - pch_set_bittiming(dev); - - /* Listen/Active */ - pch_can_set_optmode(priv); - - /* Enabling the transmit buffer. */ - for (i = PCH_TX_OBJ_START; i <= PCH_TX_OBJ_END; i++) - pch_can_set_rxtx(priv, i, priv->tx_enable[i - 1], PCH_TX_IFREG); - - /* Configuring the receive buffer and enabling them. */ - for (i = PCH_RX_OBJ_START; i <= PCH_RX_OBJ_END; i++) { - /* Restore buffer link */ - pch_can_set_rx_buffer_link(priv, i, priv->rx_link[i - 1]); - - /* Restore buffer enables */ - pch_can_set_rxtx(priv, i, priv->rx_enable[i - 1], PCH_RX_IFREG); - } - - /* Enable CAN Interrupts */ - pch_can_set_int_custom(priv); - - /* Restore Run Mode */ - pch_can_set_run_mode(priv, PCH_CAN_RUN); - - return 0; -} - -static int pch_can_get_berr_counter(const struct net_device *dev, - struct can_berr_counter *bec) -{ - struct pch_can_priv *priv = netdev_priv(dev); - u32 errc = ioread32(&priv->regs->errc); - - bec->txerr = errc & PCH_TEC; - bec->rxerr = (errc & PCH_REC) >> 8; - - return 0; -} - -static int pch_can_probe(struct pci_dev *pdev, - const struct pci_device_id *id) -{ - struct net_device *ndev; - struct pch_can_priv *priv; - int rc; - void __iomem *addr; - - rc = pci_enable_device(pdev); - if (rc) { - dev_err(&pdev->dev, "Failed pci_enable_device %d\n", rc); - goto probe_exit_endev; - } - - rc = pci_request_regions(pdev, KBUILD_MODNAME); - if (rc) { - dev_err(&pdev->dev, "Failed pci_request_regions %d\n", rc); - goto probe_exit_pcireq; - } - - addr = pci_iomap(pdev, 1, 0); - if (!addr) { - rc = -EIO; - dev_err(&pdev->dev, "Failed pci_iomap\n"); - goto probe_exit_ipmap; - } - - ndev = alloc_candev(sizeof(struct pch_can_priv), PCH_TX_OBJ_END); - if (!ndev) { - rc = -ENOMEM; - dev_err(&pdev->dev, "Failed alloc_candev\n"); - goto probe_exit_alloc_candev; - } - - priv = netdev_priv(ndev); - priv->ndev = ndev; - priv->regs = addr; - priv->dev = pdev; - priv->can.bittiming_const = &pch_can_bittiming_const; - priv->can.do_set_mode = pch_can_do_set_mode; - priv->can.do_get_berr_counter = pch_can_get_berr_counter; - priv->can.ctrlmode_supported = CAN_CTRLMODE_LISTENONLY | - CAN_CTRLMODE_LOOPBACK; - priv->tx_obj = PCH_TX_OBJ_START; /* Point head of Tx Obj */ - - ndev->irq = pdev->irq; - ndev->flags |= IFF_ECHO; - - pci_set_drvdata(pdev, ndev); - SET_NETDEV_DEV(ndev, &pdev->dev); - ndev->netdev_ops = &pch_can_netdev_ops; - ndev->ethtool_ops = &pch_can_ethtool_ops; - priv->can.clock.freq = PCH_CAN_CLK; /* Hz */ - - netif_napi_add_weight(ndev, &priv->napi, pch_can_poll, PCH_RX_OBJ_END); - - rc = pci_enable_msi(priv->dev); - if (rc) { - netdev_err(ndev, "PCH CAN opened without MSI\n"); - priv->use_msi = 0; - } else { - netdev_err(ndev, "PCH CAN opened with MSI\n"); - pci_set_master(pdev); - priv->use_msi = 1; - } - - rc = register_candev(ndev); - if (rc) { - dev_err(&pdev->dev, "Failed register_candev %d\n", rc); - goto probe_exit_reg_candev; - } - - return 0; - -probe_exit_reg_candev: - if (priv->use_msi) - pci_disable_msi(priv->dev); - free_candev(ndev); -probe_exit_alloc_candev: - pci_iounmap(pdev, addr); -probe_exit_ipmap: - pci_release_regions(pdev); -probe_exit_pcireq: - pci_disable_device(pdev); -probe_exit_endev: - return rc; -} - -static SIMPLE_DEV_PM_OPS(pch_can_pm_ops, - pch_can_suspend, - pch_can_resume); - -static struct pci_driver pch_can_pci_driver = { - .name = "pch_can", - .id_table = pch_pci_tbl, - .probe = pch_can_probe, - .remove = pch_can_remove, - .driver.pm = &pch_can_pm_ops, -}; - -module_pci_driver(pch_can_pci_driver); - -MODULE_DESCRIPTION("Intel EG20T PCH CAN(Controller Area Network) Driver"); -MODULE_LICENSE("GPL v2"); -MODULE_VERSION("0.94"); From b2df8a1bc303e01b034d11d8265354df052cd938 Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Mon, 26 Sep 2022 16:43:56 -0500 Subject: [PATCH 0205/2002] can: ucan: Replace zero-length array with DECLARE_FLEX_ARRAY() helper Zero-length arrays are deprecated and we are moving towards adopting C99 flexible-array members, instead. So, replace zero-length arrays declarations in anonymous union with the new DECLARE_FLEX_ARRAY() helper macro. This helper allows for flexible-array members in unions. Link: https://github.com/KSPP/linux/issues/193 Link: https://github.com/KSPP/linux/issues/214 Link: https://gcc.gnu.org/onlinedocs/gcc/Zero-Length.html Signed-off-by: Gustavo A. R. Silva Reviewed-by: Kees Cook Link: https://lore.kernel.org/all/YzIdHDdz30BH4SAv@work Signed-off-by: Marc Kleine-Budde --- drivers/net/can/usb/ucan.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/can/usb/ucan.c b/drivers/net/can/usb/ucan.c index 7c35f50fda4e..b53e709943bc 100644 --- a/drivers/net/can/usb/ucan.c +++ b/drivers/net/can/usb/ucan.c @@ -245,7 +245,8 @@ struct ucan_message_in { /* CAN transmission complete * (type == UCAN_IN_TX_COMPLETE) */ - struct ucan_tx_complete_entry_t can_tx_complete_msg[0]; + DECLARE_FLEX_ARRAY(struct ucan_tx_complete_entry_t, + can_tx_complete_msg); } __aligned(0x4) msg; } __packed __aligned(0x4); From 26117d92d001659b10f37ff0a338db2c8523c234 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Thu, 6 Oct 2022 12:20:35 -0700 Subject: [PATCH 0206/2002] can: kvaser_usb: Remove -Warray-bounds exception GCC-12 emits false positive -Warray-bounds warnings with CONFIG_UBSAN_SHIFT (-fsanitize=shift). This is fixed in GCC 13[1], and there is top-level Makefile logic to remove -Warray-bounds for known-bad GCC versions staring with commit f0be87c42cbd ("gcc-12: disable '-Warray-bounds' universally for now"). Remove the local work-around. [1] https://gcc.gnu.org/bugzilla/show_bug.cgi?id=105679 Signed-off-by: Kees Cook Link: https://lore.kernel.org/all/20221006192035.1742912-1-keescook@chromium.org Signed-off-by: Marc Kleine-Budde --- drivers/net/can/usb/kvaser_usb/Makefile | 5 ----- 1 file changed, 5 deletions(-) diff --git a/drivers/net/can/usb/kvaser_usb/Makefile b/drivers/net/can/usb/kvaser_usb/Makefile index b20d951a0790..cf260044f0b9 100644 --- a/drivers/net/can/usb/kvaser_usb/Makefile +++ b/drivers/net/can/usb/kvaser_usb/Makefile @@ -1,8 +1,3 @@ # SPDX-License-Identifier: GPL-2.0-only obj-$(CONFIG_CAN_KVASER_USB) += kvaser_usb.o kvaser_usb-y = kvaser_usb_core.o kvaser_usb_leaf.o kvaser_usb_hydra.o - -# FIXME: temporarily silence -Warray-bounds on non W=1+ builds -ifndef KBUILD_EXTRA_WARN -CFLAGS_kvaser_usb_hydra.o += -Wno-array-bounds -endif From 51c352bdbcd23d7ce46b06c1e64c82754dc44044 Mon Sep 17 00:00:00 2001 From: Edward Cree Date: Tue, 18 Oct 2022 15:37:27 +0100 Subject: [PATCH 0207/2002] netlink: add support for formatted extack messages Include an 80-byte buffer in struct netlink_ext_ack that can be used for scnprintf()ed messages. This does mean that the resulting string can't be enumerated, translated etc. in the way NL_SET_ERR_MSG() was designed to allow. Signed-off-by: Edward Cree Reviewed-by: Jakub Kicinski Signed-off-by: Jakub Kicinski --- include/linux/netlink.h | 29 +++++++++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/include/linux/netlink.h b/include/linux/netlink.h index d51e041d2242..d81bde5a5844 100644 --- a/include/linux/netlink.h +++ b/include/linux/netlink.h @@ -64,6 +64,7 @@ netlink_kernel_create(struct net *net, int unit, struct netlink_kernel_cfg *cfg) /* this can be increased when necessary - don't expose to userland */ #define NETLINK_MAX_COOKIE_LEN 20 +#define NETLINK_MAX_FMTMSG_LEN 80 /** * struct netlink_ext_ack - netlink extended ACK report struct @@ -75,6 +76,8 @@ netlink_kernel_create(struct net *net, int unit, struct netlink_kernel_cfg *cfg) * @miss_nest: nest missing an attribute (%NULL if missing top level attr) * @cookie: cookie data to return to userspace (for success) * @cookie_len: actual cookie data length + * @_msg_buf: output buffer for formatted message strings - don't access + * directly, use %NL_SET_ERR_MSG_FMT */ struct netlink_ext_ack { const char *_msg; @@ -84,13 +87,13 @@ struct netlink_ext_ack { u16 miss_type; u8 cookie[NETLINK_MAX_COOKIE_LEN]; u8 cookie_len; + char _msg_buf[NETLINK_MAX_FMTMSG_LEN]; }; /* Always use this macro, this allows later putting the * message into a separate section or such for things * like translation or listing all possible messages. - * Currently string formatting is not supported (due - * to the lack of an output buffer.) + * If string formatting is needed use NL_SET_ERR_MSG_FMT. */ #define NL_SET_ERR_MSG(extack, msg) do { \ static const char __msg[] = msg; \ @@ -102,9 +105,31 @@ struct netlink_ext_ack { __extack->_msg = __msg; \ } while (0) +/* We splice fmt with %s at each end even in the snprintf so that both calls + * can use the same string constant, avoiding its duplication in .ro + */ +#define NL_SET_ERR_MSG_FMT(extack, fmt, args...) do { \ + struct netlink_ext_ack *__extack = (extack); \ + \ + if (!__extack) \ + break; \ + if (snprintf(__extack->_msg_buf, NETLINK_MAX_FMTMSG_LEN, \ + "%s" fmt "%s", "", ##args, "") >= \ + NETLINK_MAX_FMTMSG_LEN) \ + net_warn_ratelimited("%s" fmt "%s", "truncated extack: ", \ + ##args, "\n"); \ + \ + do_trace_netlink_extack(__extack->_msg_buf); \ + \ + __extack->_msg = __extack->_msg_buf; \ +} while (0) + #define NL_SET_ERR_MSG_MOD(extack, msg) \ NL_SET_ERR_MSG((extack), KBUILD_MODNAME ": " msg) +#define NL_SET_ERR_MSG_FMT_MOD(extack, fmt, args...) \ + NL_SET_ERR_MSG_FMT((extack), KBUILD_MODNAME ": " fmt, ##args) + #define NL_SET_BAD_ATTR_POLICY(extack, attr, pol) do { \ if ((extack)) { \ (extack)->bad_attr = (attr); \ From ad1c80d5f777b86d071e90791d008bde393bd63f Mon Sep 17 00:00:00 2001 From: Edward Cree Date: Tue, 18 Oct 2022 15:37:28 +0100 Subject: [PATCH 0208/2002] sfc: use formatted extacks instead of efx_tc_err() Since we can now get a formatted message back to the user with NL_SET_ERR_MSG_FMT_MOD(), there's no need for our special logging. Signed-off-by: Edward Cree Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/sfc/mae.c | 5 ++-- drivers/net/ethernet/sfc/tc.c | 47 +++++++++++++++------------------- drivers/net/ethernet/sfc/tc.h | 18 ------------- 3 files changed, 23 insertions(+), 47 deletions(-) diff --git a/drivers/net/ethernet/sfc/mae.c b/drivers/net/ethernet/sfc/mae.c index 874c765b2465..6f472ea0638a 100644 --- a/drivers/net/ethernet/sfc/mae.c +++ b/drivers/net/ethernet/sfc/mae.c @@ -265,9 +265,8 @@ int efx_mae_match_check_caps(struct efx_nic *efx, rc = efx_mae_match_check_cap_typ(supported_fields[MAE_FIELD_INGRESS_PORT], ingress_port_mask_type); if (rc) { - efx_tc_err(efx, "No support for %s mask in field ingress_port\n", - mask_type_name(ingress_port_mask_type)); - NL_SET_ERR_MSG_MOD(extack, "Unsupported mask type for ingress_port"); + NL_SET_ERR_MSG_FMT_MOD(extack, "No support for %s mask in field ingress_port", + mask_type_name(ingress_port_mask_type)); return rc; } return 0; diff --git a/drivers/net/ethernet/sfc/tc.c b/drivers/net/ethernet/sfc/tc.c index 3478860d4023..b21a961eabb1 100644 --- a/drivers/net/ethernet/sfc/tc.c +++ b/drivers/net/ethernet/sfc/tc.c @@ -137,17 +137,16 @@ static int efx_tc_flower_parse_match(struct efx_nic *efx, flow_rule_match_control(rule, &fm); if (fm.mask->flags) { - efx_tc_err(efx, "Unsupported match on control.flags %#x\n", - fm.mask->flags); - NL_SET_ERR_MSG_MOD(extack, "Unsupported match on control.flags"); + NL_SET_ERR_MSG_FMT_MOD(extack, "Unsupported match on control.flags %#x", + fm.mask->flags); return -EOPNOTSUPP; } } if (dissector->used_keys & ~(BIT(FLOW_DISSECTOR_KEY_CONTROL) | BIT(FLOW_DISSECTOR_KEY_BASIC))) { - efx_tc_err(efx, "Unsupported flower keys %#x\n", dissector->used_keys); - NL_SET_ERR_MSG_MOD(extack, "Unsupported flower keys encountered"); + NL_SET_ERR_MSG_FMT_MOD(extack, "Unsupported flower keys %#x", + dissector->used_keys); return -EOPNOTSUPP; } @@ -156,11 +155,11 @@ static int efx_tc_flower_parse_match(struct efx_nic *efx, flow_rule_match_basic(rule, &fm); if (fm.mask->n_proto) { - EFX_TC_ERR_MSG(efx, extack, "Unsupported eth_proto match\n"); + NL_SET_ERR_MSG_MOD(extack, "Unsupported eth_proto match"); return -EOPNOTSUPP; } if (fm.mask->ip_proto) { - EFX_TC_ERR_MSG(efx, extack, "Unsupported ip_proto match\n"); + NL_SET_ERR_MSG_MOD(extack, "Unsupported ip_proto match"); return -EOPNOTSUPP; } } @@ -200,13 +199,9 @@ static int efx_tc_flower_replace(struct efx_nic *efx, if (efv != from_efv) { /* can't happen */ - efx_tc_err(efx, "for %s efv is %snull but from_efv is %snull\n", - netdev_name(net_dev), efv ? "non-" : "", - from_efv ? "non-" : ""); - if (efv) - NL_SET_ERR_MSG_MOD(extack, "vfrep filter has PF net_dev (can't happen)"); - else - NL_SET_ERR_MSG_MOD(extack, "PF filter has vfrep net_dev (can't happen)"); + NL_SET_ERR_MSG_FMT_MOD(extack, "for %s efv is %snull but from_efv is %snull (can't happen)", + netdev_name(net_dev), efv ? "non-" : "", + from_efv ? "non-" : ""); return -EINVAL; } @@ -214,7 +209,7 @@ static int efx_tc_flower_replace(struct efx_nic *efx, memset(&match, 0, sizeof(match)); rc = efx_tc_flower_external_mport(efx, from_efv); if (rc < 0) { - EFX_TC_ERR_MSG(efx, extack, "Failed to identify ingress m-port"); + NL_SET_ERR_MSG_MOD(extack, "Failed to identify ingress m-port"); return rc; } match.value.ingress_port = rc; @@ -224,7 +219,7 @@ static int efx_tc_flower_replace(struct efx_nic *efx, return rc; if (tc->common.chain_index) { - EFX_TC_ERR_MSG(efx, extack, "No support for nonzero chain_index"); + NL_SET_ERR_MSG_MOD(extack, "No support for nonzero chain_index"); return -EOPNOTSUPP; } match.mask.recirc_id = 0xff; @@ -261,7 +256,7 @@ static int efx_tc_flower_replace(struct efx_nic *efx, if (!act) { /* more actions after a non-pipe action */ - EFX_TC_ERR_MSG(efx, extack, "Action follows non-pipe action"); + NL_SET_ERR_MSG_MOD(extack, "Action follows non-pipe action"); rc = -EINVAL; goto release; } @@ -270,7 +265,7 @@ static int efx_tc_flower_replace(struct efx_nic *efx, case FLOW_ACTION_DROP: rc = efx_mae_alloc_action_set(efx, act); if (rc) { - EFX_TC_ERR_MSG(efx, extack, "Failed to write action set to hw (drop)"); + NL_SET_ERR_MSG_MOD(extack, "Failed to write action set to hw (drop)"); goto release; } list_add_tail(&act->list, &rule->acts.list); @@ -281,20 +276,20 @@ static int efx_tc_flower_replace(struct efx_nic *efx, save = *act; to_efv = efx_tc_flower_lookup_efv(efx, fa->dev); if (IS_ERR(to_efv)) { - EFX_TC_ERR_MSG(efx, extack, "Mirred egress device not on switch"); + NL_SET_ERR_MSG_MOD(extack, "Mirred egress device not on switch"); rc = PTR_ERR(to_efv); goto release; } rc = efx_tc_flower_external_mport(efx, to_efv); if (rc < 0) { - EFX_TC_ERR_MSG(efx, extack, "Failed to identify egress m-port"); + NL_SET_ERR_MSG_MOD(extack, "Failed to identify egress m-port"); goto release; } act->dest_mport = rc; act->deliver = 1; rc = efx_mae_alloc_action_set(efx, act); if (rc) { - EFX_TC_ERR_MSG(efx, extack, "Failed to write action set to hw (mirred)"); + NL_SET_ERR_MSG_MOD(extack, "Failed to write action set to hw (mirred)"); goto release; } list_add_tail(&act->list, &rule->acts.list); @@ -310,9 +305,9 @@ static int efx_tc_flower_replace(struct efx_nic *efx, *act = save; break; default: - efx_tc_err(efx, "Unhandled action %u\n", fa->id); + NL_SET_ERR_MSG_FMT_MOD(extack, "Unhandled action %u", + fa->id); rc = -EOPNOTSUPP; - NL_SET_ERR_MSG_MOD(extack, "Unsupported action"); goto release; } } @@ -334,7 +329,7 @@ static int efx_tc_flower_replace(struct efx_nic *efx, act->deliver = 1; rc = efx_mae_alloc_action_set(efx, act); if (rc) { - EFX_TC_ERR_MSG(efx, extack, "Failed to write action set to hw (deliver)"); + NL_SET_ERR_MSG_MOD(extack, "Failed to write action set to hw (deliver)"); goto release; } list_add_tail(&act->list, &rule->acts.list); @@ -349,13 +344,13 @@ static int efx_tc_flower_replace(struct efx_nic *efx, rc = efx_mae_alloc_action_set_list(efx, &rule->acts); if (rc) { - EFX_TC_ERR_MSG(efx, extack, "Failed to write action set list to hw"); + NL_SET_ERR_MSG_MOD(extack, "Failed to write action set list to hw"); goto release; } rc = efx_mae_insert_rule(efx, &rule->match, EFX_TC_PRIO_TC, rule->acts.fw_id, &rule->fw_id); if (rc) { - EFX_TC_ERR_MSG(efx, extack, "Failed to insert rule in hw"); + NL_SET_ERR_MSG_MOD(extack, "Failed to insert rule in hw"); goto release_acts; } return 0; diff --git a/drivers/net/ethernet/sfc/tc.h b/drivers/net/ethernet/sfc/tc.h index 196fd74ed973..4373c3243e3c 100644 --- a/drivers/net/ethernet/sfc/tc.h +++ b/drivers/net/ethernet/sfc/tc.h @@ -15,24 +15,6 @@ #include #include "net_driver.h" -/* Error reporting: convenience macros. For indicating why a given filter - * insertion is not supported; errors in internal operation or in the - * hardware should be netif_err()s instead. - */ -/* Used when error message is constant. */ -#define EFX_TC_ERR_MSG(efx, extack, message) do { \ - NL_SET_ERR_MSG_MOD(extack, message); \ - if (efx->log_tc_errs) \ - netif_info(efx, drv, efx->net_dev, "%s\n", message); \ -} while (0) -/* Used when error message is not constant; caller should also supply a - * constant extack message with NL_SET_ERR_MSG_MOD(). - */ -#define efx_tc_err(efx, fmt, args...) do { \ -if (efx->log_tc_errs) \ - netif_info(efx, drv, efx->net_dev, fmt, ##args);\ -} while (0) - struct efx_tc_action_set { u16 deliver:1; u32 dest_mport; From b799f052a987c3fdd315cbac665b9202dd96382b Mon Sep 17 00:00:00 2001 From: Edward Cree Date: Tue, 18 Oct 2022 15:37:29 +0100 Subject: [PATCH 0209/2002] sfc: remove 'log-tc-errors' ethtool private flag It no longer does anything now that we're using formatted extacks instead. So we can remove the driver's whole get/set priv_flags implementation. Signed-off-by: Edward Cree Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/sfc/ef100_ethtool.c | 2 -- drivers/net/ethernet/sfc/ethtool_common.c | 37 ----------------------- drivers/net/ethernet/sfc/ethtool_common.h | 2 -- drivers/net/ethernet/sfc/net_driver.h | 2 -- 4 files changed, 43 deletions(-) diff --git a/drivers/net/ethernet/sfc/ef100_ethtool.c b/drivers/net/ethernet/sfc/ef100_ethtool.c index 135ece2f1375..702abbe59b76 100644 --- a/drivers/net/ethernet/sfc/ef100_ethtool.c +++ b/drivers/net/ethernet/sfc/ef100_ethtool.c @@ -43,8 +43,6 @@ const struct ethtool_ops ef100_ethtool_ops = { .get_pauseparam = efx_ethtool_get_pauseparam, .set_pauseparam = efx_ethtool_set_pauseparam, .get_sset_count = efx_ethtool_get_sset_count, - .get_priv_flags = efx_ethtool_get_priv_flags, - .set_priv_flags = efx_ethtool_set_priv_flags, .self_test = efx_ethtool_self_test, .get_strings = efx_ethtool_get_strings, .get_link_ksettings = efx_ethtool_get_link_ksettings, diff --git a/drivers/net/ethernet/sfc/ethtool_common.c b/drivers/net/ethernet/sfc/ethtool_common.c index 6649a2327d03..a8cbceeb301b 100644 --- a/drivers/net/ethernet/sfc/ethtool_common.c +++ b/drivers/net/ethernet/sfc/ethtool_common.c @@ -101,14 +101,6 @@ static const struct efx_sw_stat_desc efx_sw_stat_desc[] = { #define EFX_ETHTOOL_SW_STAT_COUNT ARRAY_SIZE(efx_sw_stat_desc) -static const char efx_ethtool_priv_flags_strings[][ETH_GSTRING_LEN] = { - "log-tc-errors", -}; - -#define EFX_ETHTOOL_PRIV_FLAGS_LOG_TC_ERRS BIT(0) - -#define EFX_ETHTOOL_PRIV_FLAGS_COUNT ARRAY_SIZE(efx_ethtool_priv_flags_strings) - void efx_ethtool_get_drvinfo(struct net_device *net_dev, struct ethtool_drvinfo *info) { @@ -460,8 +452,6 @@ int efx_ethtool_get_sset_count(struct net_device *net_dev, int string_set) efx_ptp_describe_stats(efx, NULL); case ETH_SS_TEST: return efx_ethtool_fill_self_tests(efx, NULL, NULL, NULL); - case ETH_SS_PRIV_FLAGS: - return EFX_ETHTOOL_PRIV_FLAGS_COUNT; default: return -EINVAL; } @@ -488,39 +478,12 @@ void efx_ethtool_get_strings(struct net_device *net_dev, case ETH_SS_TEST: efx_ethtool_fill_self_tests(efx, NULL, strings, NULL); break; - case ETH_SS_PRIV_FLAGS: - for (i = 0; i < EFX_ETHTOOL_PRIV_FLAGS_COUNT; i++) - strscpy(strings + i * ETH_GSTRING_LEN, - efx_ethtool_priv_flags_strings[i], - ETH_GSTRING_LEN); - break; default: /* No other string sets */ break; } } -u32 efx_ethtool_get_priv_flags(struct net_device *net_dev) -{ - struct efx_nic *efx = efx_netdev_priv(net_dev); - u32 ret_flags = 0; - - if (efx->log_tc_errs) - ret_flags |= EFX_ETHTOOL_PRIV_FLAGS_LOG_TC_ERRS; - - return ret_flags; -} - -int efx_ethtool_set_priv_flags(struct net_device *net_dev, u32 flags) -{ - struct efx_nic *efx = efx_netdev_priv(net_dev); - - efx->log_tc_errs = - !!(flags & EFX_ETHTOOL_PRIV_FLAGS_LOG_TC_ERRS); - - return 0; -} - void efx_ethtool_get_stats(struct net_device *net_dev, struct ethtool_stats *stats, u64 *data) diff --git a/drivers/net/ethernet/sfc/ethtool_common.h b/drivers/net/ethernet/sfc/ethtool_common.h index 0afc74021a5e..659491932101 100644 --- a/drivers/net/ethernet/sfc/ethtool_common.h +++ b/drivers/net/ethernet/sfc/ethtool_common.h @@ -27,8 +27,6 @@ int efx_ethtool_fill_self_tests(struct efx_nic *efx, int efx_ethtool_get_sset_count(struct net_device *net_dev, int string_set); void efx_ethtool_get_strings(struct net_device *net_dev, u32 string_set, u8 *strings); -u32 efx_ethtool_get_priv_flags(struct net_device *net_dev); -int efx_ethtool_set_priv_flags(struct net_device *net_dev, u32 flags); void efx_ethtool_get_stats(struct net_device *net_dev, struct ethtool_stats *stats __attribute__ ((unused)), u64 *data); diff --git a/drivers/net/ethernet/sfc/net_driver.h b/drivers/net/ethernet/sfc/net_driver.h index 2e9ba0cfe848..7ef823d7a89a 100644 --- a/drivers/net/ethernet/sfc/net_driver.h +++ b/drivers/net/ethernet/sfc/net_driver.h @@ -855,7 +855,6 @@ enum efx_xdp_tx_queues_mode { * @timer_max_ns: Interrupt timer maximum value, in nanoseconds * @irq_rx_adaptive: Adaptive IRQ moderation enabled for RX event queues * @irqs_hooked: Channel interrupts are hooked - * @log_tc_errs: Error logging for TC filter insertion is enabled * @irq_rx_mod_step_us: Step size for IRQ moderation for RX event queues * @irq_rx_moderation_us: IRQ moderation time for RX event queues * @msg_enable: Log message enable flags @@ -1018,7 +1017,6 @@ struct efx_nic { unsigned int timer_max_ns; bool irq_rx_adaptive; bool irqs_hooked; - bool log_tc_errs; unsigned int irq_mod_step_us; unsigned int irq_rx_moderation_us; u32 msg_enable; From f33f742d567449bad965bf60c0d65f861c1d7101 Mon Sep 17 00:00:00 2001 From: Andrii Nakryiko Date: Tue, 18 Oct 2022 17:28:14 -0700 Subject: [PATCH 0210/2002] libbpf: clean up and refactor BTF fixup step Refactor libbpf's BTF fixup step during BPF object open phase. The only functional change is that we now ignore BTF_VAR_GLOBAL_EXTERN variables during fix up, not just BTF_VAR_STATIC ones, which shouldn't cause any change in behavior as there shouldn't be any extern variable in data sections for valid BPF object anyways. Otherwise it's just collapsing two functions that have no reason to be separate, and switching find_elf_var_offset() helper to return entire symbol pointer, not just its offset. This will be used by next patch to get ELF symbol visibility. While refactoring, also "normalize" debug messages inside btf_fixup_datasec() to follow general libbpf style and print out data section name consistently, where it's available. Acked-by: Stanislav Fomichev Signed-off-by: Andrii Nakryiko Link: https://lore.kernel.org/r/20221019002816.359650-2-andrii@kernel.org Signed-off-by: Alexei Starovoitov --- tools/lib/bpf/libbpf.c | 96 ++++++++++++++++++------------------------ 1 file changed, 42 insertions(+), 54 deletions(-) diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c index 8c3f236c86e4..8802e06c5569 100644 --- a/tools/lib/bpf/libbpf.c +++ b/tools/lib/bpf/libbpf.c @@ -1461,15 +1461,12 @@ static int find_elf_sec_sz(const struct bpf_object *obj, const char *name, __u32 return -ENOENT; } -static int find_elf_var_offset(const struct bpf_object *obj, const char *name, __u32 *off) +static Elf64_Sym *find_elf_var_sym(const struct bpf_object *obj, const char *name) { Elf_Data *symbols = obj->efile.symbols; const char *sname; size_t si; - if (!name || !off) - return -EINVAL; - for (si = 0; si < symbols->d_size / sizeof(Elf64_Sym); si++) { Elf64_Sym *sym = elf_sym_by_idx(obj, si); @@ -1483,15 +1480,13 @@ static int find_elf_var_offset(const struct bpf_object *obj, const char *name, _ sname = elf_sym_str(obj, sym->st_name); if (!sname) { pr_warn("failed to get sym name string for var %s\n", name); - return -EIO; - } - if (strcmp(name, sname) == 0) { - *off = sym->st_value; - return 0; + return ERR_PTR(-EIO); } + if (strcmp(name, sname) == 0) + return sym; } - return -ENOENT; + return ERR_PTR(-ENOENT); } static struct bpf_map *bpf_object__add_map(struct bpf_object *obj) @@ -2850,57 +2845,63 @@ static int compare_vsi_off(const void *_a, const void *_b) static int btf_fixup_datasec(struct bpf_object *obj, struct btf *btf, struct btf_type *t) { - __u32 size = 0, off = 0, i, vars = btf_vlen(t); - const char *name = btf__name_by_offset(btf, t->name_off); - const struct btf_type *t_var; + __u32 size = 0, i, vars = btf_vlen(t); + const char *sec_name = btf__name_by_offset(btf, t->name_off); struct btf_var_secinfo *vsi; - const struct btf_var *var; - int ret; + int err; - if (!name) { + if (!sec_name) { pr_debug("No name found in string section for DATASEC kind.\n"); return -ENOENT; } - /* .extern datasec size and var offsets were set correctly during - * extern collection step, so just skip straight to sorting variables + /* extern-backing datasecs (.ksyms, .kconfig) have their size and + * variable offsets set at the previous step, so we skip any fixups + * for such sections */ if (t->size) goto sort_vars; - ret = find_elf_sec_sz(obj, name, &size); - if (ret || !size) { - pr_debug("Invalid size for section %s: %u bytes\n", name, size); + err = find_elf_sec_sz(obj, sec_name, &size); + if (err || !size) { + pr_debug("sec '%s': failed to determine size from ELF: size %u, err %d\n", + sec_name, size, err); return -ENOENT; } t->size = size; for (i = 0, vsi = btf_var_secinfos(t); i < vars; i++, vsi++) { + const struct btf_type *t_var; + struct btf_var *var; + const char *var_name; + Elf64_Sym *sym; + t_var = btf__type_by_id(btf, vsi->type); if (!t_var || !btf_is_var(t_var)) { - pr_debug("Non-VAR type seen in section %s\n", name); + pr_debug("sec '%s': unexpected non-VAR type found\n", sec_name); return -EINVAL; } var = btf_var(t_var); - if (var->linkage == BTF_VAR_STATIC) + if (var->linkage == BTF_VAR_STATIC || var->linkage == BTF_VAR_GLOBAL_EXTERN) continue; - name = btf__name_by_offset(btf, t_var->name_off); - if (!name) { - pr_debug("No name found in string section for VAR kind\n"); + var_name = btf__name_by_offset(btf, t_var->name_off); + if (!var_name) { + pr_debug("sec '%s': failed to find name of DATASEC's member #%d\n", + sec_name, i); return -ENOENT; } - ret = find_elf_var_offset(obj, name, &off); - if (ret) { - pr_debug("No offset found in symbol table for VAR %s\n", - name); + sym = find_elf_var_sym(obj, var_name); + if (IS_ERR(sym)) { + pr_debug("sec '%s': failed to find ELF symbol for VAR '%s'\n", + sec_name, var_name); return -ENOENT; } - vsi->offset = off; + vsi->offset = sym->st_value; } sort_vars: @@ -2908,13 +2909,16 @@ sort_vars: return 0; } -static int btf_finalize_data(struct bpf_object *obj, struct btf *btf) +static int bpf_object_fixup_btf(struct bpf_object *obj) { - int err = 0; - __u32 i, n = btf__type_cnt(btf); + int i, n, err = 0; + if (!obj->btf) + return 0; + + n = btf__type_cnt(obj->btf); for (i = 1; i < n; i++) { - struct btf_type *t = btf_type_by_id(btf, i); + struct btf_type *t = btf_type_by_id(obj->btf, i); /* Loader needs to fix up some of the things compiler * couldn't get its hands on while emitting BTF. This @@ -2922,28 +2926,12 @@ static int btf_finalize_data(struct bpf_object *obj, struct btf *btf) * the info from the ELF itself for this purpose. */ if (btf_is_datasec(t)) { - err = btf_fixup_datasec(obj, btf, t); + err = btf_fixup_datasec(obj, obj->btf, t); if (err) - break; + return err; } } - return libbpf_err(err); -} - -static int bpf_object__finalize_btf(struct bpf_object *obj) -{ - int err; - - if (!obj->btf) - return 0; - - err = btf_finalize_data(obj, obj->btf); - if (err) { - pr_warn("Error finalizing %s: %d.\n", BTF_ELF_SEC, err); - return err; - } - return 0; } @@ -7233,7 +7221,7 @@ static struct bpf_object *bpf_object_open(const char *path, const void *obj_buf, err = err ? : bpf_object__check_endianness(obj); err = err ? : bpf_object__elf_collect(obj); err = err ? : bpf_object__collect_externs(obj); - err = err ? : bpf_object__finalize_btf(obj); + err = err ? : bpf_object_fixup_btf(obj); err = err ? : bpf_object__init_maps(obj, opts); err = err ? : bpf_object_init_progs(obj, opts); err = err ? : bpf_object__collect_relos(obj); From 4fcac46c7e107a93030d19c6ea7b90540fc80b1b Mon Sep 17 00:00:00 2001 From: Andrii Nakryiko Date: Tue, 18 Oct 2022 17:28:15 -0700 Subject: [PATCH 0211/2002] libbpf: only add BPF_F_MMAPABLE flag for data maps with global vars Teach libbpf to not add BPF_F_MMAPABLE flag unnecessarily for ARRAY maps that are backing data sections, if such data sections don't expose any variables to user-space. Exposed variables are those that have STB_GLOBAL or STB_WEAK ELF binding and correspond to BTF VAR's BTF_VAR_GLOBAL_ALLOCATED linkage. The overall idea is that if some data section doesn't have any variable that is exposed through BPF skeleton, then there is no reason to make such BPF array mmapable. Making BPF array mmapable is not a free no-op action, because BPF verifier doesn't allow users to put special objects (such as BPF spin locks, RB tree nodes, linked list nodes, kptrs, etc; anything that has a sensitive internal state that should not be modified arbitrarily from user space) into mmapable arrays, as there is no way to prevent user space from corrupting such sensitive state through direct memory access through memory-mapped region. By making sure that libbpf doesn't add BPF_F_MMAPABLE flag to BPF array maps corresponding to data sections that only have static variables (which are not supposed to be visible to user space according to libbpf and BPF skeleton rules), users now can have spinlocks, kptrs, etc in either default .bss/.data sections or custom .data.* sections (assuming there are no global variables in such sections). The only possible hiccup with this approach is the need to use global variables during BPF static linking, even if it's not intended to be shared with user space through BPF skeleton. To allow such scenarios, extend libbpf's STV_HIDDEN ELF visibility attribute handling to variables. Libbpf is already treating global hidden BPF subprograms as static subprograms and adjusts BTF accordingly to make BPF verifier verify such subprograms as static subprograms with preserving entire BPF verifier state between subprog calls. This patch teaches libbpf to treat global hidden variables as static ones and adjust BTF information accordingly as well. This allows to share variables between multiple object files during static linking, but still keep them internal to BPF program and not get them exposed through BPF skeleton. Note, that if the user has some advanced scenario where they absolutely need BPF_F_MMAPABLE flag on .data/.bss/.rodata BPF array map despite only having static variables, they still can achieve this by forcing it through explicit bpf_map__set_map_flags() API. Acked-by: Stanislav Fomichev Signed-off-by: Andrii Nakryiko Acked-by: Dave Marchevsky Link: https://lore.kernel.org/r/20221019002816.359650-3-andrii@kernel.org Signed-off-by: Alexei Starovoitov --- tools/lib/bpf/libbpf.c | 97 +++++++++++++++++++++++++++++++++--------- 1 file changed, 78 insertions(+), 19 deletions(-) diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c index 8802e06c5569..027fd9565c16 100644 --- a/tools/lib/bpf/libbpf.c +++ b/tools/lib/bpf/libbpf.c @@ -1577,7 +1577,38 @@ static char *internal_map_name(struct bpf_object *obj, const char *real_name) } static int -bpf_map_find_btf_info(struct bpf_object *obj, struct bpf_map *map); +map_fill_btf_type_info(struct bpf_object *obj, struct bpf_map *map); + +/* Internal BPF map is mmap()'able only if at least one of corresponding + * DATASEC's VARs are to be exposed through BPF skeleton. I.e., it's a GLOBAL + * variable and it's not marked as __hidden (which turns it into, effectively, + * a STATIC variable). + */ +static bool map_is_mmapable(struct bpf_object *obj, struct bpf_map *map) +{ + const struct btf_type *t, *vt; + struct btf_var_secinfo *vsi; + int i, n; + + if (!map->btf_value_type_id) + return false; + + t = btf__type_by_id(obj->btf, map->btf_value_type_id); + if (!btf_is_datasec(t)) + return false; + + vsi = btf_var_secinfos(t); + for (i = 0, n = btf_vlen(t); i < n; i++, vsi++) { + vt = btf__type_by_id(obj->btf, vsi->type); + if (!btf_is_var(vt)) + continue; + + if (btf_var(vt)->linkage != BTF_VAR_STATIC) + return true; + } + + return false; +} static int bpf_object__init_internal_map(struct bpf_object *obj, enum libbpf_map_type type, @@ -1609,7 +1640,12 @@ bpf_object__init_internal_map(struct bpf_object *obj, enum libbpf_map_type type, def->max_entries = 1; def->map_flags = type == LIBBPF_MAP_RODATA || type == LIBBPF_MAP_KCONFIG ? BPF_F_RDONLY_PROG : 0; - def->map_flags |= BPF_F_MMAPABLE; + + /* failures are fine because of maps like .rodata.str1.1 */ + (void) map_fill_btf_type_info(obj, map); + + if (map_is_mmapable(obj, map)) + def->map_flags |= BPF_F_MMAPABLE; pr_debug("map '%s' (global data): at sec_idx %d, offset %zu, flags %x.\n", map->name, map->sec_idx, map->sec_offset, def->map_flags); @@ -1626,9 +1662,6 @@ bpf_object__init_internal_map(struct bpf_object *obj, enum libbpf_map_type type, return err; } - /* failures are fine because of maps like .rodata.str1.1 */ - (void) bpf_map_find_btf_info(obj, map); - if (data) memcpy(map->mmaped, data, data_sz); @@ -2540,7 +2573,7 @@ static int bpf_object__init_user_btf_map(struct bpf_object *obj, fill_map_from_def(map->inner_map, &inner_def); } - err = bpf_map_find_btf_info(obj, map); + err = map_fill_btf_type_info(obj, map); if (err) return err; @@ -2848,6 +2881,7 @@ static int btf_fixup_datasec(struct bpf_object *obj, struct btf *btf, __u32 size = 0, i, vars = btf_vlen(t); const char *sec_name = btf__name_by_offset(btf, t->name_off); struct btf_var_secinfo *vsi; + bool fixup_offsets = false; int err; if (!sec_name) { @@ -2855,21 +2889,34 @@ static int btf_fixup_datasec(struct bpf_object *obj, struct btf *btf, return -ENOENT; } - /* extern-backing datasecs (.ksyms, .kconfig) have their size and - * variable offsets set at the previous step, so we skip any fixups - * for such sections + /* Extern-backing datasecs (.ksyms, .kconfig) have their size and + * variable offsets set at the previous step. Further, not every + * extern BTF VAR has corresponding ELF symbol preserved, so we skip + * all fixups altogether for such sections and go straight to sorting + * VARs within their DATASEC. */ - if (t->size) + if (strcmp(sec_name, KCONFIG_SEC) == 0 || strcmp(sec_name, KSYMS_SEC) == 0) goto sort_vars; - err = find_elf_sec_sz(obj, sec_name, &size); - if (err || !size) { - pr_debug("sec '%s': failed to determine size from ELF: size %u, err %d\n", - sec_name, size, err); - return -ENOENT; - } + /* Clang leaves DATASEC size and VAR offsets as zeroes, so we need to + * fix this up. But BPF static linker already fixes this up and fills + * all the sizes and offsets during static linking. So this step has + * to be optional. But the STV_HIDDEN handling is non-optional for any + * non-extern DATASEC, so the variable fixup loop below handles both + * functions at the same time, paying the cost of BTF VAR <-> ELF + * symbol matching just once. + */ + if (t->size == 0) { + err = find_elf_sec_sz(obj, sec_name, &size); + if (err || !size) { + pr_debug("sec '%s': failed to determine size from ELF: size %u, err %d\n", + sec_name, size, err); + return -ENOENT; + } - t->size = size; + t->size = size; + fixup_offsets = true; + } for (i = 0, vsi = btf_var_secinfos(t); i < vars; i++, vsi++) { const struct btf_type *t_var; @@ -2901,7 +2948,19 @@ static int btf_fixup_datasec(struct bpf_object *obj, struct btf *btf, return -ENOENT; } - vsi->offset = sym->st_value; + if (fixup_offsets) + vsi->offset = sym->st_value; + + /* if variable is a global/weak symbol, but has restricted + * (STV_HIDDEN or STV_INTERNAL) visibility, mark its BTF VAR + * as static. This follows similar logic for functions (BPF + * subprogs) and influences libbpf's further decisions about + * whether to make global data BPF array maps as + * BPF_F_MMAPABLE. + */ + if (ELF64_ST_VISIBILITY(sym->st_other) == STV_HIDDEN + || ELF64_ST_VISIBILITY(sym->st_other) == STV_INTERNAL) + var->linkage = BTF_VAR_STATIC; } sort_vars: @@ -4223,7 +4282,7 @@ bpf_object__collect_prog_relos(struct bpf_object *obj, Elf64_Shdr *shdr, Elf_Dat return 0; } -static int bpf_map_find_btf_info(struct bpf_object *obj, struct bpf_map *map) +static int map_fill_btf_type_info(struct bpf_object *obj, struct bpf_map *map) { int id; From 2f968e9f4a953037f798802006ecd298c014b5b4 Mon Sep 17 00:00:00 2001 From: Andrii Nakryiko Date: Tue, 18 Oct 2022 17:28:16 -0700 Subject: [PATCH 0212/2002] libbpf: add non-mmapable data section selftest Add non-mmapable data section to test_skeleton selftest and make sure it really isn't mmapable by trying to mmap() it anyways. Also make sure that libbpf doesn't report BPF_F_MMAPABLE flag to users. Additional, some more manual testing was performed that this feature works as intended. Looking at created map through bpftool shows that flags passed to kernel are indeed zero: $ bpftool map show ... 1782: array name .data.non_mmapa flags 0x0 key 4B value 16B max_entries 1 memlock 4096B btf_id 1169 pids test_progs(8311) ... Checking BTF uploaded to kernel for this map shows that zero_key and zero_value are indeed marked as static, even though zero_key is actually original global (but STV_HIDDEN) variable: $ bpftool btf dump id 1169 ... [51] VAR 'zero_key' type_id=2, linkage=static [52] VAR 'zero_value' type_id=7, linkage=static ... [62] DATASEC '.data.non_mmapable' size=16 vlen=2 type_id=51 offset=0 size=4 (VAR 'zero_key') type_id=52 offset=4 size=12 (VAR 'zero_value') ... And original BTF does have zero_key marked as linkage=global: $ bpftool btf dump file test_skeleton.bpf.linked3.o ... [51] VAR 'zero_key' type_id=2, linkage=global [52] VAR 'zero_value' type_id=7, linkage=static ... [62] DATASEC '.data.non_mmapable' size=16 vlen=2 type_id=51 offset=0 size=4 (VAR 'zero_key') type_id=52 offset=4 size=12 (VAR 'zero_value') Bpftool didn't require any changes at all because it checks whether internal map is mmapable already, but just to double-check generated skeleton, we see that .data.non_mmapable neither sets mmaped pointer nor has a corresponding field in the skeleton: $ grep non_mmapable test_skeleton.skel.h struct bpf_map *data_non_mmapable; s->maps[7].name = ".data.non_mmapable"; s->maps[7].map = &obj->maps.data_non_mmapable; But .data.read_mostly has all of those things: $ grep read_mostly test_skeleton.skel.h struct bpf_map *data_read_mostly; struct test_skeleton__data_read_mostly { int read_mostly_var; } *data_read_mostly; s->maps[6].name = ".data.read_mostly"; s->maps[6].map = &obj->maps.data_read_mostly; s->maps[6].mmaped = (void **)&obj->data_read_mostly; _Static_assert(sizeof(s->data_read_mostly->read_mostly_var) == 4, "unexpected size of 'read_mostly_var'"); Acked-by: Stanislav Fomichev Acked-by: Dave Marchevsky Signed-off-by: Andrii Nakryiko Link: https://lore.kernel.org/r/20221019002816.359650-4-andrii@kernel.org Signed-off-by: Alexei Starovoitov --- .../testing/selftests/bpf/prog_tests/skeleton.c | 11 ++++++++++- .../testing/selftests/bpf/progs/test_skeleton.c | 17 +++++++++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/tools/testing/selftests/bpf/prog_tests/skeleton.c b/tools/testing/selftests/bpf/prog_tests/skeleton.c index 99dac5292b41..bc6817aee9aa 100644 --- a/tools/testing/selftests/bpf/prog_tests/skeleton.c +++ b/tools/testing/selftests/bpf/prog_tests/skeleton.c @@ -2,6 +2,7 @@ /* Copyright (c) 2019 Facebook */ #include +#include struct s { int a; @@ -22,7 +23,8 @@ void test_skeleton(void) struct test_skeleton__kconfig *kcfg; const void *elf_bytes; size_t elf_bytes_sz = 0; - int i; + void *m; + int i, fd; skel = test_skeleton__open(); if (CHECK(!skel, "skel_open", "failed to open skeleton\n")) @@ -124,6 +126,13 @@ void test_skeleton(void) ASSERT_EQ(bss->huge_arr[ARRAY_SIZE(bss->huge_arr) - 1], 123, "huge_arr"); + fd = bpf_map__fd(skel->maps.data_non_mmapable); + m = mmap(NULL, getpagesize(), PROT_READ, MAP_SHARED, fd, 0); + if (!ASSERT_EQ(m, MAP_FAILED, "unexpected_mmap_success")) + munmap(m, getpagesize()); + + ASSERT_EQ(bpf_map__map_flags(skel->maps.data_non_mmapable), 0, "non_mmap_flags"); + elf_bytes = test_skeleton__elf_bytes(&elf_bytes_sz); ASSERT_OK_PTR(elf_bytes, "elf_bytes"); ASSERT_GE(elf_bytes_sz, 0, "elf_bytes_sz"); diff --git a/tools/testing/selftests/bpf/progs/test_skeleton.c b/tools/testing/selftests/bpf/progs/test_skeleton.c index 1a4e93f6d9df..adece9f91f58 100644 --- a/tools/testing/selftests/bpf/progs/test_skeleton.c +++ b/tools/testing/selftests/bpf/progs/test_skeleton.c @@ -53,6 +53,20 @@ int out_mostly_var; char huge_arr[16 * 1024 * 1024]; +/* non-mmapable custom .data section */ + +struct my_value { int x, y, z; }; + +__hidden int zero_key SEC(".data.non_mmapable"); +static struct my_value zero_value SEC(".data.non_mmapable"); + +struct { + __uint(type, BPF_MAP_TYPE_ARRAY); + __type(key, int); + __type(value, struct my_value); + __uint(max_entries, 1); +} my_map SEC(".maps"); + SEC("raw_tp/sys_enter") int handler(const void *ctx) { @@ -75,6 +89,9 @@ int handler(const void *ctx) huge_arr[sizeof(huge_arr) - 1] = 123; + /* make sure zero_key and zero_value are not optimized out */ + bpf_map_update_elem(&my_map, &zero_key, &zero_value, BPF_ANY); + return 0; } From 81b35e7cad790eecf9f359662804bb26055ac7e8 Mon Sep 17 00:00:00 2001 From: Jie Meng Date: Fri, 7 Oct 2022 13:23:47 -0700 Subject: [PATCH 0213/2002] bpf,x64: avoid unnecessary instructions when shift dest is ecx x64 JIT produces redundant instructions when a shift operation's destination register is BPF_REG_4/ecx and this patch removes them. Specifically, when dest reg is BPF_REG_4 but the src isn't, we needn't push and pop ecx around shift only to get it overwritten by r11 immediately afterwards. In the rare case when both dest and src registers are BPF_REG_4, a single shift instruction is sufficient and we don't need the two MOV instructions around the shift. To summarize using shift left as an example, without patch: ------------------------------------------------- | dst == ecx | dst != ecx ================================================= src == ecx | mov r11, ecx | shl dst, cl | shl r11, ecx | | mov ecx, r11 | ------------------------------------------------- src != ecx | mov r11, ecx | push ecx | push ecx | mov ecx, src | mov ecx, src | shl dst, cl | shl r11, cl | pop ecx | pop ecx | | mov ecx, r11 | ------------------------------------------------- With patch: ------------------------------------------------- | dst == ecx | dst != ecx ================================================= src == ecx | shl ecx, cl | shl dst, cl ------------------------------------------------- src != ecx | mov r11, ecx | push ecx | mov ecx, src | mov ecx, src | shl r11, cl | shl dst, cl | mov ecx, r11 | pop ecx ------------------------------------------------- Signed-off-by: Jie Meng Link: https://lore.kernel.org/r/20221007202348.1118830-2-jmeng@fb.com Signed-off-by: Alexei Starovoitov --- arch/x86/net/bpf_jit_comp.c | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/arch/x86/net/bpf_jit_comp.c b/arch/x86/net/bpf_jit_comp.c index 0abd082786e7..d926ca637d8d 100644 --- a/arch/x86/net/bpf_jit_comp.c +++ b/arch/x86/net/bpf_jit_comp.c @@ -1138,16 +1138,15 @@ static int do_jit(struct bpf_prog *bpf_prog, int *addrs, u8 *image, u8 *rw_image case BPF_ALU64 | BPF_RSH | BPF_X: case BPF_ALU64 | BPF_ARSH | BPF_X: - /* Check for bad case when dst_reg == rcx */ - if (dst_reg == BPF_REG_4) { - /* mov r11, dst_reg */ - EMIT_mov(AUX_REG, dst_reg); - dst_reg = AUX_REG; - } - if (src_reg != BPF_REG_4) { /* common case */ - EMIT1(0x51); /* push rcx */ - + /* Check for bad case when dst_reg == rcx */ + if (dst_reg == BPF_REG_4) { + /* mov r11, dst_reg */ + EMIT_mov(AUX_REG, dst_reg); + dst_reg = AUX_REG; + } else { + EMIT1(0x51); /* push rcx */ + } /* mov rcx, src_reg */ EMIT_mov(BPF_REG_4, src_reg); } @@ -1159,12 +1158,14 @@ static int do_jit(struct bpf_prog *bpf_prog, int *addrs, u8 *image, u8 *rw_image b3 = simple_alu_opcodes[BPF_OP(insn->code)]; EMIT2(0xD3, add_1reg(b3, dst_reg)); - if (src_reg != BPF_REG_4) - EMIT1(0x59); /* pop rcx */ + if (src_reg != BPF_REG_4) { + if (insn->dst_reg == BPF_REG_4) + /* mov dst_reg, r11 */ + EMIT_mov(insn->dst_reg, AUX_REG); + else + EMIT1(0x59); /* pop rcx */ + } - if (insn->dst_reg == BPF_REG_4) - /* mov dst_reg, r11 */ - EMIT_mov(insn->dst_reg, AUX_REG); break; case BPF_ALU | BPF_END | BPF_FROM_BE: From 77d8f5d47bfbb5f0a8630102838ffb22cd70d6f5 Mon Sep 17 00:00:00 2001 From: Jie Meng Date: Fri, 7 Oct 2022 13:23:48 -0700 Subject: [PATCH 0214/2002] bpf,x64: use shrx/sarx/shlx when available BMI2 provides 3 shift instructions (shrx, sarx and shlx) that use VEX encoding but target general purpose registers [1]. They allow the shift count in any general purpose register and have the same performance as non BMI2 shift instructions [2]. Instead of shr/sar/shl that implicitly use %cl (lowest 8 bit of %rcx), emit their more flexible alternatives provided in BMI2 when advantageous; keep using the non BMI2 instructions when shift count is already in BPF_REG_4/%rcx as non BMI2 instructions are shorter. To summarize, when BMI2 is available: ------------------------------------------------- | arbitrary dst ================================================= src == ecx | shl dst, cl ------------------------------------------------- src != ecx | shlx dst, dst, src ------------------------------------------------- And no additional register shuffling is needed. A concrete example between non BMI2 and BMI2 codegen. To shift %rsi by %rdi: Without BMI2: ef3: push %rcx 51 ef4: mov %rdi,%rcx 48 89 f9 ef7: shl %cl,%rsi 48 d3 e6 efa: pop %rcx 59 With BMI2: f0b: shlx %rdi,%rsi,%rsi c4 e2 c1 f7 f6 [1] https://en.wikipedia.org/wiki/X86_Bit_manipulation_instruction_set [2] https://www.agner.org/optimize/instruction_tables.pdf Signed-off-by: Jie Meng Link: https://lore.kernel.org/r/20221007202348.1118830-3-jmeng@fb.com Signed-off-by: Alexei Starovoitov --- arch/x86/net/bpf_jit_comp.c | 81 +++++++++++++++++++++++++++++++++++++ 1 file changed, 81 insertions(+) diff --git a/arch/x86/net/bpf_jit_comp.c b/arch/x86/net/bpf_jit_comp.c index d926ca637d8d..d7dd8e0db8da 100644 --- a/arch/x86/net/bpf_jit_comp.c +++ b/arch/x86/net/bpf_jit_comp.c @@ -891,6 +891,65 @@ static void emit_nops(u8 **pprog, int len) *pprog = prog; } +/* emit the 3-byte VEX prefix + * + * r: same as rex.r, extra bit for ModRM reg field + * x: same as rex.x, extra bit for SIB index field + * b: same as rex.b, extra bit for ModRM r/m, or SIB base + * m: opcode map select, encoding escape bytes e.g. 0x0f38 + * w: same as rex.w (32 bit or 64 bit) or opcode specific + * src_reg2: additional source reg (encoded as BPF reg) + * l: vector length (128 bit or 256 bit) or reserved + * pp: opcode prefix (none, 0x66, 0xf2 or 0xf3) + */ +static void emit_3vex(u8 **pprog, bool r, bool x, bool b, u8 m, + bool w, u8 src_reg2, bool l, u8 pp) +{ + u8 *prog = *pprog; + const u8 b0 = 0xc4; /* first byte of 3-byte VEX prefix */ + u8 b1, b2; + u8 vvvv = reg2hex[src_reg2]; + + /* reg2hex gives only the lower 3 bit of vvvv */ + if (is_ereg(src_reg2)) + vvvv |= 1 << 3; + + /* + * 2nd byte of 3-byte VEX prefix + * ~ means bit inverted encoding + * + * 7 0 + * +---+---+---+---+---+---+---+---+ + * |~R |~X |~B | m | + * +---+---+---+---+---+---+---+---+ + */ + b1 = (!r << 7) | (!x << 6) | (!b << 5) | (m & 0x1f); + /* + * 3rd byte of 3-byte VEX prefix + * + * 7 0 + * +---+---+---+---+---+---+---+---+ + * | W | ~vvvv | L | pp | + * +---+---+---+---+---+---+---+---+ + */ + b2 = (w << 7) | ((~vvvv & 0xf) << 3) | (l << 2) | (pp & 3); + + EMIT3(b0, b1, b2); + *pprog = prog; +} + +/* emit BMI2 shift instruction */ +static void emit_shiftx(u8 **pprog, u32 dst_reg, u8 src_reg, bool is64, u8 op) +{ + u8 *prog = *pprog; + bool r = is_ereg(dst_reg); + u8 m = 2; /* escape code 0f38 */ + + emit_3vex(&prog, r, false, r, m, is64, src_reg, false, op); + EMIT2(0xf7, add_2reg(0xC0, dst_reg, dst_reg)); + *pprog = prog; +} + #define INSN_SZ_DIFF (((addrs[i] - addrs[i - 1]) - (prog - temp))) static int do_jit(struct bpf_prog *bpf_prog, int *addrs, u8 *image, u8 *rw_image, @@ -1137,6 +1196,28 @@ static int do_jit(struct bpf_prog *bpf_prog, int *addrs, u8 *image, u8 *rw_image case BPF_ALU64 | BPF_LSH | BPF_X: case BPF_ALU64 | BPF_RSH | BPF_X: case BPF_ALU64 | BPF_ARSH | BPF_X: + /* BMI2 shifts aren't better when shift count is already in rcx */ + if (boot_cpu_has(X86_FEATURE_BMI2) && src_reg != BPF_REG_4) { + /* shrx/sarx/shlx dst_reg, dst_reg, src_reg */ + bool w = (BPF_CLASS(insn->code) == BPF_ALU64); + u8 op; + + switch (BPF_OP(insn->code)) { + case BPF_LSH: + op = 1; /* prefix 0x66 */ + break; + case BPF_RSH: + op = 3; /* prefix 0xf2 */ + break; + case BPF_ARSH: + op = 2; /* prefix 0xf3 */ + break; + } + + emit_shiftx(&prog, dst_reg, src_reg, w, op); + + break; + } if (src_reg != BPF_REG_4) { /* common case */ /* Check for bad case when dst_reg == rcx */ From 8662de2321496499a21841486660817f72ac9456 Mon Sep 17 00:00:00 2001 From: Jie Meng Date: Fri, 7 Oct 2022 13:23:49 -0700 Subject: [PATCH 0215/2002] bpf: add selftests for lsh, rsh, arsh with reg operand Current tests cover only shifts with an immediate as the source operand/shift counts; add a new test case to cover register operand. Signed-off-by: Jie Meng Link: https://lore.kernel.org/r/20221007202348.1118830-4-jmeng@fb.com Signed-off-by: Alexei Starovoitov --- tools/testing/selftests/bpf/verifier/jit.c | 24 ++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/tools/testing/selftests/bpf/verifier/jit.c b/tools/testing/selftests/bpf/verifier/jit.c index 79021c30e51e..8bf37e5207f1 100644 --- a/tools/testing/selftests/bpf/verifier/jit.c +++ b/tools/testing/selftests/bpf/verifier/jit.c @@ -20,6 +20,30 @@ .result = ACCEPT, .retval = 2, }, +{ + "jit: lsh, rsh, arsh by reg", + .insns = { + BPF_MOV64_IMM(BPF_REG_0, 1), + BPF_MOV64_IMM(BPF_REG_4, 1), + BPF_MOV64_IMM(BPF_REG_1, 0xff), + BPF_ALU64_REG(BPF_LSH, BPF_REG_1, BPF_REG_0), + BPF_ALU32_REG(BPF_LSH, BPF_REG_1, BPF_REG_4), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, 0x3fc, 1), + BPF_EXIT_INSN(), + BPF_ALU64_REG(BPF_RSH, BPF_REG_1, BPF_REG_4), + BPF_MOV64_REG(BPF_REG_4, BPF_REG_1), + BPF_ALU32_REG(BPF_RSH, BPF_REG_4, BPF_REG_0), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_4, 0xff, 1), + BPF_EXIT_INSN(), + BPF_ALU64_REG(BPF_ARSH, BPF_REG_4, BPF_REG_4), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_4, 0, 1), + BPF_EXIT_INSN(), + BPF_MOV64_IMM(BPF_REG_0, 2), + BPF_EXIT_INSN(), + }, + .result = ACCEPT, + .retval = 2, +}, { "jit: mov32 for ldimm64, 1", .insns = { From ab3f7828c9793a5dfa99a54dc19ae3491c38bfa3 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Tue, 18 Oct 2022 02:06:33 -0700 Subject: [PATCH 0216/2002] openvswitch: Use kmalloc_size_roundup() to match ksize() usage Round up allocations with kmalloc_size_roundup() so that openvswitch's use of ksize() is always accurate and no special handling of the memory is needed by KASAN, UBSAN_BOUNDS, nor FORTIFY_SOURCE. Cc: Pravin B Shelar Cc: dev@openvswitch.org Signed-off-by: Kees Cook Link: https://lore.kernel.org/r/20221018090628.never.537-kees@kernel.org Signed-off-by: Jakub Kicinski --- net/openvswitch/flow_netlink.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/openvswitch/flow_netlink.c b/net/openvswitch/flow_netlink.c index 4a07ab094a84..ead5418c126e 100644 --- a/net/openvswitch/flow_netlink.c +++ b/net/openvswitch/flow_netlink.c @@ -2309,7 +2309,7 @@ static struct sw_flow_actions *nla_alloc_flow_actions(int size) WARN_ON_ONCE(size > MAX_ACTIONS_BUFSIZE); - sfa = kmalloc(sizeof(*sfa) + size, GFP_KERNEL); + sfa = kmalloc(kmalloc_size_roundup(sizeof(*sfa) + size), GFP_KERNEL); if (!sfa) return ERR_PTR(-ENOMEM); From 36875a063b5e3618b42f7bace850473bb88a7c24 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Tue, 18 Oct 2022 02:28:27 -0700 Subject: [PATCH 0217/2002] net: ipa: Proactively round up to kmalloc bucket size Instead of discovering the kmalloc bucket size _after_ allocation, round up proactively so the allocation is explicitly made for the full size, allowing the compiler to correctly reason about the resulting size of the buffer through the existing __alloc_size() hint. Cc: "David S. Miller" Cc: Eric Dumazet Cc: Jakub Kicinski Cc: Paolo Abeni Cc: netdev@vger.kernel.org Reviewed-by: Alex Elder Link: https://lore.kernel.org/lkml/4d75a9fd-1b94-7208-9de8-5a0102223e68@ieee.org Signed-off-by: Kees Cook Link: https://lore.kernel.org/r/20221018092724.give.735-kees@kernel.org Signed-off-by: Paolo Abeni --- drivers/net/ipa/gsi_trans.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/net/ipa/gsi_trans.c b/drivers/net/ipa/gsi_trans.c index 26b7f683a3e1..0f52c068c46d 100644 --- a/drivers/net/ipa/gsi_trans.c +++ b/drivers/net/ipa/gsi_trans.c @@ -87,6 +87,7 @@ struct gsi_tre { int gsi_trans_pool_init(struct gsi_trans_pool *pool, size_t size, u32 count, u32 max_alloc) { + size_t alloc_size; void *virt; if (!size) @@ -103,13 +104,15 @@ int gsi_trans_pool_init(struct gsi_trans_pool *pool, size_t size, u32 count, * If there aren't enough entries starting at the free index, * we just allocate free entries from the beginning of the pool. */ - virt = kcalloc(count + max_alloc - 1, size, GFP_KERNEL); + alloc_size = size_mul(count + max_alloc - 1, size); + alloc_size = kmalloc_size_roundup(alloc_size); + virt = kzalloc(alloc_size, GFP_KERNEL); if (!virt) return -ENOMEM; pool->base = virt; /* If the allocator gave us any extra memory, use it */ - pool->count = ksize(pool->base) / size; + pool->count = alloc_size / size; pool->free = 0; pool->max_alloc = max_alloc; pool->size = size; From 6a8836e3c24a9f854d502b41584cf3f9765e950c Mon Sep 17 00:00:00 2001 From: Marc Kleine-Budde Date: Wed, 12 Oct 2022 09:38:42 +0200 Subject: [PATCH 0218/2002] can: m_can: is_lec_err(): clean up LEC error handling The PSR register contains among other the error for the CAN arbitration phase (LEC bits) and CAN data phase (DLEC bits). Prepare is_lec_err() to be called with the (D)LEC value only instead of the whole PSR register. While there rename LEC_UNUSED to LEC_NO_CHANGE to match the latest M_CAN reference manual. Link: https://lore.kernel.org/all/20221019211611.1605764-1-mkl@pengutronix.de Reviewed-by: Chandrasekar Ramakrishnan Signed-off-by: Marc Kleine-Budde --- drivers/net/can/m_can/m_can.c | 15 ++++++++------- drivers/net/can/m_can/m_can.h | 2 +- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/drivers/net/can/m_can/m_can.c b/drivers/net/can/m_can/m_can.c index dcb582563d5e..ebdd3c164d7b 100644 --- a/drivers/net/can/m_can/m_can.c +++ b/drivers/net/can/m_can/m_can.c @@ -816,11 +816,9 @@ static void m_can_handle_other_err(struct net_device *dev, u32 irqstatus) netdev_err(dev, "Message RAM access failure occurred\n"); } -static inline bool is_lec_err(u32 psr) +static inline bool is_lec_err(u8 lec) { - psr &= LEC_UNUSED; - - return psr && (psr != LEC_UNUSED); + return lec != LEC_NO_ERROR && lec != LEC_NO_CHANGE; } static inline bool m_can_is_protocol_err(u32 irqstatus) @@ -875,9 +873,12 @@ static int m_can_handle_bus_errors(struct net_device *dev, u32 irqstatus, work_done += m_can_handle_lost_msg(dev); /* handle lec errors on the bus */ - if ((cdev->can.ctrlmode & CAN_CTRLMODE_BERR_REPORTING) && - is_lec_err(psr)) - work_done += m_can_handle_lec_err(dev, psr & LEC_UNUSED); + if (cdev->can.ctrlmode & CAN_CTRLMODE_BERR_REPORTING) { + u8 lec = FIELD_GET(PSR_LEC_MASK, psr); + + if (is_lec_err(lec)) + work_done += m_can_handle_lec_err(dev, lec); + } /* handle protocol errors in arbitration phase */ if ((cdev->can.ctrlmode & CAN_CTRLMODE_BERR_REPORTING) && diff --git a/drivers/net/can/m_can/m_can.h b/drivers/net/can/m_can/m_can.h index 4c0267f9f297..52563c048732 100644 --- a/drivers/net/can/m_can/m_can.h +++ b/drivers/net/can/m_can/m_can.h @@ -38,7 +38,7 @@ enum m_can_lec_type { LEC_BIT1_ERROR, LEC_BIT0_ERROR, LEC_CRC_ERROR, - LEC_UNUSED, + LEC_NO_CHANGE, }; enum m_can_mram_cfg { From f5071d9e729dd3e563097949ba828ffa38629947 Mon Sep 17 00:00:00 2001 From: Vivek Yadav Date: Tue, 18 Oct 2022 10:03:33 +0530 Subject: [PATCH 0219/2002] can: m_can: m_can_handle_bus_errors(): add support for handling DLEC error on CAN-FD frames When a frame in CAN FD format has reached the data phase, the next CAN event (error or valid frame) will be shown in DLEC. Utilize the dedicated flag (Data Phase Last Error Code: DLEC flag) to determine the type of last error that occurred in the data phase of a CAN-FD frame and handle the bus errors. Signed-off-by: Vivek Yadav Link: https://lore.kernel.org/all/20221018081934.1336690-1-mkl@pengutronix.de Reviewed-by: Chandrasekar Ramakrishnan Signed-off-by: Marc Kleine-Budde --- drivers/net/can/m_can/m_can.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/drivers/net/can/m_can/m_can.c b/drivers/net/can/m_can/m_can.c index ebdd3c164d7b..34c76ee87c6e 100644 --- a/drivers/net/can/m_can/m_can.c +++ b/drivers/net/can/m_can/m_can.c @@ -156,6 +156,7 @@ enum m_can_reg { #define PSR_EW BIT(6) #define PSR_EP BIT(5) #define PSR_LEC_MASK GENMASK(2, 0) +#define PSR_DLEC_MASK GENMASK(10, 8) /* Interrupt Register (IR) */ #define IR_ALL_INT 0xffffffff @@ -875,9 +876,17 @@ static int m_can_handle_bus_errors(struct net_device *dev, u32 irqstatus, /* handle lec errors on the bus */ if (cdev->can.ctrlmode & CAN_CTRLMODE_BERR_REPORTING) { u8 lec = FIELD_GET(PSR_LEC_MASK, psr); + u8 dlec = FIELD_GET(PSR_DLEC_MASK, psr); - if (is_lec_err(lec)) + if (is_lec_err(lec)) { + netdev_dbg(dev, "Arbitration phase error detected\n"); work_done += m_can_handle_lec_err(dev, lec); + } + + if (is_lec_err(dlec)) { + netdev_dbg(dev, "Data phase error detected\n"); + work_done += m_can_handle_lec_err(dev, dlec); + } } /* handle protocol errors in arbitration phase */ From b1419cbebf5d7a1e236b6886750697eadefcb1d7 Mon Sep 17 00:00:00 2001 From: Marc Kleine-Budde Date: Sun, 18 Sep 2022 21:06:53 +0200 Subject: [PATCH 0220/2002] can: gs_usb: mention candleLight as supported device To make it easier for new users to find the correct driver for candleLight compatible CAN-USB devices mention candleLight in the driver's Kconfig input prompt. Link: https://lore.kernel.org/all/20221019205037.1600936-1-mkl@pengutronix.de Signed-off-by: Marc Kleine-Budde --- drivers/net/can/usb/Kconfig | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/net/can/usb/Kconfig b/drivers/net/can/usb/Kconfig index 1218f9642f33..8c6fea661530 100644 --- a/drivers/net/can/usb/Kconfig +++ b/drivers/net/can/usb/Kconfig @@ -38,10 +38,13 @@ config CAN_ETAS_ES58X will be called etas_es58x. config CAN_GS_USB - tristate "Geschwister Schneider UG interfaces" + tristate "Geschwister Schneider UG and candleLight compatible interfaces" help - This driver supports the Geschwister Schneider and bytewerk.org - candleLight USB CAN interfaces USB/CAN devices + This driver supports the Geschwister Schneider and + bytewerk.org candleLight compatible + (https://github.com/candle-usb/candleLight_fw) USB/CAN + interfaces. + If unsure choose N, choose Y for built in support, M to compile as module (module will be named: gs_usb). From acff76fa45b487a9dd7573a6f4d34daca6f0c045 Mon Sep 17 00:00:00 2001 From: Marc Kleine-Budde Date: Fri, 30 Sep 2022 15:59:41 +0200 Subject: [PATCH 0221/2002] can: gs_usb: gs_make_candev(): set netdev->dev_id The gs_usb driver supports USB devices with more than 1 CAN channel. Set the "netdev->dev_id" to distinguish between channels in user space. Link: https://lore.kernel.org/all/20221007075418.213403-1-mkl@pengutronix.de Signed-off-by: Marc Kleine-Budde --- drivers/net/can/usb/gs_usb.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/can/usb/gs_usb.c b/drivers/net/can/usb/gs_usb.c index f0065d40eb24..384847b4a4c9 100644 --- a/drivers/net/can/usb/gs_usb.c +++ b/drivers/net/can/usb/gs_usb.c @@ -1153,6 +1153,7 @@ static struct gs_can *gs_make_candev(unsigned int channel, netdev->ethtool_ops = &gs_usb_ethtool_ops; netdev->flags |= IFF_ECHO; /* we support full roundtrip echo */ + netdev->dev_id = channel; /* dev setup */ strcpy(dev->bt_const.name, KBUILD_MODNAME); From deb8534e8ef3905b5a977486d11d4d36b4f94347 Mon Sep 17 00:00:00 2001 From: Marc Kleine-Budde Date: Thu, 29 Sep 2022 09:58:30 +0200 Subject: [PATCH 0222/2002] can: gs_usb: gs_can_open(): allow loopback and listen only at the same time There's no reason why loopback and listen only should not be allowed at the same time. Replace the "else if" by "if" to reflect this in the code. Link: https://lore.kernel.org/all/20221019221016.1659260-2-mkl@pengutronix.de Signed-off-by: Marc Kleine-Budde --- drivers/net/can/usb/gs_usb.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/can/usb/gs_usb.c b/drivers/net/can/usb/gs_usb.c index 384847b4a4c9..aa6c84b7db06 100644 --- a/drivers/net/can/usb/gs_usb.c +++ b/drivers/net/can/usb/gs_usb.c @@ -911,7 +911,8 @@ static int gs_can_open(struct net_device *netdev) /* flags */ if (ctrlmode & CAN_CTRLMODE_LOOPBACK) flags |= GS_CAN_MODE_LOOP_BACK; - else if (ctrlmode & CAN_CTRLMODE_LISTENONLY) + + if (ctrlmode & CAN_CTRLMODE_LISTENONLY) flags |= GS_CAN_MODE_LISTEN_ONLY; /* Controller is not allowed to retry TX From f6adf410f70b6875ba6412e8909c742c3853201d Mon Sep 17 00:00:00 2001 From: Marc Kleine-Budde Date: Thu, 29 Sep 2022 10:01:22 +0200 Subject: [PATCH 0223/2002] can: gs_usb: gs_can_open(): sort checks for ctrlmode Sort the checks for dev->can.ctrlmode by values of CAN_CTRLMODE_*, so that it's clear where to add new checks. While there, remove the comment that the Atmel UC3C hardware doesn't support One Shot Mode. The One Shot mode is only available and to be activated by the user, if the device specifies the feature bit GS_CAN_FEATURE_ONE_SHOT. Link: https://lore.kernel.org/all/20221019221016.1659260-3-mkl@pengutronix.de Signed-off-by: Marc Kleine-Budde --- drivers/net/can/usb/gs_usb.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/drivers/net/can/usb/gs_usb.c b/drivers/net/can/usb/gs_usb.c index aa6c84b7db06..5dad0ebb3d3e 100644 --- a/drivers/net/can/usb/gs_usb.c +++ b/drivers/net/can/usb/gs_usb.c @@ -843,8 +843,6 @@ static int gs_can_open(struct net_device *netdev) ctrlmode = dev->can.ctrlmode; if (ctrlmode & CAN_CTRLMODE_FD) { - flags |= GS_CAN_MODE_FD; - if (dev->feature & GS_CAN_FEATURE_REQ_USB_QUIRK_LPC546XX) dev->hf_size_tx = struct_size(hf, canfd_quirk, 1); else @@ -915,14 +913,14 @@ static int gs_can_open(struct net_device *netdev) if (ctrlmode & CAN_CTRLMODE_LISTENONLY) flags |= GS_CAN_MODE_LISTEN_ONLY; - /* Controller is not allowed to retry TX - * this mode is unavailable on atmels uc3c hardware - */ + if (ctrlmode & CAN_CTRLMODE_3_SAMPLES) + flags |= GS_CAN_MODE_TRIPLE_SAMPLE; + if (ctrlmode & CAN_CTRLMODE_ONE_SHOT) flags |= GS_CAN_MODE_ONE_SHOT; - if (ctrlmode & CAN_CTRLMODE_3_SAMPLES) - flags |= GS_CAN_MODE_TRIPLE_SAMPLE; + if (ctrlmode & CAN_CTRLMODE_FD) + flags |= GS_CAN_MODE_FD; /* if hardware supports timestamps, enable it */ if (dev->feature & GS_CAN_FEATURE_HW_TIMESTAMP) From ac3f25824e4f9be1b36cc8f20572fa32b3079f94 Mon Sep 17 00:00:00 2001 From: Marc Kleine-Budde Date: Thu, 29 Sep 2022 10:02:06 +0200 Subject: [PATCH 0224/2002] can: gs_usb: gs_can_open(): merge setting of timestamp flags and init Merge the bodies of 2 consecutive "if (dev->feature & GS_CAN_FEATURE_HW_TIMESTAMP)" statements. Link: https://lore.kernel.org/all/20221019221016.1659260-4-mkl@pengutronix.de Signed-off-by: Marc Kleine-Budde --- drivers/net/can/usb/gs_usb.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/net/can/usb/gs_usb.c b/drivers/net/can/usb/gs_usb.c index 5dad0ebb3d3e..f9e2b394c71e 100644 --- a/drivers/net/can/usb/gs_usb.c +++ b/drivers/net/can/usb/gs_usb.c @@ -923,12 +923,12 @@ static int gs_can_open(struct net_device *netdev) flags |= GS_CAN_MODE_FD; /* if hardware supports timestamps, enable it */ - if (dev->feature & GS_CAN_FEATURE_HW_TIMESTAMP) + if (dev->feature & GS_CAN_FEATURE_HW_TIMESTAMP) { flags |= GS_CAN_MODE_HW_TIMESTAMP; - /* start polling timestamp */ - if (dev->feature & GS_CAN_FEATURE_HW_TIMESTAMP) + /* start polling timestamp */ gs_usb_timestamp_init(dev); + } /* finally start device */ dev->can.state = CAN_STATE_ERROR_ACTIVE; From 1f1835264d81da03fcd05646542a622678b379d6 Mon Sep 17 00:00:00 2001 From: Jeroen Hofstee Date: Sun, 28 Aug 2022 10:59:10 +0200 Subject: [PATCH 0225/2002] can: gs_usb: document GS_CAN_FEATURE_BERR_REPORTING Document the new feature ("GS_CAN_FEATURE_BERR_REPORTING") that indicates that the bus error reporting in the CAN controller can switched on and off with the GS_CAN_MODE_BERR_REPORTING mode bit in the GS_USB_BREQ_MODE control message. Signed-off-by: Jeroen Hofstee Link: https://lore.kernel.org/all/20221019221016.1659260-5-mkl@pengutronix.de Signed-off-by: Marc Kleine-Budde --- drivers/net/can/usb/gs_usb.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/net/can/usb/gs_usb.c b/drivers/net/can/usb/gs_usb.c index f9e2b394c71e..68e474a762c5 100644 --- a/drivers/net/can/usb/gs_usb.c +++ b/drivers/net/can/usb/gs_usb.c @@ -134,6 +134,7 @@ struct gs_device_config { /* GS_CAN_FEATURE_REQ_USB_QUIRK_LPC546XX BIT(9) */ /* GS_CAN_FEATURE_BT_CONST_EXT BIT(10) */ /* GS_CAN_FEATURE_TERMINATION BIT(11) */ +#define GS_CAN_MODE_BERR_REPORTING BIT(12) struct gs_device_mode { __le32 mode; @@ -174,7 +175,8 @@ struct gs_device_termination_state { #define GS_CAN_FEATURE_REQ_USB_QUIRK_LPC546XX BIT(9) #define GS_CAN_FEATURE_BT_CONST_EXT BIT(10) #define GS_CAN_FEATURE_TERMINATION BIT(11) -#define GS_CAN_FEATURE_MASK GENMASK(11, 0) +#define GS_CAN_FEATURE_BERR_REPORTING BIT(12) +#define GS_CAN_FEATURE_MASK GENMASK(12, 0) /* internal quirks - keep in GS_CAN_FEATURE space for now */ From 2f3cdad1c616891ab25ce10c1ac146403605b960 Mon Sep 17 00:00:00 2001 From: Jeroen Hofstee Date: Sun, 28 Aug 2022 10:59:10 +0200 Subject: [PATCH 0226/2002] can: gs_usb: add ability to enable / disable berr reporting The open source firmware candleLight report bus errors unconditionally. This adds support to enable / disable bus error reporting with the standard netlink property. Signed-off-by: Jeroen Hofstee Link: https://lore.kernel.org/all/20221019221016.1659260-6-mkl@pengutronix.de Signed-off-by: Marc Kleine-Budde --- drivers/net/can/usb/gs_usb.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/net/can/usb/gs_usb.c b/drivers/net/can/usb/gs_usb.c index 68e474a762c5..e2e23467caf2 100644 --- a/drivers/net/can/usb/gs_usb.c +++ b/drivers/net/can/usb/gs_usb.c @@ -921,6 +921,9 @@ static int gs_can_open(struct net_device *netdev) if (ctrlmode & CAN_CTRLMODE_ONE_SHOT) flags |= GS_CAN_MODE_ONE_SHOT; + if (ctrlmode & CAN_CTRLMODE_BERR_REPORTING) + flags |= GS_CAN_MODE_BERR_REPORTING; + if (ctrlmode & CAN_CTRLMODE_FD) flags |= GS_CAN_MODE_FD; @@ -1226,6 +1229,9 @@ static struct gs_can *gs_make_candev(unsigned int channel, } } + if (feature & GS_CAN_FEATURE_BERR_REPORTING) + dev->can.ctrlmode_supported |= CAN_CTRLMODE_BERR_REPORTING; + /* The CANtact Pro from LinkLayer Labs is based on the * LPC54616 µC, which is affected by the NXP LPC USB transfer * erratum. However, the current firmware (version 2) doesn't From 40e1997d4551e6053fc2f61959628da474775dc5 Mon Sep 17 00:00:00 2001 From: Jeroen Hofstee Date: Sun, 28 Aug 2022 12:25:02 +0200 Subject: [PATCH 0227/2002] can: gs_usb: document GS_CAN_FEATURE_GET_STATE Document the new feature ("GS_CAN_FEATURE_GET_STATE") that indicates that the state of the CAN controller can be queried with the new GS_USB_BREQ_GET_STATE control message. Signed-off-by: Jeroen Hofstee Link: https://lore.kernel.org/all/20221019221016.1659260-7-mkl@pengutronix.de Signed-off-by: Marc Kleine-Budde --- drivers/net/can/usb/gs_usb.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/net/can/usb/gs_usb.c b/drivers/net/can/usb/gs_usb.c index e2e23467caf2..3f00e8d79b75 100644 --- a/drivers/net/can/usb/gs_usb.c +++ b/drivers/net/can/usb/gs_usb.c @@ -66,6 +66,7 @@ enum gs_usb_breq { GS_USB_BREQ_BT_CONST_EXT, GS_USB_BREQ_SET_TERMINATION, GS_USB_BREQ_GET_TERMINATION, + GS_USB_BREQ_GET_STATE, }; enum gs_can_mode { @@ -135,6 +136,7 @@ struct gs_device_config { /* GS_CAN_FEATURE_BT_CONST_EXT BIT(10) */ /* GS_CAN_FEATURE_TERMINATION BIT(11) */ #define GS_CAN_MODE_BERR_REPORTING BIT(12) +/* GS_CAN_FEATURE_GET_STATE BIT(13) */ struct gs_device_mode { __le32 mode; @@ -176,7 +178,8 @@ struct gs_device_termination_state { #define GS_CAN_FEATURE_BT_CONST_EXT BIT(10) #define GS_CAN_FEATURE_TERMINATION BIT(11) #define GS_CAN_FEATURE_BERR_REPORTING BIT(12) -#define GS_CAN_FEATURE_MASK GENMASK(12, 0) +#define GS_CAN_FEATURE_GET_STATE BIT(13) +#define GS_CAN_FEATURE_MASK GENMASK(13, 0) /* internal quirks - keep in GS_CAN_FEATURE space for now */ From 0c9f92a4b795a68c005ebe215f31b2ad7838b9ea Mon Sep 17 00:00:00 2001 From: Jeroen Hofstee Date: Sun, 28 Aug 2022 12:25:02 +0200 Subject: [PATCH 0228/2002] can: gs_usb: add support for reading error counters Add support for reading the device state and CAN error counters, using the GS_USB_BREQ_GET_STATE control message, if supported by the device, indicated by the GS_CAN_FEATURE_GET_STATE feature flag. Signed-off-by: Jeroen Hofstee Link: https://lore.kernel.org/all/20221019221016.1659260-8-mkl@pengutronix.de Signed-off-by: Marc Kleine-Budde --- drivers/net/can/usb/gs_usb.c | 39 ++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/drivers/net/can/usb/gs_usb.c b/drivers/net/can/usb/gs_usb.c index 3f00e8d79b75..ccb1a29835a2 100644 --- a/drivers/net/can/usb/gs_usb.c +++ b/drivers/net/can/usb/gs_usb.c @@ -961,6 +961,42 @@ static int gs_can_open(struct net_device *netdev) return 0; } +static int gs_usb_get_state(const struct net_device *netdev, + struct can_berr_counter *bec, + enum can_state *state) +{ + struct gs_can *dev = netdev_priv(netdev); + struct gs_device_state ds; + int rc; + + rc = usb_control_msg_recv(interface_to_usbdev(dev->iface), 0, + GS_USB_BREQ_GET_STATE, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, + dev->channel, 0, + &ds, sizeof(ds), + USB_CTRL_GET_TIMEOUT, + GFP_KERNEL); + if (rc) + return rc; + + if (le32_to_cpu(ds.state) >= CAN_STATE_MAX) + return -EOPNOTSUPP; + + *state = le32_to_cpu(ds.state); + bec->txerr = le32_to_cpu(ds.txerr); + bec->rxerr = le32_to_cpu(ds.rxerr); + + return 0; +} + +static int gs_usb_can_get_berr_counter(const struct net_device *netdev, + struct can_berr_counter *bec) +{ + enum can_state state; + + return gs_usb_get_state(netdev, bec, &state); +} + static int gs_can_close(struct net_device *netdev) { int rc; @@ -1235,6 +1271,9 @@ static struct gs_can *gs_make_candev(unsigned int channel, if (feature & GS_CAN_FEATURE_BERR_REPORTING) dev->can.ctrlmode_supported |= CAN_CTRLMODE_BERR_REPORTING; + if (feature & GS_CAN_FEATURE_GET_STATE) + dev->can.do_get_berr_counter = gs_usb_can_get_berr_counter; + /* The CANtact Pro from LinkLayer Labs is based on the * LPC54616 µC, which is affected by the NXP LPC USB transfer * erratum. However, the current firmware (version 2) doesn't From fb73a20ebe15b16dee68e226ec82f8e55c4d64f9 Mon Sep 17 00:00:00 2001 From: Donald Hunter Date: Wed, 12 Oct 2022 16:27:15 +0100 Subject: [PATCH 0229/2002] bpf, docs: Reformat BPF maps page to be more readable Add a more complete introduction, with links to man pages. Move toctree of map types above usage notes. Format usage notes to improve readability. Signed-off-by: Donald Hunter Link: https://lore.kernel.org/r/20221012152715.25073-1-donald.hunter@gmail.com Signed-off-by: Alexei Starovoitov --- Documentation/bpf/maps.rst | 111 +++++++++++++++++++++++-------------- 1 file changed, 70 insertions(+), 41 deletions(-) diff --git a/Documentation/bpf/maps.rst b/Documentation/bpf/maps.rst index f41619e312ac..4906ff0f8382 100644 --- a/Documentation/bpf/maps.rst +++ b/Documentation/bpf/maps.rst @@ -1,46 +1,19 @@ -========= -eBPF maps -========= +======== +BPF maps +======== -'maps' is a generic storage of different types for sharing data between kernel -and userspace. +BPF 'maps' provide generic storage of different types for sharing data between +kernel and user space. There are several storage types available, including +hash, array, bloom filter and radix-tree. Several of the map types exist to +support specific BPF helpers that perform actions based on the map contents. The +maps are accessed from BPF programs via BPF helpers which are documented in the +`man-pages`_ for `bpf-helpers(7)`_. -The maps are accessed from user space via BPF syscall, which has commands: - -- create a map with given type and attributes - ``map_fd = bpf(BPF_MAP_CREATE, union bpf_attr *attr, u32 size)`` - using attr->map_type, attr->key_size, attr->value_size, attr->max_entries - returns process-local file descriptor or negative error - -- lookup key in a given map - ``err = bpf(BPF_MAP_LOOKUP_ELEM, union bpf_attr *attr, u32 size)`` - using attr->map_fd, attr->key, attr->value - returns zero and stores found elem into value or negative error - -- create or update key/value pair in a given map - ``err = bpf(BPF_MAP_UPDATE_ELEM, union bpf_attr *attr, u32 size)`` - using attr->map_fd, attr->key, attr->value - returns zero or negative error - -- find and delete element by key in a given map - ``err = bpf(BPF_MAP_DELETE_ELEM, union bpf_attr *attr, u32 size)`` - using attr->map_fd, attr->key - -- to delete map: close(fd) - Exiting process will delete maps automatically - -userspace programs use this syscall to create/access maps that eBPF programs -are concurrently updating. - -maps can have different types: hash, array, bloom filter, radix-tree, etc. - -The map is defined by: - - - type - - max number of elements - - key size in bytes - - value size in bytes +BPF maps are accessed from user space via the ``bpf`` syscall, which provides +commands to create maps, lookup elements, update elements and delete +elements. More details of the BPF syscall are available in +:doc:`/userspace-api/ebpf/syscall` and in the `man-pages`_ for `bpf(2)`_. Map Types ========= @@ -49,4 +22,60 @@ Map Types :maxdepth: 1 :glob: - map_* \ No newline at end of file + map_* + +Usage Notes +=========== + +.. c:function:: + int bpf(int command, union bpf_attr *attr, u32 size) + +Use the ``bpf()`` system call to perform the operation specified by +``command``. The operation takes parameters provided in ``attr``. The ``size`` +argument is the size of the ``union bpf_attr`` in ``attr``. + +**BPF_MAP_CREATE** + +Create a map with the desired type and attributes in ``attr``: + +.. code-block:: c + + int fd; + union bpf_attr attr = { + .map_type = BPF_MAP_TYPE_ARRAY; /* mandatory */ + .key_size = sizeof(__u32); /* mandatory */ + .value_size = sizeof(__u32); /* mandatory */ + .max_entries = 256; /* mandatory */ + .map_flags = BPF_F_MMAPABLE; + .map_name = "example_array"; + }; + + fd = bpf(BPF_MAP_CREATE, &attr, sizeof(attr)); + +Returns a process-local file descriptor on success, or negative error in case of +failure. The map can be deleted by calling ``close(fd)``. Maps held by open +file descriptors will be deleted automatically when a process exits. + +.. note:: Valid characters for ``map_name`` are ``A-Z``, ``a-z``, ``0-9``, + ``'_'`` and ``'.'``. + +**BPF_MAP_LOOKUP_ELEM** + +Lookup key in a given map using ``attr->map_fd``, ``attr->key``, +``attr->value``. Returns zero and stores found elem into ``attr->value`` on +success, or negative error on failure. + +**BPF_MAP_UPDATE_ELEM** + +Create or update key/value pair in a given map using ``attr->map_fd``, ``attr->key``, +``attr->value``. Returns zero on success or negative error on failure. + +**BPF_MAP_DELETE_ELEM** + +Find and delete element by key in a given map using ``attr->map_fd``, +``attr->key``. Returns zero on success or negative error on failure. + +.. Links: +.. _man-pages: https://www.kernel.org/doc/man-pages/ +.. _bpf(2): https://man7.org/linux/man-pages/man2/bpf.2.html +.. _bpf-helpers(7): https://man7.org/linux/man-pages/man7/bpf-helpers.7.html From 98af3746026c3eccb51b43181c41e001fe73f7af Mon Sep 17 00:00:00 2001 From: Wang Yufen Date: Thu, 20 Oct 2022 11:05:34 +0800 Subject: [PATCH 0230/2002] selftests/bpf: fix missing BPF object files After commit afef88e65554 ("selftests/bpf: Store BPF object files with .bpf.o extension"), we should use *.bpf.o instead of *.o. In addition, use the BPF_FILE variable to save the BPF object file name, which can be better identified and modified. Fixes: afef88e65554 ("selftests/bpf: Store BPF object files with .bpf.o extension") Signed-off-by: Wang Yufen Acked-by: Stanislav Fomichev Link: https://lore.kernel.org/r/1666235134-562-1-git-send-email-wangyufen@huawei.com Signed-off-by: Alexei Starovoitov --- .../selftests/bpf/test_bpftool_metadata.sh | 7 +++++-- .../selftests/bpf/test_flow_dissector.sh | 6 ++++-- .../testing/selftests/bpf/test_lwt_ip_encap.sh | 17 +++++++++-------- .../testing/selftests/bpf/test_lwt_seg6local.sh | 9 +++++---- tools/testing/selftests/bpf/test_tc_edt.sh | 3 ++- tools/testing/selftests/bpf/test_tc_tunnel.sh | 5 +++-- tools/testing/selftests/bpf/test_tunnel.sh | 5 +++-- tools/testing/selftests/bpf/test_xdp_meta.sh | 9 +++++---- tools/testing/selftests/bpf/test_xdp_vlan.sh | 8 ++++---- 9 files changed, 40 insertions(+), 29 deletions(-) diff --git a/tools/testing/selftests/bpf/test_bpftool_metadata.sh b/tools/testing/selftests/bpf/test_bpftool_metadata.sh index 1bf81b49457a..b5520692f41b 100755 --- a/tools/testing/selftests/bpf/test_bpftool_metadata.sh +++ b/tools/testing/selftests/bpf/test_bpftool_metadata.sh @@ -4,6 +4,9 @@ # Kselftest framework requirement - SKIP code is 4. ksft_skip=4 +BPF_FILE_USED="metadata_used.bpf.o" +BPF_FILE_UNUSED="metadata_unused.bpf.o" + TESTNAME=bpftool_metadata BPF_FS=$(awk '$3 == "bpf" {print $2; exit}' /proc/mounts) BPF_DIR=$BPF_FS/test_$TESTNAME @@ -55,7 +58,7 @@ mkdir $BPF_DIR trap cleanup EXIT -bpftool prog load metadata_unused.o $BPF_DIR/unused +bpftool prog load $BPF_FILE_UNUSED $BPF_DIR/unused METADATA_PLAIN="$(bpftool prog)" echo "$METADATA_PLAIN" | grep 'a = "foo"' > /dev/null @@ -67,7 +70,7 @@ bpftool map | grep 'metadata.rodata' > /dev/null rm $BPF_DIR/unused -bpftool prog load metadata_used.o $BPF_DIR/used +bpftool prog load $BPF_FILE_USED $BPF_DIR/used METADATA_PLAIN="$(bpftool prog)" echo "$METADATA_PLAIN" | grep 'a = "bar"' > /dev/null diff --git a/tools/testing/selftests/bpf/test_flow_dissector.sh b/tools/testing/selftests/bpf/test_flow_dissector.sh index 5303ce0c977b..4b298863797a 100755 --- a/tools/testing/selftests/bpf/test_flow_dissector.sh +++ b/tools/testing/selftests/bpf/test_flow_dissector.sh @@ -2,6 +2,8 @@ # SPDX-License-Identifier: GPL-2.0 # # Load BPF flow dissector and verify it correctly dissects traffic + +BPF_FILE="bpf_flow.bpf.o" export TESTNAME=test_flow_dissector unmount=0 @@ -22,7 +24,7 @@ if [[ -z $(ip netns identify $$) ]]; then if bpftool="$(which bpftool)"; then echo "Testing global flow dissector..." - $bpftool prog loadall ./bpf_flow.o /sys/fs/bpf/flow \ + $bpftool prog loadall $BPF_FILE /sys/fs/bpf/flow \ type flow_dissector if ! unshare --net $bpftool prog attach pinned \ @@ -95,7 +97,7 @@ else fi # Attach BPF program -./flow_dissector_load -p bpf_flow.o -s _dissect +./flow_dissector_load -p $BPF_FILE -s _dissect # Setup tc qdisc add dev lo ingress diff --git a/tools/testing/selftests/bpf/test_lwt_ip_encap.sh b/tools/testing/selftests/bpf/test_lwt_ip_encap.sh index 6c69c42b1d60..1e565f47aca9 100755 --- a/tools/testing/selftests/bpf/test_lwt_ip_encap.sh +++ b/tools/testing/selftests/bpf/test_lwt_ip_encap.sh @@ -38,6 +38,7 @@ # ping: SRC->[encap at veth2:ingress]->GRE:decap->DST # ping replies go DST->SRC directly +BPF_FILE="test_lwt_ip_encap.bpf.o" if [[ $EUID -ne 0 ]]; then echo "This script must be run as root" echo "FAIL" @@ -373,14 +374,14 @@ test_egress() # install replacement routes (LWT/eBPF), pings succeed if [ "${ENCAP}" == "IPv4" ] ; then ip -netns ${NS1} route add ${IPv4_DST} encap bpf xmit obj \ - test_lwt_ip_encap.o sec encap_gre dev veth1 ${VRF} + ${BPF_FILE} sec encap_gre dev veth1 ${VRF} ip -netns ${NS1} -6 route add ${IPv6_DST} encap bpf xmit obj \ - test_lwt_ip_encap.o sec encap_gre dev veth1 ${VRF} + ${BPF_FILE} sec encap_gre dev veth1 ${VRF} elif [ "${ENCAP}" == "IPv6" ] ; then ip -netns ${NS1} route add ${IPv4_DST} encap bpf xmit obj \ - test_lwt_ip_encap.o sec encap_gre6 dev veth1 ${VRF} + ${BPF_FILE} sec encap_gre6 dev veth1 ${VRF} ip -netns ${NS1} -6 route add ${IPv6_DST} encap bpf xmit obj \ - test_lwt_ip_encap.o sec encap_gre6 dev veth1 ${VRF} + ${BPF_FILE} sec encap_gre6 dev veth1 ${VRF} else echo " unknown encap ${ENCAP}" TEST_STATUS=1 @@ -431,14 +432,14 @@ test_ingress() # install replacement routes (LWT/eBPF), pings succeed if [ "${ENCAP}" == "IPv4" ] ; then ip -netns ${NS2} route add ${IPv4_DST} encap bpf in obj \ - test_lwt_ip_encap.o sec encap_gre dev veth2 ${VRF} + ${BPF_FILE} sec encap_gre dev veth2 ${VRF} ip -netns ${NS2} -6 route add ${IPv6_DST} encap bpf in obj \ - test_lwt_ip_encap.o sec encap_gre dev veth2 ${VRF} + ${BPF_FILE} sec encap_gre dev veth2 ${VRF} elif [ "${ENCAP}" == "IPv6" ] ; then ip -netns ${NS2} route add ${IPv4_DST} encap bpf in obj \ - test_lwt_ip_encap.o sec encap_gre6 dev veth2 ${VRF} + ${BPF_FILE} sec encap_gre6 dev veth2 ${VRF} ip -netns ${NS2} -6 route add ${IPv6_DST} encap bpf in obj \ - test_lwt_ip_encap.o sec encap_gre6 dev veth2 ${VRF} + ${BPF_FILE} sec encap_gre6 dev veth2 ${VRF} else echo "FAIL: unknown encap ${ENCAP}" TEST_STATUS=1 diff --git a/tools/testing/selftests/bpf/test_lwt_seg6local.sh b/tools/testing/selftests/bpf/test_lwt_seg6local.sh index 826f4423ce02..0efea2292d6a 100755 --- a/tools/testing/selftests/bpf/test_lwt_seg6local.sh +++ b/tools/testing/selftests/bpf/test_lwt_seg6local.sh @@ -23,6 +23,7 @@ # Kselftest framework requirement - SKIP code is 4. ksft_skip=4 +BPF_FILE="test_lwt_seg6local.bpf.o" readonly NS1="ns1-$(mktemp -u XXXXXX)" readonly NS2="ns2-$(mktemp -u XXXXXX)" readonly NS3="ns3-$(mktemp -u XXXXXX)" @@ -117,18 +118,18 @@ ip netns exec ${NS6} ip -6 addr add fb00::109/16 dev veth10 scope link ip netns exec ${NS1} ip -6 addr add fb00::1/16 dev lo ip netns exec ${NS1} ip -6 route add fb00::6 dev veth1 via fb00::21 -ip netns exec ${NS2} ip -6 route add fb00::6 encap bpf in obj test_lwt_seg6local.o sec encap_srh dev veth2 +ip netns exec ${NS2} ip -6 route add fb00::6 encap bpf in obj ${BPF_FILE} sec encap_srh dev veth2 ip netns exec ${NS2} ip -6 route add fd00::1 dev veth3 via fb00::43 scope link ip netns exec ${NS3} ip -6 route add fc42::1 dev veth5 via fb00::65 -ip netns exec ${NS3} ip -6 route add fd00::1 encap seg6local action End.BPF endpoint obj test_lwt_seg6local.o sec add_egr_x dev veth4 +ip netns exec ${NS3} ip -6 route add fd00::1 encap seg6local action End.BPF endpoint obj ${BPF_FILE} sec add_egr_x dev veth4 -ip netns exec ${NS4} ip -6 route add fd00::2 encap seg6local action End.BPF endpoint obj test_lwt_seg6local.o sec pop_egr dev veth6 +ip netns exec ${NS4} ip -6 route add fd00::2 encap seg6local action End.BPF endpoint obj ${BPF_FILE} sec pop_egr dev veth6 ip netns exec ${NS4} ip -6 addr add fc42::1 dev lo ip netns exec ${NS4} ip -6 route add fd00::3 dev veth7 via fb00::87 ip netns exec ${NS5} ip -6 route add fd00::4 table 117 dev veth9 via fb00::109 -ip netns exec ${NS5} ip -6 route add fd00::3 encap seg6local action End.BPF endpoint obj test_lwt_seg6local.o sec inspect_t dev veth8 +ip netns exec ${NS5} ip -6 route add fd00::3 encap seg6local action End.BPF endpoint obj ${BPF_FILE} sec inspect_t dev veth8 ip netns exec ${NS6} ip -6 addr add fb00::6/16 dev lo ip netns exec ${NS6} ip -6 addr add fd00::4/16 dev lo diff --git a/tools/testing/selftests/bpf/test_tc_edt.sh b/tools/testing/selftests/bpf/test_tc_edt.sh index daa7d1b8d309..76f0bd17061f 100755 --- a/tools/testing/selftests/bpf/test_tc_edt.sh +++ b/tools/testing/selftests/bpf/test_tc_edt.sh @@ -5,6 +5,7 @@ # with dst port = 9000 down to 5MBps. Then it measures actual # throughput of the flow. +BPF_FILE="test_tc_edt.bpf.o" if [[ $EUID -ne 0 ]]; then echo "This script must be run as root" echo "FAIL" @@ -54,7 +55,7 @@ ip -netns ${NS_DST} route add ${IP_SRC}/32 dev veth_dst ip netns exec ${NS_SRC} tc qdisc add dev veth_src root fq ip netns exec ${NS_SRC} tc qdisc add dev veth_src clsact ip netns exec ${NS_SRC} tc filter add dev veth_src egress \ - bpf da obj test_tc_edt.o sec cls_test + bpf da obj ${BPF_FILE} sec cls_test # start the listener diff --git a/tools/testing/selftests/bpf/test_tc_tunnel.sh b/tools/testing/selftests/bpf/test_tc_tunnel.sh index 088fcad138c9..334bdfeab940 100755 --- a/tools/testing/selftests/bpf/test_tc_tunnel.sh +++ b/tools/testing/selftests/bpf/test_tc_tunnel.sh @@ -3,6 +3,7 @@ # # In-place tunneling +BPF_FILE="test_tc_tunnel.bpf.o" # must match the port that the bpf program filters on readonly port=8000 @@ -196,7 +197,7 @@ verify_data # client can no longer connect ip netns exec "${ns1}" tc qdisc add dev veth1 clsact ip netns exec "${ns1}" tc filter add dev veth1 egress \ - bpf direct-action object-file ./test_tc_tunnel.o \ + bpf direct-action object-file ${BPF_FILE} \ section "encap_${tuntype}_${mac}" echo "test bpf encap without decap (expect failure)" server_listen @@ -296,7 +297,7 @@ fi ip netns exec "${ns2}" ip link del dev testtun0 ip netns exec "${ns2}" tc qdisc add dev veth2 clsact ip netns exec "${ns2}" tc filter add dev veth2 ingress \ - bpf direct-action object-file ./test_tc_tunnel.o section decap + bpf direct-action object-file ${BPF_FILE} section decap echo "test bpf encap with bpf decap" client_connect verify_data diff --git a/tools/testing/selftests/bpf/test_tunnel.sh b/tools/testing/selftests/bpf/test_tunnel.sh index e9ebc67d73f7..2eaedc1d9ed3 100755 --- a/tools/testing/selftests/bpf/test_tunnel.sh +++ b/tools/testing/selftests/bpf/test_tunnel.sh @@ -45,6 +45,7 @@ # 5) Tunnel protocol handler, ex: vxlan_rcv, decap the packet # 6) Forward the packet to the overlay tnl dev +BPF_FILE="test_tunnel_kern.bpf.o" BPF_PIN_TUNNEL_DIR="/sys/fs/bpf/tc/tunnel" PING_ARG="-c 3 -w 10 -q" ret=0 @@ -545,7 +546,7 @@ test_xfrm_tunnel() > /sys/kernel/debug/tracing/trace setup_xfrm_tunnel mkdir -p ${BPF_PIN_TUNNEL_DIR} - bpftool prog loadall ./test_tunnel_kern.o ${BPF_PIN_TUNNEL_DIR} + bpftool prog loadall ${BPF_FILE} ${BPF_PIN_TUNNEL_DIR} tc qdisc add dev veth1 clsact tc filter add dev veth1 proto ip ingress bpf da object-pinned \ ${BPF_PIN_TUNNEL_DIR}/xfrm_get_state @@ -572,7 +573,7 @@ attach_bpf() SET=$2 GET=$3 mkdir -p ${BPF_PIN_TUNNEL_DIR} - bpftool prog loadall ./test_tunnel_kern.o ${BPF_PIN_TUNNEL_DIR}/ + bpftool prog loadall ${BPF_FILE} ${BPF_PIN_TUNNEL_DIR}/ tc qdisc add dev $DEV clsact tc filter add dev $DEV egress bpf da object-pinned ${BPF_PIN_TUNNEL_DIR}/$SET tc filter add dev $DEV ingress bpf da object-pinned ${BPF_PIN_TUNNEL_DIR}/$GET diff --git a/tools/testing/selftests/bpf/test_xdp_meta.sh b/tools/testing/selftests/bpf/test_xdp_meta.sh index ea69370caae3..2740322c1878 100755 --- a/tools/testing/selftests/bpf/test_xdp_meta.sh +++ b/tools/testing/selftests/bpf/test_xdp_meta.sh @@ -1,5 +1,6 @@ #!/bin/sh +BPF_FILE="test_xdp_meta.bpf.o" # Kselftest framework requirement - SKIP code is 4. readonly KSFT_SKIP=4 readonly NS1="ns1-$(mktemp -u XXXXXX)" @@ -42,11 +43,11 @@ ip netns exec ${NS2} ip addr add 10.1.1.22/24 dev veth2 ip netns exec ${NS1} tc qdisc add dev veth1 clsact ip netns exec ${NS2} tc qdisc add dev veth2 clsact -ip netns exec ${NS1} tc filter add dev veth1 ingress bpf da obj test_xdp_meta.o sec t -ip netns exec ${NS2} tc filter add dev veth2 ingress bpf da obj test_xdp_meta.o sec t +ip netns exec ${NS1} tc filter add dev veth1 ingress bpf da obj ${BPF_FILE} sec t +ip netns exec ${NS2} tc filter add dev veth2 ingress bpf da obj ${BPF_FILE} sec t -ip netns exec ${NS1} ip link set dev veth1 xdp obj test_xdp_meta.o sec x -ip netns exec ${NS2} ip link set dev veth2 xdp obj test_xdp_meta.o sec x +ip netns exec ${NS1} ip link set dev veth1 xdp obj ${BPF_FILE} sec x +ip netns exec ${NS2} ip link set dev veth2 xdp obj ${BPF_FILE} sec x ip netns exec ${NS1} ip link set dev veth1 up ip netns exec ${NS2} ip link set dev veth2 up diff --git a/tools/testing/selftests/bpf/test_xdp_vlan.sh b/tools/testing/selftests/bpf/test_xdp_vlan.sh index 810c407e0286..fbcaa9f0120b 100755 --- a/tools/testing/selftests/bpf/test_xdp_vlan.sh +++ b/tools/testing/selftests/bpf/test_xdp_vlan.sh @@ -200,11 +200,11 @@ ip netns exec ${NS2} sh -c 'ping -W 1 -c 1 100.64.41.1 || echo "Success: First p # ---------------------------------------------------------------------- # In ns1: ingress use XDP to remove VLAN tags export DEVNS1=veth1 -export FILE=test_xdp_vlan.o +export BPF_FILE=test_xdp_vlan.bpf.o # First test: Remove VLAN by setting VLAN ID 0, using "xdp_vlan_change" export XDP_PROG=xdp_vlan_change -ip netns exec ${NS1} ip link set $DEVNS1 $XDP_MODE object $FILE section $XDP_PROG +ip netns exec ${NS1} ip link set $DEVNS1 $XDP_MODE object $BPF_FILE section $XDP_PROG # In ns1: egress use TC to add back VLAN tag 4011 # (del cmd) @@ -212,7 +212,7 @@ ip netns exec ${NS1} ip link set $DEVNS1 $XDP_MODE object $FILE section $XDP_PRO # ip netns exec ${NS1} tc qdisc add dev $DEVNS1 clsact ip netns exec ${NS1} tc filter add dev $DEVNS1 egress \ - prio 1 handle 1 bpf da obj $FILE sec tc_vlan_push + prio 1 handle 1 bpf da obj $BPF_FILE sec tc_vlan_push # Now the namespaces can reach each-other, test with ping: ip netns exec ${NS2} ping -i 0.2 -W 2 -c 2 $IPADDR1 @@ -226,7 +226,7 @@ ip netns exec ${NS1} ping -i 0.2 -W 2 -c 2 $IPADDR2 # export XDP_PROG=xdp_vlan_remove_outer2 ip netns exec ${NS1} ip link set $DEVNS1 $XDP_MODE off -ip netns exec ${NS1} ip link set $DEVNS1 $XDP_MODE object $FILE section $XDP_PROG +ip netns exec ${NS1} ip link set $DEVNS1 $XDP_MODE object $BPF_FILE section $XDP_PROG # Now the namespaces should still be able reach each-other, test with ping: ip netns exec ${NS2} ping -i 0.2 -W 2 -c 2 $IPADDR1 From 6fdfdef7fdb57e6b9f768c9ca0718dcb5e727a85 Mon Sep 17 00:00:00 2001 From: Alexey Kodanev Date: Wed, 19 Oct 2022 21:07:33 +0300 Subject: [PATCH 0231/2002] sctp: remove unnecessary NULL check in sctp_association_init() '&asoc->ulpq' passed to sctp_ulpq_init() as the first argument, then sctp_qlpq_init() initializes it and eventually returns the address of the struct member back. Therefore, in this case, the return pointer cannot be NULL. Moreover, it seems sctp_ulpq_init() has always been used only in sctp_association_init(), so there's really no need to return ulpq anymore. Detected using the static analysis tool - Svace. Signed-off-by: Alexey Kodanev Reviewed-by: Xin Long Link: https://lore.kernel.org/r/20221019180735.161388-1-aleksei.kodanev@bell-sw.com Signed-off-by: Jakub Kicinski --- include/net/sctp/ulpqueue.h | 3 +-- net/sctp/associola.c | 4 +--- net/sctp/ulpqueue.c | 5 +---- 3 files changed, 3 insertions(+), 9 deletions(-) diff --git a/include/net/sctp/ulpqueue.h b/include/net/sctp/ulpqueue.h index 0eaf8650e3b2..60f6641290c3 100644 --- a/include/net/sctp/ulpqueue.h +++ b/include/net/sctp/ulpqueue.h @@ -35,8 +35,7 @@ struct sctp_ulpq { }; /* Prototypes. */ -struct sctp_ulpq *sctp_ulpq_init(struct sctp_ulpq *, - struct sctp_association *); +void sctp_ulpq_init(struct sctp_ulpq *ulpq, struct sctp_association *asoc); void sctp_ulpq_flush(struct sctp_ulpq *ulpq); void sctp_ulpq_free(struct sctp_ulpq *); diff --git a/net/sctp/associola.c b/net/sctp/associola.c index 3460abceba44..63ba5551c13f 100644 --- a/net/sctp/associola.c +++ b/net/sctp/associola.c @@ -226,8 +226,7 @@ static struct sctp_association *sctp_association_init( /* Create an output queue. */ sctp_outq_init(asoc, &asoc->outqueue); - if (!sctp_ulpq_init(&asoc->ulpq, asoc)) - goto fail_init; + sctp_ulpq_init(&asoc->ulpq, asoc); if (sctp_stream_init(&asoc->stream, asoc->c.sinit_num_ostreams, 0, gfp)) goto stream_free; @@ -277,7 +276,6 @@ static struct sctp_association *sctp_association_init( stream_free: sctp_stream_free(&asoc->stream); -fail_init: sock_put(asoc->base.sk); sctp_endpoint_put(asoc->ep); return NULL; diff --git a/net/sctp/ulpqueue.c b/net/sctp/ulpqueue.c index 0a8510a0c5e6..24960dcb6a21 100644 --- a/net/sctp/ulpqueue.c +++ b/net/sctp/ulpqueue.c @@ -38,8 +38,7 @@ static void sctp_ulpq_reasm_drain(struct sctp_ulpq *ulpq); /* 1st Level Abstractions */ /* Initialize a ULP queue from a block of memory. */ -struct sctp_ulpq *sctp_ulpq_init(struct sctp_ulpq *ulpq, - struct sctp_association *asoc) +void sctp_ulpq_init(struct sctp_ulpq *ulpq, struct sctp_association *asoc) { memset(ulpq, 0, sizeof(struct sctp_ulpq)); @@ -48,8 +47,6 @@ struct sctp_ulpq *sctp_ulpq_init(struct sctp_ulpq *ulpq, skb_queue_head_init(&ulpq->reasm_uo); skb_queue_head_init(&ulpq->lobby); ulpq->pd_mode = 0; - - return ulpq; } From b66aeddbe30c26e56e33c65a74d073dc319beed5 Mon Sep 17 00:00:00 2001 From: Alexey Kodanev Date: Wed, 19 Oct 2022 21:07:34 +0300 Subject: [PATCH 0232/2002] sctp: remove unnecessary NULL check in sctp_ulpq_tail_event() After commit 013b96ec6461 ("sctp: Pass sk_buff_head explicitly to sctp_ulpq_tail_event().") there is one more unneeded check of skb_list for NULL. Detected using the static analysis tool - Svace. Signed-off-by: Alexey Kodanev Reviewed-by: Xin Long Link: https://lore.kernel.org/r/20221019180735.161388-2-aleksei.kodanev@bell-sw.com Signed-off-by: Jakub Kicinski --- net/sctp/ulpqueue.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/net/sctp/ulpqueue.c b/net/sctp/ulpqueue.c index 24960dcb6a21..b05daafd369a 100644 --- a/net/sctp/ulpqueue.c +++ b/net/sctp/ulpqueue.c @@ -256,10 +256,7 @@ int sctp_ulpq_tail_event(struct sctp_ulpq *ulpq, struct sk_buff_head *skb_list) return 1; out_free: - if (skb_list) - sctp_queue_purge_ulpevents(skb_list); - else - sctp_ulpevent_free(event); + sctp_queue_purge_ulpevents(skb_list); return 0; } From 377eb9aab084bf298eed9b55ba173dad36fdf7f2 Mon Sep 17 00:00:00 2001 From: Alexey Kodanev Date: Wed, 19 Oct 2022 21:07:35 +0300 Subject: [PATCH 0233/2002] sctp: remove unnecessary NULL checks in sctp_enqueue_event() After commit 178ca044aa60 ("sctp: Make sctp_enqueue_event tak an skb list."), skb_list cannot be NULL. Detected using the static analysis tool - Svace. Signed-off-by: Alexey Kodanev Reviewed-by: Xin Long Link: https://lore.kernel.org/r/20221019180735.161388-3-aleksei.kodanev@bell-sw.com Signed-off-by: Jakub Kicinski --- net/sctp/stream_interleave.c | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/net/sctp/stream_interleave.c b/net/sctp/stream_interleave.c index bb22b71df7a3..94727feb07b3 100644 --- a/net/sctp/stream_interleave.c +++ b/net/sctp/stream_interleave.c @@ -490,11 +490,8 @@ static int sctp_enqueue_event(struct sctp_ulpq *ulpq, if (!sctp_ulpevent_is_enabled(event, ulpq->asoc->subscribe)) goto out_free; - if (skb_list) - skb_queue_splice_tail_init(skb_list, - &sk->sk_receive_queue); - else - __skb_queue_tail(&sk->sk_receive_queue, skb); + skb_queue_splice_tail_init(skb_list, + &sk->sk_receive_queue); if (!sp->data_ready_signalled) { sp->data_ready_signalled = 1; @@ -504,10 +501,7 @@ static int sctp_enqueue_event(struct sctp_ulpq *ulpq, return 1; out_free: - if (skb_list) - sctp_queue_purge_ulpevents(skb_list); - else - sctp_ulpevent_free(event); + sctp_queue_purge_ulpevents(skb_list); return 0; } From 070f822d077faa4aaa86393c72340dafb3a55887 Mon Sep 17 00:00:00 2001 From: Doug Berger Date: Wed, 19 Oct 2022 14:51:23 -0700 Subject: [PATCH 0234/2002] net: bcmgenet: add RX_CLS_LOC_ANY support If a matching flow spec exists its current location is as good as ANY. If not add the new flow spec at the first available location. Signed-off-by: Doug Berger Acked-by: Florian Fainelli Link: https://lore.kernel.org/r/20221019215123.316997-1-opendmb@gmail.com Signed-off-by: Jakub Kicinski --- .../net/ethernet/broadcom/genet/bcmgenet.c | 31 ++++++++++++++++--- 1 file changed, 27 insertions(+), 4 deletions(-) diff --git a/drivers/net/ethernet/broadcom/genet/bcmgenet.c b/drivers/net/ethernet/broadcom/genet/bcmgenet.c index 25c450606985..a8ce8d0cf9c4 100644 --- a/drivers/net/ethernet/broadcom/genet/bcmgenet.c +++ b/drivers/net/ethernet/broadcom/genet/bcmgenet.c @@ -1387,7 +1387,8 @@ static int bcmgenet_validate_flow(struct net_device *dev, struct ethtool_usrip4_spec *l4_mask; struct ethhdr *eth_mask; - if (cmd->fs.location >= MAX_NUM_OF_FS_RULES) { + if (cmd->fs.location >= MAX_NUM_OF_FS_RULES && + cmd->fs.location != RX_CLS_LOC_ANY) { netdev_err(dev, "rxnfc: Invalid location (%d)\n", cmd->fs.location); return -EINVAL; @@ -1452,7 +1453,7 @@ static int bcmgenet_insert_flow(struct net_device *dev, { struct bcmgenet_priv *priv = netdev_priv(dev); struct bcmgenet_rxnfc_rule *loc_rule; - int err; + int err, i; if (priv->hw_params->hfb_filter_size < 128) { netdev_err(dev, "rxnfc: Not supported by this device\n"); @@ -1470,7 +1471,29 @@ static int bcmgenet_insert_flow(struct net_device *dev, if (err) return err; - loc_rule = &priv->rxnfc_rules[cmd->fs.location]; + if (cmd->fs.location == RX_CLS_LOC_ANY) { + list_for_each_entry(loc_rule, &priv->rxnfc_list, list) { + cmd->fs.location = loc_rule->fs.location; + err = memcmp(&loc_rule->fs, &cmd->fs, + sizeof(struct ethtool_rx_flow_spec)); + if (!err) + /* rule exists so return current location */ + return 0; + } + for (i = 0; i < MAX_NUM_OF_FS_RULES; i++) { + loc_rule = &priv->rxnfc_rules[i]; + if (loc_rule->state == BCMGENET_RXNFC_STATE_UNUSED) { + cmd->fs.location = i; + break; + } + } + if (i == MAX_NUM_OF_FS_RULES) { + cmd->fs.location = RX_CLS_LOC_ANY; + return -ENOSPC; + } + } else { + loc_rule = &priv->rxnfc_rules[cmd->fs.location]; + } if (loc_rule->state == BCMGENET_RXNFC_STATE_ENABLED) bcmgenet_hfb_disable_filter(priv, cmd->fs.location); if (loc_rule->state != BCMGENET_RXNFC_STATE_UNUSED) { @@ -1583,7 +1606,7 @@ static int bcmgenet_get_rxnfc(struct net_device *dev, struct ethtool_rxnfc *cmd, break; case ETHTOOL_GRXCLSRLCNT: cmd->rule_cnt = bcmgenet_get_num_flows(priv); - cmd->data = MAX_NUM_OF_FS_RULES; + cmd->data = MAX_NUM_OF_FS_RULES | RX_CLS_LOC_SPECIAL; break; case ETHTOOL_GRXCLSRULE: err = bcmgenet_get_flow(dev, cmd, cmd->fs.location); From c0facc045a144053ba2067894444d14e8fffeaa3 Mon Sep 17 00:00:00 2001 From: Jeff Johnson Date: Tue, 18 Oct 2022 14:17:18 -0700 Subject: [PATCH 0235/2002] net: ipa: Make QMI message rules const Commit ff6d365898d4 ("soc: qcom: qmi: use const for struct qmi_elem_info") allows QMI message encoding/decoding rules to be const, so do that for IPA. Signed-off-by: Jeff Johnson Reviewed-by: Alex Elder Reviewed-by: Sibi Sankar Signed-off-by: David S. Miller --- drivers/net/ipa/ipa_qmi_msg.c | 20 ++++++++++---------- drivers/net/ipa/ipa_qmi_msg.h | 20 ++++++++++---------- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/drivers/net/ipa/ipa_qmi_msg.c b/drivers/net/ipa/ipa_qmi_msg.c index 97c0befe8d86..894f99517233 100644 --- a/drivers/net/ipa/ipa_qmi_msg.c +++ b/drivers/net/ipa/ipa_qmi_msg.c @@ -9,7 +9,7 @@ #include "ipa_qmi_msg.h" /* QMI message structure definition for struct ipa_indication_register_req */ -struct qmi_elem_info ipa_indication_register_req_ei[] = { +const struct qmi_elem_info ipa_indication_register_req_ei[] = { { .data_type = QMI_OPT_FLAG, .elem_len = 1, @@ -116,7 +116,7 @@ struct qmi_elem_info ipa_indication_register_req_ei[] = { }; /* QMI message structure definition for struct ipa_indication_register_rsp */ -struct qmi_elem_info ipa_indication_register_rsp_ei[] = { +const struct qmi_elem_info ipa_indication_register_rsp_ei[] = { { .data_type = QMI_STRUCT, .elem_len = 1, @@ -134,7 +134,7 @@ struct qmi_elem_info ipa_indication_register_rsp_ei[] = { }; /* QMI message structure definition for struct ipa_driver_init_complete_req */ -struct qmi_elem_info ipa_driver_init_complete_req_ei[] = { +const struct qmi_elem_info ipa_driver_init_complete_req_ei[] = { { .data_type = QMI_UNSIGNED_1_BYTE, .elem_len = 1, @@ -151,7 +151,7 @@ struct qmi_elem_info ipa_driver_init_complete_req_ei[] = { }; /* QMI message structure definition for struct ipa_driver_init_complete_rsp */ -struct qmi_elem_info ipa_driver_init_complete_rsp_ei[] = { +const struct qmi_elem_info ipa_driver_init_complete_rsp_ei[] = { { .data_type = QMI_STRUCT, .elem_len = 1, @@ -169,7 +169,7 @@ struct qmi_elem_info ipa_driver_init_complete_rsp_ei[] = { }; /* QMI message structure definition for struct ipa_init_complete_ind */ -struct qmi_elem_info ipa_init_complete_ind_ei[] = { +const struct qmi_elem_info ipa_init_complete_ind_ei[] = { { .data_type = QMI_STRUCT, .elem_len = 1, @@ -187,7 +187,7 @@ struct qmi_elem_info ipa_init_complete_ind_ei[] = { }; /* QMI message structure definition for struct ipa_mem_bounds */ -struct qmi_elem_info ipa_mem_bounds_ei[] = { +const struct qmi_elem_info ipa_mem_bounds_ei[] = { { .data_type = QMI_UNSIGNED_4_BYTE, .elem_len = 1, @@ -208,7 +208,7 @@ struct qmi_elem_info ipa_mem_bounds_ei[] = { }; /* QMI message structure definition for struct ipa_mem_array */ -struct qmi_elem_info ipa_mem_array_ei[] = { +const struct qmi_elem_info ipa_mem_array_ei[] = { { .data_type = QMI_UNSIGNED_4_BYTE, .elem_len = 1, @@ -229,7 +229,7 @@ struct qmi_elem_info ipa_mem_array_ei[] = { }; /* QMI message structure definition for struct ipa_mem_range */ -struct qmi_elem_info ipa_mem_range_ei[] = { +const struct qmi_elem_info ipa_mem_range_ei[] = { { .data_type = QMI_UNSIGNED_4_BYTE, .elem_len = 1, @@ -250,7 +250,7 @@ struct qmi_elem_info ipa_mem_range_ei[] = { }; /* QMI message structure definition for struct ipa_init_modem_driver_req */ -struct qmi_elem_info ipa_init_modem_driver_req_ei[] = { +const struct qmi_elem_info ipa_init_modem_driver_req_ei[] = { { .data_type = QMI_OPT_FLAG, .elem_len = 1, @@ -645,7 +645,7 @@ struct qmi_elem_info ipa_init_modem_driver_req_ei[] = { }; /* QMI message structure definition for struct ipa_init_modem_driver_rsp */ -struct qmi_elem_info ipa_init_modem_driver_rsp_ei[] = { +const struct qmi_elem_info ipa_init_modem_driver_rsp_ei[] = { { .data_type = QMI_STRUCT, .elem_len = 1, diff --git a/drivers/net/ipa/ipa_qmi_msg.h b/drivers/net/ipa/ipa_qmi_msg.h index e29663965f43..b73503552c4d 100644 --- a/drivers/net/ipa/ipa_qmi_msg.h +++ b/drivers/net/ipa/ipa_qmi_msg.h @@ -247,15 +247,15 @@ struct ipa_init_modem_driver_rsp { }; /* Message structure definitions defined in "ipa_qmi_msg.c" */ -extern struct qmi_elem_info ipa_indication_register_req_ei[]; -extern struct qmi_elem_info ipa_indication_register_rsp_ei[]; -extern struct qmi_elem_info ipa_driver_init_complete_req_ei[]; -extern struct qmi_elem_info ipa_driver_init_complete_rsp_ei[]; -extern struct qmi_elem_info ipa_init_complete_ind_ei[]; -extern struct qmi_elem_info ipa_mem_bounds_ei[]; -extern struct qmi_elem_info ipa_mem_array_ei[]; -extern struct qmi_elem_info ipa_mem_range_ei[]; -extern struct qmi_elem_info ipa_init_modem_driver_req_ei[]; -extern struct qmi_elem_info ipa_init_modem_driver_rsp_ei[]; +extern const struct qmi_elem_info ipa_indication_register_req_ei[]; +extern const struct qmi_elem_info ipa_indication_register_rsp_ei[]; +extern const struct qmi_elem_info ipa_driver_init_complete_req_ei[]; +extern const struct qmi_elem_info ipa_driver_init_complete_rsp_ei[]; +extern const struct qmi_elem_info ipa_init_complete_ind_ei[]; +extern const struct qmi_elem_info ipa_mem_bounds_ei[]; +extern const struct qmi_elem_info ipa_mem_array_ei[]; +extern const struct qmi_elem_info ipa_mem_range_ei[]; +extern const struct qmi_elem_info ipa_init_modem_driver_req_ei[]; +extern const struct qmi_elem_info ipa_init_modem_driver_rsp_ei[]; #endif /* !_IPA_QMI_MSG_H_ */ From 36934cac7aaffaf11cb8fad1e828dd61708d9b89 Mon Sep 17 00:00:00 2001 From: Alexandru Tachici Date: Wed, 19 Oct 2022 20:13:13 +0300 Subject: [PATCH 0236/2002] net: ethernet: adi: adin1110: add reset GPIO Add an optional GPIO to be used for a hardware reset of the IC. Signed-off-by: Alexandru Tachici Signed-off-by: David S. Miller --- drivers/net/ethernet/adi/adin1110.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/drivers/net/ethernet/adi/adin1110.c b/drivers/net/ethernet/adi/adin1110.c index 086aa9c96b31..1c0015b55993 100644 --- a/drivers/net/ethernet/adi/adin1110.c +++ b/drivers/net/ethernet/adi/adin1110.c @@ -1082,9 +1082,30 @@ static void adin1110_adjust_link(struct net_device *dev) */ static int adin1110_check_spi(struct adin1110_priv *priv) { + struct gpio_desc *reset_gpio; int ret; u32 val; + reset_gpio = devm_gpiod_get_optional(&priv->spidev->dev, "reset", + GPIOD_OUT_LOW); + if (reset_gpio) { + /* MISO pin is used for internal configuration, can't have + * anyone else disturbing the SDO line. + */ + spi_bus_lock(priv->spidev->controller); + + gpiod_set_value(reset_gpio, 1); + fsleep(10000); + gpiod_set_value(reset_gpio, 0); + + /* Need to wait 90 ms before interacting with + * the MAC after a HW reset. + */ + fsleep(90000); + + spi_bus_unlock(priv->spidev->controller); + } + ret = adin1110_read_reg(priv, ADIN1110_PHY_ID, &val); if (ret < 0) return ret; From 3bd5549bd479d4451cf20be077fa7646f9ffc56f Mon Sep 17 00:00:00 2001 From: Alexandru Tachici Date: Wed, 19 Oct 2022 20:13:14 +0300 Subject: [PATCH 0237/2002] dt-bindings: net: adin1110: Document reset Document GPIO for HW reset. Signed-off-by: Alexandru Tachici Acked-by: Krzysztof Kozlowski Signed-off-by: David S. Miller --- Documentation/devicetree/bindings/net/adi,adin1110.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Documentation/devicetree/bindings/net/adi,adin1110.yaml b/Documentation/devicetree/bindings/net/adi,adin1110.yaml index b6bd8ee38a18..9de865295d7a 100644 --- a/Documentation/devicetree/bindings/net/adi,adin1110.yaml +++ b/Documentation/devicetree/bindings/net/adi,adin1110.yaml @@ -46,6 +46,10 @@ properties: interrupts: maxItems: 1 + reset-gpios: + maxItems: 1 + description: GPIO connected to active low reset + required: - compatible - reg From 80e5acb6dd72b25a6e6527443b9e9c1c3a7bcef6 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Thu, 20 Oct 2022 14:57:09 +0100 Subject: [PATCH 0238/2002] wifi: rtl8xxxu: Fix reads of uninitialized variables hw_ctrl_s1, sw_ctrl_s1 Variables hw_ctrl_s1 and sw_ctrl_s1 are not being initialized and potentially can contain any garbage value. Currently there is an if statement that sets one or the other of these variables, followed by an if statement that checks if any of these variables have been set to a non-zero value. In the case where they may contain uninitialized non-zero values, the latter if statement may be taken as true when it was not expected to. Fix this by ensuring hw_ctrl_s1 and sw_ctrl_s1 are initialized. Cleans up clang warning: drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8188f.c:432:7: warning: variable 'hw_ctrl_s1' is used uninitialized whenever 'if' condition is false [-Wsometimes-uninitialized] if (hw_ctrl) { ^~~~~~~ drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8188f.c:440:7: note: uninitialized use occurs here if (hw_ctrl_s1 || sw_ctrl_s1) { ^~~~~~~~~~ drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8188f.c:432:3: note: remove the 'if' if its condition is always true if (hw_ctrl) { ^~~~~~~~~~~~~ Fixes: c888183b21f3 ("wifi: rtl8xxxu: Support new chip RTL8188FU") Signed-off-by: Colin Ian King Reviewed-by: Nathan Chancellor Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20221020135709.1549086-1-colin.i.king@gmail.com --- drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8188f.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8188f.c b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8188f.c index 99610bb2afd5..0025bb32538d 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8188f.c +++ b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8188f.c @@ -412,7 +412,7 @@ static void rtl8188f_spur_calibration(struct rtl8xxxu_priv *priv, u8 channel) }; const u8 threshold = 0x16; - bool do_notch, hw_ctrl, sw_ctrl, hw_ctrl_s1, sw_ctrl_s1; + bool do_notch, hw_ctrl, sw_ctrl, hw_ctrl_s1 = 0, sw_ctrl_s1 = 0; u32 val32, initial_gain, reg948; val32 = rtl8xxxu_read32(priv, REG_OFDM0_RX_D_SYNC_PATH); From 19526e701ea096d13ebf881d32511e567dc41557 Mon Sep 17 00:00:00 2001 From: Wang Yufen Date: Fri, 14 Oct 2022 16:31:13 +0800 Subject: [PATCH 0239/2002] bpftool: Add autoattach for bpf prog load|loadall Add autoattach optional to support one-step load-attach-pin_link. For example, $ bpftool prog loadall test.o /sys/fs/bpf/test autoattach $ bpftool link 26: tracing name test1 tag f0da7d0058c00236 gpl loaded_at 2022-09-09T21:39:49+0800 uid 0 xlated 88B jited 55B memlock 4096B map_ids 3 btf_id 55 28: kprobe name test3 tag 002ef1bef0723833 gpl loaded_at 2022-09-09T21:39:49+0800 uid 0 xlated 88B jited 56B memlock 4096B map_ids 3 btf_id 55 57: tracepoint name oncpu tag 7aa55dfbdcb78941 gpl loaded_at 2022-09-09T21:41:32+0800 uid 0 xlated 456B jited 265B memlock 4096B map_ids 17,13,14,15 btf_id 82 $ bpftool link 1: tracing prog 26 prog_type tracing attach_type trace_fentry 3: perf_event prog 28 10: perf_event prog 57 The autoattach optional can support tracepoints, k(ret)probes, u(ret)probes. Signed-off-by: Wei Yongjun Signed-off-by: Wang Yufen Link: https://lore.kernel.org/r/1665736275-28143-2-git-send-email-wangyufen@huawei.com Signed-off-by: Alexei Starovoitov --- tools/bpf/bpftool/prog.c | 76 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 74 insertions(+), 2 deletions(-) diff --git a/tools/bpf/bpftool/prog.c b/tools/bpf/bpftool/prog.c index c81362a001ba..10ec29cb4598 100644 --- a/tools/bpf/bpftool/prog.c +++ b/tools/bpf/bpftool/prog.c @@ -1453,6 +1453,67 @@ get_prog_type_by_name(const char *name, enum bpf_prog_type *prog_type, return ret; } +static int +auto_attach_program(struct bpf_program *prog, const char *path) +{ + struct bpf_link *link; + int err; + + link = bpf_program__attach(prog); + if (!link) { + p_info("Program %s does not support autoattach, falling back to pinning", + bpf_program__name(prog)); + return bpf_obj_pin(bpf_program__fd(prog), path); + } + + err = bpf_link__pin(link, path); + bpf_link__destroy(link); + return err; +} + +static int pathname_concat(char *buf, size_t buf_sz, const char *path, const char *name) +{ + int len; + + len = snprintf(buf, buf_sz, "%s/%s", path, name); + if (len < 0) + return -EINVAL; + if ((size_t)len >= buf_sz) + return -ENAMETOOLONG; + + return 0; +} + +static int +auto_attach_programs(struct bpf_object *obj, const char *path) +{ + struct bpf_program *prog; + char buf[PATH_MAX]; + int err; + + bpf_object__for_each_program(prog, obj) { + err = pathname_concat(buf, sizeof(buf), path, bpf_program__name(prog)); + if (err) + goto err_unpin_programs; + + err = auto_attach_program(prog, buf); + if (err) + goto err_unpin_programs; + } + + return 0; + +err_unpin_programs: + while ((prog = bpf_object__prev_program(obj, prog))) { + if (pathname_concat(buf, sizeof(buf), path, bpf_program__name(prog))) + continue; + + bpf_program__unpin(prog, buf); + } + + return err; +} + static int load_with_options(int argc, char **argv, bool first_prog_only) { enum bpf_prog_type common_prog_type = BPF_PROG_TYPE_UNSPEC; @@ -1464,6 +1525,7 @@ static int load_with_options(int argc, char **argv, bool first_prog_only) struct bpf_program *prog = NULL, *pos; unsigned int old_map_fds = 0; const char *pinmaps = NULL; + bool auto_attach = false; struct bpf_object *obj; struct bpf_map *map; const char *pinfile; @@ -1583,6 +1645,9 @@ static int load_with_options(int argc, char **argv, bool first_prog_only) goto err_free_reuse_maps; pinmaps = GET_ARG(); + } else if (is_prefix(*argv, "autoattach")) { + auto_attach = true; + NEXT_ARG(); } else { p_err("expected no more arguments, 'type', 'map' or 'dev', got: '%s'?", *argv); @@ -1692,14 +1757,20 @@ static int load_with_options(int argc, char **argv, bool first_prog_only) goto err_close_obj; } - err = bpf_obj_pin(bpf_program__fd(prog), pinfile); + if (auto_attach) + err = auto_attach_program(prog, pinfile); + else + err = bpf_obj_pin(bpf_program__fd(prog), pinfile); if (err) { p_err("failed to pin program %s", bpf_program__section_name(prog)); goto err_close_obj; } } else { - err = bpf_object__pin_programs(obj, pinfile); + if (auto_attach) + err = auto_attach_programs(obj, pinfile); + else + err = bpf_object__pin_programs(obj, pinfile); if (err) { p_err("failed to pin all programs"); goto err_close_obj; @@ -2338,6 +2409,7 @@ static int do_help(int argc, char **argv) " [type TYPE] [dev NAME] \\\n" " [map { idx IDX | name NAME } MAP]\\\n" " [pinmaps MAP_DIR]\n" + " [autoattach]\n" " %1$s %2$s attach PROG ATTACH_TYPE [MAP]\n" " %1$s %2$s detach PROG ATTACH_TYPE [MAP]\n" " %1$s %2$s run PROG \\\n" From ff0e9a579ec9f12cb4fec9bd94b15e3d613a1a77 Mon Sep 17 00:00:00 2001 From: Wang Yufen Date: Fri, 14 Oct 2022 16:31:14 +0800 Subject: [PATCH 0240/2002] bpftool: Update doc (add autoattach to prog load) Add autoattach optional to prog load|loadall for supporting one-step load-attach-pin_link. Signed-off-by: Wang Yufen Link: https://lore.kernel.org/r/1665736275-28143-3-git-send-email-wangyufen@huawei.com Signed-off-by: Alexei Starovoitov --- tools/bpf/bpftool/Documentation/bpftool-prog.rst | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/tools/bpf/bpftool/Documentation/bpftool-prog.rst b/tools/bpf/bpftool/Documentation/bpftool-prog.rst index eb1b2a254eb1..14de72544995 100644 --- a/tools/bpf/bpftool/Documentation/bpftool-prog.rst +++ b/tools/bpf/bpftool/Documentation/bpftool-prog.rst @@ -31,7 +31,7 @@ PROG COMMANDS | **bpftool** **prog dump xlated** *PROG* [{**file** *FILE* | **opcodes** | **visual** | **linum**}] | **bpftool** **prog dump jited** *PROG* [{**file** *FILE* | **opcodes** | **linum**}] | **bpftool** **prog pin** *PROG* *FILE* -| **bpftool** **prog** { **load** | **loadall** } *OBJ* *PATH* [**type** *TYPE*] [**map** {**idx** *IDX* | **name** *NAME*} *MAP*] [**dev** *NAME*] [**pinmaps** *MAP_DIR*] +| **bpftool** **prog** { **load** | **loadall** } *OBJ* *PATH* [**type** *TYPE*] [**map** {**idx** *IDX* | **name** *NAME*} *MAP*] [**dev** *NAME*] [**pinmaps** *MAP_DIR*] [**autoattach**] | **bpftool** **prog attach** *PROG* *ATTACH_TYPE* [*MAP*] | **bpftool** **prog detach** *PROG* *ATTACH_TYPE* [*MAP*] | **bpftool** **prog tracelog** @@ -131,7 +131,7 @@ DESCRIPTION contain a dot character ('.'), which is reserved for future extensions of *bpffs*. - **bpftool prog { load | loadall }** *OBJ* *PATH* [**type** *TYPE*] [**map** {**idx** *IDX* | **name** *NAME*} *MAP*] [**dev** *NAME*] [**pinmaps** *MAP_DIR*] + **bpftool prog { load | loadall }** *OBJ* *PATH* [**type** *TYPE*] [**map** {**idx** *IDX* | **name** *NAME*} *MAP*] [**dev** *NAME*] [**pinmaps** *MAP_DIR*] [**autoattach**] Load bpf program(s) from binary *OBJ* and pin as *PATH*. **bpftool prog load** pins only the first program from the *OBJ* as *PATH*. **bpftool prog loadall** pins all programs @@ -150,6 +150,17 @@ DESCRIPTION Optional **pinmaps** argument can be provided to pin all maps under *MAP_DIR* directory. + If **autoattach** is specified program will be attached + before pin. In that case, only the link (representing the + program attached to its hook) is pinned, not the program as + such, so the path won't show in **bpftool prog show -f**, + only show in **bpftool link show -f**. Also, this only works + when bpftool (libbpf) is able to infer all necessary + information from the object file, in particular, it's not + supported for all program types. If a program does not + support autoattach, bpftool falls back to regular pinning + for that program instead. + Note: *PATH* must be located in *bpffs* mount. It must not contain a dot character ('.'), which is reserved for future extensions of *bpffs*. From b81a677400755cf24c971d1342c4c53d81744d82 Mon Sep 17 00:00:00 2001 From: Wang Yufen Date: Fri, 14 Oct 2022 16:31:15 +0800 Subject: [PATCH 0241/2002] bpftool: Update the bash completion(add autoattach to prog load) Add autoattach optional to prog load|loadall for supporting one-step load-attach-pin_link. Signed-off-by: Wang Yufen Link: https://lore.kernel.org/r/1665736275-28143-4-git-send-email-wangyufen@huawei.com Signed-off-by: Alexei Starovoitov --- tools/bpf/bpftool/bash-completion/bpftool | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/bpf/bpftool/bash-completion/bpftool b/tools/bpf/bpftool/bash-completion/bpftool index dc1641e3670e..2957b42cab67 100644 --- a/tools/bpf/bpftool/bash-completion/bpftool +++ b/tools/bpf/bpftool/bash-completion/bpftool @@ -505,6 +505,7 @@ _bpftool() _bpftool_once_attr 'type' _bpftool_once_attr 'dev' _bpftool_once_attr 'pinmaps' + _bpftool_once_attr 'autoattach' return 0 ;; esac From eb814cf1adea0ce24413c26c22e9f1a556a45d34 Mon Sep 17 00:00:00 2001 From: Delyan Kratunov Date: Fri, 21 Oct 2022 19:36:38 +0000 Subject: [PATCH 0242/2002] selftests/bpf: fix task_local_storage/exit_creds rcu usage BPF CI has revealed flakiness in the task_local_storage/exit_creds test. The failure point in CI [1] is that null_ptr_count is equal to 0, which indicates that the program hasn't run yet. This points to the kern_sync_rcu (sys_membarrier -> synchronize_rcu underneath) not waiting sufficiently. Indeed, synchronize_rcu only waits for read-side sections that started before the call. If the program execution starts *during* the synchronize_rcu invocation (due to, say, preemption), the test won't wait long enough. As a speculative fix, make the synchornize_rcu calls in a loop until an explicit run counter has gone up. [1]: https://github.com/kernel-patches/bpf/actions/runs/3268263235/jobs/5374940791 Signed-off-by: Delyan Kratunov Link: https://lore.kernel.org/r/156d4ef82275a074e8da8f4cffbd01b0c1466493.camel@meta.com Signed-off-by: Alexei Starovoitov --- .../bpf/prog_tests/task_local_storage.c | 18 +++++++++++++++--- .../bpf/progs/task_local_storage_exit_creds.c | 3 +++ 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/tools/testing/selftests/bpf/prog_tests/task_local_storage.c b/tools/testing/selftests/bpf/prog_tests/task_local_storage.c index 035c263aab1b..99a42a2b6e14 100644 --- a/tools/testing/selftests/bpf/prog_tests/task_local_storage.c +++ b/tools/testing/selftests/bpf/prog_tests/task_local_storage.c @@ -39,7 +39,8 @@ out: static void test_exit_creds(void) { struct task_local_storage_exit_creds *skel; - int err; + int err, run_count, sync_rcu_calls = 0; + const int MAX_SYNC_RCU_CALLS = 1000; skel = task_local_storage_exit_creds__open_and_load(); if (!ASSERT_OK_PTR(skel, "skel_open_and_load")) @@ -53,8 +54,19 @@ static void test_exit_creds(void) if (CHECK_FAIL(system("ls > /dev/null"))) goto out; - /* sync rcu to make sure exit_creds() is called for "ls" */ - kern_sync_rcu(); + /* kern_sync_rcu is not enough on its own as the read section we want + * to wait for may start after we enter synchronize_rcu, so our call + * won't wait for the section to finish. Loop on the run counter + * as well to ensure the program has run. + */ + do { + kern_sync_rcu(); + run_count = __atomic_load_n(&skel->bss->run_count, __ATOMIC_SEQ_CST); + } while (run_count == 0 && ++sync_rcu_calls < MAX_SYNC_RCU_CALLS); + + ASSERT_NEQ(sync_rcu_calls, MAX_SYNC_RCU_CALLS, + "sync_rcu count too high"); + ASSERT_NEQ(run_count, 0, "run_count"); ASSERT_EQ(skel->bss->valid_ptr_count, 0, "valid_ptr_count"); ASSERT_NEQ(skel->bss->null_ptr_count, 0, "null_ptr_count"); out: diff --git a/tools/testing/selftests/bpf/progs/task_local_storage_exit_creds.c b/tools/testing/selftests/bpf/progs/task_local_storage_exit_creds.c index 81758c0aef99..41d88ed222ff 100644 --- a/tools/testing/selftests/bpf/progs/task_local_storage_exit_creds.c +++ b/tools/testing/selftests/bpf/progs/task_local_storage_exit_creds.c @@ -14,6 +14,7 @@ struct { __type(value, __u64); } task_storage SEC(".maps"); +int run_count = 0; int valid_ptr_count = 0; int null_ptr_count = 0; @@ -28,5 +29,7 @@ int BPF_PROG(trace_exit_creds, struct task_struct *task) __sync_fetch_and_add(&valid_ptr_count, 1); else __sync_fetch_and_add(&null_ptr_count, 1); + + __sync_fetch_and_add(&run_count, 1); return 0; } From d9740535b857650bd6211a67ac0c0d574cba1dce Mon Sep 17 00:00:00 2001 From: Xu Kuohai Date: Tue, 18 Oct 2022 10:55:38 -0400 Subject: [PATCH 0243/2002] libbpf: Avoid allocating reg_name with sscanf in parse_usdt_arg() The reg_name in parse_usdt_arg() is used to hold register name, which is short enough to be held in a 16-byte array, so we could define reg_name as char reg_name[16] to avoid dynamically allocating reg_name with sscanf. Suggested-by: Andrii Nakryiko Signed-off-by: Xu Kuohai Signed-off-by: Andrii Nakryiko Acked-by: Stanislav Fomichev Link: https://lore.kernel.org/bpf/20221018145538.2046842-1-xukuohai@huaweicloud.com --- tools/lib/bpf/usdt.c | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/tools/lib/bpf/usdt.c b/tools/lib/bpf/usdt.c index 49f3c3b7f609..28fa1b2283de 100644 --- a/tools/lib/bpf/usdt.c +++ b/tools/lib/bpf/usdt.c @@ -1225,26 +1225,24 @@ static int calc_pt_regs_off(const char *reg_name) static int parse_usdt_arg(const char *arg_str, int arg_num, struct usdt_arg_spec *arg) { - char *reg_name = NULL; + char reg_name[16]; int arg_sz, len, reg_off; long off; - if (sscanf(arg_str, " %d @ %ld ( %%%m[^)] ) %n", &arg_sz, &off, ®_name, &len) == 3) { + if (sscanf(arg_str, " %d @ %ld ( %%%15[^)] ) %n", &arg_sz, &off, reg_name, &len) == 3) { /* Memory dereference case, e.g., -4@-20(%rbp) */ arg->arg_type = USDT_ARG_REG_DEREF; arg->val_off = off; reg_off = calc_pt_regs_off(reg_name); - free(reg_name); if (reg_off < 0) return reg_off; arg->reg_off = reg_off; - } else if (sscanf(arg_str, " %d @ %%%ms %n", &arg_sz, ®_name, &len) == 2) { + } else if (sscanf(arg_str, " %d @ %%%15s %n", &arg_sz, reg_name, &len) == 2) { /* Register read case, e.g., -4@%eax */ arg->arg_type = USDT_ARG_REG; arg->val_off = 0; reg_off = calc_pt_regs_off(reg_name); - free(reg_name); if (reg_off < 0) return reg_off; arg->reg_off = reg_off; @@ -1456,16 +1454,15 @@ static int calc_pt_regs_off(const char *reg_name) static int parse_usdt_arg(const char *arg_str, int arg_num, struct usdt_arg_spec *arg) { - char *reg_name = NULL; + char reg_name[16]; int arg_sz, len, reg_off; long off; - if (sscanf(arg_str, " %d @ %ld ( %m[a-z0-9] ) %n", &arg_sz, &off, ®_name, &len) == 3) { + if (sscanf(arg_str, " %d @ %ld ( %15[a-z0-9] ) %n", &arg_sz, &off, reg_name, &len) == 3) { /* Memory dereference case, e.g., -8@-88(s0) */ arg->arg_type = USDT_ARG_REG_DEREF; arg->val_off = off; reg_off = calc_pt_regs_off(reg_name); - free(reg_name); if (reg_off < 0) return reg_off; arg->reg_off = reg_off; @@ -1474,12 +1471,11 @@ static int parse_usdt_arg(const char *arg_str, int arg_num, struct usdt_arg_spec arg->arg_type = USDT_ARG_CONST; arg->val_off = off; arg->reg_off = 0; - } else if (sscanf(arg_str, " %d @ %m[a-z0-9] %n", &arg_sz, ®_name, &len) == 2) { + } else if (sscanf(arg_str, " %d @ %15[a-z0-9] %n", &arg_sz, reg_name, &len) == 2) { /* Register read case, e.g., -8@a1 */ arg->arg_type = USDT_ARG_REG; arg->val_off = 0; reg_off = calc_pt_regs_off(reg_name); - free(reg_name); if (reg_off < 0) return reg_off; arg->reg_off = reg_off; From 7e5eb725cf0a0a8f85e6b96e062bbd0d5d90c94e Mon Sep 17 00:00:00 2001 From: Quentin Monnet Date: Thu, 20 Oct 2022 11:03:00 +0100 Subject: [PATCH 0244/2002] bpftool: Set binary name to "bpftool" in help and version output MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commands "bpftool help" or "bpftool version" use argv[0] to display the name of the binary. While it is a convenient way to retrieve the string, it does not always produce the most readable output. For example, because of the way bpftool is currently packaged on Ubuntu (using a wrapper script), the command displays the absolute path for the binary: $ bpftool version | head -n 1 /usr/lib/linux-tools/5.15.0-50-generic/bpftool v5.15.60 More generally, there is no apparent reason for keeping the whole path and exact binary name in this output. If the user wants to understand what binary is being called, there are other ways to do so. This commit replaces argv[0] with "bpftool", to simply reflect what the tool is called. This is aligned on what "ip" or "tc" do, for example. As an additional benefit, this seems to help with integration with Meson for packaging [0]. [0] https://github.com/NixOS/nixpkgs/pull/195934 Suggested-by: Vladimír Čunát Signed-off-by: Quentin Monnet Signed-off-by: Andrii Nakryiko Link: https://lore.kernel.org/bpf/20221020100300.69328-1-quentin@isovalent.com --- tools/bpf/bpftool/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/bpf/bpftool/main.c b/tools/bpf/bpftool/main.c index ccd7457f92bf..8bf3615f684f 100644 --- a/tools/bpf/bpftool/main.c +++ b/tools/bpf/bpftool/main.c @@ -450,7 +450,7 @@ int main(int argc, char **argv) json_output = false; show_pinned = false; block_mount = false; - bin_name = argv[0]; + bin_name = "bpftool"; opterr = 0; while ((opt = getopt_long(argc, argv, "VhpjfLmndB:l", From 2c76238eaddd7c519f6b5a7ed80f7da6f4e11373 Mon Sep 17 00:00:00 2001 From: Quentin Monnet Date: Thu, 20 Oct 2022 11:03:32 +0100 Subject: [PATCH 0245/2002] bpftool: Add "bootstrap" feature to version output Along with the version number, "bpftool version" displays a list of features that were selected at compilation time for bpftool. It would be useful to indicate in that list whether a binary is a bootstrap version of bpftool. Given that an increasing number of components rely on bootstrap versions for generating skeletons, this could help understand what a binary is capable of if it has been copied outside of the usual "bootstrap" directory. To detect a bootstrap version, we simply rely on the absence of implementation for the do_prog() function. To do this, we must move the (unchanged) list of commands before do_version(), which in turn requires renaming this "cmds" array to avoid shadowing it with the "cmds" argument in cmd_select(). Signed-off-by: Quentin Monnet Signed-off-by: Daniel Borkmann Link: https://lore.kernel.org/bpf/20221020100332.69563-1-quentin@isovalent.com --- tools/bpf/bpftool/main.c | 81 ++++++++++++++++++++++++---------------- 1 file changed, 49 insertions(+), 32 deletions(-) diff --git a/tools/bpf/bpftool/main.c b/tools/bpf/bpftool/main.c index 8bf3615f684f..b22223df4431 100644 --- a/tools/bpf/bpftool/main.c +++ b/tools/bpf/bpftool/main.c @@ -71,6 +71,27 @@ static int do_help(int argc, char **argv) return 0; } +static int do_batch(int argc, char **argv); +static int do_version(int argc, char **argv); + +static const struct cmd commands[] = { + { "help", do_help }, + { "batch", do_batch }, + { "prog", do_prog }, + { "map", do_map }, + { "link", do_link }, + { "cgroup", do_cgroup }, + { "perf", do_perf }, + { "net", do_net }, + { "feature", do_feature }, + { "btf", do_btf }, + { "gen", do_gen }, + { "struct_ops", do_struct_ops }, + { "iter", do_iter }, + { "version", do_version }, + { 0 } +}; + #ifndef BPFTOOL_VERSION /* bpftool's major and minor version numbers are aligned on libbpf's. There is * an offset of 6 for the version number, because bpftool's version was higher @@ -82,6 +103,15 @@ static int do_help(int argc, char **argv) #define BPFTOOL_PATCH_VERSION 0 #endif +static void +print_feature(const char *feature, bool state, unsigned int *nb_features) +{ + if (state) { + printf("%s %s", *nb_features ? "," : "", feature); + *nb_features = *nb_features + 1; + } +} + static int do_version(int argc, char **argv) { #ifdef HAVE_LIBBFD_SUPPORT @@ -94,6 +124,18 @@ static int do_version(int argc, char **argv) #else const bool has_skeletons = true; #endif + bool bootstrap = false; + int i; + + for (i = 0; commands[i].cmd; i++) { + if (!strcmp(commands[i].cmd, "prog")) { + /* Assume we run a bootstrap version if "bpftool prog" + * is not available. + */ + bootstrap = !commands[i].func; + break; + } + } if (json_output) { jsonw_start_object(json_wtr); /* root object */ @@ -114,6 +156,7 @@ static int do_version(int argc, char **argv) jsonw_bool_field(json_wtr, "libbfd", has_libbfd); jsonw_bool_field(json_wtr, "libbpf_strict", !legacy_libbpf); jsonw_bool_field(json_wtr, "skeletons", has_skeletons); + jsonw_bool_field(json_wtr, "bootstrap", bootstrap); jsonw_end_object(json_wtr); /* features */ jsonw_end_object(json_wtr); /* root object */ @@ -128,16 +171,10 @@ static int do_version(int argc, char **argv) #endif printf("using libbpf %s\n", libbpf_version_string()); printf("features:"); - if (has_libbfd) { - printf(" libbfd"); - nb_features++; - } - if (!legacy_libbpf) { - printf("%s libbpf_strict", nb_features++ ? "," : ""); - nb_features++; - } - if (has_skeletons) - printf("%s skeletons", nb_features++ ? "," : ""); + print_feature("libbfd", has_libbfd, &nb_features); + print_feature("libbpf_strict", !legacy_libbpf, &nb_features); + print_feature("skeletons", has_skeletons, &nb_features); + print_feature("bootstrap", bootstrap, &nb_features); printf("\n"); } return 0; @@ -279,26 +316,6 @@ static int make_args(char *line, char *n_argv[], int maxargs, int cmd_nb) return n_argc; } -static int do_batch(int argc, char **argv); - -static const struct cmd cmds[] = { - { "help", do_help }, - { "batch", do_batch }, - { "prog", do_prog }, - { "map", do_map }, - { "link", do_link }, - { "cgroup", do_cgroup }, - { "perf", do_perf }, - { "net", do_net }, - { "feature", do_feature }, - { "btf", do_btf }, - { "gen", do_gen }, - { "struct_ops", do_struct_ops }, - { "iter", do_iter }, - { "version", do_version }, - { 0 } -}; - static int do_batch(int argc, char **argv) { char buf[BATCH_LINE_LEN_MAX], contline[BATCH_LINE_LEN_MAX]; @@ -386,7 +403,7 @@ static int do_batch(int argc, char **argv) jsonw_name(json_wtr, "output"); } - err = cmd_select(cmds, n_argc, n_argv, do_help); + err = cmd_select(commands, n_argc, n_argv, do_help); if (json_output) jsonw_end_object(json_wtr); @@ -528,7 +545,7 @@ int main(int argc, char **argv) if (version_requested) return do_version(argc, argv); - ret = cmd_select(cmds, argc, argv, do_help); + ret = cmd_select(commands, argc, argv, do_help); if (json_output) jsonw_destroy(&json_wtr); From 7a42af4b94f109f19b563fa7930715a3f298bf1b Mon Sep 17 00:00:00 2001 From: Manu Bretelle Date: Fri, 21 Oct 2022 14:06:58 -0700 Subject: [PATCH 0246/2002] selftests/bpf: Remove entries from config.s390x already present in config `config.s390x` had entries already present in `config`. When generating the config used by vmtest, we concatenate the `config` file with the `config.{arch}` one, making those entries duplicated. This patch removes that duplication. Before: $ comm -1 -2 <(sort tools/testing/selftests/bpf/config.s390x) <(sort tools/testing/selftests/bpf/config) CONFIG_MODULE_SIG=y CONFIG_MODULES=y CONFIG_MODULE_UNLOAD=y $ Ater: $ comm -1 -2 <(sort tools/testing/selftests/bpf/config.s390x) <(sort tools/testing/selftests/bpf/config) $ Signed-off-by: Manu Bretelle Signed-off-by: Andrii Nakryiko Link: https://lore.kernel.org/bpf/20221021210701.728135-2-chantr4@gmail.com --- tools/testing/selftests/bpf/config.s390x | 3 --- 1 file changed, 3 deletions(-) diff --git a/tools/testing/selftests/bpf/config.s390x b/tools/testing/selftests/bpf/config.s390x index f8a7a258a718..d49f6170e7bd 100644 --- a/tools/testing/selftests/bpf/config.s390x +++ b/tools/testing/selftests/bpf/config.s390x @@ -82,9 +82,6 @@ CONFIG_MARCH_Z196_TUNE=y CONFIG_MEMCG=y CONFIG_MEMORY_HOTPLUG=y CONFIG_MEMORY_HOTREMOVE=y -CONFIG_MODULE_SIG=y -CONFIG_MODULE_UNLOAD=y -CONFIG_MODULES=y CONFIG_NAMESPACES=y CONFIG_NET=y CONFIG_NET_9P=y From ec99451f0a488e50aaf0ce467db8771411edc407 Mon Sep 17 00:00:00 2001 From: Manu Bretelle Date: Fri, 21 Oct 2022 14:06:59 -0700 Subject: [PATCH 0247/2002] selftests/bpf: Add config.aarch64 config.aarch64, similarly to config.{s390x,x86_64} is a config enabling building a kernel on aarch64 to be used in bpf's selftests/kernel-patches CI. Signed-off-by: Manu Bretelle Signed-off-by: Andrii Nakryiko Link: https://lore.kernel.org/bpf/20221021210701.728135-3-chantr4@gmail.com --- tools/testing/selftests/bpf/config.aarch64 | 181 +++++++++++++++++++++ 1 file changed, 181 insertions(+) create mode 100644 tools/testing/selftests/bpf/config.aarch64 diff --git a/tools/testing/selftests/bpf/config.aarch64 b/tools/testing/selftests/bpf/config.aarch64 new file mode 100644 index 000000000000..1f0437644186 --- /dev/null +++ b/tools/testing/selftests/bpf/config.aarch64 @@ -0,0 +1,181 @@ +CONFIG_9P_FS=y +CONFIG_ARCH_VEXPRESS=y +CONFIG_ARCH_WANT_DEFAULT_BPF_JIT=y +CONFIG_ARM_SMMU_V3=y +CONFIG_ATA=y +CONFIG_AUDIT=y +CONFIG_BINFMT_MISC=y +CONFIG_BLK_CGROUP=y +CONFIG_BLK_DEV_BSGLIB=y +CONFIG_BLK_DEV_INITRD=y +CONFIG_BLK_DEV_IO_TRACE=y +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_SD=y +CONFIG_BONDING=y +CONFIG_BPFILTER=y +CONFIG_BPF_JIT_ALWAYS_ON=y +CONFIG_BPF_JIT_DEFAULT_ON=y +CONFIG_BPF_PRELOAD_UMD=y +CONFIG_BPF_PRELOAD=y +CONFIG_BRIDGE=m +CONFIG_CGROUP_CPUACCT=y +CONFIG_CGROUP_DEVICE=y +CONFIG_CGROUP_FREEZER=y +CONFIG_CGROUP_HUGETLB=y +CONFIG_CGROUP_NET_CLASSID=y +CONFIG_CGROUP_PERF=y +CONFIG_CGROUP_PIDS=y +CONFIG_CGROUP_SCHED=y +CONFIG_CGROUPS=y +CONFIG_CHECKPOINT_RESTORE=y +CONFIG_CHR_DEV_SG=y +CONFIG_COMPAT=y +CONFIG_CPUSETS=y +CONFIG_CRASH_DUMP=y +CONFIG_CRYPTO_USER_API_RNG=y +CONFIG_CRYPTO_USER_API_SKCIPHER=y +CONFIG_DEBUG_ATOMIC_SLEEP=y +CONFIG_DEBUG_INFO_BTF=y +CONFIG_DEBUG_INFO_DWARF4=y +CONFIG_DEBUG_LIST=y +CONFIG_DEBUG_LOCKDEP=y +CONFIG_DEBUG_NOTIFIERS=y +CONFIG_DEBUG_PAGEALLOC=y +CONFIG_DEBUG_SECTION_MISMATCH=y +CONFIG_DEBUG_SG=y +CONFIG_DETECT_HUNG_TASK=y +CONFIG_DEVTMPFS_MOUNT=y +CONFIG_DEVTMPFS=y +CONFIG_DRM_VIRTIO_GPU=y +CONFIG_DRM=y +CONFIG_DUMMY=y +CONFIG_EXPERT=y +CONFIG_EXT4_FS_POSIX_ACL=y +CONFIG_EXT4_FS_SECURITY=y +CONFIG_EXT4_FS=y +CONFIG_FANOTIFY=y +CONFIG_FB=y +CONFIG_FUNCTION_PROFILER=y +CONFIG_FUSE_FS=y +CONFIG_FW_CFG_SYSFS_CMDLINE=y +CONFIG_FW_CFG_SYSFS=y +CONFIG_GDB_SCRIPTS=y +CONFIG_HAVE_EBPF_JIT=y +CONFIG_HAVE_KPROBES_ON_FTRACE=y +CONFIG_HAVE_KPROBES=y +CONFIG_HAVE_KRETPROBES=y +CONFIG_HEADERS_INSTALL=y +CONFIG_HIGH_RES_TIMERS=y +CONFIG_HUGETLBFS=y +CONFIG_HW_RANDOM_VIRTIO=y +CONFIG_HW_RANDOM=y +CONFIG_HZ_100=y +CONFIG_IDLE_PAGE_TRACKING=y +CONFIG_IKHEADERS=y +CONFIG_INET6_ESP=y +CONFIG_INET_ESP=y +CONFIG_INET=y +CONFIG_INPUT_EVDEV=y +CONFIG_IP_ADVANCED_ROUTER=y +CONFIG_IP_MULTICAST=y +CONFIG_IP_MULTIPLE_TABLES=y +CONFIG_IP_NF_IPTABLES=y +CONFIG_IPV6_SEG6_LWTUNNEL=y +CONFIG_IPVLAN=y +CONFIG_JUMP_LABEL=y +CONFIG_KERNEL_UNCOMPRESSED=y +CONFIG_KPROBES_ON_FTRACE=y +CONFIG_KPROBES=y +CONFIG_KRETPROBES=y +CONFIG_KSM=y +CONFIG_LATENCYTOP=y +CONFIG_LIVEPATCH=y +CONFIG_LOCK_STAT=y +CONFIG_MACVLAN=y +CONFIG_MACVTAP=y +CONFIG_MAGIC_SYSRQ=y +CONFIG_MAILBOX=y +CONFIG_MEMCG=y +CONFIG_MEMORY_HOTPLUG=y +CONFIG_MEMORY_HOTREMOVE=y +CONFIG_NAMESPACES=y +CONFIG_NET_9P_VIRTIO=y +CONFIG_NET_9P=y +CONFIG_NET_ACT_BPF=y +CONFIG_NET_ACT_GACT=y +CONFIG_NETDEVICES=y +CONFIG_NETFILTER_XT_MATCH_BPF=y +CONFIG_NETFILTER_XT_TARGET_MARK=y +CONFIG_NET_KEY=y +CONFIG_NET_SCH_FQ=y +CONFIG_NET_VRF=y +CONFIG_NET=y +CONFIG_NF_TABLES=y +CONFIG_NLMON=y +CONFIG_NO_HZ_IDLE=y +CONFIG_NR_CPUS=256 +CONFIG_NUMA=y +CONFIG_OVERLAY_FS=y +CONFIG_PACKET_DIAG=y +CONFIG_PACKET=y +CONFIG_PANIC_ON_OOPS=y +CONFIG_PARTITION_ADVANCED=y +CONFIG_PCI_HOST_GENERIC=y +CONFIG_PCI=y +CONFIG_PL320_MBOX=y +CONFIG_POSIX_MQUEUE=y +CONFIG_PROC_KCORE=y +CONFIG_PROFILING=y +CONFIG_PROVE_LOCKING=y +CONFIG_PTDUMP_DEBUGFS=y +CONFIG_RC_DEVICES=y +CONFIG_RC_LOOPBACK=y +CONFIG_RTC_CLASS=y +CONFIG_RTC_DRV_PL031=y +CONFIG_RT_GROUP_SCHED=y +CONFIG_SAMPLE_SECCOMP=y +CONFIG_SAMPLES=y +CONFIG_SCHED_AUTOGROUP=y +CONFIG_SCHED_TRACER=y +CONFIG_SCSI_CONSTANTS=y +CONFIG_SCSI_LOGGING=y +CONFIG_SCSI_SCAN_ASYNC=y +CONFIG_SCSI_VIRTIO=y +CONFIG_SCSI=y +CONFIG_SECURITY_NETWORK=y +CONFIG_SERIAL_AMBA_PL011_CONSOLE=y +CONFIG_SERIAL_AMBA_PL011=y +CONFIG_STACK_TRACER=y +CONFIG_STATIC_KEYS_SELFTEST=y +CONFIG_SYSVIPC=y +CONFIG_TASK_DELAY_ACCT=y +CONFIG_TASK_IO_ACCOUNTING=y +CONFIG_TASKSTATS=y +CONFIG_TASK_XACCT=y +CONFIG_TCG_TIS=y +CONFIG_TCG_TPM=y +CONFIG_TCP_CONG_ADVANCED=y +CONFIG_TCP_CONG_DCTCP=y +CONFIG_TLS=y +CONFIG_TMPFS_POSIX_ACL=y +CONFIG_TMPFS=y +CONFIG_TRACER_SNAPSHOT_PER_CPU_SWAP=y +CONFIG_TRANSPARENT_HUGEPAGE=y +CONFIG_TUN=y +CONFIG_UNIX=y +CONFIG_UPROBES=y +CONFIG_USELIB=y +CONFIG_USER_NS=y +CONFIG_VETH=y +CONFIG_VIRTIO_BALLOON=y +CONFIG_VIRTIO_BLK=y +CONFIG_VIRTIO_CONSOLE=y +CONFIG_VIRTIO_FS=y +CONFIG_VIRTIO_INPUT=y +CONFIG_VIRTIO_MMIO_CMDLINE_DEVICES=y +CONFIG_VIRTIO_MMIO=y +CONFIG_VIRTIO_NET=y +CONFIG_VIRTIO_PCI=y +CONFIG_VLAN_8021Q=y +CONFIG_VSOCKETS=y +CONFIG_XFRM_USER=y From 20776b72ae2a43311f82f48f7b78f484cc89e463 Mon Sep 17 00:00:00 2001 From: Manu Bretelle Date: Fri, 21 Oct 2022 14:07:00 -0700 Subject: [PATCH 0248/2002] selftests/bpf: Update vmtests.sh to support aarch64 Add handling of aarch64 when setting QEMU options and provide the right path to aarch64 kernel image. Signed-off-by: Manu Bretelle Signed-off-by: Andrii Nakryiko Link: https://lore.kernel.org/bpf/20221021210701.728135-4-chantr4@gmail.com --- tools/testing/selftests/bpf/vmtest.sh | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tools/testing/selftests/bpf/vmtest.sh b/tools/testing/selftests/bpf/vmtest.sh index a29aa05ebb3e..316a56d680f2 100755 --- a/tools/testing/selftests/bpf/vmtest.sh +++ b/tools/testing/selftests/bpf/vmtest.sh @@ -21,6 +21,12 @@ x86_64) QEMU_FLAGS=(-cpu host -smp 8) BZIMAGE="arch/x86/boot/bzImage" ;; +aarch64) + QEMU_BINARY=qemu-system-aarch64 + QEMU_CONSOLE="ttyAMA0,115200" + QEMU_FLAGS=(-M virt,gic-version=3 -cpu host -smp 8) + BZIMAGE="arch/arm64/boot/Image" + ;; *) echo "Unsupported architecture" exit 1 From 94d52a19180726ee8ddc70bea75d6605e1dd6029 Mon Sep 17 00:00:00 2001 From: Manu Bretelle Date: Fri, 21 Oct 2022 14:07:01 -0700 Subject: [PATCH 0249/2002] selftests/bpf: Initial DENYLIST for aarch64 Those tests are currently failing on aarch64, ignore them until they are individually addressed. Using this deny list, vmtest.sh ran successfully using LLVM_STRIP=llvm-strip-16 CLANG=clang-16 \ tools/testing/selftests/bpf/vmtest.sh -- \ ./test_progs -d \ \"$(cat tools/testing/selftests/bpf/DENYLIST{,.aarch64} \ | cut -d'#' -f1 \ | sed -e 's/^[[:space:]]*//' \ -e 's/[[:space:]]*$//' \ | tr -s '\n' ','\ )\" Signed-off-by: Manu Bretelle Signed-off-by: Andrii Nakryiko Link: https://lore.kernel.org/bpf/20221021210701.728135-5-chantr4@gmail.com --- tools/testing/selftests/bpf/DENYLIST.aarch64 | 81 ++++++++++++++++++++ 1 file changed, 81 insertions(+) create mode 100644 tools/testing/selftests/bpf/DENYLIST.aarch64 diff --git a/tools/testing/selftests/bpf/DENYLIST.aarch64 b/tools/testing/selftests/bpf/DENYLIST.aarch64 new file mode 100644 index 000000000000..09416d5d2e33 --- /dev/null +++ b/tools/testing/selftests/bpf/DENYLIST.aarch64 @@ -0,0 +1,81 @@ +bloom_filter_map # libbpf: prog 'check_bloom': failed to attach: ERROR: strerror_r(-524)=22 +bpf_cookie/lsm +bpf_cookie/multi_kprobe_attach_api +bpf_cookie/multi_kprobe_link_api +bpf_cookie/trampoline +bpf_loop/check_callback_fn_stop # link unexpected error: -524 +bpf_loop/check_invalid_flags +bpf_loop/check_nested_calls +bpf_loop/check_non_constant_callback +bpf_loop/check_nr_loops +bpf_loop/check_null_callback_ctx +bpf_loop/check_stack +bpf_mod_race # bpf_mod_kfunc_race__attach unexpected error: -524 (errno 524) +bpf_tcp_ca/dctcp_fallback +btf_dump/btf_dump: var_data # find type id unexpected find type id: actual -2 < expected 0 +cgroup_hierarchical_stats # attach unexpected error: -524 (errno 524) +d_path/basic # setup attach failed: -524 +deny_namespace # attach unexpected error: -524 (errno 524) +fentry_fexit # fentry_attach unexpected error: -1 (errno 524) +fentry_test # fentry_attach unexpected error: -1 (errno 524) +fexit_sleep # fexit_attach fexit attach failed: -1 +fexit_stress # fexit attach unexpected fexit attach: actual -524 < expected 0 +fexit_test # fexit_attach unexpected error: -1 (errno 524) +get_func_args_test # get_func_args_test__attach unexpected error: -524 (errno 524) (trampoline) +get_func_ip_test # get_func_ip_test__attach unexpected error: -524 (errno 524) (trampoline) +htab_update/reenter_update +kfree_skb # attach fentry unexpected error: -524 (trampoline) +kfunc_call/subprog # extern (var ksym) 'bpf_prog_active': not found in kernel BTF +kfunc_call/subprog_lskel # skel unexpected error: -2 +kfunc_dynptr_param/dynptr_data_null # libbpf: prog 'dynptr_data_null': failed to attach: ERROR: strerror_r(-524)=22 +kprobe_multi_test/attach_api_addrs # bpf_program__attach_kprobe_multi_opts unexpected error: -95 +kprobe_multi_test/attach_api_pattern # bpf_program__attach_kprobe_multi_opts unexpected error: -95 +kprobe_multi_test/attach_api_syms # bpf_program__attach_kprobe_multi_opts unexpected error: -95 +kprobe_multi_test/bench_attach # bpf_program__attach_kprobe_multi_opts unexpected error: -95 +kprobe_multi_test/link_api_addrs # link_fd unexpected link_fd: actual -95 < expected 0 +kprobe_multi_test/link_api_syms # link_fd unexpected link_fd: actual -95 < expected 0 +kprobe_multi_test/skel_api # kprobe_multi__attach unexpected error: -524 (errno 524) +ksyms_module/libbpf # 'bpf_testmod_ksym_percpu': not found in kernel BTF +ksyms_module/lskel # test_ksyms_module_lskel__open_and_load unexpected error: -2 +libbpf_get_fd_by_id_opts # test_libbpf_get_fd_by_id_opts__attach unexpected error: -524 (errno 524) +lookup_key # test_lookup_key__attach unexpected error: -524 (errno 524) +lru_bug # lru_bug__attach unexpected error: -524 (errno 524) +modify_return # modify_return__attach failed unexpected error: -524 (errno 524) +module_attach # skel_attach skeleton attach failed: -524 +mptcp/base # run_test mptcp unexpected error: -524 (errno 524) +netcnt # packets unexpected packets: actual 10001 != expected 10000 +recursion # skel_attach unexpected error: -524 (errno 524) +ringbuf # skel_attach skeleton attachment failed: -1 +setget_sockopt # attach_cgroup unexpected error: -524 +sk_storage_tracing # test_sk_storage_tracing__attach unexpected error: -524 (errno 524) +skc_to_unix_sock # could not attach BPF object unexpected error: -524 (errno 524) +socket_cookie # prog_attach unexpected error: -524 +stacktrace_build_id # compare_stack_ips stackmap vs. stack_amap err -1 errno 2 +task_local_storage/exit_creds # skel_attach unexpected error: -524 (errno 524) +task_local_storage/recursion # skel_attach unexpected error: -524 (errno 524) +test_bprm_opts # attach attach failed: -524 +test_ima # attach attach failed: -524 +test_local_storage # attach lsm attach failed: -524 +test_lsm # test_lsm_first_attach unexpected error: -524 (errno 524) +test_overhead # attach_fentry unexpected error: -524 +timer # timer unexpected error: -524 (errno 524) +timer_crash # timer_crash__attach unexpected error: -524 (errno 524) +timer_mim # timer_mim unexpected error: -524 (errno 524) +trace_printk # trace_printk__attach unexpected error: -1 (errno 524) +trace_vprintk # trace_vprintk__attach unexpected error: -1 (errno 524) +tracing_struct # tracing_struct__attach unexpected error: -524 (errno 524) +trampoline_count # attach_prog unexpected error: -524 +unpriv_bpf_disabled # skel_attach unexpected error: -524 (errno 524) +user_ringbuf/test_user_ringbuf_post_misaligned # misaligned_skel unexpected error: -524 (errno 524) +user_ringbuf/test_user_ringbuf_post_producer_wrong_offset +user_ringbuf/test_user_ringbuf_post_larger_than_ringbuf_sz +user_ringbuf/test_user_ringbuf_basic # ringbuf_basic_skel unexpected error: -524 (errno 524) +user_ringbuf/test_user_ringbuf_sample_full_ring_buffer +user_ringbuf/test_user_ringbuf_post_alignment_autoadjust +user_ringbuf/test_user_ringbuf_overfill +user_ringbuf/test_user_ringbuf_discards_properly_ignored +user_ringbuf/test_user_ringbuf_loop +user_ringbuf/test_user_ringbuf_msg_protocol +user_ringbuf/test_user_ringbuf_blocking_reserve +verify_pkcs7_sig # test_verify_pkcs7_sig__attach unexpected error: -524 (errno 524) +vmlinux # skel_attach skeleton attach failed: -524 From 9ef40974a82a474321a4c2dd75d395943930c638 Mon Sep 17 00:00:00 2001 From: Dave Marchevsky Date: Thu, 20 Oct 2022 09:07:18 -0700 Subject: [PATCH 0250/2002] bpf: Allow ringbuf memory to be used as map key This patch adds support for the following pattern: struct some_data *data = bpf_ringbuf_reserve(&ringbuf, sizeof(struct some_data, 0)); if (!data) return; bpf_map_lookup_elem(&another_map, &data->some_field); bpf_ringbuf_submit(data); Currently the verifier does not consider bpf_ringbuf_reserve's PTR_TO_MEM | MEM_ALLOC ret type a valid key input to bpf_map_lookup_elem. Since PTR_TO_MEM is by definition a valid region of memory, it is safe to use it as a key for lookups. Signed-off-by: Dave Marchevsky Acked-by: Yonghong Song Acked-by: Andrii Nakryiko Link: https://lore.kernel.org/r/20221020160721.4030492-1-davemarchevsky@fb.com Signed-off-by: Alexei Starovoitov --- kernel/bpf/verifier.c | 1 + 1 file changed, 1 insertion(+) diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 6f6d2d511c06..97351ae3e7a7 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -5641,6 +5641,7 @@ static const struct bpf_reg_types map_key_value_types = { PTR_TO_PACKET_META, PTR_TO_MAP_KEY, PTR_TO_MAP_VALUE, + PTR_TO_MEM | MEM_ALLOC, }, }; From d1673304097c1f5b04e062cf62fb40200ef1546b Mon Sep 17 00:00:00 2001 From: Dave Marchevsky Date: Thu, 20 Oct 2022 09:07:19 -0700 Subject: [PATCH 0251/2002] bpf: Consider all mem_types compatible for map_{key,value} args After the previous patch, which added PTR_TO_MEM | MEM_ALLOC type map_key_value_types, the only difference between map_key_value_types and mem_types sets is PTR_TO_BUF and PTR_TO_MEM, which are in the latter set but not the former. Helpers which expect ARG_PTR_TO_MAP_KEY or ARG_PTR_TO_MAP_VALUE already effectively expect a valid blob of arbitrary memory that isn't necessarily explicitly associated with a map. When validating a PTR_TO_MAP_{KEY,VALUE} arg, the verifier expects meta->map_ptr to have already been set, either by an earlier ARG_CONST_MAP_PTR arg, or custom logic like that in process_timer_func or process_kptr_func. So let's get rid of map_key_value_types and just use mem_types for those args. This has the effect of adding PTR_TO_BUF and PTR_TO_MEM to the set of compatible types for ARG_PTR_TO_MAP_KEY and ARG_PTR_TO_MAP_VALUE. PTR_TO_BUF is used by various bpf_iter implementations to represent a chunk of valid r/w memory in ctx args for iter prog. PTR_TO_MEM is used by networking, tracing, and ringbuf helpers to represent a chunk of valid memory. The PTR_TO_MEM | MEM_ALLOC type added in previous commit is specific to ringbuf helpers. Presence or absence of MEM_ALLOC doesn't change the validity of using PTR_TO_MEM as a map_{key,val} input. Signed-off-by: Dave Marchevsky Acked-by: Andrii Nakryiko Link: https://lore.kernel.org/r/20221020160721.4030492-2-davemarchevsky@fb.com Signed-off-by: Alexei Starovoitov --- kernel/bpf/verifier.c | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 97351ae3e7a7..ddc1452cf023 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -5634,17 +5634,6 @@ struct bpf_reg_types { u32 *btf_id; }; -static const struct bpf_reg_types map_key_value_types = { - .types = { - PTR_TO_STACK, - PTR_TO_PACKET, - PTR_TO_PACKET_META, - PTR_TO_MAP_KEY, - PTR_TO_MAP_VALUE, - PTR_TO_MEM | MEM_ALLOC, - }, -}; - static const struct bpf_reg_types sock_types = { .types = { PTR_TO_SOCK_COMMON, @@ -5711,8 +5700,8 @@ static const struct bpf_reg_types dynptr_types = { }; static const struct bpf_reg_types *compatible_reg_types[__BPF_ARG_TYPE_MAX] = { - [ARG_PTR_TO_MAP_KEY] = &map_key_value_types, - [ARG_PTR_TO_MAP_VALUE] = &map_key_value_types, + [ARG_PTR_TO_MAP_KEY] = &mem_types, + [ARG_PTR_TO_MAP_VALUE] = &mem_types, [ARG_CONST_SIZE] = &scalar_types, [ARG_CONST_SIZE_OR_ZERO] = &scalar_types, [ARG_CONST_ALLOC_SIZE_OR_ZERO] = &scalar_types, From 51ee71d38d8ce5a30496251200ab2f12b71e1fb6 Mon Sep 17 00:00:00 2001 From: Dave Marchevsky Date: Thu, 20 Oct 2022 09:07:20 -0700 Subject: [PATCH 0252/2002] selftests/bpf: Add test verifying bpf_ringbuf_reserve retval use in map ops Add a test_ringbuf_map_key test prog, borrowing heavily from extant test_ringbuf.c. The program tries to use the result of bpf_ringbuf_reserve as map_key, which was not possible before previouis commits in this series. The test runner added to prog_tests/ringbuf.c verifies that the program loads and does basic sanity checks to confirm that it runs as expected. Also, refactor test_ringbuf such that runners for existing test_ringbuf and newly-added test_ringbuf_map_key are subtests of 'ringbuf' top-level test. Signed-off-by: Dave Marchevsky Acked-by: Yonghong Song Acked-by: Andrii Nakryiko Link: https://lore.kernel.org/r/20221020160721.4030492-3-davemarchevsky@fb.com Signed-off-by: Alexei Starovoitov --- tools/testing/selftests/bpf/Makefile | 8 ++- .../selftests/bpf/prog_tests/ringbuf.c | 66 ++++++++++++++++- .../bpf/progs/test_ringbuf_map_key.c | 70 +++++++++++++++++++ 3 files changed, 140 insertions(+), 4 deletions(-) create mode 100644 tools/testing/selftests/bpf/progs/test_ringbuf_map_key.c diff --git a/tools/testing/selftests/bpf/Makefile b/tools/testing/selftests/bpf/Makefile index e6cf21fad69f..79edef1dbda4 100644 --- a/tools/testing/selftests/bpf/Makefile +++ b/tools/testing/selftests/bpf/Makefile @@ -359,9 +359,11 @@ LINKED_SKELS := test_static_linked.skel.h linked_funcs.skel.h \ test_subskeleton.skel.h test_subskeleton_lib.skel.h \ test_usdt.skel.h -LSKELS := fentry_test.c fexit_test.c fexit_sleep.c \ - test_ringbuf.c atomics.c trace_printk.c trace_vprintk.c \ - map_ptr_kern.c core_kern.c core_kern_overflow.c +LSKELS := fentry_test.c fexit_test.c fexit_sleep.c atomics.c \ + trace_printk.c trace_vprintk.c map_ptr_kern.c \ + core_kern.c core_kern_overflow.c test_ringbuf.c \ + test_ringbuf_map_key.c + # Generate both light skeleton and libbpf skeleton for these LSKELS_EXTRA := test_ksyms_module.c test_ksyms_weak.c kfunc_call_test.c \ kfunc_call_test_subprog.c diff --git a/tools/testing/selftests/bpf/prog_tests/ringbuf.c b/tools/testing/selftests/bpf/prog_tests/ringbuf.c index 9a80fe8a6427..ac104dc652e3 100644 --- a/tools/testing/selftests/bpf/prog_tests/ringbuf.c +++ b/tools/testing/selftests/bpf/prog_tests/ringbuf.c @@ -13,6 +13,7 @@ #include #include #include "test_ringbuf.lskel.h" +#include "test_ringbuf_map_key.lskel.h" #define EDONE 7777 @@ -58,6 +59,7 @@ static int process_sample(void *ctx, void *data, size_t len) } } +static struct test_ringbuf_map_key_lskel *skel_map_key; static struct test_ringbuf_lskel *skel; static struct ring_buffer *ringbuf; @@ -81,7 +83,7 @@ static void *poll_thread(void *input) return (void *)(long)ring_buffer__poll(ringbuf, timeout); } -void test_ringbuf(void) +static void ringbuf_subtest(void) { const size_t rec_sz = BPF_RINGBUF_HDR_SZ + sizeof(struct sample); pthread_t thread; @@ -297,3 +299,65 @@ cleanup: ring_buffer__free(ringbuf); test_ringbuf_lskel__destroy(skel); } + +static int process_map_key_sample(void *ctx, void *data, size_t len) +{ + struct sample *s; + int err, val; + + s = data; + switch (s->seq) { + case 1: + ASSERT_EQ(s->value, 42, "sample_value"); + err = bpf_map_lookup_elem(skel_map_key->maps.hash_map.map_fd, + s, &val); + ASSERT_OK(err, "hash_map bpf_map_lookup_elem"); + ASSERT_EQ(val, 1, "hash_map val"); + return -EDONE; + default: + return 0; + } +} + +static void ringbuf_map_key_subtest(void) +{ + int err; + + skel_map_key = test_ringbuf_map_key_lskel__open(); + if (!ASSERT_OK_PTR(skel_map_key, "test_ringbuf_map_key_lskel__open")) + return; + + skel_map_key->maps.ringbuf.max_entries = getpagesize(); + skel_map_key->bss->pid = getpid(); + + err = test_ringbuf_map_key_lskel__load(skel_map_key); + if (!ASSERT_OK(err, "test_ringbuf_map_key_lskel__load")) + goto cleanup; + + ringbuf = ring_buffer__new(skel_map_key->maps.ringbuf.map_fd, + process_map_key_sample, NULL, NULL); + if (!ASSERT_OK_PTR(ringbuf, "ring_buffer__new")) + goto cleanup; + + err = test_ringbuf_map_key_lskel__attach(skel_map_key); + if (!ASSERT_OK(err, "test_ringbuf_map_key_lskel__attach")) + goto cleanup_ringbuf; + + syscall(__NR_getpgid); + ASSERT_EQ(skel_map_key->bss->seq, 1, "skel_map_key->bss->seq"); + err = ring_buffer__poll(ringbuf, -1); + ASSERT_EQ(err, -EDONE, "ring_buffer__poll"); + +cleanup_ringbuf: + ring_buffer__free(ringbuf); +cleanup: + test_ringbuf_map_key_lskel__destroy(skel_map_key); +} + +void test_ringbuf(void) +{ + if (test__start_subtest("ringbuf")) + ringbuf_subtest(); + if (test__start_subtest("ringbuf_map_key")) + ringbuf_map_key_subtest(); +} diff --git a/tools/testing/selftests/bpf/progs/test_ringbuf_map_key.c b/tools/testing/selftests/bpf/progs/test_ringbuf_map_key.c new file mode 100644 index 000000000000..2760bf60d05a --- /dev/null +++ b/tools/testing/selftests/bpf/progs/test_ringbuf_map_key.c @@ -0,0 +1,70 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2022 Meta Platforms, Inc. and affiliates. */ + +#include +#include +#include "bpf_misc.h" + +char _license[] SEC("license") = "GPL"; + +struct sample { + int pid; + int seq; + long value; + char comm[16]; +}; + +struct { + __uint(type, BPF_MAP_TYPE_RINGBUF); +} ringbuf SEC(".maps"); + +struct { + __uint(type, BPF_MAP_TYPE_HASH); + __uint(max_entries, 1000); + __type(key, struct sample); + __type(value, int); +} hash_map SEC(".maps"); + +/* inputs */ +int pid = 0; + +/* inner state */ +long seq = 0; + +SEC("fentry/" SYS_PREFIX "sys_getpgid") +int test_ringbuf_mem_map_key(void *ctx) +{ + int cur_pid = bpf_get_current_pid_tgid() >> 32; + struct sample *sample, sample_copy; + int *lookup_val; + + if (cur_pid != pid) + return 0; + + sample = bpf_ringbuf_reserve(&ringbuf, sizeof(*sample), 0); + if (!sample) + return 0; + + sample->pid = pid; + bpf_get_current_comm(sample->comm, sizeof(sample->comm)); + sample->seq = ++seq; + sample->value = 42; + + /* test using 'sample' (PTR_TO_MEM | MEM_ALLOC) as map key arg + */ + lookup_val = (int *)bpf_map_lookup_elem(&hash_map, sample); + + /* workaround - memcpy is necessary so that verifier doesn't + * complain with: + * verifier internal error: more than one arg with ref_obj_id R3 + * when trying to do bpf_map_update_elem(&hash_map, sample, &sample->seq, BPF_ANY); + * + * Since bpf_map_lookup_elem above uses 'sample' as key, test using + * sample field as value below + */ + __builtin_memcpy(&sample_copy, sample, sizeof(struct sample)); + bpf_map_update_elem(&hash_map, &sample_copy, &sample->seq, BPF_ANY); + + bpf_ringbuf_submit(sample, 0); + return 0; +} From 8f4bc15b9ad73434643aadb19506e1547bedf7eb Mon Sep 17 00:00:00 2001 From: Dave Marchevsky Date: Thu, 20 Oct 2022 09:07:21 -0700 Subject: [PATCH 0253/2002] selftests/bpf: Add write to hashmap to array_map iter test Modify iter prog in existing bpf_iter_bpf_array_map.c, which currently dumps arraymap key/val, to also do a write of (val, key) into a newly-added hashmap. Confirm that the write succeeds as expected by modifying the userspace runner program. Before a change added in an earlier commit - considering PTR_TO_BUF reg a valid input to helpers which expect MAP_{KEY,VAL} - the verifier would've rejected this prog change due to type mismatch. Since using current iter's key/val to access a separate map is a reasonable usecase, let's add support for it. Note that the test prog cannot directly write (val, key) into hashmap via bpf_map_update_elem when both come from iter context because key is marked MEM_RDONLY. This is due to bpf_map_update_elem - and other basic map helpers - taking ARG_PTR_TO_MAP_{KEY,VALUE} w/o MEM_RDONLY type flag. bpf_map_{lookup,update,delete}_elem don't modify their input key/val so it should be possible to tag their args READONLY, but due to the ubiquitous use of these helpers and verifier checks for type == MAP_VALUE, such a change is nontrivial and seems better to address in a followup series. Also fixup some 'goto's in test runner's map checking loop. Signed-off-by: Dave Marchevsky Acked-by: Andrii Nakryiko Link: https://lore.kernel.org/r/20221020160721.4030492-4-davemarchevsky@fb.com Signed-off-by: Alexei Starovoitov --- .../selftests/bpf/prog_tests/bpf_iter.c | 20 ++++++++++++------ .../bpf/progs/bpf_iter_bpf_array_map.c | 21 ++++++++++++++++++- 2 files changed, 34 insertions(+), 7 deletions(-) diff --git a/tools/testing/selftests/bpf/prog_tests/bpf_iter.c b/tools/testing/selftests/bpf/prog_tests/bpf_iter.c index c39d40f4b268..6f8ed61fc4b4 100644 --- a/tools/testing/selftests/bpf/prog_tests/bpf_iter.c +++ b/tools/testing/selftests/bpf/prog_tests/bpf_iter.c @@ -941,10 +941,10 @@ static void test_bpf_array_map(void) { __u64 val, expected_val = 0, res_first_val, first_val = 0; DECLARE_LIBBPF_OPTS(bpf_iter_attach_opts, opts); - __u32 expected_key = 0, res_first_key; + __u32 key, expected_key = 0, res_first_key; + int err, i, map_fd, hash_fd, iter_fd; struct bpf_iter_bpf_array_map *skel; union bpf_iter_link_info linfo; - int err, i, map_fd, iter_fd; struct bpf_link *link; char buf[64] = {}; int len, start; @@ -1001,12 +1001,20 @@ static void test_bpf_array_map(void) if (!ASSERT_EQ(skel->bss->val_sum, expected_val, "val_sum")) goto close_iter; + hash_fd = bpf_map__fd(skel->maps.hashmap1); for (i = 0; i < bpf_map__max_entries(skel->maps.arraymap1); i++) { err = bpf_map_lookup_elem(map_fd, &i, &val); - if (!ASSERT_OK(err, "map_lookup")) - goto out; - if (!ASSERT_EQ(i, val, "invalid_val")) - goto out; + if (!ASSERT_OK(err, "map_lookup arraymap1")) + goto close_iter; + if (!ASSERT_EQ(i, val, "invalid_val arraymap1")) + goto close_iter; + + val = i + 4; + err = bpf_map_lookup_elem(hash_fd, &val, &key); + if (!ASSERT_OK(err, "map_lookup hashmap1")) + goto close_iter; + if (!ASSERT_EQ(key, val - 4, "invalid_val hashmap1")) + goto close_iter; } close_iter: diff --git a/tools/testing/selftests/bpf/progs/bpf_iter_bpf_array_map.c b/tools/testing/selftests/bpf/progs/bpf_iter_bpf_array_map.c index 6286023fd62b..c5969ca6f26b 100644 --- a/tools/testing/selftests/bpf/progs/bpf_iter_bpf_array_map.c +++ b/tools/testing/selftests/bpf/progs/bpf_iter_bpf_array_map.c @@ -19,13 +19,20 @@ struct { __type(value, __u64); } arraymap1 SEC(".maps"); +struct { + __uint(type, BPF_MAP_TYPE_HASH); + __uint(max_entries, 10); + __type(key, __u64); + __type(value, __u32); +} hashmap1 SEC(".maps"); + __u32 key_sum = 0; __u64 val_sum = 0; SEC("iter/bpf_map_elem") int dump_bpf_array_map(struct bpf_iter__bpf_map_elem *ctx) { - __u32 *key = ctx->key; + __u32 *hmap_val, *key = ctx->key; __u64 *val = ctx->value; if (key == (void *)0 || val == (void *)0) @@ -35,6 +42,18 @@ int dump_bpf_array_map(struct bpf_iter__bpf_map_elem *ctx) bpf_seq_write(ctx->meta->seq, val, sizeof(__u64)); key_sum += *key; val_sum += *val; + + /* workaround - It's necessary to do this convoluted (val, key) + * write into hashmap1, instead of simply doing + * bpf_map_update_elem(&hashmap1, val, key, BPF_ANY); + * because key has MEM_RDONLY flag and bpf_map_update elem expects + * types without this flag + */ + bpf_map_update_elem(&hashmap1, val, val, BPF_ANY); + hmap_val = bpf_map_lookup_elem(&hashmap1, val); + if (hmap_val) + *hmap_val = *key; + *val = *key; return 0; } From 9a60850e8cd9160beba79aa6d529332eb18c9bc7 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Wed, 19 Oct 2022 15:44:21 +0200 Subject: [PATCH 0254/2002] ieee802154: hwsim: Introduce a helper to update all the PIB attributes Perform the update of the PIB structure only in a single place. This way we can have much simpler functions when updating the page, channel or address filters. This helper will become even more useful when we will update the ->set_promiscuous() callback to actually save the filtering level in the PIB structure. Signed-off-by: Miquel Raynal Acked-by: Alexander Aring Link: https://lore.kernel.org/r/20221019134423.877169-2-miquel.raynal@bootlin.com Signed-off-by: Stefan Schmidt --- drivers/net/ieee802154/mac802154_hwsim.c | 66 +++++++++++++----------- 1 file changed, 35 insertions(+), 31 deletions(-) diff --git a/drivers/net/ieee802154/mac802154_hwsim.c b/drivers/net/ieee802154/mac802154_hwsim.c index 1db7da3ccc1a..44dbd5f27dc5 100644 --- a/drivers/net/ieee802154/mac802154_hwsim.c +++ b/drivers/net/ieee802154/mac802154_hwsim.c @@ -90,46 +90,20 @@ static int hwsim_hw_ed(struct ieee802154_hw *hw, u8 *level) return 0; } -static int hwsim_hw_channel(struct ieee802154_hw *hw, u8 page, u8 channel) +static int hwsim_update_pib(struct ieee802154_hw *hw, u8 page, u8 channel, + struct ieee802154_hw_addr_filt *filt) { struct hwsim_phy *phy = hw->priv; struct hwsim_pib *pib, *pib_old; - pib = kzalloc(sizeof(*pib), GFP_KERNEL); + pib = kzalloc(sizeof(*pib), GFP_ATOMIC); if (!pib) return -ENOMEM; + pib_old = rtnl_dereference(phy->pib); + pib->page = page; pib->channel = channel; - - pib_old = rtnl_dereference(phy->pib); - - pib->filt.short_addr = pib_old->filt.short_addr; - pib->filt.pan_id = pib_old->filt.pan_id; - pib->filt.ieee_addr = pib_old->filt.ieee_addr; - pib->filt.pan_coord = pib_old->filt.pan_coord; - - rcu_assign_pointer(phy->pib, pib); - kfree_rcu(pib_old, rcu); - return 0; -} - -static int hwsim_hw_addr_filt(struct ieee802154_hw *hw, - struct ieee802154_hw_addr_filt *filt, - unsigned long changed) -{ - struct hwsim_phy *phy = hw->priv; - struct hwsim_pib *pib, *pib_old; - - pib = kzalloc(sizeof(*pib), GFP_KERNEL); - if (!pib) - return -ENOMEM; - - pib_old = rtnl_dereference(phy->pib); - - pib->page = pib_old->page; - pib->channel = pib_old->channel; - pib->filt.short_addr = filt->short_addr; pib->filt.pan_id = filt->pan_id; pib->filt.ieee_addr = filt->ieee_addr; @@ -140,6 +114,36 @@ static int hwsim_hw_addr_filt(struct ieee802154_hw *hw, return 0; } +static int hwsim_hw_channel(struct ieee802154_hw *hw, u8 page, u8 channel) +{ + struct hwsim_phy *phy = hw->priv; + struct hwsim_pib *pib; + int ret; + + rcu_read_lock(); + pib = rcu_dereference(phy->pib); + ret = hwsim_update_pib(hw, page, channel, &pib->filt); + rcu_read_unlock(); + + return ret; +} + +static int hwsim_hw_addr_filt(struct ieee802154_hw *hw, + struct ieee802154_hw_addr_filt *filt, + unsigned long changed) +{ + struct hwsim_phy *phy = hw->priv; + struct hwsim_pib *pib; + int ret; + + rcu_read_lock(); + pib = rcu_dereference(phy->pib); + ret = hwsim_update_pib(hw, pib->page, pib->channel, filt); + rcu_read_unlock(); + + return ret; +} + static void hwsim_hw_receive(struct ieee802154_hw *hw, struct sk_buff *skb, u8 lqi) { From 19177eedcf4412a90ec1ebaffac3514d0a3b4ff2 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Wed, 19 Oct 2022 15:44:22 +0200 Subject: [PATCH 0255/2002] ieee802154: hwsim: Save the current filtering level and use it Save the requested filtering level in the ->set_promiscuous() helper. The logic is: either we want to enable promiscuous mode and we want to disable filters entirely, or we want to use the highest filtering level by default. This is of course an assumption that only works today, but if in the future intermediate levels (such as scan filtering level) are implemented in the core, this logic will need to be updated. This would imply replacing ->set_promiscuous() by something more fine grained anyway, so we are probably safe with this assumption. Once saved in the PIB structure, we can use this value instead of trying to access the PHY structure to know what hardware filtering level has been advertised. Suggested-by: Alexander Aring Signed-off-by: Miquel Raynal Acked-by: Alexander Aring Link: https://lore.kernel.org/r/20221019134423.877169-3-miquel.raynal@bootlin.com Signed-off-by: Stefan Schmidt --- drivers/net/ieee802154/mac802154_hwsim.c | 28 +++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/drivers/net/ieee802154/mac802154_hwsim.c b/drivers/net/ieee802154/mac802154_hwsim.c index 44dbd5f27dc5..9034706d4a53 100644 --- a/drivers/net/ieee802154/mac802154_hwsim.c +++ b/drivers/net/ieee802154/mac802154_hwsim.c @@ -49,6 +49,7 @@ struct hwsim_pib { u8 page; u8 channel; struct ieee802154_hw_addr_filt filt; + enum ieee802154_filtering_level filt_level; struct rcu_head rcu; }; @@ -91,7 +92,8 @@ static int hwsim_hw_ed(struct ieee802154_hw *hw, u8 *level) } static int hwsim_update_pib(struct ieee802154_hw *hw, u8 page, u8 channel, - struct ieee802154_hw_addr_filt *filt) + struct ieee802154_hw_addr_filt *filt, + enum ieee802154_filtering_level filt_level) { struct hwsim_phy *phy = hw->priv; struct hwsim_pib *pib, *pib_old; @@ -108,6 +110,7 @@ static int hwsim_update_pib(struct ieee802154_hw *hw, u8 page, u8 channel, pib->filt.pan_id = filt->pan_id; pib->filt.ieee_addr = filt->ieee_addr; pib->filt.pan_coord = filt->pan_coord; + pib->filt_level = filt_level; rcu_assign_pointer(phy->pib, pib); kfree_rcu(pib_old, rcu); @@ -122,7 +125,7 @@ static int hwsim_hw_channel(struct ieee802154_hw *hw, u8 page, u8 channel) rcu_read_lock(); pib = rcu_dereference(phy->pib); - ret = hwsim_update_pib(hw, page, channel, &pib->filt); + ret = hwsim_update_pib(hw, page, channel, &pib->filt, pib->filt_level); rcu_read_unlock(); return ret; @@ -138,7 +141,7 @@ static int hwsim_hw_addr_filt(struct ieee802154_hw *hw, rcu_read_lock(); pib = rcu_dereference(phy->pib); - ret = hwsim_update_pib(hw, pib->page, pib->channel, filt); + ret = hwsim_update_pib(hw, pib->page, pib->channel, filt, pib->filt_level); rcu_read_unlock(); return ret; @@ -162,7 +165,7 @@ static void hwsim_hw_receive(struct ieee802154_hw *hw, struct sk_buff *skb, memcpy(&hdr, skb->data, 3); /* Level 4 filtering: Frame fields validity */ - if (hw->phy->filtering == IEEE802154_FILTERING_4_FRAME_FIELDS) { + if (pib->filt_level == IEEE802154_FILTERING_4_FRAME_FIELDS) { /* a) Drop reserved frame types */ switch (mac_cb(skb)->type) { case IEEE802154_FC_TYPE_BEACON: @@ -305,7 +308,22 @@ static void hwsim_hw_stop(struct ieee802154_hw *hw) static int hwsim_set_promiscuous_mode(struct ieee802154_hw *hw, const bool on) { - return 0; + enum ieee802154_filtering_level filt_level; + struct hwsim_phy *phy = hw->priv; + struct hwsim_pib *pib; + int ret; + + if (on) + filt_level = IEEE802154_FILTERING_NONE; + else + filt_level = IEEE802154_FILTERING_4_FRAME_FIELDS; + + rcu_read_lock(); + pib = rcu_dereference(phy->pib); + ret = hwsim_update_pib(hw, pib->page, pib->channel, &pib->filt, filt_level); + rcu_read_unlock(); + + return ret; } static const struct ieee802154_ops hwsim_ops = { From 4161634bce9537ed173b3c8fd0bf9f0218bcf41c Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Wed, 19 Oct 2022 15:44:23 +0200 Subject: [PATCH 0256/2002] mac802154: Ensure proper scan-level filtering We now have a fine grained filtering information so let's ensure proper filtering in scan mode, which means that only beacons are processed. Signed-off-by: Miquel Raynal Acked-by: Alexander Aring Link: https://lore.kernel.org/r/20221019134423.877169-4-miquel.raynal@bootlin.com Signed-off-by: Stefan Schmidt --- net/mac802154/rx.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/net/mac802154/rx.c b/net/mac802154/rx.c index d1f7b8df41fe..dc9ca2c0ea84 100644 --- a/net/mac802154/rx.c +++ b/net/mac802154/rx.c @@ -34,6 +34,7 @@ ieee802154_subif_frame(struct ieee802154_sub_if_data *sdata, struct sk_buff *skb, const struct ieee802154_hdr *hdr) { struct wpan_dev *wpan_dev = &sdata->wpan_dev; + struct wpan_phy *wpan_phy = sdata->local->hw.phy; __le16 span, sshort; int rc; @@ -42,6 +43,17 @@ ieee802154_subif_frame(struct ieee802154_sub_if_data *sdata, span = wpan_dev->pan_id; sshort = wpan_dev->short_addr; + /* Level 3 filtering: Only beacons are accepted during scans */ + if (sdata->required_filtering == IEEE802154_FILTERING_3_SCAN && + sdata->required_filtering > wpan_phy->filtering) { + if (mac_cb(skb)->type != IEEE802154_FC_TYPE_BEACON) { + dev_dbg(&sdata->dev->dev, + "drop non-beacon frame (0x%x) during scan\n", + mac_cb(skb)->type); + goto fail; + } + } + switch (mac_cb(skb)->dest.mode) { case IEEE802154_ADDR_NONE: if (mac_cb(skb)->dest.mode != IEEE802154_ADDR_NONE) @@ -277,10 +289,6 @@ void ieee802154_rx(struct ieee802154_local *local, struct sk_buff *skb) ieee802154_monitors_rx(local, skb); - /* TODO: Handle upcomming receive path where the PHY is at the - * IEEE802154_FILTERING_NONE level during a scan. - */ - /* Level 1 filtering: Check the FCS by software when relevant */ if (local->hw.phy->filtering == IEEE802154_FILTERING_NONE) { crc = crc_ccitt(0, skb->data, skb->len); From 14e493ddc341169f640a5b73120a278411129d4e Mon Sep 17 00:00:00 2001 From: Robert-Ionut Alexa Date: Tue, 18 Oct 2022 17:18:50 +0300 Subject: [PATCH 0257/2002] net: dpaa2-eth: add support to query the number of queues through ethtool The .get_channels() ethtool_ops callback is implemented and exports the number of queues: Rx, Tx, Tx conf and Rx err. The last two ones, Tx confirmation and Rx err, are counted as 'others'. The .set_channels() callback is not implemented since the DPAA2 software/firmware architecture does not allow the dynamic reconfiguration of the number of queues. Signed-off-by: Robert-Ionut Alexa Signed-off-by: Ioana Ciornei Signed-off-by: David S. Miller --- .../ethernet/freescale/dpaa2/dpaa2-ethtool.c | 27 +++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-ethtool.c b/drivers/net/ethernet/freescale/dpaa2/dpaa2-ethtool.c index eea7d7a07c00..97ec2adf5dc5 100644 --- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-ethtool.c +++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-ethtool.c @@ -1,7 +1,6 @@ // SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) /* Copyright 2014-2016 Freescale Semiconductor Inc. - * Copyright 2016 NXP - * Copyright 2020 NXP + * Copyright 2016-2022 NXP */ #include @@ -876,6 +875,29 @@ restore_rx_usecs: return err; } +static void dpaa2_eth_get_channels(struct net_device *net_dev, + struct ethtool_channels *channels) +{ + struct dpaa2_eth_priv *priv = netdev_priv(net_dev); + int queue_count = dpaa2_eth_queue_count(priv); + + channels->max_rx = queue_count; + channels->max_tx = queue_count; + channels->rx_count = queue_count; + channels->tx_count = queue_count; + + /* Tx confirmation and Rx error */ + channels->max_other = queue_count + 1; + channels->max_combined = channels->max_rx + + channels->max_tx + + channels->max_other; + /* Tx conf and Rx err */ + channels->other_count = queue_count + 1; + channels->combined_count = channels->rx_count + + channels->tx_count + + channels->other_count; +} + const struct ethtool_ops dpaa2_ethtool_ops = { .supported_coalesce_params = ETHTOOL_COALESCE_RX_USECS | ETHTOOL_COALESCE_USE_ADAPTIVE_RX, @@ -896,4 +918,5 @@ const struct ethtool_ops dpaa2_ethtool_ops = { .set_tunable = dpaa2_eth_set_tunable, .get_coalesce = dpaa2_eth_get_coalesce, .set_coalesce = dpaa2_eth_set_coalesce, + .get_channels = dpaa2_eth_get_channels, }; From 3313206827678f6f036eca601a51f6c4524b559a Mon Sep 17 00:00:00 2001 From: Ioana Ciornei Date: Tue, 18 Oct 2022 17:18:51 +0300 Subject: [PATCH 0258/2002] net: dpaa2-eth: rearrange variable in dpaa2_eth_get_ethtool_stats Rearrange the variables in the dpaa2_eth_get_ethtool_stats() function so that we adhere to the reverse Christmas tree rule. Also, in the next patch we are adding more variables and I didn't know where to place them with the current ordering. Signed-off-by: Ioana Ciornei Signed-off-by: David S. Miller --- .../ethernet/freescale/dpaa2/dpaa2-ethtool.c | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-ethtool.c b/drivers/net/ethernet/freescale/dpaa2/dpaa2-ethtool.c index 97ec2adf5dc5..46b493892f3b 100644 --- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-ethtool.c +++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-ethtool.c @@ -226,17 +226,8 @@ static void dpaa2_eth_get_ethtool_stats(struct net_device *net_dev, struct ethtool_stats *stats, u64 *data) { - int i = 0; - int j, k, err; - int num_cnt; - union dpni_statistics dpni_stats; - u32 fcnt, bcnt; - u32 fcnt_rx_total = 0, fcnt_tx_total = 0; - u32 bcnt_rx_total = 0, bcnt_tx_total = 0; - u32 buf_cnt; struct dpaa2_eth_priv *priv = netdev_priv(net_dev); - struct dpaa2_eth_drv_stats *extras; - struct dpaa2_eth_ch_stats *ch_stats; + union dpni_statistics dpni_stats; int dpni_stats_page_size[DPNI_STATISTICS_CNT] = { sizeof(dpni_stats.page_0), sizeof(dpni_stats.page_1), @@ -246,6 +237,13 @@ static void dpaa2_eth_get_ethtool_stats(struct net_device *net_dev, sizeof(dpni_stats.page_5), sizeof(dpni_stats.page_6), }; + u32 fcnt_rx_total = 0, fcnt_tx_total = 0; + u32 bcnt_rx_total = 0, bcnt_tx_total = 0; + struct dpaa2_eth_ch_stats *ch_stats; + struct dpaa2_eth_drv_stats *extras; + int j, k, err, num_cnt, i = 0; + u32 fcnt, bcnt; + u32 buf_cnt; memset(data, 0, sizeof(u64) * (DPAA2_ETH_NUM_STATS + DPAA2_ETH_NUM_EXTRA_STATS)); From 095174dafc74c392673ad73de750833804016da7 Mon Sep 17 00:00:00 2001 From: Robert-Ionut Alexa Date: Tue, 18 Oct 2022 17:18:52 +0300 Subject: [PATCH 0259/2002] net: dpaa2-eth: add support for multiple buffer pools per DPNI This patch allows the configuration of multiple buffer pools associated with a single DPNI object, each distinct DPBP object not necessarily shared among all queues. The user can interogate both the number of buffer pools and the buffer count in each buffer pool by using the .get_ethtool_stats() callback. Signed-off-by: Robert-Ionut Alexa Signed-off-by: Ioana Ciornei Signed-off-by: David S. Miller --- .../net/ethernet/freescale/dpaa2/dpaa2-eth.c | 189 ++++++++++++------ .../net/ethernet/freescale/dpaa2/dpaa2-eth.h | 26 ++- .../ethernet/freescale/dpaa2/dpaa2-ethtool.c | 15 +- 3 files changed, 163 insertions(+), 67 deletions(-) diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c index 8d029addddad..1213ae4e1301 100644 --- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c +++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) /* Copyright 2014-2016 Freescale Semiconductor Inc. - * Copyright 2016-2020 NXP + * Copyright 2016-2022 NXP */ #include #include @@ -304,7 +304,7 @@ static void dpaa2_eth_recycle_buf(struct dpaa2_eth_priv *priv, if (ch->recycled_bufs_cnt < DPAA2_ETH_BUFS_PER_CMD) return; - while ((err = dpaa2_io_service_release(ch->dpio, priv->bpid, + while ((err = dpaa2_io_service_release(ch->dpio, ch->bp->bpid, ch->recycled_bufs, ch->recycled_bufs_cnt)) == -EBUSY) { if (retries++ >= DPAA2_ETH_SWP_BUSY_RETRIES) @@ -1631,7 +1631,7 @@ static int dpaa2_eth_set_tx_csum(struct dpaa2_eth_priv *priv, bool enable) * to the specified buffer pool */ static int dpaa2_eth_add_bufs(struct dpaa2_eth_priv *priv, - struct dpaa2_eth_channel *ch, u16 bpid) + struct dpaa2_eth_channel *ch) { struct device *dev = priv->net_dev->dev.parent; u64 buf_array[DPAA2_ETH_BUFS_PER_CMD]; @@ -1663,12 +1663,12 @@ static int dpaa2_eth_add_bufs(struct dpaa2_eth_priv *priv, trace_dpaa2_eth_buf_seed(priv->net_dev, page_address(page), DPAA2_ETH_RX_BUF_RAW_SIZE, addr, priv->rx_buf_size, - bpid); + ch->bp->bpid); } release_bufs: /* In case the portal is busy, retry until successful */ - while ((err = dpaa2_io_service_release(ch->dpio, bpid, + while ((err = dpaa2_io_service_release(ch->dpio, ch->bp->bpid, buf_array, i)) == -EBUSY) { if (retries++ >= DPAA2_ETH_SWP_BUSY_RETRIES) break; @@ -1697,39 +1697,59 @@ err_alloc: return 0; } -static int dpaa2_eth_seed_pool(struct dpaa2_eth_priv *priv, u16 bpid) +static int dpaa2_eth_seed_pool(struct dpaa2_eth_priv *priv, + struct dpaa2_eth_channel *ch) { - int i, j; + int i; int new_count; - for (j = 0; j < priv->num_channels; j++) { - for (i = 0; i < DPAA2_ETH_NUM_BUFS; - i += DPAA2_ETH_BUFS_PER_CMD) { - new_count = dpaa2_eth_add_bufs(priv, priv->channel[j], bpid); - priv->channel[j]->buf_count += new_count; + for (i = 0; i < DPAA2_ETH_NUM_BUFS; i += DPAA2_ETH_BUFS_PER_CMD) { + new_count = dpaa2_eth_add_bufs(priv, ch); + ch->buf_count += new_count; - if (new_count < DPAA2_ETH_BUFS_PER_CMD) { - return -ENOMEM; - } - } + if (new_count < DPAA2_ETH_BUFS_PER_CMD) + return -ENOMEM; } return 0; } +static void dpaa2_eth_seed_pools(struct dpaa2_eth_priv *priv) +{ + struct net_device *net_dev = priv->net_dev; + struct dpaa2_eth_channel *channel; + int i, err = 0; + + for (i = 0; i < priv->num_channels; i++) { + channel = priv->channel[i]; + + err = dpaa2_eth_seed_pool(priv, channel); + + /* Not much to do; the buffer pool, though not filled up, + * may still contain some buffers which would enable us + * to limp on. + */ + if (err) + netdev_err(net_dev, "Buffer seeding failed for DPBP %d (bpid=%d)\n", + channel->bp->dev->obj_desc.id, + channel->bp->bpid); + } +} + /* - * Drain the specified number of buffers from the DPNI's private buffer pool. + * Drain the specified number of buffers from one of the DPNI's private buffer + * pools. * @count must not exceeed DPAA2_ETH_BUFS_PER_CMD */ -static void dpaa2_eth_drain_bufs(struct dpaa2_eth_priv *priv, int count) +static void dpaa2_eth_drain_bufs(struct dpaa2_eth_priv *priv, int bpid, + int count) { u64 buf_array[DPAA2_ETH_BUFS_PER_CMD]; int retries = 0; int ret; do { - ret = dpaa2_io_service_acquire(NULL, priv->bpid, - buf_array, count); + ret = dpaa2_io_service_acquire(NULL, bpid, buf_array, count); if (ret < 0) { if (ret == -EBUSY && retries++ < DPAA2_ETH_SWP_BUSY_RETRIES) @@ -1742,23 +1762,35 @@ static void dpaa2_eth_drain_bufs(struct dpaa2_eth_priv *priv, int count) } while (ret); } -static void dpaa2_eth_drain_pool(struct dpaa2_eth_priv *priv) +static void dpaa2_eth_drain_pool(struct dpaa2_eth_priv *priv, int bpid) { int i; - dpaa2_eth_drain_bufs(priv, DPAA2_ETH_BUFS_PER_CMD); - dpaa2_eth_drain_bufs(priv, 1); + /* Drain the buffer pool */ + dpaa2_eth_drain_bufs(priv, bpid, DPAA2_ETH_BUFS_PER_CMD); + dpaa2_eth_drain_bufs(priv, bpid, 1); + /* Setup to zero the buffer count of all channels which were + * using this buffer pool. + */ for (i = 0; i < priv->num_channels; i++) - priv->channel[i]->buf_count = 0; + if (priv->channel[i]->bp->bpid == bpid) + priv->channel[i]->buf_count = 0; +} + +static void dpaa2_eth_drain_pools(struct dpaa2_eth_priv *priv) +{ + int i; + + for (i = 0; i < priv->num_bps; i++) + dpaa2_eth_drain_pool(priv, priv->bp[i]->bpid); } /* Function is called from softirq context only, so we don't need to guard * the access to percpu count */ static int dpaa2_eth_refill_pool(struct dpaa2_eth_priv *priv, - struct dpaa2_eth_channel *ch, - u16 bpid) + struct dpaa2_eth_channel *ch) { int new_count; @@ -1766,7 +1798,7 @@ static int dpaa2_eth_refill_pool(struct dpaa2_eth_priv *priv, return 0; do { - new_count = dpaa2_eth_add_bufs(priv, ch, bpid); + new_count = dpaa2_eth_add_bufs(priv, ch); if (unlikely(!new_count)) { /* Out of memory; abort for now, we'll try later on */ break; @@ -1848,7 +1880,7 @@ static int dpaa2_eth_poll(struct napi_struct *napi, int budget) break; /* Refill pool if appropriate */ - dpaa2_eth_refill_pool(priv, ch, priv->bpid); + dpaa2_eth_refill_pool(priv, ch); store_cleaned = dpaa2_eth_consume_frames(ch, &fq); if (store_cleaned <= 0) @@ -2047,15 +2079,7 @@ static int dpaa2_eth_open(struct net_device *net_dev) struct dpaa2_eth_priv *priv = netdev_priv(net_dev); int err; - err = dpaa2_eth_seed_pool(priv, priv->bpid); - if (err) { - /* Not much to do; the buffer pool, though not filled up, - * may still contain some buffers which would enable us - * to limp on. - */ - netdev_err(net_dev, "Buffer seeding failed for DPBP %d (bpid=%d)\n", - priv->dpbp_dev->obj_desc.id, priv->bpid); - } + dpaa2_eth_seed_pools(priv); if (!dpaa2_eth_is_type_phy(priv)) { /* We'll only start the txqs when the link is actually ready; @@ -2088,7 +2112,7 @@ static int dpaa2_eth_open(struct net_device *net_dev) enable_err: dpaa2_eth_disable_ch_napi(priv); - dpaa2_eth_drain_pool(priv); + dpaa2_eth_drain_pools(priv); return err; } @@ -2193,7 +2217,7 @@ static int dpaa2_eth_stop(struct net_device *net_dev) dpaa2_eth_disable_ch_napi(priv); /* Empty the buffer pool */ - dpaa2_eth_drain_pool(priv); + dpaa2_eth_drain_pools(priv); /* Empty the Scatter-Gather Buffer cache */ dpaa2_eth_sgt_cache_drain(priv); @@ -3204,13 +3228,14 @@ static void dpaa2_eth_setup_fqs(struct dpaa2_eth_priv *priv) dpaa2_eth_set_fq_affinity(priv); } -/* Allocate and configure one buffer pool for each interface */ -static int dpaa2_eth_setup_dpbp(struct dpaa2_eth_priv *priv) +/* Allocate and configure a buffer pool */ +struct dpaa2_eth_bp *dpaa2_eth_allocate_dpbp(struct dpaa2_eth_priv *priv) { - int err; - struct fsl_mc_device *dpbp_dev; struct device *dev = priv->net_dev->dev.parent; + struct fsl_mc_device *dpbp_dev; struct dpbp_attr dpbp_attrs; + struct dpaa2_eth_bp *bp; + int err; err = fsl_mc_object_allocate(to_fsl_mc_device(dev), FSL_MC_POOL_DPBP, &dpbp_dev); @@ -3219,12 +3244,16 @@ static int dpaa2_eth_setup_dpbp(struct dpaa2_eth_priv *priv) err = -EPROBE_DEFER; else dev_err(dev, "DPBP device allocation failed\n"); - return err; + return ERR_PTR(err); } - priv->dpbp_dev = dpbp_dev; + bp = kzalloc(sizeof(*bp), GFP_KERNEL); + if (!bp) { + err = -ENOMEM; + goto err_alloc; + } - err = dpbp_open(priv->mc_io, 0, priv->dpbp_dev->obj_desc.id, + err = dpbp_open(priv->mc_io, 0, dpbp_dev->obj_desc.id, &dpbp_dev->mc_handle); if (err) { dev_err(dev, "dpbp_open() failed\n"); @@ -3249,9 +3278,11 @@ static int dpaa2_eth_setup_dpbp(struct dpaa2_eth_priv *priv) dev_err(dev, "dpbp_get_attributes() failed\n"); goto err_get_attr; } - priv->bpid = dpbp_attrs.bpid; - return 0; + bp->dev = dpbp_dev; + bp->bpid = dpbp_attrs.bpid; + + return bp; err_get_attr: dpbp_disable(priv->mc_io, 0, dpbp_dev->mc_handle); @@ -3259,17 +3290,58 @@ err_enable: err_reset: dpbp_close(priv->mc_io, 0, dpbp_dev->mc_handle); err_open: + kfree(bp); +err_alloc: fsl_mc_object_free(dpbp_dev); - return err; + return ERR_PTR(err); } -static void dpaa2_eth_free_dpbp(struct dpaa2_eth_priv *priv) +static int dpaa2_eth_setup_default_dpbp(struct dpaa2_eth_priv *priv) { - dpaa2_eth_drain_pool(priv); - dpbp_disable(priv->mc_io, 0, priv->dpbp_dev->mc_handle); - dpbp_close(priv->mc_io, 0, priv->dpbp_dev->mc_handle); - fsl_mc_object_free(priv->dpbp_dev); + struct dpaa2_eth_bp *bp; + int i; + + bp = dpaa2_eth_allocate_dpbp(priv); + if (IS_ERR(bp)) + return PTR_ERR(bp); + + priv->bp[DPAA2_ETH_DEFAULT_BP_IDX] = bp; + priv->num_bps++; + + for (i = 0; i < priv->num_channels; i++) + priv->channel[i]->bp = bp; + + return 0; +} + +void dpaa2_eth_free_dpbp(struct dpaa2_eth_priv *priv, struct dpaa2_eth_bp *bp) +{ + int idx_bp; + + /* Find the index at which this BP is stored */ + for (idx_bp = 0; idx_bp < priv->num_bps; idx_bp++) + if (priv->bp[idx_bp] == bp) + break; + + /* Drain the pool and disable the associated MC object */ + dpaa2_eth_drain_pool(priv, bp->bpid); + dpbp_disable(priv->mc_io, 0, bp->dev->mc_handle); + dpbp_close(priv->mc_io, 0, bp->dev->mc_handle); + fsl_mc_object_free(bp->dev); + kfree(bp); + + /* Move the last in use DPBP over in this position */ + priv->bp[idx_bp] = priv->bp[priv->num_bps - 1]; + priv->num_bps--; +} + +static void dpaa2_eth_free_dpbps(struct dpaa2_eth_priv *priv) +{ + int i; + + for (i = 0; i < priv->num_bps; i++) + dpaa2_eth_free_dpbp(priv, priv->bp[i]); } static int dpaa2_eth_set_buffer_layout(struct dpaa2_eth_priv *priv) @@ -4154,6 +4226,7 @@ out: */ static int dpaa2_eth_bind_dpni(struct dpaa2_eth_priv *priv) { + struct dpaa2_eth_bp *bp = priv->bp[DPAA2_ETH_DEFAULT_BP_IDX]; struct net_device *net_dev = priv->net_dev; struct device *dev = net_dev->dev.parent; struct dpni_pools_cfg pools_params; @@ -4162,7 +4235,7 @@ static int dpaa2_eth_bind_dpni(struct dpaa2_eth_priv *priv) int i; pools_params.num_dpbp = 1; - pools_params.pools[0].dpbp_id = priv->dpbp_dev->obj_desc.id; + pools_params.pools[0].dpbp_id = bp->dev->obj_desc.id; pools_params.pools[0].backup_pool = 0; pools_params.pools[0].buffer_size = priv->rx_buf_size; err = dpni_set_pools(priv->mc_io, 0, priv->mc_token, &pools_params); @@ -4641,7 +4714,7 @@ static int dpaa2_eth_probe(struct fsl_mc_device *dpni_dev) dpaa2_eth_setup_fqs(priv); - err = dpaa2_eth_setup_dpbp(priv); + err = dpaa2_eth_setup_default_dpbp(priv); if (err) goto err_dpbp_setup; @@ -4777,7 +4850,7 @@ err_alloc_percpu_extras: err_alloc_percpu_stats: dpaa2_eth_del_ch_napi(priv); err_bind: - dpaa2_eth_free_dpbp(priv); + dpaa2_eth_free_dpbps(priv); err_dpbp_setup: dpaa2_eth_free_dpio(priv); err_dpio_setup: @@ -4830,7 +4903,7 @@ static int dpaa2_eth_remove(struct fsl_mc_device *ls_dev) free_percpu(priv->percpu_extras); dpaa2_eth_del_ch_napi(priv); - dpaa2_eth_free_dpbp(priv); + dpaa2_eth_free_dpbps(priv); dpaa2_eth_free_dpio(priv); dpaa2_eth_free_dpni(priv); if (priv->onestep_reg_base) diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.h b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.h index 447718483ef4..bb0881e7033b 100644 --- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.h +++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) */ /* Copyright 2014-2016 Freescale Semiconductor Inc. - * Copyright 2016-2020 NXP + * Copyright 2016-2022 NXP */ #ifndef __DPAA2_ETH_H @@ -109,6 +109,14 @@ #define DPAA2_ETH_RX_BUF_ALIGN_REV1 256 #define DPAA2_ETH_RX_BUF_ALIGN 64 +/* The firmware allows assigning multiple buffer pools to a single DPNI - + * maximum 8 DPBP objects. By default, only the first DPBP (idx 0) is used for + * all queues. Thus, when enabling AF_XDP we must accommodate up to 9 DPBPs + * object: the default and 8 other distinct buffer pools, one for each queue. + */ +#define DPAA2_ETH_DEFAULT_BP_IDX 0 +#define DPAA2_ETH_MAX_BPS 9 + /* We are accommodating a skb backpointer and some S/G info * in the frame's software annotation. The hardware * options are either 0 or 64, so we choose the latter. @@ -454,6 +462,11 @@ struct dpaa2_eth_ch_xdp { unsigned int res; }; +struct dpaa2_eth_bp { + struct fsl_mc_device *dev; + int bpid; +}; + struct dpaa2_eth_channel { struct dpaa2_io_notification_ctx nctx; struct fsl_mc_device *dpcon; @@ -472,6 +485,8 @@ struct dpaa2_eth_channel { /* Buffers to be recycled back in the buffer pool */ u64 recycled_bufs[DPAA2_ETH_BUFS_PER_CMD]; int recycled_bufs_cnt; + + struct dpaa2_eth_bp *bp; }; struct dpaa2_eth_dist_fields { @@ -535,14 +550,16 @@ struct dpaa2_eth_priv { u8 ptp_correction_off; void (*dpaa2_set_onestep_params_cb)(struct dpaa2_eth_priv *priv, u32 offset, u8 udp); - struct fsl_mc_device *dpbp_dev; u16 rx_buf_size; - u16 bpid; struct iommu_domain *iommu_domain; enum hwtstamp_tx_types tx_tstamp_type; /* Tx timestamping type */ bool rx_tstamp; /* Rx timestamping enabled */ + /* Buffer pool management */ + struct dpaa2_eth_bp *bp[DPAA2_ETH_MAX_BPS]; + int num_bps; + u16 tx_qdid; struct fsl_mc_io *mc_io; /* Cores which have an affine DPIO/DPCON. @@ -771,4 +788,7 @@ void dpaa2_eth_dl_traps_unregister(struct dpaa2_eth_priv *priv); struct dpaa2_eth_trap_item *dpaa2_eth_dl_get_trap(struct dpaa2_eth_priv *priv, struct dpaa2_fapr *fapr); + +struct dpaa2_eth_bp *dpaa2_eth_allocate_dpbp(struct dpaa2_eth_priv *priv); +void dpaa2_eth_free_dpbp(struct dpaa2_eth_priv *priv, struct dpaa2_eth_bp *bp); #endif /* __DPAA2_H */ diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-ethtool.c b/drivers/net/ethernet/freescale/dpaa2/dpaa2-ethtool.c index 46b493892f3b..32a38a03db57 100644 --- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-ethtool.c +++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-ethtool.c @@ -241,9 +241,9 @@ static void dpaa2_eth_get_ethtool_stats(struct net_device *net_dev, u32 bcnt_rx_total = 0, bcnt_tx_total = 0; struct dpaa2_eth_ch_stats *ch_stats; struct dpaa2_eth_drv_stats *extras; + u32 buf_cnt, buf_cnt_total = 0; int j, k, err, num_cnt, i = 0; u32 fcnt, bcnt; - u32 buf_cnt; memset(data, 0, sizeof(u64) * (DPAA2_ETH_NUM_STATS + DPAA2_ETH_NUM_EXTRA_STATS)); @@ -305,12 +305,15 @@ static void dpaa2_eth_get_ethtool_stats(struct net_device *net_dev, *(data + i++) = fcnt_tx_total; *(data + i++) = bcnt_tx_total; - err = dpaa2_io_query_bp_count(NULL, priv->bpid, &buf_cnt); - if (err) { - netdev_warn(net_dev, "Buffer count query error %d\n", err); - return; + for (j = 0; j < priv->num_bps; j++) { + err = dpaa2_io_query_bp_count(NULL, priv->bp[j]->bpid, &buf_cnt); + if (err) { + netdev_warn(net_dev, "Buffer count query error %d\n", err); + return; + } + buf_cnt_total += buf_cnt; } - *(data + i++) = buf_cnt; + *(data + i++) = buf_cnt_total; if (dpaa2_eth_has_mac(priv)) dpaa2_mac_get_ethtool_stats(priv->mac, data + i); From 96b44697e53a46856aafe6cc4a9f3708a88cc2b5 Mon Sep 17 00:00:00 2001 From: Ioana Ciornei Date: Tue, 18 Oct 2022 17:18:53 +0300 Subject: [PATCH 0260/2002] net: dpaa2-eth: export the CH# in the 'ch_stats' debug file Just give out an index for each channel that we export into the debug file in the form of CH#. This is purely to help corelate each channel information from one debugfs file to another one. Signed-off-by: Ioana Ciornei Signed-off-by: David S. Miller --- drivers/net/ethernet/freescale/dpaa2/dpaa2-eth-debugfs.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth-debugfs.c b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth-debugfs.c index 8356af4631fd..54e7fcf95c89 100644 --- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth-debugfs.c +++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth-debugfs.c @@ -98,14 +98,14 @@ static int dpaa2_dbg_ch_show(struct seq_file *file, void *offset) int i; seq_printf(file, "Channel stats for %s:\n", priv->net_dev->name); - seq_printf(file, "%s%16s%16s%16s%16s%16s%16s\n", - "CHID", "CPU", "Deq busy", "Frames", "CDANs", + seq_printf(file, "%s %5s%16s%16s%16s%16s%16s%16s\n", + "IDX", "CHID", "CPU", "Deq busy", "Frames", "CDANs", "Avg Frm/CDAN", "Buf count"); for (i = 0; i < priv->num_channels; i++) { ch = priv->channel[i]; - seq_printf(file, "%4d%16d%16llu%16llu%16llu%16llu%16d\n", - ch->ch_id, + seq_printf(file, "%3s%d%6d%16d%16llu%16llu%16llu%16llu%16d\n", + "CH#", i, ch->ch_id, ch->nctx.desired_cpu, ch->stats.dequeue_portal_busy, ch->stats.frames, From b1dd9bf6ead81bfb910de1df3be74e9d176cae47 Mon Sep 17 00:00:00 2001 From: Ioana Ciornei Date: Tue, 18 Oct 2022 17:18:54 +0300 Subject: [PATCH 0261/2002] net: dpaa2-eth: export buffer pool info into a new debugfs file Export the allocated buffer pools, the number of buffers that they have currently and which channels are using which BP. The output looks like below: Buffer pool info for eth2: IDX BPID Buf count CH#0 CH#1 CH#2 CH#3 BP#0 1 5124 x x x x Signed-off-by: Ioana Ciornei Signed-off-by: David S. Miller --- .../freescale/dpaa2/dpaa2-eth-debugfs.c | 49 +++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth-debugfs.c b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth-debugfs.c index 54e7fcf95c89..1af254caeb0d 100644 --- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth-debugfs.c +++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth-debugfs.c @@ -119,6 +119,51 @@ static int dpaa2_dbg_ch_show(struct seq_file *file, void *offset) DEFINE_SHOW_ATTRIBUTE(dpaa2_dbg_ch); +static int dpaa2_dbg_bp_show(struct seq_file *file, void *offset) +{ + struct dpaa2_eth_priv *priv = (struct dpaa2_eth_priv *)file->private; + int i, j, num_queues, buf_cnt; + struct dpaa2_eth_bp *bp; + char ch_name[10]; + int err; + + /* Print out the header */ + seq_printf(file, "Buffer pool info for %s:\n", priv->net_dev->name); + seq_printf(file, "%s %10s%15s", "IDX", "BPID", "Buf count"); + num_queues = dpaa2_eth_queue_count(priv); + for (i = 0; i < num_queues; i++) { + snprintf(ch_name, sizeof(ch_name), "CH#%d", i); + seq_printf(file, "%10s", ch_name); + } + seq_printf(file, "\n"); + + /* For each buffer pool, print out its BPID, the number of buffers in + * that buffer pool and the channels which are using it. + */ + for (i = 0; i < priv->num_bps; i++) { + bp = priv->bp[i]; + + err = dpaa2_io_query_bp_count(NULL, bp->bpid, &buf_cnt); + if (err) { + netdev_warn(priv->net_dev, "Buffer count query error %d\n", err); + return err; + } + + seq_printf(file, "%3s%d%10d%15d", "BP#", i, bp->bpid, buf_cnt); + for (j = 0; j < num_queues; j++) { + if (priv->channel[j]->bp == bp) + seq_printf(file, "%10s", "x"); + else + seq_printf(file, "%10s", ""); + } + seq_printf(file, "\n"); + } + + return 0; +} + +DEFINE_SHOW_ATTRIBUTE(dpaa2_dbg_bp); + void dpaa2_dbg_add(struct dpaa2_eth_priv *priv) { struct fsl_mc_device *dpni_dev; @@ -139,6 +184,10 @@ void dpaa2_dbg_add(struct dpaa2_eth_priv *priv) /* per-fq stats file */ debugfs_create_file("ch_stats", 0444, dir, priv, &dpaa2_dbg_ch_fops); + + /* per buffer pool stats file */ + debugfs_create_file("bp_stats", 0444, dir, priv, &dpaa2_dbg_bp_fops); + } void dpaa2_dbg_remove(struct dpaa2_eth_priv *priv) From 801c76dd067cb7e92934f49e21d95ecc54f82689 Mon Sep 17 00:00:00 2001 From: Robert-Ionut Alexa Date: Tue, 18 Oct 2022 17:18:55 +0300 Subject: [PATCH 0262/2002] net: dpaa2-eth: update the dpni_set_pools() API to support per QDBIN pools Update the dpni_set_pool() firmware API so that in the next patches we can configure per Rx queue (per QDBIN) buffer pools. This is a hard requirement of the AF_XDP, thus we need the newer API version. Signed-off-by: Robert-Ionut Alexa Signed-off-by: Ioana Ciornei Signed-off-by: David S. Miller --- .../net/ethernet/freescale/dpaa2/dpni-cmd.h | 19 +++++++++++++------ drivers/net/ethernet/freescale/dpaa2/dpni.c | 6 +++++- drivers/net/ethernet/freescale/dpaa2/dpni.h | 9 +++++++++ 3 files changed, 27 insertions(+), 7 deletions(-) diff --git a/drivers/net/ethernet/freescale/dpaa2/dpni-cmd.h b/drivers/net/ethernet/freescale/dpaa2/dpni-cmd.h index 828f538097af..be9492b8d5dc 100644 --- a/drivers/net/ethernet/freescale/dpaa2/dpni-cmd.h +++ b/drivers/net/ethernet/freescale/dpaa2/dpni-cmd.h @@ -13,10 +13,12 @@ #define DPNI_VER_MINOR 0 #define DPNI_CMD_BASE_VERSION 1 #define DPNI_CMD_2ND_VERSION 2 +#define DPNI_CMD_3RD_VERSION 3 #define DPNI_CMD_ID_OFFSET 4 #define DPNI_CMD(id) (((id) << DPNI_CMD_ID_OFFSET) | DPNI_CMD_BASE_VERSION) #define DPNI_CMD_V2(id) (((id) << DPNI_CMD_ID_OFFSET) | DPNI_CMD_2ND_VERSION) +#define DPNI_CMD_V3(id) (((id) << DPNI_CMD_ID_OFFSET) | DPNI_CMD_3RD_VERSION) #define DPNI_CMDID_OPEN DPNI_CMD(0x801) #define DPNI_CMDID_CLOSE DPNI_CMD(0x800) @@ -39,7 +41,7 @@ #define DPNI_CMDID_GET_IRQ_STATUS DPNI_CMD(0x016) #define DPNI_CMDID_CLEAR_IRQ_STATUS DPNI_CMD(0x017) -#define DPNI_CMDID_SET_POOLS DPNI_CMD(0x200) +#define DPNI_CMDID_SET_POOLS DPNI_CMD_V3(0x200) #define DPNI_CMDID_SET_ERRORS_BEHAVIOR DPNI_CMD(0x20B) #define DPNI_CMDID_GET_QDID DPNI_CMD(0x210) @@ -115,14 +117,19 @@ struct dpni_cmd_open { }; #define DPNI_BACKUP_POOL(val, order) (((val) & 0x1) << (order)) + +struct dpni_cmd_pool { + __le16 dpbp_id; + u8 priority_mask; + u8 pad; +}; + struct dpni_cmd_set_pools { - /* cmd word 0 */ u8 num_dpbp; u8 backup_pool_mask; - __le16 pad; - /* cmd word 0..4 */ - __le32 dpbp_id[DPNI_MAX_DPBP]; - /* cmd word 4..6 */ + u8 pad; + u8 pool_options; + struct dpni_cmd_pool pool[DPNI_MAX_DPBP]; __le16 buffer_size[DPNI_MAX_DPBP]; }; diff --git a/drivers/net/ethernet/freescale/dpaa2/dpni.c b/drivers/net/ethernet/freescale/dpaa2/dpni.c index 6c3b36f20fb8..02601a283b59 100644 --- a/drivers/net/ethernet/freescale/dpaa2/dpni.c +++ b/drivers/net/ethernet/freescale/dpaa2/dpni.c @@ -173,8 +173,12 @@ int dpni_set_pools(struct fsl_mc_io *mc_io, token); cmd_params = (struct dpni_cmd_set_pools *)cmd.params; cmd_params->num_dpbp = cfg->num_dpbp; + cmd_params->pool_options = cfg->pool_options; for (i = 0; i < DPNI_MAX_DPBP; i++) { - cmd_params->dpbp_id[i] = cpu_to_le32(cfg->pools[i].dpbp_id); + cmd_params->pool[i].dpbp_id = + cpu_to_le16(cfg->pools[i].dpbp_id); + cmd_params->pool[i].priority_mask = + cfg->pools[i].priority_mask; cmd_params->buffer_size[i] = cpu_to_le16(cfg->pools[i].buffer_size); cmd_params->backup_pool_mask |= diff --git a/drivers/net/ethernet/freescale/dpaa2/dpni.h b/drivers/net/ethernet/freescale/dpaa2/dpni.h index 6fffd519aa00..5c0a1d5ac934 100644 --- a/drivers/net/ethernet/freescale/dpaa2/dpni.h +++ b/drivers/net/ethernet/freescale/dpaa2/dpni.h @@ -92,19 +92,28 @@ int dpni_close(struct fsl_mc_io *mc_io, u32 cmd_flags, u16 token); +#define DPNI_POOL_ASSOC_QPRI 0 +#define DPNI_POOL_ASSOC_QDBIN 1 + /** * struct dpni_pools_cfg - Structure representing buffer pools configuration * @num_dpbp: Number of DPBPs + * @pool_options: Buffer assignment options. + * This field is a combination of DPNI_POOL_ASSOC_flags * @pools: Array of buffer pools parameters; The number of valid entries * must match 'num_dpbp' value * @pools.dpbp_id: DPBP object ID + * @pools.priority: Priority mask that indicates TC's used with this buffer. + * If set to 0x00 MC will assume value 0xff. * @pools.buffer_size: Buffer size * @pools.backup_pool: Backup pool */ struct dpni_pools_cfg { u8 num_dpbp; + u8 pool_options; struct { int dpbp_id; + u8 priority_mask; u16 buffer_size; int backup_pool; } pools[DPNI_MAX_DPBP]; From e3caeb2ddbf2b6e876b28a5ecd3357267002682f Mon Sep 17 00:00:00 2001 From: Ioana Ciornei Date: Tue, 18 Oct 2022 17:18:56 +0300 Subject: [PATCH 0263/2002] net: dpaa2-eth: use dev_close/open instead of the internal functions Instead of calling the internal functions which implement .ndo_stop and .ndo_open, we can simply call dev_close and dev_open, so that we keep the code cleaner. Also, in the next patches we'll use the same APIs from other files without needing to export the internal functions. Signed-off-by: Ioana Ciornei Signed-off-by: David S. Miller --- drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c index 1213ae4e1301..b95e9a7123e0 100644 --- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c +++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c @@ -2626,7 +2626,7 @@ static int dpaa2_eth_setup_xdp(struct net_device *dev, struct bpf_prog *prog) need_update = (!!priv->xdp_prog != !!prog); if (up) - dpaa2_eth_stop(dev); + dev_close(dev); /* While in xdp mode, enforce a maximum Rx frame size based on MTU. * Also, when switching between xdp/non-xdp modes we need to reconfigure @@ -2654,7 +2654,7 @@ static int dpaa2_eth_setup_xdp(struct net_device *dev, struct bpf_prog *prog) } if (up) { - err = dpaa2_eth_open(dev); + err = dev_open(dev, NULL); if (err) return err; } @@ -2665,7 +2665,7 @@ out_err: if (prog) bpf_prog_sub(prog, priv->num_channels); if (up) - dpaa2_eth_open(dev); + dev_open(dev, NULL); return err; } From 129902a351bf7593c2a1e81a26900a7648845b5e Mon Sep 17 00:00:00 2001 From: Robert-Ionut Alexa Date: Tue, 18 Oct 2022 17:18:57 +0300 Subject: [PATCH 0264/2002] net: dpaa2-eth: create and export the dpaa2_eth_alloc_skb function The dpaa2_eth_alloc_skb() function is added by moving code from the dpaa2_eth_copybreak() previously defined function. What the new API does is to allocate a new skb, copy the frame data from the passed FD to the new skb and then return the skb. Export this new function since we'll need the this functionality also from the XSK code path. Signed-off-by: Robert-Ionut Alexa Signed-off-by: Ioana Ciornei Signed-off-by: David S. Miller --- .../net/ethernet/freescale/dpaa2/dpaa2-eth.c | 25 +++++++++++++------ .../net/ethernet/freescale/dpaa2/dpaa2-eth.h | 5 ++++ 2 files changed, 22 insertions(+), 8 deletions(-) diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c index b95e9a7123e0..9909fdf084e3 100644 --- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c +++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c @@ -485,19 +485,15 @@ out: return xdp_act; } -static struct sk_buff *dpaa2_eth_copybreak(struct dpaa2_eth_channel *ch, - const struct dpaa2_fd *fd, - void *fd_vaddr) +struct sk_buff *dpaa2_eth_alloc_skb(struct dpaa2_eth_priv *priv, + struct dpaa2_eth_channel *ch, + const struct dpaa2_fd *fd, u32 fd_length, + void *fd_vaddr) { u16 fd_offset = dpaa2_fd_get_offset(fd); - struct dpaa2_eth_priv *priv = ch->priv; - u32 fd_length = dpaa2_fd_get_len(fd); struct sk_buff *skb = NULL; unsigned int skb_len; - if (fd_length > priv->rx_copybreak) - return NULL; - skb_len = fd_length + dpaa2_eth_needed_headroom(NULL); skb = napi_alloc_skb(&ch->napi, skb_len); @@ -514,6 +510,19 @@ static struct sk_buff *dpaa2_eth_copybreak(struct dpaa2_eth_channel *ch, return skb; } +static struct sk_buff *dpaa2_eth_copybreak(struct dpaa2_eth_channel *ch, + const struct dpaa2_fd *fd, + void *fd_vaddr) +{ + struct dpaa2_eth_priv *priv = ch->priv; + u32 fd_length = dpaa2_fd_get_len(fd); + + if (fd_length > priv->rx_copybreak) + return NULL; + + return dpaa2_eth_alloc_skb(priv, ch, fd, fd_length, fd_vaddr); +} + /* Main Rx frame processing routine */ static void dpaa2_eth_rx(struct dpaa2_eth_priv *priv, struct dpaa2_eth_channel *ch, diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.h b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.h index bb0881e7033b..7c46ec37b29a 100644 --- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.h +++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.h @@ -791,4 +791,9 @@ struct dpaa2_eth_trap_item *dpaa2_eth_dl_get_trap(struct dpaa2_eth_priv *priv, struct dpaa2_eth_bp *dpaa2_eth_allocate_dpbp(struct dpaa2_eth_priv *priv); void dpaa2_eth_free_dpbp(struct dpaa2_eth_priv *priv, struct dpaa2_eth_bp *bp); + +struct sk_buff *dpaa2_eth_alloc_skb(struct dpaa2_eth_priv *priv, + struct dpaa2_eth_channel *ch, + const struct dpaa2_fd *fd, u32 fd_length, + void *fd_vaddr); #endif /* __DPAA2_H */ From ee2a3bdef94bf823483ca237552aad7bd374baa0 Mon Sep 17 00:00:00 2001 From: Robert-Ionut Alexa Date: Tue, 18 Oct 2022 17:18:58 +0300 Subject: [PATCH 0265/2002] net: dpaa2-eth: create and export the dpaa2_eth_receive_skb() function Carve out code from the dpaa2_eth_rx() function in order to create and export the dpaa2_eth_receive_skb() function. Do this in order to reuse this code also from the XSK path which will be introduced in a later patch. Signed-off-by: Robert-Ionut Alexa Signed-off-by: Ioana Ciornei Signed-off-by: David S. Miller --- .../net/ethernet/freescale/dpaa2/dpaa2-eth.c | 84 +++++++++++-------- .../net/ethernet/freescale/dpaa2/dpaa2-eth.h | 12 +++ 2 files changed, 59 insertions(+), 37 deletions(-) diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c index 9909fdf084e3..0b21a3ee1bf1 100644 --- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c +++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c @@ -523,11 +523,53 @@ static struct sk_buff *dpaa2_eth_copybreak(struct dpaa2_eth_channel *ch, return dpaa2_eth_alloc_skb(priv, ch, fd, fd_length, fd_vaddr); } +void dpaa2_eth_receive_skb(struct dpaa2_eth_priv *priv, + struct dpaa2_eth_channel *ch, + const struct dpaa2_fd *fd, void *vaddr, + struct dpaa2_eth_fq *fq, + struct rtnl_link_stats64 *percpu_stats, + struct sk_buff *skb) +{ + struct dpaa2_fas *fas; + u32 status = 0; + + fas = dpaa2_get_fas(vaddr, false); + prefetch(fas); + prefetch(skb->data); + + /* Get the timestamp value */ + if (priv->rx_tstamp) { + struct skb_shared_hwtstamps *shhwtstamps = skb_hwtstamps(skb); + __le64 *ts = dpaa2_get_ts(vaddr, false); + u64 ns; + + memset(shhwtstamps, 0, sizeof(*shhwtstamps)); + + ns = DPAA2_PTP_CLK_PERIOD_NS * le64_to_cpup(ts); + shhwtstamps->hwtstamp = ns_to_ktime(ns); + } + + /* Check if we need to validate the L4 csum */ + if (likely(dpaa2_fd_get_frc(fd) & DPAA2_FD_FRC_FASV)) { + status = le32_to_cpu(fas->status); + dpaa2_eth_validate_rx_csum(priv, status, skb); + } + + skb->protocol = eth_type_trans(skb, priv->net_dev); + skb_record_rx_queue(skb, fq->flowid); + + percpu_stats->rx_packets++; + percpu_stats->rx_bytes += dpaa2_fd_get_len(fd); + ch->stats.bytes_per_cdan += dpaa2_fd_get_len(fd); + + list_add_tail(&skb->list, ch->rx_list); +} + /* Main Rx frame processing routine */ -static void dpaa2_eth_rx(struct dpaa2_eth_priv *priv, - struct dpaa2_eth_channel *ch, - const struct dpaa2_fd *fd, - struct dpaa2_eth_fq *fq) +void dpaa2_eth_rx(struct dpaa2_eth_priv *priv, + struct dpaa2_eth_channel *ch, + const struct dpaa2_fd *fd, + struct dpaa2_eth_fq *fq) { dma_addr_t addr = dpaa2_fd_get_addr(fd); u8 fd_format = dpaa2_fd_get_format(fd); @@ -536,9 +578,7 @@ static void dpaa2_eth_rx(struct dpaa2_eth_priv *priv, struct rtnl_link_stats64 *percpu_stats; struct dpaa2_eth_drv_stats *percpu_extras; struct device *dev = priv->net_dev->dev.parent; - struct dpaa2_fas *fas; void *buf_data; - u32 status = 0; u32 xdp_act; /* Tracing point */ @@ -548,8 +588,6 @@ static void dpaa2_eth_rx(struct dpaa2_eth_priv *priv, dma_sync_single_for_cpu(dev, addr, priv->rx_buf_size, DMA_BIDIRECTIONAL); - fas = dpaa2_get_fas(vaddr, false); - prefetch(fas); buf_data = vaddr + dpaa2_fd_get_offset(fd); prefetch(buf_data); @@ -587,35 +625,7 @@ static void dpaa2_eth_rx(struct dpaa2_eth_priv *priv, if (unlikely(!skb)) goto err_build_skb; - prefetch(skb->data); - - /* Get the timestamp value */ - if (priv->rx_tstamp) { - struct skb_shared_hwtstamps *shhwtstamps = skb_hwtstamps(skb); - __le64 *ts = dpaa2_get_ts(vaddr, false); - u64 ns; - - memset(shhwtstamps, 0, sizeof(*shhwtstamps)); - - ns = DPAA2_PTP_CLK_PERIOD_NS * le64_to_cpup(ts); - shhwtstamps->hwtstamp = ns_to_ktime(ns); - } - - /* Check if we need to validate the L4 csum */ - if (likely(dpaa2_fd_get_frc(fd) & DPAA2_FD_FRC_FASV)) { - status = le32_to_cpu(fas->status); - dpaa2_eth_validate_rx_csum(priv, status, skb); - } - - skb->protocol = eth_type_trans(skb, priv->net_dev); - skb_record_rx_queue(skb, fq->flowid); - - percpu_stats->rx_packets++; - percpu_stats->rx_bytes += dpaa2_fd_get_len(fd); - ch->stats.bytes_per_cdan += dpaa2_fd_get_len(fd); - - list_add_tail(&skb->list, ch->rx_list); - + dpaa2_eth_receive_skb(priv, ch, fd, vaddr, fq, percpu_stats, skb); return; err_build_skb: diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.h b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.h index 7c46ec37b29a..3c4fc46b1324 100644 --- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.h +++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.h @@ -796,4 +796,16 @@ struct sk_buff *dpaa2_eth_alloc_skb(struct dpaa2_eth_priv *priv, struct dpaa2_eth_channel *ch, const struct dpaa2_fd *fd, u32 fd_length, void *fd_vaddr); + +void dpaa2_eth_receive_skb(struct dpaa2_eth_priv *priv, + struct dpaa2_eth_channel *ch, + const struct dpaa2_fd *fd, void *vaddr, + struct dpaa2_eth_fq *fq, + struct rtnl_link_stats64 *percpu_stats, + struct sk_buff *skb); + +void dpaa2_eth_rx(struct dpaa2_eth_priv *priv, + struct dpaa2_eth_channel *ch, + const struct dpaa2_fd *fd, + struct dpaa2_eth_fq *fq); #endif /* __DPAA2_H */ From 48276c08cf5d2039aad5ef92ae7057ae0946d51e Mon Sep 17 00:00:00 2001 From: Robert-Ionut Alexa Date: Tue, 18 Oct 2022 17:18:59 +0300 Subject: [PATCH 0266/2002] net: dpaa2-eth: AF_XDP RX zero copy support This patch adds the support for receiving packets via the AF_XDP zero-copy mechanism in the dpaa2-eth driver. The support is available only on the LX2160A SoC and variants because we are relying on the HW capability to associate a buffer pool to a specific queue (QDBIN), only available on newer WRIOP versions. On the control path, the dpaa2_xsk_enable_pool() function is responsible to allocate a buffer pool (BP), setup this new BP to be used only on the requested queue and change the consume function to point to the XSK ZC one. We are forced to call dev_close() in order to change the queue to buffer pool association (dpaa2_xsk_set_bp_per_qdbin) . This also works in our favor since at dev_close() the buffer pools will be drained and at the later dev_open() call they will be again seeded, this time with buffers allocated from the XSK pool if needed. On the data path, a new software annotation type is defined to be used only for the XSK scenarios. This will enable us to pass keep necessary information about a packet buffer between the moment in which it was seeded and when it's received by the driver. In the XSK case, we are keeping the associated xdp_buff. Depending on the action returned by the BPF program, we will do the following: - XDP_PASS: copy the contents of the packet into a brand new skb, recycle the initial buffer. - XDP_TX: just enqueue the same frame descriptor back into the Tx path, the buffer will get automatically released into the initial BP. - XDP_REDIRECT: call xdp_do_redirect() and exit. Signed-off-by: Robert-Ionut Alexa Signed-off-by: Ioana Ciornei Signed-off-by: David S. Miller --- MAINTAINERS | 1 + drivers/net/ethernet/freescale/dpaa2/Makefile | 2 +- .../net/ethernet/freescale/dpaa2/dpaa2-eth.c | 129 ++++--- .../net/ethernet/freescale/dpaa2/dpaa2-eth.h | 36 +- .../net/ethernet/freescale/dpaa2/dpaa2-xsk.c | 327 ++++++++++++++++++ 5 files changed, 451 insertions(+), 44 deletions(-) create mode 100644 drivers/net/ethernet/freescale/dpaa2/dpaa2-xsk.c diff --git a/MAINTAINERS b/MAINTAINERS index 92708912fef8..479a15d86a91 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -6326,6 +6326,7 @@ F: drivers/net/ethernet/freescale/dpaa2/Kconfig F: drivers/net/ethernet/freescale/dpaa2/Makefile F: drivers/net/ethernet/freescale/dpaa2/dpaa2-eth* F: drivers/net/ethernet/freescale/dpaa2/dpaa2-mac* +F: drivers/net/ethernet/freescale/dpaa2/dpaa2-xsk* F: drivers/net/ethernet/freescale/dpaa2/dpkg.h F: drivers/net/ethernet/freescale/dpaa2/dpmac* F: drivers/net/ethernet/freescale/dpaa2/dpni* diff --git a/drivers/net/ethernet/freescale/dpaa2/Makefile b/drivers/net/ethernet/freescale/dpaa2/Makefile index 3d9842af7f10..1b05ba8d1cbf 100644 --- a/drivers/net/ethernet/freescale/dpaa2/Makefile +++ b/drivers/net/ethernet/freescale/dpaa2/Makefile @@ -7,7 +7,7 @@ obj-$(CONFIG_FSL_DPAA2_ETH) += fsl-dpaa2-eth.o obj-$(CONFIG_FSL_DPAA2_PTP_CLOCK) += fsl-dpaa2-ptp.o obj-$(CONFIG_FSL_DPAA2_SWITCH) += fsl-dpaa2-switch.o -fsl-dpaa2-eth-objs := dpaa2-eth.o dpaa2-ethtool.o dpni.o dpaa2-mac.o dpmac.o dpaa2-eth-devlink.o +fsl-dpaa2-eth-objs := dpaa2-eth.o dpaa2-ethtool.o dpni.o dpaa2-mac.o dpmac.o dpaa2-eth-devlink.o dpaa2-xsk.o fsl-dpaa2-eth-${CONFIG_FSL_DPAA2_ETH_DCB} += dpaa2-eth-dcb.o fsl-dpaa2-eth-${CONFIG_DEBUG_FS} += dpaa2-eth-debugfs.o fsl-dpaa2-ptp-objs := dpaa2-ptp.o dprtc.o diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c index 0b21a3ee1bf1..4c340f70f50e 100644 --- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c +++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c @@ -19,6 +19,7 @@ #include #include #include +#include #include "dpaa2-eth.h" @@ -104,8 +105,8 @@ static void dpaa2_ptp_onestep_reg_update_method(struct dpaa2_eth_priv *priv) priv->dpaa2_set_onestep_params_cb = dpaa2_update_ptp_onestep_direct; } -static void *dpaa2_iova_to_virt(struct iommu_domain *domain, - dma_addr_t iova_addr) +void *dpaa2_iova_to_virt(struct iommu_domain *domain, + dma_addr_t iova_addr) { phys_addr_t phys_addr; @@ -279,23 +280,33 @@ static struct sk_buff *dpaa2_eth_build_frag_skb(struct dpaa2_eth_priv *priv, * be released in the pool */ static void dpaa2_eth_free_bufs(struct dpaa2_eth_priv *priv, u64 *buf_array, - int count) + int count, bool xsk_zc) { struct device *dev = priv->net_dev->dev.parent; + struct dpaa2_eth_swa *swa; + struct xdp_buff *xdp_buff; void *vaddr; int i; for (i = 0; i < count; i++) { vaddr = dpaa2_iova_to_virt(priv->iommu_domain, buf_array[i]); - dma_unmap_page(dev, buf_array[i], priv->rx_buf_size, - DMA_BIDIRECTIONAL); - free_pages((unsigned long)vaddr, 0); + + if (!xsk_zc) { + dma_unmap_page(dev, buf_array[i], priv->rx_buf_size, + DMA_BIDIRECTIONAL); + free_pages((unsigned long)vaddr, 0); + } else { + swa = (struct dpaa2_eth_swa *) + (vaddr + DPAA2_ETH_RX_HWA_SIZE); + xdp_buff = swa->xsk.xdp_buff; + xsk_buff_free(xdp_buff); + } } } -static void dpaa2_eth_recycle_buf(struct dpaa2_eth_priv *priv, - struct dpaa2_eth_channel *ch, - dma_addr_t addr) +void dpaa2_eth_recycle_buf(struct dpaa2_eth_priv *priv, + struct dpaa2_eth_channel *ch, + dma_addr_t addr) { int retries = 0; int err; @@ -313,7 +324,8 @@ static void dpaa2_eth_recycle_buf(struct dpaa2_eth_priv *priv, } if (err) { - dpaa2_eth_free_bufs(priv, ch->recycled_bufs, ch->recycled_bufs_cnt); + dpaa2_eth_free_bufs(priv, ch->recycled_bufs, + ch->recycled_bufs_cnt, ch->xsk_zc); ch->buf_count -= ch->recycled_bufs_cnt; } @@ -377,10 +389,10 @@ static void dpaa2_eth_xdp_tx_flush(struct dpaa2_eth_priv *priv, fq->xdp_tx_fds.num = 0; } -static void dpaa2_eth_xdp_enqueue(struct dpaa2_eth_priv *priv, - struct dpaa2_eth_channel *ch, - struct dpaa2_fd *fd, - void *buf_start, u16 queue_id) +void dpaa2_eth_xdp_enqueue(struct dpaa2_eth_priv *priv, + struct dpaa2_eth_channel *ch, + struct dpaa2_fd *fd, + void *buf_start, u16 queue_id) { struct dpaa2_faead *faead; struct dpaa2_fd *dest_fd; @@ -1652,37 +1664,63 @@ static int dpaa2_eth_set_tx_csum(struct dpaa2_eth_priv *priv, bool enable) static int dpaa2_eth_add_bufs(struct dpaa2_eth_priv *priv, struct dpaa2_eth_channel *ch) { + struct xdp_buff *xdp_buffs[DPAA2_ETH_BUFS_PER_CMD]; struct device *dev = priv->net_dev->dev.parent; u64 buf_array[DPAA2_ETH_BUFS_PER_CMD]; + struct dpaa2_eth_swa *swa; struct page *page; dma_addr_t addr; int retries = 0; - int i, err; + int i = 0, err; + u32 batch; - for (i = 0; i < DPAA2_ETH_BUFS_PER_CMD; i++) { - /* Allocate buffer visible to WRIOP + skb shared info + - * alignment padding + /* Allocate buffers visible to WRIOP */ + if (!ch->xsk_zc) { + for (i = 0; i < DPAA2_ETH_BUFS_PER_CMD; i++) { + /* Also allocate skb shared info and alignment padding. + * There is one page for each Rx buffer. WRIOP sees + * the entire page except for a tailroom reserved for + * skb shared info + */ + page = dev_alloc_pages(0); + if (!page) + goto err_alloc; + + addr = dma_map_page(dev, page, 0, priv->rx_buf_size, + DMA_BIDIRECTIONAL); + if (unlikely(dma_mapping_error(dev, addr))) + goto err_map; + + buf_array[i] = addr; + + /* tracing point */ + trace_dpaa2_eth_buf_seed(priv->net_dev, + page_address(page), + DPAA2_ETH_RX_BUF_RAW_SIZE, + addr, priv->rx_buf_size, + ch->bp->bpid); + } + } else if (xsk_buff_can_alloc(ch->xsk_pool, DPAA2_ETH_BUFS_PER_CMD)) { + /* Allocate XSK buffers for AF_XDP fast path in batches + * of DPAA2_ETH_BUFS_PER_CMD. Bail out if the UMEM cannot + * provide enough buffers at the moment */ - /* allocate one page for each Rx buffer. WRIOP sees - * the entire page except for a tailroom reserved for - * skb shared info - */ - page = dev_alloc_pages(0); - if (!page) + batch = xsk_buff_alloc_batch(ch->xsk_pool, xdp_buffs, + DPAA2_ETH_BUFS_PER_CMD); + if (!batch) goto err_alloc; - addr = dma_map_page(dev, page, 0, priv->rx_buf_size, - DMA_BIDIRECTIONAL); - if (unlikely(dma_mapping_error(dev, addr))) - goto err_map; + for (i = 0; i < batch; i++) { + swa = (struct dpaa2_eth_swa *)(xdp_buffs[i]->data_hard_start + + DPAA2_ETH_RX_HWA_SIZE); + swa->xsk.xdp_buff = xdp_buffs[i]; - buf_array[i] = addr; + addr = xsk_buff_xdp_get_frame_dma(xdp_buffs[i]); + if (unlikely(dma_mapping_error(dev, addr))) + goto err_map; - /* tracing point */ - trace_dpaa2_eth_buf_seed(priv->net_dev, page_address(page), - DPAA2_ETH_RX_BUF_RAW_SIZE, - addr, priv->rx_buf_size, - ch->bp->bpid); + buf_array[i] = addr; + } } release_bufs: @@ -1698,14 +1736,19 @@ release_bufs: * not much else we can do about it */ if (err) { - dpaa2_eth_free_bufs(priv, buf_array, i); + dpaa2_eth_free_bufs(priv, buf_array, i, ch->xsk_zc); return 0; } return i; err_map: - __free_pages(page, 0); + if (!ch->xsk_zc) { + __free_pages(page, 0); + } else { + for (; i < batch; i++) + xsk_buff_free(xdp_buffs[i]); + } err_alloc: /* If we managed to allocate at least some buffers, * release them to hardware @@ -1764,8 +1807,13 @@ static void dpaa2_eth_drain_bufs(struct dpaa2_eth_priv *priv, int bpid, int count) { u64 buf_array[DPAA2_ETH_BUFS_PER_CMD]; + bool xsk_zc = false; int retries = 0; - int ret; + int i, ret; + + for (i = 0; i < priv->num_channels; i++) + if (priv->channel[i]->bp->bpid == bpid) + xsk_zc = priv->channel[i]->xsk_zc; do { ret = dpaa2_io_service_acquire(NULL, bpid, buf_array, count); @@ -1776,7 +1824,7 @@ static void dpaa2_eth_drain_bufs(struct dpaa2_eth_priv *priv, int bpid, netdev_err(priv->net_dev, "dpaa2_io_service_acquire() failed\n"); return; } - dpaa2_eth_free_bufs(priv, buf_array, ret); + dpaa2_eth_free_bufs(priv, buf_array, ret, xsk_zc); retries = 0; } while (ret); } @@ -2694,6 +2742,8 @@ static int dpaa2_eth_xdp(struct net_device *dev, struct netdev_bpf *xdp) switch (xdp->command) { case XDP_SETUP_PROG: return dpaa2_eth_setup_xdp(dev, xdp->prog); + case XDP_SETUP_XSK_POOL: + return dpaa2_xsk_setup_pool(dev, xdp->xsk.pool, xdp->xsk.queue_id); default: return -EINVAL; } @@ -2924,6 +2974,7 @@ static const struct net_device_ops dpaa2_eth_ops = { .ndo_change_mtu = dpaa2_eth_change_mtu, .ndo_bpf = dpaa2_eth_xdp, .ndo_xdp_xmit = dpaa2_eth_xdp_xmit, + .ndo_xsk_wakeup = dpaa2_xsk_wakeup, .ndo_setup_tc = dpaa2_eth_setup_tc, .ndo_vlan_rx_add_vid = dpaa2_eth_rx_add_vid, .ndo_vlan_rx_kill_vid = dpaa2_eth_rx_kill_vid @@ -4247,8 +4298,8 @@ static int dpaa2_eth_bind_dpni(struct dpaa2_eth_priv *priv) { struct dpaa2_eth_bp *bp = priv->bp[DPAA2_ETH_DEFAULT_BP_IDX]; struct net_device *net_dev = priv->net_dev; + struct dpni_pools_cfg pools_params = { 0 }; struct device *dev = net_dev->dev.parent; - struct dpni_pools_cfg pools_params; struct dpni_error_cfg err_cfg; int err = 0; int i; diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.h b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.h index 3c4fc46b1324..38f67b98865f 100644 --- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.h +++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.h @@ -130,6 +130,7 @@ enum dpaa2_eth_swa_type { DPAA2_ETH_SWA_SINGLE, DPAA2_ETH_SWA_SG, DPAA2_ETH_SWA_XDP, + DPAA2_ETH_SWA_XSK, DPAA2_ETH_SWA_SW_TSO, }; @@ -151,6 +152,9 @@ struct dpaa2_eth_swa { int dma_size; struct xdp_frame *xdpf; } xdp; + struct { + struct xdp_buff *xdp_buff; + } xsk; struct { struct sk_buff *skb; int num_sg; @@ -429,12 +433,19 @@ enum dpaa2_eth_fq_type { }; struct dpaa2_eth_priv; +struct dpaa2_eth_channel; +struct dpaa2_eth_fq; struct dpaa2_eth_xdp_fds { struct dpaa2_fd fds[DEV_MAP_BULK_SIZE]; ssize_t num; }; +typedef void dpaa2_eth_consume_cb_t(struct dpaa2_eth_priv *priv, + struct dpaa2_eth_channel *ch, + const struct dpaa2_fd *fd, + struct dpaa2_eth_fq *fq); + struct dpaa2_eth_fq { u32 fqid; u32 tx_qdbin; @@ -447,10 +458,7 @@ struct dpaa2_eth_fq { struct dpaa2_eth_channel *channel; enum dpaa2_eth_fq_type type; - void (*consume)(struct dpaa2_eth_priv *priv, - struct dpaa2_eth_channel *ch, - const struct dpaa2_fd *fd, - struct dpaa2_eth_fq *fq); + dpaa2_eth_consume_cb_t *consume; struct dpaa2_eth_fq_stats stats; struct dpaa2_eth_xdp_fds xdp_redirect_fds; @@ -486,6 +494,8 @@ struct dpaa2_eth_channel { u64 recycled_bufs[DPAA2_ETH_BUFS_PER_CMD]; int recycled_bufs_cnt; + bool xsk_zc; + struct xsk_buff_pool *xsk_pool; struct dpaa2_eth_bp *bp; }; @@ -808,4 +818,22 @@ void dpaa2_eth_rx(struct dpaa2_eth_priv *priv, struct dpaa2_eth_channel *ch, const struct dpaa2_fd *fd, struct dpaa2_eth_fq *fq); + +struct dpaa2_eth_bp *dpaa2_eth_allocate_dpbp(struct dpaa2_eth_priv *priv); +void dpaa2_eth_free_dpbp(struct dpaa2_eth_priv *priv, + struct dpaa2_eth_bp *bp); + +void *dpaa2_iova_to_virt(struct iommu_domain *domain, dma_addr_t iova_addr); +void dpaa2_eth_recycle_buf(struct dpaa2_eth_priv *priv, + struct dpaa2_eth_channel *ch, + dma_addr_t addr); + +void dpaa2_eth_xdp_enqueue(struct dpaa2_eth_priv *priv, + struct dpaa2_eth_channel *ch, + struct dpaa2_fd *fd, + void *buf_start, u16 queue_id); + +int dpaa2_xsk_wakeup(struct net_device *dev, u32 qid, u32 flags); +int dpaa2_xsk_setup_pool(struct net_device *dev, struct xsk_buff_pool *pool, u16 qid); + #endif /* __DPAA2_H */ diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-xsk.c b/drivers/net/ethernet/freescale/dpaa2/dpaa2-xsk.c new file mode 100644 index 000000000000..2df7bffec5a7 --- /dev/null +++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-xsk.c @@ -0,0 +1,327 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) +/* Copyright 2022 NXP + */ +#include +#include +#include +#include +#include + +#include "dpaa2-eth.h" + +static void dpaa2_eth_setup_consume_func(struct dpaa2_eth_priv *priv, + struct dpaa2_eth_channel *ch, + enum dpaa2_eth_fq_type type, + dpaa2_eth_consume_cb_t *consume) +{ + struct dpaa2_eth_fq *fq; + int i; + + for (i = 0; i < priv->num_fqs; i++) { + fq = &priv->fq[i]; + + if (fq->type != type) + continue; + if (fq->channel != ch) + continue; + + fq->consume = consume; + } +} + +static u32 dpaa2_xsk_run_xdp(struct dpaa2_eth_priv *priv, + struct dpaa2_eth_channel *ch, + struct dpaa2_eth_fq *rx_fq, + struct dpaa2_fd *fd, void *vaddr) +{ + dma_addr_t addr = dpaa2_fd_get_addr(fd); + struct bpf_prog *xdp_prog; + struct xdp_buff *xdp_buff; + struct dpaa2_eth_swa *swa; + u32 xdp_act = XDP_PASS; + int err; + + xdp_prog = READ_ONCE(ch->xdp.prog); + if (!xdp_prog) + goto out; + + swa = (struct dpaa2_eth_swa *)(vaddr + DPAA2_ETH_RX_HWA_SIZE + + ch->xsk_pool->umem->headroom); + xdp_buff = swa->xsk.xdp_buff; + + xdp_buff->data_hard_start = vaddr; + xdp_buff->data = vaddr + dpaa2_fd_get_offset(fd); + xdp_buff->data_end = xdp_buff->data + dpaa2_fd_get_len(fd); + xdp_set_data_meta_invalid(xdp_buff); + xdp_buff->rxq = &ch->xdp_rxq; + + xsk_buff_dma_sync_for_cpu(xdp_buff, ch->xsk_pool); + xdp_act = bpf_prog_run_xdp(xdp_prog, xdp_buff); + + /* xdp.data pointer may have changed */ + dpaa2_fd_set_offset(fd, xdp_buff->data - vaddr); + dpaa2_fd_set_len(fd, xdp_buff->data_end - xdp_buff->data); + + if (likely(xdp_act == XDP_REDIRECT)) { + err = xdp_do_redirect(priv->net_dev, xdp_buff, xdp_prog); + if (unlikely(err)) { + ch->stats.xdp_drop++; + dpaa2_eth_recycle_buf(priv, ch, addr); + } else { + ch->buf_count--; + ch->stats.xdp_redirect++; + } + + goto xdp_redir; + } + + switch (xdp_act) { + case XDP_PASS: + break; + case XDP_TX: + dpaa2_eth_xdp_enqueue(priv, ch, fd, vaddr, rx_fq->flowid); + break; + default: + bpf_warn_invalid_xdp_action(priv->net_dev, xdp_prog, xdp_act); + fallthrough; + case XDP_ABORTED: + trace_xdp_exception(priv->net_dev, xdp_prog, xdp_act); + fallthrough; + case XDP_DROP: + dpaa2_eth_recycle_buf(priv, ch, addr); + ch->stats.xdp_drop++; + break; + } + +xdp_redir: + ch->xdp.res |= xdp_act; +out: + return xdp_act; +} + +/* Rx frame processing routine for the AF_XDP fast path */ +static void dpaa2_xsk_rx(struct dpaa2_eth_priv *priv, + struct dpaa2_eth_channel *ch, + const struct dpaa2_fd *fd, + struct dpaa2_eth_fq *fq) +{ + dma_addr_t addr = dpaa2_fd_get_addr(fd); + u8 fd_format = dpaa2_fd_get_format(fd); + struct rtnl_link_stats64 *percpu_stats; + u32 fd_length = dpaa2_fd_get_len(fd); + struct sk_buff *skb; + void *vaddr; + u32 xdp_act; + + vaddr = dpaa2_iova_to_virt(priv->iommu_domain, addr); + percpu_stats = this_cpu_ptr(priv->percpu_stats); + + if (fd_format != dpaa2_fd_single) { + WARN_ON(priv->xdp_prog); + /* AF_XDP doesn't support any other formats */ + goto err_frame_format; + } + + xdp_act = dpaa2_xsk_run_xdp(priv, ch, fq, (struct dpaa2_fd *)fd, vaddr); + if (xdp_act != XDP_PASS) { + percpu_stats->rx_packets++; + percpu_stats->rx_bytes += dpaa2_fd_get_len(fd); + return; + } + + /* Build skb */ + skb = dpaa2_eth_alloc_skb(priv, ch, fd, fd_length, vaddr); + if (!skb) + /* Nothing else we can do, recycle the buffer and + * drop the frame. + */ + goto err_alloc_skb; + + /* Send the skb to the Linux networking stack */ + dpaa2_eth_receive_skb(priv, ch, fd, vaddr, fq, percpu_stats, skb); + + return; + +err_alloc_skb: + dpaa2_eth_recycle_buf(priv, ch, addr); +err_frame_format: + percpu_stats->rx_dropped++; +} + +static void dpaa2_xsk_set_bp_per_qdbin(struct dpaa2_eth_priv *priv, + struct dpni_pools_cfg *pools_params) +{ + int curr_bp = 0, i, j; + + pools_params->pool_options = DPNI_POOL_ASSOC_QDBIN; + for (i = 0; i < priv->num_bps; i++) { + for (j = 0; j < priv->num_channels; j++) + if (priv->bp[i] == priv->channel[j]->bp) + pools_params->pools[curr_bp].priority_mask |= (1 << j); + if (!pools_params->pools[curr_bp].priority_mask) + continue; + + pools_params->pools[curr_bp].dpbp_id = priv->bp[i]->bpid; + pools_params->pools[curr_bp].buffer_size = priv->rx_buf_size; + pools_params->pools[curr_bp++].backup_pool = 0; + } + pools_params->num_dpbp = curr_bp; +} + +static int dpaa2_xsk_disable_pool(struct net_device *dev, u16 qid) +{ + struct xsk_buff_pool *pool = xsk_get_pool_from_qid(dev, qid); + struct dpaa2_eth_priv *priv = netdev_priv(dev); + struct dpni_pools_cfg pools_params = { 0 }; + struct dpaa2_eth_channel *ch; + int err; + bool up; + + ch = priv->channel[qid]; + if (!ch->xsk_pool) + return -EINVAL; + + up = netif_running(dev); + if (up) + dev_close(dev); + + xsk_pool_dma_unmap(pool, 0); + err = xdp_rxq_info_reg_mem_model(&ch->xdp_rxq, + MEM_TYPE_PAGE_ORDER0, NULL); + if (err) + netdev_err(dev, "xsk_rxq_info_reg_mem_model() failed (err = %d)\n", + err); + + dpaa2_eth_free_dpbp(priv, ch->bp); + + ch->xsk_zc = false; + ch->xsk_pool = NULL; + ch->bp = priv->bp[DPAA2_ETH_DEFAULT_BP_IDX]; + + dpaa2_eth_setup_consume_func(priv, ch, DPAA2_RX_FQ, dpaa2_eth_rx); + + dpaa2_xsk_set_bp_per_qdbin(priv, &pools_params); + err = dpni_set_pools(priv->mc_io, 0, priv->mc_token, &pools_params); + if (err) + netdev_err(dev, "dpni_set_pools() failed\n"); + + if (up) { + err = dev_open(dev, NULL); + if (err) + return err; + } + + return 0; +} + +static int dpaa2_xsk_enable_pool(struct net_device *dev, + struct xsk_buff_pool *pool, + u16 qid) +{ + struct dpaa2_eth_priv *priv = netdev_priv(dev); + struct dpni_pools_cfg pools_params = { 0 }; + struct dpaa2_eth_channel *ch; + int err, err2; + bool up; + + if (priv->dpni_attrs.wriop_version < DPAA2_WRIOP_VERSION(3, 0, 0)) { + netdev_err(dev, "AF_XDP zero-copy not supported on devices <= WRIOP(3, 0, 0)\n"); + return -EOPNOTSUPP; + } + + if (priv->dpni_attrs.num_queues > 8) { + netdev_err(dev, "AF_XDP zero-copy not supported on DPNI with more then 8 queues\n"); + return -EOPNOTSUPP; + } + + up = netif_running(dev); + if (up) + dev_close(dev); + + err = xsk_pool_dma_map(pool, priv->net_dev->dev.parent, 0); + if (err) { + netdev_err(dev, "xsk_pool_dma_map() failed (err = %d)\n", + err); + goto err_dma_unmap; + } + + ch = priv->channel[qid]; + err = xdp_rxq_info_reg_mem_model(&ch->xdp_rxq, MEM_TYPE_XSK_BUFF_POOL, NULL); + if (err) { + netdev_err(dev, "xdp_rxq_info_reg_mem_model() failed (err = %d)\n", err); + goto err_mem_model; + } + xsk_pool_set_rxq_info(pool, &ch->xdp_rxq); + + priv->bp[priv->num_bps] = dpaa2_eth_allocate_dpbp(priv); + if (IS_ERR(priv->bp[priv->num_bps])) { + err = PTR_ERR(priv->bp[priv->num_bps]); + goto err_bp_alloc; + } + ch->xsk_zc = true; + ch->xsk_pool = pool; + ch->bp = priv->bp[priv->num_bps++]; + + dpaa2_eth_setup_consume_func(priv, ch, DPAA2_RX_FQ, dpaa2_xsk_rx); + + dpaa2_xsk_set_bp_per_qdbin(priv, &pools_params); + err = dpni_set_pools(priv->mc_io, 0, priv->mc_token, &pools_params); + if (err) { + netdev_err(dev, "dpni_set_pools() failed\n"); + goto err_set_pools; + } + + if (up) { + err = dev_open(dev, NULL); + if (err) + return err; + } + + return 0; + +err_set_pools: + err2 = dpaa2_xsk_disable_pool(dev, qid); + if (err2) + netdev_err(dev, "dpaa2_xsk_disable_pool() failed %d\n", err2); +err_bp_alloc: + err2 = xdp_rxq_info_reg_mem_model(&priv->channel[qid]->xdp_rxq, + MEM_TYPE_PAGE_ORDER0, NULL); + if (err2) + netdev_err(dev, "xsk_rxq_info_reg_mem_model() failed with %d)\n", err2); +err_mem_model: + xsk_pool_dma_unmap(pool, 0); +err_dma_unmap: + if (up) + dev_open(dev, NULL); + + return err; +} + +int dpaa2_xsk_setup_pool(struct net_device *dev, struct xsk_buff_pool *pool, u16 qid) +{ + return pool ? dpaa2_xsk_enable_pool(dev, pool, qid) : + dpaa2_xsk_disable_pool(dev, qid); +} + +int dpaa2_xsk_wakeup(struct net_device *dev, u32 qid, u32 flags) +{ + struct dpaa2_eth_priv *priv = netdev_priv(dev); + struct dpaa2_eth_channel *ch = priv->channel[qid]; + + if (!priv->link_state.up) + return -ENETDOWN; + + if (!priv->xdp_prog) + return -EINVAL; + + if (!ch->xsk_zc) + return -EINVAL; + + /* We do not have access to a per channel SW interrupt, so instead we + * schedule a NAPI instance. + */ + if (!napi_if_scheduled_mark_missed(&ch->napi)) + napi_schedule(&ch->napi); + + return 0; +} From 4a7f6c5ac9e558b07a6cf9407ad28250b60980ab Mon Sep 17 00:00:00 2001 From: Robert-Ionut Alexa Date: Tue, 18 Oct 2022 17:19:00 +0300 Subject: [PATCH 0267/2002] net: dpaa2-eth: AF_XDP TX zero copy support Add support in dpaa2-eth for packet processing on the Tx path using AF_XDP zero copy mode. The newly added dpaa2_xsk_tx() function will handle enqueuing AF_XDP Tx packets into the appropriate queue and update any necessary statistics. On a more detailed note, the dpaa2_xsk_tx_build_fd() function handles creating a Scatter-Gather frame descriptor with only one data buffer. This is needed because otherwise we would need to impose a headroom in the Tx buffer to store our software annotation structures. This tactic is already used on the normal data path of the dpaa2-eth driver, thus we are reusing the dpaa2_eth_sgt_get/dpaa2_eth_sgt_recycle functions in order to allocate and recycle the Scatter-Gather table buffers. In case we have reached the maximum number of Tx XSK packets to be sent in a NAPI cycle, we'll exit the dpaa2_eth_poll() and hope to be rescheduled again. On the XSK Tx confirmation path, we are just unmapping the SGT buffer and recycle it for further use. Signed-off-by: Robert-Ionut Alexa Signed-off-by: Ioana Ciornei Signed-off-by: David S. Miller --- .../net/ethernet/freescale/dpaa2/dpaa2-eth.c | 48 +++++-- .../net/ethernet/freescale/dpaa2/dpaa2-eth.h | 22 +++- .../net/ethernet/freescale/dpaa2/dpaa2-xsk.c | 123 ++++++++++++++++++ 3 files changed, 183 insertions(+), 10 deletions(-) diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c index 4c340f70f50e..31eebeae5d95 100644 --- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c +++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c @@ -858,7 +858,7 @@ static void dpaa2_eth_enable_tx_tstamp(struct dpaa2_eth_priv *priv, } } -static void *dpaa2_eth_sgt_get(struct dpaa2_eth_priv *priv) +void *dpaa2_eth_sgt_get(struct dpaa2_eth_priv *priv) { struct dpaa2_eth_sgt_cache *sgt_cache; void *sgt_buf = NULL; @@ -880,7 +880,7 @@ static void *dpaa2_eth_sgt_get(struct dpaa2_eth_priv *priv) return sgt_buf; } -static void dpaa2_eth_sgt_recycle(struct dpaa2_eth_priv *priv, void *sgt_buf) +void dpaa2_eth_sgt_recycle(struct dpaa2_eth_priv *priv, void *sgt_buf) { struct dpaa2_eth_sgt_cache *sgt_cache; @@ -1115,9 +1115,10 @@ static int dpaa2_eth_build_single_fd(struct dpaa2_eth_priv *priv, * This can be called either from dpaa2_eth_tx_conf() or on the error path of * dpaa2_eth_tx(). */ -static void dpaa2_eth_free_tx_fd(struct dpaa2_eth_priv *priv, - struct dpaa2_eth_fq *fq, - const struct dpaa2_fd *fd, bool in_napi) +void dpaa2_eth_free_tx_fd(struct dpaa2_eth_priv *priv, + struct dpaa2_eth_channel *ch, + struct dpaa2_eth_fq *fq, + const struct dpaa2_fd *fd, bool in_napi) { struct device *dev = priv->net_dev->dev.parent; dma_addr_t fd_addr, sg_addr; @@ -1184,6 +1185,10 @@ static void dpaa2_eth_free_tx_fd(struct dpaa2_eth_priv *priv, if (!swa->tso.is_last_fd) should_free_skb = 0; + } else if (swa->type == DPAA2_ETH_SWA_XSK) { + /* Unmap the SGT Buffer */ + dma_unmap_single(dev, fd_addr, swa->xsk.sgt_size, + DMA_BIDIRECTIONAL); } else { skb = swa->single.skb; @@ -1201,6 +1206,12 @@ static void dpaa2_eth_free_tx_fd(struct dpaa2_eth_priv *priv, return; } + if (swa->type == DPAA2_ETH_SWA_XSK) { + ch->xsk_tx_pkts_sent++; + dpaa2_eth_sgt_recycle(priv, buffer_start); + return; + } + if (swa->type != DPAA2_ETH_SWA_XDP && in_napi) { fq->dq_frames++; fq->dq_bytes += fd_len; @@ -1375,7 +1386,7 @@ err_alloc_tso_hdr: err_sgt_get: /* Free all the other FDs that were already fully created */ for (i = 0; i < index; i++) - dpaa2_eth_free_tx_fd(priv, NULL, &fd_start[i], false); + dpaa2_eth_free_tx_fd(priv, NULL, NULL, &fd_start[i], false); return err; } @@ -1491,7 +1502,7 @@ static netdev_tx_t __dpaa2_eth_tx(struct sk_buff *skb, if (unlikely(err < 0)) { percpu_stats->tx_errors++; /* Clean up everything, including freeing the skb */ - dpaa2_eth_free_tx_fd(priv, fq, fd, false); + dpaa2_eth_free_tx_fd(priv, NULL, fq, fd, false); netdev_tx_completed_queue(nq, 1, fd_len); } else { percpu_stats->tx_packets += total_enqueued; @@ -1584,7 +1595,7 @@ static void dpaa2_eth_tx_conf(struct dpaa2_eth_priv *priv, /* Check frame errors in the FD field */ fd_errors = dpaa2_fd_get_ctrl(fd) & DPAA2_FD_TX_ERR_MASK; - dpaa2_eth_free_tx_fd(priv, fq, fd, true); + dpaa2_eth_free_tx_fd(priv, ch, fq, fd, true); if (likely(!fd_errors)) return; @@ -1929,6 +1940,7 @@ static int dpaa2_eth_poll(struct napi_struct *napi, int budget) struct dpaa2_eth_fq *fq, *txc_fq = NULL; struct netdev_queue *nq; int store_cleaned, work_done; + bool work_done_zc = false; struct list_head rx_list; int retries = 0; u16 flowid; @@ -1941,6 +1953,15 @@ static int dpaa2_eth_poll(struct napi_struct *napi, int budget) INIT_LIST_HEAD(&rx_list); ch->rx_list = &rx_list; + if (ch->xsk_zc) { + work_done_zc = dpaa2_xsk_tx(priv, ch); + /* If we reached the XSK Tx per NAPI threshold, we're done */ + if (work_done_zc) { + work_done = budget; + goto out; + } + } + do { err = dpaa2_eth_pull_channel(ch); if (unlikely(err)) @@ -1993,6 +2014,11 @@ static int dpaa2_eth_poll(struct napi_struct *napi, int budget) out: netif_receive_skb_list(ch->rx_list); + if (ch->xsk_tx_pkts_sent) { + xsk_tx_completed(ch->xsk_pool, ch->xsk_tx_pkts_sent); + ch->xsk_tx_pkts_sent = 0; + } + if (txc_fq && txc_fq->dq_frames) { nq = netdev_get_tx_queue(priv->net_dev, txc_fq->flowid); netdev_tx_completed_queue(nq, txc_fq->dq_frames, @@ -2989,7 +3015,11 @@ static void dpaa2_eth_cdan_cb(struct dpaa2_io_notification_ctx *ctx) /* Update NAPI statistics */ ch->stats.cdan++; - napi_schedule(&ch->napi); + /* NAPI can also be scheduled from the AF_XDP Tx path. Mark a missed + * so that it can be rescheduled again. + */ + if (!napi_if_scheduled_mark_missed(&ch->napi)) + napi_schedule(&ch->napi); } /* Allocate and configure a DPCON object */ diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.h b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.h index 38f67b98865f..5d0fc432e5b2 100644 --- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.h +++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.h @@ -53,6 +53,12 @@ */ #define DPAA2_ETH_TXCONF_PER_NAPI 256 +/* Maximum number of Tx frames to be processed in a single NAPI + * call when AF_XDP is running. Bind it to DPAA2_ETH_TXCONF_PER_NAPI + * to maximize the throughput. + */ +#define DPAA2_ETH_TX_ZC_PER_NAPI DPAA2_ETH_TXCONF_PER_NAPI + /* Buffer qouta per channel. We want to keep in check number of ingress frames * in flight: for small sized frames, congestion group taildrop may kick in * first; for large sizes, Rx FQ taildrop threshold will ensure only a @@ -154,6 +160,7 @@ struct dpaa2_eth_swa { } xdp; struct { struct xdp_buff *xdp_buff; + int sgt_size; } xsk; struct { struct sk_buff *skb; @@ -495,6 +502,7 @@ struct dpaa2_eth_channel { int recycled_bufs_cnt; bool xsk_zc; + int xsk_tx_pkts_sent; struct xsk_buff_pool *xsk_pool; struct dpaa2_eth_bp *bp; }; @@ -531,7 +539,7 @@ struct dpaa2_eth_trap_data { #define DPAA2_ETH_DEFAULT_COPYBREAK 512 -#define DPAA2_ETH_ENQUEUE_MAX_FDS 200 +#define DPAA2_ETH_ENQUEUE_MAX_FDS 256 struct dpaa2_eth_fds { struct dpaa2_fd array[DPAA2_ETH_ENQUEUE_MAX_FDS]; }; @@ -836,4 +844,16 @@ void dpaa2_eth_xdp_enqueue(struct dpaa2_eth_priv *priv, int dpaa2_xsk_wakeup(struct net_device *dev, u32 qid, u32 flags); int dpaa2_xsk_setup_pool(struct net_device *dev, struct xsk_buff_pool *pool, u16 qid); +void dpaa2_eth_free_tx_fd(struct dpaa2_eth_priv *priv, + struct dpaa2_eth_channel *ch, + struct dpaa2_eth_fq *fq, + const struct dpaa2_fd *fd, bool in_napi); +bool dpaa2_xsk_tx(struct dpaa2_eth_priv *priv, + struct dpaa2_eth_channel *ch); + +/* SGT (Scatter-Gather Table) cache management */ +void *dpaa2_eth_sgt_get(struct dpaa2_eth_priv *priv); + +void dpaa2_eth_sgt_recycle(struct dpaa2_eth_priv *priv, void *sgt_buf); + #endif /* __DPAA2_H */ diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-xsk.c b/drivers/net/ethernet/freescale/dpaa2/dpaa2-xsk.c index 2df7bffec5a7..731318842054 100644 --- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-xsk.c +++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-xsk.c @@ -196,6 +196,7 @@ static int dpaa2_xsk_disable_pool(struct net_device *dev, u16 qid) ch->xsk_zc = false; ch->xsk_pool = NULL; + ch->xsk_tx_pkts_sent = 0; ch->bp = priv->bp[DPAA2_ETH_DEFAULT_BP_IDX]; dpaa2_eth_setup_consume_func(priv, ch, DPAA2_RX_FQ, dpaa2_eth_rx); @@ -325,3 +326,125 @@ int dpaa2_xsk_wakeup(struct net_device *dev, u32 qid, u32 flags) return 0; } + +static int dpaa2_xsk_tx_build_fd(struct dpaa2_eth_priv *priv, + struct dpaa2_eth_channel *ch, + struct dpaa2_fd *fd, + struct xdp_desc *xdp_desc) +{ + struct device *dev = priv->net_dev->dev.parent; + struct dpaa2_sg_entry *sgt; + struct dpaa2_eth_swa *swa; + void *sgt_buf = NULL; + dma_addr_t sgt_addr; + int sgt_buf_size; + dma_addr_t addr; + int err = 0; + + /* Prepare the HW SGT structure */ + sgt_buf_size = priv->tx_data_offset + sizeof(struct dpaa2_sg_entry); + sgt_buf = dpaa2_eth_sgt_get(priv); + if (unlikely(!sgt_buf)) + return -ENOMEM; + sgt = (struct dpaa2_sg_entry *)(sgt_buf + priv->tx_data_offset); + + /* Get the address of the XSK Tx buffer */ + addr = xsk_buff_raw_get_dma(ch->xsk_pool, xdp_desc->addr); + xsk_buff_raw_dma_sync_for_device(ch->xsk_pool, addr, xdp_desc->len); + + /* Fill in the HW SGT structure */ + dpaa2_sg_set_addr(sgt, addr); + dpaa2_sg_set_len(sgt, xdp_desc->len); + dpaa2_sg_set_final(sgt, true); + + /* Store the necessary info in the SGT buffer */ + swa = (struct dpaa2_eth_swa *)sgt_buf; + swa->type = DPAA2_ETH_SWA_XSK; + swa->xsk.sgt_size = sgt_buf_size; + + /* Separately map the SGT buffer */ + sgt_addr = dma_map_single(dev, sgt_buf, sgt_buf_size, DMA_BIDIRECTIONAL); + if (unlikely(dma_mapping_error(dev, sgt_addr))) { + err = -ENOMEM; + goto sgt_map_failed; + } + + /* Initialize FD fields */ + memset(fd, 0, sizeof(struct dpaa2_fd)); + dpaa2_fd_set_offset(fd, priv->tx_data_offset); + dpaa2_fd_set_format(fd, dpaa2_fd_sg); + dpaa2_fd_set_addr(fd, sgt_addr); + dpaa2_fd_set_len(fd, xdp_desc->len); + dpaa2_fd_set_ctrl(fd, FD_CTRL_PTA); + + return 0; + +sgt_map_failed: + dpaa2_eth_sgt_recycle(priv, sgt_buf); + + return err; +} + +bool dpaa2_xsk_tx(struct dpaa2_eth_priv *priv, + struct dpaa2_eth_channel *ch) +{ + struct xdp_desc *xdp_descs = ch->xsk_pool->tx_descs; + struct dpaa2_eth_drv_stats *percpu_extras; + struct rtnl_link_stats64 *percpu_stats; + int budget = DPAA2_ETH_TX_ZC_PER_NAPI; + int total_enqueued, enqueued; + int retries, max_retries; + struct dpaa2_eth_fq *fq; + struct dpaa2_fd *fds; + int batch, i, err; + + percpu_stats = this_cpu_ptr(priv->percpu_stats); + percpu_extras = this_cpu_ptr(priv->percpu_extras); + fds = (this_cpu_ptr(priv->fd))->array; + + /* Use the FQ with the same idx as the affine CPU */ + fq = &priv->fq[ch->nctx.desired_cpu]; + + batch = xsk_tx_peek_release_desc_batch(ch->xsk_pool, budget); + if (!batch) + return false; + + /* Create a FD for each XSK frame to be sent */ + for (i = 0; i < batch; i++) { + err = dpaa2_xsk_tx_build_fd(priv, ch, &fds[i], &xdp_descs[i]); + if (err) { + batch = i; + break; + } + } + + /* Enqueue all the created FDs */ + max_retries = batch * DPAA2_ETH_ENQUEUE_RETRIES; + total_enqueued = 0; + enqueued = 0; + retries = 0; + while (total_enqueued < batch && retries < max_retries) { + err = priv->enqueue(priv, fq, &fds[total_enqueued], 0, + batch - total_enqueued, &enqueued); + if (err == -EBUSY) { + retries++; + continue; + } + + total_enqueued += enqueued; + } + percpu_extras->tx_portal_busy += retries; + + /* Update statistics */ + percpu_stats->tx_packets += total_enqueued; + for (i = 0; i < total_enqueued; i++) + percpu_stats->tx_bytes += dpaa2_fd_get_len(&fds[i]); + for (i = total_enqueued; i < batch; i++) { + dpaa2_eth_free_tx_fd(priv, ch, fq, &fds[i], false); + percpu_stats->tx_errors++; + } + + xsk_tx_release(ch->xsk_pool); + + return total_enqueued == budget ? true : false; +} From 3817b2ac71de2dd634a0d37bd548bb06c878d361 Mon Sep 17 00:00:00 2001 From: Robert-Ionut Alexa Date: Tue, 18 Oct 2022 17:19:01 +0300 Subject: [PATCH 0268/2002] net: dpaa2-eth: add trace points on XSK events Define the dpaa2_tx_xsk_fd and dpaa2_rx_xsk_fd trace events for the XSK zero-copy Rx and Tx path. Also, define the dpaa2_eth_buf as an event class so that both dpaa2_eth_buf_seed and dpaa2_xsk_buf_seed traces can derive from the same class. Signed-off-by: Robert-Ionut Alexa Signed-off-by: Ioana Ciornei Signed-off-by: David S. Miller --- .../freescale/dpaa2/dpaa2-eth-trace.h | 134 ++++++++++++------ .../net/ethernet/freescale/dpaa2/dpaa2-eth.c | 6 + .../net/ethernet/freescale/dpaa2/dpaa2-xsk.c | 4 + 3 files changed, 97 insertions(+), 47 deletions(-) diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth-trace.h b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth-trace.h index 5fb5f14e01ec..9b43fadb9b11 100644 --- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth-trace.h +++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth-trace.h @@ -73,6 +73,14 @@ DEFINE_EVENT(dpaa2_eth_fd, dpaa2_tx_fd, TP_ARGS(netdev, fd) ); +/* Tx (egress) XSK fd */ +DEFINE_EVENT(dpaa2_eth_fd, dpaa2_tx_xsk_fd, + TP_PROTO(struct net_device *netdev, + const struct dpaa2_fd *fd), + + TP_ARGS(netdev, fd) +); + /* Rx fd */ DEFINE_EVENT(dpaa2_eth_fd, dpaa2_rx_fd, TP_PROTO(struct net_device *netdev, @@ -81,6 +89,14 @@ DEFINE_EVENT(dpaa2_eth_fd, dpaa2_rx_fd, TP_ARGS(netdev, fd) ); +/* Rx XSK fd */ +DEFINE_EVENT(dpaa2_eth_fd, dpaa2_rx_xsk_fd, + TP_PROTO(struct net_device *netdev, + const struct dpaa2_fd *fd), + + TP_ARGS(netdev, fd) +); + /* Tx confirmation fd */ DEFINE_EVENT(dpaa2_eth_fd, dpaa2_tx_conf_fd, TP_PROTO(struct net_device *netdev, @@ -90,57 +106,81 @@ DEFINE_EVENT(dpaa2_eth_fd, dpaa2_tx_conf_fd, ); /* Log data about raw buffers. Useful for tracing DPBP content. */ -TRACE_EVENT(dpaa2_eth_buf_seed, - /* Trace function prototype */ - TP_PROTO(struct net_device *netdev, - /* virtual address and size */ - void *vaddr, - size_t size, - /* dma map address and size */ - dma_addr_t dma_addr, - size_t map_size, - /* buffer pool id, if relevant */ - u16 bpid), +DECLARE_EVENT_CLASS(dpaa2_eth_buf, + /* Trace function prototype */ + TP_PROTO(struct net_device *netdev, + /* virtual address and size */ + void *vaddr, + size_t size, + /* dma map address and size */ + dma_addr_t dma_addr, + size_t map_size, + /* buffer pool id, if relevant */ + u16 bpid), - /* Repeat argument list here */ - TP_ARGS(netdev, vaddr, size, dma_addr, map_size, bpid), + /* Repeat argument list here */ + TP_ARGS(netdev, vaddr, size, dma_addr, map_size, bpid), - /* A structure containing the relevant information we want - * to record. Declare name and type for each normal element, - * name, type and size for arrays. Use __string for variable - * length strings. - */ - TP_STRUCT__entry( - __field(void *, vaddr) - __field(size_t, size) - __field(dma_addr_t, dma_addr) - __field(size_t, map_size) - __field(u16, bpid) - __string(name, netdev->name) - ), + /* A structure containing the relevant information we want + * to record. Declare name and type for each normal element, + * name, type and size for arrays. Use __string for variable + * length strings. + */ + TP_STRUCT__entry( + __field(void *, vaddr) + __field(size_t, size) + __field(dma_addr_t, dma_addr) + __field(size_t, map_size) + __field(u16, bpid) + __string(name, netdev->name) + ), - /* The function that assigns values to the above declared - * fields - */ - TP_fast_assign( - __entry->vaddr = vaddr; - __entry->size = size; - __entry->dma_addr = dma_addr; - __entry->map_size = map_size; - __entry->bpid = bpid; - __assign_str(name, netdev->name); - ), + /* The function that assigns values to the above declared + * fields + */ + TP_fast_assign( + __entry->vaddr = vaddr; + __entry->size = size; + __entry->dma_addr = dma_addr; + __entry->map_size = map_size; + __entry->bpid = bpid; + __assign_str(name, netdev->name); + ), - /* This is what gets printed when the trace event is - * triggered. - */ - TP_printk(TR_BUF_FMT, - __get_str(name), - __entry->vaddr, - __entry->size, - &__entry->dma_addr, - __entry->map_size, - __entry->bpid) + /* This is what gets printed when the trace event is + * triggered. + */ + TP_printk(TR_BUF_FMT, + __get_str(name), + __entry->vaddr, + __entry->size, + &__entry->dma_addr, + __entry->map_size, + __entry->bpid) +); + +/* Main memory buff seeding */ +DEFINE_EVENT(dpaa2_eth_buf, dpaa2_eth_buf_seed, + TP_PROTO(struct net_device *netdev, + void *vaddr, + size_t size, + dma_addr_t dma_addr, + size_t map_size, + u16 bpid), + + TP_ARGS(netdev, vaddr, size, dma_addr, map_size, bpid) +); + +/* UMEM buff seeding on AF_XDP fast path */ +DEFINE_EVENT(dpaa2_eth_buf, dpaa2_xsk_buf_seed, + TP_PROTO(struct net_device *netdev, + void *vaddr, + size_t size, + dma_addr_t dma_addr, + size_t map_size, + u16 bpid), + + TP_ARGS(netdev, vaddr, size, dma_addr, map_size, bpid) ); /* If only one event of a certain type needs to be declared, use TRACE_EVENT(). diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c index 31eebeae5d95..281d7e3905c1 100644 --- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c +++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c @@ -1731,6 +1731,12 @@ static int dpaa2_eth_add_bufs(struct dpaa2_eth_priv *priv, goto err_map; buf_array[i] = addr; + + trace_dpaa2_xsk_buf_seed(priv->net_dev, + xdp_buffs[i]->data_hard_start, + DPAA2_ETH_RX_BUF_RAW_SIZE, + addr, priv->rx_buf_size, + ch->bp->bpid); } } diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-xsk.c b/drivers/net/ethernet/freescale/dpaa2/dpaa2-xsk.c index 731318842054..567f52affe21 100644 --- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-xsk.c +++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-xsk.c @@ -113,6 +113,8 @@ static void dpaa2_xsk_rx(struct dpaa2_eth_priv *priv, void *vaddr; u32 xdp_act; + trace_dpaa2_rx_xsk_fd(priv->net_dev, fd); + vaddr = dpaa2_iova_to_virt(priv->iommu_domain, addr); percpu_stats = this_cpu_ptr(priv->percpu_stats); @@ -416,6 +418,8 @@ bool dpaa2_xsk_tx(struct dpaa2_eth_priv *priv, batch = i; break; } + + trace_dpaa2_tx_xsk_fd(priv->net_dev, &fds[i]); } /* Enqueue all the created FDs */ From b5fc29233d28be7a3322848ebe73ac327559cdb9 Mon Sep 17 00:00:00 2001 From: Kuniyuki Iwashima Date: Wed, 19 Oct 2022 15:35:59 -0700 Subject: [PATCH 0269/2002] inet6: Remove inet6_destroy_sock() in sk->sk_prot->destroy(). After commit d38afeec26ed ("tcp/udp: Call inet6_destroy_sock() in IPv6 sk->sk_destruct()."), we call inet6_destroy_sock() in sk->sk_destruct() by setting inet6_sock_destruct() to it to make sure we do not leak inet6-specific resources. Now we can remove unnecessary inet6_destroy_sock() calls in sk->sk_prot->destroy(). DCCP and SCTP have their own sk->sk_destruct() function, so we change them separately in the following patches. Signed-off-by: Kuniyuki Iwashima Reviewed-by: Matthieu Baerts Signed-off-by: David S. Miller --- net/ipv6/ping.c | 6 ------ net/ipv6/raw.c | 2 -- net/ipv6/tcp_ipv6.c | 8 +------- net/ipv6/udp.c | 2 -- net/l2tp/l2tp_ip6.c | 2 -- net/mptcp/protocol.c | 7 ------- 6 files changed, 1 insertion(+), 26 deletions(-) diff --git a/net/ipv6/ping.c b/net/ipv6/ping.c index 86c26e48d065..808983bc2ec9 100644 --- a/net/ipv6/ping.c +++ b/net/ipv6/ping.c @@ -23,11 +23,6 @@ #include #include -static void ping_v6_destroy(struct sock *sk) -{ - inet6_destroy_sock(sk); -} - /* Compatibility glue so we can support IPv6 when it's compiled as a module */ static int dummy_ipv6_recv_error(struct sock *sk, struct msghdr *msg, int len, int *addr_len) @@ -205,7 +200,6 @@ struct proto pingv6_prot = { .owner = THIS_MODULE, .init = ping_init_sock, .close = ping_close, - .destroy = ping_v6_destroy, .pre_connect = ping_v6_pre_connect, .connect = ip6_datagram_connect_v6_only, .disconnect = __udp_disconnect, diff --git a/net/ipv6/raw.c b/net/ipv6/raw.c index 722de9dd0ff7..a06a9f847db5 100644 --- a/net/ipv6/raw.c +++ b/net/ipv6/raw.c @@ -1173,8 +1173,6 @@ static void raw6_destroy(struct sock *sk) lock_sock(sk); ip6_flush_pending_frames(sk); release_sock(sk); - - inet6_destroy_sock(sk); } static int rawv6_init_sk(struct sock *sk) diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index 2a3f9296df1e..f676be14e6b6 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -1966,12 +1966,6 @@ static int tcp_v6_init_sock(struct sock *sk) return 0; } -static void tcp_v6_destroy_sock(struct sock *sk) -{ - tcp_v4_destroy_sock(sk); - inet6_destroy_sock(sk); -} - #ifdef CONFIG_PROC_FS /* Proc filesystem TCPv6 sock list dumping. */ static void get_openreq6(struct seq_file *seq, @@ -2164,7 +2158,7 @@ struct proto tcpv6_prot = { .accept = inet_csk_accept, .ioctl = tcp_ioctl, .init = tcp_v6_init_sock, - .destroy = tcp_v6_destroy_sock, + .destroy = tcp_v4_destroy_sock, .shutdown = tcp_shutdown, .setsockopt = tcp_setsockopt, .getsockopt = tcp_getsockopt, diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c index 129ec5a9b0eb..2260406740d3 100644 --- a/net/ipv6/udp.c +++ b/net/ipv6/udp.c @@ -1661,8 +1661,6 @@ void udpv6_destroy_sock(struct sock *sk) udp_encap_disable(); } } - - inet6_destroy_sock(sk); } /* diff --git a/net/l2tp/l2tp_ip6.c b/net/l2tp/l2tp_ip6.c index 9dbd801ddb98..2478aa60145f 100644 --- a/net/l2tp/l2tp_ip6.c +++ b/net/l2tp/l2tp_ip6.c @@ -257,8 +257,6 @@ static void l2tp_ip6_destroy_sock(struct sock *sk) if (tunnel) l2tp_tunnel_delete(tunnel); - - inet6_destroy_sock(sk); } static int l2tp_ip6_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len) diff --git a/net/mptcp/protocol.c b/net/mptcp/protocol.c index f599ad44ed24..2e16c897c229 100644 --- a/net/mptcp/protocol.c +++ b/net/mptcp/protocol.c @@ -3898,12 +3898,6 @@ static const struct proto_ops mptcp_v6_stream_ops = { static struct proto mptcp_v6_prot; -static void mptcp_v6_destroy(struct sock *sk) -{ - mptcp_destroy(sk); - inet6_destroy_sock(sk); -} - static struct inet_protosw mptcp_v6_protosw = { .type = SOCK_STREAM, .protocol = IPPROTO_MPTCP, @@ -3919,7 +3913,6 @@ int __init mptcp_proto_v6_init(void) mptcp_v6_prot = mptcp_prot; strcpy(mptcp_v6_prot.name, "MPTCPv6"); mptcp_v6_prot.slab = NULL; - mptcp_v6_prot.destroy = mptcp_v6_destroy; mptcp_v6_prot.obj_size = sizeof(struct mptcp6_sock); err = proto_register(&mptcp_v6_prot, 1); From 1651951ebea54970e0bda60c638fc2eee7a6218f Mon Sep 17 00:00:00 2001 From: Kuniyuki Iwashima Date: Wed, 19 Oct 2022 15:36:00 -0700 Subject: [PATCH 0270/2002] dccp: Call inet6_destroy_sock() via sk->sk_destruct(). After commit d38afeec26ed ("tcp/udp: Call inet6_destroy_sock() in IPv6 sk->sk_destruct()."), we call inet6_destroy_sock() in sk->sk_destruct() by setting inet6_sock_destruct() to it to make sure we do not leak inet6-specific resources. DCCP sets its own sk->sk_destruct() in the dccp_init_sock(), and DCCPv6 socket shares it by calling the same init function via dccp_v6_init_sock(). To call inet6_sock_destruct() from DCCPv6 sk->sk_destruct(), we export it and set dccp_v6_sk_destruct() in the init function. Signed-off-by: Kuniyuki Iwashima Signed-off-by: David S. Miller --- net/dccp/dccp.h | 1 + net/dccp/ipv6.c | 15 ++++++++------- net/dccp/proto.c | 8 +++++++- net/ipv6/af_inet6.c | 1 + 4 files changed, 17 insertions(+), 8 deletions(-) diff --git a/net/dccp/dccp.h b/net/dccp/dccp.h index 7dfc00c9fb32..9ddc3a9e89e4 100644 --- a/net/dccp/dccp.h +++ b/net/dccp/dccp.h @@ -278,6 +278,7 @@ int dccp_rcv_state_process(struct sock *sk, struct sk_buff *skb, int dccp_rcv_established(struct sock *sk, struct sk_buff *skb, const struct dccp_hdr *dh, const unsigned int len); +void dccp_destruct_common(struct sock *sk); int dccp_init_sock(struct sock *sk, const __u8 ctl_sock_initialized); void dccp_destroy_sock(struct sock *sk); diff --git a/net/dccp/ipv6.c b/net/dccp/ipv6.c index e57b43006074..ae62b1591dea 100644 --- a/net/dccp/ipv6.c +++ b/net/dccp/ipv6.c @@ -1021,6 +1021,12 @@ static const struct inet_connection_sock_af_ops dccp_ipv6_mapped = { .sockaddr_len = sizeof(struct sockaddr_in6), }; +static void dccp_v6_sk_destruct(struct sock *sk) +{ + dccp_destruct_common(sk); + inet6_sock_destruct(sk); +} + /* NOTE: A lot of things set to zero explicitly by call to * sk_alloc() so need not be done here. */ @@ -1033,17 +1039,12 @@ static int dccp_v6_init_sock(struct sock *sk) if (unlikely(!dccp_v6_ctl_sock_initialized)) dccp_v6_ctl_sock_initialized = 1; inet_csk(sk)->icsk_af_ops = &dccp_ipv6_af_ops; + sk->sk_destruct = dccp_v6_sk_destruct; } return err; } -static void dccp_v6_destroy_sock(struct sock *sk) -{ - dccp_destroy_sock(sk); - inet6_destroy_sock(sk); -} - static struct timewait_sock_ops dccp6_timewait_sock_ops = { .twsk_obj_size = sizeof(struct dccp6_timewait_sock), }; @@ -1066,7 +1067,7 @@ static struct proto dccp_v6_prot = { .accept = inet_csk_accept, .get_port = inet_csk_get_port, .shutdown = dccp_shutdown, - .destroy = dccp_v6_destroy_sock, + .destroy = dccp_destroy_sock, .orphan_count = &dccp_orphan_count, .max_header = MAX_DCCP_HEADER, .obj_size = sizeof(struct dccp6_sock), diff --git a/net/dccp/proto.c b/net/dccp/proto.c index c548ca3e9b0e..9494b0d224f9 100644 --- a/net/dccp/proto.c +++ b/net/dccp/proto.c @@ -171,12 +171,18 @@ const char *dccp_packet_name(const int type) EXPORT_SYMBOL_GPL(dccp_packet_name); -static void dccp_sk_destruct(struct sock *sk) +void dccp_destruct_common(struct sock *sk) { struct dccp_sock *dp = dccp_sk(sk); ccid_hc_tx_delete(dp->dccps_hc_tx_ccid, sk); dp->dccps_hc_tx_ccid = NULL; +} +EXPORT_SYMBOL_GPL(dccp_destruct_common); + +static void dccp_sk_destruct(struct sock *sk) +{ + dccp_destruct_common(sk); inet_sock_destruct(sk); } diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c index 024191004982..6540551ea7ec 100644 --- a/net/ipv6/af_inet6.c +++ b/net/ipv6/af_inet6.c @@ -114,6 +114,7 @@ void inet6_sock_destruct(struct sock *sk) inet6_cleanup_sock(sk); inet_sock_destruct(sk); } +EXPORT_SYMBOL_GPL(inet6_sock_destruct); static int inet6_create(struct net *net, struct socket *sock, int protocol, int kern) From 6431b0f6ff1633ae598667e4cdd93830074a03e8 Mon Sep 17 00:00:00 2001 From: Kuniyuki Iwashima Date: Wed, 19 Oct 2022 15:36:01 -0700 Subject: [PATCH 0271/2002] sctp: Call inet6_destroy_sock() via sk->sk_destruct(). After commit d38afeec26ed ("tcp/udp: Call inet6_destroy_sock() in IPv6 sk->sk_destruct()."), we call inet6_destroy_sock() in sk->sk_destruct() by setting inet6_sock_destruct() to it to make sure we do not leak inet6-specific resources. SCTP sets its own sk->sk_destruct() in the sctp_init_sock(), and SCTPv6 socket reuses it as the init function. To call inet6_sock_destruct() from SCTPv6 sk->sk_destruct(), we set sctp_v6_destruct_sock() in a new init function. Signed-off-by: Kuniyuki Iwashima Signed-off-by: David S. Miller --- net/sctp/socket.c | 29 +++++++++++++++++++++-------- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/net/sctp/socket.c b/net/sctp/socket.c index 83628c347744..3e83963d1b8a 100644 --- a/net/sctp/socket.c +++ b/net/sctp/socket.c @@ -5098,13 +5098,17 @@ static void sctp_destroy_sock(struct sock *sk) } /* Triggered when there are no references on the socket anymore */ -static void sctp_destruct_sock(struct sock *sk) +static void sctp_destruct_common(struct sock *sk) { struct sctp_sock *sp = sctp_sk(sk); /* Free up the HMAC transform. */ crypto_free_shash(sp->hmac); +} +static void sctp_destruct_sock(struct sock *sk) +{ + sctp_destruct_common(sk); inet_sock_destruct(sk); } @@ -9427,7 +9431,7 @@ void sctp_copy_sock(struct sock *newsk, struct sock *sk, sctp_sk(newsk)->reuse = sp->reuse; newsk->sk_shutdown = sk->sk_shutdown; - newsk->sk_destruct = sctp_destruct_sock; + newsk->sk_destruct = sk->sk_destruct; newsk->sk_family = sk->sk_family; newsk->sk_protocol = IPPROTO_SCTP; newsk->sk_backlog_rcv = sk->sk_prot->backlog_rcv; @@ -9662,11 +9666,20 @@ struct proto sctp_prot = { #if IS_ENABLED(CONFIG_IPV6) -#include -static void sctp_v6_destroy_sock(struct sock *sk) +static void sctp_v6_destruct_sock(struct sock *sk) { - sctp_destroy_sock(sk); - inet6_destroy_sock(sk); + sctp_destruct_common(sk); + inet6_sock_destruct(sk); +} + +static int sctp_v6_init_sock(struct sock *sk) +{ + int ret = sctp_init_sock(sk); + + if (!ret) + sk->sk_destruct = sctp_v6_destruct_sock; + + return ret; } struct proto sctpv6_prot = { @@ -9676,8 +9689,8 @@ struct proto sctpv6_prot = { .disconnect = sctp_disconnect, .accept = sctp_accept, .ioctl = sctp_ioctl, - .init = sctp_init_sock, - .destroy = sctp_v6_destroy_sock, + .init = sctp_v6_init_sock, + .destroy = sctp_destroy_sock, .shutdown = sctp_shutdown, .setsockopt = sctp_setsockopt, .getsockopt = sctp_getsockopt, From 1f8c4eeb945553baf868bbec7a8c59810df97a07 Mon Sep 17 00:00:00 2001 From: Kuniyuki Iwashima Date: Wed, 19 Oct 2022 15:36:02 -0700 Subject: [PATCH 0272/2002] inet6: Remove inet6_destroy_sock(). The last user of inet6_destroy_sock() is its wrapper inet6_cleanup_sock(). Let's rename inet6_destroy_sock() to inet6_cleanup_sock(). Signed-off-by: Kuniyuki Iwashima Signed-off-by: David S. Miller --- include/net/transp_v6.h | 2 -- net/ipv6/af_inet6.c | 8 +------- 2 files changed, 1 insertion(+), 9 deletions(-) diff --git a/include/net/transp_v6.h b/include/net/transp_v6.h index b830463e3dff..d27b1caf3753 100644 --- a/include/net/transp_v6.h +++ b/include/net/transp_v6.h @@ -58,8 +58,6 @@ ip6_dgram_sock_seq_show(struct seq_file *seq, struct sock *sp, __u16 srcp, #define LOOPBACK4_IPV6 cpu_to_be32(0x7f000006) -void inet6_destroy_sock(struct sock *sk); - #define IPV6_SEQ_DGRAM_HEADER \ " sl " \ "local_address " \ diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c index 6540551ea7ec..68075295d587 100644 --- a/net/ipv6/af_inet6.c +++ b/net/ipv6/af_inet6.c @@ -490,7 +490,7 @@ int inet6_release(struct socket *sock) } EXPORT_SYMBOL(inet6_release); -void inet6_destroy_sock(struct sock *sk) +void inet6_cleanup_sock(struct sock *sk) { struct ipv6_pinfo *np = inet6_sk(sk); struct sk_buff *skb; @@ -515,12 +515,6 @@ void inet6_destroy_sock(struct sock *sk) txopt_put(opt); } } -EXPORT_SYMBOL_GPL(inet6_destroy_sock); - -void inet6_cleanup_sock(struct sock *sk) -{ - inet6_destroy_sock(sk); -} EXPORT_SYMBOL_GPL(inet6_cleanup_sock); /* From b45a337f061e131bd01b6df2d89f1228d4350a85 Mon Sep 17 00:00:00 2001 From: Kuniyuki Iwashima Date: Wed, 19 Oct 2022 15:36:03 -0700 Subject: [PATCH 0273/2002] inet6: Clean up failure path in do_ipv6_setsockopt(). We can reuse the unlock label above and need not repeat the same code. Signed-off-by: Kuniyuki Iwashima Signed-off-by: David S. Miller --- net/ipv6/ipv6_sockglue.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/net/ipv6/ipv6_sockglue.c b/net/ipv6/ipv6_sockglue.c index 532f4478c884..9ce51680290b 100644 --- a/net/ipv6/ipv6_sockglue.c +++ b/net/ipv6/ipv6_sockglue.c @@ -1005,10 +1005,8 @@ unlock: return retv; e_inval: - sockopt_release_sock(sk); - if (needs_rtnl) - rtnl_unlock(); - return -EINVAL; + retv = -EINVAL; + goto unlock; } int ipv6_setsockopt(struct sock *sk, int level, int optname, sockptr_t optval, From abc210952af71f4eb88bf8074c9982c17779c00f Mon Sep 17 00:00:00 2001 From: Yanguo Li Date: Thu, 20 Oct 2022 09:28:34 +0100 Subject: [PATCH 0274/2002] nfp: flower: tunnel neigh support bond offload Support hardware offload when tunnel neigh out port is bond. These feature work with the nfp firmware. If the firmware supports the NFP_FL_FEATS_TUNNEL_NEIGH_LAG feature, nfp driver write the bond information to the firmware neighbor table or do nothing for bond. when neighbor MAC changes, nfp driver need to update the neighbor information too. Signed-off-by: Yanguo Li Reviewed-by: Louis Peens Signed-off-by: Simon Horman Signed-off-by: David S. Miller --- .../ethernet/netronome/nfp/flower/lag_conf.c | 54 ++++++++++++++----- .../net/ethernet/netronome/nfp/flower/main.c | 9 ++++ .../net/ethernet/netronome/nfp/flower/main.h | 21 +++++++- .../netronome/nfp/flower/tunnel_conf.c | 53 ++++++++++++++---- 4 files changed, 115 insertions(+), 22 deletions(-) diff --git a/drivers/net/ethernet/netronome/nfp/flower/lag_conf.c b/drivers/net/ethernet/netronome/nfp/flower/lag_conf.c index e92860e20a24..88d6d992e7d0 100644 --- a/drivers/net/ethernet/netronome/nfp/flower/lag_conf.c +++ b/drivers/net/ethernet/netronome/nfp/flower/lag_conf.c @@ -154,10 +154,11 @@ nfp_fl_lag_find_group_for_master_with_lag(struct nfp_fl_lag *lag, return NULL; } -int nfp_flower_lag_populate_pre_action(struct nfp_app *app, - struct net_device *master, - struct nfp_fl_pre_lag *pre_act, - struct netlink_ext_ack *extack) +static int nfp_fl_lag_get_group_info(struct nfp_app *app, + struct net_device *netdev, + __be16 *group_id, + u8 *batch_ver, + u8 *group_inst) { struct nfp_flower_priv *priv = app->priv; struct nfp_fl_lag_group *group = NULL; @@ -165,23 +166,52 @@ int nfp_flower_lag_populate_pre_action(struct nfp_app *app, mutex_lock(&priv->nfp_lag.lock); group = nfp_fl_lag_find_group_for_master_with_lag(&priv->nfp_lag, - master); + netdev); if (!group) { mutex_unlock(&priv->nfp_lag.lock); + return -ENOENT; + } + + if (group_id) + *group_id = cpu_to_be16(group->group_id); + + if (batch_ver) { + temp_vers = cpu_to_be32(priv->nfp_lag.batch_ver << + NFP_FL_PRE_LAG_VER_OFF); + memcpy(batch_ver, &temp_vers, 3); + } + + if (group_inst) + *group_inst = group->group_inst; + + mutex_unlock(&priv->nfp_lag.lock); + + return 0; +} + +int nfp_flower_lag_populate_pre_action(struct nfp_app *app, + struct net_device *master, + struct nfp_fl_pre_lag *pre_act, + struct netlink_ext_ack *extack) +{ + if (nfp_fl_lag_get_group_info(app, master, &pre_act->group_id, + pre_act->lag_version, + &pre_act->instance)) { NL_SET_ERR_MSG_MOD(extack, "invalid entry: group does not exist for LAG action"); return -ENOENT; } - pre_act->group_id = cpu_to_be16(group->group_id); - temp_vers = cpu_to_be32(priv->nfp_lag.batch_ver << - NFP_FL_PRE_LAG_VER_OFF); - memcpy(pre_act->lag_version, &temp_vers, 3); - pre_act->instance = group->group_inst; - mutex_unlock(&priv->nfp_lag.lock); - return 0; } +void nfp_flower_lag_get_info_from_netdev(struct nfp_app *app, + struct net_device *netdev, + struct nfp_tun_neigh_lag *lag) +{ + nfp_fl_lag_get_group_info(app, netdev, NULL, + lag->lag_version, &lag->lag_instance); +} + int nfp_flower_lag_get_output_id(struct nfp_app *app, struct net_device *master) { struct nfp_flower_priv *priv = app->priv; diff --git a/drivers/net/ethernet/netronome/nfp/flower/main.c b/drivers/net/ethernet/netronome/nfp/flower/main.c index 4d960a9641b3..83eaa5ae3cd4 100644 --- a/drivers/net/ethernet/netronome/nfp/flower/main.c +++ b/drivers/net/ethernet/netronome/nfp/flower/main.c @@ -76,7 +76,9 @@ nfp_flower_get_internal_port_id(struct nfp_app *app, struct net_device *netdev) u32 nfp_flower_get_port_id_from_netdev(struct nfp_app *app, struct net_device *netdev) { + struct nfp_flower_priv *priv = app->priv; int ext_port; + int gid; if (nfp_netdev_is_nfp_repr(netdev)) { return nfp_repr_get_port_id(netdev); @@ -86,6 +88,13 @@ u32 nfp_flower_get_port_id_from_netdev(struct nfp_app *app, return 0; return nfp_flower_internal_port_get_port_id(ext_port); + } else if (netif_is_lag_master(netdev) && + priv->flower_ext_feats & NFP_FL_FEATS_TUNNEL_NEIGH_LAG) { + gid = nfp_flower_lag_get_output_id(app, netdev); + if (gid < 0) + return 0; + + return (NFP_FL_LAG_OUT | gid); } return 0; diff --git a/drivers/net/ethernet/netronome/nfp/flower/main.h b/drivers/net/ethernet/netronome/nfp/flower/main.h index cb799d18682d..40372545148e 100644 --- a/drivers/net/ethernet/netronome/nfp/flower/main.h +++ b/drivers/net/ethernet/netronome/nfp/flower/main.h @@ -52,6 +52,7 @@ struct nfp_app; #define NFP_FL_FEATS_QOS_PPS BIT(9) #define NFP_FL_FEATS_QOS_METER BIT(10) #define NFP_FL_FEATS_DECAP_V2 BIT(11) +#define NFP_FL_FEATS_TUNNEL_NEIGH_LAG BIT(12) #define NFP_FL_FEATS_HOST_ACK BIT(31) #define NFP_FL_ENABLE_FLOW_MERGE BIT(0) @@ -69,7 +70,8 @@ struct nfp_app; NFP_FL_FEATS_VLAN_QINQ | \ NFP_FL_FEATS_QOS_PPS | \ NFP_FL_FEATS_QOS_METER | \ - NFP_FL_FEATS_DECAP_V2) + NFP_FL_FEATS_DECAP_V2 | \ + NFP_FL_FEATS_TUNNEL_NEIGH_LAG) struct nfp_fl_mask_id { struct circ_buf mask_id_free_list; @@ -103,6 +105,16 @@ struct nfp_fl_tunnel_offloads { struct notifier_block neigh_nb; }; +/** + * struct nfp_tun_neigh_lag - lag info + * @lag_version: lag version + * @lag_instance: lag instance + */ +struct nfp_tun_neigh_lag { + u8 lag_version[3]; + u8 lag_instance; +}; + /** * struct nfp_tun_neigh - basic neighbour data * @dst_addr: Destination MAC address @@ -133,12 +145,14 @@ struct nfp_tun_neigh_ext { * @src_ipv4: Source IPv4 address * @common: Neighbour/route common info * @ext: Neighbour/route extended info + * @lag: lag port info */ struct nfp_tun_neigh_v4 { __be32 dst_ipv4; __be32 src_ipv4; struct nfp_tun_neigh common; struct nfp_tun_neigh_ext ext; + struct nfp_tun_neigh_lag lag; }; /** @@ -147,12 +161,14 @@ struct nfp_tun_neigh_v4 { * @src_ipv6: Source IPv6 address * @common: Neighbour/route common info * @ext: Neighbour/route extended info + * @lag: lag port info */ struct nfp_tun_neigh_v6 { struct in6_addr dst_ipv6; struct in6_addr src_ipv6; struct nfp_tun_neigh common; struct nfp_tun_neigh_ext ext; + struct nfp_tun_neigh_lag lag; }; /** @@ -647,6 +663,9 @@ int nfp_flower_lag_populate_pre_action(struct nfp_app *app, struct netlink_ext_ack *extack); int nfp_flower_lag_get_output_id(struct nfp_app *app, struct net_device *master); +void nfp_flower_lag_get_info_from_netdev(struct nfp_app *app, + struct net_device *netdev, + struct nfp_tun_neigh_lag *lag); void nfp_flower_qos_init(struct nfp_app *app); void nfp_flower_qos_cleanup(struct nfp_app *app); int nfp_flower_setup_qos_offload(struct nfp_app *app, struct net_device *netdev, diff --git a/drivers/net/ethernet/netronome/nfp/flower/tunnel_conf.c b/drivers/net/ethernet/netronome/nfp/flower/tunnel_conf.c index 52f67157bd0f..a8678d5612ee 100644 --- a/drivers/net/ethernet/netronome/nfp/flower/tunnel_conf.c +++ b/drivers/net/ethernet/netronome/nfp/flower/tunnel_conf.c @@ -290,6 +290,11 @@ nfp_flower_xmit_tun_conf(struct nfp_app *app, u8 mtype, u16 plen, void *pdata, mtype == NFP_FLOWER_CMSG_TYPE_TUN_NEIGH_V6)) plen -= sizeof(struct nfp_tun_neigh_ext); + if (!(priv->flower_ext_feats & NFP_FL_FEATS_TUNNEL_NEIGH_LAG) && + (mtype == NFP_FLOWER_CMSG_TYPE_TUN_NEIGH || + mtype == NFP_FLOWER_CMSG_TYPE_TUN_NEIGH_V6)) + plen -= sizeof(struct nfp_tun_neigh_lag); + skb = nfp_flower_cmsg_alloc(app, plen, mtype, flag); if (!skb) return -ENOMEM; @@ -468,6 +473,7 @@ nfp_tun_write_neigh(struct net_device *netdev, struct nfp_app *app, neigh_table_params); if (!nn_entry && !neigh_invalid) { struct nfp_tun_neigh_ext *ext; + struct nfp_tun_neigh_lag *lag; struct nfp_tun_neigh *common; nn_entry = kzalloc(sizeof(*nn_entry) + neigh_size, @@ -488,6 +494,7 @@ nfp_tun_write_neigh(struct net_device *netdev, struct nfp_app *app, payload->dst_ipv6 = flowi6->daddr; common = &payload->common; ext = &payload->ext; + lag = &payload->lag; mtype = NFP_FLOWER_CMSG_TYPE_TUN_NEIGH_V6; } else { struct flowi4 *flowi4 = (struct flowi4 *)flow; @@ -498,6 +505,7 @@ nfp_tun_write_neigh(struct net_device *netdev, struct nfp_app *app, payload->dst_ipv4 = flowi4->daddr; common = &payload->common; ext = &payload->ext; + lag = &payload->lag; mtype = NFP_FLOWER_CMSG_TYPE_TUN_NEIGH; } ext->host_ctx = cpu_to_be32(U32_MAX); @@ -505,6 +513,9 @@ nfp_tun_write_neigh(struct net_device *netdev, struct nfp_app *app, ext->vlan_tci = cpu_to_be16(U16_MAX); ether_addr_copy(common->src_addr, netdev->dev_addr); neigh_ha_snapshot(common->dst_addr, neigh, netdev); + + if ((port_id & NFP_FL_LAG_OUT) == NFP_FL_LAG_OUT) + nfp_flower_lag_get_info_from_netdev(app, netdev, lag); common->port_id = cpu_to_be32(port_id); if (rhashtable_insert_fast(&priv->neigh_table, @@ -547,13 +558,38 @@ nfp_tun_write_neigh(struct net_device *netdev, struct nfp_app *app, if (nn_entry->flow) list_del(&nn_entry->list_head); kfree(nn_entry); - } else if (nn_entry && !neigh_invalid && override) { - mtype = is_ipv6 ? NFP_FLOWER_CMSG_TYPE_TUN_NEIGH_V6 : - NFP_FLOWER_CMSG_TYPE_TUN_NEIGH; - nfp_tun_link_predt_entries(app, nn_entry); - nfp_flower_xmit_tun_conf(app, mtype, neigh_size, - nn_entry->payload, - GFP_ATOMIC); + } else if (nn_entry && !neigh_invalid) { + struct nfp_tun_neigh *common; + u8 dst_addr[ETH_ALEN]; + bool is_mac_change; + + if (is_ipv6) { + struct nfp_tun_neigh_v6 *payload; + + payload = (struct nfp_tun_neigh_v6 *)nn_entry->payload; + common = &payload->common; + mtype = NFP_FLOWER_CMSG_TYPE_TUN_NEIGH_V6; + } else { + struct nfp_tun_neigh_v4 *payload; + + payload = (struct nfp_tun_neigh_v4 *)nn_entry->payload; + common = &payload->common; + mtype = NFP_FLOWER_CMSG_TYPE_TUN_NEIGH; + } + + ether_addr_copy(dst_addr, common->dst_addr); + neigh_ha_snapshot(common->dst_addr, neigh, netdev); + is_mac_change = !ether_addr_equal(dst_addr, common->dst_addr); + if (override || is_mac_change) { + if (is_mac_change && nn_entry->flow) { + list_del(&nn_entry->list_head); + nn_entry->flow = NULL; + } + nfp_tun_link_predt_entries(app, nn_entry); + nfp_flower_xmit_tun_conf(app, mtype, neigh_size, + nn_entry->payload, + GFP_ATOMIC); + } } spin_unlock_bh(&priv->predt_lock); @@ -593,8 +629,7 @@ nfp_tun_neigh_event_handler(struct notifier_block *nb, unsigned long event, app_priv = container_of(nb, struct nfp_flower_priv, tun.neigh_nb); app = app_priv->app; - if (!nfp_netdev_is_nfp_repr(n->dev) && - !nfp_flower_internal_port_can_offload(app, n->dev)) + if (!nfp_flower_get_port_id_from_netdev(app, n->dev)) return NOTIFY_DONE; #if IS_ENABLED(CONFIG_INET) From 8beef08f4618c9bfab9374fc72930fc6ed70fdc1 Mon Sep 17 00:00:00 2001 From: Steen Hegelund Date: Thu, 20 Oct 2022 15:08:56 +0200 Subject: [PATCH 0275/2002] net: microchip: sparx5: Adding initial VCAP API support This provides the initial VCAP API framework and Sparx5 specific VCAP implementation. When the Sparx5 Switchdev driver is initialized it will also initialize its VCAP module, and this hooks up the concrete Sparx5 VCAP model to the VCAP API, so that the VCAP API knows what VCAP instances are available. Signed-off-by: Steen Hegelund Signed-off-by: David S. Miller --- MAINTAINERS | 1 + drivers/net/ethernet/microchip/Kconfig | 1 + drivers/net/ethernet/microchip/sparx5/Kconfig | 1 + .../net/ethernet/microchip/sparx5/Makefile | 8 +- .../ethernet/microchip/sparx5/sparx5_main.c | 9 + .../ethernet/microchip/sparx5/sparx5_main.h | 6 + .../microchip/sparx5/sparx5_vcap_impl.c | 41 +++ drivers/net/ethernet/microchip/vcap/Kconfig | 39 +++ .../net/ethernet/microchip/vcap/vcap_ag_api.h | 326 ++++++++++++++++++ .../net/ethernet/microchip/vcap/vcap_api.h | 269 +++++++++++++++ 10 files changed, 699 insertions(+), 2 deletions(-) create mode 100644 drivers/net/ethernet/microchip/sparx5/sparx5_vcap_impl.c create mode 100644 drivers/net/ethernet/microchip/vcap/Kconfig create mode 100644 drivers/net/ethernet/microchip/vcap/vcap_ag_api.h create mode 100644 drivers/net/ethernet/microchip/vcap/vcap_api.h diff --git a/MAINTAINERS b/MAINTAINERS index 479a15d86a91..45c79261e355 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2439,6 +2439,7 @@ L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) S: Supported T: git git://github.com/microchip-ung/linux-upstream.git F: arch/arm64/boot/dts/microchip/ +F: drivers/net/ethernet/microchip/vcap/ F: drivers/pinctrl/pinctrl-microchip-sgpio.c N: sparx5 diff --git a/drivers/net/ethernet/microchip/Kconfig b/drivers/net/ethernet/microchip/Kconfig index ed7a35c3ceac..24c994baad13 100644 --- a/drivers/net/ethernet/microchip/Kconfig +++ b/drivers/net/ethernet/microchip/Kconfig @@ -57,5 +57,6 @@ config LAN743X source "drivers/net/ethernet/microchip/lan966x/Kconfig" source "drivers/net/ethernet/microchip/sparx5/Kconfig" +source "drivers/net/ethernet/microchip/vcap/Kconfig" endif # NET_VENDOR_MICROCHIP diff --git a/drivers/net/ethernet/microchip/sparx5/Kconfig b/drivers/net/ethernet/microchip/sparx5/Kconfig index cc5e48e1bb4c..98e27530a91f 100644 --- a/drivers/net/ethernet/microchip/sparx5/Kconfig +++ b/drivers/net/ethernet/microchip/sparx5/Kconfig @@ -9,5 +9,6 @@ config SPARX5_SWITCH select PHYLINK select PHY_SPARX5_SERDES select RESET_CONTROLLER + select VCAP help This driver supports the Sparx5 network switch device. diff --git a/drivers/net/ethernet/microchip/sparx5/Makefile b/drivers/net/ethernet/microchip/sparx5/Makefile index d1c6ad966747..aa4dadb8a25d 100644 --- a/drivers/net/ethernet/microchip/sparx5/Makefile +++ b/drivers/net/ethernet/microchip/sparx5/Makefile @@ -5,7 +5,11 @@ obj-$(CONFIG_SPARX5_SWITCH) += sparx5-switch.o -sparx5-switch-objs := sparx5_main.o sparx5_packet.o \ +sparx5-switch-y := sparx5_main.o sparx5_packet.o \ sparx5_netdev.o sparx5_phylink.o sparx5_port.o sparx5_mactable.o sparx5_vlan.o \ sparx5_switchdev.o sparx5_calendar.o sparx5_ethtool.o sparx5_fdma.o \ - sparx5_ptp.o sparx5_pgid.o sparx5_tc.o sparx5_qos.o + sparx5_ptp.o sparx5_pgid.o sparx5_tc.o sparx5_qos.o \ + sparx5_vcap_impl.o + +# Provide include files +ccflags-y += -I$(srctree)/drivers/net/ethernet/microchip/vcap diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_main.c b/drivers/net/ethernet/microchip/sparx5/sparx5_main.c index 62a325e96345..0b70c00c6eaa 100644 --- a/drivers/net/ethernet/microchip/sparx5/sparx5_main.c +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_main.c @@ -672,6 +672,14 @@ static int sparx5_start(struct sparx5 *sparx5) sparx5_board_init(sparx5); err = sparx5_register_notifier_blocks(sparx5); + if (err) + return err; + + err = sparx5_vcap_init(sparx5); + if (err) { + sparx5_unregister_notifier_blocks(sparx5); + return err; + } /* Start Frame DMA with fallback to register based INJ/XTR */ err = -ENXIO; @@ -906,6 +914,7 @@ static int mchp_sparx5_remove(struct platform_device *pdev) sparx5_ptp_deinit(sparx5); sparx5_fdma_stop(sparx5); sparx5_cleanup_ports(sparx5); + sparx5_vcap_destroy(sparx5); /* Unregister netdevs */ sparx5_unregister_notifier_blocks(sparx5); diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_main.h b/drivers/net/ethernet/microchip/sparx5/sparx5_main.h index 7a83222caa73..2ab22a7b799e 100644 --- a/drivers/net/ethernet/microchip/sparx5/sparx5_main.h +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_main.h @@ -288,6 +288,8 @@ struct sparx5 { struct mutex ptp_lock; /* lock for ptp interface state */ u16 ptp_skbs; int ptp_irq; + /* VCAP */ + struct vcap_control *vcap_ctrl; /* PGID allocation map */ u8 pgid_map[PGID_TABLE_SIZE]; }; @@ -382,6 +384,10 @@ void sparx5_ptp_txtstamp_release(struct sparx5_port *port, struct sk_buff *skb); irqreturn_t sparx5_ptp_irq_handler(int irq, void *args); +/* sparx5_vcap_impl.c */ +int sparx5_vcap_init(struct sparx5 *sparx5); +void sparx5_vcap_destroy(struct sparx5 *sparx5); + /* sparx5_pgid.c */ enum sparx5_pgid_type { SPX5_PGID_FREE, diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_vcap_impl.c b/drivers/net/ethernet/microchip/sparx5/sparx5_vcap_impl.c new file mode 100644 index 000000000000..8df7cba77a28 --- /dev/null +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_vcap_impl.c @@ -0,0 +1,41 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* Microchip Sparx5 Switch driver VCAP implementation + * + * Copyright (c) 2022 Microchip Technology Inc. and its subsidiaries. + * + * The Sparx5 Chip Register Model can be browsed at this location: + * https://github.com/microchip-ung/sparx-5_reginfo + */ + +#include +#include + +#include "vcap_api.h" +#include "sparx5_main_regs.h" +#include "sparx5_main.h" + +/* Allocate a vcap control and vcap instances and configure the system */ +int sparx5_vcap_init(struct sparx5 *sparx5) +{ + struct vcap_control *ctrl; + + /* Create a VCAP control instance that owns the platform specific VCAP + * model with VCAP instances and information about keysets, keys, + * actionsets and actions + */ + ctrl = kzalloc(sizeof(*ctrl), GFP_KERNEL); + if (!ctrl) + return -ENOMEM; + + sparx5->vcap_ctrl = ctrl; + + return 0; +} + +void sparx5_vcap_destroy(struct sparx5 *sparx5) +{ + if (!sparx5->vcap_ctrl) + return; + + kfree(sparx5->vcap_ctrl); +} diff --git a/drivers/net/ethernet/microchip/vcap/Kconfig b/drivers/net/ethernet/microchip/vcap/Kconfig new file mode 100644 index 000000000000..a78cbc6ce6bb --- /dev/null +++ b/drivers/net/ethernet/microchip/vcap/Kconfig @@ -0,0 +1,39 @@ +# SPDX-License-Identifier: GPL-2.0-only +# +# Microchip VCAP API configuration +# + +if NET_VENDOR_MICROCHIP + +config VCAP + bool "VCAP (Versatile Content-Aware Processor) library" + help + Provides the basic VCAP functionality for multiple Microchip switchcores + + A VCAP is essentially a TCAM with rules consisting of + + - Programmable key fields + - Programmable action fields + - A counter (which may be only one bit wide) + + Besides this each VCAP has: + + - A number of lookups + - A keyset configuration per port per lookup + + The VCAP implementation provides switchcore independent handling of rules + and supports: + + - Creating and deleting rules + - Updating and getting rules + + The platform specific configuration as well as the platform specific model + of the VCAP instances are attached to the VCAP API and a client can then + access rules via the API in a platform independent way, with the + limitations that each VCAP has in terms of its supported keys and actions. + + Different switchcores will have different VCAP instances with different + characteristics. Look in the datasheet for the VCAP specifications for the + specific switchcore. + +endif # NET_VENDOR_MICROCHIP diff --git a/drivers/net/ethernet/microchip/vcap/vcap_ag_api.h b/drivers/net/ethernet/microchip/vcap/vcap_ag_api.h new file mode 100644 index 000000000000..804d57b9b60a --- /dev/null +++ b/drivers/net/ethernet/microchip/vcap/vcap_ag_api.h @@ -0,0 +1,326 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ +/* Copyright (C) 2022 Microchip Technology Inc. and its subsidiaries. + * Microchip VCAP API + */ + +/* This file is autogenerated by cml-utils 2022-10-13 10:04:41 +0200. + * Commit ID: fd7cafd175899f0672c73afb3a30fc872500ae86 + */ + +#ifndef __VCAP_AG_API__ +#define __VCAP_AG_API__ + +enum vcap_type { + VCAP_TYPE_IS2, + VCAP_TYPE_MAX +}; + +/* Keyfieldset names with origin information */ +enum vcap_keyfield_set { + VCAP_KFS_NO_VALUE, /* initial value */ + VCAP_KFS_ARP, /* sparx5 is2 X6 */ + VCAP_KFS_IP4_OTHER, /* sparx5 is2 X6 */ + VCAP_KFS_IP4_TCP_UDP, /* sparx5 is2 X6 */ + VCAP_KFS_IP6_STD, /* sparx5 is2 X6 */ + VCAP_KFS_IP_7TUPLE, /* sparx5 is2 X12 */ + VCAP_KFS_MAC_ETYPE, /* sparx5 is2 X6 */ +}; + +/* List of keyfields with description + * + * Keys ending in _IS are booleans derived from frame data + * Keys ending in _CLS are classified frame data + * + * VCAP_KF_8021Q_DEI_CLS: W1, sparx5: is2 + * Classified DEI + * VCAP_KF_8021Q_PCP_CLS: W3, sparx5: is2 + * Classified PCP + * VCAP_KF_8021Q_VID_CLS: W13, sparx5: is2 + * Classified VID + * VCAP_KF_8021Q_VLAN_TAGGED_IS: W1, sparx5: is2 + * Sparx5: Set if frame was received with a VLAN tag, LAN966x: Set if frame has + * one or more Q-tags. Independent of port VLAN awareness + * VCAP_KF_ARP_ADDR_SPACE_OK_IS: W1, sparx5: is2 + * Set if hardware address is Ethernet + * VCAP_KF_ARP_LEN_OK_IS: W1, sparx5: is2 + * Set if hardware address length = 6 (Ethernet) and IP address length = 4 (IP). + * VCAP_KF_ARP_OPCODE: W2, sparx5: is2 + * ARP opcode + * VCAP_KF_ARP_OPCODE_UNKNOWN_IS: W1, sparx5: is2 + * Set if not one of the codes defined in VCAP_KF_ARP_OPCODE + * VCAP_KF_ARP_PROTO_SPACE_OK_IS: W1, sparx5: is2 + * Set if protocol address space is 0x0800 + * VCAP_KF_ARP_SENDER_MATCH_IS: W1, sparx5: is2 + * Sender Hardware Address = SMAC (ARP) + * VCAP_KF_ARP_TGT_MATCH_IS: W1, sparx5: is2 + * Target Hardware Address = SMAC (RARP) + * VCAP_KF_ETYPE: W16, sparx5: is2 + * Ethernet type + * VCAP_KF_ETYPE_LEN_IS: W1, sparx5: is2 + * Set if frame has EtherType >= 0x600 + * VCAP_KF_IF_IGR_PORT_MASK: sparx5 is2 W32, sparx5 is2 W65 + * Ingress port mask, one bit per port/erleg + * VCAP_KF_IF_IGR_PORT_MASK_L3: W1, sparx5: is2 + * If set, IF_IGR_PORT_MASK, IF_IGR_PORT_MASK_RNG, and IF_IGR_PORT_MASK_SEL are + * used to specify L3 interfaces + * VCAP_KF_IF_IGR_PORT_MASK_RNG: W4, sparx5: is2 + * Range selector for IF_IGR_PORT_MASK. Specifies which group of 32 ports are + * available in IF_IGR_PORT_MASK + * VCAP_KF_IF_IGR_PORT_MASK_SEL: W2, sparx5: is2 + * Mode selector for IF_IGR_PORT_MASK, applicable when IF_IGR_PORT_MASK_L3 == 0. + * Mapping: 0: DEFAULT 1: LOOPBACK 2: MASQUERADE 3: CPU_VD + * VCAP_KF_IP4_IS: W1, sparx5: is2 + * Set if frame has EtherType = 0x800 and IP version = 4 + * VCAP_KF_ISDX_CLS: W12, sparx5: is2 + * Classified ISDX + * VCAP_KF_ISDX_GT0_IS: W1, sparx5: is2 + * Set if classified ISDX > 0 + * VCAP_KF_L2_BC_IS: W1, sparx5: is2 + * Set if frame’s destination MAC address is the broadcast address + * (FF-FF-FF-FF-FF-FF). + * VCAP_KF_L2_DMAC: W48, sparx5: is2 + * Destination MAC address + * VCAP_KF_L2_FWD_IS: W1, sparx5: is2 + * Set if the frame is allowed to be forwarded to front ports + * VCAP_KF_L2_MC_IS: W1, sparx5: is2 + * Set if frame’s destination MAC address is a multicast address (bit 40 = 1). + * VCAP_KF_L2_PAYLOAD_ETYPE: W64, sparx5: is2 + * Byte 0-7 of L2 payload after Type/Len field and overloading for OAM + * VCAP_KF_L2_SMAC: W48, sparx5: is2 + * Source MAC address + * VCAP_KF_L3_DIP_EQ_SIP_IS: W1, sparx5: is2 + * Set if Src IP matches Dst IP address + * VCAP_KF_L3_DST_IS: W1, sparx5: is2 + * Set if lookup is done for egress router leg + * VCAP_KF_L3_FRAGMENT_TYPE: W2, sparx5: is2 + * L3 Fragmentation type (none, initial, suspicious, valid follow up) + * VCAP_KF_L3_FRAG_INVLD_L4_LEN: W1, sparx5: is2 + * Set if frame's L4 length is less than ANA_CL:COMMON:CLM_FRAGMENT_CFG.L4_MIN_L + * EN + * VCAP_KF_L3_IP4_DIP: W32, sparx5: is2 + * Destination IPv4 Address + * VCAP_KF_L3_IP4_SIP: W32, sparx5: is2 + * Source IPv4 Address + * VCAP_KF_L3_IP6_DIP: W128, sparx5: is2 + * Sparx5: Full IPv6 DIP, LAN966x: Either Full IPv6 DIP or a subset depending on + * frame type + * VCAP_KF_L3_IP6_SIP: W128, sparx5: is2 + * Sparx5: Full IPv6 SIP, LAN966x: Either Full IPv6 SIP or a subset depending on + * frame type + * VCAP_KF_L3_IP_PROTO: W8, sparx5: is2 + * IPv4 frames: IP protocol. IPv6 frames: Next header, same as for IPV4 + * VCAP_KF_L3_OPTIONS_IS: W1, sparx5: is2 + * Set if IPv4 frame contains options (IP len > 5) + * VCAP_KF_L3_PAYLOAD: sparx5 is2 W96, sparx5 is2 W40 + * Sparx5: Payload bytes after IP header. IPv4: IPv4 options are not parsed so + * payload is always taken 20 bytes after the start of the IPv4 header, LAN966x: + * Bytes 0-6 after IP header + * VCAP_KF_L3_RT_IS: W1, sparx5: is2 + * Set if frame has hit a router leg + * VCAP_KF_L3_TOS: W8, sparx5: is2 + * Sparx5: Frame's IPv4/IPv6 DSCP and ECN fields, LAN966x: IP TOS field + * VCAP_KF_L3_TTL_GT0: W1, sparx5: is2 + * Set if IPv4 TTL / IPv6 hop limit is greater than 0 + * VCAP_KF_L4_ACK: W1, sparx5: is2 + * Sparx5 and LAN966x: TCP flag ACK, LAN966x only: PTP over UDP: flagField bit 2 + * (unicastFlag) + * VCAP_KF_L4_DPORT: W16, sparx5: is2 + * Sparx5: TCP/UDP destination port. Overloading for IP_7TUPLE: Non-TCP/UDP IP + * frames: L4_DPORT = L3_IP_PROTO, LAN966x: TCP/UDP destination port + * VCAP_KF_L4_FIN: W1, sparx5: is2 + * TCP flag FIN, LAN966x: TCP flag FIN, and for PTP over UDP: messageType bit 1 + * VCAP_KF_L4_PAYLOAD: W64, sparx5: is2 + * Payload bytes after TCP/UDP header Overloading for IP_7TUPLE: Non TCP/UDP + * frames: Payload bytes 0–7 after IP header. IPv4 options are not parsed so + * payload is always taken 20 bytes after the start of the IPv4 header for non + * TCP/UDP IPv4 frames + * VCAP_KF_L4_PSH: W1, sparx5: is2 + * Sparx5: TCP flag PSH, LAN966x: TCP: TCP flag PSH. PTP over UDP: flagField bit + * 1 (twoStepFlag) + * VCAP_KF_L4_RNG: W16, sparx5: is2 + * Range checker bitmask (one for each range checker). Input into range checkers + * is taken from classified results (VID, DSCP) and frame (SPORT, DPORT, ETYPE, + * outer VID, inner VID) + * VCAP_KF_L4_RST: W1, sparx5: is2 + * Sparx5: TCP flag RST , LAN966x: TCP: TCP flag RST. PTP over UDP: messageType + * bit 3 + * VCAP_KF_L4_SEQUENCE_EQ0_IS: W1, sparx5: is2 + * Set if TCP sequence number is 0, LAN966x: Overlayed with PTP over UDP: + * messageType bit 0 + * VCAP_KF_L4_SPORT: W16, sparx5: is2 + * TCP/UDP source port + * VCAP_KF_L4_SPORT_EQ_DPORT_IS: W1, sparx5: is2 + * Set if UDP or TCP source port equals UDP or TCP destination port + * VCAP_KF_L4_SYN: W1, sparx5: is2 + * Sparx5: TCP flag SYN, LAN966x: TCP: TCP flag SYN. PTP over UDP: messageType + * bit 2 + * VCAP_KF_L4_URG: W1, sparx5: is2 + * Sparx5: TCP flag URG, LAN966x: TCP: TCP flag URG. PTP over UDP: flagField bit + * 7 (reserved) + * VCAP_KF_LOOKUP_FIRST_IS: W1, sparx5: is2 + * Selects between entries relevant for first and second lookup. Set for first + * lookup, cleared for second lookup. + * VCAP_KF_LOOKUP_PAG: W8, sparx5: is2 + * Classified Policy Association Group: chains rules from IS1/CLM to IS2 + * VCAP_KF_OAM_CCM_CNTS_EQ0: W1, sparx5: is2 + * Dual-ended loss measurement counters in CCM frames are all zero + * VCAP_KF_OAM_Y1731_IS: W1, sparx5: is2 + * Set if frame’s EtherType = 0x8902 + * VCAP_KF_TCP_IS: W1, sparx5: is2 + * Set if frame is IPv4 TCP frame (IP protocol = 6) or IPv6 TCP frames (Next + * header = 6) + * VCAP_KF_TCP_UDP_IS: W1, sparx5: is2 + * Set if frame is IPv4/IPv6 TCP or UDP frame (IP protocol/next header equals 6 + * or 17) + * VCAP_KF_TYPE: sparx5 is2 W4, sparx5 is2 W2 + * Keyset type id - set by the API + */ + +/* Keyfield names */ +enum vcap_key_field { + VCAP_KF_NO_VALUE, /* initial value */ + VCAP_KF_8021Q_DEI_CLS, + VCAP_KF_8021Q_PCP_CLS, + VCAP_KF_8021Q_VID_CLS, + VCAP_KF_8021Q_VLAN_TAGGED_IS, + VCAP_KF_ARP_ADDR_SPACE_OK_IS, + VCAP_KF_ARP_LEN_OK_IS, + VCAP_KF_ARP_OPCODE, + VCAP_KF_ARP_OPCODE_UNKNOWN_IS, + VCAP_KF_ARP_PROTO_SPACE_OK_IS, + VCAP_KF_ARP_SENDER_MATCH_IS, + VCAP_KF_ARP_TGT_MATCH_IS, + VCAP_KF_ETYPE, + VCAP_KF_ETYPE_LEN_IS, + VCAP_KF_IF_IGR_PORT_MASK, + VCAP_KF_IF_IGR_PORT_MASK_L3, + VCAP_KF_IF_IGR_PORT_MASK_RNG, + VCAP_KF_IF_IGR_PORT_MASK_SEL, + VCAP_KF_IP4_IS, + VCAP_KF_ISDX_CLS, + VCAP_KF_ISDX_GT0_IS, + VCAP_KF_L2_BC_IS, + VCAP_KF_L2_DMAC, + VCAP_KF_L2_FWD_IS, + VCAP_KF_L2_MC_IS, + VCAP_KF_L2_PAYLOAD_ETYPE, + VCAP_KF_L2_SMAC, + VCAP_KF_L3_DIP_EQ_SIP_IS, + VCAP_KF_L3_DST_IS, + VCAP_KF_L3_FRAGMENT_TYPE, + VCAP_KF_L3_FRAG_INVLD_L4_LEN, + VCAP_KF_L3_IP4_DIP, + VCAP_KF_L3_IP4_SIP, + VCAP_KF_L3_IP6_DIP, + VCAP_KF_L3_IP6_SIP, + VCAP_KF_L3_IP_PROTO, + VCAP_KF_L3_OPTIONS_IS, + VCAP_KF_L3_PAYLOAD, + VCAP_KF_L3_RT_IS, + VCAP_KF_L3_TOS, + VCAP_KF_L3_TTL_GT0, + VCAP_KF_L4_ACK, + VCAP_KF_L4_DPORT, + VCAP_KF_L4_FIN, + VCAP_KF_L4_PAYLOAD, + VCAP_KF_L4_PSH, + VCAP_KF_L4_RNG, + VCAP_KF_L4_RST, + VCAP_KF_L4_SEQUENCE_EQ0_IS, + VCAP_KF_L4_SPORT, + VCAP_KF_L4_SPORT_EQ_DPORT_IS, + VCAP_KF_L4_SYN, + VCAP_KF_L4_URG, + VCAP_KF_LOOKUP_FIRST_IS, + VCAP_KF_LOOKUP_PAG, + VCAP_KF_OAM_CCM_CNTS_EQ0, + VCAP_KF_OAM_Y1731_IS, + VCAP_KF_TCP_IS, + VCAP_KF_TCP_UDP_IS, + VCAP_KF_TYPE, +}; + +/* Actionset names with origin information */ +enum vcap_actionfield_set { + VCAP_AFS_NO_VALUE, /* initial value */ + VCAP_AFS_BASE_TYPE, /* sparx5 is2 X3 */ +}; + +/* List of actionfields with description + * + * VCAP_AF_CNT_ID: W12, sparx5: is2 + * Counter ID, used per lookup to index the 4K frame counters (ANA_ACL:CNT_TBL). + * Multiple VCAP IS2 entries can use the same counter. + * VCAP_AF_CPU_COPY_ENA: W1, sparx5: is2 + * Setting this bit to 1 causes all frames that hit this action to be copied to + * the CPU extraction queue specified in CPU_QUEUE_NUM. + * VCAP_AF_CPU_QUEUE_NUM: W3, sparx5: is2 + * CPU queue number. Used when CPU_COPY_ENA is set. + * VCAP_AF_HIT_ME_ONCE: W1, sparx5: is2 + * Setting this bit to 1 causes the first frame that hits this action where the + * HIT_CNT counter is zero to be copied to the CPU extraction queue specified in + * CPU_QUEUE_NUM. The HIT_CNT counter is then incremented and any frames that + * hit this action later are not copied to the CPU. To re-enable the HIT_ME_ONCE + * functionality, the HIT_CNT counter must be cleared. + * VCAP_AF_IGNORE_PIPELINE_CTRL: W1, sparx5: is2 + * Ignore ingress pipeline control. This enforces the use of the VCAP IS2 action + * even when the pipeline control has terminated the frame before VCAP IS2. + * VCAP_AF_INTR_ENA: W1, sparx5: is2 + * If set, an interrupt is triggered when this rule is hit + * VCAP_AF_LRN_DIS: W1, sparx5: is2 + * Setting this bit to 1 disables learning of frames hitting this action. + * VCAP_AF_MASK_MODE: W3, sparx5: is2 + * Controls the PORT_MASK use. Sparx5: 0: OR_DSTMASK, 1: AND_VLANMASK, 2: + * REPLACE_PGID, 3: REPLACE_ALL, 4: REDIR_PGID, 5: OR_PGID_MASK, 6: VSTAX, 7: + * Not applicable. LAN966X: 0: No action, 1: Permit/deny (AND), 2: Policy + * forwarding (DMAC lookup), 3: Redirect. The CPU port is untouched by + * MASK_MODE. + * VCAP_AF_MATCH_ID: W16, sparx5: is2 + * Logical ID for the entry. The MATCH_ID is extracted together with the frame + * if the frame is forwarded to the CPU (CPU_COPY_ENA). The result is placed in + * IFH.CL_RSLT. + * VCAP_AF_MATCH_ID_MASK: W16, sparx5: is2 + * Mask used by MATCH_ID. + * VCAP_AF_MIRROR_PROBE: W2, sparx5: is2 + * Mirroring performed according to configuration of a mirror probe. 0: No + * mirroring. 1: Mirror probe 0. 2: Mirror probe 1. 3: Mirror probe 2 + * VCAP_AF_PIPELINE_FORCE_ENA: W1, sparx5: is2 + * If set, use PIPELINE_PT unconditionally and set PIPELINE_ACT = NONE if + * PIPELINE_PT == NONE. Overrules previous settings of pipeline point. + * VCAP_AF_PIPELINE_PT: W5, sparx5: is2 + * Pipeline point used if PIPELINE_FORCE_ENA is set + * VCAP_AF_POLICE_ENA: W1, sparx5: is2 + * Setting this bit to 1 causes frames that hit this action to be policed by the + * ACL policer specified in POLICE_IDX. Only applies to the first lookup. + * VCAP_AF_POLICE_IDX: W6, sparx5: is2 + * Selects VCAP policer used when policing frames (POLICE_ENA) + * VCAP_AF_PORT_MASK: W68, sparx5: is2 + * Port mask applied to the forwarding decision based on MASK_MODE. + * VCAP_AF_RT_DIS: W1, sparx5: is2 + * If set, routing is disallowed. Only applies when IS_INNER_ACL is 0. See also + * IGR_ACL_ENA, EGR_ACL_ENA, and RLEG_STAT_IDX. + */ + +/* Actionfield names */ +enum vcap_action_field { + VCAP_AF_NO_VALUE, /* initial value */ + VCAP_AF_CNT_ID, + VCAP_AF_CPU_COPY_ENA, + VCAP_AF_CPU_QUEUE_NUM, + VCAP_AF_HIT_ME_ONCE, + VCAP_AF_IGNORE_PIPELINE_CTRL, + VCAP_AF_INTR_ENA, + VCAP_AF_LRN_DIS, + VCAP_AF_MASK_MODE, + VCAP_AF_MATCH_ID, + VCAP_AF_MATCH_ID_MASK, + VCAP_AF_MIRROR_PROBE, + VCAP_AF_PIPELINE_FORCE_ENA, + VCAP_AF_PIPELINE_PT, + VCAP_AF_POLICE_ENA, + VCAP_AF_POLICE_IDX, + VCAP_AF_PORT_MASK, + VCAP_AF_RT_DIS, +}; + +#endif /* __VCAP_AG_API__ */ diff --git a/drivers/net/ethernet/microchip/vcap/vcap_api.h b/drivers/net/ethernet/microchip/vcap/vcap_api.h new file mode 100644 index 000000000000..4444bf67ebec --- /dev/null +++ b/drivers/net/ethernet/microchip/vcap/vcap_api.h @@ -0,0 +1,269 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ +/* Copyright (C) 2022 Microchip Technology Inc. and its subsidiaries. + * Microchip VCAP API + */ + +#ifndef __VCAP_API__ +#define __VCAP_API__ + +#include +#include +#include + +/* Use the generated API model */ +#include "vcap_ag_api.h" + +#define VCAP_CID_LOOKUP_SIZE 100000 /* Chains in a lookup */ +#define VCAP_CID_INGRESS_L0 1000000 /* Ingress Stage 1 Lookup 0 */ +#define VCAP_CID_INGRESS_L1 1100000 /* Ingress Stage 1 Lookup 1 */ +#define VCAP_CID_INGRESS_L2 1200000 /* Ingress Stage 1 Lookup 2 */ +#define VCAP_CID_INGRESS_L3 1300000 /* Ingress Stage 1 Lookup 3 */ +#define VCAP_CID_INGRESS_L4 1400000 /* Ingress Stage 1 Lookup 4 */ +#define VCAP_CID_INGRESS_L5 1500000 /* Ingress Stage 1 Lookup 5 */ + +#define VCAP_CID_PREROUTING_IPV6 3000000 /* Prerouting Stage */ +#define VCAP_CID_PREROUTING 6000000 /* Prerouting Stage */ + +#define VCAP_CID_INGRESS_STAGE2_L0 8000000 /* Ingress Stage 2 Lookup 0 */ +#define VCAP_CID_INGRESS_STAGE2_L1 8100000 /* Ingress Stage 2 Lookup 1 */ +#define VCAP_CID_INGRESS_STAGE2_L2 8200000 /* Ingress Stage 2 Lookup 2 */ +#define VCAP_CID_INGRESS_STAGE2_L3 8300000 /* Ingress Stage 2 Lookup 3 */ + +#define VCAP_CID_EGRESS_L0 10000000 /* Egress Lookup 0 */ +#define VCAP_CID_EGRESS_L1 10100000 /* Egress Lookup 1 */ + +#define VCAP_CID_EGRESS_STAGE2_L0 20000000 /* Egress Stage 2 Lookup 0 */ +#define VCAP_CID_EGRESS_STAGE2_L1 20100000 /* Egress Stage 2 Lookup 1 */ + +/* Known users of the VCAP API */ +enum vcap_user { + VCAP_USER_PTP, + VCAP_USER_MRP, + VCAP_USER_CFM, + VCAP_USER_VLAN, + VCAP_USER_QOS, + VCAP_USER_VCAP_UTIL, + VCAP_USER_TC, + VCAP_USER_TC_EXTRA, + + /* add new users above here */ + + /* used to define VCAP_USER_MAX below */ + __VCAP_USER_AFTER_LAST, + VCAP_USER_MAX = __VCAP_USER_AFTER_LAST - 1, +}; + +/* VCAP information used for displaying data */ +struct vcap_statistics { + char *name; + int count; + const char * const *keyfield_set_names; + const char * const *actionfield_set_names; + const char * const *keyfield_names; + const char * const *actionfield_names; +}; + +/* VCAP key/action field type, position and width */ +struct vcap_field { + u16 type; + u16 width; + u16 offset; +}; + +/* VCAP keyset or actionset type and width */ +struct vcap_set { + u8 type_id; + u8 sw_per_item; + u8 sw_cnt; +}; + +/* VCAP typegroup position and bitvalue */ +struct vcap_typegroup { + u16 offset; + u16 width; + u16 value; +}; + +/* VCAP model data */ +struct vcap_info { + char *name; /* user-friendly name */ + u16 rows; /* number of row in instance */ + u16 sw_count; /* maximum subwords used per rule */ + u16 sw_width; /* bits per subword in a keyset */ + u16 sticky_width; /* sticky bits per rule */ + u16 act_width; /* bits per subword in an actionset */ + u16 default_cnt; /* number of default rules */ + u16 require_cnt_dis; /* not used */ + u16 version; /* vcap rtl version */ + const struct vcap_set *keyfield_set; /* keysets */ + int keyfield_set_size; /* number of keysets */ + const struct vcap_set *actionfield_set; /* actionsets */ + int actionfield_set_size; /* number of actionsets */ + /* map of keys per keyset */ + const struct vcap_field **keyfield_set_map; + /* number of entries in the above map */ + int *keyfield_set_map_size; + /* map of actions per actionset */ + const struct vcap_field **actionfield_set_map; + /* number of entries in the above map */ + int *actionfield_set_map_size; + /* map of keyset typegroups per subword size */ + const struct vcap_typegroup **keyfield_set_typegroups; + /* map of actionset typegroups per subword size */ + const struct vcap_typegroup **actionfield_set_typegroups; +}; + +enum vcap_field_type { + VCAP_FIELD_BIT, + VCAP_FIELD_U32, + VCAP_FIELD_U48, + VCAP_FIELD_U56, + VCAP_FIELD_U64, + VCAP_FIELD_U72, + VCAP_FIELD_U112, + VCAP_FIELD_U128, +}; + +/* VCAP rule data towards the VCAP cache */ +struct vcap_cache_data { + u32 *keystream; + u32 *maskstream; + u32 *actionstream; + u32 counter; + bool sticky; +}; + +/* Selects which part of the rule must be updated */ +enum vcap_selection { + VCAP_SEL_ENTRY = 0x01, + VCAP_SEL_ACTION = 0x02, + VCAP_SEL_COUNTER = 0x04, + VCAP_SEL_ALL = 0xff, +}; + +/* Commands towards the VCAP cache */ +enum vcap_command { + VCAP_CMD_WRITE = 0, + VCAP_CMD_READ = 1, + VCAP_CMD_MOVE_DOWN = 2, + VCAP_CMD_MOVE_UP = 3, + VCAP_CMD_INITIALIZE = 4, +}; + +enum vcap_rule_error { + VCAP_ERR_NONE = 0, /* No known error */ + VCAP_ERR_NO_ADMIN, /* No admin instance */ + VCAP_ERR_NO_NETDEV, /* No netdev instance */ + VCAP_ERR_NO_KEYSET_MATCH, /* No keyset matched the rule keys */ + VCAP_ERR_NO_ACTIONSET_MATCH, /* No actionset matched the rule actions */ + VCAP_ERR_NO_PORT_KEYSET_MATCH, /* No port keyset matched the rule keys */ +}; + +/* Administration of each VCAP instance */ +struct vcap_admin { + struct list_head list; /* for insertion in vcap_control */ + struct list_head rules; /* list of rules */ + enum vcap_type vtype; /* type of vcap */ + int vinst; /* instance number within the same type */ + int first_cid; /* first chain id in this vcap */ + int last_cid; /* last chain id in this vcap */ + int tgt_inst; /* hardware instance number */ + int lookups; /* number of lookups in this vcap type */ + int lookups_per_instance; /* number of lookups in this instance */ + int last_valid_addr; /* top of address range to be used */ + int first_valid_addr; /* bottom of address range to be used */ + int last_used_addr; /* address of lowest added rule */ + bool w32be; /* vcap uses "32bit-word big-endian" encoding */ + struct vcap_cache_data cache; /* encoded rule data */ +}; + +/* Client supplied VCAP rule data */ +struct vcap_rule { + int vcap_chain_id; /* chain used for this rule */ + enum vcap_user user; /* rule owner */ + u16 priority; + u32 id; /* vcap rule id, must be unique, 0 will auto-generate a value */ + u64 cookie; /* used by the client to identify the rule */ + struct list_head keyfields; /* list of vcap_client_keyfield */ + struct list_head actionfields; /* list of vcap_client_actionfield */ + enum vcap_keyfield_set keyset; /* keyset used: may be derived from fields */ + enum vcap_actionfield_set actionset; /* actionset used: may be derived from fields */ + enum vcap_rule_error exterr; /* extended error - used by TC */ + u64 client; /* space for client defined data */ +}; + +/* List of keysets */ +struct vcap_keyset_list { + int max; /* size of the keyset list */ + int cnt; /* count of keysets actually in the list */ + enum vcap_keyfield_set *keysets; /* the list of keysets */ +}; + +/* Client supplied VCAP callback operations */ +struct vcap_operations { + /* validate port keyset operation */ + enum vcap_keyfield_set (*validate_keyset) + (struct net_device *ndev, + struct vcap_admin *admin, + struct vcap_rule *rule, + struct vcap_keyset_list *kslist, + u16 l3_proto); + /* add default rule fields for the selected keyset operations */ + void (*add_default_fields) + (struct net_device *ndev, + struct vcap_admin *admin, + struct vcap_rule *rule); + /* cache operations */ + void (*cache_erase) + (struct vcap_admin *admin); + void (*cache_write) + (struct net_device *ndev, + struct vcap_admin *admin, + enum vcap_selection sel, + u32 idx, u32 count); + void (*cache_read) + (struct net_device *ndev, + struct vcap_admin *admin, + enum vcap_selection sel, + u32 idx, + u32 count); + /* block operations */ + void (*init) + (struct net_device *ndev, + struct vcap_admin *admin, + u32 addr, + u32 count); + void (*update) + (struct net_device *ndev, + struct vcap_admin *admin, + enum vcap_command cmd, + enum vcap_selection sel, + u32 addr); + void (*move) + (struct net_device *ndev, + struct vcap_admin *admin, + u32 addr, + int offset, + int count); + /* informational */ + int (*port_info) + (struct net_device *ndev, + enum vcap_type vtype, + int (*pf)(void *out, int arg, const char *fmt, ...), + void *out, + int arg); +}; + +/* VCAP API Client control interface */ +struct vcap_control { + u32 rule_id; /* last used rule id (unique across VCAP instances) */ + struct vcap_operations *ops; /* client supplied operations */ + const struct vcap_info *vcaps; /* client supplied vcap models */ + const struct vcap_statistics *stats; /* client supplied vcap stats */ + struct list_head list; /* list of vcap instances */ +}; + +/* Set client control interface on the API */ +int vcap_api_set_client(struct vcap_control *vctrl); + +#endif /* __VCAP_API__ */ From e8145e0685beee49dcb36cb4e1d9b00bf0d57bea Mon Sep 17 00:00:00 2001 From: Steen Hegelund Date: Thu, 20 Oct 2022 15:08:57 +0200 Subject: [PATCH 0276/2002] net: microchip: sparx5: Adding IS2 VCAP model to VCAP API This provides the Sparx5 Ingress Stage 2 (IS2) model and adds it to the VCAP control instance that will be provided to the VCAP API. The Sparx5 IS2 C code model is generated from the Sparx5 RTL design model. Signed-off-by: Steen Hegelund Signed-off-by: David S. Miller --- .../net/ethernet/microchip/sparx5/Makefile | 2 +- .../microchip/sparx5/sparx5_vcap_ag_api.c | 1351 +++++++++++++++++ .../microchip/sparx5/sparx5_vcap_ag_api.h | 18 + .../microchip/sparx5/sparx5_vcap_impl.c | 4 + 4 files changed, 1374 insertions(+), 1 deletion(-) create mode 100644 drivers/net/ethernet/microchip/sparx5/sparx5_vcap_ag_api.c create mode 100644 drivers/net/ethernet/microchip/sparx5/sparx5_vcap_ag_api.h diff --git a/drivers/net/ethernet/microchip/sparx5/Makefile b/drivers/net/ethernet/microchip/sparx5/Makefile index aa4dadb8a25d..b9c6831c2d92 100644 --- a/drivers/net/ethernet/microchip/sparx5/Makefile +++ b/drivers/net/ethernet/microchip/sparx5/Makefile @@ -9,7 +9,7 @@ sparx5-switch-y := sparx5_main.o sparx5_packet.o \ sparx5_netdev.o sparx5_phylink.o sparx5_port.o sparx5_mactable.o sparx5_vlan.o \ sparx5_switchdev.o sparx5_calendar.o sparx5_ethtool.o sparx5_fdma.o \ sparx5_ptp.o sparx5_pgid.o sparx5_tc.o sparx5_qos.o \ - sparx5_vcap_impl.o + sparx5_vcap_impl.o sparx5_vcap_ag_api.o # Provide include files ccflags-y += -I$(srctree)/drivers/net/ethernet/microchip/vcap diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_vcap_ag_api.c b/drivers/net/ethernet/microchip/sparx5/sparx5_vcap_ag_api.c new file mode 100644 index 000000000000..1bd987c664e8 --- /dev/null +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_vcap_ag_api.c @@ -0,0 +1,1351 @@ +// SPDX-License-Identifier: BSD-3-Clause +/* Copyright (C) 2022 Microchip Technology Inc. and its subsidiaries. + * Microchip VCAP API + */ + +/* This file is autogenerated by cml-utils 2022-10-13 10:04:41 +0200. + * Commit ID: fd7cafd175899f0672c73afb3a30fc872500ae86 + */ + +#include +#include + +#include "vcap_api.h" +#include "sparx5_vcap_ag_api.h" + +/* keyfields */ +static const struct vcap_field is2_mac_etype_keyfield[] = { + [VCAP_KF_TYPE] = { + .type = VCAP_FIELD_U32, + .offset = 0, + .width = 4, + }, + [VCAP_KF_LOOKUP_FIRST_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 4, + .width = 1, + }, + [VCAP_KF_LOOKUP_PAG] = { + .type = VCAP_FIELD_U32, + .offset = 5, + .width = 8, + }, + [VCAP_KF_IF_IGR_PORT_MASK_L3] = { + .type = VCAP_FIELD_BIT, + .offset = 13, + .width = 1, + }, + [VCAP_KF_IF_IGR_PORT_MASK_RNG] = { + .type = VCAP_FIELD_U32, + .offset = 14, + .width = 4, + }, + [VCAP_KF_IF_IGR_PORT_MASK_SEL] = { + .type = VCAP_FIELD_U32, + .offset = 18, + .width = 2, + }, + [VCAP_KF_IF_IGR_PORT_MASK] = { + .type = VCAP_FIELD_U32, + .offset = 20, + .width = 32, + }, + [VCAP_KF_L2_MC_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 52, + .width = 1, + }, + [VCAP_KF_L2_BC_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 53, + .width = 1, + }, + [VCAP_KF_8021Q_VLAN_TAGGED_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 54, + .width = 1, + }, + [VCAP_KF_ISDX_GT0_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 55, + .width = 1, + }, + [VCAP_KF_ISDX_CLS] = { + .type = VCAP_FIELD_U32, + .offset = 56, + .width = 12, + }, + [VCAP_KF_8021Q_VID_CLS] = { + .type = VCAP_FIELD_U32, + .offset = 68, + .width = 13, + }, + [VCAP_KF_8021Q_DEI_CLS] = { + .type = VCAP_FIELD_BIT, + .offset = 81, + .width = 1, + }, + [VCAP_KF_8021Q_PCP_CLS] = { + .type = VCAP_FIELD_U32, + .offset = 82, + .width = 3, + }, + [VCAP_KF_L2_FWD_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 85, + .width = 1, + }, + [VCAP_KF_L3_RT_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 88, + .width = 1, + }, + [VCAP_KF_L3_DST_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 89, + .width = 1, + }, + [VCAP_KF_L2_DMAC] = { + .type = VCAP_FIELD_U48, + .offset = 90, + .width = 48, + }, + [VCAP_KF_L2_SMAC] = { + .type = VCAP_FIELD_U48, + .offset = 138, + .width = 48, + }, + [VCAP_KF_ETYPE_LEN_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 186, + .width = 1, + }, + [VCAP_KF_ETYPE] = { + .type = VCAP_FIELD_U32, + .offset = 187, + .width = 16, + }, + [VCAP_KF_L2_PAYLOAD_ETYPE] = { + .type = VCAP_FIELD_U64, + .offset = 203, + .width = 64, + }, + [VCAP_KF_L4_RNG] = { + .type = VCAP_FIELD_U32, + .offset = 267, + .width = 16, + }, + [VCAP_KF_OAM_CCM_CNTS_EQ0] = { + .type = VCAP_FIELD_BIT, + .offset = 283, + .width = 1, + }, + [VCAP_KF_OAM_Y1731_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 284, + .width = 1, + }, +}; + +static const struct vcap_field is2_arp_keyfield[] = { + [VCAP_KF_TYPE] = { + .type = VCAP_FIELD_U32, + .offset = 0, + .width = 4, + }, + [VCAP_KF_LOOKUP_FIRST_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 4, + .width = 1, + }, + [VCAP_KF_LOOKUP_PAG] = { + .type = VCAP_FIELD_U32, + .offset = 5, + .width = 8, + }, + [VCAP_KF_IF_IGR_PORT_MASK_L3] = { + .type = VCAP_FIELD_BIT, + .offset = 13, + .width = 1, + }, + [VCAP_KF_IF_IGR_PORT_MASK_RNG] = { + .type = VCAP_FIELD_U32, + .offset = 14, + .width = 4, + }, + [VCAP_KF_IF_IGR_PORT_MASK_SEL] = { + .type = VCAP_FIELD_U32, + .offset = 18, + .width = 2, + }, + [VCAP_KF_IF_IGR_PORT_MASK] = { + .type = VCAP_FIELD_U32, + .offset = 20, + .width = 32, + }, + [VCAP_KF_L2_MC_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 52, + .width = 1, + }, + [VCAP_KF_L2_BC_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 53, + .width = 1, + }, + [VCAP_KF_8021Q_VLAN_TAGGED_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 54, + .width = 1, + }, + [VCAP_KF_ISDX_GT0_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 55, + .width = 1, + }, + [VCAP_KF_ISDX_CLS] = { + .type = VCAP_FIELD_U32, + .offset = 56, + .width = 12, + }, + [VCAP_KF_8021Q_VID_CLS] = { + .type = VCAP_FIELD_U32, + .offset = 68, + .width = 13, + }, + [VCAP_KF_8021Q_DEI_CLS] = { + .type = VCAP_FIELD_BIT, + .offset = 81, + .width = 1, + }, + [VCAP_KF_8021Q_PCP_CLS] = { + .type = VCAP_FIELD_U32, + .offset = 82, + .width = 3, + }, + [VCAP_KF_L2_FWD_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 85, + .width = 1, + }, + [VCAP_KF_L2_SMAC] = { + .type = VCAP_FIELD_U48, + .offset = 86, + .width = 48, + }, + [VCAP_KF_ARP_ADDR_SPACE_OK_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 134, + .width = 1, + }, + [VCAP_KF_ARP_PROTO_SPACE_OK_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 135, + .width = 1, + }, + [VCAP_KF_ARP_LEN_OK_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 136, + .width = 1, + }, + [VCAP_KF_ARP_TGT_MATCH_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 137, + .width = 1, + }, + [VCAP_KF_ARP_SENDER_MATCH_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 138, + .width = 1, + }, + [VCAP_KF_ARP_OPCODE_UNKNOWN_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 139, + .width = 1, + }, + [VCAP_KF_ARP_OPCODE] = { + .type = VCAP_FIELD_U32, + .offset = 140, + .width = 2, + }, + [VCAP_KF_L3_IP4_DIP] = { + .type = VCAP_FIELD_U32, + .offset = 142, + .width = 32, + }, + [VCAP_KF_L3_IP4_SIP] = { + .type = VCAP_FIELD_U32, + .offset = 174, + .width = 32, + }, + [VCAP_KF_L3_DIP_EQ_SIP_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 206, + .width = 1, + }, + [VCAP_KF_L4_RNG] = { + .type = VCAP_FIELD_U32, + .offset = 207, + .width = 16, + }, +}; + +static const struct vcap_field is2_ip4_tcp_udp_keyfield[] = { + [VCAP_KF_TYPE] = { + .type = VCAP_FIELD_U32, + .offset = 0, + .width = 4, + }, + [VCAP_KF_LOOKUP_FIRST_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 4, + .width = 1, + }, + [VCAP_KF_LOOKUP_PAG] = { + .type = VCAP_FIELD_U32, + .offset = 5, + .width = 8, + }, + [VCAP_KF_IF_IGR_PORT_MASK_L3] = { + .type = VCAP_FIELD_BIT, + .offset = 13, + .width = 1, + }, + [VCAP_KF_IF_IGR_PORT_MASK_RNG] = { + .type = VCAP_FIELD_U32, + .offset = 14, + .width = 4, + }, + [VCAP_KF_IF_IGR_PORT_MASK_SEL] = { + .type = VCAP_FIELD_U32, + .offset = 18, + .width = 2, + }, + [VCAP_KF_IF_IGR_PORT_MASK] = { + .type = VCAP_FIELD_U32, + .offset = 20, + .width = 32, + }, + [VCAP_KF_L2_MC_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 52, + .width = 1, + }, + [VCAP_KF_L2_BC_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 53, + .width = 1, + }, + [VCAP_KF_8021Q_VLAN_TAGGED_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 54, + .width = 1, + }, + [VCAP_KF_ISDX_GT0_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 55, + .width = 1, + }, + [VCAP_KF_ISDX_CLS] = { + .type = VCAP_FIELD_U32, + .offset = 56, + .width = 12, + }, + [VCAP_KF_8021Q_VID_CLS] = { + .type = VCAP_FIELD_U32, + .offset = 68, + .width = 13, + }, + [VCAP_KF_8021Q_DEI_CLS] = { + .type = VCAP_FIELD_BIT, + .offset = 81, + .width = 1, + }, + [VCAP_KF_8021Q_PCP_CLS] = { + .type = VCAP_FIELD_U32, + .offset = 82, + .width = 3, + }, + [VCAP_KF_L2_FWD_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 85, + .width = 1, + }, + [VCAP_KF_L3_RT_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 88, + .width = 1, + }, + [VCAP_KF_L3_DST_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 89, + .width = 1, + }, + [VCAP_KF_IP4_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 90, + .width = 1, + }, + [VCAP_KF_L3_FRAGMENT_TYPE] = { + .type = VCAP_FIELD_U32, + .offset = 91, + .width = 2, + }, + [VCAP_KF_L3_FRAG_INVLD_L4_LEN] = { + .type = VCAP_FIELD_BIT, + .offset = 93, + .width = 1, + }, + [VCAP_KF_L3_OPTIONS_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 94, + .width = 1, + }, + [VCAP_KF_L3_TTL_GT0] = { + .type = VCAP_FIELD_BIT, + .offset = 95, + .width = 1, + }, + [VCAP_KF_L3_TOS] = { + .type = VCAP_FIELD_U32, + .offset = 96, + .width = 8, + }, + [VCAP_KF_L3_IP4_DIP] = { + .type = VCAP_FIELD_U32, + .offset = 104, + .width = 32, + }, + [VCAP_KF_L3_IP4_SIP] = { + .type = VCAP_FIELD_U32, + .offset = 136, + .width = 32, + }, + [VCAP_KF_L3_DIP_EQ_SIP_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 168, + .width = 1, + }, + [VCAP_KF_TCP_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 169, + .width = 1, + }, + [VCAP_KF_L4_DPORT] = { + .type = VCAP_FIELD_U32, + .offset = 170, + .width = 16, + }, + [VCAP_KF_L4_SPORT] = { + .type = VCAP_FIELD_U32, + .offset = 186, + .width = 16, + }, + [VCAP_KF_L4_RNG] = { + .type = VCAP_FIELD_U32, + .offset = 202, + .width = 16, + }, + [VCAP_KF_L4_SPORT_EQ_DPORT_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 218, + .width = 1, + }, + [VCAP_KF_L4_SEQUENCE_EQ0_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 219, + .width = 1, + }, + [VCAP_KF_L4_FIN] = { + .type = VCAP_FIELD_BIT, + .offset = 220, + .width = 1, + }, + [VCAP_KF_L4_SYN] = { + .type = VCAP_FIELD_BIT, + .offset = 221, + .width = 1, + }, + [VCAP_KF_L4_RST] = { + .type = VCAP_FIELD_BIT, + .offset = 222, + .width = 1, + }, + [VCAP_KF_L4_PSH] = { + .type = VCAP_FIELD_BIT, + .offset = 223, + .width = 1, + }, + [VCAP_KF_L4_ACK] = { + .type = VCAP_FIELD_BIT, + .offset = 224, + .width = 1, + }, + [VCAP_KF_L4_URG] = { + .type = VCAP_FIELD_BIT, + .offset = 225, + .width = 1, + }, + [VCAP_KF_L4_PAYLOAD] = { + .type = VCAP_FIELD_U64, + .offset = 226, + .width = 64, + }, +}; + +static const struct vcap_field is2_ip4_other_keyfield[] = { + [VCAP_KF_TYPE] = { + .type = VCAP_FIELD_U32, + .offset = 0, + .width = 4, + }, + [VCAP_KF_LOOKUP_FIRST_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 4, + .width = 1, + }, + [VCAP_KF_LOOKUP_PAG] = { + .type = VCAP_FIELD_U32, + .offset = 5, + .width = 8, + }, + [VCAP_KF_IF_IGR_PORT_MASK_L3] = { + .type = VCAP_FIELD_BIT, + .offset = 13, + .width = 1, + }, + [VCAP_KF_IF_IGR_PORT_MASK_RNG] = { + .type = VCAP_FIELD_U32, + .offset = 14, + .width = 4, + }, + [VCAP_KF_IF_IGR_PORT_MASK_SEL] = { + .type = VCAP_FIELD_U32, + .offset = 18, + .width = 2, + }, + [VCAP_KF_IF_IGR_PORT_MASK] = { + .type = VCAP_FIELD_U32, + .offset = 20, + .width = 32, + }, + [VCAP_KF_L2_MC_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 52, + .width = 1, + }, + [VCAP_KF_L2_BC_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 53, + .width = 1, + }, + [VCAP_KF_8021Q_VLAN_TAGGED_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 54, + .width = 1, + }, + [VCAP_KF_ISDX_GT0_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 55, + .width = 1, + }, + [VCAP_KF_ISDX_CLS] = { + .type = VCAP_FIELD_U32, + .offset = 56, + .width = 12, + }, + [VCAP_KF_8021Q_VID_CLS] = { + .type = VCAP_FIELD_U32, + .offset = 68, + .width = 13, + }, + [VCAP_KF_8021Q_DEI_CLS] = { + .type = VCAP_FIELD_BIT, + .offset = 81, + .width = 1, + }, + [VCAP_KF_8021Q_PCP_CLS] = { + .type = VCAP_FIELD_U32, + .offset = 82, + .width = 3, + }, + [VCAP_KF_L2_FWD_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 85, + .width = 1, + }, + [VCAP_KF_L3_RT_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 88, + .width = 1, + }, + [VCAP_KF_L3_DST_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 89, + .width = 1, + }, + [VCAP_KF_IP4_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 90, + .width = 1, + }, + [VCAP_KF_L3_FRAGMENT_TYPE] = { + .type = VCAP_FIELD_U32, + .offset = 91, + .width = 2, + }, + [VCAP_KF_L3_FRAG_INVLD_L4_LEN] = { + .type = VCAP_FIELD_BIT, + .offset = 93, + .width = 1, + }, + [VCAP_KF_L3_OPTIONS_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 94, + .width = 1, + }, + [VCAP_KF_L3_TTL_GT0] = { + .type = VCAP_FIELD_BIT, + .offset = 95, + .width = 1, + }, + [VCAP_KF_L3_TOS] = { + .type = VCAP_FIELD_U32, + .offset = 96, + .width = 8, + }, + [VCAP_KF_L3_IP4_DIP] = { + .type = VCAP_FIELD_U32, + .offset = 104, + .width = 32, + }, + [VCAP_KF_L3_IP4_SIP] = { + .type = VCAP_FIELD_U32, + .offset = 136, + .width = 32, + }, + [VCAP_KF_L3_DIP_EQ_SIP_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 168, + .width = 1, + }, + [VCAP_KF_L3_IP_PROTO] = { + .type = VCAP_FIELD_U32, + .offset = 169, + .width = 8, + }, + [VCAP_KF_L4_RNG] = { + .type = VCAP_FIELD_U32, + .offset = 177, + .width = 16, + }, + [VCAP_KF_L3_PAYLOAD] = { + .type = VCAP_FIELD_U112, + .offset = 193, + .width = 96, + }, +}; + +static const struct vcap_field is2_ip6_std_keyfield[] = { + [VCAP_KF_TYPE] = { + .type = VCAP_FIELD_U32, + .offset = 0, + .width = 4, + }, + [VCAP_KF_LOOKUP_FIRST_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 4, + .width = 1, + }, + [VCAP_KF_LOOKUP_PAG] = { + .type = VCAP_FIELD_U32, + .offset = 5, + .width = 8, + }, + [VCAP_KF_IF_IGR_PORT_MASK_L3] = { + .type = VCAP_FIELD_BIT, + .offset = 13, + .width = 1, + }, + [VCAP_KF_IF_IGR_PORT_MASK_RNG] = { + .type = VCAP_FIELD_U32, + .offset = 14, + .width = 4, + }, + [VCAP_KF_IF_IGR_PORT_MASK_SEL] = { + .type = VCAP_FIELD_U32, + .offset = 18, + .width = 2, + }, + [VCAP_KF_IF_IGR_PORT_MASK] = { + .type = VCAP_FIELD_U32, + .offset = 20, + .width = 32, + }, + [VCAP_KF_L2_MC_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 52, + .width = 1, + }, + [VCAP_KF_L2_BC_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 53, + .width = 1, + }, + [VCAP_KF_8021Q_VLAN_TAGGED_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 54, + .width = 1, + }, + [VCAP_KF_ISDX_GT0_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 55, + .width = 1, + }, + [VCAP_KF_ISDX_CLS] = { + .type = VCAP_FIELD_U32, + .offset = 56, + .width = 12, + }, + [VCAP_KF_8021Q_VID_CLS] = { + .type = VCAP_FIELD_U32, + .offset = 68, + .width = 13, + }, + [VCAP_KF_8021Q_DEI_CLS] = { + .type = VCAP_FIELD_BIT, + .offset = 81, + .width = 1, + }, + [VCAP_KF_8021Q_PCP_CLS] = { + .type = VCAP_FIELD_U32, + .offset = 82, + .width = 3, + }, + [VCAP_KF_L2_FWD_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 85, + .width = 1, + }, + [VCAP_KF_L3_RT_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 88, + .width = 1, + }, + [VCAP_KF_L3_TTL_GT0] = { + .type = VCAP_FIELD_BIT, + .offset = 90, + .width = 1, + }, + [VCAP_KF_L3_IP6_SIP] = { + .type = VCAP_FIELD_U128, + .offset = 91, + .width = 128, + }, + [VCAP_KF_L3_DIP_EQ_SIP_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 219, + .width = 1, + }, + [VCAP_KF_L3_IP_PROTO] = { + .type = VCAP_FIELD_U32, + .offset = 220, + .width = 8, + }, + [VCAP_KF_L4_RNG] = { + .type = VCAP_FIELD_U32, + .offset = 228, + .width = 16, + }, + [VCAP_KF_L3_PAYLOAD] = { + .type = VCAP_FIELD_U48, + .offset = 244, + .width = 40, + }, +}; + +static const struct vcap_field is2_ip_7tuple_keyfield[] = { + [VCAP_KF_TYPE] = { + .type = VCAP_FIELD_U32, + .offset = 0, + .width = 2, + }, + [VCAP_KF_LOOKUP_FIRST_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 2, + .width = 1, + }, + [VCAP_KF_LOOKUP_PAG] = { + .type = VCAP_FIELD_U32, + .offset = 3, + .width = 8, + }, + [VCAP_KF_IF_IGR_PORT_MASK_L3] = { + .type = VCAP_FIELD_BIT, + .offset = 11, + .width = 1, + }, + [VCAP_KF_IF_IGR_PORT_MASK_RNG] = { + .type = VCAP_FIELD_U32, + .offset = 12, + .width = 4, + }, + [VCAP_KF_IF_IGR_PORT_MASK_SEL] = { + .type = VCAP_FIELD_U32, + .offset = 16, + .width = 2, + }, + [VCAP_KF_IF_IGR_PORT_MASK] = { + .type = VCAP_FIELD_U72, + .offset = 18, + .width = 65, + }, + [VCAP_KF_L2_MC_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 83, + .width = 1, + }, + [VCAP_KF_L2_BC_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 84, + .width = 1, + }, + [VCAP_KF_8021Q_VLAN_TAGGED_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 85, + .width = 1, + }, + [VCAP_KF_ISDX_GT0_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 86, + .width = 1, + }, + [VCAP_KF_ISDX_CLS] = { + .type = VCAP_FIELD_U32, + .offset = 87, + .width = 12, + }, + [VCAP_KF_8021Q_VID_CLS] = { + .type = VCAP_FIELD_U32, + .offset = 99, + .width = 13, + }, + [VCAP_KF_8021Q_DEI_CLS] = { + .type = VCAP_FIELD_BIT, + .offset = 112, + .width = 1, + }, + [VCAP_KF_8021Q_PCP_CLS] = { + .type = VCAP_FIELD_U32, + .offset = 113, + .width = 3, + }, + [VCAP_KF_L2_FWD_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 116, + .width = 1, + }, + [VCAP_KF_L3_RT_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 119, + .width = 1, + }, + [VCAP_KF_L3_DST_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 120, + .width = 1, + }, + [VCAP_KF_L2_DMAC] = { + .type = VCAP_FIELD_U48, + .offset = 121, + .width = 48, + }, + [VCAP_KF_L2_SMAC] = { + .type = VCAP_FIELD_U48, + .offset = 169, + .width = 48, + }, + [VCAP_KF_IP4_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 217, + .width = 1, + }, + [VCAP_KF_L3_TTL_GT0] = { + .type = VCAP_FIELD_BIT, + .offset = 218, + .width = 1, + }, + [VCAP_KF_L3_TOS] = { + .type = VCAP_FIELD_U32, + .offset = 219, + .width = 8, + }, + [VCAP_KF_L3_IP6_DIP] = { + .type = VCAP_FIELD_U128, + .offset = 227, + .width = 128, + }, + [VCAP_KF_L3_IP6_SIP] = { + .type = VCAP_FIELD_U128, + .offset = 355, + .width = 128, + }, + [VCAP_KF_L3_DIP_EQ_SIP_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 483, + .width = 1, + }, + [VCAP_KF_TCP_UDP_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 484, + .width = 1, + }, + [VCAP_KF_TCP_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 485, + .width = 1, + }, + [VCAP_KF_L4_DPORT] = { + .type = VCAP_FIELD_U32, + .offset = 486, + .width = 16, + }, + [VCAP_KF_L4_SPORT] = { + .type = VCAP_FIELD_U32, + .offset = 502, + .width = 16, + }, + [VCAP_KF_L4_RNG] = { + .type = VCAP_FIELD_U32, + .offset = 518, + .width = 16, + }, + [VCAP_KF_L4_SPORT_EQ_DPORT_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 534, + .width = 1, + }, + [VCAP_KF_L4_SEQUENCE_EQ0_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 535, + .width = 1, + }, + [VCAP_KF_L4_FIN] = { + .type = VCAP_FIELD_BIT, + .offset = 536, + .width = 1, + }, + [VCAP_KF_L4_SYN] = { + .type = VCAP_FIELD_BIT, + .offset = 537, + .width = 1, + }, + [VCAP_KF_L4_RST] = { + .type = VCAP_FIELD_BIT, + .offset = 538, + .width = 1, + }, + [VCAP_KF_L4_PSH] = { + .type = VCAP_FIELD_BIT, + .offset = 539, + .width = 1, + }, + [VCAP_KF_L4_ACK] = { + .type = VCAP_FIELD_BIT, + .offset = 540, + .width = 1, + }, + [VCAP_KF_L4_URG] = { + .type = VCAP_FIELD_BIT, + .offset = 541, + .width = 1, + }, + [VCAP_KF_L4_PAYLOAD] = { + .type = VCAP_FIELD_U64, + .offset = 542, + .width = 64, + }, +}; + +/* keyfield_set */ +static const struct vcap_set is2_keyfield_set[] = { + [VCAP_KFS_MAC_ETYPE] = { + .type_id = 0, + .sw_per_item = 6, + .sw_cnt = 2, + }, + [VCAP_KFS_ARP] = { + .type_id = 3, + .sw_per_item = 6, + .sw_cnt = 2, + }, + [VCAP_KFS_IP4_TCP_UDP] = { + .type_id = 4, + .sw_per_item = 6, + .sw_cnt = 2, + }, + [VCAP_KFS_IP4_OTHER] = { + .type_id = 5, + .sw_per_item = 6, + .sw_cnt = 2, + }, + [VCAP_KFS_IP6_STD] = { + .type_id = 6, + .sw_per_item = 6, + .sw_cnt = 2, + }, + [VCAP_KFS_IP_7TUPLE] = { + .type_id = 1, + .sw_per_item = 12, + .sw_cnt = 1, + }, +}; + +/* keyfield_set map */ +static const struct vcap_field *is2_keyfield_set_map[] = { + [VCAP_KFS_MAC_ETYPE] = is2_mac_etype_keyfield, + [VCAP_KFS_ARP] = is2_arp_keyfield, + [VCAP_KFS_IP4_TCP_UDP] = is2_ip4_tcp_udp_keyfield, + [VCAP_KFS_IP4_OTHER] = is2_ip4_other_keyfield, + [VCAP_KFS_IP6_STD] = is2_ip6_std_keyfield, + [VCAP_KFS_IP_7TUPLE] = is2_ip_7tuple_keyfield, +}; + +/* keyfield_set map sizes */ +static int is2_keyfield_set_map_size[] = { + [VCAP_KFS_MAC_ETYPE] = ARRAY_SIZE(is2_mac_etype_keyfield), + [VCAP_KFS_ARP] = ARRAY_SIZE(is2_arp_keyfield), + [VCAP_KFS_IP4_TCP_UDP] = ARRAY_SIZE(is2_ip4_tcp_udp_keyfield), + [VCAP_KFS_IP4_OTHER] = ARRAY_SIZE(is2_ip4_other_keyfield), + [VCAP_KFS_IP6_STD] = ARRAY_SIZE(is2_ip6_std_keyfield), + [VCAP_KFS_IP_7TUPLE] = ARRAY_SIZE(is2_ip_7tuple_keyfield), +}; + +/* actionfields */ +static const struct vcap_field is2_base_type_actionfield[] = { + [VCAP_AF_PIPELINE_FORCE_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 1, + .width = 1, + }, + [VCAP_AF_PIPELINE_PT] = { + .type = VCAP_FIELD_U32, + .offset = 2, + .width = 5, + }, + [VCAP_AF_HIT_ME_ONCE] = { + .type = VCAP_FIELD_BIT, + .offset = 7, + .width = 1, + }, + [VCAP_AF_INTR_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 8, + .width = 1, + }, + [VCAP_AF_CPU_COPY_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 9, + .width = 1, + }, + [VCAP_AF_CPU_QUEUE_NUM] = { + .type = VCAP_FIELD_U32, + .offset = 10, + .width = 3, + }, + [VCAP_AF_LRN_DIS] = { + .type = VCAP_FIELD_BIT, + .offset = 14, + .width = 1, + }, + [VCAP_AF_RT_DIS] = { + .type = VCAP_FIELD_BIT, + .offset = 15, + .width = 1, + }, + [VCAP_AF_POLICE_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 16, + .width = 1, + }, + [VCAP_AF_POLICE_IDX] = { + .type = VCAP_FIELD_U32, + .offset = 17, + .width = 6, + }, + [VCAP_AF_IGNORE_PIPELINE_CTRL] = { + .type = VCAP_FIELD_BIT, + .offset = 23, + .width = 1, + }, + [VCAP_AF_MASK_MODE] = { + .type = VCAP_FIELD_U32, + .offset = 27, + .width = 3, + }, + [VCAP_AF_PORT_MASK] = { + .type = VCAP_FIELD_U72, + .offset = 30, + .width = 68, + }, + [VCAP_AF_MIRROR_PROBE] = { + .type = VCAP_FIELD_U32, + .offset = 111, + .width = 2, + }, + [VCAP_AF_MATCH_ID] = { + .type = VCAP_FIELD_U32, + .offset = 159, + .width = 16, + }, + [VCAP_AF_MATCH_ID_MASK] = { + .type = VCAP_FIELD_U32, + .offset = 175, + .width = 16, + }, + [VCAP_AF_CNT_ID] = { + .type = VCAP_FIELD_U32, + .offset = 191, + .width = 12, + }, +}; + +/* actionfield_set */ +static const struct vcap_set is2_actionfield_set[] = { + [VCAP_AFS_BASE_TYPE] = { + .type_id = -1, + .sw_per_item = 3, + .sw_cnt = 4, + }, +}; + +/* actionfield_set map */ +static const struct vcap_field *is2_actionfield_set_map[] = { + [VCAP_AFS_BASE_TYPE] = is2_base_type_actionfield, +}; + +/* actionfield_set map size */ +static int is2_actionfield_set_map_size[] = { + [VCAP_AFS_BASE_TYPE] = ARRAY_SIZE(is2_base_type_actionfield), +}; + +/* Type Groups */ +static const struct vcap_typegroup is2_x12_keyfield_set_typegroups[] = { + { + .offset = 0, + .width = 3, + .value = 4, + }, + { + .offset = 156, + .width = 1, + .value = 0, + }, + { + .offset = 312, + .width = 2, + .value = 0, + }, + { + .offset = 468, + .width = 1, + .value = 0, + }, + {} +}; + +static const struct vcap_typegroup is2_x6_keyfield_set_typegroups[] = { + { + .offset = 0, + .width = 2, + .value = 2, + }, + { + .offset = 156, + .width = 1, + .value = 0, + }, + {} +}; + +static const struct vcap_typegroup is2_x3_keyfield_set_typegroups[] = { + {} +}; + +static const struct vcap_typegroup is2_x1_keyfield_set_typegroups[] = { + {} +}; + +static const struct vcap_typegroup *is2_keyfield_set_typegroups[] = { + [12] = is2_x12_keyfield_set_typegroups, + [6] = is2_x6_keyfield_set_typegroups, + [3] = is2_x3_keyfield_set_typegroups, + [1] = is2_x1_keyfield_set_typegroups, + [13] = NULL, +}; + +static const struct vcap_typegroup is2_x3_actionfield_set_typegroups[] = { + { + .offset = 0, + .width = 2, + .value = 2, + }, + { + .offset = 110, + .width = 1, + .value = 0, + }, + { + .offset = 220, + .width = 1, + .value = 0, + }, + {} +}; + +static const struct vcap_typegroup is2_x1_actionfield_set_typegroups[] = { + {} +}; + +static const struct vcap_typegroup *is2_actionfield_set_typegroups[] = { + [3] = is2_x3_actionfield_set_typegroups, + [1] = is2_x1_actionfield_set_typegroups, + [13] = NULL, +}; + +/* Keyfieldset names */ +static const char * const vcap_keyfield_set_names[] = { + [VCAP_KFS_NO_VALUE] = "(None)", + [VCAP_KFS_ARP] = "VCAP_KFS_ARP", + [VCAP_KFS_IP4_OTHER] = "VCAP_KFS_IP4_OTHER", + [VCAP_KFS_IP4_TCP_UDP] = "VCAP_KFS_IP4_TCP_UDP", + [VCAP_KFS_IP6_STD] = "VCAP_KFS_IP6_STD", + [VCAP_KFS_IP_7TUPLE] = "VCAP_KFS_IP_7TUPLE", + [VCAP_KFS_MAC_ETYPE] = "VCAP_KFS_MAC_ETYPE", +}; + +/* Actionfieldset names */ +static const char * const vcap_actionfield_set_names[] = { + [VCAP_AFS_NO_VALUE] = "(None)", + [VCAP_AFS_BASE_TYPE] = "VCAP_AFS_BASE_TYPE", +}; + +/* Keyfield names */ +static const char * const vcap_keyfield_names[] = { + [VCAP_KF_NO_VALUE] = "(None)", + [VCAP_KF_8021Q_DEI_CLS] = "8021Q_DEI_CLS", + [VCAP_KF_8021Q_PCP_CLS] = "8021Q_PCP_CLS", + [VCAP_KF_8021Q_VID_CLS] = "8021Q_VID_CLS", + [VCAP_KF_8021Q_VLAN_TAGGED_IS] = "8021Q_VLAN_TAGGED_IS", + [VCAP_KF_ARP_ADDR_SPACE_OK_IS] = "ARP_ADDR_SPACE_OK_IS", + [VCAP_KF_ARP_LEN_OK_IS] = "ARP_LEN_OK_IS", + [VCAP_KF_ARP_OPCODE] = "ARP_OPCODE", + [VCAP_KF_ARP_OPCODE_UNKNOWN_IS] = "ARP_OPCODE_UNKNOWN_IS", + [VCAP_KF_ARP_PROTO_SPACE_OK_IS] = "ARP_PROTO_SPACE_OK_IS", + [VCAP_KF_ARP_SENDER_MATCH_IS] = "ARP_SENDER_MATCH_IS", + [VCAP_KF_ARP_TGT_MATCH_IS] = "ARP_TGT_MATCH_IS", + [VCAP_KF_ETYPE] = "ETYPE", + [VCAP_KF_ETYPE_LEN_IS] = "ETYPE_LEN_IS", + [VCAP_KF_IF_IGR_PORT_MASK] = "IF_IGR_PORT_MASK", + [VCAP_KF_IF_IGR_PORT_MASK_L3] = "IF_IGR_PORT_MASK_L3", + [VCAP_KF_IF_IGR_PORT_MASK_RNG] = "IF_IGR_PORT_MASK_RNG", + [VCAP_KF_IF_IGR_PORT_MASK_SEL] = "IF_IGR_PORT_MASK_SEL", + [VCAP_KF_IP4_IS] = "IP4_IS", + [VCAP_KF_ISDX_CLS] = "ISDX_CLS", + [VCAP_KF_ISDX_GT0_IS] = "ISDX_GT0_IS", + [VCAP_KF_L2_BC_IS] = "L2_BC_IS", + [VCAP_KF_L2_DMAC] = "L2_DMAC", + [VCAP_KF_L2_FWD_IS] = "L2_FWD_IS", + [VCAP_KF_L2_MC_IS] = "L2_MC_IS", + [VCAP_KF_L2_PAYLOAD_ETYPE] = "L2_PAYLOAD_ETYPE", + [VCAP_KF_L2_SMAC] = "L2_SMAC", + [VCAP_KF_L3_DIP_EQ_SIP_IS] = "L3_DIP_EQ_SIP_IS", + [VCAP_KF_L3_DST_IS] = "L3_DST_IS", + [VCAP_KF_L3_FRAGMENT_TYPE] = "L3_FRAGMENT_TYPE", + [VCAP_KF_L3_FRAG_INVLD_L4_LEN] = "L3_FRAG_INVLD_L4_LEN", + [VCAP_KF_L3_IP4_DIP] = "L3_IP4_DIP", + [VCAP_KF_L3_IP4_SIP] = "L3_IP4_SIP", + [VCAP_KF_L3_IP6_DIP] = "L3_IP6_DIP", + [VCAP_KF_L3_IP6_SIP] = "L3_IP6_SIP", + [VCAP_KF_L3_IP_PROTO] = "L3_IP_PROTO", + [VCAP_KF_L3_OPTIONS_IS] = "L3_OPTIONS_IS", + [VCAP_KF_L3_PAYLOAD] = "L3_PAYLOAD", + [VCAP_KF_L3_RT_IS] = "L3_RT_IS", + [VCAP_KF_L3_TOS] = "L3_TOS", + [VCAP_KF_L3_TTL_GT0] = "L3_TTL_GT0", + [VCAP_KF_L4_ACK] = "L4_ACK", + [VCAP_KF_L4_DPORT] = "L4_DPORT", + [VCAP_KF_L4_FIN] = "L4_FIN", + [VCAP_KF_L4_PAYLOAD] = "L4_PAYLOAD", + [VCAP_KF_L4_PSH] = "L4_PSH", + [VCAP_KF_L4_RNG] = "L4_RNG", + [VCAP_KF_L4_RST] = "L4_RST", + [VCAP_KF_L4_SEQUENCE_EQ0_IS] = "L4_SEQUENCE_EQ0_IS", + [VCAP_KF_L4_SPORT] = "L4_SPORT", + [VCAP_KF_L4_SPORT_EQ_DPORT_IS] = "L4_SPORT_EQ_DPORT_IS", + [VCAP_KF_L4_SYN] = "L4_SYN", + [VCAP_KF_L4_URG] = "L4_URG", + [VCAP_KF_LOOKUP_FIRST_IS] = "LOOKUP_FIRST_IS", + [VCAP_KF_LOOKUP_PAG] = "LOOKUP_PAG", + [VCAP_KF_OAM_CCM_CNTS_EQ0] = "OAM_CCM_CNTS_EQ0", + [VCAP_KF_OAM_Y1731_IS] = "OAM_Y1731_IS", + [VCAP_KF_TCP_IS] = "TCP_IS", + [VCAP_KF_TCP_UDP_IS] = "TCP_UDP_IS", + [VCAP_KF_TYPE] = "TYPE", +}; + +/* Actionfield names */ +static const char * const vcap_actionfield_names[] = { + [VCAP_AF_NO_VALUE] = "(None)", + [VCAP_AF_CNT_ID] = "CNT_ID", + [VCAP_AF_CPU_COPY_ENA] = "CPU_COPY_ENA", + [VCAP_AF_CPU_QUEUE_NUM] = "CPU_QUEUE_NUM", + [VCAP_AF_HIT_ME_ONCE] = "HIT_ME_ONCE", + [VCAP_AF_IGNORE_PIPELINE_CTRL] = "IGNORE_PIPELINE_CTRL", + [VCAP_AF_INTR_ENA] = "INTR_ENA", + [VCAP_AF_LRN_DIS] = "LRN_DIS", + [VCAP_AF_MASK_MODE] = "MASK_MODE", + [VCAP_AF_MATCH_ID] = "MATCH_ID", + [VCAP_AF_MATCH_ID_MASK] = "MATCH_ID_MASK", + [VCAP_AF_MIRROR_PROBE] = "MIRROR_PROBE", + [VCAP_AF_PIPELINE_FORCE_ENA] = "PIPELINE_FORCE_ENA", + [VCAP_AF_PIPELINE_PT] = "PIPELINE_PT", + [VCAP_AF_POLICE_ENA] = "POLICE_ENA", + [VCAP_AF_POLICE_IDX] = "POLICE_IDX", + [VCAP_AF_PORT_MASK] = "PORT_MASK", + [VCAP_AF_RT_DIS] = "RT_DIS", +}; + +/* VCAPs */ +const struct vcap_info sparx5_vcaps[] = { + [VCAP_TYPE_IS2] = { + .name = "is2", + .rows = 256, + .sw_count = 12, + .sw_width = 52, + .sticky_width = 1, + .act_width = 110, + .default_cnt = 73, + .require_cnt_dis = 0, + .version = 1, + .keyfield_set = is2_keyfield_set, + .keyfield_set_size = ARRAY_SIZE(is2_keyfield_set), + .actionfield_set = is2_actionfield_set, + .actionfield_set_size = ARRAY_SIZE(is2_actionfield_set), + .keyfield_set_map = is2_keyfield_set_map, + .keyfield_set_map_size = is2_keyfield_set_map_size, + .actionfield_set_map = is2_actionfield_set_map, + .actionfield_set_map_size = is2_actionfield_set_map_size, + .keyfield_set_typegroups = is2_keyfield_set_typegroups, + .actionfield_set_typegroups = is2_actionfield_set_typegroups, + }, +}; + +const struct vcap_statistics sparx5_vcap_stats = { + .name = "sparx5", + .count = 1, + .keyfield_set_names = vcap_keyfield_set_names, + .actionfield_set_names = vcap_actionfield_set_names, + .keyfield_names = vcap_keyfield_names, + .actionfield_names = vcap_actionfield_names, +}; diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_vcap_ag_api.h b/drivers/net/ethernet/microchip/sparx5/sparx5_vcap_ag_api.h new file mode 100644 index 000000000000..7d106f1276fe --- /dev/null +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_vcap_ag_api.h @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ +/* Copyright (C) 2022 Microchip Technology Inc. and its subsidiaries. + * Microchip VCAP API + */ + +/* This file is autogenerated by cml-utils 2022-10-13 10:04:41 +0200. + * Commit ID: fd7cafd175899f0672c73afb3a30fc872500ae86 + */ + +#ifndef __SPARX5_VCAP_AG_API_H__ +#define __SPARX5_VCAP_AG_API_H__ + +/* VCAPs */ +extern const struct vcap_info sparx5_vcaps[]; +extern const struct vcap_statistics sparx5_vcap_stats; + +#endif /* __SPARX5_VCAP_AG_API_H__ */ + diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_vcap_impl.c b/drivers/net/ethernet/microchip/sparx5/sparx5_vcap_impl.c index 8df7cba77a28..68f6fed80556 100644 --- a/drivers/net/ethernet/microchip/sparx5/sparx5_vcap_impl.c +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_vcap_impl.c @@ -13,6 +13,7 @@ #include "vcap_api.h" #include "sparx5_main_regs.h" #include "sparx5_main.h" +#include "sparx5_vcap_ag_api.h" /* Allocate a vcap control and vcap instances and configure the system */ int sparx5_vcap_init(struct sparx5 *sparx5) @@ -28,6 +29,9 @@ int sparx5_vcap_init(struct sparx5 *sparx5) return -ENOMEM; sparx5->vcap_ctrl = ctrl; + /* select the sparx5 VCAP model */ + ctrl->vcaps = sparx5_vcaps; + ctrl->stats = &sparx5_vcap_stats; return 0; } From 45c00ad0030ce94111a208c116d922c83645fc30 Mon Sep 17 00:00:00 2001 From: Steen Hegelund Date: Thu, 20 Oct 2022 15:08:58 +0200 Subject: [PATCH 0277/2002] net: microchip: sparx5: Adding IS2 VCAP register interface This adds the register interface needed to access the Sparx5 Ingress Stage 2 VCAP (IS2). The Sparx5 Chip Register Model can be browsed at this location: https://github.com/microchip-ung/sparx-5_reginfo Signed-off-by: Steen Hegelund Signed-off-by: David S. Miller --- .../microchip/sparx5/sparx5_main_regs.h | 460 +++++++++++++++++- 1 file changed, 458 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_main_regs.h b/drivers/net/ethernet/microchip/sparx5/sparx5_main_regs.h index fa2eb70f487a..c42195f4ec4d 100644 --- a/drivers/net/ethernet/microchip/sparx5/sparx5_main_regs.h +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_main_regs.h @@ -4,8 +4,8 @@ * Copyright (c) 2021 Microchip Technology Inc. */ -/* This file is autogenerated by cml-utils 2022-02-26 14:15:01 +0100. - * Commit ID: 98bdd3d171cc2a1afd30d241d41a4281d471a48c (dirty) +/* This file is autogenerated by cml-utils 2022-09-12 14:22:42 +0200. + * Commit ID: 06aecbca4eab6e85d87f665fe6b6348c48146245 */ #ifndef _SPARX5_MAIN_REGS_H_ @@ -171,6 +171,162 @@ enum sparx5_target { /* ANA_AC:STAT_CNT_CFG_PORT:STAT_LSB_CNT */ #define ANA_AC_PORT_STAT_LSB_CNT(g, r) __REG(TARGET_ANA_AC, 0, 1, 843776, g, 70, 64, 20, r, 4, 4) +/* ANA_ACL:COMMON:VCAP_S2_CFG */ +#define ANA_ACL_VCAP_S2_CFG(r) __REG(TARGET_ANA_ACL, 0, 1, 32768, 0, 1, 592, 0, r, 70, 4) + +#define ANA_ACL_VCAP_S2_CFG_SEC_ROUTE_HANDLING_ENA BIT(28) +#define ANA_ACL_VCAP_S2_CFG_SEC_ROUTE_HANDLING_ENA_SET(x)\ + FIELD_PREP(ANA_ACL_VCAP_S2_CFG_SEC_ROUTE_HANDLING_ENA, x) +#define ANA_ACL_VCAP_S2_CFG_SEC_ROUTE_HANDLING_ENA_GET(x)\ + FIELD_GET(ANA_ACL_VCAP_S2_CFG_SEC_ROUTE_HANDLING_ENA, x) + +#define ANA_ACL_VCAP_S2_CFG_SEC_TYPE_OAM_ENA GENMASK(27, 26) +#define ANA_ACL_VCAP_S2_CFG_SEC_TYPE_OAM_ENA_SET(x)\ + FIELD_PREP(ANA_ACL_VCAP_S2_CFG_SEC_TYPE_OAM_ENA, x) +#define ANA_ACL_VCAP_S2_CFG_SEC_TYPE_OAM_ENA_GET(x)\ + FIELD_GET(ANA_ACL_VCAP_S2_CFG_SEC_TYPE_OAM_ENA, x) + +#define ANA_ACL_VCAP_S2_CFG_SEC_TYPE_IP6_TCPUDP_OTHER_ENA GENMASK(25, 24) +#define ANA_ACL_VCAP_S2_CFG_SEC_TYPE_IP6_TCPUDP_OTHER_ENA_SET(x)\ + FIELD_PREP(ANA_ACL_VCAP_S2_CFG_SEC_TYPE_IP6_TCPUDP_OTHER_ENA, x) +#define ANA_ACL_VCAP_S2_CFG_SEC_TYPE_IP6_TCPUDP_OTHER_ENA_GET(x)\ + FIELD_GET(ANA_ACL_VCAP_S2_CFG_SEC_TYPE_IP6_TCPUDP_OTHER_ENA, x) + +#define ANA_ACL_VCAP_S2_CFG_SEC_TYPE_IP6_VID_ENA GENMASK(23, 22) +#define ANA_ACL_VCAP_S2_CFG_SEC_TYPE_IP6_VID_ENA_SET(x)\ + FIELD_PREP(ANA_ACL_VCAP_S2_CFG_SEC_TYPE_IP6_VID_ENA, x) +#define ANA_ACL_VCAP_S2_CFG_SEC_TYPE_IP6_VID_ENA_GET(x)\ + FIELD_GET(ANA_ACL_VCAP_S2_CFG_SEC_TYPE_IP6_VID_ENA, x) + +#define ANA_ACL_VCAP_S2_CFG_SEC_TYPE_IP6_STD_ENA GENMASK(21, 20) +#define ANA_ACL_VCAP_S2_CFG_SEC_TYPE_IP6_STD_ENA_SET(x)\ + FIELD_PREP(ANA_ACL_VCAP_S2_CFG_SEC_TYPE_IP6_STD_ENA, x) +#define ANA_ACL_VCAP_S2_CFG_SEC_TYPE_IP6_STD_ENA_GET(x)\ + FIELD_GET(ANA_ACL_VCAP_S2_CFG_SEC_TYPE_IP6_STD_ENA, x) + +#define ANA_ACL_VCAP_S2_CFG_SEC_TYPE_IP6_TCPUDP_ENA GENMASK(19, 18) +#define ANA_ACL_VCAP_S2_CFG_SEC_TYPE_IP6_TCPUDP_ENA_SET(x)\ + FIELD_PREP(ANA_ACL_VCAP_S2_CFG_SEC_TYPE_IP6_TCPUDP_ENA, x) +#define ANA_ACL_VCAP_S2_CFG_SEC_TYPE_IP6_TCPUDP_ENA_GET(x)\ + FIELD_GET(ANA_ACL_VCAP_S2_CFG_SEC_TYPE_IP6_TCPUDP_ENA, x) + +#define ANA_ACL_VCAP_S2_CFG_SEC_TYPE_IP_7TUPLE_ENA GENMASK(17, 16) +#define ANA_ACL_VCAP_S2_CFG_SEC_TYPE_IP_7TUPLE_ENA_SET(x)\ + FIELD_PREP(ANA_ACL_VCAP_S2_CFG_SEC_TYPE_IP_7TUPLE_ENA, x) +#define ANA_ACL_VCAP_S2_CFG_SEC_TYPE_IP_7TUPLE_ENA_GET(x)\ + FIELD_GET(ANA_ACL_VCAP_S2_CFG_SEC_TYPE_IP_7TUPLE_ENA, x) + +#define ANA_ACL_VCAP_S2_CFG_SEC_TYPE_IP4_VID_ENA GENMASK(15, 14) +#define ANA_ACL_VCAP_S2_CFG_SEC_TYPE_IP4_VID_ENA_SET(x)\ + FIELD_PREP(ANA_ACL_VCAP_S2_CFG_SEC_TYPE_IP4_VID_ENA, x) +#define ANA_ACL_VCAP_S2_CFG_SEC_TYPE_IP4_VID_ENA_GET(x)\ + FIELD_GET(ANA_ACL_VCAP_S2_CFG_SEC_TYPE_IP4_VID_ENA, x) + +#define ANA_ACL_VCAP_S2_CFG_SEC_TYPE_IP4_TCPUDP_ENA GENMASK(13, 12) +#define ANA_ACL_VCAP_S2_CFG_SEC_TYPE_IP4_TCPUDP_ENA_SET(x)\ + FIELD_PREP(ANA_ACL_VCAP_S2_CFG_SEC_TYPE_IP4_TCPUDP_ENA, x) +#define ANA_ACL_VCAP_S2_CFG_SEC_TYPE_IP4_TCPUDP_ENA_GET(x)\ + FIELD_GET(ANA_ACL_VCAP_S2_CFG_SEC_TYPE_IP4_TCPUDP_ENA, x) + +#define ANA_ACL_VCAP_S2_CFG_SEC_TYPE_IP4_OTHER_ENA GENMASK(11, 10) +#define ANA_ACL_VCAP_S2_CFG_SEC_TYPE_IP4_OTHER_ENA_SET(x)\ + FIELD_PREP(ANA_ACL_VCAP_S2_CFG_SEC_TYPE_IP4_OTHER_ENA, x) +#define ANA_ACL_VCAP_S2_CFG_SEC_TYPE_IP4_OTHER_ENA_GET(x)\ + FIELD_GET(ANA_ACL_VCAP_S2_CFG_SEC_TYPE_IP4_OTHER_ENA, x) + +#define ANA_ACL_VCAP_S2_CFG_SEC_TYPE_ARP_ENA GENMASK(9, 8) +#define ANA_ACL_VCAP_S2_CFG_SEC_TYPE_ARP_ENA_SET(x)\ + FIELD_PREP(ANA_ACL_VCAP_S2_CFG_SEC_TYPE_ARP_ENA, x) +#define ANA_ACL_VCAP_S2_CFG_SEC_TYPE_ARP_ENA_GET(x)\ + FIELD_GET(ANA_ACL_VCAP_S2_CFG_SEC_TYPE_ARP_ENA, x) + +#define ANA_ACL_VCAP_S2_CFG_SEC_TYPE_MAC_SNAP_ENA GENMASK(7, 6) +#define ANA_ACL_VCAP_S2_CFG_SEC_TYPE_MAC_SNAP_ENA_SET(x)\ + FIELD_PREP(ANA_ACL_VCAP_S2_CFG_SEC_TYPE_MAC_SNAP_ENA, x) +#define ANA_ACL_VCAP_S2_CFG_SEC_TYPE_MAC_SNAP_ENA_GET(x)\ + FIELD_GET(ANA_ACL_VCAP_S2_CFG_SEC_TYPE_MAC_SNAP_ENA, x) + +#define ANA_ACL_VCAP_S2_CFG_SEC_TYPE_MAC_LLC_ENA GENMASK(5, 4) +#define ANA_ACL_VCAP_S2_CFG_SEC_TYPE_MAC_LLC_ENA_SET(x)\ + FIELD_PREP(ANA_ACL_VCAP_S2_CFG_SEC_TYPE_MAC_LLC_ENA, x) +#define ANA_ACL_VCAP_S2_CFG_SEC_TYPE_MAC_LLC_ENA_GET(x)\ + FIELD_GET(ANA_ACL_VCAP_S2_CFG_SEC_TYPE_MAC_LLC_ENA, x) + +#define ANA_ACL_VCAP_S2_CFG_SEC_ENA GENMASK(3, 0) +#define ANA_ACL_VCAP_S2_CFG_SEC_ENA_SET(x)\ + FIELD_PREP(ANA_ACL_VCAP_S2_CFG_SEC_ENA, x) +#define ANA_ACL_VCAP_S2_CFG_SEC_ENA_GET(x)\ + FIELD_GET(ANA_ACL_VCAP_S2_CFG_SEC_ENA, x) + +/* ANA_ACL:COMMON:SWAP_IP_CTRL */ +#define ANA_ACL_SWAP_IP_CTRL __REG(TARGET_ANA_ACL, 0, 1, 32768, 0, 1, 592, 412, 0, 1, 4) + +#define ANA_ACL_SWAP_IP_CTRL_DMAC_REPL_OFFSET_VAL GENMASK(23, 18) +#define ANA_ACL_SWAP_IP_CTRL_DMAC_REPL_OFFSET_VAL_SET(x)\ + FIELD_PREP(ANA_ACL_SWAP_IP_CTRL_DMAC_REPL_OFFSET_VAL, x) +#define ANA_ACL_SWAP_IP_CTRL_DMAC_REPL_OFFSET_VAL_GET(x)\ + FIELD_GET(ANA_ACL_SWAP_IP_CTRL_DMAC_REPL_OFFSET_VAL, x) + +#define ANA_ACL_SWAP_IP_CTRL_IP_SWAP_IP6_HOPC_VAL GENMASK(17, 10) +#define ANA_ACL_SWAP_IP_CTRL_IP_SWAP_IP6_HOPC_VAL_SET(x)\ + FIELD_PREP(ANA_ACL_SWAP_IP_CTRL_IP_SWAP_IP6_HOPC_VAL, x) +#define ANA_ACL_SWAP_IP_CTRL_IP_SWAP_IP6_HOPC_VAL_GET(x)\ + FIELD_GET(ANA_ACL_SWAP_IP_CTRL_IP_SWAP_IP6_HOPC_VAL, x) + +#define ANA_ACL_SWAP_IP_CTRL_IP_SWAP_IP4_TTL_VAL GENMASK(9, 2) +#define ANA_ACL_SWAP_IP_CTRL_IP_SWAP_IP4_TTL_VAL_SET(x)\ + FIELD_PREP(ANA_ACL_SWAP_IP_CTRL_IP_SWAP_IP4_TTL_VAL, x) +#define ANA_ACL_SWAP_IP_CTRL_IP_SWAP_IP4_TTL_VAL_GET(x)\ + FIELD_GET(ANA_ACL_SWAP_IP_CTRL_IP_SWAP_IP4_TTL_VAL, x) + +#define ANA_ACL_SWAP_IP_CTRL_IP_SWAP_IP6_HOPC_ENA BIT(1) +#define ANA_ACL_SWAP_IP_CTRL_IP_SWAP_IP6_HOPC_ENA_SET(x)\ + FIELD_PREP(ANA_ACL_SWAP_IP_CTRL_IP_SWAP_IP6_HOPC_ENA, x) +#define ANA_ACL_SWAP_IP_CTRL_IP_SWAP_IP6_HOPC_ENA_GET(x)\ + FIELD_GET(ANA_ACL_SWAP_IP_CTRL_IP_SWAP_IP6_HOPC_ENA, x) + +#define ANA_ACL_SWAP_IP_CTRL_IP_SWAP_IP4_TTL_ENA BIT(0) +#define ANA_ACL_SWAP_IP_CTRL_IP_SWAP_IP4_TTL_ENA_SET(x)\ + FIELD_PREP(ANA_ACL_SWAP_IP_CTRL_IP_SWAP_IP4_TTL_ENA, x) +#define ANA_ACL_SWAP_IP_CTRL_IP_SWAP_IP4_TTL_ENA_GET(x)\ + FIELD_GET(ANA_ACL_SWAP_IP_CTRL_IP_SWAP_IP4_TTL_ENA, x) + +/* ANA_ACL:COMMON:VCAP_S2_RLEG_STAT */ +#define ANA_ACL_VCAP_S2_RLEG_STAT(r) __REG(TARGET_ANA_ACL, 0, 1, 32768, 0, 1, 592, 424, r, 4, 4) + +#define ANA_ACL_VCAP_S2_RLEG_STAT_IRLEG_STAT_MASK GENMASK(12, 6) +#define ANA_ACL_VCAP_S2_RLEG_STAT_IRLEG_STAT_MASK_SET(x)\ + FIELD_PREP(ANA_ACL_VCAP_S2_RLEG_STAT_IRLEG_STAT_MASK, x) +#define ANA_ACL_VCAP_S2_RLEG_STAT_IRLEG_STAT_MASK_GET(x)\ + FIELD_GET(ANA_ACL_VCAP_S2_RLEG_STAT_IRLEG_STAT_MASK, x) + +#define ANA_ACL_VCAP_S2_RLEG_STAT_ERLEG_STAT_MASK GENMASK(5, 0) +#define ANA_ACL_VCAP_S2_RLEG_STAT_ERLEG_STAT_MASK_SET(x)\ + FIELD_PREP(ANA_ACL_VCAP_S2_RLEG_STAT_ERLEG_STAT_MASK, x) +#define ANA_ACL_VCAP_S2_RLEG_STAT_ERLEG_STAT_MASK_GET(x)\ + FIELD_GET(ANA_ACL_VCAP_S2_RLEG_STAT_ERLEG_STAT_MASK, x) + +/* ANA_ACL:COMMON:VCAP_S2_FRAGMENT_CFG */ +#define ANA_ACL_VCAP_S2_FRAGMENT_CFG __REG(TARGET_ANA_ACL, 0, 1, 32768, 0, 1, 592, 440, 0, 1, 4) + +#define ANA_ACL_VCAP_S2_FRAGMENT_CFG_L4_MIN_LEN GENMASK(9, 5) +#define ANA_ACL_VCAP_S2_FRAGMENT_CFG_L4_MIN_LEN_SET(x)\ + FIELD_PREP(ANA_ACL_VCAP_S2_FRAGMENT_CFG_L4_MIN_LEN, x) +#define ANA_ACL_VCAP_S2_FRAGMENT_CFG_L4_MIN_LEN_GET(x)\ + FIELD_GET(ANA_ACL_VCAP_S2_FRAGMENT_CFG_L4_MIN_LEN, x) + +#define ANA_ACL_VCAP_S2_FRAGMENT_CFG_FRAGMENT_OFFSET_THRES_DIS BIT(4) +#define ANA_ACL_VCAP_S2_FRAGMENT_CFG_FRAGMENT_OFFSET_THRES_DIS_SET(x)\ + FIELD_PREP(ANA_ACL_VCAP_S2_FRAGMENT_CFG_FRAGMENT_OFFSET_THRES_DIS, x) +#define ANA_ACL_VCAP_S2_FRAGMENT_CFG_FRAGMENT_OFFSET_THRES_DIS_GET(x)\ + FIELD_GET(ANA_ACL_VCAP_S2_FRAGMENT_CFG_FRAGMENT_OFFSET_THRES_DIS, x) + +#define ANA_ACL_VCAP_S2_FRAGMENT_CFG_FRAGMENT_OFFSET_THRES GENMASK(3, 0) +#define ANA_ACL_VCAP_S2_FRAGMENT_CFG_FRAGMENT_OFFSET_THRES_SET(x)\ + FIELD_PREP(ANA_ACL_VCAP_S2_FRAGMENT_CFG_FRAGMENT_OFFSET_THRES, x) +#define ANA_ACL_VCAP_S2_FRAGMENT_CFG_FRAGMENT_OFFSET_THRES_GET(x)\ + FIELD_GET(ANA_ACL_VCAP_S2_FRAGMENT_CFG_FRAGMENT_OFFSET_THRES, x) + /* ANA_ACL:COMMON:OWN_UPSID */ #define ANA_ACL_OWN_UPSID(r) __REG(TARGET_ANA_ACL, 0, 1, 32768, 0, 1, 592, 580, r, 3, 4) @@ -180,6 +336,174 @@ enum sparx5_target { #define ANA_ACL_OWN_UPSID_OWN_UPSID_GET(x)\ FIELD_GET(ANA_ACL_OWN_UPSID_OWN_UPSID, x) +/* ANA_ACL:KEY_SEL:VCAP_S2_KEY_SEL */ +#define ANA_ACL_VCAP_S2_KEY_SEL(g, r) __REG(TARGET_ANA_ACL, 0, 1, 34200, g, 134, 16, 0, r, 4, 4) + +#define ANA_ACL_VCAP_S2_KEY_SEL_KEY_SEL_ENA BIT(13) +#define ANA_ACL_VCAP_S2_KEY_SEL_KEY_SEL_ENA_SET(x)\ + FIELD_PREP(ANA_ACL_VCAP_S2_KEY_SEL_KEY_SEL_ENA, x) +#define ANA_ACL_VCAP_S2_KEY_SEL_KEY_SEL_ENA_GET(x)\ + FIELD_GET(ANA_ACL_VCAP_S2_KEY_SEL_KEY_SEL_ENA, x) + +#define ANA_ACL_VCAP_S2_KEY_SEL_IGR_PORT_MASK_SEL BIT(12) +#define ANA_ACL_VCAP_S2_KEY_SEL_IGR_PORT_MASK_SEL_SET(x)\ + FIELD_PREP(ANA_ACL_VCAP_S2_KEY_SEL_IGR_PORT_MASK_SEL, x) +#define ANA_ACL_VCAP_S2_KEY_SEL_IGR_PORT_MASK_SEL_GET(x)\ + FIELD_GET(ANA_ACL_VCAP_S2_KEY_SEL_IGR_PORT_MASK_SEL, x) + +#define ANA_ACL_VCAP_S2_KEY_SEL_NON_ETH_KEY_SEL GENMASK(11, 10) +#define ANA_ACL_VCAP_S2_KEY_SEL_NON_ETH_KEY_SEL_SET(x)\ + FIELD_PREP(ANA_ACL_VCAP_S2_KEY_SEL_NON_ETH_KEY_SEL, x) +#define ANA_ACL_VCAP_S2_KEY_SEL_NON_ETH_KEY_SEL_GET(x)\ + FIELD_GET(ANA_ACL_VCAP_S2_KEY_SEL_NON_ETH_KEY_SEL, x) + +#define ANA_ACL_VCAP_S2_KEY_SEL_IP4_MC_KEY_SEL GENMASK(9, 8) +#define ANA_ACL_VCAP_S2_KEY_SEL_IP4_MC_KEY_SEL_SET(x)\ + FIELD_PREP(ANA_ACL_VCAP_S2_KEY_SEL_IP4_MC_KEY_SEL, x) +#define ANA_ACL_VCAP_S2_KEY_SEL_IP4_MC_KEY_SEL_GET(x)\ + FIELD_GET(ANA_ACL_VCAP_S2_KEY_SEL_IP4_MC_KEY_SEL, x) + +#define ANA_ACL_VCAP_S2_KEY_SEL_IP4_UC_KEY_SEL GENMASK(7, 6) +#define ANA_ACL_VCAP_S2_KEY_SEL_IP4_UC_KEY_SEL_SET(x)\ + FIELD_PREP(ANA_ACL_VCAP_S2_KEY_SEL_IP4_UC_KEY_SEL, x) +#define ANA_ACL_VCAP_S2_KEY_SEL_IP4_UC_KEY_SEL_GET(x)\ + FIELD_GET(ANA_ACL_VCAP_S2_KEY_SEL_IP4_UC_KEY_SEL, x) + +#define ANA_ACL_VCAP_S2_KEY_SEL_IP6_MC_KEY_SEL GENMASK(5, 3) +#define ANA_ACL_VCAP_S2_KEY_SEL_IP6_MC_KEY_SEL_SET(x)\ + FIELD_PREP(ANA_ACL_VCAP_S2_KEY_SEL_IP6_MC_KEY_SEL, x) +#define ANA_ACL_VCAP_S2_KEY_SEL_IP6_MC_KEY_SEL_GET(x)\ + FIELD_GET(ANA_ACL_VCAP_S2_KEY_SEL_IP6_MC_KEY_SEL, x) + +#define ANA_ACL_VCAP_S2_KEY_SEL_IP6_UC_KEY_SEL GENMASK(2, 1) +#define ANA_ACL_VCAP_S2_KEY_SEL_IP6_UC_KEY_SEL_SET(x)\ + FIELD_PREP(ANA_ACL_VCAP_S2_KEY_SEL_IP6_UC_KEY_SEL, x) +#define ANA_ACL_VCAP_S2_KEY_SEL_IP6_UC_KEY_SEL_GET(x)\ + FIELD_GET(ANA_ACL_VCAP_S2_KEY_SEL_IP6_UC_KEY_SEL, x) + +#define ANA_ACL_VCAP_S2_KEY_SEL_ARP_KEY_SEL BIT(0) +#define ANA_ACL_VCAP_S2_KEY_SEL_ARP_KEY_SEL_SET(x)\ + FIELD_PREP(ANA_ACL_VCAP_S2_KEY_SEL_ARP_KEY_SEL, x) +#define ANA_ACL_VCAP_S2_KEY_SEL_ARP_KEY_SEL_GET(x)\ + FIELD_GET(ANA_ACL_VCAP_S2_KEY_SEL_ARP_KEY_SEL, x) + +/* ANA_ACL:CNT_A:CNT_A */ +#define ANA_ACL_CNT_A(g) __REG(TARGET_ANA_ACL, 0, 1, 0, g, 4096, 4, 0, 0, 1, 4) + +/* ANA_ACL:CNT_B:CNT_B */ +#define ANA_ACL_CNT_B(g) __REG(TARGET_ANA_ACL, 0, 1, 16384, g, 4096, 4, 0, 0, 1, 4) + +/* ANA_ACL:STICKY:SEC_LOOKUP_STICKY */ +#define ANA_ACL_SEC_LOOKUP_STICKY(r) __REG(TARGET_ANA_ACL, 0, 1, 36408, 0, 1, 16, 0, r, 4, 4) + +#define ANA_ACL_SEC_LOOKUP_STICKY_KEY_SEL_CLM_STICKY BIT(17) +#define ANA_ACL_SEC_LOOKUP_STICKY_KEY_SEL_CLM_STICKY_SET(x)\ + FIELD_PREP(ANA_ACL_SEC_LOOKUP_STICKY_KEY_SEL_CLM_STICKY, x) +#define ANA_ACL_SEC_LOOKUP_STICKY_KEY_SEL_CLM_STICKY_GET(x)\ + FIELD_GET(ANA_ACL_SEC_LOOKUP_STICKY_KEY_SEL_CLM_STICKY, x) + +#define ANA_ACL_SEC_LOOKUP_STICKY_KEY_SEL_IRLEG_STICKY BIT(16) +#define ANA_ACL_SEC_LOOKUP_STICKY_KEY_SEL_IRLEG_STICKY_SET(x)\ + FIELD_PREP(ANA_ACL_SEC_LOOKUP_STICKY_KEY_SEL_IRLEG_STICKY, x) +#define ANA_ACL_SEC_LOOKUP_STICKY_KEY_SEL_IRLEG_STICKY_GET(x)\ + FIELD_GET(ANA_ACL_SEC_LOOKUP_STICKY_KEY_SEL_IRLEG_STICKY, x) + +#define ANA_ACL_SEC_LOOKUP_STICKY_KEY_SEL_ERLEG_STICKY BIT(15) +#define ANA_ACL_SEC_LOOKUP_STICKY_KEY_SEL_ERLEG_STICKY_SET(x)\ + FIELD_PREP(ANA_ACL_SEC_LOOKUP_STICKY_KEY_SEL_ERLEG_STICKY, x) +#define ANA_ACL_SEC_LOOKUP_STICKY_KEY_SEL_ERLEG_STICKY_GET(x)\ + FIELD_GET(ANA_ACL_SEC_LOOKUP_STICKY_KEY_SEL_ERLEG_STICKY, x) + +#define ANA_ACL_SEC_LOOKUP_STICKY_KEY_SEL_PORT_STICKY BIT(14) +#define ANA_ACL_SEC_LOOKUP_STICKY_KEY_SEL_PORT_STICKY_SET(x)\ + FIELD_PREP(ANA_ACL_SEC_LOOKUP_STICKY_KEY_SEL_PORT_STICKY, x) +#define ANA_ACL_SEC_LOOKUP_STICKY_KEY_SEL_PORT_STICKY_GET(x)\ + FIELD_GET(ANA_ACL_SEC_LOOKUP_STICKY_KEY_SEL_PORT_STICKY, x) + +#define ANA_ACL_SEC_LOOKUP_STICKY_SEC_TYPE_CUSTOM2_STICKY BIT(13) +#define ANA_ACL_SEC_LOOKUP_STICKY_SEC_TYPE_CUSTOM2_STICKY_SET(x)\ + FIELD_PREP(ANA_ACL_SEC_LOOKUP_STICKY_SEC_TYPE_CUSTOM2_STICKY, x) +#define ANA_ACL_SEC_LOOKUP_STICKY_SEC_TYPE_CUSTOM2_STICKY_GET(x)\ + FIELD_GET(ANA_ACL_SEC_LOOKUP_STICKY_SEC_TYPE_CUSTOM2_STICKY, x) + +#define ANA_ACL_SEC_LOOKUP_STICKY_SEC_TYPE_CUSTOM1_STICKY BIT(12) +#define ANA_ACL_SEC_LOOKUP_STICKY_SEC_TYPE_CUSTOM1_STICKY_SET(x)\ + FIELD_PREP(ANA_ACL_SEC_LOOKUP_STICKY_SEC_TYPE_CUSTOM1_STICKY, x) +#define ANA_ACL_SEC_LOOKUP_STICKY_SEC_TYPE_CUSTOM1_STICKY_GET(x)\ + FIELD_GET(ANA_ACL_SEC_LOOKUP_STICKY_SEC_TYPE_CUSTOM1_STICKY, x) + +#define ANA_ACL_SEC_LOOKUP_STICKY_SEC_TYPE_OAM_STICKY BIT(11) +#define ANA_ACL_SEC_LOOKUP_STICKY_SEC_TYPE_OAM_STICKY_SET(x)\ + FIELD_PREP(ANA_ACL_SEC_LOOKUP_STICKY_SEC_TYPE_OAM_STICKY, x) +#define ANA_ACL_SEC_LOOKUP_STICKY_SEC_TYPE_OAM_STICKY_GET(x)\ + FIELD_GET(ANA_ACL_SEC_LOOKUP_STICKY_SEC_TYPE_OAM_STICKY, x) + +#define ANA_ACL_SEC_LOOKUP_STICKY_SEC_TYPE_IP6_VID_STICKY BIT(10) +#define ANA_ACL_SEC_LOOKUP_STICKY_SEC_TYPE_IP6_VID_STICKY_SET(x)\ + FIELD_PREP(ANA_ACL_SEC_LOOKUP_STICKY_SEC_TYPE_IP6_VID_STICKY, x) +#define ANA_ACL_SEC_LOOKUP_STICKY_SEC_TYPE_IP6_VID_STICKY_GET(x)\ + FIELD_GET(ANA_ACL_SEC_LOOKUP_STICKY_SEC_TYPE_IP6_VID_STICKY, x) + +#define ANA_ACL_SEC_LOOKUP_STICKY_SEC_TYPE_IP6_STD_STICKY BIT(9) +#define ANA_ACL_SEC_LOOKUP_STICKY_SEC_TYPE_IP6_STD_STICKY_SET(x)\ + FIELD_PREP(ANA_ACL_SEC_LOOKUP_STICKY_SEC_TYPE_IP6_STD_STICKY, x) +#define ANA_ACL_SEC_LOOKUP_STICKY_SEC_TYPE_IP6_STD_STICKY_GET(x)\ + FIELD_GET(ANA_ACL_SEC_LOOKUP_STICKY_SEC_TYPE_IP6_STD_STICKY, x) + +#define ANA_ACL_SEC_LOOKUP_STICKY_SEC_TYPE_IP6_TCPUDP_STICKY BIT(8) +#define ANA_ACL_SEC_LOOKUP_STICKY_SEC_TYPE_IP6_TCPUDP_STICKY_SET(x)\ + FIELD_PREP(ANA_ACL_SEC_LOOKUP_STICKY_SEC_TYPE_IP6_TCPUDP_STICKY, x) +#define ANA_ACL_SEC_LOOKUP_STICKY_SEC_TYPE_IP6_TCPUDP_STICKY_GET(x)\ + FIELD_GET(ANA_ACL_SEC_LOOKUP_STICKY_SEC_TYPE_IP6_TCPUDP_STICKY, x) + +#define ANA_ACL_SEC_LOOKUP_STICKY_SEC_TYPE_IP_7TUPLE_STICKY BIT(7) +#define ANA_ACL_SEC_LOOKUP_STICKY_SEC_TYPE_IP_7TUPLE_STICKY_SET(x)\ + FIELD_PREP(ANA_ACL_SEC_LOOKUP_STICKY_SEC_TYPE_IP_7TUPLE_STICKY, x) +#define ANA_ACL_SEC_LOOKUP_STICKY_SEC_TYPE_IP_7TUPLE_STICKY_GET(x)\ + FIELD_GET(ANA_ACL_SEC_LOOKUP_STICKY_SEC_TYPE_IP_7TUPLE_STICKY, x) + +#define ANA_ACL_SEC_LOOKUP_STICKY_SEC_TYPE_IP4_VID_STICKY BIT(6) +#define ANA_ACL_SEC_LOOKUP_STICKY_SEC_TYPE_IP4_VID_STICKY_SET(x)\ + FIELD_PREP(ANA_ACL_SEC_LOOKUP_STICKY_SEC_TYPE_IP4_VID_STICKY, x) +#define ANA_ACL_SEC_LOOKUP_STICKY_SEC_TYPE_IP4_VID_STICKY_GET(x)\ + FIELD_GET(ANA_ACL_SEC_LOOKUP_STICKY_SEC_TYPE_IP4_VID_STICKY, x) + +#define ANA_ACL_SEC_LOOKUP_STICKY_SEC_TYPE_IP4_TCPUDP_STICKY BIT(5) +#define ANA_ACL_SEC_LOOKUP_STICKY_SEC_TYPE_IP4_TCPUDP_STICKY_SET(x)\ + FIELD_PREP(ANA_ACL_SEC_LOOKUP_STICKY_SEC_TYPE_IP4_TCPUDP_STICKY, x) +#define ANA_ACL_SEC_LOOKUP_STICKY_SEC_TYPE_IP4_TCPUDP_STICKY_GET(x)\ + FIELD_GET(ANA_ACL_SEC_LOOKUP_STICKY_SEC_TYPE_IP4_TCPUDP_STICKY, x) + +#define ANA_ACL_SEC_LOOKUP_STICKY_SEC_TYPE_IP4_OTHER_STICKY BIT(4) +#define ANA_ACL_SEC_LOOKUP_STICKY_SEC_TYPE_IP4_OTHER_STICKY_SET(x)\ + FIELD_PREP(ANA_ACL_SEC_LOOKUP_STICKY_SEC_TYPE_IP4_OTHER_STICKY, x) +#define ANA_ACL_SEC_LOOKUP_STICKY_SEC_TYPE_IP4_OTHER_STICKY_GET(x)\ + FIELD_GET(ANA_ACL_SEC_LOOKUP_STICKY_SEC_TYPE_IP4_OTHER_STICKY, x) + +#define ANA_ACL_SEC_LOOKUP_STICKY_SEC_TYPE_ARP_STICKY BIT(3) +#define ANA_ACL_SEC_LOOKUP_STICKY_SEC_TYPE_ARP_STICKY_SET(x)\ + FIELD_PREP(ANA_ACL_SEC_LOOKUP_STICKY_SEC_TYPE_ARP_STICKY, x) +#define ANA_ACL_SEC_LOOKUP_STICKY_SEC_TYPE_ARP_STICKY_GET(x)\ + FIELD_GET(ANA_ACL_SEC_LOOKUP_STICKY_SEC_TYPE_ARP_STICKY, x) + +#define ANA_ACL_SEC_LOOKUP_STICKY_SEC_TYPE_MAC_SNAP_STICKY BIT(2) +#define ANA_ACL_SEC_LOOKUP_STICKY_SEC_TYPE_MAC_SNAP_STICKY_SET(x)\ + FIELD_PREP(ANA_ACL_SEC_LOOKUP_STICKY_SEC_TYPE_MAC_SNAP_STICKY, x) +#define ANA_ACL_SEC_LOOKUP_STICKY_SEC_TYPE_MAC_SNAP_STICKY_GET(x)\ + FIELD_GET(ANA_ACL_SEC_LOOKUP_STICKY_SEC_TYPE_MAC_SNAP_STICKY, x) + +#define ANA_ACL_SEC_LOOKUP_STICKY_SEC_TYPE_MAC_LLC_STICKY BIT(1) +#define ANA_ACL_SEC_LOOKUP_STICKY_SEC_TYPE_MAC_LLC_STICKY_SET(x)\ + FIELD_PREP(ANA_ACL_SEC_LOOKUP_STICKY_SEC_TYPE_MAC_LLC_STICKY, x) +#define ANA_ACL_SEC_LOOKUP_STICKY_SEC_TYPE_MAC_LLC_STICKY_GET(x)\ + FIELD_GET(ANA_ACL_SEC_LOOKUP_STICKY_SEC_TYPE_MAC_LLC_STICKY, x) + +#define ANA_ACL_SEC_LOOKUP_STICKY_SEC_TYPE_MAC_ETYPE_STICKY BIT(0) +#define ANA_ACL_SEC_LOOKUP_STICKY_SEC_TYPE_MAC_ETYPE_STICKY_SET(x)\ + FIELD_PREP(ANA_ACL_SEC_LOOKUP_STICKY_SEC_TYPE_MAC_ETYPE_STICKY, x) +#define ANA_ACL_SEC_LOOKUP_STICKY_SEC_TYPE_MAC_ETYPE_STICKY_GET(x)\ + FIELD_GET(ANA_ACL_SEC_LOOKUP_STICKY_SEC_TYPE_MAC_ETYPE_STICKY, x) + /* ANA_AC_POL:POL_ALL_CFG:POL_UPD_INT_CFG */ #define ANA_AC_POL_POL_UPD_INT_CFG __REG(TARGET_ANA_AC_POL, 0, 1, 75968, 0, 1, 1160, 1148, 0, 1, 4) @@ -5039,6 +5363,138 @@ enum sparx5_target { #define REW_RAM_INIT_RAM_CFG_HOOK_GET(x)\ FIELD_GET(REW_RAM_INIT_RAM_CFG_HOOK, x) +/* VCAP_SUPER:VCAP_CORE_CFG:VCAP_UPDATE_CTRL */ +#define VCAP_SUPER_CTRL __REG(TARGET_VCAP_SUPER, 0, 1, 0, 0, 1, 8, 0, 0, 1, 4) + +#define VCAP_SUPER_CTRL_UPDATE_CMD GENMASK(24, 22) +#define VCAP_SUPER_CTRL_UPDATE_CMD_SET(x)\ + FIELD_PREP(VCAP_SUPER_CTRL_UPDATE_CMD, x) +#define VCAP_SUPER_CTRL_UPDATE_CMD_GET(x)\ + FIELD_GET(VCAP_SUPER_CTRL_UPDATE_CMD, x) + +#define VCAP_SUPER_CTRL_UPDATE_ENTRY_DIS BIT(21) +#define VCAP_SUPER_CTRL_UPDATE_ENTRY_DIS_SET(x)\ + FIELD_PREP(VCAP_SUPER_CTRL_UPDATE_ENTRY_DIS, x) +#define VCAP_SUPER_CTRL_UPDATE_ENTRY_DIS_GET(x)\ + FIELD_GET(VCAP_SUPER_CTRL_UPDATE_ENTRY_DIS, x) + +#define VCAP_SUPER_CTRL_UPDATE_ACTION_DIS BIT(20) +#define VCAP_SUPER_CTRL_UPDATE_ACTION_DIS_SET(x)\ + FIELD_PREP(VCAP_SUPER_CTRL_UPDATE_ACTION_DIS, x) +#define VCAP_SUPER_CTRL_UPDATE_ACTION_DIS_GET(x)\ + FIELD_GET(VCAP_SUPER_CTRL_UPDATE_ACTION_DIS, x) + +#define VCAP_SUPER_CTRL_UPDATE_CNT_DIS BIT(19) +#define VCAP_SUPER_CTRL_UPDATE_CNT_DIS_SET(x)\ + FIELD_PREP(VCAP_SUPER_CTRL_UPDATE_CNT_DIS, x) +#define VCAP_SUPER_CTRL_UPDATE_CNT_DIS_GET(x)\ + FIELD_GET(VCAP_SUPER_CTRL_UPDATE_CNT_DIS, x) + +#define VCAP_SUPER_CTRL_UPDATE_ADDR GENMASK(18, 3) +#define VCAP_SUPER_CTRL_UPDATE_ADDR_SET(x)\ + FIELD_PREP(VCAP_SUPER_CTRL_UPDATE_ADDR, x) +#define VCAP_SUPER_CTRL_UPDATE_ADDR_GET(x)\ + FIELD_GET(VCAP_SUPER_CTRL_UPDATE_ADDR, x) + +#define VCAP_SUPER_CTRL_UPDATE_SHOT BIT(2) +#define VCAP_SUPER_CTRL_UPDATE_SHOT_SET(x)\ + FIELD_PREP(VCAP_SUPER_CTRL_UPDATE_SHOT, x) +#define VCAP_SUPER_CTRL_UPDATE_SHOT_GET(x)\ + FIELD_GET(VCAP_SUPER_CTRL_UPDATE_SHOT, x) + +#define VCAP_SUPER_CTRL_CLEAR_CACHE BIT(1) +#define VCAP_SUPER_CTRL_CLEAR_CACHE_SET(x)\ + FIELD_PREP(VCAP_SUPER_CTRL_CLEAR_CACHE, x) +#define VCAP_SUPER_CTRL_CLEAR_CACHE_GET(x)\ + FIELD_GET(VCAP_SUPER_CTRL_CLEAR_CACHE, x) + +#define VCAP_SUPER_CTRL_MV_TRAFFIC_IGN BIT(0) +#define VCAP_SUPER_CTRL_MV_TRAFFIC_IGN_SET(x)\ + FIELD_PREP(VCAP_SUPER_CTRL_MV_TRAFFIC_IGN, x) +#define VCAP_SUPER_CTRL_MV_TRAFFIC_IGN_GET(x)\ + FIELD_GET(VCAP_SUPER_CTRL_MV_TRAFFIC_IGN, x) + +/* VCAP_SUPER:VCAP_CORE_CFG:VCAP_MV_CFG */ +#define VCAP_SUPER_CFG __REG(TARGET_VCAP_SUPER, 0, 1, 0, 0, 1, 8, 4, 0, 1, 4) + +#define VCAP_SUPER_CFG_MV_NUM_POS GENMASK(31, 16) +#define VCAP_SUPER_CFG_MV_NUM_POS_SET(x)\ + FIELD_PREP(VCAP_SUPER_CFG_MV_NUM_POS, x) +#define VCAP_SUPER_CFG_MV_NUM_POS_GET(x)\ + FIELD_GET(VCAP_SUPER_CFG_MV_NUM_POS, x) + +#define VCAP_SUPER_CFG_MV_SIZE GENMASK(15, 0) +#define VCAP_SUPER_CFG_MV_SIZE_SET(x)\ + FIELD_PREP(VCAP_SUPER_CFG_MV_SIZE, x) +#define VCAP_SUPER_CFG_MV_SIZE_GET(x)\ + FIELD_GET(VCAP_SUPER_CFG_MV_SIZE, x) + +/* VCAP_SUPER:VCAP_CORE_CACHE:VCAP_ENTRY_DAT */ +#define VCAP_SUPER_VCAP_ENTRY_DAT(r) __REG(TARGET_VCAP_SUPER, 0, 1, 8, 0, 1, 904, 0, r, 64, 4) + +/* VCAP_SUPER:VCAP_CORE_CACHE:VCAP_MASK_DAT */ +#define VCAP_SUPER_VCAP_MASK_DAT(r) __REG(TARGET_VCAP_SUPER, 0, 1, 8, 0, 1, 904, 256, r, 64, 4) + +/* VCAP_SUPER:VCAP_CORE_CACHE:VCAP_ACTION_DAT */ +#define VCAP_SUPER_VCAP_ACTION_DAT(r) __REG(TARGET_VCAP_SUPER, 0, 1, 8, 0, 1, 904, 512, r, 64, 4) + +/* VCAP_SUPER:VCAP_CORE_CACHE:VCAP_CNT_DAT */ +#define VCAP_SUPER_VCAP_CNT_DAT(r) __REG(TARGET_VCAP_SUPER, 0, 1, 8, 0, 1, 904, 768, r, 32, 4) + +/* VCAP_SUPER:VCAP_CORE_CACHE:VCAP_CNT_FW_DAT */ +#define VCAP_SUPER_VCAP_CNT_FW_DAT __REG(TARGET_VCAP_SUPER, 0, 1, 8, 0, 1, 904, 896, 0, 1, 4) + +/* VCAP_SUPER:VCAP_CORE_CACHE:VCAP_TG_DAT */ +#define VCAP_SUPER_VCAP_TG_DAT __REG(TARGET_VCAP_SUPER, 0, 1, 8, 0, 1, 904, 900, 0, 1, 4) + +/* VCAP_SUPER:VCAP_CORE_MAP:VCAP_CORE_IDX */ +#define VCAP_SUPER_IDX __REG(TARGET_VCAP_SUPER, 0, 1, 912, 0, 1, 8, 0, 0, 1, 4) + +#define VCAP_SUPER_IDX_CORE_IDX GENMASK(3, 0) +#define VCAP_SUPER_IDX_CORE_IDX_SET(x)\ + FIELD_PREP(VCAP_SUPER_IDX_CORE_IDX, x) +#define VCAP_SUPER_IDX_CORE_IDX_GET(x)\ + FIELD_GET(VCAP_SUPER_IDX_CORE_IDX, x) + +/* VCAP_SUPER:VCAP_CORE_MAP:VCAP_CORE_MAP */ +#define VCAP_SUPER_MAP __REG(TARGET_VCAP_SUPER, 0, 1, 912, 0, 1, 8, 4, 0, 1, 4) + +#define VCAP_SUPER_MAP_CORE_MAP GENMASK(2, 0) +#define VCAP_SUPER_MAP_CORE_MAP_SET(x)\ + FIELD_PREP(VCAP_SUPER_MAP_CORE_MAP, x) +#define VCAP_SUPER_MAP_CORE_MAP_GET(x)\ + FIELD_GET(VCAP_SUPER_MAP_CORE_MAP, x) + +/* VCAP_SUPER:VCAP_CONST:VCAP_VER */ +#define VCAP_SUPER_VCAP_VER __REG(TARGET_VCAP_SUPER, 0, 1, 924, 0, 1, 40, 0, 0, 1, 4) + +/* VCAP_SUPER:VCAP_CONST:ENTRY_WIDTH */ +#define VCAP_SUPER_ENTRY_WIDTH __REG(TARGET_VCAP_SUPER, 0, 1, 924, 0, 1, 40, 4, 0, 1, 4) + +/* VCAP_SUPER:VCAP_CONST:ENTRY_CNT */ +#define VCAP_SUPER_ENTRY_CNT __REG(TARGET_VCAP_SUPER, 0, 1, 924, 0, 1, 40, 8, 0, 1, 4) + +/* VCAP_SUPER:VCAP_CONST:ENTRY_SWCNT */ +#define VCAP_SUPER_ENTRY_SWCNT __REG(TARGET_VCAP_SUPER, 0, 1, 924, 0, 1, 40, 12, 0, 1, 4) + +/* VCAP_SUPER:VCAP_CONST:ENTRY_TG_WIDTH */ +#define VCAP_SUPER_ENTRY_TG_WIDTH __REG(TARGET_VCAP_SUPER, 0, 1, 924, 0, 1, 40, 16, 0, 1, 4) + +/* VCAP_SUPER:VCAP_CONST:ACTION_DEF_CNT */ +#define VCAP_SUPER_ACTION_DEF_CNT __REG(TARGET_VCAP_SUPER, 0, 1, 924, 0, 1, 40, 20, 0, 1, 4) + +/* VCAP_SUPER:VCAP_CONST:ACTION_WIDTH */ +#define VCAP_SUPER_ACTION_WIDTH __REG(TARGET_VCAP_SUPER, 0, 1, 924, 0, 1, 40, 24, 0, 1, 4) + +/* VCAP_SUPER:VCAP_CONST:CNT_WIDTH */ +#define VCAP_SUPER_CNT_WIDTH __REG(TARGET_VCAP_SUPER, 0, 1, 924, 0, 1, 40, 28, 0, 1, 4) + +/* VCAP_SUPER:VCAP_CONST:CORE_CNT */ +#define VCAP_SUPER_CORE_CNT __REG(TARGET_VCAP_SUPER, 0, 1, 924, 0, 1, 40, 32, 0, 1, 4) + +/* VCAP_SUPER:VCAP_CONST:IF_CNT */ +#define VCAP_SUPER_IF_CNT __REG(TARGET_VCAP_SUPER, 0, 1, 924, 0, 1, 40, 36, 0, 1, 4) + /* VCAP_SUPER:RAM_CTRL:RAM_INIT */ #define VCAP_SUPER_RAM_INIT __REG(TARGET_VCAP_SUPER, 0, 1, 1120, 0, 1, 4, 0, 0, 1, 4) From c9da1ac1c21222ad04e78347a02b3ced393d1fb8 Mon Sep 17 00:00:00 2001 From: Steen Hegelund Date: Thu, 20 Oct 2022 15:08:59 +0200 Subject: [PATCH 0278/2002] net: microchip: sparx5: Adding initial tc flower support for VCAP API This adds initial TC flower filter support to Sparx5 for the IS2 VCAP. The support consists of the source and destination MAC addresses, and the trap and pass actions. This is how you can create a rule that test the functionality: tc qdisc add dev eth0 clsact tc filter add dev eth0 ingress chain 8000000 prio 10 handle 10 \ protocol all flower skip_sw \ dst_mac 0a:0b:0c:0d:0e:0f \ src_mac 2:0:0:0:0:1 \ action trap The IS2 chains in Sparx5 are assigned like this: - chain 8000000: IS2 Lookup 0 - chain 8100000: IS2 Lookup 1 - chain 8200000: IS2 Lookup 2 - chain 8300000: IS2 Lookup 3 Signed-off-by: Steen Hegelund Tested-by: Casper Andersson Reviewed-by: Casper Andersson Signed-off-by: David S. Miller --- drivers/net/ethernet/microchip/Makefile | 1 + .../net/ethernet/microchip/sparx5/Makefile | 2 +- .../net/ethernet/microchip/sparx5/sparx5_tc.c | 46 +++ .../net/ethernet/microchip/sparx5/sparx5_tc.h | 14 + .../microchip/sparx5/sparx5_tc_flower.c | 217 ++++++++++ .../microchip/sparx5/sparx5_vcap_impl.c | 142 ++++++- .../microchip/sparx5/sparx5_vcap_impl.h | 20 + drivers/net/ethernet/microchip/vcap/Makefile | 8 + .../net/ethernet/microchip/vcap/vcap_api.c | 371 ++++++++++++++++++ .../ethernet/microchip/vcap/vcap_api_client.h | 194 +++++++++ 10 files changed, 1011 insertions(+), 4 deletions(-) create mode 100644 drivers/net/ethernet/microchip/sparx5/sparx5_tc_flower.c create mode 100644 drivers/net/ethernet/microchip/sparx5/sparx5_vcap_impl.h create mode 100644 drivers/net/ethernet/microchip/vcap/Makefile create mode 100644 drivers/net/ethernet/microchip/vcap/vcap_api.c create mode 100644 drivers/net/ethernet/microchip/vcap/vcap_api_client.h diff --git a/drivers/net/ethernet/microchip/Makefile b/drivers/net/ethernet/microchip/Makefile index 9faa41436198..bbd349264e6f 100644 --- a/drivers/net/ethernet/microchip/Makefile +++ b/drivers/net/ethernet/microchip/Makefile @@ -11,3 +11,4 @@ lan743x-objs := lan743x_main.o lan743x_ethtool.o lan743x_ptp.o obj-$(CONFIG_LAN966X_SWITCH) += lan966x/ obj-$(CONFIG_SPARX5_SWITCH) += sparx5/ +obj-$(CONFIG_VCAP) += vcap/ diff --git a/drivers/net/ethernet/microchip/sparx5/Makefile b/drivers/net/ethernet/microchip/sparx5/Makefile index b9c6831c2d92..ee2c42f66742 100644 --- a/drivers/net/ethernet/microchip/sparx5/Makefile +++ b/drivers/net/ethernet/microchip/sparx5/Makefile @@ -9,7 +9,7 @@ sparx5-switch-y := sparx5_main.o sparx5_packet.o \ sparx5_netdev.o sparx5_phylink.o sparx5_port.o sparx5_mactable.o sparx5_vlan.o \ sparx5_switchdev.o sparx5_calendar.o sparx5_ethtool.o sparx5_fdma.o \ sparx5_ptp.o sparx5_pgid.o sparx5_tc.o sparx5_qos.o \ - sparx5_vcap_impl.o sparx5_vcap_ag_api.o + sparx5_vcap_impl.o sparx5_vcap_ag_api.o sparx5_tc_flower.o # Provide include files ccflags-y += -I$(srctree)/drivers/net/ethernet/microchip/vcap diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_tc.c b/drivers/net/ethernet/microchip/sparx5/sparx5_tc.c index e05429c751ee..9432251b8322 100644 --- a/drivers/net/ethernet/microchip/sparx5/sparx5_tc.c +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_tc.c @@ -10,6 +10,50 @@ #include "sparx5_main.h" #include "sparx5_qos.h" +/* tc block handling */ +static LIST_HEAD(sparx5_block_cb_list); + +static int sparx5_tc_block_cb(enum tc_setup_type type, + void *type_data, + void *cb_priv, bool ingress) +{ + struct net_device *ndev = cb_priv; + + if (type == TC_SETUP_CLSFLOWER) + return sparx5_tc_flower(ndev, type_data, ingress); + return -EOPNOTSUPP; +} + +static int sparx5_tc_block_cb_ingress(enum tc_setup_type type, + void *type_data, + void *cb_priv) +{ + return sparx5_tc_block_cb(type, type_data, cb_priv, true); +} + +static int sparx5_tc_block_cb_egress(enum tc_setup_type type, + void *type_data, + void *cb_priv) +{ + return sparx5_tc_block_cb(type, type_data, cb_priv, false); +} + +static int sparx5_tc_setup_block(struct net_device *ndev, + struct flow_block_offload *fbo) +{ + flow_setup_cb_t *cb; + + if (fbo->binder_type == FLOW_BLOCK_BINDER_TYPE_CLSACT_INGRESS) + cb = sparx5_tc_block_cb_ingress; + else if (fbo->binder_type == FLOW_BLOCK_BINDER_TYPE_CLSACT_EGRESS) + cb = sparx5_tc_block_cb_egress; + else + return -EOPNOTSUPP; + + return flow_block_cb_setup_simple(fbo, &sparx5_block_cb_list, + cb, ndev, ndev, false); +} + static void sparx5_tc_get_layer_and_idx(u32 parent, u32 portno, u32 *layer, u32 *idx) { @@ -111,6 +155,8 @@ int sparx5_port_setup_tc(struct net_device *ndev, enum tc_setup_type type, void *type_data) { switch (type) { + case TC_SETUP_BLOCK: + return sparx5_tc_setup_block(ndev, type_data); case TC_SETUP_QDISC_MQPRIO: return sparx5_tc_setup_qdisc_mqprio(ndev, type_data); case TC_SETUP_QDISC_TBF: diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_tc.h b/drivers/net/ethernet/microchip/sparx5/sparx5_tc.h index 5b55e11b77e1..2b07a93fc9b7 100644 --- a/drivers/net/ethernet/microchip/sparx5/sparx5_tc.h +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_tc.h @@ -7,9 +7,23 @@ #ifndef __SPARX5_TC_H__ #define __SPARX5_TC_H__ +#include #include +/* Controls how PORT_MASK is applied */ +enum SPX5_PORT_MASK_MODE { + SPX5_PMM_OR_DSTMASK, + SPX5_PMM_AND_VLANMASK, + SPX5_PMM_REPLACE_PGID, + SPX5_PMM_REPLACE_ALL, + SPX5_PMM_REDIR_PGID, + SPX5_PMM_OR_PGID_MASK, +}; + int sparx5_port_setup_tc(struct net_device *ndev, enum tc_setup_type type, void *type_data); +int sparx5_tc_flower(struct net_device *ndev, struct flow_cls_offload *fco, + bool ingress); + #endif /* __SPARX5_TC_H__ */ diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_tc_flower.c b/drivers/net/ethernet/microchip/sparx5/sparx5_tc_flower.c new file mode 100644 index 000000000000..626558a5c850 --- /dev/null +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_tc_flower.c @@ -0,0 +1,217 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* Microchip VCAP API + * + * Copyright (c) 2022 Microchip Technology Inc. and its subsidiaries. + */ + +#include + +#include "sparx5_tc.h" +#include "vcap_api.h" +#include "vcap_api_client.h" +#include "sparx5_main.h" +#include "sparx5_vcap_impl.h" + +struct sparx5_tc_flower_parse_usage { + struct flow_cls_offload *fco; + struct flow_rule *frule; + struct vcap_rule *vrule; + unsigned int used_keys; +}; + +static int sparx5_tc_flower_handler_ethaddr_usage(struct sparx5_tc_flower_parse_usage *st) +{ + enum vcap_key_field smac_key = VCAP_KF_L2_SMAC; + enum vcap_key_field dmac_key = VCAP_KF_L2_DMAC; + struct flow_match_eth_addrs match; + struct vcap_u48_key smac, dmac; + int err = 0; + + flow_rule_match_eth_addrs(st->frule, &match); + + if (!is_zero_ether_addr(match.mask->src)) { + vcap_netbytes_copy(smac.value, match.key->src, ETH_ALEN); + vcap_netbytes_copy(smac.mask, match.mask->src, ETH_ALEN); + err = vcap_rule_add_key_u48(st->vrule, smac_key, &smac); + if (err) + goto out; + } + + if (!is_zero_ether_addr(match.mask->dst)) { + vcap_netbytes_copy(dmac.value, match.key->dst, ETH_ALEN); + vcap_netbytes_copy(dmac.mask, match.mask->dst, ETH_ALEN); + err = vcap_rule_add_key_u48(st->vrule, dmac_key, &dmac); + if (err) + goto out; + } + + st->used_keys |= BIT(FLOW_DISSECTOR_KEY_ETH_ADDRS); + + return err; + +out: + NL_SET_ERR_MSG_MOD(st->fco->common.extack, "eth_addr parse error"); + return err; +} + +static int (*sparx5_tc_flower_usage_handlers[])(struct sparx5_tc_flower_parse_usage *st) = { + /* More dissector handlers will be added here later */ + [FLOW_DISSECTOR_KEY_ETH_ADDRS] = sparx5_tc_flower_handler_ethaddr_usage, +}; + +static int sparx5_tc_use_dissectors(struct flow_cls_offload *fco, + struct vcap_admin *admin, + struct vcap_rule *vrule) +{ + struct sparx5_tc_flower_parse_usage state = { + .fco = fco, + .vrule = vrule, + }; + int idx, err = 0; + + state.frule = flow_cls_offload_flow_rule(fco); + for (idx = 0; idx < ARRAY_SIZE(sparx5_tc_flower_usage_handlers); ++idx) { + if (!flow_rule_match_key(state.frule, idx)) + continue; + if (!sparx5_tc_flower_usage_handlers[idx]) + continue; + err = sparx5_tc_flower_usage_handlers[idx](&state); + if (err) + return err; + } + return err; +} + +static int sparx5_tc_flower_replace(struct net_device *ndev, + struct flow_cls_offload *fco, + struct vcap_admin *admin) +{ + struct sparx5_port *port = netdev_priv(ndev); + struct flow_action_entry *act; + struct vcap_control *vctrl; + struct flow_rule *frule; + struct vcap_rule *vrule; + int err, idx; + + frule = flow_cls_offload_flow_rule(fco); + if (!flow_action_has_entries(&frule->action)) { + NL_SET_ERR_MSG_MOD(fco->common.extack, "No actions"); + return -EINVAL; + } + + if (!flow_action_basic_hw_stats_check(&frule->action, fco->common.extack)) + return -EOPNOTSUPP; + + vctrl = port->sparx5->vcap_ctrl; + vrule = vcap_alloc_rule(vctrl, ndev, fco->common.chain_index, VCAP_USER_TC, + fco->common.prio, 0); + if (IS_ERR(vrule)) + return PTR_ERR(vrule); + + vrule->cookie = fco->cookie; + sparx5_tc_use_dissectors(fco, admin, vrule); + flow_action_for_each(idx, act, &frule->action) { + switch (act->id) { + case FLOW_ACTION_TRAP: + err = vcap_rule_add_action_bit(vrule, + VCAP_AF_CPU_COPY_ENA, + VCAP_BIT_1); + if (err) + goto out; + err = vcap_rule_add_action_u32(vrule, + VCAP_AF_CPU_QUEUE_NUM, 0); + if (err) + goto out; + err = vcap_rule_add_action_u32(vrule, VCAP_AF_MASK_MODE, + SPX5_PMM_REPLACE_ALL); + if (err) + goto out; + /* For now the actionset is hardcoded */ + err = vcap_set_rule_set_actionset(vrule, + VCAP_AFS_BASE_TYPE); + if (err) + goto out; + break; + case FLOW_ACTION_ACCEPT: + /* For now the actionset is hardcoded */ + err = vcap_set_rule_set_actionset(vrule, + VCAP_AFS_BASE_TYPE); + if (err) + goto out; + break; + default: + NL_SET_ERR_MSG_MOD(fco->common.extack, + "Unsupported TC action"); + err = -EOPNOTSUPP; + goto out; + } + } + /* For now the keyset is hardcoded */ + err = vcap_set_rule_set_keyset(vrule, VCAP_KFS_MAC_ETYPE); + if (err) { + NL_SET_ERR_MSG_MOD(fco->common.extack, + "No matching port keyset for filter protocol and keys"); + goto out; + } + err = vcap_val_rule(vrule, ETH_P_ALL); + if (err) { + vcap_set_tc_exterr(fco, vrule); + goto out; + } + err = vcap_add_rule(vrule); + if (err) + NL_SET_ERR_MSG_MOD(fco->common.extack, + "Could not add the filter"); +out: + vcap_free_rule(vrule); + return err; +} + +static int sparx5_tc_flower_destroy(struct net_device *ndev, + struct flow_cls_offload *fco, + struct vcap_admin *admin) +{ + struct sparx5_port *port = netdev_priv(ndev); + struct vcap_control *vctrl; + int err = -ENOENT, rule_id; + + vctrl = port->sparx5->vcap_ctrl; + while (true) { + rule_id = vcap_lookup_rule_by_cookie(vctrl, fco->cookie); + if (rule_id <= 0) + break; + err = vcap_del_rule(vctrl, ndev, rule_id); + if (err) { + pr_err("%s:%d: could not delete rule %d\n", + __func__, __LINE__, rule_id); + break; + } + } + return err; +} + +int sparx5_tc_flower(struct net_device *ndev, struct flow_cls_offload *fco, + bool ingress) +{ + struct sparx5_port *port = netdev_priv(ndev); + struct vcap_control *vctrl; + struct vcap_admin *admin; + int err = -EINVAL; + + /* Get vcap instance from the chain id */ + vctrl = port->sparx5->vcap_ctrl; + admin = vcap_find_admin(vctrl, fco->common.chain_index); + if (!admin) { + NL_SET_ERR_MSG_MOD(fco->common.extack, "Invalid chain"); + return err; + } + + switch (fco->command) { + case FLOW_CLS_REPLACE: + return sparx5_tc_flower_replace(ndev, fco, admin); + case FLOW_CLS_DESTROY: + return sparx5_tc_flower_destroy(ndev, fco, admin); + default: + return -EOPNOTSUPP; + } +} diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_vcap_impl.c b/drivers/net/ethernet/microchip/sparx5/sparx5_vcap_impl.c index 68f6fed80556..5ec005e636aa 100644 --- a/drivers/net/ethernet/microchip/sparx5/sparx5_vcap_impl.c +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_vcap_impl.c @@ -11,18 +11,133 @@ #include #include "vcap_api.h" +#include "vcap_api_client.h" #include "sparx5_main_regs.h" #include "sparx5_main.h" +#include "sparx5_vcap_impl.h" #include "sparx5_vcap_ag_api.h" +#define SUPER_VCAP_BLK_SIZE 3072 /* addresses per Super VCAP block */ +#define STREAMSIZE (64 * 4) /* bytes in the VCAP cache area */ + +#define SPARX5_IS2_LOOKUPS 4 + +static struct sparx5_vcap_inst { + enum vcap_type vtype; /* type of vcap */ + int vinst; /* instance number within the same type */ + int lookups; /* number of lookups in this vcap type */ + int lookups_per_instance; /* number of lookups in this instance */ + int first_cid; /* first chain id in this vcap */ + int last_cid; /* last chain id in this vcap */ + int count; /* number of available addresses, not in super vcap */ + int map_id; /* id in the super vcap block mapping (if applicable) */ + int blockno; /* starting block in super vcap (if applicable) */ + int blocks; /* number of blocks in super vcap (if applicable) */ +} sparx5_vcap_inst_cfg[] = { + { + .vtype = VCAP_TYPE_IS2, /* IS2-0 */ + .vinst = 0, + .map_id = 4, + .lookups = SPARX5_IS2_LOOKUPS, + .lookups_per_instance = SPARX5_IS2_LOOKUPS / 2, + .first_cid = SPARX5_VCAP_CID_IS2_L0, + .last_cid = SPARX5_VCAP_CID_IS2_L2 - 1, + .blockno = 0, /* Maps block 0-1 */ + .blocks = 2, + }, + { + .vtype = VCAP_TYPE_IS2, /* IS2-1 */ + .vinst = 1, + .map_id = 5, + .lookups = SPARX5_IS2_LOOKUPS, + .lookups_per_instance = SPARX5_IS2_LOOKUPS / 2, + .first_cid = SPARX5_VCAP_CID_IS2_L2, + .last_cid = SPARX5_VCAP_CID_IS2_MAX, + .blockno = 2, /* Maps block 2-3 */ + .blocks = 2, + }, +}; + +static void sparx5_vcap_admin_free(struct vcap_admin *admin) +{ + if (!admin) + return; + kfree(admin->cache.keystream); + kfree(admin->cache.maskstream); + kfree(admin->cache.actionstream); + kfree(admin); +} + +/* Allocate a vcap instance with a rule list and a cache area */ +static struct vcap_admin * +sparx5_vcap_admin_alloc(struct sparx5 *sparx5, struct vcap_control *ctrl, + const struct sparx5_vcap_inst *cfg) +{ + struct vcap_admin *admin; + + admin = kzalloc(sizeof(*admin), GFP_KERNEL); + if (!admin) + return ERR_PTR(-ENOMEM); + INIT_LIST_HEAD(&admin->list); + INIT_LIST_HEAD(&admin->rules); + admin->vtype = cfg->vtype; + admin->vinst = cfg->vinst; + admin->lookups = cfg->lookups; + admin->lookups_per_instance = cfg->lookups_per_instance; + admin->first_cid = cfg->first_cid; + admin->last_cid = cfg->last_cid; + admin->cache.keystream = + kzalloc(STREAMSIZE, GFP_KERNEL); + admin->cache.maskstream = + kzalloc(STREAMSIZE, GFP_KERNEL); + admin->cache.actionstream = + kzalloc(STREAMSIZE, GFP_KERNEL); + if (!admin->cache.keystream || !admin->cache.maskstream || + !admin->cache.actionstream) { + sparx5_vcap_admin_free(admin); + return ERR_PTR(-ENOMEM); + } + return admin; +} + +/* Do block allocations and provide addresses for VCAP instances */ +static void sparx5_vcap_block_alloc(struct sparx5 *sparx5, + struct vcap_admin *admin, + const struct sparx5_vcap_inst *cfg) +{ + int idx; + + /* Super VCAP block mapping and address configuration. Block 0 + * is assigned addresses 0 through 3071, block 1 is assigned + * addresses 3072 though 6143, and so on. + */ + for (idx = cfg->blockno; idx < cfg->blockno + cfg->blocks; ++idx) { + spx5_wr(VCAP_SUPER_IDX_CORE_IDX_SET(idx), sparx5, + VCAP_SUPER_IDX); + spx5_wr(VCAP_SUPER_MAP_CORE_MAP_SET(cfg->map_id), sparx5, + VCAP_SUPER_MAP); + } + admin->first_valid_addr = cfg->blockno * SUPER_VCAP_BLK_SIZE; + admin->last_used_addr = admin->first_valid_addr + + cfg->blocks * SUPER_VCAP_BLK_SIZE; + admin->last_valid_addr = admin->last_used_addr - 1; +} + /* Allocate a vcap control and vcap instances and configure the system */ int sparx5_vcap_init(struct sparx5 *sparx5) { + const struct sparx5_vcap_inst *cfg; struct vcap_control *ctrl; + struct vcap_admin *admin; + int err = 0, idx; /* Create a VCAP control instance that owns the platform specific VCAP * model with VCAP instances and information about keysets, keys, * actionsets and actions + * - Create administrative state for each available VCAP + * - Lists of rules + * - Address information + * - Initialize VCAP blocks */ ctrl = kzalloc(sizeof(*ctrl), GFP_KERNEL); if (!ctrl) @@ -33,13 +148,34 @@ int sparx5_vcap_init(struct sparx5 *sparx5) ctrl->vcaps = sparx5_vcaps; ctrl->stats = &sparx5_vcap_stats; - return 0; + INIT_LIST_HEAD(&ctrl->list); + for (idx = 0; idx < ARRAY_SIZE(sparx5_vcap_inst_cfg); ++idx) { + cfg = &sparx5_vcap_inst_cfg[idx]; + admin = sparx5_vcap_admin_alloc(sparx5, ctrl, cfg); + if (IS_ERR(admin)) { + err = PTR_ERR(admin); + pr_err("%s:%d: vcap allocation failed: %d\n", + __func__, __LINE__, err); + return err; + } + sparx5_vcap_block_alloc(sparx5, admin, cfg); + list_add_tail(&admin->list, &ctrl->list); + } + + return err; } void sparx5_vcap_destroy(struct sparx5 *sparx5) { - if (!sparx5->vcap_ctrl) + struct vcap_control *ctrl = sparx5->vcap_ctrl; + struct vcap_admin *admin, *admin_next; + + if (!ctrl) return; - kfree(sparx5->vcap_ctrl); + list_for_each_entry_safe(admin, admin_next, &ctrl->list, list) { + list_del(&admin->list); + sparx5_vcap_admin_free(admin); + } + kfree(ctrl); } diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_vcap_impl.h b/drivers/net/ethernet/microchip/sparx5/sparx5_vcap_impl.h new file mode 100644 index 000000000000..8e44ebd76b41 --- /dev/null +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_vcap_impl.h @@ -0,0 +1,20 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* Microchip Sparx5 Switch driver VCAP implementation + * + * Copyright (c) 2022 Microchip Technology Inc. and its subsidiaries. + * + * The Sparx5 Chip Register Model can be browsed at this location: + * https://github.com/microchip-ung/sparx-5_reginfo + */ + +#ifndef __SPARX5_VCAP_IMPL_H__ +#define __SPARX5_VCAP_IMPL_H__ + +#define SPARX5_VCAP_CID_IS2_L0 VCAP_CID_INGRESS_STAGE2_L0 /* IS2 lookup 0 */ +#define SPARX5_VCAP_CID_IS2_L1 VCAP_CID_INGRESS_STAGE2_L1 /* IS2 lookup 1 */ +#define SPARX5_VCAP_CID_IS2_L2 VCAP_CID_INGRESS_STAGE2_L2 /* IS2 lookup 2 */ +#define SPARX5_VCAP_CID_IS2_L3 VCAP_CID_INGRESS_STAGE2_L3 /* IS2 lookup 3 */ +#define SPARX5_VCAP_CID_IS2_MAX \ + (VCAP_CID_INGRESS_STAGE2_L3 + VCAP_CID_LOOKUP_SIZE - 1) /* IS2 Max */ + +#endif /* __SPARX5_VCAP_IMPL_H__ */ diff --git a/drivers/net/ethernet/microchip/vcap/Makefile b/drivers/net/ethernet/microchip/vcap/Makefile new file mode 100644 index 000000000000..598d1c296f38 --- /dev/null +++ b/drivers/net/ethernet/microchip/vcap/Makefile @@ -0,0 +1,8 @@ +# SPDX-License-Identifier: GPL-2.0-only +# +# Makefile for the Microchip VCAP API +# + +obj-$(CONFIG_VCAP) += vcap.o + +vcap-y += vcap_api.o diff --git a/drivers/net/ethernet/microchip/vcap/vcap_api.c b/drivers/net/ethernet/microchip/vcap/vcap_api.c new file mode 100644 index 000000000000..3eb8aa237c8e --- /dev/null +++ b/drivers/net/ethernet/microchip/vcap/vcap_api.c @@ -0,0 +1,371 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* Microchip VCAP API + * + * Copyright (c) 2022 Microchip Technology Inc. and its subsidiaries. + */ + +#include + +#include "vcap_api.h" +#include "vcap_api_client.h" + +#define to_intrule(rule) container_of((rule), struct vcap_rule_internal, data) + +/* Private VCAP API rule data */ +struct vcap_rule_internal { + struct vcap_rule data; /* provided by the client */ + struct list_head list; /* for insertion in the vcap admin list of rules */ + struct vcap_admin *admin; /* vcap hw instance */ + struct net_device *ndev; /* the interface that the rule applies to */ + struct vcap_control *vctrl; /* the client control */ + u32 addr; /* address in the VCAP at insertion */ +}; + +/* Update the keyset for the rule */ +int vcap_set_rule_set_keyset(struct vcap_rule *rule, + enum vcap_keyfield_set keyset) +{ + /* This will be expanded with more information later */ + rule->keyset = keyset; + return 0; +} +EXPORT_SYMBOL_GPL(vcap_set_rule_set_keyset); + +/* Update the actionset for the rule */ +int vcap_set_rule_set_actionset(struct vcap_rule *rule, + enum vcap_actionfield_set actionset) +{ + /* This will be expanded with more information later */ + rule->actionset = actionset; + return 0; +} +EXPORT_SYMBOL_GPL(vcap_set_rule_set_actionset); + +/* Find a rule with a provided rule id */ +static struct vcap_rule_internal *vcap_lookup_rule(struct vcap_control *vctrl, + u32 id) +{ + struct vcap_rule_internal *ri; + struct vcap_admin *admin; + + /* Look for the rule id in all vcaps */ + list_for_each_entry(admin, &vctrl->list, list) + list_for_each_entry(ri, &admin->rules, list) + if (ri->data.id == id) + return ri; + return NULL; +} + +/* Find a rule id with a provided cookie */ +int vcap_lookup_rule_by_cookie(struct vcap_control *vctrl, u64 cookie) +{ + struct vcap_rule_internal *ri; + struct vcap_admin *admin; + + /* Look for the rule id in all vcaps */ + list_for_each_entry(admin, &vctrl->list, list) + list_for_each_entry(ri, &admin->rules, list) + if (ri->data.cookie == cookie) + return ri->data.id; + return -ENOENT; +} +EXPORT_SYMBOL_GPL(vcap_lookup_rule_by_cookie); + +/* Lookup a vcap instance using chain id */ +struct vcap_admin *vcap_find_admin(struct vcap_control *vctrl, int cid) +{ + struct vcap_admin *admin; + + list_for_each_entry(admin, &vctrl->list, list) { + if (cid >= admin->first_cid && cid <= admin->last_cid) + return admin; + } + return NULL; +} +EXPORT_SYMBOL_GPL(vcap_find_admin); + +/* Validate a rule with respect to available port keys */ +int vcap_val_rule(struct vcap_rule *rule, u16 l3_proto) +{ + struct vcap_rule_internal *ri = to_intrule(rule); + + /* This validation will be much expanded later */ + if (!ri->admin) { + ri->data.exterr = VCAP_ERR_NO_ADMIN; + return -EINVAL; + } + if (!ri->ndev) { + ri->data.exterr = VCAP_ERR_NO_NETDEV; + return -EINVAL; + } + if (ri->data.keyset == VCAP_KFS_NO_VALUE) { + ri->data.exterr = VCAP_ERR_NO_KEYSET_MATCH; + return -EINVAL; + } + if (ri->data.actionset == VCAP_AFS_NO_VALUE) { + ri->data.exterr = VCAP_ERR_NO_ACTIONSET_MATCH; + return -EINVAL; + } + return 0; +} +EXPORT_SYMBOL_GPL(vcap_val_rule); + +/* Assign a unique rule id and autogenerate one if id == 0 */ +static u32 vcap_set_rule_id(struct vcap_rule_internal *ri) +{ + u32 next_id; + + if (ri->data.id != 0) + return ri->data.id; + + next_id = ri->vctrl->rule_id + 1; + + for (next_id = ri->vctrl->rule_id + 1; next_id < ~0; ++next_id) { + if (!vcap_lookup_rule(ri->vctrl, next_id)) { + ri->data.id = next_id; + ri->vctrl->rule_id = next_id; + break; + } + } + return ri->data.id; +} + +/* Encode and write a validated rule to the VCAP */ +int vcap_add_rule(struct vcap_rule *rule) +{ + /* This will later handling the encode and writing of the rule */ + return 0; +} +EXPORT_SYMBOL_GPL(vcap_add_rule); + +/* Allocate a new rule with the provided arguments */ +struct vcap_rule *vcap_alloc_rule(struct vcap_control *vctrl, + struct net_device *ndev, int vcap_chain_id, + enum vcap_user user, u16 priority, + u32 id) +{ + struct vcap_rule_internal *ri; + struct vcap_admin *admin; + + if (!ndev) + return ERR_PTR(-ENODEV); + /* Get the VCAP instance */ + admin = vcap_find_admin(vctrl, vcap_chain_id); + if (!admin) + return ERR_PTR(-ENOENT); + /* Create a container for the rule and return it */ + ri = kzalloc(sizeof(*ri), GFP_KERNEL); + if (!ri) + return ERR_PTR(-ENOMEM); + ri->data.vcap_chain_id = vcap_chain_id; + ri->data.user = user; + ri->data.priority = priority; + ri->data.id = id; + ri->data.keyset = VCAP_KFS_NO_VALUE; + ri->data.actionset = VCAP_AFS_NO_VALUE; + INIT_LIST_HEAD(&ri->list); + INIT_LIST_HEAD(&ri->data.keyfields); + INIT_LIST_HEAD(&ri->data.actionfields); + ri->ndev = ndev; + ri->admin = admin; /* refer to the vcap instance */ + ri->vctrl = vctrl; /* refer to the client */ + if (vcap_set_rule_id(ri) == 0) + goto out_free; + return (struct vcap_rule *)ri; + +out_free: + kfree(ri); + return ERR_PTR(-EINVAL); +} +EXPORT_SYMBOL_GPL(vcap_alloc_rule); + +/* Free mem of a rule owned by client after the rule as been added to the VCAP */ +void vcap_free_rule(struct vcap_rule *rule) +{ + struct vcap_rule_internal *ri = to_intrule(rule); + struct vcap_client_actionfield *caf, *next_caf; + struct vcap_client_keyfield *ckf, *next_ckf; + + /* Deallocate the list of keys and actions */ + list_for_each_entry_safe(ckf, next_ckf, &ri->data.keyfields, ctrl.list) { + list_del(&ckf->ctrl.list); + kfree(ckf); + } + list_for_each_entry_safe(caf, next_caf, &ri->data.actionfields, ctrl.list) { + list_del(&caf->ctrl.list); + kfree(caf); + } + /* Deallocate the rule */ + kfree(rule); +} +EXPORT_SYMBOL_GPL(vcap_free_rule); + +/* Delete rule in a VCAP instance */ +int vcap_del_rule(struct vcap_control *vctrl, struct net_device *ndev, u32 id) +{ + struct vcap_rule_internal *ri, *elem; + struct vcap_admin *admin; + + /* This will later also handle rule moving */ + if (!ndev) + return -ENODEV; + /* Look for the rule id in all vcaps */ + ri = vcap_lookup_rule(vctrl, id); + if (!ri) + return -EINVAL; + admin = ri->admin; + list_del(&ri->list); + if (list_empty(&admin->rules)) { + admin->last_used_addr = admin->last_valid_addr; + } else { + /* update the address range end marker from the last rule in the list */ + elem = list_last_entry(&admin->rules, struct vcap_rule_internal, list); + admin->last_used_addr = elem->addr; + } + kfree(ri); + return 0; +} +EXPORT_SYMBOL_GPL(vcap_del_rule); + +static void vcap_copy_from_client_keyfield(struct vcap_rule *rule, + struct vcap_client_keyfield *field, + struct vcap_client_keyfield_data *data) +{ + /* This will be expanded later to handle different vcap memory layouts */ + memcpy(&field->data, data, sizeof(field->data)); +} + +static int vcap_rule_add_key(struct vcap_rule *rule, + enum vcap_key_field key, + enum vcap_field_type ftype, + struct vcap_client_keyfield_data *data) +{ + struct vcap_client_keyfield *field; + + /* More validation will be added here later */ + field = kzalloc(sizeof(*field), GFP_KERNEL); + if (!field) + return -ENOMEM; + field->ctrl.key = key; + field->ctrl.type = ftype; + vcap_copy_from_client_keyfield(rule, field, data); + list_add_tail(&field->ctrl.list, &rule->keyfields); + return 0; +} + +/* Add a 48 bit key with value and mask to the rule */ +int vcap_rule_add_key_u48(struct vcap_rule *rule, enum vcap_key_field key, + struct vcap_u48_key *fieldval) +{ + struct vcap_client_keyfield_data data; + + memcpy(&data.u48, fieldval, sizeof(data.u48)); + return vcap_rule_add_key(rule, key, VCAP_FIELD_U48, &data); +} +EXPORT_SYMBOL_GPL(vcap_rule_add_key_u48); + +static void vcap_copy_from_client_actionfield(struct vcap_rule *rule, + struct vcap_client_actionfield *field, + struct vcap_client_actionfield_data *data) +{ + /* This will be expanded later to handle different vcap memory layouts */ + memcpy(&field->data, data, sizeof(field->data)); +} + +static int vcap_rule_add_action(struct vcap_rule *rule, + enum vcap_action_field action, + enum vcap_field_type ftype, + struct vcap_client_actionfield_data *data) +{ + struct vcap_client_actionfield *field; + + /* More validation will be added here later */ + field = kzalloc(sizeof(*field), GFP_KERNEL); + if (!field) + return -ENOMEM; + field->ctrl.action = action; + field->ctrl.type = ftype; + vcap_copy_from_client_actionfield(rule, field, data); + list_add_tail(&field->ctrl.list, &rule->actionfields); + return 0; +} + +static void vcap_rule_set_action_bitsize(struct vcap_u1_action *u1, + enum vcap_bit val) +{ + switch (val) { + case VCAP_BIT_0: + u1->value = 0; + break; + case VCAP_BIT_1: + u1->value = 1; + break; + case VCAP_BIT_ANY: + u1->value = 0; + break; + } +} + +/* Add a bit action with value to the rule */ +int vcap_rule_add_action_bit(struct vcap_rule *rule, + enum vcap_action_field action, + enum vcap_bit val) +{ + struct vcap_client_actionfield_data data; + + vcap_rule_set_action_bitsize(&data.u1, val); + return vcap_rule_add_action(rule, action, VCAP_FIELD_BIT, &data); +} +EXPORT_SYMBOL_GPL(vcap_rule_add_action_bit); + +/* Add a 32 bit action field with value to the rule */ +int vcap_rule_add_action_u32(struct vcap_rule *rule, + enum vcap_action_field action, + u32 value) +{ + struct vcap_client_actionfield_data data; + + data.u32.value = value; + return vcap_rule_add_action(rule, action, VCAP_FIELD_U32, &data); +} +EXPORT_SYMBOL_GPL(vcap_rule_add_action_u32); + +/* Copy to host byte order */ +void vcap_netbytes_copy(u8 *dst, u8 *src, int count) +{ + int idx; + + for (idx = 0; idx < count; ++idx, ++dst) + *dst = src[count - idx - 1]; +} +EXPORT_SYMBOL_GPL(vcap_netbytes_copy); + +/* Convert validation error code into tc extact error message */ +void vcap_set_tc_exterr(struct flow_cls_offload *fco, struct vcap_rule *vrule) +{ + switch (vrule->exterr) { + case VCAP_ERR_NONE: + break; + case VCAP_ERR_NO_ADMIN: + NL_SET_ERR_MSG_MOD(fco->common.extack, + "Missing VCAP instance"); + break; + case VCAP_ERR_NO_NETDEV: + NL_SET_ERR_MSG_MOD(fco->common.extack, + "Missing network interface"); + break; + case VCAP_ERR_NO_KEYSET_MATCH: + NL_SET_ERR_MSG_MOD(fco->common.extack, + "No keyset matched the filter keys"); + break; + case VCAP_ERR_NO_ACTIONSET_MATCH: + NL_SET_ERR_MSG_MOD(fco->common.extack, + "No actionset matched the filter actions"); + break; + case VCAP_ERR_NO_PORT_KEYSET_MATCH: + NL_SET_ERR_MSG_MOD(fco->common.extack, + "No port keyset matched the filter keys"); + break; + } +} +EXPORT_SYMBOL_GPL(vcap_set_tc_exterr); diff --git a/drivers/net/ethernet/microchip/vcap/vcap_api_client.h b/drivers/net/ethernet/microchip/vcap/vcap_api_client.h new file mode 100644 index 000000000000..78ac4628fc9f --- /dev/null +++ b/drivers/net/ethernet/microchip/vcap/vcap_api_client.h @@ -0,0 +1,194 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* Copyright (C) 2022 Microchip Technology Inc. and its subsidiaries. + * Microchip VCAP API + */ + +#ifndef __VCAP_API_CLIENT__ +#define __VCAP_API_CLIENT__ + +#include +#include +#include +#include + +#include "vcap_api.h" + +/* Client supplied VCAP rule key control part */ +struct vcap_client_keyfield_ctrl { + struct list_head list; /* For insertion into a rule */ + enum vcap_key_field key; + enum vcap_field_type type; +}; + +struct vcap_u1_key { + u8 value; + u8 mask; +}; + +struct vcap_u32_key { + u32 value; + u32 mask; +}; + +struct vcap_u48_key { + u8 value[6]; + u8 mask[6]; +}; + +struct vcap_u56_key { + u8 value[7]; + u8 mask[7]; +}; + +struct vcap_u64_key { + u8 value[8]; + u8 mask[8]; +}; + +struct vcap_u72_key { + u8 value[9]; + u8 mask[9]; +}; + +struct vcap_u112_key { + u8 value[14]; + u8 mask[14]; +}; + +struct vcap_u128_key { + u8 value[16]; + u8 mask[16]; +}; + +/* Client supplied VCAP rule field data */ +struct vcap_client_keyfield_data { + union { + struct vcap_u1_key u1; + struct vcap_u32_key u32; + struct vcap_u48_key u48; + struct vcap_u56_key u56; + struct vcap_u64_key u64; + struct vcap_u72_key u72; + struct vcap_u112_key u112; + struct vcap_u128_key u128; + }; +}; + +/* Client supplied VCAP rule key (value, mask) */ +struct vcap_client_keyfield { + struct vcap_client_keyfield_ctrl ctrl; + struct vcap_client_keyfield_data data; +}; + +/* Client supplied VCAP rule action control part */ +struct vcap_client_actionfield_ctrl { + struct list_head list; /* For insertion into a rule */ + enum vcap_action_field action; + enum vcap_field_type type; +}; + +struct vcap_u1_action { + u8 value; +}; + +struct vcap_u32_action { + u32 value; +}; + +struct vcap_u48_action { + u8 value[6]; +}; + +struct vcap_u56_action { + u8 value[7]; +}; + +struct vcap_u64_action { + u8 value[8]; +}; + +struct vcap_u72_action { + u8 value[9]; +}; + +struct vcap_u112_action { + u8 value[14]; +}; + +struct vcap_u128_action { + u8 value[16]; +}; + +struct vcap_client_actionfield_data { + union { + struct vcap_u1_action u1; + struct vcap_u32_action u32; + struct vcap_u48_action u48; + struct vcap_u56_action u56; + struct vcap_u64_action u64; + struct vcap_u72_action u72; + struct vcap_u112_action u112; + struct vcap_u128_action u128; + }; +}; + +struct vcap_client_actionfield { + struct vcap_client_actionfield_ctrl ctrl; + struct vcap_client_actionfield_data data; +}; + +enum vcap_bit { + VCAP_BIT_ANY, + VCAP_BIT_0, + VCAP_BIT_1 +}; + +/* VCAP rule operations */ +/* Allocate a rule and fill in the basic information */ +struct vcap_rule *vcap_alloc_rule(struct vcap_control *vctrl, + struct net_device *ndev, + int vcap_chain_id, + enum vcap_user user, + u16 priority, + u32 id); +/* Free mem of a rule owned by client */ +void vcap_free_rule(struct vcap_rule *rule); +/* Validate a rule before adding it to the VCAP */ +int vcap_val_rule(struct vcap_rule *rule, u16 l3_proto); +/* Add rule to a VCAP instance */ +int vcap_add_rule(struct vcap_rule *rule); +/* Delete rule in a VCAP instance */ +int vcap_del_rule(struct vcap_control *vctrl, struct net_device *ndev, u32 id); + +/* Update the keyset for the rule */ +int vcap_set_rule_set_keyset(struct vcap_rule *rule, + enum vcap_keyfield_set keyset); +/* Update the actionset for the rule */ +int vcap_set_rule_set_actionset(struct vcap_rule *rule, + enum vcap_actionfield_set actionset); + +/* VCAP rule field operations */ +int vcap_rule_add_key_bit(struct vcap_rule *rule, enum vcap_key_field key, + enum vcap_bit val); +int vcap_rule_add_key_u32(struct vcap_rule *rule, enum vcap_key_field key, + u32 value, u32 mask); +int vcap_rule_add_key_u48(struct vcap_rule *rule, enum vcap_key_field key, + struct vcap_u48_key *fieldval); +int vcap_rule_add_action_bit(struct vcap_rule *rule, + enum vcap_action_field action, enum vcap_bit val); +int vcap_rule_add_action_u32(struct vcap_rule *rule, + enum vcap_action_field action, u32 value); + +/* VCAP lookup operations */ +/* Lookup a vcap instance using chain id */ +struct vcap_admin *vcap_find_admin(struct vcap_control *vctrl, int cid); +/* Find a rule id with a provided cookie */ +int vcap_lookup_rule_by_cookie(struct vcap_control *vctrl, u64 cookie); + +/* Copy to host byte order */ +void vcap_netbytes_copy(u8 *dst, u8 *src, int count); + +/* Convert validation error code into tc extact error message */ +void vcap_set_tc_exterr(struct flow_cls_offload *fco, struct vcap_rule *vrule); + +#endif /* __VCAP_API_CLIENT__ */ From 46be056ee0fc516b7591d549dd5fbb1e93df9c7e Mon Sep 17 00:00:00 2001 From: Steen Hegelund Date: Thu, 20 Oct 2022 15:09:00 +0200 Subject: [PATCH 0279/2002] net: microchip: sparx5: Adding port keyset config and callback interface This provides a default port keyset configuration for the Sparx5 IS2 VCAP where all ports and all lookups in IS2 use the same keyset (MAC_ETYPE) for all types of traffic. This means that no matter what frame type is received on any front port it will generate the MAC_ETYPE keyset in the IS VCAP and any rule in the IS2 VCAP that uses this keyset will be matched against the keys in the MAC_ETYPE keyset. The callback interface used by the VCAP API is populated with Sparx5 specific handler functions that takes care of the actual reading and writing to data to the Sparx5 IS2 VCAP instance. A few functions are also added to the VCAP API to support addition of rule fields such as the ingress port mask and the lookup bit. The IS2 VCAP in Sparx5 is really divided in two instances with lookup 0 and 1 in the first instance and lookup 2 and 3 in the second instance. The lookup bit selects lookup 0 or 3 in the respective instance when it is set. Signed-off-by: Steen Hegelund Tested-by: Casper Andersson Reviewed-by: Casper Andersson Signed-off-by: David S. Miller --- .../microchip/sparx5/sparx5_vcap_impl.c | 345 ++++++++++++++++++ .../net/ethernet/microchip/vcap/vcap_api.c | 81 ++++ .../ethernet/microchip/vcap/vcap_api_client.h | 5 + 3 files changed, 431 insertions(+) diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_vcap_impl.c b/drivers/net/ethernet/microchip/sparx5/sparx5_vcap_impl.c index 5ec005e636aa..dbd2c2c4d346 100644 --- a/drivers/net/ethernet/microchip/sparx5/sparx5_vcap_impl.c +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_vcap_impl.c @@ -22,6 +22,54 @@ #define SPARX5_IS2_LOOKUPS 4 +/* IS2 port keyset selection control */ + +/* IS2 non-ethernet traffic type keyset generation */ +enum vcap_is2_port_sel_noneth { + VCAP_IS2_PS_NONETH_MAC_ETYPE, + VCAP_IS2_PS_NONETH_CUSTOM_1, + VCAP_IS2_PS_NONETH_CUSTOM_2, + VCAP_IS2_PS_NONETH_NO_LOOKUP +}; + +/* IS2 IPv4 unicast traffic type keyset generation */ +enum vcap_is2_port_sel_ipv4_uc { + VCAP_IS2_PS_IPV4_UC_MAC_ETYPE, + VCAP_IS2_PS_IPV4_UC_IP4_TCP_UDP_OTHER, + VCAP_IS2_PS_IPV4_UC_IP_7TUPLE, +}; + +/* IS2 IPv4 multicast traffic type keyset generation */ +enum vcap_is2_port_sel_ipv4_mc { + VCAP_IS2_PS_IPV4_MC_MAC_ETYPE, + VCAP_IS2_PS_IPV4_MC_IP4_TCP_UDP_OTHER, + VCAP_IS2_PS_IPV4_MC_IP_7TUPLE, + VCAP_IS2_PS_IPV4_MC_IP4_VID, +}; + +/* IS2 IPv6 unicast traffic type keyset generation */ +enum vcap_is2_port_sel_ipv6_uc { + VCAP_IS2_PS_IPV6_UC_MAC_ETYPE, + VCAP_IS2_PS_IPV6_UC_IP_7TUPLE, + VCAP_IS2_PS_IPV6_UC_IP6_STD, + VCAP_IS2_PS_IPV6_UC_IP4_TCP_UDP_OTHER, +}; + +/* IS2 IPv6 multicast traffic type keyset generation */ +enum vcap_is2_port_sel_ipv6_mc { + VCAP_IS2_PS_IPV6_MC_MAC_ETYPE, + VCAP_IS2_PS_IPV6_MC_IP_7TUPLE, + VCAP_IS2_PS_IPV6_MC_IP6_VID, + VCAP_IS2_PS_IPV6_MC_IP6_STD, + VCAP_IS2_PS_IPV6_MC_IP4_TCP_UDP_OTHER, +}; + +/* IS2 ARP traffic type keyset generation */ +enum vcap_is2_port_sel_arp { + VCAP_IS2_PS_ARP_MAC_ETYPE, + VCAP_IS2_PS_ARP_ARP, +}; + static struct sparx5_vcap_inst { enum vcap_type vtype; /* type of vcap */ int vinst; /* instance number within the same type */ @@ -58,6 +106,296 @@ static struct sparx5_vcap_inst { }, }; +/* Await the super VCAP completion of the current operation */ +static void sparx5_vcap_wait_super_update(struct sparx5 *sparx5) +{ + u32 value; + + read_poll_timeout(spx5_rd, value, + !VCAP_SUPER_CTRL_UPDATE_SHOT_GET(value), 500, 10000, + false, sparx5, VCAP_SUPER_CTRL); +} + +/* Initializing a VCAP address range: only IS2 for now */ +static void _sparx5_vcap_range_init(struct sparx5 *sparx5, + struct vcap_admin *admin, + u32 addr, u32 count) +{ + u32 size = count - 1; + + spx5_wr(VCAP_SUPER_CFG_MV_NUM_POS_SET(0) | + VCAP_SUPER_CFG_MV_SIZE_SET(size), + sparx5, VCAP_SUPER_CFG); + spx5_wr(VCAP_SUPER_CTRL_UPDATE_CMD_SET(VCAP_CMD_INITIALIZE) | + VCAP_SUPER_CTRL_UPDATE_ENTRY_DIS_SET(0) | + VCAP_SUPER_CTRL_UPDATE_ACTION_DIS_SET(0) | + VCAP_SUPER_CTRL_UPDATE_CNT_DIS_SET(0) | + VCAP_SUPER_CTRL_UPDATE_ADDR_SET(addr) | + VCAP_SUPER_CTRL_CLEAR_CACHE_SET(true) | + VCAP_SUPER_CTRL_UPDATE_SHOT_SET(true), + sparx5, VCAP_SUPER_CTRL); + sparx5_vcap_wait_super_update(sparx5); +} + +/* Initializing VCAP rule data area */ +static void sparx5_vcap_block_init(struct sparx5 *sparx5, + struct vcap_admin *admin) +{ + _sparx5_vcap_range_init(sparx5, admin, admin->first_valid_addr, + admin->last_valid_addr - + admin->first_valid_addr); +} + +/* Get the keyset name from the sparx5 VCAP model */ +static const char *sparx5_vcap_keyset_name(struct net_device *ndev, + enum vcap_keyfield_set keyset) +{ + struct sparx5_port *port = netdev_priv(ndev); + + return port->sparx5->vcap_ctrl->stats->keyfield_set_names[keyset]; +} + +/* Check if this is the first lookup of IS2 */ +static bool sparx5_vcap_is2_is_first_chain(struct vcap_rule *rule) +{ + return (rule->vcap_chain_id >= SPARX5_VCAP_CID_IS2_L0 && + rule->vcap_chain_id < SPARX5_VCAP_CID_IS2_L1) || + ((rule->vcap_chain_id >= SPARX5_VCAP_CID_IS2_L2 && + rule->vcap_chain_id < SPARX5_VCAP_CID_IS2_L3)); +} + +/* Set the narrow range ingress port mask on a rule */ +static void sparx5_vcap_add_range_port_mask(struct vcap_rule *rule, + struct net_device *ndev) +{ + struct sparx5_port *port = netdev_priv(ndev); + u32 port_mask; + u32 range; + + range = port->portno / BITS_PER_TYPE(u32); + /* Port bit set to match-any */ + port_mask = ~BIT(port->portno % BITS_PER_TYPE(u32)); + vcap_rule_add_key_u32(rule, VCAP_KF_IF_IGR_PORT_MASK_SEL, 0, 0xf); + vcap_rule_add_key_u32(rule, VCAP_KF_IF_IGR_PORT_MASK_RNG, range, 0xf); + vcap_rule_add_key_u32(rule, VCAP_KF_IF_IGR_PORT_MASK, 0, port_mask); +} + +/* Set the wide range ingress port mask on a rule */ +static void sparx5_vcap_add_wide_port_mask(struct vcap_rule *rule, + struct net_device *ndev) +{ + struct sparx5_port *port = netdev_priv(ndev); + struct vcap_u72_key port_mask; + u32 range; + + /* Port bit set to match-any */ + memset(port_mask.value, 0, sizeof(port_mask.value)); + memset(port_mask.mask, 0xff, sizeof(port_mask.mask)); + range = port->portno / BITS_PER_BYTE; + port_mask.mask[range] = ~BIT(port->portno % BITS_PER_BYTE); + vcap_rule_add_key_u72(rule, VCAP_KF_IF_IGR_PORT_MASK, &port_mask); +} + +/* API callback used for validating a field keyset (check the port keysets) */ +static enum vcap_keyfield_set +sparx5_vcap_validate_keyset(struct net_device *ndev, + struct vcap_admin *admin, + struct vcap_rule *rule, + struct vcap_keyset_list *kslist, + u16 l3_proto) +{ + if (!kslist || kslist->cnt == 0) + return VCAP_KFS_NO_VALUE; + /* for now just return whatever the API suggests */ + return kslist->keysets[0]; +} + +/* API callback used for adding default fields to a rule */ +static void sparx5_vcap_add_default_fields(struct net_device *ndev, + struct vcap_admin *admin, + struct vcap_rule *rule) +{ + const struct vcap_field *field; + + field = vcap_lookup_keyfield(rule, VCAP_KF_IF_IGR_PORT_MASK); + if (field && field->width == SPX5_PORTS) + sparx5_vcap_add_wide_port_mask(rule, ndev); + else if (field && field->width == BITS_PER_TYPE(u32)) + sparx5_vcap_add_range_port_mask(rule, ndev); + else + pr_err("%s:%d: %s: could not add an ingress port mask for: %s\n", + __func__, __LINE__, netdev_name(ndev), + sparx5_vcap_keyset_name(ndev, rule->keyset)); + /* add the lookup bit */ + if (sparx5_vcap_is2_is_first_chain(rule)) + vcap_rule_add_key_bit(rule, VCAP_KF_LOOKUP_FIRST_IS, VCAP_BIT_1); + else + vcap_rule_add_key_bit(rule, VCAP_KF_LOOKUP_FIRST_IS, VCAP_BIT_0); +} + +/* API callback used for erasing the vcap cache area (not the register area) */ +static void sparx5_vcap_cache_erase(struct vcap_admin *admin) +{ + memset(admin->cache.keystream, 0, STREAMSIZE); + memset(admin->cache.maskstream, 0, STREAMSIZE); + memset(admin->cache.actionstream, 0, STREAMSIZE); + memset(&admin->cache.counter, 0, sizeof(admin->cache.counter)); +} + +/* API callback used for writing to the VCAP cache */ +static void sparx5_vcap_cache_write(struct net_device *ndev, + struct vcap_admin *admin, + enum vcap_selection sel, + u32 start, + u32 count) +{ + struct sparx5_port *port = netdev_priv(ndev); + struct sparx5 *sparx5 = port->sparx5; + u32 *keystr, *mskstr, *actstr; + int idx; + + keystr = &admin->cache.keystream[start]; + mskstr = &admin->cache.maskstream[start]; + actstr = &admin->cache.actionstream[start]; + switch (sel) { + case VCAP_SEL_ENTRY: + for (idx = 0; idx < count; ++idx) { + /* Avoid 'match-off' by setting value & mask */ + spx5_wr(keystr[idx] & mskstr[idx], sparx5, + VCAP_SUPER_VCAP_ENTRY_DAT(idx)); + spx5_wr(~mskstr[idx], sparx5, + VCAP_SUPER_VCAP_MASK_DAT(idx)); + } + break; + case VCAP_SEL_ACTION: + for (idx = 0; idx < count; ++idx) + spx5_wr(actstr[idx], sparx5, + VCAP_SUPER_VCAP_ACTION_DAT(idx)); + break; + case VCAP_SEL_ALL: + pr_err("%s:%d: cannot write all streams at once\n", + __func__, __LINE__); + break; + default: + break; + } +} + +/* API callback used for reading from the VCAP into the VCAP cache */ +static void sparx5_vcap_cache_read(struct net_device *ndev, + struct vcap_admin *admin, + enum vcap_selection sel, u32 start, + u32 count) +{ + /* this will be added later */ +} + +/* API callback used for initializing a VCAP address range */ +static void sparx5_vcap_range_init(struct net_device *ndev, + struct vcap_admin *admin, u32 addr, + u32 count) +{ + struct sparx5_port *port = netdev_priv(ndev); + struct sparx5 *sparx5 = port->sparx5; + + _sparx5_vcap_range_init(sparx5, admin, addr, count); +} + +/* API callback used for updating the VCAP cache */ +static void sparx5_vcap_update(struct net_device *ndev, + struct vcap_admin *admin, enum vcap_command cmd, + enum vcap_selection sel, u32 addr) +{ + struct sparx5_port *port = netdev_priv(ndev); + struct sparx5 *sparx5 = port->sparx5; + bool clear; + + clear = (cmd == VCAP_CMD_INITIALIZE); + spx5_wr(VCAP_SUPER_CFG_MV_NUM_POS_SET(0) | + VCAP_SUPER_CFG_MV_SIZE_SET(0), sparx5, VCAP_SUPER_CFG); + spx5_wr(VCAP_SUPER_CTRL_UPDATE_CMD_SET(cmd) | + VCAP_SUPER_CTRL_UPDATE_ENTRY_DIS_SET((VCAP_SEL_ENTRY & sel) == 0) | + VCAP_SUPER_CTRL_UPDATE_ACTION_DIS_SET((VCAP_SEL_ACTION & sel) == 0) | + VCAP_SUPER_CTRL_UPDATE_CNT_DIS_SET((VCAP_SEL_COUNTER & sel) == 0) | + VCAP_SUPER_CTRL_UPDATE_ADDR_SET(addr) | + VCAP_SUPER_CTRL_CLEAR_CACHE_SET(clear) | + VCAP_SUPER_CTRL_UPDATE_SHOT_SET(true), + sparx5, VCAP_SUPER_CTRL); + sparx5_vcap_wait_super_update(sparx5); +} + +/* API callback used for moving a block of rules in the VCAP */ +static void sparx5_vcap_move(struct net_device *ndev, struct vcap_admin *admin, + u32 addr, int offset, int count) +{ + /* this will be added later */ +} + +/* Provide port information via a callback interface */ +static int sparx5_port_info(struct net_device *ndev, enum vcap_type vtype, + int (*pf)(void *out, int arg, const char *fmt, ...), + void *out, int arg) +{ + /* this will be added later */ + return 0; +} + +/* API callback operations: only IS2 is supported for now */ +static struct vcap_operations sparx5_vcap_ops = { + .validate_keyset = sparx5_vcap_validate_keyset, + .add_default_fields = sparx5_vcap_add_default_fields, + .cache_erase = sparx5_vcap_cache_erase, + .cache_write = sparx5_vcap_cache_write, + .cache_read = sparx5_vcap_cache_read, + .init = sparx5_vcap_range_init, + .update = sparx5_vcap_update, + .move = sparx5_vcap_move, + .port_info = sparx5_port_info, +}; + +/* Enable lookups per port and set the keyset generation: only IS2 for now */ +static void sparx5_vcap_port_key_selection(struct sparx5 *sparx5, + struct vcap_admin *admin) +{ + int portno, lookup; + u32 keysel; + + /* enable all 4 lookups on all ports */ + for (portno = 0; portno < SPX5_PORTS; ++portno) + spx5_wr(ANA_ACL_VCAP_S2_CFG_SEC_ENA_SET(0xf), sparx5, + ANA_ACL_VCAP_S2_CFG(portno)); + + /* all traffic types generate the MAC_ETYPE keyset for now in all + * lookups on all ports + */ + keysel = ANA_ACL_VCAP_S2_KEY_SEL_KEY_SEL_ENA_SET(true) | + ANA_ACL_VCAP_S2_KEY_SEL_NON_ETH_KEY_SEL_SET(VCAP_IS2_PS_NONETH_MAC_ETYPE) | + ANA_ACL_VCAP_S2_KEY_SEL_IP4_MC_KEY_SEL_SET(VCAP_IS2_PS_IPV4_MC_MAC_ETYPE) | + ANA_ACL_VCAP_S2_KEY_SEL_IP4_UC_KEY_SEL_SET(VCAP_IS2_PS_IPV4_UC_MAC_ETYPE) | + ANA_ACL_VCAP_S2_KEY_SEL_IP6_MC_KEY_SEL_SET(VCAP_IS2_PS_IPV6_MC_MAC_ETYPE) | + ANA_ACL_VCAP_S2_KEY_SEL_IP6_UC_KEY_SEL_SET(VCAP_IS2_PS_IPV6_UC_MAC_ETYPE) | + ANA_ACL_VCAP_S2_KEY_SEL_ARP_KEY_SEL_SET(VCAP_IS2_PS_ARP_MAC_ETYPE); + for (lookup = 0; lookup < admin->lookups; ++lookup) { + for (portno = 0; portno < SPX5_PORTS; ++portno) { + spx5_wr(keysel, sparx5, + ANA_ACL_VCAP_S2_KEY_SEL(portno, lookup)); + } + } +} + +/* Disable lookups per port and set the keyset generation: only IS2 for now */ +static void sparx5_vcap_port_key_deselection(struct sparx5 *sparx5, + struct vcap_admin *admin) +{ + int portno; + + for (portno = 0; portno < SPX5_PORTS; ++portno) + spx5_rmw(ANA_ACL_VCAP_S2_CFG_SEC_ENA_SET(0), + ANA_ACL_VCAP_S2_CFG_SEC_ENA, + sparx5, + ANA_ACL_VCAP_S2_CFG(portno)); +} + static void sparx5_vcap_admin_free(struct vcap_admin *admin) { if (!admin) @@ -138,6 +476,7 @@ int sparx5_vcap_init(struct sparx5 *sparx5) * - Lists of rules * - Address information * - Initialize VCAP blocks + * - Configure port keysets */ ctrl = kzalloc(sizeof(*ctrl), GFP_KERNEL); if (!ctrl) @@ -147,6 +486,8 @@ int sparx5_vcap_init(struct sparx5 *sparx5) /* select the sparx5 VCAP model */ ctrl->vcaps = sparx5_vcaps; ctrl->stats = &sparx5_vcap_stats; + /* Setup callbacks to allow the API to use the VCAP HW */ + ctrl->ops = &sparx5_vcap_ops; INIT_LIST_HEAD(&ctrl->list); for (idx = 0; idx < ARRAY_SIZE(sparx5_vcap_inst_cfg); ++idx) { @@ -159,6 +500,9 @@ int sparx5_vcap_init(struct sparx5 *sparx5) return err; } sparx5_vcap_block_alloc(sparx5, admin, cfg); + sparx5_vcap_block_init(sparx5, admin); + if (cfg->vinst == 0) + sparx5_vcap_port_key_selection(sparx5, admin); list_add_tail(&admin->list, &ctrl->list); } @@ -174,6 +518,7 @@ void sparx5_vcap_destroy(struct sparx5 *sparx5) return; list_for_each_entry_safe(admin, admin_next, &ctrl->list, list) { + sparx5_vcap_port_key_deselection(sparx5, admin); list_del(&admin->list); sparx5_vcap_admin_free(admin); } diff --git a/drivers/net/ethernet/microchip/vcap/vcap_api.c b/drivers/net/ethernet/microchip/vcap/vcap_api.c index 3eb8aa237c8e..b2c0ddfc0a36 100644 --- a/drivers/net/ethernet/microchip/vcap/vcap_api.c +++ b/drivers/net/ethernet/microchip/vcap/vcap_api.c @@ -21,6 +21,17 @@ struct vcap_rule_internal { u32 addr; /* address in the VCAP at insertion */ }; +/* Return the list of keyfields for the keyset */ +static const struct vcap_field *vcap_keyfields(struct vcap_control *vctrl, + enum vcap_type vt, + enum vcap_keyfield_set keyset) +{ + /* Check that the keyset exists in the vcap keyset list */ + if (keyset >= vctrl->vcaps[vt].keyfield_set_size) + return NULL; + return vctrl->vcaps[vt].keyfield_set_map[keyset]; +} + /* Update the keyset for the rule */ int vcap_set_rule_set_keyset(struct vcap_rule *rule, enum vcap_keyfield_set keyset) @@ -227,6 +238,24 @@ int vcap_del_rule(struct vcap_control *vctrl, struct net_device *ndev, u32 id) } EXPORT_SYMBOL_GPL(vcap_del_rule); +/* Find information on a key field in a rule */ +const struct vcap_field *vcap_lookup_keyfield(struct vcap_rule *rule, + enum vcap_key_field key) +{ + struct vcap_rule_internal *ri = (struct vcap_rule_internal *)rule; + enum vcap_keyfield_set keyset = rule->keyset; + enum vcap_type vt = ri->admin->vtype; + const struct vcap_field *fields; + + if (keyset == VCAP_KFS_NO_VALUE) + return NULL; + fields = vcap_keyfields(ri->vctrl, vt, keyset); + if (!fields) + return NULL; + return &fields[key]; +} +EXPORT_SYMBOL_GPL(vcap_lookup_keyfield); + static void vcap_copy_from_client_keyfield(struct vcap_rule *rule, struct vcap_client_keyfield *field, struct vcap_client_keyfield_data *data) @@ -253,6 +282,47 @@ static int vcap_rule_add_key(struct vcap_rule *rule, return 0; } +static void vcap_rule_set_key_bitsize(struct vcap_u1_key *u1, enum vcap_bit val) +{ + switch (val) { + case VCAP_BIT_0: + u1->value = 0; + u1->mask = 1; + break; + case VCAP_BIT_1: + u1->value = 1; + u1->mask = 1; + break; + case VCAP_BIT_ANY: + u1->value = 0; + u1->mask = 0; + break; + } +} + +/* Add a bit key with value and mask to the rule */ +int vcap_rule_add_key_bit(struct vcap_rule *rule, enum vcap_key_field key, + enum vcap_bit val) +{ + struct vcap_client_keyfield_data data; + + vcap_rule_set_key_bitsize(&data.u1, val); + return vcap_rule_add_key(rule, key, VCAP_FIELD_BIT, &data); +} +EXPORT_SYMBOL_GPL(vcap_rule_add_key_bit); + +/* Add a 32 bit key field with value and mask to the rule */ +int vcap_rule_add_key_u32(struct vcap_rule *rule, enum vcap_key_field key, + u32 value, u32 mask) +{ + struct vcap_client_keyfield_data data; + + data.u32.value = value; + data.u32.mask = mask; + return vcap_rule_add_key(rule, key, VCAP_FIELD_U32, &data); +} +EXPORT_SYMBOL_GPL(vcap_rule_add_key_u32); + /* Add a 48 bit key with value and mask to the rule */ int vcap_rule_add_key_u48(struct vcap_rule *rule, enum vcap_key_field key, struct vcap_u48_key *fieldval) @@ -264,6 +334,17 @@ int vcap_rule_add_key_u48(struct vcap_rule *rule, enum vcap_key_field key, } EXPORT_SYMBOL_GPL(vcap_rule_add_key_u48); +/* Add a 72 bit key with value and mask to the rule */ +int vcap_rule_add_key_u72(struct vcap_rule *rule, enum vcap_key_field key, + struct vcap_u72_key *fieldval) +{ + struct vcap_client_keyfield_data data; + + memcpy(&data.u72, fieldval, sizeof(data.u72)); + return vcap_rule_add_key(rule, key, VCAP_FIELD_U72, &data); +} +EXPORT_SYMBOL_GPL(vcap_rule_add_key_u72); + static void vcap_copy_from_client_actionfield(struct vcap_rule *rule, struct vcap_client_actionfield *field, struct vcap_client_actionfield_data *data) diff --git a/drivers/net/ethernet/microchip/vcap/vcap_api_client.h b/drivers/net/ethernet/microchip/vcap/vcap_api_client.h index 78ac4628fc9f..68248b1b8b01 100644 --- a/drivers/net/ethernet/microchip/vcap/vcap_api_client.h +++ b/drivers/net/ethernet/microchip/vcap/vcap_api_client.h @@ -174,6 +174,8 @@ int vcap_rule_add_key_u32(struct vcap_rule *rule, enum vcap_key_field key, u32 value, u32 mask); int vcap_rule_add_key_u48(struct vcap_rule *rule, enum vcap_key_field key, struct vcap_u48_key *fieldval); +int vcap_rule_add_key_u72(struct vcap_rule *rule, enum vcap_key_field key, + struct vcap_u72_key *fieldval); int vcap_rule_add_action_bit(struct vcap_rule *rule, enum vcap_action_field action, enum vcap_bit val); int vcap_rule_add_action_u32(struct vcap_rule *rule, @@ -182,6 +184,9 @@ int vcap_rule_add_action_u32(struct vcap_rule *rule, /* VCAP lookup operations */ /* Lookup a vcap instance using chain id */ struct vcap_admin *vcap_find_admin(struct vcap_control *vctrl, int cid); +/* Find information on a key field in a rule */ +const struct vcap_field *vcap_lookup_keyfield(struct vcap_rule *rule, + enum vcap_key_field key); /* Find a rule id with a provided cookie */ int vcap_lookup_rule_by_cookie(struct vcap_control *vctrl, u64 cookie); From 8e10490b006477c42874d43a2ab4d4c6d51ccb63 Mon Sep 17 00:00:00 2001 From: Steen Hegelund Date: Thu, 20 Oct 2022 15:09:01 +0200 Subject: [PATCH 0280/2002] net: microchip: sparx5: Adding basic rule management in VCAP API This provides most of the rule handling needed to add a new rule to a VCAP. To add a rule a client must follow these steps: 1) Allocate a new rule (provide an id or get one automatically assigned) 2) Add keys to the rule 3) Add actions to the rule 4) Optionally set a keyset on the rule 5) Optionally set an actionset on the rule 6) Validate the rule (this will add keyset and actionset if not specified in the previous steps) 7) Add the rule (if the validation was successful) 8) Free the rule instance (a copy has been added to the VCAP) The validation step will fail if there are no keysets with the requested keys, or there are no actionsets with the requested actions. The validation will also fail if the keyset is not configured for the port for the requested protocol). Signed-off-by: Steen Hegelund Tested-by: Casper Andersson Reviewed-by: Casper Andersson Signed-off-by: David S. Miller --- .../microchip/sparx5/sparx5_vcap_impl.c | 1 + .../net/ethernet/microchip/vcap/vcap_api.c | 320 +++++++++++++++++- .../ethernet/microchip/vcap/vcap_api_client.h | 3 + 3 files changed, 316 insertions(+), 8 deletions(-) diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_vcap_impl.c b/drivers/net/ethernet/microchip/sparx5/sparx5_vcap_impl.c index dbd2c2c4d346..50153264179e 100644 --- a/drivers/net/ethernet/microchip/sparx5/sparx5_vcap_impl.c +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_vcap_impl.c @@ -519,6 +519,7 @@ void sparx5_vcap_destroy(struct sparx5 *sparx5) list_for_each_entry_safe(admin, admin_next, &ctrl->list, list) { sparx5_vcap_port_key_deselection(sparx5, admin); + vcap_del_rules(ctrl, admin); list_del(&admin->list); sparx5_vcap_admin_free(admin); } diff --git a/drivers/net/ethernet/microchip/vcap/vcap_api.c b/drivers/net/ethernet/microchip/vcap/vcap_api.c index b2c0ddfc0a36..ee49c0307e7b 100644 --- a/drivers/net/ethernet/microchip/vcap/vcap_api.c +++ b/drivers/net/ethernet/microchip/vcap/vcap_api.c @@ -18,9 +18,22 @@ struct vcap_rule_internal { struct vcap_admin *admin; /* vcap hw instance */ struct net_device *ndev; /* the interface that the rule applies to */ struct vcap_control *vctrl; /* the client control */ + u32 sort_key; /* defines the position in the VCAP */ + int keyset_sw; /* subwords in a keyset */ + int actionset_sw; /* subwords in an actionset */ + int keyset_sw_regs; /* registers in a subword in an keyset */ + int actionset_sw_regs; /* registers in a subword in an actionset */ + int size; /* the size of the rule: max(entry, action) */ u32 addr; /* address in the VCAP at insertion */ }; +/* Moving a rule in the VCAP address space */ +struct vcap_rule_move { + int addr; /* address to move */ + int offset; /* change in address */ + int count; /* blocksize of addresses to move */ +}; + /* Return the list of keyfields for the keyset */ static const struct vcap_field *vcap_keyfields(struct vcap_control *vctrl, enum vcap_type vt, @@ -32,12 +45,82 @@ static const struct vcap_field *vcap_keyfields(struct vcap_control *vctrl, return vctrl->vcaps[vt].keyfield_set_map[keyset]; } +/* Return the keyset information for the keyset */ +static const struct vcap_set *vcap_keyfieldset(struct vcap_control *vctrl, + enum vcap_type vt, + enum vcap_keyfield_set keyset) +{ + const struct vcap_set *kset; + + /* Check that the keyset exists in the vcap keyset list */ + if (keyset >= vctrl->vcaps[vt].keyfield_set_size) + return NULL; + kset = &vctrl->vcaps[vt].keyfield_set[keyset]; + if (kset->sw_per_item == 0 || kset->sw_per_item > vctrl->vcaps[vt].sw_count) + return NULL; + return kset; +} + +static const struct vcap_set * +vcap_actionfieldset(struct vcap_control *vctrl, + enum vcap_type vt, enum vcap_actionfield_set actionset) +{ + const struct vcap_set *aset; + + /* Check that the actionset exists in the vcap actionset list */ + if (actionset >= vctrl->vcaps[vt].actionfield_set_size) + return NULL; + aset = &vctrl->vcaps[vt].actionfield_set[actionset]; + if (aset->sw_per_item == 0 || aset->sw_per_item > vctrl->vcaps[vt].sw_count) + return NULL; + return aset; +} + +static int vcap_encode_rule(struct vcap_rule_internal *ri) +{ + /* Encoding of keyset and actionsets will be added later */ + return 0; +} + +static int vcap_api_check(struct vcap_control *ctrl) +{ + if (!ctrl) { + pr_err("%s:%d: vcap control is missing\n", __func__, __LINE__); + return -EINVAL; + } + if (!ctrl->ops || !ctrl->ops->validate_keyset || + !ctrl->ops->add_default_fields || !ctrl->ops->cache_erase || + !ctrl->ops->cache_write || !ctrl->ops->cache_read || + !ctrl->ops->init || !ctrl->ops->update || !ctrl->ops->move || + !ctrl->ops->port_info) { + pr_err("%s:%d: client operations are missing\n", + __func__, __LINE__); + return -ENOENT; + } + return 0; +} + +static void vcap_erase_cache(struct vcap_rule_internal *ri) +{ + ri->vctrl->ops->cache_erase(ri->admin); +} + /* Update the keyset for the rule */ int vcap_set_rule_set_keyset(struct vcap_rule *rule, enum vcap_keyfield_set keyset) { - /* This will be expanded with more information later */ - rule->keyset = keyset; + struct vcap_rule_internal *ri = to_intrule(rule); + const struct vcap_set *kset; + int sw_width; + + kset = vcap_keyfieldset(ri->vctrl, ri->admin->vtype, keyset); + /* Check that the keyset is valid */ + if (!kset) + return -EINVAL; + ri->keyset_sw = kset->sw_per_item; + sw_width = ri->vctrl->vcaps[ri->admin->vtype].sw_width; + ri->keyset_sw_regs = DIV_ROUND_UP(sw_width, 32); + ri->data.keyset = keyset; return 0; } EXPORT_SYMBOL_GPL(vcap_set_rule_set_keyset); @@ -46,8 +129,18 @@ EXPORT_SYMBOL_GPL(vcap_set_rule_set_keyset); int vcap_set_rule_set_actionset(struct vcap_rule *rule, enum vcap_actionfield_set actionset) { - /* This will be expanded with more information later */ - rule->actionset = actionset; + struct vcap_rule_internal *ri = to_intrule(rule); + const struct vcap_set *aset; + int act_width; + + aset = vcap_actionfieldset(ri->vctrl, ri->admin->vtype, actionset); + /* Check that the actionset is valid */ + if (!aset) + return -EINVAL; + ri->actionset_sw = aset->sw_per_item; + act_width = ri->vctrl->vcaps[ri->admin->vtype].act_width; + ri->actionset_sw_regs = DIV_ROUND_UP(act_width, 32); + ri->data.actionset = actionset; return 0; } EXPORT_SYMBOL_GPL(vcap_set_rule_set_actionset); @@ -82,11 +175,59 @@ int vcap_lookup_rule_by_cookie(struct vcap_control *vctrl, u64 cookie) } EXPORT_SYMBOL_GPL(vcap_lookup_rule_by_cookie); +/* Make a shallow copy of the rule without the fields */ +static struct vcap_rule_internal *vcap_dup_rule(struct vcap_rule_internal *ri) +{ + struct vcap_rule_internal *duprule; + + /* Allocate the client part */ + duprule = kzalloc(sizeof(*duprule), GFP_KERNEL); + if (!duprule) + return ERR_PTR(-ENOMEM); + *duprule = *ri; + /* Not inserted in the VCAP */ + INIT_LIST_HEAD(&duprule->list); + /* No elements in these lists */ + INIT_LIST_HEAD(&duprule->data.keyfields); + INIT_LIST_HEAD(&duprule->data.actionfields); + return duprule; +} + +/* Write VCAP cache content to the VCAP HW instance */ +static int vcap_write_rule(struct vcap_rule_internal *ri) +{ + struct vcap_admin *admin = ri->admin; + int sw_idx, ent_idx = 0, act_idx = 0; + u32 addr = ri->addr; + + if (!ri->size || !ri->keyset_sw_regs || !ri->actionset_sw_regs) { + pr_err("%s:%d: rule is empty\n", __func__, __LINE__); + return -EINVAL; + } + /* Use the values in the streams to write the VCAP cache */ + for (sw_idx = 0; sw_idx < ri->size; sw_idx++, addr++) { + ri->vctrl->ops->cache_write(ri->ndev, admin, + VCAP_SEL_ENTRY, ent_idx, + ri->keyset_sw_regs); + ri->vctrl->ops->cache_write(ri->ndev, admin, + VCAP_SEL_ACTION, act_idx, + ri->actionset_sw_regs); + ri->vctrl->ops->update(ri->ndev, admin, VCAP_CMD_WRITE, + VCAP_SEL_ALL, addr); + ent_idx += ri->keyset_sw_regs; + act_idx += ri->actionset_sw_regs; + } + return 0; +} + /* Lookup a vcap instance using chain id */ struct vcap_admin *vcap_find_admin(struct vcap_control *vctrl, int cid) { struct vcap_admin *admin; + if (vcap_api_check(vctrl)) + return NULL; + list_for_each_entry(admin, &vctrl->list, list) { if (cid >= admin->first_cid && cid <= admin->last_cid) return admin; @@ -95,12 +236,62 @@ struct vcap_admin *vcap_find_admin(struct vcap_control *vctrl, int cid) } EXPORT_SYMBOL_GPL(vcap_find_admin); +/* Check if there is room for a new rule */ +static int vcap_rule_space(struct vcap_admin *admin, int size) +{ + if (admin->last_used_addr - size < admin->first_valid_addr) { + pr_err("%s:%d: No room for rule size: %u, %u\n", + __func__, __LINE__, size, admin->first_valid_addr); + return -ENOSPC; + } + return 0; +} + +/* Add the keyset typefield to the list of rule keyfields */ +static int vcap_add_type_keyfield(struct vcap_rule *rule) +{ + struct vcap_rule_internal *ri = to_intrule(rule); + enum vcap_keyfield_set keyset = rule->keyset; + enum vcap_type vt = ri->admin->vtype; + const struct vcap_field *fields; + const struct vcap_set *kset; + int ret = -EINVAL; + + kset = vcap_keyfieldset(ri->vctrl, vt, keyset); + if (!kset) + return ret; + if (kset->type_id == (u8)-1) /* No type field is needed */ + return 0; + + fields = vcap_keyfields(ri->vctrl, vt, keyset); + if (!fields) + return -EINVAL; + if (fields[VCAP_KF_TYPE].width > 1) { + ret = vcap_rule_add_key_u32(rule, VCAP_KF_TYPE, + kset->type_id, 0xff); + } else { + if (kset->type_id) + ret = vcap_rule_add_key_bit(rule, VCAP_KF_TYPE, + VCAP_BIT_1); + else + ret = vcap_rule_add_key_bit(rule, VCAP_KF_TYPE, + VCAP_BIT_0); + } + return 0; +} + /* Validate a rule with respect to available port keys */ int vcap_val_rule(struct vcap_rule *rule, u16 l3_proto) { struct vcap_rule_internal *ri = to_intrule(rule); + enum vcap_keyfield_set keysets[10]; + struct vcap_keyset_list kslist; + int ret; /* This validation will be much expanded later */ + ret = vcap_api_check(ri->vctrl); + if (ret) + return ret; if (!ri->admin) { ri->data.exterr = VCAP_ERR_NO_ADMIN; return -EINVAL; @@ -113,14 +304,41 @@ int vcap_val_rule(struct vcap_rule *rule, u16 l3_proto) ri->data.exterr = VCAP_ERR_NO_KEYSET_MATCH; return -EINVAL; } + /* prepare for keyset validation */ + keysets[0] = ri->data.keyset; + kslist.keysets = keysets; + kslist.cnt = 1; + /* Pick a keyset that is supported in the port lookups */ + ret = ri->vctrl->ops->validate_keyset(ri->ndev, ri->admin, rule, &kslist, + l3_proto); + if (ret < 0) { + pr_err("%s:%d: keyset validation failed: %d\n", + __func__, __LINE__, ret); + ri->data.exterr = VCAP_ERR_NO_PORT_KEYSET_MATCH; + return ret; + } if (ri->data.actionset == VCAP_AFS_NO_VALUE) { ri->data.exterr = VCAP_ERR_NO_ACTIONSET_MATCH; return -EINVAL; } - return 0; + vcap_add_type_keyfield(rule); + /* Add default fields to this rule */ + ri->vctrl->ops->add_default_fields(ri->ndev, ri->admin, rule); + + /* Rule size is the maximum of the entry and action subword count */ + ri->size = max(ri->keyset_sw, ri->actionset_sw); + + /* Finally check if there is room for the rule in the VCAP */ + return vcap_rule_space(ri->admin, ri->size); } EXPORT_SYMBOL_GPL(vcap_val_rule); +/* calculate the address of the next rule after this (lower address and prio) */ +static u32 vcap_next_rule_addr(u32 addr, struct vcap_rule_internal *ri) +{ + return ((addr - ri->size) / ri->size) * ri->size; +} + /* Assign a unique rule id and autogenerate one if id == 0 */ static u32 vcap_set_rule_id(struct vcap_rule_internal *ri) { @@ -141,11 +359,60 @@ static u32 vcap_set_rule_id(struct vcap_rule_internal *ri) return ri->data.id; } +static int vcap_insert_rule(struct vcap_rule_internal *ri, + struct vcap_rule_move *move) +{ + struct vcap_admin *admin = ri->admin; + struct vcap_rule_internal *duprule; + + /* Only support appending rules for now */ + ri->addr = vcap_next_rule_addr(admin->last_used_addr, ri); + admin->last_used_addr = ri->addr; + /* Add a shallow copy of the rule to the VCAP list */ + duprule = vcap_dup_rule(ri); + if (IS_ERR(duprule)) + return PTR_ERR(duprule); + list_add_tail(&duprule->list, &admin->rules); + return 0; +} + +static void vcap_move_rules(struct vcap_rule_internal *ri, + struct vcap_rule_move *move) +{ + ri->vctrl->ops->move(ri->ndev, ri->admin, move->addr, + move->offset, move->count); +} + /* Encode and write a validated rule to the VCAP */ int vcap_add_rule(struct vcap_rule *rule) { - /* This will later handling the encode and writing of the rule */ - return 0; + struct vcap_rule_internal *ri = to_intrule(rule); + struct vcap_rule_move move = {0}; + int ret; + + ret = vcap_api_check(ri->vctrl); + if (ret) + return ret; + /* Insert the new rule in the list of vcap rules */ + ret = vcap_insert_rule(ri, &move); + if (ret < 0) { + pr_err("%s:%d: could not insert rule in vcap list: %d\n", + __func__, __LINE__, ret); + goto out; + } + if (move.count > 0) + vcap_move_rules(ri, &move); + ret = vcap_encode_rule(ri); + if (ret) { + pr_err("%s:%d: rule encoding error: %d\n", __func__, __LINE__, ret); + goto out; + } + + ret = vcap_write_rule(ri); + if (ret) + pr_err("%s:%d: rule write error: %d\n", __func__, __LINE__, ret); +out: + return ret; } EXPORT_SYMBOL_GPL(vcap_add_rule); @@ -157,6 +424,7 @@ struct vcap_rule *vcap_alloc_rule(struct vcap_control *vctrl, { struct vcap_rule_internal *ri; struct vcap_admin *admin; + int maxsize; if (!ndev) return ERR_PTR(-ENODEV); @@ -164,6 +432,16 @@ struct vcap_rule *vcap_alloc_rule(struct vcap_control *vctrl, admin = vcap_find_admin(vctrl, vcap_chain_id); if (!admin) return ERR_PTR(-ENOENT); + /* Sanity check that this VCAP is supported on this platform */ + if (vctrl->vcaps[admin->vtype].rows == 0) + return ERR_PTR(-EINVAL); + /* Check if a rule with this id already exists */ + if (vcap_lookup_rule(vctrl, id)) + return ERR_PTR(-EEXIST); + /* Check if there is room for the rule in the block(s) of the VCAP */ + maxsize = vctrl->vcaps[admin->vtype].sw_count; /* worst case rule size */ + if (vcap_rule_space(admin, maxsize)) + return ERR_PTR(-ENOSPC); /* Create a container for the rule and return it */ ri = kzalloc(sizeof(*ri), GFP_KERNEL); if (!ri) @@ -182,6 +460,7 @@ struct vcap_rule *vcap_alloc_rule(struct vcap_control *vctrl, ri->vctrl = vctrl; /* refer to the client */ if (vcap_set_rule_id(ri) == 0) goto out_free; + vcap_erase_cache(ri); return (struct vcap_rule *)ri; out_free: @@ -216,16 +495,23 @@ int vcap_del_rule(struct vcap_control *vctrl, struct net_device *ndev, u32 id) { struct vcap_rule_internal *ri, *elem; struct vcap_admin *admin; + int err; /* This will later also handle rule moving */ if (!ndev) return -ENODEV; + err = vcap_api_check(vctrl); + if (err) + return err; /* Look for the rule id in all vcaps */ ri = vcap_lookup_rule(vctrl, id); if (!ri) return -EINVAL; admin = ri->admin; list_del(&ri->list); + + /* delete the rule in the cache */ + vctrl->ops->init(ndev, admin, ri->addr, ri->size); if (list_empty(&admin->rules)) { admin->last_used_addr = admin->last_valid_addr; } else { @@ -238,11 +524,29 @@ int vcap_del_rule(struct vcap_control *vctrl, struct net_device *ndev, u32 id) } EXPORT_SYMBOL_GPL(vcap_del_rule); +/* Delete all rules in the VCAP instance */ +int vcap_del_rules(struct vcap_control *vctrl, struct vcap_admin *admin) +{ + struct vcap_rule_internal *ri, *next_ri; + int ret = vcap_api_check(vctrl); + + if (ret) + return ret; + list_for_each_entry_safe(ri, next_ri, &admin->rules, list) { + vctrl->ops->init(ri->ndev, admin, ri->addr, ri->size); + list_del(&ri->list); + kfree(ri); + } + admin->last_used_addr = admin->last_valid_addr; + return 0; +} +EXPORT_SYMBOL_GPL(vcap_del_rules); + /* Find information on a key field in a rule */ const struct vcap_field *vcap_lookup_keyfield(struct vcap_rule *rule, enum vcap_key_field key) { - struct vcap_rule_internal *ri = (struct vcap_rule_internal *)rule; + struct vcap_rule_internal *ri = to_intrule(rule); enum vcap_keyfield_set keyset = rule->keyset; enum vcap_type vt = ri->admin->vtype; const struct vcap_field *fields; diff --git a/drivers/net/ethernet/microchip/vcap/vcap_api_client.h b/drivers/net/ethernet/microchip/vcap/vcap_api_client.h index 68248b1b8b01..5df6808679ff 100644 --- a/drivers/net/ethernet/microchip/vcap/vcap_api_client.h +++ b/drivers/net/ethernet/microchip/vcap/vcap_api_client.h @@ -196,4 +196,7 @@ void vcap_netbytes_copy(u8 *dst, u8 *src, int count); /* Convert validation error code into tc extact error message */ void vcap_set_tc_exterr(struct flow_cls_offload *fco, struct vcap_rule *vrule); +/* Cleanup a VCAP instance */ +int vcap_del_rules(struct vcap_control *vctrl, struct vcap_admin *admin); + #endif /* __VCAP_API_CLIENT__ */ From 683e05c03275d79e603d7e0211d7aed410b0e65b Mon Sep 17 00:00:00 2001 From: Steen Hegelund Date: Thu, 20 Oct 2022 15:09:02 +0200 Subject: [PATCH 0281/2002] net: microchip: sparx5: Writing rules to the IS2 VCAP This adds rule encoding functionality to the VCAP API. A rule consists of keys and actions in separate cache sections. The maximum size of the keyset or actionset determines the size of the rule. The VCAP hardware need to be able to distinguish different rule sizes from each other, and for that purpose some extra typegroup bits are added to the rule when it is encoded. The API provides a bit stream iterator that allows highlevel encoding functionality to add key and action value bits independent of typegroup bits. This is handled by letting the concrete VCAP model provide the typegroup table for the different rule sizes. After the key and action values have been added to the encoding bit streams the typegroup bits are set to their correct values just before the rule is written to the VCAP hardware. The key and action offsets provided in the VCAP model are the offset before adding the typegroup bits. Signed-off-by: Steen Hegelund Tested-by: Casper Andersson Reviewed-by: Casper Andersson Signed-off-by: David S. Miller --- .../net/ethernet/microchip/vcap/vcap_api.c | 426 +++++++++++++++++- 1 file changed, 425 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/microchip/vcap/vcap_api.c b/drivers/net/ethernet/microchip/vcap/vcap_api.c index ee49c0307e7b..893f649bcf91 100644 --- a/drivers/net/ethernet/microchip/vcap/vcap_api.c +++ b/drivers/net/ethernet/microchip/vcap/vcap_api.c @@ -34,6 +34,141 @@ struct vcap_rule_move { int count; /* blocksize of addresses to move */ }; +/* Bit iterator for the VCAP cache streams */ +struct vcap_stream_iter { + u32 offset; /* bit offset from the stream start */ + u32 sw_width; /* subword width in bits */ + u32 regs_per_sw; /* registers per subword */ + u32 reg_idx; /* current register index */ + u32 reg_bitpos; /* bit offset in current register */ + const struct vcap_typegroup *tg; /* current typegroup */ +}; + +static void vcap_iter_set(struct vcap_stream_iter *itr, int sw_width, + const struct vcap_typegroup *tg, u32 offset) +{ + memset(itr, 0, sizeof(*itr)); + itr->offset = offset; + itr->sw_width = sw_width; + itr->regs_per_sw = DIV_ROUND_UP(sw_width, 32); + itr->tg = tg; +} + +static void vcap_iter_skip_tg(struct vcap_stream_iter *itr) +{ + /* Compensate the field offset for preceding typegroups. + * A typegroup table ends with an all-zero terminator. + */ + while (itr->tg->width && itr->offset >= itr->tg->offset) { + itr->offset += itr->tg->width; + itr->tg++; /* next typegroup */ + } +} + +static void vcap_iter_update(struct vcap_stream_iter *itr) +{ + int sw_idx, sw_bitpos; + + /* Calculate the subword index and bitposition for current bit */ + sw_idx = itr->offset / itr->sw_width; + sw_bitpos = itr->offset % itr->sw_width; + /* Calculate the register index and bitposition for current bit */ + itr->reg_idx = (sw_idx * itr->regs_per_sw) + (sw_bitpos / 32); + itr->reg_bitpos = sw_bitpos % 32; +} + +static void vcap_iter_init(struct vcap_stream_iter *itr, int sw_width, + const struct vcap_typegroup *tg, u32 offset) +{ + vcap_iter_set(itr, sw_width, tg, offset); + vcap_iter_skip_tg(itr); + vcap_iter_update(itr); +} + +static void vcap_iter_next(struct vcap_stream_iter *itr) +{ + itr->offset++; + vcap_iter_skip_tg(itr); + vcap_iter_update(itr); +} + +static void vcap_set_bit(u32 *stream, struct vcap_stream_iter *itr, bool value) +{ + u32 mask = BIT(itr->reg_bitpos); + u32 *p = &stream[itr->reg_idx]; + + if (value) + *p |= mask; + else + *p &= ~mask; +} + +static void vcap_encode_bit(u32 *stream, struct vcap_stream_iter *itr, bool val) +{ + /* When intersected by a type group field, stream the type group bits + * before continuing with the value bit + */ + while (itr->tg->width && + itr->offset >= itr->tg->offset && + itr->offset < itr->tg->offset + itr->tg->width) { + int tg_bitpos = itr->tg->offset - itr->offset; + + vcap_set_bit(stream, itr, (itr->tg->value >> tg_bitpos) & 0x1); + itr->offset++; + vcap_iter_update(itr); + } + vcap_set_bit(stream, itr, val); +} + +static void vcap_encode_field(u32 *stream, struct vcap_stream_iter *itr, + int width, const u8 *value) +{ + int idx; + + /* Loop over the field value bits and add the value bits one by one to + * the output stream. + */ + for (idx = 0; idx < width; idx++) { + u8 bidx = idx & GENMASK(2, 0); + + /* Encode one field value bit */ + vcap_encode_bit(stream, itr, (value[idx / 8] >> bidx) & 0x1); + vcap_iter_next(itr); + } +} + +static void vcap_encode_typegroups(u32 *stream, int sw_width, + const struct vcap_typegroup *tg, + bool mask) +{ + struct vcap_stream_iter iter; + int idx; + + /* Mask bits must be set to zeros (inverted later when writing to the + * mask cache register), so that the mask typegroup bits consist of + * match-1 or match-0, or both + */ + vcap_iter_set(&iter, sw_width, tg, 0); + while (iter.tg->width) { + /* Set position to current typegroup bit */ + iter.offset = iter.tg->offset; + vcap_iter_update(&iter); + for (idx = 0; idx < iter.tg->width; idx++) { + /* Iterate over current typegroup bits. Mask typegroup + * bits are always set + */ + if (mask) + vcap_set_bit(stream, &iter, 0x1); + else + vcap_set_bit(stream, &iter, + (iter.tg->value >> idx) & 0x1); + iter.offset++; + vcap_iter_update(&iter); + } + iter.tg++; /* next typegroup */ + } +} + /* Return the list of keyfields for the keyset */ static const struct vcap_field *vcap_keyfields(struct vcap_control *vctrl, enum vcap_type vt, @@ -61,6 +196,158 @@ static const struct vcap_set *vcap_keyfieldset(struct vcap_control *vctrl, return kset; } +/* Return the typegroup table for the matching keyset (using subword size) */ +static const struct vcap_typegroup * +vcap_keyfield_typegroup(struct vcap_control *vctrl, + enum vcap_type vt, enum vcap_keyfield_set keyset) +{ + const struct vcap_set *kset = vcap_keyfieldset(vctrl, vt, keyset); + + /* Check that the keyset is valid */ + if (!kset) + return NULL; + return vctrl->vcaps[vt].keyfield_set_typegroups[kset->sw_per_item]; +} + +/* Return the number of keyfields in the keyset */ +static int vcap_keyfield_count(struct vcap_control *vctrl, + enum vcap_type vt, enum vcap_keyfield_set keyset) +{ + /* Check that the keyset exists in the vcap keyset list */ + if (keyset >= vctrl->vcaps[vt].keyfield_set_size) + return 0; + return vctrl->vcaps[vt].keyfield_set_map_size[keyset]; +} + +static void vcap_encode_keyfield(struct vcap_rule_internal *ri, + const struct vcap_client_keyfield *kf, + const struct vcap_field *rf, + const struct vcap_typegroup *tgt) +{ + int sw_width = ri->vctrl->vcaps[ri->admin->vtype].sw_width; + struct vcap_cache_data *cache = &ri->admin->cache; + struct vcap_stream_iter iter; + const u8 *value, *mask; + + /* Encode the fields for the key and the mask in their respective + * streams, respecting the subword width. + */ + switch (kf->ctrl.type) { + case VCAP_FIELD_BIT: + value = &kf->data.u1.value; + mask = &kf->data.u1.mask; + break; + case VCAP_FIELD_U32: + value = (const u8 *)&kf->data.u32.value; + mask = (const u8 *)&kf->data.u32.mask; + break; + case VCAP_FIELD_U48: + value = kf->data.u48.value; + mask = kf->data.u48.mask; + break; + case VCAP_FIELD_U56: + value = kf->data.u56.value; + mask = kf->data.u56.mask; + break; + case VCAP_FIELD_U64: + value = kf->data.u64.value; + mask = kf->data.u64.mask; + break; + case VCAP_FIELD_U72: + value = kf->data.u72.value; + mask = kf->data.u72.mask; + break; + case VCAP_FIELD_U112: + value = kf->data.u112.value; + mask = kf->data.u112.mask; + break; + case VCAP_FIELD_U128: + value = kf->data.u128.value; + mask = kf->data.u128.mask; + break; + } + vcap_iter_init(&iter, sw_width, tgt, rf->offset); + vcap_encode_field(cache->keystream, &iter, rf->width, value); + vcap_iter_init(&iter, sw_width, tgt, rf->offset); + vcap_encode_field(cache->maskstream, &iter, rf->width, mask); +} + +static void vcap_encode_keyfield_typegroups(struct vcap_control *vctrl, + struct vcap_rule_internal *ri, + const struct vcap_typegroup *tgt) +{ + int sw_width = vctrl->vcaps[ri->admin->vtype].sw_width; + struct vcap_cache_data *cache = &ri->admin->cache; + + /* Encode the typegroup bits for the key and the mask in their streams, + * respecting the subword width. + */ + vcap_encode_typegroups(cache->keystream, sw_width, tgt, false); + vcap_encode_typegroups(cache->maskstream, sw_width, tgt, true); +} + +static int vcap_encode_rule_keyset(struct vcap_rule_internal *ri) +{ + const struct vcap_client_keyfield *ckf; + const struct vcap_typegroup *tg_table; + const struct vcap_field *kf_table; + int keyset_size; + + /* Get a valid set of fields for the specific keyset */ + kf_table = vcap_keyfields(ri->vctrl, ri->admin->vtype, ri->data.keyset); + if (!kf_table) { + pr_err("%s:%d: no fields available for this keyset: %d\n", + __func__, __LINE__, ri->data.keyset); + return -EINVAL; + } + /* Get a valid typegroup for the specific keyset */ + tg_table = vcap_keyfield_typegroup(ri->vctrl, ri->admin->vtype, + ri->data.keyset); + if (!tg_table) { + pr_err("%s:%d: no typegroups available for this keyset: %d\n", + __func__, __LINE__, ri->data.keyset); + return -EINVAL; + } + /* Get a valid size for the specific keyset */ + keyset_size = vcap_keyfield_count(ri->vctrl, ri->admin->vtype, + ri->data.keyset); + if (keyset_size == 0) { + pr_err("%s:%d: zero field count for this keyset: %d\n", + __func__, __LINE__, ri->data.keyset); + return -EINVAL; + } + /* Iterate over the keyfields (key, mask) in the rule + * and encode these bits + */ + if (list_empty(&ri->data.keyfields)) { + pr_err("%s:%d: no keyfields in the rule\n", __func__, __LINE__); + return -EINVAL; + } + list_for_each_entry(ckf, &ri->data.keyfields, ctrl.list) { + /* Check that the client entry exists in the keyset */ + if (ckf->ctrl.key >= keyset_size) { + pr_err("%s:%d: key %d is not in vcap\n", + __func__, __LINE__, ckf->ctrl.key); + return -EINVAL; + } + vcap_encode_keyfield(ri, ckf, &kf_table[ckf->ctrl.key], tg_table); + } + /* Add typegroup bits to the key/mask bitstreams */ + vcap_encode_keyfield_typegroups(ri->vctrl, ri, tg_table); + return 0; +} + +/* Return the list of actionfields for the actionset */ +static const struct vcap_field * +vcap_actionfields(struct vcap_control *vctrl, + enum vcap_type vt, enum vcap_actionfield_set actionset) +{ + /* Check that the actionset exists in the vcap actionset list */ + if (actionset >= vctrl->vcaps[vt].actionfield_set_size) + return NULL; + return vctrl->vcaps[vt].actionfield_set_map[actionset]; +} + static const struct vcap_set * vcap_actionfieldset(struct vcap_control *vctrl, enum vcap_type vt, enum vcap_actionfield_set actionset) @@ -76,9 +363,146 @@ vcap_actionfieldset(struct vcap_control *vctrl, return aset; } +/* Return the typegroup table for the matching actionset (using subword size) */ +static const struct vcap_typegroup * +vcap_actionfield_typegroup(struct vcap_control *vctrl, + enum vcap_type vt, enum vcap_actionfield_set actionset) +{ + const struct vcap_set *aset = vcap_actionfieldset(vctrl, vt, actionset); + + /* Check that the actionset is valid */ + if (!aset) + return NULL; + return vctrl->vcaps[vt].actionfield_set_typegroups[aset->sw_per_item]; +} + +/* Return the number of actionfields in the actionset */ +static int vcap_actionfield_count(struct vcap_control *vctrl, + enum vcap_type vt, + enum vcap_actionfield_set actionset) +{ + /* Check that the actionset exists in the vcap actionset list */ + if (actionset >= vctrl->vcaps[vt].actionfield_set_size) + return 0; + return vctrl->vcaps[vt].actionfield_set_map_size[actionset]; +} + +static void vcap_encode_actionfield(struct vcap_rule_internal *ri, + const struct vcap_client_actionfield *af, + const struct vcap_field *rf, + const struct vcap_typegroup *tgt) +{ + int act_width = ri->vctrl->vcaps[ri->admin->vtype].act_width; + + struct vcap_cache_data *cache = &ri->admin->cache; + struct vcap_stream_iter iter; + const u8 *value; + + /* Encode the action field in the stream, respecting the subword width */ + switch (af->ctrl.type) { + case VCAP_FIELD_BIT: + value = &af->data.u1.value; + break; + case VCAP_FIELD_U32: + value = (const u8 *)&af->data.u32.value; + break; + case VCAP_FIELD_U48: + value = af->data.u48.value; + break; + case VCAP_FIELD_U56: + value = af->data.u56.value; + break; + case VCAP_FIELD_U64: + value = af->data.u64.value; + break; + case VCAP_FIELD_U72: + value = af->data.u72.value; + break; + case VCAP_FIELD_U112: + value = af->data.u112.value; + break; + case VCAP_FIELD_U128: + value = af->data.u128.value; + break; + } + vcap_iter_init(&iter, act_width, tgt, rf->offset); + vcap_encode_field(cache->actionstream, &iter, rf->width, value); +} + +static void vcap_encode_actionfield_typegroups(struct vcap_rule_internal *ri, + const struct vcap_typegroup *tgt) +{ + int sw_width = ri->vctrl->vcaps[ri->admin->vtype].act_width; + struct vcap_cache_data *cache = &ri->admin->cache; + + /* Encode the typegroup bits for the actionstream respecting the subword + * width. + */ + vcap_encode_typegroups(cache->actionstream, sw_width, tgt, false); +} + +static int vcap_encode_rule_actionset(struct vcap_rule_internal *ri) +{ + const struct vcap_client_actionfield *caf; + const struct vcap_typegroup *tg_table; + const struct vcap_field *af_table; + int actionset_size; + + /* Get a valid set of actionset fields for the specific actionset */ + af_table = vcap_actionfields(ri->vctrl, ri->admin->vtype, + ri->data.actionset); + if (!af_table) { + pr_err("%s:%d: no fields available for this actionset: %d\n", + __func__, __LINE__, ri->data.actionset); + return -EINVAL; + } + /* Get a valid typegroup for the specific actionset */ + tg_table = vcap_actionfield_typegroup(ri->vctrl, ri->admin->vtype, + ri->data.actionset); + if (!tg_table) { + pr_err("%s:%d: no typegroups available for this actionset: %d\n", + __func__, __LINE__, ri->data.actionset); + return -EINVAL; + } + /* Get a valid actionset size for the specific actionset */ + actionset_size = vcap_actionfield_count(ri->vctrl, ri->admin->vtype, + ri->data.actionset); + if (actionset_size == 0) { + pr_err("%s:%d: zero field count for this actionset: %d\n", + __func__, __LINE__, ri->data.actionset); + return -EINVAL; + } + /* Iterate over the actionfields in the rule + * and encode these bits + */ + if (list_empty(&ri->data.actionfields)) + pr_warn("%s:%d: no actionfields in the rule\n", + __func__, __LINE__); + list_for_each_entry(caf, &ri->data.actionfields, ctrl.list) { + /* Check that the client action exists in the actionset */ + if (caf->ctrl.action >= actionset_size) { + pr_err("%s:%d: action %d is not in vcap\n", + __func__, __LINE__, caf->ctrl.action); + return -EINVAL; + } + vcap_encode_actionfield(ri, caf, &af_table[caf->ctrl.action], + tg_table); + } + /* Add typegroup bits to the entry bitstreams */ + vcap_encode_actionfield_typegroups(ri, tg_table); + return 0; +} + static int vcap_encode_rule(struct vcap_rule_internal *ri) { - /* Encoding of keyset and actionsets will be added later */ + int err; + + err = vcap_encode_rule_keyset(ri); + if (err) + return err; + err = vcap_encode_rule_actionset(ri); + if (err) + return err; return 0; } From 5d7e5b0401d7b7825c2a8572a403c341dc68f573 Mon Sep 17 00:00:00 2001 From: Steen Hegelund Date: Thu, 20 Oct 2022 15:09:03 +0200 Subject: [PATCH 0282/2002] net: microchip: sparx5: Adding KUNIT test VCAP model This provides a test VCAP model for use in a KUNIT test. The model provides 3 different VCAP types for better test coverage. Signed-off-by: Steen Hegelund Signed-off-by: David S. Miller --- drivers/net/ethernet/microchip/vcap/Makefile | 1 + .../microchip/vcap/vcap_model_kunit.c | 5570 +++++++++++++++++ .../microchip/vcap/vcap_model_kunit.h | 10 + 3 files changed, 5581 insertions(+) create mode 100644 drivers/net/ethernet/microchip/vcap/vcap_model_kunit.c create mode 100644 drivers/net/ethernet/microchip/vcap/vcap_model_kunit.h diff --git a/drivers/net/ethernet/microchip/vcap/Makefile b/drivers/net/ethernet/microchip/vcap/Makefile index 598d1c296f38..b377569f92d8 100644 --- a/drivers/net/ethernet/microchip/vcap/Makefile +++ b/drivers/net/ethernet/microchip/vcap/Makefile @@ -4,5 +4,6 @@ # obj-$(CONFIG_VCAP) += vcap.o +obj-$(CONFIG_VCAP_KUNIT_TEST) += vcap_model_kunit.o vcap-y += vcap_api.o diff --git a/drivers/net/ethernet/microchip/vcap/vcap_model_kunit.c b/drivers/net/ethernet/microchip/vcap/vcap_model_kunit.c new file mode 100644 index 000000000000..5d681d2697cd --- /dev/null +++ b/drivers/net/ethernet/microchip/vcap/vcap_model_kunit.c @@ -0,0 +1,5570 @@ +// SPDX-License-Identifier: BSD-3-Clause +/* Copyright (C) 2022 Microchip Technology Inc. and its subsidiaries. + * Microchip VCAP API Test VCAP Model Data + */ + +#include +#include + +#include "vcap_api.h" +#include "vcap_model_kunit.h" + +/* keyfields */ +static const struct vcap_field is0_mll_keyfield[] = { + [VCAP_KF_TYPE] = { + .type = VCAP_FIELD_U32, + .offset = 0, + .width = 2, + }, + [VCAP_KF_LOOKUP_FIRST_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 2, + .width = 1, + }, + [VCAP_KF_IF_IGR_PORT] = { + .type = VCAP_FIELD_U32, + .offset = 3, + .width = 7, + }, + [VCAP_KF_8021Q_VLAN_TAGS] = { + .type = VCAP_FIELD_U32, + .offset = 10, + .width = 3, + }, + [VCAP_KF_8021Q_TPID0] = { + .type = VCAP_FIELD_U32, + .offset = 13, + .width = 3, + }, + [VCAP_KF_8021Q_VID0] = { + .type = VCAP_FIELD_U32, + .offset = 16, + .width = 12, + }, + [VCAP_KF_8021Q_TPID1] = { + .type = VCAP_FIELD_U32, + .offset = 28, + .width = 3, + }, + [VCAP_KF_8021Q_VID1] = { + .type = VCAP_FIELD_U32, + .offset = 31, + .width = 12, + }, + [VCAP_KF_L2_DMAC] = { + .type = VCAP_FIELD_U48, + .offset = 43, + .width = 48, + }, + [VCAP_KF_L2_SMAC] = { + .type = VCAP_FIELD_U48, + .offset = 91, + .width = 48, + }, + [VCAP_KF_ETYPE_MPLS] = { + .type = VCAP_FIELD_U32, + .offset = 139, + .width = 2, + }, + [VCAP_KF_L4_RNG] = { + .type = VCAP_FIELD_U32, + .offset = 141, + .width = 8, + }, +}; + +static const struct vcap_field is0_tri_vid_keyfield[] = { + [VCAP_KF_TYPE] = { + .type = VCAP_FIELD_U32, + .offset = 0, + .width = 2, + }, + [VCAP_KF_LOOKUP_FIRST_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 2, + .width = 1, + }, + [VCAP_KF_IF_IGR_PORT] = { + .type = VCAP_FIELD_U32, + .offset = 3, + .width = 7, + }, + [VCAP_KF_LOOKUP_GEN_IDX_SEL] = { + .type = VCAP_FIELD_U32, + .offset = 10, + .width = 2, + }, + [VCAP_KF_LOOKUP_GEN_IDX] = { + .type = VCAP_FIELD_U32, + .offset = 12, + .width = 12, + }, + [VCAP_KF_8021Q_VLAN_TAGS] = { + .type = VCAP_FIELD_U32, + .offset = 24, + .width = 3, + }, + [VCAP_KF_8021Q_TPID0] = { + .type = VCAP_FIELD_U32, + .offset = 27, + .width = 3, + }, + [VCAP_KF_8021Q_PCP0] = { + .type = VCAP_FIELD_U32, + .offset = 30, + .width = 3, + }, + [VCAP_KF_8021Q_DEI0] = { + .type = VCAP_FIELD_BIT, + .offset = 33, + .width = 1, + }, + [VCAP_KF_8021Q_VID0] = { + .type = VCAP_FIELD_U32, + .offset = 34, + .width = 12, + }, + [VCAP_KF_8021Q_TPID1] = { + .type = VCAP_FIELD_U32, + .offset = 46, + .width = 3, + }, + [VCAP_KF_8021Q_PCP1] = { + .type = VCAP_FIELD_U32, + .offset = 49, + .width = 3, + }, + [VCAP_KF_8021Q_DEI1] = { + .type = VCAP_FIELD_BIT, + .offset = 52, + .width = 1, + }, + [VCAP_KF_8021Q_VID1] = { + .type = VCAP_FIELD_U32, + .offset = 53, + .width = 12, + }, + [VCAP_KF_8021Q_TPID2] = { + .type = VCAP_FIELD_U32, + .offset = 65, + .width = 3, + }, + [VCAP_KF_8021Q_PCP2] = { + .type = VCAP_FIELD_U32, + .offset = 68, + .width = 3, + }, + [VCAP_KF_8021Q_DEI2] = { + .type = VCAP_FIELD_BIT, + .offset = 71, + .width = 1, + }, + [VCAP_KF_8021Q_VID2] = { + .type = VCAP_FIELD_U32, + .offset = 72, + .width = 12, + }, + [VCAP_KF_L4_RNG] = { + .type = VCAP_FIELD_U32, + .offset = 84, + .width = 8, + }, + [VCAP_KF_OAM_Y1731_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 92, + .width = 1, + }, + [VCAP_KF_OAM_MEL_FLAGS] = { + .type = VCAP_FIELD_U32, + .offset = 93, + .width = 7, + }, +}; + +static const struct vcap_field is0_ll_full_keyfield[] = { + [VCAP_KF_TYPE] = { + .type = VCAP_FIELD_U32, + .offset = 0, + .width = 2, + }, + [VCAP_KF_LOOKUP_FIRST_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 2, + .width = 1, + }, + [VCAP_KF_IF_IGR_PORT] = { + .type = VCAP_FIELD_U32, + .offset = 3, + .width = 7, + }, + [VCAP_KF_8021Q_VLAN_TAGS] = { + .type = VCAP_FIELD_U32, + .offset = 10, + .width = 3, + }, + [VCAP_KF_8021Q_TPID0] = { + .type = VCAP_FIELD_U32, + .offset = 13, + .width = 3, + }, + [VCAP_KF_8021Q_PCP0] = { + .type = VCAP_FIELD_U32, + .offset = 16, + .width = 3, + }, + [VCAP_KF_8021Q_DEI0] = { + .type = VCAP_FIELD_BIT, + .offset = 19, + .width = 1, + }, + [VCAP_KF_8021Q_VID0] = { + .type = VCAP_FIELD_U32, + .offset = 20, + .width = 12, + }, + [VCAP_KF_8021Q_TPID1] = { + .type = VCAP_FIELD_U32, + .offset = 32, + .width = 3, + }, + [VCAP_KF_8021Q_PCP1] = { + .type = VCAP_FIELD_U32, + .offset = 35, + .width = 3, + }, + [VCAP_KF_8021Q_DEI1] = { + .type = VCAP_FIELD_BIT, + .offset = 38, + .width = 1, + }, + [VCAP_KF_8021Q_VID1] = { + .type = VCAP_FIELD_U32, + .offset = 39, + .width = 12, + }, + [VCAP_KF_8021Q_TPID2] = { + .type = VCAP_FIELD_U32, + .offset = 51, + .width = 3, + }, + [VCAP_KF_8021Q_PCP2] = { + .type = VCAP_FIELD_U32, + .offset = 54, + .width = 3, + }, + [VCAP_KF_8021Q_DEI2] = { + .type = VCAP_FIELD_BIT, + .offset = 57, + .width = 1, + }, + [VCAP_KF_8021Q_VID2] = { + .type = VCAP_FIELD_U32, + .offset = 58, + .width = 12, + }, + [VCAP_KF_L2_DMAC] = { + .type = VCAP_FIELD_U48, + .offset = 70, + .width = 48, + }, + [VCAP_KF_L2_SMAC] = { + .type = VCAP_FIELD_U48, + .offset = 118, + .width = 48, + }, + [VCAP_KF_ETYPE_LEN_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 166, + .width = 1, + }, + [VCAP_KF_ETYPE] = { + .type = VCAP_FIELD_U32, + .offset = 167, + .width = 16, + }, + [VCAP_KF_IP_SNAP_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 183, + .width = 1, + }, + [VCAP_KF_IP4_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 184, + .width = 1, + }, + [VCAP_KF_L3_FRAGMENT_TYPE] = { + .type = VCAP_FIELD_U32, + .offset = 185, + .width = 2, + }, + [VCAP_KF_L3_FRAG_INVLD_L4_LEN] = { + .type = VCAP_FIELD_BIT, + .offset = 187, + .width = 1, + }, + [VCAP_KF_L3_OPTIONS_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 188, + .width = 1, + }, + [VCAP_KF_L3_DSCP] = { + .type = VCAP_FIELD_U32, + .offset = 189, + .width = 6, + }, + [VCAP_KF_L3_IP4_DIP] = { + .type = VCAP_FIELD_U32, + .offset = 195, + .width = 32, + }, + [VCAP_KF_L3_IP4_SIP] = { + .type = VCAP_FIELD_U32, + .offset = 227, + .width = 32, + }, + [VCAP_KF_TCP_UDP_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 259, + .width = 1, + }, + [VCAP_KF_TCP_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 260, + .width = 1, + }, + [VCAP_KF_L4_SPORT] = { + .type = VCAP_FIELD_U32, + .offset = 261, + .width = 16, + }, + [VCAP_KF_L4_RNG] = { + .type = VCAP_FIELD_U32, + .offset = 277, + .width = 8, + }, +}; + +static const struct vcap_field is0_normal_keyfield[] = { + [VCAP_KF_TYPE] = { + .type = VCAP_FIELD_U32, + .offset = 0, + .width = 2, + }, + [VCAP_KF_LOOKUP_FIRST_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 2, + .width = 1, + }, + [VCAP_KF_LOOKUP_GEN_IDX_SEL] = { + .type = VCAP_FIELD_U32, + .offset = 3, + .width = 2, + }, + [VCAP_KF_LOOKUP_GEN_IDX] = { + .type = VCAP_FIELD_U32, + .offset = 5, + .width = 12, + }, + [VCAP_KF_IF_IGR_PORT_MASK_SEL] = { + .type = VCAP_FIELD_U32, + .offset = 17, + .width = 2, + }, + [VCAP_KF_IF_IGR_PORT_MASK] = { + .type = VCAP_FIELD_U72, + .offset = 19, + .width = 65, + }, + [VCAP_KF_L2_MC_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 84, + .width = 1, + }, + [VCAP_KF_L2_BC_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 85, + .width = 1, + }, + [VCAP_KF_8021Q_VLAN_TAGS] = { + .type = VCAP_FIELD_U32, + .offset = 86, + .width = 3, + }, + [VCAP_KF_8021Q_TPID0] = { + .type = VCAP_FIELD_U32, + .offset = 89, + .width = 3, + }, + [VCAP_KF_8021Q_PCP0] = { + .type = VCAP_FIELD_U32, + .offset = 92, + .width = 3, + }, + [VCAP_KF_8021Q_DEI0] = { + .type = VCAP_FIELD_BIT, + .offset = 95, + .width = 1, + }, + [VCAP_KF_8021Q_VID0] = { + .type = VCAP_FIELD_U32, + .offset = 96, + .width = 12, + }, + [VCAP_KF_8021Q_TPID1] = { + .type = VCAP_FIELD_U32, + .offset = 108, + .width = 3, + }, + [VCAP_KF_8021Q_PCP1] = { + .type = VCAP_FIELD_U32, + .offset = 111, + .width = 3, + }, + [VCAP_KF_8021Q_DEI1] = { + .type = VCAP_FIELD_BIT, + .offset = 114, + .width = 1, + }, + [VCAP_KF_8021Q_VID1] = { + .type = VCAP_FIELD_U32, + .offset = 115, + .width = 12, + }, + [VCAP_KF_8021Q_TPID2] = { + .type = VCAP_FIELD_U32, + .offset = 127, + .width = 3, + }, + [VCAP_KF_8021Q_PCP2] = { + .type = VCAP_FIELD_U32, + .offset = 130, + .width = 3, + }, + [VCAP_KF_8021Q_DEI2] = { + .type = VCAP_FIELD_BIT, + .offset = 133, + .width = 1, + }, + [VCAP_KF_8021Q_VID2] = { + .type = VCAP_FIELD_U32, + .offset = 134, + .width = 12, + }, + [VCAP_KF_DST_ENTRY] = { + .type = VCAP_FIELD_BIT, + .offset = 146, + .width = 1, + }, + [VCAP_KF_L2_SMAC] = { + .type = VCAP_FIELD_U48, + .offset = 147, + .width = 48, + }, + [VCAP_KF_IP_MC_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 195, + .width = 1, + }, + [VCAP_KF_ETYPE_LEN_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 196, + .width = 1, + }, + [VCAP_KF_ETYPE] = { + .type = VCAP_FIELD_U32, + .offset = 197, + .width = 16, + }, + [VCAP_KF_IP_SNAP_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 213, + .width = 1, + }, + [VCAP_KF_IP4_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 214, + .width = 1, + }, + [VCAP_KF_L3_FRAGMENT_TYPE] = { + .type = VCAP_FIELD_U32, + .offset = 215, + .width = 2, + }, + [VCAP_KF_L3_FRAG_INVLD_L4_LEN] = { + .type = VCAP_FIELD_BIT, + .offset = 217, + .width = 1, + }, + [VCAP_KF_L3_OPTIONS_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 218, + .width = 1, + }, + [VCAP_KF_L3_DSCP] = { + .type = VCAP_FIELD_U32, + .offset = 219, + .width = 6, + }, + [VCAP_KF_L3_IP4_SIP] = { + .type = VCAP_FIELD_U32, + .offset = 225, + .width = 32, + }, + [VCAP_KF_TCP_UDP_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 257, + .width = 1, + }, + [VCAP_KF_TCP_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 258, + .width = 1, + }, + [VCAP_KF_L4_SPORT] = { + .type = VCAP_FIELD_U32, + .offset = 259, + .width = 16, + }, + [VCAP_KF_L4_RNG] = { + .type = VCAP_FIELD_U32, + .offset = 275, + .width = 8, + }, +}; + +static const struct vcap_field is0_normal_7tuple_keyfield[] = { + [VCAP_KF_TYPE] = { + .type = VCAP_FIELD_BIT, + .offset = 0, + .width = 1, + }, + [VCAP_KF_LOOKUP_FIRST_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 1, + .width = 1, + }, + [VCAP_KF_LOOKUP_GEN_IDX_SEL] = { + .type = VCAP_FIELD_U32, + .offset = 2, + .width = 2, + }, + [VCAP_KF_LOOKUP_GEN_IDX] = { + .type = VCAP_FIELD_U32, + .offset = 4, + .width = 12, + }, + [VCAP_KF_IF_IGR_PORT_MASK_SEL] = { + .type = VCAP_FIELD_U32, + .offset = 16, + .width = 2, + }, + [VCAP_KF_IF_IGR_PORT_MASK] = { + .type = VCAP_FIELD_U72, + .offset = 18, + .width = 65, + }, + [VCAP_KF_L2_MC_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 83, + .width = 1, + }, + [VCAP_KF_L2_BC_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 84, + .width = 1, + }, + [VCAP_KF_8021Q_VLAN_TAGS] = { + .type = VCAP_FIELD_U32, + .offset = 85, + .width = 3, + }, + [VCAP_KF_8021Q_TPID0] = { + .type = VCAP_FIELD_U32, + .offset = 88, + .width = 3, + }, + [VCAP_KF_8021Q_PCP0] = { + .type = VCAP_FIELD_U32, + .offset = 91, + .width = 3, + }, + [VCAP_KF_8021Q_DEI0] = { + .type = VCAP_FIELD_BIT, + .offset = 94, + .width = 1, + }, + [VCAP_KF_8021Q_VID0] = { + .type = VCAP_FIELD_U32, + .offset = 95, + .width = 12, + }, + [VCAP_KF_8021Q_TPID1] = { + .type = VCAP_FIELD_U32, + .offset = 107, + .width = 3, + }, + [VCAP_KF_8021Q_PCP1] = { + .type = VCAP_FIELD_U32, + .offset = 110, + .width = 3, + }, + [VCAP_KF_8021Q_DEI1] = { + .type = VCAP_FIELD_BIT, + .offset = 113, + .width = 1, + }, + [VCAP_KF_8021Q_VID1] = { + .type = VCAP_FIELD_U32, + .offset = 114, + .width = 12, + }, + [VCAP_KF_8021Q_TPID2] = { + .type = VCAP_FIELD_U32, + .offset = 126, + .width = 3, + }, + [VCAP_KF_8021Q_PCP2] = { + .type = VCAP_FIELD_U32, + .offset = 129, + .width = 3, + }, + [VCAP_KF_8021Q_DEI2] = { + .type = VCAP_FIELD_BIT, + .offset = 132, + .width = 1, + }, + [VCAP_KF_8021Q_VID2] = { + .type = VCAP_FIELD_U32, + .offset = 133, + .width = 12, + }, + [VCAP_KF_L2_DMAC] = { + .type = VCAP_FIELD_U48, + .offset = 145, + .width = 48, + }, + [VCAP_KF_L2_SMAC] = { + .type = VCAP_FIELD_U48, + .offset = 193, + .width = 48, + }, + [VCAP_KF_IP_MC_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 241, + .width = 1, + }, + [VCAP_KF_ETYPE_LEN_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 242, + .width = 1, + }, + [VCAP_KF_ETYPE] = { + .type = VCAP_FIELD_U32, + .offset = 243, + .width = 16, + }, + [VCAP_KF_IP_SNAP_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 259, + .width = 1, + }, + [VCAP_KF_IP4_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 260, + .width = 1, + }, + [VCAP_KF_L3_FRAGMENT_TYPE] = { + .type = VCAP_FIELD_U32, + .offset = 261, + .width = 2, + }, + [VCAP_KF_L3_FRAG_INVLD_L4_LEN] = { + .type = VCAP_FIELD_BIT, + .offset = 263, + .width = 1, + }, + [VCAP_KF_L3_OPTIONS_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 264, + .width = 1, + }, + [VCAP_KF_L3_DSCP] = { + .type = VCAP_FIELD_U32, + .offset = 265, + .width = 6, + }, + [VCAP_KF_L3_IP6_DIP] = { + .type = VCAP_FIELD_U128, + .offset = 271, + .width = 128, + }, + [VCAP_KF_L3_IP6_SIP] = { + .type = VCAP_FIELD_U128, + .offset = 399, + .width = 128, + }, + [VCAP_KF_TCP_UDP_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 527, + .width = 1, + }, + [VCAP_KF_TCP_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 528, + .width = 1, + }, + [VCAP_KF_L4_SPORT] = { + .type = VCAP_FIELD_U32, + .offset = 529, + .width = 16, + }, + [VCAP_KF_L4_RNG] = { + .type = VCAP_FIELD_U32, + .offset = 545, + .width = 8, + }, +}; + +static const struct vcap_field is0_normal_5tuple_ip4_keyfield[] = { + [VCAP_KF_TYPE] = { + .type = VCAP_FIELD_U32, + .offset = 0, + .width = 2, + }, + [VCAP_KF_LOOKUP_FIRST_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 2, + .width = 1, + }, + [VCAP_KF_LOOKUP_GEN_IDX_SEL] = { + .type = VCAP_FIELD_U32, + .offset = 3, + .width = 2, + }, + [VCAP_KF_LOOKUP_GEN_IDX] = { + .type = VCAP_FIELD_U32, + .offset = 5, + .width = 12, + }, + [VCAP_KF_IF_IGR_PORT_MASK_SEL] = { + .type = VCAP_FIELD_U32, + .offset = 17, + .width = 2, + }, + [VCAP_KF_IF_IGR_PORT_MASK] = { + .type = VCAP_FIELD_U72, + .offset = 19, + .width = 65, + }, + [VCAP_KF_L2_MC_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 84, + .width = 1, + }, + [VCAP_KF_L2_BC_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 85, + .width = 1, + }, + [VCAP_KF_8021Q_VLAN_TAGS] = { + .type = VCAP_FIELD_U32, + .offset = 86, + .width = 3, + }, + [VCAP_KF_8021Q_TPID0] = { + .type = VCAP_FIELD_U32, + .offset = 89, + .width = 3, + }, + [VCAP_KF_8021Q_PCP0] = { + .type = VCAP_FIELD_U32, + .offset = 92, + .width = 3, + }, + [VCAP_KF_8021Q_DEI0] = { + .type = VCAP_FIELD_BIT, + .offset = 95, + .width = 1, + }, + [VCAP_KF_8021Q_VID0] = { + .type = VCAP_FIELD_U32, + .offset = 96, + .width = 12, + }, + [VCAP_KF_8021Q_TPID1] = { + .type = VCAP_FIELD_U32, + .offset = 108, + .width = 3, + }, + [VCAP_KF_8021Q_PCP1] = { + .type = VCAP_FIELD_U32, + .offset = 111, + .width = 3, + }, + [VCAP_KF_8021Q_DEI1] = { + .type = VCAP_FIELD_BIT, + .offset = 114, + .width = 1, + }, + [VCAP_KF_8021Q_VID1] = { + .type = VCAP_FIELD_U32, + .offset = 115, + .width = 12, + }, + [VCAP_KF_8021Q_TPID2] = { + .type = VCAP_FIELD_U32, + .offset = 127, + .width = 3, + }, + [VCAP_KF_8021Q_PCP2] = { + .type = VCAP_FIELD_U32, + .offset = 130, + .width = 3, + }, + [VCAP_KF_8021Q_DEI2] = { + .type = VCAP_FIELD_BIT, + .offset = 133, + .width = 1, + }, + [VCAP_KF_8021Q_VID2] = { + .type = VCAP_FIELD_U32, + .offset = 134, + .width = 12, + }, + [VCAP_KF_IP_MC_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 146, + .width = 1, + }, + [VCAP_KF_IP4_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 147, + .width = 1, + }, + [VCAP_KF_L3_FRAGMENT_TYPE] = { + .type = VCAP_FIELD_U32, + .offset = 148, + .width = 2, + }, + [VCAP_KF_L3_FRAG_INVLD_L4_LEN] = { + .type = VCAP_FIELD_BIT, + .offset = 150, + .width = 1, + }, + [VCAP_KF_L3_OPTIONS_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 151, + .width = 1, + }, + [VCAP_KF_L3_DSCP] = { + .type = VCAP_FIELD_U32, + .offset = 152, + .width = 6, + }, + [VCAP_KF_L3_IP4_DIP] = { + .type = VCAP_FIELD_U32, + .offset = 158, + .width = 32, + }, + [VCAP_KF_L3_IP4_SIP] = { + .type = VCAP_FIELD_U32, + .offset = 190, + .width = 32, + }, + [VCAP_KF_L3_IP_PROTO] = { + .type = VCAP_FIELD_U32, + .offset = 222, + .width = 8, + }, + [VCAP_KF_TCP_UDP_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 230, + .width = 1, + }, + [VCAP_KF_TCP_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 231, + .width = 1, + }, + [VCAP_KF_L4_RNG] = { + .type = VCAP_FIELD_U32, + .offset = 232, + .width = 8, + }, + [VCAP_KF_IP_PAYLOAD_5TUPLE] = { + .type = VCAP_FIELD_U32, + .offset = 240, + .width = 32, + }, +}; + +static const struct vcap_field is0_pure_5tuple_ip4_keyfield[] = { + [VCAP_KF_TYPE] = { + .type = VCAP_FIELD_U32, + .offset = 0, + .width = 2, + }, + [VCAP_KF_LOOKUP_FIRST_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 2, + .width = 1, + }, + [VCAP_KF_LOOKUP_GEN_IDX_SEL] = { + .type = VCAP_FIELD_U32, + .offset = 3, + .width = 2, + }, + [VCAP_KF_LOOKUP_GEN_IDX] = { + .type = VCAP_FIELD_U32, + .offset = 5, + .width = 12, + }, + [VCAP_KF_L3_FRAGMENT_TYPE] = { + .type = VCAP_FIELD_U32, + .offset = 17, + .width = 2, + }, + [VCAP_KF_L3_FRAG_INVLD_L4_LEN] = { + .type = VCAP_FIELD_BIT, + .offset = 19, + .width = 1, + }, + [VCAP_KF_L3_OPTIONS_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 20, + .width = 1, + }, + [VCAP_KF_L3_DSCP] = { + .type = VCAP_FIELD_U32, + .offset = 21, + .width = 6, + }, + [VCAP_KF_L3_IP4_DIP] = { + .type = VCAP_FIELD_U32, + .offset = 27, + .width = 32, + }, + [VCAP_KF_L3_IP4_SIP] = { + .type = VCAP_FIELD_U32, + .offset = 59, + .width = 32, + }, + [VCAP_KF_L3_IP_PROTO] = { + .type = VCAP_FIELD_U32, + .offset = 91, + .width = 8, + }, + [VCAP_KF_L4_RNG] = { + .type = VCAP_FIELD_U32, + .offset = 99, + .width = 8, + }, + [VCAP_KF_IP_PAYLOAD_5TUPLE] = { + .type = VCAP_FIELD_U32, + .offset = 107, + .width = 32, + }, +}; + +static const struct vcap_field is0_etag_keyfield[] = { + [VCAP_KF_TYPE] = { + .type = VCAP_FIELD_U32, + .offset = 0, + .width = 2, + }, + [VCAP_KF_LOOKUP_FIRST_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 2, + .width = 1, + }, + [VCAP_KF_IF_IGR_PORT] = { + .type = VCAP_FIELD_U32, + .offset = 3, + .width = 7, + }, + [VCAP_KF_8021BR_E_TAGGED] = { + .type = VCAP_FIELD_BIT, + .offset = 10, + .width = 1, + }, + [VCAP_KF_8021BR_GRP] = { + .type = VCAP_FIELD_U32, + .offset = 11, + .width = 2, + }, + [VCAP_KF_8021BR_ECID_EXT] = { + .type = VCAP_FIELD_U32, + .offset = 13, + .width = 8, + }, + [VCAP_KF_8021BR_ECID_BASE] = { + .type = VCAP_FIELD_U32, + .offset = 21, + .width = 12, + }, + [VCAP_KF_8021BR_IGR_ECID_EXT] = { + .type = VCAP_FIELD_U32, + .offset = 33, + .width = 8, + }, + [VCAP_KF_8021BR_IGR_ECID_BASE] = { + .type = VCAP_FIELD_U32, + .offset = 41, + .width = 12, + }, +}; + +static const struct vcap_field is2_mac_etype_keyfield[] = { + [VCAP_KF_TYPE] = { + .type = VCAP_FIELD_U32, + .offset = 0, + .width = 4, + }, + [VCAP_KF_LOOKUP_FIRST_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 4, + .width = 1, + }, + [VCAP_KF_LOOKUP_PAG] = { + .type = VCAP_FIELD_U32, + .offset = 5, + .width = 8, + }, + [VCAP_KF_IF_IGR_PORT_MASK_L3] = { + .type = VCAP_FIELD_BIT, + .offset = 13, + .width = 1, + }, + [VCAP_KF_IF_IGR_PORT_MASK_RNG] = { + .type = VCAP_FIELD_U32, + .offset = 14, + .width = 4, + }, + [VCAP_KF_IF_IGR_PORT_MASK_SEL] = { + .type = VCAP_FIELD_U32, + .offset = 18, + .width = 2, + }, + [VCAP_KF_IF_IGR_PORT_MASK] = { + .type = VCAP_FIELD_U32, + .offset = 20, + .width = 32, + }, + [VCAP_KF_L2_MC_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 52, + .width = 1, + }, + [VCAP_KF_L2_BC_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 53, + .width = 1, + }, + [VCAP_KF_8021Q_VLAN_TAGGED_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 54, + .width = 1, + }, + [VCAP_KF_ISDX_GT0_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 55, + .width = 1, + }, + [VCAP_KF_ISDX_CLS] = { + .type = VCAP_FIELD_U32, + .offset = 56, + .width = 12, + }, + [VCAP_KF_8021Q_VID_CLS] = { + .type = VCAP_FIELD_U32, + .offset = 68, + .width = 13, + }, + [VCAP_KF_8021Q_DEI_CLS] = { + .type = VCAP_FIELD_BIT, + .offset = 81, + .width = 1, + }, + [VCAP_KF_8021Q_PCP_CLS] = { + .type = VCAP_FIELD_U32, + .offset = 82, + .width = 3, + }, + [VCAP_KF_L2_FWD_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 85, + .width = 1, + }, + [VCAP_KF_L3_SMAC_SIP_MATCH] = { + .type = VCAP_FIELD_BIT, + .offset = 86, + .width = 1, + }, + [VCAP_KF_L3_DMAC_DIP_MATCH] = { + .type = VCAP_FIELD_BIT, + .offset = 87, + .width = 1, + }, + [VCAP_KF_L3_RT_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 88, + .width = 1, + }, + [VCAP_KF_L3_DST_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 89, + .width = 1, + }, + [VCAP_KF_L2_DMAC] = { + .type = VCAP_FIELD_U48, + .offset = 90, + .width = 48, + }, + [VCAP_KF_L2_SMAC] = { + .type = VCAP_FIELD_U48, + .offset = 138, + .width = 48, + }, + [VCAP_KF_ETYPE_LEN_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 186, + .width = 1, + }, + [VCAP_KF_ETYPE] = { + .type = VCAP_FIELD_U32, + .offset = 187, + .width = 16, + }, + [VCAP_KF_L2_PAYLOAD_ETYPE] = { + .type = VCAP_FIELD_U64, + .offset = 203, + .width = 64, + }, + [VCAP_KF_L4_RNG] = { + .type = VCAP_FIELD_U32, + .offset = 267, + .width = 16, + }, + [VCAP_KF_OAM_CCM_CNTS_EQ0] = { + .type = VCAP_FIELD_BIT, + .offset = 283, + .width = 1, + }, + [VCAP_KF_OAM_Y1731_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 284, + .width = 1, + }, +}; + +static const struct vcap_field is2_arp_keyfield[] = { + [VCAP_KF_TYPE] = { + .type = VCAP_FIELD_U32, + .offset = 0, + .width = 4, + }, + [VCAP_KF_LOOKUP_FIRST_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 4, + .width = 1, + }, + [VCAP_KF_LOOKUP_PAG] = { + .type = VCAP_FIELD_U32, + .offset = 5, + .width = 8, + }, + [VCAP_KF_IF_IGR_PORT_MASK_L3] = { + .type = VCAP_FIELD_BIT, + .offset = 13, + .width = 1, + }, + [VCAP_KF_IF_IGR_PORT_MASK_RNG] = { + .type = VCAP_FIELD_U32, + .offset = 14, + .width = 4, + }, + [VCAP_KF_IF_IGR_PORT_MASK_SEL] = { + .type = VCAP_FIELD_U32, + .offset = 18, + .width = 2, + }, + [VCAP_KF_IF_IGR_PORT_MASK] = { + .type = VCAP_FIELD_U32, + .offset = 20, + .width = 32, + }, + [VCAP_KF_L2_MC_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 52, + .width = 1, + }, + [VCAP_KF_L2_BC_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 53, + .width = 1, + }, + [VCAP_KF_8021Q_VLAN_TAGGED_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 54, + .width = 1, + }, + [VCAP_KF_ISDX_GT0_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 55, + .width = 1, + }, + [VCAP_KF_ISDX_CLS] = { + .type = VCAP_FIELD_U32, + .offset = 56, + .width = 12, + }, + [VCAP_KF_8021Q_VID_CLS] = { + .type = VCAP_FIELD_U32, + .offset = 68, + .width = 13, + }, + [VCAP_KF_8021Q_DEI_CLS] = { + .type = VCAP_FIELD_BIT, + .offset = 81, + .width = 1, + }, + [VCAP_KF_8021Q_PCP_CLS] = { + .type = VCAP_FIELD_U32, + .offset = 82, + .width = 3, + }, + [VCAP_KF_L2_FWD_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 85, + .width = 1, + }, + [VCAP_KF_L2_SMAC] = { + .type = VCAP_FIELD_U48, + .offset = 86, + .width = 48, + }, + [VCAP_KF_ARP_ADDR_SPACE_OK_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 134, + .width = 1, + }, + [VCAP_KF_ARP_PROTO_SPACE_OK_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 135, + .width = 1, + }, + [VCAP_KF_ARP_LEN_OK_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 136, + .width = 1, + }, + [VCAP_KF_ARP_TGT_MATCH_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 137, + .width = 1, + }, + [VCAP_KF_ARP_SENDER_MATCH_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 138, + .width = 1, + }, + [VCAP_KF_ARP_OPCODE_UNKNOWN_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 139, + .width = 1, + }, + [VCAP_KF_ARP_OPCODE] = { + .type = VCAP_FIELD_U32, + .offset = 140, + .width = 2, + }, + [VCAP_KF_L3_IP4_DIP] = { + .type = VCAP_FIELD_U32, + .offset = 142, + .width = 32, + }, + [VCAP_KF_L3_IP4_SIP] = { + .type = VCAP_FIELD_U32, + .offset = 174, + .width = 32, + }, + [VCAP_KF_L3_DIP_EQ_SIP_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 206, + .width = 1, + }, + [VCAP_KF_L4_RNG] = { + .type = VCAP_FIELD_U32, + .offset = 207, + .width = 16, + }, +}; + +static const struct vcap_field is2_ip4_tcp_udp_keyfield[] = { + [VCAP_KF_TYPE] = { + .type = VCAP_FIELD_U32, + .offset = 0, + .width = 4, + }, + [VCAP_KF_LOOKUP_FIRST_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 4, + .width = 1, + }, + [VCAP_KF_LOOKUP_PAG] = { + .type = VCAP_FIELD_U32, + .offset = 5, + .width = 8, + }, + [VCAP_KF_IF_IGR_PORT_MASK_L3] = { + .type = VCAP_FIELD_BIT, + .offset = 13, + .width = 1, + }, + [VCAP_KF_IF_IGR_PORT_MASK_RNG] = { + .type = VCAP_FIELD_U32, + .offset = 14, + .width = 4, + }, + [VCAP_KF_IF_IGR_PORT_MASK_SEL] = { + .type = VCAP_FIELD_U32, + .offset = 18, + .width = 2, + }, + [VCAP_KF_IF_IGR_PORT_MASK] = { + .type = VCAP_FIELD_U32, + .offset = 20, + .width = 32, + }, + [VCAP_KF_L2_MC_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 52, + .width = 1, + }, + [VCAP_KF_L2_BC_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 53, + .width = 1, + }, + [VCAP_KF_8021Q_VLAN_TAGGED_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 54, + .width = 1, + }, + [VCAP_KF_ISDX_GT0_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 55, + .width = 1, + }, + [VCAP_KF_ISDX_CLS] = { + .type = VCAP_FIELD_U32, + .offset = 56, + .width = 12, + }, + [VCAP_KF_8021Q_VID_CLS] = { + .type = VCAP_FIELD_U32, + .offset = 68, + .width = 13, + }, + [VCAP_KF_8021Q_DEI_CLS] = { + .type = VCAP_FIELD_BIT, + .offset = 81, + .width = 1, + }, + [VCAP_KF_8021Q_PCP_CLS] = { + .type = VCAP_FIELD_U32, + .offset = 82, + .width = 3, + }, + [VCAP_KF_L2_FWD_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 85, + .width = 1, + }, + [VCAP_KF_L3_SMAC_SIP_MATCH] = { + .type = VCAP_FIELD_BIT, + .offset = 86, + .width = 1, + }, + [VCAP_KF_L3_DMAC_DIP_MATCH] = { + .type = VCAP_FIELD_BIT, + .offset = 87, + .width = 1, + }, + [VCAP_KF_L3_RT_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 88, + .width = 1, + }, + [VCAP_KF_L3_DST_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 89, + .width = 1, + }, + [VCAP_KF_IP4_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 90, + .width = 1, + }, + [VCAP_KF_L3_FRAGMENT_TYPE] = { + .type = VCAP_FIELD_U32, + .offset = 91, + .width = 2, + }, + [VCAP_KF_L3_FRAG_INVLD_L4_LEN] = { + .type = VCAP_FIELD_BIT, + .offset = 93, + .width = 1, + }, + [VCAP_KF_L3_OPTIONS_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 94, + .width = 1, + }, + [VCAP_KF_L3_TTL_GT0] = { + .type = VCAP_FIELD_BIT, + .offset = 95, + .width = 1, + }, + [VCAP_KF_L3_TOS] = { + .type = VCAP_FIELD_U32, + .offset = 96, + .width = 8, + }, + [VCAP_KF_L3_IP4_DIP] = { + .type = VCAP_FIELD_U32, + .offset = 104, + .width = 32, + }, + [VCAP_KF_L3_IP4_SIP] = { + .type = VCAP_FIELD_U32, + .offset = 136, + .width = 32, + }, + [VCAP_KF_L3_DIP_EQ_SIP_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 168, + .width = 1, + }, + [VCAP_KF_TCP_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 169, + .width = 1, + }, + [VCAP_KF_L4_DPORT] = { + .type = VCAP_FIELD_U32, + .offset = 170, + .width = 16, + }, + [VCAP_KF_L4_SPORT] = { + .type = VCAP_FIELD_U32, + .offset = 186, + .width = 16, + }, + [VCAP_KF_L4_RNG] = { + .type = VCAP_FIELD_U32, + .offset = 202, + .width = 16, + }, + [VCAP_KF_L4_SPORT_EQ_DPORT_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 218, + .width = 1, + }, + [VCAP_KF_L4_SEQUENCE_EQ0_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 219, + .width = 1, + }, + [VCAP_KF_L4_FIN] = { + .type = VCAP_FIELD_BIT, + .offset = 220, + .width = 1, + }, + [VCAP_KF_L4_SYN] = { + .type = VCAP_FIELD_BIT, + .offset = 221, + .width = 1, + }, + [VCAP_KF_L4_RST] = { + .type = VCAP_FIELD_BIT, + .offset = 222, + .width = 1, + }, + [VCAP_KF_L4_PSH] = { + .type = VCAP_FIELD_BIT, + .offset = 223, + .width = 1, + }, + [VCAP_KF_L4_ACK] = { + .type = VCAP_FIELD_BIT, + .offset = 224, + .width = 1, + }, + [VCAP_KF_L4_URG] = { + .type = VCAP_FIELD_BIT, + .offset = 225, + .width = 1, + }, + [VCAP_KF_L4_PAYLOAD] = { + .type = VCAP_FIELD_U64, + .offset = 226, + .width = 64, + }, +}; + +static const struct vcap_field is2_ip4_other_keyfield[] = { + [VCAP_KF_TYPE] = { + .type = VCAP_FIELD_U32, + .offset = 0, + .width = 4, + }, + [VCAP_KF_LOOKUP_FIRST_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 4, + .width = 1, + }, + [VCAP_KF_LOOKUP_PAG] = { + .type = VCAP_FIELD_U32, + .offset = 5, + .width = 8, + }, + [VCAP_KF_IF_IGR_PORT_MASK_L3] = { + .type = VCAP_FIELD_BIT, + .offset = 13, + .width = 1, + }, + [VCAP_KF_IF_IGR_PORT_MASK_RNG] = { + .type = VCAP_FIELD_U32, + .offset = 14, + .width = 4, + }, + [VCAP_KF_IF_IGR_PORT_MASK_SEL] = { + .type = VCAP_FIELD_U32, + .offset = 18, + .width = 2, + }, + [VCAP_KF_IF_IGR_PORT_MASK] = { + .type = VCAP_FIELD_U32, + .offset = 20, + .width = 32, + }, + [VCAP_KF_L2_MC_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 52, + .width = 1, + }, + [VCAP_KF_L2_BC_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 53, + .width = 1, + }, + [VCAP_KF_8021Q_VLAN_TAGGED_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 54, + .width = 1, + }, + [VCAP_KF_ISDX_GT0_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 55, + .width = 1, + }, + [VCAP_KF_ISDX_CLS] = { + .type = VCAP_FIELD_U32, + .offset = 56, + .width = 12, + }, + [VCAP_KF_8021Q_VID_CLS] = { + .type = VCAP_FIELD_U32, + .offset = 68, + .width = 13, + }, + [VCAP_KF_8021Q_DEI_CLS] = { + .type = VCAP_FIELD_BIT, + .offset = 81, + .width = 1, + }, + [VCAP_KF_8021Q_PCP_CLS] = { + .type = VCAP_FIELD_U32, + .offset = 82, + .width = 3, + }, + [VCAP_KF_L2_FWD_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 85, + .width = 1, + }, + [VCAP_KF_L3_SMAC_SIP_MATCH] = { + .type = VCAP_FIELD_BIT, + .offset = 86, + .width = 1, + }, + [VCAP_KF_L3_DMAC_DIP_MATCH] = { + .type = VCAP_FIELD_BIT, + .offset = 87, + .width = 1, + }, + [VCAP_KF_L3_RT_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 88, + .width = 1, + }, + [VCAP_KF_L3_DST_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 89, + .width = 1, + }, + [VCAP_KF_IP4_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 90, + .width = 1, + }, + [VCAP_KF_L3_FRAGMENT_TYPE] = { + .type = VCAP_FIELD_U32, + .offset = 91, + .width = 2, + }, + [VCAP_KF_L3_FRAG_INVLD_L4_LEN] = { + .type = VCAP_FIELD_BIT, + .offset = 93, + .width = 1, + }, + [VCAP_KF_L3_OPTIONS_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 94, + .width = 1, + }, + [VCAP_KF_L3_TTL_GT0] = { + .type = VCAP_FIELD_BIT, + .offset = 95, + .width = 1, + }, + [VCAP_KF_L3_TOS] = { + .type = VCAP_FIELD_U32, + .offset = 96, + .width = 8, + }, + [VCAP_KF_L3_IP4_DIP] = { + .type = VCAP_FIELD_U32, + .offset = 104, + .width = 32, + }, + [VCAP_KF_L3_IP4_SIP] = { + .type = VCAP_FIELD_U32, + .offset = 136, + .width = 32, + }, + [VCAP_KF_L3_DIP_EQ_SIP_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 168, + .width = 1, + }, + [VCAP_KF_L3_IP_PROTO] = { + .type = VCAP_FIELD_U32, + .offset = 169, + .width = 8, + }, + [VCAP_KF_L4_RNG] = { + .type = VCAP_FIELD_U32, + .offset = 177, + .width = 16, + }, + [VCAP_KF_L3_PAYLOAD] = { + .type = VCAP_FIELD_U112, + .offset = 193, + .width = 96, + }, +}; + +static const struct vcap_field is2_ip6_std_keyfield[] = { + [VCAP_KF_TYPE] = { + .type = VCAP_FIELD_U32, + .offset = 0, + .width = 4, + }, + [VCAP_KF_LOOKUP_FIRST_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 4, + .width = 1, + }, + [VCAP_KF_LOOKUP_PAG] = { + .type = VCAP_FIELD_U32, + .offset = 5, + .width = 8, + }, + [VCAP_KF_IF_IGR_PORT_MASK_L3] = { + .type = VCAP_FIELD_BIT, + .offset = 13, + .width = 1, + }, + [VCAP_KF_IF_IGR_PORT_MASK_RNG] = { + .type = VCAP_FIELD_U32, + .offset = 14, + .width = 4, + }, + [VCAP_KF_IF_IGR_PORT_MASK_SEL] = { + .type = VCAP_FIELD_U32, + .offset = 18, + .width = 2, + }, + [VCAP_KF_IF_IGR_PORT_MASK] = { + .type = VCAP_FIELD_U32, + .offset = 20, + .width = 32, + }, + [VCAP_KF_L2_MC_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 52, + .width = 1, + }, + [VCAP_KF_L2_BC_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 53, + .width = 1, + }, + [VCAP_KF_8021Q_VLAN_TAGGED_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 54, + .width = 1, + }, + [VCAP_KF_ISDX_GT0_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 55, + .width = 1, + }, + [VCAP_KF_ISDX_CLS] = { + .type = VCAP_FIELD_U32, + .offset = 56, + .width = 12, + }, + [VCAP_KF_8021Q_VID_CLS] = { + .type = VCAP_FIELD_U32, + .offset = 68, + .width = 13, + }, + [VCAP_KF_8021Q_DEI_CLS] = { + .type = VCAP_FIELD_BIT, + .offset = 81, + .width = 1, + }, + [VCAP_KF_8021Q_PCP_CLS] = { + .type = VCAP_FIELD_U32, + .offset = 82, + .width = 3, + }, + [VCAP_KF_L2_FWD_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 85, + .width = 1, + }, + [VCAP_KF_L3_SMAC_SIP_MATCH] = { + .type = VCAP_FIELD_BIT, + .offset = 86, + .width = 1, + }, + [VCAP_KF_L3_DMAC_DIP_MATCH] = { + .type = VCAP_FIELD_BIT, + .offset = 87, + .width = 1, + }, + [VCAP_KF_L3_RT_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 88, + .width = 1, + }, + [VCAP_KF_L3_DST_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 89, + .width = 1, + }, + [VCAP_KF_L3_TTL_GT0] = { + .type = VCAP_FIELD_BIT, + .offset = 90, + .width = 1, + }, + [VCAP_KF_L3_IP6_SIP] = { + .type = VCAP_FIELD_U128, + .offset = 91, + .width = 128, + }, + [VCAP_KF_L3_DIP_EQ_SIP_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 219, + .width = 1, + }, + [VCAP_KF_L3_IP_PROTO] = { + .type = VCAP_FIELD_U32, + .offset = 220, + .width = 8, + }, + [VCAP_KF_L4_RNG] = { + .type = VCAP_FIELD_U32, + .offset = 228, + .width = 16, + }, + [VCAP_KF_L3_PAYLOAD] = { + .type = VCAP_FIELD_U48, + .offset = 244, + .width = 40, + }, +}; + +static const struct vcap_field is2_ip_7tuple_keyfield[] = { + [VCAP_KF_TYPE] = { + .type = VCAP_FIELD_U32, + .offset = 0, + .width = 2, + }, + [VCAP_KF_LOOKUP_FIRST_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 2, + .width = 1, + }, + [VCAP_KF_LOOKUP_PAG] = { + .type = VCAP_FIELD_U32, + .offset = 3, + .width = 8, + }, + [VCAP_KF_IF_IGR_PORT_MASK_L3] = { + .type = VCAP_FIELD_BIT, + .offset = 11, + .width = 1, + }, + [VCAP_KF_IF_IGR_PORT_MASK_RNG] = { + .type = VCAP_FIELD_U32, + .offset = 12, + .width = 4, + }, + [VCAP_KF_IF_IGR_PORT_MASK_SEL] = { + .type = VCAP_FIELD_U32, + .offset = 16, + .width = 2, + }, + [VCAP_KF_IF_IGR_PORT_MASK] = { + .type = VCAP_FIELD_U72, + .offset = 18, + .width = 65, + }, + [VCAP_KF_L2_MC_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 83, + .width = 1, + }, + [VCAP_KF_L2_BC_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 84, + .width = 1, + }, + [VCAP_KF_8021Q_VLAN_TAGGED_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 85, + .width = 1, + }, + [VCAP_KF_ISDX_GT0_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 86, + .width = 1, + }, + [VCAP_KF_ISDX_CLS] = { + .type = VCAP_FIELD_U32, + .offset = 87, + .width = 12, + }, + [VCAP_KF_8021Q_VID_CLS] = { + .type = VCAP_FIELD_U32, + .offset = 99, + .width = 13, + }, + [VCAP_KF_8021Q_DEI_CLS] = { + .type = VCAP_FIELD_BIT, + .offset = 112, + .width = 1, + }, + [VCAP_KF_8021Q_PCP_CLS] = { + .type = VCAP_FIELD_U32, + .offset = 113, + .width = 3, + }, + [VCAP_KF_L2_FWD_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 116, + .width = 1, + }, + [VCAP_KF_L3_SMAC_SIP_MATCH] = { + .type = VCAP_FIELD_BIT, + .offset = 117, + .width = 1, + }, + [VCAP_KF_L3_DMAC_DIP_MATCH] = { + .type = VCAP_FIELD_BIT, + .offset = 118, + .width = 1, + }, + [VCAP_KF_L3_RT_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 119, + .width = 1, + }, + [VCAP_KF_L3_DST_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 120, + .width = 1, + }, + [VCAP_KF_L2_DMAC] = { + .type = VCAP_FIELD_U48, + .offset = 121, + .width = 48, + }, + [VCAP_KF_L2_SMAC] = { + .type = VCAP_FIELD_U48, + .offset = 169, + .width = 48, + }, + [VCAP_KF_IP4_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 217, + .width = 1, + }, + [VCAP_KF_L3_TTL_GT0] = { + .type = VCAP_FIELD_BIT, + .offset = 218, + .width = 1, + }, + [VCAP_KF_L3_TOS] = { + .type = VCAP_FIELD_U32, + .offset = 219, + .width = 8, + }, + [VCAP_KF_L3_IP6_DIP] = { + .type = VCAP_FIELD_U128, + .offset = 227, + .width = 128, + }, + [VCAP_KF_L3_IP6_SIP] = { + .type = VCAP_FIELD_U128, + .offset = 355, + .width = 128, + }, + [VCAP_KF_L3_DIP_EQ_SIP_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 483, + .width = 1, + }, + [VCAP_KF_TCP_UDP_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 484, + .width = 1, + }, + [VCAP_KF_TCP_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 485, + .width = 1, + }, + [VCAP_KF_L4_DPORT] = { + .type = VCAP_FIELD_U32, + .offset = 486, + .width = 16, + }, + [VCAP_KF_L4_SPORT] = { + .type = VCAP_FIELD_U32, + .offset = 502, + .width = 16, + }, + [VCAP_KF_L4_RNG] = { + .type = VCAP_FIELD_U32, + .offset = 518, + .width = 16, + }, + [VCAP_KF_L4_SPORT_EQ_DPORT_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 534, + .width = 1, + }, + [VCAP_KF_L4_SEQUENCE_EQ0_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 535, + .width = 1, + }, + [VCAP_KF_L4_FIN] = { + .type = VCAP_FIELD_BIT, + .offset = 536, + .width = 1, + }, + [VCAP_KF_L4_SYN] = { + .type = VCAP_FIELD_BIT, + .offset = 537, + .width = 1, + }, + [VCAP_KF_L4_RST] = { + .type = VCAP_FIELD_BIT, + .offset = 538, + .width = 1, + }, + [VCAP_KF_L4_PSH] = { + .type = VCAP_FIELD_BIT, + .offset = 539, + .width = 1, + }, + [VCAP_KF_L4_ACK] = { + .type = VCAP_FIELD_BIT, + .offset = 540, + .width = 1, + }, + [VCAP_KF_L4_URG] = { + .type = VCAP_FIELD_BIT, + .offset = 541, + .width = 1, + }, + [VCAP_KF_L4_PAYLOAD] = { + .type = VCAP_FIELD_U64, + .offset = 542, + .width = 64, + }, +}; + +static const struct vcap_field is2_ip6_vid_keyfield[] = { + [VCAP_KF_TYPE] = { + .type = VCAP_FIELD_U32, + .offset = 0, + .width = 4, + }, + [VCAP_KF_LOOKUP_FIRST_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 4, + .width = 1, + }, + [VCAP_KF_LOOKUP_PAG] = { + .type = VCAP_FIELD_U32, + .offset = 5, + .width = 8, + }, + [VCAP_KF_ISDX_GT0_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 13, + .width = 1, + }, + [VCAP_KF_ISDX_CLS] = { + .type = VCAP_FIELD_U32, + .offset = 14, + .width = 12, + }, + [VCAP_KF_8021Q_VID_CLS] = { + .type = VCAP_FIELD_U32, + .offset = 26, + .width = 13, + }, + [VCAP_KF_L3_SMAC_SIP_MATCH] = { + .type = VCAP_FIELD_BIT, + .offset = 39, + .width = 1, + }, + [VCAP_KF_L3_DMAC_DIP_MATCH] = { + .type = VCAP_FIELD_BIT, + .offset = 40, + .width = 1, + }, + [VCAP_KF_L3_RT_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 41, + .width = 1, + }, + [VCAP_KF_L3_DST_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 42, + .width = 1, + }, + [VCAP_KF_L3_IP6_DIP] = { + .type = VCAP_FIELD_U128, + .offset = 43, + .width = 128, + }, + [VCAP_KF_L3_IP6_SIP] = { + .type = VCAP_FIELD_U128, + .offset = 171, + .width = 128, + }, +}; + +static const struct vcap_field es2_mac_etype_keyfield[] = { + [VCAP_KF_TYPE] = { + .type = VCAP_FIELD_U32, + .offset = 0, + .width = 3, + }, + [VCAP_KF_LOOKUP_FIRST_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 3, + .width = 1, + }, + [VCAP_KF_ACL_GRP_ID] = { + .type = VCAP_FIELD_U32, + .offset = 4, + .width = 8, + }, + [VCAP_KF_PROT_ACTIVE] = { + .type = VCAP_FIELD_BIT, + .offset = 12, + .width = 1, + }, + [VCAP_KF_L2_MC_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 13, + .width = 1, + }, + [VCAP_KF_L2_BC_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 14, + .width = 1, + }, + [VCAP_KF_ISDX_GT0_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 15, + .width = 1, + }, + [VCAP_KF_ISDX_CLS] = { + .type = VCAP_FIELD_U32, + .offset = 16, + .width = 12, + }, + [VCAP_KF_8021Q_VLAN_TAGGED_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 28, + .width = 1, + }, + [VCAP_KF_8021Q_VID_CLS] = { + .type = VCAP_FIELD_U32, + .offset = 29, + .width = 13, + }, + [VCAP_KF_IF_EGR_PORT_MASK_RNG] = { + .type = VCAP_FIELD_U32, + .offset = 42, + .width = 3, + }, + [VCAP_KF_IF_EGR_PORT_MASK] = { + .type = VCAP_FIELD_U32, + .offset = 45, + .width = 32, + }, + [VCAP_KF_IF_IGR_PORT_SEL] = { + .type = VCAP_FIELD_BIT, + .offset = 77, + .width = 1, + }, + [VCAP_KF_IF_IGR_PORT] = { + .type = VCAP_FIELD_U32, + .offset = 78, + .width = 9, + }, + [VCAP_KF_8021Q_PCP_CLS] = { + .type = VCAP_FIELD_U32, + .offset = 87, + .width = 3, + }, + [VCAP_KF_8021Q_DEI_CLS] = { + .type = VCAP_FIELD_BIT, + .offset = 90, + .width = 1, + }, + [VCAP_KF_COSID_CLS] = { + .type = VCAP_FIELD_U32, + .offset = 91, + .width = 3, + }, + [VCAP_KF_L3_DPL_CLS] = { + .type = VCAP_FIELD_BIT, + .offset = 94, + .width = 1, + }, + [VCAP_KF_L3_RT_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 95, + .width = 1, + }, + [VCAP_KF_ES0_ISDX_KEY_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 96, + .width = 1, + }, + [VCAP_KF_MIRROR_ENA] = { + .type = VCAP_FIELD_U32, + .offset = 97, + .width = 2, + }, + [VCAP_KF_L2_DMAC] = { + .type = VCAP_FIELD_U48, + .offset = 99, + .width = 48, + }, + [VCAP_KF_L2_SMAC] = { + .type = VCAP_FIELD_U48, + .offset = 147, + .width = 48, + }, + [VCAP_KF_ETYPE_LEN_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 195, + .width = 1, + }, + [VCAP_KF_ETYPE] = { + .type = VCAP_FIELD_U32, + .offset = 196, + .width = 16, + }, + [VCAP_KF_L2_PAYLOAD_ETYPE] = { + .type = VCAP_FIELD_U64, + .offset = 212, + .width = 64, + }, + [VCAP_KF_OAM_CCM_CNTS_EQ0] = { + .type = VCAP_FIELD_BIT, + .offset = 276, + .width = 1, + }, + [VCAP_KF_OAM_Y1731_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 277, + .width = 1, + }, +}; + +static const struct vcap_field es2_arp_keyfield[] = { + [VCAP_KF_TYPE] = { + .type = VCAP_FIELD_U32, + .offset = 0, + .width = 3, + }, + [VCAP_KF_LOOKUP_FIRST_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 3, + .width = 1, + }, + [VCAP_KF_ACL_GRP_ID] = { + .type = VCAP_FIELD_U32, + .offset = 4, + .width = 8, + }, + [VCAP_KF_PROT_ACTIVE] = { + .type = VCAP_FIELD_BIT, + .offset = 12, + .width = 1, + }, + [VCAP_KF_L2_MC_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 13, + .width = 1, + }, + [VCAP_KF_L2_BC_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 14, + .width = 1, + }, + [VCAP_KF_ISDX_GT0_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 15, + .width = 1, + }, + [VCAP_KF_ISDX_CLS] = { + .type = VCAP_FIELD_U32, + .offset = 16, + .width = 12, + }, + [VCAP_KF_8021Q_VLAN_TAGGED_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 28, + .width = 1, + }, + [VCAP_KF_8021Q_VID_CLS] = { + .type = VCAP_FIELD_U32, + .offset = 29, + .width = 13, + }, + [VCAP_KF_IF_EGR_PORT_MASK_RNG] = { + .type = VCAP_FIELD_U32, + .offset = 42, + .width = 3, + }, + [VCAP_KF_IF_EGR_PORT_MASK] = { + .type = VCAP_FIELD_U32, + .offset = 45, + .width = 32, + }, + [VCAP_KF_IF_IGR_PORT_SEL] = { + .type = VCAP_FIELD_BIT, + .offset = 77, + .width = 1, + }, + [VCAP_KF_IF_IGR_PORT] = { + .type = VCAP_FIELD_U32, + .offset = 78, + .width = 9, + }, + [VCAP_KF_8021Q_PCP_CLS] = { + .type = VCAP_FIELD_U32, + .offset = 87, + .width = 3, + }, + [VCAP_KF_8021Q_DEI_CLS] = { + .type = VCAP_FIELD_BIT, + .offset = 90, + .width = 1, + }, + [VCAP_KF_COSID_CLS] = { + .type = VCAP_FIELD_U32, + .offset = 91, + .width = 3, + }, + [VCAP_KF_L3_DPL_CLS] = { + .type = VCAP_FIELD_BIT, + .offset = 94, + .width = 1, + }, + [VCAP_KF_ES0_ISDX_KEY_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 95, + .width = 1, + }, + [VCAP_KF_MIRROR_ENA] = { + .type = VCAP_FIELD_U32, + .offset = 96, + .width = 2, + }, + [VCAP_KF_L2_SMAC] = { + .type = VCAP_FIELD_U48, + .offset = 98, + .width = 48, + }, + [VCAP_KF_ARP_ADDR_SPACE_OK_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 146, + .width = 1, + }, + [VCAP_KF_ARP_PROTO_SPACE_OK_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 147, + .width = 1, + }, + [VCAP_KF_ARP_LEN_OK_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 148, + .width = 1, + }, + [VCAP_KF_ARP_TGT_MATCH_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 149, + .width = 1, + }, + [VCAP_KF_ARP_SENDER_MATCH_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 150, + .width = 1, + }, + [VCAP_KF_ARP_OPCODE_UNKNOWN_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 151, + .width = 1, + }, + [VCAP_KF_ARP_OPCODE] = { + .type = VCAP_FIELD_U32, + .offset = 152, + .width = 2, + }, + [VCAP_KF_L3_IP4_DIP] = { + .type = VCAP_FIELD_U32, + .offset = 154, + .width = 32, + }, + [VCAP_KF_L3_IP4_SIP] = { + .type = VCAP_FIELD_U32, + .offset = 186, + .width = 32, + }, + [VCAP_KF_L3_DIP_EQ_SIP_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 218, + .width = 1, + }, +}; + +static const struct vcap_field es2_ip4_tcp_udp_keyfield[] = { + [VCAP_KF_TYPE] = { + .type = VCAP_FIELD_U32, + .offset = 0, + .width = 3, + }, + [VCAP_KF_LOOKUP_FIRST_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 3, + .width = 1, + }, + [VCAP_KF_ACL_GRP_ID] = { + .type = VCAP_FIELD_U32, + .offset = 4, + .width = 8, + }, + [VCAP_KF_PROT_ACTIVE] = { + .type = VCAP_FIELD_BIT, + .offset = 12, + .width = 1, + }, + [VCAP_KF_L2_MC_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 13, + .width = 1, + }, + [VCAP_KF_L2_BC_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 14, + .width = 1, + }, + [VCAP_KF_ISDX_GT0_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 15, + .width = 1, + }, + [VCAP_KF_ISDX_CLS] = { + .type = VCAP_FIELD_U32, + .offset = 16, + .width = 12, + }, + [VCAP_KF_8021Q_VLAN_TAGGED_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 28, + .width = 1, + }, + [VCAP_KF_8021Q_VID_CLS] = { + .type = VCAP_FIELD_U32, + .offset = 29, + .width = 13, + }, + [VCAP_KF_IF_EGR_PORT_MASK_RNG] = { + .type = VCAP_FIELD_U32, + .offset = 42, + .width = 3, + }, + [VCAP_KF_IF_EGR_PORT_MASK] = { + .type = VCAP_FIELD_U32, + .offset = 45, + .width = 32, + }, + [VCAP_KF_IF_IGR_PORT_SEL] = { + .type = VCAP_FIELD_BIT, + .offset = 77, + .width = 1, + }, + [VCAP_KF_IF_IGR_PORT] = { + .type = VCAP_FIELD_U32, + .offset = 78, + .width = 9, + }, + [VCAP_KF_8021Q_PCP_CLS] = { + .type = VCAP_FIELD_U32, + .offset = 87, + .width = 3, + }, + [VCAP_KF_8021Q_DEI_CLS] = { + .type = VCAP_FIELD_BIT, + .offset = 90, + .width = 1, + }, + [VCAP_KF_COSID_CLS] = { + .type = VCAP_FIELD_U32, + .offset = 91, + .width = 3, + }, + [VCAP_KF_L3_DPL_CLS] = { + .type = VCAP_FIELD_BIT, + .offset = 94, + .width = 1, + }, + [VCAP_KF_L3_RT_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 95, + .width = 1, + }, + [VCAP_KF_ES0_ISDX_KEY_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 96, + .width = 1, + }, + [VCAP_KF_MIRROR_ENA] = { + .type = VCAP_FIELD_U32, + .offset = 97, + .width = 2, + }, + [VCAP_KF_IP4_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 99, + .width = 1, + }, + [VCAP_KF_L3_FRAGMENT_TYPE] = { + .type = VCAP_FIELD_U32, + .offset = 100, + .width = 2, + }, + [VCAP_KF_L3_OPTIONS_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 102, + .width = 1, + }, + [VCAP_KF_L3_TTL_GT0] = { + .type = VCAP_FIELD_BIT, + .offset = 103, + .width = 1, + }, + [VCAP_KF_L3_TOS] = { + .type = VCAP_FIELD_U32, + .offset = 104, + .width = 8, + }, + [VCAP_KF_L3_IP4_DIP] = { + .type = VCAP_FIELD_U32, + .offset = 112, + .width = 32, + }, + [VCAP_KF_L3_IP4_SIP] = { + .type = VCAP_FIELD_U32, + .offset = 144, + .width = 32, + }, + [VCAP_KF_L3_DIP_EQ_SIP_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 176, + .width = 1, + }, + [VCAP_KF_TCP_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 177, + .width = 1, + }, + [VCAP_KF_L4_DPORT] = { + .type = VCAP_FIELD_U32, + .offset = 178, + .width = 16, + }, + [VCAP_KF_L4_SPORT] = { + .type = VCAP_FIELD_U32, + .offset = 194, + .width = 16, + }, + [VCAP_KF_L4_RNG] = { + .type = VCAP_FIELD_U32, + .offset = 210, + .width = 16, + }, + [VCAP_KF_L4_SPORT_EQ_DPORT_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 226, + .width = 1, + }, + [VCAP_KF_L4_SEQUENCE_EQ0_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 227, + .width = 1, + }, + [VCAP_KF_L4_FIN] = { + .type = VCAP_FIELD_BIT, + .offset = 228, + .width = 1, + }, + [VCAP_KF_L4_SYN] = { + .type = VCAP_FIELD_BIT, + .offset = 229, + .width = 1, + }, + [VCAP_KF_L4_RST] = { + .type = VCAP_FIELD_BIT, + .offset = 230, + .width = 1, + }, + [VCAP_KF_L4_PSH] = { + .type = VCAP_FIELD_BIT, + .offset = 231, + .width = 1, + }, + [VCAP_KF_L4_ACK] = { + .type = VCAP_FIELD_BIT, + .offset = 232, + .width = 1, + }, + [VCAP_KF_L4_URG] = { + .type = VCAP_FIELD_BIT, + .offset = 233, + .width = 1, + }, + [VCAP_KF_L4_PAYLOAD] = { + .type = VCAP_FIELD_U64, + .offset = 234, + .width = 64, + }, +}; + +static const struct vcap_field es2_ip4_other_keyfield[] = { + [VCAP_KF_TYPE] = { + .type = VCAP_FIELD_U32, + .offset = 0, + .width = 3, + }, + [VCAP_KF_LOOKUP_FIRST_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 3, + .width = 1, + }, + [VCAP_KF_ACL_GRP_ID] = { + .type = VCAP_FIELD_U32, + .offset = 4, + .width = 8, + }, + [VCAP_KF_PROT_ACTIVE] = { + .type = VCAP_FIELD_BIT, + .offset = 12, + .width = 1, + }, + [VCAP_KF_L2_MC_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 13, + .width = 1, + }, + [VCAP_KF_L2_BC_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 14, + .width = 1, + }, + [VCAP_KF_ISDX_GT0_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 15, + .width = 1, + }, + [VCAP_KF_ISDX_CLS] = { + .type = VCAP_FIELD_U32, + .offset = 16, + .width = 12, + }, + [VCAP_KF_8021Q_VLAN_TAGGED_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 28, + .width = 1, + }, + [VCAP_KF_8021Q_VID_CLS] = { + .type = VCAP_FIELD_U32, + .offset = 29, + .width = 13, + }, + [VCAP_KF_IF_EGR_PORT_MASK_RNG] = { + .type = VCAP_FIELD_U32, + .offset = 42, + .width = 3, + }, + [VCAP_KF_IF_EGR_PORT_MASK] = { + .type = VCAP_FIELD_U32, + .offset = 45, + .width = 32, + }, + [VCAP_KF_IF_IGR_PORT_SEL] = { + .type = VCAP_FIELD_BIT, + .offset = 77, + .width = 1, + }, + [VCAP_KF_IF_IGR_PORT] = { + .type = VCAP_FIELD_U32, + .offset = 78, + .width = 9, + }, + [VCAP_KF_8021Q_PCP_CLS] = { + .type = VCAP_FIELD_U32, + .offset = 87, + .width = 3, + }, + [VCAP_KF_8021Q_DEI_CLS] = { + .type = VCAP_FIELD_BIT, + .offset = 90, + .width = 1, + }, + [VCAP_KF_COSID_CLS] = { + .type = VCAP_FIELD_U32, + .offset = 91, + .width = 3, + }, + [VCAP_KF_L3_DPL_CLS] = { + .type = VCAP_FIELD_BIT, + .offset = 94, + .width = 1, + }, + [VCAP_KF_L3_RT_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 95, + .width = 1, + }, + [VCAP_KF_ES0_ISDX_KEY_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 96, + .width = 1, + }, + [VCAP_KF_MIRROR_ENA] = { + .type = VCAP_FIELD_U32, + .offset = 97, + .width = 2, + }, + [VCAP_KF_IP4_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 99, + .width = 1, + }, + [VCAP_KF_L3_FRAGMENT_TYPE] = { + .type = VCAP_FIELD_U32, + .offset = 100, + .width = 2, + }, + [VCAP_KF_L3_OPTIONS_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 102, + .width = 1, + }, + [VCAP_KF_L3_TTL_GT0] = { + .type = VCAP_FIELD_BIT, + .offset = 103, + .width = 1, + }, + [VCAP_KF_L3_TOS] = { + .type = VCAP_FIELD_U32, + .offset = 104, + .width = 8, + }, + [VCAP_KF_L3_IP4_DIP] = { + .type = VCAP_FIELD_U32, + .offset = 112, + .width = 32, + }, + [VCAP_KF_L3_IP4_SIP] = { + .type = VCAP_FIELD_U32, + .offset = 144, + .width = 32, + }, + [VCAP_KF_L3_DIP_EQ_SIP_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 176, + .width = 1, + }, + [VCAP_KF_L3_IP_PROTO] = { + .type = VCAP_FIELD_U32, + .offset = 177, + .width = 8, + }, + [VCAP_KF_L3_PAYLOAD] = { + .type = VCAP_FIELD_U112, + .offset = 185, + .width = 96, + }, +}; + +static const struct vcap_field es2_ip_7tuple_keyfield[] = { + [VCAP_KF_LOOKUP_FIRST_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 0, + .width = 1, + }, + [VCAP_KF_ACL_GRP_ID] = { + .type = VCAP_FIELD_U32, + .offset = 1, + .width = 8, + }, + [VCAP_KF_PROT_ACTIVE] = { + .type = VCAP_FIELD_BIT, + .offset = 9, + .width = 1, + }, + [VCAP_KF_L2_MC_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 10, + .width = 1, + }, + [VCAP_KF_L2_BC_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 11, + .width = 1, + }, + [VCAP_KF_ISDX_GT0_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 12, + .width = 1, + }, + [VCAP_KF_ISDX_CLS] = { + .type = VCAP_FIELD_U32, + .offset = 13, + .width = 12, + }, + [VCAP_KF_8021Q_VLAN_TAGGED_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 25, + .width = 1, + }, + [VCAP_KF_8021Q_VID_CLS] = { + .type = VCAP_FIELD_U32, + .offset = 26, + .width = 13, + }, + [VCAP_KF_IF_EGR_PORT_MASK_RNG] = { + .type = VCAP_FIELD_U32, + .offset = 39, + .width = 3, + }, + [VCAP_KF_IF_EGR_PORT_MASK] = { + .type = VCAP_FIELD_U32, + .offset = 42, + .width = 32, + }, + [VCAP_KF_IF_IGR_PORT_SEL] = { + .type = VCAP_FIELD_BIT, + .offset = 74, + .width = 1, + }, + [VCAP_KF_IF_IGR_PORT] = { + .type = VCAP_FIELD_U32, + .offset = 75, + .width = 9, + }, + [VCAP_KF_8021Q_PCP_CLS] = { + .type = VCAP_FIELD_U32, + .offset = 84, + .width = 3, + }, + [VCAP_KF_8021Q_DEI_CLS] = { + .type = VCAP_FIELD_BIT, + .offset = 87, + .width = 1, + }, + [VCAP_KF_COSID_CLS] = { + .type = VCAP_FIELD_U32, + .offset = 88, + .width = 3, + }, + [VCAP_KF_L3_DPL_CLS] = { + .type = VCAP_FIELD_BIT, + .offset = 91, + .width = 1, + }, + [VCAP_KF_L3_RT_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 92, + .width = 1, + }, + [VCAP_KF_ES0_ISDX_KEY_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 93, + .width = 1, + }, + [VCAP_KF_MIRROR_ENA] = { + .type = VCAP_FIELD_U32, + .offset = 94, + .width = 2, + }, + [VCAP_KF_L2_DMAC] = { + .type = VCAP_FIELD_U48, + .offset = 96, + .width = 48, + }, + [VCAP_KF_L2_SMAC] = { + .type = VCAP_FIELD_U48, + .offset = 144, + .width = 48, + }, + [VCAP_KF_IP4_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 192, + .width = 1, + }, + [VCAP_KF_L3_TTL_GT0] = { + .type = VCAP_FIELD_BIT, + .offset = 193, + .width = 1, + }, + [VCAP_KF_L3_TOS] = { + .type = VCAP_FIELD_U32, + .offset = 194, + .width = 8, + }, + [VCAP_KF_L3_IP6_DIP] = { + .type = VCAP_FIELD_U128, + .offset = 202, + .width = 128, + }, + [VCAP_KF_L3_IP6_SIP] = { + .type = VCAP_FIELD_U128, + .offset = 330, + .width = 128, + }, + [VCAP_KF_L3_DIP_EQ_SIP_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 458, + .width = 1, + }, + [VCAP_KF_TCP_UDP_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 459, + .width = 1, + }, + [VCAP_KF_TCP_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 460, + .width = 1, + }, + [VCAP_KF_L4_DPORT] = { + .type = VCAP_FIELD_U32, + .offset = 461, + .width = 16, + }, + [VCAP_KF_L4_SPORT] = { + .type = VCAP_FIELD_U32, + .offset = 477, + .width = 16, + }, + [VCAP_KF_L4_RNG] = { + .type = VCAP_FIELD_U32, + .offset = 493, + .width = 16, + }, + [VCAP_KF_L4_SPORT_EQ_DPORT_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 509, + .width = 1, + }, + [VCAP_KF_L4_SEQUENCE_EQ0_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 510, + .width = 1, + }, + [VCAP_KF_L4_FIN] = { + .type = VCAP_FIELD_BIT, + .offset = 511, + .width = 1, + }, + [VCAP_KF_L4_SYN] = { + .type = VCAP_FIELD_BIT, + .offset = 512, + .width = 1, + }, + [VCAP_KF_L4_RST] = { + .type = VCAP_FIELD_BIT, + .offset = 513, + .width = 1, + }, + [VCAP_KF_L4_PSH] = { + .type = VCAP_FIELD_BIT, + .offset = 514, + .width = 1, + }, + [VCAP_KF_L4_ACK] = { + .type = VCAP_FIELD_BIT, + .offset = 515, + .width = 1, + }, + [VCAP_KF_L4_URG] = { + .type = VCAP_FIELD_BIT, + .offset = 516, + .width = 1, + }, + [VCAP_KF_L4_PAYLOAD] = { + .type = VCAP_FIELD_U64, + .offset = 517, + .width = 64, + }, +}; + +static const struct vcap_field es2_ip4_vid_keyfield[] = { + [VCAP_KF_LOOKUP_FIRST_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 0, + .width = 1, + }, + [VCAP_KF_ACL_GRP_ID] = { + .type = VCAP_FIELD_U32, + .offset = 1, + .width = 8, + }, + [VCAP_KF_PROT_ACTIVE] = { + .type = VCAP_FIELD_BIT, + .offset = 9, + .width = 1, + }, + [VCAP_KF_L2_MC_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 10, + .width = 1, + }, + [VCAP_KF_L2_BC_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 11, + .width = 1, + }, + [VCAP_KF_ISDX_GT0_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 12, + .width = 1, + }, + [VCAP_KF_ISDX_CLS] = { + .type = VCAP_FIELD_U32, + .offset = 13, + .width = 12, + }, + [VCAP_KF_8021Q_VLAN_TAGGED_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 25, + .width = 1, + }, + [VCAP_KF_8021Q_VID_CLS] = { + .type = VCAP_FIELD_U32, + .offset = 26, + .width = 13, + }, + [VCAP_KF_8021Q_PCP_CLS] = { + .type = VCAP_FIELD_U32, + .offset = 39, + .width = 3, + }, + [VCAP_KF_8021Q_DEI_CLS] = { + .type = VCAP_FIELD_BIT, + .offset = 42, + .width = 1, + }, + [VCAP_KF_COSID_CLS] = { + .type = VCAP_FIELD_U32, + .offset = 43, + .width = 3, + }, + [VCAP_KF_L3_DPL_CLS] = { + .type = VCAP_FIELD_BIT, + .offset = 46, + .width = 1, + }, + [VCAP_KF_L3_RT_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 47, + .width = 1, + }, + [VCAP_KF_ES0_ISDX_KEY_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 48, + .width = 1, + }, + [VCAP_KF_MIRROR_ENA] = { + .type = VCAP_FIELD_U32, + .offset = 49, + .width = 2, + }, + [VCAP_KF_IP4_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 51, + .width = 1, + }, + [VCAP_KF_L3_IP4_DIP] = { + .type = VCAP_FIELD_U32, + .offset = 52, + .width = 32, + }, + [VCAP_KF_L3_IP4_SIP] = { + .type = VCAP_FIELD_U32, + .offset = 84, + .width = 32, + }, + [VCAP_KF_L4_RNG] = { + .type = VCAP_FIELD_U32, + .offset = 116, + .width = 16, + }, +}; + +static const struct vcap_field es2_ip6_vid_keyfield[] = { + [VCAP_KF_TYPE] = { + .type = VCAP_FIELD_U32, + .offset = 0, + .width = 3, + }, + [VCAP_KF_LOOKUP_FIRST_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 3, + .width = 1, + }, + [VCAP_KF_ACL_GRP_ID] = { + .type = VCAP_FIELD_U32, + .offset = 4, + .width = 8, + }, + [VCAP_KF_PROT_ACTIVE] = { + .type = VCAP_FIELD_BIT, + .offset = 12, + .width = 1, + }, + [VCAP_KF_L2_MC_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 13, + .width = 1, + }, + [VCAP_KF_L2_BC_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 14, + .width = 1, + }, + [VCAP_KF_ISDX_GT0_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 15, + .width = 1, + }, + [VCAP_KF_ISDX_CLS] = { + .type = VCAP_FIELD_U32, + .offset = 16, + .width = 12, + }, + [VCAP_KF_8021Q_VLAN_TAGGED_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 28, + .width = 1, + }, + [VCAP_KF_8021Q_VID_CLS] = { + .type = VCAP_FIELD_U32, + .offset = 29, + .width = 13, + }, + [VCAP_KF_L3_RT_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 42, + .width = 1, + }, + [VCAP_KF_L3_IP6_DIP] = { + .type = VCAP_FIELD_U128, + .offset = 43, + .width = 128, + }, + [VCAP_KF_L3_IP6_SIP] = { + .type = VCAP_FIELD_U128, + .offset = 171, + .width = 128, + }, +}; + +/* keyfield_set */ +static const struct vcap_set is0_keyfield_set[] = { + [VCAP_KFS_MLL] = { + .type_id = 0, + .sw_per_item = 3, + .sw_cnt = 4, + }, + [VCAP_KFS_TRI_VID] = { + .type_id = 0, + .sw_per_item = 2, + .sw_cnt = 6, + }, + [VCAP_KFS_LL_FULL] = { + .type_id = 0, + .sw_per_item = 6, + .sw_cnt = 2, + }, + [VCAP_KFS_NORMAL] = { + .type_id = 1, + .sw_per_item = 6, + .sw_cnt = 2, + }, + [VCAP_KFS_NORMAL_7TUPLE] = { + .type_id = 0, + .sw_per_item = 12, + .sw_cnt = 1, + }, + [VCAP_KFS_NORMAL_5TUPLE_IP4] = { + .type_id = 2, + .sw_per_item = 6, + .sw_cnt = 2, + }, + [VCAP_KFS_PURE_5TUPLE_IP4] = { + .type_id = 2, + .sw_per_item = 3, + .sw_cnt = 4, + }, + [VCAP_KFS_ETAG] = { + .type_id = 3, + .sw_per_item = 2, + .sw_cnt = 6, + }, +}; + +static const struct vcap_set is2_keyfield_set[] = { + [VCAP_KFS_MAC_ETYPE] = { + .type_id = 0, + .sw_per_item = 6, + .sw_cnt = 2, + }, + [VCAP_KFS_ARP] = { + .type_id = 3, + .sw_per_item = 6, + .sw_cnt = 2, + }, + [VCAP_KFS_IP4_TCP_UDP] = { + .type_id = 4, + .sw_per_item = 6, + .sw_cnt = 2, + }, + [VCAP_KFS_IP4_OTHER] = { + .type_id = 5, + .sw_per_item = 6, + .sw_cnt = 2, + }, + [VCAP_KFS_IP6_STD] = { + .type_id = 6, + .sw_per_item = 6, + .sw_cnt = 2, + }, + [VCAP_KFS_IP_7TUPLE] = { + .type_id = 1, + .sw_per_item = 12, + .sw_cnt = 1, + }, + [VCAP_KFS_IP6_VID] = { + .type_id = 9, + .sw_per_item = 6, + .sw_cnt = 2, + }, +}; + +static const struct vcap_set es2_keyfield_set[] = { + [VCAP_KFS_MAC_ETYPE] = { + .type_id = 0, + .sw_per_item = 6, + .sw_cnt = 2, + }, + [VCAP_KFS_ARP] = { + .type_id = 1, + .sw_per_item = 6, + .sw_cnt = 2, + }, + [VCAP_KFS_IP4_TCP_UDP] = { + .type_id = 2, + .sw_per_item = 6, + .sw_cnt = 2, + }, + [VCAP_KFS_IP4_OTHER] = { + .type_id = 3, + .sw_per_item = 6, + .sw_cnt = 2, + }, + [VCAP_KFS_IP_7TUPLE] = { + .type_id = -1, + .sw_per_item = 12, + .sw_cnt = 1, + }, + [VCAP_KFS_IP4_VID] = { + .type_id = -1, + .sw_per_item = 3, + .sw_cnt = 4, + }, + [VCAP_KFS_IP6_VID] = { + .type_id = 5, + .sw_per_item = 6, + .sw_cnt = 2, + }, +}; + +/* keyfield_set map */ +static const struct vcap_field *is0_keyfield_set_map[] = { + [VCAP_KFS_MLL] = is0_mll_keyfield, + [VCAP_KFS_TRI_VID] = is0_tri_vid_keyfield, + [VCAP_KFS_LL_FULL] = is0_ll_full_keyfield, + [VCAP_KFS_NORMAL] = is0_normal_keyfield, + [VCAP_KFS_NORMAL_7TUPLE] = is0_normal_7tuple_keyfield, + [VCAP_KFS_NORMAL_5TUPLE_IP4] = is0_normal_5tuple_ip4_keyfield, + [VCAP_KFS_PURE_5TUPLE_IP4] = is0_pure_5tuple_ip4_keyfield, + [VCAP_KFS_ETAG] = is0_etag_keyfield, +}; + +static const struct vcap_field *is2_keyfield_set_map[] = { + [VCAP_KFS_MAC_ETYPE] = is2_mac_etype_keyfield, + [VCAP_KFS_ARP] = is2_arp_keyfield, + [VCAP_KFS_IP4_TCP_UDP] = is2_ip4_tcp_udp_keyfield, + [VCAP_KFS_IP4_OTHER] = is2_ip4_other_keyfield, + [VCAP_KFS_IP6_STD] = is2_ip6_std_keyfield, + [VCAP_KFS_IP_7TUPLE] = is2_ip_7tuple_keyfield, + [VCAP_KFS_IP6_VID] = is2_ip6_vid_keyfield, +}; + +static const struct vcap_field *es2_keyfield_set_map[] = { + [VCAP_KFS_MAC_ETYPE] = es2_mac_etype_keyfield, + [VCAP_KFS_ARP] = es2_arp_keyfield, + [VCAP_KFS_IP4_TCP_UDP] = es2_ip4_tcp_udp_keyfield, + [VCAP_KFS_IP4_OTHER] = es2_ip4_other_keyfield, + [VCAP_KFS_IP_7TUPLE] = es2_ip_7tuple_keyfield, + [VCAP_KFS_IP4_VID] = es2_ip4_vid_keyfield, + [VCAP_KFS_IP6_VID] = es2_ip6_vid_keyfield, +}; + +/* keyfield_set map sizes */ +static int is0_keyfield_set_map_size[] = { + [VCAP_KFS_MLL] = ARRAY_SIZE(is0_mll_keyfield), + [VCAP_KFS_TRI_VID] = ARRAY_SIZE(is0_tri_vid_keyfield), + [VCAP_KFS_LL_FULL] = ARRAY_SIZE(is0_ll_full_keyfield), + [VCAP_KFS_NORMAL] = ARRAY_SIZE(is0_normal_keyfield), + [VCAP_KFS_NORMAL_7TUPLE] = ARRAY_SIZE(is0_normal_7tuple_keyfield), + [VCAP_KFS_NORMAL_5TUPLE_IP4] = ARRAY_SIZE(is0_normal_5tuple_ip4_keyfield), + [VCAP_KFS_PURE_5TUPLE_IP4] = ARRAY_SIZE(is0_pure_5tuple_ip4_keyfield), + [VCAP_KFS_ETAG] = ARRAY_SIZE(is0_etag_keyfield), +}; + +static int is2_keyfield_set_map_size[] = { + [VCAP_KFS_MAC_ETYPE] = ARRAY_SIZE(is2_mac_etype_keyfield), + [VCAP_KFS_ARP] = ARRAY_SIZE(is2_arp_keyfield), + [VCAP_KFS_IP4_TCP_UDP] = ARRAY_SIZE(is2_ip4_tcp_udp_keyfield), + [VCAP_KFS_IP4_OTHER] = ARRAY_SIZE(is2_ip4_other_keyfield), + [VCAP_KFS_IP6_STD] = ARRAY_SIZE(is2_ip6_std_keyfield), + [VCAP_KFS_IP_7TUPLE] = ARRAY_SIZE(is2_ip_7tuple_keyfield), + [VCAP_KFS_IP6_VID] = ARRAY_SIZE(is2_ip6_vid_keyfield), +}; + +static int es2_keyfield_set_map_size[] = { + [VCAP_KFS_MAC_ETYPE] = ARRAY_SIZE(es2_mac_etype_keyfield), + [VCAP_KFS_ARP] = ARRAY_SIZE(es2_arp_keyfield), + [VCAP_KFS_IP4_TCP_UDP] = ARRAY_SIZE(es2_ip4_tcp_udp_keyfield), + [VCAP_KFS_IP4_OTHER] = ARRAY_SIZE(es2_ip4_other_keyfield), + [VCAP_KFS_IP_7TUPLE] = ARRAY_SIZE(es2_ip_7tuple_keyfield), + [VCAP_KFS_IP4_VID] = ARRAY_SIZE(es2_ip4_vid_keyfield), + [VCAP_KFS_IP6_VID] = ARRAY_SIZE(es2_ip6_vid_keyfield), +}; + +/* actionfields */ +static const struct vcap_field is0_mlbs_actionfield[] = { + [VCAP_AF_TYPE] = { + .type = VCAP_FIELD_BIT, + .offset = 0, + .width = 1, + }, + [VCAP_AF_COSID_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 1, + .width = 1, + }, + [VCAP_AF_COSID_VAL] = { + .type = VCAP_FIELD_U32, + .offset = 2, + .width = 3, + }, + [VCAP_AF_QOS_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 5, + .width = 1, + }, + [VCAP_AF_QOS_VAL] = { + .type = VCAP_FIELD_U32, + .offset = 6, + .width = 3, + }, + [VCAP_AF_DP_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 9, + .width = 1, + }, + [VCAP_AF_DP_VAL] = { + .type = VCAP_FIELD_U32, + .offset = 10, + .width = 2, + }, + [VCAP_AF_MAP_LOOKUP_SEL] = { + .type = VCAP_FIELD_U32, + .offset = 12, + .width = 2, + }, + [VCAP_AF_MAP_KEY] = { + .type = VCAP_FIELD_U32, + .offset = 14, + .width = 3, + }, + [VCAP_AF_MAP_IDX] = { + .type = VCAP_FIELD_U32, + .offset = 17, + .width = 9, + }, + [VCAP_AF_CLS_VID_SEL] = { + .type = VCAP_FIELD_U32, + .offset = 26, + .width = 3, + }, + [VCAP_AF_GVID_ADD_REPLACE_SEL] = { + .type = VCAP_FIELD_U32, + .offset = 29, + .width = 3, + }, + [VCAP_AF_VID_VAL] = { + .type = VCAP_FIELD_U32, + .offset = 32, + .width = 13, + }, + [VCAP_AF_ISDX_ADD_REPLACE_SEL] = { + .type = VCAP_FIELD_BIT, + .offset = 45, + .width = 1, + }, + [VCAP_AF_ISDX_VAL] = { + .type = VCAP_FIELD_U32, + .offset = 46, + .width = 12, + }, + [VCAP_AF_FWD_DIS] = { + .type = VCAP_FIELD_BIT, + .offset = 58, + .width = 1, + }, + [VCAP_AF_CPU_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 59, + .width = 1, + }, + [VCAP_AF_CPU_Q] = { + .type = VCAP_FIELD_U32, + .offset = 60, + .width = 3, + }, + [VCAP_AF_OAM_Y1731_SEL] = { + .type = VCAP_FIELD_U32, + .offset = 63, + .width = 3, + }, + [VCAP_AF_OAM_TWAMP_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 66, + .width = 1, + }, + [VCAP_AF_OAM_IP_BFD_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 67, + .width = 1, + }, + [VCAP_AF_TC_LABEL] = { + .type = VCAP_FIELD_U32, + .offset = 68, + .width = 3, + }, + [VCAP_AF_TTL_LABEL] = { + .type = VCAP_FIELD_U32, + .offset = 71, + .width = 3, + }, + [VCAP_AF_NUM_VLD_LABELS] = { + .type = VCAP_FIELD_U32, + .offset = 74, + .width = 2, + }, + [VCAP_AF_FWD_TYPE] = { + .type = VCAP_FIELD_U32, + .offset = 76, + .width = 3, + }, + [VCAP_AF_MPLS_OAM_TYPE] = { + .type = VCAP_FIELD_U32, + .offset = 79, + .width = 3, + }, + [VCAP_AF_MPLS_MEP_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 82, + .width = 1, + }, + [VCAP_AF_MPLS_MIP_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 83, + .width = 1, + }, + [VCAP_AF_MPLS_OAM_FLAVOR] = { + .type = VCAP_FIELD_BIT, + .offset = 84, + .width = 1, + }, + [VCAP_AF_MPLS_IP_CTRL_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 85, + .width = 1, + }, + [VCAP_AF_PAG_OVERRIDE_MASK] = { + .type = VCAP_FIELD_U32, + .offset = 86, + .width = 8, + }, + [VCAP_AF_PAG_VAL] = { + .type = VCAP_FIELD_U32, + .offset = 94, + .width = 8, + }, + [VCAP_AF_S2_KEY_SEL_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 102, + .width = 1, + }, + [VCAP_AF_S2_KEY_SEL_IDX] = { + .type = VCAP_FIELD_U32, + .offset = 103, + .width = 6, + }, + [VCAP_AF_PIPELINE_FORCE_ENA] = { + .type = VCAP_FIELD_U32, + .offset = 109, + .width = 2, + }, + [VCAP_AF_PIPELINE_ACT_SEL] = { + .type = VCAP_FIELD_BIT, + .offset = 111, + .width = 1, + }, + [VCAP_AF_PIPELINE_PT] = { + .type = VCAP_FIELD_U32, + .offset = 112, + .width = 5, + }, + [VCAP_AF_NXT_KEY_TYPE] = { + .type = VCAP_FIELD_U32, + .offset = 117, + .width = 5, + }, + [VCAP_AF_NXT_NORM_W16_OFFSET] = { + .type = VCAP_FIELD_U32, + .offset = 122, + .width = 5, + }, + [VCAP_AF_NXT_OFFSET_FROM_TYPE] = { + .type = VCAP_FIELD_U32, + .offset = 127, + .width = 2, + }, + [VCAP_AF_NXT_TYPE_AFTER_OFFSET] = { + .type = VCAP_FIELD_U32, + .offset = 129, + .width = 2, + }, + [VCAP_AF_NXT_NORMALIZE] = { + .type = VCAP_FIELD_BIT, + .offset = 131, + .width = 1, + }, + [VCAP_AF_NXT_IDX_CTRL] = { + .type = VCAP_FIELD_U32, + .offset = 132, + .width = 3, + }, + [VCAP_AF_NXT_IDX] = { + .type = VCAP_FIELD_U32, + .offset = 135, + .width = 12, + }, +}; + +static const struct vcap_field is0_mlbs_reduced_actionfield[] = { + [VCAP_AF_TYPE] = { + .type = VCAP_FIELD_BIT, + .offset = 0, + .width = 1, + }, + [VCAP_AF_COSID_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 1, + .width = 1, + }, + [VCAP_AF_COSID_VAL] = { + .type = VCAP_FIELD_U32, + .offset = 2, + .width = 3, + }, + [VCAP_AF_QOS_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 5, + .width = 1, + }, + [VCAP_AF_QOS_VAL] = { + .type = VCAP_FIELD_U32, + .offset = 6, + .width = 3, + }, + [VCAP_AF_DP_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 9, + .width = 1, + }, + [VCAP_AF_DP_VAL] = { + .type = VCAP_FIELD_U32, + .offset = 10, + .width = 2, + }, + [VCAP_AF_MAP_LOOKUP_SEL] = { + .type = VCAP_FIELD_U32, + .offset = 12, + .width = 2, + }, + [VCAP_AF_ISDX_ADD_REPLACE_SEL] = { + .type = VCAP_FIELD_BIT, + .offset = 14, + .width = 1, + }, + [VCAP_AF_ISDX_VAL] = { + .type = VCAP_FIELD_U32, + .offset = 15, + .width = 12, + }, + [VCAP_AF_FWD_DIS] = { + .type = VCAP_FIELD_BIT, + .offset = 27, + .width = 1, + }, + [VCAP_AF_CPU_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 28, + .width = 1, + }, + [VCAP_AF_CPU_Q] = { + .type = VCAP_FIELD_U32, + .offset = 29, + .width = 3, + }, + [VCAP_AF_TC_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 32, + .width = 1, + }, + [VCAP_AF_TTL_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 33, + .width = 1, + }, + [VCAP_AF_FWD_TYPE] = { + .type = VCAP_FIELD_U32, + .offset = 34, + .width = 3, + }, + [VCAP_AF_MPLS_OAM_TYPE] = { + .type = VCAP_FIELD_U32, + .offset = 37, + .width = 3, + }, + [VCAP_AF_MPLS_MEP_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 40, + .width = 1, + }, + [VCAP_AF_MPLS_MIP_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 41, + .width = 1, + }, + [VCAP_AF_MPLS_OAM_FLAVOR] = { + .type = VCAP_FIELD_BIT, + .offset = 42, + .width = 1, + }, + [VCAP_AF_MPLS_IP_CTRL_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 43, + .width = 1, + }, + [VCAP_AF_PIPELINE_FORCE_ENA] = { + .type = VCAP_FIELD_U32, + .offset = 44, + .width = 2, + }, + [VCAP_AF_PIPELINE_ACT_SEL] = { + .type = VCAP_FIELD_BIT, + .offset = 46, + .width = 1, + }, + [VCAP_AF_PIPELINE_PT_REDUCED] = { + .type = VCAP_FIELD_U32, + .offset = 47, + .width = 3, + }, + [VCAP_AF_NXT_KEY_TYPE] = { + .type = VCAP_FIELD_U32, + .offset = 50, + .width = 5, + }, + [VCAP_AF_NXT_NORM_W32_OFFSET] = { + .type = VCAP_FIELD_U32, + .offset = 55, + .width = 2, + }, + [VCAP_AF_NXT_TYPE_AFTER_OFFSET] = { + .type = VCAP_FIELD_U32, + .offset = 57, + .width = 2, + }, + [VCAP_AF_NXT_NORMALIZE] = { + .type = VCAP_FIELD_BIT, + .offset = 59, + .width = 1, + }, + [VCAP_AF_NXT_IDX_CTRL] = { + .type = VCAP_FIELD_U32, + .offset = 60, + .width = 3, + }, + [VCAP_AF_NXT_IDX] = { + .type = VCAP_FIELD_U32, + .offset = 63, + .width = 12, + }, +}; + +static const struct vcap_field is0_classification_actionfield[] = { + [VCAP_AF_TYPE] = { + .type = VCAP_FIELD_BIT, + .offset = 0, + .width = 1, + }, + [VCAP_AF_DSCP_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 1, + .width = 1, + }, + [VCAP_AF_DSCP_VAL] = { + .type = VCAP_FIELD_U32, + .offset = 2, + .width = 6, + }, + [VCAP_AF_COSID_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 8, + .width = 1, + }, + [VCAP_AF_COSID_VAL] = { + .type = VCAP_FIELD_U32, + .offset = 9, + .width = 3, + }, + [VCAP_AF_QOS_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 12, + .width = 1, + }, + [VCAP_AF_QOS_VAL] = { + .type = VCAP_FIELD_U32, + .offset = 13, + .width = 3, + }, + [VCAP_AF_DP_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 16, + .width = 1, + }, + [VCAP_AF_DP_VAL] = { + .type = VCAP_FIELD_U32, + .offset = 17, + .width = 2, + }, + [VCAP_AF_DEI_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 19, + .width = 1, + }, + [VCAP_AF_DEI_VAL] = { + .type = VCAP_FIELD_BIT, + .offset = 20, + .width = 1, + }, + [VCAP_AF_PCP_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 21, + .width = 1, + }, + [VCAP_AF_PCP_VAL] = { + .type = VCAP_FIELD_U32, + .offset = 22, + .width = 3, + }, + [VCAP_AF_MAP_LOOKUP_SEL] = { + .type = VCAP_FIELD_U32, + .offset = 25, + .width = 2, + }, + [VCAP_AF_MAP_KEY] = { + .type = VCAP_FIELD_U32, + .offset = 27, + .width = 3, + }, + [VCAP_AF_MAP_IDX] = { + .type = VCAP_FIELD_U32, + .offset = 30, + .width = 9, + }, + [VCAP_AF_CLS_VID_SEL] = { + .type = VCAP_FIELD_U32, + .offset = 39, + .width = 3, + }, + [VCAP_AF_GVID_ADD_REPLACE_SEL] = { + .type = VCAP_FIELD_U32, + .offset = 42, + .width = 3, + }, + [VCAP_AF_VID_VAL] = { + .type = VCAP_FIELD_U32, + .offset = 45, + .width = 13, + }, + [VCAP_AF_VLAN_POP_CNT_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 58, + .width = 1, + }, + [VCAP_AF_VLAN_POP_CNT] = { + .type = VCAP_FIELD_U32, + .offset = 59, + .width = 2, + }, + [VCAP_AF_VLAN_PUSH_CNT_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 61, + .width = 1, + }, + [VCAP_AF_VLAN_PUSH_CNT] = { + .type = VCAP_FIELD_U32, + .offset = 62, + .width = 2, + }, + [VCAP_AF_TPID_SEL] = { + .type = VCAP_FIELD_U32, + .offset = 64, + .width = 2, + }, + [VCAP_AF_VLAN_WAS_TAGGED] = { + .type = VCAP_FIELD_U32, + .offset = 66, + .width = 2, + }, + [VCAP_AF_ISDX_ADD_REPLACE_SEL] = { + .type = VCAP_FIELD_BIT, + .offset = 68, + .width = 1, + }, + [VCAP_AF_ISDX_VAL] = { + .type = VCAP_FIELD_U32, + .offset = 69, + .width = 12, + }, + [VCAP_AF_RT_SEL] = { + .type = VCAP_FIELD_U32, + .offset = 81, + .width = 2, + }, + [VCAP_AF_LPM_AFFIX_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 83, + .width = 1, + }, + [VCAP_AF_LPM_AFFIX_VAL] = { + .type = VCAP_FIELD_U32, + .offset = 84, + .width = 10, + }, + [VCAP_AF_RLEG_DMAC_CHK_DIS] = { + .type = VCAP_FIELD_BIT, + .offset = 94, + .width = 1, + }, + [VCAP_AF_TTL_DECR_DIS] = { + .type = VCAP_FIELD_BIT, + .offset = 95, + .width = 1, + }, + [VCAP_AF_L3_MAC_UPDATE_DIS] = { + .type = VCAP_FIELD_BIT, + .offset = 96, + .width = 1, + }, + [VCAP_AF_FWD_DIS] = { + .type = VCAP_FIELD_BIT, + .offset = 97, + .width = 1, + }, + [VCAP_AF_CPU_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 98, + .width = 1, + }, + [VCAP_AF_CPU_Q] = { + .type = VCAP_FIELD_U32, + .offset = 99, + .width = 3, + }, + [VCAP_AF_MIP_SEL] = { + .type = VCAP_FIELD_U32, + .offset = 102, + .width = 2, + }, + [VCAP_AF_OAM_Y1731_SEL] = { + .type = VCAP_FIELD_U32, + .offset = 104, + .width = 3, + }, + [VCAP_AF_OAM_TWAMP_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 107, + .width = 1, + }, + [VCAP_AF_OAM_IP_BFD_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 108, + .width = 1, + }, + [VCAP_AF_PAG_OVERRIDE_MASK] = { + .type = VCAP_FIELD_U32, + .offset = 109, + .width = 8, + }, + [VCAP_AF_PAG_VAL] = { + .type = VCAP_FIELD_U32, + .offset = 117, + .width = 8, + }, + [VCAP_AF_S2_KEY_SEL_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 125, + .width = 1, + }, + [VCAP_AF_S2_KEY_SEL_IDX] = { + .type = VCAP_FIELD_U32, + .offset = 126, + .width = 6, + }, + [VCAP_AF_INJ_MASQ_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 132, + .width = 1, + }, + [VCAP_AF_INJ_MASQ_PORT] = { + .type = VCAP_FIELD_U32, + .offset = 133, + .width = 7, + }, + [VCAP_AF_LPORT_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 140, + .width = 1, + }, + [VCAP_AF_INJ_MASQ_LPORT] = { + .type = VCAP_FIELD_U32, + .offset = 141, + .width = 7, + }, + [VCAP_AF_PIPELINE_FORCE_ENA] = { + .type = VCAP_FIELD_U32, + .offset = 148, + .width = 2, + }, + [VCAP_AF_PIPELINE_ACT_SEL] = { + .type = VCAP_FIELD_BIT, + .offset = 150, + .width = 1, + }, + [VCAP_AF_PIPELINE_PT] = { + .type = VCAP_FIELD_U32, + .offset = 151, + .width = 5, + }, + [VCAP_AF_NXT_KEY_TYPE] = { + .type = VCAP_FIELD_U32, + .offset = 156, + .width = 5, + }, + [VCAP_AF_NXT_NORM_W16_OFFSET] = { + .type = VCAP_FIELD_U32, + .offset = 161, + .width = 5, + }, + [VCAP_AF_NXT_OFFSET_FROM_TYPE] = { + .type = VCAP_FIELD_U32, + .offset = 166, + .width = 2, + }, + [VCAP_AF_NXT_TYPE_AFTER_OFFSET] = { + .type = VCAP_FIELD_U32, + .offset = 168, + .width = 2, + }, + [VCAP_AF_NXT_NORMALIZE] = { + .type = VCAP_FIELD_BIT, + .offset = 170, + .width = 1, + }, + [VCAP_AF_NXT_IDX_CTRL] = { + .type = VCAP_FIELD_U32, + .offset = 171, + .width = 3, + }, + [VCAP_AF_NXT_IDX] = { + .type = VCAP_FIELD_U32, + .offset = 174, + .width = 12, + }, +}; + +static const struct vcap_field is0_full_actionfield[] = { + [VCAP_AF_DSCP_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 0, + .width = 1, + }, + [VCAP_AF_DSCP_VAL] = { + .type = VCAP_FIELD_U32, + .offset = 1, + .width = 6, + }, + [VCAP_AF_COSID_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 7, + .width = 1, + }, + [VCAP_AF_COSID_VAL] = { + .type = VCAP_FIELD_U32, + .offset = 8, + .width = 3, + }, + [VCAP_AF_QOS_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 11, + .width = 1, + }, + [VCAP_AF_QOS_VAL] = { + .type = VCAP_FIELD_U32, + .offset = 12, + .width = 3, + }, + [VCAP_AF_DP_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 15, + .width = 1, + }, + [VCAP_AF_DP_VAL] = { + .type = VCAP_FIELD_U32, + .offset = 16, + .width = 2, + }, + [VCAP_AF_DEI_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 18, + .width = 1, + }, + [VCAP_AF_DEI_VAL] = { + .type = VCAP_FIELD_BIT, + .offset = 19, + .width = 1, + }, + [VCAP_AF_PCP_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 20, + .width = 1, + }, + [VCAP_AF_PCP_VAL] = { + .type = VCAP_FIELD_U32, + .offset = 21, + .width = 3, + }, + [VCAP_AF_MAP_LOOKUP_SEL] = { + .type = VCAP_FIELD_U32, + .offset = 24, + .width = 2, + }, + [VCAP_AF_MAP_KEY] = { + .type = VCAP_FIELD_U32, + .offset = 26, + .width = 3, + }, + [VCAP_AF_MAP_IDX] = { + .type = VCAP_FIELD_U32, + .offset = 29, + .width = 9, + }, + [VCAP_AF_CLS_VID_SEL] = { + .type = VCAP_FIELD_U32, + .offset = 38, + .width = 3, + }, + [VCAP_AF_GVID_ADD_REPLACE_SEL] = { + .type = VCAP_FIELD_U32, + .offset = 41, + .width = 3, + }, + [VCAP_AF_VID_VAL] = { + .type = VCAP_FIELD_U32, + .offset = 44, + .width = 13, + }, + [VCAP_AF_VLAN_POP_CNT_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 57, + .width = 1, + }, + [VCAP_AF_VLAN_POP_CNT] = { + .type = VCAP_FIELD_U32, + .offset = 58, + .width = 2, + }, + [VCAP_AF_VLAN_PUSH_CNT_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 60, + .width = 1, + }, + [VCAP_AF_VLAN_PUSH_CNT] = { + .type = VCAP_FIELD_U32, + .offset = 61, + .width = 2, + }, + [VCAP_AF_TPID_SEL] = { + .type = VCAP_FIELD_U32, + .offset = 63, + .width = 2, + }, + [VCAP_AF_VLAN_WAS_TAGGED] = { + .type = VCAP_FIELD_U32, + .offset = 65, + .width = 2, + }, + [VCAP_AF_ISDX_ADD_REPLACE_SEL] = { + .type = VCAP_FIELD_BIT, + .offset = 67, + .width = 1, + }, + [VCAP_AF_ISDX_VAL] = { + .type = VCAP_FIELD_U32, + .offset = 68, + .width = 12, + }, + [VCAP_AF_MASK_MODE] = { + .type = VCAP_FIELD_U32, + .offset = 80, + .width = 3, + }, + [VCAP_AF_PORT_MASK] = { + .type = VCAP_FIELD_U72, + .offset = 83, + .width = 65, + }, + [VCAP_AF_RT_SEL] = { + .type = VCAP_FIELD_U32, + .offset = 148, + .width = 2, + }, + [VCAP_AF_LPM_AFFIX_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 150, + .width = 1, + }, + [VCAP_AF_LPM_AFFIX_VAL] = { + .type = VCAP_FIELD_U32, + .offset = 151, + .width = 10, + }, + [VCAP_AF_RLEG_DMAC_CHK_DIS] = { + .type = VCAP_FIELD_BIT, + .offset = 161, + .width = 1, + }, + [VCAP_AF_TTL_DECR_DIS] = { + .type = VCAP_FIELD_BIT, + .offset = 162, + .width = 1, + }, + [VCAP_AF_L3_MAC_UPDATE_DIS] = { + .type = VCAP_FIELD_BIT, + .offset = 163, + .width = 1, + }, + [VCAP_AF_CPU_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 164, + .width = 1, + }, + [VCAP_AF_CPU_Q] = { + .type = VCAP_FIELD_U32, + .offset = 165, + .width = 3, + }, + [VCAP_AF_MIP_SEL] = { + .type = VCAP_FIELD_U32, + .offset = 168, + .width = 2, + }, + [VCAP_AF_OAM_Y1731_SEL] = { + .type = VCAP_FIELD_U32, + .offset = 170, + .width = 3, + }, + [VCAP_AF_OAM_TWAMP_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 173, + .width = 1, + }, + [VCAP_AF_OAM_IP_BFD_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 174, + .width = 1, + }, + [VCAP_AF_RSVD_LBL_VAL] = { + .type = VCAP_FIELD_U32, + .offset = 175, + .width = 4, + }, + [VCAP_AF_TC_LABEL] = { + .type = VCAP_FIELD_U32, + .offset = 179, + .width = 3, + }, + [VCAP_AF_TTL_LABEL] = { + .type = VCAP_FIELD_U32, + .offset = 182, + .width = 3, + }, + [VCAP_AF_NUM_VLD_LABELS] = { + .type = VCAP_FIELD_U32, + .offset = 185, + .width = 2, + }, + [VCAP_AF_FWD_TYPE] = { + .type = VCAP_FIELD_U32, + .offset = 187, + .width = 3, + }, + [VCAP_AF_MPLS_OAM_TYPE] = { + .type = VCAP_FIELD_U32, + .offset = 190, + .width = 3, + }, + [VCAP_AF_MPLS_MEP_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 193, + .width = 1, + }, + [VCAP_AF_MPLS_MIP_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 194, + .width = 1, + }, + [VCAP_AF_MPLS_OAM_FLAVOR] = { + .type = VCAP_FIELD_BIT, + .offset = 195, + .width = 1, + }, + [VCAP_AF_MPLS_IP_CTRL_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 196, + .width = 1, + }, + [VCAP_AF_CUSTOM_ACE_ENA] = { + .type = VCAP_FIELD_U32, + .offset = 197, + .width = 5, + }, + [VCAP_AF_CUSTOM_ACE_OFFSET] = { + .type = VCAP_FIELD_U32, + .offset = 202, + .width = 2, + }, + [VCAP_AF_PAG_OVERRIDE_MASK] = { + .type = VCAP_FIELD_U32, + .offset = 204, + .width = 8, + }, + [VCAP_AF_PAG_VAL] = { + .type = VCAP_FIELD_U32, + .offset = 212, + .width = 8, + }, + [VCAP_AF_S2_KEY_SEL_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 220, + .width = 1, + }, + [VCAP_AF_S2_KEY_SEL_IDX] = { + .type = VCAP_FIELD_U32, + .offset = 221, + .width = 6, + }, + [VCAP_AF_INJ_MASQ_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 227, + .width = 1, + }, + [VCAP_AF_INJ_MASQ_PORT] = { + .type = VCAP_FIELD_U32, + .offset = 228, + .width = 7, + }, + [VCAP_AF_LPORT_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 235, + .width = 1, + }, + [VCAP_AF_INJ_MASQ_LPORT] = { + .type = VCAP_FIELD_U32, + .offset = 236, + .width = 7, + }, + [VCAP_AF_MATCH_ID] = { + .type = VCAP_FIELD_U32, + .offset = 243, + .width = 16, + }, + [VCAP_AF_MATCH_ID_MASK] = { + .type = VCAP_FIELD_U32, + .offset = 259, + .width = 16, + }, + [VCAP_AF_PIPELINE_FORCE_ENA] = { + .type = VCAP_FIELD_U32, + .offset = 275, + .width = 2, + }, + [VCAP_AF_PIPELINE_ACT_SEL] = { + .type = VCAP_FIELD_BIT, + .offset = 277, + .width = 1, + }, + [VCAP_AF_PIPELINE_PT] = { + .type = VCAP_FIELD_U32, + .offset = 278, + .width = 5, + }, + [VCAP_AF_NXT_KEY_TYPE] = { + .type = VCAP_FIELD_U32, + .offset = 283, + .width = 5, + }, + [VCAP_AF_NXT_NORM_W16_OFFSET] = { + .type = VCAP_FIELD_U32, + .offset = 288, + .width = 5, + }, + [VCAP_AF_NXT_OFFSET_FROM_TYPE] = { + .type = VCAP_FIELD_U32, + .offset = 293, + .width = 2, + }, + [VCAP_AF_NXT_TYPE_AFTER_OFFSET] = { + .type = VCAP_FIELD_U32, + .offset = 295, + .width = 2, + }, + [VCAP_AF_NXT_NORMALIZE] = { + .type = VCAP_FIELD_BIT, + .offset = 297, + .width = 1, + }, + [VCAP_AF_NXT_IDX_CTRL] = { + .type = VCAP_FIELD_U32, + .offset = 298, + .width = 3, + }, + [VCAP_AF_NXT_IDX] = { + .type = VCAP_FIELD_U32, + .offset = 301, + .width = 12, + }, +}; + +static const struct vcap_field is0_class_reduced_actionfield[] = { + [VCAP_AF_TYPE] = { + .type = VCAP_FIELD_BIT, + .offset = 0, + .width = 1, + }, + [VCAP_AF_COSID_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 1, + .width = 1, + }, + [VCAP_AF_COSID_VAL] = { + .type = VCAP_FIELD_U32, + .offset = 2, + .width = 3, + }, + [VCAP_AF_QOS_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 5, + .width = 1, + }, + [VCAP_AF_QOS_VAL] = { + .type = VCAP_FIELD_U32, + .offset = 6, + .width = 3, + }, + [VCAP_AF_DP_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 9, + .width = 1, + }, + [VCAP_AF_DP_VAL] = { + .type = VCAP_FIELD_U32, + .offset = 10, + .width = 2, + }, + [VCAP_AF_MAP_LOOKUP_SEL] = { + .type = VCAP_FIELD_U32, + .offset = 12, + .width = 2, + }, + [VCAP_AF_MAP_KEY] = { + .type = VCAP_FIELD_U32, + .offset = 14, + .width = 3, + }, + [VCAP_AF_CLS_VID_SEL] = { + .type = VCAP_FIELD_U32, + .offset = 17, + .width = 3, + }, + [VCAP_AF_GVID_ADD_REPLACE_SEL] = { + .type = VCAP_FIELD_U32, + .offset = 20, + .width = 3, + }, + [VCAP_AF_VID_VAL] = { + .type = VCAP_FIELD_U32, + .offset = 23, + .width = 13, + }, + [VCAP_AF_VLAN_POP_CNT_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 36, + .width = 1, + }, + [VCAP_AF_VLAN_POP_CNT] = { + .type = VCAP_FIELD_U32, + .offset = 37, + .width = 2, + }, + [VCAP_AF_VLAN_PUSH_CNT_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 39, + .width = 1, + }, + [VCAP_AF_VLAN_PUSH_CNT] = { + .type = VCAP_FIELD_U32, + .offset = 40, + .width = 2, + }, + [VCAP_AF_TPID_SEL] = { + .type = VCAP_FIELD_U32, + .offset = 42, + .width = 2, + }, + [VCAP_AF_VLAN_WAS_TAGGED] = { + .type = VCAP_FIELD_U32, + .offset = 44, + .width = 2, + }, + [VCAP_AF_ISDX_ADD_REPLACE_SEL] = { + .type = VCAP_FIELD_BIT, + .offset = 46, + .width = 1, + }, + [VCAP_AF_ISDX_VAL] = { + .type = VCAP_FIELD_U32, + .offset = 47, + .width = 12, + }, + [VCAP_AF_FWD_DIS] = { + .type = VCAP_FIELD_BIT, + .offset = 59, + .width = 1, + }, + [VCAP_AF_CPU_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 60, + .width = 1, + }, + [VCAP_AF_CPU_Q] = { + .type = VCAP_FIELD_U32, + .offset = 61, + .width = 3, + }, + [VCAP_AF_MIP_SEL] = { + .type = VCAP_FIELD_U32, + .offset = 64, + .width = 2, + }, + [VCAP_AF_OAM_Y1731_SEL] = { + .type = VCAP_FIELD_U32, + .offset = 66, + .width = 3, + }, + [VCAP_AF_LPORT_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 69, + .width = 1, + }, + [VCAP_AF_INJ_MASQ_LPORT] = { + .type = VCAP_FIELD_U32, + .offset = 70, + .width = 7, + }, + [VCAP_AF_PIPELINE_FORCE_ENA] = { + .type = VCAP_FIELD_U32, + .offset = 77, + .width = 2, + }, + [VCAP_AF_PIPELINE_ACT_SEL] = { + .type = VCAP_FIELD_BIT, + .offset = 79, + .width = 1, + }, + [VCAP_AF_PIPELINE_PT] = { + .type = VCAP_FIELD_U32, + .offset = 80, + .width = 5, + }, + [VCAP_AF_NXT_KEY_TYPE] = { + .type = VCAP_FIELD_U32, + .offset = 85, + .width = 5, + }, + [VCAP_AF_NXT_IDX_CTRL] = { + .type = VCAP_FIELD_U32, + .offset = 90, + .width = 3, + }, + [VCAP_AF_NXT_IDX] = { + .type = VCAP_FIELD_U32, + .offset = 93, + .width = 12, + }, +}; + +static const struct vcap_field is2_base_type_actionfield[] = { + [VCAP_AF_IS_INNER_ACL] = { + .type = VCAP_FIELD_BIT, + .offset = 0, + .width = 1, + }, + [VCAP_AF_PIPELINE_FORCE_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 1, + .width = 1, + }, + [VCAP_AF_PIPELINE_PT] = { + .type = VCAP_FIELD_U32, + .offset = 2, + .width = 5, + }, + [VCAP_AF_HIT_ME_ONCE] = { + .type = VCAP_FIELD_BIT, + .offset = 7, + .width = 1, + }, + [VCAP_AF_INTR_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 8, + .width = 1, + }, + [VCAP_AF_CPU_COPY_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 9, + .width = 1, + }, + [VCAP_AF_CPU_QUEUE_NUM] = { + .type = VCAP_FIELD_U32, + .offset = 10, + .width = 3, + }, + [VCAP_AF_CPU_DIS] = { + .type = VCAP_FIELD_BIT, + .offset = 13, + .width = 1, + }, + [VCAP_AF_LRN_DIS] = { + .type = VCAP_FIELD_BIT, + .offset = 14, + .width = 1, + }, + [VCAP_AF_RT_DIS] = { + .type = VCAP_FIELD_BIT, + .offset = 15, + .width = 1, + }, + [VCAP_AF_POLICE_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 16, + .width = 1, + }, + [VCAP_AF_POLICE_IDX] = { + .type = VCAP_FIELD_U32, + .offset = 17, + .width = 6, + }, + [VCAP_AF_IGNORE_PIPELINE_CTRL] = { + .type = VCAP_FIELD_BIT, + .offset = 23, + .width = 1, + }, + [VCAP_AF_DLB_OFFSET] = { + .type = VCAP_FIELD_U32, + .offset = 24, + .width = 3, + }, + [VCAP_AF_MASK_MODE] = { + .type = VCAP_FIELD_U32, + .offset = 27, + .width = 3, + }, + [VCAP_AF_PORT_MASK] = { + .type = VCAP_FIELD_U72, + .offset = 30, + .width = 68, + }, + [VCAP_AF_RSDX_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 98, + .width = 1, + }, + [VCAP_AF_RSDX_VAL] = { + .type = VCAP_FIELD_U32, + .offset = 99, + .width = 12, + }, + [VCAP_AF_MIRROR_PROBE] = { + .type = VCAP_FIELD_U32, + .offset = 111, + .width = 2, + }, + [VCAP_AF_REW_CMD] = { + .type = VCAP_FIELD_U32, + .offset = 113, + .width = 11, + }, + [VCAP_AF_TTL_UPDATE_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 124, + .width = 1, + }, + [VCAP_AF_SAM_SEQ_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 125, + .width = 1, + }, + [VCAP_AF_TCP_UDP_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 126, + .width = 1, + }, + [VCAP_AF_TCP_UDP_DPORT] = { + .type = VCAP_FIELD_U32, + .offset = 127, + .width = 16, + }, + [VCAP_AF_TCP_UDP_SPORT] = { + .type = VCAP_FIELD_U32, + .offset = 143, + .width = 16, + }, + [VCAP_AF_MATCH_ID] = { + .type = VCAP_FIELD_U32, + .offset = 159, + .width = 16, + }, + [VCAP_AF_MATCH_ID_MASK] = { + .type = VCAP_FIELD_U32, + .offset = 175, + .width = 16, + }, + [VCAP_AF_CNT_ID] = { + .type = VCAP_FIELD_U32, + .offset = 191, + .width = 12, + }, + [VCAP_AF_SWAP_MAC_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 203, + .width = 1, + }, + [VCAP_AF_ACL_RT_MODE] = { + .type = VCAP_FIELD_U32, + .offset = 204, + .width = 4, + }, + [VCAP_AF_ACL_MAC] = { + .type = VCAP_FIELD_U48, + .offset = 208, + .width = 48, + }, + [VCAP_AF_DMAC_OFFSET_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 256, + .width = 1, + }, + [VCAP_AF_PTP_MASTER_SEL] = { + .type = VCAP_FIELD_U32, + .offset = 257, + .width = 2, + }, + [VCAP_AF_LOG_MSG_INTERVAL] = { + .type = VCAP_FIELD_U32, + .offset = 259, + .width = 4, + }, + [VCAP_AF_SIP_IDX] = { + .type = VCAP_FIELD_U32, + .offset = 263, + .width = 5, + }, + [VCAP_AF_RLEG_STAT_IDX] = { + .type = VCAP_FIELD_U32, + .offset = 268, + .width = 3, + }, + [VCAP_AF_IGR_ACL_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 271, + .width = 1, + }, + [VCAP_AF_EGR_ACL_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 272, + .width = 1, + }, +}; + +static const struct vcap_field es2_base_type_actionfield[] = { + [VCAP_AF_HIT_ME_ONCE] = { + .type = VCAP_FIELD_BIT, + .offset = 0, + .width = 1, + }, + [VCAP_AF_INTR_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 1, + .width = 1, + }, + [VCAP_AF_FWD_MODE] = { + .type = VCAP_FIELD_U32, + .offset = 2, + .width = 2, + }, + [VCAP_AF_COPY_QUEUE_NUM] = { + .type = VCAP_FIELD_U32, + .offset = 4, + .width = 16, + }, + [VCAP_AF_COPY_PORT_NUM] = { + .type = VCAP_FIELD_U32, + .offset = 20, + .width = 7, + }, + [VCAP_AF_MIRROR_PROBE_ID] = { + .type = VCAP_FIELD_U32, + .offset = 27, + .width = 2, + }, + [VCAP_AF_CPU_COPY_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 29, + .width = 1, + }, + [VCAP_AF_CPU_QUEUE_NUM] = { + .type = VCAP_FIELD_U32, + .offset = 30, + .width = 3, + }, + [VCAP_AF_POLICE_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 33, + .width = 1, + }, + [VCAP_AF_POLICE_REMARK] = { + .type = VCAP_FIELD_BIT, + .offset = 34, + .width = 1, + }, + [VCAP_AF_POLICE_IDX] = { + .type = VCAP_FIELD_U32, + .offset = 35, + .width = 6, + }, + [VCAP_AF_ES2_REW_CMD] = { + .type = VCAP_FIELD_U32, + .offset = 41, + .width = 3, + }, + [VCAP_AF_CNT_ID] = { + .type = VCAP_FIELD_U32, + .offset = 44, + .width = 11, + }, + [VCAP_AF_IGNORE_PIPELINE_CTRL] = { + .type = VCAP_FIELD_BIT, + .offset = 55, + .width = 1, + }, +}; + +/* actionfield_set */ +static const struct vcap_set is0_actionfield_set[] = { + [VCAP_AFS_MLBS] = { + .type_id = 0, + .sw_per_item = 2, + .sw_cnt = 6, + }, + [VCAP_AFS_MLBS_REDUCED] = { + .type_id = 0, + .sw_per_item = 1, + .sw_cnt = 12, + }, + [VCAP_AFS_CLASSIFICATION] = { + .type_id = 1, + .sw_per_item = 2, + .sw_cnt = 6, + }, + [VCAP_AFS_FULL] = { + .type_id = -1, + .sw_per_item = 3, + .sw_cnt = 4, + }, + [VCAP_AFS_CLASS_REDUCED] = { + .type_id = 1, + .sw_per_item = 1, + .sw_cnt = 12, + }, +}; + +static const struct vcap_set is2_actionfield_set[] = { + [VCAP_AFS_BASE_TYPE] = { + .type_id = -1, + .sw_per_item = 3, + .sw_cnt = 4, + }, +}; + +static const struct vcap_set es2_actionfield_set[] = { + [VCAP_AFS_BASE_TYPE] = { + .type_id = -1, + .sw_per_item = 3, + .sw_cnt = 4, + }, +}; + +/* actionfield_set map */ +static const struct vcap_field *is0_actionfield_set_map[] = { + [VCAP_AFS_MLBS] = is0_mlbs_actionfield, + [VCAP_AFS_MLBS_REDUCED] = is0_mlbs_reduced_actionfield, + [VCAP_AFS_CLASSIFICATION] = is0_classification_actionfield, + [VCAP_AFS_FULL] = is0_full_actionfield, + [VCAP_AFS_CLASS_REDUCED] = is0_class_reduced_actionfield, +}; + +static const struct vcap_field *is2_actionfield_set_map[] = { + [VCAP_AFS_BASE_TYPE] = is2_base_type_actionfield, +}; + +static const struct vcap_field *es2_actionfield_set_map[] = { + [VCAP_AFS_BASE_TYPE] = es2_base_type_actionfield, +}; + +/* actionfield_set map size */ +static int is0_actionfield_set_map_size[] = { + [VCAP_AFS_MLBS] = ARRAY_SIZE(is0_mlbs_actionfield), + [VCAP_AFS_MLBS_REDUCED] = ARRAY_SIZE(is0_mlbs_reduced_actionfield), + [VCAP_AFS_CLASSIFICATION] = ARRAY_SIZE(is0_classification_actionfield), + [VCAP_AFS_FULL] = ARRAY_SIZE(is0_full_actionfield), + [VCAP_AFS_CLASS_REDUCED] = ARRAY_SIZE(is0_class_reduced_actionfield), +}; + +static int is2_actionfield_set_map_size[] = { + [VCAP_AFS_BASE_TYPE] = ARRAY_SIZE(is2_base_type_actionfield), +}; + +static int es2_actionfield_set_map_size[] = { + [VCAP_AFS_BASE_TYPE] = ARRAY_SIZE(es2_base_type_actionfield), +}; + +/* Type Groups */ +static const struct vcap_typegroup is0_x12_keyfield_set_typegroups[] = { + { + .offset = 0, + .width = 5, + .value = 16, + }, + { + .offset = 52, + .width = 1, + .value = 0, + }, + { + .offset = 104, + .width = 2, + .value = 0, + }, + { + .offset = 156, + .width = 3, + .value = 0, + }, + { + .offset = 208, + .width = 2, + .value = 0, + }, + { + .offset = 260, + .width = 1, + .value = 0, + }, + { + .offset = 312, + .width = 4, + .value = 0, + }, + { + .offset = 364, + .width = 1, + .value = 0, + }, + { + .offset = 416, + .width = 2, + .value = 0, + }, + { + .offset = 468, + .width = 3, + .value = 0, + }, + { + .offset = 520, + .width = 2, + .value = 0, + }, + { + .offset = 572, + .width = 1, + .value = 0, + }, + {} +}; + +static const struct vcap_typegroup is0_x6_keyfield_set_typegroups[] = { + { + .offset = 0, + .width = 4, + .value = 8, + }, + { + .offset = 52, + .width = 1, + .value = 0, + }, + { + .offset = 104, + .width = 2, + .value = 0, + }, + { + .offset = 156, + .width = 3, + .value = 0, + }, + { + .offset = 208, + .width = 2, + .value = 0, + }, + { + .offset = 260, + .width = 1, + .value = 0, + }, + {} +}; + +static const struct vcap_typegroup is0_x3_keyfield_set_typegroups[] = { + { + .offset = 0, + .width = 3, + .value = 4, + }, + { + .offset = 52, + .width = 2, + .value = 0, + }, + { + .offset = 104, + .width = 2, + .value = 0, + }, + {} +}; + +static const struct vcap_typegroup is0_x2_keyfield_set_typegroups[] = { + { + .offset = 0, + .width = 2, + .value = 2, + }, + { + .offset = 52, + .width = 1, + .value = 0, + }, + {} +}; + +static const struct vcap_typegroup is0_x1_keyfield_set_typegroups[] = { + {} +}; + +static const struct vcap_typegroup is2_x12_keyfield_set_typegroups[] = { + { + .offset = 0, + .width = 3, + .value = 4, + }, + { + .offset = 156, + .width = 1, + .value = 0, + }, + { + .offset = 312, + .width = 2, + .value = 0, + }, + { + .offset = 468, + .width = 1, + .value = 0, + }, + {} +}; + +static const struct vcap_typegroup is2_x6_keyfield_set_typegroups[] = { + { + .offset = 0, + .width = 2, + .value = 2, + }, + { + .offset = 156, + .width = 1, + .value = 0, + }, + {} +}; + +static const struct vcap_typegroup is2_x3_keyfield_set_typegroups[] = { + {} +}; + +static const struct vcap_typegroup is2_x1_keyfield_set_typegroups[] = { + {} +}; + +static const struct vcap_typegroup es2_x12_keyfield_set_typegroups[] = { + { + .offset = 0, + .width = 3, + .value = 4, + }, + { + .offset = 156, + .width = 1, + .value = 0, + }, + { + .offset = 312, + .width = 2, + .value = 0, + }, + { + .offset = 468, + .width = 1, + .value = 0, + }, + {} +}; + +static const struct vcap_typegroup es2_x6_keyfield_set_typegroups[] = { + { + .offset = 0, + .width = 2, + .value = 2, + }, + { + .offset = 156, + .width = 1, + .value = 0, + }, + {} +}; + +static const struct vcap_typegroup es2_x3_keyfield_set_typegroups[] = { + { + .offset = 0, + .width = 1, + .value = 1, + }, + {} +}; + +static const struct vcap_typegroup es2_x1_keyfield_set_typegroups[] = { + {} +}; + +static const struct vcap_typegroup *is0_keyfield_set_typegroups[] = { + [12] = is0_x12_keyfield_set_typegroups, + [6] = is0_x6_keyfield_set_typegroups, + [3] = is0_x3_keyfield_set_typegroups, + [2] = is0_x2_keyfield_set_typegroups, + [1] = is0_x1_keyfield_set_typegroups, + [13] = NULL, +}; + +static const struct vcap_typegroup *is2_keyfield_set_typegroups[] = { + [12] = is2_x12_keyfield_set_typegroups, + [6] = is2_x6_keyfield_set_typegroups, + [3] = is2_x3_keyfield_set_typegroups, + [1] = is2_x1_keyfield_set_typegroups, + [13] = NULL, +}; + +static const struct vcap_typegroup *es2_keyfield_set_typegroups[] = { + [12] = es2_x12_keyfield_set_typegroups, + [6] = es2_x6_keyfield_set_typegroups, + [3] = es2_x3_keyfield_set_typegroups, + [1] = es2_x1_keyfield_set_typegroups, + [13] = NULL, +}; + +static const struct vcap_typegroup is0_x3_actionfield_set_typegroups[] = { + { + .offset = 0, + .width = 3, + .value = 4, + }, + { + .offset = 110, + .width = 2, + .value = 0, + }, + { + .offset = 220, + .width = 2, + .value = 0, + }, + {} +}; + +static const struct vcap_typegroup is0_x2_actionfield_set_typegroups[] = { + { + .offset = 0, + .width = 2, + .value = 2, + }, + { + .offset = 110, + .width = 1, + .value = 0, + }, + {} +}; + +static const struct vcap_typegroup is0_x1_actionfield_set_typegroups[] = { + { + .offset = 0, + .width = 1, + .value = 1, + }, + {} +}; + +static const struct vcap_typegroup is2_x3_actionfield_set_typegroups[] = { + { + .offset = 0, + .width = 2, + .value = 2, + }, + { + .offset = 110, + .width = 1, + .value = 0, + }, + { + .offset = 220, + .width = 1, + .value = 0, + }, + {} +}; + +static const struct vcap_typegroup is2_x1_actionfield_set_typegroups[] = { + {} +}; + +static const struct vcap_typegroup es2_x3_actionfield_set_typegroups[] = { + { + .offset = 0, + .width = 2, + .value = 2, + }, + { + .offset = 21, + .width = 1, + .value = 0, + }, + { + .offset = 42, + .width = 1, + .value = 0, + }, + {} +}; + +static const struct vcap_typegroup es2_x1_actionfield_set_typegroups[] = { + {} +}; + +static const struct vcap_typegroup *is0_actionfield_set_typegroups[] = { + [3] = is0_x3_actionfield_set_typegroups, + [2] = is0_x2_actionfield_set_typegroups, + [1] = is0_x1_actionfield_set_typegroups, + [13] = NULL, +}; + +static const struct vcap_typegroup *is2_actionfield_set_typegroups[] = { + [3] = is2_x3_actionfield_set_typegroups, + [1] = is2_x1_actionfield_set_typegroups, + [13] = NULL, +}; + +static const struct vcap_typegroup *es2_actionfield_set_typegroups[] = { + [3] = es2_x3_actionfield_set_typegroups, + [1] = es2_x1_actionfield_set_typegroups, + [13] = NULL, +}; + +/* Keyfieldset names */ +static const char * const vcap_keyfield_set_names[] = { + [VCAP_KFS_NO_VALUE] = "(None)", + [VCAP_KFS_ARP] = "VCAP_KFS_ARP", + [VCAP_KFS_ETAG] = "VCAP_KFS_ETAG", + [VCAP_KFS_IP4_OTHER] = "VCAP_KFS_IP4_OTHER", + [VCAP_KFS_IP4_TCP_UDP] = "VCAP_KFS_IP4_TCP_UDP", + [VCAP_KFS_IP4_VID] = "VCAP_KFS_IP4_VID", + [VCAP_KFS_IP6_STD] = "VCAP_KFS_IP6_STD", + [VCAP_KFS_IP6_VID] = "VCAP_KFS_IP6_VID", + [VCAP_KFS_IP_7TUPLE] = "VCAP_KFS_IP_7TUPLE", + [VCAP_KFS_LL_FULL] = "VCAP_KFS_LL_FULL", + [VCAP_KFS_MAC_ETYPE] = "VCAP_KFS_MAC_ETYPE", + [VCAP_KFS_MLL] = "VCAP_KFS_MLL", + [VCAP_KFS_NORMAL] = "VCAP_KFS_NORMAL", + [VCAP_KFS_NORMAL_5TUPLE_IP4] = "VCAP_KFS_NORMAL_5TUPLE_IP4", + [VCAP_KFS_NORMAL_7TUPLE] = "VCAP_KFS_NORMAL_7TUPLE", + [VCAP_KFS_PURE_5TUPLE_IP4] = "VCAP_KFS_PURE_5TUPLE_IP4", + [VCAP_KFS_TRI_VID] = "VCAP_KFS_TRI_VID", +}; + +/* Actionfieldset names */ +static const char * const vcap_actionfield_set_names[] = { + [VCAP_AFS_NO_VALUE] = "(None)", + [VCAP_AFS_BASE_TYPE] = "VCAP_AFS_BASE_TYPE", + [VCAP_AFS_CLASSIFICATION] = "VCAP_AFS_CLASSIFICATION", + [VCAP_AFS_CLASS_REDUCED] = "VCAP_AFS_CLASS_REDUCED", + [VCAP_AFS_FULL] = "VCAP_AFS_FULL", + [VCAP_AFS_MLBS] = "VCAP_AFS_MLBS", + [VCAP_AFS_MLBS_REDUCED] = "VCAP_AFS_MLBS_REDUCED", +}; + +/* Keyfield names */ +static const char * const vcap_keyfield_names[] = { + [VCAP_KF_NO_VALUE] = "(None)", + [VCAP_KF_8021BR_ECID_BASE] = "8021BR_ECID_BASE", + [VCAP_KF_8021BR_ECID_EXT] = "8021BR_ECID_EXT", + [VCAP_KF_8021BR_E_TAGGED] = "8021BR_E_TAGGED", + [VCAP_KF_8021BR_GRP] = "8021BR_GRP", + [VCAP_KF_8021BR_IGR_ECID_BASE] = "8021BR_IGR_ECID_BASE", + [VCAP_KF_8021BR_IGR_ECID_EXT] = "8021BR_IGR_ECID_EXT", + [VCAP_KF_8021Q_DEI0] = "8021Q_DEI0", + [VCAP_KF_8021Q_DEI1] = "8021Q_DEI1", + [VCAP_KF_8021Q_DEI2] = "8021Q_DEI2", + [VCAP_KF_8021Q_DEI_CLS] = "8021Q_DEI_CLS", + [VCAP_KF_8021Q_PCP0] = "8021Q_PCP0", + [VCAP_KF_8021Q_PCP1] = "8021Q_PCP1", + [VCAP_KF_8021Q_PCP2] = "8021Q_PCP2", + [VCAP_KF_8021Q_PCP_CLS] = "8021Q_PCP_CLS", + [VCAP_KF_8021Q_TPID0] = "8021Q_TPID0", + [VCAP_KF_8021Q_TPID1] = "8021Q_TPID1", + [VCAP_KF_8021Q_TPID2] = "8021Q_TPID2", + [VCAP_KF_8021Q_VID0] = "8021Q_VID0", + [VCAP_KF_8021Q_VID1] = "8021Q_VID1", + [VCAP_KF_8021Q_VID2] = "8021Q_VID2", + [VCAP_KF_8021Q_VID_CLS] = "8021Q_VID_CLS", + [VCAP_KF_8021Q_VLAN_TAGGED_IS] = "8021Q_VLAN_TAGGED_IS", + [VCAP_KF_8021Q_VLAN_TAGS] = "8021Q_VLAN_TAGS", + [VCAP_KF_ACL_GRP_ID] = "ACL_GRP_ID", + [VCAP_KF_ARP_ADDR_SPACE_OK_IS] = "ARP_ADDR_SPACE_OK_IS", + [VCAP_KF_ARP_LEN_OK_IS] = "ARP_LEN_OK_IS", + [VCAP_KF_ARP_OPCODE] = "ARP_OPCODE", + [VCAP_KF_ARP_OPCODE_UNKNOWN_IS] = "ARP_OPCODE_UNKNOWN_IS", + [VCAP_KF_ARP_PROTO_SPACE_OK_IS] = "ARP_PROTO_SPACE_OK_IS", + [VCAP_KF_ARP_SENDER_MATCH_IS] = "ARP_SENDER_MATCH_IS", + [VCAP_KF_ARP_TGT_MATCH_IS] = "ARP_TGT_MATCH_IS", + [VCAP_KF_COSID_CLS] = "COSID_CLS", + [VCAP_KF_DST_ENTRY] = "DST_ENTRY", + [VCAP_KF_ES0_ISDX_KEY_ENA] = "ES0_ISDX_KEY_ENA", + [VCAP_KF_ETYPE] = "ETYPE", + [VCAP_KF_ETYPE_LEN_IS] = "ETYPE_LEN_IS", + [VCAP_KF_ETYPE_MPLS] = "ETYPE_MPLS", + [VCAP_KF_IF_EGR_PORT_MASK] = "IF_EGR_PORT_MASK", + [VCAP_KF_IF_EGR_PORT_MASK_RNG] = "IF_EGR_PORT_MASK_RNG", + [VCAP_KF_IF_IGR_PORT] = "IF_IGR_PORT", + [VCAP_KF_IF_IGR_PORT_MASK] = "IF_IGR_PORT_MASK", + [VCAP_KF_IF_IGR_PORT_MASK_L3] = "IF_IGR_PORT_MASK_L3", + [VCAP_KF_IF_IGR_PORT_MASK_RNG] = "IF_IGR_PORT_MASK_RNG", + [VCAP_KF_IF_IGR_PORT_MASK_SEL] = "IF_IGR_PORT_MASK_SEL", + [VCAP_KF_IF_IGR_PORT_SEL] = "IF_IGR_PORT_SEL", + [VCAP_KF_IP4_IS] = "IP4_IS", + [VCAP_KF_IP_MC_IS] = "IP_MC_IS", + [VCAP_KF_IP_PAYLOAD_5TUPLE] = "IP_PAYLOAD_5TUPLE", + [VCAP_KF_IP_SNAP_IS] = "IP_SNAP_IS", + [VCAP_KF_ISDX_CLS] = "ISDX_CLS", + [VCAP_KF_ISDX_GT0_IS] = "ISDX_GT0_IS", + [VCAP_KF_L2_BC_IS] = "L2_BC_IS", + [VCAP_KF_L2_DMAC] = "L2_DMAC", + [VCAP_KF_L2_FWD_IS] = "L2_FWD_IS", + [VCAP_KF_L2_MC_IS] = "L2_MC_IS", + [VCAP_KF_L2_PAYLOAD_ETYPE] = "L2_PAYLOAD_ETYPE", + [VCAP_KF_L2_SMAC] = "L2_SMAC", + [VCAP_KF_L3_DIP_EQ_SIP_IS] = "L3_DIP_EQ_SIP_IS", + [VCAP_KF_L3_DMAC_DIP_MATCH] = "L3_DMAC_DIP_MATCH", + [VCAP_KF_L3_DPL_CLS] = "L3_DPL_CLS", + [VCAP_KF_L3_DSCP] = "L3_DSCP", + [VCAP_KF_L3_DST_IS] = "L3_DST_IS", + [VCAP_KF_L3_FRAGMENT_TYPE] = "L3_FRAGMENT_TYPE", + [VCAP_KF_L3_FRAG_INVLD_L4_LEN] = "L3_FRAG_INVLD_L4_LEN", + [VCAP_KF_L3_IP4_DIP] = "L3_IP4_DIP", + [VCAP_KF_L3_IP4_SIP] = "L3_IP4_SIP", + [VCAP_KF_L3_IP6_DIP] = "L3_IP6_DIP", + [VCAP_KF_L3_IP6_SIP] = "L3_IP6_SIP", + [VCAP_KF_L3_IP_PROTO] = "L3_IP_PROTO", + [VCAP_KF_L3_OPTIONS_IS] = "L3_OPTIONS_IS", + [VCAP_KF_L3_PAYLOAD] = "L3_PAYLOAD", + [VCAP_KF_L3_RT_IS] = "L3_RT_IS", + [VCAP_KF_L3_SMAC_SIP_MATCH] = "L3_SMAC_SIP_MATCH", + [VCAP_KF_L3_TOS] = "L3_TOS", + [VCAP_KF_L3_TTL_GT0] = "L3_TTL_GT0", + [VCAP_KF_L4_ACK] = "L4_ACK", + [VCAP_KF_L4_DPORT] = "L4_DPORT", + [VCAP_KF_L4_FIN] = "L4_FIN", + [VCAP_KF_L4_PAYLOAD] = "L4_PAYLOAD", + [VCAP_KF_L4_PSH] = "L4_PSH", + [VCAP_KF_L4_RNG] = "L4_RNG", + [VCAP_KF_L4_RST] = "L4_RST", + [VCAP_KF_L4_SEQUENCE_EQ0_IS] = "L4_SEQUENCE_EQ0_IS", + [VCAP_KF_L4_SPORT] = "L4_SPORT", + [VCAP_KF_L4_SPORT_EQ_DPORT_IS] = "L4_SPORT_EQ_DPORT_IS", + [VCAP_KF_L4_SYN] = "L4_SYN", + [VCAP_KF_L4_URG] = "L4_URG", + [VCAP_KF_LOOKUP_FIRST_IS] = "LOOKUP_FIRST_IS", + [VCAP_KF_LOOKUP_GEN_IDX] = "LOOKUP_GEN_IDX", + [VCAP_KF_LOOKUP_GEN_IDX_SEL] = "LOOKUP_GEN_IDX_SEL", + [VCAP_KF_LOOKUP_PAG] = "LOOKUP_PAG", + [VCAP_KF_MIRROR_ENA] = "MIRROR_ENA", + [VCAP_KF_OAM_CCM_CNTS_EQ0] = "OAM_CCM_CNTS_EQ0", + [VCAP_KF_OAM_MEL_FLAGS] = "OAM_MEL_FLAGS", + [VCAP_KF_OAM_Y1731_IS] = "OAM_Y1731_IS", + [VCAP_KF_PROT_ACTIVE] = "PROT_ACTIVE", + [VCAP_KF_TCP_IS] = "TCP_IS", + [VCAP_KF_TCP_UDP_IS] = "TCP_UDP_IS", + [VCAP_KF_TYPE] = "TYPE", +}; + +/* Actionfield names */ +static const char * const vcap_actionfield_names[] = { + [VCAP_AF_NO_VALUE] = "(None)", + [VCAP_AF_ACL_MAC] = "ACL_MAC", + [VCAP_AF_ACL_RT_MODE] = "ACL_RT_MODE", + [VCAP_AF_CLS_VID_SEL] = "CLS_VID_SEL", + [VCAP_AF_CNT_ID] = "CNT_ID", + [VCAP_AF_COPY_PORT_NUM] = "COPY_PORT_NUM", + [VCAP_AF_COPY_QUEUE_NUM] = "COPY_QUEUE_NUM", + [VCAP_AF_COSID_ENA] = "COSID_ENA", + [VCAP_AF_COSID_VAL] = "COSID_VAL", + [VCAP_AF_CPU_COPY_ENA] = "CPU_COPY_ENA", + [VCAP_AF_CPU_DIS] = "CPU_DIS", + [VCAP_AF_CPU_ENA] = "CPU_ENA", + [VCAP_AF_CPU_Q] = "CPU_Q", + [VCAP_AF_CPU_QUEUE_NUM] = "CPU_QUEUE_NUM", + [VCAP_AF_CUSTOM_ACE_ENA] = "CUSTOM_ACE_ENA", + [VCAP_AF_CUSTOM_ACE_OFFSET] = "CUSTOM_ACE_OFFSET", + [VCAP_AF_DEI_ENA] = "DEI_ENA", + [VCAP_AF_DEI_VAL] = "DEI_VAL", + [VCAP_AF_DLB_OFFSET] = "DLB_OFFSET", + [VCAP_AF_DMAC_OFFSET_ENA] = "DMAC_OFFSET_ENA", + [VCAP_AF_DP_ENA] = "DP_ENA", + [VCAP_AF_DP_VAL] = "DP_VAL", + [VCAP_AF_DSCP_ENA] = "DSCP_ENA", + [VCAP_AF_DSCP_VAL] = "DSCP_VAL", + [VCAP_AF_EGR_ACL_ENA] = "EGR_ACL_ENA", + [VCAP_AF_ES2_REW_CMD] = "ES2_REW_CMD", + [VCAP_AF_FWD_DIS] = "FWD_DIS", + [VCAP_AF_FWD_MODE] = "FWD_MODE", + [VCAP_AF_FWD_TYPE] = "FWD_TYPE", + [VCAP_AF_GVID_ADD_REPLACE_SEL] = "GVID_ADD_REPLACE_SEL", + [VCAP_AF_HIT_ME_ONCE] = "HIT_ME_ONCE", + [VCAP_AF_IGNORE_PIPELINE_CTRL] = "IGNORE_PIPELINE_CTRL", + [VCAP_AF_IGR_ACL_ENA] = "IGR_ACL_ENA", + [VCAP_AF_INJ_MASQ_ENA] = "INJ_MASQ_ENA", + [VCAP_AF_INJ_MASQ_LPORT] = "INJ_MASQ_LPORT", + [VCAP_AF_INJ_MASQ_PORT] = "INJ_MASQ_PORT", + [VCAP_AF_INTR_ENA] = "INTR_ENA", + [VCAP_AF_ISDX_ADD_REPLACE_SEL] = "ISDX_ADD_REPLACE_SEL", + [VCAP_AF_ISDX_VAL] = "ISDX_VAL", + [VCAP_AF_IS_INNER_ACL] = "IS_INNER_ACL", + [VCAP_AF_L3_MAC_UPDATE_DIS] = "L3_MAC_UPDATE_DIS", + [VCAP_AF_LOG_MSG_INTERVAL] = "LOG_MSG_INTERVAL", + [VCAP_AF_LPM_AFFIX_ENA] = "LPM_AFFIX_ENA", + [VCAP_AF_LPM_AFFIX_VAL] = "LPM_AFFIX_VAL", + [VCAP_AF_LPORT_ENA] = "LPORT_ENA", + [VCAP_AF_LRN_DIS] = "LRN_DIS", + [VCAP_AF_MAP_IDX] = "MAP_IDX", + [VCAP_AF_MAP_KEY] = "MAP_KEY", + [VCAP_AF_MAP_LOOKUP_SEL] = "MAP_LOOKUP_SEL", + [VCAP_AF_MASK_MODE] = "MASK_MODE", + [VCAP_AF_MATCH_ID] = "MATCH_ID", + [VCAP_AF_MATCH_ID_MASK] = "MATCH_ID_MASK", + [VCAP_AF_MIP_SEL] = "MIP_SEL", + [VCAP_AF_MIRROR_PROBE] = "MIRROR_PROBE", + [VCAP_AF_MIRROR_PROBE_ID] = "MIRROR_PROBE_ID", + [VCAP_AF_MPLS_IP_CTRL_ENA] = "MPLS_IP_CTRL_ENA", + [VCAP_AF_MPLS_MEP_ENA] = "MPLS_MEP_ENA", + [VCAP_AF_MPLS_MIP_ENA] = "MPLS_MIP_ENA", + [VCAP_AF_MPLS_OAM_FLAVOR] = "MPLS_OAM_FLAVOR", + [VCAP_AF_MPLS_OAM_TYPE] = "MPLS_OAM_TYPE", + [VCAP_AF_NUM_VLD_LABELS] = "NUM_VLD_LABELS", + [VCAP_AF_NXT_IDX] = "NXT_IDX", + [VCAP_AF_NXT_IDX_CTRL] = "NXT_IDX_CTRL", + [VCAP_AF_NXT_KEY_TYPE] = "NXT_KEY_TYPE", + [VCAP_AF_NXT_NORMALIZE] = "NXT_NORMALIZE", + [VCAP_AF_NXT_NORM_W16_OFFSET] = "NXT_NORM_W16_OFFSET", + [VCAP_AF_NXT_NORM_W32_OFFSET] = "NXT_NORM_W32_OFFSET", + [VCAP_AF_NXT_OFFSET_FROM_TYPE] = "NXT_OFFSET_FROM_TYPE", + [VCAP_AF_NXT_TYPE_AFTER_OFFSET] = "NXT_TYPE_AFTER_OFFSET", + [VCAP_AF_OAM_IP_BFD_ENA] = "OAM_IP_BFD_ENA", + [VCAP_AF_OAM_TWAMP_ENA] = "OAM_TWAMP_ENA", + [VCAP_AF_OAM_Y1731_SEL] = "OAM_Y1731_SEL", + [VCAP_AF_PAG_OVERRIDE_MASK] = "PAG_OVERRIDE_MASK", + [VCAP_AF_PAG_VAL] = "PAG_VAL", + [VCAP_AF_PCP_ENA] = "PCP_ENA", + [VCAP_AF_PCP_VAL] = "PCP_VAL", + [VCAP_AF_PIPELINE_ACT_SEL] = "PIPELINE_ACT_SEL", + [VCAP_AF_PIPELINE_FORCE_ENA] = "PIPELINE_FORCE_ENA", + [VCAP_AF_PIPELINE_PT] = "PIPELINE_PT", + [VCAP_AF_PIPELINE_PT_REDUCED] = "PIPELINE_PT_REDUCED", + [VCAP_AF_POLICE_ENA] = "POLICE_ENA", + [VCAP_AF_POLICE_IDX] = "POLICE_IDX", + [VCAP_AF_POLICE_REMARK] = "POLICE_REMARK", + [VCAP_AF_PORT_MASK] = "PORT_MASK", + [VCAP_AF_PTP_MASTER_SEL] = "PTP_MASTER_SEL", + [VCAP_AF_QOS_ENA] = "QOS_ENA", + [VCAP_AF_QOS_VAL] = "QOS_VAL", + [VCAP_AF_REW_CMD] = "REW_CMD", + [VCAP_AF_RLEG_DMAC_CHK_DIS] = "RLEG_DMAC_CHK_DIS", + [VCAP_AF_RLEG_STAT_IDX] = "RLEG_STAT_IDX", + [VCAP_AF_RSDX_ENA] = "RSDX_ENA", + [VCAP_AF_RSDX_VAL] = "RSDX_VAL", + [VCAP_AF_RSVD_LBL_VAL] = "RSVD_LBL_VAL", + [VCAP_AF_RT_DIS] = "RT_DIS", + [VCAP_AF_RT_SEL] = "RT_SEL", + [VCAP_AF_S2_KEY_SEL_ENA] = "S2_KEY_SEL_ENA", + [VCAP_AF_S2_KEY_SEL_IDX] = "S2_KEY_SEL_IDX", + [VCAP_AF_SAM_SEQ_ENA] = "SAM_SEQ_ENA", + [VCAP_AF_SIP_IDX] = "SIP_IDX", + [VCAP_AF_SWAP_MAC_ENA] = "SWAP_MAC_ENA", + [VCAP_AF_TCP_UDP_DPORT] = "TCP_UDP_DPORT", + [VCAP_AF_TCP_UDP_ENA] = "TCP_UDP_ENA", + [VCAP_AF_TCP_UDP_SPORT] = "TCP_UDP_SPORT", + [VCAP_AF_TC_ENA] = "TC_ENA", + [VCAP_AF_TC_LABEL] = "TC_LABEL", + [VCAP_AF_TPID_SEL] = "TPID_SEL", + [VCAP_AF_TTL_DECR_DIS] = "TTL_DECR_DIS", + [VCAP_AF_TTL_ENA] = "TTL_ENA", + [VCAP_AF_TTL_LABEL] = "TTL_LABEL", + [VCAP_AF_TTL_UPDATE_ENA] = "TTL_UPDATE_ENA", + [VCAP_AF_TYPE] = "TYPE", + [VCAP_AF_VID_VAL] = "VID_VAL", + [VCAP_AF_VLAN_POP_CNT] = "VLAN_POP_CNT", + [VCAP_AF_VLAN_POP_CNT_ENA] = "VLAN_POP_CNT_ENA", + [VCAP_AF_VLAN_PUSH_CNT] = "VLAN_PUSH_CNT", + [VCAP_AF_VLAN_PUSH_CNT_ENA] = "VLAN_PUSH_CNT_ENA", + [VCAP_AF_VLAN_WAS_TAGGED] = "VLAN_WAS_TAGGED", +}; + +/* VCAPs */ +const struct vcap_info kunit_test_vcaps[] = { + [VCAP_TYPE_IS0] = { + .name = "is0", + .rows = 1024, + .sw_count = 12, + .sw_width = 52, + .sticky_width = 1, + .act_width = 110, + .default_cnt = 140, + .require_cnt_dis = 0, + .version = 1, + .keyfield_set = is0_keyfield_set, + .keyfield_set_size = ARRAY_SIZE(is0_keyfield_set), + .actionfield_set = is0_actionfield_set, + .actionfield_set_size = ARRAY_SIZE(is0_actionfield_set), + .keyfield_set_map = is0_keyfield_set_map, + .keyfield_set_map_size = is0_keyfield_set_map_size, + .actionfield_set_map = is0_actionfield_set_map, + .actionfield_set_map_size = is0_actionfield_set_map_size, + .keyfield_set_typegroups = is0_keyfield_set_typegroups, + .actionfield_set_typegroups = is0_actionfield_set_typegroups, + }, + [VCAP_TYPE_IS2] = { + .name = "is2", + .rows = 256, + .sw_count = 12, + .sw_width = 52, + .sticky_width = 1, + .act_width = 110, + .default_cnt = 73, + .require_cnt_dis = 0, + .version = 1, + .keyfield_set = is2_keyfield_set, + .keyfield_set_size = ARRAY_SIZE(is2_keyfield_set), + .actionfield_set = is2_actionfield_set, + .actionfield_set_size = ARRAY_SIZE(is2_actionfield_set), + .keyfield_set_map = is2_keyfield_set_map, + .keyfield_set_map_size = is2_keyfield_set_map_size, + .actionfield_set_map = is2_actionfield_set_map, + .actionfield_set_map_size = is2_actionfield_set_map_size, + .keyfield_set_typegroups = is2_keyfield_set_typegroups, + .actionfield_set_typegroups = is2_actionfield_set_typegroups, + }, + [VCAP_TYPE_ES2] = { + .name = "es2", + .rows = 1024, + .sw_count = 12, + .sw_width = 52, + .sticky_width = 1, + .act_width = 21, + .default_cnt = 74, + .require_cnt_dis = 0, + .version = 1, + .keyfield_set = es2_keyfield_set, + .keyfield_set_size = ARRAY_SIZE(es2_keyfield_set), + .actionfield_set = es2_actionfield_set, + .actionfield_set_size = ARRAY_SIZE(es2_actionfield_set), + .keyfield_set_map = es2_keyfield_set_map, + .keyfield_set_map_size = es2_keyfield_set_map_size, + .actionfield_set_map = es2_actionfield_set_map, + .actionfield_set_map_size = es2_actionfield_set_map_size, + .keyfield_set_typegroups = es2_keyfield_set_typegroups, + .actionfield_set_typegroups = es2_actionfield_set_typegroups, + }, +}; + +const struct vcap_statistics kunit_test_vcap_stats = { + .name = "kunit_test", + .count = 3, + .keyfield_set_names = vcap_keyfield_set_names, + .actionfield_set_names = vcap_actionfield_set_names, + .keyfield_names = vcap_keyfield_names, + .actionfield_names = vcap_actionfield_names, +}; diff --git a/drivers/net/ethernet/microchip/vcap/vcap_model_kunit.h b/drivers/net/ethernet/microchip/vcap/vcap_model_kunit.h new file mode 100644 index 000000000000..b5a74f0eef9b --- /dev/null +++ b/drivers/net/ethernet/microchip/vcap/vcap_model_kunit.h @@ -0,0 +1,10 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ +/* Copyright (C) 2022 Microchip Technology Inc. and its subsidiaries. + * Microchip VCAP test model interface for kunit testing + */ + +#ifndef __VCAP_MODEL_KUNIT_H__ +#define __VCAP_MODEL_KUNIT_H__ +extern const struct vcap_info kunit_test_vcaps[]; +extern const struct vcap_statistics kunit_test_vcap_stats; +#endif /* __VCAP_MODEL_KUNIT_H__ */ From 67d637516fa91c718dd60acd9358a9fb0e19b7b5 Mon Sep 17 00:00:00 2001 From: Steen Hegelund Date: Thu, 20 Oct 2022 15:09:04 +0200 Subject: [PATCH 0283/2002] net: microchip: sparx5: Adding KUNIT test for the VCAP API This provides a KUNIT test suite for the VCAP APIs encoding functionality. The test can be run by adding these settings in a .kunitconfig file CONFIG_KUNIT=y CONFIG_NET=y CONFIG_VCAP_KUNIT_TEST=y Signed-off-by: Steen Hegelund Signed-off-by: David S. Miller --- drivers/net/ethernet/microchip/vcap/Kconfig | 13 + .../microchip/vcap/vcap_ag_api_kunit.h | 643 ++++++++++++ .../net/ethernet/microchip/vcap/vcap_api.c | 4 + .../net/ethernet/microchip/vcap/vcap_api.h | 3 + .../ethernet/microchip/vcap/vcap_api_kunit.c | 933 ++++++++++++++++++ 5 files changed, 1596 insertions(+) create mode 100644 drivers/net/ethernet/microchip/vcap/vcap_ag_api_kunit.h create mode 100644 drivers/net/ethernet/microchip/vcap/vcap_api_kunit.c diff --git a/drivers/net/ethernet/microchip/vcap/Kconfig b/drivers/net/ethernet/microchip/vcap/Kconfig index a78cbc6ce6bb..1af30a358a15 100644 --- a/drivers/net/ethernet/microchip/vcap/Kconfig +++ b/drivers/net/ethernet/microchip/vcap/Kconfig @@ -36,4 +36,17 @@ config VCAP characteristics. Look in the datasheet for the VCAP specifications for the specific switchcore. +config VCAP_KUNIT_TEST + bool "KUnit test for VCAP library" if !KUNIT_ALL_TESTS + depends on KUNIT + depends on KUNIT=y && VCAP=y && y + default KUNIT_ALL_TESTS + help + This builds unit tests for the VCAP library. + + For more information on KUnit and unit tests in general, please refer + to the KUnit documentation in Documentation/dev-tools/kunit/. + + If unsure, say N. + endif # NET_VENDOR_MICROCHIP diff --git a/drivers/net/ethernet/microchip/vcap/vcap_ag_api_kunit.h b/drivers/net/ethernet/microchip/vcap/vcap_ag_api_kunit.h new file mode 100644 index 000000000000..e538ca725687 --- /dev/null +++ b/drivers/net/ethernet/microchip/vcap/vcap_ag_api_kunit.h @@ -0,0 +1,643 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ +/* Copyright (C) 2022 Microchip Technology Inc. and its subsidiaries. + * Microchip VCAP API interface for kunit testing + * This is a different interface, to be able to include different VCAPs + */ + +/* Use same include guard as the official API to be able to override it */ +#ifndef __VCAP_AG_API__ +#define __VCAP_AG_API__ + +enum vcap_type { + VCAP_TYPE_ES2, + VCAP_TYPE_IS0, + VCAP_TYPE_IS2, + VCAP_TYPE_MAX +}; + +/* Keyfieldset names with origin information */ +enum vcap_keyfield_set { + VCAP_KFS_NO_VALUE, /* initial value */ + VCAP_KFS_ARP, /* sparx5 is2 X6, sparx5 es2 X6 */ + VCAP_KFS_ETAG, /* sparx5 is0 X2 */ + VCAP_KFS_IP4_OTHER, /* sparx5 is2 X6, sparx5 es2 X6 */ + VCAP_KFS_IP4_TCP_UDP, /* sparx5 is2 X6, sparx5 es2 X6 */ + VCAP_KFS_IP4_VID, /* sparx5 es2 X3 */ + VCAP_KFS_IP6_STD, /* sparx5 is2 X6 */ + VCAP_KFS_IP6_VID, /* sparx5 is2 X6, sparx5 es2 X6 */ + VCAP_KFS_IP_7TUPLE, /* sparx5 is2 X12, sparx5 es2 X12 */ + VCAP_KFS_LL_FULL, /* sparx5 is0 X6 */ + VCAP_KFS_MAC_ETYPE, /* sparx5 is2 X6, sparx5 es2 X6 */ + VCAP_KFS_MLL, /* sparx5 is0 X3 */ + VCAP_KFS_NORMAL, /* sparx5 is0 X6 */ + VCAP_KFS_NORMAL_5TUPLE_IP4, /* sparx5 is0 X6 */ + VCAP_KFS_NORMAL_7TUPLE, /* sparx5 is0 X12 */ + VCAP_KFS_PURE_5TUPLE_IP4, /* sparx5 is0 X3 */ + VCAP_KFS_TRI_VID, /* sparx5 is0 X2 */ +}; + +/* List of keyfields with description + * + * Keys ending in _IS are booleans derived from frame data + * Keys ending in _CLS are classified frame data + * + * VCAP_KF_8021BR_ECID_BASE: W12, sparx5: is0 + * Used by 802.1BR Bridge Port Extension in an E-Tag + * VCAP_KF_8021BR_ECID_EXT: W8, sparx5: is0 + * Used by 802.1BR Bridge Port Extension in an E-Tag + * VCAP_KF_8021BR_E_TAGGED: W1, sparx5: is0 + * Set for frames containing an E-TAG (802.1BR Ethertype 893f) + * VCAP_KF_8021BR_GRP: W2, sparx5: is0 + * E-Tag group bits in 802.1BR Bridge Port Extension + * VCAP_KF_8021BR_IGR_ECID_BASE: W12, sparx5: is0 + * Used by 802.1BR Bridge Port Extension in an E-Tag + * VCAP_KF_8021BR_IGR_ECID_EXT: W8, sparx5: is0 + * Used by 802.1BR Bridge Port Extension in an E-Tag + * VCAP_KF_8021Q_DEI0: W1, sparx5: is0 + * First DEI in multiple vlan tags (outer tag or default port tag) + * VCAP_KF_8021Q_DEI1: W1, sparx5: is0 + * Second DEI in multiple vlan tags (inner tag) + * VCAP_KF_8021Q_DEI2: W1, sparx5: is0 + * Third DEI in multiple vlan tags (not always available) + * VCAP_KF_8021Q_DEI_CLS: W1, sparx5: is2/es2 + * Classified DEI + * VCAP_KF_8021Q_PCP0: W3, sparx5: is0 + * First PCP in multiple vlan tags (outer tag or default port tag) + * VCAP_KF_8021Q_PCP1: W3, sparx5: is0 + * Second PCP in multiple vlan tags (inner tag) + * VCAP_KF_8021Q_PCP2: W3, sparx5: is0 + * Third PCP in multiple vlan tags (not always available) + * VCAP_KF_8021Q_PCP_CLS: W3, sparx5: is2/es2 + * Classified PCP + * VCAP_KF_8021Q_TPID0: W3, sparx5: is0 + * First TPIC in multiple vlan tags (outer tag or default port tag) + * VCAP_KF_8021Q_TPID1: W3, sparx5: is0 + * Second TPID in multiple vlan tags (inner tag) + * VCAP_KF_8021Q_TPID2: W3, sparx5: is0 + * Third TPID in multiple vlan tags (not always available) + * VCAP_KF_8021Q_VID0: W12, sparx5: is0 + * First VID in multiple vlan tags (outer tag or default port tag) + * VCAP_KF_8021Q_VID1: W12, sparx5: is0 + * Second VID in multiple vlan tags (inner tag) + * VCAP_KF_8021Q_VID2: W12, sparx5: is0 + * Third VID in multiple vlan tags (not always available) + * VCAP_KF_8021Q_VID_CLS: W13, sparx5: is2/es2 + * Classified VID + * VCAP_KF_8021Q_VLAN_TAGGED_IS: W1, sparx5: is2/es2 + * Sparx5: Set if frame was received with a VLAN tag, LAN966x: Set if frame has + * one or more Q-tags. Independent of port VLAN awareness + * VCAP_KF_8021Q_VLAN_TAGS: W3, sparx5: is0 + * Number of VLAN tags in frame: 0: Untagged, 1: Single tagged, 3: Double + * tagged, 7: Triple tagged + * VCAP_KF_ACL_GRP_ID: W8, sparx5: es2 + * Used in interface map table + * VCAP_KF_ARP_ADDR_SPACE_OK_IS: W1, sparx5: is2/es2 + * Set if hardware address is Ethernet + * VCAP_KF_ARP_LEN_OK_IS: W1, sparx5: is2/es2 + * Set if hardware address length = 6 (Ethernet) and IP address length = 4 (IP). + * VCAP_KF_ARP_OPCODE: W2, sparx5: is2/es2 + * ARP opcode + * VCAP_KF_ARP_OPCODE_UNKNOWN_IS: W1, sparx5: is2/es2 + * Set if not one of the codes defined in VCAP_KF_ARP_OPCODE + * VCAP_KF_ARP_PROTO_SPACE_OK_IS: W1, sparx5: is2/es2 + * Set if protocol address space is 0x0800 + * VCAP_KF_ARP_SENDER_MATCH_IS: W1, sparx5: is2/es2 + * Sender Hardware Address = SMAC (ARP) + * VCAP_KF_ARP_TGT_MATCH_IS: W1, sparx5: is2/es2 + * Target Hardware Address = SMAC (RARP) + * VCAP_KF_COSID_CLS: W3, sparx5: es2 + * Class of service + * VCAP_KF_DST_ENTRY: W1, sparx5: is0 + * Selects whether the frame’s destination or source information is used for + * fields L2_SMAC and L3_IP4_SIP + * VCAP_KF_ES0_ISDX_KEY_ENA: W1, sparx5: es2 + * The value taken from the IFH .FWD.ES0_ISDX_KEY_ENA + * VCAP_KF_ETYPE: W16, sparx5: is0/is2/es2 + * Ethernet type + * VCAP_KF_ETYPE_LEN_IS: W1, sparx5: is0/is2/es2 + * Set if frame has EtherType >= 0x600 + * VCAP_KF_ETYPE_MPLS: W2, sparx5: is0 + * Type of MPLS Ethertype (or not) + * VCAP_KF_IF_EGR_PORT_MASK: W32, sparx5: es2 + * Egress port mask, one bit per port + * VCAP_KF_IF_EGR_PORT_MASK_RNG: W3, sparx5: es2 + * Select which 32 port group is available in IF_EGR_PORT (or virtual ports or + * CPU queue) + * VCAP_KF_IF_IGR_PORT: sparx5 is0 W7, sparx5 es2 W9 + * Sparx5: Logical ingress port number retrieved from + * ANA_CL::PORT_ID_CFG.LPORT_NUM or ERLEG, LAN966x: ingress port nunmber + * VCAP_KF_IF_IGR_PORT_MASK: sparx5 is0 W65, sparx5 is2 W32, sparx5 is2 W65 + * Ingress port mask, one bit per port/erleg + * VCAP_KF_IF_IGR_PORT_MASK_L3: W1, sparx5: is2 + * If set, IF_IGR_PORT_MASK, IF_IGR_PORT_MASK_RNG, and IF_IGR_PORT_MASK_SEL are + * used to specify L3 interfaces + * VCAP_KF_IF_IGR_PORT_MASK_RNG: W4, sparx5: is2 + * Range selector for IF_IGR_PORT_MASK. Specifies which group of 32 ports are + * available in IF_IGR_PORT_MASK + * VCAP_KF_IF_IGR_PORT_MASK_SEL: W2, sparx5: is0/is2 + * Mode selector for IF_IGR_PORT_MASK, applicable when IF_IGR_PORT_MASK_L3 == 0. + * Mapping: 0: DEFAULT 1: LOOPBACK 2: MASQUERADE 3: CPU_VD + * VCAP_KF_IF_IGR_PORT_SEL: W1, sparx5: es2 + * Selector for IF_IGR_PORT: physical port number or ERLEG + * VCAP_KF_IP4_IS: W1, sparx5: is0/is2/es2 + * Set if frame has EtherType = 0x800 and IP version = 4 + * VCAP_KF_IP_MC_IS: W1, sparx5: is0 + * Set if frame is IPv4 frame and frame’s destination MAC address is an IPv4 + * multicast address (0x01005E0 /25). Set if frame is IPv6 frame and frame’s + * destination MAC address is an IPv6 multicast address (0x3333/16). + * VCAP_KF_IP_PAYLOAD_5TUPLE: W32, sparx5: is0 + * Payload bytes after IP header + * VCAP_KF_IP_SNAP_IS: W1, sparx5: is0 + * Set if frame is IPv4, IPv6, or SNAP frame + * VCAP_KF_ISDX_CLS: W12, sparx5: is2/es2 + * Classified ISDX + * VCAP_KF_ISDX_GT0_IS: W1, sparx5: is2/es2 + * Set if classified ISDX > 0 + * VCAP_KF_L2_BC_IS: W1, sparx5: is0/is2/es2 + * Set if frame’s destination MAC address is the broadcast address + * (FF-FF-FF-FF-FF-FF). + * VCAP_KF_L2_DMAC: W48, sparx5: is0/is2/es2 + * Destination MAC address + * VCAP_KF_L2_FWD_IS: W1, sparx5: is2 + * Set if the frame is allowed to be forwarded to front ports + * VCAP_KF_L2_MC_IS: W1, sparx5: is0/is2/es2 + * Set if frame’s destination MAC address is a multicast address (bit 40 = 1). + * VCAP_KF_L2_PAYLOAD_ETYPE: W64, sparx5: is2/es2 + * Byte 0-7 of L2 payload after Type/Len field and overloading for OAM + * VCAP_KF_L2_SMAC: W48, sparx5: is0/is2/es2 + * Source MAC address + * VCAP_KF_L3_DIP_EQ_SIP_IS: W1, sparx5: is2/es2 + * Set if Src IP matches Dst IP address + * VCAP_KF_L3_DMAC_DIP_MATCH: W1, sparx5: is2 + * Match found in DIP security lookup in ANA_L3 + * VCAP_KF_L3_DPL_CLS: W1, sparx5: es2 + * The frames drop precedence level + * VCAP_KF_L3_DSCP: W6, sparx5: is0 + * Frame’s DSCP value + * VCAP_KF_L3_DST_IS: W1, sparx5: is2 + * Set if lookup is done for egress router leg + * VCAP_KF_L3_FRAGMENT_TYPE: W2, sparx5: is0/is2/es2 + * L3 Fragmentation type (none, initial, suspicious, valid follow up) + * VCAP_KF_L3_FRAG_INVLD_L4_LEN: W1, sparx5: is0/is2 + * Set if frame's L4 length is less than ANA_CL:COMMON:CLM_FRAGMENT_CFG.L4_MIN_L + * EN + * VCAP_KF_L3_IP4_DIP: W32, sparx5: is0/is2/es2 + * Destination IPv4 Address + * VCAP_KF_L3_IP4_SIP: W32, sparx5: is0/is2/es2 + * Source IPv4 Address + * VCAP_KF_L3_IP6_DIP: W128, sparx5: is0/is2/es2 + * Sparx5: Full IPv6 DIP, LAN966x: Either Full IPv6 DIP or a subset depending on + * frame type + * VCAP_KF_L3_IP6_SIP: W128, sparx5: is0/is2/es2 + * Sparx5: Full IPv6 SIP, LAN966x: Either Full IPv6 SIP or a subset depending on + * frame type + * VCAP_KF_L3_IP_PROTO: W8, sparx5: is0/is2/es2 + * IPv4 frames: IP protocol. IPv6 frames: Next header, same as for IPV4 + * VCAP_KF_L3_OPTIONS_IS: W1, sparx5: is0/is2/es2 + * Set if IPv4 frame contains options (IP len > 5) + * VCAP_KF_L3_PAYLOAD: sparx5 is2 W96, sparx5 is2 W40, sparx5 es2 W96 + * Sparx5: Payload bytes after IP header. IPv4: IPv4 options are not parsed so + * payload is always taken 20 bytes after the start of the IPv4 header, LAN966x: + * Bytes 0-6 after IP header + * VCAP_KF_L3_RT_IS: W1, sparx5: is2/es2 + * Set if frame has hit a router leg + * VCAP_KF_L3_SMAC_SIP_MATCH: W1, sparx5: is2 + * Match found in SIP security lookup in ANA_L3 + * VCAP_KF_L3_TOS: W8, sparx5: is2/es2 + * Sparx5: Frame's IPv4/IPv6 DSCP and ECN fields, LAN966x: IP TOS field + * VCAP_KF_L3_TTL_GT0: W1, sparx5: is2/es2 + * Set if IPv4 TTL / IPv6 hop limit is greater than 0 + * VCAP_KF_L4_ACK: W1, sparx5: is2/es2 + * Sparx5 and LAN966x: TCP flag ACK, LAN966x only: PTP over UDP: flagField bit 2 + * (unicastFlag) + * VCAP_KF_L4_DPORT: W16, sparx5: is2/es2 + * Sparx5: TCP/UDP destination port. Overloading for IP_7TUPLE: Non-TCP/UDP IP + * frames: L4_DPORT = L3_IP_PROTO, LAN966x: TCP/UDP destination port + * VCAP_KF_L4_FIN: W1, sparx5: is2/es2 + * TCP flag FIN, LAN966x: TCP flag FIN, and for PTP over UDP: messageType bit 1 + * VCAP_KF_L4_PAYLOAD: W64, sparx5: is2/es2 + * Payload bytes after TCP/UDP header Overloading for IP_7TUPLE: Non TCP/UDP + * frames: Payload bytes 0–7 after IP header. IPv4 options are not parsed so + * payload is always taken 20 bytes after the start of the IPv4 header for non + * TCP/UDP IPv4 frames + * VCAP_KF_L4_PSH: W1, sparx5: is2/es2 + * Sparx5: TCP flag PSH, LAN966x: TCP: TCP flag PSH. PTP over UDP: flagField bit + * 1 (twoStepFlag) + * VCAP_KF_L4_RNG: sparx5 is0 W8, sparx5 is2 W16, sparx5 es2 W16 + * Range checker bitmask (one for each range checker). Input into range checkers + * is taken from classified results (VID, DSCP) and frame (SPORT, DPORT, ETYPE, + * outer VID, inner VID) + * VCAP_KF_L4_RST: W1, sparx5: is2/es2 + * Sparx5: TCP flag RST , LAN966x: TCP: TCP flag RST. PTP over UDP: messageType + * bit 3 + * VCAP_KF_L4_SEQUENCE_EQ0_IS: W1, sparx5: is2/es2 + * Set if TCP sequence number is 0, LAN966x: Overlayed with PTP over UDP: + * messageType bit 0 + * VCAP_KF_L4_SPORT: W16, sparx5: is0/is2/es2 + * TCP/UDP source port + * VCAP_KF_L4_SPORT_EQ_DPORT_IS: W1, sparx5: is2/es2 + * Set if UDP or TCP source port equals UDP or TCP destination port + * VCAP_KF_L4_SYN: W1, sparx5: is2/es2 + * Sparx5: TCP flag SYN, LAN966x: TCP: TCP flag SYN. PTP over UDP: messageType + * bit 2 + * VCAP_KF_L4_URG: W1, sparx5: is2/es2 + * Sparx5: TCP flag URG, LAN966x: TCP: TCP flag URG. PTP over UDP: flagField bit + * 7 (reserved) + * VCAP_KF_LOOKUP_FIRST_IS: W1, sparx5: is0/is2/es2 + * Selects between entries relevant for first and second lookup. Set for first + * lookup, cleared for second lookup. + * VCAP_KF_LOOKUP_GEN_IDX: W12, sparx5: is0 + * Generic index - for chaining CLM instances + * VCAP_KF_LOOKUP_GEN_IDX_SEL: W2, sparx5: is0 + * Select the mode of the Generic Index + * VCAP_KF_LOOKUP_PAG: W8, sparx5: is2 + * Classified Policy Association Group: chains rules from IS1/CLM to IS2 + * VCAP_KF_OAM_CCM_CNTS_EQ0: W1, sparx5: is2/es2 + * Dual-ended loss measurement counters in CCM frames are all zero + * VCAP_KF_OAM_MEL_FLAGS: W7, sparx5: is0 + * Encoding of MD level/MEG level (MEL) + * VCAP_KF_OAM_Y1731_IS: W1, sparx5: is0/is2/es2 + * Set if frame’s EtherType = 0x8902 + * VCAP_KF_PROT_ACTIVE: W1, sparx5: es2 + * Protection is active + * VCAP_KF_TCP_IS: W1, sparx5: is0/is2/es2 + * Set if frame is IPv4 TCP frame (IP protocol = 6) or IPv6 TCP frames (Next + * header = 6) + * VCAP_KF_TCP_UDP_IS: W1, sparx5: is0/is2/es2 + * Set if frame is IPv4/IPv6 TCP or UDP frame (IP protocol/next header equals 6 + * or 17) + * VCAP_KF_TYPE: sparx5 is0 W2, sparx5 is0 W1, sparx5 is2 W4, sparx5 is2 W2, + * sparx5 es2 W3 + * Keyset type id - set by the API + */ + +/* Keyfield names */ +enum vcap_key_field { + VCAP_KF_NO_VALUE, /* initial value */ + VCAP_KF_8021BR_ECID_BASE, + VCAP_KF_8021BR_ECID_EXT, + VCAP_KF_8021BR_E_TAGGED, + VCAP_KF_8021BR_GRP, + VCAP_KF_8021BR_IGR_ECID_BASE, + VCAP_KF_8021BR_IGR_ECID_EXT, + VCAP_KF_8021Q_DEI0, + VCAP_KF_8021Q_DEI1, + VCAP_KF_8021Q_DEI2, + VCAP_KF_8021Q_DEI_CLS, + VCAP_KF_8021Q_PCP0, + VCAP_KF_8021Q_PCP1, + VCAP_KF_8021Q_PCP2, + VCAP_KF_8021Q_PCP_CLS, + VCAP_KF_8021Q_TPID0, + VCAP_KF_8021Q_TPID1, + VCAP_KF_8021Q_TPID2, + VCAP_KF_8021Q_VID0, + VCAP_KF_8021Q_VID1, + VCAP_KF_8021Q_VID2, + VCAP_KF_8021Q_VID_CLS, + VCAP_KF_8021Q_VLAN_TAGGED_IS, + VCAP_KF_8021Q_VLAN_TAGS, + VCAP_KF_ACL_GRP_ID, + VCAP_KF_ARP_ADDR_SPACE_OK_IS, + VCAP_KF_ARP_LEN_OK_IS, + VCAP_KF_ARP_OPCODE, + VCAP_KF_ARP_OPCODE_UNKNOWN_IS, + VCAP_KF_ARP_PROTO_SPACE_OK_IS, + VCAP_KF_ARP_SENDER_MATCH_IS, + VCAP_KF_ARP_TGT_MATCH_IS, + VCAP_KF_COSID_CLS, + VCAP_KF_DST_ENTRY, + VCAP_KF_ES0_ISDX_KEY_ENA, + VCAP_KF_ETYPE, + VCAP_KF_ETYPE_LEN_IS, + VCAP_KF_ETYPE_MPLS, + VCAP_KF_IF_EGR_PORT_MASK, + VCAP_KF_IF_EGR_PORT_MASK_RNG, + VCAP_KF_IF_IGR_PORT, + VCAP_KF_IF_IGR_PORT_MASK, + VCAP_KF_IF_IGR_PORT_MASK_L3, + VCAP_KF_IF_IGR_PORT_MASK_RNG, + VCAP_KF_IF_IGR_PORT_MASK_SEL, + VCAP_KF_IF_IGR_PORT_SEL, + VCAP_KF_IP4_IS, + VCAP_KF_IP_MC_IS, + VCAP_KF_IP_PAYLOAD_5TUPLE, + VCAP_KF_IP_SNAP_IS, + VCAP_KF_ISDX_CLS, + VCAP_KF_ISDX_GT0_IS, + VCAP_KF_L2_BC_IS, + VCAP_KF_L2_DMAC, + VCAP_KF_L2_FWD_IS, + VCAP_KF_L2_MC_IS, + VCAP_KF_L2_PAYLOAD_ETYPE, + VCAP_KF_L2_SMAC, + VCAP_KF_L3_DIP_EQ_SIP_IS, + VCAP_KF_L3_DMAC_DIP_MATCH, + VCAP_KF_L3_DPL_CLS, + VCAP_KF_L3_DSCP, + VCAP_KF_L3_DST_IS, + VCAP_KF_L3_FRAGMENT_TYPE, + VCAP_KF_L3_FRAG_INVLD_L4_LEN, + VCAP_KF_L3_IP4_DIP, + VCAP_KF_L3_IP4_SIP, + VCAP_KF_L3_IP6_DIP, + VCAP_KF_L3_IP6_SIP, + VCAP_KF_L3_IP_PROTO, + VCAP_KF_L3_OPTIONS_IS, + VCAP_KF_L3_PAYLOAD, + VCAP_KF_L3_RT_IS, + VCAP_KF_L3_SMAC_SIP_MATCH, + VCAP_KF_L3_TOS, + VCAP_KF_L3_TTL_GT0, + VCAP_KF_L4_ACK, + VCAP_KF_L4_DPORT, + VCAP_KF_L4_FIN, + VCAP_KF_L4_PAYLOAD, + VCAP_KF_L4_PSH, + VCAP_KF_L4_RNG, + VCAP_KF_L4_RST, + VCAP_KF_L4_SEQUENCE_EQ0_IS, + VCAP_KF_L4_SPORT, + VCAP_KF_L4_SPORT_EQ_DPORT_IS, + VCAP_KF_L4_SYN, + VCAP_KF_L4_URG, + VCAP_KF_LOOKUP_FIRST_IS, + VCAP_KF_LOOKUP_GEN_IDX, + VCAP_KF_LOOKUP_GEN_IDX_SEL, + VCAP_KF_LOOKUP_PAG, + VCAP_KF_MIRROR_ENA, + VCAP_KF_OAM_CCM_CNTS_EQ0, + VCAP_KF_OAM_MEL_FLAGS, + VCAP_KF_OAM_Y1731_IS, + VCAP_KF_PROT_ACTIVE, + VCAP_KF_TCP_IS, + VCAP_KF_TCP_UDP_IS, + VCAP_KF_TYPE, +}; + +/* Actionset names with origin information */ +enum vcap_actionfield_set { + VCAP_AFS_NO_VALUE, /* initial value */ + VCAP_AFS_BASE_TYPE, /* sparx5 is2 X3, sparx5 es2 X3 */ + VCAP_AFS_CLASSIFICATION, /* sparx5 is0 X2 */ + VCAP_AFS_CLASS_REDUCED, /* sparx5 is0 X1 */ + VCAP_AFS_FULL, /* sparx5 is0 X3 */ + VCAP_AFS_MLBS, /* sparx5 is0 X2 */ + VCAP_AFS_MLBS_REDUCED, /* sparx5 is0 X1 */ +}; + +/* List of actionfields with description + * + * VCAP_AF_CLS_VID_SEL: W3, sparx5: is0 + * Controls the classified VID: 0: VID_NONE: No action. 1: VID_ADD: New VID = + * old VID + VID_VAL. 2: VID_REPLACE: New VID = VID_VAL. 3: VID_FIRST_TAG: New + * VID = VID from frame's first tag (outer tag) if available, otherwise VID_VAL. + * 4: VID_SECOND_TAG: New VID = VID from frame's second tag (middle tag) if + * available, otherwise VID_VAL. 5: VID_THIRD_TAG: New VID = VID from frame's + * third tag (inner tag) if available, otherwise VID_VAL. + * VCAP_AF_CNT_ID: sparx5 is2 W12, sparx5 es2 W11 + * Counter ID, used per lookup to index the 4K frame counters (ANA_ACL:CNT_TBL). + * Multiple VCAP IS2 entries can use the same counter. + * VCAP_AF_COPY_PORT_NUM: W7, sparx5: es2 + * QSYS port number when FWD_MODE is redirect or copy + * VCAP_AF_COPY_QUEUE_NUM: W16, sparx5: es2 + * QSYS queue number when FWD_MODE is redirect or copy + * VCAP_AF_CPU_COPY_ENA: W1, sparx5: is2/es2 + * Setting this bit to 1 causes all frames that hit this action to be copied to + * the CPU extraction queue specified in CPU_QUEUE_NUM. + * VCAP_AF_CPU_QUEUE_NUM: W3, sparx5: is2/es2 + * CPU queue number. Used when CPU_COPY_ENA is set. + * VCAP_AF_DEI_ENA: W1, sparx5: is0 + * If set, use DEI_VAL as classified DEI value. Otherwise, DEI from basic + * classification is used + * VCAP_AF_DEI_VAL: W1, sparx5: is0 + * See DEI_ENA + * VCAP_AF_DP_ENA: W1, sparx5: is0 + * If set, use DP_VAL as classified drop precedence level. Otherwise, drop + * precedence level from basic classification is used. + * VCAP_AF_DP_VAL: W2, sparx5: is0 + * See DP_ENA. + * VCAP_AF_DSCP_ENA: W1, sparx5: is0 + * If set, use DSCP_VAL as classified DSCP value. Otherwise, DSCP value from + * basic classification is used. + * VCAP_AF_DSCP_VAL: W6, sparx5: is0 + * See DSCP_ENA. + * VCAP_AF_ES2_REW_CMD: W3, sparx5: es2 + * Command forwarded to REW: 0: No action. 1: SWAP MAC addresses. 2: Do L2CP + * DMAC translation when entering or leaving a tunnel. + * VCAP_AF_FWD_MODE: W2, sparx5: es2 + * Forward selector: 0: Forward. 1: Discard. 2: Redirect. 3: Copy. + * VCAP_AF_HIT_ME_ONCE: W1, sparx5: is2/es2 + * Setting this bit to 1 causes the first frame that hits this action where the + * HIT_CNT counter is zero to be copied to the CPU extraction queue specified in + * CPU_QUEUE_NUM. The HIT_CNT counter is then incremented and any frames that + * hit this action later are not copied to the CPU. To re-enable the HIT_ME_ONCE + * functionality, the HIT_CNT counter must be cleared. + * VCAP_AF_IGNORE_PIPELINE_CTRL: W1, sparx5: is2/es2 + * Ignore ingress pipeline control. This enforces the use of the VCAP IS2 action + * even when the pipeline control has terminated the frame before VCAP IS2. + * VCAP_AF_INTR_ENA: W1, sparx5: is2/es2 + * If set, an interrupt is triggered when this rule is hit + * VCAP_AF_ISDX_ADD_REPLACE_SEL: W1, sparx5: is0 + * Controls the classified ISDX. 0: New ISDX = old ISDX + ISDX_VAL. 1: New ISDX + * = ISDX_VAL. + * VCAP_AF_ISDX_VAL: W12, sparx5: is0 + * See isdx_add_replace_sel + * VCAP_AF_LRN_DIS: W1, sparx5: is2 + * Setting this bit to 1 disables learning of frames hitting this action. + * VCAP_AF_MAP_IDX: W9, sparx5: is0 + * Index for QoS mapping table lookup + * VCAP_AF_MAP_KEY: W3, sparx5: is0 + * Key type for QoS mapping table lookup. 0: DEI0, PCP0 (outer tag). 1: DEI1, + * PCP1 (middle tag). 2: DEI2, PCP2 (inner tag). 3: MPLS TC. 4: PCP0 (outer + * tag). 5: E-DEI, E-PCP (E-TAG). 6: DSCP if available, otherwise none. 7: DSCP + * if available, otherwise DEI0, PCP0 (outer tag) if available using MAP_IDX+8, + * otherwise none + * VCAP_AF_MAP_LOOKUP_SEL: W2, sparx5: is0 + * Selects which of the two QoS Mapping Table lookups that MAP_KEY and MAP_IDX + * are applied to. 0: No changes to the QoS Mapping Table lookup. 1: Update key + * type and index for QoS Mapping Table lookup #0. 2: Update key type and index + * for QoS Mapping Table lookup #1. 3: Reserved. + * VCAP_AF_MASK_MODE: W3, sparx5: is0/is2 + * Controls the PORT_MASK use. Sparx5: 0: OR_DSTMASK, 1: AND_VLANMASK, 2: + * REPLACE_PGID, 3: REPLACE_ALL, 4: REDIR_PGID, 5: OR_PGID_MASK, 6: VSTAX, 7: + * Not applicable. LAN966X: 0: No action, 1: Permit/deny (AND), 2: Policy + * forwarding (DMAC lookup), 3: Redirect. The CPU port is untouched by + * MASK_MODE. + * VCAP_AF_MATCH_ID: W16, sparx5: is0/is2 + * Logical ID for the entry. The MATCH_ID is extracted together with the frame + * if the frame is forwarded to the CPU (CPU_COPY_ENA). The result is placed in + * IFH.CL_RSLT. + * VCAP_AF_MATCH_ID_MASK: W16, sparx5: is0/is2 + * Mask used by MATCH_ID. + * VCAP_AF_MIRROR_PROBE: W2, sparx5: is2 + * Mirroring performed according to configuration of a mirror probe. 0: No + * mirroring. 1: Mirror probe 0. 2: Mirror probe 1. 3: Mirror probe 2 + * VCAP_AF_MIRROR_PROBE_ID: W2, sparx5: es2 + * Signals a mirror probe to be placed in the IFH. Only possible when FWD_MODE + * is copy. 0: No mirroring. 1–3: Use mirror probe 0-2. + * VCAP_AF_NXT_IDX: W12, sparx5: is0 + * Index used as part of key (field G_IDX) in the next lookup. + * VCAP_AF_NXT_IDX_CTRL: W3, sparx5: is0 + * Controls the generation of the G_IDX used in the VCAP CLM next lookup + * VCAP_AF_PAG_OVERRIDE_MASK: W8, sparx5: is0 + * Bits set in this mask will override PAG_VAL from port profile.  New PAG = + * (PAG (input) AND ~PAG_OVERRIDE_MASK) OR (PAG_VAL AND PAG_OVERRIDE_MASK) + * VCAP_AF_PAG_VAL: W8, sparx5: is0 + * See PAG_OVERRIDE_MASK. + * VCAP_AF_PCP_ENA: W1, sparx5: is0 + * If set, use PCP_VAL as classified PCP value. Otherwise, PCP from basic + * classification is used. + * VCAP_AF_PCP_VAL: W3, sparx5: is0 + * See PCP_ENA. + * VCAP_AF_PIPELINE_FORCE_ENA: sparx5 is0 W2, sparx5 is2 W1 + * If set, use PIPELINE_PT unconditionally and set PIPELINE_ACT = NONE if + * PIPELINE_PT == NONE. Overrules previous settings of pipeline point. + * VCAP_AF_PIPELINE_PT: W5, sparx5: is0/is2 + * Pipeline point used if PIPELINE_FORCE_ENA is set + * VCAP_AF_POLICE_ENA: W1, sparx5: is2/es2 + * Setting this bit to 1 causes frames that hit this action to be policed by the + * ACL policer specified in POLICE_IDX. Only applies to the first lookup. + * VCAP_AF_POLICE_IDX: W6, sparx5: is2/es2 + * Selects VCAP policer used when policing frames (POLICE_ENA) + * VCAP_AF_POLICE_REMARK: W1, sparx5: es2 + * If set, frames exceeding policer rates are marked as yellow but not + * discarded. + * VCAP_AF_PORT_MASK: sparx5 is0 W65, sparx5 is2 W68 + * Port mask applied to the forwarding decision based on MASK_MODE. + * VCAP_AF_QOS_ENA: W1, sparx5: is0 + * If set, use QOS_VAL as classified QoS class. Otherwise, QoS class from basic + * classification is used. + * VCAP_AF_QOS_VAL: W3, sparx5: is0 + * See QOS_ENA. + * VCAP_AF_RT_DIS: W1, sparx5: is2 + * If set, routing is disallowed. Only applies when IS_INNER_ACL is 0. See also + * IGR_ACL_ENA, EGR_ACL_ENA, and RLEG_STAT_IDX. + * VCAP_AF_TYPE: W1, sparx5: is0 + * Actionset type id - Set by the API + * VCAP_AF_VID_VAL: W13, sparx5: is0 + * New VID Value + */ + +/* Actionfield names */ +enum vcap_action_field { + VCAP_AF_NO_VALUE, /* initial value */ + VCAP_AF_ACL_MAC, + VCAP_AF_ACL_RT_MODE, + VCAP_AF_CLS_VID_SEL, + VCAP_AF_CNT_ID, + VCAP_AF_COPY_PORT_NUM, + VCAP_AF_COPY_QUEUE_NUM, + VCAP_AF_COSID_ENA, + VCAP_AF_COSID_VAL, + VCAP_AF_CPU_COPY_ENA, + VCAP_AF_CPU_DIS, + VCAP_AF_CPU_ENA, + VCAP_AF_CPU_Q, + VCAP_AF_CPU_QUEUE_NUM, + VCAP_AF_CUSTOM_ACE_ENA, + VCAP_AF_CUSTOM_ACE_OFFSET, + VCAP_AF_DEI_ENA, + VCAP_AF_DEI_VAL, + VCAP_AF_DLB_OFFSET, + VCAP_AF_DMAC_OFFSET_ENA, + VCAP_AF_DP_ENA, + VCAP_AF_DP_VAL, + VCAP_AF_DSCP_ENA, + VCAP_AF_DSCP_VAL, + VCAP_AF_EGR_ACL_ENA, + VCAP_AF_ES2_REW_CMD, + VCAP_AF_FWD_DIS, + VCAP_AF_FWD_MODE, + VCAP_AF_FWD_TYPE, + VCAP_AF_GVID_ADD_REPLACE_SEL, + VCAP_AF_HIT_ME_ONCE, + VCAP_AF_IGNORE_PIPELINE_CTRL, + VCAP_AF_IGR_ACL_ENA, + VCAP_AF_INJ_MASQ_ENA, + VCAP_AF_INJ_MASQ_LPORT, + VCAP_AF_INJ_MASQ_PORT, + VCAP_AF_INTR_ENA, + VCAP_AF_ISDX_ADD_REPLACE_SEL, + VCAP_AF_ISDX_VAL, + VCAP_AF_IS_INNER_ACL, + VCAP_AF_L3_MAC_UPDATE_DIS, + VCAP_AF_LOG_MSG_INTERVAL, + VCAP_AF_LPM_AFFIX_ENA, + VCAP_AF_LPM_AFFIX_VAL, + VCAP_AF_LPORT_ENA, + VCAP_AF_LRN_DIS, + VCAP_AF_MAP_IDX, + VCAP_AF_MAP_KEY, + VCAP_AF_MAP_LOOKUP_SEL, + VCAP_AF_MASK_MODE, + VCAP_AF_MATCH_ID, + VCAP_AF_MATCH_ID_MASK, + VCAP_AF_MIP_SEL, + VCAP_AF_MIRROR_PROBE, + VCAP_AF_MIRROR_PROBE_ID, + VCAP_AF_MPLS_IP_CTRL_ENA, + VCAP_AF_MPLS_MEP_ENA, + VCAP_AF_MPLS_MIP_ENA, + VCAP_AF_MPLS_OAM_FLAVOR, + VCAP_AF_MPLS_OAM_TYPE, + VCAP_AF_NUM_VLD_LABELS, + VCAP_AF_NXT_IDX, + VCAP_AF_NXT_IDX_CTRL, + VCAP_AF_NXT_KEY_TYPE, + VCAP_AF_NXT_NORMALIZE, + VCAP_AF_NXT_NORM_W16_OFFSET, + VCAP_AF_NXT_NORM_W32_OFFSET, + VCAP_AF_NXT_OFFSET_FROM_TYPE, + VCAP_AF_NXT_TYPE_AFTER_OFFSET, + VCAP_AF_OAM_IP_BFD_ENA, + VCAP_AF_OAM_TWAMP_ENA, + VCAP_AF_OAM_Y1731_SEL, + VCAP_AF_PAG_OVERRIDE_MASK, + VCAP_AF_PAG_VAL, + VCAP_AF_PCP_ENA, + VCAP_AF_PCP_VAL, + VCAP_AF_PIPELINE_ACT_SEL, + VCAP_AF_PIPELINE_FORCE_ENA, + VCAP_AF_PIPELINE_PT, + VCAP_AF_PIPELINE_PT_REDUCED, + VCAP_AF_POLICE_ENA, + VCAP_AF_POLICE_IDX, + VCAP_AF_POLICE_REMARK, + VCAP_AF_PORT_MASK, + VCAP_AF_PTP_MASTER_SEL, + VCAP_AF_QOS_ENA, + VCAP_AF_QOS_VAL, + VCAP_AF_REW_CMD, + VCAP_AF_RLEG_DMAC_CHK_DIS, + VCAP_AF_RLEG_STAT_IDX, + VCAP_AF_RSDX_ENA, + VCAP_AF_RSDX_VAL, + VCAP_AF_RSVD_LBL_VAL, + VCAP_AF_RT_DIS, + VCAP_AF_RT_SEL, + VCAP_AF_S2_KEY_SEL_ENA, + VCAP_AF_S2_KEY_SEL_IDX, + VCAP_AF_SAM_SEQ_ENA, + VCAP_AF_SIP_IDX, + VCAP_AF_SWAP_MAC_ENA, + VCAP_AF_TCP_UDP_DPORT, + VCAP_AF_TCP_UDP_ENA, + VCAP_AF_TCP_UDP_SPORT, + VCAP_AF_TC_ENA, + VCAP_AF_TC_LABEL, + VCAP_AF_TPID_SEL, + VCAP_AF_TTL_DECR_DIS, + VCAP_AF_TTL_ENA, + VCAP_AF_TTL_LABEL, + VCAP_AF_TTL_UPDATE_ENA, + VCAP_AF_TYPE, + VCAP_AF_VID_VAL, + VCAP_AF_VLAN_POP_CNT, + VCAP_AF_VLAN_POP_CNT_ENA, + VCAP_AF_VLAN_PUSH_CNT, + VCAP_AF_VLAN_PUSH_CNT_ENA, + VCAP_AF_VLAN_WAS_TAGGED, +}; + +#endif /* __VCAP_AG_API__ */ diff --git a/drivers/net/ethernet/microchip/vcap/vcap_api.c b/drivers/net/ethernet/microchip/vcap/vcap_api.c index 893f649bcf91..d255bc7deae7 100644 --- a/drivers/net/ethernet/microchip/vcap/vcap_api.c +++ b/drivers/net/ethernet/microchip/vcap/vcap_api.c @@ -1178,3 +1178,7 @@ void vcap_set_tc_exterr(struct flow_cls_offload *fco, struct vcap_rule *vrule) } } EXPORT_SYMBOL_GPL(vcap_set_tc_exterr); + +#ifdef CONFIG_VCAP_KUNIT_TEST +#include "vcap_api_kunit.c" +#endif diff --git a/drivers/net/ethernet/microchip/vcap/vcap_api.h b/drivers/net/ethernet/microchip/vcap/vcap_api.h index 4444bf67ebec..eb2eae75c7e8 100644 --- a/drivers/net/ethernet/microchip/vcap/vcap_api.h +++ b/drivers/net/ethernet/microchip/vcap/vcap_api.h @@ -11,6 +11,9 @@ #include /* Use the generated API model */ +#ifdef CONFIG_VCAP_KUNIT_TEST +#include "vcap_ag_api_kunit.h" +#endif #include "vcap_ag_api.h" #define VCAP_CID_LOOKUP_SIZE 100000 /* Chains in a lookup */ diff --git a/drivers/net/ethernet/microchip/vcap/vcap_api_kunit.c b/drivers/net/ethernet/microchip/vcap/vcap_api_kunit.c new file mode 100644 index 000000000000..b01a6e5039b0 --- /dev/null +++ b/drivers/net/ethernet/microchip/vcap/vcap_api_kunit.c @@ -0,0 +1,933 @@ +// SPDX-License-Identifier: BSD-3-Clause +/* Copyright (C) 2022 Microchip Technology Inc. and its subsidiaries. + * Microchip VCAP API kunit test suite + */ + +#include +#include "vcap_api.h" +#include "vcap_api_client.h" +#include "vcap_model_kunit.h" + +/* First we have the test infrastructure that emulates the platform + * implementation + */ +#define TEST_BUF_CNT 100 +#define TEST_BUF_SZ 350 +#define STREAMWSIZE 64 + +static u32 test_updateaddr[STREAMWSIZE] = {}; +static int test_updateaddridx; +static int test_cache_erase_count; +static u32 test_init_start; +static u32 test_init_count; +static u32 test_hw_counter_id; +static struct vcap_cache_data test_hw_cache; + +/* Callback used by the VCAP API */ +static enum vcap_keyfield_set test_val_keyset(struct net_device *ndev, + struct vcap_admin *admin, + struct vcap_rule *rule, + struct vcap_keyset_list *kslist, + u16 l3_proto) +{ + int idx; + + if (kslist->cnt > 0) { + switch (admin->vtype) { + case VCAP_TYPE_IS0: + for (idx = 0; idx < kslist->cnt; idx++) { + if (kslist->keysets[idx] == VCAP_KFS_ETAG) + return kslist->keysets[idx]; + if (kslist->keysets[idx] == VCAP_KFS_PURE_5TUPLE_IP4) + return kslist->keysets[idx]; + if (kslist->keysets[idx] == VCAP_KFS_NORMAL_5TUPLE_IP4) + return kslist->keysets[idx]; + if (kslist->keysets[idx] == VCAP_KFS_NORMAL_7TUPLE) + return kslist->keysets[idx]; + } + break; + case VCAP_TYPE_IS2: + for (idx = 0; idx < kslist->cnt; idx++) { + if (kslist->keysets[idx] == VCAP_KFS_MAC_ETYPE) + return kslist->keysets[idx]; + if (kslist->keysets[idx] == VCAP_KFS_ARP) + return kslist->keysets[idx]; + if (kslist->keysets[idx] == VCAP_KFS_IP_7TUPLE) + return kslist->keysets[idx]; + } + break; + default: + pr_info("%s:%d: no validation for VCAP %d\n", + __func__, __LINE__, admin->vtype); + break; + } + } + return -EINVAL; +} + +/* Callback used by the VCAP API */ +static void test_add_def_fields(struct net_device *ndev, + struct vcap_admin *admin, + struct vcap_rule *rule) +{ + if (admin->vinst == 0 || admin->vinst == 2) + vcap_rule_add_key_bit(rule, VCAP_KF_LOOKUP_FIRST_IS, VCAP_BIT_1); + else + vcap_rule_add_key_bit(rule, VCAP_KF_LOOKUP_FIRST_IS, VCAP_BIT_0); +} + +/* Callback used by the VCAP API */ +static void test_cache_erase(struct vcap_admin *admin) +{ + if (test_cache_erase_count) { + memset(admin->cache.keystream, 0, test_cache_erase_count); + memset(admin->cache.maskstream, 0, test_cache_erase_count); + memset(admin->cache.actionstream, 0, test_cache_erase_count); + test_cache_erase_count = 0; + } +} + +/* Callback used by the VCAP API */ +static void test_cache_init(struct net_device *ndev, struct vcap_admin *admin, + u32 start, u32 count) +{ + test_init_start = start; + test_init_count = count; +} + +/* Callback used by the VCAP API */ +static void test_cache_read(struct net_device *ndev, struct vcap_admin *admin, + enum vcap_selection sel, u32 start, u32 count) +{ + u32 *keystr, *mskstr, *actstr; + int idx; + + pr_debug("%s:%d: %d %d\n", __func__, __LINE__, start, count); + switch (sel) { + case VCAP_SEL_ENTRY: + keystr = &admin->cache.keystream[start]; + mskstr = &admin->cache.maskstream[start]; + for (idx = 0; idx < count; ++idx) { + pr_debug("%s:%d: keydata[%02d]: 0x%08x\n", __func__, + __LINE__, start + idx, keystr[idx]); + } + for (idx = 0; idx < count; ++idx) { + /* Invert the mask before decoding starts */ + mskstr[idx] = ~mskstr[idx]; + pr_debug("%s:%d: mskdata[%02d]: 0x%08x\n", __func__, + __LINE__, start + idx, mskstr[idx]); + } + break; + case VCAP_SEL_ACTION: + actstr = &admin->cache.actionstream[start]; + for (idx = 0; idx < count; ++idx) { + pr_debug("%s:%d: actdata[%02d]: 0x%08x\n", __func__, + __LINE__, start + idx, actstr[idx]); + } + break; + case VCAP_SEL_COUNTER: + pr_debug("%s:%d\n", __func__, __LINE__); + test_hw_counter_id = start; + admin->cache.counter = test_hw_cache.counter; + admin->cache.sticky = test_hw_cache.sticky; + break; + case VCAP_SEL_ALL: + pr_debug("%s:%d\n", __func__, __LINE__); + break; + } +} + +/* Callback used by the VCAP API */ +static void test_cache_write(struct net_device *ndev, struct vcap_admin *admin, + enum vcap_selection sel, u32 start, u32 count) +{ + u32 *keystr, *mskstr, *actstr; + int idx; + + switch (sel) { + case VCAP_SEL_ENTRY: + keystr = &admin->cache.keystream[start]; + mskstr = &admin->cache.maskstream[start]; + for (idx = 0; idx < count; ++idx) { + pr_debug("%s:%d: keydata[%02d]: 0x%08x\n", __func__, + __LINE__, start + idx, keystr[idx]); + } + for (idx = 0; idx < count; ++idx) { + /* Invert the mask before encoding starts */ + mskstr[idx] = ~mskstr[idx]; + pr_debug("%s:%d: mskdata[%02d]: 0x%08x\n", __func__, + __LINE__, start + idx, mskstr[idx]); + } + break; + case VCAP_SEL_ACTION: + actstr = &admin->cache.actionstream[start]; + for (idx = 0; idx < count; ++idx) { + pr_debug("%s:%d: actdata[%02d]: 0x%08x\n", __func__, + __LINE__, start + idx, actstr[idx]); + } + break; + case VCAP_SEL_COUNTER: + pr_debug("%s:%d\n", __func__, __LINE__); + test_hw_counter_id = start; + test_hw_cache.counter = admin->cache.counter; + test_hw_cache.sticky = admin->cache.sticky; + break; + case VCAP_SEL_ALL: + pr_err("%s:%d: cannot write all streams at once\n", + __func__, __LINE__); + break; + } +} + +/* Callback used by the VCAP API */ +static void test_cache_update(struct net_device *ndev, struct vcap_admin *admin, + enum vcap_command cmd, + enum vcap_selection sel, u32 addr) +{ + if (test_updateaddridx < ARRAY_SIZE(test_updateaddr)) + test_updateaddr[test_updateaddridx] = addr; + else + pr_err("%s:%d: overflow: %d\n", __func__, __LINE__, test_updateaddridx); + test_updateaddridx++; +} + +static void test_cache_move(struct net_device *ndev, struct vcap_admin *admin, + u32 addr, int offset, int count) +{ +} + +/* Provide port information via a callback interface */ +static int vcap_test_port_info(struct net_device *ndev, enum vcap_type vtype, + int (*pf)(void *out, int arg, const char *fmt, ...), + void *out, int arg) +{ + return 0; +} + +struct vcap_operations test_callbacks = { + .validate_keyset = test_val_keyset, + .add_default_fields = test_add_def_fields, + .cache_erase = test_cache_erase, + .cache_write = test_cache_write, + .cache_read = test_cache_read, + .init = test_cache_init, + .update = test_cache_update, + .move = test_cache_move, + .port_info = vcap_test_port_info, +}; + +struct vcap_control test_vctrl = { + .vcaps = kunit_test_vcaps, + .stats = &kunit_test_vcap_stats, + .ops = &test_callbacks, +}; + +static void vcap_test_api_init(struct vcap_admin *admin) +{ + /* Initialize the shared objects */ + INIT_LIST_HEAD(&test_vctrl.list); + INIT_LIST_HEAD(&admin->list); + INIT_LIST_HEAD(&admin->rules); + list_add_tail(&admin->list, &test_vctrl.list); + memset(test_updateaddr, 0, sizeof(test_updateaddr)); + test_updateaddridx = 0; +} + +/* Define the test cases. */ + +static void vcap_api_set_bit_1_test(struct kunit *test) +{ + struct vcap_stream_iter iter = { + .offset = 35, + .sw_width = 52, + .reg_idx = 1, + .reg_bitpos = 20, + .tg = 0 + }; + u32 stream[2] = {0}; + + vcap_set_bit(stream, &iter, 1); + + KUNIT_EXPECT_EQ(test, (u32)0x0, stream[0]); + KUNIT_EXPECT_EQ(test, (u32)BIT(20), stream[1]); +} + +static void vcap_api_set_bit_0_test(struct kunit *test) +{ + struct vcap_stream_iter iter = { + .offset = 35, + .sw_width = 52, + .reg_idx = 2, + .reg_bitpos = 11, + .tg = 0 + }; + u32 stream[3] = {~0, ~0, ~0}; + + vcap_set_bit(stream, &iter, 0); + + KUNIT_EXPECT_EQ(test, (u32)~0, stream[0]); + KUNIT_EXPECT_EQ(test, (u32)~0, stream[1]); + KUNIT_EXPECT_EQ(test, (u32)~BIT(11), stream[2]); +} + +static void vcap_api_iterator_init_test(struct kunit *test) +{ + struct vcap_stream_iter iter; + struct vcap_typegroup typegroups[] = { + { .offset = 0, .width = 2, .value = 2, }, + { .offset = 156, .width = 1, .value = 0, }, + { .offset = 0, .width = 0, .value = 0, }, + }; + struct vcap_typegroup typegroups2[] = { + { .offset = 0, .width = 3, .value = 4, }, + { .offset = 49, .width = 2, .value = 0, }, + { .offset = 98, .width = 2, .value = 0, }, + }; + + vcap_iter_init(&iter, 52, typegroups, 86); + + KUNIT_EXPECT_EQ(test, 52, iter.sw_width); + KUNIT_EXPECT_EQ(test, 86 + 2, iter.offset); + KUNIT_EXPECT_EQ(test, 3, iter.reg_idx); + KUNIT_EXPECT_EQ(test, 4, iter.reg_bitpos); + + vcap_iter_init(&iter, 49, typegroups2, 134); + + KUNIT_EXPECT_EQ(test, 49, iter.sw_width); + KUNIT_EXPECT_EQ(test, 134 + 7, iter.offset); + KUNIT_EXPECT_EQ(test, 5, iter.reg_idx); + KUNIT_EXPECT_EQ(test, 11, iter.reg_bitpos); +} + +static void vcap_api_iterator_next_test(struct kunit *test) +{ + struct vcap_stream_iter iter; + struct vcap_typegroup typegroups[] = { + { .offset = 0, .width = 4, .value = 8, }, + { .offset = 49, .width = 1, .value = 0, }, + { .offset = 98, .width = 2, .value = 0, }, + { .offset = 147, .width = 3, .value = 0, }, + { .offset = 196, .width = 2, .value = 0, }, + { .offset = 245, .width = 1, .value = 0, }, + }; + int idx; + + vcap_iter_init(&iter, 49, typegroups, 86); + + KUNIT_EXPECT_EQ(test, 49, iter.sw_width); + KUNIT_EXPECT_EQ(test, 86 + 5, iter.offset); + KUNIT_EXPECT_EQ(test, 3, iter.reg_idx); + KUNIT_EXPECT_EQ(test, 10, iter.reg_bitpos); + + vcap_iter_next(&iter); + + KUNIT_EXPECT_EQ(test, 91 + 1, iter.offset); + KUNIT_EXPECT_EQ(test, 3, iter.reg_idx); + KUNIT_EXPECT_EQ(test, 11, iter.reg_bitpos); + + for (idx = 0; idx < 6; idx++) + vcap_iter_next(&iter); + + KUNIT_EXPECT_EQ(test, 92 + 6 + 2, iter.offset); + KUNIT_EXPECT_EQ(test, 4, iter.reg_idx); + KUNIT_EXPECT_EQ(test, 2, iter.reg_bitpos); +} + +static void vcap_api_encode_typegroups_test(struct kunit *test) +{ + u32 stream[12] = {0}; + struct vcap_typegroup typegroups[] = { + { .offset = 0, .width = 4, .value = 8, }, + { .offset = 49, .width = 1, .value = 1, }, + { .offset = 98, .width = 2, .value = 3, }, + { .offset = 147, .width = 3, .value = 5, }, + { .offset = 196, .width = 2, .value = 2, }, + { .offset = 245, .width = 5, .value = 27, }, + { .offset = 0, .width = 0, .value = 0, }, + }; + + vcap_encode_typegroups(stream, 49, typegroups, false); + + KUNIT_EXPECT_EQ(test, (u32)0x8, stream[0]); + KUNIT_EXPECT_EQ(test, (u32)0x0, stream[1]); + KUNIT_EXPECT_EQ(test, (u32)0x1, stream[2]); + KUNIT_EXPECT_EQ(test, (u32)0x0, stream[3]); + KUNIT_EXPECT_EQ(test, (u32)0x3, stream[4]); + KUNIT_EXPECT_EQ(test, (u32)0x0, stream[5]); + KUNIT_EXPECT_EQ(test, (u32)0x5, stream[6]); + KUNIT_EXPECT_EQ(test, (u32)0x0, stream[7]); + KUNIT_EXPECT_EQ(test, (u32)0x2, stream[8]); + KUNIT_EXPECT_EQ(test, (u32)0x0, stream[9]); + KUNIT_EXPECT_EQ(test, (u32)27, stream[10]); + KUNIT_EXPECT_EQ(test, (u32)0x0, stream[11]); +} + +static void vcap_api_encode_bit_test(struct kunit *test) +{ + struct vcap_stream_iter iter; + u32 stream[4] = {0}; + struct vcap_typegroup typegroups[] = { + { .offset = 0, .width = 4, .value = 8, }, + { .offset = 49, .width = 1, .value = 1, }, + { .offset = 98, .width = 2, .value = 3, }, + { .offset = 147, .width = 3, .value = 5, }, + { .offset = 196, .width = 2, .value = 2, }, + { .offset = 245, .width = 1, .value = 0, }, + }; + + vcap_iter_init(&iter, 49, typegroups, 44); + + KUNIT_EXPECT_EQ(test, 48, iter.offset); + KUNIT_EXPECT_EQ(test, 1, iter.reg_idx); + KUNIT_EXPECT_EQ(test, 16, iter.reg_bitpos); + + vcap_encode_bit(stream, &iter, 1); + + KUNIT_EXPECT_EQ(test, (u32)0x0, stream[0]); + KUNIT_EXPECT_EQ(test, (u32)BIT(16), stream[1]); + KUNIT_EXPECT_EQ(test, (u32)0x0, stream[2]); +} + +static void vcap_api_encode_field_test(struct kunit *test) +{ + struct vcap_stream_iter iter; + u32 stream[16] = {0}; + struct vcap_typegroup typegroups[] = { + { .offset = 0, .width = 4, .value = 8, }, + { .offset = 49, .width = 1, .value = 1, }, + { .offset = 98, .width = 2, .value = 3, }, + { .offset = 147, .width = 3, .value = 5, }, + { .offset = 196, .width = 2, .value = 2, }, + { .offset = 245, .width = 5, .value = 27, }, + { .offset = 0, .width = 0, .value = 0, }, + }; + struct vcap_field rf = { + .type = VCAP_FIELD_U32, + .offset = 86, + .width = 4, + }; + u8 value[] = {0x5}; + + vcap_iter_init(&iter, 49, typegroups, rf.offset); + + KUNIT_EXPECT_EQ(test, 91, iter.offset); + KUNIT_EXPECT_EQ(test, 3, iter.reg_idx); + KUNIT_EXPECT_EQ(test, 10, iter.reg_bitpos); + + vcap_encode_field(stream, &iter, rf.width, value); + + KUNIT_EXPECT_EQ(test, (u32)0x0, stream[0]); + KUNIT_EXPECT_EQ(test, (u32)0x0, stream[1]); + KUNIT_EXPECT_EQ(test, (u32)0x0, stream[2]); + KUNIT_EXPECT_EQ(test, (u32)(0x5 << 10), stream[3]); + KUNIT_EXPECT_EQ(test, (u32)0x0, stream[4]); + + vcap_encode_typegroups(stream, 49, typegroups, false); + + KUNIT_EXPECT_EQ(test, (u32)0x8, stream[0]); + KUNIT_EXPECT_EQ(test, (u32)0x0, stream[1]); + KUNIT_EXPECT_EQ(test, (u32)0x1, stream[2]); + KUNIT_EXPECT_EQ(test, (u32)(0x5 << 10), stream[3]); + KUNIT_EXPECT_EQ(test, (u32)0x3, stream[4]); + KUNIT_EXPECT_EQ(test, (u32)0x0, stream[5]); + KUNIT_EXPECT_EQ(test, (u32)0x5, stream[6]); + KUNIT_EXPECT_EQ(test, (u32)0x0, stream[7]); + KUNIT_EXPECT_EQ(test, (u32)0x2, stream[8]); + KUNIT_EXPECT_EQ(test, (u32)0x0, stream[9]); + KUNIT_EXPECT_EQ(test, (u32)27, stream[10]); + KUNIT_EXPECT_EQ(test, (u32)0x0, stream[11]); +} + +/* In this testcase the subword is smaller than a register */ +static void vcap_api_encode_short_field_test(struct kunit *test) +{ + struct vcap_stream_iter iter; + int sw_width = 21; + u32 stream[6] = {0}; + struct vcap_typegroup tgt[] = { + { .offset = 0, .width = 3, .value = 7, }, + { .offset = 21, .width = 2, .value = 3, }, + { .offset = 42, .width = 1, .value = 1, }, + { .offset = 0, .width = 0, .value = 0, }, + }; + struct vcap_field rf = { + .type = VCAP_FIELD_U32, + .offset = 25, + .width = 4, + }; + u8 value[] = {0x5}; + + vcap_iter_init(&iter, sw_width, tgt, rf.offset); + + KUNIT_EXPECT_EQ(test, 1, iter.regs_per_sw); + KUNIT_EXPECT_EQ(test, 21, iter.sw_width); + KUNIT_EXPECT_EQ(test, 25 + 3 + 2, iter.offset); + KUNIT_EXPECT_EQ(test, 1, iter.reg_idx); + KUNIT_EXPECT_EQ(test, 25 + 3 + 2 - sw_width, iter.reg_bitpos); + + vcap_encode_field(stream, &iter, rf.width, value); + + KUNIT_EXPECT_EQ(test, (u32)0x0, stream[0]); + KUNIT_EXPECT_EQ(test, (u32)(0x5 << (25 + 3 + 2 - sw_width)), stream[1]); + KUNIT_EXPECT_EQ(test, (u32)0x0, stream[2]); + KUNIT_EXPECT_EQ(test, (u32)0x0, stream[3]); + KUNIT_EXPECT_EQ(test, (u32)0x0, stream[4]); + KUNIT_EXPECT_EQ(test, (u32)0x0, stream[5]); + + vcap_encode_typegroups(stream, sw_width, tgt, false); + + KUNIT_EXPECT_EQ(test, (u32)7, stream[0]); + KUNIT_EXPECT_EQ(test, (u32)((0x5 << (25 + 3 + 2 - sw_width)) + 3), stream[1]); + KUNIT_EXPECT_EQ(test, (u32)1, stream[2]); + KUNIT_EXPECT_EQ(test, (u32)0, stream[3]); + KUNIT_EXPECT_EQ(test, (u32)0, stream[4]); + KUNIT_EXPECT_EQ(test, (u32)0, stream[5]); +} + +static void vcap_api_encode_keyfield_test(struct kunit *test) +{ + u32 keywords[16] = {0}; + u32 maskwords[16] = {0}; + struct vcap_admin admin = { + .vtype = VCAP_TYPE_IS2, + .cache = { + .keystream = keywords, + .maskstream = maskwords, + .actionstream = keywords, + }, + }; + struct vcap_rule_internal rule = { + .admin = &admin, + .data = { + .keyset = VCAP_KFS_MAC_ETYPE, + }, + .vctrl = &test_vctrl, + }; + struct vcap_client_keyfield ckf = { + .ctrl.list = {}, + .ctrl.key = VCAP_KF_ISDX_CLS, + .ctrl.type = VCAP_FIELD_U32, + .data.u32.value = 0xeef014a1, + .data.u32.mask = 0xfff, + }; + struct vcap_field rf = { + .type = VCAP_FIELD_U32, + .offset = 56, + .width = 12, + }; + struct vcap_typegroup tgt[] = { + { .offset = 0, .width = 2, .value = 2, }, + { .offset = 156, .width = 1, .value = 1, }, + { .offset = 0, .width = 0, .value = 0, }, + }; + + vcap_test_api_init(&admin); + vcap_encode_keyfield(&rule, &ckf, &rf, tgt); + + /* Key */ + KUNIT_EXPECT_EQ(test, (u32)0x0, keywords[0]); + KUNIT_EXPECT_EQ(test, (u32)0x0, keywords[1]); + KUNIT_EXPECT_EQ(test, (u32)(0x04a1 << 6), keywords[2]); + KUNIT_EXPECT_EQ(test, (u32)0x0, keywords[3]); + KUNIT_EXPECT_EQ(test, (u32)0x0, keywords[4]); + KUNIT_EXPECT_EQ(test, (u32)0x0, keywords[5]); + KUNIT_EXPECT_EQ(test, (u32)0x0, keywords[6]); + + /* Mask */ + KUNIT_EXPECT_EQ(test, (u32)0x0, maskwords[0]); + KUNIT_EXPECT_EQ(test, (u32)0x0, maskwords[1]); + KUNIT_EXPECT_EQ(test, (u32)(0x0fff << 6), maskwords[2]); + KUNIT_EXPECT_EQ(test, (u32)0x0, maskwords[3]); + KUNIT_EXPECT_EQ(test, (u32)0x0, maskwords[4]); + KUNIT_EXPECT_EQ(test, (u32)0x0, maskwords[5]); + KUNIT_EXPECT_EQ(test, (u32)0x0, maskwords[6]); +} + +static void vcap_api_encode_max_keyfield_test(struct kunit *test) +{ + int idx; + u32 keywords[6] = {0}; + u32 maskwords[6] = {0}; + struct vcap_admin admin = { + .vtype = VCAP_TYPE_IS2, + /* IS2 sw_width = 52 bit */ + .cache = { + .keystream = keywords, + .maskstream = maskwords, + .actionstream = keywords, + }, + }; + struct vcap_rule_internal rule = { + .admin = &admin, + .data = { + .keyset = VCAP_KFS_IP_7TUPLE, + }, + .vctrl = &test_vctrl, + }; + struct vcap_client_keyfield ckf = { + .ctrl.list = {}, + .ctrl.key = VCAP_KF_L3_IP6_DIP, + .ctrl.type = VCAP_FIELD_U128, + .data.u128.value = { 0xa1, 0xa2, 0xa3, 0xa4, 0, 0, 0x43, 0, + 0, 0, 0, 0, 0, 0, 0x78, 0x8e, }, + .data.u128.mask = { 0xff, 0xff, 0xff, 0xff, 0, 0, 0xff, 0, + 0, 0, 0, 0, 0, 0, 0xff, 0xff }, + }; + struct vcap_field rf = { + .type = VCAP_FIELD_U128, + .offset = 0, + .width = 128, + }; + struct vcap_typegroup tgt[] = { + { .offset = 0, .width = 2, .value = 2, }, + { .offset = 156, .width = 1, .value = 1, }, + { .offset = 0, .width = 0, .value = 0, }, + }; + u32 keyres[] = { + 0x928e8a84, + 0x000c0002, + 0x00000010, + 0x00000000, + 0x0239e000, + 0x00000000, + }; + u32 mskres[] = { + 0xfffffffc, + 0x000c0003, + 0x0000003f, + 0x00000000, + 0x03fffc00, + 0x00000000, + }; + + vcap_encode_keyfield(&rule, &ckf, &rf, tgt); + + /* Key */ + for (idx = 0; idx < ARRAY_SIZE(keyres); ++idx) + KUNIT_EXPECT_EQ(test, keyres[idx], keywords[idx]); + /* Mask */ + for (idx = 0; idx < ARRAY_SIZE(mskres); ++idx) + KUNIT_EXPECT_EQ(test, mskres[idx], maskwords[idx]); +} + +static void vcap_api_encode_actionfield_test(struct kunit *test) +{ + u32 actwords[16] = {0}; + int sw_width = 21; + struct vcap_admin admin = { + .vtype = VCAP_TYPE_ES2, /* act_width = 21 */ + .cache = { + .actionstream = actwords, + }, + }; + struct vcap_rule_internal rule = { + .admin = &admin, + .data = { + .actionset = VCAP_AFS_BASE_TYPE, + }, + .vctrl = &test_vctrl, + }; + struct vcap_client_actionfield caf = { + .ctrl.list = {}, + .ctrl.action = VCAP_AF_POLICE_IDX, + .ctrl.type = VCAP_FIELD_U32, + .data.u32.value = 0x67908032, + }; + struct vcap_field rf = { + .type = VCAP_FIELD_U32, + .offset = 35, + .width = 6, + }; + struct vcap_typegroup tgt[] = { + { .offset = 0, .width = 2, .value = 2, }, + { .offset = 21, .width = 1, .value = 1, }, + { .offset = 42, .width = 1, .value = 0, }, + { .offset = 0, .width = 0, .value = 0, }, + }; + + vcap_encode_actionfield(&rule, &caf, &rf, tgt); + + /* Action */ + KUNIT_EXPECT_EQ(test, (u32)0x0, actwords[0]); + KUNIT_EXPECT_EQ(test, (u32)((0x32 << (35 + 2 + 1 - sw_width)) & 0x1fffff), actwords[1]); + KUNIT_EXPECT_EQ(test, (u32)((0x32 >> ((2 * sw_width) - 38 - 1))), actwords[2]); + KUNIT_EXPECT_EQ(test, (u32)0x0, actwords[3]); + KUNIT_EXPECT_EQ(test, (u32)0x0, actwords[4]); + KUNIT_EXPECT_EQ(test, (u32)0x0, actwords[5]); + KUNIT_EXPECT_EQ(test, (u32)0x0, actwords[6]); +} + +static void vcap_api_keyfield_typegroup_test(struct kunit *test) +{ + const struct vcap_typegroup *tg; + + tg = vcap_keyfield_typegroup(&test_vctrl, VCAP_TYPE_IS2, VCAP_KFS_MAC_ETYPE); + KUNIT_EXPECT_PTR_NE(test, NULL, tg); + KUNIT_EXPECT_EQ(test, 0, tg[0].offset); + KUNIT_EXPECT_EQ(test, 2, tg[0].width); + KUNIT_EXPECT_EQ(test, 2, tg[0].value); + KUNIT_EXPECT_EQ(test, 156, tg[1].offset); + KUNIT_EXPECT_EQ(test, 1, tg[1].width); + KUNIT_EXPECT_EQ(test, 0, tg[1].value); + KUNIT_EXPECT_EQ(test, 0, tg[2].offset); + KUNIT_EXPECT_EQ(test, 0, tg[2].width); + KUNIT_EXPECT_EQ(test, 0, tg[2].value); + + tg = vcap_keyfield_typegroup(&test_vctrl, VCAP_TYPE_ES2, VCAP_KFS_LL_FULL); + KUNIT_EXPECT_PTR_EQ(test, NULL, tg); +} + +static void vcap_api_actionfield_typegroup_test(struct kunit *test) +{ + const struct vcap_typegroup *tg; + + tg = vcap_actionfield_typegroup(&test_vctrl, VCAP_TYPE_IS0, VCAP_AFS_FULL); + KUNIT_EXPECT_PTR_NE(test, NULL, tg); + KUNIT_EXPECT_EQ(test, 0, tg[0].offset); + KUNIT_EXPECT_EQ(test, 3, tg[0].width); + KUNIT_EXPECT_EQ(test, 4, tg[0].value); + KUNIT_EXPECT_EQ(test, 110, tg[1].offset); + KUNIT_EXPECT_EQ(test, 2, tg[1].width); + KUNIT_EXPECT_EQ(test, 0, tg[1].value); + KUNIT_EXPECT_EQ(test, 220, tg[2].offset); + KUNIT_EXPECT_EQ(test, 2, tg[2].width); + KUNIT_EXPECT_EQ(test, 0, tg[2].value); + KUNIT_EXPECT_EQ(test, 0, tg[3].offset); + KUNIT_EXPECT_EQ(test, 0, tg[3].width); + KUNIT_EXPECT_EQ(test, 0, tg[3].value); + + tg = vcap_actionfield_typegroup(&test_vctrl, VCAP_TYPE_IS2, VCAP_AFS_CLASSIFICATION); + KUNIT_EXPECT_PTR_EQ(test, NULL, tg); +} + +static void vcap_api_vcap_keyfields_test(struct kunit *test) +{ + const struct vcap_field *ft; + + ft = vcap_keyfields(&test_vctrl, VCAP_TYPE_IS2, VCAP_KFS_MAC_ETYPE); + KUNIT_EXPECT_PTR_NE(test, NULL, ft); + + /* Keyset that is not available and within the maximum keyset enum value */ + ft = vcap_keyfields(&test_vctrl, VCAP_TYPE_ES2, VCAP_KFS_PURE_5TUPLE_IP4); + KUNIT_EXPECT_PTR_EQ(test, NULL, ft); + + /* Keyset that is not available and beyond the maximum keyset enum value */ + ft = vcap_keyfields(&test_vctrl, VCAP_TYPE_ES2, VCAP_KFS_LL_FULL); + KUNIT_EXPECT_PTR_EQ(test, NULL, ft); +} + +static void vcap_api_vcap_actionfields_test(struct kunit *test) +{ + const struct vcap_field *ft; + + ft = vcap_actionfields(&test_vctrl, VCAP_TYPE_IS0, VCAP_AFS_FULL); + KUNIT_EXPECT_PTR_NE(test, NULL, ft); + + ft = vcap_actionfields(&test_vctrl, VCAP_TYPE_IS2, VCAP_AFS_FULL); + KUNIT_EXPECT_PTR_EQ(test, NULL, ft); + + ft = vcap_actionfields(&test_vctrl, VCAP_TYPE_IS2, VCAP_AFS_CLASSIFICATION); + KUNIT_EXPECT_PTR_EQ(test, NULL, ft); +} + +static void vcap_api_encode_rule_keyset_test(struct kunit *test) +{ + u32 keywords[16] = {0}; + u32 maskwords[16] = {0}; + struct vcap_admin admin = { + .vtype = VCAP_TYPE_IS2, + .cache = { + .keystream = keywords, + .maskstream = maskwords, + }, + }; + struct vcap_rule_internal rule = { + .admin = &admin, + .data = { + .keyset = VCAP_KFS_MAC_ETYPE, + }, + .vctrl = &test_vctrl, + }; + struct vcap_client_keyfield ckf[] = { + { + .ctrl.key = VCAP_KF_TYPE, + .ctrl.type = VCAP_FIELD_U32, + .data.u32.value = 0x00, + .data.u32.mask = 0x0f, + }, + { + .ctrl.key = VCAP_KF_LOOKUP_FIRST_IS, + .ctrl.type = VCAP_FIELD_BIT, + .data.u1.value = 0x01, + .data.u1.mask = 0x01, + }, + { + .ctrl.key = VCAP_KF_IF_IGR_PORT_MASK_L3, + .ctrl.type = VCAP_FIELD_BIT, + .data.u1.value = 0x00, + .data.u1.mask = 0x01, + }, + { + .ctrl.key = VCAP_KF_IF_IGR_PORT_MASK_RNG, + .ctrl.type = VCAP_FIELD_U32, + .data.u32.value = 0x00, + .data.u32.mask = 0x0f, + }, + { + .ctrl.key = VCAP_KF_IF_IGR_PORT_MASK, + .ctrl.type = VCAP_FIELD_U72, + .data.u72.value = {0x0, 0x00, 0x00, 0x00}, + .data.u72.mask = {0xfd, 0xff, 0xff, 0xff}, + }, + { + .ctrl.key = VCAP_KF_L2_DMAC, + .ctrl.type = VCAP_FIELD_U48, + /* Opposite endianness */ + .data.u48.value = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06}, + .data.u48.mask = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, + }, + { + .ctrl.key = VCAP_KF_ETYPE_LEN_IS, + .ctrl.type = VCAP_FIELD_BIT, + .data.u1.value = 0x01, + .data.u1.mask = 0x01, + }, + { + .ctrl.key = VCAP_KF_ETYPE, + .ctrl.type = VCAP_FIELD_U32, + .data.u32.value = 0xaabb, + .data.u32.mask = 0xffff, + }, + }; + int idx; + int ret; + + /* Empty entry list */ + INIT_LIST_HEAD(&rule.data.keyfields); + ret = vcap_encode_rule_keyset(&rule); + KUNIT_EXPECT_EQ(test, -EINVAL, ret); + + for (idx = 0; idx < ARRAY_SIZE(ckf); idx++) + list_add_tail(&ckf[idx].ctrl.list, &rule.data.keyfields); + ret = vcap_encode_rule_keyset(&rule); + KUNIT_EXPECT_EQ(test, 0, ret); + + /* The key and mask values below are from an actual Sparx5 rule config */ + /* Key */ + KUNIT_EXPECT_EQ(test, (u32)0x00000042, keywords[0]); + KUNIT_EXPECT_EQ(test, (u32)0x00000000, keywords[1]); + KUNIT_EXPECT_EQ(test, (u32)0x00000000, keywords[2]); + KUNIT_EXPECT_EQ(test, (u32)0x00020100, keywords[3]); + KUNIT_EXPECT_EQ(test, (u32)0x60504030, keywords[4]); + KUNIT_EXPECT_EQ(test, (u32)0x00000000, keywords[5]); + KUNIT_EXPECT_EQ(test, (u32)0x00000000, keywords[6]); + KUNIT_EXPECT_EQ(test, (u32)0x0002aaee, keywords[7]); + KUNIT_EXPECT_EQ(test, (u32)0x00000000, keywords[8]); + KUNIT_EXPECT_EQ(test, (u32)0x00000000, keywords[9]); + KUNIT_EXPECT_EQ(test, (u32)0x00000000, keywords[10]); + KUNIT_EXPECT_EQ(test, (u32)0x00000000, keywords[11]); + + /* Mask: they will be inverted when applied to the register */ + KUNIT_EXPECT_EQ(test, (u32)~0x00b07f80, maskwords[0]); + KUNIT_EXPECT_EQ(test, (u32)~0xfff00000, maskwords[1]); + KUNIT_EXPECT_EQ(test, (u32)~0xfffffffc, maskwords[2]); + KUNIT_EXPECT_EQ(test, (u32)~0xfff000ff, maskwords[3]); + KUNIT_EXPECT_EQ(test, (u32)~0x00000000, maskwords[4]); + KUNIT_EXPECT_EQ(test, (u32)~0xfffffff0, maskwords[5]); + KUNIT_EXPECT_EQ(test, (u32)~0xfffffffe, maskwords[6]); + KUNIT_EXPECT_EQ(test, (u32)~0xfffc0001, maskwords[7]); + KUNIT_EXPECT_EQ(test, (u32)~0xffffffff, maskwords[8]); + KUNIT_EXPECT_EQ(test, (u32)~0xffffffff, maskwords[9]); + KUNIT_EXPECT_EQ(test, (u32)~0xffffffff, maskwords[10]); + KUNIT_EXPECT_EQ(test, (u32)~0xffffffff, maskwords[11]); +} + +static void vcap_api_encode_rule_actionset_test(struct kunit *test) +{ + u32 actwords[16] = {0}; + struct vcap_admin admin = { + .vtype = VCAP_TYPE_IS2, + .cache = { + .actionstream = actwords, + }, + }; + struct vcap_rule_internal rule = { + .admin = &admin, + .data = { + .actionset = VCAP_AFS_BASE_TYPE, + }, + .vctrl = &test_vctrl, + }; + struct vcap_client_actionfield caf[] = { + { + .ctrl.action = VCAP_AF_MATCH_ID, + .ctrl.type = VCAP_FIELD_U32, + .data.u32.value = 0x01, + }, + { + .ctrl.action = VCAP_AF_MATCH_ID_MASK, + .ctrl.type = VCAP_FIELD_U32, + .data.u32.value = 0x01, + }, + { + .ctrl.action = VCAP_AF_CNT_ID, + .ctrl.type = VCAP_FIELD_U32, + .data.u32.value = 0x64, + }, + }; + int idx; + int ret; + + /* Empty entry list */ + INIT_LIST_HEAD(&rule.data.actionfields); + ret = vcap_encode_rule_actionset(&rule); + /* We allow rules with no actions */ + KUNIT_EXPECT_EQ(test, 0, ret); + + for (idx = 0; idx < ARRAY_SIZE(caf); idx++) + list_add_tail(&caf[idx].ctrl.list, &rule.data.actionfields); + ret = vcap_encode_rule_actionset(&rule); + KUNIT_EXPECT_EQ(test, 0, ret); + + /* The action values below are from an actual Sparx5 rule config */ + KUNIT_EXPECT_EQ(test, (u32)0x00000002, actwords[0]); + KUNIT_EXPECT_EQ(test, (u32)0x00000000, actwords[1]); + KUNIT_EXPECT_EQ(test, (u32)0x00000000, actwords[2]); + KUNIT_EXPECT_EQ(test, (u32)0x00000000, actwords[3]); + KUNIT_EXPECT_EQ(test, (u32)0x00000000, actwords[4]); + KUNIT_EXPECT_EQ(test, (u32)0x00100000, actwords[5]); + KUNIT_EXPECT_EQ(test, (u32)0x06400010, actwords[6]); + KUNIT_EXPECT_EQ(test, (u32)0x00000000, actwords[7]); + KUNIT_EXPECT_EQ(test, (u32)0x00000000, actwords[8]); + KUNIT_EXPECT_EQ(test, (u32)0x00000000, actwords[9]); + KUNIT_EXPECT_EQ(test, (u32)0x00000000, actwords[10]); + KUNIT_EXPECT_EQ(test, (u32)0x00000000, actwords[11]); +} + +static struct kunit_case vcap_api_encoding_test_cases[] = { + KUNIT_CASE(vcap_api_set_bit_1_test), + KUNIT_CASE(vcap_api_set_bit_0_test), + KUNIT_CASE(vcap_api_iterator_init_test), + KUNIT_CASE(vcap_api_iterator_next_test), + KUNIT_CASE(vcap_api_encode_typegroups_test), + KUNIT_CASE(vcap_api_encode_bit_test), + KUNIT_CASE(vcap_api_encode_field_test), + KUNIT_CASE(vcap_api_encode_short_field_test), + KUNIT_CASE(vcap_api_encode_keyfield_test), + KUNIT_CASE(vcap_api_encode_max_keyfield_test), + KUNIT_CASE(vcap_api_encode_actionfield_test), + KUNIT_CASE(vcap_api_keyfield_typegroup_test), + KUNIT_CASE(vcap_api_actionfield_typegroup_test), + KUNIT_CASE(vcap_api_vcap_keyfields_test), + KUNIT_CASE(vcap_api_vcap_actionfields_test), + KUNIT_CASE(vcap_api_encode_rule_keyset_test), + KUNIT_CASE(vcap_api_encode_rule_actionset_test), + {} +}; + +static struct kunit_suite vcap_api_encoding_test_suite = { + .name = "VCAP_API_Encoding_Testsuite", + .test_cases = vcap_api_encoding_test_cases, +}; + +kunit_test_suite(vcap_api_encoding_test_suite); From 404c76783f322120266a4ef659abe418dc74f5c6 Mon Sep 17 00:00:00 2001 From: Amit Cohen Date: Thu, 20 Oct 2022 17:20:03 +0200 Subject: [PATCH 0284/2002] ethtool: Add support for 800Gbps link modes Add support for 800Gbps speed, link modes of 100Gbps per lane. As mentioned in slide 21 in IEEE documentation [1], all adopted 802.3df copper and optical PMDs baselines using 100G/lane will be supported. Add the relevant PMDs which are mentioned in slide 5 in IEEE documentation [1] and were approved on 10-2022 [2]: BP - KR8 Cu Cable - CR8 MMF 50m - VR8 MMF 100m - SR8 SMF 500m - DR8 SMF 2km - DR8-2 [1]: https://www.ieee802.org/3/df/public/22_10/22_1004/shrikhande_3df_01a_221004.pdf [2]: https://ieee802.org/3/df/KeyMotions_3df_221005.pdf Signed-off-by: Amit Cohen Reviewed-by: Ido Schimmel Signed-off-by: Petr Machata Signed-off-by: David S. Miller --- drivers/net/phy/phy-core.c | 11 ++++++++++- include/uapi/linux/ethtool.h | 8 ++++++++ net/ethtool/common.c | 14 ++++++++++++++ 3 files changed, 32 insertions(+), 1 deletion(-) diff --git a/drivers/net/phy/phy-core.c b/drivers/net/phy/phy-core.c index 2c8bf438ea61..5d08c627a516 100644 --- a/drivers/net/phy/phy-core.c +++ b/drivers/net/phy/phy-core.c @@ -13,7 +13,7 @@ */ const char *phy_speed_to_str(int speed) { - BUILD_BUG_ON_MSG(__ETHTOOL_LINK_MODE_MASK_NBITS != 93, + BUILD_BUG_ON_MSG(__ETHTOOL_LINK_MODE_MASK_NBITS != 99, "Enum ethtool_link_mode_bit_indices and phylib are out of sync. " "If a speed or mode has been added please update phy_speed_to_str " "and the PHY settings array.\n"); @@ -49,6 +49,8 @@ const char *phy_speed_to_str(int speed) return "200Gbps"; case SPEED_400000: return "400Gbps"; + case SPEED_800000: + return "800Gbps"; case SPEED_UNKNOWN: return "Unknown"; default: @@ -157,6 +159,13 @@ EXPORT_SYMBOL_GPL(phy_interface_num_ports); .bit = ETHTOOL_LINK_MODE_ ## b ## _BIT} static const struct phy_setting settings[] = { + /* 800G */ + PHY_SETTING( 800000, FULL, 800000baseCR8_Full ), + PHY_SETTING( 800000, FULL, 800000baseKR8_Full ), + PHY_SETTING( 800000, FULL, 800000baseDR8_Full ), + PHY_SETTING( 800000, FULL, 800000baseDR8_2_Full ), + PHY_SETTING( 800000, FULL, 800000baseSR8_Full ), + PHY_SETTING( 800000, FULL, 800000baseVR8_Full ), /* 400G */ PHY_SETTING( 400000, FULL, 400000baseCR8_Full ), PHY_SETTING( 400000, FULL, 400000baseKR8_Full ), diff --git a/include/uapi/linux/ethtool.h b/include/uapi/linux/ethtool.h index dc2aa3d75b39..f341de2ae612 100644 --- a/include/uapi/linux/ethtool.h +++ b/include/uapi/linux/ethtool.h @@ -1737,6 +1737,13 @@ enum ethtool_link_mode_bit_indices { ETHTOOL_LINK_MODE_100baseFX_Half_BIT = 90, ETHTOOL_LINK_MODE_100baseFX_Full_BIT = 91, ETHTOOL_LINK_MODE_10baseT1L_Full_BIT = 92, + ETHTOOL_LINK_MODE_800000baseCR8_Full_BIT = 93, + ETHTOOL_LINK_MODE_800000baseKR8_Full_BIT = 94, + ETHTOOL_LINK_MODE_800000baseDR8_Full_BIT = 95, + ETHTOOL_LINK_MODE_800000baseDR8_2_Full_BIT = 96, + ETHTOOL_LINK_MODE_800000baseSR8_Full_BIT = 97, + ETHTOOL_LINK_MODE_800000baseVR8_Full_BIT = 98, + /* must be last entry */ __ETHTOOL_LINK_MODE_MASK_NBITS }; @@ -1848,6 +1855,7 @@ enum ethtool_link_mode_bit_indices { #define SPEED_100000 100000 #define SPEED_200000 200000 #define SPEED_400000 400000 +#define SPEED_800000 800000 #define SPEED_UNKNOWN -1 diff --git a/net/ethtool/common.c b/net/ethtool/common.c index 566adf85e658..ee3e02da0013 100644 --- a/net/ethtool/common.c +++ b/net/ethtool/common.c @@ -202,6 +202,12 @@ const char link_mode_names[][ETH_GSTRING_LEN] = { __DEFINE_LINK_MODE_NAME(100, FX, Half), __DEFINE_LINK_MODE_NAME(100, FX, Full), __DEFINE_LINK_MODE_NAME(10, T1L, Full), + __DEFINE_LINK_MODE_NAME(800000, CR8, Full), + __DEFINE_LINK_MODE_NAME(800000, KR8, Full), + __DEFINE_LINK_MODE_NAME(800000, DR8, Full), + __DEFINE_LINK_MODE_NAME(800000, DR8_2, Full), + __DEFINE_LINK_MODE_NAME(800000, SR8, Full), + __DEFINE_LINK_MODE_NAME(800000, VR8, Full), }; static_assert(ARRAY_SIZE(link_mode_names) == __ETHTOOL_LINK_MODE_MASK_NBITS); @@ -238,6 +244,8 @@ static_assert(ARRAY_SIZE(link_mode_names) == __ETHTOOL_LINK_MODE_MASK_NBITS); #define __LINK_MODE_LANES_X 1 #define __LINK_MODE_LANES_FX 1 #define __LINK_MODE_LANES_T1L 1 +#define __LINK_MODE_LANES_VR8 8 +#define __LINK_MODE_LANES_DR8_2 8 #define __DEFINE_LINK_MODE_PARAMS(_speed, _type, _duplex) \ [ETHTOOL_LINK_MODE(_speed, _type, _duplex)] = { \ @@ -352,6 +360,12 @@ const struct link_mode_info link_mode_params[] = { __DEFINE_LINK_MODE_PARAMS(100, FX, Half), __DEFINE_LINK_MODE_PARAMS(100, FX, Full), __DEFINE_LINK_MODE_PARAMS(10, T1L, Full), + __DEFINE_LINK_MODE_PARAMS(800000, CR8, Full), + __DEFINE_LINK_MODE_PARAMS(800000, KR8, Full), + __DEFINE_LINK_MODE_PARAMS(800000, DR8, Full), + __DEFINE_LINK_MODE_PARAMS(800000, DR8_2, Full), + __DEFINE_LINK_MODE_PARAMS(800000, SR8, Full), + __DEFINE_LINK_MODE_PARAMS(800000, VR8, Full), }; static_assert(ARRAY_SIZE(link_mode_params) == __ETHTOOL_LINK_MODE_MASK_NBITS); From cceef209ddd7b4c0059cc41bdab103b804043d1f Mon Sep 17 00:00:00 2001 From: Amit Cohen Date: Thu, 20 Oct 2022 17:20:04 +0200 Subject: [PATCH 0285/2002] mlxsw: Add support for 800Gbps link modes Add support for 800Gbps speed, link modes of 100Gbps per lane. Signed-off-by: Amit Cohen Reviewed-by: Ido Schimmel Signed-off-by: Petr Machata Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/reg.h | 1 + .../mellanox/mlxsw/spectrum_ethtool.c | 21 +++++++++++++++++++ 2 files changed, 22 insertions(+) diff --git a/drivers/net/ethernet/mellanox/mlxsw/reg.h b/drivers/net/ethernet/mellanox/mlxsw/reg.h index 0777bed5bb1a..b74f30ec629a 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/reg.h +++ b/drivers/net/ethernet/mellanox/mlxsw/reg.h @@ -4620,6 +4620,7 @@ MLXSW_ITEM32(reg, ptys, an_status, 0x04, 28, 4); #define MLXSW_REG_PTYS_EXT_ETH_SPEED_100GAUI_2_100GBASE_CR2_KR2 BIT(10) #define MLXSW_REG_PTYS_EXT_ETH_SPEED_200GAUI_4_200GBASE_CR4_KR4 BIT(12) #define MLXSW_REG_PTYS_EXT_ETH_SPEED_400GAUI_8 BIT(15) +#define MLXSW_REG_PTYS_EXT_ETH_SPEED_800GAUI_8 BIT(19) /* reg_ptys_ext_eth_proto_cap * Extended Ethernet port supported speeds and protocols. diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_ethtool.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_ethtool.c index dcd79d7e2af4..472830d07ac1 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_ethtool.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_ethtool.c @@ -1672,6 +1672,19 @@ mlxsw_sp2_mask_ethtool_400gaui_8[] = { #define MLXSW_SP2_MASK_ETHTOOL_400GAUI_8_LEN \ ARRAY_SIZE(mlxsw_sp2_mask_ethtool_400gaui_8) +static const enum ethtool_link_mode_bit_indices +mlxsw_sp2_mask_ethtool_800gaui_8[] = { + ETHTOOL_LINK_MODE_800000baseCR8_Full_BIT, + ETHTOOL_LINK_MODE_800000baseKR8_Full_BIT, + ETHTOOL_LINK_MODE_800000baseDR8_Full_BIT, + ETHTOOL_LINK_MODE_800000baseDR8_2_Full_BIT, + ETHTOOL_LINK_MODE_800000baseSR8_Full_BIT, + ETHTOOL_LINK_MODE_800000baseVR8_Full_BIT, +}; + +#define MLXSW_SP2_MASK_ETHTOOL_800GAUI_8_LEN \ + ARRAY_SIZE(mlxsw_sp2_mask_ethtool_800gaui_8) + #define MLXSW_SP_PORT_MASK_WIDTH_1X BIT(0) #define MLXSW_SP_PORT_MASK_WIDTH_2X BIT(1) #define MLXSW_SP_PORT_MASK_WIDTH_4X BIT(2) @@ -1820,6 +1833,14 @@ static const struct mlxsw_sp2_port_link_mode mlxsw_sp2_port_link_mode[] = { .speed = SPEED_400000, .width = 8, }, + { + .mask = MLXSW_REG_PTYS_EXT_ETH_SPEED_800GAUI_8, + .mask_ethtool = mlxsw_sp2_mask_ethtool_800gaui_8, + .m_ethtool_len = MLXSW_SP2_MASK_ETHTOOL_800GAUI_8_LEN, + .mask_sup_width = MLXSW_SP_PORT_MASK_WIDTH_8X, + .speed = SPEED_800000, + .width = 8, + }, }; #define MLXSW_SP2_PORT_LINK_MODE_LEN ARRAY_SIZE(mlxsw_sp2_port_link_mode) From 41305d3781d705d6ea2c9cac632e6a4063e0f1ea Mon Sep 17 00:00:00 2001 From: Amit Cohen Date: Thu, 20 Oct 2022 17:20:05 +0200 Subject: [PATCH 0286/2002] bonding: 3ad: Add support for 800G speed Add support for 800Gbps speed to allow using 3ad mode with 800G devices. Signed-off-by: Amit Cohen Reviewed-by: Ido Schimmel Signed-off-by: Petr Machata Acked-by: Jay Vosburgh Signed-off-by: David S. Miller --- drivers/net/bonding/bond_3ad.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/net/bonding/bond_3ad.c b/drivers/net/bonding/bond_3ad.c index e58a1e0cadd2..455b555275f1 100644 --- a/drivers/net/bonding/bond_3ad.c +++ b/drivers/net/bonding/bond_3ad.c @@ -75,6 +75,7 @@ enum ad_link_speed_type { AD_LINK_SPEED_100000MBPS, AD_LINK_SPEED_200000MBPS, AD_LINK_SPEED_400000MBPS, + AD_LINK_SPEED_800000MBPS, }; /* compare MAC addresses */ @@ -251,6 +252,7 @@ static inline int __check_agg_selection_timer(struct port *port) * %AD_LINK_SPEED_100000MBPS * %AD_LINK_SPEED_200000MBPS * %AD_LINK_SPEED_400000MBPS + * %AD_LINK_SPEED_800000MBPS */ static u16 __get_link_speed(struct port *port) { @@ -326,6 +328,10 @@ static u16 __get_link_speed(struct port *port) speed = AD_LINK_SPEED_400000MBPS; break; + case SPEED_800000: + speed = AD_LINK_SPEED_800000MBPS; + break; + default: /* unknown speed value from ethtool. shouldn't happen */ if (slave->speed != SPEED_UNKNOWN) @@ -753,6 +759,9 @@ static u32 __get_agg_bandwidth(struct aggregator *aggregator) case AD_LINK_SPEED_400000MBPS: bandwidth = nports * 400000; break; + case AD_LINK_SPEED_800000MBPS: + bandwidth = nports * 800000; + break; default: bandwidth = 0; /* to silence the compiler */ } From a5ef058dc4d9a3e60d1808a0700e18e0e37e408e Mon Sep 17 00:00:00 2001 From: Paolo Abeni Date: Thu, 20 Oct 2022 19:48:51 +0200 Subject: [PATCH 0287/2002] net: introduce and use custom sockopt socket flag We will soon introduce custom setsockopt for UDP sockets, too. Instead of doing even more complex arbitrary checks inside sock_use_custom_sol_socket(), add a new socket flag and set it for the relevant socket types (currently only MPTCP). Reviewed-by: Matthieu Baerts Signed-off-by: Paolo Abeni Reviewed-by: Eric Dumazet Acked-by: Kuniyuki Iwashima Signed-off-by: David S. Miller --- include/linux/net.h | 1 + net/mptcp/protocol.c | 4 ++++ net/socket.c | 8 +------- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/include/linux/net.h b/include/linux/net.h index 711c3593c3b8..59350fd85823 100644 --- a/include/linux/net.h +++ b/include/linux/net.h @@ -41,6 +41,7 @@ struct net; #define SOCK_NOSPACE 2 #define SOCK_PASSCRED 3 #define SOCK_PASSSEC 4 +#define SOCK_CUSTOM_SOCKOPT 5 #ifndef ARCH_HAS_SOCKET_TYPES /** diff --git a/net/mptcp/protocol.c b/net/mptcp/protocol.c index 2e16c897c229..e60d144bfb2d 100644 --- a/net/mptcp/protocol.c +++ b/net/mptcp/protocol.c @@ -2708,6 +2708,8 @@ static int mptcp_init_sock(struct sock *sk) if (ret) return ret; + set_bit(SOCK_CUSTOM_SOCKOPT, &sk->sk_socket->flags); + /* fetch the ca name; do it outside __mptcp_init_sock(), so that clone will * propagate the correct value */ @@ -3684,6 +3686,8 @@ static int mptcp_stream_accept(struct socket *sock, struct socket *newsock, struct mptcp_subflow_context *subflow; struct sock *newsk = newsock->sk; + set_bit(SOCK_CUSTOM_SOCKOPT, &newsock->flags); + lock_sock(newsk); /* PM/worker can now acquire the first subflow socket diff --git a/net/socket.c b/net/socket.c index 00da9ce3dba0..55c5d536e5f6 100644 --- a/net/socket.c +++ b/net/socket.c @@ -2199,13 +2199,7 @@ SYSCALL_DEFINE4(recv, int, fd, void __user *, ubuf, size_t, size, static bool sock_use_custom_sol_socket(const struct socket *sock) { - const struct sock *sk = sock->sk; - - /* Use sock->ops->setsockopt() for MPTCP */ - return IS_ENABLED(CONFIG_MPTCP) && - sk->sk_protocol == IPPROTO_MPTCP && - sk->sk_type == SOCK_STREAM && - (sk->sk_family == AF_INET || sk->sk_family == AF_INET6); + return test_bit(SOCK_CUSTOM_SOCKOPT, &sock->flags); } /* From 8a3854c7b8e4532063b14bed34115079b7d0cb36 Mon Sep 17 00:00:00 2001 From: Paolo Abeni Date: Thu, 20 Oct 2022 19:48:52 +0200 Subject: [PATCH 0288/2002] udp: track the forward memory release threshold in an hot cacheline When the receiver process and the BH runs on different cores, udp_rmem_release() experience a cache miss while accessing sk_rcvbuf, as the latter shares the same cacheline with sk_forward_alloc, written by the BH. With this patch, UDP tracks the rcvbuf value and its update via custom SOL_SOCKET socket options, and copies the forward memory threshold value used by udp_rmem_release() in a different cacheline, already accessed by the above function and uncontended. Since the UDP socket init operation grown a bit, factor out the common code between v4 and v6 in a shared helper. Overall the above give a 10% peek throughput increase under UDP flood. Signed-off-by: Paolo Abeni Reviewed-by: Eric Dumazet Acked-by: Kuniyuki Iwashima Signed-off-by: David S. Miller --- include/linux/udp.h | 3 +++ include/net/udp.h | 9 +++++++++ net/ipv4/udp.c | 18 +++++++++++++++--- net/ipv6/udp.c | 4 ++-- 4 files changed, 29 insertions(+), 5 deletions(-) diff --git a/include/linux/udp.h b/include/linux/udp.h index e96da4157d04..5cdba00a904a 100644 --- a/include/linux/udp.h +++ b/include/linux/udp.h @@ -87,6 +87,9 @@ struct udp_sock { /* This field is dirtied by udp_recvmsg() */ int forward_deficit; + + /* This fields follows rcvbuf value, and is touched by udp_recvmsg */ + int forward_threshold; }; #define UDP_MAX_SEGMENTS (1 << 6UL) diff --git a/include/net/udp.h b/include/net/udp.h index fee053bcd17c..de4b528522bb 100644 --- a/include/net/udp.h +++ b/include/net/udp.h @@ -174,6 +174,15 @@ INDIRECT_CALLABLE_DECLARE(int udpv6_rcv(struct sk_buff *)); struct sk_buff *__udp_gso_segment(struct sk_buff *gso_skb, netdev_features_t features, bool is_ipv6); +static inline void udp_lib_init_sock(struct sock *sk) +{ + struct udp_sock *up = udp_sk(sk); + + skb_queue_head_init(&up->reader_queue); + up->forward_threshold = sk->sk_rcvbuf >> 2; + set_bit(SOCK_CUSTOM_SOCKOPT, &sk->sk_socket->flags); +} + /* hash routines shared between UDPv4/6 and UDP-Litev4/6 */ static inline int udp_lib_hash(struct sock *sk) { diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index c83e5271030b..e77c8f0e9087 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c @@ -1448,7 +1448,7 @@ static void udp_rmem_release(struct sock *sk, int size, int partial, if (likely(partial)) { up->forward_deficit += size; size = up->forward_deficit; - if (size < (sk->sk_rcvbuf >> 2) && + if (size < READ_ONCE(up->forward_threshold) && !skb_queue_empty(&up->reader_queue)) return; } else { @@ -1622,7 +1622,7 @@ static void udp_destruct_sock(struct sock *sk) int udp_init_sock(struct sock *sk) { - skb_queue_head_init(&udp_sk(sk)->reader_queue); + udp_lib_init_sock(sk); sk->sk_destruct = udp_destruct_sock; return 0; } @@ -2671,6 +2671,18 @@ int udp_lib_setsockopt(struct sock *sk, int level, int optname, int err = 0; int is_udplite = IS_UDPLITE(sk); + if (level == SOL_SOCKET) { + err = sk_setsockopt(sk, level, optname, optval, optlen); + + if (optname == SO_RCVBUF || optname == SO_RCVBUFFORCE) { + sockopt_lock_sock(sk); + /* paired with READ_ONCE in udp_rmem_release() */ + WRITE_ONCE(up->forward_threshold, sk->sk_rcvbuf >> 2); + sockopt_release_sock(sk); + } + return err; + } + if (optlen < sizeof(int)) return -EINVAL; @@ -2784,7 +2796,7 @@ EXPORT_SYMBOL(udp_lib_setsockopt); int udp_setsockopt(struct sock *sk, int level, int optname, sockptr_t optval, unsigned int optlen) { - if (level == SOL_UDP || level == SOL_UDPLITE) + if (level == SOL_UDP || level == SOL_UDPLITE || level == SOL_SOCKET) return udp_lib_setsockopt(sk, level, optname, optval, optlen, udp_push_pending_frames); diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c index 2260406740d3..297f7cc06044 100644 --- a/net/ipv6/udp.c +++ b/net/ipv6/udp.c @@ -64,7 +64,7 @@ static void udpv6_destruct_sock(struct sock *sk) int udpv6_init_sock(struct sock *sk) { - skb_queue_head_init(&udp_sk(sk)->reader_queue); + udp_lib_init_sock(sk); sk->sk_destruct = udpv6_destruct_sock; return 0; } @@ -1669,7 +1669,7 @@ void udpv6_destroy_sock(struct sock *sk) int udpv6_setsockopt(struct sock *sk, int level, int optname, sockptr_t optval, unsigned int optlen) { - if (level == SOL_UDP || level == SOL_UDPLITE) + if (level == SOL_UDP || level == SOL_UDPLITE || level == SOL_SOCKET) return udp_lib_setsockopt(sk, level, optname, optval, optlen, udp_v6_push_pending_frames); From 0cafd77dcd032d1687efaba5598cf07bce85997f Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Thu, 20 Oct 2022 23:20:18 +0000 Subject: [PATCH 0289/2002] net: add a refcount tracker for kernel sockets Commit ffa84b5ffb37 ("net: add netns refcount tracker to struct sock") added a tracker to sockets, but did not track kernel sockets. We still have syzbot reports hinting about netns being destroyed while some kernel TCP sockets had not been dismantled. This patch tracks kernel sockets, and adds a ref_tracker_dir_print() call to net_free() right before the netns is freed. Normally, each layer is responsible for properly releasing its kernel sockets before last call to net_free(). This debugging facility is enabled with CONFIG_NET_NS_REFCNT_TRACKER=y Signed-off-by: Eric Dumazet Reviewed-by: Kuniyuki Iwashima Tested-by: Kuniyuki Iwashima Signed-off-by: David S. Miller --- include/net/net_namespace.h | 30 ++++++++++++++++++++++-------- net/core/net_namespace.c | 5 +++++ net/core/sock.c | 14 ++++++++++++++ net/netlink/af_netlink.c | 11 +++++++++++ net/rds/tcp.c | 3 +++ 5 files changed, 55 insertions(+), 8 deletions(-) diff --git a/include/net/net_namespace.h b/include/net/net_namespace.h index 8c3587d5c308..78beaa765c73 100644 --- a/include/net/net_namespace.h +++ b/include/net/net_namespace.h @@ -92,7 +92,9 @@ struct net { struct ns_common ns; struct ref_tracker_dir refcnt_tracker; - + struct ref_tracker_dir notrefcnt_tracker; /* tracker for objects not + * refcounted against netns + */ struct list_head dev_base_head; struct proc_dir_entry *proc_net; struct proc_dir_entry *proc_net_stat; @@ -320,19 +322,31 @@ static inline int check_net(const struct net *net) #endif -static inline void netns_tracker_alloc(struct net *net, - netns_tracker *tracker, gfp_t gfp) +static inline void __netns_tracker_alloc(struct net *net, + netns_tracker *tracker, + bool refcounted, + gfp_t gfp) { #ifdef CONFIG_NET_NS_REFCNT_TRACKER - ref_tracker_alloc(&net->refcnt_tracker, tracker, gfp); + ref_tracker_alloc(refcounted ? &net->refcnt_tracker : + &net->notrefcnt_tracker, + tracker, gfp); #endif } -static inline void netns_tracker_free(struct net *net, - netns_tracker *tracker) +static inline void netns_tracker_alloc(struct net *net, netns_tracker *tracker, + gfp_t gfp) +{ + __netns_tracker_alloc(net, tracker, true, gfp); +} + +static inline void __netns_tracker_free(struct net *net, + netns_tracker *tracker, + bool refcounted) { #ifdef CONFIG_NET_NS_REFCNT_TRACKER - ref_tracker_free(&net->refcnt_tracker, tracker); + ref_tracker_free(refcounted ? &net->refcnt_tracker : + &net->notrefcnt_tracker, tracker); #endif } @@ -346,7 +360,7 @@ static inline struct net *get_net_track(struct net *net, static inline void put_net_track(struct net *net, netns_tracker *tracker) { - netns_tracker_free(net, tracker); + __netns_tracker_free(net, tracker, true); put_net(net); } diff --git a/net/core/net_namespace.c b/net/core/net_namespace.c index 0ec2f5906a27..12c68edf7682 100644 --- a/net/core/net_namespace.c +++ b/net/core/net_namespace.c @@ -309,6 +309,7 @@ static __net_init int setup_net(struct net *net, struct user_namespace *user_ns) refcount_set(&net->ns.count, 1); ref_tracker_dir_init(&net->refcnt_tracker, 128); + ref_tracker_dir_init(&net->notrefcnt_tracker, 128); refcount_set(&net->passive, 1); get_random_bytes(&net->hash_mix, sizeof(u32)); @@ -429,6 +430,10 @@ static void net_free(struct net *net) { if (refcount_dec_and_test(&net->passive)) { kfree(rcu_access_pointer(net->gen)); + + /* There should not be any trackers left there. */ + ref_tracker_dir_exit(&net->notrefcnt_tracker); + kmem_cache_free(net_cachep, net); } } diff --git a/net/core/sock.c b/net/core/sock.c index a3ba0358c77c..aa608dc0930b 100644 --- a/net/core/sock.c +++ b/net/core/sock.c @@ -2094,6 +2094,9 @@ struct sock *sk_alloc(struct net *net, int family, gfp_t priority, if (likely(sk->sk_net_refcnt)) { get_net_track(net, &sk->ns_tracker, priority); sock_inuse_add(net, 1); + } else { + __netns_tracker_alloc(net, &sk->ns_tracker, + false, priority); } sock_net_set(sk, net); @@ -2149,6 +2152,9 @@ static void __sk_destruct(struct rcu_head *head) if (likely(sk->sk_net_refcnt)) put_net_track(sock_net(sk), &sk->ns_tracker); + else + __netns_tracker_free(sock_net(sk), &sk->ns_tracker, false); + sk_prot_free(sk->sk_prot_creator, sk); } @@ -2237,6 +2243,14 @@ struct sock *sk_clone_lock(const struct sock *sk, const gfp_t priority) if (likely(newsk->sk_net_refcnt)) { get_net_track(sock_net(newsk), &newsk->ns_tracker, priority); sock_inuse_add(sock_net(newsk), 1); + } else { + /* Kernel sockets are not elevating the struct net refcount. + * Instead, use a tracker to more easily detect if a layer + * is not properly dismantling its kernel sockets at netns + * destroy time. + */ + __netns_tracker_alloc(sock_net(newsk), &newsk->ns_tracker, + false, priority); } sk_node_init(&newsk->sk_node); sock_lock_init(newsk); diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c index a662e8a5ff84..f0c94d394ab1 100644 --- a/net/netlink/af_netlink.c +++ b/net/netlink/af_netlink.c @@ -812,6 +812,17 @@ static int netlink_release(struct socket *sock) } sock_prot_inuse_add(sock_net(sk), &netlink_proto, -1); + + /* Because struct net might disappear soon, do not keep a pointer. */ + if (!sk->sk_net_refcnt && sock_net(sk) != &init_net) { + __netns_tracker_free(sock_net(sk), &sk->ns_tracker, false); + /* Because of deferred_put_nlk_sk and use of work queue, + * it is possible netns will be freed before this socket. + */ + sock_net_set(sk, &init_net); + __netns_tracker_alloc(&init_net, &sk->ns_tracker, + false, GFP_KERNEL); + } call_rcu(&nlk->rcu, deferred_put_nlk_sk); return 0; } diff --git a/net/rds/tcp.c b/net/rds/tcp.c index 4444fd82b66d..c5b86066ff66 100644 --- a/net/rds/tcp.c +++ b/net/rds/tcp.c @@ -503,6 +503,9 @@ bool rds_tcp_tune(struct socket *sock) release_sock(sk); return false; } + /* Update ns_tracker to current stack trace and refcounted tracker */ + __netns_tracker_free(net, &sk->ns_tracker, false); + sk->sk_net_refcnt = 1; netns_tracker_alloc(net, &sk->ns_tracker, GFP_KERNEL); sock_inuse_add(net, 1); From 350749b909bfbc27736334e79910d36ca58dad50 Mon Sep 17 00:00:00 2001 From: Wei Fang Date: Thu, 20 Oct 2022 12:35:56 +0800 Subject: [PATCH 0290/2002] net: fec: Add support for periodic output signal of PPS This patch adds the support for configuring periodic output signal of PPS. So the PPS can be output at a specified time and period. For developers or testers, they can use the command "echo > /sys/class/ptp/ptp0/period" to specify time and period to output PPS signal. Notice that, the channel can only be set to 0. In addtion, the start time must larger than the current PTP clock time. So users can use the command "phc_ctl /dev/ptp0 -- get" to get the current PTP clock time before. Signed-off-by: Wei Fang Acked-by: Richard Cochran Signed-off-by: David S. Miller --- drivers/net/ethernet/freescale/fec.h | 2 + drivers/net/ethernet/freescale/fec_ptp.c | 164 ++++++++++++++++++++++- 2 files changed, 164 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/freescale/fec.h b/drivers/net/ethernet/freescale/fec.h index 33f84a30e167..476e3863a310 100644 --- a/drivers/net/ethernet/freescale/fec.h +++ b/drivers/net/ethernet/freescale/fec.h @@ -658,6 +658,8 @@ struct fec_enet_private { unsigned int reload_period; int pps_enable; unsigned int next_counter; + struct hrtimer perout_timer; + u64 perout_stime; struct imx_sc_ipc *ipc_handle; diff --git a/drivers/net/ethernet/freescale/fec_ptp.c b/drivers/net/ethernet/freescale/fec_ptp.c index cffd9ad499dd..67aa694a62ec 100644 --- a/drivers/net/ethernet/freescale/fec_ptp.c +++ b/drivers/net/ethernet/freescale/fec_ptp.c @@ -88,6 +88,9 @@ #define FEC_CHANNLE_0 0 #define DEFAULT_PPS_CHANNEL FEC_CHANNLE_0 +#define FEC_PTP_MAX_NSEC_PERIOD 4000000000ULL +#define FEC_PTP_MAX_NSEC_COUNTER 0x80000000ULL + /** * fec_ptp_enable_pps * @fep: the fec_enet_private structure handle @@ -198,6 +201,78 @@ static int fec_ptp_enable_pps(struct fec_enet_private *fep, uint enable) return 0; } +static int fec_ptp_pps_perout(struct fec_enet_private *fep) +{ + u32 compare_val, ptp_hc, temp_val; + u64 curr_time; + unsigned long flags; + + spin_lock_irqsave(&fep->tmreg_lock, flags); + + /* Update time counter */ + timecounter_read(&fep->tc); + + /* Get the current ptp hardware time counter */ + temp_val = readl(fep->hwp + FEC_ATIME_CTRL); + temp_val |= FEC_T_CTRL_CAPTURE; + writel(temp_val, fep->hwp + FEC_ATIME_CTRL); + if (fep->quirks & FEC_QUIRK_BUG_CAPTURE) + udelay(1); + + ptp_hc = readl(fep->hwp + FEC_ATIME); + + /* Convert the ptp local counter to 1588 timestamp */ + curr_time = timecounter_cyc2time(&fep->tc, ptp_hc); + + /* If the pps start time less than current time add 100ms, just return. + * Because the software might not able to set the comparison time into + * the FEC_TCCR register in time and missed the start time. + */ + if (fep->perout_stime < curr_time + 100 * NSEC_PER_MSEC) { + dev_err(&fep->pdev->dev, "Current time is too close to the start time!\n"); + spin_unlock_irqrestore(&fep->tmreg_lock, flags); + return -1; + } + + compare_val = fep->perout_stime - curr_time + ptp_hc; + compare_val &= fep->cc.mask; + + writel(compare_val, fep->hwp + FEC_TCCR(fep->pps_channel)); + fep->next_counter = (compare_val + fep->reload_period) & fep->cc.mask; + + /* Enable compare event when overflow */ + temp_val = readl(fep->hwp + FEC_ATIME_CTRL); + temp_val |= FEC_T_CTRL_PINPER; + writel(temp_val, fep->hwp + FEC_ATIME_CTRL); + + /* Compare channel setting. */ + temp_val = readl(fep->hwp + FEC_TCSR(fep->pps_channel)); + temp_val |= (1 << FEC_T_TF_OFFSET | 1 << FEC_T_TIE_OFFSET); + temp_val &= ~(1 << FEC_T_TDRE_OFFSET); + temp_val &= ~(FEC_T_TMODE_MASK); + temp_val |= (FEC_TMODE_TOGGLE << FEC_T_TMODE_OFFSET); + writel(temp_val, fep->hwp + FEC_TCSR(fep->pps_channel)); + + /* Write the second compare event timestamp and calculate + * the third timestamp. Refer the TCCR register detail in the spec. + */ + writel(fep->next_counter, fep->hwp + FEC_TCCR(fep->pps_channel)); + fep->next_counter = (fep->next_counter + fep->reload_period) & fep->cc.mask; + spin_unlock_irqrestore(&fep->tmreg_lock, flags); + + return 0; +} + +static enum hrtimer_restart fec_ptp_pps_perout_handler(struct hrtimer *timer) +{ + struct fec_enet_private *fep = container_of(timer, + struct fec_enet_private, perout_timer); + + fec_ptp_pps_perout(fep); + + return HRTIMER_NORESTART; +} + /** * fec_ptp_read - read raw cycle counter (to be used by time counter) * @cc: the cyclecounter structure @@ -425,6 +500,17 @@ static int fec_ptp_settime(struct ptp_clock_info *ptp, return 0; } +static int fec_ptp_pps_disable(struct fec_enet_private *fep, uint channel) +{ + unsigned long flags; + + spin_lock_irqsave(&fep->tmreg_lock, flags); + writel(0, fep->hwp + FEC_TCSR(channel)); + spin_unlock_irqrestore(&fep->tmreg_lock, flags); + + return 0; +} + /** * fec_ptp_enable * @ptp: the ptp clock structure @@ -437,14 +523,84 @@ static int fec_ptp_enable(struct ptp_clock_info *ptp, { struct fec_enet_private *fep = container_of(ptp, struct fec_enet_private, ptp_caps); + ktime_t timeout; + struct timespec64 start_time, period; + u64 curr_time, delta, period_ns; + unsigned long flags; int ret = 0; if (rq->type == PTP_CLK_REQ_PPS) { ret = fec_ptp_enable_pps(fep, on); return ret; + } else if (rq->type == PTP_CLK_REQ_PEROUT) { + /* Reject requests with unsupported flags */ + if (rq->perout.flags) + return -EOPNOTSUPP; + + if (rq->perout.index != DEFAULT_PPS_CHANNEL) + return -EOPNOTSUPP; + + fep->pps_channel = DEFAULT_PPS_CHANNEL; + period.tv_sec = rq->perout.period.sec; + period.tv_nsec = rq->perout.period.nsec; + period_ns = timespec64_to_ns(&period); + + /* FEC PTP timer only has 31 bits, so if the period exceed + * 4s is not supported. + */ + if (period_ns > FEC_PTP_MAX_NSEC_PERIOD) { + dev_err(&fep->pdev->dev, "The period must equal to or less than 4s!\n"); + return -EOPNOTSUPP; + } + + fep->reload_period = div_u64(period_ns, 2); + if (on && fep->reload_period) { + /* Convert 1588 timestamp to ns*/ + start_time.tv_sec = rq->perout.start.sec; + start_time.tv_nsec = rq->perout.start.nsec; + fep->perout_stime = timespec64_to_ns(&start_time); + + mutex_lock(&fep->ptp_clk_mutex); + if (!fep->ptp_clk_on) { + dev_err(&fep->pdev->dev, "Error: PTP clock is closed!\n"); + mutex_unlock(&fep->ptp_clk_mutex); + return -EOPNOTSUPP; + } + spin_lock_irqsave(&fep->tmreg_lock, flags); + /* Read current timestamp */ + curr_time = timecounter_read(&fep->tc); + spin_unlock_irqrestore(&fep->tmreg_lock, flags); + mutex_unlock(&fep->ptp_clk_mutex); + + /* Calculate time difference */ + delta = fep->perout_stime - curr_time; + + if (fep->perout_stime <= curr_time) { + dev_err(&fep->pdev->dev, "Start time must larger than current time!\n"); + return -EINVAL; + } + + /* Because the timer counter of FEC only has 31-bits, correspondingly, + * the time comparison register FEC_TCCR also only low 31 bits can be + * set. If the start time of pps signal exceeds current time more than + * 0x80000000 ns, a software timer is used and the timer expires about + * 1 second before the start time to be able to set FEC_TCCR. + */ + if (delta > FEC_PTP_MAX_NSEC_COUNTER) { + timeout = ns_to_ktime(delta - NSEC_PER_SEC); + hrtimer_start(&fep->perout_timer, timeout, HRTIMER_MODE_REL); + } else { + return fec_ptp_pps_perout(fep); + } + } else { + fec_ptp_pps_disable(fep, fep->pps_channel); + } + + return 0; + } else { + return -EOPNOTSUPP; } - return -EOPNOTSUPP; } /** @@ -583,7 +739,7 @@ void fec_ptp_init(struct platform_device *pdev, int irq_idx) fep->ptp_caps.max_adj = 250000000; fep->ptp_caps.n_alarm = 0; fep->ptp_caps.n_ext_ts = 0; - fep->ptp_caps.n_per_out = 0; + fep->ptp_caps.n_per_out = 1; fep->ptp_caps.n_pins = 0; fep->ptp_caps.pps = 1; fep->ptp_caps.adjfreq = fec_ptp_adjfreq; @@ -605,6 +761,9 @@ void fec_ptp_init(struct platform_device *pdev, int irq_idx) INIT_DELAYED_WORK(&fep->time_keep, fec_time_keep); + hrtimer_init(&fep->perout_timer, CLOCK_REALTIME, HRTIMER_MODE_REL); + fep->perout_timer.function = fec_ptp_pps_perout_handler; + irq = platform_get_irq_byname_optional(pdev, "pps"); if (irq < 0) irq = platform_get_irq_optional(pdev, irq_idx); @@ -634,6 +793,7 @@ void fec_ptp_stop(struct platform_device *pdev) struct fec_enet_private *fep = netdev_priv(ndev); cancel_delayed_work_sync(&fep->time_keep); + hrtimer_cancel(&fep->perout_timer); if (fep->ptp_clock) ptp_clock_unregister(fep->ptp_clock); } From 233baf9a1bc46f18ad3bec688f52ea5f818a8a25 Mon Sep 17 00:00:00 2001 From: xu xin Date: Thu, 20 Oct 2022 06:54:41 +0000 Subject: [PATCH 0291/2002] net: remove useless parameter of __sock_cmsg_send The parameter 'msg' has never been used by __sock_cmsg_send, so we can remove it safely. Reported-by: Zeal Robot Signed-off-by: xu xin Reviewed-by: Zhang Yunkai Acked-by: Kuniyuki Iwashima Signed-off-by: David S. Miller --- include/net/sock.h | 2 +- net/core/sock.c | 4 ++-- net/ipv4/ip_sockglue.c | 2 +- net/ipv6/datagram.c | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/include/net/sock.h b/include/net/sock.h index 9e464f6409a7..b1dacc4d68c9 100644 --- a/include/net/sock.h +++ b/include/net/sock.h @@ -1901,7 +1901,7 @@ static inline void sockcm_init(struct sockcm_cookie *sockc, *sockc = (struct sockcm_cookie) { .tsflags = sk->sk_tsflags }; } -int __sock_cmsg_send(struct sock *sk, struct msghdr *msg, struct cmsghdr *cmsg, +int __sock_cmsg_send(struct sock *sk, struct cmsghdr *cmsg, struct sockcm_cookie *sockc); int sock_cmsg_send(struct sock *sk, struct msghdr *msg, struct sockcm_cookie *sockc); diff --git a/net/core/sock.c b/net/core/sock.c index aa608dc0930b..2786c1107e53 100644 --- a/net/core/sock.c +++ b/net/core/sock.c @@ -2744,7 +2744,7 @@ failure: } EXPORT_SYMBOL(sock_alloc_send_pskb); -int __sock_cmsg_send(struct sock *sk, struct msghdr *msg, struct cmsghdr *cmsg, +int __sock_cmsg_send(struct sock *sk, struct cmsghdr *cmsg, struct sockcm_cookie *sockc) { u32 tsflags; @@ -2798,7 +2798,7 @@ int sock_cmsg_send(struct sock *sk, struct msghdr *msg, return -EINVAL; if (cmsg->cmsg_level != SOL_SOCKET) continue; - ret = __sock_cmsg_send(sk, msg, cmsg, sockc); + ret = __sock_cmsg_send(sk, cmsg, sockc); if (ret) return ret; } diff --git a/net/ipv4/ip_sockglue.c b/net/ipv4/ip_sockglue.c index 6e19cad154f5..5f16807d3235 100644 --- a/net/ipv4/ip_sockglue.c +++ b/net/ipv4/ip_sockglue.c @@ -267,7 +267,7 @@ int ip_cmsg_send(struct sock *sk, struct msghdr *msg, struct ipcm_cookie *ipc, } #endif if (cmsg->cmsg_level == SOL_SOCKET) { - err = __sock_cmsg_send(sk, msg, cmsg, &ipc->sockc); + err = __sock_cmsg_send(sk, cmsg, &ipc->sockc); if (err) return err; continue; diff --git a/net/ipv6/datagram.c b/net/ipv6/datagram.c index 5ecb56522f9d..df7e032ce87d 100644 --- a/net/ipv6/datagram.c +++ b/net/ipv6/datagram.c @@ -771,7 +771,7 @@ int ip6_datagram_send_ctl(struct net *net, struct sock *sk, } if (cmsg->cmsg_level == SOL_SOCKET) { - err = __sock_cmsg_send(sk, msg, cmsg, &ipc6->sockc); + err = __sock_cmsg_send(sk, cmsg, &ipc6->sockc); if (err) return err; continue; From 127b7218bfdd60205cfe2fa3f06d95e85a2650ed Mon Sep 17 00:00:00 2001 From: Nick Child Date: Thu, 20 Oct 2022 16:40:52 -0500 Subject: [PATCH 0292/2002] ibmveth: Always stop tx queues during close netif_stop_all_queues must be called before calling H_FREE_LOGICAL_LAN. As a result, we can remove the pool_config field from the ibmveth adapter structure. Some device configuration changes call ibmveth_close in order to free the current resources held by the device. These functions then make their changes and call ibmveth_open to reallocate and reserve resources for the device. Prior to this commit, the flag pool_config was used to tell ibmveth_close that it should not halt the transmit queue. pool_config was introduced in commit 860f242eb534 ("[PATCH] ibmveth change buffer pools dynamically") to avoid interrupting the tx flow when making rx config changes. Since then, other commits adopted this approach, even if making tx config changes. The issue with this approach was that the hypervisor freed all of the devices control structures after the hcall H_FREE_LOGICAL_LAN was performed but the transmit queues were never stopped. So the higher layers in the network stack would continue transmission but any H_SEND_LOGICAL_LAN hcall would fail with H_PARAMETER until the hypervisor's structures for the device were allocated with the H_REGISTER_LOGICAL_LAN hcall in ibmveth_open. This resulted in no real networking harm but did cause several of these error messages to be logged: "h_send_logical_lan failed with rc=-4" So, instead of trying to keep the transmit queues alive during network configuration changes, just stop the queues, make necessary changes then restart the queues. Signed-off-by: Nick Child Signed-off-by: David S. Miller --- drivers/net/ethernet/ibm/ibmveth.c | 18 +----------------- drivers/net/ethernet/ibm/ibmveth.h | 1 - 2 files changed, 1 insertion(+), 18 deletions(-) diff --git a/drivers/net/ethernet/ibm/ibmveth.c b/drivers/net/ethernet/ibm/ibmveth.c index 3b14dc93f59d..7d79006250ae 100644 --- a/drivers/net/ethernet/ibm/ibmveth.c +++ b/drivers/net/ethernet/ibm/ibmveth.c @@ -690,8 +690,7 @@ static int ibmveth_close(struct net_device *netdev) napi_disable(&adapter->napi); - if (!adapter->pool_config) - netif_tx_stop_all_queues(netdev); + netif_tx_stop_all_queues(netdev); h_vio_signal(adapter->vdev->unit_address, VIO_IRQ_DISABLE); @@ -799,9 +798,7 @@ static int ibmveth_set_csum_offload(struct net_device *dev, u32 data) if (netif_running(dev)) { restart = 1; - adapter->pool_config = 1; ibmveth_close(dev); - adapter->pool_config = 0; } set_attr = 0; @@ -883,9 +880,7 @@ static int ibmveth_set_tso(struct net_device *dev, u32 data) if (netif_running(dev)) { restart = 1; - adapter->pool_config = 1; ibmveth_close(dev); - adapter->pool_config = 0; } set_attr = 0; @@ -1535,9 +1530,7 @@ static int ibmveth_change_mtu(struct net_device *dev, int new_mtu) only the buffer pools necessary to hold the new MTU */ if (netif_running(adapter->netdev)) { need_restart = 1; - adapter->pool_config = 1; ibmveth_close(adapter->netdev); - adapter->pool_config = 0; } /* Look for an active buffer pool that can hold the new MTU */ @@ -1701,7 +1694,6 @@ static int ibmveth_probe(struct vio_dev *dev, const struct vio_device_id *id) adapter->vdev = dev; adapter->netdev = netdev; adapter->mcastFilterSize = be32_to_cpu(*mcastFilterSize_p); - adapter->pool_config = 0; ibmveth_init_link_settings(netdev); netif_napi_add_weight(netdev, &adapter->napi, ibmveth_poll, 16); @@ -1841,9 +1833,7 @@ static ssize_t veth_pool_store(struct kobject *kobj, struct attribute *attr, return -ENOMEM; } pool->active = 1; - adapter->pool_config = 1; ibmveth_close(netdev); - adapter->pool_config = 0; if ((rc = ibmveth_open(netdev))) return rc; } else { @@ -1869,10 +1859,8 @@ static ssize_t veth_pool_store(struct kobject *kobj, struct attribute *attr, } if (netif_running(netdev)) { - adapter->pool_config = 1; ibmveth_close(netdev); pool->active = 0; - adapter->pool_config = 0; if ((rc = ibmveth_open(netdev))) return rc; } @@ -1883,9 +1871,7 @@ static ssize_t veth_pool_store(struct kobject *kobj, struct attribute *attr, return -EINVAL; } else { if (netif_running(netdev)) { - adapter->pool_config = 1; ibmveth_close(netdev); - adapter->pool_config = 0; pool->size = value; if ((rc = ibmveth_open(netdev))) return rc; @@ -1898,9 +1884,7 @@ static ssize_t veth_pool_store(struct kobject *kobj, struct attribute *attr, return -EINVAL; } else { if (netif_running(netdev)) { - adapter->pool_config = 1; ibmveth_close(netdev); - adapter->pool_config = 0; pool->buff_size = value; if ((rc = ibmveth_open(netdev))) return rc; diff --git a/drivers/net/ethernet/ibm/ibmveth.h b/drivers/net/ethernet/ibm/ibmveth.h index daf6f615c03f..4f8357187292 100644 --- a/drivers/net/ethernet/ibm/ibmveth.h +++ b/drivers/net/ethernet/ibm/ibmveth.h @@ -146,7 +146,6 @@ struct ibmveth_adapter { dma_addr_t filter_list_dma; struct ibmveth_buff_pool rx_buff_pool[IBMVETH_NUM_BUFF_POOLS]; struct ibmveth_rx_q rx_queue; - int pool_config; int rx_csum; int large_send; bool is_active_trunk; From 4727bab4e9bbeafeff6acdfcb077a7a548cbde30 Mon Sep 17 00:00:00 2001 From: Yunsheng Lin Date: Fri, 21 Oct 2022 10:58:22 +0800 Subject: [PATCH 0293/2002] net: skb: move skb_pp_recycle() to skbuff.c skb_pp_recycle() is only used by skb_free_head() in skbuff.c, so move it to skbuff.c. Signed-off-by: Yunsheng Lin Acked-by: Ilias Apalodimas Signed-off-by: David S. Miller --- include/linux/skbuff.h | 7 ------- net/core/skbuff.c | 7 +++++++ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index 7be5bb4c94b6..59c9fd55699d 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -5050,12 +5050,5 @@ static inline void skb_mark_for_recycle(struct sk_buff *skb) } #endif -static inline bool skb_pp_recycle(struct sk_buff *skb, void *data) -{ - if (!IS_ENABLED(CONFIG_PAGE_POOL) || !skb->pp_recycle) - return false; - return page_pool_return_skb_page(virt_to_page(data)); -} - #endif /* __KERNEL__ */ #endif /* _LINUX_SKBUFF_H */ diff --git a/net/core/skbuff.c b/net/core/skbuff.c index 1d9719e72f9d..9b3b19816d2d 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -748,6 +748,13 @@ static void skb_clone_fraglist(struct sk_buff *skb) skb_get(list); } +static bool skb_pp_recycle(struct sk_buff *skb, void *data) +{ + if (!IS_ENABLED(CONFIG_PAGE_POOL) || !skb->pp_recycle) + return false; + return page_pool_return_skb_page(virt_to_page(data)); +} + static void skb_free_head(struct sk_buff *skb) { unsigned char *head = skb->head; From 895ac5a51fe280e9898e212da7803ec9babf137f Mon Sep 17 00:00:00 2001 From: Vadim Fedorenko Date: Fri, 21 Oct 2022 02:24:29 +0300 Subject: [PATCH 0294/2002] ptp: ocp: upgrade serial line information Introduce structure to hold serial port line number and the baud rate it supports. Acked-by: Jonathan Lemon Signed-off-by: Jonathan Lemon Signed-off-by: Vadim Fedorenko Signed-off-by: David S. Miller --- drivers/ptp/ptp_ocp.c | 110 +++++++++++++++++++++++++----------------- 1 file changed, 67 insertions(+), 43 deletions(-) diff --git a/drivers/ptp/ptp_ocp.c b/drivers/ptp/ptp_ocp.c index a48d9b7d2921..1ce0f2989a85 100644 --- a/drivers/ptp/ptp_ocp.c +++ b/drivers/ptp/ptp_ocp.c @@ -278,6 +278,11 @@ struct ptp_ocp_signal { bool running; }; +struct ptp_ocp_serial_port { + int line; + int baud; +}; + #define OCP_BOARD_ID_LEN 13 #define OCP_SERIAL_LEN 6 @@ -318,10 +323,10 @@ struct ptp_ocp { time64_t gnss_lost; int id; int n_irqs; - int gnss_port; - int gnss2_port; - int mac_port; /* miniature atomic clock */ - int nmea_port; + struct ptp_ocp_serial_port gnss_port; + struct ptp_ocp_serial_port gnss2_port; + struct ptp_ocp_serial_port mac_port; /* miniature atomic clock */ + struct ptp_ocp_serial_port nmea_port; bool fw_loader; u8 fw_tag; u16 fw_version; @@ -596,14 +601,23 @@ static struct ocp_resource ocp_fb_resource[] = { { OCP_SERIAL_RESOURCE(gnss_port), .offset = 0x00160000 + 0x1000, .irq_vec = 3, + .extra = &(struct ptp_ocp_serial_port) { + .baud = 115200, + }, }, { OCP_SERIAL_RESOURCE(gnss2_port), .offset = 0x00170000 + 0x1000, .irq_vec = 4, + .extra = &(struct ptp_ocp_serial_port) { + .baud = 115200, + }, }, { OCP_SERIAL_RESOURCE(mac_port), .offset = 0x00180000 + 0x1000, .irq_vec = 5, + .extra = &(struct ptp_ocp_serial_port) { + .baud = 57600, + }, }, { OCP_SERIAL_RESOURCE(nmea_port), @@ -1872,11 +1886,15 @@ ptp_ocp_serial_line(struct ptp_ocp *bp, struct ocp_resource *r) static int ptp_ocp_register_serial(struct ptp_ocp *bp, struct ocp_resource *r) { - int port; + struct ptp_ocp_serial_port *p = (struct ptp_ocp_serial_port *)r->extra; + struct ptp_ocp_serial_port port = {}; - port = ptp_ocp_serial_line(bp, r); - if (port < 0) - return port; + port.line = ptp_ocp_serial_line(bp, r); + if (port.line < 0) + return port.line; + + if (p) + port.baud = p->baud; bp_assign_entry(bp, r, port); @@ -3177,14 +3195,16 @@ ptp_ocp_summary_show(struct seq_file *s, void *data) bp = dev_get_drvdata(dev); seq_printf(s, "%7s: /dev/ptp%d\n", "PTP", ptp_clock_index(bp->ptp)); - if (bp->gnss_port != -1) - seq_printf(s, "%7s: /dev/ttyS%d\n", "GNSS1", bp->gnss_port); - if (bp->gnss2_port != -1) - seq_printf(s, "%7s: /dev/ttyS%d\n", "GNSS2", bp->gnss2_port); - if (bp->mac_port != -1) - seq_printf(s, "%7s: /dev/ttyS%d\n", "MAC", bp->mac_port); - if (bp->nmea_port != -1) - seq_printf(s, "%7s: /dev/ttyS%d\n", "NMEA", bp->nmea_port); + if (bp->gnss_port.line != -1) + seq_printf(s, "%7s: /dev/ttyS%d\n", "GNSS1", + bp->gnss_port.line); + if (bp->gnss2_port.line != -1) + seq_printf(s, "%7s: /dev/ttyS%d\n", "GNSS2", + bp->gnss2_port.line); + if (bp->mac_port.line != -1) + seq_printf(s, "%7s: /dev/ttyS%d\n", "MAC", bp->mac_port.line); + if (bp->nmea_port.line != -1) + seq_printf(s, "%7s: /dev/ttyS%d\n", "NMEA", bp->nmea_port.line); memset(sma_val, 0xff, sizeof(sma_val)); if (bp->sma_map1) { @@ -3508,10 +3528,10 @@ ptp_ocp_device_init(struct ptp_ocp *bp, struct pci_dev *pdev) bp->ptp_info = ptp_ocp_clock_info; spin_lock_init(&bp->lock); - bp->gnss_port = -1; - bp->gnss2_port = -1; - bp->mac_port = -1; - bp->nmea_port = -1; + bp->gnss_port.line = -1; + bp->gnss2_port.line = -1; + bp->mac_port.line = -1; + bp->nmea_port.line = -1; bp->pdev = pdev; device_initialize(&bp->dev); @@ -3569,20 +3589,20 @@ ptp_ocp_complete(struct ptp_ocp *bp) struct pps_device *pps; char buf[32]; - if (bp->gnss_port != -1) { - sprintf(buf, "ttyS%d", bp->gnss_port); + if (bp->gnss_port.line != -1) { + sprintf(buf, "ttyS%d", bp->gnss_port.line); ptp_ocp_link_child(bp, buf, "ttyGNSS"); } - if (bp->gnss2_port != -1) { - sprintf(buf, "ttyS%d", bp->gnss2_port); + if (bp->gnss2_port.line != -1) { + sprintf(buf, "ttyS%d", bp->gnss2_port.line); ptp_ocp_link_child(bp, buf, "ttyGNSS2"); } - if (bp->mac_port != -1) { - sprintf(buf, "ttyS%d", bp->mac_port); + if (bp->mac_port.line != -1) { + sprintf(buf, "ttyS%d", bp->mac_port.line); ptp_ocp_link_child(bp, buf, "ttyMAC"); } - if (bp->nmea_port != -1) { - sprintf(buf, "ttyS%d", bp->nmea_port); + if (bp->nmea_port.line != -1) { + sprintf(buf, "ttyS%d", bp->nmea_port.line); ptp_ocp_link_child(bp, buf, "ttyNMEA"); } sprintf(buf, "ptp%d", ptp_clock_index(bp->ptp)); @@ -3638,16 +3658,20 @@ ptp_ocp_info(struct ptp_ocp *bp) ptp_ocp_phc_info(bp); - ptp_ocp_serial_info(dev, "GNSS", bp->gnss_port, 115200); - ptp_ocp_serial_info(dev, "GNSS2", bp->gnss2_port, 115200); - ptp_ocp_serial_info(dev, "MAC", bp->mac_port, 57600); - if (bp->nmea_out && bp->nmea_port != -1) { - int baud = -1; + ptp_ocp_serial_info(dev, "GNSS", bp->gnss_port.line, + bp->gnss_port.baud); + ptp_ocp_serial_info(dev, "GNSS2", bp->gnss2_port.line, + bp->gnss2_port.baud); + ptp_ocp_serial_info(dev, "MAC", bp->mac_port.line, bp->mac_port.baud); + if (bp->nmea_out && bp->nmea_port.line != -1) { + bp->nmea_port.baud = -1; reg = ioread32(&bp->nmea_out->uart_baud); if (reg < ARRAY_SIZE(nmea_baud)) - baud = nmea_baud[reg]; - ptp_ocp_serial_info(dev, "NMEA", bp->nmea_port, baud); + bp->nmea_port.baud = nmea_baud[reg]; + + ptp_ocp_serial_info(dev, "NMEA", bp->nmea_port.line, + bp->nmea_port.baud); } } @@ -3688,14 +3712,14 @@ ptp_ocp_detach(struct ptp_ocp *bp) for (i = 0; i < 4; i++) if (bp->signal_out[i]) ptp_ocp_unregister_ext(bp->signal_out[i]); - if (bp->gnss_port != -1) - serial8250_unregister_port(bp->gnss_port); - if (bp->gnss2_port != -1) - serial8250_unregister_port(bp->gnss2_port); - if (bp->mac_port != -1) - serial8250_unregister_port(bp->mac_port); - if (bp->nmea_port != -1) - serial8250_unregister_port(bp->nmea_port); + if (bp->gnss_port.line != -1) + serial8250_unregister_port(bp->gnss_port.line); + if (bp->gnss2_port.line != -1) + serial8250_unregister_port(bp->gnss2_port.line); + if (bp->mac_port.line != -1) + serial8250_unregister_port(bp->mac_port.line); + if (bp->nmea_port.line != -1) + serial8250_unregister_port(bp->nmea_port.line); platform_device_unregister(bp->spi_flash); platform_device_unregister(bp->i2c_ctrl); if (bp->i2c_clk) From 69dbe1079cd015d0c96ec488289981ab5663b640 Mon Sep 17 00:00:00 2001 From: Vadim Fedorenko Date: Fri, 21 Oct 2022 02:24:30 +0300 Subject: [PATCH 0295/2002] ptp: ocp: add Orolia timecard support This brings in the Orolia timecard support from the GitHub repository. The card uses different drivers to provide access to i2c EEPROM and firmware SPI flash. And it also has a bit different EEPROM map, but other parts of the code are the same and could be reused. Co-developed-by: Charles Parent Acked-by: Jonathan Lemon Signed-off-by: Jonathan Lemon Signed-off-by: Vadim Fedorenko Signed-off-by: David S. Miller --- drivers/ptp/ptp_ocp.c | 290 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 290 insertions(+) diff --git a/drivers/ptp/ptp_ocp.c b/drivers/ptp/ptp_ocp.c index 1ce0f2989a85..8c1ce630b67f 100644 --- a/drivers/ptp/ptp_ocp.c +++ b/drivers/ptp/ptp_ocp.c @@ -13,9 +13,11 @@ #include #include #include +#include #include #include #include +#include #include #include #include @@ -28,6 +30,9 @@ #define PCI_VENDOR_ID_CELESTICA 0x18d4 #define PCI_DEVICE_ID_CELESTICA_TIMECARD 0x1008 +#define PCI_VENDOR_ID_OROLIA 0x1ad7 +#define PCI_DEVICE_ID_OROLIA_ARTCARD 0xa000 + static struct class timecard_class = { .owner = THIS_MODULE, .name = "timecard", @@ -310,6 +315,7 @@ struct ptp_ocp { struct ptp_ocp_ext_src *ts2; struct ptp_ocp_ext_src *ts3; struct ptp_ocp_ext_src *ts4; + struct ocp_art_gpio_reg __iomem *art_sma; struct img_reg __iomem *image; struct ptp_clock *ptp; struct ptp_clock_info ptp_info; @@ -370,8 +376,12 @@ static int ptp_ocp_signal_from_perout(struct ptp_ocp *bp, int gen, static int ptp_ocp_signal_enable(void *priv, u32 req, bool enable); static int ptp_ocp_sma_store(struct ptp_ocp *bp, const char *buf, int sma_nr); +static int ptp_ocp_art_board_init(struct ptp_ocp *bp, struct ocp_resource *r); + static const struct ocp_attr_group fb_timecard_groups[]; +static const struct ocp_attr_group art_timecard_groups[]; + struct ptp_ocp_eeprom_map { u16 off; u16 len; @@ -394,6 +404,12 @@ static struct ptp_ocp_eeprom_map fb_eeprom_map[] = { { } }; +static struct ptp_ocp_eeprom_map art_eeprom_map[] = { + { EEPROM_ENTRY(0x200 + 0x43, board_id) }, + { EEPROM_ENTRY(0x200 + 0x63, serial) }, + { } +}; + #define bp_assign_entry(bp, res, val) ({ \ uintptr_t addr = (uintptr_t)(bp) + (res)->bp_offset; \ *(typeof(val) *)addr = val; \ @@ -435,6 +451,13 @@ static struct ptp_ocp_eeprom_map fb_eeprom_map[] = { * 14: Signal Generator 4 * 15: TS3 * 16: TS4 + -- + * 8: Orolia TS1 + * 10: Orolia TS2 + * 11: Orolia TS0 (GNSS) + * 12: Orolia PPS + * 14: Orolia TS3 + * 15: Orolia TS4 */ static struct ocp_resource ocp_fb_resource[] = { @@ -661,9 +684,127 @@ static struct ocp_resource ocp_fb_resource[] = { { } }; +struct ocp_art_gpio_reg { + struct { + u32 gpio; + u32 __pad[3]; + } map[4]; +}; + +static struct ocp_resource ocp_art_resource[] = { + { + OCP_MEM_RESOURCE(reg), + .offset = 0x01000000, .size = 0x10000, + }, + { + OCP_SERIAL_RESOURCE(gnss_port), + .offset = 0x00160000 + 0x1000, .irq_vec = 3, + .extra = &(struct ptp_ocp_serial_port) { + .baud = 115200, + }, + }, + { + OCP_MEM_RESOURCE(art_sma), + .offset = 0x003C0000, .size = 0x1000, + }, + /* Timestamp associated with GNSS1 receiver PPS */ + { + OCP_EXT_RESOURCE(ts0), + .offset = 0x360000, .size = 0x20, .irq_vec = 12, + .extra = &(struct ptp_ocp_ext_info) { + .index = 0, + .irq_fcn = ptp_ocp_ts_irq, + .enable = ptp_ocp_ts_enable, + }, + }, + { + OCP_EXT_RESOURCE(ts1), + .offset = 0x380000, .size = 0x20, .irq_vec = 8, + .extra = &(struct ptp_ocp_ext_info) { + .index = 1, + .irq_fcn = ptp_ocp_ts_irq, + .enable = ptp_ocp_ts_enable, + }, + }, + { + OCP_EXT_RESOURCE(ts2), + .offset = 0x390000, .size = 0x20, .irq_vec = 10, + .extra = &(struct ptp_ocp_ext_info) { + .index = 2, + .irq_fcn = ptp_ocp_ts_irq, + .enable = ptp_ocp_ts_enable, + }, + }, + { + OCP_EXT_RESOURCE(ts3), + .offset = 0x3A0000, .size = 0x20, .irq_vec = 14, + .extra = &(struct ptp_ocp_ext_info) { + .index = 3, + .irq_fcn = ptp_ocp_ts_irq, + .enable = ptp_ocp_ts_enable, + }, + }, + { + OCP_EXT_RESOURCE(ts4), + .offset = 0x3B0000, .size = 0x20, .irq_vec = 15, + .extra = &(struct ptp_ocp_ext_info) { + .index = 4, + .irq_fcn = ptp_ocp_ts_irq, + .enable = ptp_ocp_ts_enable, + }, + }, + /* Timestamp associated with Internal PPS of the card */ + { + OCP_EXT_RESOURCE(pps), + .offset = 0x00330000, .size = 0x20, .irq_vec = 11, + .extra = &(struct ptp_ocp_ext_info) { + .index = 5, + .irq_fcn = ptp_ocp_ts_irq, + .enable = ptp_ocp_ts_enable, + }, + }, + { + OCP_SPI_RESOURCE(spi_flash), + .offset = 0x00310000, .size = 0x10000, .irq_vec = 9, + .extra = &(struct ptp_ocp_flash_info) { + .name = "spi_altera", .pci_offset = 0, + .data_size = sizeof(struct altera_spi_platform_data), + .data = &(struct altera_spi_platform_data) { + .num_chipselect = 1, + .num_devices = 1, + .devices = &(struct spi_board_info) { + .modalias = "spi-nor", + }, + }, + }, + }, + { + OCP_I2C_RESOURCE(i2c_ctrl), + .offset = 0x350000, .size = 0x100, .irq_vec = 4, + .extra = &(struct ptp_ocp_i2c_info) { + .name = "ocores-i2c", + .fixed_rate = 400000, + .data_size = sizeof(struct ocores_i2c_platform_data), + .data = &(struct ocores_i2c_platform_data) { + .clock_khz = 125000, + .bus_khz = 400, + .num_devices = 1, + .devices = &(struct i2c_board_info) { + I2C_BOARD_INFO("24c08", 0x50), + }, + }, + }, + }, + { + .setup = ptp_ocp_art_board_init, + }, + { } +}; + static const struct pci_device_id ptp_ocp_pcidev_id[] = { { PCI_DEVICE_DATA(FACEBOOK, TIMECARD, &ocp_fb_resource) }, { PCI_DEVICE_DATA(CELESTICA, TIMECARD, &ocp_fb_resource) }, + { PCI_DEVICE_DATA(OROLIA, ARTCARD, &ocp_art_resource) }, { } }; MODULE_DEVICE_TABLE(pci, ptp_ocp_pcidev_id); @@ -728,6 +869,19 @@ static const struct ocp_selector ptp_ocp_sma_out[] = { { } }; +static const struct ocp_selector ptp_ocp_art_sma_in[] = { + { .name = "PPS1", .value = 0x0001 }, + { .name = "10Mhz", .value = 0x0008 }, + { } +}; + +static const struct ocp_selector ptp_ocp_art_sma_out[] = { + { .name = "PHC", .value = 0x0002 }, + { .name = "GNSS", .value = 0x0004 }, + { .name = "10Mhz", .value = 0x0010 }, + { } +}; + struct ocp_sma_op { const struct ocp_selector *tbl[2]; void (*init)(struct ptp_ocp *bp); @@ -2275,6 +2429,118 @@ ptp_ocp_register_resources(struct ptp_ocp *bp, kernel_ulong_t driver_data) return err; } +static void +ptp_ocp_art_sma_init(struct ptp_ocp *bp) +{ + u32 reg; + int i; + + /* defaults */ + bp->sma[0].mode = SMA_MODE_IN; + bp->sma[1].mode = SMA_MODE_IN; + bp->sma[2].mode = SMA_MODE_OUT; + bp->sma[3].mode = SMA_MODE_OUT; + + bp->sma[0].default_fcn = 0x08; /* IN: 10Mhz */ + bp->sma[1].default_fcn = 0x01; /* IN: PPS1 */ + bp->sma[2].default_fcn = 0x10; /* OUT: 10Mhz */ + bp->sma[3].default_fcn = 0x02; /* OUT: PHC */ + + /* If no SMA map, the pin functions and directions are fixed. */ + if (!bp->art_sma) { + for (i = 0; i < 4; i++) { + bp->sma[i].fixed_fcn = true; + bp->sma[i].fixed_dir = true; + } + return; + } + + for (i = 0; i < 4; i++) { + reg = ioread32(&bp->art_sma->map[i].gpio); + + switch (reg & 0xff) { + case 0: + bp->sma[i].fixed_fcn = true; + bp->sma[i].fixed_dir = true; + break; + case 1: + case 8: + bp->sma[i].mode = SMA_MODE_IN; + break; + default: + bp->sma[i].mode = SMA_MODE_OUT; + break; + } + } +} + +static u32 +ptp_ocp_art_sma_get(struct ptp_ocp *bp, int sma_nr) +{ + if (bp->sma[sma_nr - 1].fixed_fcn) + return bp->sma[sma_nr - 1].default_fcn; + + return ioread32(&bp->art_sma->map[sma_nr - 1].gpio) & 0xff; +} + +/* note: store 0 is considered invalid. */ +static int +ptp_ocp_art_sma_set(struct ptp_ocp *bp, int sma_nr, u32 val) +{ + unsigned long flags; + u32 __iomem *gpio; + int err = 0; + u32 reg; + + val &= SMA_SELECT_MASK; + if (hweight32(val) > 1) + return -EINVAL; + + gpio = &bp->art_sma->map[sma_nr - 1].gpio; + + spin_lock_irqsave(&bp->lock, flags); + reg = ioread32(gpio); + if (((reg >> 16) & val) == 0) { + err = -EOPNOTSUPP; + } else { + reg = (reg & 0xff00) | (val & 0xff); + iowrite32(reg, gpio); + } + spin_unlock_irqrestore(&bp->lock, flags); + + return err; +} + +static const struct ocp_sma_op ocp_art_sma_op = { + .tbl = { ptp_ocp_art_sma_in, ptp_ocp_art_sma_out }, + .init = ptp_ocp_art_sma_init, + .get = ptp_ocp_art_sma_get, + .set_inputs = ptp_ocp_art_sma_set, + .set_output = ptp_ocp_art_sma_set, +}; + +/* ART specific board initializers; last "resource" registered. */ +static int +ptp_ocp_art_board_init(struct ptp_ocp *bp, struct ocp_resource *r) +{ + int err; + + bp->flash_start = 0x1000000; + bp->eeprom_map = art_eeprom_map; + bp->fw_cap = OCP_CAP_BASIC; + bp->fw_version = ioread32(&bp->reg->version); + bp->fw_tag = 2; + bp->sma_op = &ocp_art_sma_op; + + ptp_ocp_sma_init(bp); + + err = ptp_ocp_attr_group_add(bp, art_timecard_groups); + if (err) + return err; + + return ptp_ocp_init_clock(bp); +} + static ssize_t ptp_ocp_show_output(const struct ocp_selector *tbl, u32 val, char *buf, int def_val) @@ -3083,6 +3349,30 @@ static const struct ocp_attr_group fb_timecard_groups[] = { { }, }; +static struct attribute *art_timecard_attrs[] = { + &dev_attr_serialnum.attr, + &dev_attr_clock_source.attr, + &dev_attr_available_clock_sources.attr, + &dev_attr_utc_tai_offset.attr, + &dev_attr_ts_window_adjust.attr, + &dev_attr_sma1.attr, + &dev_attr_sma2.attr, + &dev_attr_sma3.attr, + &dev_attr_sma4.attr, + &dev_attr_available_sma_inputs.attr, + &dev_attr_available_sma_outputs.attr, + NULL, +}; + +static const struct attribute_group art_timecard_group = { + .attrs = art_timecard_attrs, +}; + +static const struct ocp_attr_group art_timecard_groups[] = { + { .cap = OCP_CAP_BASIC, .group = &art_timecard_group }, + { }, +}; + static void gpio_input_map(char *buf, struct ptp_ocp *bp, u16 map[][2], u16 bit, const char *def) From 9c44a7ac17fb980f8e297588e75a6158c69a1a8e Mon Sep 17 00:00:00 2001 From: Vadim Fedorenko Date: Fri, 21 Oct 2022 02:24:31 +0300 Subject: [PATCH 0296/2002] ptp: ocp: add serial port of mRO50 MAC on ART card ART card provides interface to access to serial port of miniature atomic clock found on the card. Add support for this device and configure it during init phase. Acked-by: Jonathan Lemon Co-developed-by: Charles Parent Signed-off-by: Jonathan Lemon Signed-off-by: Vadim Fedorenko Signed-off-by: David S. Miller --- drivers/ptp/ptp_ocp.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/drivers/ptp/ptp_ocp.c b/drivers/ptp/ptp_ocp.c index 8c1ce630b67f..223980d725fe 100644 --- a/drivers/ptp/ptp_ocp.c +++ b/drivers/ptp/ptp_ocp.c @@ -208,6 +208,11 @@ struct frequency_reg { u32 ctrl; u32 status; }; + +struct board_config_reg { + u32 mro50_serial_activate; +}; + #define FREQ_STATUS_VALID BIT(31) #define FREQ_STATUS_ERROR BIT(30) #define FREQ_STATUS_OVERRUN BIT(29) @@ -299,6 +304,7 @@ struct ptp_ocp { struct tod_reg __iomem *tod; struct pps_reg __iomem *pps_to_ext; struct pps_reg __iomem *pps_to_clk; + struct board_config_reg __iomem *board_config; struct gpio_reg __iomem *pps_select; struct gpio_reg __iomem *sma_map1; struct gpio_reg __iomem *sma_map2; @@ -795,6 +801,17 @@ static struct ocp_resource ocp_art_resource[] = { }, }, }, + { + OCP_SERIAL_RESOURCE(mac_port), + .offset = 0x00190000, .irq_vec = 7, + .extra = &(struct ptp_ocp_serial_port) { + .baud = 9600, + }, + }, + { + OCP_MEM_RESOURCE(board_config), + .offset = 0x210000, .size = 0x1000, + }, { .setup = ptp_ocp_art_board_init, }, @@ -2532,6 +2549,9 @@ ptp_ocp_art_board_init(struct ptp_ocp *bp, struct ocp_resource *r) bp->fw_tag = 2; bp->sma_op = &ocp_art_sma_op; + /* Enable MAC serial port during initialisation */ + iowrite32(1, &bp->board_config->mro50_serial_activate); + ptp_ocp_sma_init(bp); err = ptp_ocp_attr_group_add(bp, art_timecard_groups); From ee6439aaad3245851bbbcbf6c7534c9a29407ab2 Mon Sep 17 00:00:00 2001 From: Vadim Fedorenko Date: Fri, 21 Oct 2022 02:24:32 +0300 Subject: [PATCH 0297/2002] ptp: ocp: expose config and temperature for ART card Orolia card has disciplining configuration and temperature table stored in EEPROM. This patch exposes them as binary attributes to have read and write access. Acked-by: Jonathan Lemon Co-developed-by: Charles Parent Signed-off-by: Jonathan Lemon Signed-off-by: Vadim Fedorenko Signed-off-by: David S. Miller --- drivers/ptp/ptp_ocp.c | 136 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 136 insertions(+) diff --git a/drivers/ptp/ptp_ocp.c b/drivers/ptp/ptp_ocp.c index 223980d725fe..d2d486ccd714 100644 --- a/drivers/ptp/ptp_ocp.c +++ b/drivers/ptp/ptp_ocp.c @@ -690,6 +690,9 @@ static struct ocp_resource ocp_fb_resource[] = { { } }; +#define OCP_ART_CONFIG_SIZE 144 +#define OCP_ART_TEMP_TABLE_SIZE 368 + struct ocp_art_gpio_reg { struct { u32 gpio; @@ -3334,6 +3337,130 @@ DEVICE_FREQ_GROUP(freq2, 1); DEVICE_FREQ_GROUP(freq3, 2); DEVICE_FREQ_GROUP(freq4, 3); +static ssize_t +disciplining_config_read(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, char *buf, + loff_t off, size_t count) +{ + struct ptp_ocp *bp = dev_get_drvdata(kobj_to_dev(kobj)); + size_t size = OCP_ART_CONFIG_SIZE; + struct nvmem_device *nvmem; + ssize_t err; + + nvmem = ptp_ocp_nvmem_device_get(bp, NULL); + if (IS_ERR(nvmem)) + return PTR_ERR(nvmem); + + if (off > size) { + err = 0; + goto out; + } + + if (off + count > size) + count = size - off; + + // the configuration is in the very beginning of the EEPROM + err = nvmem_device_read(nvmem, off, count, buf); + if (err != count) { + err = -EFAULT; + goto out; + } + +out: + ptp_ocp_nvmem_device_put(&nvmem); + + return err; +} + +static ssize_t +disciplining_config_write(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, char *buf, + loff_t off, size_t count) +{ + struct ptp_ocp *bp = dev_get_drvdata(kobj_to_dev(kobj)); + struct nvmem_device *nvmem; + ssize_t err; + + /* Allow write of the whole area only */ + if (off || count != OCP_ART_CONFIG_SIZE) + return -EFAULT; + + nvmem = ptp_ocp_nvmem_device_get(bp, NULL); + if (IS_ERR(nvmem)) + return PTR_ERR(nvmem); + + err = nvmem_device_write(nvmem, 0x00, count, buf); + if (err != count) + err = -EFAULT; + + ptp_ocp_nvmem_device_put(&nvmem); + + return err; +} +static BIN_ATTR_RW(disciplining_config, OCP_ART_CONFIG_SIZE); + +static ssize_t +temperature_table_read(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, char *buf, + loff_t off, size_t count) +{ + struct ptp_ocp *bp = dev_get_drvdata(kobj_to_dev(kobj)); + size_t size = OCP_ART_TEMP_TABLE_SIZE; + struct nvmem_device *nvmem; + ssize_t err; + + nvmem = ptp_ocp_nvmem_device_get(bp, NULL); + if (IS_ERR(nvmem)) + return PTR_ERR(nvmem); + + if (off > size) { + err = 0; + goto out; + } + + if (off + count > size) + count = size - off; + + // the configuration is in the very beginning of the EEPROM + err = nvmem_device_read(nvmem, 0x90 + off, count, buf); + if (err != count) { + err = -EFAULT; + goto out; + } + +out: + ptp_ocp_nvmem_device_put(&nvmem); + + return err; +} + +static ssize_t +temperature_table_write(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, char *buf, + loff_t off, size_t count) +{ + struct ptp_ocp *bp = dev_get_drvdata(kobj_to_dev(kobj)); + struct nvmem_device *nvmem; + ssize_t err; + + /* Allow write of the whole area only */ + if (off || count != OCP_ART_TEMP_TABLE_SIZE) + return -EFAULT; + + nvmem = ptp_ocp_nvmem_device_get(bp, NULL); + if (IS_ERR(nvmem)) + return PTR_ERR(nvmem); + + err = nvmem_device_write(nvmem, 0x90, count, buf); + if (err != count) + err = -EFAULT; + + ptp_ocp_nvmem_device_put(&nvmem); + + return err; +} +static BIN_ATTR_RW(temperature_table, OCP_ART_TEMP_TABLE_SIZE); + static struct attribute *fb_timecard_attrs[] = { &dev_attr_serialnum.attr, &dev_attr_gnss_sync.attr, @@ -3353,9 +3480,11 @@ static struct attribute *fb_timecard_attrs[] = { &dev_attr_tod_correction.attr, NULL, }; + static const struct attribute_group fb_timecard_group = { .attrs = fb_timecard_attrs, }; + static const struct ocp_attr_group fb_timecard_groups[] = { { .cap = OCP_CAP_BASIC, .group = &fb_timecard_group }, { .cap = OCP_CAP_SIGNAL, .group = &fb_timecard_signal0_group }, @@ -3384,8 +3513,15 @@ static struct attribute *art_timecard_attrs[] = { NULL, }; +static struct bin_attribute *bin_art_timecard_attrs[] = { + &bin_attr_disciplining_config, + &bin_attr_temperature_table, + NULL, +}; + static const struct attribute_group art_timecard_group = { .attrs = art_timecard_attrs, + .bin_attrs = bin_art_timecard_attrs, }; static const struct ocp_attr_group art_timecard_groups[] = { From c1fd463d571a155bf43e04fd23d02aef364c5b0b Mon Sep 17 00:00:00 2001 From: Vadim Fedorenko Date: Fri, 21 Oct 2022 02:24:33 +0300 Subject: [PATCH 0298/2002] ptp: ocp: remove flash image header check fallback Previously there was a fallback mode to flash firmware image without proper header. But now we have different supported vendors and flashing wrong image could destroy the hardware. Remove fallback mode and force header check. Both vendors have published firmware images with headers. Acked-by: Jonathan Lemon Signed-off-by: Vadim Fedorenko Signed-off-by: David S. Miller --- drivers/ptp/ptp_ocp.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/drivers/ptp/ptp_ocp.c b/drivers/ptp/ptp_ocp.c index d2d486ccd714..154d58cbd9ce 100644 --- a/drivers/ptp/ptp_ocp.c +++ b/drivers/ptp/ptp_ocp.c @@ -1530,11 +1530,9 @@ ptp_ocp_devlink_fw_image(struct devlink *devlink, const struct firmware *fw, hdr = (const struct ptp_ocp_firmware_header *)fw->data; if (memcmp(hdr->magic, OCP_FIRMWARE_MAGIC_HEADER, 4)) { devlink_flash_update_status_notify(devlink, - "No firmware header found, flashing raw image", + "No firmware header found, cancel firmware upgrade", NULL, 0, 0); - offset = 0; - length = fw->size; - goto out; + return -EINVAL; } if (be16_to_cpu(hdr->pci_vendor_id) != bp->pdev->vendor || @@ -1562,7 +1560,6 @@ ptp_ocp_devlink_fw_image(struct devlink *devlink, const struct firmware *fw, return -EINVAL; } -out: *data = &fw->data[offset]; *size = length; From 84a911db83055e2d4c9d0171f116a47711014374 Mon Sep 17 00:00:00 2001 From: Michael Chan Date: Fri, 21 Oct 2022 02:37:21 -0400 Subject: [PATCH 0299/2002] bnxt_en: Update firmware interface to 1.10.2.118 The main changes are PTM timestamp support, CMIS EEPROM support, and asymmetric CoS queues support. Signed-off-by: Michael Chan Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/broadcom/bnxt/bnxt_hsi.h | 234 +++++++++++++----- 1 file changed, 169 insertions(+), 65 deletions(-) diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_hsi.h b/drivers/net/ethernet/broadcom/bnxt/bnxt_hsi.h index b753032a1047..184dd8d11cd3 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_hsi.h +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_hsi.h @@ -254,6 +254,8 @@ struct cmd_nums { #define HWRM_PORT_DSC_DUMP 0xd9UL #define HWRM_PORT_EP_TX_QCFG 0xdaUL #define HWRM_PORT_EP_TX_CFG 0xdbUL + #define HWRM_PORT_CFG 0xdcUL + #define HWRM_PORT_QCFG 0xddUL #define HWRM_TEMP_MONITOR_QUERY 0xe0UL #define HWRM_REG_POWER_QUERY 0xe1UL #define HWRM_CORE_FREQUENCY_QUERY 0xe2UL @@ -379,6 +381,8 @@ struct cmd_nums { #define HWRM_FUNC_BACKING_STORE_QCAPS_V2 0x1a8UL #define HWRM_FUNC_DBR_PACING_NQLIST_QUERY 0x1a9UL #define HWRM_FUNC_DBR_RECOVERY_COMPLETED 0x1aaUL + #define HWRM_FUNC_SYNCE_CFG 0x1abUL + #define HWRM_FUNC_SYNCE_QCFG 0x1acUL #define HWRM_SELFTEST_QLIST 0x200UL #define HWRM_SELFTEST_EXEC 0x201UL #define HWRM_SELFTEST_IRQ 0x202UL @@ -417,6 +421,8 @@ struct cmd_nums { #define HWRM_TF_SESSION_RESC_FREE 0x2ceUL #define HWRM_TF_SESSION_RESC_FLUSH 0x2cfUL #define HWRM_TF_SESSION_RESC_INFO 0x2d0UL + #define HWRM_TF_SESSION_HOTUP_STATE_SET 0x2d1UL + #define HWRM_TF_SESSION_HOTUP_STATE_GET 0x2d2UL #define HWRM_TF_TBL_TYPE_GET 0x2daUL #define HWRM_TF_TBL_TYPE_SET 0x2dbUL #define HWRM_TF_TBL_TYPE_BULK_GET 0x2dcUL @@ -440,6 +446,25 @@ struct cmd_nums { #define HWRM_TF_GLOBAL_CFG_GET 0x2fdUL #define HWRM_TF_IF_TBL_SET 0x2feUL #define HWRM_TF_IF_TBL_GET 0x2ffUL + #define HWRM_TFC_TBL_SCOPE_QCAPS 0x380UL + #define HWRM_TFC_TBL_SCOPE_ID_ALLOC 0x381UL + #define HWRM_TFC_TBL_SCOPE_CONFIG 0x382UL + #define HWRM_TFC_TBL_SCOPE_DECONFIG 0x383UL + #define HWRM_TFC_TBL_SCOPE_FID_ADD 0x384UL + #define HWRM_TFC_TBL_SCOPE_FID_REM 0x385UL + #define HWRM_TFC_TBL_SCOPE_POOL_ALLOC 0x386UL + #define HWRM_TFC_TBL_SCOPE_POOL_FREE 0x387UL + #define HWRM_TFC_SESSION_ID_ALLOC 0x388UL + #define HWRM_TFC_SESSION_FID_ADD 0x389UL + #define HWRM_TFC_SESSION_FID_REM 0x38aUL + #define HWRM_TFC_IDENT_ALLOC 0x38bUL + #define HWRM_TFC_IDENT_FREE 0x38cUL + #define HWRM_TFC_IDX_TBL_ALLOC 0x38dUL + #define HWRM_TFC_IDX_TBL_ALLOC_SET 0x38eUL + #define HWRM_TFC_IDX_TBL_SET 0x38fUL + #define HWRM_TFC_IDX_TBL_GET 0x390UL + #define HWRM_TFC_IDX_TBL_FREE 0x391UL + #define HWRM_TFC_GLOBAL_ID_ALLOC 0x392UL #define HWRM_SV 0x400UL #define HWRM_DBG_READ_DIRECT 0xff10UL #define HWRM_DBG_READ_INDIRECT 0xff11UL @@ -546,8 +571,8 @@ struct hwrm_err_output { #define HWRM_VERSION_MAJOR 1 #define HWRM_VERSION_MINOR 10 #define HWRM_VERSION_UPDATE 2 -#define HWRM_VERSION_RSVD 95 -#define HWRM_VERSION_STR "1.10.2.95" +#define HWRM_VERSION_RSVD 118 +#define HWRM_VERSION_STR "1.10.2.118" /* hwrm_ver_get_input (size:192b/24B) */ struct hwrm_ver_get_input { @@ -1657,6 +1682,10 @@ struct hwrm_func_qcaps_output { #define FUNC_QCAPS_RESP_FLAGS_EXT2_DBR_PACING_EXT_SUPPORTED 0x8UL #define FUNC_QCAPS_RESP_FLAGS_EXT2_SW_DBR_DROP_RECOVERY_SUPPORTED 0x10UL #define FUNC_QCAPS_RESP_FLAGS_EXT2_GENERIC_STATS_SUPPORTED 0x20UL + #define FUNC_QCAPS_RESP_FLAGS_EXT2_UDP_GSO_SUPPORTED 0x40UL + #define FUNC_QCAPS_RESP_FLAGS_EXT2_SYNCE_SUPPORTED 0x80UL + #define FUNC_QCAPS_RESP_FLAGS_EXT2_DBR_PACING_V0_SUPPORTED 0x100UL + #define FUNC_QCAPS_RESP_FLAGS_EXT2_TX_PKT_TS_CMPL_SUPPORTED 0x200UL __le16 tunnel_disable_flag; #define FUNC_QCAPS_RESP_TUNNEL_DISABLE_FLAG_DISABLE_VXLAN 0x1UL #define FUNC_QCAPS_RESP_TUNNEL_DISABLE_FLAG_DISABLE_NGE 0x2UL @@ -1804,7 +1833,20 @@ struct hwrm_func_qcfg_output { #define FUNC_QCFG_RESP_MPC_CHNLS_TE_CFA_ENABLED 0x4UL #define FUNC_QCFG_RESP_MPC_CHNLS_RE_CFA_ENABLED 0x8UL #define FUNC_QCFG_RESP_MPC_CHNLS_PRIMATE_ENABLED 0x10UL - u8 unused_2[3]; + u8 db_page_size; + #define FUNC_QCFG_RESP_DB_PAGE_SIZE_4KB 0x0UL + #define FUNC_QCFG_RESP_DB_PAGE_SIZE_8KB 0x1UL + #define FUNC_QCFG_RESP_DB_PAGE_SIZE_16KB 0x2UL + #define FUNC_QCFG_RESP_DB_PAGE_SIZE_32KB 0x3UL + #define FUNC_QCFG_RESP_DB_PAGE_SIZE_64KB 0x4UL + #define FUNC_QCFG_RESP_DB_PAGE_SIZE_128KB 0x5UL + #define FUNC_QCFG_RESP_DB_PAGE_SIZE_256KB 0x6UL + #define FUNC_QCFG_RESP_DB_PAGE_SIZE_512KB 0x7UL + #define FUNC_QCFG_RESP_DB_PAGE_SIZE_1MB 0x8UL + #define FUNC_QCFG_RESP_DB_PAGE_SIZE_2MB 0x9UL + #define FUNC_QCFG_RESP_DB_PAGE_SIZE_4MB 0xaUL + #define FUNC_QCFG_RESP_DB_PAGE_SIZE_LAST FUNC_QCFG_RESP_DB_PAGE_SIZE_4MB + u8 unused_2[2]; __le32 partition_min_bw; #define FUNC_QCFG_RESP_PARTITION_MIN_BW_BW_VALUE_MASK 0xfffffffUL #define FUNC_QCFG_RESP_PARTITION_MIN_BW_BW_VALUE_SFT 0 @@ -1876,6 +1918,7 @@ struct hwrm_func_cfg_input { #define FUNC_CFG_REQ_FLAGS_PPP_PUSH_MODE_DISABLE 0x10000000UL #define FUNC_CFG_REQ_FLAGS_BD_METADATA_ENABLE 0x20000000UL #define FUNC_CFG_REQ_FLAGS_BD_METADATA_DISABLE 0x40000000UL + #define FUNC_CFG_REQ_FLAGS_KEY_CTX_ASSETS_TEST 0x80000000UL __le32 enables; #define FUNC_CFG_REQ_ENABLES_ADMIN_MTU 0x1UL #define FUNC_CFG_REQ_ENABLES_MRU 0x2UL @@ -2021,12 +2064,26 @@ struct hwrm_func_cfg_input { __le16 num_tx_key_ctxs; __le16 num_rx_key_ctxs; __le32 enables2; - #define FUNC_CFG_REQ_ENABLES2_KDNET 0x1UL + #define FUNC_CFG_REQ_ENABLES2_KDNET 0x1UL + #define FUNC_CFG_REQ_ENABLES2_DB_PAGE_SIZE 0x2UL u8 port_kdnet_mode; #define FUNC_CFG_REQ_PORT_KDNET_MODE_DISABLED 0x0UL #define FUNC_CFG_REQ_PORT_KDNET_MODE_ENABLED 0x1UL #define FUNC_CFG_REQ_PORT_KDNET_MODE_LAST FUNC_CFG_REQ_PORT_KDNET_MODE_ENABLED - u8 unused_0[7]; + u8 db_page_size; + #define FUNC_CFG_REQ_DB_PAGE_SIZE_4KB 0x0UL + #define FUNC_CFG_REQ_DB_PAGE_SIZE_8KB 0x1UL + #define FUNC_CFG_REQ_DB_PAGE_SIZE_16KB 0x2UL + #define FUNC_CFG_REQ_DB_PAGE_SIZE_32KB 0x3UL + #define FUNC_CFG_REQ_DB_PAGE_SIZE_64KB 0x4UL + #define FUNC_CFG_REQ_DB_PAGE_SIZE_128KB 0x5UL + #define FUNC_CFG_REQ_DB_PAGE_SIZE_256KB 0x6UL + #define FUNC_CFG_REQ_DB_PAGE_SIZE_512KB 0x7UL + #define FUNC_CFG_REQ_DB_PAGE_SIZE_1MB 0x8UL + #define FUNC_CFG_REQ_DB_PAGE_SIZE_2MB 0x9UL + #define FUNC_CFG_REQ_DB_PAGE_SIZE_4MB 0xaUL + #define FUNC_CFG_REQ_DB_PAGE_SIZE_LAST FUNC_CFG_REQ_DB_PAGE_SIZE_4MB + u8 unused_0[6]; }; /* hwrm_func_cfg_output (size:128b/16B) */ @@ -2060,10 +2117,9 @@ struct hwrm_func_qstats_input { __le64 resp_addr; __le16 fid; u8 flags; - #define FUNC_QSTATS_REQ_FLAGS_UNUSED 0x0UL - #define FUNC_QSTATS_REQ_FLAGS_ROCE_ONLY 0x1UL - #define FUNC_QSTATS_REQ_FLAGS_COUNTER_MASK 0x2UL - #define FUNC_QSTATS_REQ_FLAGS_LAST FUNC_QSTATS_REQ_FLAGS_COUNTER_MASK + #define FUNC_QSTATS_REQ_FLAGS_ROCE_ONLY 0x1UL + #define FUNC_QSTATS_REQ_FLAGS_COUNTER_MASK 0x2UL + #define FUNC_QSTATS_REQ_FLAGS_L2_ONLY 0x4UL u8 unused_0[5]; }; @@ -2093,7 +2149,8 @@ struct hwrm_func_qstats_output { __le64 rx_agg_bytes; __le64 rx_agg_events; __le64 rx_agg_aborts; - u8 unused_0[7]; + u8 clear_seq; + u8 unused_0[6]; u8 valid; }; @@ -2106,10 +2163,8 @@ struct hwrm_func_qstats_ext_input { __le64 resp_addr; __le16 fid; u8 flags; - #define FUNC_QSTATS_EXT_REQ_FLAGS_UNUSED 0x0UL - #define FUNC_QSTATS_EXT_REQ_FLAGS_ROCE_ONLY 0x1UL - #define FUNC_QSTATS_EXT_REQ_FLAGS_COUNTER_MASK 0x2UL - #define FUNC_QSTATS_EXT_REQ_FLAGS_LAST FUNC_QSTATS_EXT_REQ_FLAGS_COUNTER_MASK + #define FUNC_QSTATS_EXT_REQ_FLAGS_ROCE_ONLY 0x1UL + #define FUNC_QSTATS_EXT_REQ_FLAGS_COUNTER_MASK 0x2UL u8 unused_0[1]; __le32 enables; #define FUNC_QSTATS_EXT_REQ_ENABLES_SCHQ_ID 0x1UL @@ -2210,6 +2265,7 @@ struct hwrm_func_drv_rgtr_input { #define FUNC_DRV_RGTR_REQ_FLAGS_FAST_RESET_SUPPORT 0x80UL #define FUNC_DRV_RGTR_REQ_FLAGS_RSS_STRICT_HASH_TYPE_SUPPORT 0x100UL #define FUNC_DRV_RGTR_REQ_FLAGS_NPAR_1_2_SUPPORT 0x200UL + #define FUNC_DRV_RGTR_REQ_FLAGS_ASYM_QUEUE_CFG_SUPPORT 0x400UL __le32 enables; #define FUNC_DRV_RGTR_REQ_ENABLES_OS_TYPE 0x1UL #define FUNC_DRV_RGTR_REQ_ENABLES_VER 0x2UL @@ -3155,19 +3211,23 @@ struct hwrm_func_ptp_pin_qcfg_output { #define FUNC_PTP_PIN_QCFG_RESP_PIN1_USAGE_SYNC_OUT 0x4UL #define FUNC_PTP_PIN_QCFG_RESP_PIN1_USAGE_LAST FUNC_PTP_PIN_QCFG_RESP_PIN1_USAGE_SYNC_OUT u8 pin2_usage; - #define FUNC_PTP_PIN_QCFG_RESP_PIN2_USAGE_NONE 0x0UL - #define FUNC_PTP_PIN_QCFG_RESP_PIN2_USAGE_PPS_IN 0x1UL - #define FUNC_PTP_PIN_QCFG_RESP_PIN2_USAGE_PPS_OUT 0x2UL - #define FUNC_PTP_PIN_QCFG_RESP_PIN2_USAGE_SYNC_IN 0x3UL - #define FUNC_PTP_PIN_QCFG_RESP_PIN2_USAGE_SYNC_OUT 0x4UL - #define FUNC_PTP_PIN_QCFG_RESP_PIN2_USAGE_LAST FUNC_PTP_PIN_QCFG_RESP_PIN2_USAGE_SYNC_OUT + #define FUNC_PTP_PIN_QCFG_RESP_PIN2_USAGE_NONE 0x0UL + #define FUNC_PTP_PIN_QCFG_RESP_PIN2_USAGE_PPS_IN 0x1UL + #define FUNC_PTP_PIN_QCFG_RESP_PIN2_USAGE_PPS_OUT 0x2UL + #define FUNC_PTP_PIN_QCFG_RESP_PIN2_USAGE_SYNC_IN 0x3UL + #define FUNC_PTP_PIN_QCFG_RESP_PIN2_USAGE_SYNC_OUT 0x4UL + #define FUNC_PTP_PIN_QCFG_RESP_PIN2_USAGE_SYNCE_PRIMARY_CLOCK_OUT 0x5UL + #define FUNC_PTP_PIN_QCFG_RESP_PIN2_USAGE_SYNCE_SECONDARY_CLOCK_OUT 0x6UL + #define FUNC_PTP_PIN_QCFG_RESP_PIN2_USAGE_LAST FUNC_PTP_PIN_QCFG_RESP_PIN2_USAGE_SYNCE_SECONDARY_CLOCK_OUT u8 pin3_usage; - #define FUNC_PTP_PIN_QCFG_RESP_PIN3_USAGE_NONE 0x0UL - #define FUNC_PTP_PIN_QCFG_RESP_PIN3_USAGE_PPS_IN 0x1UL - #define FUNC_PTP_PIN_QCFG_RESP_PIN3_USAGE_PPS_OUT 0x2UL - #define FUNC_PTP_PIN_QCFG_RESP_PIN3_USAGE_SYNC_IN 0x3UL - #define FUNC_PTP_PIN_QCFG_RESP_PIN3_USAGE_SYNC_OUT 0x4UL - #define FUNC_PTP_PIN_QCFG_RESP_PIN3_USAGE_LAST FUNC_PTP_PIN_QCFG_RESP_PIN3_USAGE_SYNC_OUT + #define FUNC_PTP_PIN_QCFG_RESP_PIN3_USAGE_NONE 0x0UL + #define FUNC_PTP_PIN_QCFG_RESP_PIN3_USAGE_PPS_IN 0x1UL + #define FUNC_PTP_PIN_QCFG_RESP_PIN3_USAGE_PPS_OUT 0x2UL + #define FUNC_PTP_PIN_QCFG_RESP_PIN3_USAGE_SYNC_IN 0x3UL + #define FUNC_PTP_PIN_QCFG_RESP_PIN3_USAGE_SYNC_OUT 0x4UL + #define FUNC_PTP_PIN_QCFG_RESP_PIN3_USAGE_SYNCE_PRIMARY_CLOCK_OUT 0x5UL + #define FUNC_PTP_PIN_QCFG_RESP_PIN3_USAGE_SYNCE_SECONDARY_CLOCK_OUT 0x6UL + #define FUNC_PTP_PIN_QCFG_RESP_PIN3_USAGE_LAST FUNC_PTP_PIN_QCFG_RESP_PIN3_USAGE_SYNCE_SECONDARY_CLOCK_OUT u8 unused_0; u8 valid; }; @@ -3215,23 +3275,27 @@ struct hwrm_func_ptp_pin_cfg_input { #define FUNC_PTP_PIN_CFG_REQ_PIN2_STATE_ENABLED 0x1UL #define FUNC_PTP_PIN_CFG_REQ_PIN2_STATE_LAST FUNC_PTP_PIN_CFG_REQ_PIN2_STATE_ENABLED u8 pin2_usage; - #define FUNC_PTP_PIN_CFG_REQ_PIN2_USAGE_NONE 0x0UL - #define FUNC_PTP_PIN_CFG_REQ_PIN2_USAGE_PPS_IN 0x1UL - #define FUNC_PTP_PIN_CFG_REQ_PIN2_USAGE_PPS_OUT 0x2UL - #define FUNC_PTP_PIN_CFG_REQ_PIN2_USAGE_SYNC_IN 0x3UL - #define FUNC_PTP_PIN_CFG_REQ_PIN2_USAGE_SYNC_OUT 0x4UL - #define FUNC_PTP_PIN_CFG_REQ_PIN2_USAGE_LAST FUNC_PTP_PIN_CFG_REQ_PIN2_USAGE_SYNC_OUT + #define FUNC_PTP_PIN_CFG_REQ_PIN2_USAGE_NONE 0x0UL + #define FUNC_PTP_PIN_CFG_REQ_PIN2_USAGE_PPS_IN 0x1UL + #define FUNC_PTP_PIN_CFG_REQ_PIN2_USAGE_PPS_OUT 0x2UL + #define FUNC_PTP_PIN_CFG_REQ_PIN2_USAGE_SYNC_IN 0x3UL + #define FUNC_PTP_PIN_CFG_REQ_PIN2_USAGE_SYNC_OUT 0x4UL + #define FUNC_PTP_PIN_CFG_REQ_PIN2_USAGE_SYNCE_PRIMARY_CLOCK_OUT 0x5UL + #define FUNC_PTP_PIN_CFG_REQ_PIN2_USAGE_SYNCE_SECONDARY_CLOCK_OUT 0x6UL + #define FUNC_PTP_PIN_CFG_REQ_PIN2_USAGE_LAST FUNC_PTP_PIN_CFG_REQ_PIN2_USAGE_SYNCE_SECONDARY_CLOCK_OUT u8 pin3_state; #define FUNC_PTP_PIN_CFG_REQ_PIN3_STATE_DISABLED 0x0UL #define FUNC_PTP_PIN_CFG_REQ_PIN3_STATE_ENABLED 0x1UL #define FUNC_PTP_PIN_CFG_REQ_PIN3_STATE_LAST FUNC_PTP_PIN_CFG_REQ_PIN3_STATE_ENABLED u8 pin3_usage; - #define FUNC_PTP_PIN_CFG_REQ_PIN3_USAGE_NONE 0x0UL - #define FUNC_PTP_PIN_CFG_REQ_PIN3_USAGE_PPS_IN 0x1UL - #define FUNC_PTP_PIN_CFG_REQ_PIN3_USAGE_PPS_OUT 0x2UL - #define FUNC_PTP_PIN_CFG_REQ_PIN3_USAGE_SYNC_IN 0x3UL - #define FUNC_PTP_PIN_CFG_REQ_PIN3_USAGE_SYNC_OUT 0x4UL - #define FUNC_PTP_PIN_CFG_REQ_PIN3_USAGE_LAST FUNC_PTP_PIN_CFG_REQ_PIN3_USAGE_SYNC_OUT + #define FUNC_PTP_PIN_CFG_REQ_PIN3_USAGE_NONE 0x0UL + #define FUNC_PTP_PIN_CFG_REQ_PIN3_USAGE_PPS_IN 0x1UL + #define FUNC_PTP_PIN_CFG_REQ_PIN3_USAGE_PPS_OUT 0x2UL + #define FUNC_PTP_PIN_CFG_REQ_PIN3_USAGE_SYNC_IN 0x3UL + #define FUNC_PTP_PIN_CFG_REQ_PIN3_USAGE_SYNC_OUT 0x4UL + #define FUNC_PTP_PIN_CFG_REQ_PIN3_USAGE_SYNCE_PRIMARY_CLOCK_OUT 0x5UL + #define FUNC_PTP_PIN_CFG_REQ_PIN3_USAGE_SYNCE_SECONDARY_CLOCK_OUT 0x6UL + #define FUNC_PTP_PIN_CFG_REQ_PIN3_USAGE_LAST FUNC_PTP_PIN_CFG_REQ_PIN3_USAGE_SYNCE_SECONDARY_CLOCK_OUT u8 unused_0[4]; }; @@ -3319,9 +3383,9 @@ struct hwrm_func_ptp_ts_query_output { __le16 seq_id; __le16 resp_len; __le64 pps_event_ts; - __le64 ptm_res_local_ts; - __le64 ptm_pmstr_ts; - __le32 ptm_mstr_prop_dly; + __le64 ptm_local_ts; + __le64 ptm_system_ts; + __le32 ptm_link_delay; u8 unused_0[3]; u8 valid; }; @@ -3417,7 +3481,9 @@ struct hwrm_func_backing_store_cfg_v2_input { #define FUNC_BACKING_STORE_CFG_V2_REQ_TYPE_LAST FUNC_BACKING_STORE_CFG_V2_REQ_TYPE_INVALID __le16 instance; __le32 flags; - #define FUNC_BACKING_STORE_CFG_V2_REQ_FLAGS_PREBOOT_MODE 0x1UL + #define FUNC_BACKING_STORE_CFG_V2_REQ_FLAGS_PREBOOT_MODE 0x1UL + #define FUNC_BACKING_STORE_CFG_V2_REQ_FLAGS_BS_CFG_ALL_DONE 0x2UL + #define FUNC_BACKING_STORE_CFG_V2_REQ_FLAGS_BS_EXTEND 0x4UL __le64 page_dir; __le32 num_entries; __le16 entry_size; @@ -3853,7 +3919,7 @@ struct hwrm_port_phy_qcfg_input { u8 unused_0[6]; }; -/* hwrm_port_phy_qcfg_output (size:768b/96B) */ +/* hwrm_port_phy_qcfg_output (size:832b/104B) */ struct hwrm_port_phy_qcfg_output { __le16 error_code; __le16 req_type; @@ -4150,6 +4216,9 @@ struct hwrm_port_phy_qcfg_output { #define PORT_PHY_QCFG_RESP_LINK_PARTNER_PAM4_ADV_SPEEDS_50GB 0x1UL #define PORT_PHY_QCFG_RESP_LINK_PARTNER_PAM4_ADV_SPEEDS_100GB 0x2UL #define PORT_PHY_QCFG_RESP_LINK_PARTNER_PAM4_ADV_SPEEDS_200GB 0x4UL + u8 link_down_reason; + #define PORT_PHY_QCFG_RESP_LINK_DOWN_REASON_RF 0x1UL + u8 unused_0[7]; u8 valid; }; @@ -4422,9 +4491,7 @@ struct hwrm_port_qstats_input { __le64 resp_addr; __le16 port_id; u8 flags; - #define PORT_QSTATS_REQ_FLAGS_UNUSED 0x0UL - #define PORT_QSTATS_REQ_FLAGS_COUNTER_MASK 0x1UL - #define PORT_QSTATS_REQ_FLAGS_LAST PORT_QSTATS_REQ_FLAGS_COUNTER_MASK + #define PORT_QSTATS_REQ_FLAGS_COUNTER_MASK 0x1UL u8 unused_0[5]; __le64 tx_stat_host_addr; __le64 rx_stat_host_addr; @@ -4552,9 +4619,7 @@ struct hwrm_port_qstats_ext_input { __le16 tx_stat_size; __le16 rx_stat_size; u8 flags; - #define PORT_QSTATS_EXT_REQ_FLAGS_UNUSED 0x0UL - #define PORT_QSTATS_EXT_REQ_FLAGS_COUNTER_MASK 0x1UL - #define PORT_QSTATS_EXT_REQ_FLAGS_LAST PORT_QSTATS_EXT_REQ_FLAGS_COUNTER_MASK + #define PORT_QSTATS_EXT_REQ_FLAGS_COUNTER_MASK 0x1UL u8 unused_0; __le64 tx_stat_host_addr; __le64 rx_stat_host_addr; @@ -4613,9 +4678,7 @@ struct hwrm_port_ecn_qstats_input { __le16 port_id; __le16 ecn_stat_buf_size; u8 flags; - #define PORT_ECN_QSTATS_REQ_FLAGS_UNUSED 0x0UL - #define PORT_ECN_QSTATS_REQ_FLAGS_COUNTER_MASK 0x1UL - #define PORT_ECN_QSTATS_REQ_FLAGS_LAST PORT_ECN_QSTATS_REQ_FLAGS_COUNTER_MASK + #define PORT_ECN_QSTATS_REQ_FLAGS_COUNTER_MASK 0x1UL u8 unused_0[3]; __le64 ecn_stat_host_addr; }; @@ -4814,8 +4877,9 @@ struct hwrm_port_phy_qcaps_output { #define PORT_PHY_QCAPS_RESP_SUPPORTED_PAM4_SPEEDS_FORCE_MODE_100G 0x2UL #define PORT_PHY_QCAPS_RESP_SUPPORTED_PAM4_SPEEDS_FORCE_MODE_200G 0x4UL __le16 flags2; - #define PORT_PHY_QCAPS_RESP_FLAGS2_PAUSE_UNSUPPORTED 0x1UL - #define PORT_PHY_QCAPS_RESP_FLAGS2_PFC_UNSUPPORTED 0x2UL + #define PORT_PHY_QCAPS_RESP_FLAGS2_PAUSE_UNSUPPORTED 0x1UL + #define PORT_PHY_QCAPS_RESP_FLAGS2_PFC_UNSUPPORTED 0x2UL + #define PORT_PHY_QCAPS_RESP_FLAGS2_BANK_ADDR_SUPPORTED 0x4UL u8 internal_port_cnt; u8 valid; }; @@ -4830,9 +4894,10 @@ struct hwrm_port_phy_i2c_read_input { __le32 flags; __le32 enables; #define PORT_PHY_I2C_READ_REQ_ENABLES_PAGE_OFFSET 0x1UL + #define PORT_PHY_I2C_READ_REQ_ENABLES_BANK_NUMBER 0x2UL __le16 port_id; u8 i2c_slave_addr; - u8 unused_0; + u8 bank_number; __le16 page_number; __le16 page_offset; u8 data_length; @@ -6537,6 +6602,7 @@ struct hwrm_vnic_qcaps_output { #define VNIC_QCAPS_RESP_FLAGS_RSS_IPSEC_ESP_SPI_IPV4_CAP 0x400000UL #define VNIC_QCAPS_RESP_FLAGS_RSS_IPSEC_AH_SPI_IPV6_CAP 0x800000UL #define VNIC_QCAPS_RESP_FLAGS_RSS_IPSEC_ESP_SPI_IPV6_CAP 0x1000000UL + #define VNIC_QCAPS_RESP_FLAGS_OUTERMOST_RSS_TRUSTED_VF_CAP 0x2000000UL __le16 max_aggs_supported; u8 unused_1[5]; u8 valid; @@ -6827,6 +6893,7 @@ struct hwrm_ring_alloc_input { #define RING_ALLOC_REQ_FLAGS_RX_SOP_PAD 0x1UL #define RING_ALLOC_REQ_FLAGS_DISABLE_CQ_OVERFLOW_DETECTION 0x2UL #define RING_ALLOC_REQ_FLAGS_NQ_DBR_PACING 0x4UL + #define RING_ALLOC_REQ_FLAGS_TX_PKT_TS_CMPL_ENABLE 0x8UL __le64 page_tbl_addr; __le32 fbo; u8 page_size; @@ -7626,7 +7693,10 @@ struct hwrm_cfa_ntuple_filter_alloc_input { #define CFA_NTUPLE_FILTER_ALLOC_REQ_IP_PROTOCOL_UNKNOWN 0x0UL #define CFA_NTUPLE_FILTER_ALLOC_REQ_IP_PROTOCOL_TCP 0x6UL #define CFA_NTUPLE_FILTER_ALLOC_REQ_IP_PROTOCOL_UDP 0x11UL - #define CFA_NTUPLE_FILTER_ALLOC_REQ_IP_PROTOCOL_LAST CFA_NTUPLE_FILTER_ALLOC_REQ_IP_PROTOCOL_UDP + #define CFA_NTUPLE_FILTER_ALLOC_REQ_IP_PROTOCOL_ICMP 0x1UL + #define CFA_NTUPLE_FILTER_ALLOC_REQ_IP_PROTOCOL_ICMPV6 0x3aUL + #define CFA_NTUPLE_FILTER_ALLOC_REQ_IP_PROTOCOL_RSVD 0xffUL + #define CFA_NTUPLE_FILTER_ALLOC_REQ_IP_PROTOCOL_LAST CFA_NTUPLE_FILTER_ALLOC_REQ_IP_PROTOCOL_RSVD __le16 dst_id; __le16 mirror_vnic_id; u8 tunnel_type; @@ -8337,6 +8407,7 @@ struct hwrm_cfa_adv_flow_mgnt_qcaps_output { #define CFA_ADV_FLOW_MGNT_QCAPS_RESP_FLAGS_LAG_SUPPORTED 0x20000UL #define CFA_ADV_FLOW_MGNT_QCAPS_RESP_FLAGS_NTUPLE_FLOW_NO_L2CTX_SUPPORTED 0x40000UL #define CFA_ADV_FLOW_MGNT_QCAPS_RESP_FLAGS_NIC_FLOW_STATS_SUPPORTED 0x80000UL + #define CFA_ADV_FLOW_MGNT_QCAPS_RESP_FLAGS_NTUPLE_FLOW_RX_EXT_IP_PROTO_SUPPORTED 0x100000UL u8 unused_0[3]; u8 valid; }; @@ -8355,7 +8426,9 @@ struct hwrm_tunnel_dst_port_query_input { #define TUNNEL_DST_PORT_QUERY_REQ_TUNNEL_TYPE_IPGRE_V1 0xaUL #define TUNNEL_DST_PORT_QUERY_REQ_TUNNEL_TYPE_L2_ETYPE 0xbUL #define TUNNEL_DST_PORT_QUERY_REQ_TUNNEL_TYPE_VXLAN_GPE_V6 0xcUL - #define TUNNEL_DST_PORT_QUERY_REQ_TUNNEL_TYPE_LAST TUNNEL_DST_PORT_QUERY_REQ_TUNNEL_TYPE_VXLAN_GPE_V6 + #define TUNNEL_DST_PORT_QUERY_REQ_TUNNEL_TYPE_CUSTOM_GRE 0xdUL + #define TUNNEL_DST_PORT_QUERY_REQ_TUNNEL_TYPE_ECPRI 0xeUL + #define TUNNEL_DST_PORT_QUERY_REQ_TUNNEL_TYPE_LAST TUNNEL_DST_PORT_QUERY_REQ_TUNNEL_TYPE_ECPRI u8 unused_0[7]; }; @@ -8367,7 +8440,16 @@ struct hwrm_tunnel_dst_port_query_output { __le16 resp_len; __le16 tunnel_dst_port_id; __be16 tunnel_dst_port_val; - u8 unused_0[3]; + u8 upar_in_use; + #define TUNNEL_DST_PORT_QUERY_RESP_UPAR_IN_USE_UPAR0 0x1UL + #define TUNNEL_DST_PORT_QUERY_RESP_UPAR_IN_USE_UPAR1 0x2UL + #define TUNNEL_DST_PORT_QUERY_RESP_UPAR_IN_USE_UPAR2 0x4UL + #define TUNNEL_DST_PORT_QUERY_RESP_UPAR_IN_USE_UPAR3 0x8UL + #define TUNNEL_DST_PORT_QUERY_RESP_UPAR_IN_USE_UPAR4 0x10UL + #define TUNNEL_DST_PORT_QUERY_RESP_UPAR_IN_USE_UPAR5 0x20UL + #define TUNNEL_DST_PORT_QUERY_RESP_UPAR_IN_USE_UPAR6 0x40UL + #define TUNNEL_DST_PORT_QUERY_RESP_UPAR_IN_USE_UPAR7 0x80UL + u8 unused_0[2]; u8 valid; }; @@ -8385,7 +8467,9 @@ struct hwrm_tunnel_dst_port_alloc_input { #define TUNNEL_DST_PORT_ALLOC_REQ_TUNNEL_TYPE_IPGRE_V1 0xaUL #define TUNNEL_DST_PORT_ALLOC_REQ_TUNNEL_TYPE_L2_ETYPE 0xbUL #define TUNNEL_DST_PORT_ALLOC_REQ_TUNNEL_TYPE_VXLAN_GPE_V6 0xcUL - #define TUNNEL_DST_PORT_ALLOC_REQ_TUNNEL_TYPE_LAST TUNNEL_DST_PORT_ALLOC_REQ_TUNNEL_TYPE_VXLAN_GPE_V6 + #define TUNNEL_DST_PORT_ALLOC_REQ_TUNNEL_TYPE_CUSTOM_GRE 0xdUL + #define TUNNEL_DST_PORT_ALLOC_REQ_TUNNEL_TYPE_ECPRI 0xeUL + #define TUNNEL_DST_PORT_ALLOC_REQ_TUNNEL_TYPE_LAST TUNNEL_DST_PORT_ALLOC_REQ_TUNNEL_TYPE_ECPRI u8 unused_0; __be16 tunnel_dst_port_val; u8 unused_1[4]; @@ -8398,7 +8482,21 @@ struct hwrm_tunnel_dst_port_alloc_output { __le16 seq_id; __le16 resp_len; __le16 tunnel_dst_port_id; - u8 unused_0[5]; + u8 error_info; + #define TUNNEL_DST_PORT_ALLOC_RESP_ERROR_INFO_SUCCESS 0x0UL + #define TUNNEL_DST_PORT_ALLOC_RESP_ERROR_INFO_ERR_ALLOCATED 0x1UL + #define TUNNEL_DST_PORT_ALLOC_RESP_ERROR_INFO_ERR_NO_RESOURCE 0x2UL + #define TUNNEL_DST_PORT_ALLOC_RESP_ERROR_INFO_LAST TUNNEL_DST_PORT_ALLOC_RESP_ERROR_INFO_ERR_NO_RESOURCE + u8 upar_in_use; + #define TUNNEL_DST_PORT_ALLOC_RESP_UPAR_IN_USE_UPAR0 0x1UL + #define TUNNEL_DST_PORT_ALLOC_RESP_UPAR_IN_USE_UPAR1 0x2UL + #define TUNNEL_DST_PORT_ALLOC_RESP_UPAR_IN_USE_UPAR2 0x4UL + #define TUNNEL_DST_PORT_ALLOC_RESP_UPAR_IN_USE_UPAR3 0x8UL + #define TUNNEL_DST_PORT_ALLOC_RESP_UPAR_IN_USE_UPAR4 0x10UL + #define TUNNEL_DST_PORT_ALLOC_RESP_UPAR_IN_USE_UPAR5 0x20UL + #define TUNNEL_DST_PORT_ALLOC_RESP_UPAR_IN_USE_UPAR6 0x40UL + #define TUNNEL_DST_PORT_ALLOC_RESP_UPAR_IN_USE_UPAR7 0x80UL + u8 unused_0[3]; u8 valid; }; @@ -8416,7 +8514,9 @@ struct hwrm_tunnel_dst_port_free_input { #define TUNNEL_DST_PORT_FREE_REQ_TUNNEL_TYPE_IPGRE_V1 0xaUL #define TUNNEL_DST_PORT_FREE_REQ_TUNNEL_TYPE_L2_ETYPE 0xbUL #define TUNNEL_DST_PORT_FREE_REQ_TUNNEL_TYPE_VXLAN_GPE_V6 0xcUL - #define TUNNEL_DST_PORT_FREE_REQ_TUNNEL_TYPE_LAST TUNNEL_DST_PORT_FREE_REQ_TUNNEL_TYPE_VXLAN_GPE_V6 + #define TUNNEL_DST_PORT_FREE_REQ_TUNNEL_TYPE_CUSTOM_GRE 0xdUL + #define TUNNEL_DST_PORT_FREE_REQ_TUNNEL_TYPE_ECPRI 0xeUL + #define TUNNEL_DST_PORT_FREE_REQ_TUNNEL_TYPE_LAST TUNNEL_DST_PORT_FREE_REQ_TUNNEL_TYPE_ECPRI u8 unused_0; __le16 tunnel_dst_port_id; u8 unused_1[4]; @@ -8428,7 +8528,12 @@ struct hwrm_tunnel_dst_port_free_output { __le16 req_type; __le16 seq_id; __le16 resp_len; - u8 unused_1[7]; + u8 error_info; + #define TUNNEL_DST_PORT_FREE_RESP_ERROR_INFO_SUCCESS 0x0UL + #define TUNNEL_DST_PORT_FREE_RESP_ERROR_INFO_ERR_NOT_OWNER 0x1UL + #define TUNNEL_DST_PORT_FREE_RESP_ERROR_INFO_ERR_NOT_ALLOCATED 0x2UL + #define TUNNEL_DST_PORT_FREE_RESP_ERROR_INFO_LAST TUNNEL_DST_PORT_FREE_RESP_ERROR_INFO_ERR_NOT_ALLOCATED + u8 unused_1[6]; u8 valid; }; @@ -8686,9 +8791,7 @@ struct hwrm_stat_generic_qstats_input { __le64 resp_addr; __le16 generic_stat_size; u8 flags; - #define STAT_GENERIC_QSTATS_REQ_FLAGS_COUNTER 0x0UL - #define STAT_GENERIC_QSTATS_REQ_FLAGS_COUNTER_MASK 0x1UL - #define STAT_GENERIC_QSTATS_REQ_FLAGS_LAST STAT_GENERIC_QSTATS_REQ_FLAGS_COUNTER_MASK + #define STAT_GENERIC_QSTATS_REQ_FLAGS_COUNTER_MASK 0x1UL u8 unused_0[5]; __le64 generic_stat_host_addr; }; @@ -10202,6 +10305,7 @@ struct fw_status_reg { #define FW_STATUS_REG_SHUTDOWN 0x100000UL #define FW_STATUS_REG_CRASHED_NO_MASTER 0x200000UL #define FW_STATUS_REG_RECOVERING 0x400000UL + #define FW_STATUS_REG_MANU_DEBUG_STATUS 0x800000UL }; /* hcomm_status (size:64b/8B) */ From 7ef3d3901b99d9715840c9860082bec124beed83 Mon Sep 17 00:00:00 2001 From: Vikas Gupta Date: Fri, 21 Oct 2022 02:37:22 -0400 Subject: [PATCH 0300/2002] bnxt_en: add .get_module_eeprom_by_page() support Add support for .get_module_eeprom_by_page() callback which implements generic solution for module`s eeprom access. v3: Add bnxt_get_module_status() to get a more specific extack error string. Return -EINVAL from bnxt_get_module_eeprom_by_page() when we don't want to fallback to old method. v2: Simplification suggested by Ido Schimmel Link: https://lore.kernel.org/netdev/YzVJ%2FvKJugoz15yV@shredder/ Signed-off-by: Vikas Gupta Signed-off-by: Michael Chan Reviewed-by: Ido Schimmel Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/broadcom/bnxt/bnxt.h | 1 + .../net/ethernet/broadcom/bnxt/bnxt_ethtool.c | 77 +++++++++++++++++-- 2 files changed, 71 insertions(+), 7 deletions(-) diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.h b/drivers/net/ethernet/broadcom/bnxt/bnxt.h index b1b17f911300..91a1ba0a914d 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.h +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.h @@ -2116,6 +2116,7 @@ struct bnxt { #define BNXT_PHY_FL_NO_FCS PORT_PHY_QCAPS_RESP_FLAGS_NO_FCS #define BNXT_PHY_FL_NO_PAUSE (PORT_PHY_QCAPS_RESP_FLAGS2_PAUSE_UNSUPPORTED << 8) #define BNXT_PHY_FL_NO_PFC (PORT_PHY_QCAPS_RESP_FLAGS2_PFC_UNSUPPORTED << 8) +#define BNXT_PHY_FL_BANK_SEL (PORT_PHY_QCAPS_RESP_FLAGS2_BANK_ADDR_SUPPORTED << 8) u8 num_tests; struct bnxt_test_info *test_info; diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c index f57e524c7e30..44b715c4e19e 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c @@ -3146,8 +3146,9 @@ static int bnxt_get_eee(struct net_device *dev, struct ethtool_eee *edata) } static int bnxt_read_sfp_module_eeprom_info(struct bnxt *bp, u16 i2c_addr, - u16 page_number, u16 start_addr, - u16 data_length, u8 *buf) + u16 page_number, u8 bank, + u16 start_addr, u16 data_length, + u8 *buf) { struct hwrm_port_phy_i2c_read_output *output; struct hwrm_port_phy_i2c_read_input *req; @@ -3168,8 +3169,13 @@ static int bnxt_read_sfp_module_eeprom_info(struct bnxt *bp, u16 i2c_addr, data_length -= xfer_size; req->page_offset = cpu_to_le16(start_addr + byte_offset); req->data_length = xfer_size; - req->enables = cpu_to_le32(start_addr + byte_offset ? - PORT_PHY_I2C_READ_REQ_ENABLES_PAGE_OFFSET : 0); + req->enables = + cpu_to_le32((start_addr + byte_offset ? + PORT_PHY_I2C_READ_REQ_ENABLES_PAGE_OFFSET : + 0) | + (bank ? + PORT_PHY_I2C_READ_REQ_ENABLES_BANK_NUMBER : + 0)); rc = hwrm_req_send(bp, req); if (!rc) memcpy(buf + byte_offset, output->data, xfer_size); @@ -3199,7 +3205,7 @@ static int bnxt_get_module_info(struct net_device *dev, if (bp->hwrm_spec_code < 0x10202) return -EOPNOTSUPP; - rc = bnxt_read_sfp_module_eeprom_info(bp, I2C_DEV_ADDR_A0, 0, 0, + rc = bnxt_read_sfp_module_eeprom_info(bp, I2C_DEV_ADDR_A0, 0, 0, 0, SFF_DIAG_SUPPORT_OFFSET + 1, data); if (!rc) { @@ -3244,7 +3250,7 @@ static int bnxt_get_module_eeprom(struct net_device *dev, if (start < ETH_MODULE_SFF_8436_LEN) { if (start + eeprom->len > ETH_MODULE_SFF_8436_LEN) length = ETH_MODULE_SFF_8436_LEN - start; - rc = bnxt_read_sfp_module_eeprom_info(bp, I2C_DEV_ADDR_A0, 0, + rc = bnxt_read_sfp_module_eeprom_info(bp, I2C_DEV_ADDR_A0, 0, 0, start, length, data); if (rc) return rc; @@ -3256,12 +3262,68 @@ static int bnxt_get_module_eeprom(struct net_device *dev, /* Read A2 portion of the EEPROM */ if (length) { start -= ETH_MODULE_SFF_8436_LEN; - rc = bnxt_read_sfp_module_eeprom_info(bp, I2C_DEV_ADDR_A2, 0, + rc = bnxt_read_sfp_module_eeprom_info(bp, I2C_DEV_ADDR_A2, 0, 0, start, length, data); } return rc; } +static int bnxt_get_module_status(struct bnxt *bp, struct netlink_ext_ack *extack) +{ + if (bp->link_info.module_status <= + PORT_PHY_QCFG_RESP_MODULE_STATUS_WARNINGMSG) + return 0; + + switch (bp->link_info.module_status) { + case PORT_PHY_QCFG_RESP_MODULE_STATUS_PWRDOWN: + NL_SET_ERR_MSG_MOD(extack, "Transceiver module is powering down"); + break; + case PORT_PHY_QCFG_RESP_MODULE_STATUS_NOTINSERTED: + NL_SET_ERR_MSG_MOD(extack, "Transceiver module not inserted"); + break; + case PORT_PHY_QCFG_RESP_MODULE_STATUS_CURRENTFAULT: + NL_SET_ERR_MSG_MOD(extack, "Transceiver module disabled due to current fault"); + break; + default: + NL_SET_ERR_MSG_MOD(extack, "Unknown error"); + break; + } + return -EINVAL; +} + +static int bnxt_get_module_eeprom_by_page(struct net_device *dev, + const struct ethtool_module_eeprom *page_data, + struct netlink_ext_ack *extack) +{ + struct bnxt *bp = netdev_priv(dev); + int rc; + + rc = bnxt_get_module_status(bp, extack); + if (rc) + return rc; + + if (bp->hwrm_spec_code < 0x10202) { + NL_SET_ERR_MSG_MOD(extack, "Firmware version too old"); + return -EINVAL; + } + + if (page_data->bank && !(bp->phy_flags & BNXT_PHY_FL_BANK_SEL)) { + NL_SET_ERR_MSG_MOD(extack, "Firmware not capable for bank selection"); + return -EINVAL; + } + + rc = bnxt_read_sfp_module_eeprom_info(bp, page_data->i2c_address << 1, + page_data->page, page_data->bank, + page_data->offset, + page_data->length, + page_data->data); + if (rc) { + NL_SET_ERR_MSG_MOD(extack, "Module`s eeprom read failed"); + return rc; + } + return page_data->length; +} + static int bnxt_nway_reset(struct net_device *dev) { int rc = 0; @@ -4071,6 +4133,7 @@ const struct ethtool_ops bnxt_ethtool_ops = { .set_eee = bnxt_set_eee, .get_module_info = bnxt_get_module_info, .get_module_eeprom = bnxt_get_module_eeprom, + .get_module_eeprom_by_page = bnxt_get_module_eeprom_by_page, .nway_reset = bnxt_nway_reset, .set_phys_id = bnxt_set_phys_id, .self_test = bnxt_self_test, From 45034224623a5634e4ccc57b497ac825c260170f Mon Sep 17 00:00:00 2001 From: Vikas Gupta Date: Fri, 21 Oct 2022 02:37:23 -0400 Subject: [PATCH 0301/2002] bnxt_en: check and resize NVRAM UPDATE entry before flashing Resize of the UPDATE entry is required if the image to be flashed is larger than the available space. Add this step, otherwise flashing larger firmware images by ethtool or devlink may fail. Reviewed-by: Andy Gospodarek Signed-off-by: Vikas Gupta Signed-off-by: Michael Chan Signed-off-by: Jakub Kicinski --- .../net/ethernet/broadcom/bnxt/bnxt_ethtool.c | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c index 44b715c4e19e..cc89e5eabcb9 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c @@ -2514,6 +2514,7 @@ static int bnxt_flash_firmware_from_file(struct net_device *dev, #define MSG_INTERNAL_ERR "PKG install error : Internal error" #define MSG_NO_PKG_UPDATE_AREA_ERR "PKG update area not created in nvram" #define MSG_NO_SPACE_ERR "PKG insufficient update area in nvram" +#define MSG_RESIZE_UPDATE_ERR "Resize UPDATE entry error" #define MSG_ANTI_ROLLBACK_ERR "HWRM_NVM_INSTALL_UPDATE failure due to Anti-rollback detected" #define MSG_GENERIC_FAILURE_ERR "HWRM_NVM_INSTALL_UPDATE failure" @@ -2564,6 +2565,32 @@ static int nvm_update_err_to_stderr(struct net_device *dev, u8 result, #define BNXT_NVM_MORE_FLAG (cpu_to_le16(NVM_MODIFY_REQ_FLAGS_BATCH_MODE)) #define BNXT_NVM_LAST_FLAG (cpu_to_le16(NVM_MODIFY_REQ_FLAGS_BATCH_LAST)) +static int bnxt_resize_update_entry(struct net_device *dev, size_t fw_size, + struct netlink_ext_ack *extack) +{ + u32 item_len; + int rc; + + rc = bnxt_find_nvram_item(dev, BNX_DIR_TYPE_UPDATE, + BNX_DIR_ORDINAL_FIRST, BNX_DIR_EXT_NONE, NULL, + &item_len, NULL); + if (rc) { + BNXT_NVM_ERR_MSG(dev, extack, MSG_NO_PKG_UPDATE_AREA_ERR); + return rc; + } + + if (fw_size > item_len) { + rc = bnxt_flash_nvram(dev, BNX_DIR_TYPE_UPDATE, + BNX_DIR_ORDINAL_FIRST, 0, 1, + round_up(fw_size, 4096), NULL, 0); + if (rc) { + BNXT_NVM_ERR_MSG(dev, extack, MSG_RESIZE_UPDATE_ERR); + return rc; + } + } + return 0; +} + int bnxt_flash_package_from_fw_obj(struct net_device *dev, const struct firmware *fw, u32 install_type, struct netlink_ext_ack *extack) { @@ -2580,6 +2607,11 @@ int bnxt_flash_package_from_fw_obj(struct net_device *dev, const struct firmware u16 index; int rc; + /* resize before flashing larger image than available space */ + rc = bnxt_resize_update_entry(dev, fw->size, extack); + if (rc) + return rc; + bnxt_hwrm_fw_set_time(bp); rc = hwrm_req_init(bp, modify, HWRM_NVM_MODIFY); From a272bcb9e5ef0cce6bbfe52e52c9fc69bb90e1ca Mon Sep 17 00:00:00 2001 From: "Russell King (Oracle)" Date: Fri, 21 Oct 2022 16:09:38 +0100 Subject: [PATCH 0302/2002] dt-bindings: net: sff,sfp: update binding Add a minimum and default for the maximum-power-milliwatt option; module power levels were originally up to 1W, so this is the default and the minimum power level we can have for a functional SFP cage. Signed-off-by: Russell King (Oracle) Reviewed-by: Rob Herring Signed-off-by: Jakub Kicinski --- Documentation/devicetree/bindings/net/sff,sfp.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/net/sff,sfp.yaml b/Documentation/devicetree/bindings/net/sff,sfp.yaml index 06c66ab81c01..231c4d75e4b1 100644 --- a/Documentation/devicetree/bindings/net/sff,sfp.yaml +++ b/Documentation/devicetree/bindings/net/sff,sfp.yaml @@ -22,7 +22,8 @@ properties: phandle of an I2C bus controller for the SFP two wire serial maximum-power-milliwatt: - maxItems: 1 + minimum: 1000 + default: 1000 description: Maximum module power consumption Specifies the maximum power consumption allowable by a module in the slot, in milli-Watts. Presently, modules can From 02eaf5a79100468425fae2d0baf144b6068fc4cc Mon Sep 17 00:00:00 2001 From: "Russell King (Oracle)" Date: Fri, 21 Oct 2022 16:09:43 +0100 Subject: [PATCH 0303/2002] net: sfp: check firmware provided max power Check that the firmware provided maximum power is at least 1W, which is the minimum power level for any SFP module. Now that we enforce the minimum of 1W, we can exit early from sfp_module_parse_power() if the module power is 1W or less. Signed-off-by: Russell King (Oracle) Reviewed-by: Andrew Lunn Signed-off-by: Jakub Kicinski --- drivers/net/phy/sfp.c | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/drivers/net/phy/sfp.c b/drivers/net/phy/sfp.c index 40c9a64c5e30..f7ad4d5d9041 100644 --- a/drivers/net/phy/sfp.c +++ b/drivers/net/phy/sfp.c @@ -1766,6 +1766,12 @@ static int sfp_module_parse_power(struct sfp *sfp) if (sfp->id.ext.options & cpu_to_be16(SFP_OPTIONS_HIGH_POWER_LEVEL)) power_mW = 2000; + /* Power level 1 modules (max. 1W) are always supported. */ + if (power_mW <= 1000) { + sfp->module_power_mW = power_mW; + return 0; + } + supports_a2 = sfp->id.ext.sff8472_compliance != SFP_SFF8472_COMPLIANCE_NONE || sfp->id.ext.diagmon & SFP_DIAGMON_DDM; @@ -1789,12 +1795,6 @@ static int sfp_module_parse_power(struct sfp *sfp) } } - if (power_mW <= 1000) { - /* Modules below 1W do not require a power change sequence */ - sfp->module_power_mW = power_mW; - return 0; - } - if (!supports_a2) { /* The module power level is below the host maximum and the * module appears not to implement bus address 0xa2, so assume @@ -2729,8 +2729,12 @@ static int sfp_probe(struct platform_device *pdev) device_property_read_u32(&pdev->dev, "maximum-power-milliwatt", &sfp->max_power_mW); - if (!sfp->max_power_mW) + if (sfp->max_power_mW < 1000) { + if (sfp->max_power_mW) + dev_warn(sfp->dev, + "Firmware bug: host maximum power should be at least 1W\n"); sfp->max_power_mW = 1000; + } dev_info(sfp->dev, "Host maximum power %u.%uW\n", sfp->max_power_mW / 1000, (sfp->max_power_mW / 100) % 10); From 18cc659e95ab1661254bde815fc9345246d98906 Mon Sep 17 00:00:00 2001 From: "Russell King (Oracle)" Date: Fri, 21 Oct 2022 16:09:49 +0100 Subject: [PATCH 0304/2002] net: sfp: ignore power level 2 prior to SFF-8472 Rev 10.2 Power level 2 was introduced by SFF-8472 revision 10.2. Ignore the power declaration bit for modules that are not compliant with at least this revision. This should remove any spurious indication of 1.5W modules. Signed-off-by: Russell King (Oracle) Reviewed-by: Andrew Lunn Signed-off-by: Jakub Kicinski --- drivers/net/phy/sfp.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/phy/sfp.c b/drivers/net/phy/sfp.c index f7ad4d5d9041..a7635b02524a 100644 --- a/drivers/net/phy/sfp.c +++ b/drivers/net/phy/sfp.c @@ -1761,7 +1761,8 @@ static int sfp_module_parse_power(struct sfp *sfp) u32 power_mW = 1000; bool supports_a2; - if (sfp->id.ext.options & cpu_to_be16(SFP_OPTIONS_POWER_DECL)) + if (sfp->id.ext.sff8472_compliance >= SFP_SFF8472_COMPLIANCE_REV10_2 && + sfp->id.ext.options & cpu_to_be16(SFP_OPTIONS_POWER_DECL)) power_mW = 1500; if (sfp->id.ext.options & cpu_to_be16(SFP_OPTIONS_HIGH_POWER_LEVEL)) power_mW = 2000; From f8810ca7582933e436e6c987808bbcc14f5543df Mon Sep 17 00:00:00 2001 From: "Russell King (Oracle)" Date: Fri, 21 Oct 2022 16:09:54 +0100 Subject: [PATCH 0305/2002] net: sfp: ignore power level 3 prior to SFF-8472 Rev 11.4 Power level 3 was included in SFF-8472 revision 11.9, but this does not have a compliance code. Use revision 11.4 as the minimum compliance level instead. This should avoid any spurious indication of 2W modules. Signed-off-by: Russell King (Oracle) Reviewed-by: Andrew Lunn Signed-off-by: Jakub Kicinski --- drivers/net/phy/sfp.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/net/phy/sfp.c b/drivers/net/phy/sfp.c index a7635b02524a..af676e28ba6a 100644 --- a/drivers/net/phy/sfp.c +++ b/drivers/net/phy/sfp.c @@ -1764,7 +1764,9 @@ static int sfp_module_parse_power(struct sfp *sfp) if (sfp->id.ext.sff8472_compliance >= SFP_SFF8472_COMPLIANCE_REV10_2 && sfp->id.ext.options & cpu_to_be16(SFP_OPTIONS_POWER_DECL)) power_mW = 1500; - if (sfp->id.ext.options & cpu_to_be16(SFP_OPTIONS_HIGH_POWER_LEVEL)) + /* Added in Rev 11.9, but there is no compliance code for this */ + if (sfp->id.ext.sff8472_compliance >= SFP_SFF8472_COMPLIANCE_REV11_4 && + sfp->id.ext.options & cpu_to_be16(SFP_OPTIONS_HIGH_POWER_LEVEL)) power_mW = 2000; /* Power level 1 modules (max. 1W) are always supported. */ From 398900498485fa9465386454681763d81efaad25 Mon Sep 17 00:00:00 2001 From: "Russell King (Oracle)" Date: Fri, 21 Oct 2022 16:09:59 +0100 Subject: [PATCH 0306/2002] net: sfp: provide a definition for the power level select bit Provide a named definition for the power level select bit in the extended status register, rather than using BIT(0) in the code. Signed-off-by: Russell King (Oracle) Reviewed-by: Andrew Lunn Signed-off-by: Jakub Kicinski --- drivers/net/phy/sfp.c | 6 +++--- include/linux/sfp.h | 2 ++ 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/net/phy/sfp.c b/drivers/net/phy/sfp.c index af676e28ba6a..16bce0ea68d9 100644 --- a/drivers/net/phy/sfp.c +++ b/drivers/net/phy/sfp.c @@ -1837,13 +1837,13 @@ static int sfp_sm_mod_hpower(struct sfp *sfp, bool enable) * all bytes 0xff) at 0x51 but does not accept writes. In any case, * if the bit is already set, we're already in high power mode. */ - if (!!(val & BIT(0)) == enable) + if (!!(val & SFP_EXT_STATUS_PWRLVL_SELECT) == enable) return 0; if (enable) - val |= BIT(0); + val |= SFP_EXT_STATUS_PWRLVL_SELECT; else - val &= ~BIT(0); + val &= ~SFP_EXT_STATUS_PWRLVL_SELECT; err = sfp_write(sfp, true, SFP_EXT_STATUS, &val, sizeof(val)); if (err != sizeof(val)) { diff --git a/include/linux/sfp.h b/include/linux/sfp.h index d1f343853b6c..01ae9f1dd2ad 100644 --- a/include/linux/sfp.h +++ b/include/linux/sfp.h @@ -489,6 +489,8 @@ enum { SFP_WARN1_RXPWR_LOW = BIT(6), SFP_EXT_STATUS = 0x76, + SFP_EXT_STATUS_PWRLVL_SELECT = BIT(0), + SFP_VSL = 0x78, SFP_PAGE = 0x7f, }; From a3c536fc75803b298fd6ba75af33eecf43d31785 Mon Sep 17 00:00:00 2001 From: "Russell King (Oracle)" Date: Fri, 21 Oct 2022 16:10:04 +0100 Subject: [PATCH 0307/2002] net: sfp: add sfp_modify_u8() helper Add a helper to modify bits in a single byte in memory space, and use it when updating the soft tx-disable flag in the module. Signed-off-by: Russell King (Oracle) Reviewed-by: Andrew Lunn Signed-off-by: Jakub Kicinski --- drivers/net/phy/sfp.c | 31 ++++++++++++++++++++++--------- 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/drivers/net/phy/sfp.c b/drivers/net/phy/sfp.c index 16bce0ea68d9..921bbedd9b22 100644 --- a/drivers/net/phy/sfp.c +++ b/drivers/net/phy/sfp.c @@ -608,6 +608,22 @@ static int sfp_write(struct sfp *sfp, bool a2, u8 addr, void *buf, size_t len) return sfp->write(sfp, a2, addr, buf, len); } +static int sfp_modify_u8(struct sfp *sfp, bool a2, u8 addr, u8 mask, u8 val) +{ + int ret; + u8 old, v; + + ret = sfp_read(sfp, a2, addr, &old, sizeof(old)); + if (ret != sizeof(old)) + return ret; + + v = (old & ~mask) | (val & mask); + if (v == old) + return sizeof(v); + + return sfp_write(sfp, a2, addr, &v, sizeof(v)); +} + static unsigned int sfp_soft_get_state(struct sfp *sfp) { unsigned int state = 0; @@ -633,17 +649,14 @@ static unsigned int sfp_soft_get_state(struct sfp *sfp) static void sfp_soft_set_state(struct sfp *sfp, unsigned int state) { - u8 status; + u8 mask = SFP_STATUS_TX_DISABLE_FORCE; + u8 val = 0; - if (sfp_read(sfp, true, SFP_STATUS, &status, sizeof(status)) == - sizeof(status)) { - if (state & SFP_F_TX_DISABLE) - status |= SFP_STATUS_TX_DISABLE_FORCE; - else - status &= ~SFP_STATUS_TX_DISABLE_FORCE; + if (state & SFP_F_TX_DISABLE) + val |= SFP_STATUS_TX_DISABLE_FORCE; - sfp_write(sfp, true, SFP_STATUS, &status, sizeof(status)); - } + + sfp_modify_u8(sfp, true, SFP_STATUS, mask, val); } static void sfp_soft_start_poll(struct sfp *sfp) From bd1432f68ddc28bf3ed6e86b16ed29c0b406f91f Mon Sep 17 00:00:00 2001 From: "Russell King (Oracle)" Date: Fri, 21 Oct 2022 16:10:09 +0100 Subject: [PATCH 0308/2002] net: sfp: get rid of DM7052 hack when enabling high power Since we no longer mis-detect high-power mode with the DM7052 module, we no longer need the hack in sfp_module_enable_high_power(), and can now switch this to use sfp_modify_u8(). Signed-off-by: Russell King (Oracle) Reviewed-by: Andrew Lunn Signed-off-by: Jakub Kicinski --- drivers/net/phy/sfp.c | 29 ++++++----------------------- 1 file changed, 6 insertions(+), 23 deletions(-) diff --git a/drivers/net/phy/sfp.c b/drivers/net/phy/sfp.c index 921bbedd9b22..39fd1811375c 100644 --- a/drivers/net/phy/sfp.c +++ b/drivers/net/phy/sfp.c @@ -1837,31 +1837,14 @@ static int sfp_module_parse_power(struct sfp *sfp) static int sfp_sm_mod_hpower(struct sfp *sfp, bool enable) { - u8 val; int err; - err = sfp_read(sfp, true, SFP_EXT_STATUS, &val, sizeof(val)); - if (err != sizeof(val)) { - dev_err(sfp->dev, "Failed to read EEPROM: %pe\n", ERR_PTR(err)); - return -EAGAIN; - } - - /* DM7052 reports as a high power module, responds to reads (with - * all bytes 0xff) at 0x51 but does not accept writes. In any case, - * if the bit is already set, we're already in high power mode. - */ - if (!!(val & SFP_EXT_STATUS_PWRLVL_SELECT) == enable) - return 0; - - if (enable) - val |= SFP_EXT_STATUS_PWRLVL_SELECT; - else - val &= ~SFP_EXT_STATUS_PWRLVL_SELECT; - - err = sfp_write(sfp, true, SFP_EXT_STATUS, &val, sizeof(val)); - if (err != sizeof(val)) { - dev_err(sfp->dev, "Failed to write EEPROM: %pe\n", - ERR_PTR(err)); + err = sfp_modify_u8(sfp, true, SFP_EXT_STATUS, + SFP_EXT_STATUS_PWRLVL_SELECT, + enable ? SFP_EXT_STATUS_PWRLVL_SELECT : 0); + if (err != sizeof(u8)) { + dev_err(sfp->dev, "failed to %sable high power: %pe\n", + enable ? "en" : "dis", ERR_PTR(err)); return -EAGAIN; } From f7fe25a6f00522791cce38bac552d295339d0c79 Mon Sep 17 00:00:00 2001 From: Antony Antony Date: Fri, 21 Oct 2022 15:42:01 +0200 Subject: [PATCH 0309/2002] xfrm: update x->lastused for every packet x->lastused was only updated for outgoing mobile IPv6 packet. With this fix update it for every, in and out, packet. This is useful to check if the a SA is still in use, or when was the last time an SA was used. lastused time of in SA can used to check IPsec path is functional. Signed-off-by: Antony Antony Signed-off-by: Steffen Klassert --- net/xfrm/xfrm_input.c | 1 + net/xfrm/xfrm_output.c | 3 +-- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/net/xfrm/xfrm_input.c b/net/xfrm/xfrm_input.c index 97074f6f2bde..c06e54a10540 100644 --- a/net/xfrm/xfrm_input.c +++ b/net/xfrm/xfrm_input.c @@ -671,6 +671,7 @@ resume: x->curlft.bytes += skb->len; x->curlft.packets++; + x->lastused = ktime_get_real_seconds(); spin_unlock(&x->lock); diff --git a/net/xfrm/xfrm_output.c b/net/xfrm/xfrm_output.c index 9a5e79a38c67..78cb8d0a6a18 100644 --- a/net/xfrm/xfrm_output.c +++ b/net/xfrm/xfrm_output.c @@ -209,8 +209,6 @@ static int xfrm6_ro_output(struct xfrm_state *x, struct sk_buff *skb) __skb_pull(skb, hdr_len); memmove(ipv6_hdr(skb), iph, hdr_len); - x->lastused = ktime_get_real_seconds(); - return 0; #else WARN_ON_ONCE(1); @@ -534,6 +532,7 @@ static int xfrm_output_one(struct sk_buff *skb, int err) x->curlft.bytes += skb->len; x->curlft.packets++; + x->lastused = ktime_get_real_seconds(); spin_unlock_bh(&x->lock); From 4a6a676f8c16ec17d2f8d69ce3b5d680277ed0d2 Mon Sep 17 00:00:00 2001 From: Amritha Nambiar Date: Fri, 21 Oct 2022 00:58:39 -0700 Subject: [PATCH 0310/2002] act_skbedit: skbedit queue mapping for receive queue Add support for skbedit queue mapping action on receive side. This is supported only in hardware, so the skip_sw flag is enforced. This enables offloading filters for receive queue selection in the hardware using the skbedit action. Traffic arrives on the Rx queue requested in the skbedit action parameter. A new tc action flag TCA_ACT_FLAGS_AT_INGRESS is introduced to identify the traffic direction the action queue_mapping is requested on during filter addition. This is used to disallow offloading the skbedit queue mapping action on transmit side. Example: $tc filter add dev $IFACE ingress protocol ip flower dst_ip $DST_IP\ action skbedit queue_mapping $rxq_id skip_sw Reviewed-by: Sridhar Samudrala Signed-off-by: Amritha Nambiar Signed-off-by: Paolo Abeni --- include/net/act_api.h | 1 + include/net/flow_offload.h | 2 ++ include/net/tc_act/tc_skbedit.h | 29 +++++++++++++++++++++++++++++ net/sched/act_skbedit.c | 14 ++++++++++++-- net/sched/cls_api.c | 7 +++++++ 5 files changed, 51 insertions(+), 2 deletions(-) diff --git a/include/net/act_api.h b/include/net/act_api.h index 61f2ceb3939e..c94ea1a306e0 100644 --- a/include/net/act_api.h +++ b/include/net/act_api.h @@ -67,6 +67,7 @@ struct tc_action { #define TCA_ACT_FLAGS_BIND (1U << (TCA_ACT_FLAGS_USER_BITS + 1)) #define TCA_ACT_FLAGS_REPLACE (1U << (TCA_ACT_FLAGS_USER_BITS + 2)) #define TCA_ACT_FLAGS_NO_RTNL (1U << (TCA_ACT_FLAGS_USER_BITS + 3)) +#define TCA_ACT_FLAGS_AT_INGRESS (1U << (TCA_ACT_FLAGS_USER_BITS + 4)) /* Update lastuse only if needed, to avoid dirtying a cache line. * We use a temp variable to avoid fetching jiffies twice. diff --git a/include/net/flow_offload.h b/include/net/flow_offload.h index e343f9f8363e..7a60bc6d72c9 100644 --- a/include/net/flow_offload.h +++ b/include/net/flow_offload.h @@ -155,6 +155,7 @@ enum flow_action_id { FLOW_ACTION_MARK, FLOW_ACTION_PTYPE, FLOW_ACTION_PRIORITY, + FLOW_ACTION_RX_QUEUE_MAPPING, FLOW_ACTION_WAKE, FLOW_ACTION_QUEUE, FLOW_ACTION_SAMPLE, @@ -247,6 +248,7 @@ struct flow_action_entry { u32 csum_flags; /* FLOW_ACTION_CSUM */ u32 mark; /* FLOW_ACTION_MARK */ u16 ptype; /* FLOW_ACTION_PTYPE */ + u16 rx_queue; /* FLOW_ACTION_RX_QUEUE_MAPPING */ u32 priority; /* FLOW_ACTION_PRIORITY */ struct { /* FLOW_ACTION_QUEUE */ u32 ctx; diff --git a/include/net/tc_act/tc_skbedit.h b/include/net/tc_act/tc_skbedit.h index dc1079f28e13..9649600fb3dc 100644 --- a/include/net/tc_act/tc_skbedit.h +++ b/include/net/tc_act/tc_skbedit.h @@ -95,12 +95,41 @@ static inline u32 tcf_skbedit_priority(const struct tc_action *a) return priority; } +static inline u16 tcf_skbedit_rx_queue_mapping(const struct tc_action *a) +{ + u16 rx_queue; + + rcu_read_lock(); + rx_queue = rcu_dereference(to_skbedit(a)->params)->queue_mapping; + rcu_read_unlock(); + + return rx_queue; +} + /* Return true iff action is queue_mapping */ static inline bool is_tcf_skbedit_queue_mapping(const struct tc_action *a) { return is_tcf_skbedit_with_flag(a, SKBEDIT_F_QUEUE_MAPPING); } +/* Return true if action is on ingress traffic */ +static inline bool is_tcf_skbedit_ingress(u32 flags) +{ + return flags & TCA_ACT_FLAGS_AT_INGRESS; +} + +static inline bool is_tcf_skbedit_tx_queue_mapping(const struct tc_action *a) +{ + return is_tcf_skbedit_queue_mapping(a) && + !is_tcf_skbedit_ingress(a->tcfa_flags); +} + +static inline bool is_tcf_skbedit_rx_queue_mapping(const struct tc_action *a) +{ + return is_tcf_skbedit_queue_mapping(a) && + is_tcf_skbedit_ingress(a->tcfa_flags); +} + /* Return true iff action is inheritdsfield */ static inline bool is_tcf_skbedit_inheritdsfield(const struct tc_action *a) { diff --git a/net/sched/act_skbedit.c b/net/sched/act_skbedit.c index 7f598784fd30..1710780c908a 100644 --- a/net/sched/act_skbedit.c +++ b/net/sched/act_skbedit.c @@ -148,6 +148,11 @@ static int tcf_skbedit_init(struct net *net, struct nlattr *nla, } if (tb[TCA_SKBEDIT_QUEUE_MAPPING] != NULL) { + if (is_tcf_skbedit_ingress(act_flags) && + !(act_flags & TCA_ACT_FLAGS_SKIP_SW)) { + NL_SET_ERR_MSG_MOD(extack, "\"queue_mapping\" option on receive side is hardware only, use skip_sw"); + return -EOPNOTSUPP; + } flags |= SKBEDIT_F_QUEUE_MAPPING; queue_mapping = nla_data(tb[TCA_SKBEDIT_QUEUE_MAPPING]); } @@ -374,9 +379,12 @@ static int tcf_skbedit_offload_act_setup(struct tc_action *act, void *entry_data } else if (is_tcf_skbedit_priority(act)) { entry->id = FLOW_ACTION_PRIORITY; entry->priority = tcf_skbedit_priority(act); - } else if (is_tcf_skbedit_queue_mapping(act)) { - NL_SET_ERR_MSG_MOD(extack, "Offload not supported when \"queue_mapping\" option is used"); + } else if (is_tcf_skbedit_tx_queue_mapping(act)) { + NL_SET_ERR_MSG_MOD(extack, "Offload not supported when \"queue_mapping\" option is used on transmit side"); return -EOPNOTSUPP; + } else if (is_tcf_skbedit_rx_queue_mapping(act)) { + entry->id = FLOW_ACTION_RX_QUEUE_MAPPING; + entry->rx_queue = tcf_skbedit_rx_queue_mapping(act); } else if (is_tcf_skbedit_inheritdsfield(act)) { NL_SET_ERR_MSG_MOD(extack, "Offload not supported when \"inheritdsfield\" option is used"); return -EOPNOTSUPP; @@ -394,6 +402,8 @@ static int tcf_skbedit_offload_act_setup(struct tc_action *act, void *entry_data fl_action->id = FLOW_ACTION_PTYPE; else if (is_tcf_skbedit_priority(act)) fl_action->id = FLOW_ACTION_PRIORITY; + else if (is_tcf_skbedit_rx_queue_mapping(act)) + fl_action->id = FLOW_ACTION_RX_QUEUE_MAPPING; else return -EOPNOTSUPP; } diff --git a/net/sched/cls_api.c b/net/sched/cls_api.c index 50566db45949..23d1cfa4f58c 100644 --- a/net/sched/cls_api.c +++ b/net/sched/cls_api.c @@ -1953,6 +1953,11 @@ static void tfilter_put(struct tcf_proto *tp, void *fh) tp->ops->put(tp, fh); } +static bool is_qdisc_ingress(__u32 classid) +{ + return (TC_H_MIN(classid) == TC_H_MIN(TC_H_MIN_INGRESS)); +} + static int tc_new_tfilter(struct sk_buff *skb, struct nlmsghdr *n, struct netlink_ext_ack *extack) { @@ -2144,6 +2149,8 @@ replay: flags |= TCA_ACT_FLAGS_REPLACE; if (!rtnl_held) flags |= TCA_ACT_FLAGS_NO_RTNL; + if (is_qdisc_ingress(parent)) + flags |= TCA_ACT_FLAGS_AT_INGRESS; err = tp->ops->change(net, skb, tp, cl, t->tcm_handle, tca, &fh, flags, extack); if (err == 0) { From 143b86f346c709d6fed661aee36cea70ef874815 Mon Sep 17 00:00:00 2001 From: Amritha Nambiar Date: Fri, 21 Oct 2022 00:58:45 -0700 Subject: [PATCH 0311/2002] ice: Enable RX queue selection using skbedit action This patch uses TC skbedit queue_mapping action to support forwarding packets to a device queue. Such filters with action forward to queue will be the highest priority switch filter in HW. Example: $ tc filter add dev ens4f0 protocol ip ingress flower\ dst_ip 192.168.1.12 ip_proto tcp dst_port 5001\ action skbedit queue_mapping 5 skip_sw The above command adds an ingress filter, incoming packets qualifying the match will be accepted into queue 5. The queue number is in decimal format. Refactored ice_add_tc_flower_adv_fltr() to consolidate code with action FWD_TO_VSI and FWD_TO QUEUE. Reviewed-by: Sridhar Samudrala Reviewed-by: Vinicius Costa Gomes Signed-off-by: Amritha Nambiar Signed-off-by: Paolo Abeni --- drivers/net/ethernet/intel/ice/ice.h | 15 + drivers/net/ethernet/intel/ice/ice_main.c | 2 +- drivers/net/ethernet/intel/ice/ice_tc_lib.c | 353 ++++++++++++++------ drivers/net/ethernet/intel/ice/ice_tc_lib.h | 40 ++- 4 files changed, 300 insertions(+), 110 deletions(-) diff --git a/drivers/net/ethernet/intel/ice/ice.h b/drivers/net/ethernet/intel/ice/ice.h index 001500afc4a6..d75c8d11f867 100644 --- a/drivers/net/ethernet/intel/ice/ice.h +++ b/drivers/net/ethernet/intel/ice/ice.h @@ -137,6 +137,21 @@ */ #define ICE_BW_KBPS_DIVISOR 125 +/* Default recipes have priority 4 and below, hence priority values between 5..7 + * can be used as filter priority for advanced switch filter (advanced switch + * filters need new recipe to be created for specified extraction sequence + * because default recipe extraction sequence does not represent custom + * extraction) + */ +#define ICE_SWITCH_FLTR_PRIO_QUEUE 7 +/* prio 6 is reserved for future use (e.g. switch filter with L3 fields + + * (Optional: IP TOS/TTL) + L4 fields + (optionally: TCP fields such as + * SYN/FIN/RST)) + */ +#define ICE_SWITCH_FLTR_PRIO_RSVD 6 +#define ICE_SWITCH_FLTR_PRIO_VSI 5 +#define ICE_SWITCH_FLTR_PRIO_QGRP ICE_SWITCH_FLTR_PRIO_VSI + /* Macro for each VSI in a PF */ #define ice_for_each_vsi(pf, i) \ for ((i) = 0; (i) < (pf)->num_alloc_vsi; (i)++) diff --git a/drivers/net/ethernet/intel/ice/ice_main.c b/drivers/net/ethernet/intel/ice/ice_main.c index 0f6718719453..df65e829ea33 100644 --- a/drivers/net/ethernet/intel/ice/ice_main.c +++ b/drivers/net/ethernet/intel/ice/ice_main.c @@ -8283,7 +8283,7 @@ static void ice_rem_all_chnl_fltrs(struct ice_pf *pf) rule.rid = fltr->rid; rule.rule_id = fltr->rule_id; - rule.vsi_handle = fltr->dest_id; + rule.vsi_handle = fltr->dest_vsi_handle; status = ice_rem_adv_rule_by_id(&pf->hw, &rule); if (status) { if (status == -ENOENT) diff --git a/drivers/net/ethernet/intel/ice/ice_tc_lib.c b/drivers/net/ethernet/intel/ice/ice_tc_lib.c index f68c555be4e9..faba0f857cd9 100644 --- a/drivers/net/ethernet/intel/ice/ice_tc_lib.c +++ b/drivers/net/ethernet/intel/ice/ice_tc_lib.c @@ -724,13 +724,123 @@ ice_eswitch_add_tc_fltr(struct ice_vsi *vsi, struct ice_tc_flower_fltr *fltr) */ fltr->rid = rule_added.rid; fltr->rule_id = rule_added.rule_id; - fltr->dest_id = rule_added.vsi_handle; + fltr->dest_vsi_handle = rule_added.vsi_handle; exit: kfree(list); return ret; } +/** + * ice_locate_vsi_using_queue - locate VSI using queue (forward to queue action) + * @vsi: Pointer to VSI + * @tc_fltr: Pointer to tc_flower_filter + * + * Locate the VSI using specified queue. When ADQ is not enabled, always + * return input VSI, otherwise locate corresponding VSI based on per channel + * offset and qcount + */ +static struct ice_vsi * +ice_locate_vsi_using_queue(struct ice_vsi *vsi, + struct ice_tc_flower_fltr *tc_fltr) +{ + int num_tc, tc, queue; + + /* if ADQ is not active, passed VSI is the candidate VSI */ + if (!ice_is_adq_active(vsi->back)) + return vsi; + + /* Locate the VSI (it could still be main PF VSI or CHNL_VSI depending + * upon queue number) + */ + num_tc = vsi->mqprio_qopt.qopt.num_tc; + queue = tc_fltr->action.fwd.q.queue; + + for (tc = 0; tc < num_tc; tc++) { + int qcount = vsi->mqprio_qopt.qopt.count[tc]; + int offset = vsi->mqprio_qopt.qopt.offset[tc]; + + if (queue >= offset && queue < offset + qcount) { + /* for non-ADQ TCs, passed VSI is the candidate VSI */ + if (tc < ICE_CHNL_START_TC) + return vsi; + else + return vsi->tc_map_vsi[tc]; + } + } + return NULL; +} + +static struct ice_rx_ring * +ice_locate_rx_ring_using_queue(struct ice_vsi *vsi, + struct ice_tc_flower_fltr *tc_fltr) +{ + u16 queue = tc_fltr->action.fwd.q.queue; + + return queue < vsi->num_rxq ? vsi->rx_rings[queue] : NULL; +} + +/** + * ice_tc_forward_action - Determine destination VSI and queue for the action + * @vsi: Pointer to VSI + * @tc_fltr: Pointer to TC flower filter structure + * + * Validates the tc forward action and determines the destination VSI and queue + * for the forward action. + */ +static struct ice_vsi * +ice_tc_forward_action(struct ice_vsi *vsi, struct ice_tc_flower_fltr *tc_fltr) +{ + struct ice_rx_ring *ring = NULL; + struct ice_vsi *ch_vsi = NULL; + struct ice_pf *pf = vsi->back; + struct device *dev; + u32 tc_class; + + dev = ice_pf_to_dev(pf); + + /* Get the destination VSI and/or destination queue and validate them */ + switch (tc_fltr->action.fltr_act) { + case ICE_FWD_TO_VSI: + tc_class = tc_fltr->action.fwd.tc.tc_class; + /* Select the destination VSI */ + if (tc_class < ICE_CHNL_START_TC) { + NL_SET_ERR_MSG_MOD(tc_fltr->extack, + "Unable to add filter because of unsupported destination"); + return ERR_PTR(-EOPNOTSUPP); + } + /* Locate ADQ VSI depending on hw_tc number */ + ch_vsi = vsi->tc_map_vsi[tc_class]; + break; + case ICE_FWD_TO_Q: + /* Locate the Rx queue */ + ring = ice_locate_rx_ring_using_queue(vsi, tc_fltr); + if (!ring) { + dev_err(dev, + "Unable to locate Rx queue for action fwd_to_queue: %u\n", + tc_fltr->action.fwd.q.queue); + return ERR_PTR(-EINVAL); + } + /* Determine destination VSI even though the action is + * FWD_TO_QUEUE, because QUEUE is associated with VSI + */ + ch_vsi = tc_fltr->dest_vsi; + break; + default: + dev_err(dev, + "Unable to add filter because of unsupported action %u (supported actions: fwd to tc, fwd to queue)\n", + tc_fltr->action.fltr_act); + return ERR_PTR(-EINVAL); + } + /* Must have valid ch_vsi (it could be main VSI or ADQ VSI) */ + if (!ch_vsi) { + dev_err(dev, + "Unable to add filter because specified destination VSI doesn't exist\n"); + return ERR_PTR(-EINVAL); + } + return ch_vsi; +} + /** * ice_add_tc_flower_adv_fltr - add appropriate filter rules * @vsi: Pointer to VSI @@ -772,11 +882,10 @@ ice_add_tc_flower_adv_fltr(struct ice_vsi *vsi, return -EOPNOTSUPP; } - /* get the channel (aka ADQ VSI) */ - if (tc_fltr->dest_vsi) - ch_vsi = tc_fltr->dest_vsi; - else - ch_vsi = vsi->tc_map_vsi[tc_fltr->action.tc_class]; + /* validate forwarding action VSI and queue */ + ch_vsi = ice_tc_forward_action(vsi, tc_fltr); + if (IS_ERR(ch_vsi)) + return PTR_ERR(ch_vsi); lkups_cnt = ice_tc_count_lkups(flags, headers, tc_fltr); list = kcalloc(lkups_cnt, sizeof(*list), GFP_ATOMIC); @@ -790,30 +899,40 @@ ice_add_tc_flower_adv_fltr(struct ice_vsi *vsi, } rule_info.sw_act.fltr_act = tc_fltr->action.fltr_act; - if (tc_fltr->action.tc_class >= ICE_CHNL_START_TC) { - if (!ch_vsi) { - NL_SET_ERR_MSG_MOD(tc_fltr->extack, "Unable to add filter because specified destination doesn't exist"); - ret = -EINVAL; - goto exit; - } + /* specify the cookie as filter_rule_id */ + rule_info.fltr_rule_id = tc_fltr->cookie; - rule_info.sw_act.fltr_act = ICE_FWD_TO_VSI; + switch (tc_fltr->action.fltr_act) { + case ICE_FWD_TO_VSI: rule_info.sw_act.vsi_handle = ch_vsi->idx; - rule_info.priority = 7; + rule_info.priority = ICE_SWITCH_FLTR_PRIO_VSI; rule_info.sw_act.src = hw->pf_id; rule_info.rx = true; dev_dbg(dev, "add switch rule for TC:%u vsi_idx:%u, lkups_cnt:%u\n", - tc_fltr->action.tc_class, + tc_fltr->action.fwd.tc.tc_class, rule_info.sw_act.vsi_handle, lkups_cnt); - } else { + break; + case ICE_FWD_TO_Q: + /* HW queue number in global space */ + rule_info.sw_act.fwd_id.q_id = tc_fltr->action.fwd.q.hw_queue; + rule_info.sw_act.vsi_handle = ch_vsi->idx; + rule_info.priority = ICE_SWITCH_FLTR_PRIO_QUEUE; + rule_info.sw_act.src = hw->pf_id; + rule_info.rx = true; + dev_dbg(dev, "add switch rule action to forward to queue:%u (HW queue %u), lkups_cnt:%u\n", + tc_fltr->action.fwd.q.queue, + tc_fltr->action.fwd.q.hw_queue, lkups_cnt); + break; + default: rule_info.sw_act.flag |= ICE_FLTR_TX; + /* In case of Tx (LOOKUP_TX), src needs to be src VSI */ rule_info.sw_act.src = vsi->idx; + /* 'Rx' is false, direction of rule(LOOKUPTRX) */ rule_info.rx = false; + rule_info.priority = ICE_SWITCH_FLTR_PRIO_VSI; + break; } - /* specify the cookie as filter_rule_id */ - rule_info.fltr_rule_id = tc_fltr->cookie; - ret = ice_add_adv_rule(hw, list, lkups_cnt, &rule_info, &rule_added); if (ret == -EEXIST) { NL_SET_ERR_MSG_MOD(tc_fltr->extack, @@ -831,19 +950,14 @@ ice_add_tc_flower_adv_fltr(struct ice_vsi *vsi, */ tc_fltr->rid = rule_added.rid; tc_fltr->rule_id = rule_added.rule_id; - if (tc_fltr->action.tc_class > 0 && ch_vsi) { - /* For PF ADQ, VSI type is set as ICE_VSI_CHNL, and - * for PF ADQ filter, it is not yet set in tc_fltr, - * hence store the dest_vsi ptr in tc_fltr - */ - if (ch_vsi->type == ICE_VSI_CHNL) - tc_fltr->dest_vsi = ch_vsi; + tc_fltr->dest_vsi_handle = rule_added.vsi_handle; + if (tc_fltr->action.fltr_act == ICE_FWD_TO_VSI || + tc_fltr->action.fltr_act == ICE_FWD_TO_Q) { + tc_fltr->dest_vsi = ch_vsi; /* keep track of advanced switch filter for - * destination VSI (channel VSI) + * destination VSI */ ch_vsi->num_chnl_fltr++; - /* in this case, dest_id is VSI handle (sw handle) */ - tc_fltr->dest_id = rule_added.vsi_handle; /* keeps track of channel filters for PF VSI */ if (vsi->type == ICE_VSI_PF && @@ -851,10 +965,22 @@ ice_add_tc_flower_adv_fltr(struct ice_vsi *vsi, ICE_TC_FLWR_FIELD_ENC_DST_MAC))) pf->num_dmac_chnl_fltrs++; } - dev_dbg(dev, "added switch rule (lkups_cnt %u, flags 0x%x) for TC %u, rid %u, rule_id %u, vsi_idx %u\n", - lkups_cnt, flags, - tc_fltr->action.tc_class, rule_added.rid, - rule_added.rule_id, rule_added.vsi_handle); + switch (tc_fltr->action.fltr_act) { + case ICE_FWD_TO_VSI: + dev_dbg(dev, "added switch rule (lkups_cnt %u, flags 0x%x), action is forward to TC %u, rid %u, rule_id %u, vsi_idx %u\n", + lkups_cnt, flags, + tc_fltr->action.fwd.tc.tc_class, rule_added.rid, + rule_added.rule_id, rule_added.vsi_handle); + break; + case ICE_FWD_TO_Q: + dev_dbg(dev, "added switch rule (lkups_cnt %u, flags 0x%x), action is forward to queue: %u (HW queue %u) , rid %u, rule_id %u\n", + lkups_cnt, flags, tc_fltr->action.fwd.q.queue, + tc_fltr->action.fwd.q.hw_queue, rule_added.rid, + rule_added.rule_id); + break; + default: + break; + } exit: kfree(list); return ret; @@ -1455,43 +1581,15 @@ ice_add_switch_fltr(struct ice_vsi *vsi, struct ice_tc_flower_fltr *fltr) } /** - * ice_handle_tclass_action - Support directing to a traffic class + * ice_prep_adq_filter - Prepare ADQ filter with the required additional headers * @vsi: Pointer to VSI - * @cls_flower: Pointer to TC flower offload structure * @fltr: Pointer to TC flower filter structure * - * Support directing traffic to a traffic class + * Prepare ADQ filter with the required additional header fields */ static int -ice_handle_tclass_action(struct ice_vsi *vsi, - struct flow_cls_offload *cls_flower, - struct ice_tc_flower_fltr *fltr) +ice_prep_adq_filter(struct ice_vsi *vsi, struct ice_tc_flower_fltr *fltr) { - int tc = tc_classid_to_hwtc(vsi->netdev, cls_flower->classid); - struct ice_vsi *main_vsi; - - if (tc < 0) { - NL_SET_ERR_MSG_MOD(fltr->extack, "Unable to add filter because specified destination is invalid"); - return -EINVAL; - } - if (!tc) { - NL_SET_ERR_MSG_MOD(fltr->extack, "Unable to add filter because of invalid destination"); - return -EINVAL; - } - - if (!(vsi->all_enatc & BIT(tc))) { - NL_SET_ERR_MSG_MOD(fltr->extack, "Unable to add filter because of non-existence destination"); - return -EINVAL; - } - - /* Redirect to a TC class or Queue Group */ - main_vsi = ice_get_main_vsi(vsi->back); - if (!main_vsi || !main_vsi->netdev) { - NL_SET_ERR_MSG_MOD(fltr->extack, - "Unable to add filter because of invalid netdevice"); - return -EINVAL; - } - if ((fltr->flags & ICE_TC_FLWR_FIELD_TENANT_ID) && (fltr->flags & (ICE_TC_FLWR_FIELD_DST_MAC | ICE_TC_FLWR_FIELD_SRC_MAC))) { @@ -1503,9 +1601,8 @@ ice_handle_tclass_action(struct ice_vsi *vsi, /* For ADQ, filter must include dest MAC address, otherwise unwanted * packets with unrelated MAC address get delivered to ADQ VSIs as long * as remaining filter criteria is satisfied such as dest IP address - * and dest/src L4 port. Following code is trying to handle: - * 1. For non-tunnel, if user specify MAC addresses, use them (means - * this code won't do anything + * and dest/src L4 port. Below code handles the following cases: + * 1. For non-tunnel, if user specify MAC addresses, use them. * 2. For non-tunnel, if user didn't specify MAC address, add implicit * dest MAC to be lower netdev's active unicast MAC address * 3. For tunnel, as of now TC-filter through flower classifier doesn't @@ -1528,37 +1625,99 @@ ice_handle_tclass_action(struct ice_vsi *vsi, eth_broadcast_addr(fltr->outer_headers.l2_mask.dst_mac); } - /* validate specified dest MAC address, make sure either it belongs to - * lower netdev or any of MACVLAN. MACVLANs MAC address are added as - * unicast MAC filter destined to main VSI. - */ - if (!ice_mac_fltr_exist(&main_vsi->back->hw, - fltr->outer_headers.l2_key.dst_mac, - main_vsi->idx)) { - NL_SET_ERR_MSG_MOD(fltr->extack, - "Unable to add filter because legacy MAC filter for specified destination doesn't exist"); - return -EINVAL; - } - /* Make sure VLAN is already added to main VSI, before allowing ADQ to * add a VLAN based filter such as MAC + VLAN + L4 port. */ if (fltr->flags & ICE_TC_FLWR_FIELD_VLAN) { u16 vlan_id = be16_to_cpu(fltr->outer_headers.vlan_hdr.vlan_id); - if (!ice_vlan_fltr_exist(&main_vsi->back->hw, vlan_id, - main_vsi->idx)) { + if (!ice_vlan_fltr_exist(&vsi->back->hw, vlan_id, vsi->idx)) { NL_SET_ERR_MSG_MOD(fltr->extack, "Unable to add filter because legacy VLAN filter for specified destination doesn't exist"); return -EINVAL; } } - fltr->action.fltr_act = ICE_FWD_TO_VSI; - fltr->action.tc_class = tc; - return 0; } +/** + * ice_handle_tclass_action - Support directing to a traffic class + * @vsi: Pointer to VSI + * @cls_flower: Pointer to TC flower offload structure + * @fltr: Pointer to TC flower filter structure + * + * Support directing traffic to a traffic class/queue-set + */ +static int +ice_handle_tclass_action(struct ice_vsi *vsi, + struct flow_cls_offload *cls_flower, + struct ice_tc_flower_fltr *fltr) +{ + int tc = tc_classid_to_hwtc(vsi->netdev, cls_flower->classid); + + /* user specified hw_tc (must be non-zero for ADQ TC), action is forward + * to hw_tc (i.e. ADQ channel number) + */ + if (tc < ICE_CHNL_START_TC) { + NL_SET_ERR_MSG_MOD(fltr->extack, + "Unable to add filter because of unsupported destination"); + return -EOPNOTSUPP; + } + if (!(vsi->all_enatc & BIT(tc))) { + NL_SET_ERR_MSG_MOD(fltr->extack, + "Unable to add filter because of non-existence destination"); + return -EINVAL; + } + fltr->action.fltr_act = ICE_FWD_TO_VSI; + fltr->action.fwd.tc.tc_class = tc; + + return ice_prep_adq_filter(vsi, fltr); +} + +static int +ice_tc_forward_to_queue(struct ice_vsi *vsi, struct ice_tc_flower_fltr *fltr, + struct flow_action_entry *act) +{ + struct ice_vsi *ch_vsi = NULL; + u16 queue = act->rx_queue; + + if (queue > vsi->num_rxq) { + NL_SET_ERR_MSG_MOD(fltr->extack, + "Unable to add filter because specified queue is invalid"); + return -EINVAL; + } + fltr->action.fltr_act = ICE_FWD_TO_Q; + fltr->action.fwd.q.queue = queue; + /* determine corresponding HW queue */ + fltr->action.fwd.q.hw_queue = vsi->rxq_map[queue]; + + /* If ADQ is configured, and the queue belongs to ADQ VSI, then prepare + * ADQ switch filter + */ + ch_vsi = ice_locate_vsi_using_queue(vsi, fltr); + if (!ch_vsi) + return -EINVAL; + fltr->dest_vsi = ch_vsi; + if (!ice_is_chnl_fltr(fltr)) + return 0; + + return ice_prep_adq_filter(vsi, fltr); +} + +static int +ice_tc_parse_action(struct ice_vsi *vsi, struct ice_tc_flower_fltr *fltr, + struct flow_action_entry *act) +{ + switch (act->id) { + case FLOW_ACTION_RX_QUEUE_MAPPING: + /* forward to queue */ + return ice_tc_forward_to_queue(vsi, fltr, act); + default: + NL_SET_ERR_MSG_MOD(fltr->extack, "Unsupported TC action"); + return -EOPNOTSUPP; + } +} + /** * ice_parse_tc_flower_actions - Parse the actions for a TC filter * @vsi: Pointer to VSI @@ -1575,7 +1734,7 @@ ice_parse_tc_flower_actions(struct ice_vsi *vsi, struct flow_rule *rule = flow_cls_offload_flow_rule(cls_flower); struct flow_action *flow_action = &rule->action; struct flow_action_entry *act; - int i; + int i, err; if (cls_flower->classid) return ice_handle_tclass_action(vsi, cls_flower, fltr); @@ -1584,21 +1743,13 @@ ice_parse_tc_flower_actions(struct ice_vsi *vsi, return -EINVAL; flow_action_for_each(i, act, flow_action) { - if (ice_is_eswitch_mode_switchdev(vsi->back)) { - int err = ice_eswitch_tc_parse_action(fltr, act); - - if (err) - return err; - continue; - } - /* Allow only one rule per filter */ - - /* Drop action */ - if (act->id == FLOW_ACTION_DROP) { - NL_SET_ERR_MSG_MOD(fltr->extack, "Unsupported action DROP"); - return -EINVAL; - } - fltr->action.fltr_act = ICE_FWD_TO_VSI; + if (ice_is_eswitch_mode_switchdev(vsi->back)) + err = ice_eswitch_tc_parse_action(fltr, act); + else + err = ice_tc_parse_action(vsi, fltr, act); + if (err) + return err; + continue; } return 0; } @@ -1618,7 +1769,7 @@ static int ice_del_tc_fltr(struct ice_vsi *vsi, struct ice_tc_flower_fltr *fltr) rule_rem.rid = fltr->rid; rule_rem.rule_id = fltr->rule_id; - rule_rem.vsi_handle = fltr->dest_id; + rule_rem.vsi_handle = fltr->dest_vsi_handle; err = ice_rem_adv_rule_by_id(&pf->hw, &rule_rem); if (err) { if (err == -ENOENT) { diff --git a/drivers/net/ethernet/intel/ice/ice_tc_lib.h b/drivers/net/ethernet/intel/ice/ice_tc_lib.h index 92642faad595..d916d1e92aa3 100644 --- a/drivers/net/ethernet/intel/ice/ice_tc_lib.h +++ b/drivers/net/ethernet/intel/ice/ice_tc_lib.h @@ -45,7 +45,20 @@ struct ice_indr_block_priv { }; struct ice_tc_flower_action { - u32 tc_class; + /* forward action specific params */ + union { + struct { + u32 tc_class; /* forward to hw_tc */ + u32 rsvd; + } tc; + struct { + u16 queue; /* forward to queue */ + /* To add filter in HW, absolute queue number in global + * space of queues (between 0...N) is needed + */ + u16 hw_queue; + } q; + } fwd; enum ice_sw_fwd_act_type fltr_act; }; @@ -131,11 +144,11 @@ struct ice_tc_flower_fltr { */ u16 rid; u16 rule_id; - /* this could be queue/vsi_idx (sw handle)/queue_group, depending upon - * destination type + /* VSI handle of the destination VSI (it could be main PF VSI, CHNL_VSI, + * VF VSI) */ - u16 dest_id; - /* if dest_id is vsi_idx, then need to store destination VSI ptr */ + u16 dest_vsi_handle; + /* ptr to destination VSI */ struct ice_vsi *dest_vsi; /* direction of fltr for eswitch use case */ enum ice_eswitch_fltr_direction direction; @@ -162,12 +175,23 @@ struct ice_tc_flower_fltr { * @f: Pointer to tc-flower filter * * Criteria to determine of given filter is valid channel filter - * or not is based on its "destination". If destination is hw_tc (aka tc_class) - * and it is non-zero, then it is valid channel (aka ADQ) filter + * or not is based on its destination. + * For forward to VSI action, if destination is valid hw_tc (aka tc_class) + * and in supported range of TCs for ADQ, then return true. + * For forward to queue, as long as dest_vsi is valid and it is of type + * VSI_CHNL (PF ADQ VSI is of type VSI_CHNL), return true. + * NOTE: For forward to queue, correct dest_vsi is still set in tc_fltr based + * on destination queue specified. */ static inline bool ice_is_chnl_fltr(struct ice_tc_flower_fltr *f) { - return !!f->action.tc_class; + if (f->action.fltr_act == ICE_FWD_TO_VSI) + return f->action.fwd.tc.tc_class >= ICE_CHNL_START_TC && + f->action.fwd.tc.tc_class < ICE_CHNL_MAX_TC; + else if (f->action.fltr_act == ICE_FWD_TO_Q) + return f->dest_vsi && f->dest_vsi->type == ICE_VSI_CHNL; + + return false; } /** From d5ae8ecf38322f2072bbd46ffd0d0adf309c11f2 Mon Sep 17 00:00:00 2001 From: Amritha Nambiar Date: Fri, 21 Oct 2022 00:58:50 -0700 Subject: [PATCH 0312/2002] Documentation: networking: TC queue based filtering Add tc-queue-filters.rst with notes on TC filters for selecting a set of queues and/or a queue. Signed-off-by: Amritha Nambiar Signed-off-by: Paolo Abeni --- Documentation/networking/index.rst | 1 + Documentation/networking/tc-queue-filters.rst | 37 +++++++++++++++++++ 2 files changed, 38 insertions(+) create mode 100644 Documentation/networking/tc-queue-filters.rst diff --git a/Documentation/networking/index.rst b/Documentation/networking/index.rst index 16a153bcc5fe..4f2d1f682a18 100644 --- a/Documentation/networking/index.rst +++ b/Documentation/networking/index.rst @@ -104,6 +104,7 @@ Contents: switchdev sysfs-tagging tc-actions-env-rules + tc-queue-filters tcp-thin team timestamping diff --git a/Documentation/networking/tc-queue-filters.rst b/Documentation/networking/tc-queue-filters.rst new file mode 100644 index 000000000000..6b417092276f --- /dev/null +++ b/Documentation/networking/tc-queue-filters.rst @@ -0,0 +1,37 @@ +.. SPDX-License-Identifier: GPL-2.0 + +========================= +TC queue based filtering +========================= + +TC can be used for directing traffic to either a set of queues or +to a single queue on both the transmit and receive side. + +On the transmit side: + +1) TC filter directing traffic to a set of queues is achieved + using the action skbedit priority for Tx priority selection, + the priority maps to a traffic class (set of queues) when + the queue-sets are configured using mqprio. + +2) TC filter directs traffic to a transmit queue with the action + skbedit queue_mapping $tx_qid. The action skbedit queue_mapping + for transmit queue is executed in software only and cannot be + offloaded. + +Likewise, on the receive side, the two filters for selecting set of +queues and/or a single queue are supported as below: + +1) TC flower filter directs incoming traffic to a set of queues using + the 'hw_tc' option. + hw_tc $TCID - Specify a hardware traffic class to pass matching + packets on to. TCID is in the range 0 through 15. + +2) TC filter with action skbedit queue_mapping $rx_qid selects a + receive queue. The action skbedit queue_mapping for receive queue + is supported only in hardware. Multiple filters may compete in + the hardware for queue selection. In such case, the hardware + pipeline resolves conflicts based on priority. On Intel E810 + devices, TC filter directing traffic to a queue have higher + priority over flow director filter assigning a queue. The hash + filter has lowest priority. From cc2bbbfd9a5064cb8fc2996962d90b782f906223 Mon Sep 17 00:00:00 2001 From: Leon Romanovsky Date: Mon, 24 Oct 2022 20:19:31 +0300 Subject: [PATCH 0313/2002] xfrm: Remove not-used total variable Total variable is not used in xfrm_byidx_resize() and can be safely removed. Signed-off-by: Leon Romanovsky Signed-off-by: Steffen Klassert --- net/xfrm/xfrm_policy.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c index e392d8d05e0c..d80519c4e389 100644 --- a/net/xfrm/xfrm_policy.c +++ b/net/xfrm/xfrm_policy.c @@ -605,7 +605,7 @@ static void xfrm_bydst_resize(struct net *net, int dir) xfrm_hash_free(odst, (hmask + 1) * sizeof(struct hlist_head)); } -static void xfrm_byidx_resize(struct net *net, int total) +static void xfrm_byidx_resize(struct net *net) { unsigned int hmask = net->xfrm.policy_idx_hmask; unsigned int nhashmask = xfrm_new_hash_mask(hmask); @@ -683,7 +683,7 @@ static void xfrm_hash_resize(struct work_struct *work) xfrm_bydst_resize(net, dir); } if (xfrm_byidx_should_resize(net, total)) - xfrm_byidx_resize(net, total); + xfrm_byidx_resize(net); mutex_unlock(&hash_resize_mutex); } From fb4014ac76b837621a09c2254fa2d67bfbf58e67 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Fri, 21 Oct 2022 14:13:34 -0500 Subject: [PATCH 0314/2002] net: ipa: kill two constant symbols The entries in each IPA routing table are divided between the modem and the AP. The modem always gets some number of entries located at the base of the table; the AP gets all those that follow. There's no reason to think the modem will use anything different from the first entries in a routing table, so: - Get rid of IPA_ROUTE_MODEM_MIN (just assume it's 0) - Get rid of IPA_ROUTE_AP_MIN (just assume it's IPA_ROUTE_MODEM_COUNT) And finally: - Open-code IPA_ROUTE_AP_COUNT and remove its definition Signed-off-by: Alex Elder Signed-off-by: Paolo Abeni --- drivers/net/ipa/ipa_table.c | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/drivers/net/ipa/ipa_table.c b/drivers/net/ipa/ipa_table.c index 510ff2dc8999..74d7082b3c5a 100644 --- a/drivers/net/ipa/ipa_table.c +++ b/drivers/net/ipa/ipa_table.c @@ -106,12 +106,6 @@ * ---------------------- */ -/* Assignment of route table entries to the modem and AP */ -#define IPA_ROUTE_MODEM_MIN 0 -#define IPA_ROUTE_AP_MIN IPA_ROUTE_MODEM_COUNT -#define IPA_ROUTE_AP_COUNT \ - (IPA_ROUTE_COUNT_MAX - IPA_ROUTE_MODEM_COUNT) - /* Filter or route rules consist of a set of 32-bit values followed by a * 32-bit all-zero rule list terminator. The "zero rule" is simply an * all-zero rule followed by the list terminator. @@ -342,11 +336,11 @@ static int ipa_route_reset(struct ipa *ipa, bool modem) } if (modem) { - first = IPA_ROUTE_MODEM_MIN; + first = 0; count = IPA_ROUTE_MODEM_COUNT; } else { - first = IPA_ROUTE_AP_MIN; - count = IPA_ROUTE_AP_COUNT; + first = IPA_ROUTE_MODEM_COUNT; + count = IPA_ROUTE_COUNT_MAX - IPA_ROUTE_MODEM_COUNT; } ipa_table_reset_add(trans, false, first, count, IPA_MEM_V4_ROUTE); @@ -561,8 +555,7 @@ static void ipa_filter_config(struct ipa *ipa, bool modem) static bool ipa_route_id_modem(u32 route_id) { - return route_id >= IPA_ROUTE_MODEM_MIN && - route_id <= IPA_ROUTE_MODEM_MIN + IPA_ROUTE_MODEM_COUNT - 1; + return route_id < IPA_ROUTE_MODEM_COUNT; } /** From 2554322b31994d9a6c3ff6eaaaac7d78bac05d82 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Fri, 21 Oct 2022 14:13:35 -0500 Subject: [PATCH 0315/2002] net: ipa: remove two memory region checks There's no need to ensure table memory regions fit within the IPA-local memory range. And there's no need to ensure the modem header memory region is in range either. These are verified for all memory regions in ipa_mem_size_valid(), once we have settled on the size of IPA memory. Signed-off-by: Alex Elder Signed-off-by: Paolo Abeni --- drivers/net/ipa/ipa_cmd.c | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/drivers/net/ipa/ipa_cmd.c b/drivers/net/ipa/ipa_cmd.c index 26c3db9f52b1..e46e8f80b174 100644 --- a/drivers/net/ipa/ipa_cmd.c +++ b/drivers/net/ipa/ipa_cmd.c @@ -197,16 +197,6 @@ bool ipa_cmd_table_valid(struct ipa *ipa, const struct ipa_mem *mem, bool route) return false; } - /* Entire memory range must fit within IPA-local memory */ - if (mem->offset > ipa->mem_size || - mem->size > ipa->mem_size - mem->offset) { - dev_err(dev, "%s table region out of range\n", table); - dev_err(dev, " (0x%04x + 0x%04x > 0x%04x)\n", - mem->offset, mem->size, ipa->mem_size); - - return false; - } - return true; } @@ -257,15 +247,6 @@ static bool ipa_cmd_header_valid(struct ipa *ipa) return false; } - /* Make sure the entire combined area fits in IPA memory */ - if (size > ipa->mem_size || offset > ipa->mem_size - size) { - dev_err(dev, "header table region out of range\n"); - dev_err(dev, " (0x%04x + 0x%04x > 0x%04x)\n", - offset, size, ipa->mem_size); - - return false; - } - return true; } From cf13919654d5fe54974c7bba49c220833c26bce4 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Fri, 21 Oct 2022 14:13:36 -0500 Subject: [PATCH 0316/2002] net: ipa: validate IPA table memory earlier Add checks in ipa_table_init() to ensure the memory regions defined for IPA filter and routing tables are valid. For routing tables, the checks ensure: - The non-hashed IPv4 and IPv6 routing tables are defined - The non-hashed IPv4 and IPv6 routing tables are the same size - The number entries in the non-hashed IPv4 routing table is enough to hold the number entries available to the modem, plus at least one usable by the AP. For filter tables, the checks ensure: - The non-hashed IPv4 and IPv6 filter tables are defined - The non-hashed IPv4 and IPv6 filter tables are the same size - The number entries in the non-hashed IPv4 filter table is enough to hold the endpoint bitmap, plus an entry for each defined endpoint that supports filtering. In addition, for both routing and filter tables: - If hashing isn't supported (IPA v4.2), hashed tables are zero size - If hashing *is* supported, all hashed tables are the same size as their non-hashed counterparts. When validating the size of routing tables, require the AP to have at least one entry (in addition to those used by the modem). Signed-off-by: Alex Elder Signed-off-by: Paolo Abeni --- drivers/net/ipa/ipa_table.c | 98 +++++++++++++++++++++++++++++++++++-- 1 file changed, 94 insertions(+), 4 deletions(-) diff --git a/drivers/net/ipa/ipa_table.c b/drivers/net/ipa/ipa_table.c index 74d7082b3c5a..222362a7a2a8 100644 --- a/drivers/net/ipa/ipa_table.c +++ b/drivers/net/ipa/ipa_table.c @@ -134,9 +134,25 @@ static void ipa_table_validate_build(void) BUILD_BUG_ON(IPA_ROUTE_COUNT_MAX > 32); /* The modem must be allotted at least one route table entry */ BUILD_BUG_ON(!IPA_ROUTE_MODEM_COUNT); - /* But it can't have more than what is available */ - BUILD_BUG_ON(IPA_ROUTE_MODEM_COUNT > IPA_ROUTE_COUNT_MAX); + /* AP must too, but we can't use more than what is available */ + BUILD_BUG_ON(IPA_ROUTE_MODEM_COUNT >= IPA_ROUTE_COUNT_MAX); +} +static const struct ipa_mem * +ipa_table_mem(struct ipa *ipa, bool filter, bool hashed, bool ipv6) +{ + enum ipa_mem_id mem_id; + + mem_id = filter ? hashed ? ipv6 ? IPA_MEM_V6_FILTER_HASHED + : IPA_MEM_V4_FILTER_HASHED + : ipv6 ? IPA_MEM_V6_FILTER + : IPA_MEM_V4_FILTER + : hashed ? ipv6 ? IPA_MEM_V6_ROUTE_HASHED + : IPA_MEM_V4_ROUTE_HASHED + : ipv6 ? IPA_MEM_V6_ROUTE + : IPA_MEM_V4_ROUTE; + + return ipa_mem_find(ipa, mem_id); } static bool @@ -604,8 +620,77 @@ void ipa_table_config(struct ipa *ipa) ipa_route_config(ipa, true); } -/* - * Initialize a coherent DMA allocation containing initialized filter and +/* Zero modem_route_count means filter table memory check */ +static bool ipa_table_mem_valid(struct ipa *ipa, bool modem_route_count) +{ + bool hash_support = ipa_table_hash_support(ipa); + bool filter = !modem_route_count; + const struct ipa_mem *mem_hashed; + const struct ipa_mem *mem_ipv4; + const struct ipa_mem *mem_ipv6; + u32 count; + + /* IPv4 and IPv6 non-hashed tables are expected to be defined and + * have the same size. Both must have at least two entries (and + * would normally have more than that). + */ + mem_ipv4 = ipa_table_mem(ipa, filter, false, false); + if (!mem_ipv4) + return false; + + mem_ipv6 = ipa_table_mem(ipa, filter, false, true); + if (!mem_ipv6) + return false; + + if (mem_ipv4->size != mem_ipv6->size) + return false; + + /* Make sure the regions are big enough */ + count = mem_ipv4->size / sizeof(__le64); + if (count < 2) + return false; + if (filter) { + /* Filter tables must able to hold the endpoint bitmap plus + * an entry for each endpoint that supports filtering + */ + if (count < 1 + hweight32(ipa->filter_map)) + return false; + } else { + /* Routing tables must be able to hold all modem entries, + * plus at least one entry for the AP. + */ + if (count < modem_route_count + 1) + return false; + } + + /* If hashing is supported, hashed tables are expected to be defined, + * and have the same size as non-hashed tables. If hashing is not + * supported, hashed tables are expected to have zero size (or not + * be defined). + */ + mem_hashed = ipa_table_mem(ipa, filter, true, false); + if (hash_support) { + if (!mem_hashed || mem_hashed->size != mem_ipv4->size) + return false; + } else { + if (mem_hashed && mem_hashed->size) + return false; + } + + /* Same check for IPv6 tables */ + mem_hashed = ipa_table_mem(ipa, filter, true, true); + if (hash_support) { + if (!mem_hashed || mem_hashed->size != mem_ipv6->size) + return false; + } else { + if (mem_hashed && mem_hashed->size) + return false; + } + + return true; +} + +/* Initialize a coherent DMA allocation containing initialized filter and * route table data. This is used when initializing or resetting the IPA * filter or route table. * @@ -653,6 +738,11 @@ int ipa_table_init(struct ipa *ipa) ipa_table_validate_build(); + if (!ipa_table_mem_valid(ipa, 0)) + return -EINVAL; + if (!ipa_table_mem_valid(ipa, IPA_ROUTE_MODEM_COUNT)) + return -EINVAL; + /* The IPA hardware requires route and filter table rules to be * aligned on a 128-byte boundary. We put the "zero rule" at the * base of the table area allocated here. The DMA address returned From 5444b0ea9915132b8a202c2c664630cb99daa969 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Fri, 21 Oct 2022 14:13:37 -0500 Subject: [PATCH 0317/2002] net: ipa: verify table sizes fit in commands early We currently verify the table size and offset fit in the immediate command fields that must encode them in ipa_table_valid_one(). We can now make this check earlier, in ipa_table_mem_valid(). The non-hashed IPv4 filter and route tables will always exist, and their sizes will match the IPv6 tables, as well as the hashed tables (if supported). So it's sufficient to verify the offset and size of the IPv4 non-hashed tables fit into these fields. Rename the function ipa_cmd_table_init_valid(), to reinforce that it is the TABLE_INIT immediate command fields we're checking. Signed-off-by: Alex Elder Signed-off-by: Paolo Abeni --- drivers/net/ipa/ipa_cmd.c | 3 ++- drivers/net/ipa/ipa_cmd.h | 6 +++--- drivers/net/ipa/ipa_table.c | 8 ++++---- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/drivers/net/ipa/ipa_cmd.c b/drivers/net/ipa/ipa_cmd.c index e46e8f80b174..abee6cc018a2 100644 --- a/drivers/net/ipa/ipa_cmd.c +++ b/drivers/net/ipa/ipa_cmd.c @@ -171,7 +171,8 @@ static void ipa_cmd_validate_build(void) } /* Validate a memory region holding a table */ -bool ipa_cmd_table_valid(struct ipa *ipa, const struct ipa_mem *mem, bool route) +bool ipa_cmd_table_init_valid(struct ipa *ipa, const struct ipa_mem *mem, + bool route) { u32 offset_max = field_max(IP_FLTRT_FLAGS_NHASH_ADDR_FMASK); u32 size_max = field_max(IP_FLTRT_FLAGS_NHASH_SIZE_FMASK); diff --git a/drivers/net/ipa/ipa_cmd.h b/drivers/net/ipa/ipa_cmd.h index 8e4243c1f0bb..d03cc619e2c3 100644 --- a/drivers/net/ipa/ipa_cmd.h +++ b/drivers/net/ipa/ipa_cmd.h @@ -47,15 +47,15 @@ enum ipa_cmd_opcode { }; /** - * ipa_cmd_table_valid() - Validate a memory region holding a table + * ipa_cmd_table_init_valid() - Validate a memory region holding a table * @ipa: - IPA pointer * @mem: - IPA memory region descriptor * @route: - Whether the region holds a route or filter table * * Return: true if region is valid, false otherwise */ -bool ipa_cmd_table_valid(struct ipa *ipa, const struct ipa_mem *mem, - bool route); +bool ipa_cmd_table_init_valid(struct ipa *ipa, const struct ipa_mem *mem, + bool route); /** * ipa_cmd_data_valid() - Validate command-realted configuration is valid diff --git a/drivers/net/ipa/ipa_table.c b/drivers/net/ipa/ipa_table.c index 222362a7a2a8..9822b18d9ed3 100644 --- a/drivers/net/ipa/ipa_table.c +++ b/drivers/net/ipa/ipa_table.c @@ -166,10 +166,6 @@ ipa_table_valid_one(struct ipa *ipa, enum ipa_mem_id mem_id, bool route) size = IPA_ROUTE_COUNT_MAX * sizeof(__le64); else size = (1 + IPA_FILTER_COUNT_MAX) * sizeof(__le64); - - if (!ipa_cmd_table_valid(ipa, mem, route)) - return false; - /* mem->size >= size is sufficient, but we'll demand more */ if (mem->size == size) return true; @@ -645,6 +641,10 @@ static bool ipa_table_mem_valid(struct ipa *ipa, bool modem_route_count) if (mem_ipv4->size != mem_ipv6->size) return false; + /* Table offset and size must fit in TABLE_INIT command fields */ + if (!ipa_cmd_table_init_valid(ipa, mem_ipv4, !filter)) + return false; + /* Make sure the regions are big enough */ count = mem_ipv4->size / sizeof(__le64); if (count < 2) From 7fd10a2aca6a2ff3316fac7fb2a72f6e1b303a57 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Fri, 21 Oct 2022 14:13:38 -0500 Subject: [PATCH 0318/2002] net: ipa: introduce ipa_cmd_init() Currently, ipa_cmd_data_valid() is called by ipa_mem_config(). Nothing it does requires access to hardware though, so it can be done during the init phase of IPA driver startup. Create a new function ipa_cmd_init(), whose purpose is to do early initialization related to IPA immediate commands. It will call the build-time validation function, then will make the two calls made previously by ipa_cmd_data_valid(). This make ipa_cmd_data_valid() unnecessary, so get rid of it. Rename ipa_cmd_header_valid() to be ipa_cmd_header_init_local_valid(), so its name is clearer about which IPA immediate command it is associated with. Signed-off-by: Alex Elder Signed-off-by: Paolo Abeni --- drivers/net/ipa/ipa_cmd.c | 31 +++++++++++++++---------------- drivers/net/ipa/ipa_cmd.h | 10 ++++++++++ drivers/net/ipa/ipa_mem.c | 4 ---- 3 files changed, 25 insertions(+), 20 deletions(-) diff --git a/drivers/net/ipa/ipa_cmd.c b/drivers/net/ipa/ipa_cmd.c index abee6cc018a2..de2cd86aa9e2 100644 --- a/drivers/net/ipa/ipa_cmd.c +++ b/drivers/net/ipa/ipa_cmd.c @@ -202,7 +202,7 @@ bool ipa_cmd_table_init_valid(struct ipa *ipa, const struct ipa_mem *mem, } /* Validate the memory region that holds headers */ -static bool ipa_cmd_header_valid(struct ipa *ipa) +static bool ipa_cmd_header_init_local_valid(struct ipa *ipa) { struct device *dev = &ipa->pdev->dev; const struct ipa_mem *mem; @@ -318,26 +318,11 @@ static bool ipa_cmd_register_write_valid(struct ipa *ipa) return true; } -bool ipa_cmd_data_valid(struct ipa *ipa) -{ - if (!ipa_cmd_header_valid(ipa)) - return false; - - if (!ipa_cmd_register_write_valid(ipa)) - return false; - - return true; -} - - int ipa_cmd_pool_init(struct gsi_channel *channel, u32 tre_max) { struct gsi_trans_info *trans_info = &channel->trans_info; struct device *dev = channel->gsi->dev; - /* This is as good a place as any to validate build constants */ - ipa_cmd_validate_build(); - /* Command payloads are allocated one at a time, but a single * transaction can require up to the maximum supported by the * channel; treat them as if they were allocated all at once. @@ -637,3 +622,17 @@ struct gsi_trans *ipa_cmd_trans_alloc(struct ipa *ipa, u32 tre_count) return gsi_channel_trans_alloc(&ipa->gsi, endpoint->channel_id, tre_count, DMA_NONE); } + +/* Init function for immediate commands; there is no ipa_cmd_exit() */ +int ipa_cmd_init(struct ipa *ipa) +{ + ipa_cmd_validate_build(); + + if (!ipa_cmd_header_init_local_valid(ipa)) + return -EINVAL; + + if (!ipa_cmd_register_write_valid(ipa)) + return -EINVAL; + + return 0; +} diff --git a/drivers/net/ipa/ipa_cmd.h b/drivers/net/ipa/ipa_cmd.h index d03cc619e2c3..e2cf1c2b0ef2 100644 --- a/drivers/net/ipa/ipa_cmd.h +++ b/drivers/net/ipa/ipa_cmd.h @@ -162,4 +162,14 @@ void ipa_cmd_pipeline_clear_wait(struct ipa *ipa); */ struct gsi_trans *ipa_cmd_trans_alloc(struct ipa *ipa, u32 tre_count); +/** + * ipa_cmd_init() - Initialize IPA immediate commands + * @ipa: - IPA pointer + * + * Return: 0 if successful, or a negative error code + * + * There is no need for a matching ipa_cmd_exit() function. + */ +int ipa_cmd_init(struct ipa *ipa); + #endif /* _IPA_CMD_H_ */ diff --git a/drivers/net/ipa/ipa_mem.c b/drivers/net/ipa/ipa_mem.c index f84c6830495a..2238dac2af07 100644 --- a/drivers/net/ipa/ipa_mem.c +++ b/drivers/net/ipa/ipa_mem.c @@ -370,10 +370,6 @@ int ipa_mem_config(struct ipa *ipa) if (!ipa_table_valid(ipa)) goto err_dma_free; - /* Validate memory-related properties relevant to immediate commands */ - if (!ipa_cmd_data_valid(ipa)) - goto err_dma_free; - /* Verify the microcontroller ring alignment (if defined) */ mem = ipa_mem_find(ipa, IPA_MEM_UC_EVENT_RING); if (mem && mem->offset % 1024) { From 39ad815244ac3746c0194ae3eb45cb1f51d3489a Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Fri, 21 Oct 2022 14:13:39 -0500 Subject: [PATCH 0319/2002] net: ipa: kill ipa_table_valid() What ipa_table_valid() (and ipa_table_valid_one(), which it calls) does is ensure that the memory regions that hold routing and filter tables have reasonable size. Specifically, it checks that the size of a region is sufficient (or rather, exactly the right size) to hold the maximum number of entries supported by the driver. (There is an additional check that's erroneous, but in practice it is never reached.) Recently ipa_table_mem_valid() was added, which is called by ipa_table_init(). That function verifies that all table memory regions are of sufficient size, and requires hashed tables to have zero size if hashing is not supported. It only ensures the filter table is large enough to hold the number of endpoints that support filtering, but that is adequate. Therefore everything that ipa_table_valid() does is redundant, so get rid of it. Signed-off-by: Alex Elder Signed-off-by: Paolo Abeni --- drivers/net/ipa/ipa_mem.c | 4 --- drivers/net/ipa/ipa_table.c | 50 ------------------------------------- drivers/net/ipa/ipa_table.h | 8 ------ 3 files changed, 62 deletions(-) diff --git a/drivers/net/ipa/ipa_mem.c b/drivers/net/ipa/ipa_mem.c index 2238dac2af07..4022ae01a131 100644 --- a/drivers/net/ipa/ipa_mem.c +++ b/drivers/net/ipa/ipa_mem.c @@ -366,10 +366,6 @@ int ipa_mem_config(struct ipa *ipa) while (--canary_count); } - /* Make sure filter and route table memory regions are valid */ - if (!ipa_table_valid(ipa)) - goto err_dma_free; - /* Verify the microcontroller ring alignment (if defined) */ mem = ipa_mem_find(ipa, IPA_MEM_UC_EVENT_RING); if (mem && mem->offset % 1024) { diff --git a/drivers/net/ipa/ipa_table.c b/drivers/net/ipa/ipa_table.c index 9822b18d9ed3..7a60f2867de9 100644 --- a/drivers/net/ipa/ipa_table.c +++ b/drivers/net/ipa/ipa_table.c @@ -155,56 +155,6 @@ ipa_table_mem(struct ipa *ipa, bool filter, bool hashed, bool ipv6) return ipa_mem_find(ipa, mem_id); } -static bool -ipa_table_valid_one(struct ipa *ipa, enum ipa_mem_id mem_id, bool route) -{ - const struct ipa_mem *mem = ipa_mem_find(ipa, mem_id); - struct device *dev = &ipa->pdev->dev; - u32 size; - - if (route) - size = IPA_ROUTE_COUNT_MAX * sizeof(__le64); - else - size = (1 + IPA_FILTER_COUNT_MAX) * sizeof(__le64); - /* mem->size >= size is sufficient, but we'll demand more */ - if (mem->size == size) - return true; - - /* Hashed table regions can be zero size if hashing is not supported */ - if (ipa_table_hash_support(ipa) && !mem->size) - return true; - - dev_err(dev, "%s table region %u size 0x%02x, expected 0x%02x\n", - route ? "route" : "filter", mem_id, mem->size, size); - - return false; -} - -/* Verify the filter and route table memory regions are the expected size */ -bool ipa_table_valid(struct ipa *ipa) -{ - bool valid; - - valid = ipa_table_valid_one(ipa, IPA_MEM_V4_FILTER, false); - valid = valid && ipa_table_valid_one(ipa, IPA_MEM_V6_FILTER, false); - valid = valid && ipa_table_valid_one(ipa, IPA_MEM_V4_ROUTE, true); - valid = valid && ipa_table_valid_one(ipa, IPA_MEM_V6_ROUTE, true); - - if (!ipa_table_hash_support(ipa)) - return valid; - - valid = valid && ipa_table_valid_one(ipa, IPA_MEM_V4_FILTER_HASHED, - false); - valid = valid && ipa_table_valid_one(ipa, IPA_MEM_V6_FILTER_HASHED, - false); - valid = valid && ipa_table_valid_one(ipa, IPA_MEM_V4_ROUTE_HASHED, - true); - valid = valid && ipa_table_valid_one(ipa, IPA_MEM_V6_ROUTE_HASHED, - true); - - return valid; -} - bool ipa_filter_map_valid(struct ipa *ipa, u32 filter_map) { struct device *dev = &ipa->pdev->dev; diff --git a/drivers/net/ipa/ipa_table.h b/drivers/net/ipa/ipa_table.h index 395189f75d78..73ca8369c635 100644 --- a/drivers/net/ipa/ipa_table.h +++ b/drivers/net/ipa/ipa_table.h @@ -19,14 +19,6 @@ struct ipa; /* The maximum number of route table entries (IPv4, IPv6; hashed or not) */ #define IPA_ROUTE_COUNT_MAX 15 -/** - * ipa_table_valid() - Validate route and filter table memory regions - * @ipa: IPA pointer - * - * Return: true if all regions are valid, false otherwise - */ -bool ipa_table_valid(struct ipa *ipa); - /** * ipa_filter_map_valid() - Validate a filter table endpoint bitmap * @ipa: IPA pointer From 73da9cac517ce4dc3a00b87f800846dcc2469ddf Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Fri, 21 Oct 2022 14:13:40 -0500 Subject: [PATCH 0320/2002] net: ipa: check table memory regions earlier Verify that the sizes of the routing and filter table memory regions are valid as part of memory initialization, rather than waiting for table initialization. The main reason to do this is that upcoming patches use these memory region sizes to determine the number of entries in these tables, and we'll want to know these sizes are good sooner. Signed-off-by: Alex Elder Signed-off-by: Paolo Abeni --- drivers/net/ipa/ipa_mem.c | 6 ++++++ drivers/net/ipa/ipa_table.c | 7 +------ drivers/net/ipa/ipa_table.h | 7 +++++++ 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/drivers/net/ipa/ipa_mem.c b/drivers/net/ipa/ipa_mem.c index 4022ae01a131..a3d2317452ac 100644 --- a/drivers/net/ipa/ipa_mem.c +++ b/drivers/net/ipa/ipa_mem.c @@ -617,6 +617,12 @@ int ipa_mem_init(struct ipa *ipa, const struct ipa_mem_data *mem_data) ipa->mem_count = mem_data->local_count; ipa->mem = mem_data->local; + /* Check the route and filter table memory regions */ + if (!ipa_table_mem_valid(ipa, 0)) + return -EINVAL; + if (!ipa_table_mem_valid(ipa, IPA_ROUTE_MODEM_COUNT)) + return -EINVAL; + ret = dma_set_mask_and_coherent(&ipa->pdev->dev, DMA_BIT_MASK(64)); if (ret) { dev_err(dev, "error %d setting DMA mask\n", ret); diff --git a/drivers/net/ipa/ipa_table.c b/drivers/net/ipa/ipa_table.c index 7a60f2867de9..58a1a9da3133 100644 --- a/drivers/net/ipa/ipa_table.c +++ b/drivers/net/ipa/ipa_table.c @@ -567,7 +567,7 @@ void ipa_table_config(struct ipa *ipa) } /* Zero modem_route_count means filter table memory check */ -static bool ipa_table_mem_valid(struct ipa *ipa, bool modem_route_count) +bool ipa_table_mem_valid(struct ipa *ipa, bool modem_route_count) { bool hash_support = ipa_table_hash_support(ipa); bool filter = !modem_route_count; @@ -688,11 +688,6 @@ int ipa_table_init(struct ipa *ipa) ipa_table_validate_build(); - if (!ipa_table_mem_valid(ipa, 0)) - return -EINVAL; - if (!ipa_table_mem_valid(ipa, IPA_ROUTE_MODEM_COUNT)) - return -EINVAL; - /* The IPA hardware requires route and filter table rules to be * aligned on a 128-byte boundary. We put the "zero rule" at the * base of the table area allocated here. The DMA address returned diff --git a/drivers/net/ipa/ipa_table.h b/drivers/net/ipa/ipa_table.h index 73ca8369c635..65d96debd391 100644 --- a/drivers/net/ipa/ipa_table.h +++ b/drivers/net/ipa/ipa_table.h @@ -78,4 +78,11 @@ int ipa_table_init(struct ipa *ipa); */ void ipa_table_exit(struct ipa *ipa); +/** + * ipa_table_mem_valid() - Validate sizes of table memory regions + * @ipa: IPA pointer + * @modem_route_count: Number of modem route table entries + */ +bool ipa_table_mem_valid(struct ipa *ipa, bool modem_route_count); + #endif /* _IPA_TABLE_H_ */ From b261eda84ec136240a9ca753389853a3a1bccca2 Mon Sep 17 00:00:00 2001 From: Kuniyuki Iwashima Date: Fri, 21 Oct 2022 13:44:34 -0700 Subject: [PATCH 0321/2002] soreuseport: Fix socket selection for SO_INCOMING_CPU. Kazuho Oku reported that setsockopt(SO_INCOMING_CPU) does not work with setsockopt(SO_REUSEPORT) since v4.6. With the combination of SO_REUSEPORT and SO_INCOMING_CPU, we could build a highly efficient server application. setsockopt(SO_INCOMING_CPU) associates a CPU with a TCP listener or UDP socket, and then incoming packets processed on the CPU will likely be distributed to the socket. Technically, a socket could even receive packets handled on another CPU if no sockets in the reuseport group have the same CPU receiving the flow. The logic exists in compute_score() so that a socket will get a higher score if it has the same CPU with the flow. However, the score gets ignored after the blamed two commits, which introduced a faster socket selection algorithm for SO_REUSEPORT. This patch introduces a counter of sockets with SO_INCOMING_CPU in a reuseport group to check if we should iterate all sockets to find a proper one. We increment the counter when * calling listen() if the socket has SO_INCOMING_CPU and SO_REUSEPORT * enabling SO_INCOMING_CPU if the socket is in a reuseport group Also, we decrement it when * detaching a socket out of the group to apply SO_INCOMING_CPU to migrated TCP requests * disabling SO_INCOMING_CPU if the socket is in a reuseport group When the counter reaches 0, we can get back to the O(1) selection algorithm. The overall changes are negligible for the non-SO_INCOMING_CPU case, and the only notable thing is that we have to update sk_incomnig_cpu under reuseport_lock. Otherwise, the race prevents transitioning to the O(n) algorithm and results in the wrong socket selection. cpu1 (setsockopt) cpu2 (listen) +-----------------+ +-------------+ lock_sock(sk1) lock_sock(sk2) reuseport_update_incoming_cpu(sk1, val) . | /* set CPU as 0 */ |- WRITE_ONCE(sk1->incoming_cpu, val) | | spin_lock_bh(&reuseport_lock) | reuseport_grow(sk2, reuse) | . | |- more_socks_size = reuse->max_socks * 2U; | |- if (more_socks_size > U16_MAX && | | reuse->num_closed_socks) | | . | | |- RCU_INIT_POINTER(sk1->sk_reuseport_cb, NULL); | | `- __reuseport_detach_closed_sock(sk1, reuse) | | . | | `- reuseport_put_incoming_cpu(sk1, reuse) | | . | | | /* Read shutdown()ed sk1's sk_incoming_cpu | | | * without lock_sock(). | | | */ | | `- if (sk1->sk_incoming_cpu >= 0) | | . | | | /* decrement not-yet-incremented | | | * count, which is never incremented. | | | */ | | `- __reuseport_put_incoming_cpu(reuse); | | | `- spin_lock_bh(&reuseport_lock) | |- spin_lock_bh(&reuseport_lock) | |- reuse = rcu_dereference_protected(sk1->sk_reuseport_cb, ...) |- if (!reuse) | . | | /* Cannot increment reuse->incoming_cpu. */ | `- goto out; | `- spin_unlock_bh(&reuseport_lock) Fixes: e32ea7e74727 ("soreuseport: fast reuseport UDP socket selection") Fixes: c125e80b8868 ("soreuseport: fast reuseport TCP socket selection") Reported-by: Kazuho Oku Signed-off-by: Kuniyuki Iwashima Signed-off-by: Paolo Abeni --- include/net/sock_reuseport.h | 2 + net/core/sock.c | 2 +- net/core/sock_reuseport.c | 94 ++++++++++++++++++++++++++++++++++-- 3 files changed, 92 insertions(+), 6 deletions(-) diff --git a/include/net/sock_reuseport.h b/include/net/sock_reuseport.h index efc9085c6892..6ec140b0a61b 100644 --- a/include/net/sock_reuseport.h +++ b/include/net/sock_reuseport.h @@ -16,6 +16,7 @@ struct sock_reuseport { u16 max_socks; /* length of socks */ u16 num_socks; /* elements in socks */ u16 num_closed_socks; /* closed elements in socks */ + u16 incoming_cpu; /* The last synq overflow event timestamp of this * reuse->socks[] group. */ @@ -58,5 +59,6 @@ static inline bool reuseport_has_conns(struct sock *sk) } void reuseport_has_conns_set(struct sock *sk); +void reuseport_update_incoming_cpu(struct sock *sk, int val); #endif /* _SOCK_REUSEPORT_H */ diff --git a/net/core/sock.c b/net/core/sock.c index 2786c1107e53..4571914a4aa8 100644 --- a/net/core/sock.c +++ b/net/core/sock.c @@ -1436,7 +1436,7 @@ set_sndbuf: break; } case SO_INCOMING_CPU: - WRITE_ONCE(sk->sk_incoming_cpu, val); + reuseport_update_incoming_cpu(sk, val); break; case SO_CNX_ADVICE: diff --git a/net/core/sock_reuseport.c b/net/core/sock_reuseport.c index fb90e1e00773..5a165286e4d8 100644 --- a/net/core/sock_reuseport.c +++ b/net/core/sock_reuseport.c @@ -37,6 +37,70 @@ void reuseport_has_conns_set(struct sock *sk) } EXPORT_SYMBOL(reuseport_has_conns_set); +static void __reuseport_get_incoming_cpu(struct sock_reuseport *reuse) +{ + /* Paired with READ_ONCE() in reuseport_select_sock_by_hash(). */ + WRITE_ONCE(reuse->incoming_cpu, reuse->incoming_cpu + 1); +} + +static void __reuseport_put_incoming_cpu(struct sock_reuseport *reuse) +{ + /* Paired with READ_ONCE() in reuseport_select_sock_by_hash(). */ + WRITE_ONCE(reuse->incoming_cpu, reuse->incoming_cpu - 1); +} + +static void reuseport_get_incoming_cpu(struct sock *sk, struct sock_reuseport *reuse) +{ + if (sk->sk_incoming_cpu >= 0) + __reuseport_get_incoming_cpu(reuse); +} + +static void reuseport_put_incoming_cpu(struct sock *sk, struct sock_reuseport *reuse) +{ + if (sk->sk_incoming_cpu >= 0) + __reuseport_put_incoming_cpu(reuse); +} + +void reuseport_update_incoming_cpu(struct sock *sk, int val) +{ + struct sock_reuseport *reuse; + int old_sk_incoming_cpu; + + if (unlikely(!rcu_access_pointer(sk->sk_reuseport_cb))) { + /* Paired with REAE_ONCE() in sk_incoming_cpu_update() + * and compute_score(). + */ + WRITE_ONCE(sk->sk_incoming_cpu, val); + return; + } + + spin_lock_bh(&reuseport_lock); + + /* This must be done under reuseport_lock to avoid a race with + * reuseport_grow(), which accesses sk->sk_incoming_cpu without + * lock_sock() when detaching a shutdown()ed sk. + * + * Paired with READ_ONCE() in reuseport_select_sock_by_hash(). + */ + old_sk_incoming_cpu = sk->sk_incoming_cpu; + WRITE_ONCE(sk->sk_incoming_cpu, val); + + reuse = rcu_dereference_protected(sk->sk_reuseport_cb, + lockdep_is_held(&reuseport_lock)); + + /* reuseport_grow() has detached a closed sk. */ + if (!reuse) + goto out; + + if (old_sk_incoming_cpu < 0 && val >= 0) + __reuseport_get_incoming_cpu(reuse); + else if (old_sk_incoming_cpu >= 0 && val < 0) + __reuseport_put_incoming_cpu(reuse); + +out: + spin_unlock_bh(&reuseport_lock); +} + static int reuseport_sock_index(struct sock *sk, const struct sock_reuseport *reuse, bool closed) @@ -64,6 +128,7 @@ static void __reuseport_add_sock(struct sock *sk, /* paired with smp_rmb() in reuseport_(select|migrate)_sock() */ smp_wmb(); reuse->num_socks++; + reuseport_get_incoming_cpu(sk, reuse); } static bool __reuseport_detach_sock(struct sock *sk, @@ -76,6 +141,7 @@ static bool __reuseport_detach_sock(struct sock *sk, reuse->socks[i] = reuse->socks[reuse->num_socks - 1]; reuse->num_socks--; + reuseport_put_incoming_cpu(sk, reuse); return true; } @@ -86,6 +152,7 @@ static void __reuseport_add_closed_sock(struct sock *sk, reuse->socks[reuse->max_socks - reuse->num_closed_socks - 1] = sk; /* paired with READ_ONCE() in inet_csk_bind_conflict() */ WRITE_ONCE(reuse->num_closed_socks, reuse->num_closed_socks + 1); + reuseport_get_incoming_cpu(sk, reuse); } static bool __reuseport_detach_closed_sock(struct sock *sk, @@ -99,6 +166,7 @@ static bool __reuseport_detach_closed_sock(struct sock *sk, reuse->socks[i] = reuse->socks[reuse->max_socks - reuse->num_closed_socks]; /* paired with READ_ONCE() in inet_csk_bind_conflict() */ WRITE_ONCE(reuse->num_closed_socks, reuse->num_closed_socks - 1); + reuseport_put_incoming_cpu(sk, reuse); return true; } @@ -166,6 +234,7 @@ int reuseport_alloc(struct sock *sk, bool bind_inany) reuse->bind_inany = bind_inany; reuse->socks[0] = sk; reuse->num_socks = 1; + reuseport_get_incoming_cpu(sk, reuse); rcu_assign_pointer(sk->sk_reuseport_cb, reuse); out: @@ -209,6 +278,7 @@ static struct sock_reuseport *reuseport_grow(struct sock_reuseport *reuse) more_reuse->reuseport_id = reuse->reuseport_id; more_reuse->bind_inany = reuse->bind_inany; more_reuse->has_conns = reuse->has_conns; + more_reuse->incoming_cpu = reuse->incoming_cpu; memcpy(more_reuse->socks, reuse->socks, reuse->num_socks * sizeof(struct sock *)); @@ -458,18 +528,32 @@ static struct sock *run_bpf_filter(struct sock_reuseport *reuse, u16 socks, static struct sock *reuseport_select_sock_by_hash(struct sock_reuseport *reuse, u32 hash, u16 num_socks) { + struct sock *first_valid_sk = NULL; int i, j; i = j = reciprocal_scale(hash, num_socks); - while (reuse->socks[i]->sk_state == TCP_ESTABLISHED) { + do { + struct sock *sk = reuse->socks[i]; + + if (sk->sk_state != TCP_ESTABLISHED) { + /* Paired with WRITE_ONCE() in __reuseport_(get|put)_incoming_cpu(). */ + if (!READ_ONCE(reuse->incoming_cpu)) + return sk; + + /* Paired with WRITE_ONCE() in reuseport_update_incoming_cpu(). */ + if (READ_ONCE(sk->sk_incoming_cpu) == raw_smp_processor_id()) + return sk; + + if (!first_valid_sk) + first_valid_sk = sk; + } + i++; if (i >= num_socks) i = 0; - if (i == j) - return NULL; - } + } while (i != j); - return reuse->socks[i]; + return first_valid_sk; } /** From 6df96146b2025e122447354daf66edbfa88e8a1e Mon Sep 17 00:00:00 2001 From: Kuniyuki Iwashima Date: Fri, 21 Oct 2022 13:44:35 -0700 Subject: [PATCH 0322/2002] selftest: Add test for SO_INCOMING_CPU. Some highly optimised applications use SO_INCOMING_CPU to make them efficient, but they didn't test if it's working correctly by getsockopt() to avoid slowing down. As a result, no one noticed it had been broken for years, so it's a good time to add a test to catch future regression. The test does 1) Create $(nproc) TCP listeners associated with each CPU. 2) Create 32 child sockets for each listener by calling sched_setaffinity() for each CPU. 3) Check if accept()ed sockets' sk_incoming_cpu matches listener's one. If we see -EAGAIN, SO_INCOMING_CPU is broken. However, we might not see any error even if broken; the kernel could miraculously distribute all SYN to correct listeners. Not to let that happen, we must increase the number of clients and CPUs to some extent, so the test requires $(nproc) >= 2 and creates 64 sockets at least. Test: $ nproc 96 $ ./so_incoming_cpu Before the previous patch: # Starting 12 tests from 5 test cases. # RUN so_incoming_cpu.before_reuseport.test1 ... # so_incoming_cpu.c:191:test1:Expected cpu (5) == i (0) # test1: Test terminated by assertion # FAIL so_incoming_cpu.before_reuseport.test1 not ok 1 so_incoming_cpu.before_reuseport.test1 ... # FAILED: 0 / 12 tests passed. # Totals: pass:0 fail:12 xfail:0 xpass:0 skip:0 error:0 After: # Starting 12 tests from 5 test cases. # RUN so_incoming_cpu.before_reuseport.test1 ... # so_incoming_cpu.c:199:test1:SO_INCOMING_CPU is very likely to be working correctly with 3072 sockets. # OK so_incoming_cpu.before_reuseport.test1 ok 1 so_incoming_cpu.before_reuseport.test1 ... # PASSED: 12 / 12 tests passed. # Totals: pass:12 fail:0 xfail:0 xpass:0 skip:0 error:0 Signed-off-by: Kuniyuki Iwashima Signed-off-by: Paolo Abeni --- tools/testing/selftests/net/.gitignore | 1 + tools/testing/selftests/net/Makefile | 1 + tools/testing/selftests/net/so_incoming_cpu.c | 242 ++++++++++++++++++ 3 files changed, 244 insertions(+) create mode 100644 tools/testing/selftests/net/so_incoming_cpu.c diff --git a/tools/testing/selftests/net/.gitignore b/tools/testing/selftests/net/.gitignore index 3d7adee7a3e6..ff8807cc9c2e 100644 --- a/tools/testing/selftests/net/.gitignore +++ b/tools/testing/selftests/net/.gitignore @@ -25,6 +25,7 @@ rxtimestamp sk_bind_sendto_listen sk_connect_zero_addr socket +so_incoming_cpu so_netns_cookie so_txtime stress_reuseport_listen diff --git a/tools/testing/selftests/net/Makefile b/tools/testing/selftests/net/Makefile index 69c58362c0ed..cec4800cb017 100644 --- a/tools/testing/selftests/net/Makefile +++ b/tools/testing/selftests/net/Makefile @@ -71,6 +71,7 @@ TEST_GEN_FILES += bind_bhash TEST_GEN_PROGS += sk_bind_sendto_listen TEST_GEN_PROGS += sk_connect_zero_addr TEST_PROGS += test_ingress_egress_chaining.sh +TEST_GEN_PROGS += so_incoming_cpu TEST_FILES := settings diff --git a/tools/testing/selftests/net/so_incoming_cpu.c b/tools/testing/selftests/net/so_incoming_cpu.c new file mode 100644 index 000000000000..0e04f9fef986 --- /dev/null +++ b/tools/testing/selftests/net/so_incoming_cpu.c @@ -0,0 +1,242 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright Amazon.com Inc. or its affiliates. */ +#define _GNU_SOURCE +#include + +#include +#include +#include + +#include "../kselftest_harness.h" + +#define CLIENT_PER_SERVER 32 /* More sockets, more reliable */ +#define NR_SERVER self->nproc +#define NR_CLIENT (CLIENT_PER_SERVER * NR_SERVER) + +FIXTURE(so_incoming_cpu) +{ + int nproc; + int *servers; + union { + struct sockaddr addr; + struct sockaddr_in in_addr; + }; + socklen_t addrlen; +}; + +enum when_to_set { + BEFORE_REUSEPORT, + BEFORE_LISTEN, + AFTER_LISTEN, + AFTER_ALL_LISTEN, +}; + +FIXTURE_VARIANT(so_incoming_cpu) +{ + int when_to_set; +}; + +FIXTURE_VARIANT_ADD(so_incoming_cpu, before_reuseport) +{ + .when_to_set = BEFORE_REUSEPORT, +}; + +FIXTURE_VARIANT_ADD(so_incoming_cpu, before_listen) +{ + .when_to_set = BEFORE_LISTEN, +}; + +FIXTURE_VARIANT_ADD(so_incoming_cpu, after_listen) +{ + .when_to_set = AFTER_LISTEN, +}; + +FIXTURE_VARIANT_ADD(so_incoming_cpu, after_all_listen) +{ + .when_to_set = AFTER_ALL_LISTEN, +}; + +FIXTURE_SETUP(so_incoming_cpu) +{ + self->nproc = get_nprocs(); + ASSERT_LE(2, self->nproc); + + self->servers = malloc(sizeof(int) * NR_SERVER); + ASSERT_NE(self->servers, NULL); + + self->in_addr.sin_family = AF_INET; + self->in_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + self->in_addr.sin_port = htons(0); + self->addrlen = sizeof(struct sockaddr_in); +} + +FIXTURE_TEARDOWN(so_incoming_cpu) +{ + int i; + + for (i = 0; i < NR_SERVER; i++) + close(self->servers[i]); + + free(self->servers); +} + +void set_so_incoming_cpu(struct __test_metadata *_metadata, int fd, int cpu) +{ + int ret; + + ret = setsockopt(fd, SOL_SOCKET, SO_INCOMING_CPU, &cpu, sizeof(int)); + ASSERT_EQ(ret, 0); +} + +int create_server(struct __test_metadata *_metadata, + FIXTURE_DATA(so_incoming_cpu) *self, + const FIXTURE_VARIANT(so_incoming_cpu) *variant, + int cpu) +{ + int fd, ret; + + fd = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0); + ASSERT_NE(fd, -1); + + if (variant->when_to_set == BEFORE_REUSEPORT) + set_so_incoming_cpu(_metadata, fd, cpu); + + ret = setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &(int){1}, sizeof(int)); + ASSERT_EQ(ret, 0); + + ret = bind(fd, &self->addr, self->addrlen); + ASSERT_EQ(ret, 0); + + if (variant->when_to_set == BEFORE_LISTEN) + set_so_incoming_cpu(_metadata, fd, cpu); + + /* We don't use CLIENT_PER_SERVER here not to block + * this test at connect() if SO_INCOMING_CPU is broken. + */ + ret = listen(fd, NR_CLIENT); + ASSERT_EQ(ret, 0); + + if (variant->when_to_set == AFTER_LISTEN) + set_so_incoming_cpu(_metadata, fd, cpu); + + return fd; +} + +void create_servers(struct __test_metadata *_metadata, + FIXTURE_DATA(so_incoming_cpu) *self, + const FIXTURE_VARIANT(so_incoming_cpu) *variant) +{ + int i, ret; + + for (i = 0; i < NR_SERVER; i++) { + self->servers[i] = create_server(_metadata, self, variant, i); + + if (i == 0) { + ret = getsockname(self->servers[i], &self->addr, &self->addrlen); + ASSERT_EQ(ret, 0); + } + } + + if (variant->when_to_set == AFTER_ALL_LISTEN) { + for (i = 0; i < NR_SERVER; i++) + set_so_incoming_cpu(_metadata, self->servers[i], i); + } +} + +void create_clients(struct __test_metadata *_metadata, + FIXTURE_DATA(so_incoming_cpu) *self) +{ + cpu_set_t cpu_set; + int i, j, fd, ret; + + for (i = 0; i < NR_SERVER; i++) { + CPU_ZERO(&cpu_set); + + CPU_SET(i, &cpu_set); + ASSERT_EQ(CPU_COUNT(&cpu_set), 1); + ASSERT_NE(CPU_ISSET(i, &cpu_set), 0); + + /* Make sure SYN will be processed on the i-th CPU + * and finally distributed to the i-th listener. + */ + sched_setaffinity(0, sizeof(cpu_set), &cpu_set); + ASSERT_EQ(ret, 0); + + for (j = 0; j < CLIENT_PER_SERVER; j++) { + fd = socket(AF_INET, SOCK_STREAM, 0); + ASSERT_NE(fd, -1); + + ret = connect(fd, &self->addr, self->addrlen); + ASSERT_EQ(ret, 0); + + close(fd); + } + } +} + +void verify_incoming_cpu(struct __test_metadata *_metadata, + FIXTURE_DATA(so_incoming_cpu) *self) +{ + int i, j, fd, cpu, ret, total = 0; + socklen_t len = sizeof(int); + + for (i = 0; i < NR_SERVER; i++) { + for (j = 0; j < CLIENT_PER_SERVER; j++) { + /* If we see -EAGAIN here, SO_INCOMING_CPU is broken */ + fd = accept(self->servers[i], &self->addr, &self->addrlen); + ASSERT_NE(fd, -1); + + ret = getsockopt(fd, SOL_SOCKET, SO_INCOMING_CPU, &cpu, &len); + ASSERT_EQ(ret, 0); + ASSERT_EQ(cpu, i); + + close(fd); + total++; + } + } + + ASSERT_EQ(total, NR_CLIENT); + TH_LOG("SO_INCOMING_CPU is very likely to be " + "working correctly with %d sockets.", total); +} + +TEST_F(so_incoming_cpu, test1) +{ + create_servers(_metadata, self, variant); + create_clients(_metadata, self); + verify_incoming_cpu(_metadata, self); +} + +TEST_F(so_incoming_cpu, test2) +{ + int server; + + create_servers(_metadata, self, variant); + + /* No CPU specified */ + server = create_server(_metadata, self, variant, -1); + close(server); + + create_clients(_metadata, self); + verify_incoming_cpu(_metadata, self); +} + +TEST_F(so_incoming_cpu, test3) +{ + int server, client; + + create_servers(_metadata, self, variant); + + /* No CPU specified */ + server = create_server(_metadata, self, variant, -1); + + create_clients(_metadata, self); + + /* Never receive any requests */ + client = accept(server, &self->addr, &self->addrlen); + ASSERT_EQ(client, -1); + + verify_incoming_cpu(_metadata, self); +} + +TEST_HARNESS_MAIN From d3d429047cc66ff49780c93e4fccd9527723d385 Mon Sep 17 00:00:00 2001 From: Matthieu Baerts Date: Fri, 21 Oct 2022 17:45:03 -0700 Subject: [PATCH 0323/2002] mptcp: sockopt: make 'tcp_fastopen_connect' generic There are other socket options that need to act only on the first subflow, e.g. all TCP_FASTOPEN* socket options. This is similar to the getsockopt version. In the next commit, this new mptcp_setsockopt_first_sf_only() helper is used by other another option. Reviewed-by: Mat Martineau Signed-off-by: Matthieu Baerts Signed-off-by: Mat Martineau Signed-off-by: Paolo Abeni --- net/mptcp/sockopt.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/net/mptcp/sockopt.c b/net/mptcp/sockopt.c index c7cb68c725b2..8d3b09d75c3a 100644 --- a/net/mptcp/sockopt.c +++ b/net/mptcp/sockopt.c @@ -769,17 +769,17 @@ static int mptcp_setsockopt_sol_tcp_defer(struct mptcp_sock *msk, sockptr_t optv return tcp_setsockopt(listener->sk, SOL_TCP, TCP_DEFER_ACCEPT, optval, optlen); } -static int mptcp_setsockopt_sol_tcp_fastopen_connect(struct mptcp_sock *msk, sockptr_t optval, - unsigned int optlen) +static int mptcp_setsockopt_first_sf_only(struct mptcp_sock *msk, int level, int optname, + sockptr_t optval, unsigned int optlen) { struct socket *sock; - /* Limit to first subflow */ + /* Limit to first subflow, before the connection establishment */ sock = __mptcp_nmpc_socket(msk); if (!sock) return -EINVAL; - return tcp_setsockopt(sock->sk, SOL_TCP, TCP_FASTOPEN_CONNECT, optval, optlen); + return tcp_setsockopt(sock->sk, level, optname, optval, optlen); } static int mptcp_setsockopt_sol_tcp(struct mptcp_sock *msk, int optname, @@ -811,7 +811,8 @@ static int mptcp_setsockopt_sol_tcp(struct mptcp_sock *msk, int optname, case TCP_DEFER_ACCEPT: return mptcp_setsockopt_sol_tcp_defer(msk, optval, optlen); case TCP_FASTOPEN_CONNECT: - return mptcp_setsockopt_sol_tcp_fastopen_connect(msk, optval, optlen); + return mptcp_setsockopt_first_sf_only(msk, SOL_TCP, optname, + optval, optlen); } return -EOPNOTSUPP; From e64d4deb4de006faf7613c6603f5bf1bd1844ab3 Mon Sep 17 00:00:00 2001 From: Matthieu Baerts Date: Fri, 21 Oct 2022 17:45:04 -0700 Subject: [PATCH 0324/2002] mptcp: add TCP_FASTOPEN_NO_COOKIE support The goal of this socket option is to configure MPTCP + TFO without cookie per socket. It was already possible to enable TFO without a cookie per netns by setting net.ipv4.tcp_fastopen sysctl knob to the right value. Per route was also supported by setting 'fastopen_no_cookie' option. This patch adds a per socket support like it is possible to do with TCP thanks to TCP_FASTOPEN_NO_COOKIE socket option. The only thing to do here is to relay the request to the first subflow like it is already done for TCP_FASTOPEN_CONNECT. Reviewed-by: Mat Martineau Signed-off-by: Matthieu Baerts Signed-off-by: Mat Martineau Signed-off-by: Paolo Abeni --- net/mptcp/sockopt.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/net/mptcp/sockopt.c b/net/mptcp/sockopt.c index 8d3b09d75c3a..1857281a0dd5 100644 --- a/net/mptcp/sockopt.c +++ b/net/mptcp/sockopt.c @@ -560,6 +560,7 @@ static bool mptcp_supported_sockopt(int level, int optname) case TCP_TX_DELAY: case TCP_INQ: case TCP_FASTOPEN_CONNECT: + case TCP_FASTOPEN_NO_COOKIE: return true; } @@ -568,8 +569,8 @@ static bool mptcp_supported_sockopt(int level, int optname) /* TCP_REPAIR, TCP_REPAIR_QUEUE, TCP_QUEUE_SEQ, TCP_REPAIR_OPTIONS, * TCP_REPAIR_WINDOW are not supported, better avoid this mess */ - /* TCP_FASTOPEN_KEY, TCP_FASTOPEN, TCP_FASTOPEN_NO_COOKIE, - * are not supported fastopen is currently unsupported + /* TCP_FASTOPEN_KEY, TCP_FASTOPEN are not supported because + * fastopen for the listener side is currently unsupported */ } return false; @@ -811,6 +812,7 @@ static int mptcp_setsockopt_sol_tcp(struct mptcp_sock *msk, int optname, case TCP_DEFER_ACCEPT: return mptcp_setsockopt_sol_tcp_defer(msk, optval, optlen); case TCP_FASTOPEN_CONNECT: + case TCP_FASTOPEN_NO_COOKIE: return mptcp_setsockopt_first_sf_only(msk, SOL_TCP, optname, optval, optlen); } @@ -1175,6 +1177,7 @@ static int mptcp_getsockopt_sol_tcp(struct mptcp_sock *msk, int optname, case TCP_CC_INFO: case TCP_DEFER_ACCEPT: case TCP_FASTOPEN_CONNECT: + case TCP_FASTOPEN_NO_COOKIE: return mptcp_getsockopt_first_sf_only(msk, SOL_TCP, optname, optval, optlen); case TCP_INQ: From caea64675d8b91deb59140a92f190124399ccd37 Mon Sep 17 00:00:00 2001 From: Matthieu Baerts Date: Fri, 21 Oct 2022 17:45:05 -0700 Subject: [PATCH 0325/2002] mptcp: sockopt: use new helper for TCP_DEFER_ACCEPT mptcp_setsockopt_sol_tcp_defer() was doing the same thing as mptcp_setsockopt_first_sf_only() except for the returned code in case of error. Ignoring the error is needed to mimic how TCP_DEFER_ACCEPT is handled when used with "plain" TCP sockets. The specific function for TCP_DEFER_ACCEPT can be replaced by the new mptcp_setsockopt_first_sf_only() helper and errors can be ignored to stay compatible with TCP. A bit of cleanup. Suggested-by: Mat Martineau Reviewed-by: Mat Martineau Acked-by: Paolo Abeni Signed-off-by: Matthieu Baerts Signed-off-by: Mat Martineau Signed-off-by: Paolo Abeni --- net/mptcp/sockopt.c | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/net/mptcp/sockopt.c b/net/mptcp/sockopt.c index 1857281a0dd5..f85e9bbfe86f 100644 --- a/net/mptcp/sockopt.c +++ b/net/mptcp/sockopt.c @@ -758,18 +758,6 @@ static int mptcp_setsockopt_v4(struct mptcp_sock *msk, int optname, return -EOPNOTSUPP; } -static int mptcp_setsockopt_sol_tcp_defer(struct mptcp_sock *msk, sockptr_t optval, - unsigned int optlen) -{ - struct socket *listener; - - listener = __mptcp_nmpc_socket(msk); - if (!listener) - return 0; /* TCP_DEFER_ACCEPT does not fail */ - - return tcp_setsockopt(listener->sk, SOL_TCP, TCP_DEFER_ACCEPT, optval, optlen); -} - static int mptcp_setsockopt_first_sf_only(struct mptcp_sock *msk, int level, int optname, sockptr_t optval, unsigned int optlen) { @@ -810,7 +798,9 @@ static int mptcp_setsockopt_sol_tcp(struct mptcp_sock *msk, int optname, case TCP_NODELAY: return mptcp_setsockopt_sol_tcp_nodelay(msk, optval, optlen); case TCP_DEFER_ACCEPT: - return mptcp_setsockopt_sol_tcp_defer(msk, optval, optlen); + /* See tcp.c: TCP_DEFER_ACCEPT does not fail */ + mptcp_setsockopt_first_sf_only(msk, SOL_TCP, optname, optval, optlen); + return 0; case TCP_FASTOPEN_CONNECT: case TCP_FASTOPEN_NO_COOKIE: return mptcp_setsockopt_first_sf_only(msk, SOL_TCP, optname, From d6dd508080a3cdc0ab34ebf66c3734f2dff907ad Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Fri, 21 Oct 2022 19:10:47 -0700 Subject: [PATCH 0326/2002] bnx2: Use kmalloc_size_roundup() to match ksize() usage Round up allocations with kmalloc_size_roundup() so that build_skb()'s use of ksize() is always accurate and no special handling of the memory is needed by KASAN, UBSAN_BOUNDS, nor FORTIFY_SOURCE. Cc: Rasesh Mody Cc: GR-Linux-NIC-Dev@marvell.com Cc: "David S. Miller" Cc: Eric Dumazet Cc: Jakub Kicinski Cc: Paolo Abeni Cc: netdev@vger.kernel.org Signed-off-by: Kees Cook Link: https://lore.kernel.org/r/20221022021004.gonna.489-kees@kernel.org Signed-off-by: Paolo Abeni --- drivers/net/ethernet/broadcom/bnx2.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/broadcom/bnx2.c b/drivers/net/ethernet/broadcom/bnx2.c index fec57f1982c8..dbe310144780 100644 --- a/drivers/net/ethernet/broadcom/bnx2.c +++ b/drivers/net/ethernet/broadcom/bnx2.c @@ -5415,8 +5415,9 @@ bnx2_set_rx_ring_size(struct bnx2 *bp, u32 size) bp->rx_buf_use_size = rx_size; /* hw alignment + build_skb() overhead*/ - bp->rx_buf_size = SKB_DATA_ALIGN(bp->rx_buf_use_size + BNX2_RX_ALIGN) + - NET_SKB_PAD + SKB_DATA_ALIGN(sizeof(struct skb_shared_info)); + bp->rx_buf_size = kmalloc_size_roundup( + SKB_DATA_ALIGN(bp->rx_buf_use_size + BNX2_RX_ALIGN) + + NET_SKB_PAD + SKB_DATA_ALIGN(sizeof(struct skb_shared_info))); bp->rx_jumbo_thresh = rx_size - BNX2_RX_OFFSET; bp->rx_ring_size = size; bp->rx_max_ring = bnx2_find_max_ring(size, BNX2_MAX_RX_RINGS); From ac1f8c049319847b1b4c6b387fdb2e3f7fb84ffc Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Wed, 28 Sep 2022 23:55:06 +0200 Subject: [PATCH 0327/2002] netfilter: nft_payload: move struct nft_payload_set definition where it belongs Not required to expose this header in nf_tables_core.h, move it to where it is used, ie. nft_payload. Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/nf_tables_core.h | 10 ---------- net/netfilter/nft_payload.c | 10 ++++++++++ 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/include/net/netfilter/nf_tables_core.h b/include/net/netfilter/nf_tables_core.h index 1223af68cd9a..990c3767a350 100644 --- a/include/net/netfilter/nf_tables_core.h +++ b/include/net/netfilter/nf_tables_core.h @@ -66,16 +66,6 @@ struct nft_payload { u8 dreg; }; -struct nft_payload_set { - enum nft_payload_bases base:8; - u8 offset; - u8 len; - u8 sreg; - u8 csum_type; - u8 csum_offset; - u8 csum_flags; -}; - extern const struct nft_expr_ops nft_payload_fast_ops; extern const struct nft_expr_ops nft_bitwise_fast_ops; diff --git a/net/netfilter/nft_payload.c b/net/netfilter/nft_payload.c index 088244f9d838..07621d509a68 100644 --- a/net/netfilter/nft_payload.c +++ b/net/netfilter/nft_payload.c @@ -665,6 +665,16 @@ static int nft_payload_csum_inet(struct sk_buff *skb, const u32 *src, return 0; } +struct nft_payload_set { + enum nft_payload_bases base:8; + u8 offset; + u8 len; + u8 sreg; + u8 csum_type; + u8 csum_offset; + u8 csum_flags; +}; + static void nft_payload_set_eval(const struct nft_expr *expr, struct nft_regs *regs, const struct nft_pktinfo *pkt) From e7a1caa67ce62765fe174cae08e537d542bb44f8 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Sat, 15 Oct 2022 00:20:50 +0200 Subject: [PATCH 0328/2002] netfilter: nf_tables: reduce nft_pktinfo by 8 bytes structure is reduced from 32 to 24 bytes. While at it, also check that iphdrlen is sane, this is guaranteed for NFPROTO_IPV4 but not for ingress or bridge, so add checks for this. Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/nf_tables.h | 4 ++-- include/net/netfilter/nf_tables_ipv4.h | 4 ++++ include/net/netfilter/nf_tables_ipv6.h | 6 +++--- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/include/net/netfilter/nf_tables.h b/include/net/netfilter/nf_tables.h index cdb7db9b0e25..f6db510689a8 100644 --- a/include/net/netfilter/nf_tables.h +++ b/include/net/netfilter/nf_tables.h @@ -32,8 +32,8 @@ struct nft_pktinfo { u8 flags; u8 tprot; u16 fragoff; - unsigned int thoff; - unsigned int inneroff; + u16 thoff; + u16 inneroff; }; static inline struct sock *nft_sk(const struct nft_pktinfo *pkt) diff --git a/include/net/netfilter/nf_tables_ipv4.h b/include/net/netfilter/nf_tables_ipv4.h index c4a6147b0ef8..112708f7a6b4 100644 --- a/include/net/netfilter/nf_tables_ipv4.h +++ b/include/net/netfilter/nf_tables_ipv4.h @@ -35,6 +35,8 @@ static inline int __nft_set_pktinfo_ipv4_validate(struct nft_pktinfo *pkt) return -1; else if (len < thoff) return -1; + else if (thoff < sizeof(*iph)) + return -1; pkt->flags = NFT_PKTINFO_L4PROTO; pkt->tprot = iph->protocol; @@ -69,6 +71,8 @@ static inline int nft_set_pktinfo_ipv4_ingress(struct nft_pktinfo *pkt) return -1; } else if (len < thoff) { goto inhdr_error; + } else if (thoff < sizeof(*iph)) { + return -1; } pkt->flags = NFT_PKTINFO_L4PROTO; diff --git a/include/net/netfilter/nf_tables_ipv6.h b/include/net/netfilter/nf_tables_ipv6.h index ec7eaeaf4f04..467d59b9e533 100644 --- a/include/net/netfilter/nf_tables_ipv6.h +++ b/include/net/netfilter/nf_tables_ipv6.h @@ -13,7 +13,7 @@ static inline void nft_set_pktinfo_ipv6(struct nft_pktinfo *pkt) unsigned short frag_off; protohdr = ipv6_find_hdr(pkt->skb, &thoff, -1, &frag_off, &flags); - if (protohdr < 0) { + if (protohdr < 0 || thoff > U16_MAX) { nft_set_pktinfo_unspec(pkt); return; } @@ -47,7 +47,7 @@ static inline int __nft_set_pktinfo_ipv6_validate(struct nft_pktinfo *pkt) return -1; protohdr = ipv6_find_hdr(pkt->skb, &thoff, -1, &frag_off, &flags); - if (protohdr < 0) + if (protohdr < 0 || thoff > U16_MAX) return -1; pkt->flags = NFT_PKTINFO_L4PROTO; @@ -93,7 +93,7 @@ static inline int nft_set_pktinfo_ipv6_ingress(struct nft_pktinfo *pkt) } protohdr = ipv6_find_hdr(pkt->skb, &thoff, -1, &frag_off, &flags); - if (protohdr < 0) + if (protohdr < 0 || thoff > U16_MAX) goto inhdr_error; pkt->flags = NFT_PKTINFO_L4PROTO; From d037abc2414b4539401e0e6aa278bedc4628ad69 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Fri, 21 Oct 2022 16:17:53 +0200 Subject: [PATCH 0329/2002] netfilter: nft_objref: make it builtin nft_objref is needed to reference named objects, it makes no sense to disable it. Before: text data bss dec filename 4014 424 0 4438 nft_objref.o 4174 1128 0 5302 nft_objref.ko 359351 15276 864 375491 nf_tables.ko After: text data bss dec filename 3815 408 0 4223 nft_objref.o 363161 15692 864 379717 nf_tables.ko Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/nf_tables_core.h | 1 + net/netfilter/Kconfig | 6 ------ net/netfilter/Makefile | 4 ++-- net/netfilter/nf_tables_core.c | 1 + net/netfilter/nft_objref.c | 22 +--------------------- 5 files changed, 5 insertions(+), 29 deletions(-) diff --git a/include/net/netfilter/nf_tables_core.h b/include/net/netfilter/nf_tables_core.h index 990c3767a350..83d763631f81 100644 --- a/include/net/netfilter/nf_tables_core.h +++ b/include/net/netfilter/nf_tables_core.h @@ -18,6 +18,7 @@ extern struct nft_expr_type nft_meta_type; extern struct nft_expr_type nft_rt_type; extern struct nft_expr_type nft_exthdr_type; extern struct nft_expr_type nft_last_type; +extern struct nft_expr_type nft_objref_type; #ifdef CONFIG_NETWORK_SECMARK extern struct nft_object_type nft_secmark_obj_type; diff --git a/net/netfilter/Kconfig b/net/netfilter/Kconfig index 4b8d04640ff3..0846bd75b1da 100644 --- a/net/netfilter/Kconfig +++ b/net/netfilter/Kconfig @@ -568,12 +568,6 @@ config NFT_TUNNEL This option adds the "tunnel" expression that you can use to set tunneling policies. -config NFT_OBJREF - tristate "Netfilter nf_tables stateful object reference module" - help - This option adds the "objref" expression that allows you to refer to - stateful objects, such as counters and quotas. - config NFT_QUEUE depends on NETFILTER_NETLINK_QUEUE tristate "Netfilter nf_tables queue module" diff --git a/net/netfilter/Makefile b/net/netfilter/Makefile index 0f060d100880..7a6b518ba2b4 100644 --- a/net/netfilter/Makefile +++ b/net/netfilter/Makefile @@ -86,7 +86,8 @@ nf_tables-objs := nf_tables_core.o nf_tables_api.o nft_chain_filter.o \ nf_tables_trace.o nft_immediate.o nft_cmp.o nft_range.o \ nft_bitwise.o nft_byteorder.o nft_payload.o nft_lookup.o \ nft_dynset.o nft_meta.o nft_rt.o nft_exthdr.o nft_last.o \ - nft_counter.o nft_chain_route.o nf_tables_offload.o \ + nft_counter.o nft_objref.o \ + nft_chain_route.o nf_tables_offload.o \ nft_set_hash.o nft_set_bitmap.o nft_set_rbtree.o \ nft_set_pipapo.o @@ -104,7 +105,6 @@ obj-$(CONFIG_NFT_CT) += nft_ct.o obj-$(CONFIG_NFT_FLOW_OFFLOAD) += nft_flow_offload.o obj-$(CONFIG_NFT_LIMIT) += nft_limit.o obj-$(CONFIG_NFT_NAT) += nft_nat.o -obj-$(CONFIG_NFT_OBJREF) += nft_objref.o obj-$(CONFIG_NFT_QUEUE) += nft_queue.o obj-$(CONFIG_NFT_QUOTA) += nft_quota.o obj-$(CONFIG_NFT_REJECT) += nft_reject.o diff --git a/net/netfilter/nf_tables_core.c b/net/netfilter/nf_tables_core.c index cee3e4e905ec..6dcead50208c 100644 --- a/net/netfilter/nf_tables_core.c +++ b/net/netfilter/nf_tables_core.c @@ -340,6 +340,7 @@ static struct nft_expr_type *nft_basic_types[] = { &nft_exthdr_type, &nft_last_type, &nft_counter_type, + &nft_objref_type, }; static struct nft_object_type *nft_basic_objects[] = { diff --git a/net/netfilter/nft_objref.c b/net/netfilter/nft_objref.c index 5d8d91b3904d..74e0eea4abac 100644 --- a/net/netfilter/nft_objref.c +++ b/net/netfilter/nft_objref.c @@ -82,7 +82,6 @@ static void nft_objref_activate(const struct nft_ctx *ctx, obj->use++; } -static struct nft_expr_type nft_objref_type; static const struct nft_expr_ops nft_objref_ops = { .type = &nft_objref_type, .size = NFT_EXPR_SIZE(sizeof(struct nft_object *)), @@ -195,7 +194,6 @@ static void nft_objref_map_destroy(const struct nft_ctx *ctx, nf_tables_destroy_set(ctx, priv->set); } -static struct nft_expr_type nft_objref_type; static const struct nft_expr_ops nft_objref_map_ops = { .type = &nft_objref_type, .size = NFT_EXPR_SIZE(sizeof(struct nft_objref_map)), @@ -233,28 +231,10 @@ static const struct nla_policy nft_objref_policy[NFTA_OBJREF_MAX + 1] = { [NFTA_OBJREF_SET_ID] = { .type = NLA_U32 }, }; -static struct nft_expr_type nft_objref_type __read_mostly = { +struct nft_expr_type nft_objref_type __read_mostly = { .name = "objref", .select_ops = nft_objref_select_ops, .policy = nft_objref_policy, .maxattr = NFTA_OBJREF_MAX, .owner = THIS_MODULE, }; - -static int __init nft_objref_module_init(void) -{ - return nft_register_expr(&nft_objref_type); -} - -static void __exit nft_objref_module_exit(void) -{ - nft_unregister_expr(&nft_objref_type); -} - -module_init(nft_objref_module_init); -module_exit(nft_objref_module_exit); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Pablo Neira Ayuso "); -MODULE_ALIAS_NFT_EXPR("objref"); -MODULE_DESCRIPTION("nftables stateful object reference module"); From c247897d7c194e6fb4a7c8314af2226bec017a59 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Mon, 17 Oct 2022 13:03:29 +0200 Subject: [PATCH 0330/2002] netfilter: nft_payload: access GRE payload via inner offset Parse GRE v0 packets to properly set up inner offset, this allow for matching on inner headers. Signed-off-by: Pablo Neira Ayuso --- net/netfilter/nft_payload.c | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/net/netfilter/nft_payload.c b/net/netfilter/nft_payload.c index 07621d509a68..03a1f271bf4f 100644 --- a/net/netfilter/nft_payload.c +++ b/net/netfilter/nft_payload.c @@ -19,6 +19,7 @@ /* For layer 4 checksum field offset. */ #include #include +#include #include #include #include @@ -100,6 +101,37 @@ static int __nft_payload_inner_offset(struct nft_pktinfo *pkt) pkt->inneroff = thoff + __tcp_hdrlen(th); } break; + case IPPROTO_GRE: { + u32 offset = sizeof(struct gre_base_hdr), version; + struct gre_base_hdr *gre, _gre; + + gre = skb_header_pointer(pkt->skb, thoff, sizeof(_gre), &_gre); + if (!gre) + return -1; + + version = gre->flags & GRE_VERSION; + switch (version) { + case GRE_VERSION_0: + if (gre->flags & GRE_ROUTING) + return -1; + + if (gre->flags & GRE_CSUM) { + offset += sizeof_field(struct gre_full_hdr, csum) + + sizeof_field(struct gre_full_hdr, reserved1); + } + if (gre->flags & GRE_KEY) + offset += sizeof_field(struct gre_full_hdr, key); + + if (gre->flags & GRE_SEQ) + offset += sizeof_field(struct gre_full_hdr, seq); + break; + default: + return -1; + } + + pkt->inneroff = thoff + offset; + } + break; default: return -1; } From 3927ce8850cacc1790cd4c5d02ca42e24df9fa6b Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Mon, 17 Oct 2022 13:03:30 +0200 Subject: [PATCH 0331/2002] netfilter: nft_payload: access ipip payload for inner offset ipip is an special case, transport and inner header offset are set to the same offset to use the upcoming inner expression for matching on inner tunnel headers. Signed-off-by: Pablo Neira Ayuso --- net/netfilter/nft_payload.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/netfilter/nft_payload.c b/net/netfilter/nft_payload.c index 03a1f271bf4f..84b490d6cc75 100644 --- a/net/netfilter/nft_payload.c +++ b/net/netfilter/nft_payload.c @@ -132,6 +132,9 @@ static int __nft_payload_inner_offset(struct nft_pktinfo *pkt) pkt->inneroff = thoff + offset; } break; + case IPPROTO_IPIP: + pkt->inneroff = thoff; + break; default: return -1; } From 3a07327d10a09379315c844c63f27941f5081e0a Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Tue, 25 Oct 2022 13:48:15 +0200 Subject: [PATCH 0332/2002] netfilter: nft_inner: support for inner tunnel header matching This new expression allows you to match on the inner headers that are encapsulated by any of the existing tunneling protocols. This expression parses the inner packet to set the link, network and transport offsets, so the existing expressions (with a few updates) can be reused to match on the inner headers. The inner expression supports for different tunnel combinations such as: - ethernet frame over IPv4/IPv6 packet, eg. VxLAN. - IPv4/IPv6 packet over IPv4/IPv6 packet, eg. IPIP. - IPv4/IPv6 packet over IPv4/IPv6 + transport header, eg. GRE. - transport header (ESP or SCTP) over transport header (usually UDP) The following fields are used to describe the tunnel protocol: - flags, which describe how to parse the inner headers: NFT_PAYLOAD_CTX_INNER_TUN, the tunnel provides its own header. NFT_PAYLOAD_CTX_INNER_ETHER, the ethernet frame is available as inner header. NFT_PAYLOAD_CTX_INNER_NH, the network header is available as inner header. NFT_PAYLOAD_CTX_INNER_TH, the transport header is available as inner header. For example, VxLAN sets on all of these flags. While GRE only sets on NFT_PAYLOAD_CTX_INNER_NH and NFT_PAYLOAD_CTX_INNER_TH. Then, ESP over UDP only sets on NFT_PAYLOAD_CTX_INNER_TH. The tunnel description is composed of the following attributes: - header size: in case the tunnel comes with its own header, eg. VxLAN. - type: this provides a hint to userspace on how to delinearize the rule. This is useful for VxLAN and Geneve since they run over UDP, since transport does not provide a hint. This is also useful in case hardware offload is ever supported. The type is not currently interpreted by the kernel. - expression: currently only payload supported. Follow up patch adds also inner meta support which is required by autogenerated dependencies. The exthdr expression should be supported too at some point. There is a new inner_ops operation that needs to be set on to allow to use an existing expression from the inner expression. This patch adds a new NFT_PAYLOAD_TUN_HEADER base which allows to match on the tunnel header fields, eg. vxlan vni. The payload expression is embedded into nft_inner private area and this private data area is passed to the payload inner eval function via direct call. Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/nf_tables.h | 5 + include/net/netfilter/nf_tables_core.h | 24 ++ include/uapi/linux/netfilter/nf_tables.h | 26 ++ net/netfilter/Makefile | 2 +- net/netfilter/nf_tables_api.c | 37 +++ net/netfilter/nf_tables_core.c | 1 + net/netfilter/nft_inner.c | 336 +++++++++++++++++++++++ net/netfilter/nft_payload.c | 89 +++++- 8 files changed, 518 insertions(+), 2 deletions(-) create mode 100644 net/netfilter/nft_inner.c diff --git a/include/net/netfilter/nf_tables.h b/include/net/netfilter/nf_tables.h index f6db510689a8..2dbfe7524a7e 100644 --- a/include/net/netfilter/nf_tables.h +++ b/include/net/netfilter/nf_tables.h @@ -375,6 +375,10 @@ static inline void *nft_expr_priv(const struct nft_expr *expr) return (void *)expr->data; } +struct nft_expr_info; + +int nft_expr_inner_parse(const struct nft_ctx *ctx, const struct nlattr *nla, + struct nft_expr_info *info); int nft_expr_clone(struct nft_expr *dst, struct nft_expr *src); void nft_expr_destroy(const struct nft_ctx *ctx, struct nft_expr *expr); int nft_expr_dump(struct sk_buff *skb, unsigned int attr, @@ -864,6 +868,7 @@ struct nft_expr_type { const struct nlattr * const tb[]); void (*release_ops)(const struct nft_expr_ops *ops); const struct nft_expr_ops *ops; + const struct nft_expr_ops *inner_ops; struct list_head list; const char *name; struct module *owner; diff --git a/include/net/netfilter/nf_tables_core.h b/include/net/netfilter/nf_tables_core.h index 83d763631f81..be2b2b5d0a52 100644 --- a/include/net/netfilter/nf_tables_core.h +++ b/include/net/netfilter/nf_tables_core.h @@ -19,6 +19,7 @@ extern struct nft_expr_type nft_rt_type; extern struct nft_expr_type nft_exthdr_type; extern struct nft_expr_type nft_last_type; extern struct nft_expr_type nft_objref_type; +extern struct nft_expr_type nft_inner_type; #ifdef CONFIG_NETWORK_SECMARK extern struct nft_object_type nft_secmark_obj_type; @@ -139,4 +140,27 @@ void nft_rt_get_eval(const struct nft_expr *expr, struct nft_regs *regs, const struct nft_pktinfo *pkt); void nft_counter_eval(const struct nft_expr *expr, struct nft_regs *regs, const struct nft_pktinfo *pkt); + +enum { + NFT_PAYLOAD_CTX_INNER_TUN = (1 << 0), + NFT_PAYLOAD_CTX_INNER_LL = (1 << 1), + NFT_PAYLOAD_CTX_INNER_NH = (1 << 2), + NFT_PAYLOAD_CTX_INNER_TH = (1 << 3), +}; + +struct nft_inner_tun_ctx { + u16 inner_tunoff; + u16 inner_lloff; + u16 inner_nhoff; + u16 inner_thoff; + __be16 llproto; + u8 l4proto; + u8 flags; +}; + +int nft_payload_inner_offset(const struct nft_pktinfo *pkt); +void nft_payload_inner_eval(const struct nft_expr *expr, struct nft_regs *regs, + const struct nft_pktinfo *pkt, + struct nft_inner_tun_ctx *ctx); + #endif /* _NET_NF_TABLES_CORE_H */ diff --git a/include/uapi/linux/netfilter/nf_tables.h b/include/uapi/linux/netfilter/nf_tables.h index 466fd3f4447c..05a15dce8271 100644 --- a/include/uapi/linux/netfilter/nf_tables.h +++ b/include/uapi/linux/netfilter/nf_tables.h @@ -760,6 +760,7 @@ enum nft_payload_bases { NFT_PAYLOAD_NETWORK_HEADER, NFT_PAYLOAD_TRANSPORT_HEADER, NFT_PAYLOAD_INNER_HEADER, + NFT_PAYLOAD_TUN_HEADER, }; /** @@ -779,6 +780,31 @@ enum nft_payload_csum_flags { NFT_PAYLOAD_L4CSUM_PSEUDOHDR = (1 << 0), }; +enum nft_inner_type { + NFT_INNER_UNSPEC = 0, + NFT_INNER_VXLAN, +}; + +enum nft_inner_flags { + NFT_INNER_HDRSIZE = (1 << 0), + NFT_INNER_LL = (1 << 1), + NFT_INNER_NH = (1 << 2), + NFT_INNER_TH = (1 << 3), +}; +#define NFT_INNER_MASK (NFT_INNER_HDRSIZE | NFT_INNER_LL | \ + NFT_INNER_NH | NFT_INNER_TH) + +enum nft_inner_attributes { + NFTA_INNER_UNSPEC, + NFTA_INNER_NUM, + NFTA_INNER_TYPE, + NFTA_INNER_FLAGS, + NFTA_INNER_HDRSIZE, + NFTA_INNER_EXPR, + __NFTA_INNER_MAX +}; +#define NFTA_INNER_MAX (__NFTA_INNER_MAX - 1) + /** * enum nft_payload_attributes - nf_tables payload expression netlink attributes * diff --git a/net/netfilter/Makefile b/net/netfilter/Makefile index 7a6b518ba2b4..1d4db1943936 100644 --- a/net/netfilter/Makefile +++ b/net/netfilter/Makefile @@ -86,7 +86,7 @@ nf_tables-objs := nf_tables_core.o nf_tables_api.o nft_chain_filter.o \ nf_tables_trace.o nft_immediate.o nft_cmp.o nft_range.o \ nft_bitwise.o nft_byteorder.o nft_payload.o nft_lookup.o \ nft_dynset.o nft_meta.o nft_rt.o nft_exthdr.o nft_last.o \ - nft_counter.o nft_objref.o \ + nft_counter.o nft_objref.o nft_inner.o \ nft_chain_route.o nf_tables_offload.o \ nft_set_hash.o nft_set_bitmap.o nft_set_rbtree.o \ nft_set_pipapo.o diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c index 58d9cbc9ccdc..6b79f5e18f08 100644 --- a/net/netfilter/nf_tables_api.c +++ b/net/netfilter/nf_tables_api.c @@ -2857,6 +2857,43 @@ err1: return err; } +int nft_expr_inner_parse(const struct nft_ctx *ctx, const struct nlattr *nla, + struct nft_expr_info *info) +{ + struct nlattr *tb[NFTA_EXPR_MAX + 1]; + const struct nft_expr_type *type; + int err; + + err = nla_parse_nested_deprecated(tb, NFTA_EXPR_MAX, nla, + nft_expr_policy, NULL); + if (err < 0) + return err; + + if (!tb[NFTA_EXPR_DATA]) + return -EINVAL; + + type = __nft_expr_type_get(ctx->family, tb[NFTA_EXPR_NAME]); + if (IS_ERR(type)) + return PTR_ERR(type); + + if (!type->inner_ops) + return -EOPNOTSUPP; + + err = nla_parse_nested_deprecated(info->tb, type->maxattr, + tb[NFTA_EXPR_DATA], + type->policy, NULL); + if (err < 0) + goto err_nla_parse; + + info->attr = nla; + info->ops = type->inner_ops; + + return 0; + +err_nla_parse: + return err; +} + static int nf_tables_newexpr(const struct nft_ctx *ctx, const struct nft_expr_info *expr_info, struct nft_expr *expr) diff --git a/net/netfilter/nf_tables_core.c b/net/netfilter/nf_tables_core.c index 6dcead50208c..709a736c301c 100644 --- a/net/netfilter/nf_tables_core.c +++ b/net/netfilter/nf_tables_core.c @@ -341,6 +341,7 @@ static struct nft_expr_type *nft_basic_types[] = { &nft_last_type, &nft_counter_type, &nft_objref_type, + &nft_inner_type, }; static struct nft_object_type *nft_basic_objects[] = { diff --git a/net/netfilter/nft_inner.c b/net/netfilter/nft_inner.c new file mode 100644 index 000000000000..1e4079b5b431 --- /dev/null +++ b/net/netfilter/nft_inner.c @@ -0,0 +1,336 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2022 Pablo Neira Ayuso + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Same layout as nft_expr but it embeds the private expression data area. */ +struct __nft_expr { + const struct nft_expr_ops *ops; + union { + struct nft_payload payload; + } __attribute__((aligned(__alignof__(u64)))); +}; + +enum { + NFT_INNER_EXPR_PAYLOAD, +}; + +struct nft_inner { + u8 flags; + u8 hdrsize; + u8 type; + u8 expr_type; + + struct __nft_expr expr; +}; + +static int nft_inner_parse_l2l3(const struct nft_inner *priv, + const struct nft_pktinfo *pkt, + struct nft_inner_tun_ctx *ctx, u32 off) +{ + __be16 llproto, outer_llproto; + u32 nhoff, thoff; + + if (priv->flags & NFT_INNER_LL) { + struct vlan_ethhdr *veth, _veth; + struct ethhdr *eth, _eth; + u32 hdrsize; + + eth = skb_header_pointer(pkt->skb, off, sizeof(_eth), &_eth); + if (!eth) + return -1; + + switch (eth->h_proto) { + case htons(ETH_P_IP): + case htons(ETH_P_IPV6): + llproto = eth->h_proto; + hdrsize = sizeof(_eth); + break; + case htons(ETH_P_8021Q): + veth = skb_header_pointer(pkt->skb, off, sizeof(_veth), &_veth); + if (!eth) + return -1; + + outer_llproto = veth->h_vlan_encapsulated_proto; + llproto = veth->h_vlan_proto; + hdrsize = sizeof(_veth); + break; + default: + return -1; + } + + ctx->inner_lloff = off; + ctx->flags |= NFT_PAYLOAD_CTX_INNER_LL; + off += hdrsize; + } else { + struct iphdr *iph; + u32 _version; + + iph = skb_header_pointer(pkt->skb, off, sizeof(_version), &_version); + if (!iph) + return -1; + + switch (iph->version) { + case 4: + llproto = htons(ETH_P_IP); + break; + case 6: + llproto = htons(ETH_P_IPV6); + break; + default: + return -1; + } + } + + ctx->llproto = llproto; + if (llproto == htons(ETH_P_8021Q)) + llproto = outer_llproto; + + nhoff = off; + + switch (llproto) { + case htons(ETH_P_IP): { + struct iphdr *iph, _iph; + + iph = skb_header_pointer(pkt->skb, nhoff, sizeof(_iph), &_iph); + if (!iph) + return -1; + + if (iph->ihl < 5 || iph->version != 4) + return -1; + + ctx->inner_nhoff = nhoff; + ctx->flags |= NFT_PAYLOAD_CTX_INNER_NH; + + thoff = nhoff + (iph->ihl * 4); + if ((ntohs(iph->frag_off) & IP_OFFSET) == 0) { + ctx->flags |= NFT_PAYLOAD_CTX_INNER_TH; + ctx->inner_thoff = thoff; + ctx->l4proto = iph->protocol; + } + } + break; + case htons(ETH_P_IPV6): { + struct ipv6hdr *ip6h, _ip6h; + int fh_flags = IP6_FH_F_AUTH; + unsigned short fragoff; + int l4proto; + + ip6h = skb_header_pointer(pkt->skb, nhoff, sizeof(_ip6h), &_ip6h); + if (!ip6h) + return -1; + + if (ip6h->version != 6) + return -1; + + ctx->inner_nhoff = nhoff; + ctx->flags |= NFT_PAYLOAD_CTX_INNER_NH; + + thoff = nhoff; + l4proto = ipv6_find_hdr(pkt->skb, &thoff, -1, &fragoff, &fh_flags); + if (l4proto < 0 || thoff > U16_MAX) + return -1; + + if (fragoff == 0) { + thoff = nhoff + sizeof(_ip6h); + ctx->flags |= NFT_PAYLOAD_CTX_INNER_TH; + ctx->inner_thoff = thoff; + ctx->l4proto = l4proto; + } + } + break; + default: + return -1; + } + + return 0; +} + +static int nft_inner_parse_tunhdr(const struct nft_inner *priv, + const struct nft_pktinfo *pkt, + struct nft_inner_tun_ctx *ctx, u32 *off) +{ + if (pkt->tprot != IPPROTO_UDP || + pkt->tprot != IPPROTO_GRE) + return -1; + + ctx->inner_tunoff = *off; + ctx->flags |= NFT_PAYLOAD_CTX_INNER_TUN; + *off += priv->hdrsize; + + return 0; +} + +static int nft_inner_parse(const struct nft_inner *priv, + const struct nft_pktinfo *pkt, + struct nft_inner_tun_ctx *tun_ctx) +{ + struct nft_inner_tun_ctx ctx = {}; + u32 off = pkt->inneroff; + + if (priv->flags & NFT_INNER_HDRSIZE && + nft_inner_parse_tunhdr(priv, pkt, &ctx, &off) < 0) + return -1; + + if (priv->flags & (NFT_INNER_LL | NFT_INNER_NH)) { + if (nft_inner_parse_l2l3(priv, pkt, &ctx, off) < 0) + return -1; + } else if (priv->flags & NFT_INNER_TH) { + ctx.inner_thoff = off; + ctx.flags |= NFT_PAYLOAD_CTX_INNER_TH; + } + + *tun_ctx = ctx; + + return 0; +} + +static void nft_inner_eval(const struct nft_expr *expr, struct nft_regs *regs, + const struct nft_pktinfo *pkt) +{ + const struct nft_inner *priv = nft_expr_priv(expr); + struct nft_inner_tun_ctx tun_ctx = {}; + + if (nft_payload_inner_offset(pkt) < 0) + goto err; + + if (nft_inner_parse(priv, pkt, &tun_ctx) < 0) + goto err; + + switch (priv->expr_type) { + case NFT_INNER_EXPR_PAYLOAD: + nft_payload_inner_eval((struct nft_expr *)&priv->expr, regs, pkt, &tun_ctx); + break; + default: + WARN_ON_ONCE(1); + goto err; + } + return; +err: + regs->verdict.code = NFT_BREAK; +} + +static const struct nla_policy nft_inner_policy[NFTA_INNER_MAX + 1] = { + [NFTA_INNER_NUM] = { .type = NLA_U32 }, + [NFTA_INNER_FLAGS] = { .type = NLA_U32 }, + [NFTA_INNER_HDRSIZE] = { .type = NLA_U32 }, + [NFTA_INNER_TYPE] = { .type = NLA_U32 }, + [NFTA_INNER_EXPR] = { .type = NLA_NESTED }, +}; + +struct nft_expr_info { + const struct nft_expr_ops *ops; + const struct nlattr *attr; + struct nlattr *tb[NFT_EXPR_MAXATTR + 1]; +}; + +static int nft_inner_init(const struct nft_ctx *ctx, + const struct nft_expr *expr, + const struct nlattr * const tb[]) +{ + struct nft_inner *priv = nft_expr_priv(expr); + u32 flags, hdrsize, type, num; + struct nft_expr_info expr_info; + int err; + + if (!tb[NFTA_INNER_FLAGS] || + !tb[NFTA_INNER_HDRSIZE] || + !tb[NFTA_INNER_TYPE] || + !tb[NFTA_INNER_EXPR]) + return -EINVAL; + + flags = ntohl(nla_get_be32(tb[NFTA_INNER_FLAGS])); + if (flags & ~NFT_INNER_MASK) + return -EOPNOTSUPP; + + num = ntohl(nla_get_be32(tb[NFTA_INNER_NUM])); + if (num != 0) + return -EOPNOTSUPP; + + hdrsize = ntohl(nla_get_be32(tb[NFTA_INNER_HDRSIZE])); + type = ntohl(nla_get_be32(tb[NFTA_INNER_TYPE])); + + if (type > U8_MAX) + return -EINVAL; + + if (flags & NFT_INNER_HDRSIZE) { + if (hdrsize == 0 || hdrsize > 64) + return -EOPNOTSUPP; + } + + priv->flags = flags; + priv->hdrsize = hdrsize; + priv->type = type; + + err = nft_expr_inner_parse(ctx, tb[NFTA_INNER_EXPR], &expr_info); + if (err < 0) + return err; + + priv->expr.ops = expr_info.ops; + + if (!strcmp(expr_info.ops->type->name, "payload")) + priv->expr_type = NFT_INNER_EXPR_PAYLOAD; + else + return -EINVAL; + + err = expr_info.ops->init(ctx, (struct nft_expr *)&priv->expr, + (const struct nlattr * const*)expr_info.tb); + if (err < 0) + return err; + + return 0; +} + +static int nft_inner_dump(struct sk_buff *skb, const struct nft_expr *expr) +{ + const struct nft_inner *priv = nft_expr_priv(expr); + + if (nla_put_be32(skb, NFTA_INNER_NUM, htonl(0)) || + nla_put_be32(skb, NFTA_INNER_TYPE, htonl(priv->type)) || + nla_put_be32(skb, NFTA_INNER_FLAGS, htonl(priv->flags)) || + nla_put_be32(skb, NFTA_INNER_HDRSIZE, htonl(priv->hdrsize))) + goto nla_put_failure; + + if (nft_expr_dump(skb, NFTA_INNER_EXPR, + (struct nft_expr *)&priv->expr) < 0) + goto nla_put_failure; + + return 0; + +nla_put_failure: + return -1; +} + +static const struct nft_expr_ops nft_inner_ops = { + .type = &nft_inner_type, + .size = NFT_EXPR_SIZE(sizeof(struct nft_inner)), + .eval = nft_inner_eval, + .init = nft_inner_init, + .dump = nft_inner_dump, +}; + +struct nft_expr_type nft_inner_type __read_mostly = { + .name = "inner", + .ops = &nft_inner_ops, + .policy = nft_inner_policy, + .maxattr = NFTA_INNER_MAX, + .owner = THIS_MODULE, +}; diff --git a/net/netfilter/nft_payload.c b/net/netfilter/nft_payload.c index 84b490d6cc75..9d2ac764a14c 100644 --- a/net/netfilter/nft_payload.c +++ b/net/netfilter/nft_payload.c @@ -144,7 +144,7 @@ static int __nft_payload_inner_offset(struct nft_pktinfo *pkt) return 0; } -static int nft_payload_inner_offset(const struct nft_pktinfo *pkt) +int nft_payload_inner_offset(const struct nft_pktinfo *pkt) { if (!(pkt->flags & NFT_PKTINFO_INNER) && __nft_payload_inner_offset((struct nft_pktinfo *)pkt) < 0) @@ -587,6 +587,92 @@ const struct nft_expr_ops nft_payload_fast_ops = { .offload = nft_payload_offload, }; +void nft_payload_inner_eval(const struct nft_expr *expr, struct nft_regs *regs, + const struct nft_pktinfo *pkt, + struct nft_inner_tun_ctx *tun_ctx) +{ + const struct nft_payload *priv = nft_expr_priv(expr); + const struct sk_buff *skb = pkt->skb; + u32 *dest = ®s->data[priv->dreg]; + int offset; + + if (priv->len % NFT_REG32_SIZE) + dest[priv->len / NFT_REG32_SIZE] = 0; + + switch (priv->base) { + case NFT_PAYLOAD_TUN_HEADER: + if (!(tun_ctx->flags & NFT_PAYLOAD_CTX_INNER_TUN)) + goto err; + + offset = tun_ctx->inner_tunoff; + break; + case NFT_PAYLOAD_LL_HEADER: + if (!(tun_ctx->flags & NFT_PAYLOAD_CTX_INNER_LL)) + goto err; + + offset = tun_ctx->inner_lloff; + break; + case NFT_PAYLOAD_NETWORK_HEADER: + if (!(tun_ctx->flags & NFT_PAYLOAD_CTX_INNER_NH)) + goto err; + + offset = tun_ctx->inner_nhoff; + break; + case NFT_PAYLOAD_TRANSPORT_HEADER: + if (!(tun_ctx->flags & NFT_PAYLOAD_CTX_INNER_TH)) + goto err; + + offset = tun_ctx->inner_thoff; + break; + default: + WARN_ON_ONCE(1); + goto err; + } + offset += priv->offset; + + if (skb_copy_bits(skb, offset, dest, priv->len) < 0) + goto err; + + return; +err: + regs->verdict.code = NFT_BREAK; +} + +static int nft_payload_inner_init(const struct nft_ctx *ctx, + const struct nft_expr *expr, + const struct nlattr * const tb[]) +{ + struct nft_payload *priv = nft_expr_priv(expr); + u32 base; + + base = ntohl(nla_get_be32(tb[NFTA_PAYLOAD_BASE])); + switch (base) { + case NFT_PAYLOAD_TUN_HEADER: + case NFT_PAYLOAD_LL_HEADER: + case NFT_PAYLOAD_NETWORK_HEADER: + case NFT_PAYLOAD_TRANSPORT_HEADER: + break; + default: + return -EOPNOTSUPP; + } + + priv->base = base; + priv->offset = ntohl(nla_get_be32(tb[NFTA_PAYLOAD_OFFSET])); + priv->len = ntohl(nla_get_be32(tb[NFTA_PAYLOAD_LEN])); + + return nft_parse_register_store(ctx, tb[NFTA_PAYLOAD_DREG], + &priv->dreg, NULL, NFT_DATA_VALUE, + priv->len); +} + +static const struct nft_expr_ops nft_payload_inner_ops = { + .type = &nft_payload_type, + .size = NFT_EXPR_SIZE(sizeof(struct nft_payload)), + .init = nft_payload_inner_init, + .dump = nft_payload_dump, + /* direct call to nft_payload_inner_eval(). */ +}; + static inline void nft_csum_replace(__sum16 *sum, __wsum fsum, __wsum tsum) { *sum = csum_fold(csum_add(csum_sub(~csum_unfold(*sum), fsum), tsum)); @@ -930,6 +1016,7 @@ nft_payload_select_ops(const struct nft_ctx *ctx, struct nft_expr_type nft_payload_type __read_mostly = { .name = "payload", .select_ops = nft_payload_select_ops, + .inner_ops = &nft_payload_inner_ops, .policy = nft_payload_policy, .maxattr = NFTA_PAYLOAD_MAX, .owner = THIS_MODULE, From 0e795b37ba044893107f887b037594645a6fc584 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Mon, 17 Oct 2022 13:03:32 +0200 Subject: [PATCH 0333/2002] netfilter: nft_inner: add percpu inner context Add NFT_PKTINFO_INNER_FULL flag to annotate that inner offsets are available. Store nft_inner_tun_ctx object in percpu area to cache existing inner offsets for this skbuff. Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/nf_tables.h | 1 + include/net/netfilter/nf_tables_core.h | 1 + net/netfilter/nft_inner.c | 26 ++++++++++++++++++++++---- 3 files changed, 24 insertions(+), 4 deletions(-) diff --git a/include/net/netfilter/nf_tables.h b/include/net/netfilter/nf_tables.h index 2dbfe7524a7e..38e2b396e38a 100644 --- a/include/net/netfilter/nf_tables.h +++ b/include/net/netfilter/nf_tables.h @@ -24,6 +24,7 @@ struct module; enum { NFT_PKTINFO_L4PROTO = (1 << 0), NFT_PKTINFO_INNER = (1 << 1), + NFT_PKTINFO_INNER_FULL = (1 << 2), }; struct nft_pktinfo { diff --git a/include/net/netfilter/nf_tables_core.h b/include/net/netfilter/nf_tables_core.h index be2b2b5d0a52..3e825381ac5c 100644 --- a/include/net/netfilter/nf_tables_core.h +++ b/include/net/netfilter/nf_tables_core.h @@ -149,6 +149,7 @@ enum { }; struct nft_inner_tun_ctx { + u16 type; u16 inner_tunoff; u16 inner_lloff; u16 inner_nhoff; diff --git a/net/netfilter/nft_inner.c b/net/netfilter/nft_inner.c index 1e4079b5b431..29f2eefe0357 100644 --- a/net/netfilter/nft_inner.c +++ b/net/netfilter/nft_inner.c @@ -21,6 +21,8 @@ #include #include +static DEFINE_PER_CPU(struct nft_inner_tun_ctx, nft_pcpu_tun_ctx); + /* Same layout as nft_expr but it embeds the private expression data area. */ struct __nft_expr { const struct nft_expr_ops *ops; @@ -180,7 +182,7 @@ static int nft_inner_parse_tunhdr(const struct nft_inner *priv, } static int nft_inner_parse(const struct nft_inner *priv, - const struct nft_pktinfo *pkt, + struct nft_pktinfo *pkt, struct nft_inner_tun_ctx *tun_ctx) { struct nft_inner_tun_ctx ctx = {}; @@ -199,25 +201,41 @@ static int nft_inner_parse(const struct nft_inner *priv, } *tun_ctx = ctx; + tun_ctx->type = priv->type; + pkt->flags |= NFT_PKTINFO_INNER_FULL; return 0; } +static bool nft_inner_parse_needed(const struct nft_inner *priv, + const struct nft_pktinfo *pkt, + const struct nft_inner_tun_ctx *tun_ctx) +{ + if (!(pkt->flags & NFT_PKTINFO_INNER_FULL)) + return true; + + if (priv->type != tun_ctx->type) + return true; + + return false; +} + static void nft_inner_eval(const struct nft_expr *expr, struct nft_regs *regs, const struct nft_pktinfo *pkt) { + struct nft_inner_tun_ctx *tun_ctx = this_cpu_ptr(&nft_pcpu_tun_ctx); const struct nft_inner *priv = nft_expr_priv(expr); - struct nft_inner_tun_ctx tun_ctx = {}; if (nft_payload_inner_offset(pkt) < 0) goto err; - if (nft_inner_parse(priv, pkt, &tun_ctx) < 0) + if (nft_inner_parse_needed(priv, pkt, tun_ctx) && + nft_inner_parse(priv, (struct nft_pktinfo *)pkt, tun_ctx) < 0) goto err; switch (priv->expr_type) { case NFT_INNER_EXPR_PAYLOAD: - nft_payload_inner_eval((struct nft_expr *)&priv->expr, regs, pkt, &tun_ctx); + nft_payload_inner_eval((struct nft_expr *)&priv->expr, regs, pkt, tun_ctx); break; default: WARN_ON_ONCE(1); From a150d122b6bdb84df532057aa3b2faf8c6485792 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Mon, 17 Oct 2022 13:03:33 +0200 Subject: [PATCH 0334/2002] netfilter: nft_meta: add inner match support Add support for inner meta matching on: - NFT_META_PROTOCOL: to match on the ethertype, this can be used regardless tunnel protocol provides no link layer header, in that case nft_inner sets on the ethertype based on the IP header version field. - NFT_META_L4PROTO: to match on the layer 4 protocol. These meta expression are usually autogenerated as dependencies by userspace nftables. Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/nft_meta.h | 6 ++++ net/netfilter/nft_inner.c | 8 +++++ net/netfilter/nft_meta.c | 62 ++++++++++++++++++++++++++++++++ 3 files changed, 76 insertions(+) diff --git a/include/net/netfilter/nft_meta.h b/include/net/netfilter/nft_meta.h index 9b51cc67de54..f3a5285a511c 100644 --- a/include/net/netfilter/nft_meta.h +++ b/include/net/netfilter/nft_meta.h @@ -46,4 +46,10 @@ int nft_meta_set_validate(const struct nft_ctx *ctx, bool nft_meta_get_reduce(struct nft_regs_track *track, const struct nft_expr *expr); + +struct nft_inner_tun_ctx; +void nft_meta_inner_eval(const struct nft_expr *expr, + struct nft_regs *regs, const struct nft_pktinfo *pkt, + struct nft_inner_tun_ctx *tun_ctx); + #endif diff --git a/net/netfilter/nft_inner.c b/net/netfilter/nft_inner.c index 29f2eefe0357..c43a2fe0ceb7 100644 --- a/net/netfilter/nft_inner.c +++ b/net/netfilter/nft_inner.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -28,11 +29,13 @@ struct __nft_expr { const struct nft_expr_ops *ops; union { struct nft_payload payload; + struct nft_meta meta; } __attribute__((aligned(__alignof__(u64)))); }; enum { NFT_INNER_EXPR_PAYLOAD, + NFT_INNER_EXPR_META, }; struct nft_inner { @@ -237,6 +240,9 @@ static void nft_inner_eval(const struct nft_expr *expr, struct nft_regs *regs, case NFT_INNER_EXPR_PAYLOAD: nft_payload_inner_eval((struct nft_expr *)&priv->expr, regs, pkt, tun_ctx); break; + case NFT_INNER_EXPR_META: + nft_meta_inner_eval((struct nft_expr *)&priv->expr, regs, pkt, tun_ctx); + break; default: WARN_ON_ONCE(1); goto err; @@ -306,6 +312,8 @@ static int nft_inner_init(const struct nft_ctx *ctx, if (!strcmp(expr_info.ops->type->name, "payload")) priv->expr_type = NFT_INNER_EXPR_PAYLOAD; + else if (!strcmp(expr_info.ops->type->name, "meta")) + priv->expr_type = NFT_INNER_EXPR_META; else return -EINVAL; diff --git a/net/netfilter/nft_meta.c b/net/netfilter/nft_meta.c index 55d2d49c3425..8c39adeebb5c 100644 --- a/net/netfilter/nft_meta.c +++ b/net/netfilter/nft_meta.c @@ -831,9 +831,71 @@ nft_meta_select_ops(const struct nft_ctx *ctx, return ERR_PTR(-EINVAL); } +static int nft_meta_inner_init(const struct nft_ctx *ctx, + const struct nft_expr *expr, + const struct nlattr * const tb[]) +{ + struct nft_meta *priv = nft_expr_priv(expr); + unsigned int len; + + priv->key = ntohl(nla_get_be32(tb[NFTA_META_KEY])); + switch (priv->key) { + case NFT_META_PROTOCOL: + len = sizeof(u16); + break; + case NFT_META_L4PROTO: + len = sizeof(u32); + break; + default: + return -EOPNOTSUPP; + } + priv->len = len; + + return nft_parse_register_store(ctx, tb[NFTA_META_DREG], &priv->dreg, + NULL, NFT_DATA_VALUE, len); +} + +void nft_meta_inner_eval(const struct nft_expr *expr, + struct nft_regs *regs, + const struct nft_pktinfo *pkt, + struct nft_inner_tun_ctx *tun_ctx) +{ + const struct nft_meta *priv = nft_expr_priv(expr); + u32 *dest = ®s->data[priv->dreg]; + + switch (priv->key) { + case NFT_META_PROTOCOL: + nft_reg_store16(dest, (__force u16)tun_ctx->llproto); + break; + case NFT_META_L4PROTO: + if (!(tun_ctx->flags & NFT_PAYLOAD_CTX_INNER_TH)) + goto err; + + nft_reg_store8(dest, tun_ctx->l4proto); + break; + default: + WARN_ON_ONCE(1); + goto err; + } + return; + +err: + regs->verdict.code = NFT_BREAK; +} +EXPORT_SYMBOL_GPL(nft_meta_inner_eval); + +static const struct nft_expr_ops nft_meta_inner_ops = { + .type = &nft_meta_type, + .size = NFT_EXPR_SIZE(sizeof(struct nft_meta)), + .init = nft_meta_inner_init, + .dump = nft_meta_get_dump, + /* direct call to nft_meta_inner_eval(). */ +}; + struct nft_expr_type nft_meta_type __read_mostly = { .name = "meta", .select_ops = nft_meta_select_ops, + .inner_ops = &nft_meta_inner_ops, .policy = nft_meta_policy, .maxattr = NFTA_META_MAX, .owner = THIS_MODULE, From 0db14b95660b63dceeb7e89f2e3ffa97d331fce0 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Mon, 17 Oct 2022 13:03:34 +0200 Subject: [PATCH 0335/2002] netfilter: nft_inner: add geneve support Geneve tunnel header may contain options, parse geneve header and update offset to point to the link layer header according to the opt_len field. Signed-off-by: Pablo Neira Ayuso --- include/uapi/linux/netfilter/nf_tables.h | 1 + net/netfilter/nft_inner.c | 17 +++++++++++++++++ 2 files changed, 18 insertions(+) diff --git a/include/uapi/linux/netfilter/nf_tables.h b/include/uapi/linux/netfilter/nf_tables.h index 05a15dce8271..e4b739d57480 100644 --- a/include/uapi/linux/netfilter/nf_tables.h +++ b/include/uapi/linux/netfilter/nf_tables.h @@ -783,6 +783,7 @@ enum nft_payload_csum_flags { enum nft_inner_type { NFT_INNER_UNSPEC = 0, NFT_INNER_VXLAN, + NFT_INNER_GENEVE, }; enum nft_inner_flags { diff --git a/net/netfilter/nft_inner.c b/net/netfilter/nft_inner.c index c43a2fe0ceb7..19fdc8c70cd1 100644 --- a/net/netfilter/nft_inner.c +++ b/net/netfilter/nft_inner.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -181,6 +182,22 @@ static int nft_inner_parse_tunhdr(const struct nft_inner *priv, ctx->flags |= NFT_PAYLOAD_CTX_INNER_TUN; *off += priv->hdrsize; + switch (priv->type) { + case NFT_INNER_GENEVE: { + struct genevehdr *gnvh, _gnvh; + + gnvh = skb_header_pointer(pkt->skb, pkt->inneroff, + sizeof(_gnvh), &_gnvh); + if (!gnvh) + return -1; + + *off += gnvh->opt_len * 4; + } + break; + default: + break; + } + return 0; } From 91619eb60aeccd3181d9b88975add706a9b763c1 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Mon, 17 Oct 2022 13:03:35 +0200 Subject: [PATCH 0336/2002] netfilter: nft_inner: set tunnel offset to GRE header offset Set inner tunnel offset to the GRE header, this is redundant to existing transport header offset, but this normalizes the handling of the tunnel header regardless its location in the layering. GRE version 0 is overloaded with RFCs, the type decorator in the inner expression might also be useful to interpret matching fields from the netlink delinearize path in userspace. Signed-off-by: Pablo Neira Ayuso --- net/netfilter/nft_inner.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/net/netfilter/nft_inner.c b/net/netfilter/nft_inner.c index 19fdc8c70cd1..eae7caeff316 100644 --- a/net/netfilter/nft_inner.c +++ b/net/netfilter/nft_inner.c @@ -174,8 +174,13 @@ static int nft_inner_parse_tunhdr(const struct nft_inner *priv, const struct nft_pktinfo *pkt, struct nft_inner_tun_ctx *ctx, u32 *off) { - if (pkt->tprot != IPPROTO_UDP || - pkt->tprot != IPPROTO_GRE) + if (pkt->tprot == IPPROTO_GRE) { + ctx->inner_tunoff = pkt->thoff; + ctx->flags |= NFT_PAYLOAD_CTX_INNER_TUN; + return 0; + } + + if (pkt->tprot != IPPROTO_UDP) return -1; ctx->inner_tunoff = *off; From b3d84af7cdfc079ef86d94f7cf125821559925fa Mon Sep 17 00:00:00 2001 From: Quentin Monnet Date: Tue, 25 Oct 2022 16:03:22 +0100 Subject: [PATCH 0337/2002] bpftool: Define _GNU_SOURCE only once MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit _GNU_SOURCE is defined in several source files for bpftool, but only one of them takes the precaution of checking whether the value is already defined. Add #ifndef for other occurrences too. This is in preparation for the support of disassembling JIT-ed programs with LLVM, with $(llvm-config --cflags) passing -D_GNU_SOURCE as a compilation argument. Signed-off-by: Quentin Monnet Tested-by: Niklas Söderlund Acked-by: Song Liu Link: https://lore.kernel.org/r/20221025150329.97371-2-quentin@isovalent.com Signed-off-by: Alexei Starovoitov --- tools/bpf/bpftool/common.c | 2 ++ tools/bpf/bpftool/iter.c | 2 ++ tools/bpf/bpftool/jit_disasm.c | 2 ++ tools/bpf/bpftool/net.c | 2 ++ tools/bpf/bpftool/perf.c | 2 ++ tools/bpf/bpftool/prog.c | 2 ++ tools/bpf/bpftool/xlated_dumper.c | 2 ++ 7 files changed, 14 insertions(+) diff --git a/tools/bpf/bpftool/common.c b/tools/bpf/bpftool/common.c index 8727765add88..4c2e909a2d67 100644 --- a/tools/bpf/bpftool/common.c +++ b/tools/bpf/bpftool/common.c @@ -1,7 +1,9 @@ // SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) /* Copyright (C) 2017-2018 Netronome Systems, Inc. */ +#ifndef _GNU_SOURCE #define _GNU_SOURCE +#endif #include #include #include diff --git a/tools/bpf/bpftool/iter.c b/tools/bpf/bpftool/iter.c index f88fdc820d23..a3e6b167153d 100644 --- a/tools/bpf/bpftool/iter.c +++ b/tools/bpf/bpftool/iter.c @@ -1,7 +1,9 @@ // SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) // Copyright (C) 2020 Facebook +#ifndef _GNU_SOURCE #define _GNU_SOURCE +#endif #include #include #include diff --git a/tools/bpf/bpftool/jit_disasm.c b/tools/bpf/bpftool/jit_disasm.c index aaf99a0168c9..71cb258ab0ee 100644 --- a/tools/bpf/bpftool/jit_disasm.c +++ b/tools/bpf/bpftool/jit_disasm.c @@ -11,7 +11,9 @@ * Licensed under the GNU General Public License, version 2.0 (GPLv2) */ +#ifndef _GNU_SOURCE #define _GNU_SOURCE +#endif #include #include #include diff --git a/tools/bpf/bpftool/net.c b/tools/bpf/bpftool/net.c index 526a332c48e6..c40e44c938ae 100644 --- a/tools/bpf/bpftool/net.c +++ b/tools/bpf/bpftool/net.c @@ -1,7 +1,9 @@ // SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) // Copyright (C) 2018 Facebook +#ifndef _GNU_SOURCE #define _GNU_SOURCE +#endif #include #include #include diff --git a/tools/bpf/bpftool/perf.c b/tools/bpf/bpftool/perf.c index 226ec2c39052..91743445e4c7 100644 --- a/tools/bpf/bpftool/perf.c +++ b/tools/bpf/bpftool/perf.c @@ -2,7 +2,9 @@ // Copyright (C) 2018 Facebook // Author: Yonghong Song +#ifndef _GNU_SOURCE #define _GNU_SOURCE +#endif #include #include #include diff --git a/tools/bpf/bpftool/prog.c b/tools/bpf/bpftool/prog.c index 10ec29cb4598..dbf5489b8fde 100644 --- a/tools/bpf/bpftool/prog.c +++ b/tools/bpf/bpftool/prog.c @@ -1,7 +1,9 @@ // SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) /* Copyright (C) 2017-2018 Netronome Systems, Inc. */ +#ifndef _GNU_SOURCE #define _GNU_SOURCE +#endif #include #include #include diff --git a/tools/bpf/bpftool/xlated_dumper.c b/tools/bpf/bpftool/xlated_dumper.c index 2d9cd6a7b3c8..6fe3134ae45d 100644 --- a/tools/bpf/bpftool/xlated_dumper.c +++ b/tools/bpf/bpftool/xlated_dumper.c @@ -1,7 +1,9 @@ // SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) /* Copyright (C) 2018 Netronome Systems, Inc. */ +#ifndef _GNU_SOURCE #define _GNU_SOURCE +#endif #include #include #include From 55b4de58d0e2aca810ed2b198a0173640300acf8 Mon Sep 17 00:00:00 2001 From: Quentin Monnet Date: Tue, 25 Oct 2022 16:03:23 +0100 Subject: [PATCH 0338/2002] bpftool: Remove asserts from JIT disassembler MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The JIT disassembler in bpftool is the only components (with the JSON writer) using asserts to check the return values of functions. But it does not do so in a consistent way, and diasm_print_insn() returns no value, although sometimes the operation failed. Remove the asserts, and instead check the return values, print messages on errors, and propagate the error to the caller from prog.c. Remove the inclusion of assert.h from jit_disasm.c, and also from map.c where it is unused. Signed-off-by: Quentin Monnet Tested-by: Niklas Söderlund Acked-by: Song Liu Link: https://lore.kernel.org/r/20221025150329.97371-3-quentin@isovalent.com Signed-off-by: Alexei Starovoitov --- tools/bpf/bpftool/jit_disasm.c | 51 +++++++++++++++++++++++----------- tools/bpf/bpftool/main.h | 25 +++++++++-------- tools/bpf/bpftool/map.c | 1 - tools/bpf/bpftool/prog.c | 15 ++++++---- 4 files changed, 57 insertions(+), 35 deletions(-) diff --git a/tools/bpf/bpftool/jit_disasm.c b/tools/bpf/bpftool/jit_disasm.c index 71cb258ab0ee..723a9b799a0c 100644 --- a/tools/bpf/bpftool/jit_disasm.c +++ b/tools/bpf/bpftool/jit_disasm.c @@ -18,7 +18,6 @@ #include #include #include -#include #include #include #include @@ -31,14 +30,18 @@ #include "json_writer.h" #include "main.h" -static void get_exec_path(char *tpath, size_t size) +static int get_exec_path(char *tpath, size_t size) { const char *path = "/proc/self/exe"; ssize_t len; len = readlink(path, tpath, size - 1); - assert(len > 0); + if (len <= 0) + return -1; + tpath[len] = 0; + + return 0; } static int oper_count; @@ -99,30 +102,39 @@ static int fprintf_json_styled(void *out, return r; } -void disasm_print_insn(unsigned char *image, ssize_t len, int opcodes, - const char *arch, const char *disassembler_options, - const struct btf *btf, - const struct bpf_prog_linfo *prog_linfo, - __u64 func_ksym, unsigned int func_idx, - bool linum) +int disasm_print_insn(unsigned char *image, ssize_t len, int opcodes, + const char *arch, const char *disassembler_options, + const struct btf *btf, + const struct bpf_prog_linfo *prog_linfo, + __u64 func_ksym, unsigned int func_idx, + bool linum) { const struct bpf_line_info *linfo = NULL; disassembler_ftype disassemble; + int count, i, pc = 0, err = -1; struct disassemble_info info; unsigned int nr_skip = 0; - int count, i, pc = 0; char tpath[PATH_MAX]; bfd *bfdf; if (!len) - return; + return -1; memset(tpath, 0, sizeof(tpath)); - get_exec_path(tpath, sizeof(tpath)); + if (get_exec_path(tpath, sizeof(tpath))) { + p_err("failed to create disasembler (get_exec_path)"); + return -1; + } bfdf = bfd_openr(tpath, NULL); - assert(bfdf); - assert(bfd_check_format(bfdf, bfd_object)); + if (!bfdf) { + p_err("failed to create disassembler (bfd_openr)"); + return -1; + } + if (!bfd_check_format(bfdf, bfd_object)) { + p_err("failed to create disassembler (bfd_check_format)"); + goto exit_close; + } if (json_output) init_disassemble_info_compat(&info, stdout, @@ -141,7 +153,7 @@ void disasm_print_insn(unsigned char *image, ssize_t len, int opcodes, bfdf->arch_info = inf; } else { p_err("No libbfd support for %s", arch); - return; + goto exit_close; } } @@ -162,7 +174,10 @@ void disasm_print_insn(unsigned char *image, ssize_t len, int opcodes, #else disassemble = disassembler(bfdf); #endif - assert(disassemble); + if (!disassemble) { + p_err("failed to create disassembler"); + goto exit_close; + } if (json_output) jsonw_start_array(json_wtr); @@ -226,7 +241,11 @@ void disasm_print_insn(unsigned char *image, ssize_t len, int opcodes, if (json_output) jsonw_end_array(json_wtr); + err = 0; + +exit_close: bfd_close(bfdf); + return err; } int disasm_init(void) diff --git a/tools/bpf/bpftool/main.h b/tools/bpf/bpftool/main.h index 5e5060c2ac04..c9e171082cf6 100644 --- a/tools/bpf/bpftool/main.h +++ b/tools/bpf/bpftool/main.h @@ -173,22 +173,23 @@ int map_parse_fd_and_info(int *argc, char ***argv, void *info, __u32 *info_len); struct bpf_prog_linfo; #ifdef HAVE_LIBBFD_SUPPORT -void disasm_print_insn(unsigned char *image, ssize_t len, int opcodes, - const char *arch, const char *disassembler_options, - const struct btf *btf, - const struct bpf_prog_linfo *prog_linfo, - __u64 func_ksym, unsigned int func_idx, - bool linum); +int disasm_print_insn(unsigned char *image, ssize_t len, int opcodes, + const char *arch, const char *disassembler_options, + const struct btf *btf, + const struct bpf_prog_linfo *prog_linfo, + __u64 func_ksym, unsigned int func_idx, + bool linum); int disasm_init(void); #else static inline -void disasm_print_insn(unsigned char *image, ssize_t len, int opcodes, - const char *arch, const char *disassembler_options, - const struct btf *btf, - const struct bpf_prog_linfo *prog_linfo, - __u64 func_ksym, unsigned int func_idx, - bool linum) +int disasm_print_insn(unsigned char *image, ssize_t len, int opcodes, + const char *arch, const char *disassembler_options, + const struct btf *btf, + const struct bpf_prog_linfo *prog_linfo, + __u64 func_ksym, unsigned int func_idx, + bool linum) { + return 0; } static inline int disasm_init(void) { diff --git a/tools/bpf/bpftool/map.c b/tools/bpf/bpftool/map.c index 9a6ca9f31133..3087ced658ad 100644 --- a/tools/bpf/bpftool/map.c +++ b/tools/bpf/bpftool/map.c @@ -1,7 +1,6 @@ // SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) /* Copyright (C) 2017-2018 Netronome Systems, Inc. */ -#include #include #include #include diff --git a/tools/bpf/bpftool/prog.c b/tools/bpf/bpftool/prog.c index dbf5489b8fde..93dfb89b85e3 100644 --- a/tools/bpf/bpftool/prog.c +++ b/tools/bpf/bpftool/prog.c @@ -822,10 +822,11 @@ prog_dump(struct bpf_prog_info *info, enum dump_mode mode, printf("%s:\n", sym_name); } - disasm_print_insn(img, lens[i], opcodes, - name, disasm_opt, btf, - prog_linfo, ksyms[i], i, - linum); + if (disasm_print_insn(img, lens[i], opcodes, + name, disasm_opt, btf, + prog_linfo, ksyms[i], i, + linum)) + goto exit_free; img += lens[i]; @@ -838,8 +839,10 @@ prog_dump(struct bpf_prog_info *info, enum dump_mode mode, if (json_output) jsonw_end_array(json_wtr); } else { - disasm_print_insn(buf, member_len, opcodes, name, - disasm_opt, btf, NULL, 0, 0, false); + if (disasm_print_insn(buf, member_len, opcodes, name, + disasm_opt, btf, NULL, 0, 0, + false)) + goto exit_free; } } else if (visual) { if (json_output) From 108326d6fa6c04e1473886fd662981792fc708a2 Mon Sep 17 00:00:00 2001 From: Quentin Monnet Date: Tue, 25 Oct 2022 16:03:24 +0100 Subject: [PATCH 0339/2002] bpftool: Split FEATURE_TESTS/FEATURE_DISPLAY definitions in Makefile MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make FEATURE_TESTS and FEATURE_DISPLAY easier to read and less likely to be subject to conflicts on updates by having one feature per line. Suggested-by: Andres Freund Signed-off-by: Quentin Monnet Tested-by: Niklas Söderlund Acked-by: Song Liu Link: https://lore.kernel.org/r/20221025150329.97371-4-quentin@isovalent.com Signed-off-by: Alexei Starovoitov --- tools/bpf/bpftool/Makefile | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/tools/bpf/bpftool/Makefile b/tools/bpf/bpftool/Makefile index 4a95c017ad4c..0218d6a1cae7 100644 --- a/tools/bpf/bpftool/Makefile +++ b/tools/bpf/bpftool/Makefile @@ -93,11 +93,20 @@ INSTALL ?= install RM ?= rm -f FEATURE_USER = .bpftool -FEATURE_TESTS = libbfd libbfd-liberty libbfd-liberty-z \ - disassembler-four-args disassembler-init-styled libcap \ - clang-bpf-co-re -FEATURE_DISPLAY = libbfd libbfd-liberty libbfd-liberty-z \ - libcap clang-bpf-co-re + +FEATURE_TESTS := clang-bpf-co-re +FEATURE_TESTS += libcap +FEATURE_TESTS += libbfd +FEATURE_TESTS += libbfd-liberty +FEATURE_TESTS += libbfd-liberty-z +FEATURE_TESTS += disassembler-four-args +FEATURE_TESTS += disassembler-init-styled + +FEATURE_DISPLAY := clang-bpf-co-re +FEATURE_DISPLAY += libcap +FEATURE_DISPLAY += libbfd +FEATURE_DISPLAY += libbfd-liberty +FEATURE_DISPLAY += libbfd-liberty-z check_feat := 1 NON_CHECK_FEAT_TARGETS := clean uninstall doc doc-clean doc-install doc-uninstall From 2ea4d86a5093defcb2fc49799184ede178e64d36 Mon Sep 17 00:00:00 2001 From: Quentin Monnet Date: Tue, 25 Oct 2022 16:03:25 +0100 Subject: [PATCH 0340/2002] bpftool: Group libbfd defs in Makefile, only pass them if we use libbfd MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bpftool uses libbfd for disassembling JIT-ed programs. But the feature is optional, and the tool can be compiled without libbfd support. The Makefile sets the relevant variables accordingly. It also sets variables related to libbfd's interface, given that it has changed over time. Group all those libbfd-related definitions so that it's easier to understand what we are testing for, and only use variables related to libbfd's interface if we need libbfd in the first place. In addition to make the Makefile clearer, grouping the definitions related to disassembling JIT-ed programs will help support alternatives to libbfd. Signed-off-by: Quentin Monnet Tested-by: Niklas Söderlund Acked-by: Song Liu Link: https://lore.kernel.org/r/20221025150329.97371-5-quentin@isovalent.com Signed-off-by: Alexei Starovoitov --- tools/bpf/bpftool/Makefile | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/tools/bpf/bpftool/Makefile b/tools/bpf/bpftool/Makefile index 0218d6a1cae7..1c81f4d514bb 100644 --- a/tools/bpf/bpftool/Makefile +++ b/tools/bpf/bpftool/Makefile @@ -124,13 +124,6 @@ include $(FEATURES_DUMP) endif endif -ifeq ($(feature-disassembler-four-args), 1) -CFLAGS += -DDISASM_FOUR_ARGS_SIGNATURE -endif -ifeq ($(feature-disassembler-init-styled), 1) - CFLAGS += -DDISASM_INIT_STYLED -endif - LIBS = $(LIBBPF) -lelf -lz LIBS_BOOTSTRAP = $(LIBBPF_BOOTSTRAP) -lelf -lz ifeq ($(feature-libcap), 1) @@ -142,9 +135,7 @@ include $(wildcard $(OUTPUT)*.d) all: $(OUTPUT)bpftool -BFD_SRCS = jit_disasm.c - -SRCS = $(filter-out $(BFD_SRCS),$(wildcard *.c)) +SRCS := $(wildcard *.c) ifeq ($(feature-libbfd),1) LIBS += -lbfd -ldl -lopcodes @@ -154,9 +145,21 @@ else ifeq ($(feature-libbfd-liberty-z),1) LIBS += -lbfd -ldl -lopcodes -liberty -lz endif +# If one of the above feature combinations is set, we support libbfd ifneq ($(filter -lbfd,$(LIBS)),) -CFLAGS += -DHAVE_LIBBFD_SUPPORT -SRCS += $(BFD_SRCS) + CFLAGS += -DHAVE_LIBBFD_SUPPORT + + # Libbfd interface changed over time, figure out what we need + ifeq ($(feature-disassembler-four-args), 1) + CFLAGS += -DDISASM_FOUR_ARGS_SIGNATURE + endif + ifeq ($(feature-disassembler-init-styled), 1) + CFLAGS += -DDISASM_INIT_STYLED + endif +endif +ifeq ($(filter -DHAVE_LIBBFD_SUPPORT,$(CFLAGS)),) + # No support for JIT disassembly + SRCS := $(filter-out jit_disasm.c,$(SRCS)) endif HOST_CFLAGS = $(subst -I$(LIBBPF_INCLUDE),-I$(LIBBPF_BOOTSTRAP_INCLUDE),\ From e1947c750ffe5effa371d7b7bc72043f100f6a93 Mon Sep 17 00:00:00 2001 From: Quentin Monnet Date: Tue, 25 Oct 2022 16:03:26 +0100 Subject: [PATCH 0341/2002] bpftool: Refactor disassembler for JIT-ed programs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Refactor disasm_print_insn() to extract the code specific to libbfd and move it to dedicated functions. There is no functional change. This is in preparation for supporting an alternative library for disassembling the instructions. Signed-off-by: Quentin Monnet Tested-by: Niklas Söderlund Acked-by: Song Liu Link: https://lore.kernel.org/r/20221025150329.97371-6-quentin@isovalent.com Signed-off-by: Alexei Starovoitov --- tools/bpf/bpftool/jit_disasm.c | 133 ++++++++++++++++++++++----------- 1 file changed, 88 insertions(+), 45 deletions(-) diff --git a/tools/bpf/bpftool/jit_disasm.c b/tools/bpf/bpftool/jit_disasm.c index 723a9b799a0c..e31ad3950fd6 100644 --- a/tools/bpf/bpftool/jit_disasm.c +++ b/tools/bpf/bpftool/jit_disasm.c @@ -30,6 +30,14 @@ #include "json_writer.h" #include "main.h" +static int oper_count; + +typedef struct { + struct disassemble_info *info; + disassembler_ftype disassemble; + bfd *bfdf; +} disasm_ctx_t; + static int get_exec_path(char *tpath, size_t size) { const char *path = "/proc/self/exe"; @@ -44,7 +52,6 @@ static int get_exec_path(char *tpath, size_t size) return 0; } -static int oper_count; static int printf_json(void *out, const char *fmt, va_list ap) { char *s; @@ -102,46 +109,44 @@ static int fprintf_json_styled(void *out, return r; } -int disasm_print_insn(unsigned char *image, ssize_t len, int opcodes, - const char *arch, const char *disassembler_options, - const struct btf *btf, - const struct bpf_prog_linfo *prog_linfo, - __u64 func_ksym, unsigned int func_idx, - bool linum) +static int init_context(disasm_ctx_t *ctx, const char *arch, + const char *disassembler_options, + unsigned char *image, ssize_t len) { - const struct bpf_line_info *linfo = NULL; - disassembler_ftype disassemble; - int count, i, pc = 0, err = -1; - struct disassemble_info info; - unsigned int nr_skip = 0; + struct disassemble_info *info; char tpath[PATH_MAX]; bfd *bfdf; - if (!len) - return -1; - memset(tpath, 0, sizeof(tpath)); if (get_exec_path(tpath, sizeof(tpath))) { p_err("failed to create disasembler (get_exec_path)"); return -1; } - bfdf = bfd_openr(tpath, NULL); - if (!bfdf) { + ctx->bfdf = bfd_openr(tpath, NULL); + if (!ctx->bfdf) { p_err("failed to create disassembler (bfd_openr)"); return -1; } - if (!bfd_check_format(bfdf, bfd_object)) { + if (!bfd_check_format(ctx->bfdf, bfd_object)) { p_err("failed to create disassembler (bfd_check_format)"); - goto exit_close; + goto err_close; } + bfdf = ctx->bfdf; + + ctx->info = malloc(sizeof(struct disassemble_info)); + if (!ctx->info) { + p_err("mem alloc failed"); + goto err_close; + } + info = ctx->info; if (json_output) - init_disassemble_info_compat(&info, stdout, + init_disassemble_info_compat(info, stdout, (fprintf_ftype) fprintf_json, fprintf_json_styled); else - init_disassemble_info_compat(&info, stdout, + init_disassemble_info_compat(info, stdout, (fprintf_ftype) fprintf, fprintf_styled); @@ -153,31 +158,76 @@ int disasm_print_insn(unsigned char *image, ssize_t len, int opcodes, bfdf->arch_info = inf; } else { p_err("No libbfd support for %s", arch); - goto exit_close; + goto err_free; } } - info.arch = bfd_get_arch(bfdf); - info.mach = bfd_get_mach(bfdf); + info->arch = bfd_get_arch(bfdf); + info->mach = bfd_get_mach(bfdf); if (disassembler_options) - info.disassembler_options = disassembler_options; - info.buffer = image; - info.buffer_length = len; + info->disassembler_options = disassembler_options; + info->buffer = image; + info->buffer_length = len; - disassemble_init_for_target(&info); + disassemble_init_for_target(info); #ifdef DISASM_FOUR_ARGS_SIGNATURE - disassemble = disassembler(info.arch, - bfd_big_endian(bfdf), - info.mach, - bfdf); + ctx->disassemble = disassembler(info->arch, + bfd_big_endian(bfdf), + info->mach, + bfdf); #else - disassemble = disassembler(bfdf); + ctx->disassemble = disassembler(bfdf); #endif - if (!disassemble) { + if (!ctx->disassemble) { p_err("failed to create disassembler"); - goto exit_close; + goto err_free; } + return 0; + +err_free: + free(info); +err_close: + bfd_close(ctx->bfdf); + return -1; +} + +static void destroy_context(disasm_ctx_t *ctx) +{ + free(ctx->info); + bfd_close(ctx->bfdf); +} + +static int +disassemble_insn(disasm_ctx_t *ctx, __maybe_unused unsigned char *image, + __maybe_unused ssize_t len, int pc) +{ + return ctx->disassemble(pc, ctx->info); +} + +int disasm_init(void) +{ + bfd_init(); + return 0; +} + +int disasm_print_insn(unsigned char *image, ssize_t len, int opcodes, + const char *arch, const char *disassembler_options, + const struct btf *btf, + const struct bpf_prog_linfo *prog_linfo, + __u64 func_ksym, unsigned int func_idx, + bool linum) +{ + const struct bpf_line_info *linfo = NULL; + unsigned int nr_skip = 0; + int count, i, pc = 0; + disasm_ctx_t ctx; + + if (!len) + return -1; + + if (init_context(&ctx, arch, disassembler_options, image, len)) + return -1; if (json_output) jsonw_start_array(json_wtr); @@ -205,7 +255,8 @@ int disasm_print_insn(unsigned char *image, ssize_t len, int opcodes, printf("%4x:\t", pc); } - count = disassemble(pc, &info); + count = disassemble_insn(&ctx, image, len, pc); + if (json_output) { /* Operand array, was started in fprintf_json. Before * that, make sure we have a _null_ value if no operand @@ -241,15 +292,7 @@ int disasm_print_insn(unsigned char *image, ssize_t len, int opcodes, if (json_output) jsonw_end_array(json_wtr); - err = 0; + destroy_context(&ctx); -exit_close: - bfd_close(bfdf); - return err; -} - -int disasm_init(void) -{ - bfd_init(); return 0; } From eb9d1acf634baf6401dfb4f67dc895290713a357 Mon Sep 17 00:00:00 2001 From: Quentin Monnet Date: Tue, 25 Oct 2022 16:03:27 +0100 Subject: [PATCH 0342/2002] bpftool: Add LLVM as default library for disassembling JIT-ed programs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit To disassemble instructions for JIT-ed programs, bpftool has relied on the libbfd library. This has been problematic in the past: libbfd's interface is not meant to be stable and has changed several times. For building bpftool, we have to detect how the libbfd version on the system behaves, which is why we have to handle features disassembler-four-args and disassembler-init-styled in the Makefile. When it comes to shipping bpftool, this has also caused issues with several distribution maintainers unwilling to support the feature (see for example Debian's page for binutils-dev, which ships libbfd: "Note that building Debian packages which depend on the shared libbfd is Not Allowed." [0]). For these reasons, we add support for LLVM as an alternative to libbfd for disassembling instructions of JIT-ed programs. Thanks to the preparation work in the previous commits, it's easy to add the library by passing the relevant compilation options in the Makefile, and by adding the functions for setting up the LLVM disassembler in file jit_disasm.c. The LLVM disassembler requires the LLVM development package (usually llvm-dev or llvm-devel). The expectation is that the interface for this disassembler will be more stable. There is a note in LLVM's Developer Policy [1] stating that the stability for the C API is "best effort" and not guaranteed, but at least there is some effort to keep compatibility when possible (which hasn't really been the case for libbfd so far). Furthermore, the Debian page for the related LLVM package does not caution against linking to the lib, as binutils-dev page does. Naturally, the display of disassembled instructions comes with a few minor differences. Here is a sample output with libbfd (already supported before this patch): # bpftool prog dump jited id 56 bpf_prog_6deef7357e7b4530: 0: nopl 0x0(%rax,%rax,1) 5: xchg %ax,%ax 7: push %rbp 8: mov %rsp,%rbp b: push %rbx c: push %r13 e: push %r14 10: mov %rdi,%rbx 13: movzwq 0xb4(%rbx),%r13 1b: xor %r14d,%r14d 1e: or $0x2,%r14d 22: mov $0x1,%eax 27: cmp $0x2,%r14 2b: jne 0x000000000000002f 2d: xor %eax,%eax 2f: pop %r14 31: pop %r13 33: pop %rbx 34: leave 35: ret LLVM supports several variants that we could set when initialising the disassembler, for example with: LLVMSetDisasmOptions(*ctx, LLVMDisassembler_Option_AsmPrinterVariant); but the default printer is used for now. Here is the output with LLVM: # bpftool prog dump jited id 56 bpf_prog_6deef7357e7b4530: 0: nopl (%rax,%rax) 5: nop 7: pushq %rbp 8: movq %rsp, %rbp b: pushq %rbx c: pushq %r13 e: pushq %r14 10: movq %rdi, %rbx 13: movzwq 180(%rbx), %r13 1b: xorl %r14d, %r14d 1e: orl $2, %r14d 22: movl $1, %eax 27: cmpq $2, %r14 2b: jne 0x2f 2d: xorl %eax, %eax 2f: popq %r14 31: popq %r13 33: popq %rbx 34: leave 35: retq The LLVM disassembler comes as the default choice, with libbfd as a fall-back. Of course, we could replace libbfd entirely and avoid supporting two different libraries. One reason for keeping libbfd is that, right now, it works well, we have all we need in terms of features detection in the Makefile, so it provides a fallback for disassembling JIT-ed programs if libbfd is installed but LLVM is not. The other motivation is that libbfd supports nfp instruction for Netronome's SmartNICs and can be used to disassemble offloaded programs, something that LLVM cannot do. If libbfd's interface breaks again in the future, we might reconsider keeping support for it. [0] https://packages.debian.org/buster/binutils-dev [1] https://llvm.org/docs/DeveloperPolicy.html#c-api-changes Signed-off-by: Quentin Monnet Tested-by: Niklas Söderlund Acked-by: Yonghong Song Link: https://lore.kernel.org/r/20221025150329.97371-7-quentin@isovalent.com Signed-off-by: Alexei Starovoitov --- tools/bpf/bpftool/Makefile | 48 ++++++++------ tools/bpf/bpftool/jit_disasm.c | 112 ++++++++++++++++++++++++++++++++- tools/bpf/bpftool/main.h | 4 +- 3 files changed, 141 insertions(+), 23 deletions(-) diff --git a/tools/bpf/bpftool/Makefile b/tools/bpf/bpftool/Makefile index 1c81f4d514bb..787b857d3fb5 100644 --- a/tools/bpf/bpftool/Makefile +++ b/tools/bpf/bpftool/Makefile @@ -95,6 +95,7 @@ RM ?= rm -f FEATURE_USER = .bpftool FEATURE_TESTS := clang-bpf-co-re +FEATURE_TESTS += llvm FEATURE_TESTS += libcap FEATURE_TESTS += libbfd FEATURE_TESTS += libbfd-liberty @@ -103,6 +104,7 @@ FEATURE_TESTS += disassembler-four-args FEATURE_TESTS += disassembler-init-styled FEATURE_DISPLAY := clang-bpf-co-re +FEATURE_DISPLAY += llvm FEATURE_DISPLAY += libcap FEATURE_DISPLAY += libbfd FEATURE_DISPLAY += libbfd-liberty @@ -137,27 +139,37 @@ all: $(OUTPUT)bpftool SRCS := $(wildcard *.c) -ifeq ($(feature-libbfd),1) - LIBS += -lbfd -ldl -lopcodes -else ifeq ($(feature-libbfd-liberty),1) - LIBS += -lbfd -ldl -lopcodes -liberty -else ifeq ($(feature-libbfd-liberty-z),1) - LIBS += -lbfd -ldl -lopcodes -liberty -lz -endif - -# If one of the above feature combinations is set, we support libbfd -ifneq ($(filter -lbfd,$(LIBS)),) - CFLAGS += -DHAVE_LIBBFD_SUPPORT - - # Libbfd interface changed over time, figure out what we need - ifeq ($(feature-disassembler-four-args), 1) - CFLAGS += -DDISASM_FOUR_ARGS_SIGNATURE +ifeq ($(feature-llvm),1) + # If LLVM is available, use it for JIT disassembly + CFLAGS += -DHAVE_LLVM_SUPPORT + LLVM_CONFIG_LIB_COMPONENTS := mcdisassembler all-targets + CFLAGS += $(shell $(LLVM_CONFIG) --cflags --libs $(LLVM_CONFIG_LIB_COMPONENTS)) + LIBS += $(shell $(LLVM_CONFIG) --libs $(LLVM_CONFIG_LIB_COMPONENTS)) + LDFLAGS += $(shell $(LLVM_CONFIG) --ldflags) +else + # Fall back on libbfd + ifeq ($(feature-libbfd),1) + LIBS += -lbfd -ldl -lopcodes + else ifeq ($(feature-libbfd-liberty),1) + LIBS += -lbfd -ldl -lopcodes -liberty + else ifeq ($(feature-libbfd-liberty-z),1) + LIBS += -lbfd -ldl -lopcodes -liberty -lz endif - ifeq ($(feature-disassembler-init-styled), 1) - CFLAGS += -DDISASM_INIT_STYLED + + # If one of the above feature combinations is set, we support libbfd + ifneq ($(filter -lbfd,$(LIBS)),) + CFLAGS += -DHAVE_LIBBFD_SUPPORT + + # Libbfd interface changed over time, figure out what we need + ifeq ($(feature-disassembler-four-args), 1) + CFLAGS += -DDISASM_FOUR_ARGS_SIGNATURE + endif + ifeq ($(feature-disassembler-init-styled), 1) + CFLAGS += -DDISASM_INIT_STYLED + endif endif endif -ifeq ($(filter -DHAVE_LIBBFD_SUPPORT,$(CFLAGS)),) +ifeq ($(filter -DHAVE_LLVM_SUPPORT -DHAVE_LIBBFD_SUPPORT,$(CFLAGS)),) # No support for JIT disassembly SRCS := $(filter-out jit_disasm.c,$(SRCS)) endif diff --git a/tools/bpf/bpftool/jit_disasm.c b/tools/bpf/bpftool/jit_disasm.c index e31ad3950fd6..c28b21f90cb9 100644 --- a/tools/bpf/bpftool/jit_disasm.c +++ b/tools/bpf/bpftool/jit_disasm.c @@ -20,18 +20,123 @@ #include #include #include -#include -#include #include #include #include + +#ifdef HAVE_LLVM_SUPPORT +#include +#include +#include +#include +#endif + +#ifdef HAVE_LIBBFD_SUPPORT +#include +#include #include +#endif #include "json_writer.h" #include "main.h" static int oper_count; +#ifdef HAVE_LLVM_SUPPORT +#define DISASM_SPACER + +typedef LLVMDisasmContextRef disasm_ctx_t; + +static int printf_json(char *s) +{ + s = strtok(s, " \t"); + jsonw_string_field(json_wtr, "operation", s); + + jsonw_name(json_wtr, "operands"); + jsonw_start_array(json_wtr); + oper_count = 1; + + while ((s = strtok(NULL, " \t,()")) != 0) { + jsonw_string(json_wtr, s); + oper_count++; + } + return 0; +} + +/* This callback to set the ref_type is necessary to have the LLVM disassembler + * print PC-relative addresses instead of byte offsets for branch instruction + * targets. + */ +static const char * +symbol_lookup_callback(__maybe_unused void *disasm_info, + __maybe_unused uint64_t ref_value, + uint64_t *ref_type, __maybe_unused uint64_t ref_PC, + __maybe_unused const char **ref_name) +{ + *ref_type = LLVMDisassembler_ReferenceType_InOut_None; + return NULL; +} + +static int +init_context(disasm_ctx_t *ctx, const char *arch, + __maybe_unused const char *disassembler_options, + __maybe_unused unsigned char *image, __maybe_unused ssize_t len) +{ + char *triple; + + if (arch) { + p_err("Architecture %s not supported", arch); + return -1; + } + + triple = LLVMGetDefaultTargetTriple(); + if (!triple) { + p_err("Failed to retrieve triple"); + return -1; + } + *ctx = LLVMCreateDisasm(triple, NULL, 0, NULL, symbol_lookup_callback); + LLVMDisposeMessage(triple); + + if (!*ctx) { + p_err("Failed to create disassembler"); + return -1; + } + + return 0; +} + +static void destroy_context(disasm_ctx_t *ctx) +{ + LLVMDisposeMessage(*ctx); +} + +static int +disassemble_insn(disasm_ctx_t *ctx, unsigned char *image, ssize_t len, int pc) +{ + char buf[256]; + int count; + + count = LLVMDisasmInstruction(*ctx, image + pc, len - pc, pc, + buf, sizeof(buf)); + if (json_output) + printf_json(buf); + else + printf("%s", buf); + + return count; +} + +int disasm_init(void) +{ + LLVMInitializeNativeTarget(); + LLVMInitializeNativeDisassembler(); + return 0; +} +#endif /* HAVE_LLVM_SUPPORT */ + +#ifdef HAVE_LIBBFD_SUPPORT +#define DISASM_SPACER "\t" + typedef struct { struct disassemble_info *info; disassembler_ftype disassemble; @@ -210,6 +315,7 @@ int disasm_init(void) bfd_init(); return 0; } +#endif /* HAVE_LIBBPFD_SUPPORT */ int disasm_print_insn(unsigned char *image, ssize_t len, int opcodes, const char *arch, const char *disassembler_options, @@ -252,7 +358,7 @@ int disasm_print_insn(unsigned char *image, ssize_t len, int opcodes, if (linfo) btf_dump_linfo_plain(btf, linfo, "; ", linum); - printf("%4x:\t", pc); + printf("%4x:" DISASM_SPACER, pc); } count = disassemble_insn(&ctx, image, len, pc); diff --git a/tools/bpf/bpftool/main.h b/tools/bpf/bpftool/main.h index c9e171082cf6..9a149c67aa5d 100644 --- a/tools/bpf/bpftool/main.h +++ b/tools/bpf/bpftool/main.h @@ -172,7 +172,7 @@ int map_parse_fds(int *argc, char ***argv, int **fds); int map_parse_fd_and_info(int *argc, char ***argv, void *info, __u32 *info_len); struct bpf_prog_linfo; -#ifdef HAVE_LIBBFD_SUPPORT +#if defined(HAVE_LLVM_SUPPORT) || defined(HAVE_LIBBFD_SUPPORT) int disasm_print_insn(unsigned char *image, ssize_t len, int opcodes, const char *arch, const char *disassembler_options, const struct btf *btf, @@ -193,7 +193,7 @@ int disasm_print_insn(unsigned char *image, ssize_t len, int opcodes, } static inline int disasm_init(void) { - p_err("No libbfd support"); + p_err("No JIT disassembly support"); return -1; } #endif From ce4f660862359f2172ff4e3df30e641df5475cf6 Mon Sep 17 00:00:00 2001 From: Quentin Monnet Date: Tue, 25 Oct 2022 16:03:28 +0100 Subject: [PATCH 0343/2002] bpftool: Support setting alternative arch for JIT disasm with LLVM For offloaded BPF programs, instead of failing to create the LLVM disassembler without even looking for a triple at all, do run the function that attempts to retrieve a valid architecture name for the device. It will still fail for the LLVM disassembler, because currently we have no valid triple to return (NFP disassembly is not supported by LLVM). But failing in that function is more logical than to assume in jit_disasm.c that passing an "arch" name is simply not supported. Suggested-by: Song Liu Signed-off-by: Quentin Monnet Link: https://lore.kernel.org/r/20221025150329.97371-8-quentin@isovalent.com Signed-off-by: Alexei Starovoitov --- tools/bpf/bpftool/common.c | 10 ++++++---- tools/bpf/bpftool/jit_disasm.c | 15 +++++++-------- tools/bpf/bpftool/main.h | 3 +-- tools/bpf/bpftool/prog.c | 6 ++---- 4 files changed, 16 insertions(+), 18 deletions(-) diff --git a/tools/bpf/bpftool/common.c b/tools/bpf/bpftool/common.c index 4c2e909a2d67..e4d33bc8bbbf 100644 --- a/tools/bpf/bpftool/common.c +++ b/tools/bpf/bpftool/common.c @@ -627,12 +627,11 @@ static int read_sysfs_netdev_hex_int(char *devname, const char *entry_name) } const char * -ifindex_to_bfd_params(__u32 ifindex, __u64 ns_dev, __u64 ns_ino, - const char **opt) +ifindex_to_arch(__u32 ifindex, __u64 ns_dev, __u64 ns_ino, const char **opt) { + __maybe_unused int device_id; char devname[IF_NAMESIZE]; int vendor_id; - int device_id; if (!ifindex_to_name_ns(ifindex, ns_dev, ns_ino, devname)) { p_err("Can't get net device name for ifindex %d: %s", ifindex, @@ -647,6 +646,7 @@ ifindex_to_bfd_params(__u32 ifindex, __u64 ns_dev, __u64 ns_ino, } switch (vendor_id) { +#ifdef HAVE_LIBBFD_SUPPORT case 0x19ee: device_id = read_sysfs_netdev_hex_int(devname, "device"); if (device_id != 0x4000 && @@ -655,8 +655,10 @@ ifindex_to_bfd_params(__u32 ifindex, __u64 ns_dev, __u64 ns_ino, p_info("Unknown NFP device ID, assuming it is NFP-6xxx arch"); *opt = "ctx4"; return "NFP-6xxx"; +#endif /* HAVE_LIBBFD_SUPPORT */ + /* No NFP support in LLVM, we have no valid triple to return. */ default: - p_err("Can't get bfd arch name for device vendor id 0x%04x", + p_err("Can't get arch name for device vendor id 0x%04x", vendor_id); return NULL; } diff --git a/tools/bpf/bpftool/jit_disasm.c b/tools/bpf/bpftool/jit_disasm.c index c28b21f90cb9..58a5017034a2 100644 --- a/tools/bpf/bpftool/jit_disasm.c +++ b/tools/bpf/bpftool/jit_disasm.c @@ -84,12 +84,10 @@ init_context(disasm_ctx_t *ctx, const char *arch, { char *triple; - if (arch) { - p_err("Architecture %s not supported", arch); - return -1; - } - - triple = LLVMGetDefaultTargetTriple(); + if (arch) + triple = LLVMNormalizeTargetTriple(arch); + else + triple = LLVMGetDefaultTargetTriple(); if (!triple) { p_err("Failed to retrieve triple"); return -1; @@ -128,8 +126,9 @@ disassemble_insn(disasm_ctx_t *ctx, unsigned char *image, ssize_t len, int pc) int disasm_init(void) { - LLVMInitializeNativeTarget(); - LLVMInitializeNativeDisassembler(); + LLVMInitializeAllTargetInfos(); + LLVMInitializeAllTargetMCs(); + LLVMInitializeAllDisassemblers(); return 0; } #endif /* HAVE_LLVM_SUPPORT */ diff --git a/tools/bpf/bpftool/main.h b/tools/bpf/bpftool/main.h index 9a149c67aa5d..467d8472df0c 100644 --- a/tools/bpf/bpftool/main.h +++ b/tools/bpf/bpftool/main.h @@ -203,8 +203,7 @@ void print_hex_data_json(uint8_t *data, size_t len); unsigned int get_page_size(void); unsigned int get_possible_cpus(void); const char * -ifindex_to_bfd_params(__u32 ifindex, __u64 ns_dev, __u64 ns_ino, - const char **opt); +ifindex_to_arch(__u32 ifindex, __u64 ns_dev, __u64 ns_ino, const char **opt); struct btf_dumper { const struct btf *btf; diff --git a/tools/bpf/bpftool/prog.c b/tools/bpf/bpftool/prog.c index 93dfb89b85e3..a858b907da16 100644 --- a/tools/bpf/bpftool/prog.c +++ b/tools/bpf/bpftool/prog.c @@ -764,10 +764,8 @@ prog_dump(struct bpf_prog_info *info, enum dump_mode mode, const char *name = NULL; if (info->ifindex) { - name = ifindex_to_bfd_params(info->ifindex, - info->netns_dev, - info->netns_ino, - &disasm_opt); + name = ifindex_to_arch(info->ifindex, info->netns_dev, + info->netns_ino, &disasm_opt); if (!name) goto exit_free; } From 08b8191ba7f99cd1444001e9f4800ff27607d054 Mon Sep 17 00:00:00 2001 From: Quentin Monnet Date: Tue, 25 Oct 2022 16:03:29 +0100 Subject: [PATCH 0344/2002] bpftool: Add llvm feature to "bpftool version" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Similarly to "libbfd", add a "llvm" feature to the output of command "bpftool version" to indicate that LLVM is used for disassembling JIT-ed programs. This feature is mutually exclusive (from Makefile definitions) with "libbfd". Signed-off-by: Quentin Monnet Tested-by: Niklas Söderlund Acked-by: Yonghong Song Link: https://lore.kernel.org/r/20221025150329.97371-9-quentin@isovalent.com Signed-off-by: Alexei Starovoitov --- tools/bpf/bpftool/Documentation/common_options.rst | 8 ++++---- tools/bpf/bpftool/main.c | 7 +++++++ 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/tools/bpf/bpftool/Documentation/common_options.rst b/tools/bpf/bpftool/Documentation/common_options.rst index 4107a586b68b..05350a1aadf9 100644 --- a/tools/bpf/bpftool/Documentation/common_options.rst +++ b/tools/bpf/bpftool/Documentation/common_options.rst @@ -7,10 +7,10 @@ Print bpftool's version number (similar to **bpftool version**), the number of the libbpf version in use, and optional features that were included when bpftool was compiled. Optional features include linking - against libbfd to provide the disassembler for JIT-ted programs - (**bpftool prog dump jited**) and usage of BPF skeletons (some - features like **bpftool prog profile** or showing pids associated to - BPF objects may rely on it). + against LLVM or libbfd to provide the disassembler for JIT-ted + programs (**bpftool prog dump jited**) and usage of BPF skeletons + (some features like **bpftool prog profile** or showing pids + associated to BPF objects may rely on it). -j, --json Generate JSON output. For commands that cannot produce JSON, this diff --git a/tools/bpf/bpftool/main.c b/tools/bpf/bpftool/main.c index b22223df4431..741e50ee0b6c 100644 --- a/tools/bpf/bpftool/main.c +++ b/tools/bpf/bpftool/main.c @@ -119,6 +119,11 @@ static int do_version(int argc, char **argv) #else const bool has_libbfd = false; #endif +#ifdef HAVE_LLVM_SUPPORT + const bool has_llvm = true; +#else + const bool has_llvm = false; +#endif #ifdef BPFTOOL_WITHOUT_SKELETONS const bool has_skeletons = false; #else @@ -154,6 +159,7 @@ static int do_version(int argc, char **argv) jsonw_name(json_wtr, "features"); jsonw_start_object(json_wtr); /* features */ jsonw_bool_field(json_wtr, "libbfd", has_libbfd); + jsonw_bool_field(json_wtr, "llvm", has_llvm); jsonw_bool_field(json_wtr, "libbpf_strict", !legacy_libbpf); jsonw_bool_field(json_wtr, "skeletons", has_skeletons); jsonw_bool_field(json_wtr, "bootstrap", bootstrap); @@ -172,6 +178,7 @@ static int do_version(int argc, char **argv) printf("using libbpf %s\n", libbpf_version_string()); printf("features:"); print_feature("libbfd", has_libbfd, &nb_features); + print_feature("llvm", has_llvm, &nb_features); print_feature("libbpf_strict", !legacy_libbpf, &nb_features); print_feature("skeletons", has_skeletons, &nb_features); print_feature("bootstrap", bootstrap, &nb_features); From 73feb8d5fa3b755bb51077c0aabfb6aa556fd498 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Tue, 25 Oct 2022 15:41:41 +0200 Subject: [PATCH 0345/2002] kallsyms: Make module_kallsyms_on_each_symbol generally available Making module_kallsyms_on_each_symbol generally available, so it can be used outside CONFIG_LIVEPATCH option in following changes. Rather than adding another ifdef option let's make the function generally available (when CONFIG_KALLSYMS and CONFIG_MODULES options are defined). Cc: Christoph Hellwig Acked-by: Song Liu Signed-off-by: Jiri Olsa Link: https://lore.kernel.org/r/20221025134148.3300700-2-jolsa@kernel.org Signed-off-by: Alexei Starovoitov --- include/linux/module.h | 9 +++++++++ kernel/module/kallsyms.c | 2 -- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/include/linux/module.h b/include/linux/module.h index ec61fb53979a..35876e89eb93 100644 --- a/include/linux/module.h +++ b/include/linux/module.h @@ -879,8 +879,17 @@ static inline bool module_sig_ok(struct module *module) } #endif /* CONFIG_MODULE_SIG */ +#if defined(CONFIG_MODULES) && defined(CONFIG_KALLSYMS) int module_kallsyms_on_each_symbol(int (*fn)(void *, const char *, struct module *, unsigned long), void *data); +#else +static inline int module_kallsyms_on_each_symbol(int (*fn)(void *, const char *, + struct module *, unsigned long), + void *data) +{ + return -EOPNOTSUPP; +} +#endif /* CONFIG_MODULES && CONFIG_KALLSYMS */ #endif /* _LINUX_MODULE_H */ diff --git a/kernel/module/kallsyms.c b/kernel/module/kallsyms.c index f5c5c9175333..4523f99b0358 100644 --- a/kernel/module/kallsyms.c +++ b/kernel/module/kallsyms.c @@ -494,7 +494,6 @@ unsigned long module_kallsyms_lookup_name(const char *name) return ret; } -#ifdef CONFIG_LIVEPATCH int module_kallsyms_on_each_symbol(int (*fn)(void *, const char *, struct module *, unsigned long), void *data) @@ -531,4 +530,3 @@ out: mutex_unlock(&module_mutex); return ret; } -#endif /* CONFIG_LIVEPATCH */ From 3640bf8584f4ab0f5eed6285f09213954acd8b62 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Tue, 25 Oct 2022 15:41:42 +0200 Subject: [PATCH 0346/2002] ftrace: Add support to resolve module symbols in ftrace_lookup_symbols Currently ftrace_lookup_symbols iterates only over core symbols, adding module_kallsyms_on_each_symbol call to check on modules symbols as well. Also removing 'args.found == args.cnt' condition, because it's already checked in kallsyms_callback function. Also removing 'err < 0' check, because both *kallsyms_on_each_symbol functions do not return error. Reported-by: Martynas Pumputis Acked-by: Song Liu Signed-off-by: Jiri Olsa Link: https://lore.kernel.org/r/20221025134148.3300700-3-jolsa@kernel.org Signed-off-by: Alexei Starovoitov --- kernel/trace/ftrace.c | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c index fbf2543111c0..72de9009a6a0 100644 --- a/kernel/trace/ftrace.c +++ b/kernel/trace/ftrace.c @@ -8267,6 +8267,10 @@ struct kallsyms_data { size_t found; }; +/* This function gets called for all kernel and module symbols + * and returns 1 in case we resolved all the requested symbols, + * 0 otherwise. + */ static int kallsyms_callback(void *data, const char *name, struct module *mod, unsigned long addr) { @@ -8309,17 +8313,19 @@ static int kallsyms_callback(void *data, const char *name, int ftrace_lookup_symbols(const char **sorted_syms, size_t cnt, unsigned long *addrs) { struct kallsyms_data args; - int err; + int found_all; memset(addrs, 0, sizeof(*addrs) * cnt); args.addrs = addrs; args.syms = sorted_syms; args.cnt = cnt; args.found = 0; - err = kallsyms_on_each_symbol(kallsyms_callback, &args); - if (err < 0) - return err; - return args.found == args.cnt ? 0 : -ESRCH; + + found_all = kallsyms_on_each_symbol(kallsyms_callback, &args); + if (found_all) + return 0; + found_all = module_kallsyms_on_each_symbol(kallsyms_callback, &args); + return found_all ? 0 : -ESRCH; } #ifdef CONFIG_SYSCTL From 1a1b0716d36d21f8448bd7d3f1c0ade7230bb294 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Tue, 25 Oct 2022 15:41:43 +0200 Subject: [PATCH 0347/2002] bpf: Rename __bpf_kprobe_multi_cookie_cmp to bpf_kprobe_multi_addrs_cmp Renaming __bpf_kprobe_multi_cookie_cmp to bpf_kprobe_multi_addrs_cmp, because it's more suitable to current and upcoming code. Acked-by: Song Liu Signed-off-by: Jiri Olsa Link: https://lore.kernel.org/r/20221025134148.3300700-4-jolsa@kernel.org Signed-off-by: Alexei Starovoitov --- kernel/trace/bpf_trace.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/kernel/trace/bpf_trace.c b/kernel/trace/bpf_trace.c index 49fb9ec8366d..2e1f03dcad10 100644 --- a/kernel/trace/bpf_trace.c +++ b/kernel/trace/bpf_trace.c @@ -2548,7 +2548,7 @@ static void bpf_kprobe_multi_cookie_swap(void *a, void *b, int size, const void swap(*cookie_a, *cookie_b); } -static int __bpf_kprobe_multi_cookie_cmp(const void *a, const void *b) +static int bpf_kprobe_multi_addrs_cmp(const void *a, const void *b) { const unsigned long *addr_a = a, *addr_b = b; @@ -2559,7 +2559,7 @@ static int __bpf_kprobe_multi_cookie_cmp(const void *a, const void *b) static int bpf_kprobe_multi_cookie_cmp(const void *a, const void *b, const void *priv) { - return __bpf_kprobe_multi_cookie_cmp(a, b); + return bpf_kprobe_multi_addrs_cmp(a, b); } static u64 bpf_kprobe_multi_cookie(struct bpf_run_ctx *ctx) @@ -2577,7 +2577,7 @@ static u64 bpf_kprobe_multi_cookie(struct bpf_run_ctx *ctx) return 0; entry_ip = run_ctx->entry_ip; addr = bsearch(&entry_ip, link->addrs, link->cnt, sizeof(entry_ip), - __bpf_kprobe_multi_cookie_cmp); + bpf_kprobe_multi_addrs_cmp); if (!addr) return 0; cookie = link->cookies + (addr - link->addrs); From e22061b2d3095c12f90336479f24bf5eeb70e1bd Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Tue, 25 Oct 2022 15:41:44 +0200 Subject: [PATCH 0348/2002] bpf: Take module reference on kprobe_multi link Currently we allow to create kprobe multi link on function from kernel module, but we don't take the module reference to ensure it's not unloaded while we are tracing it. The multi kprobe link is based on fprobe/ftrace layer which takes different approach and releases ftrace hooks when module is unloaded even if there's tracer registered on top of it. Adding code that gathers all the related modules for the link and takes their references before it's attached. All kernel module references are released after link is unregistered. Note that we do it the same way already for trampoline probes (but for single address). Acked-by: Andrii Nakryiko Signed-off-by: Jiri Olsa Link: https://lore.kernel.org/r/20221025134148.3300700-5-jolsa@kernel.org Signed-off-by: Alexei Starovoitov --- kernel/trace/bpf_trace.c | 92 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 92 insertions(+) diff --git a/kernel/trace/bpf_trace.c b/kernel/trace/bpf_trace.c index 2e1f03dcad10..2460e38f75ff 100644 --- a/kernel/trace/bpf_trace.c +++ b/kernel/trace/bpf_trace.c @@ -2450,6 +2450,8 @@ struct bpf_kprobe_multi_link { unsigned long *addrs; u64 *cookies; u32 cnt; + u32 mods_cnt; + struct module **mods; }; struct bpf_kprobe_multi_run_ctx { @@ -2505,6 +2507,14 @@ error: return err; } +static void kprobe_multi_put_modules(struct module **mods, u32 cnt) +{ + u32 i; + + for (i = 0; i < cnt; i++) + module_put(mods[i]); +} + static void free_user_syms(struct user_syms *us) { kvfree(us->syms); @@ -2517,6 +2527,7 @@ static void bpf_kprobe_multi_link_release(struct bpf_link *link) kmulti_link = container_of(link, struct bpf_kprobe_multi_link, link); unregister_fprobe(&kmulti_link->fp); + kprobe_multi_put_modules(kmulti_link->mods, kmulti_link->mods_cnt); } static void bpf_kprobe_multi_link_dealloc(struct bpf_link *link) @@ -2526,6 +2537,7 @@ static void bpf_kprobe_multi_link_dealloc(struct bpf_link *link) kmulti_link = container_of(link, struct bpf_kprobe_multi_link, link); kvfree(kmulti_link->addrs); kvfree(kmulti_link->cookies); + kfree(kmulti_link->mods); kfree(kmulti_link); } @@ -2661,6 +2673,71 @@ static void symbols_swap_r(void *a, void *b, int size, const void *priv) } } +struct module_addr_args { + unsigned long *addrs; + u32 addrs_cnt; + struct module **mods; + int mods_cnt; + int mods_cap; +}; + +static int module_callback(void *data, const char *name, + struct module *mod, unsigned long addr) +{ + struct module_addr_args *args = data; + struct module **mods; + + /* We iterate all modules symbols and for each we: + * - search for it in provided addresses array + * - if found we check if we already have the module pointer stored + * (we iterate modules sequentially, so we can check just the last + * module pointer) + * - take module reference and store it + */ + if (!bsearch(&addr, args->addrs, args->addrs_cnt, sizeof(addr), + bpf_kprobe_multi_addrs_cmp)) + return 0; + + if (args->mods && args->mods[args->mods_cnt - 1] == mod) + return 0; + + if (args->mods_cnt == args->mods_cap) { + args->mods_cap = max(16, args->mods_cap * 3 / 2); + mods = krealloc_array(args->mods, args->mods_cap, sizeof(*mods), GFP_KERNEL); + if (!mods) + return -ENOMEM; + args->mods = mods; + } + + if (!try_module_get(mod)) + return -EINVAL; + + args->mods[args->mods_cnt] = mod; + args->mods_cnt++; + return 0; +} + +static int get_modules_for_addrs(struct module ***mods, unsigned long *addrs, u32 addrs_cnt) +{ + struct module_addr_args args = { + .addrs = addrs, + .addrs_cnt = addrs_cnt, + }; + int err; + + /* We return either err < 0 in case of error, ... */ + err = module_kallsyms_on_each_symbol(module_callback, &args); + if (err) { + kprobe_multi_put_modules(args.mods, args.mods_cnt); + kfree(args.mods); + return err; + } + + /* or number of modules found if everything is ok. */ + *mods = args.mods; + return args.mods_cnt; +} + int bpf_kprobe_multi_link_attach(const union bpf_attr *attr, struct bpf_prog *prog) { struct bpf_kprobe_multi_link *link = NULL; @@ -2771,10 +2848,25 @@ int bpf_kprobe_multi_link_attach(const union bpf_attr *attr, struct bpf_prog *pr bpf_kprobe_multi_cookie_cmp, bpf_kprobe_multi_cookie_swap, link); + } else { + /* + * We need to sort addrs array even if there are no cookies + * provided, to allow bsearch in get_modules_for_addrs. + */ + sort(addrs, cnt, sizeof(*addrs), + bpf_kprobe_multi_addrs_cmp, NULL); } + err = get_modules_for_addrs(&link->mods, addrs, cnt); + if (err < 0) { + bpf_link_cleanup(&link_primer); + return err; + } + link->mods_cnt = err; + err = register_fprobe_ips(&link->fp, addrs, cnt); if (err) { + kprobe_multi_put_modules(link->mods, link->mods_cnt); bpf_link_cleanup(&link_primer); return err; } From 10705b2b7a8e4eb46ab5bf1b9ee354cb9a929428 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Tue, 25 Oct 2022 15:41:45 +0200 Subject: [PATCH 0349/2002] selftests/bpf: Add load_kallsyms_refresh function Adding load_kallsyms_refresh function to re-read symbols from /proc/kallsyms file. This will be needed to get proper functions addresses from bpf_testmod.ko module, which is loaded/unloaded several times during the tests run, so symbols might be already old when we need to use them. Acked-by: Song Liu Signed-off-by: Jiri Olsa Link: https://lore.kernel.org/r/20221025134148.3300700-6-jolsa@kernel.org Signed-off-by: Alexei Starovoitov --- tools/testing/selftests/bpf/trace_helpers.c | 20 +++++++++++++------- tools/testing/selftests/bpf/trace_helpers.h | 2 ++ 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/tools/testing/selftests/bpf/trace_helpers.c b/tools/testing/selftests/bpf/trace_helpers.c index 9c4be2cdb21a..09a16a77bae4 100644 --- a/tools/testing/selftests/bpf/trace_helpers.c +++ b/tools/testing/selftests/bpf/trace_helpers.c @@ -23,7 +23,7 @@ static int ksym_cmp(const void *p1, const void *p2) return ((struct ksym *)p1)->addr - ((struct ksym *)p2)->addr; } -int load_kallsyms(void) +int load_kallsyms_refresh(void) { FILE *f; char func[256], buf[256]; @@ -31,12 +31,7 @@ int load_kallsyms(void) void *addr; int i = 0; - /* - * This is called/used from multiplace places, - * load symbols just once. - */ - if (sym_cnt) - return 0; + sym_cnt = 0; f = fopen("/proc/kallsyms", "r"); if (!f) @@ -57,6 +52,17 @@ int load_kallsyms(void) return 0; } +int load_kallsyms(void) +{ + /* + * This is called/used from multiplace places, + * load symbols just once. + */ + if (sym_cnt) + return 0; + return load_kallsyms_refresh(); +} + struct ksym *ksym_search(long key) { int start = 0, end = sym_cnt; diff --git a/tools/testing/selftests/bpf/trace_helpers.h b/tools/testing/selftests/bpf/trace_helpers.h index 238a9c98cde2..53efde0e2998 100644 --- a/tools/testing/selftests/bpf/trace_helpers.h +++ b/tools/testing/selftests/bpf/trace_helpers.h @@ -10,6 +10,8 @@ struct ksym { }; int load_kallsyms(void); +int load_kallsyms_refresh(void); + struct ksym *ksym_search(long key); long ksym_get_addr(const char *name); From fee356ede980b6c2c8db612e18b25738356d6744 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Tue, 25 Oct 2022 15:41:46 +0200 Subject: [PATCH 0350/2002] selftests/bpf: Add bpf_testmod_fentry_* functions Adding 3 bpf_testmod_fentry_* functions to have a way to test kprobe multi link on kernel module. They follow bpf_fentry_test* functions prototypes/code. Adding equivalent functions to all bpf_fentry_test* does not seems necessary at the moment, could be added later. Acked-by: Song Liu Signed-off-by: Jiri Olsa Link: https://lore.kernel.org/r/20221025134148.3300700-7-jolsa@kernel.org Signed-off-by: Alexei Starovoitov --- .../selftests/bpf/bpf_testmod/bpf_testmod.c | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/tools/testing/selftests/bpf/bpf_testmod/bpf_testmod.c b/tools/testing/selftests/bpf/bpf_testmod/bpf_testmod.c index a6021d6117b5..5085fea3cac5 100644 --- a/tools/testing/selftests/bpf/bpf_testmod/bpf_testmod.c +++ b/tools/testing/selftests/bpf/bpf_testmod/bpf_testmod.c @@ -128,6 +128,23 @@ __weak noinline struct file *bpf_testmod_return_ptr(int arg) } } +noinline int bpf_testmod_fentry_test1(int a) +{ + return a + 1; +} + +noinline int bpf_testmod_fentry_test2(int a, u64 b) +{ + return a + b; +} + +noinline int bpf_testmod_fentry_test3(char a, int b, u64 c) +{ + return a + b + c; +} + +int bpf_testmod_fentry_ok; + noinline ssize_t bpf_testmod_test_read(struct file *file, struct kobject *kobj, struct bin_attribute *bin_attr, @@ -167,6 +184,13 @@ bpf_testmod_test_read(struct file *file, struct kobject *kobj, return snprintf(buf, len, "%d\n", writable.val); } + if (bpf_testmod_fentry_test1(1) != 2 || + bpf_testmod_fentry_test2(2, 3) != 5 || + bpf_testmod_fentry_test3(4, 5, 6) != 15) + goto out; + + bpf_testmod_fentry_ok = 1; +out: return -EIO; /* always fail */ } EXPORT_SYMBOL(bpf_testmod_test_read); From e697d8dcebd2f557fa5e5ed57aaf0a9992ce9df8 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Tue, 25 Oct 2022 15:41:47 +0200 Subject: [PATCH 0351/2002] selftests/bpf: Add kprobe_multi check to module attach test Adding test that makes sure the kernel module won't be removed if there's kprobe multi link defined on top of it. Acked-by: Song Liu Signed-off-by: Jiri Olsa Link: https://lore.kernel.org/r/20221025134148.3300700-8-jolsa@kernel.org Signed-off-by: Alexei Starovoitov --- tools/testing/selftests/bpf/prog_tests/module_attach.c | 7 +++++++ tools/testing/selftests/bpf/progs/test_module_attach.c | 6 ++++++ 2 files changed, 13 insertions(+) diff --git a/tools/testing/selftests/bpf/prog_tests/module_attach.c b/tools/testing/selftests/bpf/prog_tests/module_attach.c index 6d0e50dcf47c..7fc01ff490db 100644 --- a/tools/testing/selftests/bpf/prog_tests/module_attach.c +++ b/tools/testing/selftests/bpf/prog_tests/module_attach.c @@ -103,6 +103,13 @@ void test_module_attach(void) ASSERT_ERR(delete_module("bpf_testmod", 0), "delete_module"); bpf_link__destroy(link); + link = bpf_program__attach(skel->progs.kprobe_multi); + if (!ASSERT_OK_PTR(link, "attach_kprobe_multi")) + goto cleanup; + + ASSERT_ERR(delete_module("bpf_testmod", 0), "delete_module"); + bpf_link__destroy(link); + cleanup: test_module_attach__destroy(skel); } diff --git a/tools/testing/selftests/bpf/progs/test_module_attach.c b/tools/testing/selftests/bpf/progs/test_module_attach.c index 08628afedb77..8a1b50f3a002 100644 --- a/tools/testing/selftests/bpf/progs/test_module_attach.c +++ b/tools/testing/selftests/bpf/progs/test_module_attach.c @@ -110,4 +110,10 @@ int BPF_PROG(handle_fmod_ret, return 0; /* don't override the exit code */ } +SEC("kprobe.multi/bpf_testmod_test_read") +int BPF_PROG(kprobe_multi) +{ + return 0; +} + char _license[] SEC("license") = "GPL"; From b2440443a64f1b687df364089e6dcb23da4f9598 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Tue, 25 Oct 2022 15:41:48 +0200 Subject: [PATCH 0352/2002] selftests/bpf: Add kprobe_multi kmod attach api tests Adding kprobe_multi kmod attach api tests that attach bpf_testmod functions via bpf_program__attach_kprobe_multi_opts. Running it as serial test, because we don't want other tests to reload bpf_testmod while it's running. Acked-by: Song Liu Signed-off-by: Jiri Olsa Link: https://lore.kernel.org/r/20221025134148.3300700-9-jolsa@kernel.org Signed-off-by: Alexei Starovoitov --- .../prog_tests/kprobe_multi_testmod_test.c | 89 +++++++++++++++++++ .../selftests/bpf/progs/kprobe_multi.c | 50 +++++++++++ 2 files changed, 139 insertions(+) create mode 100644 tools/testing/selftests/bpf/prog_tests/kprobe_multi_testmod_test.c diff --git a/tools/testing/selftests/bpf/prog_tests/kprobe_multi_testmod_test.c b/tools/testing/selftests/bpf/prog_tests/kprobe_multi_testmod_test.c new file mode 100644 index 000000000000..1fbe7e4ac00a --- /dev/null +++ b/tools/testing/selftests/bpf/prog_tests/kprobe_multi_testmod_test.c @@ -0,0 +1,89 @@ +// SPDX-License-Identifier: GPL-2.0 +#include +#include "kprobe_multi.skel.h" +#include "trace_helpers.h" +#include "bpf/libbpf_internal.h" + +static void kprobe_multi_testmod_check(struct kprobe_multi *skel) +{ + ASSERT_EQ(skel->bss->kprobe_testmod_test1_result, 1, "kprobe_test1_result"); + ASSERT_EQ(skel->bss->kprobe_testmod_test2_result, 1, "kprobe_test2_result"); + ASSERT_EQ(skel->bss->kprobe_testmod_test3_result, 1, "kprobe_test3_result"); + + ASSERT_EQ(skel->bss->kretprobe_testmod_test1_result, 1, "kretprobe_test1_result"); + ASSERT_EQ(skel->bss->kretprobe_testmod_test2_result, 1, "kretprobe_test2_result"); + ASSERT_EQ(skel->bss->kretprobe_testmod_test3_result, 1, "kretprobe_test3_result"); +} + +static void test_testmod_attach_api(struct bpf_kprobe_multi_opts *opts) +{ + struct kprobe_multi *skel = NULL; + + skel = kprobe_multi__open_and_load(); + if (!ASSERT_OK_PTR(skel, "fentry_raw_skel_load")) + return; + + skel->bss->pid = getpid(); + + skel->links.test_kprobe_testmod = bpf_program__attach_kprobe_multi_opts( + skel->progs.test_kprobe_testmod, + NULL, opts); + if (!skel->links.test_kprobe_testmod) + goto cleanup; + + opts->retprobe = true; + skel->links.test_kretprobe_testmod = bpf_program__attach_kprobe_multi_opts( + skel->progs.test_kretprobe_testmod, + NULL, opts); + if (!skel->links.test_kretprobe_testmod) + goto cleanup; + + ASSERT_OK(trigger_module_test_read(1), "trigger_read"); + kprobe_multi_testmod_check(skel); + +cleanup: + kprobe_multi__destroy(skel); +} + +static void test_testmod_attach_api_addrs(void) +{ + LIBBPF_OPTS(bpf_kprobe_multi_opts, opts); + unsigned long long addrs[3]; + + addrs[0] = ksym_get_addr("bpf_testmod_fentry_test1"); + ASSERT_NEQ(addrs[0], 0, "ksym_get_addr"); + addrs[1] = ksym_get_addr("bpf_testmod_fentry_test2"); + ASSERT_NEQ(addrs[1], 0, "ksym_get_addr"); + addrs[2] = ksym_get_addr("bpf_testmod_fentry_test3"); + ASSERT_NEQ(addrs[2], 0, "ksym_get_addr"); + + opts.addrs = (const unsigned long *) addrs; + opts.cnt = ARRAY_SIZE(addrs); + + test_testmod_attach_api(&opts); +} + +static void test_testmod_attach_api_syms(void) +{ + LIBBPF_OPTS(bpf_kprobe_multi_opts, opts); + const char *syms[3] = { + "bpf_testmod_fentry_test1", + "bpf_testmod_fentry_test2", + "bpf_testmod_fentry_test3", + }; + + opts.syms = syms; + opts.cnt = ARRAY_SIZE(syms); + test_testmod_attach_api(&opts); +} + +void serial_test_kprobe_multi_testmod_test(void) +{ + if (!ASSERT_OK(load_kallsyms_refresh(), "load_kallsyms_refresh")) + return; + + if (test__start_subtest("testmod_attach_api_syms")) + test_testmod_attach_api_syms(); + if (test__start_subtest("testmod_attach_api_addrs")) + test_testmod_attach_api_addrs(); +} diff --git a/tools/testing/selftests/bpf/progs/kprobe_multi.c b/tools/testing/selftests/bpf/progs/kprobe_multi.c index 98c3399e15c0..9e1ca8e34913 100644 --- a/tools/testing/selftests/bpf/progs/kprobe_multi.c +++ b/tools/testing/selftests/bpf/progs/kprobe_multi.c @@ -110,3 +110,53 @@ int test_kretprobe_manual(struct pt_regs *ctx) kprobe_multi_check(ctx, true); return 0; } + +extern const void bpf_testmod_fentry_test1 __ksym; +extern const void bpf_testmod_fentry_test2 __ksym; +extern const void bpf_testmod_fentry_test3 __ksym; + +__u64 kprobe_testmod_test1_result = 0; +__u64 kprobe_testmod_test2_result = 0; +__u64 kprobe_testmod_test3_result = 0; + +__u64 kretprobe_testmod_test1_result = 0; +__u64 kretprobe_testmod_test2_result = 0; +__u64 kretprobe_testmod_test3_result = 0; + +static void kprobe_multi_testmod_check(void *ctx, bool is_return) +{ + if (bpf_get_current_pid_tgid() >> 32 != pid) + return; + + __u64 addr = bpf_get_func_ip(ctx); + + if (is_return) { + if ((const void *) addr == &bpf_testmod_fentry_test1) + kretprobe_testmod_test1_result = 1; + if ((const void *) addr == &bpf_testmod_fentry_test2) + kretprobe_testmod_test2_result = 1; + if ((const void *) addr == &bpf_testmod_fentry_test3) + kretprobe_testmod_test3_result = 1; + } else { + if ((const void *) addr == &bpf_testmod_fentry_test1) + kprobe_testmod_test1_result = 1; + if ((const void *) addr == &bpf_testmod_fentry_test2) + kprobe_testmod_test2_result = 1; + if ((const void *) addr == &bpf_testmod_fentry_test3) + kprobe_testmod_test3_result = 1; + } +} + +SEC("kprobe.multi") +int test_kprobe_testmod(struct pt_regs *ctx) +{ + kprobe_multi_testmod_check(ctx, false); + return 0; +} + +SEC("kretprobe.multi") +int test_kretprobe_testmod(struct pt_regs *ctx) +{ + kprobe_multi_testmod_check(ctx, true); + return 0; +} From b5f0de6df6dce8d641ef58ef7012f3304dffb9a1 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Tue, 18 Oct 2022 02:56:03 -0700 Subject: [PATCH 0353/2002] net: dev: Convert sa_data to flexible array in struct sockaddr One of the worst offenders of "fake flexible arrays" is struct sockaddr, as it is the classic example of why GCC and Clang have been traditionally forced to treat all trailing arrays as fake flexible arrays: in the distant misty past, sa_data became too small, and code started just treating it as a flexible array, even though it was fixed-size. The special case by the compiler is specifically that sizeof(sa->sa_data) and FORTIFY_SOURCE (which uses __builtin_object_size(sa->sa_data, 1)) do not agree (14 and -1 respectively), which makes FORTIFY_SOURCE treat it as a flexible array. However, the coming -fstrict-flex-arrays compiler flag will remove these special cases so that FORTIFY_SOURCE can gain coverage over all the trailing arrays in the kernel that are _not_ supposed to be treated as a flexible array. To deal with this change, convert sa_data to a true flexible array. To keep the structure size the same, move sa_data into a union with a newly introduced sa_data_min with the original size. The result is that FORTIFY_SOURCE can continue to have no idea how large sa_data may actually be, but anything using sizeof(sa->sa_data) must switch to sizeof(sa->sa_data_min). Cc: Jens Axboe Cc: Pavel Begunkov Cc: David Ahern Cc: Dylan Yudaken Cc: Yajun Deng Cc: Petr Machata Cc: Hangbin Liu Cc: Leon Romanovsky Cc: syzbot Cc: Willem de Bruijn Cc: Pablo Neira Ayuso Signed-off-by: Kees Cook Link: https://lore.kernel.org/r/20221018095503.never.671-kees@kernel.org Signed-off-by: Jakub Kicinski --- include/linux/socket.h | 5 ++++- net/core/dev.c | 2 +- net/core/dev_ioctl.c | 2 +- net/packet/af_packet.c | 10 +++++----- 4 files changed, 11 insertions(+), 8 deletions(-) diff --git a/include/linux/socket.h b/include/linux/socket.h index de3701a2a212..13c3a237b9c9 100644 --- a/include/linux/socket.h +++ b/include/linux/socket.h @@ -33,7 +33,10 @@ typedef __kernel_sa_family_t sa_family_t; struct sockaddr { sa_family_t sa_family; /* address family, AF_xxx */ - char sa_data[14]; /* 14 bytes of protocol address */ + union { + char sa_data_min[14]; /* Minimum 14 bytes of protocol address */ + DECLARE_FLEX_ARRAY(char, sa_data); + }; }; struct linger { diff --git a/net/core/dev.c b/net/core/dev.c index 3be256051e99..fff62068a53d 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -8822,7 +8822,7 @@ EXPORT_SYMBOL(dev_set_mac_address_user); int dev_get_mac_address(struct sockaddr *sa, struct net *net, char *dev_name) { - size_t size = sizeof(sa->sa_data); + size_t size = sizeof(sa->sa_data_min); struct net_device *dev; int ret = 0; diff --git a/net/core/dev_ioctl.c b/net/core/dev_ioctl.c index 7674bb9f3076..5cdbfbf9a7dc 100644 --- a/net/core/dev_ioctl.c +++ b/net/core/dev_ioctl.c @@ -342,7 +342,7 @@ static int dev_ifsioc(struct net *net, struct ifreq *ifr, void __user *data, if (ifr->ifr_hwaddr.sa_family != dev->type) return -EINVAL; memcpy(dev->broadcast, ifr->ifr_hwaddr.sa_data, - min(sizeof(ifr->ifr_hwaddr.sa_data), + min(sizeof(ifr->ifr_hwaddr.sa_data_min), (size_t)dev->addr_len)); call_netdevice_notifiers(NETDEV_CHANGEADDR, dev); return 0; diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c index 6ce8dd19f33c..8c5b3da0c29f 100644 --- a/net/packet/af_packet.c +++ b/net/packet/af_packet.c @@ -3277,7 +3277,7 @@ static int packet_bind_spkt(struct socket *sock, struct sockaddr *uaddr, int addr_len) { struct sock *sk = sock->sk; - char name[sizeof(uaddr->sa_data) + 1]; + char name[sizeof(uaddr->sa_data_min) + 1]; /* * Check legality @@ -3288,8 +3288,8 @@ static int packet_bind_spkt(struct socket *sock, struct sockaddr *uaddr, /* uaddr->sa_data comes from the userspace, it's not guaranteed to be * zero-terminated. */ - memcpy(name, uaddr->sa_data, sizeof(uaddr->sa_data)); - name[sizeof(uaddr->sa_data)] = 0; + memcpy(name, uaddr->sa_data, sizeof(uaddr->sa_data_min)); + name[sizeof(uaddr->sa_data_min)] = 0; return packet_do_bind(sk, name, 0, pkt_sk(sk)->num); } @@ -3561,11 +3561,11 @@ static int packet_getname_spkt(struct socket *sock, struct sockaddr *uaddr, return -EOPNOTSUPP; uaddr->sa_family = AF_PACKET; - memset(uaddr->sa_data, 0, sizeof(uaddr->sa_data)); + memset(uaddr->sa_data, 0, sizeof(uaddr->sa_data_min)); rcu_read_lock(); dev = dev_get_by_index_rcu(sock_net(sk), READ_ONCE(pkt_sk(sk)->ifindex)); if (dev) - strscpy(uaddr->sa_data, dev->name, sizeof(uaddr->sa_data)); + strscpy(uaddr->sa_data, dev->name, sizeof(uaddr->sa_data_min)); rcu_read_unlock(); return sizeof(*uaddr); From cdc045402594f0ba635f1f1a7c810e6ff93485be Mon Sep 17 00:00:00 2001 From: Raju Lakkaraju Date: Mon, 24 Oct 2022 13:55:15 +0530 Subject: [PATCH 0354/2002] net: lan743x: Add support for get_pauseparam and set_pauseparam Add pause get and set functions Signed-off-by: Raju Lakkaraju Reviewed-by: Andrew Lunn Signed-off-by: Jakub Kicinski --- .../net/ethernet/microchip/lan743x_ethtool.c | 46 +++++++++++++++++++ drivers/net/ethernet/microchip/lan743x_main.c | 4 +- drivers/net/ethernet/microchip/lan743x_main.h | 2 + 3 files changed, 50 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/microchip/lan743x_ethtool.c b/drivers/net/ethernet/microchip/lan743x_ethtool.c index c739d60ee17d..88f9484cc2a7 100644 --- a/drivers/net/ethernet/microchip/lan743x_ethtool.c +++ b/drivers/net/ethernet/microchip/lan743x_ethtool.c @@ -1233,6 +1233,50 @@ static void lan743x_get_regs(struct net_device *dev, lan743x_common_regs(dev, regs, p); } +static void lan743x_get_pauseparam(struct net_device *dev, + struct ethtool_pauseparam *pause) +{ + struct lan743x_adapter *adapter = netdev_priv(dev); + struct lan743x_phy *phy = &adapter->phy; + + if (phy->fc_request_control & FLOW_CTRL_TX) + pause->tx_pause = 1; + if (phy->fc_request_control & FLOW_CTRL_RX) + pause->rx_pause = 1; + pause->autoneg = phy->fc_autoneg; +} + +static int lan743x_set_pauseparam(struct net_device *dev, + struct ethtool_pauseparam *pause) +{ + struct lan743x_adapter *adapter = netdev_priv(dev); + struct phy_device *phydev = dev->phydev; + struct lan743x_phy *phy = &adapter->phy; + + if (!phydev) + return -ENODEV; + + if (!phy_validate_pause(phydev, pause)) + return -EINVAL; + + phy->fc_request_control = 0; + if (pause->rx_pause) + phy->fc_request_control |= FLOW_CTRL_RX; + + if (pause->tx_pause) + phy->fc_request_control |= FLOW_CTRL_TX; + + phy->fc_autoneg = pause->autoneg; + + if (pause->autoneg == AUTONEG_DISABLE) + lan743x_mac_flow_ctrl_set_enables(adapter, pause->tx_pause, + pause->rx_pause); + else + phy_set_asym_pause(phydev, pause->rx_pause, pause->tx_pause); + + return 0; +} + const struct ethtool_ops lan743x_ethtool_ops = { .get_drvinfo = lan743x_ethtool_get_drvinfo, .get_msglevel = lan743x_ethtool_get_msglevel, @@ -1259,6 +1303,8 @@ const struct ethtool_ops lan743x_ethtool_ops = { .set_link_ksettings = phy_ethtool_set_link_ksettings, .get_regs_len = lan743x_get_regs_len, .get_regs = lan743x_get_regs, + .get_pauseparam = lan743x_get_pauseparam, + .set_pauseparam = lan743x_set_pauseparam, #ifdef CONFIG_PM .get_wol = lan743x_ethtool_get_wol, .set_wol = lan743x_ethtool_set_wol, diff --git a/drivers/net/ethernet/microchip/lan743x_main.c b/drivers/net/ethernet/microchip/lan743x_main.c index 50eeecba1f18..c0f8ba601c01 100644 --- a/drivers/net/ethernet/microchip/lan743x_main.c +++ b/drivers/net/ethernet/microchip/lan743x_main.c @@ -1326,8 +1326,8 @@ static void lan743x_mac_close(struct lan743x_adapter *adapter) 1, 1000, 20000, 100); } -static void lan743x_mac_flow_ctrl_set_enables(struct lan743x_adapter *adapter, - bool tx_enable, bool rx_enable) +void lan743x_mac_flow_ctrl_set_enables(struct lan743x_adapter *adapter, + bool tx_enable, bool rx_enable) { u32 flow_setting = 0; diff --git a/drivers/net/ethernet/microchip/lan743x_main.h b/drivers/net/ethernet/microchip/lan743x_main.h index 67877d3b6dd9..bc5eea4c7b40 100644 --- a/drivers/net/ethernet/microchip/lan743x_main.h +++ b/drivers/net/ethernet/microchip/lan743x_main.h @@ -1159,5 +1159,7 @@ u32 lan743x_csr_read(struct lan743x_adapter *adapter, int offset); void lan743x_csr_write(struct lan743x_adapter *adapter, int offset, u32 data); int lan743x_hs_syslock_acquire(struct lan743x_adapter *adapter, u16 timeout); void lan743x_hs_syslock_release(struct lan743x_adapter *adapter); +void lan743x_mac_flow_ctrl_set_enables(struct lan743x_adapter *adapter, + bool tx_enable, bool rx_enable); #endif /* _LAN743X_H */ From b64e6a8794d9d56f9713b6ceb9e3fdfc9c5f99af Mon Sep 17 00:00:00 2001 From: Raju Lakkaraju Date: Mon, 24 Oct 2022 13:55:16 +0530 Subject: [PATCH 0355/2002] net: phy: micrel: Add PHY Auto/MDI/MDI-X set driver for KSZ9131 Add support for MDI-X status and configuration for KSZ9131 chips Signed-off-by: Raju Lakkaraju Reviewed-by: Horatiu Vultur Reviewed-by: Andrew Lunn Signed-off-by: Jakub Kicinski --- drivers/net/phy/micrel.c | 77 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) diff --git a/drivers/net/phy/micrel.c b/drivers/net/phy/micrel.c index 54a17b576eac..26ce0c5defcd 100644 --- a/drivers/net/phy/micrel.c +++ b/drivers/net/phy/micrel.c @@ -1295,6 +1295,81 @@ static int ksz9131_config_init(struct phy_device *phydev) return 0; } +#define MII_KSZ9131_AUTO_MDIX 0x1C +#define MII_KSZ9131_AUTO_MDI_SET BIT(7) +#define MII_KSZ9131_AUTO_MDIX_SWAP_OFF BIT(6) + +static int ksz9131_mdix_update(struct phy_device *phydev) +{ + int ret; + + ret = phy_read(phydev, MII_KSZ9131_AUTO_MDIX); + if (ret < 0) + return ret; + + if (ret & MII_KSZ9131_AUTO_MDIX_SWAP_OFF) { + if (ret & MII_KSZ9131_AUTO_MDI_SET) + phydev->mdix_ctrl = ETH_TP_MDI; + else + phydev->mdix_ctrl = ETH_TP_MDI_X; + } else { + phydev->mdix_ctrl = ETH_TP_MDI_AUTO; + } + + if (ret & MII_KSZ9131_AUTO_MDI_SET) + phydev->mdix = ETH_TP_MDI; + else + phydev->mdix = ETH_TP_MDI_X; + + return 0; +} + +static int ksz9131_config_mdix(struct phy_device *phydev, u8 ctrl) +{ + u16 val; + + switch (ctrl) { + case ETH_TP_MDI: + val = MII_KSZ9131_AUTO_MDIX_SWAP_OFF | + MII_KSZ9131_AUTO_MDI_SET; + break; + case ETH_TP_MDI_X: + val = MII_KSZ9131_AUTO_MDIX_SWAP_OFF; + break; + case ETH_TP_MDI_AUTO: + val = 0; + break; + default: + return 0; + } + + return phy_modify(phydev, MII_KSZ9131_AUTO_MDIX, + MII_KSZ9131_AUTO_MDIX_SWAP_OFF | + MII_KSZ9131_AUTO_MDI_SET, val); +} + +static int ksz9131_read_status(struct phy_device *phydev) +{ + int ret; + + ret = ksz9131_mdix_update(phydev); + if (ret < 0) + return ret; + + return genphy_read_status(phydev); +} + +static int ksz9131_config_aneg(struct phy_device *phydev) +{ + int ret; + + ret = ksz9131_config_mdix(phydev, phydev->mdix_ctrl); + if (ret) + return ret; + + return genphy_config_aneg(phydev); +} + #define KSZ8873MLL_GLOBAL_CONTROL_4 0x06 #define KSZ8873MLL_GLOBAL_CONTROL_4_DUPLEX BIT(6) #define KSZ8873MLL_GLOBAL_CONTROL_4_SPEED BIT(4) @@ -3304,6 +3379,8 @@ static struct phy_driver ksphy_driver[] = { .probe = kszphy_probe, .config_init = ksz9131_config_init, .config_intr = kszphy_config_intr, + .config_aneg = ksz9131_config_aneg, + .read_status = ksz9131_read_status, .handle_interrupt = kszphy_handle_interrupt, .get_sset_count = kszphy_get_sset_count, .get_strings = kszphy_get_strings, From f3c51fe02c55bd944662714e5b91b96dc271ad9f Mon Sep 17 00:00:00 2001 From: Alan Maguire Date: Mon, 24 Oct 2022 15:38:29 +0100 Subject: [PATCH 0356/2002] libbpf: Btf dedup identical struct test needs check for nested structs/arrays When examining module BTF, it is common to see core kernel structures such as sk_buff, net_device duplicated in the module. After adding debug messaging to BTF it turned out that much of the problem was down to the identical struct test failing during deduplication; sometimes the compiler adds identical structs. However it turns out sometimes that type ids of identical struct members can also differ, even when the containing structs are still identical. To take an example, for struct sk_buff, debug messaging revealed that the identical struct matching was failing for the anon struct "headers"; specifically for the first field: __u8 __pkt_type_offset[0]; /* 128 0 */ Looking at the code in BTF deduplication, we have code that guards against the possibility of identical struct definitions, down to type ids, and identical array definitions. However in this case we have a struct which is being defined twice but does not have identical type ids since each duplicate struct has separate type ids for the above array member. A similar problem (though not observed) could occur for struct-in-struct. The solution is to make the "identical struct" test check members not just for matching ids, but to also check if they in turn are identical structs or arrays. The results of doing this are quite dramatic (for some modules at least); I see the number of type ids drop from around 10000 to just over 1000 in one module for example. For testing use latest pahole or apply [1], otherwise dedups can fail for the reasons described there. Also fix return type of btf_dedup_identical_arrays() as suggested by Andrii to match boolean return type used elsewhere. Fixes: efdd3eb8015e ("libbpf: Accommodate DWARF/compiler bug with duplicated structs") Signed-off-by: Alan Maguire Signed-off-by: Andrii Nakryiko Link: https://lore.kernel.org/bpf/1666622309-22289-1-git-send-email-alan.maguire@oracle.com [1] https://lore.kernel.org/bpf/1666364523-9648-1-git-send-email-alan.maguire --- tools/lib/bpf/btf.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/tools/lib/bpf/btf.c b/tools/lib/bpf/btf.c index d88647da2c7f..675a0df5c840 100644 --- a/tools/lib/bpf/btf.c +++ b/tools/lib/bpf/btf.c @@ -3887,14 +3887,14 @@ static inline __u16 btf_fwd_kind(struct btf_type *t) } /* Check if given two types are identical ARRAY definitions */ -static int btf_dedup_identical_arrays(struct btf_dedup *d, __u32 id1, __u32 id2) +static bool btf_dedup_identical_arrays(struct btf_dedup *d, __u32 id1, __u32 id2) { struct btf_type *t1, *t2; t1 = btf_type_by_id(d->btf, id1); t2 = btf_type_by_id(d->btf, id2); if (!btf_is_array(t1) || !btf_is_array(t2)) - return 0; + return false; return btf_equal_array(t1, t2); } @@ -3918,7 +3918,9 @@ static bool btf_dedup_identical_structs(struct btf_dedup *d, __u32 id1, __u32 id m1 = btf_members(t1); m2 = btf_members(t2); for (i = 0, n = btf_vlen(t1); i < n; i++, m1++, m2++) { - if (m1->type != m2->type) + if (m1->type != m2->type && + !btf_dedup_identical_arrays(d, m1->type, m2->type) && + !btf_dedup_identical_structs(d, m1->type, m2->type)) return false; } return true; From 022f19cf361bdbc72b51e684573c847c4f37478d Mon Sep 17 00:00:00 2001 From: caihuoqing Date: Mon, 24 Oct 2022 18:33:35 +0800 Subject: [PATCH 0357/2002] net: hinic: Set max_mtu/min_mtu directly to simplify the code. Set max_mtu/min_mtu directly to avoid making the validity judgment when set mtu, because the judgment is made in net/core: dev_validate_mtu, so to simplify the code. Signed-off-by: caihuoqing Link: https://lore.kernel.org/r/20221024103349.4494-1-cai.huoqing@linux.dev Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/huawei/hinic/hinic_dev.h | 4 ++++ drivers/net/ethernet/huawei/hinic/hinic_main.c | 3 ++- drivers/net/ethernet/huawei/hinic/hinic_port.c | 17 +---------------- 3 files changed, 7 insertions(+), 17 deletions(-) diff --git a/drivers/net/ethernet/huawei/hinic/hinic_dev.h b/drivers/net/ethernet/huawei/hinic/hinic_dev.h index a4fbf44f944c..52ea97c818b8 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_dev.h +++ b/drivers/net/ethernet/huawei/hinic/hinic_dev.h @@ -22,6 +22,10 @@ #define LP_PKT_CNT 64 +#define HINIC_MAX_JUMBO_FRAME_SIZE 15872 +#define HINIC_MAX_MTU_SIZE (HINIC_MAX_JUMBO_FRAME_SIZE - ETH_HLEN - ETH_FCS_LEN) +#define HINIC_MIN_MTU_SIZE 256 + enum hinic_flags { HINIC_LINK_UP = BIT(0), HINIC_INTF_UP = BIT(1), diff --git a/drivers/net/ethernet/huawei/hinic/hinic_main.c b/drivers/net/ethernet/huawei/hinic/hinic_main.c index e1f54a2f28b2..9d4d795e1081 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_main.c +++ b/drivers/net/ethernet/huawei/hinic/hinic_main.c @@ -1187,7 +1187,8 @@ static int nic_dev_init(struct pci_dev *pdev) else netdev->netdev_ops = &hinicvf_netdev_ops; - netdev->max_mtu = ETH_MAX_MTU; + netdev->max_mtu = HINIC_MAX_MTU_SIZE; + netdev->min_mtu = HINIC_MIN_MTU_SIZE; nic_dev = netdev_priv(netdev); nic_dev->netdev = netdev; diff --git a/drivers/net/ethernet/huawei/hinic/hinic_port.c b/drivers/net/ethernet/huawei/hinic/hinic_port.c index 28ae6f1201a8..0a39c3dffa9a 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_port.c +++ b/drivers/net/ethernet/huawei/hinic/hinic_port.c @@ -17,9 +17,6 @@ #include "hinic_port.h" #include "hinic_dev.h" -#define HINIC_MIN_MTU_SIZE 256 -#define HINIC_MAX_JUMBO_FRAME_SIZE 15872 - enum mac_op { MAC_DEL, MAC_SET, @@ -147,24 +144,12 @@ int hinic_port_get_mac(struct hinic_dev *nic_dev, u8 *addr) **/ int hinic_port_set_mtu(struct hinic_dev *nic_dev, int new_mtu) { - struct net_device *netdev = nic_dev->netdev; struct hinic_hwdev *hwdev = nic_dev->hwdev; struct hinic_port_mtu_cmd port_mtu_cmd; struct hinic_hwif *hwif = hwdev->hwif; u16 out_size = sizeof(port_mtu_cmd); struct pci_dev *pdev = hwif->pdev; - int err, max_frame; - - if (new_mtu < HINIC_MIN_MTU_SIZE) { - netif_err(nic_dev, drv, netdev, "mtu < MIN MTU size"); - return -EINVAL; - } - - max_frame = new_mtu + ETH_HLEN + ETH_FCS_LEN; - if (max_frame > HINIC_MAX_JUMBO_FRAME_SIZE) { - netif_err(nic_dev, drv, netdev, "mtu > MAX MTU size"); - return -EINVAL; - } + int err; port_mtu_cmd.func_idx = HINIC_HWIF_FUNC_IDX(hwif); port_mtu_cmd.mtu = new_mtu; From bb214ac47e0a6d58940cb89d3b036ba042387418 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Mon, 24 Oct 2022 13:59:51 +0100 Subject: [PATCH 0358/2002] bna: remove variable num_entries Variable num_entries is just being incremented and it's never used anywhere else. The variable and the increment are redundant so remove it. Signed-off-by: Colin Ian King Link: https://lore.kernel.org/r/20221024125951.2155434-1-colin.i.king@gmail.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/brocade/bna/bfa_msgq.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/net/ethernet/brocade/bna/bfa_msgq.c b/drivers/net/ethernet/brocade/bna/bfa_msgq.c index 47125f419530..fa40d5ec6f1c 100644 --- a/drivers/net/ethernet/brocade/bna/bfa_msgq.c +++ b/drivers/net/ethernet/brocade/bna/bfa_msgq.c @@ -202,7 +202,6 @@ static void __cmd_copy(struct bfa_msgq_cmdq *cmdq, struct bfa_msgq_cmd_entry *cmd) { size_t len = cmd->msg_size; - int num_entries = 0; size_t to_copy; u8 *src, *dst; @@ -219,7 +218,6 @@ __cmd_copy(struct bfa_msgq_cmdq *cmdq, struct bfa_msgq_cmd_entry *cmd) BFA_MSGQ_INDX_ADD(cmdq->producer_index, 1, cmdq->depth); dst = (u8 *)cmdq->addr.kva; dst += (cmdq->producer_index * BFI_MSGQ_CMD_ENTRY_SIZE); - num_entries++; } } From d0217284cea7d470e4140e98b806cb3cdf8257d6 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Mon, 24 Oct 2022 15:35:01 +0100 Subject: [PATCH 0359/2002] net: dl2k: remove variable tx_use Variable tx_use is just being incremented and it's never used anywhere else. The variable and the increment are redundant so remove it. Signed-off-by: Colin Ian King Link: https://lore.kernel.org/r/20221024143501.2163720-1-colin.i.king@gmail.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/dlink/dl2k.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/net/ethernet/dlink/dl2k.c b/drivers/net/ethernet/dlink/dl2k.c index 2c67a857a42f..db6615aa921b 100644 --- a/drivers/net/ethernet/dlink/dl2k.c +++ b/drivers/net/ethernet/dlink/dl2k.c @@ -814,7 +814,6 @@ rio_free_tx (struct net_device *dev, int irq) { struct netdev_private *np = netdev_priv(dev); int entry = np->old_tx % TX_RING_SIZE; - int tx_use = 0; unsigned long flag = 0; if (irq) @@ -839,7 +838,6 @@ rio_free_tx (struct net_device *dev, int irq) np->tx_skbuff[entry] = NULL; entry = (entry + 1) % TX_RING_SIZE; - tx_use++; } if (irq) spin_unlock(&np->tx_lock); From 271de525e1d7f564e88a9d212c50998b49a54476 Mon Sep 17 00:00:00 2001 From: Martin KaFai Lau Date: Tue, 25 Oct 2022 11:45:16 -0700 Subject: [PATCH 0360/2002] bpf: Remove prog->active check for bpf_lsm and bpf_iter The commit 64696c40d03c ("bpf: Add __bpf_prog_{enter,exit}_struct_ops for struct_ops trampoline") removed prog->active check for struct_ops prog. The bpf_lsm and bpf_iter is also using trampoline. Like struct_ops, the bpf_lsm and bpf_iter have fixed hooks for the prog to attach. The kernel does not call the same hook in a recursive way. This patch also removes the prog->active check for bpf_lsm and bpf_iter. A later patch has a test to reproduce the recursion issue for a sleepable bpf_lsm program. This patch appends the '_recur' naming to the existing enter and exit functions that track the prog->active counter. New __bpf_prog_{enter,exit}[_sleepable] function are added to skip the prog->active tracking. The '_struct_ops' version is also removed. It also moves the decision on picking the enter and exit function to the new bpf_trampoline_{enter,exit}(). It returns the '_recur' ones for all tracing progs to use. For bpf_lsm, bpf_iter, struct_ops (no prog->active tracking after 64696c40d03c), and bpf_lsm_cgroup (no prog->active tracking after 69fd337a975c7), it will return the functions that don't track the prog->active. Signed-off-by: Martin KaFai Lau Link: https://lore.kernel.org/r/20221025184524.3526117-2-martin.lau@linux.dev Signed-off-by: Alexei Starovoitov --- arch/arm64/net/bpf_jit_comp.c | 9 +--- arch/x86/net/bpf_jit_comp.c | 19 +-------- include/linux/bpf.h | 24 +++++------ include/linux/bpf_verifier.h | 15 ++++++- kernel/bpf/syscall.c | 5 ++- kernel/bpf/trampoline.c | 80 +++++++++++++++++++++++++++++------ 6 files changed, 98 insertions(+), 54 deletions(-) diff --git a/arch/arm64/net/bpf_jit_comp.c b/arch/arm64/net/bpf_jit_comp.c index 30f76178608b..62f805f427b7 100644 --- a/arch/arm64/net/bpf_jit_comp.c +++ b/arch/arm64/net/bpf_jit_comp.c @@ -1649,13 +1649,8 @@ static void invoke_bpf_prog(struct jit_ctx *ctx, struct bpf_tramp_link *l, struct bpf_prog *p = l->link.prog; int cookie_off = offsetof(struct bpf_tramp_run_ctx, bpf_cookie); - if (p->aux->sleepable) { - enter_prog = (u64)__bpf_prog_enter_sleepable; - exit_prog = (u64)__bpf_prog_exit_sleepable; - } else { - enter_prog = (u64)__bpf_prog_enter; - exit_prog = (u64)__bpf_prog_exit; - } + enter_prog = (u64)bpf_trampoline_enter(p); + exit_prog = (u64)bpf_trampoline_exit(p); if (l->cookie == 0) { /* if cookie is zero, one instruction is enough to store it */ diff --git a/arch/x86/net/bpf_jit_comp.c b/arch/x86/net/bpf_jit_comp.c index d7dd8e0db8da..36ffe67ad6e5 100644 --- a/arch/x86/net/bpf_jit_comp.c +++ b/arch/x86/net/bpf_jit_comp.c @@ -1894,10 +1894,6 @@ static int invoke_bpf_prog(const struct btf_func_model *m, u8 **pprog, struct bpf_tramp_link *l, int stack_size, int run_ctx_off, bool save_ret) { - void (*exit)(struct bpf_prog *prog, u64 start, - struct bpf_tramp_run_ctx *run_ctx) = __bpf_prog_exit; - u64 (*enter)(struct bpf_prog *prog, - struct bpf_tramp_run_ctx *run_ctx) = __bpf_prog_enter; u8 *prog = *pprog; u8 *jmp_insn; int ctx_cookie_off = offsetof(struct bpf_tramp_run_ctx, bpf_cookie); @@ -1916,23 +1912,12 @@ static int invoke_bpf_prog(const struct btf_func_model *m, u8 **pprog, */ emit_stx(&prog, BPF_DW, BPF_REG_FP, BPF_REG_1, -run_ctx_off + ctx_cookie_off); - if (p->aux->sleepable) { - enter = __bpf_prog_enter_sleepable; - exit = __bpf_prog_exit_sleepable; - } else if (p->type == BPF_PROG_TYPE_STRUCT_OPS) { - enter = __bpf_prog_enter_struct_ops; - exit = __bpf_prog_exit_struct_ops; - } else if (p->expected_attach_type == BPF_LSM_CGROUP) { - enter = __bpf_prog_enter_lsm_cgroup; - exit = __bpf_prog_exit_lsm_cgroup; - } - /* arg1: mov rdi, progs[i] */ emit_mov_imm64(&prog, BPF_REG_1, (long) p >> 32, (u32) (long) p); /* arg2: lea rsi, [rbp - ctx_cookie_off] */ EMIT4(0x48, 0x8D, 0x75, -run_ctx_off); - if (emit_call(&prog, enter, prog)) + if (emit_call(&prog, bpf_trampoline_enter(p), prog)) return -EINVAL; /* remember prog start time returned by __bpf_prog_enter */ emit_mov_reg(&prog, true, BPF_REG_6, BPF_REG_0); @@ -1977,7 +1962,7 @@ static int invoke_bpf_prog(const struct btf_func_model *m, u8 **pprog, emit_mov_reg(&prog, true, BPF_REG_2, BPF_REG_6); /* arg3: lea rdx, [rbp - run_ctx_off] */ EMIT4(0x48, 0x8D, 0x55, -run_ctx_off); - if (emit_call(&prog, exit, prog)) + if (emit_call(&prog, bpf_trampoline_exit(p), prog)) return -EINVAL; *pprog = prog; diff --git a/include/linux/bpf.h b/include/linux/bpf.h index 9e7d46d16032..1279e699dc98 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -854,22 +854,18 @@ int arch_prepare_bpf_trampoline(struct bpf_tramp_image *tr, void *image, void *i const struct btf_func_model *m, u32 flags, struct bpf_tramp_links *tlinks, void *orig_call); -/* these two functions are called from generated trampoline */ -u64 notrace __bpf_prog_enter(struct bpf_prog *prog, struct bpf_tramp_run_ctx *run_ctx); -void notrace __bpf_prog_exit(struct bpf_prog *prog, u64 start, struct bpf_tramp_run_ctx *run_ctx); -u64 notrace __bpf_prog_enter_sleepable(struct bpf_prog *prog, struct bpf_tramp_run_ctx *run_ctx); -void notrace __bpf_prog_exit_sleepable(struct bpf_prog *prog, u64 start, - struct bpf_tramp_run_ctx *run_ctx); -u64 notrace __bpf_prog_enter_lsm_cgroup(struct bpf_prog *prog, - struct bpf_tramp_run_ctx *run_ctx); -void notrace __bpf_prog_exit_lsm_cgroup(struct bpf_prog *prog, u64 start, - struct bpf_tramp_run_ctx *run_ctx); -u64 notrace __bpf_prog_enter_struct_ops(struct bpf_prog *prog, - struct bpf_tramp_run_ctx *run_ctx); -void notrace __bpf_prog_exit_struct_ops(struct bpf_prog *prog, u64 start, - struct bpf_tramp_run_ctx *run_ctx); +u64 notrace __bpf_prog_enter_sleepable_recur(struct bpf_prog *prog, + struct bpf_tramp_run_ctx *run_ctx); +void notrace __bpf_prog_exit_sleepable_recur(struct bpf_prog *prog, u64 start, + struct bpf_tramp_run_ctx *run_ctx); void notrace __bpf_tramp_enter(struct bpf_tramp_image *tr); void notrace __bpf_tramp_exit(struct bpf_tramp_image *tr); +typedef u64 (*bpf_trampoline_enter_t)(struct bpf_prog *prog, + struct bpf_tramp_run_ctx *run_ctx); +typedef void (*bpf_trampoline_exit_t)(struct bpf_prog *prog, u64 start, + struct bpf_tramp_run_ctx *run_ctx); +bpf_trampoline_enter_t bpf_trampoline_enter(const struct bpf_prog *prog); +bpf_trampoline_exit_t bpf_trampoline_exit(const struct bpf_prog *prog); struct bpf_ksym { unsigned long start; diff --git a/include/linux/bpf_verifier.h b/include/linux/bpf_verifier.h index 9e1e6965f407..1a32baa78ce2 100644 --- a/include/linux/bpf_verifier.h +++ b/include/linux/bpf_verifier.h @@ -642,10 +642,23 @@ static inline u32 type_flag(u32 type) } /* only use after check_attach_btf_id() */ -static inline enum bpf_prog_type resolve_prog_type(struct bpf_prog *prog) +static inline enum bpf_prog_type resolve_prog_type(const struct bpf_prog *prog) { return prog->type == BPF_PROG_TYPE_EXT ? prog->aux->dst_prog->type : prog->type; } +static inline bool bpf_prog_check_recur(const struct bpf_prog *prog) +{ + switch (resolve_prog_type(prog)) { + case BPF_PROG_TYPE_TRACING: + return prog->expected_attach_type != BPF_TRACE_ITER; + case BPF_PROG_TYPE_STRUCT_OPS: + case BPF_PROG_TYPE_LSM: + return false; + default: + return true; + } +} + #endif /* _LINUX_BPF_VERIFIER_H */ diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c index 7b373a5e861f..a0b4266196a8 100644 --- a/kernel/bpf/syscall.c +++ b/kernel/bpf/syscall.c @@ -5133,13 +5133,14 @@ int kern_sys_bpf(int cmd, union bpf_attr *attr, unsigned int size) run_ctx.bpf_cookie = 0; run_ctx.saved_run_ctx = NULL; - if (!__bpf_prog_enter_sleepable(prog, &run_ctx)) { + if (!__bpf_prog_enter_sleepable_recur(prog, &run_ctx)) { /* recursion detected */ bpf_prog_put(prog); return -EBUSY; } attr->test.retval = bpf_prog_run(prog, (void *) (long) attr->test.ctx_in); - __bpf_prog_exit_sleepable(prog, 0 /* bpf_prog_run does runtime stats */, &run_ctx); + __bpf_prog_exit_sleepable_recur(prog, 0 /* bpf_prog_run does runtime stats */, + &run_ctx); bpf_prog_put(prog); return 0; #endif diff --git a/kernel/bpf/trampoline.c b/kernel/bpf/trampoline.c index bf0906e1e2b9..d6395215b849 100644 --- a/kernel/bpf/trampoline.c +++ b/kernel/bpf/trampoline.c @@ -864,7 +864,7 @@ static __always_inline u64 notrace bpf_prog_start_time(void) * [2..MAX_U64] - execute bpf prog and record execution time. * This is start time. */ -u64 notrace __bpf_prog_enter(struct bpf_prog *prog, struct bpf_tramp_run_ctx *run_ctx) +static u64 notrace __bpf_prog_enter_recur(struct bpf_prog *prog, struct bpf_tramp_run_ctx *run_ctx) __acquires(RCU) { rcu_read_lock(); @@ -901,7 +901,8 @@ static void notrace update_prog_stats(struct bpf_prog *prog, } } -void notrace __bpf_prog_exit(struct bpf_prog *prog, u64 start, struct bpf_tramp_run_ctx *run_ctx) +static void notrace __bpf_prog_exit_recur(struct bpf_prog *prog, u64 start, + struct bpf_tramp_run_ctx *run_ctx) __releases(RCU) { bpf_reset_run_ctx(run_ctx->saved_run_ctx); @@ -912,8 +913,8 @@ void notrace __bpf_prog_exit(struct bpf_prog *prog, u64 start, struct bpf_tramp_ rcu_read_unlock(); } -u64 notrace __bpf_prog_enter_lsm_cgroup(struct bpf_prog *prog, - struct bpf_tramp_run_ctx *run_ctx) +static u64 notrace __bpf_prog_enter_lsm_cgroup(struct bpf_prog *prog, + struct bpf_tramp_run_ctx *run_ctx) __acquires(RCU) { /* Runtime stats are exported via actual BPF_LSM_CGROUP @@ -927,8 +928,8 @@ u64 notrace __bpf_prog_enter_lsm_cgroup(struct bpf_prog *prog, return NO_START_TIME; } -void notrace __bpf_prog_exit_lsm_cgroup(struct bpf_prog *prog, u64 start, - struct bpf_tramp_run_ctx *run_ctx) +static void notrace __bpf_prog_exit_lsm_cgroup(struct bpf_prog *prog, u64 start, + struct bpf_tramp_run_ctx *run_ctx) __releases(RCU) { bpf_reset_run_ctx(run_ctx->saved_run_ctx); @@ -937,7 +938,8 @@ void notrace __bpf_prog_exit_lsm_cgroup(struct bpf_prog *prog, u64 start, rcu_read_unlock(); } -u64 notrace __bpf_prog_enter_sleepable(struct bpf_prog *prog, struct bpf_tramp_run_ctx *run_ctx) +u64 notrace __bpf_prog_enter_sleepable_recur(struct bpf_prog *prog, + struct bpf_tramp_run_ctx *run_ctx) { rcu_read_lock_trace(); migrate_disable(); @@ -953,8 +955,8 @@ u64 notrace __bpf_prog_enter_sleepable(struct bpf_prog *prog, struct bpf_tramp_r return bpf_prog_start_time(); } -void notrace __bpf_prog_exit_sleepable(struct bpf_prog *prog, u64 start, - struct bpf_tramp_run_ctx *run_ctx) +void notrace __bpf_prog_exit_sleepable_recur(struct bpf_prog *prog, u64 start, + struct bpf_tramp_run_ctx *run_ctx) { bpf_reset_run_ctx(run_ctx->saved_run_ctx); @@ -964,8 +966,30 @@ void notrace __bpf_prog_exit_sleepable(struct bpf_prog *prog, u64 start, rcu_read_unlock_trace(); } -u64 notrace __bpf_prog_enter_struct_ops(struct bpf_prog *prog, - struct bpf_tramp_run_ctx *run_ctx) +static u64 notrace __bpf_prog_enter_sleepable(struct bpf_prog *prog, + struct bpf_tramp_run_ctx *run_ctx) +{ + rcu_read_lock_trace(); + migrate_disable(); + might_fault(); + + run_ctx->saved_run_ctx = bpf_set_run_ctx(&run_ctx->run_ctx); + + return bpf_prog_start_time(); +} + +static void notrace __bpf_prog_exit_sleepable(struct bpf_prog *prog, u64 start, + struct bpf_tramp_run_ctx *run_ctx) +{ + bpf_reset_run_ctx(run_ctx->saved_run_ctx); + + update_prog_stats(prog, start); + migrate_enable(); + rcu_read_unlock_trace(); +} + +static u64 notrace __bpf_prog_enter(struct bpf_prog *prog, + struct bpf_tramp_run_ctx *run_ctx) __acquires(RCU) { rcu_read_lock(); @@ -976,8 +1000,8 @@ u64 notrace __bpf_prog_enter_struct_ops(struct bpf_prog *prog, return bpf_prog_start_time(); } -void notrace __bpf_prog_exit_struct_ops(struct bpf_prog *prog, u64 start, - struct bpf_tramp_run_ctx *run_ctx) +static void notrace __bpf_prog_exit(struct bpf_prog *prog, u64 start, + struct bpf_tramp_run_ctx *run_ctx) __releases(RCU) { bpf_reset_run_ctx(run_ctx->saved_run_ctx); @@ -997,6 +1021,36 @@ void notrace __bpf_tramp_exit(struct bpf_tramp_image *tr) percpu_ref_put(&tr->pcref); } +bpf_trampoline_enter_t bpf_trampoline_enter(const struct bpf_prog *prog) +{ + bool sleepable = prog->aux->sleepable; + + if (bpf_prog_check_recur(prog)) + return sleepable ? __bpf_prog_enter_sleepable_recur : + __bpf_prog_enter_recur; + + if (resolve_prog_type(prog) == BPF_PROG_TYPE_LSM && + prog->expected_attach_type == BPF_LSM_CGROUP) + return __bpf_prog_enter_lsm_cgroup; + + return sleepable ? __bpf_prog_enter_sleepable : __bpf_prog_enter; +} + +bpf_trampoline_exit_t bpf_trampoline_exit(const struct bpf_prog *prog) +{ + bool sleepable = prog->aux->sleepable; + + if (bpf_prog_check_recur(prog)) + return sleepable ? __bpf_prog_exit_sleepable_recur : + __bpf_prog_exit_recur; + + if (resolve_prog_type(prog) == BPF_PROG_TYPE_LSM && + prog->expected_attach_type == BPF_LSM_CGROUP) + return __bpf_prog_exit_lsm_cgroup; + + return sleepable ? __bpf_prog_exit_sleepable : __bpf_prog_exit; +} + int __weak arch_prepare_bpf_trampoline(struct bpf_tramp_image *tr, void *image, void *image_end, const struct btf_func_model *m, u32 flags, From 0593dd34e53489557569d5e6d27371b49aa9b41f Mon Sep 17 00:00:00 2001 From: Martin KaFai Lau Date: Tue, 25 Oct 2022 11:45:17 -0700 Subject: [PATCH 0361/2002] bpf: Append _recur naming to the bpf_task_storage helper proto This patch adds the "_recur" naming to the bpf_task_storage_{get,delete} proto. In a latter patch, they will only be used by the tracing programs that requires a deadlock detection because a tracing prog may use bpf_task_storage_{get,delete} recursively and cause a deadlock. Another following patch will add a different helper proto for the non tracing programs because they do not need the deadlock prevention. This patch does this rename to prepare for this future proto additions. Signed-off-by: Martin KaFai Lau Link: https://lore.kernel.org/r/20221025184524.3526117-3-martin.lau@linux.dev Signed-off-by: Alexei Starovoitov --- include/linux/bpf.h | 4 ++-- kernel/bpf/bpf_task_storage.c | 12 ++++++------ kernel/trace/bpf_trace.c | 4 ++-- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/include/linux/bpf.h b/include/linux/bpf.h index 1279e699dc98..b04fe3f4342e 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -2519,8 +2519,8 @@ extern const struct bpf_func_proto bpf_this_cpu_ptr_proto; extern const struct bpf_func_proto bpf_ktime_get_coarse_ns_proto; extern const struct bpf_func_proto bpf_sock_from_file_proto; extern const struct bpf_func_proto bpf_get_socket_ptr_cookie_proto; -extern const struct bpf_func_proto bpf_task_storage_get_proto; -extern const struct bpf_func_proto bpf_task_storage_delete_proto; +extern const struct bpf_func_proto bpf_task_storage_get_recur_proto; +extern const struct bpf_func_proto bpf_task_storage_delete_recur_proto; extern const struct bpf_func_proto bpf_for_each_map_elem_proto; extern const struct bpf_func_proto bpf_btf_find_by_name_kind_proto; extern const struct bpf_func_proto bpf_sk_setsockopt_proto; diff --git a/kernel/bpf/bpf_task_storage.c b/kernel/bpf/bpf_task_storage.c index 6f290623347e..bce50ae03f42 100644 --- a/kernel/bpf/bpf_task_storage.c +++ b/kernel/bpf/bpf_task_storage.c @@ -228,7 +228,7 @@ out: } /* *gfp_flags* is a hidden argument provided by the verifier */ -BPF_CALL_5(bpf_task_storage_get, struct bpf_map *, map, struct task_struct *, +BPF_CALL_5(bpf_task_storage_get_recur, struct bpf_map *, map, struct task_struct *, task, void *, value, u64, flags, gfp_t, gfp_flags) { struct bpf_local_storage_data *sdata; @@ -260,7 +260,7 @@ unlock: (unsigned long)sdata->data; } -BPF_CALL_2(bpf_task_storage_delete, struct bpf_map *, map, struct task_struct *, +BPF_CALL_2(bpf_task_storage_delete_recur, struct bpf_map *, map, struct task_struct *, task) { int ret; @@ -322,8 +322,8 @@ const struct bpf_map_ops task_storage_map_ops = { .map_owner_storage_ptr = task_storage_ptr, }; -const struct bpf_func_proto bpf_task_storage_get_proto = { - .func = bpf_task_storage_get, +const struct bpf_func_proto bpf_task_storage_get_recur_proto = { + .func = bpf_task_storage_get_recur, .gpl_only = false, .ret_type = RET_PTR_TO_MAP_VALUE_OR_NULL, .arg1_type = ARG_CONST_MAP_PTR, @@ -333,8 +333,8 @@ const struct bpf_func_proto bpf_task_storage_get_proto = { .arg4_type = ARG_ANYTHING, }; -const struct bpf_func_proto bpf_task_storage_delete_proto = { - .func = bpf_task_storage_delete, +const struct bpf_func_proto bpf_task_storage_delete_recur_proto = { + .func = bpf_task_storage_delete_recur, .gpl_only = false, .ret_type = RET_INTEGER, .arg1_type = ARG_CONST_MAP_PTR, diff --git a/kernel/trace/bpf_trace.c b/kernel/trace/bpf_trace.c index 2460e38f75ff..83b9b9afe235 100644 --- a/kernel/trace/bpf_trace.c +++ b/kernel/trace/bpf_trace.c @@ -1488,9 +1488,9 @@ bpf_tracing_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog) case BPF_FUNC_this_cpu_ptr: return &bpf_this_cpu_ptr_proto; case BPF_FUNC_task_storage_get: - return &bpf_task_storage_get_proto; + return &bpf_task_storage_get_recur_proto; case BPF_FUNC_task_storage_delete: - return &bpf_task_storage_delete_proto; + return &bpf_task_storage_delete_recur_proto; case BPF_FUNC_for_each_map_elem: return &bpf_for_each_map_elem_proto; case BPF_FUNC_snprintf: From 6d65500c34d897329ed1be0fd3c4014ec52cd473 Mon Sep 17 00:00:00 2001 From: Martin KaFai Lau Date: Tue, 25 Oct 2022 11:45:18 -0700 Subject: [PATCH 0362/2002] bpf: Refactor the core bpf_task_storage_get logic into a new function This patch creates a new function __bpf_task_storage_get() and moves the core logic of the existing bpf_task_storage_get() into this new function. This new function will be shared by another new helper proto in the latter patch. Signed-off-by: Martin KaFai Lau Link: https://lore.kernel.org/r/20221025184524.3526117-4-martin.lau@linux.dev Signed-off-by: Alexei Starovoitov --- kernel/bpf/bpf_task_storage.c | 48 ++++++++++++++++++++--------------- 1 file changed, 28 insertions(+), 20 deletions(-) diff --git a/kernel/bpf/bpf_task_storage.c b/kernel/bpf/bpf_task_storage.c index bce50ae03f42..2726435e3eda 100644 --- a/kernel/bpf/bpf_task_storage.c +++ b/kernel/bpf/bpf_task_storage.c @@ -227,37 +227,45 @@ out: return err; } +/* Called by bpf_task_storage_get*() helpers */ +static void *__bpf_task_storage_get(struct bpf_map *map, + struct task_struct *task, void *value, + u64 flags, gfp_t gfp_flags) +{ + struct bpf_local_storage_data *sdata; + + sdata = task_storage_lookup(task, map, true); + if (sdata) + return sdata->data; + + /* only allocate new storage, when the task is refcounted */ + if (refcount_read(&task->usage) && + (flags & BPF_LOCAL_STORAGE_GET_F_CREATE)) { + sdata = bpf_local_storage_update( + task, (struct bpf_local_storage_map *)map, value, + BPF_NOEXIST, gfp_flags); + return IS_ERR(sdata) ? NULL : sdata->data; + } + + return NULL; +} + /* *gfp_flags* is a hidden argument provided by the verifier */ BPF_CALL_5(bpf_task_storage_get_recur, struct bpf_map *, map, struct task_struct *, task, void *, value, u64, flags, gfp_t, gfp_flags) { - struct bpf_local_storage_data *sdata; + void *data; WARN_ON_ONCE(!bpf_rcu_lock_held()); - if (flags & ~(BPF_LOCAL_STORAGE_GET_F_CREATE)) - return (unsigned long)NULL; - - if (!task) + if (flags & ~BPF_LOCAL_STORAGE_GET_F_CREATE || !task) return (unsigned long)NULL; if (!bpf_task_storage_trylock()) return (unsigned long)NULL; - - sdata = task_storage_lookup(task, map, true); - if (sdata) - goto unlock; - - /* only allocate new storage, when the task is refcounted */ - if (refcount_read(&task->usage) && - (flags & BPF_LOCAL_STORAGE_GET_F_CREATE)) - sdata = bpf_local_storage_update( - task, (struct bpf_local_storage_map *)map, value, - BPF_NOEXIST, gfp_flags); - -unlock: + data = __bpf_task_storage_get(map, task, value, flags, + gfp_flags); bpf_task_storage_unlock(); - return IS_ERR_OR_NULL(sdata) ? (unsigned long)NULL : - (unsigned long)sdata->data; + return (unsigned long)data; } BPF_CALL_2(bpf_task_storage_delete_recur, struct bpf_map *, map, struct task_struct *, From e8b02296a6b8d07de752d6157d863a642117bcd3 Mon Sep 17 00:00:00 2001 From: Martin KaFai Lau Date: Tue, 25 Oct 2022 11:45:19 -0700 Subject: [PATCH 0363/2002] bpf: Avoid taking spinlock in bpf_task_storage_get if potential deadlock is detected bpf_task_storage_get() does a lookup and optionally inserts new data if BPF_LOCAL_STORAGE_GET_F_CREATE is present. During lookup, it will cache the lookup result and caching requires to acquire a spinlock. When potential deadlock is detected (by the bpf_task_storage_busy pcpu-counter added in commit bc235cdb423a ("bpf: Prevent deadlock from recursive bpf_task_storage_[get|delete]")), the current behavior is returning NULL immediately to avoid deadlock. It is too pessimistic. This patch will go ahead to do a lookup (which is a lockless operation) but it will avoid caching it in order to avoid acquiring the spinlock. When lookup fails to find the data and BPF_LOCAL_STORAGE_GET_F_CREATE is set, an insertion is needed and this requires acquiring a spinlock. This patch will still return NULL when a potential deadlock is detected. Signed-off-by: Martin KaFai Lau Link: https://lore.kernel.org/r/20221025184524.3526117-5-martin.lau@linux.dev Signed-off-by: Alexei Starovoitov --- kernel/bpf/bpf_local_storage.c | 1 + kernel/bpf/bpf_task_storage.c | 15 ++++++++------- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/kernel/bpf/bpf_local_storage.c b/kernel/bpf/bpf_local_storage.c index 9dc6de1cf185..781d14167140 100644 --- a/kernel/bpf/bpf_local_storage.c +++ b/kernel/bpf/bpf_local_storage.c @@ -242,6 +242,7 @@ void bpf_selem_unlink(struct bpf_local_storage_elem *selem, bool use_trace_rcu) __bpf_selem_unlink_storage(selem, use_trace_rcu); } +/* If cacheit_lockit is false, this lookup function is lockless */ struct bpf_local_storage_data * bpf_local_storage_lookup(struct bpf_local_storage *local_storage, struct bpf_local_storage_map *smap, diff --git a/kernel/bpf/bpf_task_storage.c b/kernel/bpf/bpf_task_storage.c index 2726435e3eda..bc52bc8b59f7 100644 --- a/kernel/bpf/bpf_task_storage.c +++ b/kernel/bpf/bpf_task_storage.c @@ -230,17 +230,17 @@ out: /* Called by bpf_task_storage_get*() helpers */ static void *__bpf_task_storage_get(struct bpf_map *map, struct task_struct *task, void *value, - u64 flags, gfp_t gfp_flags) + u64 flags, gfp_t gfp_flags, bool nobusy) { struct bpf_local_storage_data *sdata; - sdata = task_storage_lookup(task, map, true); + sdata = task_storage_lookup(task, map, nobusy); if (sdata) return sdata->data; /* only allocate new storage, when the task is refcounted */ if (refcount_read(&task->usage) && - (flags & BPF_LOCAL_STORAGE_GET_F_CREATE)) { + (flags & BPF_LOCAL_STORAGE_GET_F_CREATE) && nobusy) { sdata = bpf_local_storage_update( task, (struct bpf_local_storage_map *)map, value, BPF_NOEXIST, gfp_flags); @@ -254,17 +254,18 @@ static void *__bpf_task_storage_get(struct bpf_map *map, BPF_CALL_5(bpf_task_storage_get_recur, struct bpf_map *, map, struct task_struct *, task, void *, value, u64, flags, gfp_t, gfp_flags) { + bool nobusy; void *data; WARN_ON_ONCE(!bpf_rcu_lock_held()); if (flags & ~BPF_LOCAL_STORAGE_GET_F_CREATE || !task) return (unsigned long)NULL; - if (!bpf_task_storage_trylock()) - return (unsigned long)NULL; + nobusy = bpf_task_storage_trylock(); data = __bpf_task_storage_get(map, task, value, flags, - gfp_flags); - bpf_task_storage_unlock(); + gfp_flags, nobusy); + if (nobusy) + bpf_task_storage_unlock(); return (unsigned long)data; } From 4279adb094a17132423f1271c3d11b593fc2327e Mon Sep 17 00:00:00 2001 From: Martin KaFai Lau Date: Tue, 25 Oct 2022 11:45:20 -0700 Subject: [PATCH 0364/2002] bpf: Add new bpf_task_storage_get proto with no deadlock detection The bpf_lsm and bpf_iter do not recur that will cause a deadlock. The situation is similar to the bpf_pid_task_storage_lookup_elem() which is called from the syscall map_lookup_elem. It does not need deadlock detection. Otherwise, it will cause unnecessary failure when calling the bpf_task_storage_get() helper. This patch adds bpf_task_storage_get proto that does not do deadlock detection. It will be used by bpf_lsm and bpf_iter programs. Signed-off-by: Martin KaFai Lau Link: https://lore.kernel.org/r/20221025184524.3526117-6-martin.lau@linux.dev Signed-off-by: Alexei Starovoitov --- include/linux/bpf.h | 1 + kernel/bpf/bpf_task_storage.c | 28 ++++++++++++++++++++++++++++ kernel/trace/bpf_trace.c | 5 ++++- 3 files changed, 33 insertions(+), 1 deletion(-) diff --git a/include/linux/bpf.h b/include/linux/bpf.h index b04fe3f4342e..ef3f98afa45d 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -2520,6 +2520,7 @@ extern const struct bpf_func_proto bpf_ktime_get_coarse_ns_proto; extern const struct bpf_func_proto bpf_sock_from_file_proto; extern const struct bpf_func_proto bpf_get_socket_ptr_cookie_proto; extern const struct bpf_func_proto bpf_task_storage_get_recur_proto; +extern const struct bpf_func_proto bpf_task_storage_get_proto; extern const struct bpf_func_proto bpf_task_storage_delete_recur_proto; extern const struct bpf_func_proto bpf_for_each_map_elem_proto; extern const struct bpf_func_proto bpf_btf_find_by_name_kind_proto; diff --git a/kernel/bpf/bpf_task_storage.c b/kernel/bpf/bpf_task_storage.c index bc52bc8b59f7..c3a841be438f 100644 --- a/kernel/bpf/bpf_task_storage.c +++ b/kernel/bpf/bpf_task_storage.c @@ -269,6 +269,23 @@ BPF_CALL_5(bpf_task_storage_get_recur, struct bpf_map *, map, struct task_struct return (unsigned long)data; } +/* *gfp_flags* is a hidden argument provided by the verifier */ +BPF_CALL_5(bpf_task_storage_get, struct bpf_map *, map, struct task_struct *, + task, void *, value, u64, flags, gfp_t, gfp_flags) +{ + void *data; + + WARN_ON_ONCE(!bpf_rcu_lock_held()); + if (flags & ~BPF_LOCAL_STORAGE_GET_F_CREATE || !task) + return (unsigned long)NULL; + + bpf_task_storage_lock(); + data = __bpf_task_storage_get(map, task, value, flags, + gfp_flags, true); + bpf_task_storage_unlock(); + return (unsigned long)data; +} + BPF_CALL_2(bpf_task_storage_delete_recur, struct bpf_map *, map, struct task_struct *, task) { @@ -342,6 +359,17 @@ const struct bpf_func_proto bpf_task_storage_get_recur_proto = { .arg4_type = ARG_ANYTHING, }; +const struct bpf_func_proto bpf_task_storage_get_proto = { + .func = bpf_task_storage_get, + .gpl_only = false, + .ret_type = RET_PTR_TO_MAP_VALUE_OR_NULL, + .arg1_type = ARG_CONST_MAP_PTR, + .arg2_type = ARG_PTR_TO_BTF_ID, + .arg2_btf_id = &btf_tracing_ids[BTF_TRACING_TYPE_TASK], + .arg3_type = ARG_PTR_TO_MAP_VALUE_OR_NULL, + .arg4_type = ARG_ANYTHING, +}; + const struct bpf_func_proto bpf_task_storage_delete_recur_proto = { .func = bpf_task_storage_delete_recur, .gpl_only = false, diff --git a/kernel/trace/bpf_trace.c b/kernel/trace/bpf_trace.c index 83b9b9afe235..e9759b0f7199 100644 --- a/kernel/trace/bpf_trace.c +++ b/kernel/trace/bpf_trace.c @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -1488,7 +1489,9 @@ bpf_tracing_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog) case BPF_FUNC_this_cpu_ptr: return &bpf_this_cpu_ptr_proto; case BPF_FUNC_task_storage_get: - return &bpf_task_storage_get_recur_proto; + if (bpf_prog_check_recur(prog)) + return &bpf_task_storage_get_recur_proto; + return &bpf_task_storage_get_proto; case BPF_FUNC_task_storage_delete: return &bpf_task_storage_delete_recur_proto; case BPF_FUNC_for_each_map_elem: From fda64ae0bb3e37b5a4292625c6931cb156224d0f Mon Sep 17 00:00:00 2001 From: Martin KaFai Lau Date: Tue, 25 Oct 2022 11:45:21 -0700 Subject: [PATCH 0365/2002] bpf: bpf_task_storage_delete_recur does lookup first before the deadlock check Similar to the earlier change in bpf_task_storage_get_recur. This patch changes bpf_task_storage_delete_recur such that it does the lookup first. It only returns -EBUSY if it needs to take the spinlock to do the deletion when potential deadlock is detected. Signed-off-by: Martin KaFai Lau Link: https://lore.kernel.org/r/20221025184524.3526117-7-martin.lau@linux.dev Signed-off-by: Alexei Starovoitov --- kernel/bpf/bpf_task_storage.c | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/kernel/bpf/bpf_task_storage.c b/kernel/bpf/bpf_task_storage.c index c3a841be438f..f3f79b618a68 100644 --- a/kernel/bpf/bpf_task_storage.c +++ b/kernel/bpf/bpf_task_storage.c @@ -184,7 +184,8 @@ out: return err; } -static int task_storage_delete(struct task_struct *task, struct bpf_map *map) +static int task_storage_delete(struct task_struct *task, struct bpf_map *map, + bool nobusy) { struct bpf_local_storage_data *sdata; @@ -192,6 +193,9 @@ static int task_storage_delete(struct task_struct *task, struct bpf_map *map) if (!sdata) return -ENOENT; + if (!nobusy) + return -EBUSY; + bpf_selem_unlink(SELEM(sdata), true); return 0; @@ -220,7 +224,7 @@ static int bpf_pid_task_storage_delete_elem(struct bpf_map *map, void *key) } bpf_task_storage_lock(); - err = task_storage_delete(task, map); + err = task_storage_delete(task, map, true); bpf_task_storage_unlock(); out: put_pid(pid); @@ -289,21 +293,21 @@ BPF_CALL_5(bpf_task_storage_get, struct bpf_map *, map, struct task_struct *, BPF_CALL_2(bpf_task_storage_delete_recur, struct bpf_map *, map, struct task_struct *, task) { + bool nobusy; int ret; WARN_ON_ONCE(!bpf_rcu_lock_held()); if (!task) return -EINVAL; - if (!bpf_task_storage_trylock()) - return -EBUSY; - + nobusy = bpf_task_storage_trylock(); /* This helper must only be called from places where the lifetime of the task * is guaranteed. Either by being refcounted or by being protected * by an RCU read-side critical section. */ - ret = task_storage_delete(task, map); - bpf_task_storage_unlock(); + ret = task_storage_delete(task, map, nobusy); + if (nobusy) + bpf_task_storage_unlock(); return ret; } From 8a7dac37f27a3dfbd814bf29a73d6417db2c81d9 Mon Sep 17 00:00:00 2001 From: Martin KaFai Lau Date: Tue, 25 Oct 2022 11:45:22 -0700 Subject: [PATCH 0366/2002] bpf: Add new bpf_task_storage_delete proto with no deadlock detection The bpf_lsm and bpf_iter do not recur that will cause a deadlock. The situation is similar to the bpf_pid_task_storage_delete_elem() which is called from the syscall map_delete_elem. It does not need deadlock detection. Otherwise, it will cause unnecessary failure when calling the bpf_task_storage_delete() helper. This patch adds bpf_task_storage_delete proto that does not do deadlock detection. It will be used by bpf_lsm and bpf_iter program. Signed-off-by: Martin KaFai Lau Link: https://lore.kernel.org/r/20221025184524.3526117-8-martin.lau@linux.dev Signed-off-by: Alexei Starovoitov --- include/linux/bpf.h | 1 + kernel/bpf/bpf_task_storage.c | 28 ++++++++++++++++++++++++++++ kernel/trace/bpf_trace.c | 4 +++- 3 files changed, 32 insertions(+), 1 deletion(-) diff --git a/include/linux/bpf.h b/include/linux/bpf.h index ef3f98afa45d..a5dbac8f5aba 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -2522,6 +2522,7 @@ extern const struct bpf_func_proto bpf_get_socket_ptr_cookie_proto; extern const struct bpf_func_proto bpf_task_storage_get_recur_proto; extern const struct bpf_func_proto bpf_task_storage_get_proto; extern const struct bpf_func_proto bpf_task_storage_delete_recur_proto; +extern const struct bpf_func_proto bpf_task_storage_delete_proto; extern const struct bpf_func_proto bpf_for_each_map_elem_proto; extern const struct bpf_func_proto bpf_btf_find_by_name_kind_proto; extern const struct bpf_func_proto bpf_sk_setsockopt_proto; diff --git a/kernel/bpf/bpf_task_storage.c b/kernel/bpf/bpf_task_storage.c index f3f79b618a68..ba3fe72d1fa5 100644 --- a/kernel/bpf/bpf_task_storage.c +++ b/kernel/bpf/bpf_task_storage.c @@ -311,6 +311,25 @@ BPF_CALL_2(bpf_task_storage_delete_recur, struct bpf_map *, map, struct task_str return ret; } +BPF_CALL_2(bpf_task_storage_delete, struct bpf_map *, map, struct task_struct *, + task) +{ + int ret; + + WARN_ON_ONCE(!bpf_rcu_lock_held()); + if (!task) + return -EINVAL; + + bpf_task_storage_lock(); + /* This helper must only be called from places where the lifetime of the task + * is guaranteed. Either by being refcounted or by being protected + * by an RCU read-side critical section. + */ + ret = task_storage_delete(task, map, true); + bpf_task_storage_unlock(); + return ret; +} + static int notsupp_get_next_key(struct bpf_map *map, void *key, void *next_key) { return -ENOTSUPP; @@ -382,3 +401,12 @@ const struct bpf_func_proto bpf_task_storage_delete_recur_proto = { .arg2_type = ARG_PTR_TO_BTF_ID, .arg2_btf_id = &btf_tracing_ids[BTF_TRACING_TYPE_TASK], }; + +const struct bpf_func_proto bpf_task_storage_delete_proto = { + .func = bpf_task_storage_delete, + .gpl_only = false, + .ret_type = RET_INTEGER, + .arg1_type = ARG_CONST_MAP_PTR, + .arg2_type = ARG_PTR_TO_BTF_ID, + .arg2_btf_id = &btf_tracing_ids[BTF_TRACING_TYPE_TASK], +}; diff --git a/kernel/trace/bpf_trace.c b/kernel/trace/bpf_trace.c index e9759b0f7199..eed1bd952c3a 100644 --- a/kernel/trace/bpf_trace.c +++ b/kernel/trace/bpf_trace.c @@ -1493,7 +1493,9 @@ bpf_tracing_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog) return &bpf_task_storage_get_recur_proto; return &bpf_task_storage_get_proto; case BPF_FUNC_task_storage_delete: - return &bpf_task_storage_delete_recur_proto; + if (bpf_prog_check_recur(prog)) + return &bpf_task_storage_delete_recur_proto; + return &bpf_task_storage_delete_proto; case BPF_FUNC_for_each_map_elem: return &bpf_for_each_map_elem_proto; case BPF_FUNC_snprintf: From 0334b4d8822a22b3593aec7361c50e9ebc31ee88 Mon Sep 17 00:00:00 2001 From: Martin KaFai Lau Date: Tue, 25 Oct 2022 11:45:23 -0700 Subject: [PATCH 0367/2002] selftests/bpf: Ensure no task storage failure for bpf_lsm.s prog due to deadlock detection This patch adds a test to check for deadlock failure in bpf_task_storage_{get,delete} when called by a sleepable bpf_lsm prog. It also checks if the prog_info.recursion_misses is non zero. The test starts with 32 threads and they are affinitized to one cpu. In my qemu setup, with CONFIG_PREEMPT=y, I can reproduce it within one second if it is run without the previous patches of this set. Here is the test error message before adding the no deadlock detection version of the bpf_task_storage_{get,delete}: test_nodeadlock:FAIL:bpf_task_storage_get busy unexpected bpf_task_storage_get busy: actual 2 != expected 0 test_nodeadlock:FAIL:bpf_task_storage_delete busy unexpected bpf_task_storage_delete busy: actual 2 != expected 0 Signed-off-by: Martin KaFai Lau Link: https://lore.kernel.org/r/20221025184524.3526117-9-martin.lau@linux.dev Signed-off-by: Alexei Starovoitov --- .../bpf/prog_tests/task_local_storage.c | 98 +++++++++++++++++++ .../bpf/progs/task_storage_nodeadlock.c | 47 +++++++++ 2 files changed, 145 insertions(+) create mode 100644 tools/testing/selftests/bpf/progs/task_storage_nodeadlock.c diff --git a/tools/testing/selftests/bpf/prog_tests/task_local_storage.c b/tools/testing/selftests/bpf/prog_tests/task_local_storage.c index 99a42a2b6e14..ae535f5de6a2 100644 --- a/tools/testing/selftests/bpf/prog_tests/task_local_storage.c +++ b/tools/testing/selftests/bpf/prog_tests/task_local_storage.c @@ -3,12 +3,15 @@ #define _GNU_SOURCE /* See feature_test_macros(7) */ #include +#include +#include #include /* For SYS_xxx definitions */ #include #include #include "task_local_storage.skel.h" #include "task_local_storage_exit_creds.skel.h" #include "task_ls_recursion.skel.h" +#include "task_storage_nodeadlock.skel.h" static void test_sys_enter_exit(void) { @@ -93,6 +96,99 @@ out: task_ls_recursion__destroy(skel); } +static bool stop; + +static void waitall(const pthread_t *tids, int nr) +{ + int i; + + stop = true; + for (i = 0; i < nr; i++) + pthread_join(tids[i], NULL); +} + +static void *sock_create_loop(void *arg) +{ + struct task_storage_nodeadlock *skel = arg; + int fd; + + while (!stop) { + fd = socket(AF_INET, SOCK_STREAM, 0); + close(fd); + if (skel->bss->nr_get_errs || skel->bss->nr_del_errs) + stop = true; + } + + return NULL; +} + +static void test_nodeadlock(void) +{ + struct task_storage_nodeadlock *skel; + struct bpf_prog_info info = {}; + __u32 info_len = sizeof(info); + const int nr_threads = 32; + pthread_t tids[nr_threads]; + int i, prog_fd, err; + cpu_set_t old, new; + + /* Pin all threads to one cpu to increase the chance of preemption + * in a sleepable bpf prog. + */ + CPU_ZERO(&new); + CPU_SET(0, &new); + err = sched_getaffinity(getpid(), sizeof(old), &old); + if (!ASSERT_OK(err, "getaffinity")) + return; + err = sched_setaffinity(getpid(), sizeof(new), &new); + if (!ASSERT_OK(err, "setaffinity")) + return; + + skel = task_storage_nodeadlock__open_and_load(); + if (!ASSERT_OK_PTR(skel, "open_and_load")) + goto done; + + /* Unnecessary recursion and deadlock detection are reproducible + * in the preemptible kernel. + */ + if (!skel->kconfig->CONFIG_PREEMPT) { + test__skip(); + goto done; + } + + err = task_storage_nodeadlock__attach(skel); + ASSERT_OK(err, "attach prog"); + + for (i = 0; i < nr_threads; i++) { + err = pthread_create(&tids[i], NULL, sock_create_loop, skel); + if (err) { + /* Only assert once here to avoid excessive + * PASS printing during test failure. + */ + ASSERT_OK(err, "pthread_create"); + waitall(tids, i); + goto done; + } + } + + /* With 32 threads, 1s is enough to reproduce the issue */ + sleep(1); + waitall(tids, nr_threads); + + info_len = sizeof(info); + prog_fd = bpf_program__fd(skel->progs.socket_post_create); + err = bpf_obj_get_info_by_fd(prog_fd, &info, &info_len); + ASSERT_OK(err, "get prog info"); + ASSERT_EQ(info.recursion_misses, 0, "prog recursion"); + + ASSERT_EQ(skel->bss->nr_get_errs, 0, "bpf_task_storage_get busy"); + ASSERT_EQ(skel->bss->nr_del_errs, 0, "bpf_task_storage_delete busy"); + +done: + task_storage_nodeadlock__destroy(skel); + sched_setaffinity(getpid(), sizeof(old), &old); +} + void test_task_local_storage(void) { if (test__start_subtest("sys_enter_exit")) @@ -101,4 +197,6 @@ void test_task_local_storage(void) test_exit_creds(); if (test__start_subtest("recursion")) test_recursion(); + if (test__start_subtest("nodeadlock")) + test_nodeadlock(); } diff --git a/tools/testing/selftests/bpf/progs/task_storage_nodeadlock.c b/tools/testing/selftests/bpf/progs/task_storage_nodeadlock.c new file mode 100644 index 000000000000..ea2dbb80f7b3 --- /dev/null +++ b/tools/testing/selftests/bpf/progs/task_storage_nodeadlock.c @@ -0,0 +1,47 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include "vmlinux.h" +#include +#include + +char _license[] SEC("license") = "GPL"; + +#ifndef EBUSY +#define EBUSY 16 +#endif + +extern bool CONFIG_PREEMPT __kconfig __weak; +int nr_get_errs = 0; +int nr_del_errs = 0; + +struct { + __uint(type, BPF_MAP_TYPE_TASK_STORAGE); + __uint(map_flags, BPF_F_NO_PREALLOC); + __type(key, int); + __type(value, int); +} task_storage SEC(".maps"); + +SEC("lsm.s/socket_post_create") +int BPF_PROG(socket_post_create, struct socket *sock, int family, int type, + int protocol, int kern) +{ + struct task_struct *task; + int ret, zero = 0; + int *value; + + if (!CONFIG_PREEMPT) + return 0; + + task = bpf_get_current_task_btf(); + value = bpf_task_storage_get(&task_storage, task, &zero, + BPF_LOCAL_STORAGE_GET_F_CREATE); + if (!value) + __sync_fetch_and_add(&nr_get_errs, 1); + + ret = bpf_task_storage_delete(&task_storage, + bpf_get_current_task_btf()); + if (ret == -EBUSY) + __sync_fetch_and_add(&nr_del_errs, 1); + + return 0; +} From 387b532138eed5b12e1afa68cafb6a389507310f Mon Sep 17 00:00:00 2001 From: Martin KaFai Lau Date: Tue, 25 Oct 2022 11:45:24 -0700 Subject: [PATCH 0368/2002] selftests/bpf: Tracing prog can still do lookup under busy lock This patch modifies the task_ls_recursion test to check that the first bpf_task_storage_get(&map_a, ...) in BPF_PROG(on_update) can still do the lockless lookup even it cannot acquire the percpu busy lock. If the lookup succeeds, it will increment the value by 1 and the value in the task storage map_a will become 200+1=201. After that, BPF_PROG(on_update) tries to delete from map_a and should get -EBUSY because it cannot acquire the percpu busy lock after finding the data. Signed-off-by: Martin KaFai Lau Link: https://lore.kernel.org/r/20221025184524.3526117-10-martin.lau@linux.dev Signed-off-by: Alexei Starovoitov --- .../bpf/prog_tests/task_local_storage.c | 48 ++++++++++++++++++- .../selftests/bpf/progs/task_ls_recursion.c | 45 +++++++++++++++-- 2 files changed, 87 insertions(+), 6 deletions(-) diff --git a/tools/testing/selftests/bpf/prog_tests/task_local_storage.c b/tools/testing/selftests/bpf/prog_tests/task_local_storage.c index ae535f5de6a2..a176bd75a748 100644 --- a/tools/testing/selftests/bpf/prog_tests/task_local_storage.c +++ b/tools/testing/selftests/bpf/prog_tests/task_local_storage.c @@ -8,6 +8,7 @@ #include /* For SYS_xxx definitions */ #include #include +#include "task_local_storage_helpers.h" #include "task_local_storage.skel.h" #include "task_local_storage_exit_creds.skel.h" #include "task_ls_recursion.skel.h" @@ -78,21 +79,64 @@ out: static void test_recursion(void) { + int err, map_fd, prog_fd, task_fd; struct task_ls_recursion *skel; - int err; + struct bpf_prog_info info; + __u32 info_len = sizeof(info); + long value; + + task_fd = sys_pidfd_open(getpid(), 0); + if (!ASSERT_NEQ(task_fd, -1, "sys_pidfd_open")) + return; skel = task_ls_recursion__open_and_load(); if (!ASSERT_OK_PTR(skel, "skel_open_and_load")) - return; + goto out; err = task_ls_recursion__attach(skel); if (!ASSERT_OK(err, "skel_attach")) goto out; /* trigger sys_enter, make sure it does not cause deadlock */ + skel->bss->test_pid = getpid(); syscall(SYS_gettid); + skel->bss->test_pid = 0; + task_ls_recursion__detach(skel); + + /* Refer to the comment in BPF_PROG(on_update) for + * the explanation on the value 201 and 100. + */ + map_fd = bpf_map__fd(skel->maps.map_a); + err = bpf_map_lookup_elem(map_fd, &task_fd, &value); + ASSERT_OK(err, "lookup map_a"); + ASSERT_EQ(value, 201, "map_a value"); + ASSERT_EQ(skel->bss->nr_del_errs, 1, "bpf_task_storage_delete busy"); + + map_fd = bpf_map__fd(skel->maps.map_b); + err = bpf_map_lookup_elem(map_fd, &task_fd, &value); + ASSERT_OK(err, "lookup map_b"); + ASSERT_EQ(value, 100, "map_b value"); + + prog_fd = bpf_program__fd(skel->progs.on_lookup); + memset(&info, 0, sizeof(info)); + err = bpf_obj_get_info_by_fd(prog_fd, &info, &info_len); + ASSERT_OK(err, "get prog info"); + ASSERT_GT(info.recursion_misses, 0, "on_lookup prog recursion"); + + prog_fd = bpf_program__fd(skel->progs.on_update); + memset(&info, 0, sizeof(info)); + err = bpf_obj_get_info_by_fd(prog_fd, &info, &info_len); + ASSERT_OK(err, "get prog info"); + ASSERT_EQ(info.recursion_misses, 0, "on_update prog recursion"); + + prog_fd = bpf_program__fd(skel->progs.on_enter); + memset(&info, 0, sizeof(info)); + err = bpf_obj_get_info_by_fd(prog_fd, &info, &info_len); + ASSERT_OK(err, "get prog info"); + ASSERT_EQ(info.recursion_misses, 0, "on_enter prog recursion"); out: + close(task_fd); task_ls_recursion__destroy(skel); } diff --git a/tools/testing/selftests/bpf/progs/task_ls_recursion.c b/tools/testing/selftests/bpf/progs/task_ls_recursion.c index 564583dca7c8..4542dc683b44 100644 --- a/tools/testing/selftests/bpf/progs/task_ls_recursion.c +++ b/tools/testing/selftests/bpf/progs/task_ls_recursion.c @@ -5,7 +5,13 @@ #include #include +#ifndef EBUSY +#define EBUSY 16 +#endif + char _license[] SEC("license") = "GPL"; +int nr_del_errs = 0; +int test_pid = 0; struct { __uint(type, BPF_MAP_TYPE_TASK_STORAGE); @@ -26,6 +32,13 @@ int BPF_PROG(on_lookup) { struct task_struct *task = bpf_get_current_task_btf(); + if (!test_pid || task->pid != test_pid) + return 0; + + /* The bpf_task_storage_delete will call + * bpf_local_storage_lookup. The prog->active will + * stop the recursion. + */ bpf_task_storage_delete(&map_a, task); bpf_task_storage_delete(&map_b, task); return 0; @@ -37,11 +50,32 @@ int BPF_PROG(on_update) struct task_struct *task = bpf_get_current_task_btf(); long *ptr; + if (!test_pid || task->pid != test_pid) + return 0; + ptr = bpf_task_storage_get(&map_a, task, 0, BPF_LOCAL_STORAGE_GET_F_CREATE); - if (ptr) - *ptr += 1; + /* ptr will not be NULL when it is called from + * the bpf_task_storage_get(&map_b,...F_CREATE) in + * the BPF_PROG(on_enter) below. It is because + * the value can be found in map_a and the kernel + * does not need to acquire any spin_lock. + */ + if (ptr) { + int err; + *ptr += 1; + err = bpf_task_storage_delete(&map_a, task); + if (err == -EBUSY) + nr_del_errs++; + } + + /* This will still fail because map_b is empty and + * this BPF_PROG(on_update) has failed to acquire + * the percpu busy lock => meaning potential + * deadlock is detected and it will fail to create + * new storage. + */ ptr = bpf_task_storage_get(&map_b, task, 0, BPF_LOCAL_STORAGE_GET_F_CREATE); if (ptr) @@ -57,14 +91,17 @@ int BPF_PROG(on_enter, struct pt_regs *regs, long id) long *ptr; task = bpf_get_current_task_btf(); + if (!test_pid || task->pid != test_pid) + return 0; + ptr = bpf_task_storage_get(&map_a, task, 0, BPF_LOCAL_STORAGE_GET_F_CREATE); - if (ptr) + if (ptr && !*ptr) *ptr = 200; ptr = bpf_task_storage_get(&map_b, task, 0, BPF_LOCAL_STORAGE_GET_F_CREATE); - if (ptr) + if (ptr && !*ptr) *ptr = 100; return 0; } From 5e67b8ef125bb6e83bf0f0442ad7ffc09e7956f9 Mon Sep 17 00:00:00 2001 From: Yonghong Song Date: Tue, 25 Oct 2022 21:28:40 -0700 Subject: [PATCH 0369/2002] bpf: Make struct cgroup btf id global Make struct cgroup btf id global so later patch can reuse the same btf id. Acked-by: David Vernet Signed-off-by: Yonghong Song Link: https://lore.kernel.org/r/20221026042840.672602-1-yhs@fb.com Signed-off-by: Alexei Starovoitov --- include/linux/btf_ids.h | 1 + kernel/bpf/cgroup_iter.c | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/include/linux/btf_ids.h b/include/linux/btf_ids.h index 2aea877d644f..c9744efd202f 100644 --- a/include/linux/btf_ids.h +++ b/include/linux/btf_ids.h @@ -265,5 +265,6 @@ MAX_BTF_TRACING_TYPE, }; extern u32 btf_tracing_ids[]; +extern u32 bpf_cgroup_btf_id[]; #endif diff --git a/kernel/bpf/cgroup_iter.c b/kernel/bpf/cgroup_iter.c index 0d200a993489..c6ffc706d583 100644 --- a/kernel/bpf/cgroup_iter.c +++ b/kernel/bpf/cgroup_iter.c @@ -157,7 +157,7 @@ static const struct seq_operations cgroup_iter_seq_ops = { .show = cgroup_iter_seq_show, }; -BTF_ID_LIST_SINGLE(bpf_cgroup_btf_id, struct, cgroup) +BTF_ID_LIST_GLOBAL_SINGLE(bpf_cgroup_btf_id, struct, cgroup) static int cgroup_iter_seq_init(void *priv, struct bpf_iter_aux_info *aux) { From c83597fa5dc6b322e9bdf929e5f4136a3f4aa4db Mon Sep 17 00:00:00 2001 From: Yonghong Song Date: Tue, 25 Oct 2022 21:28:45 -0700 Subject: [PATCH 0370/2002] bpf: Refactor some inode/task/sk storage functions for reuse Refactor codes so that inode/task/sk storage implementation can maximally share the same code. I also added some comments in new function bpf_local_storage_unlink_nolock() to make codes easy to understand. There is no functionality change. Acked-by: David Vernet Signed-off-by: Yonghong Song Link: https://lore.kernel.org/r/20221026042845.672944-1-yhs@fb.com Signed-off-by: Alexei Starovoitov --- include/linux/bpf_local_storage.h | 17 +-- kernel/bpf/bpf_inode_storage.c | 38 +----- kernel/bpf/bpf_local_storage.c | 208 +++++++++++++++++++----------- kernel/bpf/bpf_task_storage.c | 38 +----- net/core/bpf_sk_storage.c | 35 +---- 5 files changed, 146 insertions(+), 190 deletions(-) diff --git a/include/linux/bpf_local_storage.h b/include/linux/bpf_local_storage.h index 7ea18d4da84b..6d37a40cd90e 100644 --- a/include/linux/bpf_local_storage.h +++ b/include/linux/bpf_local_storage.h @@ -116,21 +116,22 @@ static struct bpf_local_storage_cache name = { \ .idx_lock = __SPIN_LOCK_UNLOCKED(name.idx_lock), \ } -u16 bpf_local_storage_cache_idx_get(struct bpf_local_storage_cache *cache); -void bpf_local_storage_cache_idx_free(struct bpf_local_storage_cache *cache, - u16 idx); - /* Helper functions for bpf_local_storage */ int bpf_local_storage_map_alloc_check(union bpf_attr *attr); -struct bpf_local_storage_map *bpf_local_storage_map_alloc(union bpf_attr *attr); +struct bpf_map * +bpf_local_storage_map_alloc(union bpf_attr *attr, + struct bpf_local_storage_cache *cache); struct bpf_local_storage_data * bpf_local_storage_lookup(struct bpf_local_storage *local_storage, struct bpf_local_storage_map *smap, bool cacheit_lockit); -void bpf_local_storage_map_free(struct bpf_local_storage_map *smap, +bool bpf_local_storage_unlink_nolock(struct bpf_local_storage *local_storage); + +void bpf_local_storage_map_free(struct bpf_map *map, + struct bpf_local_storage_cache *cache, int __percpu *busy_counter); int bpf_local_storage_map_check_btf(const struct bpf_map *map, @@ -141,10 +142,6 @@ int bpf_local_storage_map_check_btf(const struct bpf_map *map, void bpf_selem_link_storage_nolock(struct bpf_local_storage *local_storage, struct bpf_local_storage_elem *selem); -bool bpf_selem_unlink_storage_nolock(struct bpf_local_storage *local_storage, - struct bpf_local_storage_elem *selem, - bool uncharge_omem, bool use_trace_rcu); - void bpf_selem_unlink(struct bpf_local_storage_elem *selem, bool use_trace_rcu); void bpf_selem_link_map(struct bpf_local_storage_map *smap, diff --git a/kernel/bpf/bpf_inode_storage.c b/kernel/bpf/bpf_inode_storage.c index 5f7683b19199..6a1d4d22816a 100644 --- a/kernel/bpf/bpf_inode_storage.c +++ b/kernel/bpf/bpf_inode_storage.c @@ -56,11 +56,9 @@ static struct bpf_local_storage_data *inode_storage_lookup(struct inode *inode, void bpf_inode_storage_free(struct inode *inode) { - struct bpf_local_storage_elem *selem; struct bpf_local_storage *local_storage; bool free_inode_storage = false; struct bpf_storage_blob *bsb; - struct hlist_node *n; bsb = bpf_inode(inode); if (!bsb) @@ -74,30 +72,11 @@ void bpf_inode_storage_free(struct inode *inode) return; } - /* Neither the bpf_prog nor the bpf-map's syscall - * could be modifying the local_storage->list now. - * Thus, no elem can be added-to or deleted-from the - * local_storage->list by the bpf_prog or by the bpf-map's syscall. - * - * It is racing with bpf_local_storage_map_free() alone - * when unlinking elem from the local_storage->list and - * the map's bucket->list. - */ raw_spin_lock_bh(&local_storage->lock); - hlist_for_each_entry_safe(selem, n, &local_storage->list, snode) { - /* Always unlink from map before unlinking from - * local_storage. - */ - bpf_selem_unlink_map(selem); - free_inode_storage = bpf_selem_unlink_storage_nolock( - local_storage, selem, false, false); - } + free_inode_storage = bpf_local_storage_unlink_nolock(local_storage); raw_spin_unlock_bh(&local_storage->lock); rcu_read_unlock(); - /* free_inoode_storage should always be true as long as - * local_storage->list was non-empty. - */ if (free_inode_storage) kfree_rcu(local_storage, rcu); } @@ -226,23 +205,12 @@ static int notsupp_get_next_key(struct bpf_map *map, void *key, static struct bpf_map *inode_storage_map_alloc(union bpf_attr *attr) { - struct bpf_local_storage_map *smap; - - smap = bpf_local_storage_map_alloc(attr); - if (IS_ERR(smap)) - return ERR_CAST(smap); - - smap->cache_idx = bpf_local_storage_cache_idx_get(&inode_cache); - return &smap->map; + return bpf_local_storage_map_alloc(attr, &inode_cache); } static void inode_storage_map_free(struct bpf_map *map) { - struct bpf_local_storage_map *smap; - - smap = (struct bpf_local_storage_map *)map; - bpf_local_storage_cache_idx_free(&inode_cache, smap->cache_idx); - bpf_local_storage_map_free(smap, NULL); + bpf_local_storage_map_free(map, &inode_cache, NULL); } BTF_ID_LIST_SINGLE(inode_storage_map_btf_ids, struct, diff --git a/kernel/bpf/bpf_local_storage.c b/kernel/bpf/bpf_local_storage.c index 781d14167140..93d9b1b17bc8 100644 --- a/kernel/bpf/bpf_local_storage.c +++ b/kernel/bpf/bpf_local_storage.c @@ -113,9 +113,9 @@ static void bpf_selem_free_rcu(struct rcu_head *rcu) * The caller must ensure selem->smap is still valid to be * dereferenced for its smap->elem_size and smap->cache_idx. */ -bool bpf_selem_unlink_storage_nolock(struct bpf_local_storage *local_storage, - struct bpf_local_storage_elem *selem, - bool uncharge_mem, bool use_trace_rcu) +static bool bpf_selem_unlink_storage_nolock(struct bpf_local_storage *local_storage, + struct bpf_local_storage_elem *selem, + bool uncharge_mem, bool use_trace_rcu) { struct bpf_local_storage_map *smap; bool free_local_storage; @@ -501,7 +501,7 @@ unlock_err: return ERR_PTR(err); } -u16 bpf_local_storage_cache_idx_get(struct bpf_local_storage_cache *cache) +static u16 bpf_local_storage_cache_idx_get(struct bpf_local_storage_cache *cache) { u64 min_usage = U64_MAX; u16 i, res = 0; @@ -525,21 +525,143 @@ u16 bpf_local_storage_cache_idx_get(struct bpf_local_storage_cache *cache) return res; } -void bpf_local_storage_cache_idx_free(struct bpf_local_storage_cache *cache, - u16 idx) +static void bpf_local_storage_cache_idx_free(struct bpf_local_storage_cache *cache, + u16 idx) { spin_lock(&cache->idx_lock); cache->idx_usage_counts[idx]--; spin_unlock(&cache->idx_lock); } -void bpf_local_storage_map_free(struct bpf_local_storage_map *smap, - int __percpu *busy_counter) +int bpf_local_storage_map_alloc_check(union bpf_attr *attr) +{ + if (attr->map_flags & ~BPF_LOCAL_STORAGE_CREATE_FLAG_MASK || + !(attr->map_flags & BPF_F_NO_PREALLOC) || + attr->max_entries || + attr->key_size != sizeof(int) || !attr->value_size || + /* Enforce BTF for userspace sk dumping */ + !attr->btf_key_type_id || !attr->btf_value_type_id) + return -EINVAL; + + if (!bpf_capable()) + return -EPERM; + + if (attr->value_size > BPF_LOCAL_STORAGE_MAX_VALUE_SIZE) + return -E2BIG; + + return 0; +} + +static struct bpf_local_storage_map *__bpf_local_storage_map_alloc(union bpf_attr *attr) +{ + struct bpf_local_storage_map *smap; + unsigned int i; + u32 nbuckets; + + smap = bpf_map_area_alloc(sizeof(*smap), NUMA_NO_NODE); + if (!smap) + return ERR_PTR(-ENOMEM); + bpf_map_init_from_attr(&smap->map, attr); + + nbuckets = roundup_pow_of_two(num_possible_cpus()); + /* Use at least 2 buckets, select_bucket() is undefined behavior with 1 bucket */ + nbuckets = max_t(u32, 2, nbuckets); + smap->bucket_log = ilog2(nbuckets); + + smap->buckets = kvcalloc(sizeof(*smap->buckets), nbuckets, + GFP_USER | __GFP_NOWARN | __GFP_ACCOUNT); + if (!smap->buckets) { + bpf_map_area_free(smap); + return ERR_PTR(-ENOMEM); + } + + for (i = 0; i < nbuckets; i++) { + INIT_HLIST_HEAD(&smap->buckets[i].list); + raw_spin_lock_init(&smap->buckets[i].lock); + } + + smap->elem_size = + sizeof(struct bpf_local_storage_elem) + attr->value_size; + + return smap; +} + +int bpf_local_storage_map_check_btf(const struct bpf_map *map, + const struct btf *btf, + const struct btf_type *key_type, + const struct btf_type *value_type) +{ + u32 int_data; + + if (BTF_INFO_KIND(key_type->info) != BTF_KIND_INT) + return -EINVAL; + + int_data = *(u32 *)(key_type + 1); + if (BTF_INT_BITS(int_data) != 32 || BTF_INT_OFFSET(int_data)) + return -EINVAL; + + return 0; +} + +bool bpf_local_storage_unlink_nolock(struct bpf_local_storage *local_storage) { struct bpf_local_storage_elem *selem; + bool free_storage = false; + struct hlist_node *n; + + /* Neither the bpf_prog nor the bpf_map's syscall + * could be modifying the local_storage->list now. + * Thus, no elem can be added to or deleted from the + * local_storage->list by the bpf_prog or by the bpf_map's syscall. + * + * It is racing with bpf_local_storage_map_free() alone + * when unlinking elem from the local_storage->list and + * the map's bucket->list. + */ + hlist_for_each_entry_safe(selem, n, &local_storage->list, snode) { + /* Always unlink from map before unlinking from + * local_storage. + */ + bpf_selem_unlink_map(selem); + /* If local_storage list has only one element, the + * bpf_selem_unlink_storage_nolock() will return true. + * Otherwise, it will return false. The current loop iteration + * intends to remove all local storage. So the last iteration + * of the loop will set the free_cgroup_storage to true. + */ + free_storage = bpf_selem_unlink_storage_nolock( + local_storage, selem, false, false); + } + + return free_storage; +} + +struct bpf_map * +bpf_local_storage_map_alloc(union bpf_attr *attr, + struct bpf_local_storage_cache *cache) +{ + struct bpf_local_storage_map *smap; + + smap = __bpf_local_storage_map_alloc(attr); + if (IS_ERR(smap)) + return ERR_CAST(smap); + + smap->cache_idx = bpf_local_storage_cache_idx_get(cache); + return &smap->map; +} + +void bpf_local_storage_map_free(struct bpf_map *map, + struct bpf_local_storage_cache *cache, + int __percpu *busy_counter) +{ struct bpf_local_storage_map_bucket *b; + struct bpf_local_storage_elem *selem; + struct bpf_local_storage_map *smap; unsigned int i; + smap = (struct bpf_local_storage_map *)map; + bpf_local_storage_cache_idx_free(cache, smap->cache_idx); + /* Note that this map might be concurrently cloned from * bpf_sk_storage_clone. Wait for any existing bpf_sk_storage_clone * RCU read section to finish before proceeding. New RCU @@ -594,73 +716,3 @@ void bpf_local_storage_map_free(struct bpf_local_storage_map *smap, kvfree(smap->buckets); bpf_map_area_free(smap); } - -int bpf_local_storage_map_alloc_check(union bpf_attr *attr) -{ - if (attr->map_flags & ~BPF_LOCAL_STORAGE_CREATE_FLAG_MASK || - !(attr->map_flags & BPF_F_NO_PREALLOC) || - attr->max_entries || - attr->key_size != sizeof(int) || !attr->value_size || - /* Enforce BTF for userspace sk dumping */ - !attr->btf_key_type_id || !attr->btf_value_type_id) - return -EINVAL; - - if (!bpf_capable()) - return -EPERM; - - if (attr->value_size > BPF_LOCAL_STORAGE_MAX_VALUE_SIZE) - return -E2BIG; - - return 0; -} - -struct bpf_local_storage_map *bpf_local_storage_map_alloc(union bpf_attr *attr) -{ - struct bpf_local_storage_map *smap; - unsigned int i; - u32 nbuckets; - - smap = bpf_map_area_alloc(sizeof(*smap), NUMA_NO_NODE); - if (!smap) - return ERR_PTR(-ENOMEM); - bpf_map_init_from_attr(&smap->map, attr); - - nbuckets = roundup_pow_of_two(num_possible_cpus()); - /* Use at least 2 buckets, select_bucket() is undefined behavior with 1 bucket */ - nbuckets = max_t(u32, 2, nbuckets); - smap->bucket_log = ilog2(nbuckets); - - smap->buckets = kvcalloc(sizeof(*smap->buckets), nbuckets, - GFP_USER | __GFP_NOWARN | __GFP_ACCOUNT); - if (!smap->buckets) { - bpf_map_area_free(smap); - return ERR_PTR(-ENOMEM); - } - - for (i = 0; i < nbuckets; i++) { - INIT_HLIST_HEAD(&smap->buckets[i].list); - raw_spin_lock_init(&smap->buckets[i].lock); - } - - smap->elem_size = - sizeof(struct bpf_local_storage_elem) + attr->value_size; - - return smap; -} - -int bpf_local_storage_map_check_btf(const struct bpf_map *map, - const struct btf *btf, - const struct btf_type *key_type, - const struct btf_type *value_type) -{ - u32 int_data; - - if (BTF_INFO_KIND(key_type->info) != BTF_KIND_INT) - return -EINVAL; - - int_data = *(u32 *)(key_type + 1); - if (BTF_INT_BITS(int_data) != 32 || BTF_INT_OFFSET(int_data)) - return -EINVAL; - - return 0; -} diff --git a/kernel/bpf/bpf_task_storage.c b/kernel/bpf/bpf_task_storage.c index ba3fe72d1fa5..8e832db8151a 100644 --- a/kernel/bpf/bpf_task_storage.c +++ b/kernel/bpf/bpf_task_storage.c @@ -71,10 +71,8 @@ task_storage_lookup(struct task_struct *task, struct bpf_map *map, void bpf_task_storage_free(struct task_struct *task) { - struct bpf_local_storage_elem *selem; struct bpf_local_storage *local_storage; bool free_task_storage = false; - struct hlist_node *n; unsigned long flags; rcu_read_lock(); @@ -85,32 +83,13 @@ void bpf_task_storage_free(struct task_struct *task) return; } - /* Neither the bpf_prog nor the bpf-map's syscall - * could be modifying the local_storage->list now. - * Thus, no elem can be added-to or deleted-from the - * local_storage->list by the bpf_prog or by the bpf-map's syscall. - * - * It is racing with bpf_local_storage_map_free() alone - * when unlinking elem from the local_storage->list and - * the map's bucket->list. - */ bpf_task_storage_lock(); raw_spin_lock_irqsave(&local_storage->lock, flags); - hlist_for_each_entry_safe(selem, n, &local_storage->list, snode) { - /* Always unlink from map before unlinking from - * local_storage. - */ - bpf_selem_unlink_map(selem); - free_task_storage = bpf_selem_unlink_storage_nolock( - local_storage, selem, false, false); - } + free_task_storage = bpf_local_storage_unlink_nolock(local_storage); raw_spin_unlock_irqrestore(&local_storage->lock, flags); bpf_task_storage_unlock(); rcu_read_unlock(); - /* free_task_storage should always be true as long as - * local_storage->list was non-empty. - */ if (free_task_storage) kfree_rcu(local_storage, rcu); } @@ -337,23 +316,12 @@ static int notsupp_get_next_key(struct bpf_map *map, void *key, void *next_key) static struct bpf_map *task_storage_map_alloc(union bpf_attr *attr) { - struct bpf_local_storage_map *smap; - - smap = bpf_local_storage_map_alloc(attr); - if (IS_ERR(smap)) - return ERR_CAST(smap); - - smap->cache_idx = bpf_local_storage_cache_idx_get(&task_cache); - return &smap->map; + return bpf_local_storage_map_alloc(attr, &task_cache); } static void task_storage_map_free(struct bpf_map *map) { - struct bpf_local_storage_map *smap; - - smap = (struct bpf_local_storage_map *)map; - bpf_local_storage_cache_idx_free(&task_cache, smap->cache_idx); - bpf_local_storage_map_free(smap, &bpf_task_storage_busy); + bpf_local_storage_map_free(map, &task_cache, &bpf_task_storage_busy); } BTF_ID_LIST_SINGLE(task_storage_map_btf_ids, struct, bpf_local_storage_map) diff --git a/net/core/bpf_sk_storage.c b/net/core/bpf_sk_storage.c index 94374d529ea4..49884e7de080 100644 --- a/net/core/bpf_sk_storage.c +++ b/net/core/bpf_sk_storage.c @@ -48,10 +48,8 @@ static int bpf_sk_storage_del(struct sock *sk, struct bpf_map *map) /* Called by __sk_destruct() & bpf_sk_storage_clone() */ void bpf_sk_storage_free(struct sock *sk) { - struct bpf_local_storage_elem *selem; struct bpf_local_storage *sk_storage; bool free_sk_storage = false; - struct hlist_node *n; rcu_read_lock(); sk_storage = rcu_dereference(sk->sk_bpf_storage); @@ -60,24 +58,8 @@ void bpf_sk_storage_free(struct sock *sk) return; } - /* Netiher the bpf_prog nor the bpf-map's syscall - * could be modifying the sk_storage->list now. - * Thus, no elem can be added-to or deleted-from the - * sk_storage->list by the bpf_prog or by the bpf-map's syscall. - * - * It is racing with bpf_local_storage_map_free() alone - * when unlinking elem from the sk_storage->list and - * the map's bucket->list. - */ raw_spin_lock_bh(&sk_storage->lock); - hlist_for_each_entry_safe(selem, n, &sk_storage->list, snode) { - /* Always unlink from map before unlinking from - * sk_storage. - */ - bpf_selem_unlink_map(selem); - free_sk_storage = bpf_selem_unlink_storage_nolock( - sk_storage, selem, true, false); - } + free_sk_storage = bpf_local_storage_unlink_nolock(sk_storage); raw_spin_unlock_bh(&sk_storage->lock); rcu_read_unlock(); @@ -87,23 +69,12 @@ void bpf_sk_storage_free(struct sock *sk) static void bpf_sk_storage_map_free(struct bpf_map *map) { - struct bpf_local_storage_map *smap; - - smap = (struct bpf_local_storage_map *)map; - bpf_local_storage_cache_idx_free(&sk_cache, smap->cache_idx); - bpf_local_storage_map_free(smap, NULL); + bpf_local_storage_map_free(map, &sk_cache, NULL); } static struct bpf_map *bpf_sk_storage_map_alloc(union bpf_attr *attr) { - struct bpf_local_storage_map *smap; - - smap = bpf_local_storage_map_alloc(attr); - if (IS_ERR(smap)) - return ERR_CAST(smap); - - smap->cache_idx = bpf_local_storage_cache_idx_get(&sk_cache); - return &smap->map; + return bpf_local_storage_map_alloc(attr, &sk_cache); } static int notsupp_get_next_key(struct bpf_map *map, void *key, From c4bcfb38a95edb1021a53f2d0356a78120ecfbe4 Mon Sep 17 00:00:00 2001 From: Yonghong Song Date: Tue, 25 Oct 2022 21:28:50 -0700 Subject: [PATCH 0371/2002] bpf: Implement cgroup storage available to non-cgroup-attached bpf progs Similar to sk/inode/task storage, implement similar cgroup local storage. There already exists a local storage implementation for cgroup-attached bpf programs. See map type BPF_MAP_TYPE_CGROUP_STORAGE and helper bpf_get_local_storage(). But there are use cases such that non-cgroup attached bpf progs wants to access cgroup local storage data. For example, tc egress prog has access to sk and cgroup. It is possible to use sk local storage to emulate cgroup local storage by storing data in socket. But this is a waste as it could be lots of sockets belonging to a particular cgroup. Alternatively, a separate map can be created with cgroup id as the key. But this will introduce additional overhead to manipulate the new map. A cgroup local storage, similar to existing sk/inode/task storage, should help for this use case. The life-cycle of storage is managed with the life-cycle of the cgroup struct. i.e. the storage is destroyed along with the owning cgroup with a call to bpf_cgrp_storage_free() when cgroup itself is deleted. The userspace map operations can be done by using a cgroup fd as a key passed to the lookup, update and delete operations. Typically, the following code is used to get the current cgroup: struct task_struct *task = bpf_get_current_task_btf(); ... task->cgroups->dfl_cgrp ... and in structure task_struct definition: struct task_struct { .... struct css_set __rcu *cgroups; .... } With sleepable program, accessing task->cgroups is not protected by rcu_read_lock. So the current implementation only supports non-sleepable program and supporting sleepable program will be the next step together with adding rcu_read_lock protection for rcu tagged structures. Since map name BPF_MAP_TYPE_CGROUP_STORAGE has been used for old cgroup local storage support, the new map name BPF_MAP_TYPE_CGRP_STORAGE is used for cgroup storage available to non-cgroup-attached bpf programs. The old cgroup storage supports bpf_get_local_storage() helper to get the cgroup data. The new cgroup storage helper bpf_cgrp_storage_get() can provide similar functionality. While old cgroup storage pre-allocates storage memory, the new mechanism can also pre-allocate with a user space bpf_map_update_elem() call to avoid potential run-time memory allocation failure. Therefore, the new cgroup storage can provide all functionality w.r.t. the old one. So in uapi bpf.h, the old BPF_MAP_TYPE_CGROUP_STORAGE is alias to BPF_MAP_TYPE_CGROUP_STORAGE_DEPRECATED to indicate the old cgroup storage can be deprecated since the new one can provide the same functionality. Acked-by: David Vernet Signed-off-by: Yonghong Song Link: https://lore.kernel.org/r/20221026042850.673791-1-yhs@fb.com Signed-off-by: Alexei Starovoitov --- include/linux/bpf.h | 7 + include/linux/bpf_types.h | 1 + include/linux/cgroup-defs.h | 4 + include/uapi/linux/bpf.h | 50 ++++++- kernel/bpf/Makefile | 2 +- kernel/bpf/bpf_cgrp_storage.c | 247 +++++++++++++++++++++++++++++++++ kernel/bpf/helpers.c | 6 + kernel/bpf/syscall.c | 3 +- kernel/bpf/verifier.c | 13 +- kernel/cgroup/cgroup.c | 1 + kernel/trace/bpf_trace.c | 4 + scripts/bpf_doc.py | 2 + tools/include/uapi/linux/bpf.h | 50 ++++++- 13 files changed, 385 insertions(+), 5 deletions(-) create mode 100644 kernel/bpf/bpf_cgrp_storage.c diff --git a/include/linux/bpf.h b/include/linux/bpf.h index a5dbac8f5aba..9fd68b0b3e9c 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -2041,6 +2041,7 @@ struct bpf_link *bpf_link_by_id(u32 id); const struct bpf_func_proto *bpf_base_func_proto(enum bpf_func_id func_id); void bpf_task_storage_free(struct task_struct *task); +void bpf_cgrp_storage_free(struct cgroup *cgroup); bool bpf_prog_has_kfunc_call(const struct bpf_prog *prog); const struct btf_func_model * bpf_jit_find_kfunc_model(const struct bpf_prog *prog, @@ -2295,6 +2296,10 @@ static inline bool has_current_bpf_ctx(void) static inline void bpf_prog_inc_misses_counter(struct bpf_prog *prog) { } + +static inline void bpf_cgrp_storage_free(struct cgroup *cgroup) +{ +} #endif /* CONFIG_BPF_SYSCALL */ void __bpf_free_used_btfs(struct bpf_prog_aux *aux, @@ -2535,6 +2540,8 @@ extern const struct bpf_func_proto bpf_copy_from_user_task_proto; extern const struct bpf_func_proto bpf_set_retval_proto; extern const struct bpf_func_proto bpf_get_retval_proto; extern const struct bpf_func_proto bpf_user_ringbuf_drain_proto; +extern const struct bpf_func_proto bpf_cgrp_storage_get_proto; +extern const struct bpf_func_proto bpf_cgrp_storage_delete_proto; const struct bpf_func_proto *tracing_prog_func_proto( enum bpf_func_id func_id, const struct bpf_prog *prog); diff --git a/include/linux/bpf_types.h b/include/linux/bpf_types.h index 2c6a4f2562a7..d4ee3ccd3753 100644 --- a/include/linux/bpf_types.h +++ b/include/linux/bpf_types.h @@ -86,6 +86,7 @@ BPF_MAP_TYPE(BPF_MAP_TYPE_PROG_ARRAY, prog_array_map_ops) BPF_MAP_TYPE(BPF_MAP_TYPE_PERF_EVENT_ARRAY, perf_event_array_map_ops) #ifdef CONFIG_CGROUPS BPF_MAP_TYPE(BPF_MAP_TYPE_CGROUP_ARRAY, cgroup_array_map_ops) +BPF_MAP_TYPE(BPF_MAP_TYPE_CGRP_STORAGE, cgrp_storage_map_ops) #endif #ifdef CONFIG_CGROUP_BPF BPF_MAP_TYPE(BPF_MAP_TYPE_CGROUP_STORAGE, cgroup_storage_map_ops) diff --git a/include/linux/cgroup-defs.h b/include/linux/cgroup-defs.h index 8f481d1b159a..c466fdc3a32a 100644 --- a/include/linux/cgroup-defs.h +++ b/include/linux/cgroup-defs.h @@ -504,6 +504,10 @@ struct cgroup { /* Used to store internal freezer state */ struct cgroup_freezer_state freezer; +#ifdef CONFIG_BPF_SYSCALL + struct bpf_local_storage __rcu *bpf_cgrp_storage; +#endif + /* All ancestors including self */ struct cgroup *ancestors[]; }; diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h index 17f61338f8f8..94659f6b3395 100644 --- a/include/uapi/linux/bpf.h +++ b/include/uapi/linux/bpf.h @@ -922,7 +922,14 @@ enum bpf_map_type { BPF_MAP_TYPE_CPUMAP, BPF_MAP_TYPE_XSKMAP, BPF_MAP_TYPE_SOCKHASH, - BPF_MAP_TYPE_CGROUP_STORAGE, + BPF_MAP_TYPE_CGROUP_STORAGE_DEPRECATED, + /* BPF_MAP_TYPE_CGROUP_STORAGE is available to bpf programs attaching + * to a cgroup. The newer BPF_MAP_TYPE_CGRP_STORAGE is available to + * both cgroup-attached and other progs and supports all functionality + * provided by BPF_MAP_TYPE_CGROUP_STORAGE. So mark + * BPF_MAP_TYPE_CGROUP_STORAGE deprecated. + */ + BPF_MAP_TYPE_CGROUP_STORAGE = BPF_MAP_TYPE_CGROUP_STORAGE_DEPRECATED, BPF_MAP_TYPE_REUSEPORT_SOCKARRAY, BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE, BPF_MAP_TYPE_QUEUE, @@ -935,6 +942,7 @@ enum bpf_map_type { BPF_MAP_TYPE_TASK_STORAGE, BPF_MAP_TYPE_BLOOM_FILTER, BPF_MAP_TYPE_USER_RINGBUF, + BPF_MAP_TYPE_CGRP_STORAGE, }; /* Note that tracing related programs such as @@ -5435,6 +5443,44 @@ union bpf_attr { * **-E2BIG** if user-space has tried to publish a sample which is * larger than the size of the ring buffer, or which cannot fit * within a struct bpf_dynptr. + * + * void *bpf_cgrp_storage_get(struct bpf_map *map, struct cgroup *cgroup, void *value, u64 flags) + * Description + * Get a bpf_local_storage from the *cgroup*. + * + * Logically, it could be thought of as getting the value from + * a *map* with *cgroup* as the **key**. From this + * perspective, the usage is not much different from + * **bpf_map_lookup_elem**\ (*map*, **&**\ *cgroup*) except this + * helper enforces the key must be a cgroup struct and the map must also + * be a **BPF_MAP_TYPE_CGRP_STORAGE**. + * + * In reality, the local-storage value is embedded directly inside of the + * *cgroup* object itself, rather than being located in the + * **BPF_MAP_TYPE_CGRP_STORAGE** map. When the local-storage value is + * queried for some *map* on a *cgroup* object, the kernel will perform an + * O(n) iteration over all of the live local-storage values for that + * *cgroup* object until the local-storage value for the *map* is found. + * + * An optional *flags* (**BPF_LOCAL_STORAGE_GET_F_CREATE**) can be + * used such that a new bpf_local_storage will be + * created if one does not exist. *value* can be used + * together with **BPF_LOCAL_STORAGE_GET_F_CREATE** to specify + * the initial value of a bpf_local_storage. If *value* is + * **NULL**, the new bpf_local_storage will be zero initialized. + * Return + * A bpf_local_storage pointer is returned on success. + * + * **NULL** if not found or there was an error in adding + * a new bpf_local_storage. + * + * long bpf_cgrp_storage_delete(struct bpf_map *map, struct cgroup *cgroup) + * Description + * Delete a bpf_local_storage from a *cgroup*. + * Return + * 0 on success. + * + * **-ENOENT** if the bpf_local_storage cannot be found. */ #define ___BPF_FUNC_MAPPER(FN, ctx...) \ FN(unspec, 0, ##ctx) \ @@ -5647,6 +5693,8 @@ union bpf_attr { FN(tcp_raw_check_syncookie_ipv6, 207, ##ctx) \ FN(ktime_get_tai_ns, 208, ##ctx) \ FN(user_ringbuf_drain, 209, ##ctx) \ + FN(cgrp_storage_get, 210, ##ctx) \ + FN(cgrp_storage_delete, 211, ##ctx) \ /* */ /* backwards-compatibility macros for users of __BPF_FUNC_MAPPER that don't diff --git a/kernel/bpf/Makefile b/kernel/bpf/Makefile index 341c94f208f4..3a12e6b400a2 100644 --- a/kernel/bpf/Makefile +++ b/kernel/bpf/Makefile @@ -25,7 +25,7 @@ ifeq ($(CONFIG_PERF_EVENTS),y) obj-$(CONFIG_BPF_SYSCALL) += stackmap.o endif ifeq ($(CONFIG_CGROUPS),y) -obj-$(CONFIG_BPF_SYSCALL) += cgroup_iter.o +obj-$(CONFIG_BPF_SYSCALL) += cgroup_iter.o bpf_cgrp_storage.o endif obj-$(CONFIG_CGROUP_BPF) += cgroup.o ifeq ($(CONFIG_INET),y) diff --git a/kernel/bpf/bpf_cgrp_storage.c b/kernel/bpf/bpf_cgrp_storage.c new file mode 100644 index 000000000000..309403800f82 --- /dev/null +++ b/kernel/bpf/bpf_cgrp_storage.c @@ -0,0 +1,247 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2022 Meta Platforms, Inc. and affiliates. + */ + +#include +#include +#include +#include +#include + +DEFINE_BPF_STORAGE_CACHE(cgroup_cache); + +static DEFINE_PER_CPU(int, bpf_cgrp_storage_busy); + +static void bpf_cgrp_storage_lock(void) +{ + migrate_disable(); + this_cpu_inc(bpf_cgrp_storage_busy); +} + +static void bpf_cgrp_storage_unlock(void) +{ + this_cpu_dec(bpf_cgrp_storage_busy); + migrate_enable(); +} + +static bool bpf_cgrp_storage_trylock(void) +{ + migrate_disable(); + if (unlikely(this_cpu_inc_return(bpf_cgrp_storage_busy) != 1)) { + this_cpu_dec(bpf_cgrp_storage_busy); + migrate_enable(); + return false; + } + return true; +} + +static struct bpf_local_storage __rcu **cgroup_storage_ptr(void *owner) +{ + struct cgroup *cg = owner; + + return &cg->bpf_cgrp_storage; +} + +void bpf_cgrp_storage_free(struct cgroup *cgroup) +{ + struct bpf_local_storage *local_storage; + bool free_cgroup_storage = false; + unsigned long flags; + + rcu_read_lock(); + local_storage = rcu_dereference(cgroup->bpf_cgrp_storage); + if (!local_storage) { + rcu_read_unlock(); + return; + } + + bpf_cgrp_storage_lock(); + raw_spin_lock_irqsave(&local_storage->lock, flags); + free_cgroup_storage = bpf_local_storage_unlink_nolock(local_storage); + raw_spin_unlock_irqrestore(&local_storage->lock, flags); + bpf_cgrp_storage_unlock(); + rcu_read_unlock(); + + if (free_cgroup_storage) + kfree_rcu(local_storage, rcu); +} + +static struct bpf_local_storage_data * +cgroup_storage_lookup(struct cgroup *cgroup, struct bpf_map *map, bool cacheit_lockit) +{ + struct bpf_local_storage *cgroup_storage; + struct bpf_local_storage_map *smap; + + cgroup_storage = rcu_dereference_check(cgroup->bpf_cgrp_storage, + bpf_rcu_lock_held()); + if (!cgroup_storage) + return NULL; + + smap = (struct bpf_local_storage_map *)map; + return bpf_local_storage_lookup(cgroup_storage, smap, cacheit_lockit); +} + +static void *bpf_cgrp_storage_lookup_elem(struct bpf_map *map, void *key) +{ + struct bpf_local_storage_data *sdata; + struct cgroup *cgroup; + int fd; + + fd = *(int *)key; + cgroup = cgroup_get_from_fd(fd); + if (IS_ERR(cgroup)) + return ERR_CAST(cgroup); + + bpf_cgrp_storage_lock(); + sdata = cgroup_storage_lookup(cgroup, map, true); + bpf_cgrp_storage_unlock(); + cgroup_put(cgroup); + return sdata ? sdata->data : NULL; +} + +static int bpf_cgrp_storage_update_elem(struct bpf_map *map, void *key, + void *value, u64 map_flags) +{ + struct bpf_local_storage_data *sdata; + struct cgroup *cgroup; + int fd; + + fd = *(int *)key; + cgroup = cgroup_get_from_fd(fd); + if (IS_ERR(cgroup)) + return PTR_ERR(cgroup); + + bpf_cgrp_storage_lock(); + sdata = bpf_local_storage_update(cgroup, (struct bpf_local_storage_map *)map, + value, map_flags, GFP_ATOMIC); + bpf_cgrp_storage_unlock(); + cgroup_put(cgroup); + return PTR_ERR_OR_ZERO(sdata); +} + +static int cgroup_storage_delete(struct cgroup *cgroup, struct bpf_map *map) +{ + struct bpf_local_storage_data *sdata; + + sdata = cgroup_storage_lookup(cgroup, map, false); + if (!sdata) + return -ENOENT; + + bpf_selem_unlink(SELEM(sdata), true); + return 0; +} + +static int bpf_cgrp_storage_delete_elem(struct bpf_map *map, void *key) +{ + struct cgroup *cgroup; + int err, fd; + + fd = *(int *)key; + cgroup = cgroup_get_from_fd(fd); + if (IS_ERR(cgroup)) + return PTR_ERR(cgroup); + + bpf_cgrp_storage_lock(); + err = cgroup_storage_delete(cgroup, map); + bpf_cgrp_storage_unlock(); + cgroup_put(cgroup); + return err; +} + +static int notsupp_get_next_key(struct bpf_map *map, void *key, void *next_key) +{ + return -ENOTSUPP; +} + +static struct bpf_map *cgroup_storage_map_alloc(union bpf_attr *attr) +{ + return bpf_local_storage_map_alloc(attr, &cgroup_cache); +} + +static void cgroup_storage_map_free(struct bpf_map *map) +{ + bpf_local_storage_map_free(map, &cgroup_cache, NULL); +} + +/* *gfp_flags* is a hidden argument provided by the verifier */ +BPF_CALL_5(bpf_cgrp_storage_get, struct bpf_map *, map, struct cgroup *, cgroup, + void *, value, u64, flags, gfp_t, gfp_flags) +{ + struct bpf_local_storage_data *sdata; + + WARN_ON_ONCE(!bpf_rcu_lock_held()); + if (flags & ~(BPF_LOCAL_STORAGE_GET_F_CREATE)) + return (unsigned long)NULL; + + if (!cgroup) + return (unsigned long)NULL; + + if (!bpf_cgrp_storage_trylock()) + return (unsigned long)NULL; + + sdata = cgroup_storage_lookup(cgroup, map, true); + if (sdata) + goto unlock; + + /* only allocate new storage, when the cgroup is refcounted */ + if (!percpu_ref_is_dying(&cgroup->self.refcnt) && + (flags & BPF_LOCAL_STORAGE_GET_F_CREATE)) + sdata = bpf_local_storage_update(cgroup, (struct bpf_local_storage_map *)map, + value, BPF_NOEXIST, gfp_flags); + +unlock: + bpf_cgrp_storage_unlock(); + return IS_ERR_OR_NULL(sdata) ? (unsigned long)NULL : (unsigned long)sdata->data; +} + +BPF_CALL_2(bpf_cgrp_storage_delete, struct bpf_map *, map, struct cgroup *, cgroup) +{ + int ret; + + WARN_ON_ONCE(!bpf_rcu_lock_held()); + if (!cgroup) + return -EINVAL; + + if (!bpf_cgrp_storage_trylock()) + return -EBUSY; + + ret = cgroup_storage_delete(cgroup, map); + bpf_cgrp_storage_unlock(); + return ret; +} + +BTF_ID_LIST_SINGLE(cgroup_storage_map_btf_ids, struct, bpf_local_storage_map) +const struct bpf_map_ops cgrp_storage_map_ops = { + .map_meta_equal = bpf_map_meta_equal, + .map_alloc_check = bpf_local_storage_map_alloc_check, + .map_alloc = cgroup_storage_map_alloc, + .map_free = cgroup_storage_map_free, + .map_get_next_key = notsupp_get_next_key, + .map_lookup_elem = bpf_cgrp_storage_lookup_elem, + .map_update_elem = bpf_cgrp_storage_update_elem, + .map_delete_elem = bpf_cgrp_storage_delete_elem, + .map_check_btf = bpf_local_storage_map_check_btf, + .map_btf_id = &cgroup_storage_map_btf_ids[0], + .map_owner_storage_ptr = cgroup_storage_ptr, +}; + +const struct bpf_func_proto bpf_cgrp_storage_get_proto = { + .func = bpf_cgrp_storage_get, + .gpl_only = false, + .ret_type = RET_PTR_TO_MAP_VALUE_OR_NULL, + .arg1_type = ARG_CONST_MAP_PTR, + .arg2_type = ARG_PTR_TO_BTF_ID, + .arg2_btf_id = &bpf_cgroup_btf_id[0], + .arg3_type = ARG_PTR_TO_MAP_VALUE_OR_NULL, + .arg4_type = ARG_ANYTHING, +}; + +const struct bpf_func_proto bpf_cgrp_storage_delete_proto = { + .func = bpf_cgrp_storage_delete, + .gpl_only = false, + .ret_type = RET_INTEGER, + .arg1_type = ARG_CONST_MAP_PTR, + .arg2_type = ARG_PTR_TO_BTF_ID, + .arg2_btf_id = &bpf_cgroup_btf_id[0], +}; diff --git a/kernel/bpf/helpers.c b/kernel/bpf/helpers.c index a6b04faed282..124fd199ce5c 100644 --- a/kernel/bpf/helpers.c +++ b/kernel/bpf/helpers.c @@ -1663,6 +1663,12 @@ bpf_base_func_proto(enum bpf_func_id func_id) return &bpf_dynptr_write_proto; case BPF_FUNC_dynptr_data: return &bpf_dynptr_data_proto; +#ifdef CONFIG_CGROUPS + case BPF_FUNC_cgrp_storage_get: + return &bpf_cgrp_storage_get_proto; + case BPF_FUNC_cgrp_storage_delete: + return &bpf_cgrp_storage_delete_proto; +#endif default: break; } diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c index a0b4266196a8..11df90962101 100644 --- a/kernel/bpf/syscall.c +++ b/kernel/bpf/syscall.c @@ -1016,7 +1016,8 @@ static int map_check_btf(struct bpf_map *map, const struct btf *btf, map->map_type != BPF_MAP_TYPE_CGROUP_STORAGE && map->map_type != BPF_MAP_TYPE_SK_STORAGE && map->map_type != BPF_MAP_TYPE_INODE_STORAGE && - map->map_type != BPF_MAP_TYPE_TASK_STORAGE) + map->map_type != BPF_MAP_TYPE_TASK_STORAGE && + map->map_type != BPF_MAP_TYPE_CGRP_STORAGE) return -ENOTSUPP; if (map->spin_lock_off + sizeof(struct bpf_spin_lock) > map->value_size) { diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index ddc1452cf023..8f849a763b79 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -6350,6 +6350,11 @@ static int check_map_func_compatibility(struct bpf_verifier_env *env, func_id != BPF_FUNC_task_storage_delete) goto error; break; + case BPF_MAP_TYPE_CGRP_STORAGE: + if (func_id != BPF_FUNC_cgrp_storage_get && + func_id != BPF_FUNC_cgrp_storage_delete) + goto error; + break; case BPF_MAP_TYPE_BLOOM_FILTER: if (func_id != BPF_FUNC_map_peek_elem && func_id != BPF_FUNC_map_push_elem) @@ -6462,6 +6467,11 @@ static int check_map_func_compatibility(struct bpf_verifier_env *env, if (map->map_type != BPF_MAP_TYPE_TASK_STORAGE) goto error; break; + case BPF_FUNC_cgrp_storage_get: + case BPF_FUNC_cgrp_storage_delete: + if (map->map_type != BPF_MAP_TYPE_CGRP_STORAGE) + goto error; + break; default: break; } @@ -14139,7 +14149,8 @@ static int do_misc_fixups(struct bpf_verifier_env *env) if (insn->imm == BPF_FUNC_task_storage_get || insn->imm == BPF_FUNC_sk_storage_get || - insn->imm == BPF_FUNC_inode_storage_get) { + insn->imm == BPF_FUNC_inode_storage_get || + insn->imm == BPF_FUNC_cgrp_storage_get) { if (env->prog->aux->sleepable) insn_buf[0] = BPF_MOV64_IMM(BPF_REG_5, (__force __s32)GFP_KERNEL); else diff --git a/kernel/cgroup/cgroup.c b/kernel/cgroup/cgroup.c index 764bdd5fd8d1..12001928511b 100644 --- a/kernel/cgroup/cgroup.c +++ b/kernel/cgroup/cgroup.c @@ -5245,6 +5245,7 @@ static void css_free_rwork_fn(struct work_struct *work) atomic_dec(&cgrp->root->nr_cgrps); cgroup1_pidlist_destroy_all(cgrp); cancel_work_sync(&cgrp->release_agent_work); + bpf_cgrp_storage_free(cgrp); if (cgroup_parent(cgrp)) { /* diff --git a/kernel/trace/bpf_trace.c b/kernel/trace/bpf_trace.c index eed1bd952c3a..ce0228c72a93 100644 --- a/kernel/trace/bpf_trace.c +++ b/kernel/trace/bpf_trace.c @@ -1455,6 +1455,10 @@ bpf_tracing_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog) return &bpf_get_current_cgroup_id_proto; case BPF_FUNC_get_current_ancestor_cgroup_id: return &bpf_get_current_ancestor_cgroup_id_proto; + case BPF_FUNC_cgrp_storage_get: + return &bpf_cgrp_storage_get_proto; + case BPF_FUNC_cgrp_storage_delete: + return &bpf_cgrp_storage_delete_proto; #endif case BPF_FUNC_send_signal: return &bpf_send_signal_proto; diff --git a/scripts/bpf_doc.py b/scripts/bpf_doc.py index c0e6690be82a..fdb0aff8cb5a 100755 --- a/scripts/bpf_doc.py +++ b/scripts/bpf_doc.py @@ -685,6 +685,7 @@ class PrinterHelpers(Printer): 'struct udp6_sock', 'struct unix_sock', 'struct task_struct', + 'struct cgroup', 'struct __sk_buff', 'struct sk_msg_md', @@ -742,6 +743,7 @@ class PrinterHelpers(Printer): 'struct udp6_sock', 'struct unix_sock', 'struct task_struct', + 'struct cgroup', 'struct path', 'struct btf_ptr', 'struct inode', diff --git a/tools/include/uapi/linux/bpf.h b/tools/include/uapi/linux/bpf.h index 17f61338f8f8..94659f6b3395 100644 --- a/tools/include/uapi/linux/bpf.h +++ b/tools/include/uapi/linux/bpf.h @@ -922,7 +922,14 @@ enum bpf_map_type { BPF_MAP_TYPE_CPUMAP, BPF_MAP_TYPE_XSKMAP, BPF_MAP_TYPE_SOCKHASH, - BPF_MAP_TYPE_CGROUP_STORAGE, + BPF_MAP_TYPE_CGROUP_STORAGE_DEPRECATED, + /* BPF_MAP_TYPE_CGROUP_STORAGE is available to bpf programs attaching + * to a cgroup. The newer BPF_MAP_TYPE_CGRP_STORAGE is available to + * both cgroup-attached and other progs and supports all functionality + * provided by BPF_MAP_TYPE_CGROUP_STORAGE. So mark + * BPF_MAP_TYPE_CGROUP_STORAGE deprecated. + */ + BPF_MAP_TYPE_CGROUP_STORAGE = BPF_MAP_TYPE_CGROUP_STORAGE_DEPRECATED, BPF_MAP_TYPE_REUSEPORT_SOCKARRAY, BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE, BPF_MAP_TYPE_QUEUE, @@ -935,6 +942,7 @@ enum bpf_map_type { BPF_MAP_TYPE_TASK_STORAGE, BPF_MAP_TYPE_BLOOM_FILTER, BPF_MAP_TYPE_USER_RINGBUF, + BPF_MAP_TYPE_CGRP_STORAGE, }; /* Note that tracing related programs such as @@ -5435,6 +5443,44 @@ union bpf_attr { * **-E2BIG** if user-space has tried to publish a sample which is * larger than the size of the ring buffer, or which cannot fit * within a struct bpf_dynptr. + * + * void *bpf_cgrp_storage_get(struct bpf_map *map, struct cgroup *cgroup, void *value, u64 flags) + * Description + * Get a bpf_local_storage from the *cgroup*. + * + * Logically, it could be thought of as getting the value from + * a *map* with *cgroup* as the **key**. From this + * perspective, the usage is not much different from + * **bpf_map_lookup_elem**\ (*map*, **&**\ *cgroup*) except this + * helper enforces the key must be a cgroup struct and the map must also + * be a **BPF_MAP_TYPE_CGRP_STORAGE**. + * + * In reality, the local-storage value is embedded directly inside of the + * *cgroup* object itself, rather than being located in the + * **BPF_MAP_TYPE_CGRP_STORAGE** map. When the local-storage value is + * queried for some *map* on a *cgroup* object, the kernel will perform an + * O(n) iteration over all of the live local-storage values for that + * *cgroup* object until the local-storage value for the *map* is found. + * + * An optional *flags* (**BPF_LOCAL_STORAGE_GET_F_CREATE**) can be + * used such that a new bpf_local_storage will be + * created if one does not exist. *value* can be used + * together with **BPF_LOCAL_STORAGE_GET_F_CREATE** to specify + * the initial value of a bpf_local_storage. If *value* is + * **NULL**, the new bpf_local_storage will be zero initialized. + * Return + * A bpf_local_storage pointer is returned on success. + * + * **NULL** if not found or there was an error in adding + * a new bpf_local_storage. + * + * long bpf_cgrp_storage_delete(struct bpf_map *map, struct cgroup *cgroup) + * Description + * Delete a bpf_local_storage from a *cgroup*. + * Return + * 0 on success. + * + * **-ENOENT** if the bpf_local_storage cannot be found. */ #define ___BPF_FUNC_MAPPER(FN, ctx...) \ FN(unspec, 0, ##ctx) \ @@ -5647,6 +5693,8 @@ union bpf_attr { FN(tcp_raw_check_syncookie_ipv6, 207, ##ctx) \ FN(ktime_get_tai_ns, 208, ##ctx) \ FN(user_ringbuf_drain, 209, ##ctx) \ + FN(cgrp_storage_get, 210, ##ctx) \ + FN(cgrp_storage_delete, 211, ##ctx) \ /* */ /* backwards-compatibility macros for users of __BPF_FUNC_MAPPER that don't From 4fe64af23c12ae9f2c1f0ca0d556d9cb8f088dfb Mon Sep 17 00:00:00 2001 From: Yonghong Song Date: Tue, 25 Oct 2022 21:28:56 -0700 Subject: [PATCH 0372/2002] libbpf: Support new cgroup local storage Add support for new cgroup local storage. Acked-by: David Vernet Acked-by: Andrii Nakryiko Signed-off-by: Yonghong Song Link: https://lore.kernel.org/r/20221026042856.673989-1-yhs@fb.com Signed-off-by: Alexei Starovoitov --- tools/lib/bpf/libbpf.c | 1 + tools/lib/bpf/libbpf_probes.c | 1 + 2 files changed, 2 insertions(+) diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c index 027fd9565c16..5d7819edf074 100644 --- a/tools/lib/bpf/libbpf.c +++ b/tools/lib/bpf/libbpf.c @@ -164,6 +164,7 @@ static const char * const map_type_name[] = { [BPF_MAP_TYPE_TASK_STORAGE] = "task_storage", [BPF_MAP_TYPE_BLOOM_FILTER] = "bloom_filter", [BPF_MAP_TYPE_USER_RINGBUF] = "user_ringbuf", + [BPF_MAP_TYPE_CGRP_STORAGE] = "cgrp_storage", }; static const char * const prog_type_name[] = { diff --git a/tools/lib/bpf/libbpf_probes.c b/tools/lib/bpf/libbpf_probes.c index f3a8e8e74eb8..bdb83d467f9a 100644 --- a/tools/lib/bpf/libbpf_probes.c +++ b/tools/lib/bpf/libbpf_probes.c @@ -221,6 +221,7 @@ static int probe_map_create(enum bpf_map_type map_type) case BPF_MAP_TYPE_SK_STORAGE: case BPF_MAP_TYPE_INODE_STORAGE: case BPF_MAP_TYPE_TASK_STORAGE: + case BPF_MAP_TYPE_CGRP_STORAGE: btf_key_type_id = 1; btf_value_type_id = 3; value_size = 8; From f7f0f1657d9552e8667bc4758cbf41909bcdf7e2 Mon Sep 17 00:00:00 2001 From: Yonghong Song Date: Tue, 25 Oct 2022 21:29:01 -0700 Subject: [PATCH 0373/2002] bpftool: Support new cgroup local storage Add support for new cgroup local storage Acked-by: Quentin Monnet Signed-off-by: Yonghong Song Link: https://lore.kernel.org/r/20221026042901.674177-1-yhs@fb.com Signed-off-by: Alexei Starovoitov --- tools/bpf/bpftool/Documentation/bpftool-map.rst | 2 +- tools/bpf/bpftool/map.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/bpf/bpftool/Documentation/bpftool-map.rst b/tools/bpf/bpftool/Documentation/bpftool-map.rst index 7f3b67a8b48f..11250c4734fe 100644 --- a/tools/bpf/bpftool/Documentation/bpftool-map.rst +++ b/tools/bpf/bpftool/Documentation/bpftool-map.rst @@ -55,7 +55,7 @@ MAP COMMANDS | | **devmap** | **devmap_hash** | **sockmap** | **cpumap** | **xskmap** | **sockhash** | | **cgroup_storage** | **reuseport_sockarray** | **percpu_cgroup_storage** | | **queue** | **stack** | **sk_storage** | **struct_ops** | **ringbuf** | **inode_storage** -| | **task_storage** | **bloom_filter** | **user_ringbuf** } +| | **task_storage** | **bloom_filter** | **user_ringbuf** | **cgrp_storage** } DESCRIPTION =========== diff --git a/tools/bpf/bpftool/map.c b/tools/bpf/bpftool/map.c index 3087ced658ad..f941ac5c7b73 100644 --- a/tools/bpf/bpftool/map.c +++ b/tools/bpf/bpftool/map.c @@ -1458,7 +1458,7 @@ static int do_help(int argc, char **argv) " devmap | devmap_hash | sockmap | cpumap | xskmap | sockhash |\n" " cgroup_storage | reuseport_sockarray | percpu_cgroup_storage |\n" " queue | stack | sk_storage | struct_ops | ringbuf | inode_storage |\n" - " task_storage | bloom_filter | user_ringbuf }\n" + " task_storage | bloom_filter | user_ringbuf | cgrp_storage }\n" " " HELP_SPEC_OPTIONS " |\n" " {-f|--bpffs} | {-n|--nomount} }\n" "", From fd4ca6c1facfdc370d5f0b798106d07433e33aec Mon Sep 17 00:00:00 2001 From: Yonghong Song Date: Tue, 25 Oct 2022 21:29:06 -0700 Subject: [PATCH 0374/2002] selftests/bpf: Fix test test_libbpf_str/bpf_map_type_str Previous bpf patch made a change to uapi bpf.h like @@ -922,7 +922,14 @@ enum bpf_map_type { BPF_MAP_TYPE_SOCKHASH, - BPF_MAP_TYPE_CGROUP_STORAGE, + BPF_MAP_TYPE_CGROUP_STORAGE_DEPRECATED, + BPF_MAP_TYPE_CGROUP_STORAGE = BPF_MAP_TYPE_CGROUP_STORAGE_DEPRECATED, BPF_MAP_TYPE_REUSEPORT_SOCKARRAY, where BPF_MAP_TYPE_CGROUP_STORAGE_DEPRECATED and BPF_MAP_TYPE_CGROUP_STORAGE have the same enum value. This will cause selftest test_libbpf_str/bpf_map_type_str failing. This patch fixed the issue by avoid the check for BPF_MAP_TYPE_CGROUP_STORAGE_DEPRECATED in the test. Signed-off-by: Yonghong Song Link: https://lore.kernel.org/r/20221026042906.674830-1-yhs@fb.com Signed-off-by: Alexei Starovoitov --- tools/testing/selftests/bpf/prog_tests/libbpf_str.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tools/testing/selftests/bpf/prog_tests/libbpf_str.c b/tools/testing/selftests/bpf/prog_tests/libbpf_str.c index 93e9cddaadcf..efb8bd43653c 100644 --- a/tools/testing/selftests/bpf/prog_tests/libbpf_str.c +++ b/tools/testing/selftests/bpf/prog_tests/libbpf_str.c @@ -139,6 +139,14 @@ static void test_libbpf_bpf_map_type_str(void) snprintf(buf, sizeof(buf), "BPF_MAP_TYPE_%s", map_type_str); uppercase(buf); + /* Special case for map_type_name BPF_MAP_TYPE_CGROUP_STORAGE_DEPRECATED + * where it and BPF_MAP_TYPE_CGROUP_STORAGE have the same enum value + * (map_type). For this enum value, libbpf_bpf_map_type_str() picks + * BPF_MAP_TYPE_CGROUP_STORAGE. + */ + if (strcmp(map_type_name, "BPF_MAP_TYPE_CGROUP_STORAGE_DEPRECATED") == 0) + continue; + ASSERT_STREQ(buf, map_type_name, "exp_str_value"); } From 12bb6ca4e2fa14ecb007228160d1e5f8c5b92d01 Mon Sep 17 00:00:00 2001 From: Yonghong Song Date: Tue, 25 Oct 2022 21:29:11 -0700 Subject: [PATCH 0375/2002] selftests/bpf: Add selftests for new cgroup local storage Add four tests for new cgroup local storage, (1) testing bpf program helpers and user space map APIs, (2) testing recursive fentry triggering won't deadlock, (3) testing progs attached to cgroups, and (4) a negative test if the bpf_cgrp_storage_get() helper key is not a cgroup btf id. Signed-off-by: Yonghong Song Link: https://lore.kernel.org/r/20221026042911.675546-1-yhs@fb.com Signed-off-by: Alexei Starovoitov --- .../bpf/prog_tests/cgrp_local_storage.c | 171 ++++++++++++++++++ .../bpf/progs/cgrp_ls_attach_cgroup.c | 101 +++++++++++ .../selftests/bpf/progs/cgrp_ls_negative.c | 26 +++ .../selftests/bpf/progs/cgrp_ls_recursion.c | 70 +++++++ .../selftests/bpf/progs/cgrp_ls_tp_btf.c | 88 +++++++++ 5 files changed, 456 insertions(+) create mode 100644 tools/testing/selftests/bpf/prog_tests/cgrp_local_storage.c create mode 100644 tools/testing/selftests/bpf/progs/cgrp_ls_attach_cgroup.c create mode 100644 tools/testing/selftests/bpf/progs/cgrp_ls_negative.c create mode 100644 tools/testing/selftests/bpf/progs/cgrp_ls_recursion.c create mode 100644 tools/testing/selftests/bpf/progs/cgrp_ls_tp_btf.c diff --git a/tools/testing/selftests/bpf/prog_tests/cgrp_local_storage.c b/tools/testing/selftests/bpf/prog_tests/cgrp_local_storage.c new file mode 100644 index 000000000000..1c30412ba132 --- /dev/null +++ b/tools/testing/selftests/bpf/prog_tests/cgrp_local_storage.c @@ -0,0 +1,171 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2022 Meta Platforms, Inc. and affiliates.*/ + +#define _GNU_SOURCE +#include +#include +#include +#include +#include "cgrp_ls_tp_btf.skel.h" +#include "cgrp_ls_recursion.skel.h" +#include "cgrp_ls_attach_cgroup.skel.h" +#include "cgrp_ls_negative.skel.h" +#include "network_helpers.h" + +struct socket_cookie { + __u64 cookie_key; + __u32 cookie_value; +}; + +static void test_tp_btf(int cgroup_fd) +{ + struct cgrp_ls_tp_btf *skel; + long val1 = 1, val2 = 0; + int err; + + skel = cgrp_ls_tp_btf__open_and_load(); + if (!ASSERT_OK_PTR(skel, "skel_open_and_load")) + return; + + /* populate a value in map_b */ + err = bpf_map_update_elem(bpf_map__fd(skel->maps.map_b), &cgroup_fd, &val1, BPF_ANY); + if (!ASSERT_OK(err, "map_update_elem")) + goto out; + + /* check value */ + err = bpf_map_lookup_elem(bpf_map__fd(skel->maps.map_b), &cgroup_fd, &val2); + if (!ASSERT_OK(err, "map_lookup_elem")) + goto out; + if (!ASSERT_EQ(val2, 1, "map_lookup_elem, invalid val")) + goto out; + + /* delete value */ + err = bpf_map_delete_elem(bpf_map__fd(skel->maps.map_b), &cgroup_fd); + if (!ASSERT_OK(err, "map_delete_elem")) + goto out; + + skel->bss->target_pid = syscall(SYS_gettid); + + err = cgrp_ls_tp_btf__attach(skel); + if (!ASSERT_OK(err, "skel_attach")) + goto out; + + syscall(SYS_gettid); + syscall(SYS_gettid); + + skel->bss->target_pid = 0; + + /* 3x syscalls: 1x attach and 2x gettid */ + ASSERT_EQ(skel->bss->enter_cnt, 3, "enter_cnt"); + ASSERT_EQ(skel->bss->exit_cnt, 3, "exit_cnt"); + ASSERT_EQ(skel->bss->mismatch_cnt, 0, "mismatch_cnt"); +out: + cgrp_ls_tp_btf__destroy(skel); +} + +static void test_attach_cgroup(int cgroup_fd) +{ + int server_fd = 0, client_fd = 0, err = 0; + socklen_t addr_len = sizeof(struct sockaddr_in6); + struct cgrp_ls_attach_cgroup *skel; + __u32 cookie_expected_value; + struct sockaddr_in6 addr; + struct socket_cookie val; + + skel = cgrp_ls_attach_cgroup__open_and_load(); + if (!ASSERT_OK_PTR(skel, "skel_open")) + return; + + skel->links.set_cookie = bpf_program__attach_cgroup( + skel->progs.set_cookie, cgroup_fd); + if (!ASSERT_OK_PTR(skel->links.set_cookie, "prog_attach")) + goto out; + + skel->links.update_cookie_sockops = bpf_program__attach_cgroup( + skel->progs.update_cookie_sockops, cgroup_fd); + if (!ASSERT_OK_PTR(skel->links.update_cookie_sockops, "prog_attach")) + goto out; + + skel->links.update_cookie_tracing = bpf_program__attach( + skel->progs.update_cookie_tracing); + if (!ASSERT_OK_PTR(skel->links.update_cookie_tracing, "prog_attach")) + goto out; + + server_fd = start_server(AF_INET6, SOCK_STREAM, "::1", 0, 0); + if (!ASSERT_GE(server_fd, 0, "start_server")) + goto out; + + client_fd = connect_to_fd(server_fd, 0); + if (!ASSERT_GE(client_fd, 0, "connect_to_fd")) + goto close_server_fd; + + err = bpf_map_lookup_elem(bpf_map__fd(skel->maps.socket_cookies), + &cgroup_fd, &val); + if (!ASSERT_OK(err, "map_lookup(socket_cookies)")) + goto close_client_fd; + + err = getsockname(client_fd, (struct sockaddr *)&addr, &addr_len); + if (!ASSERT_OK(err, "getsockname")) + goto close_client_fd; + + cookie_expected_value = (ntohs(addr.sin6_port) << 8) | 0xFF; + ASSERT_EQ(val.cookie_value, cookie_expected_value, "cookie_value"); + +close_client_fd: + close(client_fd); +close_server_fd: + close(server_fd); +out: + cgrp_ls_attach_cgroup__destroy(skel); +} + +static void test_recursion(int cgroup_fd) +{ + struct cgrp_ls_recursion *skel; + int err; + + skel = cgrp_ls_recursion__open_and_load(); + if (!ASSERT_OK_PTR(skel, "skel_open_and_load")) + return; + + err = cgrp_ls_recursion__attach(skel); + if (!ASSERT_OK(err, "skel_attach")) + goto out; + + /* trigger sys_enter, make sure it does not cause deadlock */ + syscall(SYS_gettid); + +out: + cgrp_ls_recursion__destroy(skel); +} + +static void test_negative(void) +{ + struct cgrp_ls_negative *skel; + + skel = cgrp_ls_negative__open_and_load(); + if (!ASSERT_ERR_PTR(skel, "skel_open_and_load")) { + cgrp_ls_negative__destroy(skel); + return; + } +} + +void test_cgrp_local_storage(void) +{ + int cgroup_fd; + + cgroup_fd = test__join_cgroup("/cgrp_local_storage"); + if (!ASSERT_GE(cgroup_fd, 0, "join_cgroup /cgrp_local_storage")) + return; + + if (test__start_subtest("tp_btf")) + test_tp_btf(cgroup_fd); + if (test__start_subtest("attach_cgroup")) + test_attach_cgroup(cgroup_fd); + if (test__start_subtest("recursion")) + test_recursion(cgroup_fd); + if (test__start_subtest("negative")) + test_negative(); + + close(cgroup_fd); +} diff --git a/tools/testing/selftests/bpf/progs/cgrp_ls_attach_cgroup.c b/tools/testing/selftests/bpf/progs/cgrp_ls_attach_cgroup.c new file mode 100644 index 000000000000..6652d18465b2 --- /dev/null +++ b/tools/testing/selftests/bpf/progs/cgrp_ls_attach_cgroup.c @@ -0,0 +1,101 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2022 Meta Platforms, Inc. and affiliates. */ + +#include "vmlinux.h" +#include +#include +#include "bpf_tracing_net.h" + +char _license[] SEC("license") = "GPL"; + +struct socket_cookie { + __u64 cookie_key; + __u64 cookie_value; +}; + +struct { + __uint(type, BPF_MAP_TYPE_CGRP_STORAGE); + __uint(map_flags, BPF_F_NO_PREALLOC); + __type(key, int); + __type(value, struct socket_cookie); +} socket_cookies SEC(".maps"); + +SEC("cgroup/connect6") +int set_cookie(struct bpf_sock_addr *ctx) +{ + struct socket_cookie *p; + struct tcp_sock *tcp_sk; + struct bpf_sock *sk; + + if (ctx->family != AF_INET6 || ctx->user_family != AF_INET6) + return 1; + + sk = ctx->sk; + if (!sk) + return 1; + + tcp_sk = bpf_skc_to_tcp_sock(sk); + if (!tcp_sk) + return 1; + + p = bpf_cgrp_storage_get(&socket_cookies, + tcp_sk->inet_conn.icsk_inet.sk.sk_cgrp_data.cgroup, 0, + BPF_LOCAL_STORAGE_GET_F_CREATE); + if (!p) + return 1; + + p->cookie_value = 0xF; + p->cookie_key = bpf_get_socket_cookie(ctx); + return 1; +} + +SEC("sockops") +int update_cookie_sockops(struct bpf_sock_ops *ctx) +{ + struct socket_cookie *p; + struct tcp_sock *tcp_sk; + struct bpf_sock *sk; + + if (ctx->family != AF_INET6 || ctx->op != BPF_SOCK_OPS_TCP_CONNECT_CB) + return 1; + + sk = ctx->sk; + if (!sk) + return 1; + + tcp_sk = bpf_skc_to_tcp_sock(sk); + if (!tcp_sk) + return 1; + + p = bpf_cgrp_storage_get(&socket_cookies, + tcp_sk->inet_conn.icsk_inet.sk.sk_cgrp_data.cgroup, 0, 0); + if (!p) + return 1; + + if (p->cookie_key != bpf_get_socket_cookie(ctx)) + return 1; + + p->cookie_value |= (ctx->local_port << 8); + return 1; +} + +SEC("fexit/inet_stream_connect") +int BPF_PROG(update_cookie_tracing, struct socket *sock, + struct sockaddr *uaddr, int addr_len, int flags) +{ + struct socket_cookie *p; + struct tcp_sock *tcp_sk; + + if (uaddr->sa_family != AF_INET6) + return 0; + + p = bpf_cgrp_storage_get(&socket_cookies, sock->sk->sk_cgrp_data.cgroup, 0, 0); + if (!p) + return 0; + + if (p->cookie_key != bpf_get_socket_cookie(sock->sk)) + return 0; + + p->cookie_value |= 0xF0; + return 0; +} diff --git a/tools/testing/selftests/bpf/progs/cgrp_ls_negative.c b/tools/testing/selftests/bpf/progs/cgrp_ls_negative.c new file mode 100644 index 000000000000..d41f90e2ab64 --- /dev/null +++ b/tools/testing/selftests/bpf/progs/cgrp_ls_negative.c @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2022 Meta Platforms, Inc. and affiliates. */ + +#include "vmlinux.h" +#include +#include + +char _license[] SEC("license") = "GPL"; + +struct { + __uint(type, BPF_MAP_TYPE_CGRP_STORAGE); + __uint(map_flags, BPF_F_NO_PREALLOC); + __type(key, int); + __type(value, long); +} map_a SEC(".maps"); + +SEC("tp_btf/sys_enter") +int BPF_PROG(on_enter, struct pt_regs *regs, long id) +{ + struct task_struct *task; + + task = bpf_get_current_task_btf(); + (void)bpf_cgrp_storage_get(&map_a, (struct cgroup *)task, 0, + BPF_LOCAL_STORAGE_GET_F_CREATE); + return 0; +} diff --git a/tools/testing/selftests/bpf/progs/cgrp_ls_recursion.c b/tools/testing/selftests/bpf/progs/cgrp_ls_recursion.c new file mode 100644 index 000000000000..a043d8fefdac --- /dev/null +++ b/tools/testing/selftests/bpf/progs/cgrp_ls_recursion.c @@ -0,0 +1,70 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2022 Meta Platforms, Inc. and affiliates. */ + +#include "vmlinux.h" +#include +#include + +char _license[] SEC("license") = "GPL"; + +struct { + __uint(type, BPF_MAP_TYPE_CGRP_STORAGE); + __uint(map_flags, BPF_F_NO_PREALLOC); + __type(key, int); + __type(value, long); +} map_a SEC(".maps"); + +struct { + __uint(type, BPF_MAP_TYPE_CGRP_STORAGE); + __uint(map_flags, BPF_F_NO_PREALLOC); + __type(key, int); + __type(value, long); +} map_b SEC(".maps"); + +SEC("fentry/bpf_local_storage_lookup") +int BPF_PROG(on_lookup) +{ + struct task_struct *task = bpf_get_current_task_btf(); + + bpf_cgrp_storage_delete(&map_a, task->cgroups->dfl_cgrp); + bpf_cgrp_storage_delete(&map_b, task->cgroups->dfl_cgrp); + return 0; +} + +SEC("fentry/bpf_local_storage_update") +int BPF_PROG(on_update) +{ + struct task_struct *task = bpf_get_current_task_btf(); + long *ptr; + + ptr = bpf_cgrp_storage_get(&map_a, task->cgroups->dfl_cgrp, 0, + BPF_LOCAL_STORAGE_GET_F_CREATE); + if (ptr) + *ptr += 1; + + ptr = bpf_cgrp_storage_get(&map_b, task->cgroups->dfl_cgrp, 0, + BPF_LOCAL_STORAGE_GET_F_CREATE); + if (ptr) + *ptr += 1; + + return 0; +} + +SEC("tp_btf/sys_enter") +int BPF_PROG(on_enter, struct pt_regs *regs, long id) +{ + struct task_struct *task; + long *ptr; + + task = bpf_get_current_task_btf(); + ptr = bpf_cgrp_storage_get(&map_a, task->cgroups->dfl_cgrp, 0, + BPF_LOCAL_STORAGE_GET_F_CREATE); + if (ptr) + *ptr = 200; + + ptr = bpf_cgrp_storage_get(&map_b, task->cgroups->dfl_cgrp, 0, + BPF_LOCAL_STORAGE_GET_F_CREATE); + if (ptr) + *ptr = 100; + return 0; +} diff --git a/tools/testing/selftests/bpf/progs/cgrp_ls_tp_btf.c b/tools/testing/selftests/bpf/progs/cgrp_ls_tp_btf.c new file mode 100644 index 000000000000..9ebb8e2fe541 --- /dev/null +++ b/tools/testing/selftests/bpf/progs/cgrp_ls_tp_btf.c @@ -0,0 +1,88 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2022 Meta Platforms, Inc. and affiliates. */ + +#include "vmlinux.h" +#include +#include + +char _license[] SEC("license") = "GPL"; + +struct { + __uint(type, BPF_MAP_TYPE_CGRP_STORAGE); + __uint(map_flags, BPF_F_NO_PREALLOC); + __type(key, int); + __type(value, long); +} map_a SEC(".maps"); + +struct { + __uint(type, BPF_MAP_TYPE_CGRP_STORAGE); + __uint(map_flags, BPF_F_NO_PREALLOC); + __type(key, int); + __type(value, long); +} map_b SEC(".maps"); + +#define MAGIC_VALUE 0xabcd1234 + +pid_t target_pid = 0; +int mismatch_cnt = 0; +int enter_cnt = 0; +int exit_cnt = 0; + +SEC("tp_btf/sys_enter") +int BPF_PROG(on_enter, struct pt_regs *regs, long id) +{ + struct task_struct *task; + long *ptr; + int err; + + task = bpf_get_current_task_btf(); + if (task->pid != target_pid) + return 0; + + /* populate value 0 */ + ptr = bpf_cgrp_storage_get(&map_a, task->cgroups->dfl_cgrp, 0, + BPF_LOCAL_STORAGE_GET_F_CREATE); + if (!ptr) + return 0; + + /* delete value 0 */ + err = bpf_cgrp_storage_delete(&map_a, task->cgroups->dfl_cgrp); + if (err) + return 0; + + /* value is not available */ + ptr = bpf_cgrp_storage_get(&map_a, task->cgroups->dfl_cgrp, 0, 0); + if (ptr) + return 0; + + /* re-populate the value */ + ptr = bpf_cgrp_storage_get(&map_a, task->cgroups->dfl_cgrp, 0, + BPF_LOCAL_STORAGE_GET_F_CREATE); + if (!ptr) + return 0; + __sync_fetch_and_add(&enter_cnt, 1); + *ptr = MAGIC_VALUE + enter_cnt; + + return 0; +} + +SEC("tp_btf/sys_exit") +int BPF_PROG(on_exit, struct pt_regs *regs, long id) +{ + struct task_struct *task; + long *ptr; + + task = bpf_get_current_task_btf(); + if (task->pid != target_pid) + return 0; + + ptr = bpf_cgrp_storage_get(&map_a, task->cgroups->dfl_cgrp, 0, + BPF_LOCAL_STORAGE_GET_F_CREATE); + if (!ptr) + return 0; + + __sync_fetch_and_add(&exit_cnt, 1); + if (*ptr != MAGIC_VALUE + exit_cnt) + __sync_fetch_and_add(&mismatch_cnt, 1); + return 0; +} From 0a1b69d1c736130dfe41d9c999d1be17b2794cda Mon Sep 17 00:00:00 2001 From: Yonghong Song Date: Tue, 25 Oct 2022 21:29:17 -0700 Subject: [PATCH 0376/2002] selftests/bpf: Add test cgrp_local_storage to DENYLIST.s390x Test cgrp_local_storage have some programs utilizing trampoline. Arch s390x does not support trampoline so add the test to the corresponding DENYLIST file. Signed-off-by: Yonghong Song Link: https://lore.kernel.org/r/20221026042917.675685-1-yhs@fb.com Signed-off-by: Alexei Starovoitov --- tools/testing/selftests/bpf/DENYLIST.s390x | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/testing/selftests/bpf/DENYLIST.s390x b/tools/testing/selftests/bpf/DENYLIST.s390x index 520f12229b98..be4e3d47ea3e 100644 --- a/tools/testing/selftests/bpf/DENYLIST.s390x +++ b/tools/testing/selftests/bpf/DENYLIST.s390x @@ -10,6 +10,7 @@ bpf_nf # JIT does not support calling kernel f bpf_tcp_ca # JIT does not support calling kernel function (kfunc) cb_refs # expected error message unexpected error: -524 (trampoline) cgroup_hierarchical_stats # JIT does not support calling kernel function (kfunc) +cgrp_local_storage # prog_attach unexpected error: -524 (trampoline) core_read_macros # unknown func bpf_probe_read#4 (overlapping) d_path # failed to auto-attach program 'prog_stat': -524 (trampoline) deny_namespace # failed to attach: ERROR: strerror_r(-524)=22 (trampoline) From d43198017ea39e5040a578d0dd622c7b0e939019 Mon Sep 17 00:00:00 2001 From: Yonghong Song Date: Tue, 25 Oct 2022 21:29:22 -0700 Subject: [PATCH 0377/2002] docs/bpf: Add documentation for new cgroup local storage Add some descriptions and examples for BPF_MAP_TYPE_CGRP_STORAGE. Also illustate the major difference between BPF_MAP_TYPE_CGRP_STORAGE and BPF_MAP_TYPE_CGROUP_STORAGE and recommend to use BPF_MAP_TYPE_CGRP_STORAGE instead of BPF_MAP_TYPE_CGROUP_STORAGE in the end. Acked-by: David Vernet Signed-off-by: Yonghong Song Link: https://lore.kernel.org/r/20221026042922.676383-1-yhs@fb.com Signed-off-by: Alexei Starovoitov --- Documentation/bpf/map_cgrp_storage.rst | 109 +++++++++++++++++++++++++ 1 file changed, 109 insertions(+) create mode 100644 Documentation/bpf/map_cgrp_storage.rst diff --git a/Documentation/bpf/map_cgrp_storage.rst b/Documentation/bpf/map_cgrp_storage.rst new file mode 100644 index 000000000000..5d3f603efffa --- /dev/null +++ b/Documentation/bpf/map_cgrp_storage.rst @@ -0,0 +1,109 @@ +.. SPDX-License-Identifier: GPL-2.0-only +.. Copyright (C) 2022 Meta Platforms, Inc. and affiliates. + +========================= +BPF_MAP_TYPE_CGRP_STORAGE +========================= + +The ``BPF_MAP_TYPE_CGRP_STORAGE`` map type represents a local fix-sized +storage for cgroups. It is only available with ``CONFIG_CGROUPS``. +The programs are made available by the same Kconfig. The +data for a particular cgroup can be retrieved by looking up the map +with that cgroup. + +This document describes the usage and semantics of the +``BPF_MAP_TYPE_CGRP_STORAGE`` map type. + +Usage +===== + +The map key must be ``sizeof(int)`` representing a cgroup fd. +To access the storage in a program, use ``bpf_cgrp_storage_get``:: + + void *bpf_cgrp_storage_get(struct bpf_map *map, struct cgroup *cgroup, void *value, u64 flags) + +``flags`` could be 0 or ``BPF_LOCAL_STORAGE_GET_F_CREATE`` which indicates that +a new local storage will be created if one does not exist. + +The local storage can be removed with ``bpf_cgrp_storage_delete``:: + + long bpf_cgrp_storage_delete(struct bpf_map *map, struct cgroup *cgroup) + +The map is available to all program types. + +Examples +======== + +A BPF program example with BPF_MAP_TYPE_CGRP_STORAGE:: + + #include + #include + #include + + struct { + __uint(type, BPF_MAP_TYPE_CGRP_STORAGE); + __uint(map_flags, BPF_F_NO_PREALLOC); + __type(key, int); + __type(value, long); + } cgrp_storage SEC(".maps"); + + SEC("tp_btf/sys_enter") + int BPF_PROG(on_enter, struct pt_regs *regs, long id) + { + struct task_struct *task = bpf_get_current_task_btf(); + long *ptr; + + ptr = bpf_cgrp_storage_get(&cgrp_storage, task->cgroups->dfl_cgrp, 0, + BPF_LOCAL_STORAGE_GET_F_CREATE); + if (ptr) + __sync_fetch_and_add(ptr, 1); + + return 0; + } + +Userspace accessing map declared above:: + + #include + #include + + __u32 map_lookup(struct bpf_map *map, int cgrp_fd) + { + __u32 *value; + value = bpf_map_lookup_elem(bpf_map__fd(map), &cgrp_fd); + if (value) + return *value; + return 0; + } + +Difference Between BPF_MAP_TYPE_CGRP_STORAGE and BPF_MAP_TYPE_CGROUP_STORAGE +============================================================================ + +The old cgroup storage map ``BPF_MAP_TYPE_CGROUP_STORAGE`` has been marked as +deprecated (renamed to ``BPF_MAP_TYPE_CGROUP_STORAGE_DEPRECATED``). The new +``BPF_MAP_TYPE_CGRP_STORAGE`` map should be used instead. The following +illusates the main difference between ``BPF_MAP_TYPE_CGRP_STORAGE`` and +``BPF_MAP_TYPE_CGROUP_STORAGE_DEPRECATED``. + +(1). ``BPF_MAP_TYPE_CGRP_STORAGE`` can be used by all program types while + ``BPF_MAP_TYPE_CGROUP_STORAGE_DEPRECATED`` is available only to cgroup program types + like BPF_CGROUP_INET_INGRESS or BPF_CGROUP_SOCK_OPS, etc. + +(2). ``BPF_MAP_TYPE_CGRP_STORAGE`` supports local storage for more than one + cgroup while ``BPF_MAP_TYPE_CGROUP_STORAGE_DEPRECATED`` only supports one cgroup + which is attached by a BPF program. + +(3). ``BPF_MAP_TYPE_CGROUP_STORAGE_DEPRECATED`` allocates local storage at attach time so + ``bpf_get_local_storage()`` always returns non-NULL local storage. + ``BPF_MAP_TYPE_CGRP_STORAGE`` allocates local storage at runtime so + it is possible that ``bpf_cgrp_storage_get()`` may return null local storage. + To avoid such null local storage issue, user space can do + ``bpf_map_update_elem()`` to pre-allocate local storage before a BPF program + is attached. + +(4). ``BPF_MAP_TYPE_CGRP_STORAGE`` supports deleting local storage by a BPF program + while ``BPF_MAP_TYPE_CGROUP_STORAGE_DEPRECATED`` only deletes storage during + prog detach time. + +So overall, ``BPF_MAP_TYPE_CGRP_STORAGE`` supports all ``BPF_MAP_TYPE_CGROUP_STORAGE_DEPRECATED`` +functionality and beyond. It is recommended to use ``BPF_MAP_TYPE_CGRP_STORAGE`` +instead of ``BPF_MAP_TYPE_CGROUP_STORAGE_DEPRECATED``. From 5ed88f81511ce695692f0510ab3ca17eee68eff6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20M=C3=BCller?= Date: Tue, 25 Oct 2022 23:15:46 +0000 Subject: [PATCH 0378/2002] selftests/bpf: Panic on hard/soft lockup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When running tests, we should probably accept any help we can get when it comes to detecting issues early or making them more debuggable. We have seen a few cases where a test_progs_noalu32 run, for example, encountered a soft lockup and stopped making progress. It was only interrupted once we hit the overall test timeout [0]. We can not and do not want to necessarily rely on test timeouts, because those rely on infrastructure provided by the environment we run in (and which is not present in tools/testing/selftests/bpf/vmtest.sh, for example). To that end, let's enable panics on soft as well as hard lockups to fail fast should we encounter one. That's happening in the configuration indented to be used for selftests (including when using vmtest.sh or when running in BPF CI). [0] https://github.com/kernel-patches/bpf/runs/7844499997 Signed-off-by: Daniel Müller Link: https://lore.kernel.org/r/20221025231546.811766-1-deso@posteo.net Signed-off-by: Alexei Starovoitov --- tools/testing/selftests/bpf/config | 2 ++ tools/testing/selftests/bpf/config.x86_64 | 1 - 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/tools/testing/selftests/bpf/config b/tools/testing/selftests/bpf/config index 9213565c0311..7a99a6728169 100644 --- a/tools/testing/selftests/bpf/config +++ b/tools/testing/selftests/bpf/config @@ -1,4 +1,6 @@ CONFIG_BLK_DEV_LOOP=y +CONFIG_BOOTPARAM_HARDLOCKUP_PANIC=y +CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC=y CONFIG_BPF=y CONFIG_BPF_EVENTS=y CONFIG_BPF_JIT=y diff --git a/tools/testing/selftests/bpf/config.x86_64 b/tools/testing/selftests/bpf/config.x86_64 index 21ce5ea4304e..dd97d61d325c 100644 --- a/tools/testing/selftests/bpf/config.x86_64 +++ b/tools/testing/selftests/bpf/config.x86_64 @@ -18,7 +18,6 @@ CONFIG_BLK_DEV_RAM=y CONFIG_BLK_DEV_RAM_SIZE=16384 CONFIG_BLK_DEV_THROTTLING=y CONFIG_BONDING=y -CONFIG_BOOTPARAM_HARDLOCKUP_PANIC=y CONFIG_BOOTTIME_TRACING=y CONFIG_BPF_JIT_ALWAYS_ON=y CONFIG_BPF_KPROBE_OVERRIDE=y From 982e2b7329fe5c5387453524f40eec706d2c60bf Mon Sep 17 00:00:00 2001 From: Stefan Schmidt Date: Wed, 26 Oct 2022 09:40:34 +0200 Subject: [PATCH 0379/2002] net: mac802154: Fixup function parameter name in docs The function parameter name was wrong in kdocs. net/mac802154/util.c:27: warning: Function parameter or member 'hw' not described in 'ieee802154_wake_queue' net/mac802154/util.c:27: warning: Excess function parameter 'local' description in 'ieee802154_wake_queue' net/mac802154/util.c:53: warning: Function parameter or member 'hw' not described in 'ieee802154_stop_queue' net/mac802154/util.c:53: warning: Excess function parameter 'local' description in 'ieee802154_stop_queue' Fixing name and description. Signed-off-by: Stefan Schmidt --- net/mac802154/util.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/mac802154/util.c b/net/mac802154/util.c index f08605f59b60..ebc9a8521765 100644 --- a/net/mac802154/util.c +++ b/net/mac802154/util.c @@ -15,7 +15,7 @@ const void *const mac802154_wpan_phy_privid = &mac802154_wpan_phy_privid; /** * ieee802154_wake_queue - wake ieee802154 queue - * @local: main mac object + * @hw: main hardware object * * Tranceivers usually have either one transmit framebuffer or one framebuffer * for both transmitting and receiving. Hence, the core currently only handles @@ -41,7 +41,7 @@ static void ieee802154_wake_queue(struct ieee802154_hw *hw) /** * ieee802154_stop_queue - stop ieee802154 queue - * @local: main mac object + * @hw: main hardware object * * Tranceivers usually have either one transmit framebuffer or one framebuffer * for both transmitting and receiving. Hence, the core currently only handles From 35364f5b41a4917fe94a3f393d149b63ec583297 Mon Sep 17 00:00:00 2001 From: Jimmy Assarsson Date: Mon, 10 Oct 2022 20:52:28 +0200 Subject: [PATCH 0380/2002] can: kvaser_usb: kvaser_usb_leaf: Get capabilities from device Use the CMD_GET_CAPABILITIES_REQ command to query the device for certain capabilities. We are only interested in LISTENONLY mode and wither the device reports CAN error counters. Fixes: 080f40a6fa28 ("can: kvaser_usb: Add support for Kvaser CAN/USB devices") Reported-by: Anssi Hannula Tested-by: Anssi Hannula Signed-off-by: Jimmy Assarsson Link: https://lore.kernel.org/all/20221010185237.319219-3-extja@kvaser.com Signed-off-by: Marc Kleine-Budde --- .../net/can/usb/kvaser_usb/kvaser_usb_leaf.c | 144 +++++++++++++++++- 1 file changed, 143 insertions(+), 1 deletion(-) diff --git a/drivers/net/can/usb/kvaser_usb/kvaser_usb_leaf.c b/drivers/net/can/usb/kvaser_usb/kvaser_usb_leaf.c index 50f2ac8319ff..c87b13dc1048 100644 --- a/drivers/net/can/usb/kvaser_usb/kvaser_usb_leaf.c +++ b/drivers/net/can/usb/kvaser_usb/kvaser_usb_leaf.c @@ -74,6 +74,8 @@ #define CMD_TX_ACKNOWLEDGE 50 #define CMD_CAN_ERROR_EVENT 51 #define CMD_FLUSH_QUEUE_REPLY 68 +#define CMD_GET_CAPABILITIES_REQ 95 +#define CMD_GET_CAPABILITIES_RESP 96 #define CMD_LEAF_LOG_MESSAGE 106 @@ -83,6 +85,8 @@ #define KVASER_USB_LEAF_SWOPTION_FREQ_32_MHZ_CLK BIT(5) #define KVASER_USB_LEAF_SWOPTION_FREQ_24_MHZ_CLK BIT(6) +#define KVASER_USB_LEAF_SWOPTION_EXT_CAP BIT(12) + /* error factors */ #define M16C_EF_ACKE BIT(0) #define M16C_EF_CRCE BIT(1) @@ -278,6 +282,28 @@ struct leaf_cmd_log_message { u8 data[8]; } __packed; +/* Sub commands for cap_req and cap_res */ +#define KVASER_USB_LEAF_CAP_CMD_LISTEN_MODE 0x02 +#define KVASER_USB_LEAF_CAP_CMD_ERR_REPORT 0x05 +struct kvaser_cmd_cap_req { + __le16 padding0; + __le16 cap_cmd; + __le16 padding1; + __le16 channel; +} __packed; + +/* Status codes for cap_res */ +#define KVASER_USB_LEAF_CAP_STAT_OK 0x00 +#define KVASER_USB_LEAF_CAP_STAT_NOT_IMPL 0x01 +#define KVASER_USB_LEAF_CAP_STAT_UNAVAIL 0x02 +struct kvaser_cmd_cap_res { + __le16 padding; + __le16 cap_cmd; + __le16 status; + __le32 mask; + __le32 value; +} __packed; + struct kvaser_cmd { u8 len; u8 id; @@ -295,6 +321,8 @@ struct kvaser_cmd { struct leaf_cmd_chip_state_event chip_state_event; struct leaf_cmd_error_event error_event; struct leaf_cmd_log_message log_message; + struct kvaser_cmd_cap_req cap_req; + struct kvaser_cmd_cap_res cap_res; } __packed leaf; union { @@ -324,6 +352,7 @@ static const u8 kvaser_usb_leaf_cmd_sizes_leaf[] = { [CMD_LEAF_LOG_MESSAGE] = kvaser_fsize(u.leaf.log_message), [CMD_CHIP_STATE_EVENT] = kvaser_fsize(u.leaf.chip_state_event), [CMD_CAN_ERROR_EVENT] = kvaser_fsize(u.leaf.error_event), + [CMD_GET_CAPABILITIES_RESP] = kvaser_fsize(u.leaf.cap_res), /* ignored events: */ [CMD_FLUSH_QUEUE_REPLY] = CMD_SIZE_ANY, }; @@ -606,6 +635,9 @@ static void kvaser_usb_leaf_get_software_info_leaf(struct kvaser_usb *dev, dev->fw_version = le32_to_cpu(softinfo->fw_version); dev->max_tx_urbs = le16_to_cpu(softinfo->max_outstanding_tx); + if (sw_options & KVASER_USB_LEAF_SWOPTION_EXT_CAP) + dev->card_data.capabilities |= KVASER_USB_CAP_EXT_CAP; + if (dev->driver_info->quirks & KVASER_USB_QUIRK_IGNORE_CLK_FREQ) { /* Firmware expects bittiming parameters calculated for 16MHz * clock, regardless of the actual clock @@ -693,6 +725,116 @@ static int kvaser_usb_leaf_get_card_info(struct kvaser_usb *dev) return 0; } +static int kvaser_usb_leaf_get_single_capability(struct kvaser_usb *dev, + u16 cap_cmd_req, u16 *status) +{ + struct kvaser_usb_dev_card_data *card_data = &dev->card_data; + struct kvaser_cmd *cmd; + u32 value = 0; + u32 mask = 0; + u16 cap_cmd_res; + int err; + int i; + + cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); + if (!cmd) + return -ENOMEM; + + cmd->id = CMD_GET_CAPABILITIES_REQ; + cmd->u.leaf.cap_req.cap_cmd = cpu_to_le16(cap_cmd_req); + cmd->len = CMD_HEADER_LEN + sizeof(struct kvaser_cmd_cap_req); + + err = kvaser_usb_send_cmd(dev, cmd, cmd->len); + if (err) + goto end; + + err = kvaser_usb_leaf_wait_cmd(dev, CMD_GET_CAPABILITIES_RESP, cmd); + if (err) + goto end; + + *status = le16_to_cpu(cmd->u.leaf.cap_res.status); + + if (*status != KVASER_USB_LEAF_CAP_STAT_OK) + goto end; + + cap_cmd_res = le16_to_cpu(cmd->u.leaf.cap_res.cap_cmd); + switch (cap_cmd_res) { + case KVASER_USB_LEAF_CAP_CMD_LISTEN_MODE: + case KVASER_USB_LEAF_CAP_CMD_ERR_REPORT: + value = le32_to_cpu(cmd->u.leaf.cap_res.value); + mask = le32_to_cpu(cmd->u.leaf.cap_res.mask); + break; + default: + dev_warn(&dev->intf->dev, "Unknown capability command %u\n", + cap_cmd_res); + break; + } + + for (i = 0; i < dev->nchannels; i++) { + if (BIT(i) & (value & mask)) { + switch (cap_cmd_res) { + case KVASER_USB_LEAF_CAP_CMD_LISTEN_MODE: + card_data->ctrlmode_supported |= + CAN_CTRLMODE_LISTENONLY; + break; + case KVASER_USB_LEAF_CAP_CMD_ERR_REPORT: + card_data->capabilities |= + KVASER_USB_CAP_BERR_CAP; + break; + } + } + } + +end: + kfree(cmd); + + return err; +} + +static int kvaser_usb_leaf_get_capabilities_leaf(struct kvaser_usb *dev) +{ + int err; + u16 status; + + if (!(dev->card_data.capabilities & KVASER_USB_CAP_EXT_CAP)) { + dev_info(&dev->intf->dev, + "No extended capability support. Upgrade device firmware.\n"); + return 0; + } + + err = kvaser_usb_leaf_get_single_capability(dev, + KVASER_USB_LEAF_CAP_CMD_LISTEN_MODE, + &status); + if (err) + return err; + if (status) + dev_info(&dev->intf->dev, + "KVASER_USB_LEAF_CAP_CMD_LISTEN_MODE failed %u\n", + status); + + err = kvaser_usb_leaf_get_single_capability(dev, + KVASER_USB_LEAF_CAP_CMD_ERR_REPORT, + &status); + if (err) + return err; + if (status) + dev_info(&dev->intf->dev, + "KVASER_USB_LEAF_CAP_CMD_ERR_REPORT failed %u\n", + status); + + return 0; +} + +static int kvaser_usb_leaf_get_capabilities(struct kvaser_usb *dev) +{ + int err = 0; + + if (dev->driver_info->family == KVASER_LEAF) + err = kvaser_usb_leaf_get_capabilities_leaf(dev); + + return err; +} + static void kvaser_usb_leaf_tx_acknowledge(const struct kvaser_usb *dev, const struct kvaser_cmd *cmd) { @@ -1486,7 +1628,7 @@ const struct kvaser_usb_dev_ops kvaser_usb_leaf_dev_ops = { .dev_get_software_info = kvaser_usb_leaf_get_software_info, .dev_get_software_details = NULL, .dev_get_card_info = kvaser_usb_leaf_get_card_info, - .dev_get_capabilities = NULL, + .dev_get_capabilities = kvaser_usb_leaf_get_capabilities, .dev_set_opt_mode = kvaser_usb_leaf_set_opt_mode, .dev_start_chip = kvaser_usb_leaf_start_chip, .dev_stop_chip = kvaser_usb_leaf_stop_chip, From 7ea56128dbf904a3359bcf9289cccdfa3c85c7e8 Mon Sep 17 00:00:00 2001 From: Jimmy Assarsson Date: Mon, 10 Oct 2022 20:52:29 +0200 Subject: [PATCH 0381/2002] can: kvaser_usb: kvaser_usb_leaf: Rename {leaf,usbcan}_cmd_error_event to {leaf,usbcan}_cmd_can_error_event Prepare for handling CMD_ERROR_EVENT. Rename struct {leaf,usbcan}_cmd_error_event to {leaf,usbcan}_cmd_can_error_event. Fixes: 080f40a6fa28 ("can: kvaser_usb: Add support for Kvaser CAN/USB devices") Reported-by: Anssi Hannula Tested-by: Anssi Hannula Signed-off-by: Jimmy Assarsson Link: https://lore.kernel.org/all/20221010185237.319219-4-extja@kvaser.com Signed-off-by: Marc Kleine-Budde --- .../net/can/usb/kvaser_usb/kvaser_usb_leaf.c | 38 +++++++++---------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/drivers/net/can/usb/kvaser_usb/kvaser_usb_leaf.c b/drivers/net/can/usb/kvaser_usb/kvaser_usb_leaf.c index c87b13dc1048..f34ca9a850aa 100644 --- a/drivers/net/can/usb/kvaser_usb/kvaser_usb_leaf.c +++ b/drivers/net/can/usb/kvaser_usb/kvaser_usb_leaf.c @@ -234,7 +234,7 @@ struct kvaser_cmd_tx_acknowledge_header { u8 tid; } __packed; -struct leaf_cmd_error_event { +struct leaf_cmd_can_error_event { u8 tid; u8 flags; __le16 time[3]; @@ -246,7 +246,7 @@ struct leaf_cmd_error_event { u8 error_factor; } __packed; -struct usbcan_cmd_error_event { +struct usbcan_cmd_can_error_event { u8 tid; u8 padding; u8 tx_errors_count_ch0; @@ -319,7 +319,7 @@ struct kvaser_cmd { struct leaf_cmd_softinfo softinfo; struct leaf_cmd_rx_can rx_can; struct leaf_cmd_chip_state_event chip_state_event; - struct leaf_cmd_error_event error_event; + struct leaf_cmd_can_error_event can_error_event; struct leaf_cmd_log_message log_message; struct kvaser_cmd_cap_req cap_req; struct kvaser_cmd_cap_res cap_res; @@ -329,7 +329,7 @@ struct kvaser_cmd { struct usbcan_cmd_softinfo softinfo; struct usbcan_cmd_rx_can rx_can; struct usbcan_cmd_chip_state_event chip_state_event; - struct usbcan_cmd_error_event error_event; + struct usbcan_cmd_can_error_event can_error_event; } __packed usbcan; struct kvaser_cmd_tx_can tx_can; @@ -351,7 +351,7 @@ static const u8 kvaser_usb_leaf_cmd_sizes_leaf[] = { [CMD_RX_EXT_MESSAGE] = kvaser_fsize(u.leaf.rx_can), [CMD_LEAF_LOG_MESSAGE] = kvaser_fsize(u.leaf.log_message), [CMD_CHIP_STATE_EVENT] = kvaser_fsize(u.leaf.chip_state_event), - [CMD_CAN_ERROR_EVENT] = kvaser_fsize(u.leaf.error_event), + [CMD_CAN_ERROR_EVENT] = kvaser_fsize(u.leaf.can_error_event), [CMD_GET_CAPABILITIES_RESP] = kvaser_fsize(u.leaf.cap_res), /* ignored events: */ [CMD_FLUSH_QUEUE_REPLY] = CMD_SIZE_ANY, @@ -366,7 +366,7 @@ static const u8 kvaser_usb_leaf_cmd_sizes_usbcan[] = { [CMD_RX_STD_MESSAGE] = kvaser_fsize(u.usbcan.rx_can), [CMD_RX_EXT_MESSAGE] = kvaser_fsize(u.usbcan.rx_can), [CMD_CHIP_STATE_EVENT] = kvaser_fsize(u.usbcan.chip_state_event), - [CMD_CAN_ERROR_EVENT] = kvaser_fsize(u.usbcan.error_event), + [CMD_CAN_ERROR_EVENT] = kvaser_fsize(u.usbcan.can_error_event), /* ignored events: */ [CMD_USBCAN_CLOCK_OVERFLOW_EVENT] = CMD_SIZE_ANY, }; @@ -1132,11 +1132,11 @@ static void kvaser_usb_leaf_usbcan_rx_error(const struct kvaser_usb *dev, case CMD_CAN_ERROR_EVENT: es.channel = 0; - es.status = cmd->u.usbcan.error_event.status_ch0; - es.txerr = cmd->u.usbcan.error_event.tx_errors_count_ch0; - es.rxerr = cmd->u.usbcan.error_event.rx_errors_count_ch0; + es.status = cmd->u.usbcan.can_error_event.status_ch0; + es.txerr = cmd->u.usbcan.can_error_event.tx_errors_count_ch0; + es.rxerr = cmd->u.usbcan.can_error_event.rx_errors_count_ch0; es.usbcan.other_ch_status = - cmd->u.usbcan.error_event.status_ch1; + cmd->u.usbcan.can_error_event.status_ch1; kvaser_usb_leaf_usbcan_conditionally_rx_error(dev, &es); /* The USBCAN firmware supports up to 2 channels. @@ -1144,13 +1144,13 @@ static void kvaser_usb_leaf_usbcan_rx_error(const struct kvaser_usb *dev, */ if (dev->nchannels == MAX_USBCAN_NET_DEVICES) { es.channel = 1; - es.status = cmd->u.usbcan.error_event.status_ch1; + es.status = cmd->u.usbcan.can_error_event.status_ch1; es.txerr = - cmd->u.usbcan.error_event.tx_errors_count_ch1; + cmd->u.usbcan.can_error_event.tx_errors_count_ch1; es.rxerr = - cmd->u.usbcan.error_event.rx_errors_count_ch1; + cmd->u.usbcan.can_error_event.rx_errors_count_ch1; es.usbcan.other_ch_status = - cmd->u.usbcan.error_event.status_ch0; + cmd->u.usbcan.can_error_event.status_ch0; kvaser_usb_leaf_usbcan_conditionally_rx_error(dev, &es); } break; @@ -1167,11 +1167,11 @@ static void kvaser_usb_leaf_leaf_rx_error(const struct kvaser_usb *dev, switch (cmd->id) { case CMD_CAN_ERROR_EVENT: - es.channel = cmd->u.leaf.error_event.channel; - es.status = cmd->u.leaf.error_event.status; - es.txerr = cmd->u.leaf.error_event.tx_errors_count; - es.rxerr = cmd->u.leaf.error_event.rx_errors_count; - es.leaf.error_factor = cmd->u.leaf.error_event.error_factor; + es.channel = cmd->u.leaf.can_error_event.channel; + es.status = cmd->u.leaf.can_error_event.status; + es.txerr = cmd->u.leaf.can_error_event.tx_errors_count; + es.rxerr = cmd->u.leaf.can_error_event.rx_errors_count; + es.leaf.error_factor = cmd->u.leaf.can_error_event.error_factor; break; case CMD_LEAF_LOG_MESSAGE: es.channel = cmd->u.leaf.log_message.channel; From b24cb2d169e0c9dce664a959e1f2aa9781285dc9 Mon Sep 17 00:00:00 2001 From: Jimmy Assarsson Date: Mon, 10 Oct 2022 20:52:30 +0200 Subject: [PATCH 0382/2002] can: kvaser_usb: kvaser_usb_leaf: Handle CMD_ERROR_EVENT The device will send an error event command, to indicate certain errors. This indicates a misbehaving driver, and should never occur. Fixes: 080f40a6fa28 ("can: kvaser_usb: Add support for Kvaser CAN/USB devices") Tested-by: Anssi Hannula Co-developed-by: Anssi Hannula Signed-off-by: Anssi Hannula Signed-off-by: Jimmy Assarsson Link: https://lore.kernel.org/all/20221010185237.319219-5-extja@kvaser.com Signed-off-by: Marc Kleine-Budde --- .../net/can/usb/kvaser_usb/kvaser_usb_leaf.c | 99 +++++++++++++++++++ 1 file changed, 99 insertions(+) diff --git a/drivers/net/can/usb/kvaser_usb/kvaser_usb_leaf.c b/drivers/net/can/usb/kvaser_usb/kvaser_usb_leaf.c index f34ca9a850aa..e391ec247f54 100644 --- a/drivers/net/can/usb/kvaser_usb/kvaser_usb_leaf.c +++ b/drivers/net/can/usb/kvaser_usb/kvaser_usb_leaf.c @@ -70,6 +70,7 @@ #define CMD_GET_CARD_INFO_REPLY 35 #define CMD_GET_SOFTWARE_INFO 38 #define CMD_GET_SOFTWARE_INFO_REPLY 39 +#define CMD_ERROR_EVENT 45 #define CMD_FLUSH_QUEUE 48 #define CMD_TX_ACKNOWLEDGE 50 #define CMD_CAN_ERROR_EVENT 51 @@ -258,6 +259,28 @@ struct usbcan_cmd_can_error_event { __le16 time; } __packed; +/* CMD_ERROR_EVENT error codes */ +#define KVASER_USB_LEAF_ERROR_EVENT_TX_QUEUE_FULL 0x8 +#define KVASER_USB_LEAF_ERROR_EVENT_PARAM 0x9 + +struct leaf_cmd_error_event { + u8 tid; + u8 error_code; + __le16 timestamp[3]; + __le16 padding; + __le16 info1; + __le16 info2; +} __packed; + +struct usbcan_cmd_error_event { + u8 tid; + u8 error_code; + __le16 info1; + __le16 info2; + __le16 timestamp; + __le16 padding; +} __packed; + struct kvaser_cmd_ctrl_mode { u8 tid; u8 channel; @@ -321,6 +344,7 @@ struct kvaser_cmd { struct leaf_cmd_chip_state_event chip_state_event; struct leaf_cmd_can_error_event can_error_event; struct leaf_cmd_log_message log_message; + struct leaf_cmd_error_event error_event; struct kvaser_cmd_cap_req cap_req; struct kvaser_cmd_cap_res cap_res; } __packed leaf; @@ -330,6 +354,7 @@ struct kvaser_cmd { struct usbcan_cmd_rx_can rx_can; struct usbcan_cmd_chip_state_event chip_state_event; struct usbcan_cmd_can_error_event can_error_event; + struct usbcan_cmd_error_event error_event; } __packed usbcan; struct kvaser_cmd_tx_can tx_can; @@ -353,6 +378,7 @@ static const u8 kvaser_usb_leaf_cmd_sizes_leaf[] = { [CMD_CHIP_STATE_EVENT] = kvaser_fsize(u.leaf.chip_state_event), [CMD_CAN_ERROR_EVENT] = kvaser_fsize(u.leaf.can_error_event), [CMD_GET_CAPABILITIES_RESP] = kvaser_fsize(u.leaf.cap_res), + [CMD_ERROR_EVENT] = kvaser_fsize(u.leaf.error_event), /* ignored events: */ [CMD_FLUSH_QUEUE_REPLY] = CMD_SIZE_ANY, }; @@ -367,6 +393,7 @@ static const u8 kvaser_usb_leaf_cmd_sizes_usbcan[] = { [CMD_RX_EXT_MESSAGE] = kvaser_fsize(u.usbcan.rx_can), [CMD_CHIP_STATE_EVENT] = kvaser_fsize(u.usbcan.chip_state_event), [CMD_CAN_ERROR_EVENT] = kvaser_fsize(u.usbcan.can_error_event), + [CMD_ERROR_EVENT] = kvaser_fsize(u.usbcan.error_event), /* ignored events: */ [CMD_USBCAN_CLOCK_OVERFLOW_EVENT] = CMD_SIZE_ANY, }; @@ -1304,6 +1331,74 @@ static void kvaser_usb_leaf_rx_can_msg(const struct kvaser_usb *dev, netif_rx(skb); } +static void kvaser_usb_leaf_error_event_parameter(const struct kvaser_usb *dev, + const struct kvaser_cmd *cmd) +{ + u16 info1 = 0; + + switch (dev->driver_info->family) { + case KVASER_LEAF: + info1 = le16_to_cpu(cmd->u.leaf.error_event.info1); + break; + case KVASER_USBCAN: + info1 = le16_to_cpu(cmd->u.usbcan.error_event.info1); + break; + } + + /* info1 will contain the offending cmd_no */ + switch (info1) { + case CMD_SET_CTRL_MODE: + dev_warn(&dev->intf->dev, + "CMD_SET_CTRL_MODE error in parameter\n"); + break; + + case CMD_SET_BUS_PARAMS: + dev_warn(&dev->intf->dev, + "CMD_SET_BUS_PARAMS error in parameter\n"); + break; + + default: + dev_warn(&dev->intf->dev, + "Unhandled parameter error event cmd_no (%u)\n", + info1); + break; + } +} + +static void kvaser_usb_leaf_error_event(const struct kvaser_usb *dev, + const struct kvaser_cmd *cmd) +{ + u8 error_code = 0; + + switch (dev->driver_info->family) { + case KVASER_LEAF: + error_code = cmd->u.leaf.error_event.error_code; + break; + case KVASER_USBCAN: + error_code = cmd->u.usbcan.error_event.error_code; + break; + } + + switch (error_code) { + case KVASER_USB_LEAF_ERROR_EVENT_TX_QUEUE_FULL: + /* Received additional CAN message, when firmware TX queue is + * already full. Something is wrong with the driver. + * This should never happen! + */ + dev_err(&dev->intf->dev, + "Received error event TX_QUEUE_FULL\n"); + break; + case KVASER_USB_LEAF_ERROR_EVENT_PARAM: + kvaser_usb_leaf_error_event_parameter(dev, cmd); + break; + + default: + dev_warn(&dev->intf->dev, + "Unhandled error event (%d)\n", error_code); + break; + } +} + static void kvaser_usb_leaf_start_chip_reply(const struct kvaser_usb *dev, const struct kvaser_cmd *cmd) { @@ -1382,6 +1477,10 @@ static void kvaser_usb_leaf_handle_command(const struct kvaser_usb *dev, kvaser_usb_leaf_tx_acknowledge(dev, cmd); break; + case CMD_ERROR_EVENT: + kvaser_usb_leaf_error_event(dev, cmd); + break; + /* Ignored commands */ case CMD_USBCAN_CLOCK_OVERFLOW_EVENT: if (dev->driver_info->family != KVASER_USBCAN) From df1b7af2761b935f63b4a53e789d41ed859edf61 Mon Sep 17 00:00:00 2001 From: Anssi Hannula Date: Mon, 10 Oct 2022 20:52:31 +0200 Subject: [PATCH 0383/2002] can: kvaser_usb_leaf: Set Warning state even without bus errors kvaser_usb_leaf_rx_error_update_can_state() sets error state according to error counters when the hardware does not indicate a specific state directly. However, this is currently gated behind a check for M16C_STATE_BUS_ERROR which does not always seem to be set when error counters are increasing, and may not be set when error counters are decreasing. This causes the CAN_STATE_ERROR_WARNING state to not be set in some cases even when appropriate. Change the code to set error state from counters even without M16C_STATE_BUS_ERROR. The Error-Passive case seems superfluous as it is already set via M16C_STATE_BUS_PASSIVE flag above, but it is kept for now. Tested with 0bfd:0124 Kvaser Mini PCI Express 2xHS FW 4.18.778. Fixes: 080f40a6fa28 ("can: kvaser_usb: Add support for Kvaser CAN/USB devices") Tested-by: Jimmy Assarsson Signed-off-by: Anssi Hannula Signed-off-by: Jimmy Assarsson Link: https://lore.kernel.org/all/20221010185237.319219-6-extja@kvaser.com Signed-off-by: Marc Kleine-Budde --- .../net/can/usb/kvaser_usb/kvaser_usb_leaf.c | 20 ++++++++----------- 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/drivers/net/can/usb/kvaser_usb/kvaser_usb_leaf.c b/drivers/net/can/usb/kvaser_usb/kvaser_usb_leaf.c index e391ec247f54..a9d565463559 100644 --- a/drivers/net/can/usb/kvaser_usb/kvaser_usb_leaf.c +++ b/drivers/net/can/usb/kvaser_usb/kvaser_usb_leaf.c @@ -961,20 +961,16 @@ kvaser_usb_leaf_rx_error_update_can_state(struct kvaser_usb_net_priv *priv, new_state = CAN_STATE_BUS_OFF; } else if (es->status & M16C_STATE_BUS_PASSIVE) { new_state = CAN_STATE_ERROR_PASSIVE; - } else if (es->status & M16C_STATE_BUS_ERROR) { + } else if ((es->status & M16C_STATE_BUS_ERROR) && + cur_state >= CAN_STATE_BUS_OFF) { /* Guard against spurious error events after a busoff */ - if (cur_state < CAN_STATE_BUS_OFF) { - if (es->txerr >= 128 || es->rxerr >= 128) - new_state = CAN_STATE_ERROR_PASSIVE; - else if (es->txerr >= 96 || es->rxerr >= 96) - new_state = CAN_STATE_ERROR_WARNING; - else if (cur_state > CAN_STATE_ERROR_ACTIVE) - new_state = CAN_STATE_ERROR_ACTIVE; - } - } - - if (!es->status) + } else if (es->txerr >= 128 || es->rxerr >= 128) { + new_state = CAN_STATE_ERROR_PASSIVE; + } else if (es->txerr >= 96 || es->rxerr >= 96) { + new_state = CAN_STATE_ERROR_WARNING; + } else { new_state = CAN_STATE_ERROR_ACTIVE; + } if (new_state != cur_state) { tx_state = (es->txerr >= es->rxerr) ? new_state : 0; From 8d21f5927ae604881f98587fabf6753f88730968 Mon Sep 17 00:00:00 2001 From: Anssi Hannula Date: Mon, 10 Oct 2022 20:52:32 +0200 Subject: [PATCH 0384/2002] can: kvaser_usb_leaf: Fix improved state not being reported The tested 0bfd:0017 Kvaser Memorator Professional HS/HS FW 2.0.50 and 0bfd:0124 Kvaser Mini PCI Express 2xHS FW 4.18.778 do not seem to send any unsolicited events when error counters decrease or when the device transitions from ERROR_PASSIVE to ERROR_ACTIVE (or WARNING). This causes the interface to e.g. indefinitely stay in the ERROR_PASSIVE state. Fix that by asking for chip state (inc. counters) event every 0.5 secs when error counters are non-zero. Since there are non-error-counter devices, also always poll in ERROR_PASSIVE even if the counters show zero. Fixes: 080f40a6fa28 ("can: kvaser_usb: Add support for Kvaser CAN/USB devices") Tested-by: Jimmy Assarsson Signed-off-by: Anssi Hannula Signed-off-by: Jimmy Assarsson Link: https://lore.kernel.org/all/20221010185237.319219-7-extja@kvaser.com Signed-off-by: Marc Kleine-Budde --- drivers/net/can/usb/kvaser_usb/kvaser_usb.h | 7 +++ .../net/can/usb/kvaser_usb/kvaser_usb_core.c | 19 +++++- .../net/can/usb/kvaser_usb/kvaser_usb_leaf.c | 58 +++++++++++++++++++ 3 files changed, 81 insertions(+), 3 deletions(-) diff --git a/drivers/net/can/usb/kvaser_usb/kvaser_usb.h b/drivers/net/can/usb/kvaser_usb/kvaser_usb.h index f6c0938027ec..d9c5dd5da908 100644 --- a/drivers/net/can/usb/kvaser_usb/kvaser_usb.h +++ b/drivers/net/can/usb/kvaser_usb/kvaser_usb.h @@ -104,6 +104,9 @@ struct kvaser_usb_net_priv { struct can_priv can; struct can_berr_counter bec; + /* subdriver-specific data */ + void *sub_priv; + struct kvaser_usb *dev; struct net_device *netdev; int channel; @@ -125,6 +128,8 @@ struct kvaser_usb_net_priv { * * @dev_setup_endpoints: setup USB in and out endpoints * @dev_init_card: initialize card + * @dev_init_channel: initialize channel + * @dev_remove_channel: uninitialize channel * @dev_get_software_info: get software info * @dev_get_software_details: get software details * @dev_get_card_info: get card info @@ -146,6 +151,8 @@ struct kvaser_usb_dev_ops { struct can_berr_counter *bec); int (*dev_setup_endpoints)(struct kvaser_usb *dev); int (*dev_init_card)(struct kvaser_usb *dev); + int (*dev_init_channel)(struct kvaser_usb_net_priv *priv); + void (*dev_remove_channel)(struct kvaser_usb_net_priv *priv); int (*dev_get_software_info)(struct kvaser_usb *dev); int (*dev_get_software_details)(struct kvaser_usb *dev); int (*dev_get_card_info)(struct kvaser_usb *dev); diff --git a/drivers/net/can/usb/kvaser_usb/kvaser_usb_core.c b/drivers/net/can/usb/kvaser_usb/kvaser_usb_core.c index e91648ed7386..19df05887166 100644 --- a/drivers/net/can/usb/kvaser_usb/kvaser_usb_core.c +++ b/drivers/net/can/usb/kvaser_usb/kvaser_usb_core.c @@ -684,6 +684,7 @@ static const struct ethtool_ops kvaser_usb_ethtool_ops_hwts = { static void kvaser_usb_remove_interfaces(struct kvaser_usb *dev) { + const struct kvaser_usb_dev_ops *ops = dev->driver_info->ops; int i; for (i = 0; i < dev->nchannels; i++) { @@ -699,6 +700,9 @@ static void kvaser_usb_remove_interfaces(struct kvaser_usb *dev) if (!dev->nets[i]) continue; + if (ops->dev_remove_channel) + ops->dev_remove_channel(dev->nets[i]); + free_candev(dev->nets[i]->netdev); } } @@ -772,17 +776,26 @@ static int kvaser_usb_init_one(struct kvaser_usb *dev, int channel) dev->nets[channel] = priv; + if (ops->dev_init_channel) { + err = ops->dev_init_channel(priv); + if (err) + goto err; + } + err = register_candev(netdev); if (err) { dev_err(&dev->intf->dev, "Failed to register CAN device\n"); - free_candev(netdev); - dev->nets[channel] = NULL; - return err; + goto err; } netdev_dbg(netdev, "device registered\n"); return 0; + +err: + free_candev(netdev); + dev->nets[channel] = NULL; + return err; } static int kvaser_usb_probe(struct usb_interface *intf, diff --git a/drivers/net/can/usb/kvaser_usb/kvaser_usb_leaf.c b/drivers/net/can/usb/kvaser_usb/kvaser_usb_leaf.c index a9d565463559..12f3d023b72b 100644 --- a/drivers/net/can/usb/kvaser_usb/kvaser_usb_leaf.c +++ b/drivers/net/can/usb/kvaser_usb/kvaser_usb_leaf.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include @@ -56,6 +57,7 @@ #define CMD_RX_EXT_MESSAGE 14 #define CMD_TX_EXT_MESSAGE 15 #define CMD_SET_BUS_PARAMS 16 +#define CMD_GET_CHIP_STATE 19 #define CMD_CHIP_STATE_EVENT 20 #define CMD_SET_CTRL_MODE 21 #define CMD_RESET_CHIP 24 @@ -421,6 +423,12 @@ struct kvaser_usb_err_summary { }; }; +struct kvaser_usb_net_leaf_priv { + struct kvaser_usb_net_priv *net; + + struct delayed_work chip_state_req_work; +}; + static const struct can_bittiming_const kvaser_usb_leaf_m16c_bittiming_const = { .name = "kvaser_usb_ucii", .tseg1_min = 4, @@ -943,6 +951,16 @@ static int kvaser_usb_leaf_simple_cmd_async(struct kvaser_usb_net_priv *priv, return err; } +static void kvaser_usb_leaf_chip_state_req_work(struct work_struct *work) +{ + struct kvaser_usb_net_leaf_priv *leaf = + container_of(work, struct kvaser_usb_net_leaf_priv, + chip_state_req_work.work); + struct kvaser_usb_net_priv *priv = leaf->net; + + kvaser_usb_leaf_simple_cmd_async(priv, CMD_GET_CHIP_STATE); +} + static void kvaser_usb_leaf_rx_error_update_can_state(struct kvaser_usb_net_priv *priv, const struct kvaser_usb_err_summary *es, @@ -1014,6 +1032,7 @@ static void kvaser_usb_leaf_rx_error(const struct kvaser_usb *dev, struct sk_buff *skb; struct net_device_stats *stats; struct kvaser_usb_net_priv *priv; + struct kvaser_usb_net_leaf_priv *leaf; enum can_state old_state, new_state; if (es->channel >= dev->nchannels) { @@ -1023,6 +1042,7 @@ static void kvaser_usb_leaf_rx_error(const struct kvaser_usb *dev, } priv = dev->nets[es->channel]; + leaf = priv->sub_priv; stats = &priv->netdev->stats; /* Update all of the CAN interface's state and error counters before @@ -1039,6 +1059,14 @@ static void kvaser_usb_leaf_rx_error(const struct kvaser_usb *dev, kvaser_usb_leaf_rx_error_update_can_state(priv, es, &tmp_cf); new_state = priv->can.state; + /* If there are errors, request status updates periodically as we do + * not get automatic notifications of improved state. + */ + if (new_state < CAN_STATE_BUS_OFF && + (es->rxerr || es->txerr || new_state == CAN_STATE_ERROR_PASSIVE)) + schedule_delayed_work(&leaf->chip_state_req_work, + msecs_to_jiffies(500)); + skb = alloc_can_err_skb(priv->netdev, &cf); if (!skb) { stats->rx_dropped++; @@ -1573,10 +1601,13 @@ static int kvaser_usb_leaf_start_chip(struct kvaser_usb_net_priv *priv) static int kvaser_usb_leaf_stop_chip(struct kvaser_usb_net_priv *priv) { + struct kvaser_usb_net_leaf_priv *leaf = priv->sub_priv; int err; init_completion(&priv->stop_comp); + cancel_delayed_work(&leaf->chip_state_req_work); + err = kvaser_usb_leaf_send_simple_cmd(priv->dev, CMD_STOP_CHIP, priv->channel); if (err) @@ -1623,6 +1654,31 @@ static int kvaser_usb_leaf_init_card(struct kvaser_usb *dev) return 0; } +static int kvaser_usb_leaf_init_channel(struct kvaser_usb_net_priv *priv) +{ + struct kvaser_usb_net_leaf_priv *leaf; + + leaf = devm_kzalloc(&priv->dev->intf->dev, sizeof(*leaf), GFP_KERNEL); + if (!leaf) + return -ENOMEM; + + leaf->net = priv; + INIT_DELAYED_WORK(&leaf->chip_state_req_work, + kvaser_usb_leaf_chip_state_req_work); + + priv->sub_priv = leaf; + + return 0; +} + +static void kvaser_usb_leaf_remove_channel(struct kvaser_usb_net_priv *priv) +{ + struct kvaser_usb_net_leaf_priv *leaf = priv->sub_priv; + + if (leaf) + cancel_delayed_work_sync(&leaf->chip_state_req_work); +} + static int kvaser_usb_leaf_set_bittiming(struct net_device *netdev) { struct kvaser_usb_net_priv *priv = netdev_priv(netdev); @@ -1720,6 +1776,8 @@ const struct kvaser_usb_dev_ops kvaser_usb_leaf_dev_ops = { .dev_get_berr_counter = kvaser_usb_leaf_get_berr_counter, .dev_setup_endpoints = kvaser_usb_leaf_setup_endpoints, .dev_init_card = kvaser_usb_leaf_init_card, + .dev_init_channel = kvaser_usb_leaf_init_channel, + .dev_remove_channel = kvaser_usb_leaf_remove_channel, .dev_get_software_info = kvaser_usb_leaf_get_software_info, .dev_get_software_details = NULL, .dev_get_card_info = kvaser_usb_leaf_get_card_info, From a11249acf802341294557895d8e5f6aef080253f Mon Sep 17 00:00:00 2001 From: Anssi Hannula Date: Mon, 10 Oct 2022 20:52:33 +0200 Subject: [PATCH 0385/2002] can: kvaser_usb_leaf: Fix wrong CAN state after stopping 0bfd:0124 Kvaser Mini PCI Express 2xHS FW 4.18.778 sends a CMD_CHIP_STATE_EVENT indicating bus-off after stopping the device, causing a stopped device to appear as CAN_STATE_BUS_OFF instead of CAN_STATE_STOPPED. Fix that by not handling error events on stopped devices. Fixes: 080f40a6fa28 ("can: kvaser_usb: Add support for Kvaser CAN/USB devices") Tested-by: Jimmy Assarsson Signed-off-by: Anssi Hannula Signed-off-by: Jimmy Assarsson Link: https://lore.kernel.org/all/20221010185237.319219-8-extja@kvaser.com Signed-off-by: Marc Kleine-Budde --- drivers/net/can/usb/kvaser_usb/kvaser_usb_leaf.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/net/can/usb/kvaser_usb/kvaser_usb_leaf.c b/drivers/net/can/usb/kvaser_usb/kvaser_usb_leaf.c index 12f3d023b72b..d6ca2ac8626c 100644 --- a/drivers/net/can/usb/kvaser_usb/kvaser_usb_leaf.c +++ b/drivers/net/can/usb/kvaser_usb/kvaser_usb_leaf.c @@ -1045,6 +1045,10 @@ static void kvaser_usb_leaf_rx_error(const struct kvaser_usb *dev, leaf = priv->sub_priv; stats = &priv->netdev->stats; + /* Ignore e.g. state change to bus-off reported just after stopping */ + if (!netif_running(priv->netdev)) + return; + /* Update all of the CAN interface's state and error counters before * trying any memory allocation that can actually fail with -ENOMEM. * From abb8670938b23a3be0772153125895c7e6742840 Mon Sep 17 00:00:00 2001 From: Anssi Hannula Date: Mon, 10 Oct 2022 20:52:34 +0200 Subject: [PATCH 0386/2002] can: kvaser_usb_leaf: Ignore stale bus-off after start With 0bfd:0124 Kvaser Mini PCI Express 2xHS FW 4.18.778 it was observed that if the device was bus-off when stopped, at next start (either via interface down/up or manual bus-off restart) the initial CMD_CHIP_STATE_EVENT received just after CMD_START_CHIP_REPLY will have the M16C_STATE_BUS_OFF bit still set, causing the interface to immediately go bus-off again. The bit seems to internally clear quickly afterwards but we do not get another CMD_CHIP_STATE_EVENT. Fix the issue by ignoring any initial bus-off state until we see at least one bus-on state. Also, poll the state periodically until that occurs. It is possible we lose one actual immediately occurring bus-off event here in which case the HW will auto-recover and we see the recovery event. We will then catch the next bus-off event, if any. This issue did not reproduce with 0bfd:0017 Kvaser Memorator Professional HS/HS FW 2.0.50. Fixes: 71873a9b38d1 ("can: kvaser_usb: Add support for more Kvaser Leaf v2 devices") Tested-by: Jimmy Assarsson Signed-off-by: Anssi Hannula Signed-off-by: Jimmy Assarsson Link: https://lore.kernel.org/all/20221010185237.319219-9-extja@kvaser.com Signed-off-by: Marc Kleine-Budde --- .../net/can/usb/kvaser_usb/kvaser_usb_leaf.c | 31 ++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/drivers/net/can/usb/kvaser_usb/kvaser_usb_leaf.c b/drivers/net/can/usb/kvaser_usb/kvaser_usb_leaf.c index d6ca2ac8626c..6df7b2eb7b6c 100644 --- a/drivers/net/can/usb/kvaser_usb/kvaser_usb_leaf.c +++ b/drivers/net/can/usb/kvaser_usb/kvaser_usb_leaf.c @@ -427,6 +427,9 @@ struct kvaser_usb_net_leaf_priv { struct kvaser_usb_net_priv *net; struct delayed_work chip_state_req_work; + + /* started but not reported as bus-on yet */ + bool joining_bus; }; static const struct can_bittiming_const kvaser_usb_leaf_m16c_bittiming_const = { @@ -966,6 +969,7 @@ kvaser_usb_leaf_rx_error_update_can_state(struct kvaser_usb_net_priv *priv, const struct kvaser_usb_err_summary *es, struct can_frame *cf) { + struct kvaser_usb_net_leaf_priv *leaf = priv->sub_priv; struct kvaser_usb *dev = priv->dev; struct net_device_stats *stats = &priv->netdev->stats; enum can_state cur_state, new_state, tx_state, rx_state; @@ -990,6 +994,22 @@ kvaser_usb_leaf_rx_error_update_can_state(struct kvaser_usb_net_priv *priv, new_state = CAN_STATE_ERROR_ACTIVE; } + /* 0bfd:0124 FW 4.18.778 was observed to send the initial + * CMD_CHIP_STATE_EVENT after CMD_START_CHIP with M16C_STATE_BUS_OFF + * bit set if the channel was bus-off when it was last stopped (even + * across chip resets). This bit will clear shortly afterwards, without + * triggering a second unsolicited chip state event. + * Ignore this initial bus-off. + */ + if (leaf->joining_bus) { + if (new_state == CAN_STATE_BUS_OFF) { + netdev_dbg(priv->netdev, "ignoring bus-off during startup"); + new_state = cur_state; + } else { + leaf->joining_bus = false; + } + } + if (new_state != cur_state) { tx_state = (es->txerr >= es->rxerr) ? new_state : 0; rx_state = (es->txerr <= es->rxerr) ? new_state : 0; @@ -1065,9 +1085,12 @@ static void kvaser_usb_leaf_rx_error(const struct kvaser_usb *dev, /* If there are errors, request status updates periodically as we do * not get automatic notifications of improved state. + * Also request updates if we saw a stale BUS_OFF during startup + * (joining_bus). */ if (new_state < CAN_STATE_BUS_OFF && - (es->rxerr || es->txerr || new_state == CAN_STATE_ERROR_PASSIVE)) + (es->rxerr || es->txerr || new_state == CAN_STATE_ERROR_PASSIVE || + leaf->joining_bus)) schedule_delayed_work(&leaf->chip_state_req_work, msecs_to_jiffies(500)); @@ -1587,8 +1610,11 @@ static int kvaser_usb_leaf_set_opt_mode(const struct kvaser_usb_net_priv *priv) static int kvaser_usb_leaf_start_chip(struct kvaser_usb_net_priv *priv) { + struct kvaser_usb_net_leaf_priv *leaf = priv->sub_priv; int err; + leaf->joining_bus = true; + init_completion(&priv->start_comp); err = kvaser_usb_leaf_send_simple_cmd(priv->dev, CMD_START_CHIP, @@ -1719,12 +1745,15 @@ static int kvaser_usb_leaf_set_mode(struct net_device *netdev, enum can_mode mode) { struct kvaser_usb_net_priv *priv = netdev_priv(netdev); + struct kvaser_usb_net_leaf_priv *leaf = priv->sub_priv; int err; switch (mode) { case CAN_MODE_START: kvaser_usb_unlink_tx_urbs(priv); + leaf->joining_bus = true; + err = kvaser_usb_leaf_simple_cmd_async(priv, CMD_START_CHIP); if (err) return err; From 90904d326269a38fe5dd895fb2db7c03199654c4 Mon Sep 17 00:00:00 2001 From: Anssi Hannula Date: Mon, 10 Oct 2022 20:52:35 +0200 Subject: [PATCH 0387/2002] can: kvaser_usb_leaf: Fix bogus restart events When auto-restart is enabled, the kvaser_usb_leaf driver considers transition from any state >= CAN_STATE_BUS_OFF as a bus-off recovery event (restart). However, these events may occur at interface startup time before kvaser_usb_open() has set the state to CAN_STATE_ERROR_ACTIVE, causing restarts counter to increase and CAN_ERR_RESTARTED to be sent despite no actual restart having occurred. Fix that by making the auto-restart condition checks more strict so that they only trigger when the interface was actually in the BUS_OFF state. Fixes: 080f40a6fa28 ("can: kvaser_usb: Add support for Kvaser CAN/USB devices") Tested-by: Jimmy Assarsson Signed-off-by: Anssi Hannula Signed-off-by: Jimmy Assarsson Link: https://lore.kernel.org/all/20221010185237.319219-10-extja@kvaser.com Signed-off-by: Marc Kleine-Budde --- drivers/net/can/usb/kvaser_usb/kvaser_usb_leaf.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/net/can/usb/kvaser_usb/kvaser_usb_leaf.c b/drivers/net/can/usb/kvaser_usb/kvaser_usb_leaf.c index 6df7b2eb7b6c..2250c8707ccc 100644 --- a/drivers/net/can/usb/kvaser_usb/kvaser_usb_leaf.c +++ b/drivers/net/can/usb/kvaser_usb/kvaser_usb_leaf.c @@ -901,7 +901,7 @@ static void kvaser_usb_leaf_tx_acknowledge(const struct kvaser_usb *dev, context = &priv->tx_contexts[tid % dev->max_tx_urbs]; /* Sometimes the state change doesn't come after a bus-off event */ - if (priv->can.restart_ms && priv->can.state >= CAN_STATE_BUS_OFF) { + if (priv->can.restart_ms && priv->can.state == CAN_STATE_BUS_OFF) { struct sk_buff *skb; struct can_frame *cf; @@ -1018,7 +1018,7 @@ kvaser_usb_leaf_rx_error_update_can_state(struct kvaser_usb_net_priv *priv, } if (priv->can.restart_ms && - cur_state >= CAN_STATE_BUS_OFF && + cur_state == CAN_STATE_BUS_OFF && new_state < CAN_STATE_BUS_OFF) priv->can.can_stats.restarts++; @@ -1111,7 +1111,7 @@ static void kvaser_usb_leaf_rx_error(const struct kvaser_usb *dev, } if (priv->can.restart_ms && - old_state >= CAN_STATE_BUS_OFF && + old_state == CAN_STATE_BUS_OFF && new_state < CAN_STATE_BUS_OFF) { cf->can_id |= CAN_ERR_RESTARTED; netif_carrier_on(priv->netdev); From 00e5786177649c1e3110f9454fdd34e336597265 Mon Sep 17 00:00:00 2001 From: Jimmy Assarsson Date: Mon, 10 Oct 2022 20:52:36 +0200 Subject: [PATCH 0388/2002] can: kvaser_usb: Add struct kvaser_usb_busparams Add struct kvaser_usb_busparams containing the busparameters used in CMD_{SET,GET}_BUSPARAMS* commands. Tested-by: Anssi Hannula Signed-off-by: Jimmy Assarsson Link: https://lore.kernel.org/all/20221010185237.319219-11-extja@kvaser.com Signed-off-by: Marc Kleine-Budde --- drivers/net/can/usb/kvaser_usb/kvaser_usb.h | 8 +++++ .../net/can/usb/kvaser_usb/kvaser_usb_hydra.c | 32 +++++++------------ .../net/can/usb/kvaser_usb/kvaser_usb_leaf.c | 18 ++++------- 3 files changed, 27 insertions(+), 31 deletions(-) diff --git a/drivers/net/can/usb/kvaser_usb/kvaser_usb.h b/drivers/net/can/usb/kvaser_usb/kvaser_usb.h index d9c5dd5da908..778b61c90c2b 100644 --- a/drivers/net/can/usb/kvaser_usb/kvaser_usb.h +++ b/drivers/net/can/usb/kvaser_usb/kvaser_usb.h @@ -76,6 +76,14 @@ struct kvaser_usb_tx_urb_context { u32 echo_index; }; +struct kvaser_usb_busparams { + __le32 bitrate; + u8 tseg1; + u8 tseg2; + u8 sjw; + u8 nsamples; +} __packed; + struct kvaser_usb { struct usb_device *udev; struct usb_interface *intf; diff --git a/drivers/net/can/usb/kvaser_usb/kvaser_usb_hydra.c b/drivers/net/can/usb/kvaser_usb/kvaser_usb_hydra.c index 7b52fda73d82..1c4ce42b980d 100644 --- a/drivers/net/can/usb/kvaser_usb/kvaser_usb_hydra.c +++ b/drivers/net/can/usb/kvaser_usb/kvaser_usb_hydra.c @@ -196,17 +196,9 @@ struct kvaser_cmd_chip_state_event { #define KVASER_USB_HYDRA_BUS_MODE_CANFD_ISO 0x01 #define KVASER_USB_HYDRA_BUS_MODE_NONISO 0x02 struct kvaser_cmd_set_busparams { - __le32 bitrate; - u8 tseg1; - u8 tseg2; - u8 sjw; - u8 nsamples; + struct kvaser_usb_busparams busparams_arb; u8 reserved0[4]; - __le32 bitrate_d; - u8 tseg1_d; - u8 tseg2_d; - u8 sjw_d; - u8 nsamples_d; + struct kvaser_usb_busparams busparams_data; u8 canfd_mode; u8 reserved1[7]; } __packed; @@ -1538,11 +1530,11 @@ static int kvaser_usb_hydra_set_bittiming(struct net_device *netdev) return -ENOMEM; cmd->header.cmd_no = CMD_SET_BUSPARAMS_REQ; - cmd->set_busparams_req.bitrate = cpu_to_le32(bt->bitrate); - cmd->set_busparams_req.sjw = (u8)sjw; - cmd->set_busparams_req.tseg1 = (u8)tseg1; - cmd->set_busparams_req.tseg2 = (u8)tseg2; - cmd->set_busparams_req.nsamples = 1; + cmd->set_busparams_req.busparams_arb.bitrate = cpu_to_le32(bt->bitrate); + cmd->set_busparams_req.busparams_arb.sjw = (u8)sjw; + cmd->set_busparams_req.busparams_arb.tseg1 = (u8)tseg1; + cmd->set_busparams_req.busparams_arb.tseg2 = (u8)tseg2; + cmd->set_busparams_req.busparams_arb.nsamples = 1; kvaser_usb_hydra_set_cmd_dest_he (cmd, dev->card_data.hydra.channel_to_he[priv->channel]); @@ -1572,11 +1564,11 @@ static int kvaser_usb_hydra_set_data_bittiming(struct net_device *netdev) return -ENOMEM; cmd->header.cmd_no = CMD_SET_BUSPARAMS_FD_REQ; - cmd->set_busparams_req.bitrate_d = cpu_to_le32(dbt->bitrate); - cmd->set_busparams_req.sjw_d = (u8)sjw; - cmd->set_busparams_req.tseg1_d = (u8)tseg1; - cmd->set_busparams_req.tseg2_d = (u8)tseg2; - cmd->set_busparams_req.nsamples_d = 1; + cmd->set_busparams_req.busparams_data.bitrate = cpu_to_le32(dbt->bitrate); + cmd->set_busparams_req.busparams_data.sjw = (u8)sjw; + cmd->set_busparams_req.busparams_data.tseg1 = (u8)tseg1; + cmd->set_busparams_req.busparams_data.tseg2 = (u8)tseg2; + cmd->set_busparams_req.busparams_data.nsamples = 1; if (priv->can.ctrlmode & CAN_CTRLMODE_FD) { if (priv->can.ctrlmode & CAN_CTRLMODE_FD_NON_ISO) diff --git a/drivers/net/can/usb/kvaser_usb/kvaser_usb_leaf.c b/drivers/net/can/usb/kvaser_usb/kvaser_usb_leaf.c index 2250c8707ccc..8392c5268379 100644 --- a/drivers/net/can/usb/kvaser_usb/kvaser_usb_leaf.c +++ b/drivers/net/can/usb/kvaser_usb/kvaser_usb_leaf.c @@ -164,11 +164,7 @@ struct usbcan_cmd_softinfo { struct kvaser_cmd_busparams { u8 tid; u8 channel; - __le32 bitrate; - u8 tseg1; - u8 tseg2; - u8 sjw; - u8 no_samp; + struct kvaser_usb_busparams busparams; } __packed; struct kvaser_cmd_tx_can { @@ -1725,15 +1721,15 @@ static int kvaser_usb_leaf_set_bittiming(struct net_device *netdev) cmd->len = CMD_HEADER_LEN + sizeof(struct kvaser_cmd_busparams); cmd->u.busparams.channel = priv->channel; cmd->u.busparams.tid = 0xff; - cmd->u.busparams.bitrate = cpu_to_le32(bt->bitrate); - cmd->u.busparams.sjw = bt->sjw; - cmd->u.busparams.tseg1 = bt->prop_seg + bt->phase_seg1; - cmd->u.busparams.tseg2 = bt->phase_seg2; + cmd->u.busparams.busparams.bitrate = cpu_to_le32(bt->bitrate); + cmd->u.busparams.busparams.sjw = bt->sjw; + cmd->u.busparams.busparams.tseg1 = bt->prop_seg + bt->phase_seg1; + cmd->u.busparams.busparams.tseg2 = bt->phase_seg2; if (priv->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES) - cmd->u.busparams.no_samp = 3; + cmd->u.busparams.busparams.nsamples = 3; else - cmd->u.busparams.no_samp = 1; + cmd->u.busparams.busparams.nsamples = 1; rc = kvaser_usb_send_cmd(dev, cmd, cmd->len); From 39d3df6b0ea80f9b515c632ca07b39b1c156edee Mon Sep 17 00:00:00 2001 From: Jimmy Assarsson Date: Mon, 10 Oct 2022 20:52:37 +0200 Subject: [PATCH 0389/2002] can: kvaser_usb: Compare requested bittiming parameters with actual parameters in do_set_{,data}_bittiming The device will respond with a CMD_ERROR_EVENT command, with error_code KVASER_USB_{LEAF,HYDRA}_ERROR_EVENT_PARAM, if the CMD_SET_BUSPARAMS_REQ contains invalid bittiming parameters. However, this command does not contain any channel reference. To check if the CMD_SET_BUSPARAMS_REQ was successful, redback and compare the requested bittiming parameters with the device reported parameters. Fixes: 080f40a6fa28 ("can: kvaser_usb: Add support for Kvaser CAN/USB devices") Fixes: aec5fb2268b7 ("can: kvaser_usb: Add support for Kvaser USB hydra family") Tested-by: Anssi Hannula Co-developed-by: Anssi Hannula Signed-off-by: Anssi Hannula Signed-off-by: Jimmy Assarsson Link: https://lore.kernel.org/all/20221010185237.319219-12-extja@kvaser.com Signed-off-by: Marc Kleine-Budde --- drivers/net/can/usb/kvaser_usb/kvaser_usb.h | 15 +- .../net/can/usb/kvaser_usb/kvaser_usb_core.c | 96 ++++++++++- .../net/can/usb/kvaser_usb/kvaser_usb_hydra.c | 150 +++++++++++++++--- .../net/can/usb/kvaser_usb/kvaser_usb_leaf.c | 64 ++++++-- 4 files changed, 284 insertions(+), 41 deletions(-) diff --git a/drivers/net/can/usb/kvaser_usb/kvaser_usb.h b/drivers/net/can/usb/kvaser_usb/kvaser_usb.h index 778b61c90c2b..ff10b3790d84 100644 --- a/drivers/net/can/usb/kvaser_usb/kvaser_usb.h +++ b/drivers/net/can/usb/kvaser_usb/kvaser_usb.h @@ -119,9 +119,12 @@ struct kvaser_usb_net_priv { struct net_device *netdev; int channel; - struct completion start_comp, stop_comp, flush_comp; + struct completion start_comp, stop_comp, flush_comp, + get_busparams_comp; struct usb_anchor tx_submitted; + struct kvaser_usb_busparams busparams_nominal, busparams_data; + spinlock_t tx_contexts_lock; /* lock for active_tx_contexts */ int active_tx_contexts; struct kvaser_usb_tx_urb_context tx_contexts[]; @@ -131,7 +134,9 @@ struct kvaser_usb_net_priv { * struct kvaser_usb_dev_ops - Device specific functions * @dev_set_mode: used for can.do_set_mode * @dev_set_bittiming: used for can.do_set_bittiming + * @dev_get_busparams: readback arbitration busparams * @dev_set_data_bittiming: used for can.do_set_data_bittiming + * @dev_get_data_busparams: readback data busparams * @dev_get_berr_counter: used for can.do_get_berr_counter * * @dev_setup_endpoints: setup USB in and out endpoints @@ -153,8 +158,12 @@ struct kvaser_usb_net_priv { */ struct kvaser_usb_dev_ops { int (*dev_set_mode)(struct net_device *netdev, enum can_mode mode); - int (*dev_set_bittiming)(struct net_device *netdev); - int (*dev_set_data_bittiming)(struct net_device *netdev); + int (*dev_set_bittiming)(const struct net_device *netdev, + const struct kvaser_usb_busparams *busparams); + int (*dev_get_busparams)(struct kvaser_usb_net_priv *priv); + int (*dev_set_data_bittiming)(const struct net_device *netdev, + const struct kvaser_usb_busparams *busparams); + int (*dev_get_data_busparams)(struct kvaser_usb_net_priv *priv); int (*dev_get_berr_counter)(const struct net_device *netdev, struct can_berr_counter *bec); int (*dev_setup_endpoints)(struct kvaser_usb *dev); diff --git a/drivers/net/can/usb/kvaser_usb/kvaser_usb_core.c b/drivers/net/can/usb/kvaser_usb/kvaser_usb_core.c index 19df05887166..989e75351062 100644 --- a/drivers/net/can/usb/kvaser_usb/kvaser_usb_core.c +++ b/drivers/net/can/usb/kvaser_usb/kvaser_usb_core.c @@ -440,10 +440,6 @@ static int kvaser_usb_open(struct net_device *netdev) if (err) return err; - err = kvaser_usb_setup_rx_urbs(dev); - if (err) - goto error; - err = ops->dev_set_opt_mode(priv); if (err) goto error; @@ -534,6 +530,93 @@ static int kvaser_usb_close(struct net_device *netdev) return 0; } +static int kvaser_usb_set_bittiming(struct net_device *netdev) +{ + struct kvaser_usb_net_priv *priv = netdev_priv(netdev); + struct kvaser_usb *dev = priv->dev; + const struct kvaser_usb_dev_ops *ops = dev->driver_info->ops; + struct can_bittiming *bt = &priv->can.bittiming; + + struct kvaser_usb_busparams busparams; + int tseg1 = bt->prop_seg + bt->phase_seg1; + int tseg2 = bt->phase_seg2; + int sjw = bt->sjw; + int err = -EOPNOTSUPP; + + busparams.bitrate = cpu_to_le32(bt->bitrate); + busparams.sjw = (u8)sjw; + busparams.tseg1 = (u8)tseg1; + busparams.tseg2 = (u8)tseg2; + if (priv->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES) + busparams.nsamples = 3; + else + busparams.nsamples = 1; + + err = ops->dev_set_bittiming(netdev, &busparams); + if (err) + return err; + + err = kvaser_usb_setup_rx_urbs(priv->dev); + if (err) + return err; + + err = ops->dev_get_busparams(priv); + if (err) { + /* Treat EOPNOTSUPP as success */ + if (err == -EOPNOTSUPP) + err = 0; + return err; + } + + if (memcmp(&busparams, &priv->busparams_nominal, + sizeof(priv->busparams_nominal)) != 0) + err = -EINVAL; + + return err; +} + +static int kvaser_usb_set_data_bittiming(struct net_device *netdev) +{ + struct kvaser_usb_net_priv *priv = netdev_priv(netdev); + struct kvaser_usb *dev = priv->dev; + const struct kvaser_usb_dev_ops *ops = dev->driver_info->ops; + struct can_bittiming *dbt = &priv->can.data_bittiming; + + struct kvaser_usb_busparams busparams; + int tseg1 = dbt->prop_seg + dbt->phase_seg1; + int tseg2 = dbt->phase_seg2; + int sjw = dbt->sjw; + int err; + + if (!ops->dev_set_data_bittiming || + !ops->dev_get_data_busparams) + return -EOPNOTSUPP; + + busparams.bitrate = cpu_to_le32(dbt->bitrate); + busparams.sjw = (u8)sjw; + busparams.tseg1 = (u8)tseg1; + busparams.tseg2 = (u8)tseg2; + busparams.nsamples = 1; + + err = ops->dev_set_data_bittiming(netdev, &busparams); + if (err) + return err; + + err = kvaser_usb_setup_rx_urbs(priv->dev); + if (err) + return err; + + err = ops->dev_get_data_busparams(priv); + if (err) + return err; + + if (memcmp(&busparams, &priv->busparams_data, + sizeof(priv->busparams_data)) != 0) + err = -EINVAL; + + return err; +} + static void kvaser_usb_write_bulk_callback(struct urb *urb) { struct kvaser_usb_tx_urb_context *context = urb->context; @@ -734,6 +817,7 @@ static int kvaser_usb_init_one(struct kvaser_usb *dev, int channel) init_completion(&priv->start_comp); init_completion(&priv->stop_comp); init_completion(&priv->flush_comp); + init_completion(&priv->get_busparams_comp); priv->can.ctrlmode_supported = 0; priv->dev = dev; @@ -746,7 +830,7 @@ static int kvaser_usb_init_one(struct kvaser_usb *dev, int channel) priv->can.state = CAN_STATE_STOPPED; priv->can.clock.freq = dev->cfg->clock.freq; priv->can.bittiming_const = dev->cfg->bittiming_const; - priv->can.do_set_bittiming = ops->dev_set_bittiming; + priv->can.do_set_bittiming = kvaser_usb_set_bittiming; priv->can.do_set_mode = ops->dev_set_mode; if ((driver_info->quirks & KVASER_USB_QUIRK_HAS_TXRX_ERRORS) || (priv->dev->card_data.capabilities & KVASER_USB_CAP_BERR_CAP)) @@ -758,7 +842,7 @@ static int kvaser_usb_init_one(struct kvaser_usb *dev, int channel) if (priv->can.ctrlmode_supported & CAN_CTRLMODE_FD) { priv->can.data_bittiming_const = dev->cfg->data_bittiming_const; - priv->can.do_set_data_bittiming = ops->dev_set_data_bittiming; + priv->can.do_set_data_bittiming = kvaser_usb_set_data_bittiming; } netdev->flags |= IFF_ECHO; diff --git a/drivers/net/can/usb/kvaser_usb/kvaser_usb_hydra.c b/drivers/net/can/usb/kvaser_usb/kvaser_usb_hydra.c index 1c4ce42b980d..bf63d885ac2f 100644 --- a/drivers/net/can/usb/kvaser_usb/kvaser_usb_hydra.c +++ b/drivers/net/can/usb/kvaser_usb/kvaser_usb_hydra.c @@ -45,6 +45,8 @@ static const struct kvaser_usb_dev_cfg kvaser_usb_hydra_dev_cfg_rt; /* Minihydra command IDs */ #define CMD_SET_BUSPARAMS_REQ 16 +#define CMD_GET_BUSPARAMS_REQ 17 +#define CMD_GET_BUSPARAMS_RESP 18 #define CMD_GET_CHIP_STATE_REQ 19 #define CMD_CHIP_STATE_EVENT 20 #define CMD_SET_DRIVERMODE_REQ 21 @@ -196,13 +198,26 @@ struct kvaser_cmd_chip_state_event { #define KVASER_USB_HYDRA_BUS_MODE_CANFD_ISO 0x01 #define KVASER_USB_HYDRA_BUS_MODE_NONISO 0x02 struct kvaser_cmd_set_busparams { - struct kvaser_usb_busparams busparams_arb; + struct kvaser_usb_busparams busparams_nominal; u8 reserved0[4]; struct kvaser_usb_busparams busparams_data; u8 canfd_mode; u8 reserved1[7]; } __packed; +/* Busparam type */ +#define KVASER_USB_HYDRA_BUSPARAM_TYPE_CAN 0x00 +#define KVASER_USB_HYDRA_BUSPARAM_TYPE_CANFD 0x01 +struct kvaser_cmd_get_busparams_req { + u8 type; + u8 reserved[27]; +} __packed; + +struct kvaser_cmd_get_busparams_res { + struct kvaser_usb_busparams busparams; + u8 reserved[20]; +} __packed; + /* Ctrl modes */ #define KVASER_USB_HYDRA_CTRLMODE_NORMAL 0x01 #define KVASER_USB_HYDRA_CTRLMODE_LISTEN 0x02 @@ -273,6 +288,8 @@ struct kvaser_cmd { struct kvaser_cmd_error_event error_event; struct kvaser_cmd_set_busparams set_busparams_req; + struct kvaser_cmd_get_busparams_req get_busparams_req; + struct kvaser_cmd_get_busparams_res get_busparams_res; struct kvaser_cmd_chip_state_event chip_state_event; @@ -355,6 +372,10 @@ struct kvaser_cmd_ext { } __packed; } __packed; +struct kvaser_usb_net_hydra_priv { + int pending_get_busparams_type; +}; + static const struct can_bittiming_const kvaser_usb_hydra_kcan_bittiming_c = { .name = "kvaser_usb_kcan", .tseg1_min = 1, @@ -832,6 +853,39 @@ static void kvaser_usb_hydra_flush_queue_reply(const struct kvaser_usb *dev, complete(&priv->flush_comp); } +static void kvaser_usb_hydra_get_busparams_reply(const struct kvaser_usb *dev, + const struct kvaser_cmd *cmd) +{ + struct kvaser_usb_net_priv *priv; + struct kvaser_usb_net_hydra_priv *hydra; + + priv = kvaser_usb_hydra_net_priv_from_cmd(dev, cmd); + if (!priv) + return; + + hydra = priv->sub_priv; + if (!hydra) + return; + + switch (hydra->pending_get_busparams_type) { + case KVASER_USB_HYDRA_BUSPARAM_TYPE_CAN: + memcpy(&priv->busparams_nominal, &cmd->get_busparams_res.busparams, + sizeof(priv->busparams_nominal)); + break; + case KVASER_USB_HYDRA_BUSPARAM_TYPE_CANFD: + memcpy(&priv->busparams_data, &cmd->get_busparams_res.busparams, + sizeof(priv->busparams_nominal)); + break; + default: + dev_warn(&dev->intf->dev, "Unknown get_busparams_type %d\n", + hydra->pending_get_busparams_type); + break; + } + hydra->pending_get_busparams_type = -1; + + complete(&priv->get_busparams_comp); +} + static void kvaser_usb_hydra_bus_status_to_can_state(const struct kvaser_usb_net_priv *priv, u8 bus_status, @@ -1318,6 +1372,10 @@ static void kvaser_usb_hydra_handle_cmd_std(const struct kvaser_usb *dev, kvaser_usb_hydra_state_event(dev, cmd); break; + case CMD_GET_BUSPARAMS_RESP: + kvaser_usb_hydra_get_busparams_reply(dev, cmd); + break; + case CMD_ERROR_EVENT: kvaser_usb_hydra_error_event(dev, cmd); break; @@ -1514,15 +1572,58 @@ static int kvaser_usb_hydra_set_mode(struct net_device *netdev, return err; } -static int kvaser_usb_hydra_set_bittiming(struct net_device *netdev) +static int kvaser_usb_hydra_get_busparams(struct kvaser_usb_net_priv *priv, + int busparams_type) +{ + struct kvaser_usb *dev = priv->dev; + struct kvaser_usb_net_hydra_priv *hydra = priv->sub_priv; + struct kvaser_cmd *cmd; + int err; + + if (!hydra) + return -EINVAL; + + cmd = kcalloc(1, sizeof(struct kvaser_cmd), GFP_KERNEL); + if (!cmd) + return -ENOMEM; + + cmd->header.cmd_no = CMD_GET_BUSPARAMS_REQ; + kvaser_usb_hydra_set_cmd_dest_he + (cmd, dev->card_data.hydra.channel_to_he[priv->channel]); + kvaser_usb_hydra_set_cmd_transid + (cmd, kvaser_usb_hydra_get_next_transid(dev)); + cmd->get_busparams_req.type = busparams_type; + hydra->pending_get_busparams_type = busparams_type; + + reinit_completion(&priv->get_busparams_comp); + + err = kvaser_usb_send_cmd(dev, cmd, kvaser_usb_hydra_cmd_size(cmd)); + if (err) + return err; + + if (!wait_for_completion_timeout(&priv->get_busparams_comp, + msecs_to_jiffies(KVASER_USB_TIMEOUT))) + return -ETIMEDOUT; + + return err; +} + +static int kvaser_usb_hydra_get_nominal_busparams(struct kvaser_usb_net_priv *priv) +{ + return kvaser_usb_hydra_get_busparams(priv, KVASER_USB_HYDRA_BUSPARAM_TYPE_CAN); +} + +static int kvaser_usb_hydra_get_data_busparams(struct kvaser_usb_net_priv *priv) +{ + return kvaser_usb_hydra_get_busparams(priv, KVASER_USB_HYDRA_BUSPARAM_TYPE_CANFD); +} + +static int kvaser_usb_hydra_set_bittiming(const struct net_device *netdev, + const struct kvaser_usb_busparams *busparams) { struct kvaser_cmd *cmd; struct kvaser_usb_net_priv *priv = netdev_priv(netdev); - struct can_bittiming *bt = &priv->can.bittiming; struct kvaser_usb *dev = priv->dev; - int tseg1 = bt->prop_seg + bt->phase_seg1; - int tseg2 = bt->phase_seg2; - int sjw = bt->sjw; int err; cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); @@ -1530,11 +1631,8 @@ static int kvaser_usb_hydra_set_bittiming(struct net_device *netdev) return -ENOMEM; cmd->header.cmd_no = CMD_SET_BUSPARAMS_REQ; - cmd->set_busparams_req.busparams_arb.bitrate = cpu_to_le32(bt->bitrate); - cmd->set_busparams_req.busparams_arb.sjw = (u8)sjw; - cmd->set_busparams_req.busparams_arb.tseg1 = (u8)tseg1; - cmd->set_busparams_req.busparams_arb.tseg2 = (u8)tseg2; - cmd->set_busparams_req.busparams_arb.nsamples = 1; + memcpy(&cmd->set_busparams_req.busparams_nominal, busparams, + sizeof(cmd->set_busparams_req.busparams_nominal)); kvaser_usb_hydra_set_cmd_dest_he (cmd, dev->card_data.hydra.channel_to_he[priv->channel]); @@ -1548,15 +1646,12 @@ static int kvaser_usb_hydra_set_bittiming(struct net_device *netdev) return err; } -static int kvaser_usb_hydra_set_data_bittiming(struct net_device *netdev) +static int kvaser_usb_hydra_set_data_bittiming(const struct net_device *netdev, + const struct kvaser_usb_busparams *busparams) { struct kvaser_cmd *cmd; struct kvaser_usb_net_priv *priv = netdev_priv(netdev); - struct can_bittiming *dbt = &priv->can.data_bittiming; struct kvaser_usb *dev = priv->dev; - int tseg1 = dbt->prop_seg + dbt->phase_seg1; - int tseg2 = dbt->phase_seg2; - int sjw = dbt->sjw; int err; cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); @@ -1564,11 +1659,8 @@ static int kvaser_usb_hydra_set_data_bittiming(struct net_device *netdev) return -ENOMEM; cmd->header.cmd_no = CMD_SET_BUSPARAMS_FD_REQ; - cmd->set_busparams_req.busparams_data.bitrate = cpu_to_le32(dbt->bitrate); - cmd->set_busparams_req.busparams_data.sjw = (u8)sjw; - cmd->set_busparams_req.busparams_data.tseg1 = (u8)tseg1; - cmd->set_busparams_req.busparams_data.tseg2 = (u8)tseg2; - cmd->set_busparams_req.busparams_data.nsamples = 1; + memcpy(&cmd->set_busparams_req.busparams_data, busparams, + sizeof(cmd->set_busparams_req.busparams_data)); if (priv->can.ctrlmode & CAN_CTRLMODE_FD) { if (priv->can.ctrlmode & CAN_CTRLMODE_FD_NON_ISO) @@ -1675,6 +1767,19 @@ static int kvaser_usb_hydra_init_card(struct kvaser_usb *dev) return 0; } +static int kvaser_usb_hydra_init_channel(struct kvaser_usb_net_priv *priv) +{ + struct kvaser_usb_net_hydra_priv *hydra; + + hydra = devm_kzalloc(&priv->dev->intf->dev, sizeof(*hydra), GFP_KERNEL); + if (!hydra) + return -ENOMEM; + + priv->sub_priv = hydra; + + return 0; +} + static int kvaser_usb_hydra_get_software_info(struct kvaser_usb *dev) { struct kvaser_cmd cmd; @@ -2019,10 +2124,13 @@ kvaser_usb_hydra_frame_to_cmd(const struct kvaser_usb_net_priv *priv, const struct kvaser_usb_dev_ops kvaser_usb_hydra_dev_ops = { .dev_set_mode = kvaser_usb_hydra_set_mode, .dev_set_bittiming = kvaser_usb_hydra_set_bittiming, + .dev_get_busparams = kvaser_usb_hydra_get_nominal_busparams, .dev_set_data_bittiming = kvaser_usb_hydra_set_data_bittiming, + .dev_get_data_busparams = kvaser_usb_hydra_get_data_busparams, .dev_get_berr_counter = kvaser_usb_hydra_get_berr_counter, .dev_setup_endpoints = kvaser_usb_hydra_setup_endpoints, .dev_init_card = kvaser_usb_hydra_init_card, + .dev_init_channel = kvaser_usb_hydra_init_channel, .dev_get_software_info = kvaser_usb_hydra_get_software_info, .dev_get_software_details = kvaser_usb_hydra_get_software_details, .dev_get_card_info = kvaser_usb_hydra_get_card_info, diff --git a/drivers/net/can/usb/kvaser_usb/kvaser_usb_leaf.c b/drivers/net/can/usb/kvaser_usb/kvaser_usb_leaf.c index 8392c5268379..5225e2da6437 100644 --- a/drivers/net/can/usb/kvaser_usb/kvaser_usb_leaf.c +++ b/drivers/net/can/usb/kvaser_usb/kvaser_usb_leaf.c @@ -57,6 +57,8 @@ #define CMD_RX_EXT_MESSAGE 14 #define CMD_TX_EXT_MESSAGE 15 #define CMD_SET_BUS_PARAMS 16 +#define CMD_GET_BUS_PARAMS 17 +#define CMD_GET_BUS_PARAMS_REPLY 18 #define CMD_GET_CHIP_STATE 19 #define CMD_CHIP_STATE_EVENT 20 #define CMD_SET_CTRL_MODE 21 @@ -376,6 +378,7 @@ static const u8 kvaser_usb_leaf_cmd_sizes_leaf[] = { [CMD_CHIP_STATE_EVENT] = kvaser_fsize(u.leaf.chip_state_event), [CMD_CAN_ERROR_EVENT] = kvaser_fsize(u.leaf.can_error_event), [CMD_GET_CAPABILITIES_RESP] = kvaser_fsize(u.leaf.cap_res), + [CMD_GET_BUS_PARAMS_REPLY] = kvaser_fsize(u.busparams), [CMD_ERROR_EVENT] = kvaser_fsize(u.leaf.error_event), /* ignored events: */ [CMD_FLUSH_QUEUE_REPLY] = CMD_SIZE_ANY, @@ -1486,6 +1489,25 @@ static void kvaser_usb_leaf_stop_chip_reply(const struct kvaser_usb *dev, complete(&priv->stop_comp); } +static void kvaser_usb_leaf_get_busparams_reply(const struct kvaser_usb *dev, + const struct kvaser_cmd *cmd) +{ + struct kvaser_usb_net_priv *priv; + u8 channel = cmd->u.busparams.channel; + + if (channel >= dev->nchannels) { + dev_err(&dev->intf->dev, + "Invalid channel number (%d)\n", channel); + return; + } + + priv = dev->nets[channel]; + memcpy(&priv->busparams_nominal, &cmd->u.busparams.busparams, + sizeof(priv->busparams_nominal)); + + complete(&priv->get_busparams_comp); +} + static void kvaser_usb_leaf_handle_command(const struct kvaser_usb *dev, const struct kvaser_cmd *cmd) { @@ -1528,6 +1550,10 @@ static void kvaser_usb_leaf_handle_command(const struct kvaser_usb *dev, kvaser_usb_leaf_error_event(dev, cmd); break; + case CMD_GET_BUS_PARAMS_REPLY: + kvaser_usb_leaf_get_busparams_reply(dev, cmd); + break; + /* Ignored commands */ case CMD_USBCAN_CLOCK_OVERFLOW_EVENT: if (dev->driver_info->family != KVASER_USBCAN) @@ -1705,10 +1731,10 @@ static void kvaser_usb_leaf_remove_channel(struct kvaser_usb_net_priv *priv) cancel_delayed_work_sync(&leaf->chip_state_req_work); } -static int kvaser_usb_leaf_set_bittiming(struct net_device *netdev) +static int kvaser_usb_leaf_set_bittiming(const struct net_device *netdev, + const struct kvaser_usb_busparams *busparams) { struct kvaser_usb_net_priv *priv = netdev_priv(netdev); - struct can_bittiming *bt = &priv->can.bittiming; struct kvaser_usb *dev = priv->dev; struct kvaser_cmd *cmd; int rc; @@ -1721,15 +1747,8 @@ static int kvaser_usb_leaf_set_bittiming(struct net_device *netdev) cmd->len = CMD_HEADER_LEN + sizeof(struct kvaser_cmd_busparams); cmd->u.busparams.channel = priv->channel; cmd->u.busparams.tid = 0xff; - cmd->u.busparams.busparams.bitrate = cpu_to_le32(bt->bitrate); - cmd->u.busparams.busparams.sjw = bt->sjw; - cmd->u.busparams.busparams.tseg1 = bt->prop_seg + bt->phase_seg1; - cmd->u.busparams.busparams.tseg2 = bt->phase_seg2; - - if (priv->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES) - cmd->u.busparams.busparams.nsamples = 3; - else - cmd->u.busparams.busparams.nsamples = 1; + memcpy(&cmd->u.busparams.busparams, busparams, + sizeof(cmd->u.busparams.busparams)); rc = kvaser_usb_send_cmd(dev, cmd, cmd->len); @@ -1737,6 +1756,27 @@ static int kvaser_usb_leaf_set_bittiming(struct net_device *netdev) return rc; } +static int kvaser_usb_leaf_get_busparams(struct kvaser_usb_net_priv *priv) +{ + int err; + + if (priv->dev->driver_info->family == KVASER_USBCAN) + return -EOPNOTSUPP; + + reinit_completion(&priv->get_busparams_comp); + + err = kvaser_usb_leaf_send_simple_cmd(priv->dev, CMD_GET_BUS_PARAMS, + priv->channel); + if (err) + return err; + + if (!wait_for_completion_timeout(&priv->get_busparams_comp, + msecs_to_jiffies(KVASER_USB_TIMEOUT))) + return -ETIMEDOUT; + + return 0; +} + static int kvaser_usb_leaf_set_mode(struct net_device *netdev, enum can_mode mode) { @@ -1801,7 +1841,9 @@ static int kvaser_usb_leaf_setup_endpoints(struct kvaser_usb *dev) const struct kvaser_usb_dev_ops kvaser_usb_leaf_dev_ops = { .dev_set_mode = kvaser_usb_leaf_set_mode, .dev_set_bittiming = kvaser_usb_leaf_set_bittiming, + .dev_get_busparams = kvaser_usb_leaf_get_busparams, .dev_set_data_bittiming = NULL, + .dev_get_data_busparams = NULL, .dev_get_berr_counter = kvaser_usb_leaf_get_berr_counter, .dev_setup_endpoints = kvaser_usb_leaf_setup_endpoints, .dev_init_card = kvaser_usb_leaf_init_card, From 4aeb91880999775b04ead5609823dca47b4e30a2 Mon Sep 17 00:00:00 2001 From: Marc Kleine-Budde Date: Wed, 12 Oct 2022 10:46:10 +0200 Subject: [PATCH 0390/2002] can: m_can: use consistent indention The driver uses indent-with-tab for defines. Replace spaces after IR_ERR_BUS_31X with tab for consistent indention. Link: https://lore.kernel.org/all/20221024185544.68240-1-mkl@pengutronix.de Signed-off-by: Marc Kleine-Budde --- drivers/net/can/m_can/m_can.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/can/m_can/m_can.c b/drivers/net/can/m_can/m_can.c index 34c76ee87c6e..59deb185fd6b 100644 --- a/drivers/net/can/m_can/m_can.c +++ b/drivers/net/can/m_can/m_can.c @@ -210,7 +210,7 @@ enum m_can_reg { /* Interrupts for version >= 3.1.x */ #define IR_ERR_LEC_31X (IR_PED | IR_PEA) -#define IR_ERR_BUS_31X (IR_ERR_LEC_31X | IR_WDI | IR_BEU | IR_BEC | \ +#define IR_ERR_BUS_31X (IR_ERR_LEC_31X | IR_WDI | IR_BEU | IR_BEC | \ IR_TOO | IR_MRAF | IR_TSW | IR_TEFL | IR_RF1L | \ IR_RF0L) #define IR_ERR_ALL_31X (IR_ERR_STATE | IR_ERR_BUS_31X) From aa9832e4501264a43db671bba09fe4d8bc39fcff Mon Sep 17 00:00:00 2001 From: Dongliang Mu Date: Mon, 24 Oct 2022 19:00:30 +0800 Subject: [PATCH 0391/2002] can: ucan: ucan_disconnect(): change unregister_netdev() to unregister_candev() From API pairing, change unregister_netdev() to unregister_candev() since the registration function is register_candev(). Actually, they are the same. Signed-off-by: Dongliang Mu Link: https://lore.kernel.org/all/20221024110033.727542-1-dzm91@hust.edu.cn [mkl: adjust subject + commit message] Signed-off-by: Marc Kleine-Budde --- drivers/net/can/usb/ucan.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/can/usb/ucan.c b/drivers/net/can/usb/ucan.c index b53e709943bc..a1734f1c0148 100644 --- a/drivers/net/can/usb/ucan.c +++ b/drivers/net/can/usb/ucan.c @@ -1582,7 +1582,7 @@ static void ucan_disconnect(struct usb_interface *intf) usb_set_intfdata(intf, NULL); if (up) { - unregister_netdev(up->netdev); + unregister_candev(up->netdev); free_candev(up->netdev); } } From 68399ff574e4faf42b8d85da9339ca3ee2892cc7 Mon Sep 17 00:00:00 2001 From: Biju Das Date: Tue, 25 Oct 2022 16:56:57 +0100 Subject: [PATCH 0392/2002] can: rcar_canfd: Use devm_reset_control_get_optional_exclusive Replace devm_reset_control_get_exclusive->devm_reset_control_ get_optional_exclusive so that we can avoid unnecessary SoC specific check in probe(). Signed-off-by: Biju Das Reviewed-by: Geert Uytterhoeven Link: https://lore.kernel.org/all/20221025155657.1426948-4-biju.das.jz@bp.renesas.com Signed-off-by: Marc Kleine-Budde --- drivers/net/can/rcar/rcar_canfd.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/drivers/net/can/rcar/rcar_canfd.c b/drivers/net/can/rcar/rcar_canfd.c index 567620d215f8..9a55a54c4507 100644 --- a/drivers/net/can/rcar/rcar_canfd.c +++ b/drivers/net/can/rcar/rcar_canfd.c @@ -1889,17 +1889,17 @@ static int rcar_canfd_probe(struct platform_device *pdev) gpriv->chip_id = chip_id; gpriv->max_channels = max_channels; - if (gpriv->chip_id == RENESAS_RZG2L) { - gpriv->rstc1 = devm_reset_control_get_exclusive(&pdev->dev, "rstp_n"); - if (IS_ERR(gpriv->rstc1)) - return dev_err_probe(&pdev->dev, PTR_ERR(gpriv->rstc1), - "failed to get rstp_n\n"); + gpriv->rstc1 = devm_reset_control_get_optional_exclusive(&pdev->dev, + "rstp_n"); + if (IS_ERR(gpriv->rstc1)) + return dev_err_probe(&pdev->dev, PTR_ERR(gpriv->rstc1), + "failed to get rstp_n\n"); - gpriv->rstc2 = devm_reset_control_get_exclusive(&pdev->dev, "rstc_n"); - if (IS_ERR(gpriv->rstc2)) - return dev_err_probe(&pdev->dev, PTR_ERR(gpriv->rstc2), - "failed to get rstc_n\n"); - } + gpriv->rstc2 = devm_reset_control_get_optional_exclusive(&pdev->dev, + "rstc_n"); + if (IS_ERR(gpriv->rstc2)) + return dev_err_probe(&pdev->dev, PTR_ERR(gpriv->rstc2), + "failed to get rstc_n\n"); /* Peripheral clock */ gpriv->clkp = devm_clk_get(&pdev->dev, "fck"); From d96d4276eaeb09b07e0949d432622691ea5c96f5 Mon Sep 17 00:00:00 2001 From: Yonghong Song Date: Wed, 26 Oct 2022 09:30:14 -0700 Subject: [PATCH 0393/2002] selftests/bpf: Fix bpftool synctypes checking failure kernel-patches/bpf failed with error: Running bpftool checks... Comparing /data/users/ast/net-next/tools/include/uapi/linux/bpf.h (bpf_map_type) and /data/users/ast/net-next/tools/bpf/bpftool/map.c (do_help() TYPE): {'cgroup_storage_deprecated', 'cgroup_storage'} Comparing /data/users/ast/net-next/tools/include/uapi/linux/bpf.h (bpf_map_type) and /data/users/ast/net-next/tools/bpf/bpftool/Documentation/bpftool-map.rst (TYPE): {'cgroup_storage_deprecated', 'cgroup_storage'} The selftests/bpf/test_bpftool_synctypes.py runs checking in the above. The failure is introduced by Commit c4bcfb38a95e("bpf: Implement cgroup storage available to non-cgroup-attached bpf progs"). The commit introduced BPF_MAP_TYPE_CGROUP_STORAGE_DEPRECATED which has the same enum value as BPF_MAP_TYPE_CGROUP_STORAGE. In test_bpftool_synctypes.py, one test is to compare uapi bpf.h map types and bpftool supported maps. The tool picks 'cgroup_storage_deprecated' from bpf.h while bpftool supported map is displayed as 'cgroup_storage'. The test failure can be fixed by explicitly replacing 'cgroup_storage_deprecated' with 'cgroup_storage' in uapi bpf.h map types. Signed-off-by: Yonghong Song Reviewed-by: Quentin Monnet Link: https://lore.kernel.org/r/20221026163014.470732-1-yhs@fb.com Signed-off-by: Alexei Starovoitov --- tools/testing/selftests/bpf/test_bpftool_synctypes.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tools/testing/selftests/bpf/test_bpftool_synctypes.py b/tools/testing/selftests/bpf/test_bpftool_synctypes.py index a6410bebe603..9fe4c9336c6f 100755 --- a/tools/testing/selftests/bpf/test_bpftool_synctypes.py +++ b/tools/testing/selftests/bpf/test_bpftool_synctypes.py @@ -501,6 +501,14 @@ def main(): source_map_types = set(bpf_info.get_map_type_map().values()) source_map_types.discard('unspec') + # BPF_MAP_TYPE_CGROUP_STORAGE_DEPRECATED and BPF_MAP_TYPE_CGROUP_STORAGE + # share the same enum value and source_map_types picks + # BPF_MAP_TYPE_CGROUP_STORAGE_DEPRECATED/cgroup_storage_deprecated. + # Replace 'cgroup_storage_deprecated' with 'cgroup_storage' + # so it aligns with what `bpftool map help` shows. + source_map_types.remove('cgroup_storage_deprecated') + source_map_types.add('cgroup_storage') + help_map_types = map_info.get_map_help() help_map_options = map_info.get_options() map_info.close() From 96f341a4751d1939a303c5c526b2ca79ebda075f Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Wed, 26 Oct 2022 09:16:45 +0100 Subject: [PATCH 0394/2002] bpftool: Fix spelling mistake "disasembler" -> "disassembler" There is a spelling mistake in an error message. Fix it. Signed-off-by: Colin Ian King Acked-by: Quentin Monnet Link: https://lore.kernel.org/r/20221026081645.3186878-1-colin.i.king@gmail.com Signed-off-by: Martin KaFai Lau --- tools/bpf/bpftool/jit_disasm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/bpf/bpftool/jit_disasm.c b/tools/bpf/bpftool/jit_disasm.c index 58a5017034a2..7b8d9ec89ebd 100644 --- a/tools/bpf/bpftool/jit_disasm.c +++ b/tools/bpf/bpftool/jit_disasm.c @@ -223,7 +223,7 @@ static int init_context(disasm_ctx_t *ctx, const char *arch, memset(tpath, 0, sizeof(tpath)); if (get_exec_path(tpath, sizeof(tpath))) { - p_err("failed to create disasembler (get_exec_path)"); + p_err("failed to create disassembler (get_exec_path)"); return -1; } From d1e96cc4fbe031c19d6fd9d8d2e63c03452fa290 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Tue, 25 Oct 2022 18:05:46 +0000 Subject: [PATCH 0395/2002] mptcp: fix tracking issue in mptcp_subflow_create_socket() My recent patch missed that mptcp_subflow_create_socket() was creating a 'kernel' socket, then converted it to 'user' socket. Fixes: 0cafd77dcd03 ("net: add a refcount tracker for kernel sockets") Reported-by: syzbot Signed-off-by: Eric Dumazet Cc: Matthieu Baerts Reviewed-by: Kuniyuki Iwashima Reviewed-by: Mat Martineau Link: https://lore.kernel.org/r/20221025180546.652251-1-edumazet@google.com Signed-off-by: Jakub Kicinski --- net/mptcp/subflow.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/net/mptcp/subflow.c b/net/mptcp/subflow.c index 07dd23d0fe04..120f792fda97 100644 --- a/net/mptcp/subflow.c +++ b/net/mptcp/subflow.c @@ -1595,7 +1595,9 @@ int mptcp_subflow_create_socket(struct sock *sk, struct socket **new_sock) /* kernel sockets do not by default acquire net ref, but TCP timer * needs it. + * Update ns_tracker to current stack trace and refcounted tracker. */ + __netns_tracker_free(net, &sf->sk->ns_tracker, false); sf->sk->sk_net_refcnt = 1; get_net_track(net, &sf->sk->ns_tracker, GFP_KERNEL); sock_inuse_add(net, 1); From e0b3ef17f45eb8a6860189306cf0ce5f509a043b Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Tue, 25 Oct 2022 11:51:26 -0700 Subject: [PATCH 0396/2002] phylink: require valid state argument to phylink_validate_mask_caps() state is deferenced earlier in the function, the NULL check is pointless. Since we don't have any crash reports presumably it's safe to assume state is not NULL. Fixes: f392a1846489 ("net: phylink: provide phylink_validate_mask_caps() helper") Reviewed-by: Sean Anderson Link: https://lore.kernel.org/r/20221025185126.1720553-1-kuba@kernel.org Signed-off-by: Jakub Kicinski --- drivers/net/phy/phylink.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c index 62106c9e9a9d..88f60e98b760 100644 --- a/drivers/net/phy/phylink.c +++ b/drivers/net/phy/phylink.c @@ -564,7 +564,7 @@ EXPORT_SYMBOL_GPL(phylink_get_capabilities); /** * phylink_validate_mask_caps() - Restrict link modes based on caps * @supported: ethtool bitmask for supported link modes. - * @state: an (optional) pointer to a &struct phylink_link_state. + * @state: pointer to a &struct phylink_link_state. * @mac_capabilities: bitmask of MAC capabilities * * Calculate the supported link modes based on @mac_capabilities, and restrict @@ -585,8 +585,7 @@ void phylink_validate_mask_caps(unsigned long *supported, phylink_caps_to_linkmodes(mask, caps); linkmode_and(supported, supported, mask); - if (state) - linkmode_and(state->advertising, state->advertising, mask); + linkmode_and(state->advertising, state->advertising, mask); } EXPORT_SYMBOL_GPL(phylink_validate_mask_caps); From b65ef50e0647cb6d7317ee5122f461286bb7f8d5 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Mon, 24 Oct 2022 14:50:46 +0100 Subject: [PATCH 0397/2002] net/rds: remove variable total_copied Variable total_copied is just being incremented and it's never used anywhere else. The variable and the increment are redundant so remove it. Signed-off-by: Colin Ian King Link: https://lore.kernel.org/r/20221024135046.2159523-1-colin.i.king@gmail.com Signed-off-by: Jakub Kicinski --- net/rds/message.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/net/rds/message.c b/net/rds/message.c index 44dbc612ef54..b47e4f0a1639 100644 --- a/net/rds/message.c +++ b/net/rds/message.c @@ -366,7 +366,6 @@ static int rds_message_zcopy_from_user(struct rds_message *rm, struct iov_iter * struct scatterlist *sg; int ret = 0; int length = iov_iter_count(from); - int total_copied = 0; struct rds_msg_zcopy_info *info; rm->m_inc.i_hdr.h_len = cpu_to_be32(iov_iter_count(from)); @@ -404,7 +403,6 @@ static int rds_message_zcopy_from_user(struct rds_message *rm, struct iov_iter * ret = -EFAULT; goto err; } - total_copied += copied; length -= copied; sg_set_page(sg, pages, copied, start); rm->data.op_nents++; From 3a6573b7a21864aafd43b347918a39e84dabfd89 Mon Sep 17 00:00:00 2001 From: Kunihiko Hayashi Date: Mon, 24 Oct 2022 16:23:14 +0900 Subject: [PATCH 0398/2002] net: ethernet: ave: Remove duplicate phy_resume() calls ave_open() in ave_resume() executes __phy_resume() via phy_start(), so no need to call phy_resume() explicitly. Remove it. Signed-off-by: Kunihiko Hayashi Link: https://lore.kernel.org/r/20221024072314.24969-1-hayashi.kunihiko@socionext.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/socionext/sni_ave.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/drivers/net/ethernet/socionext/sni_ave.c b/drivers/net/ethernet/socionext/sni_ave.c index 1fa09b49ba7f..f7a44e039b5f 100644 --- a/drivers/net/ethernet/socionext/sni_ave.c +++ b/drivers/net/ethernet/socionext/sni_ave.c @@ -1760,12 +1760,6 @@ static int ave_resume(struct device *dev) wol.wolopts = priv->wolopts; __ave_ethtool_set_wol(ndev, &wol); - if (ndev->phydev) { - ret = phy_resume(ndev->phydev); - if (ret) - return ret; - } - if (netif_running(ndev)) { ret = ave_open(ndev); netif_device_attach(ndev); From 95d9a3dab109f2806980d55634972120824a5a5a Mon Sep 17 00:00:00 2001 From: Victor Nogueira Date: Mon, 24 Oct 2022 11:16:03 +0000 Subject: [PATCH 0399/2002] selftests: tc-testing: Add matchJSON to tdc This allows the use of a matchJSON field in tests to match against JSON output from the command under test, if that command outputs JSON. You specify what you want to match against as a JSON array or object in the test's matchJSON field. You can leave out any fields you don't want to match against that are present in the output and they will be skipped. An example matchJSON value would look like this: "matchJSON": [ { "Value": { "neighIP": { "family": 4, "addr": "AQIDBA==", "width": 32 }, "nsflags": 142, "ncflags": 0, "LLADDR": "ESIzRFVm" } } ] The real output from the command under test might have some extra fields that we don't care about for matching, and since we didn't include them in our matchJSON value, those fields will not be attempted to be matched. If everything we included above has the same values as the real command output, the test will pass. The matchJSON field's type must be the same as the command output's type, otherwise the test will fail. So if the command outputs an array, then the value of matchJSON must also be an array. If matchJSON is an array, it must not contain more elements than the command output's array, otherwise the test will fail. Signed-off-by: Jeremy Carter Signed-off-by: Victor Nogueira Acked-by: Jamal Hadi Salim Link: https://lore.kernel.org/r/20221024111603.2185410-1-victor@mojatatu.com Signed-off-by: Jakub Kicinski --- tools/testing/selftests/tc-testing/tdc.py | 125 ++++++++++++++++++++-- 1 file changed, 118 insertions(+), 7 deletions(-) diff --git a/tools/testing/selftests/tc-testing/tdc.py b/tools/testing/selftests/tc-testing/tdc.py index ee22e3447ec7..7bd94f8e490a 100755 --- a/tools/testing/selftests/tc-testing/tdc.py +++ b/tools/testing/selftests/tc-testing/tdc.py @@ -246,6 +246,110 @@ def prepare_env(args, pm, stage, prefix, cmdlist, output = None): stage, output, '"{}" did not complete successfully'.format(prefix)) +def verify_by_json(procout, res, tidx, args, pm): + try: + outputJSON = json.loads(procout) + except json.JSONDecodeError: + res.set_result(ResultState.fail) + res.set_failmsg('Cannot decode verify command\'s output. Is it JSON?') + return res + + matchJSON = json.loads(json.dumps(tidx['matchJSON'])) + + if type(outputJSON) != type(matchJSON): + failmsg = 'Original output and matchJSON value are not the same type: output: {} != matchJSON: {} ' + failmsg = failmsg.format(type(outputJSON).__name__, type(matchJSON).__name__) + res.set_result(ResultState.fail) + res.set_failmsg(failmsg) + return res + + if len(matchJSON) > len(outputJSON): + failmsg = "Your matchJSON value is an array, and it contains more elements than the command under test\'s output:\ncommand output (length: {}):\n{}\nmatchJSON value (length: {}):\n{}" + failmsg = failmsg.format(len(outputJSON), outputJSON, len(matchJSON), matchJSON) + res.set_result(ResultState.fail) + res.set_failmsg(failmsg) + return res + res = find_in_json(res, outputJSON, matchJSON, 0) + + return res + +def find_in_json(res, outputJSONVal, matchJSONVal, matchJSONKey=None): + if res.get_result() == ResultState.fail: + return res + + if type(matchJSONVal) == list: + res = find_in_json_list(res, outputJSONVal, matchJSONVal, matchJSONKey) + + elif type(matchJSONVal) == dict: + res = find_in_json_dict(res, outputJSONVal, matchJSONVal) + else: + res = find_in_json_other(res, outputJSONVal, matchJSONVal, matchJSONKey) + + if res.get_result() != ResultState.fail: + res.set_result(ResultState.success) + return res + + return res + +def find_in_json_list(res, outputJSONVal, matchJSONVal, matchJSONKey=None): + if (type(matchJSONVal) != type(outputJSONVal)): + failmsg = 'Original output and matchJSON value are not the same type: output: {} != matchJSON: {}' + failmsg = failmsg.format(outputJSONVal, matchJSONVal) + res.set_result(ResultState.fail) + res.set_failmsg(failmsg) + return res + + if len(matchJSONVal) > len(outputJSONVal): + failmsg = "Your matchJSON value is an array, and it contains more elements than the command under test\'s output:\ncommand output (length: {}):\n{}\nmatchJSON value (length: {}):\n{}" + failmsg = failmsg.format(len(outputJSONVal), outputJSONVal, len(matchJSONVal), matchJSONVal) + res.set_result(ResultState.fail) + res.set_failmsg(failmsg) + return res + + for matchJSONIdx, matchJSONVal in enumerate(matchJSONVal): + res = find_in_json(res, outputJSONVal[matchJSONIdx], matchJSONVal, + matchJSONKey) + return res + +def find_in_json_dict(res, outputJSONVal, matchJSONVal): + for matchJSONKey, matchJSONVal in matchJSONVal.items(): + if type(outputJSONVal) == dict: + if matchJSONKey not in outputJSONVal: + failmsg = 'Key not found in json output: {}: {}\nMatching against output: {}' + failmsg = failmsg.format(matchJSONKey, matchJSONVal, outputJSONVal) + res.set_result(ResultState.fail) + res.set_failmsg(failmsg) + return res + + else: + failmsg = 'Original output and matchJSON value are not the same type: output: {} != matchJSON: {}' + failmsg = failmsg.format(type(outputJSON).__name__, type(matchJSON).__name__) + res.set_result(ResultState.fail) + res.set_failmsg(failmsg) + return rest + + if type(outputJSONVal) == dict and (type(outputJSONVal[matchJSONKey]) == dict or + type(outputJSONVal[matchJSONKey]) == list): + if len(matchJSONVal) > 0: + res = find_in_json(res, outputJSONVal[matchJSONKey], matchJSONVal, matchJSONKey) + # handling corner case where matchJSONVal == [] or matchJSONVal == {} + else: + res = find_in_json_other(res, outputJSONVal, matchJSONVal, matchJSONKey) + else: + res = find_in_json(res, outputJSONVal, matchJSONVal, matchJSONKey) + return res + +def find_in_json_other(res, outputJSONVal, matchJSONVal, matchJSONKey=None): + if matchJSONKey in outputJSONVal: + if matchJSONVal != outputJSONVal[matchJSONKey]: + failmsg = 'Value doesn\'t match: {}: {} != {}\nMatching against output: {}' + failmsg = failmsg.format(matchJSONKey, matchJSONVal, outputJSONVal[matchJSONKey], outputJSONVal) + res.set_result(ResultState.fail) + res.set_failmsg(failmsg) + return res + + return res + def run_one_test(pm, args, index, tidx): global NAMES result = True @@ -292,16 +396,22 @@ def run_one_test(pm, args, index, tidx): else: if args.verbose > 0: print('-----> verify stage') - match_pattern = re.compile( - str(tidx["matchPattern"]), re.DOTALL | re.MULTILINE) (p, procout) = exec_cmd(args, pm, 'verify', tidx["verifyCmd"]) if procout: - match_index = re.findall(match_pattern, procout) - if len(match_index) != int(tidx["matchCount"]): - res.set_result(ResultState.fail) - res.set_failmsg('Could not match regex pattern. Verify command output:\n{}'.format(procout)) + if 'matchJSON' in tidx: + verify_by_json(procout, res, tidx, args, pm) + elif 'matchPattern' in tidx: + match_pattern = re.compile( + str(tidx["matchPattern"]), re.DOTALL | re.MULTILINE) + match_index = re.findall(match_pattern, procout) + if len(match_index) != int(tidx["matchCount"]): + res.set_result(ResultState.fail) + res.set_failmsg('Could not match regex pattern. Verify command output:\n{}'.format(procout)) + else: + res.set_result(ResultState.success) else: - res.set_result(ResultState.success) + res.set_result(ResultState.fail) + res.set_failmsg('Must specify a match option: matchJSON or matchPattern\n{}'.format(procout)) elif int(tidx["matchCount"]) != 0: res.set_result(ResultState.fail) res.set_failmsg('No output generated by verify command.') @@ -365,6 +475,7 @@ def test_runner(pm, args, filtered_tests): res.set_result(ResultState.skip) res.set_errormsg(errmsg) tsr.add_resultdata(res) + index += 1 continue try: badtest = tidx # in case it goes bad From 330543d04f2c9c5924736d81f67b50b925bd0864 Mon Sep 17 00:00:00 2001 From: Junxiao Chang Date: Tue, 25 Oct 2022 16:17:47 +0800 Subject: [PATCH 0400/2002] net: stmmac: remove duplicate dma queue channel macros It doesn't need extra macros for queue 0 & 4. Same macro could be used for all 8 queues. Related queue/channel functions could be combined together. Original macro which has two same parameters is unsafe macro and might have potential side effects. Each MTL RxQ DMA channel mask is 4 bits, so using (0xf << chan) instead of GENMASK(x + 3, x) to avoid unsafe macro. Signed-off-by: Junxiao Chang Link: https://lore.kernel.org/r/20221025081747.1884926-1-junxiao.chang@intel.com Signed-off-by: Paolo Abeni --- drivers/net/ethernet/stmicro/stmmac/dwmac4.h | 4 +--- .../net/ethernet/stmicro/stmmac/dwmac4_core.c | 21 ++++++------------- 2 files changed, 7 insertions(+), 18 deletions(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4.h b/drivers/net/ethernet/stmicro/stmmac/dwmac4.h index 71dad409f78b..ccd49346d3b3 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac4.h +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4.h @@ -331,9 +331,7 @@ enum power_event { #define MTL_RXQ_DMA_MAP0 0x00000c30 /* queue 0 to 3 */ #define MTL_RXQ_DMA_MAP1 0x00000c34 /* queue 4 to 7 */ -#define MTL_RXQ_DMA_Q04MDMACH_MASK GENMASK(3, 0) -#define MTL_RXQ_DMA_Q04MDMACH(x) ((x) << 0) -#define MTL_RXQ_DMA_QXMDMACH_MASK(x) GENMASK(11 + (8 * ((x) - 1)), 8 * (x)) +#define MTL_RXQ_DMA_QXMDMACH_MASK(x) (0xf << 8 * (x)) #define MTL_RXQ_DMA_QXMDMACH(chan, q) ((chan) << (8 * (q))) #define MTL_CHAN_BASE_ADDR 0x00000d00 diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c b/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c index c25bfecb4a2d..513f6ea335a8 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c @@ -214,26 +214,17 @@ static void dwmac4_map_mtl_dma(struct mac_device_info *hw, u32 queue, u32 chan) void __iomem *ioaddr = hw->pcsr; u32 value; - if (queue < 4) + if (queue < 4) { value = readl(ioaddr + MTL_RXQ_DMA_MAP0); - else - value = readl(ioaddr + MTL_RXQ_DMA_MAP1); - - if (queue == 0 || queue == 4) { - value &= ~MTL_RXQ_DMA_Q04MDMACH_MASK; - value |= MTL_RXQ_DMA_Q04MDMACH(chan); - } else if (queue > 4) { - value &= ~MTL_RXQ_DMA_QXMDMACH_MASK(queue - 4); - value |= MTL_RXQ_DMA_QXMDMACH(chan, queue - 4); - } else { value &= ~MTL_RXQ_DMA_QXMDMACH_MASK(queue); value |= MTL_RXQ_DMA_QXMDMACH(chan, queue); - } - - if (queue < 4) writel(value, ioaddr + MTL_RXQ_DMA_MAP0); - else + } else { + value = readl(ioaddr + MTL_RXQ_DMA_MAP1); + value &= ~MTL_RXQ_DMA_QXMDMACH_MASK(queue - 4); + value |= MTL_RXQ_DMA_QXMDMACH(chan, queue - 4); writel(value, ioaddr + MTL_RXQ_DMA_MAP1); + } } static void dwmac4_config_cbs(struct mac_device_info *hw, From 28581b9c2c94cc912354eadc98c1146fdc7092e6 Mon Sep 17 00:00:00 2001 From: Tariq Toukan Date: Tue, 25 Oct 2022 13:53:00 +0300 Subject: [PATCH 0401/2002] bond: Disable TLS features indication Bond agnostically interacts with TLS device-offload requests via the .ndo_sk_get_lower_dev operation. Return value is true iff bond guarantees fixed mapping between the TLS connection and a lower netdev. Due to this nature, the bond TLS device offload features are not explicitly controllable in the bond layer. As of today, these are read-only values based on the evaluation of bond_sk_check(). However, this indication might be incorrect and misleading, when the feature bits are "fixed" by some dependency features. For example, NETIF_F_HW_TLS_TX/RX are forcefully cleared in case the corresponding checksum offload is disabled. But in fact the bond ability to still offload TLS connections to the lower device is not hurt. This means that these bits can not be trusted, and hence better become unused. This patch revives some old discussion [1] and proposes a much simpler solution: Clear the bond's TLS features bits. Everyone should stop reading them. [1] https://lore.kernel.org/netdev/20210526095747.22446-1-tariqt@nvidia.com/ Signed-off-by: Tariq Toukan Reviewed-by: Gal Pressman Acked-by: Jakub Kicinski Link: https://lore.kernel.org/r/20221025105300.4718-1-tariqt@nvidia.com Signed-off-by: Paolo Abeni --- drivers/net/bonding/bond_main.c | 13 +------------ drivers/net/bonding/bond_options.c | 18 ------------------ include/net/bonding.h | 4 ---- 3 files changed, 1 insertion(+), 34 deletions(-) diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index e84c49bf4d0c..1cd4e71916f8 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -307,7 +307,7 @@ netdev_tx_t bond_dev_queue_xmit(struct bonding *bond, struct sk_buff *skb, return dev_queue_xmit(skb); } -bool bond_sk_check(struct bonding *bond) +static bool bond_sk_check(struct bonding *bond) { switch (BOND_MODE(bond)) { case BOND_MODE_8023AD: @@ -1398,13 +1398,6 @@ static netdev_features_t bond_fix_features(struct net_device *dev, netdev_features_t mask; struct slave *slave; -#if IS_ENABLED(CONFIG_TLS_DEVICE) - if (bond_sk_check(bond)) - features |= BOND_TLS_FEATURES; - else - features &= ~BOND_TLS_FEATURES; -#endif - mask = features; features &= ~NETIF_F_ONE_FOR_ALL; @@ -5806,10 +5799,6 @@ void bond_setup(struct net_device *bond_dev) if (BOND_MODE(bond) == BOND_MODE_ACTIVEBACKUP) bond_dev->features |= BOND_XFRM_FEATURES; #endif /* CONFIG_XFRM_OFFLOAD */ -#if IS_ENABLED(CONFIG_TLS_DEVICE) - if (bond_sk_check(bond)) - bond_dev->features |= BOND_TLS_FEATURES; -#endif } /* Destroy a bonding device. diff --git a/drivers/net/bonding/bond_options.c b/drivers/net/bonding/bond_options.c index 3498db1c1b3c..f71d5517f829 100644 --- a/drivers/net/bonding/bond_options.c +++ b/drivers/net/bonding/bond_options.c @@ -842,19 +842,6 @@ static bool bond_set_xfrm_features(struct bonding *bond) return true; } -static bool bond_set_tls_features(struct bonding *bond) -{ - if (!IS_ENABLED(CONFIG_TLS_DEVICE)) - return false; - - if (bond_sk_check(bond)) - bond->dev->wanted_features |= BOND_TLS_FEATURES; - else - bond->dev->wanted_features &= ~BOND_TLS_FEATURES; - - return true; -} - static int bond_option_mode_set(struct bonding *bond, const struct bond_opt_value *newval) { @@ -885,7 +872,6 @@ static int bond_option_mode_set(struct bonding *bond, bool update = false; update |= bond_set_xfrm_features(bond); - update |= bond_set_tls_features(bond); if (update) netdev_update_features(bond->dev); @@ -1418,10 +1404,6 @@ static int bond_option_xmit_hash_policy_set(struct bonding *bond, newval->string, newval->value); bond->params.xmit_policy = newval->value; - if (bond->dev->reg_state == NETREG_REGISTERED) - if (bond_set_tls_features(bond)) - netdev_update_features(bond->dev); - return 0; } diff --git a/include/net/bonding.h b/include/net/bonding.h index e999f851738b..ea36ab7f9e72 100644 --- a/include/net/bonding.h +++ b/include/net/bonding.h @@ -92,8 +92,6 @@ #define BOND_XFRM_FEATURES (NETIF_F_HW_ESP | NETIF_F_HW_ESP_TX_CSUM | \ NETIF_F_GSO_ESP) -#define BOND_TLS_FEATURES (NETIF_F_HW_TLS_TX | NETIF_F_HW_TLS_RX) - #ifdef CONFIG_NET_POLL_CONTROLLER extern atomic_t netpoll_block_tx; @@ -280,8 +278,6 @@ struct bond_vlan_tag { unsigned short vlan_id; }; -bool bond_sk_check(struct bonding *bond); - /** * Returns NULL if the net_device does not belong to any of the bond's slaves * From c926b4c3fa1fdce5e128bc954cad94ca16acce41 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Tue, 25 Oct 2022 09:01:09 -0300 Subject: [PATCH 0402/2002] net: dp83822: Print the SOR1 strap status During the bring-up of the Ethernet PHY, it is very useful to see the bootstrap status information, as it can help identifying hardware bootstrap mistakes. Allow printing the SOR1 register, which contains the strap status to ease the bring-up. Signed-off-by: Fabio Estevam Reviewed-by: Andrew Lunn Link: https://lore.kernel.org/r/20221025120109.779337-1-festevam@gmail.com Signed-off-by: Paolo Abeni --- drivers/net/phy/dp83822.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/phy/dp83822.c b/drivers/net/phy/dp83822.c index b60db8b6f477..a6f05e35d91f 100644 --- a/drivers/net/phy/dp83822.c +++ b/drivers/net/phy/dp83822.c @@ -524,6 +524,8 @@ static int dp83822_read_straps(struct phy_device *phydev) if (val < 0) return val; + phydev_dbg(phydev, "SOR1 strap register: 0x%04x\n", val); + fx_enabled = (val & DP83822_COL_STRAP_MASK) >> DP83822_COL_SHIFT; if (fx_enabled == DP83822_STRAP_MODE2 || fx_enabled == DP83822_STRAP_MODE3) From 3a1cc23a75abcd9cea585eb84846507363d58397 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= Date: Tue, 25 Oct 2022 15:22:45 +0200 Subject: [PATCH 0403/2002] net: broadcom: bcm4908_enet: use build_skb() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit RX code can be more efficient with the build_skb(). Allocating actual SKB around eth packet buffer - right before passing it up - results in a better cache usage. Without RPS (echo 0 > rps_cpus) BCM4908 NAT masq performance "jumps" between two speeds: ~900 Mbps and 940 Mbps (it's a 4 CPUs SoC). This change bumps the lower speed from 905 Mb/s to 918 Mb/s (tested using single stream iperf 2.0.5 traffic). There are more optimizations to consider. One obvious to try is GRO however as BCM4908 doesn't do hw csum is may actually lower performance. Sometimes. Some early testing: ┌─────────────────────────────────┬─────────────────────┬────────────────────┐ │ │ netif_receive_skb() │ napi_gro_receive() │ ├─────────────────────────────────┼─────────────────────┼────────────────────┤ │ netdev_alloc_skb() │ 905 Mb/s │ 892 Mb/s │ │ napi_alloc_frag() + build_skb() │ 918 Mb/s │ 917 Mb/s │ └─────────────────────────────────┴─────────────────────┴────────────────────┘ Another ideas: 1. napi_build_skb() 2. skb_copy_from_linear_data() for small packets Those need proper testing first though. That can be done later. Signed-off-by: Rafał Miłecki Link: https://lore.kernel.org/r/20221025132245.22871-1-zajec5@gmail.com Signed-off-by: Paolo Abeni --- drivers/net/ethernet/broadcom/bcm4908_enet.c | 53 +++++++++++++------- 1 file changed, 36 insertions(+), 17 deletions(-) diff --git a/drivers/net/ethernet/broadcom/bcm4908_enet.c b/drivers/net/ethernet/broadcom/bcm4908_enet.c index 93ccf549e2ed..ca8c86ee44c0 100644 --- a/drivers/net/ethernet/broadcom/bcm4908_enet.c +++ b/drivers/net/ethernet/broadcom/bcm4908_enet.c @@ -36,13 +36,24 @@ #define ENET_MAX_ETH_OVERHEAD (ETH_HLEN + BRCM_MAX_TAG_LEN + VLAN_HLEN + \ ETH_FCS_LEN + 4) /* 32 */ +#define ENET_RX_SKB_BUF_SIZE (NET_SKB_PAD + NET_IP_ALIGN + \ + ETH_HLEN + BRCM_MAX_TAG_LEN + VLAN_HLEN + \ + ENET_MTU_MAX + ETH_FCS_LEN + 4) +#define ENET_RX_SKB_BUF_ALLOC_SIZE (SKB_DATA_ALIGN(ENET_RX_SKB_BUF_SIZE) + \ + SKB_DATA_ALIGN(sizeof(struct skb_shared_info))) +#define ENET_RX_BUF_DMA_OFFSET (NET_SKB_PAD + NET_IP_ALIGN) +#define ENET_RX_BUF_DMA_SIZE (ENET_RX_SKB_BUF_SIZE - ENET_RX_BUF_DMA_OFFSET) + struct bcm4908_enet_dma_ring_bd { __le32 ctl; __le32 addr; } __packed; struct bcm4908_enet_dma_ring_slot { - struct sk_buff *skb; + union { + void *buf; /* RX */ + struct sk_buff *skb; /* TX */ + }; unsigned int len; dma_addr_t dma_addr; }; @@ -260,22 +271,21 @@ static int bcm4908_enet_dma_alloc_rx_buf(struct bcm4908_enet *enet, unsigned int u32 tmp; int err; - slot->len = ENET_MTU_MAX + ENET_MAX_ETH_OVERHEAD; - - slot->skb = netdev_alloc_skb(enet->netdev, slot->len); - if (!slot->skb) + slot->buf = napi_alloc_frag(ENET_RX_SKB_BUF_ALLOC_SIZE); + if (!slot->buf) return -ENOMEM; - slot->dma_addr = dma_map_single(dev, slot->skb->data, slot->len, DMA_FROM_DEVICE); + slot->dma_addr = dma_map_single(dev, slot->buf + ENET_RX_BUF_DMA_OFFSET, + ENET_RX_BUF_DMA_SIZE, DMA_FROM_DEVICE); err = dma_mapping_error(dev, slot->dma_addr); if (err) { dev_err(dev, "Failed to map DMA buffer: %d\n", err); - kfree_skb(slot->skb); - slot->skb = NULL; + skb_free_frag(slot->buf); + slot->buf = NULL; return err; } - tmp = slot->len << DMA_CTL_LEN_DESC_BUFLENGTH_SHIFT; + tmp = ENET_RX_BUF_DMA_SIZE << DMA_CTL_LEN_DESC_BUFLENGTH_SHIFT; tmp |= DMA_CTL_STATUS_OWN; if (idx == enet->rx_ring.length - 1) tmp |= DMA_CTL_STATUS_WRAP; @@ -315,11 +325,11 @@ static void bcm4908_enet_dma_uninit(struct bcm4908_enet *enet) for (i = rx_ring->length - 1; i >= 0; i--) { slot = &rx_ring->slots[i]; - if (!slot->skb) + if (!slot->buf) continue; dma_unmap_single(dev, slot->dma_addr, slot->len, DMA_FROM_DEVICE); - kfree_skb(slot->skb); - slot->skb = NULL; + skb_free_frag(slot->buf); + slot->buf = NULL; } } @@ -577,6 +587,7 @@ static int bcm4908_enet_poll_rx(struct napi_struct *napi, int weight) while (handled < weight) { struct bcm4908_enet_dma_ring_bd *buf_desc; struct bcm4908_enet_dma_ring_slot slot; + struct sk_buff *skb; u32 ctl; int len; int err; @@ -600,16 +611,24 @@ static int bcm4908_enet_poll_rx(struct napi_struct *napi, int weight) if (len < ETH_ZLEN || (ctl & (DMA_CTL_STATUS_SOP | DMA_CTL_STATUS_EOP)) != (DMA_CTL_STATUS_SOP | DMA_CTL_STATUS_EOP)) { - kfree_skb(slot.skb); + skb_free_frag(slot.buf); enet->netdev->stats.rx_dropped++; break; } - dma_unmap_single(dev, slot.dma_addr, slot.len, DMA_FROM_DEVICE); + dma_unmap_single(dev, slot.dma_addr, ENET_RX_BUF_DMA_SIZE, DMA_FROM_DEVICE); - skb_put(slot.skb, len - ETH_FCS_LEN); - slot.skb->protocol = eth_type_trans(slot.skb, enet->netdev); - netif_receive_skb(slot.skb); + skb = build_skb(slot.buf, ENET_RX_SKB_BUF_ALLOC_SIZE); + if (unlikely(!skb)) { + skb_free_frag(slot.buf); + enet->netdev->stats.rx_dropped++; + break; + } + skb_reserve(skb, ENET_RX_BUF_DMA_OFFSET); + skb_put(skb, len - ETH_FCS_LEN); + skb->protocol = eth_type_trans(skb, enet->netdev); + + netif_receive_skb(skb); enet->netdev->stats.rx_packets++; enet->netdev->stats.rx_bytes += len; From e753df8fbca592d36f539ed950fcdf412166c549 Mon Sep 17 00:00:00 2001 From: Michal Jaron Date: Tue, 25 Oct 2022 09:12:52 -0700 Subject: [PATCH 0404/2002] ice: Add support Flex RXD Add new VIRTCHNL_VF_OFFLOAD_RX_FLEX_DESC flag, opcode VIRTCHNL_OP_GET_SUPPORTED_RXDIDS and add member rxdid in struct virtchnl_rxq_info to support AVF Flex RXD extension. Add support to allow VF to query flexible descriptor RXDIDs supported by DDP package and configure Rx queues with selected RXDID for IAVF. Add code to allow VIRTCHNL_OP_GET_SUPPORTED_RXDIDS message to be processed. Add necessary macros for registers. Signed-off-by: Leyi Rong Signed-off-by: Xu Ting Signed-off-by: Michal Jaron Signed-off-by: Mateusz Palczewski Tested-by: Maxime Coquelin Tested-by: Konrad Jankowski Signed-off-by: Jacob Keller Link: https://lore.kernel.org/r/20221025161252.1952939-1-jacob.e.keller@intel.com Signed-off-by: Paolo Abeni --- drivers/net/ethernet/intel/ice/ice.h | 2 + .../net/ethernet/intel/ice/ice_hw_autogen.h | 3 + drivers/net/ethernet/intel/ice/ice_virtchnl.c | 86 +++++++++++++++++++ drivers/net/ethernet/intel/ice/ice_virtchnl.h | 2 + .../intel/ice/ice_virtchnl_allowlist.c | 6 ++ include/linux/avf/virtchnl.h | 14 ++- 6 files changed, 111 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/intel/ice/ice.h b/drivers/net/ethernet/intel/ice/ice.h index d75c8d11f867..f88ee051e71c 100644 --- a/drivers/net/ethernet/intel/ice/ice.h +++ b/drivers/net/ethernet/intel/ice/ice.h @@ -609,6 +609,8 @@ struct ice_pf { u16 num_dmac_chnl_fltrs; struct hlist_head tc_flower_fltr_list; + u64 supported_rxdids; + __le64 nvm_phy_type_lo; /* NVM PHY type low */ __le64 nvm_phy_type_hi; /* NVM PHY type high */ struct ice_link_default_override_tlv link_dflt_override; diff --git a/drivers/net/ethernet/intel/ice/ice_hw_autogen.h b/drivers/net/ethernet/intel/ice/ice_hw_autogen.h index d16738a3d3a7..a92dc9a16035 100644 --- a/drivers/net/ethernet/intel/ice/ice_hw_autogen.h +++ b/drivers/net/ethernet/intel/ice/ice_hw_autogen.h @@ -110,6 +110,9 @@ #define PRTDCB_TUP2TC 0x001D26C0 #define GL_PREEXT_L2_PMASK0(_i) (0x0020F0FC + ((_i) * 4)) #define GL_PREEXT_L2_PMASK1(_i) (0x0020F108 + ((_i) * 4)) +#define GLFLXP_RXDID_FLAGS(_i, _j) (0x0045D000 + ((_i) * 4 + (_j) * 256)) +#define GLFLXP_RXDID_FLAGS_FLEXIFLAG_4N_S 0 +#define GLFLXP_RXDID_FLAGS_FLEXIFLAG_4N_M ICE_M(0x3F, 0) #define GLFLXP_RXDID_FLX_WRD_0(_i) (0x0045c800 + ((_i) * 4)) #define GLFLXP_RXDID_FLX_WRD_0_PROT_MDID_S 0 #define GLFLXP_RXDID_FLX_WRD_0_PROT_MDID_M ICE_M(0xFF, 0) diff --git a/drivers/net/ethernet/intel/ice/ice_virtchnl.c b/drivers/net/ethernet/intel/ice/ice_virtchnl.c index 2b4c791b6cba..c1fa94381f4e 100644 --- a/drivers/net/ethernet/intel/ice/ice_virtchnl.c +++ b/drivers/net/ethernet/intel/ice/ice_virtchnl.c @@ -462,6 +462,9 @@ static int ice_vc_get_vf_res_msg(struct ice_vf *vf, u8 *msg) vfres->vf_cap_flags |= VIRTCHNL_VF_OFFLOAD_RSS_REG; } + if (vf->driver_caps & VIRTCHNL_VF_OFFLOAD_RX_FLEX_DESC) + vfres->vf_cap_flags |= VIRTCHNL_VF_OFFLOAD_RX_FLEX_DESC; + if (vf->driver_caps & VIRTCHNL_VF_OFFLOAD_FDIR_PF) vfres->vf_cap_flags |= VIRTCHNL_VF_OFFLOAD_FDIR_PF; @@ -1618,6 +1621,9 @@ static int ice_vc_cfg_qs_msg(struct ice_vf *vf, u8 *msg) } for (i = 0; i < qci->num_queue_pairs; i++) { + struct ice_hw *hw; + u32 rxdid; + u16 pf_q; qpi = &qci->qpair[i]; if (qpi->txq.vsi_id != qci->vsi_id || qpi->rxq.vsi_id != qci->vsi_id || @@ -1686,6 +1692,25 @@ static int ice_vc_cfg_qs_msg(struct ice_vf *vf, u8 *msg) goto error_param; } } + + /* VF Rx queue RXDID configuration */ + pf_q = vsi->rxq_map[qpi->rxq.queue_id]; + rxdid = qpi->rxq.rxdid; + hw = &vsi->back->hw; + + /* If Rx flex desc is supported, select RXDID for Rx queues. + * Otherwise, use legacy 32byte descriptor format. + * Legacy 16byte descriptor is not supported. If this RXDID + * is selected, return error. + */ + if (vf->driver_caps & VIRTCHNL_VF_OFFLOAD_RX_FLEX_DESC) { + if (!(BIT(rxdid) & pf->supported_rxdids)) + goto error_param; + } else { + rxdid = ICE_RXDID_LEGACY_1; + } + + ice_write_qrxflxp_cntxt(hw, pf_q, rxdid, 0x03, false); } /* send the response to the VF */ @@ -2456,6 +2481,62 @@ error_param: v_ret, NULL, 0); } +/** + * ice_vc_query_rxdid - query RXDID supported by DDP package + * @vf: pointer to VF info + * + * Called from VF to query a bitmap of supported flexible + * descriptor RXDIDs of a DDP package. + */ +static int ice_vc_query_rxdid(struct ice_vf *vf) +{ + enum virtchnl_status_code v_ret = VIRTCHNL_STATUS_SUCCESS; + struct virtchnl_supported_rxdids *rxdid = NULL; + struct ice_hw *hw = &vf->pf->hw; + struct ice_pf *pf = vf->pf; + int len = 0; + int ret, i; + u32 regval; + + if (!test_bit(ICE_VF_STATE_ACTIVE, vf->vf_states)) { + v_ret = VIRTCHNL_STATUS_ERR_PARAM; + goto err; + } + + if (!(vf->driver_caps & VIRTCHNL_VF_OFFLOAD_RX_FLEX_DESC)) { + v_ret = VIRTCHNL_STATUS_ERR_PARAM; + goto err; + } + + len = sizeof(struct virtchnl_supported_rxdids); + rxdid = kzalloc(len, GFP_KERNEL); + if (!rxdid) { + v_ret = VIRTCHNL_STATUS_ERR_NO_MEMORY; + len = 0; + goto err; + } + + /* Read flexiflag registers to determine whether the + * corresponding RXDID is configured and supported or not. + * Since Legacy 16byte descriptor format is not supported, + * start from Legacy 32byte descriptor. + */ + for (i = ICE_RXDID_LEGACY_1; i < ICE_FLEX_DESC_RXDID_MAX_NUM; i++) { + regval = rd32(hw, GLFLXP_RXDID_FLAGS(i, 0)); + if ((regval >> GLFLXP_RXDID_FLAGS_FLEXIFLAG_4N_S) + & GLFLXP_RXDID_FLAGS_FLEXIFLAG_4N_M) + rxdid->supported_rxdids |= BIT(i); + } + + pf->supported_rxdids = rxdid->supported_rxdids; + +err: + ret = ice_vc_send_msg_to_vf(vf, VIRTCHNL_OP_GET_SUPPORTED_RXDIDS, + v_ret, (u8 *)rxdid, len); + kfree(rxdid); + return ret; +} + /** * ice_vf_init_vlan_stripping - enable/disable VLAN stripping on initialization * @vf: VF to enable/disable VLAN stripping for on initialization @@ -3490,6 +3571,7 @@ static const struct ice_virtchnl_ops ice_virtchnl_dflt_ops = { .cfg_promiscuous_mode_msg = ice_vc_cfg_promiscuous_mode_msg, .add_vlan_msg = ice_vc_add_vlan_msg, .remove_vlan_msg = ice_vc_remove_vlan_msg, + .query_rxdid = ice_vc_query_rxdid, .ena_vlan_stripping = ice_vc_ena_vlan_stripping, .dis_vlan_stripping = ice_vc_dis_vlan_stripping, .handle_rss_cfg_msg = ice_vc_handle_rss_cfg, @@ -3624,6 +3706,7 @@ static const struct ice_virtchnl_ops ice_virtchnl_repr_ops = { .cfg_promiscuous_mode_msg = ice_vc_repr_cfg_promiscuous_mode, .add_vlan_msg = ice_vc_add_vlan_msg, .remove_vlan_msg = ice_vc_remove_vlan_msg, + .query_rxdid = ice_vc_query_rxdid, .ena_vlan_stripping = ice_vc_ena_vlan_stripping, .dis_vlan_stripping = ice_vc_dis_vlan_stripping, .handle_rss_cfg_msg = ice_vc_handle_rss_cfg, @@ -3764,6 +3847,9 @@ error_handler: case VIRTCHNL_OP_DEL_VLAN: err = ops->remove_vlan_msg(vf, msg); break; + case VIRTCHNL_OP_GET_SUPPORTED_RXDIDS: + err = ops->query_rxdid(vf); + break; case VIRTCHNL_OP_ENABLE_VLAN_STRIPPING: err = ops->ena_vlan_stripping(vf); break; diff --git a/drivers/net/ethernet/intel/ice/ice_virtchnl.h b/drivers/net/ethernet/intel/ice/ice_virtchnl.h index b5a3fd8adbb4..4867a92ebefb 100644 --- a/drivers/net/ethernet/intel/ice/ice_virtchnl.h +++ b/drivers/net/ethernet/intel/ice/ice_virtchnl.h @@ -17,6 +17,7 @@ * broadcast, and 16 for additional unicast/multicast filters */ #define ICE_MAX_MACADDR_PER_VF 18 +#define ICE_FLEX_DESC_RXDID_MAX_NUM 64 struct ice_virtchnl_ops { int (*get_ver_msg)(struct ice_vf *vf, u8 *msg); @@ -35,6 +36,7 @@ struct ice_virtchnl_ops { int (*cfg_promiscuous_mode_msg)(struct ice_vf *vf, u8 *msg); int (*add_vlan_msg)(struct ice_vf *vf, u8 *msg); int (*remove_vlan_msg)(struct ice_vf *vf, u8 *msg); + int (*query_rxdid)(struct ice_vf *vf); int (*ena_vlan_stripping)(struct ice_vf *vf); int (*dis_vlan_stripping)(struct ice_vf *vf); int (*handle_rss_cfg_msg)(struct ice_vf *vf, u8 *msg, bool add); diff --git a/drivers/net/ethernet/intel/ice/ice_virtchnl_allowlist.c b/drivers/net/ethernet/intel/ice/ice_virtchnl_allowlist.c index 5a82216e7d03..7d547fa616fa 100644 --- a/drivers/net/ethernet/intel/ice/ice_virtchnl_allowlist.c +++ b/drivers/net/ethernet/intel/ice/ice_virtchnl_allowlist.c @@ -70,6 +70,11 @@ static const u32 rss_pf_allowlist_opcodes[] = { VIRTCHNL_OP_GET_RSS_HENA_CAPS, VIRTCHNL_OP_SET_RSS_HENA, }; +/* VIRTCHNL_VF_OFFLOAD_RX_FLEX_DESC */ +static const u32 rx_flex_desc_allowlist_opcodes[] = { + VIRTCHNL_OP_GET_SUPPORTED_RXDIDS, +}; + /* VIRTCHNL_VF_OFFLOAD_ADV_RSS_PF */ static const u32 adv_rss_pf_allowlist_opcodes[] = { VIRTCHNL_OP_ADD_RSS_CFG, VIRTCHNL_OP_DEL_RSS_CFG, @@ -96,6 +101,7 @@ static const struct allowlist_opcode_info allowlist_opcodes[] = { ALLOW_ITEM(VIRTCHNL_VF_OFFLOAD_REQ_QUEUES, req_queues_allowlist_opcodes), ALLOW_ITEM(VIRTCHNL_VF_OFFLOAD_VLAN, vlan_allowlist_opcodes), ALLOW_ITEM(VIRTCHNL_VF_OFFLOAD_RSS_PF, rss_pf_allowlist_opcodes), + ALLOW_ITEM(VIRTCHNL_VF_OFFLOAD_RX_FLEX_DESC, rx_flex_desc_allowlist_opcodes), ALLOW_ITEM(VIRTCHNL_VF_OFFLOAD_ADV_RSS_PF, adv_rss_pf_allowlist_opcodes), ALLOW_ITEM(VIRTCHNL_VF_OFFLOAD_FDIR_PF, fdir_pf_allowlist_opcodes), ALLOW_ITEM(VIRTCHNL_VF_OFFLOAD_VLAN_V2, vlan_v2_allowlist_opcodes), diff --git a/include/linux/avf/virtchnl.h b/include/linux/avf/virtchnl.h index 2ce27e8e4f19..d91af50ac58d 100644 --- a/include/linux/avf/virtchnl.h +++ b/include/linux/avf/virtchnl.h @@ -136,7 +136,8 @@ enum virtchnl_ops { VIRTCHNL_OP_DISABLE_CHANNELS = 31, VIRTCHNL_OP_ADD_CLOUD_FILTER = 32, VIRTCHNL_OP_DEL_CLOUD_FILTER = 33, - /* opcode 34 - 44 are reserved */ + /* opcode 34 - 43 are reserved */ + VIRTCHNL_OP_GET_SUPPORTED_RXDIDS = 44, VIRTCHNL_OP_ADD_RSS_CFG = 45, VIRTCHNL_OP_DEL_RSS_CFG = 46, VIRTCHNL_OP_ADD_FDIR_FILTER = 47, @@ -263,6 +264,7 @@ VIRTCHNL_CHECK_STRUCT_LEN(16, virtchnl_vsi_resource); #define VIRTCHNL_VF_OFFLOAD_RX_ENCAP_CSUM BIT(22) #define VIRTCHNL_VF_OFFLOAD_ADQ BIT(23) #define VIRTCHNL_VF_OFFLOAD_USO BIT(25) +#define VIRTCHNL_VF_OFFLOAD_RX_FLEX_DESC BIT(26) #define VIRTCHNL_VF_OFFLOAD_ADV_RSS_PF BIT(27) #define VIRTCHNL_VF_OFFLOAD_FDIR_PF BIT(28) @@ -318,7 +320,9 @@ struct virtchnl_rxq_info { u16 splithdr_enabled; /* deprecated with AVF 1.0 */ u32 databuffer_size; u32 max_pkt_size; - u32 pad1; + u8 pad0; + u8 rxdid; + u8 pad1[2]; u64 dma_ring_addr; enum virtchnl_rx_hsplit rx_split_pos; /* deprecated with AVF 1.0 */ u32 pad2; @@ -970,6 +974,10 @@ struct virtchnl_filter { VIRTCHNL_CHECK_STRUCT_LEN(272, virtchnl_filter); +struct virtchnl_supported_rxdids { + u64 supported_rxdids; +}; + /* VIRTCHNL_OP_EVENT * PF sends this message to inform the VF driver of events that may affect it. * No direct response is expected from the VF, though it may generate other @@ -1499,6 +1507,8 @@ virtchnl_vc_validate_vf_msg(struct virtchnl_version_info *ver, u32 v_opcode, case VIRTCHNL_OP_DEL_CLOUD_FILTER: valid_len = sizeof(struct virtchnl_filter); break; + case VIRTCHNL_OP_GET_SUPPORTED_RXDIDS: + break; case VIRTCHNL_OP_ADD_RSS_CFG: case VIRTCHNL_OP_DEL_RSS_CFG: valid_len = sizeof(struct virtchnl_rss_cfg); From d5e2d038dbece821f1af57acbeded3aa9a1832c1 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Tue, 25 Oct 2022 11:42:54 -0700 Subject: [PATCH 0405/2002] eth: fealnx: delete the driver for Myson MTD-800 The git history for this driver seems to be completely automated / tree wide changes. I can't find any boards or systems which would use this chip. Google search shows pictures of towel warmers and no networking products. Signed-off-by: Jakub Kicinski Reviewed-by: Leon Romanovsky Link: https://lore.kernel.org/r/20221025184254.1717982-1-kuba@kernel.org Signed-off-by: Paolo Abeni --- arch/mips/configs/mtx1_defconfig | 1 - arch/powerpc/configs/ppc6xx_defconfig | 1 - drivers/net/ethernet/Kconfig | 10 - drivers/net/ethernet/Makefile | 1 - drivers/net/ethernet/fealnx.c | 1953 ------------------------- 5 files changed, 1966 deletions(-) delete mode 100644 drivers/net/ethernet/fealnx.c diff --git a/arch/mips/configs/mtx1_defconfig b/arch/mips/configs/mtx1_defconfig index edf9634aa8ee..89a1511d2ee4 100644 --- a/arch/mips/configs/mtx1_defconfig +++ b/arch/mips/configs/mtx1_defconfig @@ -284,7 +284,6 @@ CONFIG_IXGB=m CONFIG_SKGE=m CONFIG_SKY2=m CONFIG_MYRI10GE=m -CONFIG_FEALNX=m CONFIG_NATSEMI=m CONFIG_NS83820=m CONFIG_S2IO=m diff --git a/arch/powerpc/configs/ppc6xx_defconfig b/arch/powerpc/configs/ppc6xx_defconfig index d23deb94b36e..115d40be5481 100644 --- a/arch/powerpc/configs/ppc6xx_defconfig +++ b/arch/powerpc/configs/ppc6xx_defconfig @@ -461,7 +461,6 @@ CONFIG_MV643XX_ETH=m CONFIG_SKGE=m CONFIG_SKY2=m CONFIG_MYRI10GE=m -CONFIG_FEALNX=m CONFIG_NATSEMI=m CONFIG_NS83820=m CONFIG_PCMCIA_AXNET=m diff --git a/drivers/net/ethernet/Kconfig b/drivers/net/ethernet/Kconfig index 1917da784191..323ec56e8a74 100644 --- a/drivers/net/ethernet/Kconfig +++ b/drivers/net/ethernet/Kconfig @@ -132,16 +132,6 @@ source "drivers/net/ethernet/mscc/Kconfig" source "drivers/net/ethernet/microsoft/Kconfig" source "drivers/net/ethernet/moxa/Kconfig" source "drivers/net/ethernet/myricom/Kconfig" - -config FEALNX - tristate "Myson MTD-8xx PCI Ethernet support" - depends on PCI - select CRC32 - select MII - help - Say Y here to support the Myson MTD-800 family of PCI-based Ethernet - cards. - source "drivers/net/ethernet/ni/Kconfig" source "drivers/net/ethernet/natsemi/Kconfig" source "drivers/net/ethernet/neterion/Kconfig" diff --git a/drivers/net/ethernet/Makefile b/drivers/net/ethernet/Makefile index 0d872d4efcd1..2fedbaa545eb 100644 --- a/drivers/net/ethernet/Makefile +++ b/drivers/net/ethernet/Makefile @@ -64,7 +64,6 @@ obj-$(CONFIG_NET_VENDOR_MICROCHIP) += microchip/ obj-$(CONFIG_NET_VENDOR_MICROSEMI) += mscc/ obj-$(CONFIG_NET_VENDOR_MOXART) += moxa/ obj-$(CONFIG_NET_VENDOR_MYRI) += myricom/ -obj-$(CONFIG_FEALNX) += fealnx.o obj-$(CONFIG_NET_VENDOR_NATSEMI) += natsemi/ obj-$(CONFIG_NET_VENDOR_NETERION) += neterion/ obj-$(CONFIG_NET_VENDOR_NETRONOME) += netronome/ diff --git a/drivers/net/ethernet/fealnx.c b/drivers/net/ethernet/fealnx.c deleted file mode 100644 index ed18450fd2cc..000000000000 --- a/drivers/net/ethernet/fealnx.c +++ /dev/null @@ -1,1953 +0,0 @@ -/* - Written 1998-2000 by Donald Becker. - - This software may be used and distributed according to the terms of - the GNU General Public License (GPL), incorporated herein by reference. - Drivers based on or derived from this code fall under the GPL and must - retain the authorship, copyright and license notice. This file is not - a complete program and may only be used when the entire operating - system is licensed under the GPL. - - The author may be reached as becker@scyld.com, or C/O - Scyld Computing Corporation - 410 Severn Ave., Suite 210 - Annapolis MD 21403 - - Support information and updates available at - http://www.scyld.com/network/pci-skeleton.html - - Linux kernel updates: - - Version 2.51, Nov 17, 2001 (jgarzik): - - Add ethtool support - - Replace some MII-related magic numbers with constants - -*/ - -#define DRV_NAME "fealnx" - -static int debug; /* 1-> print debug message */ -static int max_interrupt_work = 20; - -/* Maximum number of multicast addresses to filter (vs. Rx-all-multicast). */ -static int multicast_filter_limit = 32; - -/* Set the copy breakpoint for the copy-only-tiny-frames scheme. */ -/* Setting to > 1518 effectively disables this feature. */ -static int rx_copybreak; - -/* Used to pass the media type, etc. */ -/* Both 'options[]' and 'full_duplex[]' should exist for driver */ -/* interoperability. */ -/* The media type is usually passed in 'options[]'. */ -#define MAX_UNITS 8 /* More are supported, limit only on options */ -static int options[MAX_UNITS] = { -1, -1, -1, -1, -1, -1, -1, -1 }; -static int full_duplex[MAX_UNITS] = { -1, -1, -1, -1, -1, -1, -1, -1 }; - -/* Operational parameters that are set at compile time. */ -/* Keep the ring sizes a power of two for compile efficiency. */ -/* The compiler will convert '%'<2^N> into a bit mask. */ -/* Making the Tx ring too large decreases the effectiveness of channel */ -/* bonding and packet priority. */ -/* There are no ill effects from too-large receive rings. */ -// 88-12-9 modify, -// #define TX_RING_SIZE 16 -// #define RX_RING_SIZE 32 -#define TX_RING_SIZE 6 -#define RX_RING_SIZE 12 -#define TX_TOTAL_SIZE TX_RING_SIZE*sizeof(struct fealnx_desc) -#define RX_TOTAL_SIZE RX_RING_SIZE*sizeof(struct fealnx_desc) - -/* Operational parameters that usually are not changed. */ -/* Time in jiffies before concluding the transmitter is hung. */ -#define TX_TIMEOUT (2*HZ) - -#define PKT_BUF_SZ 1536 /* Size of each temporary Rx buffer. */ - - -/* Include files, designed to support most kernel versions 2.0.0 and later. */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include /* Processor type for cache alignment. */ -#include -#include -#include - -/* This driver was written to use PCI memory space, however some x86 systems - work only with I/O space accesses. */ -#ifndef __alpha__ -#define USE_IO_OPS -#endif - -/* Kernel compatibility defines, some common to David Hinds' PCMCIA package. */ -/* This is only in the support-all-kernels source code. */ - -#define RUN_AT(x) (jiffies + (x)) - -MODULE_AUTHOR("Myson or whoever"); -MODULE_DESCRIPTION("Myson MTD-8xx 100/10M Ethernet PCI Adapter Driver"); -MODULE_LICENSE("GPL"); -module_param(max_interrupt_work, int, 0); -module_param(debug, int, 0); -module_param(rx_copybreak, int, 0); -module_param(multicast_filter_limit, int, 0); -module_param_array(options, int, NULL, 0); -module_param_array(full_duplex, int, NULL, 0); -MODULE_PARM_DESC(max_interrupt_work, "fealnx maximum events handled per interrupt"); -MODULE_PARM_DESC(debug, "fealnx enable debugging (0-1)"); -MODULE_PARM_DESC(rx_copybreak, "fealnx copy breakpoint for copy-only-tiny-frames"); -MODULE_PARM_DESC(multicast_filter_limit, "fealnx maximum number of filtered multicast addresses"); -MODULE_PARM_DESC(options, "fealnx: Bits 0-3: media type, bit 17: full duplex"); -MODULE_PARM_DESC(full_duplex, "fealnx full duplex setting(s) (1)"); - -enum { - MIN_REGION_SIZE = 136, -}; - -/* A chip capabilities table, matching the entries in pci_tbl[] above. */ -enum chip_capability_flags { - HAS_MII_XCVR, - HAS_CHIP_XCVR, -}; - -/* 89/6/13 add, */ -/* for different PHY */ -enum phy_type_flags { - MysonPHY = 1, - AhdocPHY = 2, - SeeqPHY = 3, - MarvellPHY = 4, - Myson981 = 5, - LevelOnePHY = 6, - OtherPHY = 10, -}; - -struct chip_info { - char *chip_name; - int flags; -}; - -static const struct chip_info skel_netdrv_tbl[] = { - { "100/10M Ethernet PCI Adapter", HAS_MII_XCVR }, - { "100/10M Ethernet PCI Adapter", HAS_CHIP_XCVR }, - { "1000/100/10M Ethernet PCI Adapter", HAS_MII_XCVR }, -}; - -/* Offsets to the Command and Status Registers. */ -enum fealnx_offsets { - PAR0 = 0x0, /* physical address 0-3 */ - PAR1 = 0x04, /* physical address 4-5 */ - MAR0 = 0x08, /* multicast address 0-3 */ - MAR1 = 0x0C, /* multicast address 4-7 */ - FAR0 = 0x10, /* flow-control address 0-3 */ - FAR1 = 0x14, /* flow-control address 4-5 */ - TCRRCR = 0x18, /* receive & transmit configuration */ - BCR = 0x1C, /* bus command */ - TXPDR = 0x20, /* transmit polling demand */ - RXPDR = 0x24, /* receive polling demand */ - RXCWP = 0x28, /* receive current word pointer */ - TXLBA = 0x2C, /* transmit list base address */ - RXLBA = 0x30, /* receive list base address */ - ISR = 0x34, /* interrupt status */ - IMR = 0x38, /* interrupt mask */ - FTH = 0x3C, /* flow control high/low threshold */ - MANAGEMENT = 0x40, /* bootrom/eeprom and mii management */ - TALLY = 0x44, /* tally counters for crc and mpa */ - TSR = 0x48, /* tally counter for transmit status */ - BMCRSR = 0x4c, /* basic mode control and status */ - PHYIDENTIFIER = 0x50, /* phy identifier */ - ANARANLPAR = 0x54, /* auto-negotiation advertisement and link - partner ability */ - ANEROCR = 0x58, /* auto-negotiation expansion and pci conf. */ - BPREMRPSR = 0x5c, /* bypass & receive error mask and phy status */ -}; - -/* Bits in the interrupt status/enable registers. */ -/* The bits in the Intr Status/Enable registers, mostly interrupt sources. */ -enum intr_status_bits { - RFCON = 0x00020000, /* receive flow control xon packet */ - RFCOFF = 0x00010000, /* receive flow control xoff packet */ - LSCStatus = 0x00008000, /* link status change */ - ANCStatus = 0x00004000, /* autonegotiation completed */ - FBE = 0x00002000, /* fatal bus error */ - FBEMask = 0x00001800, /* mask bit12-11 */ - ParityErr = 0x00000000, /* parity error */ - TargetErr = 0x00001000, /* target abort */ - MasterErr = 0x00000800, /* master error */ - TUNF = 0x00000400, /* transmit underflow */ - ROVF = 0x00000200, /* receive overflow */ - ETI = 0x00000100, /* transmit early int */ - ERI = 0x00000080, /* receive early int */ - CNTOVF = 0x00000040, /* counter overflow */ - RBU = 0x00000020, /* receive buffer unavailable */ - TBU = 0x00000010, /* transmit buffer unavilable */ - TI = 0x00000008, /* transmit interrupt */ - RI = 0x00000004, /* receive interrupt */ - RxErr = 0x00000002, /* receive error */ -}; - -/* Bits in the NetworkConfig register, W for writing, R for reading */ -/* FIXME: some names are invented by me. Marked with (name?) */ -/* If you have docs and know bit names, please fix 'em */ -enum rx_mode_bits { - CR_W_ENH = 0x02000000, /* enhanced mode (name?) */ - CR_W_FD = 0x00100000, /* full duplex */ - CR_W_PS10 = 0x00080000, /* 10 mbit */ - CR_W_TXEN = 0x00040000, /* tx enable (name?) */ - CR_W_PS1000 = 0x00010000, /* 1000 mbit */ - /* CR_W_RXBURSTMASK= 0x00000e00, Im unsure about this */ - CR_W_RXMODEMASK = 0x000000e0, - CR_W_PROM = 0x00000080, /* promiscuous mode */ - CR_W_AB = 0x00000040, /* accept broadcast */ - CR_W_AM = 0x00000020, /* accept mutlicast */ - CR_W_ARP = 0x00000008, /* receive runt pkt */ - CR_W_ALP = 0x00000004, /* receive long pkt */ - CR_W_SEP = 0x00000002, /* receive error pkt */ - CR_W_RXEN = 0x00000001, /* rx enable (unicast?) (name?) */ - - CR_R_TXSTOP = 0x04000000, /* tx stopped (name?) */ - CR_R_FD = 0x00100000, /* full duplex detected */ - CR_R_PS10 = 0x00080000, /* 10 mbit detected */ - CR_R_RXSTOP = 0x00008000, /* rx stopped (name?) */ -}; - -/* The Tulip Rx and Tx buffer descriptors. */ -struct fealnx_desc { - s32 status; - s32 control; - u32 buffer; - u32 next_desc; - struct fealnx_desc *next_desc_logical; - struct sk_buff *skbuff; - u32 reserved1; - u32 reserved2; -}; - -/* Bits in network_desc.status */ -enum rx_desc_status_bits { - RXOWN = 0x80000000, /* own bit */ - FLNGMASK = 0x0fff0000, /* frame length */ - FLNGShift = 16, - MARSTATUS = 0x00004000, /* multicast address received */ - BARSTATUS = 0x00002000, /* broadcast address received */ - PHYSTATUS = 0x00001000, /* physical address received */ - RXFSD = 0x00000800, /* first descriptor */ - RXLSD = 0x00000400, /* last descriptor */ - ErrorSummary = 0x80, /* error summary */ - RUNTPKT = 0x40, /* runt packet received */ - LONGPKT = 0x20, /* long packet received */ - FAE = 0x10, /* frame align error */ - CRC = 0x08, /* crc error */ - RXER = 0x04, /* receive error */ -}; - -enum rx_desc_control_bits { - RXIC = 0x00800000, /* interrupt control */ - RBSShift = 0, -}; - -enum tx_desc_status_bits { - TXOWN = 0x80000000, /* own bit */ - JABTO = 0x00004000, /* jabber timeout */ - CSL = 0x00002000, /* carrier sense lost */ - LC = 0x00001000, /* late collision */ - EC = 0x00000800, /* excessive collision */ - UDF = 0x00000400, /* fifo underflow */ - DFR = 0x00000200, /* deferred */ - HF = 0x00000100, /* heartbeat fail */ - NCRMask = 0x000000ff, /* collision retry count */ - NCRShift = 0, -}; - -enum tx_desc_control_bits { - TXIC = 0x80000000, /* interrupt control */ - ETIControl = 0x40000000, /* early transmit interrupt */ - TXLD = 0x20000000, /* last descriptor */ - TXFD = 0x10000000, /* first descriptor */ - CRCEnable = 0x08000000, /* crc control */ - PADEnable = 0x04000000, /* padding control */ - RetryTxLC = 0x02000000, /* retry late collision */ - PKTSMask = 0x3ff800, /* packet size bit21-11 */ - PKTSShift = 11, - TBSMask = 0x000007ff, /* transmit buffer bit 10-0 */ - TBSShift = 0, -}; - -/* BootROM/EEPROM/MII Management Register */ -#define MASK_MIIR_MII_READ 0x00000000 -#define MASK_MIIR_MII_WRITE 0x00000008 -#define MASK_MIIR_MII_MDO 0x00000004 -#define MASK_MIIR_MII_MDI 0x00000002 -#define MASK_MIIR_MII_MDC 0x00000001 - -/* ST+OP+PHYAD+REGAD+TA */ -#define OP_READ 0x6000 /* ST:01+OP:10+PHYAD+REGAD+TA:Z0 */ -#define OP_WRITE 0x5002 /* ST:01+OP:01+PHYAD+REGAD+TA:10 */ - -/* ------------------------------------------------------------------------- */ -/* Constants for Myson PHY */ -/* ------------------------------------------------------------------------- */ -#define MysonPHYID 0xd0000302 -/* 89-7-27 add, (begin) */ -#define MysonPHYID0 0x0302 -#define StatusRegister 18 -#define SPEED100 0x0400 // bit10 -#define FULLMODE 0x0800 // bit11 -/* 89-7-27 add, (end) */ - -/* ------------------------------------------------------------------------- */ -/* Constants for Seeq 80225 PHY */ -/* ------------------------------------------------------------------------- */ -#define SeeqPHYID0 0x0016 - -#define MIIRegister18 18 -#define SPD_DET_100 0x80 -#define DPLX_DET_FULL 0x40 - -/* ------------------------------------------------------------------------- */ -/* Constants for Ahdoc 101 PHY */ -/* ------------------------------------------------------------------------- */ -#define AhdocPHYID0 0x0022 - -#define DiagnosticReg 18 -#define DPLX_FULL 0x0800 -#define Speed_100 0x0400 - -/* 89/6/13 add, */ -/* -------------------------------------------------------------------------- */ -/* Constants */ -/* -------------------------------------------------------------------------- */ -#define MarvellPHYID0 0x0141 -#define LevelOnePHYID0 0x0013 - -#define MII1000BaseTControlReg 9 -#define MII1000BaseTStatusReg 10 -#define SpecificReg 17 - -/* for 1000BaseT Control Register */ -#define PHYAbletoPerform1000FullDuplex 0x0200 -#define PHYAbletoPerform1000HalfDuplex 0x0100 -#define PHY1000AbilityMask 0x300 - -// for phy specific status register, marvell phy. -#define SpeedMask 0x0c000 -#define Speed_1000M 0x08000 -#define Speed_100M 0x4000 -#define Speed_10M 0 -#define Full_Duplex 0x2000 - -// 89/12/29 add, for phy specific status register, levelone phy, (begin) -#define LXT1000_100M 0x08000 -#define LXT1000_1000M 0x0c000 -#define LXT1000_Full 0x200 -// 89/12/29 add, for phy specific status register, levelone phy, (end) - -/* for 3-in-1 case, BMCRSR register */ -#define LinkIsUp2 0x00040000 - -/* for PHY */ -#define LinkIsUp 0x0004 - - -struct netdev_private { - /* Descriptor rings first for alignment. */ - struct fealnx_desc *rx_ring; - struct fealnx_desc *tx_ring; - - dma_addr_t rx_ring_dma; - dma_addr_t tx_ring_dma; - - spinlock_t lock; - - /* Media monitoring timer. */ - struct timer_list timer; - - /* Reset timer */ - struct timer_list reset_timer; - int reset_timer_armed; - unsigned long crvalue_sv; - unsigned long imrvalue_sv; - - /* Frequently used values: keep some adjacent for cache effect. */ - int flags; - struct pci_dev *pci_dev; - unsigned long crvalue; - unsigned long bcrvalue; - unsigned long imrvalue; - struct fealnx_desc *cur_rx; - struct fealnx_desc *lack_rxbuf; - int really_rx_count; - struct fealnx_desc *cur_tx; - struct fealnx_desc *cur_tx_copy; - int really_tx_count; - int free_tx_count; - unsigned int rx_buf_sz; /* Based on MTU+slack. */ - - /* These values are keep track of the transceiver/media in use. */ - unsigned int linkok; - unsigned int line_speed; - unsigned int duplexmode; - unsigned int default_port:4; /* Last dev->if_port value. */ - unsigned int PHYType; - - /* MII transceiver section. */ - int mii_cnt; /* MII device addresses. */ - unsigned char phys[2]; /* MII device addresses. */ - struct mii_if_info mii; - void __iomem *mem; -}; - - -static int mdio_read(struct net_device *dev, int phy_id, int location); -static void mdio_write(struct net_device *dev, int phy_id, int location, int value); -static int netdev_open(struct net_device *dev); -static void getlinktype(struct net_device *dev); -static void getlinkstatus(struct net_device *dev); -static void netdev_timer(struct timer_list *t); -static void reset_timer(struct timer_list *t); -static void fealnx_tx_timeout(struct net_device *dev, unsigned int txqueue); -static void init_ring(struct net_device *dev); -static netdev_tx_t start_tx(struct sk_buff *skb, struct net_device *dev); -static irqreturn_t intr_handler(int irq, void *dev_instance); -static int netdev_rx(struct net_device *dev); -static void set_rx_mode(struct net_device *dev); -static void __set_rx_mode(struct net_device *dev); -static struct net_device_stats *get_stats(struct net_device *dev); -static int mii_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); -static const struct ethtool_ops netdev_ethtool_ops; -static int netdev_close(struct net_device *dev); -static void reset_rx_descriptors(struct net_device *dev); -static void reset_tx_descriptors(struct net_device *dev); - -static void stop_nic_rx(void __iomem *ioaddr, long crvalue) -{ - int delay = 0x1000; - iowrite32(crvalue & ~(CR_W_RXEN), ioaddr + TCRRCR); - while (--delay) { - if ( (ioread32(ioaddr + TCRRCR) & CR_R_RXSTOP) == CR_R_RXSTOP) - break; - } -} - - -static void stop_nic_rxtx(void __iomem *ioaddr, long crvalue) -{ - int delay = 0x1000; - iowrite32(crvalue & ~(CR_W_RXEN+CR_W_TXEN), ioaddr + TCRRCR); - while (--delay) { - if ( (ioread32(ioaddr + TCRRCR) & (CR_R_RXSTOP+CR_R_TXSTOP)) - == (CR_R_RXSTOP+CR_R_TXSTOP) ) - break; - } -} - -static const struct net_device_ops netdev_ops = { - .ndo_open = netdev_open, - .ndo_stop = netdev_close, - .ndo_start_xmit = start_tx, - .ndo_get_stats = get_stats, - .ndo_set_rx_mode = set_rx_mode, - .ndo_eth_ioctl = mii_ioctl, - .ndo_tx_timeout = fealnx_tx_timeout, - .ndo_set_mac_address = eth_mac_addr, - .ndo_validate_addr = eth_validate_addr, -}; - -static int fealnx_init_one(struct pci_dev *pdev, - const struct pci_device_id *ent) -{ - struct netdev_private *np; - int i, option, err, irq; - static int card_idx = -1; - char boardname[12]; - void __iomem *ioaddr; - unsigned long len; - unsigned int chip_id = ent->driver_data; - struct net_device *dev; - void *ring_space; - dma_addr_t ring_dma; - u8 addr[ETH_ALEN]; -#ifdef USE_IO_OPS - int bar = 0; -#else - int bar = 1; -#endif - - card_idx++; - sprintf(boardname, "fealnx%d", card_idx); - - option = card_idx < MAX_UNITS ? options[card_idx] : 0; - - i = pci_enable_device(pdev); - if (i) return i; - pci_set_master(pdev); - - len = pci_resource_len(pdev, bar); - if (len < MIN_REGION_SIZE) { - dev_err(&pdev->dev, - "region size %ld too small, aborting\n", len); - return -ENODEV; - } - - i = pci_request_regions(pdev, boardname); - if (i) - return i; - - irq = pdev->irq; - - ioaddr = pci_iomap(pdev, bar, len); - if (!ioaddr) { - err = -ENOMEM; - goto err_out_res; - } - - dev = alloc_etherdev(sizeof(struct netdev_private)); - if (!dev) { - err = -ENOMEM; - goto err_out_unmap; - } - SET_NETDEV_DEV(dev, &pdev->dev); - - /* read ethernet id */ - for (i = 0; i < 6; ++i) - addr[i] = ioread8(ioaddr + PAR0 + i); - eth_hw_addr_set(dev, addr); - - /* Reset the chip to erase previous misconfiguration. */ - iowrite32(0x00000001, ioaddr + BCR); - - /* Make certain the descriptor lists are aligned. */ - np = netdev_priv(dev); - np->mem = ioaddr; - spin_lock_init(&np->lock); - np->pci_dev = pdev; - np->flags = skel_netdrv_tbl[chip_id].flags; - pci_set_drvdata(pdev, dev); - np->mii.dev = dev; - np->mii.mdio_read = mdio_read; - np->mii.mdio_write = mdio_write; - np->mii.phy_id_mask = 0x1f; - np->mii.reg_num_mask = 0x1f; - - ring_space = dma_alloc_coherent(&pdev->dev, RX_TOTAL_SIZE, &ring_dma, - GFP_KERNEL); - if (!ring_space) { - err = -ENOMEM; - goto err_out_free_dev; - } - np->rx_ring = ring_space; - np->rx_ring_dma = ring_dma; - - ring_space = dma_alloc_coherent(&pdev->dev, TX_TOTAL_SIZE, &ring_dma, - GFP_KERNEL); - if (!ring_space) { - err = -ENOMEM; - goto err_out_free_rx; - } - np->tx_ring = ring_space; - np->tx_ring_dma = ring_dma; - - /* find the connected MII xcvrs */ - if (np->flags == HAS_MII_XCVR) { - int phy, phy_idx = 0; - - for (phy = 1; phy < 32 && phy_idx < ARRAY_SIZE(np->phys); - phy++) { - int mii_status = mdio_read(dev, phy, 1); - - if (mii_status != 0xffff && mii_status != 0x0000) { - np->phys[phy_idx++] = phy; - dev_info(&pdev->dev, - "MII PHY found at address %d, status " - "0x%4.4x.\n", phy, mii_status); - /* get phy type */ - { - unsigned int data; - - data = mdio_read(dev, np->phys[0], 2); - if (data == SeeqPHYID0) - np->PHYType = SeeqPHY; - else if (data == AhdocPHYID0) - np->PHYType = AhdocPHY; - else if (data == MarvellPHYID0) - np->PHYType = MarvellPHY; - else if (data == MysonPHYID0) - np->PHYType = Myson981; - else if (data == LevelOnePHYID0) - np->PHYType = LevelOnePHY; - else - np->PHYType = OtherPHY; - } - } - } - - np->mii_cnt = phy_idx; - if (phy_idx == 0) - dev_warn(&pdev->dev, - "MII PHY not found -- this device may " - "not operate correctly.\n"); - } else { - np->phys[0] = 32; -/* 89/6/23 add, (begin) */ - /* get phy type */ - if (ioread32(ioaddr + PHYIDENTIFIER) == MysonPHYID) - np->PHYType = MysonPHY; - else - np->PHYType = OtherPHY; - } - np->mii.phy_id = np->phys[0]; - - if (dev->mem_start) - option = dev->mem_start; - - /* The lower four bits are the media type. */ - if (option > 0) { - if (option & 0x200) - np->mii.full_duplex = 1; - np->default_port = option & 15; - } - - if (card_idx < MAX_UNITS && full_duplex[card_idx] > 0) - np->mii.full_duplex = full_duplex[card_idx]; - - if (np->mii.full_duplex) { - dev_info(&pdev->dev, "Media type forced to Full Duplex.\n"); -/* 89/6/13 add, (begin) */ -// if (np->PHYType==MarvellPHY) - if ((np->PHYType == MarvellPHY) || (np->PHYType == LevelOnePHY)) { - unsigned int data; - - data = mdio_read(dev, np->phys[0], 9); - data = (data & 0xfcff) | 0x0200; - mdio_write(dev, np->phys[0], 9, data); - } -/* 89/6/13 add, (end) */ - if (np->flags == HAS_MII_XCVR) - mdio_write(dev, np->phys[0], MII_ADVERTISE, ADVERTISE_FULL); - else - iowrite32(ADVERTISE_FULL, ioaddr + ANARANLPAR); - np->mii.force_media = 1; - } - - dev->netdev_ops = &netdev_ops; - dev->ethtool_ops = &netdev_ethtool_ops; - dev->watchdog_timeo = TX_TIMEOUT; - - err = register_netdev(dev); - if (err) - goto err_out_free_tx; - - printk(KERN_INFO "%s: %s at %p, %pM, IRQ %d.\n", - dev->name, skel_netdrv_tbl[chip_id].chip_name, ioaddr, - dev->dev_addr, irq); - - return 0; - -err_out_free_tx: - dma_free_coherent(&pdev->dev, TX_TOTAL_SIZE, np->tx_ring, - np->tx_ring_dma); -err_out_free_rx: - dma_free_coherent(&pdev->dev, RX_TOTAL_SIZE, np->rx_ring, - np->rx_ring_dma); -err_out_free_dev: - free_netdev(dev); -err_out_unmap: - pci_iounmap(pdev, ioaddr); -err_out_res: - pci_release_regions(pdev); - return err; -} - - -static void fealnx_remove_one(struct pci_dev *pdev) -{ - struct net_device *dev = pci_get_drvdata(pdev); - - if (dev) { - struct netdev_private *np = netdev_priv(dev); - - dma_free_coherent(&pdev->dev, TX_TOTAL_SIZE, np->tx_ring, - np->tx_ring_dma); - dma_free_coherent(&pdev->dev, RX_TOTAL_SIZE, np->rx_ring, - np->rx_ring_dma); - unregister_netdev(dev); - pci_iounmap(pdev, np->mem); - free_netdev(dev); - pci_release_regions(pdev); - } else - printk(KERN_ERR "fealnx: remove for unknown device\n"); -} - - -static ulong m80x_send_cmd_to_phy(void __iomem *miiport, int opcode, int phyad, int regad) -{ - ulong miir; - int i; - unsigned int mask, data; - - /* enable MII output */ - miir = (ulong) ioread32(miiport); - miir &= 0xfffffff0; - - miir |= MASK_MIIR_MII_WRITE + MASK_MIIR_MII_MDO; - - /* send 32 1's preamble */ - for (i = 0; i < 32; i++) { - /* low MDC; MDO is already high (miir) */ - miir &= ~MASK_MIIR_MII_MDC; - iowrite32(miir, miiport); - - /* high MDC */ - miir |= MASK_MIIR_MII_MDC; - iowrite32(miir, miiport); - } - - /* calculate ST+OP+PHYAD+REGAD+TA */ - data = opcode | (phyad << 7) | (regad << 2); - - /* sent out */ - mask = 0x8000; - while (mask) { - /* low MDC, prepare MDO */ - miir &= ~(MASK_MIIR_MII_MDC + MASK_MIIR_MII_MDO); - if (mask & data) - miir |= MASK_MIIR_MII_MDO; - - iowrite32(miir, miiport); - /* high MDC */ - miir |= MASK_MIIR_MII_MDC; - iowrite32(miir, miiport); - udelay(30); - - /* next */ - mask >>= 1; - if (mask == 0x2 && opcode == OP_READ) - miir &= ~MASK_MIIR_MII_WRITE; - } - return miir; -} - - -static int mdio_read(struct net_device *dev, int phyad, int regad) -{ - struct netdev_private *np = netdev_priv(dev); - void __iomem *miiport = np->mem + MANAGEMENT; - ulong miir; - unsigned int mask, data; - - miir = m80x_send_cmd_to_phy(miiport, OP_READ, phyad, regad); - - /* read data */ - mask = 0x8000; - data = 0; - while (mask) { - /* low MDC */ - miir &= ~MASK_MIIR_MII_MDC; - iowrite32(miir, miiport); - - /* read MDI */ - miir = ioread32(miiport); - if (miir & MASK_MIIR_MII_MDI) - data |= mask; - - /* high MDC, and wait */ - miir |= MASK_MIIR_MII_MDC; - iowrite32(miir, miiport); - udelay(30); - - /* next */ - mask >>= 1; - } - - /* low MDC */ - miir &= ~MASK_MIIR_MII_MDC; - iowrite32(miir, miiport); - - return data & 0xffff; -} - - -static void mdio_write(struct net_device *dev, int phyad, int regad, int data) -{ - struct netdev_private *np = netdev_priv(dev); - void __iomem *miiport = np->mem + MANAGEMENT; - ulong miir; - unsigned int mask; - - miir = m80x_send_cmd_to_phy(miiport, OP_WRITE, phyad, regad); - - /* write data */ - mask = 0x8000; - while (mask) { - /* low MDC, prepare MDO */ - miir &= ~(MASK_MIIR_MII_MDC + MASK_MIIR_MII_MDO); - if (mask & data) - miir |= MASK_MIIR_MII_MDO; - iowrite32(miir, miiport); - - /* high MDC */ - miir |= MASK_MIIR_MII_MDC; - iowrite32(miir, miiport); - - /* next */ - mask >>= 1; - } - - /* low MDC */ - miir &= ~MASK_MIIR_MII_MDC; - iowrite32(miir, miiport); -} - - -static int netdev_open(struct net_device *dev) -{ - struct netdev_private *np = netdev_priv(dev); - void __iomem *ioaddr = np->mem; - const int irq = np->pci_dev->irq; - int rc, i; - - iowrite32(0x00000001, ioaddr + BCR); /* Reset */ - - rc = request_irq(irq, intr_handler, IRQF_SHARED, dev->name, dev); - if (rc) - return -EAGAIN; - - for (i = 0; i < 3; i++) - iowrite16(((const unsigned short *)dev->dev_addr)[i], - ioaddr + PAR0 + i*2); - - init_ring(dev); - - iowrite32(np->rx_ring_dma, ioaddr + RXLBA); - iowrite32(np->tx_ring_dma, ioaddr + TXLBA); - - /* Initialize other registers. */ - /* Configure the PCI bus bursts and FIFO thresholds. - 486: Set 8 longword burst. - 586: no burst limit. - Burst length 5:3 - 0 0 0 1 - 0 0 1 4 - 0 1 0 8 - 0 1 1 16 - 1 0 0 32 - 1 0 1 64 - 1 1 0 128 - 1 1 1 256 - Wait the specified 50 PCI cycles after a reset by initializing - Tx and Rx queues and the address filter list. - FIXME (Ueimor): optimistic for alpha + posted writes ? */ - - np->bcrvalue = 0x10; /* little-endian, 8 burst length */ -#ifdef __BIG_ENDIAN - np->bcrvalue |= 0x04; /* big-endian */ -#endif - -#if defined(__i386__) && !defined(MODULE) && !defined(CONFIG_UML) - if (boot_cpu_data.x86 <= 4) - np->crvalue = 0xa00; - else -#endif - np->crvalue = 0xe00; /* rx 128 burst length */ - - -// 89/12/29 add, -// 90/1/16 modify, -// np->imrvalue=FBE|TUNF|CNTOVF|RBU|TI|RI; - np->imrvalue = TUNF | CNTOVF | RBU | TI | RI; - if (np->pci_dev->device == 0x891) { - np->bcrvalue |= 0x200; /* set PROG bit */ - np->crvalue |= CR_W_ENH; /* set enhanced bit */ - np->imrvalue |= ETI; - } - iowrite32(np->bcrvalue, ioaddr + BCR); - - if (dev->if_port == 0) - dev->if_port = np->default_port; - - iowrite32(0, ioaddr + RXPDR); -// 89/9/1 modify, -// np->crvalue = 0x00e40001; /* tx store and forward, tx/rx enable */ - np->crvalue |= 0x00e40001; /* tx store and forward, tx/rx enable */ - np->mii.full_duplex = np->mii.force_media; - getlinkstatus(dev); - if (np->linkok) - getlinktype(dev); - __set_rx_mode(dev); - - netif_start_queue(dev); - - /* Clear and Enable interrupts by setting the interrupt mask. */ - iowrite32(FBE | TUNF | CNTOVF | RBU | TI | RI, ioaddr + ISR); - iowrite32(np->imrvalue, ioaddr + IMR); - - if (debug) - printk(KERN_DEBUG "%s: Done netdev_open().\n", dev->name); - - /* Set the timer to check for link beat. */ - timer_setup(&np->timer, netdev_timer, 0); - np->timer.expires = RUN_AT(3 * HZ); - - /* timer handler */ - add_timer(&np->timer); - - timer_setup(&np->reset_timer, reset_timer, 0); - np->reset_timer_armed = 0; - return rc; -} - - -static void getlinkstatus(struct net_device *dev) -/* function: Routine will read MII Status Register to get link status. */ -/* input : dev... pointer to the adapter block. */ -/* output : none. */ -{ - struct netdev_private *np = netdev_priv(dev); - unsigned int i, DelayTime = 0x1000; - - np->linkok = 0; - - if (np->PHYType == MysonPHY) { - for (i = 0; i < DelayTime; ++i) { - if (ioread32(np->mem + BMCRSR) & LinkIsUp2) { - np->linkok = 1; - return; - } - udelay(100); - } - } else { - for (i = 0; i < DelayTime; ++i) { - if (mdio_read(dev, np->phys[0], MII_BMSR) & BMSR_LSTATUS) { - np->linkok = 1; - return; - } - udelay(100); - } - } -} - - -static void getlinktype(struct net_device *dev) -{ - struct netdev_private *np = netdev_priv(dev); - - if (np->PHYType == MysonPHY) { /* 3-in-1 case */ - if (ioread32(np->mem + TCRRCR) & CR_R_FD) - np->duplexmode = 2; /* full duplex */ - else - np->duplexmode = 1; /* half duplex */ - if (ioread32(np->mem + TCRRCR) & CR_R_PS10) - np->line_speed = 1; /* 10M */ - else - np->line_speed = 2; /* 100M */ - } else { - if (np->PHYType == SeeqPHY) { /* this PHY is SEEQ 80225 */ - unsigned int data; - - data = mdio_read(dev, np->phys[0], MIIRegister18); - if (data & SPD_DET_100) - np->line_speed = 2; /* 100M */ - else - np->line_speed = 1; /* 10M */ - if (data & DPLX_DET_FULL) - np->duplexmode = 2; /* full duplex mode */ - else - np->duplexmode = 1; /* half duplex mode */ - } else if (np->PHYType == AhdocPHY) { - unsigned int data; - - data = mdio_read(dev, np->phys[0], DiagnosticReg); - if (data & Speed_100) - np->line_speed = 2; /* 100M */ - else - np->line_speed = 1; /* 10M */ - if (data & DPLX_FULL) - np->duplexmode = 2; /* full duplex mode */ - else - np->duplexmode = 1; /* half duplex mode */ - } -/* 89/6/13 add, (begin) */ - else if (np->PHYType == MarvellPHY) { - unsigned int data; - - data = mdio_read(dev, np->phys[0], SpecificReg); - if (data & Full_Duplex) - np->duplexmode = 2; /* full duplex mode */ - else - np->duplexmode = 1; /* half duplex mode */ - data &= SpeedMask; - if (data == Speed_1000M) - np->line_speed = 3; /* 1000M */ - else if (data == Speed_100M) - np->line_speed = 2; /* 100M */ - else - np->line_speed = 1; /* 10M */ - } -/* 89/6/13 add, (end) */ -/* 89/7/27 add, (begin) */ - else if (np->PHYType == Myson981) { - unsigned int data; - - data = mdio_read(dev, np->phys[0], StatusRegister); - - if (data & SPEED100) - np->line_speed = 2; - else - np->line_speed = 1; - - if (data & FULLMODE) - np->duplexmode = 2; - else - np->duplexmode = 1; - } -/* 89/7/27 add, (end) */ -/* 89/12/29 add */ - else if (np->PHYType == LevelOnePHY) { - unsigned int data; - - data = mdio_read(dev, np->phys[0], SpecificReg); - if (data & LXT1000_Full) - np->duplexmode = 2; /* full duplex mode */ - else - np->duplexmode = 1; /* half duplex mode */ - data &= SpeedMask; - if (data == LXT1000_1000M) - np->line_speed = 3; /* 1000M */ - else if (data == LXT1000_100M) - np->line_speed = 2; /* 100M */ - else - np->line_speed = 1; /* 10M */ - } - np->crvalue &= (~CR_W_PS10) & (~CR_W_FD) & (~CR_W_PS1000); - if (np->line_speed == 1) - np->crvalue |= CR_W_PS10; - else if (np->line_speed == 3) - np->crvalue |= CR_W_PS1000; - if (np->duplexmode == 2) - np->crvalue |= CR_W_FD; - } -} - - -/* Take lock before calling this */ -static void allocate_rx_buffers(struct net_device *dev) -{ - struct netdev_private *np = netdev_priv(dev); - - /* allocate skb for rx buffers */ - while (np->really_rx_count != RX_RING_SIZE) { - struct sk_buff *skb; - - skb = netdev_alloc_skb(dev, np->rx_buf_sz); - if (skb == NULL) - break; /* Better luck next round. */ - - while (np->lack_rxbuf->skbuff) - np->lack_rxbuf = np->lack_rxbuf->next_desc_logical; - - np->lack_rxbuf->skbuff = skb; - np->lack_rxbuf->buffer = dma_map_single(&np->pci_dev->dev, - skb->data, - np->rx_buf_sz, - DMA_FROM_DEVICE); - np->lack_rxbuf->status = RXOWN; - ++np->really_rx_count; - } -} - - -static void netdev_timer(struct timer_list *t) -{ - struct netdev_private *np = from_timer(np, t, timer); - struct net_device *dev = np->mii.dev; - void __iomem *ioaddr = np->mem; - int old_crvalue = np->crvalue; - unsigned int old_linkok = np->linkok; - unsigned long flags; - - if (debug) - printk(KERN_DEBUG "%s: Media selection timer tick, status %8.8x " - "config %8.8x.\n", dev->name, ioread32(ioaddr + ISR), - ioread32(ioaddr + TCRRCR)); - - spin_lock_irqsave(&np->lock, flags); - - if (np->flags == HAS_MII_XCVR) { - getlinkstatus(dev); - if ((old_linkok == 0) && (np->linkok == 1)) { /* we need to detect the media type again */ - getlinktype(dev); - if (np->crvalue != old_crvalue) { - stop_nic_rxtx(ioaddr, np->crvalue); - iowrite32(np->crvalue, ioaddr + TCRRCR); - } - } - } - - allocate_rx_buffers(dev); - - spin_unlock_irqrestore(&np->lock, flags); - - np->timer.expires = RUN_AT(10 * HZ); - add_timer(&np->timer); -} - - -/* Take lock before calling */ -/* Reset chip and disable rx, tx and interrupts */ -static void reset_and_disable_rxtx(struct net_device *dev) -{ - struct netdev_private *np = netdev_priv(dev); - void __iomem *ioaddr = np->mem; - int delay=51; - - /* Reset the chip's Tx and Rx processes. */ - stop_nic_rxtx(ioaddr, 0); - - /* Disable interrupts by clearing the interrupt mask. */ - iowrite32(0, ioaddr + IMR); - - /* Reset the chip to erase previous misconfiguration. */ - iowrite32(0x00000001, ioaddr + BCR); - - /* Ueimor: wait for 50 PCI cycles (and flush posted writes btw). - We surely wait too long (address+data phase). Who cares? */ - while (--delay) { - ioread32(ioaddr + BCR); - rmb(); - } -} - - -/* Take lock before calling */ -/* Restore chip after reset */ -static void enable_rxtx(struct net_device *dev) -{ - struct netdev_private *np = netdev_priv(dev); - void __iomem *ioaddr = np->mem; - - reset_rx_descriptors(dev); - - iowrite32(np->tx_ring_dma + ((char*)np->cur_tx - (char*)np->tx_ring), - ioaddr + TXLBA); - iowrite32(np->rx_ring_dma + ((char*)np->cur_rx - (char*)np->rx_ring), - ioaddr + RXLBA); - - iowrite32(np->bcrvalue, ioaddr + BCR); - - iowrite32(0, ioaddr + RXPDR); - __set_rx_mode(dev); /* changes np->crvalue, writes it into TCRRCR */ - - /* Clear and Enable interrupts by setting the interrupt mask. */ - iowrite32(FBE | TUNF | CNTOVF | RBU | TI | RI, ioaddr + ISR); - iowrite32(np->imrvalue, ioaddr + IMR); - - iowrite32(0, ioaddr + TXPDR); -} - - -static void reset_timer(struct timer_list *t) -{ - struct netdev_private *np = from_timer(np, t, reset_timer); - struct net_device *dev = np->mii.dev; - unsigned long flags; - - printk(KERN_WARNING "%s: resetting tx and rx machinery\n", dev->name); - - spin_lock_irqsave(&np->lock, flags); - np->crvalue = np->crvalue_sv; - np->imrvalue = np->imrvalue_sv; - - reset_and_disable_rxtx(dev); - /* works for me without this: - reset_tx_descriptors(dev); */ - enable_rxtx(dev); - netif_start_queue(dev); /* FIXME: or netif_wake_queue(dev); ? */ - - np->reset_timer_armed = 0; - - spin_unlock_irqrestore(&np->lock, flags); -} - - -static void fealnx_tx_timeout(struct net_device *dev, unsigned int txqueue) -{ - struct netdev_private *np = netdev_priv(dev); - void __iomem *ioaddr = np->mem; - unsigned long flags; - int i; - - printk(KERN_WARNING - "%s: Transmit timed out, status %8.8x, resetting...\n", - dev->name, ioread32(ioaddr + ISR)); - - { - printk(KERN_DEBUG " Rx ring %p: ", np->rx_ring); - for (i = 0; i < RX_RING_SIZE; i++) - printk(KERN_CONT " %8.8x", - (unsigned int) np->rx_ring[i].status); - printk(KERN_CONT "\n"); - printk(KERN_DEBUG " Tx ring %p: ", np->tx_ring); - for (i = 0; i < TX_RING_SIZE; i++) - printk(KERN_CONT " %4.4x", np->tx_ring[i].status); - printk(KERN_CONT "\n"); - } - - spin_lock_irqsave(&np->lock, flags); - - reset_and_disable_rxtx(dev); - reset_tx_descriptors(dev); - enable_rxtx(dev); - - spin_unlock_irqrestore(&np->lock, flags); - - netif_trans_update(dev); /* prevent tx timeout */ - dev->stats.tx_errors++; - netif_wake_queue(dev); /* or .._start_.. ?? */ -} - - -/* Initialize the Rx and Tx rings, along with various 'dev' bits. */ -static void init_ring(struct net_device *dev) -{ - struct netdev_private *np = netdev_priv(dev); - int i; - - /* initialize rx variables */ - np->rx_buf_sz = (dev->mtu <= 1500 ? PKT_BUF_SZ : dev->mtu + 32); - np->cur_rx = &np->rx_ring[0]; - np->lack_rxbuf = np->rx_ring; - np->really_rx_count = 0; - - /* initial rx descriptors. */ - for (i = 0; i < RX_RING_SIZE; i++) { - np->rx_ring[i].status = 0; - np->rx_ring[i].control = np->rx_buf_sz << RBSShift; - np->rx_ring[i].next_desc = np->rx_ring_dma + - (i + 1)*sizeof(struct fealnx_desc); - np->rx_ring[i].next_desc_logical = &np->rx_ring[i + 1]; - np->rx_ring[i].skbuff = NULL; - } - - /* for the last rx descriptor */ - np->rx_ring[i - 1].next_desc = np->rx_ring_dma; - np->rx_ring[i - 1].next_desc_logical = np->rx_ring; - - /* allocate skb for rx buffers */ - for (i = 0; i < RX_RING_SIZE; i++) { - struct sk_buff *skb = netdev_alloc_skb(dev, np->rx_buf_sz); - - if (skb == NULL) { - np->lack_rxbuf = &np->rx_ring[i]; - break; - } - - ++np->really_rx_count; - np->rx_ring[i].skbuff = skb; - np->rx_ring[i].buffer = dma_map_single(&np->pci_dev->dev, - skb->data, - np->rx_buf_sz, - DMA_FROM_DEVICE); - np->rx_ring[i].status = RXOWN; - np->rx_ring[i].control |= RXIC; - } - - /* initialize tx variables */ - np->cur_tx = &np->tx_ring[0]; - np->cur_tx_copy = &np->tx_ring[0]; - np->really_tx_count = 0; - np->free_tx_count = TX_RING_SIZE; - - for (i = 0; i < TX_RING_SIZE; i++) { - np->tx_ring[i].status = 0; - /* do we need np->tx_ring[i].control = XXX; ?? */ - np->tx_ring[i].next_desc = np->tx_ring_dma + - (i + 1)*sizeof(struct fealnx_desc); - np->tx_ring[i].next_desc_logical = &np->tx_ring[i + 1]; - np->tx_ring[i].skbuff = NULL; - } - - /* for the last tx descriptor */ - np->tx_ring[i - 1].next_desc = np->tx_ring_dma; - np->tx_ring[i - 1].next_desc_logical = &np->tx_ring[0]; -} - - -static netdev_tx_t start_tx(struct sk_buff *skb, struct net_device *dev) -{ - struct netdev_private *np = netdev_priv(dev); - unsigned long flags; - - spin_lock_irqsave(&np->lock, flags); - - np->cur_tx_copy->skbuff = skb; - -#define one_buffer -#define BPT 1022 -#if defined(one_buffer) - np->cur_tx_copy->buffer = dma_map_single(&np->pci_dev->dev, skb->data, - skb->len, DMA_TO_DEVICE); - np->cur_tx_copy->control = TXIC | TXLD | TXFD | CRCEnable | PADEnable; - np->cur_tx_copy->control |= (skb->len << PKTSShift); /* pkt size */ - np->cur_tx_copy->control |= (skb->len << TBSShift); /* buffer size */ -// 89/12/29 add, - if (np->pci_dev->device == 0x891) - np->cur_tx_copy->control |= ETIControl | RetryTxLC; - np->cur_tx_copy->status = TXOWN; - np->cur_tx_copy = np->cur_tx_copy->next_desc_logical; - --np->free_tx_count; -#elif defined(two_buffer) - if (skb->len > BPT) { - struct fealnx_desc *next; - - /* for the first descriptor */ - np->cur_tx_copy->buffer = dma_map_single(&np->pci_dev->dev, - skb->data, BPT, - DMA_TO_DEVICE); - np->cur_tx_copy->control = TXIC | TXFD | CRCEnable | PADEnable; - np->cur_tx_copy->control |= (skb->len << PKTSShift); /* pkt size */ - np->cur_tx_copy->control |= (BPT << TBSShift); /* buffer size */ - - /* for the last descriptor */ - next = np->cur_tx_copy->next_desc_logical; - next->skbuff = skb; - next->control = TXIC | TXLD | CRCEnable | PADEnable; - next->control |= (skb->len << PKTSShift); /* pkt size */ - next->control |= ((skb->len - BPT) << TBSShift); /* buf size */ -// 89/12/29 add, - if (np->pci_dev->device == 0x891) - np->cur_tx_copy->control |= ETIControl | RetryTxLC; - next->buffer = dma_map_single(&ep->pci_dev->dev, - skb->data + BPT, skb->len - BPT, - DMA_TO_DEVICE); - - next->status = TXOWN; - np->cur_tx_copy->status = TXOWN; - - np->cur_tx_copy = next->next_desc_logical; - np->free_tx_count -= 2; - } else { - np->cur_tx_copy->buffer = dma_map_single(&np->pci_dev->dev, - skb->data, skb->len, - DMA_TO_DEVICE); - np->cur_tx_copy->control = TXIC | TXLD | TXFD | CRCEnable | PADEnable; - np->cur_tx_copy->control |= (skb->len << PKTSShift); /* pkt size */ - np->cur_tx_copy->control |= (skb->len << TBSShift); /* buffer size */ -// 89/12/29 add, - if (np->pci_dev->device == 0x891) - np->cur_tx_copy->control |= ETIControl | RetryTxLC; - np->cur_tx_copy->status = TXOWN; - np->cur_tx_copy = np->cur_tx_copy->next_desc_logical; - --np->free_tx_count; - } -#endif - - if (np->free_tx_count < 2) - netif_stop_queue(dev); - ++np->really_tx_count; - iowrite32(0, np->mem + TXPDR); - - spin_unlock_irqrestore(&np->lock, flags); - return NETDEV_TX_OK; -} - - -/* Take lock before calling */ -/* Chip probably hosed tx ring. Clean up. */ -static void reset_tx_descriptors(struct net_device *dev) -{ - struct netdev_private *np = netdev_priv(dev); - struct fealnx_desc *cur; - int i; - - /* initialize tx variables */ - np->cur_tx = &np->tx_ring[0]; - np->cur_tx_copy = &np->tx_ring[0]; - np->really_tx_count = 0; - np->free_tx_count = TX_RING_SIZE; - - for (i = 0; i < TX_RING_SIZE; i++) { - cur = &np->tx_ring[i]; - if (cur->skbuff) { - dma_unmap_single(&np->pci_dev->dev, cur->buffer, - cur->skbuff->len, DMA_TO_DEVICE); - dev_kfree_skb_any(cur->skbuff); - cur->skbuff = NULL; - } - cur->status = 0; - cur->control = 0; /* needed? */ - /* probably not needed. We do it for purely paranoid reasons */ - cur->next_desc = np->tx_ring_dma + - (i + 1)*sizeof(struct fealnx_desc); - cur->next_desc_logical = &np->tx_ring[i + 1]; - } - /* for the last tx descriptor */ - np->tx_ring[TX_RING_SIZE - 1].next_desc = np->tx_ring_dma; - np->tx_ring[TX_RING_SIZE - 1].next_desc_logical = &np->tx_ring[0]; -} - - -/* Take lock and stop rx before calling this */ -static void reset_rx_descriptors(struct net_device *dev) -{ - struct netdev_private *np = netdev_priv(dev); - struct fealnx_desc *cur = np->cur_rx; - int i; - - allocate_rx_buffers(dev); - - for (i = 0; i < RX_RING_SIZE; i++) { - if (cur->skbuff) - cur->status = RXOWN; - cur = cur->next_desc_logical; - } - - iowrite32(np->rx_ring_dma + ((char*)np->cur_rx - (char*)np->rx_ring), - np->mem + RXLBA); -} - - -/* The interrupt handler does all of the Rx thread work and cleans up - after the Tx thread. */ -static irqreturn_t intr_handler(int irq, void *dev_instance) -{ - struct net_device *dev = (struct net_device *) dev_instance; - struct netdev_private *np = netdev_priv(dev); - void __iomem *ioaddr = np->mem; - long boguscnt = max_interrupt_work; - unsigned int num_tx = 0; - int handled = 0; - - spin_lock(&np->lock); - - iowrite32(0, ioaddr + IMR); - - do { - u32 intr_status = ioread32(ioaddr + ISR); - - /* Acknowledge all of the current interrupt sources ASAP. */ - iowrite32(intr_status, ioaddr + ISR); - - if (debug) - printk(KERN_DEBUG "%s: Interrupt, status %4.4x.\n", dev->name, - intr_status); - - if (!(intr_status & np->imrvalue)) - break; - - handled = 1; - -// 90/1/16 delete, -// -// if (intr_status & FBE) -// { /* fatal error */ -// stop_nic_tx(ioaddr, 0); -// stop_nic_rx(ioaddr, 0); -// break; -// }; - - if (intr_status & TUNF) - iowrite32(0, ioaddr + TXPDR); - - if (intr_status & CNTOVF) { - /* missed pkts */ - dev->stats.rx_missed_errors += - ioread32(ioaddr + TALLY) & 0x7fff; - - /* crc error */ - dev->stats.rx_crc_errors += - (ioread32(ioaddr + TALLY) & 0x7fff0000) >> 16; - } - - if (intr_status & (RI | RBU)) { - if (intr_status & RI) - netdev_rx(dev); - else { - stop_nic_rx(ioaddr, np->crvalue); - reset_rx_descriptors(dev); - iowrite32(np->crvalue, ioaddr + TCRRCR); - } - } - - while (np->really_tx_count) { - long tx_status = np->cur_tx->status; - long tx_control = np->cur_tx->control; - - if (!(tx_control & TXLD)) { /* this pkt is combined by two tx descriptors */ - struct fealnx_desc *next; - - next = np->cur_tx->next_desc_logical; - tx_status = next->status; - tx_control = next->control; - } - - if (tx_status & TXOWN) - break; - - if (!(np->crvalue & CR_W_ENH)) { - if (tx_status & (CSL | LC | EC | UDF | HF)) { - dev->stats.tx_errors++; - if (tx_status & EC) - dev->stats.tx_aborted_errors++; - if (tx_status & CSL) - dev->stats.tx_carrier_errors++; - if (tx_status & LC) - dev->stats.tx_window_errors++; - if (tx_status & UDF) - dev->stats.tx_fifo_errors++; - if ((tx_status & HF) && np->mii.full_duplex == 0) - dev->stats.tx_heartbeat_errors++; - - } else { - dev->stats.tx_bytes += - ((tx_control & PKTSMask) >> PKTSShift); - - dev->stats.collisions += - ((tx_status & NCRMask) >> NCRShift); - dev->stats.tx_packets++; - } - } else { - dev->stats.tx_bytes += - ((tx_control & PKTSMask) >> PKTSShift); - dev->stats.tx_packets++; - } - - /* Free the original skb. */ - dma_unmap_single(&np->pci_dev->dev, - np->cur_tx->buffer, - np->cur_tx->skbuff->len, - DMA_TO_DEVICE); - dev_consume_skb_irq(np->cur_tx->skbuff); - np->cur_tx->skbuff = NULL; - --np->really_tx_count; - if (np->cur_tx->control & TXLD) { - np->cur_tx = np->cur_tx->next_desc_logical; - ++np->free_tx_count; - } else { - np->cur_tx = np->cur_tx->next_desc_logical; - np->cur_tx = np->cur_tx->next_desc_logical; - np->free_tx_count += 2; - } - num_tx++; - } /* end of for loop */ - - if (num_tx && np->free_tx_count >= 2) - netif_wake_queue(dev); - - /* read transmit status for enhanced mode only */ - if (np->crvalue & CR_W_ENH) { - long data; - - data = ioread32(ioaddr + TSR); - dev->stats.tx_errors += (data & 0xff000000) >> 24; - dev->stats.tx_aborted_errors += - (data & 0xff000000) >> 24; - dev->stats.tx_window_errors += - (data & 0x00ff0000) >> 16; - dev->stats.collisions += (data & 0x0000ffff); - } - - if (--boguscnt < 0) { - printk(KERN_WARNING "%s: Too much work at interrupt, " - "status=0x%4.4x.\n", dev->name, intr_status); - if (!np->reset_timer_armed) { - np->reset_timer_armed = 1; - np->reset_timer.expires = RUN_AT(HZ/2); - add_timer(&np->reset_timer); - stop_nic_rxtx(ioaddr, 0); - netif_stop_queue(dev); - /* or netif_tx_disable(dev); ?? */ - /* Prevent other paths from enabling tx,rx,intrs */ - np->crvalue_sv = np->crvalue; - np->imrvalue_sv = np->imrvalue; - np->crvalue &= ~(CR_W_TXEN | CR_W_RXEN); /* or simply = 0? */ - np->imrvalue = 0; - } - - break; - } - } while (1); - - /* read the tally counters */ - /* missed pkts */ - dev->stats.rx_missed_errors += ioread32(ioaddr + TALLY) & 0x7fff; - - /* crc error */ - dev->stats.rx_crc_errors += - (ioread32(ioaddr + TALLY) & 0x7fff0000) >> 16; - - if (debug) - printk(KERN_DEBUG "%s: exiting interrupt, status=%#4.4x.\n", - dev->name, ioread32(ioaddr + ISR)); - - iowrite32(np->imrvalue, ioaddr + IMR); - - spin_unlock(&np->lock); - - return IRQ_RETVAL(handled); -} - - -/* This routine is logically part of the interrupt handler, but separated - for clarity and better register allocation. */ -static int netdev_rx(struct net_device *dev) -{ - struct netdev_private *np = netdev_priv(dev); - void __iomem *ioaddr = np->mem; - - /* If EOP is set on the next entry, it's a new packet. Send it up. */ - while (!(np->cur_rx->status & RXOWN) && np->cur_rx->skbuff) { - s32 rx_status = np->cur_rx->status; - - if (np->really_rx_count == 0) - break; - - if (debug) - printk(KERN_DEBUG " netdev_rx() status was %8.8x.\n", rx_status); - - if ((!((rx_status & RXFSD) && (rx_status & RXLSD))) || - (rx_status & ErrorSummary)) { - if (rx_status & ErrorSummary) { /* there was a fatal error */ - if (debug) - printk(KERN_DEBUG - "%s: Receive error, Rx status %8.8x.\n", - dev->name, rx_status); - - dev->stats.rx_errors++; /* end of a packet. */ - if (rx_status & (LONGPKT | RUNTPKT)) - dev->stats.rx_length_errors++; - if (rx_status & RXER) - dev->stats.rx_frame_errors++; - if (rx_status & CRC) - dev->stats.rx_crc_errors++; - } else { - int need_to_reset = 0; - int desno = 0; - - if (rx_status & RXFSD) { /* this pkt is too long, over one rx buffer */ - struct fealnx_desc *cur; - - /* check this packet is received completely? */ - cur = np->cur_rx; - while (desno <= np->really_rx_count) { - ++desno; - if ((!(cur->status & RXOWN)) && - (cur->status & RXLSD)) - break; - /* goto next rx descriptor */ - cur = cur->next_desc_logical; - } - if (desno > np->really_rx_count) - need_to_reset = 1; - } else /* RXLSD did not find, something error */ - need_to_reset = 1; - - if (need_to_reset == 0) { - int i; - - dev->stats.rx_length_errors++; - - /* free all rx descriptors related this long pkt */ - for (i = 0; i < desno; ++i) { - if (!np->cur_rx->skbuff) { - printk(KERN_DEBUG - "%s: I'm scared\n", dev->name); - break; - } - np->cur_rx->status = RXOWN; - np->cur_rx = np->cur_rx->next_desc_logical; - } - continue; - } else { /* rx error, need to reset this chip */ - stop_nic_rx(ioaddr, np->crvalue); - reset_rx_descriptors(dev); - iowrite32(np->crvalue, ioaddr + TCRRCR); - } - break; /* exit the while loop */ - } - } else { /* this received pkt is ok */ - - struct sk_buff *skb; - /* Omit the four octet CRC from the length. */ - short pkt_len = ((rx_status & FLNGMASK) >> FLNGShift) - 4; - -#ifndef final_version - if (debug) - printk(KERN_DEBUG " netdev_rx() normal Rx pkt length %d" - " status %x.\n", pkt_len, rx_status); -#endif - - /* Check if the packet is long enough to accept without copying - to a minimally-sized skbuff. */ - if (pkt_len < rx_copybreak && - (skb = netdev_alloc_skb(dev, pkt_len + 2)) != NULL) { - skb_reserve(skb, 2); /* 16 byte align the IP header */ - dma_sync_single_for_cpu(&np->pci_dev->dev, - np->cur_rx->buffer, - np->rx_buf_sz, - DMA_FROM_DEVICE); - /* Call copy + cksum if available. */ - -#if ! defined(__alpha__) - skb_copy_to_linear_data(skb, - np->cur_rx->skbuff->data, pkt_len); - skb_put(skb, pkt_len); -#else - skb_put_data(skb, np->cur_rx->skbuff->data, - pkt_len); -#endif - dma_sync_single_for_device(&np->pci_dev->dev, - np->cur_rx->buffer, - np->rx_buf_sz, - DMA_FROM_DEVICE); - } else { - dma_unmap_single(&np->pci_dev->dev, - np->cur_rx->buffer, - np->rx_buf_sz, - DMA_FROM_DEVICE); - skb_put(skb = np->cur_rx->skbuff, pkt_len); - np->cur_rx->skbuff = NULL; - --np->really_rx_count; - } - skb->protocol = eth_type_trans(skb, dev); - netif_rx(skb); - dev->stats.rx_packets++; - dev->stats.rx_bytes += pkt_len; - } - - np->cur_rx = np->cur_rx->next_desc_logical; - } /* end of while loop */ - - /* allocate skb for rx buffers */ - allocate_rx_buffers(dev); - - return 0; -} - - -static struct net_device_stats *get_stats(struct net_device *dev) -{ - struct netdev_private *np = netdev_priv(dev); - void __iomem *ioaddr = np->mem; - - /* The chip only need report frame silently dropped. */ - if (netif_running(dev)) { - dev->stats.rx_missed_errors += - ioread32(ioaddr + TALLY) & 0x7fff; - dev->stats.rx_crc_errors += - (ioread32(ioaddr + TALLY) & 0x7fff0000) >> 16; - } - - return &dev->stats; -} - - -/* for dev->set_multicast_list */ -static void set_rx_mode(struct net_device *dev) -{ - spinlock_t *lp = &((struct netdev_private *)netdev_priv(dev))->lock; - unsigned long flags; - spin_lock_irqsave(lp, flags); - __set_rx_mode(dev); - spin_unlock_irqrestore(lp, flags); -} - - -/* Take lock before calling */ -static void __set_rx_mode(struct net_device *dev) -{ - struct netdev_private *np = netdev_priv(dev); - void __iomem *ioaddr = np->mem; - u32 mc_filter[2]; /* Multicast hash filter */ - u32 rx_mode; - - if (dev->flags & IFF_PROMISC) { /* Set promiscuous. */ - memset(mc_filter, 0xff, sizeof(mc_filter)); - rx_mode = CR_W_PROM | CR_W_AB | CR_W_AM; - } else if ((netdev_mc_count(dev) > multicast_filter_limit) || - (dev->flags & IFF_ALLMULTI)) { - /* Too many to match, or accept all multicasts. */ - memset(mc_filter, 0xff, sizeof(mc_filter)); - rx_mode = CR_W_AB | CR_W_AM; - } else { - struct netdev_hw_addr *ha; - - memset(mc_filter, 0, sizeof(mc_filter)); - netdev_for_each_mc_addr(ha, dev) { - unsigned int bit; - bit = (ether_crc(ETH_ALEN, ha->addr) >> 26) ^ 0x3F; - mc_filter[bit >> 5] |= (1 << bit); - } - rx_mode = CR_W_AB | CR_W_AM; - } - - stop_nic_rxtx(ioaddr, np->crvalue); - - iowrite32(mc_filter[0], ioaddr + MAR0); - iowrite32(mc_filter[1], ioaddr + MAR1); - np->crvalue &= ~CR_W_RXMODEMASK; - np->crvalue |= rx_mode; - iowrite32(np->crvalue, ioaddr + TCRRCR); -} - -static void netdev_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) -{ - struct netdev_private *np = netdev_priv(dev); - - strscpy(info->driver, DRV_NAME, sizeof(info->driver)); - strscpy(info->bus_info, pci_name(np->pci_dev), sizeof(info->bus_info)); -} - -static int netdev_get_link_ksettings(struct net_device *dev, - struct ethtool_link_ksettings *cmd) -{ - struct netdev_private *np = netdev_priv(dev); - - spin_lock_irq(&np->lock); - mii_ethtool_get_link_ksettings(&np->mii, cmd); - spin_unlock_irq(&np->lock); - - return 0; -} - -static int netdev_set_link_ksettings(struct net_device *dev, - const struct ethtool_link_ksettings *cmd) -{ - struct netdev_private *np = netdev_priv(dev); - int rc; - - spin_lock_irq(&np->lock); - rc = mii_ethtool_set_link_ksettings(&np->mii, cmd); - spin_unlock_irq(&np->lock); - - return rc; -} - -static int netdev_nway_reset(struct net_device *dev) -{ - struct netdev_private *np = netdev_priv(dev); - return mii_nway_restart(&np->mii); -} - -static u32 netdev_get_link(struct net_device *dev) -{ - struct netdev_private *np = netdev_priv(dev); - return mii_link_ok(&np->mii); -} - -static u32 netdev_get_msglevel(struct net_device *dev) -{ - return debug; -} - -static void netdev_set_msglevel(struct net_device *dev, u32 value) -{ - debug = value; -} - -static const struct ethtool_ops netdev_ethtool_ops = { - .get_drvinfo = netdev_get_drvinfo, - .nway_reset = netdev_nway_reset, - .get_link = netdev_get_link, - .get_msglevel = netdev_get_msglevel, - .set_msglevel = netdev_set_msglevel, - .get_link_ksettings = netdev_get_link_ksettings, - .set_link_ksettings = netdev_set_link_ksettings, -}; - -static int mii_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) -{ - struct netdev_private *np = netdev_priv(dev); - int rc; - - if (!netif_running(dev)) - return -EINVAL; - - spin_lock_irq(&np->lock); - rc = generic_mii_ioctl(&np->mii, if_mii(rq), cmd, NULL); - spin_unlock_irq(&np->lock); - - return rc; -} - - -static int netdev_close(struct net_device *dev) -{ - struct netdev_private *np = netdev_priv(dev); - void __iomem *ioaddr = np->mem; - int i; - - netif_stop_queue(dev); - - /* Disable interrupts by clearing the interrupt mask. */ - iowrite32(0x0000, ioaddr + IMR); - - /* Stop the chip's Tx and Rx processes. */ - stop_nic_rxtx(ioaddr, 0); - - del_timer_sync(&np->timer); - del_timer_sync(&np->reset_timer); - - free_irq(np->pci_dev->irq, dev); - - /* Free all the skbuffs in the Rx queue. */ - for (i = 0; i < RX_RING_SIZE; i++) { - struct sk_buff *skb = np->rx_ring[i].skbuff; - - np->rx_ring[i].status = 0; - if (skb) { - dma_unmap_single(&np->pci_dev->dev, - np->rx_ring[i].buffer, np->rx_buf_sz, - DMA_FROM_DEVICE); - dev_kfree_skb(skb); - np->rx_ring[i].skbuff = NULL; - } - } - - for (i = 0; i < TX_RING_SIZE; i++) { - struct sk_buff *skb = np->tx_ring[i].skbuff; - - if (skb) { - dma_unmap_single(&np->pci_dev->dev, - np->tx_ring[i].buffer, skb->len, - DMA_TO_DEVICE); - dev_kfree_skb(skb); - np->tx_ring[i].skbuff = NULL; - } - } - - return 0; -} - -static const struct pci_device_id fealnx_pci_tbl[] = { - {0x1516, 0x0800, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, - {0x1516, 0x0803, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 1}, - {0x1516, 0x0891, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 2}, - {} /* terminate list */ -}; -MODULE_DEVICE_TABLE(pci, fealnx_pci_tbl); - - -static struct pci_driver fealnx_driver = { - .name = "fealnx", - .id_table = fealnx_pci_tbl, - .probe = fealnx_init_one, - .remove = fealnx_remove_one, -}; - -module_pci_driver(fealnx_driver); From fc094058ce01984aa4cb8b580812b16f5429c7e7 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Tue, 25 Oct 2022 14:51:40 -0500 Subject: [PATCH 0406/2002] net: ipa: record the route table size in the IPA structure The non-hashed routing tables for IPv4 and IPv6 will be the same size. And if supported, the hashed routing tables will be the same size as the non-hashed tables. Record the size (number of entries) of all routing tables in the IPA structure. For now, initialize this field using IPA_ROUTE_TABLE_MAX, and just do so when the first route table is validated. Signed-off-by: Alex Elder Signed-off-by: Paolo Abeni --- drivers/net/ipa/ipa.h | 2 ++ drivers/net/ipa/ipa_table.c | 22 +++++++++++++++------- 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/drivers/net/ipa/ipa.h b/drivers/net/ipa/ipa.h index 09ead433ec38..aa39509e209a 100644 --- a/drivers/net/ipa/ipa.h +++ b/drivers/net/ipa/ipa.h @@ -39,6 +39,7 @@ struct ipa_interrupt; * @power: IPA power information * @table_addr: DMA address of filter/route table content * @table_virt: Virtual address of filter/route table content + * @route_count: Total number of entries in a routing table * @interrupt: IPA Interrupt information * @uc_powered: true if power is active by proxy for microcontroller * @uc_loaded: true after microcontroller has reported it's ready @@ -84,6 +85,7 @@ struct ipa { dma_addr_t table_addr; __le64 *table_virt; + u32 route_count; struct ipa_interrupt *interrupt; bool uc_powered; diff --git a/drivers/net/ipa/ipa_table.c b/drivers/net/ipa/ipa_table.c index 58a1a9da3133..0815c8967e91 100644 --- a/drivers/net/ipa/ipa_table.c +++ b/drivers/net/ipa/ipa_table.c @@ -185,7 +185,7 @@ static dma_addr_t ipa_table_addr(struct ipa *ipa, bool filter_mask, u16 count) if (!count) return 0; - WARN_ON(count > max_t(u32, IPA_FILTER_COUNT_MAX, IPA_ROUTE_COUNT_MAX)); + WARN_ON(count > max_t(u32, IPA_FILTER_COUNT_MAX, ipa->route_count)); /* Skip over the zero rule and possibly the filter mask */ skip = filter_mask ? 1 : 2; @@ -302,7 +302,7 @@ static int ipa_route_reset(struct ipa *ipa, bool modem) count = IPA_ROUTE_MODEM_COUNT; } else { first = IPA_ROUTE_MODEM_COUNT; - count = IPA_ROUTE_COUNT_MAX - IPA_ROUTE_MODEM_COUNT; + count = ipa->route_count - IPA_ROUTE_MODEM_COUNT; } ipa_table_reset_add(trans, false, first, count, IPA_MEM_V4_ROUTE); @@ -552,7 +552,7 @@ static void ipa_route_config(struct ipa *ipa, bool modem) if (!ipa_table_hash_support(ipa)) return; - for (route_id = 0; route_id < IPA_ROUTE_COUNT_MAX; route_id++) + for (route_id = 0; route_id < ipa->route_count; route_id++) if (ipa_route_id_modem(route_id) == modem) ipa_route_tuple_zero(ipa, route_id); } @@ -566,7 +566,9 @@ void ipa_table_config(struct ipa *ipa) ipa_route_config(ipa, true); } -/* Zero modem_route_count means filter table memory check */ +/* Verify the sizes of all IPA table filter or routing table memory regions + * are valid. If valid, this records the size of the routing table. + */ bool ipa_table_mem_valid(struct ipa *ipa, bool modem_route_count) { bool hash_support = ipa_table_hash_support(ipa); @@ -591,6 +593,10 @@ bool ipa_table_mem_valid(struct ipa *ipa, bool modem_route_count) if (mem_ipv4->size != mem_ipv6->size) return false; + /* Record the number of routing table entries */ + if (!filter) + ipa->route_count = IPA_ROUTE_COUNT_MAX; + /* Table offset and size must fit in TABLE_INIT command fields */ if (!ipa_cmd_table_init_valid(ipa, mem_ipv4, !filter)) return false; @@ -671,7 +677,7 @@ bool ipa_table_mem_valid(struct ipa *ipa, bool modem_route_count) * | ---- zero rule address | \ * |\ |-------------------| | * | ---- zero rule address | | IPA_FILTER_COUNT_MAX - * | |-------------------| > or IPA_ROUTE_COUNT_MAX, + * | |-------------------| > or IPA route count, * | ... | whichever is greater * \ |-------------------| | * ---- zero rule address | / @@ -679,15 +685,17 @@ bool ipa_table_mem_valid(struct ipa *ipa, bool modem_route_count) */ int ipa_table_init(struct ipa *ipa) { - u32 count = max_t(u32, IPA_FILTER_COUNT_MAX, IPA_ROUTE_COUNT_MAX); struct device *dev = &ipa->pdev->dev; dma_addr_t addr; __le64 le_addr; __le64 *virt; size_t size; + u32 count; ipa_table_validate_build(); + count = max_t(u32, IPA_FILTER_COUNT_MAX, ipa->route_count); + /* The IPA hardware requires route and filter table rules to be * aligned on a 128-byte boundary. We put the "zero rule" at the * base of the table area allocated here. The DMA address returned @@ -722,7 +730,7 @@ int ipa_table_init(struct ipa *ipa) void ipa_table_exit(struct ipa *ipa) { - u32 count = max_t(u32, 1 + IPA_FILTER_COUNT_MAX, IPA_ROUTE_COUNT_MAX); + u32 count = max_t(u32, 1 + IPA_FILTER_COUNT_MAX, ipa->route_count); struct device *dev = &ipa->pdev->dev; size_t size; From 0439e6743c5c77520e91bf52a0d16da586214753 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Tue, 25 Oct 2022 14:51:41 -0500 Subject: [PATCH 0407/2002] net: ipa: determine route table size from memory region Currently we assume that any routing table contains a fixed number of entries. The number of entries in a routing table can actually vary, depending only on the size of the IPA-local memory region used to hold the table. Stop assuming that a routing table has exactly 15 entries. Instead, determine the number of entries in a routing table by dividing its memory region size by the size of an entry. The number of entries is computed early, when ipa_table_mem_valid() is called by ipa_table_init(). Signed-off-by: Alex Elder Signed-off-by: Paolo Abeni --- drivers/net/ipa/ipa_cmd.c | 17 ++++++++--------- drivers/net/ipa/ipa_table.c | 14 +++++--------- drivers/net/ipa/ipa_table.h | 3 --- 3 files changed, 13 insertions(+), 21 deletions(-) diff --git a/drivers/net/ipa/ipa_cmd.c b/drivers/net/ipa/ipa_cmd.c index de2cd86aa9e2..08e3f395a945 100644 --- a/drivers/net/ipa/ipa_cmd.c +++ b/drivers/net/ipa/ipa_cmd.c @@ -145,18 +145,15 @@ union ipa_cmd_payload { static void ipa_cmd_validate_build(void) { - /* The sizes of a filter and route tables need to fit into fields - * in the ipa_cmd_hw_ip_fltrt_init structure. Although hashed tables + /* The size of a filter table needs to fit into fields in the + * ipa_cmd_hw_ip_fltrt_init structure. Although hashed tables * might not be used, non-hashed and hashed tables have the same * maximum size. IPv4 and IPv6 filter tables have the same number - * of entries, as and IPv4 and IPv6 route tables have the same number * of entries. */ -#define TABLE_SIZE (TABLE_COUNT_MAX * sizeof(__le64)) -#define TABLE_COUNT_MAX max_t(u32, IPA_ROUTE_COUNT_MAX, IPA_FILTER_COUNT_MAX) +#define TABLE_SIZE (IPA_FILTER_COUNT_MAX * sizeof(__le64)) BUILD_BUG_ON(TABLE_SIZE > field_max(IP_FLTRT_FLAGS_HASH_SIZE_FMASK)); BUILD_BUG_ON(TABLE_SIZE > field_max(IP_FLTRT_FLAGS_NHASH_SIZE_FMASK)); -#undef TABLE_COUNT_MAX #undef TABLE_SIZE /* Hashed and non-hashed fields are assumed to be the same size */ @@ -178,12 +175,14 @@ bool ipa_cmd_table_init_valid(struct ipa *ipa, const struct ipa_mem *mem, u32 size_max = field_max(IP_FLTRT_FLAGS_NHASH_SIZE_FMASK); const char *table = route ? "route" : "filter"; struct device *dev = &ipa->pdev->dev; + u32 size; + + size = route ? ipa->route_count * sizeof(__le64) : mem->size; /* Size must fit in the immediate command field that holds it */ - if (mem->size > size_max) { + if (size > size_max) { dev_err(dev, "%s table region size too large\n", table); - dev_err(dev, " (0x%04x > 0x%04x)\n", - mem->size, size_max); + dev_err(dev, " (0x%04x > 0x%04x)\n", size, size_max); return false; } diff --git a/drivers/net/ipa/ipa_table.c b/drivers/net/ipa/ipa_table.c index 0815c8967e91..23d3f081ac8e 100644 --- a/drivers/net/ipa/ipa_table.c +++ b/drivers/net/ipa/ipa_table.c @@ -130,12 +130,8 @@ static void ipa_table_validate_build(void) */ BUILD_BUG_ON(IPA_ZERO_RULE_SIZE != sizeof(__le64)); - /* Impose a practical limit on the number of routes */ - BUILD_BUG_ON(IPA_ROUTE_COUNT_MAX > 32); /* The modem must be allotted at least one route table entry */ BUILD_BUG_ON(!IPA_ROUTE_MODEM_COUNT); - /* AP must too, but we can't use more than what is available */ - BUILD_BUG_ON(IPA_ROUTE_MODEM_COUNT >= IPA_ROUTE_COUNT_MAX); } static const struct ipa_mem * @@ -593,18 +589,18 @@ bool ipa_table_mem_valid(struct ipa *ipa, bool modem_route_count) if (mem_ipv4->size != mem_ipv6->size) return false; - /* Record the number of routing table entries */ + /* Compute the number of entries, and for routing tables, record it */ + count = mem_ipv4->size / sizeof(__le64); + if (count < 2) + return false; if (!filter) - ipa->route_count = IPA_ROUTE_COUNT_MAX; + ipa->route_count = count; /* Table offset and size must fit in TABLE_INIT command fields */ if (!ipa_cmd_table_init_valid(ipa, mem_ipv4, !filter)) return false; /* Make sure the regions are big enough */ - count = mem_ipv4->size / sizeof(__le64); - if (count < 2) - return false; if (filter) { /* Filter tables must able to hold the endpoint bitmap plus * an entry for each endpoint that supports filtering diff --git a/drivers/net/ipa/ipa_table.h b/drivers/net/ipa/ipa_table.h index 65d96debd391..31363292dc1d 100644 --- a/drivers/net/ipa/ipa_table.h +++ b/drivers/net/ipa/ipa_table.h @@ -16,9 +16,6 @@ struct ipa; /* The number of route table entries allotted to the modem */ #define IPA_ROUTE_MODEM_COUNT 8 -/* The maximum number of route table entries (IPv4, IPv6; hashed or not) */ -#define IPA_ROUTE_COUNT_MAX 15 - /** * ipa_filter_map_valid() - Validate a filter table endpoint bitmap * @ipa: IPA pointer From 8defab8bdfb1d0dc4e4e3c687cfde33b596896f7 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Tue, 25 Oct 2022 14:51:42 -0500 Subject: [PATCH 0408/2002] net: ipa: don't assume 8 modem routing table entries Currently all platforms are assumed allot 8 routing table entries for use by the modem. Instead, add a new configuration data entry that defines the number of modem routing table entries, and record that in the IPA structure. Signed-off-by: Alex Elder Signed-off-by: Paolo Abeni --- drivers/net/ipa/data/ipa_data-v3.1.c | 19 +++++++++--------- drivers/net/ipa/data/ipa_data-v3.5.1.c | 27 +++++++++++++------------- drivers/net/ipa/data/ipa_data-v4.11.c | 17 ++++++++-------- drivers/net/ipa/data/ipa_data-v4.2.c | 17 ++++++++-------- drivers/net/ipa/data/ipa_data-v4.5.c | 17 ++++++++-------- drivers/net/ipa/data/ipa_data-v4.9.c | 17 ++++++++-------- drivers/net/ipa/ipa.h | 2 ++ drivers/net/ipa/ipa_data.h | 2 ++ drivers/net/ipa/ipa_main.c | 6 ++++++ drivers/net/ipa/ipa_mem.c | 4 ++-- drivers/net/ipa/ipa_qmi.c | 9 +++++---- drivers/net/ipa/ipa_table.c | 21 +++++++++----------- drivers/net/ipa/ipa_table.h | 7 ++----- 13 files changed, 88 insertions(+), 77 deletions(-) diff --git a/drivers/net/ipa/data/ipa_data-v3.1.c b/drivers/net/ipa/data/ipa_data-v3.1.c index e0d71f609272..3380fb3483b2 100644 --- a/drivers/net/ipa/data/ipa_data-v3.1.c +++ b/drivers/net/ipa/data/ipa_data-v3.1.c @@ -525,13 +525,14 @@ static const struct ipa_power_data ipa_power_data = { /* Configuration data for an SoC having IPA v3.1 */ const struct ipa_data ipa_data_v3_1 = { - .version = IPA_VERSION_3_1, - .backward_compat = BIT(BCR_CMDQ_L_LACK_ONE_ENTRY), - .qsb_count = ARRAY_SIZE(ipa_qsb_data), - .qsb_data = ipa_qsb_data, - .endpoint_count = ARRAY_SIZE(ipa_gsi_endpoint_data), - .endpoint_data = ipa_gsi_endpoint_data, - .resource_data = &ipa_resource_data, - .mem_data = &ipa_mem_data, - .power_data = &ipa_power_data, + .version = IPA_VERSION_3_1, + .backward_compat = BIT(BCR_CMDQ_L_LACK_ONE_ENTRY), + .qsb_count = ARRAY_SIZE(ipa_qsb_data), + .qsb_data = ipa_qsb_data, + .modem_route_count = 8, + .endpoint_count = ARRAY_SIZE(ipa_gsi_endpoint_data), + .endpoint_data = ipa_gsi_endpoint_data, + .resource_data = &ipa_resource_data, + .mem_data = &ipa_mem_data, + .power_data = &ipa_power_data, }; diff --git a/drivers/net/ipa/data/ipa_data-v3.5.1.c b/drivers/net/ipa/data/ipa_data-v3.5.1.c index 383ef1890065..47591faf8bd6 100644 --- a/drivers/net/ipa/data/ipa_data-v3.5.1.c +++ b/drivers/net/ipa/data/ipa_data-v3.5.1.c @@ -406,17 +406,18 @@ static const struct ipa_power_data ipa_power_data = { /* Configuration data for an SoC having IPA v3.5.1 */ const struct ipa_data ipa_data_v3_5_1 = { - .version = IPA_VERSION_3_5_1, - .backward_compat = BIT(BCR_CMDQ_L_LACK_ONE_ENTRY) | - BIT(BCR_TX_NOT_USING_BRESP) | - BIT(BCR_SUSPEND_L2_IRQ) | - BIT(BCR_HOLB_DROP_L2_IRQ) | - BIT(BCR_DUAL_TX), - .qsb_count = ARRAY_SIZE(ipa_qsb_data), - .qsb_data = ipa_qsb_data, - .endpoint_count = ARRAY_SIZE(ipa_gsi_endpoint_data), - .endpoint_data = ipa_gsi_endpoint_data, - .resource_data = &ipa_resource_data, - .mem_data = &ipa_mem_data, - .power_data = &ipa_power_data, + .version = IPA_VERSION_3_5_1, + .backward_compat = BIT(BCR_CMDQ_L_LACK_ONE_ENTRY) | + BIT(BCR_TX_NOT_USING_BRESP) | + BIT(BCR_SUSPEND_L2_IRQ) | + BIT(BCR_HOLB_DROP_L2_IRQ) | + BIT(BCR_DUAL_TX), + .qsb_count = ARRAY_SIZE(ipa_qsb_data), + .qsb_data = ipa_qsb_data, + .modem_route_count = 8, + .endpoint_count = ARRAY_SIZE(ipa_gsi_endpoint_data), + .endpoint_data = ipa_gsi_endpoint_data, + .resource_data = &ipa_resource_data, + .mem_data = &ipa_mem_data, + .power_data = &ipa_power_data, }; diff --git a/drivers/net/ipa/data/ipa_data-v4.11.c b/drivers/net/ipa/data/ipa_data-v4.11.c index a204e439c23d..1b4b52501ee3 100644 --- a/drivers/net/ipa/data/ipa_data-v4.11.c +++ b/drivers/net/ipa/data/ipa_data-v4.11.c @@ -394,12 +394,13 @@ static const struct ipa_power_data ipa_power_data = { /* Configuration data for an SoC having IPA v4.11 */ const struct ipa_data ipa_data_v4_11 = { - .version = IPA_VERSION_4_11, - .qsb_count = ARRAY_SIZE(ipa_qsb_data), - .qsb_data = ipa_qsb_data, - .endpoint_count = ARRAY_SIZE(ipa_gsi_endpoint_data), - .endpoint_data = ipa_gsi_endpoint_data, - .resource_data = &ipa_resource_data, - .mem_data = &ipa_mem_data, - .power_data = &ipa_power_data, + .version = IPA_VERSION_4_11, + .qsb_count = ARRAY_SIZE(ipa_qsb_data), + .qsb_data = ipa_qsb_data, + .modem_route_count = 8, + .endpoint_count = ARRAY_SIZE(ipa_gsi_endpoint_data), + .endpoint_data = ipa_gsi_endpoint_data, + .resource_data = &ipa_resource_data, + .mem_data = &ipa_mem_data, + .power_data = &ipa_power_data, }; diff --git a/drivers/net/ipa/data/ipa_data-v4.2.c b/drivers/net/ipa/data/ipa_data-v4.2.c index 04f574fe006f..199ed0ed868b 100644 --- a/drivers/net/ipa/data/ipa_data-v4.2.c +++ b/drivers/net/ipa/data/ipa_data-v4.2.c @@ -372,13 +372,14 @@ static const struct ipa_power_data ipa_power_data = { /* Configuration data for an SoC having IPA v4.2 */ const struct ipa_data ipa_data_v4_2 = { - .version = IPA_VERSION_4_2, + .version = IPA_VERSION_4_2, /* backward_compat value is 0 */ - .qsb_count = ARRAY_SIZE(ipa_qsb_data), - .qsb_data = ipa_qsb_data, - .endpoint_count = ARRAY_SIZE(ipa_gsi_endpoint_data), - .endpoint_data = ipa_gsi_endpoint_data, - .resource_data = &ipa_resource_data, - .mem_data = &ipa_mem_data, - .power_data = &ipa_power_data, + .qsb_count = ARRAY_SIZE(ipa_qsb_data), + .qsb_data = ipa_qsb_data, + .modem_route_count = 8, + .endpoint_count = ARRAY_SIZE(ipa_gsi_endpoint_data), + .endpoint_data = ipa_gsi_endpoint_data, + .resource_data = &ipa_resource_data, + .mem_data = &ipa_mem_data, + .power_data = &ipa_power_data, }; diff --git a/drivers/net/ipa/data/ipa_data-v4.5.c b/drivers/net/ipa/data/ipa_data-v4.5.c index 684239e71f46..19b549f2998b 100644 --- a/drivers/net/ipa/data/ipa_data-v4.5.c +++ b/drivers/net/ipa/data/ipa_data-v4.5.c @@ -450,12 +450,13 @@ static const struct ipa_power_data ipa_power_data = { /* Configuration data for an SoC having IPA v4.5 */ const struct ipa_data ipa_data_v4_5 = { - .version = IPA_VERSION_4_5, - .qsb_count = ARRAY_SIZE(ipa_qsb_data), - .qsb_data = ipa_qsb_data, - .endpoint_count = ARRAY_SIZE(ipa_gsi_endpoint_data), - .endpoint_data = ipa_gsi_endpoint_data, - .resource_data = &ipa_resource_data, - .mem_data = &ipa_mem_data, - .power_data = &ipa_power_data, + .version = IPA_VERSION_4_5, + .qsb_count = ARRAY_SIZE(ipa_qsb_data), + .qsb_data = ipa_qsb_data, + .modem_route_count = 8, + .endpoint_count = ARRAY_SIZE(ipa_gsi_endpoint_data), + .endpoint_data = ipa_gsi_endpoint_data, + .resource_data = &ipa_resource_data, + .mem_data = &ipa_mem_data, + .power_data = &ipa_power_data, }; diff --git a/drivers/net/ipa/data/ipa_data-v4.9.c b/drivers/net/ipa/data/ipa_data-v4.9.c index 2333e15f9533..d30fc1fe6ca2 100644 --- a/drivers/net/ipa/data/ipa_data-v4.9.c +++ b/drivers/net/ipa/data/ipa_data-v4.9.c @@ -444,12 +444,13 @@ static const struct ipa_power_data ipa_power_data = { /* Configuration data for an SoC having IPA v4.9. */ const struct ipa_data ipa_data_v4_9 = { - .version = IPA_VERSION_4_9, - .qsb_count = ARRAY_SIZE(ipa_qsb_data), - .qsb_data = ipa_qsb_data, - .endpoint_count = ARRAY_SIZE(ipa_gsi_endpoint_data), - .endpoint_data = ipa_gsi_endpoint_data, - .resource_data = &ipa_resource_data, - .mem_data = &ipa_mem_data, - .power_data = &ipa_power_data, + .version = IPA_VERSION_4_9, + .qsb_count = ARRAY_SIZE(ipa_qsb_data), + .qsb_data = ipa_qsb_data, + .modem_route_count = 8, + .endpoint_count = ARRAY_SIZE(ipa_gsi_endpoint_data), + .endpoint_data = ipa_gsi_endpoint_data, + .resource_data = &ipa_resource_data, + .mem_data = &ipa_mem_data, + .power_data = &ipa_power_data, }; diff --git a/drivers/net/ipa/ipa.h b/drivers/net/ipa/ipa.h index aa39509e209a..5c95acc70bb3 100644 --- a/drivers/net/ipa/ipa.h +++ b/drivers/net/ipa/ipa.h @@ -40,6 +40,7 @@ struct ipa_interrupt; * @table_addr: DMA address of filter/route table content * @table_virt: Virtual address of filter/route table content * @route_count: Total number of entries in a routing table + * @modem_route_count: Number of modem entries in a routing table * @interrupt: IPA Interrupt information * @uc_powered: true if power is active by proxy for microcontroller * @uc_loaded: true after microcontroller has reported it's ready @@ -86,6 +87,7 @@ struct ipa { dma_addr_t table_addr; __le64 *table_virt; u32 route_count; + u32 modem_route_count; struct ipa_interrupt *interrupt; bool uc_powered; diff --git a/drivers/net/ipa/ipa_data.h b/drivers/net/ipa/ipa_data.h index e5a6ce75c7dd..412edbfac786 100644 --- a/drivers/net/ipa/ipa_data.h +++ b/drivers/net/ipa/ipa_data.h @@ -222,6 +222,7 @@ struct ipa_power_data { * @backward_compat: BCR register value (prior to IPA v4.5 only) * @qsb_count: number of entries in the qsb_data array * @qsb_data: Qualcomm System Bus configuration data + * @modem_route_count: number of modem entries in a routing table * @endpoint_count: number of entries in the endpoint_data array * @endpoint_data: IPA endpoint/GSI channel data * @resource_data: IPA resource configuration data @@ -233,6 +234,7 @@ struct ipa_data { u32 backward_compat; u32 qsb_count; /* number of entries in qsb_data[] */ const struct ipa_qsb_data *qsb_data; + u32 modem_route_count; u32 endpoint_count; /* number of entries in endpoint_data[] */ const struct ipa_gsi_endpoint_data *endpoint_data; const struct ipa_resource_data *resource_data; diff --git a/drivers/net/ipa/ipa_main.c b/drivers/net/ipa/ipa_main.c index 3461ad3029ab..ce5eadad671f 100644 --- a/drivers/net/ipa/ipa_main.c +++ b/drivers/net/ipa/ipa_main.c @@ -739,6 +739,11 @@ static int ipa_probe(struct platform_device *pdev) return -EINVAL; } + if (!data->modem_route_count) { + dev_err(dev, "modem_route_count cannot be zero\n"); + return -EINVAL; + } + /* If we need Trust Zone, make sure it's available */ modem_init = of_property_read_bool(dev->of_node, "modem-init"); if (!modem_init) @@ -763,6 +768,7 @@ static int ipa_probe(struct platform_device *pdev) dev_set_drvdata(dev, ipa); ipa->power = power; ipa->version = data->version; + ipa->modem_route_count = data->modem_route_count; init_completion(&ipa->completion); ret = ipa_reg_init(ipa); diff --git a/drivers/net/ipa/ipa_mem.c b/drivers/net/ipa/ipa_mem.c index a3d2317452ac..cb9540201839 100644 --- a/drivers/net/ipa/ipa_mem.c +++ b/drivers/net/ipa/ipa_mem.c @@ -618,9 +618,9 @@ int ipa_mem_init(struct ipa *ipa, const struct ipa_mem_data *mem_data) ipa->mem = mem_data->local; /* Check the route and filter table memory regions */ - if (!ipa_table_mem_valid(ipa, 0)) + if (!ipa_table_mem_valid(ipa, false)) return -EINVAL; - if (!ipa_table_mem_valid(ipa, IPA_ROUTE_MODEM_COUNT)) + if (!ipa_table_mem_valid(ipa, true)) return -EINVAL; ret = dma_set_mask_and_coherent(&ipa->pdev->dev, DMA_BIT_MASK(64)); diff --git a/drivers/net/ipa/ipa_qmi.c b/drivers/net/ipa/ipa_qmi.c index 8295fd4b70d1..f70f0a1d1cda 100644 --- a/drivers/net/ipa/ipa_qmi.c +++ b/drivers/net/ipa/ipa_qmi.c @@ -284,6 +284,7 @@ static const struct ipa_init_modem_driver_req * init_modem_driver_req(struct ipa_qmi *ipa_qmi) { struct ipa *ipa = container_of(ipa_qmi, struct ipa, qmi); + u32 modem_route_count = ipa->modem_route_count; static struct ipa_init_modem_driver_req req; const struct ipa_mem *mem; @@ -308,12 +309,12 @@ init_modem_driver_req(struct ipa_qmi *ipa_qmi) mem = ipa_mem_find(ipa, IPA_MEM_V4_ROUTE); req.v4_route_tbl_info_valid = 1; req.v4_route_tbl_info.start = ipa->mem_offset + mem->offset; - req.v4_route_tbl_info.end = IPA_ROUTE_MODEM_COUNT - 1; + req.v4_route_tbl_info.end = modem_route_count - 1; mem = ipa_mem_find(ipa, IPA_MEM_V6_ROUTE); req.v6_route_tbl_info_valid = 1; req.v6_route_tbl_info.start = ipa->mem_offset + mem->offset; - req.v6_route_tbl_info.end = IPA_ROUTE_MODEM_COUNT - 1; + req.v6_route_tbl_info.end = modem_route_count - 1; mem = ipa_mem_find(ipa, IPA_MEM_V4_FILTER); req.v4_filter_tbl_start_valid = 1; @@ -352,7 +353,7 @@ init_modem_driver_req(struct ipa_qmi *ipa_qmi) req.v4_hash_route_tbl_info_valid = 1; req.v4_hash_route_tbl_info.start = ipa->mem_offset + mem->offset; - req.v4_hash_route_tbl_info.end = IPA_ROUTE_MODEM_COUNT - 1; + req.v4_hash_route_tbl_info.end = modem_route_count - 1; } mem = ipa_mem_find(ipa, IPA_MEM_V6_ROUTE_HASHED); @@ -360,7 +361,7 @@ init_modem_driver_req(struct ipa_qmi *ipa_qmi) req.v6_hash_route_tbl_info_valid = 1; req.v6_hash_route_tbl_info.start = ipa->mem_offset + mem->offset; - req.v6_hash_route_tbl_info.end = IPA_ROUTE_MODEM_COUNT - 1; + req.v6_hash_route_tbl_info.end = modem_route_count - 1; } mem = ipa_mem_find(ipa, IPA_MEM_V4_FILTER_HASHED); diff --git a/drivers/net/ipa/ipa_table.c b/drivers/net/ipa/ipa_table.c index 23d3f081ac8e..c9ab6a3fabbc 100644 --- a/drivers/net/ipa/ipa_table.c +++ b/drivers/net/ipa/ipa_table.c @@ -129,9 +129,6 @@ static void ipa_table_validate_build(void) * assumes that it can be written using a pointer to __le64. */ BUILD_BUG_ON(IPA_ZERO_RULE_SIZE != sizeof(__le64)); - - /* The modem must be allotted at least one route table entry */ - BUILD_BUG_ON(!IPA_ROUTE_MODEM_COUNT); } static const struct ipa_mem * @@ -281,6 +278,7 @@ static int ipa_filter_reset(struct ipa *ipa, bool modem) * */ static int ipa_route_reset(struct ipa *ipa, bool modem) { + u32 modem_route_count = ipa->modem_route_count; struct gsi_trans *trans; u16 first; u16 count; @@ -295,10 +293,10 @@ static int ipa_route_reset(struct ipa *ipa, bool modem) if (modem) { first = 0; - count = IPA_ROUTE_MODEM_COUNT; + count = modem_route_count; } else { - first = IPA_ROUTE_MODEM_COUNT; - count = ipa->route_count - IPA_ROUTE_MODEM_COUNT; + first = modem_route_count; + count = ipa->route_count - modem_route_count; } ipa_table_reset_add(trans, false, first, count, IPA_MEM_V4_ROUTE); @@ -511,9 +509,9 @@ static void ipa_filter_config(struct ipa *ipa, bool modem) } } -static bool ipa_route_id_modem(u32 route_id) +static bool ipa_route_id_modem(struct ipa *ipa, u32 route_id) { - return route_id < IPA_ROUTE_MODEM_COUNT; + return route_id < ipa->modem_route_count; } /** @@ -549,7 +547,7 @@ static void ipa_route_config(struct ipa *ipa, bool modem) return; for (route_id = 0; route_id < ipa->route_count; route_id++) - if (ipa_route_id_modem(route_id) == modem) + if (ipa_route_id_modem(ipa, route_id) == modem) ipa_route_tuple_zero(ipa, route_id); } @@ -565,10 +563,9 @@ void ipa_table_config(struct ipa *ipa) /* Verify the sizes of all IPA table filter or routing table memory regions * are valid. If valid, this records the size of the routing table. */ -bool ipa_table_mem_valid(struct ipa *ipa, bool modem_route_count) +bool ipa_table_mem_valid(struct ipa *ipa, bool filter) { bool hash_support = ipa_table_hash_support(ipa); - bool filter = !modem_route_count; const struct ipa_mem *mem_hashed; const struct ipa_mem *mem_ipv4; const struct ipa_mem *mem_ipv6; @@ -611,7 +608,7 @@ bool ipa_table_mem_valid(struct ipa *ipa, bool modem_route_count) /* Routing tables must be able to hold all modem entries, * plus at least one entry for the AP. */ - if (count < modem_route_count + 1) + if (count < ipa->modem_route_count + 1) return false; } diff --git a/drivers/net/ipa/ipa_table.h b/drivers/net/ipa/ipa_table.h index 31363292dc1d..79583b16f363 100644 --- a/drivers/net/ipa/ipa_table.h +++ b/drivers/net/ipa/ipa_table.h @@ -13,9 +13,6 @@ struct ipa; /* The maximum number of filter table entries (IPv4, IPv6; hashed or not) */ #define IPA_FILTER_COUNT_MAX 14 -/* The number of route table entries allotted to the modem */ -#define IPA_ROUTE_MODEM_COUNT 8 - /** * ipa_filter_map_valid() - Validate a filter table endpoint bitmap * @ipa: IPA pointer @@ -78,8 +75,8 @@ void ipa_table_exit(struct ipa *ipa); /** * ipa_table_mem_valid() - Validate sizes of table memory regions * @ipa: IPA pointer - * @modem_route_count: Number of modem route table entries + * @filter: Whether to check filter or routing tables */ -bool ipa_table_mem_valid(struct ipa *ipa, bool modem_route_count); +bool ipa_table_mem_valid(struct ipa *ipa, bool filter); #endif /* _IPA_TABLE_H_ */ From f787d84830152be76b76470ac865d2033285c2d9 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Tue, 25 Oct 2022 14:51:43 -0500 Subject: [PATCH 0409/2002] net: ipa: determine filter table size from memory region Currently we assume that any filter table contains a fixed number of entries. Like routing tables, the number of entries in a filter table is limited only by the size of the IPA-local memory region used to hold the table. Stop assuming that a filter table has exactly 14 entries. Instead, determine the number of entries in a routing table by dividing its memory region size by the size of an entry. (Note that the first "entry" in a filter table contains an endpoint bitmap.) Signed-off-by: Alex Elder Signed-off-by: Paolo Abeni --- drivers/net/ipa/ipa.h | 2 ++ drivers/net/ipa/ipa_cmd.c | 8 ++------ drivers/net/ipa/ipa_table.c | 20 +++++++++++--------- drivers/net/ipa/ipa_table.h | 3 --- 4 files changed, 15 insertions(+), 18 deletions(-) diff --git a/drivers/net/ipa/ipa.h b/drivers/net/ipa/ipa.h index 5c95acc70bb3..82225316a2e2 100644 --- a/drivers/net/ipa/ipa.h +++ b/drivers/net/ipa/ipa.h @@ -41,6 +41,7 @@ struct ipa_interrupt; * @table_virt: Virtual address of filter/route table content * @route_count: Total number of entries in a routing table * @modem_route_count: Number of modem entries in a routing table + * @filter_count: Maximum number of entries in a filter table * @interrupt: IPA Interrupt information * @uc_powered: true if power is active by proxy for microcontroller * @uc_loaded: true after microcontroller has reported it's ready @@ -88,6 +89,7 @@ struct ipa { __le64 *table_virt; u32 route_count; u32 modem_route_count; + u32 filter_count; struct ipa_interrupt *interrupt; bool uc_powered; diff --git a/drivers/net/ipa/ipa_cmd.c b/drivers/net/ipa/ipa_cmd.c index 08e3f395a945..bb3dfa9a2bc8 100644 --- a/drivers/net/ipa/ipa_cmd.c +++ b/drivers/net/ipa/ipa_cmd.c @@ -151,11 +151,6 @@ static void ipa_cmd_validate_build(void) * maximum size. IPv4 and IPv6 filter tables have the same number * of entries. */ -#define TABLE_SIZE (IPA_FILTER_COUNT_MAX * sizeof(__le64)) - BUILD_BUG_ON(TABLE_SIZE > field_max(IP_FLTRT_FLAGS_HASH_SIZE_FMASK)); - BUILD_BUG_ON(TABLE_SIZE > field_max(IP_FLTRT_FLAGS_NHASH_SIZE_FMASK)); -#undef TABLE_SIZE - /* Hashed and non-hashed fields are assumed to be the same size */ BUILD_BUG_ON(field_max(IP_FLTRT_FLAGS_HASH_SIZE_FMASK) != field_max(IP_FLTRT_FLAGS_NHASH_SIZE_FMASK)); @@ -177,7 +172,8 @@ bool ipa_cmd_table_init_valid(struct ipa *ipa, const struct ipa_mem *mem, struct device *dev = &ipa->pdev->dev; u32 size; - size = route ? ipa->route_count * sizeof(__le64) : mem->size; + size = route ? ipa->route_count : ipa->filter_count + 1; + size *= sizeof(__le64); /* Size must fit in the immediate command field that holds it */ if (size > size_max) { diff --git a/drivers/net/ipa/ipa_table.c b/drivers/net/ipa/ipa_table.c index c9ab6a3fabbc..db1992eaafaa 100644 --- a/drivers/net/ipa/ipa_table.c +++ b/drivers/net/ipa/ipa_table.c @@ -160,9 +160,9 @@ bool ipa_filter_map_valid(struct ipa *ipa, u32 filter_map) } count = hweight32(filter_map); - if (count > IPA_FILTER_COUNT_MAX) { + if (count > ipa->filter_count) { dev_err(dev, "too many filtering endpoints (%u, max %u)\n", - count, IPA_FILTER_COUNT_MAX); + count, ipa->filter_count); return false; } @@ -178,7 +178,7 @@ static dma_addr_t ipa_table_addr(struct ipa *ipa, bool filter_mask, u16 count) if (!count) return 0; - WARN_ON(count > max_t(u32, IPA_FILTER_COUNT_MAX, ipa->route_count)); + WARN_ON(count > max_t(u32, ipa->filter_count, ipa->route_count)); /* Skip over the zero rule and possibly the filter mask */ skip = filter_mask ? 1 : 2; @@ -586,11 +586,13 @@ bool ipa_table_mem_valid(struct ipa *ipa, bool filter) if (mem_ipv4->size != mem_ipv6->size) return false; - /* Compute the number of entries, and for routing tables, record it */ + /* Compute and record the number of entries for each table type */ count = mem_ipv4->size / sizeof(__le64); if (count < 2) return false; - if (!filter) + if (filter) + ipa->filter_count = count - 1; /* Filter map in first entry */ + else ipa->route_count = count; /* Table offset and size must fit in TABLE_INIT command fields */ @@ -645,7 +647,7 @@ bool ipa_table_mem_valid(struct ipa *ipa, bool filter) * * The first entry in a filter table contains a bitmap indicating which * endpoints contain entries in the table. In addition to that first entry, - * there are at most IPA_FILTER_COUNT_MAX entries that follow. Filter table + * there is a fixed maximum number of entries that follow. Filter table * entries are 64 bits wide, and (other than the bitmap) contain the DMA * address of a filter rule. A "zero rule" indicates no filtering, and * consists of 64 bits of zeroes. When a filter table is initialized (or @@ -669,7 +671,7 @@ bool ipa_table_mem_valid(struct ipa *ipa, bool filter) * |\ |-------------------| * | ---- zero rule address | \ * |\ |-------------------| | - * | ---- zero rule address | | IPA_FILTER_COUNT_MAX + * | ---- zero rule address | | Max IPA filter count * | |-------------------| > or IPA route count, * | ... | whichever is greater * \ |-------------------| | @@ -687,7 +689,7 @@ int ipa_table_init(struct ipa *ipa) ipa_table_validate_build(); - count = max_t(u32, IPA_FILTER_COUNT_MAX, ipa->route_count); + count = max_t(u32, ipa->filter_count, ipa->route_count); /* The IPA hardware requires route and filter table rules to be * aligned on a 128-byte boundary. We put the "zero rule" at the @@ -723,7 +725,7 @@ int ipa_table_init(struct ipa *ipa) void ipa_table_exit(struct ipa *ipa) { - u32 count = max_t(u32, 1 + IPA_FILTER_COUNT_MAX, ipa->route_count); + u32 count = max_t(u32, 1 + ipa->filter_count, ipa->route_count); struct device *dev = &ipa->pdev->dev; size_t size; diff --git a/drivers/net/ipa/ipa_table.h b/drivers/net/ipa/ipa_table.h index 79583b16f363..8a4dcd7df4c0 100644 --- a/drivers/net/ipa/ipa_table.h +++ b/drivers/net/ipa/ipa_table.h @@ -10,9 +10,6 @@ struct ipa; -/* The maximum number of filter table entries (IPv4, IPv6; hashed or not) */ -#define IPA_FILTER_COUNT_MAX 14 - /** * ipa_filter_map_valid() - Validate a filter table endpoint bitmap * @ipa: IPA pointer From 12d6c1d3a2ad0c199ec57c201cdc71e8e157a232 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Tue, 25 Oct 2022 15:39:35 -0700 Subject: [PATCH 0410/2002] skbuff: Proactively round up to kmalloc bucket size Instead of discovering the kmalloc bucket size _after_ allocation, round up proactively so the allocation is explicitly made for the full size, allowing the compiler to correctly reason about the resulting size of the buffer through the existing __alloc_size() hint. This will allow for kernels built with CONFIG_UBSAN_BOUNDS or the coming dynamic bounds checking under CONFIG_FORTIFY_SOURCE to gain back the __alloc_size() hints that were temporarily reverted in commit 93dd04ab0b2b ("slab: remove __alloc_size attribute from __kmalloc_track_caller") Cc: "David S. Miller" Cc: Eric Dumazet Cc: Jakub Kicinski Cc: Paolo Abeni Cc: netdev@vger.kernel.org Cc: Greg Kroah-Hartman Cc: Nick Desaulniers Cc: David Rientjes Acked-by: Vlastimil Babka Link: https://patchwork.kernel.org/project/netdevbpf/patch/20221021234713.you.031-kees@kernel.org/ Signed-off-by: Kees Cook Link: https://lore.kernel.org/r/20221025223811.up.360-kees@kernel.org Signed-off-by: Paolo Abeni --- net/core/skbuff.c | 52 +++++++++++++++++++++++------------------------ 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/net/core/skbuff.c b/net/core/skbuff.c index 9b3b19816d2d..7c41025ae76a 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -506,14 +506,14 @@ struct sk_buff *__alloc_skb(unsigned int size, gfp_t gfp_mask, */ size = SKB_DATA_ALIGN(size); size += SKB_DATA_ALIGN(sizeof(struct skb_shared_info)); - data = kmalloc_reserve(size, gfp_mask, node, &pfmemalloc); + osize = kmalloc_size_roundup(size); + data = kmalloc_reserve(osize, gfp_mask, node, &pfmemalloc); if (unlikely(!data)) goto nodata; - /* kmalloc(size) might give us more room than requested. + /* kmalloc_size_roundup() might give us more room than requested. * Put skb_shared_info exactly at the end of allocated zone, * to allow max possible filling before reallocation. */ - osize = ksize(data); size = SKB_WITH_OVERHEAD(osize); prefetchw(data + size); @@ -1821,10 +1821,11 @@ EXPORT_SYMBOL(__pskb_copy_fclone); int pskb_expand_head(struct sk_buff *skb, int nhead, int ntail, gfp_t gfp_mask) { - int i, osize = skb_end_offset(skb); - int size = osize + nhead + ntail; + unsigned int osize = skb_end_offset(skb); + unsigned int size = osize + nhead + ntail; long off; u8 *data; + int i; BUG_ON(nhead < 0); @@ -1832,15 +1833,16 @@ int pskb_expand_head(struct sk_buff *skb, int nhead, int ntail, skb_zcopy_downgrade_managed(skb); - size = SKB_DATA_ALIGN(size); - if (skb_pfmemalloc(skb)) gfp_mask |= __GFP_MEMALLOC; - data = kmalloc_reserve(size + SKB_DATA_ALIGN(sizeof(struct skb_shared_info)), - gfp_mask, NUMA_NO_NODE, NULL); + + size = SKB_DATA_ALIGN(size); + size += SKB_DATA_ALIGN(sizeof(struct skb_shared_info)); + size = kmalloc_size_roundup(size); + data = kmalloc_reserve(size, gfp_mask, NUMA_NO_NODE, NULL); if (!data) goto nodata; - size = SKB_WITH_OVERHEAD(ksize(data)); + size = SKB_WITH_OVERHEAD(size); /* Copy only real data... and, alas, header. This should be * optimized for the cases when header is void. @@ -6174,21 +6176,20 @@ static int pskb_carve_inside_header(struct sk_buff *skb, const u32 off, const int headlen, gfp_t gfp_mask) { int i; - int size = skb_end_offset(skb); + unsigned int size = skb_end_offset(skb); int new_hlen = headlen - off; u8 *data; - size = SKB_DATA_ALIGN(size); - if (skb_pfmemalloc(skb)) gfp_mask |= __GFP_MEMALLOC; - data = kmalloc_reserve(size + - SKB_DATA_ALIGN(sizeof(struct skb_shared_info)), - gfp_mask, NUMA_NO_NODE, NULL); + + size = SKB_DATA_ALIGN(size); + size += SKB_DATA_ALIGN(sizeof(struct skb_shared_info)); + size = kmalloc_size_roundup(size); + data = kmalloc_reserve(size, gfp_mask, NUMA_NO_NODE, NULL); if (!data) return -ENOMEM; - - size = SKB_WITH_OVERHEAD(ksize(data)); + size = SKB_WITH_OVERHEAD(size); /* Copy real data, and all frags */ skb_copy_from_linear_data_offset(skb, off, data, new_hlen); @@ -6293,22 +6294,21 @@ static int pskb_carve_inside_nonlinear(struct sk_buff *skb, const u32 off, int pos, gfp_t gfp_mask) { int i, k = 0; - int size = skb_end_offset(skb); + unsigned int size = skb_end_offset(skb); u8 *data; const int nfrags = skb_shinfo(skb)->nr_frags; struct skb_shared_info *shinfo; - size = SKB_DATA_ALIGN(size); - if (skb_pfmemalloc(skb)) gfp_mask |= __GFP_MEMALLOC; - data = kmalloc_reserve(size + - SKB_DATA_ALIGN(sizeof(struct skb_shared_info)), - gfp_mask, NUMA_NO_NODE, NULL); + + size = SKB_DATA_ALIGN(size); + size += SKB_DATA_ALIGN(sizeof(struct skb_shared_info)); + size = kmalloc_size_roundup(size); + data = kmalloc_reserve(size, gfp_mask, NUMA_NO_NODE, NULL); if (!data) return -ENOMEM; - - size = SKB_WITH_OVERHEAD(ksize(data)); + size = SKB_WITH_OVERHEAD(size); memcpy((struct skb_shared_info *)(data + size), skb_shinfo(skb), offsetof(struct skb_shared_info, frags[0])); From 14335d54e721bdfa098d2972e5a0cc742c30c1ad Mon Sep 17 00:00:00 2001 From: Yevgeny Kliteynik Date: Mon, 25 Jul 2022 00:30:50 +0300 Subject: [PATCH 0411/2002] net/mlx5: DR, In destroy flow, free resources even if FW command failed Otherwise resources will never be freed and refcount will not be decreased. Signed-off-by: Chris Mi Signed-off-by: Yevgeny Kliteynik Reviewed-by: Alex Vesker Signed-off-by: Saeed Mahameed --- drivers/net/ethernet/mellanox/mlx5/core/steering/dr_table.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_table.c b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_table.c index 31d443dd8386..eb81759244d5 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_table.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_table.c @@ -292,7 +292,7 @@ int mlx5dr_table_destroy(struct mlx5dr_table *tbl) mlx5dr_dbg_tbl_del(tbl); ret = dr_table_destroy_sw_owned_tbl(tbl); if (ret) - return ret; + mlx5dr_err(tbl->dmn, "Failed to destoy sw owned table\n"); dr_table_uninit(tbl); From c4193a1281ac6c209e1586b8ff07297b5a9cd8ad Mon Sep 17 00:00:00 2001 From: Yevgeny Kliteynik Date: Mon, 25 Jul 2022 00:59:44 +0300 Subject: [PATCH 0412/2002] net/mlx5: DR, Fix the SMFS sync_steering for fast teardown If sync happens when the device is in fast teardown, just bail and don't do anything, because the PCI device is not there any more. Signed-off-by: Yevgeny Kliteynik Reviewed-by: Alex Vesker Signed-off-by: Saeed Mahameed --- drivers/net/ethernet/mellanox/mlx5/core/steering/dr_cmd.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_cmd.c b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_cmd.c index 16d65fe4f654..b4739eafc180 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_cmd.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_cmd.c @@ -271,6 +271,13 @@ int mlx5dr_cmd_sync_steering(struct mlx5_core_dev *mdev) { u32 in[MLX5_ST_SZ_DW(sync_steering_in)] = {}; + /* Skip SYNC in case the device is internal error state. + * Besides a device error, this also happens when we're + * in fast teardown + */ + if (mdev->state == MLX5_DEVICE_STATE_INTERNAL_ERROR) + return 0; + MLX5_SET(sync_steering_in, in, opcode, MLX5_CMD_OP_SYNC_STEERING); return mlx5_cmd_exec_in(mdev, sync_steering, in); From 5fd08f653991faf3dcbdfe35f38e1b9274bdd341 Mon Sep 17 00:00:00 2001 From: Yevgeny Kliteynik Date: Mon, 25 Jul 2022 01:11:19 +0300 Subject: [PATCH 0413/2002] net/mlx5: DR, Check device state when polling CQ Calling fast teardown as part of the normal unloading caused a problem with SW steering - SW steering still needs to clear its tables, write to ICM and poll for completions. When teardown has been done, SW steering keeps polling the CQ forever, because nobody flushes it. This patch fixes the issue by checking the device state in cases where no CQE was returned. Signed-off-by: Yevgeny Kliteynik Reviewed-by: Alex Vesker Signed-off-by: Saeed Mahameed --- .../net/ethernet/mellanox/mlx5/core/steering/dr_send.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_send.c b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_send.c index ef19a66f5233..6ad026123b16 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_send.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_send.c @@ -78,8 +78,15 @@ static int dr_cq_poll_one(struct mlx5dr_cq *dr_cq) int err; cqe64 = mlx5_cqwq_get_cqe(&dr_cq->wq); - if (!cqe64) + if (!cqe64) { + if (unlikely(dr_cq->mdev->state == + MLX5_DEVICE_STATE_INTERNAL_ERROR)) { + mlx5_core_dbg_once(dr_cq->mdev, + "Polling CQ while device is shutting down\n"); + return CQ_POLL_ERR; + } return CQ_EMPTY; + } mlx5_cqwq_pop(&dr_cq->wq); err = dr_parse_cqe(dr_cq, cqe64); @@ -833,6 +840,7 @@ static struct mlx5dr_cq *dr_create_cq(struct mlx5_core_dev *mdev, cq->mcq.vector = 0; cq->mcq.uar = uar; + cq->mdev = mdev; return cq; From d277b55f0fa805dbcabef8b4c3295a2cd6506581 Mon Sep 17 00:00:00 2001 From: Yevgeny Kliteynik Date: Thu, 26 May 2022 10:15:57 +0300 Subject: [PATCH 0414/2002] net/mlx5: DR, Remove unneeded argument from dr_icm_chunk_destroy Remove an argument that can be extracted in the function. Signed-off-by: Yevgeny Kliteynik Reviewed-by: Alex Vesker Signed-off-by: Saeed Mahameed --- .../mellanox/mlx5/core/steering/dr_icm_pool.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_icm_pool.c b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_icm_pool.c index 4ca67fa24cc6..4cdc9e9a54e1 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_icm_pool.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_icm_pool.c @@ -203,12 +203,11 @@ get_chunk_icm_type(struct mlx5dr_icm_chunk *chunk) return chunk->buddy_mem->pool->icm_type; } -static void dr_icm_chunk_destroy(struct mlx5dr_icm_chunk *chunk, - struct mlx5dr_icm_buddy_mem *buddy) +static void dr_icm_chunk_destroy(struct mlx5dr_icm_chunk *chunk) { enum mlx5dr_icm_type icm_type = get_chunk_icm_type(chunk); - buddy->used_memory -= mlx5dr_icm_pool_get_chunk_byte_size(chunk); + chunk->buddy_mem->used_memory -= mlx5dr_icm_pool_get_chunk_byte_size(chunk); list_del(&chunk->chunk_list); if (icm_type == DR_ICM_TYPE_STE) @@ -299,10 +298,10 @@ static void dr_icm_buddy_destroy(struct mlx5dr_icm_buddy_mem *buddy) struct mlx5dr_icm_chunk *chunk, *next; list_for_each_entry_safe(chunk, next, &buddy->hot_list, chunk_list) - dr_icm_chunk_destroy(chunk, buddy); + dr_icm_chunk_destroy(chunk); list_for_each_entry_safe(chunk, next, &buddy->used_list, chunk_list) - dr_icm_chunk_destroy(chunk, buddy); + dr_icm_chunk_destroy(chunk); dr_icm_pool_mr_destroy(buddy->icm_mr); @@ -376,7 +375,7 @@ static int dr_icm_pool_sync_all_buddy_pools(struct mlx5dr_icm_pool *pool) num_entries = mlx5dr_icm_pool_get_chunk_num_of_entries(chunk); mlx5dr_buddy_free_mem(buddy, chunk->seg, ilog2(num_entries)); pool->hot_memory_size -= mlx5dr_icm_pool_get_chunk_byte_size(chunk); - dr_icm_chunk_destroy(chunk, buddy); + dr_icm_chunk_destroy(chunk); } if (!buddy->used_memory && pool->icm_type == DR_ICM_TYPE_STE) From b9b81e1e93820eb47aeaf0fe7cdda535adf8e7a4 Mon Sep 17 00:00:00 2001 From: Yevgeny Kliteynik Date: Tue, 29 Mar 2022 15:23:55 +0300 Subject: [PATCH 0415/2002] net/mlx5: DR, For short chains of STEs, avoid allocating ste_arr dynamically While creating rule, ste_arr is an array that is allocated at the start of the function and freed at the end. This memory allocation can sometimes lead to "hiccups" of up to 10ms. However, the common use case is short chains of STEs. For such cases, we can use a local buffer on stack instead. Changes in v2: Use small local array for short rules, allocate dynamically for long rules Signed-off-by: Yevgeny Kliteynik Reviewed-by: Alex Vesker Signed-off-by: Saeed Mahameed --- .../mellanox/mlx5/core/steering/dr_rule.c | 40 +++++++++++++------ 1 file changed, 27 insertions(+), 13 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_rule.c b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_rule.c index ddfaf7891188..6cbc444ad791 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_rule.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_rule.c @@ -3,7 +3,8 @@ #include "dr_types.h" -#define DR_RULE_MAX_STE_CHAIN (DR_RULE_MAX_STES + DR_ACTION_MAX_STES) +#define DR_RULE_MAX_STES_OPTIMIZED 5 +#define DR_RULE_MAX_STE_CHAIN_OPTIMIZED (DR_RULE_MAX_STES_OPTIMIZED + DR_ACTION_MAX_STES) static int dr_rule_append_to_miss_list(struct mlx5dr_ste_ctx *ste_ctx, struct mlx5dr_ste *new_last_ste, @@ -1089,6 +1090,7 @@ dr_rule_create_rule_nic(struct mlx5dr_rule *rule, size_t num_actions, struct mlx5dr_action *actions[]) { + u8 hw_ste_arr_optimized[DR_RULE_MAX_STE_CHAIN_OPTIMIZED * DR_STE_SIZE] = {}; struct mlx5dr_ste_send_info *ste_info, *tmp_ste_info; struct mlx5dr_matcher *matcher = rule->matcher; struct mlx5dr_domain *dmn = matcher->tbl->dmn; @@ -1098,6 +1100,7 @@ dr_rule_create_rule_nic(struct mlx5dr_rule *rule, struct mlx5dr_ste_htbl *cur_htbl; struct mlx5dr_ste *ste = NULL; LIST_HEAD(send_ste_list); + bool hw_ste_arr_is_opt; u8 *hw_ste_arr = NULL; u32 new_hw_ste_arr_sz; int ret, i; @@ -1109,9 +1112,23 @@ dr_rule_create_rule_nic(struct mlx5dr_rule *rule, rule->flow_source)) return 0; - hw_ste_arr = kzalloc(DR_RULE_MAX_STE_CHAIN * DR_STE_SIZE, GFP_KERNEL); - if (!hw_ste_arr) - return -ENOMEM; + ret = mlx5dr_matcher_select_builders(matcher, + nic_matcher, + dr_rule_get_ipv(¶m->outer), + dr_rule_get_ipv(¶m->inner)); + if (ret) + return ret; + + hw_ste_arr_is_opt = nic_matcher->num_of_builders <= DR_RULE_MAX_STES_OPTIMIZED; + if (likely(hw_ste_arr_is_opt)) { + hw_ste_arr = hw_ste_arr_optimized; + } else { + hw_ste_arr = kzalloc((nic_matcher->num_of_builders + DR_ACTION_MAX_STES) * + DR_STE_SIZE, GFP_KERNEL); + + if (!hw_ste_arr) + return -ENOMEM; + } mlx5dr_domain_nic_lock(nic_dmn); @@ -1119,13 +1136,6 @@ dr_rule_create_rule_nic(struct mlx5dr_rule *rule, if (ret) goto free_hw_ste; - ret = mlx5dr_matcher_select_builders(matcher, - nic_matcher, - dr_rule_get_ipv(¶m->outer), - dr_rule_get_ipv(¶m->inner)); - if (ret) - goto remove_from_nic_tbl; - /* Set the tag values inside the ste array */ ret = mlx5dr_ste_build_ste_arr(matcher, nic_matcher, param, hw_ste_arr); if (ret) @@ -1187,7 +1197,8 @@ dr_rule_create_rule_nic(struct mlx5dr_rule *rule, mlx5dr_domain_nic_unlock(nic_dmn); - kfree(hw_ste_arr); + if (unlikely(!hw_ste_arr_is_opt)) + kfree(hw_ste_arr); return 0; @@ -1204,7 +1215,10 @@ remove_from_nic_tbl: free_hw_ste: mlx5dr_domain_nic_unlock(nic_dmn); - kfree(hw_ste_arr); + + if (unlikely(!hw_ste_arr_is_opt)) + kfree(hw_ste_arr); + return ret; } From 06ab4a4089d483ec29ddfc9d0503f3468b25f384 Mon Sep 17 00:00:00 2001 From: Yevgeny Kliteynik Date: Thu, 26 May 2022 01:31:27 +0300 Subject: [PATCH 0416/2002] net/mlx5: DR, Initialize chunk's ste_arrays at chunk creation Rather than cleaning the corresponding chunk's section of ste_arrays on chunk deletion, initialize these areas upon chunk creation. Chunk destruction tend to come in large batches (during pool syncing). To reduce the "hiccup" in such cases, moving ste_arrays init from chunk destruction to initialization. Signed-off-by: Yevgeny Kliteynik Reviewed-by: Alex Vesker Signed-off-by: Saeed Mahameed --- .../mellanox/mlx5/core/steering/dr_icm_pool.c | 25 +++---------------- 1 file changed, 4 insertions(+), 21 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_icm_pool.c b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_icm_pool.c index 4cdc9e9a54e1..7ca1ef073f55 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_icm_pool.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_icm_pool.c @@ -177,42 +177,25 @@ static int dr_icm_buddy_get_ste_size(struct mlx5dr_icm_buddy_mem *buddy) static void dr_icm_chunk_ste_init(struct mlx5dr_icm_chunk *chunk, int offset) { + int num_of_entries = mlx5dr_icm_pool_get_chunk_num_of_entries(chunk); struct mlx5dr_icm_buddy_mem *buddy = chunk->buddy_mem; + int ste_size = dr_icm_buddy_get_ste_size(buddy); int index = offset / DR_STE_SIZE; chunk->ste_arr = &buddy->ste_arr[index]; chunk->miss_list = &buddy->miss_list[index]; - chunk->hw_ste_arr = buddy->hw_ste_arr + - index * dr_icm_buddy_get_ste_size(buddy); -} + chunk->hw_ste_arr = buddy->hw_ste_arr + index * ste_size; -static void dr_icm_chunk_ste_cleanup(struct mlx5dr_icm_chunk *chunk) -{ - int num_of_entries = mlx5dr_icm_pool_get_chunk_num_of_entries(chunk); - struct mlx5dr_icm_buddy_mem *buddy = chunk->buddy_mem; - - memset(chunk->hw_ste_arr, 0, - num_of_entries * dr_icm_buddy_get_ste_size(buddy)); + memset(chunk->hw_ste_arr, 0, num_of_entries * ste_size); memset(chunk->ste_arr, 0, num_of_entries * sizeof(chunk->ste_arr[0])); } -static enum mlx5dr_icm_type -get_chunk_icm_type(struct mlx5dr_icm_chunk *chunk) -{ - return chunk->buddy_mem->pool->icm_type; -} - static void dr_icm_chunk_destroy(struct mlx5dr_icm_chunk *chunk) { - enum mlx5dr_icm_type icm_type = get_chunk_icm_type(chunk); - chunk->buddy_mem->used_memory -= mlx5dr_icm_pool_get_chunk_byte_size(chunk); list_del(&chunk->chunk_list); - if (icm_type == DR_ICM_TYPE_STE) - dr_icm_chunk_ste_cleanup(chunk); - kvfree(chunk); } From 183a6706a07ea95cdd57a7703acec5f39fe3edde Mon Sep 17 00:00:00 2001 From: Yevgeny Kliteynik Date: Tue, 28 Jun 2022 14:32:37 +0300 Subject: [PATCH 0417/2002] net/mlx5: DR, Handle domain memory resources init/uninit separately Handle creation/destruction of all the domain's memory pools and other memory-related fields in a separate init/uninit functions. This simplifies error flow and allows cleaner addition of new pools. Signed-off-by: Yevgeny Kliteynik Reviewed-by: Alex Vesker Signed-off-by: Saeed Mahameed --- .../mellanox/mlx5/core/steering/dr_domain.c | 55 +++++++++++++------ 1 file changed, 37 insertions(+), 18 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_domain.c b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_domain.c index fc6ae49b5ecc..543d655ae3b6 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_domain.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_domain.c @@ -56,6 +56,36 @@ int mlx5dr_domain_get_recalc_cs_ft_addr(struct mlx5dr_domain *dmn, return 0; } +static int dr_domain_init_mem_resources(struct mlx5dr_domain *dmn) +{ + int ret; + + dmn->ste_icm_pool = mlx5dr_icm_pool_create(dmn, DR_ICM_TYPE_STE); + if (!dmn->ste_icm_pool) { + mlx5dr_err(dmn, "Couldn't get icm memory\n"); + return -ENOMEM; + } + + dmn->action_icm_pool = mlx5dr_icm_pool_create(dmn, DR_ICM_TYPE_MODIFY_ACTION); + if (!dmn->action_icm_pool) { + mlx5dr_err(dmn, "Couldn't get action icm memory\n"); + ret = -ENOMEM; + goto free_ste_icm_pool; + } + + return 0; + +free_ste_icm_pool: + mlx5dr_icm_pool_destroy(dmn->ste_icm_pool); + return ret; +} + +static void dr_domain_uninit_mem_resources(struct mlx5dr_domain *dmn) +{ + mlx5dr_icm_pool_destroy(dmn->action_icm_pool); + mlx5dr_icm_pool_destroy(dmn->ste_icm_pool); +} + static int dr_domain_init_resources(struct mlx5dr_domain *dmn) { int ret; @@ -79,32 +109,22 @@ static int dr_domain_init_resources(struct mlx5dr_domain *dmn) goto clean_pd; } - dmn->ste_icm_pool = mlx5dr_icm_pool_create(dmn, DR_ICM_TYPE_STE); - if (!dmn->ste_icm_pool) { - mlx5dr_err(dmn, "Couldn't get icm memory\n"); - ret = -ENOMEM; + ret = dr_domain_init_mem_resources(dmn); + if (ret) { + mlx5dr_err(dmn, "Couldn't create domain memory resources\n"); goto clean_uar; } - dmn->action_icm_pool = mlx5dr_icm_pool_create(dmn, DR_ICM_TYPE_MODIFY_ACTION); - if (!dmn->action_icm_pool) { - mlx5dr_err(dmn, "Couldn't get action icm memory\n"); - ret = -ENOMEM; - goto free_ste_icm_pool; - } - ret = mlx5dr_send_ring_alloc(dmn); if (ret) { mlx5dr_err(dmn, "Couldn't create send-ring\n"); - goto free_action_icm_pool; + goto clean_mem_resources; } return 0; -free_action_icm_pool: - mlx5dr_icm_pool_destroy(dmn->action_icm_pool); -free_ste_icm_pool: - mlx5dr_icm_pool_destroy(dmn->ste_icm_pool); +clean_mem_resources: + dr_domain_uninit_mem_resources(dmn); clean_uar: mlx5_put_uars_page(dmn->mdev, dmn->uar); clean_pd: @@ -116,8 +136,7 @@ clean_pd: static void dr_domain_uninit_resources(struct mlx5dr_domain *dmn) { mlx5dr_send_ring_free(dmn, dmn->send_ring); - mlx5dr_icm_pool_destroy(dmn->action_icm_pool); - mlx5dr_icm_pool_destroy(dmn->ste_icm_pool); + dr_domain_uninit_mem_resources(dmn); mlx5_put_uars_page(dmn->mdev, dmn->uar); mlx5_core_dealloc_pd(dmn->mdev, dmn->pdn); } From 1bea2dc7f4ff30bd785e1949d237cd2ce1e87c4f Mon Sep 17 00:00:00 2001 From: Yevgeny Kliteynik Date: Wed, 29 Jun 2022 13:19:36 +0300 Subject: [PATCH 0418/2002] net/mlx5: DR, In rehash write the line in the entry immediately Don't wait for the whole table to be ready - write each row immediately. This way we save allocations of the ste_send_info structure and improve performance. Signed-off-by: Erez Shitrit Signed-off-by: Yevgeny Kliteynik Reviewed-by: Alex Vesker Signed-off-by: Saeed Mahameed --- .../net/ethernet/mellanox/mlx5/core/steering/dr_rule.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_rule.c b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_rule.c index 6cbc444ad791..22878dcd7c8b 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_rule.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_rule.c @@ -358,6 +358,15 @@ static int dr_rule_rehash_copy_htbl(struct mlx5dr_matcher *matcher, update_list); if (err) goto clean_copy; + + /* In order to decrease the number of allocated ste_send_info + * structs, send the current table row now. + */ + err = dr_rule_send_update_list(update_list, matcher->tbl->dmn, false); + if (err) { + mlx5dr_dbg(matcher->tbl->dmn, "Failed updating table to HW\n"); + goto clean_copy; + } } clean_copy: From 17b56073a0668a153f0e1051dc1bf60960f74810 Mon Sep 17 00:00:00 2001 From: Yevgeny Kliteynik Date: Tue, 29 Mar 2022 15:21:02 +0300 Subject: [PATCH 0419/2002] net/mlx5: DR, Manage STE send info objects in pool Instead of allocating/freeing send info objects dynamically, manage them in pool. The number of send info objects doesn't depend on rules, so after pre-populating the pool with an initial batch of send info objects, the pool is not expected to grow. This way we save alloc/free during writing STEs to ICM, which can sometimes take up to 40msec. Signed-off-by: Yevgeny Kliteynik Reviewed-by: Alex Vesker Signed-off-by: Saeed Mahameed --- .../mellanox/mlx5/core/steering/dr_domain.c | 9 ++ .../mellanox/mlx5/core/steering/dr_rule.c | 43 +++--- .../mellanox/mlx5/core/steering/dr_send.c | 131 ++++++++++++++++++ .../mellanox/mlx5/core/steering/dr_types.h | 9 ++ 4 files changed, 173 insertions(+), 19 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_domain.c b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_domain.c index 543d655ae3b6..3dc784b22741 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_domain.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_domain.c @@ -73,8 +73,16 @@ static int dr_domain_init_mem_resources(struct mlx5dr_domain *dmn) goto free_ste_icm_pool; } + ret = mlx5dr_send_info_pool_create(dmn); + if (ret) { + mlx5dr_err(dmn, "Couldn't create send info pool\n"); + goto free_action_icm_pool; + } + return 0; +free_action_icm_pool: + mlx5dr_icm_pool_destroy(dmn->action_icm_pool); free_ste_icm_pool: mlx5dr_icm_pool_destroy(dmn->ste_icm_pool); return ret; @@ -82,6 +90,7 @@ free_ste_icm_pool: static void dr_domain_uninit_mem_resources(struct mlx5dr_domain *dmn) { + mlx5dr_send_info_pool_destroy(dmn); mlx5dr_icm_pool_destroy(dmn->action_icm_pool); mlx5dr_icm_pool_destroy(dmn->ste_icm_pool); } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_rule.c b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_rule.c index 22878dcd7c8b..45542e5a5b3a 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_rule.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_rule.c @@ -6,11 +6,13 @@ #define DR_RULE_MAX_STES_OPTIMIZED 5 #define DR_RULE_MAX_STE_CHAIN_OPTIMIZED (DR_RULE_MAX_STES_OPTIMIZED + DR_ACTION_MAX_STES) -static int dr_rule_append_to_miss_list(struct mlx5dr_ste_ctx *ste_ctx, +static int dr_rule_append_to_miss_list(struct mlx5dr_domain *dmn, + enum mlx5dr_domain_nic_type nic_type, struct mlx5dr_ste *new_last_ste, struct list_head *miss_list, struct list_head *send_list) { + struct mlx5dr_ste_ctx *ste_ctx = dmn->ste_ctx; struct mlx5dr_ste_send_info *ste_info_last; struct mlx5dr_ste *last_ste; @@ -18,7 +20,7 @@ static int dr_rule_append_to_miss_list(struct mlx5dr_ste_ctx *ste_ctx, last_ste = list_last_entry(miss_list, struct mlx5dr_ste, miss_list_node); WARN_ON(!last_ste); - ste_info_last = kzalloc(sizeof(*ste_info_last), GFP_KERNEL); + ste_info_last = mlx5dr_send_info_alloc(dmn, nic_type); if (!ste_info_last) return -ENOMEM; @@ -121,7 +123,7 @@ dr_rule_handle_one_ste_in_update_list(struct mlx5dr_ste_send_info *ste_info, goto out; out: - kfree(ste_info); + mlx5dr_send_info_free(ste_info); return ret; } @@ -192,8 +194,8 @@ dr_rule_rehash_handle_collision(struct mlx5dr_matcher *matcher, new_ste->htbl->chunk->miss_list = mlx5dr_ste_get_miss_list(col_ste); /* Update the previous from the list */ - ret = dr_rule_append_to_miss_list(dmn->ste_ctx, new_ste, - mlx5dr_ste_get_miss_list(col_ste), + ret = dr_rule_append_to_miss_list(dmn, nic_matcher->nic_tbl->nic_dmn->type, + new_ste, mlx5dr_ste_get_miss_list(col_ste), update_list); if (ret) { mlx5dr_dbg(dmn, "Failed update dup entry\n"); @@ -279,7 +281,8 @@ dr_rule_rehash_copy_ste(struct mlx5dr_matcher *matcher, new_htbl->ctrl.num_of_valid_entries++; if (use_update_list) { - ste_info = kzalloc(sizeof(*ste_info), GFP_KERNEL); + ste_info = mlx5dr_send_info_alloc(dmn, + nic_matcher->nic_tbl->nic_dmn->type); if (!ste_info) goto err_exit; @@ -397,7 +400,8 @@ dr_rule_rehash_htbl(struct mlx5dr_rule *rule, nic_matcher = nic_rule->nic_matcher; nic_dmn = nic_matcher->nic_tbl->nic_dmn; - ste_info = kzalloc(sizeof(*ste_info), GFP_KERNEL); + ste_info = mlx5dr_send_info_alloc(dmn, + nic_matcher->nic_tbl->nic_dmn->type); if (!ste_info) return NULL; @@ -483,13 +487,13 @@ free_ste_list: list_for_each_entry_safe(del_ste_info, tmp_ste_info, &rehash_table_send_list, send_list) { list_del(&del_ste_info->send_list); - kfree(del_ste_info); + mlx5dr_send_info_free(del_ste_info); } free_new_htbl: mlx5dr_ste_htbl_free(new_htbl); free_ste_info: - kfree(ste_info); + mlx5dr_send_info_free(ste_info); mlx5dr_info(dmn, "Failed creating rehash table\n"); return NULL; } @@ -522,11 +526,11 @@ dr_rule_handle_collision(struct mlx5dr_matcher *matcher, struct list_head *send_list) { struct mlx5dr_domain *dmn = matcher->tbl->dmn; - struct mlx5dr_ste_ctx *ste_ctx = dmn->ste_ctx; struct mlx5dr_ste_send_info *ste_info; struct mlx5dr_ste *new_ste; - ste_info = kzalloc(sizeof(*ste_info), GFP_KERNEL); + ste_info = mlx5dr_send_info_alloc(dmn, + nic_matcher->nic_tbl->nic_dmn->type); if (!ste_info) return NULL; @@ -534,8 +538,8 @@ dr_rule_handle_collision(struct mlx5dr_matcher *matcher, if (!new_ste) goto free_send_info; - if (dr_rule_append_to_miss_list(ste_ctx, new_ste, - miss_list, send_list)) { + if (dr_rule_append_to_miss_list(dmn, nic_matcher->nic_tbl->nic_dmn->type, + new_ste, miss_list, send_list)) { mlx5dr_dbg(dmn, "Failed to update prev miss_list\n"); goto err_exit; } @@ -551,7 +555,7 @@ dr_rule_handle_collision(struct mlx5dr_matcher *matcher, err_exit: mlx5dr_ste_free(new_ste, matcher, nic_matcher); free_send_info: - kfree(ste_info); + mlx5dr_send_info_free(ste_info); return NULL; } @@ -731,8 +735,8 @@ static int dr_rule_handle_action_stes(struct mlx5dr_rule *rule, list_add_tail(&action_ste->miss_list_node, mlx5dr_ste_get_miss_list(action_ste)); - ste_info_arr[k] = kzalloc(sizeof(*ste_info_arr[k]), - GFP_KERNEL); + ste_info_arr[k] = mlx5dr_send_info_alloc(dmn, + nic_matcher->nic_tbl->nic_dmn->type); if (!ste_info_arr[k]) goto err_exit; @@ -782,7 +786,8 @@ static int dr_rule_handle_empty_entry(struct mlx5dr_matcher *matcher, ste->ste_chain_location = ste_location; - ste_info = kzalloc(sizeof(*ste_info), GFP_KERNEL); + ste_info = mlx5dr_send_info_alloc(dmn, + nic_matcher->nic_tbl->nic_dmn->type); if (!ste_info) goto clean_ste_setting; @@ -803,7 +808,7 @@ static int dr_rule_handle_empty_entry(struct mlx5dr_matcher *matcher, return 0; clean_ste_info: - kfree(ste_info); + mlx5dr_send_info_free(ste_info); clean_ste_setting: list_del_init(&ste->miss_list_node); mlx5dr_htbl_put(cur_htbl); @@ -1216,7 +1221,7 @@ free_rule: /* Clean all ste_info's */ list_for_each_entry_safe(ste_info, tmp_ste_info, &send_ste_list, send_list) { list_del(&ste_info->send_list); - kfree(ste_info); + mlx5dr_send_info_free(ste_info); } remove_from_nic_tbl: diff --git a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_send.c b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_send.c index 6ad026123b16..a4476cb4c3b3 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_send.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_send.c @@ -7,6 +7,7 @@ #define QUEUE_SIZE 128 #define SIGNAL_PER_DIV_QUEUE 16 #define TH_NUMS_TO_DRAIN 2 +#define DR_SEND_INFO_POOL_SIZE 1000 enum { CQ_OK = 0, CQ_EMPTY = -1, CQ_POLL_ERR = -2 }; @@ -49,6 +50,136 @@ struct dr_qp_init_attr { u8 isolate_vl_tc:1; }; +struct mlx5dr_send_info_pool_obj { + struct mlx5dr_ste_send_info ste_send_info; + struct mlx5dr_send_info_pool *pool; + struct list_head list_node; +}; + +struct mlx5dr_send_info_pool { + struct list_head free_list; +}; + +static int dr_send_info_pool_fill(struct mlx5dr_send_info_pool *pool) +{ + struct mlx5dr_send_info_pool_obj *pool_obj, *tmp_pool_obj; + int i; + + for (i = 0; i < DR_SEND_INFO_POOL_SIZE; i++) { + pool_obj = kzalloc(sizeof(*pool_obj), GFP_KERNEL); + if (!pool_obj) + goto clean_pool; + + pool_obj->pool = pool; + list_add_tail(&pool_obj->list_node, &pool->free_list); + } + + return 0; + +clean_pool: + list_for_each_entry_safe(pool_obj, tmp_pool_obj, &pool->free_list, list_node) { + list_del(&pool_obj->list_node); + kfree(pool_obj); + } + + return -ENOMEM; +} + +static void dr_send_info_pool_destroy(struct mlx5dr_send_info_pool *pool) +{ + struct mlx5dr_send_info_pool_obj *pool_obj, *tmp_pool_obj; + + list_for_each_entry_safe(pool_obj, tmp_pool_obj, &pool->free_list, list_node) { + list_del(&pool_obj->list_node); + kfree(pool_obj); + } + + kfree(pool); +} + +void mlx5dr_send_info_pool_destroy(struct mlx5dr_domain *dmn) +{ + dr_send_info_pool_destroy(dmn->send_info_pool_tx); + dr_send_info_pool_destroy(dmn->send_info_pool_rx); +} + +static struct mlx5dr_send_info_pool *dr_send_info_pool_create(void) +{ + struct mlx5dr_send_info_pool *pool; + int ret; + + pool = kzalloc(sizeof(*pool), GFP_KERNEL); + if (!pool) + return NULL; + + INIT_LIST_HEAD(&pool->free_list); + + ret = dr_send_info_pool_fill(pool); + if (ret) { + kfree(pool); + return NULL; + } + + return pool; +} + +int mlx5dr_send_info_pool_create(struct mlx5dr_domain *dmn) +{ + dmn->send_info_pool_rx = dr_send_info_pool_create(); + if (!dmn->send_info_pool_rx) + return -ENOMEM; + + dmn->send_info_pool_tx = dr_send_info_pool_create(); + if (!dmn->send_info_pool_tx) { + dr_send_info_pool_destroy(dmn->send_info_pool_rx); + return -ENOMEM; + } + + return 0; +} + +struct mlx5dr_ste_send_info +*mlx5dr_send_info_alloc(struct mlx5dr_domain *dmn, + enum mlx5dr_domain_nic_type nic_type) +{ + struct mlx5dr_send_info_pool_obj *pool_obj; + struct mlx5dr_send_info_pool *pool; + int ret; + + pool = nic_type == DR_DOMAIN_NIC_TYPE_RX ? dmn->send_info_pool_rx : + dmn->send_info_pool_tx; + + if (unlikely(list_empty(&pool->free_list))) { + ret = dr_send_info_pool_fill(pool); + if (ret) + return NULL; + } + + pool_obj = list_first_entry_or_null(&pool->free_list, + struct mlx5dr_send_info_pool_obj, + list_node); + + if (likely(pool_obj)) { + list_del_init(&pool_obj->list_node); + } else { + WARN_ONCE(!pool_obj, "Failed getting ste send info obj from pool"); + return NULL; + } + + return &pool_obj->ste_send_info; +} + +void mlx5dr_send_info_free(struct mlx5dr_ste_send_info *ste_send_info) +{ + struct mlx5dr_send_info_pool_obj *pool_obj; + + pool_obj = container_of(ste_send_info, + struct mlx5dr_send_info_pool_obj, + ste_send_info); + + list_add(&pool_obj->list_node, &pool_obj->pool->free_list); +} + static int dr_parse_cqe(struct mlx5dr_cq *dr_cq, struct mlx5_cqe64 *cqe64) { unsigned int idx; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_types.h b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_types.h index 1777a1e508e7..244685453a27 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_types.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_types.h @@ -146,6 +146,7 @@ struct mlx5dr_cmd_caps; struct mlx5dr_rule_rx_tx; struct mlx5dr_matcher_rx_tx; struct mlx5dr_ste_ctx; +struct mlx5dr_send_info_pool; struct mlx5dr_ste { /* refcount: indicates the num of rules that using this ste */ @@ -912,6 +913,8 @@ struct mlx5dr_domain { refcount_t refcount; struct mlx5dr_icm_pool *ste_icm_pool; struct mlx5dr_icm_pool *action_icm_pool; + struct mlx5dr_send_info_pool *send_info_pool_rx; + struct mlx5dr_send_info_pool *send_info_pool_tx; struct mlx5dr_send_ring *send_ring; struct mlx5dr_domain_info info; struct xarray csum_fts_xa; @@ -1404,6 +1407,12 @@ int mlx5dr_send_postsend_formatted_htbl(struct mlx5dr_domain *dmn, int mlx5dr_send_postsend_action(struct mlx5dr_domain *dmn, struct mlx5dr_action *action); +int mlx5dr_send_info_pool_create(struct mlx5dr_domain *dmn); +void mlx5dr_send_info_pool_destroy(struct mlx5dr_domain *dmn); +struct mlx5dr_ste_send_info *mlx5dr_send_info_alloc(struct mlx5dr_domain *dmn, + enum mlx5dr_domain_nic_type nic_type); +void mlx5dr_send_info_free(struct mlx5dr_ste_send_info *ste_send_info); + struct mlx5dr_cmd_ft_info { u32 id; u16 vport; From fd785e5213f012ec086fd93319b9e154caae6ddc Mon Sep 17 00:00:00 2001 From: Yevgeny Kliteynik Date: Thu, 26 May 2022 01:44:18 +0300 Subject: [PATCH 0420/2002] net/mlx5: DR, Allocate icm_chunks from their own slab allocator SW steering allocates/frees lots of icm_chunk structs. To make this more efficiently, create a separate kmem_cache and allocate these chunks from this allocator. By doing this we observe that the alloc/free "hiccups" frequency has become much lower, which allows for a more steady rule insersion rate. Signed-off-by: Yevgeny Kliteynik Reviewed-by: Alex Vesker Signed-off-by: Saeed Mahameed --- .../mellanox/mlx5/core/steering/dr_domain.c | 15 ++++++++++++++- .../mellanox/mlx5/core/steering/dr_icm_pool.c | 11 +++++++++-- .../mellanox/mlx5/core/steering/dr_types.h | 1 + 3 files changed, 24 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_domain.c b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_domain.c index 3dc784b22741..3fbcb2883a26 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_domain.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_domain.c @@ -60,10 +60,19 @@ static int dr_domain_init_mem_resources(struct mlx5dr_domain *dmn) { int ret; + dmn->chunks_kmem_cache = kmem_cache_create("mlx5_dr_chunks", + sizeof(struct mlx5dr_icm_chunk), 0, + SLAB_HWCACHE_ALIGN, NULL); + if (!dmn->chunks_kmem_cache) { + mlx5dr_err(dmn, "Couldn't create chunks kmem_cache\n"); + return -ENOMEM; + } + dmn->ste_icm_pool = mlx5dr_icm_pool_create(dmn, DR_ICM_TYPE_STE); if (!dmn->ste_icm_pool) { mlx5dr_err(dmn, "Couldn't get icm memory\n"); - return -ENOMEM; + ret = -ENOMEM; + goto free_chunks_kmem_cache; } dmn->action_icm_pool = mlx5dr_icm_pool_create(dmn, DR_ICM_TYPE_MODIFY_ACTION); @@ -85,6 +94,9 @@ free_action_icm_pool: mlx5dr_icm_pool_destroy(dmn->action_icm_pool); free_ste_icm_pool: mlx5dr_icm_pool_destroy(dmn->ste_icm_pool); +free_chunks_kmem_cache: + kmem_cache_destroy(dmn->chunks_kmem_cache); + return ret; } @@ -93,6 +105,7 @@ static void dr_domain_uninit_mem_resources(struct mlx5dr_domain *dmn) mlx5dr_send_info_pool_destroy(dmn); mlx5dr_icm_pool_destroy(dmn->action_icm_pool); mlx5dr_icm_pool_destroy(dmn->ste_icm_pool); + kmem_cache_destroy(dmn->chunks_kmem_cache); } static int dr_domain_init_resources(struct mlx5dr_domain *dmn) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_icm_pool.c b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_icm_pool.c index 7ca1ef073f55..be02546a7de0 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_icm_pool.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_icm_pool.c @@ -9,6 +9,8 @@ struct mlx5dr_icm_pool { enum mlx5dr_icm_type icm_type; enum mlx5dr_icm_chunk_size max_log_chunk_sz; struct mlx5dr_domain *dmn; + struct kmem_cache *chunks_kmem_cache; + /* memory management */ struct mutex mutex; /* protect the ICM pool and ICM buddy */ struct list_head buddy_mem_list; @@ -193,10 +195,13 @@ static void dr_icm_chunk_ste_init(struct mlx5dr_icm_chunk *chunk, int offset) static void dr_icm_chunk_destroy(struct mlx5dr_icm_chunk *chunk) { + struct kmem_cache *chunks_cache = + chunk->buddy_mem->pool->chunks_kmem_cache; + chunk->buddy_mem->used_memory -= mlx5dr_icm_pool_get_chunk_byte_size(chunk); list_del(&chunk->chunk_list); - kvfree(chunk); + kmem_cache_free(chunks_cache, chunk); } static int dr_icm_buddy_init_ste_cache(struct mlx5dr_icm_buddy_mem *buddy) @@ -302,10 +307,11 @@ dr_icm_chunk_create(struct mlx5dr_icm_pool *pool, struct mlx5dr_icm_buddy_mem *buddy_mem_pool, unsigned int seg) { + struct kmem_cache *chunks_cache = buddy_mem_pool->pool->chunks_kmem_cache; struct mlx5dr_icm_chunk *chunk; int offset; - chunk = kvzalloc(sizeof(*chunk), GFP_KERNEL); + chunk = kmem_cache_alloc(chunks_cache, GFP_KERNEL); if (!chunk) return NULL; @@ -482,6 +488,7 @@ struct mlx5dr_icm_pool *mlx5dr_icm_pool_create(struct mlx5dr_domain *dmn, pool->dmn = dmn; pool->icm_type = icm_type; pool->max_log_chunk_sz = max_log_chunk_sz; + pool->chunks_kmem_cache = dmn->chunks_kmem_cache; INIT_LIST_HEAD(&pool->buddy_mem_list); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_types.h b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_types.h index 244685453a27..4f38f0f5b352 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_types.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_types.h @@ -915,6 +915,7 @@ struct mlx5dr_domain { struct mlx5dr_icm_pool *action_icm_pool; struct mlx5dr_send_info_pool *send_info_pool_rx; struct mlx5dr_send_info_pool *send_info_pool_tx; + struct kmem_cache *chunks_kmem_cache; struct mlx5dr_send_ring *send_ring; struct mlx5dr_domain_info info; struct xarray csum_fts_xa; From fb628b71fb2a93e5346cbd8c80d254683c08e490 Mon Sep 17 00:00:00 2001 From: Yevgeny Kliteynik Date: Thu, 26 May 2022 02:32:49 +0300 Subject: [PATCH 0421/2002] net/mlx5: DR, Allocate htbl from its own slab allocator SW steering allocates/frees lots of htbl structs. Create a separate kmem_cache and allocate htbls from this allocator. Signed-off-by: Yevgeny Kliteynik Reviewed-by: Alex Vesker Signed-off-by: Saeed Mahameed --- .../mellanox/mlx5/core/steering/dr_domain.c | 14 +++++++++++++- .../mellanox/mlx5/core/steering/dr_icm_pool.c | 10 ++++++++++ .../ethernet/mellanox/mlx5/core/steering/dr_ste.c | 12 +++++++++--- .../mellanox/mlx5/core/steering/dr_types.h | 4 ++++ 4 files changed, 36 insertions(+), 4 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_domain.c b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_domain.c index 3fbcb2883a26..9a9836218c8e 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_domain.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_domain.c @@ -68,11 +68,20 @@ static int dr_domain_init_mem_resources(struct mlx5dr_domain *dmn) return -ENOMEM; } + dmn->htbls_kmem_cache = kmem_cache_create("mlx5_dr_htbls", + sizeof(struct mlx5dr_ste_htbl), 0, + SLAB_HWCACHE_ALIGN, NULL); + if (!dmn->htbls_kmem_cache) { + mlx5dr_err(dmn, "Couldn't create hash tables kmem_cache\n"); + ret = -ENOMEM; + goto free_chunks_kmem_cache; + } + dmn->ste_icm_pool = mlx5dr_icm_pool_create(dmn, DR_ICM_TYPE_STE); if (!dmn->ste_icm_pool) { mlx5dr_err(dmn, "Couldn't get icm memory\n"); ret = -ENOMEM; - goto free_chunks_kmem_cache; + goto free_htbls_kmem_cache; } dmn->action_icm_pool = mlx5dr_icm_pool_create(dmn, DR_ICM_TYPE_MODIFY_ACTION); @@ -94,6 +103,8 @@ free_action_icm_pool: mlx5dr_icm_pool_destroy(dmn->action_icm_pool); free_ste_icm_pool: mlx5dr_icm_pool_destroy(dmn->ste_icm_pool); +free_htbls_kmem_cache: + kmem_cache_destroy(dmn->htbls_kmem_cache); free_chunks_kmem_cache: kmem_cache_destroy(dmn->chunks_kmem_cache); @@ -105,6 +116,7 @@ static void dr_domain_uninit_mem_resources(struct mlx5dr_domain *dmn) mlx5dr_send_info_pool_destroy(dmn); mlx5dr_icm_pool_destroy(dmn->action_icm_pool); mlx5dr_icm_pool_destroy(dmn->ste_icm_pool); + kmem_cache_destroy(dmn->htbls_kmem_cache); kmem_cache_destroy(dmn->chunks_kmem_cache); } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_icm_pool.c b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_icm_pool.c index be02546a7de0..ca91a0211a5c 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_icm_pool.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_icm_pool.c @@ -470,6 +470,16 @@ void mlx5dr_icm_free_chunk(struct mlx5dr_icm_chunk *chunk) mutex_unlock(&pool->mutex); } +struct mlx5dr_ste_htbl *mlx5dr_icm_pool_alloc_htbl(struct mlx5dr_icm_pool *pool) +{ + return kmem_cache_alloc(pool->dmn->htbls_kmem_cache, GFP_KERNEL); +} + +void mlx5dr_icm_pool_free_htbl(struct mlx5dr_icm_pool *pool, struct mlx5dr_ste_htbl *htbl) +{ + kmem_cache_free(pool->dmn->htbls_kmem_cache, htbl); +} + struct mlx5dr_icm_pool *mlx5dr_icm_pool_create(struct mlx5dr_domain *dmn, enum mlx5dr_icm_type icm_type) { diff --git a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_ste.c b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_ste.c index 09ebd3088857..9e19a8dc9022 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_ste.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_ste.c @@ -491,7 +491,7 @@ struct mlx5dr_ste_htbl *mlx5dr_ste_htbl_alloc(struct mlx5dr_icm_pool *pool, u32 num_entries; int i; - htbl = kzalloc(sizeof(*htbl), GFP_KERNEL); + htbl = mlx5dr_icm_pool_alloc_htbl(pool); if (!htbl) return NULL; @@ -503,6 +503,9 @@ struct mlx5dr_ste_htbl *mlx5dr_ste_htbl_alloc(struct mlx5dr_icm_pool *pool, htbl->lu_type = lu_type; htbl->byte_mask = byte_mask; htbl->refcount = 0; + htbl->pointing_ste = NULL; + htbl->ctrl.num_of_valid_entries = 0; + htbl->ctrl.num_of_collisions = 0; num_entries = mlx5dr_icm_pool_get_chunk_num_of_entries(chunk); for (i = 0; i < num_entries; i++) { @@ -517,17 +520,20 @@ struct mlx5dr_ste_htbl *mlx5dr_ste_htbl_alloc(struct mlx5dr_icm_pool *pool, return htbl; out_free_htbl: - kfree(htbl); + mlx5dr_icm_pool_free_htbl(pool, htbl); return NULL; } int mlx5dr_ste_htbl_free(struct mlx5dr_ste_htbl *htbl) { + struct mlx5dr_icm_pool *pool = htbl->chunk->buddy_mem->pool; + if (htbl->refcount) return -EBUSY; mlx5dr_icm_free_chunk(htbl->chunk); - kfree(htbl); + mlx5dr_icm_pool_free_htbl(pool, htbl); + return 0; } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_types.h b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_types.h index 4f38f0f5b352..b645c0ab9a72 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_types.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_types.h @@ -916,6 +916,7 @@ struct mlx5dr_domain { struct mlx5dr_send_info_pool *send_info_pool_rx; struct mlx5dr_send_info_pool *send_info_pool_tx; struct kmem_cache *chunks_kmem_cache; + struct kmem_cache *htbls_kmem_cache; struct mlx5dr_send_ring *send_ring; struct mlx5dr_domain_info info; struct xarray csum_fts_xa; @@ -1162,6 +1163,9 @@ u32 mlx5dr_icm_pool_get_chunk_num_of_entries(struct mlx5dr_icm_chunk *chunk); u32 mlx5dr_icm_pool_get_chunk_byte_size(struct mlx5dr_icm_chunk *chunk); u8 *mlx5dr_ste_get_hw_ste(struct mlx5dr_ste *ste); +struct mlx5dr_ste_htbl *mlx5dr_icm_pool_alloc_htbl(struct mlx5dr_icm_pool *pool); +void mlx5dr_icm_pool_free_htbl(struct mlx5dr_icm_pool *pool, struct mlx5dr_ste_htbl *htbl); + static inline int mlx5dr_icm_pool_dm_type_to_entry_size(enum mlx5dr_icm_type icm_type) { From 133ea373a04399a9443b976a3c82c17afa81591d Mon Sep 17 00:00:00 2001 From: Yevgeny Kliteynik Date: Thu, 26 May 2022 10:27:12 +0300 Subject: [PATCH 0422/2002] net/mlx5: DR, Lower sync threshold for ICM hot memory Instead of hiding the math in the code, define a value that sets the fraction of allowed hot memory of ICM pool. Set the threshold for sync of ICM hot chunks to 1/4 of the pool instead of 1/2 of the pool. Although we will have more syncs, each sync will be shorter and will help with insertion rate stability. Signed-off-by: Yevgeny Kliteynik Reviewed-by: Alex Vesker Signed-off-by: Saeed Mahameed --- .../net/ethernet/mellanox/mlx5/core/steering/dr_icm_pool.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_icm_pool.c b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_icm_pool.c index ca91a0211a5c..286b7ce6bc0b 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_icm_pool.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_icm_pool.c @@ -4,6 +4,7 @@ #include "dr_types.h" #define DR_ICM_MODIFY_HDR_ALIGN_BASE 64 +#define DR_ICM_POOL_HOT_MEMORY_FRACTION 4 struct mlx5dr_icm_pool { enum mlx5dr_icm_type icm_type; @@ -337,10 +338,11 @@ static bool dr_icm_pool_is_sync_required(struct mlx5dr_icm_pool *pool) { int allow_hot_size; - /* sync when hot memory reaches half of the pool size */ + /* sync when hot memory reaches a certain fraction of the pool size */ allow_hot_size = mlx5dr_icm_pool_chunk_size_to_byte(pool->max_log_chunk_sz, - pool->icm_type) / 2; + pool->icm_type) / + DR_ICM_POOL_HOT_MEMORY_FRACTION; return pool->hot_memory_size > allow_hot_size; } From 4519fc45beebcb05a052ea631d22c85e3ab5665d Mon Sep 17 00:00:00 2001 From: Yevgeny Kliteynik Date: Thu, 26 May 2022 01:52:43 +0300 Subject: [PATCH 0423/2002] net/mlx5: DR, Keep track of hot ICM chunks in an array instead of list When ICM chunk is freed, it might still be accessed by HW until we do sync with HW. This sync is expensive operation, so we don't do it often. Instead, when the chunk is freed, it is moved to the buddy's "hot memory" list. Once sync is done, we traverse the hot list and finally free all the chunks. It appears that traversing a long list takes unusually long time due to cache misses on many entries, which causes a big "hiccup" during rule insertion. This patch deals with this issue the following way: - Move hot chunks list from buddy to pool, so that the pool will keep track of all its hot memory. - Replace the list with pre-allocated array on the memory pool struct, and store only the information that is needed to later free this chunk in its buddy allocator. This cost additional memory for the array that is dynamically allocated, but it allows not to save long list of hot chunks, so at peak times it actually saves memory due to the fact that each array entry is much smaller than the chunk struct. This way an overhead of traversing the long list is virtually removed: the loop of freeing hot chunks takes ~27 msec instead of ~70 msec, where most of it are the actual freeing activities. Signed-off-by: Yevgeny Kliteynik Reviewed-by: Alex Vesker Signed-off-by: Saeed Mahameed --- .../mellanox/mlx5/core/steering/dr_buddy.c | 1 - .../mellanox/mlx5/core/steering/dr_icm_pool.c | 85 +++++++++++++++---- .../mellanox/mlx5/core/steering/dr_types.h | 1 + .../mellanox/mlx5/core/steering/mlx5dr.h | 6 -- 4 files changed, 71 insertions(+), 22 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_buddy.c b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_buddy.c index 7df11a019df9..7e30dc64c10c 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_buddy.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_buddy.c @@ -16,7 +16,6 @@ int mlx5dr_buddy_init(struct mlx5dr_icm_buddy_mem *buddy, INIT_LIST_HEAD(&buddy->list_node); INIT_LIST_HEAD(&buddy->used_list); - INIT_LIST_HEAD(&buddy->hot_list); buddy->bitmap = kcalloc(buddy->max_order + 1, sizeof(*buddy->bitmap), diff --git a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_icm_pool.c b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_icm_pool.c index 286b7ce6bc0b..c31608a3f11c 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_icm_pool.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_icm_pool.c @@ -6,6 +6,12 @@ #define DR_ICM_MODIFY_HDR_ALIGN_BASE 64 #define DR_ICM_POOL_HOT_MEMORY_FRACTION 4 +struct mlx5dr_icm_hot_chunk { + struct mlx5dr_icm_buddy_mem *buddy_mem; + unsigned int seg; + enum mlx5dr_icm_chunk_size size; +}; + struct mlx5dr_icm_pool { enum mlx5dr_icm_type icm_type; enum mlx5dr_icm_chunk_size max_log_chunk_sz; @@ -15,6 +21,13 @@ struct mlx5dr_icm_pool { /* memory management */ struct mutex mutex; /* protect the ICM pool and ICM buddy */ struct list_head buddy_mem_list; + + /* Hardware may be accessing this memory but at some future, + * undetermined time, it might cease to do so. + * sync_ste command sets them free. + */ + struct mlx5dr_icm_hot_chunk *hot_chunks_arr; + u32 hot_chunks_num; u64 hot_memory_size; }; @@ -286,9 +299,6 @@ static void dr_icm_buddy_destroy(struct mlx5dr_icm_buddy_mem *buddy) { struct mlx5dr_icm_chunk *chunk, *next; - list_for_each_entry_safe(chunk, next, &buddy->hot_list, chunk_list) - dr_icm_chunk_destroy(chunk); - list_for_each_entry_safe(chunk, next, &buddy->used_list, chunk_list) dr_icm_chunk_destroy(chunk); @@ -347,10 +357,28 @@ static bool dr_icm_pool_is_sync_required(struct mlx5dr_icm_pool *pool) return pool->hot_memory_size > allow_hot_size; } +static void dr_icm_pool_clear_hot_chunks_arr(struct mlx5dr_icm_pool *pool) +{ + struct mlx5dr_icm_hot_chunk *hot_chunk; + u32 i, num_entries; + + for (i = 0; i < pool->hot_chunks_num; i++) { + hot_chunk = &pool->hot_chunks_arr[i]; + num_entries = mlx5dr_icm_pool_chunk_size_to_entries(hot_chunk->size); + mlx5dr_buddy_free_mem(hot_chunk->buddy_mem, + hot_chunk->seg, ilog2(num_entries)); + hot_chunk->buddy_mem->used_memory -= + mlx5dr_icm_pool_chunk_size_to_byte(hot_chunk->size, + pool->icm_type); + } + + pool->hot_chunks_num = 0; + pool->hot_memory_size = 0; +} + static int dr_icm_pool_sync_all_buddy_pools(struct mlx5dr_icm_pool *pool) { struct mlx5dr_icm_buddy_mem *buddy, *tmp_buddy; - u32 num_entries; int err; err = mlx5dr_cmd_sync_steering(pool->dmn->mdev); @@ -359,16 +387,9 @@ static int dr_icm_pool_sync_all_buddy_pools(struct mlx5dr_icm_pool *pool) return err; } + dr_icm_pool_clear_hot_chunks_arr(pool); + list_for_each_entry_safe(buddy, tmp_buddy, &pool->buddy_mem_list, list_node) { - struct mlx5dr_icm_chunk *chunk, *tmp_chunk; - - list_for_each_entry_safe(chunk, tmp_chunk, &buddy->hot_list, chunk_list) { - num_entries = mlx5dr_icm_pool_get_chunk_num_of_entries(chunk); - mlx5dr_buddy_free_mem(buddy, chunk->seg, ilog2(num_entries)); - pool->hot_memory_size -= mlx5dr_icm_pool_get_chunk_byte_size(chunk); - dr_icm_chunk_destroy(chunk); - } - if (!buddy->used_memory && pool->icm_type == DR_ICM_TYPE_STE) dr_icm_buddy_destroy(buddy); } @@ -459,12 +480,24 @@ void mlx5dr_icm_free_chunk(struct mlx5dr_icm_chunk *chunk) { struct mlx5dr_icm_buddy_mem *buddy = chunk->buddy_mem; struct mlx5dr_icm_pool *pool = buddy->pool; + struct mlx5dr_icm_hot_chunk *hot_chunk; + struct kmem_cache *chunks_cache; - /* move the memory to the waiting list AKA "hot" */ + chunks_cache = pool->chunks_kmem_cache; + + /* move the chunk to the waiting chunks array, AKA "hot" memory */ mutex_lock(&pool->mutex); - list_move_tail(&chunk->chunk_list, &buddy->hot_list); + pool->hot_memory_size += mlx5dr_icm_pool_get_chunk_byte_size(chunk); + hot_chunk = &pool->hot_chunks_arr[pool->hot_chunks_num++]; + hot_chunk->buddy_mem = chunk->buddy_mem; + hot_chunk->seg = chunk->seg; + hot_chunk->size = chunk->size; + + list_del(&chunk->chunk_list); + kmem_cache_free(chunks_cache, chunk); + /* Check if we have chunks that are waiting for sync-ste */ if (dr_icm_pool_is_sync_required(pool)) dr_icm_pool_sync_all_buddy_pools(pool); @@ -485,6 +518,7 @@ void mlx5dr_icm_pool_free_htbl(struct mlx5dr_icm_pool *pool, struct mlx5dr_ste_h struct mlx5dr_icm_pool *mlx5dr_icm_pool_create(struct mlx5dr_domain *dmn, enum mlx5dr_icm_type icm_type) { + u32 num_of_chunks, entry_size, max_hot_size; enum mlx5dr_icm_chunk_size max_log_chunk_sz; struct mlx5dr_icm_pool *pool; @@ -506,16 +540,37 @@ struct mlx5dr_icm_pool *mlx5dr_icm_pool_create(struct mlx5dr_domain *dmn, mutex_init(&pool->mutex); + entry_size = mlx5dr_icm_pool_dm_type_to_entry_size(pool->icm_type); + + max_hot_size = mlx5dr_icm_pool_chunk_size_to_byte(pool->max_log_chunk_sz, + pool->icm_type) / + DR_ICM_POOL_HOT_MEMORY_FRACTION; + + num_of_chunks = DIV_ROUND_UP(max_hot_size, entry_size) + 1; + + pool->hot_chunks_arr = kvcalloc(num_of_chunks, + sizeof(struct mlx5dr_icm_hot_chunk), + GFP_KERNEL); + if (!pool->hot_chunks_arr) + goto free_pool; + return pool; + +free_pool: + kvfree(pool); + return NULL; } void mlx5dr_icm_pool_destroy(struct mlx5dr_icm_pool *pool) { struct mlx5dr_icm_buddy_mem *buddy, *tmp_buddy; + dr_icm_pool_clear_hot_chunks_arr(pool); + list_for_each_entry_safe(buddy, tmp_buddy, &pool->buddy_mem_list, list_node) dr_icm_buddy_destroy(buddy); + kvfree(pool->hot_chunks_arr); mutex_destroy(&pool->mutex); kvfree(pool); } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_types.h b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_types.h index b645c0ab9a72..bd2c3073591e 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_types.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_types.h @@ -147,6 +147,7 @@ struct mlx5dr_rule_rx_tx; struct mlx5dr_matcher_rx_tx; struct mlx5dr_ste_ctx; struct mlx5dr_send_info_pool; +struct mlx5dr_icm_hot_chunk; struct mlx5dr_ste { /* refcount: indicates the num of rules that using this ste */ diff --git a/drivers/net/ethernet/mellanox/mlx5/core/steering/mlx5dr.h b/drivers/net/ethernet/mellanox/mlx5/core/steering/mlx5dr.h index 226a0d7bb06d..674efb3607b1 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/steering/mlx5dr.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/steering/mlx5dr.h @@ -168,12 +168,6 @@ struct mlx5dr_icm_buddy_mem { struct list_head used_list; u64 used_memory; - /* Hardware may be accessing this memory but at some future, - * undetermined time, it might cease to do so. - * sync_ste command sets them free. - */ - struct list_head hot_list; - /* Memory optimisation */ struct mlx5dr_ste *ste_arr; struct list_head *miss_list; From edaea001442a792e4b14c7523072b9687700585e Mon Sep 17 00:00:00 2001 From: Yevgeny Kliteynik Date: Thu, 30 Jun 2022 18:17:29 +0300 Subject: [PATCH 0424/2002] net/mlx5: DR, Remove the buddy used_list No need to have the used_list - we don't need to keep track of the used chunks, we only need to know the amount of used memory. Signed-off-by: Yevgeny Kliteynik Reviewed-by: Alex Vesker Signed-off-by: Saeed Mahameed --- .../mellanox/mlx5/core/steering/dr_buddy.c | 1 - .../mellanox/mlx5/core/steering/dr_icm_pool.c | 50 +++++-------------- .../mellanox/mlx5/core/steering/dr_types.h | 1 - .../mellanox/mlx5/core/steering/mlx5dr.h | 3 +- 4 files changed, 13 insertions(+), 42 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_buddy.c b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_buddy.c index 7e30dc64c10c..fe228d948b47 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_buddy.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_buddy.c @@ -15,7 +15,6 @@ int mlx5dr_buddy_init(struct mlx5dr_icm_buddy_mem *buddy, buddy->max_order = max_order; INIT_LIST_HEAD(&buddy->list_node); - INIT_LIST_HEAD(&buddy->used_list); buddy->bitmap = kcalloc(buddy->max_order + 1, sizeof(*buddy->bitmap), diff --git a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_icm_pool.c b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_icm_pool.c index c31608a3f11c..3eb6719bc8eb 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_icm_pool.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_icm_pool.c @@ -207,17 +207,6 @@ static void dr_icm_chunk_ste_init(struct mlx5dr_icm_chunk *chunk, int offset) num_of_entries * sizeof(chunk->ste_arr[0])); } -static void dr_icm_chunk_destroy(struct mlx5dr_icm_chunk *chunk) -{ - struct kmem_cache *chunks_cache = - chunk->buddy_mem->pool->chunks_kmem_cache; - - chunk->buddy_mem->used_memory -= mlx5dr_icm_pool_get_chunk_byte_size(chunk); - list_del(&chunk->chunk_list); - - kmem_cache_free(chunks_cache, chunk); -} - static int dr_icm_buddy_init_ste_cache(struct mlx5dr_icm_buddy_mem *buddy) { int num_of_entries = @@ -297,11 +286,6 @@ free_mr: static void dr_icm_buddy_destroy(struct mlx5dr_icm_buddy_mem *buddy) { - struct mlx5dr_icm_chunk *chunk, *next; - - list_for_each_entry_safe(chunk, next, &buddy->used_list, chunk_list) - dr_icm_chunk_destroy(chunk); - dr_icm_pool_mr_destroy(buddy->icm_mr); mlx5dr_buddy_cleanup(buddy); @@ -312,36 +296,25 @@ static void dr_icm_buddy_destroy(struct mlx5dr_icm_buddy_mem *buddy) kvfree(buddy); } -static struct mlx5dr_icm_chunk * -dr_icm_chunk_create(struct mlx5dr_icm_pool *pool, - enum mlx5dr_icm_chunk_size chunk_size, - struct mlx5dr_icm_buddy_mem *buddy_mem_pool, - unsigned int seg) +static void +dr_icm_chunk_init(struct mlx5dr_icm_chunk *chunk, + struct mlx5dr_icm_pool *pool, + enum mlx5dr_icm_chunk_size chunk_size, + struct mlx5dr_icm_buddy_mem *buddy_mem_pool, + unsigned int seg) { - struct kmem_cache *chunks_cache = buddy_mem_pool->pool->chunks_kmem_cache; - struct mlx5dr_icm_chunk *chunk; int offset; - chunk = kmem_cache_alloc(chunks_cache, GFP_KERNEL); - if (!chunk) - return NULL; - - offset = mlx5dr_icm_pool_dm_type_to_entry_size(pool->icm_type) * seg; - chunk->seg = seg; chunk->size = chunk_size; chunk->buddy_mem = buddy_mem_pool; - if (pool->icm_type == DR_ICM_TYPE_STE) + if (pool->icm_type == DR_ICM_TYPE_STE) { + offset = mlx5dr_icm_pool_dm_type_to_entry_size(pool->icm_type) * seg; dr_icm_chunk_ste_init(chunk, offset); + } buddy_mem_pool->used_memory += mlx5dr_icm_pool_get_chunk_byte_size(chunk); - INIT_LIST_HEAD(&chunk->chunk_list); - - /* chunk now is part of the used_list */ - list_add_tail(&chunk->chunk_list, &buddy_mem_pool->used_list); - - return chunk; } static bool dr_icm_pool_is_sync_required(struct mlx5dr_icm_pool *pool) @@ -463,10 +436,12 @@ mlx5dr_icm_alloc_chunk(struct mlx5dr_icm_pool *pool, if (ret) goto out; - chunk = dr_icm_chunk_create(pool, chunk_size, buddy, seg); + chunk = kmem_cache_alloc(pool->chunks_kmem_cache, GFP_KERNEL); if (!chunk) goto out_err; + dr_icm_chunk_init(chunk, pool, chunk_size, buddy, seg); + goto out; out_err: @@ -495,7 +470,6 @@ void mlx5dr_icm_free_chunk(struct mlx5dr_icm_chunk *chunk) hot_chunk->seg = chunk->seg; hot_chunk->size = chunk->size; - list_del(&chunk->chunk_list); kmem_cache_free(chunks_cache, chunk); /* Check if we have chunks that are waiting for sync-ste */ diff --git a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_types.h b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_types.h index bd2c3073591e..41a37b9ac98b 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_types.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_types.h @@ -1111,7 +1111,6 @@ int mlx5dr_rule_get_reverse_rule_members(struct mlx5dr_ste **ste_arr, struct mlx5dr_icm_chunk { struct mlx5dr_icm_buddy_mem *buddy_mem; - struct list_head chunk_list; /* indicates the index of this chunk in the whole memory, * used for deleting the chunk from the buddy diff --git a/drivers/net/ethernet/mellanox/mlx5/core/steering/mlx5dr.h b/drivers/net/ethernet/mellanox/mlx5/core/steering/mlx5dr.h index 674efb3607b1..84ed77763b21 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/steering/mlx5dr.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/steering/mlx5dr.h @@ -164,8 +164,7 @@ struct mlx5dr_icm_buddy_mem { struct mlx5dr_icm_mr *icm_mr; struct mlx5dr_icm_pool *pool; - /* This is the list of used chunks. HW may be accessing this memory */ - struct list_head used_list; + /* Amount of memory in used chunks - HW may be accessing this memory */ u64 used_memory; /* Memory optimisation */ From b6d207999c350b19a787e48757f98198d0bf3e5b Mon Sep 17 00:00:00 2001 From: Xu Kuohai Date: Wed, 26 Oct 2022 23:44:58 -0400 Subject: [PATCH 0425/2002] bpf: Fix a typo in comment for DFS algorithm There is a typo in comment for DFS algorithm in bpf/verifier.c. The top element should not be popped until all its neighbors have been checked. Fix it. Fixes: 475fb78fbf48 ("bpf: verifier (add branch/goto checks)") Signed-off-by: Xu Kuohai Signed-off-by: Andrii Nakryiko Link: https://lore.kernel.org/bpf/20221027034458.2925218-1-xukuohai@huaweicloud.com --- kernel/bpf/verifier.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 8f849a763b79..275c2f1f00ee 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -10670,7 +10670,7 @@ static int check_return_code(struct bpf_verifier_env *env) * 3 let S be a stack * 4 S.push(v) * 5 while S is not empty - * 6 t <- S.pop() + * 6 t <- S.peek() * 7 if t is what we're looking for: * 8 return t * 9 for all edges e in G.adjacentEdges(t) do From db28adf9afeb1c3a627546d9b9c8cf45c27267b4 Mon Sep 17 00:00:00 2001 From: Shannon Nelson Date: Wed, 26 Oct 2022 07:37:40 -0700 Subject: [PATCH 0426/2002] ionic: replay VF attributes after fw crash recovery The VF attributes that the user has set into the FW through the PF can be lost over a FW crash recovery. Much like we already replay the PF mac/vlan filters, we now add a replay in the recovery path to be sure the FW has the up-to-date VF configurations. Signed-off-by: Shannon Nelson Signed-off-by: Jakub Kicinski --- .../net/ethernet/pensando/ionic/ionic_lif.c | 70 +++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/drivers/net/ethernet/pensando/ionic/ionic_lif.c b/drivers/net/ethernet/pensando/ionic/ionic_lif.c index 19d4848df17d..865ee58a3b1f 100644 --- a/drivers/net/ethernet/pensando/ionic/ionic_lif.c +++ b/drivers/net/ethernet/pensando/ionic/ionic_lif.c @@ -2562,6 +2562,74 @@ static int ionic_set_vf_link_state(struct net_device *netdev, int vf, int set) return ret; } +static void ionic_vf_attr_replay(struct ionic_lif *lif) +{ + struct ionic_vf_setattr_cmd vfc = { }; + struct ionic *ionic = lif->ionic; + struct ionic_vf *v; + int i; + + if (!ionic->vfs) + return; + + down_read(&ionic->vf_op_lock); + + for (i = 0; i < ionic->num_vfs; i++) { + v = &ionic->vfs[i]; + + if (v->stats_pa) { + vfc.attr = IONIC_VF_ATTR_STATSADDR; + vfc.stats_pa = cpu_to_le64(v->stats_pa); + ionic_set_vf_config(ionic, i, &vfc); + vfc.stats_pa = 0; + } + + if (!is_zero_ether_addr(v->macaddr)) { + vfc.attr = IONIC_VF_ATTR_MAC; + ether_addr_copy(vfc.macaddr, v->macaddr); + ionic_set_vf_config(ionic, i, &vfc); + eth_zero_addr(vfc.macaddr); + } + + if (v->vlanid) { + vfc.attr = IONIC_VF_ATTR_VLAN; + vfc.vlanid = v->vlanid; + ionic_set_vf_config(ionic, i, &vfc); + vfc.vlanid = 0; + } + + if (v->maxrate) { + vfc.attr = IONIC_VF_ATTR_RATE; + vfc.maxrate = v->maxrate; + ionic_set_vf_config(ionic, i, &vfc); + vfc.maxrate = 0; + } + + if (v->spoofchk) { + vfc.attr = IONIC_VF_ATTR_SPOOFCHK; + vfc.spoofchk = v->spoofchk; + ionic_set_vf_config(ionic, i, &vfc); + vfc.spoofchk = 0; + } + + if (v->trusted) { + vfc.attr = IONIC_VF_ATTR_TRUST; + vfc.trust = v->trusted; + ionic_set_vf_config(ionic, i, &vfc); + vfc.trust = 0; + } + + if (v->linkstate) { + vfc.attr = IONIC_VF_ATTR_LINKSTATE; + vfc.linkstate = v->linkstate; + ionic_set_vf_config(ionic, i, &vfc); + vfc.linkstate = 0; + } + } + + up_read(&ionic->vf_op_lock); +} + static const struct net_device_ops ionic_netdev_ops = { .ndo_open = ionic_open, .ndo_stop = ionic_stop, @@ -3042,6 +3110,8 @@ static void ionic_lif_handle_fw_up(struct ionic_lif *lif) if (err) goto err_qcqs_free; + ionic_vf_attr_replay(lif); + if (lif->registered) ionic_lif_set_netdev_info(lif); From 23e884a253a77ed565e0c410fae660ea8e7081c8 Mon Sep 17 00:00:00 2001 From: Shannon Nelson Date: Wed, 26 Oct 2022 07:37:41 -0700 Subject: [PATCH 0427/2002] ionic: only save the user set VF attributes Report the current FW values for the VF attributes, but don't save the FW values locally, only save the vf attributes that are given to us from the user. This allows us to replay user data, and doesn't end up confusing things like "who set the mac address". Signed-off-by: Shannon Nelson Signed-off-by: Jakub Kicinski --- .../net/ethernet/pensando/ionic/ionic_lif.c | 33 ++++++++++--------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/drivers/net/ethernet/pensando/ionic/ionic_lif.c b/drivers/net/ethernet/pensando/ionic/ionic_lif.c index 865ee58a3b1f..a06b2da1e0c4 100644 --- a/drivers/net/ethernet/pensando/ionic/ionic_lif.c +++ b/drivers/net/ethernet/pensando/ionic/ionic_lif.c @@ -2220,7 +2220,7 @@ static int ionic_eth_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd } } -static int ionic_update_cached_vf_config(struct ionic *ionic, int vf) +static int ionic_get_fw_vf_config(struct ionic *ionic, int vf, struct ionic_vf *vfdata) { struct ionic_vf_getattr_comp comp = { 0 }; int err; @@ -2231,14 +2231,14 @@ static int ionic_update_cached_vf_config(struct ionic *ionic, int vf) if (err && comp.status != IONIC_RC_ENOSUPP) goto err_out; if (!err) - ionic->vfs[vf].vlanid = comp.vlanid; + vfdata->vlanid = comp.vlanid; attr = IONIC_VF_ATTR_SPOOFCHK; err = ionic_dev_cmd_vf_getattr(ionic, vf, attr, &comp); if (err && comp.status != IONIC_RC_ENOSUPP) goto err_out; if (!err) - ionic->vfs[vf].spoofchk = comp.spoofchk; + vfdata->spoofchk = comp.spoofchk; attr = IONIC_VF_ATTR_LINKSTATE; err = ionic_dev_cmd_vf_getattr(ionic, vf, attr, &comp); @@ -2247,13 +2247,13 @@ static int ionic_update_cached_vf_config(struct ionic *ionic, int vf) if (!err) { switch (comp.linkstate) { case IONIC_VF_LINK_STATUS_UP: - ionic->vfs[vf].linkstate = IFLA_VF_LINK_STATE_ENABLE; + vfdata->linkstate = IFLA_VF_LINK_STATE_ENABLE; break; case IONIC_VF_LINK_STATUS_DOWN: - ionic->vfs[vf].linkstate = IFLA_VF_LINK_STATE_DISABLE; + vfdata->linkstate = IFLA_VF_LINK_STATE_DISABLE; break; case IONIC_VF_LINK_STATUS_AUTO: - ionic->vfs[vf].linkstate = IFLA_VF_LINK_STATE_AUTO; + vfdata->linkstate = IFLA_VF_LINK_STATE_AUTO; break; default: dev_warn(ionic->dev, "Unexpected link state %u\n", comp.linkstate); @@ -2266,21 +2266,21 @@ static int ionic_update_cached_vf_config(struct ionic *ionic, int vf) if (err && comp.status != IONIC_RC_ENOSUPP) goto err_out; if (!err) - ionic->vfs[vf].maxrate = comp.maxrate; + vfdata->maxrate = comp.maxrate; attr = IONIC_VF_ATTR_TRUST; err = ionic_dev_cmd_vf_getattr(ionic, vf, attr, &comp); if (err && comp.status != IONIC_RC_ENOSUPP) goto err_out; if (!err) - ionic->vfs[vf].trusted = comp.trust; + vfdata->trusted = comp.trust; attr = IONIC_VF_ATTR_MAC; err = ionic_dev_cmd_vf_getattr(ionic, vf, attr, &comp); if (err && comp.status != IONIC_RC_ENOSUPP) goto err_out; if (!err) - ether_addr_copy(ionic->vfs[vf].macaddr, comp.macaddr); + ether_addr_copy(vfdata->macaddr, comp.macaddr); err_out: if (err) @@ -2295,6 +2295,7 @@ static int ionic_get_vf_config(struct net_device *netdev, { struct ionic_lif *lif = netdev_priv(netdev); struct ionic *ionic = lif->ionic; + struct ionic_vf vfdata = { 0 }; int ret = 0; if (!netif_device_present(netdev)) @@ -2308,14 +2309,14 @@ static int ionic_get_vf_config(struct net_device *netdev, ivf->vf = vf; ivf->qos = 0; - ret = ionic_update_cached_vf_config(ionic, vf); + ret = ionic_get_fw_vf_config(ionic, vf, &vfdata); if (!ret) { - ivf->vlan = le16_to_cpu(ionic->vfs[vf].vlanid); - ivf->spoofchk = ionic->vfs[vf].spoofchk; - ivf->linkstate = ionic->vfs[vf].linkstate; - ivf->max_tx_rate = le32_to_cpu(ionic->vfs[vf].maxrate); - ivf->trusted = ionic->vfs[vf].trusted; - ether_addr_copy(ivf->mac, ionic->vfs[vf].macaddr); + ivf->vlan = le16_to_cpu(vfdata.vlanid); + ivf->spoofchk = vfdata.spoofchk; + ivf->linkstate = vfdata.linkstate; + ivf->max_tx_rate = le32_to_cpu(vfdata.maxrate); + ivf->trusted = vfdata.trusted; + ether_addr_copy(ivf->mac, vfdata.macaddr); } } From f43a96d91df1a097d7d7edd5baff960f4067b2a7 Mon Sep 17 00:00:00 2001 From: Shannon Nelson Date: Wed, 26 Oct 2022 07:37:42 -0700 Subject: [PATCH 0428/2002] ionic: new ionic device identity level and VF start control A new ionic dev_cmd is added to the interface in ionic_if.h, with a new capabilities field in the ionic device identity to signal its availability in the FW. The identity level code is incremented to '2' to show support for this new capabilities bitfield. If the driver has indicated with the new identity level that it has the VF_CTRL command, newer FW will wait for the start command before starting the VFs after a FW update or crash recovery. This patch updates the driver to make use of the new VF start control in fw_up path to be sure that the PF has set the user attributes on the VF before the FW allows the VFs to restart. Signed-off-by: Shannon Nelson Signed-off-by: Jakub Kicinski --- .../net/ethernet/pensando/ionic/ionic_dev.c | 14 ++++++ .../net/ethernet/pensando/ionic/ionic_dev.h | 3 ++ .../net/ethernet/pensando/ionic/ionic_if.h | 45 ++++++++++++++++++- .../net/ethernet/pensando/ionic/ionic_lif.c | 2 + .../net/ethernet/pensando/ionic/ionic_main.c | 2 +- 5 files changed, 64 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/pensando/ionic/ionic_dev.c b/drivers/net/ethernet/pensando/ionic/ionic_dev.c index 9d0514cfeb5c..626b9113e7c4 100644 --- a/drivers/net/ethernet/pensando/ionic/ionic_dev.c +++ b/drivers/net/ethernet/pensando/ionic/ionic_dev.c @@ -481,6 +481,20 @@ int ionic_dev_cmd_vf_getattr(struct ionic *ionic, int vf, u8 attr, return err; } +void ionic_vf_start(struct ionic *ionic) +{ + union ionic_dev_cmd cmd = { + .vf_ctrl.opcode = IONIC_CMD_VF_CTRL, + .vf_ctrl.ctrl_opcode = IONIC_VF_CTRL_START_ALL, + }; + + if (!(ionic->ident.dev.capabilities & cpu_to_le64(IONIC_DEV_CAP_VF_CTRL))) + return; + + ionic_dev_cmd_go(&ionic->idev, &cmd); + ionic_dev_cmd_wait(ionic, DEVCMD_TIMEOUT); +} + /* LIF commands */ void ionic_dev_cmd_queue_identify(struct ionic_dev *idev, u16 lif_type, u8 qtype, u8 qver) diff --git a/drivers/net/ethernet/pensando/ionic/ionic_dev.h b/drivers/net/ethernet/pensando/ionic/ionic_dev.h index 563c302eb033..2a1d7b9c07e7 100644 --- a/drivers/net/ethernet/pensando/ionic/ionic_dev.h +++ b/drivers/net/ethernet/pensando/ionic/ionic_dev.h @@ -124,6 +124,8 @@ static_assert(sizeof(struct ionic_vf_setattr_cmd) == 64); static_assert(sizeof(struct ionic_vf_setattr_comp) == 16); static_assert(sizeof(struct ionic_vf_getattr_cmd) == 64); static_assert(sizeof(struct ionic_vf_getattr_comp) == 16); +static_assert(sizeof(struct ionic_vf_ctrl_cmd) == 64); +static_assert(sizeof(struct ionic_vf_ctrl_comp) == 16); #endif /* __CHECKER__ */ struct ionic_devinfo { @@ -324,6 +326,7 @@ int ionic_dev_cmd_vf_getattr(struct ionic *ionic, int vf, u8 attr, struct ionic_vf_getattr_comp *comp); void ionic_dev_cmd_queue_identify(struct ionic_dev *idev, u16 lif_type, u8 qtype, u8 qver); +void ionic_vf_start(struct ionic *ionic); void ionic_dev_cmd_lif_identify(struct ionic_dev *idev, u8 type, u8 ver); void ionic_dev_cmd_lif_init(struct ionic_dev *idev, u16 lif_index, dma_addr_t addr); diff --git a/drivers/net/ethernet/pensando/ionic/ionic_if.h b/drivers/net/ethernet/pensando/ionic/ionic_if.h index 4a90f611c611..eac09b2375b8 100644 --- a/drivers/net/ethernet/pensando/ionic/ionic_if.h +++ b/drivers/net/ethernet/pensando/ionic/ionic_if.h @@ -8,7 +8,7 @@ #define IONIC_DEV_INFO_VERSION 1 #define IONIC_IFNAMSIZ 16 -/** +/* * enum ionic_cmd_opcode - Device commands */ enum ionic_cmd_opcode { @@ -54,6 +54,7 @@ enum ionic_cmd_opcode { /* SR/IOV commands */ IONIC_CMD_VF_GETATTR = 60, IONIC_CMD_VF_SETATTR = 61, + IONIC_CMD_VF_CTRL = 62, /* QoS commands */ IONIC_CMD_QOS_CLASS_IDENTIFY = 240, @@ -200,6 +201,7 @@ struct ionic_dev_reset_comp { }; #define IONIC_IDENTITY_VERSION_1 1 +#define IONIC_DEV_IDENTITY_VERSION_2 2 /** * struct ionic_dev_identify_cmd - Driver/device identify command @@ -253,6 +255,14 @@ union ionic_drv_identity { __le32 words[478]; }; +/** + * enum ionic_dev_capability - Device capabilities + * @IONIC_DEV_CAP_VF_CTRL: Device supports VF ctrl operations + */ +enum ionic_dev_capability { + IONIC_DEV_CAP_VF_CTRL = BIT(0), +}; + /** * union ionic_dev_identity - device identity information * @version: Version of device identify @@ -273,6 +283,7 @@ union ionic_drv_identity { * @hwstamp_mask: Bitmask for subtraction of hardware tick values. * @hwstamp_mult: Hardware tick to nanosecond multiplier. * @hwstamp_shift: Hardware tick to nanosecond divisor (power of two). + * @capabilities: Device capabilities */ union ionic_dev_identity { struct { @@ -290,6 +301,7 @@ union ionic_dev_identity { __le64 hwstamp_mask; __le32 hwstamp_mult; __le32 hwstamp_shift; + __le64 capabilities; }; __le32 words[478]; }; @@ -2044,6 +2056,35 @@ struct ionic_vf_getattr_comp { u8 color; }; +enum ionic_vf_ctrl_opcode { + IONIC_VF_CTRL_START_ALL = 0, + IONIC_VF_CTRL_START = 1, +}; + +/** + * struct ionic_vf_ctrl_cmd - VF control command + * @opcode: Opcode for the command + * @vf_index: VF Index. It is unused if op START_ALL is used. + * @ctrl_opcode: VF control operation type + */ +struct ionic_vf_ctrl_cmd { + u8 opcode; + u8 ctrl_opcode; + __le16 vf_index; + /* private: */ + u8 rsvd1[60]; +}; + +/** + * struct ionic_vf_ctrl_comp - VF_CTRL command completion. + * @status: Status of the command (enum ionic_status_code) + */ +struct ionic_vf_ctrl_comp { + u8 status; + /* private: */ + u8 rsvd[15]; +}; + /** * struct ionic_qos_identify_cmd - QoS identify command * @opcode: opcode @@ -2865,6 +2906,7 @@ union ionic_dev_cmd { struct ionic_vf_setattr_cmd vf_setattr; struct ionic_vf_getattr_cmd vf_getattr; + struct ionic_vf_ctrl_cmd vf_ctrl; struct ionic_lif_identify_cmd lif_identify; struct ionic_lif_init_cmd lif_init; @@ -2903,6 +2945,7 @@ union ionic_dev_cmd_comp { struct ionic_vf_setattr_comp vf_setattr; struct ionic_vf_getattr_comp vf_getattr; + struct ionic_vf_ctrl_comp vf_ctrl; struct ionic_lif_identify_comp lif_identify; struct ionic_lif_init_comp lif_init; diff --git a/drivers/net/ethernet/pensando/ionic/ionic_lif.c b/drivers/net/ethernet/pensando/ionic/ionic_lif.c index a06b2da1e0c4..f5d39594ef54 100644 --- a/drivers/net/ethernet/pensando/ionic/ionic_lif.c +++ b/drivers/net/ethernet/pensando/ionic/ionic_lif.c @@ -2629,6 +2629,8 @@ static void ionic_vf_attr_replay(struct ionic_lif *lif) } up_read(&ionic->vf_op_lock); + + ionic_vf_start(ionic); } static const struct net_device_ops ionic_netdev_ops = { diff --git a/drivers/net/ethernet/pensando/ionic/ionic_main.c b/drivers/net/ethernet/pensando/ionic/ionic_main.c index 56f93b030551..ed9d8c995236 100644 --- a/drivers/net/ethernet/pensando/ionic/ionic_main.c +++ b/drivers/net/ethernet/pensando/ionic/ionic_main.c @@ -533,7 +533,7 @@ int ionic_identify(struct ionic *ionic) sz = min(sizeof(ident->drv), sizeof(idev->dev_cmd_regs->data)); memcpy_toio(&idev->dev_cmd_regs->data, &ident->drv, sz); - ionic_dev_cmd_identify(idev, IONIC_IDENTITY_VERSION_1); + ionic_dev_cmd_identify(idev, IONIC_DEV_IDENTITY_VERSION_2); err = ionic_dev_cmd_wait(ionic, DEVCMD_TIMEOUT); if (!err) { sz = min(sizeof(ident->dev), sizeof(idev->dev_cmd_regs->data)); From cad478c7c3321b03f6f1d07772afad468e698fc2 Mon Sep 17 00:00:00 2001 From: Neel Patel Date: Wed, 26 Oct 2022 07:37:43 -0700 Subject: [PATCH 0429/2002] ionic: enable tunnel offloads Support stateless offloads for GRE, VXLAN, GENEVE, IPXIP4 and IPXIP6 when the FW supports them. Signed-off-by: Neel Patel Signed-off-by: Shannon Nelson Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/pensando/ionic/ionic_lif.c | 8 +++++++- drivers/net/ethernet/pensando/ionic/ionic_txrx.c | 8 ++++++-- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/pensando/ionic/ionic_lif.c b/drivers/net/ethernet/pensando/ionic/ionic_lif.c index f5d39594ef54..4dd16c487f2b 100644 --- a/drivers/net/ethernet/pensando/ionic/ionic_lif.c +++ b/drivers/net/ethernet/pensando/ionic/ionic_lif.c @@ -1491,7 +1491,13 @@ static int ionic_init_nic_features(struct ionic_lif *lif) NETIF_F_RXCSUM | NETIF_F_TSO | NETIF_F_TSO6 | - NETIF_F_TSO_ECN; + NETIF_F_TSO_ECN | + NETIF_F_GSO_GRE | + NETIF_F_GSO_GRE_CSUM | + NETIF_F_GSO_IPXIP4 | + NETIF_F_GSO_IPXIP6 | + NETIF_F_GSO_UDP_TUNNEL | + NETIF_F_GSO_UDP_TUNNEL_CSUM; if (lif->nxqs > 1) features |= NETIF_F_RXHASH; diff --git a/drivers/net/ethernet/pensando/ionic/ionic_txrx.c b/drivers/net/ethernet/pensando/ionic/ionic_txrx.c index c03986bf2628..190681aa7187 100644 --- a/drivers/net/ethernet/pensando/ionic/ionic_txrx.c +++ b/drivers/net/ethernet/pensando/ionic/ionic_txrx.c @@ -925,8 +925,12 @@ static int ionic_tx_tso(struct ionic_queue *q, struct sk_buff *skb) len = skb->len; mss = skb_shinfo(skb)->gso_size; - outer_csum = (skb_shinfo(skb)->gso_type & SKB_GSO_GRE_CSUM) || - (skb_shinfo(skb)->gso_type & SKB_GSO_UDP_TUNNEL_CSUM); + outer_csum = (skb_shinfo(skb)->gso_type & (SKB_GSO_GRE | + SKB_GSO_GRE_CSUM | + SKB_GSO_IPXIP4 | + SKB_GSO_IPXIP6 | + SKB_GSO_UDP_TUNNEL | + SKB_GSO_UDP_TUNNEL_CSUM)); has_vlan = !!skb_vlan_tag_present(skb); vlan_tci = skb_vlan_tag_get(skb); encap = skb->encapsulation; From e55f0f5befc26e2ba6bb8c1f945ea8e37ee0e334 Mon Sep 17 00:00:00 2001 From: Neel Patel Date: Wed, 26 Oct 2022 07:37:44 -0700 Subject: [PATCH 0430/2002] ionic: refactor use of ionic_rx_fill() The same pre-work code is used before each call to ionic_rx_fill(), so bring it in and make it a part of the routine. Signed-off-by: Neel Patel Signed-off-by: Shannon Nelson Signed-off-by: Jakub Kicinski --- .../net/ethernet/pensando/ionic/ionic_txrx.c | 23 ++++++++++--------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/drivers/net/ethernet/pensando/ionic/ionic_txrx.c b/drivers/net/ethernet/pensando/ionic/ionic_txrx.c index 190681aa7187..0c3977416cd1 100644 --- a/drivers/net/ethernet/pensando/ionic/ionic_txrx.c +++ b/drivers/net/ethernet/pensando/ionic/ionic_txrx.c @@ -348,16 +348,25 @@ void ionic_rx_fill(struct ionic_queue *q) struct ionic_rxq_sg_desc *sg_desc; struct ionic_rxq_sg_elem *sg_elem; struct ionic_buf_info *buf_info; + unsigned int fill_threshold; struct ionic_rxq_desc *desc; unsigned int remain_len; unsigned int frag_len; unsigned int nfrags; + unsigned int n_fill; unsigned int i, j; unsigned int len; + n_fill = ionic_q_space_avail(q); + + fill_threshold = min_t(unsigned int, IONIC_RX_FILL_THRESHOLD, + q->num_descs / IONIC_RX_FILL_DIV); + if (n_fill < fill_threshold) + return; + len = netdev->mtu + ETH_HLEN + VLAN_HLEN; - for (i = ionic_q_space_avail(q); i; i--) { + for (i = n_fill; i; i--) { nfrags = 0; remain_len = len; desc_info = &q->info[q->head_idx]; @@ -511,7 +520,6 @@ int ionic_rx_napi(struct napi_struct *napi, int budget) struct ionic_cq *cq = napi_to_cq(napi); struct ionic_dev *idev; struct ionic_lif *lif; - u16 rx_fill_threshold; u32 work_done = 0; u32 flags = 0; @@ -521,10 +529,7 @@ int ionic_rx_napi(struct napi_struct *napi, int budget) work_done = ionic_cq_service(cq, budget, ionic_rx_service, NULL, NULL); - rx_fill_threshold = min_t(u16, IONIC_RX_FILL_THRESHOLD, - cq->num_descs / IONIC_RX_FILL_DIV); - if (work_done && ionic_q_space_avail(cq->bound_q) >= rx_fill_threshold) - ionic_rx_fill(cq->bound_q); + ionic_rx_fill(cq->bound_q); if (work_done < budget && napi_complete_done(napi, work_done)) { ionic_dim_update(qcq, IONIC_LIF_F_RX_DIM_INTR); @@ -550,7 +555,6 @@ int ionic_txrx_napi(struct napi_struct *napi, int budget) struct ionic_dev *idev; struct ionic_lif *lif; struct ionic_cq *txcq; - u16 rx_fill_threshold; u32 rx_work_done = 0; u32 tx_work_done = 0; u32 flags = 0; @@ -565,10 +569,7 @@ int ionic_txrx_napi(struct napi_struct *napi, int budget) rx_work_done = ionic_cq_service(rxcq, budget, ionic_rx_service, NULL, NULL); - rx_fill_threshold = min_t(u16, IONIC_RX_FILL_THRESHOLD, - rxcq->num_descs / IONIC_RX_FILL_DIV); - if (rx_work_done && ionic_q_space_avail(rxcq->bound_q) >= rx_fill_threshold) - ionic_rx_fill(rxcq->bound_q); + ionic_rx_fill(rxcq->bound_q); if (rx_work_done < budget && napi_complete_done(napi, rx_work_done)) { ionic_dim_update(qcq, 0); From 148b811c7797c3facb3da509a63c80f30a039f4a Mon Sep 17 00:00:00 2001 From: Yang Li Date: Wed, 26 Oct 2022 13:18:24 +0800 Subject: [PATCH 0431/2002] net: dpaa2-eth: Simplify bool conversion ./drivers/net/ethernet/freescale/dpaa2/dpaa2-xsk.c:453:42-47: WARNING: conversion to bool not needed here Link: https://bugzilla.openanolis.cn/show_bug.cgi?id=2577 Reported-by: Abaci Robot Signed-off-by: Yang Li Link: https://lore.kernel.org/r/20221026051824.38730-1-yang.lee@linux.alibaba.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/freescale/dpaa2/dpaa2-xsk.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-xsk.c b/drivers/net/ethernet/freescale/dpaa2/dpaa2-xsk.c index 567f52affe21..051748b997f3 100644 --- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-xsk.c +++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-xsk.c @@ -450,5 +450,5 @@ bool dpaa2_xsk_tx(struct dpaa2_eth_priv *priv, xsk_tx_release(ch->xsk_pool); - return total_enqueued == budget ? true : false; + return total_enqueued == budget; } From 7a495dde27ebca4f161c116d6a0f685138843d5d Mon Sep 17 00:00:00 2001 From: Raju Lakkaraju Date: Wed, 26 Oct 2022 11:29:17 +0530 Subject: [PATCH 0432/2002] net: phy: mxl-gpy: Change gpy_update_interface() function return type gpy_update_interface() is called from gpy_read_status() which does return error codes. gpy_read_status() would benefit from returning -EINVAL, etc. Signed-off-by: Raju Lakkaraju Signed-off-by: David S. Miller --- drivers/net/phy/mxl-gpy.c | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/drivers/net/phy/mxl-gpy.c b/drivers/net/phy/mxl-gpy.c index 24bae27eedef..1383af3c2677 100644 --- a/drivers/net/phy/mxl-gpy.c +++ b/drivers/net/phy/mxl-gpy.c @@ -370,14 +370,14 @@ static int gpy_config_aneg(struct phy_device *phydev) VSPEC1_SGMII_CTRL_ANRS, VSPEC1_SGMII_CTRL_ANRS); } -static void gpy_update_interface(struct phy_device *phydev) +static int gpy_update_interface(struct phy_device *phydev) { int ret; /* Interface mode is fixed for USXGMII and integrated PHY */ if (phydev->interface == PHY_INTERFACE_MODE_USXGMII || phydev->interface == PHY_INTERFACE_MODE_INTERNAL) - return; + return -EINVAL; /* Automatically switch SERDES interface between SGMII and 2500-BaseX * according to speed. Disable ANEG in 2500-BaseX mode. @@ -387,10 +387,12 @@ static void gpy_update_interface(struct phy_device *phydev) phydev->interface = PHY_INTERFACE_MODE_2500BASEX; ret = phy_modify_mmd(phydev, MDIO_MMD_VEND1, VSPEC1_SGMII_CTRL, VSPEC1_SGMII_CTRL_ANEN, 0); - if (ret < 0) + if (ret < 0) { phydev_err(phydev, "Error: Disable of SGMII ANEG failed: %d\n", ret); + return ret; + } break; case SPEED_1000: case SPEED_100: @@ -404,15 +406,22 @@ static void gpy_update_interface(struct phy_device *phydev) ret = phy_modify_mmd(phydev, MDIO_MMD_VEND1, VSPEC1_SGMII_CTRL, VSPEC1_SGMII_ANEN_ANRS, VSPEC1_SGMII_ANEN_ANRS); - if (ret < 0) + if (ret < 0) { phydev_err(phydev, "Error: Enable of SGMII ANEG failed: %d\n", ret); + return ret; + } break; } - if (phydev->speed == SPEED_2500 || phydev->speed == SPEED_1000) - genphy_read_master_slave(phydev); + if (phydev->speed == SPEED_2500 || phydev->speed == SPEED_1000) { + ret = genphy_read_master_slave(phydev); + if (ret < 0) + return ret; + } + + return 0; } static int gpy_read_status(struct phy_device *phydev) @@ -463,8 +472,11 @@ static int gpy_read_status(struct phy_device *phydev) break; } - if (phydev->link) - gpy_update_interface(phydev); + if (phydev->link) { + ret = gpy_update_interface(phydev); + if (ret < 0) + return ret; + } return 0; } From fd8825cd8c6fc833be90a00ed1be41f9e0c05db8 Mon Sep 17 00:00:00 2001 From: Raju Lakkaraju Date: Wed, 26 Oct 2022 11:29:18 +0530 Subject: [PATCH 0433/2002] net: phy: mxl-gpy: Add PHY Auto/MDI/MDI-X set driver for GPY211 chips Add support for MDI-X status and configuration for GPY211 chips Signed-off-by: Raju Lakkaraju Signed-off-by: David S. Miller --- drivers/net/phy/mxl-gpy.c | 72 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 71 insertions(+), 1 deletion(-) diff --git a/drivers/net/phy/mxl-gpy.c b/drivers/net/phy/mxl-gpy.c index 1383af3c2677..27c0f161623e 100644 --- a/drivers/net/phy/mxl-gpy.c +++ b/drivers/net/phy/mxl-gpy.c @@ -29,6 +29,10 @@ #define PHY_ID_GPY241BM 0x67C9DE80 #define PHY_ID_GPY245B 0x67C9DEC0 +#define PHY_CTL1 0x13 +#define PHY_CTL1_MDICD BIT(3) +#define PHY_CTL1_MDIAB BIT(2) +#define PHY_CTL1_AMDIX BIT(0) #define PHY_MIISTAT 0x18 /* MII state */ #define PHY_IMASK 0x19 /* interrupt mask */ #define PHY_ISTAT 0x1A /* interrupt status */ @@ -59,6 +63,13 @@ #define PHY_FWV_MAJOR_MASK GENMASK(11, 8) #define PHY_FWV_MINOR_MASK GENMASK(7, 0) +#define PHY_PMA_MGBT_POLARITY 0x82 +#define PHY_MDI_MDI_X_MASK GENMASK(1, 0) +#define PHY_MDI_MDI_X_NORMAL 0x3 +#define PHY_MDI_MDI_X_AB 0x2 +#define PHY_MDI_MDI_X_CD 0x1 +#define PHY_MDI_MDI_X_CROSS 0x0 + /* SGMII */ #define VSPEC1_SGMII_CTRL 0x08 #define VSPEC1_SGMII_CTRL_ANEN BIT(12) /* Aneg enable */ @@ -289,6 +300,33 @@ static bool gpy_sgmii_aneg_en(struct phy_device *phydev) return (ret & VSPEC1_SGMII_CTRL_ANEN) ? true : false; } +static int gpy_config_mdix(struct phy_device *phydev, u8 ctrl) +{ + int ret; + u16 val; + + switch (ctrl) { + case ETH_TP_MDI_AUTO: + val = PHY_CTL1_AMDIX; + break; + case ETH_TP_MDI_X: + val = (PHY_CTL1_MDIAB | PHY_CTL1_MDICD); + break; + case ETH_TP_MDI: + val = 0; + break; + default: + return 0; + } + + ret = phy_modify(phydev, PHY_CTL1, PHY_CTL1_AMDIX | PHY_CTL1_MDIAB | + PHY_CTL1_MDICD, val); + if (ret < 0) + return ret; + + return genphy_c45_restart_aneg(phydev); +} + static int gpy_config_aneg(struct phy_device *phydev) { bool changed = false; @@ -304,6 +342,10 @@ static int gpy_config_aneg(struct phy_device *phydev) : genphy_c45_pma_setup_forced(phydev); } + ret = gpy_config_mdix(phydev, phydev->mdix_ctrl); + if (ret < 0) + return ret; + ret = genphy_c45_an_config_aneg(phydev); if (ret < 0) return ret; @@ -370,6 +412,34 @@ static int gpy_config_aneg(struct phy_device *phydev) VSPEC1_SGMII_CTRL_ANRS, VSPEC1_SGMII_CTRL_ANRS); } +static int gpy_update_mdix(struct phy_device *phydev) +{ + int ret; + + ret = phy_read(phydev, PHY_CTL1); + if (ret < 0) + return ret; + + if (ret & PHY_CTL1_AMDIX) + phydev->mdix_ctrl = ETH_TP_MDI_AUTO; + else + if (ret & PHY_CTL1_MDICD || ret & PHY_CTL1_MDIAB) + phydev->mdix_ctrl = ETH_TP_MDI_X; + else + phydev->mdix_ctrl = ETH_TP_MDI; + + ret = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, PHY_PMA_MGBT_POLARITY); + if (ret < 0) + return ret; + + if ((ret & PHY_MDI_MDI_X_MASK) < PHY_MDI_MDI_X_NORMAL) + phydev->mdix = ETH_TP_MDI_X; + else + phydev->mdix = ETH_TP_MDI; + + return 0; +} + static int gpy_update_interface(struct phy_device *phydev) { int ret; @@ -421,7 +491,7 @@ static int gpy_update_interface(struct phy_device *phydev) return ret; } - return 0; + return gpy_update_mdix(phydev); } static int gpy_read_status(struct phy_device *phydev) From bd456f283b66704920fae8e655ebc769cb743420 Mon Sep 17 00:00:00 2001 From: Mubashir Adnan Qureshi Date: Wed, 26 Oct 2022 13:51:11 +0000 Subject: [PATCH 0434/2002] tcp: add sysctls for TCP PLB parameters PLB (Protective Load Balancing) is a host based mechanism for load balancing across switch links. It leverages congestion signals(e.g. ECN) from transport layer to randomly change the path of the connection experiencing congestion. PLB changes the path of the connection by changing the outgoing IPv6 flow label for IPv6 connections (implemented in Linux by calling sk_rethink_txhash()). Because of this implementation mechanism, PLB can currently only work for IPv6 traffic. For more information, see the SIGCOMM 2022 paper: https://doi.org/10.1145/3544216.3544226 This commit adds new sysctl knobs and sets their default values for TCP PLB. Signed-off-by: Mubashir Adnan Qureshi Signed-off-by: Yuchung Cheng Signed-off-by: Neal Cardwell Reviewed-by: Eric Dumazet Signed-off-by: David S. Miller --- Documentation/networking/ip-sysctl.rst | 75 ++++++++++++++++++++++++++ include/net/netns/ipv4.h | 5 ++ net/ipv4/sysctl_net_ipv4.c | 43 +++++++++++++++ net/ipv4/tcp_ipv4.c | 8 +++ 4 files changed, 131 insertions(+) diff --git a/Documentation/networking/ip-sysctl.rst b/Documentation/networking/ip-sysctl.rst index e7b3fa7bb3f7..815efc89ad73 100644 --- a/Documentation/networking/ip-sysctl.rst +++ b/Documentation/networking/ip-sysctl.rst @@ -1069,6 +1069,81 @@ tcp_child_ehash_entries - INTEGER Default: 0 +tcp_plb_enabled - BOOLEAN + If set and the underlying congestion control (e.g. DCTCP) supports + and enables PLB feature, TCP PLB (Protective Load Balancing) is + enabled. PLB is described in the following paper: + https://doi.org/10.1145/3544216.3544226. Based on PLB parameters, + upon sensing sustained congestion, TCP triggers a change in + flow label field for outgoing IPv6 packets. A change in flow label + field potentially changes the path of outgoing packets for switches + that use ECMP/WCMP for routing. + + PLB changes socket txhash which results in a change in IPv6 Flow Label + field, and currently no-op for IPv4 headers. It is possible + to apply PLB for IPv4 with other network header fields (e.g. TCP + or IPv4 options) or using encapsulation where outer header is used + by switches to determine next hop. In either case, further host + and switch side changes will be needed. + + When set, PLB assumes that congestion signal (e.g. ECN) is made + available and used by congestion control module to estimate a + congestion measure (e.g. ce_ratio). PLB needs a congestion measure to + make repathing decisions. + + Default: FALSE + +tcp_plb_idle_rehash_rounds - INTEGER + Number of consecutive congested rounds (RTT) seen after which + a rehash can be performed, given there are no packets in flight. + This is referred to as M in PLB paper: + https://doi.org/10.1145/3544216.3544226. + + Possible Values: 0 - 31 + + Default: 3 + +tcp_plb_rehash_rounds - INTEGER + Number of consecutive congested rounds (RTT) seen after which + a forced rehash can be performed. Be careful when setting this + parameter, as a small value increases the risk of retransmissions. + This is referred to as N in PLB paper: + https://doi.org/10.1145/3544216.3544226. + + Possible Values: 0 - 31 + + Default: 12 + +tcp_plb_suspend_rto_sec - INTEGER + Time, in seconds, to suspend PLB in event of an RTO. In order to avoid + having PLB repath onto a connectivity "black hole", after an RTO a TCP + connection suspends PLB repathing for a random duration between 1x and + 2x of this parameter. Randomness is added to avoid concurrent rehashing + of multiple TCP connections. This should be set corresponding to the + amount of time it takes to repair a failed link. + + Possible Values: 0 - 255 + + Default: 60 + +tcp_plb_cong_thresh - INTEGER + Fraction of packets marked with congestion over a round (RTT) to + tag that round as congested. This is referred to as K in the PLB paper: + https://doi.org/10.1145/3544216.3544226. + + The 0-1 fraction range is mapped to 0-256 range to avoid floating + point operations. For example, 128 means that if at least 50% of + the packets in a round were marked as congested then the round + will be tagged as congested. + + Setting threshold to 0 means that PLB repaths every RTT regardless + of congestion. This is not intended behavior for PLB and should be + used only for experimentation purpose. + + Possible Values: 0 - 256 + + Default: 128 + UDP variables ============= diff --git a/include/net/netns/ipv4.h b/include/net/netns/ipv4.h index 1b8004679445..25f90bba4889 100644 --- a/include/net/netns/ipv4.h +++ b/include/net/netns/ipv4.h @@ -183,6 +183,11 @@ struct netns_ipv4 { unsigned long tfo_active_disable_stamp; u32 tcp_challenge_timestamp; u32 tcp_challenge_count; + u8 sysctl_tcp_plb_enabled; + u8 sysctl_tcp_plb_idle_rehash_rounds; + u8 sysctl_tcp_plb_rehash_rounds; + u8 sysctl_tcp_plb_suspend_rto_sec; + int sysctl_tcp_plb_cong_thresh; int sysctl_udp_wmem_min; int sysctl_udp_rmem_min; diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c index 9b8a6db7a66b..0af28cedd071 100644 --- a/net/ipv4/sysctl_net_ipv4.c +++ b/net/ipv4/sysctl_net_ipv4.c @@ -40,6 +40,8 @@ static int one_day_secs = 24 * 3600; static u32 fib_multipath_hash_fields_all_mask __maybe_unused = FIB_MULTIPATH_HASH_FIELD_ALL_MASK; static unsigned int tcp_child_ehash_entries_max = 16 * 1024 * 1024; +static int tcp_plb_max_rounds = 31; +static int tcp_plb_max_cong_thresh = 256; /* obsolete */ static int sysctl_tcp_low_latency __read_mostly; @@ -1384,6 +1386,47 @@ static struct ctl_table ipv4_net_table[] = { .extra1 = SYSCTL_ZERO, .extra2 = SYSCTL_TWO, }, + { + .procname = "tcp_plb_enabled", + .data = &init_net.ipv4.sysctl_tcp_plb_enabled, + .maxlen = sizeof(u8), + .mode = 0644, + .proc_handler = proc_dou8vec_minmax, + .extra1 = SYSCTL_ZERO, + .extra2 = SYSCTL_ONE, + }, + { + .procname = "tcp_plb_idle_rehash_rounds", + .data = &init_net.ipv4.sysctl_tcp_plb_idle_rehash_rounds, + .maxlen = sizeof(u8), + .mode = 0644, + .proc_handler = proc_dou8vec_minmax, + .extra2 = &tcp_plb_max_rounds, + }, + { + .procname = "tcp_plb_rehash_rounds", + .data = &init_net.ipv4.sysctl_tcp_plb_rehash_rounds, + .maxlen = sizeof(u8), + .mode = 0644, + .proc_handler = proc_dou8vec_minmax, + .extra2 = &tcp_plb_max_rounds, + }, + { + .procname = "tcp_plb_suspend_rto_sec", + .data = &init_net.ipv4.sysctl_tcp_plb_suspend_rto_sec, + .maxlen = sizeof(u8), + .mode = 0644, + .proc_handler = proc_dou8vec_minmax, + }, + { + .procname = "tcp_plb_cong_thresh", + .data = &init_net.ipv4.sysctl_tcp_plb_cong_thresh, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec_minmax, + .extra1 = SYSCTL_ZERO, + .extra2 = &tcp_plb_max_cong_thresh, + }, { } }; diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 87d440f47a70..58b838b56c7f 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -3218,6 +3218,14 @@ static int __net_init tcp_sk_init(struct net *net) net->ipv4.sysctl_tcp_fastopen_blackhole_timeout = 0; atomic_set(&net->ipv4.tfo_active_disable_times, 0); + /* Set default values for PLB */ + net->ipv4.sysctl_tcp_plb_enabled = 0; /* Disabled by default */ + net->ipv4.sysctl_tcp_plb_idle_rehash_rounds = 3; + net->ipv4.sysctl_tcp_plb_rehash_rounds = 12; + net->ipv4.sysctl_tcp_plb_suspend_rto_sec = 60; + /* Default congestion threshold for PLB to mark a round is 50% */ + net->ipv4.sysctl_tcp_plb_cong_thresh = 128; + /* Reno is always built in */ if (!net_eq(net, &init_net) && bpf_try_module_get(init_net.ipv4.tcp_congestion_control, From 1a91bb7c3ebf95e908ec33220defbcda1ecc072f Mon Sep 17 00:00:00 2001 From: Mubashir Adnan Qureshi Date: Wed, 26 Oct 2022 13:51:12 +0000 Subject: [PATCH 0435/2002] tcp: add PLB functionality for TCP Congestion control algorithms track PLB state and cause the connection to trigger a path change when either of the 2 conditions is satisfied: - No packets are in flight and (# consecutive congested rounds >= sysctl_tcp_plb_idle_rehash_rounds) - (# consecutive congested rounds >= sysctl_tcp_plb_rehash_rounds) A round (RTT) is marked as congested when congestion signal (ECN ce_ratio) over an RTT is greater than sysctl_tcp_plb_cong_thresh. In the event of RTO, PLB (via tcp_write_timeout()) triggers a path change and disables congestion-triggered path changes for random time between (sysctl_tcp_plb_suspend_rto_sec, 2*sysctl_tcp_plb_suspend_rto_sec) to avoid hopping onto the "connectivity blackhole". RTO-triggered path changes can still happen during this cool-off period. Signed-off-by: Mubashir Adnan Qureshi Signed-off-by: Yuchung Cheng Signed-off-by: Neal Cardwell Reviewed-by: Eric Dumazet Signed-off-by: David S. Miller --- include/net/tcp.h | 28 ++++++++++++ net/ipv4/Makefile | 2 +- net/ipv4/tcp_ipv4.c | 2 +- net/ipv4/tcp_plb.c | 107 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 137 insertions(+), 2 deletions(-) create mode 100644 net/ipv4/tcp_plb.c diff --git a/include/net/tcp.h b/include/net/tcp.h index 14d45661a84d..6b814e788f00 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -2140,6 +2140,34 @@ extern void tcp_rack_advance(struct tcp_sock *tp, u8 sacked, u32 end_seq, extern void tcp_rack_reo_timeout(struct sock *sk); extern void tcp_rack_update_reo_wnd(struct sock *sk, struct rate_sample *rs); +/* tcp_plb.c */ + +/* + * Scaling factor for fractions in PLB. For example, tcp_plb_update_state + * expects cong_ratio which represents fraction of traffic that experienced + * congestion over a single RTT. In order to avoid floating point operations, + * this fraction should be mapped to (1 << TCP_PLB_SCALE) and passed in. + */ +#define TCP_PLB_SCALE 8 + +/* State for PLB (Protective Load Balancing) for a single TCP connection. */ +struct tcp_plb_state { + u8 consec_cong_rounds:5, /* consecutive congested rounds */ + unused:3; + u32 pause_until; /* jiffies32 when PLB can resume rerouting */ +}; + +static inline void tcp_plb_init(const struct sock *sk, + struct tcp_plb_state *plb) +{ + plb->consec_cong_rounds = 0; + plb->pause_until = 0; +} +void tcp_plb_update_state(const struct sock *sk, struct tcp_plb_state *plb, + const int cong_ratio); +void tcp_plb_check_rehash(struct sock *sk, struct tcp_plb_state *plb); +void tcp_plb_update_state_upon_rto(struct sock *sk, struct tcp_plb_state *plb); + /* At how many usecs into the future should the RTO fire? */ static inline s64 tcp_rto_delta_us(const struct sock *sk) { diff --git a/net/ipv4/Makefile b/net/ipv4/Makefile index bbdd9c44f14e..af7d2cf490fb 100644 --- a/net/ipv4/Makefile +++ b/net/ipv4/Makefile @@ -10,7 +10,7 @@ obj-y := route.o inetpeer.o protocol.o \ tcp.o tcp_input.o tcp_output.o tcp_timer.o tcp_ipv4.o \ tcp_minisocks.o tcp_cong.o tcp_metrics.o tcp_fastopen.o \ tcp_rate.o tcp_recovery.o tcp_ulp.o \ - tcp_offload.o datagram.o raw.o udp.o udplite.o \ + tcp_offload.o tcp_plb.o datagram.o raw.o udp.o udplite.o \ udp_offload.o arp.o icmp.o devinet.o af_inet.o igmp.o \ fib_frontend.o fib_semantics.o fib_trie.o fib_notifier.o \ inet_fragment.o ping.o ip_tunnel_core.o gre_offload.o \ diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 58b838b56c7f..ebab9e8b184c 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -3224,7 +3224,7 @@ static int __net_init tcp_sk_init(struct net *net) net->ipv4.sysctl_tcp_plb_rehash_rounds = 12; net->ipv4.sysctl_tcp_plb_suspend_rto_sec = 60; /* Default congestion threshold for PLB to mark a round is 50% */ - net->ipv4.sysctl_tcp_plb_cong_thresh = 128; + net->ipv4.sysctl_tcp_plb_cong_thresh = (1 << TCP_PLB_SCALE) / 2; /* Reno is always built in */ if (!net_eq(net, &init_net) && diff --git a/net/ipv4/tcp_plb.c b/net/ipv4/tcp_plb.c new file mode 100644 index 000000000000..f4ced370acad --- /dev/null +++ b/net/ipv4/tcp_plb.c @@ -0,0 +1,107 @@ +/* Protective Load Balancing (PLB) + * + * PLB was designed to reduce link load imbalance across datacenter + * switches. PLB is a host-based optimization; it leverages congestion + * signals from the transport layer to randomly change the path of the + * connection experiencing sustained congestion. PLB prefers to repath + * after idle periods to minimize packet reordering. It repaths by + * changing the IPv6 Flow Label on the packets of a connection, which + * datacenter switches include as part of ECMP/WCMP hashing. + * + * PLB is described in detail in: + * + * Mubashir Adnan Qureshi, Yuchung Cheng, Qianwen Yin, Qiaobin Fu, + * Gautam Kumar, Masoud Moshref, Junhua Yan, Van Jacobson, + * David Wetherall,Abdul Kabbani: + * "PLB: Congestion Signals are Simple and Effective for + * Network Load Balancing" + * In ACM SIGCOMM 2022, Amsterdam Netherlands. + * + */ + +#include + +/* Called once per round-trip to update PLB state for a connection. */ +void tcp_plb_update_state(const struct sock *sk, struct tcp_plb_state *plb, + const int cong_ratio) +{ + struct net *net = sock_net(sk); + + if (!READ_ONCE(net->ipv4.sysctl_tcp_plb_enabled)) + return; + + if (cong_ratio >= 0) { + if (cong_ratio < READ_ONCE(net->ipv4.sysctl_tcp_plb_cong_thresh)) + plb->consec_cong_rounds = 0; + else if (plb->consec_cong_rounds < + READ_ONCE(net->ipv4.sysctl_tcp_plb_rehash_rounds)) + plb->consec_cong_rounds++; + } +} +EXPORT_SYMBOL_GPL(tcp_plb_update_state); + +/* Check whether recent congestion has been persistent enough to warrant + * a load balancing decision that switches the connection to another path. + */ +void tcp_plb_check_rehash(struct sock *sk, struct tcp_plb_state *plb) +{ + struct net *net = sock_net(sk); + u32 max_suspend; + bool forced_rehash = false, idle_rehash = false; + + if (!READ_ONCE(net->ipv4.sysctl_tcp_plb_enabled)) + return; + + forced_rehash = plb->consec_cong_rounds >= + READ_ONCE(net->ipv4.sysctl_tcp_plb_rehash_rounds); + /* If sender goes idle then we check whether to rehash. */ + idle_rehash = READ_ONCE(net->ipv4.sysctl_tcp_plb_idle_rehash_rounds) && + !tcp_sk(sk)->packets_out && + plb->consec_cong_rounds >= + READ_ONCE(net->ipv4.sysctl_tcp_plb_idle_rehash_rounds); + + if (!forced_rehash && !idle_rehash) + return; + + /* Note that tcp_jiffies32 can wrap; we detect wraps by checking for + * cases where the max suspension end is before the actual suspension + * end. We clear pause_until to 0 to indicate there is no recent + * RTO event that constrains PLB rehashing. + */ + max_suspend = 2 * READ_ONCE(net->ipv4.sysctl_tcp_plb_suspend_rto_sec) * HZ; + if (plb->pause_until && + (!before(tcp_jiffies32, plb->pause_until) || + before(tcp_jiffies32 + max_suspend, plb->pause_until))) + plb->pause_until = 0; + + if (plb->pause_until) + return; + + sk_rethink_txhash(sk); + plb->consec_cong_rounds = 0; +} +EXPORT_SYMBOL_GPL(tcp_plb_check_rehash); + +/* Upon RTO, disallow load balancing for a while, to avoid having load + * balancing decisions switch traffic to a black-holed path that was + * previously avoided with a sk_rethink_txhash() call at RTO time. + */ +void tcp_plb_update_state_upon_rto(struct sock *sk, struct tcp_plb_state *plb) +{ + struct net *net = sock_net(sk); + u32 pause; + + if (!READ_ONCE(net->ipv4.sysctl_tcp_plb_enabled)) + return; + + pause = READ_ONCE(net->ipv4.sysctl_tcp_plb_suspend_rto_sec) * HZ; + pause += prandom_u32_max(pause); + plb->pause_until = tcp_jiffies32 + pause; + + /* Reset PLB state upon RTO, since an RTO causes a sk_rethink_txhash() call + * that may switch this connection to a path with completely different + * congestion characteristics. + */ + plb->consec_cong_rounds = 0; +} +EXPORT_SYMBOL_GPL(tcp_plb_update_state_upon_rto); From c30f8e0b048087ffbf20e51a4be160f7ec279ff6 Mon Sep 17 00:00:00 2001 From: Mubashir Adnan Qureshi Date: Wed, 26 Oct 2022 13:51:13 +0000 Subject: [PATCH 0436/2002] tcp: add support for PLB in DCTCP PLB support is added to TCP DCTCP code. As DCTCP uses ECN as the congestion signal, PLB also uses ECN to make decisions whether to change the path or not upon sustained congestion. Signed-off-by: Mubashir Adnan Qureshi Signed-off-by: Yuchung Cheng Signed-off-by: Neal Cardwell Reviewed-by: Eric Dumazet Signed-off-by: David S. Miller --- net/ipv4/tcp_dctcp.c | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/net/ipv4/tcp_dctcp.c b/net/ipv4/tcp_dctcp.c index 2a6c0dd665a4..e0a2ca7456ff 100644 --- a/net/ipv4/tcp_dctcp.c +++ b/net/ipv4/tcp_dctcp.c @@ -54,6 +54,7 @@ struct dctcp { u32 next_seq; u32 ce_state; u32 loss_cwnd; + struct tcp_plb_state plb; }; static unsigned int dctcp_shift_g __read_mostly = 4; /* g = 1/2^4 */ @@ -91,6 +92,8 @@ static void dctcp_init(struct sock *sk) ca->ce_state = 0; dctcp_reset(tp, ca); + tcp_plb_init(sk, &ca->plb); + return; } @@ -117,14 +120,28 @@ static void dctcp_update_alpha(struct sock *sk, u32 flags) /* Expired RTT */ if (!before(tp->snd_una, ca->next_seq)) { + u32 delivered = tp->delivered - ca->old_delivered; u32 delivered_ce = tp->delivered_ce - ca->old_delivered_ce; u32 alpha = ca->dctcp_alpha; + u32 ce_ratio = 0; + + if (delivered > 0) { + /* dctcp_alpha keeps EWMA of fraction of ECN marked + * packets. Because of EWMA smoothing, PLB reaction can + * be slow so we use ce_ratio which is an instantaneous + * measure of congestion. ce_ratio is the fraction of + * ECN marked packets in the previous RTT. + */ + if (delivered_ce > 0) + ce_ratio = (delivered_ce << TCP_PLB_SCALE) / delivered; + tcp_plb_update_state(sk, &ca->plb, (int)ce_ratio); + tcp_plb_check_rehash(sk, &ca->plb); + } /* alpha = (1 - g) * alpha + g * F */ alpha -= min_not_zero(alpha, alpha >> dctcp_shift_g); if (delivered_ce) { - u32 delivered = tp->delivered - ca->old_delivered; /* If dctcp_shift_g == 1, a 32bit value would overflow * after 8 M packets. @@ -172,8 +189,12 @@ static void dctcp_cwnd_event(struct sock *sk, enum tcp_ca_event ev) dctcp_ece_ack_update(sk, ev, &ca->prior_rcv_nxt, &ca->ce_state); break; case CA_EVENT_LOSS: + tcp_plb_update_state_upon_rto(sk, &ca->plb); dctcp_react_to_loss(sk); break; + case CA_EVENT_TX_START: + tcp_plb_check_rehash(sk, &ca->plb); /* Maybe rehash when inflight is 0 */ + break; default: /* Don't care for the rest. */ break; From 29c1c44646aec5d5134f2365259a84becc1ee7d3 Mon Sep 17 00:00:00 2001 From: Mubashir Adnan Qureshi Date: Wed, 26 Oct 2022 13:51:14 +0000 Subject: [PATCH 0437/2002] tcp: add u32 counter in tcp_sock and an SNMP counter for PLB A u32 counter is added to tcp_sock for counting the number of PLB triggered rehashes for a TCP connection. An SNMP counter is also added to count overall PLB triggered rehash events for a host. These counters are hooked up to PLB implementation for DCTCP. TCP_NLA_REHASH is added to SCM_TIMESTAMPING_OPT_STATS that reports the rehash attempts triggered due to PLB or timeouts. This gives a historical view of sustained congestion or timeouts experienced by the TCP connection. Signed-off-by: Mubashir Adnan Qureshi Signed-off-by: Yuchung Cheng Signed-off-by: Neal Cardwell Reviewed-by: Eric Dumazet Signed-off-by: David S. Miller --- include/linux/tcp.h | 1 + include/uapi/linux/snmp.h | 1 + include/uapi/linux/tcp.h | 1 + net/ipv4/proc.c | 1 + net/ipv4/tcp.c | 3 +++ net/ipv4/tcp_plb.c | 2 ++ 6 files changed, 9 insertions(+) diff --git a/include/linux/tcp.h b/include/linux/tcp.h index 41b1da621a45..ca7f05a130d2 100644 --- a/include/linux/tcp.h +++ b/include/linux/tcp.h @@ -423,6 +423,7 @@ struct tcp_sock { u32 probe_seq_start; u32 probe_seq_end; } mtu_probe; + u32 plb_rehash; /* PLB-triggered rehash attempts */ u32 mtu_info; /* We received an ICMP_FRAG_NEEDED / ICMPV6_PKT_TOOBIG * while socket was owned by user. */ diff --git a/include/uapi/linux/snmp.h b/include/uapi/linux/snmp.h index 4d7470036a8b..6600cb0164c2 100644 --- a/include/uapi/linux/snmp.h +++ b/include/uapi/linux/snmp.h @@ -292,6 +292,7 @@ enum LINUX_MIB_TCPDSACKIGNOREDDUBIOUS, /* TCPDSACKIgnoredDubious */ LINUX_MIB_TCPMIGRATEREQSUCCESS, /* TCPMigrateReqSuccess */ LINUX_MIB_TCPMIGRATEREQFAILURE, /* TCPMigrateReqFailure */ + LINUX_MIB_TCPPLBREHASH, /* TCPPLBRehash */ __LINUX_MIB_MAX }; diff --git a/include/uapi/linux/tcp.h b/include/uapi/linux/tcp.h index 8fc09e8638b3..c9abe86eda5f 100644 --- a/include/uapi/linux/tcp.h +++ b/include/uapi/linux/tcp.h @@ -315,6 +315,7 @@ enum { TCP_NLA_BYTES_NOTSENT, /* Bytes in write queue not yet sent */ TCP_NLA_EDT, /* Earliest departure time (CLOCK_MONOTONIC) */ TCP_NLA_TTL, /* TTL or hop limit of a packet received */ + TCP_NLA_REHASH, /* PLB and timeout triggered rehash attempts */ }; /* for TCP_MD5SIG socket option */ diff --git a/net/ipv4/proc.c b/net/ipv4/proc.c index 5386f460bd20..f88daace9de3 100644 --- a/net/ipv4/proc.c +++ b/net/ipv4/proc.c @@ -297,6 +297,7 @@ static const struct snmp_mib snmp4_net_list[] = { SNMP_MIB_ITEM("TCPDSACKIgnoredDubious", LINUX_MIB_TCPDSACKIGNOREDDUBIOUS), SNMP_MIB_ITEM("TCPMigrateReqSuccess", LINUX_MIB_TCPMIGRATEREQSUCCESS), SNMP_MIB_ITEM("TCPMigrateReqFailure", LINUX_MIB_TCPMIGRATEREQFAILURE), + SNMP_MIB_ITEM("TCPPLBRehash", LINUX_MIB_TCPPLBREHASH), SNMP_MIB_SENTINEL }; diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index ef14efa1fb70..1da7c53b6cb5 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -3176,6 +3176,7 @@ int tcp_disconnect(struct sock *sk, int flags) tp->sacked_out = 0; tp->tlp_high_seq = 0; tp->last_oow_ack_time = 0; + tp->plb_rehash = 0; /* There's a bubble in the pipe until at least the first ACK. */ tp->app_limited = ~0U; tp->rack.mstamp = 0; @@ -3973,6 +3974,7 @@ static size_t tcp_opt_stats_get_size(void) nla_total_size(sizeof(u32)) + /* TCP_NLA_BYTES_NOTSENT */ nla_total_size_64bit(sizeof(u64)) + /* TCP_NLA_EDT */ nla_total_size(sizeof(u8)) + /* TCP_NLA_TTL */ + nla_total_size(sizeof(u32)) + /* TCP_NLA_REHASH */ 0; } @@ -4049,6 +4051,7 @@ struct sk_buff *tcp_get_timestamping_opt_stats(const struct sock *sk, nla_put_u8(stats, TCP_NLA_TTL, tcp_skb_ttl_or_hop_limit(ack_skb)); + nla_put_u32(stats, TCP_NLA_REHASH, tp->plb_rehash + tp->timeout_rehash); return stats; } diff --git a/net/ipv4/tcp_plb.c b/net/ipv4/tcp_plb.c index f4ced370acad..bb1a08fda113 100644 --- a/net/ipv4/tcp_plb.c +++ b/net/ipv4/tcp_plb.c @@ -79,6 +79,8 @@ void tcp_plb_check_rehash(struct sock *sk, struct tcp_plb_state *plb) sk_rethink_txhash(sk); plb->consec_cong_rounds = 0; + tcp_sk(sk)->plb_rehash++; + NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPPLBREHASH); } EXPORT_SYMBOL_GPL(tcp_plb_check_rehash); From 71fc704768f601ed3fa36310822a5e03f310f781 Mon Sep 17 00:00:00 2001 From: Mubashir Adnan Qureshi Date: Wed, 26 Oct 2022 13:51:15 +0000 Subject: [PATCH 0438/2002] tcp: add rcv_wnd and plb_rehash to TCP_INFO rcv_wnd can be useful to diagnose TCP performance where receiver window becomes the bottleneck. rehash reports the PLB and timeout triggered rehash attempts by the TCP connection. Signed-off-by: Mubashir Adnan Qureshi Signed-off-by: Yuchung Cheng Signed-off-by: Neal Cardwell Reviewed-by: Eric Dumazet Signed-off-by: David S. Miller --- include/uapi/linux/tcp.h | 5 +++++ net/ipv4/tcp.c | 2 ++ 2 files changed, 7 insertions(+) diff --git a/include/uapi/linux/tcp.h b/include/uapi/linux/tcp.h index c9abe86eda5f..879eeb0a084b 100644 --- a/include/uapi/linux/tcp.h +++ b/include/uapi/linux/tcp.h @@ -284,6 +284,11 @@ struct tcp_info { __u32 tcpi_snd_wnd; /* peer's advertised receive window after * scaling (bytes) */ + __u32 tcpi_rcv_wnd; /* local advertised receive window after + * scaling (bytes) + */ + + __u32 tcpi_rehash; /* PLB or timeout triggered rehash attempts */ }; /* netlink attributes types for SCM_TIMESTAMPING_OPT_STATS */ diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index 1da7c53b6cb5..de8f0cd7cb32 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -3940,6 +3940,8 @@ void tcp_get_info(struct sock *sk, struct tcp_info *info) info->tcpi_reord_seen = tp->reord_seen; info->tcpi_rcv_ooopack = tp->rcv_ooopack; info->tcpi_snd_wnd = tp->snd_wnd; + info->tcpi_rcv_wnd = tp->rcv_wnd; + info->tcpi_rehash = tp->plb_rehash + tp->timeout_rehash; info->tcpi_fastopen_client_fail = tp->fastopen_client_fail; unlock_sock_fast(sk, slow); } From a34b3e6ed8fbf668f995abdfd410784c695b6d03 Mon Sep 17 00:00:00 2001 From: Jiawen Wu Date: Thu, 27 Oct 2022 14:11:14 +0800 Subject: [PATCH 0439/2002] net: txgbe: Store PCI info Get PCI config space info, set LAN id and check flash status. Signed-off-by: Jiawen Wu Signed-off-by: David S. Miller --- drivers/net/ethernet/wangxun/Kconfig | 6 ++ drivers/net/ethernet/wangxun/Makefile | 1 + drivers/net/ethernet/wangxun/libwx/Makefile | 7 ++ drivers/net/ethernet/wangxun/libwx/wx_hw.c | 88 +++++++++++++++ drivers/net/ethernet/wangxun/libwx/wx_hw.h | 10 ++ drivers/net/ethernet/wangxun/libwx/wx_type.h | 54 ++++++++++ drivers/net/ethernet/wangxun/txgbe/txgbe.h | 4 + .../net/ethernet/wangxun/txgbe/txgbe_main.c | 101 +++++++++++++++++- .../net/ethernet/wangxun/txgbe/txgbe_type.h | 19 ++-- 9 files changed, 280 insertions(+), 10 deletions(-) create mode 100644 drivers/net/ethernet/wangxun/libwx/Makefile create mode 100644 drivers/net/ethernet/wangxun/libwx/wx_hw.c create mode 100644 drivers/net/ethernet/wangxun/libwx/wx_hw.h create mode 100644 drivers/net/ethernet/wangxun/libwx/wx_type.h diff --git a/drivers/net/ethernet/wangxun/Kconfig b/drivers/net/ethernet/wangxun/Kconfig index f5d43d8c9629..565fa826b056 100644 --- a/drivers/net/ethernet/wangxun/Kconfig +++ b/drivers/net/ethernet/wangxun/Kconfig @@ -16,6 +16,11 @@ config NET_VENDOR_WANGXUN if NET_VENDOR_WANGXUN +config LIBWX + tristate + help + Common library for Wangxun(R) Ethernet drivers. + config NGBE tristate "Wangxun(R) GbE PCI Express adapters support" depends on PCI @@ -32,6 +37,7 @@ config NGBE config TXGBE tristate "Wangxun(R) 10GbE PCI Express adapters support" depends on PCI + select LIBWX help This driver supports Wangxun(R) 10GbE PCI Express family of adapters. diff --git a/drivers/net/ethernet/wangxun/Makefile b/drivers/net/ethernet/wangxun/Makefile index ac3fb06b233c..ca19311dbe38 100644 --- a/drivers/net/ethernet/wangxun/Makefile +++ b/drivers/net/ethernet/wangxun/Makefile @@ -3,5 +3,6 @@ # Makefile for the Wangxun network device drivers. # +obj-$(CONFIG_LIBWX) += libwx/ obj-$(CONFIG_TXGBE) += txgbe/ obj-$(CONFIG_NGBE) += ngbe/ diff --git a/drivers/net/ethernet/wangxun/libwx/Makefile b/drivers/net/ethernet/wangxun/libwx/Makefile new file mode 100644 index 000000000000..1ed5e23af944 --- /dev/null +++ b/drivers/net/ethernet/wangxun/libwx/Makefile @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: GPL-2.0 +# Copyright (c) 2015 - 2022 Beijing WangXun Technology Co., Ltd. +# + +obj-$(CONFIG_LIBWX) += libwx.o + +libwx-objs := wx_hw.o diff --git a/drivers/net/ethernet/wangxun/libwx/wx_hw.c b/drivers/net/ethernet/wangxun/libwx/wx_hw.c new file mode 100644 index 000000000000..fed51c2f3071 --- /dev/null +++ b/drivers/net/ethernet/wangxun/libwx/wx_hw.c @@ -0,0 +1,88 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2015 - 2022 Beijing WangXun Technology Co., Ltd. */ + +#include +#include + +#include "wx_type.h" +#include "wx_hw.h" + +/* cmd_addr is used for some special command: + * 1. to be sector address, when implemented erase sector command + * 2. to be flash address when implemented read, write flash address + */ +static int wx_fmgr_cmd_op(struct wx_hw *wxhw, u32 cmd, u32 cmd_addr) +{ + u32 cmd_val = 0, val = 0; + + cmd_val = WX_SPI_CMD_CMD(cmd) | + WX_SPI_CMD_CLK(WX_SPI_CLK_DIV) | + cmd_addr; + wr32(wxhw, WX_SPI_CMD, cmd_val); + + return read_poll_timeout(rd32, val, (val & 0x1), 10, 100000, + false, wxhw, WX_SPI_STATUS); +} + +static int wx_flash_read_dword(struct wx_hw *wxhw, u32 addr, u32 *data) +{ + int ret = 0; + + ret = wx_fmgr_cmd_op(wxhw, WX_SPI_CMD_READ_DWORD, addr); + if (ret < 0) + return ret; + + *data = rd32(wxhw, WX_SPI_DATA); + + return ret; +} + +int wx_check_flash_load(struct wx_hw *hw, u32 check_bit) +{ + u32 reg = 0; + int err = 0; + + /* if there's flash existing */ + if (!(rd32(hw, WX_SPI_STATUS) & + WX_SPI_STATUS_FLASH_BYPASS)) { + /* wait hw load flash done */ + err = read_poll_timeout(rd32, reg, !(reg & check_bit), 20000, 2000000, + false, hw, WX_SPI_ILDR_STATUS); + if (err < 0) + wx_err(hw, "Check flash load timeout.\n"); + } + + return err; +} +EXPORT_SYMBOL(wx_check_flash_load); + +int wx_sw_init(struct wx_hw *wxhw) +{ + struct pci_dev *pdev = wxhw->pdev; + u32 ssid = 0; + int err = 0; + + wxhw->vendor_id = pdev->vendor; + wxhw->device_id = pdev->device; + wxhw->revision_id = pdev->revision; + wxhw->oem_svid = pdev->subsystem_vendor; + wxhw->oem_ssid = pdev->subsystem_device; + wxhw->bus.device = PCI_SLOT(pdev->devfn); + wxhw->bus.func = PCI_FUNC(pdev->devfn); + + if (wxhw->oem_svid == PCI_VENDOR_ID_WANGXUN) { + wxhw->subsystem_vendor_id = pdev->subsystem_vendor; + wxhw->subsystem_device_id = pdev->subsystem_device; + } else { + err = wx_flash_read_dword(wxhw, 0xfffdc, &ssid); + if (!err) + wxhw->subsystem_device_id = swab16((u16)ssid); + + return err; + } + + return 0; +} +EXPORT_SYMBOL(wx_sw_init); + +MODULE_LICENSE("GPL"); diff --git a/drivers/net/ethernet/wangxun/libwx/wx_hw.h b/drivers/net/ethernet/wangxun/libwx/wx_hw.h new file mode 100644 index 000000000000..42e95283242c --- /dev/null +++ b/drivers/net/ethernet/wangxun/libwx/wx_hw.h @@ -0,0 +1,10 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (c) 2015 - 2022 Beijing WangXun Technology Co., Ltd. */ + +#ifndef _WX_HW_H_ +#define _WX_HW_H_ + +int wx_check_flash_load(struct wx_hw *hw, u32 check_bit); +int wx_sw_init(struct wx_hw *wxhw); + +#endif /* _WX_HW_H_ */ diff --git a/drivers/net/ethernet/wangxun/libwx/wx_type.h b/drivers/net/ethernet/wangxun/libwx/wx_type.h new file mode 100644 index 000000000000..1b2d8ad38dba --- /dev/null +++ b/drivers/net/ethernet/wangxun/libwx/wx_type.h @@ -0,0 +1,54 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (c) 2015 - 2022 Beijing WangXun Technology Co., Ltd. */ + +#ifndef _WX_TYPE_H_ +#define _WX_TYPE_H_ + +/* Vendor ID */ +#ifndef PCI_VENDOR_ID_WANGXUN +#define PCI_VENDOR_ID_WANGXUN 0x8088 +#endif + +/* FMGR Registers */ +#define WX_SPI_CMD 0x10104 +#define WX_SPI_CMD_READ_DWORD 0x1 +#define WX_SPI_CLK_DIV 0x3 +#define WX_SPI_CMD_CMD(_v) (((_v) & 0x7) << 28) +#define WX_SPI_CMD_CLK(_v) (((_v) & 0x7) << 25) +#define WX_SPI_CMD_ADDR(_v) (((_v) & 0xFFFFFF)) +#define WX_SPI_DATA 0x10108 +#define WX_SPI_DATA_BYPASS BIT(31) +#define WX_SPI_DATA_STATUS(_v) (((_v) & 0xFF) << 16) +#define WX_SPI_DATA_OP_DONE BIT(0) +#define WX_SPI_STATUS 0x1010C +#define WX_SPI_STATUS_OPDONE BIT(0) +#define WX_SPI_STATUS_FLASH_BYPASS BIT(31) +#define WX_SPI_ILDR_STATUS 0x10120 + +/* Bus parameters */ +struct wx_bus_info { + u8 func; + u16 device; +}; + +struct wx_hw { + u8 __iomem *hw_addr; + struct pci_dev *pdev; + struct wx_bus_info bus; + u16 device_id; + u16 vendor_id; + u16 subsystem_device_id; + u16 subsystem_vendor_id; + u8 revision_id; + u16 oem_ssid; + u16 oem_svid; +}; + +/* register operations */ +#define wr32(a, reg, value) writel((value), ((a)->hw_addr + (reg))) +#define rd32(a, reg) readl((a)->hw_addr + (reg)) + +#define wx_err(wxhw, fmt, arg...) \ + dev_err(&(wxhw)->pdev->dev, fmt, ##arg) + +#endif /* _WX_TYPE_H_ */ diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe.h b/drivers/net/ethernet/wangxun/txgbe/txgbe.h index 38ddbde0ed0f..9a97b85be3ac 100644 --- a/drivers/net/ethernet/wangxun/txgbe/txgbe.h +++ b/drivers/net/ethernet/wangxun/txgbe/txgbe.h @@ -17,6 +17,10 @@ struct txgbe_adapter { /* OS defined structs */ struct net_device *netdev; struct pci_dev *pdev; + + /* structs defined in txgbe_type.h */ + struct txgbe_hw hw; + u16 msg_enable; }; extern char txgbe_driver_name[]; diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c b/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c index d3b9f73ecba4..71ea197fe299 100644 --- a/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c +++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c @@ -9,6 +9,8 @@ #include #include +#include "../libwx/wx_type.h" +#include "../libwx/wx_hw.h" #include "txgbe.h" char txgbe_driver_name[] = "txgbe"; @@ -30,6 +32,69 @@ static const struct pci_device_id txgbe_pci_tbl[] = { #define DEFAULT_DEBUG_LEVEL_SHIFT 3 +static void txgbe_check_minimum_link(struct txgbe_adapter *adapter) +{ + struct pci_dev *pdev; + + pdev = adapter->pdev; + pcie_print_link_status(pdev); +} + +/** + * txgbe_enumerate_functions - Get the number of ports this device has + * @adapter: adapter structure + * + * This function enumerates the phsyical functions co-located on a single slot, + * in order to determine how many ports a device has. This is most useful in + * determining the required GT/s of PCIe bandwidth necessary for optimal + * performance. + **/ +static int txgbe_enumerate_functions(struct txgbe_adapter *adapter) +{ + struct pci_dev *entry, *pdev = adapter->pdev; + int physfns = 0; + + list_for_each_entry(entry, &pdev->bus->devices, bus_list) { + /* When the devices on the bus don't all match our device ID, + * we can't reliably determine the correct number of + * functions. This can occur if a function has been direct + * attached to a virtual machine using VT-d. + */ + if (entry->vendor != pdev->vendor || + entry->device != pdev->device) + return -EINVAL; + + physfns++; + } + + return physfns; +} + +/** + * txgbe_sw_init - Initialize general software structures (struct txgbe_adapter) + * @adapter: board private structure to initialize + **/ +static int txgbe_sw_init(struct txgbe_adapter *adapter) +{ + struct pci_dev *pdev = adapter->pdev; + struct txgbe_hw *hw = &adapter->hw; + struct wx_hw *wxhw = &hw->wxhw; + int err; + + wxhw->hw_addr = adapter->io_addr; + wxhw->pdev = pdev; + + /* PCI config space info */ + err = wx_sw_init(wxhw); + if (err < 0) { + netif_err(adapter, probe, adapter->netdev, + "read of internal subsystem device id failed\n"); + return err; + } + + return 0; +} + static void txgbe_dev_shutdown(struct pci_dev *pdev, bool *enable_wake) { struct txgbe_adapter *adapter = pci_get_drvdata(pdev); @@ -67,8 +132,10 @@ static int txgbe_probe(struct pci_dev *pdev, const struct pci_device_id __always_unused *ent) { struct txgbe_adapter *adapter = NULL; + struct txgbe_hw *hw = NULL; + struct wx_hw *wxhw = NULL; struct net_device *netdev; - int err; + int err, expected_gts; err = pci_enable_device_mem(pdev); if (err) @@ -107,6 +174,9 @@ static int txgbe_probe(struct pci_dev *pdev, adapter = netdev_priv(netdev); adapter->netdev = netdev; adapter->pdev = pdev; + hw = &adapter->hw; + wxhw = &hw->wxhw; + adapter->msg_enable = (1 << DEFAULT_DEBUG_LEVEL_SHIFT) - 1; adapter->io_addr = devm_ioremap(&pdev->dev, pci_resource_start(pdev, 0), @@ -116,10 +186,39 @@ static int txgbe_probe(struct pci_dev *pdev, goto err_pci_release_regions; } + strncpy(netdev->name, pci_name(pdev), sizeof(netdev->name) - 1); + + /* setup the private structure */ + err = txgbe_sw_init(adapter); + if (err) + goto err_pci_release_regions; + + /* check if flash load is done after hw power up */ + err = wx_check_flash_load(wxhw, TXGBE_SPI_ILDR_STATUS_PERST); + if (err) + goto err_pci_release_regions; + err = wx_check_flash_load(wxhw, TXGBE_SPI_ILDR_STATUS_PWRRST); + if (err) + goto err_pci_release_regions; + netdev->features |= NETIF_F_HIGHDMA; pci_set_drvdata(pdev, adapter); + /* calculate the expected PCIe bandwidth required for optimal + * performance. Note that some older parts will never have enough + * bandwidth due to being older generation PCIe parts. We clamp these + * parts to ensure that no warning is displayed, as this could confuse + * users otherwise. + */ + expected_gts = txgbe_enumerate_functions(adapter) * 10; + + /* don't check link if we failed to enumerate functions */ + if (expected_gts > 0) + txgbe_check_minimum_link(adapter); + else + dev_warn(&pdev->dev, "Failed to enumerate PF devices.\n"); + return 0; err_pci_release_regions: diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_type.h b/drivers/net/ethernet/wangxun/txgbe/txgbe_type.h index b2e329f50bae..1d1ead3d3c06 100644 --- a/drivers/net/ethernet/wangxun/txgbe/txgbe_type.h +++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_type.h @@ -4,15 +4,6 @@ #ifndef _TXGBE_TYPE_H_ #define _TXGBE_TYPE_H_ -#include -#include - -/************ txgbe_register.h ************/ -/* Vendor ID */ -#ifndef PCI_VENDOR_ID_WANGXUN -#define PCI_VENDOR_ID_WANGXUN 0x8088 -#endif - /* Device IDs */ #define TXGBE_DEV_ID_SP1000 0x1001 #define TXGBE_DEV_ID_WX1820 0x2001 @@ -54,4 +45,14 @@ /* Revision ID */ #define TXGBE_SP_MPW 1 +/**************** SP Registers ****************************/ +/* FMGR Registers */ +#define TXGBE_SPI_ILDR_STATUS 0x10120 +#define TXGBE_SPI_ILDR_STATUS_PERST BIT(0) /* PCIE_PERST is done */ +#define TXGBE_SPI_ILDR_STATUS_PWRRST BIT(1) /* Power on reset is done */ + +struct txgbe_hw { + struct wx_hw wxhw; +}; + #endif /* _TXGBE_TYPE_H_ */ From b08012568ebb2098015dac3fcde78a20c09d838e Mon Sep 17 00:00:00 2001 From: Jiawen Wu Date: Thu, 27 Oct 2022 14:11:15 +0800 Subject: [PATCH 0440/2002] net: txgbe: Reset hardware Reset and initialize the hardware by configuring the MAC layer. Signed-off-by: Jiawen Wu Signed-off-by: David S. Miller --- drivers/net/ethernet/wangxun/libwx/wx_hw.c | 160 ++++++++++++++++++ drivers/net/ethernet/wangxun/libwx/wx_hw.h | 2 + drivers/net/ethernet/wangxun/libwx/wx_type.h | 146 ++++++++++++++++ drivers/net/ethernet/wangxun/txgbe/Makefile | 3 +- drivers/net/ethernet/wangxun/txgbe/txgbe.h | 5 +- drivers/net/ethernet/wangxun/txgbe/txgbe_hw.c | 86 ++++++++++ drivers/net/ethernet/wangxun/txgbe/txgbe_hw.h | 9 + .../net/ethernet/wangxun/txgbe/txgbe_main.c | 21 +++ .../net/ethernet/wangxun/txgbe/txgbe_type.h | 11 +- 9 files changed, 434 insertions(+), 9 deletions(-) create mode 100644 drivers/net/ethernet/wangxun/txgbe/txgbe_hw.c create mode 100644 drivers/net/ethernet/wangxun/txgbe/txgbe_hw.h diff --git a/drivers/net/ethernet/wangxun/libwx/wx_hw.c b/drivers/net/ethernet/wangxun/libwx/wx_hw.c index fed51c2f3071..6e2a2d251a09 100644 --- a/drivers/net/ethernet/wangxun/libwx/wx_hw.c +++ b/drivers/net/ethernet/wangxun/libwx/wx_hw.c @@ -7,6 +7,21 @@ #include "wx_type.h" #include "wx_hw.h" +static void wx_intr_disable(struct wx_hw *wxhw, u64 qmask) +{ + u32 mask; + + mask = (qmask & 0xFFFFFFFF); + if (mask) + wr32(wxhw, WX_PX_IMS(0), mask); + + if (wxhw->mac.type == wx_mac_sp) { + mask = (qmask >> 32); + if (mask) + wr32(wxhw, WX_PX_IMS(1), mask); + } +} + /* cmd_addr is used for some special command: * 1. to be sector address, when implemented erase sector command * 2. to be flash address when implemented read, write flash address @@ -56,6 +71,151 @@ int wx_check_flash_load(struct wx_hw *hw, u32 check_bit) } EXPORT_SYMBOL(wx_check_flash_load); +static void wx_disable_rx(struct wx_hw *wxhw) +{ + u32 pfdtxgswc; + u32 rxctrl; + + rxctrl = rd32(wxhw, WX_RDB_PB_CTL); + if (rxctrl & WX_RDB_PB_CTL_RXEN) { + pfdtxgswc = rd32(wxhw, WX_PSR_CTL); + if (pfdtxgswc & WX_PSR_CTL_SW_EN) { + pfdtxgswc &= ~WX_PSR_CTL_SW_EN; + wr32(wxhw, WX_PSR_CTL, pfdtxgswc); + wxhw->mac.set_lben = true; + } else { + wxhw->mac.set_lben = false; + } + rxctrl &= ~WX_RDB_PB_CTL_RXEN; + wr32(wxhw, WX_RDB_PB_CTL, rxctrl); + + if (!(((wxhw->subsystem_device_id & WX_NCSI_MASK) == WX_NCSI_SUP) || + ((wxhw->subsystem_device_id & WX_WOL_MASK) == WX_WOL_SUP))) { + /* disable mac receiver */ + wr32m(wxhw, WX_MAC_RX_CFG, + WX_MAC_RX_CFG_RE, 0); + } + } +} + +/** + * wx_disable_pcie_master - Disable PCI-express master access + * @wxhw: pointer to hardware structure + * + * Disables PCI-Express master access and verifies there are no pending + * requests. + **/ +static int wx_disable_pcie_master(struct wx_hw *wxhw) +{ + int status = 0; + u32 val; + + /* Always set this bit to ensure any future transactions are blocked */ + pci_clear_master(wxhw->pdev); + + /* Exit if master requests are blocked */ + if (!(rd32(wxhw, WX_PX_TRANSACTION_PENDING))) + return 0; + + /* Poll for master request bit to clear */ + status = read_poll_timeout(rd32, val, !val, 100, WX_PCI_MASTER_DISABLE_TIMEOUT, + false, wxhw, WX_PX_TRANSACTION_PENDING); + if (status < 0) + wx_err(wxhw, "PCIe transaction pending bit did not clear.\n"); + + return status; +} + +/** + * wx_stop_adapter - Generic stop Tx/Rx units + * @wxhw: pointer to hardware structure + * + * Sets the adapter_stopped flag within wx_hw struct. Clears interrupts, + * disables transmit and receive units. The adapter_stopped flag is used by + * the shared code and drivers to determine if the adapter is in a stopped + * state and should not touch the hardware. + **/ +int wx_stop_adapter(struct wx_hw *wxhw) +{ + u16 i; + + /* Set the adapter_stopped flag so other driver functions stop touching + * the hardware + */ + wxhw->adapter_stopped = true; + + /* Disable the receive unit */ + wx_disable_rx(wxhw); + + /* Set interrupt mask to stop interrupts from being generated */ + wx_intr_disable(wxhw, WX_INTR_ALL); + + /* Clear any pending interrupts, flush previous writes */ + wr32(wxhw, WX_PX_MISC_IC, 0xffffffff); + wr32(wxhw, WX_BME_CTL, 0x3); + + /* Disable the transmit unit. Each queue must be disabled. */ + for (i = 0; i < wxhw->mac.max_tx_queues; i++) { + wr32m(wxhw, WX_PX_TR_CFG(i), + WX_PX_TR_CFG_SWFLSH | WX_PX_TR_CFG_ENABLE, + WX_PX_TR_CFG_SWFLSH); + } + + /* Disable the receive unit by stopping each queue */ + for (i = 0; i < wxhw->mac.max_rx_queues; i++) { + wr32m(wxhw, WX_PX_RR_CFG(i), + WX_PX_RR_CFG_RR_EN, 0); + } + + /* flush all queues disables */ + WX_WRITE_FLUSH(wxhw); + + /* Prevent the PCI-E bus from hanging by disabling PCI-E master + * access and verify no pending requests + */ + return wx_disable_pcie_master(wxhw); +} +EXPORT_SYMBOL(wx_stop_adapter); + +void wx_reset_misc(struct wx_hw *wxhw) +{ + int i; + + /* receive packets that size > 2048 */ + wr32m(wxhw, WX_MAC_RX_CFG, WX_MAC_RX_CFG_JE, WX_MAC_RX_CFG_JE); + + /* clear counters on read */ + wr32m(wxhw, WX_MMC_CONTROL, + WX_MMC_CONTROL_RSTONRD, WX_MMC_CONTROL_RSTONRD); + + wr32m(wxhw, WX_MAC_RX_FLOW_CTRL, + WX_MAC_RX_FLOW_CTRL_RFE, WX_MAC_RX_FLOW_CTRL_RFE); + + wr32(wxhw, WX_MAC_PKT_FLT, WX_MAC_PKT_FLT_PR); + + wr32m(wxhw, WX_MIS_RST_ST, + WX_MIS_RST_ST_RST_INIT, 0x1E00); + + /* errata 4: initialize mng flex tbl and wakeup flex tbl*/ + wr32(wxhw, WX_PSR_MNG_FLEX_SEL, 0); + for (i = 0; i < 16; i++) { + wr32(wxhw, WX_PSR_MNG_FLEX_DW_L(i), 0); + wr32(wxhw, WX_PSR_MNG_FLEX_DW_H(i), 0); + wr32(wxhw, WX_PSR_MNG_FLEX_MSK(i), 0); + } + wr32(wxhw, WX_PSR_LAN_FLEX_SEL, 0); + for (i = 0; i < 16; i++) { + wr32(wxhw, WX_PSR_LAN_FLEX_DW_L(i), 0); + wr32(wxhw, WX_PSR_LAN_FLEX_DW_H(i), 0); + wr32(wxhw, WX_PSR_LAN_FLEX_MSK(i), 0); + } + + /* set pause frame dst mac addr */ + wr32(wxhw, WX_RDB_PFCMACDAL, 0xC2000001); + wr32(wxhw, WX_RDB_PFCMACDAH, 0x0180); +} +EXPORT_SYMBOL(wx_reset_misc); + int wx_sw_init(struct wx_hw *wxhw) { struct pci_dev *pdev = wxhw->pdev; diff --git a/drivers/net/ethernet/wangxun/libwx/wx_hw.h b/drivers/net/ethernet/wangxun/libwx/wx_hw.h index 42e95283242c..2c4c4cbbfb46 100644 --- a/drivers/net/ethernet/wangxun/libwx/wx_hw.h +++ b/drivers/net/ethernet/wangxun/libwx/wx_hw.h @@ -5,6 +5,8 @@ #define _WX_HW_H_ int wx_check_flash_load(struct wx_hw *hw, u32 check_bit); +int wx_stop_adapter(struct wx_hw *wxhw); +void wx_reset_misc(struct wx_hw *wxhw); int wx_sw_init(struct wx_hw *wxhw); #endif /* _WX_HW_H_ */ diff --git a/drivers/net/ethernet/wangxun/libwx/wx_type.h b/drivers/net/ethernet/wangxun/libwx/wx_type.h index 1b2d8ad38dba..1d8e8a529d7a 100644 --- a/drivers/net/ethernet/wangxun/libwx/wx_type.h +++ b/drivers/net/ethernet/wangxun/libwx/wx_type.h @@ -9,6 +9,20 @@ #define PCI_VENDOR_ID_WANGXUN 0x8088 #endif +#define WX_NCSI_SUP 0x8000 +#define WX_NCSI_MASK 0x8000 +#define WX_WOL_SUP 0x4000 +#define WX_WOL_MASK 0x4000 + +/**************** Global Registers ****************************/ +/* chip control Registers */ +#define WX_MIS_PWR 0x10000 +#define WX_MIS_RST 0x1000C +#define WX_MIS_RST_LAN_RST(_i) BIT((_i) + 1) +#define WX_MIS_RST_ST 0x10030 +#define WX_MIS_RST_ST_RST_INI_SHIFT 8 +#define WX_MIS_RST_ST_RST_INIT (0xFF << WX_MIS_RST_ST_RST_INI_SHIFT) + /* FMGR Registers */ #define WX_SPI_CMD 0x10104 #define WX_SPI_CMD_READ_DWORD 0x1 @@ -25,16 +39,120 @@ #define WX_SPI_STATUS_FLASH_BYPASS BIT(31) #define WX_SPI_ILDR_STATUS 0x10120 +/* Sensors for PVT(Process Voltage Temperature) */ +#define WX_TS_EN 0x10304 +#define WX_TS_EN_ENA BIT(0) +#define WX_TS_ALARM_THRE 0x1030C +#define WX_TS_DALARM_THRE 0x10310 +#define WX_TS_INT_EN 0x10314 +#define WX_TS_INT_EN_DALARM_INT_EN BIT(1) +#define WX_TS_INT_EN_ALARM_INT_EN BIT(0) +#define WX_TS_ALARM_ST 0x10318 +#define WX_TS_ALARM_ST_DALARM BIT(1) +#define WX_TS_ALARM_ST_ALARM BIT(0) + +/***************************** RDB registers *********************************/ +/* receive packet buffer */ +#define WX_RDB_PB_CTL 0x19000 +#define WX_RDB_PB_CTL_RXEN BIT(31) /* Enable Receiver */ +#define WX_RDB_PB_CTL_DISABLED BIT(0) +/* statistic */ +#define WX_RDB_PFCMACDAL 0x19210 +#define WX_RDB_PFCMACDAH 0x19214 + +/******************************* PSR Registers *******************************/ +/* psr control */ +#define WX_PSR_CTL 0x15000 +/* Header split receive */ +#define WX_PSR_CTL_SW_EN BIT(18) +#define WX_PSR_CTL_RSC_ACK BIT(17) +#define WX_PSR_CTL_RSC_DIS BIT(16) +#define WX_PSR_CTL_PCSD BIT(13) +#define WX_PSR_CTL_IPPCSE BIT(12) +#define WX_PSR_CTL_BAM BIT(10) +#define WX_PSR_CTL_UPE BIT(9) +#define WX_PSR_CTL_MPE BIT(8) +#define WX_PSR_CTL_MFE BIT(7) +#define WX_PSR_CTL_MO_SHIFT 5 +#define WX_PSR_CTL_MO (0x3 << WX_PSR_CTL_MO_SHIFT) +#define WX_PSR_CTL_TPE BIT(4) + +/* Management */ +#define WX_PSR_MNG_FLEX_SEL 0x1582C +#define WX_PSR_MNG_FLEX_DW_L(_i) (0x15A00 + ((_i) * 16)) +#define WX_PSR_MNG_FLEX_DW_H(_i) (0x15A04 + ((_i) * 16)) +#define WX_PSR_MNG_FLEX_MSK(_i) (0x15A08 + ((_i) * 16)) +#define WX_PSR_LAN_FLEX_SEL 0x15B8C +#define WX_PSR_LAN_FLEX_DW_L(_i) (0x15C00 + ((_i) * 16)) +#define WX_PSR_LAN_FLEX_DW_H(_i) (0x15C04 + ((_i) * 16)) +#define WX_PSR_LAN_FLEX_MSK(_i) (0x15C08 + ((_i) * 16)) + +/************************************* ETH MAC *****************************/ +#define WX_MAC_RX_CFG 0x11004 +#define WX_MAC_RX_CFG_RE BIT(0) +#define WX_MAC_RX_CFG_JE BIT(8) +#define WX_MAC_PKT_FLT 0x11008 +#define WX_MAC_PKT_FLT_PR BIT(0) /* promiscuous mode */ +#define WX_MAC_RX_FLOW_CTRL 0x11090 +#define WX_MAC_RX_FLOW_CTRL_RFE BIT(0) /* receive fc enable */ +#define WX_MMC_CONTROL 0x11800 +#define WX_MMC_CONTROL_RSTONRD BIT(2) /* reset on read */ + +/********************************* BAR registers ***************************/ +/* Interrupt Registers */ +#define WX_BME_CTL 0x12020 +#define WX_PX_MISC_IC 0x100 +#define WX_PX_IMS(_i) (0x140 + (_i) * 4) +#define WX_PX_TRANSACTION_PENDING 0x168 + +/* transmit DMA Registers */ +#define WX_PX_TR_CFG(_i) (0x03010 + ((_i) * 0x40)) +/* Transmit Config masks */ +#define WX_PX_TR_CFG_ENABLE BIT(0) /* Ena specific Tx Queue */ +#define WX_PX_TR_CFG_TR_SIZE_SHIFT 1 /* tx desc number per ring */ +#define WX_PX_TR_CFG_SWFLSH BIT(26) /* Tx Desc. wr-bk flushing */ +#define WX_PX_TR_CFG_WTHRESH_SHIFT 16 /* shift to WTHRESH bits */ +#define WX_PX_TR_CFG_THRE_SHIFT 8 + +/* Receive DMA Registers */ +#define WX_PX_RR_CFG(_i) (0x01010 + ((_i) * 0x40)) +/* PX_RR_CFG bit definitions */ +#define WX_PX_RR_CFG_RR_EN BIT(0) + +/* Number of 80 microseconds we wait for PCI Express master disable */ +#define WX_PCI_MASTER_DISABLE_TIMEOUT 80000 + /* Bus parameters */ struct wx_bus_info { u8 func; u16 device; }; +struct wx_thermal_sensor_data { + s16 temp; + s16 alarm_thresh; + s16 dalarm_thresh; +}; + +enum wx_mac_type { + wx_mac_unknown = 0, + wx_mac_sp, + wx_mac_em +}; + +struct wx_mac_info { + enum wx_mac_type type; + bool set_lben; + u32 max_tx_queues; + u32 max_rx_queues; + struct wx_thermal_sensor_data sensor; +}; + struct wx_hw { u8 __iomem *hw_addr; struct pci_dev *pdev; struct wx_bus_info bus; + struct wx_mac_info mac; u16 device_id; u16 vendor_id; u16 subsystem_device_id; @@ -42,12 +160,40 @@ struct wx_hw { u8 revision_id; u16 oem_ssid; u16 oem_svid; + bool adapter_stopped; }; +#define WX_INTR_ALL (~0ULL) + /* register operations */ #define wr32(a, reg, value) writel((value), ((a)->hw_addr + (reg))) #define rd32(a, reg) readl((a)->hw_addr + (reg)) +static inline u32 +rd32m(struct wx_hw *wxhw, u32 reg, u32 mask) +{ + u32 val; + + val = rd32(wxhw, reg); + return val & mask; +} + +static inline void +wr32m(struct wx_hw *wxhw, u32 reg, u32 mask, u32 field) +{ + u32 val; + + val = rd32(wxhw, reg); + val = ((val & ~mask) | (field & mask)); + + wr32(wxhw, reg, val); +} + +/* On some domestic CPU platforms, sometimes IO is not synchronized with + * flushing memory, here use readl() to flush PCI read and write. + */ +#define WX_WRITE_FLUSH(H) rd32(H, WX_MIS_PWR) + #define wx_err(wxhw, fmt, arg...) \ dev_err(&(wxhw)->pdev->dev, fmt, ##arg) diff --git a/drivers/net/ethernet/wangxun/txgbe/Makefile b/drivers/net/ethernet/wangxun/txgbe/Makefile index 431303ca75b4..78484c58b78b 100644 --- a/drivers/net/ethernet/wangxun/txgbe/Makefile +++ b/drivers/net/ethernet/wangxun/txgbe/Makefile @@ -6,4 +6,5 @@ obj-$(CONFIG_TXGBE) += txgbe.o -txgbe-objs := txgbe_main.o +txgbe-objs := txgbe_main.o \ + txgbe_hw.o diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe.h b/drivers/net/ethernet/wangxun/txgbe/txgbe.h index 9a97b85be3ac..f866d7fa7161 100644 --- a/drivers/net/ethernet/wangxun/txgbe/txgbe.h +++ b/drivers/net/ethernet/wangxun/txgbe/txgbe.h @@ -4,13 +4,14 @@ #ifndef _TXGBE_H_ #define _TXGBE_H_ -#include "txgbe_type.h" - #define TXGBE_MAX_FDIR_INDICES 63 #define TXGBE_MAX_RX_QUEUES (TXGBE_MAX_FDIR_INDICES + 1) #define TXGBE_MAX_TX_QUEUES (TXGBE_MAX_FDIR_INDICES + 1) +#define TXGBE_SP_MAX_TX_QUEUES 128 +#define TXGBE_SP_MAX_RX_QUEUES 128 + /* board specific private data structure */ struct txgbe_adapter { u8 __iomem *io_addr; /* Mainly for iounmap use */ diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_hw.c b/drivers/net/ethernet/wangxun/txgbe/txgbe_hw.c new file mode 100644 index 000000000000..a679db3f2e41 --- /dev/null +++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_hw.c @@ -0,0 +1,86 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2015 - 2022 Beijing WangXun Technology Co., Ltd. */ + +#include +#include +#include +#include + +#include "../libwx/wx_type.h" +#include "../libwx/wx_hw.h" +#include "txgbe_type.h" +#include "txgbe_hw.h" +#include "txgbe.h" + +/** + * txgbe_init_thermal_sensor_thresh - Inits thermal sensor thresholds + * @hw: pointer to hardware structure + * + * Inits the thermal sensor thresholds according to the NVM map + * and save off the threshold and location values into mac.thermal_sensor_data + **/ +static void txgbe_init_thermal_sensor_thresh(struct txgbe_hw *hw) +{ + struct wx_hw *wxhw = &hw->wxhw; + struct wx_thermal_sensor_data *data = &wxhw->mac.sensor; + + memset(data, 0, sizeof(struct wx_thermal_sensor_data)); + + /* Only support thermal sensors attached to SP physical port 0 */ + if (wxhw->bus.func) + return; + + wr32(wxhw, TXGBE_TS_CTL, TXGBE_TS_CTL_EVAL_MD); + + wr32(wxhw, WX_TS_INT_EN, + WX_TS_INT_EN_ALARM_INT_EN | WX_TS_INT_EN_DALARM_INT_EN); + wr32(wxhw, WX_TS_EN, WX_TS_EN_ENA); + + data->alarm_thresh = 100; + wr32(wxhw, WX_TS_ALARM_THRE, 677); + data->dalarm_thresh = 90; + wr32(wxhw, WX_TS_DALARM_THRE, 614); +} + +static void txgbe_reset_misc(struct txgbe_hw *hw) +{ + struct wx_hw *wxhw = &hw->wxhw; + + wx_reset_misc(wxhw); + txgbe_init_thermal_sensor_thresh(hw); +} + +/** + * txgbe_reset_hw - Perform hardware reset + * @hw: pointer to hardware structure + * + * Resets the hardware by resetting the transmit and receive units, masks + * and clears all interrupts, perform a PHY reset, and perform a link (MAC) + * reset. + **/ +int txgbe_reset_hw(struct txgbe_hw *hw) +{ + struct wx_hw *wxhw = &hw->wxhw; + u32 reset = 0; + int status; + + /* Call adapter stop to disable tx/rx and clear interrupts */ + status = wx_stop_adapter(wxhw); + if (status != 0) + return status; + + reset = WX_MIS_RST_LAN_RST(wxhw->bus.func); + wr32(wxhw, WX_MIS_RST, reset | rd32(wxhw, WX_MIS_RST)); + + WX_WRITE_FLUSH(wxhw); + usleep_range(10, 100); + + status = wx_check_flash_load(wxhw, TXGBE_SPI_ILDR_STATUS_LAN_SW_RST(wxhw->bus.func)); + if (status != 0) + return status; + + txgbe_reset_misc(hw); + pci_set_master(wxhw->pdev); + + return 0; +} diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_hw.h b/drivers/net/ethernet/wangxun/txgbe/txgbe_hw.h new file mode 100644 index 000000000000..155f18ea4b8c --- /dev/null +++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_hw.h @@ -0,0 +1,9 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (c) 2015 - 2022 Beijing WangXun Technology Co., Ltd. */ + +#ifndef _TXGBE_HW_H_ +#define _TXGBE_HW_H_ + +int txgbe_reset_hw(struct txgbe_hw *hw); + +#endif /* _TXGBE_HW_H_ */ diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c b/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c index 71ea197fe299..409103d5ac2b 100644 --- a/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c +++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c @@ -11,6 +11,8 @@ #include "../libwx/wx_type.h" #include "../libwx/wx_hw.h" +#include "txgbe_type.h" +#include "txgbe_hw.h" #include "txgbe.h" char txgbe_driver_name[] = "txgbe"; @@ -92,6 +94,19 @@ static int txgbe_sw_init(struct txgbe_adapter *adapter) return err; } + switch (wxhw->device_id) { + case TXGBE_DEV_ID_SP1000: + case TXGBE_DEV_ID_WX1820: + wxhw->mac.type = wx_mac_sp; + break; + default: + wxhw->mac.type = wx_mac_unknown; + break; + } + + wxhw->mac.max_tx_queues = TXGBE_SP_MAX_TX_QUEUES; + wxhw->mac.max_rx_queues = TXGBE_SP_MAX_RX_QUEUES; + return 0; } @@ -201,6 +216,12 @@ static int txgbe_probe(struct pci_dev *pdev, if (err) goto err_pci_release_regions; + err = txgbe_reset_hw(hw); + if (err) { + dev_err(&pdev->dev, "HW Init failed: %d\n", err); + goto err_pci_release_regions; + } + netdev->features |= NETIF_F_HIGHDMA; pci_set_drvdata(pdev, adapter); diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_type.h b/drivers/net/ethernet/wangxun/txgbe/txgbe_type.h index 1d1ead3d3c06..c891b0f07227 100644 --- a/drivers/net/ethernet/wangxun/txgbe/txgbe_type.h +++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_type.h @@ -33,12 +33,6 @@ #define TXGBE_ID_WX1820_MAC_SGMII 0x2060 #define TXGBE_ID_MAC_SGMII 0x60 -#define TXGBE_NCSI_SUP 0x8000 -#define TXGBE_NCSI_MASK 0x8000 -#define TXGBE_WOL_SUP 0x4000 -#define TXGBE_WOL_MASK 0x4000 -#define TXGBE_DEV_MASK 0xf0 - /* Combined interface*/ #define TXGBE_ID_SFI_XAUI 0x50 @@ -50,6 +44,11 @@ #define TXGBE_SPI_ILDR_STATUS 0x10120 #define TXGBE_SPI_ILDR_STATUS_PERST BIT(0) /* PCIE_PERST is done */ #define TXGBE_SPI_ILDR_STATUS_PWRRST BIT(1) /* Power on reset is done */ +#define TXGBE_SPI_ILDR_STATUS_LAN_SW_RST(_i) BIT((_i) + 9) /* lan soft reset done */ + +/* Sensors for PVT(Process Voltage Temperature) */ +#define TXGBE_TS_CTL 0x10300 +#define TXGBE_TS_CTL_EVAL_MD BIT(31) struct txgbe_hw { struct wx_hw wxhw; From d21d2c7f586cec0a735031a8a60276a990684e4c Mon Sep 17 00:00:00 2001 From: Jiawen Wu Date: Thu, 27 Oct 2022 14:11:16 +0800 Subject: [PATCH 0441/2002] net: txgbe: Set MAC address and register netdev Add MAC address related operations, and register netdev. Signed-off-by: Jiawen Wu Signed-off-by: David S. Miller --- drivers/net/ethernet/wangxun/libwx/wx_hw.c | 231 ++++++++++++++- drivers/net/ethernet/wangxun/libwx/wx_hw.h | 6 + drivers/net/ethernet/wangxun/libwx/wx_type.h | 37 +++ drivers/net/ethernet/wangxun/txgbe/txgbe.h | 13 + drivers/net/ethernet/wangxun/txgbe/txgbe_hw.c | 13 + .../net/ethernet/wangxun/txgbe/txgbe_main.c | 270 +++++++++++++++++- .../net/ethernet/wangxun/txgbe/txgbe_type.h | 3 + 7 files changed, 566 insertions(+), 7 deletions(-) diff --git a/drivers/net/ethernet/wangxun/libwx/wx_hw.c b/drivers/net/ethernet/wangxun/libwx/wx_hw.c index 6e2a2d251a09..8dbbac862f27 100644 --- a/drivers/net/ethernet/wangxun/libwx/wx_hw.c +++ b/drivers/net/ethernet/wangxun/libwx/wx_hw.c @@ -1,6 +1,8 @@ // SPDX-License-Identifier: GPL-2.0 /* Copyright (c) 2015 - 2022 Beijing WangXun Technology Co., Ltd. */ +#include +#include #include #include @@ -71,7 +73,230 @@ int wx_check_flash_load(struct wx_hw *hw, u32 check_bit) } EXPORT_SYMBOL(wx_check_flash_load); -static void wx_disable_rx(struct wx_hw *wxhw) +/** + * wx_get_mac_addr - Generic get MAC address + * @wxhw: pointer to hardware structure + * @mac_addr: Adapter MAC address + * + * Reads the adapter's MAC address from first Receive Address Register (RAR0) + * A reset of the adapter must be performed prior to calling this function + * in order for the MAC address to have been loaded from the EEPROM into RAR0 + **/ +void wx_get_mac_addr(struct wx_hw *wxhw, u8 *mac_addr) +{ + u32 rar_high; + u32 rar_low; + u16 i; + + wr32(wxhw, WX_PSR_MAC_SWC_IDX, 0); + rar_high = rd32(wxhw, WX_PSR_MAC_SWC_AD_H); + rar_low = rd32(wxhw, WX_PSR_MAC_SWC_AD_L); + + for (i = 0; i < 2; i++) + mac_addr[i] = (u8)(rar_high >> (1 - i) * 8); + + for (i = 0; i < 4; i++) + mac_addr[i + 2] = (u8)(rar_low >> (3 - i) * 8); +} +EXPORT_SYMBOL(wx_get_mac_addr); + +/** + * wx_set_rar - Set Rx address register + * @wxhw: pointer to hardware structure + * @index: Receive address register to write + * @addr: Address to put into receive address register + * @pools: VMDq "set" or "pool" index + * @enable_addr: set flag that address is active + * + * Puts an ethernet address into a receive address register. + **/ +int wx_set_rar(struct wx_hw *wxhw, u32 index, u8 *addr, u64 pools, + u32 enable_addr) +{ + u32 rar_entries = wxhw->mac.num_rar_entries; + u32 rar_low, rar_high; + + /* Make sure we are using a valid rar index range */ + if (index >= rar_entries) { + wx_err(wxhw, "RAR index %d is out of range.\n", index); + return -EINVAL; + } + + /* select the MAC address */ + wr32(wxhw, WX_PSR_MAC_SWC_IDX, index); + + /* setup VMDq pool mapping */ + wr32(wxhw, WX_PSR_MAC_SWC_VM_L, pools & 0xFFFFFFFF); + if (wxhw->mac.type == wx_mac_sp) + wr32(wxhw, WX_PSR_MAC_SWC_VM_H, pools >> 32); + + /* HW expects these in little endian so we reverse the byte + * order from network order (big endian) to little endian + * + * Some parts put the VMDq setting in the extra RAH bits, + * so save everything except the lower 16 bits that hold part + * of the address and the address valid bit. + */ + rar_low = ((u32)addr[5] | + ((u32)addr[4] << 8) | + ((u32)addr[3] << 16) | + ((u32)addr[2] << 24)); + rar_high = ((u32)addr[1] | + ((u32)addr[0] << 8)); + if (enable_addr != 0) + rar_high |= WX_PSR_MAC_SWC_AD_H_AV; + + wr32(wxhw, WX_PSR_MAC_SWC_AD_L, rar_low); + wr32m(wxhw, WX_PSR_MAC_SWC_AD_H, + (WX_PSR_MAC_SWC_AD_H_AD(~0) | + WX_PSR_MAC_SWC_AD_H_ADTYPE(~0) | + WX_PSR_MAC_SWC_AD_H_AV), + rar_high); + + return 0; +} +EXPORT_SYMBOL(wx_set_rar); + +/** + * wx_clear_rar - Remove Rx address register + * @wxhw: pointer to hardware structure + * @index: Receive address register to write + * + * Clears an ethernet address from a receive address register. + **/ +int wx_clear_rar(struct wx_hw *wxhw, u32 index) +{ + u32 rar_entries = wxhw->mac.num_rar_entries; + + /* Make sure we are using a valid rar index range */ + if (index >= rar_entries) { + wx_err(wxhw, "RAR index %d is out of range.\n", index); + return -EINVAL; + } + + /* Some parts put the VMDq setting in the extra RAH bits, + * so save everything except the lower 16 bits that hold part + * of the address and the address valid bit. + */ + wr32(wxhw, WX_PSR_MAC_SWC_IDX, index); + + wr32(wxhw, WX_PSR_MAC_SWC_VM_L, 0); + wr32(wxhw, WX_PSR_MAC_SWC_VM_H, 0); + + wr32(wxhw, WX_PSR_MAC_SWC_AD_L, 0); + wr32m(wxhw, WX_PSR_MAC_SWC_AD_H, + (WX_PSR_MAC_SWC_AD_H_AD(~0) | + WX_PSR_MAC_SWC_AD_H_ADTYPE(~0) | + WX_PSR_MAC_SWC_AD_H_AV), + 0); + + return 0; +} +EXPORT_SYMBOL(wx_clear_rar); + +/** + * wx_clear_vmdq - Disassociate a VMDq pool index from a rx address + * @wxhw: pointer to hardware struct + * @rar: receive address register index to disassociate + * @vmdq: VMDq pool index to remove from the rar + **/ +static int wx_clear_vmdq(struct wx_hw *wxhw, u32 rar, u32 __maybe_unused vmdq) +{ + u32 rar_entries = wxhw->mac.num_rar_entries; + u32 mpsar_lo, mpsar_hi; + + /* Make sure we are using a valid rar index range */ + if (rar >= rar_entries) { + wx_err(wxhw, "RAR index %d is out of range.\n", rar); + return -EINVAL; + } + + wr32(wxhw, WX_PSR_MAC_SWC_IDX, rar); + mpsar_lo = rd32(wxhw, WX_PSR_MAC_SWC_VM_L); + mpsar_hi = rd32(wxhw, WX_PSR_MAC_SWC_VM_H); + + if (!mpsar_lo && !mpsar_hi) + return 0; + + /* was that the last pool using this rar? */ + if (mpsar_lo == 0 && mpsar_hi == 0 && rar != 0) + wx_clear_rar(wxhw, rar); + + return 0; +} + +/** + * wx_init_uta_tables - Initialize the Unicast Table Array + * @wxhw: pointer to hardware structure + **/ +static void wx_init_uta_tables(struct wx_hw *wxhw) +{ + int i; + + wx_dbg(wxhw, " Clearing UTA\n"); + + for (i = 0; i < 128; i++) + wr32(wxhw, WX_PSR_UC_TBL(i), 0); +} + +/** + * wx_init_rx_addrs - Initializes receive address filters. + * @wxhw: pointer to hardware structure + * + * Places the MAC address in receive address register 0 and clears the rest + * of the receive address registers. Clears the multicast table. Assumes + * the receiver is in reset when the routine is called. + **/ +void wx_init_rx_addrs(struct wx_hw *wxhw) +{ + u32 rar_entries = wxhw->mac.num_rar_entries; + u32 psrctl; + int i; + + /* If the current mac address is valid, assume it is a software override + * to the permanent address. + * Otherwise, use the permanent address from the eeprom. + */ + if (!is_valid_ether_addr(wxhw->mac.addr)) { + /* Get the MAC address from the RAR0 for later reference */ + wx_get_mac_addr(wxhw, wxhw->mac.addr); + wx_dbg(wxhw, "Keeping Current RAR0 Addr = %pM\n", wxhw->mac.addr); + } else { + /* Setup the receive address. */ + wx_dbg(wxhw, "Overriding MAC Address in RAR[0]\n"); + wx_dbg(wxhw, "New MAC Addr = %pM\n", wxhw->mac.addr); + + wx_set_rar(wxhw, 0, wxhw->mac.addr, 0, WX_PSR_MAC_SWC_AD_H_AV); + + if (wxhw->mac.type == wx_mac_sp) { + /* clear VMDq pool/queue selection for RAR 0 */ + wx_clear_vmdq(wxhw, 0, WX_CLEAR_VMDQ_ALL); + } + } + + /* Zero out the other receive addresses. */ + wx_dbg(wxhw, "Clearing RAR[1-%d]\n", rar_entries - 1); + for (i = 1; i < rar_entries; i++) { + wr32(wxhw, WX_PSR_MAC_SWC_IDX, i); + wr32(wxhw, WX_PSR_MAC_SWC_AD_L, 0); + wr32(wxhw, WX_PSR_MAC_SWC_AD_H, 0); + } + + /* Clear the MTA */ + wxhw->addr_ctrl.mta_in_use = 0; + psrctl = rd32(wxhw, WX_PSR_CTL); + psrctl &= ~(WX_PSR_CTL_MO | WX_PSR_CTL_MFE); + psrctl |= wxhw->mac.mc_filter_type << WX_PSR_CTL_MO_SHIFT; + wr32(wxhw, WX_PSR_CTL, psrctl); + wx_dbg(wxhw, " Clearing MTA\n"); + for (i = 0; i < wxhw->mac.mcft_size; i++) + wr32(wxhw, WX_PSR_MC_TBL(i), 0); + + wx_init_uta_tables(wxhw); +} +EXPORT_SYMBOL(wx_init_rx_addrs); + +void wx_disable_rx(struct wx_hw *wxhw) { u32 pfdtxgswc; u32 rxctrl; @@ -97,6 +322,7 @@ static void wx_disable_rx(struct wx_hw *wxhw) } } } +EXPORT_SYMBOL(wx_disable_rx); /** * wx_disable_pcie_master - Disable PCI-express master access @@ -105,7 +331,7 @@ static void wx_disable_rx(struct wx_hw *wxhw) * Disables PCI-Express master access and verifies there are no pending * requests. **/ -static int wx_disable_pcie_master(struct wx_hw *wxhw) +int wx_disable_pcie_master(struct wx_hw *wxhw) { int status = 0; u32 val; @@ -125,6 +351,7 @@ static int wx_disable_pcie_master(struct wx_hw *wxhw) return status; } +EXPORT_SYMBOL(wx_disable_pcie_master); /** * wx_stop_adapter - Generic stop Tx/Rx units diff --git a/drivers/net/ethernet/wangxun/libwx/wx_hw.h b/drivers/net/ethernet/wangxun/libwx/wx_hw.h index 2c4c4cbbfb46..58a943dc76c1 100644 --- a/drivers/net/ethernet/wangxun/libwx/wx_hw.h +++ b/drivers/net/ethernet/wangxun/libwx/wx_hw.h @@ -5,6 +5,12 @@ #define _WX_HW_H_ int wx_check_flash_load(struct wx_hw *hw, u32 check_bit); +void wx_get_mac_addr(struct wx_hw *wxhw, u8 *mac_addr); +int wx_set_rar(struct wx_hw *wxhw, u32 index, u8 *addr, u64 pools, u32 enable_addr); +int wx_clear_rar(struct wx_hw *wxhw, u32 index); +void wx_init_rx_addrs(struct wx_hw *wxhw); +void wx_disable_rx(struct wx_hw *wxhw); +int wx_disable_pcie_master(struct wx_hw *wxhw); int wx_stop_adapter(struct wx_hw *wxhw); void wx_reset_misc(struct wx_hw *wxhw); int wx_sw_init(struct wx_hw *wxhw); diff --git a/drivers/net/ethernet/wangxun/libwx/wx_type.h b/drivers/net/ethernet/wangxun/libwx/wx_type.h index 1d8e8a529d7a..790497cec603 100644 --- a/drivers/net/ethernet/wangxun/libwx/wx_type.h +++ b/drivers/net/ethernet/wangxun/libwx/wx_type.h @@ -51,6 +51,12 @@ #define WX_TS_ALARM_ST_DALARM BIT(1) #define WX_TS_ALARM_ST_ALARM BIT(0) +/*********************** Transmit DMA registers **************************/ +/* transmit global control */ +#define WX_TDM_CTL 0x18000 +/* TDM CTL BIT */ +#define WX_TDM_CTL_TE BIT(0) /* Transmit Enable */ + /***************************** RDB registers *********************************/ /* receive packet buffer */ #define WX_RDB_PB_CTL 0x19000 @@ -76,6 +82,9 @@ #define WX_PSR_CTL_MO_SHIFT 5 #define WX_PSR_CTL_MO (0x3 << WX_PSR_CTL_MO_SHIFT) #define WX_PSR_CTL_TPE BIT(4) +/* mcasst/ucast overflow tbl */ +#define WX_PSR_MC_TBL(_i) (0x15200 + ((_i) * 4)) +#define WX_PSR_UC_TBL(_i) (0x15400 + ((_i) * 4)) /* Management */ #define WX_PSR_MNG_FLEX_SEL 0x1582C @@ -87,7 +96,20 @@ #define WX_PSR_LAN_FLEX_DW_H(_i) (0x15C04 + ((_i) * 16)) #define WX_PSR_LAN_FLEX_MSK(_i) (0x15C08 + ((_i) * 16)) +/* mac switcher */ +#define WX_PSR_MAC_SWC_AD_L 0x16200 +#define WX_PSR_MAC_SWC_AD_H 0x16204 +#define WX_PSR_MAC_SWC_AD_H_AD(v) (((v) & 0xFFFF)) +#define WX_PSR_MAC_SWC_AD_H_ADTYPE(v) (((v) & 0x1) << 30) +#define WX_PSR_MAC_SWC_AD_H_AV BIT(31) +#define WX_PSR_MAC_SWC_VM_L 0x16208 +#define WX_PSR_MAC_SWC_VM_H 0x1620C +#define WX_PSR_MAC_SWC_IDX 0x16210 +#define WX_CLEAR_VMDQ_ALL 0xFFFFFFFFU + /************************************* ETH MAC *****************************/ +#define WX_MAC_TX_CFG 0x11000 +#define WX_MAC_TX_CFG_TE BIT(0) #define WX_MAC_RX_CFG 0x11004 #define WX_MAC_RX_CFG_RE BIT(0) #define WX_MAC_RX_CFG_JE BIT(8) @@ -143,16 +165,28 @@ enum wx_mac_type { struct wx_mac_info { enum wx_mac_type type; bool set_lben; + u8 addr[ETH_ALEN]; + u8 perm_addr[ETH_ALEN]; + s32 mc_filter_type; + u32 mcft_size; + u32 num_rar_entries; u32 max_tx_queues; u32 max_rx_queues; struct wx_thermal_sensor_data sensor; }; +struct wx_addr_filter_info { + u32 num_mc_addrs; + u32 mta_in_use; + bool user_set_promisc; +}; + struct wx_hw { u8 __iomem *hw_addr; struct pci_dev *pdev; struct wx_bus_info bus; struct wx_mac_info mac; + struct wx_addr_filter_info addr_ctrl; u16 device_id; u16 vendor_id; u16 subsystem_device_id; @@ -197,4 +231,7 @@ wr32m(struct wx_hw *wxhw, u32 reg, u32 mask, u32 field) #define wx_err(wxhw, fmt, arg...) \ dev_err(&(wxhw)->pdev->dev, fmt, ##arg) +#define wx_dbg(wxhw, fmt, arg...) \ + dev_dbg(&(wxhw)->pdev->dev, fmt, ##arg) + #endif /* _WX_TYPE_H_ */ diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe.h b/drivers/net/ethernet/wangxun/txgbe/txgbe.h index f866d7fa7161..52e350f9a7d9 100644 --- a/drivers/net/ethernet/wangxun/txgbe/txgbe.h +++ b/drivers/net/ethernet/wangxun/txgbe/txgbe.h @@ -11,6 +11,18 @@ #define TXGBE_SP_MAX_TX_QUEUES 128 #define TXGBE_SP_MAX_RX_QUEUES 128 +#define TXGBE_SP_RAR_ENTRIES 128 +#define TXGBE_SP_MC_TBL_SIZE 128 + +struct txgbe_mac_addr { + u8 addr[ETH_ALEN]; + u16 state; /* bitmask */ + u64 pools; +}; + +#define TXGBE_MAC_STATE_DEFAULT 0x1 +#define TXGBE_MAC_STATE_MODIFIED 0x2 +#define TXGBE_MAC_STATE_IN_USE 0x4 /* board specific private data structure */ struct txgbe_adapter { @@ -22,6 +34,7 @@ struct txgbe_adapter { /* structs defined in txgbe_type.h */ struct txgbe_hw hw; u16 msg_enable; + struct txgbe_mac_addr *mac_table; }; extern char txgbe_driver_name[]; diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_hw.c b/drivers/net/ethernet/wangxun/txgbe/txgbe_hw.c index a679db3f2e41..c7c92c0ec561 100644 --- a/drivers/net/ethernet/wangxun/txgbe/txgbe_hw.c +++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_hw.c @@ -1,6 +1,8 @@ // SPDX-License-Identifier: GPL-2.0 /* Copyright (c) 2015 - 2022 Beijing WangXun Technology Co., Ltd. */ +#include +#include #include #include #include @@ -80,6 +82,17 @@ int txgbe_reset_hw(struct txgbe_hw *hw) return status; txgbe_reset_misc(hw); + + /* Store the permanent mac address */ + wx_get_mac_addr(wxhw, wxhw->mac.perm_addr); + + /* Store MAC address from RAR0, clear receive address registers, and + * clear the multicast table. Also reset num_rar_entries to 128, + * since we modify this value when programming the SAN MAC address. + */ + wxhw->mac.num_rar_entries = TXGBE_SP_RAR_ENTRIES; + wx_init_rx_addrs(wxhw); + pci_set_master(wxhw->pdev); return 0; diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c b/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c index 409103d5ac2b..adfa4e7d0d52 100644 --- a/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c +++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c @@ -8,6 +8,7 @@ #include #include #include +#include #include "../libwx/wx_type.h" #include "../libwx/wx_hw.h" @@ -72,6 +73,143 @@ static int txgbe_enumerate_functions(struct txgbe_adapter *adapter) return physfns; } +static void txgbe_sync_mac_table(struct txgbe_adapter *adapter) +{ + struct txgbe_hw *hw = &adapter->hw; + struct wx_hw *wxhw = &hw->wxhw; + int i; + + for (i = 0; i < wxhw->mac.num_rar_entries; i++) { + if (adapter->mac_table[i].state & TXGBE_MAC_STATE_MODIFIED) { + if (adapter->mac_table[i].state & TXGBE_MAC_STATE_IN_USE) { + wx_set_rar(wxhw, i, + adapter->mac_table[i].addr, + adapter->mac_table[i].pools, + WX_PSR_MAC_SWC_AD_H_AV); + } else { + wx_clear_rar(wxhw, i); + } + adapter->mac_table[i].state &= ~(TXGBE_MAC_STATE_MODIFIED); + } + } +} + +/* this function destroys the first RAR entry */ +static void txgbe_mac_set_default_filter(struct txgbe_adapter *adapter, + u8 *addr) +{ + struct wx_hw *wxhw = &adapter->hw.wxhw; + + memcpy(&adapter->mac_table[0].addr, addr, ETH_ALEN); + adapter->mac_table[0].pools = 1ULL; + adapter->mac_table[0].state = (TXGBE_MAC_STATE_DEFAULT | + TXGBE_MAC_STATE_IN_USE); + wx_set_rar(wxhw, 0, adapter->mac_table[0].addr, + adapter->mac_table[0].pools, + WX_PSR_MAC_SWC_AD_H_AV); +} + +static void txgbe_flush_sw_mac_table(struct txgbe_adapter *adapter) +{ + struct wx_hw *wxhw = &adapter->hw.wxhw; + u32 i; + + for (i = 0; i < wxhw->mac.num_rar_entries; i++) { + adapter->mac_table[i].state |= TXGBE_MAC_STATE_MODIFIED; + adapter->mac_table[i].state &= ~TXGBE_MAC_STATE_IN_USE; + memset(adapter->mac_table[i].addr, 0, ETH_ALEN); + adapter->mac_table[i].pools = 0; + } + txgbe_sync_mac_table(adapter); +} + +static int txgbe_del_mac_filter(struct txgbe_adapter *adapter, u8 *addr, u16 pool) +{ + struct wx_hw *wxhw = &adapter->hw.wxhw; + u32 i; + + if (is_zero_ether_addr(addr)) + return -EINVAL; + + /* search table for addr, if found, set to 0 and sync */ + for (i = 0; i < wxhw->mac.num_rar_entries; i++) { + if (ether_addr_equal(addr, adapter->mac_table[i].addr)) { + if (adapter->mac_table[i].pools & (1ULL << pool)) { + adapter->mac_table[i].state |= TXGBE_MAC_STATE_MODIFIED; + adapter->mac_table[i].state &= ~TXGBE_MAC_STATE_IN_USE; + adapter->mac_table[i].pools &= ~(1ULL << pool); + txgbe_sync_mac_table(adapter); + } + return 0; + } + + if (adapter->mac_table[i].pools != (1 << pool)) + continue; + if (!ether_addr_equal(addr, adapter->mac_table[i].addr)) + continue; + + adapter->mac_table[i].state |= TXGBE_MAC_STATE_MODIFIED; + adapter->mac_table[i].state &= ~TXGBE_MAC_STATE_IN_USE; + memset(adapter->mac_table[i].addr, 0, ETH_ALEN); + adapter->mac_table[i].pools = 0; + txgbe_sync_mac_table(adapter); + return 0; + } + return -ENOMEM; +} + +static void txgbe_reset(struct txgbe_adapter *adapter) +{ + struct net_device *netdev = adapter->netdev; + struct txgbe_hw *hw = &adapter->hw; + u8 old_addr[ETH_ALEN]; + int err; + + err = txgbe_reset_hw(hw); + if (err != 0) + dev_err(&adapter->pdev->dev, "Hardware Error: %d\n", err); + + /* do not flush user set addresses */ + memcpy(old_addr, &adapter->mac_table[0].addr, netdev->addr_len); + txgbe_flush_sw_mac_table(adapter); + txgbe_mac_set_default_filter(adapter, old_addr); +} + +static void txgbe_disable_device(struct txgbe_adapter *adapter) +{ + struct net_device *netdev = adapter->netdev; + struct wx_hw *wxhw = &adapter->hw.wxhw; + + wx_disable_pcie_master(wxhw); + /* disable receives */ + wx_disable_rx(wxhw); + + netif_carrier_off(netdev); + netif_tx_disable(netdev); + + if (wxhw->bus.func < 2) + wr32m(wxhw, TXGBE_MIS_PRB_CTL, TXGBE_MIS_PRB_CTL_LAN_UP(wxhw->bus.func), 0); + else + dev_err(&adapter->pdev->dev, + "%s: invalid bus lan id %d\n", + __func__, wxhw->bus.func); + + if (!(((wxhw->subsystem_device_id & WX_NCSI_MASK) == WX_NCSI_SUP) || + ((wxhw->subsystem_device_id & WX_WOL_MASK) == WX_WOL_SUP))) { + /* disable mac transmiter */ + wr32m(wxhw, WX_MAC_TX_CFG, WX_MAC_TX_CFG_TE, 0); + } + + /* Disable the Tx DMA engine */ + wr32m(wxhw, WX_TDM_CTL, WX_TDM_CTL_TE, 0); +} + +static void txgbe_down(struct txgbe_adapter *adapter) +{ + txgbe_disable_device(adapter); + txgbe_reset(adapter); +} + /** * txgbe_sw_init - Initialize general software structures (struct txgbe_adapter) * @adapter: board private structure to initialize @@ -104,8 +242,65 @@ static int txgbe_sw_init(struct txgbe_adapter *adapter) break; } + wxhw->mac.num_rar_entries = TXGBE_SP_RAR_ENTRIES; wxhw->mac.max_tx_queues = TXGBE_SP_MAX_TX_QUEUES; wxhw->mac.max_rx_queues = TXGBE_SP_MAX_RX_QUEUES; + wxhw->mac.mcft_size = TXGBE_SP_MC_TBL_SIZE; + + adapter->mac_table = kcalloc(wxhw->mac.num_rar_entries, + sizeof(struct txgbe_mac_addr), + GFP_KERNEL); + if (!adapter->mac_table) { + netif_err(adapter, probe, adapter->netdev, + "mac_table allocation failed\n"); + return -ENOMEM; + } + + return 0; +} + +/** + * txgbe_open - Called when a network interface is made active + * @netdev: network interface device structure + * + * Returns 0 on success, negative value on failure + * + * The open entry point is called when a network interface is made + * active by the system (IFF_UP). + **/ +static int txgbe_open(struct net_device *netdev) +{ + return 0; +} + +/** + * txgbe_close_suspend - actions necessary to both suspend and close flows + * @adapter: the private adapter struct + * + * This function should contain the necessary work common to both suspending + * and closing of the device. + */ +static void txgbe_close_suspend(struct txgbe_adapter *adapter) +{ + txgbe_disable_device(adapter); +} + +/** + * txgbe_close - Disables a network interface + * @netdev: network interface device structure + * + * Returns 0, this is not allowed to fail + * + * The close entry point is called when an interface is de-activated + * by the OS. The hardware is still under the drivers control, but + * needs to be disabled. A global MAC reset is issued to stop the + * hardware, and all transmit and receive resources are freed. + **/ +static int txgbe_close(struct net_device *netdev) +{ + struct txgbe_adapter *adapter = netdev_priv(netdev); + + txgbe_down(adapter); return 0; } @@ -117,6 +312,11 @@ static void txgbe_dev_shutdown(struct pci_dev *pdev, bool *enable_wake) netif_device_detach(netdev); + rtnl_lock(); + if (netif_running(netdev)) + txgbe_close_suspend(adapter); + rtnl_unlock(); + pci_disable_device(pdev); } @@ -132,6 +332,47 @@ static void txgbe_shutdown(struct pci_dev *pdev) } } +static netdev_tx_t txgbe_xmit_frame(struct sk_buff *skb, + struct net_device *netdev) +{ + return NETDEV_TX_OK; +} + +/** + * txgbe_set_mac - Change the Ethernet Address of the NIC + * @netdev: network interface device structure + * @p: pointer to an address structure + * + * Returns 0 on success, negative on failure + **/ +static int txgbe_set_mac(struct net_device *netdev, void *p) +{ + struct txgbe_adapter *adapter = netdev_priv(netdev); + struct wx_hw *wxhw = &adapter->hw.wxhw; + struct sockaddr *addr = p; + int retval; + + retval = eth_prepare_mac_addr_change(netdev, addr); + if (retval) + return retval; + + txgbe_del_mac_filter(adapter, wxhw->mac.addr, 0); + eth_hw_addr_set(netdev, addr->sa_data); + memcpy(wxhw->mac.addr, addr->sa_data, netdev->addr_len); + + txgbe_mac_set_default_filter(adapter, wxhw->mac.addr); + + return 0; +} + +static const struct net_device_ops txgbe_netdev_ops = { + .ndo_open = txgbe_open, + .ndo_stop = txgbe_close, + .ndo_start_xmit = txgbe_xmit_frame, + .ndo_validate_addr = eth_validate_addr, + .ndo_set_mac_address = txgbe_set_mac, +}; + /** * txgbe_probe - Device Initialization Routine * @pdev: PCI device information struct @@ -201,29 +442,36 @@ static int txgbe_probe(struct pci_dev *pdev, goto err_pci_release_regions; } - strncpy(netdev->name, pci_name(pdev), sizeof(netdev->name) - 1); + netdev->netdev_ops = &txgbe_netdev_ops; /* setup the private structure */ err = txgbe_sw_init(adapter); if (err) - goto err_pci_release_regions; + goto err_free_mac_table; /* check if flash load is done after hw power up */ err = wx_check_flash_load(wxhw, TXGBE_SPI_ILDR_STATUS_PERST); if (err) - goto err_pci_release_regions; + goto err_free_mac_table; err = wx_check_flash_load(wxhw, TXGBE_SPI_ILDR_STATUS_PWRRST); if (err) - goto err_pci_release_regions; + goto err_free_mac_table; err = txgbe_reset_hw(hw); if (err) { dev_err(&pdev->dev, "HW Init failed: %d\n", err); - goto err_pci_release_regions; + goto err_free_mac_table; } netdev->features |= NETIF_F_HIGHDMA; + eth_hw_addr_set(netdev, wxhw->mac.perm_addr); + txgbe_mac_set_default_filter(adapter, wxhw->mac.perm_addr); + + err = register_netdev(netdev); + if (err) + goto err_free_mac_table; + pci_set_drvdata(pdev, adapter); /* calculate the expected PCIe bandwidth required for optimal @@ -240,8 +488,12 @@ static int txgbe_probe(struct pci_dev *pdev, else dev_warn(&pdev->dev, "Failed to enumerate PF devices.\n"); + netif_info(adapter, probe, netdev, "%pM\n", netdev->dev_addr); + return 0; +err_free_mac_table: + kfree(adapter->mac_table); err_pci_release_regions: pci_disable_pcie_error_reporting(pdev); pci_release_selected_regions(pdev, @@ -262,9 +514,17 @@ err_pci_disable_dev: **/ static void txgbe_remove(struct pci_dev *pdev) { + struct txgbe_adapter *adapter = pci_get_drvdata(pdev); + struct net_device *netdev; + + netdev = adapter->netdev; + unregister_netdev(netdev); + pci_release_selected_regions(pdev, pci_select_bars(pdev, IORESOURCE_MEM)); + kfree(adapter->mac_table); + pci_disable_pcie_error_reporting(pdev); pci_disable_device(pdev); diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_type.h b/drivers/net/ethernet/wangxun/txgbe/txgbe_type.h index c891b0f07227..4082d3b76709 100644 --- a/drivers/net/ethernet/wangxun/txgbe/txgbe_type.h +++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_type.h @@ -40,6 +40,9 @@ #define TXGBE_SP_MPW 1 /**************** SP Registers ****************************/ +/* chip control Registers */ +#define TXGBE_MIS_PRB_CTL 0x10010 +#define TXGBE_MIS_PRB_CTL_LAN_UP(_i) BIT(1 - (_i)) /* FMGR Registers */ #define TXGBE_SPI_ILDR_STATUS 0x10120 #define TXGBE_SPI_ILDR_STATUS_PERST BIT(0) /* PCIE_PERST is done */ From f8f797f35a9ababcf763b7a3984aff41fdd3f064 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Thu, 27 Oct 2022 00:34:02 -0700 Subject: [PATCH 0442/2002] nfc: s3fwrn5: use devm_clk_get_optional_enabled() helper Because we enable the clock immediately after acquiring it in probe, we can combine the 2 operations and use devm_clk_get_optional_enabled() helper. Signed-off-by: Dmitry Torokhov Reviewed-by: Krzysztof Kozlowski Signed-off-by: David S. Miller --- drivers/nfc/s3fwrn5/i2c.c | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/drivers/nfc/s3fwrn5/i2c.c b/drivers/nfc/s3fwrn5/i2c.c index f824dc7099ce..ecdee838d25d 100644 --- a/drivers/nfc/s3fwrn5/i2c.c +++ b/drivers/nfc/s3fwrn5/i2c.c @@ -209,27 +209,21 @@ static int s3fwrn5_i2c_probe(struct i2c_client *client, if (ret < 0) return ret; - phy->clk = devm_clk_get_optional(&client->dev, NULL); - if (IS_ERR(phy->clk)) - return dev_err_probe(&client->dev, PTR_ERR(phy->clk), - "failed to get clock\n"); - /* * S3FWRN5 depends on a clock input ("XI" pin) to function properly. * Depending on the hardware configuration this could be an always-on * oscillator or some external clock that must be explicitly enabled. * Make sure the clock is running before starting S3FWRN5. */ - ret = clk_prepare_enable(phy->clk); - if (ret < 0) { - dev_err(&client->dev, "failed to enable clock: %d\n", ret); - return ret; - } + phy->clk = devm_clk_get_optional_enabled(&client->dev, NULL); + if (IS_ERR(phy->clk)) + return dev_err_probe(&client->dev, PTR_ERR(phy->clk), + "failed to get clock\n"); ret = s3fwrn5_probe(&phy->common.ndev, phy, &phy->i2c_dev->dev, &i2c_phy_ops); if (ret < 0) - goto disable_clk; + return ret; ret = devm_request_threaded_irq(&client->dev, phy->i2c_dev->irq, NULL, s3fwrn5_i2c_irq_thread_fn, IRQF_ONESHOT, @@ -241,8 +235,6 @@ static int s3fwrn5_i2c_probe(struct i2c_client *client, s3fwrn5_remove: s3fwrn5_remove(phy->common.ndev); -disable_clk: - clk_disable_unprepare(phy->clk); return ret; } @@ -251,7 +243,6 @@ static void s3fwrn5_i2c_remove(struct i2c_client *client) struct s3fwrn5_i2c_phy *phy = i2c_get_clientdata(client); s3fwrn5_remove(phy->common.ndev); - clk_disable_unprepare(phy->clk); } static const struct i2c_device_id s3fwrn5_i2c_id_table[] = { From f3ca72b0327101a074a871539e61775d43908ca4 Mon Sep 17 00:00:00 2001 From: Nagarajan Maran Date: Fri, 14 Oct 2022 21:20:54 +0530 Subject: [PATCH 0443/2002] wifi: ath11k: fix monitor vdev creation with firmware recovery During firmware recovery, the monitor interface is not getting created in the driver and firmware since the respective flags are not updated properly. So after firmware recovery is successful, when monitor interface is brought down manually, firmware assertion is observed, since we are trying to bring down the interface which is not yet created in the firmware. Fix this by updating the monitor flags properly per phy#, during firmware recovery. Tested-on: IPQ8074 hw2.0 AHB WLAN.HK.2.7.0.1-01744-QCAHKSWPL_SILICONZ-1 Signed-off-by: Nagarajan Maran Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20221014155054.11471-1-quic_nmaran@quicinc.com --- drivers/net/wireless/ath/ath11k/core.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/net/wireless/ath/ath11k/core.c b/drivers/net/wireless/ath/ath11k/core.c index 35a5de3a0a8a..0ab2f190f1ec 100644 --- a/drivers/net/wireless/ath/ath11k/core.c +++ b/drivers/net/wireless/ath/ath11k/core.c @@ -1677,6 +1677,10 @@ void ath11k_core_pre_reconfigure_recovery(struct ath11k_base *ab) ath11k_mac_tx_mgmt_pending_free, ar); idr_destroy(&ar->txmgmt_idr); wake_up(&ar->txmgmt_empty_waitq); + + ar->monitor_vdev_id = -1; + clear_bit(ATH11K_FLAG_MONITOR_STARTED, &ar->monitor_flags); + clear_bit(ATH11K_FLAG_MONITOR_VDEV_CREATED, &ar->monitor_flags); } wake_up(&ab->wmi_ab.tx_credits_wq); From e39e739ab57399f46167d453bbdb8ef8d57c6488 Mon Sep 17 00:00:00 2001 From: Florian Lehner Date: Fri, 28 Oct 2022 20:34:05 +0200 Subject: [PATCH 0444/2002] bpf: check max_entries before allocating memory For maps of type BPF_MAP_TYPE_CPUMAP memory is allocated first before checking the max_entries argument. If then max_entries is greater than NR_CPUS additional work needs to be done to free allocated memory before an error is returned. This changes moves the check on max_entries before the allocation happens. Signed-off-by: Florian Lehner Link: https://lore.kernel.org/r/20221028183405.59554-1-dev@der-flo.net Signed-off-by: Martin KaFai Lau --- kernel/bpf/cpumap.c | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/kernel/bpf/cpumap.c b/kernel/bpf/cpumap.c index b5ba34ddd4b6..bb03fdba73bb 100644 --- a/kernel/bpf/cpumap.c +++ b/kernel/bpf/cpumap.c @@ -85,7 +85,6 @@ static struct bpf_map *cpu_map_alloc(union bpf_attr *attr) { u32 value_size = attr->value_size; struct bpf_cpu_map *cmap; - int err = -ENOMEM; if (!bpf_capable()) return ERR_PTR(-EPERM); @@ -97,29 +96,26 @@ static struct bpf_map *cpu_map_alloc(union bpf_attr *attr) attr->map_flags & ~BPF_F_NUMA_NODE) return ERR_PTR(-EINVAL); + /* Pre-limit array size based on NR_CPUS, not final CPU check */ + if (attr->max_entries > NR_CPUS) + return ERR_PTR(-E2BIG); + cmap = bpf_map_area_alloc(sizeof(*cmap), NUMA_NO_NODE); if (!cmap) return ERR_PTR(-ENOMEM); bpf_map_init_from_attr(&cmap->map, attr); - /* Pre-limit array size based on NR_CPUS, not final CPU check */ - if (cmap->map.max_entries > NR_CPUS) { - err = -E2BIG; - goto free_cmap; - } - /* Alloc array for possible remote "destination" CPUs */ cmap->cpu_map = bpf_map_area_alloc(cmap->map.max_entries * sizeof(struct bpf_cpu_map_entry *), cmap->map.numa_node); - if (!cmap->cpu_map) - goto free_cmap; + if (!cmap->cpu_map) { + bpf_map_area_free(cmap); + return ERR_PTR(-ENOMEM); + } return &cmap->map; -free_cmap: - bpf_map_area_free(cmap); - return ERR_PTR(err); } static void get_cpu_map_entry(struct bpf_cpu_map_entry *rcpu) From 068c38ad88ccb09e5e966d4db5cedab0e02b3b95 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Wed, 26 Oct 2022 15:22:14 +0200 Subject: [PATCH 0445/2002] net: Remove the obsolte u64_stats_fetch_*_irq() users (drivers). Now that the 32bit UP oddity is gone and 32bit uses always a sequence count, there is no need for the fetch_irq() variants anymore. Convert to the regular interface. Signed-off-by: Thomas Gleixner Signed-off-by: Sebastian Andrzej Siewior Acked-by: Peter Zijlstra (Intel) Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/alacritech/slic.h | 12 +++---- drivers/net/ethernet/amazon/ena/ena_ethtool.c | 4 +-- drivers/net/ethernet/amazon/ena/ena_netdev.c | 12 +++---- .../net/ethernet/aquantia/atlantic/aq_ring.c | 8 ++--- drivers/net/ethernet/asix/ax88796c_main.c | 4 +-- drivers/net/ethernet/broadcom/b44.c | 8 ++--- drivers/net/ethernet/broadcom/bcmsysport.c | 12 +++---- drivers/net/ethernet/cortina/gemini.c | 24 +++++++------- .../net/ethernet/emulex/benet/be_ethtool.c | 12 +++---- drivers/net/ethernet/emulex/benet/be_main.c | 16 +++++----- .../ethernet/fungible/funeth/funeth_txrx.h | 4 +-- drivers/net/ethernet/google/gve/gve_ethtool.c | 16 +++++----- drivers/net/ethernet/google/gve/gve_main.c | 12 +++---- .../net/ethernet/hisilicon/hns3/hns3_enet.c | 4 +-- drivers/net/ethernet/huawei/hinic/hinic_rx.c | 4 +-- drivers/net/ethernet/huawei/hinic/hinic_tx.c | 4 +-- .../net/ethernet/intel/fm10k/fm10k_netdev.c | 8 ++--- .../net/ethernet/intel/i40e/i40e_ethtool.c | 8 ++--- drivers/net/ethernet/intel/i40e/i40e_main.c | 20 ++++++------ .../net/ethernet/intel/iavf/iavf_ethtool.c | 8 ++--- drivers/net/ethernet/intel/ice/ice_main.c | 4 +-- drivers/net/ethernet/intel/igb/igb_ethtool.c | 12 +++---- drivers/net/ethernet/intel/igb/igb_main.c | 8 ++--- drivers/net/ethernet/intel/igc/igc_ethtool.c | 12 +++---- drivers/net/ethernet/intel/igc/igc_main.c | 8 ++--- .../net/ethernet/intel/ixgbe/ixgbe_ethtool.c | 8 ++--- drivers/net/ethernet/intel/ixgbe/ixgbe_main.c | 8 ++--- drivers/net/ethernet/intel/ixgbevf/ethtool.c | 12 +++---- .../net/ethernet/intel/ixgbevf/ixgbevf_main.c | 8 ++--- drivers/net/ethernet/marvell/mvneta.c | 8 ++--- .../net/ethernet/marvell/mvpp2/mvpp2_main.c | 8 ++--- drivers/net/ethernet/marvell/sky2.c | 8 ++--- drivers/net/ethernet/mediatek/mtk_eth_soc.c | 8 ++--- .../net/ethernet/mellanox/mlxsw/spectrum.c | 4 +-- drivers/net/ethernet/microsoft/mana/mana_en.c | 8 ++--- .../ethernet/microsoft/mana/mana_ethtool.c | 8 ++--- .../ethernet/netronome/nfp/nfp_net_common.c | 8 ++--- .../ethernet/netronome/nfp/nfp_net_ethtool.c | 8 ++--- .../net/ethernet/netronome/nfp/nfp_net_repr.c | 4 +-- drivers/net/ethernet/nvidia/forcedeth.c | 8 ++--- .../net/ethernet/qualcomm/rmnet/rmnet_vnd.c | 4 +-- drivers/net/ethernet/realtek/8139too.c | 8 ++--- drivers/net/ethernet/socionext/sni_ave.c | 8 ++--- drivers/net/ethernet/ti/am65-cpsw-nuss.c | 4 +-- drivers/net/ethernet/ti/netcp_core.c | 8 ++--- drivers/net/ethernet/via/via-rhine.c | 8 ++--- .../net/ethernet/xilinx/xilinx_axienet_main.c | 8 ++--- drivers/net/hyperv/netvsc_drv.c | 32 +++++++++---------- drivers/net/ifb.c | 12 +++---- drivers/net/ipvlan/ipvlan_main.c | 4 +-- drivers/net/loopback.c | 4 +-- drivers/net/macsec.c | 12 +++---- drivers/net/macvlan.c | 4 +-- drivers/net/mhi_net.c | 8 ++--- drivers/net/netdevsim/netdev.c | 4 +-- drivers/net/team/team.c | 4 +-- drivers/net/team/team_mode_loadbalance.c | 4 +-- drivers/net/veth.c | 12 +++---- drivers/net/virtio_net.c | 16 +++++----- drivers/net/vrf.c | 4 +-- drivers/net/vxlan/vxlan_vnifilter.c | 4 +-- drivers/net/wwan/mhi_wwan_mbim.c | 8 ++--- drivers/net/xen-netfront.c | 8 ++--- 63 files changed, 274 insertions(+), 274 deletions(-) diff --git a/drivers/net/ethernet/alacritech/slic.h b/drivers/net/ethernet/alacritech/slic.h index 4eecbdfff3ff..82071d0e5f7f 100644 --- a/drivers/net/ethernet/alacritech/slic.h +++ b/drivers/net/ethernet/alacritech/slic.h @@ -288,13 +288,13 @@ do { \ u64_stats_update_end(&(st)->syncp); \ } while (0) -#define SLIC_GET_STATS_COUNTER(newst, st, counter) \ -{ \ - unsigned int start; \ +#define SLIC_GET_STATS_COUNTER(newst, st, counter) \ +{ \ + unsigned int start; \ do { \ - start = u64_stats_fetch_begin_irq(&(st)->syncp); \ - newst = (st)->counter; \ - } while (u64_stats_fetch_retry_irq(&(st)->syncp, start)); \ + start = u64_stats_fetch_begin(&(st)->syncp); \ + newst = (st)->counter; \ + } while (u64_stats_fetch_retry(&(st)->syncp, start)); \ } struct slic_upr { diff --git a/drivers/net/ethernet/amazon/ena/ena_ethtool.c b/drivers/net/ethernet/amazon/ena/ena_ethtool.c index 98d6386b7f39..48ae6d810f8f 100644 --- a/drivers/net/ethernet/amazon/ena/ena_ethtool.c +++ b/drivers/net/ethernet/amazon/ena/ena_ethtool.c @@ -118,9 +118,9 @@ static void ena_safe_update_stat(u64 *src, u64 *dst, unsigned int start; do { - start = u64_stats_fetch_begin_irq(syncp); + start = u64_stats_fetch_begin(syncp); *(dst) = *src; - } while (u64_stats_fetch_retry_irq(syncp, start)); + } while (u64_stats_fetch_retry(syncp, start)); } static void ena_queue_stats(struct ena_adapter *adapter, u64 **data) diff --git a/drivers/net/ethernet/amazon/ena/ena_netdev.c b/drivers/net/ethernet/amazon/ena/ena_netdev.c index d350eeec8bad..df83f04b0980 100644 --- a/drivers/net/ethernet/amazon/ena/ena_netdev.c +++ b/drivers/net/ethernet/amazon/ena/ena_netdev.c @@ -3268,10 +3268,10 @@ static void ena_get_stats64(struct net_device *netdev, tx_ring = &adapter->tx_ring[i]; do { - start = u64_stats_fetch_begin_irq(&tx_ring->syncp); + start = u64_stats_fetch_begin(&tx_ring->syncp); packets = tx_ring->tx_stats.cnt; bytes = tx_ring->tx_stats.bytes; - } while (u64_stats_fetch_retry_irq(&tx_ring->syncp, start)); + } while (u64_stats_fetch_retry(&tx_ring->syncp, start)); stats->tx_packets += packets; stats->tx_bytes += bytes; @@ -3279,20 +3279,20 @@ static void ena_get_stats64(struct net_device *netdev, rx_ring = &adapter->rx_ring[i]; do { - start = u64_stats_fetch_begin_irq(&rx_ring->syncp); + start = u64_stats_fetch_begin(&rx_ring->syncp); packets = rx_ring->rx_stats.cnt; bytes = rx_ring->rx_stats.bytes; - } while (u64_stats_fetch_retry_irq(&rx_ring->syncp, start)); + } while (u64_stats_fetch_retry(&rx_ring->syncp, start)); stats->rx_packets += packets; stats->rx_bytes += bytes; } do { - start = u64_stats_fetch_begin_irq(&adapter->syncp); + start = u64_stats_fetch_begin(&adapter->syncp); rx_drops = adapter->dev_stats.rx_drops; tx_drops = adapter->dev_stats.tx_drops; - } while (u64_stats_fetch_retry_irq(&adapter->syncp, start)); + } while (u64_stats_fetch_retry(&adapter->syncp, start)); stats->rx_dropped = rx_drops; stats->tx_dropped = tx_drops; diff --git a/drivers/net/ethernet/aquantia/atlantic/aq_ring.c b/drivers/net/ethernet/aquantia/atlantic/aq_ring.c index 25129e723b57..1e8d902e1c8e 100644 --- a/drivers/net/ethernet/aquantia/atlantic/aq_ring.c +++ b/drivers/net/ethernet/aquantia/atlantic/aq_ring.c @@ -934,7 +934,7 @@ unsigned int aq_ring_fill_stats_data(struct aq_ring_s *self, u64 *data) /* This data should mimic aq_ethtool_queue_rx_stat_names structure */ do { count = 0; - start = u64_stats_fetch_begin_irq(&self->stats.rx.syncp); + start = u64_stats_fetch_begin(&self->stats.rx.syncp); data[count] = self->stats.rx.packets; data[++count] = self->stats.rx.jumbo_packets; data[++count] = self->stats.rx.lro_packets; @@ -951,15 +951,15 @@ unsigned int aq_ring_fill_stats_data(struct aq_ring_s *self, u64 *data) data[++count] = self->stats.rx.xdp_tx; data[++count] = self->stats.rx.xdp_invalid; data[++count] = self->stats.rx.xdp_redirect; - } while (u64_stats_fetch_retry_irq(&self->stats.rx.syncp, start)); + } while (u64_stats_fetch_retry(&self->stats.rx.syncp, start)); } else { /* This data should mimic aq_ethtool_queue_tx_stat_names structure */ do { count = 0; - start = u64_stats_fetch_begin_irq(&self->stats.tx.syncp); + start = u64_stats_fetch_begin(&self->stats.tx.syncp); data[count] = self->stats.tx.packets; data[++count] = self->stats.tx.queue_restarts; - } while (u64_stats_fetch_retry_irq(&self->stats.tx.syncp, start)); + } while (u64_stats_fetch_retry(&self->stats.tx.syncp, start)); } return ++count; diff --git a/drivers/net/ethernet/asix/ax88796c_main.c b/drivers/net/ethernet/asix/ax88796c_main.c index 8b7cdf015a16..21376c79f671 100644 --- a/drivers/net/ethernet/asix/ax88796c_main.c +++ b/drivers/net/ethernet/asix/ax88796c_main.c @@ -662,12 +662,12 @@ static void ax88796c_get_stats64(struct net_device *ndev, s = per_cpu_ptr(ax_local->stats, cpu); do { - start = u64_stats_fetch_begin_irq(&s->syncp); + start = u64_stats_fetch_begin(&s->syncp); rx_packets = u64_stats_read(&s->rx_packets); rx_bytes = u64_stats_read(&s->rx_bytes); tx_packets = u64_stats_read(&s->tx_packets); tx_bytes = u64_stats_read(&s->tx_bytes); - } while (u64_stats_fetch_retry_irq(&s->syncp, start)); + } while (u64_stats_fetch_retry(&s->syncp, start)); stats->rx_packets += rx_packets; stats->rx_bytes += rx_bytes; diff --git a/drivers/net/ethernet/broadcom/b44.c b/drivers/net/ethernet/broadcom/b44.c index 7f876721596c..b751dc8486dc 100644 --- a/drivers/net/ethernet/broadcom/b44.c +++ b/drivers/net/ethernet/broadcom/b44.c @@ -1680,7 +1680,7 @@ static void b44_get_stats64(struct net_device *dev, unsigned int start; do { - start = u64_stats_fetch_begin_irq(&hwstat->syncp); + start = u64_stats_fetch_begin(&hwstat->syncp); /* Convert HW stats into rtnl_link_stats64 stats. */ nstat->rx_packets = hwstat->rx_pkts; @@ -1714,7 +1714,7 @@ static void b44_get_stats64(struct net_device *dev, /* Carrier lost counter seems to be broken for some devices */ nstat->tx_carrier_errors = hwstat->tx_carrier_lost; #endif - } while (u64_stats_fetch_retry_irq(&hwstat->syncp, start)); + } while (u64_stats_fetch_retry(&hwstat->syncp, start)); } @@ -2082,12 +2082,12 @@ static void b44_get_ethtool_stats(struct net_device *dev, do { data_src = &hwstat->tx_good_octets; data_dst = data; - start = u64_stats_fetch_begin_irq(&hwstat->syncp); + start = u64_stats_fetch_begin(&hwstat->syncp); for (i = 0; i < ARRAY_SIZE(b44_gstrings); i++) *data_dst++ = *data_src++; - } while (u64_stats_fetch_retry_irq(&hwstat->syncp, start)); + } while (u64_stats_fetch_retry(&hwstat->syncp, start)); } static void b44_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol) diff --git a/drivers/net/ethernet/broadcom/bcmsysport.c b/drivers/net/ethernet/broadcom/bcmsysport.c index 425d6ccd5413..f8b1adc389b3 100644 --- a/drivers/net/ethernet/broadcom/bcmsysport.c +++ b/drivers/net/ethernet/broadcom/bcmsysport.c @@ -457,10 +457,10 @@ static void bcm_sysport_update_tx_stats(struct bcm_sysport_priv *priv, for (q = 0; q < priv->netdev->num_tx_queues; q++) { ring = &priv->tx_rings[q]; do { - start = u64_stats_fetch_begin_irq(&priv->syncp); + start = u64_stats_fetch_begin(&priv->syncp); bytes = ring->bytes; packets = ring->packets; - } while (u64_stats_fetch_retry_irq(&priv->syncp, start)); + } while (u64_stats_fetch_retry(&priv->syncp, start)); *tx_bytes += bytes; *tx_packets += packets; @@ -504,9 +504,9 @@ static void bcm_sysport_get_stats(struct net_device *dev, if (s->stat_sizeof == sizeof(u64) && s->type == BCM_SYSPORT_STAT_NETDEV64) { do { - start = u64_stats_fetch_begin_irq(syncp); + start = u64_stats_fetch_begin(syncp); data[i] = *(u64 *)p; - } while (u64_stats_fetch_retry_irq(syncp, start)); + } while (u64_stats_fetch_retry(syncp, start)); } else data[i] = *(u32 *)p; j++; @@ -1878,10 +1878,10 @@ static void bcm_sysport_get_stats64(struct net_device *dev, &stats->tx_packets); do { - start = u64_stats_fetch_begin_irq(&priv->syncp); + start = u64_stats_fetch_begin(&priv->syncp); stats->rx_packets = stats64->rx_packets; stats->rx_bytes = stats64->rx_bytes; - } while (u64_stats_fetch_retry_irq(&priv->syncp, start)); + } while (u64_stats_fetch_retry(&priv->syncp, start)); } static void bcm_sysport_netif_start(struct net_device *dev) diff --git a/drivers/net/ethernet/cortina/gemini.c b/drivers/net/ethernet/cortina/gemini.c index fdf10318758b..5715b9ab2712 100644 --- a/drivers/net/ethernet/cortina/gemini.c +++ b/drivers/net/ethernet/cortina/gemini.c @@ -1919,7 +1919,7 @@ static void gmac_get_stats64(struct net_device *netdev, /* Racing with RX NAPI */ do { - start = u64_stats_fetch_begin_irq(&port->rx_stats_syncp); + start = u64_stats_fetch_begin(&port->rx_stats_syncp); stats->rx_packets = port->stats.rx_packets; stats->rx_bytes = port->stats.rx_bytes; @@ -1931,11 +1931,11 @@ static void gmac_get_stats64(struct net_device *netdev, stats->rx_crc_errors = port->stats.rx_crc_errors; stats->rx_frame_errors = port->stats.rx_frame_errors; - } while (u64_stats_fetch_retry_irq(&port->rx_stats_syncp, start)); + } while (u64_stats_fetch_retry(&port->rx_stats_syncp, start)); /* Racing with MIB and TX completion interrupts */ do { - start = u64_stats_fetch_begin_irq(&port->ir_stats_syncp); + start = u64_stats_fetch_begin(&port->ir_stats_syncp); stats->tx_errors = port->stats.tx_errors; stats->tx_packets = port->stats.tx_packets; @@ -1945,15 +1945,15 @@ static void gmac_get_stats64(struct net_device *netdev, stats->rx_missed_errors = port->stats.rx_missed_errors; stats->rx_fifo_errors = port->stats.rx_fifo_errors; - } while (u64_stats_fetch_retry_irq(&port->ir_stats_syncp, start)); + } while (u64_stats_fetch_retry(&port->ir_stats_syncp, start)); /* Racing with hard_start_xmit */ do { - start = u64_stats_fetch_begin_irq(&port->tx_stats_syncp); + start = u64_stats_fetch_begin(&port->tx_stats_syncp); stats->tx_dropped = port->stats.tx_dropped; - } while (u64_stats_fetch_retry_irq(&port->tx_stats_syncp, start)); + } while (u64_stats_fetch_retry(&port->tx_stats_syncp, start)); stats->rx_dropped += stats->rx_missed_errors; } @@ -2031,18 +2031,18 @@ static void gmac_get_ethtool_stats(struct net_device *netdev, /* Racing with MIB interrupt */ do { p = values; - start = u64_stats_fetch_begin_irq(&port->ir_stats_syncp); + start = u64_stats_fetch_begin(&port->ir_stats_syncp); for (i = 0; i < RX_STATS_NUM; i++) *p++ = port->hw_stats[i]; - } while (u64_stats_fetch_retry_irq(&port->ir_stats_syncp, start)); + } while (u64_stats_fetch_retry(&port->ir_stats_syncp, start)); values = p; /* Racing with RX NAPI */ do { p = values; - start = u64_stats_fetch_begin_irq(&port->rx_stats_syncp); + start = u64_stats_fetch_begin(&port->rx_stats_syncp); for (i = 0; i < RX_STATUS_NUM; i++) *p++ = port->rx_stats[i]; @@ -2050,13 +2050,13 @@ static void gmac_get_ethtool_stats(struct net_device *netdev, *p++ = port->rx_csum_stats[i]; *p++ = port->rx_napi_exits; - } while (u64_stats_fetch_retry_irq(&port->rx_stats_syncp, start)); + } while (u64_stats_fetch_retry(&port->rx_stats_syncp, start)); values = p; /* Racing with TX start_xmit */ do { p = values; - start = u64_stats_fetch_begin_irq(&port->tx_stats_syncp); + start = u64_stats_fetch_begin(&port->tx_stats_syncp); for (i = 0; i < TX_MAX_FRAGS; i++) { *values++ = port->tx_frag_stats[i]; @@ -2065,7 +2065,7 @@ static void gmac_get_ethtool_stats(struct net_device *netdev, *values++ = port->tx_frags_linearized; *values++ = port->tx_hw_csummed; - } while (u64_stats_fetch_retry_irq(&port->tx_stats_syncp, start)); + } while (u64_stats_fetch_retry(&port->tx_stats_syncp, start)); } static int gmac_get_ksettings(struct net_device *netdev, diff --git a/drivers/net/ethernet/emulex/benet/be_ethtool.c b/drivers/net/ethernet/emulex/benet/be_ethtool.c index 77edc3d9b505..a29de29bdf23 100644 --- a/drivers/net/ethernet/emulex/benet/be_ethtool.c +++ b/drivers/net/ethernet/emulex/benet/be_ethtool.c @@ -389,10 +389,10 @@ static void be_get_ethtool_stats(struct net_device *netdev, struct be_rx_stats *stats = rx_stats(rxo); do { - start = u64_stats_fetch_begin_irq(&stats->sync); + start = u64_stats_fetch_begin(&stats->sync); data[base] = stats->rx_bytes; data[base + 1] = stats->rx_pkts; - } while (u64_stats_fetch_retry_irq(&stats->sync, start)); + } while (u64_stats_fetch_retry(&stats->sync, start)); for (i = 2; i < ETHTOOL_RXSTATS_NUM; i++) { p = (u8 *)stats + et_rx_stats[i].offset; @@ -405,19 +405,19 @@ static void be_get_ethtool_stats(struct net_device *netdev, struct be_tx_stats *stats = tx_stats(txo); do { - start = u64_stats_fetch_begin_irq(&stats->sync_compl); + start = u64_stats_fetch_begin(&stats->sync_compl); data[base] = stats->tx_compl; - } while (u64_stats_fetch_retry_irq(&stats->sync_compl, start)); + } while (u64_stats_fetch_retry(&stats->sync_compl, start)); do { - start = u64_stats_fetch_begin_irq(&stats->sync); + start = u64_stats_fetch_begin(&stats->sync); for (i = 1; i < ETHTOOL_TXSTATS_NUM; i++) { p = (u8 *)stats + et_tx_stats[i].offset; data[base + i] = (et_tx_stats[i].size == sizeof(u64)) ? *(u64 *)p : *(u32 *)p; } - } while (u64_stats_fetch_retry_irq(&stats->sync, start)); + } while (u64_stats_fetch_retry(&stats->sync, start)); base += ETHTOOL_TXSTATS_NUM; } } diff --git a/drivers/net/ethernet/emulex/benet/be_main.c b/drivers/net/ethernet/emulex/benet/be_main.c index a92a74761546..46fe3d74e2e9 100644 --- a/drivers/net/ethernet/emulex/benet/be_main.c +++ b/drivers/net/ethernet/emulex/benet/be_main.c @@ -665,10 +665,10 @@ static void be_get_stats64(struct net_device *netdev, const struct be_rx_stats *rx_stats = rx_stats(rxo); do { - start = u64_stats_fetch_begin_irq(&rx_stats->sync); + start = u64_stats_fetch_begin(&rx_stats->sync); pkts = rx_stats(rxo)->rx_pkts; bytes = rx_stats(rxo)->rx_bytes; - } while (u64_stats_fetch_retry_irq(&rx_stats->sync, start)); + } while (u64_stats_fetch_retry(&rx_stats->sync, start)); stats->rx_packets += pkts; stats->rx_bytes += bytes; stats->multicast += rx_stats(rxo)->rx_mcast_pkts; @@ -680,10 +680,10 @@ static void be_get_stats64(struct net_device *netdev, const struct be_tx_stats *tx_stats = tx_stats(txo); do { - start = u64_stats_fetch_begin_irq(&tx_stats->sync); + start = u64_stats_fetch_begin(&tx_stats->sync); pkts = tx_stats(txo)->tx_pkts; bytes = tx_stats(txo)->tx_bytes; - } while (u64_stats_fetch_retry_irq(&tx_stats->sync, start)); + } while (u64_stats_fetch_retry(&tx_stats->sync, start)); stats->tx_packets += pkts; stats->tx_bytes += bytes; } @@ -2155,16 +2155,16 @@ static int be_get_new_eqd(struct be_eq_obj *eqo) for_all_rx_queues_on_eq(adapter, eqo, rxo, i) { do { - start = u64_stats_fetch_begin_irq(&rxo->stats.sync); + start = u64_stats_fetch_begin(&rxo->stats.sync); rx_pkts += rxo->stats.rx_pkts; - } while (u64_stats_fetch_retry_irq(&rxo->stats.sync, start)); + } while (u64_stats_fetch_retry(&rxo->stats.sync, start)); } for_all_tx_queues_on_eq(adapter, eqo, txo, i) { do { - start = u64_stats_fetch_begin_irq(&txo->stats.sync); + start = u64_stats_fetch_begin(&txo->stats.sync); tx_pkts += txo->stats.tx_reqs; - } while (u64_stats_fetch_retry_irq(&txo->stats.sync, start)); + } while (u64_stats_fetch_retry(&txo->stats.sync, start)); } /* Skip, if wrapped around or first calculation */ diff --git a/drivers/net/ethernet/fungible/funeth/funeth_txrx.h b/drivers/net/ethernet/fungible/funeth/funeth_txrx.h index 671f51135c26..53b7e95213a8 100644 --- a/drivers/net/ethernet/fungible/funeth/funeth_txrx.h +++ b/drivers/net/ethernet/fungible/funeth/funeth_txrx.h @@ -206,9 +206,9 @@ struct funeth_rxq { #define FUN_QSTAT_READ(q, seq, stats_copy) \ do { \ - seq = u64_stats_fetch_begin_irq(&(q)->syncp); \ + seq = u64_stats_fetch_begin(&(q)->syncp); \ stats_copy = (q)->stats; \ - } while (u64_stats_fetch_retry_irq(&(q)->syncp, (seq))) + } while (u64_stats_fetch_retry(&(q)->syncp, (seq))) #define FUN_INT_NAME_LEN (IFNAMSIZ + 16) diff --git a/drivers/net/ethernet/google/gve/gve_ethtool.c b/drivers/net/ethernet/google/gve/gve_ethtool.c index 7b9a2d9d9624..50b384910c83 100644 --- a/drivers/net/ethernet/google/gve/gve_ethtool.c +++ b/drivers/net/ethernet/google/gve/gve_ethtool.c @@ -177,14 +177,14 @@ gve_get_ethtool_stats(struct net_device *netdev, struct gve_rx_ring *rx = &priv->rx[ring]; start = - u64_stats_fetch_begin_irq(&priv->rx[ring].statss); + u64_stats_fetch_begin(&priv->rx[ring].statss); tmp_rx_pkts = rx->rpackets; tmp_rx_bytes = rx->rbytes; tmp_rx_skb_alloc_fail = rx->rx_skb_alloc_fail; tmp_rx_buf_alloc_fail = rx->rx_buf_alloc_fail; tmp_rx_desc_err_dropped_pkt = rx->rx_desc_err_dropped_pkt; - } while (u64_stats_fetch_retry_irq(&priv->rx[ring].statss, + } while (u64_stats_fetch_retry(&priv->rx[ring].statss, start)); rx_pkts += tmp_rx_pkts; rx_bytes += tmp_rx_bytes; @@ -198,10 +198,10 @@ gve_get_ethtool_stats(struct net_device *netdev, if (priv->tx) { do { start = - u64_stats_fetch_begin_irq(&priv->tx[ring].statss); + u64_stats_fetch_begin(&priv->tx[ring].statss); tmp_tx_pkts = priv->tx[ring].pkt_done; tmp_tx_bytes = priv->tx[ring].bytes_done; - } while (u64_stats_fetch_retry_irq(&priv->tx[ring].statss, + } while (u64_stats_fetch_retry(&priv->tx[ring].statss, start)); tx_pkts += tmp_tx_pkts; tx_bytes += tmp_tx_bytes; @@ -259,13 +259,13 @@ gve_get_ethtool_stats(struct net_device *netdev, data[i++] = rx->fill_cnt - rx->cnt; do { start = - u64_stats_fetch_begin_irq(&priv->rx[ring].statss); + u64_stats_fetch_begin(&priv->rx[ring].statss); tmp_rx_bytes = rx->rbytes; tmp_rx_skb_alloc_fail = rx->rx_skb_alloc_fail; tmp_rx_buf_alloc_fail = rx->rx_buf_alloc_fail; tmp_rx_desc_err_dropped_pkt = rx->rx_desc_err_dropped_pkt; - } while (u64_stats_fetch_retry_irq(&priv->rx[ring].statss, + } while (u64_stats_fetch_retry(&priv->rx[ring].statss, start)); data[i++] = tmp_rx_bytes; data[i++] = rx->rx_cont_packet_cnt; @@ -331,9 +331,9 @@ gve_get_ethtool_stats(struct net_device *netdev, } do { start = - u64_stats_fetch_begin_irq(&priv->tx[ring].statss); + u64_stats_fetch_begin(&priv->tx[ring].statss); tmp_tx_bytes = tx->bytes_done; - } while (u64_stats_fetch_retry_irq(&priv->tx[ring].statss, + } while (u64_stats_fetch_retry(&priv->tx[ring].statss, start)); data[i++] = tmp_tx_bytes; data[i++] = tx->wake_queue; diff --git a/drivers/net/ethernet/google/gve/gve_main.c b/drivers/net/ethernet/google/gve/gve_main.c index d3e3ac242bfc..5a229a01f49d 100644 --- a/drivers/net/ethernet/google/gve/gve_main.c +++ b/drivers/net/ethernet/google/gve/gve_main.c @@ -51,10 +51,10 @@ static void gve_get_stats(struct net_device *dev, struct rtnl_link_stats64 *s) for (ring = 0; ring < priv->rx_cfg.num_queues; ring++) { do { start = - u64_stats_fetch_begin_irq(&priv->rx[ring].statss); + u64_stats_fetch_begin(&priv->rx[ring].statss); packets = priv->rx[ring].rpackets; bytes = priv->rx[ring].rbytes; - } while (u64_stats_fetch_retry_irq(&priv->rx[ring].statss, + } while (u64_stats_fetch_retry(&priv->rx[ring].statss, start)); s->rx_packets += packets; s->rx_bytes += bytes; @@ -64,10 +64,10 @@ static void gve_get_stats(struct net_device *dev, struct rtnl_link_stats64 *s) for (ring = 0; ring < priv->tx_cfg.num_queues; ring++) { do { start = - u64_stats_fetch_begin_irq(&priv->tx[ring].statss); + u64_stats_fetch_begin(&priv->tx[ring].statss); packets = priv->tx[ring].pkt_done; bytes = priv->tx[ring].bytes_done; - } while (u64_stats_fetch_retry_irq(&priv->tx[ring].statss, + } while (u64_stats_fetch_retry(&priv->tx[ring].statss, start)); s->tx_packets += packets; s->tx_bytes += bytes; @@ -1273,9 +1273,9 @@ void gve_handle_report_stats(struct gve_priv *priv) } do { - start = u64_stats_fetch_begin_irq(&priv->tx[idx].statss); + start = u64_stats_fetch_begin(&priv->tx[idx].statss); tx_bytes = priv->tx[idx].bytes_done; - } while (u64_stats_fetch_retry_irq(&priv->tx[idx].statss, start)); + } while (u64_stats_fetch_retry(&priv->tx[idx].statss, start)); stats[stats_idx++] = (struct stats) { .stat_name = cpu_to_be32(TX_WAKE_CNT), .value = cpu_to_be64(priv->tx[idx].wake_queue), diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c b/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c index 4cb2421e71a7..813d5b3d7b58 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c @@ -2486,7 +2486,7 @@ static void hns3_fetch_stats(struct rtnl_link_stats64 *stats, unsigned int start; do { - start = u64_stats_fetch_begin_irq(&ring->syncp); + start = u64_stats_fetch_begin(&ring->syncp); if (is_tx) { stats->tx_bytes += ring->stats.tx_bytes; stats->tx_packets += ring->stats.tx_pkts; @@ -2520,7 +2520,7 @@ static void hns3_fetch_stats(struct rtnl_link_stats64 *stats, stats->multicast += ring->stats.rx_multicast; stats->rx_length_errors += ring->stats.err_pkt_len; } - } while (u64_stats_fetch_retry_irq(&ring->syncp, start)); + } while (u64_stats_fetch_retry(&ring->syncp, start)); } static void hns3_nic_get_stats64(struct net_device *netdev, diff --git a/drivers/net/ethernet/huawei/hinic/hinic_rx.c b/drivers/net/ethernet/huawei/hinic/hinic_rx.c index d649c6e323c8..ceec8be2a73b 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_rx.c +++ b/drivers/net/ethernet/huawei/hinic/hinic_rx.c @@ -74,14 +74,14 @@ void hinic_rxq_get_stats(struct hinic_rxq *rxq, struct hinic_rxq_stats *stats) unsigned int start; do { - start = u64_stats_fetch_begin_irq(&rxq_stats->syncp); + start = u64_stats_fetch_begin(&rxq_stats->syncp); stats->pkts = rxq_stats->pkts; stats->bytes = rxq_stats->bytes; stats->errors = rxq_stats->csum_errors + rxq_stats->other_errors; stats->csum_errors = rxq_stats->csum_errors; stats->other_errors = rxq_stats->other_errors; - } while (u64_stats_fetch_retry_irq(&rxq_stats->syncp, start)); + } while (u64_stats_fetch_retry(&rxq_stats->syncp, start)); } /** diff --git a/drivers/net/ethernet/huawei/hinic/hinic_tx.c b/drivers/net/ethernet/huawei/hinic/hinic_tx.c index e91476c8ff8b..ad47ac51a139 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_tx.c +++ b/drivers/net/ethernet/huawei/hinic/hinic_tx.c @@ -99,14 +99,14 @@ void hinic_txq_get_stats(struct hinic_txq *txq, struct hinic_txq_stats *stats) unsigned int start; do { - start = u64_stats_fetch_begin_irq(&txq_stats->syncp); + start = u64_stats_fetch_begin(&txq_stats->syncp); stats->pkts = txq_stats->pkts; stats->bytes = txq_stats->bytes; stats->tx_busy = txq_stats->tx_busy; stats->tx_wake = txq_stats->tx_wake; stats->tx_dropped = txq_stats->tx_dropped; stats->big_frags_pkts = txq_stats->big_frags_pkts; - } while (u64_stats_fetch_retry_irq(&txq_stats->syncp, start)); + } while (u64_stats_fetch_retry(&txq_stats->syncp, start)); } /** diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_netdev.c b/drivers/net/ethernet/intel/fm10k/fm10k_netdev.c index 2cca9e84e31e..34ab5ff9823b 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k_netdev.c +++ b/drivers/net/ethernet/intel/fm10k/fm10k_netdev.c @@ -1229,10 +1229,10 @@ static void fm10k_get_stats64(struct net_device *netdev, continue; do { - start = u64_stats_fetch_begin_irq(&ring->syncp); + start = u64_stats_fetch_begin(&ring->syncp); packets = ring->stats.packets; bytes = ring->stats.bytes; - } while (u64_stats_fetch_retry_irq(&ring->syncp, start)); + } while (u64_stats_fetch_retry(&ring->syncp, start)); stats->rx_packets += packets; stats->rx_bytes += bytes; @@ -1245,10 +1245,10 @@ static void fm10k_get_stats64(struct net_device *netdev, continue; do { - start = u64_stats_fetch_begin_irq(&ring->syncp); + start = u64_stats_fetch_begin(&ring->syncp); packets = ring->stats.packets; bytes = ring->stats.bytes; - } while (u64_stats_fetch_retry_irq(&ring->syncp, start)); + } while (u64_stats_fetch_retry(&ring->syncp, start)); stats->tx_packets += packets; stats->tx_bytes += bytes; diff --git a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c index 4a6a6e48c615..cc269f9db99f 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c +++ b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c @@ -154,7 +154,7 @@ __i40e_add_ethtool_stats(u64 **data, void *pointer, * @ring: the ring to copy * * Queue statistics must be copied while protected by - * u64_stats_fetch_begin_irq, so we can't directly use i40e_add_ethtool_stats. + * u64_stats_fetch_begin, so we can't directly use i40e_add_ethtool_stats. * Assumes that queue stats are defined in i40e_gstrings_queue_stats. If the * ring pointer is null, zero out the queue stat values and update the data * pointer. Otherwise safely copy the stats from the ring into the supplied @@ -172,16 +172,16 @@ i40e_add_queue_stats(u64 **data, struct i40e_ring *ring) /* To avoid invalid statistics values, ensure that we keep retrying * the copy until we get a consistent value according to - * u64_stats_fetch_retry_irq. But first, make sure our ring is + * u64_stats_fetch_retry. But first, make sure our ring is * non-null before attempting to access its syncp. */ do { - start = !ring ? 0 : u64_stats_fetch_begin_irq(&ring->syncp); + start = !ring ? 0 : u64_stats_fetch_begin(&ring->syncp); for (i = 0; i < size; i++) { i40e_add_one_ethtool_stat(&(*data)[i], ring, &stats[i]); } - } while (ring && u64_stats_fetch_retry_irq(&ring->syncp, start)); + } while (ring && u64_stats_fetch_retry(&ring->syncp, start)); /* Once we successfully copy the stats in, update the data pointer */ *data += size; diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index b5dcd15ced36..1a1fab94205d 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -419,10 +419,10 @@ static void i40e_get_netdev_stats_struct_tx(struct i40e_ring *ring, unsigned int start; do { - start = u64_stats_fetch_begin_irq(&ring->syncp); + start = u64_stats_fetch_begin(&ring->syncp); packets = ring->stats.packets; bytes = ring->stats.bytes; - } while (u64_stats_fetch_retry_irq(&ring->syncp, start)); + } while (u64_stats_fetch_retry(&ring->syncp, start)); stats->tx_packets += packets; stats->tx_bytes += bytes; @@ -472,10 +472,10 @@ static void i40e_get_netdev_stats_struct(struct net_device *netdev, if (!ring) continue; do { - start = u64_stats_fetch_begin_irq(&ring->syncp); + start = u64_stats_fetch_begin(&ring->syncp); packets = ring->stats.packets; bytes = ring->stats.bytes; - } while (u64_stats_fetch_retry_irq(&ring->syncp, start)); + } while (u64_stats_fetch_retry(&ring->syncp, start)); stats->rx_packets += packets; stats->rx_bytes += bytes; @@ -897,10 +897,10 @@ static void i40e_update_vsi_stats(struct i40e_vsi *vsi) continue; do { - start = u64_stats_fetch_begin_irq(&p->syncp); + start = u64_stats_fetch_begin(&p->syncp); packets = p->stats.packets; bytes = p->stats.bytes; - } while (u64_stats_fetch_retry_irq(&p->syncp, start)); + } while (u64_stats_fetch_retry(&p->syncp, start)); tx_b += bytes; tx_p += packets; tx_restart += p->tx_stats.restart_queue; @@ -915,10 +915,10 @@ static void i40e_update_vsi_stats(struct i40e_vsi *vsi) continue; do { - start = u64_stats_fetch_begin_irq(&p->syncp); + start = u64_stats_fetch_begin(&p->syncp); packets = p->stats.packets; bytes = p->stats.bytes; - } while (u64_stats_fetch_retry_irq(&p->syncp, start)); + } while (u64_stats_fetch_retry(&p->syncp, start)); rx_b += bytes; rx_p += packets; rx_buf += p->rx_stats.alloc_buff_failed; @@ -935,10 +935,10 @@ static void i40e_update_vsi_stats(struct i40e_vsi *vsi) continue; do { - start = u64_stats_fetch_begin_irq(&p->syncp); + start = u64_stats_fetch_begin(&p->syncp); packets = p->stats.packets; bytes = p->stats.bytes; - } while (u64_stats_fetch_retry_irq(&p->syncp, start)); + } while (u64_stats_fetch_retry(&p->syncp, start)); tx_b += bytes; tx_p += packets; tx_restart += p->tx_stats.restart_queue; diff --git a/drivers/net/ethernet/intel/iavf/iavf_ethtool.c b/drivers/net/ethernet/intel/iavf/iavf_ethtool.c index a056e1545615..d79ead5e8d0c 100644 --- a/drivers/net/ethernet/intel/iavf/iavf_ethtool.c +++ b/drivers/net/ethernet/intel/iavf/iavf_ethtool.c @@ -147,7 +147,7 @@ __iavf_add_ethtool_stats(u64 **data, void *pointer, * @ring: the ring to copy * * Queue statistics must be copied while protected by - * u64_stats_fetch_begin_irq, so we can't directly use iavf_add_ethtool_stats. + * u64_stats_fetch_begin, so we can't directly use iavf_add_ethtool_stats. * Assumes that queue stats are defined in iavf_gstrings_queue_stats. If the * ring pointer is null, zero out the queue stat values and update the data * pointer. Otherwise safely copy the stats from the ring into the supplied @@ -165,14 +165,14 @@ iavf_add_queue_stats(u64 **data, struct iavf_ring *ring) /* To avoid invalid statistics values, ensure that we keep retrying * the copy until we get a consistent value according to - * u64_stats_fetch_retry_irq. But first, make sure our ring is + * u64_stats_fetch_retry. But first, make sure our ring is * non-null before attempting to access its syncp. */ do { - start = !ring ? 0 : u64_stats_fetch_begin_irq(&ring->syncp); + start = !ring ? 0 : u64_stats_fetch_begin(&ring->syncp); for (i = 0; i < size; i++) iavf_add_one_ethtool_stat(&(*data)[i], ring, &stats[i]); - } while (ring && u64_stats_fetch_retry_irq(&ring->syncp, start)); + } while (ring && u64_stats_fetch_retry(&ring->syncp, start)); /* Once we successfully copy the stats in, update the data pointer */ *data += size; diff --git a/drivers/net/ethernet/intel/ice/ice_main.c b/drivers/net/ethernet/intel/ice/ice_main.c index df65e829ea33..1f27dc20b4f1 100644 --- a/drivers/net/ethernet/intel/ice/ice_main.c +++ b/drivers/net/ethernet/intel/ice/ice_main.c @@ -6370,10 +6370,10 @@ ice_fetch_u64_stats_per_ring(struct u64_stats_sync *syncp, unsigned int start; do { - start = u64_stats_fetch_begin_irq(syncp); + start = u64_stats_fetch_begin(syncp); *pkts = stats.pkts; *bytes = stats.bytes; - } while (u64_stats_fetch_retry_irq(syncp, start)); + } while (u64_stats_fetch_retry(syncp, start)); } /** diff --git a/drivers/net/ethernet/intel/igb/igb_ethtool.c b/drivers/net/ethernet/intel/igb/igb_ethtool.c index e5f3e7680dc6..36acec89d3d4 100644 --- a/drivers/net/ethernet/intel/igb/igb_ethtool.c +++ b/drivers/net/ethernet/intel/igb/igb_ethtool.c @@ -2311,15 +2311,15 @@ static void igb_get_ethtool_stats(struct net_device *netdev, ring = adapter->tx_ring[j]; do { - start = u64_stats_fetch_begin_irq(&ring->tx_syncp); + start = u64_stats_fetch_begin(&ring->tx_syncp); data[i] = ring->tx_stats.packets; data[i+1] = ring->tx_stats.bytes; data[i+2] = ring->tx_stats.restart_queue; - } while (u64_stats_fetch_retry_irq(&ring->tx_syncp, start)); + } while (u64_stats_fetch_retry(&ring->tx_syncp, start)); do { - start = u64_stats_fetch_begin_irq(&ring->tx_syncp2); + start = u64_stats_fetch_begin(&ring->tx_syncp2); restart2 = ring->tx_stats.restart_queue2; - } while (u64_stats_fetch_retry_irq(&ring->tx_syncp2, start)); + } while (u64_stats_fetch_retry(&ring->tx_syncp2, start)); data[i+2] += restart2; i += IGB_TX_QUEUE_STATS_LEN; @@ -2327,13 +2327,13 @@ static void igb_get_ethtool_stats(struct net_device *netdev, for (j = 0; j < adapter->num_rx_queues; j++) { ring = adapter->rx_ring[j]; do { - start = u64_stats_fetch_begin_irq(&ring->rx_syncp); + start = u64_stats_fetch_begin(&ring->rx_syncp); data[i] = ring->rx_stats.packets; data[i+1] = ring->rx_stats.bytes; data[i+2] = ring->rx_stats.drops; data[i+3] = ring->rx_stats.csum_err; data[i+4] = ring->rx_stats.alloc_failed; - } while (u64_stats_fetch_retry_irq(&ring->rx_syncp, start)); + } while (u64_stats_fetch_retry(&ring->rx_syncp, start)); i += IGB_RX_QUEUE_STATS_LEN; } spin_unlock(&adapter->stats64_lock); diff --git a/drivers/net/ethernet/intel/igb/igb_main.c b/drivers/net/ethernet/intel/igb/igb_main.c index f8e32833226c..d6c1c2e66f26 100644 --- a/drivers/net/ethernet/intel/igb/igb_main.c +++ b/drivers/net/ethernet/intel/igb/igb_main.c @@ -6632,10 +6632,10 @@ void igb_update_stats(struct igb_adapter *adapter) } do { - start = u64_stats_fetch_begin_irq(&ring->rx_syncp); + start = u64_stats_fetch_begin(&ring->rx_syncp); _bytes = ring->rx_stats.bytes; _packets = ring->rx_stats.packets; - } while (u64_stats_fetch_retry_irq(&ring->rx_syncp, start)); + } while (u64_stats_fetch_retry(&ring->rx_syncp, start)); bytes += _bytes; packets += _packets; } @@ -6648,10 +6648,10 @@ void igb_update_stats(struct igb_adapter *adapter) for (i = 0; i < adapter->num_tx_queues; i++) { struct igb_ring *ring = adapter->tx_ring[i]; do { - start = u64_stats_fetch_begin_irq(&ring->tx_syncp); + start = u64_stats_fetch_begin(&ring->tx_syncp); _bytes = ring->tx_stats.bytes; _packets = ring->tx_stats.packets; - } while (u64_stats_fetch_retry_irq(&ring->tx_syncp, start)); + } while (u64_stats_fetch_retry(&ring->tx_syncp, start)); bytes += _bytes; packets += _packets; } diff --git a/drivers/net/ethernet/intel/igc/igc_ethtool.c b/drivers/net/ethernet/intel/igc/igc_ethtool.c index 8cc077b712ad..5a26a7805ef8 100644 --- a/drivers/net/ethernet/intel/igc/igc_ethtool.c +++ b/drivers/net/ethernet/intel/igc/igc_ethtool.c @@ -839,15 +839,15 @@ static void igc_ethtool_get_stats(struct net_device *netdev, ring = adapter->tx_ring[j]; do { - start = u64_stats_fetch_begin_irq(&ring->tx_syncp); + start = u64_stats_fetch_begin(&ring->tx_syncp); data[i] = ring->tx_stats.packets; data[i + 1] = ring->tx_stats.bytes; data[i + 2] = ring->tx_stats.restart_queue; - } while (u64_stats_fetch_retry_irq(&ring->tx_syncp, start)); + } while (u64_stats_fetch_retry(&ring->tx_syncp, start)); do { - start = u64_stats_fetch_begin_irq(&ring->tx_syncp2); + start = u64_stats_fetch_begin(&ring->tx_syncp2); restart2 = ring->tx_stats.restart_queue2; - } while (u64_stats_fetch_retry_irq(&ring->tx_syncp2, start)); + } while (u64_stats_fetch_retry(&ring->tx_syncp2, start)); data[i + 2] += restart2; i += IGC_TX_QUEUE_STATS_LEN; @@ -855,13 +855,13 @@ static void igc_ethtool_get_stats(struct net_device *netdev, for (j = 0; j < adapter->num_rx_queues; j++) { ring = adapter->rx_ring[j]; do { - start = u64_stats_fetch_begin_irq(&ring->rx_syncp); + start = u64_stats_fetch_begin(&ring->rx_syncp); data[i] = ring->rx_stats.packets; data[i + 1] = ring->rx_stats.bytes; data[i + 2] = ring->rx_stats.drops; data[i + 3] = ring->rx_stats.csum_err; data[i + 4] = ring->rx_stats.alloc_failed; - } while (u64_stats_fetch_retry_irq(&ring->rx_syncp, start)); + } while (u64_stats_fetch_retry(&ring->rx_syncp, start)); i += IGC_RX_QUEUE_STATS_LEN; } spin_unlock(&adapter->stats64_lock); diff --git a/drivers/net/ethernet/intel/igc/igc_main.c b/drivers/net/ethernet/intel/igc/igc_main.c index 34889be63e78..5d307b6e660b 100644 --- a/drivers/net/ethernet/intel/igc/igc_main.c +++ b/drivers/net/ethernet/intel/igc/igc_main.c @@ -4682,10 +4682,10 @@ void igc_update_stats(struct igc_adapter *adapter) } do { - start = u64_stats_fetch_begin_irq(&ring->rx_syncp); + start = u64_stats_fetch_begin(&ring->rx_syncp); _bytes = ring->rx_stats.bytes; _packets = ring->rx_stats.packets; - } while (u64_stats_fetch_retry_irq(&ring->rx_syncp, start)); + } while (u64_stats_fetch_retry(&ring->rx_syncp, start)); bytes += _bytes; packets += _packets; } @@ -4699,10 +4699,10 @@ void igc_update_stats(struct igc_adapter *adapter) struct igc_ring *ring = adapter->tx_ring[i]; do { - start = u64_stats_fetch_begin_irq(&ring->tx_syncp); + start = u64_stats_fetch_begin(&ring->tx_syncp); _bytes = ring->tx_stats.bytes; _packets = ring->tx_stats.packets; - } while (u64_stats_fetch_retry_irq(&ring->tx_syncp, start)); + } while (u64_stats_fetch_retry(&ring->tx_syncp, start)); bytes += _bytes; packets += _packets; } diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c index e88e3dfac8c2..eda7188e8df4 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c @@ -1335,10 +1335,10 @@ static void ixgbe_get_ethtool_stats(struct net_device *netdev, } do { - start = u64_stats_fetch_begin_irq(&ring->syncp); + start = u64_stats_fetch_begin(&ring->syncp); data[i] = ring->stats.packets; data[i+1] = ring->stats.bytes; - } while (u64_stats_fetch_retry_irq(&ring->syncp, start)); + } while (u64_stats_fetch_retry(&ring->syncp, start)); i += 2; } for (j = 0; j < IXGBE_NUM_RX_QUEUES; j++) { @@ -1351,10 +1351,10 @@ static void ixgbe_get_ethtool_stats(struct net_device *netdev, } do { - start = u64_stats_fetch_begin_irq(&ring->syncp); + start = u64_stats_fetch_begin(&ring->syncp); data[i] = ring->stats.packets; data[i+1] = ring->stats.bytes; - } while (u64_stats_fetch_retry_irq(&ring->syncp, start)); + } while (u64_stats_fetch_retry(&ring->syncp, start)); i += 2; } diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c index 298cfbfcb7b6..ab8370c413f3 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c @@ -9041,10 +9041,10 @@ static void ixgbe_get_ring_stats64(struct rtnl_link_stats64 *stats, if (ring) { do { - start = u64_stats_fetch_begin_irq(&ring->syncp); + start = u64_stats_fetch_begin(&ring->syncp); packets = ring->stats.packets; bytes = ring->stats.bytes; - } while (u64_stats_fetch_retry_irq(&ring->syncp, start)); + } while (u64_stats_fetch_retry(&ring->syncp, start)); stats->tx_packets += packets; stats->tx_bytes += bytes; } @@ -9064,10 +9064,10 @@ static void ixgbe_get_stats64(struct net_device *netdev, if (ring) { do { - start = u64_stats_fetch_begin_irq(&ring->syncp); + start = u64_stats_fetch_begin(&ring->syncp); packets = ring->stats.packets; bytes = ring->stats.bytes; - } while (u64_stats_fetch_retry_irq(&ring->syncp, start)); + } while (u64_stats_fetch_retry(&ring->syncp, start)); stats->rx_packets += packets; stats->rx_bytes += bytes; } diff --git a/drivers/net/ethernet/intel/ixgbevf/ethtool.c b/drivers/net/ethernet/intel/ixgbevf/ethtool.c index ccfa6b91aac6..296915414a7c 100644 --- a/drivers/net/ethernet/intel/ixgbevf/ethtool.c +++ b/drivers/net/ethernet/intel/ixgbevf/ethtool.c @@ -458,10 +458,10 @@ static void ixgbevf_get_ethtool_stats(struct net_device *netdev, } do { - start = u64_stats_fetch_begin_irq(&ring->syncp); + start = u64_stats_fetch_begin(&ring->syncp); data[i] = ring->stats.packets; data[i + 1] = ring->stats.bytes; - } while (u64_stats_fetch_retry_irq(&ring->syncp, start)); + } while (u64_stats_fetch_retry(&ring->syncp, start)); i += 2; } @@ -475,10 +475,10 @@ static void ixgbevf_get_ethtool_stats(struct net_device *netdev, } do { - start = u64_stats_fetch_begin_irq(&ring->syncp); + start = u64_stats_fetch_begin(&ring->syncp); data[i] = ring->stats.packets; data[i + 1] = ring->stats.bytes; - } while (u64_stats_fetch_retry_irq(&ring->syncp, start)); + } while (u64_stats_fetch_retry(&ring->syncp, start)); i += 2; } @@ -492,10 +492,10 @@ static void ixgbevf_get_ethtool_stats(struct net_device *netdev, } do { - start = u64_stats_fetch_begin_irq(&ring->syncp); + start = u64_stats_fetch_begin(&ring->syncp); data[i] = ring->stats.packets; data[i + 1] = ring->stats.bytes; - } while (u64_stats_fetch_retry_irq(&ring->syncp, start)); + } while (u64_stats_fetch_retry(&ring->syncp, start)); i += 2; } } diff --git a/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c b/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c index 99933e89717a..be733677bdc8 100644 --- a/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c +++ b/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c @@ -4350,10 +4350,10 @@ static void ixgbevf_get_tx_ring_stats(struct rtnl_link_stats64 *stats, if (ring) { do { - start = u64_stats_fetch_begin_irq(&ring->syncp); + start = u64_stats_fetch_begin(&ring->syncp); bytes = ring->stats.bytes; packets = ring->stats.packets; - } while (u64_stats_fetch_retry_irq(&ring->syncp, start)); + } while (u64_stats_fetch_retry(&ring->syncp, start)); stats->tx_bytes += bytes; stats->tx_packets += packets; } @@ -4376,10 +4376,10 @@ static void ixgbevf_get_stats(struct net_device *netdev, for (i = 0; i < adapter->num_rx_queues; i++) { ring = adapter->rx_ring[i]; do { - start = u64_stats_fetch_begin_irq(&ring->syncp); + start = u64_stats_fetch_begin(&ring->syncp); bytes = ring->stats.bytes; packets = ring->stats.packets; - } while (u64_stats_fetch_retry_irq(&ring->syncp, start)); + } while (u64_stats_fetch_retry(&ring->syncp, start)); stats->rx_bytes += bytes; stats->rx_packets += packets; } diff --git a/drivers/net/ethernet/marvell/mvneta.c b/drivers/net/ethernet/marvell/mvneta.c index ff3e361e06e7..81dc57a69fd0 100644 --- a/drivers/net/ethernet/marvell/mvneta.c +++ b/drivers/net/ethernet/marvell/mvneta.c @@ -813,14 +813,14 @@ mvneta_get_stats64(struct net_device *dev, cpu_stats = per_cpu_ptr(pp->stats, cpu); do { - start = u64_stats_fetch_begin_irq(&cpu_stats->syncp); + start = u64_stats_fetch_begin(&cpu_stats->syncp); rx_packets = cpu_stats->es.ps.rx_packets; rx_bytes = cpu_stats->es.ps.rx_bytes; rx_dropped = cpu_stats->rx_dropped; rx_errors = cpu_stats->rx_errors; tx_packets = cpu_stats->es.ps.tx_packets; tx_bytes = cpu_stats->es.ps.tx_bytes; - } while (u64_stats_fetch_retry_irq(&cpu_stats->syncp, start)); + } while (u64_stats_fetch_retry(&cpu_stats->syncp, start)); stats->rx_packets += rx_packets; stats->rx_bytes += rx_bytes; @@ -4762,7 +4762,7 @@ mvneta_ethtool_update_pcpu_stats(struct mvneta_port *pp, stats = per_cpu_ptr(pp->stats, cpu); do { - start = u64_stats_fetch_begin_irq(&stats->syncp); + start = u64_stats_fetch_begin(&stats->syncp); skb_alloc_error = stats->es.skb_alloc_error; refill_error = stats->es.refill_error; xdp_redirect = stats->es.ps.xdp_redirect; @@ -4772,7 +4772,7 @@ mvneta_ethtool_update_pcpu_stats(struct mvneta_port *pp, xdp_xmit_err = stats->es.ps.xdp_xmit_err; xdp_tx = stats->es.ps.xdp_tx; xdp_tx_err = stats->es.ps.xdp_tx_err; - } while (u64_stats_fetch_retry_irq(&stats->syncp, start)); + } while (u64_stats_fetch_retry(&stats->syncp, start)); es->skb_alloc_error += skb_alloc_error; es->refill_error += refill_error; diff --git a/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c b/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c index eb0fb8128096..116e53172072 100644 --- a/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c +++ b/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c @@ -2008,7 +2008,7 @@ mvpp2_get_xdp_stats(struct mvpp2_port *port, struct mvpp2_pcpu_stats *xdp_stats) cpu_stats = per_cpu_ptr(port->stats, cpu); do { - start = u64_stats_fetch_begin_irq(&cpu_stats->syncp); + start = u64_stats_fetch_begin(&cpu_stats->syncp); xdp_redirect = cpu_stats->xdp_redirect; xdp_pass = cpu_stats->xdp_pass; xdp_drop = cpu_stats->xdp_drop; @@ -2016,7 +2016,7 @@ mvpp2_get_xdp_stats(struct mvpp2_port *port, struct mvpp2_pcpu_stats *xdp_stats) xdp_xmit_err = cpu_stats->xdp_xmit_err; xdp_tx = cpu_stats->xdp_tx; xdp_tx_err = cpu_stats->xdp_tx_err; - } while (u64_stats_fetch_retry_irq(&cpu_stats->syncp, start)); + } while (u64_stats_fetch_retry(&cpu_stats->syncp, start)); xdp_stats->xdp_redirect += xdp_redirect; xdp_stats->xdp_pass += xdp_pass; @@ -5115,12 +5115,12 @@ mvpp2_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats) cpu_stats = per_cpu_ptr(port->stats, cpu); do { - start = u64_stats_fetch_begin_irq(&cpu_stats->syncp); + start = u64_stats_fetch_begin(&cpu_stats->syncp); rx_packets = cpu_stats->rx_packets; rx_bytes = cpu_stats->rx_bytes; tx_packets = cpu_stats->tx_packets; tx_bytes = cpu_stats->tx_bytes; - } while (u64_stats_fetch_retry_irq(&cpu_stats->syncp, start)); + } while (u64_stats_fetch_retry(&cpu_stats->syncp, start)); stats->rx_packets += rx_packets; stats->rx_bytes += rx_bytes; diff --git a/drivers/net/ethernet/marvell/sky2.c b/drivers/net/ethernet/marvell/sky2.c index ab33ba1c3023..ff97b140886a 100644 --- a/drivers/net/ethernet/marvell/sky2.c +++ b/drivers/net/ethernet/marvell/sky2.c @@ -3894,19 +3894,19 @@ static void sky2_get_stats(struct net_device *dev, u64 _bytes, _packets; do { - start = u64_stats_fetch_begin_irq(&sky2->rx_stats.syncp); + start = u64_stats_fetch_begin(&sky2->rx_stats.syncp); _bytes = sky2->rx_stats.bytes; _packets = sky2->rx_stats.packets; - } while (u64_stats_fetch_retry_irq(&sky2->rx_stats.syncp, start)); + } while (u64_stats_fetch_retry(&sky2->rx_stats.syncp, start)); stats->rx_packets = _packets; stats->rx_bytes = _bytes; do { - start = u64_stats_fetch_begin_irq(&sky2->tx_stats.syncp); + start = u64_stats_fetch_begin(&sky2->tx_stats.syncp); _bytes = sky2->tx_stats.bytes; _packets = sky2->tx_stats.packets; - } while (u64_stats_fetch_retry_irq(&sky2->tx_stats.syncp, start)); + } while (u64_stats_fetch_retry(&sky2->tx_stats.syncp, start)); stats->tx_packets = _packets; stats->tx_bytes = _bytes; diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c index 7cd381530aa4..789268b15106 100644 --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c @@ -865,7 +865,7 @@ static void mtk_get_stats64(struct net_device *dev, } do { - start = u64_stats_fetch_begin_irq(&hw_stats->syncp); + start = u64_stats_fetch_begin(&hw_stats->syncp); storage->rx_packets = hw_stats->rx_packets; storage->tx_packets = hw_stats->tx_packets; storage->rx_bytes = hw_stats->rx_bytes; @@ -877,7 +877,7 @@ static void mtk_get_stats64(struct net_device *dev, storage->rx_crc_errors = hw_stats->rx_fcs_errors; storage->rx_errors = hw_stats->rx_checksum_errors; storage->tx_aborted_errors = hw_stats->tx_skip; - } while (u64_stats_fetch_retry_irq(&hw_stats->syncp, start)); + } while (u64_stats_fetch_retry(&hw_stats->syncp, start)); storage->tx_errors = dev->stats.tx_errors; storage->rx_dropped = dev->stats.rx_dropped; @@ -3684,13 +3684,13 @@ static void mtk_get_ethtool_stats(struct net_device *dev, do { data_dst = data; - start = u64_stats_fetch_begin_irq(&hwstats->syncp); + start = u64_stats_fetch_begin(&hwstats->syncp); for (i = 0; i < ARRAY_SIZE(mtk_ethtool_stats); i++) *data_dst++ = *(data_src + mtk_ethtool_stats[i].offset); if (mtk_page_pool_enabled(mac->hw)) mtk_ethtool_pp_stats(mac->hw, data_dst); - } while (u64_stats_fetch_retry_irq(&hwstats->syncp, start)); + } while (u64_stats_fetch_retry(&hwstats->syncp, start)); } static int mtk_get_rxnfc(struct net_device *dev, struct ethtool_rxnfc *cmd, diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c index 5bcf5bceff71..6ed496f6cbfb 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c @@ -827,12 +827,12 @@ mlxsw_sp_port_get_sw_stats64(const struct net_device *dev, for_each_possible_cpu(i) { p = per_cpu_ptr(mlxsw_sp_port->pcpu_stats, i); do { - start = u64_stats_fetch_begin_irq(&p->syncp); + start = u64_stats_fetch_begin(&p->syncp); rx_packets = p->rx_packets; rx_bytes = p->rx_bytes; tx_packets = p->tx_packets; tx_bytes = p->tx_bytes; - } while (u64_stats_fetch_retry_irq(&p->syncp, start)); + } while (u64_stats_fetch_retry(&p->syncp, start)); stats->rx_packets += rx_packets; stats->rx_bytes += rx_bytes; diff --git a/drivers/net/ethernet/microsoft/mana/mana_en.c b/drivers/net/ethernet/microsoft/mana/mana_en.c index 9259a74eca40..318dbbb48279 100644 --- a/drivers/net/ethernet/microsoft/mana/mana_en.c +++ b/drivers/net/ethernet/microsoft/mana/mana_en.c @@ -315,10 +315,10 @@ static void mana_get_stats64(struct net_device *ndev, rx_stats = &apc->rxqs[q]->stats; do { - start = u64_stats_fetch_begin_irq(&rx_stats->syncp); + start = u64_stats_fetch_begin(&rx_stats->syncp); packets = rx_stats->packets; bytes = rx_stats->bytes; - } while (u64_stats_fetch_retry_irq(&rx_stats->syncp, start)); + } while (u64_stats_fetch_retry(&rx_stats->syncp, start)); st->rx_packets += packets; st->rx_bytes += bytes; @@ -328,10 +328,10 @@ static void mana_get_stats64(struct net_device *ndev, tx_stats = &apc->tx_qp[q].txq.stats; do { - start = u64_stats_fetch_begin_irq(&tx_stats->syncp); + start = u64_stats_fetch_begin(&tx_stats->syncp); packets = tx_stats->packets; bytes = tx_stats->bytes; - } while (u64_stats_fetch_retry_irq(&tx_stats->syncp, start)); + } while (u64_stats_fetch_retry(&tx_stats->syncp, start)); st->tx_packets += packets; st->tx_bytes += bytes; diff --git a/drivers/net/ethernet/microsoft/mana/mana_ethtool.c b/drivers/net/ethernet/microsoft/mana/mana_ethtool.c index c530db76880f..96d55c91c969 100644 --- a/drivers/net/ethernet/microsoft/mana/mana_ethtool.c +++ b/drivers/net/ethernet/microsoft/mana/mana_ethtool.c @@ -90,13 +90,13 @@ static void mana_get_ethtool_stats(struct net_device *ndev, rx_stats = &apc->rxqs[q]->stats; do { - start = u64_stats_fetch_begin_irq(&rx_stats->syncp); + start = u64_stats_fetch_begin(&rx_stats->syncp); packets = rx_stats->packets; bytes = rx_stats->bytes; xdp_drop = rx_stats->xdp_drop; xdp_tx = rx_stats->xdp_tx; xdp_redirect = rx_stats->xdp_redirect; - } while (u64_stats_fetch_retry_irq(&rx_stats->syncp, start)); + } while (u64_stats_fetch_retry(&rx_stats->syncp, start)); data[i++] = packets; data[i++] = bytes; @@ -109,11 +109,11 @@ static void mana_get_ethtool_stats(struct net_device *ndev, tx_stats = &apc->tx_qp[q].txq.stats; do { - start = u64_stats_fetch_begin_irq(&tx_stats->syncp); + start = u64_stats_fetch_begin(&tx_stats->syncp); packets = tx_stats->packets; bytes = tx_stats->bytes; xdp_xmit = tx_stats->xdp_xmit; - } while (u64_stats_fetch_retry_irq(&tx_stats->syncp, start)); + } while (u64_stats_fetch_retry(&tx_stats->syncp, start)); data[i++] = packets; data[i++] = bytes; diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c index 27f4786ace4f..a5ca5c4a7896 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c @@ -1631,21 +1631,21 @@ static void nfp_net_stat64(struct net_device *netdev, unsigned int start; do { - start = u64_stats_fetch_begin_irq(&r_vec->rx_sync); + start = u64_stats_fetch_begin(&r_vec->rx_sync); data[0] = r_vec->rx_pkts; data[1] = r_vec->rx_bytes; data[2] = r_vec->rx_drops; - } while (u64_stats_fetch_retry_irq(&r_vec->rx_sync, start)); + } while (u64_stats_fetch_retry(&r_vec->rx_sync, start)); stats->rx_packets += data[0]; stats->rx_bytes += data[1]; stats->rx_dropped += data[2]; do { - start = u64_stats_fetch_begin_irq(&r_vec->tx_sync); + start = u64_stats_fetch_begin(&r_vec->tx_sync); data[0] = r_vec->tx_pkts; data[1] = r_vec->tx_bytes; data[2] = r_vec->tx_errors; - } while (u64_stats_fetch_retry_irq(&r_vec->tx_sync, start)); + } while (u64_stats_fetch_retry(&r_vec->tx_sync, start)); stats->tx_packets += data[0]; stats->tx_bytes += data[1]; stats->tx_errors += data[2]; diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c b/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c index 22a5d2419084..f6b09eed73dc 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c @@ -686,7 +686,7 @@ static u64 *nfp_vnic_get_sw_stats(struct net_device *netdev, u64 *data) unsigned int start; do { - start = u64_stats_fetch_begin_irq(&nn->r_vecs[i].rx_sync); + start = u64_stats_fetch_begin(&nn->r_vecs[i].rx_sync); data[0] = nn->r_vecs[i].rx_pkts; tmp[0] = nn->r_vecs[i].hw_csum_rx_ok; tmp[1] = nn->r_vecs[i].hw_csum_rx_inner_ok; @@ -694,10 +694,10 @@ static u64 *nfp_vnic_get_sw_stats(struct net_device *netdev, u64 *data) tmp[3] = nn->r_vecs[i].hw_csum_rx_error; tmp[4] = nn->r_vecs[i].rx_replace_buf_alloc_fail; tmp[5] = nn->r_vecs[i].hw_tls_rx; - } while (u64_stats_fetch_retry_irq(&nn->r_vecs[i].rx_sync, start)); + } while (u64_stats_fetch_retry(&nn->r_vecs[i].rx_sync, start)); do { - start = u64_stats_fetch_begin_irq(&nn->r_vecs[i].tx_sync); + start = u64_stats_fetch_begin(&nn->r_vecs[i].tx_sync); data[1] = nn->r_vecs[i].tx_pkts; data[2] = nn->r_vecs[i].tx_busy; tmp[6] = nn->r_vecs[i].hw_csum_tx; @@ -707,7 +707,7 @@ static u64 *nfp_vnic_get_sw_stats(struct net_device *netdev, u64 *data) tmp[10] = nn->r_vecs[i].hw_tls_tx; tmp[11] = nn->r_vecs[i].tls_tx_fallback; tmp[12] = nn->r_vecs[i].tls_tx_no_fallback; - } while (u64_stats_fetch_retry_irq(&nn->r_vecs[i].tx_sync, start)); + } while (u64_stats_fetch_retry(&nn->r_vecs[i].tx_sync, start)); data += NN_RVEC_PER_Q_STATS; diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_repr.c b/drivers/net/ethernet/netronome/nfp/nfp_net_repr.c index 8b77582bdfa0..a6b6ca1fd55e 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_repr.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_repr.c @@ -134,13 +134,13 @@ nfp_repr_get_host_stats64(const struct net_device *netdev, repr_stats = per_cpu_ptr(repr->stats, i); do { - start = u64_stats_fetch_begin_irq(&repr_stats->syncp); + start = u64_stats_fetch_begin(&repr_stats->syncp); tbytes = repr_stats->tx_bytes; tpkts = repr_stats->tx_packets; tdrops = repr_stats->tx_drops; rbytes = repr_stats->rx_bytes; rpkts = repr_stats->rx_packets; - } while (u64_stats_fetch_retry_irq(&repr_stats->syncp, start)); + } while (u64_stats_fetch_retry(&repr_stats->syncp, start)); stats->tx_bytes += tbytes; stats->tx_packets += tpkts; diff --git a/drivers/net/ethernet/nvidia/forcedeth.c b/drivers/net/ethernet/nvidia/forcedeth.c index daa028729d44..0605d1ee490d 100644 --- a/drivers/net/ethernet/nvidia/forcedeth.c +++ b/drivers/net/ethernet/nvidia/forcedeth.c @@ -1734,12 +1734,12 @@ static void nv_get_stats(int cpu, struct fe_priv *np, u64 tx_packets, tx_bytes, tx_dropped; do { - syncp_start = u64_stats_fetch_begin_irq(&np->swstats_rx_syncp); + syncp_start = u64_stats_fetch_begin(&np->swstats_rx_syncp); rx_packets = src->stat_rx_packets; rx_bytes = src->stat_rx_bytes; rx_dropped = src->stat_rx_dropped; rx_missed_errors = src->stat_rx_missed_errors; - } while (u64_stats_fetch_retry_irq(&np->swstats_rx_syncp, syncp_start)); + } while (u64_stats_fetch_retry(&np->swstats_rx_syncp, syncp_start)); storage->rx_packets += rx_packets; storage->rx_bytes += rx_bytes; @@ -1747,11 +1747,11 @@ static void nv_get_stats(int cpu, struct fe_priv *np, storage->rx_missed_errors += rx_missed_errors; do { - syncp_start = u64_stats_fetch_begin_irq(&np->swstats_tx_syncp); + syncp_start = u64_stats_fetch_begin(&np->swstats_tx_syncp); tx_packets = src->stat_tx_packets; tx_bytes = src->stat_tx_bytes; tx_dropped = src->stat_tx_dropped; - } while (u64_stats_fetch_retry_irq(&np->swstats_tx_syncp, syncp_start)); + } while (u64_stats_fetch_retry(&np->swstats_tx_syncp, syncp_start)); storage->tx_packets += tx_packets; storage->tx_bytes += tx_bytes; diff --git a/drivers/net/ethernet/qualcomm/rmnet/rmnet_vnd.c b/drivers/net/ethernet/qualcomm/rmnet/rmnet_vnd.c index 1b2119b1d48a..3f5e6572d20e 100644 --- a/drivers/net/ethernet/qualcomm/rmnet/rmnet_vnd.c +++ b/drivers/net/ethernet/qualcomm/rmnet/rmnet_vnd.c @@ -135,9 +135,9 @@ static void rmnet_get_stats64(struct net_device *dev, pcpu_ptr = per_cpu_ptr(priv->pcpu_stats, cpu); do { - start = u64_stats_fetch_begin_irq(&pcpu_ptr->syncp); + start = u64_stats_fetch_begin(&pcpu_ptr->syncp); snapshot = pcpu_ptr->stats; /* struct assignment */ - } while (u64_stats_fetch_retry_irq(&pcpu_ptr->syncp, start)); + } while (u64_stats_fetch_retry(&pcpu_ptr->syncp, start)); total_stats.rx_pkts += snapshot.rx_pkts; total_stats.rx_bytes += snapshot.rx_bytes; diff --git a/drivers/net/ethernet/realtek/8139too.c b/drivers/net/ethernet/realtek/8139too.c index 469e2e229c6e..9ce0e8a64ba8 100644 --- a/drivers/net/ethernet/realtek/8139too.c +++ b/drivers/net/ethernet/realtek/8139too.c @@ -2532,16 +2532,16 @@ rtl8139_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats) netdev_stats_to_stats64(stats, &dev->stats); do { - start = u64_stats_fetch_begin_irq(&tp->rx_stats.syncp); + start = u64_stats_fetch_begin(&tp->rx_stats.syncp); stats->rx_packets = tp->rx_stats.packets; stats->rx_bytes = tp->rx_stats.bytes; - } while (u64_stats_fetch_retry_irq(&tp->rx_stats.syncp, start)); + } while (u64_stats_fetch_retry(&tp->rx_stats.syncp, start)); do { - start = u64_stats_fetch_begin_irq(&tp->tx_stats.syncp); + start = u64_stats_fetch_begin(&tp->tx_stats.syncp); stats->tx_packets = tp->tx_stats.packets; stats->tx_bytes = tp->tx_stats.bytes; - } while (u64_stats_fetch_retry_irq(&tp->tx_stats.syncp, start)); + } while (u64_stats_fetch_retry(&tp->tx_stats.syncp, start)); } /* Set or clear the multicast filter for this adaptor. diff --git a/drivers/net/ethernet/socionext/sni_ave.c b/drivers/net/ethernet/socionext/sni_ave.c index c6a6c9ed5365..492c39c08af1 100644 --- a/drivers/net/ethernet/socionext/sni_ave.c +++ b/drivers/net/ethernet/socionext/sni_ave.c @@ -1508,16 +1508,16 @@ static void ave_get_stats64(struct net_device *ndev, unsigned int start; do { - start = u64_stats_fetch_begin_irq(&priv->stats_rx.syncp); + start = u64_stats_fetch_begin(&priv->stats_rx.syncp); stats->rx_packets = priv->stats_rx.packets; stats->rx_bytes = priv->stats_rx.bytes; - } while (u64_stats_fetch_retry_irq(&priv->stats_rx.syncp, start)); + } while (u64_stats_fetch_retry(&priv->stats_rx.syncp, start)); do { - start = u64_stats_fetch_begin_irq(&priv->stats_tx.syncp); + start = u64_stats_fetch_begin(&priv->stats_tx.syncp); stats->tx_packets = priv->stats_tx.packets; stats->tx_bytes = priv->stats_tx.bytes; - } while (u64_stats_fetch_retry_irq(&priv->stats_tx.syncp, start)); + } while (u64_stats_fetch_retry(&priv->stats_tx.syncp, start)); stats->rx_errors = priv->stats_rx.errors; stats->tx_errors = priv->stats_tx.errors; diff --git a/drivers/net/ethernet/ti/am65-cpsw-nuss.c b/drivers/net/ethernet/ti/am65-cpsw-nuss.c index 7f86068f3ff6..8800c8a010ff 100644 --- a/drivers/net/ethernet/ti/am65-cpsw-nuss.c +++ b/drivers/net/ethernet/ti/am65-cpsw-nuss.c @@ -1362,12 +1362,12 @@ static void am65_cpsw_nuss_ndo_get_stats(struct net_device *dev, cpu_stats = per_cpu_ptr(ndev_priv->stats, cpu); do { - start = u64_stats_fetch_begin_irq(&cpu_stats->syncp); + start = u64_stats_fetch_begin(&cpu_stats->syncp); rx_packets = cpu_stats->rx_packets; rx_bytes = cpu_stats->rx_bytes; tx_packets = cpu_stats->tx_packets; tx_bytes = cpu_stats->tx_bytes; - } while (u64_stats_fetch_retry_irq(&cpu_stats->syncp, start)); + } while (u64_stats_fetch_retry(&cpu_stats->syncp, start)); stats->rx_packets += rx_packets; stats->rx_bytes += rx_bytes; diff --git a/drivers/net/ethernet/ti/netcp_core.c b/drivers/net/ethernet/ti/netcp_core.c index aba70bef4894..8b776f9cdb3f 100644 --- a/drivers/net/ethernet/ti/netcp_core.c +++ b/drivers/net/ethernet/ti/netcp_core.c @@ -1916,16 +1916,16 @@ netcp_get_stats(struct net_device *ndev, struct rtnl_link_stats64 *stats) unsigned int start; do { - start = u64_stats_fetch_begin_irq(&p->syncp_rx); + start = u64_stats_fetch_begin(&p->syncp_rx); rxpackets = p->rx_packets; rxbytes = p->rx_bytes; - } while (u64_stats_fetch_retry_irq(&p->syncp_rx, start)); + } while (u64_stats_fetch_retry(&p->syncp_rx, start)); do { - start = u64_stats_fetch_begin_irq(&p->syncp_tx); + start = u64_stats_fetch_begin(&p->syncp_tx); txpackets = p->tx_packets; txbytes = p->tx_bytes; - } while (u64_stats_fetch_retry_irq(&p->syncp_tx, start)); + } while (u64_stats_fetch_retry(&p->syncp_tx, start)); stats->rx_packets = rxpackets; stats->rx_bytes = rxbytes; diff --git a/drivers/net/ethernet/via/via-rhine.c b/drivers/net/ethernet/via/via-rhine.c index 0fb15a17b547..d716e6fe26e1 100644 --- a/drivers/net/ethernet/via/via-rhine.c +++ b/drivers/net/ethernet/via/via-rhine.c @@ -2217,16 +2217,16 @@ rhine_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats) netdev_stats_to_stats64(stats, &dev->stats); do { - start = u64_stats_fetch_begin_irq(&rp->rx_stats.syncp); + start = u64_stats_fetch_begin(&rp->rx_stats.syncp); stats->rx_packets = rp->rx_stats.packets; stats->rx_bytes = rp->rx_stats.bytes; - } while (u64_stats_fetch_retry_irq(&rp->rx_stats.syncp, start)); + } while (u64_stats_fetch_retry(&rp->rx_stats.syncp, start)); do { - start = u64_stats_fetch_begin_irq(&rp->tx_stats.syncp); + start = u64_stats_fetch_begin(&rp->tx_stats.syncp); stats->tx_packets = rp->tx_stats.packets; stats->tx_bytes = rp->tx_stats.bytes; - } while (u64_stats_fetch_retry_irq(&rp->tx_stats.syncp, start)); + } while (u64_stats_fetch_retry(&rp->tx_stats.syncp, start)); } static void rhine_set_rx_mode(struct net_device *dev) diff --git a/drivers/net/ethernet/xilinx/xilinx_axienet_main.c b/drivers/net/ethernet/xilinx/xilinx_axienet_main.c index d1d772580da9..441e1058104f 100644 --- a/drivers/net/ethernet/xilinx/xilinx_axienet_main.c +++ b/drivers/net/ethernet/xilinx/xilinx_axienet_main.c @@ -1305,16 +1305,16 @@ axienet_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats) netdev_stats_to_stats64(stats, &dev->stats); do { - start = u64_stats_fetch_begin_irq(&lp->rx_stat_sync); + start = u64_stats_fetch_begin(&lp->rx_stat_sync); stats->rx_packets = u64_stats_read(&lp->rx_packets); stats->rx_bytes = u64_stats_read(&lp->rx_bytes); - } while (u64_stats_fetch_retry_irq(&lp->rx_stat_sync, start)); + } while (u64_stats_fetch_retry(&lp->rx_stat_sync, start)); do { - start = u64_stats_fetch_begin_irq(&lp->tx_stat_sync); + start = u64_stats_fetch_begin(&lp->tx_stat_sync); stats->tx_packets = u64_stats_read(&lp->tx_packets); stats->tx_bytes = u64_stats_read(&lp->tx_bytes); - } while (u64_stats_fetch_retry_irq(&lp->tx_stat_sync, start)); + } while (u64_stats_fetch_retry(&lp->tx_stat_sync, start)); } static const struct net_device_ops axienet_netdev_ops = { diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c index 89eb4f179a3c..f9b219e6cd58 100644 --- a/drivers/net/hyperv/netvsc_drv.c +++ b/drivers/net/hyperv/netvsc_drv.c @@ -1264,12 +1264,12 @@ static void netvsc_get_vf_stats(struct net_device *net, unsigned int start; do { - start = u64_stats_fetch_begin_irq(&stats->syncp); + start = u64_stats_fetch_begin(&stats->syncp); rx_packets = stats->rx_packets; tx_packets = stats->tx_packets; rx_bytes = stats->rx_bytes; tx_bytes = stats->tx_bytes; - } while (u64_stats_fetch_retry_irq(&stats->syncp, start)); + } while (u64_stats_fetch_retry(&stats->syncp, start)); tot->rx_packets += rx_packets; tot->tx_packets += tx_packets; @@ -1294,12 +1294,12 @@ static void netvsc_get_pcpu_stats(struct net_device *net, unsigned int start; do { - start = u64_stats_fetch_begin_irq(&stats->syncp); + start = u64_stats_fetch_begin(&stats->syncp); this_tot->vf_rx_packets = stats->rx_packets; this_tot->vf_tx_packets = stats->tx_packets; this_tot->vf_rx_bytes = stats->rx_bytes; this_tot->vf_tx_bytes = stats->tx_bytes; - } while (u64_stats_fetch_retry_irq(&stats->syncp, start)); + } while (u64_stats_fetch_retry(&stats->syncp, start)); this_tot->rx_packets = this_tot->vf_rx_packets; this_tot->tx_packets = this_tot->vf_tx_packets; this_tot->rx_bytes = this_tot->vf_rx_bytes; @@ -1318,20 +1318,20 @@ static void netvsc_get_pcpu_stats(struct net_device *net, tx_stats = &nvchan->tx_stats; do { - start = u64_stats_fetch_begin_irq(&tx_stats->syncp); + start = u64_stats_fetch_begin(&tx_stats->syncp); packets = tx_stats->packets; bytes = tx_stats->bytes; - } while (u64_stats_fetch_retry_irq(&tx_stats->syncp, start)); + } while (u64_stats_fetch_retry(&tx_stats->syncp, start)); this_tot->tx_bytes += bytes; this_tot->tx_packets += packets; rx_stats = &nvchan->rx_stats; do { - start = u64_stats_fetch_begin_irq(&rx_stats->syncp); + start = u64_stats_fetch_begin(&rx_stats->syncp); packets = rx_stats->packets; bytes = rx_stats->bytes; - } while (u64_stats_fetch_retry_irq(&rx_stats->syncp, start)); + } while (u64_stats_fetch_retry(&rx_stats->syncp, start)); this_tot->rx_bytes += bytes; this_tot->rx_packets += packets; @@ -1370,21 +1370,21 @@ static void netvsc_get_stats64(struct net_device *net, tx_stats = &nvchan->tx_stats; do { - start = u64_stats_fetch_begin_irq(&tx_stats->syncp); + start = u64_stats_fetch_begin(&tx_stats->syncp); packets = tx_stats->packets; bytes = tx_stats->bytes; - } while (u64_stats_fetch_retry_irq(&tx_stats->syncp, start)); + } while (u64_stats_fetch_retry(&tx_stats->syncp, start)); t->tx_bytes += bytes; t->tx_packets += packets; rx_stats = &nvchan->rx_stats; do { - start = u64_stats_fetch_begin_irq(&rx_stats->syncp); + start = u64_stats_fetch_begin(&rx_stats->syncp); packets = rx_stats->packets; bytes = rx_stats->bytes; multicast = rx_stats->multicast + rx_stats->broadcast; - } while (u64_stats_fetch_retry_irq(&rx_stats->syncp, start)); + } while (u64_stats_fetch_retry(&rx_stats->syncp, start)); t->rx_bytes += bytes; t->rx_packets += packets; @@ -1527,24 +1527,24 @@ static void netvsc_get_ethtool_stats(struct net_device *dev, tx_stats = &nvdev->chan_table[j].tx_stats; do { - start = u64_stats_fetch_begin_irq(&tx_stats->syncp); + start = u64_stats_fetch_begin(&tx_stats->syncp); packets = tx_stats->packets; bytes = tx_stats->bytes; xdp_xmit = tx_stats->xdp_xmit; - } while (u64_stats_fetch_retry_irq(&tx_stats->syncp, start)); + } while (u64_stats_fetch_retry(&tx_stats->syncp, start)); data[i++] = packets; data[i++] = bytes; data[i++] = xdp_xmit; rx_stats = &nvdev->chan_table[j].rx_stats; do { - start = u64_stats_fetch_begin_irq(&rx_stats->syncp); + start = u64_stats_fetch_begin(&rx_stats->syncp); packets = rx_stats->packets; bytes = rx_stats->bytes; xdp_drop = rx_stats->xdp_drop; xdp_redirect = rx_stats->xdp_redirect; xdp_tx = rx_stats->xdp_tx; - } while (u64_stats_fetch_retry_irq(&rx_stats->syncp, start)); + } while (u64_stats_fetch_retry(&rx_stats->syncp, start)); data[i++] = packets; data[i++] = bytes; data[i++] = xdp_drop; diff --git a/drivers/net/ifb.c b/drivers/net/ifb.c index 1c64d5347b8e..78253ad57b2e 100644 --- a/drivers/net/ifb.c +++ b/drivers/net/ifb.c @@ -162,18 +162,18 @@ static void ifb_stats64(struct net_device *dev, for (i = 0; i < dev->num_tx_queues; i++,txp++) { do { - start = u64_stats_fetch_begin_irq(&txp->rx_stats.sync); + start = u64_stats_fetch_begin(&txp->rx_stats.sync); packets = txp->rx_stats.packets; bytes = txp->rx_stats.bytes; - } while (u64_stats_fetch_retry_irq(&txp->rx_stats.sync, start)); + } while (u64_stats_fetch_retry(&txp->rx_stats.sync, start)); stats->rx_packets += packets; stats->rx_bytes += bytes; do { - start = u64_stats_fetch_begin_irq(&txp->tx_stats.sync); + start = u64_stats_fetch_begin(&txp->tx_stats.sync); packets = txp->tx_stats.packets; bytes = txp->tx_stats.bytes; - } while (u64_stats_fetch_retry_irq(&txp->tx_stats.sync, start)); + } while (u64_stats_fetch_retry(&txp->tx_stats.sync, start)); stats->tx_packets += packets; stats->tx_bytes += bytes; } @@ -245,12 +245,12 @@ static void ifb_fill_stats_data(u64 **data, int j; do { - start = u64_stats_fetch_begin_irq(&q_stats->sync); + start = u64_stats_fetch_begin(&q_stats->sync); for (j = 0; j < IFB_Q_STATS_LEN; j++) { offset = ifb_q_stats_desc[j].offset; (*data)[j] = *(u64 *)(stats_base + offset); } - } while (u64_stats_fetch_retry_irq(&q_stats->sync, start)); + } while (u64_stats_fetch_retry(&q_stats->sync, start)); *data += IFB_Q_STATS_LEN; } diff --git a/drivers/net/ipvlan/ipvlan_main.c b/drivers/net/ipvlan/ipvlan_main.c index 54c94a69c2bb..b6bfa9fdca62 100644 --- a/drivers/net/ipvlan/ipvlan_main.c +++ b/drivers/net/ipvlan/ipvlan_main.c @@ -299,13 +299,13 @@ static void ipvlan_get_stats64(struct net_device *dev, for_each_possible_cpu(idx) { pcptr = per_cpu_ptr(ipvlan->pcpu_stats, idx); do { - strt= u64_stats_fetch_begin_irq(&pcptr->syncp); + strt = u64_stats_fetch_begin(&pcptr->syncp); rx_pkts = u64_stats_read(&pcptr->rx_pkts); rx_bytes = u64_stats_read(&pcptr->rx_bytes); rx_mcast = u64_stats_read(&pcptr->rx_mcast); tx_pkts = u64_stats_read(&pcptr->tx_pkts); tx_bytes = u64_stats_read(&pcptr->tx_bytes); - } while (u64_stats_fetch_retry_irq(&pcptr->syncp, + } while (u64_stats_fetch_retry(&pcptr->syncp, strt)); s->rx_packets += rx_pkts; diff --git a/drivers/net/loopback.c b/drivers/net/loopback.c index 14e8d04cb434..c4ad98d39ea6 100644 --- a/drivers/net/loopback.c +++ b/drivers/net/loopback.c @@ -106,10 +106,10 @@ void dev_lstats_read(struct net_device *dev, u64 *packets, u64 *bytes) lb_stats = per_cpu_ptr(dev->lstats, i); do { - start = u64_stats_fetch_begin_irq(&lb_stats->syncp); + start = u64_stats_fetch_begin(&lb_stats->syncp); tpackets = u64_stats_read(&lb_stats->packets); tbytes = u64_stats_read(&lb_stats->bytes); - } while (u64_stats_fetch_retry_irq(&lb_stats->syncp, start)); + } while (u64_stats_fetch_retry(&lb_stats->syncp, start)); *bytes += tbytes; *packets += tpackets; } diff --git a/drivers/net/macsec.c b/drivers/net/macsec.c index c891b60937a7..ad38fadd0d89 100644 --- a/drivers/net/macsec.c +++ b/drivers/net/macsec.c @@ -2795,9 +2795,9 @@ static void get_rx_sc_stats(struct net_device *dev, stats = per_cpu_ptr(rx_sc->stats, cpu); do { - start = u64_stats_fetch_begin_irq(&stats->syncp); + start = u64_stats_fetch_begin(&stats->syncp); memcpy(&tmp, &stats->stats, sizeof(tmp)); - } while (u64_stats_fetch_retry_irq(&stats->syncp, start)); + } while (u64_stats_fetch_retry(&stats->syncp, start)); sum->InOctetsValidated += tmp.InOctetsValidated; sum->InOctetsDecrypted += tmp.InOctetsDecrypted; @@ -2876,9 +2876,9 @@ static void get_tx_sc_stats(struct net_device *dev, stats = per_cpu_ptr(macsec_priv(dev)->secy.tx_sc.stats, cpu); do { - start = u64_stats_fetch_begin_irq(&stats->syncp); + start = u64_stats_fetch_begin(&stats->syncp); memcpy(&tmp, &stats->stats, sizeof(tmp)); - } while (u64_stats_fetch_retry_irq(&stats->syncp, start)); + } while (u64_stats_fetch_retry(&stats->syncp, start)); sum->OutPktsProtected += tmp.OutPktsProtected; sum->OutPktsEncrypted += tmp.OutPktsEncrypted; @@ -2932,9 +2932,9 @@ static void get_secy_stats(struct net_device *dev, struct macsec_dev_stats *sum) stats = per_cpu_ptr(macsec_priv(dev)->stats, cpu); do { - start = u64_stats_fetch_begin_irq(&stats->syncp); + start = u64_stats_fetch_begin(&stats->syncp); memcpy(&tmp, &stats->stats, sizeof(tmp)); - } while (u64_stats_fetch_retry_irq(&stats->syncp, start)); + } while (u64_stats_fetch_retry(&stats->syncp, start)); sum->OutPktsUntagged += tmp.OutPktsUntagged; sum->InPktsUntagged += tmp.InPktsUntagged; diff --git a/drivers/net/macvlan.c b/drivers/net/macvlan.c index c5cfe8555199..c58fea63be7d 100644 --- a/drivers/net/macvlan.c +++ b/drivers/net/macvlan.c @@ -948,13 +948,13 @@ static void macvlan_dev_get_stats64(struct net_device *dev, for_each_possible_cpu(i) { p = per_cpu_ptr(vlan->pcpu_stats, i); do { - start = u64_stats_fetch_begin_irq(&p->syncp); + start = u64_stats_fetch_begin(&p->syncp); rx_packets = u64_stats_read(&p->rx_packets); rx_bytes = u64_stats_read(&p->rx_bytes); rx_multicast = u64_stats_read(&p->rx_multicast); tx_packets = u64_stats_read(&p->tx_packets); tx_bytes = u64_stats_read(&p->tx_bytes); - } while (u64_stats_fetch_retry_irq(&p->syncp, start)); + } while (u64_stats_fetch_retry(&p->syncp, start)); stats->rx_packets += rx_packets; stats->rx_bytes += rx_bytes; diff --git a/drivers/net/mhi_net.c b/drivers/net/mhi_net.c index 0b1b6f650104..ff302144029d 100644 --- a/drivers/net/mhi_net.c +++ b/drivers/net/mhi_net.c @@ -104,19 +104,19 @@ static void mhi_ndo_get_stats64(struct net_device *ndev, unsigned int start; do { - start = u64_stats_fetch_begin_irq(&mhi_netdev->stats.rx_syncp); + start = u64_stats_fetch_begin(&mhi_netdev->stats.rx_syncp); stats->rx_packets = u64_stats_read(&mhi_netdev->stats.rx_packets); stats->rx_bytes = u64_stats_read(&mhi_netdev->stats.rx_bytes); stats->rx_errors = u64_stats_read(&mhi_netdev->stats.rx_errors); - } while (u64_stats_fetch_retry_irq(&mhi_netdev->stats.rx_syncp, start)); + } while (u64_stats_fetch_retry(&mhi_netdev->stats.rx_syncp, start)); do { - start = u64_stats_fetch_begin_irq(&mhi_netdev->stats.tx_syncp); + start = u64_stats_fetch_begin(&mhi_netdev->stats.tx_syncp); stats->tx_packets = u64_stats_read(&mhi_netdev->stats.tx_packets); stats->tx_bytes = u64_stats_read(&mhi_netdev->stats.tx_bytes); stats->tx_errors = u64_stats_read(&mhi_netdev->stats.tx_errors); stats->tx_dropped = u64_stats_read(&mhi_netdev->stats.tx_dropped); - } while (u64_stats_fetch_retry_irq(&mhi_netdev->stats.tx_syncp, start)); + } while (u64_stats_fetch_retry(&mhi_netdev->stats.tx_syncp, start)); } static const struct net_device_ops mhi_netdev_ops = { diff --git a/drivers/net/netdevsim/netdev.c b/drivers/net/netdevsim/netdev.c index 9a1a5b203624..e470e3398abc 100644 --- a/drivers/net/netdevsim/netdev.c +++ b/drivers/net/netdevsim/netdev.c @@ -67,10 +67,10 @@ nsim_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats) unsigned int start; do { - start = u64_stats_fetch_begin_irq(&ns->syncp); + start = u64_stats_fetch_begin(&ns->syncp); stats->tx_bytes = ns->tx_bytes; stats->tx_packets = ns->tx_packets; - } while (u64_stats_fetch_retry_irq(&ns->syncp, start)); + } while (u64_stats_fetch_retry(&ns->syncp, start)); } static int diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c index 62ade69295a9..d10606f257c4 100644 --- a/drivers/net/team/team.c +++ b/drivers/net/team/team.c @@ -1865,13 +1865,13 @@ team_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats) for_each_possible_cpu(i) { p = per_cpu_ptr(team->pcpu_stats, i); do { - start = u64_stats_fetch_begin_irq(&p->syncp); + start = u64_stats_fetch_begin(&p->syncp); rx_packets = u64_stats_read(&p->rx_packets); rx_bytes = u64_stats_read(&p->rx_bytes); rx_multicast = u64_stats_read(&p->rx_multicast); tx_packets = u64_stats_read(&p->tx_packets); tx_bytes = u64_stats_read(&p->tx_bytes); - } while (u64_stats_fetch_retry_irq(&p->syncp, start)); + } while (u64_stats_fetch_retry(&p->syncp, start)); stats->rx_packets += rx_packets; stats->rx_bytes += rx_bytes; diff --git a/drivers/net/team/team_mode_loadbalance.c b/drivers/net/team/team_mode_loadbalance.c index b095a4b4957b..18d99fda997c 100644 --- a/drivers/net/team/team_mode_loadbalance.c +++ b/drivers/net/team/team_mode_loadbalance.c @@ -466,9 +466,9 @@ static void __lb_one_cpu_stats_add(struct lb_stats *acc_stats, struct lb_stats tmp; do { - start = u64_stats_fetch_begin_irq(syncp); + start = u64_stats_fetch_begin(syncp); tmp.tx_bytes = cpu_stats->tx_bytes; - } while (u64_stats_fetch_retry_irq(syncp, start)); + } while (u64_stats_fetch_retry(syncp, start)); acc_stats->tx_bytes += tmp.tx_bytes; } diff --git a/drivers/net/veth.c b/drivers/net/veth.c index 09682ea3354e..740506c44427 100644 --- a/drivers/net/veth.c +++ b/drivers/net/veth.c @@ -182,12 +182,12 @@ static void veth_get_ethtool_stats(struct net_device *dev, size_t offset; do { - start = u64_stats_fetch_begin_irq(&rq_stats->syncp); + start = u64_stats_fetch_begin(&rq_stats->syncp); for (j = 0; j < VETH_RQ_STATS_LEN; j++) { offset = veth_rq_stats_desc[j].offset; data[idx + j] = *(u64 *)(stats_base + offset); } - } while (u64_stats_fetch_retry_irq(&rq_stats->syncp, start)); + } while (u64_stats_fetch_retry(&rq_stats->syncp, start)); idx += VETH_RQ_STATS_LEN; } @@ -203,12 +203,12 @@ static void veth_get_ethtool_stats(struct net_device *dev, tx_idx += (i % dev->real_num_tx_queues) * VETH_TQ_STATS_LEN; do { - start = u64_stats_fetch_begin_irq(&rq_stats->syncp); + start = u64_stats_fetch_begin(&rq_stats->syncp); for (j = 0; j < VETH_TQ_STATS_LEN; j++) { offset = veth_tq_stats_desc[j].offset; data[tx_idx + j] += *(u64 *)(base + offset); } - } while (u64_stats_fetch_retry_irq(&rq_stats->syncp, start)); + } while (u64_stats_fetch_retry(&rq_stats->syncp, start)); } } @@ -379,13 +379,13 @@ static void veth_stats_rx(struct veth_stats *result, struct net_device *dev) unsigned int start; do { - start = u64_stats_fetch_begin_irq(&stats->syncp); + start = u64_stats_fetch_begin(&stats->syncp); peer_tq_xdp_xmit_err = stats->vs.peer_tq_xdp_xmit_err; xdp_tx_err = stats->vs.xdp_tx_err; packets = stats->vs.xdp_packets; bytes = stats->vs.xdp_bytes; drops = stats->vs.rx_drops; - } while (u64_stats_fetch_retry_irq(&stats->syncp, start)); + } while (u64_stats_fetch_retry(&stats->syncp, start)); result->peer_tq_xdp_xmit_err += peer_tq_xdp_xmit_err; result->xdp_tx_err += xdp_tx_err; result->xdp_packets += packets; diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c index 7106932c6f88..56dbd645d7c8 100644 --- a/drivers/net/virtio_net.c +++ b/drivers/net/virtio_net.c @@ -2069,18 +2069,18 @@ static void virtnet_stats(struct net_device *dev, struct send_queue *sq = &vi->sq[i]; do { - start = u64_stats_fetch_begin_irq(&sq->stats.syncp); + start = u64_stats_fetch_begin(&sq->stats.syncp); tpackets = sq->stats.packets; tbytes = sq->stats.bytes; terrors = sq->stats.tx_timeouts; - } while (u64_stats_fetch_retry_irq(&sq->stats.syncp, start)); + } while (u64_stats_fetch_retry(&sq->stats.syncp, start)); do { - start = u64_stats_fetch_begin_irq(&rq->stats.syncp); + start = u64_stats_fetch_begin(&rq->stats.syncp); rpackets = rq->stats.packets; rbytes = rq->stats.bytes; rdrops = rq->stats.drops; - } while (u64_stats_fetch_retry_irq(&rq->stats.syncp, start)); + } while (u64_stats_fetch_retry(&rq->stats.syncp, start)); tot->rx_packets += rpackets; tot->tx_packets += tpackets; @@ -2691,12 +2691,12 @@ static void virtnet_get_ethtool_stats(struct net_device *dev, stats_base = (u8 *)&rq->stats; do { - start = u64_stats_fetch_begin_irq(&rq->stats.syncp); + start = u64_stats_fetch_begin(&rq->stats.syncp); for (j = 0; j < VIRTNET_RQ_STATS_LEN; j++) { offset = virtnet_rq_stats_desc[j].offset; data[idx + j] = *(u64 *)(stats_base + offset); } - } while (u64_stats_fetch_retry_irq(&rq->stats.syncp, start)); + } while (u64_stats_fetch_retry(&rq->stats.syncp, start)); idx += VIRTNET_RQ_STATS_LEN; } @@ -2705,12 +2705,12 @@ static void virtnet_get_ethtool_stats(struct net_device *dev, stats_base = (u8 *)&sq->stats; do { - start = u64_stats_fetch_begin_irq(&sq->stats.syncp); + start = u64_stats_fetch_begin(&sq->stats.syncp); for (j = 0; j < VIRTNET_SQ_STATS_LEN; j++) { offset = virtnet_sq_stats_desc[j].offset; data[idx + j] = *(u64 *)(stats_base + offset); } - } while (u64_stats_fetch_retry_irq(&sq->stats.syncp, start)); + } while (u64_stats_fetch_retry(&sq->stats.syncp, start)); idx += VIRTNET_SQ_STATS_LEN; } } diff --git a/drivers/net/vrf.c b/drivers/net/vrf.c index badf6f09ae51..6b5a4d036d15 100644 --- a/drivers/net/vrf.c +++ b/drivers/net/vrf.c @@ -159,13 +159,13 @@ static void vrf_get_stats64(struct net_device *dev, dstats = per_cpu_ptr(dev->dstats, i); do { - start = u64_stats_fetch_begin_irq(&dstats->syncp); + start = u64_stats_fetch_begin(&dstats->syncp); tbytes = dstats->tx_bytes; tpkts = dstats->tx_pkts; tdrops = dstats->tx_drps; rbytes = dstats->rx_bytes; rpkts = dstats->rx_pkts; - } while (u64_stats_fetch_retry_irq(&dstats->syncp, start)); + } while (u64_stats_fetch_retry(&dstats->syncp, start)); stats->tx_bytes += tbytes; stats->tx_packets += tpkts; stats->tx_dropped += tdrops; diff --git a/drivers/net/vxlan/vxlan_vnifilter.c b/drivers/net/vxlan/vxlan_vnifilter.c index 3e04af4c5daa..a3de081cda5e 100644 --- a/drivers/net/vxlan/vxlan_vnifilter.c +++ b/drivers/net/vxlan/vxlan_vnifilter.c @@ -129,9 +129,9 @@ static void vxlan_vnifilter_stats_get(const struct vxlan_vni_node *vninode, pstats = per_cpu_ptr(vninode->stats, i); do { - start = u64_stats_fetch_begin_irq(&pstats->syncp); + start = u64_stats_fetch_begin(&pstats->syncp); memcpy(&temp, &pstats->stats, sizeof(temp)); - } while (u64_stats_fetch_retry_irq(&pstats->syncp, start)); + } while (u64_stats_fetch_retry(&pstats->syncp, start)); dest->rx_packets += temp.rx_packets; dest->rx_bytes += temp.rx_bytes; diff --git a/drivers/net/wwan/mhi_wwan_mbim.c b/drivers/net/wwan/mhi_wwan_mbim.c index 6872782e8dd8..22b5939a42bb 100644 --- a/drivers/net/wwan/mhi_wwan_mbim.c +++ b/drivers/net/wwan/mhi_wwan_mbim.c @@ -456,19 +456,19 @@ static void mhi_mbim_ndo_get_stats64(struct net_device *ndev, unsigned int start; do { - start = u64_stats_fetch_begin_irq(&link->rx_syncp); + start = u64_stats_fetch_begin(&link->rx_syncp); stats->rx_packets = u64_stats_read(&link->rx_packets); stats->rx_bytes = u64_stats_read(&link->rx_bytes); stats->rx_errors = u64_stats_read(&link->rx_errors); - } while (u64_stats_fetch_retry_irq(&link->rx_syncp, start)); + } while (u64_stats_fetch_retry(&link->rx_syncp, start)); do { - start = u64_stats_fetch_begin_irq(&link->tx_syncp); + start = u64_stats_fetch_begin(&link->tx_syncp); stats->tx_packets = u64_stats_read(&link->tx_packets); stats->tx_bytes = u64_stats_read(&link->tx_bytes); stats->tx_errors = u64_stats_read(&link->tx_errors); stats->tx_dropped = u64_stats_read(&link->tx_dropped); - } while (u64_stats_fetch_retry_irq(&link->tx_syncp, start)); + } while (u64_stats_fetch_retry(&link->tx_syncp, start)); } static void mhi_mbim_ul_callback(struct mhi_device *mhi_dev, diff --git a/drivers/net/xen-netfront.c b/drivers/net/xen-netfront.c index 9af2b027c19c..ef4e53bf5604 100644 --- a/drivers/net/xen-netfront.c +++ b/drivers/net/xen-netfront.c @@ -1392,16 +1392,16 @@ static void xennet_get_stats64(struct net_device *dev, unsigned int start; do { - start = u64_stats_fetch_begin_irq(&tx_stats->syncp); + start = u64_stats_fetch_begin(&tx_stats->syncp); tx_packets = tx_stats->packets; tx_bytes = tx_stats->bytes; - } while (u64_stats_fetch_retry_irq(&tx_stats->syncp, start)); + } while (u64_stats_fetch_retry(&tx_stats->syncp, start)); do { - start = u64_stats_fetch_begin_irq(&rx_stats->syncp); + start = u64_stats_fetch_begin(&rx_stats->syncp); rx_packets = rx_stats->packets; rx_bytes = rx_stats->bytes; - } while (u64_stats_fetch_retry_irq(&rx_stats->syncp, start)); + } while (u64_stats_fetch_retry(&rx_stats->syncp, start)); tot->rx_packets += rx_packets; tot->tx_packets += tx_packets; From d120d1a63b2c484d6175873d8ee736a633f74b70 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Wed, 26 Oct 2022 15:22:15 +0200 Subject: [PATCH 0446/2002] net: Remove the obsolte u64_stats_fetch_*_irq() users (net). Now that the 32bit UP oddity is gone and 32bit uses always a sequence count, there is no need for the fetch_irq() variants anymore. Convert to the regular interface. Signed-off-by: Thomas Gleixner Signed-off-by: Sebastian Andrzej Siewior Acked-by: Peter Zijlstra (Intel) Signed-off-by: Jakub Kicinski --- net/8021q/vlan_dev.c | 4 ++-- net/bridge/br_multicast.c | 4 ++-- net/bridge/br_vlan.c | 4 ++-- net/core/dev.c | 4 ++-- net/core/devlink.c | 4 ++-- net/core/drop_monitor.c | 8 ++++---- net/core/gen_stats.c | 16 ++++++++-------- net/dsa/slave.c | 4 ++-- net/ipv4/af_inet.c | 4 ++-- net/ipv6/seg6_local.c | 4 ++-- net/mac80211/sta_info.c | 8 ++++---- net/mpls/af_mpls.c | 4 ++-- net/netfilter/ipvs/ip_vs_ctl.c | 4 ++-- net/netfilter/nf_tables_api.c | 4 ++-- net/openvswitch/datapath.c | 4 ++-- net/openvswitch/flow_table.c | 9 ++++----- 16 files changed, 44 insertions(+), 45 deletions(-) diff --git a/net/8021q/vlan_dev.c b/net/8021q/vlan_dev.c index e1bb41a443c4..296d0145932f 100644 --- a/net/8021q/vlan_dev.c +++ b/net/8021q/vlan_dev.c @@ -712,13 +712,13 @@ static void vlan_dev_get_stats64(struct net_device *dev, p = per_cpu_ptr(vlan_dev_priv(dev)->vlan_pcpu_stats, i); do { - start = u64_stats_fetch_begin_irq(&p->syncp); + start = u64_stats_fetch_begin(&p->syncp); rxpackets = u64_stats_read(&p->rx_packets); rxbytes = u64_stats_read(&p->rx_bytes); rxmulticast = u64_stats_read(&p->rx_multicast); txpackets = u64_stats_read(&p->tx_packets); txbytes = u64_stats_read(&p->tx_bytes); - } while (u64_stats_fetch_retry_irq(&p->syncp, start)); + } while (u64_stats_fetch_retry(&p->syncp, start)); stats->rx_packets += rxpackets; stats->rx_bytes += rxbytes; diff --git a/net/bridge/br_multicast.c b/net/bridge/br_multicast.c index 09140bc8c15e..5e988f0ed2c0 100644 --- a/net/bridge/br_multicast.c +++ b/net/bridge/br_multicast.c @@ -4899,9 +4899,9 @@ void br_multicast_get_stats(const struct net_bridge *br, unsigned int start; do { - start = u64_stats_fetch_begin_irq(&cpu_stats->syncp); + start = u64_stats_fetch_begin(&cpu_stats->syncp); memcpy(&temp, &cpu_stats->mstats, sizeof(temp)); - } while (u64_stats_fetch_retry_irq(&cpu_stats->syncp, start)); + } while (u64_stats_fetch_retry(&cpu_stats->syncp, start)); mcast_stats_add_dir(tdst.igmp_v1queries, temp.igmp_v1queries); mcast_stats_add_dir(tdst.igmp_v2queries, temp.igmp_v2queries); diff --git a/net/bridge/br_vlan.c b/net/bridge/br_vlan.c index 6e53dc991409..f2fc284abab3 100644 --- a/net/bridge/br_vlan.c +++ b/net/bridge/br_vlan.c @@ -1378,12 +1378,12 @@ void br_vlan_get_stats(const struct net_bridge_vlan *v, cpu_stats = per_cpu_ptr(v->stats, i); do { - start = u64_stats_fetch_begin_irq(&cpu_stats->syncp); + start = u64_stats_fetch_begin(&cpu_stats->syncp); rxpackets = u64_stats_read(&cpu_stats->rx_packets); rxbytes = u64_stats_read(&cpu_stats->rx_bytes); txbytes = u64_stats_read(&cpu_stats->tx_bytes); txpackets = u64_stats_read(&cpu_stats->tx_packets); - } while (u64_stats_fetch_retry_irq(&cpu_stats->syncp, start)); + } while (u64_stats_fetch_retry(&cpu_stats->syncp, start)); u64_stats_add(&stats->rx_packets, rxpackets); u64_stats_add(&stats->rx_bytes, rxbytes); diff --git a/net/core/dev.c b/net/core/dev.c index fff62068a53d..cfb68db040a4 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -10477,12 +10477,12 @@ void dev_fetch_sw_netstats(struct rtnl_link_stats64 *s, stats = per_cpu_ptr(netstats, cpu); do { - start = u64_stats_fetch_begin_irq(&stats->syncp); + start = u64_stats_fetch_begin(&stats->syncp); rx_packets = u64_stats_read(&stats->rx_packets); rx_bytes = u64_stats_read(&stats->rx_bytes); tx_packets = u64_stats_read(&stats->tx_packets); tx_bytes = u64_stats_read(&stats->tx_bytes); - } while (u64_stats_fetch_retry_irq(&stats->syncp, start)); + } while (u64_stats_fetch_retry(&stats->syncp, start)); s->rx_packets += rx_packets; s->rx_bytes += rx_bytes; diff --git a/net/core/devlink.c b/net/core/devlink.c index 89baa7c0938b..0a16ad45520e 100644 --- a/net/core/devlink.c +++ b/net/core/devlink.c @@ -8304,10 +8304,10 @@ static void devlink_trap_stats_read(struct devlink_stats __percpu *trap_stats, cpu_stats = per_cpu_ptr(trap_stats, i); do { - start = u64_stats_fetch_begin_irq(&cpu_stats->syncp); + start = u64_stats_fetch_begin(&cpu_stats->syncp); rx_packets = u64_stats_read(&cpu_stats->rx_packets); rx_bytes = u64_stats_read(&cpu_stats->rx_bytes); - } while (u64_stats_fetch_retry_irq(&cpu_stats->syncp, start)); + } while (u64_stats_fetch_retry(&cpu_stats->syncp, start)); u64_stats_add(&stats->rx_packets, rx_packets); u64_stats_add(&stats->rx_bytes, rx_bytes); diff --git a/net/core/drop_monitor.c b/net/core/drop_monitor.c index f084a4a6b7ab..11aa6e8a3098 100644 --- a/net/core/drop_monitor.c +++ b/net/core/drop_monitor.c @@ -1432,9 +1432,9 @@ static void net_dm_stats_read(struct net_dm_stats *stats) u64 dropped; do { - start = u64_stats_fetch_begin_irq(&cpu_stats->syncp); + start = u64_stats_fetch_begin(&cpu_stats->syncp); dropped = u64_stats_read(&cpu_stats->dropped); - } while (u64_stats_fetch_retry_irq(&cpu_stats->syncp, start)); + } while (u64_stats_fetch_retry(&cpu_stats->syncp, start)); u64_stats_add(&stats->dropped, dropped); } @@ -1476,9 +1476,9 @@ static void net_dm_hw_stats_read(struct net_dm_stats *stats) u64 dropped; do { - start = u64_stats_fetch_begin_irq(&cpu_stats->syncp); + start = u64_stats_fetch_begin(&cpu_stats->syncp); dropped = u64_stats_read(&cpu_stats->dropped); - } while (u64_stats_fetch_retry_irq(&cpu_stats->syncp, start)); + } while (u64_stats_fetch_retry(&cpu_stats->syncp, start)); u64_stats_add(&stats->dropped, dropped); } diff --git a/net/core/gen_stats.c b/net/core/gen_stats.c index c8d137ef5980..b71ccaec0991 100644 --- a/net/core/gen_stats.c +++ b/net/core/gen_stats.c @@ -135,10 +135,10 @@ static void gnet_stats_add_basic_cpu(struct gnet_stats_basic_sync *bstats, u64 bytes, packets; do { - start = u64_stats_fetch_begin_irq(&bcpu->syncp); + start = u64_stats_fetch_begin(&bcpu->syncp); bytes = u64_stats_read(&bcpu->bytes); packets = u64_stats_read(&bcpu->packets); - } while (u64_stats_fetch_retry_irq(&bcpu->syncp, start)); + } while (u64_stats_fetch_retry(&bcpu->syncp, start)); t_bytes += bytes; t_packets += packets; @@ -162,10 +162,10 @@ void gnet_stats_add_basic(struct gnet_stats_basic_sync *bstats, } do { if (running) - start = u64_stats_fetch_begin_irq(&b->syncp); + start = u64_stats_fetch_begin(&b->syncp); bytes = u64_stats_read(&b->bytes); packets = u64_stats_read(&b->packets); - } while (running && u64_stats_fetch_retry_irq(&b->syncp, start)); + } while (running && u64_stats_fetch_retry(&b->syncp, start)); _bstats_update(bstats, bytes, packets); } @@ -187,10 +187,10 @@ static void gnet_stats_read_basic(u64 *ret_bytes, u64 *ret_packets, u64 bytes, packets; do { - start = u64_stats_fetch_begin_irq(&bcpu->syncp); + start = u64_stats_fetch_begin(&bcpu->syncp); bytes = u64_stats_read(&bcpu->bytes); packets = u64_stats_read(&bcpu->packets); - } while (u64_stats_fetch_retry_irq(&bcpu->syncp, start)); + } while (u64_stats_fetch_retry(&bcpu->syncp, start)); t_bytes += bytes; t_packets += packets; @@ -201,10 +201,10 @@ static void gnet_stats_read_basic(u64 *ret_bytes, u64 *ret_packets, } do { if (running) - start = u64_stats_fetch_begin_irq(&b->syncp); + start = u64_stats_fetch_begin(&b->syncp); *ret_bytes = u64_stats_read(&b->bytes); *ret_packets = u64_stats_read(&b->packets); - } while (running && u64_stats_fetch_retry_irq(&b->syncp, start)); + } while (running && u64_stats_fetch_retry(&b->syncp, start)); } static int diff --git a/net/dsa/slave.c b/net/dsa/slave.c index a9fde48cffd4..83e419afa89e 100644 --- a/net/dsa/slave.c +++ b/net/dsa/slave.c @@ -976,12 +976,12 @@ static void dsa_slave_get_ethtool_stats(struct net_device *dev, s = per_cpu_ptr(dev->tstats, i); do { - start = u64_stats_fetch_begin_irq(&s->syncp); + start = u64_stats_fetch_begin(&s->syncp); tx_packets = u64_stats_read(&s->tx_packets); tx_bytes = u64_stats_read(&s->tx_bytes); rx_packets = u64_stats_read(&s->rx_packets); rx_bytes = u64_stats_read(&s->rx_bytes); - } while (u64_stats_fetch_retry_irq(&s->syncp, start)); + } while (u64_stats_fetch_retry(&s->syncp, start)); data[0] += tx_packets; data[1] += tx_bytes; data[2] += rx_packets; diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c index 3dd02396517d..585f13b6fef6 100644 --- a/net/ipv4/af_inet.c +++ b/net/ipv4/af_inet.c @@ -1706,9 +1706,9 @@ u64 snmp_get_cpu_field64(void __percpu *mib, int cpu, int offt, bhptr = per_cpu_ptr(mib, cpu); syncp = (struct u64_stats_sync *)(bhptr + syncp_offset); do { - start = u64_stats_fetch_begin_irq(syncp); + start = u64_stats_fetch_begin(syncp); v = *(((u64 *)bhptr) + offt); - } while (u64_stats_fetch_retry_irq(syncp, start)); + } while (u64_stats_fetch_retry(syncp, start)); return v; } diff --git a/net/ipv6/seg6_local.c b/net/ipv6/seg6_local.c index 8370726ae7bf..487f8e98deaa 100644 --- a/net/ipv6/seg6_local.c +++ b/net/ipv6/seg6_local.c @@ -1644,13 +1644,13 @@ static int put_nla_counters(struct sk_buff *skb, struct seg6_local_lwt *slwt) pcounters = per_cpu_ptr(slwt->pcpu_counters, i); do { - start = u64_stats_fetch_begin_irq(&pcounters->syncp); + start = u64_stats_fetch_begin(&pcounters->syncp); packets = u64_stats_read(&pcounters->packets); bytes = u64_stats_read(&pcounters->bytes); errors = u64_stats_read(&pcounters->errors); - } while (u64_stats_fetch_retry_irq(&pcounters->syncp, start)); + } while (u64_stats_fetch_retry(&pcounters->syncp, start)); counters.packets += packets; counters.bytes += bytes; diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index 6e0fd82855ae..04e0f132b1d9 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -2427,9 +2427,9 @@ static inline u64 sta_get_tidstats_msdu(struct ieee80211_sta_rx_stats *rxstats, u64 value; do { - start = u64_stats_fetch_begin_irq(&rxstats->syncp); + start = u64_stats_fetch_begin(&rxstats->syncp); value = rxstats->msdu[tid]; - } while (u64_stats_fetch_retry_irq(&rxstats->syncp, start)); + } while (u64_stats_fetch_retry(&rxstats->syncp, start)); return value; } @@ -2495,9 +2495,9 @@ static inline u64 sta_get_stats_bytes(struct ieee80211_sta_rx_stats *rxstats) u64 value; do { - start = u64_stats_fetch_begin_irq(&rxstats->syncp); + start = u64_stats_fetch_begin(&rxstats->syncp); value = rxstats->bytes; - } while (u64_stats_fetch_retry_irq(&rxstats->syncp, start)); + } while (u64_stats_fetch_retry(&rxstats->syncp, start)); return value; } diff --git a/net/mpls/af_mpls.c b/net/mpls/af_mpls.c index b52afe316dc4..35b5f806fdda 100644 --- a/net/mpls/af_mpls.c +++ b/net/mpls/af_mpls.c @@ -1079,9 +1079,9 @@ static void mpls_get_stats(struct mpls_dev *mdev, p = per_cpu_ptr(mdev->stats, i); do { - start = u64_stats_fetch_begin_irq(&p->syncp); + start = u64_stats_fetch_begin(&p->syncp); local = p->stats; - } while (u64_stats_fetch_retry_irq(&p->syncp, start)); + } while (u64_stats_fetch_retry(&p->syncp, start)); stats->rx_packets += local.rx_packets; stats->rx_bytes += local.rx_bytes; diff --git a/net/netfilter/ipvs/ip_vs_ctl.c b/net/netfilter/ipvs/ip_vs_ctl.c index 988222fff9f0..4d62059a6021 100644 --- a/net/netfilter/ipvs/ip_vs_ctl.c +++ b/net/netfilter/ipvs/ip_vs_ctl.c @@ -2296,13 +2296,13 @@ static int ip_vs_stats_percpu_show(struct seq_file *seq, void *v) u64 conns, inpkts, outpkts, inbytes, outbytes; do { - start = u64_stats_fetch_begin_irq(&u->syncp); + start = u64_stats_fetch_begin(&u->syncp); conns = u->cnt.conns; inpkts = u->cnt.inpkts; outpkts = u->cnt.outpkts; inbytes = u->cnt.inbytes; outbytes = u->cnt.outbytes; - } while (u64_stats_fetch_retry_irq(&u->syncp, start)); + } while (u64_stats_fetch_retry(&u->syncp, start)); seq_printf(seq, "%3X %8LX %8LX %8LX %16LX %16LX\n", i, (u64)conns, (u64)inpkts, diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c index 6b79f5e18f08..62da204eed41 100644 --- a/net/netfilter/nf_tables_api.c +++ b/net/netfilter/nf_tables_api.c @@ -1534,10 +1534,10 @@ static int nft_dump_stats(struct sk_buff *skb, struct nft_stats __percpu *stats) for_each_possible_cpu(cpu) { cpu_stats = per_cpu_ptr(stats, cpu); do { - seq = u64_stats_fetch_begin_irq(&cpu_stats->syncp); + seq = u64_stats_fetch_begin(&cpu_stats->syncp); pkts = cpu_stats->pkts; bytes = cpu_stats->bytes; - } while (u64_stats_fetch_retry_irq(&cpu_stats->syncp, seq)); + } while (u64_stats_fetch_retry(&cpu_stats->syncp, seq)); total.pkts += pkts; total.bytes += bytes; } diff --git a/net/openvswitch/datapath.c b/net/openvswitch/datapath.c index 155263e73512..f95b716ea96d 100644 --- a/net/openvswitch/datapath.c +++ b/net/openvswitch/datapath.c @@ -716,9 +716,9 @@ static void get_dp_stats(const struct datapath *dp, struct ovs_dp_stats *stats, percpu_stats = per_cpu_ptr(dp->stats_percpu, i); do { - start = u64_stats_fetch_begin_irq(&percpu_stats->syncp); + start = u64_stats_fetch_begin(&percpu_stats->syncp); local_stats = *percpu_stats; - } while (u64_stats_fetch_retry_irq(&percpu_stats->syncp, start)); + } while (u64_stats_fetch_retry(&percpu_stats->syncp, start)); stats->n_hit += local_stats.n_hit; stats->n_missed += local_stats.n_missed; diff --git a/net/openvswitch/flow_table.c b/net/openvswitch/flow_table.c index d4a2db0b2299..0a0e4c283f02 100644 --- a/net/openvswitch/flow_table.c +++ b/net/openvswitch/flow_table.c @@ -205,9 +205,9 @@ static void tbl_mask_array_reset_counters(struct mask_array *ma) stats = per_cpu_ptr(ma->masks_usage_stats, cpu); do { - start = u64_stats_fetch_begin_irq(&stats->syncp); + start = u64_stats_fetch_begin(&stats->syncp); counter = stats->usage_cntrs[i]; - } while (u64_stats_fetch_retry_irq(&stats->syncp, start)); + } while (u64_stats_fetch_retry(&stats->syncp, start)); ma->masks_usage_zero_cntr[i] += counter; } @@ -1136,10 +1136,9 @@ void ovs_flow_masks_rebalance(struct flow_table *table) stats = per_cpu_ptr(ma->masks_usage_stats, cpu); do { - start = u64_stats_fetch_begin_irq(&stats->syncp); + start = u64_stats_fetch_begin(&stats->syncp); counter = stats->usage_cntrs[i]; - } while (u64_stats_fetch_retry_irq(&stats->syncp, - start)); + } while (u64_stats_fetch_retry(&stats->syncp, start)); masks_and_count[i].counter += counter; } From 9c5a170677c3c8facc83e931a57f4c99c0511ae0 Mon Sep 17 00:00:00 2001 From: "Russell King (Oracle)" Date: Thu, 27 Oct 2022 14:10:37 +0100 Subject: [PATCH 0447/2002] net: phylink: add phylink_get_link_timer_ns() helper Add a helper to convert the PHY interface mode to the required link timer setting as stated by the appropriate standard. Inappropriate interface modes return an error. Signed-off-by: Russell King (Oracle) Signed-off-by: Jakub Kicinski --- include/linux/phylink.h | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/include/linux/phylink.h b/include/linux/phylink.h index 63800bf4a7ac..1df3e5e316e8 100644 --- a/include/linux/phylink.h +++ b/include/linux/phylink.h @@ -616,6 +616,30 @@ int phylink_speed_up(struct phylink *pl); void phylink_set_port_modes(unsigned long *bits); +/** + * phylink_get_link_timer_ns - return the PCS link timer value + * @interface: link &typedef phy_interface_t mode + * + * Return the PCS link timer setting in nanoseconds for the PHY @interface + * mode, or -EINVAL if not appropriate. + */ +static inline int phylink_get_link_timer_ns(phy_interface_t interface) +{ + switch (interface) { + case PHY_INTERFACE_MODE_SGMII: + case PHY_INTERFACE_MODE_QSGMII: + case PHY_INTERFACE_MODE_USXGMII: + return 1600000; + + case PHY_INTERFACE_MODE_1000BASEX: + case PHY_INTERFACE_MODE_2500BASEX: + return 10000000; + + default: + return -EINVAL; + } +} + void phylink_mii_c22_pcs_decode_state(struct phylink_link_state *state, u16 bmsr, u16 lpa); void phylink_mii_c22_pcs_get_state(struct mdio_device *pcs, From b6a709cb51f7bdc55c01cec886098a9753ce8c28 Mon Sep 17 00:00:00 2001 From: "Russell King (Oracle)" Date: Thu, 27 Oct 2022 14:10:42 +0100 Subject: [PATCH 0448/2002] net: mtk_eth_soc: add definitions for PCS As a result of help from Frank Wunderlich to investigate and test, we know a bit more about the PCS on the Mediatek platforms. Update the definitions from this investigation. This PCS appears similar, but not identical to the Lynx PCS. Although not included in this patch, but for future reference, the PHY ID registers at offset 4 read as 0x4d544950 'MTIP'. Signed-off-by: Russell King (Oracle) Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/mediatek/mtk_eth_soc.h | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.h b/drivers/net/ethernet/mediatek/mtk_eth_soc.h index b52f3b0177ef..589f27ddc401 100644 --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h @@ -466,8 +466,10 @@ #define ETHSYS_DMA_AG_MAP_PPE BIT(2) /* SGMII subsystem config registers */ -/* Register to auto-negotiation restart */ +/* BMCR (low 16) BMSR (high 16) */ #define SGMSYS_PCS_CONTROL_1 0x0 +#define SGMII_BMCR GENMASK(15, 0) +#define SGMII_BMSR GENMASK(31, 16) #define SGMII_AN_RESTART BIT(9) #define SGMII_ISOLATE BIT(10) #define SGMII_AN_ENABLE BIT(12) @@ -477,13 +479,18 @@ #define SGMII_PCS_FAULT BIT(23) #define SGMII_AN_EXPANSION_CLR BIT(30) +#define SGMSYS_PCS_ADVERTISE 0x8 +#define SGMII_ADVERTISE GENMASK(15, 0) +#define SGMII_LPA GENMASK(31, 16) + /* Register to programmable link timer, the unit in 2 * 8ns */ #define SGMSYS_PCS_LINK_TIMER 0x18 -#define SGMII_LINK_TIMER_DEFAULT (0x186a0 & GENMASK(19, 0)) +#define SGMII_LINK_TIMER_MASK GENMASK(19, 0) +#define SGMII_LINK_TIMER_DEFAULT (0x186a0 & SGMII_LINK_TIMER_MASK) /* Register to control remote fault */ #define SGMSYS_SGMII_MODE 0x20 -#define SGMII_IF_MODE_BIT0 BIT(0) +#define SGMII_IF_MODE_SGMII BIT(0) #define SGMII_SPEED_DUPLEX_AN BIT(1) #define SGMII_SPEED_MASK GENMASK(3, 2) #define SGMII_SPEED_10 FIELD_PREP(SGMII_SPEED_MASK, 0) From 5cf7797526ee81bea0f627bccaa3d887f48f53e0 Mon Sep 17 00:00:00 2001 From: "Russell King (Oracle)" Date: Thu, 27 Oct 2022 14:10:47 +0100 Subject: [PATCH 0449/2002] net: mtk_eth_soc: eliminate unnecessary error handling The functions called by the pcs_config() method always return zero, so there is no point trying to handle an error from these functions. Make these functions void, eliminate the "err" variable and simply return zero from the pcs_config() function itself. Signed-off-by: Russell King (Oracle) Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/mediatek/mtk_sgmii.c | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/drivers/net/ethernet/mediatek/mtk_sgmii.c b/drivers/net/ethernet/mediatek/mtk_sgmii.c index 736839c84130..7637ba16b44b 100644 --- a/drivers/net/ethernet/mediatek/mtk_sgmii.c +++ b/drivers/net/ethernet/mediatek/mtk_sgmii.c @@ -20,7 +20,7 @@ static struct mtk_pcs *pcs_to_mtk_pcs(struct phylink_pcs *pcs) } /* For SGMII interface mode */ -static int mtk_pcs_setup_mode_an(struct mtk_pcs *mpcs) +static void mtk_pcs_setup_mode_an(struct mtk_pcs *mpcs) { unsigned int val; @@ -39,16 +39,13 @@ static int mtk_pcs_setup_mode_an(struct mtk_pcs *mpcs) regmap_read(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL, &val); val &= ~SGMII_PHYA_PWD; regmap_write(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL, val); - - return 0; - } /* For 1000BASE-X and 2500BASE-X interface modes, which operate at a * fixed speed. */ -static int mtk_pcs_setup_mode_force(struct mtk_pcs *mpcs, - phy_interface_t interface) +static void mtk_pcs_setup_mode_force(struct mtk_pcs *mpcs, + phy_interface_t interface) { unsigned int val; @@ -73,8 +70,6 @@ static int mtk_pcs_setup_mode_force(struct mtk_pcs *mpcs, regmap_read(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL, &val); val &= ~SGMII_PHYA_PWD; regmap_write(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL, val); - - return 0; } static int mtk_pcs_config(struct phylink_pcs *pcs, unsigned int mode, @@ -83,15 +78,14 @@ static int mtk_pcs_config(struct phylink_pcs *pcs, unsigned int mode, bool permit_pause_to_mac) { struct mtk_pcs *mpcs = pcs_to_mtk_pcs(pcs); - int err = 0; /* Setup SGMIISYS with the determined property */ if (interface != PHY_INTERFACE_MODE_SGMII) - err = mtk_pcs_setup_mode_force(mpcs, interface); + mtk_pcs_setup_mode_force(mpcs, interface); else if (phylink_autoneg_inband(mode)) - err = mtk_pcs_setup_mode_an(mpcs); + mtk_pcs_setup_mode_an(mpcs); - return err; + return 0; } static void mtk_pcs_restart_an(struct phylink_pcs *pcs) From c000dca098002da193b98099df051c9ead0cacb4 Mon Sep 17 00:00:00 2001 From: "Russell King (Oracle)" Date: Thu, 27 Oct 2022 14:10:52 +0100 Subject: [PATCH 0450/2002] net: mtk_eth_soc: add pcs_get_state() implementation Add a pcs_get_state() implementation which uses the advertisements to compute the resulting link modes, and BMSR contents to determine negotiation and link status. Signed-off-by: Russell King (Oracle) Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/mediatek/mtk_sgmii.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/drivers/net/ethernet/mediatek/mtk_sgmii.c b/drivers/net/ethernet/mediatek/mtk_sgmii.c index 7637ba16b44b..63785bd9a118 100644 --- a/drivers/net/ethernet/mediatek/mtk_sgmii.c +++ b/drivers/net/ethernet/mediatek/mtk_sgmii.c @@ -19,6 +19,20 @@ static struct mtk_pcs *pcs_to_mtk_pcs(struct phylink_pcs *pcs) return container_of(pcs, struct mtk_pcs, pcs); } +static void mtk_pcs_get_state(struct phylink_pcs *pcs, + struct phylink_link_state *state) +{ + struct mtk_pcs *mpcs = pcs_to_mtk_pcs(pcs); + unsigned int bm, adv; + + /* Read the BMSR and LPA */ + regmap_read(mpcs->regmap, SGMSYS_PCS_CONTROL_1, &bm); + regmap_read(mpcs->regmap, SGMSYS_PCS_ADVERTISE, &adv); + + phylink_mii_c22_pcs_decode_state(state, FIELD_GET(SGMII_BMSR, bm), + FIELD_GET(SGMII_LPA, adv)); +} + /* For SGMII interface mode */ static void mtk_pcs_setup_mode_an(struct mtk_pcs *mpcs) { @@ -117,6 +131,7 @@ static void mtk_pcs_link_up(struct phylink_pcs *pcs, unsigned int mode, } static const struct phylink_pcs_ops mtk_pcs_ops = { + .pcs_get_state = mtk_pcs_get_state, .pcs_config = mtk_pcs_config, .pcs_an_restart = mtk_pcs_restart_an, .pcs_link_up = mtk_pcs_link_up, From 0d2351dc2768061689abd4de1529fa206bbd574e Mon Sep 17 00:00:00 2001 From: "Russell King (Oracle)" Date: Thu, 27 Oct 2022 14:10:58 +0100 Subject: [PATCH 0451/2002] net: mtk_eth_soc: convert mtk_sgmii to use regmap_update_bits() mtk_sgmii does a lot of read-modify-write operations, for which there is a specific regmap function. Use this function instead of open-coding the operations. Signed-off-by: Russell King (Oracle) Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/mediatek/mtk_sgmii.c | 61 ++++++++++------------- 1 file changed, 26 insertions(+), 35 deletions(-) diff --git a/drivers/net/ethernet/mediatek/mtk_sgmii.c b/drivers/net/ethernet/mediatek/mtk_sgmii.c index 63785bd9a118..868ff0b2e036 100644 --- a/drivers/net/ethernet/mediatek/mtk_sgmii.c +++ b/drivers/net/ethernet/mediatek/mtk_sgmii.c @@ -36,23 +36,18 @@ static void mtk_pcs_get_state(struct phylink_pcs *pcs, /* For SGMII interface mode */ static void mtk_pcs_setup_mode_an(struct mtk_pcs *mpcs) { - unsigned int val; - /* Setup the link timer and QPHY power up inside SGMIISYS */ regmap_write(mpcs->regmap, SGMSYS_PCS_LINK_TIMER, SGMII_LINK_TIMER_DEFAULT); - regmap_read(mpcs->regmap, SGMSYS_SGMII_MODE, &val); - val |= SGMII_REMOTE_FAULT_DIS; - regmap_write(mpcs->regmap, SGMSYS_SGMII_MODE, val); + regmap_update_bits(mpcs->regmap, SGMSYS_SGMII_MODE, + SGMII_REMOTE_FAULT_DIS, SGMII_REMOTE_FAULT_DIS); - regmap_read(mpcs->regmap, SGMSYS_PCS_CONTROL_1, &val); - val |= SGMII_AN_RESTART; - regmap_write(mpcs->regmap, SGMSYS_PCS_CONTROL_1, val); + regmap_update_bits(mpcs->regmap, SGMSYS_PCS_CONTROL_1, + SGMII_AN_RESTART, SGMII_AN_RESTART); - regmap_read(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL, &val); - val &= ~SGMII_PHYA_PWD; - regmap_write(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL, val); + regmap_update_bits(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL, + SGMII_PHYA_PWD, 0); } /* For 1000BASE-X and 2500BASE-X interface modes, which operate at a @@ -61,29 +56,26 @@ static void mtk_pcs_setup_mode_an(struct mtk_pcs *mpcs) static void mtk_pcs_setup_mode_force(struct mtk_pcs *mpcs, phy_interface_t interface) { - unsigned int val; + unsigned int rgc3; - regmap_read(mpcs->regmap, mpcs->ana_rgc3, &val); - val &= ~RG_PHY_SPEED_MASK; if (interface == PHY_INTERFACE_MODE_2500BASEX) - val |= RG_PHY_SPEED_3_125G; - regmap_write(mpcs->regmap, mpcs->ana_rgc3, val); + rgc3 = RG_PHY_SPEED_3_125G; + + regmap_update_bits(mpcs->regmap, mpcs->ana_rgc3, + RG_PHY_SPEED_3_125G, rgc3); /* Disable SGMII AN */ - regmap_read(mpcs->regmap, SGMSYS_PCS_CONTROL_1, &val); - val &= ~SGMII_AN_ENABLE; - regmap_write(mpcs->regmap, SGMSYS_PCS_CONTROL_1, val); + regmap_update_bits(mpcs->regmap, SGMSYS_PCS_CONTROL_1, + SGMII_AN_ENABLE, 0); /* Set the speed etc but leave the duplex unchanged */ - regmap_read(mpcs->regmap, SGMSYS_SGMII_MODE, &val); - val &= SGMII_DUPLEX_FULL | ~SGMII_IF_MODE_MASK; - val |= SGMII_SPEED_1000; - regmap_write(mpcs->regmap, SGMSYS_SGMII_MODE, val); + regmap_update_bits(mpcs->regmap, SGMSYS_SGMII_MODE, + SGMII_IF_MODE_MASK & ~SGMII_DUPLEX_FULL, + SGMII_SPEED_1000); /* Release PHYA power down state */ - regmap_read(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL, &val); - val &= ~SGMII_PHYA_PWD; - regmap_write(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL, val); + regmap_update_bits(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL, + SGMII_PHYA_PWD, 0); } static int mtk_pcs_config(struct phylink_pcs *pcs, unsigned int mode, @@ -105,29 +97,28 @@ static int mtk_pcs_config(struct phylink_pcs *pcs, unsigned int mode, static void mtk_pcs_restart_an(struct phylink_pcs *pcs) { struct mtk_pcs *mpcs = pcs_to_mtk_pcs(pcs); - unsigned int val; - regmap_read(mpcs->regmap, SGMSYS_PCS_CONTROL_1, &val); - val |= SGMII_AN_RESTART; - regmap_write(mpcs->regmap, SGMSYS_PCS_CONTROL_1, val); + regmap_update_bits(mpcs->regmap, SGMSYS_PCS_CONTROL_1, + SGMII_AN_RESTART, SGMII_AN_RESTART); } static void mtk_pcs_link_up(struct phylink_pcs *pcs, unsigned int mode, phy_interface_t interface, int speed, int duplex) { struct mtk_pcs *mpcs = pcs_to_mtk_pcs(pcs); - unsigned int val; + unsigned int sgm_mode; if (!phy_interface_mode_is_8023z(interface)) return; /* SGMII force duplex setting */ - regmap_read(mpcs->regmap, SGMSYS_SGMII_MODE, &val); - val &= ~SGMII_DUPLEX_FULL; if (duplex == DUPLEX_FULL) - val |= SGMII_DUPLEX_FULL; + sgm_mode = SGMII_DUPLEX_FULL; + else + sgm_mode = 0; - regmap_write(mpcs->regmap, SGMSYS_SGMII_MODE, val); + regmap_update_bits(mpcs->regmap, SGMSYS_SGMII_MODE, + SGMII_DUPLEX_FULL, sgm_mode); } static const struct phylink_pcs_ops mtk_pcs_ops = { From 12198c3a410fe69843e335c1bbf6d4c2a4d48e4e Mon Sep 17 00:00:00 2001 From: "Russell King (Oracle)" Date: Thu, 27 Oct 2022 14:11:03 +0100 Subject: [PATCH 0452/2002] net: mtk_eth_soc: add out of band forcing of speed and duplex in pcs_link_up Add support for forcing the link speed and duplex setting in the pcs_link_up() method for out of band modes, which will be useful when we finish converting the pcs_config() method. Until then, we still have to force duplex for 802.3z modes to work correctly. Signed-off-by: Russell King (Oracle) Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/mediatek/mtk_sgmii.c | 24 ++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/drivers/net/ethernet/mediatek/mtk_sgmii.c b/drivers/net/ethernet/mediatek/mtk_sgmii.c index 868ff0b2e036..d26a0ba2e47b 100644 --- a/drivers/net/ethernet/mediatek/mtk_sgmii.c +++ b/drivers/net/ethernet/mediatek/mtk_sgmii.c @@ -108,17 +108,23 @@ static void mtk_pcs_link_up(struct phylink_pcs *pcs, unsigned int mode, struct mtk_pcs *mpcs = pcs_to_mtk_pcs(pcs); unsigned int sgm_mode; - if (!phy_interface_mode_is_8023z(interface)) - return; + if (!phylink_autoneg_inband(mode) || + phy_interface_mode_is_8023z(interface)) { + /* Force the speed and duplex setting */ + if (speed == SPEED_10) + sgm_mode = SGMII_SPEED_10; + else if (speed == SPEED_100) + sgm_mode = SGMII_SPEED_100; + else + sgm_mode = SGMII_SPEED_1000; - /* SGMII force duplex setting */ - if (duplex == DUPLEX_FULL) - sgm_mode = SGMII_DUPLEX_FULL; - else - sgm_mode = 0; + if (duplex == DUPLEX_FULL) + sgm_mode |= SGMII_DUPLEX_FULL; - regmap_update_bits(mpcs->regmap, SGMSYS_SGMII_MODE, - SGMII_DUPLEX_FULL, sgm_mode); + regmap_update_bits(mpcs->regmap, SGMSYS_SGMII_MODE, + SGMII_DUPLEX_FULL | SGMII_SPEED_MASK, + sgm_mode); + } } static const struct phylink_pcs_ops mtk_pcs_ops = { From 6f38fffe2179dd29612aea2c67c46ed6682b4e46 Mon Sep 17 00:00:00 2001 From: "Russell King (Oracle)" Date: Thu, 27 Oct 2022 14:11:08 +0100 Subject: [PATCH 0453/2002] net: mtk_eth_soc: move PHY power up The PHY power up is common to both configuration paths, so move it into the parent function. We need to do this for all serdes modes. Signed-off-by: Russell King (Oracle) Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/mediatek/mtk_sgmii.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/drivers/net/ethernet/mediatek/mtk_sgmii.c b/drivers/net/ethernet/mediatek/mtk_sgmii.c index d26a0ba2e47b..63b25574caac 100644 --- a/drivers/net/ethernet/mediatek/mtk_sgmii.c +++ b/drivers/net/ethernet/mediatek/mtk_sgmii.c @@ -45,9 +45,6 @@ static void mtk_pcs_setup_mode_an(struct mtk_pcs *mpcs) regmap_update_bits(mpcs->regmap, SGMSYS_PCS_CONTROL_1, SGMII_AN_RESTART, SGMII_AN_RESTART); - - regmap_update_bits(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL, - SGMII_PHYA_PWD, 0); } /* For 1000BASE-X and 2500BASE-X interface modes, which operate at a @@ -72,10 +69,6 @@ static void mtk_pcs_setup_mode_force(struct mtk_pcs *mpcs, regmap_update_bits(mpcs->regmap, SGMSYS_SGMII_MODE, SGMII_IF_MODE_MASK & ~SGMII_DUPLEX_FULL, SGMII_SPEED_1000); - - /* Release PHYA power down state */ - regmap_update_bits(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL, - SGMII_PHYA_PWD, 0); } static int mtk_pcs_config(struct phylink_pcs *pcs, unsigned int mode, @@ -91,6 +84,10 @@ static int mtk_pcs_config(struct phylink_pcs *pcs, unsigned int mode, else if (phylink_autoneg_inband(mode)) mtk_pcs_setup_mode_an(mpcs); + /* Release PHYA power down state */ + regmap_update_bits(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL, + SGMII_PHYA_PWD, 0); + return 0; } From f752c0df13dfeb721c11d3debb79f08cf437344f Mon Sep 17 00:00:00 2001 From: "Russell King (Oracle)" Date: Thu, 27 Oct 2022 14:11:13 +0100 Subject: [PATCH 0454/2002] net: mtk_eth_soc: move interface speed selection Move the selection of the underlying interface speed to the pcs_config function, so we always program the interface speed. Signed-off-by: Russell King (Oracle) Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/mediatek/mtk_sgmii.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/drivers/net/ethernet/mediatek/mtk_sgmii.c b/drivers/net/ethernet/mediatek/mtk_sgmii.c index 63b25574caac..c590d5847e2e 100644 --- a/drivers/net/ethernet/mediatek/mtk_sgmii.c +++ b/drivers/net/ethernet/mediatek/mtk_sgmii.c @@ -53,14 +53,6 @@ static void mtk_pcs_setup_mode_an(struct mtk_pcs *mpcs) static void mtk_pcs_setup_mode_force(struct mtk_pcs *mpcs, phy_interface_t interface) { - unsigned int rgc3; - - if (interface == PHY_INTERFACE_MODE_2500BASEX) - rgc3 = RG_PHY_SPEED_3_125G; - - regmap_update_bits(mpcs->regmap, mpcs->ana_rgc3, - RG_PHY_SPEED_3_125G, rgc3); - /* Disable SGMII AN */ regmap_update_bits(mpcs->regmap, SGMSYS_PCS_CONTROL_1, SGMII_AN_ENABLE, 0); @@ -77,6 +69,16 @@ static int mtk_pcs_config(struct phylink_pcs *pcs, unsigned int mode, bool permit_pause_to_mac) { struct mtk_pcs *mpcs = pcs_to_mtk_pcs(pcs); + unsigned int rgc3; + + if (interface == PHY_INTERFACE_MODE_2500BASEX) + rgc3 = RG_PHY_SPEED_3_125G; + else + rgc3 = 0; + + /* Configure the underlying interface speed */ + regmap_update_bits(mpcs->regmap, mpcs->ana_rgc3, + RG_PHY_SPEED_3_125G, rgc3); /* Setup SGMIISYS with the determined property */ if (interface != PHY_INTERFACE_MODE_SGMII) From c125c66ea71b9377ae2478c4f1b87b180cc5c6ef Mon Sep 17 00:00:00 2001 From: "Russell King (Oracle)" Date: Thu, 27 Oct 2022 14:11:18 +0100 Subject: [PATCH 0455/2002] net: mtk_eth_soc: add advertisement programming Program the advertisement into the mtk PCS block. Signed-off-by: Russell King (Oracle) Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/mediatek/mtk_sgmii.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/mediatek/mtk_sgmii.c b/drivers/net/ethernet/mediatek/mtk_sgmii.c index c590d5847e2e..1f6e58cba162 100644 --- a/drivers/net/ethernet/mediatek/mtk_sgmii.c +++ b/drivers/net/ethernet/mediatek/mtk_sgmii.c @@ -70,16 +70,27 @@ static int mtk_pcs_config(struct phylink_pcs *pcs, unsigned int mode, { struct mtk_pcs *mpcs = pcs_to_mtk_pcs(pcs); unsigned int rgc3; + int advertise; + bool changed; if (interface == PHY_INTERFACE_MODE_2500BASEX) rgc3 = RG_PHY_SPEED_3_125G; else rgc3 = 0; + advertise = phylink_mii_c22_pcs_encode_advertisement(interface, + advertising); + if (advertise < 0) + return advertise; + /* Configure the underlying interface speed */ regmap_update_bits(mpcs->regmap, mpcs->ana_rgc3, RG_PHY_SPEED_3_125G, rgc3); + /* Update the advertisement, noting whether it has changed */ + regmap_update_bits_check(mpcs->regmap, SGMSYS_PCS_ADVERTISE, + SGMII_ADVERTISE, advertise, &changed); + /* Setup SGMIISYS with the determined property */ if (interface != PHY_INTERFACE_MODE_SGMII) mtk_pcs_setup_mode_force(mpcs, interface); @@ -90,7 +101,7 @@ static int mtk_pcs_config(struct phylink_pcs *pcs, unsigned int mode, regmap_update_bits(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL, SGMII_PHYA_PWD, 0); - return 0; + return changed; } static void mtk_pcs_restart_an(struct phylink_pcs *pcs) From 3027d89f87707e7f3e5b683e0d37a32afb5bde96 Mon Sep 17 00:00:00 2001 From: "Russell King (Oracle)" Date: Thu, 27 Oct 2022 14:11:23 +0100 Subject: [PATCH 0456/2002] net: mtk_eth_soc: move and correct link timer programming Program the link timer appropriately for the interface mode being used, using the newly introduced phylink helper that provides the nanosecond link timer interval. The intervals are 1.6ms for SGMII based protocols and 10ms for 802.3z based protocols. Signed-off-by: Russell King (Oracle) Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/mediatek/mtk_sgmii.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/drivers/net/ethernet/mediatek/mtk_sgmii.c b/drivers/net/ethernet/mediatek/mtk_sgmii.c index 1f6e58cba162..12e01d0ef52d 100644 --- a/drivers/net/ethernet/mediatek/mtk_sgmii.c +++ b/drivers/net/ethernet/mediatek/mtk_sgmii.c @@ -36,10 +36,6 @@ static void mtk_pcs_get_state(struct phylink_pcs *pcs, /* For SGMII interface mode */ static void mtk_pcs_setup_mode_an(struct mtk_pcs *mpcs) { - /* Setup the link timer and QPHY power up inside SGMIISYS */ - regmap_write(mpcs->regmap, SGMSYS_PCS_LINK_TIMER, - SGMII_LINK_TIMER_DEFAULT); - regmap_update_bits(mpcs->regmap, SGMSYS_SGMII_MODE, SGMII_REMOTE_FAULT_DIS, SGMII_REMOTE_FAULT_DIS); @@ -69,8 +65,8 @@ static int mtk_pcs_config(struct phylink_pcs *pcs, unsigned int mode, bool permit_pause_to_mac) { struct mtk_pcs *mpcs = pcs_to_mtk_pcs(pcs); + int advertise, link_timer; unsigned int rgc3; - int advertise; bool changed; if (interface == PHY_INTERFACE_MODE_2500BASEX) @@ -83,6 +79,10 @@ static int mtk_pcs_config(struct phylink_pcs *pcs, unsigned int mode, if (advertise < 0) return advertise; + link_timer = phylink_get_link_timer_ns(interface); + if (link_timer < 0) + return link_timer; + /* Configure the underlying interface speed */ regmap_update_bits(mpcs->regmap, mpcs->ana_rgc3, RG_PHY_SPEED_3_125G, rgc3); @@ -91,6 +91,9 @@ static int mtk_pcs_config(struct phylink_pcs *pcs, unsigned int mode, regmap_update_bits_check(mpcs->regmap, SGMSYS_PCS_ADVERTISE, SGMII_ADVERTISE, advertise, &changed); + /* Setup the link timer and QPHY power up inside SGMIISYS */ + regmap_write(mpcs->regmap, SGMSYS_PCS_LINK_TIMER, link_timer / 2 / 8); + /* Setup SGMIISYS with the determined property */ if (interface != PHY_INTERFACE_MODE_SGMII) mtk_pcs_setup_mode_force(mpcs, interface); From 81b0f12a2a8a1699a7d49c3995e5f71e4ec018e6 Mon Sep 17 00:00:00 2001 From: "Russell King (Oracle)" Date: Thu, 27 Oct 2022 14:11:28 +0100 Subject: [PATCH 0457/2002] net: mtk_eth_soc: add support for in-band 802.3z negotiation As a result of help from Frank Wunderlich to investigate and test, we now know how to program this PCS for in-band 802.3z negotiation. Add support for this by moving the contents of the two functions into the common mtk_pcs_config() function and adding the register settings for 802.3z negotiation. Signed-off-by: Russell King (Oracle) Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/mediatek/mtk_sgmii.c | 77 ++++++++++++----------- 1 file changed, 42 insertions(+), 35 deletions(-) diff --git a/drivers/net/ethernet/mediatek/mtk_sgmii.c b/drivers/net/ethernet/mediatek/mtk_sgmii.c index 12e01d0ef52d..5c286f2c9418 100644 --- a/drivers/net/ethernet/mediatek/mtk_sgmii.c +++ b/drivers/net/ethernet/mediatek/mtk_sgmii.c @@ -33,41 +33,15 @@ static void mtk_pcs_get_state(struct phylink_pcs *pcs, FIELD_GET(SGMII_LPA, adv)); } -/* For SGMII interface mode */ -static void mtk_pcs_setup_mode_an(struct mtk_pcs *mpcs) -{ - regmap_update_bits(mpcs->regmap, SGMSYS_SGMII_MODE, - SGMII_REMOTE_FAULT_DIS, SGMII_REMOTE_FAULT_DIS); - - regmap_update_bits(mpcs->regmap, SGMSYS_PCS_CONTROL_1, - SGMII_AN_RESTART, SGMII_AN_RESTART); -} - -/* For 1000BASE-X and 2500BASE-X interface modes, which operate at a - * fixed speed. - */ -static void mtk_pcs_setup_mode_force(struct mtk_pcs *mpcs, - phy_interface_t interface) -{ - /* Disable SGMII AN */ - regmap_update_bits(mpcs->regmap, SGMSYS_PCS_CONTROL_1, - SGMII_AN_ENABLE, 0); - - /* Set the speed etc but leave the duplex unchanged */ - regmap_update_bits(mpcs->regmap, SGMSYS_SGMII_MODE, - SGMII_IF_MODE_MASK & ~SGMII_DUPLEX_FULL, - SGMII_SPEED_1000); -} - static int mtk_pcs_config(struct phylink_pcs *pcs, unsigned int mode, phy_interface_t interface, const unsigned long *advertising, bool permit_pause_to_mac) { struct mtk_pcs *mpcs = pcs_to_mtk_pcs(pcs); + unsigned int rgc3, sgm_mode, bmcr; int advertise, link_timer; - unsigned int rgc3; - bool changed; + bool changed, use_an; if (interface == PHY_INTERFACE_MODE_2500BASEX) rgc3 = RG_PHY_SPEED_3_125G; @@ -83,6 +57,37 @@ static int mtk_pcs_config(struct phylink_pcs *pcs, unsigned int mode, if (link_timer < 0) return link_timer; + /* Clearing IF_MODE_BIT0 switches the PCS to BASE-X mode, and + * we assume that fixes it's speed at bitrate = line rate (in + * other words, 1000Mbps or 2500Mbps). + */ + if (interface == PHY_INTERFACE_MODE_SGMII) { + sgm_mode = SGMII_IF_MODE_SGMII; + if (phylink_autoneg_inband(mode)) { + sgm_mode |= SGMII_REMOTE_FAULT_DIS | + SGMII_SPEED_DUPLEX_AN; + use_an = true; + } else { + use_an = false; + } + } else if (phylink_autoneg_inband(mode)) { + /* 1000base-X or 2500base-X autoneg */ + sgm_mode = SGMII_REMOTE_FAULT_DIS; + use_an = linkmode_test_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, + advertising); + } else { + /* 1000base-X or 2500base-X without autoneg */ + sgm_mode = 0; + use_an = false; + } + + if (use_an) { + /* FIXME: Do we need to set AN_RESTART here? */ + bmcr = SGMII_AN_RESTART | SGMII_AN_ENABLE; + } else { + bmcr = 0; + } + /* Configure the underlying interface speed */ regmap_update_bits(mpcs->regmap, mpcs->ana_rgc3, RG_PHY_SPEED_3_125G, rgc3); @@ -94,11 +99,14 @@ static int mtk_pcs_config(struct phylink_pcs *pcs, unsigned int mode, /* Setup the link timer and QPHY power up inside SGMIISYS */ regmap_write(mpcs->regmap, SGMSYS_PCS_LINK_TIMER, link_timer / 2 / 8); - /* Setup SGMIISYS with the determined property */ - if (interface != PHY_INTERFACE_MODE_SGMII) - mtk_pcs_setup_mode_force(mpcs, interface); - else if (phylink_autoneg_inband(mode)) - mtk_pcs_setup_mode_an(mpcs); + /* Update the sgmsys mode register */ + regmap_update_bits(mpcs->regmap, SGMSYS_SGMII_MODE, + SGMII_REMOTE_FAULT_DIS | SGMII_SPEED_DUPLEX_AN | + SGMII_IF_MODE_SGMII, sgm_mode); + + /* Update the BMCR */ + regmap_update_bits(mpcs->regmap, SGMSYS_PCS_CONTROL_1, + SGMII_AN_RESTART | SGMII_AN_ENABLE, bmcr); /* Release PHYA power down state */ regmap_update_bits(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL, @@ -121,8 +129,7 @@ static void mtk_pcs_link_up(struct phylink_pcs *pcs, unsigned int mode, struct mtk_pcs *mpcs = pcs_to_mtk_pcs(pcs); unsigned int sgm_mode; - if (!phylink_autoneg_inband(mode) || - phy_interface_mode_is_8023z(interface)) { + if (!phylink_autoneg_inband(mode)) { /* Force the speed and duplex setting */ if (speed == SPEED_10) sgm_mode = SGMII_SPEED_10; From 17dd361119e5bc4cfb85aed660674a846b613817 Mon Sep 17 00:00:00 2001 From: "Russell King (Oracle)" Date: Thu, 27 Oct 2022 14:21:16 +0100 Subject: [PATCH 0458/2002] net: sfp: convert register indexes from hex to decimal The register indexes in the standards are in decimal rather than hex, so lets specify them in decimal in the header file so we can easily cross-reference without converting between hex and decimal. Signed-off-by: Russell King (Oracle) Reviewed-by: Andrew Lunn Signed-off-by: Jakub Kicinski --- include/linux/sfp.h | 176 ++++++++++++++++++++++---------------------- 1 file changed, 88 insertions(+), 88 deletions(-) diff --git a/include/linux/sfp.h b/include/linux/sfp.h index 01ae9f1dd2ad..4e2db155664d 100644 --- a/include/linux/sfp.h +++ b/include/linux/sfp.h @@ -332,37 +332,37 @@ enum { /* SFP EEPROM registers */ enum { - SFP_PHYS_ID = 0x00, - SFP_PHYS_EXT_ID = 0x01, - SFP_CONNECTOR = 0x02, - SFP_COMPLIANCE = 0x03, - SFP_ENCODING = 0x0b, - SFP_BR_NOMINAL = 0x0c, - SFP_RATE_ID = 0x0d, - SFP_LINK_LEN_SM_KM = 0x0e, - SFP_LINK_LEN_SM_100M = 0x0f, - SFP_LINK_LEN_50UM_OM2_10M = 0x10, - SFP_LINK_LEN_62_5UM_OM1_10M = 0x11, - SFP_LINK_LEN_COPPER_1M = 0x12, - SFP_LINK_LEN_50UM_OM4_10M = 0x12, - SFP_LINK_LEN_50UM_OM3_10M = 0x13, - SFP_VENDOR_NAME = 0x14, - SFP_VENDOR_OUI = 0x25, - SFP_VENDOR_PN = 0x28, - SFP_VENDOR_REV = 0x38, - SFP_OPTICAL_WAVELENGTH_MSB = 0x3c, - SFP_OPTICAL_WAVELENGTH_LSB = 0x3d, - SFP_CABLE_SPEC = 0x3c, - SFP_CC_BASE = 0x3f, - SFP_OPTIONS = 0x40, /* 2 bytes, MSB, LSB */ - SFP_BR_MAX = 0x42, - SFP_BR_MIN = 0x43, - SFP_VENDOR_SN = 0x44, - SFP_DATECODE = 0x54, - SFP_DIAGMON = 0x5c, - SFP_ENHOPTS = 0x5d, - SFP_SFF8472_COMPLIANCE = 0x5e, - SFP_CC_EXT = 0x5f, + SFP_PHYS_ID = 0, + SFP_PHYS_EXT_ID = 1, + SFP_CONNECTOR = 2, + SFP_COMPLIANCE = 3, + SFP_ENCODING = 11, + SFP_BR_NOMINAL = 12, + SFP_RATE_ID = 13, + SFP_LINK_LEN_SM_KM = 14, + SFP_LINK_LEN_SM_100M = 15, + SFP_LINK_LEN_50UM_OM2_10M = 16, + SFP_LINK_LEN_62_5UM_OM1_10M = 17, + SFP_LINK_LEN_COPPER_1M = 18, + SFP_LINK_LEN_50UM_OM4_10M = 18, + SFP_LINK_LEN_50UM_OM3_10M = 19, + SFP_VENDOR_NAME = 20, + SFP_VENDOR_OUI = 37, + SFP_VENDOR_PN = 40, + SFP_VENDOR_REV = 56, + SFP_OPTICAL_WAVELENGTH_MSB = 60, + SFP_OPTICAL_WAVELENGTH_LSB = 61, + SFP_CABLE_SPEC = 60, + SFP_CC_BASE = 63, + SFP_OPTIONS = 64, /* 2 bytes, MSB, LSB */ + SFP_BR_MAX = 66, + SFP_BR_MIN = 67, + SFP_VENDOR_SN = 68, + SFP_DATECODE = 84, + SFP_DIAGMON = 92, + SFP_ENHOPTS = 93, + SFP_SFF8472_COMPLIANCE = 94, + SFP_CC_EXT = 95, SFP_PHYS_EXT_ID_SFP = 0x04, SFP_OPTIONS_HIGH_POWER_LEVEL = BIT(13), @@ -404,63 +404,63 @@ enum { /* SFP Diagnostics */ enum { /* Alarm and warnings stored MSB at lower address then LSB */ - SFP_TEMP_HIGH_ALARM = 0x00, - SFP_TEMP_LOW_ALARM = 0x02, - SFP_TEMP_HIGH_WARN = 0x04, - SFP_TEMP_LOW_WARN = 0x06, - SFP_VOLT_HIGH_ALARM = 0x08, - SFP_VOLT_LOW_ALARM = 0x0a, - SFP_VOLT_HIGH_WARN = 0x0c, - SFP_VOLT_LOW_WARN = 0x0e, - SFP_BIAS_HIGH_ALARM = 0x10, - SFP_BIAS_LOW_ALARM = 0x12, - SFP_BIAS_HIGH_WARN = 0x14, - SFP_BIAS_LOW_WARN = 0x16, - SFP_TXPWR_HIGH_ALARM = 0x18, - SFP_TXPWR_LOW_ALARM = 0x1a, - SFP_TXPWR_HIGH_WARN = 0x1c, - SFP_TXPWR_LOW_WARN = 0x1e, - SFP_RXPWR_HIGH_ALARM = 0x20, - SFP_RXPWR_LOW_ALARM = 0x22, - SFP_RXPWR_HIGH_WARN = 0x24, - SFP_RXPWR_LOW_WARN = 0x26, - SFP_LASER_TEMP_HIGH_ALARM = 0x28, - SFP_LASER_TEMP_LOW_ALARM = 0x2a, - SFP_LASER_TEMP_HIGH_WARN = 0x2c, - SFP_LASER_TEMP_LOW_WARN = 0x2e, - SFP_TEC_CUR_HIGH_ALARM = 0x30, - SFP_TEC_CUR_LOW_ALARM = 0x32, - SFP_TEC_CUR_HIGH_WARN = 0x34, - SFP_TEC_CUR_LOW_WARN = 0x36, - SFP_CAL_RXPWR4 = 0x38, - SFP_CAL_RXPWR3 = 0x3c, - SFP_CAL_RXPWR2 = 0x40, - SFP_CAL_RXPWR1 = 0x44, - SFP_CAL_RXPWR0 = 0x48, - SFP_CAL_TXI_SLOPE = 0x4c, - SFP_CAL_TXI_OFFSET = 0x4e, - SFP_CAL_TXPWR_SLOPE = 0x50, - SFP_CAL_TXPWR_OFFSET = 0x52, - SFP_CAL_T_SLOPE = 0x54, - SFP_CAL_T_OFFSET = 0x56, - SFP_CAL_V_SLOPE = 0x58, - SFP_CAL_V_OFFSET = 0x5a, - SFP_CHKSUM = 0x5f, + SFP_TEMP_HIGH_ALARM = 0, + SFP_TEMP_LOW_ALARM = 2, + SFP_TEMP_HIGH_WARN = 4, + SFP_TEMP_LOW_WARN = 6, + SFP_VOLT_HIGH_ALARM = 8, + SFP_VOLT_LOW_ALARM = 10, + SFP_VOLT_HIGH_WARN = 12, + SFP_VOLT_LOW_WARN = 14, + SFP_BIAS_HIGH_ALARM = 16, + SFP_BIAS_LOW_ALARM = 18, + SFP_BIAS_HIGH_WARN = 20, + SFP_BIAS_LOW_WARN = 22, + SFP_TXPWR_HIGH_ALARM = 24, + SFP_TXPWR_LOW_ALARM = 26, + SFP_TXPWR_HIGH_WARN = 28, + SFP_TXPWR_LOW_WARN = 30, + SFP_RXPWR_HIGH_ALARM = 32, + SFP_RXPWR_LOW_ALARM = 34, + SFP_RXPWR_HIGH_WARN = 36, + SFP_RXPWR_LOW_WARN = 38, + SFP_LASER_TEMP_HIGH_ALARM = 40, + SFP_LASER_TEMP_LOW_ALARM = 42, + SFP_LASER_TEMP_HIGH_WARN = 44, + SFP_LASER_TEMP_LOW_WARN = 46, + SFP_TEC_CUR_HIGH_ALARM = 48, + SFP_TEC_CUR_LOW_ALARM = 50, + SFP_TEC_CUR_HIGH_WARN = 52, + SFP_TEC_CUR_LOW_WARN = 54, + SFP_CAL_RXPWR4 = 56, + SFP_CAL_RXPWR3 = 60, + SFP_CAL_RXPWR2 = 64, + SFP_CAL_RXPWR1 = 68, + SFP_CAL_RXPWR0 = 72, + SFP_CAL_TXI_SLOPE = 76, + SFP_CAL_TXI_OFFSET = 78, + SFP_CAL_TXPWR_SLOPE = 80, + SFP_CAL_TXPWR_OFFSET = 82, + SFP_CAL_T_SLOPE = 84, + SFP_CAL_T_OFFSET = 86, + SFP_CAL_V_SLOPE = 88, + SFP_CAL_V_OFFSET = 90, + SFP_CHKSUM = 95, - SFP_TEMP = 0x60, - SFP_VCC = 0x62, - SFP_TX_BIAS = 0x64, - SFP_TX_POWER = 0x66, - SFP_RX_POWER = 0x68, - SFP_LASER_TEMP = 0x6a, - SFP_TEC_CUR = 0x6c, + SFP_TEMP = 96, + SFP_VCC = 98, + SFP_TX_BIAS = 100, + SFP_TX_POWER = 102, + SFP_RX_POWER = 104, + SFP_LASER_TEMP = 106, + SFP_TEC_CUR = 108, - SFP_STATUS = 0x6e, + SFP_STATUS = 110, SFP_STATUS_TX_DISABLE = BIT(7), SFP_STATUS_TX_DISABLE_FORCE = BIT(6), SFP_STATUS_TX_FAULT = BIT(2), SFP_STATUS_RX_LOS = BIT(1), - SFP_ALARM0 = 0x70, + SFP_ALARM0 = 112, SFP_ALARM0_TEMP_HIGH = BIT(7), SFP_ALARM0_TEMP_LOW = BIT(6), SFP_ALARM0_VCC_HIGH = BIT(5), @@ -470,11 +470,11 @@ enum { SFP_ALARM0_TXPWR_HIGH = BIT(1), SFP_ALARM0_TXPWR_LOW = BIT(0), - SFP_ALARM1 = 0x71, + SFP_ALARM1 = 113, SFP_ALARM1_RXPWR_HIGH = BIT(7), SFP_ALARM1_RXPWR_LOW = BIT(6), - SFP_WARN0 = 0x74, + SFP_WARN0 = 116, SFP_WARN0_TEMP_HIGH = BIT(7), SFP_WARN0_TEMP_LOW = BIT(6), SFP_WARN0_VCC_HIGH = BIT(5), @@ -484,15 +484,15 @@ enum { SFP_WARN0_TXPWR_HIGH = BIT(1), SFP_WARN0_TXPWR_LOW = BIT(0), - SFP_WARN1 = 0x75, + SFP_WARN1 = 117, SFP_WARN1_RXPWR_HIGH = BIT(7), SFP_WARN1_RXPWR_LOW = BIT(6), - SFP_EXT_STATUS = 0x76, + SFP_EXT_STATUS = 118, SFP_EXT_STATUS_PWRLVL_SELECT = BIT(0), - SFP_VSL = 0x78, - SFP_PAGE = 0x7f, + SFP_VSL = 120, + SFP_PAGE = 127, }; struct fwnode_handle; From d83845d224a059165902ecd0e1203015c5713a78 Mon Sep 17 00:00:00 2001 From: "Russell King (Oracle)" Date: Thu, 27 Oct 2022 14:21:21 +0100 Subject: [PATCH 0459/2002] net: sfp: move field definitions along side register index Just as we do for the A2h enum, arrange the A0h enum to have the field definitions next to their corresponding register index. Signed-off-by: Russell King (Oracle) Reviewed-by: Andrew Lunn Signed-off-by: Jakub Kicinski --- include/linux/sfp.h | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/include/linux/sfp.h b/include/linux/sfp.h index 4e2db155664d..52b98f9666a2 100644 --- a/include/linux/sfp.h +++ b/include/linux/sfp.h @@ -333,7 +333,10 @@ enum { /* SFP EEPROM registers */ enum { SFP_PHYS_ID = 0, + SFP_PHYS_EXT_ID = 1, + SFP_PHYS_EXT_ID_SFP = 0x04, + SFP_CONNECTOR = 2, SFP_COMPLIANCE = 3, SFP_ENCODING = 11, @@ -354,17 +357,8 @@ enum { SFP_OPTICAL_WAVELENGTH_LSB = 61, SFP_CABLE_SPEC = 60, SFP_CC_BASE = 63, - SFP_OPTIONS = 64, /* 2 bytes, MSB, LSB */ - SFP_BR_MAX = 66, - SFP_BR_MIN = 67, - SFP_VENDOR_SN = 68, - SFP_DATECODE = 84, - SFP_DIAGMON = 92, - SFP_ENHOPTS = 93, - SFP_SFF8472_COMPLIANCE = 94, - SFP_CC_EXT = 95, - SFP_PHYS_EXT_ID_SFP = 0x04, + SFP_OPTIONS = 64, /* 2 bytes, MSB, LSB */ SFP_OPTIONS_HIGH_POWER_LEVEL = BIT(13), SFP_OPTIONS_PAGING_A2 = BIT(12), SFP_OPTIONS_RETIMER = BIT(11), @@ -378,11 +372,20 @@ enum { SFP_OPTIONS_TX_FAULT = BIT(3), SFP_OPTIONS_LOS_INVERTED = BIT(2), SFP_OPTIONS_LOS_NORMAL = BIT(1), + + SFP_BR_MAX = 66, + SFP_BR_MIN = 67, + SFP_VENDOR_SN = 68, + SFP_DATECODE = 84, + + SFP_DIAGMON = 92, SFP_DIAGMON_DDM = BIT(6), SFP_DIAGMON_INT_CAL = BIT(5), SFP_DIAGMON_EXT_CAL = BIT(4), SFP_DIAGMON_RXPWR_AVG = BIT(3), SFP_DIAGMON_ADDRMODE = BIT(2), + + SFP_ENHOPTS = 93, SFP_ENHOPTS_ALARMWARN = BIT(7), SFP_ENHOPTS_SOFT_TX_DISABLE = BIT(6), SFP_ENHOPTS_SOFT_TX_FAULT = BIT(5), @@ -390,6 +393,8 @@ enum { SFP_ENHOPTS_SOFT_RATE_SELECT = BIT(3), SFP_ENHOPTS_APP_SELECT_SFF8079 = BIT(2), SFP_ENHOPTS_SOFT_RATE_SFF8431 = BIT(1), + + SFP_SFF8472_COMPLIANCE = 94, SFP_SFF8472_COMPLIANCE_NONE = 0x00, SFP_SFF8472_COMPLIANCE_REV9_3 = 0x01, SFP_SFF8472_COMPLIANCE_REV9_5 = 0x02, @@ -399,6 +404,8 @@ enum { SFP_SFF8472_COMPLIANCE_REV11_3 = 0x06, SFP_SFF8472_COMPLIANCE_REV11_4 = 0x07, SFP_SFF8472_COMPLIANCE_REV12_0 = 0x08, + + SFP_CC_EXT = 95, }; /* SFP Diagnostics */ From 637639cbfebb747406b9a57befc0b347057a3a24 Mon Sep 17 00:00:00 2001 From: Lukasz Czapnik Date: Thu, 27 Oct 2022 03:42:39 -0700 Subject: [PATCH 0460/2002] ice: Add additional CSR registers to ETHTOOL_GREGS In the event of a Tx hang it can be useful to read a variety of hardware registers to capture some state about why the transmit queue got stuck. Extend the ETHTOOL_GREGS dump provided by the ice driver with several CSR registers that provide such relevant information regarding the hardware Tx state. This enables capturing relevant data to enable debugging such a Tx hang. Signed-off-by: Lukasz Czapnik Signed-off-by: Mateusz Palczewski Tested-by: Gurucharan (A Contingent worker at Intel) Link: https://lore.kernel.org/r/20221027104239.1691549-1-jacob.e.keller@intel.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/intel/ice/ice_ethtool.c | 169 +++++++++++++++++++ 1 file changed, 169 insertions(+) diff --git a/drivers/net/ethernet/intel/ice/ice_ethtool.c b/drivers/net/ethernet/intel/ice/ice_ethtool.c index b7be84bbe72d..f71a7521c7bd 100644 --- a/drivers/net/ethernet/intel/ice/ice_ethtool.c +++ b/drivers/net/ethernet/intel/ice/ice_ethtool.c @@ -151,6 +151,175 @@ static const u32 ice_regs_dump_list[] = { QINT_RQCTL(0), PFINT_OICR_ENA, QRX_ITR(0), +#define GLDCB_TLPM_PCI_DM 0x000A0180 + GLDCB_TLPM_PCI_DM, +#define GLDCB_TLPM_TC2PFC 0x000A0194 + GLDCB_TLPM_TC2PFC, +#define TCDCB_TLPM_WAIT_DM(_i) (0x000A0080 + ((_i) * 4)) + TCDCB_TLPM_WAIT_DM(0), + TCDCB_TLPM_WAIT_DM(1), + TCDCB_TLPM_WAIT_DM(2), + TCDCB_TLPM_WAIT_DM(3), + TCDCB_TLPM_WAIT_DM(4), + TCDCB_TLPM_WAIT_DM(5), + TCDCB_TLPM_WAIT_DM(6), + TCDCB_TLPM_WAIT_DM(7), + TCDCB_TLPM_WAIT_DM(8), + TCDCB_TLPM_WAIT_DM(9), + TCDCB_TLPM_WAIT_DM(10), + TCDCB_TLPM_WAIT_DM(11), + TCDCB_TLPM_WAIT_DM(12), + TCDCB_TLPM_WAIT_DM(13), + TCDCB_TLPM_WAIT_DM(14), + TCDCB_TLPM_WAIT_DM(15), + TCDCB_TLPM_WAIT_DM(16), + TCDCB_TLPM_WAIT_DM(17), + TCDCB_TLPM_WAIT_DM(18), + TCDCB_TLPM_WAIT_DM(19), + TCDCB_TLPM_WAIT_DM(20), + TCDCB_TLPM_WAIT_DM(21), + TCDCB_TLPM_WAIT_DM(22), + TCDCB_TLPM_WAIT_DM(23), + TCDCB_TLPM_WAIT_DM(24), + TCDCB_TLPM_WAIT_DM(25), + TCDCB_TLPM_WAIT_DM(26), + TCDCB_TLPM_WAIT_DM(27), + TCDCB_TLPM_WAIT_DM(28), + TCDCB_TLPM_WAIT_DM(29), + TCDCB_TLPM_WAIT_DM(30), + TCDCB_TLPM_WAIT_DM(31), +#define GLPCI_WATMK_CLNT_PIPEMON 0x000BFD90 + GLPCI_WATMK_CLNT_PIPEMON, +#define GLPCI_CUR_CLNT_COMMON 0x000BFD84 + GLPCI_CUR_CLNT_COMMON, +#define GLPCI_CUR_CLNT_PIPEMON 0x000BFD88 + GLPCI_CUR_CLNT_PIPEMON, +#define GLPCI_PCIERR 0x0009DEB0 + GLPCI_PCIERR, +#define GLPSM_DEBUG_CTL_STATUS 0x000B0600 + GLPSM_DEBUG_CTL_STATUS, +#define GLPSM0_DEBUG_FIFO_OVERFLOW_DETECT 0x000B0680 + GLPSM0_DEBUG_FIFO_OVERFLOW_DETECT, +#define GLPSM0_DEBUG_FIFO_UNDERFLOW_DETECT 0x000B0684 + GLPSM0_DEBUG_FIFO_UNDERFLOW_DETECT, +#define GLPSM0_DEBUG_DT_OUT_OF_WINDOW 0x000B0688 + GLPSM0_DEBUG_DT_OUT_OF_WINDOW, +#define GLPSM0_DEBUG_INTF_HW_ERROR_DETECT 0x000B069C + GLPSM0_DEBUG_INTF_HW_ERROR_DETECT, +#define GLPSM0_DEBUG_MISC_HW_ERROR_DETECT 0x000B06A0 + GLPSM0_DEBUG_MISC_HW_ERROR_DETECT, +#define GLPSM1_DEBUG_FIFO_OVERFLOW_DETECT 0x000B0E80 + GLPSM1_DEBUG_FIFO_OVERFLOW_DETECT, +#define GLPSM1_DEBUG_FIFO_UNDERFLOW_DETECT 0x000B0E84 + GLPSM1_DEBUG_FIFO_UNDERFLOW_DETECT, +#define GLPSM1_DEBUG_SRL_FIFO_OVERFLOW_DETECT 0x000B0E88 + GLPSM1_DEBUG_SRL_FIFO_OVERFLOW_DETECT, +#define GLPSM1_DEBUG_SRL_FIFO_UNDERFLOW_DETECT 0x000B0E8C + GLPSM1_DEBUG_SRL_FIFO_UNDERFLOW_DETECT, +#define GLPSM1_DEBUG_MISC_HW_ERROR_DETECT 0x000B0E90 + GLPSM1_DEBUG_MISC_HW_ERROR_DETECT, +#define GLPSM2_DEBUG_FIFO_OVERFLOW_DETECT 0x000B1680 + GLPSM2_DEBUG_FIFO_OVERFLOW_DETECT, +#define GLPSM2_DEBUG_FIFO_UNDERFLOW_DETECT 0x000B1684 + GLPSM2_DEBUG_FIFO_UNDERFLOW_DETECT, +#define GLPSM2_DEBUG_MISC_HW_ERROR_DETECT 0x000B1688 + GLPSM2_DEBUG_MISC_HW_ERROR_DETECT, +#define GLTDPU_TCLAN_COMP_BOB(_i) (0x00049ADC + ((_i) * 4)) + GLTDPU_TCLAN_COMP_BOB(1), + GLTDPU_TCLAN_COMP_BOB(2), + GLTDPU_TCLAN_COMP_BOB(3), + GLTDPU_TCLAN_COMP_BOB(4), + GLTDPU_TCLAN_COMP_BOB(5), + GLTDPU_TCLAN_COMP_BOB(6), + GLTDPU_TCLAN_COMP_BOB(7), + GLTDPU_TCLAN_COMP_BOB(8), +#define GLTDPU_TCB_CMD_BOB(_i) (0x0004975C + ((_i) * 4)) + GLTDPU_TCB_CMD_BOB(1), + GLTDPU_TCB_CMD_BOB(2), + GLTDPU_TCB_CMD_BOB(3), + GLTDPU_TCB_CMD_BOB(4), + GLTDPU_TCB_CMD_BOB(5), + GLTDPU_TCB_CMD_BOB(6), + GLTDPU_TCB_CMD_BOB(7), + GLTDPU_TCB_CMD_BOB(8), +#define GLTDPU_PSM_UPDATE_BOB(_i) (0x00049B5C + ((_i) * 4)) + GLTDPU_PSM_UPDATE_BOB(1), + GLTDPU_PSM_UPDATE_BOB(2), + GLTDPU_PSM_UPDATE_BOB(3), + GLTDPU_PSM_UPDATE_BOB(4), + GLTDPU_PSM_UPDATE_BOB(5), + GLTDPU_PSM_UPDATE_BOB(6), + GLTDPU_PSM_UPDATE_BOB(7), + GLTDPU_PSM_UPDATE_BOB(8), +#define GLTCB_CMD_IN_BOB(_i) (0x000AE288 + ((_i) * 4)) + GLTCB_CMD_IN_BOB(1), + GLTCB_CMD_IN_BOB(2), + GLTCB_CMD_IN_BOB(3), + GLTCB_CMD_IN_BOB(4), + GLTCB_CMD_IN_BOB(5), + GLTCB_CMD_IN_BOB(6), + GLTCB_CMD_IN_BOB(7), + GLTCB_CMD_IN_BOB(8), +#define GLLAN_TCLAN_FETCH_CTL_FBK_BOB_CTL(_i) (0x000FC148 + ((_i) * 4)) + GLLAN_TCLAN_FETCH_CTL_FBK_BOB_CTL(1), + GLLAN_TCLAN_FETCH_CTL_FBK_BOB_CTL(2), + GLLAN_TCLAN_FETCH_CTL_FBK_BOB_CTL(3), + GLLAN_TCLAN_FETCH_CTL_FBK_BOB_CTL(4), + GLLAN_TCLAN_FETCH_CTL_FBK_BOB_CTL(5), + GLLAN_TCLAN_FETCH_CTL_FBK_BOB_CTL(6), + GLLAN_TCLAN_FETCH_CTL_FBK_BOB_CTL(7), + GLLAN_TCLAN_FETCH_CTL_FBK_BOB_CTL(8), +#define GLLAN_TCLAN_FETCH_CTL_SCHED_BOB_CTL(_i) (0x000FC248 + ((_i) * 4)) + GLLAN_TCLAN_FETCH_CTL_SCHED_BOB_CTL(1), + GLLAN_TCLAN_FETCH_CTL_SCHED_BOB_CTL(2), + GLLAN_TCLAN_FETCH_CTL_SCHED_BOB_CTL(3), + GLLAN_TCLAN_FETCH_CTL_SCHED_BOB_CTL(4), + GLLAN_TCLAN_FETCH_CTL_SCHED_BOB_CTL(5), + GLLAN_TCLAN_FETCH_CTL_SCHED_BOB_CTL(6), + GLLAN_TCLAN_FETCH_CTL_SCHED_BOB_CTL(7), + GLLAN_TCLAN_FETCH_CTL_SCHED_BOB_CTL(8), +#define GLLAN_TCLAN_CACHE_CTL_BOB_CTL(_i) (0x000FC1C8 + ((_i) * 4)) + GLLAN_TCLAN_CACHE_CTL_BOB_CTL(1), + GLLAN_TCLAN_CACHE_CTL_BOB_CTL(2), + GLLAN_TCLAN_CACHE_CTL_BOB_CTL(3), + GLLAN_TCLAN_CACHE_CTL_BOB_CTL(4), + GLLAN_TCLAN_CACHE_CTL_BOB_CTL(5), + GLLAN_TCLAN_CACHE_CTL_BOB_CTL(6), + GLLAN_TCLAN_CACHE_CTL_BOB_CTL(7), + GLLAN_TCLAN_CACHE_CTL_BOB_CTL(8), +#define GLLAN_TCLAN_FETCH_CTL_PROC_BOB_CTL(_i) (0x000FC188 + ((_i) * 4)) + GLLAN_TCLAN_FETCH_CTL_PROC_BOB_CTL(1), + GLLAN_TCLAN_FETCH_CTL_PROC_BOB_CTL(2), + GLLAN_TCLAN_FETCH_CTL_PROC_BOB_CTL(3), + GLLAN_TCLAN_FETCH_CTL_PROC_BOB_CTL(4), + GLLAN_TCLAN_FETCH_CTL_PROC_BOB_CTL(5), + GLLAN_TCLAN_FETCH_CTL_PROC_BOB_CTL(6), + GLLAN_TCLAN_FETCH_CTL_PROC_BOB_CTL(7), + GLLAN_TCLAN_FETCH_CTL_PROC_BOB_CTL(8), +#define GLLAN_TCLAN_FETCH_CTL_PCIE_RD_BOB_CTL(_i) (0x000FC288 + ((_i) * 4)) + GLLAN_TCLAN_FETCH_CTL_PCIE_RD_BOB_CTL(1), + GLLAN_TCLAN_FETCH_CTL_PCIE_RD_BOB_CTL(2), + GLLAN_TCLAN_FETCH_CTL_PCIE_RD_BOB_CTL(3), + GLLAN_TCLAN_FETCH_CTL_PCIE_RD_BOB_CTL(4), + GLLAN_TCLAN_FETCH_CTL_PCIE_RD_BOB_CTL(5), + GLLAN_TCLAN_FETCH_CTL_PCIE_RD_BOB_CTL(6), + GLLAN_TCLAN_FETCH_CTL_PCIE_RD_BOB_CTL(7), + GLLAN_TCLAN_FETCH_CTL_PCIE_RD_BOB_CTL(8), +#define PRTDCB_TCUPM_REG_CM(_i) (0x000BC360 + ((_i) * 4)) + PRTDCB_TCUPM_REG_CM(0), + PRTDCB_TCUPM_REG_CM(1), + PRTDCB_TCUPM_REG_CM(2), + PRTDCB_TCUPM_REG_CM(3), +#define PRTDCB_TCUPM_REG_DM(_i) (0x000BC3A0 + ((_i) * 4)) + PRTDCB_TCUPM_REG_DM(0), + PRTDCB_TCUPM_REG_DM(1), + PRTDCB_TCUPM_REG_DM(2), + PRTDCB_TCUPM_REG_DM(3), +#define PRTDCB_TLPM_REG_DM(_i) (0x000A0000 + ((_i) * 4)) + PRTDCB_TLPM_REG_DM(0), + PRTDCB_TLPM_REG_DM(1), + PRTDCB_TLPM_REG_DM(2), + PRTDCB_TLPM_REG_DM(3), }; struct ice_priv_flag { From 58ba426388d9fe56aa638f555b01d6e63cada88c Mon Sep 17 00:00:00 2001 From: Willem de Bruijn Date: Thu, 27 Oct 2022 17:10:14 -0400 Subject: [PATCH 0461/2002] net/packet: add PACKET_FANOUT_FLAG_IGNORE_OUTGOING Extend packet socket option PACKET_IGNORE_OUTGOING to fanout groups. The socket option sets ptype.ignore_outgoing, which makes dev_queue_xmit_nit skip the socket. When the socket joins a fanout group, the option is not reflected in the struct ptype of the group. dev_queue_xmit_nit only tests the fanout ptype, so the flag is ignored once a socket joins a fanout group. Inheriting the option from a socket would change established behavior. Different sockets in the group can set different flags, and can also change them at runtime. Testing in packet_rcv_fanout defeats the purpose of the original patch, which is to avoid skb_clone in dev_queue_xmit_nit (esp. for MSG_ZEROCOPY packets). Instead, introduce a new fanout group flag with the same behavior. Tested with https://github.com/wdebruij/kerneltools/blob/master/tests/test_psock_fanout_ignore_outgoing.c Signed-off-by: Willem de Bruijn Reviewed-by: Eric Dumazet Link: https://lore.kernel.org/r/20221027211014.3581513-1-willemdebruijn.kernel@gmail.com Signed-off-by: Jakub Kicinski --- include/uapi/linux/if_packet.h | 1 + net/packet/af_packet.c | 1 + 2 files changed, 2 insertions(+) diff --git a/include/uapi/linux/if_packet.h b/include/uapi/linux/if_packet.h index c07caf7b40db..a8516b3594a4 100644 --- a/include/uapi/linux/if_packet.h +++ b/include/uapi/linux/if_packet.h @@ -70,6 +70,7 @@ struct sockaddr_ll { #define PACKET_FANOUT_EBPF 7 #define PACKET_FANOUT_FLAG_ROLLOVER 0x1000 #define PACKET_FANOUT_FLAG_UNIQUEID 0x2000 +#define PACKET_FANOUT_FLAG_IGNORE_OUTGOING 0x4000 #define PACKET_FANOUT_FLAG_DEFRAG 0x8000 struct tpacket_stats { diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c index 8c5b3da0c29f..44f20cf8a0c0 100644 --- a/net/packet/af_packet.c +++ b/net/packet/af_packet.c @@ -1777,6 +1777,7 @@ static int fanout_add(struct sock *sk, struct fanout_args *args) match->prot_hook.af_packet_net = read_pnet(&match->net); match->prot_hook.id_match = match_fanout_group; match->max_num_members = args->max_num_members; + match->prot_hook.ignore_outgoing = type_flags & PACKET_FANOUT_FLAG_IGNORE_OUTGOING; list_add(&match->list, &fanout_list); } err = -EINVAL; From 5783c68a25199c47291e5dfa430264417072ebd8 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Thu, 27 Oct 2022 07:26:26 -0500 Subject: [PATCH 0462/2002] net: ipa: define IPA v5.0 In preparation for adding support for IPA v5.0, define it as an understood version. Signed-off-by: Alex Elder Signed-off-by: Jakub Kicinski --- drivers/net/ipa/ipa_version.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/ipa/ipa_version.h b/drivers/net/ipa/ipa_version.h index 7870e0cc3d7c..7889c310e943 100644 --- a/drivers/net/ipa/ipa_version.h +++ b/drivers/net/ipa/ipa_version.h @@ -19,6 +19,7 @@ * @IPA_VERSION_4_7: IPA version 4.7/GSI version 2.7 * @IPA_VERSION_4_9: IPA version 4.9/GSI version 2.9 * @IPA_VERSION_4_11: IPA version 4.11/GSI version 2.11 (2.1.1) + * @IPA_VERSION_5_0: IPA version 5.0/GSI version 3.0 * @IPA_VERSION_COUNT: Number of defined IPA versions * * Defines the version of IPA (and GSI) hardware present on the platform. @@ -36,6 +37,7 @@ enum ipa_version { IPA_VERSION_4_7, IPA_VERSION_4_9, IPA_VERSION_4_11, + IPA_VERSION_5_0, IPA_VERSION_COUNT, /* Last; not a version */ }; @@ -48,6 +50,7 @@ static inline bool ipa_version_supported(enum ipa_version version) case IPA_VERSION_4_5: case IPA_VERSION_4_9: case IPA_VERSION_4_11: + case IPA_VERSION_5_0: return true; default: return false; From 5ba5faa2e271bbb8c6e4c48c814f5277c8c014c6 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Thu, 27 Oct 2022 07:26:27 -0500 Subject: [PATCH 0463/2002] net: ipa: change an IPA v5.0 memory requirement Don't require IPA v5.0 to have a STATS_TETHERING memory region. Downstream defines its size to 0, so it apparently is unused. Signed-off-by: Alex Elder Signed-off-by: Jakub Kicinski --- drivers/net/ipa/ipa_mem.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/net/ipa/ipa_mem.c b/drivers/net/ipa/ipa_mem.c index cb9540201839..9ec5af323f73 100644 --- a/drivers/net/ipa/ipa_mem.c +++ b/drivers/net/ipa/ipa_mem.c @@ -198,9 +198,12 @@ static bool ipa_mem_id_required(struct ipa *ipa, enum ipa_mem_id mem_id) case IPA_MEM_PDN_CONFIG: case IPA_MEM_STATS_QUOTA_MODEM: - case IPA_MEM_STATS_TETHERING: return ipa->version >= IPA_VERSION_4_0; + case IPA_MEM_STATS_TETHERING: + return ipa->version >= IPA_VERSION_4_0 && + ipa->version != IPA_VERSION_5_0; + default: return false; /* Anything else is optional */ } From bd5524930ba7c9ed914952da48a77f803f0b5011 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Thu, 27 Oct 2022 07:26:28 -0500 Subject: [PATCH 0464/2002] net: ipa: no more global filtering starting with IPA v5.0 IPA v5.0 eliminates the global filter table entry. As a result, there is no need to shift the filtered endpoint bitmap when it is written to IPA local memory. Update comments to explain this. Also delete a redundant block of comments above the function. Signed-off-by: Alex Elder Signed-off-by: Jakub Kicinski --- drivers/net/ipa/ipa_table.c | 59 ++++++++++++++++++++++--------------- 1 file changed, 35 insertions(+), 24 deletions(-) diff --git a/drivers/net/ipa/ipa_table.c b/drivers/net/ipa/ipa_table.c index db1992eaafaa..cf3a3de239dc 100644 --- a/drivers/net/ipa/ipa_table.c +++ b/drivers/net/ipa/ipa_table.c @@ -32,8 +32,8 @@ * endian 64-bit "slot" that holds the address of a rule definition. (The * size of these slots is 64 bits regardless of the host DMA address size.) * - * Separate tables (both filter and route) used for IPv4 and IPv6. There - * are normally another set of "hashed" filter and route tables, which are + * Separate tables (both filter and route) are used for IPv4 and IPv6. There + * is normally another set of "hashed" filter and route tables, which are * used with a hash of message metadata. Hashed operation is not supported * by all IPA hardware (IPA v4.2 doesn't support hashed tables). * @@ -51,19 +51,32 @@ * Each filter rule is associated with an AP or modem TX endpoint, though * not all TX endpoints support filtering. The first 64-bit slot in a * filter table is a bitmap indicating which endpoints have entries in - * the table. The low-order bit (bit 0) in this bitmap represents a - * special global filter, which applies to all traffic. This is not - * used in the current code. Bit 1, if set, indicates that there is an - * entry (i.e. slot containing a system address referring to a rule) for - * endpoint 0 in the table. Bit 3, if set, indicates there is an entry - * for endpoint 2, and so on. Space is set aside in IPA local memory to - * hold as many filter table entries as might be required, but typically - * they are not all used. + * the table. Each set bit in this bitmap indicates the presence of the + * address of a filter rule in the memory following the bitmap. Until IPA + * v5.0, the low-order bit (bit 0) in this bitmap represents a special + * global filter, which applies to all traffic. Otherwise the position of + * each set bit represents an endpoint for which a filter rule is defined. + * + * The global rule is not used in current code, and support for it is + * removed starting at IPA v5.0. For IPA v5.0+, the endpoint bitmap + * position defines the endpoint ID--i.e. if bit 1 is set in the endpoint + * bitmap, endpoint 1 has a filter rule. Older versions of IPA represent + * the presence of a filter rule for endpoint X by bit (X + 1) being set. + * I.e., bit 1 set indicates the presence of a filter rule for endpoint 0, + * and bit 3 set means there is a filter rule present for endpoint 2. + * + * Each filter table entry has the address of a set of equations that + * implement a filter rule. So following the endpoint bitmap there + * will be such an address/entry for each endpoint with a set bit in + * the bitmap. * * The AP initializes all entries in a filter table to refer to a "zero" - * entry. Once initialized the modem and AP update the entries for - * endpoints they "own" directly. Currently the AP does not use the - * IPA filtering functionality. + * rule. Once initialized, the modem and AP update the entries for + * endpoints they "own" directly. Currently the AP does not use the IPA + * filtering functionality. + * + * This diagram shows an example of a filter table with an endpoint + * bitmap as defined prior to IPA v5.0. * * IPA Filter Table * ---------------------- @@ -658,12 +671,6 @@ bool ipa_table_mem_valid(struct ipa *ipa, bool filter) * when a route table is initialized or reset, its entries are made to refer * to the zero rule. The zero rule is shared for route and filter tables. * - * Note that the IPA hardware requires a filter or route rule address to be - * aligned on a 128 byte boundary. The coherent DMA buffer we allocate here - * has a minimum alignment, and we place the zero rule at the base of that - * allocated space. In ipa_table_init() we verify the minimum DMA allocation - * meets our requirement. - * * +-------------------+ * --> | zero rule | * / |-------------------| @@ -708,12 +715,16 @@ int ipa_table_init(struct ipa *ipa) /* First slot is the zero rule */ *virt++ = 0; - /* Next is the filter table bitmap. The "soft" bitmap value - * must be converted to the hardware representation by shifting - * it left one position. (Bit 0 repesents global filtering, - * which is possible but not used.) + /* Next is the filter table bitmap. The "soft" bitmap value might + * need to be converted to the hardware representation by shifting + * it left one position. Prior to IPA v5.0, bit 0 repesents global + * filtering, which is possible but not used. IPA v5.0+ eliminated + * that option, so there's no shifting required. */ - *virt++ = cpu_to_le64((u64)ipa->filter_map << 1); + if (ipa->version < IPA_VERSION_5_0) + *virt++ = cpu_to_le64((u64)ipa->filter_map << 1); + else + *virt++ = cpu_to_le64((u64)ipa->filter_map); /* All the rest contain the DMA address of the zero rule */ le_addr = cpu_to_le64(addr); From 2b87d721999a7591af7a63c1908084b1d59ba4e5 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Thu, 27 Oct 2022 07:26:29 -0500 Subject: [PATCH 0465/2002] net: ipa: more completely check endpoint validity Ensure all defined TX endpoints are in the range [0, CONS_PIPES) and defined RX endpoints are within [PROD_LOWEST, PROD_LOWEST+PROD_PIPES). Modify the way local variables are used to make the checks easier to understand. Check for each endpoint being in valid range in the loop, and drop the logical-AND check of initialized against unavailable IDs. Signed-off-by: Alex Elder Signed-off-by: Jakub Kicinski --- drivers/net/ipa/ipa_endpoint.c | 65 +++++++++++++++++++--------------- 1 file changed, 36 insertions(+), 29 deletions(-) diff --git a/drivers/net/ipa/ipa_endpoint.c b/drivers/net/ipa/ipa_endpoint.c index 093e11ec7c2d..6fc3cc6379fb 100644 --- a/drivers/net/ipa/ipa_endpoint.c +++ b/drivers/net/ipa/ipa_endpoint.c @@ -1843,14 +1843,13 @@ int ipa_endpoint_config(struct ipa *ipa) struct device *dev = &ipa->pdev->dev; const struct ipa_reg *reg; u32 initialized; + u32 tx_count; + u32 rx_count; u32 rx_base; - u32 rx_mask; - u32 tx_mask; - int ret = 0; - u32 max; + u32 limit; u32 val; - /* Prior to IPAv3.5, the FLAVOR_0 register was not supported. + /* Prior to IPA v3.5, the FLAVOR_0 register was not supported. * Furthermore, the endpoints were not grouped such that TX * endpoint numbers started with 0 and RX endpoints had numbers * higher than all TX endpoints, so we can't do the simple @@ -1866,33 +1865,25 @@ int ipa_endpoint_config(struct ipa *ipa) } /* Find out about the endpoints supplied by the hardware, and ensure - * the highest one doesn't exceed the number we support. + * the highest one doesn't exceed the number supported by software. */ reg = ipa_reg(ipa, FLAVOR_0); val = ioread32(ipa->reg_virt + ipa_reg_offset(reg)); - /* Our RX is an IPA producer */ + /* Our RX is an IPA producer; our TX is an IPA consumer. */ + tx_count = ipa_reg_decode(reg, MAX_CONS_PIPES, val); + rx_count = ipa_reg_decode(reg, MAX_PROD_PIPES, val); rx_base = ipa_reg_decode(reg, PROD_LOWEST, val); - max = rx_base + ipa_reg_decode(reg, MAX_PROD_PIPES, val); - if (max > IPA_ENDPOINT_MAX) { - dev_err(dev, "too many endpoints (%u > %u)\n", - max, IPA_ENDPOINT_MAX); + + limit = rx_base + rx_count; + if (limit > IPA_ENDPOINT_MAX) { + dev_err(dev, "too many endpoints, %u > %u\n", + limit, IPA_ENDPOINT_MAX); return -EINVAL; } - rx_mask = GENMASK(max - 1, rx_base); - /* Our TX is an IPA consumer */ - max = ipa_reg_decode(reg, MAX_CONS_PIPES, val); - tx_mask = GENMASK(max - 1, 0); - - ipa->available = rx_mask | tx_mask; - - /* Check for initialized endpoints not supported by the hardware */ - if (ipa->initialized & ~ipa->available) { - dev_err(dev, "unavailable endpoint id(s) 0x%08x\n", - ipa->initialized & ~ipa->available); - ret = -EINVAL; /* Report other errors too */ - } + /* Mark all supported RX and TX endpoints as available */ + ipa->available = GENMASK(limit - 1, rx_base) | GENMASK(tx_count - 1, 0); initialized = ipa->initialized; while (initialized) { @@ -1901,16 +1892,32 @@ int ipa_endpoint_config(struct ipa *ipa) initialized ^= BIT(endpoint_id); + if (endpoint_id >= limit) { + dev_err(dev, "invalid endpoint id, %u > %u\n", + endpoint_id, limit - 1); + return -EINVAL; + } + + if (!(BIT(endpoint_id) & ipa->available)) { + dev_err(dev, "unavailable endpoint id %u\n", + endpoint_id); + return -EINVAL; + } + /* Make sure it's pointing in the right direction */ endpoint = &ipa->endpoint[endpoint_id]; - if ((endpoint_id < rx_base) != endpoint->toward_ipa) { - dev_err(dev, "endpoint id %u wrong direction\n", - endpoint_id); - ret = -EINVAL; + if (endpoint->toward_ipa) { + if (endpoint_id < tx_count) + continue; + } else if (endpoint_id >= rx_base) { + continue; } + + dev_err(dev, "endpoint id %u wrong direction\n", endpoint_id); + return -EINVAL; } - return ret; + return 0; } void ipa_endpoint_deconfig(struct ipa *ipa) From e359ba89a4aae690fb51d19fc68f23f38435ae39 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Thu, 27 Oct 2022 07:26:30 -0500 Subject: [PATCH 0466/2002] net: ipa: refactor endpoint loops Change two functions that iterate over all endpoints to use while loops, using "endpoint_id" as the index variables in both spots. Signed-off-by: Alex Elder Signed-off-by: Jakub Kicinski --- drivers/net/ipa/ipa_endpoint.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/net/ipa/ipa_endpoint.c b/drivers/net/ipa/ipa_endpoint.c index 6fc3cc6379fb..740b2e4e0c50 100644 --- a/drivers/net/ipa/ipa_endpoint.c +++ b/drivers/net/ipa/ipa_endpoint.c @@ -426,10 +426,10 @@ ipa_endpoint_program_suspend(struct ipa_endpoint *endpoint, bool enable) */ void ipa_endpoint_modem_pause_all(struct ipa *ipa, bool enable) { - u32 endpoint_id; + u32 endpoint_id = 0; - for (endpoint_id = 0; endpoint_id < IPA_ENDPOINT_MAX; endpoint_id++) { - struct ipa_endpoint *endpoint = &ipa->endpoint[endpoint_id]; + while (endpoint_id < IPA_ENDPOINT_MAX) { + struct ipa_endpoint *endpoint = &ipa->endpoint[endpoint_id++]; if (endpoint->ee_id != GSI_EE_MODEM) continue; @@ -1008,10 +1008,10 @@ static void ipa_endpoint_init_hol_block_disable(struct ipa_endpoint *endpoint) void ipa_endpoint_modem_hol_block_clear_all(struct ipa *ipa) { - u32 i; + u32 endpoint_id = 0; - for (i = 0; i < IPA_ENDPOINT_MAX; i++) { - struct ipa_endpoint *endpoint = &ipa->endpoint[i]; + while (endpoint_id < IPA_ENDPOINT_MAX) { + struct ipa_endpoint *endpoint = &ipa->endpoint[endpoint_id++]; if (endpoint->toward_ipa || endpoint->ee_id != GSI_EE_MODEM) continue; From 5274c7158b2b6661f43883617ce638200312412b Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Thu, 27 Oct 2022 07:26:31 -0500 Subject: [PATCH 0467/2002] net: ipa: determine the maximum endpoint ID Each endpoint ID has an entry in the IPA endpoint array. But the size of that array is defined at compile time. Instead, rename ipa_endpoint_data_valid() to be ipa_endpoint_max() and have it return the maximum endpoint ID defined in configuration data. That function will still validate configuration data. Zero is returned on error; it's a valid endpoint ID, but we need more than one, so it can't be the maximum. The next patch makes use of the returned maximum value. Finally, rename the "initialized" mask of endpoints defined by configuration data to be "defined". Signed-off-by: Alex Elder Signed-off-by: Jakub Kicinski --- drivers/net/ipa/ipa.h | 6 +-- drivers/net/ipa/ipa_endpoint.c | 67 ++++++++++++++++++---------------- 2 files changed, 39 insertions(+), 34 deletions(-) diff --git a/drivers/net/ipa/ipa.h b/drivers/net/ipa/ipa.h index 82225316a2e2..e975f63271c9 100644 --- a/drivers/net/ipa/ipa.h +++ b/drivers/net/ipa/ipa.h @@ -61,9 +61,9 @@ struct ipa_interrupt; * @zero_addr: DMA address of preallocated zero-filled memory * @zero_virt: Virtual address of preallocated zero-filled memory * @zero_size: Size (bytes) of preallocated zero-filled memory + * @defined: Bit mask indicating endpoints defined in config data * @available: Bit mask indicating endpoints hardware supports * @filter_map: Bit mask indicating endpoints that support filtering - * @initialized: Bit mask indicating endpoints initialized * @set_up: Bit mask indicating endpoints set up * @enabled: Bit mask indicating endpoints enabled * @modem_tx_count: Number of defined modem TX endoints @@ -117,9 +117,9 @@ struct ipa { size_t zero_size; /* Bit masks indicating endpoint state */ - u32 available; /* supported by hardware */ + u32 defined; /* Defined in configuration data */ + u32 available; /* Supported by hardware */ u32 filter_map; - u32 initialized; u32 set_up; u32 enabled; diff --git a/drivers/net/ipa/ipa_endpoint.c b/drivers/net/ipa/ipa_endpoint.c index 740b2e4e0c50..9fd72ba149af 100644 --- a/drivers/net/ipa/ipa_endpoint.c +++ b/drivers/net/ipa/ipa_endpoint.c @@ -243,42 +243,47 @@ static bool ipa_endpoint_data_valid_one(struct ipa *ipa, u32 count, return true; } -static bool ipa_endpoint_data_valid(struct ipa *ipa, u32 count, - const struct ipa_gsi_endpoint_data *data) +/* Validate endpoint configuration data. Return max defined endpoint ID */ +static u32 ipa_endpoint_max(struct ipa *ipa, u32 count, + const struct ipa_gsi_endpoint_data *data) { const struct ipa_gsi_endpoint_data *dp = data; struct device *dev = &ipa->pdev->dev; enum ipa_endpoint_name name; + u32 max; if (count > IPA_ENDPOINT_COUNT) { dev_err(dev, "too many endpoints specified (%u > %u)\n", count, IPA_ENDPOINT_COUNT); - return false; + return 0; } /* Make sure needed endpoints have defined data */ if (ipa_gsi_endpoint_data_empty(&data[IPA_ENDPOINT_AP_COMMAND_TX])) { dev_err(dev, "command TX endpoint not defined\n"); - return false; + return 0; } if (ipa_gsi_endpoint_data_empty(&data[IPA_ENDPOINT_AP_LAN_RX])) { dev_err(dev, "LAN RX endpoint not defined\n"); - return false; + return 0; } if (ipa_gsi_endpoint_data_empty(&data[IPA_ENDPOINT_AP_MODEM_TX])) { dev_err(dev, "AP->modem TX endpoint not defined\n"); - return false; + return 0; } if (ipa_gsi_endpoint_data_empty(&data[IPA_ENDPOINT_AP_MODEM_RX])) { dev_err(dev, "AP<-modem RX endpoint not defined\n"); - return false; + return 0; } - for (name = 0; name < count; name++, dp++) + max = 0; + for (name = 0; name < count; name++, dp++) { if (!ipa_endpoint_data_valid_one(ipa, count, data, dp)) - return false; + return 0; + max = max_t(u32, max, dp->endpoint_id); + } - return true; + return max; } /* Allocate a transaction to use on a non-command endpoint */ @@ -448,7 +453,7 @@ void ipa_endpoint_modem_pause_all(struct ipa *ipa, bool enable) /* Reset all modem endpoints to use the default exception endpoint */ int ipa_endpoint_modem_exception_reset_all(struct ipa *ipa) { - u32 initialized = ipa->initialized; + u32 defined = ipa->defined; struct gsi_trans *trans; u32 count; @@ -463,13 +468,13 @@ int ipa_endpoint_modem_exception_reset_all(struct ipa *ipa) return -EBUSY; } - while (initialized) { - u32 endpoint_id = __ffs(initialized); + while (defined) { + u32 endpoint_id = __ffs(defined); struct ipa_endpoint *endpoint; const struct ipa_reg *reg; u32 offset; - initialized ^= BIT(endpoint_id); + defined ^= BIT(endpoint_id); /* We only reset modem TX endpoints */ endpoint = &ipa->endpoint[endpoint_id]; @@ -1812,13 +1817,13 @@ static void ipa_endpoint_teardown_one(struct ipa_endpoint *endpoint) void ipa_endpoint_setup(struct ipa *ipa) { - u32 initialized = ipa->initialized; + u32 defined = ipa->defined; ipa->set_up = 0; - while (initialized) { - u32 endpoint_id = __ffs(initialized); + while (defined) { + u32 endpoint_id = __ffs(defined); - initialized ^= BIT(endpoint_id); + defined ^= BIT(endpoint_id); ipa_endpoint_setup_one(&ipa->endpoint[endpoint_id]); } @@ -1842,10 +1847,10 @@ int ipa_endpoint_config(struct ipa *ipa) { struct device *dev = &ipa->pdev->dev; const struct ipa_reg *reg; - u32 initialized; u32 tx_count; u32 rx_count; u32 rx_base; + u32 defined; u32 limit; u32 val; @@ -1885,12 +1890,12 @@ int ipa_endpoint_config(struct ipa *ipa) /* Mark all supported RX and TX endpoints as available */ ipa->available = GENMASK(limit - 1, rx_base) | GENMASK(tx_count - 1, 0); - initialized = ipa->initialized; - while (initialized) { - u32 endpoint_id = __ffs(initialized); + defined = ipa->defined; + while (defined) { + u32 endpoint_id = __ffs(defined); struct ipa_endpoint *endpoint; - initialized ^= BIT(endpoint_id); + defined ^= BIT(endpoint_id); if (endpoint_id >= limit) { dev_err(dev, "invalid endpoint id, %u > %u\n", @@ -1943,24 +1948,24 @@ static void ipa_endpoint_init_one(struct ipa *ipa, enum ipa_endpoint_name name, endpoint->toward_ipa = data->toward_ipa; endpoint->config = data->endpoint.config; - ipa->initialized |= BIT(endpoint->endpoint_id); + ipa->defined |= BIT(endpoint->endpoint_id); } static void ipa_endpoint_exit_one(struct ipa_endpoint *endpoint) { - endpoint->ipa->initialized &= ~BIT(endpoint->endpoint_id); + endpoint->ipa->defined &= ~BIT(endpoint->endpoint_id); memset(endpoint, 0, sizeof(*endpoint)); } void ipa_endpoint_exit(struct ipa *ipa) { - u32 initialized = ipa->initialized; + u32 defined = ipa->defined; - while (initialized) { - u32 endpoint_id = __fls(initialized); + while (defined) { + u32 endpoint_id = __fls(defined); - initialized ^= BIT(endpoint_id); + defined ^= BIT(endpoint_id); ipa_endpoint_exit_one(&ipa->endpoint[endpoint_id]); } @@ -1977,10 +1982,10 @@ u32 ipa_endpoint_init(struct ipa *ipa, u32 count, BUILD_BUG_ON(!IPA_REPLENISH_BATCH); - if (!ipa_endpoint_data_valid(ipa, count, data)) + if (!ipa_endpoint_max(ipa, count, data)) return 0; /* Error */ - ipa->initialized = 0; + ipa->defined = 0; filter_map = 0; for (name = 0; name < count; name++, data++) { From b7aaff0b010ede619bdea22118d4a2f9aa966867 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Thu, 27 Oct 2022 07:26:32 -0500 Subject: [PATCH 0468/2002] net: ipa: record and use the number of defined endpoint IDs Define a new field in the IPA structure that records the maximum number of entries that will be used in the IPA endpoint array. Use that value rather than IPA_ENDPOINT_MAX to determine the end condition for two loops that iterate over all endpoints. Signed-off-by: Alex Elder Signed-off-by: Jakub Kicinski --- drivers/net/ipa/ipa.h | 2 ++ drivers/net/ipa/ipa_endpoint.c | 8 +++++--- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/drivers/net/ipa/ipa.h b/drivers/net/ipa/ipa.h index e975f63271c9..a44595575d06 100644 --- a/drivers/net/ipa/ipa.h +++ b/drivers/net/ipa/ipa.h @@ -61,6 +61,7 @@ struct ipa_interrupt; * @zero_addr: DMA address of preallocated zero-filled memory * @zero_virt: Virtual address of preallocated zero-filled memory * @zero_size: Size (bytes) of preallocated zero-filled memory + * @endpoint_count: Number of endpoints represented by bit masks below * @defined: Bit mask indicating endpoints defined in config data * @available: Bit mask indicating endpoints hardware supports * @filter_map: Bit mask indicating endpoints that support filtering @@ -117,6 +118,7 @@ struct ipa { size_t zero_size; /* Bit masks indicating endpoint state */ + u32 endpoint_count; u32 defined; /* Defined in configuration data */ u32 available; /* Supported by hardware */ u32 filter_map; diff --git a/drivers/net/ipa/ipa_endpoint.c b/drivers/net/ipa/ipa_endpoint.c index 9fd72ba149af..2a6184ea8f5c 100644 --- a/drivers/net/ipa/ipa_endpoint.c +++ b/drivers/net/ipa/ipa_endpoint.c @@ -433,7 +433,7 @@ void ipa_endpoint_modem_pause_all(struct ipa *ipa, bool enable) { u32 endpoint_id = 0; - while (endpoint_id < IPA_ENDPOINT_MAX) { + while (endpoint_id < ipa->endpoint_count) { struct ipa_endpoint *endpoint = &ipa->endpoint[endpoint_id++]; if (endpoint->ee_id != GSI_EE_MODEM) @@ -1015,7 +1015,7 @@ void ipa_endpoint_modem_hol_block_clear_all(struct ipa *ipa) { u32 endpoint_id = 0; - while (endpoint_id < IPA_ENDPOINT_MAX) { + while (endpoint_id < ipa->endpoint_count) { struct ipa_endpoint *endpoint = &ipa->endpoint[endpoint_id++]; if (endpoint->toward_ipa || endpoint->ee_id != GSI_EE_MODEM) @@ -1982,7 +1982,9 @@ u32 ipa_endpoint_init(struct ipa *ipa, u32 count, BUILD_BUG_ON(!IPA_REPLENISH_BATCH); - if (!ipa_endpoint_max(ipa, count, data)) + /* Number of endpoints is one more than the maximum ID */ + ipa->endpoint_count = ipa_endpoint_max(ipa, count, data) + 1; + if (!ipa->endpoint_count) return 0; /* Error */ ipa->defined = 0; From f3fb589aeb88c5bf7a7a804d36a8fa219b72e290 Mon Sep 17 00:00:00 2001 From: Juhee Kang Date: Fri, 28 Oct 2022 01:04:24 +0900 Subject: [PATCH 0469/2002] net: remove unused netdev_unregistering() Currently, use dev->reg_state == NETREG_UNREGISTERING to check the status which is NETREG_UNREGISTERING, rather than using netdev_unregistering. Also, A helper function which is netdev_unregistering on nedevice.h is no longer used. Thus, netdev_unregistering removes from netdevice.h. Signed-off-by: Juhee Kang Signed-off-by: David S. Miller --- include/linux/netdevice.h | 5 ----- 1 file changed, 5 deletions(-) diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index eddf8ee270e7..99e58b773266 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -5101,11 +5101,6 @@ static inline const char *netdev_name(const struct net_device *dev) return dev->name; } -static inline bool netdev_unregistering(const struct net_device *dev) -{ - return dev->reg_state == NETREG_UNREGISTERING; -} - static inline const char *netdev_reg_state(const struct net_device *dev) { switch (dev->reg_state) { From 8fc4deaa8bd2a8bef3f7fc15dced8da2f3e97286 Mon Sep 17 00:00:00 2001 From: Sebastian Reichel Date: Thu, 27 Oct 2022 18:31:19 +0200 Subject: [PATCH 0470/2002] dt-bindings: net: snps,dwmac: Document queue config subnodes The queue configuration is referenced by snps,mtl-rx-config and snps,mtl-tx-config. Some in-tree DTs and the example put the referenced config nodes directly beneath the root node, but most in-tree DTs put it as child node of the dwmac node. This adds proper description for this setup, which has the advantage of validating the queue configuration node content. The example is also updated to use the sub-node style, incl. the axi bus configuration node, which got the same treatment as the queues config in 5361660af6d3 ("dt-bindings: net: snps,dwmac: Document stmmac-axi-config subnode"). Signed-off-by: Sebastian Reichel Reviewed-by: Krzysztof Kozlowski Signed-off-by: David S. Miller --- .../devicetree/bindings/net/snps,dwmac.yaml | 345 ++++++++++++++---- 1 file changed, 264 insertions(+), 81 deletions(-) diff --git a/Documentation/devicetree/bindings/net/snps,dwmac.yaml b/Documentation/devicetree/bindings/net/snps,dwmac.yaml index 13b984076af5..e88a86623fce 100644 --- a/Documentation/devicetree/bindings/net/snps,dwmac.yaml +++ b/Documentation/devicetree/bindings/net/snps,dwmac.yaml @@ -167,56 +167,238 @@ properties: snps,mtl-rx-config: $ref: /schemas/types.yaml#/definitions/phandle description: - Multiple RX Queues parameters. Phandle to a node that can - contain the following properties - * snps,rx-queues-to-use, number of RX queues to be used in the - driver - * Choose one of these RX scheduling algorithms - * snps,rx-sched-sp, Strict priority - * snps,rx-sched-wsp, Weighted Strict priority - * For each RX queue - * Choose one of these modes - * snps,dcb-algorithm, Queue to be enabled as DCB - * snps,avb-algorithm, Queue to be enabled as AVB - * snps,map-to-dma-channel, Channel to map - * Specifiy specific packet routing - * snps,route-avcp, AV Untagged Control packets - * snps,route-ptp, PTP Packets - * snps,route-dcbcp, DCB Control Packets - * snps,route-up, Untagged Packets - * snps,route-multi-broad, Multicast & Broadcast Packets - * snps,priority, bitmask of the tagged frames priorities assigned to - the queue + Multiple RX Queues parameters. Phandle to a node that + implements the 'rx-queues-config' object described in + this binding. + + rx-queues-config: + type: object + properties: + snps,rx-queues-to-use: + $ref: /schemas/types.yaml#/definitions/uint32 + description: number of RX queues to be used in the driver + snps,rx-sched-sp: + type: boolean + description: Strict priority + snps,rx-sched-wsp: + type: boolean + description: Weighted Strict priority + allOf: + - if: + required: + - snps,rx-sched-sp + then: + properties: + snps,rx-sched-wsp: false + - if: + required: + - snps,rx-sched-wsp + then: + properties: + snps,rx-sched-sp: false + patternProperties: + "^queue[0-9]$": + description: Each subnode represents a queue. + type: object + properties: + snps,dcb-algorithm: + type: boolean + description: Queue to be enabled as DCB + snps,avb-algorithm: + type: boolean + description: Queue to be enabled as AVB + snps,map-to-dma-channel: + $ref: /schemas/types.yaml#/definitions/uint32 + description: DMA channel id to map + snps,route-avcp: + type: boolean + description: AV Untagged Control packets + snps,route-ptp: + type: boolean + description: PTP Packets + snps,route-dcbcp: + type: boolean + description: DCB Control Packets + snps,route-up: + type: boolean + description: Untagged Packets + snps,route-multi-broad: + type: boolean + description: Multicast & Broadcast Packets + snps,priority: + $ref: /schemas/types.yaml#/definitions/uint32 + description: Bitmask of the tagged frames priorities assigned to the queue + allOf: + - if: + required: + - snps,dcb-algorithm + then: + properties: + snps,avb-algorithm: false + - if: + required: + - snps,avb-algorithm + then: + properties: + snps,dcb-algorithm: false + - if: + required: + - snps,route-avcp + then: + properties: + snps,route-ptp: false + snps,route-dcbcp: false + snps,route-up: false + snps,route-multi-broad: false + - if: + required: + - snps,route-ptp + then: + properties: + snps,route-avcp: false + snps,route-dcbcp: false + snps,route-up: false + snps,route-multi-broad: false + - if: + required: + - snps,route-dcbcp + then: + properties: + snps,route-avcp: false + snps,route-ptp: false + snps,route-up: false + snps,route-multi-broad: false + - if: + required: + - snps,route-up + then: + properties: + snps,route-avcp: false + snps,route-ptp: false + snps,route-dcbcp: false + snps,route-multi-broad: false + - if: + required: + - snps,route-multi-broad + then: + properties: + snps,route-avcp: false + snps,route-ptp: false + snps,route-dcbcp: false + snps,route-up: false + additionalProperties: false + additionalProperties: false snps,mtl-tx-config: $ref: /schemas/types.yaml#/definitions/phandle description: - Multiple TX Queues parameters. Phandle to a node that can - contain the following properties - * snps,tx-queues-to-use, number of TX queues to be used in the - driver - * Choose one of these TX scheduling algorithms - * snps,tx-sched-wrr, Weighted Round Robin - * snps,tx-sched-wfq, Weighted Fair Queuing - * snps,tx-sched-dwrr, Deficit Weighted Round Robin - * snps,tx-sched-sp, Strict priority - * For each TX queue - * snps,weight, TX queue weight (if using a DCB weight - algorithm) - * Choose one of these modes - * snps,dcb-algorithm, TX queue will be working in DCB - * snps,avb-algorithm, TX queue will be working in AVB - [Attention] Queue 0 is reserved for legacy traffic - and so no AVB is available in this queue. - * Configure Credit Base Shaper (if AVB Mode selected) - * snps,send_slope, enable Low Power Interface - * snps,idle_slope, unlock on WoL - * snps,high_credit, max write outstanding req. limit - * snps,low_credit, max read outstanding req. limit - * snps,priority, bitmask of the priorities assigned to the queue. - When a PFC frame is received with priorities matching the bitmask, - the queue is blocked from transmitting for the pause time specified - in the PFC frame. + Multiple TX Queues parameters. Phandle to a node that + implements the 'tx-queues-config' object described in + this binding. + + tx-queues-config: + type: object + properties: + snps,tx-queues-to-use: + $ref: /schemas/types.yaml#/definitions/uint32 + description: number of TX queues to be used in the driver + snps,tx-sched-wrr: + type: boolean + description: Weighted Round Robin + snps,tx-sched-wfq: + type: boolean + description: Weighted Fair Queuing + snps,tx-sched-dwrr: + type: boolean + description: Deficit Weighted Round Robin + snps,tx-sched-sp: + type: boolean + description: Strict priority + allOf: + - if: + required: + - snps,tx-sched-wrr + then: + properties: + snps,tx-sched-wfq: false + snps,tx-sched-dwrr: false + snps,tx-sched-sp: false + - if: + required: + - snps,tx-sched-wfq + then: + properties: + snps,tx-sched-wrr: false + snps,tx-sched-dwrr: false + snps,tx-sched-sp: false + - if: + required: + - snps,tx-sched-dwrr + then: + properties: + snps,tx-sched-wrr: false + snps,tx-sched-wfq: false + snps,tx-sched-sp: false + - if: + required: + - snps,tx-sched-sp + then: + properties: + snps,tx-sched-wrr: false + snps,tx-sched-wfq: false + snps,tx-sched-dwrr: false + patternProperties: + "^queue[0-9]$": + description: Each subnode represents a queue. + type: object + properties: + snps,weight: + $ref: /schemas/types.yaml#/definitions/uint32 + description: TX queue weight (if using a DCB weight algorithm) + snps,dcb-algorithm: + type: boolean + description: TX queue will be working in DCB + snps,avb-algorithm: + type: boolean + description: + TX queue will be working in AVB. + Queue 0 is reserved for legacy traffic and so no AVB is + available in this queue. + snps,send_slope: + $ref: /schemas/types.yaml#/definitions/uint32 + description: enable Low Power Interface + snps,idle_slope: + $ref: /schemas/types.yaml#/definitions/uint32 + description: unlock on WoL + snps,high_credit: + $ref: /schemas/types.yaml#/definitions/uint32 + description: max write outstanding req. limit + snps,low_credit: + $ref: /schemas/types.yaml#/definitions/uint32 + description: max read outstanding req. limit + snps,priority: + $ref: /schemas/types.yaml#/definitions/uint32 + description: + Bitmask of the tagged frames priorities assigned to the queue. + When a PFC frame is received with priorities matching the bitmask, + the queue is blocked from transmitting for the pause time specified + in the PFC frame. + allOf: + - if: + required: + - snps,dcb-algorithm + then: + properties: + snps,avb-algorithm: false + - if: + required: + - snps,avb-algorithm + then: + properties: + snps,dcb-algorithm: false + snps,weight: false + additionalProperties: false + additionalProperties: false snps,reset-gpio: deprecated: true @@ -463,41 +645,6 @@ additionalProperties: true examples: - | - stmmac_axi_setup: stmmac-axi-config { - snps,wr_osr_lmt = <0xf>; - snps,rd_osr_lmt = <0xf>; - snps,blen = <256 128 64 32 0 0 0>; - }; - - mtl_rx_setup: rx-queues-config { - snps,rx-queues-to-use = <1>; - snps,rx-sched-sp; - queue0 { - snps,dcb-algorithm; - snps,map-to-dma-channel = <0x0>; - snps,priority = <0x0>; - }; - }; - - mtl_tx_setup: tx-queues-config { - snps,tx-queues-to-use = <2>; - snps,tx-sched-wrr; - queue0 { - snps,weight = <0x10>; - snps,dcb-algorithm; - snps,priority = <0x0>; - }; - - queue1 { - snps,avb-algorithm; - snps,send_slope = <0x1000>; - snps,idle_slope = <0x1000>; - snps,high_credit = <0x3E800>; - snps,low_credit = <0xFFC18000>; - snps,priority = <0x1>; - }; - }; - gmac0: ethernet@e0800000 { compatible = "snps,dwxgmac-2.10", "snps,dwxgmac"; reg = <0xe0800000 0x8000>; @@ -516,6 +663,42 @@ examples: snps,axi-config = <&stmmac_axi_setup>; snps,mtl-rx-config = <&mtl_rx_setup>; snps,mtl-tx-config = <&mtl_tx_setup>; + + stmmac_axi_setup: stmmac-axi-config { + snps,wr_osr_lmt = <0xf>; + snps,rd_osr_lmt = <0xf>; + snps,blen = <256 128 64 32 0 0 0>; + }; + + mtl_rx_setup: rx-queues-config { + snps,rx-queues-to-use = <1>; + snps,rx-sched-sp; + queue0 { + snps,dcb-algorithm; + snps,map-to-dma-channel = <0x0>; + snps,priority = <0x0>; + }; + }; + + mtl_tx_setup: tx-queues-config { + snps,tx-queues-to-use = <2>; + snps,tx-sched-wrr; + queue0 { + snps,weight = <0x10>; + snps,dcb-algorithm; + snps,priority = <0x0>; + }; + + queue1 { + snps,avb-algorithm; + snps,send_slope = <0x1000>; + snps,idle_slope = <0x1000>; + snps,high_credit = <0x3E800>; + snps,low_credit = <0xFFC18000>; + snps,priority = <0x1>; + }; + }; + mdio0 { #address-cells = <1>; #size-cells = <0>; From eca485d22165695587bed02d8b9d0f7f44246c4a Mon Sep 17 00:00:00 2001 From: Denis Kirjanov Date: Thu, 27 Oct 2022 21:45:02 +0300 Subject: [PATCH 0471/2002] drivers: net: convert to boolean for the mac_managed_pm flag Signed-off-by: Dennis Kirjanov Signed-off-by: David S. Miller --- drivers/net/ethernet/freescale/fec_main.c | 2 +- drivers/net/ethernet/realtek/r8169_main.c | 2 +- drivers/net/usb/asix_devices.c | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c index 34566c007069..bfbb5602e38a 100644 --- a/drivers/net/ethernet/freescale/fec_main.c +++ b/drivers/net/ethernet/freescale/fec_main.c @@ -2169,7 +2169,7 @@ static int fec_enet_mii_probe(struct net_device *ndev) fep->link = 0; fep->full_duplex = 0; - phy_dev->mac_managed_pm = 1; + phy_dev->mac_managed_pm = true; phy_attached_info(phy_dev); diff --git a/drivers/net/ethernet/realtek/r8169_main.c b/drivers/net/ethernet/realtek/r8169_main.c index a73d061d9fcb..5bc1181f829b 100644 --- a/drivers/net/ethernet/realtek/r8169_main.c +++ b/drivers/net/ethernet/realtek/r8169_main.c @@ -5018,7 +5018,7 @@ static int r8169_mdio_register(struct rtl8169_private *tp) return -EUNATCH; } - tp->phydev->mac_managed_pm = 1; + tp->phydev->mac_managed_pm = true; phy_support_asym_pause(tp->phydev); diff --git a/drivers/net/usb/asix_devices.c b/drivers/net/usb/asix_devices.c index 11f60d32be82..02941d97d034 100644 --- a/drivers/net/usb/asix_devices.c +++ b/drivers/net/usb/asix_devices.c @@ -700,7 +700,7 @@ static int ax88772_init_phy(struct usbnet *dev) } phy_suspend(priv->phydev); - priv->phydev->mac_managed_pm = 1; + priv->phydev->mac_managed_pm = true; phy_attached_info(priv->phydev); @@ -720,7 +720,7 @@ static int ax88772_init_phy(struct usbnet *dev) return -ENODEV; } - priv->phydev_int->mac_managed_pm = 1; + priv->phydev_int->mac_managed_pm = true; phy_suspend(priv->phydev_int); return 0; From 738136a0e3757a8534df3ad97d6ff6d7f429f6c1 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Thu, 27 Oct 2022 14:25:53 -0700 Subject: [PATCH 0472/2002] netlink: split up copies in the ack construction Clean up the use of unsafe_memcpy() by adding a flexible array at the end of netlink message header and splitting up the header and data copies. Reviewed-by: Kees Cook Signed-off-by: Jakub Kicinski Signed-off-by: David S. Miller --- include/net/netlink.h | 21 +++++++++++++++++++++ include/uapi/linux/netlink.h | 2 ++ net/netlink/af_netlink.c | 29 ++++++++++++++++++++--------- 3 files changed, 43 insertions(+), 9 deletions(-) diff --git a/include/net/netlink.h b/include/net/netlink.h index 4418b1981e31..784b4688fc6f 100644 --- a/include/net/netlink.h +++ b/include/net/netlink.h @@ -931,6 +931,27 @@ static inline struct nlmsghdr *nlmsg_put(struct sk_buff *skb, u32 portid, u32 se return __nlmsg_put(skb, portid, seq, type, payload, flags); } +/** + * nlmsg_append - Add more data to a nlmsg in a skb + * @skb: socket buffer to store message in + * @size: length of message payload + * + * Append data to an existing nlmsg, used when constructing a message + * with multiple fixed-format headers (which is rare). + * Returns NULL if the tailroom of the skb is insufficient to store + * the extra payload. + */ +static inline void *nlmsg_append(struct sk_buff *skb, u32 size) +{ + if (unlikely(skb_tailroom(skb) < NLMSG_ALIGN(size))) + return NULL; + + if (NLMSG_ALIGN(size) - size) + memset(skb_tail_pointer(skb) + size, 0, + NLMSG_ALIGN(size) - size); + return __skb_put(skb, NLMSG_ALIGN(size)); +} + /** * nlmsg_put_answer - Add a new callback based netlink message to an skb * @skb: socket buffer to store message in diff --git a/include/uapi/linux/netlink.h b/include/uapi/linux/netlink.h index e2ae82e3f9f7..5da0da59bf01 100644 --- a/include/uapi/linux/netlink.h +++ b/include/uapi/linux/netlink.h @@ -48,6 +48,7 @@ struct sockaddr_nl { * @nlmsg_flags: Additional flags * @nlmsg_seq: Sequence number * @nlmsg_pid: Sending process port ID + * @nlmsg_data: Message payload */ struct nlmsghdr { __u32 nlmsg_len; @@ -55,6 +56,7 @@ struct nlmsghdr { __u16 nlmsg_flags; __u32 nlmsg_seq; __u32 nlmsg_pid; + __u8 nlmsg_data[]; }; /* Flags values */ diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c index f0c94d394ab1..b10d5e50b99d 100644 --- a/net/netlink/af_netlink.c +++ b/net/netlink/af_netlink.c @@ -2499,19 +2499,24 @@ void netlink_ack(struct sk_buff *in_skb, struct nlmsghdr *nlh, int err, flags |= NLM_F_ACK_TLVS; skb = nlmsg_new(payload + tlvlen, GFP_KERNEL); - if (!skb) { - NETLINK_CB(in_skb).sk->sk_err = ENOBUFS; - sk_error_report(NETLINK_CB(in_skb).sk); - return; - } + if (!skb) + goto err_bad_put; rep = nlmsg_put(skb, NETLINK_CB(in_skb).portid, nlh->nlmsg_seq, - NLMSG_ERROR, payload, flags); + NLMSG_ERROR, sizeof(*errmsg), flags); + if (!rep) + goto err_bad_put; errmsg = nlmsg_data(rep); errmsg->error = err; - unsafe_memcpy(&errmsg->msg, nlh, payload > sizeof(*errmsg) - ? nlh->nlmsg_len : sizeof(*nlh), - /* Bounds checked by the skb layer. */); + errmsg->msg = *nlh; + + if (!(flags & NLM_F_CAPPED)) { + if (!nlmsg_append(skb, nlmsg_len(nlh))) + goto err_bad_put; + + memcpy(errmsg->msg.nlmsg_data, nlh->nlmsg_data, + nlmsg_len(nlh)); + } if (tlvlen) netlink_ack_tlv_fill(in_skb, skb, nlh, err, extack); @@ -2519,6 +2524,12 @@ void netlink_ack(struct sk_buff *in_skb, struct nlmsghdr *nlh, int err, nlmsg_end(skb, rep); nlmsg_unicast(in_skb->sk, skb, NETLINK_CB(in_skb).portid); + + return; + +err_bad_put: + NETLINK_CB(in_skb).sk->sk_err = ENOBUFS; + sk_error_report(NETLINK_CB(in_skb).sk); } EXPORT_SYMBOL(netlink_ack); From 47aeed9d2ccde99c990ba3163b8d8308ad00df1b Mon Sep 17 00:00:00 2001 From: Yang Yingliang Date: Fri, 28 Oct 2022 15:34:57 +0800 Subject: [PATCH 0473/2002] net: hns: hnae: remove unnecessary __module_get() and module_put() hnae_ae_register() is called from hns_dsaf_probe(), the refcount of module hnae has already be got in resolve_symbol() while calling the function, so the __module_get()/module_put() can be removed. Signed-off-by: Yang Yingliang Signed-off-by: David S. Miller --- drivers/net/ethernet/hisilicon/hns/hnae.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/net/ethernet/hisilicon/hns/hnae.c b/drivers/net/ethernet/hisilicon/hns/hnae.c index 430eccea8e5e..9b26f0f2c748 100644 --- a/drivers/net/ethernet/hisilicon/hns/hnae.c +++ b/drivers/net/ethernet/hisilicon/hns/hnae.c @@ -424,8 +424,6 @@ int hnae_ae_register(struct hnae_ae_dev *hdev, struct module *owner) return ret; } - __module_get(THIS_MODULE); - INIT_LIST_HEAD(&hdev->handle_list); spin_lock_init(&hdev->lock); @@ -445,7 +443,6 @@ EXPORT_SYMBOL(hnae_ae_register); void hnae_ae_unregister(struct hnae_ae_dev *hdev) { device_unregister(&hdev->cls_dev); - module_put(THIS_MODULE); } EXPORT_SYMBOL(hnae_ae_unregister); From 8c2a535e089b2ab82cf50c876bd10c8ed33252c9 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Thu, 27 Oct 2022 20:52:59 -0700 Subject: [PATCH 0474/2002] net: geneve: fix array of flexible structures warnings New compilers don't like flexible array of flexible structs: include/net/geneve.h:62:34: warning: array of flexible structures Signed-off-by: Jakub Kicinski Signed-off-by: David S. Miller --- include/net/geneve.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/net/geneve.h b/include/net/geneve.h index bced0b1d9fe4..5c96827a487e 100644 --- a/include/net/geneve.h +++ b/include/net/geneve.h @@ -59,7 +59,7 @@ struct genevehdr { __be16 proto_type; u8 vni[3]; u8 rsvd2; - struct geneve_opt options[]; + u8 options[]; }; static inline bool netif_is_geneve(const struct net_device *dev) From e8572f038a527a4f865b0992057963252f14ba40 Mon Sep 17 00:00:00 2001 From: Yang Yingliang Date: Fri, 28 Oct 2022 16:11:06 +0800 Subject: [PATCH 0475/2002] net: microchip: sparx5: kunit test: change test_callbacks and test_vctrl to static test_callbacks and test_vctrl are only used in vcap_api_kunit.c now, change them to static. Fixes: 67d637516fa9 ("net: microchip: sparx5: Adding KUNIT test for the VCAP API") Signed-off-by: Yang Yingliang Reviewed-by: Steen Hegelund Signed-off-by: David S. Miller --- drivers/net/ethernet/microchip/vcap/vcap_api_kunit.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/microchip/vcap/vcap_api_kunit.c b/drivers/net/ethernet/microchip/vcap/vcap_api_kunit.c index b01a6e5039b0..d142ed660338 100644 --- a/drivers/net/ethernet/microchip/vcap/vcap_api_kunit.c +++ b/drivers/net/ethernet/microchip/vcap/vcap_api_kunit.c @@ -204,7 +204,7 @@ static int vcap_test_port_info(struct net_device *ndev, enum vcap_type vtype, return 0; } -struct vcap_operations test_callbacks = { +static struct vcap_operations test_callbacks = { .validate_keyset = test_val_keyset, .add_default_fields = test_add_def_fields, .cache_erase = test_cache_erase, @@ -216,7 +216,7 @@ struct vcap_operations test_callbacks = { .port_info = vcap_test_port_info, }; -struct vcap_control test_vctrl = { +static struct vcap_control test_vctrl = { .vcaps = kunit_test_vcaps, .stats = &kunit_test_vcap_stats, .ops = &test_callbacks, From 70479a40954cf353e87a486997a3477108c75aa9 Mon Sep 17 00:00:00 2001 From: Frank Date: Fri, 28 Oct 2022 17:26:21 +0800 Subject: [PATCH 0476/2002] net: phy: Add driver for Motorcomm yt8521 gigabit ethernet phy Add a driver for the motorcomm yt8521 gigabit ethernet phy. We have verified the driver on StarFive VisionFive development board, which is developed by Shanghai StarFive Technology Co., Ltd.. On the board, yt8521 gigabit ethernet phy works in utp mode, RGMII interface, supports 1000M/100M/10M speeds, and wol(magic package). Signed-off-by: Frank Reviewed-by: Andrew Lunn Signed-off-by: David S. Miller --- MAINTAINERS | 1 + drivers/net/phy/Kconfig | 2 +- drivers/net/phy/motorcomm.c | 1635 ++++++++++++++++++++++++++++++++++- 3 files changed, 1635 insertions(+), 3 deletions(-) diff --git a/MAINTAINERS b/MAINTAINERS index 377aedb3808b..7d44c5a6607d 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -13943,6 +13943,7 @@ F: include/uapi/linux/meye.h MOTORCOMM PHY DRIVER M: Peter Geis +M: Frank L: netdev@vger.kernel.org S: Maintained F: drivers/net/phy/motorcomm.c diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig index c57a0262fb64..040c8bf6d05b 100644 --- a/drivers/net/phy/Kconfig +++ b/drivers/net/phy/Kconfig @@ -260,7 +260,7 @@ config MOTORCOMM_PHY tristate "Motorcomm PHYs" help Enables support for Motorcomm network PHYs. - Currently supports the YT8511 gigabit PHY. + Currently supports the YT8511, YT8521 Gigabit Ethernet PHYs. config NATIONAL_PHY tristate "National Semiconductor PHYs" diff --git a/drivers/net/phy/motorcomm.c b/drivers/net/phy/motorcomm.c index 7e6ac2c5e27e..c7593f224177 100644 --- a/drivers/net/phy/motorcomm.c +++ b/drivers/net/phy/motorcomm.c @@ -1,15 +1,106 @@ // SPDX-License-Identifier: GPL-2.0+ /* - * Driver for Motorcomm PHYs + * Motorcomm 8511/8521 PHY driver. * * Author: Peter Geis + * Author: Frank */ +#include #include #include #include #define PHY_ID_YT8511 0x0000010a +#define PHY_ID_YT8521 0x0000011A + +/* YT8521 Register Overview + * UTP Register space | FIBER Register space + * ------------------------------------------------------------ + * | UTP MII | FIBER MII | + * | UTP MMD | | + * | UTP Extended | FIBER Extended | + * ------------------------------------------------------------ + * | Common Extended | + * ------------------------------------------------------------ + */ + +/* 0x10 ~ 0x15 , 0x1E and 0x1F are common MII registers of yt phy */ + +/* Specific Function Control Register */ +#define YTPHY_SPECIFIC_FUNCTION_CONTROL_REG 0x10 + +/* 2b00 Manual MDI configuration + * 2b01 Manual MDIX configuration + * 2b10 Reserved + * 2b11 Enable automatic crossover for all modes *default* + */ +#define YTPHY_SFCR_MDI_CROSSOVER_MODE_MASK (BIT(6) | BIT(5)) +#define YTPHY_SFCR_CROSSOVER_EN BIT(3) +#define YTPHY_SFCR_SQE_TEST_EN BIT(2) +#define YTPHY_SFCR_POLARITY_REVERSAL_EN BIT(1) +#define YTPHY_SFCR_JABBER_DIS BIT(0) + +/* Specific Status Register */ +#define YTPHY_SPECIFIC_STATUS_REG 0x11 +#define YTPHY_SSR_SPEED_MODE_OFFSET 14 + +#define YTPHY_SSR_SPEED_MODE_MASK (BIT(15) | BIT(14)) +#define YTPHY_SSR_SPEED_10M 0x0 +#define YTPHY_SSR_SPEED_100M 0x1 +#define YTPHY_SSR_SPEED_1000M 0x2 +#define YTPHY_SSR_DUPLEX_OFFSET 13 +#define YTPHY_SSR_DUPLEX BIT(13) +#define YTPHY_SSR_PAGE_RECEIVED BIT(12) +#define YTPHY_SSR_SPEED_DUPLEX_RESOLVED BIT(11) +#define YTPHY_SSR_LINK BIT(10) +#define YTPHY_SSR_MDIX_CROSSOVER BIT(6) +#define YTPHY_SSR_DOWNGRADE BIT(5) +#define YTPHY_SSR_TRANSMIT_PAUSE BIT(3) +#define YTPHY_SSR_RECEIVE_PAUSE BIT(2) +#define YTPHY_SSR_POLARITY BIT(1) +#define YTPHY_SSR_JABBER BIT(0) + +/* Interrupt enable Register */ +#define YTPHY_INTERRUPT_ENABLE_REG 0x12 +#define YTPHY_IER_WOL BIT(6) + +/* Interrupt Status Register */ +#define YTPHY_INTERRUPT_STATUS_REG 0x13 +#define YTPHY_ISR_AUTONEG_ERR BIT(15) +#define YTPHY_ISR_SPEED_CHANGED BIT(14) +#define YTPHY_ISR_DUPLEX_CHANGED BIT(13) +#define YTPHY_ISR_PAGE_RECEIVED BIT(12) +#define YTPHY_ISR_LINK_FAILED BIT(11) +#define YTPHY_ISR_LINK_SUCCESSED BIT(10) +#define YTPHY_ISR_WOL BIT(6) +#define YTPHY_ISR_WIRESPEED_DOWNGRADE BIT(5) +#define YTPHY_ISR_SERDES_LINK_FAILED BIT(3) +#define YTPHY_ISR_SERDES_LINK_SUCCESSED BIT(2) +#define YTPHY_ISR_POLARITY_CHANGED BIT(1) +#define YTPHY_ISR_JABBER_HAPPENED BIT(0) + +/* Speed Auto Downgrade Control Register */ +#define YTPHY_SPEED_AUTO_DOWNGRADE_CONTROL_REG 0x14 +#define YTPHY_SADCR_SPEED_DOWNGRADE_EN BIT(5) + +/* If these bits are set to 3, the PHY attempts five times ( 3(set value) + + * additional 2) before downgrading, default 0x3 + */ +#define YTPHY_SADCR_SPEED_RETRY_LIMIT (0x3 << 2) + +/* Rx Error Counter Register */ +#define YTPHY_RX_ERROR_COUNTER_REG 0x15 + +/* Extended Register's Address Offset Register */ +#define YTPHY_PAGE_SELECT 0x1E + +/* Extended Register's Data Register */ +#define YTPHY_PAGE_DATA 0x1F + +/* FIBER Auto-Negotiation link partner ability */ +#define YTPHY_FLPA_PAUSE (0x3 << 7) +#define YTPHY_FLPA_ASYM_PAUSE (0x2 << 7) #define YT8511_PAGE_SELECT 0x1e #define YT8511_PAGE 0x1f @@ -38,6 +129,352 @@ #define YT8511_DELAY_FE_TX_EN (0xf << 12) #define YT8511_DELAY_FE_TX_DIS (0x2 << 12) +/* Extended register is different from MMD Register and MII Register. + * We can use ytphy_read_ext/ytphy_write_ext/ytphy_modify_ext function to + * operate extended register. + * Extended Register start + */ + +/* Phy gmii clock gating Register */ +#define YT8521_CLOCK_GATING_REG 0xC +#define YT8521_CGR_RX_CLK_EN BIT(12) + +#define YT8521_EXTREG_SLEEP_CONTROL1_REG 0x27 +#define YT8521_ESC1R_SLEEP_SW BIT(15) +#define YT8521_ESC1R_PLLON_SLP BIT(14) + +/* Phy fiber Link timer cfg2 Register */ +#define YT8521_LINK_TIMER_CFG2_REG 0xA5 +#define YT8521_LTCR_EN_AUTOSEN BIT(15) + +/* 0xA000, 0xA001, 0xA003 ,and 0xA006 ~ 0xA00A are common ext registers + * of yt8521 phy. There is no need to switch reg space when operating these + * registers. + */ + +#define YT8521_REG_SPACE_SELECT_REG 0xA000 +#define YT8521_RSSR_SPACE_MASK BIT(1) +#define YT8521_RSSR_FIBER_SPACE (0x1 << 1) +#define YT8521_RSSR_UTP_SPACE (0x0 << 1) +#define YT8521_RSSR_TO_BE_ARBITRATED (0xFF) + +#define YT8521_CHIP_CONFIG_REG 0xA001 +#define YT8521_CCR_SW_RST BIT(15) + +#define YT8521_CCR_MODE_SEL_MASK (BIT(2) | BIT(1) | BIT(0)) +#define YT8521_CCR_MODE_UTP_TO_RGMII 0 +#define YT8521_CCR_MODE_FIBER_TO_RGMII 1 +#define YT8521_CCR_MODE_UTP_FIBER_TO_RGMII 2 +#define YT8521_CCR_MODE_UTP_TO_SGMII 3 +#define YT8521_CCR_MODE_SGPHY_TO_RGMAC 4 +#define YT8521_CCR_MODE_SGMAC_TO_RGPHY 5 +#define YT8521_CCR_MODE_UTP_TO_FIBER_AUTO 6 +#define YT8521_CCR_MODE_UTP_TO_FIBER_FORCE 7 + +/* 3 phy polling modes,poll mode combines utp and fiber mode*/ +#define YT8521_MODE_FIBER 0x1 +#define YT8521_MODE_UTP 0x2 +#define YT8521_MODE_POLL 0x3 + +#define YT8521_RGMII_CONFIG1_REG 0xA003 + +/* TX Gig-E Delay is bits 3:0, default 0x1 + * TX Fast-E Delay is bits 7:4, default 0xf + * RX Delay is bits 13:10, default 0x0 + * Delay = 150ps * N + * On = 2250ps, off = 0ps + */ +#define YT8521_RC1R_RX_DELAY_MASK (0xF << 10) +#define YT8521_RC1R_RX_DELAY_EN (0xF << 10) +#define YT8521_RC1R_RX_DELAY_DIS (0x0 << 10) +#define YT8521_RC1R_FE_TX_DELAY_MASK (0xF << 4) +#define YT8521_RC1R_FE_TX_DELAY_EN (0xF << 4) +#define YT8521_RC1R_FE_TX_DELAY_DIS (0x0 << 4) +#define YT8521_RC1R_GE_TX_DELAY_MASK (0xF << 0) +#define YT8521_RC1R_GE_TX_DELAY_EN (0xF << 0) +#define YT8521_RC1R_GE_TX_DELAY_DIS (0x0 << 0) + +#define YTPHY_MISC_CONFIG_REG 0xA006 +#define YTPHY_MCR_FIBER_SPEED_MASK BIT(0) +#define YTPHY_MCR_FIBER_1000BX (0x1 << 0) +#define YTPHY_MCR_FIBER_100FX (0x0 << 0) + +/* WOL MAC ADDR: MACADDR2(highest), MACADDR1(middle), MACADDR0(lowest) */ +#define YTPHY_WOL_MACADDR2_REG 0xA007 +#define YTPHY_WOL_MACADDR1_REG 0xA008 +#define YTPHY_WOL_MACADDR0_REG 0xA009 + +#define YTPHY_WOL_CONFIG_REG 0xA00A +#define YTPHY_WCR_INTR_SEL BIT(6) +#define YTPHY_WCR_ENABLE BIT(3) + +/* 2b00 84ms + * 2b01 168ms *default* + * 2b10 336ms + * 2b11 672ms + */ +#define YTPHY_WCR_PULSE_WIDTH_MASK (BIT(2) | BIT(1)) +#define YTPHY_WCR_PULSE_WIDTH_672MS (BIT(2) | BIT(1)) + +/* 1b0 Interrupt and WOL events is level triggered and active LOW *default* + * 1b1 Interrupt and WOL events is pulse triggered and active LOW + */ +#define YTPHY_WCR_TYPE_PULSE BIT(0) + +/* Extended Register end */ + +struct yt8521_priv { + /* combo_advertising is used for case of YT8521 in combo mode, + * this means that yt8521 may work in utp or fiber mode which depends + * on which media is connected (YT8521_RSSR_TO_BE_ARBITRATED). + */ + __ETHTOOL_DECLARE_LINK_MODE_MASK(combo_advertising); + + /* YT8521_MODE_FIBER / YT8521_MODE_UTP / YT8521_MODE_POLL*/ + u8 polling_mode; + u8 strap_mode; /* 8 working modes */ + /* current reg page of yt8521 phy: + * YT8521_RSSR_UTP_SPACE + * YT8521_RSSR_FIBER_SPACE + * YT8521_RSSR_TO_BE_ARBITRATED + */ + u8 reg_page; +}; + +/** + * ytphy_read_ext() - read a PHY's extended register + * @phydev: a pointer to a &struct phy_device + * @regnum: register number to read + * + * NOTE:The caller must have taken the MDIO bus lock. + * + * returns the value of regnum reg or negative error code + */ +static int ytphy_read_ext(struct phy_device *phydev, u16 regnum) +{ + int ret; + + ret = __phy_write(phydev, YTPHY_PAGE_SELECT, regnum); + if (ret < 0) + return ret; + + return __phy_read(phydev, YTPHY_PAGE_DATA); +} + +/** + * ytphy_read_ext_with_lock() - read a PHY's extended register + * @phydev: a pointer to a &struct phy_device + * @regnum: register number to read + * + * returns the value of regnum reg or negative error code + */ +static int ytphy_read_ext_with_lock(struct phy_device *phydev, u16 regnum) +{ + int ret; + + phy_lock_mdio_bus(phydev); + ret = ytphy_read_ext(phydev, regnum); + phy_unlock_mdio_bus(phydev); + + return ret; +} + +/** + * ytphy_write_ext() - write a PHY's extended register + * @phydev: a pointer to a &struct phy_device + * @regnum: register number to write + * @val: value to write to @regnum + * + * NOTE:The caller must have taken the MDIO bus lock. + * + * returns 0 or negative error code + */ +static int ytphy_write_ext(struct phy_device *phydev, u16 regnum, u16 val) +{ + int ret; + + ret = __phy_write(phydev, YTPHY_PAGE_SELECT, regnum); + if (ret < 0) + return ret; + + return __phy_write(phydev, YTPHY_PAGE_DATA, val); +} + +/** + * ytphy_write_ext_with_lock() - write a PHY's extended register + * @phydev: a pointer to a &struct phy_device + * @regnum: register number to write + * @val: value to write to @regnum + * + * returns 0 or negative error code + */ +static int ytphy_write_ext_with_lock(struct phy_device *phydev, u16 regnum, + u16 val) +{ + int ret; + + phy_lock_mdio_bus(phydev); + ret = ytphy_write_ext(phydev, regnum, val); + phy_unlock_mdio_bus(phydev); + + return ret; +} + +/** + * ytphy_modify_ext() - bits modify a PHY's extended register + * @phydev: a pointer to a &struct phy_device + * @regnum: register number to write + * @mask: bit mask of bits to clear + * @set: bit mask of bits to set + * + * NOTE: Convenience function which allows a PHY's extended register to be + * modified as new register value = (old register value & ~mask) | set. + * The caller must have taken the MDIO bus lock. + * + * returns 0 or negative error code + */ +static int ytphy_modify_ext(struct phy_device *phydev, u16 regnum, u16 mask, + u16 set) +{ + int ret; + + ret = __phy_write(phydev, YTPHY_PAGE_SELECT, regnum); + if (ret < 0) + return ret; + + return __phy_modify(phydev, YTPHY_PAGE_DATA, mask, set); +} + +/** + * ytphy_modify_ext_with_lock() - bits modify a PHY's extended register + * @phydev: a pointer to a &struct phy_device + * @regnum: register number to write + * @mask: bit mask of bits to clear + * @set: bit mask of bits to set + * + * NOTE: Convenience function which allows a PHY's extended register to be + * modified as new register value = (old register value & ~mask) | set. + * + * returns 0 or negative error code + */ +static int ytphy_modify_ext_with_lock(struct phy_device *phydev, u16 regnum, + u16 mask, u16 set) +{ + int ret; + + phy_lock_mdio_bus(phydev); + ret = ytphy_modify_ext(phydev, regnum, mask, set); + phy_unlock_mdio_bus(phydev); + + return ret; +} + +/** + * ytphy_get_wol() - report whether wake-on-lan is enabled + * @phydev: a pointer to a &struct phy_device + * @wol: a pointer to a &struct ethtool_wolinfo + * + * NOTE: YTPHY_WOL_CONFIG_REG is common ext reg. + */ +static void ytphy_get_wol(struct phy_device *phydev, + struct ethtool_wolinfo *wol) +{ + int wol_config; + + wol->supported = WAKE_MAGIC; + wol->wolopts = 0; + + wol_config = ytphy_read_ext_with_lock(phydev, YTPHY_WOL_CONFIG_REG); + if (wol_config < 0) + return; + + if (wol_config & YTPHY_WCR_ENABLE) + wol->wolopts |= WAKE_MAGIC; +} + +/** + * ytphy_set_wol() - turn wake-on-lan on or off + * @phydev: a pointer to a &struct phy_device + * @wol: a pointer to a &struct ethtool_wolinfo + * + * NOTE: YTPHY_WOL_CONFIG_REG, YTPHY_WOL_MACADDR2_REG, YTPHY_WOL_MACADDR1_REG + * and YTPHY_WOL_MACADDR0_REG are common ext reg. The + * YTPHY_INTERRUPT_ENABLE_REG of UTP is special, fiber also use this register. + * + * returns 0 or negative errno code + */ +static int ytphy_set_wol(struct phy_device *phydev, struct ethtool_wolinfo *wol) +{ + struct net_device *p_attached_dev; + const u16 mac_addr_reg[] = { + YTPHY_WOL_MACADDR2_REG, + YTPHY_WOL_MACADDR1_REG, + YTPHY_WOL_MACADDR0_REG, + }; + const u8 *mac_addr; + int old_page; + int ret = 0; + u16 mask; + u16 val; + u8 i; + + if (wol->wolopts & WAKE_MAGIC) { + p_attached_dev = phydev->attached_dev; + if (!p_attached_dev) + return -ENODEV; + + mac_addr = (const u8 *)p_attached_dev->dev_addr; + if (!is_valid_ether_addr(mac_addr)) + return -EINVAL; + + /* lock mdio bus then switch to utp reg space */ + old_page = phy_select_page(phydev, YT8521_RSSR_UTP_SPACE); + if (old_page < 0) + goto err_restore_page; + + /* Store the device address for the magic packet */ + for (i = 0; i < 3; i++) { + ret = ytphy_write_ext(phydev, mac_addr_reg[i], + ((mac_addr[i * 2] << 8)) | + (mac_addr[i * 2 + 1])); + if (ret < 0) + goto err_restore_page; + } + + /* Enable WOL feature */ + mask = YTPHY_WCR_PULSE_WIDTH_MASK | YTPHY_WCR_INTR_SEL; + val = YTPHY_WCR_ENABLE | YTPHY_WCR_INTR_SEL; + val |= YTPHY_WCR_TYPE_PULSE | YTPHY_WCR_PULSE_WIDTH_672MS; + ret = ytphy_modify_ext(phydev, YTPHY_WOL_CONFIG_REG, mask, val); + if (ret < 0) + goto err_restore_page; + + /* Enable WOL interrupt */ + ret = __phy_modify(phydev, YTPHY_INTERRUPT_ENABLE_REG, 0, + YTPHY_IER_WOL); + if (ret < 0) + goto err_restore_page; + + } else { + old_page = phy_select_page(phydev, YT8521_RSSR_UTP_SPACE); + if (old_page < 0) + goto err_restore_page; + + /* Disable WOL feature */ + mask = YTPHY_WCR_ENABLE | YTPHY_WCR_INTR_SEL; + ret = ytphy_modify_ext(phydev, YTPHY_WOL_CONFIG_REG, mask, 0); + + /* Disable WOL interrupt */ + ret = __phy_modify(phydev, YTPHY_INTERRUPT_ENABLE_REG, + YTPHY_IER_WOL, 0); + if (ret < 0) + goto err_restore_page; + } + +err_restore_page: + return phy_restore_page(phydev, old_page, ret); +} + static int yt8511_read_page(struct phy_device *phydev) { return __phy_read(phydev, YT8511_PAGE_SELECT); @@ -111,6 +548,1181 @@ err_restore_page: return phy_restore_page(phydev, oldpage, ret); } +/** + * yt8521_read_page() - read reg page + * @phydev: a pointer to a &struct phy_device + * + * returns current reg space of yt8521 (YT8521_RSSR_FIBER_SPACE/ + * YT8521_RSSR_UTP_SPACE) or negative errno code + */ +static int yt8521_read_page(struct phy_device *phydev) +{ + int old_page; + + old_page = ytphy_read_ext(phydev, YT8521_REG_SPACE_SELECT_REG); + if (old_page < 0) + return old_page; + + if ((old_page & YT8521_RSSR_SPACE_MASK) == YT8521_RSSR_FIBER_SPACE) + return YT8521_RSSR_FIBER_SPACE; + + return YT8521_RSSR_UTP_SPACE; +}; + +/** + * yt8521_write_page() - write reg page + * @phydev: a pointer to a &struct phy_device + * @page: The reg page(YT8521_RSSR_FIBER_SPACE/YT8521_RSSR_UTP_SPACE) to write. + * + * returns 0 or negative errno code + */ +static int yt8521_write_page(struct phy_device *phydev, int page) +{ + int mask = YT8521_RSSR_SPACE_MASK; + int set; + + if ((page & YT8521_RSSR_SPACE_MASK) == YT8521_RSSR_FIBER_SPACE) + set = YT8521_RSSR_FIBER_SPACE; + else + set = YT8521_RSSR_UTP_SPACE; + + return ytphy_modify_ext(phydev, YT8521_REG_SPACE_SELECT_REG, mask, set); +}; + +/** + * yt8521_probe() - read chip config then set suitable polling_mode + * @phydev: a pointer to a &struct phy_device + * + * returns 0 or negative errno code + */ +static int yt8521_probe(struct phy_device *phydev) +{ + struct device *dev = &phydev->mdio.dev; + struct yt8521_priv *priv; + int chip_config; + int ret; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + phydev->priv = priv; + + chip_config = ytphy_read_ext_with_lock(phydev, YT8521_CHIP_CONFIG_REG); + if (chip_config < 0) + return chip_config; + + priv->strap_mode = chip_config & YT8521_CCR_MODE_SEL_MASK; + switch (priv->strap_mode) { + case YT8521_CCR_MODE_FIBER_TO_RGMII: + case YT8521_CCR_MODE_SGPHY_TO_RGMAC: + case YT8521_CCR_MODE_SGMAC_TO_RGPHY: + priv->polling_mode = YT8521_MODE_FIBER; + priv->reg_page = YT8521_RSSR_FIBER_SPACE; + phydev->port = PORT_FIBRE; + break; + case YT8521_CCR_MODE_UTP_FIBER_TO_RGMII: + case YT8521_CCR_MODE_UTP_TO_FIBER_AUTO: + case YT8521_CCR_MODE_UTP_TO_FIBER_FORCE: + priv->polling_mode = YT8521_MODE_POLL; + priv->reg_page = YT8521_RSSR_TO_BE_ARBITRATED; + phydev->port = PORT_NONE; + break; + case YT8521_CCR_MODE_UTP_TO_SGMII: + case YT8521_CCR_MODE_UTP_TO_RGMII: + priv->polling_mode = YT8521_MODE_UTP; + priv->reg_page = YT8521_RSSR_UTP_SPACE; + phydev->port = PORT_TP; + break; + } + /* set default reg space */ + if (priv->reg_page != YT8521_RSSR_TO_BE_ARBITRATED) { + ret = ytphy_write_ext_with_lock(phydev, + YT8521_REG_SPACE_SELECT_REG, + priv->reg_page); + if (ret < 0) + return ret; + } + + return 0; +} + +/** + * ytphy_utp_read_lpa() - read LPA then setup lp_advertising for utp + * @phydev: a pointer to a &struct phy_device + * + * NOTE:The caller must have taken the MDIO bus lock. + * + * returns 0 or negative errno code + */ +static int ytphy_utp_read_lpa(struct phy_device *phydev) +{ + int lpa, lpagb; + + if (phydev->autoneg == AUTONEG_ENABLE) { + if (!phydev->autoneg_complete) { + mii_stat1000_mod_linkmode_lpa_t(phydev->lp_advertising, + 0); + mii_lpa_mod_linkmode_lpa_t(phydev->lp_advertising, 0); + return 0; + } + + if (phydev->is_gigabit_capable) { + lpagb = __phy_read(phydev, MII_STAT1000); + if (lpagb < 0) + return lpagb; + + if (lpagb & LPA_1000MSFAIL) { + int adv = __phy_read(phydev, MII_CTRL1000); + + if (adv < 0) + return adv; + + if (adv & CTL1000_ENABLE_MASTER) + phydev_err(phydev, "Master/Slave resolution failed, maybe conflicting manual settings?\n"); + else + phydev_err(phydev, "Master/Slave resolution failed\n"); + return -ENOLINK; + } + + mii_stat1000_mod_linkmode_lpa_t(phydev->lp_advertising, + lpagb); + } + + lpa = __phy_read(phydev, MII_LPA); + if (lpa < 0) + return lpa; + + mii_lpa_mod_linkmode_lpa_t(phydev->lp_advertising, lpa); + } else { + linkmode_zero(phydev->lp_advertising); + } + + return 0; +} + +/** + * yt8521_adjust_status() - update speed and duplex to phydev. when in fiber + * mode, adjust speed and duplex. + * @phydev: a pointer to a &struct phy_device + * @status: yt8521 status read from YTPHY_SPECIFIC_STATUS_REG + * @is_utp: false(yt8521 work in fiber mode) or true(yt8521 work in utp mode) + * + * NOTE:The caller must have taken the MDIO bus lock. + * + * returns 0 + */ +static int yt8521_adjust_status(struct phy_device *phydev, int status, + bool is_utp) +{ + int speed_mode, duplex; + int speed; + int err; + int lpa; + + if (is_utp) + duplex = (status & YTPHY_SSR_DUPLEX) >> YTPHY_SSR_DUPLEX_OFFSET; + else + duplex = DUPLEX_FULL; /* for fiber, it always DUPLEX_FULL */ + + speed_mode = (status & YTPHY_SSR_SPEED_MODE_MASK) >> + YTPHY_SSR_SPEED_MODE_OFFSET; + + switch (speed_mode) { + case YTPHY_SSR_SPEED_10M: + if (is_utp) + speed = SPEED_10; + else + /* for fiber, it will never run here, default to + * SPEED_UNKNOWN + */ + speed = SPEED_UNKNOWN; + break; + case YTPHY_SSR_SPEED_100M: + speed = SPEED_100; + break; + case YTPHY_SSR_SPEED_1000M: + speed = SPEED_1000; + break; + default: + speed = SPEED_UNKNOWN; + break; + } + + phydev->speed = speed; + phydev->duplex = duplex; + + if (is_utp) { + err = ytphy_utp_read_lpa(phydev); + if (err < 0) + return err; + + phy_resolve_aneg_pause(phydev); + } else { + lpa = __phy_read(phydev, MII_LPA); + if (lpa < 0) + return lpa; + + /* only support 1000baseX Full */ + linkmode_mod_bit(ETHTOOL_LINK_MODE_1000baseX_Full_BIT, + phydev->lp_advertising, lpa & LPA_1000XFULL); + + if (!(lpa & YTPHY_FLPA_PAUSE)) { + phydev->pause = 0; + phydev->asym_pause = 0; + } else if ((lpa & YTPHY_FLPA_ASYM_PAUSE)) { + phydev->pause = 1; + phydev->asym_pause = 1; + } else { + phydev->pause = 1; + phydev->asym_pause = 0; + } + } + + return 0; +} + +/** + * yt8521_read_status_paged() - determines the speed and duplex of one page + * @phydev: a pointer to a &struct phy_device + * @page: The reg page(YT8521_RSSR_FIBER_SPACE/YT8521_RSSR_UTP_SPACE) to + * operate. + * + * returns 1 (utp or fiber link),0 (no link) or negative errno code + */ +static int yt8521_read_status_paged(struct phy_device *phydev, int page) +{ + int fiber_latch_val; + int fiber_curr_val; + int old_page; + int ret = 0; + int status; + int link; + + linkmode_zero(phydev->lp_advertising); + phydev->duplex = DUPLEX_UNKNOWN; + phydev->speed = SPEED_UNKNOWN; + phydev->asym_pause = 0; + phydev->pause = 0; + + /* YT8521 has two reg space (utp/fiber) for linkup with utp/fiber + * respectively. but for utp/fiber combo mode, reg space should be + * arbitrated based on media priority. by default, utp takes + * priority. reg space should be properly set before read + * YTPHY_SPECIFIC_STATUS_REG. + */ + + page &= YT8521_RSSR_SPACE_MASK; + old_page = phy_select_page(phydev, page); + if (old_page < 0) + goto err_restore_page; + + /* Read YTPHY_SPECIFIC_STATUS_REG, which indicates the speed and duplex + * of the PHY is actually using. + */ + ret = __phy_read(phydev, YTPHY_SPECIFIC_STATUS_REG); + if (ret < 0) + goto err_restore_page; + + status = ret; + link = !!(status & YTPHY_SSR_LINK); + + /* When PHY is in fiber mode, speed transferred from 1000Mbps to + * 100Mbps,there is not link down from YTPHY_SPECIFIC_STATUS_REG, so + * we need check MII_BMSR to identify such case. + */ + if (page == YT8521_RSSR_FIBER_SPACE) { + ret = __phy_read(phydev, MII_BMSR); + if (ret < 0) + goto err_restore_page; + + fiber_latch_val = ret; + ret = __phy_read(phydev, MII_BMSR); + if (ret < 0) + goto err_restore_page; + + fiber_curr_val = ret; + if (link && fiber_latch_val != fiber_curr_val) { + link = 0; + phydev_info(phydev, + "%s, fiber link down detect, latch = %04x, curr = %04x\n", + __func__, fiber_latch_val, fiber_curr_val); + } + } else { + /* Read autonegotiation status */ + ret = __phy_read(phydev, MII_BMSR); + if (ret < 0) + goto err_restore_page; + + phydev->autoneg_complete = ret & BMSR_ANEGCOMPLETE ? 1 : 0; + } + + if (link) { + if (page == YT8521_RSSR_UTP_SPACE) + yt8521_adjust_status(phydev, status, true); + else + yt8521_adjust_status(phydev, status, false); + } + return phy_restore_page(phydev, old_page, link); + +err_restore_page: + return phy_restore_page(phydev, old_page, ret); +} + +/** + * yt8521_read_status() - determines the negotiated speed and duplex + * @phydev: a pointer to a &struct phy_device + * + * returns 0 or negative errno code + */ +static int yt8521_read_status(struct phy_device *phydev) +{ + struct yt8521_priv *priv = phydev->priv; + int link_fiber = 0; + int link_utp; + int link; + int ret; + + if (priv->reg_page != YT8521_RSSR_TO_BE_ARBITRATED) { + link = yt8521_read_status_paged(phydev, priv->reg_page); + if (link < 0) + return link; + } else { + /* when page is YT8521_RSSR_TO_BE_ARBITRATED, arbitration is + * needed. by default, utp is higher priority. + */ + + link_utp = yt8521_read_status_paged(phydev, + YT8521_RSSR_UTP_SPACE); + if (link_utp < 0) + return link_utp; + + if (!link_utp) { + link_fiber = yt8521_read_status_paged(phydev, + YT8521_RSSR_FIBER_SPACE); + if (link_fiber < 0) + return link_fiber; + } + + link = link_utp || link_fiber; + } + + if (link) { + if (phydev->link == 0) { + /* arbitrate reg space based on linkup media type. */ + if (priv->polling_mode == YT8521_MODE_POLL && + priv->reg_page == YT8521_RSSR_TO_BE_ARBITRATED) { + if (link_fiber) + priv->reg_page = + YT8521_RSSR_FIBER_SPACE; + else + priv->reg_page = YT8521_RSSR_UTP_SPACE; + + ret = ytphy_write_ext_with_lock(phydev, + YT8521_REG_SPACE_SELECT_REG, + priv->reg_page); + if (ret < 0) + return ret; + + phydev->port = link_fiber ? PORT_FIBRE : PORT_TP; + + phydev_info(phydev, "%s, link up, media: %s\n", + __func__, + (phydev->port == PORT_TP) ? + "UTP" : "Fiber"); + } + } + phydev->link = 1; + } else { + if (phydev->link == 1) { + phydev_info(phydev, "%s, link down, media: %s\n", + __func__, (phydev->port == PORT_TP) ? + "UTP" : "Fiber"); + + /* When in YT8521_MODE_POLL mode, need prepare for next + * arbitration. + */ + if (priv->polling_mode == YT8521_MODE_POLL) { + priv->reg_page = YT8521_RSSR_TO_BE_ARBITRATED; + phydev->port = PORT_NONE; + } + } + + phydev->link = 0; + } + + return 0; +} + +/** + * yt8521_modify_bmcr_paged - bits modify a PHY's BMCR register of one page + * @phydev: the phy_device struct + * @page: The reg page(YT8521_RSSR_FIBER_SPACE/YT8521_RSSR_UTP_SPACE) to operate + * @mask: bit mask of bits to clear + * @set: bit mask of bits to set + * + * NOTE: Convenience function which allows a PHY's BMCR register to be + * modified as new register value = (old register value & ~mask) | set. + * YT8521 has two space (utp/fiber) and three mode (utp/fiber/poll), each space + * has MII_BMCR. poll mode combines utp and faber,so need do both. + * If it is reset, it will wait for completion. + * + * returns 0 or negative errno code + */ +static int yt8521_modify_bmcr_paged(struct phy_device *phydev, int page, + u16 mask, u16 set) +{ + int max_cnt = 500; /* the max wait time of reset ~ 500 ms */ + int old_page; + int ret = 0; + + old_page = phy_select_page(phydev, page & YT8521_RSSR_SPACE_MASK); + if (old_page < 0) + goto err_restore_page; + + ret = __phy_modify(phydev, MII_BMCR, mask, set); + if (ret < 0) + goto err_restore_page; + + /* If it is reset, need to wait for the reset to complete */ + if (set == BMCR_RESET) { + while (max_cnt--) { + usleep_range(1000, 1100); + ret = __phy_read(phydev, MII_BMCR); + if (ret < 0) + goto err_restore_page; + + if (!(ret & BMCR_RESET)) + return phy_restore_page(phydev, old_page, 0); + } + } + +err_restore_page: + return phy_restore_page(phydev, old_page, ret); +} + +/** + * yt8521_modify_utp_fiber_bmcr - bits modify a PHY's BMCR register + * @phydev: the phy_device struct + * @mask: bit mask of bits to clear + * @set: bit mask of bits to set + * + * NOTE: Convenience function which allows a PHY's BMCR register to be + * modified as new register value = (old register value & ~mask) | set. + * YT8521 has two space (utp/fiber) and three mode (utp/fiber/poll), each space + * has MII_BMCR. poll mode combines utp and faber,so need do both. + * + * returns 0 or negative errno code + */ +static int yt8521_modify_utp_fiber_bmcr(struct phy_device *phydev, u16 mask, + u16 set) +{ + struct yt8521_priv *priv = phydev->priv; + int ret; + + if (priv->reg_page != YT8521_RSSR_TO_BE_ARBITRATED) { + ret = yt8521_modify_bmcr_paged(phydev, priv->reg_page, mask, + set); + if (ret < 0) + return ret; + } else { + ret = yt8521_modify_bmcr_paged(phydev, YT8521_RSSR_UTP_SPACE, + mask, set); + if (ret < 0) + return ret; + + ret = yt8521_modify_bmcr_paged(phydev, YT8521_RSSR_FIBER_SPACE, + mask, set); + if (ret < 0) + return ret; + } + return 0; +} + +/** + * yt8521_soft_reset() - called to issue a PHY software reset + * @phydev: a pointer to a &struct phy_device + * + * returns 0 or negative errno code + */ +static int yt8521_soft_reset(struct phy_device *phydev) +{ + return yt8521_modify_utp_fiber_bmcr(phydev, 0, BMCR_RESET); +} + +/** + * yt8521_suspend() - suspend the hardware + * @phydev: a pointer to a &struct phy_device + * + * returns 0 or negative errno code + */ +static int yt8521_suspend(struct phy_device *phydev) +{ + int wol_config; + + /* YTPHY_WOL_CONFIG_REG is common ext reg */ + wol_config = ytphy_read_ext_with_lock(phydev, YTPHY_WOL_CONFIG_REG); + if (wol_config < 0) + return wol_config; + + /* if wol enable, do nothing */ + if (wol_config & YTPHY_WCR_ENABLE) + return 0; + + return yt8521_modify_utp_fiber_bmcr(phydev, 0, BMCR_PDOWN); +} + +/** + * yt8521_resume() - resume the hardware + * @phydev: a pointer to a &struct phy_device + * + * returns 0 or negative errno code + */ +static int yt8521_resume(struct phy_device *phydev) +{ + int ret; + int wol_config; + + /* disable auto sleep */ + ret = ytphy_modify_ext_with_lock(phydev, + YT8521_EXTREG_SLEEP_CONTROL1_REG, + YT8521_ESC1R_SLEEP_SW, 0); + if (ret < 0) + return ret; + + wol_config = ytphy_read_ext_with_lock(phydev, YTPHY_WOL_CONFIG_REG); + if (wol_config < 0) + return wol_config; + + /* if wol enable, do nothing */ + if (wol_config & YTPHY_WCR_ENABLE) + return 0; + + return yt8521_modify_utp_fiber_bmcr(phydev, BMCR_PDOWN, 0); +} + +/** + * yt8521_config_init() - called to initialize the PHY + * @phydev: a pointer to a &struct phy_device + * + * returns 0 or negative errno code + */ +static int yt8521_config_init(struct phy_device *phydev) +{ + int old_page; + int ret = 0; + u16 val; + + old_page = phy_select_page(phydev, YT8521_RSSR_UTP_SPACE); + if (old_page < 0) + goto err_restore_page; + + switch (phydev->interface) { + case PHY_INTERFACE_MODE_RGMII: + val = YT8521_RC1R_GE_TX_DELAY_DIS | YT8521_RC1R_GE_TX_DELAY_DIS; + val |= YT8521_RC1R_RX_DELAY_DIS; + break; + case PHY_INTERFACE_MODE_RGMII_RXID: + val = YT8521_RC1R_GE_TX_DELAY_DIS | YT8521_RC1R_GE_TX_DELAY_DIS; + val |= YT8521_RC1R_RX_DELAY_EN; + break; + case PHY_INTERFACE_MODE_RGMII_TXID: + val = YT8521_RC1R_GE_TX_DELAY_EN | YT8521_RC1R_GE_TX_DELAY_EN; + val |= YT8521_RC1R_RX_DELAY_DIS; + break; + case PHY_INTERFACE_MODE_RGMII_ID: + val = YT8521_RC1R_GE_TX_DELAY_EN | YT8521_RC1R_GE_TX_DELAY_EN; + val |= YT8521_RC1R_RX_DELAY_EN; + break; + case PHY_INTERFACE_MODE_SGMII: + break; + default: /* do not support other modes */ + ret = -EOPNOTSUPP; + goto err_restore_page; + } + + /* set rgmii delay mode */ + if (phydev->interface != PHY_INTERFACE_MODE_SGMII) { + ret = ytphy_modify_ext(phydev, YT8521_RGMII_CONFIG1_REG, + (YT8521_RC1R_RX_DELAY_MASK | + YT8521_RC1R_FE_TX_DELAY_MASK | + YT8521_RC1R_GE_TX_DELAY_MASK), + val); + if (ret < 0) + goto err_restore_page; + } + + /* disable auto sleep */ + ret = ytphy_modify_ext(phydev, YT8521_EXTREG_SLEEP_CONTROL1_REG, + YT8521_ESC1R_SLEEP_SW, 0); + if (ret < 0) + goto err_restore_page; + + /* enable RXC clock when no wire plug */ + ret = ytphy_modify_ext(phydev, YT8521_CLOCK_GATING_REG, + YT8521_CGR_RX_CLK_EN, 0); + if (ret < 0) + goto err_restore_page; + +err_restore_page: + return phy_restore_page(phydev, old_page, ret); +} + +/** + * yt8521_prepare_fiber_features() - A small helper function that setup + * fiber's features. + * @phydev: a pointer to a &struct phy_device + * @dst: a pointer to store fiber's features + */ +static void yt8521_prepare_fiber_features(struct phy_device *phydev, + unsigned long *dst) +{ + linkmode_set_bit(ETHTOOL_LINK_MODE_100baseFX_Full_BIT, dst); + linkmode_set_bit(ETHTOOL_LINK_MODE_1000baseX_Full_BIT, dst); + linkmode_set_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, dst); + linkmode_set_bit(ETHTOOL_LINK_MODE_FIBRE_BIT, dst); +} + +/** + * yt8521_fiber_setup_forced - configures/forces speed from @phydev + * @phydev: target phy_device struct + * + * NOTE:The caller must have taken the MDIO bus lock. + * + * returns 0 or negative errno code + */ +static int yt8521_fiber_setup_forced(struct phy_device *phydev) +{ + u16 val; + int ret; + + if (phydev->speed == SPEED_1000) + val = YTPHY_MCR_FIBER_1000BX; + else if (phydev->speed == SPEED_100) + val = YTPHY_MCR_FIBER_100FX; + else + return -EINVAL; + + ret = __phy_modify(phydev, MII_BMCR, BMCR_ANENABLE, 0); + if (ret < 0) + return ret; + + /* disable Fiber auto sensing */ + ret = ytphy_modify_ext(phydev, YT8521_LINK_TIMER_CFG2_REG, + YT8521_LTCR_EN_AUTOSEN, 0); + if (ret < 0) + return ret; + + ret = ytphy_modify_ext(phydev, YTPHY_MISC_CONFIG_REG, + YTPHY_MCR_FIBER_SPEED_MASK, val); + if (ret < 0) + return ret; + + return ytphy_modify_ext(phydev, YT8521_CHIP_CONFIG_REG, + YT8521_CCR_SW_RST, 0); +} + +/** + * ytphy_check_and_restart_aneg - Enable and restart auto-negotiation + * @phydev: target phy_device struct + * @restart: whether aneg restart is requested + * + * NOTE:The caller must have taken the MDIO bus lock. + * + * returns 0 or negative errno code + */ +static int ytphy_check_and_restart_aneg(struct phy_device *phydev, bool restart) +{ + int ret; + + if (!restart) { + /* Advertisement hasn't changed, but maybe aneg was never on to + * begin with? Or maybe phy was isolated? + */ + ret = __phy_read(phydev, MII_BMCR); + if (ret < 0) + return ret; + + if (!(ret & BMCR_ANENABLE) || (ret & BMCR_ISOLATE)) + restart = true; + } + /* Enable and Restart Autonegotiation + * Don't isolate the PHY if we're negotiating + */ + if (restart) + return __phy_modify(phydev, MII_BMCR, BMCR_ISOLATE, + BMCR_ANENABLE | BMCR_ANRESTART); + + return 0; +} + +/** + * yt8521_fiber_config_aneg - restart auto-negotiation or write + * YTPHY_MISC_CONFIG_REG. + * @phydev: target phy_device struct + * + * NOTE:The caller must have taken the MDIO bus lock. + * + * returns 0 or negative errno code + */ +static int yt8521_fiber_config_aneg(struct phy_device *phydev) +{ + int err, changed = 0; + int bmcr; + u16 adv; + + if (phydev->autoneg != AUTONEG_ENABLE) + return yt8521_fiber_setup_forced(phydev); + + /* enable Fiber auto sensing */ + err = ytphy_modify_ext(phydev, YT8521_LINK_TIMER_CFG2_REG, + 0, YT8521_LTCR_EN_AUTOSEN); + if (err < 0) + return err; + + err = ytphy_modify_ext(phydev, YT8521_CHIP_CONFIG_REG, + YT8521_CCR_SW_RST, 0); + if (err < 0) + return err; + + bmcr = __phy_read(phydev, MII_BMCR); + if (bmcr < 0) + return bmcr; + + /* When it is coming from fiber forced mode, add bmcr power down + * and power up to let aneg work fine. + */ + if (!(bmcr & BMCR_ANENABLE)) { + __phy_modify(phydev, MII_BMCR, 0, BMCR_PDOWN); + usleep_range(1000, 1100); + __phy_modify(phydev, MII_BMCR, BMCR_PDOWN, 0); + } + + adv = linkmode_adv_to_mii_adv_x(phydev->advertising, + ETHTOOL_LINK_MODE_1000baseX_Full_BIT); + + /* Setup fiber advertisement */ + err = __phy_modify_changed(phydev, MII_ADVERTISE, + ADVERTISE_1000XHALF | ADVERTISE_1000XFULL | + ADVERTISE_1000XPAUSE | + ADVERTISE_1000XPSE_ASYM, + adv); + if (err < 0) + return err; + + if (err > 0) + changed = 1; + + return ytphy_check_and_restart_aneg(phydev, changed); +} + +/** + * ytphy_setup_master_slave + * @phydev: target phy_device struct + * + * NOTE: The caller must have taken the MDIO bus lock. + * + * returns 0 or negative errno code + */ +static int ytphy_setup_master_slave(struct phy_device *phydev) +{ + u16 ctl = 0; + + if (!phydev->is_gigabit_capable) + return 0; + + switch (phydev->master_slave_set) { + case MASTER_SLAVE_CFG_MASTER_PREFERRED: + ctl |= CTL1000_PREFER_MASTER; + break; + case MASTER_SLAVE_CFG_SLAVE_PREFERRED: + break; + case MASTER_SLAVE_CFG_MASTER_FORCE: + ctl |= CTL1000_AS_MASTER; + fallthrough; + case MASTER_SLAVE_CFG_SLAVE_FORCE: + ctl |= CTL1000_ENABLE_MASTER; + break; + case MASTER_SLAVE_CFG_UNKNOWN: + case MASTER_SLAVE_CFG_UNSUPPORTED: + return 0; + default: + phydev_warn(phydev, "Unsupported Master/Slave mode\n"); + return -EOPNOTSUPP; + } + + return __phy_modify_changed(phydev, MII_CTRL1000, + (CTL1000_ENABLE_MASTER | CTL1000_AS_MASTER | + CTL1000_PREFER_MASTER), ctl); +} + +/** + * ytphy_utp_config_advert - sanitize and advertise auto-negotiation parameters + * @phydev: target phy_device struct + * + * NOTE: Writes MII_ADVERTISE with the appropriate values, + * after sanitizing the values to make sure we only advertise + * what is supported. Returns < 0 on error, 0 if the PHY's advertisement + * hasn't changed, and > 0 if it has changed. + * The caller must have taken the MDIO bus lock. + * + * returns 0 or negative errno code + */ +static int ytphy_utp_config_advert(struct phy_device *phydev) +{ + int err, bmsr, changed = 0; + u32 adv; + + /* Only allow advertising what this PHY supports */ + linkmode_and(phydev->advertising, phydev->advertising, + phydev->supported); + + adv = linkmode_adv_to_mii_adv_t(phydev->advertising); + + /* Setup standard advertisement */ + err = __phy_modify_changed(phydev, MII_ADVERTISE, + ADVERTISE_ALL | ADVERTISE_100BASE4 | + ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM, + adv); + if (err < 0) + return err; + if (err > 0) + changed = 1; + + bmsr = __phy_read(phydev, MII_BMSR); + if (bmsr < 0) + return bmsr; + + /* Per 802.3-2008, Section 22.2.4.2.16 Extended status all + * 1000Mbits/sec capable PHYs shall have the BMSR_ESTATEN bit set to a + * logical 1. + */ + if (!(bmsr & BMSR_ESTATEN)) + return changed; + + adv = linkmode_adv_to_mii_ctrl1000_t(phydev->advertising); + + err = __phy_modify_changed(phydev, MII_CTRL1000, + ADVERTISE_1000FULL | ADVERTISE_1000HALF, + adv); + if (err < 0) + return err; + if (err > 0) + changed = 1; + + return changed; +} + +/** + * ytphy_utp_config_aneg - restart auto-negotiation or write BMCR + * @phydev: target phy_device struct + * @changed: whether autoneg is requested + * + * NOTE: If auto-negotiation is enabled, we configure the + * advertising, and then restart auto-negotiation. If it is not + * enabled, then we write the BMCR. + * The caller must have taken the MDIO bus lock. + * + * returns 0 or negative errno code + */ +static int ytphy_utp_config_aneg(struct phy_device *phydev, bool changed) +{ + int err; + u16 ctl; + + err = ytphy_setup_master_slave(phydev); + if (err < 0) + return err; + else if (err) + changed = true; + + if (phydev->autoneg != AUTONEG_ENABLE) { + /* configures/forces speed/duplex from @phydev */ + + ctl = mii_bmcr_encode_fixed(phydev->speed, phydev->duplex); + + return __phy_modify(phydev, MII_BMCR, ~(BMCR_LOOPBACK | + BMCR_ISOLATE | BMCR_PDOWN), ctl); + } + + err = ytphy_utp_config_advert(phydev); + if (err < 0) /* error */ + return err; + else if (err) + changed = true; + + return ytphy_check_and_restart_aneg(phydev, changed); +} + +/** + * yt8521_config_aneg_paged() - switch reg space then call genphy_config_aneg + * of one page + * @phydev: a pointer to a &struct phy_device + * @page: The reg page(YT8521_RSSR_FIBER_SPACE/YT8521_RSSR_UTP_SPACE) to + * operate. + * + * returns 0 or negative errno code + */ +static int yt8521_config_aneg_paged(struct phy_device *phydev, int page) +{ + __ETHTOOL_DECLARE_LINK_MODE_MASK(fiber_supported); + struct yt8521_priv *priv = phydev->priv; + int old_page; + int ret = 0; + + page &= YT8521_RSSR_SPACE_MASK; + + old_page = phy_select_page(phydev, page); + if (old_page < 0) + goto err_restore_page; + + /* If reg_page is YT8521_RSSR_TO_BE_ARBITRATED, + * phydev->advertising should be updated. + */ + if (priv->reg_page == YT8521_RSSR_TO_BE_ARBITRATED) { + linkmode_zero(fiber_supported); + yt8521_prepare_fiber_features(phydev, fiber_supported); + + /* prepare fiber_supported, then setup advertising. */ + if (page == YT8521_RSSR_FIBER_SPACE) { + linkmode_set_bit(ETHTOOL_LINK_MODE_Pause_BIT, + fiber_supported); + linkmode_set_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT, + fiber_supported); + linkmode_and(phydev->advertising, + priv->combo_advertising, fiber_supported); + } else { + /* ETHTOOL_LINK_MODE_Autoneg_BIT is also used in utp */ + linkmode_clear_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, + fiber_supported); + linkmode_andnot(phydev->advertising, + priv->combo_advertising, + fiber_supported); + } + } + + if (page == YT8521_RSSR_FIBER_SPACE) + ret = yt8521_fiber_config_aneg(phydev); + else + ret = ytphy_utp_config_aneg(phydev, false); + +err_restore_page: + return phy_restore_page(phydev, old_page, ret); +} + +/** + * yt8521_config_aneg() - change reg space then call yt8521_config_aneg_paged + * @phydev: a pointer to a &struct phy_device + * + * returns 0 or negative errno code + */ +static int yt8521_config_aneg(struct phy_device *phydev) +{ + struct yt8521_priv *priv = phydev->priv; + int ret; + + if (priv->reg_page != YT8521_RSSR_TO_BE_ARBITRATED) { + ret = yt8521_config_aneg_paged(phydev, priv->reg_page); + if (ret < 0) + return ret; + } else { + /* If reg_page is YT8521_RSSR_TO_BE_ARBITRATED, + * phydev->advertising need to be saved at first run. + * Because it contains the advertising which supported by both + * mac and yt8521(utp and fiber). + */ + if (linkmode_empty(priv->combo_advertising)) { + linkmode_copy(priv->combo_advertising, + phydev->advertising); + } + + ret = yt8521_config_aneg_paged(phydev, YT8521_RSSR_UTP_SPACE); + if (ret < 0) + return ret; + + ret = yt8521_config_aneg_paged(phydev, YT8521_RSSR_FIBER_SPACE); + if (ret < 0) + return ret; + + /* we don't known which will be link, so restore + * phydev->advertising as default value. + */ + linkmode_copy(phydev->advertising, priv->combo_advertising); + } + return 0; +} + +/** + * yt8521_aneg_done_paged() - determines the auto negotiation result of one + * page. + * @phydev: a pointer to a &struct phy_device + * @page: The reg page(YT8521_RSSR_FIBER_SPACE/YT8521_RSSR_UTP_SPACE) to + * operate. + * + * returns 0(no link)or 1(fiber or utp link) or negative errno code + */ +static int yt8521_aneg_done_paged(struct phy_device *phydev, int page) +{ + int old_page; + int ret = 0; + int link; + + old_page = phy_select_page(phydev, page & YT8521_RSSR_SPACE_MASK); + if (old_page < 0) + goto err_restore_page; + + ret = __phy_read(phydev, YTPHY_SPECIFIC_STATUS_REG); + if (ret < 0) + goto err_restore_page; + + link = !!(ret & YTPHY_SSR_LINK); + ret = link; + +err_restore_page: + return phy_restore_page(phydev, old_page, ret); +} + +/** + * yt8521_aneg_done() - determines the auto negotiation result + * @phydev: a pointer to a &struct phy_device + * + * returns 0(no link)or 1(fiber or utp link) or negative errno code + */ +static int yt8521_aneg_done(struct phy_device *phydev) +{ + struct yt8521_priv *priv = phydev->priv; + int link_fiber = 0; + int link_utp; + int link; + + if (priv->reg_page != YT8521_RSSR_TO_BE_ARBITRATED) { + link = yt8521_aneg_done_paged(phydev, priv->reg_page); + } else { + link_utp = yt8521_aneg_done_paged(phydev, + YT8521_RSSR_UTP_SPACE); + if (link_utp < 0) + return link_utp; + + if (!link_utp) { + link_fiber = yt8521_aneg_done_paged(phydev, + YT8521_RSSR_FIBER_SPACE); + if (link_fiber < 0) + return link_fiber; + } + link = link_fiber || link_utp; + phydev_info(phydev, "%s, link_fiber: %d, link_utp: %d\n", + __func__, link_fiber, link_utp); + } + + return link; +} + +/** + * ytphy_utp_read_abilities - read PHY abilities from Clause 22 registers + * @phydev: target phy_device struct + * + * NOTE: Reads the PHY's abilities and populates + * phydev->supported accordingly. + * The caller must have taken the MDIO bus lock. + * + * returns 0 or negative errno code + */ +static int ytphy_utp_read_abilities(struct phy_device *phydev) +{ + int val; + + linkmode_set_bit_array(phy_basic_ports_array, + ARRAY_SIZE(phy_basic_ports_array), + phydev->supported); + + val = __phy_read(phydev, MII_BMSR); + if (val < 0) + return val; + + linkmode_mod_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, phydev->supported, + val & BMSR_ANEGCAPABLE); + + linkmode_mod_bit(ETHTOOL_LINK_MODE_100baseT_Full_BIT, phydev->supported, + val & BMSR_100FULL); + linkmode_mod_bit(ETHTOOL_LINK_MODE_100baseT_Half_BIT, phydev->supported, + val & BMSR_100HALF); + linkmode_mod_bit(ETHTOOL_LINK_MODE_10baseT_Full_BIT, phydev->supported, + val & BMSR_10FULL); + linkmode_mod_bit(ETHTOOL_LINK_MODE_10baseT_Half_BIT, phydev->supported, + val & BMSR_10HALF); + + if (val & BMSR_ESTATEN) { + val = __phy_read(phydev, MII_ESTATUS); + if (val < 0) + return val; + + linkmode_mod_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT, + phydev->supported, val & ESTATUS_1000_TFULL); + linkmode_mod_bit(ETHTOOL_LINK_MODE_1000baseT_Half_BIT, + phydev->supported, val & ESTATUS_1000_THALF); + linkmode_mod_bit(ETHTOOL_LINK_MODE_1000baseX_Full_BIT, + phydev->supported, val & ESTATUS_1000_XFULL); + } + + return 0; +} + +/** + * yt8521_get_features_paged() - read supported link modes for one page + * @phydev: a pointer to a &struct phy_device + * @page: The reg page(YT8521_RSSR_FIBER_SPACE/YT8521_RSSR_UTP_SPACE) to + * operate. + * + * returns 0 or negative errno code + */ +static int yt8521_get_features_paged(struct phy_device *phydev, int page) +{ + int old_page; + int ret = 0; + + page &= YT8521_RSSR_SPACE_MASK; + old_page = phy_select_page(phydev, page); + if (old_page < 0) + goto err_restore_page; + + if (page == YT8521_RSSR_FIBER_SPACE) { + linkmode_zero(phydev->supported); + yt8521_prepare_fiber_features(phydev, phydev->supported); + } else { + ret = ytphy_utp_read_abilities(phydev); + if (ret < 0) + goto err_restore_page; + } + +err_restore_page: + return phy_restore_page(phydev, old_page, ret); +} + +/** + * yt8521_get_features - switch reg space then call yt8521_get_features_paged + * @phydev: target phy_device struct + * + * returns 0 or negative errno code + */ +static int yt8521_get_features(struct phy_device *phydev) +{ + struct yt8521_priv *priv = phydev->priv; + int ret; + + if (priv->reg_page != YT8521_RSSR_TO_BE_ARBITRATED) { + ret = yt8521_get_features_paged(phydev, priv->reg_page); + } else { + ret = yt8521_get_features_paged(phydev, + YT8521_RSSR_UTP_SPACE); + if (ret < 0) + return ret; + + /* add fiber's features to phydev->supported */ + yt8521_prepare_fiber_features(phydev, phydev->supported); + } + return ret; +} + static struct phy_driver motorcomm_phy_drvs[] = { { PHY_ID_MATCH_EXACT(PHY_ID_YT8511), @@ -121,16 +1733,35 @@ static struct phy_driver motorcomm_phy_drvs[] = { .read_page = yt8511_read_page, .write_page = yt8511_write_page, }, + { + PHY_ID_MATCH_EXACT(PHY_ID_YT8521), + .name = "YT8521 Gigabit Ethernet", + .get_features = yt8521_get_features, + .probe = yt8521_probe, + .read_page = yt8521_read_page, + .write_page = yt8521_write_page, + .get_wol = ytphy_get_wol, + .set_wol = ytphy_set_wol, + .config_aneg = yt8521_config_aneg, + .aneg_done = yt8521_aneg_done, + .config_init = yt8521_config_init, + .read_status = yt8521_read_status, + .soft_reset = yt8521_soft_reset, + .suspend = yt8521_suspend, + .resume = yt8521_resume, + }, }; module_phy_driver(motorcomm_phy_drvs); -MODULE_DESCRIPTION("Motorcomm PHY driver"); +MODULE_DESCRIPTION("Motorcomm 8511/8521 PHY driver"); MODULE_AUTHOR("Peter Geis"); +MODULE_AUTHOR("Frank"); MODULE_LICENSE("GPL"); static const struct mdio_device_id __maybe_unused motorcomm_tbl[] = { { PHY_ID_MATCH_EXACT(PHY_ID_YT8511) }, + { PHY_ID_MATCH_EXACT(PHY_ID_YT8521) }, { /* sentinal */ } }; From b9a61b97798ca6d0c856a217e61e2177bd8f6eae Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Fri, 28 Oct 2022 04:04:12 -0700 Subject: [PATCH 0477/2002] ptp: add missing documentation for parameters The ptp_find_pin_unlocked function and the ptp_system_timestamp structure didn't document their parameters and fields. Fix this. Signed-off-by: Jacob Keller Acked-by: Richard Cochran Signed-off-by: David S. Miller --- include/linux/ptp_clock_kernel.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/include/linux/ptp_clock_kernel.h b/include/linux/ptp_clock_kernel.h index 92b44161408e..ad4aaadc2f7a 100644 --- a/include/linux/ptp_clock_kernel.h +++ b/include/linux/ptp_clock_kernel.h @@ -45,6 +45,8 @@ struct system_device_crosststamp; /** * struct ptp_system_timestamp - system time corresponding to a PHC timestamp + * @pre_ts: system timestamp before capturing PHC + * @post_ts: system timestamp after capturing PHC */ struct ptp_system_timestamp { struct timespec64 pre_ts; @@ -316,6 +318,11 @@ int ptp_find_pin(struct ptp_clock *ptp, * should most likely call ptp_find_pin() directly from their * ptp_clock_info::enable() method. * +* @ptp: The clock obtained from ptp_clock_register(). +* @func: One of the ptp_pin_function enumerated values. +* @chan: The particular functional channel to find. +* Return: Pin index in the range of zero to ptp_clock_caps.n_pins - 1, +* or -1 if the auxiliary function cannot be found. */ int ptp_find_pin_unlocked(struct ptp_clock *ptp, From 1060707e380994e7c9b754b6eb74f25459b4a5b3 Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Fri, 28 Oct 2022 04:04:13 -0700 Subject: [PATCH 0478/2002] ptp: introduce helpers to adjust by scaled parts per million Many drivers implement the .adjfreq or .adjfine PTP op function with the same basic logic: 1. Determine a base frequency value 2. Multiply this by the abs() of the requested adjustment, then divide by the appropriate divisor (1 billion, or 65,536 billion). 3. Add or subtract this difference from the base frequency to calculate a new adjustment. A few drivers need the difference and direction rather than the combined new increment value. I recently converted the Intel drivers to .adjfine and the scaled parts per million (65.536 parts per billion) logic. To avoid overflow with minimal loss of precision, mul_u64_u64_div_u64 was used. The basic logic used by all of these drivers is very similar, and leads to a lot of duplicate code to perform the same task. Rather than keep this duplicate code, introduce diff_by_scaled_ppm and adjust_by_scaled_ppm. These helper functions calculate the difference or adjustment necessary based on the scaled parts per million input. The diff_by_scaled_ppm function returns true if the difference should be subtracted, and false otherwise. Update the Intel drivers to use the new helper functions. Other vendor drivers will be converted to .adjfine and this helper function in the following changes. Signed-off-by: Jacob Keller Acked-by: Richard Cochran Signed-off-by: David S. Miller --- drivers/net/ethernet/intel/e1000e/ptp.c | 16 ++----- drivers/net/ethernet/intel/i40e/i40e_ptp.c | 17 ++------ drivers/net/ethernet/intel/ice/ice_ptp.c | 18 +------- drivers/net/ethernet/intel/igb/igb_ptp.c | 18 +------- drivers/net/ethernet/intel/ixgbe/ixgbe_ptp.c | 24 ++-------- include/linux/ptp_clock_kernel.h | 46 ++++++++++++++++++++ 6 files changed, 60 insertions(+), 79 deletions(-) diff --git a/drivers/net/ethernet/intel/e1000e/ptp.c b/drivers/net/ethernet/intel/e1000e/ptp.c index 0e488e4fa5c1..6e5a1720e6cd 100644 --- a/drivers/net/ethernet/intel/e1000e/ptp.c +++ b/drivers/net/ethernet/intel/e1000e/ptp.c @@ -29,17 +29,11 @@ static int e1000e_phc_adjfine(struct ptp_clock_info *ptp, long delta) struct e1000_adapter *adapter = container_of(ptp, struct e1000_adapter, ptp_clock_info); struct e1000_hw *hw = &adapter->hw; - bool neg_adj = false; unsigned long flags; - u64 adjustment; - u32 timinca, incvalue; + u64 incvalue; + u32 timinca; s32 ret_val; - if (delta < 0) { - neg_adj = true; - delta = -delta; - } - /* Get the System Time Register SYSTIM base frequency */ ret_val = e1000e_get_base_timinca(adapter, &timinca); if (ret_val) @@ -48,11 +42,7 @@ static int e1000e_phc_adjfine(struct ptp_clock_info *ptp, long delta) spin_lock_irqsave(&adapter->systim_lock, flags); incvalue = timinca & E1000_TIMINCA_INCVALUE_MASK; - - adjustment = mul_u64_u64_div_u64(incvalue, (u64)delta, - 1000000ULL << 16); - - incvalue = neg_adj ? (incvalue - adjustment) : (incvalue + adjustment); + incvalue = adjust_by_scaled_ppm(incvalue, delta); timinca &= ~E1000_TIMINCA_INCVALUE_MASK; timinca |= incvalue; diff --git a/drivers/net/ethernet/intel/i40e/i40e_ptp.c b/drivers/net/ethernet/intel/i40e/i40e_ptp.c index ffea0c9c82f1..c37abbb3cd06 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_ptp.c +++ b/drivers/net/ethernet/intel/i40e/i40e_ptp.c @@ -347,23 +347,12 @@ static int i40e_ptp_adjfine(struct ptp_clock_info *ptp, long scaled_ppm) { struct i40e_pf *pf = container_of(ptp, struct i40e_pf, ptp_caps); struct i40e_hw *hw = &pf->hw; - u64 adj, freq, diff; - int neg_adj = 0; - - if (scaled_ppm < 0) { - neg_adj = 1; - scaled_ppm = -scaled_ppm; - } + u64 adj, base_adj; smp_mb(); /* Force any pending update before accessing. */ - freq = I40E_PTP_40GB_INCVAL * READ_ONCE(pf->ptp_adj_mult); - diff = mul_u64_u64_div_u64(freq, (u64)scaled_ppm, - 1000000ULL << 16); + base_adj = I40E_PTP_40GB_INCVAL * READ_ONCE(pf->ptp_adj_mult); - if (neg_adj) - adj = I40E_PTP_40GB_INCVAL - diff; - else - adj = I40E_PTP_40GB_INCVAL + diff; + adj = adjust_by_scaled_ppm(base_adj, scaled_ppm); wr32(hw, I40E_PRTTSYN_INC_L, adj & 0xFFFFFFFF); wr32(hw, I40E_PRTTSYN_INC_H, adj >> 32); diff --git a/drivers/net/ethernet/intel/ice/ice_ptp.c b/drivers/net/ethernet/intel/ice/ice_ptp.c index 011b727ab190..5cf198a33e26 100644 --- a/drivers/net/ethernet/intel/ice/ice_ptp.c +++ b/drivers/net/ethernet/intel/ice/ice_ptp.c @@ -1444,24 +1444,10 @@ static int ice_ptp_adjfine(struct ptp_clock_info *info, long scaled_ppm) { struct ice_pf *pf = ptp_info_to_pf(info); struct ice_hw *hw = &pf->hw; - u64 incval, diff; - int neg_adj = 0; + u64 incval; int err; - incval = ice_base_incval(pf); - - if (scaled_ppm < 0) { - neg_adj = 1; - scaled_ppm = -scaled_ppm; - } - - diff = mul_u64_u64_div_u64(incval, (u64)scaled_ppm, - 1000000ULL << 16); - if (neg_adj) - incval -= diff; - else - incval += diff; - + incval = adjust_by_scaled_ppm(ice_base_incval(pf), scaled_ppm); err = ice_ptp_write_incval_locked(hw, incval); if (err) { dev_err(ice_pf_to_dev(pf), "PTP failed to set incval, err %d\n", diff --git a/drivers/net/ethernet/intel/igb/igb_ptp.c b/drivers/net/ethernet/intel/igb/igb_ptp.c index 15e57460e19e..6f471b91f562 100644 --- a/drivers/net/ethernet/intel/igb/igb_ptp.c +++ b/drivers/net/ethernet/intel/igb/igb_ptp.c @@ -195,23 +195,9 @@ static int igb_ptp_adjfine_82576(struct ptp_clock_info *ptp, long scaled_ppm) struct igb_adapter *igb = container_of(ptp, struct igb_adapter, ptp_caps); struct e1000_hw *hw = &igb->hw; - int neg_adj = 0; - u64 rate; - u32 incvalue; + u64 incvalue; - if (scaled_ppm < 0) { - neg_adj = 1; - scaled_ppm = -scaled_ppm; - } - - incvalue = INCVALUE_82576; - rate = mul_u64_u64_div_u64(incvalue, (u64)scaled_ppm, - 1000000ULL << 16); - - if (neg_adj) - incvalue -= rate; - else - incvalue += rate; + incvalue = adjust_by_scaled_ppm(INCVALUE_82576, scaled_ppm); wr32(E1000_TIMINCA, INCPERIOD_82576 | (incvalue & INCVALUE_82576_MASK)); diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_ptp.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_ptp.c index f8605f57bd06..fd3f77a9e28d 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_ptp.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_ptp.c @@ -451,21 +451,11 @@ static int ixgbe_ptp_adjfine_82599(struct ptp_clock_info *ptp, long scaled_ppm) struct ixgbe_adapter *adapter = container_of(ptp, struct ixgbe_adapter, ptp_caps); struct ixgbe_hw *hw = &adapter->hw; - u64 incval, diff; - int neg_adj = 0; - - if (scaled_ppm < 0) { - neg_adj = 1; - scaled_ppm = -scaled_ppm; - } + u64 incval; smp_mb(); incval = READ_ONCE(adapter->base_incval); - - diff = mul_u64_u64_div_u64(incval, scaled_ppm, - 1000000ULL << 16); - - incval = neg_adj ? (incval - diff) : (incval + diff); + incval = adjust_by_scaled_ppm(incval, scaled_ppm); switch (hw->mac.type) { case ixgbe_mac_X540: @@ -502,17 +492,11 @@ static int ixgbe_ptp_adjfine_X550(struct ptp_clock_info *ptp, long scaled_ppm) struct ixgbe_adapter *adapter = container_of(ptp, struct ixgbe_adapter, ptp_caps); struct ixgbe_hw *hw = &adapter->hw; - int neg_adj = 0; + bool neg_adj; u64 rate; u32 inca; - if (scaled_ppm < 0) { - neg_adj = 1; - scaled_ppm = -scaled_ppm; - } - - rate = mul_u64_u64_div_u64(IXGBE_X550_BASE_PERIOD, scaled_ppm, - 1000000ULL << 16); + neg_adj = diff_by_scaled_ppm(IXGBE_X550_BASE_PERIOD, scaled_ppm, &rate); /* warn if rate is too large */ if (rate >= INCVALUE_MASK) diff --git a/include/linux/ptp_clock_kernel.h b/include/linux/ptp_clock_kernel.h index ad4aaadc2f7a..f4781c5766d6 100644 --- a/include/linux/ptp_clock_kernel.h +++ b/include/linux/ptp_clock_kernel.h @@ -248,6 +248,52 @@ static inline long scaled_ppm_to_ppb(long ppm) return (long)ppb; } +/** + * diff_by_scaled_ppm - Calculate difference using scaled ppm + * @base: the base increment value to adjust + * @scaled_ppm: scaled parts per million to adjust by + * @diff: on return, the absolute value of calculated diff + * + * Calculate the difference to adjust the base increment using scaled parts + * per million. + * + * Use mul_u64_u64_div_u64 to perform the difference calculation in avoid + * possible overflow. + * + * Returns: true if scaled_ppm is negative, false otherwise + */ +static inline bool diff_by_scaled_ppm(u64 base, long scaled_ppm, u64 *diff) +{ + bool negative = false; + + if (scaled_ppm < 0) { + negative = true; + scaled_ppm = -scaled_ppm; + } + + *diff = mul_u64_u64_div_u64(base, (u64)scaled_ppm, 1000000ULL << 16); + + return negative; +} + +/** + * adjust_by_scaled_ppm - Adjust a base increment by scaled parts per million + * @base: the base increment value to adjust + * @scaled_ppm: scaled parts per million frequency adjustment + * + * Helper function which calculates a new increment value based on the + * requested scaled parts per million adjustment. + */ +static inline u64 adjust_by_scaled_ppm(u64 base, long scaled_ppm) +{ + u64 diff; + + if (diff_by_scaled_ppm(base, scaled_ppm, &diff)) + return base - diff; + + return base + diff; +} + #if IS_ENABLED(CONFIG_PTP_1588_CLOCK) /** From 73aa29a2b119619bbc1db87e8a8103f4b7e5a5db Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Fri, 28 Oct 2022 04:04:14 -0700 Subject: [PATCH 0479/2002] drivers: convert unsupported .adjfreq to .adjfine A few PTP drivers implement a .adjfreq handler which indicates the operation is not supported. Convert all of these to .adjfine. Signed-off-by: Jacob Keller Acked-by: Richard Cochran Cc: "K. Y. Srinivasan" Cc: Haiyang Zhang Cc: Stephen Hemminger Cc: Wei Liu Cc: Dexuan Cui Cc: Vivek Thampi Signed-off-by: David S. Miller --- drivers/hv/hv_util.c | 4 ++-- drivers/ptp/ptp_kvm_common.c | 4 ++-- drivers/ptp/ptp_vmw.c | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/hv/hv_util.c b/drivers/hv/hv_util.c index 835e6039c186..d776074b49cb 100644 --- a/drivers/hv/hv_util.c +++ b/drivers/hv/hv_util.c @@ -706,7 +706,7 @@ static int hv_ptp_settime(struct ptp_clock_info *p, const struct timespec64 *ts) return -EOPNOTSUPP; } -static int hv_ptp_adjfreq(struct ptp_clock_info *ptp, s32 delta) +static int hv_ptp_adjfine(struct ptp_clock_info *ptp, long delta) { return -EOPNOTSUPP; } @@ -724,7 +724,7 @@ static struct ptp_clock_info ptp_hyperv_info = { .name = "hyperv", .enable = hv_ptp_enable, .adjtime = hv_ptp_adjtime, - .adjfreq = hv_ptp_adjfreq, + .adjfine = hv_ptp_adjfine, .gettime64 = hv_ptp_gettime, .settime64 = hv_ptp_settime, .owner = THIS_MODULE, diff --git a/drivers/ptp/ptp_kvm_common.c b/drivers/ptp/ptp_kvm_common.c index fcae32f56f25..9141162c4237 100644 --- a/drivers/ptp/ptp_kvm_common.c +++ b/drivers/ptp/ptp_kvm_common.c @@ -66,7 +66,7 @@ static int ptp_kvm_getcrosststamp(struct ptp_clock_info *ptp, * PTP clock operations */ -static int ptp_kvm_adjfreq(struct ptp_clock_info *ptp, s32 ppb) +static int ptp_kvm_adjfine(struct ptp_clock_info *ptp, long delta) { return -EOPNOTSUPP; } @@ -115,7 +115,7 @@ static const struct ptp_clock_info ptp_kvm_caps = { .n_ext_ts = 0, .n_pins = 0, .pps = 0, - .adjfreq = ptp_kvm_adjfreq, + .adjfine = ptp_kvm_adjfine, .adjtime = ptp_kvm_adjtime, .gettime64 = ptp_kvm_gettime, .settime64 = ptp_kvm_settime, diff --git a/drivers/ptp/ptp_vmw.c b/drivers/ptp/ptp_vmw.c index 5dca26e14bdc..d64eec5b1788 100644 --- a/drivers/ptp/ptp_vmw.c +++ b/drivers/ptp/ptp_vmw.c @@ -47,7 +47,7 @@ static int ptp_vmw_adjtime(struct ptp_clock_info *info, s64 delta) return -EOPNOTSUPP; } -static int ptp_vmw_adjfreq(struct ptp_clock_info *info, s32 delta) +static int ptp_vmw_adjfine(struct ptp_clock_info *info, long delta) { return -EOPNOTSUPP; } @@ -79,7 +79,7 @@ static struct ptp_clock_info ptp_vmw_clock_info = { .name = "ptp_vmw", .max_adj = 0, .adjtime = ptp_vmw_adjtime, - .adjfreq = ptp_vmw_adjfreq, + .adjfine = ptp_vmw_adjfine, .gettime64 = ptp_vmw_gettime, .settime64 = ptp_vmw_settime, .enable = ptp_vmw_enable, From 6ed795965ede203d762f59b791e3490eb6f74b13 Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Fri, 28 Oct 2022 04:04:15 -0700 Subject: [PATCH 0480/2002] ptp: mlx4: convert to .adjfine and adjust_by_scaled_ppm The mlx4 implementation of .adjfreq is implemented in terms of a straight forward "base * ppb / 1 billion" calculation. Convert this driver to .adjfine and use adjust_by_scaled_ppm to perform the calculation. Signed-off-by: Jacob Keller Acked-by: Richard Cochran Cc: Tariq Toukan Reviewed-by: Tariq Toukan Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx4/en_clock.c | 29 +++++++------------ 1 file changed, 11 insertions(+), 18 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx4/en_clock.c b/drivers/net/ethernet/mellanox/mlx4/en_clock.c index 024788549c25..98b5ffb4d729 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_clock.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_clock.c @@ -111,34 +111,27 @@ void mlx4_en_ptp_overflow_check(struct mlx4_en_dev *mdev) } /** - * mlx4_en_phc_adjfreq - adjust the frequency of the hardware clock + * mlx4_en_phc_adjfine - adjust the frequency of the hardware clock * @ptp: ptp clock structure - * @delta: Desired frequency change in parts per billion + * @scaled_ppm: Desired frequency change in scaled parts per million * - * Adjust the frequency of the PHC cycle counter by the indicated delta from - * the base frequency. + * Adjust the frequency of the PHC cycle counter by the indicated scaled_ppm + * from the base frequency. + * + * Scaled parts per million is ppm with a 16-bit binary fractional field. **/ -static int mlx4_en_phc_adjfreq(struct ptp_clock_info *ptp, s32 delta) +static int mlx4_en_phc_adjfine(struct ptp_clock_info *ptp, long scaled_ppm) { - u64 adj; - u32 diff, mult; - int neg_adj = 0; + u32 mult; unsigned long flags; struct mlx4_en_dev *mdev = container_of(ptp, struct mlx4_en_dev, ptp_clock_info); - if (delta < 0) { - neg_adj = 1; - delta = -delta; - } - mult = mdev->nominal_c_mult; - adj = mult; - adj *= delta; - diff = div_u64(adj, 1000000000ULL); + mult = (u32)adjust_by_scaled_ppm(mdev->nominal_c_mult, scaled_ppm); write_seqlock_irqsave(&mdev->clock_lock, flags); timecounter_read(&mdev->clock); - mdev->cycles.mult = neg_adj ? mult - diff : mult + diff; + mdev->cycles.mult = mult; write_sequnlock_irqrestore(&mdev->clock_lock, flags); return 0; @@ -237,7 +230,7 @@ static const struct ptp_clock_info mlx4_en_ptp_clock_info = { .n_per_out = 0, .n_pins = 0, .pps = 0, - .adjfreq = mlx4_en_phc_adjfreq, + .adjfine = mlx4_en_phc_adjfine, .adjtime = mlx4_en_phc_adjtime, .gettime64 = mlx4_en_phc_gettime, .settime64 = mlx4_en_phc_settime, From d8aad3f3694f1170fb4ea8030e3d8a27324e81d2 Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Fri, 28 Oct 2022 04:04:16 -0700 Subject: [PATCH 0481/2002] ptp: mlx5: convert to .adjfine and adjust_by_scaled_ppm The mlx5 implementation of .adjfreq is implemented in terms of a straight forward "base * ppb / 1 billion" calculation. Convert this to the .adjfine interface and use adjust_by_scaled_ppm for the calculation of the new mult value. Note that the mlx5_ptp_adjfreq_real_time function expects input in terms of ppb, so use the scaled_ppm_to_ppb to convert before passing to this function. Signed-off-by: Jacob Keller Tested-by: Shirly Ohnona Acked-by: Richard Cochran Cc: Gal Pressman Cc: Saeed Mahameed Cc: Leon Romanovsky Cc: Aya Levin Signed-off-by: David S. Miller --- .../ethernet/mellanox/mlx5/core/lib/clock.c | 22 +++++-------------- 1 file changed, 6 insertions(+), 16 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lib/clock.c b/drivers/net/ethernet/mellanox/mlx5/core/lib/clock.c index d3a9ae80fd30..69cfe60c558a 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/lib/clock.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/lib/clock.c @@ -339,35 +339,25 @@ static int mlx5_ptp_adjfreq_real_time(struct mlx5_core_dev *mdev, s32 freq) return mlx5_set_mtutc(mdev, in, sizeof(in)); } -static int mlx5_ptp_adjfreq(struct ptp_clock_info *ptp, s32 delta) +static int mlx5_ptp_adjfine(struct ptp_clock_info *ptp, long scaled_ppm) { struct mlx5_clock *clock = container_of(ptp, struct mlx5_clock, ptp_info); struct mlx5_timer *timer = &clock->timer; struct mlx5_core_dev *mdev; unsigned long flags; - int neg_adj = 0; - u32 diff; - u64 adj; + u32 mult; int err; mdev = container_of(clock, struct mlx5_core_dev, clock); - err = mlx5_ptp_adjfreq_real_time(mdev, delta); + err = mlx5_ptp_adjfreq_real_time(mdev, scaled_ppm_to_ppb(scaled_ppm)); if (err) return err; - if (delta < 0) { - neg_adj = 1; - delta = -delta; - } - - adj = timer->nominal_c_mult; - adj *= delta; - diff = div_u64(adj, 1000000000ULL); + mult = (u32)adjust_by_scaled_ppm(timer->nominal_c_mult, scaled_ppm); write_seqlock_irqsave(&clock->lock, flags); timecounter_read(&timer->tc); - timer->cycles.mult = neg_adj ? timer->nominal_c_mult - diff : - timer->nominal_c_mult + diff; + timer->cycles.mult = mult; mlx5_update_clock_info_page(mdev); write_sequnlock_irqrestore(&clock->lock, flags); @@ -697,7 +687,7 @@ static const struct ptp_clock_info mlx5_ptp_clock_info = { .n_per_out = 0, .n_pins = 0, .pps = 0, - .adjfreq = mlx5_ptp_adjfreq, + .adjfine = mlx5_ptp_adjfine, .adjtime = mlx5_ptp_adjtime, .gettimex64 = mlx5_ptp_gettimex, .settime64 = mlx5_ptp_settime, From c56dff6a9a315703c7f94eb0b9f682248b96ff0b Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Fri, 28 Oct 2022 04:04:17 -0700 Subject: [PATCH 0482/2002] ptp: lan743x: remove .adjfreq implementation The lan743x driver implements both .adjfreq and .adjfine, but the core PTP subsystem prefers .adjfine if implemented. There is no reason to carry a .adjfreq implementation, so we can remove it. Signed-off-by: Jacob Keller Acked-by: Richard Cochran Cc: Bryan Whitehead Cc: UNGLinuxDriver@microchip.com Signed-off-by: David S. Miller --- drivers/net/ethernet/microchip/lan743x_ptp.c | 35 -------------------- 1 file changed, 35 deletions(-) diff --git a/drivers/net/ethernet/microchip/lan743x_ptp.c b/drivers/net/ethernet/microchip/lan743x_ptp.c index da3ea905adbb..28930f3c52c2 100644 --- a/drivers/net/ethernet/microchip/lan743x_ptp.c +++ b/drivers/net/ethernet/microchip/lan743x_ptp.c @@ -365,40 +365,6 @@ static int lan743x_ptpci_adjfine(struct ptp_clock_info *ptpci, long scaled_ppm) return 0; } -static int lan743x_ptpci_adjfreq(struct ptp_clock_info *ptpci, s32 delta_ppb) -{ - struct lan743x_ptp *ptp = - container_of(ptpci, struct lan743x_ptp, ptp_clock_info); - struct lan743x_adapter *adapter = - container_of(ptp, struct lan743x_adapter, ptp); - u32 lan743x_rate_adj = 0; - bool positive = true; - u32 u32_delta = 0; - u64 u64_delta = 0; - - if ((delta_ppb < (-LAN743X_PTP_MAX_FREQ_ADJ_IN_PPB)) || - delta_ppb > LAN743X_PTP_MAX_FREQ_ADJ_IN_PPB) { - return -EINVAL; - } - if (delta_ppb > 0) { - u32_delta = (u32)delta_ppb; - positive = true; - } else { - u32_delta = (u32)(-delta_ppb); - positive = false; - } - u64_delta = (((u64)u32_delta) << 35); - lan743x_rate_adj = div_u64(u64_delta, 1000000000); - - if (positive) - lan743x_rate_adj |= PTP_CLOCK_RATE_ADJ_DIR_; - - lan743x_csr_write(adapter, PTP_CLOCK_RATE_ADJ, - lan743x_rate_adj); - - return 0; -} - static int lan743x_ptpci_adjtime(struct ptp_clock_info *ptpci, s64 delta) { struct lan743x_ptp *ptp = @@ -1583,7 +1549,6 @@ int lan743x_ptp_open(struct lan743x_adapter *adapter) ptp->ptp_clock_info.pps = LAN743X_PTP_N_PPS; ptp->ptp_clock_info.pin_config = ptp->pin_config; ptp->ptp_clock_info.adjfine = lan743x_ptpci_adjfine; - ptp->ptp_clock_info.adjfreq = lan743x_ptpci_adjfreq; ptp->ptp_clock_info.adjtime = lan743x_ptpci_adjtime; ptp->ptp_clock_info.gettime64 = lan743x_ptpci_gettime64; ptp->ptp_clock_info.getcrosststamp = NULL; From 8bc900cbffa7725e8bbba21a99f6261e6bd61ef4 Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Fri, 28 Oct 2022 04:04:18 -0700 Subject: [PATCH 0483/2002] ptp: lan743x: use diff_by_scaled_ppm in .adjfine implementation Update the lan743x driver to use the recently added diff_by_scaled_ppm helper function. This reduces the amount of code required in lan743x_ptp.c driver file. Signed-off-by: Jacob Keller Acked-by: Richard Cochran Cc: Bryan Whitehead Cc: UNGLinuxDriver@microchip.com Signed-off-by: David S. Miller --- drivers/net/ethernet/microchip/lan743x_ptp.c | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/drivers/net/ethernet/microchip/lan743x_ptp.c b/drivers/net/ethernet/microchip/lan743x_ptp.c index 28930f3c52c2..39e1066ecd5f 100644 --- a/drivers/net/ethernet/microchip/lan743x_ptp.c +++ b/drivers/net/ethernet/microchip/lan743x_ptp.c @@ -339,25 +339,18 @@ static int lan743x_ptpci_adjfine(struct ptp_clock_info *ptpci, long scaled_ppm) struct lan743x_adapter *adapter = container_of(ptp, struct lan743x_adapter, ptp); u32 lan743x_rate_adj = 0; - bool positive = true; - u64 u64_delta = 0; + u64 u64_delta; if ((scaled_ppm < (-LAN743X_PTP_MAX_FINE_ADJ_IN_SCALED_PPM)) || scaled_ppm > LAN743X_PTP_MAX_FINE_ADJ_IN_SCALED_PPM) { return -EINVAL; } - if (scaled_ppm > 0) { - u64_delta = (u64)scaled_ppm; - positive = true; - } else { - u64_delta = (u64)(-scaled_ppm); - positive = false; - } - u64_delta = (u64_delta << 19); - lan743x_rate_adj = div_u64(u64_delta, 1000000); - if (positive) - lan743x_rate_adj |= PTP_CLOCK_RATE_ADJ_DIR_; + /* diff_by_scaled_ppm returns true if the difference is negative */ + if (diff_by_scaled_ppm(1ULL << 35, scaled_ppm, &u64_delta)) + lan743x_rate_adj = (u32)u64_delta; + else + lan743x_rate_adj = (u32)u64_delta | PTP_CLOCK_RATE_ADJ_DIR_; lan743x_csr_write(adapter, PTP_CLOCK_RATE_ADJ, lan743x_rate_adj); From 673dd2c78817f62a9e8c79fdcb0920815b4c95c8 Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Fri, 28 Oct 2022 04:04:19 -0700 Subject: [PATCH 0484/2002] ptp: ravb: convert to .adjfine and adjust_by_scaled_ppm The ravb implementation of .adjfreq is implemented in terms of a straight forward "base * ppb / 1 billion" calculation. Convert this driver to .adjfine and use the adjust_by_scaled_ppm helper function to calculate the new addend. Signed-off-by: Jacob Keller Acked-by: Richard Cochran Cc: Sergey Shtylyov Cc: Biju Das Cc: Phil Edworthy Cc: Lad Prabhakar Cc: linux-renesas-soc@vger.kernel.org Signed-off-by: David S. Miller --- drivers/net/ethernet/renesas/ravb_ptp.c | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/drivers/net/ethernet/renesas/ravb_ptp.c b/drivers/net/ethernet/renesas/ravb_ptp.c index 87c4306d66ec..6e4ef7af27bf 100644 --- a/drivers/net/ethernet/renesas/ravb_ptp.c +++ b/drivers/net/ethernet/renesas/ravb_ptp.c @@ -88,24 +88,17 @@ static int ravb_ptp_update_compare(struct ravb_private *priv, u32 ns) } /* PTP clock operations */ -static int ravb_ptp_adjfreq(struct ptp_clock_info *ptp, s32 ppb) +static int ravb_ptp_adjfine(struct ptp_clock_info *ptp, long scaled_ppm) { struct ravb_private *priv = container_of(ptp, struct ravb_private, ptp.info); struct net_device *ndev = priv->ndev; unsigned long flags; - u32 diff, addend; - bool neg_adj = false; + u32 addend; u32 gccr; - if (ppb < 0) { - neg_adj = true; - ppb = -ppb; - } - addend = priv->ptp.default_addend; - diff = div_u64((u64)addend * ppb, NSEC_PER_SEC); - - addend = neg_adj ? addend - diff : addend + diff; + addend = (u32)adjust_by_scaled_ppm(priv->ptp.default_addend, + scaled_ppm); spin_lock_irqsave(&priv->lock, flags); @@ -295,7 +288,7 @@ static const struct ptp_clock_info ravb_ptp_info = { .max_adj = 50000000, .n_ext_ts = N_EXT_TS, .n_per_out = N_PER_OUT, - .adjfreq = ravb_ptp_adjfreq, + .adjfine = ravb_ptp_adjfine, .adjtime = ravb_ptp_adjtime, .gettime64 = ravb_ptp_gettime64, .settime64 = ravb_ptp_settime64, From 337ffae0e4d6217c92c8622d3ed26e9c8d3d5f28 Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Fri, 28 Oct 2022 04:04:20 -0700 Subject: [PATCH 0485/2002] ptp: xgbe: convert to .adjfine and adjust_by_scaled_ppm The xgbe implementation of .adjfreq is implemented in terms of a straight forward "base * ppb / 1 billion" calculation. Convert this driver to .adjfine and use adjust_by_scaled_ppm to calculate the new addend value. Signed-off-by: Jacob Keller Acked-by: Richard Cochran Acked-by: Tom Lendacky Cc: Shyam Sundar S K Signed-off-by: David S. Miller --- drivers/net/ethernet/amd/xgbe/xgbe-ptp.c | 20 ++++---------------- 1 file changed, 4 insertions(+), 16 deletions(-) diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-ptp.c b/drivers/net/ethernet/amd/xgbe/xgbe-ptp.c index d06d260cf1e2..7051bd7cf6dc 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-ptp.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-ptp.c @@ -134,27 +134,15 @@ static u64 xgbe_cc_read(const struct cyclecounter *cc) return nsec; } -static int xgbe_adjfreq(struct ptp_clock_info *info, s32 delta) +static int xgbe_adjfine(struct ptp_clock_info *info, long scaled_ppm) { struct xgbe_prv_data *pdata = container_of(info, struct xgbe_prv_data, ptp_clock_info); unsigned long flags; - u64 adjust; - u32 addend, diff; - unsigned int neg_adjust = 0; + u64 addend; - if (delta < 0) { - neg_adjust = 1; - delta = -delta; - } - - adjust = pdata->tstamp_addend; - adjust *= delta; - diff = div_u64(adjust, 1000000000UL); - - addend = (neg_adjust) ? pdata->tstamp_addend - diff : - pdata->tstamp_addend + diff; + addend = adjust_by_scaled_ppm(pdata->tstamp_addend, scaled_ppm); spin_lock_irqsave(&pdata->tstamp_lock, flags); @@ -235,7 +223,7 @@ void xgbe_ptp_register(struct xgbe_prv_data *pdata) netdev_name(pdata->netdev)); info->owner = THIS_MODULE; info->max_adj = pdata->ptpclk_rate; - info->adjfreq = xgbe_adjfreq; + info->adjfine = xgbe_adjfine; info->adjtime = xgbe_adjtime; info->gettime64 = xgbe_gettime; info->settime64 = xgbe_settime; From 0cf9deb3005f552a3d125436fc8ccedd31a925a9 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Fri, 28 Oct 2022 13:36:24 +0100 Subject: [PATCH 0486/2002] net: mvneta: Remove unused variable i Variable i is just being incremented and it's never used anywhere else. The variable and the increment are redundant so remove it. Signed-off-by: Colin Ian King Signed-off-by: David S. Miller --- drivers/net/ethernet/marvell/mvneta.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/net/ethernet/marvell/mvneta.c b/drivers/net/ethernet/marvell/mvneta.c index 81dc57a69fd0..05ff3c58087e 100644 --- a/drivers/net/ethernet/marvell/mvneta.c +++ b/drivers/net/ethernet/marvell/mvneta.c @@ -4266,7 +4266,7 @@ static void mvneta_mdio_remove(struct mvneta_port *pp) */ static void mvneta_percpu_elect(struct mvneta_port *pp) { - int elected_cpu = 0, max_cpu, cpu, i = 0; + int elected_cpu = 0, max_cpu, cpu; /* Use the cpu associated to the rxq when it is online, in all * the other cases, use the cpu 0 which can't be offline. @@ -4306,8 +4306,6 @@ static void mvneta_percpu_elect(struct mvneta_port *pp) */ smp_call_function_single(cpu, mvneta_percpu_unmask_interrupt, pp, true); - i++; - } }; From 97c4090badca743451c3798f1c1846e9f3f252de Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Wed, 26 Oct 2022 14:31:10 +0200 Subject: [PATCH 0487/2002] bpf: Remove the obsolte u64_stats_fetch_*_irq() users. Now that the 32bit UP oddity is gone and 32bit uses always a sequence count, there is no need for the fetch_irq() variants anymore. Convert to the regular interface. Signed-off-by: Thomas Gleixner Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Daniel Borkmann Acked-by: Peter Zijlstra (Intel) Link: https://lore.kernel.org/bpf/20221026123110.331690-1-bigeasy@linutronix.de --- kernel/bpf/syscall.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c index 11df90962101..5887592eeb93 100644 --- a/kernel/bpf/syscall.c +++ b/kernel/bpf/syscall.c @@ -2118,11 +2118,11 @@ static void bpf_prog_get_stats(const struct bpf_prog *prog, st = per_cpu_ptr(prog->stats, cpu); do { - start = u64_stats_fetch_begin_irq(&st->syncp); + start = u64_stats_fetch_begin(&st->syncp); tnsecs = u64_stats_read(&st->nsecs); tcnt = u64_stats_read(&st->cnt); tmisses = u64_stats_read(&st->misses); - } while (u64_stats_fetch_retry_irq(&st->syncp, start)); + } while (u64_stats_fetch_retry(&st->syncp, start)); nsecs += tnsecs; cnt += tcnt; misses += tmisses; From 37fe9b981667b37925e5f0d62aa59176c3633437 Mon Sep 17 00:00:00 2001 From: Sean Anderson Date: Thu, 27 Oct 2022 15:00:05 -0400 Subject: [PATCH 0488/2002] net: dpaa2: Add some debug prints on deferred probe When this device is deferred, there is often no way to determine what the cause was. Add some debug prints to make it easier to figure out what is blocking the probe. Signed-off-by: Sean Anderson Link: https://lore.kernel.org/r/20221027190005.400839-1-sean.anderson@seco.com Signed-off-by: Jakub Kicinski --- .../net/ethernet/freescale/dpaa2/dpaa2-eth.c | 33 +++++++++++++------ .../net/ethernet/freescale/dpaa2/dpaa2-mac.c | 5 ++- 2 files changed, 27 insertions(+), 11 deletions(-) diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c index 281d7e3905c1..892a1403c469 100644 --- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c +++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c @@ -3038,10 +3038,12 @@ static struct fsl_mc_device *dpaa2_eth_setup_dpcon(struct dpaa2_eth_priv *priv) err = fsl_mc_object_allocate(to_fsl_mc_device(dev), FSL_MC_POOL_DPCON, &dpcon); if (err) { - if (err == -ENXIO) + if (err == -ENXIO) { + dev_dbg(dev, "Waiting for DPCON\n"); err = -EPROBE_DEFER; - else + } else { dev_info(dev, "Not enough DPCONs, will go on as-is\n"); + } return ERR_PTR(err); } @@ -3151,7 +3153,9 @@ static int dpaa2_eth_setup_dpio(struct dpaa2_eth_priv *priv) channel = dpaa2_eth_alloc_channel(priv); if (IS_ERR_OR_NULL(channel)) { err = PTR_ERR_OR_ZERO(channel); - if (err != -EPROBE_DEFER) + if (err == -EPROBE_DEFER) + dev_dbg(dev, "waiting for affine channel\n"); + else dev_info(dev, "No affine channel for cpu %d and above\n", i); goto err_alloc_ch; @@ -4605,8 +4609,10 @@ static int dpaa2_eth_connect_mac(struct dpaa2_eth_priv *priv) dpni_dev = to_fsl_mc_device(priv->net_dev->dev.parent); dpmac_dev = fsl_mc_get_endpoint(dpni_dev, 0); - if (PTR_ERR(dpmac_dev) == -EPROBE_DEFER) + if (PTR_ERR(dpmac_dev) == -EPROBE_DEFER) { + netdev_dbg(priv->net_dev, "waiting for mac\n"); return PTR_ERR(dpmac_dev); + } if (IS_ERR(dpmac_dev) || dpmac_dev->dev.type != &fsl_mc_bus_dpmac_type) return 0; @@ -4626,11 +4632,16 @@ static int dpaa2_eth_connect_mac(struct dpaa2_eth_priv *priv) if (dpaa2_eth_is_type_phy(priv)) { err = dpaa2_mac_connect(mac); - if (err && err != -EPROBE_DEFER) - netdev_err(priv->net_dev, "Error connecting to the MAC endpoint: %pe", - ERR_PTR(err)); - if (err) + if (err) { + if (err == -EPROBE_DEFER) + netdev_dbg(priv->net_dev, + "could not connect to MAC\n"); + else + netdev_err(priv->net_dev, + "Error connecting to the MAC endpoint: %pe", + ERR_PTR(err)); goto err_close_mac; + } } return 0; @@ -4802,10 +4813,12 @@ static int dpaa2_eth_probe(struct fsl_mc_device *dpni_dev) err = fsl_mc_portal_allocate(dpni_dev, FSL_MC_IO_ATOMIC_CONTEXT_PORTAL, &priv->mc_io); if (err) { - if (err == -ENXIO) + if (err == -ENXIO) { + dev_dbg(dev, "waiting for MC portal\n"); err = -EPROBE_DEFER; - else + } else { dev_err(dev, "MC portal allocation failed\n"); + } goto err_portal_alloc; } diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c b/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c index 49ff85633783..2bbab28f763d 100644 --- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c +++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c @@ -105,6 +105,7 @@ static struct fwnode_handle *dpaa2_mac_get_node(struct device *dev, * thus the fwnode field is not yet set. Defer probe if we are * facing this situation. */ + dev_dbg(dev, "dprc not finished probing\n"); return ERR_PTR(-EPROBE_DEFER); } @@ -264,8 +265,10 @@ static int dpaa2_pcs_create(struct dpaa2_mac *mac, mdiodev = fwnode_mdio_find_device(node); fwnode_handle_put(node); - if (!mdiodev) + if (!mdiodev) { + netdev_dbg(mac->net_dev, "missing PCS device\n"); return -EPROBE_DEFER; + } mac->pcs = lynx_pcs_create(mdiodev); if (!mac->pcs) { From 1d997f1013079c05b642c739901e3584a3ae558d Mon Sep 17 00:00:00 2001 From: Hangbin Liu Date: Fri, 28 Oct 2022 04:42:21 -0400 Subject: [PATCH 0489/2002] rtnetlink: pass netlink message header and portid to rtnl_configure_link() This patch pass netlink message header and portid to rtnl_configure_link() All the functions in this call chain need to add the parameters so we can use them in the last call rtnl_notify(), and notify the userspace about the new link info if NLM_F_ECHO flag is set. - rtnl_configure_link() - __dev_notify_flags() - rtmsg_ifinfo() - rtmsg_ifinfo_event() - rtmsg_ifinfo_build_skb() - rtmsg_ifinfo_send() - rtnl_notify() Also move __dev_notify_flags() declaration to net/core/dev.h, as Jakub suggested. Signed-off-by: Hangbin Liu Reviewed-by: Guillaume Nault Signed-off-by: Jakub Kicinski --- drivers/net/can/vxcan.c | 2 +- drivers/net/geneve.c | 2 +- drivers/net/veth.c | 2 +- drivers/net/vxlan/vxlan_core.c | 4 ++-- drivers/net/wwan/wwan_core.c | 2 +- include/linux/netdevice.h | 2 -- include/linux/rtnetlink.h | 9 +++++---- include/net/netlink.h | 11 +++++++++++ include/net/rtnetlink.h | 3 ++- net/core/dev.c | 25 ++++++++++++------------ net/core/dev.h | 4 ++++ net/core/rtnetlink.c | 35 ++++++++++++++++++---------------- net/ipv4/ip_gre.c | 2 +- 13 files changed, 61 insertions(+), 42 deletions(-) diff --git a/drivers/net/can/vxcan.c b/drivers/net/can/vxcan.c index 26a472d2ea58..4068d962203d 100644 --- a/drivers/net/can/vxcan.c +++ b/drivers/net/can/vxcan.c @@ -236,7 +236,7 @@ static int vxcan_newlink(struct net *net, struct net_device *dev, netif_carrier_off(peer); - err = rtnl_configure_link(peer, ifmp); + err = rtnl_configure_link(peer, ifmp, 0, NULL); if (err < 0) goto unregister_network_device; diff --git a/drivers/net/geneve.c b/drivers/net/geneve.c index f393e454f45c..89ff7f8e8c7e 100644 --- a/drivers/net/geneve.c +++ b/drivers/net/geneve.c @@ -1907,7 +1907,7 @@ struct net_device *geneve_dev_create_fb(struct net *net, const char *name, if (err) goto err; - err = rtnl_configure_link(dev, NULL); + err = rtnl_configure_link(dev, NULL, 0, NULL); if (err < 0) goto err; diff --git a/drivers/net/veth.c b/drivers/net/veth.c index 740506c44427..ac7c0653695f 100644 --- a/drivers/net/veth.c +++ b/drivers/net/veth.c @@ -1773,7 +1773,7 @@ static int veth_newlink(struct net *src_net, struct net_device *dev, veth_disable_gro(peer); netif_carrier_off(peer); - err = rtnl_configure_link(peer, ifmp); + err = rtnl_configure_link(peer, ifmp, 0, NULL); if (err < 0) goto err_configure_peer; diff --git a/drivers/net/vxlan/vxlan_core.c b/drivers/net/vxlan/vxlan_core.c index 6ab669dcd1c6..92224b36787a 100644 --- a/drivers/net/vxlan/vxlan_core.c +++ b/drivers/net/vxlan/vxlan_core.c @@ -3794,7 +3794,7 @@ static int __vxlan_dev_create(struct net *net, struct net_device *dev, goto errout; } - err = rtnl_configure_link(dev, NULL); + err = rtnl_configure_link(dev, NULL, 0, NULL); if (err < 0) goto unlink; @@ -4416,7 +4416,7 @@ struct net_device *vxlan_dev_create(struct net *net, const char *name, return ERR_PTR(err); } - err = rtnl_configure_link(dev, NULL); + err = rtnl_configure_link(dev, NULL, 0, NULL); if (err < 0) { LIST_HEAD(list_kill); diff --git a/drivers/net/wwan/wwan_core.c b/drivers/net/wwan/wwan_core.c index 62e9f7d6c9fe..d72ee18476d1 100644 --- a/drivers/net/wwan/wwan_core.c +++ b/drivers/net/wwan/wwan_core.c @@ -1058,7 +1058,7 @@ static void wwan_create_default_link(struct wwan_device *wwandev, goto unlock; } - rtnl_configure_link(dev, NULL); /* Link initialized, notify new link */ + rtnl_configure_link(dev, NULL, 0, NULL); /* Link initialized, notify new link */ unlock: rtnl_unlock(); diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 99e58b773266..4b5052db978f 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -3855,8 +3855,6 @@ int __dev_change_flags(struct net_device *dev, unsigned int flags, struct netlink_ext_ack *extack); int dev_change_flags(struct net_device *dev, unsigned int flags, struct netlink_ext_ack *extack); -void __dev_notify_flags(struct net_device *, unsigned int old_flags, - unsigned int gchanges); int dev_set_alias(struct net_device *, const char *, size_t); int dev_get_alias(const struct net_device *, char *, size_t); int __dev_change_net_namespace(struct net_device *dev, struct net *net, diff --git a/include/linux/rtnetlink.h b/include/linux/rtnetlink.h index ae2c6a3cec5d..92ad75549e9c 100644 --- a/include/linux/rtnetlink.h +++ b/include/linux/rtnetlink.h @@ -12,21 +12,22 @@ extern int rtnetlink_send(struct sk_buff *skb, struct net *net, u32 pid, u32 group, int echo); extern int rtnl_unicast(struct sk_buff *skb, struct net *net, u32 pid); extern void rtnl_notify(struct sk_buff *skb, struct net *net, u32 pid, - u32 group, struct nlmsghdr *nlh, gfp_t flags); + u32 group, const struct nlmsghdr *nlh, gfp_t flags); extern void rtnl_set_sk_err(struct net *net, u32 group, int error); extern int rtnetlink_put_metrics(struct sk_buff *skb, u32 *metrics); extern int rtnl_put_cacheinfo(struct sk_buff *skb, struct dst_entry *dst, u32 id, long expires, u32 error); -void rtmsg_ifinfo(int type, struct net_device *dev, unsigned change, gfp_t flags); +void rtmsg_ifinfo(int type, struct net_device *dev, unsigned int change, gfp_t flags, + u32 portid, const struct nlmsghdr *nlh); void rtmsg_ifinfo_newnet(int type, struct net_device *dev, unsigned int change, gfp_t flags, int *new_nsid, int new_ifindex); struct sk_buff *rtmsg_ifinfo_build_skb(int type, struct net_device *dev, unsigned change, u32 event, gfp_t flags, int *new_nsid, - int new_ifindex); + int new_ifindex, u32 portid, u32 seq); void rtmsg_ifinfo_send(struct sk_buff *skb, struct net_device *dev, - gfp_t flags); + gfp_t flags, u32 portid, const struct nlmsghdr *nlh); /* RTNL is used as a global lock for all changes to network configuration */ diff --git a/include/net/netlink.h b/include/net/netlink.h index 784b4688fc6f..464e2e026f7b 100644 --- a/include/net/netlink.h +++ b/include/net/netlink.h @@ -899,6 +899,17 @@ static inline int nlmsg_report(const struct nlmsghdr *nlh) return nlh ? !!(nlh->nlmsg_flags & NLM_F_ECHO) : 0; } +/** + * nlmsg_seq - return the seq number of netlink message + * @nlh: netlink message header + * + * Returns 0 if netlink message is NULL + */ +static inline u32 nlmsg_seq(const struct nlmsghdr *nlh) +{ + return nlh ? nlh->nlmsg_seq : 0; +} + /** * nlmsg_for_each_attr - iterate over a stream of attributes * @pos: loop counter, set to current attribute diff --git a/include/net/rtnetlink.h b/include/net/rtnetlink.h index bf8bb3357825..cd94f65dc2a9 100644 --- a/include/net/rtnetlink.h +++ b/include/net/rtnetlink.h @@ -187,7 +187,8 @@ struct net_device *rtnl_create_link(struct net *net, const char *ifname, struct nlattr *tb[], struct netlink_ext_ack *extack); int rtnl_delete_link(struct net_device *dev); -int rtnl_configure_link(struct net_device *dev, const struct ifinfomsg *ifm); +int rtnl_configure_link(struct net_device *dev, const struct ifinfomsg *ifm, + u32 portid, const struct nlmsghdr *nlh); int rtnl_nla_parse_ifla(struct nlattr **tb, const struct nlattr *head, int len, struct netlink_ext_ack *exterr); diff --git a/net/core/dev.c b/net/core/dev.c index cfb68db040a4..19e0db536022 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -1333,7 +1333,7 @@ void netdev_state_change(struct net_device *dev) call_netdevice_notifiers_info(NETDEV_CHANGE, &change_info.info); - rtmsg_ifinfo(RTM_NEWLINK, dev, 0, GFP_KERNEL); + rtmsg_ifinfo(RTM_NEWLINK, dev, 0, GFP_KERNEL, 0, NULL); } } EXPORT_SYMBOL(netdev_state_change); @@ -1469,7 +1469,7 @@ int dev_open(struct net_device *dev, struct netlink_ext_ack *extack) if (ret < 0) return ret; - rtmsg_ifinfo(RTM_NEWLINK, dev, IFF_UP|IFF_RUNNING, GFP_KERNEL); + rtmsg_ifinfo(RTM_NEWLINK, dev, IFF_UP | IFF_RUNNING, GFP_KERNEL, 0, NULL); call_netdevice_notifiers(NETDEV_UP, dev); return ret; @@ -1541,7 +1541,7 @@ void dev_close_many(struct list_head *head, bool unlink) __dev_close_many(head); list_for_each_entry_safe(dev, tmp, head, close_list) { - rtmsg_ifinfo(RTM_NEWLINK, dev, IFF_UP|IFF_RUNNING, GFP_KERNEL); + rtmsg_ifinfo(RTM_NEWLINK, dev, IFF_UP | IFF_RUNNING, GFP_KERNEL, 0, NULL); call_netdevice_notifiers(NETDEV_DOWN, dev); if (unlink) list_del_init(&dev->close_list); @@ -8351,7 +8351,7 @@ static int __dev_set_promiscuity(struct net_device *dev, int inc, bool notify) dev_change_rx_flags(dev, IFF_PROMISC); } if (notify) - __dev_notify_flags(dev, old_flags, IFF_PROMISC); + __dev_notify_flags(dev, old_flags, IFF_PROMISC, 0, NULL); return 0; } @@ -8406,7 +8406,7 @@ static int __dev_set_allmulti(struct net_device *dev, int inc, bool notify) dev_set_rx_mode(dev); if (notify) __dev_notify_flags(dev, old_flags, - dev->gflags ^ old_gflags); + dev->gflags ^ old_gflags, 0, NULL); } return 0; } @@ -8569,12 +8569,13 @@ int __dev_change_flags(struct net_device *dev, unsigned int flags, } void __dev_notify_flags(struct net_device *dev, unsigned int old_flags, - unsigned int gchanges) + unsigned int gchanges, u32 portid, + const struct nlmsghdr *nlh) { unsigned int changes = dev->flags ^ old_flags; if (gchanges) - rtmsg_ifinfo(RTM_NEWLINK, dev, gchanges, GFP_ATOMIC); + rtmsg_ifinfo(RTM_NEWLINK, dev, gchanges, GFP_ATOMIC, portid, nlh); if (changes & IFF_UP) { if (dev->flags & IFF_UP) @@ -8616,7 +8617,7 @@ int dev_change_flags(struct net_device *dev, unsigned int flags, return ret; changes = (old_flags ^ dev->flags) | (old_gflags ^ dev->gflags); - __dev_notify_flags(dev, old_flags, changes); + __dev_notify_flags(dev, old_flags, changes, 0, NULL); return ret; } EXPORT_SYMBOL(dev_change_flags); @@ -10101,7 +10102,7 @@ int register_netdevice(struct net_device *dev) */ if (!dev->rtnl_link_ops || dev->rtnl_link_state == RTNL_LINK_INITIALIZED) - rtmsg_ifinfo(RTM_NEWLINK, dev, ~0U, GFP_KERNEL); + rtmsg_ifinfo(RTM_NEWLINK, dev, ~0U, GFP_KERNEL, 0, NULL); out: return ret; @@ -10849,7 +10850,7 @@ void unregister_netdevice_many(struct list_head *head) if (!dev->rtnl_link_ops || dev->rtnl_link_state == RTNL_LINK_INITIALIZED) skb = rtmsg_ifinfo_build_skb(RTM_DELLINK, dev, ~0U, 0, - GFP_KERNEL, NULL, 0); + GFP_KERNEL, NULL, 0, 0, 0); /* * Flush the unicast and multicast chains @@ -10864,7 +10865,7 @@ void unregister_netdevice_many(struct list_head *head) dev->netdev_ops->ndo_uninit(dev); if (skb) - rtmsg_ifinfo_send(skb, dev, GFP_KERNEL); + rtmsg_ifinfo_send(skb, dev, GFP_KERNEL, 0, NULL); /* Notifier chain MUST detach us all upper devices. */ WARN_ON(netdev_has_any_upper_dev(dev)); @@ -11042,7 +11043,7 @@ int __dev_change_net_namespace(struct net_device *dev, struct net *net, * Prevent userspace races by waiting until the network * device is fully setup before sending notifications. */ - rtmsg_ifinfo(RTM_NEWLINK, dev, ~0U, GFP_KERNEL); + rtmsg_ifinfo(RTM_NEWLINK, dev, ~0U, GFP_KERNEL, 0, NULL); synchronize_net(); err = 0; diff --git a/net/core/dev.h b/net/core/dev.h index cbb8a925175a..6b3c7302f570 100644 --- a/net/core/dev.h +++ b/net/core/dev.h @@ -88,6 +88,10 @@ int dev_change_carrier(struct net_device *dev, bool new_carrier); void __dev_set_rx_mode(struct net_device *dev); +void __dev_notify_flags(struct net_device *dev, unsigned int old_flags, + unsigned int gchanges, u32 portid, + const struct nlmsghdr *nlh); + static inline void netif_set_gso_max_size(struct net_device *dev, unsigned int size) { diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index 74864dc46a7e..c9dd9730f3c6 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -760,7 +760,7 @@ int rtnl_unicast(struct sk_buff *skb, struct net *net, u32 pid) EXPORT_SYMBOL(rtnl_unicast); void rtnl_notify(struct sk_buff *skb, struct net *net, u32 pid, u32 group, - struct nlmsghdr *nlh, gfp_t flags) + const struct nlmsghdr *nlh, gfp_t flags) { struct sock *rtnl = net->rtnl; @@ -3180,7 +3180,8 @@ out: return err; } -int rtnl_configure_link(struct net_device *dev, const struct ifinfomsg *ifm) +int rtnl_configure_link(struct net_device *dev, const struct ifinfomsg *ifm, + u32 portid, const struct nlmsghdr *nlh) { unsigned int old_flags; int err; @@ -3194,10 +3195,10 @@ int rtnl_configure_link(struct net_device *dev, const struct ifinfomsg *ifm) } if (dev->rtnl_link_state == RTNL_LINK_INITIALIZED) { - __dev_notify_flags(dev, old_flags, (old_flags ^ dev->flags)); + __dev_notify_flags(dev, old_flags, (old_flags ^ dev->flags), portid, nlh); } else { dev->rtnl_link_state = RTNL_LINK_INITIALIZED; - __dev_notify_flags(dev, old_flags, ~0U); + __dev_notify_flags(dev, old_flags, ~0U, portid, nlh); } return 0; } @@ -3369,7 +3370,7 @@ static int rtnl_newlink_create(struct sk_buff *skb, struct ifinfomsg *ifm, goto out; } - err = rtnl_configure_link(dev, ifm); + err = rtnl_configure_link(dev, ifm, 0, NULL); if (err < 0) goto out_unregister; if (link_net) { @@ -3896,7 +3897,7 @@ static int rtnl_dump_all(struct sk_buff *skb, struct netlink_callback *cb) struct sk_buff *rtmsg_ifinfo_build_skb(int type, struct net_device *dev, unsigned int change, u32 event, gfp_t flags, int *new_nsid, - int new_ifindex) + int new_ifindex, u32 portid, u32 seq) { struct net *net = dev_net(dev); struct sk_buff *skb; @@ -3907,7 +3908,7 @@ struct sk_buff *rtmsg_ifinfo_build_skb(int type, struct net_device *dev, goto errout; err = rtnl_fill_ifinfo(skb, dev, dev_net(dev), - type, 0, 0, change, 0, 0, event, + type, portid, seq, change, 0, 0, event, new_nsid, new_ifindex, -1, flags); if (err < 0) { /* -EMSGSIZE implies BUG in if_nlmsg_size() */ @@ -3922,16 +3923,18 @@ errout: return NULL; } -void rtmsg_ifinfo_send(struct sk_buff *skb, struct net_device *dev, gfp_t flags) +void rtmsg_ifinfo_send(struct sk_buff *skb, struct net_device *dev, gfp_t flags, + u32 portid, const struct nlmsghdr *nlh) { struct net *net = dev_net(dev); - rtnl_notify(skb, net, 0, RTNLGRP_LINK, NULL, flags); + rtnl_notify(skb, net, portid, RTNLGRP_LINK, nlh, flags); } static void rtmsg_ifinfo_event(int type, struct net_device *dev, unsigned int change, u32 event, - gfp_t flags, int *new_nsid, int new_ifindex) + gfp_t flags, int *new_nsid, int new_ifindex, + u32 portid, const struct nlmsghdr *nlh) { struct sk_buff *skb; @@ -3939,23 +3942,23 @@ static void rtmsg_ifinfo_event(int type, struct net_device *dev, return; skb = rtmsg_ifinfo_build_skb(type, dev, change, event, flags, new_nsid, - new_ifindex); + new_ifindex, portid, nlmsg_seq(nlh)); if (skb) - rtmsg_ifinfo_send(skb, dev, flags); + rtmsg_ifinfo_send(skb, dev, flags, portid, nlh); } void rtmsg_ifinfo(int type, struct net_device *dev, unsigned int change, - gfp_t flags) + gfp_t flags, u32 portid, const struct nlmsghdr *nlh) { rtmsg_ifinfo_event(type, dev, change, rtnl_get_event(0), flags, - NULL, 0); + NULL, 0, portid, nlh); } void rtmsg_ifinfo_newnet(int type, struct net_device *dev, unsigned int change, gfp_t flags, int *new_nsid, int new_ifindex) { rtmsg_ifinfo_event(type, dev, change, rtnl_get_event(0), flags, - new_nsid, new_ifindex); + new_nsid, new_ifindex, 0, NULL); } static int nlmsg_populate_fdb_fill(struct sk_buff *skb, @@ -6140,7 +6143,7 @@ static int rtnetlink_event(struct notifier_block *this, unsigned long event, voi case NETDEV_CHANGELOWERSTATE: case NETDEV_CHANGE_TX_QUEUE_LEN: rtmsg_ifinfo_event(RTM_NEWLINK, dev, 0, rtnl_get_event(event), - GFP_KERNEL, NULL, 0); + GFP_KERNEL, NULL, 0, 0, NULL); break; default: break; diff --git a/net/ipv4/ip_gre.c b/net/ipv4/ip_gre.c index f866d6282b2b..d8ee5238c395 100644 --- a/net/ipv4/ip_gre.c +++ b/net/ipv4/ip_gre.c @@ -1665,7 +1665,7 @@ struct net_device *gretap_fb_dev_create(struct net *net, const char *name, if (err) goto out; - err = rtnl_configure_link(dev, NULL); + err = rtnl_configure_link(dev, NULL, 0, NULL); if (err < 0) goto out; From 77f4aa9a2a1766a0b9343fd812b71f18d05178da Mon Sep 17 00:00:00 2001 From: Hangbin Liu Date: Fri, 28 Oct 2022 04:42:22 -0400 Subject: [PATCH 0490/2002] net: add new helper unregister_netdevice_many_notify Add new helper unregister_netdevice_many_notify(), pass netlink message header and portid, which could be used to notify userspace when flag NLM_F_ECHO is set. Make the unregister_netdevice_many() as a wrapper of new function unregister_netdevice_many_notify(). Suggested-by: Guillaume Nault Signed-off-by: Hangbin Liu Reviewed-by: Guillaume Nault Signed-off-by: Jakub Kicinski --- net/core/dev.c | 27 +++++++++++++++++---------- net/core/dev.h | 3 +++ 2 files changed, 20 insertions(+), 10 deletions(-) diff --git a/net/core/dev.c b/net/core/dev.c index 19e0db536022..2e4f1c97b59e 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -10781,14 +10781,8 @@ void unregister_netdevice_queue(struct net_device *dev, struct list_head *head) } EXPORT_SYMBOL(unregister_netdevice_queue); -/** - * unregister_netdevice_many - unregister many devices - * @head: list of devices - * - * Note: As most callers use a stack allocated list_head, - * we force a list_del() to make sure stack wont be corrupted later. - */ -void unregister_netdevice_many(struct list_head *head) +void unregister_netdevice_many_notify(struct list_head *head, + u32 portid, const struct nlmsghdr *nlh) { struct net_device *dev, *tmp; LIST_HEAD(close_head); @@ -10850,7 +10844,8 @@ void unregister_netdevice_many(struct list_head *head) if (!dev->rtnl_link_ops || dev->rtnl_link_state == RTNL_LINK_INITIALIZED) skb = rtmsg_ifinfo_build_skb(RTM_DELLINK, dev, ~0U, 0, - GFP_KERNEL, NULL, 0, 0, 0); + GFP_KERNEL, NULL, 0, + portid, nlmsg_seq(nlh)); /* * Flush the unicast and multicast chains @@ -10865,7 +10860,7 @@ void unregister_netdevice_many(struct list_head *head) dev->netdev_ops->ndo_uninit(dev); if (skb) - rtmsg_ifinfo_send(skb, dev, GFP_KERNEL, 0, NULL); + rtmsg_ifinfo_send(skb, dev, GFP_KERNEL, portid, nlh); /* Notifier chain MUST detach us all upper devices. */ WARN_ON(netdev_has_any_upper_dev(dev)); @@ -10888,6 +10883,18 @@ void unregister_netdevice_many(struct list_head *head) list_del(head); } + +/** + * unregister_netdevice_many - unregister many devices + * @head: list of devices + * + * Note: As most callers use a stack allocated list_head, + * we force a list_del() to make sure stack wont be corrupted later. + */ +void unregister_netdevice_many(struct list_head *head) +{ + unregister_netdevice_many_notify(head, 0, NULL); +} EXPORT_SYMBOL(unregister_netdevice_many); /** diff --git a/net/core/dev.h b/net/core/dev.h index 6b3c7302f570..814ed5b7b960 100644 --- a/net/core/dev.h +++ b/net/core/dev.h @@ -92,6 +92,9 @@ void __dev_notify_flags(struct net_device *dev, unsigned int old_flags, unsigned int gchanges, u32 portid, const struct nlmsghdr *nlh); +void unregister_netdevice_many_notify(struct list_head *head, + u32 portid, const struct nlmsghdr *nlh); + static inline void netif_set_gso_max_size(struct net_device *dev, unsigned int size) { From d88e136cab37d6a5aa3691a2f636d37bd6520cc2 Mon Sep 17 00:00:00 2001 From: Hangbin Liu Date: Fri, 28 Oct 2022 04:42:23 -0400 Subject: [PATCH 0491/2002] rtnetlink: Honour NLM_F_ECHO flag in rtnl_newlink_create This patch pass the netlink header message in rtnl_newlink_create() to the new updated rtnl_configure_link(), so that the kernel could reply unicast when userspace set NLM_F_ECHO flag to request the new created interface info. Suggested-by: Guillaume Nault Signed-off-by: Hangbin Liu Reviewed-by: Guillaume Nault Signed-off-by: Jakub Kicinski --- net/core/rtnetlink.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index c9dd9730f3c6..839ff8b7eadc 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -3312,11 +3312,13 @@ static int rtnl_group_changelink(const struct sk_buff *skb, static int rtnl_newlink_create(struct sk_buff *skb, struct ifinfomsg *ifm, const struct rtnl_link_ops *ops, + const struct nlmsghdr *nlh, struct nlattr **tb, struct nlattr **data, struct netlink_ext_ack *extack) { unsigned char name_assign_type = NET_NAME_USER; struct net *net = sock_net(skb->sk); + u32 portid = NETLINK_CB(skb).portid; struct net *dest_net, *link_net; struct net_device *dev; char ifname[IFNAMSIZ]; @@ -3370,7 +3372,7 @@ static int rtnl_newlink_create(struct sk_buff *skb, struct ifinfomsg *ifm, goto out; } - err = rtnl_configure_link(dev, ifm, 0, NULL); + err = rtnl_configure_link(dev, ifm, portid, nlh); if (err < 0) goto out_unregister; if (link_net) { @@ -3579,7 +3581,7 @@ replay: return -EOPNOTSUPP; } - return rtnl_newlink_create(skb, ifm, ops, tb, data, extack); + return rtnl_newlink_create(skb, ifm, ops, nlh, tb, data, extack); } static int rtnl_newlink(struct sk_buff *skb, struct nlmsghdr *nlh, From f3a63cce1b4fbde7738395c5a2dea83f05de3407 Mon Sep 17 00:00:00 2001 From: Hangbin Liu Date: Fri, 28 Oct 2022 04:42:24 -0400 Subject: [PATCH 0492/2002] rtnetlink: Honour NLM_F_ECHO flag in rtnl_delete_link This patch use the new helper unregister_netdevice_many_notify() for rtnl_delete_link(), so that the kernel could reply unicast when userspace set NLM_F_ECHO flag to request the new created interface info. At the same time, the parameters of rtnl_delete_link() need to be updated since we need nlmsghdr and portid info. Suggested-by: Guillaume Nault Signed-off-by: Hangbin Liu Reviewed-by: Guillaume Nault Signed-off-by: Jakub Kicinski --- include/net/rtnetlink.h | 2 +- net/core/rtnetlink.c | 7 ++++--- net/openvswitch/vport-geneve.c | 2 +- net/openvswitch/vport-gre.c | 2 +- net/openvswitch/vport-netdev.c | 2 +- net/openvswitch/vport-vxlan.c | 2 +- 6 files changed, 9 insertions(+), 8 deletions(-) diff --git a/include/net/rtnetlink.h b/include/net/rtnetlink.h index cd94f65dc2a9..d9076a7a430c 100644 --- a/include/net/rtnetlink.h +++ b/include/net/rtnetlink.h @@ -186,7 +186,7 @@ struct net_device *rtnl_create_link(struct net *net, const char *ifname, const struct rtnl_link_ops *ops, struct nlattr *tb[], struct netlink_ext_ack *extack); -int rtnl_delete_link(struct net_device *dev); +int rtnl_delete_link(struct net_device *dev, u32 portid, const struct nlmsghdr *nlh); int rtnl_configure_link(struct net_device *dev, const struct ifinfomsg *ifm, u32 portid, const struct nlmsghdr *nlh); diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index 839ff8b7eadc..d2f27548fc0b 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -3110,7 +3110,7 @@ static int rtnl_group_dellink(const struct net *net, int group) return 0; } -int rtnl_delete_link(struct net_device *dev) +int rtnl_delete_link(struct net_device *dev, u32 portid, const struct nlmsghdr *nlh) { const struct rtnl_link_ops *ops; LIST_HEAD(list_kill); @@ -3120,7 +3120,7 @@ int rtnl_delete_link(struct net_device *dev) return -EOPNOTSUPP; ops->dellink(dev, &list_kill); - unregister_netdevice_many(&list_kill); + unregister_netdevice_many_notify(&list_kill, portid, nlh); return 0; } @@ -3130,6 +3130,7 @@ static int rtnl_dellink(struct sk_buff *skb, struct nlmsghdr *nlh, struct netlink_ext_ack *extack) { struct net *net = sock_net(skb->sk); + u32 portid = NETLINK_CB(skb).portid; struct net *tgt_net = net; struct net_device *dev = NULL; struct ifinfomsg *ifm; @@ -3171,7 +3172,7 @@ static int rtnl_dellink(struct sk_buff *skb, struct nlmsghdr *nlh, goto out; } - err = rtnl_delete_link(dev); + err = rtnl_delete_link(dev, portid, nlh); out: if (netnsid >= 0) diff --git a/net/openvswitch/vport-geneve.c b/net/openvswitch/vport-geneve.c index 89a8e1501809..b10e1602c6b1 100644 --- a/net/openvswitch/vport-geneve.c +++ b/net/openvswitch/vport-geneve.c @@ -91,7 +91,7 @@ static struct vport *geneve_tnl_create(const struct vport_parms *parms) err = dev_change_flags(dev, dev->flags | IFF_UP, NULL); if (err < 0) { - rtnl_delete_link(dev); + rtnl_delete_link(dev, 0, NULL); rtnl_unlock(); ovs_vport_free(vport); goto error; diff --git a/net/openvswitch/vport-gre.c b/net/openvswitch/vport-gre.c index e6b5e76a962a..4014c9b5eb79 100644 --- a/net/openvswitch/vport-gre.c +++ b/net/openvswitch/vport-gre.c @@ -57,7 +57,7 @@ static struct vport *gre_tnl_create(const struct vport_parms *parms) err = dev_change_flags(dev, dev->flags | IFF_UP, NULL); if (err < 0) { - rtnl_delete_link(dev); + rtnl_delete_link(dev, 0, NULL); rtnl_unlock(); ovs_vport_free(vport); return ERR_PTR(err); diff --git a/net/openvswitch/vport-netdev.c b/net/openvswitch/vport-netdev.c index 2f61d5bdce1a..903537a5da22 100644 --- a/net/openvswitch/vport-netdev.c +++ b/net/openvswitch/vport-netdev.c @@ -172,7 +172,7 @@ void ovs_netdev_tunnel_destroy(struct vport *vport) * if it's not already shutting down. */ if (vport->dev->reg_state == NETREG_REGISTERED) - rtnl_delete_link(vport->dev); + rtnl_delete_link(vport->dev, 0, NULL); netdev_put(vport->dev, &vport->dev_tracker); vport->dev = NULL; rtnl_unlock(); diff --git a/net/openvswitch/vport-vxlan.c b/net/openvswitch/vport-vxlan.c index 188e9c1360a1..0b881b043bcf 100644 --- a/net/openvswitch/vport-vxlan.c +++ b/net/openvswitch/vport-vxlan.c @@ -120,7 +120,7 @@ static struct vport *vxlan_tnl_create(const struct vport_parms *parms) err = dev_change_flags(dev, dev->flags | IFF_UP, NULL); if (err < 0) { - rtnl_delete_link(dev); + rtnl_delete_link(dev, 0, NULL); rtnl_unlock(); ovs_vport_free(vport); goto error; From 91e87045a5ef6f7003e9a2cb7dfa435b9b002dbe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Steffen=20B=C3=A4tz?= Date: Fri, 28 Oct 2022 13:31:58 -0300 Subject: [PATCH 0493/2002] net: dsa: mv88e6xxx: Add RGMII delay to 88E6320 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently, the .port_set_rgmii_delay hook is missing for the 88E6320 family, which causes failure to retrieve an IP address via DHCP. Add mv88e6320_port_set_rgmii_delay() that allows applying the RGMII delay for ports 2, 5, and 6, which are the only ports that can be used in RGMII mode. Tested on a custom i.MX8MN board connected to an 88E6320 switch. This change also applies safely to the 88E6321 variant. The only difference between 88E6320 versus 88E6321 is the temperature grade and pinout. They share exactly the same MDIO register map for ports 2, 5, and 6, which are the only ports that can be used in RGMII mode. Signed-off-by: Steffen Bätz [fabio: Improved commit log and extended it to mv88e6321_ops] Signed-off-by: Fabio Estevam Reviewed-by: Andrew Lunn Link: https://lore.kernel.org/r/20221028163158.198108-1-festevam@gmail.com Signed-off-by: Jakub Kicinski --- drivers/net/dsa/mv88e6xxx/chip.c | 2 ++ drivers/net/dsa/mv88e6xxx/port.c | 9 +++++++++ drivers/net/dsa/mv88e6xxx/port.h | 2 ++ 3 files changed, 13 insertions(+) diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index 2479be3a1e35..bf34c942db99 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -5029,6 +5029,7 @@ static const struct mv88e6xxx_ops mv88e6320_ops = { .phy_write = mv88e6xxx_g2_smi_phy_write, .port_set_link = mv88e6xxx_port_set_link, .port_sync_link = mv88e6xxx_port_sync_link, + .port_set_rgmii_delay = mv88e6320_port_set_rgmii_delay, .port_set_speed_duplex = mv88e6185_port_set_speed_duplex, .port_tag_remap = mv88e6095_port_tag_remap, .port_set_frame_mode = mv88e6351_port_set_frame_mode, @@ -5073,6 +5074,7 @@ static const struct mv88e6xxx_ops mv88e6321_ops = { .phy_write = mv88e6xxx_g2_smi_phy_write, .port_set_link = mv88e6xxx_port_set_link, .port_sync_link = mv88e6xxx_port_sync_link, + .port_set_rgmii_delay = mv88e6320_port_set_rgmii_delay, .port_set_speed_duplex = mv88e6185_port_set_speed_duplex, .port_tag_remap = mv88e6095_port_tag_remap, .port_set_frame_mode = mv88e6351_port_set_frame_mode, diff --git a/drivers/net/dsa/mv88e6xxx/port.c b/drivers/net/dsa/mv88e6xxx/port.c index 5c4195c635b0..f79cf716c541 100644 --- a/drivers/net/dsa/mv88e6xxx/port.c +++ b/drivers/net/dsa/mv88e6xxx/port.c @@ -133,6 +133,15 @@ int mv88e6390_port_set_rgmii_delay(struct mv88e6xxx_chip *chip, int port, return mv88e6xxx_port_set_rgmii_delay(chip, port, mode); } +int mv88e6320_port_set_rgmii_delay(struct mv88e6xxx_chip *chip, int port, + phy_interface_t mode) +{ + if (port != 2 && port != 5 && port != 6) + return -EOPNOTSUPP; + + return mv88e6xxx_port_set_rgmii_delay(chip, port, mode); +} + int mv88e6xxx_port_set_link(struct mv88e6xxx_chip *chip, int port, int link) { u16 reg; diff --git a/drivers/net/dsa/mv88e6xxx/port.h b/drivers/net/dsa/mv88e6xxx/port.h index cb04243f37c1..aec9d4fd20e3 100644 --- a/drivers/net/dsa/mv88e6xxx/port.h +++ b/drivers/net/dsa/mv88e6xxx/port.h @@ -332,6 +332,8 @@ int mv88e6xxx_port_wait_bit(struct mv88e6xxx_chip *chip, int port, int reg, int mv88e6185_port_set_pause(struct mv88e6xxx_chip *chip, int port, int pause); +int mv88e6320_port_set_rgmii_delay(struct mv88e6xxx_chip *chip, int port, + phy_interface_t mode); int mv88e6352_port_set_rgmii_delay(struct mv88e6xxx_chip *chip, int port, phy_interface_t mode); int mv88e6390_port_set_rgmii_delay(struct mv88e6xxx_chip *chip, int port, From 55f6f3dbcf4ccc4ed154a7f89ec5315d967bd2c6 Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Fri, 28 Oct 2022 21:32:18 +0300 Subject: [PATCH 0494/2002] net: ftmac100: prepare data path for receiving single segment packets > 1514 Eliminate one check in the data path and move it elsewhere, to where our real limitation is. We'll want to start processing "too long" frames in the driver (currently there is a hardware MAC setting which drops theses). Signed-off-by: Vladimir Oltean Signed-off-by: Sergei Antonov Link: https://lore.kernel.org/r/20221028183220.155948-1-saproj@gmail.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/faraday/ftmac100.c | 29 ++++++++++--------------- 1 file changed, 12 insertions(+), 17 deletions(-) diff --git a/drivers/net/ethernet/faraday/ftmac100.c b/drivers/net/ethernet/faraday/ftmac100.c index d95d78230828..8013f85fc148 100644 --- a/drivers/net/ethernet/faraday/ftmac100.c +++ b/drivers/net/ethernet/faraday/ftmac100.c @@ -218,11 +218,6 @@ static bool ftmac100_rxdes_crc_error(struct ftmac100_rxdes *rxdes) return rxdes->rxdes0 & cpu_to_le32(FTMAC100_RXDES0_CRC_ERR); } -static bool ftmac100_rxdes_frame_too_long(struct ftmac100_rxdes *rxdes) -{ - return rxdes->rxdes0 & cpu_to_le32(FTMAC100_RXDES0_FTL); -} - static bool ftmac100_rxdes_runt(struct ftmac100_rxdes *rxdes) { return rxdes->rxdes0 & cpu_to_le32(FTMAC100_RXDES0_RUNT); @@ -337,13 +332,7 @@ static bool ftmac100_rx_packet_error(struct ftmac100 *priv, error = true; } - if (unlikely(ftmac100_rxdes_frame_too_long(rxdes))) { - if (net_ratelimit()) - netdev_info(netdev, "rx frame too long\n"); - - netdev->stats.rx_length_errors++; - error = true; - } else if (unlikely(ftmac100_rxdes_runt(rxdes))) { + if (unlikely(ftmac100_rxdes_runt(rxdes))) { if (net_ratelimit()) netdev_info(netdev, "rx runt\n"); @@ -356,6 +345,11 @@ static bool ftmac100_rx_packet_error(struct ftmac100 *priv, netdev->stats.rx_length_errors++; error = true; } + /* + * FTMAC100_RXDES0_FTL is not an error, it just indicates that the + * frame is longer than 1518 octets. Receiving these is possible when + * we told the hardware not to drop them, via FTMAC100_MACCR_RX_FTL. + */ return error; } @@ -400,12 +394,13 @@ static bool ftmac100_rx_packet(struct ftmac100 *priv, int *processed) return true; } - /* - * It is impossible to get multi-segment packets - * because we always provide big enough receive buffers. - */ + /* We don't support multi-segment packets for now, so drop them. */ ret = ftmac100_rxdes_last_segment(rxdes); - BUG_ON(!ret); + if (unlikely(!ret)) { + netdev->stats.rx_length_errors++; + ftmac100_rx_drop_packet(priv); + return true; + } /* start processing */ skb = netdev_alloc_skb_ip_align(netdev, 128); From 30f837b7b92394599ddc366ff4cf8b77d070d4f4 Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Fri, 28 Oct 2022 21:32:19 +0300 Subject: [PATCH 0495/2002] net: ftmac100: report the correct maximum MTU of 1500 The driver uses the MAX_PKT_SIZE (1518) for both MTU reporting and for TX. However, the 2 places do not measure the same thing. On TX, skb->len measures the entire L2 packet length (without FCS, which software does not possess). So the comparison against 1518 there is correct. What is not correct is the reporting of dev->max_mtu as 1518. Since MTU measures L2 *payload* length (excluding L2 overhead) and not total L2 packet length, it means that the correct max_mtu supported by this device is the standard 1500. Anything higher than that will be dropped on RX currently. To fix this, subtract VLAN_ETH_HLEN from MAX_PKT_SIZE when reporting the max_mtu, since that is the difference between L2 payload length and total L2 length as seen by software. Signed-off-by: Vladimir Oltean Signed-off-by: Sergei Antonov Link: https://lore.kernel.org/r/20221028183220.155948-2-saproj@gmail.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/faraday/ftmac100.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/faraday/ftmac100.c b/drivers/net/ethernet/faraday/ftmac100.c index 8013f85fc148..7c571b4515a9 100644 --- a/drivers/net/ethernet/faraday/ftmac100.c +++ b/drivers/net/ethernet/faraday/ftmac100.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -1070,7 +1071,7 @@ static int ftmac100_probe(struct platform_device *pdev) SET_NETDEV_DEV(netdev, &pdev->dev); netdev->ethtool_ops = &ftmac100_ethtool_ops; netdev->netdev_ops = &ftmac100_netdev_ops; - netdev->max_mtu = MAX_PKT_SIZE; + netdev->max_mtu = MAX_PKT_SIZE - VLAN_ETH_HLEN; err = platform_get_ethdev_address(&pdev->dev, netdev); if (err == -EPROBE_DEFER) From 37c8489012dd9e830f2138ef6f2f52dc60ff944c Mon Sep 17 00:00:00 2001 From: Sergei Antonov Date: Fri, 28 Oct 2022 21:32:20 +0300 Subject: [PATCH 0496/2002] net: ftmac100: allow increasing MTU to make most use of single-segment buffers If the FTMAC100 is used as a DSA master, then it is expected that frames which are MTU sized on the wire facing the external switch port (1500 octets in L2 payload, plus L2 header) also get a DSA tag when seen by the host port. This extra tag increases the length of the packet as the host port sees it, and the FTMAC100 is not prepared to handle frames whose length exceeds 1518 octets (including FCS) at all. Only a minimal rework is needed to support this configuration. Since MTU-sized DSA-tagged frames still fit within a single buffer (RX_BUF_SIZE), we just need to optimize the resource management rather than implement multi buffer RX. In ndo_change_mtu(), we toggle the FTMAC100_MACCR_RX_FTL bit to tell the hardware to drop (or not) frames with an L2 payload length larger than 1500. We need to replicate the MACCR configuration in ftmac100_start_hw() as well, since there is a hardware reset there which clears previous settings. The advantage of dynamically changing FTMAC100_MACCR_RX_FTL is that when dev->mtu is at the default value of 1500, large frames are automatically dropped in hardware and we do not spend CPU cycles dropping them. Suggested-by: Vladimir Oltean Signed-off-by: Sergei Antonov Link: https://lore.kernel.org/r/20221028183220.155948-3-saproj@gmail.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/faraday/ftmac100.c | 33 +++++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/faraday/ftmac100.c b/drivers/net/ethernet/faraday/ftmac100.c index 7c571b4515a9..6c8c78018ce6 100644 --- a/drivers/net/ethernet/faraday/ftmac100.c +++ b/drivers/net/ethernet/faraday/ftmac100.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -28,8 +29,8 @@ #define RX_QUEUE_ENTRIES 128 /* must be power of 2 */ #define TX_QUEUE_ENTRIES 16 /* must be power of 2 */ -#define MAX_PKT_SIZE 1518 #define RX_BUF_SIZE 2044 /* must be smaller than 0x7ff */ +#define MAX_PKT_SIZE RX_BUF_SIZE /* multi-segment not supported */ #if MAX_PKT_SIZE > 0x7ff #error invalid MAX_PKT_SIZE @@ -160,6 +161,7 @@ static void ftmac100_set_mac(struct ftmac100 *priv, const unsigned char *mac) static int ftmac100_start_hw(struct ftmac100 *priv) { struct net_device *netdev = priv->netdev; + unsigned int maccr = MACCR_ENABLE_ALL; if (ftmac100_reset(priv)) return -EIO; @@ -176,7 +178,11 @@ static int ftmac100_start_hw(struct ftmac100 *priv) ftmac100_set_mac(priv, netdev->dev_addr); - iowrite32(MACCR_ENABLE_ALL, priv->base + FTMAC100_OFFSET_MACCR); + /* See ftmac100_change_mtu() */ + if (netdev->mtu > ETH_DATA_LEN) + maccr |= FTMAC100_MACCR_RX_FTL; + + iowrite32(maccr, priv->base + FTMAC100_OFFSET_MACCR); return 0; } @@ -1033,6 +1039,28 @@ static int ftmac100_do_ioctl(struct net_device *netdev, struct ifreq *ifr, int c return generic_mii_ioctl(&priv->mii, data, cmd, NULL); } +static int ftmac100_change_mtu(struct net_device *netdev, int mtu) +{ + struct ftmac100 *priv = netdev_priv(netdev); + unsigned int maccr; + + maccr = ioread32(priv->base + FTMAC100_OFFSET_MACCR); + if (mtu > ETH_DATA_LEN) { + /* process long packets in the driver */ + maccr |= FTMAC100_MACCR_RX_FTL; + } else { + /* Let the controller drop incoming packets greater + * than 1518 (that is 1500 + 14 Ethernet + 4 FCS). + */ + maccr &= ~FTMAC100_MACCR_RX_FTL; + } + iowrite32(maccr, priv->base + FTMAC100_OFFSET_MACCR); + + netdev->mtu = mtu; + + return 0; +} + static const struct net_device_ops ftmac100_netdev_ops = { .ndo_open = ftmac100_open, .ndo_stop = ftmac100_stop, @@ -1040,6 +1068,7 @@ static const struct net_device_ops ftmac100_netdev_ops = { .ndo_set_mac_address = eth_mac_addr, .ndo_validate_addr = eth_validate_addr, .ndo_eth_ioctl = ftmac100_do_ioctl, + .ndo_change_mtu = ftmac100_change_mtu, }; /****************************************************************************** From b98deb2f9803849fd0db714ea9f27aa18f3d9103 Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Fri, 28 Oct 2022 15:21:40 -0700 Subject: [PATCH 0497/2002] net: systemport: Add support for RDMA overflow statistic counter RDMA overflows can happen if the Ethernet controller does not have enough bandwidth allocated at the memory controller level, report RDMA overflows and deal with saturation, similar to the RBUF overflow counter. Signed-off-by: Florian Fainelli Link: https://lore.kernel.org/r/20221028222141.3208429-1-f.fainelli@gmail.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/broadcom/bcmsysport.c | 11 +++++++++++ drivers/net/ethernet/broadcom/bcmsysport.h | 11 +++++++++++ 2 files changed, 22 insertions(+) diff --git a/drivers/net/ethernet/broadcom/bcmsysport.c b/drivers/net/ethernet/broadcom/bcmsysport.c index f8b1adc389b3..38d0cdaf22a5 100644 --- a/drivers/net/ethernet/broadcom/bcmsysport.c +++ b/drivers/net/ethernet/broadcom/bcmsysport.c @@ -295,6 +295,8 @@ static const struct bcm_sysport_stats bcm_sysport_gstrings_stats[] = { /* RBUF misc statistics */ STAT_RBUF("rbuf_ovflow_cnt", mib.rbuf_ovflow_cnt, RBUF_OVFL_DISC_CNTR), STAT_RBUF("rbuf_err_cnt", mib.rbuf_err_cnt, RBUF_ERR_PKT_CNTR), + /* RDMA misc statistics */ + STAT_RDMA("rdma_ovflow_cnt", mib.rdma_ovflow_cnt, RDMA_OVFL_DISC_CNTR), STAT_MIB_SOFT("alloc_rx_buff_failed", mib.alloc_rx_buff_failed), STAT_MIB_SOFT("rx_dma_failed", mib.rx_dma_failed), STAT_MIB_SOFT("tx_dma_failed", mib.tx_dma_failed), @@ -333,6 +335,7 @@ static inline bool bcm_sysport_lite_stat_valid(enum bcm_sysport_stat_type type) case BCM_SYSPORT_STAT_NETDEV64: case BCM_SYSPORT_STAT_RXCHK: case BCM_SYSPORT_STAT_RBUF: + case BCM_SYSPORT_STAT_RDMA: case BCM_SYSPORT_STAT_SOFT: return true; default: @@ -436,6 +439,14 @@ static void bcm_sysport_update_mib_counters(struct bcm_sysport_priv *priv) if (val == ~0) rbuf_writel(priv, 0, s->reg_offset); break; + case BCM_SYSPORT_STAT_RDMA: + if (!priv->is_lite) + continue; + + val = rdma_readl(priv, s->reg_offset); + if (val == ~0) + rdma_writel(priv, 0, s->reg_offset); + break; } j += s->stat_sizeof; diff --git a/drivers/net/ethernet/broadcom/bcmsysport.h b/drivers/net/ethernet/broadcom/bcmsysport.h index 5af16e5f9ad0..335cf6631db5 100644 --- a/drivers/net/ethernet/broadcom/bcmsysport.h +++ b/drivers/net/ethernet/broadcom/bcmsysport.h @@ -290,6 +290,7 @@ struct bcm_rsb { #define RDMA_WRITE_PTR_HI 0x1010 #define RDMA_WRITE_PTR_LO 0x1014 +#define RDMA_OVFL_DISC_CNTR 0x1018 #define RDMA_PROD_INDEX 0x1018 #define RDMA_PROD_INDEX_MASK 0xffff @@ -565,6 +566,7 @@ struct bcm_sysport_mib { u32 rxchk_other_pkt_disc; u32 rbuf_ovflow_cnt; u32 rbuf_err_cnt; + u32 rdma_ovflow_cnt; u32 alloc_rx_buff_failed; u32 rx_dma_failed; u32 tx_dma_failed; @@ -581,6 +583,7 @@ enum bcm_sysport_stat_type { BCM_SYSPORT_STAT_RUNT, BCM_SYSPORT_STAT_RXCHK, BCM_SYSPORT_STAT_RBUF, + BCM_SYSPORT_STAT_RDMA, BCM_SYSPORT_STAT_SOFT, }; @@ -627,6 +630,14 @@ enum bcm_sysport_stat_type { .reg_offset = ofs, \ } +#define STAT_RDMA(str, m, ofs) { \ + .stat_string = str, \ + .stat_sizeof = sizeof(((struct bcm_sysport_priv *)0)->m), \ + .stat_offset = offsetof(struct bcm_sysport_priv, m), \ + .type = BCM_SYSPORT_STAT_RDMA, \ + .reg_offset = ofs, \ +} + /* TX bytes and packets */ #define NUM_SYSPORT_TXQ_STAT 2 From 0e84afe8ebfbb9eade3f4f6de4720887bf908e26 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Sat, 29 Oct 2022 15:45:16 +0000 Subject: [PATCH 0498/2002] net: dropreason: add SKB_CONSUMED reason This will allow to simply use in the future: kfree_skb_reason(skb, reason); Instead of repeating sequences like: if (dropped) kfree_skb_reason(skb, reason); else consume_skb(skb); For instance, following patch in the series is adding @reason to skb_release_data() and skb_release_all(), so that we can propagate a meaningful @reason whenever consume_skb()/kfree_skb() have to take care of a potential frag_list. Signed-off-by: Eric Dumazet Signed-off-by: Jakub Kicinski --- include/net/dropreason.h | 2 ++ net/core/skbuff.c | 6 +++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/include/net/dropreason.h b/include/net/dropreason.h index c1cbcdbaf149..0bd18c14dae0 100644 --- a/include/net/dropreason.h +++ b/include/net/dropreason.h @@ -80,6 +80,8 @@ enum skb_drop_reason { * @SKB_NOT_DROPPED_YET: skb is not dropped yet (used for no-drop case) */ SKB_NOT_DROPPED_YET = 0, + /** @SKB_CONSUMED: packet has been consumed */ + SKB_CONSUMED, /** @SKB_DROP_REASON_NOT_SPECIFIED: drop reason is not specified */ SKB_DROP_REASON_NOT_SPECIFIED, /** @SKB_DROP_REASON_NO_SOCKET: socket not found */ diff --git a/net/core/skbuff.c b/net/core/skbuff.c index 1d84a17eada5..7ce797cd121f 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -94,6 +94,7 @@ EXPORT_SYMBOL(sysctl_max_skb_frags); #undef FN #define FN(reason) [SKB_DROP_REASON_##reason] = #reason, const char * const drop_reasons[] = { + [SKB_CONSUMED] = "CONSUMED", DEFINE_DROP_REASON(FN, FN) }; EXPORT_SYMBOL(drop_reasons); @@ -894,7 +895,10 @@ kfree_skb_reason(struct sk_buff *skb, enum skb_drop_reason reason) DEBUG_NET_WARN_ON_ONCE(reason <= 0 || reason >= SKB_DROP_REASON_MAX); - trace_kfree_skb(skb, __builtin_return_address(0), reason); + if (reason == SKB_CONSUMED) + trace_consume_skb(skb); + else + trace_kfree_skb(skb, __builtin_return_address(0), reason); __kfree_skb(skb); } EXPORT_SYMBOL(kfree_skb_reason); From 511a3eda2f8d4719114ee3f2c781c37233bd171f Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Sat, 29 Oct 2022 15:45:17 +0000 Subject: [PATCH 0499/2002] net: dropreason: propagate drop_reason to skb_release_data() When an skb with a frag list is consumed, we currently pretend all skbs in the frag list were dropped. In order to fix this, add a @reason argument to skb_release_data() and skb_release_all(). Signed-off-by: Eric Dumazet Signed-off-by: Jakub Kicinski --- net/core/skbuff.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/net/core/skbuff.c b/net/core/skbuff.c index 7ce797cd121f..42a35b59fb1e 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -769,7 +769,7 @@ static void skb_free_head(struct sk_buff *skb) } } -static void skb_release_data(struct sk_buff *skb) +static void skb_release_data(struct sk_buff *skb, enum skb_drop_reason reason) { struct skb_shared_info *shinfo = skb_shinfo(skb); int i; @@ -792,7 +792,7 @@ static void skb_release_data(struct sk_buff *skb) free_head: if (shinfo->frag_list) - kfree_skb_list(shinfo->frag_list); + kfree_skb_list_reason(shinfo->frag_list, reason); skb_free_head(skb); exit: @@ -855,11 +855,11 @@ void skb_release_head_state(struct sk_buff *skb) } /* Free everything but the sk_buff shell. */ -static void skb_release_all(struct sk_buff *skb) +static void skb_release_all(struct sk_buff *skb, enum skb_drop_reason reason) { skb_release_head_state(skb); if (likely(skb->head)) - skb_release_data(skb); + skb_release_data(skb, reason); } /** @@ -873,7 +873,7 @@ static void skb_release_all(struct sk_buff *skb) void __kfree_skb(struct sk_buff *skb) { - skb_release_all(skb); + skb_release_all(skb, SKB_DROP_REASON_NOT_SPECIFIED); kfree_skbmem(skb); } EXPORT_SYMBOL(__kfree_skb); @@ -1056,7 +1056,7 @@ EXPORT_SYMBOL(consume_skb); void __consume_stateless_skb(struct sk_buff *skb) { trace_consume_skb(skb); - skb_release_data(skb); + skb_release_data(skb, SKB_CONSUMED); kfree_skbmem(skb); } @@ -1081,7 +1081,7 @@ static void napi_skb_cache_put(struct sk_buff *skb) void __kfree_skb_defer(struct sk_buff *skb) { - skb_release_all(skb); + skb_release_all(skb, SKB_DROP_REASON_NOT_SPECIFIED); napi_skb_cache_put(skb); } @@ -1119,7 +1119,7 @@ void napi_consume_skb(struct sk_buff *skb, int budget) return; } - skb_release_all(skb); + skb_release_all(skb, SKB_CONSUMED); napi_skb_cache_put(skb); } EXPORT_SYMBOL(napi_consume_skb); @@ -1250,7 +1250,7 @@ EXPORT_SYMBOL_GPL(alloc_skb_for_msg); */ struct sk_buff *skb_morph(struct sk_buff *dst, struct sk_buff *src) { - skb_release_all(dst); + skb_release_all(dst, SKB_CONSUMED); return __skb_clone(dst, src); } EXPORT_SYMBOL_GPL(skb_morph); @@ -1873,7 +1873,7 @@ int pskb_expand_head(struct sk_buff *skb, int nhead, int ntail, if (skb_has_frag_list(skb)) skb_clone_fraglist(skb); - skb_release_data(skb); + skb_release_data(skb, SKB_CONSUMED); } else { skb_free_head(skb); } @@ -6213,7 +6213,7 @@ static int pskb_carve_inside_header(struct sk_buff *skb, const u32 off, skb_frag_ref(skb, i); if (skb_has_frag_list(skb)) skb_clone_fraglist(skb); - skb_release_data(skb); + skb_release_data(skb, SKB_CONSUMED); } else { /* we can reuse existing recount- all we did was * relocate values @@ -6356,7 +6356,7 @@ static int pskb_carve_inside_nonlinear(struct sk_buff *skb, const u32 off, kfree(data); return -ENOMEM; } - skb_release_data(skb); + skb_release_data(skb, SKB_CONSUMED); skb->head = data; skb->head_frag = 0; From 4ecbb1c27c363686d11a241cd682a454a8454c2b Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Sat, 29 Oct 2022 15:45:18 +0000 Subject: [PATCH 0500/2002] net: dropreason: add SKB_DROP_REASON_DUP_FRAG This is used to track when a duplicate segment received by various reassembly units is dropped. Signed-off-by: Eric Dumazet Signed-off-by: Jakub Kicinski --- include/net/dropreason.h | 3 +++ net/ipv4/ip_fragment.c | 13 +++++++++---- net/ipv6/netfilter/nf_conntrack_reasm.c | 2 +- net/ipv6/reassembly.c | 13 +++++++++---- 4 files changed, 22 insertions(+), 9 deletions(-) diff --git a/include/net/dropreason.h b/include/net/dropreason.h index 0bd18c14dae0..602d555a5f83 100644 --- a/include/net/dropreason.h +++ b/include/net/dropreason.h @@ -68,6 +68,7 @@ FN(IP_INADDRERRORS) \ FN(IP_INNOROUTES) \ FN(PKT_TOO_BIG) \ + FN(DUP_FRAG) \ FNe(MAX) /** @@ -300,6 +301,8 @@ enum skb_drop_reason { * MTU) */ SKB_DROP_REASON_PKT_TOO_BIG, + /** @SKB_DROP_REASON_DUP_FRAG: duplicate fragment */ + SKB_DROP_REASON_DUP_FRAG, /** * @SKB_DROP_REASON_MAX: the maximum of drop reason, which shouldn't be * used as a real 'reason' diff --git a/net/ipv4/ip_fragment.c b/net/ipv4/ip_fragment.c index fb153569889e..676bd8d25955 100644 --- a/net/ipv4/ip_fragment.c +++ b/net/ipv4/ip_fragment.c @@ -278,10 +278,14 @@ static int ip_frag_queue(struct ipq *qp, struct sk_buff *skb) struct net_device *dev; unsigned int fragsize; int err = -ENOENT; + SKB_DR(reason); u8 ecn; - if (qp->q.flags & INET_FRAG_COMPLETE) + /* If reassembly is already done, @skb must be a duplicate frag. */ + if (qp->q.flags & INET_FRAG_COMPLETE) { + SKB_DR_SET(reason, DUP_FRAG); goto err; + } if (!(IPCB(skb)->flags & IPSKB_FRAG_COMPLETE) && unlikely(ip_frag_too_far(qp)) && @@ -382,8 +386,9 @@ static int ip_frag_queue(struct ipq *qp, struct sk_buff *skb) insert_error: if (err == IPFRAG_DUP) { - kfree_skb(skb); - return -EINVAL; + SKB_DR_SET(reason, DUP_FRAG); + err = -EINVAL; + goto err; } err = -EINVAL; __IP_INC_STATS(net, IPSTATS_MIB_REASM_OVERLAPS); @@ -391,7 +396,7 @@ discard_qp: inet_frag_kill(&qp->q); __IP_INC_STATS(net, IPSTATS_MIB_REASMFAILS); err: - kfree_skb(skb); + kfree_skb_reason(skb, reason); return err; } diff --git a/net/ipv6/netfilter/nf_conntrack_reasm.c b/net/ipv6/netfilter/nf_conntrack_reasm.c index 38db0064d661..d13240f13607 100644 --- a/net/ipv6/netfilter/nf_conntrack_reasm.c +++ b/net/ipv6/netfilter/nf_conntrack_reasm.c @@ -253,7 +253,7 @@ static int nf_ct_frag6_queue(struct frag_queue *fq, struct sk_buff *skb, if (err) { if (err == IPFRAG_DUP) { /* No error for duplicates, pretend they got queued. */ - kfree_skb(skb); + kfree_skb_reason(skb, SKB_DROP_REASON_DUP_FRAG); return -EINPROGRESS; } goto insert_error; diff --git a/net/ipv6/reassembly.c b/net/ipv6/reassembly.c index ff866f2a879e..5bc8a28e67f9 100644 --- a/net/ipv6/reassembly.c +++ b/net/ipv6/reassembly.c @@ -112,10 +112,14 @@ static int ip6_frag_queue(struct frag_queue *fq, struct sk_buff *skb, struct sk_buff *prev_tail; struct net_device *dev; int err = -ENOENT; + SKB_DR(reason); u8 ecn; - if (fq->q.flags & INET_FRAG_COMPLETE) + /* If reassembly is already done, @skb must be a duplicate frag. */ + if (fq->q.flags & INET_FRAG_COMPLETE) { + SKB_DR_SET(reason, DUP_FRAG); goto err; + } err = -EINVAL; offset = ntohs(fhdr->frag_off) & ~0x7; @@ -226,8 +230,9 @@ static int ip6_frag_queue(struct frag_queue *fq, struct sk_buff *skb, insert_error: if (err == IPFRAG_DUP) { - kfree_skb(skb); - return -EINVAL; + SKB_DR_SET(reason, DUP_FRAG); + err = -EINVAL; + goto err; } err = -EINVAL; __IP6_INC_STATS(net, ip6_dst_idev(skb_dst(skb)), @@ -237,7 +242,7 @@ discard_fq: __IP6_INC_STATS(net, ip6_dst_idev(skb_dst(skb)), IPSTATS_MIB_REASMFAILS); err: - kfree_skb(skb); + kfree_skb_reason(skb, reason); return err; } From 77adfd3a1d44c4730fd2af99b497e04ddc2b5837 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Sat, 29 Oct 2022 15:45:19 +0000 Subject: [PATCH 0501/2002] net: dropreason: add SKB_DROP_REASON_FRAG_REASM_TIMEOUT Used to track skbs freed after a timeout happened in a reassmbly unit. Passing a @reason argument to inet_frag_rbtree_purge() allows to use correct consumed status for frags that have been successfully re-assembled. Signed-off-by: Eric Dumazet Signed-off-by: Jakub Kicinski --- include/net/dropreason.h | 3 +++ include/net/inet_frag.h | 6 +++++- include/net/ipv6_frag.h | 3 ++- net/ipv4/inet_fragment.c | 14 ++++++++++---- net/ipv4/ip_fragment.c | 6 ++++-- 5 files changed, 24 insertions(+), 8 deletions(-) diff --git a/include/net/dropreason.h b/include/net/dropreason.h index 602d555a5f83..1d45a74148c3 100644 --- a/include/net/dropreason.h +++ b/include/net/dropreason.h @@ -69,6 +69,7 @@ FN(IP_INNOROUTES) \ FN(PKT_TOO_BIG) \ FN(DUP_FRAG) \ + FN(FRAG_REASM_TIMEOUT) \ FNe(MAX) /** @@ -303,6 +304,8 @@ enum skb_drop_reason { SKB_DROP_REASON_PKT_TOO_BIG, /** @SKB_DROP_REASON_DUP_FRAG: duplicate fragment */ SKB_DROP_REASON_DUP_FRAG, + /** @SKB_DROP_REASON_FRAG_REASM_TIMEOUT: fragment reassembly timeout */ + SKB_DROP_REASON_FRAG_REASM_TIMEOUT, /** * @SKB_DROP_REASON_MAX: the maximum of drop reason, which shouldn't be * used as a real 'reason' diff --git a/include/net/inet_frag.h b/include/net/inet_frag.h index 0b0876610553..b23ddec3cd5c 100644 --- a/include/net/inet_frag.h +++ b/include/net/inet_frag.h @@ -7,6 +7,7 @@ #include #include #include +#include /* Per netns frag queues directory */ struct fqdir { @@ -34,12 +35,14 @@ struct fqdir { * @INET_FRAG_LAST_IN: final fragment has arrived * @INET_FRAG_COMPLETE: frag queue has been processed and is due for destruction * @INET_FRAG_HASH_DEAD: inet_frag_kill() has not removed fq from rhashtable + * @INET_FRAG_DROP: if skbs must be dropped (instead of being consumed) */ enum { INET_FRAG_FIRST_IN = BIT(0), INET_FRAG_LAST_IN = BIT(1), INET_FRAG_COMPLETE = BIT(2), INET_FRAG_HASH_DEAD = BIT(3), + INET_FRAG_DROP = BIT(4), }; struct frag_v4_compare_key { @@ -139,7 +142,8 @@ void inet_frag_destroy(struct inet_frag_queue *q); struct inet_frag_queue *inet_frag_find(struct fqdir *fqdir, void *key); /* Free all skbs in the queue; return the sum of their truesizes. */ -unsigned int inet_frag_rbtree_purge(struct rb_root *root); +unsigned int inet_frag_rbtree_purge(struct rb_root *root, + enum skb_drop_reason reason); static inline void inet_frag_put(struct inet_frag_queue *q) { diff --git a/include/net/ipv6_frag.h b/include/net/ipv6_frag.h index 5052c66e22d2..7321ffe3a108 100644 --- a/include/net/ipv6_frag.h +++ b/include/net/ipv6_frag.h @@ -76,6 +76,7 @@ ip6frag_expire_frag_queue(struct net *net, struct frag_queue *fq) if (fq->q.flags & INET_FRAG_COMPLETE) goto out; + fq->q.flags |= INET_FRAG_DROP; inet_frag_kill(&fq->q); dev = dev_get_by_index_rcu(net, fq->iif); @@ -101,7 +102,7 @@ ip6frag_expire_frag_queue(struct net *net, struct frag_queue *fq) spin_unlock(&fq->q.lock); icmpv6_send(head, ICMPV6_TIME_EXCEED, ICMPV6_EXC_FRAGTIME, 0); - kfree_skb(head); + kfree_skb_reason(head, SKB_DROP_REASON_FRAG_REASM_TIMEOUT); goto out_rcu_unlock; out: diff --git a/net/ipv4/inet_fragment.c b/net/ipv4/inet_fragment.c index c9f9ac5013a7..7072fc0783ef 100644 --- a/net/ipv4/inet_fragment.c +++ b/net/ipv4/inet_fragment.c @@ -133,6 +133,7 @@ static void inet_frags_free_cb(void *ptr, void *arg) count = del_timer_sync(&fq->timer) ? 1 : 0; spin_lock_bh(&fq->lock); + fq->flags |= INET_FRAG_DROP; if (!(fq->flags & INET_FRAG_COMPLETE)) { fq->flags |= INET_FRAG_COMPLETE; count++; @@ -260,7 +261,8 @@ static void inet_frag_destroy_rcu(struct rcu_head *head) kmem_cache_free(f->frags_cachep, q); } -unsigned int inet_frag_rbtree_purge(struct rb_root *root) +unsigned int inet_frag_rbtree_purge(struct rb_root *root, + enum skb_drop_reason reason) { struct rb_node *p = rb_first(root); unsigned int sum = 0; @@ -274,7 +276,7 @@ unsigned int inet_frag_rbtree_purge(struct rb_root *root) struct sk_buff *next = FRAG_CB(skb)->next_frag; sum += skb->truesize; - kfree_skb(skb); + kfree_skb_reason(skb, reason); skb = next; } } @@ -284,17 +286,21 @@ EXPORT_SYMBOL(inet_frag_rbtree_purge); void inet_frag_destroy(struct inet_frag_queue *q) { - struct fqdir *fqdir; unsigned int sum, sum_truesize = 0; + enum skb_drop_reason reason; struct inet_frags *f; + struct fqdir *fqdir; WARN_ON(!(q->flags & INET_FRAG_COMPLETE)); + reason = (q->flags & INET_FRAG_DROP) ? + SKB_DROP_REASON_FRAG_REASM_TIMEOUT : + SKB_CONSUMED; WARN_ON(del_timer(&q->timer) != 0); /* Release all fragment data. */ fqdir = q->fqdir; f = fqdir->f; - sum_truesize = inet_frag_rbtree_purge(&q->rb_fragments); + sum_truesize = inet_frag_rbtree_purge(&q->rb_fragments, reason); sum = sum_truesize + f->qsize; call_rcu(&q->rcu, inet_frag_destroy_rcu); diff --git a/net/ipv4/ip_fragment.c b/net/ipv4/ip_fragment.c index 676bd8d25955..85e8113259c3 100644 --- a/net/ipv4/ip_fragment.c +++ b/net/ipv4/ip_fragment.c @@ -153,6 +153,7 @@ static void ip_expire(struct timer_list *t) if (qp->q.flags & INET_FRAG_COMPLETE) goto out; + qp->q.flags |= INET_FRAG_DROP; ipq_kill(qp); __IP_INC_STATS(net, IPSTATS_MIB_REASMFAILS); __IP_INC_STATS(net, IPSTATS_MIB_REASMTIMEOUT); @@ -194,7 +195,7 @@ out: spin_unlock(&qp->q.lock); out_rcu_unlock: rcu_read_unlock(); - kfree_skb(head); + kfree_skb_reason(head, SKB_DROP_REASON_FRAG_REASM_TIMEOUT); ipq_put(qp); } @@ -254,7 +255,8 @@ static int ip_frag_reinit(struct ipq *qp) return -ETIMEDOUT; } - sum_truesize = inet_frag_rbtree_purge(&qp->q.rb_fragments); + sum_truesize = inet_frag_rbtree_purge(&qp->q.rb_fragments, + SKB_DROP_REASON_NOT_SPECIFIED); sub_frag_mem_limit(qp->q.fqdir, sum_truesize); qp->q.flags = 0; From 3bdfb04f13ebdd4ae50fc5dc595663874781e48c Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Sat, 29 Oct 2022 15:45:20 +0000 Subject: [PATCH 0502/2002] net: dropreason: add SKB_DROP_REASON_FRAG_TOO_FAR IPv4 reassembly unit can decide to drop frags based on /proc/sys/net/ipv4/ipfrag_max_dist sysctl. Add a specific drop reason to track this specific and weird case. Signed-off-by: Eric Dumazet Signed-off-by: Jakub Kicinski --- include/net/dropreason.h | 6 ++++++ net/ipv4/ip_fragment.c | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/include/net/dropreason.h b/include/net/dropreason.h index 1d45a74148c3..70539288f995 100644 --- a/include/net/dropreason.h +++ b/include/net/dropreason.h @@ -70,6 +70,7 @@ FN(PKT_TOO_BIG) \ FN(DUP_FRAG) \ FN(FRAG_REASM_TIMEOUT) \ + FN(FRAG_TOO_FAR) \ FNe(MAX) /** @@ -306,6 +307,11 @@ enum skb_drop_reason { SKB_DROP_REASON_DUP_FRAG, /** @SKB_DROP_REASON_FRAG_REASM_TIMEOUT: fragment reassembly timeout */ SKB_DROP_REASON_FRAG_REASM_TIMEOUT, + /** + * @SKB_DROP_REASON_FRAG_TOO_FAR: ipv4 fragment too far. + * (/proc/sys/net/ipv4/ipfrag_max_dist) + */ + SKB_DROP_REASON_FRAG_TOO_FAR, /** * @SKB_DROP_REASON_MAX: the maximum of drop reason, which shouldn't be * used as a real 'reason' diff --git a/net/ipv4/ip_fragment.c b/net/ipv4/ip_fragment.c index 85e8113259c3..69c00ffdcf3e 100644 --- a/net/ipv4/ip_fragment.c +++ b/net/ipv4/ip_fragment.c @@ -256,7 +256,7 @@ static int ip_frag_reinit(struct ipq *qp) } sum_truesize = inet_frag_rbtree_purge(&qp->q.rb_fragments, - SKB_DROP_REASON_NOT_SPECIFIED); + SKB_DROP_REASON_FRAG_TOO_FAR); sub_frag_mem_limit(qp->q.fqdir, sum_truesize); qp->q.flags = 0; From 4feda7f317cb7e4b6fcdaffb0d1ad363ee28fdea Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Thu, 20 Oct 2022 13:25:49 +0800 Subject: [PATCH 0503/2002] wifi: rtw89: fw: adapt to new firmware format of dynamic header Since firmware size is limited, we create variant firmwares for variant application areas. To help driver to know firmware's capabilities, firmware dynamic header is introduced to have more information, such as firmware features and firmware compile flags. Since this driver rtw89 only uses single one specific firmware at runtime, this patch is just to ignore this dynamic header, not actually use the content. This patch can be backward compatible, and no this kind of firmware is added to linux-firmware yet, so I can prepare this in advance. Signed-off-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20221020052549.33783-1-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/fw.c | 22 +++++++++++++++++++--- drivers/net/wireless/realtek/rtw89/fw.h | 12 ++++++++++++ 2 files changed, 31 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/fw.c b/drivers/net/wireless/realtek/rtw89/fw.c index d21f87e25ae1..548b772eccfe 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.c +++ b/drivers/net/wireless/realtek/rtw89/fw.c @@ -85,15 +85,31 @@ static int rtw89_fw_hdr_parser(struct rtw89_dev *rtwdev, const u8 *fw, u32 len, { struct rtw89_fw_hdr_section_info *section_info; const u8 *fw_end = fw + len; + const u8 *fwdynhdr; const u8 *bin; + u32 base_hdr_len; u32 i; if (!info) return -EINVAL; info->section_num = GET_FW_HDR_SEC_NUM(fw); - info->hdr_len = RTW89_FW_HDR_SIZE + - info->section_num * RTW89_FW_SECTION_HDR_SIZE; + base_hdr_len = RTW89_FW_HDR_SIZE + + info->section_num * RTW89_FW_SECTION_HDR_SIZE; + info->dynamic_hdr_en = GET_FW_HDR_DYN_HDR(fw); + + if (info->dynamic_hdr_en) { + info->hdr_len = GET_FW_HDR_LEN(fw); + info->dynamic_hdr_len = info->hdr_len - base_hdr_len; + fwdynhdr = fw + base_hdr_len; + if (GET_FW_DYNHDR_LEN(fwdynhdr) != info->dynamic_hdr_len) { + rtw89_err(rtwdev, "[ERR]invalid fw dynamic header len\n"); + return -EINVAL; + } + } else { + info->hdr_len = base_hdr_len; + info->dynamic_hdr_len = 0; + } bin = fw + info->hdr_len; @@ -534,7 +550,7 @@ int rtw89_fw_download(struct rtw89_dev *rtwdev, enum rtw89_fw_type type) goto fwdl_err; } - ret = rtw89_fw_download_hdr(rtwdev, fw, info.hdr_len); + ret = rtw89_fw_download_hdr(rtwdev, fw, info.hdr_len - info.dynamic_hdr_len); if (ret) { ret = -EBUSY; goto fwdl_err; diff --git a/drivers/net/wireless/realtek/rtw89/fw.h b/drivers/net/wireless/realtek/rtw89/fw.h index 6ef392ef9c6f..8563efa5f641 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.h +++ b/drivers/net/wireless/realtek/rtw89/fw.h @@ -176,6 +176,8 @@ struct rtw89_fw_hdr_section_info { struct rtw89_fw_bin_info { u8 section_num; u32 hdr_len; + bool dynamic_hdr_en; + u32 dynamic_hdr_len; struct rtw89_fw_hdr_section_info section_info[FWDL_SECTION_MAX_NUM]; }; @@ -495,6 +497,8 @@ static inline void RTW89_SET_EDCA_PARAM(void *cmd, u32 val) le32_get_bits(*((const __le32 *)(fwhdr) + 1), GENMASK(23, 16)) #define GET_FW_HDR_SUBINDEX(fwhdr) \ le32_get_bits(*((const __le32 *)(fwhdr) + 1), GENMASK(31, 24)) +#define GET_FW_HDR_LEN(fwhdr) \ + le32_get_bits(*((const __le32 *)(fwhdr) + 3), GENMASK(23, 16)) #define GET_FW_HDR_MONTH(fwhdr) \ le32_get_bits(*((const __le32 *)(fwhdr) + 4), GENMASK(7, 0)) #define GET_FW_HDR_DATE(fwhdr) \ @@ -507,8 +511,16 @@ static inline void RTW89_SET_EDCA_PARAM(void *cmd, u32 val) le32_get_bits(*((const __le32 *)(fwhdr) + 5), GENMASK(31, 0)) #define GET_FW_HDR_SEC_NUM(fwhdr) \ le32_get_bits(*((const __le32 *)(fwhdr) + 6), GENMASK(15, 8)) +#define GET_FW_HDR_DYN_HDR(fwhdr) \ + le32_get_bits(*((const __le32 *)(fwhdr) + 7), BIT(16)) #define GET_FW_HDR_CMD_VERSERION(fwhdr) \ le32_get_bits(*((const __le32 *)(fwhdr) + 7), GENMASK(31, 24)) + +#define GET_FW_DYNHDR_LEN(fwdynhdr) \ + le32_get_bits(*((const __le32 *)(fwdynhdr)), GENMASK(31, 0)) +#define GET_FW_DYNHDR_COUNT(fwdynhdr) \ + le32_get_bits(*((const __le32 *)(fwdynhdr) + 1), GENMASK(31, 0)) + static inline void SET_FW_HDR_PART_SIZE(void *fwhdr, u32 val) { le32p_replace_bits((__le32 *)fwhdr + 7, val, GENMASK(15, 0)); From a29dba478b6f4e8d3fbfe0b2f097cad1e0b93cf6 Mon Sep 17 00:00:00 2001 From: Zong-Zhe Yang Date: Thu, 20 Oct 2022 13:27:01 +0800 Subject: [PATCH 0504/2002] wifi: rtw89: declare support bands with const They are just default declarations and we won't modify them directly. Instead, we actually do moification on their memdup now. So, they should be declared with const. Signed-off-by: Zong-Zhe Yang Signed-off-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20221020052702.33988-1-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/core.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c index a0fa9639b509..6d8243ee184c 100644 --- a/drivers/net/wireless/realtek/rtw89/core.c +++ b/drivers/net/wireless/realtek/rtw89/core.c @@ -171,7 +171,7 @@ bool rtw89_ra_report_to_bitrate(struct rtw89_dev *rtwdev, u8 rpt_rate, u16 *bitr return true; } -static struct ieee80211_supported_band rtw89_sband_2ghz = { +static const struct ieee80211_supported_band rtw89_sband_2ghz = { .band = NL80211_BAND_2GHZ, .channels = rtw89_channels_2ghz, .n_channels = ARRAY_SIZE(rtw89_channels_2ghz), @@ -181,7 +181,7 @@ static struct ieee80211_supported_band rtw89_sband_2ghz = { .vht_cap = {0}, }; -static struct ieee80211_supported_band rtw89_sband_5ghz = { +static const struct ieee80211_supported_band rtw89_sband_5ghz = { .band = NL80211_BAND_5GHZ, .channels = rtw89_channels_5ghz, .n_channels = ARRAY_SIZE(rtw89_channels_5ghz), @@ -193,7 +193,7 @@ static struct ieee80211_supported_band rtw89_sband_5ghz = { .vht_cap = {0}, }; -static struct ieee80211_supported_band rtw89_sband_6ghz = { +static const struct ieee80211_supported_band rtw89_sband_6ghz = { .band = NL80211_BAND_6GHZ, .channels = rtw89_channels_6ghz, .n_channels = ARRAY_SIZE(rtw89_channels_6ghz), From e69ae29e00cec66609bd555398aa4f59f2a9ae84 Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Thu, 20 Oct 2022 13:27:02 +0800 Subject: [PATCH 0505/2002] wifi: rtw89: 8852c: make table of RU mask constant This table must be constant, so change it as expectation. Signed-off-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20221020052702.33988-2-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/rtw8852c.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852c.c b/drivers/net/wireless/realtek/rtw89/rtw8852c.c index f6bcac826816..7e208a8fdf4b 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852c.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852c.c @@ -1683,12 +1683,12 @@ static void rtw8852c_set_channel_bb(struct rtw89_dev *rtwdev, const struct rtw89_chan *chan, enum rtw89_phy_idx phy_idx) { + static const u32 ru_alloc_msk[2] = {B_P80_AT_HIGH_FREQ_RU_ALLOC_PHY0, + B_P80_AT_HIGH_FREQ_RU_ALLOC_PHY1}; struct rtw89_hal *hal = &rtwdev->hal; bool cck_en = chan->band_type == RTW89_BAND_2G; u8 pri_ch_idx = chan->pri_ch_idx; u32 mask, reg; - u32 ru_alloc_msk[2] = {B_P80_AT_HIGH_FREQ_RU_ALLOC_PHY0, - B_P80_AT_HIGH_FREQ_RU_ALLOC_PHY1}; u8 ntx_path; if (chan->band_type == RTW89_BAND_2G) From 25f49617b5c9c9afa829030f14606be6351d4771 Mon Sep 17 00:00:00 2001 From: Eric Huang Date: Fri, 21 Oct 2022 17:16:01 +0800 Subject: [PATCH 0506/2002] wifi: rtw89: add BW info for both TX and RX in phy_info In order to debug performance issue intuitively, add bandwidth information into debugfs entry phy_info. After applying this patch, it looks like: TX rate [0]: HE 2SS MCS-11 GI:0.8 BW:80 (hw_rate=0x19b) ==> agg_wait=1 (3500) RX rate [0]: HE 2SS MCS-9 GI:0.8 BW:80 (hw_rate=0x199) Signed-off-by: Eric Huang Signed-off-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20221021091601.39884-1-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/debug.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/debug.c b/drivers/net/wireless/realtek/rtw89/debug.c index 8f27c883eeab..cd44d9aa37ca 100644 --- a/drivers/net/wireless/realtek/rtw89/debug.c +++ b/drivers/net/wireless/realtek/rtw89/debug.c @@ -51,6 +51,22 @@ struct rtw89_debugfs_priv { }; }; +static const u16 rtw89_rate_info_bw_to_mhz_map[] = { + [RATE_INFO_BW_20] = 20, + [RATE_INFO_BW_40] = 40, + [RATE_INFO_BW_80] = 80, + [RATE_INFO_BW_160] = 160, + [RATE_INFO_BW_320] = 320, +}; + +static u16 rtw89_rate_info_bw_to_mhz(enum rate_info_bw bw) +{ + if (bw < ARRAY_SIZE(rtw89_rate_info_bw_to_mhz_map)) + return rtw89_rate_info_bw_to_mhz_map[bw]; + + return 0; +} + static int rtw89_debugfs_single_show(struct seq_file *m, void *v) { struct rtw89_debugfs_priv *debugfs_priv = m->private; @@ -2379,6 +2395,7 @@ static void rtw89_sta_info_get_iter(void *data, struct ieee80211_sta *sta) else seq_printf(m, "Legacy %d", rate->legacy); seq_printf(m, "%s", rtwsta->ra_report.might_fallback_legacy ? " FB_G" : ""); + seq_printf(m, " BW:%u", rtw89_rate_info_bw_to_mhz(rate->bw)); seq_printf(m, "\t(hw_rate=0x%x)", rtwsta->ra_report.hw_rate); seq_printf(m, "\t==> agg_wait=%d (%d)\n", rtwsta->max_agg_wait, sta->deflink.agg.max_rc_amsdu_len); @@ -2404,6 +2421,7 @@ static void rtw89_sta_info_get_iter(void *data, struct ieee80211_sta *sta) he_gi_str[rate->he_gi] : "N/A"); break; } + seq_printf(m, " BW:%u", rtw89_rate_info_bw_to_mhz(status->bw)); seq_printf(m, "\t(hw_rate=0x%x)\n", rtwsta->rx_hw_rate); rssi = ewma_rssi_read(&rtwsta->avg_rssi); From 46245bc42aff5e67b0498fa365a4baeaaaaeda86 Mon Sep 17 00:00:00 2001 From: Zong-Zhe Yang Date: Fri, 21 Oct 2022 17:18:28 +0800 Subject: [PATCH 0507/2002] wifi: rtw89: check if sta's mac_id is valid under AP/TDLS Add boundary check of mac_id when adding sta under AP/TDLS. And, return -ENOSPC if the acquired mac_id is invalid. Signed-off-by: Zong-Zhe Yang Signed-off-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20221021091828.40157-1-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/core.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c index 6d8243ee184c..a27ca62274cc 100644 --- a/drivers/net/wireless/realtek/rtw89/core.c +++ b/drivers/net/wireless/realtek/rtw89/core.c @@ -2413,6 +2413,8 @@ int rtw89_core_sta_add(struct rtw89_dev *rtwdev, } else if (vif->type == NL80211_IFTYPE_AP || sta->tdls) { rtwsta->mac_id = rtw89_core_acquire_bit_map(rtwdev->mac_id_map, RTW89_MAX_MAC_ID_NUM); + if (rtwsta->mac_id == RTW89_MAX_MAC_ID_NUM) + return -ENOSPC; } return 0; From d9112042d9942648825d3ebe837dd33dbd7c6ddb Mon Sep 17 00:00:00 2001 From: Chih-Kang Chang Date: Thu, 27 Oct 2022 13:27:01 +0800 Subject: [PATCH 0508/2002] wifi: rtw89: collect and send RF parameters to firmware for WoWLAN For WoWLAN mode, we only collect and send RF parameters to Firmware without writing RF registers. So we add one function to practice it. Signed-off-by: Chih-Kang Chang Signed-off-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20221027052707.14605-2-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/core.c | 2 +- drivers/net/wireless/realtek/rtw89/phy.c | 31 +++++++++++++++++++++-- drivers/net/wireless/realtek/rtw89/phy.h | 2 +- 3 files changed, 31 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c index a27ca62274cc..0e4153b1188f 100644 --- a/drivers/net/wireless/realtek/rtw89/core.c +++ b/drivers/net/wireless/realtek/rtw89/core.c @@ -2962,7 +2962,7 @@ int rtw89_core_start(struct rtw89_dev *rtwdev) return ret; rtw89_phy_init_bb_reg(rtwdev); - rtw89_phy_init_rf_reg(rtwdev); + rtw89_phy_init_rf_reg(rtwdev, false); rtw89_btc_ntfy_init(rtwdev, BTC_MODE_NORMAL); diff --git a/drivers/net/wireless/realtek/rtw89/phy.c b/drivers/net/wireless/realtek/rtw89/phy.c index 35a0d190434a..dd4685698983 100644 --- a/drivers/net/wireless/realtek/rtw89/phy.c +++ b/drivers/net/wireless/realtek/rtw89/phy.c @@ -801,6 +801,11 @@ bool rtw89_phy_write_rf_v1(struct rtw89_dev *rtwdev, enum rtw89_rf_path rf_path, } EXPORT_SYMBOL(rtw89_phy_write_rf_v1); +static bool rtw89_chip_rf_v1(struct rtw89_dev *rtwdev) +{ + return rtwdev->chip->ops->write_rf == rtw89_phy_write_rf_v1; +} + static void rtw89_phy_bb_reset(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx) { @@ -1123,6 +1128,24 @@ out: return ret; } +static void rtw89_phy_config_rf_reg_noio(struct rtw89_dev *rtwdev, + const struct rtw89_reg2_def *reg, + enum rtw89_rf_path rf_path, + void *extra_data) +{ + u32 addr = reg->addr; + + if (addr == 0xfe || addr == 0xfd || addr == 0xfc || addr == 0xfb || + addr == 0xfa || addr == 0xf9) + return; + + if (rtw89_chip_rf_v1(rtwdev) && addr < 0x100) + return; + + rtw89_phy_cofig_rf_reg_store(rtwdev, reg, rf_path, + (struct rtw89_fw_h2c_rf_reg_info *)extra_data); +} + static void rtw89_phy_config_rf_reg(struct rtw89_dev *rtwdev, const struct rtw89_reg2_def *reg, enum rtw89_rf_path rf_path, @@ -1335,7 +1358,7 @@ static u32 rtw89_phy_nctl_poll(struct rtw89_dev *rtwdev) return rtw89_phy_read32(rtwdev, 0x8080); } -void rtw89_phy_init_rf_reg(struct rtw89_dev *rtwdev) +void rtw89_phy_init_rf_reg(struct rtw89_dev *rtwdev, bool noio) { void (*config)(struct rtw89_dev *rtwdev, const struct rtw89_reg2_def *reg, enum rtw89_rf_path rf_path, void *data); @@ -1351,7 +1374,11 @@ void rtw89_phy_init_rf_reg(struct rtw89_dev *rtwdev) for (path = RF_PATH_A; path < chip->rf_path_num; path++) { rf_table = chip->rf_table[path]; rf_reg_info->rf_path = rf_table->rf_path; - config = rf_table->config ? rf_table->config : rtw89_phy_config_rf_reg; + if (noio) + config = rtw89_phy_config_rf_reg_noio; + else + config = rf_table->config ? rf_table->config : + rtw89_phy_config_rf_reg; rtw89_phy_init_reg(rtwdev, rf_table, config, (void *)rf_reg_info); if (rtw89_phy_config_rf_reg_fw(rtwdev, rf_reg_info)) rtw89_warn(rtwdev, "rf path %d reg h2c config failed\n", diff --git a/drivers/net/wireless/realtek/rtw89/phy.h b/drivers/net/wireless/realtek/rtw89/phy.h index 995c13f6f906..dac69a02e868 100644 --- a/drivers/net/wireless/realtek/rtw89/phy.h +++ b/drivers/net/wireless/realtek/rtw89/phy.h @@ -500,7 +500,7 @@ bool rtw89_phy_write_rf(struct rtw89_dev *rtwdev, enum rtw89_rf_path rf_path, bool rtw89_phy_write_rf_v1(struct rtw89_dev *rtwdev, enum rtw89_rf_path rf_path, u32 addr, u32 mask, u32 data); void rtw89_phy_init_bb_reg(struct rtw89_dev *rtwdev); -void rtw89_phy_init_rf_reg(struct rtw89_dev *rtwdev); +void rtw89_phy_init_rf_reg(struct rtw89_dev *rtwdev, bool noio); void rtw89_phy_config_rf_reg_v1(struct rtw89_dev *rtwdev, const struct rtw89_reg2_def *reg, enum rtw89_rf_path rf_path, From 5f05bdb0a770b5d03d3453c0b0743bb3dcd1a2ba Mon Sep 17 00:00:00 2001 From: Chih-Kang Chang Date: Thu, 27 Oct 2022 13:27:02 +0800 Subject: [PATCH 0509/2002] wifi: rtw89: move enable_cpu/disable_cpu into fw_download For WoWLAN mode, we need to download WoWLAN firmware by calling fw_download(). Another, to disable/enable WiFi CPU is needed before calling fw_download. Since Firmware runs on WiFi CPU, it is intuitive to combine enable_cpu/disable_cpu functions into fw_download. Signed-off-by: Chih-Kang Chang Signed-off-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20221027052707.14605-3-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/fw.c | 5 +++++ drivers/net/wireless/realtek/rtw89/mac.c | 10 ++-------- drivers/net/wireless/realtek/rtw89/mac.h | 2 ++ 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/fw.c b/drivers/net/wireless/realtek/rtw89/fw.c index 548b772eccfe..8b82ea68f6c2 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.c +++ b/drivers/net/wireless/realtek/rtw89/fw.c @@ -531,6 +531,11 @@ int rtw89_fw_download(struct rtw89_dev *rtwdev, enum rtw89_fw_type type) u8 val; int ret; + rtw89_mac_disable_cpu(rtwdev); + ret = rtw89_mac_enable_cpu(rtwdev, 0, true); + if (ret) + return ret; + if (!fw || !len) { rtw89_err(rtwdev, "fw type %d isn't recognized\n", type); return -ENOENT; diff --git a/drivers/net/wireless/realtek/rtw89/mac.c b/drivers/net/wireless/realtek/rtw89/mac.c index 3531a859d6c8..de9708eb9726 100644 --- a/drivers/net/wireless/realtek/rtw89/mac.c +++ b/drivers/net/wireless/realtek/rtw89/mac.c @@ -3116,7 +3116,7 @@ static void rtw89_disable_fw_watchdog(struct rtw89_dev *rtwdev) rtw89_mac_mem_write(rtwdev, R_AX_WDT_STATUS, val32, RTW89_MAC_MEM_CPU_LOCAL); } -static void rtw89_mac_disable_cpu(struct rtw89_dev *rtwdev) +void rtw89_mac_disable_cpu(struct rtw89_dev *rtwdev) { clear_bit(RTW89_FLAG_FW_RDY, rtwdev->flags); @@ -3131,8 +3131,7 @@ static void rtw89_mac_disable_cpu(struct rtw89_dev *rtwdev) rtw89_write32_set(rtwdev, R_AX_PLATFORM_ENABLE, B_AX_PLATFORM_EN); } -static int rtw89_mac_enable_cpu(struct rtw89_dev *rtwdev, u8 boot_reason, - bool dlfw) +int rtw89_mac_enable_cpu(struct rtw89_dev *rtwdev, u8 boot_reason, bool dlfw) { u32 val; int ret; @@ -3271,11 +3270,6 @@ int rtw89_mac_partial_init(struct rtw89_dev *rtwdev) return ret; } - rtw89_mac_disable_cpu(rtwdev); - ret = rtw89_mac_enable_cpu(rtwdev, 0, true); - if (ret) - return ret; - ret = rtw89_fw_download(rtwdev, RTW89_FW_NORMAL); if (ret) return ret; diff --git a/drivers/net/wireless/realtek/rtw89/mac.h b/drivers/net/wireless/realtek/rtw89/mac.h index a6cbafb75a2b..6e03f5e4ae24 100644 --- a/drivers/net/wireless/realtek/rtw89/mac.h +++ b/drivers/net/wireless/realtek/rtw89/mac.h @@ -815,6 +815,8 @@ int rtw89_mac_port_update(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif); void rtw89_mac_set_he_obss_narrow_bw_ru(struct rtw89_dev *rtwdev, struct ieee80211_vif *vif); int rtw89_mac_remove_vif(struct rtw89_dev *rtwdev, struct rtw89_vif *vif); +void rtw89_mac_disable_cpu(struct rtw89_dev *rtwdev); +int rtw89_mac_enable_cpu(struct rtw89_dev *rtwdev, u8 boot_reason, bool dlfw); int rtw89_mac_enable_bb_rf(struct rtw89_dev *rtwdev); int rtw89_mac_disable_bb_rf(struct rtw89_dev *rtwdev); From 7a68ec3da79ea702fdeeb94c31f743fd37446a32 Mon Sep 17 00:00:00 2001 From: Chih-Kang Chang Date: Thu, 27 Oct 2022 13:27:03 +0800 Subject: [PATCH 0510/2002] wifi: rtw89: add function to adjust and restore PLE quota PLE RX quota, which is the setting of RX buffer, is needed to be adjusted dynamically for WoWLAN mode, and restored when back to normal mode. The action is not needed for rtw8852c chip. Signed-off-by: Chih-Kang Chang Signed-off-by: Chin-Yen Lee Signed-off-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20221027052707.14605-4-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/core.h | 1 + drivers/net/wireless/realtek/rtw89/mac.c | 32 +++++++++++++++++++ drivers/net/wireless/realtek/rtw89/mac.h | 2 ++ drivers/net/wireless/realtek/rtw89/rtw8852a.c | 4 +++ 4 files changed, 39 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index 90bf7bdb6062..10ccb047d6a0 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -2389,6 +2389,7 @@ enum rtw89_dma_ch { enum rtw89_qta_mode { RTW89_QTA_SCC, RTW89_QTA_DLFW, + RTW89_QTA_WOW, /* keep last */ RTW89_QTA_INVALID, diff --git a/drivers/net/wireless/realtek/rtw89/mac.c b/drivers/net/wireless/realtek/rtw89/mac.c index de9708eb9726..061244a28f4b 100644 --- a/drivers/net/wireless/realtek/rtw89/mac.c +++ b/drivers/net/wireless/realtek/rtw89/mac.c @@ -1306,6 +1306,8 @@ const struct rtw89_mac_size_set rtw89_mac_size = { .ple_qt47 = {525, 0, 32, 20, 1034, 13, 1199, 0, 1053, 62, 160, 1037,}, /* PCIE 64 */ .ple_qt58 = {147, 0, 16, 20, 157, 13, 229, 0, 172, 14, 24, 0,}, + /* 8852A PCIE WOW */ + .ple_qt_52a_wow = {264, 0, 32, 20, 64, 13, 1005, 0, 64, 128, 120,}, }; EXPORT_SYMBOL(rtw89_mac_size); @@ -1478,6 +1480,36 @@ static void ple_quota_cfg(struct rtw89_dev *rtwdev, SET_QUOTA(tx_rpt, PLE, 11); } +int rtw89_mac_resize_ple_rx_quota(struct rtw89_dev *rtwdev, bool wow) +{ + const struct rtw89_ple_quota *min_cfg, *max_cfg; + const struct rtw89_dle_mem *cfg; + u32 val; + + if (rtwdev->chip->chip_id == RTL8852C) + return 0; + + if (rtwdev->mac.qta_mode != RTW89_QTA_SCC) { + rtw89_err(rtwdev, "[ERR]support SCC mode only\n"); + return -EINVAL; + } + + if (wow) + cfg = get_dle_mem_cfg(rtwdev, RTW89_QTA_WOW); + else + cfg = get_dle_mem_cfg(rtwdev, RTW89_QTA_SCC); + if (!cfg) { + rtw89_err(rtwdev, "[ERR]get_dle_mem_cfg\n"); + return -EINVAL; + } + + min_cfg = cfg->ple_min_qt; + max_cfg = cfg->ple_max_qt; + SET_QUOTA(cma0_dma, PLE, 6); + SET_QUOTA(cma1_dma, PLE, 7); + + return 0; +} #undef SET_QUOTA static void dle_quota_cfg(struct rtw89_dev *rtwdev, diff --git a/drivers/net/wireless/realtek/rtw89/mac.h b/drivers/net/wireless/realtek/rtw89/mac.h index 6e03f5e4ae24..20211c4e62db 100644 --- a/drivers/net/wireless/realtek/rtw89/mac.h +++ b/drivers/net/wireless/realtek/rtw89/mac.h @@ -719,6 +719,7 @@ struct rtw89_mac_size_set { const struct rtw89_ple_quota ple_qt46; const struct rtw89_ple_quota ple_qt47; const struct rtw89_ple_quota ple_qt58; + const struct rtw89_ple_quota ple_qt_52a_wow; }; extern const struct rtw89_mac_size_set rtw89_mac_size; @@ -1026,5 +1027,6 @@ void rtw89_mac_pkt_drop_vif(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif); u16 rtw89_mac_dle_buf_req(struct rtw89_dev *rtwdev, u16 buf_len, bool wd); int rtw89_mac_set_cpuio(struct rtw89_dev *rtwdev, struct rtw89_cpuio_ctrl *ctrl_para, bool wd); +int rtw89_mac_resize_ple_rx_quota(struct rtw89_dev *rtwdev, bool wow); #endif diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852a.c b/drivers/net/wireless/realtek/rtw89/rtw8852a.c index b5aa8697a098..375e84f5fe5c 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852a.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852a.c @@ -48,6 +48,10 @@ static const struct rtw89_dle_mem rtw8852a_dle_mem_pcie[] = { &rtw89_mac_size.ple_size0, &rtw89_mac_size.wde_qt0, &rtw89_mac_size.wde_qt0, &rtw89_mac_size.ple_qt4, &rtw89_mac_size.ple_qt5}, + [RTW89_QTA_WOW] = {RTW89_QTA_WOW, &rtw89_mac_size.wde_size0, + &rtw89_mac_size.ple_size0, &rtw89_mac_size.wde_qt0, + &rtw89_mac_size.wde_qt0, &rtw89_mac_size.ple_qt4, + &rtw89_mac_size.ple_qt_52a_wow}, [RTW89_QTA_DLFW] = {RTW89_QTA_DLFW, &rtw89_mac_size.wde_size4, &rtw89_mac_size.ple_size4, &rtw89_mac_size.wde_qt4, &rtw89_mac_size.wde_qt4, &rtw89_mac_size.ple_qt13, From 41d567699283b86ce7443a5bf07114f1bde8e203 Mon Sep 17 00:00:00 2001 From: Chih-Kang Chang Date: Thu, 27 Oct 2022 13:27:04 +0800 Subject: [PATCH 0511/2002] wifi: rtw89: add drop tx packet function When entering WoWLAN mode, we need to drop all transmit packets, including those in mac buffer, to avoid memory leakage, so implement the drop_tx function. Signed-off-by: Chih-Kang Chang Signed-off-by: Chin-Yen Lee Signed-off-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20221027052707.14605-5-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/core.h | 3 + drivers/net/wireless/realtek/rtw89/fw.c | 9 +++ drivers/net/wireless/realtek/rtw89/fw.h | 20 +++++ drivers/net/wireless/realtek/rtw89/mac.c | 75 +++++++++++++++++++ drivers/net/wireless/realtek/rtw89/mac.h | 13 ++++ drivers/net/wireless/realtek/rtw89/reg.h | 13 ++++ drivers/net/wireless/realtek/rtw89/rtw8852a.c | 2 + drivers/net/wireless/realtek/rtw89/rtw8852c.c | 2 + 8 files changed, 137 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index 10ccb047d6a0..6b6bb3260fff 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -2624,6 +2624,8 @@ struct rtw89_chip_info { u32 rsvd_ple_ofst; const struct rtw89_hfc_param_ini *hfc_param_ini; const struct rtw89_dle_mem *dle_mem; + u8 wde_qempty_acq_num; + u8 wde_qempty_mgq_sel; u32 rf_base_addr[2]; u8 support_chanctx_num; u8 support_bands; @@ -2949,6 +2951,7 @@ struct rtw89_pkt_drop_params { u8 port; u8 mbssid; bool tf_trs; + u32 macid_band_sel[4]; }; struct rtw89_pkt_stat { diff --git a/drivers/net/wireless/realtek/rtw89/fw.c b/drivers/net/wireless/realtek/rtw89/fw.c index 8b82ea68f6c2..cd2c84bd2c26 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.c +++ b/drivers/net/wireless/realtek/rtw89/fw.c @@ -2900,6 +2900,7 @@ int rtw89_fw_h2c_pkt_drop(struct rtw89_dev *rtwdev, case RTW89_PKT_DROP_SEL_MACID_BK_ONCE: case RTW89_PKT_DROP_SEL_MACID_VI_ONCE: case RTW89_PKT_DROP_SEL_MACID_VO_ONCE: + case RTW89_PKT_DROP_SEL_BAND_ONCE: break; default: rtw89_debug(rtwdev, RTW89_DBG_FW, @@ -2915,6 +2916,14 @@ int rtw89_fw_h2c_pkt_drop(struct rtw89_dev *rtwdev, RTW89_SET_FWCMD_PKT_DROP_PORT(skb->data, params->port); RTW89_SET_FWCMD_PKT_DROP_MBSSID(skb->data, params->mbssid); RTW89_SET_FWCMD_PKT_DROP_ROLE_A_INFO_TF_TRS(skb->data, params->tf_trs); + RTW89_SET_FWCMD_PKT_DROP_MACID_BAND_SEL_0(skb->data, + params->macid_band_sel[0]); + RTW89_SET_FWCMD_PKT_DROP_MACID_BAND_SEL_1(skb->data, + params->macid_band_sel[1]); + RTW89_SET_FWCMD_PKT_DROP_MACID_BAND_SEL_2(skb->data, + params->macid_band_sel[2]); + RTW89_SET_FWCMD_PKT_DROP_MACID_BAND_SEL_3(skb->data, + params->macid_band_sel[3]); rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C, H2C_CAT_MAC, diff --git a/drivers/net/wireless/realtek/rtw89/fw.h b/drivers/net/wireless/realtek/rtw89/fw.h index 8563efa5f641..3845581d5d28 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.h +++ b/drivers/net/wireless/realtek/rtw89/fw.h @@ -1873,6 +1873,26 @@ static inline void RTW89_SET_FWCMD_PKT_DROP_ROLE_A_INFO_TF_TRS(void *cmd, u32 va le32p_replace_bits((__le32 *)cmd + 1, val, GENMASK(15, 8)); } +static inline void RTW89_SET_FWCMD_PKT_DROP_MACID_BAND_SEL_0(void *cmd, u32 val) +{ + le32p_replace_bits((__le32 *)cmd + 2, val, GENMASK(31, 0)); +} + +static inline void RTW89_SET_FWCMD_PKT_DROP_MACID_BAND_SEL_1(void *cmd, u32 val) +{ + le32p_replace_bits((__le32 *)cmd + 3, val, GENMASK(31, 0)); +} + +static inline void RTW89_SET_FWCMD_PKT_DROP_MACID_BAND_SEL_2(void *cmd, u32 val) +{ + le32p_replace_bits((__le32 *)cmd + 4, val, GENMASK(31, 0)); +} + +static inline void RTW89_SET_FWCMD_PKT_DROP_MACID_BAND_SEL_3(void *cmd, u32 val) +{ + le32p_replace_bits((__le32 *)cmd + 5, val, GENMASK(31, 0)); +} + enum rtw89_btc_btf_h2c_class { BTFC_SET = 0x10, BTFC_GET = 0x11, diff --git a/drivers/net/wireless/realtek/rtw89/mac.c b/drivers/net/wireless/realtek/rtw89/mac.c index 061244a28f4b..5b706611d533 100644 --- a/drivers/net/wireless/realtek/rtw89/mac.c +++ b/drivers/net/wireless/realtek/rtw89/mac.c @@ -1335,6 +1335,60 @@ static const struct rtw89_dle_mem *get_dle_mem_cfg(struct rtw89_dev *rtwdev, return cfg; } +static bool mac_is_txq_empty(struct rtw89_dev *rtwdev) +{ + struct rtw89_mac_dle_dfi_qempty qempty; + u32 qnum, qtmp, val32, msk32; + int i, j, ret; + + qnum = rtwdev->chip->wde_qempty_acq_num; + qempty.dle_type = DLE_CTRL_TYPE_WDE; + + for (i = 0; i < qnum; i++) { + qempty.grpsel = i; + ret = dle_dfi_qempty(rtwdev, &qempty); + if (ret) { + rtw89_warn(rtwdev, "dle dfi acq empty %d\n", ret); + return false; + } + qtmp = qempty.qempty; + for (j = 0 ; j < QEMP_ACQ_GRP_MACID_NUM; j++) { + val32 = FIELD_GET(QEMP_ACQ_GRP_QSEL_MASK, qtmp); + if (val32 != QEMP_ACQ_GRP_QSEL_MASK) + return false; + qtmp >>= QEMP_ACQ_GRP_QSEL_SH; + } + } + + qempty.grpsel = rtwdev->chip->wde_qempty_mgq_sel; + ret = dle_dfi_qempty(rtwdev, &qempty); + if (ret) { + rtw89_warn(rtwdev, "dle dfi mgq empty %d\n", ret); + return false; + } + msk32 = B_CMAC0_MGQ_NORMAL | B_CMAC0_MGQ_NO_PWRSAV | B_CMAC0_CPUMGQ; + if ((qempty.qempty & msk32) != msk32) + return false; + + if (rtwdev->dbcc_en) { + msk32 |= B_CMAC1_MGQ_NORMAL | B_CMAC1_MGQ_NO_PWRSAV | B_CMAC1_CPUMGQ; + if ((qempty.qempty & msk32) != msk32) + return false; + } + + msk32 = B_AX_WDE_EMPTY_QTA_DMAC_WLAN_CPU | B_AX_WDE_EMPTY_QTA_DMAC_DATA_CPU | + B_AX_PLE_EMPTY_QTA_DMAC_WLAN_CPU | B_AX_PLE_EMPTY_QTA_DMAC_H2C | + B_AX_WDE_EMPTY_QUE_OTHERS | B_AX_PLE_EMPTY_QUE_DMAC_MPDU_TX | + B_AX_WDE_EMPTY_QTA_DMAC_CPUIO | B_AX_PLE_EMPTY_QTA_DMAC_CPUIO | + B_AX_WDE_EMPTY_QUE_DMAC_PKTIN | B_AX_WDE_EMPTY_QTA_DMAC_HIF | + B_AX_PLE_EMPTY_QUE_DMAC_SEC_TX | B_AX_WDE_EMPTY_QTA_DMAC_PKTIN | + B_AX_PLE_EMPTY_QTA_DMAC_B0_TXPL | B_AX_PLE_EMPTY_QTA_DMAC_B1_TXPL | + B_AX_PLE_EMPTY_QTA_DMAC_MPDU_TX; + val32 = rtw89_read32(rtwdev, R_AX_DLE_EMPTY0); + + return (val32 & msk32) == msk32; +} + static inline u32 dle_used_size(const struct rtw89_dle_size *wde, const struct rtw89_dle_size *ple) { @@ -4893,3 +4947,24 @@ void rtw89_mac_pkt_drop_vif(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif) rtw89_mac_pkt_drop_vif_iter, rtwvif); } + +int rtw89_mac_ptk_drop_by_band_and_wait(struct rtw89_dev *rtwdev, + enum rtw89_mac_idx band) +{ + struct rtw89_pkt_drop_params params = {0}; + bool empty; + int i, ret = 0, try_cnt = 3; + + params.mac_band = band; + params.sel = RTW89_PKT_DROP_SEL_BAND_ONCE; + + for (i = 0; i < try_cnt; i++) { + ret = read_poll_timeout(mac_is_txq_empty, empty, empty, 50, + 50000, false, rtwdev); + if (ret) + rtw89_fw_h2c_pkt_drop(rtwdev, ¶ms); + else + return 0; + } + return ret; +} diff --git a/drivers/net/wireless/realtek/rtw89/mac.h b/drivers/net/wireless/realtek/rtw89/mac.h index 20211c4e62db..e3b4c7830f44 100644 --- a/drivers/net/wireless/realtek/rtw89/mac.h +++ b/drivers/net/wireless/realtek/rtw89/mac.h @@ -420,6 +420,17 @@ enum rtw89_mac_bf_rrsc_rate { #define S_AX_PLE_PAGE_SEL_128 1 #define S_AX_PLE_PAGE_SEL_256 2 +#define B_CMAC0_MGQ_NORMAL BIT(2) +#define B_CMAC0_MGQ_NO_PWRSAV BIT(3) +#define B_CMAC0_CPUMGQ BIT(4) +#define B_CMAC1_MGQ_NORMAL BIT(10) +#define B_CMAC1_MGQ_NO_PWRSAV BIT(11) +#define B_CMAC1_CPUMGQ BIT(12) + +#define QEMP_ACQ_GRP_MACID_NUM 8 +#define QEMP_ACQ_GRP_QSEL_SH 4 +#define QEMP_ACQ_GRP_QSEL_MASK 0xF + #define SDIO_LOCAL_BASE_ADDR 0x80000000 #define PWR_CMD_WRITE 0 @@ -1028,5 +1039,7 @@ u16 rtw89_mac_dle_buf_req(struct rtw89_dev *rtwdev, u16 buf_len, bool wd); int rtw89_mac_set_cpuio(struct rtw89_dev *rtwdev, struct rtw89_cpuio_ctrl *ctrl_para, bool wd); int rtw89_mac_resize_ple_rx_quota(struct rtw89_dev *rtwdev, bool wow); +int rtw89_mac_ptk_drop_by_band_and_wait(struct rtw89_dev *rtwdev, + enum rtw89_mac_idx band); #endif diff --git a/drivers/net/wireless/realtek/rtw89/reg.h b/drivers/net/wireless/realtek/rtw89/reg.h index 2b938d11d238..33f2b67bfca3 100644 --- a/drivers/net/wireless/realtek/rtw89/reg.h +++ b/drivers/net/wireless/realtek/rtw89/reg.h @@ -545,6 +545,19 @@ #define B_AX_WDE_EMPTY_QUE_CMAC0_MBH BIT(1) #define B_AX_WDE_EMPTY_QUE_CMAC0_ALL_AC BIT(0) +#define R_AX_DLE_EMPTY1 0x8434 +#define B_AX_PLE_EMPTY_QTA_DMAC_WDRLS BIT(20) +#define B_AX_PLE_EMPTY_QTA_CMAC1_DMA_BBRPT BIT(19) +#define B_AX_PLE_EMPTY_QTA_CMAC1_DMA_RX BIT(18) +#define B_AX_PLE_EMPTY_QTA_CMAC0_DMA_RX BIT(17) +#define B_AX_PLE_EMPTY_QTA_DMAC_C2H BIT(16) +#define B_AX_PLE_EMPTY_QUE_DMAC_PLRLS BIT(5) +#define B_AX_PLE_EMPTY_QUE_DMAC_CPUIO BIT(4) +#define B_AX_PLE_EMPTY_QUE_DMAC_SEC_RX BIT(3) +#define B_AX_PLE_EMPTY_QUE_DMAC_MPDU_RX BIT(2) +#define B_AX_PLE_EMPTY_QUE_DMAC_HDP BIT(1) +#define B_AX_WDE_EMPTY_QUE_DMAC_WDRLS BIT(0) + #define R_AX_DMAC_ERR_IMR 0x8520 #define B_AX_DLE_CPUIO_ERR_INT_EN BIT(10) #define B_AX_APB_BRIDGE_ERR_INT_EN BIT(9) diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852a.c b/drivers/net/wireless/realtek/rtw89/rtw8852a.c index 375e84f5fe5c..7995d720dc92 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852a.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852a.c @@ -2049,6 +2049,8 @@ const struct rtw89_chip_info rtw8852a_chip_info = { .rsvd_ple_ofst = 0x6f800, .hfc_param_ini = rtw8852a_hfc_param_ini_pcie, .dle_mem = rtw8852a_dle_mem_pcie, + .wde_qempty_acq_num = 16, + .wde_qempty_mgq_sel = 16, .rf_base_addr = {0xc000, 0xd000}, .pwr_on_seq = pwr_on_seq_8852a, .pwr_off_seq = pwr_off_seq_8852a, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852c.c b/drivers/net/wireless/realtek/rtw89/rtw8852c.c index 7e208a8fdf4b..93332feabc44 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852c.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852c.c @@ -2855,6 +2855,8 @@ const struct rtw89_chip_info rtw8852c_chip_info = { .rsvd_ple_ofst = 0x6f800, .hfc_param_ini = rtw8852c_hfc_param_ini_pcie, .dle_mem = rtw8852c_dle_mem_pcie, + .wde_qempty_acq_num = 16, + .wde_qempty_mgq_sel = 16, .rf_base_addr = {0xe000, 0xf000}, .pwr_on_seq = NULL, .pwr_off_seq = NULL, From ee88d748f1ace450cc0e8ca6a93cb11e7e8d6ad9 Mon Sep 17 00:00:00 2001 From: Chin-Yen Lee Date: Thu, 27 Oct 2022 13:27:05 +0800 Subject: [PATCH 0512/2002] wifi: rtw89: add related H2C for WoWLAN mode In this patch we define some H2C, which will be called during suspend flow, to enable WoWLAN function provided by WoWLAN firmware. These H2C includes keep alive used to send null packet to AP periodically to avoid being disconnected by AP, disconnect detection used to configure how we check if AP is offline, wake up control used to decide which WiFi events could trigger resume flow, and global control used to enable WoWLAN function. Signed-off-by: Chin-Yen Lee Signed-off-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20221027052707.14605-6-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/core.h | 30 +++ drivers/net/wireless/realtek/rtw89/fw.c | 229 ++++++++++++++++++++++ drivers/net/wireless/realtek/rtw89/fw.h | 160 +++++++++++++++ 3 files changed, 419 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index 6b6bb3260fff..d20cb3ae89e9 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -477,6 +477,20 @@ enum rtw89_regulation_type { RTW89_REGD_NUM, }; +enum rtw89_fw_pkt_ofld_type { + RTW89_PKT_OFLD_TYPE_PROBE_RSP = 0, + RTW89_PKT_OFLD_TYPE_PS_POLL = 1, + RTW89_PKT_OFLD_TYPE_NULL_DATA = 2, + RTW89_PKT_OFLD_TYPE_QOS_NULL = 3, + RTW89_PKT_OFLD_TYPE_CTS2SELF = 4, + RTW89_PKT_OFLD_TYPE_ARP_RSP = 5, + RTW89_PKT_OFLD_TYPE_NDP = 6, + RTW89_PKT_OFLD_TYPE_EAPOL_KEY = 7, + RTW89_PKT_OFLD_TYPE_SA_QUERY = 8, + RTW89_PKT_OFLD_TYPE_PROBE_REQ = 12, + RTW89_PKT_OFLD_TYPE_NUM, +}; + struct rtw89_txpwr_byrate { s8 cck[RTW89_RATE_CCK_MAX]; s8 ofdm[RTW89_RATE_OFDM_MAX]; @@ -636,6 +650,13 @@ enum rtw89_sc_offset { RTW89_SC_40_LOWER = 10, }; +enum rtw89_wow_flags { + RTW89_WOW_FLAG_EN_MAGIC_PKT, + RTW89_WOW_FLAG_EN_REKEY_PKT, + RTW89_WOW_FLAG_EN_DISCONNECT, + RTW89_WOW_FLAG_NUM, +}; + struct rtw89_chan { u8 channel; u8 primary_channel; @@ -3453,6 +3474,13 @@ struct rtw89_phy_efuse_gain { s8 comp[RF_PATH_MAX][RTW89_SUBBAND_NR]; /* S(8, 0) */ }; +struct rtw89_wow_param { + struct ieee80211_vif *wow_vif; + DECLARE_BITMAP(flags, RTW89_WOW_FLAG_NUM); + u8 pattern_cnt; + struct list_head pkt_list; +}; + struct rtw89_dev { struct ieee80211_hw *hw; struct device *dev; @@ -3541,6 +3569,8 @@ struct rtw89_dev { enum rtw89_ps_mode ps_mode; bool lps_enabled; + struct rtw89_wow_param wow; + /* napi structure */ struct net_device netdev; struct napi_struct napi; diff --git a/drivers/net/wireless/realtek/rtw89/fw.c b/drivers/net/wireless/realtek/rtw89/fw.c index cd2c84bd2c26..2cbcf23409db 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.c +++ b/drivers/net/wireless/realtek/rtw89/fw.c @@ -869,6 +869,56 @@ fail: return ret; } +static int rtw89_fw_h2c_add_wow_fw_ofld(struct rtw89_dev *rtwdev, + struct rtw89_vif *rtwvif, + enum rtw89_fw_pkt_ofld_type type, + u8 *id) +{ + struct ieee80211_vif *vif = rtwvif_to_vif(rtwvif); + struct rtw89_wow_param *rtw_wow = &rtwdev->wow; + struct rtw89_pktofld_info *info; + struct sk_buff *skb; + int ret; + + info = kzalloc(sizeof(*info), GFP_KERNEL); + if (!info) + return -ENOMEM; + + switch (type) { + case RTW89_PKT_OFLD_TYPE_PS_POLL: + skb = ieee80211_pspoll_get(rtwdev->hw, vif); + break; + case RTW89_PKT_OFLD_TYPE_PROBE_RSP: + skb = ieee80211_proberesp_get(rtwdev->hw, vif); + break; + case RTW89_PKT_OFLD_TYPE_NULL_DATA: + skb = ieee80211_nullfunc_get(rtwdev->hw, vif, -1, false); + break; + case RTW89_PKT_OFLD_TYPE_QOS_NULL: + skb = ieee80211_nullfunc_get(rtwdev->hw, vif, -1, true); + break; + default: + goto err; + } + + if (!skb) + goto err; + + list_add_tail(&info->list, &rtw_wow->pkt_list); + ret = rtw89_fw_h2c_add_pkt_offload(rtwdev, &info->id, skb); + kfree_skb(skb); + + if (ret) + return ret; + + *id = info->id; + return 0; + +err: + kfree(info); + return -ENOMEM; +} + #define H2C_GENERAL_PKT_LEN 6 #define H2C_GENERAL_PKT_ID_UND 0xff int rtw89_fw_h2c_general_pkt(struct rtw89_dev *rtwdev, u8 macid) @@ -2943,3 +2993,182 @@ fail: dev_kfree_skb_any(skb); return ret; } + +#define H2C_KEEP_ALIVE_LEN 4 +int rtw89_fw_h2c_keep_alive(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif, + bool enable) +{ + struct sk_buff *skb; + u8 pkt_id = 0; + int ret; + + if (enable) { + ret = rtw89_fw_h2c_add_wow_fw_ofld(rtwdev, rtwvif, + RTW89_PKT_OFLD_TYPE_NULL_DATA, &pkt_id); + if (ret) + return -EPERM; + } + + skb = rtw89_fw_h2c_alloc_skb_with_hdr(rtwdev, H2C_KEEP_ALIVE_LEN); + if (!skb) { + rtw89_err(rtwdev, "failed to alloc skb for keep alive\n"); + return -ENOMEM; + } + + skb_put(skb, H2C_KEEP_ALIVE_LEN); + + RTW89_SET_KEEP_ALIVE_ENABLE(skb->data, enable); + RTW89_SET_KEEP_ALIVE_PKT_NULL_ID(skb->data, pkt_id); + RTW89_SET_KEEP_ALIVE_PERIOD(skb->data, 5); + RTW89_SET_KEEP_ALIVE_MACID(skb->data, rtwvif->mac_id); + + rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C, + H2C_CAT_MAC, + H2C_CL_MAC_WOW, + H2C_FUNC_KEEP_ALIVE, 0, 1, + H2C_KEEP_ALIVE_LEN); + + ret = rtw89_h2c_tx(rtwdev, skb, false); + if (ret) { + rtw89_err(rtwdev, "failed to send h2c\n"); + goto fail; + } + + return 0; + +fail: + dev_kfree_skb_any(skb); + + return ret; +} + +#define H2C_DISCONNECT_DETECT_LEN 8 +int rtw89_fw_h2c_disconnect_detect(struct rtw89_dev *rtwdev, + struct rtw89_vif *rtwvif, bool enable) +{ + struct rtw89_wow_param *rtw_wow = &rtwdev->wow; + struct sk_buff *skb; + u8 macid = rtwvif->mac_id; + int ret; + + skb = rtw89_fw_h2c_alloc_skb_with_hdr(rtwdev, H2C_DISCONNECT_DETECT_LEN); + if (!skb) { + rtw89_err(rtwdev, "failed to alloc skb for keep alive\n"); + return -ENOMEM; + } + + skb_put(skb, H2C_DISCONNECT_DETECT_LEN); + + if (test_bit(RTW89_WOW_FLAG_EN_DISCONNECT, rtw_wow->flags)) { + RTW89_SET_DISCONNECT_DETECT_ENABLE(skb->data, enable); + RTW89_SET_DISCONNECT_DETECT_DISCONNECT(skb->data, !enable); + RTW89_SET_DISCONNECT_DETECT_MAC_ID(skb->data, macid); + RTW89_SET_DISCONNECT_DETECT_CHECK_PERIOD(skb->data, 100); + RTW89_SET_DISCONNECT_DETECT_TRY_PKT_COUNT(skb->data, 5); + } + + rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C, + H2C_CAT_MAC, + H2C_CL_MAC_WOW, + H2C_FUNC_DISCONNECT_DETECT, 0, 1, + H2C_DISCONNECT_DETECT_LEN); + + ret = rtw89_h2c_tx(rtwdev, skb, false); + if (ret) { + rtw89_err(rtwdev, "failed to send h2c\n"); + goto fail; + } + + return 0; + +fail: + dev_kfree_skb_any(skb); + + return ret; +} + +#define H2C_WOW_GLOBAL_LEN 8 +int rtw89_fw_h2c_wow_global(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif, + bool enable) +{ + struct sk_buff *skb; + u8 macid = rtwvif->mac_id; + int ret; + + skb = rtw89_fw_h2c_alloc_skb_with_hdr(rtwdev, H2C_WOW_GLOBAL_LEN); + if (!skb) { + rtw89_err(rtwdev, "failed to alloc skb for keep alive\n"); + return -ENOMEM; + } + + skb_put(skb, H2C_WOW_GLOBAL_LEN); + + RTW89_SET_WOW_GLOBAL_ENABLE(skb->data, enable); + RTW89_SET_WOW_GLOBAL_MAC_ID(skb->data, macid); + + rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C, + H2C_CAT_MAC, + H2C_CL_MAC_WOW, + H2C_FUNC_WOW_GLOBAL, 0, 1, + H2C_WOW_GLOBAL_LEN); + + ret = rtw89_h2c_tx(rtwdev, skb, false); + if (ret) { + rtw89_err(rtwdev, "failed to send h2c\n"); + goto fail; + } + + return 0; + +fail: + dev_kfree_skb_any(skb); + + return ret; +} + +#define H2C_WAKEUP_CTRL_LEN 4 +int rtw89_fw_h2c_wow_wakeup_ctrl(struct rtw89_dev *rtwdev, + struct rtw89_vif *rtwvif, + bool enable) +{ + struct rtw89_wow_param *rtw_wow = &rtwdev->wow; + struct sk_buff *skb; + u8 macid = rtwvif->mac_id; + int ret; + + skb = rtw89_fw_h2c_alloc_skb_with_hdr(rtwdev, H2C_WAKEUP_CTRL_LEN); + if (!skb) { + rtw89_err(rtwdev, "failed to alloc skb for keep alive\n"); + return -ENOMEM; + } + + skb_put(skb, H2C_WAKEUP_CTRL_LEN); + + if (rtw_wow->pattern_cnt) + RTW89_SET_WOW_WAKEUP_CTRL_PATTERN_MATCH_ENABLE(skb->data, enable); + if (test_bit(RTW89_WOW_FLAG_EN_MAGIC_PKT, rtw_wow->flags)) + RTW89_SET_WOW_WAKEUP_CTRL_MAGIC_ENABLE(skb->data, enable); + if (test_bit(RTW89_WOW_FLAG_EN_DISCONNECT, rtw_wow->flags)) + RTW89_SET_WOW_WAKEUP_CTRL_DEAUTH_ENABLE(skb->data, enable); + + RTW89_SET_WOW_WAKEUP_CTRL_MAC_ID(skb->data, macid); + + rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C, + H2C_CAT_MAC, + H2C_CL_MAC_WOW, + H2C_FUNC_WAKEUP_CTRL, 0, 1, + H2C_WAKEUP_CTRL_LEN); + + ret = rtw89_h2c_tx(rtwdev, skb, false); + if (ret) { + rtw89_err(rtwdev, "failed to send h2c\n"); + goto fail; + } + + return 0; + +fail: + dev_kfree_skb_any(skb); + + return ret; +} diff --git a/drivers/net/wireless/realtek/rtw89/fw.h b/drivers/net/wireless/realtek/rtw89/fw.h index 3845581d5d28..25dd7bf8e730 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.h +++ b/drivers/net/wireless/realtek/rtw89/fw.h @@ -1893,6 +1893,146 @@ static inline void RTW89_SET_FWCMD_PKT_DROP_MACID_BAND_SEL_3(void *cmd, u32 val) le32p_replace_bits((__le32 *)cmd + 5, val, GENMASK(31, 0)); } +static inline void RTW89_SET_KEEP_ALIVE_ENABLE(void *h2c, u32 val) +{ + le32p_replace_bits((__le32 *)h2c, val, GENMASK(1, 0)); +} + +static inline void RTW89_SET_KEEP_ALIVE_PKT_NULL_ID(void *h2c, u32 val) +{ + le32p_replace_bits((__le32 *)h2c, val, GENMASK(15, 8)); +} + +static inline void RTW89_SET_KEEP_ALIVE_PERIOD(void *h2c, u32 val) +{ + le32p_replace_bits((__le32 *)h2c, val, GENMASK(24, 16)); +} + +static inline void RTW89_SET_KEEP_ALIVE_MACID(void *h2c, u32 val) +{ + le32p_replace_bits((__le32 *)h2c, val, GENMASK(31, 24)); +} + +static inline void RTW89_SET_DISCONNECT_DETECT_ENABLE(void *h2c, u32 val) +{ + le32p_replace_bits((__le32 *)h2c, val, BIT(0)); +} + +static inline void RTW89_SET_DISCONNECT_DETECT_TRYOK_BCNFAIL_COUNT_EN(void *h2c, u32 val) +{ + le32p_replace_bits((__le32 *)h2c, val, BIT(1)); +} + +static inline void RTW89_SET_DISCONNECT_DETECT_DISCONNECT(void *h2c, u32 val) +{ + le32p_replace_bits((__le32 *)h2c, val, BIT(2)); +} + +static inline void RTW89_SET_DISCONNECT_DETECT_MAC_ID(void *h2c, u32 val) +{ + le32p_replace_bits((__le32 *)h2c, val, GENMASK(15, 8)); +} + +static inline void RTW89_SET_DISCONNECT_DETECT_CHECK_PERIOD(void *h2c, u32 val) +{ + le32p_replace_bits((__le32 *)h2c, val, GENMASK(23, 16)); +} + +static inline void RTW89_SET_DISCONNECT_DETECT_TRY_PKT_COUNT(void *h2c, u32 val) +{ + le32p_replace_bits((__le32 *)h2c, val, GENMASK(31, 24)); +} + +static inline void RTW89_SET_DISCONNECT_DETECT_TRYOK_BCNFAIL_COUNT_LIMIT(void *h2c, u32 val) +{ + le32p_replace_bits((__le32 *)(h2c) + 1, val, GENMASK(7, 0)); +} + +static inline void RTW89_SET_WOW_GLOBAL_ENABLE(void *h2c, u32 val) +{ + le32p_replace_bits((__le32 *)h2c, val, BIT(0)); +} + +static inline void RTW89_SET_WOW_GLOBAL_DROP_ALL_PKT(void *h2c, u32 val) +{ + le32p_replace_bits((__le32 *)h2c, val, BIT(1)); +} + +static inline void RTW89_SET_WOW_GLOBAL_RX_PARSE_AFTER_WAKE(void *h2c, u32 val) +{ + le32p_replace_bits((__le32 *)h2c, val, BIT(2)); +} + +static inline void RTW89_SET_WOW_GLOBAL_WAKE_BAR_PULLED(void *h2c, u32 val) +{ + le32p_replace_bits((__le32 *)h2c, val, BIT(3)); +} + +static inline void RTW89_SET_WOW_GLOBAL_MAC_ID(void *h2c, u32 val) +{ + le32p_replace_bits((__le32 *)h2c, val, GENMASK(15, 8)); +} + +static inline void RTW89_SET_WOW_GLOBAL_PAIRWISE_SEC_ALGO(void *h2c, u32 val) +{ + le32p_replace_bits((__le32 *)h2c, val, GENMASK(23, 16)); +} + +static inline void RTW89_SET_WOW_GLOBAL_GROUP_SEC_ALGO(void *h2c, u32 val) +{ + le32p_replace_bits((__le32 *)h2c, val, GENMASK(31, 24)); +} + +static inline void RTW89_SET_WOW_GLOBAL_REMOTECTRL_INFO_CONTENT(void *h2c, u32 val) +{ + le32p_replace_bits((__le32 *)(h2c) + 1, val, GENMASK(31, 0)); +} + +static inline void RTW89_SET_WOW_WAKEUP_CTRL_PATTERN_MATCH_ENABLE(void *h2c, u32 val) +{ + le32p_replace_bits((__le32 *)h2c, val, BIT(0)); +} + +static inline void RTW89_SET_WOW_WAKEUP_CTRL_MAGIC_ENABLE(void *h2c, u32 val) +{ + le32p_replace_bits((__le32 *)h2c, val, BIT(1)); +} + +static inline void RTW89_SET_WOW_WAKEUP_CTRL_HW_UNICAST_ENABLE(void *h2c, u32 val) +{ + le32p_replace_bits((__le32 *)h2c, val, BIT(2)); +} + +static inline void RTW89_SET_WOW_WAKEUP_CTRL_FW_UNICAST_ENABLE(void *h2c, u32 val) +{ + le32p_replace_bits((__le32 *)h2c, val, BIT(3)); +} + +static inline void RTW89_SET_WOW_WAKEUP_CTRL_DEAUTH_ENABLE(void *h2c, u32 val) +{ + le32p_replace_bits((__le32 *)h2c, val, BIT(4)); +} + +static inline void RTW89_SET_WOW_WAKEUP_CTRL_REKEYP_ENABLE(void *h2c, u32 val) +{ + le32p_replace_bits((__le32 *)h2c, val, BIT(5)); +} + +static inline void RTW89_SET_WOW_WAKEUP_CTRL_EAP_ENABLE(void *h2c, u32 val) +{ + le32p_replace_bits((__le32 *)h2c, val, BIT(6)); +} + +static inline void RTW89_SET_WOW_WAKEUP_CTRL_ALL_DATA_ENABLE(void *h2c, u32 val) +{ + le32p_replace_bits((__le32 *)h2c, val, BIT(7)); +} + +static inline void RTW89_SET_WOW_WAKEUP_CTRL_MAC_ID(void *h2c, u32 val) +{ + le32p_replace_bits((__le32 *)h2c, val, GENMASK(31, 24)); +} + enum rtw89_btc_btf_h2c_class { BTFC_SET = 0x10, BTFC_GET = 0x11, @@ -2709,6 +2849,14 @@ struct rtw89_fw_h2c_rf_reg_info { #define H2C_FUNC_LOG_CFG 0x0 #define H2C_FUNC_MAC_GENERAL_PKT 0x1 +/* CLASS 1 - WOW */ +#define H2C_CL_MAC_WOW 0x1 +#define H2C_FUNC_KEEP_ALIVE 0x0 +#define H2C_FUNC_DISCONNECT_DETECT 0x1 +#define H2C_FUNC_WOW_GLOBAL 0x2 +#define H2C_FUNC_WAKEUP_CTRL 0x8 +#define H2C_FUNC_WOW_CAM_UPD 0xC + /* CLASS 2 - PS */ #define H2C_CL_MAC_PS 0x2 #define H2C_FUNC_MAC_LPS_PARM 0x0 @@ -2878,6 +3026,18 @@ int rtw89_fw_h2c_p2p_act(struct rtw89_dev *rtwdev, struct ieee80211_vif *vif, u8 act, u8 noa_id); int rtw89_fw_h2c_tsf32_toggle(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif, bool en); +int rtw89_fw_h2c_wow_global(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif, + bool enable); +int rtw89_fw_h2c_wow_wakeup_ctrl(struct rtw89_dev *rtwdev, + struct rtw89_vif *rtwvif, bool enable); +int rtw89_fw_h2c_keep_alive(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif, + bool enable); +int rtw89_fw_h2c_disconnect_detect(struct rtw89_dev *rtwdev, + struct rtw89_vif *rtwvif, bool enable); +int rtw89_fw_h2c_wow_global(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif, + bool enable); +int rtw89_fw_h2c_wow_wakeup_ctrl(struct rtw89_dev *rtwdev, + struct rtw89_vif *rtwvif, bool enable); static inline void rtw89_fw_h2c_init_ba_cam(struct rtw89_dev *rtwdev) { From 19e28c7fcc7408be8479f91d47146245c8be293e Mon Sep 17 00:00:00 2001 From: Chin-Yen Lee Date: Thu, 27 Oct 2022 13:27:06 +0800 Subject: [PATCH 0513/2002] wifi: rtw89: add WoWLAN function support WoWLAN is a feature which allows devices to be woken up from suspend state through WLAN events. When user enables WoWLAN feature and then let the device enter suspend state, WoWLAN firmware will be loaded by the driver and periodically monitors WiFi packets. Power consumption of WiFi chip will be reduced in this state. We now implement WoWLAN function in rtw8852ae and rtw8852ce chip. Currently supported WLAN events include receiving magic packet, rekey packet and deauth packet, and disconnecting from AP. Signed-off-by: Chin-Yen Lee Signed-off-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20221027052707.14605-7-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/Makefile | 2 + drivers/net/wireless/realtek/rtw89/core.c | 8 + drivers/net/wireless/realtek/rtw89/core.h | 77 ++- drivers/net/wireless/realtek/rtw89/debug.h | 1 + drivers/net/wireless/realtek/rtw89/mac.c | 18 +- drivers/net/wireless/realtek/rtw89/mac.h | 14 + drivers/net/wireless/realtek/rtw89/mac80211.c | 55 ++ drivers/net/wireless/realtek/rtw89/pci.c | 23 +- drivers/net/wireless/realtek/rtw89/ps.c | 2 +- drivers/net/wireless/realtek/rtw89/ps.h | 1 + drivers/net/wireless/realtek/rtw89/reg.h | 8 + drivers/net/wireless/realtek/rtw89/rtw8852a.c | 9 + drivers/net/wireless/realtek/rtw89/rtw8852c.c | 9 + drivers/net/wireless/realtek/rtw89/wow.c | 633 ++++++++++++++++++ drivers/net/wireless/realtek/rtw89/wow.h | 21 + 15 files changed, 874 insertions(+), 7 deletions(-) create mode 100644 drivers/net/wireless/realtek/rtw89/wow.c create mode 100644 drivers/net/wireless/realtek/rtw89/wow.h diff --git a/drivers/net/wireless/realtek/rtw89/Makefile b/drivers/net/wireless/realtek/rtw89/Makefile index ec0f5da65d6a..2dc48fa10c6b 100644 --- a/drivers/net/wireless/realtek/rtw89/Makefile +++ b/drivers/net/wireless/realtek/rtw89/Makefile @@ -15,6 +15,8 @@ rtw89_core-y += core.o \ chan.o \ ser.o +rtw89_core-$(CONFIG_PM) += wow.o + obj-$(CONFIG_RTW89_8852A) += rtw89_8852a.o rtw89_8852a-objs := rtw8852a.o \ rtw8852a_table.o \ diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c index 0e4153b1188f..e716b96d0f56 100644 --- a/drivers/net/wireless/realtek/rtw89/core.c +++ b/drivers/net/wireless/realtek/rtw89/core.c @@ -2204,6 +2204,9 @@ static void rtw89_track_work(struct work_struct *work) track_work.work); bool tfc_changed; + if (test_bit(RTW89_FLAG_FORBIDDEN_TRACK_WROK, rtwdev->flags)) + return; + mutex_lock(&rtwdev->mutex); if (!test_bit(RTW89_FLAG_RUNNING, rtwdev->flags)) @@ -3042,6 +3045,7 @@ int rtw89_core_init(struct rtw89_dev *rtwdev) continue; INIT_LIST_HEAD(&rtwdev->scan_info.pkt_list[band]); } + INIT_LIST_HEAD(&rtwdev->wow.pkt_list); INIT_WORK(&rtwdev->ba_work, rtw89_core_ba_work); INIT_WORK(&rtwdev->txq_work, rtw89_core_txq_work); INIT_DELAYED_WORK(&rtwdev->txq_reinvoke_work, rtw89_core_txq_reinvoke_work); @@ -3273,6 +3277,10 @@ static int rtw89_core_register_hw(struct rtw89_dev *rtwdev) hw->wiphy->max_scan_ssids = RTW89_SCANOFLD_MAX_SSID; hw->wiphy->max_scan_ie_len = RTW89_SCANOFLD_MAX_IE_LEN; +#ifdef CONFIG_PM + hw->wiphy->wowlan = rtwdev->chip->wowlan_stub; +#endif + hw->wiphy->tid_config_support.vif |= BIT(NL80211_TID_CONFIG_ATTR_AMPDU_CTRL); hw->wiphy->tid_config_support.peer |= BIT(NL80211_TID_CONFIG_ATTR_AMPDU_CTRL); hw->wiphy->tid_config_support.vif |= BIT(NL80211_TID_CONFIG_ATTR_AMSDU_CTRL); diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index d20cb3ae89e9..9f80359e27aa 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -178,7 +178,9 @@ enum rtw89_upd_mode { RTW89_ROLE_REMOVE, RTW89_ROLE_TYPE_CHANGE, RTW89_ROLE_INFO_CHANGE, - RTW89_ROLE_CON_DISCONN + RTW89_ROLE_CON_DISCONN, + RTW89_ROLE_BAND_SW, + RTW89_ROLE_FW_RESTORE, }; enum rtw89_self_role { @@ -2307,6 +2309,16 @@ struct rtw89_hci_ops { */ void (*recovery_start)(struct rtw89_dev *rtwdev); void (*recovery_complete)(struct rtw89_dev *rtwdev); + + void (*ctrl_txdma_ch)(struct rtw89_dev *rtwdev, bool enable); + void (*ctrl_txdma_fw_ch)(struct rtw89_dev *rtwdev, bool enable); + void (*ctrl_trxhci)(struct rtw89_dev *rtwdev, bool enable); + int (*poll_txdma_ch)(struct rtw89_dev *rtwdev); + void (*clr_idx_all)(struct rtw89_dev *rtwdev); + void (*clear)(struct rtw89_dev *rtwdev, struct pci_dev *pdev); + void (*disable_intr)(struct rtw89_dev *rtwdev); + void (*enable_intr)(struct rtw89_dev *rtwdev); + int (*rst_bdram)(struct rtw89_dev *rtwdev); }; struct rtw89_hci_info { @@ -2748,6 +2760,7 @@ struct rtw89_chip_info { const struct rtw89_imr_info *imr_info; const struct rtw89_rrsr_cfgs *rrsr_cfgs; u32 dma_ch_mask; + const struct wiphy_wowlan_support *wowlan_stub; }; union rtw89_bus_info { @@ -2944,6 +2957,8 @@ enum rtw89_flags { RTW89_FLAG_LOW_POWER_MODE, RTW89_FLAG_INACTIVE_PS, RTW89_FLAG_CRASH_SIMULATING, + RTW89_FLAG_WOWLAN, + RTW89_FLAG_FORBIDDEN_TRACK_WROK, NUM_OF_RTW89_FLAGS, }; @@ -3653,6 +3668,66 @@ static inline void rtw89_hci_recovery_complete(struct rtw89_dev *rtwdev) rtwdev->hci.ops->recovery_complete(rtwdev); } +static inline void rtw89_hci_enable_intr(struct rtw89_dev *rtwdev) +{ + if (rtwdev->hci.ops->enable_intr) + rtwdev->hci.ops->enable_intr(rtwdev); +} + +static inline void rtw89_hci_disable_intr(struct rtw89_dev *rtwdev) +{ + if (rtwdev->hci.ops->disable_intr) + rtwdev->hci.ops->disable_intr(rtwdev); +} + +static inline void rtw89_hci_ctrl_txdma_ch(struct rtw89_dev *rtwdev, bool enable) +{ + if (rtwdev->hci.ops->ctrl_txdma_ch) + rtwdev->hci.ops->ctrl_txdma_ch(rtwdev, enable); +} + +static inline void rtw89_hci_ctrl_txdma_fw_ch(struct rtw89_dev *rtwdev, bool enable) +{ + if (rtwdev->hci.ops->ctrl_txdma_fw_ch) + rtwdev->hci.ops->ctrl_txdma_fw_ch(rtwdev, enable); +} + +static inline void rtw89_hci_ctrl_trxhci(struct rtw89_dev *rtwdev, bool enable) +{ + if (rtwdev->hci.ops->ctrl_trxhci) + rtwdev->hci.ops->ctrl_trxhci(rtwdev, enable); +} + +static inline int rtw89_hci_poll_txdma_ch(struct rtw89_dev *rtwdev) +{ + int ret = 0; + + if (rtwdev->hci.ops->poll_txdma_ch) + ret = rtwdev->hci.ops->poll_txdma_ch(rtwdev); + return ret; +} + +static inline void rtw89_hci_clr_idx_all(struct rtw89_dev *rtwdev) +{ + if (rtwdev->hci.ops->clr_idx_all) + rtwdev->hci.ops->clr_idx_all(rtwdev); +} + +static inline int rtw89_hci_rst_bdram(struct rtw89_dev *rtwdev) +{ + int ret = 0; + + if (rtwdev->hci.ops->rst_bdram) + ret = rtwdev->hci.ops->rst_bdram(rtwdev); + return ret; +} + +static inline void rtw89_hci_clear(struct rtw89_dev *rtwdev, struct pci_dev *pdev) +{ + if (rtwdev->hci.ops->clear) + rtwdev->hci.ops->clear(rtwdev, pdev); +} + static inline u8 rtw89_read8(struct rtw89_dev *rtwdev, u32 addr) { return rtwdev->hci.ops->read8(rtwdev, addr); diff --git a/drivers/net/wireless/realtek/rtw89/debug.h b/drivers/net/wireless/realtek/rtw89/debug.h index ee243aadde87..e7971583acbc 100644 --- a/drivers/net/wireless/realtek/rtw89/debug.h +++ b/drivers/net/wireless/realtek/rtw89/debug.h @@ -26,6 +26,7 @@ enum rtw89_debug_mask { RTW89_DBG_HW_SCAN = BIT(15), RTW89_DBG_SAR = BIT(16), RTW89_DBG_STATE = BIT(17), + RTW89_DBG_WOW = BIT(18), RTW89_DBG_UNEXP = BIT(31), }; diff --git a/drivers/net/wireless/realtek/rtw89/mac.c b/drivers/net/wireless/realtek/rtw89/mac.c index 5b706611d533..bb49033b587d 100644 --- a/drivers/net/wireless/realtek/rtw89/mac.c +++ b/drivers/net/wireless/realtek/rtw89/mac.c @@ -1566,6 +1566,16 @@ int rtw89_mac_resize_ple_rx_quota(struct rtw89_dev *rtwdev, bool wow) } #undef SET_QUOTA +void rtw89_mac_hw_mgnt_sec(struct rtw89_dev *rtwdev, bool enable) +{ + u32 msk32 = B_AX_UC_MGNT_DEC | B_AX_BMC_MGNT_DEC; + + if (enable) + rtw89_write32_set(rtwdev, R_AX_SEC_ENG_CTRL, msk32); + else + rtw89_write32_clr(rtwdev, R_AX_SEC_ENG_CTRL, msk32); +} + static void dle_quota_cfg(struct rtw89_dev *rtwdev, const struct rtw89_dle_mem *cfg, u16 ext_wde_min_qt_wcpu) @@ -1915,10 +1925,10 @@ static int scheduler_init(struct rtw89_dev *rtwdev, u8 mac_idx) return 0; } -static int rtw89_mac_typ_fltr_opt(struct rtw89_dev *rtwdev, - enum rtw89_machdr_frame_type type, - enum rtw89_mac_fwd_target fwd_target, - u8 mac_idx) +int rtw89_mac_typ_fltr_opt(struct rtw89_dev *rtwdev, + enum rtw89_machdr_frame_type type, + enum rtw89_mac_fwd_target fwd_target, + u8 mac_idx) { u32 reg; u32 val; diff --git a/drivers/net/wireless/realtek/rtw89/mac.h b/drivers/net/wireless/realtek/rtw89/mac.h index e3b4c7830f44..34f7d51be3f9 100644 --- a/drivers/net/wireless/realtek/rtw89/mac.h +++ b/drivers/net/wireless/realtek/rtw89/mac.h @@ -980,6 +980,16 @@ static inline void rtw89_mac_ctrl_hci_dma_trx(struct rtw89_dev *rtwdev, B_AX_HCI_TXDMA_EN | B_AX_HCI_RXDMA_EN); } +static inline bool rtw89_mac_get_power_state(struct rtw89_dev *rtwdev) +{ + u32 val; + + val = rtw89_read32_mask(rtwdev, R_AX_IC_PWR_STATE, + B_AX_WLMAC_PWR_STE_MASK); + + return !!val; +} + int rtw89_mac_set_tx_time(struct rtw89_dev *rtwdev, struct rtw89_sta *rtwsta, bool resume, u32 tx_time); int rtw89_mac_get_tx_time(struct rtw89_dev *rtwdev, struct rtw89_sta *rtwsta, @@ -1038,8 +1048,12 @@ void rtw89_mac_pkt_drop_vif(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif); u16 rtw89_mac_dle_buf_req(struct rtw89_dev *rtwdev, u16 buf_len, bool wd); int rtw89_mac_set_cpuio(struct rtw89_dev *rtwdev, struct rtw89_cpuio_ctrl *ctrl_para, bool wd); +int rtw89_mac_typ_fltr_opt(struct rtw89_dev *rtwdev, + enum rtw89_machdr_frame_type type, + enum rtw89_mac_fwd_target fwd_target, u8 mac_idx); int rtw89_mac_resize_ple_rx_quota(struct rtw89_dev *rtwdev, bool wow); int rtw89_mac_ptk_drop_by_band_and_wait(struct rtw89_dev *rtwdev, enum rtw89_mac_idx band); +void rtw89_mac_hw_mgnt_sec(struct rtw89_dev *rtwdev, bool wow); #endif diff --git a/drivers/net/wireless/realtek/rtw89/mac80211.c b/drivers/net/wireless/realtek/rtw89/mac80211.c index a296bfa8188f..6e79bf899901 100644 --- a/drivers/net/wireless/realtek/rtw89/mac80211.c +++ b/drivers/net/wireless/realtek/rtw89/mac80211.c @@ -14,6 +14,7 @@ #include "sar.h" #include "ser.h" #include "util.h" +#include "wow.h" static void rtw89_ops_tx(struct ieee80211_hw *hw, struct ieee80211_tx_control *control, @@ -916,6 +917,55 @@ static int rtw89_ops_set_tid_config(struct ieee80211_hw *hw, return 0; } +#ifdef CONFIG_PM +static int rtw89_ops_suspend(struct ieee80211_hw *hw, + struct cfg80211_wowlan *wowlan) +{ + struct rtw89_dev *rtwdev = hw->priv; + int ret; + + set_bit(RTW89_FLAG_FORBIDDEN_TRACK_WROK, rtwdev->flags); + cancel_delayed_work_sync(&rtwdev->track_work); + + mutex_lock(&rtwdev->mutex); + ret = rtw89_wow_suspend(rtwdev, wowlan); + mutex_unlock(&rtwdev->mutex); + + if (ret) { + rtw89_warn(rtwdev, "failed to suspend for wow %d\n", ret); + clear_bit(RTW89_FLAG_FORBIDDEN_TRACK_WROK, rtwdev->flags); + return 1; + } + + return 0; +} + +static int rtw89_ops_resume(struct ieee80211_hw *hw) +{ + struct rtw89_dev *rtwdev = hw->priv; + int ret; + + mutex_lock(&rtwdev->mutex); + ret = rtw89_wow_resume(rtwdev); + if (ret) + rtw89_warn(rtwdev, "failed to resume for wow %d\n", ret); + mutex_unlock(&rtwdev->mutex); + + clear_bit(RTW89_FLAG_FORBIDDEN_TRACK_WROK, rtwdev->flags); + ieee80211_queue_delayed_work(rtwdev->hw, &rtwdev->track_work, + RTW89_TRACK_WORK_PERIOD); + + return ret ? 1 : 0; +} + +static void rtw89_ops_set_wakeup(struct ieee80211_hw *hw, bool enabled) +{ + struct rtw89_dev *rtwdev = hw->priv; + + device_set_wakeup_enable(rtwdev->dev, enabled); +} +#endif + const struct ieee80211_ops rtw89_ops = { .tx = rtw89_ops_tx, .wake_tx_queue = rtw89_ops_wake_tx_queue, @@ -953,5 +1003,10 @@ const struct ieee80211_ops rtw89_ops = { .set_sar_specs = rtw89_ops_set_sar_specs, .sta_rc_update = rtw89_ops_sta_rc_update, .set_tid_config = rtw89_ops_set_tid_config, +#ifdef CONFIG_PM + .suspend = rtw89_ops_suspend, + .resume = rtw89_ops_resume, + .set_wakeup = rtw89_ops_set_wakeup, +#endif }; EXPORT_SYMBOL(rtw89_ops); diff --git a/drivers/net/wireless/realtek/rtw89/pci.c b/drivers/net/wireless/realtek/rtw89/pci.c index 5f8e19639362..07a2e23759f0 100644 --- a/drivers/net/wireless/realtek/rtw89/pci.c +++ b/drivers/net/wireless/realtek/rtw89/pci.c @@ -186,6 +186,17 @@ static void rtw89_pci_ctrl_txdma_ch_pcie(struct rtw89_dev *rtwdev, bool enable) } } +static void rtw89_pci_ctrl_txdma_fw_ch_pcie(struct rtw89_dev *rtwdev, bool enable) +{ + const struct rtw89_pci_info *info = rtwdev->pci_info; + const struct rtw89_reg_def *dma_stop1 = &info->dma_stop1; + + if (enable) + rtw89_write32_clr(rtwdev, dma_stop1->addr, B_AX_STOP_CH12); + else + rtw89_write32_set(rtwdev, dma_stop1->addr, B_AX_STOP_CH12); +} + static bool rtw89_skb_put_rx_data(struct rtw89_dev *rtwdev, bool fs, bool ls, struct sk_buff *new, @@ -2513,7 +2524,7 @@ static int rtw89_pci_ops_mac_pre_init(struct rtw89_dev *rtwdev) /* disable all channels except to FW CMD channel to download firmware */ rtw89_pci_ctrl_txdma_ch_pcie(rtwdev, false); - rtw89_write32_clr(rtwdev, info->dma_stop1.addr, B_AX_STOP_CH12); + rtw89_pci_ctrl_txdma_fw_ch_pcie(rtwdev, true); /* start DMA activities */ rtw89_pci_ctrl_dma_all(rtwdev, true); @@ -3771,6 +3782,16 @@ static const struct rtw89_hci_ops rtw89_pci_ops = { .recovery_start = rtw89_pci_ops_recovery_start, .recovery_complete = rtw89_pci_ops_recovery_complete, + + .ctrl_txdma_ch = rtw89_pci_ctrl_txdma_ch_pcie, + .ctrl_txdma_fw_ch = rtw89_pci_ctrl_txdma_fw_ch_pcie, + .ctrl_trxhci = rtw89_pci_ctrl_dma_trx, + .poll_txdma_ch = rtw89_poll_txdma_ch_idle_pcie, + .clr_idx_all = rtw89_pci_clr_idx_all, + .clear = rtw89_pci_clear_resource, + .disable_intr = rtw89_pci_disable_intr_lock, + .enable_intr = rtw89_pci_enable_intr_lock, + .rst_bdram = rtw89_pci_rst_bdram_pcie, }; int rtw89_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) diff --git a/drivers/net/wireless/realtek/rtw89/ps.c b/drivers/net/wireless/realtek/rtw89/ps.c index bf41a1141679..40498812205e 100644 --- a/drivers/net/wireless/realtek/rtw89/ps.c +++ b/drivers/net/wireless/realtek/rtw89/ps.c @@ -59,7 +59,7 @@ static void rtw89_ps_power_mode_change(struct rtw89_dev *rtwdev, bool enter) rtw89_mac_power_mode_change(rtwdev, enter); } -static void __rtw89_enter_ps_mode(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif) +void __rtw89_enter_ps_mode(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif) { if (rtwvif->wifi_role == RTW89_WIFI_ROLE_P2P_CLIENT) return; diff --git a/drivers/net/wireless/realtek/rtw89/ps.h b/drivers/net/wireless/realtek/rtw89/ps.h index 0feae3991623..6ac1f7ea5339 100644 --- a/drivers/net/wireless/realtek/rtw89/ps.h +++ b/drivers/net/wireless/realtek/rtw89/ps.h @@ -8,6 +8,7 @@ void rtw89_enter_lps(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif); void rtw89_leave_lps(struct rtw89_dev *rtwdev); void __rtw89_leave_ps_mode(struct rtw89_dev *rtwdev); +void __rtw89_enter_ps_mode(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif); void rtw89_leave_ps_mode(struct rtw89_dev *rtwdev); void rtw89_enter_ips(struct rtw89_dev *rtwdev); void rtw89_leave_ips(struct rtw89_dev *rtwdev); diff --git a/drivers/net/wireless/realtek/rtw89/reg.h b/drivers/net/wireless/realtek/rtw89/reg.h index 33f2b67bfca3..2f6358244934 100644 --- a/drivers/net/wireless/realtek/rtw89/reg.h +++ b/drivers/net/wireless/realtek/rtw89/reg.h @@ -952,6 +952,9 @@ B_AX_STF_OQT_OVERFLOW_ERR_INT_EN | \ B_AX_STF_OQT_UNDERFLOW_ERR_INT_EN) +#define R_AX_RX_FUNCTION_STOP 0x8920 +#define B_AX_HDR_RX_STOP BIT(0) + #define R_AX_HCI_FC_CTRL 0x8A00 #define B_AX_HCI_FC_CH12_FULL_COND_MASK GENMASK(11, 10) #define B_AX_HCI_FC_WP_CH811_FULL_COND_MASK GENMASK(9, 8) @@ -1570,6 +1573,8 @@ #define R_AX_ACTION_FWD0 0x9C04 #define TRXCFG_MPDU_PROC_ACT_FRWD 0x02A95A95 +#define R_AX_ACTION_FWD1 0x9C08 + #define R_AX_TF_FWD 0x9C14 #define TRXCFG_MPDU_PROC_TF_FRWD 0x0000AA55 @@ -1581,6 +1586,9 @@ #define R_AX_CUT_AMSDU_CTRL 0x9C40 #define TRXCFG_MPDU_PROC_CUT_CTRL 0x010E05F0 +#define R_AX_WOW_CTRL 0x9C50 +#define B_AX_WOW_WOWEN BIT(1) + #define R_AX_MPDU_RX_ERR_ISR 0x9CF0 #define R_AX_MPDU_RX_ERR_IMR 0x9CF4 #define B_AX_RPT_ERR_INT_EN BIT(3) diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852a.c b/drivers/net/wireless/realtek/rtw89/rtw8852a.c index 7995d720dc92..becce3b48f51 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852a.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852a.c @@ -1990,6 +1990,12 @@ static void rtw8852a_query_ppdu(struct rtw89_dev *rtwdev, rtw8852a_fill_freq_with_ppdu(rtwdev, phy_ppdu, status); } +#ifdef CONFIG_PM +static const struct wiphy_wowlan_support rtw_wowlan_stub_8852a = { + .flags = WIPHY_WOWLAN_MAGIC_PKT | WIPHY_WOWLAN_DISCONNECT, +}; +#endif + static const struct rtw89_chip_ops rtw8852a_chip_ops = { .enable_bb_rf = rtw89_mac_enable_bb_rf, .disable_bb_rf = rtw89_mac_disable_bb_rf, @@ -2139,6 +2145,9 @@ const struct rtw89_chip_info rtw8852a_chip_info = { .imr_info = &rtw8852a_imr_info, .rrsr_cfgs = &rtw8852a_rrsr_cfgs, .dma_ch_mask = 0, +#ifdef CONFIG_PM + .wowlan_stub = &rtw_wowlan_stub_8852a, +#endif }; EXPORT_SYMBOL(rtw8852a_chip_info); diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852c.c b/drivers/net/wireless/realtek/rtw89/rtw8852c.c index 93332feabc44..acb0e2e0adba 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852c.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852c.c @@ -2796,6 +2796,12 @@ static int rtw8852c_mac_disable_bb_rf(struct rtw89_dev *rtwdev) return 0; } +#ifdef CONFIG_PM +static const struct wiphy_wowlan_support rtw_wowlan_stub_8852c = { + .flags = WIPHY_WOWLAN_MAGIC_PKT | WIPHY_WOWLAN_DISCONNECT, +}; +#endif + static const struct rtw89_chip_ops rtw8852c_chip_ops = { .enable_bb_rf = rtw8852c_mac_enable_bb_rf, .disable_bb_rf = rtw8852c_mac_disable_bb_rf, @@ -2949,6 +2955,9 @@ const struct rtw89_chip_info rtw8852c_chip_info = { .imr_info = &rtw8852c_imr_info, .rrsr_cfgs = &rtw8852c_rrsr_cfgs, .dma_ch_mask = 0, +#ifdef CONFIG_PM + .wowlan_stub = &rtw_wowlan_stub_8852c, +#endif }; EXPORT_SYMBOL(rtw8852c_chip_info); diff --git a/drivers/net/wireless/realtek/rtw89/wow.c b/drivers/net/wireless/realtek/rtw89/wow.c new file mode 100644 index 000000000000..3d30083fcb9a --- /dev/null +++ b/drivers/net/wireless/realtek/rtw89/wow.c @@ -0,0 +1,633 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* Copyright(c) 2019-2022 Realtek Corporation + */ +#include "cam.h" +#include "core.h" +#include "debug.h" +#include "fw.h" +#include "mac.h" +#include "phy.h" +#include "ps.h" +#include "reg.h" +#include "util.h" +#include "wow.h" + +static void rtw89_wow_leave_deep_ps(struct rtw89_dev *rtwdev) +{ + __rtw89_leave_ps_mode(rtwdev); +} + +static void rtw89_wow_enter_deep_ps(struct rtw89_dev *rtwdev) +{ + struct ieee80211_vif *wow_vif = rtwdev->wow.wow_vif; + struct rtw89_vif *rtwvif = (struct rtw89_vif *)wow_vif->drv_priv; + + __rtw89_enter_ps_mode(rtwdev, rtwvif); +} + +static void rtw89_wow_enter_lps(struct rtw89_dev *rtwdev) +{ + struct ieee80211_vif *wow_vif = rtwdev->wow.wow_vif; + struct rtw89_vif *rtwvif = (struct rtw89_vif *)wow_vif->drv_priv; + + rtw89_enter_lps(rtwdev, rtwvif); +} + +static void rtw89_wow_leave_lps(struct rtw89_dev *rtwdev) +{ + rtw89_leave_lps(rtwdev); +} + +static int rtw89_wow_config_mac(struct rtw89_dev *rtwdev, bool enable_wow) +{ + int ret; + + if (enable_wow) { + ret = rtw89_mac_resize_ple_rx_quota(rtwdev, true); + if (ret) { + rtw89_err(rtwdev, "[ERR]patch rx qta %d\n", ret); + return ret; + } + rtw89_write32_set(rtwdev, R_AX_RX_FUNCTION_STOP, B_AX_HDR_RX_STOP); + rtw89_write32_clr(rtwdev, R_AX_RX_FLTR_OPT, B_AX_SNIFFER_MODE); + rtw89_mac_cfg_ppdu_status(rtwdev, RTW89_MAC_0, false); + rtw89_write32(rtwdev, R_AX_ACTION_FWD0, 0); + rtw89_write32(rtwdev, R_AX_ACTION_FWD1, 0); + rtw89_write32(rtwdev, R_AX_TF_FWD, 0); + rtw89_write32(rtwdev, R_AX_HW_RPT_FWD, 0); + } else { + ret = rtw89_mac_resize_ple_rx_quota(rtwdev, false); + if (ret) { + rtw89_err(rtwdev, "[ERR]patch rx qta %d\n", ret); + return ret; + } + rtw89_write32_clr(rtwdev, R_AX_RX_FUNCTION_STOP, B_AX_HDR_RX_STOP); + rtw89_mac_cfg_ppdu_status(rtwdev, RTW89_MAC_0, true); + rtw89_write32(rtwdev, R_AX_ACTION_FWD0, TRXCFG_MPDU_PROC_ACT_FRWD); + rtw89_write32(rtwdev, R_AX_TF_FWD, TRXCFG_MPDU_PROC_TF_FRWD); + } + + return 0; +} + +static void rtw89_wow_set_rx_filter(struct rtw89_dev *rtwdev, bool enable) +{ + enum rtw89_mac_fwd_target fwd_target = enable ? + RTW89_FWD_DONT_CARE : + RTW89_FWD_TO_HOST; + + rtw89_mac_typ_fltr_opt(rtwdev, RTW89_MGNT, fwd_target, RTW89_MAC_0); + rtw89_mac_typ_fltr_opt(rtwdev, RTW89_CTRL, fwd_target, RTW89_MAC_0); + rtw89_mac_typ_fltr_opt(rtwdev, RTW89_DATA, fwd_target, RTW89_MAC_0); +} + +static void rtw89_wow_show_wakeup_reason(struct rtw89_dev *rtwdev) +{ + enum rtw89_core_chip_id chip_id = rtwdev->chip->chip_id; + struct cfg80211_wowlan_nd_info nd_info; + struct cfg80211_wowlan_wakeup wakeup = { + .pattern_idx = -1, + }; + u32 wow_reason_reg; + u8 reason; + + if (chip_id == RTL8852A || chip_id == RTL8852B) + wow_reason_reg = R_AX_C2HREG_DATA3 + 3; + else + wow_reason_reg = R_AX_C2HREG_DATA3_V1 + 3; + + reason = rtw89_read8(rtwdev, wow_reason_reg); + + switch (reason) { + case RTW89_WOW_RSN_RX_DEAUTH: + wakeup.disconnect = true; + rtw89_debug(rtwdev, RTW89_DBG_WOW, "WOW: Rx deauth\n"); + break; + case RTW89_WOW_RSN_DISCONNECT: + wakeup.disconnect = true; + rtw89_debug(rtwdev, RTW89_DBG_WOW, "WOW: AP is off\n"); + break; + case RTW89_WOW_RSN_RX_MAGIC_PKT: + wakeup.magic_pkt = true; + rtw89_debug(rtwdev, RTW89_DBG_WOW, "WOW: Rx magic packet\n"); + break; + case RTW89_WOW_RSN_RX_GTK_REKEY: + wakeup.gtk_rekey_failure = true; + rtw89_debug(rtwdev, RTW89_DBG_WOW, "WOW: Rx gtk rekey\n"); + break; + case RTW89_WOW_RSN_RX_PATTERN_MATCH: + /* Current firmware and driver don't report pattern index + * Use pattern_idx to 0 defaultly. + */ + wakeup.pattern_idx = 0; + rtw89_debug(rtwdev, RTW89_DBG_WOW, "WOW: Rx pattern match packet\n"); + break; + case RTW89_WOW_RSN_RX_NLO: + /* Current firmware and driver don't report ssid index. + * Use 0 for n_matches based on its comment. + */ + nd_info.n_matches = 0; + wakeup.net_detect = &nd_info; + rtw89_debug(rtwdev, RTW89_DBG_WOW, "Rx NLO\n"); + break; + default: + rtw89_warn(rtwdev, "Unknown wakeup reason %x\n", reason); + ieee80211_report_wowlan_wakeup(rtwdev->wow.wow_vif, NULL, + GFP_KERNEL); + return; + } + + ieee80211_report_wowlan_wakeup(rtwdev->wow.wow_vif, &wakeup, + GFP_KERNEL); +} + +static void rtw89_wow_vif_iter(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif) +{ + struct rtw89_wow_param *rtw_wow = &rtwdev->wow; + struct ieee80211_vif *vif = rtwvif_to_vif(rtwvif); + + /* Current wowlan function support setting of only one STATION vif. + * So when one suitable vif is found, stop the iteration. + */ + if (rtw_wow->wow_vif || vif->type != NL80211_IFTYPE_STATION) + return; + + switch (rtwvif->net_type) { + case RTW89_NET_TYPE_INFRA: + rtw_wow->wow_vif = vif; + break; + case RTW89_NET_TYPE_NO_LINK: + default: + break; + } +} + +static void rtw89_wow_clear_wakeups(struct rtw89_dev *rtwdev) +{ + struct rtw89_wow_param *rtw_wow = &rtwdev->wow; + + rtw_wow->wow_vif = NULL; + rtw89_core_release_all_bits_map(rtw_wow->flags, RTW89_WOW_FLAG_NUM); + rtw_wow->pattern_cnt = 0; +} + +static int rtw89_wow_set_wakeups(struct rtw89_dev *rtwdev, + struct cfg80211_wowlan *wowlan) +{ + struct rtw89_wow_param *rtw_wow = &rtwdev->wow; + struct rtw89_vif *rtwvif; + + if (wowlan->disconnect) + set_bit(RTW89_WOW_FLAG_EN_DISCONNECT, rtw_wow->flags); + if (wowlan->magic_pkt) + set_bit(RTW89_WOW_FLAG_EN_MAGIC_PKT, rtw_wow->flags); + + rtw89_for_each_rtwvif(rtwdev, rtwvif) + rtw89_wow_vif_iter(rtwdev, rtwvif); + + if (!rtw_wow->wow_vif) + return -EPERM; + + return 0; +} + +static int rtw89_wow_cfg_wake(struct rtw89_dev *rtwdev, bool wow) +{ + struct rtw89_wow_param *rtw_wow = &rtwdev->wow; + struct ieee80211_vif *wow_vif = rtw_wow->wow_vif; + struct rtw89_vif *rtwvif = (struct rtw89_vif *)wow_vif->drv_priv; + struct ieee80211_sta *wow_sta; + struct rtw89_sta *rtwsta = NULL; + bool is_conn = true; + int ret; + + wow_sta = ieee80211_find_sta(wow_vif, rtwvif->bssid); + if (wow_sta) + rtwsta = (struct rtw89_sta *)wow_sta->drv_priv; + else + is_conn = false; + + if (wow) { + if (rtw_wow->pattern_cnt) + rtwvif->wowlan_pattern = true; + if (test_bit(RTW89_WOW_FLAG_EN_MAGIC_PKT, rtw_wow->flags)) + rtwvif->wowlan_magic = true; + } else { + rtwvif->wowlan_pattern = false; + rtwvif->wowlan_magic = false; + } + + ret = rtw89_fw_h2c_wow_wakeup_ctrl(rtwdev, rtwvif, wow); + if (ret) { + rtw89_err(rtwdev, "failed to fw wow wakeup ctrl\n"); + return ret; + } + + if (wow) { + ret = rtw89_chip_h2c_dctl_sec_cam(rtwdev, rtwvif, rtwsta); + if (ret) { + rtw89_err(rtwdev, "failed to update dctl cam sec entry: %d\n", + ret); + return ret; + } + } + + ret = rtw89_fw_h2c_join_info(rtwdev, rtwvif, rtwsta, !is_conn); + if (ret) { + rtw89_warn(rtwdev, "failed to send h2c join info\n"); + return ret; + } + + ret = rtw89_fw_h2c_cam(rtwdev, rtwvif, rtwsta, NULL); + if (ret) { + rtw89_warn(rtwdev, "failed to send h2c cam\n"); + return ret; + } + + ret = rtw89_fw_h2c_wow_global(rtwdev, rtwvif, wow); + if (ret) { + rtw89_err(rtwdev, "failed to fw wow global\n"); + return ret; + } + + return 0; +} + +static int rtw89_wow_check_fw_status(struct rtw89_dev *rtwdev, bool wow_enable) +{ + u8 polling; + int ret; + + ret = read_poll_timeout_atomic(rtw89_read8_mask, polling, + wow_enable == !!polling, + 50, 50000, false, rtwdev, + R_AX_WOW_CTRL, B_AX_WOW_WOWEN); + if (ret) + rtw89_err(rtwdev, "failed to check wow status %s\n", + wow_enable ? "enabled" : "disabled"); + return ret; +} + +static void rtw89_wow_release_pkt_list(struct rtw89_dev *rtwdev) +{ + struct rtw89_wow_param *rtw_wow = &rtwdev->wow; + struct list_head *pkt_list = &rtw_wow->pkt_list; + struct rtw89_pktofld_info *info, *tmp; + + list_for_each_entry_safe(info, tmp, pkt_list, list) { + rtw89_fw_h2c_del_pkt_offload(rtwdev, info->id); + rtw89_core_release_bit_map(rtwdev->pkt_offload, + info->id); + list_del(&info->list); + kfree(info); + } +} + +static int rtw89_wow_swap_fw(struct rtw89_dev *rtwdev, bool wow) +{ + enum rtw89_fw_type fw_type = wow ? RTW89_FW_WOWLAN : RTW89_FW_NORMAL; + struct rtw89_wow_param *rtw_wow = &rtwdev->wow; + struct ieee80211_vif *wow_vif = rtw_wow->wow_vif; + struct rtw89_vif *rtwvif = (struct rtw89_vif *)wow_vif->drv_priv; + struct ieee80211_sta *wow_sta; + struct rtw89_sta *rtwsta = NULL; + bool is_conn = true; + int ret; + + rtw89_hci_disable_intr(rtwdev); + + wow_sta = ieee80211_find_sta(wow_vif, rtwvif->bssid); + if (wow_sta) + rtwsta = (struct rtw89_sta *)wow_sta->drv_priv; + else + is_conn = false; + + ret = rtw89_fw_download(rtwdev, fw_type); + if (ret) { + rtw89_warn(rtwdev, "download fw failed\n"); + return ret; + } + + rtw89_phy_init_rf_reg(rtwdev, true); + + ret = rtw89_fw_h2c_role_maintain(rtwdev, rtwvif, rtwsta, + RTW89_ROLE_FW_RESTORE); + if (ret) { + rtw89_warn(rtwdev, "failed to send h2c role maintain\n"); + return ret; + } + + ret = rtw89_fw_h2c_assoc_cmac_tbl(rtwdev, wow_vif, wow_sta); + if (ret) { + rtw89_warn(rtwdev, "failed to send h2c assoc cmac tbl\n"); + return ret; + } + + if (!is_conn) + rtw89_cam_reset_keys(rtwdev); + + ret = rtw89_fw_h2c_join_info(rtwdev, rtwvif, rtwsta, !is_conn); + if (ret) { + rtw89_warn(rtwdev, "failed to send h2c join info\n"); + return ret; + } + + ret = rtw89_fw_h2c_cam(rtwdev, rtwvif, rtwsta, NULL); + if (ret) { + rtw89_warn(rtwdev, "failed to send h2c cam\n"); + return ret; + } + + if (is_conn) { + rtw89_phy_ra_assoc(rtwdev, wow_sta); + rtw89_phy_set_bss_color(rtwdev, wow_vif); + rtw89_chip_cfg_txpwr_ul_tb_offset(rtwdev, wow_vif); + } + + rtw89_mac_hw_mgnt_sec(rtwdev, wow); + rtw89_hci_enable_intr(rtwdev); + + return 0; +} + +static int rtw89_wow_enable_trx_pre(struct rtw89_dev *rtwdev) +{ + int ret; + + rtw89_hci_ctrl_txdma_ch(rtwdev, false); + rtw89_hci_ctrl_txdma_fw_ch(rtwdev, true); + + rtw89_mac_ptk_drop_by_band_and_wait(rtwdev, RTW89_MAC_0); + + ret = rtw89_hci_poll_txdma_ch(rtwdev); + if (ret) { + rtw89_err(rtwdev, "txdma ch busy\n"); + return ret; + } + rtw89_wow_set_rx_filter(rtwdev, true); + + ret = rtw89_mac_cfg_ppdu_status(rtwdev, RTW89_MAC_0, false); + if (ret) { + rtw89_err(rtwdev, "cfg ppdu status\n"); + return ret; + } + + return 0; +} + +static int rtw89_wow_enable_trx_post(struct rtw89_dev *rtwdev) +{ + int ret; + + rtw89_hci_disable_intr(rtwdev); + rtw89_hci_ctrl_trxhci(rtwdev, false); + + ret = rtw89_hci_poll_txdma_ch(rtwdev); + if (ret) { + rtw89_err(rtwdev, "failed to poll txdma ch idle pcie\n"); + return ret; + } + + ret = rtw89_wow_config_mac(rtwdev, true); + if (ret) { + rtw89_err(rtwdev, "failed to config mac\n"); + return ret; + } + + rtw89_wow_set_rx_filter(rtwdev, false); + rtw89_hci_reset(rtwdev); + + return 0; +} + +static int rtw89_wow_disable_trx_pre(struct rtw89_dev *rtwdev) +{ + int ret; + + rtw89_hci_clr_idx_all(rtwdev); + + ret = rtw89_hci_rst_bdram(rtwdev); + if (ret) { + rtw89_warn(rtwdev, "reset bdram busy\n"); + return ret; + } + + rtw89_hci_ctrl_trxhci(rtwdev, true); + rtw89_hci_ctrl_txdma_ch(rtwdev, true); + + ret = rtw89_wow_config_mac(rtwdev, false); + if (ret) { + rtw89_err(rtwdev, "failed to config mac\n"); + return ret; + } + rtw89_hci_enable_intr(rtwdev); + + return 0; +} + +static int rtw89_wow_disable_trx_post(struct rtw89_dev *rtwdev) +{ + int ret; + + ret = rtw89_mac_cfg_ppdu_status(rtwdev, RTW89_MAC_0, true); + if (ret) + rtw89_err(rtwdev, "cfg ppdu status\n"); + + return ret; +} + +static int rtw89_wow_fw_start(struct rtw89_dev *rtwdev) +{ + struct rtw89_wow_param *rtw_wow = &rtwdev->wow; + struct rtw89_vif *rtwvif = (struct rtw89_vif *)rtw_wow->wow_vif->drv_priv; + int ret; + + ret = rtw89_fw_h2c_keep_alive(rtwdev, rtwvif, true); + if (ret) { + rtw89_err(rtwdev, "wow: failed to enable keep alive\n"); + return ret; + } + + ret = rtw89_fw_h2c_disconnect_detect(rtwdev, rtwvif, true); + if (ret) { + rtw89_err(rtwdev, "wow: failed to enable disconnect detect\n"); + goto out; + } + + ret = rtw89_wow_cfg_wake(rtwdev, true); + if (ret) { + rtw89_err(rtwdev, "wow: failed to config wake\n"); + goto out; + } + + ret = rtw89_wow_check_fw_status(rtwdev, true); + if (ret) { + rtw89_err(rtwdev, "wow: failed to check enable fw ready\n"); + goto out; + } + +out: + return ret; +} + +static int rtw89_wow_fw_stop(struct rtw89_dev *rtwdev) +{ + struct rtw89_wow_param *rtw_wow = &rtwdev->wow; + struct rtw89_vif *rtwvif = (struct rtw89_vif *)rtw_wow->wow_vif->drv_priv; + int ret; + + ret = rtw89_fw_h2c_keep_alive(rtwdev, rtwvif, false); + if (ret) { + rtw89_err(rtwdev, "wow: failed to disable keep alive\n"); + goto out; + } + + rtw89_wow_release_pkt_list(rtwdev); + + ret = rtw89_fw_h2c_disconnect_detect(rtwdev, rtwvif, false); + if (ret) { + rtw89_err(rtwdev, "wow: failed to disable disconnect detect\n"); + goto out; + } + + ret = rtw89_wow_cfg_wake(rtwdev, false); + if (ret) { + rtw89_err(rtwdev, "wow: failed to disable config wake\n"); + goto out; + } + + ret = rtw89_wow_check_fw_status(rtwdev, false); + if (ret) { + rtw89_err(rtwdev, "wow: failed to check disable fw ready\n"); + goto out; + } + +out: + return ret; +} + +static int rtw89_wow_enable(struct rtw89_dev *rtwdev) +{ + int ret; + + set_bit(RTW89_FLAG_WOWLAN, rtwdev->flags); + + ret = rtw89_wow_enable_trx_pre(rtwdev); + if (ret) { + rtw89_err(rtwdev, "wow: failed to enable trx_pre\n"); + goto out; + } + + rtw89_wow_swap_fw(rtwdev, true); + if (ret) { + rtw89_err(rtwdev, "wow: failed to swap to wow fw\n"); + goto out; + } + + rtw89_wow_fw_start(rtwdev); + if (ret) { + rtw89_err(rtwdev, "wow: failed to let wow fw start\n"); + goto out; + } + + rtw89_wow_enter_lps(rtwdev); + + rtw89_wow_enable_trx_post(rtwdev); + if (ret) { + rtw89_err(rtwdev, "wow: failed to enable trx_post\n"); + goto out; + } + + return 0; + +out: + clear_bit(RTW89_FLAG_WOWLAN, rtwdev->flags); + return ret; +} + +static int rtw89_wow_disable(struct rtw89_dev *rtwdev) +{ + int ret; + + ret = rtw89_wow_disable_trx_pre(rtwdev); + if (ret) { + rtw89_err(rtwdev, "wow: failed to disable trx_pre\n"); + goto out; + } + + rtw89_wow_leave_lps(rtwdev); + + ret = rtw89_wow_fw_stop(rtwdev); + if (ret) { + rtw89_err(rtwdev, "wow: failed to swap to normal fw\n"); + goto out; + } + + ret = rtw89_wow_swap_fw(rtwdev, false); + if (ret) { + rtw89_err(rtwdev, "wow: failed to disable trx_post\n"); + goto out; + } + + ret = rtw89_wow_disable_trx_post(rtwdev); + if (ret) { + rtw89_err(rtwdev, "wow: failed to disable trx_pre\n"); + goto out; + } + +out: + clear_bit(RTW89_FLAG_WOWLAN, rtwdev->flags); + return ret; +} + +int rtw89_wow_resume(struct rtw89_dev *rtwdev) +{ + int ret; + + if (!test_bit(RTW89_FLAG_WOWLAN, rtwdev->flags)) { + rtw89_err(rtwdev, "wow is not enabled\n"); + ret = -EPERM; + goto out; + } + + if (!rtw89_mac_get_power_state(rtwdev)) { + rtw89_err(rtwdev, "chip is no power when resume\n"); + ret = -EPERM; + goto out; + } + + rtw89_wow_leave_deep_ps(rtwdev); + + rtw89_wow_show_wakeup_reason(rtwdev); + + ret = rtw89_wow_disable(rtwdev); + if (ret) + rtw89_err(rtwdev, "failed to disable wow\n"); + +out: + rtw89_wow_clear_wakeups(rtwdev); + return ret; +} + +int rtw89_wow_suspend(struct rtw89_dev *rtwdev, struct cfg80211_wowlan *wowlan) +{ + int ret; + + ret = rtw89_wow_set_wakeups(rtwdev, wowlan); + if (ret) { + rtw89_err(rtwdev, "failed to set wakeup event\n"); + return ret; + } + + rtw89_wow_leave_lps(rtwdev); + + ret = rtw89_wow_enable(rtwdev); + if (ret) { + rtw89_err(rtwdev, "failed to enable wow\n"); + return ret; + } + + rtw89_wow_enter_deep_ps(rtwdev); + + return 0; +} diff --git a/drivers/net/wireless/realtek/rtw89/wow.h b/drivers/net/wireless/realtek/rtw89/wow.h new file mode 100644 index 000000000000..a2f7b2e3cdb4 --- /dev/null +++ b/drivers/net/wireless/realtek/rtw89/wow.h @@ -0,0 +1,21 @@ +/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ +/* Copyright(c) 2019-2022 Realtek Corporation + */ + +#ifndef __RTW89_WOW_H__ +#define __RTW89_WOW_H__ + +enum rtw89_wake_reason { + RTW89_WOW_RSN_RX_PTK_REKEY = 0x1, + RTW89_WOW_RSN_RX_GTK_REKEY = 0x2, + RTW89_WOW_RSN_RX_DEAUTH = 0x8, + RTW89_WOW_RSN_DISCONNECT = 0x10, + RTW89_WOW_RSN_RX_MAGIC_PKT = 0x21, + RTW89_WOW_RSN_RX_PATTERN_MATCH = 0x23, + RTW89_WOW_RSN_RX_NLO = 0x55, +}; + +int rtw89_wow_suspend(struct rtw89_dev *rtwdev, struct cfg80211_wowlan *wowlan); +int rtw89_wow_resume(struct rtw89_dev *rtwdev); + +#endif From d2b68e95b5bc97d81d150a46447167bf21bd555f Mon Sep 17 00:00:00 2001 From: Chin-Yen Lee Date: Thu, 27 Oct 2022 13:27:07 +0800 Subject: [PATCH 0514/2002] wifi: rtw89: add WoWLAN pattern match support Pattern match is an option of WoWLAN to allow the device to be woken up from suspend mode when receiving packets matched user-designed patterns. The patterns are written into hardware via WoWLAN firmware in suspend flow if users have set up them. If packets matched designed pattern are received, WoWLAN firmware will send an interrupt and then wake up the device. Signed-off-by: Chin-Yen Lee Signed-off-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20221027052707.14605-8-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/core.h | 18 ++ drivers/net/wireless/realtek/rtw89/fw.c | 52 ++++ drivers/net/wireless/realtek/rtw89/fw.h | 67 +++++ drivers/net/wireless/realtek/rtw89/rtw8852a.c | 3 + drivers/net/wireless/realtek/rtw89/rtw8852c.c | 3 + drivers/net/wireless/realtek/rtw89/util.h | 11 + drivers/net/wireless/realtek/rtw89/wow.c | 228 +++++++++++++++++- 7 files changed, 381 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index 9f80359e27aa..b60de6662548 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -3489,9 +3489,27 @@ struct rtw89_phy_efuse_gain { s8 comp[RF_PATH_MAX][RTW89_SUBBAND_NR]; /* S(8, 0) */ }; +#define RTW89_MAX_PATTERN_NUM 18 +#define RTW89_MAX_PATTERN_MASK_SIZE 4 +#define RTW89_MAX_PATTERN_SIZE 128 + +struct rtw89_wow_cam_info { + bool r_w; + u8 idx; + u32 mask[RTW89_MAX_PATTERN_MASK_SIZE]; + u16 crc; + bool negative_pattern_match; + bool skip_mac_hdr; + bool uc; + bool mc; + bool bc; + bool valid; +}; + struct rtw89_wow_param { struct ieee80211_vif *wow_vif; DECLARE_BITMAP(flags, RTW89_WOW_FLAG_NUM); + struct rtw89_wow_cam_info patterns[RTW89_MAX_PATTERN_NUM]; u8 pattern_cnt; struct list_head pkt_list; }; diff --git a/drivers/net/wireless/realtek/rtw89/fw.c b/drivers/net/wireless/realtek/rtw89/fw.c index 2cbcf23409db..10c5ed21da02 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.c +++ b/drivers/net/wireless/realtek/rtw89/fw.c @@ -3172,3 +3172,55 @@ fail: return ret; } + +#define H2C_WOW_CAM_UPD_LEN 24 +int rtw89_fw_wow_cam_update(struct rtw89_dev *rtwdev, + struct rtw89_wow_cam_info *cam_info) +{ + struct sk_buff *skb; + int ret; + + skb = rtw89_fw_h2c_alloc_skb_with_hdr(rtwdev, H2C_WOW_CAM_UPD_LEN); + if (!skb) { + rtw89_err(rtwdev, "failed to alloc skb for keep alive\n"); + return -ENOMEM; + } + + skb_put(skb, H2C_WOW_CAM_UPD_LEN); + + RTW89_SET_WOW_CAM_UPD_R_W(skb->data, cam_info->r_w); + RTW89_SET_WOW_CAM_UPD_IDX(skb->data, cam_info->idx); + if (cam_info->valid) { + RTW89_SET_WOW_CAM_UPD_WKFM1(skb->data, cam_info->mask[0]); + RTW89_SET_WOW_CAM_UPD_WKFM2(skb->data, cam_info->mask[1]); + RTW89_SET_WOW_CAM_UPD_WKFM3(skb->data, cam_info->mask[2]); + RTW89_SET_WOW_CAM_UPD_WKFM4(skb->data, cam_info->mask[3]); + RTW89_SET_WOW_CAM_UPD_CRC(skb->data, cam_info->crc); + RTW89_SET_WOW_CAM_UPD_NEGATIVE_PATTERN_MATCH(skb->data, + cam_info->negative_pattern_match); + RTW89_SET_WOW_CAM_UPD_SKIP_MAC_HDR(skb->data, + cam_info->skip_mac_hdr); + RTW89_SET_WOW_CAM_UPD_UC(skb->data, cam_info->uc); + RTW89_SET_WOW_CAM_UPD_MC(skb->data, cam_info->mc); + RTW89_SET_WOW_CAM_UPD_BC(skb->data, cam_info->bc); + } + RTW89_SET_WOW_CAM_UPD_VALID(skb->data, cam_info->valid); + + rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C, + H2C_CAT_MAC, + H2C_CL_MAC_WOW, + H2C_FUNC_WOW_CAM_UPD, 0, 1, + H2C_WOW_CAM_UPD_LEN); + + ret = rtw89_h2c_tx(rtwdev, skb, false); + if (ret) { + rtw89_err(rtwdev, "failed to send h2c\n"); + goto fail; + } + + return 0; +fail: + dev_kfree_skb_any(skb); + + return ret; +} diff --git a/drivers/net/wireless/realtek/rtw89/fw.h b/drivers/net/wireless/realtek/rtw89/fw.h index 25dd7bf8e730..509a3eac5ffe 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.h +++ b/drivers/net/wireless/realtek/rtw89/fw.h @@ -2033,6 +2033,71 @@ static inline void RTW89_SET_WOW_WAKEUP_CTRL_MAC_ID(void *h2c, u32 val) le32p_replace_bits((__le32 *)h2c, val, GENMASK(31, 24)); } +static inline void RTW89_SET_WOW_CAM_UPD_R_W(void *h2c, u32 val) +{ + le32p_replace_bits((__le32 *)h2c, val, BIT(0)); +} + +static inline void RTW89_SET_WOW_CAM_UPD_IDX(void *h2c, u32 val) +{ + le32p_replace_bits((__le32 *)h2c, val, GENMASK(7, 1)); +} + +static inline void RTW89_SET_WOW_CAM_UPD_WKFM1(void *h2c, u32 val) +{ + le32p_replace_bits((__le32 *)h2c + 1, val, GENMASK(31, 0)); +} + +static inline void RTW89_SET_WOW_CAM_UPD_WKFM2(void *h2c, u32 val) +{ + le32p_replace_bits((__le32 *)h2c + 2, val, GENMASK(31, 0)); +} + +static inline void RTW89_SET_WOW_CAM_UPD_WKFM3(void *h2c, u32 val) +{ + le32p_replace_bits((__le32 *)h2c + 3, val, GENMASK(31, 0)); +} + +static inline void RTW89_SET_WOW_CAM_UPD_WKFM4(void *h2c, u32 val) +{ + le32p_replace_bits((__le32 *)h2c + 4, val, GENMASK(31, 0)); +} + +static inline void RTW89_SET_WOW_CAM_UPD_CRC(void *h2c, u32 val) +{ + le32p_replace_bits((__le32 *)h2c + 5, val, GENMASK(15, 0)); +} + +static inline void RTW89_SET_WOW_CAM_UPD_NEGATIVE_PATTERN_MATCH(void *h2c, u32 val) +{ + le32p_replace_bits((__le32 *)h2c + 5, val, BIT(22)); +} + +static inline void RTW89_SET_WOW_CAM_UPD_SKIP_MAC_HDR(void *h2c, u32 val) +{ + le32p_replace_bits((__le32 *)h2c + 5, val, BIT(23)); +} + +static inline void RTW89_SET_WOW_CAM_UPD_UC(void *h2c, u32 val) +{ + le32p_replace_bits((__le32 *)h2c + 5, val, BIT(24)); +} + +static inline void RTW89_SET_WOW_CAM_UPD_MC(void *h2c, u32 val) +{ + le32p_replace_bits((__le32 *)h2c + 5, val, BIT(25)); +} + +static inline void RTW89_SET_WOW_CAM_UPD_BC(void *h2c, u32 val) +{ + le32p_replace_bits((__le32 *)h2c + 5, val, BIT(26)); +} + +static inline void RTW89_SET_WOW_CAM_UPD_VALID(void *h2c, u32 val) +{ + le32p_replace_bits((__le32 *)h2c + 5, val, BIT(31)); +} + enum rtw89_btc_btf_h2c_class { BTFC_SET = 0x10, BTFC_GET = 0x11, @@ -3039,6 +3104,8 @@ int rtw89_fw_h2c_wow_global(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif, int rtw89_fw_h2c_wow_wakeup_ctrl(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif, bool enable); +int rtw89_fw_wow_cam_update(struct rtw89_dev *rtwdev, + struct rtw89_wow_cam_info *cam_info); static inline void rtw89_fw_h2c_init_ba_cam(struct rtw89_dev *rtwdev) { const struct rtw89_chip_info *chip = rtwdev->chip; diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852a.c b/drivers/net/wireless/realtek/rtw89/rtw8852a.c index becce3b48f51..4cea5fb4327d 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852a.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852a.c @@ -1993,6 +1993,9 @@ static void rtw8852a_query_ppdu(struct rtw89_dev *rtwdev, #ifdef CONFIG_PM static const struct wiphy_wowlan_support rtw_wowlan_stub_8852a = { .flags = WIPHY_WOWLAN_MAGIC_PKT | WIPHY_WOWLAN_DISCONNECT, + .n_patterns = RTW89_MAX_PATTERN_NUM, + .pattern_max_len = RTW89_MAX_PATTERN_SIZE, + .pattern_min_len = 1, }; #endif diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852c.c b/drivers/net/wireless/realtek/rtw89/rtw8852c.c index acb0e2e0adba..6619ba730719 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852c.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852c.c @@ -2799,6 +2799,9 @@ static int rtw8852c_mac_disable_bb_rf(struct rtw89_dev *rtwdev) #ifdef CONFIG_PM static const struct wiphy_wowlan_support rtw_wowlan_stub_8852c = { .flags = WIPHY_WOWLAN_MAGIC_PKT | WIPHY_WOWLAN_DISCONNECT, + .n_patterns = RTW89_MAX_PATTERN_NUM, + .pattern_max_len = RTW89_MAX_PATTERN_SIZE, + .pattern_min_len = 1, }; #endif diff --git a/drivers/net/wireless/realtek/rtw89/util.h b/drivers/net/wireless/realtek/rtw89/util.h index 1ae80b7561da..e2ed4565025d 100644 --- a/drivers/net/wireless/realtek/rtw89/util.h +++ b/drivers/net/wireless/realtek/rtw89/util.h @@ -44,4 +44,15 @@ static inline s32 s32_div_u32_round_closest(s32 dividend, u32 divisor) return s32_div_u32_round_down(dividend + divisor / 2, divisor, NULL); } +static inline void ether_addr_copy_mask(u8 *dst, const u8 *src, u8 mask) +{ + int i; + + eth_zero_addr(dst); + for (i = 0; i < ETH_ALEN; i++) { + if (mask & BIT(i)) + dst[i] = src[i]; + } +} + #endif diff --git a/drivers/net/wireless/realtek/rtw89/wow.c b/drivers/net/wireless/realtek/rtw89/wow.c index 3d30083fcb9a..7de4dd047d6b 100644 --- a/drivers/net/wireless/realtek/rtw89/wow.c +++ b/drivers/net/wireless/realtek/rtw89/wow.c @@ -162,6 +162,227 @@ static void rtw89_wow_vif_iter(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvi } } +static u16 __rtw89_cal_crc16(u8 data, u16 crc) +{ + u8 shift_in, data_bit; + u8 crc_bit4, crc_bit11, crc_bit15; + u16 crc_result; + int index; + + for (index = 0; index < 8; index++) { + crc_bit15 = crc & BIT(15) ? 1 : 0; + data_bit = data & BIT(index) ? 1 : 0; + shift_in = crc_bit15 ^ data_bit; + + crc_result = crc << 1; + + if (shift_in == 0) + crc_result &= ~BIT(0); + else + crc_result |= BIT(0); + + crc_bit11 = (crc & BIT(11) ? 1 : 0) ^ shift_in; + + if (crc_bit11 == 0) + crc_result &= ~BIT(12); + else + crc_result |= BIT(12); + + crc_bit4 = (crc & BIT(4) ? 1 : 0) ^ shift_in; + + if (crc_bit4 == 0) + crc_result &= ~BIT(5); + else + crc_result |= BIT(5); + + crc = crc_result; + } + return crc; +} + +static u16 rtw89_calc_crc(u8 *pdata, int length) +{ + u16 crc = 0xffff; + int i; + + for (i = 0; i < length; i++) + crc = __rtw89_cal_crc16(pdata[i], crc); + + /* get 1' complement */ + return ~crc; +} + +static int rtw89_wow_pattern_get_type(struct rtw89_vif *rtwvif, + struct rtw89_wow_cam_info *rtw_pattern, + const u8 *pattern, u8 da_mask) +{ + u8 da[ETH_ALEN]; + + ether_addr_copy_mask(da, pattern, da_mask); + + /* Each pattern is divided into different kinds by DA address + * a. DA is broadcast address: set bc = 0; + * b. DA is multicast address: set mc = 0 + * c. DA is unicast address same as dev's mac address: set uc = 0 + * d. DA is unmasked. Also called wildcard type: set uc = bc = mc = 0 + * e. Others is invalid type. + */ + + if (is_broadcast_ether_addr(da)) + rtw_pattern->bc = true; + else if (is_multicast_ether_addr(da)) + rtw_pattern->mc = true; + else if (ether_addr_equal(da, rtwvif->mac_addr) && + da_mask == GENMASK(5, 0)) + rtw_pattern->uc = true; + else if (!da_mask) /*da_mask == 0 mean wildcard*/ + return 0; + else + return -EPERM; + + return 0; +} + +static int rtw89_wow_pattern_generate(struct rtw89_dev *rtwdev, + struct rtw89_vif *rtwvif, + const struct cfg80211_pkt_pattern *pkt_pattern, + struct rtw89_wow_cam_info *rtw_pattern) +{ + u8 mask_hw[RTW89_MAX_PATTERN_MASK_SIZE * 4] = {0}; + u8 content[RTW89_MAX_PATTERN_SIZE] = {0}; + const u8 *mask; + const u8 *pattern; + u8 mask_len; + u16 count; + u32 len; + int i, ret; + + pattern = pkt_pattern->pattern; + len = pkt_pattern->pattern_len; + mask = pkt_pattern->mask; + mask_len = DIV_ROUND_UP(len, 8); + memset(rtw_pattern, 0, sizeof(*rtw_pattern)); + + ret = rtw89_wow_pattern_get_type(rtwvif, rtw_pattern, pattern, + mask[0] & GENMASK(5, 0)); + if (ret) + return ret; + + /* translate mask from os to mask for hw + * pattern from OS uses 'ethenet frame', like this: + * | 6 | 6 | 2 | 20 | Variable | 4 | + * |--------+--------+------+-----------+------------+-----| + * | 802.3 Mac Header | IP Header | TCP Packet | FCS | + * | DA | SA | Type | + * + * BUT, packet catched by our HW is in '802.11 frame', begin from LLC + * | 24 or 30 | 6 | 2 | 20 | Variable | 4 | + * |-------------------+--------+------+-----------+------------+-----| + * | 802.11 MAC Header | LLC | IP Header | TCP Packet | FCS | + * | Others | Tpye | + * + * Therefore, we need translate mask_from_OS to mask_to_hw. + * We should left-shift mask by 6 bits, then set the new bit[0~5] = 0, + * because new mask[0~5] means 'SA', but our HW packet begins from LLC, + * bit[0~5] corresponds to first 6 Bytes in LLC, they just don't match. + */ + + /* Shift 6 bits */ + for (i = 0; i < mask_len - 1; i++) { + mask_hw[i] = u8_get_bits(mask[i], GENMASK(7, 6)) | + u8_get_bits(mask[i + 1], GENMASK(5, 0)) << 2; + } + mask_hw[i] = u8_get_bits(mask[i], GENMASK(7, 6)); + + /* Set bit 0-5 to zero */ + mask_hw[0] &= ~GENMASK(5, 0); + + memcpy(rtw_pattern->mask, mask_hw, sizeof(rtw_pattern->mask)); + + /* To get the wake up pattern from the mask. + * We do not count first 12 bits which means + * DA[6] and SA[6] in the pattern to match HW design. + */ + count = 0; + for (i = 12; i < len; i++) { + if ((mask[i / 8] >> (i % 8)) & 0x01) { + content[count] = pattern[i]; + count++; + } + } + + rtw_pattern->crc = rtw89_calc_crc(content, count); + + return 0; +} + +static int rtw89_wow_parse_patterns(struct rtw89_dev *rtwdev, + struct rtw89_vif *rtwvif, + struct cfg80211_wowlan *wowlan) +{ + struct rtw89_wow_param *rtw_wow = &rtwdev->wow; + struct rtw89_wow_cam_info *rtw_pattern = rtw_wow->patterns; + int i; + int ret; + + if (!wowlan->n_patterns || !wowlan->patterns) + return 0; + + for (i = 0; i < wowlan->n_patterns; i++) { + rtw_pattern = &rtw_wow->patterns[i]; + ret = rtw89_wow_pattern_generate(rtwdev, rtwvif, + &wowlan->patterns[i], + rtw_pattern); + if (ret) { + rtw89_err(rtwdev, "failed to generate pattern(%d)\n", i); + rtw_wow->pattern_cnt = 0; + return ret; + } + + rtw_pattern->r_w = true; + rtw_pattern->idx = i; + rtw_pattern->negative_pattern_match = false; + rtw_pattern->skip_mac_hdr = true; + rtw_pattern->valid = true; + } + rtw_wow->pattern_cnt = wowlan->n_patterns; + + return 0; +} + +static void rtw89_wow_pattern_clear_cam(struct rtw89_dev *rtwdev) +{ + struct rtw89_wow_param *rtw_wow = &rtwdev->wow; + struct rtw89_wow_cam_info *rtw_pattern = rtw_wow->patterns; + int i = 0; + + for (i = 0; i < rtw_wow->pattern_cnt; i++) { + rtw_pattern = &rtw_wow->patterns[i]; + rtw_pattern->valid = false; + rtw89_fw_wow_cam_update(rtwdev, rtw_pattern); + } +} + +static void rtw89_wow_pattern_write(struct rtw89_dev *rtwdev) +{ + struct rtw89_wow_param *rtw_wow = &rtwdev->wow; + struct rtw89_wow_cam_info *rtw_pattern = rtw_wow->patterns; + int i; + + for (i = 0; i < rtw_wow->pattern_cnt; i++) + rtw89_fw_wow_cam_update(rtwdev, rtw_pattern + i); +} + +static void rtw89_wow_pattern_clear(struct rtw89_dev *rtwdev) +{ + struct rtw89_wow_param *rtw_wow = &rtwdev->wow; + + rtw89_wow_pattern_clear_cam(rtwdev); + + rtw_wow->pattern_cnt = 0; + memset(rtw_wow->patterns, 0, sizeof(rtw_wow->patterns)); +} + static void rtw89_wow_clear_wakeups(struct rtw89_dev *rtwdev) { struct rtw89_wow_param *rtw_wow = &rtwdev->wow; @@ -188,7 +409,8 @@ static int rtw89_wow_set_wakeups(struct rtw89_dev *rtwdev, if (!rtw_wow->wow_vif) return -EPERM; - return 0; + rtwvif = (struct rtw89_vif *)rtw_wow->wow_vif->drv_priv; + return rtw89_wow_parse_patterns(rtwdev, rtwvif, wowlan); } static int rtw89_wow_cfg_wake(struct rtw89_dev *rtwdev, bool wow) @@ -442,6 +664,8 @@ static int rtw89_wow_fw_start(struct rtw89_dev *rtwdev) struct rtw89_vif *rtwvif = (struct rtw89_vif *)rtw_wow->wow_vif->drv_priv; int ret; + rtw89_wow_pattern_write(rtwdev); + ret = rtw89_fw_h2c_keep_alive(rtwdev, rtwvif, true); if (ret) { rtw89_err(rtwdev, "wow: failed to enable keep alive\n"); @@ -476,6 +700,8 @@ static int rtw89_wow_fw_stop(struct rtw89_dev *rtwdev) struct rtw89_vif *rtwvif = (struct rtw89_vif *)rtw_wow->wow_vif->drv_priv; int ret; + rtw89_wow_pattern_clear(rtwdev); + ret = rtw89_fw_h2c_keep_alive(rtwdev, rtwvif, false); if (ret) { rtw89_err(rtwdev, "wow: failed to disable keep alive\n"); From 8fa681703175ba0ea283dd564a64edaef3472ee6 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Thu, 20 Oct 2022 08:26:46 +0100 Subject: [PATCH 0515/2002] wifi: rtw89: 8852b: Fix spelling mistake KIP_RESOTRE -> KIP_RESTORE Ther is a spelling mistake in a rtw89_debug message. Fix it. Signed-off-by: Colin Ian King Acked-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20221020072646.1513307-1-colin.i.king@gmail.com --- drivers/net/wireless/realtek/rtw89/rtw8852b_rfk.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852b_rfk.c b/drivers/net/wireless/realtek/rtw89/rtw8852b_rfk.c index 8fd01502ac5b..722ae34b09c1 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852b_rfk.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852b_rfk.c @@ -1754,7 +1754,7 @@ static void _dpk_one_shot(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy, id == 0x14 ? "PWR_CAL" : id == 0x15 ? "DPK_RXAGC" : id == 0x16 ? "KIP_PRESET" : - id == 0x17 ? "KIP_RESOTRE" : "DPK_TXAGC", + id == 0x17 ? "KIP_RESTORE" : "DPK_TXAGC", dpk_cmd); } From 7bb09fb8f5772c92313834e60eb350f2d8888921 Mon Sep 17 00:00:00 2001 From: Jiapeng Chong Date: Mon, 17 Oct 2022 15:17:46 +0800 Subject: [PATCH 0516/2002] wifi: ipw2200: Remove the unused function ipw_alive() The function ipw_alive() is defined in the ipw2200.c file, but not called elsewhere, so delete this unused function. drivers/net/wireless/intel/ipw2x00/ipw2200.c:3007:19: warning: unused function 'ipw_alive'. Link: https://bugzilla.openanolis.cn/show_bug.cgi?id=2410 Reported-by: Abaci Robot Signed-off-by: Jiapeng Chong Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20221017071746.118685-1-jiapeng.chong@linux.alibaba.com --- drivers/net/wireless/intel/ipw2x00/ipw2200.c | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/drivers/net/wireless/intel/ipw2x00/ipw2200.c b/drivers/net/wireless/intel/ipw2x00/ipw2200.c index 5b483de18c81..79d5c09757d4 100644 --- a/drivers/net/wireless/intel/ipw2x00/ipw2200.c +++ b/drivers/net/wireless/intel/ipw2x00/ipw2200.c @@ -2995,20 +2995,6 @@ static void ipw_remove_current_network(struct ipw_priv *priv) spin_unlock_irqrestore(&priv->ieee->lock, flags); } -/* - * Check that card is still alive. - * Reads debug register from domain0. - * If card is present, pre-defined value should - * be found there. - * - * @param priv - * @return 1 if card is present, 0 otherwise - */ -static inline int ipw_alive(struct ipw_priv *priv) -{ - return ipw_read32(priv, 0x90) == 0xd55555d5; -} - /* timeout in msec, attempted in 10-msec quanta */ static int ipw_poll_bit(struct ipw_priv *priv, u32 addr, u32 mask, int timeout) From 4d1c7d87030be2a0043620b240cdb88199396366 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Wed, 26 Oct 2022 11:35:00 +0200 Subject: [PATCH 0517/2002] mac802154: Move an skb free within the rx path It may appear clearer to free the skb at the end of the path rather than in the middle, within a helper. Move kfree_skb() from the end of __ieee802154_rx_handle_packet() to right after it in the calling function ieee802154_rx(). Doing so implies reworking a little bit the exit path. Suggested-by: Alexander Aring Signed-off-by: Miquel Raynal Acked-by: Alexander Aring Link: https://lore.kernel.org/r/20221026093502.602734-2-miquel.raynal@bootlin.com Signed-off-by: Stefan Schmidt --- net/mac802154/rx.c | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/net/mac802154/rx.c b/net/mac802154/rx.c index 2ae23a2f4a09..499f71df257f 100644 --- a/net/mac802154/rx.c +++ b/net/mac802154/rx.c @@ -234,8 +234,6 @@ __ieee802154_rx_handle_packet(struct ieee802154_local *local, skb = NULL; break; } - - kfree_skb(skb); } static void @@ -274,7 +272,7 @@ void ieee802154_rx(struct ieee802154_local *local, struct sk_buff *skb) WARN_ON_ONCE(softirq_count() == 0); if (local->suspended) - goto drop; + goto free_skb; /* TODO: When a transceiver omits the checksum here, we * add an own calculated one. This is currently an ugly @@ -292,20 +290,17 @@ void ieee802154_rx(struct ieee802154_local *local, struct sk_buff *skb) /* Level 1 filtering: Check the FCS by software when relevant */ if (local->hw.phy->filtering == IEEE802154_FILTERING_NONE) { crc = crc_ccitt(0, skb->data, skb->len); - if (crc) { - rcu_read_unlock(); + if (crc) goto drop; - } } /* remove crc */ skb_trim(skb, skb->len - 2); __ieee802154_rx_handle_packet(local, skb); - rcu_read_unlock(); - - return; drop: + rcu_read_unlock(); +free_skb: kfree_skb(skb); } From eb30a7a5c88f65d06c35c2268af0075c0d6d803e Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Wed, 26 Oct 2022 11:35:01 +0200 Subject: [PATCH 0518/2002] mac802154: Clarify an expression While going through the whole interface opening logic in my head I was consistently bothered by the condition checking whether there was only one interface of type NODE/COORD opened at the same time. What actually bothered me was the fact that in one case we would use the wpan_dev pointer directly while in the other case we would use the sdata pointer, making it harder to differentiate both. In practice the condition should be straightforward to read. IMHO dropping the wpan_dev indirection allows to clarify the check. Signed-off-by: Miquel Raynal Acked-by: Alexander Aring Link: https://lore.kernel.org/r/20221026093502.602734-3-miquel.raynal@bootlin.com Signed-off-by: Stefan Schmidt --- net/mac802154/iface.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/net/mac802154/iface.c b/net/mac802154/iface.c index d9b50884d34e..f21132dd82cf 100644 --- a/net/mac802154/iface.c +++ b/net/mac802154/iface.c @@ -254,7 +254,6 @@ ieee802154_check_concurrent_iface(struct ieee802154_sub_if_data *sdata, enum nl802154_iftype iftype) { struct ieee802154_local *local = sdata->local; - struct wpan_dev *wpan_dev = &sdata->wpan_dev; struct ieee802154_sub_if_data *nsdata; /* we hold the RTNL here so can safely walk the list */ @@ -267,7 +266,7 @@ ieee802154_check_concurrent_iface(struct ieee802154_sub_if_data *sdata, * exist really an use case if we need to support * multiple node types at the same time. */ - if (wpan_dev->iftype == NL802154_IFTYPE_NODE && + if (sdata->wpan_dev.iftype == NL802154_IFTYPE_NODE && nsdata->wpan_dev.iftype == NL802154_IFTYPE_NODE) return -EBUSY; From 2622e785f7579e8c92d0fcf55e9d6b3955e4f1a6 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Wed, 26 Oct 2022 11:35:02 +0200 Subject: [PATCH 0519/2002] mac802154: Allow the creation of coordinator interfaces As a first strep in introducing proper PAN management and association, we need to be able to create coordinator interfaces which might act as coordinator or PAN coordinator. Hence, let's add the minimum support to allow the creation of these interfaces. Even though the necessary logic to handle several interfaces on the same device is added to make this future move easier, in practice only several interfaces of type MONITOR are allowed at the same time. The other combinations are not allowed (interface creation is possible but only one can be opened at a time) because, with a single PHY featuring a single set of address filters, we cannot afford handling two distinct interfaces (with different address filters or filtering requirements): * Having 2 NODEs, 2 COORDs or 1 NODE + 1 COORD -> cannot work because the address filters would be different * Having 1 MONITOR + either 1 NODE or 1 COORD -> cannot work because the filtering levels are incompatible Signed-off-by: Miquel Raynal Acked-by: Alexander Aring Link: https://lore.kernel.org/r/20221026093502.602734-4-miquel.raynal@bootlin.com Signed-off-by: Stefan Schmidt --- net/mac802154/iface.c | 14 ++++++++------ net/mac802154/main.c | 2 +- net/mac802154/rx.c | 11 +++++++---- 3 files changed, 16 insertions(+), 11 deletions(-) diff --git a/net/mac802154/iface.c b/net/mac802154/iface.c index f21132dd82cf..7de2f843379c 100644 --- a/net/mac802154/iface.c +++ b/net/mac802154/iface.c @@ -261,13 +261,13 @@ ieee802154_check_concurrent_iface(struct ieee802154_sub_if_data *sdata, if (nsdata != sdata && ieee802154_sdata_running(nsdata)) { int ret; - /* TODO currently we don't support multiple node types - * we need to run skb_clone at rx path. Check if there - * exist really an use case if we need to support - * multiple node types at the same time. + /* TODO currently we don't support multiple node/coord + * types we need to run skb_clone at rx path. Check if + * there exist really an use case if we need to support + * multiple node/coord types at the same time. */ - if (sdata->wpan_dev.iftype == NL802154_IFTYPE_NODE && - nsdata->wpan_dev.iftype == NL802154_IFTYPE_NODE) + if (sdata->wpan_dev.iftype != NL802154_IFTYPE_MONITOR && + nsdata->wpan_dev.iftype != NL802154_IFTYPE_MONITOR) return -EBUSY; /* check all phy mac sublayer settings are the same. @@ -564,6 +564,7 @@ ieee802154_setup_sdata(struct ieee802154_sub_if_data *sdata, wpan_dev->short_addr = cpu_to_le16(IEEE802154_ADDR_BROADCAST); switch (type) { + case NL802154_IFTYPE_COORD: case NL802154_IFTYPE_NODE: ieee802154_be64_to_le64(&wpan_dev->extended_addr, sdata->dev->dev_addr); @@ -623,6 +624,7 @@ ieee802154_if_add(struct ieee802154_local *local, const char *name, ieee802154_le64_to_be64(ndev->perm_addr, &local->hw.phy->perm_extended_addr); switch (type) { + case NL802154_IFTYPE_COORD: case NL802154_IFTYPE_NODE: ndev->type = ARPHRD_IEEE802154; if (ieee802154_is_valid_extended_unicast_addr(extended_addr)) { diff --git a/net/mac802154/main.c b/net/mac802154/main.c index 40fab08df24b..3ed31daf7b9c 100644 --- a/net/mac802154/main.c +++ b/net/mac802154/main.c @@ -107,7 +107,7 @@ ieee802154_alloc_hw(size_t priv_data_len, const struct ieee802154_ops *ops) phy->supported.lbt = NL802154_SUPPORTED_BOOL_FALSE; /* always supported */ - phy->supported.iftypes = BIT(NL802154_IFTYPE_NODE); + phy->supported.iftypes = BIT(NL802154_IFTYPE_NODE) | BIT(NL802154_IFTYPE_COORD); return &local->hw; } diff --git a/net/mac802154/rx.c b/net/mac802154/rx.c index 499f71df257f..7ca41fd259f1 100644 --- a/net/mac802154/rx.c +++ b/net/mac802154/rx.c @@ -208,6 +208,7 @@ __ieee802154_rx_handle_packet(struct ieee802154_local *local, int ret; struct ieee802154_sub_if_data *sdata; struct ieee802154_hdr hdr; + struct sk_buff *skb2; ret = ieee802154_parse_frame_start(skb, &hdr); if (ret) { @@ -217,7 +218,7 @@ __ieee802154_rx_handle_packet(struct ieee802154_local *local, } list_for_each_entry_rcu(sdata, &local->interfaces, list) { - if (sdata->wpan_dev.iftype != NL802154_IFTYPE_NODE) + if (sdata->wpan_dev.iftype == NL802154_IFTYPE_MONITOR) continue; if (!ieee802154_sdata_running(sdata)) @@ -230,9 +231,11 @@ __ieee802154_rx_handle_packet(struct ieee802154_local *local, sdata->required_filtering == IEEE802154_FILTERING_4_FRAME_FIELDS) continue; - ieee802154_subif_frame(sdata, skb, &hdr); - skb = NULL; - break; + skb2 = skb_clone(skb, GFP_ATOMIC); + if (skb2) { + skb2->dev = sdata->dev; + ieee802154_subif_frame(sdata, skb2, &hdr); + } } } From 8b860466b137d77cb95907cacb1c9443712e6f6b Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Mon, 17 Oct 2022 19:37:59 -0700 Subject: [PATCH 0520/2002] wifi: atmel: Fix atmel_private_handler array size Fix the atmel_private_handler to correctly sized (1 element) again. (I should have checked the data segment for differences.) This had no behavioral impact (no private callbacks), but it made a very large zero-filled array. Cc: Simon Kelley Cc: Kalle Valo Cc: "David S. Miller" Cc: Eric Dumazet Cc: Jakub Kicinski Cc: Paolo Abeni Cc: linux-wireless@vger.kernel.org Cc: netdev@vger.kernel.org Fixes: 8af9d4068e86 ("wifi: atmel: Avoid clashing function prototypes") Signed-off-by: Kees Cook Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20221018023732.never.700-kees@kernel.org --- drivers/net/wireless/atmel/atmel.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/atmel/atmel.c b/drivers/net/wireless/atmel/atmel.c index ed430b91e2aa..7c2d1c588156 100644 --- a/drivers/net/wireless/atmel/atmel.c +++ b/drivers/net/wireless/atmel/atmel.c @@ -2570,7 +2570,7 @@ static const iw_handler atmel_handler[] = static const iw_handler atmel_private_handler[] = { - IW_HANDLER(SIOCIWFIRSTPRIV, NULL), + NULL, /* SIOCIWFIRSTPRIV */ }; struct atmel_priv_ioctl { From 57d545b5a3d6ce3a8fb6b093f02bfcbb908973f3 Mon Sep 17 00:00:00 2001 From: Michael Walle Date: Thu, 27 Oct 2022 19:12:21 +0200 Subject: [PATCH 0521/2002] wifi: wilc1000: sdio: fix module autoloading There are no SDIO module aliases included in the driver, therefore, module autoloading isn't working. Add the proper MODULE_DEVICE_TABLE(). Cc: stable@vger.kernel.org Signed-off-by: Michael Walle Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20221027171221.491937-1-michael@walle.cc --- drivers/net/wireless/microchip/wilc1000/sdio.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/wireless/microchip/wilc1000/sdio.c b/drivers/net/wireless/microchip/wilc1000/sdio.c index 7390f94cd4ca..a05bda7b9a3b 100644 --- a/drivers/net/wireless/microchip/wilc1000/sdio.c +++ b/drivers/net/wireless/microchip/wilc1000/sdio.c @@ -20,6 +20,7 @@ static const struct sdio_device_id wilc_sdio_ids[] = { { SDIO_DEVICE(SDIO_VENDOR_ID_MICROCHIP_WILC, SDIO_DEVICE_ID_MICROCHIP_WILC1000) }, { }, }; +MODULE_DEVICE_TABLE(sdio, wilc_sdio_ids); #define WILC_SDIO_BLOCK_SIZE 512 From 66394126bf2045d25b70c9327a6372186bba7d66 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Fri, 28 Oct 2022 10:05:57 +0200 Subject: [PATCH 0522/2002] netfilter: nft_payload: use __be16 to store gre version GRE_VERSION and GRE_VERSION0 are expressed in network byte order, use __be16. Uncovered by sparse: net/netfilter/nft_payload.c:112:25: warning: incorrect type in assignment (different base types) net/netfilter/nft_payload.c:112:25: expected unsigned int [usertype] version net/netfilter/nft_payload.c:112:25: got restricted __be16 net/netfilter/nft_payload.c:114:22: warning: restricted __be16 degrades to integer Fixes: c247897d7c19 ("netfilter: nft_payload: access GRE payload via inner offset") Reported-by: Jakub Kicinski Signed-off-by: Pablo Neira Ayuso --- net/netfilter/nft_payload.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/net/netfilter/nft_payload.c b/net/netfilter/nft_payload.c index 9d2ac764a14c..53e64d8aa01f 100644 --- a/net/netfilter/nft_payload.c +++ b/net/netfilter/nft_payload.c @@ -102,8 +102,9 @@ static int __nft_payload_inner_offset(struct nft_pktinfo *pkt) } break; case IPPROTO_GRE: { - u32 offset = sizeof(struct gre_base_hdr), version; + u32 offset = sizeof(struct gre_base_hdr); struct gre_base_hdr *gre, _gre; + __be16 version; gre = skb_header_pointer(pkt->skb, thoff, sizeof(_gre), &_gre); if (!gre) From 7394c2dd62dec8bdbe347d195c2dc1b332cc9ec5 Mon Sep 17 00:00:00 2001 From: Peng Wu Date: Tue, 1 Nov 2022 01:37:28 +0000 Subject: [PATCH 0523/2002] netfilter: nft_inner: fix return value check in nft_inner_parse_l2l3() In nft_inner_parse_l2l3(), the return value of skb_header_pointer() is 'veth' instead of 'eth' when case 'htons(ETH_P_8021Q)' and fix it. Fixes: 3a07327d10a0 ("netfilter: nft_inner: support for inner tunnel header matching") Signed-off-by: Peng Wu Signed-off-by: Pablo Neira Ayuso --- net/netfilter/nft_inner.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/netfilter/nft_inner.c b/net/netfilter/nft_inner.c index eae7caeff316..809f0d0787ec 100644 --- a/net/netfilter/nft_inner.c +++ b/net/netfilter/nft_inner.c @@ -72,7 +72,7 @@ static int nft_inner_parse_l2l3(const struct nft_inner *priv, break; case htons(ETH_P_8021Q): veth = skb_header_pointer(pkt->skb, off, sizeof(_veth), &_veth); - if (!eth) + if (!veth) return -1; outer_llproto = veth->h_vlan_encapsulated_proto; From 3a07dcf8f57b9a90b1c07df3e9091fd04baa3036 Mon Sep 17 00:00:00 2001 From: Kang Minchul Date: Mon, 31 Oct 2022 03:02:54 +0900 Subject: [PATCH 0524/2002] samples/bpf: Fix typo in README Fix 'cofiguration' typo in BPF samples README. Signed-off-by: Kang Minchul Signed-off-by: Daniel Borkmann Link: https://lore.kernel.org/bpf/20221030180254.34138-1-tegongkang@gmail.com --- samples/bpf/README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/bpf/README.rst b/samples/bpf/README.rst index 6cfb74fb5ee6..57f93edd1957 100644 --- a/samples/bpf/README.rst +++ b/samples/bpf/README.rst @@ -87,7 +87,7 @@ Cross compiling samples ----------------------- In order to cross-compile, say for arm64 targets, export CROSS_COMPILE and ARCH environment variables before calling make. But do this before clean, -cofiguration and header install steps described above. This will direct make to +configuration and header install steps described above. This will direct make to build samples for the cross target:: export ARCH=arm64 From 44827016be44c6b2634a92ebbdb3d95610ff5268 Mon Sep 17 00:00:00 2001 From: "Dr. David Alan Gilbert" Date: Sat, 29 Oct 2022 02:46:04 +0100 Subject: [PATCH 0525/2002] net: core: inet[46]_pton strlen len types inet[46]_pton check the input length against a sane length limit (INET[6]_ADDRSTRLEN), but the strlen value gets truncated due to being stored in an int, so there's a theoretical potential for a >4G string to pass the limit test. Use size_t since that's what strlen actually returns. I've had a hunt for callers that could hit this, but I've not managed to find anything that doesn't get checked with some other limit first; but it's possible that I've missed something in the depth of the storage target paths. Signed-off-by: Dr. David Alan Gilbert Link: https://lore.kernel.org/r/20221029014604.114024-1-linux@treblig.org Signed-off-by: Jakub Kicinski --- net/core/utils.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/core/utils.c b/net/core/utils.c index 938495bc1d34..c994e95172ac 100644 --- a/net/core/utils.c +++ b/net/core/utils.c @@ -302,7 +302,7 @@ static int inet4_pton(const char *src, u16 port_num, struct sockaddr_storage *addr) { struct sockaddr_in *addr4 = (struct sockaddr_in *)addr; - int srclen = strlen(src); + size_t srclen = strlen(src); if (srclen > INET_ADDRSTRLEN) return -EINVAL; @@ -322,7 +322,7 @@ static int inet6_pton(struct net *net, const char *src, u16 port_num, { struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)addr; const char *scope_delim; - int srclen = strlen(src); + size_t srclen = strlen(src); if (srclen > INET6_ADDRSTRLEN) return -EINVAL; From b0e01253a764faab9ecf56c0759e6b06470eb9ff Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Tue, 1 Nov 2022 03:52:34 +0000 Subject: [PATCH 0526/2002] tcp: refine tcp_prune_ofo_queue() logic After commits 36a6503fedda ("tcp: refine tcp_prune_ofo_queue() to not drop all packets") and 72cd43ba64fc1 ("tcp: free batches of packets in tcp_prune_ofo_queue()") tcp_prune_ofo_queue() drops a fraction of ooo queue, to make room for incoming packet. However it makes no sense to drop packets that are before the incoming packet, in sequence space. In order to recover from packet losses faster, it makes more sense to only drop ooo packets which are after the incoming packet. Tested: packetdrill test: 0 socket(..., SOCK_STREAM, IPPROTO_TCP) = 3 +0 setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0 +0 setsockopt(3, SOL_SOCKET, SO_RCVBUF, [3800], 4) = 0 +0 bind(3, ..., ...) = 0 +0 listen(3, 1) = 0 +0 < S 0:0(0) win 32792 +0 > S. 0:0(0) ack 1 +.1 < . 1:1(0) ack 1 win 1024 +0 accept(3, ..., ...) = 4 +.01 < . 200:300(100) ack 1 win 1024 +0 > . 1:1(0) ack 1 +.01 < . 400:500(100) ack 1 win 1024 +0 > . 1:1(0) ack 1 +.01 < . 600:700(100) ack 1 win 1024 +0 > . 1:1(0) ack 1 +.01 < . 800:900(100) ack 1 win 1024 +0 > . 1:1(0) ack 1 +.01 < . 1000:1100(100) ack 1 win 1024 +0 > . 1:1(0) ack 1 +.01 < . 1200:1300(100) ack 1 win 1024 +0 > . 1:1(0) ack 1 // this packet is dropped because we have no room left. +.01 < . 1400:1500(100) ack 1 win 1024 +.01 < . 1:200(199) ack 1 win 1024 // Make sure kernel did not drop 200:300 sequence +0 > . 1:1(0) ack 300 // Make room, since our RCVBUF is very small +0 read(4, ..., 299) = 299 +.01 < . 300:400(100) ack 1 win 1024 +0 > . 1:1(0) ack 500 +.01 < . 500:600(100) ack 1 win 1024 +0 > . 1:1(0) ack 700 +0 read(4, ..., 400) = 400 +.01 < . 700:800(100) ack 1 win 1024 +0 > . 1:1(0) ack 900 +.01 < . 900:1000(100) ack 1 win 1024 +0 > . 1:1(0) ack 1100 +.01 < . 1100:1200(100) ack 1 win 1024 // This checks that 1200:1300 has not been removed from ooo queue +0 > . 1:1(0) ack 1300 Suggested-by: Jakub Kicinski Signed-off-by: Eric Dumazet Acked-by: Soheil Hassas Yeganeh Acked-by: Kuniyuki Iwashima Link: https://lore.kernel.org/r/20221101035234.3910189-1-edumazet@google.com Signed-off-by: Jakub Kicinski --- net/ipv4/tcp_input.c | 51 +++++++++++++++++++++++++++----------------- 1 file changed, 31 insertions(+), 20 deletions(-) diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index 0640453fce54..d764b5854dfc 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -4764,8 +4764,8 @@ static void tcp_ofo_queue(struct sock *sk) } } -static bool tcp_prune_ofo_queue(struct sock *sk); -static int tcp_prune_queue(struct sock *sk); +static bool tcp_prune_ofo_queue(struct sock *sk, const struct sk_buff *in_skb); +static int tcp_prune_queue(struct sock *sk, const struct sk_buff *in_skb); static int tcp_try_rmem_schedule(struct sock *sk, struct sk_buff *skb, unsigned int size) @@ -4773,11 +4773,11 @@ static int tcp_try_rmem_schedule(struct sock *sk, struct sk_buff *skb, if (atomic_read(&sk->sk_rmem_alloc) > sk->sk_rcvbuf || !sk_rmem_schedule(sk, skb, size)) { - if (tcp_prune_queue(sk) < 0) + if (tcp_prune_queue(sk, skb) < 0) return -1; while (!sk_rmem_schedule(sk, skb, size)) { - if (!tcp_prune_ofo_queue(sk)) + if (!tcp_prune_ofo_queue(sk, skb)) return -1; } } @@ -5329,6 +5329,8 @@ new_range: * Clean the out-of-order queue to make room. * We drop high sequences packets to : * 1) Let a chance for holes to be filled. + * This means we do not drop packets from ooo queue if their sequence + * is before incoming packet sequence. * 2) not add too big latencies if thousands of packets sit there. * (But if application shrinks SO_RCVBUF, we could still end up * freeing whole queue here) @@ -5336,24 +5338,31 @@ new_range: * * Return true if queue has shrunk. */ -static bool tcp_prune_ofo_queue(struct sock *sk) +static bool tcp_prune_ofo_queue(struct sock *sk, const struct sk_buff *in_skb) { struct tcp_sock *tp = tcp_sk(sk); struct rb_node *node, *prev; + bool pruned = false; int goal; if (RB_EMPTY_ROOT(&tp->out_of_order_queue)) return false; - NET_INC_STATS(sock_net(sk), LINUX_MIB_OFOPRUNED); goal = sk->sk_rcvbuf >> 3; node = &tp->ooo_last_skb->rbnode; + do { + struct sk_buff *skb = rb_to_skb(node); + + /* If incoming skb would land last in ofo queue, stop pruning. */ + if (after(TCP_SKB_CB(in_skb)->seq, TCP_SKB_CB(skb)->seq)) + break; + pruned = true; prev = rb_prev(node); rb_erase(node, &tp->out_of_order_queue); - goal -= rb_to_skb(node)->truesize; - tcp_drop_reason(sk, rb_to_skb(node), - SKB_DROP_REASON_TCP_OFO_QUEUE_PRUNE); + goal -= skb->truesize; + tcp_drop_reason(sk, skb, SKB_DROP_REASON_TCP_OFO_QUEUE_PRUNE); + tp->ooo_last_skb = rb_to_skb(prev); if (!prev || goal <= 0) { if (atomic_read(&sk->sk_rmem_alloc) <= sk->sk_rcvbuf && !tcp_under_memory_pressure(sk)) @@ -5362,16 +5371,18 @@ static bool tcp_prune_ofo_queue(struct sock *sk) } node = prev; } while (node); - tp->ooo_last_skb = rb_to_skb(prev); - /* Reset SACK state. A conforming SACK implementation will - * do the same at a timeout based retransmit. When a connection - * is in a sad state like this, we care only about integrity - * of the connection not performance. - */ - if (tp->rx_opt.sack_ok) - tcp_sack_reset(&tp->rx_opt); - return true; + if (pruned) { + NET_INC_STATS(sock_net(sk), LINUX_MIB_OFOPRUNED); + /* Reset SACK state. A conforming SACK implementation will + * do the same at a timeout based retransmit. When a connection + * is in a sad state like this, we care only about integrity + * of the connection not performance. + */ + if (tp->rx_opt.sack_ok) + tcp_sack_reset(&tp->rx_opt); + } + return pruned; } /* Reduce allocated memory if we can, trying to get @@ -5381,7 +5392,7 @@ static bool tcp_prune_ofo_queue(struct sock *sk) * until the socket owning process reads some of the data * to stabilize the situation. */ -static int tcp_prune_queue(struct sock *sk) +static int tcp_prune_queue(struct sock *sk, const struct sk_buff *in_skb) { struct tcp_sock *tp = tcp_sk(sk); @@ -5408,7 +5419,7 @@ static int tcp_prune_queue(struct sock *sk) /* Collapsing did not help, destructive actions follow. * This must not ever occur. */ - tcp_prune_ofo_queue(sk); + tcp_prune_ofo_queue(sk, in_skb); if (atomic_read(&sk->sk_rmem_alloc) <= sk->sk_rcvbuf) return 0; From fece7a8c65d1476b901b969a07b2979e1b459e66 Mon Sep 17 00:00:00 2001 From: M Chetan Kumar Date: Fri, 28 Oct 2022 21:04:50 +0530 Subject: [PATCH 0527/2002] net: wwan: t7xx: use union to group port type specific data Use union inside t7xx_port to group port type specific data members. Signed-off-by: M Chetan Kumar Reviewed-by: Sergey Ryazanov Signed-off-by: David S. Miller --- drivers/net/wwan/t7xx/t7xx_port.h | 6 +++++- drivers/net/wwan/t7xx/t7xx_port_wwan.c | 16 ++++++++-------- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/drivers/net/wwan/t7xx/t7xx_port.h b/drivers/net/wwan/t7xx/t7xx_port.h index dc4133eb433a..fbc6d724b7c2 100644 --- a/drivers/net/wwan/t7xx/t7xx_port.h +++ b/drivers/net/wwan/t7xx/t7xx_port.h @@ -99,7 +99,6 @@ struct t7xx_port_conf { struct t7xx_port { /* Members not initialized in definition */ const struct t7xx_port_conf *port_conf; - struct wwan_port *wwan_port; struct t7xx_pci_dev *t7xx_dev; struct device *dev; u16 seq_nums[2]; /* TX/RX sequence numbers */ @@ -122,6 +121,11 @@ struct t7xx_port { int rx_length_th; bool chan_enable; struct task_struct *thread; + union { + struct { + struct wwan_port *wwan_port; + } wwan; + }; }; struct sk_buff *t7xx_port_alloc_skb(int payload); diff --git a/drivers/net/wwan/t7xx/t7xx_port_wwan.c b/drivers/net/wwan/t7xx/t7xx_port_wwan.c index 33931bfd78fd..24bd21942403 100644 --- a/drivers/net/wwan/t7xx/t7xx_port_wwan.c +++ b/drivers/net/wwan/t7xx/t7xx_port_wwan.c @@ -109,12 +109,12 @@ static int t7xx_port_wwan_init(struct t7xx_port *port) static void t7xx_port_wwan_uninit(struct t7xx_port *port) { - if (!port->wwan_port) + if (!port->wwan.wwan_port) return; port->rx_length_th = 0; - wwan_remove_port(port->wwan_port); - port->wwan_port = NULL; + wwan_remove_port(port->wwan.wwan_port); + port->wwan.wwan_port = NULL; } static int t7xx_port_wwan_recv_skb(struct t7xx_port *port, struct sk_buff *skb) @@ -129,7 +129,7 @@ static int t7xx_port_wwan_recv_skb(struct t7xx_port *port, struct sk_buff *skb) return 0; } - wwan_port_rx(port->wwan_port, skb); + wwan_port_rx(port->wwan.wwan_port, skb); return 0; } @@ -158,10 +158,10 @@ static void t7xx_port_wwan_md_state_notify(struct t7xx_port *port, unsigned int if (state != MD_STATE_READY) return; - if (!port->wwan_port) { - port->wwan_port = wwan_create_port(port->dev, port_conf->port_type, - &wwan_ops, port); - if (IS_ERR(port->wwan_port)) + if (!port->wwan.wwan_port) { + port->wwan.wwan_port = wwan_create_port(port->dev, port_conf->port_type, + &wwan_ops, port); + if (IS_ERR(port->wwan.wwan_port)) dev_err(port->dev, "Unable to create WWWAN port %s", port_conf->name); } } From 3349e4a48acb0923fa98d2beac82a833a76116cb Mon Sep 17 00:00:00 2001 From: M Chetan Kumar Date: Fri, 28 Oct 2022 21:05:34 +0530 Subject: [PATCH 0528/2002] net: wwan: t7xx: Add port for modem logging The Modem Logging (MDL) port provides an interface to collect modem logs for debugging purposes. MDL is supported by the relay interface, and the mtk_t7xx port infrastructure. MDL allows user-space apps to control logging via mbim command and to collect logs via the relay interface, while port infrastructure facilitates communication between the driver and the modem. Signed-off-by: Moises Veleta Signed-off-by: M Chetan Kumar Signed-off-by: Devegowda Chandrashekar Acked-by: Ricardo Martinez Reviewed-by: Sergey Ryazanov Signed-off-by: David S. Miller --- drivers/net/wwan/Kconfig | 1 + drivers/net/wwan/t7xx/Makefile | 3 + drivers/net/wwan/t7xx/t7xx_hif_cldma.c | 2 + drivers/net/wwan/t7xx/t7xx_pci.h | 3 + drivers/net/wwan/t7xx/t7xx_port.h | 3 + drivers/net/wwan/t7xx/t7xx_port_proxy.c | 12 +++ drivers/net/wwan/t7xx/t7xx_port_proxy.h | 4 + drivers/net/wwan/t7xx/t7xx_port_trace.c | 116 ++++++++++++++++++++++++ 8 files changed, 144 insertions(+) create mode 100644 drivers/net/wwan/t7xx/t7xx_port_trace.c diff --git a/drivers/net/wwan/Kconfig b/drivers/net/wwan/Kconfig index 3486ffe94ac4..32149029c891 100644 --- a/drivers/net/wwan/Kconfig +++ b/drivers/net/wwan/Kconfig @@ -108,6 +108,7 @@ config IOSM config MTK_T7XX tristate "MediaTek PCIe 5G WWAN modem T7xx device" depends on PCI + select RELAY if WWAN_DEBUGFS help Enables MediaTek PCIe based 5G WWAN modem (T7xx series) device. Adapts WWAN framework and provides network interface like wwan0 diff --git a/drivers/net/wwan/t7xx/Makefile b/drivers/net/wwan/t7xx/Makefile index dc6a7d682c15..268ff9e87e5b 100644 --- a/drivers/net/wwan/t7xx/Makefile +++ b/drivers/net/wwan/t7xx/Makefile @@ -18,3 +18,6 @@ mtk_t7xx-y:= t7xx_pci.o \ t7xx_hif_dpmaif_rx.o \ t7xx_dpmaif.o \ t7xx_netdev.o + +mtk_t7xx-$(CONFIG_WWAN_DEBUGFS) += \ + t7xx_port_trace.o \ diff --git a/drivers/net/wwan/t7xx/t7xx_hif_cldma.c b/drivers/net/wwan/t7xx/t7xx_hif_cldma.c index 6ff30cb8eb16..aec3a18d44bd 100644 --- a/drivers/net/wwan/t7xx/t7xx_hif_cldma.c +++ b/drivers/net/wwan/t7xx/t7xx_hif_cldma.c @@ -1018,6 +1018,8 @@ static int t7xx_cldma_late_init(struct cldma_ctrl *md_ctrl) dev_err(md_ctrl->dev, "control TX ring init fail\n"); goto err_free_tx_ring; } + + md_ctrl->tx_ring[i].pkt_size = CLDMA_MTU; } for (j = 0; j < CLDMA_RXQ_NUM; j++) { diff --git a/drivers/net/wwan/t7xx/t7xx_pci.h b/drivers/net/wwan/t7xx/t7xx_pci.h index 50b37056ce5a..112efa534eac 100644 --- a/drivers/net/wwan/t7xx/t7xx_pci.h +++ b/drivers/net/wwan/t7xx/t7xx_pci.h @@ -78,6 +78,9 @@ struct t7xx_pci_dev { spinlock_t md_pm_lock; /* Protects PCI resource lock */ unsigned int sleep_disable_count; struct completion sleep_lock_acquire; +#ifdef CONFIG_WWAN_DEBUGFS + struct dentry *debugfs_dir; +#endif }; enum t7xx_pm_id { diff --git a/drivers/net/wwan/t7xx/t7xx_port.h b/drivers/net/wwan/t7xx/t7xx_port.h index fbc6d724b7c2..8ea9079af997 100644 --- a/drivers/net/wwan/t7xx/t7xx_port.h +++ b/drivers/net/wwan/t7xx/t7xx_port.h @@ -125,6 +125,9 @@ struct t7xx_port { struct { struct wwan_port *wwan_port; } wwan; + struct { + struct rchan *relaych; + } log; }; }; diff --git a/drivers/net/wwan/t7xx/t7xx_port_proxy.c b/drivers/net/wwan/t7xx/t7xx_port_proxy.c index d4de047ff0d4..894b1d11b2c9 100644 --- a/drivers/net/wwan/t7xx/t7xx_port_proxy.c +++ b/drivers/net/wwan/t7xx/t7xx_port_proxy.c @@ -70,6 +70,18 @@ static const struct t7xx_port_conf t7xx_md_port_conf[] = { .name = "MBIM", .port_type = WWAN_PORT_MBIM, }, { +#ifdef CONFIG_WWAN_DEBUGFS + .tx_ch = PORT_CH_MD_LOG_TX, + .rx_ch = PORT_CH_MD_LOG_RX, + .txq_index = 7, + .rxq_index = 7, + .txq_exp_index = 7, + .rxq_exp_index = 7, + .path_id = CLDMA_ID_MD, + .ops = &t7xx_trace_port_ops, + .name = "mdlog", + }, { +#endif .tx_ch = PORT_CH_CONTROL_TX, .rx_ch = PORT_CH_CONTROL_RX, .txq_index = Q_IDX_CTRL, diff --git a/drivers/net/wwan/t7xx/t7xx_port_proxy.h b/drivers/net/wwan/t7xx/t7xx_port_proxy.h index bc1ff5c6c700..81d059fbc0fb 100644 --- a/drivers/net/wwan/t7xx/t7xx_port_proxy.h +++ b/drivers/net/wwan/t7xx/t7xx_port_proxy.h @@ -87,6 +87,10 @@ struct ctrl_msg_header { extern struct port_ops wwan_sub_port_ops; extern struct port_ops ctl_port_ops; +#ifdef CONFIG_WWAN_DEBUGFS +extern struct port_ops t7xx_trace_port_ops; +#endif + void t7xx_port_proxy_reset(struct port_proxy *port_prox); void t7xx_port_proxy_uninit(struct port_proxy *port_prox); int t7xx_port_proxy_init(struct t7xx_modem *md); diff --git a/drivers/net/wwan/t7xx/t7xx_port_trace.c b/drivers/net/wwan/t7xx/t7xx_port_trace.c new file mode 100644 index 000000000000..6a3f36385865 --- /dev/null +++ b/drivers/net/wwan/t7xx/t7xx_port_trace.c @@ -0,0 +1,116 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2022 Intel Corporation. + */ + +#include +#include +#include +#include + +#include "t7xx_port.h" +#include "t7xx_port_proxy.h" +#include "t7xx_state_monitor.h" + +#define T7XX_TRC_SUB_BUFF_SIZE 131072 +#define T7XX_TRC_N_SUB_BUFF 32 + +static struct dentry *t7xx_trace_create_buf_file_handler(const char *filename, + struct dentry *parent, + umode_t mode, + struct rchan_buf *buf, + int *is_global) +{ + *is_global = 1; + return debugfs_create_file(filename, mode, parent, buf, + &relay_file_operations); +} + +static int t7xx_trace_remove_buf_file_handler(struct dentry *dentry) +{ + debugfs_remove(dentry); + return 0; +} + +static int t7xx_trace_subbuf_start_handler(struct rchan_buf *buf, void *subbuf, + void *prev_subbuf, size_t prev_padding) +{ + if (relay_buf_full(buf)) { + pr_err_ratelimited("Relay_buf full dropping traces"); + return 0; + } + + return 1; +} + +static struct rchan_callbacks relay_callbacks = { + .subbuf_start = t7xx_trace_subbuf_start_handler, + .create_buf_file = t7xx_trace_create_buf_file_handler, + .remove_buf_file = t7xx_trace_remove_buf_file_handler, +}; + +static void t7xx_trace_port_uninit(struct t7xx_port *port) +{ + struct dentry *debugfs_dir = port->t7xx_dev->debugfs_dir; + struct rchan *relaych = port->log.relaych; + + if (!relaych) + return; + + relay_close(relaych); + debugfs_remove_recursive(debugfs_dir); +} + +static int t7xx_trace_port_recv_skb(struct t7xx_port *port, struct sk_buff *skb) +{ + struct rchan *relaych = port->log.relaych; + + if (!relaych) + return -EINVAL; + + relay_write(relaych, skb->data, skb->len); + dev_kfree_skb(skb); + return 0; +} + +static void t7xx_port_trace_md_state_notify(struct t7xx_port *port, unsigned int state) +{ + struct rchan *relaych = port->log.relaych; + struct dentry *debugfs_wwan_dir; + struct dentry *debugfs_dir; + + if (state != MD_STATE_READY || relaych) + return; + + debugfs_wwan_dir = wwan_get_debugfs_dir(port->dev); + if (IS_ERR(debugfs_wwan_dir)) + return; + + debugfs_dir = debugfs_create_dir(KBUILD_MODNAME, debugfs_wwan_dir); + if (IS_ERR_OR_NULL(debugfs_dir)) { + wwan_put_debugfs_dir(debugfs_wwan_dir); + dev_err(port->dev, "Unable to create debugfs for trace"); + return; + } + + relaych = relay_open("relay_ch", debugfs_dir, T7XX_TRC_SUB_BUFF_SIZE, + T7XX_TRC_N_SUB_BUFF, &relay_callbacks, NULL); + if (!relaych) + goto err_rm_debugfs_dir; + + wwan_put_debugfs_dir(debugfs_wwan_dir); + port->log.relaych = relaych; + port->t7xx_dev->debugfs_dir = debugfs_dir; + return; + +err_rm_debugfs_dir: + debugfs_remove_recursive(debugfs_dir); + wwan_put_debugfs_dir(debugfs_wwan_dir); + dev_err(port->dev, "Unable to create trace port %s", port->port_conf->name); +} + +struct port_ops t7xx_trace_port_ops = { + .recv_skb = t7xx_trace_port_recv_skb, + .uninit = t7xx_trace_port_uninit, + .md_state_notify = t7xx_port_trace_md_state_notify, +}; From d08b0f8f46e45a274fc8c9a5bc92cb9da70d9887 Mon Sep 17 00:00:00 2001 From: Shane Parslow Date: Sat, 29 Oct 2022 02:03:56 -0700 Subject: [PATCH 0529/2002] net: wwan: iosm: add rpc interface for xmm modems Add a new iosm wwan port that connects to the modem rpc interface. This interface provides a configuration channel, and in the case of the 7360, is the only way to configure the modem (as it does not support mbim). The new interface is compatible with existing software, such as open_xdatachannel.py from the xmm7360-pci project [1]. [1] https://github.com/xmm7360/xmm7360-pci Signed-off-by: Shane Parslow Reviewed-by: Loic Poulain Signed-off-by: David S. Miller --- drivers/net/wwan/iosm/iosm_ipc_chnl_cfg.c | 2 +- drivers/net/wwan/wwan_core.c | 4 ++++ include/linux/wwan.h | 2 ++ 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/net/wwan/iosm/iosm_ipc_chnl_cfg.c b/drivers/net/wwan/iosm/iosm_ipc_chnl_cfg.c index 128c999e08bb..bcfbc6b3d617 100644 --- a/drivers/net/wwan/iosm/iosm_ipc_chnl_cfg.c +++ b/drivers/net/wwan/iosm/iosm_ipc_chnl_cfg.c @@ -39,7 +39,7 @@ static struct ipc_chnl_cfg modem_cfg[] = { /* RPC - 0 */ { IPC_MEM_CTRL_CHL_ID_1, IPC_MEM_PIPE_2, IPC_MEM_PIPE_3, IPC_MEM_MAX_TDS_RPC, IPC_MEM_MAX_TDS_RPC, - IPC_MEM_MAX_DL_RPC_BUF_SIZE, WWAN_PORT_UNKNOWN }, + IPC_MEM_MAX_DL_RPC_BUF_SIZE, WWAN_PORT_XMMRPC }, /* IAT0 */ { IPC_MEM_CTRL_CHL_ID_2, IPC_MEM_PIPE_4, IPC_MEM_PIPE_5, IPC_MEM_MAX_TDS_AT, IPC_MEM_MAX_TDS_AT, IPC_MEM_MAX_DL_AT_BUF_SIZE, diff --git a/drivers/net/wwan/wwan_core.c b/drivers/net/wwan/wwan_core.c index d72ee18476d1..966d0ccd2276 100644 --- a/drivers/net/wwan/wwan_core.c +++ b/drivers/net/wwan/wwan_core.c @@ -319,6 +319,10 @@ static const struct { .name = "FIREHOSE", .devsuf = "firehose", }, + [WWAN_PORT_XMMRPC] = { + .name = "XMMRPC", + .devsuf = "xmmrpc", + }, }; static ssize_t type_show(struct device *dev, struct device_attribute *attr, diff --git a/include/linux/wwan.h b/include/linux/wwan.h index 5ce2acf444fb..24d76500b1cc 100644 --- a/include/linux/wwan.h +++ b/include/linux/wwan.h @@ -15,6 +15,7 @@ * @WWAN_PORT_QMI: Qcom modem/MSM interface for modem control * @WWAN_PORT_QCDM: Qcom Modem diagnostic interface * @WWAN_PORT_FIREHOSE: XML based command protocol + * @WWAN_PORT_XMMRPC: Control protocol for Intel XMM modems * * @WWAN_PORT_MAX: Highest supported port types * @WWAN_PORT_UNKNOWN: Special value to indicate an unknown port type @@ -26,6 +27,7 @@ enum wwan_port_type { WWAN_PORT_QMI, WWAN_PORT_QCDM, WWAN_PORT_FIREHOSE, + WWAN_PORT_XMMRPC, /* Add new port types above this line */ From 82fd151d38d9fda714c5bb2e9e79ecd6bdc72da6 Mon Sep 17 00:00:00 2001 From: Shailend Chand Date: Sat, 29 Oct 2022 09:53:22 -0700 Subject: [PATCH 0530/2002] gve: Reduce alloc and copy costs in the GQ rx path Previously, even if just one of the many fragments of a 9k packet required a copy, we'd copy the whole packet into a freshly-allocated 9k-sized linear SKB, and this led to performance issues. By having a pool of pages to copy into, each fragment can be independently handled, leading to a reduced incidence of allocation and copy. Signed-off-by: Shailend Chand Signed-off-by: David S. Miller --- drivers/net/ethernet/google/gve/gve.h | 26 +- drivers/net/ethernet/google/gve/gve_ethtool.c | 2 + drivers/net/ethernet/google/gve/gve_rx.c | 534 ++++++++++-------- drivers/net/ethernet/google/gve/gve_rx_dqo.c | 2 +- drivers/net/ethernet/google/gve/gve_utils.c | 28 +- drivers/net/ethernet/google/gve/gve_utils.h | 2 +- 6 files changed, 333 insertions(+), 261 deletions(-) diff --git a/drivers/net/ethernet/google/gve/gve.h b/drivers/net/ethernet/google/gve/gve.h index 160735484465..5655da9cd236 100644 --- a/drivers/net/ethernet/google/gve/gve.h +++ b/drivers/net/ethernet/google/gve/gve.h @@ -60,7 +60,8 @@ struct gve_rx_slot_page_info { void *page_address; u32 page_offset; /* offset to write to in page */ int pagecnt_bias; /* expected pagecnt if only the driver has a ref */ - u8 can_flip; + u16 pad; /* adjustment for rx padding */ + u8 can_flip; /* tracks if the networking stack is using the page */ }; /* A list of pages registered with the device during setup and used by a queue @@ -149,10 +150,17 @@ struct gve_rx_ctx { /* head and tail of skb chain for the current packet or NULL if none */ struct sk_buff *skb_head; struct sk_buff *skb_tail; - u16 total_expected_size; - u8 expected_frag_cnt; - u8 curr_frag_cnt; - u8 reuse_frags; + u32 total_size; + u8 frag_cnt; + bool drop_pkt; +}; + +struct gve_rx_cnts { + u32 ok_pkt_bytes; + u16 ok_pkt_cnt; + u16 total_pkt_cnt; + u16 cont_pkt_cnt; + u16 desc_err_pkt_cnt; }; /* Contains datapath state used to represent an RX queue. */ @@ -167,6 +175,10 @@ struct gve_rx_ring { /* threshold for posting new buffs and descs */ u32 db_threshold; u16 packet_buffer_size; + + u32 qpl_copy_pool_mask; + u32 qpl_copy_pool_head; + struct gve_rx_slot_page_info *qpl_copy_pool; }; /* DQO fields. */ @@ -216,7 +228,9 @@ struct gve_rx_ring { u64 rx_desc_err_dropped_pkt; /* free-running count of packets dropped by descriptor error */ u64 rx_cont_packet_cnt; /* free-running multi-fragment packets received */ u64 rx_frag_flip_cnt; /* free-running count of rx segments where page_flip was used */ - u64 rx_frag_copy_cnt; /* free-running count of rx segments copied into skb linear portion */ + u64 rx_frag_copy_cnt; /* free-running count of rx segments copied */ + u64 rx_frag_alloc_cnt; /* free-running count of rx page allocations */ + u32 q_num; /* queue index */ u32 ntfy_id; /* notification block index */ struct gve_queue_resources *q_resources; /* head and tail pointer idx */ diff --git a/drivers/net/ethernet/google/gve/gve_ethtool.c b/drivers/net/ethernet/google/gve/gve_ethtool.c index 50b384910c83..ce574d097e28 100644 --- a/drivers/net/ethernet/google/gve/gve_ethtool.c +++ b/drivers/net/ethernet/google/gve/gve_ethtool.c @@ -45,6 +45,7 @@ static const char gve_gstrings_main_stats[][ETH_GSTRING_LEN] = { static const char gve_gstrings_rx_stats[][ETH_GSTRING_LEN] = { "rx_posted_desc[%u]", "rx_completed_desc[%u]", "rx_consumed_desc[%u]", "rx_bytes[%u]", "rx_cont_packet_cnt[%u]", "rx_frag_flip_cnt[%u]", "rx_frag_copy_cnt[%u]", + "rx_frag_alloc_cnt[%u]", "rx_dropped_pkt[%u]", "rx_copybreak_pkt[%u]", "rx_copied_pkt[%u]", "rx_queue_drop_cnt[%u]", "rx_no_buffers_posted[%u]", "rx_drops_packet_over_mru[%u]", "rx_drops_invalid_checksum[%u]", @@ -271,6 +272,7 @@ gve_get_ethtool_stats(struct net_device *netdev, data[i++] = rx->rx_cont_packet_cnt; data[i++] = rx->rx_frag_flip_cnt; data[i++] = rx->rx_frag_copy_cnt; + data[i++] = rx->rx_frag_alloc_cnt; /* rx dropped packets */ data[i++] = tmp_rx_skb_alloc_fail + tmp_rx_buf_alloc_fail + diff --git a/drivers/net/ethernet/google/gve/gve_rx.c b/drivers/net/ethernet/google/gve/gve_rx.c index 021bbf308d68..e2f4494c65fb 100644 --- a/drivers/net/ethernet/google/gve/gve_rx.c +++ b/drivers/net/ethernet/google/gve/gve_rx.c @@ -35,6 +35,12 @@ static void gve_rx_unfill_pages(struct gve_priv *priv, struct gve_rx_ring *rx) rx->data.page_info[i].pagecnt_bias - 1); gve_unassign_qpl(priv, rx->data.qpl->id); rx->data.qpl = NULL; + + for (i = 0; i < rx->qpl_copy_pool_mask + 1; i++) { + page_ref_sub(rx->qpl_copy_pool[i].page, + rx->qpl_copy_pool[i].pagecnt_bias - 1); + put_page(rx->qpl_copy_pool[i].page); + } } kvfree(rx->data.page_info); rx->data.page_info = NULL; @@ -63,6 +69,10 @@ static void gve_rx_free_ring(struct gve_priv *priv, int idx) dma_free_coherent(dev, bytes, rx->data.data_ring, rx->data.data_bus); rx->data.data_ring = NULL; + + kvfree(rx->qpl_copy_pool); + rx->qpl_copy_pool = NULL; + netif_dbg(priv, drv, priv->dev, "freed rx ring %d\n", idx); } @@ -101,6 +111,7 @@ static int gve_prefill_rx_pages(struct gve_rx_ring *rx) u32 slots; int err; int i; + int j; /* Allocate one page per Rx queue slot. Each page is split into two * packet buffers, when possible we "page flip" between the two. @@ -135,7 +146,31 @@ static int gve_prefill_rx_pages(struct gve_rx_ring *rx) goto alloc_err; } + if (!rx->data.raw_addressing) { + for (j = 0; j < rx->qpl_copy_pool_mask + 1; j++) { + struct page *page = alloc_page(GFP_KERNEL); + + if (!page) + goto alloc_err_qpl; + + rx->qpl_copy_pool[j].page = page; + rx->qpl_copy_pool[j].page_offset = 0; + rx->qpl_copy_pool[j].page_address = page_address(page); + + /* The page already has 1 ref. */ + page_ref_add(page, INT_MAX - 1); + rx->qpl_copy_pool[j].pagecnt_bias = INT_MAX; + } + } + return slots; + +alloc_err_qpl: + while (j--) { + page_ref_sub(rx->qpl_copy_pool[j].page, + rx->qpl_copy_pool[j].pagecnt_bias - 1); + put_page(rx->qpl_copy_pool[j].page); + } alloc_err: while (i--) gve_rx_free_buffer(&priv->pdev->dev, @@ -146,12 +181,11 @@ alloc_err: static void gve_rx_ctx_clear(struct gve_rx_ctx *ctx) { - ctx->curr_frag_cnt = 0; - ctx->total_expected_size = 0; - ctx->expected_frag_cnt = 0; ctx->skb_head = NULL; ctx->skb_tail = NULL; - ctx->reuse_frags = false; + ctx->total_size = 0; + ctx->frag_cnt = 0; + ctx->drop_pkt = false; } static int gve_rx_alloc_ring(struct gve_priv *priv, int idx) @@ -181,10 +215,22 @@ static int gve_rx_alloc_ring(struct gve_priv *priv, int idx) GFP_KERNEL); if (!rx->data.data_ring) return -ENOMEM; + + rx->qpl_copy_pool_mask = min_t(u32, U32_MAX, slots * 2) - 1; + rx->qpl_copy_pool_head = 0; + rx->qpl_copy_pool = kvcalloc(rx->qpl_copy_pool_mask + 1, + sizeof(rx->qpl_copy_pool[0]), + GFP_KERNEL); + + if (!rx->qpl_copy_pool) { + err = -ENOMEM; + goto abort_with_slots; + } + filled_pages = gve_prefill_rx_pages(rx); if (filled_pages < 0) { err = -ENOMEM; - goto abort_with_slots; + goto abort_with_copy_pool; } rx->fill_cnt = filled_pages; /* Ensure data ring slots (packet buffers) are visible. */ @@ -236,6 +282,9 @@ abort_with_q_resources: rx->q_resources = NULL; abort_filled: gve_rx_unfill_pages(priv, rx); +abort_with_copy_pool: + kvfree(rx->qpl_copy_pool); + rx->qpl_copy_pool = NULL; abort_with_slots: bytes = sizeof(*rx->data.data_ring) * slots; dma_free_coherent(hdev, bytes, rx->data.data_ring, rx->data.data_bus); @@ -292,30 +341,47 @@ static enum pkt_hash_types gve_rss_type(__be16 pkt_flags) return PKT_HASH_TYPE_L2; } -static u16 gve_rx_ctx_padding(struct gve_rx_ctx *ctx) -{ - return (ctx->curr_frag_cnt == 0) ? GVE_RX_PAD : 0; -} - static struct sk_buff *gve_rx_add_frags(struct napi_struct *napi, struct gve_rx_slot_page_info *page_info, u16 packet_buffer_size, u16 len, struct gve_rx_ctx *ctx) { - u32 offset = page_info->page_offset + gve_rx_ctx_padding(ctx); - struct sk_buff *skb; + u32 offset = page_info->page_offset + page_info->pad; + struct sk_buff *skb = ctx->skb_tail; + int num_frags = 0; - if (!ctx->skb_head) - ctx->skb_head = napi_get_frags(napi); + if (!skb) { + skb = napi_get_frags(napi); + if (unlikely(!skb)) + return NULL; - if (unlikely(!ctx->skb_head)) - return NULL; + ctx->skb_head = skb; + ctx->skb_tail = skb; + } else { + num_frags = skb_shinfo(ctx->skb_tail)->nr_frags; + if (num_frags == MAX_SKB_FRAGS) { + skb = napi_alloc_skb(napi, 0); + if (!skb) + return NULL; - skb = ctx->skb_head; - skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags, page_info->page, + // We will never chain more than two SKBs: 2 * 16 * 2k > 64k + // which is why we do not need to chain by using skb->next + skb_shinfo(ctx->skb_tail)->frag_list = skb; + + ctx->skb_tail = skb; + num_frags = 0; + } + } + + if (skb != ctx->skb_head) { + ctx->skb_head->len += len; + ctx->skb_head->data_len += len; + ctx->skb_head->truesize += packet_buffer_size; + } + skb_add_rx_frag(skb, num_frags, page_info->page, offset, len, packet_buffer_size); - return skb; + return ctx->skb_head; } static void gve_rx_flip_buff(struct gve_rx_slot_page_info *page_info, __be64 *slot_addr) @@ -363,6 +429,92 @@ gve_rx_raw_addressing(struct device *dev, struct net_device *netdev, return skb; } +static struct sk_buff *gve_rx_copy_to_pool(struct gve_rx_ring *rx, + struct gve_rx_slot_page_info *page_info, + u16 len, struct napi_struct *napi) +{ + u32 pool_idx = rx->qpl_copy_pool_head & rx->qpl_copy_pool_mask; + void *src = page_info->page_address + page_info->page_offset; + struct gve_rx_slot_page_info *copy_page_info; + struct gve_rx_ctx *ctx = &rx->ctx; + bool alloc_page = false; + struct sk_buff *skb; + void *dst; + + copy_page_info = &rx->qpl_copy_pool[pool_idx]; + if (!copy_page_info->can_flip) { + int recycle = gve_rx_can_recycle_buffer(copy_page_info); + + if (unlikely(recycle < 0)) { + gve_schedule_reset(rx->gve); + return NULL; + } + alloc_page = !recycle; + } + + if (alloc_page) { + struct gve_rx_slot_page_info alloc_page_info; + struct page *page; + + /* The least recently used page turned out to be + * still in use by the kernel. Ignoring it and moving + * on alleviates head-of-line blocking. + */ + rx->qpl_copy_pool_head++; + + page = alloc_page(GFP_ATOMIC); + if (!page) + return NULL; + + alloc_page_info.page = page; + alloc_page_info.page_offset = 0; + alloc_page_info.page_address = page_address(page); + alloc_page_info.pad = page_info->pad; + + memcpy(alloc_page_info.page_address, src, page_info->pad + len); + skb = gve_rx_add_frags(napi, &alloc_page_info, + rx->packet_buffer_size, + len, ctx); + + u64_stats_update_begin(&rx->statss); + rx->rx_frag_copy_cnt++; + rx->rx_frag_alloc_cnt++; + u64_stats_update_end(&rx->statss); + + return skb; + } + + dst = copy_page_info->page_address + copy_page_info->page_offset; + memcpy(dst, src, page_info->pad + len); + copy_page_info->pad = page_info->pad; + + skb = gve_rx_add_frags(napi, copy_page_info, + rx->packet_buffer_size, len, ctx); + if (unlikely(!skb)) + return NULL; + + gve_dec_pagecnt_bias(copy_page_info); + copy_page_info->page_offset += rx->packet_buffer_size; + copy_page_info->page_offset &= (PAGE_SIZE - 1); + + if (copy_page_info->can_flip) { + /* We have used both halves of this copy page, it + * is time for it to go to the back of the queue. + */ + copy_page_info->can_flip = false; + rx->qpl_copy_pool_head++; + prefetch(rx->qpl_copy_pool[rx->qpl_copy_pool_head & rx->qpl_copy_pool_mask].page); + } else { + copy_page_info->can_flip = true; + } + + u64_stats_update_begin(&rx->statss); + rx->rx_frag_copy_cnt++; + u64_stats_update_end(&rx->statss); + + return skb; +} + static struct sk_buff * gve_rx_qpl(struct device *dev, struct net_device *netdev, struct gve_rx_ring *rx, struct gve_rx_slot_page_info *page_info, @@ -377,7 +529,7 @@ gve_rx_qpl(struct device *dev, struct net_device *netdev, * choice is to copy the data out of it so that we can return it to the * device. */ - if (ctx->reuse_frags) { + if (page_info->can_flip) { skb = gve_rx_add_frags(napi, page_info, rx->packet_buffer_size, len, ctx); /* No point in recycling if we didn't get the skb */ if (skb) { @@ -386,116 +538,23 @@ gve_rx_qpl(struct device *dev, struct net_device *netdev, gve_rx_flip_buff(page_info, &data_slot->qpl_offset); } } else { - const u16 padding = gve_rx_ctx_padding(ctx); - - skb = gve_rx_copy(netdev, napi, page_info, len, padding, ctx); - if (skb) { - u64_stats_update_begin(&rx->statss); - rx->rx_frag_copy_cnt++; - u64_stats_update_end(&rx->statss); - } + skb = gve_rx_copy_to_pool(rx, page_info, len, napi); } return skb; } -#define GVE_PKTCONT_BIT_IS_SET(x) (GVE_RXF_PKT_CONT & (x)) -static u16 gve_rx_get_fragment_size(struct gve_rx_ctx *ctx, struct gve_rx_desc *desc) -{ - return be16_to_cpu(desc->len) - gve_rx_ctx_padding(ctx); -} - -static bool gve_rx_ctx_init(struct gve_rx_ctx *ctx, struct gve_rx_ring *rx) -{ - bool qpl_mode = !rx->data.raw_addressing, packet_size_error = false; - bool buffer_error = false, desc_error = false, seqno_error = false; - struct gve_rx_slot_page_info *page_info; - struct gve_priv *priv = rx->gve; - u32 idx = rx->cnt & rx->mask; - bool reuse_frags, can_flip; - struct gve_rx_desc *desc; - u16 packet_size = 0; - u16 n_frags = 0; - int recycle; - - /** In QPL mode, we only flip buffers when all buffers containing the packet - * can be flipped. RDA can_flip decisions will be made later, per frag. - */ - can_flip = qpl_mode; - reuse_frags = can_flip; - do { - u16 frag_size; - - n_frags++; - desc = &rx->desc.desc_ring[idx]; - desc_error = unlikely(desc->flags_seq & GVE_RXF_ERR) || desc_error; - if (GVE_SEQNO(desc->flags_seq) != rx->desc.seqno) { - seqno_error = true; - netdev_warn(priv->dev, - "RX seqno error: want=%d, got=%d, dropping packet and scheduling reset.", - rx->desc.seqno, GVE_SEQNO(desc->flags_seq)); - } - frag_size = be16_to_cpu(desc->len); - packet_size += frag_size; - if (frag_size > rx->packet_buffer_size) { - packet_size_error = true; - netdev_warn(priv->dev, - "RX fragment error: packet_buffer_size=%d, frag_size=%d, dropping packet.", - rx->packet_buffer_size, be16_to_cpu(desc->len)); - } - page_info = &rx->data.page_info[idx]; - if (can_flip) { - recycle = gve_rx_can_recycle_buffer(page_info); - reuse_frags = reuse_frags && recycle > 0; - buffer_error = buffer_error || unlikely(recycle < 0); - } - idx = (idx + 1) & rx->mask; - rx->desc.seqno = gve_next_seqno(rx->desc.seqno); - } while (GVE_PKTCONT_BIT_IS_SET(desc->flags_seq)); - - prefetch(rx->desc.desc_ring + idx); - - ctx->curr_frag_cnt = 0; - ctx->total_expected_size = packet_size - GVE_RX_PAD; - ctx->expected_frag_cnt = n_frags; - ctx->skb_head = NULL; - ctx->reuse_frags = reuse_frags; - - if (ctx->expected_frag_cnt > 1) { - u64_stats_update_begin(&rx->statss); - rx->rx_cont_packet_cnt++; - u64_stats_update_end(&rx->statss); - } - if (ctx->total_expected_size > priv->rx_copybreak && !ctx->reuse_frags && qpl_mode) { - u64_stats_update_begin(&rx->statss); - rx->rx_copied_pkt++; - u64_stats_update_end(&rx->statss); - } - - if (unlikely(buffer_error || seqno_error || packet_size_error)) { - gve_schedule_reset(priv); - return false; - } - - if (unlikely(desc_error)) { - u64_stats_update_begin(&rx->statss); - rx->rx_desc_err_dropped_pkt++; - u64_stats_update_end(&rx->statss); - return false; - } - return true; -} - static struct sk_buff *gve_rx_skb(struct gve_priv *priv, struct gve_rx_ring *rx, struct gve_rx_slot_page_info *page_info, struct napi_struct *napi, - u16 len, union gve_rx_data_slot *data_slot) + u16 len, union gve_rx_data_slot *data_slot, + bool is_only_frag) { struct net_device *netdev = priv->dev; struct gve_rx_ctx *ctx = &rx->ctx; struct sk_buff *skb = NULL; - if (len <= priv->rx_copybreak && ctx->expected_frag_cnt == 1) { + if (len <= priv->rx_copybreak && is_only_frag) { /* Just copy small packets */ - skb = gve_rx_copy(netdev, napi, page_info, len, GVE_RX_PAD, ctx); + skb = gve_rx_copy(netdev, napi, page_info, len, GVE_RX_PAD); if (skb) { u64_stats_update_begin(&rx->statss); rx->rx_copied_pkt++; @@ -504,29 +563,25 @@ static struct sk_buff *gve_rx_skb(struct gve_priv *priv, struct gve_rx_ring *rx, u64_stats_update_end(&rx->statss); } } else { - if (rx->data.raw_addressing) { - int recycle = gve_rx_can_recycle_buffer(page_info); + int recycle = gve_rx_can_recycle_buffer(page_info); - if (unlikely(recycle < 0)) { - gve_schedule_reset(priv); - return NULL; - } - page_info->can_flip = recycle; - if (page_info->can_flip) { - u64_stats_update_begin(&rx->statss); - rx->rx_frag_flip_cnt++; - u64_stats_update_end(&rx->statss); - } + if (unlikely(recycle < 0)) { + gve_schedule_reset(priv); + return NULL; + } + page_info->can_flip = recycle; + if (page_info->can_flip) { + u64_stats_update_begin(&rx->statss); + rx->rx_frag_flip_cnt++; + u64_stats_update_end(&rx->statss); + } + + if (rx->data.raw_addressing) { skb = gve_rx_raw_addressing(&priv->pdev->dev, netdev, page_info, len, napi, data_slot, rx->packet_buffer_size, ctx); } else { - if (ctx->reuse_frags) { - u64_stats_update_begin(&rx->statss); - rx->rx_frag_flip_cnt++; - u64_stats_update_end(&rx->statss); - } skb = gve_rx_qpl(&priv->pdev->dev, netdev, rx, page_info, len, napi, data_slot); } @@ -534,101 +589,113 @@ static struct sk_buff *gve_rx_skb(struct gve_priv *priv, struct gve_rx_ring *rx, return skb; } -static bool gve_rx(struct gve_rx_ring *rx, netdev_features_t feat, - u64 *packet_size_bytes, u32 *work_done) +#define GVE_PKTCONT_BIT_IS_SET(x) (GVE_RXF_PKT_CONT & (x)) +static void gve_rx(struct gve_rx_ring *rx, netdev_features_t feat, + struct gve_rx_desc *desc, u32 idx, + struct gve_rx_cnts *cnts) { + bool is_last_frag = !GVE_PKTCONT_BIT_IS_SET(desc->flags_seq); struct gve_rx_slot_page_info *page_info; + u16 frag_size = be16_to_cpu(desc->len); struct gve_rx_ctx *ctx = &rx->ctx; union gve_rx_data_slot *data_slot; struct gve_priv *priv = rx->gve; - struct gve_rx_desc *first_desc; struct sk_buff *skb = NULL; - struct gve_rx_desc *desc; - struct napi_struct *napi; dma_addr_t page_bus; - u32 work_cnt = 0; void *va; - u32 idx; - u16 len; - idx = rx->cnt & rx->mask; - first_desc = &rx->desc.desc_ring[idx]; - desc = first_desc; - napi = &priv->ntfy_blocks[rx->ntfy_id].napi; + struct napi_struct *napi = &priv->ntfy_blocks[rx->ntfy_id].napi; + bool is_first_frag = ctx->frag_cnt == 0; - if (unlikely(!gve_rx_ctx_init(ctx, rx))) - goto skb_alloc_fail; + bool is_only_frag = is_first_frag && is_last_frag; - while (ctx->curr_frag_cnt < ctx->expected_frag_cnt) { - /* Prefetch two packet buffers ahead, we will need it soon. */ - page_info = &rx->data.page_info[(idx + 2) & rx->mask]; - va = page_info->page_address + page_info->page_offset; + if (unlikely(ctx->drop_pkt)) + goto finish_frag; - prefetch(page_info->page); /* Kernel page struct. */ - prefetch(va); /* Packet header. */ - prefetch(va + 64); /* Next cacheline too. */ + if (desc->flags_seq & GVE_RXF_ERR) { + ctx->drop_pkt = true; + cnts->desc_err_pkt_cnt++; + napi_free_frags(napi); + goto finish_frag; + } - len = gve_rx_get_fragment_size(ctx, desc); + if (unlikely(frag_size > rx->packet_buffer_size)) { + netdev_warn(priv->dev, "Unexpected frag size %d, can't exceed %d, scheduling reset", + frag_size, rx->packet_buffer_size); + ctx->drop_pkt = true; + napi_free_frags(napi); + gve_schedule_reset(rx->gve); + goto finish_frag; + } - page_info = &rx->data.page_info[idx]; - data_slot = &rx->data.data_ring[idx]; - page_bus = rx->data.raw_addressing ? - be64_to_cpu(data_slot->addr) - page_info->page_offset : - rx->data.qpl->page_buses[idx]; - dma_sync_single_for_cpu(&priv->pdev->dev, page_bus, PAGE_SIZE, DMA_FROM_DEVICE); + /* Prefetch two packet buffers ahead, we will need it soon. */ + page_info = &rx->data.page_info[(idx + 2) & rx->mask]; + va = page_info->page_address + page_info->page_offset; + prefetch(page_info->page); /* Kernel page struct. */ + prefetch(va); /* Packet header. */ + prefetch(va + 64); /* Next cacheline too. */ - skb = gve_rx_skb(priv, rx, page_info, napi, len, data_slot); - if (!skb) { - u64_stats_update_begin(&rx->statss); - rx->rx_skb_alloc_fail++; - u64_stats_update_end(&rx->statss); - goto skb_alloc_fail; + page_info = &rx->data.page_info[idx]; + data_slot = &rx->data.data_ring[idx]; + page_bus = (rx->data.raw_addressing) ? + be64_to_cpu(data_slot->addr) - page_info->page_offset : + rx->data.qpl->page_buses[idx]; + dma_sync_single_for_cpu(&priv->pdev->dev, page_bus, + PAGE_SIZE, DMA_FROM_DEVICE); + page_info->pad = is_first_frag ? GVE_RX_PAD : 0; + frag_size -= page_info->pad; + + skb = gve_rx_skb(priv, rx, page_info, napi, frag_size, + data_slot, is_only_frag); + if (!skb) { + u64_stats_update_begin(&rx->statss); + rx->rx_skb_alloc_fail++; + u64_stats_update_end(&rx->statss); + + napi_free_frags(napi); + ctx->drop_pkt = true; + goto finish_frag; + } + ctx->total_size += frag_size; + + if (is_first_frag) { + if (likely(feat & NETIF_F_RXCSUM)) { + /* NIC passes up the partial sum */ + if (desc->csum) + skb->ip_summed = CHECKSUM_COMPLETE; + else + skb->ip_summed = CHECKSUM_NONE; + skb->csum = csum_unfold(desc->csum); } - ctx->curr_frag_cnt++; - rx->cnt++; - idx = rx->cnt & rx->mask; - work_cnt++; - desc = &rx->desc.desc_ring[idx]; + /* parse flags & pass relevant info up */ + if (likely(feat & NETIF_F_RXHASH) && + gve_needs_rss(desc->flags_seq)) + skb_set_hash(skb, be32_to_cpu(desc->rss_hash), + gve_rss_type(desc->flags_seq)); } - if (likely(feat & NETIF_F_RXCSUM)) { - /* NIC passes up the partial sum */ - if (first_desc->csum) - skb->ip_summed = CHECKSUM_COMPLETE; + if (is_last_frag) { + skb_record_rx_queue(skb, rx->q_num); + if (skb_is_nonlinear(skb)) + napi_gro_frags(napi); else - skb->ip_summed = CHECKSUM_NONE; - skb->csum = csum_unfold(first_desc->csum); + napi_gro_receive(napi, skb); + goto finish_ok_pkt; } - /* parse flags & pass relevant info up */ - if (likely(feat & NETIF_F_RXHASH) && - gve_needs_rss(first_desc->flags_seq)) - skb_set_hash(skb, be32_to_cpu(first_desc->rss_hash), - gve_rss_type(first_desc->flags_seq)); + goto finish_frag; - *packet_size_bytes = skb->len + (skb->protocol ? ETH_HLEN : 0); - *work_done = work_cnt; - skb_record_rx_queue(skb, rx->q_num); - if (skb_is_nonlinear(skb)) - napi_gro_frags(napi); - else - napi_gro_receive(napi, skb); - - gve_rx_ctx_clear(ctx); - return true; - -skb_alloc_fail: - if (napi->skb) - napi_free_frags(napi); - *packet_size_bytes = 0; - *work_done = ctx->expected_frag_cnt; - while (ctx->curr_frag_cnt < ctx->expected_frag_cnt) { - rx->cnt++; - ctx->curr_frag_cnt++; +finish_ok_pkt: + cnts->ok_pkt_bytes += ctx->total_size; + cnts->ok_pkt_cnt++; +finish_frag: + ctx->frag_cnt++; + if (is_last_frag) { + cnts->total_pkt_cnt++; + cnts->cont_pkt_cnt += (ctx->frag_cnt > 1); + gve_rx_ctx_clear(ctx); } - gve_rx_ctx_clear(ctx); - return false; } bool gve_rx_work_pending(struct gve_rx_ring *rx) @@ -704,36 +771,39 @@ static bool gve_rx_refill_buffers(struct gve_priv *priv, struct gve_rx_ring *rx) static int gve_clean_rx_done(struct gve_rx_ring *rx, int budget, netdev_features_t feat) { - u32 work_done = 0, total_packet_cnt = 0, ok_packet_cnt = 0; + struct gve_rx_ctx *ctx = &rx->ctx; struct gve_priv *priv = rx->gve; + struct gve_rx_cnts cnts = {0}; + struct gve_rx_desc *next_desc; u32 idx = rx->cnt & rx->mask; - struct gve_rx_desc *desc; - u64 bytes = 0; + u32 work_done = 0; - desc = &rx->desc.desc_ring[idx]; + struct gve_rx_desc *desc = &rx->desc.desc_ring[idx]; + + // Exceed budget only if (and till) the inflight packet is consumed. while ((GVE_SEQNO(desc->flags_seq) == rx->desc.seqno) && - work_done < budget) { - u64 packet_size_bytes = 0; - u32 work_cnt = 0; - bool dropped; + (work_done < budget || ctx->frag_cnt)) { + next_desc = &rx->desc.desc_ring[(idx + 1) & rx->mask]; + prefetch(next_desc); - netif_info(priv, rx_status, priv->dev, - "[%d] idx=%d desc=%p desc->flags_seq=0x%x\n", - rx->q_num, idx, desc, desc->flags_seq); - netif_info(priv, rx_status, priv->dev, - "[%d] seqno=%d rx->desc.seqno=%d\n", - rx->q_num, GVE_SEQNO(desc->flags_seq), - rx->desc.seqno); + gve_rx(rx, feat, desc, idx, &cnts); - dropped = !gve_rx(rx, feat, &packet_size_bytes, &work_cnt); - if (!dropped) { - bytes += packet_size_bytes; - ok_packet_cnt++; - } - total_packet_cnt++; + rx->cnt++; idx = rx->cnt & rx->mask; desc = &rx->desc.desc_ring[idx]; - work_done += work_cnt; + rx->desc.seqno = gve_next_seqno(rx->desc.seqno); + work_done++; + } + + // The device will only send whole packets. + if (unlikely(ctx->frag_cnt)) { + struct napi_struct *napi = &priv->ntfy_blocks[rx->ntfy_id].napi; + + napi_free_frags(napi); + gve_rx_ctx_clear(&rx->ctx); + netdev_warn(priv->dev, "Unexpected seq number %d with incomplete packet, expected %d, scheduling reset", + GVE_SEQNO(desc->flags_seq), rx->desc.seqno); + gve_schedule_reset(rx->gve); } if (!work_done && rx->fill_cnt - rx->cnt > rx->db_threshold) @@ -741,8 +811,10 @@ static int gve_clean_rx_done(struct gve_rx_ring *rx, int budget, if (work_done) { u64_stats_update_begin(&rx->statss); - rx->rpackets += ok_packet_cnt; - rx->rbytes += bytes; + rx->rpackets += cnts.ok_pkt_cnt; + rx->rbytes += cnts.ok_pkt_bytes; + rx->rx_cont_packet_cnt += cnts.cont_pkt_cnt; + rx->rx_desc_err_dropped_pkt += cnts.desc_err_pkt_cnt; u64_stats_update_end(&rx->statss); } @@ -767,7 +839,7 @@ static int gve_clean_rx_done(struct gve_rx_ring *rx, int budget, } gve_rx_write_doorbell(priv, rx); - return total_packet_cnt; + return cnts.total_pkt_cnt; } int gve_rx_poll(struct gve_notify_block *block, int budget) diff --git a/drivers/net/ethernet/google/gve/gve_rx_dqo.c b/drivers/net/ethernet/google/gve/gve_rx_dqo.c index 2e6461b0ea8b..630f42a3037b 100644 --- a/drivers/net/ethernet/google/gve/gve_rx_dqo.c +++ b/drivers/net/ethernet/google/gve/gve_rx_dqo.c @@ -568,7 +568,7 @@ static int gve_rx_dqo(struct napi_struct *napi, struct gve_rx_ring *rx, if (eop && buf_len <= priv->rx_copybreak) { rx->ctx.skb_head = gve_rx_copy(priv->dev, napi, - &buf_state->page_info, buf_len, 0, NULL); + &buf_state->page_info, buf_len, 0); if (unlikely(!rx->ctx.skb_head)) goto error; rx->ctx.skb_tail = rx->ctx.skb_head; diff --git a/drivers/net/ethernet/google/gve/gve_utils.c b/drivers/net/ethernet/google/gve/gve_utils.c index d57508bc4307..6ba46adaaee3 100644 --- a/drivers/net/ethernet/google/gve/gve_utils.c +++ b/drivers/net/ethernet/google/gve/gve_utils.c @@ -50,34 +50,18 @@ void gve_rx_add_to_block(struct gve_priv *priv, int queue_idx) struct sk_buff *gve_rx_copy(struct net_device *dev, struct napi_struct *napi, struct gve_rx_slot_page_info *page_info, u16 len, - u16 padding, struct gve_rx_ctx *ctx) + u16 padding) { void *va = page_info->page_address + padding + page_info->page_offset; - int skb_linear_offset = 0; - bool set_protocol = false; struct sk_buff *skb; - if (ctx) { - if (!ctx->skb_head) - ctx->skb_head = napi_alloc_skb(napi, ctx->total_expected_size); + skb = napi_alloc_skb(napi, len); + if (unlikely(!skb)) + return NULL; - if (unlikely(!ctx->skb_head)) - return NULL; - skb = ctx->skb_head; - skb_linear_offset = skb->len; - set_protocol = ctx->curr_frag_cnt == ctx->expected_frag_cnt - 1; - } else { - skb = napi_alloc_skb(napi, len); - - if (unlikely(!skb)) - return NULL; - set_protocol = true; - } __skb_put(skb, len); - skb_copy_to_linear_data_offset(skb, skb_linear_offset, va, len); - - if (set_protocol) - skb->protocol = eth_type_trans(skb, dev); + skb_copy_to_linear_data_offset(skb, 0, va, len); + skb->protocol = eth_type_trans(skb, dev); return skb; } diff --git a/drivers/net/ethernet/google/gve/gve_utils.h b/drivers/net/ethernet/google/gve/gve_utils.h index 6d98e69fd3b8..79595940b351 100644 --- a/drivers/net/ethernet/google/gve/gve_utils.h +++ b/drivers/net/ethernet/google/gve/gve_utils.h @@ -19,7 +19,7 @@ void gve_rx_add_to_block(struct gve_priv *priv, int queue_idx); struct sk_buff *gve_rx_copy(struct net_device *dev, struct napi_struct *napi, struct gve_rx_slot_page_info *page_info, u16 len, - u16 pad, struct gve_rx_ctx *ctx); + u16 pad); /* Decrement pagecnt_bias. Set it back to INT_MAX if it reached zero. */ void gve_dec_pagecnt_bias(struct gve_rx_slot_page_info *page_info); From 7e8cdc97148c6ba66671e88ad9f7d434f4df3438 Mon Sep 17 00:00:00 2001 From: Dmitry Vyukov Date: Sun, 30 Oct 2022 16:03:37 +0100 Subject: [PATCH 0531/2002] nfc: Add KCOV annotations Add remote KCOV annotations for NFC processing that is done in background threads. This enables efficient coverage-guided fuzzing of the NFC subsystem. The intention is to add annotations to background threads that process skb's that were allocated in syscall context (thus have a KCOV handle associated with the current fuzz test). This includes nci_recv_frame() that is called by the virtual nci driver in the syscall context. Signed-off-by: Dmitry Vyukov Cc: Bongsu Jeon Cc: Krzysztof Kozlowski Cc: netdev@vger.kernel.org Signed-off-by: David S. Miller --- net/nfc/nci/core.c | 8 +++++++- net/nfc/nci/hci.c | 4 +++- net/nfc/rawsock.c | 3 +++ 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/net/nfc/nci/core.c b/net/nfc/nci/core.c index 6a193cce2a75..dbe5258e13ff 100644 --- a/net/nfc/nci/core.c +++ b/net/nfc/nci/core.c @@ -24,6 +24,7 @@ #include #include #include +#include #include "../nfc.h" #include @@ -1472,6 +1473,7 @@ static void nci_tx_work(struct work_struct *work) skb = skb_dequeue(&ndev->tx_q); if (!skb) return; + kcov_remote_start_common(skb_get_kcov_handle(skb)); /* Check if data flow control is used */ if (atomic_read(&conn_info->credits_cnt) != @@ -1487,6 +1489,7 @@ static void nci_tx_work(struct work_struct *work) mod_timer(&ndev->data_timer, jiffies + msecs_to_jiffies(NCI_DATA_TIMEOUT)); + kcov_remote_stop(); } } @@ -1497,7 +1500,8 @@ static void nci_rx_work(struct work_struct *work) struct nci_dev *ndev = container_of(work, struct nci_dev, rx_work); struct sk_buff *skb; - while ((skb = skb_dequeue(&ndev->rx_q))) { + for (; (skb = skb_dequeue(&ndev->rx_q)); kcov_remote_stop()) { + kcov_remote_start_common(skb_get_kcov_handle(skb)); /* Send copy to sniffer */ nfc_send_to_raw_sock(ndev->nfc_dev, skb, @@ -1551,6 +1555,7 @@ static void nci_cmd_work(struct work_struct *work) if (!skb) return; + kcov_remote_start_common(skb_get_kcov_handle(skb)); atomic_dec(&ndev->cmd_cnt); pr_debug("NCI TX: MT=cmd, PBF=%d, GID=0x%x, OID=0x%x, plen=%d\n", @@ -1563,6 +1568,7 @@ static void nci_cmd_work(struct work_struct *work) mod_timer(&ndev->cmd_timer, jiffies + msecs_to_jiffies(NCI_CMD_TIMEOUT)); + kcov_remote_stop(); } } diff --git a/net/nfc/nci/hci.c b/net/nfc/nci/hci.c index 78c4b6addf15..de175318a3a0 100644 --- a/net/nfc/nci/hci.c +++ b/net/nfc/nci/hci.c @@ -14,6 +14,7 @@ #include #include #include +#include struct nci_data { u8 conn_id; @@ -409,7 +410,8 @@ static void nci_hci_msg_rx_work(struct work_struct *work) const struct nci_hcp_message *message; u8 pipe, type, instruction; - while ((skb = skb_dequeue(&hdev->msg_rx_queue)) != NULL) { + for (; (skb = skb_dequeue(&hdev->msg_rx_queue)); kcov_remote_stop()) { + kcov_remote_start_common(skb_get_kcov_handle(skb)); pipe = NCI_HCP_MSG_GET_PIPE(skb->data[0]); skb_pull(skb, NCI_HCI_HCP_PACKET_HEADER_LEN); message = (struct nci_hcp_message *)skb->data; diff --git a/net/nfc/rawsock.c b/net/nfc/rawsock.c index 8dd569765f96..5125392bb68e 100644 --- a/net/nfc/rawsock.c +++ b/net/nfc/rawsock.c @@ -12,6 +12,7 @@ #include #include #include +#include #include "nfc.h" @@ -189,6 +190,7 @@ static void rawsock_tx_work(struct work_struct *work) } skb = skb_dequeue(&sk->sk_write_queue); + kcov_remote_start_common(skb_get_kcov_handle(skb)); sock_hold(sk); rc = nfc_data_exchange(dev, target_idx, skb, @@ -197,6 +199,7 @@ static void rawsock_tx_work(struct work_struct *work) rawsock_report_error(sk, rc); sock_put(sk); } + kcov_remote_stop(); } static int rawsock_sendmsg(struct socket *sock, struct msghdr *msg, size_t len) From 2e0de6366ac16ab4d0abb2aaddbc8a1eba216d11 Mon Sep 17 00:00:00 2001 From: Heng Qi Date: Mon, 31 Oct 2022 14:19:22 +0800 Subject: [PATCH 0532/2002] veth: Avoid drop packets when xdp_redirect performs In the current processing logic, when xdp_redirect occurs, it transmits the xdp frame based on napi. If napi of the peer veth is not ready, the veth will drop the packets. This doesn't meet our expectations. In this context, we enable napi of the peer veth automatically when the veth loads the xdp. Then if the veth unloads the xdp, we need to correctly judge whether to disable napi of the peer veth, because the peer veth may have loaded xdp, or even the user has enabled GRO. Signed-off-by: Heng Qi Reviewed-by: Xuan Zhuo Signed-off-by: David S. Miller --- drivers/net/veth.c | 90 +++++++++++++++++++++++++++++++++++++++------- 1 file changed, 77 insertions(+), 13 deletions(-) diff --git a/drivers/net/veth.c b/drivers/net/veth.c index ac7c0653695f..b1ed5a93b6c5 100644 --- a/drivers/net/veth.c +++ b/drivers/net/veth.c @@ -1119,10 +1119,14 @@ static void veth_disable_xdp_range(struct net_device *dev, int start, int end, static int veth_enable_xdp(struct net_device *dev) { - bool napi_already_on = veth_gro_requested(dev) && (dev->flags & IFF_UP); struct veth_priv *priv = netdev_priv(dev); + bool napi_already_on; + struct veth_rq *rq; int err, i; + rq = &priv->rq[0]; + napi_already_on = (dev->flags & IFF_UP) && rcu_access_pointer(rq->napi); + if (!xdp_rxq_info_is_reg(&priv->rq[0].xdp_rxq)) { err = veth_enable_xdp_range(dev, 0, dev->real_num_rx_queues, napi_already_on); if (err) @@ -1323,18 +1327,28 @@ revert: static int veth_open(struct net_device *dev) { - struct veth_priv *priv = netdev_priv(dev); + struct veth_priv *peer_priv, *priv = netdev_priv(dev); struct net_device *peer = rtnl_dereference(priv->peer); + struct veth_rq *peer_rq; int err; if (!peer) return -ENOTCONN; + peer_priv = netdev_priv(peer); + peer_rq = &peer_priv->rq[0]; + if (priv->_xdp_prog) { err = veth_enable_xdp(dev); if (err) return err; - } else if (veth_gro_requested(dev)) { + /* refer to the logic in veth_xdp_set() */ + if (!rtnl_dereference(peer_rq->napi)) { + err = veth_napi_enable(peer); + if (err) + return err; + } + } else if (veth_gro_requested(dev) || peer_priv->_xdp_prog) { err = veth_napi_enable(dev); if (err) return err; @@ -1350,18 +1364,30 @@ static int veth_open(struct net_device *dev) static int veth_close(struct net_device *dev) { - struct veth_priv *priv = netdev_priv(dev); + struct veth_priv *peer_priv, *priv = netdev_priv(dev); struct net_device *peer = rtnl_dereference(priv->peer); + struct veth_rq *peer_rq; netif_carrier_off(dev); + if (peer) { + peer_priv = netdev_priv(peer); + peer_rq = &peer_priv->rq[0]; + } + + if (priv->_xdp_prog) { + veth_disable_xdp(dev); + /* refer to the logic in veth_xdp_set */ + if (peer && rtnl_dereference(peer_rq->napi)) { + if (!veth_gro_requested(peer) && !peer_priv->_xdp_prog) + veth_napi_del(peer); + } + } else if (veth_gro_requested(dev) || (peer && peer_priv->_xdp_prog)) { + veth_napi_del(dev); + } + if (peer) netif_carrier_off(peer); - if (priv->_xdp_prog) - veth_disable_xdp(dev); - else if (veth_gro_requested(dev)) - veth_napi_del(dev); - return 0; } @@ -1470,17 +1496,21 @@ static int veth_set_features(struct net_device *dev, { netdev_features_t changed = features ^ dev->features; struct veth_priv *priv = netdev_priv(dev); + struct veth_rq *rq = &priv->rq[0]; int err; if (!(changed & NETIF_F_GRO) || !(dev->flags & IFF_UP) || priv->_xdp_prog) return 0; if (features & NETIF_F_GRO) { - err = veth_napi_enable(dev); - if (err) - return err; + if (!rtnl_dereference(rq->napi)) { + err = veth_napi_enable(dev); + if (err) + return err; + } } else { - veth_napi_del(dev); + if (rtnl_dereference(rq->napi)) + veth_napi_del(dev); } return 0; } @@ -1512,14 +1542,19 @@ static int veth_xdp_set(struct net_device *dev, struct bpf_prog *prog, struct netlink_ext_ack *extack) { struct veth_priv *priv = netdev_priv(dev); + struct veth_priv *peer_priv; struct bpf_prog *old_prog; + struct veth_rq *peer_rq; struct net_device *peer; + bool napi_already_off; unsigned int max_mtu; + bool noreq_napi; int err; old_prog = priv->_xdp_prog; priv->_xdp_prog = prog; peer = rtnl_dereference(priv->peer); + peer_priv = netdev_priv(peer); if (prog) { if (!peer) { @@ -1556,6 +1591,24 @@ static int veth_xdp_set(struct net_device *dev, struct bpf_prog *prog, } } + if (peer && (peer->flags & IFF_UP)) { + peer_rq = &peer_priv->rq[0]; + + /* If the peer hasn't enabled GRO and loaded xdp, + * then we enable napi automatically if its napi + * is not ready. + */ + napi_already_off = !rtnl_dereference(peer_rq->napi); + if (napi_already_off) { + err = veth_napi_enable(peer); + if (err) { + NL_SET_ERR_MSG_MOD(extack, + "Failed to automatically enable napi of peer"); + goto err; + } + } + } + if (!old_prog) { peer->hw_features &= ~NETIF_F_GSO_SOFTWARE; peer->max_mtu = max_mtu; @@ -1570,6 +1623,17 @@ static int veth_xdp_set(struct net_device *dev, struct bpf_prog *prog, if (peer) { peer->hw_features |= NETIF_F_GSO_SOFTWARE; peer->max_mtu = ETH_MAX_MTU; + peer_rq = &peer_priv->rq[0]; + + /* If the peer doesn't has its xdp and enabled + * GRO, then we disable napi if its napi is ready; + */ + if (rtnl_dereference(peer_rq->napi)) { + noreq_napi = !veth_gro_requested(peer) && + !peer_priv->_xdp_prog; + if (noreq_napi && (peer->flags & IFF_UP)) + veth_napi_del(peer); + } } } bpf_prog_put(old_prog); From 1efa9bfe58c595149ef1d5a7c23ad406d713b158 Mon Sep 17 00:00:00 2001 From: Jiawen Wu Date: Mon, 31 Oct 2022 15:07:55 +0800 Subject: [PATCH 0533/2002] net: libwx: Implement interaction with firmware Add mailbox commands to interact with firmware. Signed-off-by: Jiawen Wu Signed-off-by: David S. Miller --- drivers/net/ethernet/wangxun/libwx/wx_hw.c | 427 +++++++++++++++++++ drivers/net/ethernet/wangxun/libwx/wx_hw.h | 7 + drivers/net/ethernet/wangxun/libwx/wx_type.h | 109 +++++ 3 files changed, 543 insertions(+) diff --git a/drivers/net/ethernet/wangxun/libwx/wx_hw.c b/drivers/net/ethernet/wangxun/libwx/wx_hw.c index 8dbbac862f27..eafc1791f859 100644 --- a/drivers/net/ethernet/wangxun/libwx/wx_hw.c +++ b/drivers/net/ethernet/wangxun/libwx/wx_hw.c @@ -73,6 +73,433 @@ int wx_check_flash_load(struct wx_hw *hw, u32 check_bit) } EXPORT_SYMBOL(wx_check_flash_load); +void wx_control_hw(struct wx_hw *wxhw, bool drv) +{ + if (drv) { + /* Let firmware know the driver has taken over */ + wr32m(wxhw, WX_CFG_PORT_CTL, + WX_CFG_PORT_CTL_DRV_LOAD, WX_CFG_PORT_CTL_DRV_LOAD); + } else { + /* Let firmware take over control of hw */ + wr32m(wxhw, WX_CFG_PORT_CTL, + WX_CFG_PORT_CTL_DRV_LOAD, 0); + } +} +EXPORT_SYMBOL(wx_control_hw); + +/** + * wx_mng_present - returns 0 when management capability is present + * @wxhw: pointer to hardware structure + */ +int wx_mng_present(struct wx_hw *wxhw) +{ + u32 fwsm; + + fwsm = rd32(wxhw, WX_MIS_ST); + if (fwsm & WX_MIS_ST_MNG_INIT_DN) + return 0; + else + return -EACCES; +} +EXPORT_SYMBOL(wx_mng_present); + +/* Software lock to be held while software semaphore is being accessed. */ +static DEFINE_MUTEX(wx_sw_sync_lock); + +/** + * wx_release_sw_sync - Release SW semaphore + * @wxhw: pointer to hardware structure + * @mask: Mask to specify which semaphore to release + * + * Releases the SW semaphore for the specified + * function (CSR, PHY0, PHY1, EEPROM, Flash) + **/ +static void wx_release_sw_sync(struct wx_hw *wxhw, u32 mask) +{ + mutex_lock(&wx_sw_sync_lock); + wr32m(wxhw, WX_MNG_SWFW_SYNC, mask, 0); + mutex_unlock(&wx_sw_sync_lock); +} + +/** + * wx_acquire_sw_sync - Acquire SW semaphore + * @wxhw: pointer to hardware structure + * @mask: Mask to specify which semaphore to acquire + * + * Acquires the SW semaphore for the specified + * function (CSR, PHY0, PHY1, EEPROM, Flash) + **/ +static int wx_acquire_sw_sync(struct wx_hw *wxhw, u32 mask) +{ + u32 sem = 0; + int ret = 0; + + mutex_lock(&wx_sw_sync_lock); + ret = read_poll_timeout(rd32, sem, !(sem & mask), + 5000, 2000000, false, wxhw, WX_MNG_SWFW_SYNC); + if (!ret) { + sem |= mask; + wr32(wxhw, WX_MNG_SWFW_SYNC, sem); + } else { + wx_err(wxhw, "SW Semaphore not granted: 0x%x.\n", sem); + } + mutex_unlock(&wx_sw_sync_lock); + + return ret; +} + +/** + * wx_host_interface_command - Issue command to manageability block + * @wxhw: pointer to the HW structure + * @buffer: contains the command to write and where the return status will + * be placed + * @length: length of buffer, must be multiple of 4 bytes + * @timeout: time in ms to wait for command completion + * @return_data: read and return data from the buffer (true) or not (false) + * Needed because FW structures are big endian and decoding of + * these fields can be 8 bit or 16 bit based on command. Decoding + * is not easily understood without making a table of commands. + * So we will leave this up to the caller to read back the data + * in these cases. + **/ +static int wx_host_interface_command(struct wx_hw *wxhw, u32 *buffer, + u32 length, u32 timeout, bool return_data) +{ + u32 hdr_size = sizeof(struct wx_hic_hdr); + u32 hicr, i, bi, buf[64] = {}; + int status = 0; + u32 dword_len; + u16 buf_len; + + if (length == 0 || length > WX_HI_MAX_BLOCK_BYTE_LENGTH) { + wx_err(wxhw, "Buffer length failure buffersize=%d.\n", length); + return -EINVAL; + } + + status = wx_acquire_sw_sync(wxhw, WX_MNG_SWFW_SYNC_SW_MB); + if (status != 0) + return status; + + /* Calculate length in DWORDs. We must be DWORD aligned */ + if ((length % (sizeof(u32))) != 0) { + wx_err(wxhw, "Buffer length failure, not aligned to dword"); + status = -EINVAL; + goto rel_out; + } + + dword_len = length >> 2; + + /* The device driver writes the relevant command block + * into the ram area. + */ + for (i = 0; i < dword_len; i++) { + wr32a(wxhw, WX_MNG_MBOX, i, (__force u32)cpu_to_le32(buffer[i])); + /* write flush */ + buf[i] = rd32a(wxhw, WX_MNG_MBOX, i); + } + /* Setting this bit tells the ARC that a new command is pending. */ + wr32m(wxhw, WX_MNG_MBOX_CTL, + WX_MNG_MBOX_CTL_SWRDY, WX_MNG_MBOX_CTL_SWRDY); + + status = read_poll_timeout(rd32, hicr, hicr & WX_MNG_MBOX_CTL_FWRDY, 1000, + timeout * 1000, false, wxhw, WX_MNG_MBOX_CTL); + if (status) + goto rel_out; + + /* Check command completion */ + if (status) { + wx_dbg(wxhw, "Command has failed with no status valid.\n"); + + buf[0] = rd32(wxhw, WX_MNG_MBOX); + if ((buffer[0] & 0xff) != (~buf[0] >> 24)) { + status = -EINVAL; + goto rel_out; + } + if ((buf[0] & 0xff0000) >> 16 == 0x80) { + wx_dbg(wxhw, "It's unknown cmd.\n"); + status = -EINVAL; + goto rel_out; + } + + wx_dbg(wxhw, "write value:\n"); + for (i = 0; i < dword_len; i++) + wx_dbg(wxhw, "%x ", buffer[i]); + wx_dbg(wxhw, "read value:\n"); + for (i = 0; i < dword_len; i++) + wx_dbg(wxhw, "%x ", buf[i]); + } + + if (!return_data) + goto rel_out; + + /* Calculate length in DWORDs */ + dword_len = hdr_size >> 2; + + /* first pull in the header so we know the buffer length */ + for (bi = 0; bi < dword_len; bi++) { + buffer[bi] = rd32a(wxhw, WX_MNG_MBOX, bi); + le32_to_cpus(&buffer[bi]); + } + + /* If there is any thing in data position pull it in */ + buf_len = ((struct wx_hic_hdr *)buffer)->buf_len; + if (buf_len == 0) + goto rel_out; + + if (length < buf_len + hdr_size) { + wx_err(wxhw, "Buffer not large enough for reply message.\n"); + status = -EFAULT; + goto rel_out; + } + + /* Calculate length in DWORDs, add 3 for odd lengths */ + dword_len = (buf_len + 3) >> 2; + + /* Pull in the rest of the buffer (bi is where we left off) */ + for (; bi <= dword_len; bi++) { + buffer[bi] = rd32a(wxhw, WX_MNG_MBOX, bi); + le32_to_cpus(&buffer[bi]); + } + +rel_out: + wx_release_sw_sync(wxhw, WX_MNG_SWFW_SYNC_SW_MB); + return status; +} + +/** + * wx_read_ee_hostif_data - Read EEPROM word using a host interface cmd + * assuming that the semaphore is already obtained. + * @wxhw: pointer to hardware structure + * @offset: offset of word in the EEPROM to read + * @data: word read from the EEPROM + * + * Reads a 16 bit word from the EEPROM using the hostif. + **/ +static int wx_read_ee_hostif_data(struct wx_hw *wxhw, u16 offset, u16 *data) +{ + struct wx_hic_read_shadow_ram buffer; + int status; + + buffer.hdr.req.cmd = FW_READ_SHADOW_RAM_CMD; + buffer.hdr.req.buf_lenh = 0; + buffer.hdr.req.buf_lenl = FW_READ_SHADOW_RAM_LEN; + buffer.hdr.req.checksum = FW_DEFAULT_CHECKSUM; + + /* convert offset from words to bytes */ + buffer.address = (__force u32)cpu_to_be32(offset * 2); + /* one word */ + buffer.length = (__force u16)cpu_to_be16(sizeof(u16)); + + status = wx_host_interface_command(wxhw, (u32 *)&buffer, sizeof(buffer), + WX_HI_COMMAND_TIMEOUT, false); + + if (status != 0) + return status; + + *data = (u16)rd32a(wxhw, WX_MNG_MBOX, FW_NVM_DATA_OFFSET); + + return status; +} + +/** + * wx_read_ee_hostif - Read EEPROM word using a host interface cmd + * @wxhw: pointer to hardware structure + * @offset: offset of word in the EEPROM to read + * @data: word read from the EEPROM + * + * Reads a 16 bit word from the EEPROM using the hostif. + **/ +int wx_read_ee_hostif(struct wx_hw *wxhw, u16 offset, u16 *data) +{ + int status = 0; + + status = wx_acquire_sw_sync(wxhw, WX_MNG_SWFW_SYNC_SW_FLASH); + if (status == 0) { + status = wx_read_ee_hostif_data(wxhw, offset, data); + wx_release_sw_sync(wxhw, WX_MNG_SWFW_SYNC_SW_FLASH); + } + + return status; +} +EXPORT_SYMBOL(wx_read_ee_hostif); + +/** + * wx_read_ee_hostif_buffer- Read EEPROM word(s) using hostif + * @wxhw: pointer to hardware structure + * @offset: offset of word in the EEPROM to read + * @words: number of words + * @data: word(s) read from the EEPROM + * + * Reads a 16 bit word(s) from the EEPROM using the hostif. + **/ +int wx_read_ee_hostif_buffer(struct wx_hw *wxhw, + u16 offset, u16 words, u16 *data) +{ + struct wx_hic_read_shadow_ram buffer; + u32 current_word = 0; + u16 words_to_read; + u32 value = 0; + int status; + u32 i; + + /* Take semaphore for the entire operation. */ + status = wx_acquire_sw_sync(wxhw, WX_MNG_SWFW_SYNC_SW_FLASH); + if (status != 0) + return status; + + while (words) { + if (words > FW_MAX_READ_BUFFER_SIZE / 2) + words_to_read = FW_MAX_READ_BUFFER_SIZE / 2; + else + words_to_read = words; + + buffer.hdr.req.cmd = FW_READ_SHADOW_RAM_CMD; + buffer.hdr.req.buf_lenh = 0; + buffer.hdr.req.buf_lenl = FW_READ_SHADOW_RAM_LEN; + buffer.hdr.req.checksum = FW_DEFAULT_CHECKSUM; + + /* convert offset from words to bytes */ + buffer.address = (__force u32)cpu_to_be32((offset + current_word) * 2); + buffer.length = (__force u16)cpu_to_be16(words_to_read * 2); + + status = wx_host_interface_command(wxhw, (u32 *)&buffer, + sizeof(buffer), + WX_HI_COMMAND_TIMEOUT, + false); + + if (status != 0) { + wx_err(wxhw, "Host interface command failed\n"); + goto out; + } + + for (i = 0; i < words_to_read; i++) { + u32 reg = WX_MNG_MBOX + (FW_NVM_DATA_OFFSET << 2) + 2 * i; + + value = rd32(wxhw, reg); + data[current_word] = (u16)(value & 0xffff); + current_word++; + i++; + if (i < words_to_read) { + value >>= 16; + data[current_word] = (u16)(value & 0xffff); + current_word++; + } + } + words -= words_to_read; + } + +out: + wx_release_sw_sync(wxhw, WX_MNG_SWFW_SYNC_SW_FLASH); + return status; +} +EXPORT_SYMBOL(wx_read_ee_hostif_buffer); + +/** + * wx_calculate_checksum - Calculate checksum for buffer + * @buffer: pointer to EEPROM + * @length: size of EEPROM to calculate a checksum for + * Calculates the checksum for some buffer on a specified length. The + * checksum calculated is returned. + **/ +static u8 wx_calculate_checksum(u8 *buffer, u32 length) +{ + u8 sum = 0; + u32 i; + + if (!buffer) + return 0; + + for (i = 0; i < length; i++) + sum += buffer[i]; + + return (u8)(0 - sum); +} + +/** + * wx_reset_hostif - send reset cmd to fw + * @wxhw: pointer to hardware structure + * + * Sends reset cmd to firmware through the manageability + * block. + **/ +int wx_reset_hostif(struct wx_hw *wxhw) +{ + struct wx_hic_reset reset_cmd; + int ret_val = 0; + int i; + + reset_cmd.hdr.cmd = FW_RESET_CMD; + reset_cmd.hdr.buf_len = FW_RESET_LEN; + reset_cmd.hdr.cmd_or_resp.cmd_resv = FW_CEM_CMD_RESERVED; + reset_cmd.lan_id = wxhw->bus.func; + reset_cmd.reset_type = (u16)wxhw->reset_type; + reset_cmd.hdr.checksum = 0; + reset_cmd.hdr.checksum = wx_calculate_checksum((u8 *)&reset_cmd, + (FW_CEM_HDR_LEN + + reset_cmd.hdr.buf_len)); + + for (i = 0; i <= FW_CEM_MAX_RETRIES; i++) { + ret_val = wx_host_interface_command(wxhw, (u32 *)&reset_cmd, + sizeof(reset_cmd), + WX_HI_COMMAND_TIMEOUT, + true); + if (ret_val != 0) + continue; + + if (reset_cmd.hdr.cmd_or_resp.ret_status == + FW_CEM_RESP_STATUS_SUCCESS) + ret_val = 0; + else + ret_val = -EFAULT; + + break; + } + + return ret_val; +} +EXPORT_SYMBOL(wx_reset_hostif); + +/** + * wx_init_eeprom_params - Initialize EEPROM params + * @wxhw: pointer to hardware structure + * + * Initializes the EEPROM parameters wx_eeprom_info within the + * wx_hw struct in order to set up EEPROM access. + **/ +void wx_init_eeprom_params(struct wx_hw *wxhw) +{ + struct wx_eeprom_info *eeprom = &wxhw->eeprom; + u16 eeprom_size; + u16 data = 0x80; + + if (eeprom->type == wx_eeprom_uninitialized) { + eeprom->semaphore_delay = 10; + eeprom->type = wx_eeprom_none; + + if (!(rd32(wxhw, WX_SPI_STATUS) & + WX_SPI_STATUS_FLASH_BYPASS)) { + eeprom->type = wx_flash; + + eeprom_size = 4096; + eeprom->word_size = eeprom_size >> 1; + + wx_dbg(wxhw, "Eeprom params: type = %d, size = %d\n", + eeprom->type, eeprom->word_size); + } + } + + if (wxhw->mac.type == wx_mac_sp) { + if (wx_read_ee_hostif(wxhw, WX_SW_REGION_PTR, &data)) { + wx_err(wxhw, "NVM Read Error\n"); + return; + } + data = data >> 1; + } + + eeprom->sw_region_offset = data; +} +EXPORT_SYMBOL(wx_init_eeprom_params); + /** * wx_get_mac_addr - Generic get MAC address * @wxhw: pointer to hardware structure diff --git a/drivers/net/ethernet/wangxun/libwx/wx_hw.h b/drivers/net/ethernet/wangxun/libwx/wx_hw.h index 58a943dc76c1..163777d5ed96 100644 --- a/drivers/net/ethernet/wangxun/libwx/wx_hw.h +++ b/drivers/net/ethernet/wangxun/libwx/wx_hw.h @@ -5,6 +5,13 @@ #define _WX_HW_H_ int wx_check_flash_load(struct wx_hw *hw, u32 check_bit); +void wx_control_hw(struct wx_hw *wxhw, bool drv); +int wx_mng_present(struct wx_hw *wxhw); +int wx_read_ee_hostif(struct wx_hw *wxhw, u16 offset, u16 *data); +int wx_read_ee_hostif_buffer(struct wx_hw *wxhw, + u16 offset, u16 words, u16 *data); +int wx_reset_hostif(struct wx_hw *wxhw); +void wx_init_eeprom_params(struct wx_hw *wxhw); void wx_get_mac_addr(struct wx_hw *wxhw, u8 *mac_addr); int wx_set_rar(struct wx_hw *wxhw, u32 index, u8 *addr, u64 pools, u32 enable_addr); int wx_clear_rar(struct wx_hw *wxhw, u32 index); diff --git a/drivers/net/ethernet/wangxun/libwx/wx_type.h b/drivers/net/ethernet/wangxun/libwx/wx_type.h index 790497cec603..3b2c4586e0c3 100644 --- a/drivers/net/ethernet/wangxun/libwx/wx_type.h +++ b/drivers/net/ethernet/wangxun/libwx/wx_type.h @@ -19,6 +19,11 @@ #define WX_MIS_PWR 0x10000 #define WX_MIS_RST 0x1000C #define WX_MIS_RST_LAN_RST(_i) BIT((_i) + 1) +#define WX_MIS_RST_SW_RST BIT(0) +#define WX_MIS_ST 0x10028 +#define WX_MIS_ST_MNG_INIT_DN BIT(0) +#define WX_MIS_SWSM 0x1002C +#define WX_MIS_SWSM_SMBI BIT(0) #define WX_MIS_RST_ST 0x10030 #define WX_MIS_RST_ST_RST_INI_SHIFT 8 #define WX_MIS_RST_ST_RST_INIT (0xFF << WX_MIS_RST_ST_RST_INI_SHIFT) @@ -51,6 +56,11 @@ #define WX_TS_ALARM_ST_DALARM BIT(1) #define WX_TS_ALARM_ST_ALARM BIT(0) +/************************* Port Registers ************************************/ +/* port cfg Registers */ +#define WX_CFG_PORT_CTL 0x14400 +#define WX_CFG_PORT_CTL_DRV_LOAD BIT(3) + /*********************** Transmit DMA registers **************************/ /* transmit global control */ #define WX_TDM_CTL 0x18000 @@ -107,6 +117,15 @@ #define WX_PSR_MAC_SWC_IDX 0x16210 #define WX_CLEAR_VMDQ_ALL 0xFFFFFFFFU +/************************************** MNG ********************************/ +#define WX_MNG_SWFW_SYNC 0x1E008 +#define WX_MNG_SWFW_SYNC_SW_MB BIT(2) +#define WX_MNG_SWFW_SYNC_SW_FLASH BIT(3) +#define WX_MNG_MBOX 0x1E100 +#define WX_MNG_MBOX_CTL 0x1E044 +#define WX_MNG_MBOX_CTL_SWRDY BIT(0) +#define WX_MNG_MBOX_CTL_FWRDY BIT(2) + /************************************* ETH MAC *****************************/ #define WX_MAC_TX_CFG 0x11000 #define WX_MAC_TX_CFG_TE BIT(0) @@ -144,6 +163,70 @@ /* Number of 80 microseconds we wait for PCI Express master disable */ #define WX_PCI_MASTER_DISABLE_TIMEOUT 80000 +/****************** Manageablility Host Interface defines ********************/ +#define WX_HI_MAX_BLOCK_BYTE_LENGTH 256 /* Num of bytes in range */ +#define WX_HI_COMMAND_TIMEOUT 1000 /* Process HI command limit */ + +#define FW_READ_SHADOW_RAM_CMD 0x31 +#define FW_READ_SHADOW_RAM_LEN 0x6 +#define FW_DEFAULT_CHECKSUM 0xFF /* checksum always 0xFF */ +#define FW_NVM_DATA_OFFSET 3 +#define FW_MAX_READ_BUFFER_SIZE 244 +#define FW_RESET_CMD 0xDF +#define FW_RESET_LEN 0x2 +#define FW_CEM_HDR_LEN 0x4 +#define FW_CEM_CMD_RESERVED 0X0 +#define FW_CEM_MAX_RETRIES 3 +#define FW_CEM_RESP_STATUS_SUCCESS 0x1 + +#define WX_SW_REGION_PTR 0x1C + +/* Host Interface Command Structures */ +struct wx_hic_hdr { + u8 cmd; + u8 buf_len; + union { + u8 cmd_resv; + u8 ret_status; + } cmd_or_resp; + u8 checksum; +}; + +struct wx_hic_hdr2_req { + u8 cmd; + u8 buf_lenh; + u8 buf_lenl; + u8 checksum; +}; + +struct wx_hic_hdr2_rsp { + u8 cmd; + u8 buf_lenl; + u8 buf_lenh_status; /* 7-5: high bits of buf_len, 4-0: status */ + u8 checksum; +}; + +union wx_hic_hdr2 { + struct wx_hic_hdr2_req req; + struct wx_hic_hdr2_rsp rsp; +}; + +/* These need to be dword aligned */ +struct wx_hic_read_shadow_ram { + union wx_hic_hdr2 hdr; + u32 address; + u16 length; + u16 pad2; + u16 data; + u16 pad3; +}; + +struct wx_hic_reset { + struct wx_hic_hdr hdr; + u16 lan_id; + u16 reset_type; +}; + /* Bus parameters */ struct wx_bus_info { u8 func; @@ -175,17 +258,38 @@ struct wx_mac_info { struct wx_thermal_sensor_data sensor; }; +enum wx_eeprom_type { + wx_eeprom_uninitialized = 0, + wx_eeprom_spi, + wx_flash, + wx_eeprom_none /* No NVM support */ +}; + +struct wx_eeprom_info { + enum wx_eeprom_type type; + u32 semaphore_delay; + u16 word_size; + u16 sw_region_offset; +}; + struct wx_addr_filter_info { u32 num_mc_addrs; u32 mta_in_use; bool user_set_promisc; }; +enum wx_reset_type { + WX_LAN_RESET = 0, + WX_SW_RESET, + WX_GLOBAL_RESET +}; + struct wx_hw { u8 __iomem *hw_addr; struct pci_dev *pdev; struct wx_bus_info bus; struct wx_mac_info mac; + struct wx_eeprom_info eeprom; struct wx_addr_filter_info addr_ctrl; u16 device_id; u16 vendor_id; @@ -195,6 +299,7 @@ struct wx_hw { u16 oem_ssid; u16 oem_svid; bool adapter_stopped; + enum wx_reset_type reset_type; }; #define WX_INTR_ALL (~0ULL) @@ -202,6 +307,10 @@ struct wx_hw { /* register operations */ #define wr32(a, reg, value) writel((value), ((a)->hw_addr + (reg))) #define rd32(a, reg) readl((a)->hw_addr + (reg)) +#define rd32a(a, reg, offset) ( \ + rd32((a), (reg) + ((offset) << 2))) +#define wr32a(a, reg, off, val) \ + wr32((a), (reg) + ((off) << 2), (val)) static inline u32 rd32m(struct wx_hw *wxhw, u32 reg, u32 mask) From 049fe5365324c879f26842d44291a5042bbd6cbc Mon Sep 17 00:00:00 2001 From: Jiawen Wu Date: Mon, 31 Oct 2022 15:07:56 +0800 Subject: [PATCH 0534/2002] net: txgbe: Add operations to interact with firmware Add firmware interaction to get EEPROM information. Signed-off-by: Jiawen Wu Signed-off-by: David S. Miller --- drivers/net/ethernet/wangxun/txgbe/txgbe.h | 1 + drivers/net/ethernet/wangxun/txgbe/txgbe_hw.c | 219 +++++++++++++++++- drivers/net/ethernet/wangxun/txgbe/txgbe_hw.h | 2 + .../net/ethernet/wangxun/txgbe/txgbe_main.c | 85 ++++++- .../net/ethernet/wangxun/txgbe/txgbe_type.h | 14 ++ 5 files changed, 316 insertions(+), 5 deletions(-) diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe.h b/drivers/net/ethernet/wangxun/txgbe/txgbe.h index 52e350f9a7d9..19e61377bd00 100644 --- a/drivers/net/ethernet/wangxun/txgbe/txgbe.h +++ b/drivers/net/ethernet/wangxun/txgbe/txgbe.h @@ -35,6 +35,7 @@ struct txgbe_adapter { struct txgbe_hw hw; u16 msg_enable; struct txgbe_mac_addr *mac_table; + char eeprom_id[32]; }; extern char txgbe_driver_name[]; diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_hw.c b/drivers/net/ethernet/wangxun/txgbe/txgbe_hw.c index c7c92c0ec561..0b1032195859 100644 --- a/drivers/net/ethernet/wangxun/txgbe/txgbe_hw.c +++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_hw.c @@ -44,6 +44,218 @@ static void txgbe_init_thermal_sensor_thresh(struct txgbe_hw *hw) wr32(wxhw, WX_TS_DALARM_THRE, 614); } +/** + * txgbe_read_pba_string - Reads part number string from EEPROM + * @hw: pointer to hardware structure + * @pba_num: stores the part number string from the EEPROM + * @pba_num_size: part number string buffer length + * + * Reads the part number string from the EEPROM. + **/ +int txgbe_read_pba_string(struct txgbe_hw *hw, u8 *pba_num, u32 pba_num_size) +{ + u16 pba_ptr, offset, length, data; + struct wx_hw *wxhw = &hw->wxhw; + int ret_val; + + if (!pba_num) { + wx_err(wxhw, "PBA string buffer was null\n"); + return -EINVAL; + } + + ret_val = wx_read_ee_hostif(wxhw, + wxhw->eeprom.sw_region_offset + TXGBE_PBANUM0_PTR, + &data); + if (ret_val != 0) { + wx_err(wxhw, "NVM Read Error\n"); + return ret_val; + } + + ret_val = wx_read_ee_hostif(wxhw, + wxhw->eeprom.sw_region_offset + TXGBE_PBANUM1_PTR, + &pba_ptr); + if (ret_val != 0) { + wx_err(wxhw, "NVM Read Error\n"); + return ret_val; + } + + /* if data is not ptr guard the PBA must be in legacy format which + * means pba_ptr is actually our second data word for the PBA number + * and we can decode it into an ascii string + */ + if (data != TXGBE_PBANUM_PTR_GUARD) { + wx_err(wxhw, "NVM PBA number is not stored as string\n"); + + /* we will need 11 characters to store the PBA */ + if (pba_num_size < 11) { + wx_err(wxhw, "PBA string buffer too small\n"); + return -ENOMEM; + } + + /* extract hex string from data and pba_ptr */ + pba_num[0] = (data >> 12) & 0xF; + pba_num[1] = (data >> 8) & 0xF; + pba_num[2] = (data >> 4) & 0xF; + pba_num[3] = data & 0xF; + pba_num[4] = (pba_ptr >> 12) & 0xF; + pba_num[5] = (pba_ptr >> 8) & 0xF; + pba_num[6] = '-'; + pba_num[7] = 0; + pba_num[8] = (pba_ptr >> 4) & 0xF; + pba_num[9] = pba_ptr & 0xF; + + /* put a null character on the end of our string */ + pba_num[10] = '\0'; + + /* switch all the data but the '-' to hex char */ + for (offset = 0; offset < 10; offset++) { + if (pba_num[offset] < 0xA) + pba_num[offset] += '0'; + else if (pba_num[offset] < 0x10) + pba_num[offset] += 'A' - 0xA; + } + + return 0; + } + + ret_val = wx_read_ee_hostif(wxhw, pba_ptr, &length); + if (ret_val != 0) { + wx_err(wxhw, "NVM Read Error\n"); + return ret_val; + } + + if (length == 0xFFFF || length == 0) { + wx_err(wxhw, "NVM PBA number section invalid length\n"); + return -EINVAL; + } + + /* check if pba_num buffer is big enough */ + if (pba_num_size < (((u32)length * 2) - 1)) { + wx_err(wxhw, "PBA string buffer too small\n"); + return -ENOMEM; + } + + /* trim pba length from start of string */ + pba_ptr++; + length--; + + for (offset = 0; offset < length; offset++) { + ret_val = wx_read_ee_hostif(wxhw, pba_ptr + offset, &data); + if (ret_val != 0) { + wx_err(wxhw, "NVM Read Error\n"); + return ret_val; + } + pba_num[offset * 2] = (u8)(data >> 8); + pba_num[(offset * 2) + 1] = (u8)(data & 0xFF); + } + pba_num[offset * 2] = '\0'; + + return 0; +} + +/** + * txgbe_calc_eeprom_checksum - Calculates and returns the checksum + * @hw: pointer to hardware structure + * @checksum: pointer to cheksum + * + * Returns a negative error code on error + **/ +static int txgbe_calc_eeprom_checksum(struct txgbe_hw *hw, u16 *checksum) +{ + struct wx_hw *wxhw = &hw->wxhw; + u16 *eeprom_ptrs = NULL; + u32 buffer_size = 0; + u16 *buffer = NULL; + u16 *local_buffer; + int status; + u16 i; + + wx_init_eeprom_params(wxhw); + + if (!buffer) { + eeprom_ptrs = kvmalloc_array(TXGBE_EEPROM_LAST_WORD, sizeof(u16), + GFP_KERNEL); + if (!eeprom_ptrs) + return -ENOMEM; + /* Read pointer area */ + status = wx_read_ee_hostif_buffer(wxhw, 0, + TXGBE_EEPROM_LAST_WORD, + eeprom_ptrs); + if (status != 0) { + wx_err(wxhw, "Failed to read EEPROM image\n"); + return status; + } + local_buffer = eeprom_ptrs; + } else { + if (buffer_size < TXGBE_EEPROM_LAST_WORD) + return -EFAULT; + local_buffer = buffer; + } + + for (i = 0; i < TXGBE_EEPROM_LAST_WORD; i++) + if (i != wxhw->eeprom.sw_region_offset + TXGBE_EEPROM_CHECKSUM) + *checksum += local_buffer[i]; + + *checksum = TXGBE_EEPROM_SUM - *checksum; + if (*checksum < 0) + return -EINVAL; + + if (eeprom_ptrs) + kvfree(eeprom_ptrs); + + return 0; +} + +/** + * txgbe_validate_eeprom_checksum - Validate EEPROM checksum + * @hw: pointer to hardware structure + * @checksum_val: calculated checksum + * + * Performs checksum calculation and validates the EEPROM checksum. If the + * caller does not need checksum_val, the value can be NULL. + **/ +int txgbe_validate_eeprom_checksum(struct txgbe_hw *hw, u16 *checksum_val) +{ + struct wx_hw *wxhw = &hw->wxhw; + u16 read_checksum = 0; + u16 checksum; + int status; + + /* Read the first word from the EEPROM. If this times out or fails, do + * not continue or we could be in for a very long wait while every + * EEPROM read fails + */ + status = wx_read_ee_hostif(wxhw, 0, &checksum); + if (status) { + wx_err(wxhw, "EEPROM read failed\n"); + return status; + } + + checksum = 0; + status = txgbe_calc_eeprom_checksum(hw, &checksum); + if (status != 0) + return status; + + status = wx_read_ee_hostif(wxhw, wxhw->eeprom.sw_region_offset + + TXGBE_EEPROM_CHECKSUM, &read_checksum); + if (status != 0) + return status; + + /* Verify read checksum from EEPROM is the same as + * calculated checksum + */ + if (read_checksum != checksum) { + status = -EIO; + wx_err(wxhw, "Invalid EEPROM checksum\n"); + } + + /* If the user cares, return the calculated checksum */ + if (checksum_val) + *checksum_val = checksum; + + return status; +} + static void txgbe_reset_misc(struct txgbe_hw *hw) { struct wx_hw *wxhw = &hw->wxhw; @@ -63,7 +275,6 @@ static void txgbe_reset_misc(struct txgbe_hw *hw) int txgbe_reset_hw(struct txgbe_hw *hw) { struct wx_hw *wxhw = &hw->wxhw; - u32 reset = 0; int status; /* Call adapter stop to disable tx/rx and clear interrupts */ @@ -71,10 +282,10 @@ int txgbe_reset_hw(struct txgbe_hw *hw) if (status != 0) return status; - reset = WX_MIS_RST_LAN_RST(wxhw->bus.func); - wr32(wxhw, WX_MIS_RST, reset | rd32(wxhw, WX_MIS_RST)); + if (!(((wxhw->subsystem_device_id & WX_NCSI_MASK) == WX_NCSI_SUP) || + ((wxhw->subsystem_device_id & WX_WOL_MASK) == WX_WOL_SUP))) + wx_reset_hostif(wxhw); - WX_WRITE_FLUSH(wxhw); usleep_range(10, 100); status = wx_check_flash_load(wxhw, TXGBE_SPI_ILDR_STATUS_LAN_SW_RST(wxhw->bus.func)); diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_hw.h b/drivers/net/ethernet/wangxun/txgbe/txgbe_hw.h index 155f18ea4b8c..6a751a69177b 100644 --- a/drivers/net/ethernet/wangxun/txgbe/txgbe_hw.h +++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_hw.h @@ -4,6 +4,8 @@ #ifndef _TXGBE_HW_H_ #define _TXGBE_HW_H_ +int txgbe_read_pba_string(struct txgbe_hw *hw, u8 *pba_num, u32 pba_num_size); +int txgbe_validate_eeprom_checksum(struct txgbe_hw *hw, u16 *checksum_val); int txgbe_reset_hw(struct txgbe_hw *hw); #endif /* _TXGBE_HW_H_ */ diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c b/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c index adfa4e7d0d52..36780e7f05b7 100644 --- a/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c +++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c @@ -158,6 +158,14 @@ static int txgbe_del_mac_filter(struct txgbe_adapter *adapter, u8 *addr, u16 poo return -ENOMEM; } +static void txgbe_up_complete(struct txgbe_adapter *adapter) +{ + struct txgbe_hw *hw = &adapter->hw; + struct wx_hw *wxhw = &hw->wxhw; + + wx_control_hw(wxhw, true); +} + static void txgbe_reset(struct txgbe_adapter *adapter) { struct net_device *netdev = adapter->netdev; @@ -270,6 +278,10 @@ static int txgbe_sw_init(struct txgbe_adapter *adapter) **/ static int txgbe_open(struct net_device *netdev) { + struct txgbe_adapter *adapter = netdev_priv(netdev); + + txgbe_up_complete(adapter); + return 0; } @@ -301,6 +313,7 @@ static int txgbe_close(struct net_device *netdev) struct txgbe_adapter *adapter = netdev_priv(netdev); txgbe_down(adapter); + wx_control_hw(&adapter->hw.wxhw, false); return 0; } @@ -309,6 +322,8 @@ static void txgbe_dev_shutdown(struct pci_dev *pdev, bool *enable_wake) { struct txgbe_adapter *adapter = pci_get_drvdata(pdev); struct net_device *netdev = adapter->netdev; + struct txgbe_hw *hw = &adapter->hw; + struct wx_hw *wxhw = &hw->wxhw; netif_device_detach(netdev); @@ -317,6 +332,8 @@ static void txgbe_dev_shutdown(struct pci_dev *pdev, bool *enable_wake) txgbe_close_suspend(adapter); rtnl_unlock(); + wx_control_hw(wxhw, false); + pci_disable_device(pdev); } @@ -393,6 +410,12 @@ static int txgbe_probe(struct pci_dev *pdev, struct net_device *netdev; int err, expected_gts; + u16 eeprom_verh = 0, eeprom_verl = 0, offset = 0; + u16 eeprom_cfg_blkh = 0, eeprom_cfg_blkl = 0; + u16 build = 0, major = 0, patch = 0; + u8 part_str[TXGBE_PBANUM_LENGTH]; + u32 etrack_id = 0; + err = pci_enable_device_mem(pdev); if (err) return err; @@ -457,6 +480,12 @@ static int txgbe_probe(struct pci_dev *pdev, if (err) goto err_free_mac_table; + err = wx_mng_present(wxhw); + if (err) { + dev_err(&pdev->dev, "Management capability is not present\n"); + goto err_free_mac_table; + } + err = txgbe_reset_hw(hw); if (err) { dev_err(&pdev->dev, "HW Init failed: %d\n", err); @@ -465,12 +494,59 @@ static int txgbe_probe(struct pci_dev *pdev, netdev->features |= NETIF_F_HIGHDMA; + /* make sure the EEPROM is good */ + err = txgbe_validate_eeprom_checksum(hw, NULL); + if (err != 0) { + dev_err(&pdev->dev, "The EEPROM Checksum Is Not Valid\n"); + wr32(wxhw, WX_MIS_RST, WX_MIS_RST_SW_RST); + err = -EIO; + goto err_free_mac_table; + } + eth_hw_addr_set(netdev, wxhw->mac.perm_addr); txgbe_mac_set_default_filter(adapter, wxhw->mac.perm_addr); + /* Save off EEPROM version number and Option Rom version which + * together make a unique identify for the eeprom + */ + wx_read_ee_hostif(wxhw, + wxhw->eeprom.sw_region_offset + TXGBE_EEPROM_VERSION_H, + &eeprom_verh); + wx_read_ee_hostif(wxhw, + wxhw->eeprom.sw_region_offset + TXGBE_EEPROM_VERSION_L, + &eeprom_verl); + etrack_id = (eeprom_verh << 16) | eeprom_verl; + + wx_read_ee_hostif(wxhw, + wxhw->eeprom.sw_region_offset + TXGBE_ISCSI_BOOT_CONFIG, + &offset); + + /* Make sure offset to SCSI block is valid */ + if (!(offset == 0x0) && !(offset == 0xffff)) { + wx_read_ee_hostif(wxhw, offset + 0x84, &eeprom_cfg_blkh); + wx_read_ee_hostif(wxhw, offset + 0x83, &eeprom_cfg_blkl); + + /* Only display Option Rom if exist */ + if (eeprom_cfg_blkl && eeprom_cfg_blkh) { + major = eeprom_cfg_blkl >> 8; + build = (eeprom_cfg_blkl << 8) | (eeprom_cfg_blkh >> 8); + patch = eeprom_cfg_blkh & 0x00ff; + + snprintf(adapter->eeprom_id, sizeof(adapter->eeprom_id), + "0x%08x, %d.%d.%d", etrack_id, major, build, + patch); + } else { + snprintf(adapter->eeprom_id, sizeof(adapter->eeprom_id), + "0x%08x", etrack_id); + } + } else { + snprintf(adapter->eeprom_id, sizeof(adapter->eeprom_id), + "0x%08x", etrack_id); + } + err = register_netdev(netdev); if (err) - goto err_free_mac_table; + goto err_release_hw; pci_set_drvdata(pdev, adapter); @@ -488,10 +564,17 @@ static int txgbe_probe(struct pci_dev *pdev, else dev_warn(&pdev->dev, "Failed to enumerate PF devices.\n"); + /* First try to read PBA as a string */ + err = txgbe_read_pba_string(hw, part_str, TXGBE_PBANUM_LENGTH); + if (err) + strncpy(part_str, "Unknown", TXGBE_PBANUM_LENGTH); + netif_info(adapter, probe, netdev, "%pM\n", netdev->dev_addr); return 0; +err_release_hw: + wx_control_hw(wxhw, false); err_free_mac_table: kfree(adapter->mac_table); err_pci_release_regions: diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_type.h b/drivers/net/ethernet/wangxun/txgbe/txgbe_type.h index 4082d3b76709..740a1c447e20 100644 --- a/drivers/net/ethernet/wangxun/txgbe/txgbe_type.h +++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_type.h @@ -53,6 +53,20 @@ #define TXGBE_TS_CTL 0x10300 #define TXGBE_TS_CTL_EVAL_MD BIT(31) +/* Part Number String Length */ +#define TXGBE_PBANUM_LENGTH 32 + +/* Checksum and EEPROM pointers */ +#define TXGBE_EEPROM_LAST_WORD 0x800 +#define TXGBE_EEPROM_CHECKSUM 0x2F +#define TXGBE_EEPROM_SUM 0xBABA +#define TXGBE_EEPROM_VERSION_L 0x1D +#define TXGBE_EEPROM_VERSION_H 0x1E +#define TXGBE_ISCSI_BOOT_CONFIG 0x07 +#define TXGBE_PBANUM0_PTR 0x05 +#define TXGBE_PBANUM1_PTR 0x06 +#define TXGBE_PBANUM_PTR_GUARD 0xFAFA + struct txgbe_hw { struct wx_hw wxhw; }; From 02338c484ab6250b81f0266ffb40d53c3efe0f47 Mon Sep 17 00:00:00 2001 From: Mengyuan Lou Date: Mon, 31 Oct 2022 15:07:57 +0800 Subject: [PATCH 0535/2002] net: ngbe: Initialize sw info and register netdev Initialize ngbe mac/phy type. Check whether the firmware is initialized. Initialize ngbe hw and register netdev. Signed-off-by: Mengyuan Lou Signed-off-by: David S. Miller --- drivers/net/ethernet/wangxun/Kconfig | 1 + drivers/net/ethernet/wangxun/libwx/wx_hw.c | 40 +- drivers/net/ethernet/wangxun/libwx/wx_hw.h | 3 + drivers/net/ethernet/wangxun/libwx/wx_type.h | 6 + drivers/net/ethernet/wangxun/ngbe/Makefile | 2 +- drivers/net/ethernet/wangxun/ngbe/ngbe.h | 55 +++ drivers/net/ethernet/wangxun/ngbe/ngbe_hw.c | 87 +++++ drivers/net/ethernet/wangxun/ngbe/ngbe_hw.h | 12 + drivers/net/ethernet/wangxun/ngbe/ngbe_main.c | 368 ++++++++++++++++++ drivers/net/ethernet/wangxun/ngbe/ngbe_type.h | 99 ++++- 10 files changed, 665 insertions(+), 8 deletions(-) create mode 100644 drivers/net/ethernet/wangxun/ngbe/ngbe_hw.c create mode 100644 drivers/net/ethernet/wangxun/ngbe/ngbe_hw.h diff --git a/drivers/net/ethernet/wangxun/Kconfig b/drivers/net/ethernet/wangxun/Kconfig index 565fa826b056..86310588c6c1 100644 --- a/drivers/net/ethernet/wangxun/Kconfig +++ b/drivers/net/ethernet/wangxun/Kconfig @@ -24,6 +24,7 @@ config LIBWX config NGBE tristate "Wangxun(R) GbE PCI Express adapters support" depends on PCI + select LIBWX help This driver supports Wangxun(R) GbE PCI Express family of adapters. diff --git a/drivers/net/ethernet/wangxun/libwx/wx_hw.c b/drivers/net/ethernet/wangxun/libwx/wx_hw.c index eafc1791f859..1eb7388f1dd5 100644 --- a/drivers/net/ethernet/wangxun/libwx/wx_hw.c +++ b/drivers/net/ethernet/wangxun/libwx/wx_hw.c @@ -162,8 +162,8 @@ static int wx_acquire_sw_sync(struct wx_hw *wxhw, u32 mask) * So we will leave this up to the caller to read back the data * in these cases. **/ -static int wx_host_interface_command(struct wx_hw *wxhw, u32 *buffer, - u32 length, u32 timeout, bool return_data) +int wx_host_interface_command(struct wx_hw *wxhw, u32 *buffer, + u32 length, u32 timeout, bool return_data) { u32 hdr_size = sizeof(struct wx_hic_hdr); u32 hicr, i, bi, buf[64] = {}; @@ -265,6 +265,7 @@ rel_out: wx_release_sw_sync(wxhw, WX_MNG_SWFW_SYNC_SW_MB); return status; } +EXPORT_SYMBOL(wx_host_interface_command); /** * wx_read_ee_hostif_data - Read EEPROM word using a host interface cmd @@ -870,6 +871,41 @@ void wx_reset_misc(struct wx_hw *wxhw) } EXPORT_SYMBOL(wx_reset_misc); +/** + * wx_get_pcie_msix_counts - Gets MSI-X vector count + * @wxhw: pointer to hardware structure + * @msix_count: number of MSI interrupts that can be obtained + * @max_msix_count: number of MSI interrupts that mac need + * + * Read PCIe configuration space, and get the MSI-X vector count from + * the capabilities table. + **/ +int wx_get_pcie_msix_counts(struct wx_hw *wxhw, u16 *msix_count, u16 max_msix_count) +{ + struct pci_dev *pdev = wxhw->pdev; + struct device *dev = &pdev->dev; + int pos; + + *msix_count = 1; + pos = pci_find_capability(pdev, PCI_CAP_ID_MSIX); + if (!pos) { + dev_err(dev, "Unable to find MSI-X Capabilities\n"); + return -EINVAL; + } + pci_read_config_word(pdev, + pos + PCI_MSIX_FLAGS, + msix_count); + *msix_count &= WX_PCIE_MSIX_TBL_SZ_MASK; + /* MSI-X count is zero-based in HW */ + *msix_count += 1; + + if (*msix_count > max_msix_count) + *msix_count = max_msix_count; + + return 0; +} +EXPORT_SYMBOL(wx_get_pcie_msix_counts); + int wx_sw_init(struct wx_hw *wxhw) { struct pci_dev *pdev = wxhw->pdev; diff --git a/drivers/net/ethernet/wangxun/libwx/wx_hw.h b/drivers/net/ethernet/wangxun/libwx/wx_hw.h index 163777d5ed96..a0652f5e9939 100644 --- a/drivers/net/ethernet/wangxun/libwx/wx_hw.h +++ b/drivers/net/ethernet/wangxun/libwx/wx_hw.h @@ -7,6 +7,8 @@ int wx_check_flash_load(struct wx_hw *hw, u32 check_bit); void wx_control_hw(struct wx_hw *wxhw, bool drv); int wx_mng_present(struct wx_hw *wxhw); +int wx_host_interface_command(struct wx_hw *wxhw, u32 *buffer, + u32 length, u32 timeout, bool return_data); int wx_read_ee_hostif(struct wx_hw *wxhw, u16 offset, u16 *data); int wx_read_ee_hostif_buffer(struct wx_hw *wxhw, u16 offset, u16 words, u16 *data); @@ -20,6 +22,7 @@ void wx_disable_rx(struct wx_hw *wxhw); int wx_disable_pcie_master(struct wx_hw *wxhw); int wx_stop_adapter(struct wx_hw *wxhw); void wx_reset_misc(struct wx_hw *wxhw); +int wx_get_pcie_msix_counts(struct wx_hw *wxhw, u16 *msix_count, u16 max_msix_count); int wx_sw_init(struct wx_hw *wxhw); #endif /* _WX_HW_H_ */ diff --git a/drivers/net/ethernet/wangxun/libwx/wx_type.h b/drivers/net/ethernet/wangxun/libwx/wx_type.h index 3b2c4586e0c3..1cbeef8230bf 100644 --- a/drivers/net/ethernet/wangxun/libwx/wx_type.h +++ b/drivers/net/ethernet/wangxun/libwx/wx_type.h @@ -14,6 +14,10 @@ #define WX_WOL_SUP 0x4000 #define WX_WOL_MASK 0x4000 +/* MSI-X capability fields masks */ +#define WX_PCIE_MSIX_TBL_SZ_MASK 0x7FF +#define WX_PCI_LINK_STATUS 0xB2 + /**************** Global Registers ****************************/ /* chip control Registers */ #define WX_MIS_PWR 0x10000 @@ -255,6 +259,8 @@ struct wx_mac_info { u32 num_rar_entries; u32 max_tx_queues; u32 max_rx_queues; + + u16 max_msix_vectors; struct wx_thermal_sensor_data sensor; }; diff --git a/drivers/net/ethernet/wangxun/ngbe/Makefile b/drivers/net/ethernet/wangxun/ngbe/Makefile index 0baf75907496..391c2cbc1bb4 100644 --- a/drivers/net/ethernet/wangxun/ngbe/Makefile +++ b/drivers/net/ethernet/wangxun/ngbe/Makefile @@ -6,4 +6,4 @@ obj-$(CONFIG_NGBE) += ngbe.o -ngbe-objs := ngbe_main.o +ngbe-objs := ngbe_main.o ngbe_hw.o diff --git a/drivers/net/ethernet/wangxun/ngbe/ngbe.h b/drivers/net/ethernet/wangxun/ngbe/ngbe.h index f5fa6e5238cc..af147ca8605c 100644 --- a/drivers/net/ethernet/wangxun/ngbe/ngbe.h +++ b/drivers/net/ethernet/wangxun/ngbe/ngbe.h @@ -11,12 +11,67 @@ #define NGBE_MAX_RX_QUEUES (NGBE_MAX_FDIR_INDICES + 1) #define NGBE_MAX_TX_QUEUES (NGBE_MAX_FDIR_INDICES + 1) +#define NGBE_ETH_LENGTH_OF_ADDRESS 6 +#define NGBE_MAX_MSIX_VECTORS 0x09 +#define NGBE_RAR_ENTRIES 32 + +/* TX/RX descriptor defines */ +#define NGBE_DEFAULT_TXD 512 /* default ring size */ +#define NGBE_DEFAULT_TX_WORK 256 +#define NGBE_MAX_TXD 8192 +#define NGBE_MIN_TXD 128 + +#define NGBE_DEFAULT_RXD 512 /* default ring size */ +#define NGBE_DEFAULT_RX_WORK 256 +#define NGBE_MAX_RXD 8192 +#define NGBE_MIN_RXD 128 + +#define NGBE_MAC_STATE_DEFAULT 0x1 +#define NGBE_MAC_STATE_MODIFIED 0x2 +#define NGBE_MAC_STATE_IN_USE 0x4 + +struct ngbe_mac_addr { + u8 addr[ETH_ALEN]; + u16 state; /* bitmask */ + u64 pools; +}; + /* board specific private data structure */ struct ngbe_adapter { u8 __iomem *io_addr; /* Mainly for iounmap use */ /* OS defined structs */ struct net_device *netdev; struct pci_dev *pdev; + + /* structs defined in ngbe_hw.h */ + struct ngbe_hw hw; + struct ngbe_mac_addr *mac_table; + u16 msg_enable; + + /* Tx fast path data */ + int num_tx_queues; + u16 tx_itr_setting; + u16 tx_work_limit; + + /* Rx fast path data */ + int num_rx_queues; + u16 rx_itr_setting; + u16 rx_work_limit; + + int num_q_vectors; /* current number of q_vectors for device */ + int max_q_vectors; /* upper limit of q_vectors for device */ + + u32 tx_ring_count; + u32 rx_ring_count; + +#define NGBE_MAX_RETA_ENTRIES 128 + u8 rss_indir_tbl[NGBE_MAX_RETA_ENTRIES]; + +#define NGBE_RSS_KEY_SIZE 40 /* size of RSS Hash Key in bytes */ + u32 *rss_key; + u32 wol; + + u16 bd_number; }; extern char ngbe_driver_name[]; diff --git a/drivers/net/ethernet/wangxun/ngbe/ngbe_hw.c b/drivers/net/ethernet/wangxun/ngbe/ngbe_hw.c new file mode 100644 index 000000000000..0e3923b3737e --- /dev/null +++ b/drivers/net/ethernet/wangxun/ngbe/ngbe_hw.c @@ -0,0 +1,87 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2019 - 2022 Beijing WangXun Technology Co., Ltd. */ + +#include +#include +#include + +#include "../libwx/wx_type.h" +#include "../libwx/wx_hw.h" +#include "ngbe_type.h" +#include "ngbe_hw.h" +#include "ngbe.h" + +int ngbe_eeprom_chksum_hostif(struct ngbe_hw *hw) +{ + struct wx_hic_read_shadow_ram buffer; + struct wx_hw *wxhw = &hw->wxhw; + int status; + int tmp; + + buffer.hdr.req.cmd = NGBE_FW_EEPROM_CHECKSUM_CMD; + buffer.hdr.req.buf_lenh = 0; + buffer.hdr.req.buf_lenl = 0; + buffer.hdr.req.checksum = NGBE_FW_CMD_DEFAULT_CHECKSUM; + /* convert offset from words to bytes */ + buffer.address = 0; + /* one word */ + buffer.length = 0; + + status = wx_host_interface_command(wxhw, (u32 *)&buffer, sizeof(buffer), + WX_HI_COMMAND_TIMEOUT, false); + + if (status < 0) + return status; + tmp = rd32a(wxhw, WX_MNG_MBOX, 1); + if (tmp == NGBE_FW_CMD_ST_PASS) + return 0; + return -EIO; +} + +static int ngbe_reset_misc(struct ngbe_hw *hw) +{ + struct wx_hw *wxhw = &hw->wxhw; + + wx_reset_misc(wxhw); + if (hw->mac_type == ngbe_mac_type_rgmii) + wr32(wxhw, NGBE_MDIO_CLAUSE_SELECT, 0xF); + if (hw->gpio_ctrl) { + /* gpio0 is used to power on/off control*/ + wr32(wxhw, NGBE_GPIO_DDR, 0x1); + wr32(wxhw, NGBE_GPIO_DR, NGBE_GPIO_DR_0); + } + return 0; +} + +/** + * ngbe_reset_hw - Perform hardware reset + * @hw: pointer to hardware structure + * + * Resets the hardware by resetting the transmit and receive units, masks + * and clears all interrupts, perform a PHY reset, and perform a link (MAC) + * reset. + **/ +int ngbe_reset_hw(struct ngbe_hw *hw) +{ + struct wx_hw *wxhw = &hw->wxhw; + int status = 0; + u32 reset = 0; + + /* Call adapter stop to disable tx/rx and clear interrupts */ + status = wx_stop_adapter(wxhw); + if (status != 0) + return status; + reset = WX_MIS_RST_LAN_RST(wxhw->bus.func); + wr32(wxhw, WX_MIS_RST, reset | rd32(wxhw, WX_MIS_RST)); + ngbe_reset_misc(hw); + + /* Store the permanent mac address */ + wx_get_mac_addr(wxhw, wxhw->mac.perm_addr); + + /* reset num_rar_entries to 128 */ + wxhw->mac.num_rar_entries = NGBE_RAR_ENTRIES; + wx_init_rx_addrs(wxhw); + pci_set_master(wxhw->pdev); + + return 0; +} diff --git a/drivers/net/ethernet/wangxun/ngbe/ngbe_hw.h b/drivers/net/ethernet/wangxun/ngbe/ngbe_hw.h new file mode 100644 index 000000000000..42476a3fe57c --- /dev/null +++ b/drivers/net/ethernet/wangxun/ngbe/ngbe_hw.h @@ -0,0 +1,12 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * WangXun Gigabit PCI Express Linux driver + * Copyright (c) 2019 - 2022 Beijing WangXun Technology Co., Ltd. + */ + +#ifndef _NGBE_HW_H_ +#define _NGBE_HW_H_ + +int ngbe_eeprom_chksum_hostif(struct ngbe_hw *hw); +int ngbe_reset_hw(struct ngbe_hw *hw); +#endif /* _NGBE_HW_H_ */ diff --git a/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c b/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c index 7674cb6e5700..f0b24366da18 100644 --- a/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c +++ b/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c @@ -8,7 +8,12 @@ #include #include #include +#include +#include "../libwx/wx_type.h" +#include "../libwx/wx_hw.h" +#include "ngbe_type.h" +#include "ngbe_hw.h" #include "ngbe.h" char ngbe_driver_name[] = "ngbe"; @@ -34,6 +39,247 @@ static const struct pci_device_id ngbe_pci_tbl[] = { { .device = 0 } }; +static void ngbe_mac_set_default_filter(struct ngbe_adapter *adapter, u8 *addr) +{ + struct ngbe_hw *hw = &adapter->hw; + + memcpy(&adapter->mac_table[0].addr, addr, ETH_ALEN); + adapter->mac_table[0].pools = 1ULL; + adapter->mac_table[0].state = (NGBE_MAC_STATE_DEFAULT | + NGBE_MAC_STATE_IN_USE); + wx_set_rar(&hw->wxhw, 0, adapter->mac_table[0].addr, + adapter->mac_table[0].pools, + WX_PSR_MAC_SWC_AD_H_AV); +} + +/** + * ngbe_init_type_code - Initialize the shared code + * @hw: pointer to hardware structure + **/ +static void ngbe_init_type_code(struct ngbe_hw *hw) +{ + int wol_mask = 0, ncsi_mask = 0; + struct wx_hw *wxhw = &hw->wxhw; + u16 type_mask = 0; + + wxhw->mac.type = wx_mac_em; + type_mask = (u16)(wxhw->subsystem_device_id & NGBE_OEM_MASK); + ncsi_mask = wxhw->subsystem_device_id & NGBE_NCSI_MASK; + wol_mask = wxhw->subsystem_device_id & NGBE_WOL_MASK; + + switch (type_mask) { + case NGBE_SUBID_M88E1512_SFP: + case NGBE_SUBID_LY_M88E1512_SFP: + hw->phy.type = ngbe_phy_m88e1512_sfi; + break; + case NGBE_SUBID_M88E1512_RJ45: + hw->phy.type = ngbe_phy_m88e1512; + break; + case NGBE_SUBID_M88E1512_MIX: + hw->phy.type = ngbe_phy_m88e1512_unknown; + break; + case NGBE_SUBID_YT8521S_SFP: + case NGBE_SUBID_YT8521S_SFP_GPIO: + case NGBE_SUBID_LY_YT8521S_SFP: + hw->phy.type = ngbe_phy_yt8521s_sfi; + break; + case NGBE_SUBID_INTERNAL_YT8521S_SFP: + case NGBE_SUBID_INTERNAL_YT8521S_SFP_GPIO: + hw->phy.type = ngbe_phy_internal_yt8521s_sfi; + break; + case NGBE_SUBID_RGMII_FPGA: + case NGBE_SUBID_OCP_CARD: + fallthrough; + default: + hw->phy.type = ngbe_phy_internal; + break; + } + + if (hw->phy.type == ngbe_phy_internal || + hw->phy.type == ngbe_phy_internal_yt8521s_sfi) + hw->mac_type = ngbe_mac_type_mdi; + else + hw->mac_type = ngbe_mac_type_rgmii; + + hw->wol_enabled = (wol_mask == NGBE_WOL_SUP) ? 1 : 0; + hw->ncsi_enabled = (ncsi_mask == NGBE_NCSI_MASK || + type_mask == NGBE_SUBID_OCP_CARD) ? 1 : 0; + + switch (type_mask) { + case NGBE_SUBID_LY_YT8521S_SFP: + case NGBE_SUBID_LY_M88E1512_SFP: + case NGBE_SUBID_YT8521S_SFP_GPIO: + case NGBE_SUBID_INTERNAL_YT8521S_SFP_GPIO: + hw->gpio_ctrl = 1; + break; + default: + hw->gpio_ctrl = 0; + break; + } +} + +/** + * ngbe_init_rss_key - Initialize adapter RSS key + * @adapter: device handle + * + * Allocates and initializes the RSS key if it is not allocated. + **/ +static inline int ngbe_init_rss_key(struct ngbe_adapter *adapter) +{ + u32 *rss_key; + + if (!adapter->rss_key) { + rss_key = kzalloc(NGBE_RSS_KEY_SIZE, GFP_KERNEL); + if (unlikely(!rss_key)) + return -ENOMEM; + + netdev_rss_key_fill(rss_key, NGBE_RSS_KEY_SIZE); + adapter->rss_key = rss_key; + } + + return 0; +} + +/** + * ngbe_sw_init - Initialize general software structures + * @adapter: board private structure to initialize + **/ +static int ngbe_sw_init(struct ngbe_adapter *adapter) +{ + struct pci_dev *pdev = adapter->pdev; + struct ngbe_hw *hw = &adapter->hw; + struct wx_hw *wxhw = &hw->wxhw; + u16 msix_count = 0; + int err = 0; + + wxhw->hw_addr = adapter->io_addr; + wxhw->pdev = pdev; + + /* PCI config space info */ + err = wx_sw_init(wxhw); + if (err < 0) { + netif_err(adapter, probe, adapter->netdev, + "Read of internal subsystem device id failed\n"); + return err; + } + + /* mac type, phy type , oem type */ + ngbe_init_type_code(hw); + + wxhw->mac.max_rx_queues = NGBE_MAX_RX_QUEUES; + wxhw->mac.max_tx_queues = NGBE_MAX_TX_QUEUES; + wxhw->mac.num_rar_entries = NGBE_RAR_ENTRIES; + /* Set common capability flags and settings */ + adapter->max_q_vectors = NGBE_MAX_MSIX_VECTORS; + + err = wx_get_pcie_msix_counts(wxhw, &msix_count, NGBE_MAX_MSIX_VECTORS); + if (err) + dev_err(&pdev->dev, "Do not support MSI-X\n"); + wxhw->mac.max_msix_vectors = msix_count; + + adapter->mac_table = kcalloc(wxhw->mac.num_rar_entries, + sizeof(struct ngbe_mac_addr), + GFP_KERNEL); + if (!adapter->mac_table) { + dev_err(&pdev->dev, "mac_table allocation failed: %d\n", err); + return -ENOMEM; + } + + if (ngbe_init_rss_key(adapter)) + return -ENOMEM; + + /* enable itr by default in dynamic mode */ + adapter->rx_itr_setting = 1; + adapter->tx_itr_setting = 1; + + /* set default ring sizes */ + adapter->tx_ring_count = NGBE_DEFAULT_TXD; + adapter->rx_ring_count = NGBE_DEFAULT_RXD; + + /* set default work limits */ + adapter->tx_work_limit = NGBE_DEFAULT_TX_WORK; + adapter->rx_work_limit = NGBE_DEFAULT_RX_WORK; + + return 0; +} + +static void ngbe_down(struct ngbe_adapter *adapter) +{ + netif_carrier_off(adapter->netdev); + netif_tx_disable(adapter->netdev); +}; + +/** + * ngbe_open - Called when a network interface is made active + * @netdev: network interface device structure + * + * Returns 0 on success, negative value on failure + * + * The open entry point is called when a network interface is made + * active by the system (IFF_UP). + **/ +static int ngbe_open(struct net_device *netdev) +{ + struct ngbe_adapter *adapter = netdev_priv(netdev); + struct ngbe_hw *hw = &adapter->hw; + struct wx_hw *wxhw = &hw->wxhw; + + wx_control_hw(wxhw, true); + + return 0; +} + +/** + * ngbe_close - Disables a network interface + * @netdev: network interface device structure + * + * Returns 0, this is not allowed to fail + * + * The close entry point is called when an interface is de-activated + * by the OS. The hardware is still under the drivers control, but + * needs to be disabled. A global MAC reset is issued to stop the + * hardware, and all transmit and receive resources are freed. + **/ +static int ngbe_close(struct net_device *netdev) +{ + struct ngbe_adapter *adapter = netdev_priv(netdev); + + ngbe_down(adapter); + wx_control_hw(&adapter->hw.wxhw, false); + + return 0; +} + +static netdev_tx_t ngbe_xmit_frame(struct sk_buff *skb, + struct net_device *netdev) +{ + return NETDEV_TX_OK; +} + +/** + * ngbe_set_mac - Change the Ethernet Address of the NIC + * @netdev: network interface device structure + * @p: pointer to an address structure + * + * Returns 0 on success, negative on failure + **/ +static int ngbe_set_mac(struct net_device *netdev, void *p) +{ + struct ngbe_adapter *adapter = netdev_priv(netdev); + struct wx_hw *wxhw = &adapter->hw.wxhw; + struct sockaddr *addr = p; + + if (!is_valid_ether_addr(addr->sa_data)) + return -EADDRNOTAVAIL; + + eth_hw_addr_set(netdev, addr->sa_data); + memcpy(wxhw->mac.addr, addr->sa_data, netdev->addr_len); + + ngbe_mac_set_default_filter(adapter, wxhw->mac.addr); + + return 0; +} + static void ngbe_dev_shutdown(struct pci_dev *pdev, bool *enable_wake) { struct ngbe_adapter *adapter = pci_get_drvdata(pdev); @@ -41,13 +287,22 @@ static void ngbe_dev_shutdown(struct pci_dev *pdev, bool *enable_wake) netif_device_detach(netdev); + rtnl_lock(); + if (netif_running(netdev)) + ngbe_down(adapter); + rtnl_unlock(); + wx_control_hw(&adapter->hw.wxhw, false); + pci_disable_device(pdev); } static void ngbe_shutdown(struct pci_dev *pdev) { + struct ngbe_adapter *adapter = pci_get_drvdata(pdev); bool wake; + wake = !!adapter->wol; + ngbe_dev_shutdown(pdev, &wake); if (system_state == SYSTEM_POWER_OFF) { @@ -56,6 +311,14 @@ static void ngbe_shutdown(struct pci_dev *pdev) } } +static const struct net_device_ops ngbe_netdev_ops = { + .ndo_open = ngbe_open, + .ndo_stop = ngbe_close, + .ndo_start_xmit = ngbe_xmit_frame, + .ndo_validate_addr = eth_validate_addr, + .ndo_set_mac_address = ngbe_set_mac, +}; + /** * ngbe_probe - Device Initialization Routine * @pdev: PCI device information struct @@ -71,7 +334,14 @@ static int ngbe_probe(struct pci_dev *pdev, const struct pci_device_id __always_unused *ent) { struct ngbe_adapter *adapter = NULL; + struct ngbe_hw *hw = NULL; + struct wx_hw *wxhw = NULL; struct net_device *netdev; + u32 e2rom_cksum_cap = 0; + static int func_nums; + u16 e2rom_ver = 0; + u32 etrack_id = 0; + u32 saved_ver = 0; int err; err = pci_enable_device_mem(pdev); @@ -111,6 +381,9 @@ static int ngbe_probe(struct pci_dev *pdev, adapter = netdev_priv(netdev); adapter->netdev = netdev; adapter->pdev = pdev; + hw = &adapter->hw; + wxhw = &hw->wxhw; + adapter->msg_enable = BIT(3) - 1; adapter->io_addr = devm_ioremap(&pdev->dev, pci_resource_start(pdev, 0), @@ -120,12 +393,101 @@ static int ngbe_probe(struct pci_dev *pdev, goto err_pci_release_regions; } + netdev->netdev_ops = &ngbe_netdev_ops; + netdev->features |= NETIF_F_HIGHDMA; + adapter->bd_number = func_nums; + /* setup the private structure */ + err = ngbe_sw_init(adapter); + if (err) + goto err_free_mac_table; + + /* check if flash load is done after hw power up */ + err = wx_check_flash_load(wxhw, NGBE_SPI_ILDR_STATUS_PERST); + if (err) + goto err_free_mac_table; + err = wx_check_flash_load(wxhw, NGBE_SPI_ILDR_STATUS_PWRRST); + if (err) + goto err_free_mac_table; + + err = wx_mng_present(wxhw); + if (err) { + dev_err(&pdev->dev, "Management capability is not present\n"); + goto err_free_mac_table; + } + + err = ngbe_reset_hw(hw); + if (err) { + dev_err(&pdev->dev, "HW Init failed: %d\n", err); + goto err_free_mac_table; + } + + if (wxhw->bus.func == 0) { + wr32(wxhw, NGBE_CALSUM_CAP_STATUS, 0x0); + wr32(wxhw, NGBE_EEPROM_VERSION_STORE_REG, 0x0); + } else { + e2rom_cksum_cap = rd32(wxhw, NGBE_CALSUM_CAP_STATUS); + saved_ver = rd32(wxhw, NGBE_EEPROM_VERSION_STORE_REG); + } + + wx_init_eeprom_params(wxhw); + if (wxhw->bus.func == 0 || e2rom_cksum_cap == 0) { + /* make sure the EEPROM is ready */ + err = ngbe_eeprom_chksum_hostif(hw); + if (err) { + dev_err(&pdev->dev, "The EEPROM Checksum Is Not Valid\n"); + err = -EIO; + goto err_free_mac_table; + } + } + + adapter->wol = 0; + if (hw->wol_enabled) + adapter->wol = NGBE_PSR_WKUP_CTL_MAG; + + hw->wol_enabled = !!(adapter->wol); + wr32(wxhw, NGBE_PSR_WKUP_CTL, adapter->wol); + + device_set_wakeup_enable(&pdev->dev, adapter->wol); + + /* Save off EEPROM version number and Option Rom version which + * together make a unique identify for the eeprom + */ + if (saved_ver) { + etrack_id = saved_ver; + } else { + wx_read_ee_hostif(wxhw, + wxhw->eeprom.sw_region_offset + NGBE_EEPROM_VERSION_H, + &e2rom_ver); + etrack_id = e2rom_ver << 16; + wx_read_ee_hostif(wxhw, + wxhw->eeprom.sw_region_offset + NGBE_EEPROM_VERSION_L, + &e2rom_ver); + etrack_id |= e2rom_ver; + wr32(wxhw, NGBE_EEPROM_VERSION_STORE_REG, etrack_id); + } + + eth_hw_addr_set(netdev, wxhw->mac.perm_addr); + ngbe_mac_set_default_filter(adapter, wxhw->mac.perm_addr); + + err = register_netdev(netdev); + if (err) + goto err_register; + pci_set_drvdata(pdev, adapter); + netif_info(adapter, probe, netdev, + "PHY: %s, PBA No: Wang Xun GbE Family Controller\n", + hw->phy.type == ngbe_phy_internal ? "Internal" : "External"); + netif_info(adapter, probe, netdev, "%pM\n", netdev->dev_addr); + return 0; +err_register: + wx_control_hw(wxhw, false); +err_free_mac_table: + kfree(adapter->mac_table); err_pci_release_regions: pci_disable_pcie_error_reporting(pdev); pci_release_selected_regions(pdev, @@ -146,9 +508,15 @@ err_pci_disable_dev: **/ static void ngbe_remove(struct pci_dev *pdev) { + struct ngbe_adapter *adapter = pci_get_drvdata(pdev); + struct net_device *netdev; + + netdev = adapter->netdev; + unregister_netdev(netdev); pci_release_selected_regions(pdev, pci_select_bars(pdev, IORESOURCE_MEM)); + kfree(adapter->mac_table); pci_disable_pcie_error_reporting(pdev); pci_disable_device(pdev); diff --git a/drivers/net/ethernet/wangxun/ngbe/ngbe_type.h b/drivers/net/ethernet/wangxun/ngbe/ngbe_type.h index 26e776c3539a..39f6c03f1a54 100644 --- a/drivers/net/ethernet/wangxun/ngbe/ngbe_type.h +++ b/drivers/net/ethernet/wangxun/ngbe/ngbe_type.h @@ -8,11 +8,6 @@ #include /************ NGBE_register.h ************/ -/* Vendor ID */ -#ifndef PCI_VENDOR_ID_WANGXUN -#define PCI_VENDOR_ID_WANGXUN 0x8088 -#endif - /* Device IDs */ #define NGBE_DEV_ID_EM_WX1860AL_W 0x0100 #define NGBE_DEV_ID_EM_WX1860A2 0x0101 @@ -47,4 +42,98 @@ #define NGBE_WOL_SUP 0x4000 #define NGBE_WOL_MASK 0x4000 +/**************** EM Registers ****************************/ +/* chip control Registers */ +#define NGBE_MIS_PRB_CTL 0x10010 +/* FMGR Registers */ +#define NGBE_SPI_ILDR_STATUS 0x10120 +#define NGBE_SPI_ILDR_STATUS_PERST BIT(0) /* PCIE_PERST is done */ +#define NGBE_SPI_ILDR_STATUS_PWRRST BIT(1) /* Power on reset is done */ +#define NGBE_SPI_ILDR_STATUS_LAN_SW_RST(_i) BIT((_i) + 9) /* lan soft reset done */ + +/* Checksum and EEPROM pointers */ +#define NGBE_CALSUM_COMMAND 0xE9 +#define NGBE_CALSUM_CAP_STATUS 0x10224 +#define NGBE_EEPROM_VERSION_STORE_REG 0x1022C +#define NGBE_SAN_MAC_ADDR_PTR 0x18 +#define NGBE_DEVICE_CAPS 0x1C +#define NGBE_EEPROM_VERSION_L 0x1D +#define NGBE_EEPROM_VERSION_H 0x1E + +/* Media-dependent registers. */ +#define NGBE_MDIO_CLAUSE_SELECT 0x11220 + +/* GPIO Registers */ +#define NGBE_GPIO_DR 0x14800 +#define NGBE_GPIO_DDR 0x14804 +/*GPIO bit */ +#define NGBE_GPIO_DR_0 BIT(0) /* SDP0 Data Value */ +#define NGBE_GPIO_DR_1 BIT(1) /* SDP1 Data Value */ +#define NGBE_GPIO_DDR_0 BIT(0) /* SDP0 IO direction */ +#define NGBE_GPIO_DDR_1 BIT(1) /* SDP1 IO direction */ + +/* Wake up registers */ +#define NGBE_PSR_WKUP_CTL 0x15B80 +/* Wake Up Filter Control Bit */ +#define NGBE_PSR_WKUP_CTL_LNKC BIT(0) /* Link Status Change Wakeup Enable*/ +#define NGBE_PSR_WKUP_CTL_MAG BIT(1) /* Magic Packet Wakeup Enable */ +#define NGBE_PSR_WKUP_CTL_EX BIT(2) /* Directed Exact Wakeup Enable */ +#define NGBE_PSR_WKUP_CTL_MC BIT(3) /* Directed Multicast Wakeup Enable*/ +#define NGBE_PSR_WKUP_CTL_BC BIT(4) /* Broadcast Wakeup Enable */ +#define NGBE_PSR_WKUP_CTL_ARP BIT(5) /* ARP Request Packet Wakeup Enable*/ +#define NGBE_PSR_WKUP_CTL_IPV4 BIT(6) /* Directed IPv4 Pkt Wakeup Enable */ +#define NGBE_PSR_WKUP_CTL_IPV6 BIT(7) /* Directed IPv6 Pkt Wakeup Enable */ + +#define NGBE_FW_EEPROM_CHECKSUM_CMD 0xE9 +#define NGBE_FW_NVM_DATA_OFFSET 3 +#define NGBE_FW_CMD_DEFAULT_CHECKSUM 0xFF /* checksum always 0xFF */ +#define NGBE_FW_CMD_ST_PASS 0x80658383 +#define NGBE_FW_CMD_ST_FAIL 0x70657376 + +enum ngbe_phy_type { + ngbe_phy_unknown = 0, + ngbe_phy_none, + ngbe_phy_internal, + ngbe_phy_m88e1512, + ngbe_phy_m88e1512_sfi, + ngbe_phy_m88e1512_unknown, + ngbe_phy_yt8521s, + ngbe_phy_yt8521s_sfi, + ngbe_phy_internal_yt8521s_sfi, + ngbe_phy_generic +}; + +enum ngbe_media_type { + ngbe_media_type_unknown = 0, + ngbe_media_type_fiber, + ngbe_media_type_copper, + ngbe_media_type_backplane, +}; + +enum ngbe_mac_type { + ngbe_mac_type_unknown = 0, + ngbe_mac_type_mdi, + ngbe_mac_type_rgmii +}; + +struct ngbe_phy_info { + enum ngbe_phy_type type; + enum ngbe_media_type media_type; + + u32 addr; + u32 id; + + bool reset_if_overtemp; + +}; + +struct ngbe_hw { + struct wx_hw wxhw; + struct ngbe_phy_info phy; + enum ngbe_mac_type mac_type; + + bool wol_enabled; + bool ncsi_enabled; + bool gpio_ctrl; +}; #endif /* _NGBE_TYPE_H_ */ From f9edd82774c0c8dde1ce26637a9e29f9bba92fef Mon Sep 17 00:00:00 2001 From: Yoshihiro Shimoda Date: Mon, 31 Oct 2022 21:32:40 +0900 Subject: [PATCH 0536/2002] dt-bindings: net: renesas: Document Renesas Ethernet Switch Document Renesas Etherent Switch for R-Car S4-8 (r8a779f0). Signed-off-by: Yoshihiro Shimoda Reviewed-by: Krzysztof Kozlowski Reviewed-by: Andrew Lunn Signed-off-by: David S. Miller --- .../net/renesas,r8a779f0-ether-switch.yaml | 262 ++++++++++++++++++ 1 file changed, 262 insertions(+) create mode 100644 Documentation/devicetree/bindings/net/renesas,r8a779f0-ether-switch.yaml diff --git a/Documentation/devicetree/bindings/net/renesas,r8a779f0-ether-switch.yaml b/Documentation/devicetree/bindings/net/renesas,r8a779f0-ether-switch.yaml new file mode 100644 index 000000000000..e933a1e48d67 --- /dev/null +++ b/Documentation/devicetree/bindings/net/renesas,r8a779f0-ether-switch.yaml @@ -0,0 +1,262 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/net/renesas,r8a779f0-ether-switch.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Renesas Ethernet Switch + +maintainers: + - Yoshihiro Shimoda + +properties: + compatible: + const: renesas,r8a779f0-ether-switch + + reg: + maxItems: 2 + + reg-names: + items: + - const: base + - const: secure_base + + interrupts: + maxItems: 47 + + interrupt-names: + items: + - const: mfwd_error + - const: race_error + - const: coma_error + - const: gwca0_error + - const: gwca1_error + - const: etha0_error + - const: etha1_error + - const: etha2_error + - const: gptp0_status + - const: gptp1_status + - const: mfwd_status + - const: race_status + - const: coma_status + - const: gwca0_status + - const: gwca1_status + - const: etha0_status + - const: etha1_status + - const: etha2_status + - const: rmac0_status + - const: rmac1_status + - const: rmac2_status + - const: gwca0_rxtx0 + - const: gwca0_rxtx1 + - const: gwca0_rxtx2 + - const: gwca0_rxtx3 + - const: gwca0_rxtx4 + - const: gwca0_rxtx5 + - const: gwca0_rxtx6 + - const: gwca0_rxtx7 + - const: gwca1_rxtx0 + - const: gwca1_rxtx1 + - const: gwca1_rxtx2 + - const: gwca1_rxtx3 + - const: gwca1_rxtx4 + - const: gwca1_rxtx5 + - const: gwca1_rxtx6 + - const: gwca1_rxtx7 + - const: gwca0_rxts0 + - const: gwca0_rxts1 + - const: gwca1_rxts0 + - const: gwca1_rxts1 + - const: rmac0_mdio + - const: rmac1_mdio + - const: rmac2_mdio + - const: rmac0_phy + - const: rmac1_phy + - const: rmac2_phy + + clocks: + maxItems: 1 + + resets: + maxItems: 1 + + iommus: + maxItems: 16 + + power-domains: + maxItems: 1 + + ethernet-ports: + type: object + additionalProperties: false + + properties: + '#address-cells': + description: Port number of ETHA (TSNA). + const: 1 + + '#size-cells': + const: 0 + + patternProperties: + "^port@[0-9a-f]+$": + type: object + $ref: /schemas/net/ethernet-controller.yaml# + unevaluatedProperties: false + + properties: + reg: + maxItems: 1 + description: + Port number of ETHA (TSNA). + + phys: + maxItems: 1 + description: + Phandle of an Ethernet SERDES. + + mdio: + $ref: /schemas/net/mdio.yaml# + unevaluatedProperties: false + + required: + - reg + - phy-handle + - phy-mode + - phys + - mdio + +required: + - compatible + - reg + - reg-names + - interrupts + - interrupt-names + - clocks + - resets + - power-domains + - ethernet-ports + +additionalProperties: false + +examples: + - | + #include + #include + #include + + ethernet@e6880000 { + compatible = "renesas,r8a779f0-ether-switch"; + reg = <0xe6880000 0x20000>, <0xe68c0000 0x20000>; + reg-names = "base", "secure_base"; + interrupts = , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + ; + interrupt-names = "mfwd_error", "race_error", + "coma_error", "gwca0_error", + "gwca1_error", "etha0_error", + "etha1_error", "etha2_error", + "gptp0_status", "gptp1_status", + "mfwd_status", "race_status", + "coma_status", "gwca0_status", + "gwca1_status", "etha0_status", + "etha1_status", "etha2_status", + "rmac0_status", "rmac1_status", + "rmac2_status", + "gwca0_rxtx0", "gwca0_rxtx1", + "gwca0_rxtx2", "gwca0_rxtx3", + "gwca0_rxtx4", "gwca0_rxtx5", + "gwca0_rxtx6", "gwca0_rxtx7", + "gwca1_rxtx0", "gwca1_rxtx1", + "gwca1_rxtx2", "gwca1_rxtx3", + "gwca1_rxtx4", "gwca1_rxtx5", + "gwca1_rxtx6", "gwca1_rxtx7", + "gwca0_rxts0", "gwca0_rxts1", + "gwca1_rxts0", "gwca1_rxts1", + "rmac0_mdio", "rmac1_mdio", + "rmac2_mdio", + "rmac0_phy", "rmac1_phy", + "rmac2_phy"; + clocks = <&cpg CPG_MOD 1505>; + power-domains = <&sysc R8A779F0_PD_ALWAYS_ON>; + resets = <&cpg 1505>; + + ethernet-ports { + #address-cells = <1>; + #size-cells = <0>; + port@0 { + reg = <0>; + phy-handle = <ð_phy0>; + phy-mode = "sgmii"; + phys = <ð_serdes 0>; + mdio { + #address-cells = <1>; + #size-cells = <0>; + }; + }; + port@1 { + reg = <1>; + phy-handle = <ð_phy1>; + phy-mode = "sgmii"; + phys = <ð_serdes 1>; + mdio { + #address-cells = <1>; + #size-cells = <0>; + }; + }; + port@2 { + reg = <2>; + phy-handle = <ð_phy2>; + phy-mode = "sgmii"; + phys = <ð_serdes 2>; + mdio { + #address-cells = <1>; + #size-cells = <0>; + }; + }; + }; + }; From 3590918b5d07aa5e4702f753c375adb4868d3abd Mon Sep 17 00:00:00 2001 From: Yoshihiro Shimoda Date: Mon, 31 Oct 2022 21:32:41 +0900 Subject: [PATCH 0537/2002] net: ethernet: renesas: Add support for "Ethernet Switch" Add initial support for Renesas "Ethernet Switch" device of R-Car S4-8. The hardware has features about forwarding for an ethernet switch device. But, for now, it acts as ethernet controllers so that any forwarding offload features are not supported. So, any switchdev header files and DSA framework are not used. Signed-off-by: Yoshihiro Shimoda Reviewed-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/ethernet/renesas/Kconfig | 11 + drivers/net/ethernet/renesas/Makefile | 4 + drivers/net/ethernet/renesas/rswitch.c | 1699 ++++++++++++++++++++++++ drivers/net/ethernet/renesas/rswitch.h | 972 ++++++++++++++ 4 files changed, 2686 insertions(+) create mode 100644 drivers/net/ethernet/renesas/rswitch.c create mode 100644 drivers/net/ethernet/renesas/rswitch.h diff --git a/drivers/net/ethernet/renesas/Kconfig b/drivers/net/ethernet/renesas/Kconfig index 8008b2f45934..7a5e26b6ea9b 100644 --- a/drivers/net/ethernet/renesas/Kconfig +++ b/drivers/net/ethernet/renesas/Kconfig @@ -42,4 +42,15 @@ config RAVB This driver supports the following SoCs: - R8A779x. +config RENESAS_ETHER_SWITCH + tristate "Renesas Ethernet Switch support" + depends on ARCH_RENESAS || COMPILE_TEST + select CRC32 + select MII + select PHYLINK + help + Renesas Ethernet Switch device driver. + This driver supports the following SoCs: + - R8A779Fx. + endif # NET_VENDOR_RENESAS diff --git a/drivers/net/ethernet/renesas/Makefile b/drivers/net/ethernet/renesas/Makefile index f21ab8c02af0..32845ba53970 100644 --- a/drivers/net/ethernet/renesas/Makefile +++ b/drivers/net/ethernet/renesas/Makefile @@ -8,3 +8,7 @@ obj-$(CONFIG_SH_ETH) += sh_eth.o ravb-objs := ravb_main.o ravb_ptp.o obj-$(CONFIG_RAVB) += ravb.o + +rswitch_drv-objs := rswitch.o + +obj-$(CONFIG_RENESAS_ETHER_SWITCH) += rswitch_drv.o diff --git a/drivers/net/ethernet/renesas/rswitch.c b/drivers/net/ethernet/renesas/rswitch.c new file mode 100644 index 000000000000..5ca38e6a9e97 --- /dev/null +++ b/drivers/net/ethernet/renesas/rswitch.c @@ -0,0 +1,1699 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Renesas Ethernet Switch device driver + * + * Copyright (C) 2022 Renesas Electronics Corporation + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "rswitch.h" + +static int rswitch_reg_wait(void __iomem *addr, u32 offs, u32 mask, u32 expected) +{ + u32 val; + + return readl_poll_timeout_atomic(addr + offs, val, (val & mask) == expected, + 1, RSWITCH_TIMEOUT_US); +} + +static void rswitch_modify(void __iomem *addr, enum rswitch_reg reg, u32 clear, u32 set) +{ + iowrite32((ioread32(addr + reg) & ~clear) | set, addr + reg); +} + +/* Common Agent block (COMA) */ +static void rswitch_reset(struct rswitch_private *priv) +{ + iowrite32(RRC_RR, priv->addr + RRC); + iowrite32(RRC_RR_CLR, priv->addr + RRC); +} + +static void rswitch_clock_enable(struct rswitch_private *priv) +{ + iowrite32(RCEC_ACE_DEFAULT | RCEC_RCE, priv->addr + RCEC); +} + +static void rswitch_clock_disable(struct rswitch_private *priv) +{ + iowrite32(RCDC_RCD, priv->addr + RCDC); +} + +static bool rswitch_agent_clock_is_enabled(void __iomem *coma_addr, int port) +{ + u32 val = ioread32(coma_addr + RCEC); + + if (val & RCEC_RCE) + return (val & BIT(port)) ? true : false; + else + return false; +} + +static void rswitch_agent_clock_ctrl(void __iomem *coma_addr, int port, int enable) +{ + u32 val; + + if (enable) { + val = ioread32(coma_addr + RCEC); + iowrite32(val | RCEC_RCE | BIT(port), coma_addr + RCEC); + } else { + val = ioread32(coma_addr + RCDC); + iowrite32(val | BIT(port), coma_addr + RCDC); + } +} + +static int rswitch_bpool_config(struct rswitch_private *priv) +{ + u32 val; + + val = ioread32(priv->addr + CABPIRM); + if (val & CABPIRM_BPR) + return 0; + + iowrite32(CABPIRM_BPIOG, priv->addr + CABPIRM); + + return rswitch_reg_wait(priv->addr, CABPIRM, CABPIRM_BPR, CABPIRM_BPR); +} + +/* R-Switch-2 block (TOP) */ +static void rswitch_top_init(struct rswitch_private *priv) +{ + int i; + + for (i = 0; i < RSWITCH_MAX_NUM_QUEUES; i++) + iowrite32((i / 16) << (GWCA_INDEX * 8), priv->addr + TPEMIMC7(i)); +} + +/* Forwarding engine block (MFWD) */ +static void rswitch_fwd_init(struct rswitch_private *priv) +{ + int i; + + /* For ETHA */ + for (i = 0; i < RSWITCH_NUM_PORTS; i++) { + iowrite32(FWPC0_DEFAULT, priv->addr + FWPC0(i)); + iowrite32(0, priv->addr + FWPBFC(i)); + } + + for (i = 0; i < RSWITCH_NUM_PORTS; i++) { + iowrite32(priv->rdev[i]->rx_queue->index, + priv->addr + FWPBFCSDC(GWCA_INDEX, i)); + iowrite32(BIT(priv->gwca.index), priv->addr + FWPBFC(i)); + } + + /* For GWCA */ + iowrite32(FWPC0_DEFAULT, priv->addr + FWPC0(priv->gwca.index)); + iowrite32(FWPC1_DDE, priv->addr + FWPC1(priv->gwca.index)); + iowrite32(0, priv->addr + FWPBFC(priv->gwca.index)); + iowrite32(GENMASK(RSWITCH_NUM_PORTS - 1, 0), priv->addr + FWPBFC(priv->gwca.index)); +} + +/* Gateway CPU agent block (GWCA) */ +static int rswitch_gwca_change_mode(struct rswitch_private *priv, + enum rswitch_gwca_mode mode) +{ + int ret; + + if (!rswitch_agent_clock_is_enabled(priv->addr, priv->gwca.index)) + rswitch_agent_clock_ctrl(priv->addr, priv->gwca.index, 1); + + iowrite32(mode, priv->addr + GWMC); + + ret = rswitch_reg_wait(priv->addr, GWMS, GWMS_OPS_MASK, mode); + + if (mode == GWMC_OPC_DISABLE) + rswitch_agent_clock_ctrl(priv->addr, priv->gwca.index, 0); + + return ret; +} + +static int rswitch_gwca_mcast_table_reset(struct rswitch_private *priv) +{ + iowrite32(GWMTIRM_MTIOG, priv->addr + GWMTIRM); + + return rswitch_reg_wait(priv->addr, GWMTIRM, GWMTIRM_MTR, GWMTIRM_MTR); +} + +static int rswitch_gwca_axi_ram_reset(struct rswitch_private *priv) +{ + iowrite32(GWARIRM_ARIOG, priv->addr + GWARIRM); + + return rswitch_reg_wait(priv->addr, GWARIRM, GWARIRM_ARR, GWARIRM_ARR); +} + +static void rswitch_gwca_set_rate_limit(struct rswitch_private *priv, int rate) +{ + u32 gwgrlulc, gwgrlc; + + switch (rate) { + case 1000: + gwgrlulc = 0x0000005f; + gwgrlc = 0x00010260; + break; + default: + dev_err(&priv->pdev->dev, "%s: This rate is not supported (%d)\n", __func__, rate); + return; + } + + iowrite32(gwgrlulc, priv->addr + GWGRLULC); + iowrite32(gwgrlc, priv->addr + GWGRLC); +} + +static bool rswitch_is_any_data_irq(struct rswitch_private *priv, u32 *dis, bool tx) +{ + u32 *mask = tx ? priv->gwca.tx_irq_bits : priv->gwca.rx_irq_bits; + int i; + + for (i = 0; i < RSWITCH_NUM_IRQ_REGS; i++) { + if (dis[i] & mask[i]) + return true; + } + + return false; +} + +static void rswitch_get_data_irq_status(struct rswitch_private *priv, u32 *dis) +{ + int i; + + for (i = 0; i < RSWITCH_NUM_IRQ_REGS; i++) { + dis[i] = ioread32(priv->addr + GWDIS(i)); + dis[i] &= ioread32(priv->addr + GWDIE(i)); + } +} + +static void rswitch_enadis_data_irq(struct rswitch_private *priv, int index, bool enable) +{ + u32 offs = enable ? GWDIE(index / 32) : GWDID(index / 32); + + iowrite32(BIT(index % 32), priv->addr + offs); +} + +static void rswitch_ack_data_irq(struct rswitch_private *priv, int index) +{ + u32 offs = GWDIS(index / 32); + + iowrite32(BIT(index % 32), priv->addr + offs); +} + +static u32 rswitch_next_queue_index(struct rswitch_gwca_queue *gq, bool cur, u32 num) +{ + u32 index = cur ? gq->cur : gq->dirty; + + if (index + num >= gq->ring_size) + index = (index + num) % gq->ring_size; + else + index += num; + + return index; +} + +static u32 rswitch_get_num_cur_queues(struct rswitch_gwca_queue *gq) +{ + if (gq->cur >= gq->dirty) + return gq->cur - gq->dirty; + else + return gq->ring_size - gq->dirty + gq->cur; +} + +static bool rswitch_is_queue_rxed(struct rswitch_gwca_queue *gq) +{ + struct rswitch_ext_ts_desc *desc = &gq->ts_ring[gq->dirty]; + + if ((desc->desc.die_dt & DT_MASK) != DT_FEMPTY) + return true; + + return false; +} + +static int rswitch_gwca_queue_alloc_skb(struct rswitch_gwca_queue *gq, + u32 start_index, u32 num) +{ + u32 i, index; + + for (i = 0; i < num; i++) { + index = (i + start_index) % gq->ring_size; + if (gq->skbs[index]) + continue; + gq->skbs[index] = netdev_alloc_skb_ip_align(gq->ndev, + PKT_BUF_SZ + RSWITCH_ALIGN - 1); + if (!gq->skbs[index]) + goto err; + } + + return 0; + +err: + for (i--; i >= 0; i--) { + index = (i + start_index) % gq->ring_size; + dev_kfree_skb(gq->skbs[index]); + gq->skbs[index] = NULL; + } + + return -ENOMEM; +} + +static void rswitch_gwca_queue_free(struct net_device *ndev, + struct rswitch_gwca_queue *gq) +{ + int i; + + if (gq->gptp) { + dma_free_coherent(ndev->dev.parent, + sizeof(struct rswitch_ext_ts_desc) * + (gq->ring_size + 1), gq->ts_ring, gq->ring_dma); + gq->ts_ring = NULL; + } else { + dma_free_coherent(ndev->dev.parent, + sizeof(struct rswitch_ext_desc) * + (gq->ring_size + 1), gq->ring, gq->ring_dma); + gq->ring = NULL; + } + + if (!gq->dir_tx) { + for (i = 0; i < gq->ring_size; i++) + dev_kfree_skb(gq->skbs[i]); + } + + kfree(gq->skbs); + gq->skbs = NULL; +} + +static int rswitch_gwca_queue_alloc(struct net_device *ndev, + struct rswitch_private *priv, + struct rswitch_gwca_queue *gq, + bool dir_tx, bool gptp, int ring_size) +{ + int i, bit; + + gq->dir_tx = dir_tx; + gq->gptp = gptp; + gq->ring_size = ring_size; + gq->ndev = ndev; + + gq->skbs = kcalloc(gq->ring_size, sizeof(*gq->skbs), GFP_KERNEL); + if (!gq->skbs) + return -ENOMEM; + + if (!dir_tx) + rswitch_gwca_queue_alloc_skb(gq, 0, gq->ring_size); + + if (gptp) + gq->ts_ring = dma_alloc_coherent(ndev->dev.parent, + sizeof(struct rswitch_ext_ts_desc) * + (gq->ring_size + 1), &gq->ring_dma, GFP_KERNEL); + else + gq->ring = dma_alloc_coherent(ndev->dev.parent, + sizeof(struct rswitch_ext_desc) * + (gq->ring_size + 1), &gq->ring_dma, GFP_KERNEL); + if (!gq->ts_ring && !gq->ring) + goto out; + + i = gq->index / 32; + bit = BIT(gq->index % 32); + if (dir_tx) + priv->gwca.tx_irq_bits[i] |= bit; + else + priv->gwca.rx_irq_bits[i] |= bit; + + return 0; + +out: + rswitch_gwca_queue_free(ndev, gq); + + return -ENOMEM; +} + +static void rswitch_desc_set_dptr(struct rswitch_desc *desc, dma_addr_t addr) +{ + desc->dptrl = cpu_to_le32(lower_32_bits(addr)); + desc->dptrh = upper_32_bits(addr) & 0xff; +} + +static dma_addr_t rswitch_desc_get_dptr(const struct rswitch_desc *desc) +{ + return __le32_to_cpu(desc->dptrl) | (u64)(desc->dptrh) << 32; +} + +static int rswitch_gwca_queue_format(struct net_device *ndev, + struct rswitch_private *priv, + struct rswitch_gwca_queue *gq) +{ + int tx_ring_size = sizeof(struct rswitch_ext_desc) * gq->ring_size; + struct rswitch_ext_desc *desc; + struct rswitch_desc *linkfix; + dma_addr_t dma_addr; + int i; + + memset(gq->ring, 0, tx_ring_size); + for (i = 0, desc = gq->ring; i < gq->ring_size; i++, desc++) { + if (!gq->dir_tx) { + dma_addr = dma_map_single(ndev->dev.parent, + gq->skbs[i]->data, PKT_BUF_SZ, + DMA_FROM_DEVICE); + if (dma_mapping_error(ndev->dev.parent, dma_addr)) + goto err; + + desc->desc.info_ds = cpu_to_le16(PKT_BUF_SZ); + rswitch_desc_set_dptr(&desc->desc, dma_addr); + desc->desc.die_dt = DT_FEMPTY | DIE; + } else { + desc->desc.die_dt = DT_EEMPTY | DIE; + } + } + rswitch_desc_set_dptr(&desc->desc, gq->ring_dma); + desc->desc.die_dt = DT_LINKFIX; + + linkfix = &priv->linkfix_table[gq->index]; + linkfix->die_dt = DT_LINKFIX; + rswitch_desc_set_dptr(linkfix, gq->ring_dma); + + iowrite32(GWDCC_BALR | (gq->dir_tx ? GWDCC_DQT : 0) | GWDCC_EDE, + priv->addr + GWDCC_OFFS(gq->index)); + + return 0; + +err: + if (!gq->dir_tx) { + for (i--, desc = gq->ring; i >= 0; i--, desc++) { + dma_addr = rswitch_desc_get_dptr(&desc->desc); + dma_unmap_single(ndev->dev.parent, dma_addr, PKT_BUF_SZ, + DMA_FROM_DEVICE); + } + } + + return -ENOMEM; +} + +static int rswitch_gwca_queue_ts_fill(struct net_device *ndev, + struct rswitch_gwca_queue *gq, + u32 start_index, u32 num) +{ + struct rswitch_device *rdev = netdev_priv(ndev); + struct rswitch_ext_ts_desc *desc; + dma_addr_t dma_addr; + u32 i, index; + + for (i = 0; i < num; i++) { + index = (i + start_index) % gq->ring_size; + desc = &gq->ts_ring[index]; + if (!gq->dir_tx) { + dma_addr = dma_map_single(ndev->dev.parent, + gq->skbs[index]->data, PKT_BUF_SZ, + DMA_FROM_DEVICE); + if (dma_mapping_error(ndev->dev.parent, dma_addr)) + goto err; + + desc->desc.info_ds = cpu_to_le16(PKT_BUF_SZ); + rswitch_desc_set_dptr(&desc->desc, dma_addr); + dma_wmb(); + desc->desc.die_dt = DT_FEMPTY | DIE; + desc->info1 = cpu_to_le64(INFO1_SPN(rdev->etha->index)); + } else { + desc->desc.die_dt = DT_EEMPTY | DIE; + } + } + + return 0; + +err: + if (!gq->dir_tx) { + for (i--; i >= 0; i--) { + index = (i + start_index) % gq->ring_size; + desc = &gq->ts_ring[index]; + dma_addr = rswitch_desc_get_dptr(&desc->desc); + dma_unmap_single(ndev->dev.parent, dma_addr, PKT_BUF_SZ, + DMA_FROM_DEVICE); + } + } + + return -ENOMEM; +} + +static int rswitch_gwca_queue_ts_format(struct net_device *ndev, + struct rswitch_private *priv, + struct rswitch_gwca_queue *gq) +{ + int tx_ts_ring_size = sizeof(struct rswitch_ext_ts_desc) * gq->ring_size; + struct rswitch_ext_ts_desc *desc; + struct rswitch_desc *linkfix; + int err; + + memset(gq->ts_ring, 0, tx_ts_ring_size); + err = rswitch_gwca_queue_ts_fill(ndev, gq, 0, gq->ring_size); + if (err < 0) + return err; + + desc = &gq->ts_ring[gq->ring_size]; /* Last */ + rswitch_desc_set_dptr(&desc->desc, gq->ring_dma); + desc->desc.die_dt = DT_LINKFIX; + + linkfix = &priv->linkfix_table[gq->index]; + linkfix->die_dt = DT_LINKFIX; + rswitch_desc_set_dptr(linkfix, gq->ring_dma); + + iowrite32(GWDCC_BALR | (gq->dir_tx ? GWDCC_DQT : 0) | GWDCC_ETS | GWDCC_EDE, + priv->addr + GWDCC_OFFS(gq->index)); + + return 0; +} + +static int rswitch_gwca_desc_alloc(struct rswitch_private *priv) +{ + int i, num_queues = priv->gwca.num_queues; + struct device *dev = &priv->pdev->dev; + + priv->linkfix_table_size = sizeof(struct rswitch_desc) * num_queues; + priv->linkfix_table = dma_alloc_coherent(dev, priv->linkfix_table_size, + &priv->linkfix_table_dma, GFP_KERNEL); + if (!priv->linkfix_table) + return -ENOMEM; + for (i = 0; i < num_queues; i++) + priv->linkfix_table[i].die_dt = DT_EOS; + + return 0; +} + +static void rswitch_gwca_desc_free(struct rswitch_private *priv) +{ + if (priv->linkfix_table) + dma_free_coherent(&priv->pdev->dev, priv->linkfix_table_size, + priv->linkfix_table, priv->linkfix_table_dma); + priv->linkfix_table = NULL; +} + +static struct rswitch_gwca_queue *rswitch_gwca_get(struct rswitch_private *priv) +{ + struct rswitch_gwca_queue *gq; + int index; + + index = find_first_zero_bit(priv->gwca.used, priv->gwca.num_queues); + if (index >= priv->gwca.num_queues) + return NULL; + set_bit(index, priv->gwca.used); + gq = &priv->gwca.queues[index]; + memset(gq, 0, sizeof(*gq)); + gq->index = index; + + return gq; +} + +static void rswitch_gwca_put(struct rswitch_private *priv, + struct rswitch_gwca_queue *gq) +{ + clear_bit(gq->index, priv->gwca.used); +} + +static int rswitch_txdmac_alloc(struct net_device *ndev) +{ + struct rswitch_device *rdev = netdev_priv(ndev); + struct rswitch_private *priv = rdev->priv; + int err; + + rdev->tx_queue = rswitch_gwca_get(priv); + if (!rdev->tx_queue) + return -EBUSY; + + err = rswitch_gwca_queue_alloc(ndev, priv, rdev->tx_queue, true, false, + TX_RING_SIZE); + if (err < 0) { + rswitch_gwca_put(priv, rdev->tx_queue); + return err; + } + + return 0; +} + +static void rswitch_txdmac_free(struct net_device *ndev) +{ + struct rswitch_device *rdev = netdev_priv(ndev); + + rswitch_gwca_queue_free(ndev, rdev->tx_queue); + rswitch_gwca_put(rdev->priv, rdev->tx_queue); +} + +static int rswitch_txdmac_init(struct rswitch_private *priv, int index) +{ + struct rswitch_device *rdev = priv->rdev[index]; + + return rswitch_gwca_queue_format(rdev->ndev, priv, rdev->tx_queue); +} + +static int rswitch_rxdmac_alloc(struct net_device *ndev) +{ + struct rswitch_device *rdev = netdev_priv(ndev); + struct rswitch_private *priv = rdev->priv; + int err; + + rdev->rx_queue = rswitch_gwca_get(priv); + if (!rdev->rx_queue) + return -EBUSY; + + err = rswitch_gwca_queue_alloc(ndev, priv, rdev->rx_queue, false, true, + RX_RING_SIZE); + if (err < 0) { + rswitch_gwca_put(priv, rdev->rx_queue); + return err; + } + + return 0; +} + +static void rswitch_rxdmac_free(struct net_device *ndev) +{ + struct rswitch_device *rdev = netdev_priv(ndev); + + rswitch_gwca_queue_free(ndev, rdev->rx_queue); + rswitch_gwca_put(rdev->priv, rdev->rx_queue); +} + +static int rswitch_rxdmac_init(struct rswitch_private *priv, int index) +{ + struct rswitch_device *rdev = priv->rdev[index]; + struct net_device *ndev = rdev->ndev; + + return rswitch_gwca_queue_ts_format(ndev, priv, rdev->rx_queue); +} + +static int rswitch_gwca_hw_init(struct rswitch_private *priv) +{ + int i, err; + + err = rswitch_gwca_change_mode(priv, GWMC_OPC_DISABLE); + if (err < 0) + return err; + err = rswitch_gwca_change_mode(priv, GWMC_OPC_CONFIG); + if (err < 0) + return err; + + err = rswitch_gwca_mcast_table_reset(priv); + if (err < 0) + return err; + err = rswitch_gwca_axi_ram_reset(priv); + if (err < 0) + return err; + + iowrite32(GWVCC_VEM_SC_TAG, priv->addr + GWVCC); + iowrite32(0, priv->addr + GWTTFC); + iowrite32(lower_32_bits(priv->linkfix_table_dma), priv->addr + GWDCBAC1); + iowrite32(upper_32_bits(priv->linkfix_table_dma), priv->addr + GWDCBAC0); + rswitch_gwca_set_rate_limit(priv, priv->gwca.speed); + + for (i = 0; i < RSWITCH_NUM_PORTS; i++) { + err = rswitch_rxdmac_init(priv, i); + if (err < 0) + return err; + err = rswitch_txdmac_init(priv, i); + if (err < 0) + return err; + } + + err = rswitch_gwca_change_mode(priv, GWMC_OPC_DISABLE); + if (err < 0) + return err; + return rswitch_gwca_change_mode(priv, GWMC_OPC_OPERATION); +} + +static int rswitch_gwca_hw_deinit(struct rswitch_private *priv) +{ + int err; + + err = rswitch_gwca_change_mode(priv, GWMC_OPC_DISABLE); + if (err < 0) + return err; + err = rswitch_gwca_change_mode(priv, GWMC_OPC_RESET); + if (err < 0) + return err; + + return rswitch_gwca_change_mode(priv, GWMC_OPC_DISABLE); +} + +static int rswitch_gwca_halt(struct rswitch_private *priv) +{ + int err; + + priv->gwca_halt = true; + err = rswitch_gwca_hw_deinit(priv); + dev_err(&priv->pdev->dev, "halted (%d)\n", err); + + return err; +} + +static bool rswitch_rx(struct net_device *ndev, int *quota) +{ + struct rswitch_device *rdev = netdev_priv(ndev); + struct rswitch_gwca_queue *gq = rdev->rx_queue; + struct rswitch_ext_ts_desc *desc; + int limit, boguscnt, num, ret; + struct sk_buff *skb; + dma_addr_t dma_addr; + u16 pkt_len; + + boguscnt = min_t(int, gq->ring_size, *quota); + limit = boguscnt; + + desc = &gq->ts_ring[gq->cur]; + while ((desc->desc.die_dt & DT_MASK) != DT_FEMPTY) { + if (--boguscnt < 0) + break; + dma_rmb(); + pkt_len = le16_to_cpu(desc->desc.info_ds) & RX_DS; + skb = gq->skbs[gq->cur]; + gq->skbs[gq->cur] = NULL; + dma_addr = rswitch_desc_get_dptr(&desc->desc); + dma_unmap_single(ndev->dev.parent, dma_addr, PKT_BUF_SZ, DMA_FROM_DEVICE); + skb_put(skb, pkt_len); + skb->protocol = eth_type_trans(skb, ndev); + netif_receive_skb(skb); + rdev->ndev->stats.rx_packets++; + rdev->ndev->stats.rx_bytes += pkt_len; + + gq->cur = rswitch_next_queue_index(gq, true, 1); + desc = &gq->ts_ring[gq->cur]; + } + + num = rswitch_get_num_cur_queues(gq); + ret = rswitch_gwca_queue_alloc_skb(gq, gq->dirty, num); + if (ret < 0) + goto err; + ret = rswitch_gwca_queue_ts_fill(ndev, gq, gq->dirty, num); + if (ret < 0) + goto err; + gq->dirty = rswitch_next_queue_index(gq, false, num); + + *quota -= limit - (++boguscnt); + + return boguscnt <= 0; + +err: + rswitch_gwca_halt(rdev->priv); + + return 0; +} + +static int rswitch_tx_free(struct net_device *ndev, bool free_txed_only) +{ + struct rswitch_device *rdev = netdev_priv(ndev); + struct rswitch_gwca_queue *gq = rdev->tx_queue; + struct rswitch_ext_desc *desc; + dma_addr_t dma_addr; + struct sk_buff *skb; + int free_num = 0; + int size; + + for (; gq->cur - gq->dirty > 0; gq->dirty = rswitch_next_queue_index(gq, false, 1)) { + desc = &gq->ring[gq->dirty]; + if (free_txed_only && (desc->desc.die_dt & DT_MASK) != DT_FEMPTY) + break; + + dma_rmb(); + size = le16_to_cpu(desc->desc.info_ds) & TX_DS; + skb = gq->skbs[gq->dirty]; + if (skb) { + dma_addr = rswitch_desc_get_dptr(&desc->desc); + dma_unmap_single(ndev->dev.parent, dma_addr, + size, DMA_TO_DEVICE); + dev_kfree_skb_any(gq->skbs[gq->dirty]); + gq->skbs[gq->dirty] = NULL; + free_num++; + } + desc->desc.die_dt = DT_EEMPTY; + rdev->ndev->stats.tx_packets++; + rdev->ndev->stats.tx_bytes += size; + } + + return free_num; +} + +static int rswitch_poll(struct napi_struct *napi, int budget) +{ + struct net_device *ndev = napi->dev; + struct rswitch_private *priv; + struct rswitch_device *rdev; + int quota = budget; + + rdev = netdev_priv(ndev); + priv = rdev->priv; + +retry: + rswitch_tx_free(ndev, true); + + if (rswitch_rx(ndev, "a)) + goto out; + else if (rdev->priv->gwca_halt) + goto err; + else if (rswitch_is_queue_rxed(rdev->rx_queue)) + goto retry; + + netif_wake_subqueue(ndev, 0); + + napi_complete(napi); + + rswitch_enadis_data_irq(priv, rdev->tx_queue->index, true); + rswitch_enadis_data_irq(priv, rdev->rx_queue->index, true); + +out: + return budget - quota; + +err: + napi_complete(napi); + + return 0; +} + +static void rswitch_queue_interrupt(struct net_device *ndev) +{ + struct rswitch_device *rdev = netdev_priv(ndev); + + if (napi_schedule_prep(&rdev->napi)) { + rswitch_enadis_data_irq(rdev->priv, rdev->tx_queue->index, false); + rswitch_enadis_data_irq(rdev->priv, rdev->rx_queue->index, false); + __napi_schedule(&rdev->napi); + } +} + +static irqreturn_t rswitch_data_irq(struct rswitch_private *priv, u32 *dis) +{ + struct rswitch_gwca_queue *gq; + int i, index, bit; + + for (i = 0; i < priv->gwca.num_queues; i++) { + gq = &priv->gwca.queues[i]; + index = gq->index / 32; + bit = BIT(gq->index % 32); + if (!(dis[index] & bit)) + continue; + + rswitch_ack_data_irq(priv, gq->index); + rswitch_queue_interrupt(gq->ndev); + } + + return IRQ_HANDLED; +} + +static irqreturn_t rswitch_gwca_irq(int irq, void *dev_id) +{ + struct rswitch_private *priv = dev_id; + u32 dis[RSWITCH_NUM_IRQ_REGS]; + irqreturn_t ret = IRQ_NONE; + + rswitch_get_data_irq_status(priv, dis); + + if (rswitch_is_any_data_irq(priv, dis, true) || + rswitch_is_any_data_irq(priv, dis, false)) + ret = rswitch_data_irq(priv, dis); + + return ret; +} + +static int rswitch_gwca_request_irqs(struct rswitch_private *priv) +{ + char *resource_name, *irq_name; + int i, ret, irq; + + for (i = 0; i < GWCA_NUM_IRQS; i++) { + resource_name = kasprintf(GFP_KERNEL, GWCA_IRQ_RESOURCE_NAME, i); + if (!resource_name) + return -ENOMEM; + + irq = platform_get_irq_byname(priv->pdev, resource_name); + kfree(resource_name); + if (irq < 0) + return irq; + + irq_name = devm_kasprintf(&priv->pdev->dev, GFP_KERNEL, + GWCA_IRQ_NAME, i); + if (!irq_name) + return -ENOMEM; + + ret = devm_request_irq(&priv->pdev->dev, irq, rswitch_gwca_irq, + 0, irq_name, priv); + if (ret < 0) + return ret; + } + + return 0; +} + +/* Ethernet TSN Agent block (ETHA) and Ethernet MAC IP block (RMAC) */ +static int rswitch_etha_change_mode(struct rswitch_etha *etha, + enum rswitch_etha_mode mode) +{ + int ret; + + if (!rswitch_agent_clock_is_enabled(etha->coma_addr, etha->index)) + rswitch_agent_clock_ctrl(etha->coma_addr, etha->index, 1); + + iowrite32(mode, etha->addr + EAMC); + + ret = rswitch_reg_wait(etha->addr, EAMS, EAMS_OPS_MASK, mode); + + if (mode == EAMC_OPC_DISABLE) + rswitch_agent_clock_ctrl(etha->coma_addr, etha->index, 0); + + return ret; +} + +static void rswitch_etha_read_mac_address(struct rswitch_etha *etha) +{ + u32 mrmac0 = ioread32(etha->addr + MRMAC0); + u32 mrmac1 = ioread32(etha->addr + MRMAC1); + u8 *mac = ða->mac_addr[0]; + + mac[0] = (mrmac0 >> 8) & 0xFF; + mac[1] = (mrmac0 >> 0) & 0xFF; + mac[2] = (mrmac1 >> 24) & 0xFF; + mac[3] = (mrmac1 >> 16) & 0xFF; + mac[4] = (mrmac1 >> 8) & 0xFF; + mac[5] = (mrmac1 >> 0) & 0xFF; +} + +static void rswitch_etha_write_mac_address(struct rswitch_etha *etha, const u8 *mac) +{ + iowrite32((mac[0] << 8) | mac[1], etha->addr + MRMAC0); + iowrite32((mac[2] << 24) | (mac[3] << 16) | (mac[4] << 8) | mac[5], + etha->addr + MRMAC1); +} + +static bool rswitch_etha_wait_link_verification(struct rswitch_etha *etha) +{ + iowrite32(MLVC_PLV, etha->addr + MLVC); + + return rswitch_reg_wait(etha->addr, MLVC, MLVC_PLV, 0); +} + +static void rswitch_rmac_setting(struct rswitch_etha *etha, const u8 *mac) +{ + u32 val; + + rswitch_etha_write_mac_address(etha, mac); + + switch (etha->speed) { + case 100: + val = MPIC_LSC_100M; + break; + case 1000: + val = MPIC_LSC_1G; + break; + case 2500: + val = MPIC_LSC_2_5G; + break; + default: + return; + } + + iowrite32(MPIC_PIS_GMII | val, etha->addr + MPIC); +} + +static void rswitch_etha_enable_mii(struct rswitch_etha *etha) +{ + rswitch_modify(etha->addr, MPIC, MPIC_PSMCS_MASK | MPIC_PSMHT_MASK, + MPIC_PSMCS(0x05) | MPIC_PSMHT(0x06)); + rswitch_modify(etha->addr, MPSM, 0, MPSM_MFF_C45); +} + +static int rswitch_etha_hw_init(struct rswitch_etha *etha, const u8 *mac) +{ + int err; + + err = rswitch_etha_change_mode(etha, EAMC_OPC_DISABLE); + if (err < 0) + return err; + err = rswitch_etha_change_mode(etha, EAMC_OPC_CONFIG); + if (err < 0) + return err; + + iowrite32(EAVCC_VEM_SC_TAG, etha->addr + EAVCC); + rswitch_rmac_setting(etha, mac); + rswitch_etha_enable_mii(etha); + + err = rswitch_etha_wait_link_verification(etha); + if (err < 0) + return err; + + err = rswitch_etha_change_mode(etha, EAMC_OPC_DISABLE); + if (err < 0) + return err; + + return rswitch_etha_change_mode(etha, EAMC_OPC_OPERATION); +} + +static int rswitch_etha_set_access(struct rswitch_etha *etha, bool read, + int phyad, int devad, int regad, int data) +{ + int pop = read ? MDIO_READ_C45 : MDIO_WRITE_C45; + u32 val; + int ret; + + if (devad == 0xffffffff) + return -ENODEV; + + writel(MMIS1_CLEAR_FLAGS, etha->addr + MMIS1); + + val = MPSM_PSME | MPSM_MFF_C45; + iowrite32((regad << 16) | (devad << 8) | (phyad << 3) | val, etha->addr + MPSM); + + ret = rswitch_reg_wait(etha->addr, MMIS1, MMIS1_PAACS, MMIS1_PAACS); + if (ret) + return ret; + + rswitch_modify(etha->addr, MMIS1, MMIS1_PAACS, MMIS1_PAACS); + + if (read) { + writel((pop << 13) | (devad << 8) | (phyad << 3) | val, etha->addr + MPSM); + + ret = rswitch_reg_wait(etha->addr, MMIS1, MMIS1_PRACS, MMIS1_PRACS); + if (ret) + return ret; + + ret = (ioread32(etha->addr + MPSM) & MPSM_PRD_MASK) >> 16; + + rswitch_modify(etha->addr, MMIS1, MMIS1_PRACS, MMIS1_PRACS); + } else { + iowrite32((data << 16) | (pop << 13) | (devad << 8) | (phyad << 3) | val, + etha->addr + MPSM); + + ret = rswitch_reg_wait(etha->addr, MMIS1, MMIS1_PWACS, MMIS1_PWACS); + } + + return ret; +} + +static int rswitch_etha_mii_read(struct mii_bus *bus, int addr, int regnum) +{ + struct rswitch_etha *etha = bus->priv; + int mode, devad, regad; + + mode = regnum & MII_ADDR_C45; + devad = (regnum >> MII_DEVADDR_C45_SHIFT) & 0x1f; + regad = regnum & MII_REGADDR_C45_MASK; + + /* Not support Clause 22 access method */ + if (!mode) + return -EOPNOTSUPP; + + return rswitch_etha_set_access(etha, true, addr, devad, regad, 0); +} + +static int rswitch_etha_mii_write(struct mii_bus *bus, int addr, int regnum, u16 val) +{ + struct rswitch_etha *etha = bus->priv; + int mode, devad, regad; + + mode = regnum & MII_ADDR_C45; + devad = (regnum >> MII_DEVADDR_C45_SHIFT) & 0x1f; + regad = regnum & MII_REGADDR_C45_MASK; + + /* Not support Clause 22 access method */ + if (!mode) + return -EOPNOTSUPP; + + return rswitch_etha_set_access(etha, false, addr, devad, regad, val); +} + +/* Call of_node_put(port) after done */ +static struct device_node *rswitch_get_port_node(struct rswitch_device *rdev) +{ + struct device_node *ports, *port; + int err = 0; + u32 index; + + ports = of_get_child_by_name(rdev->ndev->dev.parent->of_node, + "ethernet-ports"); + if (!ports) + return NULL; + + for_each_child_of_node(ports, port) { + err = of_property_read_u32(port, "reg", &index); + if (err < 0) { + port = NULL; + goto out; + } + if (index == rdev->etha->index) + break; + } + +out: + of_node_put(ports); + + return port; +} + +/* Call of_node_put(mdio) after done */ +static struct device_node *rswitch_get_mdio_node(struct rswitch_device *rdev) +{ + struct device_node *port, *mdio; + + port = rswitch_get_port_node(rdev); + if (!port) + return NULL; + + mdio = of_get_child_by_name(port, "mdio"); + of_node_put(port); + + return mdio; +} + +static int rswitch_etha_get_params(struct rswitch_device *rdev) +{ + struct device_node *port; + int err; + + port = rswitch_get_port_node(rdev); + if (!port) + return -ENODEV; + + err = of_get_phy_mode(port, &rdev->etha->phy_interface); + of_node_put(port); + + switch (rdev->etha->phy_interface) { + case PHY_INTERFACE_MODE_MII: + rdev->etha->speed = SPEED_100; + break; + case PHY_INTERFACE_MODE_SGMII: + rdev->etha->speed = SPEED_1000; + break; + case PHY_INTERFACE_MODE_USXGMII: + rdev->etha->speed = SPEED_2500; + break; + default: + err = -EINVAL; + break; + } + + return err; +} + +static int rswitch_mii_register(struct rswitch_device *rdev) +{ + struct device_node *mdio_np; + struct mii_bus *mii_bus; + int err; + + mii_bus = mdiobus_alloc(); + if (!mii_bus) + return -ENOMEM; + + mii_bus->name = "rswitch_mii"; + sprintf(mii_bus->id, "etha%d", rdev->etha->index); + mii_bus->priv = rdev->etha; + mii_bus->read = rswitch_etha_mii_read; + mii_bus->write = rswitch_etha_mii_write; + mii_bus->parent = &rdev->priv->pdev->dev; + + mdio_np = rswitch_get_mdio_node(rdev); + err = of_mdiobus_register(mii_bus, mdio_np); + if (err < 0) { + mdiobus_free(mii_bus); + goto out; + } + + rdev->etha->mii = mii_bus; + +out: + of_node_put(mdio_np); + + return err; +} + +static void rswitch_mii_unregister(struct rswitch_device *rdev) +{ + if (rdev->etha->mii) { + mdiobus_unregister(rdev->etha->mii); + mdiobus_free(rdev->etha->mii); + rdev->etha->mii = NULL; + } +} + +static void rswitch_mac_config(struct phylink_config *config, + unsigned int mode, + const struct phylink_link_state *state) +{ +} + +static void rswitch_mac_link_down(struct phylink_config *config, + unsigned int mode, + phy_interface_t interface) +{ +} + +static void rswitch_mac_link_up(struct phylink_config *config, + struct phy_device *phydev, unsigned int mode, + phy_interface_t interface, int speed, + int duplex, bool tx_pause, bool rx_pause) +{ + /* Current hardware cannot change speed at runtime */ +} + +static const struct phylink_mac_ops rswitch_phylink_ops = { + .validate = phylink_generic_validate, + .mac_config = rswitch_mac_config, + .mac_link_down = rswitch_mac_link_down, + .mac_link_up = rswitch_mac_link_up, +}; + +static int rswitch_phylink_init(struct rswitch_device *rdev) +{ + struct device_node *port; + struct phylink *phylink; + int err; + + port = rswitch_get_port_node(rdev); + if (!port) + return -ENODEV; + + rdev->phylink_config.dev = &rdev->ndev->dev; + rdev->phylink_config.type = PHYLINK_NETDEV; + __set_bit(PHY_INTERFACE_MODE_SGMII, rdev->phylink_config.supported_interfaces); + __set_bit(PHY_INTERFACE_MODE_USXGMII, rdev->phylink_config.supported_interfaces); + rdev->phylink_config.mac_capabilities = MAC_100FD | MAC_1000FD | MAC_2500FD; + + phylink = phylink_create(&rdev->phylink_config, &port->fwnode, + rdev->etha->phy_interface, &rswitch_phylink_ops); + if (IS_ERR(phylink)) { + err = PTR_ERR(phylink); + goto out; + } + + rdev->phylink = phylink; + err = phylink_of_phy_connect(rdev->phylink, port, rdev->etha->phy_interface); +out: + of_node_put(port); + + return err; +} + +static void rswitch_phylink_deinit(struct rswitch_device *rdev) +{ + rtnl_lock(); + phylink_disconnect_phy(rdev->phylink); + rtnl_unlock(); + phylink_destroy(rdev->phylink); +} + +static int rswitch_serdes_set_params(struct rswitch_device *rdev) +{ + struct device_node *port = rswitch_get_port_node(rdev); + struct phy *serdes; + int err; + + serdes = devm_of_phy_get(&rdev->priv->pdev->dev, port, NULL); + of_node_put(port); + if (IS_ERR(serdes)) + return PTR_ERR(serdes); + + err = phy_set_mode_ext(serdes, PHY_MODE_ETHERNET, + rdev->etha->phy_interface); + if (err < 0) + return err; + + return phy_set_speed(serdes, rdev->etha->speed); +} + +static int rswitch_serdes_init(struct rswitch_device *rdev) +{ + struct device_node *port = rswitch_get_port_node(rdev); + struct phy *serdes; + + serdes = devm_of_phy_get(&rdev->priv->pdev->dev, port, NULL); + of_node_put(port); + if (IS_ERR(serdes)) + return PTR_ERR(serdes); + + return phy_init(serdes); +} + +static int rswitch_serdes_deinit(struct rswitch_device *rdev) +{ + struct device_node *port = rswitch_get_port_node(rdev); + struct phy *serdes; + + serdes = devm_of_phy_get(&rdev->priv->pdev->dev, port, NULL); + of_node_put(port); + if (IS_ERR(serdes)) + return PTR_ERR(serdes); + + return phy_exit(serdes); +} + +static int rswitch_ether_port_init_one(struct rswitch_device *rdev) +{ + int err; + + if (!rdev->etha->operated) { + err = rswitch_etha_hw_init(rdev->etha, rdev->ndev->dev_addr); + if (err < 0) + return err; + rdev->etha->operated = true; + } + + err = rswitch_mii_register(rdev); + if (err < 0) + return err; + + err = rswitch_phylink_init(rdev); + if (err < 0) + goto err_phylink_init; + + err = rswitch_serdes_set_params(rdev); + if (err < 0) + goto err_serdes_set_params; + + return 0; + +err_serdes_set_params: + rswitch_phylink_deinit(rdev); + +err_phylink_init: + rswitch_mii_unregister(rdev); + + return err; +} + +static void rswitch_ether_port_deinit_one(struct rswitch_device *rdev) +{ + rswitch_phylink_deinit(rdev); + rswitch_mii_unregister(rdev); +} + +static int rswitch_ether_port_init_all(struct rswitch_private *priv) +{ + int i, err; + + for (i = 0; i < RSWITCH_NUM_PORTS; i++) { + err = rswitch_ether_port_init_one(priv->rdev[i]); + if (err) + goto err_init_one; + } + + for (i = 0; i < RSWITCH_NUM_PORTS; i++) { + err = rswitch_serdes_init(priv->rdev[i]); + if (err) + goto err_serdes; + } + + return 0; + +err_serdes: + for (i--; i >= 0; i--) + rswitch_serdes_deinit(priv->rdev[i]); + i = RSWITCH_NUM_PORTS; + +err_init_one: + for (i--; i >= 0; i--) + rswitch_ether_port_deinit_one(priv->rdev[i]); + + return err; +} + +static void rswitch_ether_port_deinit_all(struct rswitch_private *priv) +{ + int i; + + for (i = 0; i < RSWITCH_NUM_PORTS; i++) { + rswitch_serdes_deinit(priv->rdev[i]); + rswitch_ether_port_deinit_one(priv->rdev[i]); + } +} + +static int rswitch_open(struct net_device *ndev) +{ + struct rswitch_device *rdev = netdev_priv(ndev); + + phylink_start(rdev->phylink); + + napi_enable(&rdev->napi); + netif_start_queue(ndev); + + rswitch_enadis_data_irq(rdev->priv, rdev->tx_queue->index, true); + rswitch_enadis_data_irq(rdev->priv, rdev->rx_queue->index, true); + + return 0; +}; + +static int rswitch_stop(struct net_device *ndev) +{ + struct rswitch_device *rdev = netdev_priv(ndev); + + netif_tx_stop_all_queues(ndev); + + rswitch_enadis_data_irq(rdev->priv, rdev->tx_queue->index, false); + rswitch_enadis_data_irq(rdev->priv, rdev->rx_queue->index, false); + + phylink_stop(rdev->phylink); + napi_disable(&rdev->napi); + + return 0; +}; + +static int rswitch_start_xmit(struct sk_buff *skb, struct net_device *ndev) +{ + struct rswitch_device *rdev = netdev_priv(ndev); + struct rswitch_gwca_queue *gq = rdev->tx_queue; + struct rswitch_ext_desc *desc; + int ret = NETDEV_TX_OK; + dma_addr_t dma_addr; + + if (rswitch_get_num_cur_queues(gq) >= gq->ring_size - 1) { + netif_stop_subqueue(ndev, 0); + return ret; + } + + if (skb_put_padto(skb, ETH_ZLEN)) + return ret; + + dma_addr = dma_map_single(ndev->dev.parent, skb->data, skb->len, DMA_TO_DEVICE); + if (dma_mapping_error(ndev->dev.parent, dma_addr)) { + dev_kfree_skb_any(skb); + return ret; + } + + gq->skbs[gq->cur] = skb; + desc = &gq->ring[gq->cur]; + rswitch_desc_set_dptr(&desc->desc, dma_addr); + desc->desc.info_ds = cpu_to_le16(skb->len); + + desc->info1 = cpu_to_le64(INFO1_DV(BIT(rdev->etha->index)) | INFO1_FMT); + if (skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP) { + skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS; + rdev->ts_tag++; + desc->info1 |= cpu_to_le64(INFO1_TSUN(rdev->ts_tag) | INFO1_TXC); + } + skb_tx_timestamp(skb); + + dma_wmb(); + + desc->desc.die_dt = DT_FSINGLE | DIE; + wmb(); /* gq->cur must be incremented after die_dt was set */ + + gq->cur = rswitch_next_queue_index(gq, true, 1); + rswitch_modify(rdev->addr, GWTRC(gq->index), 0, BIT(gq->index % 32)); + + return ret; +} + +static struct net_device_stats *rswitch_get_stats(struct net_device *ndev) +{ + return &ndev->stats; +} + +static int rswitch_eth_ioctl(struct net_device *ndev, struct ifreq *req, int cmd) +{ + struct rswitch_device *rdev = netdev_priv(ndev); + + if (!netif_running(ndev)) + return -EINVAL; + + return phylink_mii_ioctl(rdev->phylink, req, cmd); +} + +static const struct net_device_ops rswitch_netdev_ops = { + .ndo_open = rswitch_open, + .ndo_stop = rswitch_stop, + .ndo_start_xmit = rswitch_start_xmit, + .ndo_get_stats = rswitch_get_stats, + .ndo_eth_ioctl = rswitch_eth_ioctl, + .ndo_validate_addr = eth_validate_addr, + .ndo_set_mac_address = eth_mac_addr, +}; + +static const struct of_device_id renesas_eth_sw_of_table[] = { + { .compatible = "renesas,r8a779f0-ether-switch", }, + { } +}; +MODULE_DEVICE_TABLE(of, renesas_eth_sw_of_table); + +static void rswitch_etha_init(struct rswitch_private *priv, int index) +{ + struct rswitch_etha *etha = &priv->etha[index]; + + memset(etha, 0, sizeof(*etha)); + etha->index = index; + etha->addr = priv->addr + RSWITCH_ETHA_OFFSET + index * RSWITCH_ETHA_SIZE; + etha->coma_addr = priv->addr; +} + +static int rswitch_device_alloc(struct rswitch_private *priv, int index) +{ + struct platform_device *pdev = priv->pdev; + struct rswitch_device *rdev; + struct net_device *ndev; + int err; + + if (index >= RSWITCH_NUM_PORTS) + return -EINVAL; + + ndev = alloc_etherdev_mqs(sizeof(struct rswitch_device), 1, 1); + if (!ndev) + return -ENOMEM; + + SET_NETDEV_DEV(ndev, &pdev->dev); + ether_setup(ndev); + + rdev = netdev_priv(ndev); + rdev->ndev = ndev; + rdev->priv = priv; + priv->rdev[index] = rdev; + rdev->port = index; + rdev->etha = &priv->etha[index]; + rdev->addr = priv->addr; + + ndev->base_addr = (unsigned long)rdev->addr; + snprintf(ndev->name, IFNAMSIZ, "tsn%d", index); + ndev->netdev_ops = &rswitch_netdev_ops; + + netif_napi_add(ndev, &rdev->napi, rswitch_poll); + + err = of_get_ethdev_address(pdev->dev.of_node, ndev); + if (err) { + if (is_valid_ether_addr(rdev->etha->mac_addr)) + eth_hw_addr_set(ndev, rdev->etha->mac_addr); + else + eth_hw_addr_random(ndev); + } + + err = rswitch_etha_get_params(rdev); + if (err < 0) + goto out_get_params; + + if (rdev->priv->gwca.speed < rdev->etha->speed) + rdev->priv->gwca.speed = rdev->etha->speed; + + err = rswitch_rxdmac_alloc(ndev); + if (err < 0) + goto out_rxdmac; + + err = rswitch_txdmac_alloc(ndev); + if (err < 0) + goto out_txdmac; + + return 0; + +out_txdmac: + rswitch_rxdmac_free(ndev); + +out_rxdmac: +out_get_params: + netif_napi_del(&rdev->napi); + free_netdev(ndev); + + return err; +} + +static void rswitch_device_free(struct rswitch_private *priv, int index) +{ + struct rswitch_device *rdev = priv->rdev[index]; + struct net_device *ndev = rdev->ndev; + + rswitch_txdmac_free(ndev); + rswitch_rxdmac_free(ndev); + netif_napi_del(&rdev->napi); + free_netdev(ndev); +} + +static int rswitch_init(struct rswitch_private *priv) +{ + int i, err; + + for (i = 0; i < RSWITCH_NUM_PORTS; i++) + rswitch_etha_init(priv, i); + + rswitch_clock_enable(priv); + for (i = 0; i < RSWITCH_NUM_PORTS; i++) + rswitch_etha_read_mac_address(&priv->etha[i]); + + rswitch_reset(priv); + + rswitch_clock_enable(priv); + rswitch_top_init(priv); + err = rswitch_bpool_config(priv); + if (err < 0) + return err; + + err = rswitch_gwca_desc_alloc(priv); + if (err < 0) + return -ENOMEM; + + for (i = 0; i < RSWITCH_NUM_PORTS; i++) { + err = rswitch_device_alloc(priv, i); + if (err < 0) { + for (i--; i >= 0; i--) + rswitch_device_free(priv, i); + goto err_device_alloc; + } + } + + rswitch_fwd_init(priv); + + err = rswitch_gwca_request_irqs(priv); + if (err < 0) + goto err_gwca_request_irq; + + err = rswitch_gwca_hw_init(priv); + if (err < 0) + goto err_gwca_hw_init; + + err = rswitch_ether_port_init_all(priv); + if (err) + goto err_ether_port_init_all; + + for (i = 0; i < RSWITCH_NUM_PORTS; i++) { + err = register_netdev(priv->rdev[i]->ndev); + if (err) { + for (i--; i >= 0; i--) + unregister_netdev(priv->rdev[i]->ndev); + goto err_register_netdev; + } + } + + for (i = 0; i < RSWITCH_NUM_PORTS; i++) + netdev_info(priv->rdev[i]->ndev, "MAC address %pMn", + priv->rdev[i]->ndev->dev_addr); + + return 0; + +err_register_netdev: + rswitch_ether_port_deinit_all(priv); + +err_ether_port_init_all: + rswitch_gwca_hw_deinit(priv); + +err_gwca_hw_init: +err_gwca_request_irq: + for (i = 0; i < RSWITCH_NUM_PORTS; i++) + rswitch_device_free(priv, i); + +err_device_alloc: + rswitch_gwca_desc_free(priv); + + return err; +} + +static int renesas_eth_sw_probe(struct platform_device *pdev) +{ + struct rswitch_private *priv; + struct resource *res; + int ret; + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "secure_base"); + if (!res) { + dev_err(&pdev->dev, "invalid resource\n"); + return -EINVAL; + } + + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + platform_set_drvdata(pdev, priv); + priv->pdev = pdev; + priv->addr = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(priv->addr)) + return PTR_ERR(priv->addr); + + ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(40)); + if (ret < 0) { + ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)); + if (ret < 0) + return ret; + } + + priv->gwca.index = AGENT_INDEX_GWCA; + priv->gwca.num_queues = min(RSWITCH_NUM_PORTS * NUM_QUEUES_PER_NDEV, + RSWITCH_MAX_NUM_QUEUES); + priv->gwca.queues = devm_kcalloc(&pdev->dev, priv->gwca.num_queues, + sizeof(*priv->gwca.queues), GFP_KERNEL); + if (!priv->gwca.queues) + return -ENOMEM; + + pm_runtime_enable(&pdev->dev); + pm_runtime_get_sync(&pdev->dev); + + ret = rswitch_init(priv); + + device_set_wakeup_capable(&pdev->dev, 1); + + return ret; +} + +static void rswitch_deinit(struct rswitch_private *priv) +{ + int i; + + rswitch_gwca_hw_deinit(priv); + + for (i = 0; i < RSWITCH_NUM_PORTS; i++) { + struct rswitch_device *rdev = priv->rdev[i]; + + rswitch_serdes_deinit(rdev); + rswitch_ether_port_deinit_one(rdev); + unregister_netdev(rdev->ndev); + rswitch_device_free(priv, i); + } + + rswitch_gwca_desc_free(priv); + + rswitch_clock_disable(priv); +} + +static int renesas_eth_sw_remove(struct platform_device *pdev) +{ + struct rswitch_private *priv = platform_get_drvdata(pdev); + + rswitch_deinit(priv); + + pm_runtime_put(&pdev->dev); + pm_runtime_disable(&pdev->dev); + + platform_set_drvdata(pdev, NULL); + + return 0; +} + +static struct platform_driver renesas_eth_sw_driver_platform = { + .probe = renesas_eth_sw_probe, + .remove = renesas_eth_sw_remove, + .driver = { + .name = "renesas_eth_sw", + .of_match_table = renesas_eth_sw_of_table, + } +}; +module_platform_driver(renesas_eth_sw_driver_platform); +MODULE_AUTHOR("Yoshihiro Shimoda"); +MODULE_DESCRIPTION("Renesas Ethernet Switch device driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/net/ethernet/renesas/rswitch.h b/drivers/net/ethernet/renesas/rswitch.h new file mode 100644 index 000000000000..3c58a7ca8b8a --- /dev/null +++ b/drivers/net/ethernet/renesas/rswitch.h @@ -0,0 +1,972 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Renesas Ethernet Switch device driver + * + * Copyright (C) 2022 Renesas Electronics Corporation + */ + +#ifndef __RSWITCH_H__ +#define __RSWITCH_H__ + +#include + +#define RSWITCH_MAX_NUM_QUEUES 128 + +#define RSWITCH_NUM_PORTS 3 + +#define TX_RING_SIZE 1024 +#define RX_RING_SIZE 1024 + +#define PKT_BUF_SZ 1584 +#define RSWITCH_ALIGN 128 +#define RSWITCH_MAX_CTAG_PCP 7 + +#define RSWITCH_TIMEOUT_US 100000 + +#define RSWITCH_TOP_OFFSET 0x00008000 +#define RSWITCH_COMA_OFFSET 0x00009000 +#define RSWITCH_ETHA_OFFSET 0x0000a000 /* with RMAC */ +#define RSWITCH_ETHA_SIZE 0x00002000 /* with RMAC */ +#define RSWITCH_GWCA0_OFFSET 0x00010000 +#define RSWITCH_GWCA1_OFFSET 0x00012000 + +/* TODO: hardcoded ETHA/GWCA settings for now */ +#define GWCA_IRQ_RESOURCE_NAME "gwca0_rxtx%d" +#define GWCA_IRQ_NAME "rswitch: gwca0_rxtx%d" +#define GWCA_NUM_IRQS 8 +#define GWCA_INDEX 0 +#define AGENT_INDEX_GWCA 3 +#define GWRO RSWITCH_GWCA0_OFFSET + +#define FWRO 0 +#define TPRO RSWITCH_TOP_OFFSET +#define CARO RSWITCH_COMA_OFFSET +#define TARO 0 +#define RMRO 0x1000 +enum rswitch_reg { + FWGC = FWRO + 0x0000, + FWTTC0 = FWRO + 0x0010, + FWTTC1 = FWRO + 0x0014, + FWLBMC = FWRO + 0x0018, + FWCEPTC = FWRO + 0x0020, + FWCEPRC0 = FWRO + 0x0024, + FWCEPRC1 = FWRO + 0x0028, + FWCEPRC2 = FWRO + 0x002c, + FWCLPTC = FWRO + 0x0030, + FWCLPRC = FWRO + 0x0034, + FWCMPTC = FWRO + 0x0040, + FWEMPTC = FWRO + 0x0044, + FWSDMPTC = FWRO + 0x0050, + FWSDMPVC = FWRO + 0x0054, + FWLBWMC0 = FWRO + 0x0080, + FWPC00 = FWRO + 0x0100, + FWPC10 = FWRO + 0x0104, + FWPC20 = FWRO + 0x0108, + FWCTGC00 = FWRO + 0x0400, + FWCTGC10 = FWRO + 0x0404, + FWCTTC00 = FWRO + 0x0408, + FWCTTC10 = FWRO + 0x040c, + FWCTTC200 = FWRO + 0x0410, + FWCTSC00 = FWRO + 0x0420, + FWCTSC10 = FWRO + 0x0424, + FWCTSC20 = FWRO + 0x0428, + FWCTSC30 = FWRO + 0x042c, + FWCTSC40 = FWRO + 0x0430, + FWTWBFC0 = FWRO + 0x1000, + FWTWBFVC0 = FWRO + 0x1004, + FWTHBFC0 = FWRO + 0x1400, + FWTHBFV0C0 = FWRO + 0x1404, + FWTHBFV1C0 = FWRO + 0x1408, + FWFOBFC0 = FWRO + 0x1800, + FWFOBFV0C0 = FWRO + 0x1804, + FWFOBFV1C0 = FWRO + 0x1808, + FWRFC0 = FWRO + 0x1c00, + FWRFVC0 = FWRO + 0x1c04, + FWCFC0 = FWRO + 0x2000, + FWCFMC00 = FWRO + 0x2004, + FWIP4SC = FWRO + 0x4008, + FWIP6SC = FWRO + 0x4018, + FWIP6OC = FWRO + 0x401c, + FWL2SC = FWRO + 0x4020, + FWSFHEC = FWRO + 0x4030, + FWSHCR0 = FWRO + 0x4040, + FWSHCR1 = FWRO + 0x4044, + FWSHCR2 = FWRO + 0x4048, + FWSHCR3 = FWRO + 0x404c, + FWSHCR4 = FWRO + 0x4050, + FWSHCR5 = FWRO + 0x4054, + FWSHCR6 = FWRO + 0x4058, + FWSHCR7 = FWRO + 0x405c, + FWSHCR8 = FWRO + 0x4060, + FWSHCR9 = FWRO + 0x4064, + FWSHCR10 = FWRO + 0x4068, + FWSHCR11 = FWRO + 0x406c, + FWSHCR12 = FWRO + 0x4070, + FWSHCR13 = FWRO + 0x4074, + FWSHCRR = FWRO + 0x4078, + FWLTHHEC = FWRO + 0x4090, + FWLTHHC = FWRO + 0x4094, + FWLTHTL0 = FWRO + 0x40a0, + FWLTHTL1 = FWRO + 0x40a4, + FWLTHTL2 = FWRO + 0x40a8, + FWLTHTL3 = FWRO + 0x40ac, + FWLTHTL4 = FWRO + 0x40b0, + FWLTHTL5 = FWRO + 0x40b4, + FWLTHTL6 = FWRO + 0x40b8, + FWLTHTL7 = FWRO + 0x40bc, + FWLTHTL80 = FWRO + 0x40c0, + FWLTHTL9 = FWRO + 0x40d0, + FWLTHTLR = FWRO + 0x40d4, + FWLTHTIM = FWRO + 0x40e0, + FWLTHTEM = FWRO + 0x40e4, + FWLTHTS0 = FWRO + 0x4100, + FWLTHTS1 = FWRO + 0x4104, + FWLTHTS2 = FWRO + 0x4108, + FWLTHTS3 = FWRO + 0x410c, + FWLTHTS4 = FWRO + 0x4110, + FWLTHTSR0 = FWRO + 0x4120, + FWLTHTSR1 = FWRO + 0x4124, + FWLTHTSR2 = FWRO + 0x4128, + FWLTHTSR3 = FWRO + 0x412c, + FWLTHTSR40 = FWRO + 0x4130, + FWLTHTSR5 = FWRO + 0x4140, + FWLTHTR = FWRO + 0x4150, + FWLTHTRR0 = FWRO + 0x4154, + FWLTHTRR1 = FWRO + 0x4158, + FWLTHTRR2 = FWRO + 0x415c, + FWLTHTRR3 = FWRO + 0x4160, + FWLTHTRR4 = FWRO + 0x4164, + FWLTHTRR5 = FWRO + 0x4168, + FWLTHTRR6 = FWRO + 0x416c, + FWLTHTRR7 = FWRO + 0x4170, + FWLTHTRR8 = FWRO + 0x4174, + FWLTHTRR9 = FWRO + 0x4180, + FWLTHTRR10 = FWRO + 0x4190, + FWIPHEC = FWRO + 0x4214, + FWIPHC = FWRO + 0x4218, + FWIPTL0 = FWRO + 0x4220, + FWIPTL1 = FWRO + 0x4224, + FWIPTL2 = FWRO + 0x4228, + FWIPTL3 = FWRO + 0x422c, + FWIPTL4 = FWRO + 0x4230, + FWIPTL5 = FWRO + 0x4234, + FWIPTL6 = FWRO + 0x4238, + FWIPTL7 = FWRO + 0x4240, + FWIPTL8 = FWRO + 0x4250, + FWIPTLR = FWRO + 0x4254, + FWIPTIM = FWRO + 0x4260, + FWIPTEM = FWRO + 0x4264, + FWIPTS0 = FWRO + 0x4270, + FWIPTS1 = FWRO + 0x4274, + FWIPTS2 = FWRO + 0x4278, + FWIPTS3 = FWRO + 0x427c, + FWIPTS4 = FWRO + 0x4280, + FWIPTSR0 = FWRO + 0x4284, + FWIPTSR1 = FWRO + 0x4288, + FWIPTSR2 = FWRO + 0x428c, + FWIPTSR3 = FWRO + 0x4290, + FWIPTSR4 = FWRO + 0x42a0, + FWIPTR = FWRO + 0x42b0, + FWIPTRR0 = FWRO + 0x42b4, + FWIPTRR1 = FWRO + 0x42b8, + FWIPTRR2 = FWRO + 0x42bc, + FWIPTRR3 = FWRO + 0x42c0, + FWIPTRR4 = FWRO + 0x42c4, + FWIPTRR5 = FWRO + 0x42c8, + FWIPTRR6 = FWRO + 0x42cc, + FWIPTRR7 = FWRO + 0x42d0, + FWIPTRR8 = FWRO + 0x42e0, + FWIPTRR9 = FWRO + 0x42f0, + FWIPHLEC = FWRO + 0x4300, + FWIPAGUSPC = FWRO + 0x4500, + FWIPAGC = FWRO + 0x4504, + FWIPAGM0 = FWRO + 0x4510, + FWIPAGM1 = FWRO + 0x4514, + FWIPAGM2 = FWRO + 0x4518, + FWIPAGM3 = FWRO + 0x451c, + FWIPAGM4 = FWRO + 0x4520, + FWMACHEC = FWRO + 0x4620, + FWMACHC = FWRO + 0x4624, + FWMACTL0 = FWRO + 0x4630, + FWMACTL1 = FWRO + 0x4634, + FWMACTL2 = FWRO + 0x4638, + FWMACTL3 = FWRO + 0x463c, + FWMACTL4 = FWRO + 0x4640, + FWMACTL5 = FWRO + 0x4650, + FWMACTLR = FWRO + 0x4654, + FWMACTIM = FWRO + 0x4660, + FWMACTEM = FWRO + 0x4664, + FWMACTS0 = FWRO + 0x4670, + FWMACTS1 = FWRO + 0x4674, + FWMACTSR0 = FWRO + 0x4678, + FWMACTSR1 = FWRO + 0x467c, + FWMACTSR2 = FWRO + 0x4680, + FWMACTSR3 = FWRO + 0x4690, + FWMACTR = FWRO + 0x46a0, + FWMACTRR0 = FWRO + 0x46a4, + FWMACTRR1 = FWRO + 0x46a8, + FWMACTRR2 = FWRO + 0x46ac, + FWMACTRR3 = FWRO + 0x46b0, + FWMACTRR4 = FWRO + 0x46b4, + FWMACTRR5 = FWRO + 0x46c0, + FWMACTRR6 = FWRO + 0x46d0, + FWMACHLEC = FWRO + 0x4700, + FWMACAGUSPC = FWRO + 0x4880, + FWMACAGC = FWRO + 0x4884, + FWMACAGM0 = FWRO + 0x4888, + FWMACAGM1 = FWRO + 0x488c, + FWVLANTEC = FWRO + 0x4900, + FWVLANTL0 = FWRO + 0x4910, + FWVLANTL1 = FWRO + 0x4914, + FWVLANTL2 = FWRO + 0x4918, + FWVLANTL3 = FWRO + 0x4920, + FWVLANTL4 = FWRO + 0x4930, + FWVLANTLR = FWRO + 0x4934, + FWVLANTIM = FWRO + 0x4940, + FWVLANTEM = FWRO + 0x4944, + FWVLANTS = FWRO + 0x4950, + FWVLANTSR0 = FWRO + 0x4954, + FWVLANTSR1 = FWRO + 0x4958, + FWVLANTSR2 = FWRO + 0x4960, + FWVLANTSR3 = FWRO + 0x4970, + FWPBFC0 = FWRO + 0x4a00, + FWPBFCSDC00 = FWRO + 0x4a04, + FWL23URL0 = FWRO + 0x4e00, + FWL23URL1 = FWRO + 0x4e04, + FWL23URL2 = FWRO + 0x4e08, + FWL23URL3 = FWRO + 0x4e0c, + FWL23URLR = FWRO + 0x4e10, + FWL23UTIM = FWRO + 0x4e20, + FWL23URR = FWRO + 0x4e30, + FWL23URRR0 = FWRO + 0x4e34, + FWL23URRR1 = FWRO + 0x4e38, + FWL23URRR2 = FWRO + 0x4e3c, + FWL23URRR3 = FWRO + 0x4e40, + FWL23URMC0 = FWRO + 0x4f00, + FWPMFGC0 = FWRO + 0x5000, + FWPGFC0 = FWRO + 0x5100, + FWPGFIGSC0 = FWRO + 0x5104, + FWPGFENC0 = FWRO + 0x5108, + FWPGFENM0 = FWRO + 0x510c, + FWPGFCSTC00 = FWRO + 0x5110, + FWPGFCSTC10 = FWRO + 0x5114, + FWPGFCSTM00 = FWRO + 0x5118, + FWPGFCSTM10 = FWRO + 0x511c, + FWPGFCTC0 = FWRO + 0x5120, + FWPGFCTM0 = FWRO + 0x5124, + FWPGFHCC0 = FWRO + 0x5128, + FWPGFSM0 = FWRO + 0x512c, + FWPGFGC0 = FWRO + 0x5130, + FWPGFGL0 = FWRO + 0x5500, + FWPGFGL1 = FWRO + 0x5504, + FWPGFGLR = FWRO + 0x5518, + FWPGFGR = FWRO + 0x5510, + FWPGFGRR0 = FWRO + 0x5514, + FWPGFGRR1 = FWRO + 0x5518, + FWPGFRIM = FWRO + 0x5520, + FWPMTRFC0 = FWRO + 0x5600, + FWPMTRCBSC0 = FWRO + 0x5604, + FWPMTRC0RC0 = FWRO + 0x5608, + FWPMTREBSC0 = FWRO + 0x560c, + FWPMTREIRC0 = FWRO + 0x5610, + FWPMTRFM0 = FWRO + 0x5614, + FWFTL0 = FWRO + 0x6000, + FWFTL1 = FWRO + 0x6004, + FWFTLR = FWRO + 0x6008, + FWFTOC = FWRO + 0x6010, + FWFTOPC = FWRO + 0x6014, + FWFTIM = FWRO + 0x6020, + FWFTR = FWRO + 0x6030, + FWFTRR0 = FWRO + 0x6034, + FWFTRR1 = FWRO + 0x6038, + FWFTRR2 = FWRO + 0x603c, + FWSEQNGC0 = FWRO + 0x6100, + FWSEQNGM0 = FWRO + 0x6104, + FWSEQNRC = FWRO + 0x6200, + FWCTFDCN0 = FWRO + 0x6300, + FWLTHFDCN0 = FWRO + 0x6304, + FWIPFDCN0 = FWRO + 0x6308, + FWLTWFDCN0 = FWRO + 0x630c, + FWPBFDCN0 = FWRO + 0x6310, + FWMHLCN0 = FWRO + 0x6314, + FWIHLCN0 = FWRO + 0x6318, + FWICRDCN0 = FWRO + 0x6500, + FWWMRDCN0 = FWRO + 0x6504, + FWCTRDCN0 = FWRO + 0x6508, + FWLTHRDCN0 = FWRO + 0x650c, + FWIPRDCN0 = FWRO + 0x6510, + FWLTWRDCN0 = FWRO + 0x6514, + FWPBRDCN0 = FWRO + 0x6518, + FWPMFDCN0 = FWRO + 0x6700, + FWPGFDCN0 = FWRO + 0x6780, + FWPMGDCN0 = FWRO + 0x6800, + FWPMYDCN0 = FWRO + 0x6804, + FWPMRDCN0 = FWRO + 0x6808, + FWFRPPCN0 = FWRO + 0x6a00, + FWFRDPCN0 = FWRO + 0x6a04, + FWEIS00 = FWRO + 0x7900, + FWEIE00 = FWRO + 0x7904, + FWEID00 = FWRO + 0x7908, + FWEIS1 = FWRO + 0x7a00, + FWEIE1 = FWRO + 0x7a04, + FWEID1 = FWRO + 0x7a08, + FWEIS2 = FWRO + 0x7a10, + FWEIE2 = FWRO + 0x7a14, + FWEID2 = FWRO + 0x7a18, + FWEIS3 = FWRO + 0x7a20, + FWEIE3 = FWRO + 0x7a24, + FWEID3 = FWRO + 0x7a28, + FWEIS4 = FWRO + 0x7a30, + FWEIE4 = FWRO + 0x7a34, + FWEID4 = FWRO + 0x7a38, + FWEIS5 = FWRO + 0x7a40, + FWEIE5 = FWRO + 0x7a44, + FWEID5 = FWRO + 0x7a48, + FWEIS60 = FWRO + 0x7a50, + FWEIE60 = FWRO + 0x7a54, + FWEID60 = FWRO + 0x7a58, + FWEIS61 = FWRO + 0x7a60, + FWEIE61 = FWRO + 0x7a64, + FWEID61 = FWRO + 0x7a68, + FWEIS62 = FWRO + 0x7a70, + FWEIE62 = FWRO + 0x7a74, + FWEID62 = FWRO + 0x7a78, + FWEIS63 = FWRO + 0x7a80, + FWEIE63 = FWRO + 0x7a84, + FWEID63 = FWRO + 0x7a88, + FWEIS70 = FWRO + 0x7a90, + FWEIE70 = FWRO + 0x7A94, + FWEID70 = FWRO + 0x7a98, + FWEIS71 = FWRO + 0x7aa0, + FWEIE71 = FWRO + 0x7aa4, + FWEID71 = FWRO + 0x7aa8, + FWEIS72 = FWRO + 0x7ab0, + FWEIE72 = FWRO + 0x7ab4, + FWEID72 = FWRO + 0x7ab8, + FWEIS73 = FWRO + 0x7ac0, + FWEIE73 = FWRO + 0x7ac4, + FWEID73 = FWRO + 0x7ac8, + FWEIS80 = FWRO + 0x7ad0, + FWEIE80 = FWRO + 0x7ad4, + FWEID80 = FWRO + 0x7ad8, + FWEIS81 = FWRO + 0x7ae0, + FWEIE81 = FWRO + 0x7ae4, + FWEID81 = FWRO + 0x7ae8, + FWEIS82 = FWRO + 0x7af0, + FWEIE82 = FWRO + 0x7af4, + FWEID82 = FWRO + 0x7af8, + FWEIS83 = FWRO + 0x7b00, + FWEIE83 = FWRO + 0x7b04, + FWEID83 = FWRO + 0x7b08, + FWMIS0 = FWRO + 0x7c00, + FWMIE0 = FWRO + 0x7c04, + FWMID0 = FWRO + 0x7c08, + FWSCR0 = FWRO + 0x7d00, + FWSCR1 = FWRO + 0x7d04, + FWSCR2 = FWRO + 0x7d08, + FWSCR3 = FWRO + 0x7d0c, + FWSCR4 = FWRO + 0x7d10, + FWSCR5 = FWRO + 0x7d14, + FWSCR6 = FWRO + 0x7d18, + FWSCR7 = FWRO + 0x7d1c, + FWSCR8 = FWRO + 0x7d20, + FWSCR9 = FWRO + 0x7d24, + FWSCR10 = FWRO + 0x7d28, + FWSCR11 = FWRO + 0x7d2c, + FWSCR12 = FWRO + 0x7d30, + FWSCR13 = FWRO + 0x7d34, + FWSCR14 = FWRO + 0x7d38, + FWSCR15 = FWRO + 0x7d3c, + FWSCR16 = FWRO + 0x7d40, + FWSCR17 = FWRO + 0x7d44, + FWSCR18 = FWRO + 0x7d48, + FWSCR19 = FWRO + 0x7d4c, + FWSCR20 = FWRO + 0x7d50, + FWSCR21 = FWRO + 0x7d54, + FWSCR22 = FWRO + 0x7d58, + FWSCR23 = FWRO + 0x7d5c, + FWSCR24 = FWRO + 0x7d60, + FWSCR25 = FWRO + 0x7d64, + FWSCR26 = FWRO + 0x7d68, + FWSCR27 = FWRO + 0x7d6c, + FWSCR28 = FWRO + 0x7d70, + FWSCR29 = FWRO + 0x7d74, + FWSCR30 = FWRO + 0x7d78, + FWSCR31 = FWRO + 0x7d7c, + FWSCR32 = FWRO + 0x7d80, + FWSCR33 = FWRO + 0x7d84, + FWSCR34 = FWRO + 0x7d88, + FWSCR35 = FWRO + 0x7d8c, + FWSCR36 = FWRO + 0x7d90, + FWSCR37 = FWRO + 0x7d94, + FWSCR38 = FWRO + 0x7d98, + FWSCR39 = FWRO + 0x7d9c, + FWSCR40 = FWRO + 0x7da0, + FWSCR41 = FWRO + 0x7da4, + FWSCR42 = FWRO + 0x7da8, + FWSCR43 = FWRO + 0x7dac, + FWSCR44 = FWRO + 0x7db0, + FWSCR45 = FWRO + 0x7db4, + FWSCR46 = FWRO + 0x7db8, + + TPEMIMC0 = TPRO + 0x0000, + TPEMIMC1 = TPRO + 0x0004, + TPEMIMC2 = TPRO + 0x0008, + TPEMIMC3 = TPRO + 0x000c, + TPEMIMC4 = TPRO + 0x0010, + TPEMIMC5 = TPRO + 0x0014, + TPEMIMC60 = TPRO + 0x0080, + TPEMIMC70 = TPRO + 0x0100, + TSIM = TPRO + 0x0700, + TFIM = TPRO + 0x0704, + TCIM = TPRO + 0x0708, + TGIM0 = TPRO + 0x0710, + TGIM1 = TPRO + 0x0714, + TEIM0 = TPRO + 0x0720, + TEIM1 = TPRO + 0x0724, + TEIM2 = TPRO + 0x0728, + + RIPV = CARO + 0x0000, + RRC = CARO + 0x0004, + RCEC = CARO + 0x0008, + RCDC = CARO + 0x000c, + RSSIS = CARO + 0x0010, + RSSIE = CARO + 0x0014, + RSSID = CARO + 0x0018, + CABPIBWMC = CARO + 0x0020, + CABPWMLC = CARO + 0x0040, + CABPPFLC0 = CARO + 0x0050, + CABPPWMLC0 = CARO + 0x0060, + CABPPPFLC00 = CARO + 0x00a0, + CABPULC = CARO + 0x0100, + CABPIRM = CARO + 0x0140, + CABPPCM = CARO + 0x0144, + CABPLCM = CARO + 0x0148, + CABPCPM = CARO + 0x0180, + CABPMCPM = CARO + 0x0200, + CARDNM = CARO + 0x0280, + CARDMNM = CARO + 0x0284, + CARDCN = CARO + 0x0290, + CAEIS0 = CARO + 0x0300, + CAEIE0 = CARO + 0x0304, + CAEID0 = CARO + 0x0308, + CAEIS1 = CARO + 0x0310, + CAEIE1 = CARO + 0x0314, + CAEID1 = CARO + 0x0318, + CAMIS0 = CARO + 0x0340, + CAMIE0 = CARO + 0x0344, + CAMID0 = CARO + 0x0348, + CAMIS1 = CARO + 0x0350, + CAMIE1 = CARO + 0x0354, + CAMID1 = CARO + 0x0358, + CASCR = CARO + 0x0380, + + EAMC = TARO + 0x0000, + EAMS = TARO + 0x0004, + EAIRC = TARO + 0x0010, + EATDQSC = TARO + 0x0014, + EATDQC = TARO + 0x0018, + EATDQAC = TARO + 0x001c, + EATPEC = TARO + 0x0020, + EATMFSC0 = TARO + 0x0040, + EATDQDC0 = TARO + 0x0060, + EATDQM0 = TARO + 0x0080, + EATDQMLM0 = TARO + 0x00a0, + EACTQC = TARO + 0x0100, + EACTDQDC = TARO + 0x0104, + EACTDQM = TARO + 0x0108, + EACTDQMLM = TARO + 0x010c, + EAVCC = TARO + 0x0130, + EAVTC = TARO + 0x0134, + EATTFC = TARO + 0x0138, + EACAEC = TARO + 0x0200, + EACC = TARO + 0x0204, + EACAIVC0 = TARO + 0x0220, + EACAULC0 = TARO + 0x0240, + EACOEM = TARO + 0x0260, + EACOIVM0 = TARO + 0x0280, + EACOULM0 = TARO + 0x02a0, + EACGSM = TARO + 0x02c0, + EATASC = TARO + 0x0300, + EATASENC0 = TARO + 0x0320, + EATASCTENC = TARO + 0x0340, + EATASENM0 = TARO + 0x0360, + EATASCTENM = TARO + 0x0380, + EATASCSTC0 = TARO + 0x03a0, + EATASCSTC1 = TARO + 0x03a4, + EATASCSTM0 = TARO + 0x03a8, + EATASCSTM1 = TARO + 0x03ac, + EATASCTC = TARO + 0x03b0, + EATASCTM = TARO + 0x03b4, + EATASGL0 = TARO + 0x03c0, + EATASGL1 = TARO + 0x03c4, + EATASGLR = TARO + 0x03c8, + EATASGR = TARO + 0x03d0, + EATASGRR = TARO + 0x03d4, + EATASHCC = TARO + 0x03e0, + EATASRIRM = TARO + 0x03e4, + EATASSM = TARO + 0x03e8, + EAUSMFSECN = TARO + 0x0400, + EATFECN = TARO + 0x0404, + EAFSECN = TARO + 0x0408, + EADQOECN = TARO + 0x040c, + EADQSECN = TARO + 0x0410, + EACKSECN = TARO + 0x0414, + EAEIS0 = TARO + 0x0500, + EAEIE0 = TARO + 0x0504, + EAEID0 = TARO + 0x0508, + EAEIS1 = TARO + 0x0510, + EAEIE1 = TARO + 0x0514, + EAEID1 = TARO + 0x0518, + EAEIS2 = TARO + 0x0520, + EAEIE2 = TARO + 0x0524, + EAEID2 = TARO + 0x0528, + EASCR = TARO + 0x0580, + + MPSM = RMRO + 0x0000, + MPIC = RMRO + 0x0004, + MPIM = RMRO + 0x0008, + MIOC = RMRO + 0x0010, + MIOM = RMRO + 0x0014, + MXMS = RMRO + 0x0018, + MTFFC = RMRO + 0x0020, + MTPFC = RMRO + 0x0024, + MTPFC2 = RMRO + 0x0028, + MTPFC30 = RMRO + 0x0030, + MTATC0 = RMRO + 0x0050, + MTIM = RMRO + 0x0060, + MRGC = RMRO + 0x0080, + MRMAC0 = RMRO + 0x0084, + MRMAC1 = RMRO + 0x0088, + MRAFC = RMRO + 0x008c, + MRSCE = RMRO + 0x0090, + MRSCP = RMRO + 0x0094, + MRSCC = RMRO + 0x0098, + MRFSCE = RMRO + 0x009c, + MRFSCP = RMRO + 0x00a0, + MTRC = RMRO + 0x00a4, + MRIM = RMRO + 0x00a8, + MRPFM = RMRO + 0x00ac, + MPFC0 = RMRO + 0x0100, + MLVC = RMRO + 0x0180, + MEEEC = RMRO + 0x0184, + MLBC = RMRO + 0x0188, + MXGMIIC = RMRO + 0x0190, + MPCH = RMRO + 0x0194, + MANC = RMRO + 0x0198, + MANM = RMRO + 0x019c, + MPLCA1 = RMRO + 0x01a0, + MPLCA2 = RMRO + 0x01a4, + MPLCA3 = RMRO + 0x01a8, + MPLCA4 = RMRO + 0x01ac, + MPLCAM = RMRO + 0x01b0, + MHDC1 = RMRO + 0x01c0, + MHDC2 = RMRO + 0x01c4, + MEIS = RMRO + 0x0200, + MEIE = RMRO + 0x0204, + MEID = RMRO + 0x0208, + MMIS0 = RMRO + 0x0210, + MMIE0 = RMRO + 0x0214, + MMID0 = RMRO + 0x0218, + MMIS1 = RMRO + 0x0220, + MMIE1 = RMRO + 0x0224, + MMID1 = RMRO + 0x0228, + MMIS2 = RMRO + 0x0230, + MMIE2 = RMRO + 0x0234, + MMID2 = RMRO + 0x0238, + MMPFTCT = RMRO + 0x0300, + MAPFTCT = RMRO + 0x0304, + MPFRCT = RMRO + 0x0308, + MFCICT = RMRO + 0x030c, + MEEECT = RMRO + 0x0310, + MMPCFTCT0 = RMRO + 0x0320, + MAPCFTCT0 = RMRO + 0x0330, + MPCFRCT0 = RMRO + 0x0340, + MHDCC = RMRO + 0x0350, + MROVFC = RMRO + 0x0354, + MRHCRCEC = RMRO + 0x0358, + MRXBCE = RMRO + 0x0400, + MRXBCP = RMRO + 0x0404, + MRGFCE = RMRO + 0x0408, + MRGFCP = RMRO + 0x040c, + MRBFC = RMRO + 0x0410, + MRMFC = RMRO + 0x0414, + MRUFC = RMRO + 0x0418, + MRPEFC = RMRO + 0x041c, + MRNEFC = RMRO + 0x0420, + MRFMEFC = RMRO + 0x0424, + MRFFMEFC = RMRO + 0x0428, + MRCFCEFC = RMRO + 0x042c, + MRFCEFC = RMRO + 0x0430, + MRRCFEFC = RMRO + 0x0434, + MRUEFC = RMRO + 0x043c, + MROEFC = RMRO + 0x0440, + MRBOEC = RMRO + 0x0444, + MTXBCE = RMRO + 0x0500, + MTXBCP = RMRO + 0x0504, + MTGFCE = RMRO + 0x0508, + MTGFCP = RMRO + 0x050c, + MTBFC = RMRO + 0x0510, + MTMFC = RMRO + 0x0514, + MTUFC = RMRO + 0x0518, + MTEFC = RMRO + 0x051c, + + GWMC = GWRO + 0x0000, + GWMS = GWRO + 0x0004, + GWIRC = GWRO + 0x0010, + GWRDQSC = GWRO + 0x0014, + GWRDQC = GWRO + 0x0018, + GWRDQAC = GWRO + 0x001c, + GWRGC = GWRO + 0x0020, + GWRMFSC0 = GWRO + 0x0040, + GWRDQDC0 = GWRO + 0x0060, + GWRDQM0 = GWRO + 0x0080, + GWRDQMLM0 = GWRO + 0x00a0, + GWMTIRM = GWRO + 0x0100, + GWMSTLS = GWRO + 0x0104, + GWMSTLR = GWRO + 0x0108, + GWMSTSS = GWRO + 0x010c, + GWMSTSR = GWRO + 0x0110, + GWMAC0 = GWRO + 0x0120, + GWMAC1 = GWRO + 0x0124, + GWVCC = GWRO + 0x0130, + GWVTC = GWRO + 0x0134, + GWTTFC = GWRO + 0x0138, + GWTDCAC00 = GWRO + 0x0140, + GWTDCAC10 = GWRO + 0x0144, + GWTSDCC0 = GWRO + 0x0160, + GWTNM = GWRO + 0x0180, + GWTMNM = GWRO + 0x0184, + GWAC = GWRO + 0x0190, + GWDCBAC0 = GWRO + 0x0194, + GWDCBAC1 = GWRO + 0x0198, + GWIICBSC = GWRO + 0x019c, + GWMDNC = GWRO + 0x01a0, + GWTRC0 = GWRO + 0x0200, + GWTPC0 = GWRO + 0x0300, + GWARIRM = GWRO + 0x0380, + GWDCC0 = GWRO + 0x0400, + GWAARSS = GWRO + 0x0800, + GWAARSR0 = GWRO + 0x0804, + GWAARSR1 = GWRO + 0x0808, + GWIDAUAS0 = GWRO + 0x0840, + GWIDASM0 = GWRO + 0x0880, + GWIDASAM00 = GWRO + 0x0900, + GWIDASAM10 = GWRO + 0x0904, + GWIDACAM00 = GWRO + 0x0980, + GWIDACAM10 = GWRO + 0x0984, + GWGRLC = GWRO + 0x0a00, + GWGRLULC = GWRO + 0x0a04, + GWRLIVC0 = GWRO + 0x0a80, + GWRLULC0 = GWRO + 0x0a84, + GWIDPC = GWRO + 0x0b00, + GWIDC0 = GWRO + 0x0c00, + GWDIS0 = GWRO + 0x1100, + GWDIE0 = GWRO + 0x1104, + GWDID0 = GWRO + 0x1108, + GWTSDIS = GWRO + 0x1180, + GWTSDIE = GWRO + 0x1184, + GWTSDID = GWRO + 0x1188, + GWEIS0 = GWRO + 0x1190, + GWEIE0 = GWRO + 0x1194, + GWEID0 = GWRO + 0x1198, + GWEIS1 = GWRO + 0x11a0, + GWEIE1 = GWRO + 0x11a4, + GWEID1 = GWRO + 0x11a8, + GWEIS20 = GWRO + 0x1200, + GWEIE20 = GWRO + 0x1204, + GWEID20 = GWRO + 0x1208, + GWEIS3 = GWRO + 0x1280, + GWEIE3 = GWRO + 0x1284, + GWEID3 = GWRO + 0x1288, + GWEIS4 = GWRO + 0x1290, + GWEIE4 = GWRO + 0x1294, + GWEID4 = GWRO + 0x1298, + GWEIS5 = GWRO + 0x12a0, + GWEIE5 = GWRO + 0x12a4, + GWEID5 = GWRO + 0x12a8, + GWSCR0 = GWRO + 0x1800, + GWSCR1 = GWRO + 0x1900, +}; + +/* ETHA/RMAC */ +enum rswitch_etha_mode { + EAMC_OPC_RESET, + EAMC_OPC_DISABLE, + EAMC_OPC_CONFIG, + EAMC_OPC_OPERATION, +}; + +#define EAMS_OPS_MASK EAMC_OPC_OPERATION + +#define EAVCC_VEM_SC_TAG (0x3 << 16) + +#define MPIC_PIS_MII 0x00 +#define MPIC_PIS_GMII 0x02 +#define MPIC_PIS_XGMII 0x04 +#define MPIC_LSC_SHIFT 3 +#define MPIC_LSC_100M (1 << MPIC_LSC_SHIFT) +#define MPIC_LSC_1G (2 << MPIC_LSC_SHIFT) +#define MPIC_LSC_2_5G (3 << MPIC_LSC_SHIFT) + +#define MDIO_READ_C45 0x03 +#define MDIO_WRITE_C45 0x01 + +#define MPSM_PSME BIT(0) +#define MPSM_MFF_C45 BIT(2) +#define MPSM_PRD_SHIFT 16 +#define MPSM_PRD_MASK GENMASK(31, MPSM_PRD_SHIFT) + +/* Completion flags */ +#define MMIS1_PAACS BIT(2) /* Address */ +#define MMIS1_PWACS BIT(1) /* Write */ +#define MMIS1_PRACS BIT(0) /* Read */ +#define MMIS1_CLEAR_FLAGS 0xf + +#define MPIC_PSMCS_SHIFT 16 +#define MPIC_PSMCS_MASK GENMASK(22, MPIC_PSMCS_SHIFT) +#define MPIC_PSMCS(val) ((val) << MPIC_PSMCS_SHIFT) + +#define MPIC_PSMHT_SHIFT 24 +#define MPIC_PSMHT_MASK GENMASK(26, MPIC_PSMHT_SHIFT) +#define MPIC_PSMHT(val) ((val) << MPIC_PSMHT_SHIFT) + +#define MLVC_PLV BIT(16) + +/* GWCA */ +enum rswitch_gwca_mode { + GWMC_OPC_RESET, + GWMC_OPC_DISABLE, + GWMC_OPC_CONFIG, + GWMC_OPC_OPERATION, +}; + +#define GWMS_OPS_MASK GWMC_OPC_OPERATION + +#define GWMTIRM_MTIOG BIT(0) +#define GWMTIRM_MTR BIT(1) + +#define GWVCC_VEM_SC_TAG (0x3 << 16) + +#define GWARIRM_ARIOG BIT(0) +#define GWARIRM_ARR BIT(1) + +#define GWDCC_BALR BIT(24) +#define GWDCC_DQT BIT(11) +#define GWDCC_ETS BIT(9) +#define GWDCC_EDE BIT(8) + +#define GWTRC(queue) (GWTRC0 + (queue) / 32 * 4) +#define GWDCC_OFFS(queue) (GWDCC0 + (queue) * 4) + +#define GWDIS(i) (GWDIS0 + (i) * 0x10) +#define GWDIE(i) (GWDIE0 + (i) * 0x10) +#define GWDID(i) (GWDID0 + (i) * 0x10) + +/* COMA */ +#define RRC_RR BIT(0) +#define RRC_RR_CLR 0 +#define RCEC_ACE_DEFAULT (BIT(0) | BIT(AGENT_INDEX_GWCA)) +#define RCEC_RCE BIT(16) +#define RCDC_RCD BIT(16) + +#define CABPIRM_BPIOG BIT(0) +#define CABPIRM_BPR BIT(1) + +/* MFWD */ +#define FWPC0_LTHTA BIT(0) +#define FWPC0_IP4UE BIT(3) +#define FWPC0_IP4TE BIT(4) +#define FWPC0_IP4OE BIT(5) +#define FWPC0_L2SE BIT(9) +#define FWPC0_IP4EA BIT(10) +#define FWPC0_IPDSA BIT(12) +#define FWPC0_IPHLA BIT(18) +#define FWPC0_MACSDA BIT(20) +#define FWPC0_MACHLA BIT(26) +#define FWPC0_MACHMA BIT(27) +#define FWPC0_VLANSA BIT(28) + +#define FWPC0(i) (FWPC00 + (i) * 0x10) +#define FWPC0_DEFAULT (FWPC0_LTHTA | FWPC0_IP4UE | FWPC0_IP4TE | \ + FWPC0_IP4OE | FWPC0_L2SE | FWPC0_IP4EA | \ + FWPC0_IPDSA | FWPC0_IPHLA | FWPC0_MACSDA | \ + FWPC0_MACHLA | FWPC0_MACHMA | FWPC0_VLANSA) +#define FWPC1(i) (FWPC10 + (i) * 0x10) +#define FWPC1_DDE BIT(0) + +#define FWPBFC(i) (FWPBFC0 + (i) * 0x10) + +#define FWPBFCSDC(j, i) (FWPBFCSDC00 + (i) * 0x10 + (j) * 0x04) + +/* TOP */ +#define TPEMIMC7(queue) (TPEMIMC70 + (queue) * 4) + +/* Descriptors */ +enum RX_DS_CC_BIT { + RX_DS = 0x0fff, /* Data size */ + RX_TR = 0x1000, /* Truncation indication */ + RX_EI = 0x2000, /* Error indication */ + RX_PS = 0xc000, /* Padding selection */ +}; + +enum TX_DS_TAGL_BIT { + TX_DS = 0x0fff, /* Data size */ + TX_TAGL = 0xf000, /* Frame tag LSBs */ +}; + +enum DIE_DT { + /* Frame data */ + DT_FSINGLE = 0x80, + DT_FSTART = 0x90, + DT_FMID = 0xa0, + DT_FEND = 0xb8, + + /* Chain control */ + DT_LEMPTY = 0xc0, + DT_EEMPTY = 0xd0, + DT_LINKFIX = 0x00, + DT_LINK = 0xe0, + DT_EOS = 0xf0, + /* HW/SW arbitration */ + DT_FEMPTY = 0x40, + DT_FEMPTY_IS = 0x10, + DT_FEMPTY_IC = 0x20, + DT_FEMPTY_ND = 0x38, + DT_FEMPTY_START = 0x50, + DT_FEMPTY_MID = 0x60, + DT_FEMPTY_END = 0x70, + + DT_MASK = 0xf0, + DIE = 0x08, /* Descriptor Interrupt Enable */ +}; + +/* Both transmission and reception */ +#define INFO1_FMT BIT(2) +#define INFO1_TXC BIT(3) + +/* For transmission */ +#define INFO1_TSUN(val) ((u64)(val) << 8ULL) +#define INFO1_CSD0(index) ((u64)(index) << 32ULL) +#define INFO1_CSD1(index) ((u64)(index) << 40ULL) +#define INFO1_DV(port_vector) ((u64)(port_vector) << 48ULL) + +/* For reception */ +#define INFO1_SPN(port) ((u64)(port) << 36ULL) + +struct rswitch_desc { + __le16 info_ds; /* Descriptor size */ + u8 die_dt; /* Descriptor interrupt enable and type */ + __u8 dptrh; /* Descriptor pointer MSB */ + __le32 dptrl; /* Descriptor pointer LSW */ +} __packed; + +struct rswitch_ts_desc { + struct rswitch_desc desc; + __le32 ts_nsec; + __le32 ts_sec; +} __packed; + +struct rswitch_ext_desc { + struct rswitch_desc desc; + __le64 info1; +} __packed; + +struct rswitch_ext_ts_desc { + struct rswitch_desc desc; + __le64 info1; + __le32 ts_nsec; + __le32 ts_sec; +} __packed; + +struct rswitch_etha { + int index; + void __iomem *addr; + void __iomem *coma_addr; + bool external_phy; + struct mii_bus *mii; + phy_interface_t phy_interface; + u8 mac_addr[MAX_ADDR_LEN]; + int link; + int speed; + + /* This hardware could not be initialized twice so that marked + * this flag to avoid multiple initialization. + */ + bool operated; +}; + +/* The datasheet said descriptor "chain" and/or "queue". For consistency of + * name, this driver calls "queue". + */ +struct rswitch_gwca_queue { + int index; + bool dir_tx; + bool gptp; + union { + struct rswitch_ext_desc *ring; + struct rswitch_ext_ts_desc *ts_ring; + }; + dma_addr_t ring_dma; + u32 ring_size; + u32 cur; + u32 dirty; + struct sk_buff **skbs; + + struct net_device *ndev; /* queue to ndev for irq */ +}; + +#define RSWITCH_NUM_IRQ_REGS (RSWITCH_MAX_NUM_QUEUES / BITS_PER_TYPE(u32)) +struct rswitch_gwca { + int index; + struct rswitch_gwca_queue *queues; + int num_queues; + DECLARE_BITMAP(used, RSWITCH_MAX_NUM_QUEUES); + u32 tx_irq_bits[RSWITCH_NUM_IRQ_REGS]; + u32 rx_irq_bits[RSWITCH_NUM_IRQ_REGS]; + int speed; +}; + +#define NUM_QUEUES_PER_NDEV 2 +struct rswitch_device { + struct rswitch_private *priv; + struct net_device *ndev; + struct napi_struct napi; + struct phylink *phylink; + struct phylink_config phylink_config; + void __iomem *addr; + struct rswitch_gwca_queue *tx_queue; + struct rswitch_gwca_queue *rx_queue; + u8 ts_tag; + + int port; + struct rswitch_etha *etha; +}; + +struct rswitch_mfwd_mac_table_entry { + int queue_index; + unsigned char addr[MAX_ADDR_LEN]; +}; + +struct rswitch_mfwd { + struct rswitch_mac_table_entry *mac_table_entries; + int num_mac_table_entries; +}; + +struct rswitch_private { + struct platform_device *pdev; + void __iomem *addr; + struct rcar_gen4_ptp_private *ptp_priv; + struct rswitch_desc *linkfix_table; + dma_addr_t linkfix_table_dma; + u32 linkfix_table_size; + + struct rswitch_device *rdev[RSWITCH_NUM_PORTS]; + + struct rswitch_gwca gwca; + struct rswitch_etha etha[RSWITCH_NUM_PORTS]; + struct rswitch_mfwd mfwd; + + bool gwca_halt; +}; + +#endif /* #ifndef __RSWITCH_H__ */ From 6c6fa1a00ad3df994c96af9468cc83f225df9df4 Mon Sep 17 00:00:00 2001 From: Yoshihiro Shimoda Date: Mon, 31 Oct 2022 21:32:42 +0900 Subject: [PATCH 0538/2002] net: ethernet: renesas: rswitch: Add R-Car Gen4 gPTP support Add R-Car Gen4 gPTP support into the rswitch driver. Signed-off-by: Yoshihiro Shimoda Signed-off-by: David S. Miller --- drivers/net/ethernet/renesas/Makefile | 2 +- drivers/net/ethernet/renesas/rcar_gen4_ptp.c | 181 +++++++++++++++++++ drivers/net/ethernet/renesas/rcar_gen4_ptp.h | 72 ++++++++ drivers/net/ethernet/renesas/rswitch.c | 144 ++++++++++++++- drivers/net/ethernet/renesas/rswitch.h | 1 + 5 files changed, 398 insertions(+), 2 deletions(-) create mode 100644 drivers/net/ethernet/renesas/rcar_gen4_ptp.c create mode 100644 drivers/net/ethernet/renesas/rcar_gen4_ptp.h diff --git a/drivers/net/ethernet/renesas/Makefile b/drivers/net/ethernet/renesas/Makefile index 32845ba53970..592005893464 100644 --- a/drivers/net/ethernet/renesas/Makefile +++ b/drivers/net/ethernet/renesas/Makefile @@ -9,6 +9,6 @@ ravb-objs := ravb_main.o ravb_ptp.o obj-$(CONFIG_RAVB) += ravb.o -rswitch_drv-objs := rswitch.o +rswitch_drv-objs := rswitch.o rcar_gen4_ptp.o obj-$(CONFIG_RENESAS_ETHER_SWITCH) += rswitch_drv.o diff --git a/drivers/net/ethernet/renesas/rcar_gen4_ptp.c b/drivers/net/ethernet/renesas/rcar_gen4_ptp.c new file mode 100644 index 000000000000..c007e33c47e1 --- /dev/null +++ b/drivers/net/ethernet/renesas/rcar_gen4_ptp.c @@ -0,0 +1,181 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Renesas R-Car Gen4 gPTP device driver + * + * Copyright (C) 2022 Renesas Electronics Corporation + */ + +#include +#include +#include +#include +#include +#include + +#include "rcar_gen4_ptp.h" +#define ptp_to_priv(ptp) container_of(ptp, struct rcar_gen4_ptp_private, info) + +static const struct rcar_gen4_ptp_reg_offset s4_offs = { + .enable = PTPTMEC, + .disable = PTPTMDC, + .increment = PTPTIVC0, + .config_t0 = PTPTOVC00, + .config_t1 = PTPTOVC10, + .config_t2 = PTPTOVC20, + .monitor_t0 = PTPGPTPTM00, + .monitor_t1 = PTPGPTPTM10, + .monitor_t2 = PTPGPTPTM20, +}; + +static int rcar_gen4_ptp_adjfine(struct ptp_clock_info *ptp, long scaled_ppm) +{ + struct rcar_gen4_ptp_private *ptp_priv = ptp_to_priv(ptp); + bool neg_adj = scaled_ppm < 0 ? true : false; + s64 addend = ptp_priv->default_addend; + s64 diff; + + if (neg_adj) + scaled_ppm = -scaled_ppm; + diff = div_s64(addend * scaled_ppm_to_ppb(scaled_ppm), NSEC_PER_SEC); + addend = neg_adj ? addend - diff : addend + diff; + + iowrite32(addend, ptp_priv->addr + ptp_priv->offs->increment); + + return 0; +} + +/* Caller must hold the lock */ +static void _rcar_gen4_ptp_gettime(struct ptp_clock_info *ptp, + struct timespec64 *ts) +{ + struct rcar_gen4_ptp_private *ptp_priv = ptp_to_priv(ptp); + + ts->tv_nsec = ioread32(ptp_priv->addr + ptp_priv->offs->monitor_t0); + ts->tv_sec = ioread32(ptp_priv->addr + ptp_priv->offs->monitor_t1) | + ((s64)ioread32(ptp_priv->addr + ptp_priv->offs->monitor_t2) << 32); +} + +static int rcar_gen4_ptp_gettime(struct ptp_clock_info *ptp, + struct timespec64 *ts) +{ + struct rcar_gen4_ptp_private *ptp_priv = ptp_to_priv(ptp); + unsigned long flags; + + spin_lock_irqsave(&ptp_priv->lock, flags); + _rcar_gen4_ptp_gettime(ptp, ts); + spin_unlock_irqrestore(&ptp_priv->lock, flags); + + return 0; +} + +/* Caller must hold the lock */ +static void _rcar_gen4_ptp_settime(struct ptp_clock_info *ptp, + const struct timespec64 *ts) +{ + struct rcar_gen4_ptp_private *ptp_priv = ptp_to_priv(ptp); + + iowrite32(1, ptp_priv->addr + ptp_priv->offs->disable); + iowrite32(0, ptp_priv->addr + ptp_priv->offs->config_t2); + iowrite32(0, ptp_priv->addr + ptp_priv->offs->config_t1); + iowrite32(0, ptp_priv->addr + ptp_priv->offs->config_t0); + iowrite32(1, ptp_priv->addr + ptp_priv->offs->enable); + iowrite32(ts->tv_sec >> 32, ptp_priv->addr + ptp_priv->offs->config_t2); + iowrite32(ts->tv_sec, ptp_priv->addr + ptp_priv->offs->config_t1); + iowrite32(ts->tv_nsec, ptp_priv->addr + ptp_priv->offs->config_t0); +} + +static int rcar_gen4_ptp_settime(struct ptp_clock_info *ptp, + const struct timespec64 *ts) +{ + struct rcar_gen4_ptp_private *ptp_priv = ptp_to_priv(ptp); + unsigned long flags; + + spin_lock_irqsave(&ptp_priv->lock, flags); + _rcar_gen4_ptp_settime(ptp, ts); + spin_unlock_irqrestore(&ptp_priv->lock, flags); + + return 0; +} + +static int rcar_gen4_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta) +{ + struct rcar_gen4_ptp_private *ptp_priv = ptp_to_priv(ptp); + struct timespec64 ts; + unsigned long flags; + s64 now; + + spin_lock_irqsave(&ptp_priv->lock, flags); + _rcar_gen4_ptp_gettime(ptp, &ts); + now = ktime_to_ns(timespec64_to_ktime(ts)); + ts = ns_to_timespec64(now + delta); + _rcar_gen4_ptp_settime(ptp, &ts); + spin_unlock_irqrestore(&ptp_priv->lock, flags); + + return 0; +} + +static int rcar_gen4_ptp_enable(struct ptp_clock_info *ptp, + struct ptp_clock_request *rq, int on) +{ + return -EOPNOTSUPP; +} + +static struct ptp_clock_info rcar_gen4_ptp_info = { + .owner = THIS_MODULE, + .name = "rcar_gen4_ptp", + .max_adj = 50000000, + .adjfine = rcar_gen4_ptp_adjfine, + .adjtime = rcar_gen4_ptp_adjtime, + .gettime64 = rcar_gen4_ptp_gettime, + .settime64 = rcar_gen4_ptp_settime, + .enable = rcar_gen4_ptp_enable, +}; + +static void rcar_gen4_ptp_set_offs(struct rcar_gen4_ptp_private *ptp_priv, + enum rcar_gen4_ptp_reg_layout layout) +{ + WARN_ON(layout != RCAR_GEN4_PTP_REG_LAYOUT_S4); + + ptp_priv->offs = &s4_offs; +} + +int rcar_gen4_ptp_register(struct rcar_gen4_ptp_private *ptp_priv, + enum rcar_gen4_ptp_reg_layout layout, u32 clock) +{ + if (ptp_priv->initialized) + return 0; + + spin_lock_init(&ptp_priv->lock); + + rcar_gen4_ptp_set_offs(ptp_priv, layout); + + ptp_priv->default_addend = clock; + iowrite32(ptp_priv->default_addend, ptp_priv->addr + ptp_priv->offs->increment); + ptp_priv->clock = ptp_clock_register(&ptp_priv->info, NULL); + if (IS_ERR(ptp_priv->clock)) + return PTR_ERR(ptp_priv->clock); + + iowrite32(0x01, ptp_priv->addr + ptp_priv->offs->enable); + ptp_priv->initialized = true; + + return 0; +} + +int rcar_gen4_ptp_unregister(struct rcar_gen4_ptp_private *ptp_priv) +{ + iowrite32(1, ptp_priv->addr + ptp_priv->offs->disable); + + return ptp_clock_unregister(ptp_priv->clock); +} + +struct rcar_gen4_ptp_private *rcar_gen4_ptp_alloc(struct platform_device *pdev) +{ + struct rcar_gen4_ptp_private *ptp; + + ptp = devm_kzalloc(&pdev->dev, sizeof(*ptp), GFP_KERNEL); + if (!ptp) + return NULL; + + ptp->info = rcar_gen4_ptp_info; + + return ptp; +} diff --git a/drivers/net/ethernet/renesas/rcar_gen4_ptp.h b/drivers/net/ethernet/renesas/rcar_gen4_ptp.h new file mode 100644 index 000000000000..b1bbea8d3a52 --- /dev/null +++ b/drivers/net/ethernet/renesas/rcar_gen4_ptp.h @@ -0,0 +1,72 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Renesas R-Car Gen4 gPTP device driver + * + * Copyright (C) 2022 Renesas Electronics Corporation + */ + +#ifndef __RCAR_GEN4_PTP_H__ +#define __RCAR_GEN4_PTP_H__ + +#include + +#define PTPTIVC_INIT 0x19000000 /* 320MHz */ +#define RCAR_GEN4_PTP_CLOCK_S4 PTPTIVC_INIT +#define RCAR_GEN4_GPTP_OFFSET_S4 0x00018000 + +/* for rcar_gen4_ptp_init */ +enum rcar_gen4_ptp_reg_layout { + RCAR_GEN4_PTP_REG_LAYOUT_S4 +}; + +/* driver's definitions */ +#define RCAR_GEN4_RXTSTAMP_ENABLED BIT(0) +#define RCAR_GEN4_RXTSTAMP_TYPE_V2_L2_EVENT BIT(1) +#define RCAR_GEN4_RXTSTAMP_TYPE_ALL (RCAR_GEN4_RXTSTAMP_TYPE_V2_L2_EVENT | BIT(2)) +#define RCAR_GEN4_RXTSTAMP_TYPE RCAR_GEN4_RXTSTAMP_TYPE_ALL + +#define RCAR_GEN4_TXTSTAMP_ENABLED BIT(0) + +#define PTPRO 0 + +enum rcar_gen4_ptp_reg_s4 { + PTPTMEC = PTPRO + 0x0010, + PTPTMDC = PTPRO + 0x0014, + PTPTIVC0 = PTPRO + 0x0020, + PTPTOVC00 = PTPRO + 0x0030, + PTPTOVC10 = PTPRO + 0x0034, + PTPTOVC20 = PTPRO + 0x0038, + PTPGPTPTM00 = PTPRO + 0x0050, + PTPGPTPTM10 = PTPRO + 0x0054, + PTPGPTPTM20 = PTPRO + 0x0058, +}; + +struct rcar_gen4_ptp_reg_offset { + u16 enable; + u16 disable; + u16 increment; + u16 config_t0; + u16 config_t1; + u16 config_t2; + u16 monitor_t0; + u16 monitor_t1; + u16 monitor_t2; +}; + +struct rcar_gen4_ptp_private { + void __iomem *addr; + struct ptp_clock *clock; + struct ptp_clock_info info; + const struct rcar_gen4_ptp_reg_offset *offs; + spinlock_t lock; /* For multiple registers access */ + u32 tstamp_tx_ctrl; + u32 tstamp_rx_ctrl; + s64 default_addend; + bool initialized; +}; + +int rcar_gen4_ptp_register(struct rcar_gen4_ptp_private *ptp_priv, + enum rcar_gen4_ptp_reg_layout layout, u32 clock); +int rcar_gen4_ptp_unregister(struct rcar_gen4_ptp_private *ptp_priv); +struct rcar_gen4_ptp_private *rcar_gen4_ptp_alloc(struct platform_device *pdev); + +#endif /* #ifndef __RCAR_GEN4_PTP_H__ */ diff --git a/drivers/net/ethernet/renesas/rswitch.c b/drivers/net/ethernet/renesas/rswitch.c index 5ca38e6a9e97..20df2020d3e5 100644 --- a/drivers/net/ethernet/renesas/rswitch.c +++ b/drivers/net/ethernet/renesas/rswitch.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -123,6 +124,13 @@ static void rswitch_fwd_init(struct rswitch_private *priv) iowrite32(GENMASK(RSWITCH_NUM_PORTS - 1, 0), priv->addr + FWPBFC(priv->gwca.index)); } +/* gPTP timer (gPTP) */ +static void rswitch_get_timestamp(struct rswitch_private *priv, + struct timespec64 *ts) +{ + priv->ptp_priv->info.gettime64(&priv->ptp_priv->info, ts); +} + /* Gateway CPU agent block (GWCA) */ static int rswitch_gwca_change_mode(struct rswitch_private *priv, enum rswitch_gwca_mode mode) @@ -663,6 +671,7 @@ static bool rswitch_rx(struct net_device *ndev, int *quota) struct sk_buff *skb; dma_addr_t dma_addr; u16 pkt_len; + u32 get_ts; boguscnt = min_t(int, gq->ring_size, *quota); limit = boguscnt; @@ -677,6 +686,17 @@ static bool rswitch_rx(struct net_device *ndev, int *quota) gq->skbs[gq->cur] = NULL; dma_addr = rswitch_desc_get_dptr(&desc->desc); dma_unmap_single(ndev->dev.parent, dma_addr, PKT_BUF_SZ, DMA_FROM_DEVICE); + get_ts = rdev->priv->ptp_priv->tstamp_rx_ctrl & RCAR_GEN4_RXTSTAMP_TYPE_V2_L2_EVENT; + if (get_ts) { + struct skb_shared_hwtstamps *shhwtstamps; + struct timespec64 ts; + + shhwtstamps = skb_hwtstamps(skb); + memset(shhwtstamps, 0, sizeof(*shhwtstamps)); + ts.tv_sec = __le32_to_cpu(desc->ts_sec); + ts.tv_nsec = __le32_to_cpu(desc->ts_nsec & cpu_to_le32(0x3fffffff)); + shhwtstamps->hwtstamp = timespec64_to_ktime(ts); + } skb_put(skb, pkt_len); skb->protocol = eth_type_trans(skb, ndev); netif_receive_skb(skb); @@ -725,6 +745,15 @@ static int rswitch_tx_free(struct net_device *ndev, bool free_txed_only) size = le16_to_cpu(desc->desc.info_ds) & TX_DS; skb = gq->skbs[gq->dirty]; if (skb) { + if (skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP) { + struct skb_shared_hwtstamps shhwtstamps; + struct timespec64 ts; + + rswitch_get_timestamp(rdev->priv, &ts); + memset(&shhwtstamps, 0, sizeof(shhwtstamps)); + shhwtstamps.hwtstamp = timespec64_to_ktime(ts); + skb_tstamp_tx(skb, &shhwtstamps); + } dma_addr = rswitch_desc_get_dptr(&desc->desc); dma_unmap_single(ndev->dev.parent, dma_addr, size, DMA_TO_DEVICE); @@ -1412,6 +1441,75 @@ static struct net_device_stats *rswitch_get_stats(struct net_device *ndev) return &ndev->stats; } +static int rswitch_hwstamp_get(struct net_device *ndev, struct ifreq *req) +{ + struct rswitch_device *rdev = netdev_priv(ndev); + struct rcar_gen4_ptp_private *ptp_priv; + struct hwtstamp_config config; + + ptp_priv = rdev->priv->ptp_priv; + + config.flags = 0; + config.tx_type = ptp_priv->tstamp_tx_ctrl ? HWTSTAMP_TX_ON : + HWTSTAMP_TX_OFF; + switch (ptp_priv->tstamp_rx_ctrl & RCAR_GEN4_RXTSTAMP_TYPE) { + case RCAR_GEN4_RXTSTAMP_TYPE_V2_L2_EVENT: + config.rx_filter = HWTSTAMP_FILTER_PTP_V2_L2_EVENT; + break; + case RCAR_GEN4_RXTSTAMP_TYPE_ALL: + config.rx_filter = HWTSTAMP_FILTER_ALL; + break; + default: + config.rx_filter = HWTSTAMP_FILTER_NONE; + break; + } + + return copy_to_user(req->ifr_data, &config, sizeof(config)) ? -EFAULT : 0; +} + +static int rswitch_hwstamp_set(struct net_device *ndev, struct ifreq *req) +{ + struct rswitch_device *rdev = netdev_priv(ndev); + u32 tstamp_rx_ctrl = RCAR_GEN4_RXTSTAMP_ENABLED; + struct hwtstamp_config config; + u32 tstamp_tx_ctrl; + + if (copy_from_user(&config, req->ifr_data, sizeof(config))) + return -EFAULT; + + if (config.flags) + return -EINVAL; + + switch (config.tx_type) { + case HWTSTAMP_TX_OFF: + tstamp_tx_ctrl = 0; + break; + case HWTSTAMP_TX_ON: + tstamp_tx_ctrl = RCAR_GEN4_TXTSTAMP_ENABLED; + break; + default: + return -ERANGE; + } + + switch (config.rx_filter) { + case HWTSTAMP_FILTER_NONE: + tstamp_rx_ctrl = 0; + break; + case HWTSTAMP_FILTER_PTP_V2_L2_EVENT: + tstamp_rx_ctrl |= RCAR_GEN4_RXTSTAMP_TYPE_V2_L2_EVENT; + break; + default: + config.rx_filter = HWTSTAMP_FILTER_ALL; + tstamp_rx_ctrl |= RCAR_GEN4_RXTSTAMP_TYPE_ALL; + break; + } + + rdev->priv->ptp_priv->tstamp_tx_ctrl = tstamp_tx_ctrl; + rdev->priv->ptp_priv->tstamp_rx_ctrl = tstamp_rx_ctrl; + + return copy_to_user(req->ifr_data, &config, sizeof(config)) ? -EFAULT : 0; +} + static int rswitch_eth_ioctl(struct net_device *ndev, struct ifreq *req, int cmd) { struct rswitch_device *rdev = netdev_priv(ndev); @@ -1419,7 +1517,14 @@ static int rswitch_eth_ioctl(struct net_device *ndev, struct ifreq *req, int cmd if (!netif_running(ndev)) return -EINVAL; - return phylink_mii_ioctl(rdev->phylink, req, cmd); + switch (cmd) { + case SIOCGHWTSTAMP: + return rswitch_hwstamp_get(ndev, req); + case SIOCSHWTSTAMP: + return rswitch_hwstamp_set(ndev, req); + default: + return phylink_mii_ioctl(rdev->phylink, req, cmd); + } } static const struct net_device_ops rswitch_netdev_ops = { @@ -1432,6 +1537,27 @@ static const struct net_device_ops rswitch_netdev_ops = { .ndo_set_mac_address = eth_mac_addr, }; +static int rswitch_get_ts_info(struct net_device *ndev, struct ethtool_ts_info *info) +{ + struct rswitch_device *rdev = netdev_priv(ndev); + + info->phc_index = ptp_clock_index(rdev->priv->ptp_priv->clock); + info->so_timestamping = SOF_TIMESTAMPING_TX_SOFTWARE | + SOF_TIMESTAMPING_RX_SOFTWARE | + SOF_TIMESTAMPING_SOFTWARE | + SOF_TIMESTAMPING_TX_HARDWARE | + SOF_TIMESTAMPING_RX_HARDWARE | + SOF_TIMESTAMPING_RAW_HARDWARE; + info->tx_types = BIT(HWTSTAMP_TX_OFF) | BIT(HWTSTAMP_TX_ON); + info->rx_filters = BIT(HWTSTAMP_FILTER_NONE) | BIT(HWTSTAMP_FILTER_ALL); + + return 0; +} + +static const struct ethtool_ops rswitch_ethtool_ops = { + .get_ts_info = rswitch_get_ts_info, +}; + static const struct of_device_id renesas_eth_sw_of_table[] = { { .compatible = "renesas,r8a779f0-ether-switch", }, { } @@ -1476,6 +1602,7 @@ static int rswitch_device_alloc(struct rswitch_private *priv, int index) ndev->base_addr = (unsigned long)rdev->addr; snprintf(ndev->name, IFNAMSIZ, "tsn%d", index); ndev->netdev_ops = &rswitch_netdev_ops; + ndev->ethtool_ops = &rswitch_ethtool_ops; netif_napi_add(ndev, &rdev->napi, rswitch_poll); @@ -1560,6 +1687,11 @@ static int rswitch_init(struct rswitch_private *priv) rswitch_fwd_init(priv); + err = rcar_gen4_ptp_register(priv->ptp_priv, RCAR_GEN4_PTP_REG_LAYOUT_S4, + RCAR_GEN4_PTP_CLOCK_S4); + if (err < 0) + goto err_ptp_register; + err = rswitch_gwca_request_irqs(priv); if (err < 0) goto err_gwca_request_irq; @@ -1595,6 +1727,9 @@ err_ether_port_init_all: err_gwca_hw_init: err_gwca_request_irq: + rcar_gen4_ptp_unregister(priv->ptp_priv); + +err_ptp_register: for (i = 0; i < RSWITCH_NUM_PORTS; i++) rswitch_device_free(priv, i); @@ -1620,12 +1755,18 @@ static int renesas_eth_sw_probe(struct platform_device *pdev) if (!priv) return -ENOMEM; + priv->ptp_priv = rcar_gen4_ptp_alloc(pdev); + if (!priv->ptp_priv) + return -ENOMEM; + platform_set_drvdata(pdev, priv); priv->pdev = pdev; priv->addr = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(priv->addr)) return PTR_ERR(priv->addr); + priv->ptp_priv->addr = priv->addr + RCAR_GEN4_GPTP_OFFSET_S4; + ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(40)); if (ret < 0) { ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)); @@ -1656,6 +1797,7 @@ static void rswitch_deinit(struct rswitch_private *priv) int i; rswitch_gwca_hw_deinit(priv); + rcar_gen4_ptp_unregister(priv->ptp_priv); for (i = 0; i < RSWITCH_NUM_PORTS; i++) { struct rswitch_device *rdev = priv->rdev[i]; diff --git a/drivers/net/ethernet/renesas/rswitch.h b/drivers/net/ethernet/renesas/rswitch.h index 3c58a7ca8b8a..778177ec8d4f 100644 --- a/drivers/net/ethernet/renesas/rswitch.h +++ b/drivers/net/ethernet/renesas/rswitch.h @@ -8,6 +8,7 @@ #define __RSWITCH_H__ #include +#include "rcar_gen4_ptp.h" #define RSWITCH_MAX_NUM_QUEUES 128 From 4f6620cd6bbd1e10018969ab52bdd8e083d91884 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Mon, 24 Oct 2022 16:39:54 +0100 Subject: [PATCH 0539/2002] wifi: ath9k: remove variable sent MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Variable sent is just being incremented and it's never used anywhere else. The variable and the increment are redundant so remove it. Signed-off-by: Colin Ian King Acked-by: Toke Høiland-Jørgensen Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20221024153954.2168503-1-colin.i.king@gmail.com --- drivers/net/wireless/ath/ath9k/xmit.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c index ba271a10d4ab..39abb59d8771 100644 --- a/drivers/net/wireless/ath/ath9k/xmit.c +++ b/drivers/net/wireless/ath/ath9k/xmit.c @@ -1678,7 +1678,6 @@ void ath9k_release_buffered_frames(struct ieee80211_hw *hw, struct ieee80211_tx_info *info; struct list_head bf_q; struct ath_buf *bf_tail = NULL, *bf = NULL; - int sent = 0; int i, ret; INIT_LIST_HEAD(&bf_q); @@ -1707,7 +1706,6 @@ void ath9k_release_buffered_frames(struct ieee80211_hw *hw, bf_tail = bf; nframes--; - sent++; TX_STAT_INC(sc, txq->axq_qnum, a_queued_hw); if (an->sta && skb_queue_empty(&tid->retry_q)) From ed3725e15a154ebebf44e0c34806c57525483f92 Mon Sep 17 00:00:00 2001 From: Rahul Bhattacharjee Date: Fri, 21 Oct 2022 14:31:26 +0530 Subject: [PATCH 0540/2002] wifi: ath11k: Fix qmi_msg_handler data structure initialization qmi_msg_handler is required to be null terminated by QMI module. There might be a case where a handler for a msg id is not present in the handlers array which can lead to infinite loop while searching the handler and therefore out of bound access in qmi_invoke_handler(). Hence update the initialization in qmi_msg_handler data structure. Tested-on: IPQ8074 hw2.0 AHB WLAN.HK.2.5.0.1-01100-QCAHKSWPL_SILICONZ-1 Signed-off-by: Rahul Bhattacharjee Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20221021090126.28626-1-quic_rbhattac@quicinc.com --- drivers/net/wireless/ath/ath11k/qmi.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/wireless/ath/ath11k/qmi.c b/drivers/net/wireless/ath/ath11k/qmi.c index 145f20a681bd..72cfc13ef6a8 100644 --- a/drivers/net/wireless/ath/ath11k/qmi.c +++ b/drivers/net/wireless/ath/ath11k/qmi.c @@ -3090,6 +3090,9 @@ static const struct qmi_msg_handler ath11k_qmi_msg_handlers[] = { sizeof(struct qmi_wlfw_fw_init_done_ind_msg_v01), .fn = ath11k_qmi_msg_fw_init_done_cb, }, + + /* end of list */ + {}, }; static int ath11k_qmi_ops_new_server(struct qmi_handle *qmi_hdl, From 6b85a4f39ff7177b2428d4deab1151a31754e391 Mon Sep 17 00:00:00 2001 From: Joe Damato Date: Fri, 7 Oct 2022 14:38:40 -0700 Subject: [PATCH 0541/2002] i40e: Store the irq number in i40e_q_vector Make it easy to figure out the IRQ number for a particular i40e_q_vector by storing the assigned IRQ in the structure itself. Signed-off-by: Joe Damato Acked-by: Jesse Brandeburg Acked-by: Sridhar Samudrala Tested-by: Gurucharan (A Contingent worker at Intel) Signed-off-by: Tony Nguyen --- drivers/net/ethernet/intel/i40e/i40e.h | 1 + drivers/net/ethernet/intel/i40e/i40e_main.c | 1 + 2 files changed, 2 insertions(+) diff --git a/drivers/net/ethernet/intel/i40e/i40e.h b/drivers/net/ethernet/intel/i40e/i40e.h index 9a60d6b207f7..60e351665c70 100644 --- a/drivers/net/ethernet/intel/i40e/i40e.h +++ b/drivers/net/ethernet/intel/i40e/i40e.h @@ -992,6 +992,7 @@ struct i40e_q_vector { struct rcu_head rcu; /* to avoid race with update stats on free */ char name[I40E_INT_NAME_STR_LEN]; bool arm_wb_state; + int irq_num; /* IRQ assigned to this q_vector */ } ____cacheline_internodealigned_in_smp; /* lan device */ diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index 1a1fab94205d..4880b740fa6e 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -4123,6 +4123,7 @@ static int i40e_vsi_request_irq_msix(struct i40e_vsi *vsi, char *basename) } /* register for affinity change notifications */ + q_vector->irq_num = irq_num; q_vector->affinity_notify.notify = i40e_irq_affinity_notify; q_vector->affinity_notify.release = i40e_irq_affinity_release; irq_set_affinity_notifier(irq_num, &q_vector->affinity_notify); From 8c1a595cc63ed55efcfec207ce0e35b2ca11401e Mon Sep 17 00:00:00 2001 From: Joe Damato Date: Fri, 7 Oct 2022 14:38:41 -0700 Subject: [PATCH 0542/2002] i40e: Record number TXes cleaned during NAPI Update i40e_clean_tx_irq to take an out parameter (tx_cleaned) which stores the number TXs cleaned. No XDP related TX code is touched. Care has been taken to avoid changing the control flow of i40e_clean_tx_irq and i40e_napi_poll. Signed-off-by: Joe Damato Acked-by: Jesse Brandeburg Acked-by: Sridhar Samudrala Tested-by: Gurucharan (A Contingent worker at Intel) Signed-off-by: Tony Nguyen --- drivers/net/ethernet/intel/i40e/i40e_txrx.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/intel/i40e/i40e_txrx.c b/drivers/net/ethernet/intel/i40e/i40e_txrx.c index b97c95f89fa0..75a3218d94a3 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_txrx.c +++ b/drivers/net/ethernet/intel/i40e/i40e_txrx.c @@ -923,11 +923,13 @@ void i40e_detect_recover_hung(struct i40e_vsi *vsi) * @vsi: the VSI we care about * @tx_ring: Tx ring to clean * @napi_budget: Used to determine if we are in netpoll + * @tx_cleaned: Out parameter set to the number of TXes cleaned * * Returns true if there's any budget left (e.g. the clean is finished) **/ static bool i40e_clean_tx_irq(struct i40e_vsi *vsi, - struct i40e_ring *tx_ring, int napi_budget) + struct i40e_ring *tx_ring, int napi_budget, + unsigned int *tx_cleaned) { int i = tx_ring->next_to_clean; struct i40e_tx_buffer *tx_buf; @@ -1048,6 +1050,7 @@ static bool i40e_clean_tx_irq(struct i40e_vsi *vsi, } } + *tx_cleaned = total_packets; return !!budget; } @@ -2689,6 +2692,8 @@ int i40e_napi_poll(struct napi_struct *napi, int budget) container_of(napi, struct i40e_q_vector, napi); struct i40e_vsi *vsi = q_vector->vsi; struct i40e_ring *ring; + bool tx_clean_complete = true; + unsigned int tx_cleaned = 0; bool clean_complete = true; bool arm_wb = false; int budget_per_ring; @@ -2705,10 +2710,10 @@ int i40e_napi_poll(struct napi_struct *napi, int budget) i40e_for_each_ring(ring, q_vector->tx) { bool wd = ring->xsk_pool ? i40e_clean_xdp_tx_irq(vsi, ring) : - i40e_clean_tx_irq(vsi, ring, budget); + i40e_clean_tx_irq(vsi, ring, budget, &tx_cleaned); if (!wd) { - clean_complete = false; + clean_complete = tx_clean_complete = false; continue; } arm_wb |= ring->arm_wb; From 717b5bc43c1fe7a19f8305123cf2c87ee562aba5 Mon Sep 17 00:00:00 2001 From: Joe Damato Date: Fri, 7 Oct 2022 14:38:42 -0700 Subject: [PATCH 0543/2002] i40e: Record number of RXes cleaned during NAPI Adjust i40e_clean_rx_irq to accept an out parameter which records the number of RX packets cleaned. No XDP related code is modified and care has been taken to avoid changing control flow. Signed-off-by: Joe Damato Acked-by: Jesse Brandeburg Acked-by: Sridhar Samudrala Tested-by: Gurucharan (A Contingent worker at Intel) Signed-off-by: Tony Nguyen --- drivers/net/ethernet/intel/i40e/i40e_txrx.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/intel/i40e/i40e_txrx.c b/drivers/net/ethernet/intel/i40e/i40e_txrx.c index 75a3218d94a3..6313395d2d3c 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_txrx.c +++ b/drivers/net/ethernet/intel/i40e/i40e_txrx.c @@ -2425,6 +2425,7 @@ static void i40e_inc_ntc(struct i40e_ring *rx_ring) * i40e_clean_rx_irq - Clean completed descriptors from Rx ring - bounce buf * @rx_ring: rx descriptor ring to transact packets on * @budget: Total limit on number of packets to process + * @rx_cleaned: Out parameter of the number of packets processed * * This function provides a "bounce buffer" approach to Rx interrupt * processing. The advantage to this is that on systems that have @@ -2433,7 +2434,8 @@ static void i40e_inc_ntc(struct i40e_ring *rx_ring) * * Returns amount of work completed **/ -static int i40e_clean_rx_irq(struct i40e_ring *rx_ring, int budget) +static int i40e_clean_rx_irq(struct i40e_ring *rx_ring, int budget, + unsigned int *rx_cleaned) { unsigned int total_rx_bytes = 0, total_rx_packets = 0, frame_sz = 0; u16 cleaned_count = I40E_DESC_UNUSED(rx_ring); @@ -2570,6 +2572,8 @@ static int i40e_clean_rx_irq(struct i40e_ring *rx_ring, int budget) i40e_update_rx_stats(rx_ring, total_rx_bytes, total_rx_packets); + *rx_cleaned = total_rx_packets; + /* guarantee a trip back through this routine if there was a failure */ return failure ? budget : (int)total_rx_packets; } @@ -2693,7 +2697,9 @@ int i40e_napi_poll(struct napi_struct *napi, int budget) struct i40e_vsi *vsi = q_vector->vsi; struct i40e_ring *ring; bool tx_clean_complete = true; + bool rx_clean_complete = true; unsigned int tx_cleaned = 0; + unsigned int rx_cleaned = 0; bool clean_complete = true; bool arm_wb = false; int budget_per_ring; @@ -2738,12 +2744,12 @@ int i40e_napi_poll(struct napi_struct *napi, int budget) i40e_for_each_ring(ring, q_vector->rx) { int cleaned = ring->xsk_pool ? i40e_clean_rx_irq_zc(ring, budget_per_ring) : - i40e_clean_rx_irq(ring, budget_per_ring); + i40e_clean_rx_irq(ring, budget_per_ring, &rx_cleaned); work_done += cleaned; /* if we clean as many as budgeted, we must not be done */ if (cleaned >= budget_per_ring) - clean_complete = false; + clean_complete = rx_clean_complete = false; } /* If work not completed, return budget and polling will return */ From 6d4d584a7ea8fc8d2be77545cb503118c193738a Mon Sep 17 00:00:00 2001 From: Joe Damato Date: Fri, 7 Oct 2022 14:38:43 -0700 Subject: [PATCH 0544/2002] i40e: Add i40e_napi_poll tracepoint Add a tracepoint for i40e_napi_poll that allows users to get detailed information about the amount of work done. This information can help users better tune the correct NAPI parameters (like weight and budget), as well as debug NIC settings like rx-usecs and tx-usecs, etc. When perf is attached, this tracepoint only fires when not in XDP mode. An example of the output from this tracepoint: $ sudo perf trace -e i40e:i40e_napi_poll -a --call-graph=fp --libtraceevent_print [..snip..] 388.258 :0/0 i40e:i40e_napi_poll(i40e_napi_poll on dev eth2 q i40e-eth2-TxRx-9 irq 346 irq_mask 00000000,00000000,00000000,00000000,00000000,00800000 curr_cpu 23 budget 64 bpr 64 rx_cleaned 28 tx_cleaned 0 rx_clean_complete 1 tx_clean_complete 1) i40e_napi_poll ([i40e]) i40e_napi_poll ([i40e]) __napi_poll ([kernel.kallsyms]) net_rx_action ([kernel.kallsyms]) __do_softirq ([kernel.kallsyms]) common_interrupt ([kernel.kallsyms]) asm_common_interrupt ([kernel.kallsyms]) intel_idle_irq ([kernel.kallsyms]) cpuidle_enter_state ([kernel.kallsyms]) cpuidle_enter ([kernel.kallsyms]) do_idle ([kernel.kallsyms]) cpu_startup_entry ([kernel.kallsyms]) [0x243fd8] ([kernel.kallsyms]) secondary_startup_64_no_verify ([kernel.kallsyms]) Signed-off-by: Joe Damato Acked-by: Jesse Brandeburg Acked-by: Sridhar Samudrala Tested-by: Gurucharan (A Contingent worker at Intel) Signed-off-by: Tony Nguyen --- drivers/net/ethernet/intel/i40e/i40e_trace.h | 49 ++++++++++++++++++++ drivers/net/ethernet/intel/i40e/i40e_txrx.c | 4 ++ 2 files changed, 53 insertions(+) diff --git a/drivers/net/ethernet/intel/i40e/i40e_trace.h b/drivers/net/ethernet/intel/i40e/i40e_trace.h index b5b12299931f..79d587ad5409 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_trace.h +++ b/drivers/net/ethernet/intel/i40e/i40e_trace.h @@ -55,6 +55,55 @@ * being built from shared code. */ +#define NO_DEV "(i40e no_device)" + +TRACE_EVENT(i40e_napi_poll, + + TP_PROTO(struct napi_struct *napi, struct i40e_q_vector *q, int budget, + int budget_per_ring, unsigned int rx_cleaned, unsigned int tx_cleaned, + bool rx_clean_complete, bool tx_clean_complete), + + TP_ARGS(napi, q, budget, budget_per_ring, rx_cleaned, tx_cleaned, + rx_clean_complete, tx_clean_complete), + + TP_STRUCT__entry( + __field(int, budget) + __field(int, budget_per_ring) + __field(unsigned int, rx_cleaned) + __field(unsigned int, tx_cleaned) + __field(int, rx_clean_complete) + __field(int, tx_clean_complete) + __field(int, irq_num) + __field(int, curr_cpu) + __string(qname, q->name) + __string(dev_name, napi->dev ? napi->dev->name : NO_DEV) + __bitmask(irq_affinity, nr_cpumask_bits) + ), + + TP_fast_assign( + __entry->budget = budget; + __entry->budget_per_ring = budget_per_ring; + __entry->rx_cleaned = rx_cleaned; + __entry->tx_cleaned = tx_cleaned; + __entry->rx_clean_complete = rx_clean_complete; + __entry->tx_clean_complete = tx_clean_complete; + __entry->irq_num = q->irq_num; + __entry->curr_cpu = get_cpu(); + __assign_str(qname, q->name); + __assign_str(dev_name, napi->dev ? napi->dev->name : NO_DEV); + __assign_bitmask(irq_affinity, cpumask_bits(&q->affinity_mask), + nr_cpumask_bits); + ), + + TP_printk("i40e_napi_poll on dev %s q %s irq %d irq_mask %s curr_cpu %d " + "budget %d bpr %d rx_cleaned %u tx_cleaned %u " + "rx_clean_complete %d tx_clean_complete %d", + __get_str(dev_name), __get_str(qname), __entry->irq_num, + __get_bitmask(irq_affinity), __entry->curr_cpu, __entry->budget, + __entry->budget_per_ring, __entry->rx_cleaned, __entry->tx_cleaned, + __entry->rx_clean_complete, __entry->tx_clean_complete) +); + /* Events related to a vsi & ring */ DECLARE_EVENT_CLASS( i40e_tx_template, diff --git a/drivers/net/ethernet/intel/i40e/i40e_txrx.c b/drivers/net/ethernet/intel/i40e/i40e_txrx.c index 6313395d2d3c..924f972b91fa 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_txrx.c +++ b/drivers/net/ethernet/intel/i40e/i40e_txrx.c @@ -2752,6 +2752,10 @@ int i40e_napi_poll(struct napi_struct *napi, int budget) clean_complete = rx_clean_complete = false; } + if (!i40e_enabled_xdp_vsi(vsi)) + trace_i40e_napi_poll(napi, q_vector, budget, budget_per_ring, rx_cleaned, + tx_cleaned, rx_clean_complete, tx_clean_complete); + /* If work not completed, return budget and polling will return */ if (!clean_complete) { int cpu_id = smp_processor_id(); From 30872d834bdbf20e12e3277394f85f1f4d7a9b3f Mon Sep 17 00:00:00 2001 From: Alicja Kowalska Date: Thu, 8 Sep 2022 10:07:10 +0200 Subject: [PATCH 0545/2002] i40e: Add appropriate error message logged for incorrect duplex setting Nothing logged in dmesg for attempting to set incorrect duplex. Add appropriate error message logged for incorrect duplex setting. Signed-off-by: Alicja Kowalska Signed-off-by: Kamil Maziarz Tested-by: Gurucharan (A Contingent worker at Intel) Signed-off-by: Tony Nguyen --- drivers/net/ethernet/intel/i40e/i40e_ethtool.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c index cc269f9db99f..616d27ec3226 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c +++ b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c @@ -1287,8 +1287,10 @@ static int i40e_set_link_ksettings(struct net_device *netdev, * trying to set something that we do not support. */ if (memcmp(©_ks.base, &safe_ks.base, - sizeof(struct ethtool_link_settings))) + sizeof(struct ethtool_link_settings))) { + netdev_err(netdev, "Only speed and autoneg are supported.\n"); return -EOPNOTSUPP; + } while (test_and_set_bit(__I40E_CONFIG_BUSY, pf->state)) { timeout--; From 619058eca5099a29d717202b3a0adcfc87b77445 Mon Sep 17 00:00:00 2001 From: ye xingchen Date: Tue, 18 Oct 2022 01:52:04 +0000 Subject: [PATCH 0546/2002] iavf: Replace __FUNCTION__ with __func__ __FUNCTION__ exists only for backwards compatibility reasons with old gcc versions. Replace it with __func__. Signed-off-by: ye xingchen Reviewed-by: Jesse Brandeburg Tested-by: Konrad Jankowski Signed-off-by: Tony Nguyen --- drivers/net/ethernet/intel/iavf/iavf_main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/intel/iavf/iavf_main.c b/drivers/net/ethernet/intel/iavf/iavf_main.c index 3fc572341781..efa2692af577 100644 --- a/drivers/net/ethernet/intel/iavf/iavf_main.c +++ b/drivers/net/ethernet/intel/iavf/iavf_main.c @@ -4820,7 +4820,7 @@ static void iavf_shutdown(struct pci_dev *pdev) iavf_close(netdev); if (iavf_lock_timeout(&adapter->crit_lock, 5000)) - dev_warn(&adapter->pdev->dev, "failed to acquire crit_lock in %s\n", __FUNCTION__); + dev_warn(&adapter->pdev->dev, "%s: failed to acquire crit_lock\n", __func__); /* Prevent the watchdog from running. */ iavf_change_state(adapter, __IAVF_REMOVE); adapter->aq_required = 0; From 69b957440a6379b4ae4bc6eef07bbf5efdeb4233 Mon Sep 17 00:00:00 2001 From: Bartosz Staszewski Date: Mon, 24 Oct 2022 10:49:55 +0200 Subject: [PATCH 0547/2002] iavf: Change information about device removal in dmesg Changed information about device removal in dmesg. In function iavf_remove changed printed message from "Remove" to "Removing" after hot vf plug/unplug. Reason for this change is that, that "Removing" word is better because it is clearer for the user that the device is already being removed rather than implying that the user should remove this device. Signed-off-by: Bartosz Staszewski Signed-off-by: Kamil Maziarz Tested-by: Gurucharan (A Contingent worker at Intel) Signed-off-by: Tony Nguyen --- drivers/net/ethernet/intel/iavf/iavf_main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/intel/iavf/iavf_main.c b/drivers/net/ethernet/intel/iavf/iavf_main.c index efa2692af577..258bdf8906dd 100644 --- a/drivers/net/ethernet/intel/iavf/iavf_main.c +++ b/drivers/net/ethernet/intel/iavf/iavf_main.c @@ -5083,7 +5083,7 @@ static void iavf_remove(struct pci_dev *pdev) } mutex_lock(&adapter->crit_lock); - dev_info(&adapter->pdev->dev, "Remove device\n"); + dev_info(&adapter->pdev->dev, "Removing device\n"); iavf_change_state(adapter, __IAVF_REMOVE); iavf_request_reset(adapter); From dd1c2322694522f674c874f5fa02ac5ae39135dd Mon Sep 17 00:00:00 2001 From: "Jiri Slaby (SUSE)" Date: Mon, 31 Oct 2022 12:43:41 +0100 Subject: [PATCH 0548/2002] wifi: ath11k: synchronize ath11k_mac_he_gi_to_nl80211_he_gi()'s return type ath11k_mac_he_gi_to_nl80211_he_gi() generates a valid warning with gcc-13: drivers/net/wireless/ath/ath11k/mac.c:321:20: error: conflicting types for 'ath11k_mac_he_gi_to_nl80211_he_gi' due to enum/integer mismatch; have 'enum nl80211_he_gi(u8)' drivers/net/wireless/ath/ath11k/mac.h:166:5: note: previous declaration of 'ath11k_mac_he_gi_to_nl80211_he_gi' with type 'u32(u8)' I.e. the type of the return value ath11k_mac_he_gi_to_nl80211_he_gi() in the declaration is u32, while the definition spells enum nl80211_he_gi. Synchronize them to the latter. Cc: Martin Liska Cc: Kalle Valo Cc: "David S. Miller" Cc: Eric Dumazet Cc: Jakub Kicinski Cc: Paolo Abeni Cc: ath11k@lists.infradead.org Cc: linux-wireless@vger.kernel.org Cc: netdev@vger.kernel.org Signed-off-by: Jiri Slaby (SUSE) Reviewed-by: Jeff Johnson Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20221031114341.10377-1-jirislaby@kernel.org --- drivers/net/wireless/ath/ath11k/mac.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath11k/mac.h b/drivers/net/wireless/ath/ath11k/mac.h index 2a0d3afb0c99..0231783ad754 100644 --- a/drivers/net/wireless/ath/ath11k/mac.h +++ b/drivers/net/wireless/ath/ath11k/mac.h @@ -163,7 +163,7 @@ void ath11k_mac_drain_tx(struct ath11k *ar); void ath11k_mac_peer_cleanup_all(struct ath11k *ar); int ath11k_mac_tx_mgmt_pending_free(int buf_id, void *skb, void *ctx); u8 ath11k_mac_bw_to_mac80211_bw(u8 bw); -u32 ath11k_mac_he_gi_to_nl80211_he_gi(u8 sgi); +enum nl80211_he_gi ath11k_mac_he_gi_to_nl80211_he_gi(u8 sgi); enum nl80211_he_ru_alloc ath11k_mac_phy_he_ru_to_nl80211_he_ru_alloc(u16 ru_phy); enum nl80211_he_ru_alloc ath11k_mac_he_ru_tones_to_nl80211_he_ru_alloc(u16 ru_tones); enum ath11k_supported_bw ath11k_mac_mac80211_bw_to_ath11k_bw(enum rate_info_bw bw); From db2d737d63c5b501eb103d5e5c96499cc792c420 Mon Sep 17 00:00:00 2001 From: Sasha Neftin Date: Tue, 26 Jul 2022 17:36:25 +0300 Subject: [PATCH 0549/2002] e1000e: Separate MTP board type from ADP We have the same LAN controller on different PCH's. Separate MTP board type from an ADP which will allow for specific fixes to be applied for MTP platforms. Signed-off-by: Sasha Neftin Tested-by: Naama Meir Signed-off-by: Tony Nguyen --- drivers/net/ethernet/intel/e1000e/e1000.h | 4 +++- drivers/net/ethernet/intel/e1000e/ich8lan.c | 20 ++++++++++++++++++++ drivers/net/ethernet/intel/e1000e/netdev.c | 17 +++++++++-------- 3 files changed, 32 insertions(+), 9 deletions(-) diff --git a/drivers/net/ethernet/intel/e1000e/e1000.h b/drivers/net/ethernet/intel/e1000e/e1000.h index e8a9a9610ac6..a187582d2299 100644 --- a/drivers/net/ethernet/intel/e1000e/e1000.h +++ b/drivers/net/ethernet/intel/e1000e/e1000.h @@ -116,7 +116,8 @@ enum e1000_boards { board_pch_spt, board_pch_cnp, board_pch_tgp, - board_pch_adp + board_pch_adp, + board_pch_mtp }; struct e1000_ps_page { @@ -504,6 +505,7 @@ extern const struct e1000_info e1000_pch_spt_info; extern const struct e1000_info e1000_pch_cnp_info; extern const struct e1000_info e1000_pch_tgp_info; extern const struct e1000_info e1000_pch_adp_info; +extern const struct e1000_info e1000_pch_mtp_info; extern const struct e1000_info e1000_es2_info; void e1000e_ptp_init(struct e1000_adapter *adapter); diff --git a/drivers/net/ethernet/intel/e1000e/ich8lan.c b/drivers/net/ethernet/intel/e1000e/ich8lan.c index 9466f65a6da7..e8f7cb9d8724 100644 --- a/drivers/net/ethernet/intel/e1000e/ich8lan.c +++ b/drivers/net/ethernet/intel/e1000e/ich8lan.c @@ -6041,3 +6041,23 @@ const struct e1000_info e1000_pch_adp_info = { .phy_ops = &ich8_phy_ops, .nvm_ops = &spt_nvm_ops, }; + +const struct e1000_info e1000_pch_mtp_info = { + .mac = e1000_pch_mtp, + .flags = FLAG_IS_ICH + | FLAG_HAS_WOL + | FLAG_HAS_HW_TIMESTAMP + | FLAG_HAS_CTRLEXT_ON_LOAD + | FLAG_HAS_AMT + | FLAG_HAS_FLASH + | FLAG_HAS_JUMBO_FRAMES + | FLAG_APME_IN_WUC, + .flags2 = FLAG2_HAS_PHY_STATS + | FLAG2_HAS_EEE, + .pba = 26, + .max_hw_frame_size = 9022, + .get_variants = e1000_get_variants_ich8lan, + .mac_ops = &ich8_mac_ops, + .phy_ops = &ich8_phy_ops, + .nvm_ops = &spt_nvm_ops, +}; diff --git a/drivers/net/ethernet/intel/e1000e/netdev.c b/drivers/net/ethernet/intel/e1000e/netdev.c index 49e926959ad3..4dc862399f0c 100644 --- a/drivers/net/ethernet/intel/e1000e/netdev.c +++ b/drivers/net/ethernet/intel/e1000e/netdev.c @@ -53,6 +53,7 @@ static const struct e1000_info *e1000_info_tbl[] = { [board_pch_cnp] = &e1000_pch_cnp_info, [board_pch_tgp] = &e1000_pch_tgp_info, [board_pch_adp] = &e1000_pch_adp_info, + [board_pch_mtp] = &e1000_pch_mtp_info, }; struct e1000_reg_info { @@ -7905,14 +7906,14 @@ static const struct pci_device_id e1000_pci_tbl[] = { { PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_ADP_I219_V17), board_pch_adp }, { PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_RPL_I219_LM22), board_pch_adp }, { PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_RPL_I219_V22), board_pch_adp }, - { PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_MTP_I219_LM18), board_pch_adp }, - { PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_MTP_I219_V18), board_pch_adp }, - { PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_MTP_I219_LM19), board_pch_adp }, - { PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_MTP_I219_V19), board_pch_adp }, - { PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_LNP_I219_LM20), board_pch_adp }, - { PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_LNP_I219_V20), board_pch_adp }, - { PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_LNP_I219_LM21), board_pch_adp }, - { PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_LNP_I219_V21), board_pch_adp }, + { PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_MTP_I219_LM18), board_pch_mtp }, + { PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_MTP_I219_V18), board_pch_mtp }, + { PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_MTP_I219_LM19), board_pch_mtp }, + { PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_MTP_I219_V19), board_pch_mtp }, + { PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_LNP_I219_LM20), board_pch_mtp }, + { PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_LNP_I219_V20), board_pch_mtp }, + { PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_LNP_I219_LM21), board_pch_mtp }, + { PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_LNP_I219_V21), board_pch_mtp }, { 0, 0, 0, 0, 0, 0, 0 } /* terminate list */ }; From 0c9183ce61bc5e546fc06d1f3237120778a81192 Mon Sep 17 00:00:00 2001 From: Sasha Neftin Date: Thu, 29 Sep 2022 11:08:59 +0300 Subject: [PATCH 0550/2002] e1000e: Add support for the next LOM generation Add devices IDs for the next LOM generations that will be available on the next Intel Client platforms. This patch provides the initial support for these devices. Signed-off-by: Sasha Neftin Tested-by: Naama Meir Signed-off-by: Tony Nguyen --- drivers/net/ethernet/intel/e1000e/ethtool.c | 2 ++ drivers/net/ethernet/intel/e1000e/hw.h | 9 +++++++++ drivers/net/ethernet/intel/e1000e/ich8lan.c | 7 +++++++ drivers/net/ethernet/intel/e1000e/netdev.c | 10 ++++++++++ drivers/net/ethernet/intel/e1000e/ptp.c | 1 + 5 files changed, 29 insertions(+) diff --git a/drivers/net/ethernet/intel/e1000e/ethtool.c b/drivers/net/ethernet/intel/e1000e/ethtool.c index 51a5afe9df2f..59e82d131d88 100644 --- a/drivers/net/ethernet/intel/e1000e/ethtool.c +++ b/drivers/net/ethernet/intel/e1000e/ethtool.c @@ -908,6 +908,7 @@ static int e1000_reg_test(struct e1000_adapter *adapter, u64 *data) case e1000_pch_adp: case e1000_pch_mtp: case e1000_pch_lnp: + case e1000_pch_ptp: mask |= BIT(18); break; default: @@ -1575,6 +1576,7 @@ static void e1000_loopback_cleanup(struct e1000_adapter *adapter) case e1000_pch_adp: case e1000_pch_mtp: case e1000_pch_lnp: + case e1000_pch_ptp: fext_nvm11 = er32(FEXTNVM11); fext_nvm11 &= ~E1000_FEXTNVM11_DISABLE_MULR_FIX; ew32(FEXTNVM11, fext_nvm11); diff --git a/drivers/net/ethernet/intel/e1000e/hw.h b/drivers/net/ethernet/intel/e1000e/hw.h index bcf680e83811..29f9fae35f42 100644 --- a/drivers/net/ethernet/intel/e1000e/hw.h +++ b/drivers/net/ethernet/intel/e1000e/hw.h @@ -114,6 +114,14 @@ struct e1000_hw; #define E1000_DEV_ID_PCH_LNP_I219_V20 0x550F #define E1000_DEV_ID_PCH_LNP_I219_LM21 0x5510 #define E1000_DEV_ID_PCH_LNP_I219_V21 0x5511 +#define E1000_DEV_ID_PCH_ARL_I219_LM24 0x57A0 +#define E1000_DEV_ID_PCH_ARL_I219_V24 0x57A1 +#define E1000_DEV_ID_PCH_PTP_I219_LM25 0x57B3 +#define E1000_DEV_ID_PCH_PTP_I219_V25 0x57B4 +#define E1000_DEV_ID_PCH_PTP_I219_LM26 0x57B5 +#define E1000_DEV_ID_PCH_PTP_I219_V26 0x57B6 +#define E1000_DEV_ID_PCH_PTP_I219_LM27 0x57B7 +#define E1000_DEV_ID_PCH_PTP_I219_V27 0x57B8 #define E1000_REVISION_4 4 @@ -141,6 +149,7 @@ enum e1000_mac_type { e1000_pch_adp, e1000_pch_mtp, e1000_pch_lnp, + e1000_pch_ptp, }; enum e1000_media_type { diff --git a/drivers/net/ethernet/intel/e1000e/ich8lan.c b/drivers/net/ethernet/intel/e1000e/ich8lan.c index e8f7cb9d8724..0c7fd10312c8 100644 --- a/drivers/net/ethernet/intel/e1000e/ich8lan.c +++ b/drivers/net/ethernet/intel/e1000e/ich8lan.c @@ -322,6 +322,7 @@ static s32 e1000_init_phy_workarounds_pchlan(struct e1000_hw *hw) case e1000_pch_adp: case e1000_pch_mtp: case e1000_pch_lnp: + case e1000_pch_ptp: if (e1000_phy_is_accessible_pchlan(hw)) break; @@ -468,6 +469,7 @@ static s32 e1000_init_phy_params_pchlan(struct e1000_hw *hw) case e1000_pch_adp: case e1000_pch_mtp: case e1000_pch_lnp: + case e1000_pch_ptp: /* In case the PHY needs to be in mdio slow mode, * set slow mode and try to get the PHY id again. */ @@ -714,6 +716,7 @@ static s32 e1000_init_mac_params_ich8lan(struct e1000_hw *hw) case e1000_pch_adp: case e1000_pch_mtp: case e1000_pch_lnp: + case e1000_pch_ptp: case e1000_pchlan: /* check management mode */ mac->ops.check_mng_mode = e1000_check_mng_mode_pchlan; @@ -1681,6 +1684,7 @@ static s32 e1000_get_variants_ich8lan(struct e1000_adapter *adapter) case e1000_pch_adp: case e1000_pch_mtp: case e1000_pch_lnp: + case e1000_pch_ptp: rc = e1000_init_phy_params_pchlan(hw); break; default: @@ -2137,6 +2141,7 @@ static s32 e1000_sw_lcd_config_ich8lan(struct e1000_hw *hw) case e1000_pch_adp: case e1000_pch_mtp: case e1000_pch_lnp: + case e1000_pch_ptp: sw_cfg_mask = E1000_FEXTNVM_SW_CONFIG_ICH8M; break; default: @@ -3182,6 +3187,7 @@ static s32 e1000_valid_nvm_bank_detect_ich8lan(struct e1000_hw *hw, u32 *bank) case e1000_pch_adp: case e1000_pch_mtp: case e1000_pch_lnp: + case e1000_pch_ptp: bank1_offset = nvm->flash_bank_size; act_offset = E1000_ICH_NVM_SIG_WORD; @@ -4122,6 +4128,7 @@ static s32 e1000_validate_nvm_checksum_ich8lan(struct e1000_hw *hw) case e1000_pch_adp: case e1000_pch_mtp: case e1000_pch_lnp: + case e1000_pch_ptp: word = NVM_COMPAT; valid_csum_mask = NVM_COMPAT_VALID_CSUM; break; diff --git a/drivers/net/ethernet/intel/e1000e/netdev.c b/drivers/net/ethernet/intel/e1000e/netdev.c index 4dc862399f0c..edc8aa9822ee 100644 --- a/drivers/net/ethernet/intel/e1000e/netdev.c +++ b/drivers/net/ethernet/intel/e1000e/netdev.c @@ -3553,6 +3553,7 @@ s32 e1000e_get_base_timinca(struct e1000_adapter *adapter, u32 *timinca) case e1000_pch_adp: case e1000_pch_mtp: case e1000_pch_lnp: + case e1000_pch_ptp: if (er32(TSYNCRXCTL) & E1000_TSYNCRXCTL_SYSCFI) { /* Stable 24MHz frequency */ incperiod = INCPERIOD_24MHZ; @@ -4068,6 +4069,7 @@ void e1000e_reset(struct e1000_adapter *adapter) case e1000_pch_adp: case e1000_pch_mtp: case e1000_pch_lnp: + case e1000_pch_ptp: fc->refresh_time = 0xFFFF; fc->pause_time = 0xFFFF; @@ -7914,6 +7916,14 @@ static const struct pci_device_id e1000_pci_tbl[] = { { PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_LNP_I219_V20), board_pch_mtp }, { PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_LNP_I219_LM21), board_pch_mtp }, { PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_LNP_I219_V21), board_pch_mtp }, + { PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_ARL_I219_LM24), board_pch_mtp }, + { PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_ARL_I219_V24), board_pch_mtp }, + { PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_PTP_I219_LM25), board_pch_mtp }, + { PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_PTP_I219_V25), board_pch_mtp }, + { PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_PTP_I219_LM26), board_pch_mtp }, + { PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_PTP_I219_V26), board_pch_mtp }, + { PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_PTP_I219_LM27), board_pch_mtp }, + { PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_PTP_I219_V27), board_pch_mtp }, { 0, 0, 0, 0, 0, 0, 0 } /* terminate list */ }; diff --git a/drivers/net/ethernet/intel/e1000e/ptp.c b/drivers/net/ethernet/intel/e1000e/ptp.c index 6e5a1720e6cd..def4566a916f 100644 --- a/drivers/net/ethernet/intel/e1000e/ptp.c +++ b/drivers/net/ethernet/intel/e1000e/ptp.c @@ -287,6 +287,7 @@ void e1000e_ptp_init(struct e1000_adapter *adapter) case e1000_pch_adp: case e1000_pch_mtp: case e1000_pch_lnp: + case e1000_pch_ptp: if ((hw->mac.type < e1000_pch_lpt) || (er32(TSYNCRXCTL) & E1000_TSYNCRXCTL_SYSCFI)) { adapter->ptp_clock_info.max_adj = 24000000 - 1; From 7bab8828e1ec33e579a664c4262053e16e9cc936 Mon Sep 17 00:00:00 2001 From: Sasha Neftin Date: Wed, 21 Sep 2022 10:59:37 +0300 Subject: [PATCH 0551/2002] e1000e: Add e1000e trace module Add tracepoints to the driver via a new file e1000e_trace.h and some new trace calls added in interesting places in the driver. Add some tracing for s0ix flows to help in a debug of shared resources with the CSME firmware. The idea here is that tracepoints have such low performance cost when disabled that we can leave these in the upstream driver. Performance not affected, and this can be very useful for debugging and adding new trace events to paths in the future. Usage: echo "e1000e_trace:*" > /sys/kernel/debug/tracing/set_event echo 1 > /sys/kernel/debug/tracing/events/e1000e_trace/enable Signed-off-by: Sasha Neftin Tested-by: Naama Meir Signed-off-by: Tony Nguyen --- drivers/net/ethernet/intel/e1000e/Makefile | 3 ++ .../net/ethernet/intel/e1000e/e1000e_trace.h | 42 +++++++++++++++++++ drivers/net/ethernet/intel/e1000e/netdev.c | 4 ++ 3 files changed, 49 insertions(+) create mode 100644 drivers/net/ethernet/intel/e1000e/e1000e_trace.h diff --git a/drivers/net/ethernet/intel/e1000e/Makefile b/drivers/net/ethernet/intel/e1000e/Makefile index 44e58b6e7660..0baa15503c38 100644 --- a/drivers/net/ethernet/intel/e1000e/Makefile +++ b/drivers/net/ethernet/intel/e1000e/Makefile @@ -5,6 +5,9 @@ # Makefile for the Intel(R) PRO/1000 ethernet driver # +ccflags-y += -I$(src) +subdir-ccflags-y += -I$(src) + obj-$(CONFIG_E1000E) += e1000e.o e1000e-objs := 82571.o ich8lan.o 80003es2lan.o \ diff --git a/drivers/net/ethernet/intel/e1000e/e1000e_trace.h b/drivers/net/ethernet/intel/e1000e/e1000e_trace.h new file mode 100644 index 000000000000..19d3cf4d924e --- /dev/null +++ b/drivers/net/ethernet/intel/e1000e/e1000e_trace.h @@ -0,0 +1,42 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright(c) 2022, Intel Corporation. */ +/* Modeled on trace-events-sample.h */ +/* The trace subsystem name for e1000e will be "e1000e_trace". + * + * This file is named e1000e_trace.h. + * + * Since this include file's name is different from the trace + * subsystem name, we'll have to define TRACE_INCLUDE_FILE at the end + * of this file. + */ + +#undef TRACE_SYSTEM +#define TRACE_SYSTEM e1000e_trace + +#if !defined(_TRACE_E1000E_TRACE_H) || defined(TRACE_HEADER_MULTI_READ) +#define _TRACE_E1000E_TRACE_H + +#include + +TRACE_EVENT(e1000e_trace_mac_register, + TP_PROTO(uint32_t reg), + TP_ARGS(reg), + TP_STRUCT__entry(__field(uint32_t, reg)), + TP_fast_assign(__entry->reg = reg;), + TP_printk("event: TraceHub e1000e mac register: 0x%08x", + __entry->reg) +); + +#endif +/* This must be outside ifdef _E1000E_TRACE_H */ +/* This trace include file is not located in the .../include/trace + * with the kernel tracepoint definitions, because we're a loadable + * module. + */ + +#undef TRACE_INCLUDE_PATH +#define TRACE_INCLUDE_PATH . +#undef TRACE_INCLUDE_FILE +#define TRACE_INCLUDE_FILE e1000e_trace + +#include diff --git a/drivers/net/ethernet/intel/e1000e/netdev.c b/drivers/net/ethernet/intel/e1000e/netdev.c index edc8aa9822ee..f0e015673f48 100644 --- a/drivers/net/ethernet/intel/e1000e/netdev.c +++ b/drivers/net/ethernet/intel/e1000e/netdev.c @@ -28,6 +28,8 @@ #include #include "e1000.h" +#define CREATE_TRACE_POINTS +#include "e1000e_trace.h" char e1000e_driver_name[] = "e1000e"; @@ -6351,6 +6353,7 @@ static void e1000e_s0ix_entry_flow(struct e1000_adapter *adapter) mac_data = er32(H2ME); mac_data |= E1000_H2ME_START_DPG; mac_data &= ~E1000_H2ME_EXIT_DPG; + trace_e1000e_trace_mac_register(mac_data); ew32(H2ME, mac_data); } else { /* Request driver configure the device to S0ix */ @@ -6505,6 +6508,7 @@ static void e1000e_s0ix_exit_flow(struct e1000_adapter *adapter) mac_data = er32(H2ME); mac_data &= ~E1000_H2ME_START_DPG; mac_data |= E1000_H2ME_EXIT_DPG; + trace_e1000e_trace_mac_register(mac_data); ew32(H2ME, mac_data); /* Poll up to 2.5 seconds for ME to unconfigure DPG. From ab400b0dd4ec186e483abc667358b4135ab94f1a Mon Sep 17 00:00:00 2001 From: Anirudh Venkataramanan Date: Fri, 30 Sep 2022 15:33:24 -0700 Subject: [PATCH 0552/2002] e1000e: Remove unnecessary use of kmap_atomic() alloc_rx_buf() allocates ps_page->page and buffer_info->page using either GFP_ATOMIC or GFP_KERNEL. Memory allocated with GFP_KERNEL/GFP_ATOMIC can't come from highmem and so there's no need to kmap() them. Just use page_address(). I don't have access to a 32-bit system so did some limited testing on qemu (qemu-system-i386 -m 4096 -smp 4 -device e1000e) with a 32-bit Debian 11.04 image. Cc: Ira Weiny Cc: Fabio M. De Francesco Cc: Jesse Brandeburg Cc: Tony Nguyen Suggested-by: Ira Weiny Suggested-by: Fabio M. De Francesco Signed-off-by: Anirudh Venkataramanan Tested-by: Naama Meir Signed-off-by: Tony Nguyen --- drivers/net/ethernet/intel/e1000e/netdev.c | 20 +++++--------------- 1 file changed, 5 insertions(+), 15 deletions(-) diff --git a/drivers/net/ethernet/intel/e1000e/netdev.c b/drivers/net/ethernet/intel/e1000e/netdev.c index f0e015673f48..36bc4fd91ef4 100644 --- a/drivers/net/ethernet/intel/e1000e/netdev.c +++ b/drivers/net/ethernet/intel/e1000e/netdev.c @@ -1391,26 +1391,18 @@ static bool e1000_clean_rx_irq_ps(struct e1000_ring *rx_ring, int *work_done, /* page alloc/put takes too long and effects small * packet throughput, so unsplit small packets and - * save the alloc/put only valid in softirq (napi) - * context to call kmap_* + * save the alloc/put */ if (l1 && (l1 <= copybreak) && ((length + l1) <= adapter->rx_ps_bsize0)) { - u8 *vaddr; - ps_page = &buffer_info->ps_pages[0]; - /* there is no documentation about how to call - * kmap_atomic, so we can't hold the mapping - * very long - */ dma_sync_single_for_cpu(&pdev->dev, ps_page->dma, PAGE_SIZE, DMA_FROM_DEVICE); - vaddr = kmap_atomic(ps_page->page); - memcpy(skb_tail_pointer(skb), vaddr, l1); - kunmap_atomic(vaddr); + memcpy(skb_tail_pointer(skb), + page_address(ps_page->page), l1); dma_sync_single_for_device(&pdev->dev, ps_page->dma, PAGE_SIZE, @@ -1610,11 +1602,9 @@ static bool e1000_clean_jumbo_rx_irq(struct e1000_ring *rx_ring, int *work_done, */ if (length <= copybreak && skb_tailroom(skb) >= length) { - u8 *vaddr; - vaddr = kmap_atomic(buffer_info->page); - memcpy(skb_tail_pointer(skb), vaddr, + memcpy(skb_tail_pointer(skb), + page_address(buffer_info->page), length); - kunmap_atomic(vaddr); /* re-use the page, so don't erase * buffer_info->page */ From 3e7b52e0eb9ef7e67c26072cb1c7a49a5cc356f5 Mon Sep 17 00:00:00 2001 From: Anirudh Venkataramanan Date: Mon, 19 Sep 2022 11:09:48 -0700 Subject: [PATCH 0553/2002] e1000: Remove unnecessary use of kmap_atomic() buffer_info->rxbuf.page accessed in e1000_clean_jumbo_rx_irq() is allocated using GFP_ATOMIC. Pages allocated with GFP_ATOMIC can't come from highmem and so there's no need to kmap() them. Just use page_address(). I don't have access to a 32-bit system so did some limited testing on qemu (qemu-system-i386 -m 4096 -smp 4 -device e1000e) with a 32-bit Debian 11.04 image. Cc: Ira Weiny Cc: Fabio M. De Francesco Cc: Jesse Brandeburg Cc: Tony Nguyen Suggested-by: Ira Weiny Suggested-by: Fabio M. De Francesco Signed-off-by: Anirudh Venkataramanan Reviewed-by: Ira Weiny Signed-off-by: Tony Nguyen --- drivers/net/ethernet/intel/e1000/e1000_main.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/drivers/net/ethernet/intel/e1000/e1000_main.c b/drivers/net/ethernet/intel/e1000/e1000_main.c index 61e60e4de600..da6e303ad99b 100644 --- a/drivers/net/ethernet/intel/e1000/e1000_main.c +++ b/drivers/net/ethernet/intel/e1000/e1000_main.c @@ -4229,8 +4229,6 @@ process_skb: */ p = buffer_info->rxbuf.page; if (length <= copybreak) { - u8 *vaddr; - if (likely(!(netdev->features & NETIF_F_RXFCS))) length -= 4; skb = e1000_alloc_rx_skb(adapter, @@ -4238,10 +4236,9 @@ process_skb: if (!skb) break; - vaddr = kmap_atomic(p); - memcpy(skb_tail_pointer(skb), vaddr, - length); - kunmap_atomic(vaddr); + memcpy(skb_tail_pointer(skb), + page_address(p), length); + /* re-use the page, so don't erase * buffer_info->rxbuf.page */ From 790835fcc0cb9992349ae3c9010dbc7321aaa24d Mon Sep 17 00:00:00 2001 From: Muhammad Husaini Zulkifli Date: Wed, 21 Sep 2022 10:49:40 +0800 Subject: [PATCH 0554/2002] igc: Correct the launchtime offset The launchtime offset should be corrected according to sections 7.5.2.6 Transmit Scheduling Latency of the Intel Ethernet I225/I226 Software User Manual. Software can compensate the latency between the transmission scheduling and the time that packet is transmitted to the network by setting this GTxOffset register. Without setting this register, there may be a significant delay between the packet scheduling and the network point. This patch helps to reduce the latency for each of the link speed. Before: 10Mbps : 11000 - 13800 nanosecond 100Mbps : 1300 - 1700 nanosecond 1000Mbps : 190 - 600 nanosecond 2500Mbps : 1400 - 1700 nanosecond After: 10Mbps : less than 750 nanosecond 100Mbps : less than 192 nanosecond 1000Mbps : less than 128 nanosecond 2500Mbps : less than 128 nanosecond Test Setup: Talker : Use l2_tai.c to generate the launchtime into packet payload. Listener: Use timedump.c to compute the delta between packet arrival and LaunchTime packet payload. Signed-off-by: Vinicius Costa Gomes Signed-off-by: Muhammad Husaini Zulkifli Acked-by: Sasha Neftin Acked-by: Paul Menzel Tested-by: Naama Meir Signed-off-by: Tony Nguyen --- drivers/net/ethernet/intel/igc/igc_defines.h | 9 ++++++ drivers/net/ethernet/intel/igc/igc_main.c | 7 +++++ drivers/net/ethernet/intel/igc/igc_regs.h | 1 + drivers/net/ethernet/intel/igc/igc_tsn.c | 30 ++++++++++++++++++++ drivers/net/ethernet/intel/igc/igc_tsn.h | 1 + 5 files changed, 48 insertions(+) diff --git a/drivers/net/ethernet/intel/igc/igc_defines.h b/drivers/net/ethernet/intel/igc/igc_defines.h index 4f9d7f013a95..f7311aeb293b 100644 --- a/drivers/net/ethernet/intel/igc/igc_defines.h +++ b/drivers/net/ethernet/intel/igc/igc_defines.h @@ -400,6 +400,15 @@ #define IGC_DTXMXPKTSZ_TSN 0x19 /* 1600 bytes of max TX DMA packet size */ #define IGC_DTXMXPKTSZ_DEFAULT 0x98 /* 9728-byte Jumbo frames */ +/* Transmit Scheduling Latency */ +/* Latency between transmission scheduling (LaunchTime) and the time + * the packet is transmitted to the network in nanosecond. + */ +#define IGC_TXOFFSET_SPEED_10 0x000034BC +#define IGC_TXOFFSET_SPEED_100 0x00000578 +#define IGC_TXOFFSET_SPEED_1000 0x0000012C +#define IGC_TXOFFSET_SPEED_2500 0x00000578 + /* Time Sync Interrupt Causes */ #define IGC_TSICR_SYS_WRAP BIT(0) /* SYSTIM Wrap around. */ #define IGC_TSICR_TXTS BIT(1) /* Transmit Timestamp. */ diff --git a/drivers/net/ethernet/intel/igc/igc_main.c b/drivers/net/ethernet/intel/igc/igc_main.c index 5d307b6e660b..1586e1e435c6 100644 --- a/drivers/net/ethernet/intel/igc/igc_main.c +++ b/drivers/net/ethernet/intel/igc/igc_main.c @@ -5381,6 +5381,13 @@ static void igc_watchdog_task(struct work_struct *work) break; } + /* Once the launch time has been set on the wire, there + * is a delay before the link speed can be determined + * based on link-up activity. Write into the register + * as soon as we know the correct link speed. + */ + igc_tsn_adjust_txtime_offset(adapter); + if (adapter->link_speed != SPEED_1000) goto no_wait; diff --git a/drivers/net/ethernet/intel/igc/igc_regs.h b/drivers/net/ethernet/intel/igc/igc_regs.h index c0d8214148d1..01c86d36856d 100644 --- a/drivers/net/ethernet/intel/igc/igc_regs.h +++ b/drivers/net/ethernet/intel/igc/igc_regs.h @@ -224,6 +224,7 @@ /* Transmit Scheduling Registers */ #define IGC_TQAVCTRL 0x3570 #define IGC_TXQCTL(_n) (0x3344 + 0x4 * (_n)) +#define IGC_GTXOFFSET 0x3310 #define IGC_BASET_L 0x3314 #define IGC_BASET_H 0x3318 #define IGC_QBVCYCLET 0x331C diff --git a/drivers/net/ethernet/intel/igc/igc_tsn.c b/drivers/net/ethernet/intel/igc/igc_tsn.c index 0fce22de2ab8..f975ed807da1 100644 --- a/drivers/net/ethernet/intel/igc/igc_tsn.c +++ b/drivers/net/ethernet/intel/igc/igc_tsn.c @@ -48,6 +48,35 @@ static unsigned int igc_tsn_new_flags(struct igc_adapter *adapter) return new_flags; } +void igc_tsn_adjust_txtime_offset(struct igc_adapter *adapter) +{ + struct igc_hw *hw = &adapter->hw; + u16 txoffset; + + if (!is_any_launchtime(adapter)) + return; + + switch (adapter->link_speed) { + case SPEED_10: + txoffset = IGC_TXOFFSET_SPEED_10; + break; + case SPEED_100: + txoffset = IGC_TXOFFSET_SPEED_100; + break; + case SPEED_1000: + txoffset = IGC_TXOFFSET_SPEED_1000; + break; + case SPEED_2500: + txoffset = IGC_TXOFFSET_SPEED_2500; + break; + default: + txoffset = 0; + break; + } + + wr32(IGC_GTXOFFSET, txoffset); +} + /* Returns the TSN specific registers to their default values after * the adapter is reset. */ @@ -57,6 +86,7 @@ static int igc_tsn_disable_offload(struct igc_adapter *adapter) u32 tqavctrl; int i; + wr32(IGC_GTXOFFSET, 0); wr32(IGC_TXPBS, I225_TXPBSIZE_DEFAULT); wr32(IGC_DTXMXPKTSZ, IGC_DTXMXPKTSZ_DEFAULT); diff --git a/drivers/net/ethernet/intel/igc/igc_tsn.h b/drivers/net/ethernet/intel/igc/igc_tsn.h index 1512307f5a52..b53e6af560b7 100644 --- a/drivers/net/ethernet/intel/igc/igc_tsn.h +++ b/drivers/net/ethernet/intel/igc/igc_tsn.h @@ -6,5 +6,6 @@ int igc_tsn_offload_apply(struct igc_adapter *adapter); int igc_tsn_reset(struct igc_adapter *adapter); +void igc_tsn_adjust_txtime_offset(struct igc_adapter *adapter); #endif /* _IGC_BASE_H */ From 2cee6401c4eaa562abc1d437d5d03e80bbee79c1 Mon Sep 17 00:00:00 2001 From: Suman Ghosh Date: Mon, 31 Oct 2022 14:38:56 +0530 Subject: [PATCH 0555/2002] octeontx2-af: Allow mkex profile without DMAC and add L2M/L2B header extraction support 1. It is possible to have custom mkex profiles which do not extract DMAC at all into the key. Hence allow mkex profiles which do not have DMAC to be loaded into MCAM hardware. This patch also adds debugging prints needed to identify profiles with wrong configuration. 2. If a mkex profile set "l2l3mb" field for Rx interface, then Rx multicast and broadcast entry should be configured. Signed-off-by: Suman Ghosh Link: https://lore.kernel.org/r/20221031090856.1404303-1-sumang@marvell.com Signed-off-by: Jakub Kicinski --- .../net/ethernet/marvell/octeontx2/af/npc.h | 1 + .../marvell/octeontx2/af/rvu_debugfs.c | 6 ++ .../marvell/octeontx2/af/rvu_npc_fs.c | 81 ++++++++++++++----- 3 files changed, 70 insertions(+), 18 deletions(-) diff --git a/drivers/net/ethernet/marvell/octeontx2/af/npc.h b/drivers/net/ethernet/marvell/octeontx2/af/npc.h index f187293e3e08..d027c23b8ef8 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/npc.h +++ b/drivers/net/ethernet/marvell/octeontx2/af/npc.h @@ -620,6 +620,7 @@ struct rvu_npc_mcam_rule { bool vfvlan_cfg; u16 chan; u16 chan_mask; + u8 lxmb; }; #endif /* NPC_H */ diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_debugfs.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_debugfs.c index a1970ebedf95..642e58a04da0 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_debugfs.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_debugfs.c @@ -2756,6 +2756,12 @@ static void rvu_dbg_npc_mcam_show_flows(struct seq_file *s, for_each_set_bit(bit, (unsigned long *)&rule->features, 64) { seq_printf(s, "\t%s ", npc_get_field_name(bit)); switch (bit) { + case NPC_LXMB: + if (rule->lxmb == 1) + seq_puts(s, "\tL2M nibble is set\n"); + else + seq_puts(s, "\tL2B nibble is set\n"); + break; case NPC_DMAC: seq_printf(s, "%pM ", rule->packet.dmac); seq_printf(s, "mask %pM\n", rule->mask.dmac); diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_fs.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_fs.c index 7c4e1acd0f77..50d3994efa97 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_fs.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_fs.c @@ -43,6 +43,7 @@ static const char * const npc_flow_names[] = { [NPC_DPORT_UDP] = "udp destination port", [NPC_SPORT_SCTP] = "sctp source port", [NPC_DPORT_SCTP] = "sctp destination port", + [NPC_LXMB] = "Mcast/Bcast header ", [NPC_UNKNOWN] = "unknown", }; @@ -340,8 +341,10 @@ static void npc_handle_multi_layer_fields(struct rvu *rvu, int blkaddr, u8 intf) vlan_tag2 = &key_fields[NPC_VLAN_TAG2]; /* if key profile programmed does not extract Ethertype at all */ - if (!etype_ether->nr_kws && !etype_tag1->nr_kws && !etype_tag2->nr_kws) + if (!etype_ether->nr_kws && !etype_tag1->nr_kws && !etype_tag2->nr_kws) { + dev_err(rvu->dev, "mkex: Ethertype is not extracted.\n"); goto vlan_tci; + } /* if key profile programmed extracts Ethertype from one layer */ if (etype_ether->nr_kws && !etype_tag1->nr_kws && !etype_tag2->nr_kws) @@ -354,35 +357,45 @@ static void npc_handle_multi_layer_fields(struct rvu *rvu, int blkaddr, u8 intf) /* if key profile programmed extracts Ethertype from multiple layers */ if (etype_ether->nr_kws && etype_tag1->nr_kws) { for (i = 0; i < NPC_MAX_KWS_IN_KEY; i++) { - if (etype_ether->kw_mask[i] != etype_tag1->kw_mask[i]) + if (etype_ether->kw_mask[i] != etype_tag1->kw_mask[i]) { + dev_err(rvu->dev, "mkex: Etype pos is different for untagged and tagged pkts.\n"); goto vlan_tci; + } } key_fields[NPC_ETYPE] = *etype_tag1; } if (etype_ether->nr_kws && etype_tag2->nr_kws) { for (i = 0; i < NPC_MAX_KWS_IN_KEY; i++) { - if (etype_ether->kw_mask[i] != etype_tag2->kw_mask[i]) + if (etype_ether->kw_mask[i] != etype_tag2->kw_mask[i]) { + dev_err(rvu->dev, "mkex: Etype pos is different for untagged and double tagged pkts.\n"); goto vlan_tci; + } } key_fields[NPC_ETYPE] = *etype_tag2; } if (etype_tag1->nr_kws && etype_tag2->nr_kws) { for (i = 0; i < NPC_MAX_KWS_IN_KEY; i++) { - if (etype_tag1->kw_mask[i] != etype_tag2->kw_mask[i]) + if (etype_tag1->kw_mask[i] != etype_tag2->kw_mask[i]) { + dev_err(rvu->dev, "mkex: Etype pos is different for tagged and double tagged pkts.\n"); goto vlan_tci; + } } key_fields[NPC_ETYPE] = *etype_tag2; } /* check none of higher layers overwrite Ethertype */ start_lid = key_fields[NPC_ETYPE].layer_mdata.lid + 1; - if (npc_check_overlap(rvu, blkaddr, NPC_ETYPE, start_lid, intf)) + if (npc_check_overlap(rvu, blkaddr, NPC_ETYPE, start_lid, intf)) { + dev_err(rvu->dev, "mkex: Ethertype is overwritten by higher layers.\n"); goto vlan_tci; + } *features |= BIT_ULL(NPC_ETYPE); vlan_tci: /* if key profile does not extract outer vlan tci at all */ - if (!vlan_tag1->nr_kws && !vlan_tag2->nr_kws) + if (!vlan_tag1->nr_kws && !vlan_tag2->nr_kws) { + dev_err(rvu->dev, "mkex: Outer vlan tci is not extracted.\n"); goto done; + } /* if key profile extracts outer vlan tci from one layer */ if (vlan_tag1->nr_kws && !vlan_tag2->nr_kws) @@ -393,15 +406,19 @@ vlan_tci: /* if key profile extracts outer vlan tci from multiple layers */ if (vlan_tag1->nr_kws && vlan_tag2->nr_kws) { for (i = 0; i < NPC_MAX_KWS_IN_KEY; i++) { - if (vlan_tag1->kw_mask[i] != vlan_tag2->kw_mask[i]) + if (vlan_tag1->kw_mask[i] != vlan_tag2->kw_mask[i]) { + dev_err(rvu->dev, "mkex: Out vlan tci pos is different for tagged and double tagged pkts.\n"); goto done; + } } key_fields[NPC_OUTER_VID] = *vlan_tag2; } /* check none of higher layers overwrite outer vlan tci */ start_lid = key_fields[NPC_OUTER_VID].layer_mdata.lid + 1; - if (npc_check_overlap(rvu, blkaddr, NPC_OUTER_VID, start_lid, intf)) + if (npc_check_overlap(rvu, blkaddr, NPC_OUTER_VID, start_lid, intf)) { + dev_err(rvu->dev, "mkex: Outer vlan tci is overwritten by higher layers.\n"); goto done; + } *features |= BIT_ULL(NPC_OUTER_VID); done: return; @@ -522,6 +539,10 @@ static void npc_set_features(struct rvu *rvu, int blkaddr, u8 intf) if (npc_check_field(rvu, blkaddr, NPC_LB, intf)) *features |= BIT_ULL(NPC_VLAN_ETYPE_CTAG) | BIT_ULL(NPC_VLAN_ETYPE_STAG); + + /* for L2M/L2B/L3M/L3B, check if the type is present in the key */ + if (npc_check_field(rvu, blkaddr, NPC_LXMB, intf)) + *features |= BIT_ULL(NPC_LXMB); } /* Scan key extraction profile and record how fields of our interest @@ -599,16 +620,6 @@ static int npc_scan_verify_kex(struct rvu *rvu, int blkaddr) dev_err(rvu->dev, "Channel cannot be overwritten\n"); return -EINVAL; } - /* DMAC should be present in key for unicast filter to work */ - if (!npc_is_field_present(rvu, NPC_DMAC, NIX_INTF_RX)) { - dev_err(rvu->dev, "DMAC not present in Key\n"); - return -EINVAL; - } - /* check that none of the fields overwrite DMAC */ - if (npc_check_overlap(rvu, blkaddr, NPC_DMAC, 0, NIX_INTF_RX)) { - dev_err(rvu->dev, "DMAC cannot be overwritten\n"); - return -EINVAL; - } npc_set_features(rvu, blkaddr, NIX_INTF_TX); npc_set_features(rvu, blkaddr, NIX_INTF_RX); @@ -851,6 +862,11 @@ static void npc_update_flow(struct rvu *rvu, struct mcam_entry *entry, npc_update_entry(rvu, NPC_LE, entry, NPC_LT_LE_ESP, 0, ~0ULL, 0, intf); + if (features & BIT_ULL(NPC_LXMB)) { + output->lxmb = is_broadcast_ether_addr(pkt->dmac) ? 2 : 1; + npc_update_entry(rvu, NPC_LXMB, entry, output->lxmb, 0, + output->lxmb, 0, intf); + } #define NPC_WRITE_FLOW(field, member, val_lo, val_hi, mask_lo, mask_hi) \ do { \ if (features & BIT_ULL((field))) { \ @@ -1153,6 +1169,7 @@ find_rule: rule->chan_mask = write_req.entry_data.kw_mask[0] & NPC_KEX_CHAN_MASK; rule->chan = write_req.entry_data.kw[0] & NPC_KEX_CHAN_MASK; rule->chan &= rule->chan_mask; + rule->lxmb = dummy.lxmb; if (is_npc_intf_tx(req->intf)) rule->intf = pfvf->nix_tx_intf; else @@ -1215,6 +1232,34 @@ int rvu_mbox_handler_npc_install_flow(struct rvu *rvu, if (!is_npc_interface_valid(rvu, req->intf)) return NPC_FLOW_INTF_INVALID; + /* If DMAC is not extracted in MKEX, rules installed by AF + * can rely on L2MB bit set by hardware protocol checker for + * broadcast and multicast addresses. + */ + if (npc_check_field(rvu, blkaddr, NPC_DMAC, req->intf)) + goto process_flow; + + if (is_pffunc_af(req->hdr.pcifunc)) { + if (is_unicast_ether_addr(req->packet.dmac)) { + dev_err(rvu->dev, + "%s: mkex profile does not support ucast flow\n", + __func__); + return NPC_FLOW_NOT_SUPPORTED; + } + + if (!npc_is_field_present(rvu, NPC_LXMB, req->intf)) { + dev_err(rvu->dev, + "%s: mkex profile does not support bcast/mcast flow", + __func__); + return NPC_FLOW_NOT_SUPPORTED; + } + + /* Modify feature to use LXMB instead of DMAC */ + req->features &= ~BIT_ULL(NPC_DMAC); + req->features |= BIT_ULL(NPC_LXMB); + } + +process_flow: if (from_vf && req->default_rule) return NPC_FLOW_VF_PERM_DENIED; From 471ef777ec79baadc5cd9773d08f95f49cf5e2b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= Date: Mon, 31 Oct 2022 11:48:56 +0100 Subject: [PATCH 0556/2002] net: broadcom: bcm4908_enet: report queued and transmitted bytes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This allows BQL to operate avoiding buffer bloat and reducing latency. Signed-off-by: Rafał Miłecki Link: https://lore.kernel.org/r/20221031104856.32388-1-zajec5@gmail.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/broadcom/bcm4908_enet.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/net/ethernet/broadcom/bcm4908_enet.c b/drivers/net/ethernet/broadcom/bcm4908_enet.c index b0aac0bcb060..33d86683af50 100644 --- a/drivers/net/ethernet/broadcom/bcm4908_enet.c +++ b/drivers/net/ethernet/broadcom/bcm4908_enet.c @@ -505,6 +505,7 @@ static int bcm4908_enet_stop(struct net_device *netdev) netif_carrier_off(netdev); napi_disable(&rx_ring->napi); napi_disable(&tx_ring->napi); + netdev_reset_queue(netdev); bcm4908_enet_dma_rx_ring_disable(enet, &enet->rx_ring); bcm4908_enet_dma_tx_ring_disable(enet, &enet->tx_ring); @@ -564,6 +565,8 @@ static netdev_tx_t bcm4908_enet_start_xmit(struct sk_buff *skb, struct net_devic if (ring->write_idx + 1 == ring->length - 1) tmp |= DMA_CTL_STATUS_WRAP; + netdev_sent_queue(enet->netdev, skb->len); + buf_desc->addr = cpu_to_le32((uint32_t)slot->dma_addr); buf_desc->ctl = cpu_to_le32(tmp); @@ -671,6 +674,7 @@ static int bcm4908_enet_poll_tx(struct napi_struct *napi, int weight) tx_ring->read_idx = 0; } + netdev_completed_queue(enet->netdev, handled, bytes); enet->netdev->stats.tx_packets += handled; enet->netdev->stats.tx_bytes += bytes; From 7d84118229bf7f7290438c85caa8e49de52d50c1 Mon Sep 17 00:00:00 2001 From: "Jiri Slaby (SUSE)" Date: Mon, 31 Oct 2022 12:43:54 +0100 Subject: [PATCH 0557/2002] qed (gcc13): use u16 for fid to be big enough gcc 13 correctly reports overflow in qed_grc_dump_addr_range(): In file included from drivers/net/ethernet/qlogic/qed/qed.h:23, from drivers/net/ethernet/qlogic/qed/qed_debug.c:10: drivers/net/ethernet/qlogic/qed/qed_debug.c: In function 'qed_grc_dump_addr_range': include/linux/qed/qed_if.h:1217:9: error: overflow in conversion from 'int' to 'u8' {aka 'unsigned char'} changes value from '(int)vf_id << 8 | 128' to '128' [-Werror=overflow] We do: u8 fid; ... fid = vf_id << 8 | 128; Since fid is 16bit (and the stored value above too), fid should be u16, not u8. Fix that. Cc: Martin Liska Cc: Ariel Elior Cc: Manish Chopra Signed-off-by: Jiri Slaby (SUSE) Link: https://lore.kernel.org/r/20221031114354.10398-1-jirislaby@kernel.org Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/qlogic/qed/qed_debug.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/qlogic/qed/qed_debug.c b/drivers/net/ethernet/qlogic/qed/qed_debug.c index 5250d1d1e49c..86ecb080b153 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_debug.c +++ b/drivers/net/ethernet/qlogic/qed/qed_debug.c @@ -1972,9 +1972,10 @@ static u32 qed_grc_dump_addr_range(struct qed_hwfn *p_hwfn, u8 split_id) { struct dbg_tools_data *dev_data = &p_hwfn->dbg_info; - u8 port_id = 0, pf_id = 0, vf_id = 0, fid = 0; + u8 port_id = 0, pf_id = 0, vf_id = 0; bool read_using_dmae = false; u32 thresh; + u16 fid; if (!dump) return len; From 777fa87c7682228e155cf0892ba61cb2ab1fe3ae Mon Sep 17 00:00:00 2001 From: "Jiri Slaby (SUSE)" Date: Mon, 31 Oct 2022 12:44:09 +0100 Subject: [PATCH 0558/2002] bonding (gcc13): synchronize bond_{a,t}lb_xmit() types Both bond_alb_xmit() and bond_tlb_xmit() produce a valid warning with gcc-13: drivers/net/bonding/bond_alb.c:1409:13: error: conflicting types for 'bond_tlb_xmit' due to enum/integer mismatch; have 'netdev_tx_t(struct sk_buff *, struct net_device *)' ... include/net/bond_alb.h:160:5: note: previous declaration of 'bond_tlb_xmit' with type 'int(struct sk_buff *, struct net_device *)' drivers/net/bonding/bond_alb.c:1523:13: error: conflicting types for 'bond_alb_xmit' due to enum/integer mismatch; have 'netdev_tx_t(struct sk_buff *, struct net_device *)' ... include/net/bond_alb.h:159:5: note: previous declaration of 'bond_alb_xmit' with type 'int(struct sk_buff *, struct net_device *)' I.e. the return type of the declaration is int, while the definitions spell netdev_tx_t. Synchronize both of them to the latter. Cc: Martin Liska Cc: Jay Vosburgh Cc: Veaceslav Falico Cc: Andy Gospodarek Signed-off-by: Jiri Slaby (SUSE) Link: https://lore.kernel.org/r/20221031114409.10417-1-jirislaby@kernel.org Signed-off-by: Jakub Kicinski --- include/net/bond_alb.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/net/bond_alb.h b/include/net/bond_alb.h index 191c36afa1f4..9dc082b2d543 100644 --- a/include/net/bond_alb.h +++ b/include/net/bond_alb.h @@ -156,8 +156,8 @@ int bond_alb_init_slave(struct bonding *bond, struct slave *slave); void bond_alb_deinit_slave(struct bonding *bond, struct slave *slave); void bond_alb_handle_link_change(struct bonding *bond, struct slave *slave, char link); void bond_alb_handle_active_change(struct bonding *bond, struct slave *new_slave); -int bond_alb_xmit(struct sk_buff *skb, struct net_device *bond_dev); -int bond_tlb_xmit(struct sk_buff *skb, struct net_device *bond_dev); +netdev_tx_t bond_alb_xmit(struct sk_buff *skb, struct net_device *bond_dev); +netdev_tx_t bond_tlb_xmit(struct sk_buff *skb, struct net_device *bond_dev); struct slave *bond_xmit_alb_slave_get(struct bonding *bond, struct sk_buff *skb); struct slave *bond_xmit_tlb_slave_get(struct bonding *bond, From 3319dbb3e755398f254c3daa04b9030197137efe Mon Sep 17 00:00:00 2001 From: "Jiri Slaby (SUSE)" Date: Mon, 31 Oct 2022 12:44:40 +0100 Subject: [PATCH 0559/2002] sfc (gcc13): synchronize ef100_enqueue_skb()'s return type ef100_enqueue_skb() generates a valid warning with gcc-13: drivers/net/ethernet/sfc/ef100_tx.c:370:5: error: conflicting types for 'ef100_enqueue_skb' due to enum/integer mismatch; have 'int(struct efx_tx_queue *, struct sk_buff *)' drivers/net/ethernet/sfc/ef100_tx.h:25:13: note: previous declaration of 'ef100_enqueue_skb' with type 'netdev_tx_t(struct efx_tx_queue *, struct sk_buff *)' I.e. the type of the ef100_enqueue_skb()'s return value in the declaration is int, while the definition spells enum netdev_tx_t. Synchronize them to the latter. Cc: Martin Liska Cc: Edward Cree Cc: Martin Habets Signed-off-by: Jiri Slaby (SUSE) Link: https://lore.kernel.org/r/20221031114440.10461-1-jirislaby@kernel.org Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/sfc/ef100_tx.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/sfc/ef100_tx.c b/drivers/net/ethernet/sfc/ef100_tx.c index 102ddc7e206a..29ffaf35559d 100644 --- a/drivers/net/ethernet/sfc/ef100_tx.c +++ b/drivers/net/ethernet/sfc/ef100_tx.c @@ -367,7 +367,8 @@ void ef100_ev_tx(struct efx_channel *channel, const efx_qword_t *p_event) * Returns 0 on success, error code otherwise. In case of an error this * function will free the SKB. */ -int ef100_enqueue_skb(struct efx_tx_queue *tx_queue, struct sk_buff *skb) +netdev_tx_t ef100_enqueue_skb(struct efx_tx_queue *tx_queue, + struct sk_buff *skb) { return __ef100_enqueue_skb(tx_queue, skb, NULL); } From 42e51de97cb449aa7d2aa1638cb5fcd2695b8f98 Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Tue, 1 Nov 2022 14:39:35 +0200 Subject: [PATCH 0560/2002] rocker: Avoid unnecessary scheduling of work item The work item function ofdpa_port_fdb_learn_work() does not do anything when 'OFDPA_OP_FLAG_LEARNED' is not set in the work item's flags. Therefore, do not allocate and do not schedule the work item when the flag is not set. Suggested-by: Vladimir Oltean Signed-off-by: Ido Schimmel Reviewed-by: Vladimir Oltean Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/rocker/rocker_ofdpa.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/net/ethernet/rocker/rocker_ofdpa.c b/drivers/net/ethernet/rocker/rocker_ofdpa.c index 58cf7cc54f40..77ad09ad8304 100644 --- a/drivers/net/ethernet/rocker/rocker_ofdpa.c +++ b/drivers/net/ethernet/rocker/rocker_ofdpa.c @@ -1821,19 +1821,16 @@ static void ofdpa_port_fdb_learn_work(struct work_struct *work) const struct ofdpa_fdb_learn_work *lw = container_of(work, struct ofdpa_fdb_learn_work, work); bool removing = (lw->flags & OFDPA_OP_FLAG_REMOVE); - bool learned = (lw->flags & OFDPA_OP_FLAG_LEARNED); struct switchdev_notifier_fdb_info info = {}; + enum switchdev_notifier_type event; info.addr = lw->addr; info.vid = lw->vid; + event = removing ? SWITCHDEV_FDB_DEL_TO_BRIDGE : + SWITCHDEV_FDB_ADD_TO_BRIDGE; rtnl_lock(); - if (learned && removing) - call_switchdev_notifiers(SWITCHDEV_FDB_DEL_TO_BRIDGE, - lw->ofdpa_port->dev, &info.info, NULL); - else if (learned && !removing) - call_switchdev_notifiers(SWITCHDEV_FDB_ADD_TO_BRIDGE, - lw->ofdpa_port->dev, &info.info, NULL); + call_switchdev_notifiers(event, lw->ofdpa_port->dev, &info.info, NULL); rtnl_unlock(); kfree(work); @@ -1865,6 +1862,9 @@ static int ofdpa_port_fdb_learn(struct ofdpa_port *ofdpa_port, if (!ofdpa_port_is_bridged(ofdpa_port)) return 0; + if (!(flags & OFDPA_OP_FLAG_LEARNED)) + return 0; + lw = kzalloc(sizeof(*lw), GFP_ATOMIC); if (!lw) return -ENOMEM; From 386b4174827ce0c7ecc789a6c8bf98e994dc2dee Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Tue, 1 Nov 2022 14:39:36 +0200 Subject: [PATCH 0561/2002] rocker: Explicitly mark learned FDB entries as offloaded Currently, FDB entries that are notified to the bridge driver via 'SWITCHDEV_FDB_ADD_TO_BRIDGE' are always marked as offloaded by the bridge. With MAB enabled, this will no longer be universally true. Device drivers will report locked FDB entries to the bridge to let it know that the corresponding hosts required authorization, but it does not mean that these entries are necessarily programmed in the underlying hardware. We would like to solve it by having the bridge driver determine the offload indication based of the 'offloaded' bit in the FDB notification [1]. Prepare for that change by having rocker explicitly mark learned FDB entries as offloaded. This is consistent with all the other switchdev drivers. [1] https://lore.kernel.org/netdev/20221025100024.1287157-4-idosch@nvidia.com/ Signed-off-by: Ido Schimmel Reviewed-by: Vladimir Oltean Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/rocker/rocker_ofdpa.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/ethernet/rocker/rocker_ofdpa.c b/drivers/net/ethernet/rocker/rocker_ofdpa.c index 77ad09ad8304..826990459fa4 100644 --- a/drivers/net/ethernet/rocker/rocker_ofdpa.c +++ b/drivers/net/ethernet/rocker/rocker_ofdpa.c @@ -1826,6 +1826,7 @@ static void ofdpa_port_fdb_learn_work(struct work_struct *work) info.addr = lw->addr; info.vid = lw->vid; + info.offloaded = !removing; event = removing ? SWITCHDEV_FDB_DEL_TO_BRIDGE : SWITCHDEV_FDB_ADD_TO_BRIDGE; From 11e1fcf2b494da810d551553c46f739e36501adc Mon Sep 17 00:00:00 2001 From: Jeff Johnson Date: Wed, 14 Sep 2022 17:26:12 -0700 Subject: [PATCH 0562/2002] wifi: ath10k: Make QMI message rules const Commit ff6d365898d4 ("soc: qcom: qmi: use const for struct qmi_elem_info") allows QMI message encoding/decoding rules to be const, so do that for ath10k. Compile tested only. Signed-off-by: Jeff Johnson Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20220915002612.13394-1-quic_jjohnson@quicinc.com --- drivers/net/wireless/ath/ath10k/qmi.c | 2 +- .../net/wireless/ath/ath10k/qmi_wlfw_v01.c | 126 +++++++++--------- .../net/wireless/ath/ath10k/qmi_wlfw_v01.h | 102 +++++++------- 3 files changed, 115 insertions(+), 115 deletions(-) diff --git a/drivers/net/wireless/ath/ath10k/qmi.c b/drivers/net/wireless/ath/ath10k/qmi.c index 66cb7a1e628a..8930a0f4c42f 100644 --- a/drivers/net/wireless/ath/ath10k/qmi.c +++ b/drivers/net/wireless/ath/ath10k/qmi.c @@ -618,7 +618,7 @@ static int ath10k_qmi_host_cap_send_sync(struct ath10k_qmi *qmi) { struct wlfw_host_cap_resp_msg_v01 resp = {}; struct wlfw_host_cap_req_msg_v01 req = {}; - struct qmi_elem_info *req_ei; + const struct qmi_elem_info *req_ei; struct ath10k *ar = qmi->ar; struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar); struct qmi_txn txn; diff --git a/drivers/net/wireless/ath/ath10k/qmi_wlfw_v01.c b/drivers/net/wireless/ath/ath10k/qmi_wlfw_v01.c index 86fcf4e1de5f..1c81e454f943 100644 --- a/drivers/net/wireless/ath/ath10k/qmi_wlfw_v01.c +++ b/drivers/net/wireless/ath/ath10k/qmi_wlfw_v01.c @@ -7,7 +7,7 @@ #include #include "qmi_wlfw_v01.h" -static struct qmi_elem_info wlfw_ce_tgt_pipe_cfg_s_v01_ei[] = { +static const struct qmi_elem_info wlfw_ce_tgt_pipe_cfg_s_v01_ei[] = { { .data_type = QMI_UNSIGNED_4_BYTE, .elem_len = 1, @@ -56,7 +56,7 @@ static struct qmi_elem_info wlfw_ce_tgt_pipe_cfg_s_v01_ei[] = { {} }; -static struct qmi_elem_info wlfw_ce_svc_pipe_cfg_s_v01_ei[] = { +static const struct qmi_elem_info wlfw_ce_svc_pipe_cfg_s_v01_ei[] = { { .data_type = QMI_UNSIGNED_4_BYTE, .elem_len = 1, @@ -87,7 +87,7 @@ static struct qmi_elem_info wlfw_ce_svc_pipe_cfg_s_v01_ei[] = { {} }; -static struct qmi_elem_info wlfw_shadow_reg_cfg_s_v01_ei[] = { +static const struct qmi_elem_info wlfw_shadow_reg_cfg_s_v01_ei[] = { { .data_type = QMI_UNSIGNED_2_BYTE, .elem_len = 1, @@ -109,7 +109,7 @@ static struct qmi_elem_info wlfw_shadow_reg_cfg_s_v01_ei[] = { {} }; -static struct qmi_elem_info wlfw_shadow_reg_v2_cfg_s_v01_ei[] = { +static const struct qmi_elem_info wlfw_shadow_reg_v2_cfg_s_v01_ei[] = { { .data_type = QMI_UNSIGNED_4_BYTE, .elem_len = 1, @@ -122,7 +122,7 @@ static struct qmi_elem_info wlfw_shadow_reg_v2_cfg_s_v01_ei[] = { {} }; -static struct qmi_elem_info wlfw_memory_region_info_s_v01_ei[] = { +static const struct qmi_elem_info wlfw_memory_region_info_s_v01_ei[] = { { .data_type = QMI_UNSIGNED_8_BYTE, .elem_len = 1, @@ -153,7 +153,7 @@ static struct qmi_elem_info wlfw_memory_region_info_s_v01_ei[] = { {} }; -static struct qmi_elem_info wlfw_mem_cfg_s_v01_ei[] = { +static const struct qmi_elem_info wlfw_mem_cfg_s_v01_ei[] = { { .data_type = QMI_UNSIGNED_8_BYTE, .elem_len = 1, @@ -184,7 +184,7 @@ static struct qmi_elem_info wlfw_mem_cfg_s_v01_ei[] = { {} }; -static struct qmi_elem_info wlfw_mem_seg_s_v01_ei[] = { +static const struct qmi_elem_info wlfw_mem_seg_s_v01_ei[] = { { .data_type = QMI_UNSIGNED_4_BYTE, .elem_len = 1, @@ -225,7 +225,7 @@ static struct qmi_elem_info wlfw_mem_seg_s_v01_ei[] = { {} }; -static struct qmi_elem_info wlfw_mem_seg_resp_s_v01_ei[] = { +static const struct qmi_elem_info wlfw_mem_seg_resp_s_v01_ei[] = { { .data_type = QMI_UNSIGNED_8_BYTE, .elem_len = 1, @@ -256,7 +256,7 @@ static struct qmi_elem_info wlfw_mem_seg_resp_s_v01_ei[] = { {} }; -static struct qmi_elem_info wlfw_rf_chip_info_s_v01_ei[] = { +static const struct qmi_elem_info wlfw_rf_chip_info_s_v01_ei[] = { { .data_type = QMI_UNSIGNED_4_BYTE, .elem_len = 1, @@ -278,7 +278,7 @@ static struct qmi_elem_info wlfw_rf_chip_info_s_v01_ei[] = { {} }; -static struct qmi_elem_info wlfw_rf_board_info_s_v01_ei[] = { +static const struct qmi_elem_info wlfw_rf_board_info_s_v01_ei[] = { { .data_type = QMI_UNSIGNED_4_BYTE, .elem_len = 1, @@ -291,7 +291,7 @@ static struct qmi_elem_info wlfw_rf_board_info_s_v01_ei[] = { {} }; -static struct qmi_elem_info wlfw_soc_info_s_v01_ei[] = { +static const struct qmi_elem_info wlfw_soc_info_s_v01_ei[] = { { .data_type = QMI_UNSIGNED_4_BYTE, .elem_len = 1, @@ -304,7 +304,7 @@ static struct qmi_elem_info wlfw_soc_info_s_v01_ei[] = { {} }; -static struct qmi_elem_info wlfw_fw_version_info_s_v01_ei[] = { +static const struct qmi_elem_info wlfw_fw_version_info_s_v01_ei[] = { { .data_type = QMI_UNSIGNED_4_BYTE, .elem_len = 1, @@ -326,7 +326,7 @@ static struct qmi_elem_info wlfw_fw_version_info_s_v01_ei[] = { {} }; -struct qmi_elem_info wlfw_ind_register_req_msg_v01_ei[] = { +const struct qmi_elem_info wlfw_ind_register_req_msg_v01_ei[] = { { .data_type = QMI_OPT_FLAG, .elem_len = 1, @@ -528,7 +528,7 @@ struct qmi_elem_info wlfw_ind_register_req_msg_v01_ei[] = { {} }; -struct qmi_elem_info wlfw_ind_register_resp_msg_v01_ei[] = { +const struct qmi_elem_info wlfw_ind_register_resp_msg_v01_ei[] = { { .data_type = QMI_STRUCT, .elem_len = 1, @@ -560,15 +560,15 @@ struct qmi_elem_info wlfw_ind_register_resp_msg_v01_ei[] = { {} }; -struct qmi_elem_info wlfw_fw_ready_ind_msg_v01_ei[] = { +const struct qmi_elem_info wlfw_fw_ready_ind_msg_v01_ei[] = { {} }; -struct qmi_elem_info wlfw_msa_ready_ind_msg_v01_ei[] = { +const struct qmi_elem_info wlfw_msa_ready_ind_msg_v01_ei[] = { {} }; -struct qmi_elem_info wlfw_pin_connect_result_ind_msg_v01_ei[] = { +const struct qmi_elem_info wlfw_pin_connect_result_ind_msg_v01_ei[] = { { .data_type = QMI_OPT_FLAG, .elem_len = 1, @@ -626,7 +626,7 @@ struct qmi_elem_info wlfw_pin_connect_result_ind_msg_v01_ei[] = { {} }; -struct qmi_elem_info wlfw_wlan_mode_req_msg_v01_ei[] = { +const struct qmi_elem_info wlfw_wlan_mode_req_msg_v01_ei[] = { { .data_type = QMI_SIGNED_4_BYTE_ENUM, .elem_len = 1, @@ -657,7 +657,7 @@ struct qmi_elem_info wlfw_wlan_mode_req_msg_v01_ei[] = { {} }; -struct qmi_elem_info wlfw_wlan_mode_resp_msg_v01_ei[] = { +const struct qmi_elem_info wlfw_wlan_mode_resp_msg_v01_ei[] = { { .data_type = QMI_STRUCT, .elem_len = 1, @@ -671,7 +671,7 @@ struct qmi_elem_info wlfw_wlan_mode_resp_msg_v01_ei[] = { {} }; -struct qmi_elem_info wlfw_wlan_cfg_req_msg_v01_ei[] = { +const struct qmi_elem_info wlfw_wlan_cfg_req_msg_v01_ei[] = { { .data_type = QMI_OPT_FLAG, .elem_len = 1, @@ -805,7 +805,7 @@ struct qmi_elem_info wlfw_wlan_cfg_req_msg_v01_ei[] = { {} }; -struct qmi_elem_info wlfw_wlan_cfg_resp_msg_v01_ei[] = { +const struct qmi_elem_info wlfw_wlan_cfg_resp_msg_v01_ei[] = { { .data_type = QMI_STRUCT, .elem_len = 1, @@ -819,11 +819,11 @@ struct qmi_elem_info wlfw_wlan_cfg_resp_msg_v01_ei[] = { {} }; -struct qmi_elem_info wlfw_cap_req_msg_v01_ei[] = { +const struct qmi_elem_info wlfw_cap_req_msg_v01_ei[] = { {} }; -struct qmi_elem_info wlfw_cap_resp_msg_v01_ei[] = { +const struct qmi_elem_info wlfw_cap_resp_msg_v01_ei[] = { { .data_type = QMI_STRUCT, .elem_len = 1, @@ -949,7 +949,7 @@ struct qmi_elem_info wlfw_cap_resp_msg_v01_ei[] = { {} }; -struct qmi_elem_info wlfw_bdf_download_req_msg_v01_ei[] = { +const struct qmi_elem_info wlfw_bdf_download_req_msg_v01_ei[] = { { .data_type = QMI_UNSIGNED_1_BYTE, .elem_len = 1, @@ -1079,7 +1079,7 @@ struct qmi_elem_info wlfw_bdf_download_req_msg_v01_ei[] = { {} }; -struct qmi_elem_info wlfw_bdf_download_resp_msg_v01_ei[] = { +const struct qmi_elem_info wlfw_bdf_download_resp_msg_v01_ei[] = { { .data_type = QMI_STRUCT, .elem_len = 1, @@ -1093,7 +1093,7 @@ struct qmi_elem_info wlfw_bdf_download_resp_msg_v01_ei[] = { {} }; -struct qmi_elem_info wlfw_cal_report_req_msg_v01_ei[] = { +const struct qmi_elem_info wlfw_cal_report_req_msg_v01_ei[] = { { .data_type = QMI_DATA_LEN, .elem_len = 1, @@ -1133,7 +1133,7 @@ struct qmi_elem_info wlfw_cal_report_req_msg_v01_ei[] = { {} }; -struct qmi_elem_info wlfw_cal_report_resp_msg_v01_ei[] = { +const struct qmi_elem_info wlfw_cal_report_resp_msg_v01_ei[] = { { .data_type = QMI_STRUCT, .elem_len = 1, @@ -1147,7 +1147,7 @@ struct qmi_elem_info wlfw_cal_report_resp_msg_v01_ei[] = { {} }; -struct qmi_elem_info wlfw_initiate_cal_download_ind_msg_v01_ei[] = { +const struct qmi_elem_info wlfw_initiate_cal_download_ind_msg_v01_ei[] = { { .data_type = QMI_SIGNED_4_BYTE_ENUM, .elem_len = 1, @@ -1160,7 +1160,7 @@ struct qmi_elem_info wlfw_initiate_cal_download_ind_msg_v01_ei[] = { {} }; -struct qmi_elem_info wlfw_cal_download_req_msg_v01_ei[] = { +const struct qmi_elem_info wlfw_cal_download_req_msg_v01_ei[] = { { .data_type = QMI_UNSIGNED_1_BYTE, .elem_len = 1, @@ -1272,7 +1272,7 @@ struct qmi_elem_info wlfw_cal_download_req_msg_v01_ei[] = { {} }; -struct qmi_elem_info wlfw_cal_download_resp_msg_v01_ei[] = { +const struct qmi_elem_info wlfw_cal_download_resp_msg_v01_ei[] = { { .data_type = QMI_STRUCT, .elem_len = 1, @@ -1286,7 +1286,7 @@ struct qmi_elem_info wlfw_cal_download_resp_msg_v01_ei[] = { {} }; -struct qmi_elem_info wlfw_initiate_cal_update_ind_msg_v01_ei[] = { +const struct qmi_elem_info wlfw_initiate_cal_update_ind_msg_v01_ei[] = { { .data_type = QMI_SIGNED_4_BYTE_ENUM, .elem_len = 1, @@ -1308,7 +1308,7 @@ struct qmi_elem_info wlfw_initiate_cal_update_ind_msg_v01_ei[] = { {} }; -struct qmi_elem_info wlfw_cal_update_req_msg_v01_ei[] = { +const struct qmi_elem_info wlfw_cal_update_req_msg_v01_ei[] = { { .data_type = QMI_SIGNED_4_BYTE_ENUM, .elem_len = 1, @@ -1330,7 +1330,7 @@ struct qmi_elem_info wlfw_cal_update_req_msg_v01_ei[] = { {} }; -struct qmi_elem_info wlfw_cal_update_resp_msg_v01_ei[] = { +const struct qmi_elem_info wlfw_cal_update_resp_msg_v01_ei[] = { { .data_type = QMI_STRUCT, .elem_len = 1, @@ -1443,7 +1443,7 @@ struct qmi_elem_info wlfw_cal_update_resp_msg_v01_ei[] = { {} }; -struct qmi_elem_info wlfw_msa_info_req_msg_v01_ei[] = { +const struct qmi_elem_info wlfw_msa_info_req_msg_v01_ei[] = { { .data_type = QMI_UNSIGNED_8_BYTE, .elem_len = 1, @@ -1465,7 +1465,7 @@ struct qmi_elem_info wlfw_msa_info_req_msg_v01_ei[] = { {} }; -struct qmi_elem_info wlfw_msa_info_resp_msg_v01_ei[] = { +const struct qmi_elem_info wlfw_msa_info_resp_msg_v01_ei[] = { { .data_type = QMI_STRUCT, .elem_len = 1, @@ -1498,11 +1498,11 @@ struct qmi_elem_info wlfw_msa_info_resp_msg_v01_ei[] = { {} }; -struct qmi_elem_info wlfw_msa_ready_req_msg_v01_ei[] = { +const struct qmi_elem_info wlfw_msa_ready_req_msg_v01_ei[] = { {} }; -struct qmi_elem_info wlfw_msa_ready_resp_msg_v01_ei[] = { +const struct qmi_elem_info wlfw_msa_ready_resp_msg_v01_ei[] = { { .data_type = QMI_STRUCT, .elem_len = 1, @@ -1516,7 +1516,7 @@ struct qmi_elem_info wlfw_msa_ready_resp_msg_v01_ei[] = { {} }; -struct qmi_elem_info wlfw_ini_req_msg_v01_ei[] = { +const struct qmi_elem_info wlfw_ini_req_msg_v01_ei[] = { { .data_type = QMI_OPT_FLAG, .elem_len = 1, @@ -1538,7 +1538,7 @@ struct qmi_elem_info wlfw_ini_req_msg_v01_ei[] = { {} }; -struct qmi_elem_info wlfw_ini_resp_msg_v01_ei[] = { +const struct qmi_elem_info wlfw_ini_resp_msg_v01_ei[] = { { .data_type = QMI_STRUCT, .elem_len = 1, @@ -1552,7 +1552,7 @@ struct qmi_elem_info wlfw_ini_resp_msg_v01_ei[] = { {} }; -struct qmi_elem_info wlfw_athdiag_read_req_msg_v01_ei[] = { +const struct qmi_elem_info wlfw_athdiag_read_req_msg_v01_ei[] = { { .data_type = QMI_UNSIGNED_4_BYTE, .elem_len = 1, @@ -1583,7 +1583,7 @@ struct qmi_elem_info wlfw_athdiag_read_req_msg_v01_ei[] = { {} }; -struct qmi_elem_info wlfw_athdiag_read_resp_msg_v01_ei[] = { +const struct qmi_elem_info wlfw_athdiag_read_resp_msg_v01_ei[] = { { .data_type = QMI_STRUCT, .elem_len = 1, @@ -1624,7 +1624,7 @@ struct qmi_elem_info wlfw_athdiag_read_resp_msg_v01_ei[] = { {} }; -struct qmi_elem_info wlfw_athdiag_write_req_msg_v01_ei[] = { +const struct qmi_elem_info wlfw_athdiag_write_req_msg_v01_ei[] = { { .data_type = QMI_UNSIGNED_4_BYTE, .elem_len = 1, @@ -1664,7 +1664,7 @@ struct qmi_elem_info wlfw_athdiag_write_req_msg_v01_ei[] = { {} }; -struct qmi_elem_info wlfw_athdiag_write_resp_msg_v01_ei[] = { +const struct qmi_elem_info wlfw_athdiag_write_resp_msg_v01_ei[] = { { .data_type = QMI_STRUCT, .elem_len = 1, @@ -1678,7 +1678,7 @@ struct qmi_elem_info wlfw_athdiag_write_resp_msg_v01_ei[] = { {} }; -struct qmi_elem_info wlfw_vbatt_req_msg_v01_ei[] = { +const struct qmi_elem_info wlfw_vbatt_req_msg_v01_ei[] = { { .data_type = QMI_UNSIGNED_8_BYTE, .elem_len = 1, @@ -1691,7 +1691,7 @@ struct qmi_elem_info wlfw_vbatt_req_msg_v01_ei[] = { {} }; -struct qmi_elem_info wlfw_vbatt_resp_msg_v01_ei[] = { +const struct qmi_elem_info wlfw_vbatt_resp_msg_v01_ei[] = { { .data_type = QMI_STRUCT, .elem_len = 1, @@ -1705,7 +1705,7 @@ struct qmi_elem_info wlfw_vbatt_resp_msg_v01_ei[] = { {} }; -struct qmi_elem_info wlfw_mac_addr_req_msg_v01_ei[] = { +const struct qmi_elem_info wlfw_mac_addr_req_msg_v01_ei[] = { { .data_type = QMI_OPT_FLAG, .elem_len = 1, @@ -1727,7 +1727,7 @@ struct qmi_elem_info wlfw_mac_addr_req_msg_v01_ei[] = { {} }; -struct qmi_elem_info wlfw_mac_addr_resp_msg_v01_ei[] = { +const struct qmi_elem_info wlfw_mac_addr_resp_msg_v01_ei[] = { { .data_type = QMI_STRUCT, .elem_len = 1, @@ -1741,7 +1741,7 @@ struct qmi_elem_info wlfw_mac_addr_resp_msg_v01_ei[] = { {} }; -struct qmi_elem_info wlfw_host_cap_req_msg_v01_ei[] = { +const struct qmi_elem_info wlfw_host_cap_req_msg_v01_ei[] = { { .data_type = QMI_OPT_FLAG, .elem_len = 1, @@ -1988,7 +1988,7 @@ struct qmi_elem_info wlfw_host_cap_req_msg_v01_ei[] = { {} }; -struct qmi_elem_info wlfw_host_cap_8bit_req_msg_v01_ei[] = { +const struct qmi_elem_info wlfw_host_cap_8bit_req_msg_v01_ei[] = { { .data_type = QMI_OPT_FLAG, .elem_len = 1, @@ -2010,7 +2010,7 @@ struct qmi_elem_info wlfw_host_cap_8bit_req_msg_v01_ei[] = { {} }; -struct qmi_elem_info wlfw_host_cap_resp_msg_v01_ei[] = { +const struct qmi_elem_info wlfw_host_cap_resp_msg_v01_ei[] = { { .data_type = QMI_STRUCT, .elem_len = 1, @@ -2024,7 +2024,7 @@ struct qmi_elem_info wlfw_host_cap_resp_msg_v01_ei[] = { {} }; -struct qmi_elem_info wlfw_request_mem_ind_msg_v01_ei[] = { +const struct qmi_elem_info wlfw_request_mem_ind_msg_v01_ei[] = { { .data_type = QMI_DATA_LEN, .elem_len = 1, @@ -2047,7 +2047,7 @@ struct qmi_elem_info wlfw_request_mem_ind_msg_v01_ei[] = { {} }; -struct qmi_elem_info wlfw_respond_mem_req_msg_v01_ei[] = { +const struct qmi_elem_info wlfw_respond_mem_req_msg_v01_ei[] = { { .data_type = QMI_DATA_LEN, .elem_len = 1, @@ -2070,7 +2070,7 @@ struct qmi_elem_info wlfw_respond_mem_req_msg_v01_ei[] = { {} }; -struct qmi_elem_info wlfw_respond_mem_resp_msg_v01_ei[] = { +const struct qmi_elem_info wlfw_respond_mem_resp_msg_v01_ei[] = { { .data_type = QMI_STRUCT, .elem_len = 1, @@ -2084,15 +2084,15 @@ struct qmi_elem_info wlfw_respond_mem_resp_msg_v01_ei[] = { {} }; -struct qmi_elem_info wlfw_mem_ready_ind_msg_v01_ei[] = { +const struct qmi_elem_info wlfw_mem_ready_ind_msg_v01_ei[] = { {} }; -struct qmi_elem_info wlfw_fw_init_done_ind_msg_v01_ei[] = { +const struct qmi_elem_info wlfw_fw_init_done_ind_msg_v01_ei[] = { {} }; -struct qmi_elem_info wlfw_rejuvenate_ind_msg_v01_ei[] = { +const struct qmi_elem_info wlfw_rejuvenate_ind_msg_v01_ei[] = { { .data_type = QMI_OPT_FLAG, .elem_len = 1, @@ -2168,11 +2168,11 @@ struct qmi_elem_info wlfw_rejuvenate_ind_msg_v01_ei[] = { {} }; -struct qmi_elem_info wlfw_rejuvenate_ack_req_msg_v01_ei[] = { +const struct qmi_elem_info wlfw_rejuvenate_ack_req_msg_v01_ei[] = { {} }; -struct qmi_elem_info wlfw_rejuvenate_ack_resp_msg_v01_ei[] = { +const struct qmi_elem_info wlfw_rejuvenate_ack_resp_msg_v01_ei[] = { { .data_type = QMI_STRUCT, .elem_len = 1, @@ -2186,7 +2186,7 @@ struct qmi_elem_info wlfw_rejuvenate_ack_resp_msg_v01_ei[] = { {} }; -struct qmi_elem_info wlfw_dynamic_feature_mask_req_msg_v01_ei[] = { +const struct qmi_elem_info wlfw_dynamic_feature_mask_req_msg_v01_ei[] = { { .data_type = QMI_OPT_FLAG, .elem_len = 1, @@ -2208,7 +2208,7 @@ struct qmi_elem_info wlfw_dynamic_feature_mask_req_msg_v01_ei[] = { {} }; -struct qmi_elem_info wlfw_dynamic_feature_mask_resp_msg_v01_ei[] = { +const struct qmi_elem_info wlfw_dynamic_feature_mask_resp_msg_v01_ei[] = { { .data_type = QMI_STRUCT, .elem_len = 1, @@ -2258,7 +2258,7 @@ struct qmi_elem_info wlfw_dynamic_feature_mask_resp_msg_v01_ei[] = { {} }; -struct qmi_elem_info wlfw_m3_info_req_msg_v01_ei[] = { +const struct qmi_elem_info wlfw_m3_info_req_msg_v01_ei[] = { { .data_type = QMI_UNSIGNED_8_BYTE, .elem_len = 1, @@ -2280,7 +2280,7 @@ struct qmi_elem_info wlfw_m3_info_req_msg_v01_ei[] = { {} }; -struct qmi_elem_info wlfw_m3_info_resp_msg_v01_ei[] = { +const struct qmi_elem_info wlfw_m3_info_resp_msg_v01_ei[] = { { .data_type = QMI_STRUCT, .elem_len = 1, @@ -2294,7 +2294,7 @@ struct qmi_elem_info wlfw_m3_info_resp_msg_v01_ei[] = { {} }; -struct qmi_elem_info wlfw_xo_cal_ind_msg_v01_ei[] = { +const struct qmi_elem_info wlfw_xo_cal_ind_msg_v01_ei[] = { { .data_type = QMI_UNSIGNED_1_BYTE, .elem_len = 1, diff --git a/drivers/net/wireless/ath/ath10k/qmi_wlfw_v01.h b/drivers/net/wireless/ath/ath10k/qmi_wlfw_v01.h index 4d107e1364a8..f0db991408dc 100644 --- a/drivers/net/wireless/ath/ath10k/qmi_wlfw_v01.h +++ b/drivers/net/wireless/ath/ath10k/qmi_wlfw_v01.h @@ -215,7 +215,7 @@ struct wlfw_ind_register_req_msg_v01 { }; #define WLFW_IND_REGISTER_REQ_MSG_V01_MAX_MSG_LEN 50 -extern struct qmi_elem_info wlfw_ind_register_req_msg_v01_ei[]; +extern const struct qmi_elem_info wlfw_ind_register_req_msg_v01_ei[]; struct wlfw_ind_register_resp_msg_v01 { struct qmi_response_type_v01 resp; @@ -224,21 +224,21 @@ struct wlfw_ind_register_resp_msg_v01 { }; #define WLFW_IND_REGISTER_RESP_MSG_V01_MAX_MSG_LEN 18 -extern struct qmi_elem_info wlfw_ind_register_resp_msg_v01_ei[]; +extern const struct qmi_elem_info wlfw_ind_register_resp_msg_v01_ei[]; struct wlfw_fw_ready_ind_msg_v01 { char placeholder; }; #define WLFW_FW_READY_IND_MSG_V01_MAX_MSG_LEN 0 -extern struct qmi_elem_info wlfw_fw_ready_ind_msg_v01_ei[]; +extern const struct qmi_elem_info wlfw_fw_ready_ind_msg_v01_ei[]; struct wlfw_msa_ready_ind_msg_v01 { char placeholder; }; #define WLFW_MSA_READY_IND_MSG_V01_MAX_MSG_LEN 0 -extern struct qmi_elem_info wlfw_msa_ready_ind_msg_v01_ei[]; +extern const struct qmi_elem_info wlfw_msa_ready_ind_msg_v01_ei[]; struct wlfw_pin_connect_result_ind_msg_v01 { u8 pwr_pin_result_valid; @@ -250,7 +250,7 @@ struct wlfw_pin_connect_result_ind_msg_v01 { }; #define WLFW_PIN_CONNECT_RESULT_IND_MSG_V01_MAX_MSG_LEN 21 -extern struct qmi_elem_info wlfw_pin_connect_result_ind_msg_v01_ei[]; +extern const struct qmi_elem_info wlfw_pin_connect_result_ind_msg_v01_ei[]; struct wlfw_wlan_mode_req_msg_v01 { enum wlfw_driver_mode_enum_v01 mode; @@ -259,14 +259,14 @@ struct wlfw_wlan_mode_req_msg_v01 { }; #define WLFW_WLAN_MODE_REQ_MSG_V01_MAX_MSG_LEN 11 -extern struct qmi_elem_info wlfw_wlan_mode_req_msg_v01_ei[]; +extern const struct qmi_elem_info wlfw_wlan_mode_req_msg_v01_ei[]; struct wlfw_wlan_mode_resp_msg_v01 { struct qmi_response_type_v01 resp; }; #define WLFW_WLAN_MODE_RESP_MSG_V01_MAX_MSG_LEN 7 -extern struct qmi_elem_info wlfw_wlan_mode_resp_msg_v01_ei[]; +extern const struct qmi_elem_info wlfw_wlan_mode_resp_msg_v01_ei[]; struct wlfw_wlan_cfg_req_msg_v01 { u8 host_version_valid; @@ -286,21 +286,21 @@ struct wlfw_wlan_cfg_req_msg_v01 { }; #define WLFW_WLAN_CFG_REQ_MSG_V01_MAX_MSG_LEN 803 -extern struct qmi_elem_info wlfw_wlan_cfg_req_msg_v01_ei[]; +extern const struct qmi_elem_info wlfw_wlan_cfg_req_msg_v01_ei[]; struct wlfw_wlan_cfg_resp_msg_v01 { struct qmi_response_type_v01 resp; }; #define WLFW_WLAN_CFG_RESP_MSG_V01_MAX_MSG_LEN 7 -extern struct qmi_elem_info wlfw_wlan_cfg_resp_msg_v01_ei[]; +extern const struct qmi_elem_info wlfw_wlan_cfg_resp_msg_v01_ei[]; struct wlfw_cap_req_msg_v01 { char placeholder; }; #define WLFW_CAP_REQ_MSG_V01_MAX_MSG_LEN 0 -extern struct qmi_elem_info wlfw_cap_req_msg_v01_ei[]; +extern const struct qmi_elem_info wlfw_cap_req_msg_v01_ei[]; struct wlfw_cap_resp_msg_v01 { struct qmi_response_type_v01 resp; @@ -319,7 +319,7 @@ struct wlfw_cap_resp_msg_v01 { }; #define WLFW_CAP_RESP_MSG_V01_MAX_MSG_LEN 207 -extern struct qmi_elem_info wlfw_cap_resp_msg_v01_ei[]; +extern const struct qmi_elem_info wlfw_cap_resp_msg_v01_ei[]; struct wlfw_bdf_download_req_msg_v01 { u8 valid; @@ -339,14 +339,14 @@ struct wlfw_bdf_download_req_msg_v01 { }; #define WLFW_BDF_DOWNLOAD_REQ_MSG_V01_MAX_MSG_LEN 6182 -extern struct qmi_elem_info wlfw_bdf_download_req_msg_v01_ei[]; +extern const struct qmi_elem_info wlfw_bdf_download_req_msg_v01_ei[]; struct wlfw_bdf_download_resp_msg_v01 { struct qmi_response_type_v01 resp; }; #define WLFW_BDF_DOWNLOAD_RESP_MSG_V01_MAX_MSG_LEN 7 -extern struct qmi_elem_info wlfw_bdf_download_resp_msg_v01_ei[]; +extern const struct qmi_elem_info wlfw_bdf_download_resp_msg_v01_ei[]; struct wlfw_cal_report_req_msg_v01 { u32 meta_data_len; @@ -356,21 +356,21 @@ struct wlfw_cal_report_req_msg_v01 { }; #define WLFW_CAL_REPORT_REQ_MSG_V01_MAX_MSG_LEN 28 -extern struct qmi_elem_info wlfw_cal_report_req_msg_v01_ei[]; +extern const struct qmi_elem_info wlfw_cal_report_req_msg_v01_ei[]; struct wlfw_cal_report_resp_msg_v01 { struct qmi_response_type_v01 resp; }; #define WLFW_CAL_REPORT_RESP_MSG_V01_MAX_MSG_LEN 7 -extern struct qmi_elem_info wlfw_cal_report_resp_msg_v01_ei[]; +extern const struct qmi_elem_info wlfw_cal_report_resp_msg_v01_ei[]; struct wlfw_initiate_cal_download_ind_msg_v01 { enum wlfw_cal_temp_id_enum_v01 cal_id; }; #define WLFW_INITIATE_CAL_DOWNLOAD_IND_MSG_V01_MAX_MSG_LEN 7 -extern struct qmi_elem_info wlfw_initiate_cal_download_ind_msg_v01_ei[]; +extern const struct qmi_elem_info wlfw_initiate_cal_download_ind_msg_v01_ei[]; struct wlfw_cal_download_req_msg_v01 { u8 valid; @@ -388,14 +388,14 @@ struct wlfw_cal_download_req_msg_v01 { }; #define WLFW_CAL_DOWNLOAD_REQ_MSG_V01_MAX_MSG_LEN 6178 -extern struct qmi_elem_info wlfw_cal_download_req_msg_v01_ei[]; +extern const struct qmi_elem_info wlfw_cal_download_req_msg_v01_ei[]; struct wlfw_cal_download_resp_msg_v01 { struct qmi_response_type_v01 resp; }; #define WLFW_CAL_DOWNLOAD_RESP_MSG_V01_MAX_MSG_LEN 7 -extern struct qmi_elem_info wlfw_cal_download_resp_msg_v01_ei[]; +extern const struct qmi_elem_info wlfw_cal_download_resp_msg_v01_ei[]; struct wlfw_initiate_cal_update_ind_msg_v01 { enum wlfw_cal_temp_id_enum_v01 cal_id; @@ -403,7 +403,7 @@ struct wlfw_initiate_cal_update_ind_msg_v01 { }; #define WLFW_INITIATE_CAL_UPDATE_IND_MSG_V01_MAX_MSG_LEN 14 -extern struct qmi_elem_info wlfw_initiate_cal_update_ind_msg_v01_ei[]; +extern const struct qmi_elem_info wlfw_initiate_cal_update_ind_msg_v01_ei[]; struct wlfw_cal_update_req_msg_v01 { enum wlfw_cal_temp_id_enum_v01 cal_id; @@ -411,7 +411,7 @@ struct wlfw_cal_update_req_msg_v01 { }; #define WLFW_CAL_UPDATE_REQ_MSG_V01_MAX_MSG_LEN 14 -extern struct qmi_elem_info wlfw_cal_update_req_msg_v01_ei[]; +extern const struct qmi_elem_info wlfw_cal_update_req_msg_v01_ei[]; struct wlfw_cal_update_resp_msg_v01 { struct qmi_response_type_v01 resp; @@ -429,7 +429,7 @@ struct wlfw_cal_update_resp_msg_v01 { }; #define WLFW_CAL_UPDATE_RESP_MSG_V01_MAX_MSG_LEN 6181 -extern struct qmi_elem_info wlfw_cal_update_resp_msg_v01_ei[]; +extern const struct qmi_elem_info wlfw_cal_update_resp_msg_v01_ei[]; struct wlfw_msa_info_req_msg_v01 { u64 msa_addr; @@ -437,7 +437,7 @@ struct wlfw_msa_info_req_msg_v01 { }; #define WLFW_MSA_INFO_REQ_MSG_V01_MAX_MSG_LEN 18 -extern struct qmi_elem_info wlfw_msa_info_req_msg_v01_ei[]; +extern const struct qmi_elem_info wlfw_msa_info_req_msg_v01_ei[]; struct wlfw_msa_info_resp_msg_v01 { struct qmi_response_type_v01 resp; @@ -446,21 +446,21 @@ struct wlfw_msa_info_resp_msg_v01 { }; #define WLFW_MSA_INFO_RESP_MSG_V01_MAX_MSG_LEN 37 -extern struct qmi_elem_info wlfw_msa_info_resp_msg_v01_ei[]; +extern const struct qmi_elem_info wlfw_msa_info_resp_msg_v01_ei[]; struct wlfw_msa_ready_req_msg_v01 { char placeholder; }; #define WLFW_MSA_READY_REQ_MSG_V01_MAX_MSG_LEN 0 -extern struct qmi_elem_info wlfw_msa_ready_req_msg_v01_ei[]; +extern const struct qmi_elem_info wlfw_msa_ready_req_msg_v01_ei[]; struct wlfw_msa_ready_resp_msg_v01 { struct qmi_response_type_v01 resp; }; #define WLFW_MSA_READY_RESP_MSG_V01_MAX_MSG_LEN 7 -extern struct qmi_elem_info wlfw_msa_ready_resp_msg_v01_ei[]; +extern const struct qmi_elem_info wlfw_msa_ready_resp_msg_v01_ei[]; struct wlfw_ini_req_msg_v01 { u8 enablefwlog_valid; @@ -468,14 +468,14 @@ struct wlfw_ini_req_msg_v01 { }; #define WLFW_INI_REQ_MSG_V01_MAX_MSG_LEN 4 -extern struct qmi_elem_info wlfw_ini_req_msg_v01_ei[]; +extern const struct qmi_elem_info wlfw_ini_req_msg_v01_ei[]; struct wlfw_ini_resp_msg_v01 { struct qmi_response_type_v01 resp; }; #define WLFW_INI_RESP_MSG_V01_MAX_MSG_LEN 7 -extern struct qmi_elem_info wlfw_ini_resp_msg_v01_ei[]; +extern const struct qmi_elem_info wlfw_ini_resp_msg_v01_ei[]; struct wlfw_athdiag_read_req_msg_v01 { u32 offset; @@ -484,7 +484,7 @@ struct wlfw_athdiag_read_req_msg_v01 { }; #define WLFW_ATHDIAG_READ_REQ_MSG_V01_MAX_MSG_LEN 21 -extern struct qmi_elem_info wlfw_athdiag_read_req_msg_v01_ei[]; +extern const struct qmi_elem_info wlfw_athdiag_read_req_msg_v01_ei[]; struct wlfw_athdiag_read_resp_msg_v01 { struct qmi_response_type_v01 resp; @@ -494,7 +494,7 @@ struct wlfw_athdiag_read_resp_msg_v01 { }; #define WLFW_ATHDIAG_READ_RESP_MSG_V01_MAX_MSG_LEN 6156 -extern struct qmi_elem_info wlfw_athdiag_read_resp_msg_v01_ei[]; +extern const struct qmi_elem_info wlfw_athdiag_read_resp_msg_v01_ei[]; struct wlfw_athdiag_write_req_msg_v01 { u32 offset; @@ -504,28 +504,28 @@ struct wlfw_athdiag_write_req_msg_v01 { }; #define WLFW_ATHDIAG_WRITE_REQ_MSG_V01_MAX_MSG_LEN 6163 -extern struct qmi_elem_info wlfw_athdiag_write_req_msg_v01_ei[]; +extern const struct qmi_elem_info wlfw_athdiag_write_req_msg_v01_ei[]; struct wlfw_athdiag_write_resp_msg_v01 { struct qmi_response_type_v01 resp; }; #define WLFW_ATHDIAG_WRITE_RESP_MSG_V01_MAX_MSG_LEN 7 -extern struct qmi_elem_info wlfw_athdiag_write_resp_msg_v01_ei[]; +extern const struct qmi_elem_info wlfw_athdiag_write_resp_msg_v01_ei[]; struct wlfw_vbatt_req_msg_v01 { u64 voltage_uv; }; #define WLFW_VBATT_REQ_MSG_V01_MAX_MSG_LEN 11 -extern struct qmi_elem_info wlfw_vbatt_req_msg_v01_ei[]; +extern const struct qmi_elem_info wlfw_vbatt_req_msg_v01_ei[]; struct wlfw_vbatt_resp_msg_v01 { struct qmi_response_type_v01 resp; }; #define WLFW_VBATT_RESP_MSG_V01_MAX_MSG_LEN 7 -extern struct qmi_elem_info wlfw_vbatt_resp_msg_v01_ei[]; +extern const struct qmi_elem_info wlfw_vbatt_resp_msg_v01_ei[]; struct wlfw_mac_addr_req_msg_v01 { u8 mac_addr_valid; @@ -533,14 +533,14 @@ struct wlfw_mac_addr_req_msg_v01 { }; #define WLFW_MAC_ADDR_REQ_MSG_V01_MAX_MSG_LEN 9 -extern struct qmi_elem_info wlfw_mac_addr_req_msg_v01_ei[]; +extern const struct qmi_elem_info wlfw_mac_addr_req_msg_v01_ei[]; struct wlfw_mac_addr_resp_msg_v01 { struct qmi_response_type_v01 resp; }; #define WLFW_MAC_ADDR_RESP_MSG_V01_MAX_MSG_LEN 7 -extern struct qmi_elem_info wlfw_mac_addr_resp_msg_v01_ei[]; +extern const struct qmi_elem_info wlfw_mac_addr_resp_msg_v01_ei[]; #define QMI_WLFW_MAX_NUM_GPIO_V01 32 struct wlfw_host_cap_req_msg_v01 { @@ -574,15 +574,15 @@ struct wlfw_host_cap_req_msg_v01 { }; #define WLFW_HOST_CAP_REQ_MSG_V01_MAX_MSG_LEN 189 -extern struct qmi_elem_info wlfw_host_cap_req_msg_v01_ei[]; -extern struct qmi_elem_info wlfw_host_cap_8bit_req_msg_v01_ei[]; +extern const struct qmi_elem_info wlfw_host_cap_req_msg_v01_ei[]; +extern const struct qmi_elem_info wlfw_host_cap_8bit_req_msg_v01_ei[]; struct wlfw_host_cap_resp_msg_v01 { struct qmi_response_type_v01 resp; }; #define WLFW_HOST_CAP_RESP_MSG_V01_MAX_MSG_LEN 7 -extern struct qmi_elem_info wlfw_host_cap_resp_msg_v01_ei[]; +extern const struct qmi_elem_info wlfw_host_cap_resp_msg_v01_ei[]; struct wlfw_request_mem_ind_msg_v01 { u32 mem_seg_len; @@ -590,7 +590,7 @@ struct wlfw_request_mem_ind_msg_v01 { }; #define WLFW_REQUEST_MEM_IND_MSG_V01_MAX_MSG_LEN 564 -extern struct qmi_elem_info wlfw_request_mem_ind_msg_v01_ei[]; +extern const struct qmi_elem_info wlfw_request_mem_ind_msg_v01_ei[]; struct wlfw_respond_mem_req_msg_v01 { u32 mem_seg_len; @@ -598,28 +598,28 @@ struct wlfw_respond_mem_req_msg_v01 { }; #define WLFW_RESPOND_MEM_REQ_MSG_V01_MAX_MSG_LEN 260 -extern struct qmi_elem_info wlfw_respond_mem_req_msg_v01_ei[]; +extern const struct qmi_elem_info wlfw_respond_mem_req_msg_v01_ei[]; struct wlfw_respond_mem_resp_msg_v01 { struct qmi_response_type_v01 resp; }; #define WLFW_RESPOND_MEM_RESP_MSG_V01_MAX_MSG_LEN 7 -extern struct qmi_elem_info wlfw_respond_mem_resp_msg_v01_ei[]; +extern const struct qmi_elem_info wlfw_respond_mem_resp_msg_v01_ei[]; struct wlfw_mem_ready_ind_msg_v01 { char placeholder; }; #define WLFW_MEM_READY_IND_MSG_V01_MAX_MSG_LEN 0 -extern struct qmi_elem_info wlfw_mem_ready_ind_msg_v01_ei[]; +extern const struct qmi_elem_info wlfw_mem_ready_ind_msg_v01_ei[]; struct wlfw_fw_init_done_ind_msg_v01 { char placeholder; }; #define WLFW_FW_INIT_DONE_IND_MSG_V01_MAX_MSG_LEN 0 -extern struct qmi_elem_info wlfw_fw_init_done_ind_msg_v01_ei[]; +extern const struct qmi_elem_info wlfw_fw_init_done_ind_msg_v01_ei[]; struct wlfw_rejuvenate_ind_msg_v01 { u8 cause_for_rejuvenation_valid; @@ -633,21 +633,21 @@ struct wlfw_rejuvenate_ind_msg_v01 { }; #define WLFW_REJUVENATE_IND_MSG_V01_MAX_MSG_LEN 144 -extern struct qmi_elem_info wlfw_rejuvenate_ind_msg_v01_ei[]; +extern const struct qmi_elem_info wlfw_rejuvenate_ind_msg_v01_ei[]; struct wlfw_rejuvenate_ack_req_msg_v01 { char placeholder; }; #define WLFW_REJUVENATE_ACK_REQ_MSG_V01_MAX_MSG_LEN 0 -extern struct qmi_elem_info wlfw_rejuvenate_ack_req_msg_v01_ei[]; +extern const struct qmi_elem_info wlfw_rejuvenate_ack_req_msg_v01_ei[]; struct wlfw_rejuvenate_ack_resp_msg_v01 { struct qmi_response_type_v01 resp; }; #define WLFW_REJUVENATE_ACK_RESP_MSG_V01_MAX_MSG_LEN 7 -extern struct qmi_elem_info wlfw_rejuvenate_ack_resp_msg_v01_ei[]; +extern const struct qmi_elem_info wlfw_rejuvenate_ack_resp_msg_v01_ei[]; struct wlfw_dynamic_feature_mask_req_msg_v01 { u8 mask_valid; @@ -655,7 +655,7 @@ struct wlfw_dynamic_feature_mask_req_msg_v01 { }; #define WLFW_DYNAMIC_FEATURE_MASK_REQ_MSG_V01_MAX_MSG_LEN 11 -extern struct qmi_elem_info wlfw_dynamic_feature_mask_req_msg_v01_ei[]; +extern const struct qmi_elem_info wlfw_dynamic_feature_mask_req_msg_v01_ei[]; struct wlfw_dynamic_feature_mask_resp_msg_v01 { struct qmi_response_type_v01 resp; @@ -666,7 +666,7 @@ struct wlfw_dynamic_feature_mask_resp_msg_v01 { }; #define WLFW_DYNAMIC_FEATURE_MASK_RESP_MSG_V01_MAX_MSG_LEN 29 -extern struct qmi_elem_info wlfw_dynamic_feature_mask_resp_msg_v01_ei[]; +extern const struct qmi_elem_info wlfw_dynamic_feature_mask_resp_msg_v01_ei[]; struct wlfw_m3_info_req_msg_v01 { u64 addr; @@ -674,20 +674,20 @@ struct wlfw_m3_info_req_msg_v01 { }; #define WLFW_M3_INFO_REQ_MSG_V01_MAX_MSG_LEN 18 -extern struct qmi_elem_info wlfw_m3_info_req_msg_v01_ei[]; +extern const struct qmi_elem_info wlfw_m3_info_req_msg_v01_ei[]; struct wlfw_m3_info_resp_msg_v01 { struct qmi_response_type_v01 resp; }; #define WLFW_M3_INFO_RESP_MSG_V01_MAX_MSG_LEN 7 -extern struct qmi_elem_info wlfw_m3_info_resp_msg_v01_ei[]; +extern const struct qmi_elem_info wlfw_m3_info_resp_msg_v01_ei[]; struct wlfw_xo_cal_ind_msg_v01 { u8 xo_cal_data; }; #define WLFW_XO_CAL_IND_MSG_V01_MAX_MSG_LEN 4 -extern struct qmi_elem_info wlfw_xo_cal_ind_msg_v01_ei[]; +extern const struct qmi_elem_info wlfw_xo_cal_ind_msg_v01_ei[]; #endif From 93c1592889fca46d09d833455628bab05516cdbf Mon Sep 17 00:00:00 2001 From: Jeff Johnson Date: Wed, 14 Sep 2022 17:23:03 -0700 Subject: [PATCH 0563/2002] wifi: ath11k: Make QMI message rules const Commit ff6d365898d4 ("soc: qcom: qmi: use const for struct qmi_elem_info") allows QMI message encoding/decoding rules to be const, so do that for ath11k. Compile tested only. Signed-off-by: Jeff Johnson Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20220915002303.12206-1-quic_jjohnson@quicinc.com --- drivers/net/wireless/ath/ath11k/qmi.c | 72 +++++++++++++-------------- 1 file changed, 36 insertions(+), 36 deletions(-) diff --git a/drivers/net/wireless/ath/ath11k/qmi.c b/drivers/net/wireless/ath/ath11k/qmi.c index 72cfc13ef6a8..ab923e24b0a9 100644 --- a/drivers/net/wireless/ath/ath11k/qmi.c +++ b/drivers/net/wireless/ath/ath11k/qmi.c @@ -29,7 +29,7 @@ module_param_named(cold_boot_cal, ath11k_cold_boot_cal, bool, 0644); MODULE_PARM_DESC(cold_boot_cal, "Decrease the channel switch time but increase the driver load time (Default: true)"); -static struct qmi_elem_info qmi_wlanfw_host_cap_req_msg_v01_ei[] = { +static const struct qmi_elem_info qmi_wlanfw_host_cap_req_msg_v01_ei[] = { { .data_type = QMI_OPT_FLAG, .elem_len = 1, @@ -280,7 +280,7 @@ static struct qmi_elem_info qmi_wlanfw_host_cap_req_msg_v01_ei[] = { }, }; -static struct qmi_elem_info qmi_wlanfw_host_cap_resp_msg_v01_ei[] = { +static const struct qmi_elem_info qmi_wlanfw_host_cap_resp_msg_v01_ei[] = { { .data_type = QMI_STRUCT, .elem_len = 1, @@ -297,7 +297,7 @@ static struct qmi_elem_info qmi_wlanfw_host_cap_resp_msg_v01_ei[] = { }, }; -static struct qmi_elem_info qmi_wlanfw_ind_register_req_msg_v01_ei[] = { +static const struct qmi_elem_info qmi_wlanfw_ind_register_req_msg_v01_ei[] = { { .data_type = QMI_OPT_FLAG, .elem_len = 1, @@ -522,7 +522,7 @@ static struct qmi_elem_info qmi_wlanfw_ind_register_req_msg_v01_ei[] = { }, }; -static struct qmi_elem_info qmi_wlanfw_ind_register_resp_msg_v01_ei[] = { +static const struct qmi_elem_info qmi_wlanfw_ind_register_resp_msg_v01_ei[] = { { .data_type = QMI_STRUCT, .elem_len = 1, @@ -558,7 +558,7 @@ static struct qmi_elem_info qmi_wlanfw_ind_register_resp_msg_v01_ei[] = { }, }; -static struct qmi_elem_info qmi_wlanfw_mem_cfg_s_v01_ei[] = { +static const struct qmi_elem_info qmi_wlanfw_mem_cfg_s_v01_ei[] = { { .data_type = QMI_UNSIGNED_8_BYTE, .elem_len = 1, @@ -590,7 +590,7 @@ static struct qmi_elem_info qmi_wlanfw_mem_cfg_s_v01_ei[] = { }, }; -static struct qmi_elem_info qmi_wlanfw_mem_seg_s_v01_ei[] = { +static const struct qmi_elem_info qmi_wlanfw_mem_seg_s_v01_ei[] = { { .data_type = QMI_UNSIGNED_4_BYTE, .elem_len = 1, @@ -632,7 +632,7 @@ static struct qmi_elem_info qmi_wlanfw_mem_seg_s_v01_ei[] = { }, }; -static struct qmi_elem_info qmi_wlanfw_request_mem_ind_msg_v01_ei[] = { +static const struct qmi_elem_info qmi_wlanfw_request_mem_ind_msg_v01_ei[] = { { .data_type = QMI_DATA_LEN, .elem_len = 1, @@ -659,7 +659,7 @@ static struct qmi_elem_info qmi_wlanfw_request_mem_ind_msg_v01_ei[] = { }, }; -static struct qmi_elem_info qmi_wlanfw_mem_seg_resp_s_v01_ei[] = { +static const struct qmi_elem_info qmi_wlanfw_mem_seg_resp_s_v01_ei[] = { { .data_type = QMI_UNSIGNED_8_BYTE, .elem_len = 1, @@ -699,7 +699,7 @@ static struct qmi_elem_info qmi_wlanfw_mem_seg_resp_s_v01_ei[] = { }, }; -static struct qmi_elem_info qmi_wlanfw_respond_mem_req_msg_v01_ei[] = { +static const struct qmi_elem_info qmi_wlanfw_respond_mem_req_msg_v01_ei[] = { { .data_type = QMI_DATA_LEN, .elem_len = 1, @@ -726,7 +726,7 @@ static struct qmi_elem_info qmi_wlanfw_respond_mem_req_msg_v01_ei[] = { }, }; -static struct qmi_elem_info qmi_wlanfw_respond_mem_resp_msg_v01_ei[] = { +static const struct qmi_elem_info qmi_wlanfw_respond_mem_resp_msg_v01_ei[] = { { .data_type = QMI_STRUCT, .elem_len = 1, @@ -744,7 +744,7 @@ static struct qmi_elem_info qmi_wlanfw_respond_mem_resp_msg_v01_ei[] = { }, }; -static struct qmi_elem_info qmi_wlanfw_cap_req_msg_v01_ei[] = { +static const struct qmi_elem_info qmi_wlanfw_cap_req_msg_v01_ei[] = { { .data_type = QMI_EOTI, .array_type = NO_ARRAY, @@ -752,7 +752,7 @@ static struct qmi_elem_info qmi_wlanfw_cap_req_msg_v01_ei[] = { }, }; -static struct qmi_elem_info qmi_wlanfw_device_info_req_msg_v01_ei[] = { +static const struct qmi_elem_info qmi_wlanfw_device_info_req_msg_v01_ei[] = { { .data_type = QMI_EOTI, .array_type = NO_ARRAY, @@ -760,7 +760,7 @@ static struct qmi_elem_info qmi_wlanfw_device_info_req_msg_v01_ei[] = { }, }; -static struct qmi_elem_info qmi_wlfw_device_info_resp_msg_v01_ei[] = { +static const struct qmi_elem_info qmi_wlfw_device_info_resp_msg_v01_ei[] = { { .data_type = QMI_STRUCT, .elem_len = 1, @@ -814,7 +814,7 @@ static struct qmi_elem_info qmi_wlfw_device_info_resp_msg_v01_ei[] = { }, }; -static struct qmi_elem_info qmi_wlanfw_rf_chip_info_s_v01_ei[] = { +static const struct qmi_elem_info qmi_wlanfw_rf_chip_info_s_v01_ei[] = { { .data_type = QMI_UNSIGNED_4_BYTE, .elem_len = 1, @@ -840,7 +840,7 @@ static struct qmi_elem_info qmi_wlanfw_rf_chip_info_s_v01_ei[] = { }, }; -static struct qmi_elem_info qmi_wlanfw_rf_board_info_s_v01_ei[] = { +static const struct qmi_elem_info qmi_wlanfw_rf_board_info_s_v01_ei[] = { { .data_type = QMI_UNSIGNED_4_BYTE, .elem_len = 1, @@ -857,7 +857,7 @@ static struct qmi_elem_info qmi_wlanfw_rf_board_info_s_v01_ei[] = { }, }; -static struct qmi_elem_info qmi_wlanfw_soc_info_s_v01_ei[] = { +static const struct qmi_elem_info qmi_wlanfw_soc_info_s_v01_ei[] = { { .data_type = QMI_UNSIGNED_4_BYTE, .elem_len = 1, @@ -873,7 +873,7 @@ static struct qmi_elem_info qmi_wlanfw_soc_info_s_v01_ei[] = { }, }; -static struct qmi_elem_info qmi_wlanfw_fw_version_info_s_v01_ei[] = { +static const struct qmi_elem_info qmi_wlanfw_fw_version_info_s_v01_ei[] = { { .data_type = QMI_UNSIGNED_4_BYTE, .elem_len = 1, @@ -899,7 +899,7 @@ static struct qmi_elem_info qmi_wlanfw_fw_version_info_s_v01_ei[] = { }, }; -static struct qmi_elem_info qmi_wlanfw_cap_resp_msg_v01_ei[] = { +static const struct qmi_elem_info qmi_wlanfw_cap_resp_msg_v01_ei[] = { { .data_type = QMI_STRUCT, .elem_len = 1, @@ -1100,7 +1100,7 @@ static struct qmi_elem_info qmi_wlanfw_cap_resp_msg_v01_ei[] = { }, }; -static struct qmi_elem_info qmi_wlanfw_bdf_download_req_msg_v01_ei[] = { +static const struct qmi_elem_info qmi_wlanfw_bdf_download_req_msg_v01_ei[] = { { .data_type = QMI_UNSIGNED_1_BYTE, .elem_len = 1, @@ -1235,7 +1235,7 @@ static struct qmi_elem_info qmi_wlanfw_bdf_download_req_msg_v01_ei[] = { }, }; -static struct qmi_elem_info qmi_wlanfw_bdf_download_resp_msg_v01_ei[] = { +static const struct qmi_elem_info qmi_wlanfw_bdf_download_resp_msg_v01_ei[] = { { .data_type = QMI_STRUCT, .elem_len = 1, @@ -1253,7 +1253,7 @@ static struct qmi_elem_info qmi_wlanfw_bdf_download_resp_msg_v01_ei[] = { }, }; -static struct qmi_elem_info qmi_wlanfw_m3_info_req_msg_v01_ei[] = { +static const struct qmi_elem_info qmi_wlanfw_m3_info_req_msg_v01_ei[] = { { .data_type = QMI_UNSIGNED_8_BYTE, .elem_len = 1, @@ -1277,7 +1277,7 @@ static struct qmi_elem_info qmi_wlanfw_m3_info_req_msg_v01_ei[] = { }, }; -static struct qmi_elem_info qmi_wlanfw_m3_info_resp_msg_v01_ei[] = { +static const struct qmi_elem_info qmi_wlanfw_m3_info_resp_msg_v01_ei[] = { { .data_type = QMI_STRUCT, .elem_len = 1, @@ -1294,7 +1294,7 @@ static struct qmi_elem_info qmi_wlanfw_m3_info_resp_msg_v01_ei[] = { }, }; -static struct qmi_elem_info qmi_wlanfw_ce_tgt_pipe_cfg_s_v01_ei[] = { +static const struct qmi_elem_info qmi_wlanfw_ce_tgt_pipe_cfg_s_v01_ei[] = { { .data_type = QMI_UNSIGNED_4_BYTE, .elem_len = 1, @@ -1347,7 +1347,7 @@ static struct qmi_elem_info qmi_wlanfw_ce_tgt_pipe_cfg_s_v01_ei[] = { }, }; -static struct qmi_elem_info qmi_wlanfw_ce_svc_pipe_cfg_s_v01_ei[] = { +static const struct qmi_elem_info qmi_wlanfw_ce_svc_pipe_cfg_s_v01_ei[] = { { .data_type = QMI_UNSIGNED_4_BYTE, .elem_len = 1, @@ -1382,7 +1382,7 @@ static struct qmi_elem_info qmi_wlanfw_ce_svc_pipe_cfg_s_v01_ei[] = { }, }; -static struct qmi_elem_info qmi_wlanfw_shadow_reg_cfg_s_v01_ei[] = { +static const struct qmi_elem_info qmi_wlanfw_shadow_reg_cfg_s_v01_ei[] = { { .data_type = QMI_UNSIGNED_2_BYTE, .elem_len = 1, @@ -1406,7 +1406,7 @@ static struct qmi_elem_info qmi_wlanfw_shadow_reg_cfg_s_v01_ei[] = { }, }; -static struct qmi_elem_info qmi_wlanfw_shadow_reg_v2_cfg_s_v01_ei[] = { +static const struct qmi_elem_info qmi_wlanfw_shadow_reg_v2_cfg_s_v01_ei[] = { { .data_type = QMI_UNSIGNED_4_BYTE, .elem_len = 1, @@ -1423,7 +1423,7 @@ static struct qmi_elem_info qmi_wlanfw_shadow_reg_v2_cfg_s_v01_ei[] = { }, }; -static struct qmi_elem_info qmi_wlanfw_wlan_mode_req_msg_v01_ei[] = { +static const struct qmi_elem_info qmi_wlanfw_wlan_mode_req_msg_v01_ei[] = { { .data_type = QMI_UNSIGNED_4_BYTE, .elem_len = 1, @@ -1458,7 +1458,7 @@ static struct qmi_elem_info qmi_wlanfw_wlan_mode_req_msg_v01_ei[] = { }, }; -static struct qmi_elem_info qmi_wlanfw_wlan_mode_resp_msg_v01_ei[] = { +static const struct qmi_elem_info qmi_wlanfw_wlan_mode_resp_msg_v01_ei[] = { { .data_type = QMI_STRUCT, .elem_len = 1, @@ -1476,7 +1476,7 @@ static struct qmi_elem_info qmi_wlanfw_wlan_mode_resp_msg_v01_ei[] = { }, }; -static struct qmi_elem_info qmi_wlanfw_wlan_cfg_req_msg_v01_ei[] = { +static const struct qmi_elem_info qmi_wlanfw_wlan_cfg_req_msg_v01_ei[] = { { .data_type = QMI_OPT_FLAG, .elem_len = 1, @@ -1615,7 +1615,7 @@ static struct qmi_elem_info qmi_wlanfw_wlan_cfg_req_msg_v01_ei[] = { }, }; -static struct qmi_elem_info qmi_wlanfw_wlan_cfg_resp_msg_v01_ei[] = { +static const struct qmi_elem_info qmi_wlanfw_wlan_cfg_resp_msg_v01_ei[] = { { .data_type = QMI_STRUCT, .elem_len = 1, @@ -1632,28 +1632,28 @@ static struct qmi_elem_info qmi_wlanfw_wlan_cfg_resp_msg_v01_ei[] = { }, }; -static struct qmi_elem_info qmi_wlanfw_mem_ready_ind_msg_v01_ei[] = { +static const struct qmi_elem_info qmi_wlanfw_mem_ready_ind_msg_v01_ei[] = { { .data_type = QMI_EOTI, .array_type = NO_ARRAY, }, }; -static struct qmi_elem_info qmi_wlanfw_fw_ready_ind_msg_v01_ei[] = { +static const struct qmi_elem_info qmi_wlanfw_fw_ready_ind_msg_v01_ei[] = { { .data_type = QMI_EOTI, .array_type = NO_ARRAY, }, }; -static struct qmi_elem_info qmi_wlanfw_cold_boot_cal_done_ind_msg_v01_ei[] = { +static const struct qmi_elem_info qmi_wlanfw_cold_boot_cal_done_ind_msg_v01_ei[] = { { .data_type = QMI_EOTI, .array_type = NO_ARRAY, }, }; -static struct qmi_elem_info qmi_wlanfw_wlan_ini_req_msg_v01_ei[] = { +static const struct qmi_elem_info qmi_wlanfw_wlan_ini_req_msg_v01_ei[] = { { .data_type = QMI_OPT_FLAG, .elem_len = 1, @@ -1679,7 +1679,7 @@ static struct qmi_elem_info qmi_wlanfw_wlan_ini_req_msg_v01_ei[] = { }, }; -static struct qmi_elem_info qmi_wlanfw_wlan_ini_resp_msg_v01_ei[] = { +static const struct qmi_elem_info qmi_wlanfw_wlan_ini_resp_msg_v01_ei[] = { { .data_type = QMI_STRUCT, .elem_len = 1, @@ -1697,7 +1697,7 @@ static struct qmi_elem_info qmi_wlanfw_wlan_ini_resp_msg_v01_ei[] = { }, }; -static struct qmi_elem_info qmi_wlfw_fw_init_done_ind_msg_v01_ei[] = { +static const struct qmi_elem_info qmi_wlfw_fw_init_done_ind_msg_v01_ei[] = { { .data_type = QMI_EOTI, .array_type = NO_ARRAY, From 598d2982b11144e44a5beb5cb6fd899873b394e3 Mon Sep 17 00:00:00 2001 From: Ilya Maximets Date: Mon, 31 Oct 2022 18:39:53 +0100 Subject: [PATCH 0564/2002] net: tun: bump the link speed from 10Mbps to 10Gbps The 10Mbps link speed was set in 2004 when the ethtool interface was initially added to the tun driver. It might have been a good assumption 18 years ago, but CPUs and network stack came a long way since then. Other virtual ports typically report much higher speeds. For example, veth reports 10Gbps since its introduction in 2007. Some userspace applications rely on the current link speed in certain situations. For example, Open vSwitch is using link speed as an upper bound for QoS configuration if user didn't specify the maximum rate. Advertised 10Mbps doesn't match reality in a modern world, so users have to always manually override the value with something more sensible to avoid configuration issues, e.g. limiting the traffic too much. This also creates additional confusion among users. Bump the advertised speed to at least match the veth. Alternative might be to explicitly report UNKNOWN and let the user decide on a right value for them. And it is indeed "the right way" of fixing the problem. However, that may cause issues with bonding or with some userspace applications that may rely on speed value to be reported (even though they should not). Just changing the speed value should be a safer option. Users can still override the speed with ethtool, if necessary. RFC discussion is linked below. Link: https://lore.kernel.org/lkml/20221021114921.3705550-1-i.maximets@ovn.org/ Link: https://mail.openvswitch.org/pipermail/ovs-discuss/2022-July/051958.html Signed-off-by: Ilya Maximets Reviewed-by: Nicolas Dichtel Link: https://lore.kernel.org/r/20221031173953.614577-1-i.maximets@ovn.org Signed-off-by: Paolo Abeni --- drivers/net/tun.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/tun.c b/drivers/net/tun.c index 27c6d235cbda..48bb4a166ad4 100644 --- a/drivers/net/tun.c +++ b/drivers/net/tun.c @@ -3514,7 +3514,7 @@ static void tun_default_link_ksettings(struct net_device *dev, { ethtool_link_ksettings_zero_link_mode(cmd, supported); ethtool_link_ksettings_zero_link_mode(cmd, advertising); - cmd->base.speed = SPEED_10; + cmd->base.speed = SPEED_10000; cmd->base.duplex = DUPLEX_FULL; cmd->base.port = PORT_TP; cmd->base.phy_address = 0; From 6d6b39f180b83dfe1e938382b68dd1e6cb51363c Mon Sep 17 00:00:00 2001 From: Shenwei Wang Date: Mon, 31 Oct 2022 13:53:50 -0500 Subject: [PATCH 0565/2002] net: fec: add initial XDP support This patch adds the initial XDP support to Freescale driver. It supports XDP_PASS, XDP_DROP and XDP_REDIRECT actions. Upcoming patches will add support for XDP_TX and Zero Copy features. As the patch is rather large, the part of codes to collect the statistics is separated and will prepare a dedicated patch for that part. I just tested with the application of xdpsock. -- Native here means running command of "xdpsock -i eth0" -- SKB-Mode means running command of "xdpsock -S -i eth0" The following are the testing result relating to XDP mode: root@imx8qxpc0mek:~/bpf# ./xdpsock -i eth0 sock0@eth0:0 rxdrop xdp-drv pps pkts 1.00 rx 371347 2717794 tx 0 0 root@imx8qxpc0mek:~/bpf# ./xdpsock -S -i eth0 sock0@eth0:0 rxdrop xdp-skb pps pkts 1.00 rx 202229 404528 tx 0 0 root@imx8qxpc0mek:~/bpf# ./xdp2 eth0 proto 0: 496708 pkt/s proto 0: 505469 pkt/s proto 0: 505283 pkt/s proto 0: 505443 pkt/s proto 0: 505465 pkt/s root@imx8qxpc0mek:~/bpf# ./xdp2 -S eth0 proto 0: 0 pkt/s proto 17: 118778 pkt/s proto 17: 118989 pkt/s proto 0: 1 pkt/s proto 17: 118987 pkt/s proto 0: 0 pkt/s proto 17: 118943 pkt/s proto 17: 118976 pkt/s proto 0: 1 pkt/s proto 17: 119006 pkt/s proto 0: 0 pkt/s proto 17: 119071 pkt/s proto 17: 119092 pkt/s Signed-off-by: Shenwei Wang Reported-by: kernel test robot Link: https://lore.kernel.org/r/20221031185350.2045675-1-shenwei.wang@nxp.com Signed-off-by: Paolo Abeni --- drivers/net/ethernet/freescale/fec.h | 4 +- drivers/net/ethernet/freescale/fec_main.c | 224 +++++++++++++++++++++- 2 files changed, 226 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/freescale/fec.h b/drivers/net/ethernet/freescale/fec.h index 476e3863a310..61e847b18343 100644 --- a/drivers/net/ethernet/freescale/fec.h +++ b/drivers/net/ethernet/freescale/fec.h @@ -348,7 +348,6 @@ struct bufdesc_ex { */ #define FEC_ENET_XDP_HEADROOM (XDP_PACKET_HEADROOM) - #define FEC_ENET_RX_PAGES 256 #define FEC_ENET_RX_FRSIZE (PAGE_SIZE - FEC_ENET_XDP_HEADROOM \ - SKB_DATA_ALIGN(sizeof(struct skb_shared_info))) @@ -663,6 +662,9 @@ struct fec_enet_private { struct imx_sc_ipc *ipc_handle; + /* XDP BPF Program */ + struct bpf_prog *xdp_prog; + u64 ethtool_stats[]; }; diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c index bfbb5602e38a..4fbdefb4f039 100644 --- a/drivers/net/ethernet/freescale/fec_main.c +++ b/drivers/net/ethernet/freescale/fec_main.c @@ -89,6 +89,11 @@ static const u16 fec_enet_vlan_pri_to_queue[8] = {0, 0, 1, 1, 1, 2, 2, 2}; #define FEC_ENET_OPD_V 0xFFF0 #define FEC_MDIO_PM_TIMEOUT 100 /* ms */ +#define FEC_ENET_XDP_PASS 0 +#define FEC_ENET_XDP_CONSUMED BIT(0) +#define FEC_ENET_XDP_TX BIT(1) +#define FEC_ENET_XDP_REDIR BIT(2) + struct fec_devinfo { u32 quirks; }; @@ -418,13 +423,14 @@ static int fec_enet_create_page_pool(struct fec_enet_private *fep, struct fec_enet_priv_rx_q *rxq, int size) { + struct bpf_prog *xdp_prog = READ_ONCE(fep->xdp_prog); struct page_pool_params pp_params = { .order = 0, .flags = PP_FLAG_DMA_MAP | PP_FLAG_DMA_SYNC_DEV, .pool_size = size, .nid = dev_to_node(&fep->pdev->dev), .dev = &fep->pdev->dev, - .dma_dir = DMA_FROM_DEVICE, + .dma_dir = xdp_prog ? DMA_BIDIRECTIONAL : DMA_FROM_DEVICE, .offset = FEC_ENET_XDP_HEADROOM, .max_len = FEC_ENET_RX_FRSIZE, }; @@ -1499,6 +1505,59 @@ static void fec_enet_update_cbd(struct fec_enet_priv_rx_q *rxq, bdp->cbd_bufaddr = cpu_to_fec32(phys_addr); } +static u32 +fec_enet_run_xdp(struct fec_enet_private *fep, struct bpf_prog *prog, + struct xdp_buff *xdp, struct fec_enet_priv_rx_q *rxq, int index) +{ + unsigned int sync, len = xdp->data_end - xdp->data; + u32 ret = FEC_ENET_XDP_PASS; + struct page *page; + int err; + u32 act; + + act = bpf_prog_run_xdp(prog, xdp); + + /* Due xdp_adjust_tail: DMA sync for_device cover max len CPU touch */ + sync = xdp->data_end - xdp->data_hard_start - FEC_ENET_XDP_HEADROOM; + sync = max(sync, len); + + switch (act) { + case XDP_PASS: + ret = FEC_ENET_XDP_PASS; + break; + + case XDP_REDIRECT: + err = xdp_do_redirect(fep->netdev, xdp, prog); + if (!err) { + ret = FEC_ENET_XDP_REDIR; + } else { + ret = FEC_ENET_XDP_CONSUMED; + page = virt_to_head_page(xdp->data); + page_pool_put_page(rxq->page_pool, page, sync, true); + } + break; + + default: + bpf_warn_invalid_xdp_action(fep->netdev, prog, act); + fallthrough; + + case XDP_TX: + bpf_warn_invalid_xdp_action(fep->netdev, prog, act); + fallthrough; + + case XDP_ABORTED: + fallthrough; /* handle aborts by dropping packet */ + + case XDP_DROP: + ret = FEC_ENET_XDP_CONSUMED; + page = virt_to_head_page(xdp->data); + page_pool_put_page(rxq->page_pool, page, sync, true); + break; + } + + return ret; +} + /* During a receive, the bd_rx.cur points to the current incoming buffer. * When we update through the ring, if the next incoming buffer has * not been given to the system, we just set the empty indicator, @@ -1520,6 +1579,9 @@ fec_enet_rx_queue(struct net_device *ndev, int budget, u16 queue_id) u16 vlan_tag; int index = 0; bool need_swap = fep->quirks & FEC_QUIRK_SWAP_FRAME; + struct bpf_prog *xdp_prog = READ_ONCE(fep->xdp_prog); + u32 ret, xdp_result = FEC_ENET_XDP_PASS; + struct xdp_buff xdp; struct page *page; #ifdef CONFIG_M532x @@ -1531,6 +1593,7 @@ fec_enet_rx_queue(struct net_device *ndev, int budget, u16 queue_id) * These get messed up if we get called due to a busy condition. */ bdp = rxq->bd.cur; + xdp_init_buff(&xdp, PAGE_SIZE, &rxq->xdp_rxq); while (!((status = fec16_to_cpu(bdp->cbd_sc)) & BD_ENET_RX_EMPTY)) { @@ -1580,6 +1643,17 @@ fec_enet_rx_queue(struct net_device *ndev, int budget, u16 queue_id) prefetch(page_address(page)); fec_enet_update_cbd(rxq, bdp, index); + if (xdp_prog) { + xdp_buff_clear_frags_flag(&xdp); + xdp_prepare_buff(&xdp, page_address(page), + FEC_ENET_XDP_HEADROOM, pkt_len, false); + + ret = fec_enet_run_xdp(fep, xdp_prog, &xdp, rxq, index); + xdp_result |= ret; + if (ret != FEC_ENET_XDP_PASS) + goto rx_processing_done; + } + /* The packet length includes FCS, but we don't want to * include that when passing upstream as it messes up * bridging applications. @@ -1675,6 +1749,10 @@ rx_processing_done: writel(0, rxq->bd.reg_desc_active); } rxq->bd.cur = bdp; + + if (xdp_result & FEC_ENET_XDP_REDIR) + xdp_do_flush_map(); + return pkt_received; } @@ -3518,6 +3596,148 @@ static u16 fec_enet_select_queue(struct net_device *ndev, struct sk_buff *skb, return fec_enet_vlan_pri_to_queue[vlan_tag >> 13]; } +static int fec_enet_bpf(struct net_device *dev, struct netdev_bpf *bpf) +{ + struct fec_enet_private *fep = netdev_priv(dev); + bool is_run = netif_running(dev); + struct bpf_prog *old_prog; + + switch (bpf->command) { + case XDP_SETUP_PROG: + if (is_run) { + napi_disable(&fep->napi); + netif_tx_disable(dev); + } + + old_prog = xchg(&fep->xdp_prog, bpf->prog); + fec_restart(dev); + + if (is_run) { + napi_enable(&fep->napi); + netif_tx_start_all_queues(dev); + } + + if (old_prog) + bpf_prog_put(old_prog); + + return 0; + + case XDP_SETUP_XSK_POOL: + return -EOPNOTSUPP; + + default: + return -EOPNOTSUPP; + } +} + +static int +fec_enet_xdp_get_tx_queue(struct fec_enet_private *fep, int cpu) +{ + int index = cpu; + + if (unlikely(index < 0)) + index = 0; + + while (index >= fep->num_tx_queues) + index -= fep->num_tx_queues; + + return index; +} + +static int fec_enet_txq_xmit_frame(struct fec_enet_private *fep, + struct fec_enet_priv_tx_q *txq, + struct xdp_frame *frame) +{ + unsigned int index, status, estatus; + struct bufdesc *bdp, *last_bdp; + dma_addr_t dma_addr; + int entries_free; + + entries_free = fec_enet_get_free_txdesc_num(txq); + if (entries_free < MAX_SKB_FRAGS + 1) { + netdev_err(fep->netdev, "NOT enough BD for SG!\n"); + return NETDEV_TX_OK; + } + + /* Fill in a Tx ring entry */ + bdp = txq->bd.cur; + last_bdp = bdp; + status = fec16_to_cpu(bdp->cbd_sc); + status &= ~BD_ENET_TX_STATS; + + index = fec_enet_get_bd_index(bdp, &txq->bd); + + dma_addr = dma_map_single(&fep->pdev->dev, frame->data, + frame->len, DMA_TO_DEVICE); + if (dma_mapping_error(&fep->pdev->dev, dma_addr)) + return FEC_ENET_XDP_CONSUMED; + + status |= (BD_ENET_TX_INTR | BD_ENET_TX_LAST); + if (fep->bufdesc_ex) + estatus = BD_ENET_TX_INT; + + bdp->cbd_bufaddr = cpu_to_fec32(dma_addr); + bdp->cbd_datlen = cpu_to_fec16(frame->len); + + if (fep->bufdesc_ex) { + struct bufdesc_ex *ebdp = (struct bufdesc_ex *)bdp; + + if (fep->quirks & FEC_QUIRK_HAS_AVB) + estatus |= FEC_TX_BD_FTYPE(txq->bd.qid); + + ebdp->cbd_bdu = 0; + ebdp->cbd_esc = cpu_to_fec32(estatus); + } + + index = fec_enet_get_bd_index(last_bdp, &txq->bd); + txq->tx_skbuff[index] = NULL; + + /* Send it on its way. Tell FEC it's ready, interrupt when done, + * it's the last BD of the frame, and to put the CRC on the end. + */ + status |= (BD_ENET_TX_READY | BD_ENET_TX_TC); + bdp->cbd_sc = cpu_to_fec16(status); + + /* If this was the last BD in the ring, start at the beginning again. */ + bdp = fec_enet_get_nextdesc(last_bdp, &txq->bd); + + txq->bd.cur = bdp; + + return 0; +} + +static int fec_enet_xdp_xmit(struct net_device *dev, + int num_frames, + struct xdp_frame **frames, + u32 flags) +{ + struct fec_enet_private *fep = netdev_priv(dev); + struct fec_enet_priv_tx_q *txq; + int cpu = smp_processor_id(); + struct netdev_queue *nq; + unsigned int queue; + int i; + + queue = fec_enet_xdp_get_tx_queue(fep, cpu); + txq = fep->tx_queue[queue]; + nq = netdev_get_tx_queue(fep->netdev, queue); + + __netif_tx_lock(nq, cpu); + + for (i = 0; i < num_frames; i++) + fec_enet_txq_xmit_frame(fep, txq, frames[i]); + + /* Make sure the update to bdp and tx_skbuff are performed. */ + wmb(); + + /* Trigger transmission start */ + writel(0, txq->bd.reg_desc_active); + + __netif_tx_unlock(nq); + + return num_frames; +} + static const struct net_device_ops fec_netdev_ops = { .ndo_open = fec_enet_open, .ndo_stop = fec_enet_close, @@ -3532,6 +3752,8 @@ static const struct net_device_ops fec_netdev_ops = { .ndo_poll_controller = fec_poll_controller, #endif .ndo_set_features = fec_set_features, + .ndo_bpf = fec_enet_bpf, + .ndo_xdp_xmit = fec_enet_xdp_xmit, }; static const unsigned short offset_des_active_rxq[] = { From 71fa6887eeca7b631528f9c7a39815498de8028c Mon Sep 17 00:00:00 2001 From: Saurabh Sengar Date: Mon, 31 Oct 2022 23:06:01 -0700 Subject: [PATCH 0566/2002] net: mana: Assign interrupts to CPUs based on NUMA nodes In large VMs with multiple NUMA nodes, network performance is usually best if network interrupts are all assigned to the same virtual NUMA node. This patch assigns online CPU according to a numa aware policy, local cpus are returned first, followed by non-local ones, then it wraps around. Signed-off-by: Saurabh Sengar Reviewed-by: Haiyang Zhang Link: https://lore.kernel.org/r/1667282761-11547-1-git-send-email-ssengar@linux.microsoft.com Signed-off-by: Paolo Abeni --- drivers/net/ethernet/microsoft/mana/gdma.h | 1 + .../net/ethernet/microsoft/mana/gdma_main.c | 30 +++++++++++++++++-- 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/microsoft/mana/gdma.h b/drivers/net/ethernet/microsoft/mana/gdma.h index 4a6efe6ada08..db340f36ef29 100644 --- a/drivers/net/ethernet/microsoft/mana/gdma.h +++ b/drivers/net/ethernet/microsoft/mana/gdma.h @@ -353,6 +353,7 @@ struct gdma_context { void __iomem *shm_base; void __iomem *db_page_base; u32 db_page_size; + int numa_node; /* Shared memory chanenl (used to bootstrap HWC) */ struct shm_channel shm_channel; diff --git a/drivers/net/ethernet/microsoft/mana/gdma_main.c b/drivers/net/ethernet/microsoft/mana/gdma_main.c index a6f99b4344d9..726ac94d96ae 100644 --- a/drivers/net/ethernet/microsoft/mana/gdma_main.c +++ b/drivers/net/ethernet/microsoft/mana/gdma_main.c @@ -1208,8 +1208,10 @@ static int mana_gd_setup_irqs(struct pci_dev *pdev) struct gdma_context *gc = pci_get_drvdata(pdev); struct gdma_irq_context *gic; unsigned int max_irqs; + u16 *cpus; + cpumask_var_t req_mask; int nvec, irq; - int err, i, j; + int err, i = 0, j; if (max_queues_per_port > MANA_MAX_NUM_QUEUES) max_queues_per_port = MANA_MAX_NUM_QUEUES; @@ -1228,7 +1230,21 @@ static int mana_gd_setup_irqs(struct pci_dev *pdev) goto free_irq_vector; } + if (!zalloc_cpumask_var(&req_mask, GFP_KERNEL)) { + err = -ENOMEM; + goto free_irq; + } + + cpus = kcalloc(nvec, sizeof(*cpus), GFP_KERNEL); + if (!cpus) { + err = -ENOMEM; + goto free_mask; + } + for (i = 0; i < nvec; i++) + cpus[i] = cpumask_local_spread(i, gc->numa_node); + for (i = 0; i < nvec; i++) { + cpumask_set_cpu(cpus[i], req_mask); gic = &gc->irq_contexts[i]; gic->handler = NULL; gic->arg = NULL; @@ -1236,13 +1252,17 @@ static int mana_gd_setup_irqs(struct pci_dev *pdev) irq = pci_irq_vector(pdev, i); if (irq < 0) { err = irq; - goto free_irq; + goto free_mask; } err = request_irq(irq, mana_gd_intr, 0, "mana_intr", gic); if (err) - goto free_irq; + goto free_mask; + irq_set_affinity_and_hint(irq, req_mask); + cpumask_clear(req_mask); } + free_cpumask_var(req_mask); + kfree(cpus); err = mana_gd_alloc_res_map(nvec, &gc->msix_resource); if (err) @@ -1253,6 +1273,9 @@ static int mana_gd_setup_irqs(struct pci_dev *pdev) return 0; +free_mask: + free_cpumask_var(req_mask); + kfree(cpus); free_irq: for (j = i - 1; j >= 0; j--) { irq = pci_irq_vector(pdev, j); @@ -1382,6 +1405,7 @@ static int mana_gd_probe(struct pci_dev *pdev, const struct pci_device_id *ent) if (!bar0_va) goto free_gc; + gc->numa_node = dev_to_node(&pdev->dev); gc->is_pf = mana_is_pf(pdev->device); gc->bar0_va = bar0_va; gc->dev = &pdev->dev; From ec32c0c42d0a7208571c4c77f140bffaa4e5e304 Mon Sep 17 00:00:00 2001 From: Daniel Machon Date: Tue, 1 Nov 2022 10:48:29 +0100 Subject: [PATCH 0567/2002] net: dcb: add new pcp selector to app object Add new PCP selector for the 8021Qaz APP managed object. As the PCP selector is not part of the 8021Qaz standard, a new non-std extension attribute DCB_ATTR_DCB_APP has been introduced. Also two helper functions to translate between selector and app attribute type has been added. The new selector has been given a value of 255, to minimize the risk of future overlap of std- and non-std attributes. The new DCB_ATTR_DCB_APP is sent alongside the ieee std attribute in the app table. This means that the dcb_app struct can now both contain std- and non-std app attributes. Currently there is no overlap between the selector values of the two attributes. The purpose of adding the PCP selector, is to be able to offload PCP-based queue classification to the 8021Q Priority Code Point table, see 6.9.3 of IEEE Std 802.1Q-2018. PCP and DEI is encoded in the protocol field as 8*dei+pcp, so that a mapping of PCP 2 and DEI 1 to priority 3 is encoded as {255, 10, 3}. Signed-off-by: Daniel Machon Reviewed-by: Petr Machata Signed-off-by: Paolo Abeni --- include/uapi/linux/dcbnl.h | 6 ++++ net/dcb/dcbnl.c | 58 +++++++++++++++++++++++++++++++++++--- 2 files changed, 60 insertions(+), 4 deletions(-) diff --git a/include/uapi/linux/dcbnl.h b/include/uapi/linux/dcbnl.h index a791a94013a6..dc7ef96207ca 100644 --- a/include/uapi/linux/dcbnl.h +++ b/include/uapi/linux/dcbnl.h @@ -218,6 +218,9 @@ struct cee_pfc { #define IEEE_8021QAZ_APP_SEL_ANY 4 #define IEEE_8021QAZ_APP_SEL_DSCP 5 +/* Non-std selector values */ +#define DCB_APP_SEL_PCP 255 + /* This structure contains the IEEE 802.1Qaz APP managed object. This * object is also used for the CEE std as well. * @@ -247,6 +250,8 @@ struct dcb_app { __u16 protocol; }; +#define IEEE_8021QAZ_APP_SEL_MAX 255 + /** * struct dcb_peer_app_info - APP feature information sent by the peer * @@ -425,6 +430,7 @@ enum ieee_attrs { enum ieee_attrs_app { DCB_ATTR_IEEE_APP_UNSPEC, DCB_ATTR_IEEE_APP, + DCB_ATTR_DCB_APP, __DCB_ATTR_IEEE_APP_MAX }; #define DCB_ATTR_IEEE_APP_MAX (__DCB_ATTR_IEEE_APP_MAX - 1) diff --git a/net/dcb/dcbnl.c b/net/dcb/dcbnl.c index dc4fb699b56c..0c17bb28ae60 100644 --- a/net/dcb/dcbnl.c +++ b/net/dcb/dcbnl.c @@ -179,6 +179,38 @@ static const struct nla_policy dcbnl_featcfg_nest[DCB_FEATCFG_ATTR_MAX + 1] = { static LIST_HEAD(dcb_app_list); static DEFINE_SPINLOCK(dcb_lock); +static enum ieee_attrs_app dcbnl_app_attr_type_get(u8 selector) +{ + switch (selector) { + case IEEE_8021QAZ_APP_SEL_ETHERTYPE: + case IEEE_8021QAZ_APP_SEL_STREAM: + case IEEE_8021QAZ_APP_SEL_DGRAM: + case IEEE_8021QAZ_APP_SEL_ANY: + case IEEE_8021QAZ_APP_SEL_DSCP: + return DCB_ATTR_IEEE_APP; + case DCB_APP_SEL_PCP: + return DCB_ATTR_DCB_APP; + default: + return DCB_ATTR_IEEE_APP_UNSPEC; + } +} + +static bool dcbnl_app_attr_type_validate(enum ieee_attrs_app type) +{ + switch (type) { + case DCB_ATTR_IEEE_APP: + case DCB_ATTR_DCB_APP: + return true; + default: + return false; + } +} + +static bool dcbnl_app_selector_validate(enum ieee_attrs_app type, u8 selector) +{ + return dcbnl_app_attr_type_get(selector) == type; +} + static struct sk_buff *dcbnl_newmsg(int type, u8 cmd, u32 port, u32 seq, u32 flags, struct nlmsghdr **nlhp) { @@ -1116,8 +1148,9 @@ static int dcbnl_ieee_fill(struct sk_buff *skb, struct net_device *netdev) spin_lock_bh(&dcb_lock); list_for_each_entry(itr, &dcb_app_list, list) { if (itr->ifindex == netdev->ifindex) { - err = nla_put(skb, DCB_ATTR_IEEE_APP, sizeof(itr->app), - &itr->app); + enum ieee_attrs_app type = + dcbnl_app_attr_type_get(itr->app.selector); + err = nla_put(skb, type, sizeof(itr->app), &itr->app); if (err) { spin_unlock_bh(&dcb_lock); return -EMSGSIZE; @@ -1493,9 +1526,10 @@ static int dcbnl_ieee_set(struct net_device *netdev, struct nlmsghdr *nlh, int rem; nla_for_each_nested(attr, ieee[DCB_ATTR_IEEE_APP_TABLE], rem) { + enum ieee_attrs_app type = nla_type(attr); struct dcb_app *app_data; - if (nla_type(attr) != DCB_ATTR_IEEE_APP) + if (!dcbnl_app_attr_type_validate(type)) continue; if (nla_len(attr) < sizeof(struct dcb_app)) { @@ -1504,6 +1538,13 @@ static int dcbnl_ieee_set(struct net_device *netdev, struct nlmsghdr *nlh, } app_data = nla_data(attr); + + if (!dcbnl_app_selector_validate(type, + app_data->selector)) { + err = -EINVAL; + goto err; + } + if (ops->ieee_setapp) err = ops->ieee_setapp(netdev, app_data); else @@ -1554,11 +1595,20 @@ static int dcbnl_ieee_del(struct net_device *netdev, struct nlmsghdr *nlh, int rem; nla_for_each_nested(attr, ieee[DCB_ATTR_IEEE_APP_TABLE], rem) { + enum ieee_attrs_app type = nla_type(attr); struct dcb_app *app_data; - if (nla_type(attr) != DCB_ATTR_IEEE_APP) + if (!dcbnl_app_attr_type_validate(type)) continue; + app_data = nla_data(attr); + + if (!dcbnl_app_selector_validate(type, + app_data->selector)) { + err = -EINVAL; + goto err; + } + if (ops->ieee_delapp) err = ops->ieee_delapp(netdev, app_data); else From 6182d5875c330a5a611687caa05f47752455720c Mon Sep 17 00:00:00 2001 From: Daniel Machon Date: Tue, 1 Nov 2022 10:48:30 +0100 Subject: [PATCH 0568/2002] net: dcb: add new apptrust attribute Add new apptrust extension attributes to the 8021Qaz APP managed object. Two new attributes, DCB_ATTR_DCB_APP_TRUST_TABLE and DCB_ATTR_DCB_APP_TRUST, has been added. Trusted selectors are passed in the nested attribute DCB_ATTR_DCB_APP_TRUST, in order of precedence. The new attributes are meant to allow drivers, whose hw supports the notion of trust, to be able to set whether a particular app selector is trusted - and in which order. Signed-off-by: Daniel Machon Reviewed-by: Petr Machata Signed-off-by: Paolo Abeni --- include/net/dcbnl.h | 4 ++ include/uapi/linux/dcbnl.h | 2 + net/dcb/dcbnl.c | 76 +++++++++++++++++++++++++++++++++++++- 3 files changed, 80 insertions(+), 2 deletions(-) diff --git a/include/net/dcbnl.h b/include/net/dcbnl.h index 2b2d86fb3131..8841ab6c2de7 100644 --- a/include/net/dcbnl.h +++ b/include/net/dcbnl.h @@ -109,6 +109,10 @@ struct dcbnl_rtnl_ops { /* buffer settings */ int (*dcbnl_getbuffer)(struct net_device *, struct dcbnl_buffer *); int (*dcbnl_setbuffer)(struct net_device *, struct dcbnl_buffer *); + + /* apptrust */ + int (*dcbnl_setapptrust)(struct net_device *, u8 *, int); + int (*dcbnl_getapptrust)(struct net_device *, u8 *, int *); }; #endif /* __NET_DCBNL_H__ */ diff --git a/include/uapi/linux/dcbnl.h b/include/uapi/linux/dcbnl.h index dc7ef96207ca..99047223ab26 100644 --- a/include/uapi/linux/dcbnl.h +++ b/include/uapi/linux/dcbnl.h @@ -410,6 +410,7 @@ enum dcbnl_attrs { * @DCB_ATTR_IEEE_PEER_ETS: peer ETS configuration - get only * @DCB_ATTR_IEEE_PEER_PFC: peer PFC configuration - get only * @DCB_ATTR_IEEE_PEER_APP: peer APP tlv - get only + * @DCB_ATTR_DCB_APP_TRUST_TABLE: selector trust table */ enum ieee_attrs { DCB_ATTR_IEEE_UNSPEC, @@ -423,6 +424,7 @@ enum ieee_attrs { DCB_ATTR_IEEE_QCN, DCB_ATTR_IEEE_QCN_STATS, DCB_ATTR_DCB_BUFFER, + DCB_ATTR_DCB_APP_TRUST_TABLE, __DCB_ATTR_IEEE_MAX }; #define DCB_ATTR_IEEE_MAX (__DCB_ATTR_IEEE_MAX - 1) diff --git a/net/dcb/dcbnl.c b/net/dcb/dcbnl.c index 0c17bb28ae60..cec0632f96db 100644 --- a/net/dcb/dcbnl.c +++ b/net/dcb/dcbnl.c @@ -166,6 +166,7 @@ static const struct nla_policy dcbnl_ieee_policy[DCB_ATTR_IEEE_MAX + 1] = { [DCB_ATTR_IEEE_QCN] = {.len = sizeof(struct ieee_qcn)}, [DCB_ATTR_IEEE_QCN_STATS] = {.len = sizeof(struct ieee_qcn_stats)}, [DCB_ATTR_DCB_BUFFER] = {.len = sizeof(struct dcbnl_buffer)}, + [DCB_ATTR_DCB_APP_TRUST_TABLE] = {.type = NLA_NESTED}, }; /* DCB number of traffic classes nested attributes. */ @@ -1062,9 +1063,9 @@ nla_put_failure: /* Handle IEEE 802.1Qaz/802.1Qau/802.1Qbb GET commands. */ static int dcbnl_ieee_fill(struct sk_buff *skb, struct net_device *netdev) { - struct nlattr *ieee, *app; - struct dcb_app_type *itr; const struct dcbnl_rtnl_ops *ops = netdev->dcbnl_ops; + struct nlattr *ieee, *app, *apptrust; + struct dcb_app_type *itr; int dcbx; int err; @@ -1166,6 +1167,30 @@ static int dcbnl_ieee_fill(struct sk_buff *skb, struct net_device *netdev) spin_unlock_bh(&dcb_lock); nla_nest_end(skb, app); + if (ops->dcbnl_getapptrust) { + u8 selectors[IEEE_8021QAZ_APP_SEL_MAX + 1] = {0}; + int nselectors, i; + + apptrust = nla_nest_start(skb, DCB_ATTR_DCB_APP_TRUST_TABLE); + if (!apptrust) + return -EMSGSIZE; + + err = ops->dcbnl_getapptrust(netdev, selectors, &nselectors); + if (!err) { + for (i = 0; i < nselectors; i++) { + enum ieee_attrs_app type = + dcbnl_app_attr_type_get(selectors[i]); + err = nla_put_u8(skb, type, selectors[i]); + if (err) { + nla_nest_cancel(skb, apptrust); + return err; + } + } + } + + nla_nest_end(skb, apptrust); + } + /* get peer info if available */ if (ops->ieee_peer_getets) { struct ieee_ets ets; @@ -1554,6 +1579,53 @@ static int dcbnl_ieee_set(struct net_device *netdev, struct nlmsghdr *nlh, } } + if (ieee[DCB_ATTR_DCB_APP_TRUST_TABLE]) { + u8 selectors[IEEE_8021QAZ_APP_SEL_MAX + 1] = {0}; + struct nlattr *attr; + int nselectors = 0; + int rem; + + if (!ops->dcbnl_setapptrust) { + err = -EOPNOTSUPP; + goto err; + } + + nla_for_each_nested(attr, ieee[DCB_ATTR_DCB_APP_TRUST_TABLE], + rem) { + enum ieee_attrs_app type = nla_type(attr); + u8 selector; + int i; + + if (!dcbnl_app_attr_type_validate(type) || + nla_len(attr) != 1 || + nselectors >= sizeof(selectors)) { + err = -EINVAL; + goto err; + } + + selector = nla_get_u8(attr); + + if (!dcbnl_app_selector_validate(type, selector)) { + err = -EINVAL; + goto err; + } + + /* Duplicate selector ? */ + for (i = 0; i < nselectors; i++) { + if (selectors[i] == selector) { + err = -EINVAL; + goto err; + } + } + + selectors[nselectors++] = selector; + } + + err = ops->dcbnl_setapptrust(netdev, selectors, nselectors); + if (err) + goto err; + } + err: err = nla_put_u8(skb, DCB_ATTR_IEEE, err); dcbnl_ieee_notify(netdev, RTM_SETDCB, DCB_CMD_IEEE_SET, seq, 0); From 92ef3d011e17238483a336bdb714309f597e05d2 Mon Sep 17 00:00:00 2001 From: Daniel Machon Date: Tue, 1 Nov 2022 10:48:31 +0100 Subject: [PATCH 0569/2002] net: microchip: sparx5: add support for offloading pcp table Add new registers and functions to support offload of pcp app entries. Signed-off-by: Daniel Machon Signed-off-by: Paolo Abeni --- drivers/net/ethernet/microchip/sparx5/Kconfig | 11 ++ .../net/ethernet/microchip/sparx5/Makefile | 2 + .../ethernet/microchip/sparx5/sparx5_dcb.c | 116 ++++++++++++++++ .../ethernet/microchip/sparx5/sparx5_main.h | 11 ++ .../microchip/sparx5/sparx5_main_regs.h | 127 +++++++++++++++++- .../ethernet/microchip/sparx5/sparx5_port.c | 37 +++++ .../ethernet/microchip/sparx5/sparx5_port.h | 21 +++ .../ethernet/microchip/sparx5/sparx5_qos.c | 4 + 8 files changed, 327 insertions(+), 2 deletions(-) create mode 100644 drivers/net/ethernet/microchip/sparx5/sparx5_dcb.c diff --git a/drivers/net/ethernet/microchip/sparx5/Kconfig b/drivers/net/ethernet/microchip/sparx5/Kconfig index 98e27530a91f..f58c506bda22 100644 --- a/drivers/net/ethernet/microchip/sparx5/Kconfig +++ b/drivers/net/ethernet/microchip/sparx5/Kconfig @@ -12,3 +12,14 @@ config SPARX5_SWITCH select VCAP help This driver supports the Sparx5 network switch device. + +config SPARX5_DCB + bool "Data Center Bridging (DCB) support" + depends on SPARX5_SWITCH && DCB + default y + help + Say Y here if you want to use Data Center Bridging (DCB) in the + driver. This can be used to assign priority to traffic, based on + DSCP and PCP. + + If unsure, set to Y. diff --git a/drivers/net/ethernet/microchip/sparx5/Makefile b/drivers/net/ethernet/microchip/sparx5/Makefile index ee2c42f66742..38adf917bc09 100644 --- a/drivers/net/ethernet/microchip/sparx5/Makefile +++ b/drivers/net/ethernet/microchip/sparx5/Makefile @@ -11,5 +11,7 @@ sparx5-switch-y := sparx5_main.o sparx5_packet.o \ sparx5_ptp.o sparx5_pgid.o sparx5_tc.o sparx5_qos.o \ sparx5_vcap_impl.o sparx5_vcap_ag_api.o sparx5_tc_flower.o +sparx5-switch-$(CONFIG_SPARX5_DCB) += sparx5_dcb.o + # Provide include files ccflags-y += -I$(srctree)/drivers/net/ethernet/microchip/vcap diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_dcb.c b/drivers/net/ethernet/microchip/sparx5/sparx5_dcb.c new file mode 100644 index 000000000000..df96c17582ef --- /dev/null +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_dcb.c @@ -0,0 +1,116 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* Microchip Sparx5 Switch driver + * + * Copyright (c) 2022 Microchip Technology Inc. and its subsidiaries. + */ + +#include + +#include "sparx5_port.h" + +/* Validate app entry. + * + * Check for valid selectors and valid protocol and priority ranges. + */ +static int sparx5_dcb_app_validate(struct net_device *dev, + const struct dcb_app *app) +{ + int err = 0; + + switch (app->selector) { + /* Pcp checks */ + case DCB_APP_SEL_PCP: + if (app->protocol >= SPARX5_PORT_QOS_PCP_DEI_COUNT) + err = -EINVAL; + else if (app->priority >= SPX5_PRIOS) + err = -ERANGE; + break; + default: + err = -EINVAL; + break; + } + + if (err) + netdev_err(dev, "Invalid entry: %d:%d\n", app->protocol, + app->priority); + + return err; +} + +static int sparx5_dcb_app_update(struct net_device *dev) +{ + struct dcb_app app_itr = { .selector = DCB_APP_SEL_PCP }; + struct sparx5_port *port = netdev_priv(dev); + struct sparx5_port_qos_pcp_map *pcp_map; + struct sparx5_port_qos qos = {0}; + int i; + + pcp_map = &qos.pcp.map; + + /* Get pcp ingress mapping */ + for (i = 0; i < ARRAY_SIZE(pcp_map->map); i++) { + app_itr.protocol = i; + pcp_map->map[i] = dcb_getapp(dev, &app_itr); + } + + return sparx5_port_qos_set(port, &qos); +} + +static int sparx5_dcb_ieee_setapp(struct net_device *dev, struct dcb_app *app) +{ + struct dcb_app app_itr; + int err = 0; + u8 prio; + + err = sparx5_dcb_app_validate(dev, app); + if (err) + goto out; + + /* Delete current mapping, if it exists */ + prio = dcb_getapp(dev, app); + if (prio) { + app_itr = *app; + app_itr.priority = prio; + dcb_ieee_delapp(dev, &app_itr); + } + + err = dcb_ieee_setapp(dev, app); + if (err) + goto out; + + sparx5_dcb_app_update(dev); + +out: + return err; +} + +static int sparx5_dcb_ieee_delapp(struct net_device *dev, struct dcb_app *app) +{ + int err; + + err = dcb_ieee_delapp(dev, app); + if (err < 0) + return err; + + return sparx5_dcb_app_update(dev); +} + +const struct dcbnl_rtnl_ops sparx5_dcbnl_ops = { + .ieee_setapp = sparx5_dcb_ieee_setapp, + .ieee_delapp = sparx5_dcb_ieee_delapp, +}; + +int sparx5_dcb_init(struct sparx5 *sparx5) +{ + struct sparx5_port *port; + int i; + + for (i = 0; i < SPX5_PORTS; i++) { + port = sparx5->ports[i]; + if (!port) + continue; + port->ndev->dcbnl_ops = &sparx5_dcbnl_ops; + } + + return 0; +} diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_main.h b/drivers/net/ethernet/microchip/sparx5/sparx5_main.h index 2ab22a7b799e..5985f2087d7f 100644 --- a/drivers/net/ethernet/microchip/sparx5/sparx5_main.h +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_main.h @@ -359,6 +359,16 @@ int sparx5_config_dsm_calendar(struct sparx5 *sparx5); void sparx5_get_stats64(struct net_device *ndev, struct rtnl_link_stats64 *stats); int sparx_stats_init(struct sparx5 *sparx5); +/* sparx5_dcb.c */ +#ifdef CONFIG_SPARX5_DCB +int sparx5_dcb_init(struct sparx5 *sparx5); +#else +static inline int sparx5_dcb_init(struct sparx5 *sparx5) +{ + return 0; +} +#endif + /* sparx5_netdev.c */ void sparx5_set_port_ifh_timestamp(void *ifh_hdr, u64 timestamp); void sparx5_set_port_ifh_rew_op(void *ifh_hdr, u32 rew_op); @@ -424,6 +434,7 @@ static inline bool sparx5_is_baser(phy_interface_t interface) extern const struct phylink_mac_ops sparx5_phylink_mac_ops; extern const struct phylink_pcs_ops sparx5_phylink_pcs_ops; extern const struct ethtool_ops sparx5_ethtool_ops; +extern const struct dcbnl_rtnl_ops sparx5_dcbnl_ops; /* Calculate raw offset */ static inline __pure int spx5_offset(int id, int tinst, int tcnt, diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_main_regs.h b/drivers/net/ethernet/microchip/sparx5/sparx5_main_regs.h index c42195f4ec4d..6c93dd6b01b0 100644 --- a/drivers/net/ethernet/microchip/sparx5/sparx5_main_regs.h +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_main_regs.h @@ -4,8 +4,8 @@ * Copyright (c) 2021 Microchip Technology Inc. */ -/* This file is autogenerated by cml-utils 2022-09-12 14:22:42 +0200. - * Commit ID: 06aecbca4eab6e85d87f665fe6b6348c48146245 +/* This file is autogenerated by cml-utils 2022-09-28 11:17:02 +0200. + * Commit ID: 385c8a11d71a9f6a60368d3a3cb648fa257b479a */ #ifndef _SPARX5_MAIN_REGS_H_ @@ -750,6 +750,96 @@ enum sparx5_target { #define ANA_CL_VLAN_CTRL_2_VLAN_PUSH_CNT_GET(x)\ FIELD_GET(ANA_CL_VLAN_CTRL_2_VLAN_PUSH_CNT, x) +/* ANA_CL:PORT:PCP_DEI_MAP_CFG */ +#define ANA_CL_PCP_DEI_MAP_CFG(g, r) __REG(TARGET_ANA_CL, 0, 1, 131072, g, 70, 512, 108, r, 16, 4) + +#define ANA_CL_PCP_DEI_MAP_CFG_PCP_DEI_DP_VAL GENMASK(4, 3) +#define ANA_CL_PCP_DEI_MAP_CFG_PCP_DEI_DP_VAL_SET(x)\ + FIELD_PREP(ANA_CL_PCP_DEI_MAP_CFG_PCP_DEI_DP_VAL, x) +#define ANA_CL_PCP_DEI_MAP_CFG_PCP_DEI_DP_VAL_GET(x)\ + FIELD_GET(ANA_CL_PCP_DEI_MAP_CFG_PCP_DEI_DP_VAL, x) + +#define ANA_CL_PCP_DEI_MAP_CFG_PCP_DEI_QOS_VAL GENMASK(2, 0) +#define ANA_CL_PCP_DEI_MAP_CFG_PCP_DEI_QOS_VAL_SET(x)\ + FIELD_PREP(ANA_CL_PCP_DEI_MAP_CFG_PCP_DEI_QOS_VAL, x) +#define ANA_CL_PCP_DEI_MAP_CFG_PCP_DEI_QOS_VAL_GET(x)\ + FIELD_GET(ANA_CL_PCP_DEI_MAP_CFG_PCP_DEI_QOS_VAL, x) + +/* ANA_CL:PORT:QOS_CFG */ +#define ANA_CL_QOS_CFG(g) __REG(TARGET_ANA_CL, 0, 1, 131072, g, 70, 512, 172, 0, 1, 4) + +#define ANA_CL_QOS_CFG_DEFAULT_COSID_ENA BIT(17) +#define ANA_CL_QOS_CFG_DEFAULT_COSID_ENA_SET(x)\ + FIELD_PREP(ANA_CL_QOS_CFG_DEFAULT_COSID_ENA, x) +#define ANA_CL_QOS_CFG_DEFAULT_COSID_ENA_GET(x)\ + FIELD_GET(ANA_CL_QOS_CFG_DEFAULT_COSID_ENA, x) + +#define ANA_CL_QOS_CFG_DEFAULT_COSID_VAL GENMASK(16, 14) +#define ANA_CL_QOS_CFG_DEFAULT_COSID_VAL_SET(x)\ + FIELD_PREP(ANA_CL_QOS_CFG_DEFAULT_COSID_VAL, x) +#define ANA_CL_QOS_CFG_DEFAULT_COSID_VAL_GET(x)\ + FIELD_GET(ANA_CL_QOS_CFG_DEFAULT_COSID_VAL, x) + +#define ANA_CL_QOS_CFG_DSCP_REWR_MODE_SEL GENMASK(13, 12) +#define ANA_CL_QOS_CFG_DSCP_REWR_MODE_SEL_SET(x)\ + FIELD_PREP(ANA_CL_QOS_CFG_DSCP_REWR_MODE_SEL, x) +#define ANA_CL_QOS_CFG_DSCP_REWR_MODE_SEL_GET(x)\ + FIELD_GET(ANA_CL_QOS_CFG_DSCP_REWR_MODE_SEL, x) + +#define ANA_CL_QOS_CFG_DSCP_TRANSLATE_ENA BIT(11) +#define ANA_CL_QOS_CFG_DSCP_TRANSLATE_ENA_SET(x)\ + FIELD_PREP(ANA_CL_QOS_CFG_DSCP_TRANSLATE_ENA, x) +#define ANA_CL_QOS_CFG_DSCP_TRANSLATE_ENA_GET(x)\ + FIELD_GET(ANA_CL_QOS_CFG_DSCP_TRANSLATE_ENA, x) + +#define ANA_CL_QOS_CFG_DSCP_KEEP_ENA BIT(10) +#define ANA_CL_QOS_CFG_DSCP_KEEP_ENA_SET(x)\ + FIELD_PREP(ANA_CL_QOS_CFG_DSCP_KEEP_ENA, x) +#define ANA_CL_QOS_CFG_DSCP_KEEP_ENA_GET(x)\ + FIELD_GET(ANA_CL_QOS_CFG_DSCP_KEEP_ENA, x) + +#define ANA_CL_QOS_CFG_KEEP_ENA BIT(9) +#define ANA_CL_QOS_CFG_KEEP_ENA_SET(x)\ + FIELD_PREP(ANA_CL_QOS_CFG_KEEP_ENA, x) +#define ANA_CL_QOS_CFG_KEEP_ENA_GET(x)\ + FIELD_GET(ANA_CL_QOS_CFG_KEEP_ENA, x) + +#define ANA_CL_QOS_CFG_PCP_DEI_DP_ENA BIT(8) +#define ANA_CL_QOS_CFG_PCP_DEI_DP_ENA_SET(x)\ + FIELD_PREP(ANA_CL_QOS_CFG_PCP_DEI_DP_ENA, x) +#define ANA_CL_QOS_CFG_PCP_DEI_DP_ENA_GET(x)\ + FIELD_GET(ANA_CL_QOS_CFG_PCP_DEI_DP_ENA, x) + +#define ANA_CL_QOS_CFG_PCP_DEI_QOS_ENA BIT(7) +#define ANA_CL_QOS_CFG_PCP_DEI_QOS_ENA_SET(x)\ + FIELD_PREP(ANA_CL_QOS_CFG_PCP_DEI_QOS_ENA, x) +#define ANA_CL_QOS_CFG_PCP_DEI_QOS_ENA_GET(x)\ + FIELD_GET(ANA_CL_QOS_CFG_PCP_DEI_QOS_ENA, x) + +#define ANA_CL_QOS_CFG_DSCP_DP_ENA BIT(6) +#define ANA_CL_QOS_CFG_DSCP_DP_ENA_SET(x)\ + FIELD_PREP(ANA_CL_QOS_CFG_DSCP_DP_ENA, x) +#define ANA_CL_QOS_CFG_DSCP_DP_ENA_GET(x)\ + FIELD_GET(ANA_CL_QOS_CFG_DSCP_DP_ENA, x) + +#define ANA_CL_QOS_CFG_DSCP_QOS_ENA BIT(5) +#define ANA_CL_QOS_CFG_DSCP_QOS_ENA_SET(x)\ + FIELD_PREP(ANA_CL_QOS_CFG_DSCP_QOS_ENA, x) +#define ANA_CL_QOS_CFG_DSCP_QOS_ENA_GET(x)\ + FIELD_GET(ANA_CL_QOS_CFG_DSCP_QOS_ENA, x) + +#define ANA_CL_QOS_CFG_DEFAULT_DP_VAL GENMASK(4, 3) +#define ANA_CL_QOS_CFG_DEFAULT_DP_VAL_SET(x)\ + FIELD_PREP(ANA_CL_QOS_CFG_DEFAULT_DP_VAL, x) +#define ANA_CL_QOS_CFG_DEFAULT_DP_VAL_GET(x)\ + FIELD_GET(ANA_CL_QOS_CFG_DEFAULT_DP_VAL, x) + +#define ANA_CL_QOS_CFG_DEFAULT_QOS_VAL GENMASK(2, 0) +#define ANA_CL_QOS_CFG_DEFAULT_QOS_VAL_SET(x)\ + FIELD_PREP(ANA_CL_QOS_CFG_DEFAULT_QOS_VAL, x) +#define ANA_CL_QOS_CFG_DEFAULT_QOS_VAL_GET(x)\ + FIELD_GET(ANA_CL_QOS_CFG_DEFAULT_QOS_VAL, x) + /* ANA_CL:PORT:CAPTURE_BPDU_CFG */ #define ANA_CL_CAPTURE_BPDU_CFG(g) __REG(TARGET_ANA_CL, 0, 1, 131072, g, 70, 512, 196, 0, 1, 4) @@ -762,6 +852,39 @@ enum sparx5_target { #define ANA_CL_OWN_UPSID_OWN_UPSID_GET(x)\ FIELD_GET(ANA_CL_OWN_UPSID_OWN_UPSID, x) +/* ANA_CL:COMMON:DSCP_CFG */ +#define ANA_CL_DSCP_CFG(r) __REG(TARGET_ANA_CL, 0, 1, 166912, 0, 1, 756, 256, r, 64, 4) + +#define ANA_CL_DSCP_CFG_DSCP_TRANSLATE_VAL GENMASK(12, 7) +#define ANA_CL_DSCP_CFG_DSCP_TRANSLATE_VAL_SET(x)\ + FIELD_PREP(ANA_CL_DSCP_CFG_DSCP_TRANSLATE_VAL, x) +#define ANA_CL_DSCP_CFG_DSCP_TRANSLATE_VAL_GET(x)\ + FIELD_GET(ANA_CL_DSCP_CFG_DSCP_TRANSLATE_VAL, x) + +#define ANA_CL_DSCP_CFG_DSCP_QOS_VAL GENMASK(6, 4) +#define ANA_CL_DSCP_CFG_DSCP_QOS_VAL_SET(x)\ + FIELD_PREP(ANA_CL_DSCP_CFG_DSCP_QOS_VAL, x) +#define ANA_CL_DSCP_CFG_DSCP_QOS_VAL_GET(x)\ + FIELD_GET(ANA_CL_DSCP_CFG_DSCP_QOS_VAL, x) + +#define ANA_CL_DSCP_CFG_DSCP_DP_VAL GENMASK(3, 2) +#define ANA_CL_DSCP_CFG_DSCP_DP_VAL_SET(x)\ + FIELD_PREP(ANA_CL_DSCP_CFG_DSCP_DP_VAL, x) +#define ANA_CL_DSCP_CFG_DSCP_DP_VAL_GET(x)\ + FIELD_GET(ANA_CL_DSCP_CFG_DSCP_DP_VAL, x) + +#define ANA_CL_DSCP_CFG_DSCP_REWR_ENA BIT(1) +#define ANA_CL_DSCP_CFG_DSCP_REWR_ENA_SET(x)\ + FIELD_PREP(ANA_CL_DSCP_CFG_DSCP_REWR_ENA, x) +#define ANA_CL_DSCP_CFG_DSCP_REWR_ENA_GET(x)\ + FIELD_GET(ANA_CL_DSCP_CFG_DSCP_REWR_ENA, x) + +#define ANA_CL_DSCP_CFG_DSCP_TRUST_ENA BIT(0) +#define ANA_CL_DSCP_CFG_DSCP_TRUST_ENA_SET(x)\ + FIELD_PREP(ANA_CL_DSCP_CFG_DSCP_TRUST_ENA, x) +#define ANA_CL_DSCP_CFG_DSCP_TRUST_ENA_GET(x)\ + FIELD_GET(ANA_CL_DSCP_CFG_DSCP_TRUST_ENA, x) + /* ANA_L2:COMMON:AUTO_LRN_CFG */ #define ANA_L2_AUTO_LRN_CFG __REG(TARGET_ANA_L2, 0, 1, 566024, 0, 1, 700, 24, 0, 1, 4) diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_port.c b/drivers/net/ethernet/microchip/sparx5/sparx5_port.c index 32709d21ab2f..9444ec4fff15 100644 --- a/drivers/net/ethernet/microchip/sparx5/sparx5_port.c +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_port.c @@ -6,6 +6,7 @@ #include #include +#include #include "sparx5_main_regs.h" #include "sparx5_main.h" @@ -1144,3 +1145,39 @@ void sparx5_port_enable(struct sparx5_port *port, bool enable) sparx5, QFWD_SWITCH_PORT_MODE(port->portno)); } + +int sparx5_port_qos_set(struct sparx5_port *port, + struct sparx5_port_qos *qos) +{ + sparx5_port_qos_pcp_set(port, &qos->pcp); + + return 0; +} + +int sparx5_port_qos_pcp_set(const struct sparx5_port *port, + struct sparx5_port_qos_pcp *qos) +{ + struct sparx5 *sparx5 = port->sparx5; + u8 *pcp_itr = qos->map.map; + u8 pcp, dp; + int i; + + /* Enable/disable pcp and dp for qos classification. */ + spx5_rmw(ANA_CL_QOS_CFG_PCP_DEI_QOS_ENA_SET(1) | + ANA_CL_QOS_CFG_PCP_DEI_DP_ENA_SET(1), + ANA_CL_QOS_CFG_PCP_DEI_QOS_ENA | ANA_CL_QOS_CFG_PCP_DEI_DP_ENA, + sparx5, ANA_CL_QOS_CFG(port->portno)); + + /* Map each pcp and dei value to priority and dp */ + for (i = 0; i < ARRAY_SIZE(qos->map.map); i++) { + pcp = *(pcp_itr + i); + dp = (i < SPARX5_PORT_QOS_PCP_COUNT) ? 0 : 1; + spx5_rmw(ANA_CL_PCP_DEI_MAP_CFG_PCP_DEI_QOS_VAL_SET(pcp) | + ANA_CL_PCP_DEI_MAP_CFG_PCP_DEI_DP_VAL_SET(dp), + ANA_CL_PCP_DEI_MAP_CFG_PCP_DEI_QOS_VAL | + ANA_CL_PCP_DEI_MAP_CFG_PCP_DEI_DP_VAL, sparx5, + ANA_CL_PCP_DEI_MAP_CFG(port->portno, i)); + } + + return 0; +} diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_port.h b/drivers/net/ethernet/microchip/sparx5/sparx5_port.h index 2f8043eac71b..331d2ad0913a 100644 --- a/drivers/net/ethernet/microchip/sparx5/sparx5_port.h +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_port.h @@ -91,4 +91,25 @@ int sparx5_get_port_status(struct sparx5 *sparx5, void sparx5_port_enable(struct sparx5_port *port, bool enable); int sparx5_port_fwd_urg(struct sparx5 *sparx5, u32 speed); +#define SPARX5_PORT_QOS_PCP_COUNT 8 +#define SPARX5_PORT_QOS_DEI_COUNT 8 +#define SPARX5_PORT_QOS_PCP_DEI_COUNT \ + (SPARX5_PORT_QOS_PCP_COUNT + SPARX5_PORT_QOS_DEI_COUNT) +struct sparx5_port_qos_pcp_map { + u8 map[SPARX5_PORT_QOS_PCP_DEI_COUNT]; +}; + +struct sparx5_port_qos_pcp { + struct sparx5_port_qos_pcp_map map; +}; + +struct sparx5_port_qos { + struct sparx5_port_qos_pcp pcp; +}; + +int sparx5_port_qos_set(struct sparx5_port *port, struct sparx5_port_qos *qos); + +int sparx5_port_qos_pcp_set(const struct sparx5_port *port, + struct sparx5_port_qos_pcp *qos); + #endif /* __SPARX5_PORT_H__ */ diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_qos.c b/drivers/net/ethernet/microchip/sparx5/sparx5_qos.c index 1e79d0ef0cb8..379e540e5e6a 100644 --- a/drivers/net/ethernet/microchip/sparx5/sparx5_qos.c +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_qos.c @@ -389,6 +389,10 @@ int sparx5_qos_init(struct sparx5 *sparx5) if (ret < 0) return ret; + ret = sparx5_dcb_init(sparx5); + if (ret < 0) + return ret; + return 0; } From 23f8382cd95db36dc32ca81ce72d0936caa34f80 Mon Sep 17 00:00:00 2001 From: Daniel Machon Date: Tue, 1 Nov 2022 10:48:32 +0100 Subject: [PATCH 0570/2002] net: microchip: sparx5: add support for apptrust Make use of set/getapptrust() to implement per-selector trust and trust order. Signed-off-by: Daniel Machon Signed-off-by: Paolo Abeni --- .../ethernet/microchip/sparx5/sparx5_dcb.c | 122 ++++++++++++++++++ .../ethernet/microchip/sparx5/sparx5_port.c | 4 +- .../ethernet/microchip/sparx5/sparx5_port.h | 2 + 3 files changed, 126 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_dcb.c b/drivers/net/ethernet/microchip/sparx5/sparx5_dcb.c index df96c17582ef..96100a063e13 100644 --- a/drivers/net/ethernet/microchip/sparx5/sparx5_dcb.c +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_dcb.c @@ -8,6 +8,37 @@ #include "sparx5_port.h" +enum sparx5_dcb_apptrust_values { + SPARX5_DCB_APPTRUST_EMPTY, + SPARX5_DCB_APPTRUST_DSCP, + SPARX5_DCB_APPTRUST_PCP, + SPARX5_DCB_APPTRUST_DSCP_PCP, + __SPARX5_DCB_APPTRUST_MAX +}; + +static const struct sparx5_dcb_apptrust { + u8 selectors[IEEE_8021QAZ_APP_SEL_MAX + 1]; + int nselectors; +} *sparx5_port_apptrust[SPX5_PORTS]; + +static const char *sparx5_dcb_apptrust_names[__SPARX5_DCB_APPTRUST_MAX] = { + [SPARX5_DCB_APPTRUST_EMPTY] = "empty", + [SPARX5_DCB_APPTRUST_DSCP] = "dscp", + [SPARX5_DCB_APPTRUST_PCP] = "pcp", + [SPARX5_DCB_APPTRUST_DSCP_PCP] = "dscp pcp" +}; + +/* Sparx5 supported apptrust policies */ +static const struct sparx5_dcb_apptrust + sparx5_dcb_apptrust_policies[__SPARX5_DCB_APPTRUST_MAX] = { + /* Empty *must* be first */ + [SPARX5_DCB_APPTRUST_EMPTY] = { { 0 }, 0 }, + [SPARX5_DCB_APPTRUST_DSCP] = { { IEEE_8021QAZ_APP_SEL_DSCP }, 1 }, + [SPARX5_DCB_APPTRUST_PCP] = { { DCB_APP_SEL_PCP }, 1 }, + [SPARX5_DCB_APPTRUST_DSCP_PCP] = { { IEEE_8021QAZ_APP_SEL_DSCP, + DCB_APP_SEL_PCP }, 2 }, +}; + /* Validate app entry. * * Check for valid selectors and valid protocol and priority ranges. @@ -37,12 +68,62 @@ static int sparx5_dcb_app_validate(struct net_device *dev, return err; } +/* Validate apptrust configuration. + * + * Return index of supported apptrust configuration if valid, otherwise return + * error. + */ +static int sparx5_dcb_apptrust_validate(struct net_device *dev, u8 *selectors, + int nselectors, int *err) +{ + bool match; + int i, ii; + + for (i = 0; i < ARRAY_SIZE(sparx5_dcb_apptrust_policies); i++) { + if (sparx5_dcb_apptrust_policies[i].nselectors != nselectors) + continue; + match = true; + for (ii = 0; ii < nselectors; ii++) { + if (sparx5_dcb_apptrust_policies[i].selectors[ii] != + *(selectors + ii)) { + match = false; + break; + } + } + if (match) + break; + } + + /* Requested trust configuration is not supported */ + if (!match) { + netdev_err(dev, "Valid apptrust configurations are:\n"); + for (i = 0; i < ARRAY_SIZE(sparx5_dcb_apptrust_names); i++) + pr_info("order: %s\n", sparx5_dcb_apptrust_names[i]); + *err = -EOPNOTSUPP; + } + + return i; +} + +static bool sparx5_dcb_apptrust_contains(int portno, u8 selector) +{ + const struct sparx5_dcb_apptrust *conf = sparx5_port_apptrust[portno]; + int i; + + for (i = 0; i < conf->nselectors; i++) + if (conf->selectors[i] == selector) + return true; + + return false; +} + static int sparx5_dcb_app_update(struct net_device *dev) { struct dcb_app app_itr = { .selector = DCB_APP_SEL_PCP }; struct sparx5_port *port = netdev_priv(dev); struct sparx5_port_qos_pcp_map *pcp_map; struct sparx5_port_qos qos = {0}; + int portno = port->portno; int i; pcp_map = &qos.pcp.map; @@ -53,6 +134,12 @@ static int sparx5_dcb_app_update(struct net_device *dev) pcp_map->map[i] = dcb_getapp(dev, &app_itr); } + /* Enable use of pcp for queue classification ? */ + if (sparx5_dcb_apptrust_contains(portno, DCB_APP_SEL_PCP)) { + qos.pcp.qos_enable = true; + qos.pcp.dp_enable = qos.pcp.qos_enable; + } + return sparx5_port_qos_set(port, &qos); } @@ -95,9 +182,40 @@ static int sparx5_dcb_ieee_delapp(struct net_device *dev, struct dcb_app *app) return sparx5_dcb_app_update(dev); } +static int sparx5_dcb_setapptrust(struct net_device *dev, u8 *selectors, + int nselectors) +{ + struct sparx5_port *port = netdev_priv(dev); + int err = 0, idx; + + idx = sparx5_dcb_apptrust_validate(dev, selectors, nselectors, &err); + if (err < 0) + return err; + + sparx5_port_apptrust[port->portno] = &sparx5_dcb_apptrust_policies[idx]; + + return sparx5_dcb_app_update(dev); +} + +static int sparx5_dcb_getapptrust(struct net_device *dev, u8 *selectors, + int *nselectors) +{ + struct sparx5_port *port = netdev_priv(dev); + const struct sparx5_dcb_apptrust *trust; + + trust = sparx5_port_apptrust[port->portno]; + + memcpy(selectors, trust->selectors, trust->nselectors); + *nselectors = trust->nselectors; + + return 0; +} + const struct dcbnl_rtnl_ops sparx5_dcbnl_ops = { .ieee_setapp = sparx5_dcb_ieee_setapp, .ieee_delapp = sparx5_dcb_ieee_delapp, + .dcbnl_setapptrust = sparx5_dcb_setapptrust, + .dcbnl_getapptrust = sparx5_dcb_getapptrust, }; int sparx5_dcb_init(struct sparx5 *sparx5) @@ -110,6 +228,10 @@ int sparx5_dcb_init(struct sparx5 *sparx5) if (!port) continue; port->ndev->dcbnl_ops = &sparx5_dcbnl_ops; + /* Initialize [dscp, pcp] default trust */ + sparx5_port_apptrust[port->portno] = + &sparx5_dcb_apptrust_policies + [SPARX5_DCB_APPTRUST_DSCP_PCP]; } return 0; diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_port.c b/drivers/net/ethernet/microchip/sparx5/sparx5_port.c index 9444ec4fff15..3cd310f5b9ee 100644 --- a/drivers/net/ethernet/microchip/sparx5/sparx5_port.c +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_port.c @@ -1163,8 +1163,8 @@ int sparx5_port_qos_pcp_set(const struct sparx5_port *port, int i; /* Enable/disable pcp and dp for qos classification. */ - spx5_rmw(ANA_CL_QOS_CFG_PCP_DEI_QOS_ENA_SET(1) | - ANA_CL_QOS_CFG_PCP_DEI_DP_ENA_SET(1), + spx5_rmw(ANA_CL_QOS_CFG_PCP_DEI_QOS_ENA_SET(qos->qos_enable) | + ANA_CL_QOS_CFG_PCP_DEI_DP_ENA_SET(qos->dp_enable), ANA_CL_QOS_CFG_PCP_DEI_QOS_ENA | ANA_CL_QOS_CFG_PCP_DEI_DP_ENA, sparx5, ANA_CL_QOS_CFG(port->portno)); diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_port.h b/drivers/net/ethernet/microchip/sparx5/sparx5_port.h index 331d2ad0913a..ae9625cbca8e 100644 --- a/drivers/net/ethernet/microchip/sparx5/sparx5_port.h +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_port.h @@ -101,6 +101,8 @@ struct sparx5_port_qos_pcp_map { struct sparx5_port_qos_pcp { struct sparx5_port_qos_pcp_map map; + bool qos_enable; + bool dp_enable; }; struct sparx5_port_qos { From 8dcf69a64118f0ae9506ce6c584bc5648ad84aa1 Mon Sep 17 00:00:00 2001 From: Daniel Machon Date: Tue, 1 Nov 2022 10:48:33 +0100 Subject: [PATCH 0571/2002] net: microchip: sparx5: add support for offloading dscp table Add support for offloading dscp app entries. Dscp values are global for all ports on the sparx5 switch. Therefore, we replicate each dscp app entry per-port. Signed-off-by: Daniel Machon Signed-off-by: Paolo Abeni --- .../ethernet/microchip/sparx5/sparx5_dcb.c | 66 ++++++++++++++++++- .../ethernet/microchip/sparx5/sparx5_port.c | 39 +++++++++++ .../ethernet/microchip/sparx5/sparx5_port.h | 14 ++++ 3 files changed, 116 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_dcb.c b/drivers/net/ethernet/microchip/sparx5/sparx5_dcb.c index 96100a063e13..be1dc78fc7ba 100644 --- a/drivers/net/ethernet/microchip/sparx5/sparx5_dcb.c +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_dcb.c @@ -49,6 +49,13 @@ static int sparx5_dcb_app_validate(struct net_device *dev, int err = 0; switch (app->selector) { + /* Dscp checks */ + case IEEE_8021QAZ_APP_SEL_DSCP: + if (app->protocol >= SPARX5_PORT_QOS_DSCP_COUNT) + err = -EINVAL; + else if (app->priority >= SPX5_PRIOS) + err = -ERANGE; + break; /* Pcp checks */ case DCB_APP_SEL_PCP: if (app->protocol >= SPARX5_PORT_QOS_PCP_DEI_COUNT) @@ -119,17 +126,27 @@ static bool sparx5_dcb_apptrust_contains(int portno, u8 selector) static int sparx5_dcb_app_update(struct net_device *dev) { - struct dcb_app app_itr = { .selector = DCB_APP_SEL_PCP }; struct sparx5_port *port = netdev_priv(dev); + struct sparx5_port_qos_dscp_map *dscp_map; struct sparx5_port_qos_pcp_map *pcp_map; struct sparx5_port_qos qos = {0}; + struct dcb_app app_itr = {0}; int portno = port->portno; int i; + dscp_map = &qos.dscp.map; pcp_map = &qos.pcp.map; + /* Get dscp ingress mapping */ + for (i = 0; i < ARRAY_SIZE(dscp_map->map); i++) { + app_itr.selector = IEEE_8021QAZ_APP_SEL_DSCP; + app_itr.protocol = i; + dscp_map->map[i] = dcb_getapp(dev, &app_itr); + } + /* Get pcp ingress mapping */ for (i = 0; i < ARRAY_SIZE(pcp_map->map); i++) { + app_itr.selector = DCB_APP_SEL_PCP; app_itr.protocol = i; pcp_map->map[i] = dcb_getapp(dev, &app_itr); } @@ -140,9 +157,44 @@ static int sparx5_dcb_app_update(struct net_device *dev) qos.pcp.dp_enable = qos.pcp.qos_enable; } + /* Enable use of dscp for queue classification ? */ + if (sparx5_dcb_apptrust_contains(portno, IEEE_8021QAZ_APP_SEL_DSCP)) { + qos.dscp.qos_enable = true; + qos.dscp.dp_enable = qos.dscp.qos_enable; + } + return sparx5_port_qos_set(port, &qos); } +/* Set or delete dscp app entry. + * + * Dscp mapping is global for all ports, so set and delete app entries are + * replicated for each port. + */ +static int sparx5_dcb_ieee_dscp_setdel_app(struct net_device *dev, + struct dcb_app *app, bool del) +{ + struct sparx5_port *port = netdev_priv(dev); + struct dcb_app apps[SPX5_PORTS]; + struct sparx5_port *port_itr; + int err, i; + + for (i = 0; i < SPX5_PORTS; i++) { + port_itr = port->sparx5->ports[i]; + if (!port_itr) + continue; + memcpy(&apps[i], app, sizeof(struct dcb_app)); + if (del) + err = dcb_ieee_delapp(port_itr->ndev, &apps[i]); + else + err = dcb_ieee_setapp(port_itr->ndev, &apps[i]); + if (err) + return err; + } + + return 0; +} + static int sparx5_dcb_ieee_setapp(struct net_device *dev, struct dcb_app *app) { struct dcb_app app_itr; @@ -161,7 +213,11 @@ static int sparx5_dcb_ieee_setapp(struct net_device *dev, struct dcb_app *app) dcb_ieee_delapp(dev, &app_itr); } - err = dcb_ieee_setapp(dev, app); + if (app->selector == IEEE_8021QAZ_APP_SEL_DSCP) + err = sparx5_dcb_ieee_dscp_setdel_app(dev, app, false); + else + err = dcb_ieee_setapp(dev, app); + if (err) goto out; @@ -175,7 +231,11 @@ static int sparx5_dcb_ieee_delapp(struct net_device *dev, struct dcb_app *app) { int err; - err = dcb_ieee_delapp(dev, app); + if (app->selector == IEEE_8021QAZ_APP_SEL_DSCP) + err = sparx5_dcb_ieee_dscp_setdel_app(dev, app, true); + else + err = dcb_ieee_delapp(dev, app); + if (err < 0) return err; diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_port.c b/drivers/net/ethernet/microchip/sparx5/sparx5_port.c index 3cd310f5b9ee..23ef93919753 100644 --- a/drivers/net/ethernet/microchip/sparx5/sparx5_port.c +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_port.c @@ -1149,6 +1149,7 @@ void sparx5_port_enable(struct sparx5_port *port, bool enable) int sparx5_port_qos_set(struct sparx5_port *port, struct sparx5_port_qos *qos) { + sparx5_port_qos_dscp_set(port, &qos->dscp); sparx5_port_qos_pcp_set(port, &qos->pcp); return 0; @@ -1181,3 +1182,41 @@ int sparx5_port_qos_pcp_set(const struct sparx5_port *port, return 0; } + +int sparx5_port_qos_dscp_set(const struct sparx5_port *port, + struct sparx5_port_qos_dscp *qos) +{ + struct sparx5 *sparx5 = port->sparx5; + u8 *dscp = qos->map.map; + int i; + + /* Enable/disable dscp and dp for qos classification. + * Disable rewrite of dscp values for now. + */ + spx5_rmw(ANA_CL_QOS_CFG_DSCP_QOS_ENA_SET(qos->qos_enable) | + ANA_CL_QOS_CFG_DSCP_DP_ENA_SET(qos->dp_enable) | + ANA_CL_QOS_CFG_DSCP_KEEP_ENA_SET(1), + ANA_CL_QOS_CFG_DSCP_QOS_ENA | ANA_CL_QOS_CFG_DSCP_DP_ENA | + ANA_CL_QOS_CFG_DSCP_KEEP_ENA, sparx5, + ANA_CL_QOS_CFG(port->portno)); + + /* Map each dscp value to priority and dp */ + for (i = 0; i < ARRAY_SIZE(qos->map.map); i++) { + spx5_rmw(ANA_CL_DSCP_CFG_DSCP_QOS_VAL_SET(*(dscp + i)) | + ANA_CL_DSCP_CFG_DSCP_DP_VAL_SET(0), + ANA_CL_DSCP_CFG_DSCP_QOS_VAL | + ANA_CL_DSCP_CFG_DSCP_DP_VAL, sparx5, + ANA_CL_DSCP_CFG(i)); + } + + /* Set per-dscp trust */ + for (i = 0; i < ARRAY_SIZE(qos->map.map); i++) { + if (qos->qos_enable) { + spx5_rmw(ANA_CL_DSCP_CFG_DSCP_TRUST_ENA_SET(1), + ANA_CL_DSCP_CFG_DSCP_TRUST_ENA, sparx5, + ANA_CL_DSCP_CFG(i)); + } + } + + return 0; +} diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_port.h b/drivers/net/ethernet/microchip/sparx5/sparx5_port.h index ae9625cbca8e..6141c0278e87 100644 --- a/drivers/net/ethernet/microchip/sparx5/sparx5_port.h +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_port.h @@ -99,14 +99,26 @@ struct sparx5_port_qos_pcp_map { u8 map[SPARX5_PORT_QOS_PCP_DEI_COUNT]; }; +#define SPARX5_PORT_QOS_DSCP_COUNT 64 +struct sparx5_port_qos_dscp_map { + u8 map[SPARX5_PORT_QOS_DSCP_COUNT]; +}; + struct sparx5_port_qos_pcp { struct sparx5_port_qos_pcp_map map; bool qos_enable; bool dp_enable; }; +struct sparx5_port_qos_dscp { + struct sparx5_port_qos_dscp_map map; + bool qos_enable; + bool dp_enable; +}; + struct sparx5_port_qos { struct sparx5_port_qos_pcp pcp; + struct sparx5_port_qos_dscp dscp; }; int sparx5_port_qos_set(struct sparx5_port *port, struct sparx5_port_qos *qos); @@ -114,4 +126,6 @@ int sparx5_port_qos_set(struct sparx5_port *port, struct sparx5_port_qos *qos); int sparx5_port_qos_pcp_set(const struct sparx5_port *port, struct sparx5_port_qos_pcp *qos); +int sparx5_port_qos_dscp_set(const struct sparx5_port *port, + struct sparx5_port_qos_dscp *qos); #endif /* __SPARX5_PORT_H__ */ From c58ff3ed432d0337272c0c5d06a715b3ec6a3d76 Mon Sep 17 00:00:00 2001 From: Daniel Machon Date: Tue, 1 Nov 2022 10:48:34 +0100 Subject: [PATCH 0572/2002] net: microchip: sparx5: add support for offloading default prio Add support for offloading default prio {ETHERTYPE, 0, prio}. Signed-off-by: Daniel Machon Signed-off-by: Paolo Abeni --- .../ethernet/microchip/sparx5/sparx5_dcb.c | 12 ++++++++++ .../ethernet/microchip/sparx5/sparx5_port.c | 23 +++++++++++++++++++ .../ethernet/microchip/sparx5/sparx5_port.h | 5 ++++ 3 files changed, 40 insertions(+) diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_dcb.c b/drivers/net/ethernet/microchip/sparx5/sparx5_dcb.c index be1dc78fc7ba..8108f3767767 100644 --- a/drivers/net/ethernet/microchip/sparx5/sparx5_dcb.c +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_dcb.c @@ -49,6 +49,13 @@ static int sparx5_dcb_app_validate(struct net_device *dev, int err = 0; switch (app->selector) { + /* Default priority checks */ + case IEEE_8021QAZ_APP_SEL_ETHERTYPE: + if (app->protocol != 0) + err = -EINVAL; + else if (app->priority >= SPX5_PRIOS) + err = -ERANGE; + break; /* Dscp checks */ case IEEE_8021QAZ_APP_SEL_DSCP: if (app->protocol >= SPARX5_PORT_QOS_DSCP_COUNT) @@ -137,6 +144,11 @@ static int sparx5_dcb_app_update(struct net_device *dev) dscp_map = &qos.dscp.map; pcp_map = &qos.pcp.map; + /* Get default prio. */ + qos.default_prio = dcb_ieee_getapp_default_prio_mask(dev); + if (qos.default_prio) + qos.default_prio = fls(qos.default_prio) - 1; + /* Get dscp ingress mapping */ for (i = 0; i < ARRAY_SIZE(dscp_map->map); i++) { app_itr.selector = IEEE_8021QAZ_APP_SEL_DSCP; diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_port.c b/drivers/net/ethernet/microchip/sparx5/sparx5_port.c index 23ef93919753..107b9cd931c0 100644 --- a/drivers/net/ethernet/microchip/sparx5/sparx5_port.c +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_port.c @@ -1151,6 +1151,7 @@ int sparx5_port_qos_set(struct sparx5_port *port, { sparx5_port_qos_dscp_set(port, &qos->dscp); sparx5_port_qos_pcp_set(port, &qos->pcp); + sparx5_port_qos_default_set(port, qos); return 0; } @@ -1220,3 +1221,25 @@ int sparx5_port_qos_dscp_set(const struct sparx5_port *port, return 0; } + +int sparx5_port_qos_default_set(const struct sparx5_port *port, + const struct sparx5_port_qos *qos) +{ + struct sparx5 *sparx5 = port->sparx5; + + /* Set default prio and dp level */ + spx5_rmw(ANA_CL_QOS_CFG_DEFAULT_QOS_VAL_SET(qos->default_prio) | + ANA_CL_QOS_CFG_DEFAULT_DP_VAL_SET(0), + ANA_CL_QOS_CFG_DEFAULT_QOS_VAL | + ANA_CL_QOS_CFG_DEFAULT_DP_VAL, + sparx5, ANA_CL_QOS_CFG(port->portno)); + + /* Set default pcp and dei for untagged frames */ + spx5_rmw(ANA_CL_VLAN_CTRL_PORT_PCP_SET(0) | + ANA_CL_VLAN_CTRL_PORT_DEI_SET(0), + ANA_CL_VLAN_CTRL_PORT_PCP | + ANA_CL_VLAN_CTRL_PORT_DEI, + sparx5, ANA_CL_VLAN_CTRL(port->portno)); + + return 0; +} diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_port.h b/drivers/net/ethernet/microchip/sparx5/sparx5_port.h index 6141c0278e87..fbafe22e25cc 100644 --- a/drivers/net/ethernet/microchip/sparx5/sparx5_port.h +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_port.h @@ -119,6 +119,7 @@ struct sparx5_port_qos_dscp { struct sparx5_port_qos { struct sparx5_port_qos_pcp pcp; struct sparx5_port_qos_dscp dscp; + u8 default_prio; }; int sparx5_port_qos_set(struct sparx5_port *port, struct sparx5_port_qos *qos); @@ -128,4 +129,8 @@ int sparx5_port_qos_pcp_set(const struct sparx5_port *port, int sparx5_port_qos_dscp_set(const struct sparx5_port *port, struct sparx5_port_qos_dscp *qos); + +int sparx5_port_qos_default_set(const struct sparx5_port *port, + const struct sparx5_port_qos *qos); + #endif /* __SPARX5_PORT_H__ */ From 07ec7b502800ba9f7b8b15cb01dd6556bb41aaca Mon Sep 17 00:00:00 2001 From: Stanislav Fomichev Date: Thu, 27 Oct 2022 15:55:37 -0700 Subject: [PATCH 0573/2002] bpf: make sure skb->len != 0 when redirecting to a tunneling device syzkaller managed to trigger another case where skb->len == 0 when we enter __dev_queue_xmit: WARNING: CPU: 0 PID: 2470 at include/linux/skbuff.h:2576 skb_assert_len include/linux/skbuff.h:2576 [inline] WARNING: CPU: 0 PID: 2470 at include/linux/skbuff.h:2576 __dev_queue_xmit+0x2069/0x35e0 net/core/dev.c:4295 Call Trace: dev_queue_xmit+0x17/0x20 net/core/dev.c:4406 __bpf_tx_skb net/core/filter.c:2115 [inline] __bpf_redirect_no_mac net/core/filter.c:2140 [inline] __bpf_redirect+0x5fb/0xda0 net/core/filter.c:2163 ____bpf_clone_redirect net/core/filter.c:2447 [inline] bpf_clone_redirect+0x247/0x390 net/core/filter.c:2419 bpf_prog_48159a89cb4a9a16+0x59/0x5e bpf_dispatcher_nop_func include/linux/bpf.h:897 [inline] __bpf_prog_run include/linux/filter.h:596 [inline] bpf_prog_run include/linux/filter.h:603 [inline] bpf_test_run+0x46c/0x890 net/bpf/test_run.c:402 bpf_prog_test_run_skb+0xbdc/0x14c0 net/bpf/test_run.c:1170 bpf_prog_test_run+0x345/0x3c0 kernel/bpf/syscall.c:3648 __sys_bpf+0x43a/0x6c0 kernel/bpf/syscall.c:5005 __do_sys_bpf kernel/bpf/syscall.c:5091 [inline] __se_sys_bpf kernel/bpf/syscall.c:5089 [inline] __x64_sys_bpf+0x7c/0x90 kernel/bpf/syscall.c:5089 do_syscall_64+0x54/0x70 arch/x86/entry/common.c:48 entry_SYSCALL_64_after_hwframe+0x61/0xc6 The reproducer doesn't really reproduce outside of syzkaller environment, so I'm taking a guess here. It looks like we do generate correct ETH_HLEN-sized packet, but we redirect the packet to the tunneling device. Before we do so, we __skb_pull l2 header and arrive again at skb->len == 0. Doesn't seem like we can do anything better than having an explicit check after __skb_pull? Cc: Eric Dumazet Reported-by: syzbot+f635e86ec3fa0a37e019@syzkaller.appspotmail.com Signed-off-by: Stanislav Fomichev Link: https://lore.kernel.org/r/20221027225537.353077-1-sdf@google.com Signed-off-by: Martin KaFai Lau Signed-off-by: Alexei Starovoitov --- net/core/filter.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/net/core/filter.c b/net/core/filter.c index bb0136e7a8e4..cb3b635e35be 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -2126,6 +2126,10 @@ static int __bpf_redirect_no_mac(struct sk_buff *skb, struct net_device *dev, if (mlen) { __skb_pull(skb, mlen); + if (unlikely(!skb->len)) { + kfree_skb(skb); + return -ERANGE; + } /* At ingress, the mac header has already been pulled once. * At egress, skb_pospull_rcsum has to be done in case that From 9805af8d8a5b17f9ea0a16f636f3440b970049f3 Mon Sep 17 00:00:00 2001 From: Kumar Kartikeya Dwivedi Date: Fri, 4 Nov 2022 00:39:50 +0530 Subject: [PATCH 0574/2002] bpf: Document UAPI details for special BPF types The kernel recognizes some special BPF types in map values or local kptrs. Document that only bpf_spin_lock and bpf_timer will preserve backwards compatibility, and kptr will preserve backwards compatibility for the operations on the pointer, not the types supported for such kptrs. For local kptrs, document that there are no stability guarantees at all. Finally, document that 'bpf_' namespace is reserved for adding future special fields, hence BPF programs must not declare types with such names in their programs and still expect backwards compatibility. Signed-off-by: Kumar Kartikeya Dwivedi Acked-by: David Vernet Link: https://lore.kernel.org/r/20221103191013.1236066-2-memxor@gmail.com Signed-off-by: Alexei Starovoitov --- Documentation/bpf/bpf_design_QA.rst | 44 +++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/Documentation/bpf/bpf_design_QA.rst b/Documentation/bpf/bpf_design_QA.rst index a210b8a4df00..4e4af398607b 100644 --- a/Documentation/bpf/bpf_design_QA.rst +++ b/Documentation/bpf/bpf_design_QA.rst @@ -298,3 +298,47 @@ A: NO. The BTF_ID macro does not cause a function to become part of the ABI any more than does the EXPORT_SYMBOL_GPL macro. + +Q: What is the compatibility story for special BPF types in map values? +----------------------------------------------------------------------- +Q: Users are allowed to embed bpf_spin_lock, bpf_timer fields in their BPF map +values (when using BTF support for BPF maps). This allows to use helpers for +such objects on these fields inside map values. Users are also allowed to embed +pointers to some kernel types (with __kptr and __kptr_ref BTF tags). Will the +kernel preserve backwards compatibility for these features? + +A: It depends. For bpf_spin_lock, bpf_timer: YES, for kptr and everything else: +NO, but see below. + +For struct types that have been added already, like bpf_spin_lock and bpf_timer, +the kernel will preserve backwards compatibility, as they are part of UAPI. + +For kptrs, they are also part of UAPI, but only with respect to the kptr +mechanism. The types that you can use with a __kptr and __kptr_ref tagged +pointer in your struct are NOT part of the UAPI contract. The supported types can +and will change across kernel releases. However, operations like accessing kptr +fields and bpf_kptr_xchg() helper will continue to be supported across kernel +releases for the supported types. + +For any other supported struct type, unless explicitly stated in this document +and added to bpf.h UAPI header, such types can and will arbitrarily change their +size, type, and alignment, or any other user visible API or ABI detail across +kernel releases. The users must adapt their BPF programs to the new changes and +update them to make sure their programs continue to work correctly. + +NOTE: BPF subsystem specially reserves the 'bpf_' prefix for type names, in +order to introduce more special fields in the future. Hence, user programs must +avoid defining types with 'bpf_' prefix to not be broken in future releases. In +other words, no backwards compatibility is guaranteed if one using a type in BTF +with 'bpf_' prefix. + +Q: What is the compatibility story for special BPF types in local kptrs? +------------------------------------------------------------------------ +Q: Same as above, but for local kptrs (i.e. pointers to objects allocated using +bpf_obj_new for user defined structures). Will the kernel preserve backwards +compatibility for these features? + +A: NO. + +Unlike map value types, there are no stability guarantees for this case. The +whole local kptr API itself is unstable (since it is exposed through kfuncs). From 23da464dd6b8935b66f4ee306ad8947fd32ccd75 Mon Sep 17 00:00:00 2001 From: Kumar Kartikeya Dwivedi Date: Fri, 4 Nov 2022 00:39:51 +0530 Subject: [PATCH 0575/2002] bpf: Allow specifying volatile type modifier for kptrs This is useful in particular to mark the pointer as volatile, so that compiler treats each load and store to the field as a volatile access. The alternative is having to define and use READ_ONCE and WRITE_ONCE in the BPF program. Signed-off-by: Kumar Kartikeya Dwivedi Acked-by: David Vernet Link: https://lore.kernel.org/r/20221103191013.1236066-3-memxor@gmail.com Signed-off-by: Alexei Starovoitov --- include/linux/btf.h | 5 +++++ kernel/bpf/btf.c | 3 +++ 2 files changed, 8 insertions(+) diff --git a/include/linux/btf.h b/include/linux/btf.h index f9aababc5d78..86aad9b2ce02 100644 --- a/include/linux/btf.h +++ b/include/linux/btf.h @@ -288,6 +288,11 @@ static inline bool btf_type_is_typedef(const struct btf_type *t) return BTF_INFO_KIND(t->info) == BTF_KIND_TYPEDEF; } +static inline bool btf_type_is_volatile(const struct btf_type *t) +{ + return BTF_INFO_KIND(t->info) == BTF_KIND_VOLATILE; +} + static inline bool btf_type_is_func(const struct btf_type *t) { return BTF_INFO_KIND(t->info) == BTF_KIND_FUNC; diff --git a/kernel/bpf/btf.c b/kernel/bpf/btf.c index 35c07afac924..f4d21eef6ebd 100644 --- a/kernel/bpf/btf.c +++ b/kernel/bpf/btf.c @@ -3225,6 +3225,9 @@ static int btf_find_kptr(const struct btf *btf, const struct btf_type *t, enum bpf_kptr_type type; u32 res_id; + /* Permit modifiers on the pointer itself */ + if (btf_type_is_volatile(t)) + t = btf_type_by_id(btf, t->type); /* For PTR, sz is always == 8 */ if (!btf_type_is_ptr(t)) return BTF_FIELD_IGNORE; From 261f4664caffdeb9dff4e83ee3c0334b1c3a552f Mon Sep 17 00:00:00 2001 From: Kumar Kartikeya Dwivedi Date: Fri, 4 Nov 2022 00:39:52 +0530 Subject: [PATCH 0576/2002] bpf: Clobber stack slot when writing over spilled PTR_TO_BTF_ID When support was added for spilled PTR_TO_BTF_ID to be accessed by helper memory access, the stack slot was not overwritten to STACK_MISC (and that too is only safe when env->allow_ptr_leaks is true). This means that helpers who take ARG_PTR_TO_MEM and write to it may essentially overwrite the value while the verifier continues to track the slot for spilled register. This can cause issues when PTR_TO_BTF_ID is spilled to stack, and then overwritten by helper write access, which can then be passed to BPF helpers or kfuncs. Handle this by falling back to the case introduced in a later commit, which will also handle PTR_TO_BTF_ID along with other pointer types, i.e. cd17d38f8b28 ("bpf: Permits pointers on stack for helper calls"). Finally, include a comment on why REG_LIVE_WRITTEN is not being set when clobber is set to true. In short, the reason is that while when clobber is unset, we know that we won't be writing, when it is true, we *may* write to any of the stack slots in that range. It may be a partial or complete write, to just one or many stack slots. We cannot be sure, hence to be conservative, we leave things as is and never set REG_LIVE_WRITTEN for any stack slot. However, clobber still needs to reset them to STACK_MISC assuming writes happened. However read marks still need to be propagated upwards from liveness point of view, as parent stack slot's contents may still continue to matter to child states. Cc: Yonghong Song Fixes: 1d68f22b3d53 ("bpf: Handle spilled PTR_TO_BTF_ID properly when checking stack_boundary") Signed-off-by: Kumar Kartikeya Dwivedi Link: https://lore.kernel.org/r/20221103191013.1236066-4-memxor@gmail.com Signed-off-by: Alexei Starovoitov --- kernel/bpf/verifier.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 82c07fe0bfb1..7bf12c492201 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -5154,10 +5154,6 @@ static int check_stack_range_initialized( goto mark; } - if (is_spilled_reg(&state->stack[spi]) && - base_type(state->stack[spi].spilled_ptr.type) == PTR_TO_BTF_ID) - goto mark; - if (is_spilled_reg(&state->stack[spi]) && (state->stack[spi].spilled_ptr.type == SCALAR_VALUE || env->allow_ptr_leaks)) { @@ -5188,6 +5184,11 @@ mark: mark_reg_read(env, &state->stack[spi].spilled_ptr, state->stack[spi].spilled_ptr.parent, REG_LIVE_READ64); + /* We do not set REG_LIVE_WRITTEN for stack slot, as we can not + * be sure that whether stack slot is written to or not. Hence, + * we must still conservatively propagate reads upwards even if + * helper may write to the entire memory range. + */ } return update_stack_depth(env, state, min_off); } From f5e477a861e4a20d8a1c5f7a245f3a3c3c376b03 Mon Sep 17 00:00:00 2001 From: Kumar Kartikeya Dwivedi Date: Fri, 4 Nov 2022 00:39:53 +0530 Subject: [PATCH 0577/2002] bpf: Fix slot type check in check_stack_write_var_off For the case where allow_ptr_leaks is false, code is checking whether slot type is STACK_INVALID and STACK_SPILL and rejecting other cases. This is a consequence of incorrectly checking for register type instead of the slot type (NOT_INIT and SCALAR_VALUE respectively). Fix the check. Fixes: 01f810ace9ed ("bpf: Allow variable-offset stack access") Signed-off-by: Kumar Kartikeya Dwivedi Link: https://lore.kernel.org/r/20221103191013.1236066-5-memxor@gmail.com Signed-off-by: Alexei Starovoitov --- kernel/bpf/verifier.c | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 7bf12c492201..eb111a8034e7 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -3181,14 +3181,17 @@ static int check_stack_write_var_off(struct bpf_verifier_env *env, stype = &state->stack[spi].slot_type[slot % BPF_REG_SIZE]; mark_stack_slot_scratched(env, spi); - if (!env->allow_ptr_leaks - && *stype != NOT_INIT - && *stype != SCALAR_VALUE) { - /* Reject the write if there's are spilled pointers in - * range. If we didn't reject here, the ptr status - * would be erased below (even though not all slots are - * actually overwritten), possibly opening the door to - * leaks. + if (!env->allow_ptr_leaks && *stype != STACK_MISC && *stype != STACK_ZERO) { + /* Reject the write if range we may write to has not + * been initialized beforehand. If we didn't reject + * here, the ptr status would be erased below (even + * though not all slots are actually overwritten), + * possibly opening the door to leaks. + * + * We do however catch STACK_INVALID case below, and + * only allow reading possibly uninitialized memory + * later for CAP_PERFMON, as the write may not happen to + * that slot. */ verbose(env, "spilled ptr in range of var-offset stack write; insn %d, ptr off: %d", insn_idx, i); From a28ace782e687424d7aa2c29a4516f54d5561a14 Mon Sep 17 00:00:00 2001 From: Kumar Kartikeya Dwivedi Date: Fri, 4 Nov 2022 00:39:54 +0530 Subject: [PATCH 0578/2002] bpf: Drop reg_type_may_be_refcounted_or_null It is not scalable to maintain a list of types that can have non-zero ref_obj_id. It is never set for scalars anyway, so just remove the conditional on register types and print it whenever it is non-zero. Acked-by: Dave Marchevsky Signed-off-by: Kumar Kartikeya Dwivedi Acked-by: David Vernet Link: https://lore.kernel.org/r/20221103191013.1236066-6-memxor@gmail.com Signed-off-by: Alexei Starovoitov --- kernel/bpf/verifier.c | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index eb111a8034e7..14d350a25d5d 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -457,13 +457,6 @@ static bool reg_may_point_to_spin_lock(const struct bpf_reg_state *reg) map_value_has_spin_lock(reg->map_ptr); } -static bool reg_type_may_be_refcounted_or_null(enum bpf_reg_type type) -{ - type = base_type(type); - return type == PTR_TO_SOCKET || type == PTR_TO_TCP_SOCK || - type == PTR_TO_MEM || type == PTR_TO_BTF_ID; -} - static bool type_is_rdonly_mem(u32 type) { return type & MEM_RDONLY; @@ -875,7 +868,7 @@ static void print_verifier_state(struct bpf_verifier_env *env, if (reg->id) verbose_a("id=%d", reg->id); - if (reg_type_may_be_refcounted_or_null(t) && reg->ref_obj_id) + if (reg->ref_obj_id) verbose_a("ref_obj_id=%d", reg->ref_obj_id); if (t != SCALAR_VALUE) verbose_a("off=%d", reg->off); From a35ec8e38cdd1766f29924ca391a01de20163931 Mon Sep 17 00:00:00 2001 From: "Hans J. Schultz" Date: Tue, 1 Nov 2022 21:39:21 +0200 Subject: [PATCH 0579/2002] bridge: Add MAC Authentication Bypass (MAB) support Hosts that support 802.1X authentication are able to authenticate themselves by exchanging EAPOL frames with an authenticator (Ethernet bridge, in this case) and an authentication server. Access to the network is only granted by the authenticator to successfully authenticated hosts. The above is implemented in the bridge using the "locked" bridge port option. When enabled, link-local frames (e.g., EAPOL) can be locally received by the bridge, but all other frames are dropped unless the host is authenticated. That is, unless the user space control plane installed an FDB entry according to which the source address of the frame is located behind the locked ingress port. The entry can be dynamic, in which case learning needs to be enabled so that the entry will be refreshed by incoming traffic. There are deployments in which not all the devices connected to the authenticator (the bridge) support 802.1X. Such devices can include printers and cameras. One option to support such deployments is to unlock the bridge ports connecting these devices, but a slightly more secure option is to use MAB. When MAB is enabled, the MAC address of the connected device is used as the user name and password for the authentication. For MAB to work, the user space control plane needs to be notified about MAC addresses that are trying to gain access so that they will be compared against an allow list. This can be implemented via the regular learning process with the sole difference that learned FDB entries are installed with a new "locked" flag indicating that the entry cannot be used to authenticate the device. The flag cannot be set by user space, but user space can clear the flag by replacing the entry, thereby authenticating the device. Locked FDB entries implement the following semantics with regards to roaming, aging and forwarding: 1. Roaming: Locked FDB entries can roam to unlocked (authorized) ports, in which case the "locked" flag is cleared. FDB entries cannot roam to locked ports regardless of MAB being enabled or not. Therefore, locked FDB entries are only created if an FDB entry with the given {MAC, VID} does not already exist. This behavior prevents unauthenticated devices from disrupting traffic destined to already authenticated devices. 2. Aging: Locked FDB entries age and refresh by incoming traffic like regular entries. 3. Forwarding: Locked FDB entries forward traffic like regular entries. If user space detects an unauthorized MAC behind a locked port and wishes to prevent traffic with this MAC DA from reaching the host, it can do so using tc or a different mechanism. Enable the above behavior using a new bridge port option called "mab". It can only be enabled on a bridge port that is both locked and has learning enabled. Locked FDB entries are flushed from the port once MAB is disabled. A new option is added because there are pure 802.1X deployments that are not interested in notifications about locked FDB entries. Signed-off-by: Hans J. Schultz Signed-off-by: Ido Schimmel Acked-by: Nikolay Aleksandrov Reviewed-by: Vladimir Oltean Signed-off-by: Jakub Kicinski --- include/linux/if_bridge.h | 1 + include/uapi/linux/if_link.h | 1 + include/uapi/linux/neighbour.h | 8 +++++++- net/bridge/br_fdb.c | 24 ++++++++++++++++++++++++ net/bridge/br_input.c | 21 +++++++++++++++++++-- net/bridge/br_netlink.c | 21 ++++++++++++++++++++- net/bridge/br_private.h | 3 ++- net/core/rtnetlink.c | 5 +++++ 8 files changed, 79 insertions(+), 5 deletions(-) diff --git a/include/linux/if_bridge.h b/include/linux/if_bridge.h index d62ef428e3aa..1668ac4d7adc 100644 --- a/include/linux/if_bridge.h +++ b/include/linux/if_bridge.h @@ -59,6 +59,7 @@ struct br_ip_list { #define BR_MRP_LOST_IN_CONT BIT(19) #define BR_TX_FWD_OFFLOAD BIT(20) #define BR_PORT_LOCKED BIT(21) +#define BR_PORT_MAB BIT(22) #define BR_DEFAULT_AGEING_TIME (300 * HZ) diff --git a/include/uapi/linux/if_link.h b/include/uapi/linux/if_link.h index 5e7a1041df3a..d92b3f79eba3 100644 --- a/include/uapi/linux/if_link.h +++ b/include/uapi/linux/if_link.h @@ -561,6 +561,7 @@ enum { IFLA_BRPORT_MCAST_EHT_HOSTS_LIMIT, IFLA_BRPORT_MCAST_EHT_HOSTS_CNT, IFLA_BRPORT_LOCKED, + IFLA_BRPORT_MAB, __IFLA_BRPORT_MAX }; #define IFLA_BRPORT_MAX (__IFLA_BRPORT_MAX - 1) diff --git a/include/uapi/linux/neighbour.h b/include/uapi/linux/neighbour.h index a998bf761635..5e67a7eaf4a7 100644 --- a/include/uapi/linux/neighbour.h +++ b/include/uapi/linux/neighbour.h @@ -52,7 +52,8 @@ enum { #define NTF_STICKY (1 << 6) #define NTF_ROUTER (1 << 7) /* Extended flags under NDA_FLAGS_EXT: */ -#define NTF_EXT_MANAGED (1 << 0) +#define NTF_EXT_MANAGED (1 << 0) +#define NTF_EXT_LOCKED (1 << 1) /* * Neighbor Cache Entry States. @@ -86,6 +87,11 @@ enum { * NTF_EXT_MANAGED flagged neigbor entries are managed by the kernel on behalf * of a user space control plane, and automatically refreshed so that (if * possible) they remain in NUD_REACHABLE state. + * + * NTF_EXT_LOCKED flagged bridge FDB entries are entries generated by the + * bridge in response to a host trying to communicate via a locked bridge port + * with MAB enabled. Their purpose is to notify user space that a host requires + * authentication. */ struct nda_cacheinfo { diff --git a/net/bridge/br_fdb.c b/net/bridge/br_fdb.c index e7f4fccb6adb..3b83af4458b8 100644 --- a/net/bridge/br_fdb.c +++ b/net/bridge/br_fdb.c @@ -105,6 +105,7 @@ static int fdb_fill_info(struct sk_buff *skb, const struct net_bridge *br, struct nda_cacheinfo ci; struct nlmsghdr *nlh; struct ndmsg *ndm; + u32 ext_flags = 0; nlh = nlmsg_put(skb, portid, seq, type, sizeof(*ndm), flags); if (nlh == NULL) @@ -125,11 +126,16 @@ static int fdb_fill_info(struct sk_buff *skb, const struct net_bridge *br, ndm->ndm_flags |= NTF_EXT_LEARNED; if (test_bit(BR_FDB_STICKY, &fdb->flags)) ndm->ndm_flags |= NTF_STICKY; + if (test_bit(BR_FDB_LOCKED, &fdb->flags)) + ext_flags |= NTF_EXT_LOCKED; if (nla_put(skb, NDA_LLADDR, ETH_ALEN, &fdb->key.addr)) goto nla_put_failure; if (nla_put_u32(skb, NDA_MASTER, br->dev->ifindex)) goto nla_put_failure; + if (nla_put_u32(skb, NDA_FLAGS_EXT, ext_flags)) + goto nla_put_failure; + ci.ndm_used = jiffies_to_clock_t(now - fdb->used); ci.ndm_confirmed = 0; ci.ndm_updated = jiffies_to_clock_t(now - fdb->updated); @@ -171,6 +177,7 @@ static inline size_t fdb_nlmsg_size(void) return NLMSG_ALIGN(sizeof(struct ndmsg)) + nla_total_size(ETH_ALEN) /* NDA_LLADDR */ + nla_total_size(sizeof(u32)) /* NDA_MASTER */ + + nla_total_size(sizeof(u32)) /* NDA_FLAGS_EXT */ + nla_total_size(sizeof(u16)) /* NDA_VLAN */ + nla_total_size(sizeof(struct nda_cacheinfo)) + nla_total_size(0) /* NDA_FDB_EXT_ATTRS */ @@ -879,6 +886,11 @@ void br_fdb_update(struct net_bridge *br, struct net_bridge_port *source, &fdb->flags))) clear_bit(BR_FDB_ADDED_BY_EXT_LEARN, &fdb->flags); + /* Clear locked flag when roaming to an + * unlocked port. + */ + if (unlikely(test_bit(BR_FDB_LOCKED, &fdb->flags))) + clear_bit(BR_FDB_LOCKED, &fdb->flags); } if (unlikely(test_bit(BR_FDB_ADDED_BY_USER, &flags))) @@ -1082,6 +1094,9 @@ static int fdb_add_entry(struct net_bridge *br, struct net_bridge_port *source, modified = true; } + if (test_and_clear_bit(BR_FDB_LOCKED, &fdb->flags)) + modified = true; + if (fdb_handle_notify(fdb, notify)) modified = true; @@ -1150,6 +1165,7 @@ int br_fdb_add(struct ndmsg *ndm, struct nlattr *tb[], struct net_bridge_port *p = NULL; struct net_bridge_vlan *v; struct net_bridge *br = NULL; + u32 ext_flags = 0; int err = 0; trace_br_fdb_add(ndm, dev, addr, vid, nlh_flags); @@ -1178,6 +1194,14 @@ int br_fdb_add(struct ndmsg *ndm, struct nlattr *tb[], vg = nbp_vlan_group(p); } + if (tb[NDA_FLAGS_EXT]) + ext_flags = nla_get_u32(tb[NDA_FLAGS_EXT]); + + if (ext_flags & NTF_EXT_LOCKED) { + NL_SET_ERR_MSG_MOD(extack, "Cannot add FDB entry with \"locked\" flag set"); + return -EINVAL; + } + if (tb[NDA_FDB_EXT_ATTRS]) { attr = tb[NDA_FDB_EXT_ATTRS]; err = nla_parse_nested(nfea_tb, NFEA_MAX, attr, diff --git a/net/bridge/br_input.c b/net/bridge/br_input.c index 68b3e850bcb9..d04d2205ad4e 100644 --- a/net/bridge/br_input.c +++ b/net/bridge/br_input.c @@ -109,9 +109,26 @@ int br_handle_frame_finish(struct net *net, struct sock *sk, struct sk_buff *skb struct net_bridge_fdb_entry *fdb_src = br_fdb_find_rcu(br, eth_hdr(skb)->h_source, vid); - if (!fdb_src || READ_ONCE(fdb_src->dst) != p || - test_bit(BR_FDB_LOCAL, &fdb_src->flags)) + if (!fdb_src) { + /* FDB miss. Create locked FDB entry if MAB is enabled + * and drop the packet. + */ + if (p->flags & BR_PORT_MAB) + br_fdb_update(br, p, eth_hdr(skb)->h_source, + vid, BIT(BR_FDB_LOCKED)); goto drop; + } else if (READ_ONCE(fdb_src->dst) != p || + test_bit(BR_FDB_LOCAL, &fdb_src->flags)) { + /* FDB mismatch. Drop the packet without roaming. */ + goto drop; + } else if test_bit(BR_FDB_LOCKED, &fdb_src->flags) { + /* FDB match, but entry is locked. Refresh it and drop + * the packet. + */ + br_fdb_update(br, p, eth_hdr(skb)->h_source, vid, + BIT(BR_FDB_LOCKED)); + goto drop; + } } nbp_switchdev_frame_mark(p, skb); diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c index d087fd4c784a..4316cc82ae17 100644 --- a/net/bridge/br_netlink.c +++ b/net/bridge/br_netlink.c @@ -188,6 +188,7 @@ static inline size_t br_port_info_size(void) + nla_total_size(1) /* IFLA_BRPORT_NEIGH_SUPPRESS */ + nla_total_size(1) /* IFLA_BRPORT_ISOLATED */ + nla_total_size(1) /* IFLA_BRPORT_LOCKED */ + + nla_total_size(1) /* IFLA_BRPORT_MAB */ + nla_total_size(sizeof(struct ifla_bridge_id)) /* IFLA_BRPORT_ROOT_ID */ + nla_total_size(sizeof(struct ifla_bridge_id)) /* IFLA_BRPORT_BRIDGE_ID */ + nla_total_size(sizeof(u16)) /* IFLA_BRPORT_DESIGNATED_PORT */ @@ -274,7 +275,8 @@ static int br_port_fill_attrs(struct sk_buff *skb, nla_put_u8(skb, IFLA_BRPORT_MRP_IN_OPEN, !!(p->flags & BR_MRP_LOST_IN_CONT)) || nla_put_u8(skb, IFLA_BRPORT_ISOLATED, !!(p->flags & BR_ISOLATED)) || - nla_put_u8(skb, IFLA_BRPORT_LOCKED, !!(p->flags & BR_PORT_LOCKED))) + nla_put_u8(skb, IFLA_BRPORT_LOCKED, !!(p->flags & BR_PORT_LOCKED)) || + nla_put_u8(skb, IFLA_BRPORT_MAB, !!(p->flags & BR_PORT_MAB))) return -EMSGSIZE; timerval = br_timer_value(&p->message_age_timer); @@ -876,6 +878,7 @@ static const struct nla_policy br_port_policy[IFLA_BRPORT_MAX + 1] = { [IFLA_BRPORT_NEIGH_SUPPRESS] = { .type = NLA_U8 }, [IFLA_BRPORT_ISOLATED] = { .type = NLA_U8 }, [IFLA_BRPORT_LOCKED] = { .type = NLA_U8 }, + [IFLA_BRPORT_MAB] = { .type = NLA_U8 }, [IFLA_BRPORT_BACKUP_PORT] = { .type = NLA_U32 }, [IFLA_BRPORT_MCAST_EHT_HOSTS_LIMIT] = { .type = NLA_U32 }, }; @@ -943,6 +946,22 @@ static int br_setport(struct net_bridge_port *p, struct nlattr *tb[], br_set_port_flag(p, tb, IFLA_BRPORT_NEIGH_SUPPRESS, BR_NEIGH_SUPPRESS); br_set_port_flag(p, tb, IFLA_BRPORT_ISOLATED, BR_ISOLATED); br_set_port_flag(p, tb, IFLA_BRPORT_LOCKED, BR_PORT_LOCKED); + br_set_port_flag(p, tb, IFLA_BRPORT_MAB, BR_PORT_MAB); + + if ((p->flags & BR_PORT_MAB) && + (!(p->flags & BR_PORT_LOCKED) || !(p->flags & BR_LEARNING))) { + NL_SET_ERR_MSG(extack, "Bridge port must be locked and have learning enabled when MAB is enabled"); + p->flags = old_flags; + return -EINVAL; + } else if (!(p->flags & BR_PORT_MAB) && (old_flags & BR_PORT_MAB)) { + struct net_bridge_fdb_flush_desc desc = { + .flags = BIT(BR_FDB_LOCKED), + .flags_mask = BIT(BR_FDB_LOCKED), + .port_ifindex = p->dev->ifindex, + }; + + br_fdb_flush(p->br, &desc); + } changed_mask = old_flags ^ p->flags; diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h index 06e5f6faa431..4ce8b8e5ae0b 100644 --- a/net/bridge/br_private.h +++ b/net/bridge/br_private.h @@ -251,7 +251,8 @@ enum { BR_FDB_ADDED_BY_EXT_LEARN, BR_FDB_OFFLOADED, BR_FDB_NOTIFY, - BR_FDB_NOTIFY_INACTIVE + BR_FDB_NOTIFY_INACTIVE, + BR_FDB_LOCKED, }; struct net_bridge_fdb_key { diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index d2f27548fc0b..b64fffeb3844 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -4051,6 +4051,11 @@ int ndo_dflt_fdb_add(struct ndmsg *ndm, return err; } + if (tb[NDA_FLAGS_EXT]) { + netdev_info(dev, "invalid flags given to default FDB implementation\n"); + return err; + } + if (vid) { netdev_info(dev, "vlans aren't supported yet for dev_uc|mc_add()\n"); return err; From 4a331d3469963b5db37d462963397a8fe52aace0 Mon Sep 17 00:00:00 2001 From: "Hans J. Schultz" Date: Tue, 1 Nov 2022 21:39:22 +0200 Subject: [PATCH 0580/2002] selftests: forwarding: Add MAC Authentication Bypass (MAB) test cases Add four test cases to verify MAB functionality: * Verify that a locked FDB entry can be generated by the bridge, preventing a host from communicating via the bridge. Test that user space can clear the "locked" flag by replacing the entry, thereby authenticating the host and allowing it to communicate via the bridge. * Test that an entry cannot roam to a locked port, but that it can roam to an unlocked port. * Test that MAB can only be enabled on a port that is both locked and has learning enabled. * Test that locked FDB entries are flushed from a port when MAB is disabled. Signed-off-by: Hans J. Schultz Signed-off-by: Ido Schimmel Acked-by: Nikolay Aleksandrov Reviewed-by: Vladimir Oltean Signed-off-by: Jakub Kicinski --- .../net/forwarding/bridge_locked_port.sh | 155 +++++++++++++++++- tools/testing/selftests/net/forwarding/lib.sh | 8 + 2 files changed, 162 insertions(+), 1 deletion(-) diff --git a/tools/testing/selftests/net/forwarding/bridge_locked_port.sh b/tools/testing/selftests/net/forwarding/bridge_locked_port.sh index 5b02b6b60ce7..dc92d32464f6 100755 --- a/tools/testing/selftests/net/forwarding/bridge_locked_port.sh +++ b/tools/testing/selftests/net/forwarding/bridge_locked_port.sh @@ -1,7 +1,16 @@ #!/bin/bash # SPDX-License-Identifier: GPL-2.0 -ALL_TESTS="locked_port_ipv4 locked_port_ipv6 locked_port_vlan" +ALL_TESTS=" + locked_port_ipv4 + locked_port_ipv6 + locked_port_vlan + locked_port_mab + locked_port_mab_roam + locked_port_mab_config + locked_port_mab_flush +" + NUM_NETIFS=4 CHECK_TC="no" source lib.sh @@ -166,6 +175,150 @@ locked_port_ipv6() log_test "Locked port ipv6" } +locked_port_mab() +{ + RET=0 + check_port_mab_support || return 0 + + ping_do $h1 192.0.2.2 + check_err $? "Ping did not work before locking port" + + bridge link set dev $swp1 learning on locked on + + ping_do $h1 192.0.2.2 + check_fail $? "Ping worked on a locked port without an FDB entry" + + bridge fdb get `mac_get $h1` br br0 vlan 1 &> /dev/null + check_fail $? "FDB entry created before enabling MAB" + + bridge link set dev $swp1 learning on locked on mab on + + ping_do $h1 192.0.2.2 + check_fail $? "Ping worked on MAB enabled port without an FDB entry" + + bridge fdb get `mac_get $h1` br br0 vlan 1 | grep "dev $swp1" | grep -q "locked" + check_err $? "Locked FDB entry not created" + + bridge fdb replace `mac_get $h1` dev $swp1 master static + + ping_do $h1 192.0.2.2 + check_err $? "Ping did not work after replacing FDB entry" + + bridge fdb get `mac_get $h1` br br0 vlan 1 | grep "dev $swp1" | grep -q "locked" + check_fail $? "FDB entry marked as locked after replacement" + + bridge fdb del `mac_get $h1` dev $swp1 master + bridge link set dev $swp1 learning off locked off mab off + + log_test "Locked port MAB" +} + +# Check that entries cannot roam to a locked port, but that entries can roam +# to an unlocked port. +locked_port_mab_roam() +{ + local mac=a0:b0:c0:c0:b0:a0 + + RET=0 + check_port_mab_support || return 0 + + bridge link set dev $swp1 learning on locked on mab on + + $MZ $h1 -q -c 5 -d 100msec -t udp -a $mac -b rand + bridge fdb get $mac br br0 vlan 1 | grep "dev $swp1" | grep -q "locked" + check_err $? "No locked entry on first injection" + + $MZ $h2 -q -c 5 -d 100msec -t udp -a $mac -b rand + bridge fdb get $mac br br0 vlan 1 | grep -q "dev $swp2" + check_err $? "Entry did not roam to an unlocked port" + + bridge fdb get $mac br br0 vlan 1 | grep -q "locked" + check_fail $? "Entry roamed with locked flag on" + + $MZ $h1 -q -c 5 -d 100msec -t udp -a $mac -b rand + bridge fdb get $mac br br0 vlan 1 | grep -q "dev $swp1" + check_fail $? "Entry roamed back to locked port" + + bridge fdb del $mac vlan 1 dev $swp2 master + bridge link set dev $swp1 learning off locked off mab off + + log_test "Locked port MAB roam" +} + +# Check that MAB can only be enabled on a port that is both locked and has +# learning enabled. +locked_port_mab_config() +{ + RET=0 + check_port_mab_support || return 0 + + bridge link set dev $swp1 learning on locked off mab on &> /dev/null + check_fail $? "MAB enabled while port is unlocked" + + bridge link set dev $swp1 learning off locked on mab on &> /dev/null + check_fail $? "MAB enabled while port has learning disabled" + + bridge link set dev $swp1 learning on locked on mab on + check_err $? "Failed to enable MAB when port is locked and has learning enabled" + + bridge link set dev $swp1 learning off locked off mab off + + log_test "Locked port MAB configuration" +} + +# Check that locked FDB entries are flushed from a port when MAB is disabled. +locked_port_mab_flush() +{ + local locked_mac1=00:01:02:03:04:05 + local unlocked_mac1=00:01:02:03:04:06 + local locked_mac2=00:01:02:03:04:07 + local unlocked_mac2=00:01:02:03:04:08 + + RET=0 + check_port_mab_support || return 0 + + bridge link set dev $swp1 learning on locked on mab on + bridge link set dev $swp2 learning on locked on mab on + + # Create regular and locked FDB entries on each port. + bridge fdb add $unlocked_mac1 dev $swp1 vlan 1 master static + bridge fdb add $unlocked_mac2 dev $swp2 vlan 1 master static + + $MZ $h1 -q -c 5 -d 100msec -t udp -a $locked_mac1 -b rand + bridge fdb get $locked_mac1 br br0 vlan 1 | grep "dev $swp1" | \ + grep -q "locked" + check_err $? "Failed to create locked FDB entry on first port" + + $MZ $h2 -q -c 5 -d 100msec -t udp -a $locked_mac2 -b rand + bridge fdb get $locked_mac2 br br0 vlan 1 | grep "dev $swp2" | \ + grep -q "locked" + check_err $? "Failed to create locked FDB entry on second port" + + # Disable MAB on the first port and check that only the first locked + # FDB entry was flushed. + bridge link set dev $swp1 mab off + + bridge fdb get $unlocked_mac1 br br0 vlan 1 &> /dev/null + check_err $? "Regular FDB entry on first port was flushed after disabling MAB" + + bridge fdb get $unlocked_mac2 br br0 vlan 1 &> /dev/null + check_err $? "Regular FDB entry on second port was flushed after disabling MAB" + + bridge fdb get $locked_mac1 br br0 vlan 1 &> /dev/null + check_fail $? "Locked FDB entry on first port was not flushed after disabling MAB" + + bridge fdb get $locked_mac2 br br0 vlan 1 &> /dev/null + check_err $? "Locked FDB entry on second port was flushed after disabling MAB" + + bridge fdb del $unlocked_mac2 dev $swp2 vlan 1 master static + bridge fdb del $unlocked_mac1 dev $swp1 vlan 1 master static + + bridge link set dev $swp2 learning on locked off mab off + bridge link set dev $swp1 learning off locked off mab off + + log_test "Locked port MAB FDB flush" +} + trap cleanup EXIT setup_prepare diff --git a/tools/testing/selftests/net/forwarding/lib.sh b/tools/testing/selftests/net/forwarding/lib.sh index 3ffb9d6c0950..1c4f866de7d7 100755 --- a/tools/testing/selftests/net/forwarding/lib.sh +++ b/tools/testing/selftests/net/forwarding/lib.sh @@ -137,6 +137,14 @@ check_locked_port_support() fi } +check_port_mab_support() +{ + if ! bridge -d link show | grep -q "mab"; then + echo "SKIP: iproute2 too old; MacAuth feature not supported." + return $ksft_skip + fi +} + if [[ "$(id -u)" -ne 0 ]]; then echo "SKIP: need root privileges" exit $ksft_skip From 3830c5719af66fac9849cf5fb04b03d4e4bb46ff Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Wed, 2 Nov 2022 17:01:59 +0100 Subject: [PATCH 0581/2002] net: devlink: convert devlink port type-specific pointers to union Instead of storing type_dev as a void pointer, convert it to union and use it to store either struct net_device or struct ib_device pointer. Signed-off-by: Jiri Pirko Signed-off-by: Jakub Kicinski --- include/net/devlink.h | 13 ++++++++++--- net/core/devlink.c | 17 +++++++++++++---- 2 files changed, 23 insertions(+), 7 deletions(-) diff --git a/include/net/devlink.h b/include/net/devlink.h index ba6b8b094943..6c55aabaedf1 100644 --- a/include/net/devlink.h +++ b/include/net/devlink.h @@ -121,12 +121,19 @@ struct devlink_port { struct list_head region_list; struct devlink *devlink; unsigned int index; - spinlock_t type_lock; /* Protects type and type_dev - * pointer consistency. + spinlock_t type_lock; /* Protects type and type_eth/ib + * structures consistency. */ enum devlink_port_type type; enum devlink_port_type desired_type; - void *type_dev; + union { + struct { + struct net_device *netdev; + } type_eth; + struct { + struct ib_device *ibdev; + } type_ib; + }; struct devlink_port_attrs attrs; u8 attrs_set:1, switch_port:1, diff --git a/net/core/devlink.c b/net/core/devlink.c index 0a16ad45520e..868d04c2164f 100644 --- a/net/core/devlink.c +++ b/net/core/devlink.c @@ -1303,7 +1303,7 @@ static int devlink_nl_port_fill(struct sk_buff *msg, goto nla_put_failure_type_locked; if (devlink_port->type == DEVLINK_PORT_TYPE_ETH) { struct net *net = devlink_net(devlink_port->devlink); - struct net_device *netdev = devlink_port->type_dev; + struct net_device *netdev = devlink_port->type_eth.netdev; if (netdev && net_eq(net, dev_net(netdev)) && (nla_put_u32(msg, DEVLINK_ATTR_PORT_NETDEV_IFINDEX, @@ -1313,7 +1313,7 @@ static int devlink_nl_port_fill(struct sk_buff *msg, goto nla_put_failure_type_locked; } if (devlink_port->type == DEVLINK_PORT_TYPE_IB) { - struct ib_device *ibdev = devlink_port->type_dev; + struct ib_device *ibdev = devlink_port->type_ib.ibdev; if (ibdev && nla_put_string(msg, DEVLINK_ATTR_PORT_IBDEV_NAME, @@ -10003,7 +10003,16 @@ static void __devlink_port_type_set(struct devlink_port *devlink_port, devlink_port_type_warn_cancel(devlink_port); spin_lock_bh(&devlink_port->type_lock); devlink_port->type = type; - devlink_port->type_dev = type_dev; + switch (type) { + case DEVLINK_PORT_TYPE_ETH: + devlink_port->type_eth.netdev = type_dev; + break; + case DEVLINK_PORT_TYPE_IB: + devlink_port->type_ib.ibdev = type_dev; + break; + default: + break; + } spin_unlock_bh(&devlink_port->type_lock); devlink_port_notify(devlink_port, DEVLINK_CMD_PORT_NEW); } @@ -12016,7 +12025,7 @@ devlink_trap_report_metadata_set(struct devlink_trap_metadata *metadata, spin_lock(&in_devlink_port->type_lock); if (in_devlink_port->type == DEVLINK_PORT_TYPE_ETH) - metadata->input_dev = in_devlink_port->type_dev; + metadata->input_dev = in_devlink_port->type_eth.netdev; spin_unlock(&in_devlink_port->type_lock); } From 8573a04404ddacb2d966eef09bf38b2ad6dbe86f Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Wed, 2 Nov 2022 17:02:00 +0100 Subject: [PATCH 0582/2002] net: devlink: move port_type_warn_schedule() call to __devlink_port_type_set() As __devlink_port_type_set() is going to be called directly from netdevice notifier event handle in one of the follow-up patches, move the port_type_warn_schedule() call there. Signed-off-by: Jiri Pirko Signed-off-by: Jakub Kicinski --- net/core/devlink.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/net/core/devlink.c b/net/core/devlink.c index 868d04c2164f..3ba3435e2cd5 100644 --- a/net/core/devlink.c +++ b/net/core/devlink.c @@ -10000,7 +10000,11 @@ static void __devlink_port_type_set(struct devlink_port *devlink_port, { ASSERT_DEVLINK_PORT_REGISTERED(devlink_port); - devlink_port_type_warn_cancel(devlink_port); + if (type == DEVLINK_PORT_TYPE_NOTSET) + devlink_port_type_warn_schedule(devlink_port); + else + devlink_port_type_warn_cancel(devlink_port); + spin_lock_bh(&devlink_port->type_lock); devlink_port->type = type; switch (type) { @@ -10095,7 +10099,6 @@ EXPORT_SYMBOL_GPL(devlink_port_type_ib_set); void devlink_port_type_clear(struct devlink_port *devlink_port) { __devlink_port_type_set(devlink_port, DEVLINK_PORT_TYPE_NOTSET, NULL); - devlink_port_type_warn_schedule(devlink_port); } EXPORT_SYMBOL_GPL(devlink_port_type_clear); From 45791e0d00c445936bb19535fe847083b1edd26d Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Wed, 2 Nov 2022 17:02:01 +0100 Subject: [PATCH 0583/2002] net: devlink: move port_type_netdev_checks() call to __devlink_port_type_set() As __devlink_port_type_set() is going to be called directly from netdevice notifier event handle in one of the follow-up patches, move the port_type_netdev_checks() call there. Signed-off-by: Jiri Pirko Signed-off-by: Jakub Kicinski --- net/core/devlink.c | 63 ++++++++++++++++++++++++---------------------- 1 file changed, 33 insertions(+), 30 deletions(-) diff --git a/net/core/devlink.c b/net/core/devlink.c index 3ba3435e2cd5..ff81a5a5087c 100644 --- a/net/core/devlink.c +++ b/net/core/devlink.c @@ -9994,33 +9994,6 @@ void devlink_port_unregister(struct devlink_port *devlink_port) } EXPORT_SYMBOL_GPL(devlink_port_unregister); -static void __devlink_port_type_set(struct devlink_port *devlink_port, - enum devlink_port_type type, - void *type_dev) -{ - ASSERT_DEVLINK_PORT_REGISTERED(devlink_port); - - if (type == DEVLINK_PORT_TYPE_NOTSET) - devlink_port_type_warn_schedule(devlink_port); - else - devlink_port_type_warn_cancel(devlink_port); - - spin_lock_bh(&devlink_port->type_lock); - devlink_port->type = type; - switch (type) { - case DEVLINK_PORT_TYPE_ETH: - devlink_port->type_eth.netdev = type_dev; - break; - case DEVLINK_PORT_TYPE_IB: - devlink_port->type_ib.ibdev = type_dev; - break; - default: - break; - } - spin_unlock_bh(&devlink_port->type_lock); - devlink_port_notify(devlink_port, DEVLINK_CMD_PORT_NEW); -} - static void devlink_port_type_netdev_checks(struct devlink_port *devlink_port, struct net_device *netdev) { @@ -10058,6 +10031,38 @@ static void devlink_port_type_netdev_checks(struct devlink_port *devlink_port, } } +static void __devlink_port_type_set(struct devlink_port *devlink_port, + enum devlink_port_type type, + void *type_dev) +{ + struct net_device *netdev = type_dev; + + ASSERT_DEVLINK_PORT_REGISTERED(devlink_port); + + if (type == DEVLINK_PORT_TYPE_NOTSET) { + devlink_port_type_warn_schedule(devlink_port); + } else { + devlink_port_type_warn_cancel(devlink_port); + if (type == DEVLINK_PORT_TYPE_ETH && netdev) + devlink_port_type_netdev_checks(devlink_port, netdev); + } + + spin_lock_bh(&devlink_port->type_lock); + devlink_port->type = type; + switch (type) { + case DEVLINK_PORT_TYPE_ETH: + devlink_port->type_eth.netdev = netdev; + break; + case DEVLINK_PORT_TYPE_IB: + devlink_port->type_ib.ibdev = type_dev; + break; + default: + break; + } + spin_unlock_bh(&devlink_port->type_lock); + devlink_port_notify(devlink_port, DEVLINK_CMD_PORT_NEW); +} + /** * devlink_port_type_eth_set - Set port type to Ethernet * @@ -10067,9 +10072,7 @@ static void devlink_port_type_netdev_checks(struct devlink_port *devlink_port, void devlink_port_type_eth_set(struct devlink_port *devlink_port, struct net_device *netdev) { - if (netdev) - devlink_port_type_netdev_checks(devlink_port, netdev); - else + if (!netdev) dev_warn(devlink_port->devlink->dev, "devlink port type for port %d set to Ethernet without a software interface reference, device type not supported by the kernel?\n", devlink_port->index); From d41c9dbd12745cfc1cb2946cd99016d83c2c5364 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Wed, 2 Nov 2022 17:02:02 +0100 Subject: [PATCH 0584/2002] net: devlink: take RTNL in port_fill() function only if it is not held Follow-up patch is going to introduce a netdevice notifier event processing which is called with RTNL mutex held. Processing of this will eventually lead to call to port_notity() and port_fill() which currently takes RTNL mutex internally. So as a temporary solution, propagate a bool indicating if the mutex is already held. This will go away in one of the follow-up patches. Signed-off-by: Jiri Pirko Signed-off-by: Jakub Kicinski --- net/core/devlink.c | 46 +++++++++++++++++++++++++++++++--------------- 1 file changed, 31 insertions(+), 15 deletions(-) diff --git a/net/core/devlink.c b/net/core/devlink.c index ff81a5a5087c..3387dfbb80c5 100644 --- a/net/core/devlink.c +++ b/net/core/devlink.c @@ -1278,7 +1278,8 @@ out: static int devlink_nl_port_fill(struct sk_buff *msg, struct devlink_port *devlink_port, enum devlink_command cmd, u32 portid, u32 seq, - int flags, struct netlink_ext_ack *extack) + int flags, struct netlink_ext_ack *extack, + bool rtnl_held) { struct devlink *devlink = devlink_port->devlink; void *hdr; @@ -1293,7 +1294,8 @@ static int devlink_nl_port_fill(struct sk_buff *msg, goto nla_put_failure; /* Hold rtnl lock while accessing port's netdev attributes. */ - rtnl_lock(); + if (!rtnl_held) + rtnl_lock(); spin_lock_bh(&devlink_port->type_lock); if (nla_put_u16(msg, DEVLINK_ATTR_PORT_TYPE, devlink_port->type)) goto nla_put_failure_type_locked; @@ -1321,7 +1323,8 @@ static int devlink_nl_port_fill(struct sk_buff *msg, goto nla_put_failure_type_locked; } spin_unlock_bh(&devlink_port->type_lock); - rtnl_unlock(); + if (!rtnl_held) + rtnl_unlock(); if (devlink_nl_port_attrs_put(msg, devlink_port)) goto nla_put_failure; if (devlink_nl_port_function_attrs_put(msg, devlink_port, extack)) @@ -1336,14 +1339,15 @@ static int devlink_nl_port_fill(struct sk_buff *msg, nla_put_failure_type_locked: spin_unlock_bh(&devlink_port->type_lock); - rtnl_unlock(); + if (!rtnl_held) + rtnl_unlock(); nla_put_failure: genlmsg_cancel(msg, hdr); return -EMSGSIZE; } -static void devlink_port_notify(struct devlink_port *devlink_port, - enum devlink_command cmd) +static void __devlink_port_notify(struct devlink_port *devlink_port, + enum devlink_command cmd, bool rtnl_held) { struct devlink *devlink = devlink_port->devlink; struct sk_buff *msg; @@ -1358,7 +1362,8 @@ static void devlink_port_notify(struct devlink_port *devlink_port, if (!msg) return; - err = devlink_nl_port_fill(msg, devlink_port, cmd, 0, 0, 0, NULL); + err = devlink_nl_port_fill(msg, devlink_port, cmd, 0, 0, 0, NULL, + rtnl_held); if (err) { nlmsg_free(msg); return; @@ -1368,6 +1373,12 @@ static void devlink_port_notify(struct devlink_port *devlink_port, 0, DEVLINK_MCGRP_CONFIG, GFP_KERNEL); } +static void devlink_port_notify(struct devlink_port *devlink_port, + enum devlink_command cmd) +{ + __devlink_port_notify(devlink_port, cmd, false); +} + static void devlink_rate_notify(struct devlink_rate *devlink_rate, enum devlink_command cmd) { @@ -1531,7 +1542,7 @@ static int devlink_nl_cmd_port_get_doit(struct sk_buff *skb, err = devlink_nl_port_fill(msg, devlink_port, DEVLINK_CMD_PORT_NEW, info->snd_portid, info->snd_seq, 0, - info->extack); + info->extack, false); if (err) { nlmsg_free(msg); return err; @@ -1561,7 +1572,8 @@ static int devlink_nl_cmd_port_get_dumpit(struct sk_buff *msg, DEVLINK_CMD_NEW, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, - NLM_F_MULTI, cb->extack); + NLM_F_MULTI, cb->extack, + false); if (err) { devl_unlock(devlink); devlink_put(devlink); @@ -1773,7 +1785,8 @@ static int devlink_port_new_notify(struct devlink *devlink, } err = devlink_nl_port_fill(msg, devlink_port, DEVLINK_CMD_NEW, - info->snd_portid, info->snd_seq, 0, NULL); + info->snd_portid, info->snd_seq, 0, NULL, + false); if (err) goto out; @@ -10033,7 +10046,7 @@ static void devlink_port_type_netdev_checks(struct devlink_port *devlink_port, static void __devlink_port_type_set(struct devlink_port *devlink_port, enum devlink_port_type type, - void *type_dev) + void *type_dev, bool rtnl_held) { struct net_device *netdev = type_dev; @@ -10060,7 +10073,7 @@ static void __devlink_port_type_set(struct devlink_port *devlink_port, break; } spin_unlock_bh(&devlink_port->type_lock); - devlink_port_notify(devlink_port, DEVLINK_CMD_PORT_NEW); + __devlink_port_notify(devlink_port, DEVLINK_CMD_PORT_NEW, rtnl_held); } /** @@ -10077,7 +10090,8 @@ void devlink_port_type_eth_set(struct devlink_port *devlink_port, "devlink port type for port %d set to Ethernet without a software interface reference, device type not supported by the kernel?\n", devlink_port->index); - __devlink_port_type_set(devlink_port, DEVLINK_PORT_TYPE_ETH, netdev); + __devlink_port_type_set(devlink_port, DEVLINK_PORT_TYPE_ETH, netdev, + false); } EXPORT_SYMBOL_GPL(devlink_port_type_eth_set); @@ -10090,7 +10104,8 @@ EXPORT_SYMBOL_GPL(devlink_port_type_eth_set); void devlink_port_type_ib_set(struct devlink_port *devlink_port, struct ib_device *ibdev) { - __devlink_port_type_set(devlink_port, DEVLINK_PORT_TYPE_IB, ibdev); + __devlink_port_type_set(devlink_port, DEVLINK_PORT_TYPE_IB, ibdev, + false); } EXPORT_SYMBOL_GPL(devlink_port_type_ib_set); @@ -10101,7 +10116,8 @@ EXPORT_SYMBOL_GPL(devlink_port_type_ib_set); */ void devlink_port_type_clear(struct devlink_port *devlink_port) { - __devlink_port_type_set(devlink_port, DEVLINK_PORT_TYPE_NOTSET, NULL); + __devlink_port_type_set(devlink_port, DEVLINK_PORT_TYPE_NOTSET, NULL, + false); } EXPORT_SYMBOL_GPL(devlink_port_type_clear); From 02a68a47eadedf95748facfca6ced31fb0181d52 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Wed, 2 Nov 2022 17:02:03 +0100 Subject: [PATCH 0585/2002] net: devlink: track netdev with devlink_port assigned Currently, ethernet drivers are using devlink_port_type_eth_set() and devlink_port_type_clear() to set devlink port type and link to related netdev. Instead of calling them directly, let the driver use SET_NETDEV_DEVLINK_PORT macro to assign devlink_port pointer and let devlink to track it. Note the devlink port pointer is static during the time netdevice is registered. In devlink code, use per-namespace netdev notifier to track the netdevices with devlink_port assigned and change the internal devlink_port type and related type pointer accordingly. Signed-off-by: Jiri Pirko Signed-off-by: Jakub Kicinski --- include/linux/netdevice.h | 19 ++++++++++ net/core/dev.c | 14 +++++--- net/core/devlink.c | 75 ++++++++++++++++++++++++++++++++++++--- 3 files changed, 99 insertions(+), 9 deletions(-) diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 4b5052db978f..f048a30ea10b 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -1999,6 +1999,11 @@ enum netdev_ml_priv_type { * registered * @offload_xstats_l3: L3 HW stats for this netdevice. * + * @devlink_port: Pointer to related devlink port structure. + * Assigned by a driver before netdev registration using + * SET_NETDEV_DEVLINK_PORT macro. This pointer is static + * during the time netdevice is registered. + * * FIXME: cleanup struct net_device such that network protocol info * moves out. */ @@ -2349,9 +2354,22 @@ struct net_device { netdevice_tracker watchdog_dev_tracker; netdevice_tracker dev_registered_tracker; struct rtnl_hw_stats64 *offload_xstats_l3; + + struct devlink_port *devlink_port; }; #define to_net_dev(d) container_of(d, struct net_device, dev) +/* + * Driver should use this to assign devlink port instance to a netdevice + * before it registers the netdevice. Therefore devlink_port is static + * during the netdev lifetime after it is registered. + */ +#define SET_NETDEV_DEVLINK_PORT(dev, port) \ +({ \ + WARN_ON((dev)->reg_state != NETREG_UNINITIALIZED); \ + ((dev)->devlink_port = (port)); \ +}) + static inline bool netif_elide_gro(const struct net_device *dev) { if (!(dev->features & NETIF_F_GRO) || dev->xdp_prog) @@ -2785,6 +2803,7 @@ enum netdev_cmd { NETDEV_PRE_TYPE_CHANGE, NETDEV_POST_TYPE_CHANGE, NETDEV_POST_INIT, + NETDEV_PRE_UNINIT, NETDEV_RELEASE, NETDEV_NOTIFY_PEERS, NETDEV_JOIN, diff --git a/net/core/dev.c b/net/core/dev.c index 2e4f1c97b59e..3bacee3bee78 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -1621,10 +1621,10 @@ const char *netdev_cmd_to_name(enum netdev_cmd cmd) N(UP) N(DOWN) N(REBOOT) N(CHANGE) N(REGISTER) N(UNREGISTER) N(CHANGEMTU) N(CHANGEADDR) N(GOING_DOWN) N(CHANGENAME) N(FEAT_CHANGE) N(BONDING_FAILOVER) N(PRE_UP) N(PRE_TYPE_CHANGE) N(POST_TYPE_CHANGE) - N(POST_INIT) N(RELEASE) N(NOTIFY_PEERS) N(JOIN) N(CHANGEUPPER) - N(RESEND_IGMP) N(PRECHANGEMTU) N(CHANGEINFODATA) N(BONDING_INFO) - N(PRECHANGEUPPER) N(CHANGELOWERSTATE) N(UDP_TUNNEL_PUSH_INFO) - N(UDP_TUNNEL_DROP_INFO) N(CHANGE_TX_QUEUE_LEN) + N(POST_INIT) N(PRE_UNINIT) N(RELEASE) N(NOTIFY_PEERS) N(JOIN) + N(CHANGEUPPER) N(RESEND_IGMP) N(PRECHANGEMTU) N(CHANGEINFODATA) + N(BONDING_INFO) N(PRECHANGEUPPER) N(CHANGELOWERSTATE) + N(UDP_TUNNEL_PUSH_INFO) N(UDP_TUNNEL_DROP_INFO) N(CHANGE_TX_QUEUE_LEN) N(CVLAN_FILTER_PUSH_INFO) N(CVLAN_FILTER_DROP_INFO) N(SVLAN_FILTER_PUSH_INFO) N(SVLAN_FILTER_DROP_INFO) N(PRE_CHANGEADDR) N(OFFLOAD_XSTATS_ENABLE) N(OFFLOAD_XSTATS_DISABLE) @@ -10060,7 +10060,7 @@ int register_netdevice(struct net_device *dev) dev->reg_state = ret ? NETREG_UNREGISTERED : NETREG_REGISTERED; write_unlock(&dev_base_lock); if (ret) - goto err_uninit; + goto err_uninit_notify; __netdev_update_features(dev); @@ -10107,6 +10107,8 @@ int register_netdevice(struct net_device *dev) out: return ret; +err_uninit_notify: + call_netdevice_notifiers(NETDEV_PRE_UNINIT, dev); err_uninit: if (dev->netdev_ops->ndo_uninit) dev->netdev_ops->ndo_uninit(dev); @@ -10856,6 +10858,8 @@ void unregister_netdevice_many_notify(struct list_head *head, netdev_name_node_alt_flush(dev); netdev_name_node_free(dev->name_node); + call_netdevice_notifiers(NETDEV_PRE_UNINIT, dev); + if (dev->netdev_ops->ndo_uninit) dev->netdev_ops->ndo_uninit(dev); diff --git a/net/core/devlink.c b/net/core/devlink.c index 3387dfbb80c5..6f06c05c7b1a 100644 --- a/net/core/devlink.c +++ b/net/core/devlink.c @@ -71,6 +71,7 @@ struct devlink { refcount_t refcount; struct completion comp; struct rcu_head rcu; + struct notifier_block netdevice_nb; char priv[] __aligned(NETDEV_ALIGN); }; @@ -9615,6 +9616,9 @@ void devlink_set_features(struct devlink *devlink, u64 features) } EXPORT_SYMBOL_GPL(devlink_set_features); +static int devlink_netdevice_event(struct notifier_block *nb, + unsigned long event, void *ptr); + /** * devlink_alloc_ns - Allocate new devlink instance resources * in specific namespace @@ -9645,10 +9649,13 @@ struct devlink *devlink_alloc_ns(const struct devlink_ops *ops, ret = xa_alloc_cyclic(&devlinks, &devlink->index, devlink, xa_limit_31b, &last_id, GFP_KERNEL); - if (ret < 0) { - kfree(devlink); - return NULL; - } + if (ret < 0) + goto err_xa_alloc; + + devlink->netdevice_nb.notifier_call = devlink_netdevice_event; + ret = register_netdevice_notifier_net(net, &devlink->netdevice_nb); + if (ret) + goto err_register_netdevice_notifier; devlink->dev = dev; devlink->ops = ops; @@ -9675,6 +9682,12 @@ struct devlink *devlink_alloc_ns(const struct devlink_ops *ops, init_completion(&devlink->comp); return devlink; + +err_register_netdevice_notifier: + xa_erase(&devlinks, devlink->index); +err_xa_alloc: + kfree(devlink); + return NULL; } EXPORT_SYMBOL_GPL(devlink_alloc_ns); @@ -9828,6 +9841,10 @@ void devlink_free(struct devlink *devlink) WARN_ON(!list_empty(&devlink->port_list)); xa_destroy(&devlink->snapshot_ids); + + unregister_netdevice_notifier_net(devlink_net(devlink), + &devlink->netdevice_nb); + xa_erase(&devlinks, devlink->index); kfree(devlink); @@ -10121,6 +10138,56 @@ void devlink_port_type_clear(struct devlink_port *devlink_port) } EXPORT_SYMBOL_GPL(devlink_port_type_clear); +static int devlink_netdevice_event(struct notifier_block *nb, + unsigned long event, void *ptr) +{ + struct net_device *netdev = netdev_notifier_info_to_dev(ptr); + struct devlink_port *devlink_port = netdev->devlink_port; + struct devlink *devlink; + + devlink = container_of(nb, struct devlink, netdevice_nb); + + if (!devlink_port || devlink_port->devlink != devlink) + return NOTIFY_OK; + + switch (event) { + case NETDEV_POST_INIT: + /* Set the type but not netdev pointer. It is going to be set + * later on by NETDEV_REGISTER event. Happens once during + * netdevice register + */ + __devlink_port_type_set(devlink_port, DEVLINK_PORT_TYPE_ETH, + NULL, true); + break; + case NETDEV_REGISTER: + /* Set the netdev on top of previously set type. Note this + * event happens also during net namespace change so here + * we take into account netdev pointer appearing in this + * namespace. + */ + __devlink_port_type_set(devlink_port, DEVLINK_PORT_TYPE_ETH, + netdev, true); + break; + case NETDEV_UNREGISTER: + /* Clear netdev pointer, but not the type. This event happens + * also during net namespace change so we need to clear + * pointer to netdev that is going to another net namespace. + */ + __devlink_port_type_set(devlink_port, DEVLINK_PORT_TYPE_ETH, + NULL, true); + break; + case NETDEV_PRE_UNINIT: + /* Clear the type and the netdev pointer. Happens one during + * netdevice unregister. + */ + __devlink_port_type_set(devlink_port, DEVLINK_PORT_TYPE_NOTSET, + NULL, true); + break; + } + + return NOTIFY_OK; +} + static int __devlink_port_attrs_set(struct devlink_port *devlink_port, enum devlink_port_flavour flavour) { From ac73d4bf2cdaf2cb8a43df8ee4a5c066d2c5d7b4 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Wed, 2 Nov 2022 17:02:04 +0100 Subject: [PATCH 0586/2002] net: make drivers to use SET_NETDEV_DEVLINK_PORT to set devlink_port Benefit from the previously implemented tracking of netdev events in devlink code and instead of calling devlink_port_type_eth_set() and devlink_port_type_clear() to set devlink port type and link to related netdev, use SET_NETDEV_DEVLINK_PORT() macro to assign devlink_port pointer to netdevice which is about to be registered. Signed-off-by: Jiri Pirko Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/broadcom/bnxt/bnxt.c | 6 +--- .../freescale/dpaa2/dpaa2-eth-devlink.c | 11 +----- .../net/ethernet/freescale/dpaa2/dpaa2-eth.c | 1 + .../ethernet/fungible/funeth/funeth_main.c | 5 +-- drivers/net/ethernet/intel/ice/ice_devlink.c | 14 ++------ drivers/net/ethernet/intel/ice/ice_main.c | 3 +- drivers/net/ethernet/intel/ice/ice_repr.c | 3 +- .../marvell/prestera/prestera_devlink.c | 10 ------ .../marvell/prestera/prestera_devlink.h | 3 -- .../ethernet/marvell/prestera/prestera_main.c | 4 +-- .../net/ethernet/mellanox/mlx4/en_netdev.c | 9 ++--- .../ethernet/mellanox/mlx5/core/en/devlink.c | 7 ---- .../ethernet/mellanox/mlx5/core/en/devlink.h | 1 - .../net/ethernet/mellanox/mlx5/core/en_main.c | 3 +- .../net/ethernet/mellanox/mlx5/core/en_rep.c | 34 ++++--------------- drivers/net/ethernet/mellanox/mlxsw/core.c | 20 +++-------- drivers/net/ethernet/mellanox/mlxsw/core.h | 7 ++-- drivers/net/ethernet/mellanox/mlxsw/minimal.c | 6 ++-- .../net/ethernet/mellanox/mlxsw/spectrum.c | 5 ++- drivers/net/ethernet/mscc/ocelot_net.c | 1 + drivers/net/ethernet/mscc/ocelot_vsc7514.c | 9 ----- .../net/ethernet/netronome/nfp/nfp_devlink.c | 12 ++----- .../net/ethernet/netronome/nfp/nfp_net_main.c | 11 ++---- drivers/net/ethernet/netronome/nfp/nfp_port.h | 2 -- .../ethernet/pensando/ionic/ionic_devlink.c | 2 +- drivers/net/ethernet/ti/am65-cpsw-nuss.c | 6 ++-- drivers/net/netdevsim/dev.c | 2 -- drivers/net/netdevsim/netdev.c | 1 + net/dsa/dsa2.c | 9 ----- net/dsa/slave.c | 1 + 30 files changed, 41 insertions(+), 167 deletions(-) diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c index 04cf7684f1b0..9604294b67aa 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c @@ -13122,9 +13122,6 @@ static void bnxt_remove_one(struct pci_dev *pdev) if (BNXT_PF(bp)) bnxt_sriov_disable(bp); - if (BNXT_PF(bp)) - devlink_port_type_clear(&bp->dl_port); - bnxt_ptp_clear(bp); pci_disable_pcie_error_reporting(pdev); unregister_netdev(dev); @@ -13537,6 +13534,7 @@ static int bnxt_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) return -ENOMEM; bp = netdev_priv(dev); + SET_NETDEV_DEVLINK_PORT(dev, &bp->dl_port); bp->board_idx = ent->driver_data; bp->msg_enable = BNXT_DEF_MSG_ENABLE; bnxt_set_max_func_irqs(bp, max_irqs); @@ -13712,8 +13710,6 @@ static int bnxt_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) if (rc) goto init_err_cleanup; - if (BNXT_PF(bp)) - devlink_port_type_eth_set(&bp->dl_port, bp->dev); bnxt_dl_fw_reporters_create(bp); bnxt_print_device_info(bp); diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth-devlink.c b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth-devlink.c index 7fefe1574b6a..5c6dd3029e2f 100644 --- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth-devlink.c +++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth-devlink.c @@ -226,25 +226,16 @@ int dpaa2_eth_dl_port_add(struct dpaa2_eth_priv *priv) { struct devlink_port *devlink_port = &priv->devlink_port; struct devlink_port_attrs attrs = {}; - int err; attrs.flavour = DEVLINK_PORT_FLAVOUR_PHYSICAL; devlink_port_attrs_set(devlink_port, &attrs); - - err = devlink_port_register(priv->devlink, devlink_port, 0); - if (err) - return err; - - devlink_port_type_eth_set(devlink_port, priv->net_dev); - - return 0; + return devlink_port_register(priv->devlink, devlink_port, 0); } void dpaa2_eth_dl_port_del(struct dpaa2_eth_priv *priv) { struct devlink_port *devlink_port = &priv->devlink_port; - devlink_port_type_clear(devlink_port); devlink_port_unregister(devlink_port); } diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c index 892a1403c469..273f1d77c012 100644 --- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c +++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c @@ -4791,6 +4791,7 @@ static int dpaa2_eth_probe(struct fsl_mc_device *dpni_dev) priv = netdev_priv(net_dev); priv->net_dev = net_dev; + SET_NETDEV_DEVLINK_PORT(net_dev, &priv->devlink_port); priv->iommu_domain = iommu_get_domain_for_dev(dev); diff --git a/drivers/net/ethernet/fungible/funeth/funeth_main.c b/drivers/net/ethernet/fungible/funeth/funeth_main.c index 095f51c4d9d9..208dc89f4972 100644 --- a/drivers/net/ethernet/fungible/funeth/funeth_main.c +++ b/drivers/net/ethernet/fungible/funeth/funeth_main.c @@ -1760,6 +1760,7 @@ static int fun_create_netdev(struct fun_ethdev *ed, unsigned int portid) goto free_rss; SET_NETDEV_DEV(netdev, fdev->dev); + SET_NETDEV_DEVLINK_PORT(netdev, &fp->dl_port); netdev->netdev_ops = &fun_netdev_ops; netdev->hw_features = NETIF_F_SG | NETIF_F_RXHASH | NETIF_F_RXCSUM; @@ -1800,9 +1801,6 @@ static int fun_create_netdev(struct fun_ethdev *ed, unsigned int portid) rc = register_netdev(netdev); if (rc) goto unreg_devlink; - - devlink_port_type_eth_set(&fp->dl_port, netdev); - return 0; unreg_devlink: @@ -1827,7 +1825,6 @@ static void fun_destroy_netdev(struct net_device *netdev) struct funeth_priv *fp; fp = netdev_priv(netdev); - devlink_port_type_clear(&fp->dl_port); unregister_netdev(netdev); devlink_port_unregister(&fp->dl_port); fun_ktls_cleanup(fp); diff --git a/drivers/net/ethernet/intel/ice/ice_devlink.c b/drivers/net/ethernet/intel/ice/ice_devlink.c index e6ec20079ced..455489e9457d 100644 --- a/drivers/net/ethernet/intel/ice/ice_devlink.c +++ b/drivers/net/ethernet/intel/ice/ice_devlink.c @@ -1033,12 +1033,7 @@ int ice_devlink_create_pf_port(struct ice_pf *pf) */ void ice_devlink_destroy_pf_port(struct ice_pf *pf) { - struct devlink_port *devlink_port; - - devlink_port = &pf->devlink_port; - - devlink_port_type_clear(devlink_port); - devlink_port_unregister(devlink_port); + devlink_port_unregister(&pf->devlink_port); } /** @@ -1094,12 +1089,7 @@ int ice_devlink_create_vf_port(struct ice_vf *vf) */ void ice_devlink_destroy_vf_port(struct ice_vf *vf) { - struct devlink_port *devlink_port; - - devlink_port = &vf->devlink_port; - - devlink_port_type_clear(devlink_port); - devlink_port_unregister(devlink_port); + devlink_port_unregister(&vf->devlink_port); } #define ICE_DEVLINK_READ_BLK_SIZE (1024 * 1024) diff --git a/drivers/net/ethernet/intel/ice/ice_main.c b/drivers/net/ethernet/intel/ice/ice_main.c index 1f27dc20b4f1..74d25fda11bd 100644 --- a/drivers/net/ethernet/intel/ice/ice_main.c +++ b/drivers/net/ethernet/intel/ice/ice_main.c @@ -4603,6 +4603,7 @@ static int ice_register_netdev(struct ice_pf *pf) if (err) goto err_devlink_create; + SET_NETDEV_DEVLINK_PORT(vsi->netdev, &pf->devlink_port); err = register_netdev(vsi->netdev); if (err) goto err_register_netdev; @@ -4611,8 +4612,6 @@ static int ice_register_netdev(struct ice_pf *pf) netif_carrier_off(vsi->netdev); netif_tx_stop_all_queues(vsi->netdev); - devlink_port_type_eth_set(&pf->devlink_port, vsi->netdev); - return 0; err_register_netdev: ice_devlink_destroy_pf_port(pf); diff --git a/drivers/net/ethernet/intel/ice/ice_repr.c b/drivers/net/ethernet/intel/ice/ice_repr.c index bd31748aae1b..663a7a0e1814 100644 --- a/drivers/net/ethernet/intel/ice/ice_repr.c +++ b/drivers/net/ethernet/intel/ice/ice_repr.c @@ -339,12 +339,11 @@ static int ice_repr_add(struct ice_vf *vf) repr->netdev->max_mtu = ICE_MAX_MTU; SET_NETDEV_DEV(repr->netdev, ice_pf_to_dev(vf->pf)); + SET_NETDEV_DEVLINK_PORT(repr->netdev, &vf->devlink_port); err = ice_repr_reg_netdev(repr->netdev); if (err) goto err_netdev; - devlink_port_type_eth_set(&vf->devlink_port, repr->netdev); - ice_virtchnl_set_repr_ops(vf); return 0; diff --git a/drivers/net/ethernet/marvell/prestera/prestera_devlink.c b/drivers/net/ethernet/marvell/prestera/prestera_devlink.c index 06279cd6da67..637b8fee65e7 100644 --- a/drivers/net/ethernet/marvell/prestera/prestera_devlink.c +++ b/drivers/net/ethernet/marvell/prestera/prestera_devlink.c @@ -445,16 +445,6 @@ void prestera_devlink_port_unregister(struct prestera_port *port) devlink_port_unregister(&port->dl_port); } -void prestera_devlink_port_set(struct prestera_port *port) -{ - devlink_port_type_eth_set(&port->dl_port, port->dev); -} - -void prestera_devlink_port_clear(struct prestera_port *port) -{ - devlink_port_type_clear(&port->dl_port); -} - struct devlink_port *prestera_devlink_get_port(struct net_device *dev) { struct prestera_port *port = netdev_priv(dev); diff --git a/drivers/net/ethernet/marvell/prestera/prestera_devlink.h b/drivers/net/ethernet/marvell/prestera/prestera_devlink.h index b322295bad3a..04e8556f748a 100644 --- a/drivers/net/ethernet/marvell/prestera/prestera_devlink.h +++ b/drivers/net/ethernet/marvell/prestera/prestera_devlink.h @@ -15,9 +15,6 @@ void prestera_devlink_unregister(struct prestera_switch *sw); int prestera_devlink_port_register(struct prestera_port *port); void prestera_devlink_port_unregister(struct prestera_port *port); -void prestera_devlink_port_set(struct prestera_port *port); -void prestera_devlink_port_clear(struct prestera_port *port); - struct devlink_port *prestera_devlink_get_port(struct net_device *dev); void prestera_devlink_trap_report(struct prestera_port *port, diff --git a/drivers/net/ethernet/marvell/prestera/prestera_main.c b/drivers/net/ethernet/marvell/prestera/prestera_main.c index 24f9d6024745..a76bf3606aa6 100644 --- a/drivers/net/ethernet/marvell/prestera/prestera_main.c +++ b/drivers/net/ethernet/marvell/prestera/prestera_main.c @@ -644,6 +644,7 @@ static int prestera_port_create(struct prestera_switch *sw, u32 id) dev->netdev_ops = &prestera_netdev_ops; dev->ethtool_ops = &prestera_ethtool_ops; SET_NETDEV_DEV(dev, sw->dev->dev); + SET_NETDEV_DEVLINK_PORT(dev, &port->dl_port); if (port->caps.transceiver != PRESTERA_PORT_TCVR_SFP) netif_carrier_off(dev); @@ -737,8 +738,6 @@ static int prestera_port_create(struct prestera_switch *sw, u32 id) if (err) goto err_register_netdev; - prestera_devlink_port_set(port); - err = prestera_port_sfp_bind(port); if (err) goto err_sfp_bind; @@ -761,7 +760,6 @@ static void prestera_port_destroy(struct prestera_port *port) struct net_device *dev = port->dev; cancel_delayed_work_sync(&port->cached_hw_stats.caching_dw); - prestera_devlink_port_clear(port); unregister_netdev(dev); prestera_port_list_del(port); prestera_devlink_port_unregister(port); diff --git a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c index ca4b93a01034..8800d3f1f55c 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c @@ -2337,11 +2337,8 @@ void mlx4_en_destroy_netdev(struct net_device *dev) en_dbg(DRV, priv, "Destroying netdev on port:%d\n", priv->port); /* Unregister device - this will close the port if it was up */ - if (priv->registered) { - devlink_port_type_clear(mlx4_get_devlink_port(mdev->dev, - priv->port)); + if (priv->registered) unregister_netdev(dev); - } if (priv->allocated) mlx4_free_hwq_res(mdev->dev, &priv->res, MLX4_EN_PAGE_SIZE); @@ -3474,6 +3471,8 @@ int mlx4_en_init_netdev(struct mlx4_en_dev *mdev, int port, mdev->profile.prof[priv->port].tx_ppp, mdev->profile.prof[priv->port].tx_pause); + SET_NETDEV_DEVLINK_PORT(dev, + mlx4_get_devlink_port(mdev->dev, priv->port)); err = register_netdev(dev); if (err) { en_err(priv, "Netdev registration failed for port %d\n", port); @@ -3481,8 +3480,6 @@ int mlx4_en_init_netdev(struct mlx4_en_dev *mdev, int port, } priv->registered = 1; - devlink_port_type_eth_set(mlx4_get_devlink_port(mdev->dev, priv->port), - dev); return 0; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/devlink.c b/drivers/net/ethernet/mellanox/mlx5/core/en/devlink.c index b69f9d10ccbd..ce0e56f856d6 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/devlink.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/devlink.c @@ -51,13 +51,6 @@ int mlx5e_devlink_port_register(struct mlx5e_priv *priv) return ret; } -void mlx5e_devlink_port_type_eth_set(struct mlx5e_priv *priv) -{ - struct devlink_port *dl_port = mlx5e_devlink_get_dl_port(priv); - - devlink_port_type_eth_set(dl_port, priv->netdev); -} - void mlx5e_devlink_port_unregister(struct mlx5e_priv *priv) { struct devlink_port *dl_port = mlx5e_devlink_get_dl_port(priv); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/devlink.h b/drivers/net/ethernet/mellanox/mlx5/core/en/devlink.h index 10b50feb9883..1c203257ac30 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/devlink.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/devlink.h @@ -9,7 +9,6 @@ int mlx5e_devlink_port_register(struct mlx5e_priv *priv); void mlx5e_devlink_port_unregister(struct mlx5e_priv *priv); -void mlx5e_devlink_port_type_eth_set(struct mlx5e_priv *priv); struct devlink_port *mlx5e_get_devlink_port(struct net_device *dev); static inline struct devlink_port * diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c index 364f04309149..97e876472a06 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c @@ -5932,14 +5932,13 @@ static int mlx5e_probe(struct auxiliary_device *adev, goto err_profile_cleanup; } + SET_NETDEV_DEVLINK_PORT(netdev, mlx5e_devlink_get_dl_port(priv)); err = register_netdev(netdev); if (err) { mlx5_core_err(mdev, "register_netdev failed, %d\n", err); goto err_resume; } - mlx5e_devlink_port_type_eth_set(priv); - mlx5e_dcbnl_init_app(priv); mlx5_uplink_netdev_set(mdev, netdev); return 0; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c b/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c index 794cd8dfe9c9..5412633b9c8c 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c @@ -1253,37 +1253,20 @@ mlx5e_vport_uplink_rep_load(struct mlx5_core_dev *dev, struct mlx5_eswitch_rep * { struct mlx5e_priv *priv = netdev_priv(mlx5_uplink_netdev_get(dev)); struct mlx5e_rep_priv *rpriv = mlx5e_rep_to_rep_priv(rep); - struct devlink_port *dl_port; - int err; rpriv->netdev = priv->netdev; - - err = mlx5e_netdev_change_profile(priv, &mlx5e_uplink_rep_profile, - rpriv); - if (err) - return err; - - dl_port = mlx5_esw_offloads_devlink_port(dev->priv.eswitch, rpriv->rep->vport); - if (dl_port) - devlink_port_type_eth_set(dl_port, rpriv->netdev); - - return 0; + return mlx5e_netdev_change_profile(priv, &mlx5e_uplink_rep_profile, + rpriv); } static void mlx5e_vport_uplink_rep_unload(struct mlx5e_rep_priv *rpriv) { struct net_device *netdev = rpriv->netdev; - struct devlink_port *dl_port; - struct mlx5_core_dev *dev; struct mlx5e_priv *priv; priv = netdev_priv(netdev); - dev = priv->mdev; - dl_port = mlx5_esw_offloads_devlink_port(dev->priv.eswitch, rpriv->rep->vport); - if (dl_port) - devlink_port_type_clear(dl_port); mlx5e_netdev_attach_nic_profile(priv); } @@ -1326,6 +1309,11 @@ mlx5e_vport_vf_rep_load(struct mlx5_core_dev *dev, struct mlx5_eswitch_rep *rep) goto err_cleanup_profile; } + dl_port = mlx5_esw_offloads_devlink_port(dev->priv.eswitch, + rpriv->rep->vport); + if (dl_port) + SET_NETDEV_DEVLINK_PORT(netdev, dl_port); + err = register_netdev(netdev); if (err) { netdev_warn(netdev, @@ -1334,9 +1322,6 @@ mlx5e_vport_vf_rep_load(struct mlx5_core_dev *dev, struct mlx5_eswitch_rep *rep) goto err_detach_netdev; } - dl_port = mlx5_esw_offloads_devlink_port(dev->priv.eswitch, rpriv->rep->vport); - if (dl_port) - devlink_port_type_eth_set(dl_port, netdev); return 0; err_detach_netdev: @@ -1382,8 +1367,6 @@ mlx5e_vport_rep_unload(struct mlx5_eswitch_rep *rep) struct mlx5e_rep_priv *rpriv = mlx5e_rep_to_rep_priv(rep); struct net_device *netdev = rpriv->netdev; struct mlx5e_priv *priv = netdev_priv(netdev); - struct mlx5_core_dev *dev = priv->mdev; - struct devlink_port *dl_port; void *ppriv = priv->ppriv; if (rep->vport == MLX5_VPORT_UPLINK) { @@ -1391,9 +1374,6 @@ mlx5e_vport_rep_unload(struct mlx5_eswitch_rep *rep) goto free_ppriv; } - dl_port = mlx5_esw_offloads_devlink_port(dev->priv.eswitch, rpriv->rep->vport); - if (dl_port) - devlink_port_type_clear(dl_port); unregister_netdev(netdev); mlx5e_detach_netdev(priv); priv->profile->cleanup(priv); diff --git a/drivers/net/ethernet/mellanox/mlxsw/core.c b/drivers/net/ethernet/mellanox/mlxsw/core.c index e2a985ec2c76..a83f6bc30072 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/core.c +++ b/drivers/net/ethernet/mellanox/mlxsw/core.c @@ -3172,29 +3172,17 @@ void mlxsw_core_cpu_port_fini(struct mlxsw_core *mlxsw_core) } EXPORT_SYMBOL(mlxsw_core_cpu_port_fini); -void mlxsw_core_port_eth_set(struct mlxsw_core *mlxsw_core, u16 local_port, - void *port_driver_priv, struct net_device *dev) +void mlxsw_core_port_netdev_link(struct mlxsw_core *mlxsw_core, u16 local_port, + void *port_driver_priv, struct net_device *dev) { struct mlxsw_core_port *mlxsw_core_port = &mlxsw_core->ports[local_port]; struct devlink_port *devlink_port = &mlxsw_core_port->devlink_port; mlxsw_core_port->port_driver_priv = port_driver_priv; - devlink_port_type_eth_set(devlink_port, dev); + SET_NETDEV_DEVLINK_PORT(dev, devlink_port); } -EXPORT_SYMBOL(mlxsw_core_port_eth_set); - -void mlxsw_core_port_clear(struct mlxsw_core *mlxsw_core, u16 local_port, - void *port_driver_priv) -{ - struct mlxsw_core_port *mlxsw_core_port = - &mlxsw_core->ports[local_port]; - struct devlink_port *devlink_port = &mlxsw_core_port->devlink_port; - - mlxsw_core_port->port_driver_priv = port_driver_priv; - devlink_port_type_clear(devlink_port); -} -EXPORT_SYMBOL(mlxsw_core_port_clear); +EXPORT_SYMBOL(mlxsw_core_port_netdev_link); struct devlink_port * mlxsw_core_port_devlink_port_get(struct mlxsw_core *mlxsw_core, diff --git a/drivers/net/ethernet/mellanox/mlxsw/core.h b/drivers/net/ethernet/mellanox/mlxsw/core.h index ca0c3d2bee6b..e0a6fcbbcb19 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/core.h +++ b/drivers/net/ethernet/mellanox/mlxsw/core.h @@ -264,10 +264,9 @@ int mlxsw_core_cpu_port_init(struct mlxsw_core *mlxsw_core, const unsigned char *switch_id, unsigned char switch_id_len); void mlxsw_core_cpu_port_fini(struct mlxsw_core *mlxsw_core); -void mlxsw_core_port_eth_set(struct mlxsw_core *mlxsw_core, u16 local_port, - void *port_driver_priv, struct net_device *dev); -void mlxsw_core_port_clear(struct mlxsw_core *mlxsw_core, u16 local_port, - void *port_driver_priv); +void mlxsw_core_port_netdev_link(struct mlxsw_core *mlxsw_core, u16 local_port, + void *port_driver_priv, + struct net_device *dev); struct devlink_port * mlxsw_core_port_devlink_port_get(struct mlxsw_core *mlxsw_core, u16 local_port); diff --git a/drivers/net/ethernet/mellanox/mlxsw/minimal.c b/drivers/net/ethernet/mellanox/mlxsw/minimal.c index 55b3c42bb007..177cf7e4db34 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/minimal.c +++ b/drivers/net/ethernet/mellanox/mlxsw/minimal.c @@ -265,6 +265,8 @@ mlxsw_m_port_create(struct mlxsw_m *mlxsw_m, u16 local_port, u8 slot_index, SET_NETDEV_DEV(dev, mlxsw_m->bus_info->dev); dev_net_set(dev, mlxsw_core_net(mlxsw_m->core)); mlxsw_m_port = netdev_priv(dev); + mlxsw_core_port_netdev_link(mlxsw_m->core, local_port, + mlxsw_m_port, dev); mlxsw_m_port->dev = dev; mlxsw_m_port->mlxsw_m = mlxsw_m; mlxsw_m_port->local_port = local_port; @@ -298,9 +300,6 @@ mlxsw_m_port_create(struct mlxsw_m *mlxsw_m, u16 local_port, u8 slot_index, goto err_register_netdev; } - mlxsw_core_port_eth_set(mlxsw_m->core, mlxsw_m_port->local_port, - mlxsw_m_port, dev); - return 0; err_register_netdev: @@ -316,7 +315,6 @@ static void mlxsw_m_port_remove(struct mlxsw_m *mlxsw_m, u16 local_port) { struct mlxsw_m_port *mlxsw_m_port = mlxsw_m->ports[local_port]; - mlxsw_core_port_clear(mlxsw_m->core, local_port, mlxsw_m); unregister_netdev(mlxsw_m_port->dev); /* This calls ndo_stop */ mlxsw_m->ports[local_port] = NULL; free_netdev(mlxsw_m_port->dev); diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c index 6ed496f6cbfb..b13739e1f8f3 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c @@ -1651,6 +1651,8 @@ static int mlxsw_sp_port_create(struct mlxsw_sp *mlxsw_sp, u16 local_port, SET_NETDEV_DEV(dev, mlxsw_sp->bus_info->dev); dev_net_set(dev, mlxsw_sp_net(mlxsw_sp)); mlxsw_sp_port = netdev_priv(dev); + mlxsw_core_port_netdev_link(mlxsw_sp->core, local_port, + mlxsw_sp_port, dev); mlxsw_sp_port->dev = dev; mlxsw_sp_port->mlxsw_sp = mlxsw_sp; mlxsw_sp_port->local_port = local_port; @@ -1839,8 +1841,6 @@ static int mlxsw_sp_port_create(struct mlxsw_sp *mlxsw_sp, u16 local_port, goto err_register_netdev; } - mlxsw_core_port_eth_set(mlxsw_sp->core, mlxsw_sp_port->local_port, - mlxsw_sp_port, dev); mlxsw_core_schedule_dw(&mlxsw_sp_port->periodic_hw_stats.update_dw, 0); return 0; @@ -1897,7 +1897,6 @@ static void mlxsw_sp_port_remove(struct mlxsw_sp *mlxsw_sp, u16 local_port) cancel_delayed_work_sync(&mlxsw_sp_port->periodic_hw_stats.update_dw); cancel_delayed_work_sync(&mlxsw_sp_port->ptp.shaper_dw); - mlxsw_core_port_clear(mlxsw_sp->core, local_port, mlxsw_sp); unregister_netdev(mlxsw_sp_port->dev); /* This calls ndo_stop */ mlxsw_sp_port_ptp_clear(mlxsw_sp_port); mlxsw_sp_port_vlan_classification_set(mlxsw_sp_port, true, true); diff --git a/drivers/net/ethernet/mscc/ocelot_net.c b/drivers/net/ethernet/mscc/ocelot_net.c index 50858cc10fef..5efc07751c8d 100644 --- a/drivers/net/ethernet/mscc/ocelot_net.c +++ b/drivers/net/ethernet/mscc/ocelot_net.c @@ -1873,6 +1873,7 @@ int ocelot_probe_port(struct ocelot *ocelot, int port, struct regmap *target, if (ocelot->fdma) ocelot_fdma_netdev_init(ocelot, dev); + SET_NETDEV_DEVLINK_PORT(dev, &ocelot->devlink_ports[port]); err = register_netdev(dev); if (err) { dev_err(ocelot->dev, "register_netdev failed\n"); diff --git a/drivers/net/ethernet/mscc/ocelot_vsc7514.c b/drivers/net/ethernet/mscc/ocelot_vsc7514.c index 6f22aea08a64..93431d2ff4f1 100644 --- a/drivers/net/ethernet/mscc/ocelot_vsc7514.c +++ b/drivers/net/ethernet/mscc/ocelot_vsc7514.c @@ -377,9 +377,6 @@ static int mscc_ocelot_init_ports(struct platform_device *pdev, return -ENOMEM; for_each_available_child_of_node(ports, portnp) { - struct ocelot_port_private *priv; - struct ocelot_port *ocelot_port; - struct devlink_port *dlp; struct regmap *target; struct resource *res; char res_name[8]; @@ -420,12 +417,6 @@ static int mscc_ocelot_init_ports(struct platform_device *pdev, } devlink_ports_registered |= BIT(port); - - ocelot_port = ocelot->ports[port]; - priv = container_of(ocelot_port, struct ocelot_port_private, - port); - dlp = &ocelot->devlink_ports[port]; - devlink_port_type_eth_set(dlp, priv->dev); } /* Initialize unused devlink ports at the end */ diff --git a/drivers/net/ethernet/netronome/nfp/nfp_devlink.c b/drivers/net/ethernet/netronome/nfp/nfp_devlink.c index 405786c00334..f3f0f11d8b52 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_devlink.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_devlink.c @@ -334,6 +334,8 @@ int nfp_devlink_port_register(struct nfp_app *app, struct nfp_port *port) int serial_len; int ret; + SET_NETDEV_DEVLINK_PORT(port->netdev, &port->dl_port); + rtnl_lock(); ret = nfp_devlink_fill_eth_port(port, ð_port); rtnl_unlock(); @@ -361,16 +363,6 @@ void nfp_devlink_port_unregister(struct nfp_port *port) devl_port_unregister(&port->dl_port); } -void nfp_devlink_port_type_eth_set(struct nfp_port *port) -{ - devlink_port_type_eth_set(&port->dl_port, port->netdev); -} - -void nfp_devlink_port_type_clear(struct nfp_port *port) -{ - devlink_port_type_clear(&port->dl_port); -} - struct devlink_port *nfp_devlink_get_devlink_port(struct net_device *netdev) { struct nfp_port *port; diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_main.c b/drivers/net/ethernet/netronome/nfp/nfp_net_main.c index 3bae92dc899e..7abf0c576868 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_main.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_main.c @@ -156,22 +156,17 @@ nfp_net_pf_init_vnic(struct nfp_pf *pf, struct nfp_net *nn, unsigned int id) nfp_net_debugfs_vnic_add(nn, pf->ddir); - if (nn->port) - nfp_devlink_port_type_eth_set(nn->port); - nfp_net_info(nn); if (nfp_net_is_data_vnic(nn)) { err = nfp_app_vnic_init(pf->app, nn); if (err) - goto err_devlink_port_type_clean; + goto err_debugfs_vnic_clean; } return 0; -err_devlink_port_type_clean: - if (nn->port) - nfp_devlink_port_type_clear(nn->port); +err_debugfs_vnic_clean: nfp_net_debugfs_dir_clean(&nn->debugfs_dir); nfp_net_clean(nn); err_devlink_port_clean: @@ -220,8 +215,6 @@ static void nfp_net_pf_clean_vnic(struct nfp_pf *pf, struct nfp_net *nn) { if (nfp_net_is_data_vnic(nn)) nfp_app_vnic_clean(pf->app, nn); - if (nn->port) - nfp_devlink_port_type_clear(nn->port); nfp_net_debugfs_dir_clean(&nn->debugfs_dir); nfp_net_clean(nn); if (nn->port) diff --git a/drivers/net/ethernet/netronome/nfp/nfp_port.h b/drivers/net/ethernet/netronome/nfp/nfp_port.h index 6793cdf9ff11..f8cd157ca1d7 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_port.h +++ b/drivers/net/ethernet/netronome/nfp/nfp_port.h @@ -129,8 +129,6 @@ int nfp_net_refresh_port_table_sync(struct nfp_pf *pf); int nfp_devlink_port_register(struct nfp_app *app, struct nfp_port *port); void nfp_devlink_port_unregister(struct nfp_port *port); -void nfp_devlink_port_type_eth_set(struct nfp_port *port); -void nfp_devlink_port_type_clear(struct nfp_port *port); /* Mac stats (0x0000 - 0x0200) * all counters are 64bit. diff --git a/drivers/net/ethernet/pensando/ionic/ionic_devlink.c b/drivers/net/ethernet/pensando/ionic/ionic_devlink.c index 4297ed9024c0..567f778433e2 100644 --- a/drivers/net/ethernet/pensando/ionic/ionic_devlink.c +++ b/drivers/net/ethernet/pensando/ionic/ionic_devlink.c @@ -90,7 +90,7 @@ int ionic_devlink_register(struct ionic *ionic) return err; } - devlink_port_type_eth_set(&ionic->dl_port, ionic->lif->netdev); + SET_NETDEV_DEVLINK_PORT(ionic->lif->netdev, &ionic->dl_port); devlink_register(dl); return 0; } diff --git a/drivers/net/ethernet/ti/am65-cpsw-nuss.c b/drivers/net/ethernet/ti/am65-cpsw-nuss.c index 8800c8a010ff..b2a08f3f0e93 100644 --- a/drivers/net/ethernet/ti/am65-cpsw-nuss.c +++ b/drivers/net/ethernet/ti/am65-cpsw-nuss.c @@ -2534,7 +2534,6 @@ static void am65_cpsw_unregister_devlink(struct am65_cpsw_common *common) static int am65_cpsw_nuss_register_ndevs(struct am65_cpsw_common *common) { struct device *dev = common->dev; - struct devlink_port *dl_port; struct am65_cpsw_port *port; int ret = 0, i; @@ -2561,15 +2560,14 @@ static int am65_cpsw_nuss_register_ndevs(struct am65_cpsw_common *common) if (!port->ndev) continue; + SET_NETDEV_DEVLINK_PORT(port->ndev, &port->devlink_port); + ret = register_netdev(port->ndev); if (ret) { dev_err(dev, "error registering slave net device%i %d\n", i, ret); goto err_cleanup_ndev; } - - dl_port = &port->devlink_port; - devlink_port_type_eth_set(dl_port, port->ndev); } ret = am65_cpsw_register_notifiers(common); diff --git a/drivers/net/netdevsim/dev.c b/drivers/net/netdevsim/dev.c index a7880c7ce94c..387c05953a8b 100644 --- a/drivers/net/netdevsim/dev.c +++ b/drivers/net/netdevsim/dev.c @@ -1406,7 +1406,6 @@ static int __nsim_dev_port_add(struct nsim_dev *nsim_dev, enum nsim_dev_port_typ goto err_nsim_destroy; } - devlink_port_type_eth_set(devlink_port, nsim_dev_port->ns->netdev); list_add(&nsim_dev_port->list, &nsim_dev->port_list); return 0; @@ -1429,7 +1428,6 @@ static void __nsim_dev_port_del(struct nsim_dev_port *nsim_dev_port) list_del(&nsim_dev_port->list); if (nsim_dev_port_is_vf(nsim_dev_port)) devl_rate_leaf_destroy(&nsim_dev_port->devlink_port); - devlink_port_type_clear(devlink_port); nsim_destroy(nsim_dev_port->ns); nsim_dev_port_debugfs_exit(nsim_dev_port); devl_port_unregister(devlink_port); diff --git a/drivers/net/netdevsim/netdev.c b/drivers/net/netdevsim/netdev.c index e470e3398abc..e7cbae743e86 100644 --- a/drivers/net/netdevsim/netdev.c +++ b/drivers/net/netdevsim/netdev.c @@ -360,6 +360,7 @@ nsim_create(struct nsim_dev *nsim_dev, struct nsim_dev_port *nsim_dev_port) ns->nsim_dev_port = nsim_dev_port; ns->nsim_bus_dev = nsim_dev->nsim_bus_dev; SET_NETDEV_DEV(dev, &ns->nsim_bus_dev->dev); + SET_NETDEV_DEVLINK_PORT(dev, &nsim_dev_port->devlink_port); nsim_ethtool_init(ns); if (nsim_dev_port_is_pf(nsim_dev_port)) err = nsim_init_netdevsim(ns); diff --git a/net/dsa/dsa2.c b/net/dsa/dsa2.c index e504a18fc125..b80dbd02e154 100644 --- a/net/dsa/dsa2.c +++ b/net/dsa/dsa2.c @@ -529,7 +529,6 @@ static void dsa_port_devlink_teardown(struct dsa_port *dp) static int dsa_port_setup(struct dsa_port *dp) { - struct devlink_port *dlp = &dp->devlink_port; bool dsa_port_link_registered = false; struct dsa_switch *ds = dp->ds; bool dsa_port_enabled = false; @@ -585,10 +584,6 @@ static int dsa_port_setup(struct dsa_port *dp) case DSA_PORT_TYPE_USER: of_get_mac_address(dp->dn, dp->mac); err = dsa_slave_create(dp); - if (err) - break; - - devlink_port_type_eth_set(dlp, dp->slave); break; } @@ -608,13 +603,9 @@ static int dsa_port_setup(struct dsa_port *dp) static void dsa_port_teardown(struct dsa_port *dp) { - struct devlink_port *dlp = &dp->devlink_port; - if (!dp->setup) return; - devlink_port_type_clear(dlp); - switch (dp->type) { case DSA_PORT_TYPE_UNUSED: break; diff --git a/net/dsa/slave.c b/net/dsa/slave.c index 83e419afa89e..158bb3f1aa0b 100644 --- a/net/dsa/slave.c +++ b/net/dsa/slave.c @@ -2406,6 +2406,7 @@ int dsa_slave_create(struct dsa_port *port) SET_NETDEV_DEVTYPE(slave_dev, &dsa_type); SET_NETDEV_DEV(slave_dev, port->ds->dev); + SET_NETDEV_DEVLINK_PORT(slave_dev, &port->devlink_port); slave_dev->dev.of_node = port->dn; slave_dev->vlan_features = master->vlan_features; From c80965784dbf2fd624be654c1e73c24beada7441 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Wed, 2 Nov 2022 17:02:05 +0100 Subject: [PATCH 0587/2002] net: devlink: remove netdev arg from devlink_port_type_eth_set() Since devlink_port_type_eth_set() should no longer be called by any driver with netdev pointer as it should rather use SET_NETDEV_DEVLINK_PORT, remove the netdev arg. Add a warn to type_clear() Signed-off-by: Jiri Pirko Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/mellanox/mlx4/main.c | 2 +- include/net/devlink.h | 3 +-- net/core/devlink.c | 23 ++++++++++++++--------- 3 files changed, 16 insertions(+), 12 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx4/main.c b/drivers/net/ethernet/mellanox/mlx4/main.c index d3fc86cd3c1d..3ae246391549 100644 --- a/drivers/net/ethernet/mellanox/mlx4/main.c +++ b/drivers/net/ethernet/mellanox/mlx4/main.c @@ -3043,7 +3043,7 @@ static int mlx4_init_port_info(struct mlx4_dev *dev, int port) */ if (!IS_ENABLED(CONFIG_MLX4_EN) && dev->caps.port_type[port] == MLX4_PORT_TYPE_ETH) - devlink_port_type_eth_set(&info->devlink_port, NULL); + devlink_port_type_eth_set(&info->devlink_port); else if (!IS_ENABLED(CONFIG_MLX4_INFINIBAND) && dev->caps.port_type[port] == MLX4_PORT_TYPE_IB) devlink_port_type_ib_set(&info->devlink_port, NULL); diff --git a/include/net/devlink.h b/include/net/devlink.h index 6c55aabaedf1..b1582b32183a 100644 --- a/include/net/devlink.h +++ b/include/net/devlink.h @@ -1582,8 +1582,7 @@ int devlink_port_register(struct devlink *devlink, unsigned int port_index); void devl_port_unregister(struct devlink_port *devlink_port); void devlink_port_unregister(struct devlink_port *devlink_port); -void devlink_port_type_eth_set(struct devlink_port *devlink_port, - struct net_device *netdev); +void devlink_port_type_eth_set(struct devlink_port *devlink_port); void devlink_port_type_ib_set(struct devlink_port *devlink_port, struct ib_device *ibdev); void devlink_port_type_clear(struct devlink_port *devlink_port); diff --git a/net/core/devlink.c b/net/core/devlink.c index 6f06c05c7b1a..70a374c828ae 100644 --- a/net/core/devlink.c +++ b/net/core/devlink.c @@ -10097,17 +10097,15 @@ static void __devlink_port_type_set(struct devlink_port *devlink_port, * devlink_port_type_eth_set - Set port type to Ethernet * * @devlink_port: devlink port - * @netdev: related netdevice + * + * If driver is calling this, most likely it is doing something wrong. */ -void devlink_port_type_eth_set(struct devlink_port *devlink_port, - struct net_device *netdev) +void devlink_port_type_eth_set(struct devlink_port *devlink_port) { - if (!netdev) - dev_warn(devlink_port->devlink->dev, - "devlink port type for port %d set to Ethernet without a software interface reference, device type not supported by the kernel?\n", - devlink_port->index); - - __devlink_port_type_set(devlink_port, DEVLINK_PORT_TYPE_ETH, netdev, + dev_warn(devlink_port->devlink->dev, + "devlink port type for port %d set to Ethernet without a software interface reference, device type not supported by the kernel?\n", + devlink_port->index); + __devlink_port_type_set(devlink_port, DEVLINK_PORT_TYPE_ETH, NULL, false); } EXPORT_SYMBOL_GPL(devlink_port_type_eth_set); @@ -10130,9 +10128,16 @@ EXPORT_SYMBOL_GPL(devlink_port_type_ib_set); * devlink_port_type_clear - Clear port type * * @devlink_port: devlink port + * + * If driver is calling this for clearing Ethernet type, most likely + * it is doing something wrong. */ void devlink_port_type_clear(struct devlink_port *devlink_port) { + if (devlink_port->type == DEVLINK_PORT_TYPE_ETH) + dev_warn(devlink_port->devlink->dev, + "devlink port type for port %d cleared without a software interface reference, device type not supported by the kernel?\n", + devlink_port->index); __devlink_port_type_set(devlink_port, DEVLINK_PORT_TYPE_NOTSET, NULL, false); } From d0f5172629339f4a9cbbe5f9ae51cea48b4af333 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Wed, 2 Nov 2022 17:02:06 +0100 Subject: [PATCH 0588/2002] net: devlink: remove net namespace check from devlink_nl_port_fill() It is ensured by the netdevice notifier event processing, that only netdev pointers from the same net namespaces are filled. Remove the net namespace check from devlink_nl_port_fill() as it is no longer needed. Signed-off-by: Jiri Pirko Signed-off-by: Jakub Kicinski --- net/core/devlink.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/net/core/devlink.c b/net/core/devlink.c index 70a374c828ae..d948bb2fdd5f 100644 --- a/net/core/devlink.c +++ b/net/core/devlink.c @@ -1305,10 +1305,9 @@ static int devlink_nl_port_fill(struct sk_buff *msg, devlink_port->desired_type)) goto nla_put_failure_type_locked; if (devlink_port->type == DEVLINK_PORT_TYPE_ETH) { - struct net *net = devlink_net(devlink_port->devlink); struct net_device *netdev = devlink_port->type_eth.netdev; - if (netdev && net_eq(net, dev_net(netdev)) && + if (netdev && (nla_put_u32(msg, DEVLINK_ATTR_PORT_NETDEV_IFINDEX, netdev->ifindex) || nla_put_string(msg, DEVLINK_ATTR_PORT_NETDEV_NAME, From 31265c1e29eb28f17df50d04ee421b5b6369fefd Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Wed, 2 Nov 2022 17:02:07 +0100 Subject: [PATCH 0589/2002] net: devlink: store copy netdevice ifindex and ifname to allow port_fill() without RTNL held To avoid a need to take RTNL mutex in port_fill() function, benefit from the introduce infrastructure that tracks netdevice notifier events. Store the ifindex and ifname upon register and change name events. Remove the rtnl_held bool propagated down to port_fill() function as it is no longer needed. Signed-off-by: Jiri Pirko Signed-off-by: Jakub Kicinski --- include/net/devlink.h | 2 ++ net/core/devlink.c | 68 +++++++++++++++++-------------------------- 2 files changed, 29 insertions(+), 41 deletions(-) diff --git a/include/net/devlink.h b/include/net/devlink.h index b1582b32183a..7befad57afd4 100644 --- a/include/net/devlink.h +++ b/include/net/devlink.h @@ -129,6 +129,8 @@ struct devlink_port { union { struct { struct net_device *netdev; + int ifindex; + char ifname[IFNAMSIZ]; } type_eth; struct { struct ib_device *ibdev; diff --git a/net/core/devlink.c b/net/core/devlink.c index d948bb2fdd5f..38de3a1dff36 100644 --- a/net/core/devlink.c +++ b/net/core/devlink.c @@ -1279,8 +1279,7 @@ out: static int devlink_nl_port_fill(struct sk_buff *msg, struct devlink_port *devlink_port, enum devlink_command cmd, u32 portid, u32 seq, - int flags, struct netlink_ext_ack *extack, - bool rtnl_held) + int flags, struct netlink_ext_ack *extack) { struct devlink *devlink = devlink_port->devlink; void *hdr; @@ -1294,9 +1293,6 @@ static int devlink_nl_port_fill(struct sk_buff *msg, if (nla_put_u32(msg, DEVLINK_ATTR_PORT_INDEX, devlink_port->index)) goto nla_put_failure; - /* Hold rtnl lock while accessing port's netdev attributes. */ - if (!rtnl_held) - rtnl_lock(); spin_lock_bh(&devlink_port->type_lock); if (nla_put_u16(msg, DEVLINK_ATTR_PORT_TYPE, devlink_port->type)) goto nla_put_failure_type_locked; @@ -1305,13 +1301,11 @@ static int devlink_nl_port_fill(struct sk_buff *msg, devlink_port->desired_type)) goto nla_put_failure_type_locked; if (devlink_port->type == DEVLINK_PORT_TYPE_ETH) { - struct net_device *netdev = devlink_port->type_eth.netdev; - - if (netdev && + if (devlink_port->type_eth.netdev && (nla_put_u32(msg, DEVLINK_ATTR_PORT_NETDEV_IFINDEX, - netdev->ifindex) || + devlink_port->type_eth.ifindex) || nla_put_string(msg, DEVLINK_ATTR_PORT_NETDEV_NAME, - netdev->name))) + devlink_port->type_eth.ifname))) goto nla_put_failure_type_locked; } if (devlink_port->type == DEVLINK_PORT_TYPE_IB) { @@ -1323,8 +1317,6 @@ static int devlink_nl_port_fill(struct sk_buff *msg, goto nla_put_failure_type_locked; } spin_unlock_bh(&devlink_port->type_lock); - if (!rtnl_held) - rtnl_unlock(); if (devlink_nl_port_attrs_put(msg, devlink_port)) goto nla_put_failure; if (devlink_nl_port_function_attrs_put(msg, devlink_port, extack)) @@ -1339,15 +1331,13 @@ static int devlink_nl_port_fill(struct sk_buff *msg, nla_put_failure_type_locked: spin_unlock_bh(&devlink_port->type_lock); - if (!rtnl_held) - rtnl_unlock(); nla_put_failure: genlmsg_cancel(msg, hdr); return -EMSGSIZE; } -static void __devlink_port_notify(struct devlink_port *devlink_port, - enum devlink_command cmd, bool rtnl_held) +static void devlink_port_notify(struct devlink_port *devlink_port, + enum devlink_command cmd) { struct devlink *devlink = devlink_port->devlink; struct sk_buff *msg; @@ -1362,8 +1352,7 @@ static void __devlink_port_notify(struct devlink_port *devlink_port, if (!msg) return; - err = devlink_nl_port_fill(msg, devlink_port, cmd, 0, 0, 0, NULL, - rtnl_held); + err = devlink_nl_port_fill(msg, devlink_port, cmd, 0, 0, 0, NULL); if (err) { nlmsg_free(msg); return; @@ -1373,12 +1362,6 @@ static void __devlink_port_notify(struct devlink_port *devlink_port, 0, DEVLINK_MCGRP_CONFIG, GFP_KERNEL); } -static void devlink_port_notify(struct devlink_port *devlink_port, - enum devlink_command cmd) -{ - __devlink_port_notify(devlink_port, cmd, false); -} - static void devlink_rate_notify(struct devlink_rate *devlink_rate, enum devlink_command cmd) { @@ -1542,7 +1525,7 @@ static int devlink_nl_cmd_port_get_doit(struct sk_buff *skb, err = devlink_nl_port_fill(msg, devlink_port, DEVLINK_CMD_PORT_NEW, info->snd_portid, info->snd_seq, 0, - info->extack, false); + info->extack); if (err) { nlmsg_free(msg); return err; @@ -1572,8 +1555,7 @@ static int devlink_nl_cmd_port_get_dumpit(struct sk_buff *msg, DEVLINK_CMD_NEW, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, - NLM_F_MULTI, cb->extack, - false); + NLM_F_MULTI, cb->extack); if (err) { devl_unlock(devlink); devlink_put(devlink); @@ -1785,8 +1767,7 @@ static int devlink_port_new_notify(struct devlink *devlink, } err = devlink_nl_port_fill(msg, devlink_port, DEVLINK_CMD_NEW, - info->snd_portid, info->snd_seq, 0, NULL, - false); + info->snd_portid, info->snd_seq, 0, NULL); if (err) goto out; @@ -10062,7 +10043,7 @@ static void devlink_port_type_netdev_checks(struct devlink_port *devlink_port, static void __devlink_port_type_set(struct devlink_port *devlink_port, enum devlink_port_type type, - void *type_dev, bool rtnl_held) + void *type_dev) { struct net_device *netdev = type_dev; @@ -10081,6 +10062,13 @@ static void __devlink_port_type_set(struct devlink_port *devlink_port, switch (type) { case DEVLINK_PORT_TYPE_ETH: devlink_port->type_eth.netdev = netdev; + if (netdev) { + ASSERT_RTNL(); + devlink_port->type_eth.ifindex = netdev->ifindex; + BUILD_BUG_ON(sizeof(devlink_port->type_eth.ifname) != + sizeof(netdev->name)); + strcpy(devlink_port->type_eth.ifname, netdev->name); + } break; case DEVLINK_PORT_TYPE_IB: devlink_port->type_ib.ibdev = type_dev; @@ -10089,7 +10077,7 @@ static void __devlink_port_type_set(struct devlink_port *devlink_port, break; } spin_unlock_bh(&devlink_port->type_lock); - __devlink_port_notify(devlink_port, DEVLINK_CMD_PORT_NEW, rtnl_held); + devlink_port_notify(devlink_port, DEVLINK_CMD_PORT_NEW); } /** @@ -10104,8 +10092,7 @@ void devlink_port_type_eth_set(struct devlink_port *devlink_port) dev_warn(devlink_port->devlink->dev, "devlink port type for port %d set to Ethernet without a software interface reference, device type not supported by the kernel?\n", devlink_port->index); - __devlink_port_type_set(devlink_port, DEVLINK_PORT_TYPE_ETH, NULL, - false); + __devlink_port_type_set(devlink_port, DEVLINK_PORT_TYPE_ETH, NULL); } EXPORT_SYMBOL_GPL(devlink_port_type_eth_set); @@ -10118,8 +10105,7 @@ EXPORT_SYMBOL_GPL(devlink_port_type_eth_set); void devlink_port_type_ib_set(struct devlink_port *devlink_port, struct ib_device *ibdev) { - __devlink_port_type_set(devlink_port, DEVLINK_PORT_TYPE_IB, ibdev, - false); + __devlink_port_type_set(devlink_port, DEVLINK_PORT_TYPE_IB, ibdev); } EXPORT_SYMBOL_GPL(devlink_port_type_ib_set); @@ -10137,8 +10123,7 @@ void devlink_port_type_clear(struct devlink_port *devlink_port) dev_warn(devlink_port->devlink->dev, "devlink port type for port %d cleared without a software interface reference, device type not supported by the kernel?\n", devlink_port->index); - __devlink_port_type_set(devlink_port, DEVLINK_PORT_TYPE_NOTSET, NULL, - false); + __devlink_port_type_set(devlink_port, DEVLINK_PORT_TYPE_NOTSET, NULL); } EXPORT_SYMBOL_GPL(devlink_port_type_clear); @@ -10161,16 +10146,17 @@ static int devlink_netdevice_event(struct notifier_block *nb, * netdevice register */ __devlink_port_type_set(devlink_port, DEVLINK_PORT_TYPE_ETH, - NULL, true); + NULL); break; case NETDEV_REGISTER: + case NETDEV_CHANGENAME: /* Set the netdev on top of previously set type. Note this * event happens also during net namespace change so here * we take into account netdev pointer appearing in this * namespace. */ __devlink_port_type_set(devlink_port, DEVLINK_PORT_TYPE_ETH, - netdev, true); + netdev); break; case NETDEV_UNREGISTER: /* Clear netdev pointer, but not the type. This event happens @@ -10178,14 +10164,14 @@ static int devlink_netdevice_event(struct notifier_block *nb, * pointer to netdev that is going to another net namespace. */ __devlink_port_type_set(devlink_port, DEVLINK_PORT_TYPE_ETH, - NULL, true); + NULL); break; case NETDEV_PRE_UNINIT: /* Clear the type and the netdev pointer. Happens one during * netdevice unregister. */ __devlink_port_type_set(devlink_port, DEVLINK_PORT_TYPE_NOTSET, - NULL, true); + NULL); break; } From e705a621c071b43e4ea971abb70d7677dc640c27 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Wed, 2 Nov 2022 17:02:08 +0100 Subject: [PATCH 0590/2002] net: devlink: add not cleared type warning to port unregister By the time port unregister is called. There should be no type set. Make sure that the driver cleared it before and warn in case it didn't. This enforces symmetricity with type set and port register. Signed-off-by: Jiri Pirko Signed-off-by: Jakub Kicinski --- net/core/devlink.c | 1 + 1 file changed, 1 insertion(+) diff --git a/net/core/devlink.c b/net/core/devlink.c index 38de3a1dff36..4a0ba86b86ed 100644 --- a/net/core/devlink.c +++ b/net/core/devlink.c @@ -9977,6 +9977,7 @@ EXPORT_SYMBOL_GPL(devlink_port_register); void devl_port_unregister(struct devlink_port *devlink_port) { lockdep_assert_held(&devlink_port->devlink->lock); + WARN_ON(devlink_port->type != DEVLINK_PORT_TYPE_NOTSET); devlink_port_type_warn_cancel(devlink_port); devlink_port_notify(devlink_port, DEVLINK_CMD_PORT_DEL); From 8eba37f7e9bc82fac08f31d318e36f341494442d Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Wed, 2 Nov 2022 17:02:09 +0100 Subject: [PATCH 0591/2002] net: devlink: use devlink_port pointer instead of ndo_get_devlink_port Use newly introduced devlink_port pointer instead of getting it calling to ndo_get_devlink_port op. Signed-off-by: Jiri Pirko Signed-off-by: Jakub Kicinski --- net/core/devlink.c | 12 ++---------- net/core/net-sysfs.c | 4 ++-- net/ethtool/ioctl.c | 11 ++--------- 3 files changed, 6 insertions(+), 21 deletions(-) diff --git a/net/core/devlink.c b/net/core/devlink.c index 4a0ba86b86ed..3a454d0045e5 100644 --- a/net/core/devlink.c +++ b/net/core/devlink.c @@ -12505,14 +12505,6 @@ free_msg: nlmsg_free(msg); } -static struct devlink_port *netdev_to_devlink_port(struct net_device *dev) -{ - if (!dev->netdev_ops->ndo_get_devlink_port) - return NULL; - - return dev->netdev_ops->ndo_get_devlink_port(dev); -} - void devlink_compat_running_version(struct devlink *devlink, char *buf, size_t len) { @@ -12558,7 +12550,7 @@ int devlink_compat_phys_port_name_get(struct net_device *dev, */ ASSERT_RTNL(); - devlink_port = netdev_to_devlink_port(dev); + devlink_port = dev->devlink_port; if (!devlink_port) return -EOPNOTSUPP; @@ -12574,7 +12566,7 @@ int devlink_compat_switch_id_get(struct net_device *dev, * devlink_port instance cannot disappear in the middle. No need to take * any devlink lock as only permanent values are accessed. */ - devlink_port = netdev_to_devlink_port(dev); + devlink_port = dev->devlink_port; if (!devlink_port || !devlink_port->switch_port) return -EOPNOTSUPP; diff --git a/net/core/net-sysfs.c b/net/core/net-sysfs.c index 8409d41405df..679b84cc8794 100644 --- a/net/core/net-sysfs.c +++ b/net/core/net-sysfs.c @@ -532,7 +532,7 @@ static ssize_t phys_port_name_show(struct device *dev, * returning early without hitting the trylock/restart below. */ if (!netdev->netdev_ops->ndo_get_phys_port_name && - !netdev->netdev_ops->ndo_get_devlink_port) + !netdev->devlink_port) return -EOPNOTSUPP; if (!rtnl_trylock()) @@ -562,7 +562,7 @@ static ssize_t phys_switch_id_show(struct device *dev, * because recurse is false when calling dev_get_port_parent_id. */ if (!netdev->netdev_ops->ndo_get_port_parent_id && - !netdev->netdev_ops->ndo_get_devlink_port) + !netdev->devlink_port) return -EOPNOTSUPP; if (!rtnl_trylock()) diff --git a/net/ethtool/ioctl.c b/net/ethtool/ioctl.c index 57e7238a4136..b6835136c53f 100644 --- a/net/ethtool/ioctl.c +++ b/net/ethtool/ioctl.c @@ -44,16 +44,9 @@ struct ethtool_devlink_compat { static struct devlink *netdev_to_devlink_get(struct net_device *dev) { - struct devlink_port *devlink_port; - - if (!dev->netdev_ops->ndo_get_devlink_port) + if (!dev->devlink_port) return NULL; - - devlink_port = dev->netdev_ops->ndo_get_devlink_port(dev); - if (!devlink_port) - return NULL; - - return devlink_try_get(devlink_port->devlink); + return devlink_try_get(dev->devlink_port->devlink); } /* From 77df1db80da384c565106321f5934967690da7dd Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Wed, 2 Nov 2022 17:02:10 +0100 Subject: [PATCH 0592/2002] net: remove unused ndo_get_devlink_port Remove ndo_get_devlink_port which is no longer used alongside with the implementations in drivers. Signed-off-by: Jiri Pirko Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/broadcom/bnxt/bnxt.c | 8 -------- .../net/ethernet/fungible/funeth/funeth_main.c | 8 -------- drivers/net/ethernet/intel/ice/ice_main.c | 15 --------------- drivers/net/ethernet/intel/ice/ice_repr.c | 9 --------- .../ethernet/marvell/prestera/prestera_devlink.c | 7 ------- .../ethernet/marvell/prestera/prestera_devlink.h | 2 -- .../net/ethernet/marvell/prestera/prestera_main.c | 1 - .../net/ethernet/mellanox/mlx5/core/en/devlink.c | 10 ---------- .../net/ethernet/mellanox/mlx5/core/en/devlink.h | 1 - drivers/net/ethernet/mellanox/mlx5/core/en_main.c | 1 - drivers/net/ethernet/mellanox/mlx5/core/en_rep.c | 10 ---------- drivers/net/ethernet/mellanox/mlxsw/minimal.c | 11 ----------- drivers/net/ethernet/mellanox/mlxsw/spectrum.c | 11 ----------- drivers/net/ethernet/mscc/ocelot_net.c | 10 ---------- drivers/net/ethernet/netronome/nfp/nfp_app.h | 2 -- drivers/net/ethernet/netronome/nfp/nfp_devlink.c | 11 ----------- .../net/ethernet/netronome/nfp/nfp_net_common.c | 2 -- drivers/net/ethernet/netronome/nfp/nfp_net_repr.c | 1 - drivers/net/ethernet/ti/am65-cpsw-nuss.c | 8 -------- drivers/net/netdevsim/netdev.c | 9 --------- include/linux/netdevice.h | 5 ----- net/dsa/slave.c | 8 -------- 22 files changed, 150 deletions(-) diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c index 9604294b67aa..03272aa48511 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c @@ -13073,13 +13073,6 @@ int bnxt_get_port_parent_id(struct net_device *dev, return 0; } -static struct devlink_port *bnxt_get_devlink_port(struct net_device *dev) -{ - struct bnxt *bp = netdev_priv(dev); - - return &bp->dl_port; -} - static const struct net_device_ops bnxt_netdev_ops = { .ndo_open = bnxt_open, .ndo_start_xmit = bnxt_start_xmit, @@ -13111,7 +13104,6 @@ static const struct net_device_ops bnxt_netdev_ops = { .ndo_xdp_xmit = bnxt_xdp_xmit, .ndo_bridge_getlink = bnxt_bridge_getlink, .ndo_bridge_setlink = bnxt_bridge_setlink, - .ndo_get_devlink_port = bnxt_get_devlink_port, }; static void bnxt_remove_one(struct pci_dev *pdev) diff --git a/drivers/net/ethernet/fungible/funeth/funeth_main.c b/drivers/net/ethernet/fungible/funeth/funeth_main.c index 208dc89f4972..b4cce30e526a 100644 --- a/drivers/net/ethernet/fungible/funeth/funeth_main.c +++ b/drivers/net/ethernet/fungible/funeth/funeth_main.c @@ -1178,13 +1178,6 @@ static int fun_xdp(struct net_device *dev, struct netdev_bpf *xdp) } } -static struct devlink_port *fun_get_devlink_port(struct net_device *netdev) -{ - struct funeth_priv *fp = netdev_priv(netdev); - - return &fp->dl_port; -} - static int fun_init_vports(struct fun_ethdev *ed, unsigned int n) { if (ed->num_vports) @@ -1350,7 +1343,6 @@ static const struct net_device_ops fun_netdev_ops = { .ndo_set_vf_vlan = fun_set_vf_vlan, .ndo_set_vf_rate = fun_set_vf_rate, .ndo_get_vf_config = fun_get_vf_config, - .ndo_get_devlink_port = fun_get_devlink_port, }; #define GSO_ENCAP_FLAGS (NETIF_F_GSO_GRE | NETIF_F_GSO_IPXIP4 | \ diff --git a/drivers/net/ethernet/intel/ice/ice_main.c b/drivers/net/ethernet/intel/ice/ice_main.c index 74d25fda11bd..a9fc89aebebe 100644 --- a/drivers/net/ethernet/intel/ice/ice_main.c +++ b/drivers/net/ethernet/intel/ice/ice_main.c @@ -298,20 +298,6 @@ static int ice_clear_promisc(struct ice_vsi *vsi, u8 promisc_m) return status; } -/** - * ice_get_devlink_port - Get devlink port from netdev - * @netdev: the netdevice structure - */ -static struct devlink_port *ice_get_devlink_port(struct net_device *netdev) -{ - struct ice_pf *pf = ice_netdev_to_pf(netdev); - - if (!ice_is_switchdev_running(pf)) - return NULL; - - return &pf->devlink_port; -} - /** * ice_vsi_sync_fltr - Update the VSI filter list to the HW * @vsi: ptr to the VSI @@ -9107,5 +9093,4 @@ static const struct net_device_ops ice_netdev_ops = { .ndo_bpf = ice_xdp, .ndo_xdp_xmit = ice_xdp_xmit, .ndo_xsk_wakeup = ice_xsk_wakeup, - .ndo_get_devlink_port = ice_get_devlink_port, }; diff --git a/drivers/net/ethernet/intel/ice/ice_repr.c b/drivers/net/ethernet/intel/ice/ice_repr.c index 663a7a0e1814..0483eb14c288 100644 --- a/drivers/net/ethernet/intel/ice/ice_repr.c +++ b/drivers/net/ethernet/intel/ice/ice_repr.c @@ -134,14 +134,6 @@ static int ice_repr_stop(struct net_device *netdev) return 0; } -static struct devlink_port * -ice_repr_get_devlink_port(struct net_device *netdev) -{ - struct ice_repr *repr = ice_netdev_to_repr(netdev); - - return &repr->vf->devlink_port; -} - /** * ice_repr_sp_stats64 - get slow path stats for port representor * @dev: network interface device structure @@ -250,7 +242,6 @@ static const struct net_device_ops ice_repr_netdev_ops = { .ndo_open = ice_repr_open, .ndo_stop = ice_repr_stop, .ndo_start_xmit = ice_eswitch_port_start_xmit, - .ndo_get_devlink_port = ice_repr_get_devlink_port, .ndo_setup_tc = ice_repr_setup_tc, .ndo_has_offload_stats = ice_repr_ndo_has_offload_stats, .ndo_get_offload_stats = ice_repr_ndo_get_offload_stats, diff --git a/drivers/net/ethernet/marvell/prestera/prestera_devlink.c b/drivers/net/ethernet/marvell/prestera/prestera_devlink.c index 637b8fee65e7..84ad05c9f12d 100644 --- a/drivers/net/ethernet/marvell/prestera/prestera_devlink.c +++ b/drivers/net/ethernet/marvell/prestera/prestera_devlink.c @@ -445,13 +445,6 @@ void prestera_devlink_port_unregister(struct prestera_port *port) devlink_port_unregister(&port->dl_port); } -struct devlink_port *prestera_devlink_get_port(struct net_device *dev) -{ - struct prestera_port *port = netdev_priv(dev); - - return &port->dl_port; -} - int prestera_devlink_traps_register(struct prestera_switch *sw) { const u32 groups_count = ARRAY_SIZE(prestera_trap_groups_arr); diff --git a/drivers/net/ethernet/marvell/prestera/prestera_devlink.h b/drivers/net/ethernet/marvell/prestera/prestera_devlink.h index 04e8556f748a..bf84ad6fd87e 100644 --- a/drivers/net/ethernet/marvell/prestera/prestera_devlink.h +++ b/drivers/net/ethernet/marvell/prestera/prestera_devlink.h @@ -15,8 +15,6 @@ void prestera_devlink_unregister(struct prestera_switch *sw); int prestera_devlink_port_register(struct prestera_port *port); void prestera_devlink_port_unregister(struct prestera_port *port); -struct devlink_port *prestera_devlink_get_port(struct net_device *dev); - void prestera_devlink_trap_report(struct prestera_port *port, struct sk_buff *skb, u8 cpu_code); int prestera_devlink_traps_register(struct prestera_switch *sw); diff --git a/drivers/net/ethernet/marvell/prestera/prestera_main.c b/drivers/net/ethernet/marvell/prestera/prestera_main.c index a76bf3606aa6..edbdda8f958d 100644 --- a/drivers/net/ethernet/marvell/prestera/prestera_main.c +++ b/drivers/net/ethernet/marvell/prestera/prestera_main.c @@ -569,7 +569,6 @@ static const struct net_device_ops prestera_netdev_ops = { .ndo_change_mtu = prestera_port_change_mtu, .ndo_get_stats64 = prestera_port_get_stats64, .ndo_set_mac_address = prestera_port_set_mac_address, - .ndo_get_devlink_port = prestera_devlink_get_port, }; int prestera_port_autoneg_set(struct prestera_port *port, u64 link_modes) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/devlink.c b/drivers/net/ethernet/mellanox/mlx5/core/en/devlink.c index ce0e56f856d6..83adaabf59f5 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/devlink.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/devlink.c @@ -62,13 +62,3 @@ void mlx5e_devlink_port_unregister(struct mlx5e_priv *priv) if (!(priv->mdev->priv.flags & MLX5_PRIV_FLAGS_MLX5E_LOCKED_FLOW)) devl_unlock(devlink); } - -struct devlink_port *mlx5e_get_devlink_port(struct net_device *dev) -{ - struct mlx5e_priv *priv = netdev_priv(dev); - - if (!netif_device_present(dev)) - return NULL; - - return mlx5e_devlink_get_dl_port(priv); -} diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/devlink.h b/drivers/net/ethernet/mellanox/mlx5/core/en/devlink.h index 1c203257ac30..4f238d4fff55 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/devlink.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/devlink.h @@ -9,7 +9,6 @@ int mlx5e_devlink_port_register(struct mlx5e_priv *priv); void mlx5e_devlink_port_unregister(struct mlx5e_priv *priv); -struct devlink_port *mlx5e_get_devlink_port(struct net_device *dev); static inline struct devlink_port * mlx5e_devlink_get_dl_port(struct mlx5e_priv *priv) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c index 97e876472a06..78d34c039fa7 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c @@ -4897,7 +4897,6 @@ const struct net_device_ops mlx5e_netdev_ops = { .ndo_has_offload_stats = mlx5e_has_offload_stats, .ndo_get_offload_stats = mlx5e_get_offload_stats, #endif - .ndo_get_devlink_port = mlx5e_get_devlink_port, }; static u32 mlx5e_choose_lro_timeout(struct mlx5_core_dev *mdev, u32 wanted_timeout) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c b/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c index 5412633b9c8c..1b53e8852c86 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c @@ -607,15 +607,6 @@ static int mlx5e_rep_change_mtu(struct net_device *netdev, int new_mtu) return mlx5e_change_mtu(netdev, new_mtu, NULL); } -static struct devlink_port *mlx5e_rep_get_devlink_port(struct net_device *netdev) -{ - struct mlx5e_priv *priv = netdev_priv(netdev); - struct mlx5e_rep_priv *rpriv = priv->ppriv; - struct mlx5_core_dev *dev = priv->mdev; - - return mlx5_esw_offloads_devlink_port(dev->priv.eswitch, rpriv->rep->vport); -} - static int mlx5e_rep_change_carrier(struct net_device *dev, bool new_carrier) { struct mlx5e_priv *priv = netdev_priv(dev); @@ -644,7 +635,6 @@ static const struct net_device_ops mlx5e_netdev_ops_rep = { .ndo_stop = mlx5e_rep_close, .ndo_start_xmit = mlx5e_xmit, .ndo_setup_tc = mlx5e_rep_setup_tc, - .ndo_get_devlink_port = mlx5e_rep_get_devlink_port, .ndo_get_stats64 = mlx5e_rep_get_stats, .ndo_has_offload_stats = mlx5e_rep_has_offload_stats, .ndo_get_offload_stats = mlx5e_rep_get_offload_stats, diff --git a/drivers/net/ethernet/mellanox/mlxsw/minimal.c b/drivers/net/ethernet/mellanox/mlxsw/minimal.c index 177cf7e4db34..6b56eadd736e 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/minimal.c +++ b/drivers/net/ethernet/mellanox/mlxsw/minimal.c @@ -81,20 +81,9 @@ static int mlxsw_m_port_stop(struct net_device *dev) return 0; } -static struct devlink_port * -mlxsw_m_port_get_devlink_port(struct net_device *dev) -{ - struct mlxsw_m_port *mlxsw_m_port = netdev_priv(dev); - struct mlxsw_m *mlxsw_m = mlxsw_m_port->mlxsw_m; - - return mlxsw_core_port_devlink_port_get(mlxsw_m->core, - mlxsw_m_port->local_port); -} - static const struct net_device_ops mlxsw_m_port_netdev_ops = { .ndo_open = mlxsw_m_port_open, .ndo_stop = mlxsw_m_port_stop, - .ndo_get_devlink_port = mlxsw_m_port_get_devlink_port, }; static void mlxsw_m_module_get_drvinfo(struct net_device *dev, diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c index b13739e1f8f3..04dc79da6024 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c @@ -1259,16 +1259,6 @@ static int mlxsw_sp_set_features(struct net_device *dev, return 0; } -static struct devlink_port * -mlxsw_sp_port_get_devlink_port(struct net_device *dev) -{ - struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev); - struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; - - return mlxsw_core_port_devlink_port_get(mlxsw_sp->core, - mlxsw_sp_port->local_port); -} - static int mlxsw_sp_port_hwtstamp_set(struct mlxsw_sp_port *mlxsw_sp_port, struct ifreq *ifr) { @@ -1342,7 +1332,6 @@ static const struct net_device_ops mlxsw_sp_port_netdev_ops = { .ndo_vlan_rx_add_vid = mlxsw_sp_port_add_vid, .ndo_vlan_rx_kill_vid = mlxsw_sp_port_kill_vid, .ndo_set_features = mlxsw_sp_set_features, - .ndo_get_devlink_port = mlxsw_sp_port_get_devlink_port, .ndo_eth_ioctl = mlxsw_sp_port_ioctl, }; diff --git a/drivers/net/ethernet/mscc/ocelot_net.c b/drivers/net/ethernet/mscc/ocelot_net.c index 5efc07751c8d..f50dada2bb8e 100644 --- a/drivers/net/ethernet/mscc/ocelot_net.c +++ b/drivers/net/ethernet/mscc/ocelot_net.c @@ -194,15 +194,6 @@ void ocelot_port_devlink_teardown(struct ocelot *ocelot, int port) devlink_port_unregister(dlp); } -static struct devlink_port *ocelot_get_devlink_port(struct net_device *dev) -{ - struct ocelot_port_private *priv = netdev_priv(dev); - struct ocelot *ocelot = priv->port.ocelot; - int port = priv->port.index; - - return &ocelot->devlink_ports[port]; -} - int ocelot_setup_tc_cls_flower(struct ocelot_port_private *priv, struct flow_cls_offload *f, bool ingress) @@ -925,7 +916,6 @@ static const struct net_device_ops ocelot_port_netdev_ops = { .ndo_set_features = ocelot_set_features, .ndo_setup_tc = ocelot_setup_tc, .ndo_eth_ioctl = ocelot_ioctl, - .ndo_get_devlink_port = ocelot_get_devlink_port, }; struct net_device *ocelot_port_to_netdev(struct ocelot *ocelot, int port) diff --git a/drivers/net/ethernet/netronome/nfp/nfp_app.h b/drivers/net/ethernet/netronome/nfp/nfp_app.h index dd56207df246..90707346a4ef 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_app.h +++ b/drivers/net/ethernet/netronome/nfp/nfp_app.h @@ -445,6 +445,4 @@ int nfp_app_nic_vnic_alloc(struct nfp_app *app, struct nfp_net *nn, int nfp_app_nic_vnic_init_phy_port(struct nfp_pf *pf, struct nfp_app *app, struct nfp_net *nn, unsigned int id); -struct devlink_port *nfp_devlink_get_devlink_port(struct net_device *netdev); - #endif diff --git a/drivers/net/ethernet/netronome/nfp/nfp_devlink.c b/drivers/net/ethernet/netronome/nfp/nfp_devlink.c index f3f0f11d8b52..8bfd48d50ef0 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_devlink.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_devlink.c @@ -362,14 +362,3 @@ void nfp_devlink_port_unregister(struct nfp_port *port) { devl_port_unregister(&port->dl_port); } - -struct devlink_port *nfp_devlink_get_devlink_port(struct net_device *netdev) -{ - struct nfp_port *port; - - port = nfp_port_from_netdev(netdev); - if (!port) - return NULL; - - return &port->dl_port; -} diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c index a5ca5c4a7896..8c1a870bc0e5 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c @@ -2013,7 +2013,6 @@ const struct net_device_ops nfp_nfd3_netdev_ops = { .ndo_get_phys_port_name = nfp_net_get_phys_port_name, .ndo_bpf = nfp_net_xdp, .ndo_xsk_wakeup = nfp_net_xsk_wakeup, - .ndo_get_devlink_port = nfp_devlink_get_devlink_port, .ndo_bridge_getlink = nfp_net_bridge_getlink, .ndo_bridge_setlink = nfp_net_bridge_setlink, }; @@ -2044,7 +2043,6 @@ const struct net_device_ops nfp_nfdk_netdev_ops = { .ndo_features_check = nfp_net_features_check, .ndo_get_phys_port_name = nfp_net_get_phys_port_name, .ndo_bpf = nfp_net_xdp, - .ndo_get_devlink_port = nfp_devlink_get_devlink_port, .ndo_bridge_getlink = nfp_net_bridge_getlink, .ndo_bridge_setlink = nfp_net_bridge_setlink, }; diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_repr.c b/drivers/net/ethernet/netronome/nfp/nfp_net_repr.c index a6b6ca1fd55e..3af1229a3f08 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_repr.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_repr.c @@ -275,7 +275,6 @@ const struct net_device_ops nfp_repr_netdev_ops = { .ndo_set_features = nfp_port_set_features, .ndo_set_mac_address = eth_mac_addr, .ndo_get_port_parent_id = nfp_port_get_port_parent_id, - .ndo_get_devlink_port = nfp_devlink_get_devlink_port, }; void diff --git a/drivers/net/ethernet/ti/am65-cpsw-nuss.c b/drivers/net/ethernet/ti/am65-cpsw-nuss.c index b2a08f3f0e93..0dbdce4dc077 100644 --- a/drivers/net/ethernet/ti/am65-cpsw-nuss.c +++ b/drivers/net/ethernet/ti/am65-cpsw-nuss.c @@ -1380,13 +1380,6 @@ static void am65_cpsw_nuss_ndo_get_stats(struct net_device *dev, stats->tx_dropped = dev->stats.tx_dropped; } -static struct devlink_port *am65_cpsw_ndo_get_devlink_port(struct net_device *ndev) -{ - struct am65_cpsw_port *port = am65_ndev_to_port(ndev); - - return &port->devlink_port; -} - static const struct net_device_ops am65_cpsw_nuss_netdev_ops = { .ndo_open = am65_cpsw_nuss_ndo_slave_open, .ndo_stop = am65_cpsw_nuss_ndo_slave_stop, @@ -1400,7 +1393,6 @@ static const struct net_device_ops am65_cpsw_nuss_netdev_ops = { .ndo_vlan_rx_kill_vid = am65_cpsw_nuss_ndo_slave_kill_vid, .ndo_eth_ioctl = am65_cpsw_nuss_ndo_slave_ioctl, .ndo_setup_tc = am65_cpsw_qos_ndo_setup_tc, - .ndo_get_devlink_port = am65_cpsw_ndo_get_devlink_port, }; static void am65_cpsw_nuss_mac_config(struct phylink_config *config, unsigned int mode, diff --git a/drivers/net/netdevsim/netdev.c b/drivers/net/netdevsim/netdev.c index e7cbae743e86..6db6a75ff9b9 100644 --- a/drivers/net/netdevsim/netdev.c +++ b/drivers/net/netdevsim/netdev.c @@ -238,13 +238,6 @@ nsim_set_features(struct net_device *dev, netdev_features_t features) return 0; } -static struct devlink_port *nsim_get_devlink_port(struct net_device *dev) -{ - struct netdevsim *ns = netdev_priv(dev); - - return &ns->nsim_dev_port->devlink_port; -} - static const struct net_device_ops nsim_netdev_ops = { .ndo_start_xmit = nsim_start_xmit, .ndo_set_rx_mode = nsim_set_rx_mode, @@ -263,7 +256,6 @@ static const struct net_device_ops nsim_netdev_ops = { .ndo_setup_tc = nsim_setup_tc, .ndo_set_features = nsim_set_features, .ndo_bpf = nsim_bpf, - .ndo_get_devlink_port = nsim_get_devlink_port, }; static const struct net_device_ops nsim_vf_netdev_ops = { @@ -275,7 +267,6 @@ static const struct net_device_ops nsim_vf_netdev_ops = { .ndo_get_stats64 = nsim_get_stats64, .ndo_setup_tc = nsim_setup_tc, .ndo_set_features = nsim_set_features, - .ndo_get_devlink_port = nsim_get_devlink_port, }; static void nsim_setup(struct net_device *dev) diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index f048a30ea10b..d45713a06568 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -1366,10 +1366,6 @@ struct netdev_net_notifier { * queue id bound to an AF_XDP socket. The flags field specifies if * only RX, only Tx, or both should be woken up using the flags * XDP_WAKEUP_RX and XDP_WAKEUP_TX. - * struct devlink_port *(*ndo_get_devlink_port)(struct net_device *dev); - * Get devlink port instance associated with a given netdev. - * Called with a reference on the netdevice and devlink locks only, - * rtnl_lock is not held. * int (*ndo_tunnel_ctl)(struct net_device *dev, struct ip_tunnel_parm *p, * int cmd); * Add, change, delete or get information on an IPv4 tunnel. @@ -1600,7 +1596,6 @@ struct net_device_ops { struct xdp_buff *xdp); int (*ndo_xsk_wakeup)(struct net_device *dev, u32 queue_id, u32 flags); - struct devlink_port * (*ndo_get_devlink_port)(struct net_device *dev); int (*ndo_tunnel_ctl)(struct net_device *dev, struct ip_tunnel_parm *p, int cmd); struct net_device * (*ndo_get_peer_dev)(struct net_device *dev); diff --git a/net/dsa/slave.c b/net/dsa/slave.c index 158bb3f1aa0b..4176482cd03f 100644 --- a/net/dsa/slave.c +++ b/net/dsa/slave.c @@ -2165,13 +2165,6 @@ static const struct dcbnl_rtnl_ops __maybe_unused dsa_slave_dcbnl_ops = { .ieee_delapp = dsa_slave_dcbnl_ieee_delapp, }; -static struct devlink_port *dsa_slave_get_devlink_port(struct net_device *dev) -{ - struct dsa_port *dp = dsa_slave_to_port(dev); - - return &dp->devlink_port; -} - static void dsa_slave_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *s) { @@ -2219,7 +2212,6 @@ static const struct net_device_ops dsa_slave_netdev_ops = { .ndo_get_stats64 = dsa_slave_get_stats64, .ndo_vlan_rx_add_vid = dsa_slave_vlan_rx_add_vid, .ndo_vlan_rx_kill_vid = dsa_slave_vlan_rx_kill_vid, - .ndo_get_devlink_port = dsa_slave_get_devlink_port, .ndo_change_mtu = dsa_slave_change_mtu, .ndo_fill_forward_path = dsa_slave_fill_forward_path, }; From dca56c3038c34a3e5acfe0aadb1f2bc9d724ae79 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Wed, 2 Nov 2022 17:02:11 +0100 Subject: [PATCH 0593/2002] net: expose devlink port over rtnetlink Expose devlink port handle related to netdev over rtnetlink. Introduce a new nested IFLA attribute to carry the info. Call into devlink code to fill-up the nest with existing devlink attributes that are used over devlink netlink. Signed-off-by: Jiri Pirko Signed-off-by: Jakub Kicinski --- include/net/devlink.h | 14 +++++++++++++ include/uapi/linux/if_link.h | 2 ++ net/core/devlink.c | 18 +++++++++++++++++ net/core/rtnetlink.c | 39 ++++++++++++++++++++++++++++++++++++ 4 files changed, 73 insertions(+) diff --git a/include/net/devlink.h b/include/net/devlink.h index 7befad57afd4..fa6e936af1a5 100644 --- a/include/net/devlink.h +++ b/include/net/devlink.h @@ -1873,6 +1873,9 @@ int devlink_compat_phys_port_name_get(struct net_device *dev, int devlink_compat_switch_id_get(struct net_device *dev, struct netdev_phys_item_id *ppid); +int devlink_nl_port_handle_fill(struct sk_buff *msg, struct devlink_port *devlink_port); +size_t devlink_nl_port_handle_size(struct devlink_port *devlink_port); + #else static inline struct devlink *devlink_try_get(struct devlink *devlink) @@ -1909,6 +1912,17 @@ devlink_compat_switch_id_get(struct net_device *dev, return -EOPNOTSUPP; } +static inline int +devlink_nl_port_handle_fill(struct sk_buff *msg, struct devlink_port *devlink_port) +{ + return 0; +} + +static inline size_t devlink_nl_port_handle_size(struct devlink_port *devlink_port) +{ + return 0; +} + #endif #endif /* _NET_DEVLINK_H_ */ diff --git a/include/uapi/linux/if_link.h b/include/uapi/linux/if_link.h index d92b3f79eba3..1021a7e47a86 100644 --- a/include/uapi/linux/if_link.h +++ b/include/uapi/linux/if_link.h @@ -372,6 +372,8 @@ enum { IFLA_TSO_MAX_SEGS, IFLA_ALLMULTI, /* Allmulti count: > 0 means acts ALLMULTI */ + IFLA_DEVLINK_PORT, + __IFLA_MAX }; diff --git a/net/core/devlink.c b/net/core/devlink.c index 3a454d0045e5..2dcf2bcc3527 100644 --- a/net/core/devlink.c +++ b/net/core/devlink.c @@ -880,6 +880,24 @@ nla_put_failure: return -EMSGSIZE; } +int devlink_nl_port_handle_fill(struct sk_buff *msg, struct devlink_port *devlink_port) +{ + if (devlink_nl_put_handle(msg, devlink_port->devlink)) + return -EMSGSIZE; + if (nla_put_u32(msg, DEVLINK_ATTR_PORT_INDEX, devlink_port->index)) + return -EMSGSIZE; + return 0; +} + +size_t devlink_nl_port_handle_size(struct devlink_port *devlink_port) +{ + struct devlink *devlink = devlink_port->devlink; + + return nla_total_size(strlen(devlink->dev->bus->name) + 1) /* DEVLINK_ATTR_BUS_NAME */ + + nla_total_size(strlen(dev_name(devlink->dev)) + 1) /* DEVLINK_ATTR_DEV_NAME */ + + nla_total_size(4); /* DEVLINK_ATTR_PORT_INDEX */ +} + struct devlink_reload_combination { enum devlink_reload_action action; enum devlink_reload_limit limit; diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index b64fffeb3844..64289bc98887 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -53,6 +53,7 @@ #include #include #include +#include #include "dev.h" @@ -1038,6 +1039,16 @@ static size_t rtnl_proto_down_size(const struct net_device *dev) return size; } +static size_t rtnl_devlink_port_size(const struct net_device *dev) +{ + size_t size = nla_total_size(0); /* nest IFLA_DEVLINK_PORT */ + + if (dev->devlink_port) + size += devlink_nl_port_handle_size(dev->devlink_port); + + return size; +} + static noinline size_t if_nlmsg_size(const struct net_device *dev, u32 ext_filter_mask) { @@ -1091,6 +1102,7 @@ static noinline size_t if_nlmsg_size(const struct net_device *dev, + nla_total_size(4) /* IFLA_MAX_MTU */ + rtnl_prop_list_size(dev) + nla_total_size(MAX_ADDR_LEN) /* IFLA_PERM_ADDRESS */ + + rtnl_devlink_port_size(dev) + 0; } @@ -1728,6 +1740,30 @@ nla_put_failure: return -EMSGSIZE; } +static int rtnl_fill_devlink_port(struct sk_buff *skb, + const struct net_device *dev) +{ + struct nlattr *devlink_port_nest; + int ret; + + devlink_port_nest = nla_nest_start(skb, IFLA_DEVLINK_PORT); + if (!devlink_port_nest) + return -EMSGSIZE; + + if (dev->devlink_port) { + ret = devlink_nl_port_handle_fill(skb, dev->devlink_port); + if (ret < 0) + goto nest_cancel; + } + + nla_nest_end(skb, devlink_port_nest); + return 0; + +nest_cancel: + nla_nest_cancel(skb, devlink_port_nest); + return ret; +} + static int rtnl_fill_ifinfo(struct sk_buff *skb, struct net_device *dev, struct net *src_net, int type, u32 pid, u32 seq, u32 change, @@ -1865,6 +1901,9 @@ static int rtnl_fill_ifinfo(struct sk_buff *skb, dev->dev.parent->bus->name)) goto nla_put_failure; + if (rtnl_fill_devlink_port(skb, dev)) + goto nla_put_failure; + nlmsg_end(skb, nlh); return 0; From c2cce3a6e8ebe0e694e3380f8e60cd20bf21df82 Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Wed, 2 Nov 2022 07:36:23 +0100 Subject: [PATCH 0594/2002] net: usb: Use kstrtobool() instead of strtobool() strtobool() is the same as kstrtobool(). However, the latter is more used within the kernel. In order to remove strtobool() and slightly simplify kstrtox.h, switch to the other function name. While at it, include the corresponding header file (). Signed-off-by: Christophe JAILLET Link: https://lore.kernel.org/r/d4432a67b6f769cac0a9ec910ac725298b64e102.1667336095.git.christophe.jaillet@wanadoo.fr Signed-off-by: Jakub Kicinski --- drivers/net/usb/cdc_ncm.c | 3 ++- drivers/net/usb/qmi_wwan.c | 5 +++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/net/usb/cdc_ncm.c b/drivers/net/usb/cdc_ncm.c index 8d5cbda33f66..6b5f24f28dd1 100644 --- a/drivers/net/usb/cdc_ncm.c +++ b/drivers/net/usb/cdc_ncm.c @@ -43,6 +43,7 @@ #include #include #include +#include #include #include #include @@ -318,7 +319,7 @@ static ssize_t ndp_to_end_store(struct device *d, struct device_attribute *attr struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0]; bool enable; - if (strtobool(buf, &enable)) + if (kstrtobool(buf, &enable)) return -EINVAL; /* no change? */ diff --git a/drivers/net/usb/qmi_wwan.c b/drivers/net/usb/qmi_wwan.c index 26c34a7c21bd..30d733c81ed8 100644 --- a/drivers/net/usb/qmi_wwan.c +++ b/drivers/net/usb/qmi_wwan.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -343,7 +344,7 @@ static ssize_t raw_ip_store(struct device *d, struct device_attribute *attr, co bool enable; int ret; - if (strtobool(buf, &enable)) + if (kstrtobool(buf, &enable)) return -EINVAL; /* no change? */ @@ -492,7 +493,7 @@ static ssize_t pass_through_store(struct device *d, struct qmi_wwan_state *info; bool enable; - if (strtobool(buf, &enable)) + if (kstrtobool(buf, &enable)) return -EINVAL; info = (void *)&dev->data; From 63fe6ff674a96cfcfc0fa8df1051a27aa31c70b4 Mon Sep 17 00:00:00 2001 From: Nathan Chancellor Date: Wed, 2 Nov 2022 09:09:33 -0700 Subject: [PATCH 0595/2002] net: ethernet: ti: Fix return type of netcp_ndo_start_xmit() With clang's kernel control flow integrity (kCFI, CONFIG_CFI_CLANG), indirect call targets are validated against the expected function pointer prototype to make sure the call target is valid to help mitigate ROP attacks. If they are not identical, there is a failure at run time, which manifests as either a kernel panic or thread getting killed. A proposed warning in clang aims to catch these at compile time, which reveals: drivers/net/ethernet/ti/netcp_core.c:1944:21: error: incompatible function pointer types initializing 'netdev_tx_t (*)(struct sk_buff *, struct net_device *)' (aka 'enum netdev_tx (*)(struct sk_buff *, struct net_device *)') with an expression of type 'int (struct sk_buff *, struct net_device *)' [-Werror,-Wincompatible-function-pointer-types-strict] .ndo_start_xmit = netcp_ndo_start_xmit, ^~~~~~~~~~~~~~~~~~~~ 1 error generated. ->ndo_start_xmit() in 'struct net_device_ops' expects a return type of 'netdev_tx_t', not 'int'. Adjust the return type of netcp_ndo_start_xmit() to match the prototype's to resolve the warning and CFI failure. Link: https://github.com/ClangBuiltLinux/linux/issues/1750 Signed-off-by: Nathan Chancellor Reviewed-by: Kees Cook Link: https://lore.kernel.org/r/20221102160933.1601260-1-nathan@kernel.org Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/ti/netcp_core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/ti/netcp_core.c b/drivers/net/ethernet/ti/netcp_core.c index 8b776f9cdb3f..1bb596a9d8a2 100644 --- a/drivers/net/ethernet/ti/netcp_core.c +++ b/drivers/net/ethernet/ti/netcp_core.c @@ -1261,7 +1261,7 @@ out: } /* Submit the packet */ -static int netcp_ndo_start_xmit(struct sk_buff *skb, struct net_device *ndev) +static netdev_tx_t netcp_ndo_start_xmit(struct sk_buff *skb, struct net_device *ndev) { struct netcp_intf *netcp = netdev_priv(ndev); struct netcp_stats *tx_stats = &netcp->stats; From c5733e5b15d91ab679646ec3149e192996a27d5d Mon Sep 17 00:00:00 2001 From: Nathan Chancellor Date: Wed, 2 Nov 2022 09:06:10 -0700 Subject: [PATCH 0596/2002] hamradio: baycom_epp: Fix return type of baycom_send_packet() With clang's kernel control flow integrity (kCFI, CONFIG_CFI_CLANG), indirect call targets are validated against the expected function pointer prototype to make sure the call target is valid to help mitigate ROP attacks. If they are not identical, there is a failure at run time, which manifests as either a kernel panic or thread getting killed. A proposed warning in clang aims to catch these at compile time, which reveals: drivers/net/hamradio/baycom_epp.c:1119:25: error: incompatible function pointer types initializing 'netdev_tx_t (*)(struct sk_buff *, struct net_device *)' (aka 'enum netdev_tx (*)(struct sk_buff *, struct net_device *)') with an expression of type 'int (struct sk_buff *, struct net_device *)' [-Werror,-Wincompatible-function-pointer-types-strict] .ndo_start_xmit = baycom_send_packet, ^~~~~~~~~~~~~~~~~~ 1 error generated. ->ndo_start_xmit() in 'struct net_device_ops' expects a return type of 'netdev_tx_t', not 'int'. Adjust the return type of baycom_send_packet() to match the prototype's to resolve the warning and CFI failure. Link: https://github.com/ClangBuiltLinux/linux/issues/1750 Signed-off-by: Nathan Chancellor Reviewed-by: Kees Cook Link: https://lore.kernel.org/r/20221102160610.1186145-1-nathan@kernel.org Signed-off-by: Jakub Kicinski --- drivers/net/hamradio/baycom_epp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/hamradio/baycom_epp.c b/drivers/net/hamradio/baycom_epp.c index 791b4a53d69f..bd3b0c2655a2 100644 --- a/drivers/net/hamradio/baycom_epp.c +++ b/drivers/net/hamradio/baycom_epp.c @@ -758,7 +758,7 @@ static void epp_bh(struct work_struct *work) * ===================== network driver interface ========================= */ -static int baycom_send_packet(struct sk_buff *skb, struct net_device *dev) +static netdev_tx_t baycom_send_packet(struct sk_buff *skb, struct net_device *dev) { struct baycom_state *bc = netdev_priv(dev); From aa3496accc412b3d975e4ee5d06076d73394d8b5 Mon Sep 17 00:00:00 2001 From: Kumar Kartikeya Dwivedi Date: Fri, 4 Nov 2022 00:39:55 +0530 Subject: [PATCH 0597/2002] bpf: Refactor kptr_off_tab into btf_record To prepare the BPF verifier to handle special fields in both map values and program allocated types coming from program BTF, we need to refactor the kptr_off_tab handling code into something more generic and reusable across both cases to avoid code duplication. Later patches also require passing this data to helpers at runtime, so that they can work on user defined types, initialize them, destruct them, etc. The main observation is that both map values and such allocated types point to a type in program BTF, hence they can be handled similarly. We can prepare a field metadata table for both cases and store them in struct bpf_map or struct btf depending on the use case. Hence, refactor the code into generic btf_record and btf_field member structs. The btf_record represents the fields of a specific btf_type in user BTF. The cnt indicates the number of special fields we successfully recognized, and field_mask is a bitmask of fields that were found, to enable quick determination of availability of a certain field. Subsequently, refactor the rest of the code to work with these generic types, remove assumptions about kptr and kptr_off_tab, rename variables to more meaningful names, etc. Signed-off-by: Kumar Kartikeya Dwivedi Link: https://lore.kernel.org/r/20221103191013.1236066-7-memxor@gmail.com Signed-off-by: Alexei Starovoitov --- include/linux/bpf.h | 125 ++++++++++------ include/linux/btf.h | 3 +- kernel/bpf/arraymap.c | 13 +- kernel/bpf/btf.c | 67 ++++----- kernel/bpf/hashtab.c | 14 +- kernel/bpf/map_in_map.c | 14 +- kernel/bpf/syscall.c | 307 ++++++++++++++++++++++------------------ kernel/bpf/verifier.c | 94 ++++++------ 8 files changed, 353 insertions(+), 284 deletions(-) diff --git a/include/linux/bpf.h b/include/linux/bpf.h index 8d948bfcb984..5f2a42033a37 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -165,35 +165,41 @@ struct bpf_map_ops { }; enum { - /* Support at most 8 pointers in a BPF map value */ - BPF_MAP_VALUE_OFF_MAX = 8, - BPF_MAP_OFF_ARR_MAX = BPF_MAP_VALUE_OFF_MAX + + /* Support at most 8 pointers in a BTF type */ + BTF_FIELDS_MAX = 8, + BPF_MAP_OFF_ARR_MAX = BTF_FIELDS_MAX + 1 + /* for bpf_spin_lock */ 1, /* for bpf_timer */ }; -enum bpf_kptr_type { - BPF_KPTR_UNREF, - BPF_KPTR_REF, +enum btf_field_type { + BPF_KPTR_UNREF = (1 << 2), + BPF_KPTR_REF = (1 << 3), + BPF_KPTR = BPF_KPTR_UNREF | BPF_KPTR_REF, }; -struct bpf_map_value_off_desc { +struct btf_field_kptr { + struct btf *btf; + struct module *module; + btf_dtor_kfunc_t dtor; + u32 btf_id; +}; + +struct btf_field { u32 offset; - enum bpf_kptr_type type; - struct { - struct btf *btf; - struct module *module; - btf_dtor_kfunc_t dtor; - u32 btf_id; - } kptr; + enum btf_field_type type; + union { + struct btf_field_kptr kptr; + }; }; -struct bpf_map_value_off { - u32 nr_off; - struct bpf_map_value_off_desc off[]; +struct btf_record { + u32 cnt; + u32 field_mask; + struct btf_field fields[]; }; -struct bpf_map_off_arr { +struct btf_field_offs { u32 cnt; u32 field_off[BPF_MAP_OFF_ARR_MAX]; u8 field_sz[BPF_MAP_OFF_ARR_MAX]; @@ -215,7 +221,7 @@ struct bpf_map { u64 map_extra; /* any per-map-type extra fields */ u32 map_flags; int spin_lock_off; /* >=0 valid offset, <0 error */ - struct bpf_map_value_off *kptr_off_tab; + struct btf_record *record; int timer_off; /* >=0 valid offset, <0 error */ u32 id; int numa_node; @@ -227,7 +233,7 @@ struct bpf_map { struct obj_cgroup *objcg; #endif char name[BPF_OBJ_NAME_LEN]; - struct bpf_map_off_arr *off_arr; + struct btf_field_offs *field_offs; /* The 3rd and 4th cacheline with misc members to avoid false sharing * particularly with refcounting. */ @@ -251,6 +257,37 @@ struct bpf_map { bool frozen; /* write-once; write-protected by freeze_mutex */ }; +static inline u32 btf_field_type_size(enum btf_field_type type) +{ + switch (type) { + case BPF_KPTR_UNREF: + case BPF_KPTR_REF: + return sizeof(u64); + default: + WARN_ON_ONCE(1); + return 0; + } +} + +static inline u32 btf_field_type_align(enum btf_field_type type) +{ + switch (type) { + case BPF_KPTR_UNREF: + case BPF_KPTR_REF: + return __alignof__(u64); + default: + WARN_ON_ONCE(1); + return 0; + } +} + +static inline bool btf_record_has_field(const struct btf_record *rec, enum btf_field_type type) +{ + if (IS_ERR_OR_NULL(rec)) + return false; + return rec->field_mask & type; +} + static inline bool map_value_has_spin_lock(const struct bpf_map *map) { return map->spin_lock_off >= 0; @@ -261,23 +298,19 @@ static inline bool map_value_has_timer(const struct bpf_map *map) return map->timer_off >= 0; } -static inline bool map_value_has_kptrs(const struct bpf_map *map) -{ - return !IS_ERR_OR_NULL(map->kptr_off_tab); -} - static inline void check_and_init_map_value(struct bpf_map *map, void *dst) { if (unlikely(map_value_has_spin_lock(map))) memset(dst + map->spin_lock_off, 0, sizeof(struct bpf_spin_lock)); if (unlikely(map_value_has_timer(map))) memset(dst + map->timer_off, 0, sizeof(struct bpf_timer)); - if (unlikely(map_value_has_kptrs(map))) { - struct bpf_map_value_off *tab = map->kptr_off_tab; + if (!IS_ERR_OR_NULL(map->record)) { + struct btf_field *fields = map->record->fields; + u32 cnt = map->record->cnt; int i; - for (i = 0; i < tab->nr_off; i++) - *(u64 *)(dst + tab->off[i].offset) = 0; + for (i = 0; i < cnt; i++) + memset(dst + fields[i].offset, 0, btf_field_type_size(fields[i].type)); } } @@ -303,7 +336,7 @@ static inline void __copy_map_value(struct bpf_map *map, void *dst, void *src, b u32 curr_off = 0; int i; - if (likely(!map->off_arr)) { + if (likely(!map->field_offs)) { if (long_memcpy) bpf_long_memcpy(dst, src, round_up(map->value_size, 8)); else @@ -311,11 +344,12 @@ static inline void __copy_map_value(struct bpf_map *map, void *dst, void *src, b return; } - for (i = 0; i < map->off_arr->cnt; i++) { - u32 next_off = map->off_arr->field_off[i]; + for (i = 0; i < map->field_offs->cnt; i++) { + u32 next_off = map->field_offs->field_off[i]; + u32 sz = next_off - curr_off; - memcpy(dst + curr_off, src + curr_off, next_off - curr_off); - curr_off += map->off_arr->field_sz[i]; + memcpy(dst + curr_off, src + curr_off, sz); + curr_off += map->field_offs->field_sz[i]; } memcpy(dst + curr_off, src + curr_off, map->value_size - curr_off); } @@ -335,16 +369,17 @@ static inline void zero_map_value(struct bpf_map *map, void *dst) u32 curr_off = 0; int i; - if (likely(!map->off_arr)) { + if (likely(!map->field_offs)) { memset(dst, 0, map->value_size); return; } - for (i = 0; i < map->off_arr->cnt; i++) { - u32 next_off = map->off_arr->field_off[i]; + for (i = 0; i < map->field_offs->cnt; i++) { + u32 next_off = map->field_offs->field_off[i]; + u32 sz = next_off - curr_off; - memset(dst + curr_off, 0, next_off - curr_off); - curr_off += map->off_arr->field_sz[i]; + memset(dst + curr_off, 0, sz); + curr_off += map->field_offs->field_sz[i]; } memset(dst + curr_off, 0, map->value_size - curr_off); } @@ -1699,11 +1734,13 @@ void bpf_prog_put(struct bpf_prog *prog); void bpf_prog_free_id(struct bpf_prog *prog, bool do_idr_lock); void bpf_map_free_id(struct bpf_map *map, bool do_idr_lock); -struct bpf_map_value_off_desc *bpf_map_kptr_off_contains(struct bpf_map *map, u32 offset); -void bpf_map_free_kptr_off_tab(struct bpf_map *map); -struct bpf_map_value_off *bpf_map_copy_kptr_off_tab(const struct bpf_map *map); -bool bpf_map_equal_kptr_off_tab(const struct bpf_map *map_a, const struct bpf_map *map_b); -void bpf_map_free_kptrs(struct bpf_map *map, void *map_value); +struct btf_field *btf_record_find(const struct btf_record *rec, + u32 offset, enum btf_field_type type); +void btf_record_free(struct btf_record *rec); +void bpf_map_free_record(struct bpf_map *map); +struct btf_record *btf_record_dup(const struct btf_record *rec); +bool btf_record_equal(const struct btf_record *rec_a, const struct btf_record *rec_b); +void bpf_obj_free_fields(const struct btf_record *rec, void *obj); struct bpf_map *bpf_map_get(u32 ufd); struct bpf_map *bpf_map_get_with_uref(u32 ufd); diff --git a/include/linux/btf.h b/include/linux/btf.h index 86aad9b2ce02..9e62717cdc7a 100644 --- a/include/linux/btf.h +++ b/include/linux/btf.h @@ -163,8 +163,7 @@ bool btf_member_is_reg_int(const struct btf *btf, const struct btf_type *s, u32 expected_offset, u32 expected_size); int btf_find_spin_lock(const struct btf *btf, const struct btf_type *t); int btf_find_timer(const struct btf *btf, const struct btf_type *t); -struct bpf_map_value_off *btf_parse_kptrs(const struct btf *btf, - const struct btf_type *t); +struct btf_record *btf_parse_fields(const struct btf *btf, const struct btf_type *t); bool btf_type_is_void(const struct btf_type *t); s32 btf_find_by_name_kind(const struct btf *btf, const char *name, u8 kind); const struct btf_type *btf_type_skip_modifiers(const struct btf *btf, diff --git a/kernel/bpf/arraymap.c b/kernel/bpf/arraymap.c index 832b2659e96e..417f84342e98 100644 --- a/kernel/bpf/arraymap.c +++ b/kernel/bpf/arraymap.c @@ -310,8 +310,7 @@ static void check_and_free_fields(struct bpf_array *arr, void *val) { if (map_value_has_timer(&arr->map)) bpf_timer_cancel_and_free(val + arr->map.timer_off); - if (map_value_has_kptrs(&arr->map)) - bpf_map_free_kptrs(&arr->map, val); + bpf_obj_free_fields(arr->map.record, val); } /* Called from syscall or from eBPF program */ @@ -409,7 +408,7 @@ static void array_map_free_timers(struct bpf_map *map) struct bpf_array *array = container_of(map, struct bpf_array, map); int i; - /* We don't reset or free kptr on uref dropping to zero. */ + /* We don't reset or free fields other than timer on uref dropping to zero. */ if (!map_value_has_timer(map)) return; @@ -423,22 +422,22 @@ static void array_map_free(struct bpf_map *map) struct bpf_array *array = container_of(map, struct bpf_array, map); int i; - if (map_value_has_kptrs(map)) { + if (!IS_ERR_OR_NULL(map->record)) { if (array->map.map_type == BPF_MAP_TYPE_PERCPU_ARRAY) { for (i = 0; i < array->map.max_entries; i++) { void __percpu *pptr = array->pptrs[i & array->index_mask]; int cpu; for_each_possible_cpu(cpu) { - bpf_map_free_kptrs(map, per_cpu_ptr(pptr, cpu)); + bpf_obj_free_fields(map->record, per_cpu_ptr(pptr, cpu)); cond_resched(); } } } else { for (i = 0; i < array->map.max_entries; i++) - bpf_map_free_kptrs(map, array_map_elem_ptr(array, i)); + bpf_obj_free_fields(map->record, array_map_elem_ptr(array, i)); } - bpf_map_free_kptr_off_tab(map); + bpf_map_free_record(map); } if (array->map.map_type == BPF_MAP_TYPE_PERCPU_ARRAY) diff --git a/kernel/bpf/btf.c b/kernel/bpf/btf.c index f4d21eef6ebd..8391a77138ee 100644 --- a/kernel/bpf/btf.c +++ b/kernel/bpf/btf.c @@ -3191,7 +3191,7 @@ static void btf_struct_log(struct btf_verifier_env *env, btf_verifier_log(env, "size=%u vlen=%u", t->size, btf_type_vlen(t)); } -enum btf_field_type { +enum btf_field_info_type { BTF_FIELD_SPIN_LOCK, BTF_FIELD_TIMER, BTF_FIELD_KPTR, @@ -3203,9 +3203,9 @@ enum { }; struct btf_field_info { - u32 type_id; + enum btf_field_type type; u32 off; - enum bpf_kptr_type type; + u32 type_id; }; static int btf_find_struct(const struct btf *btf, const struct btf_type *t, @@ -3222,7 +3222,7 @@ static int btf_find_struct(const struct btf *btf, const struct btf_type *t, static int btf_find_kptr(const struct btf *btf, const struct btf_type *t, u32 off, int sz, struct btf_field_info *info) { - enum bpf_kptr_type type; + enum btf_field_type type; u32 res_id; /* Permit modifiers on the pointer itself */ @@ -3259,7 +3259,7 @@ static int btf_find_kptr(const struct btf *btf, const struct btf_type *t, static int btf_find_struct_field(const struct btf *btf, const struct btf_type *t, const char *name, int sz, int align, - enum btf_field_type field_type, + enum btf_field_info_type field_type, struct btf_field_info *info, int info_cnt) { const struct btf_member *member; @@ -3311,7 +3311,7 @@ static int btf_find_struct_field(const struct btf *btf, const struct btf_type *t static int btf_find_datasec_var(const struct btf *btf, const struct btf_type *t, const char *name, int sz, int align, - enum btf_field_type field_type, + enum btf_field_info_type field_type, struct btf_field_info *info, int info_cnt) { const struct btf_var_secinfo *vsi; @@ -3360,7 +3360,7 @@ static int btf_find_datasec_var(const struct btf *btf, const struct btf_type *t, } static int btf_find_field(const struct btf *btf, const struct btf_type *t, - enum btf_field_type field_type, + enum btf_field_info_type field_type, struct btf_field_info *info, int info_cnt) { const char *name; @@ -3423,14 +3423,13 @@ int btf_find_timer(const struct btf *btf, const struct btf_type *t) return info.off; } -struct bpf_map_value_off *btf_parse_kptrs(const struct btf *btf, - const struct btf_type *t) +struct btf_record *btf_parse_fields(const struct btf *btf, const struct btf_type *t) { - struct btf_field_info info_arr[BPF_MAP_VALUE_OFF_MAX]; - struct bpf_map_value_off *tab; + struct btf_field_info info_arr[BTF_FIELDS_MAX]; struct btf *kernel_btf = NULL; struct module *mod = NULL; - int ret, i, nr_off; + struct btf_record *rec; + int ret, i, cnt; ret = btf_find_field(btf, t, BTF_FIELD_KPTR, info_arr, ARRAY_SIZE(info_arr)); if (ret < 0) @@ -3438,12 +3437,12 @@ struct bpf_map_value_off *btf_parse_kptrs(const struct btf *btf, if (!ret) return NULL; - nr_off = ret; - tab = kzalloc(offsetof(struct bpf_map_value_off, off[nr_off]), GFP_KERNEL | __GFP_NOWARN); - if (!tab) + cnt = ret; + rec = kzalloc(offsetof(struct btf_record, fields[cnt]), GFP_KERNEL | __GFP_NOWARN); + if (!rec) return ERR_PTR(-ENOMEM); - - for (i = 0; i < nr_off; i++) { + rec->cnt = 0; + for (i = 0; i < cnt; i++) { const struct btf_type *t; s32 id; @@ -3500,28 +3499,24 @@ struct bpf_map_value_off *btf_parse_kptrs(const struct btf *btf, ret = -EINVAL; goto end_mod; } - tab->off[i].kptr.dtor = (void *)addr; + rec->fields[i].kptr.dtor = (void *)addr; } - tab->off[i].offset = info_arr[i].off; - tab->off[i].type = info_arr[i].type; - tab->off[i].kptr.btf_id = id; - tab->off[i].kptr.btf = kernel_btf; - tab->off[i].kptr.module = mod; + rec->field_mask |= info_arr[i].type; + rec->fields[i].offset = info_arr[i].off; + rec->fields[i].type = info_arr[i].type; + rec->fields[i].kptr.btf_id = id; + rec->fields[i].kptr.btf = kernel_btf; + rec->fields[i].kptr.module = mod; + rec->cnt++; } - tab->nr_off = nr_off; - return tab; + return rec; end_mod: module_put(mod); end_btf: btf_put(kernel_btf); end: - while (i--) { - btf_put(tab->off[i].kptr.btf); - if (tab->off[i].kptr.module) - module_put(tab->off[i].kptr.module); - } - kfree(tab); + btf_record_free(rec); return ERR_PTR(ret); } @@ -6370,7 +6365,7 @@ static int btf_check_func_arg_match(struct bpf_verifier_env *env, /* kptr_get is only true for kfunc */ if (i == 0 && kptr_get) { - struct bpf_map_value_off_desc *off_desc; + struct btf_field *kptr_field; if (reg->type != PTR_TO_MAP_VALUE) { bpf_log(log, "arg#0 expected pointer to map value\n"); @@ -6386,8 +6381,8 @@ static int btf_check_func_arg_match(struct bpf_verifier_env *env, return -EINVAL; } - off_desc = bpf_map_kptr_off_contains(reg->map_ptr, reg->off + reg->var_off.value); - if (!off_desc || off_desc->type != BPF_KPTR_REF) { + kptr_field = btf_record_find(reg->map_ptr->record, reg->off + reg->var_off.value, BPF_KPTR); + if (!kptr_field || kptr_field->type != BPF_KPTR_REF) { bpf_log(log, "arg#0 no referenced kptr at map value offset=%llu\n", reg->off + reg->var_off.value); return -EINVAL; @@ -6406,8 +6401,8 @@ static int btf_check_func_arg_match(struct bpf_verifier_env *env, func_name, i, btf_type_str(ref_t), ref_tname); return -EINVAL; } - if (!btf_struct_ids_match(log, btf, ref_id, 0, off_desc->kptr.btf, - off_desc->kptr.btf_id, true)) { + if (!btf_struct_ids_match(log, btf, ref_id, 0, kptr_field->kptr.btf, + kptr_field->kptr.btf_id, true)) { bpf_log(log, "kernel function %s args#%d expected pointer to %s %s\n", func_name, i, btf_type_str(ref_t), ref_tname); return -EINVAL; diff --git a/kernel/bpf/hashtab.c b/kernel/bpf/hashtab.c index f39ee3e05589..c5ea8f9bb7a9 100644 --- a/kernel/bpf/hashtab.c +++ b/kernel/bpf/hashtab.c @@ -238,21 +238,20 @@ static void htab_free_prealloced_timers(struct bpf_htab *htab) } } -static void htab_free_prealloced_kptrs(struct bpf_htab *htab) +static void htab_free_prealloced_fields(struct bpf_htab *htab) { u32 num_entries = htab->map.max_entries; int i; - if (!map_value_has_kptrs(&htab->map)) + if (IS_ERR_OR_NULL(htab->map.record)) return; if (htab_has_extra_elems(htab)) num_entries += num_possible_cpus(); - for (i = 0; i < num_entries; i++) { struct htab_elem *elem; elem = get_htab_elem(htab, i); - bpf_map_free_kptrs(&htab->map, elem->key + round_up(htab->map.key_size, 8)); + bpf_obj_free_fields(htab->map.record, elem->key + round_up(htab->map.key_size, 8)); cond_resched(); } } @@ -766,8 +765,7 @@ static void check_and_free_fields(struct bpf_htab *htab, if (map_value_has_timer(&htab->map)) bpf_timer_cancel_and_free(map_value + htab->map.timer_off); - if (map_value_has_kptrs(&htab->map)) - bpf_map_free_kptrs(&htab->map, map_value); + bpf_obj_free_fields(htab->map.record, map_value); } /* It is called from the bpf_lru_list when the LRU needs to delete @@ -1517,11 +1515,11 @@ static void htab_map_free(struct bpf_map *map) if (!htab_is_prealloc(htab)) { delete_all_elements(htab); } else { - htab_free_prealloced_kptrs(htab); + htab_free_prealloced_fields(htab); prealloc_destroy(htab); } - bpf_map_free_kptr_off_tab(map); + bpf_map_free_record(map); free_percpu(htab->extra_elems); bpf_map_area_free(htab->buckets); bpf_mem_alloc_destroy(&htab->pcpu_ma); diff --git a/kernel/bpf/map_in_map.c b/kernel/bpf/map_in_map.c index 135205d0d560..d6c662183f88 100644 --- a/kernel/bpf/map_in_map.c +++ b/kernel/bpf/map_in_map.c @@ -52,7 +52,15 @@ struct bpf_map *bpf_map_meta_alloc(int inner_map_ufd) inner_map_meta->max_entries = inner_map->max_entries; inner_map_meta->spin_lock_off = inner_map->spin_lock_off; inner_map_meta->timer_off = inner_map->timer_off; - inner_map_meta->kptr_off_tab = bpf_map_copy_kptr_off_tab(inner_map); + inner_map_meta->record = btf_record_dup(inner_map->record); + if (IS_ERR(inner_map_meta->record)) { + /* btf_record_dup returns NULL or valid pointer in case of + * invalid/empty/valid, but ERR_PTR in case of errors. During + * equality NULL or IS_ERR is equivalent. + */ + fdput(f); + return ERR_CAST(inner_map_meta->record); + } if (inner_map->btf) { btf_get(inner_map->btf); inner_map_meta->btf = inner_map->btf; @@ -72,7 +80,7 @@ struct bpf_map *bpf_map_meta_alloc(int inner_map_ufd) void bpf_map_meta_free(struct bpf_map *map_meta) { - bpf_map_free_kptr_off_tab(map_meta); + bpf_map_free_record(map_meta); btf_put(map_meta->btf); kfree(map_meta); } @@ -86,7 +94,7 @@ bool bpf_map_meta_equal(const struct bpf_map *meta0, meta0->value_size == meta1->value_size && meta0->timer_off == meta1->timer_off && meta0->map_flags == meta1->map_flags && - bpf_map_equal_kptr_off_tab(meta0, meta1); + btf_record_equal(meta0->record, meta1->record); } void *bpf_map_fd_get_ptr(struct bpf_map *map, diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c index 5887592eeb93..b80c0e2eb73f 100644 --- a/kernel/bpf/syscall.c +++ b/kernel/bpf/syscall.c @@ -495,114 +495,134 @@ static void bpf_map_release_memcg(struct bpf_map *map) } #endif -static int bpf_map_kptr_off_cmp(const void *a, const void *b) +static int btf_field_cmp(const void *a, const void *b) { - const struct bpf_map_value_off_desc *off_desc1 = a, *off_desc2 = b; + const struct btf_field *f1 = a, *f2 = b; - if (off_desc1->offset < off_desc2->offset) + if (f1->offset < f2->offset) return -1; - else if (off_desc1->offset > off_desc2->offset) + else if (f1->offset > f2->offset) return 1; return 0; } -struct bpf_map_value_off_desc *bpf_map_kptr_off_contains(struct bpf_map *map, u32 offset) +struct btf_field *btf_record_find(const struct btf_record *rec, u32 offset, + enum btf_field_type type) { - /* Since members are iterated in btf_find_field in increasing order, - * offsets appended to kptr_off_tab are in increasing order, so we can - * do bsearch to find exact match. - */ - struct bpf_map_value_off *tab; + struct btf_field *field; - if (!map_value_has_kptrs(map)) + if (IS_ERR_OR_NULL(rec) || !(rec->field_mask & type)) return NULL; - tab = map->kptr_off_tab; - return bsearch(&offset, tab->off, tab->nr_off, sizeof(tab->off[0]), bpf_map_kptr_off_cmp); + field = bsearch(&offset, rec->fields, rec->cnt, sizeof(rec->fields[0]), btf_field_cmp); + if (!field || !(field->type & type)) + return NULL; + return field; } -void bpf_map_free_kptr_off_tab(struct bpf_map *map) +void btf_record_free(struct btf_record *rec) { - struct bpf_map_value_off *tab = map->kptr_off_tab; int i; - if (!map_value_has_kptrs(map)) + if (IS_ERR_OR_NULL(rec)) return; - for (i = 0; i < tab->nr_off; i++) { - if (tab->off[i].kptr.module) - module_put(tab->off[i].kptr.module); - btf_put(tab->off[i].kptr.btf); - } - kfree(tab); - map->kptr_off_tab = NULL; -} - -struct bpf_map_value_off *bpf_map_copy_kptr_off_tab(const struct bpf_map *map) -{ - struct bpf_map_value_off *tab = map->kptr_off_tab, *new_tab; - int size, i; - - if (!map_value_has_kptrs(map)) - return ERR_PTR(-ENOENT); - size = offsetof(struct bpf_map_value_off, off[tab->nr_off]); - new_tab = kmemdup(tab, size, GFP_KERNEL | __GFP_NOWARN); - if (!new_tab) - return ERR_PTR(-ENOMEM); - /* Do a deep copy of the kptr_off_tab */ - for (i = 0; i < tab->nr_off; i++) { - btf_get(tab->off[i].kptr.btf); - if (tab->off[i].kptr.module && !try_module_get(tab->off[i].kptr.module)) { - while (i--) { - if (tab->off[i].kptr.module) - module_put(tab->off[i].kptr.module); - btf_put(tab->off[i].kptr.btf); - } - kfree(new_tab); - return ERR_PTR(-ENXIO); - } - } - return new_tab; -} - -bool bpf_map_equal_kptr_off_tab(const struct bpf_map *map_a, const struct bpf_map *map_b) -{ - struct bpf_map_value_off *tab_a = map_a->kptr_off_tab, *tab_b = map_b->kptr_off_tab; - bool a_has_kptr = map_value_has_kptrs(map_a), b_has_kptr = map_value_has_kptrs(map_b); - int size; - - if (!a_has_kptr && !b_has_kptr) - return true; - if (a_has_kptr != b_has_kptr) - return false; - if (tab_a->nr_off != tab_b->nr_off) - return false; - size = offsetof(struct bpf_map_value_off, off[tab_a->nr_off]); - return !memcmp(tab_a, tab_b, size); -} - -/* Caller must ensure map_value_has_kptrs is true. Note that this function can - * be called on a map value while the map_value is visible to BPF programs, as - * it ensures the correct synchronization, and we already enforce the same using - * the bpf_kptr_xchg helper on the BPF program side for referenced kptrs. - */ -void bpf_map_free_kptrs(struct bpf_map *map, void *map_value) -{ - struct bpf_map_value_off *tab = map->kptr_off_tab; - unsigned long *btf_id_ptr; - int i; - - for (i = 0; i < tab->nr_off; i++) { - struct bpf_map_value_off_desc *off_desc = &tab->off[i]; - unsigned long old_ptr; - - btf_id_ptr = map_value + off_desc->offset; - if (off_desc->type == BPF_KPTR_UNREF) { - u64 *p = (u64 *)btf_id_ptr; - - WRITE_ONCE(*p, 0); + for (i = 0; i < rec->cnt; i++) { + switch (rec->fields[i].type) { + case BPF_KPTR_UNREF: + case BPF_KPTR_REF: + if (rec->fields[i].kptr.module) + module_put(rec->fields[i].kptr.module); + btf_put(rec->fields[i].kptr.btf); + break; + default: + WARN_ON_ONCE(1); + continue; + } + } + kfree(rec); +} + +void bpf_map_free_record(struct bpf_map *map) +{ + btf_record_free(map->record); + map->record = NULL; +} + +struct btf_record *btf_record_dup(const struct btf_record *rec) +{ + const struct btf_field *fields; + struct btf_record *new_rec; + int ret, size, i; + + if (IS_ERR_OR_NULL(rec)) + return NULL; + size = offsetof(struct btf_record, fields[rec->cnt]); + new_rec = kmemdup(rec, size, GFP_KERNEL | __GFP_NOWARN); + if (!new_rec) + return ERR_PTR(-ENOMEM); + /* Do a deep copy of the btf_record */ + fields = rec->fields; + new_rec->cnt = 0; + for (i = 0; i < rec->cnt; i++) { + switch (fields[i].type) { + case BPF_KPTR_UNREF: + case BPF_KPTR_REF: + btf_get(fields[i].kptr.btf); + if (fields[i].kptr.module && !try_module_get(fields[i].kptr.module)) { + ret = -ENXIO; + goto free; + } + break; + default: + ret = -EFAULT; + WARN_ON_ONCE(1); + goto free; + } + new_rec->cnt++; + } + return new_rec; +free: + btf_record_free(new_rec); + return ERR_PTR(ret); +} + +bool btf_record_equal(const struct btf_record *rec_a, const struct btf_record *rec_b) +{ + bool a_has_fields = !IS_ERR_OR_NULL(rec_a), b_has_fields = !IS_ERR_OR_NULL(rec_b); + int size; + + if (!a_has_fields && !b_has_fields) + return true; + if (a_has_fields != b_has_fields) + return false; + if (rec_a->cnt != rec_b->cnt) + return false; + size = offsetof(struct btf_record, fields[rec_a->cnt]); + return !memcmp(rec_a, rec_b, size); +} + +void bpf_obj_free_fields(const struct btf_record *rec, void *obj) +{ + const struct btf_field *fields; + int i; + + if (IS_ERR_OR_NULL(rec)) + return; + fields = rec->fields; + for (i = 0; i < rec->cnt; i++) { + const struct btf_field *field = &fields[i]; + void *field_ptr = obj + field->offset; + + switch (fields[i].type) { + case BPF_KPTR_UNREF: + WRITE_ONCE(*(u64 *)field_ptr, 0); + break; + case BPF_KPTR_REF: + field->kptr.dtor((void *)xchg((unsigned long *)field_ptr, 0)); + break; + default: + WARN_ON_ONCE(1); continue; } - old_ptr = xchg(btf_id_ptr, 0); - off_desc->kptr.dtor((void *)old_ptr); } } @@ -612,10 +632,10 @@ static void bpf_map_free_deferred(struct work_struct *work) struct bpf_map *map = container_of(work, struct bpf_map, work); security_bpf_map_free(map); - kfree(map->off_arr); + kfree(map->field_offs); bpf_map_release_memcg(map); /* implementation dependent freeing, map_free callback also does - * bpf_map_free_kptr_off_tab, if needed. + * bpf_map_free_record, if needed. */ map->ops->map_free(map); } @@ -779,7 +799,7 @@ static int bpf_map_mmap(struct file *filp, struct vm_area_struct *vma) int err; if (!map->ops->map_mmap || map_value_has_spin_lock(map) || - map_value_has_timer(map) || map_value_has_kptrs(map)) + map_value_has_timer(map) || !IS_ERR_OR_NULL(map->record)) return -ENOTSUPP; if (!(vma->vm_flags & VM_SHARED)) @@ -906,7 +926,7 @@ int map_check_no_btf(const struct bpf_map *map, return -ENOTSUPP; } -static int map_off_arr_cmp(const void *_a, const void *_b, const void *priv) +static int map_field_offs_cmp(const void *_a, const void *_b, const void *priv) { const u32 a = *(const u32 *)_a; const u32 b = *(const u32 *)_b; @@ -918,15 +938,15 @@ static int map_off_arr_cmp(const void *_a, const void *_b, const void *priv) return 0; } -static void map_off_arr_swap(void *_a, void *_b, int size, const void *priv) +static void map_field_offs_swap(void *_a, void *_b, int size, const void *priv) { struct bpf_map *map = (struct bpf_map *)priv; - u32 *off_base = map->off_arr->field_off; + u32 *off_base = map->field_offs->field_off; u32 *a = _a, *b = _b; u8 *sz_a, *sz_b; - sz_a = map->off_arr->field_sz + (a - off_base); - sz_b = map->off_arr->field_sz + (b - off_base); + sz_a = map->field_offs->field_sz + (a - off_base); + sz_b = map->field_offs->field_sz + (b - off_base); swap(*a, *b); swap(*sz_a, *sz_b); @@ -936,51 +956,51 @@ static int bpf_map_alloc_off_arr(struct bpf_map *map) { bool has_spin_lock = map_value_has_spin_lock(map); bool has_timer = map_value_has_timer(map); - bool has_kptrs = map_value_has_kptrs(map); - struct bpf_map_off_arr *off_arr; + bool has_fields = !IS_ERR_OR_NULL(map->record); + struct btf_field_offs *fo; u32 i; - if (!has_spin_lock && !has_timer && !has_kptrs) { - map->off_arr = NULL; + if (!has_spin_lock && !has_timer && !has_fields) { + map->field_offs = NULL; return 0; } - off_arr = kmalloc(sizeof(*map->off_arr), GFP_KERNEL | __GFP_NOWARN); - if (!off_arr) + fo = kmalloc(sizeof(*map->field_offs), GFP_KERNEL | __GFP_NOWARN); + if (!fo) return -ENOMEM; - map->off_arr = off_arr; + map->field_offs = fo; - off_arr->cnt = 0; + fo->cnt = 0; if (has_spin_lock) { - i = off_arr->cnt; + i = fo->cnt; - off_arr->field_off[i] = map->spin_lock_off; - off_arr->field_sz[i] = sizeof(struct bpf_spin_lock); - off_arr->cnt++; + fo->field_off[i] = map->spin_lock_off; + fo->field_sz[i] = sizeof(struct bpf_spin_lock); + fo->cnt++; } if (has_timer) { - i = off_arr->cnt; + i = fo->cnt; - off_arr->field_off[i] = map->timer_off; - off_arr->field_sz[i] = sizeof(struct bpf_timer); - off_arr->cnt++; + fo->field_off[i] = map->timer_off; + fo->field_sz[i] = sizeof(struct bpf_timer); + fo->cnt++; } - if (has_kptrs) { - struct bpf_map_value_off *tab = map->kptr_off_tab; - u32 *off = &off_arr->field_off[off_arr->cnt]; - u8 *sz = &off_arr->field_sz[off_arr->cnt]; + if (has_fields) { + struct btf_record *rec = map->record; + u32 *off = &fo->field_off[fo->cnt]; + u8 *sz = &fo->field_sz[fo->cnt]; - for (i = 0; i < tab->nr_off; i++) { - *off++ = tab->off[i].offset; - *sz++ = sizeof(u64); + for (i = 0; i < rec->cnt; i++) { + *off++ = rec->fields[i].offset; + *sz++ = btf_field_type_size(rec->fields[i].type); } - off_arr->cnt += tab->nr_off; + fo->cnt += rec->cnt; } - if (off_arr->cnt == 1) + if (fo->cnt == 1) return 0; - sort_r(off_arr->field_off, off_arr->cnt, sizeof(off_arr->field_off[0]), - map_off_arr_cmp, map_off_arr_swap, map); + sort_r(fo->field_off, fo->cnt, sizeof(fo->field_off[0]), + map_field_offs_cmp, map_field_offs_swap, map); return 0; } @@ -1038,8 +1058,10 @@ static int map_check_btf(struct bpf_map *map, const struct btf *btf, return -EOPNOTSUPP; } - map->kptr_off_tab = btf_parse_kptrs(btf, value_type); - if (map_value_has_kptrs(map)) { + map->record = btf_parse_fields(btf, value_type); + if (!IS_ERR_OR_NULL(map->record)) { + int i; + if (!bpf_capable()) { ret = -EPERM; goto free_map_tab; @@ -1048,12 +1070,25 @@ static int map_check_btf(struct bpf_map *map, const struct btf *btf, ret = -EACCES; goto free_map_tab; } - if (map->map_type != BPF_MAP_TYPE_HASH && - map->map_type != BPF_MAP_TYPE_LRU_HASH && - map->map_type != BPF_MAP_TYPE_ARRAY && - map->map_type != BPF_MAP_TYPE_PERCPU_ARRAY) { - ret = -EOPNOTSUPP; - goto free_map_tab; + for (i = 0; i < sizeof(map->record->field_mask) * 8; i++) { + switch (map->record->field_mask & (1 << i)) { + case 0: + continue; + case BPF_KPTR_UNREF: + case BPF_KPTR_REF: + if (map->map_type != BPF_MAP_TYPE_HASH && + map->map_type != BPF_MAP_TYPE_LRU_HASH && + map->map_type != BPF_MAP_TYPE_ARRAY && + map->map_type != BPF_MAP_TYPE_PERCPU_ARRAY) { + ret = -EOPNOTSUPP; + goto free_map_tab; + } + break; + default: + /* Fail if map_type checks are missing for a field type */ + ret = -EOPNOTSUPP; + goto free_map_tab; + } } } @@ -1065,7 +1100,7 @@ static int map_check_btf(struct bpf_map *map, const struct btf *btf, return ret; free_map_tab: - bpf_map_free_kptr_off_tab(map); + bpf_map_free_record(map); return ret; } @@ -1186,7 +1221,7 @@ static int map_create(union bpf_attr *attr) free_map_sec: security_bpf_map_free(map); free_map_off_arr: - kfree(map->off_arr); + kfree(map->field_offs); free_map: btf_put(map->btf); map->ops->map_free(map); @@ -1883,7 +1918,7 @@ static int map_freeze(const union bpf_attr *attr) return PTR_ERR(map); if (map->map_type == BPF_MAP_TYPE_STRUCT_OPS || - map_value_has_timer(map) || map_value_has_kptrs(map)) { + map_value_has_timer(map) || !IS_ERR_OR_NULL(map->record)) { fdput(f); return -ENOTSUPP; } diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 14d350a25d5d..5ce5364ce898 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -262,7 +262,7 @@ struct bpf_call_arg_meta { struct btf *ret_btf; u32 ret_btf_id; u32 subprogno; - struct bpf_map_value_off_desc *kptr_off_desc; + struct btf_field *kptr_field; u8 uninit_dynptr_regno; }; @@ -3674,15 +3674,15 @@ int check_ptr_off_reg(struct bpf_verifier_env *env, } static int map_kptr_match_type(struct bpf_verifier_env *env, - struct bpf_map_value_off_desc *off_desc, + struct btf_field *kptr_field, struct bpf_reg_state *reg, u32 regno) { - const char *targ_name = kernel_type_name(off_desc->kptr.btf, off_desc->kptr.btf_id); + const char *targ_name = kernel_type_name(kptr_field->kptr.btf, kptr_field->kptr.btf_id); int perm_flags = PTR_MAYBE_NULL; const char *reg_name = ""; /* Only unreferenced case accepts untrusted pointers */ - if (off_desc->type == BPF_KPTR_UNREF) + if (kptr_field->type == BPF_KPTR_UNREF) perm_flags |= PTR_UNTRUSTED; if (base_type(reg->type) != PTR_TO_BTF_ID || (type_flag(reg->type) & ~perm_flags)) @@ -3729,15 +3729,15 @@ static int map_kptr_match_type(struct bpf_verifier_env *env, * strict mode to true for type match. */ if (!btf_struct_ids_match(&env->log, reg->btf, reg->btf_id, reg->off, - off_desc->kptr.btf, off_desc->kptr.btf_id, - off_desc->type == BPF_KPTR_REF)) + kptr_field->kptr.btf, kptr_field->kptr.btf_id, + kptr_field->type == BPF_KPTR_REF)) goto bad_type; return 0; bad_type: verbose(env, "invalid kptr access, R%d type=%s%s ", regno, reg_type_str(env, reg->type), reg_name); verbose(env, "expected=%s%s", reg_type_str(env, PTR_TO_BTF_ID), targ_name); - if (off_desc->type == BPF_KPTR_UNREF) + if (kptr_field->type == BPF_KPTR_UNREF) verbose(env, " or %s%s\n", reg_type_str(env, PTR_TO_BTF_ID | PTR_UNTRUSTED), targ_name); else @@ -3747,7 +3747,7 @@ bad_type: static int check_map_kptr_access(struct bpf_verifier_env *env, u32 regno, int value_regno, int insn_idx, - struct bpf_map_value_off_desc *off_desc) + struct btf_field *kptr_field) { struct bpf_insn *insn = &env->prog->insnsi[insn_idx]; int class = BPF_CLASS(insn->code); @@ -3757,7 +3757,7 @@ static int check_map_kptr_access(struct bpf_verifier_env *env, u32 regno, * - Reject cases where variable offset may touch kptr * - size of access (must be BPF_DW) * - tnum_is_const(reg->var_off) - * - off_desc->offset == off + reg->var_off.value + * - kptr_field->offset == off + reg->var_off.value */ /* Only BPF_[LDX,STX,ST] | BPF_MEM | BPF_DW is supported */ if (BPF_MODE(insn->code) != BPF_MEM) { @@ -3768,7 +3768,7 @@ static int check_map_kptr_access(struct bpf_verifier_env *env, u32 regno, /* We only allow loading referenced kptr, since it will be marked as * untrusted, similar to unreferenced kptr. */ - if (class != BPF_LDX && off_desc->type == BPF_KPTR_REF) { + if (class != BPF_LDX && kptr_field->type == BPF_KPTR_REF) { verbose(env, "store to referenced kptr disallowed\n"); return -EACCES; } @@ -3778,19 +3778,19 @@ static int check_map_kptr_access(struct bpf_verifier_env *env, u32 regno, /* We can simply mark the value_regno receiving the pointer * value from map as PTR_TO_BTF_ID, with the correct type. */ - mark_btf_ld_reg(env, cur_regs(env), value_regno, PTR_TO_BTF_ID, off_desc->kptr.btf, - off_desc->kptr.btf_id, PTR_MAYBE_NULL | PTR_UNTRUSTED); + mark_btf_ld_reg(env, cur_regs(env), value_regno, PTR_TO_BTF_ID, kptr_field->kptr.btf, + kptr_field->kptr.btf_id, PTR_MAYBE_NULL | PTR_UNTRUSTED); /* For mark_ptr_or_null_reg */ val_reg->id = ++env->id_gen; } else if (class == BPF_STX) { val_reg = reg_state(env, value_regno); if (!register_is_null(val_reg) && - map_kptr_match_type(env, off_desc, val_reg, value_regno)) + map_kptr_match_type(env, kptr_field, val_reg, value_regno)) return -EACCES; } else if (class == BPF_ST) { if (insn->imm) { verbose(env, "BPF_ST imm must be 0 when storing to kptr at off=%u\n", - off_desc->offset); + kptr_field->offset); return -EACCES; } } else { @@ -3809,7 +3809,8 @@ static int check_map_access(struct bpf_verifier_env *env, u32 regno, struct bpf_func_state *state = vstate->frame[vstate->curframe]; struct bpf_reg_state *reg = &state->regs[regno]; struct bpf_map *map = reg->map_ptr; - int err; + struct btf_record *rec; + int err, i; err = check_mem_region_access(env, regno, off, size, map->value_size, zero_size_allowed); @@ -3839,15 +3840,18 @@ static int check_map_access(struct bpf_verifier_env *env, u32 regno, return -EACCES; } } - if (map_value_has_kptrs(map)) { - struct bpf_map_value_off *tab = map->kptr_off_tab; - int i; + if (IS_ERR_OR_NULL(map->record)) + return 0; + rec = map->record; + for (i = 0; i < rec->cnt; i++) { + struct btf_field *field = &rec->fields[i]; + u32 p = field->offset; - for (i = 0; i < tab->nr_off; i++) { - u32 p = tab->off[i].offset; - - if (reg->smin_value + off < p + sizeof(u64) && - p < reg->umax_value + off + size) { + if (reg->smin_value + off < p + btf_field_type_size(field->type) && + p < reg->umax_value + off + size) { + switch (field->type) { + case BPF_KPTR_UNREF: + case BPF_KPTR_REF: if (src != ACCESS_DIRECT) { verbose(env, "kptr cannot be accessed indirectly by helper\n"); return -EACCES; @@ -3866,10 +3870,13 @@ static int check_map_access(struct bpf_verifier_env *env, u32 regno, return -EACCES; } break; + default: + verbose(env, "field cannot be accessed directly by load/store\n"); + return -EACCES; } } } - return err; + return 0; } #define MAX_PACKET_OFF 0xffff @@ -4742,7 +4749,7 @@ static int check_mem_access(struct bpf_verifier_env *env, int insn_idx, u32 regn if (value_regno >= 0) mark_reg_unknown(env, regs, value_regno); } else if (reg->type == PTR_TO_MAP_VALUE) { - struct bpf_map_value_off_desc *kptr_off_desc = NULL; + struct btf_field *kptr_field = NULL; if (t == BPF_WRITE && value_regno >= 0 && is_pointer_value(env, value_regno)) { @@ -4756,11 +4763,10 @@ static int check_mem_access(struct bpf_verifier_env *env, int insn_idx, u32 regn if (err) return err; if (tnum_is_const(reg->var_off)) - kptr_off_desc = bpf_map_kptr_off_contains(reg->map_ptr, - off + reg->var_off.value); - if (kptr_off_desc) { - err = check_map_kptr_access(env, regno, value_regno, insn_idx, - kptr_off_desc); + kptr_field = btf_record_find(reg->map_ptr->record, + off + reg->var_off.value, BPF_KPTR); + if (kptr_field) { + err = check_map_kptr_access(env, regno, value_regno, insn_idx, kptr_field); } else if (t == BPF_READ && value_regno >= 0) { struct bpf_map *map = reg->map_ptr; @@ -5527,10 +5533,9 @@ static int process_kptr_func(struct bpf_verifier_env *env, int regno, struct bpf_call_arg_meta *meta) { struct bpf_reg_state *regs = cur_regs(env), *reg = ®s[regno]; - struct bpf_map_value_off_desc *off_desc; struct bpf_map *map_ptr = reg->map_ptr; + struct btf_field *kptr_field; u32 kptr_off; - int ret; if (!tnum_is_const(reg->var_off)) { verbose(env, @@ -5543,30 +5548,23 @@ static int process_kptr_func(struct bpf_verifier_env *env, int regno, map_ptr->name); return -EINVAL; } - if (!map_value_has_kptrs(map_ptr)) { - ret = PTR_ERR_OR_ZERO(map_ptr->kptr_off_tab); - if (ret == -E2BIG) - verbose(env, "map '%s' has more than %d kptr\n", map_ptr->name, - BPF_MAP_VALUE_OFF_MAX); - else if (ret == -EEXIST) - verbose(env, "map '%s' has repeating kptr BTF tags\n", map_ptr->name); - else - verbose(env, "map '%s' has no valid kptr\n", map_ptr->name); + if (!btf_record_has_field(map_ptr->record, BPF_KPTR)) { + verbose(env, "map '%s' has no valid kptr\n", map_ptr->name); return -EINVAL; } meta->map_ptr = map_ptr; kptr_off = reg->off + reg->var_off.value; - off_desc = bpf_map_kptr_off_contains(map_ptr, kptr_off); - if (!off_desc) { + kptr_field = btf_record_find(map_ptr->record, kptr_off, BPF_KPTR); + if (!kptr_field) { verbose(env, "off=%d doesn't point to kptr\n", kptr_off); return -EACCES; } - if (off_desc->type != BPF_KPTR_REF) { + if (kptr_field->type != BPF_KPTR_REF) { verbose(env, "off=%d kptr isn't referenced kptr\n", kptr_off); return -EACCES; } - meta->kptr_off_desc = off_desc; + meta->kptr_field = kptr_field; return 0; } @@ -5788,7 +5786,7 @@ found: } if (meta->func_id == BPF_FUNC_kptr_xchg) { - if (map_kptr_match_type(env, meta->kptr_off_desc, reg, regno)) + if (map_kptr_match_type(env, meta->kptr_field, reg, regno)) return -EACCES; } else { if (arg_btf_id == BPF_PTR_POISON) { @@ -7536,8 +7534,8 @@ static int check_helper_call(struct bpf_verifier_env *env, struct bpf_insn *insn mark_reg_known_zero(env, regs, BPF_REG_0); regs[BPF_REG_0].type = PTR_TO_BTF_ID | ret_flag; if (func_id == BPF_FUNC_kptr_xchg) { - ret_btf = meta.kptr_off_desc->kptr.btf; - ret_btf_id = meta.kptr_off_desc->kptr.btf_id; + ret_btf = meta.kptr_field->kptr.btf; + ret_btf_id = meta.kptr_field->kptr.btf_id; } else { if (fn->ret_btf_id == BPF_PTR_POISON) { verbose(env, "verifier internal error:"); From 9b5e3536c898b23143748b8cc01c64f868ec7078 Mon Sep 17 00:00:00 2001 From: Andrii Nakryiko Date: Wed, 2 Nov 2022 22:52:55 -0700 Subject: [PATCH 0598/2002] selftests/bpf: add veristat replay mode Replay mode allow to parse previously stored CSV file with verification results and present it in desired output (presumable human-readable table, but CSV to CSV convertion is supported as well). While doing that, it's possible to use veristat's sorting rules, specify subset of columns, and filter by file and program name. In subsequent patches veristat's filtering capabilities will just grow making replay mode even more useful in practice for post-processing results. Signed-off-by: Andrii Nakryiko Link: https://lore.kernel.org/r/20221103055304.2904589-2-andrii@kernel.org Signed-off-by: Alexei Starovoitov --- tools/testing/selftests/bpf/veristat.c | 126 +++++++++++++++++-------- 1 file changed, 88 insertions(+), 38 deletions(-) diff --git a/tools/testing/selftests/bpf/veristat.c b/tools/testing/selftests/bpf/veristat.c index 973cbf6af323..7e1432c06e0c 100644 --- a/tools/testing/selftests/bpf/veristat.c +++ b/tools/testing/selftests/bpf/veristat.c @@ -67,6 +67,7 @@ static struct env { int log_level; enum resfmt out_fmt; bool comparison_mode; + bool replay_mode; struct verif_stats *prog_stats; int prog_stat_cnt; @@ -115,6 +116,7 @@ static const struct argp_option opts[] = { { "sort", 's', "SPEC", 0, "Specify sort order" }, { "output-format", 'o', "FMT", 0, "Result output format (table, csv), default is table." }, { "compare", 'C', NULL, 0, "Comparison mode" }, + { "replay", 'R', NULL, 0, "Replay mode" }, { "filter", 'f', "FILTER", 0, "Filter expressions (or @filename for file with expressions)." }, {}, }; @@ -169,6 +171,9 @@ static error_t parse_arg(int key, char *arg, struct argp_state *state) case 'C': env.comparison_mode = true; break; + case 'R': + env.replay_mode = true; + break; case 'f': if (arg[0] == '@') err = append_filter_file(arg + 1); @@ -841,42 +846,6 @@ static void output_stats(const struct verif_stats *s, enum resfmt fmt, bool last } } -static int handle_verif_mode(void) -{ - int i, err; - - if (env.filename_cnt == 0) { - fprintf(stderr, "Please provide path to BPF object file!\n"); - argp_help(&argp, stderr, ARGP_HELP_USAGE, "veristat"); - return -EINVAL; - } - - for (i = 0; i < env.filename_cnt; i++) { - err = process_obj(env.filenames[i]); - if (err) { - fprintf(stderr, "Failed to process '%s': %d\n", env.filenames[i], err); - return err; - } - } - - qsort(env.prog_stats, env.prog_stat_cnt, sizeof(*env.prog_stats), cmp_prog_stats); - - if (env.out_fmt == RESFMT_TABLE) { - /* calculate column widths */ - output_headers(RESFMT_TABLE_CALCLEN); - for (i = 0; i < env.prog_stat_cnt; i++) - output_stats(&env.prog_stats[i], RESFMT_TABLE_CALCLEN, false); - } - - /* actually output the table */ - output_headers(env.out_fmt); - for (i = 0; i < env.prog_stat_cnt; i++) { - output_stats(&env.prog_stats[i], env.out_fmt, i == env.prog_stat_cnt - 1); - } - - return 0; -} - static int parse_stat_value(const char *str, enum stat_id id, struct verif_stats *st) { switch (id) { @@ -1206,7 +1175,7 @@ static int handle_comparison_mode(void) int err, i, j; if (env.filename_cnt != 2) { - fprintf(stderr, "Comparison mode expects exactly two input CSV files!\n"); + fprintf(stderr, "Comparison mode expects exactly two input CSV files!\n\n"); argp_help(&argp, stderr, ARGP_HELP_USAGE, "veristat"); return -EINVAL; } @@ -1307,6 +1276,79 @@ one_more_time: return 0; } +static void output_prog_stats(void) +{ + const struct verif_stats *stats; + int i, last_stat_idx = 0; + + if (env.out_fmt == RESFMT_TABLE) { + /* calculate column widths */ + output_headers(RESFMT_TABLE_CALCLEN); + for (i = 0; i < env.prog_stat_cnt; i++) { + stats = &env.prog_stats[i]; + output_stats(stats, RESFMT_TABLE_CALCLEN, false); + last_stat_idx = i; + } + } + + /* actually output the table */ + output_headers(env.out_fmt); + for (i = 0; i < env.prog_stat_cnt; i++) { + stats = &env.prog_stats[i]; + output_stats(stats, env.out_fmt, i == last_stat_idx); + } +} + +static int handle_verif_mode(void) +{ + int i, err; + + if (env.filename_cnt == 0) { + fprintf(stderr, "Please provide path to BPF object file!\n\n"); + argp_help(&argp, stderr, ARGP_HELP_USAGE, "veristat"); + return -EINVAL; + } + + for (i = 0; i < env.filename_cnt; i++) { + err = process_obj(env.filenames[i]); + if (err) { + fprintf(stderr, "Failed to process '%s': %d\n", env.filenames[i], err); + return err; + } + } + + qsort(env.prog_stats, env.prog_stat_cnt, sizeof(*env.prog_stats), cmp_prog_stats); + + output_prog_stats(); + + return 0; +} + +static int handle_replay_mode(void) +{ + struct stat_specs specs = {}; + int err; + + if (env.filename_cnt != 1) { + fprintf(stderr, "Replay mode expects exactly one input CSV file!\n\n"); + argp_help(&argp, stderr, ARGP_HELP_USAGE, "veristat"); + return -EINVAL; + } + + err = parse_stats_csv(env.filenames[0], &specs, + &env.prog_stats, &env.prog_stat_cnt); + if (err) { + fprintf(stderr, "Failed to parse stats from '%s': %d\n", env.filenames[0], err); + return err; + } + + qsort(env.prog_stats, env.prog_stat_cnt, sizeof(*env.prog_stats), cmp_prog_stats); + + output_prog_stats(); + + return 0; +} + int main(int argc, char **argv) { int err = 0, i; @@ -1315,7 +1357,7 @@ int main(int argc, char **argv) return 1; if (env.verbose && env.quiet) { - fprintf(stderr, "Verbose and quiet modes are incompatible, please specify just one or neither!\n"); + fprintf(stderr, "Verbose and quiet modes are incompatible, please specify just one or neither!\n\n"); argp_help(&argp, stderr, ARGP_HELP_USAGE, "veristat"); return 1; } @@ -1327,8 +1369,16 @@ int main(int argc, char **argv) if (env.sort_spec.spec_cnt == 0) env.sort_spec = default_sort_spec; + if (env.comparison_mode && env.replay_mode) { + fprintf(stderr, "Can't specify replay and comparison mode at the same time!\n\n"); + argp_help(&argp, stderr, ARGP_HELP_USAGE, "veristat"); + return 1; + } + if (env.comparison_mode) err = handle_comparison_mode(); + else if (env.replay_mode) + err = handle_replay_mode(); else err = handle_verif_mode(); From 62d2c08bb91cc3fc26319c571000cacac0312426 Mon Sep 17 00:00:00 2001 From: Andrii Nakryiko Date: Wed, 2 Nov 2022 22:52:56 -0700 Subject: [PATCH 0599/2002] selftests/bpf: shorten "Total insns/states" column names in veristat In comparison mode the "Total " part is pretty useless, but takes a considerable amount of horizontal space. Drop the "Total " parts. Also make sure that table headers for numerical columns are aligned in the same fashion as integer values in those columns. This looks better and is now more obvious with shorter "Insns" and "States" column headers. Signed-off-by: Andrii Nakryiko Link: https://lore.kernel.org/r/20221103055304.2904589-3-andrii@kernel.org Signed-off-by: Alexei Starovoitov --- tools/testing/selftests/bpf/veristat.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/tools/testing/selftests/bpf/veristat.c b/tools/testing/selftests/bpf/veristat.c index 7e1432c06e0c..d553f38a6cee 100644 --- a/tools/testing/selftests/bpf/veristat.c +++ b/tools/testing/selftests/bpf/veristat.c @@ -405,13 +405,14 @@ static struct stat_def { const char *header; const char *names[4]; bool asc_by_default; + bool left_aligned; } stat_defs[] = { - [FILE_NAME] = { "File", {"file_name", "filename", "file"}, true /* asc */ }, - [PROG_NAME] = { "Program", {"prog_name", "progname", "prog"}, true /* asc */ }, - [VERDICT] = { "Verdict", {"verdict"}, true /* asc: failure, success */ }, + [FILE_NAME] = { "File", {"file_name", "filename", "file"}, true /* asc */, true /* left */ }, + [PROG_NAME] = { "Program", {"prog_name", "progname", "prog"}, true /* asc */, true /* left */ }, + [VERDICT] = { "Verdict", {"verdict"}, true /* asc: failure, success */, true /* left */ }, [DURATION] = { "Duration (us)", {"duration", "dur"}, }, - [TOTAL_INSNS] = { "Total insns", {"total_insns", "insns"}, }, - [TOTAL_STATES] = { "Total states", {"total_states", "states"}, }, + [TOTAL_INSNS] = { "Insns", {"total_insns", "insns"}, }, + [TOTAL_STATES] = { "States", {"total_states", "states"}, }, [PEAK_STATES] = { "Peak states", {"peak_states"}, }, [MAX_STATES_PER_INSN] = { "Max states per insn", {"max_states_per_insn"}, }, [MARK_READ_MAX_LEN] = { "Max mark read length", {"max_mark_read_len", "mark_read"}, }, @@ -743,6 +744,7 @@ static void output_header_underlines(void) static void output_headers(enum resfmt fmt) { + const char *fmt_str; int i, len; for (i = 0; i < env.output_spec.spec_cnt; i++) { @@ -756,7 +758,8 @@ static void output_headers(enum resfmt fmt) *max_len = len; break; case RESFMT_TABLE: - printf("%s%-*s", i == 0 ? "" : COLUMN_SEP, *max_len, stat_defs[id].header); + fmt_str = stat_defs[id].left_aligned ? "%s%-*s" : "%s%*s"; + printf(fmt_str, i == 0 ? "" : COLUMN_SEP, *max_len, stat_defs[id].header); if (i == env.output_spec.spec_cnt - 1) printf("\n"); break; From 10b1b3f3e56a6a3586356bf8cc77d7753ba8fcc9 Mon Sep 17 00:00:00 2001 From: Andrii Nakryiko Date: Wed, 2 Nov 2022 22:52:57 -0700 Subject: [PATCH 0600/2002] selftests/bpf: consolidate and improve file/prog filtering in veristat Slightly change rules of specifying file/prog glob filters. In practice it's quite often inconvenient to do `*/` if that program glob is unique enough and won't accidentally match any file names. This patch changes the rules so that `-f ` will apply specified glob to both file and program names. User still has all the control by doing '*/' or '' and ' Link: https://lore.kernel.org/r/20221103055304.2904589-4-andrii@kernel.org Signed-off-by: Alexei Starovoitov --- tools/testing/selftests/bpf/veristat.c | 129 +++++++++++++------------ 1 file changed, 66 insertions(+), 63 deletions(-) diff --git a/tools/testing/selftests/bpf/veristat.c b/tools/testing/selftests/bpf/veristat.c index d553f38a6cee..f6f6a2490489 100644 --- a/tools/testing/selftests/bpf/veristat.c +++ b/tools/testing/selftests/bpf/veristat.c @@ -55,6 +55,7 @@ enum resfmt { }; struct filter { + char *any_glob; char *file_glob; char *prog_glob; }; @@ -231,28 +232,6 @@ static bool glob_matches(const char *str, const char *pat) return !*str && !*pat; } -static bool should_process_file(const char *filename) -{ - int i; - - if (env.deny_filter_cnt > 0) { - for (i = 0; i < env.deny_filter_cnt; i++) { - if (glob_matches(filename, env.deny_filters[i].file_glob)) - return false; - } - } - - if (env.allow_filter_cnt == 0) - return true; - - for (i = 0; i < env.allow_filter_cnt; i++) { - if (glob_matches(filename, env.allow_filters[i].file_glob)) - return true; - } - - return false; -} - static bool is_bpf_obj_file(const char *path) { Elf64_Ehdr *ehdr; int fd, err = -EINVAL; @@ -285,38 +264,46 @@ cleanup: return err == 0; } -static bool should_process_prog(const char *path, const char *prog_name) +static bool should_process_file_prog(const char *filename, const char *prog_name) { - const char *filename = basename(path); - int i; + struct filter *f; + int i, allow_cnt = 0; - if (env.deny_filter_cnt > 0) { - for (i = 0; i < env.deny_filter_cnt; i++) { - if (glob_matches(filename, env.deny_filters[i].file_glob)) - return false; - if (!env.deny_filters[i].prog_glob) + for (i = 0; i < env.deny_filter_cnt; i++) { + f = &env.deny_filters[i]; + + if (f->any_glob && glob_matches(filename, f->any_glob)) + return false; + if (f->any_glob && prog_name && glob_matches(prog_name, f->any_glob)) + return false; + if (f->file_glob && glob_matches(filename, f->file_glob)) + return false; + if (f->prog_glob && prog_name && glob_matches(prog_name, f->prog_glob)) + return false; + } + + for (i = 0; i < env.allow_filter_cnt; i++) { + f = &env.allow_filters[i]; + allow_cnt++; + + if (f->any_glob) { + if (glob_matches(filename, f->any_glob)) + return true; + if (prog_name && glob_matches(prog_name, f->any_glob)) + return true; + } else { + if (f->file_glob && !glob_matches(filename, f->file_glob)) continue; - if (glob_matches(prog_name, env.deny_filters[i].prog_glob)) - return false; + if (f->prog_glob && prog_name && !glob_matches(prog_name, f->prog_glob)) + continue; + return true; } } - if (env.allow_filter_cnt == 0) - return true; - - for (i = 0; i < env.allow_filter_cnt; i++) { - if (!glob_matches(filename, env.allow_filters[i].file_glob)) - continue; - /* if filter specifies only filename glob part, it implicitly - * allows all progs within that file - */ - if (!env.allow_filters[i].prog_glob) - return true; - if (glob_matches(prog_name, env.allow_filters[i].prog_glob)) - return true; - } - - return false; + /* if there are no file/prog name allow filters, allow all progs, + * unless they are denied earlier explicitly + */ + return allow_cnt == 0; } static int append_filter(struct filter **filters, int *cnt, const char *str) @@ -331,26 +318,40 @@ static int append_filter(struct filter **filters, int *cnt, const char *str) *filters = tmp; f = &(*filters)[*cnt]; - f->file_glob = f->prog_glob = NULL; + memset(f, 0, sizeof(*f)); - /* filter can be specified either as "" or "/" */ + /* File/prog filter can be specified either as '' or + * '/'. In the former case is applied to + * both file and program names. This seems to be way more useful in + * practice. If user needs full control, they can use '/' + * form to glob just program name, or '/' to glob only file + * name. But usually common seems to be the most useful and + * ergonomic way. + */ p = strchr(str, '/'); if (!p) { - f->file_glob = strdup(str); - if (!f->file_glob) + f->any_glob = strdup(str); + if (!f->any_glob) return -ENOMEM; } else { - f->file_glob = strndup(str, p - str); - f->prog_glob = strdup(p + 1); - if (!f->file_glob || !f->prog_glob) { - free(f->file_glob); - free(f->prog_glob); - f->file_glob = f->prog_glob = NULL; - return -ENOMEM; + if (str != p) { + /* non-empty file glob */ + f->file_glob = strndup(str, p - str); + if (!f->file_glob) + return -ENOMEM; + } + if (strlen(p + 1) > 0) { + /* non-empty prog glob */ + f->prog_glob = strdup(p + 1); + if (!f->prog_glob) { + free(f->file_glob); + f->file_glob = NULL; + return -ENOMEM; + } } } - *cnt = *cnt + 1; + *cnt += 1; return 0; } @@ -546,7 +547,7 @@ static int process_prog(const char *filename, struct bpf_object *obj, struct bpf int err = 0; void *tmp; - if (!should_process_prog(filename, bpf_program__name(prog))) { + if (!should_process_file_prog(basename(filename), bpf_program__name(prog))) { env.progs_skipped++; return 0; } @@ -602,7 +603,7 @@ static int process_obj(const char *filename) LIBBPF_OPTS(bpf_object_open_opts, opts); int err = 0, prog_cnt = 0; - if (!should_process_file(basename(filename))) { + if (!should_process_file_prog(basename(filename), NULL)) { if (env.verbose) printf("Skipping '%s' due to filters...\n", filename); env.files_skipped++; @@ -980,7 +981,7 @@ static int parse_stats_csv(const char *filename, struct stat_specs *specs, * parsed entire line; if row should be ignored we pretend we * never parsed it */ - if (!should_process_prog(st->file_name, st->prog_name)) { + if (!should_process_file_prog(st->file_name, st->prog_name)) { free(st->file_name); free(st->prog_name); *stat_cntp -= 1; @@ -1391,11 +1392,13 @@ int main(int argc, char **argv) free(env.filenames[i]); free(env.filenames); for (i = 0; i < env.allow_filter_cnt; i++) { + free(env.allow_filters[i].any_glob); free(env.allow_filters[i].file_glob); free(env.allow_filters[i].prog_glob); } free(env.allow_filters); for (i = 0; i < env.deny_filter_cnt; i++) { + free(env.deny_filters[i].any_glob); free(env.deny_filters[i].file_glob); free(env.deny_filters[i].prog_glob); } From b9670b904a59808ef4222179a255978384bcc119 Mon Sep 17 00:00:00 2001 From: Andrii Nakryiko Date: Wed, 2 Nov 2022 22:52:58 -0700 Subject: [PATCH 0601/2002] selftests/bpf: ensure we always have non-ambiguous sorting in veristat Always fall back to unique file/prog comparison if user's custom order specs are ambiguous. This ensures stable output no matter what. Signed-off-by: Andrii Nakryiko Link: https://lore.kernel.org/r/20221103055304.2904589-5-andrii@kernel.org Signed-off-by: Alexei Starovoitov --- tools/testing/selftests/bpf/veristat.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tools/testing/selftests/bpf/veristat.c b/tools/testing/selftests/bpf/veristat.c index f6f6a2490489..0da3ecf6ed52 100644 --- a/tools/testing/selftests/bpf/veristat.c +++ b/tools/testing/selftests/bpf/veristat.c @@ -723,7 +723,11 @@ static int cmp_prog_stats(const void *v1, const void *v2) return cmp; } - return 0; + /* always disambiguate with file+prog, which are unique */ + cmp = strcmp(s1->file_name, s2->file_name); + if (cmp != 0) + return cmp; + return strcmp(s1->prog_name, s2->prog_name); } #define HEADER_CHAR '-' From d68c07e2dd91c3e8bc451ecb218b77da2635cdd4 Mon Sep 17 00:00:00 2001 From: Andrii Nakryiko Date: Wed, 2 Nov 2022 22:52:59 -0700 Subject: [PATCH 0602/2002] selftests/bpf: allow to define asc/desc ordering for sort specs in veristat Allow to specify '^' at the end of stat name to designate that it should be sorted in ascending order. Similarly, allow any of 'v', 'V', '.', '!', or '_' suffix "symbols" to designate descending order. It's such a zoo for descending order because there is no single intuitive symbol that could be used (using 'v' looks pretty weird in practice), so few symbols that are "downwards leaning or pointing" were chosen. Either way, it shouldn't cause any troubles in practice. This new feature allows to customize sortering order to match user's needs. Signed-off-by: Andrii Nakryiko Link: https://lore.kernel.org/r/20221103055304.2904589-6-andrii@kernel.org Signed-off-by: Alexei Starovoitov --- tools/testing/selftests/bpf/veristat.c | 65 +++++++++++++++++++------- 1 file changed, 49 insertions(+), 16 deletions(-) diff --git a/tools/testing/selftests/bpf/veristat.c b/tools/testing/selftests/bpf/veristat.c index 0da3ecf6ed52..56ba55156abb 100644 --- a/tools/testing/selftests/bpf/veristat.c +++ b/tools/testing/selftests/bpf/veristat.c @@ -419,32 +419,65 @@ static struct stat_def { [MARK_READ_MAX_LEN] = { "Max mark read length", {"max_mark_read_len", "mark_read"}, }, }; +static bool parse_stat_id(const char *name, size_t len, int *id) +{ + int i, j; + + for (i = 0; i < ARRAY_SIZE(stat_defs); i++) { + struct stat_def *def = &stat_defs[i]; + + for (j = 0; j < ARRAY_SIZE(stat_defs[i].names); j++) { + + if (!def->names[j] || + strlen(def->names[j]) != len || + strncmp(def->names[j], name, len) != 0) + continue; + + *id = i; + return true; + } + } + + return false; +} + +static bool is_asc_sym(char c) +{ + return c == '^'; +} + +static bool is_desc_sym(char c) +{ + return c == 'v' || c == 'V' || c == '.' || c == '!' || c == '_'; +} + static int parse_stat(const char *stat_name, struct stat_specs *specs) { - int id, i; + int id; + bool has_order = false, is_asc = false; + size_t len = strlen(stat_name); if (specs->spec_cnt >= ARRAY_SIZE(specs->ids)) { fprintf(stderr, "Can't specify more than %zd stats\n", ARRAY_SIZE(specs->ids)); return -E2BIG; } - for (id = 0; id < ARRAY_SIZE(stat_defs); id++) { - struct stat_def *def = &stat_defs[id]; - - for (i = 0; i < ARRAY_SIZE(stat_defs[id].names); i++) { - if (!def->names[i] || strcmp(def->names[i], stat_name) != 0) - continue; - - specs->ids[specs->spec_cnt] = id; - specs->asc[specs->spec_cnt] = def->asc_by_default; - specs->spec_cnt++; - - return 0; - } + if (len > 1 && (is_asc_sym(stat_name[len - 1]) || is_desc_sym(stat_name[len - 1]))) { + has_order = true; + is_asc = is_asc_sym(stat_name[len - 1]); + len -= 1; } - fprintf(stderr, "Unrecognized stat name '%s'\n", stat_name); - return -ESRCH; + if (!parse_stat_id(stat_name, len, &id)) { + fprintf(stderr, "Unrecognized stat name '%s'\n", stat_name); + return -ESRCH; + } + + specs->ids[specs->spec_cnt] = id; + specs->asc[specs->spec_cnt] = has_order ? is_asc : stat_defs[id].asc_by_default; + specs->spec_cnt++; + + return 0; } static int parse_stats(const char *stats_str, struct stat_specs *specs) From 1bb4ec815015609c9458d5ffeb5c8cc95b7d44d6 Mon Sep 17 00:00:00 2001 From: Andrii Nakryiko Date: Wed, 2 Nov 2022 22:53:00 -0700 Subject: [PATCH 0603/2002] selftests/bpf: support simple filtering of stats in veristat Define simple expressions to filter not just by file and program name, but also by resulting values of collected stats. Support usual equality and inequality operators. Verdict, which is a boolean-like field can be also filtered either as 0/1, failure/success (with f/s, fail/succ, and failure/success aliases) symbols, or as false/true (f/t). Aliases are case insensitive. Currently this filtering is honored only in verification and replay modes. Comparison mode support will be added in next patch. Here's an example of verifying a bunch of BPF object files and emitting only results for successfully validated programs that have more than 100 total instructions processed by BPF verifier, sorted by number of instructions in ascending order: $ sudo ./veristat *.bpf.o -s insns^ -f 'insns>100' There can be many filters (both allow and deny flavors), all of them are combined. Signed-off-by: Andrii Nakryiko Link: https://lore.kernel.org/r/20221103055304.2904589-7-andrii@kernel.org Signed-off-by: Alexei Starovoitov --- tools/testing/selftests/bpf/veristat.c | 158 ++++++++++++++++++++++++- 1 file changed, 157 insertions(+), 1 deletion(-) diff --git a/tools/testing/selftests/bpf/veristat.c b/tools/testing/selftests/bpf/veristat.c index 56ba55156abb..37e512d233a7 100644 --- a/tools/testing/selftests/bpf/veristat.c +++ b/tools/testing/selftests/bpf/veristat.c @@ -54,10 +54,30 @@ enum resfmt { RESFMT_CSV, }; +enum filter_kind { + FILTER_NAME, + FILTER_STAT, +}; + +enum operator_kind { + OP_EQ, /* == or = */ + OP_NEQ, /* != or <> */ + OP_LT, /* < */ + OP_LE, /* <= */ + OP_GT, /* > */ + OP_GE, /* >= */ +}; + struct filter { + enum filter_kind kind; + /* FILTER_NAME */ char *any_glob; char *file_glob; char *prog_glob; + /* FILTER_STAT */ + enum operator_kind op; + int stat_id; + long value; }; static struct env { @@ -271,6 +291,8 @@ static bool should_process_file_prog(const char *filename, const char *prog_name for (i = 0; i < env.deny_filter_cnt; i++) { f = &env.deny_filters[i]; + if (f->kind != FILTER_NAME) + continue; if (f->any_glob && glob_matches(filename, f->any_glob)) return false; @@ -284,8 +306,10 @@ static bool should_process_file_prog(const char *filename, const char *prog_name for (i = 0; i < env.allow_filter_cnt; i++) { f = &env.allow_filters[i]; - allow_cnt++; + if (f->kind != FILTER_NAME) + continue; + allow_cnt++; if (f->any_glob) { if (glob_matches(filename, f->any_glob)) return true; @@ -306,11 +330,32 @@ static bool should_process_file_prog(const char *filename, const char *prog_name return allow_cnt == 0; } +static struct { + enum operator_kind op_kind; + const char *op_str; +} operators[] = { + /* Order of these definitions matter to avoid situations like '<' + * matching part of what is actually a '<>' operator. That is, + * substrings should go last. + */ + { OP_EQ, "==" }, + { OP_NEQ, "!=" }, + { OP_NEQ, "<>" }, + { OP_LE, "<=" }, + { OP_LT, "<" }, + { OP_GE, ">=" }, + { OP_GT, ">" }, + { OP_EQ, "=" }, +}; + +static bool parse_stat_id(const char *name, size_t len, int *id); + static int append_filter(struct filter **filters, int *cnt, const char *str) { struct filter *f; void *tmp; const char *p; + int i; tmp = realloc(*filters, (*cnt + 1) * sizeof(**filters)); if (!tmp) @@ -320,6 +365,67 @@ static int append_filter(struct filter **filters, int *cnt, const char *str) f = &(*filters)[*cnt]; memset(f, 0, sizeof(*f)); + /* First, let's check if it's a stats filter of the following form: + * is one of supported numerical stats (verdict is also + * considered numerical, failure == 0, success == 1); + * - is comparison operator (see `operators` definitions); + * - is an integer (or failure/success, or false/true as + * special aliases for 0 and 1, respectively). + * If the form doesn't match what user provided, we assume file/prog + * glob filter. + */ + for (i = 0; i < ARRAY_SIZE(operators); i++) { + int id; + long val; + const char *end = str; + const char *op_str; + + op_str = operators[i].op_str; + p = strstr(str, op_str); + if (!p) + continue; + + if (!parse_stat_id(str, p - str, &id)) { + fprintf(stderr, "Unrecognized stat name in '%s'!\n", str); + return -EINVAL; + } + if (id >= FILE_NAME) { + fprintf(stderr, "Non-integer stat is specified in '%s'!\n", str); + return -EINVAL; + } + + p += strlen(op_str); + + if (strcasecmp(p, "true") == 0 || + strcasecmp(p, "t") == 0 || + strcasecmp(p, "success") == 0 || + strcasecmp(p, "succ") == 0 || + strcasecmp(p, "s") == 0) { + val = 1; + } else if (strcasecmp(p, "false") == 0 || + strcasecmp(p, "f") == 0 || + strcasecmp(p, "failure") == 0 || + strcasecmp(p, "fail") == 0) { + val = 0; + } else { + errno = 0; + val = strtol(p, (char **)&end, 10); + if (errno || end == p || *end != '\0' ) { + fprintf(stderr, "Invalid integer value in '%s'!\n", str); + return -EINVAL; + } + } + + f->kind = FILTER_STAT; + f->stat_id = id; + f->op = operators[i].op_kind; + f->value = val; + + *cnt += 1; + return 0; + } + /* File/prog filter can be specified either as '' or * '/'. In the former case is applied to * both file and program names. This seems to be way more useful in @@ -328,6 +434,7 @@ static int append_filter(struct filter **filters, int *cnt, const char *str) * name. But usually common seems to be the most useful and * ergonomic way. */ + f->kind = FILTER_NAME; p = strchr(str, '/'); if (!p) { f->any_glob = strdup(str); @@ -1317,6 +1424,51 @@ one_more_time: return 0; } +static bool is_stat_filter_matched(struct filter *f, const struct verif_stats *stats) +{ + long value = stats->stats[f->stat_id]; + + switch (f->op) { + case OP_EQ: return value == f->value; + case OP_NEQ: return value != f->value; + case OP_LT: return value < f->value; + case OP_LE: return value <= f->value; + case OP_GT: return value > f->value; + case OP_GE: return value >= f->value; + } + + fprintf(stderr, "BUG: unknown filter op %d!\n", f->op); + return false; +} + +static bool should_output_stats(const struct verif_stats *stats) +{ + struct filter *f; + int i, allow_cnt = 0; + + for (i = 0; i < env.deny_filter_cnt; i++) { + f = &env.deny_filters[i]; + if (f->kind != FILTER_STAT) + continue; + + if (is_stat_filter_matched(f, stats)) + return false; + } + + for (i = 0; i < env.allow_filter_cnt; i++) { + f = &env.allow_filters[i]; + if (f->kind != FILTER_STAT) + continue; + allow_cnt++; + + if (is_stat_filter_matched(f, stats)) + return true; + } + + /* if there are no stat allowed filters, pass everything through */ + return allow_cnt == 0; +} + static void output_prog_stats(void) { const struct verif_stats *stats; @@ -1327,6 +1479,8 @@ static void output_prog_stats(void) output_headers(RESFMT_TABLE_CALCLEN); for (i = 0; i < env.prog_stat_cnt; i++) { stats = &env.prog_stats[i]; + if (!should_output_stats(stats)) + continue; output_stats(stats, RESFMT_TABLE_CALCLEN, false); last_stat_idx = i; } @@ -1336,6 +1490,8 @@ static void output_prog_stats(void) output_headers(env.out_fmt); for (i = 0; i < env.prog_stat_cnt; i++) { stats = &env.prog_stats[i]; + if (!should_output_stats(stats)) + continue; output_stats(stats, env.out_fmt, i == last_stat_idx); } } From 77534401d69c2a35d2a53e599fafb5f0f604e45d Mon Sep 17 00:00:00 2001 From: Andrii Nakryiko Date: Wed, 2 Nov 2022 22:53:01 -0700 Subject: [PATCH 0604/2002] selftests/bpf: make veristat emit all stats in CSV mode by default Make veristat distinguish between table and CSV output formats and use different default set of stats (columns) that are emitted. While for human-readable table output it doesn't make sense to output all known stats, it is very useful for CSV mode to record all possible data, so that it can later be queried and filtered in replay or comparison mode. Signed-off-by: Andrii Nakryiko Link: https://lore.kernel.org/r/20221103055304.2904589-8-andrii@kernel.org Signed-off-by: Alexei Starovoitov --- tools/testing/selftests/bpf/veristat.c | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/tools/testing/selftests/bpf/veristat.c b/tools/testing/selftests/bpf/veristat.c index 37e512d233a7..ec1a8ba7791c 100644 --- a/tools/testing/selftests/bpf/veristat.c +++ b/tools/testing/selftests/bpf/veristat.c @@ -501,6 +501,15 @@ static const struct stat_specs default_output_spec = { }, }; +static const struct stat_specs default_csv_output_spec = { + .spec_cnt = 9, + .ids = { + FILE_NAME, PROG_NAME, VERDICT, DURATION, + TOTAL_INSNS, TOTAL_STATES, PEAK_STATES, + MAX_STATES_PER_INSN, MARK_READ_MAX_LEN, + }, +}; + static const struct stat_specs default_sort_spec = { .spec_cnt = 2, .ids = { @@ -1561,8 +1570,12 @@ int main(int argc, char **argv) if (env.verbose && env.log_level == 0) env.log_level = 1; - if (env.output_spec.spec_cnt == 0) - env.output_spec = default_output_spec; + if (env.output_spec.spec_cnt == 0) { + if (env.out_fmt == RESFMT_CSV) + env.output_spec = default_csv_output_spec; + else + env.output_spec = default_output_spec; + } if (env.sort_spec.spec_cnt == 0) env.sort_spec = default_sort_spec; From a5710848d824168b5f7f02aa3689e648c46b2e46 Mon Sep 17 00:00:00 2001 From: Andrii Nakryiko Date: Wed, 2 Nov 2022 22:53:02 -0700 Subject: [PATCH 0605/2002] selftests/bpf: handle missing records in comparison mode better in veristat When comparing two datasets, if either side is missing corresponding record with the same file and prog name, currently veristat emits misleading zeros/failures, and even tried to calculate a difference, even though there is no data to compare against. This patch improves internal logic of handling such situations. Now we'll emit "N/A" in places where data is missing and comparison is non-sensical. As an example, in an artificially truncated and mismatched Cilium results, the output looks like below: $ ./veristat -e file,prog,verdict,insns -C ~/base.csv ~/comp.csv File Program Verdict (A) Verdict (B) Verdict (DIFF) Insns (A) Insns (B) Insns (DIFF) ------------------ ------------------------------ ----------- ----------- -------------- --------- --------- -------------- bpf_alignchecker.o __send_drop_notify success N/A N/A 53 N/A N/A bpf_alignchecker.o tail_icmp6_handle_ns failure failure MATCH 33 33 +0 (+0.00%) bpf_alignchecker.o tail_icmp6_send_echo_reply N/A failure N/A N/A 74 N/A bpf_host.o __send_drop_notify success N/A N/A 53 N/A N/A bpf_host.o cil_from_host success N/A N/A 762 N/A N/A bpf_xdp.o __send_drop_notify success success MATCH 151 151 +0 (+0.00%) bpf_xdp.o cil_xdp_entry success success MATCH 423 423 +0 (+0.00%) bpf_xdp.o tail_handle_nat_fwd_ipv4 success success MATCH 21547 20920 -627 (-2.91%) bpf_xdp.o tail_handle_nat_fwd_ipv6 success success MATCH 16974 17039 +65 (+0.38%) bpf_xdp.o tail_lb_ipv4 success success MATCH 71736 73430 +1694 (+2.36%) bpf_xdp.o tail_lb_ipv6 N/A success N/A N/A 151895 N/A bpf_xdp.o tail_nodeport_ipv4_dsr N/A success N/A N/A 1162 N/A bpf_xdp.o tail_nodeport_ipv6_dsr N/A success N/A N/A 1206 N/A bpf_xdp.o tail_nodeport_nat_egress_ipv4 N/A success N/A N/A 15619 N/A bpf_xdp.o tail_nodeport_nat_ingress_ipv4 success success MATCH 7658 7713 +55 (+0.72%) bpf_xdp.o tail_nodeport_nat_ingress_ipv6 success success MATCH 6405 6397 -8 (-0.12%) bpf_xdp.o tail_nodeport_nat_ipv6_egress failure failure MATCH 752 752 +0 (+0.00%) bpf_xdp.o tail_rev_nodeport_lb4 success success MATCH 7126 6934 -192 (-2.69%) bpf_xdp.o tail_rev_nodeport_lb6 success success MATCH 17954 17905 -49 (-0.27%) ------------------ ------------------------------ ----------- ----------- -------------- --------- --------- -------------- Internally veristat now separates joining two datasets and remembering the join, and actually emitting a comparison view. This will come handy when we add support for filtering and custom ordering in comparison mode. Signed-off-by: Andrii Nakryiko Link: https://lore.kernel.org/r/20221103055304.2904589-9-andrii@kernel.org Signed-off-by: Alexei Starovoitov --- tools/testing/selftests/bpf/veristat.c | 147 ++++++++++++++++++------- 1 file changed, 110 insertions(+), 37 deletions(-) diff --git a/tools/testing/selftests/bpf/veristat.c b/tools/testing/selftests/bpf/veristat.c index ec1a8ba7791c..5a9568a8c0bf 100644 --- a/tools/testing/selftests/bpf/veristat.c +++ b/tools/testing/selftests/bpf/veristat.c @@ -41,6 +41,15 @@ struct verif_stats { long stats[NUM_STATS_CNT]; }; +/* joined comparison mode stats */ +struct verif_stats_join { + char *file_name; + char *prog_name; + + const struct verif_stats *stats_a; + const struct verif_stats *stats_b; +}; + struct stat_specs { int spec_cnt; enum stat_id ids[ALL_STATS_CNT]; @@ -97,6 +106,9 @@ static struct env { struct verif_stats *baseline_stats; int baseline_stat_cnt; + struct verif_stats_join *join_stats; + int join_stat_cnt; + struct stat_specs output_spec; struct stat_specs sort_spec; @@ -518,6 +530,15 @@ static const struct stat_specs default_sort_spec = { .asc = { true, true, }, }; +/* sorting for comparison mode to join two data sets */ +static const struct stat_specs join_sort_spec = { + .spec_cnt = 2, + .ids = { + FILE_NAME, PROG_NAME, + }, + .asc = { true, true, }, +}; + static struct stat_def { const char *header; const char *names[4]; @@ -934,13 +955,16 @@ static void prepare_value(const struct verif_stats *s, enum stat_id id, { switch (id) { case FILE_NAME: - *str = s->file_name; + *str = s ? s->file_name : "N/A"; break; case PROG_NAME: - *str = s->prog_name; + *str = s ? s->prog_name : "N/A"; break; case VERDICT: - *str = s->stats[VERDICT] ? "success" : "failure"; + if (!s) + *str = "N/A"; + else + *str = s->stats[VERDICT] ? "success" : "failure"; break; case DURATION: case TOTAL_INSNS: @@ -948,7 +972,7 @@ static void prepare_value(const struct verif_stats *s, enum stat_id id, case PEAK_STATES: case MAX_STATES_PER_INSN: case MARK_READ_MAX_LEN: - *val = s->stats[id]; + *val = s ? s->stats[id] : 0; break; default: fprintf(stderr, "Unrecognized stat #%d\n", id); @@ -1223,9 +1247,11 @@ static void output_comp_headers(enum resfmt fmt) output_comp_header_underlines(); } -static void output_comp_stats(const struct verif_stats *base, const struct verif_stats *comp, +static void output_comp_stats(const struct verif_stats_join *join_stats, enum resfmt fmt, bool last) { + const struct verif_stats *base = join_stats->stats_a; + const struct verif_stats *comp = join_stats->stats_b; char base_buf[1024] = {}, comp_buf[1024] = {}, diff_buf[1024] = {}; int i; @@ -1243,33 +1269,45 @@ static void output_comp_stats(const struct verif_stats *base, const struct verif /* normalize all the outputs to be in string buffers for simplicity */ if (is_key_stat(id)) { /* key stats (file and program name) are always strings */ - if (base != &fallback_stats) + if (base) snprintf(base_buf, sizeof(base_buf), "%s", base_str); else snprintf(base_buf, sizeof(base_buf), "%s", comp_str); } else if (base_str) { snprintf(base_buf, sizeof(base_buf), "%s", base_str); snprintf(comp_buf, sizeof(comp_buf), "%s", comp_str); - if (strcmp(base_str, comp_str) == 0) + if (!base || !comp) + snprintf(diff_buf, sizeof(diff_buf), "%s", "N/A"); + else if (strcmp(base_str, comp_str) == 0) snprintf(diff_buf, sizeof(diff_buf), "%s", "MATCH"); else snprintf(diff_buf, sizeof(diff_buf), "%s", "MISMATCH"); } else { double p = 0.0; - snprintf(base_buf, sizeof(base_buf), "%ld", base_val); - snprintf(comp_buf, sizeof(comp_buf), "%ld", comp_val); + if (base) + snprintf(base_buf, sizeof(base_buf), "%ld", base_val); + else + snprintf(base_buf, sizeof(base_buf), "%s", "N/A"); + if (comp) + snprintf(comp_buf, sizeof(comp_buf), "%ld", comp_val); + else + snprintf(comp_buf, sizeof(comp_buf), "%s", "N/A"); diff_val = comp_val - base_val; - if (base == &fallback_stats || comp == &fallback_stats || base_val == 0) { - if (comp_val == base_val) - p = 0.0; /* avoid +0 (+100%) case */ - else - p = comp_val < base_val ? -100.0 : 100.0; + if (!base || !comp) { + snprintf(diff_buf, sizeof(diff_buf), "%s", "N/A"); } else { - p = diff_val * 100.0 / base_val; + if (base_val == 0) { + if (comp_val == base_val) + p = 0.0; /* avoid +0 (+100%) case */ + else + p = comp_val < base_val ? -100.0 : 100.0; + } else { + p = diff_val * 100.0 / base_val; + } + snprintf(diff_buf, sizeof(diff_buf), "%+ld (%+.2lf%%)", diff_val, p); } - snprintf(diff_buf, sizeof(diff_buf), "%+ld (%+.2lf%%)", diff_val, p); } switch (fmt) { @@ -1328,6 +1366,7 @@ static int cmp_stats_key(const struct verif_stats *base, const struct verif_stat static int handle_comparison_mode(void) { struct stat_specs base_specs = {}, comp_specs = {}; + struct stat_specs tmp_sort_spec; enum resfmt cur_fmt; int err, i, j; @@ -1370,31 +1409,26 @@ static int handle_comparison_mode(void) } } + /* Replace user-specified sorting spec with file+prog sorting rule to + * be able to join two datasets correctly. Once we are done, we will + * restore the original sort spec. + */ + tmp_sort_spec = env.sort_spec; + env.sort_spec = join_sort_spec; qsort(env.prog_stats, env.prog_stat_cnt, sizeof(*env.prog_stats), cmp_prog_stats); qsort(env.baseline_stats, env.baseline_stat_cnt, sizeof(*env.baseline_stats), cmp_prog_stats); + env.sort_spec = tmp_sort_spec; - /* for human-readable table output we need to do extra pass to - * calculate column widths, so we substitute current output format - * with RESFMT_TABLE_CALCLEN and later revert it back to RESFMT_TABLE - * and do everything again. - */ - if (env.out_fmt == RESFMT_TABLE) - cur_fmt = RESFMT_TABLE_CALCLEN; - else - cur_fmt = env.out_fmt; - -one_more_time: - output_comp_headers(cur_fmt); - - /* If baseline and comparison datasets have different subset of rows - * (we match by 'object + prog' as a unique key) then assume - * empty/missing/zero value for rows that are missing in the opposite - * data set + /* Join two datasets together. If baseline and comparison datasets + * have different subset of rows (we match by 'object + prog' as + * a unique key) then assume empty/missing/zero value for rows that + * are missing in the opposite data set. */ i = j = 0; while (i < env.baseline_stat_cnt || j < env.prog_stat_cnt) { - bool last = (i == env.baseline_stat_cnt - 1) || (j == env.prog_stat_cnt - 1); const struct verif_stats *base, *comp; + struct verif_stats_join *join; + void *tmp; int r; base = i < env.baseline_stat_cnt ? &env.baseline_stats[i] : &fallback_stats; @@ -1411,18 +1445,56 @@ one_more_time: return -EINVAL; } + tmp = realloc(env.join_stats, (env.join_stat_cnt + 1) * sizeof(*env.join_stats)); + if (!tmp) + return -ENOMEM; + env.join_stats = tmp; + + join = &env.join_stats[env.join_stat_cnt]; + memset(join, 0, sizeof(*join)); + r = cmp_stats_key(base, comp); if (r == 0) { - output_comp_stats(base, comp, cur_fmt, last); + join->file_name = base->file_name; + join->prog_name = base->prog_name; + join->stats_a = base; + join->stats_b = comp; i++; j++; } else if (comp == &fallback_stats || r < 0) { - output_comp_stats(base, &fallback_stats, cur_fmt, last); + join->file_name = base->file_name; + join->prog_name = base->prog_name; + join->stats_a = base; + join->stats_b = NULL; i++; } else { - output_comp_stats(&fallback_stats, comp, cur_fmt, last); + join->file_name = comp->file_name; + join->prog_name = comp->prog_name; + join->stats_a = NULL; + join->stats_b = comp; j++; } + env.join_stat_cnt += 1; + } + + /* for human-readable table output we need to do extra pass to + * calculate column widths, so we substitute current output format + * with RESFMT_TABLE_CALCLEN and later revert it back to RESFMT_TABLE + * and do everything again. + */ + if (env.out_fmt == RESFMT_TABLE) + cur_fmt = RESFMT_TABLE_CALCLEN; + else + cur_fmt = env.out_fmt; + +one_more_time: + output_comp_headers(cur_fmt); + + for (i = 0; i < env.join_stat_cnt; i++) { + const struct verif_stats_join *join = &env.join_stats[i]; + bool last = i == env.join_stat_cnt - 1; + + output_comp_stats(join, cur_fmt, last); } if (cur_fmt == RESFMT_TABLE_CALCLEN) { @@ -1594,6 +1666,7 @@ int main(int argc, char **argv) free_verif_stats(env.prog_stats, env.prog_stat_cnt); free_verif_stats(env.baseline_stats, env.baseline_stat_cnt); + free(env.join_stats); for (i = 0; i < env.filename_cnt; i++) free(env.filenames[i]); free(env.filenames); From fa9bb590c2895b14c9da46ba1860d06efba55657 Mon Sep 17 00:00:00 2001 From: Andrii Nakryiko Date: Wed, 2 Nov 2022 22:53:03 -0700 Subject: [PATCH 0606/2002] selftests/bpf: support stats ordering in comparison mode in veristat Introduce the concept of "stat variant", by which it's possible to specify whether to use the value from A (baseline) side, B (comparison or control) side, the absolute difference value or relative (percentage) difference value. To support specifying this, veristat recognizes `_a`, `_b`, `_diff`, `_pct` suffixes, which can be appended to stat name(s). In non-comparison mode variants are ignored (there is only `_a` variant effectively), if no variant suffix is provided, `_b` is assumed, as control group is of primary interest in comparison mode. These stat variants can be flexibly combined with asc/desc orders. Here's an example of ordering results first by verdict match/mismatch (or n/a if one of the sides is missing; n/a is always considered to be the lowest value), and within each match/mismatch/n/a group further sort by number of instructions in B side. In this case we don't have MISMATCH cases, but N/A are split from MATCH, demonstrating this custom ordering. $ ./veristat -e file,prog,verdict,insns -s verdict_diff,insns_b_ -C ~/base.csv ~/comp.csv File Program Verdict (A) Verdict (B) Verdict (DIFF) Insns (A) Insns (B) Insns (DIFF) ------------------ ------------------------------ ----------- ----------- -------------- --------- --------- -------------- bpf_xdp.o tail_lb_ipv6 N/A success N/A N/A 151895 N/A bpf_xdp.o tail_nodeport_nat_egress_ipv4 N/A success N/A N/A 15619 N/A bpf_xdp.o tail_nodeport_ipv6_dsr N/A success N/A N/A 1206 N/A bpf_xdp.o tail_nodeport_ipv4_dsr N/A success N/A N/A 1162 N/A bpf_alignchecker.o tail_icmp6_send_echo_reply N/A failure N/A N/A 74 N/A bpf_alignchecker.o __send_drop_notify success N/A N/A 53 N/A N/A bpf_host.o __send_drop_notify success N/A N/A 53 N/A N/A bpf_host.o cil_from_host success N/A N/A 762 N/A N/A bpf_xdp.o tail_lb_ipv4 success success MATCH 71736 73430 +1694 (+2.36%) bpf_xdp.o tail_handle_nat_fwd_ipv4 success success MATCH 21547 20920 -627 (-2.91%) bpf_xdp.o tail_rev_nodeport_lb6 success success MATCH 17954 17905 -49 (-0.27%) bpf_xdp.o tail_handle_nat_fwd_ipv6 success success MATCH 16974 17039 +65 (+0.38%) bpf_xdp.o tail_nodeport_nat_ingress_ipv4 success success MATCH 7658 7713 +55 (+0.72%) bpf_xdp.o tail_rev_nodeport_lb4 success success MATCH 7126 6934 -192 (-2.69%) bpf_xdp.o tail_nodeport_nat_ingress_ipv6 success success MATCH 6405 6397 -8 (-0.12%) bpf_xdp.o tail_nodeport_nat_ipv6_egress failure failure MATCH 752 752 +0 (+0.00%) bpf_xdp.o cil_xdp_entry success success MATCH 423 423 +0 (+0.00%) bpf_xdp.o __send_drop_notify success success MATCH 151 151 +0 (+0.00%) bpf_alignchecker.o tail_icmp6_handle_ns failure failure MATCH 33 33 +0 (+0.00%) ------------------ ------------------------------ ----------- ----------- -------------- --------- --------- -------------- Signed-off-by: Andrii Nakryiko Link: https://lore.kernel.org/r/20221103055304.2904589-10-andrii@kernel.org Signed-off-by: Alexei Starovoitov --- tools/testing/selftests/bpf/veristat.c | 194 +++++++++++++++++++++++-- 1 file changed, 183 insertions(+), 11 deletions(-) diff --git a/tools/testing/selftests/bpf/veristat.c b/tools/testing/selftests/bpf/veristat.c index 5a9568a8c0bf..f2ea825ee80a 100644 --- a/tools/testing/selftests/bpf/veristat.c +++ b/tools/testing/selftests/bpf/veristat.c @@ -17,6 +17,7 @@ #include #include #include +#include enum stat_id { VERDICT, @@ -34,6 +35,45 @@ enum stat_id { NUM_STATS_CNT = FILE_NAME - VERDICT, }; +/* In comparison mode each stat can specify up to four different values: + * - A side value; + * - B side value; + * - absolute diff value; + * - relative (percentage) diff value. + * + * When specifying stat specs in comparison mode, user can use one of the + * following variant suffixes to specify which exact variant should be used for + * ordering or filtering: + * - `_a` for A side value; + * - `_b` for B side value; + * - `_diff` for absolute diff value; + * - `_pct` for relative (percentage) diff value. + * + * If no variant suffix is provided, then `_b` (control data) is assumed. + * + * As an example, let's say instructions stat has the following output: + * + * Insns (A) Insns (B) Insns (DIFF) + * --------- --------- -------------- + * 21547 20920 -627 (-2.91%) + * + * Then: + * - 21547 is A side value (insns_a); + * - 20920 is B side value (insns_b); + * - -627 is absolute diff value (insns_diff); + * - -2.91% is relative diff value (insns_pct). + * + * For verdict there is no verdict_pct variant. + * For file and program name, _a and _b variants are equivalent and there are + * no _diff or _pct variants. + */ +enum stat_variant { + VARIANT_A, + VARIANT_B, + VARIANT_DIFF, + VARIANT_PCT, +}; + struct verif_stats { char *file_name; char *prog_name; @@ -53,6 +93,7 @@ struct verif_stats_join { struct stat_specs { int spec_cnt; enum stat_id ids[ALL_STATS_CNT]; + enum stat_variant variants[ALL_STATS_CNT]; bool asc[ALL_STATS_CNT]; int lens[ALL_STATS_CNT * 3]; /* 3x for comparison mode */ }; @@ -86,6 +127,7 @@ struct filter { /* FILTER_STAT */ enum operator_kind op; int stat_id; + enum stat_variant stat_var; long value; }; @@ -360,7 +402,7 @@ static struct { { OP_EQ, "=" }, }; -static bool parse_stat_id(const char *name, size_t len, int *id); +static bool parse_stat_id_var(const char *name, size_t len, int *id, enum stat_variant *var); static int append_filter(struct filter **filters, int *cnt, const char *str) { @@ -388,6 +430,7 @@ static int append_filter(struct filter **filters, int *cnt, const char *str) * glob filter. */ for (i = 0; i < ARRAY_SIZE(operators); i++) { + enum stat_variant var; int id; long val; const char *end = str; @@ -398,7 +441,7 @@ static int append_filter(struct filter **filters, int *cnt, const char *str) if (!p) continue; - if (!parse_stat_id(str, p - str, &id)) { + if (!parse_stat_id_var(str, p - str, &id, &var)) { fprintf(stderr, "Unrecognized stat name in '%s'!\n", str); return -EINVAL; } @@ -431,6 +474,7 @@ static int append_filter(struct filter **filters, int *cnt, const char *str) f->kind = FILTER_STAT; f->stat_id = id; + f->stat_var = var; f->op = operators[i].op_kind; f->value = val; @@ -556,22 +600,52 @@ static struct stat_def { [MARK_READ_MAX_LEN] = { "Max mark read length", {"max_mark_read_len", "mark_read"}, }, }; -static bool parse_stat_id(const char *name, size_t len, int *id) +static bool parse_stat_id_var(const char *name, size_t len, int *id, enum stat_variant *var) { - int i, j; + static const char *var_sfxs[] = { + [VARIANT_A] = "_a", + [VARIANT_B] = "_b", + [VARIANT_DIFF] = "_diff", + [VARIANT_PCT] = "_pct", + }; + int i, j, k; for (i = 0; i < ARRAY_SIZE(stat_defs); i++) { struct stat_def *def = &stat_defs[i]; + size_t alias_len, sfx_len; + const char *alias; for (j = 0; j < ARRAY_SIZE(stat_defs[i].names); j++) { - - if (!def->names[j] || - strlen(def->names[j]) != len || - strncmp(def->names[j], name, len) != 0) + alias = def->names[j]; + if (!alias) continue; - *id = i; - return true; + alias_len = strlen(alias); + if (strncmp(name, alias, alias_len) != 0) + continue; + + if (alias_len == len) { + /* If no variant suffix is specified, we + * assume control group (just in case we are + * in comparison mode. Variant is ignored in + * non-comparison mode. + */ + *var = VARIANT_B; + *id = i; + return true; + } + + for (k = 0; k < ARRAY_SIZE(var_sfxs); k++) { + sfx_len = strlen(var_sfxs[k]); + if (alias_len + sfx_len != len) + continue; + + if (strncmp(name + alias_len, var_sfxs[k], sfx_len) == 0) { + *var = (enum stat_variant)k; + *id = i; + return true; + } + } } } @@ -593,6 +667,7 @@ static int parse_stat(const char *stat_name, struct stat_specs *specs) int id; bool has_order = false, is_asc = false; size_t len = strlen(stat_name); + enum stat_variant var; if (specs->spec_cnt >= ARRAY_SIZE(specs->ids)) { fprintf(stderr, "Can't specify more than %zd stats\n", ARRAY_SIZE(specs->ids)); @@ -605,12 +680,13 @@ static int parse_stat(const char *stat_name, struct stat_specs *specs) len -= 1; } - if (!parse_stat_id(stat_name, len, &id)) { + if (!parse_stat_id_var(stat_name, len, &id, &var)) { fprintf(stderr, "Unrecognized stat name '%s'\n", stat_name); return -ESRCH; } specs->ids[specs->spec_cnt] = id; + specs->variants[specs->spec_cnt] = var; specs->asc[specs->spec_cnt] = has_order ? is_asc : stat_defs[id].asc_by_default; specs->spec_cnt++; @@ -900,6 +976,99 @@ static int cmp_prog_stats(const void *v1, const void *v2) return strcmp(s1->prog_name, s2->prog_name); } +static void fetch_join_stat_value(const struct verif_stats_join *s, + enum stat_id id, enum stat_variant var, + const char **str_val, + double *num_val) +{ + long v1, v2; + + if (id == FILE_NAME) { + *str_val = s->file_name; + return; + } + if (id == PROG_NAME) { + *str_val = s->prog_name; + return; + } + + v1 = s->stats_a ? s->stats_a->stats[id] : 0; + v2 = s->stats_b ? s->stats_b->stats[id] : 0; + + switch (var) { + case VARIANT_A: + if (!s->stats_a) + *num_val = -DBL_MAX; + else + *num_val = s->stats_a->stats[id]; + return; + case VARIANT_B: + if (!s->stats_b) + *num_val = -DBL_MAX; + else + *num_val = s->stats_b->stats[id]; + return; + case VARIANT_DIFF: + if (!s->stats_a || !s->stats_b) + *num_val = -DBL_MAX; + else + *num_val = (double)(v2 - v1); + return; + case VARIANT_PCT: + if (!s->stats_a || !s->stats_b) { + *num_val = -DBL_MAX; + } else if (v1 == 0) { + if (v1 == v2) + *num_val = 0.0; + else + *num_val = v2 < v1 ? -100.0 : 100.0; + } else { + *num_val = (v2 - v1) * 100.0 / v1; + } + return; + } +} + +static int cmp_join_stat(const struct verif_stats_join *s1, + const struct verif_stats_join *s2, + enum stat_id id, enum stat_variant var, bool asc) +{ + const char *str1 = NULL, *str2 = NULL; + double v1, v2; + int cmp = 0; + + fetch_join_stat_value(s1, id, var, &str1, &v1); + fetch_join_stat_value(s2, id, var, &str2, &v2); + + if (str1) + cmp = strcmp(str1, str2); + else if (v1 != v2) + cmp = v1 < v2 ? -1 : 1; + + return asc ? cmp : -cmp; +} + +static int cmp_join_stats(const void *v1, const void *v2) +{ + const struct verif_stats_join *s1 = v1, *s2 = v2; + int i, cmp; + + for (i = 0; i < env.sort_spec.spec_cnt; i++) { + cmp = cmp_join_stat(s1, s2, + env.sort_spec.ids[i], + env.sort_spec.variants[i], + env.sort_spec.asc[i]); + if (cmp != 0) + return cmp; + } + + /* always disambiguate with file+prog, which are unique */ + cmp = strcmp(s1->file_name, s2->file_name); + if (cmp != 0) + return cmp; + return strcmp(s1->prog_name, s2->prog_name); +} + #define HEADER_CHAR '-' #define COLUMN_SEP " " @@ -1477,6 +1646,9 @@ static int handle_comparison_mode(void) env.join_stat_cnt += 1; } + /* now sort joined results accorsing to sort spec */ + qsort(env.join_stats, env.join_stat_cnt, sizeof(*env.join_stats), cmp_join_stats); + /* for human-readable table output we need to do extra pass to * calculate column widths, so we substitute current output format * with RESFMT_TABLE_CALCLEN and later revert it back to RESFMT_TABLE From d5ce4b89234156d66ac8a59bddbc341667aadf86 Mon Sep 17 00:00:00 2001 From: Andrii Nakryiko Date: Wed, 2 Nov 2022 22:53:04 -0700 Subject: [PATCH 0607/2002] selftests/bpf: support stat filtering in comparison mode in veristat Finally add support for filtering stats values, similar to non-comparison mode filtering. For comparison mode 4 variants of stats are important for filtering, as they allow to filter either A or B side, but even more importantly they allow to filter based on value difference, and for verdict stat value difference is MATCH/MISMATCH classification. So with these changes it's finally possible to easily check if there were any mismatches between failure/success outcomes on two separate data sets. Like in an example below: $ ./veristat -e file,prog,verdict,insns -C ~/baseline-results.csv ~/shortest-results.csv -f verdict_diff=mismatch File Program Verdict (A) Verdict (B) Verdict (DIFF) Insns (A) Insns (B) Insns (DIFF) ------------------------------------- --------------------- ----------- ----------- -------------- --------- --------- ------------------- dynptr_success.bpf.linked1.o test_data_slice success failure MISMATCH 85 0 -85 (-100.00%) dynptr_success.bpf.linked1.o test_read_write success failure MISMATCH 1992 0 -1992 (-100.00%) dynptr_success.bpf.linked1.o test_ringbuf success failure MISMATCH 74 0 -74 (-100.00%) kprobe_multi.bpf.linked1.o test_kprobe failure success MISMATCH 0 246 +246 (+100.00%) kprobe_multi.bpf.linked1.o test_kprobe_manual failure success MISMATCH 0 246 +246 (+100.00%) kprobe_multi.bpf.linked1.o test_kretprobe failure success MISMATCH 0 248 +248 (+100.00%) kprobe_multi.bpf.linked1.o test_kretprobe_manual failure success MISMATCH 0 248 +248 (+100.00%) kprobe_multi.bpf.linked1.o trigger failure success MISMATCH 0 2 +2 (+100.00%) netcnt_prog.bpf.linked1.o bpf_nextcnt failure success MISMATCH 0 56 +56 (+100.00%) pyperf600_nounroll.bpf.linked1.o on_event success failure MISMATCH 568128 1000001 +431873 (+76.02%) ringbuf_bench.bpf.linked1.o bench_ringbuf success failure MISMATCH 8 0 -8 (-100.00%) strobemeta.bpf.linked1.o on_event success failure MISMATCH 557149 1000001 +442852 (+79.49%) strobemeta_nounroll1.bpf.linked1.o on_event success failure MISMATCH 57240 1000001 +942761 (+1647.03%) strobemeta_nounroll2.bpf.linked1.o on_event success failure MISMATCH 501725 1000001 +498276 (+99.31%) strobemeta_subprogs.bpf.linked1.o on_event success failure MISMATCH 65420 1000001 +934581 (+1428.59%) test_map_in_map_invalid.bpf.linked1.o xdp_noop0 success failure MISMATCH 2 0 -2 (-100.00%) test_mmap.bpf.linked1.o test_mmap success failure MISMATCH 46 0 -46 (-100.00%) test_verif_scale3.bpf.linked1.o balancer_ingress success failure MISMATCH 845499 1000001 +154502 (+18.27%) ------------------------------------- --------------------- ----------- ----------- -------------- --------- --------- ------------------- Note that by filtering on verdict_diff=mismatch, it's now extremely easy and fast to see any changes in verdict. Example above showcases both failure -> success transitions (which are generally surprising) and success -> failure transitions (which are expected if bugs are present). Given veristat allows to query relative percent difference values, internal logic for comparison mode is based on floating point numbers, so requires a bit of epsilon precision logic, deviating from typical integer simple handling rules. Signed-off-by: Andrii Nakryiko Link: https://lore.kernel.org/r/20221103055304.2904589-11-andrii@kernel.org Signed-off-by: Alexei Starovoitov --- tools/testing/selftests/bpf/veristat.c | 70 ++++++++++++++++++++++++-- 1 file changed, 65 insertions(+), 5 deletions(-) diff --git a/tools/testing/selftests/bpf/veristat.c b/tools/testing/selftests/bpf/veristat.c index f2ea825ee80a..9e3811ab4866 100644 --- a/tools/testing/selftests/bpf/veristat.c +++ b/tools/testing/selftests/bpf/veristat.c @@ -456,12 +456,16 @@ static int append_filter(struct filter **filters, int *cnt, const char *str) strcasecmp(p, "t") == 0 || strcasecmp(p, "success") == 0 || strcasecmp(p, "succ") == 0 || - strcasecmp(p, "s") == 0) { + strcasecmp(p, "s") == 0 || + strcasecmp(p, "match") == 0 || + strcasecmp(p, "m") == 0) { val = 1; } else if (strcasecmp(p, "false") == 0 || strcasecmp(p, "f") == 0 || strcasecmp(p, "failure") == 0 || - strcasecmp(p, "fail") == 0) { + strcasecmp(p, "fail") == 0 || + strcasecmp(p, "mismatch") == 0 || + strcasecmp(p, "mis") == 0) { val = 0; } else { errno = 0; @@ -1011,6 +1015,8 @@ static void fetch_join_stat_value(const struct verif_stats_join *s, case VARIANT_DIFF: if (!s->stats_a || !s->stats_b) *num_val = -DBL_MAX; + else if (id == VERDICT) + *num_val = v1 == v2 ? 1.0 /* MATCH */ : 0.0 /* MISMATCH */; else *num_val = (double)(v2 - v1); return; @@ -1532,12 +1538,61 @@ static int cmp_stats_key(const struct verif_stats *base, const struct verif_stat return strcmp(base->prog_name, comp->prog_name); } +static bool is_join_stat_filter_matched(struct filter *f, const struct verif_stats_join *stats) +{ + static const double eps = 1e-9; + const char *str = NULL; + double value = 0.0; + + fetch_join_stat_value(stats, f->stat_id, f->stat_var, &str, &value); + + switch (f->op) { + case OP_EQ: return value > f->value - eps && value < f->value + eps; + case OP_NEQ: return value < f->value - eps || value > f->value + eps; + case OP_LT: return value < f->value - eps; + case OP_LE: return value <= f->value + eps; + case OP_GT: return value > f->value + eps; + case OP_GE: return value >= f->value - eps; + } + + fprintf(stderr, "BUG: unknown filter op %d!\n", f->op); + return false; +} + +static bool should_output_join_stats(const struct verif_stats_join *stats) +{ + struct filter *f; + int i, allow_cnt = 0; + + for (i = 0; i < env.deny_filter_cnt; i++) { + f = &env.deny_filters[i]; + if (f->kind != FILTER_STAT) + continue; + + if (is_join_stat_filter_matched(f, stats)) + return false; + } + + for (i = 0; i < env.allow_filter_cnt; i++) { + f = &env.allow_filters[i]; + if (f->kind != FILTER_STAT) + continue; + allow_cnt++; + + if (is_join_stat_filter_matched(f, stats)) + return true; + } + + /* if there are no stat allowed filters, pass everything through */ + return allow_cnt == 0; +} + static int handle_comparison_mode(void) { struct stat_specs base_specs = {}, comp_specs = {}; struct stat_specs tmp_sort_spec; enum resfmt cur_fmt; - int err, i, j; + int err, i, j, last_idx; if (env.filename_cnt != 2) { fprintf(stderr, "Comparison mode expects exactly two input CSV files!\n\n"); @@ -1664,9 +1719,14 @@ one_more_time: for (i = 0; i < env.join_stat_cnt; i++) { const struct verif_stats_join *join = &env.join_stats[i]; - bool last = i == env.join_stat_cnt - 1; - output_comp_stats(join, cur_fmt, last); + if (!should_output_join_stats(join)) + continue; + + if (cur_fmt == RESFMT_TABLE_CALCLEN) + last_idx = i; + + output_comp_stats(join, cur_fmt, i == last_idx); } if (cur_fmt == RESFMT_TABLE_CALCLEN) { From db559117828d2448fe81ada051c60bcf39f822e9 Mon Sep 17 00:00:00 2001 From: Kumar Kartikeya Dwivedi Date: Fri, 4 Nov 2022 00:39:56 +0530 Subject: [PATCH 0608/2002] bpf: Consolidate spin_lock, timer management into btf_record Now that kptr_off_tab has been refactored into btf_record, and can hold more than one specific field type, accomodate bpf_spin_lock and bpf_timer as well. While they don't require any more metadata than offset, having all special fields in one place allows us to share the same code for allocated user defined types and handle both map values and these allocated objects in a similar fashion. As an optimization, we still keep spin_lock_off and timer_off offsets in the btf_record structure, just to avoid having to find the btf_field struct each time their offset is needed. This is mostly needed to manipulate such objects in a map value at runtime. It's ok to hardcode just one offset as more than one field is disallowed. Signed-off-by: Kumar Kartikeya Dwivedi Link: https://lore.kernel.org/r/20221103191013.1236066-8-memxor@gmail.com Signed-off-by: Alexei Starovoitov --- include/linux/bpf.h | 53 +++--- include/linux/btf.h | 3 +- kernel/bpf/arraymap.c | 19 +- kernel/bpf/bpf_local_storage.c | 2 +- kernel/bpf/btf.c | 327 ++++++++++++++++++--------------- kernel/bpf/hashtab.c | 24 +-- kernel/bpf/helpers.c | 6 +- kernel/bpf/local_storage.c | 2 +- kernel/bpf/map_in_map.c | 5 +- kernel/bpf/syscall.c | 135 ++++++-------- kernel/bpf/verifier.c | 82 +++------ net/core/bpf_sk_storage.c | 4 +- 12 files changed, 316 insertions(+), 346 deletions(-) diff --git a/include/linux/bpf.h b/include/linux/bpf.h index 5f2a42033a37..aae85019abde 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -166,13 +166,13 @@ struct bpf_map_ops { enum { /* Support at most 8 pointers in a BTF type */ - BTF_FIELDS_MAX = 8, - BPF_MAP_OFF_ARR_MAX = BTF_FIELDS_MAX + - 1 + /* for bpf_spin_lock */ - 1, /* for bpf_timer */ + BTF_FIELDS_MAX = 10, + BPF_MAP_OFF_ARR_MAX = BTF_FIELDS_MAX, }; enum btf_field_type { + BPF_SPIN_LOCK = (1 << 0), + BPF_TIMER = (1 << 1), BPF_KPTR_UNREF = (1 << 2), BPF_KPTR_REF = (1 << 3), BPF_KPTR = BPF_KPTR_UNREF | BPF_KPTR_REF, @@ -196,6 +196,8 @@ struct btf_field { struct btf_record { u32 cnt; u32 field_mask; + int spin_lock_off; + int timer_off; struct btf_field fields[]; }; @@ -220,10 +222,8 @@ struct bpf_map { u32 max_entries; u64 map_extra; /* any per-map-type extra fields */ u32 map_flags; - int spin_lock_off; /* >=0 valid offset, <0 error */ - struct btf_record *record; - int timer_off; /* >=0 valid offset, <0 error */ u32 id; + struct btf_record *record; int numa_node; u32 btf_key_type_id; u32 btf_value_type_id; @@ -257,9 +257,29 @@ struct bpf_map { bool frozen; /* write-once; write-protected by freeze_mutex */ }; +static inline const char *btf_field_type_name(enum btf_field_type type) +{ + switch (type) { + case BPF_SPIN_LOCK: + return "bpf_spin_lock"; + case BPF_TIMER: + return "bpf_timer"; + case BPF_KPTR_UNREF: + case BPF_KPTR_REF: + return "kptr"; + default: + WARN_ON_ONCE(1); + return "unknown"; + } +} + static inline u32 btf_field_type_size(enum btf_field_type type) { switch (type) { + case BPF_SPIN_LOCK: + return sizeof(struct bpf_spin_lock); + case BPF_TIMER: + return sizeof(struct bpf_timer); case BPF_KPTR_UNREF: case BPF_KPTR_REF: return sizeof(u64); @@ -272,6 +292,10 @@ static inline u32 btf_field_type_size(enum btf_field_type type) static inline u32 btf_field_type_align(enum btf_field_type type) { switch (type) { + case BPF_SPIN_LOCK: + return __alignof__(struct bpf_spin_lock); + case BPF_TIMER: + return __alignof__(struct bpf_timer); case BPF_KPTR_UNREF: case BPF_KPTR_REF: return __alignof__(u64); @@ -288,22 +312,8 @@ static inline bool btf_record_has_field(const struct btf_record *rec, enum btf_f return rec->field_mask & type; } -static inline bool map_value_has_spin_lock(const struct bpf_map *map) -{ - return map->spin_lock_off >= 0; -} - -static inline bool map_value_has_timer(const struct bpf_map *map) -{ - return map->timer_off >= 0; -} - static inline void check_and_init_map_value(struct bpf_map *map, void *dst) { - if (unlikely(map_value_has_spin_lock(map))) - memset(dst + map->spin_lock_off, 0, sizeof(struct bpf_spin_lock)); - if (unlikely(map_value_has_timer(map))) - memset(dst + map->timer_off, 0, sizeof(struct bpf_timer)); if (!IS_ERR_OR_NULL(map->record)) { struct btf_field *fields = map->record->fields; u32 cnt = map->record->cnt; @@ -1740,6 +1750,7 @@ void btf_record_free(struct btf_record *rec); void bpf_map_free_record(struct bpf_map *map); struct btf_record *btf_record_dup(const struct btf_record *rec); bool btf_record_equal(const struct btf_record *rec_a, const struct btf_record *rec_b); +void bpf_obj_free_timer(const struct btf_record *rec, void *obj); void bpf_obj_free_fields(const struct btf_record *rec, void *obj); struct bpf_map *bpf_map_get(u32 ufd); diff --git a/include/linux/btf.h b/include/linux/btf.h index 9e62717cdc7a..282006abd062 100644 --- a/include/linux/btf.h +++ b/include/linux/btf.h @@ -163,7 +163,8 @@ bool btf_member_is_reg_int(const struct btf *btf, const struct btf_type *s, u32 expected_offset, u32 expected_size); int btf_find_spin_lock(const struct btf *btf, const struct btf_type *t); int btf_find_timer(const struct btf *btf, const struct btf_type *t); -struct btf_record *btf_parse_fields(const struct btf *btf, const struct btf_type *t); +struct btf_record *btf_parse_fields(const struct btf *btf, const struct btf_type *t, + u32 field_mask, u32 value_size); bool btf_type_is_void(const struct btf_type *t); s32 btf_find_by_name_kind(const struct btf *btf, const char *name, u8 kind); const struct btf_type *btf_type_skip_modifiers(const struct btf *btf, diff --git a/kernel/bpf/arraymap.c b/kernel/bpf/arraymap.c index 417f84342e98..672eb17ac421 100644 --- a/kernel/bpf/arraymap.c +++ b/kernel/bpf/arraymap.c @@ -306,13 +306,6 @@ static int array_map_get_next_key(struct bpf_map *map, void *key, void *next_key return 0; } -static void check_and_free_fields(struct bpf_array *arr, void *val) -{ - if (map_value_has_timer(&arr->map)) - bpf_timer_cancel_and_free(val + arr->map.timer_off); - bpf_obj_free_fields(arr->map.record, val); -} - /* Called from syscall or from eBPF program */ static int array_map_update_elem(struct bpf_map *map, void *key, void *value, u64 map_flags) @@ -334,13 +327,13 @@ static int array_map_update_elem(struct bpf_map *map, void *key, void *value, return -EEXIST; if (unlikely((map_flags & BPF_F_LOCK) && - !map_value_has_spin_lock(map))) + !btf_record_has_field(map->record, BPF_SPIN_LOCK))) return -EINVAL; if (array->map.map_type == BPF_MAP_TYPE_PERCPU_ARRAY) { val = this_cpu_ptr(array->pptrs[index & array->index_mask]); copy_map_value(map, val, value); - check_and_free_fields(array, val); + bpf_obj_free_fields(array->map.record, val); } else { val = array->value + (u64)array->elem_size * (index & array->index_mask); @@ -348,7 +341,7 @@ static int array_map_update_elem(struct bpf_map *map, void *key, void *value, copy_map_value_locked(map, val, value, false); else copy_map_value(map, val, value); - check_and_free_fields(array, val); + bpf_obj_free_fields(array->map.record, val); } return 0; } @@ -385,7 +378,7 @@ int bpf_percpu_array_update(struct bpf_map *map, void *key, void *value, pptr = array->pptrs[index & array->index_mask]; for_each_possible_cpu(cpu) { copy_map_value_long(map, per_cpu_ptr(pptr, cpu), value + off); - check_and_free_fields(array, per_cpu_ptr(pptr, cpu)); + bpf_obj_free_fields(array->map.record, per_cpu_ptr(pptr, cpu)); off += size; } rcu_read_unlock(); @@ -409,11 +402,11 @@ static void array_map_free_timers(struct bpf_map *map) int i; /* We don't reset or free fields other than timer on uref dropping to zero. */ - if (!map_value_has_timer(map)) + if (!btf_record_has_field(map->record, BPF_TIMER)) return; for (i = 0; i < array->map.max_entries; i++) - bpf_timer_cancel_and_free(array_map_elem_ptr(array, i) + map->timer_off); + bpf_obj_free_timer(map->record, array_map_elem_ptr(array, i)); } /* Called when map->refcnt goes to zero, either from workqueue or from syscall */ diff --git a/kernel/bpf/bpf_local_storage.c b/kernel/bpf/bpf_local_storage.c index 93d9b1b17bc8..37020078d1c1 100644 --- a/kernel/bpf/bpf_local_storage.c +++ b/kernel/bpf/bpf_local_storage.c @@ -382,7 +382,7 @@ bpf_local_storage_update(void *owner, struct bpf_local_storage_map *smap, if (unlikely((map_flags & ~BPF_F_LOCK) > BPF_EXIST) || /* BPF_F_LOCK can only be used in a value with spin_lock */ unlikely((map_flags & BPF_F_LOCK) && - !map_value_has_spin_lock(&smap->map))) + !btf_record_has_field(smap->map.record, BPF_SPIN_LOCK))) return ERR_PTR(-EINVAL); if (gfp_flags == GFP_KERNEL && (map_flags & ~BPF_F_LOCK) != BPF_NOEXIST) diff --git a/kernel/bpf/btf.c b/kernel/bpf/btf.c index 8391a77138ee..3dad828db13c 100644 --- a/kernel/bpf/btf.c +++ b/kernel/bpf/btf.c @@ -3205,16 +3205,20 @@ enum { struct btf_field_info { enum btf_field_type type; u32 off; - u32 type_id; + struct { + u32 type_id; + } kptr; }; static int btf_find_struct(const struct btf *btf, const struct btf_type *t, - u32 off, int sz, struct btf_field_info *info) + u32 off, int sz, enum btf_field_type field_type, + struct btf_field_info *info) { if (!__btf_type_is_struct(t)) return BTF_FIELD_IGNORE; if (t->size != sz) return BTF_FIELD_IGNORE; + info->type = field_type; info->off = off; return BTF_FIELD_FOUND; } @@ -3251,28 +3255,66 @@ static int btf_find_kptr(const struct btf *btf, const struct btf_type *t, if (!__btf_type_is_struct(t)) return -EINVAL; - info->type_id = res_id; - info->off = off; info->type = type; + info->off = off; + info->kptr.type_id = res_id; return BTF_FIELD_FOUND; } -static int btf_find_struct_field(const struct btf *btf, const struct btf_type *t, - const char *name, int sz, int align, - enum btf_field_info_type field_type, +static int btf_get_field_type(const char *name, u32 field_mask, u32 *seen_mask, + int *align, int *sz) +{ + int type = 0; + + if (field_mask & BPF_SPIN_LOCK) { + if (!strcmp(name, "bpf_spin_lock")) { + if (*seen_mask & BPF_SPIN_LOCK) + return -E2BIG; + *seen_mask |= BPF_SPIN_LOCK; + type = BPF_SPIN_LOCK; + goto end; + } + } + if (field_mask & BPF_TIMER) { + if (!strcmp(name, "bpf_timer")) { + if (*seen_mask & BPF_TIMER) + return -E2BIG; + *seen_mask |= BPF_TIMER; + type = BPF_TIMER; + goto end; + } + } + /* Only return BPF_KPTR when all other types with matchable names fail */ + if (field_mask & BPF_KPTR) { + type = BPF_KPTR_REF; + goto end; + } + return 0; +end: + *sz = btf_field_type_size(type); + *align = btf_field_type_align(type); + return type; +} + +static int btf_find_struct_field(const struct btf *btf, + const struct btf_type *t, u32 field_mask, struct btf_field_info *info, int info_cnt) { + int ret, idx = 0, align, sz, field_type; const struct btf_member *member; struct btf_field_info tmp; - int ret, idx = 0; - u32 i, off; + u32 i, off, seen_mask = 0; for_each_member(i, t, member) { const struct btf_type *member_type = btf_type_by_id(btf, member->type); - if (name && strcmp(__btf_name_by_offset(btf, member_type->name_off), name)) + field_type = btf_get_field_type(__btf_name_by_offset(btf, member_type->name_off), + field_mask, &seen_mask, &align, &sz); + if (field_type == 0) continue; + if (field_type < 0) + return field_type; off = __btf_member_bit_offset(t, member); if (off % 8) @@ -3280,17 +3322,18 @@ static int btf_find_struct_field(const struct btf *btf, const struct btf_type *t return -EINVAL; off /= 8; if (off % align) - return -EINVAL; + continue; switch (field_type) { - case BTF_FIELD_SPIN_LOCK: - case BTF_FIELD_TIMER: - ret = btf_find_struct(btf, member_type, off, sz, + case BPF_SPIN_LOCK: + case BPF_TIMER: + ret = btf_find_struct(btf, member_type, off, sz, field_type, idx < info_cnt ? &info[idx] : &tmp); if (ret < 0) return ret; break; - case BTF_FIELD_KPTR: + case BPF_KPTR_UNREF: + case BPF_KPTR_REF: ret = btf_find_kptr(btf, member_type, off, sz, idx < info_cnt ? &info[idx] : &tmp); if (ret < 0) @@ -3310,37 +3353,41 @@ static int btf_find_struct_field(const struct btf *btf, const struct btf_type *t } static int btf_find_datasec_var(const struct btf *btf, const struct btf_type *t, - const char *name, int sz, int align, - enum btf_field_info_type field_type, - struct btf_field_info *info, int info_cnt) + u32 field_mask, struct btf_field_info *info, + int info_cnt) { + int ret, idx = 0, align, sz, field_type; const struct btf_var_secinfo *vsi; struct btf_field_info tmp; - int ret, idx = 0; - u32 i, off; + u32 i, off, seen_mask = 0; for_each_vsi(i, t, vsi) { const struct btf_type *var = btf_type_by_id(btf, vsi->type); const struct btf_type *var_type = btf_type_by_id(btf, var->type); - off = vsi->offset; - - if (name && strcmp(__btf_name_by_offset(btf, var_type->name_off), name)) + field_type = btf_get_field_type(__btf_name_by_offset(btf, var_type->name_off), + field_mask, &seen_mask, &align, &sz); + if (field_type == 0) continue; + if (field_type < 0) + return field_type; + + off = vsi->offset; if (vsi->size != sz) continue; if (off % align) - return -EINVAL; + continue; switch (field_type) { - case BTF_FIELD_SPIN_LOCK: - case BTF_FIELD_TIMER: - ret = btf_find_struct(btf, var_type, off, sz, + case BPF_SPIN_LOCK: + case BPF_TIMER: + ret = btf_find_struct(btf, var_type, off, sz, field_type, idx < info_cnt ? &info[idx] : &tmp); if (ret < 0) return ret; break; - case BTF_FIELD_KPTR: + case BPF_KPTR_UNREF: + case BPF_KPTR_REF: ret = btf_find_kptr(btf, var_type, off, sz, idx < info_cnt ? &info[idx] : &tmp); if (ret < 0) @@ -3360,78 +3407,98 @@ static int btf_find_datasec_var(const struct btf *btf, const struct btf_type *t, } static int btf_find_field(const struct btf *btf, const struct btf_type *t, - enum btf_field_info_type field_type, - struct btf_field_info *info, int info_cnt) + u32 field_mask, struct btf_field_info *info, + int info_cnt) { - const char *name; - int sz, align; - - switch (field_type) { - case BTF_FIELD_SPIN_LOCK: - name = "bpf_spin_lock"; - sz = sizeof(struct bpf_spin_lock); - align = __alignof__(struct bpf_spin_lock); - break; - case BTF_FIELD_TIMER: - name = "bpf_timer"; - sz = sizeof(struct bpf_timer); - align = __alignof__(struct bpf_timer); - break; - case BTF_FIELD_KPTR: - name = NULL; - sz = sizeof(u64); - align = 8; - break; - default: - return -EFAULT; - } - if (__btf_type_is_struct(t)) - return btf_find_struct_field(btf, t, name, sz, align, field_type, info, info_cnt); + return btf_find_struct_field(btf, t, field_mask, info, info_cnt); else if (btf_type_is_datasec(t)) - return btf_find_datasec_var(btf, t, name, sz, align, field_type, info, info_cnt); + return btf_find_datasec_var(btf, t, field_mask, info, info_cnt); return -EINVAL; } -/* find 'struct bpf_spin_lock' in map value. - * return >= 0 offset if found - * and < 0 in case of error - */ -int btf_find_spin_lock(const struct btf *btf, const struct btf_type *t) +static int btf_parse_kptr(const struct btf *btf, struct btf_field *field, + struct btf_field_info *info) { - struct btf_field_info info; + struct module *mod = NULL; + const struct btf_type *t; + struct btf *kernel_btf; int ret; + s32 id; - ret = btf_find_field(btf, t, BTF_FIELD_SPIN_LOCK, &info, 1); - if (ret < 0) - return ret; - if (!ret) - return -ENOENT; - return info.off; + /* Find type in map BTF, and use it to look up the matching type + * in vmlinux or module BTFs, by name and kind. + */ + t = btf_type_by_id(btf, info->kptr.type_id); + id = bpf_find_btf_id(__btf_name_by_offset(btf, t->name_off), BTF_INFO_KIND(t->info), + &kernel_btf); + if (id < 0) + return id; + + /* Find and stash the function pointer for the destruction function that + * needs to be eventually invoked from the map free path. + */ + if (info->type == BPF_KPTR_REF) { + const struct btf_type *dtor_func; + const char *dtor_func_name; + unsigned long addr; + s32 dtor_btf_id; + + /* This call also serves as a whitelist of allowed objects that + * can be used as a referenced pointer and be stored in a map at + * the same time. + */ + dtor_btf_id = btf_find_dtor_kfunc(kernel_btf, id); + if (dtor_btf_id < 0) { + ret = dtor_btf_id; + goto end_btf; + } + + dtor_func = btf_type_by_id(kernel_btf, dtor_btf_id); + if (!dtor_func) { + ret = -ENOENT; + goto end_btf; + } + + if (btf_is_module(kernel_btf)) { + mod = btf_try_get_module(kernel_btf); + if (!mod) { + ret = -ENXIO; + goto end_btf; + } + } + + /* We already verified dtor_func to be btf_type_is_func + * in register_btf_id_dtor_kfuncs. + */ + dtor_func_name = __btf_name_by_offset(kernel_btf, dtor_func->name_off); + addr = kallsyms_lookup_name(dtor_func_name); + if (!addr) { + ret = -EINVAL; + goto end_mod; + } + field->kptr.dtor = (void *)addr; + } + + field->kptr.btf_id = id; + field->kptr.btf = kernel_btf; + field->kptr.module = mod; + return 0; +end_mod: + module_put(mod); +end_btf: + btf_put(kernel_btf); + return ret; } -int btf_find_timer(const struct btf *btf, const struct btf_type *t) -{ - struct btf_field_info info; - int ret; - - ret = btf_find_field(btf, t, BTF_FIELD_TIMER, &info, 1); - if (ret < 0) - return ret; - if (!ret) - return -ENOENT; - return info.off; -} - -struct btf_record *btf_parse_fields(const struct btf *btf, const struct btf_type *t) +struct btf_record *btf_parse_fields(const struct btf *btf, const struct btf_type *t, + u32 field_mask, u32 value_size) { struct btf_field_info info_arr[BTF_FIELDS_MAX]; - struct btf *kernel_btf = NULL; - struct module *mod = NULL; struct btf_record *rec; int ret, i, cnt; - ret = btf_find_field(btf, t, BTF_FIELD_KPTR, info_arr, ARRAY_SIZE(info_arr)); + ret = btf_find_field(btf, t, field_mask, info_arr, ARRAY_SIZE(info_arr)); if (ret < 0) return ERR_PTR(ret); if (!ret) @@ -3441,80 +3508,44 @@ struct btf_record *btf_parse_fields(const struct btf *btf, const struct btf_type rec = kzalloc(offsetof(struct btf_record, fields[cnt]), GFP_KERNEL | __GFP_NOWARN); if (!rec) return ERR_PTR(-ENOMEM); - rec->cnt = 0; + + rec->spin_lock_off = -EINVAL; + rec->timer_off = -EINVAL; for (i = 0; i < cnt; i++) { - const struct btf_type *t; - s32 id; - - /* Find type in map BTF, and use it to look up the matching type - * in vmlinux or module BTFs, by name and kind. - */ - t = btf_type_by_id(btf, info_arr[i].type_id); - id = bpf_find_btf_id(__btf_name_by_offset(btf, t->name_off), BTF_INFO_KIND(t->info), - &kernel_btf); - if (id < 0) { - ret = id; + if (info_arr[i].off + btf_field_type_size(info_arr[i].type) > value_size) { + WARN_ONCE(1, "verifier bug off %d size %d", info_arr[i].off, value_size); + ret = -EFAULT; goto end; } - /* Find and stash the function pointer for the destruction function that - * needs to be eventually invoked from the map free path. - */ - if (info_arr[i].type == BPF_KPTR_REF) { - const struct btf_type *dtor_func; - const char *dtor_func_name; - unsigned long addr; - s32 dtor_btf_id; - - /* This call also serves as a whitelist of allowed objects that - * can be used as a referenced pointer and be stored in a map at - * the same time. - */ - dtor_btf_id = btf_find_dtor_kfunc(kernel_btf, id); - if (dtor_btf_id < 0) { - ret = dtor_btf_id; - goto end_btf; - } - - dtor_func = btf_type_by_id(kernel_btf, dtor_btf_id); - if (!dtor_func) { - ret = -ENOENT; - goto end_btf; - } - - if (btf_is_module(kernel_btf)) { - mod = btf_try_get_module(kernel_btf); - if (!mod) { - ret = -ENXIO; - goto end_btf; - } - } - - /* We already verified dtor_func to be btf_type_is_func - * in register_btf_id_dtor_kfuncs. - */ - dtor_func_name = __btf_name_by_offset(kernel_btf, dtor_func->name_off); - addr = kallsyms_lookup_name(dtor_func_name); - if (!addr) { - ret = -EINVAL; - goto end_mod; - } - rec->fields[i].kptr.dtor = (void *)addr; - } - rec->field_mask |= info_arr[i].type; rec->fields[i].offset = info_arr[i].off; rec->fields[i].type = info_arr[i].type; - rec->fields[i].kptr.btf_id = id; - rec->fields[i].kptr.btf = kernel_btf; - rec->fields[i].kptr.module = mod; + + switch (info_arr[i].type) { + case BPF_SPIN_LOCK: + WARN_ON_ONCE(rec->spin_lock_off >= 0); + /* Cache offset for faster lookup at runtime */ + rec->spin_lock_off = rec->fields[i].offset; + break; + case BPF_TIMER: + WARN_ON_ONCE(rec->timer_off >= 0); + /* Cache offset for faster lookup at runtime */ + rec->timer_off = rec->fields[i].offset; + break; + case BPF_KPTR_UNREF: + case BPF_KPTR_REF: + ret = btf_parse_kptr(btf, &rec->fields[i], &info_arr[i]); + if (ret < 0) + goto end; + break; + default: + ret = -EFAULT; + goto end; + } rec->cnt++; } return rec; -end_mod: - module_put(mod); -end_btf: - btf_put(kernel_btf); end: btf_record_free(rec); return ERR_PTR(ret); diff --git a/kernel/bpf/hashtab.c b/kernel/bpf/hashtab.c index c5ea8f9bb7a9..50d254cd0709 100644 --- a/kernel/bpf/hashtab.c +++ b/kernel/bpf/hashtab.c @@ -222,7 +222,7 @@ static void htab_free_prealloced_timers(struct bpf_htab *htab) u32 num_entries = htab->map.max_entries; int i; - if (!map_value_has_timer(&htab->map)) + if (!btf_record_has_field(htab->map.record, BPF_TIMER)) return; if (htab_has_extra_elems(htab)) num_entries += num_possible_cpus(); @@ -231,9 +231,7 @@ static void htab_free_prealloced_timers(struct bpf_htab *htab) struct htab_elem *elem; elem = get_htab_elem(htab, i); - bpf_timer_cancel_and_free(elem->key + - round_up(htab->map.key_size, 8) + - htab->map.timer_off); + bpf_obj_free_timer(htab->map.record, elem->key + round_up(htab->map.key_size, 8)); cond_resched(); } } @@ -763,8 +761,6 @@ static void check_and_free_fields(struct bpf_htab *htab, { void *map_value = elem->key + round_up(htab->map.key_size, 8); - if (map_value_has_timer(&htab->map)) - bpf_timer_cancel_and_free(map_value + htab->map.timer_off); bpf_obj_free_fields(htab->map.record, map_value); } @@ -1089,7 +1085,7 @@ static int htab_map_update_elem(struct bpf_map *map, void *key, void *value, head = &b->head; if (unlikely(map_flags & BPF_F_LOCK)) { - if (unlikely(!map_value_has_spin_lock(map))) + if (unlikely(!btf_record_has_field(map->record, BPF_SPIN_LOCK))) return -EINVAL; /* find an element without taking the bucket lock */ l_old = lookup_nulls_elem_raw(head, hash, key, key_size, @@ -1472,12 +1468,8 @@ static void htab_free_malloced_timers(struct bpf_htab *htab) struct htab_elem *l; hlist_nulls_for_each_entry(l, n, head, hash_node) { - /* We don't reset or free kptr on uref dropping to zero, - * hence just free timer. - */ - bpf_timer_cancel_and_free(l->key + - round_up(htab->map.key_size, 8) + - htab->map.timer_off); + /* We only free timer on uref dropping to zero */ + bpf_obj_free_timer(htab->map.record, l->key + round_up(htab->map.key_size, 8)); } cond_resched_rcu(); } @@ -1488,8 +1480,8 @@ static void htab_map_free_timers(struct bpf_map *map) { struct bpf_htab *htab = container_of(map, struct bpf_htab, map); - /* We don't reset or free kptr on uref dropping to zero. */ - if (!map_value_has_timer(&htab->map)) + /* We only free timer on uref dropping to zero */ + if (!btf_record_has_field(htab->map.record, BPF_TIMER)) return; if (!htab_is_prealloc(htab)) htab_free_malloced_timers(htab); @@ -1673,7 +1665,7 @@ __htab_map_lookup_and_delete_batch(struct bpf_map *map, elem_map_flags = attr->batch.elem_flags; if ((elem_map_flags & ~BPF_F_LOCK) || - ((elem_map_flags & BPF_F_LOCK) && !map_value_has_spin_lock(map))) + ((elem_map_flags & BPF_F_LOCK) && !btf_record_has_field(map->record, BPF_SPIN_LOCK))) return -EINVAL; map_flags = attr->batch.flags; diff --git a/kernel/bpf/helpers.c b/kernel/bpf/helpers.c index 124fd199ce5c..283f55bbeb70 100644 --- a/kernel/bpf/helpers.c +++ b/kernel/bpf/helpers.c @@ -366,9 +366,9 @@ void copy_map_value_locked(struct bpf_map *map, void *dst, void *src, struct bpf_spin_lock *lock; if (lock_src) - lock = src + map->spin_lock_off; + lock = src + map->record->spin_lock_off; else - lock = dst + map->spin_lock_off; + lock = dst + map->record->spin_lock_off; preempt_disable(); __bpf_spin_lock_irqsave(lock); copy_map_value(map, dst, src); @@ -1169,7 +1169,7 @@ BPF_CALL_3(bpf_timer_init, struct bpf_timer_kern *, timer, struct bpf_map *, map ret = -ENOMEM; goto out; } - t->value = (void *)timer - map->timer_off; + t->value = (void *)timer - map->record->timer_off; t->map = map; t->prog = NULL; rcu_assign_pointer(t->callback_fn, NULL); diff --git a/kernel/bpf/local_storage.c b/kernel/bpf/local_storage.c index 098cf336fae6..e90d9f63edc5 100644 --- a/kernel/bpf/local_storage.c +++ b/kernel/bpf/local_storage.c @@ -151,7 +151,7 @@ static int cgroup_storage_update_elem(struct bpf_map *map, void *key, return -EINVAL; if (unlikely((flags & BPF_F_LOCK) && - !map_value_has_spin_lock(map))) + !btf_record_has_field(map->record, BPF_SPIN_LOCK))) return -EINVAL; storage = cgroup_storage_lookup((struct bpf_cgroup_storage_map *)map, diff --git a/kernel/bpf/map_in_map.c b/kernel/bpf/map_in_map.c index d6c662183f88..8ca0cca39d49 100644 --- a/kernel/bpf/map_in_map.c +++ b/kernel/bpf/map_in_map.c @@ -29,7 +29,7 @@ struct bpf_map *bpf_map_meta_alloc(int inner_map_ufd) return ERR_PTR(-ENOTSUPP); } - if (map_value_has_spin_lock(inner_map)) { + if (btf_record_has_field(inner_map->record, BPF_SPIN_LOCK)) { fdput(f); return ERR_PTR(-ENOTSUPP); } @@ -50,8 +50,6 @@ struct bpf_map *bpf_map_meta_alloc(int inner_map_ufd) inner_map_meta->value_size = inner_map->value_size; inner_map_meta->map_flags = inner_map->map_flags; inner_map_meta->max_entries = inner_map->max_entries; - inner_map_meta->spin_lock_off = inner_map->spin_lock_off; - inner_map_meta->timer_off = inner_map->timer_off; inner_map_meta->record = btf_record_dup(inner_map->record); if (IS_ERR(inner_map_meta->record)) { /* btf_record_dup returns NULL or valid pointer in case of @@ -92,7 +90,6 @@ bool bpf_map_meta_equal(const struct bpf_map *meta0, return meta0->map_type == meta1->map_type && meta0->key_size == meta1->key_size && meta0->value_size == meta1->value_size && - meta0->timer_off == meta1->timer_off && meta0->map_flags == meta1->map_flags && btf_record_equal(meta0->record, meta1->record); } diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c index b80c0e2eb73f..53d6dc5cf0e2 100644 --- a/kernel/bpf/syscall.c +++ b/kernel/bpf/syscall.c @@ -527,6 +527,9 @@ void btf_record_free(struct btf_record *rec) return; for (i = 0; i < rec->cnt; i++) { switch (rec->fields[i].type) { + case BPF_SPIN_LOCK: + case BPF_TIMER: + break; case BPF_KPTR_UNREF: case BPF_KPTR_REF: if (rec->fields[i].kptr.module) @@ -564,6 +567,9 @@ struct btf_record *btf_record_dup(const struct btf_record *rec) new_rec->cnt = 0; for (i = 0; i < rec->cnt; i++) { switch (fields[i].type) { + case BPF_SPIN_LOCK: + case BPF_TIMER: + break; case BPF_KPTR_UNREF: case BPF_KPTR_REF: btf_get(fields[i].kptr.btf); @@ -600,6 +606,13 @@ bool btf_record_equal(const struct btf_record *rec_a, const struct btf_record *r return !memcmp(rec_a, rec_b, size); } +void bpf_obj_free_timer(const struct btf_record *rec, void *obj) +{ + if (WARN_ON_ONCE(!btf_record_has_field(rec, BPF_TIMER))) + return; + bpf_timer_cancel_and_free(obj + rec->timer_off); +} + void bpf_obj_free_fields(const struct btf_record *rec, void *obj) { const struct btf_field *fields; @@ -613,6 +626,11 @@ void bpf_obj_free_fields(const struct btf_record *rec, void *obj) void *field_ptr = obj + field->offset; switch (fields[i].type) { + case BPF_SPIN_LOCK: + break; + case BPF_TIMER: + bpf_timer_cancel_and_free(field_ptr); + break; case BPF_KPTR_UNREF: WRITE_ONCE(*(u64 *)field_ptr, 0); break; @@ -798,8 +816,7 @@ static int bpf_map_mmap(struct file *filp, struct vm_area_struct *vma) struct bpf_map *map = filp->private_data; int err; - if (!map->ops->map_mmap || map_value_has_spin_lock(map) || - map_value_has_timer(map) || !IS_ERR_OR_NULL(map->record)) + if (!map->ops->map_mmap || !IS_ERR_OR_NULL(map->record)) return -ENOTSUPP; if (!(vma->vm_flags & VM_SHARED)) @@ -954,48 +971,30 @@ static void map_field_offs_swap(void *_a, void *_b, int size, const void *priv) static int bpf_map_alloc_off_arr(struct bpf_map *map) { - bool has_spin_lock = map_value_has_spin_lock(map); - bool has_timer = map_value_has_timer(map); bool has_fields = !IS_ERR_OR_NULL(map->record); struct btf_field_offs *fo; - u32 i; + struct btf_record *rec; + u32 i, *off; + u8 *sz; - if (!has_spin_lock && !has_timer && !has_fields) { + if (!has_fields) { map->field_offs = NULL; return 0; } - fo = kmalloc(sizeof(*map->field_offs), GFP_KERNEL | __GFP_NOWARN); + fo = kzalloc(sizeof(*map->field_offs), GFP_KERNEL | __GFP_NOWARN); if (!fo) return -ENOMEM; map->field_offs = fo; - fo->cnt = 0; - if (has_spin_lock) { - i = fo->cnt; - - fo->field_off[i] = map->spin_lock_off; - fo->field_sz[i] = sizeof(struct bpf_spin_lock); - fo->cnt++; - } - if (has_timer) { - i = fo->cnt; - - fo->field_off[i] = map->timer_off; - fo->field_sz[i] = sizeof(struct bpf_timer); - fo->cnt++; - } - if (has_fields) { - struct btf_record *rec = map->record; - u32 *off = &fo->field_off[fo->cnt]; - u8 *sz = &fo->field_sz[fo->cnt]; - - for (i = 0; i < rec->cnt; i++) { - *off++ = rec->fields[i].offset; - *sz++ = btf_field_type_size(rec->fields[i].type); - } - fo->cnt += rec->cnt; + rec = map->record; + off = fo->field_off; + sz = fo->field_sz; + for (i = 0; i < rec->cnt; i++) { + *off++ = rec->fields[i].offset; + *sz++ = btf_field_type_size(rec->fields[i].type); } + fo->cnt = rec->cnt; if (fo->cnt == 1) return 0; @@ -1026,39 +1025,8 @@ static int map_check_btf(struct bpf_map *map, const struct btf *btf, if (!value_type || value_size != map->value_size) return -EINVAL; - map->spin_lock_off = btf_find_spin_lock(btf, value_type); - - if (map_value_has_spin_lock(map)) { - if (map->map_flags & BPF_F_RDONLY_PROG) - return -EACCES; - if (map->map_type != BPF_MAP_TYPE_HASH && - map->map_type != BPF_MAP_TYPE_ARRAY && - map->map_type != BPF_MAP_TYPE_CGROUP_STORAGE && - map->map_type != BPF_MAP_TYPE_SK_STORAGE && - map->map_type != BPF_MAP_TYPE_INODE_STORAGE && - map->map_type != BPF_MAP_TYPE_TASK_STORAGE && - map->map_type != BPF_MAP_TYPE_CGRP_STORAGE) - return -ENOTSUPP; - if (map->spin_lock_off + sizeof(struct bpf_spin_lock) > - map->value_size) { - WARN_ONCE(1, - "verifier bug spin_lock_off %d value_size %d\n", - map->spin_lock_off, map->value_size); - return -EFAULT; - } - } - - map->timer_off = btf_find_timer(btf, value_type); - if (map_value_has_timer(map)) { - if (map->map_flags & BPF_F_RDONLY_PROG) - return -EACCES; - if (map->map_type != BPF_MAP_TYPE_HASH && - map->map_type != BPF_MAP_TYPE_LRU_HASH && - map->map_type != BPF_MAP_TYPE_ARRAY) - return -EOPNOTSUPP; - } - - map->record = btf_parse_fields(btf, value_type); + map->record = btf_parse_fields(btf, value_type, BPF_SPIN_LOCK | BPF_TIMER | BPF_KPTR, + map->value_size); if (!IS_ERR_OR_NULL(map->record)) { int i; @@ -1074,6 +1042,26 @@ static int map_check_btf(struct bpf_map *map, const struct btf *btf, switch (map->record->field_mask & (1 << i)) { case 0: continue; + case BPF_SPIN_LOCK: + if (map->map_type != BPF_MAP_TYPE_HASH && + map->map_type != BPF_MAP_TYPE_ARRAY && + map->map_type != BPF_MAP_TYPE_CGROUP_STORAGE && + map->map_type != BPF_MAP_TYPE_SK_STORAGE && + map->map_type != BPF_MAP_TYPE_INODE_STORAGE && + map->map_type != BPF_MAP_TYPE_TASK_STORAGE && + map->map_type != BPF_MAP_TYPE_CGRP_STORAGE) { + ret = -EOPNOTSUPP; + goto free_map_tab; + } + break; + case BPF_TIMER: + if (map->map_type != BPF_MAP_TYPE_HASH && + map->map_type != BPF_MAP_TYPE_LRU_HASH && + map->map_type != BPF_MAP_TYPE_ARRAY) { + return -EOPNOTSUPP; + goto free_map_tab; + } + break; case BPF_KPTR_UNREF: case BPF_KPTR_REF: if (map->map_type != BPF_MAP_TYPE_HASH && @@ -1153,8 +1141,6 @@ static int map_create(union bpf_attr *attr) mutex_init(&map->freeze_mutex); spin_lock_init(&map->owner.lock); - map->spin_lock_off = -EINVAL; - map->timer_off = -EINVAL; if (attr->btf_key_type_id || attr->btf_value_type_id || /* Even the map's value is a kernel's struct, * the bpf_prog.o must have BTF to begin with @@ -1368,7 +1354,7 @@ static int map_lookup_elem(union bpf_attr *attr) } if ((attr->flags & BPF_F_LOCK) && - !map_value_has_spin_lock(map)) { + !btf_record_has_field(map->record, BPF_SPIN_LOCK)) { err = -EINVAL; goto err_put; } @@ -1441,7 +1427,7 @@ static int map_update_elem(union bpf_attr *attr, bpfptr_t uattr) } if ((attr->flags & BPF_F_LOCK) && - !map_value_has_spin_lock(map)) { + !btf_record_has_field(map->record, BPF_SPIN_LOCK)) { err = -EINVAL; goto err_put; } @@ -1604,7 +1590,7 @@ int generic_map_delete_batch(struct bpf_map *map, return -EINVAL; if ((attr->batch.elem_flags & BPF_F_LOCK) && - !map_value_has_spin_lock(map)) { + !btf_record_has_field(map->record, BPF_SPIN_LOCK)) { return -EINVAL; } @@ -1661,7 +1647,7 @@ int generic_map_update_batch(struct bpf_map *map, return -EINVAL; if ((attr->batch.elem_flags & BPF_F_LOCK) && - !map_value_has_spin_lock(map)) { + !btf_record_has_field(map->record, BPF_SPIN_LOCK)) { return -EINVAL; } @@ -1724,7 +1710,7 @@ int generic_map_lookup_batch(struct bpf_map *map, return -EINVAL; if ((attr->batch.elem_flags & BPF_F_LOCK) && - !map_value_has_spin_lock(map)) + !btf_record_has_field(map->record, BPF_SPIN_LOCK)) return -EINVAL; value_size = bpf_map_value_size(map); @@ -1846,7 +1832,7 @@ static int map_lookup_and_delete_elem(union bpf_attr *attr) } if ((attr->flags & BPF_F_LOCK) && - !map_value_has_spin_lock(map)) { + !btf_record_has_field(map->record, BPF_SPIN_LOCK)) { err = -EINVAL; goto err_put; } @@ -1917,8 +1903,7 @@ static int map_freeze(const union bpf_attr *attr) if (IS_ERR(map)) return PTR_ERR(map); - if (map->map_type == BPF_MAP_TYPE_STRUCT_OPS || - map_value_has_timer(map) || !IS_ERR_OR_NULL(map->record)) { + if (map->map_type == BPF_MAP_TYPE_STRUCT_OPS || !IS_ERR_OR_NULL(map->record)) { fdput(f); return -ENOTSUPP; } diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 5ce5364ce898..73a3516f1a48 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -454,7 +454,7 @@ static bool reg_type_not_null(enum bpf_reg_type type) static bool reg_may_point_to_spin_lock(const struct bpf_reg_state *reg) { return reg->type == PTR_TO_MAP_VALUE && - map_value_has_spin_lock(reg->map_ptr); + btf_record_has_field(reg->map_ptr->record, BPF_SPIN_LOCK); } static bool type_is_rdonly_mem(u32 type) @@ -1388,7 +1388,7 @@ static void mark_ptr_not_null_reg(struct bpf_reg_state *reg) /* transfer reg's id which is unique for every map_lookup_elem * as UID of the inner map. */ - if (map_value_has_timer(map->inner_map_meta)) + if (btf_record_has_field(map->inner_map_meta->record, BPF_TIMER)) reg->map_uid = reg->id; } else if (map->map_type == BPF_MAP_TYPE_XSKMAP) { reg->type = PTR_TO_XDP_SOCK; @@ -3817,29 +3817,6 @@ static int check_map_access(struct bpf_verifier_env *env, u32 regno, if (err) return err; - if (map_value_has_spin_lock(map)) { - u32 lock = map->spin_lock_off; - - /* if any part of struct bpf_spin_lock can be touched by - * load/store reject this program. - * To check that [x1, x2) overlaps with [y1, y2) - * it is sufficient to check x1 < y2 && y1 < x2. - */ - if (reg->smin_value + off < lock + sizeof(struct bpf_spin_lock) && - lock < reg->umax_value + off + size) { - verbose(env, "bpf_spin_lock cannot be accessed directly by load/store\n"); - return -EACCES; - } - } - if (map_value_has_timer(map)) { - u32 t = map->timer_off; - - if (reg->smin_value + off < t + sizeof(struct bpf_timer) && - t < reg->umax_value + off + size) { - verbose(env, "bpf_timer cannot be accessed directly by load/store\n"); - return -EACCES; - } - } if (IS_ERR_OR_NULL(map->record)) return 0; rec = map->record; @@ -3847,6 +3824,10 @@ static int check_map_access(struct bpf_verifier_env *env, u32 regno, struct btf_field *field = &rec->fields[i]; u32 p = field->offset; + /* If any part of a field can be touched by load/store, reject + * this program. To check that [x1, x2) overlaps with [y1, y2), + * it is sufficient to check x1 < y2 && y1 < x2. + */ if (reg->smin_value + off < p + btf_field_type_size(field->type) && p < reg->umax_value + off + size) { switch (field->type) { @@ -3871,7 +3852,8 @@ static int check_map_access(struct bpf_verifier_env *env, u32 regno, } break; default: - verbose(env, "field cannot be accessed directly by load/store\n"); + verbose(env, "%s cannot be accessed directly by load/store\n", + btf_field_type_name(field->type)); return -EACCES; } } @@ -5440,24 +5422,13 @@ static int process_spin_lock(struct bpf_verifier_env *env, int regno, map->name); return -EINVAL; } - if (!map_value_has_spin_lock(map)) { - if (map->spin_lock_off == -E2BIG) - verbose(env, - "map '%s' has more than one 'struct bpf_spin_lock'\n", - map->name); - else if (map->spin_lock_off == -ENOENT) - verbose(env, - "map '%s' doesn't have 'struct bpf_spin_lock'\n", - map->name); - else - verbose(env, - "map '%s' is not a struct type or bpf_spin_lock is mangled\n", - map->name); + if (!btf_record_has_field(map->record, BPF_SPIN_LOCK)) { + verbose(env, "map '%s' has no valid bpf_spin_lock\n", map->name); return -EINVAL; } - if (map->spin_lock_off != val + reg->off) { - verbose(env, "off %lld doesn't point to 'struct bpf_spin_lock'\n", - val + reg->off); + if (map->record->spin_lock_off != val + reg->off) { + verbose(env, "off %lld doesn't point to 'struct bpf_spin_lock' that is at %d\n", + val + reg->off, map->record->spin_lock_off); return -EINVAL; } if (is_lock) { @@ -5500,24 +5471,13 @@ static int process_timer_func(struct bpf_verifier_env *env, int regno, map->name); return -EINVAL; } - if (!map_value_has_timer(map)) { - if (map->timer_off == -E2BIG) - verbose(env, - "map '%s' has more than one 'struct bpf_timer'\n", - map->name); - else if (map->timer_off == -ENOENT) - verbose(env, - "map '%s' doesn't have 'struct bpf_timer'\n", - map->name); - else - verbose(env, - "map '%s' is not a struct type or bpf_timer is mangled\n", - map->name); + if (!btf_record_has_field(map->record, BPF_TIMER)) { + verbose(env, "map '%s' has no valid bpf_timer\n", map->name); return -EINVAL; } - if (map->timer_off != val + reg->off) { + if (map->record->timer_off != val + reg->off) { verbose(env, "off %lld doesn't point to 'struct bpf_timer' that is at %d\n", - val + reg->off, map->timer_off); + val + reg->off, map->record->timer_off); return -EINVAL; } if (meta->map_ptr) { @@ -7470,7 +7430,7 @@ static int check_helper_call(struct bpf_verifier_env *env, struct bpf_insn *insn regs[BPF_REG_0].map_uid = meta.map_uid; regs[BPF_REG_0].type = PTR_TO_MAP_VALUE | ret_flag; if (!type_may_be_null(ret_type) && - map_value_has_spin_lock(meta.map_ptr)) { + btf_record_has_field(meta.map_ptr->record, BPF_SPIN_LOCK)) { regs[BPF_REG_0].id = ++env->id_gen; } break; @@ -10381,7 +10341,7 @@ static int check_ld_imm(struct bpf_verifier_env *env, struct bpf_insn *insn) insn->src_reg == BPF_PSEUDO_MAP_IDX_VALUE) { dst_reg->type = PTR_TO_MAP_VALUE; dst_reg->off = aux->map_off; - if (map_value_has_spin_lock(map)) + if (btf_record_has_field(map->record, BPF_SPIN_LOCK)) dst_reg->id = ++env->id_gen; } else if (insn->src_reg == BPF_PSEUDO_MAP_FD || insn->src_reg == BPF_PSEUDO_MAP_IDX) { @@ -12659,7 +12619,7 @@ static int check_map_prog_compatibility(struct bpf_verifier_env *env, { enum bpf_prog_type prog_type = resolve_prog_type(prog); - if (map_value_has_spin_lock(map)) { + if (btf_record_has_field(map->record, BPF_SPIN_LOCK)) { if (prog_type == BPF_PROG_TYPE_SOCKET_FILTER) { verbose(env, "socket filter progs cannot use bpf_spin_lock yet\n"); return -EINVAL; @@ -12676,7 +12636,7 @@ static int check_map_prog_compatibility(struct bpf_verifier_env *env, } } - if (map_value_has_timer(map)) { + if (btf_record_has_field(map->record, BPF_TIMER)) { if (is_tracing_prog_type(prog_type)) { verbose(env, "tracing progs cannot use bpf_timer yet\n"); return -EINVAL; diff --git a/net/core/bpf_sk_storage.c b/net/core/bpf_sk_storage.c index 49884e7de080..9d2288c0736e 100644 --- a/net/core/bpf_sk_storage.c +++ b/net/core/bpf_sk_storage.c @@ -147,7 +147,7 @@ bpf_sk_storage_clone_elem(struct sock *newsk, if (!copy_selem) return NULL; - if (map_value_has_spin_lock(&smap->map)) + if (btf_record_has_field(smap->map.record, BPF_SPIN_LOCK)) copy_map_value_locked(&smap->map, SDATA(copy_selem)->data, SDATA(selem)->data, true); else @@ -566,7 +566,7 @@ static int diag_get(struct bpf_local_storage_data *sdata, struct sk_buff *skb) if (!nla_value) goto errout; - if (map_value_has_spin_lock(&smap->map)) + if (btf_record_has_field(smap->map.record, BPF_SPIN_LOCK)) copy_map_value_locked(&smap->map, nla_data(nla_value), sdata->data, true); else From f71b2f64177a199d5b1d2047e155d45fd98f564a Mon Sep 17 00:00:00 2001 From: Kumar Kartikeya Dwivedi Date: Fri, 4 Nov 2022 00:39:57 +0530 Subject: [PATCH 0609/2002] bpf: Refactor map->off_arr handling Refactor map->off_arr handling into generic functions that can work on their own without hardcoding map specific code. The btf_fields_offs structure is now returned from btf_parse_field_offs, which can be reused later for types in program BTF. All functions like copy_map_value, zero_map_value call generic underlying functions so that they can also be reused later for copying to values allocated in programs which encode specific fields. Later, some helper functions will also require access to this btf_field_offs structure to be able to skip over special fields at runtime. Signed-off-by: Kumar Kartikeya Dwivedi Link: https://lore.kernel.org/r/20221103191013.1236066-9-memxor@gmail.com Signed-off-by: Alexei Starovoitov --- include/linux/bpf.h | 41 ++++++++++++++----------- include/linux/btf.h | 1 + kernel/bpf/btf.c | 55 +++++++++++++++++++++++++++++++++ kernel/bpf/syscall.c | 73 ++++++-------------------------------------- 4 files changed, 89 insertions(+), 81 deletions(-) diff --git a/include/linux/bpf.h b/include/linux/bpf.h index aae85019abde..798aec816970 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -341,57 +341,64 @@ static inline void bpf_long_memcpy(void *dst, const void *src, u32 size) } /* copy everything but bpf_spin_lock, bpf_timer, and kptrs. There could be one of each. */ -static inline void __copy_map_value(struct bpf_map *map, void *dst, void *src, bool long_memcpy) +static inline void bpf_obj_memcpy(struct btf_field_offs *foffs, + void *dst, void *src, u32 size, + bool long_memcpy) { u32 curr_off = 0; int i; - if (likely(!map->field_offs)) { + if (likely(!foffs)) { if (long_memcpy) - bpf_long_memcpy(dst, src, round_up(map->value_size, 8)); + bpf_long_memcpy(dst, src, round_up(size, 8)); else - memcpy(dst, src, map->value_size); + memcpy(dst, src, size); return; } - for (i = 0; i < map->field_offs->cnt; i++) { - u32 next_off = map->field_offs->field_off[i]; + for (i = 0; i < foffs->cnt; i++) { + u32 next_off = foffs->field_off[i]; u32 sz = next_off - curr_off; memcpy(dst + curr_off, src + curr_off, sz); - curr_off += map->field_offs->field_sz[i]; + curr_off += foffs->field_sz[i]; } - memcpy(dst + curr_off, src + curr_off, map->value_size - curr_off); + memcpy(dst + curr_off, src + curr_off, size - curr_off); } static inline void copy_map_value(struct bpf_map *map, void *dst, void *src) { - __copy_map_value(map, dst, src, false); + bpf_obj_memcpy(map->field_offs, dst, src, map->value_size, false); } static inline void copy_map_value_long(struct bpf_map *map, void *dst, void *src) { - __copy_map_value(map, dst, src, true); + bpf_obj_memcpy(map->field_offs, dst, src, map->value_size, true); } -static inline void zero_map_value(struct bpf_map *map, void *dst) +static inline void bpf_obj_memzero(struct btf_field_offs *foffs, void *dst, u32 size) { u32 curr_off = 0; int i; - if (likely(!map->field_offs)) { - memset(dst, 0, map->value_size); + if (likely(!foffs)) { + memset(dst, 0, size); return; } - for (i = 0; i < map->field_offs->cnt; i++) { - u32 next_off = map->field_offs->field_off[i]; + for (i = 0; i < foffs->cnt; i++) { + u32 next_off = foffs->field_off[i]; u32 sz = next_off - curr_off; memset(dst + curr_off, 0, sz); - curr_off += map->field_offs->field_sz[i]; + curr_off += foffs->field_sz[i]; } - memset(dst + curr_off, 0, map->value_size - curr_off); + memset(dst + curr_off, 0, size - curr_off); +} + +static inline void zero_map_value(struct bpf_map *map, void *dst) +{ + bpf_obj_memzero(map->field_offs, dst, map->value_size); } void copy_map_value_locked(struct bpf_map *map, void *dst, void *src, diff --git a/include/linux/btf.h b/include/linux/btf.h index 282006abd062..d80345fa566b 100644 --- a/include/linux/btf.h +++ b/include/linux/btf.h @@ -165,6 +165,7 @@ int btf_find_spin_lock(const struct btf *btf, const struct btf_type *t); int btf_find_timer(const struct btf *btf, const struct btf_type *t); struct btf_record *btf_parse_fields(const struct btf *btf, const struct btf_type *t, u32 field_mask, u32 value_size); +struct btf_field_offs *btf_parse_field_offs(struct btf_record *rec); bool btf_type_is_void(const struct btf_type *t); s32 btf_find_by_name_kind(const struct btf *btf, const char *name, u8 kind); const struct btf_type *btf_type_skip_modifiers(const struct btf *btf, diff --git a/kernel/bpf/btf.c b/kernel/bpf/btf.c index 3dad828db13c..5579ff3a5b54 100644 --- a/kernel/bpf/btf.c +++ b/kernel/bpf/btf.c @@ -3551,6 +3551,61 @@ end: return ERR_PTR(ret); } +static int btf_field_offs_cmp(const void *_a, const void *_b, const void *priv) +{ + const u32 a = *(const u32 *)_a; + const u32 b = *(const u32 *)_b; + + if (a < b) + return -1; + else if (a > b) + return 1; + return 0; +} + +static void btf_field_offs_swap(void *_a, void *_b, int size, const void *priv) +{ + struct btf_field_offs *foffs = (void *)priv; + u32 *off_base = foffs->field_off; + u32 *a = _a, *b = _b; + u8 *sz_a, *sz_b; + + sz_a = foffs->field_sz + (a - off_base); + sz_b = foffs->field_sz + (b - off_base); + + swap(*a, *b); + swap(*sz_a, *sz_b); +} + +struct btf_field_offs *btf_parse_field_offs(struct btf_record *rec) +{ + struct btf_field_offs *foffs; + u32 i, *off; + u8 *sz; + + BUILD_BUG_ON(ARRAY_SIZE(foffs->field_off) != ARRAY_SIZE(foffs->field_sz)); + if (IS_ERR_OR_NULL(rec) || WARN_ON_ONCE(rec->cnt > sizeof(foffs->field_off))) + return NULL; + + foffs = kzalloc(sizeof(*foffs), GFP_KERNEL | __GFP_NOWARN); + if (!foffs) + return ERR_PTR(-ENOMEM); + + off = foffs->field_off; + sz = foffs->field_sz; + for (i = 0; i < rec->cnt; i++) { + off[i] = rec->fields[i].offset; + sz[i] = btf_field_type_size(rec->fields[i].type); + } + foffs->cnt = rec->cnt; + + if (foffs->cnt == 1) + return foffs; + sort_r(foffs->field_off, foffs->cnt, sizeof(foffs->field_off[0]), + btf_field_offs_cmp, btf_field_offs_swap, foffs); + return foffs; +} + static void __btf_struct_show(const struct btf *btf, const struct btf_type *t, u32 type_id, void *data, u8 bits_offset, struct btf_show *show) diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c index 53d6dc5cf0e2..85532d301124 100644 --- a/kernel/bpf/syscall.c +++ b/kernel/bpf/syscall.c @@ -943,66 +943,6 @@ int map_check_no_btf(const struct bpf_map *map, return -ENOTSUPP; } -static int map_field_offs_cmp(const void *_a, const void *_b, const void *priv) -{ - const u32 a = *(const u32 *)_a; - const u32 b = *(const u32 *)_b; - - if (a < b) - return -1; - else if (a > b) - return 1; - return 0; -} - -static void map_field_offs_swap(void *_a, void *_b, int size, const void *priv) -{ - struct bpf_map *map = (struct bpf_map *)priv; - u32 *off_base = map->field_offs->field_off; - u32 *a = _a, *b = _b; - u8 *sz_a, *sz_b; - - sz_a = map->field_offs->field_sz + (a - off_base); - sz_b = map->field_offs->field_sz + (b - off_base); - - swap(*a, *b); - swap(*sz_a, *sz_b); -} - -static int bpf_map_alloc_off_arr(struct bpf_map *map) -{ - bool has_fields = !IS_ERR_OR_NULL(map->record); - struct btf_field_offs *fo; - struct btf_record *rec; - u32 i, *off; - u8 *sz; - - if (!has_fields) { - map->field_offs = NULL; - return 0; - } - - fo = kzalloc(sizeof(*map->field_offs), GFP_KERNEL | __GFP_NOWARN); - if (!fo) - return -ENOMEM; - map->field_offs = fo; - - rec = map->record; - off = fo->field_off; - sz = fo->field_sz; - for (i = 0; i < rec->cnt; i++) { - *off++ = rec->fields[i].offset; - *sz++ = btf_field_type_size(rec->fields[i].type); - } - fo->cnt = rec->cnt; - - if (fo->cnt == 1) - return 0; - sort_r(fo->field_off, fo->cnt, sizeof(fo->field_off[0]), - map_field_offs_cmp, map_field_offs_swap, map); - return 0; -} - static int map_check_btf(struct bpf_map *map, const struct btf *btf, u32 btf_key_id, u32 btf_value_id) { @@ -1097,6 +1037,7 @@ free_map_tab: static int map_create(union bpf_attr *attr) { int numa_node = bpf_map_attr_numa_node(attr); + struct btf_field_offs *foffs; struct bpf_map *map; int f_flags; int err; @@ -1176,13 +1117,17 @@ static int map_create(union bpf_attr *attr) attr->btf_vmlinux_value_type_id; } - err = bpf_map_alloc_off_arr(map); - if (err) + + foffs = btf_parse_field_offs(map->record); + if (IS_ERR(foffs)) { + err = PTR_ERR(foffs); goto free_map; + } + map->field_offs = foffs; err = security_bpf_map_alloc(map); if (err) - goto free_map_off_arr; + goto free_map_field_offs; err = bpf_map_alloc_id(map); if (err) @@ -1206,7 +1151,7 @@ static int map_create(union bpf_attr *attr) free_map_sec: security_bpf_map_free(map); -free_map_off_arr: +free_map_field_offs: kfree(map->field_offs); free_map: btf_put(map->btf); From d28c0e73efbee95edf789f07c494592f89862606 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Tue, 1 Nov 2022 10:41:04 +0000 Subject: [PATCH 0610/2002] rds: remove redundant variable total_payload_len Variable total_payload_len is being used to accumulate payload lengths however it is never read or used afterwards. It is redundant and can be removed. Signed-off-by: Colin Ian King Signed-off-by: David S. Miller --- net/rds/send.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/net/rds/send.c b/net/rds/send.c index 0c5504068e3c..5e57a1581dc6 100644 --- a/net/rds/send.c +++ b/net/rds/send.c @@ -1114,7 +1114,7 @@ int rds_sendmsg(struct socket *sock, struct msghdr *msg, size_t payload_len) struct rds_conn_path *cpath; struct in6_addr daddr; __u32 scope_id = 0; - size_t total_payload_len = payload_len, rdma_payload_len = 0; + size_t rdma_payload_len = 0; bool zcopy = ((msg->msg_flags & MSG_ZEROCOPY) && sock_flag(rds_rs_to_sk(rs), SOCK_ZEROCOPY)); int num_sgs = DIV_ROUND_UP(payload_len, PAGE_SIZE); @@ -1243,7 +1243,6 @@ int rds_sendmsg(struct socket *sock, struct msghdr *msg, size_t payload_len) if (ret) goto out; - total_payload_len += rdma_payload_len; if (max_t(size_t, payload_len, rdma_payload_len) > RDS_MAX_MSG_SIZE) { ret = -EMSGSIZE; goto out; From 5cb76899fb47791c12eb9fce4aef8a858c41783d Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Wed, 2 Nov 2022 17:11:31 -0500 Subject: [PATCH 0611/2002] net: ipa: reduce arguments to ipa_table_init_add() Recently ipa_table_mem() was added as a way to look up one of 8 possible memory regions by indicating whether it was a filter or route table, hashed or not, and IPv6 or not. We can simplify the interface to ipa_table_init_add() by passing two flags to it instead of the opcode and both hashed and non-hashed memory region IDs. The "filter" and "ipv6" flags are sufficient to determine the opcode to use, and with ipa_table_mem() can look up the correct memory region as well. It's possible to not have hashed tables, but we already verify the number of entries in a filter or routing table is nonzero. Stop assuming a hashed table entry exists in ipa_table_init_add(). Signed-off-by: Alex Elder Signed-off-by: David S. Miller --- drivers/net/ipa/ipa_table.c | 37 ++++++++++++++++++------------------- 1 file changed, 18 insertions(+), 19 deletions(-) diff --git a/drivers/net/ipa/ipa_table.c b/drivers/net/ipa/ipa_table.c index cf3a3de239dc..94bb7611e574 100644 --- a/drivers/net/ipa/ipa_table.c +++ b/drivers/net/ipa/ipa_table.c @@ -376,14 +376,12 @@ int ipa_table_hash_flush(struct ipa *ipa) return 0; } -static void ipa_table_init_add(struct gsi_trans *trans, bool filter, - enum ipa_cmd_opcode opcode, - enum ipa_mem_id mem_id, - enum ipa_mem_id hash_mem_id) +static void ipa_table_init_add(struct gsi_trans *trans, bool filter, bool ipv6) { struct ipa *ipa = container_of(trans->gsi, struct ipa, gsi); - const struct ipa_mem *hash_mem = ipa_mem_find(ipa, hash_mem_id); - const struct ipa_mem *mem = ipa_mem_find(ipa, mem_id); + const struct ipa_mem *hash_mem; + enum ipa_cmd_opcode opcode; + const struct ipa_mem *mem; dma_addr_t hash_addr; dma_addr_t addr; u32 zero_offset; @@ -393,6 +391,14 @@ static void ipa_table_init_add(struct gsi_trans *trans, bool filter, u16 count; u16 size; + opcode = filter ? ipv6 ? IPA_CMD_IP_V6_FILTER_INIT + : IPA_CMD_IP_V4_FILTER_INIT + : ipv6 ? IPA_CMD_IP_V6_ROUTING_INIT + : IPA_CMD_IP_V4_ROUTING_INIT; + + mem = ipa_table_mem(ipa, filter, false, ipv6); + hash_mem = ipa_table_mem(ipa, filter, true, ipv6); + /* Compute the number of table entries to initialize */ if (filter) { /* The number of filtering endpoints determines number of @@ -401,13 +407,13 @@ static void ipa_table_init_add(struct gsi_trans *trans, bool filter, * table is either the same as the non-hashed one, or zero. */ count = 1 + hweight32(ipa->filter_map); - hash_count = hash_mem->size ? count : 0; + hash_count = hash_mem && hash_mem->size ? count : 0; } else { /* The size of a route table region determines the number * of entries it has. */ count = mem->size / sizeof(__le64); - hash_count = hash_mem->size / sizeof(__le64); + hash_count = hash_mem && hash_mem->size / sizeof(__le64); } size = count * sizeof(__le64); hash_size = hash_count * sizeof(__le64); @@ -458,17 +464,10 @@ int ipa_table_setup(struct ipa *ipa) return -EBUSY; } - ipa_table_init_add(trans, false, IPA_CMD_IP_V4_ROUTING_INIT, - IPA_MEM_V4_ROUTE, IPA_MEM_V4_ROUTE_HASHED); - - ipa_table_init_add(trans, false, IPA_CMD_IP_V6_ROUTING_INIT, - IPA_MEM_V6_ROUTE, IPA_MEM_V6_ROUTE_HASHED); - - ipa_table_init_add(trans, true, IPA_CMD_IP_V4_FILTER_INIT, - IPA_MEM_V4_FILTER, IPA_MEM_V4_FILTER_HASHED); - - ipa_table_init_add(trans, true, IPA_CMD_IP_V6_FILTER_INIT, - IPA_MEM_V6_FILTER, IPA_MEM_V6_FILTER_HASHED); + ipa_table_init_add(trans, false, false); + ipa_table_init_add(trans, false, true); + ipa_table_init_add(trans, true, false); + ipa_table_init_add(trans, true, true); gsi_trans_commit_wait(trans); From 6337b147828b4e3e893bde16dca8d4b6654d11c1 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Wed, 2 Nov 2022 17:11:32 -0500 Subject: [PATCH 0612/2002] net: ipa: use ipa_table_mem() in ipa_table_reset_add() Similar to the previous commit, pass flags rather than a memory region ID to ipa_table_reset_add(), and there use ipa_table_mem() to look up the memory region affected based on those flags. Currently all eight of these table memory regions are assumed to exist, because they all have canaries within them. Stop assuming that will always be the case, and in ipa_table_reset_add() allow these memory regions to be non-existent. Signed-off-by: Alex Elder Signed-off-by: David S. Miller --- drivers/net/ipa/ipa_table.c | 31 +++++++++++++++---------------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/drivers/net/ipa/ipa_table.c b/drivers/net/ipa/ipa_table.c index 94bb7611e574..3a14465bf8a6 100644 --- a/drivers/net/ipa/ipa_table.c +++ b/drivers/net/ipa/ipa_table.c @@ -200,16 +200,17 @@ static dma_addr_t ipa_table_addr(struct ipa *ipa, bool filter_mask, u16 count) } static void ipa_table_reset_add(struct gsi_trans *trans, bool filter, - u16 first, u16 count, enum ipa_mem_id mem_id) + bool hashed, bool ipv6, u16 first, u16 count) { struct ipa *ipa = container_of(trans->gsi, struct ipa, gsi); - const struct ipa_mem *mem = ipa_mem_find(ipa, mem_id); + const struct ipa_mem *mem; dma_addr_t addr; u32 offset; u16 size; - /* Nothing to do if the table memory region is empty */ - if (!mem->size) + /* Nothing to do if the memory region is doesn't exist or is empty */ + mem = ipa_table_mem(ipa, filter, hashed, ipv6); + if (!mem || !mem->size) return; if (filter) @@ -227,7 +228,7 @@ static void ipa_table_reset_add(struct gsi_trans *trans, bool filter, * for the IPv4 and IPv6 non-hashed and hashed filter tables. */ static int -ipa_filter_reset_table(struct ipa *ipa, enum ipa_mem_id mem_id, bool modem) +ipa_filter_reset_table(struct ipa *ipa, bool hashed, bool ipv6, bool modem) { u32 ep_mask = ipa->filter_map; u32 count = hweight32(ep_mask); @@ -253,7 +254,7 @@ ipa_filter_reset_table(struct ipa *ipa, enum ipa_mem_id mem_id, bool modem) if (endpoint->ee_id != ee_id) continue; - ipa_table_reset_add(trans, true, endpoint_id, 1, mem_id); + ipa_table_reset_add(trans, true, hashed, ipv6, endpoint_id, 1); } gsi_trans_commit_wait(trans); @@ -269,18 +270,18 @@ static int ipa_filter_reset(struct ipa *ipa, bool modem) { int ret; - ret = ipa_filter_reset_table(ipa, IPA_MEM_V4_FILTER, modem); + ret = ipa_filter_reset_table(ipa, false, false, modem); if (ret) return ret; - ret = ipa_filter_reset_table(ipa, IPA_MEM_V4_FILTER_HASHED, modem); + ret = ipa_filter_reset_table(ipa, true, false, modem); if (ret) return ret; - ret = ipa_filter_reset_table(ipa, IPA_MEM_V6_FILTER, modem); + ret = ipa_filter_reset_table(ipa, false, true, modem); if (ret) return ret; - ret = ipa_filter_reset_table(ipa, IPA_MEM_V6_FILTER_HASHED, modem); + ret = ipa_filter_reset_table(ipa, true, true, modem); return ret; } @@ -312,13 +313,11 @@ static int ipa_route_reset(struct ipa *ipa, bool modem) count = ipa->route_count - modem_route_count; } - ipa_table_reset_add(trans, false, first, count, IPA_MEM_V4_ROUTE); - ipa_table_reset_add(trans, false, first, count, - IPA_MEM_V4_ROUTE_HASHED); + ipa_table_reset_add(trans, false, false, false, first, count); + ipa_table_reset_add(trans, false, true, false, first, count); - ipa_table_reset_add(trans, false, first, count, IPA_MEM_V6_ROUTE); - ipa_table_reset_add(trans, false, first, count, - IPA_MEM_V6_ROUTE_HASHED); + ipa_table_reset_add(trans, false, false, true, first, count); + ipa_table_reset_add(trans, false, true, true, first, count); gsi_trans_commit_wait(trans); From 1d8f16dbdf3693cae7567806cb1fb714fd1bcc70 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Wed, 2 Nov 2022 17:11:33 -0500 Subject: [PATCH 0613/2002] net: ipa: add a parameter to aggregation registers Starting with IPA v5.0, a single IPA instance can have more than 32 endpoints defined. To handle this, each register that holds a bitmap of IPA endpoints is replicated as needed to represent the available endpoints. To prepare for this, registers that represent endpoint IDs in a bit mask will be defined to have a parameter, with a stride value of 4 bytes. The first 32 endpoints are represented in the first 32-bit register, then the next (up to) 32 endpoints at an offset 4 bytes higher. When accessing such a register, the endpoint ID divided by 32 determines the offset, and the endpoint ID modulo 32 defines the endpoint's bit position within the register. The first two registers we'll update for this are STATE_AGGR_ACTIVE and AGGR_FORCE_CLOSE. Until more than 32 endpoints are supported, this change has no practical effect. Signed-off-by: Alex Elder Signed-off-by: David S. Miller --- drivers/net/ipa/ipa_endpoint.c | 14 ++++++++++---- drivers/net/ipa/reg/ipa_reg-v3.1.c | 4 ++-- drivers/net/ipa/reg/ipa_reg-v3.5.1.c | 4 ++-- drivers/net/ipa/reg/ipa_reg-v4.11.c | 4 ++-- drivers/net/ipa/reg/ipa_reg-v4.2.c | 4 ++-- drivers/net/ipa/reg/ipa_reg-v4.5.c | 4 ++-- drivers/net/ipa/reg/ipa_reg-v4.9.c | 4 ++-- 7 files changed, 22 insertions(+), 16 deletions(-) diff --git a/drivers/net/ipa/ipa_endpoint.c b/drivers/net/ipa/ipa_endpoint.c index 2a6184ea8f5c..32559ed498c1 100644 --- a/drivers/net/ipa/ipa_endpoint.c +++ b/drivers/net/ipa/ipa_endpoint.c @@ -350,29 +350,35 @@ ipa_endpoint_program_delay(struct ipa_endpoint *endpoint, bool enable) static bool ipa_endpoint_aggr_active(struct ipa_endpoint *endpoint) { - u32 mask = BIT(endpoint->endpoint_id); + u32 endpoint_id = endpoint->endpoint_id; + u32 mask = BIT(endpoint_id % 32); struct ipa *ipa = endpoint->ipa; + u32 unit = endpoint_id / 32; const struct ipa_reg *reg; u32 val; + /* This works until we actually have more than 32 endpoints */ WARN_ON(!(mask & ipa->available)); reg = ipa_reg(ipa, STATE_AGGR_ACTIVE); - val = ioread32(ipa->reg_virt + ipa_reg_offset(reg)); + val = ioread32(ipa->reg_virt + ipa_reg_n_offset(reg, unit)); return !!(val & mask); } static void ipa_endpoint_force_close(struct ipa_endpoint *endpoint) { - u32 mask = BIT(endpoint->endpoint_id); + u32 endpoint_id = endpoint->endpoint_id; + u32 mask = BIT(endpoint_id % 32); struct ipa *ipa = endpoint->ipa; + u32 unit = endpoint_id / 32; const struct ipa_reg *reg; + /* This works until we actually have more than 32 endpoints */ WARN_ON(!(mask & ipa->available)); reg = ipa_reg(ipa, AGGR_FORCE_CLOSE); - iowrite32(mask, ipa->reg_virt + ipa_reg_offset(reg)); + iowrite32(mask, ipa->reg_virt + ipa_reg_n_offset(reg, unit)); } /** diff --git a/drivers/net/ipa/reg/ipa_reg-v3.1.c b/drivers/net/ipa/reg/ipa_reg-v3.1.c index 0d002c3c38a2..0b6edc2912bd 100644 --- a/drivers/net/ipa/reg/ipa_reg-v3.1.c +++ b/drivers/net/ipa/reg/ipa_reg-v3.1.c @@ -103,7 +103,7 @@ static const u32 ipa_reg_filt_rout_hash_flush_fmask[] = { IPA_REG_FIELDS(FILT_ROUT_HASH_FLUSH, filt_rout_hash_flush, 0x0000090); /* Valid bits defined by ipa->available */ -IPA_REG(STATE_AGGR_ACTIVE, state_aggr_active, 0x0000010c); +IPA_REG_STRIDE(STATE_AGGR_ACTIVE, state_aggr_active, 0x0000010c, 0x0004); IPA_REG(IPA_BCR, ipa_bcr, 0x000001d0); @@ -116,7 +116,7 @@ static const u32 ipa_reg_local_pkt_proc_cntxt_fmask[] = { IPA_REG_FIELDS(LOCAL_PKT_PROC_CNTXT, local_pkt_proc_cntxt, 0x000001e8); /* Valid bits defined by ipa->available */ -IPA_REG(AGGR_FORCE_CLOSE, aggr_force_close, 0x000001ec); +IPA_REG_STRIDE(AGGR_FORCE_CLOSE, aggr_force_close, 0x000001ec, 0x0004); static const u32 ipa_reg_counter_cfg_fmask[] = { [EOT_COAL_GRANULARITY] = GENMASK(3, 0), diff --git a/drivers/net/ipa/reg/ipa_reg-v3.5.1.c b/drivers/net/ipa/reg/ipa_reg-v3.5.1.c index 6e2f939b18f1..10f62f6aaf7a 100644 --- a/drivers/net/ipa/reg/ipa_reg-v3.5.1.c +++ b/drivers/net/ipa/reg/ipa_reg-v3.5.1.c @@ -108,7 +108,7 @@ static const u32 ipa_reg_filt_rout_hash_flush_fmask[] = { IPA_REG_FIELDS(FILT_ROUT_HASH_FLUSH, filt_rout_hash_flush, 0x0000090); /* Valid bits defined by ipa->available */ -IPA_REG(STATE_AGGR_ACTIVE, state_aggr_active, 0x0000010c); +IPA_REG_STRIDE(STATE_AGGR_ACTIVE, state_aggr_active, 0x0000010c, 0x0004); IPA_REG(IPA_BCR, ipa_bcr, 0x000001d0); @@ -121,7 +121,7 @@ static const u32 ipa_reg_local_pkt_proc_cntxt_fmask[] = { IPA_REG_FIELDS(LOCAL_PKT_PROC_CNTXT, local_pkt_proc_cntxt, 0x000001e8); /* Valid bits defined by ipa->available */ -IPA_REG(AGGR_FORCE_CLOSE, aggr_force_close, 0x000001ec); +IPA_REG_STRIDE(AGGR_FORCE_CLOSE, aggr_force_close, 0x000001ec, 0x0004); static const u32 ipa_reg_counter_cfg_fmask[] = { /* Bits 0-3 reserved */ diff --git a/drivers/net/ipa/reg/ipa_reg-v4.11.c b/drivers/net/ipa/reg/ipa_reg-v4.11.c index 8fd36569bb9f..113a25c006da 100644 --- a/drivers/net/ipa/reg/ipa_reg-v4.11.c +++ b/drivers/net/ipa/reg/ipa_reg-v4.11.c @@ -140,7 +140,7 @@ static const u32 ipa_reg_filt_rout_hash_flush_fmask[] = { IPA_REG_FIELDS(FILT_ROUT_HASH_FLUSH, filt_rout_hash_flush, 0x000014c); /* Valid bits defined by ipa->available */ -IPA_REG(STATE_AGGR_ACTIVE, state_aggr_active, 0x000000b4); +IPA_REG_STRIDE(STATE_AGGR_ACTIVE, state_aggr_active, 0x000000b4, 0x0004); static const u32 ipa_reg_local_pkt_proc_cntxt_fmask[] = { [IPA_BASE_ADDR] = GENMASK(17, 0), @@ -151,7 +151,7 @@ static const u32 ipa_reg_local_pkt_proc_cntxt_fmask[] = { IPA_REG_FIELDS(LOCAL_PKT_PROC_CNTXT, local_pkt_proc_cntxt, 0x000001e8); /* Valid bits defined by ipa->available */ -IPA_REG(AGGR_FORCE_CLOSE, aggr_force_close, 0x000001ec); +IPA_REG_STRIDE(AGGR_FORCE_CLOSE, aggr_force_close, 0x000001ec, 0x0004); static const u32 ipa_reg_ipa_tx_cfg_fmask[] = { /* Bits 0-1 reserved */ diff --git a/drivers/net/ipa/reg/ipa_reg-v4.2.c b/drivers/net/ipa/reg/ipa_reg-v4.2.c index f8e78e1907c8..c93f2da9290f 100644 --- a/drivers/net/ipa/reg/ipa_reg-v4.2.c +++ b/drivers/net/ipa/reg/ipa_reg-v4.2.c @@ -132,7 +132,7 @@ static const u32 ipa_reg_filt_rout_hash_flush_fmask[] = { IPA_REG_FIELDS(FILT_ROUT_HASH_FLUSH, filt_rout_hash_flush, 0x000014c); /* Valid bits defined by ipa->available */ -IPA_REG(STATE_AGGR_ACTIVE, state_aggr_active, 0x000000b4); +IPA_REG_STRIDE(STATE_AGGR_ACTIVE, state_aggr_active, 0x000000b4, 0x0004); IPA_REG(IPA_BCR, ipa_bcr, 0x000001d0); @@ -145,7 +145,7 @@ static const u32 ipa_reg_local_pkt_proc_cntxt_fmask[] = { IPA_REG_FIELDS(LOCAL_PKT_PROC_CNTXT, local_pkt_proc_cntxt, 0x000001e8); /* Valid bits defined by ipa->available */ -IPA_REG(AGGR_FORCE_CLOSE, aggr_force_close, 0x000001ec); +IPA_REG_STRIDE(AGGR_FORCE_CLOSE, aggr_force_close, 0x000001ec, 0x0004); static const u32 ipa_reg_counter_cfg_fmask[] = { /* Bits 0-3 reserved */ diff --git a/drivers/net/ipa/reg/ipa_reg-v4.5.c b/drivers/net/ipa/reg/ipa_reg-v4.5.c index d32b805abb11..1615c5ead8cc 100644 --- a/drivers/net/ipa/reg/ipa_reg-v4.5.c +++ b/drivers/net/ipa/reg/ipa_reg-v4.5.c @@ -134,7 +134,7 @@ static const u32 ipa_reg_filt_rout_hash_flush_fmask[] = { IPA_REG_FIELDS(FILT_ROUT_HASH_FLUSH, filt_rout_hash_flush, 0x000014c); /* Valid bits defined by ipa->available */ -IPA_REG(STATE_AGGR_ACTIVE, state_aggr_active, 0x000000b4); +IPA_REG_STRIDE(STATE_AGGR_ACTIVE, state_aggr_active, 0x000000b4, 0x0004); static const u32 ipa_reg_local_pkt_proc_cntxt_fmask[] = { [IPA_BASE_ADDR] = GENMASK(17, 0), @@ -145,7 +145,7 @@ static const u32 ipa_reg_local_pkt_proc_cntxt_fmask[] = { IPA_REG_FIELDS(LOCAL_PKT_PROC_CNTXT, local_pkt_proc_cntxt, 0x000001e8); /* Valid bits defined by ipa->available */ -IPA_REG(AGGR_FORCE_CLOSE, aggr_force_close, 0x000001ec); +IPA_REG_STRIDE(AGGR_FORCE_CLOSE, aggr_force_close, 0x000001ec, 0x0004); static const u32 ipa_reg_ipa_tx_cfg_fmask[] = { /* Bits 0-1 reserved */ diff --git a/drivers/net/ipa/reg/ipa_reg-v4.9.c b/drivers/net/ipa/reg/ipa_reg-v4.9.c index eabbc5451937..4efc890d3158 100644 --- a/drivers/net/ipa/reg/ipa_reg-v4.9.c +++ b/drivers/net/ipa/reg/ipa_reg-v4.9.c @@ -139,7 +139,7 @@ static const u32 ipa_reg_filt_rout_hash_flush_fmask[] = { IPA_REG_FIELDS(FILT_ROUT_HASH_FLUSH, filt_rout_hash_flush, 0x000014c); /* Valid bits defined by ipa->available */ -IPA_REG(STATE_AGGR_ACTIVE, state_aggr_active, 0x000000b4); +IPA_REG_STRIDE(STATE_AGGR_ACTIVE, state_aggr_active, 0x000000b4, 0x0004); static const u32 ipa_reg_local_pkt_proc_cntxt_fmask[] = { [IPA_BASE_ADDR] = GENMASK(17, 0), @@ -150,7 +150,7 @@ static const u32 ipa_reg_local_pkt_proc_cntxt_fmask[] = { IPA_REG_FIELDS(LOCAL_PKT_PROC_CNTXT, local_pkt_proc_cntxt, 0x000001e8); /* Valid bits defined by ipa->available */ -IPA_REG(AGGR_FORCE_CLOSE, aggr_force_close, 0x000001ec); +IPA_REG_STRIDE(AGGR_FORCE_CLOSE, aggr_force_close, 0x000001ec, 0x0004); static const u32 ipa_reg_ipa_tx_cfg_fmask[] = { /* Bits 0-1 reserved */ From f298ba785e2d4f18bc2d01ee1ce719efe14242f0 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Wed, 2 Nov 2022 17:11:34 -0500 Subject: [PATCH 0614/2002] net: ipa: add a parameter to suspend registers The SUSPEND_INFO, SUSPEND_EN, SUSPEND_CLR registers represent endpoint IDs in a bit mask. When more than 32 endpoints are supported, these registers will be replicated as needed to represent the number of supported endpoints. Update the definitions of these registers to have a stride of 4 bytes, and update the code that operates them to select the proper offset and bit. Signed-off-by: Alex Elder Signed-off-by: David S. Miller --- drivers/net/ipa/ipa_interrupt.c | 30 ++++++++++++++++++---------- drivers/net/ipa/reg/ipa_reg-v3.1.c | 9 ++++++--- drivers/net/ipa/reg/ipa_reg-v3.5.1.c | 9 ++++++--- drivers/net/ipa/reg/ipa_reg-v4.11.c | 9 ++++++--- drivers/net/ipa/reg/ipa_reg-v4.2.c | 9 ++++++--- drivers/net/ipa/reg/ipa_reg-v4.5.c | 9 ++++++--- drivers/net/ipa/reg/ipa_reg-v4.9.c | 9 ++++++--- 7 files changed, 55 insertions(+), 29 deletions(-) diff --git a/drivers/net/ipa/ipa_interrupt.c b/drivers/net/ipa/ipa_interrupt.c index c269432f9c2e..a62bc667bda0 100644 --- a/drivers/net/ipa/ipa_interrupt.c +++ b/drivers/net/ipa/ipa_interrupt.c @@ -132,11 +132,13 @@ static void ipa_interrupt_suspend_control(struct ipa_interrupt *interrupt, u32 endpoint_id, bool enable) { struct ipa *ipa = interrupt->ipa; - u32 mask = BIT(endpoint_id); + u32 mask = BIT(endpoint_id % 32); + u32 unit = endpoint_id / 32; const struct ipa_reg *reg; u32 offset; u32 val; + /* This works until we actually have more than 32 endpoints */ WARN_ON(!(mask & ipa->available)); /* IPA version 3.0 does not support TX_SUSPEND interrupt control */ @@ -144,7 +146,7 @@ static void ipa_interrupt_suspend_control(struct ipa_interrupt *interrupt, return; reg = ipa_reg(ipa, IRQ_SUSPEND_EN); - offset = ipa_reg_offset(reg); + offset = ipa_reg_n_offset(reg, unit); val = ioread32(ipa->reg_virt + offset); if (enable) val |= mask; @@ -171,18 +173,24 @@ ipa_interrupt_suspend_disable(struct ipa_interrupt *interrupt, u32 endpoint_id) void ipa_interrupt_suspend_clear_all(struct ipa_interrupt *interrupt) { struct ipa *ipa = interrupt->ipa; - const struct ipa_reg *reg; - u32 val; + u32 unit_count; + u32 unit; - reg = ipa_reg(ipa, IRQ_SUSPEND_INFO); - val = ioread32(ipa->reg_virt + ipa_reg_offset(reg)); + unit_count = roundup(ipa->endpoint_count, 32); + for (unit = 0; unit < unit_count; unit++) { + const struct ipa_reg *reg; + u32 val; - /* SUSPEND interrupt status isn't cleared on IPA version 3.0 */ - if (ipa->version == IPA_VERSION_3_0) - return; + reg = ipa_reg(ipa, IRQ_SUSPEND_INFO); + val = ioread32(ipa->reg_virt + ipa_reg_n_offset(reg, unit)); - reg = ipa_reg(ipa, IRQ_SUSPEND_CLR); - iowrite32(val, ipa->reg_virt + ipa_reg_offset(reg)); + /* SUSPEND interrupt status isn't cleared on IPA version 3.0 */ + if (ipa->version == IPA_VERSION_3_0) + continue; + + reg = ipa_reg(ipa, IRQ_SUSPEND_CLR); + iowrite32(val, ipa->reg_virt + ipa_reg_n_offset(reg, unit)); + } } /* Simulate arrival of an IPA TX_SUSPEND interrupt */ diff --git a/drivers/net/ipa/reg/ipa_reg-v3.1.c b/drivers/net/ipa/reg/ipa_reg-v3.1.c index 0b6edc2912bd..677ece3bce9e 100644 --- a/drivers/net/ipa/reg/ipa_reg-v3.1.c +++ b/drivers/net/ipa/reg/ipa_reg-v3.1.c @@ -386,13 +386,16 @@ static const u32 ipa_reg_ipa_irq_uc_fmask[] = { IPA_REG_FIELDS(IPA_IRQ_UC, ipa_irq_uc, 0x0000301c + 0x1000 * GSI_EE_AP); /* Valid bits defined by ipa->available */ -IPA_REG(IRQ_SUSPEND_INFO, irq_suspend_info, 0x00003030 + 0x1000 * GSI_EE_AP); +IPA_REG_STRIDE(IRQ_SUSPEND_INFO, irq_suspend_info, + 0x00003030 + 0x1000 * GSI_EE_AP, 0x0004); /* Valid bits defined by ipa->available */ -IPA_REG(IRQ_SUSPEND_EN, irq_suspend_en, 0x00003034 + 0x1000 * GSI_EE_AP); +IPA_REG_STRIDE(IRQ_SUSPEND_EN, irq_suspend_en, + 0x00003034 + 0x1000 * GSI_EE_AP, 0x0004); /* Valid bits defined by ipa->available */ -IPA_REG(IRQ_SUSPEND_CLR, irq_suspend_clr, 0x00003038 + 0x1000 * GSI_EE_AP); +IPA_REG_STRIDE(IRQ_SUSPEND_CLR, irq_suspend_clr, + 0x00003038 + 0x1000 * GSI_EE_AP, 0x0004); static const struct ipa_reg *ipa_reg_array[] = { [COMP_CFG] = &ipa_reg_comp_cfg, diff --git a/drivers/net/ipa/reg/ipa_reg-v3.5.1.c b/drivers/net/ipa/reg/ipa_reg-v3.5.1.c index 10f62f6aaf7a..b9c6a50de243 100644 --- a/drivers/net/ipa/reg/ipa_reg-v3.5.1.c +++ b/drivers/net/ipa/reg/ipa_reg-v3.5.1.c @@ -397,13 +397,16 @@ static const u32 ipa_reg_ipa_irq_uc_fmask[] = { IPA_REG_FIELDS(IPA_IRQ_UC, ipa_irq_uc, 0x0000301c + 0x1000 * GSI_EE_AP); /* Valid bits defined by ipa->available */ -IPA_REG(IRQ_SUSPEND_INFO, irq_suspend_info, 0x00003030 + 0x1000 * GSI_EE_AP); +IPA_REG_STRIDE(IRQ_SUSPEND_INFO, irq_suspend_info, + 0x00003030 + 0x1000 * GSI_EE_AP, 0x0004); /* Valid bits defined by ipa->available */ -IPA_REG(IRQ_SUSPEND_EN, irq_suspend_en, 0x00003034 + 0x1000 * GSI_EE_AP); +IPA_REG_STRIDE(IRQ_SUSPEND_EN, irq_suspend_en, + 0x00003034 + 0x1000 * GSI_EE_AP, 0x0004); /* Valid bits defined by ipa->available */ -IPA_REG(IRQ_SUSPEND_CLR, irq_suspend_clr, 0x00003038 + 0x1000 * GSI_EE_AP); +IPA_REG_STRIDE(IRQ_SUSPEND_CLR, irq_suspend_clr, + 0x00003038 + 0x1000 * GSI_EE_AP, 0x0004); static const struct ipa_reg *ipa_reg_array[] = { [COMP_CFG] = &ipa_reg_comp_cfg, diff --git a/drivers/net/ipa/reg/ipa_reg-v4.11.c b/drivers/net/ipa/reg/ipa_reg-v4.11.c index 113a25c006da..9a315130530d 100644 --- a/drivers/net/ipa/reg/ipa_reg-v4.11.c +++ b/drivers/net/ipa/reg/ipa_reg-v4.11.c @@ -453,13 +453,16 @@ static const u32 ipa_reg_ipa_irq_uc_fmask[] = { IPA_REG_FIELDS(IPA_IRQ_UC, ipa_irq_uc, 0x0000401c + 0x1000 * GSI_EE_AP); /* Valid bits defined by ipa->available */ -IPA_REG(IRQ_SUSPEND_INFO, irq_suspend_info, 0x00004030 + 0x1000 * GSI_EE_AP); +IPA_REG_STRIDE(IRQ_SUSPEND_INFO, irq_suspend_info, + 0x00004030 + 0x1000 * GSI_EE_AP, 0x0004); /* Valid bits defined by ipa->available */ -IPA_REG(IRQ_SUSPEND_EN, irq_suspend_en, 0x00004034 + 0x1000 * GSI_EE_AP); +IPA_REG_STRIDE(IRQ_SUSPEND_EN, irq_suspend_en, + 0x00004034 + 0x1000 * GSI_EE_AP, 0x0004); /* Valid bits defined by ipa->available */ -IPA_REG(IRQ_SUSPEND_CLR, irq_suspend_clr, 0x00004038 + 0x1000 * GSI_EE_AP); +IPA_REG_STRIDE(IRQ_SUSPEND_CLR, irq_suspend_clr, + 0x00004038 + 0x1000 * GSI_EE_AP, 0x0004); static const struct ipa_reg *ipa_reg_array[] = { [COMP_CFG] = &ipa_reg_comp_cfg, diff --git a/drivers/net/ipa/reg/ipa_reg-v4.2.c b/drivers/net/ipa/reg/ipa_reg-v4.2.c index c93f2da9290f..7a95149f8ec7 100644 --- a/drivers/net/ipa/reg/ipa_reg-v4.2.c +++ b/drivers/net/ipa/reg/ipa_reg-v4.2.c @@ -399,13 +399,16 @@ static const u32 ipa_reg_ipa_irq_uc_fmask[] = { IPA_REG_FIELDS(IPA_IRQ_UC, ipa_irq_uc, 0x0000301c + 0x1000 * GSI_EE_AP); /* Valid bits defined by ipa->available */ -IPA_REG(IRQ_SUSPEND_INFO, irq_suspend_info, 0x00003030 + 0x1000 * GSI_EE_AP); +IPA_REG_STRIDE(IRQ_SUSPEND_INFO, irq_suspend_info, + 0x00003030 + 0x1000 * GSI_EE_AP, 0x0004); /* Valid bits defined by ipa->available */ -IPA_REG(IRQ_SUSPEND_EN, irq_suspend_en, 0x00003034 + 0x1000 * GSI_EE_AP); +IPA_REG_STRIDE(IRQ_SUSPEND_EN, irq_suspend_en, + 0x00003034 + 0x1000 * GSI_EE_AP, 0x0004); /* Valid bits defined by ipa->available */ -IPA_REG(IRQ_SUSPEND_CLR, irq_suspend_clr, 0x00003038 + 0x1000 * GSI_EE_AP); +IPA_REG_STRIDE(IRQ_SUSPEND_CLR, irq_suspend_clr, + 0x00003038 + 0x1000 * GSI_EE_AP, 0x0004); static const struct ipa_reg *ipa_reg_array[] = { [COMP_CFG] = &ipa_reg_comp_cfg, diff --git a/drivers/net/ipa/reg/ipa_reg-v4.5.c b/drivers/net/ipa/reg/ipa_reg-v4.5.c index 1615c5ead8cc..587eb8d4e00f 100644 --- a/drivers/net/ipa/reg/ipa_reg-v4.5.c +++ b/drivers/net/ipa/reg/ipa_reg-v4.5.c @@ -472,13 +472,16 @@ static const u32 ipa_reg_ipa_irq_uc_fmask[] = { IPA_REG_FIELDS(IPA_IRQ_UC, ipa_irq_uc, 0x0000301c + 0x1000 * GSI_EE_AP); /* Valid bits defined by ipa->available */ -IPA_REG(IRQ_SUSPEND_INFO, irq_suspend_info, 0x00003030 + 0x1000 * GSI_EE_AP); +IPA_REG_STRIDE(IRQ_SUSPEND_INFO, irq_suspend_info, + 0x00003030 + 0x1000 * GSI_EE_AP, 0x0004); /* Valid bits defined by ipa->available */ -IPA_REG(IRQ_SUSPEND_EN, irq_suspend_en, 0x00003034 + 0x1000 * GSI_EE_AP); +IPA_REG_STRIDE(IRQ_SUSPEND_EN, irq_suspend_en, + 0x00003034 + 0x1000 * GSI_EE_AP, 0x0004); /* Valid bits defined by ipa->available */ -IPA_REG(IRQ_SUSPEND_CLR, irq_suspend_clr, 0x00003038 + 0x1000 * GSI_EE_AP); +IPA_REG_STRIDE(IRQ_SUSPEND_CLR, irq_suspend_clr, + 0x00003038 + 0x1000 * GSI_EE_AP, 0x0004); static const struct ipa_reg *ipa_reg_array[] = { [COMP_CFG] = &ipa_reg_comp_cfg, diff --git a/drivers/net/ipa/reg/ipa_reg-v4.9.c b/drivers/net/ipa/reg/ipa_reg-v4.9.c index 4efc890d3158..1f67a03fe599 100644 --- a/drivers/net/ipa/reg/ipa_reg-v4.9.c +++ b/drivers/net/ipa/reg/ipa_reg-v4.9.c @@ -450,13 +450,16 @@ static const u32 ipa_reg_ipa_irq_uc_fmask[] = { IPA_REG_FIELDS(IPA_IRQ_UC, ipa_irq_uc, 0x0000401c + 0x1000 * GSI_EE_AP); /* Valid bits defined by ipa->available */ -IPA_REG(IRQ_SUSPEND_INFO, irq_suspend_info, 0x00004030 + 0x1000 * GSI_EE_AP); +IPA_REG_STRIDE(IRQ_SUSPEND_INFO, irq_suspend_info, + 0x00004030 + 0x1000 * GSI_EE_AP, 0x0004); /* Valid bits defined by ipa->available */ -IPA_REG(IRQ_SUSPEND_EN, irq_suspend_en, 0x00004034 + 0x1000 * GSI_EE_AP); +IPA_REG_STRIDE(IRQ_SUSPEND_EN, irq_suspend_en, + 0x00004034 + 0x1000 * GSI_EE_AP, 0x0004); /* Valid bits defined by ipa->available */ -IPA_REG(IRQ_SUSPEND_CLR, irq_suspend_clr, 0x00004038 + 0x1000 * GSI_EE_AP); +IPA_REG_STRIDE(IRQ_SUSPEND_CLR, irq_suspend_clr, + 0x00004038 + 0x1000 * GSI_EE_AP, 0x0004); static const struct ipa_reg *ipa_reg_array[] = { [COMP_CFG] = &ipa_reg_comp_cfg, From 9a9f512974d5e3e721e106f30429c16dfeb23326 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Wed, 2 Nov 2022 17:11:35 -0500 Subject: [PATCH 0615/2002] net: ipa: use a bitmap for defined endpoints IPA v5.0 supports more than 32 endpoints, so we will be unable to represent endpoints defined in the configuration data with a 32-bit value. To prepare for that, convert the field in the IPA structure representing defined endpoints to be a Linux bitmap. Convert loops based on that field into for_each_set_bit() calls over the new bitmap. Note that the loop in ipa_endpoint_config() still assumes there are 32 or fewer endpoints (when comparing against the available endpoint bit mask); that assumption goes away in the next patch. Signed-off-by: Alex Elder Signed-off-by: David S. Miller --- drivers/net/ipa/ipa.h | 6 ++--- drivers/net/ipa/ipa_endpoint.c | 46 +++++++++++++--------------------- 2 files changed, 21 insertions(+), 31 deletions(-) diff --git a/drivers/net/ipa/ipa.h b/drivers/net/ipa/ipa.h index a44595575d06..261c7263f9e3 100644 --- a/drivers/net/ipa/ipa.h +++ b/drivers/net/ipa/ipa.h @@ -62,7 +62,7 @@ struct ipa_interrupt; * @zero_virt: Virtual address of preallocated zero-filled memory * @zero_size: Size (bytes) of preallocated zero-filled memory * @endpoint_count: Number of endpoints represented by bit masks below - * @defined: Bit mask indicating endpoints defined in config data + * @defined: Bitmap of endpoints defined in config data * @available: Bit mask indicating endpoints hardware supports * @filter_map: Bit mask indicating endpoints that support filtering * @set_up: Bit mask indicating endpoints set up @@ -117,9 +117,9 @@ struct ipa { void *zero_virt; size_t zero_size; - /* Bit masks indicating endpoint state */ + /* Bitmaps indicating endpoint state */ u32 endpoint_count; - u32 defined; /* Defined in configuration data */ + unsigned long *defined; /* Defined in configuration data */ u32 available; /* Supported by hardware */ u32 filter_map; u32 set_up; diff --git a/drivers/net/ipa/ipa_endpoint.c b/drivers/net/ipa/ipa_endpoint.c index 32559ed498c1..abc939c272b5 100644 --- a/drivers/net/ipa/ipa_endpoint.c +++ b/drivers/net/ipa/ipa_endpoint.c @@ -459,8 +459,8 @@ void ipa_endpoint_modem_pause_all(struct ipa *ipa, bool enable) /* Reset all modem endpoints to use the default exception endpoint */ int ipa_endpoint_modem_exception_reset_all(struct ipa *ipa) { - u32 defined = ipa->defined; struct gsi_trans *trans; + u32 endpoint_id; u32 count; /* We need one command per modem TX endpoint, plus the commands @@ -474,14 +474,11 @@ int ipa_endpoint_modem_exception_reset_all(struct ipa *ipa) return -EBUSY; } - while (defined) { - u32 endpoint_id = __ffs(defined); + for_each_set_bit(endpoint_id, ipa->defined, ipa->endpoint_count) { struct ipa_endpoint *endpoint; const struct ipa_reg *reg; u32 offset; - defined ^= BIT(endpoint_id); - /* We only reset modem TX endpoints */ endpoint = &ipa->endpoint[endpoint_id]; if (!(endpoint->ee_id == GSI_EE_MODEM && endpoint->toward_ipa)) @@ -1823,16 +1820,11 @@ static void ipa_endpoint_teardown_one(struct ipa_endpoint *endpoint) void ipa_endpoint_setup(struct ipa *ipa) { - u32 defined = ipa->defined; + u32 endpoint_id; ipa->set_up = 0; - while (defined) { - u32 endpoint_id = __ffs(defined); - - defined ^= BIT(endpoint_id); - + for_each_set_bit(endpoint_id, ipa->defined, ipa->endpoint_count) ipa_endpoint_setup_one(&ipa->endpoint[endpoint_id]); - } } void ipa_endpoint_teardown(struct ipa *ipa) @@ -1853,10 +1845,10 @@ int ipa_endpoint_config(struct ipa *ipa) { struct device *dev = &ipa->pdev->dev; const struct ipa_reg *reg; + u32 endpoint_id; u32 tx_count; u32 rx_count; u32 rx_base; - u32 defined; u32 limit; u32 val; @@ -1896,13 +1888,9 @@ int ipa_endpoint_config(struct ipa *ipa) /* Mark all supported RX and TX endpoints as available */ ipa->available = GENMASK(limit - 1, rx_base) | GENMASK(tx_count - 1, 0); - defined = ipa->defined; - while (defined) { - u32 endpoint_id = __ffs(defined); + for_each_set_bit(endpoint_id, ipa->defined, ipa->endpoint_count) { struct ipa_endpoint *endpoint; - defined ^= BIT(endpoint_id); - if (endpoint_id >= limit) { dev_err(dev, "invalid endpoint id, %u > %u\n", endpoint_id, limit - 1); @@ -1954,27 +1942,26 @@ static void ipa_endpoint_init_one(struct ipa *ipa, enum ipa_endpoint_name name, endpoint->toward_ipa = data->toward_ipa; endpoint->config = data->endpoint.config; - ipa->defined |= BIT(endpoint->endpoint_id); + __set_bit(endpoint->endpoint_id, ipa->defined); } static void ipa_endpoint_exit_one(struct ipa_endpoint *endpoint) { - endpoint->ipa->defined &= ~BIT(endpoint->endpoint_id); + __clear_bit(endpoint->endpoint_id, endpoint->ipa->defined); memset(endpoint, 0, sizeof(*endpoint)); } void ipa_endpoint_exit(struct ipa *ipa) { - u32 defined = ipa->defined; - - while (defined) { - u32 endpoint_id = __fls(defined); - - defined ^= BIT(endpoint_id); + u32 endpoint_id; + for_each_set_bit(endpoint_id, ipa->defined, ipa->endpoint_count) ipa_endpoint_exit_one(&ipa->endpoint[endpoint_id]); - } + + bitmap_free(ipa->defined); + ipa->defined = NULL; + memset(ipa->name_map, 0, sizeof(ipa->name_map)); memset(ipa->channel_map, 0, sizeof(ipa->channel_map)); } @@ -1993,7 +1980,10 @@ u32 ipa_endpoint_init(struct ipa *ipa, u32 count, if (!ipa->endpoint_count) return 0; /* Error */ - ipa->defined = 0; + /* Initialize the defined endpoint bitmap */ + ipa->defined = bitmap_zalloc(ipa->endpoint_count, GFP_KERNEL); + if (!ipa->defined) + return 0; /* Error */ filter_map = 0; for (name = 0; name < count; name++, data++) { From 88de7672404de72e273c4f1f4228120b8d7f01f1 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Wed, 2 Nov 2022 17:11:36 -0500 Subject: [PATCH 0616/2002] net: ipa: use a bitmap for available endpoints Similar to the previous patch, replace the 32-bit unsigned used to track endpoints supported by hardware with a Linux bitmap, to allow an arbitrary number of endpoints to be represented. Move ipa_endpoint_deconfig() above ipa_endpoint_config() and use it in the error path of the latter function. Signed-off-by: Alex Elder Signed-off-by: David S. Miller --- drivers/net/ipa/ipa.h | 8 ++++-- drivers/net/ipa/ipa_endpoint.c | 49 ++++++++++++++++++++++----------- drivers/net/ipa/ipa_interrupt.c | 8 ++++-- 3 files changed, 43 insertions(+), 22 deletions(-) diff --git a/drivers/net/ipa/ipa.h b/drivers/net/ipa/ipa.h index 261c7263f9e3..c603575e2a58 100644 --- a/drivers/net/ipa/ipa.h +++ b/drivers/net/ipa/ipa.h @@ -61,9 +61,10 @@ struct ipa_interrupt; * @zero_addr: DMA address of preallocated zero-filled memory * @zero_virt: Virtual address of preallocated zero-filled memory * @zero_size: Size (bytes) of preallocated zero-filled memory - * @endpoint_count: Number of endpoints represented by bit masks below + * @endpoint_count: Number of defined bits in most bitmaps below + * @available_count: Number of defined bits in the available bitmap * @defined: Bitmap of endpoints defined in config data - * @available: Bit mask indicating endpoints hardware supports + * @available: Bitmap of endpoints supported by hardware * @filter_map: Bit mask indicating endpoints that support filtering * @set_up: Bit mask indicating endpoints set up * @enabled: Bit mask indicating endpoints enabled @@ -119,8 +120,9 @@ struct ipa { /* Bitmaps indicating endpoint state */ u32 endpoint_count; + u32 available_count; unsigned long *defined; /* Defined in configuration data */ - u32 available; /* Supported by hardware */ + unsigned long *available; /* Supported by hardware */ u32 filter_map; u32 set_up; u32 enabled; diff --git a/drivers/net/ipa/ipa_endpoint.c b/drivers/net/ipa/ipa_endpoint.c index abc939c272b5..a7932e8d0b2b 100644 --- a/drivers/net/ipa/ipa_endpoint.c +++ b/drivers/net/ipa/ipa_endpoint.c @@ -351,19 +351,17 @@ ipa_endpoint_program_delay(struct ipa_endpoint *endpoint, bool enable) static bool ipa_endpoint_aggr_active(struct ipa_endpoint *endpoint) { u32 endpoint_id = endpoint->endpoint_id; - u32 mask = BIT(endpoint_id % 32); struct ipa *ipa = endpoint->ipa; u32 unit = endpoint_id / 32; const struct ipa_reg *reg; u32 val; - /* This works until we actually have more than 32 endpoints */ - WARN_ON(!(mask & ipa->available)); + WARN_ON(!test_bit(endpoint_id, ipa->available)); reg = ipa_reg(ipa, STATE_AGGR_ACTIVE); val = ioread32(ipa->reg_virt + ipa_reg_n_offset(reg, unit)); - return !!(val & mask); + return !!(val & BIT(endpoint_id % 32)); } static void ipa_endpoint_force_close(struct ipa_endpoint *endpoint) @@ -374,8 +372,7 @@ static void ipa_endpoint_force_close(struct ipa_endpoint *endpoint) u32 unit = endpoint_id / 32; const struct ipa_reg *reg; - /* This works until we actually have more than 32 endpoints */ - WARN_ON(!(mask & ipa->available)); + WARN_ON(!test_bit(endpoint_id, ipa->available)); reg = ipa_reg(ipa, AGGR_FORCE_CLOSE); iowrite32(mask, ipa->reg_virt + ipa_reg_n_offset(reg, unit)); @@ -1841,6 +1838,13 @@ void ipa_endpoint_teardown(struct ipa *ipa) ipa->set_up = 0; } +void ipa_endpoint_deconfig(struct ipa *ipa) +{ + ipa->available_count = 0; + bitmap_free(ipa->available); + ipa->available = NULL; +} + int ipa_endpoint_config(struct ipa *ipa) { struct device *dev = &ipa->pdev->dev; @@ -1863,7 +1867,13 @@ int ipa_endpoint_config(struct ipa *ipa) * assume the configuration is valid. */ if (ipa->version < IPA_VERSION_3_5) { - ipa->available = ~0; + ipa->available = bitmap_zalloc(IPA_ENDPOINT_MAX, GFP_KERNEL); + if (!ipa->available) + return -ENOMEM; + ipa->available_count = IPA_ENDPOINT_MAX; + + bitmap_set(ipa->available, 0, IPA_ENDPOINT_MAX); + return 0; } @@ -1885,8 +1895,15 @@ int ipa_endpoint_config(struct ipa *ipa) return -EINVAL; } + /* Allocate and initialize the available endpoint bitmap */ + ipa->available = bitmap_zalloc(limit, GFP_KERNEL); + if (!ipa->available) + return -ENOMEM; + ipa->available_count = limit; + /* Mark all supported RX and TX endpoints as available */ - ipa->available = GENMASK(limit - 1, rx_base) | GENMASK(tx_count - 1, 0); + bitmap_set(ipa->available, 0, tx_count); + bitmap_set(ipa->available, rx_base, rx_count); for_each_set_bit(endpoint_id, ipa->defined, ipa->endpoint_count) { struct ipa_endpoint *endpoint; @@ -1894,13 +1911,13 @@ int ipa_endpoint_config(struct ipa *ipa) if (endpoint_id >= limit) { dev_err(dev, "invalid endpoint id, %u > %u\n", endpoint_id, limit - 1); - return -EINVAL; + goto err_free_bitmap; } - if (!(BIT(endpoint_id) & ipa->available)) { + if (!test_bit(endpoint_id, ipa->available)) { dev_err(dev, "unavailable endpoint id %u\n", endpoint_id); - return -EINVAL; + goto err_free_bitmap; } /* Make sure it's pointing in the right direction */ @@ -1913,15 +1930,15 @@ int ipa_endpoint_config(struct ipa *ipa) } dev_err(dev, "endpoint id %u wrong direction\n", endpoint_id); - return -EINVAL; + goto err_free_bitmap; } return 0; -} -void ipa_endpoint_deconfig(struct ipa *ipa) -{ - ipa->available = 0; /* Nothing more to do */ +err_free_bitmap: + ipa_endpoint_deconfig(ipa); + + return -EINVAL; } static void ipa_endpoint_init_one(struct ipa *ipa, enum ipa_endpoint_name name, diff --git a/drivers/net/ipa/ipa_interrupt.c b/drivers/net/ipa/ipa_interrupt.c index a62bc667bda0..a49f66efacb8 100644 --- a/drivers/net/ipa/ipa_interrupt.c +++ b/drivers/net/ipa/ipa_interrupt.c @@ -132,14 +132,13 @@ static void ipa_interrupt_suspend_control(struct ipa_interrupt *interrupt, u32 endpoint_id, bool enable) { struct ipa *ipa = interrupt->ipa; - u32 mask = BIT(endpoint_id % 32); u32 unit = endpoint_id / 32; const struct ipa_reg *reg; u32 offset; + u32 mask; u32 val; - /* This works until we actually have more than 32 endpoints */ - WARN_ON(!(mask & ipa->available)); + WARN_ON(!test_bit(endpoint_id, ipa->available)); /* IPA version 3.0 does not support TX_SUSPEND interrupt control */ if (ipa->version == IPA_VERSION_3_0) @@ -148,10 +147,13 @@ static void ipa_interrupt_suspend_control(struct ipa_interrupt *interrupt, reg = ipa_reg(ipa, IRQ_SUSPEND_EN); offset = ipa_reg_n_offset(reg, unit); val = ioread32(ipa->reg_virt + offset); + + mask = BIT(endpoint_id); if (enable) val |= mask; else val &= ~mask; + iowrite32(val, ipa->reg_virt + offset); } From 0f97fbd478587a4ac2d53a88e5a9aefd6632a251 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Wed, 2 Nov 2022 17:11:37 -0500 Subject: [PATCH 0617/2002] net: ipa: support more filtering endpoints Prior to IPA v5.0, there could be no more than 32 endpoints. A filter table begins with a bitmap indicating which endpoints have a filter defined. That bitmap is currently assumed to fit in a 32-bit value. Starting with IPA v5.0, more than 32 endpoints are supported, so it's conceivable that a TX endpoint has an ID that exceeds 32. Increase the size of the field representing endpoints that support filtering to 64 bits. Rename the bitmap field "filtered". Unlike other similar fields, we do not use an (arbitrarily long) Linux bitmap for this purpose. The reason is that if a filter table ever *did* need to support more than 64 TX endpoints, its format would change in ways we can't anticipate. Have ipa_endpoint_init() return a negative errno rather than a mask that indicates which endpoints support filtering, and have that function assign the "filtered" field directly. Signed-off-by: Alex Elder Signed-off-by: David S. Miller --- drivers/net/ipa/ipa.h | 4 ++-- drivers/net/ipa/ipa_endpoint.c | 22 +++++++++++++--------- drivers/net/ipa/ipa_endpoint.h | 2 +- drivers/net/ipa/ipa_main.c | 7 ++----- drivers/net/ipa/ipa_table.c | 23 +++++++++++------------ drivers/net/ipa/ipa_table.h | 6 +++--- 6 files changed, 32 insertions(+), 32 deletions(-) diff --git a/drivers/net/ipa/ipa.h b/drivers/net/ipa/ipa.h index c603575e2a58..557101c2d583 100644 --- a/drivers/net/ipa/ipa.h +++ b/drivers/net/ipa/ipa.h @@ -65,7 +65,7 @@ struct ipa_interrupt; * @available_count: Number of defined bits in the available bitmap * @defined: Bitmap of endpoints defined in config data * @available: Bitmap of endpoints supported by hardware - * @filter_map: Bit mask indicating endpoints that support filtering + * @filtered: Bitmap of endpoints that support filtering * @set_up: Bit mask indicating endpoints set up * @enabled: Bit mask indicating endpoints enabled * @modem_tx_count: Number of defined modem TX endoints @@ -123,7 +123,7 @@ struct ipa { u32 available_count; unsigned long *defined; /* Defined in configuration data */ unsigned long *available; /* Supported by hardware */ - u32 filter_map; + u64 filtered; /* Support filtering (AP and modem) */ u32 set_up; u32 enabled; diff --git a/drivers/net/ipa/ipa_endpoint.c b/drivers/net/ipa/ipa_endpoint.c index a7932e8d0b2b..03811871dc4a 100644 --- a/drivers/net/ipa/ipa_endpoint.c +++ b/drivers/net/ipa/ipa_endpoint.c @@ -1973,6 +1973,8 @@ void ipa_endpoint_exit(struct ipa *ipa) { u32 endpoint_id; + ipa->filtered = 0; + for_each_set_bit(endpoint_id, ipa->defined, ipa->endpoint_count) ipa_endpoint_exit_one(&ipa->endpoint[endpoint_id]); @@ -1984,25 +1986,25 @@ void ipa_endpoint_exit(struct ipa *ipa) } /* Returns a bitmask of endpoints that support filtering, or 0 on error */ -u32 ipa_endpoint_init(struct ipa *ipa, u32 count, +int ipa_endpoint_init(struct ipa *ipa, u32 count, const struct ipa_gsi_endpoint_data *data) { enum ipa_endpoint_name name; - u32 filter_map; + u32 filtered; BUILD_BUG_ON(!IPA_REPLENISH_BATCH); /* Number of endpoints is one more than the maximum ID */ ipa->endpoint_count = ipa_endpoint_max(ipa, count, data) + 1; if (!ipa->endpoint_count) - return 0; /* Error */ + return -EINVAL; /* Initialize the defined endpoint bitmap */ ipa->defined = bitmap_zalloc(ipa->endpoint_count, GFP_KERNEL); if (!ipa->defined) - return 0; /* Error */ + return -ENOMEM; - filter_map = 0; + filtered = 0; for (name = 0; name < count; name++, data++) { if (ipa_gsi_endpoint_data_empty(data)) continue; /* Skip over empty slots */ @@ -2010,18 +2012,20 @@ u32 ipa_endpoint_init(struct ipa *ipa, u32 count, ipa_endpoint_init_one(ipa, name, data); if (data->endpoint.filter_support) - filter_map |= BIT(data->endpoint_id); + filtered |= BIT(data->endpoint_id); if (data->ee_id == GSI_EE_MODEM && data->toward_ipa) ipa->modem_tx_count++; } - if (!ipa_filter_map_valid(ipa, filter_map)) + if (!ipa_filtered_valid(ipa, filtered)) goto err_endpoint_exit; - return filter_map; /* Non-zero bitmask */ + ipa->filtered = filtered; + + return 0; err_endpoint_exit: ipa_endpoint_exit(ipa); - return 0; /* Error */ + return -EINVAL; } diff --git a/drivers/net/ipa/ipa_endpoint.h b/drivers/net/ipa/ipa_endpoint.h index d8dfa24f5214..4a5c3bc549df 100644 --- a/drivers/net/ipa/ipa_endpoint.h +++ b/drivers/net/ipa/ipa_endpoint.h @@ -195,7 +195,7 @@ void ipa_endpoint_deconfig(struct ipa *ipa); void ipa_endpoint_default_route_set(struct ipa *ipa, u32 endpoint_id); void ipa_endpoint_default_route_clear(struct ipa *ipa); -u32 ipa_endpoint_init(struct ipa *ipa, u32 count, +int ipa_endpoint_init(struct ipa *ipa, u32 count, const struct ipa_gsi_endpoint_data *data); void ipa_endpoint_exit(struct ipa *ipa); diff --git a/drivers/net/ipa/ipa_main.c b/drivers/net/ipa/ipa_main.c index 8f6a6890697e..ebb6c9b311eb 100644 --- a/drivers/net/ipa/ipa_main.c +++ b/drivers/net/ipa/ipa_main.c @@ -788,12 +788,9 @@ static int ipa_probe(struct platform_device *pdev) goto err_mem_exit; /* Result is a non-zero mask of endpoints that support filtering */ - ipa->filter_map = ipa_endpoint_init(ipa, data->endpoint_count, - data->endpoint_data); - if (!ipa->filter_map) { - ret = -EINVAL; + ret = ipa_endpoint_init(ipa, data->endpoint_count, data->endpoint_data); + if (ret) goto err_gsi_exit; - } ret = ipa_table_init(ipa); if (ret) diff --git a/drivers/net/ipa/ipa_table.c b/drivers/net/ipa/ipa_table.c index 3a14465bf8a6..cc9349a1d4df 100644 --- a/drivers/net/ipa/ipa_table.c +++ b/drivers/net/ipa/ipa_table.c @@ -161,20 +161,20 @@ ipa_table_mem(struct ipa *ipa, bool filter, bool hashed, bool ipv6) return ipa_mem_find(ipa, mem_id); } -bool ipa_filter_map_valid(struct ipa *ipa, u32 filter_map) +bool ipa_filtered_valid(struct ipa *ipa, u64 filtered) { struct device *dev = &ipa->pdev->dev; u32 count; - if (!filter_map) { + if (!filtered) { dev_err(dev, "at least one filtering endpoint is required\n"); return false; } - count = hweight32(filter_map); + count = hweight64(filtered); if (count > ipa->filter_count) { - dev_err(dev, "too many filtering endpoints (%u, max %u)\n", + dev_err(dev, "too many filtering endpoints (%u > %u)\n", count, ipa->filter_count); return false; @@ -230,12 +230,11 @@ static void ipa_table_reset_add(struct gsi_trans *trans, bool filter, static int ipa_filter_reset_table(struct ipa *ipa, bool hashed, bool ipv6, bool modem) { - u32 ep_mask = ipa->filter_map; - u32 count = hweight32(ep_mask); + u64 ep_mask = ipa->filtered; struct gsi_trans *trans; enum gsi_ee_id ee_id; - trans = ipa_cmd_trans_alloc(ipa, count); + trans = ipa_cmd_trans_alloc(ipa, hweight64(ep_mask)); if (!trans) { dev_err(&ipa->pdev->dev, "no transaction for %s filter reset\n", @@ -405,7 +404,7 @@ static void ipa_table_init_add(struct gsi_trans *trans, bool filter, bool ipv6) * to hold the bitmap itself. The size of the hashed filter * table is either the same as the non-hashed one, or zero. */ - count = 1 + hweight32(ipa->filter_map); + count = 1 + hweight64(ipa->filtered); hash_count = hash_mem && hash_mem->size ? count : 0; } else { /* The size of a route table region determines the number @@ -503,7 +502,7 @@ static void ipa_filter_tuple_zero(struct ipa_endpoint *endpoint) static void ipa_filter_config(struct ipa *ipa, bool modem) { enum gsi_ee_id ee_id = modem ? GSI_EE_MODEM : GSI_EE_AP; - u32 ep_mask = ipa->filter_map; + u64 ep_mask = ipa->filtered; if (!ipa_table_hash_support(ipa)) return; @@ -615,7 +614,7 @@ bool ipa_table_mem_valid(struct ipa *ipa, bool filter) /* Filter tables must able to hold the endpoint bitmap plus * an entry for each endpoint that supports filtering */ - if (count < 1 + hweight32(ipa->filter_map)) + if (count < 1 + hweight64(ipa->filtered)) return false; } else { /* Routing tables must be able to hold all modem entries, @@ -720,9 +719,9 @@ int ipa_table_init(struct ipa *ipa) * that option, so there's no shifting required. */ if (ipa->version < IPA_VERSION_5_0) - *virt++ = cpu_to_le64((u64)ipa->filter_map << 1); + *virt++ = cpu_to_le64(ipa->filtered << 1); else - *virt++ = cpu_to_le64((u64)ipa->filter_map); + *virt++ = cpu_to_le64(ipa->filtered); /* All the rest contain the DMA address of the zero rule */ le_addr = cpu_to_le64(addr); diff --git a/drivers/net/ipa/ipa_table.h b/drivers/net/ipa/ipa_table.h index 8a4dcd7df4c0..7cc951904bb4 100644 --- a/drivers/net/ipa/ipa_table.h +++ b/drivers/net/ipa/ipa_table.h @@ -11,13 +11,13 @@ struct ipa; /** - * ipa_filter_map_valid() - Validate a filter table endpoint bitmap + * ipa_filtered_valid() - Validate a filter table endpoint bitmap * @ipa: IPA pointer - * @filter_mask: Filter table endpoint bitmap to check + * @filtered: Filter table endpoint bitmap to check * * Return: true if all regions are valid, false otherwise */ -bool ipa_filter_map_valid(struct ipa *ipa, u32 filter_mask); +bool ipa_filtered_valid(struct ipa *ipa, u64 filtered); /** * ipa_table_hash_support() - Return true if hashed tables are supported From ae5108e9b7fa255e67323b70e37264a5ec7f1a62 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Wed, 2 Nov 2022 17:11:38 -0500 Subject: [PATCH 0618/2002] net: ipa: use a bitmap for set-up endpoints Replace the 32-bit unsigned used to track endpoints that have completed setup with a Linux bitmap, to allow an arbitrary number of endpoints to be represented. Rework the error handling in ipa_endpoint_init() so the defined endpoint bitmap is freed if an error occurs early. Once endpoints have been initialized, ipa_endpoint_exit() is used to recover if the set of filtered endpoints is invalid. Signed-off-by: Alex Elder Signed-off-by: David S. Miller --- drivers/net/ipa/ipa.h | 4 ++-- drivers/net/ipa/ipa_endpoint.c | 38 +++++++++++++++++++--------------- 2 files changed, 23 insertions(+), 19 deletions(-) diff --git a/drivers/net/ipa/ipa.h b/drivers/net/ipa/ipa.h index 557101c2d583..f14d1bd34e7e 100644 --- a/drivers/net/ipa/ipa.h +++ b/drivers/net/ipa/ipa.h @@ -66,7 +66,7 @@ struct ipa_interrupt; * @defined: Bitmap of endpoints defined in config data * @available: Bitmap of endpoints supported by hardware * @filtered: Bitmap of endpoints that support filtering - * @set_up: Bit mask indicating endpoints set up + * @set_up: Bitmap of endpoints that are set up for use * @enabled: Bit mask indicating endpoints enabled * @modem_tx_count: Number of defined modem TX endoints * @endpoint: Array of endpoint information @@ -124,7 +124,7 @@ struct ipa { unsigned long *defined; /* Defined in configuration data */ unsigned long *available; /* Supported by hardware */ u64 filtered; /* Support filtering (AP and modem) */ - u32 set_up; + unsigned long *set_up; u32 enabled; u32 modem_tx_count; diff --git a/drivers/net/ipa/ipa_endpoint.c b/drivers/net/ipa/ipa_endpoint.c index 03811871dc4a..3fe20b4d9c90 100644 --- a/drivers/net/ipa/ipa_endpoint.c +++ b/drivers/net/ipa/ipa_endpoint.c @@ -1802,12 +1802,12 @@ static void ipa_endpoint_setup_one(struct ipa_endpoint *endpoint) ipa_endpoint_program(endpoint); - endpoint->ipa->set_up |= BIT(endpoint->endpoint_id); + __set_bit(endpoint->endpoint_id, endpoint->ipa->set_up); } static void ipa_endpoint_teardown_one(struct ipa_endpoint *endpoint) { - endpoint->ipa->set_up &= ~BIT(endpoint->endpoint_id); + __clear_bit(endpoint->endpoint_id, endpoint->ipa->set_up); if (!endpoint->toward_ipa) cancel_delayed_work_sync(&endpoint->replenish_work); @@ -1819,23 +1819,16 @@ void ipa_endpoint_setup(struct ipa *ipa) { u32 endpoint_id; - ipa->set_up = 0; for_each_set_bit(endpoint_id, ipa->defined, ipa->endpoint_count) ipa_endpoint_setup_one(&ipa->endpoint[endpoint_id]); } void ipa_endpoint_teardown(struct ipa *ipa) { - u32 set_up = ipa->set_up; - - while (set_up) { - u32 endpoint_id = __fls(set_up); - - set_up ^= BIT(endpoint_id); + u32 endpoint_id; + for_each_set_bit(endpoint_id, ipa->set_up, ipa->endpoint_count) ipa_endpoint_teardown_one(&ipa->endpoint[endpoint_id]); - } - ipa->set_up = 0; } void ipa_endpoint_deconfig(struct ipa *ipa) @@ -1978,6 +1971,8 @@ void ipa_endpoint_exit(struct ipa *ipa) for_each_set_bit(endpoint_id, ipa->defined, ipa->endpoint_count) ipa_endpoint_exit_one(&ipa->endpoint[endpoint_id]); + bitmap_free(ipa->set_up); + ipa->set_up = NULL; bitmap_free(ipa->defined); ipa->defined = NULL; @@ -1999,11 +1994,15 @@ int ipa_endpoint_init(struct ipa *ipa, u32 count, if (!ipa->endpoint_count) return -EINVAL; - /* Initialize the defined endpoint bitmap */ + /* Initialize endpoint state bitmaps */ ipa->defined = bitmap_zalloc(ipa->endpoint_count, GFP_KERNEL); if (!ipa->defined) return -ENOMEM; + ipa->set_up = bitmap_zalloc(ipa->endpoint_count, GFP_KERNEL); + if (!ipa->set_up) + goto err_free_defined; + filtered = 0; for (name = 0; name < count; name++, data++) { if (ipa_gsi_endpoint_data_empty(data)) @@ -2017,15 +2016,20 @@ int ipa_endpoint_init(struct ipa *ipa, u32 count, ipa->modem_tx_count++; } - if (!ipa_filtered_valid(ipa, filtered)) - goto err_endpoint_exit; + /* Make sure the set of filtered endpoints is valid */ + if (!ipa_filtered_valid(ipa, filtered)) { + ipa_endpoint_exit(ipa); + + return -EINVAL; + } ipa->filtered = filtered; return 0; -err_endpoint_exit: - ipa_endpoint_exit(ipa); +err_free_defined: + bitmap_free(ipa->defined); + ipa->defined = NULL; - return -EINVAL; + return -ENOMEM; } From 9b7a00653651d8e6ac05c6d4baa39bcf21090328 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Wed, 2 Nov 2022 17:11:39 -0500 Subject: [PATCH 0619/2002] net: ipa: use a bitmap for enabled endpoints Replace the 32-bit unsigned used to track enabled endpoints with a Linux bitmap, to allow an arbitrary number of endpoints to be represented. Signed-off-by: Alex Elder Signed-off-by: David S. Miller --- drivers/net/ipa/ipa.h | 4 ++-- drivers/net/ipa/ipa_endpoint.c | 32 ++++++++++++++++++++------------ 2 files changed, 22 insertions(+), 14 deletions(-) diff --git a/drivers/net/ipa/ipa.h b/drivers/net/ipa/ipa.h index f14d1bd34e7e..5372db58b5bd 100644 --- a/drivers/net/ipa/ipa.h +++ b/drivers/net/ipa/ipa.h @@ -67,7 +67,7 @@ struct ipa_interrupt; * @available: Bitmap of endpoints supported by hardware * @filtered: Bitmap of endpoints that support filtering * @set_up: Bitmap of endpoints that are set up for use - * @enabled: Bit mask indicating endpoints enabled + * @enabled: Bitmap of currently enabled endpoints * @modem_tx_count: Number of defined modem TX endoints * @endpoint: Array of endpoint information * @channel_map: Mapping of GSI channel to IPA endpoint @@ -125,7 +125,7 @@ struct ipa { unsigned long *available; /* Supported by hardware */ u64 filtered; /* Support filtering (AP and modem) */ unsigned long *set_up; - u32 enabled; + unsigned long *enabled; u32 modem_tx_count; struct ipa_endpoint endpoint[IPA_ENDPOINT_MAX]; diff --git a/drivers/net/ipa/ipa_endpoint.c b/drivers/net/ipa/ipa_endpoint.c index 3fe20b4d9c90..136932464261 100644 --- a/drivers/net/ipa/ipa_endpoint.c +++ b/drivers/net/ipa/ipa_endpoint.c @@ -1666,6 +1666,7 @@ static void ipa_endpoint_program(struct ipa_endpoint *endpoint) int ipa_endpoint_enable_one(struct ipa_endpoint *endpoint) { + u32 endpoint_id = endpoint->endpoint_id; struct ipa *ipa = endpoint->ipa; struct gsi *gsi = &ipa->gsi; int ret; @@ -1675,37 +1676,35 @@ int ipa_endpoint_enable_one(struct ipa_endpoint *endpoint) dev_err(&ipa->pdev->dev, "error %d starting %cX channel %u for endpoint %u\n", ret, endpoint->toward_ipa ? 'T' : 'R', - endpoint->channel_id, endpoint->endpoint_id); + endpoint->channel_id, endpoint_id); return ret; } if (!endpoint->toward_ipa) { - ipa_interrupt_suspend_enable(ipa->interrupt, - endpoint->endpoint_id); + ipa_interrupt_suspend_enable(ipa->interrupt, endpoint_id); ipa_endpoint_replenish_enable(endpoint); } - ipa->enabled |= BIT(endpoint->endpoint_id); + __set_bit(endpoint_id, ipa->enabled); return 0; } void ipa_endpoint_disable_one(struct ipa_endpoint *endpoint) { - u32 mask = BIT(endpoint->endpoint_id); + u32 endpoint_id = endpoint->endpoint_id; struct ipa *ipa = endpoint->ipa; struct gsi *gsi = &ipa->gsi; int ret; - if (!(ipa->enabled & mask)) + if (!test_bit(endpoint_id, ipa->enabled)) return; - ipa->enabled ^= mask; + __clear_bit(endpoint_id, endpoint->ipa->enabled); if (!endpoint->toward_ipa) { ipa_endpoint_replenish_disable(endpoint); - ipa_interrupt_suspend_disable(ipa->interrupt, - endpoint->endpoint_id); + ipa_interrupt_suspend_disable(ipa->interrupt, endpoint_id); } /* Note that if stop fails, the channel's state is not well-defined */ @@ -1713,7 +1712,7 @@ void ipa_endpoint_disable_one(struct ipa_endpoint *endpoint) if (ret) dev_err(&ipa->pdev->dev, "error %d attempting to stop endpoint %u\n", ret, - endpoint->endpoint_id); + endpoint_id); } void ipa_endpoint_suspend_one(struct ipa_endpoint *endpoint) @@ -1722,7 +1721,7 @@ void ipa_endpoint_suspend_one(struct ipa_endpoint *endpoint) struct gsi *gsi = &endpoint->ipa->gsi; int ret; - if (!(endpoint->ipa->enabled & BIT(endpoint->endpoint_id))) + if (!test_bit(endpoint->endpoint_id, endpoint->ipa->enabled)) return; if (!endpoint->toward_ipa) { @@ -1742,7 +1741,7 @@ void ipa_endpoint_resume_one(struct ipa_endpoint *endpoint) struct gsi *gsi = &endpoint->ipa->gsi; int ret; - if (!(endpoint->ipa->enabled & BIT(endpoint->endpoint_id))) + if (!test_bit(endpoint->endpoint_id, endpoint->ipa->enabled)) return; if (!endpoint->toward_ipa) @@ -1971,6 +1970,8 @@ void ipa_endpoint_exit(struct ipa *ipa) for_each_set_bit(endpoint_id, ipa->defined, ipa->endpoint_count) ipa_endpoint_exit_one(&ipa->endpoint[endpoint_id]); + bitmap_free(ipa->enabled); + ipa->enabled = NULL; bitmap_free(ipa->set_up); ipa->set_up = NULL; bitmap_free(ipa->defined); @@ -2003,6 +2004,10 @@ int ipa_endpoint_init(struct ipa *ipa, u32 count, if (!ipa->set_up) goto err_free_defined; + ipa->enabled = bitmap_zalloc(ipa->endpoint_count, GFP_KERNEL); + if (!ipa->enabled) + goto err_free_set_up; + filtered = 0; for (name = 0; name < count; name++, data++) { if (ipa_gsi_endpoint_data_empty(data)) @@ -2027,6 +2032,9 @@ int ipa_endpoint_init(struct ipa *ipa, u32 count, return 0; +err_free_set_up: + bitmap_free(ipa->set_up); + ipa->set_up = NULL; err_free_defined: bitmap_free(ipa->defined); ipa->defined = NULL; From 81d17f6f3331f03c8eafdacea68ab773426c1e3c Mon Sep 17 00:00:00 2001 From: Minsuk Kang Date: Mon, 24 Oct 2022 16:13:29 +0900 Subject: [PATCH 0620/2002] wifi: brcmfmac: Fix potential shift-out-of-bounds in brcmf_fw_alloc_request() This patch fixes a shift-out-of-bounds in brcmfmac that occurs in BIT(chiprev) when a 'chiprev' provided by the device is too large. It should also not be equal to or greater than BITS_PER_TYPE(u32) as we do bitwise AND with a u32 variable and BIT(chiprev). The patch adds a check that makes the function return NULL if that is the case. Note that the NULL case is later handled by the bus-specific caller, brcmf_usb_probe_cb() or brcmf_usb_reset_resume(), for example. Found by a modified version of syzkaller. UBSAN: shift-out-of-bounds in drivers/net/wireless/broadcom/brcm80211/brcmfmac/firmware.c shift exponent 151055786 is too large for 64-bit type 'long unsigned int' CPU: 0 PID: 1885 Comm: kworker/0:2 Tainted: G O 5.14.0+ #132 Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS rel-1.12.1-0-ga5cab58e9a3f-prebuilt.qemu.org 04/01/2014 Workqueue: usb_hub_wq hub_event Call Trace: dump_stack_lvl+0x57/0x7d ubsan_epilogue+0x5/0x40 __ubsan_handle_shift_out_of_bounds.cold+0x53/0xdb ? lock_chain_count+0x20/0x20 brcmf_fw_alloc_request.cold+0x19/0x3ea ? brcmf_fw_get_firmwares+0x250/0x250 ? brcmf_usb_ioctl_resp_wait+0x1a7/0x1f0 brcmf_usb_get_fwname+0x114/0x1a0 ? brcmf_usb_reset_resume+0x120/0x120 ? number+0x6c4/0x9a0 brcmf_c_process_clm_blob+0x168/0x590 ? put_dec+0x90/0x90 ? enable_ptr_key_workfn+0x20/0x20 ? brcmf_common_pd_remove+0x50/0x50 ? rcu_read_lock_sched_held+0xa1/0xd0 brcmf_c_preinit_dcmds+0x673/0xc40 ? brcmf_c_set_joinpref_default+0x100/0x100 ? rcu_read_lock_sched_held+0xa1/0xd0 ? rcu_read_lock_bh_held+0xb0/0xb0 ? lock_acquire+0x19d/0x4e0 ? find_held_lock+0x2d/0x110 ? brcmf_usb_deq+0x1cc/0x260 ? mark_held_locks+0x9f/0xe0 ? lockdep_hardirqs_on_prepare+0x273/0x3e0 ? _raw_spin_unlock_irqrestore+0x47/0x50 ? trace_hardirqs_on+0x1c/0x120 ? brcmf_usb_deq+0x1a7/0x260 ? brcmf_usb_rx_fill_all+0x5a/0xf0 brcmf_attach+0x246/0xd40 ? wiphy_new_nm+0x1476/0x1d50 ? kmemdup+0x30/0x40 brcmf_usb_probe+0x12de/0x1690 ? brcmf_usbdev_qinit.constprop.0+0x470/0x470 usb_probe_interface+0x25f/0x710 really_probe+0x1be/0xa90 __driver_probe_device+0x2ab/0x460 ? usb_match_id.part.0+0x88/0xc0 driver_probe_device+0x49/0x120 __device_attach_driver+0x18a/0x250 ? driver_allows_async_probing+0x120/0x120 bus_for_each_drv+0x123/0x1a0 ? bus_rescan_devices+0x20/0x20 ? lockdep_hardirqs_on_prepare+0x273/0x3e0 ? trace_hardirqs_on+0x1c/0x120 __device_attach+0x207/0x330 ? device_bind_driver+0xb0/0xb0 ? kobject_uevent_env+0x230/0x12c0 bus_probe_device+0x1a2/0x260 device_add+0xa61/0x1ce0 ? __mutex_unlock_slowpath+0xe7/0x660 ? __fw_devlink_link_to_suppliers+0x550/0x550 usb_set_configuration+0x984/0x1770 ? kernfs_create_link+0x175/0x230 usb_generic_driver_probe+0x69/0x90 usb_probe_device+0x9c/0x220 really_probe+0x1be/0xa90 __driver_probe_device+0x2ab/0x460 driver_probe_device+0x49/0x120 __device_attach_driver+0x18a/0x250 ? driver_allows_async_probing+0x120/0x120 bus_for_each_drv+0x123/0x1a0 ? bus_rescan_devices+0x20/0x20 ? lockdep_hardirqs_on_prepare+0x273/0x3e0 ? trace_hardirqs_on+0x1c/0x120 __device_attach+0x207/0x330 ? device_bind_driver+0xb0/0xb0 ? kobject_uevent_env+0x230/0x12c0 bus_probe_device+0x1a2/0x260 device_add+0xa61/0x1ce0 ? __fw_devlink_link_to_suppliers+0x550/0x550 usb_new_device.cold+0x463/0xf66 ? hub_disconnect+0x400/0x400 ? _raw_spin_unlock_irq+0x24/0x30 hub_event+0x10d5/0x3330 ? hub_port_debounce+0x280/0x280 ? __lock_acquire+0x1671/0x5790 ? wq_calc_node_cpumask+0x170/0x2a0 ? lock_release+0x640/0x640 ? rcu_read_lock_sched_held+0xa1/0xd0 ? rcu_read_lock_bh_held+0xb0/0xb0 ? lockdep_hardirqs_on_prepare+0x273/0x3e0 process_one_work+0x873/0x13e0 ? lock_release+0x640/0x640 ? pwq_dec_nr_in_flight+0x320/0x320 ? rwlock_bug.part.0+0x90/0x90 worker_thread+0x8b/0xd10 ? __kthread_parkme+0xd9/0x1d0 ? process_one_work+0x13e0/0x13e0 kthread+0x379/0x450 ? _raw_spin_unlock_irq+0x24/0x30 ? set_kthread_struct+0x100/0x100 ret_from_fork+0x1f/0x30 Reported-by: Dokyung Song Reported-by: Jisoo Jang Reported-by: Minsuk Kang Signed-off-by: Minsuk Kang Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20221024071329.504277-1-linuxlovemin@yonsei.ac.kr --- drivers/net/wireless/broadcom/brcm80211/brcmfmac/firmware.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/firmware.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/firmware.c index f2207793f6e2..09d2f2dc2b46 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/firmware.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/firmware.c @@ -803,6 +803,11 @@ brcmf_fw_alloc_request(u32 chip, u32 chiprev, u32 i, j; char end = '\0'; + if (chiprev >= BITS_PER_TYPE(u32)) { + brcmf_err("Invalid chip revision %u\n", chiprev); + return NULL; + } + for (i = 0; i < table_size; i++) { if (mapping_table[i].chipid == chip && mapping_table[i].revmask & BIT(chiprev)) From 2b6c0e152868c9c5939a5c5094d5b2be61cf48e6 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Fri, 28 Oct 2022 11:23:32 +0200 Subject: [PATCH 0621/2002] bcma: Use the proper gpio include The is including the legacy header to obtain struct gpio_chip. Instead, include where this struct is defined. It turns out that the brcm80211 brcmsmac depends on this to bring in the symbol gpio_is_valid(). The driver looks up the BCMA parent GPIO driver and checks that this succeeds, but then it goes on to use the deprecated GPIO call gpio_is_valid() to check the consistency of the .base member of the BCMA GPIO struct. The whole check can be dropped because the bcma_gpio is initialized in the declarations: struct gpio_chip *bcma_gpio = &cc_drv->gpio; And this can never be NULL. Cc: Jonas Gorski Acked-by: Arend van Spriel Signed-off-by: Linus Walleij Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20221028092332.238728-1-linus.walleij@linaro.org --- drivers/net/wireless/broadcom/brcm80211/brcmsmac/led.c | 3 --- include/linux/bcma/bcma_driver_chipcommon.h | 2 +- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/led.c b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/led.c index c1b9ac692d26..9540a05247c2 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/led.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/led.c @@ -63,9 +63,6 @@ int brcms_led_register(struct brcms_info *wl) int hwnum = -1; enum gpio_lookup_flags lflags = GPIO_ACTIVE_HIGH; - if (!bcma_gpio || !gpio_is_valid(bcma_gpio->base)) - return -ENODEV; - /* find radio enabled LED */ for (i = 0; i < BRCMS_LED_NO; i++) { u8 led = *leds[i]; diff --git a/include/linux/bcma/bcma_driver_chipcommon.h b/include/linux/bcma/bcma_driver_chipcommon.h index 2d94c30ed439..0cb6638b55e5 100644 --- a/include/linux/bcma/bcma_driver_chipcommon.h +++ b/include/linux/bcma/bcma_driver_chipcommon.h @@ -4,7 +4,7 @@ #include #include -#include +#include /** ChipCommon core registers. **/ #define BCMA_CC_ID 0x0000 From 74a473007ce54b1f42267cf13e008abfb1849ad0 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Fri, 28 Oct 2022 11:30:00 +0200 Subject: [PATCH 0622/2002] bcma: Fail probe if GPIO subdriver fails We currently register the BCMA core even if the GPIO portions fail. There is no reason for this: the GPIO should register just fine, if it fails the BCMA driver should fail. We already gracefully handle the case where the GPIO driver is not compiled in. Signed-off-by: Linus Walleij Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20221028093000.239020-1-linus.walleij@linaro.org --- drivers/bcma/main.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/bcma/main.c b/drivers/bcma/main.c index 44392b624b20..0a8469e0b13a 100644 --- a/drivers/bcma/main.c +++ b/drivers/bcma/main.c @@ -344,8 +344,10 @@ static int bcma_register_devices(struct bcma_bus *bus) err = bcma_gpio_init(&bus->drv_cc); if (err == -ENOTSUPP) bcma_debug(bus, "GPIO driver not activated\n"); - else if (err) + else if (err) { bcma_err(bus, "Error registering GPIO driver: %i\n", err); + return err; + } if (bus->hosttype == BCMA_HOSTTYPE_SOC) { err = bcma_chipco_watchdog_register(&bus->drv_cc); From cdeee85409526cf5a4ca6528bd4d148833a940a3 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Mon, 31 Oct 2022 15:56:37 +0000 Subject: [PATCH 0623/2002] wifi: rtlwifi: rtl8192ee: remove static variable stop_report_cnt Variable stop_report_cnt is being set or incremented but is never being used for anything meaningful. The variable and code relating to it's use is redundant and can be removed. Signed-off-by: Colin Ian King Acked-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20221031155637.871164-1-colin.i.king@gmail.com --- drivers/net/wireless/realtek/rtlwifi/rtl8192ee/trx.c | 8 -------- 1 file changed, 8 deletions(-) diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192ee/trx.c b/drivers/net/wireless/realtek/rtlwifi/rtl8192ee/trx.c index 8043d819fb85..a182cdeb58e2 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8192ee/trx.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192ee/trx.c @@ -997,7 +997,6 @@ bool rtl92ee_is_tx_desc_closed(struct ieee80211_hw *hw, u8 hw_queue, u16 index) struct rtl_priv *rtlpriv = rtl_priv(hw); u16 read_point, write_point; bool ret = false; - static u8 stop_report_cnt; struct rtl8192_tx_ring *ring = &rtlpci->tx_ring[hw_queue]; { @@ -1038,13 +1037,6 @@ bool rtl92ee_is_tx_desc_closed(struct ieee80211_hw *hw, u8 hw_queue, u16 index) rtlpriv->psc.rfoff_reason > RF_CHANGE_BY_PS) ret = true; - if (hw_queue < BEACON_QUEUE) { - if (!ret) - stop_report_cnt++; - else - stop_report_cnt = 0; - } - return ret; } From 22ebc2640cc7b4ecf139737506629fb012e65fd5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonathan=20Neusch=C3=A4fer?= Date: Tue, 1 Nov 2022 18:02:51 +0100 Subject: [PATCH 0624/2002] wifi: brcmfmac: Fix a typo "unknow" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It should be "unknown". Signed-off-by: Jonathan Neuschäfer Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20221101170252.1032085-1-j.neuschaefer@gmx.net --- drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c index deb0199fcf8c..244ba48cc304 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c @@ -1884,7 +1884,7 @@ static uint brcmf_sdio_readframes(struct brcmf_sdio *bus, uint maxframes) } rd->len_left = rd->len; - /* read header first for unknow frame length */ + /* read header first for unknown frame length */ sdio_claim_host(bus->sdiodev->func1); if (!rd->len) { ret = brcmf_sdiod_recv_buf(bus->sdiodev, From 683b9728f28895660c66da250cd31654b8fcbc6e Mon Sep 17 00:00:00 2001 From: Jisoo Jang Date: Wed, 2 Nov 2022 03:36:42 +0900 Subject: [PATCH 0625/2002] wifi: brcmfmac: Fix potential NULL pointer dereference in 'brcmf_c_preinit_dcmds()' This patch fixes a NULL pointer dereference bug in brcmfmac that occurs when ptr which is NULL pointer passed as an argument of strlcpy() in brcmf_c_preinit_dcmds(). This happens when the driver passes a firmware version string that does not contain a space " ", making strrchr() return a null pointer. This patch adds a null pointer check. Found by a modified version of syzkaller. KASAN: null-ptr-deref in range [0x0000000000000000-0x0000000000000007] CPU: 0 PID: 1983 Comm: kworker/0:2 Not tainted 5.14.0+ #79 Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS rel-1.12.1-0-ga5cab58e9a3f-prebuilt.qemu.org 04/01/2014 Workqueue: usb_hub_wq hub_event RIP: 0010:strlen+0x1a/0x90 Code: 23 ff ff ff 66 66 2e 0f 1f 84 00 00 00 00 00 90 48 b8 00 00 00 00 00 fc ff df 48 89 fa 55 48 89 fd 48 c1 ea 03 53 48 83 ec 08 <0f> b6 04 02 48 89 fa 83 e2 07 38 d0 7f 04 84 c0 75 48 80 7d 00 00 RSP: 0018:ffffc90002bfedd8 EFLAGS: 00010296 RAX: dffffc0000000000 RBX: 1ffff9200057fdc1 RCX: 0000000000000000 RDX: 0000000000000000 RSI: 0000000000000020 RDI: 0000000000000001 RBP: 0000000000000001 R08: 0000000000000039 R09: ffffed1023549801 R10: ffff88811aa4c007 R11: ffffed1023549800 R12: ffff88800bc68d6c R13: ffffc90002bfef08 R14: ffff88800bc6bc7c R15: 0000000000000001 FS: 0000000000000000(0000) GS:ffff88811aa00000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 0000000020546180 CR3: 0000000117ff1000 CR4: 0000000000750ef0 PKRU: 55555554 Call Trace: brcmf_c_preinit_dcmds+0x9f2/0xc40 ? brcmf_c_set_joinpref_default+0x100/0x100 ? rcu_read_lock_sched_held+0xa1/0xd0 ? rcu_read_lock_bh_held+0xb0/0xb0 ? lock_acquire+0x19d/0x4e0 ? find_held_lock+0x2d/0x110 ? brcmf_usb_deq+0x1a7/0x260 ? brcmf_usb_rx_fill_all+0x5a/0xf0 brcmf_attach+0x246/0xd40 ? wiphy_new_nm+0x1703/0x1dd0 ? kmemdup+0x43/0x50 brcmf_usb_probe+0x12de/0x1690 ? brcmf_usbdev_qinit.constprop.0+0x470/0x470 usb_probe_interface+0x2aa/0x760 ? usb_probe_device+0x250/0x250 really_probe+0x205/0xb70 ? driver_allows_async_probing+0x130/0x130 __driver_probe_device+0x311/0x4b0 ? driver_allows_async_probing+0x130/0x130 driver_probe_device+0x4e/0x150 __device_attach_driver+0x1cc/0x2a0 bus_for_each_drv+0x156/0x1d0 ? bus_rescan_devices+0x30/0x30 ? lockdep_hardirqs_on_prepare+0x273/0x3e0 ? trace_hardirqs_on+0x46/0x160 __device_attach+0x23f/0x3a0 ? device_bind_driver+0xd0/0xd0 ? kobject_uevent_env+0x287/0x14b0 bus_probe_device+0x1da/0x290 device_add+0xb7b/0x1eb0 ? wait_for_completion+0x290/0x290 ? __fw_devlink_link_to_suppliers+0x5a0/0x5a0 usb_set_configuration+0xf59/0x16f0 usb_generic_driver_probe+0x82/0xa0 usb_probe_device+0xbb/0x250 ? usb_suspend+0x590/0x590 really_probe+0x205/0xb70 ? driver_allows_async_probing+0x130/0x130 __driver_probe_device+0x311/0x4b0 ? usb_generic_driver_match+0x75/0x90 ? driver_allows_async_probing+0x130/0x130 driver_probe_device+0x4e/0x150 __device_attach_driver+0x1cc/0x2a0 bus_for_each_drv+0x156/0x1d0 ? bus_rescan_devices+0x30/0x30 ? _raw_spin_unlock_irqrestore+0x47/0x50 __device_attach+0x23f/0x3a0 ? device_bind_driver+0xd0/0xd0 ? kobject_uevent_env+0x287/0x14b0 bus_probe_device+0x1da/0x290 device_add+0xb7b/0x1eb0 ? __fw_devlink_link_to_suppliers+0x5a0/0x5a0 ? kfree+0x14a/0x6b0 ? __usb_get_extra_descriptor+0x116/0x160 usb_new_device.cold+0x49c/0x1029 ? hub_disconnect+0x450/0x450 ? rwlock_bug.part.0+0x90/0x90 ? _raw_spin_unlock_irq+0x24/0x30 ? lockdep_hardirqs_on_prepare+0x273/0x3e0 hub_event+0x248b/0x31c9 ? usb_port_suspend.cold+0x139/0x139 ? check_irq_usage+0x861/0xf20 ? drain_workqueue+0x280/0x360 ? lock_release+0x640/0x640 ? rcu_read_lock_sched_held+0xa1/0xd0 ? rcu_read_lock_bh_held+0xb0/0xb0 ? lockdep_hardirqs_on_prepare+0x273/0x3e0 process_one_work+0x92b/0x1460 ? pwq_dec_nr_in_flight+0x330/0x330 ? rwlock_bug.part.0+0x90/0x90 worker_thread+0x95/0xe00 ? __kthread_parkme+0x115/0x1e0 ? process_one_work+0x1460/0x1460 kthread+0x3a1/0x480 ? set_kthread_struct+0x120/0x120 ret_from_fork+0x1f/0x30 Modulesdd linked in: ---[ end trace c112c68924ddd800 ]--- RIP: 0010:strlen+0x1a/0x90 Code: 23 ff ff ff 66 66 2e 0f 1f 84 00 00 00 00 00 90 48 b8 00 00 00 00 00 fc ff df 48 89 fa 55 48 89 fd 48 c1 ea 03 53 48 83 ec 08 <0f> b6 04 02 48 89 fa 83 e2 07 38 d0 7f 04 84 c0 75 48 80 7d 00 00 RSP: 0018:ffffc90002bfedd8 EFLAGS: 00010296 RAX: dffffc0000000000 RBX: 1ffff9200057fdc1 RCX: 0000000000000000 RDX: 0000000000000000 RSI: 0000000000000020 RDI: 0000000000000001 RBP: 0000000000000001 R08: 0000000000000039 R09: ffffed1023549801 R10: ffff88811aa4c007 R11: ffffed1023549800 R12: ffff88800bc68d6c R13: ffffc90002bfef08 R14: ffff88800bc6bc7c R15: 0000000000000001 FS: 0000000000000000(0000) GS:ffff88811aa00000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 0000000020546180 CR3: 0000000117ff1000 CR4: 0000000000750ef0 PKRU: 55555554 Kernel panic - not syncing: Fatal exception Kernel Offset: disabled Reported-by: Dokyung Song Reported-by: Jisoo Jang Reported-by: Minsuk Kang Signed-off-by: Jisoo Jang Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20221101183642.166450-1-jisoo.jang@yonsei.ac.kr --- drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c index 74020fa10065..22344e68fd59 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c @@ -305,8 +305,12 @@ int brcmf_c_preinit_dcmds(struct brcmf_if *ifp) brcmf_info("Firmware: %s %s\n", ri->chipname, buf); /* locate firmware version number for ethtool */ - ptr = strrchr(buf, ' ') + 1; - strscpy(ifp->drvr->fwver, ptr, sizeof(ifp->drvr->fwver)); + ptr = strrchr(buf, ' '); + if (!ptr) { + bphy_err(drvr, "Retrieving version number failed"); + goto done; + } + strscpy(ifp->drvr->fwver, ptr + 1, sizeof(ifp->drvr->fwver)); /* Query for 'clmver' to get CLM version info from firmware */ memset(buf, 0, sizeof(buf)); From 57b328bc79967a2392af6ac724d3fd36322cd7f2 Mon Sep 17 00:00:00 2001 From: Bitterblue Smith Date: Tue, 1 Nov 2022 22:31:33 +0200 Subject: [PATCH 0626/2002] wifi: rtl8xxxu: Add central frequency offset tracking According to Realtek programmers, "to adjust oscillator to align central frequency of connected AP. Then, it can yield better performance." From commit fb8517f4fade ("rtw88: 8822c: add CFO tracking"). The RTL8192CU and a version of RTL8723AU apparently don't have the ability to adjust the oscillator, so this doesn't apply to them. This also doesn't apply to the wifi + bluetooth combo chips (RTL8723AU and RTL8723BU) because the CFO tracking should only be done when bluetooth is disabled, and determining that looked complicated. That leaves only the RTL8192EU and RTL8188FU chips. I tested this with the latter. Signed-off-by: Bitterblue Smith Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/80aba428-0aff-f4b2-dea5-35d1425982b6@gmail.com --- .../net/wireless/realtek/rtl8xxxu/rtl8xxxu.h | 21 ++- .../realtek/rtl8xxxu/rtl8xxxu_8188f.c | 39 ++++- .../realtek/rtl8xxxu/rtl8xxxu_8192e.c | 4 +- .../realtek/rtl8xxxu/rtl8xxxu_8723a.c | 38 ++++- .../realtek/rtl8xxxu/rtl8xxxu_8723b.c | 4 +- .../wireless/realtek/rtl8xxxu/rtl8xxxu_core.c | 137 ++++++++++++++++-- .../wireless/realtek/rtl8xxxu/rtl8xxxu_regs.h | 1 + 7 files changed, 212 insertions(+), 32 deletions(-) diff --git a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h index ad3f32f4c6e7..eee48817d4d7 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h +++ b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h @@ -570,7 +570,7 @@ struct rtl8723au_phy_stats { u8 cck_rpt_b_ofdm_cfosho_b; u8 reserved_1; u8 noise_power_db_msb; - u8 path_cfotail[RTL8723A_MAX_RF_PATHS]; + s8 path_cfotail[RTL8723A_MAX_RF_PATHS]; u8 pcts_mask[RTL8723A_MAX_RF_PATHS]; s8 stream_rxevm[RTL8723A_MAX_RF_PATHS]; u8 path_rxsnr[RTL8723A_MAX_RF_PATHS]; @@ -1323,6 +1323,19 @@ struct rtl8xxxu_ra_report { u8 desc_rate; }; +#define CFO_TH_XTAL_HIGH 20 /* kHz */ +#define CFO_TH_XTAL_LOW 10 /* kHz */ +#define CFO_TH_ATC 80 /* kHz */ + +struct rtl8xxxu_cfo_tracking { + bool adjust; + bool atc_status; + int cfo_tail[2]; + u8 crystal_cap; + u32 packet_count; + u32 packet_count_pre; +}; + struct rtl8xxxu_priv { struct ieee80211_hw *hw; struct usb_device *udev; @@ -1381,9 +1394,8 @@ struct rtl8xxxu_priv { u32 ep_tx_high_queue:1; u32 ep_tx_normal_queue:1; u32 ep_tx_low_queue:1; - u32 has_xtalk:1; u32 rx_buf_aggregation:1; - u8 xtalk; + u8 default_crystal_cap; unsigned int pipe_interrupt; unsigned int pipe_in; unsigned int pipe_out[TXDESC_QUEUE_MAX]; @@ -1441,6 +1453,7 @@ struct rtl8xxxu_priv { struct sk_buff_head c2hcmd_queue; struct rtl8xxxu_btcoex bt_coex; struct rtl8xxxu_ra_report ra_report; + struct rtl8xxxu_cfo_tracking cfo_tracking; }; struct rtl8xxxu_rx_urb { @@ -1485,6 +1498,7 @@ struct rtl8xxxu_fileops { struct rtl8xxxu_txdesc32 *tx_desc, bool sgi, bool short_preamble, bool ampdu_enable, u32 rts_rate); + void (*set_crystal_cap) (struct rtl8xxxu_priv *priv, u8 crystal_cap); int writeN_block_size; int rx_agg_buf_size; char tx_desc_size; @@ -1594,6 +1608,7 @@ void rtl8xxxu_fill_txdesc_v2(struct ieee80211_hw *hw, struct ieee80211_hdr *hdr, void rtl8723bu_set_ps_tdma(struct rtl8xxxu_priv *priv, u8 arg1, u8 arg2, u8 arg3, u8 arg4, u8 arg5); void rtl8723bu_phy_init_antenna_selection(struct rtl8xxxu_priv *priv); +void rtl8723a_set_crystal_cap(struct rtl8xxxu_priv *priv, u8 crystal_cap); extern struct rtl8xxxu_fileops rtl8188fu_fops; extern struct rtl8xxxu_fileops rtl8192cu_fops; diff --git a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8188f.c b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8188f.c index 0025bb32538d..9f40d261ac71 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8188f.c +++ b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8188f.c @@ -703,7 +703,7 @@ static int rtl8188fu_parse_efuse(struct rtl8xxxu_priv *priv) priv->ofdm_tx_power_diff[0].a = efuse->tx_power_index_A.ht20_ofdm_1s_diff.a; priv->ht20_tx_power_diff[0].a = efuse->tx_power_index_A.ht20_ofdm_1s_diff.b; - priv->xtalk = efuse->xtal_k & 0x3f; + priv->default_crystal_cap = efuse->xtal_k & 0x3f; dev_info(&priv->udev->dev, "Vendor: %.7s\n", efuse->vendor_name); dev_info(&priv->udev->dev, "Product: %.7s\n", efuse->device_name); @@ -737,7 +737,6 @@ static void rtl8188fu_init_phy_bb(struct rtl8xxxu_priv *priv) { u8 val8; u16 val16; - u32 val32; /* Enable BB and RF */ val16 = rtl8xxxu_read16(priv, REG_SYS_FUNC); @@ -759,12 +758,6 @@ static void rtl8188fu_init_phy_bb(struct rtl8xxxu_priv *priv) rtl8xxxu_init_phy_regs(priv, rtl8188fu_phy_init_table); rtl8xxxu_init_phy_regs(priv, rtl8188f_agc_table); - - val32 = rtl8xxxu_read32(priv, REG_AFE_XTAL_CTRL); - val8 = priv->xtalk; - val32 &= ~0x007FF800; - val32 |= ((val8 | (val8 << 6)) << 11); - rtl8xxxu_write32(priv, REG_AFE_XTAL_CTRL, val32); } static int rtl8188fu_init_phy_rf(struct rtl8xxxu_priv *priv) @@ -1636,6 +1629,35 @@ static void rtl8188f_usb_quirks(struct rtl8xxxu_priv *priv) rtl8xxxu_write32(priv, REG_TXDMA_OFFSET_CHK, val32); } +#define XTAL1 GENMASK(22, 17) +#define XTAL0 GENMASK(16, 11) + +static void rtl8188f_set_crystal_cap(struct rtl8xxxu_priv *priv, u8 crystal_cap) +{ + struct rtl8xxxu_cfo_tracking *cfo = &priv->cfo_tracking; + u32 val32; + + if (crystal_cap == cfo->crystal_cap) + return; + + val32 = rtl8xxxu_read32(priv, REG_AFE_XTAL_CTRL); + + dev_dbg(&priv->udev->dev, + "%s: Adjusting crystal cap from 0x%x (actually 0x%lx 0x%lx) to 0x%x\n", + __func__, + cfo->crystal_cap, + FIELD_GET(XTAL1, val32), + FIELD_GET(XTAL0, val32), + crystal_cap); + + val32 &= ~(XTAL1 | XTAL0); + val32 |= FIELD_PREP(XTAL1, crystal_cap) | + FIELD_PREP(XTAL0, crystal_cap); + rtl8xxxu_write32(priv, REG_AFE_XTAL_CTRL, val32); + + cfo->crystal_cap = crystal_cap; +} + struct rtl8xxxu_fileops rtl8188fu_fops = { .parse_efuse = rtl8188fu_parse_efuse, .load_firmware = rtl8188fu_load_firmware, @@ -1659,6 +1681,7 @@ struct rtl8xxxu_fileops rtl8188fu_fops = { .update_rate_mask = rtl8xxxu_gen2_update_rate_mask, .report_connect = rtl8xxxu_gen2_report_connect, .fill_txdesc = rtl8xxxu_fill_txdesc_v2, + .set_crystal_cap = rtl8188f_set_crystal_cap, .writeN_block_size = 128, .rx_desc_size = sizeof(struct rtl8xxxu_rxdesc24), .tx_desc_size = sizeof(struct rtl8xxxu_txdesc40), diff --git a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8192e.c b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8192e.c index 550290ae9e17..bc0ec72d0126 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8192e.c +++ b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8192e.c @@ -635,8 +635,7 @@ static int rtl8192eu_parse_efuse(struct rtl8xxxu_priv *priv) efuse->tx_power_index_B.pwr_diff[i - 1].ht40; } - priv->has_xtalk = 1; - priv->xtalk = priv->efuse_wifi.efuse8192eu.xtal_k & 0x3f; + priv->default_crystal_cap = priv->efuse_wifi.efuse8192eu.xtal_k & 0x3f; /* * device_info section seems to be laid out as records @@ -1691,6 +1690,7 @@ struct rtl8xxxu_fileops rtl8192eu_fops = { .update_rate_mask = rtl8xxxu_gen2_update_rate_mask, .report_connect = rtl8xxxu_gen2_report_connect, .fill_txdesc = rtl8xxxu_fill_txdesc_v2, + .set_crystal_cap = rtl8723a_set_crystal_cap, .writeN_block_size = 128, .tx_desc_size = sizeof(struct rtl8xxxu_txdesc40), .rx_desc_size = sizeof(struct rtl8xxxu_rxdesc24), diff --git a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8723a.c b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8723a.c index 44565bf4c1c2..552763a2215c 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8723a.c +++ b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8723a.c @@ -166,10 +166,10 @@ static int rtl8723au_parse_efuse(struct rtl8xxxu_priv *priv) efuse->ht20_max_power_offset, sizeof(efuse->ht20_max_power_offset)); - if (priv->efuse_wifi.efuse8723.version >= 0x01) { - priv->has_xtalk = 1; - priv->xtalk = priv->efuse_wifi.efuse8723.xtal_k & 0x3f; - } + if (priv->efuse_wifi.efuse8723.version >= 0x01) + priv->default_crystal_cap = priv->efuse_wifi.efuse8723.xtal_k & 0x3f; + else + priv->fops->set_crystal_cap = NULL; priv->power_base = &rtl8723a_power_base; @@ -357,6 +357,35 @@ exit: return ret; } +#define XTAL1 GENMASK(23, 18) +#define XTAL0 GENMASK(17, 12) + +void rtl8723a_set_crystal_cap(struct rtl8xxxu_priv *priv, u8 crystal_cap) +{ + struct rtl8xxxu_cfo_tracking *cfo = &priv->cfo_tracking; + u32 val32; + + if (crystal_cap == cfo->crystal_cap) + return; + + val32 = rtl8xxxu_read32(priv, REG_MAC_PHY_CTRL); + + dev_dbg(&priv->udev->dev, + "%s: Adjusting crystal cap from 0x%x (actually 0x%lx 0x%lx) to 0x%x\n", + __func__, + cfo->crystal_cap, + FIELD_GET(XTAL1, val32), + FIELD_GET(XTAL0, val32), + crystal_cap); + + val32 &= ~(XTAL1 | XTAL0); + val32 |= FIELD_PREP(XTAL1, crystal_cap) | + FIELD_PREP(XTAL0, crystal_cap); + rtl8xxxu_write32(priv, REG_MAC_PHY_CTRL, val32); + + cfo->crystal_cap = crystal_cap; +} + struct rtl8xxxu_fileops rtl8723au_fops = { .parse_efuse = rtl8723au_parse_efuse, .load_firmware = rtl8723au_load_firmware, @@ -378,6 +407,7 @@ struct rtl8xxxu_fileops rtl8723au_fops = { .update_rate_mask = rtl8xxxu_update_rate_mask, .report_connect = rtl8xxxu_gen1_report_connect, .fill_txdesc = rtl8xxxu_fill_txdesc_v1, + .set_crystal_cap = rtl8723a_set_crystal_cap, .writeN_block_size = 1024, .rx_agg_buf_size = 16000, .tx_desc_size = sizeof(struct rtl8xxxu_txdesc32), diff --git a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8723b.c b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8723b.c index a27fe06d6f77..9bfcb1e12183 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8723b.c +++ b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8723b.c @@ -445,8 +445,7 @@ static int rtl8723bu_parse_efuse(struct rtl8xxxu_priv *priv) efuse->tx_power_index_B.pwr_diff[i - 1].ht40; } - priv->has_xtalk = 1; - priv->xtalk = priv->efuse_wifi.efuse8723bu.xtal_k & 0x3f; + priv->default_crystal_cap = priv->efuse_wifi.efuse8723bu.xtal_k & 0x3f; dev_info(&priv->udev->dev, "Vendor: %.7s\n", efuse->vendor_name); dev_info(&priv->udev->dev, "Product: %.41s\n", efuse->device_name); @@ -1663,6 +1662,7 @@ struct rtl8xxxu_fileops rtl8723bu_fops = { .update_rate_mask = rtl8xxxu_gen2_update_rate_mask, .report_connect = rtl8xxxu_gen2_report_connect, .fill_txdesc = rtl8xxxu_fill_txdesc_v2, + .set_crystal_cap = rtl8723a_set_crystal_cap, .writeN_block_size = 1024, .tx_desc_size = sizeof(struct rtl8xxxu_txdesc40), .rx_desc_size = sizeof(struct rtl8xxxu_rxdesc24), diff --git a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c index 65bb4dc8f555..3bd9e5370b23 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c +++ b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c @@ -2286,7 +2286,6 @@ void rtl8xxxu_gen1_init_phy_bb(struct rtl8xxxu_priv *priv) */ static int rtl8xxxu_init_phy_bb(struct rtl8xxxu_priv *priv) { - u8 val8; u32 val32; priv->fops->init_phy_bb(priv); @@ -2351,15 +2350,8 @@ static int rtl8xxxu_init_phy_bb(struct rtl8xxxu_priv *priv) rtl8xxxu_write32(priv, REG_TX_TO_TX, val32); } - if (priv->has_xtalk) { - val32 = rtl8xxxu_read32(priv, REG_MAC_PHY_CTRL); - - val8 = priv->xtalk; - val32 &= 0xff000fff; - val32 |= ((val8 | (val8 << 6)) << 12); - - rtl8xxxu_write32(priv, REG_MAC_PHY_CTRL, val32); - } + if (priv->fops->set_crystal_cap) + priv->fops->set_crystal_cap(priv, priv->default_crystal_cap); if (priv->rtl_chip == RTL8192E) rtl8xxxu_write32(priv, REG_AFE_XTAL_CTRL, 0x000f81fb); @@ -4327,6 +4319,15 @@ static int rtl8xxxu_init_device(struct ieee80211_hw *hw) val32 |= 0x0007e000; rtl8xxxu_write32(priv, REG_AFE_MISC, val32); } + + /* Initialise the center frequency offset tracking */ + if (priv->fops->set_crystal_cap) { + val32 = rtl8xxxu_read32(priv, REG_OFDM1_CFO_TRACKING); + priv->cfo_tracking.atc_status = val32 & CFO_TRACKING_ATC_STATUS; + priv->cfo_tracking.adjust = true; + priv->cfo_tracking.crystal_cap = priv->default_crystal_cap; + } + exit: return ret; } @@ -5294,7 +5295,8 @@ error: static void rtl8xxxu_rx_parse_phystats(struct rtl8xxxu_priv *priv, struct ieee80211_rx_status *rx_status, struct rtl8723au_phy_stats *phy_stats, - u32 rxmcs) + u32 rxmcs, struct ieee80211_hdr *hdr, + bool crc_icv_err) { if (phy_stats->sgi_en) rx_status->enc_flags |= RX_ENC_FLAG_SHORT_GI; @@ -5320,6 +5322,21 @@ static void rtl8xxxu_rx_parse_phystats(struct rtl8xxxu_priv *priv, break; } } else { + bool parse_cfo = priv->fops->set_crystal_cap && + priv->vif && + priv->vif->type == NL80211_IFTYPE_STATION && + priv->vif->cfg.assoc && + !crc_icv_err && + !ieee80211_is_ctl(hdr->frame_control) && + ether_addr_equal(priv->vif->bss_conf.bssid, hdr->addr2); + + if (parse_cfo) { + priv->cfo_tracking.cfo_tail[0] = phy_stats->path_cfotail[0]; + priv->cfo_tracking.cfo_tail[1] = phy_stats->path_cfotail[1]; + + priv->cfo_tracking.packet_count++; + } + rx_status->signal = (phy_stats->cck_sig_qual_ofdm_pwdb_all >> 1) - 110; } @@ -5802,7 +5819,8 @@ int rtl8xxxu_parse_rxdesc16(struct rtl8xxxu_priv *priv, struct sk_buff *skb) if (rx_desc->phy_stats) rtl8xxxu_rx_parse_phystats(priv, rx_status, phy_stats, - rx_desc->rxmcs); + rx_desc->rxmcs, (struct ieee80211_hdr *)skb->data, + rx_desc->crc32 || rx_desc->icverr); rx_status->mactime = rx_desc->tsfl; rx_status->flag |= RX_FLAG_MACTIME_START; @@ -5873,7 +5891,8 @@ int rtl8xxxu_parse_rxdesc24(struct rtl8xxxu_priv *priv, struct sk_buff *skb) if (rx_desc->phy_stats) rtl8xxxu_rx_parse_phystats(priv, rx_status, phy_stats, - rx_desc->rxmcs); + rx_desc->rxmcs, (struct ieee80211_hdr *)skb->data, + rx_desc->crc32 || rx_desc->icverr); rx_status->mactime = rx_desc->tsfl; rx_status->flag |= RX_FLAG_MACTIME_START; @@ -6488,6 +6507,94 @@ static void rtl8xxxu_refresh_rate_mask(struct rtl8xxxu_priv *priv, } } +static void rtl8xxxu_set_atc_status(struct rtl8xxxu_priv *priv, bool atc_status) +{ + struct rtl8xxxu_cfo_tracking *cfo = &priv->cfo_tracking; + u32 val32; + + if (atc_status == cfo->atc_status) + return; + + cfo->atc_status = atc_status; + + val32 = rtl8xxxu_read32(priv, REG_OFDM1_CFO_TRACKING); + if (atc_status) + val32 |= CFO_TRACKING_ATC_STATUS; + else + val32 &= ~CFO_TRACKING_ATC_STATUS; + rtl8xxxu_write32(priv, REG_OFDM1_CFO_TRACKING, val32); +} + +/* Central frequency offset correction */ +static void rtl8xxxu_track_cfo(struct rtl8xxxu_priv *priv) +{ + struct rtl8xxxu_cfo_tracking *cfo = &priv->cfo_tracking; + int cfo_khz_a, cfo_khz_b, cfo_average; + int crystal_cap; + + if (!priv->vif || !priv->vif->cfg.assoc) { + /* Reset */ + cfo->adjust = true; + + if (cfo->crystal_cap > priv->default_crystal_cap) + priv->fops->set_crystal_cap(priv, cfo->crystal_cap - 1); + else if (cfo->crystal_cap < priv->default_crystal_cap) + priv->fops->set_crystal_cap(priv, cfo->crystal_cap + 1); + + rtl8xxxu_set_atc_status(priv, true); + + return; + } + + if (cfo->packet_count == cfo->packet_count_pre) + /* No new information. */ + return; + + cfo->packet_count_pre = cfo->packet_count; + + /* CFO_tail[1:0] is S(8,7), (num_subcarrier>>7) x 312.5K = CFO value(K Hz) */ + cfo_khz_a = (int)((cfo->cfo_tail[0] * 3125) / 10) >> 7; + cfo_khz_b = (int)((cfo->cfo_tail[1] * 3125) / 10) >> 7; + + if (priv->tx_paths == 1) + cfo_average = cfo_khz_a; + else + cfo_average = (cfo_khz_a + cfo_khz_b) / 2; + + dev_dbg(&priv->udev->dev, "cfo_average: %d\n", cfo_average); + + if (cfo->adjust) { + if (abs(cfo_average) < CFO_TH_XTAL_LOW) + cfo->adjust = false; + } else { + if (abs(cfo_average) > CFO_TH_XTAL_HIGH) + cfo->adjust = true; + } + + /* + * TODO: We should return here only if bluetooth is enabled. + * See the vendor drivers for how to determine that. + */ + if (priv->has_bluetooth) + return; + + if (!cfo->adjust) + return; + + crystal_cap = cfo->crystal_cap; + + if (cfo_average > CFO_TH_XTAL_LOW) + crystal_cap++; + else if (cfo_average < -CFO_TH_XTAL_LOW) + crystal_cap--; + + crystal_cap = clamp(crystal_cap, 0, 0x3f); + + priv->fops->set_crystal_cap(priv, crystal_cap); + + rtl8xxxu_set_atc_status(priv, abs(cfo_average) >= CFO_TH_ATC); +} + static void rtl8xxxu_watchdog_callback(struct work_struct *work) { struct ieee80211_vif *vif; @@ -6512,6 +6619,10 @@ static void rtl8xxxu_watchdog_callback(struct work_struct *work) rcu_read_unlock(); signal = ieee80211_ave_rssi(vif); + + if (priv->fops->set_crystal_cap) + rtl8xxxu_track_cfo(priv); + rtl8xxxu_refresh_rate_mask(priv, signal, sta); } diff --git a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_regs.h b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_regs.h index 35bde1404793..190bc0e8dc33 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_regs.h +++ b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_regs.h @@ -1027,6 +1027,7 @@ #define REG_OFDM1_TRX_PATH_ENABLE 0x0d04 #define REG_OFDM1_CFO_TRACKING 0x0d2c +#define CFO_TRACKING_ATC_STATUS BIT(11) #define REG_OFDM1_CSI_FIX_MASK1 0x0d40 #define REG_OFDM1_CSI_FIX_MASK2 0x0d44 From 2ad2a813b803593832fa10b2c3d6d6065aec17ad Mon Sep 17 00:00:00 2001 From: Bitterblue Smith Date: Tue, 1 Nov 2022 22:33:09 +0200 Subject: [PATCH 0627/2002] wifi: rtl8xxxu: Fix the CCK RSSI calculation The CCK RSSI calculation is incorrect for the RTL8723BU, RTL8192EU, and RTL8188FU. Add new functions for these chips with code copied from their vendor drivers. Use the old code only for the RTL8723AU and RTL8192CU. I didn't notice any difference in the reported signal strength with my RTL8188FU, but I didn't look very hard either. Signed-off-by: Bitterblue Smith Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/926c838f-4997-698b-4da9-44582e2af99a@gmail.com --- .../net/wireless/realtek/rtl8xxxu/rtl8xxxu.h | 3 ++ .../realtek/rtl8xxxu/rtl8xxxu_8188f.c | 32 ++++++++++++++++ .../realtek/rtl8xxxu/rtl8xxxu_8192c.c | 1 + .../realtek/rtl8xxxu/rtl8xxxu_8192e.c | 23 +++++++++++ .../realtek/rtl8xxxu/rtl8xxxu_8723a.c | 23 +++++++++++ .../realtek/rtl8xxxu/rtl8xxxu_8723b.c | 29 ++++++++++++++ .../wireless/realtek/rtl8xxxu/rtl8xxxu_core.c | 38 ++++++++++++------- .../wireless/realtek/rtl8xxxu/rtl8xxxu_regs.h | 3 ++ 8 files changed, 138 insertions(+), 14 deletions(-) diff --git a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h index eee48817d4d7..6ff554ba0250 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h +++ b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h @@ -1395,6 +1395,7 @@ struct rtl8xxxu_priv { u32 ep_tx_normal_queue:1; u32 ep_tx_low_queue:1; u32 rx_buf_aggregation:1; + u32 cck_agc_report_type:1; u8 default_crystal_cap; unsigned int pipe_interrupt; unsigned int pipe_in; @@ -1499,6 +1500,7 @@ struct rtl8xxxu_fileops { bool short_preamble, bool ampdu_enable, u32 rts_rate); void (*set_crystal_cap) (struct rtl8xxxu_priv *priv, u8 crystal_cap); + s8 (*cck_rssi) (struct rtl8xxxu_priv *priv, u8 cck_agc_rpt); int writeN_block_size; int rx_agg_buf_size; char tx_desc_size; @@ -1609,6 +1611,7 @@ void rtl8723bu_set_ps_tdma(struct rtl8xxxu_priv *priv, u8 arg1, u8 arg2, u8 arg3, u8 arg4, u8 arg5); void rtl8723bu_phy_init_antenna_selection(struct rtl8xxxu_priv *priv); void rtl8723a_set_crystal_cap(struct rtl8xxxu_priv *priv, u8 crystal_cap); +s8 rtl8723a_cck_rssi(struct rtl8xxxu_priv *priv, u8 cck_agc_rpt); extern struct rtl8xxxu_fileops rtl8188fu_fops; extern struct rtl8xxxu_fileops rtl8192cu_fops; diff --git a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8188f.c b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8188f.c index 9f40d261ac71..5a5b7fa4283c 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8188f.c +++ b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8188f.c @@ -1658,6 +1658,37 @@ static void rtl8188f_set_crystal_cap(struct rtl8xxxu_priv *priv, u8 crystal_cap) cfo->crystal_cap = crystal_cap; } +static s8 rtl8188f_cck_rssi(struct rtl8xxxu_priv *priv, u8 cck_agc_rpt) +{ + s8 rx_pwr_all = 0x00; + u8 vga_idx, lna_idx; + + lna_idx = (cck_agc_rpt & 0xE0) >> 5; + vga_idx = cck_agc_rpt & 0x1F; + + switch (lna_idx) { + case 7: + if (vga_idx <= 27) + rx_pwr_all = -100 + 2 * (27 - vga_idx); + else + rx_pwr_all = -100; + break; + case 5: + rx_pwr_all = -74 + 2 * (21 - vga_idx); + break; + case 3: + rx_pwr_all = -60 + 2 * (20 - vga_idx); + break; + case 1: + rx_pwr_all = -44 + 2 * (19 - vga_idx); + break; + default: + break; + } + + return rx_pwr_all; +} + struct rtl8xxxu_fileops rtl8188fu_fops = { .parse_efuse = rtl8188fu_parse_efuse, .load_firmware = rtl8188fu_load_firmware, @@ -1682,6 +1713,7 @@ struct rtl8xxxu_fileops rtl8188fu_fops = { .report_connect = rtl8xxxu_gen2_report_connect, .fill_txdesc = rtl8xxxu_fill_txdesc_v2, .set_crystal_cap = rtl8188f_set_crystal_cap, + .cck_rssi = rtl8188f_cck_rssi, .writeN_block_size = 128, .rx_desc_size = sizeof(struct rtl8xxxu_rxdesc24), .tx_desc_size = sizeof(struct rtl8xxxu_txdesc40), diff --git a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8192c.c b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8192c.c index e9bc94edfa79..9dfeeaa74927 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8192c.c +++ b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8192c.c @@ -561,6 +561,7 @@ struct rtl8xxxu_fileops rtl8192cu_fops = { .update_rate_mask = rtl8xxxu_update_rate_mask, .report_connect = rtl8xxxu_gen1_report_connect, .fill_txdesc = rtl8xxxu_fill_txdesc_v1, + .cck_rssi = rtl8723a_cck_rssi, .writeN_block_size = 128, .rx_agg_buf_size = 16000, .tx_desc_size = sizeof(struct rtl8xxxu_txdesc32), diff --git a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8192e.c b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8192e.c index bc0ec72d0126..973a02afc82b 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8192e.c +++ b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8192e.c @@ -1670,6 +1670,28 @@ static void rtl8192e_enable_rf(struct rtl8xxxu_priv *priv) rtl8xxxu_write8(priv, REG_PAD_CTRL1, val8); } +static s8 rtl8192e_cck_rssi(struct rtl8xxxu_priv *priv, u8 cck_agc_rpt) +{ + static const s8 lna_gain_table_0[8] = {15, 9, -10, -21, -23, -27, -43, -44}; + static const s8 lna_gain_table_1[8] = {24, 18, 13, -4, -11, -18, -31, -36}; + + s8 rx_pwr_all = 0x00; + u8 vga_idx, lna_idx; + s8 lna_gain = 0; + + lna_idx = (cck_agc_rpt & 0xE0) >> 5; + vga_idx = cck_agc_rpt & 0x1F; + + if (priv->cck_agc_report_type == 0) + lna_gain = lna_gain_table_0[lna_idx]; + else + lna_gain = lna_gain_table_1[lna_idx]; + + rx_pwr_all = lna_gain - (2 * vga_idx); + + return rx_pwr_all; +} + struct rtl8xxxu_fileops rtl8192eu_fops = { .parse_efuse = rtl8192eu_parse_efuse, .load_firmware = rtl8192eu_load_firmware, @@ -1691,6 +1713,7 @@ struct rtl8xxxu_fileops rtl8192eu_fops = { .report_connect = rtl8xxxu_gen2_report_connect, .fill_txdesc = rtl8xxxu_fill_txdesc_v2, .set_crystal_cap = rtl8723a_set_crystal_cap, + .cck_rssi = rtl8192e_cck_rssi, .writeN_block_size = 128, .tx_desc_size = sizeof(struct rtl8xxxu_txdesc40), .rx_desc_size = sizeof(struct rtl8xxxu_rxdesc24), diff --git a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8723a.c b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8723a.c index 552763a2215c..8d8eb16a0970 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8723a.c +++ b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8723a.c @@ -386,6 +386,28 @@ void rtl8723a_set_crystal_cap(struct rtl8xxxu_priv *priv, u8 crystal_cap) cfo->crystal_cap = crystal_cap; } +s8 rtl8723a_cck_rssi(struct rtl8xxxu_priv *priv, u8 cck_agc_rpt) +{ + s8 rx_pwr_all = 0x00; + + switch (cck_agc_rpt & 0xc0) { + case 0xc0: + rx_pwr_all = -46 - (cck_agc_rpt & 0x3e); + break; + case 0x80: + rx_pwr_all = -26 - (cck_agc_rpt & 0x3e); + break; + case 0x40: + rx_pwr_all = -12 - (cck_agc_rpt & 0x3e); + break; + case 0x00: + rx_pwr_all = 16 - (cck_agc_rpt & 0x3e); + break; + } + + return rx_pwr_all; +} + struct rtl8xxxu_fileops rtl8723au_fops = { .parse_efuse = rtl8723au_parse_efuse, .load_firmware = rtl8723au_load_firmware, @@ -408,6 +430,7 @@ struct rtl8xxxu_fileops rtl8723au_fops = { .report_connect = rtl8xxxu_gen1_report_connect, .fill_txdesc = rtl8xxxu_fill_txdesc_v1, .set_crystal_cap = rtl8723a_set_crystal_cap, + .cck_rssi = rtl8723a_cck_rssi, .writeN_block_size = 1024, .rx_agg_buf_size = 16000, .tx_desc_size = sizeof(struct rtl8xxxu_txdesc32), diff --git a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8723b.c b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8723b.c index 9bfcb1e12183..9214c1d3b644 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8723b.c +++ b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8723b.c @@ -1639,6 +1639,34 @@ static void rtl8723bu_init_statistics(struct rtl8xxxu_priv *priv) rtl8xxxu_write32(priv, REG_OFDM0_FA_RSTC, val32); } +static s8 rtl8723b_cck_rssi(struct rtl8xxxu_priv *priv, u8 cck_agc_rpt) +{ + s8 rx_pwr_all = 0x00; + u8 vga_idx, lna_idx; + + lna_idx = (cck_agc_rpt & 0xE0) >> 5; + vga_idx = cck_agc_rpt & 0x1F; + + switch (lna_idx) { + case 6: + rx_pwr_all = -34 - (2 * vga_idx); + break; + case 4: + rx_pwr_all = -14 - (2 * vga_idx); + break; + case 1: + rx_pwr_all = 6 - (2 * vga_idx); + break; + case 0: + rx_pwr_all = 16 - (2 * vga_idx); + break; + default: + break; + } + + return rx_pwr_all; +} + struct rtl8xxxu_fileops rtl8723bu_fops = { .parse_efuse = rtl8723bu_parse_efuse, .load_firmware = rtl8723bu_load_firmware, @@ -1663,6 +1691,7 @@ struct rtl8xxxu_fileops rtl8723bu_fops = { .report_connect = rtl8xxxu_gen2_report_connect, .fill_txdesc = rtl8xxxu_fill_txdesc_v2, .set_crystal_cap = rtl8723a_set_crystal_cap, + .cck_rssi = rtl8723b_cck_rssi, .writeN_block_size = 1024, .tx_desc_size = sizeof(struct rtl8xxxu_txdesc40), .rx_desc_size = sizeof(struct rtl8xxxu_rxdesc24), diff --git a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c index 3bd9e5370b23..51cb22ee55cd 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c +++ b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c @@ -4318,6 +4318,29 @@ static int rtl8xxxu_init_device(struct ieee80211_hw *hw) val32 &= 0xfff00fff; val32 |= 0x0007e000; rtl8xxxu_write32(priv, REG_AFE_MISC, val32); + + /* + * 0x824[9] = 0x82C[9] = 0xA80[7] those registers setting + * should be equal or CCK RSSI report may be incorrect + */ + val32 = rtl8xxxu_read32(priv, REG_FPGA0_XA_HSSI_PARM2); + priv->cck_agc_report_type = val32 & FPGA0_HSSI_PARM2_CCK_HIGH_PWR; + + val32 = rtl8xxxu_read32(priv, REG_FPGA0_XB_HSSI_PARM2); + if (priv->cck_agc_report_type != (bool)(val32 & FPGA0_HSSI_PARM2_CCK_HIGH_PWR)) { + if (priv->cck_agc_report_type) + val32 |= FPGA0_HSSI_PARM2_CCK_HIGH_PWR; + else + val32 &= ~FPGA0_HSSI_PARM2_CCK_HIGH_PWR; + rtl8xxxu_write32(priv, REG_FPGA0_XB_HSSI_PARM2, val32); + } + + val32 = rtl8xxxu_read32(priv, REG_AGC_RPT); + if (priv->cck_agc_report_type) + val32 |= AGC_RPT_CCK; + else + val32 &= ~AGC_RPT_CCK; + rtl8xxxu_write32(priv, REG_AGC_RPT, val32); } /* Initialise the center frequency offset tracking */ @@ -5307,20 +5330,7 @@ static void rtl8xxxu_rx_parse_phystats(struct rtl8xxxu_priv *priv, */ u8 cck_agc_rpt = phy_stats->cck_agc_rpt_ofdm_cfosho_a; - switch (cck_agc_rpt & 0xc0) { - case 0xc0: - rx_status->signal = -46 - (cck_agc_rpt & 0x3e); - break; - case 0x80: - rx_status->signal = -26 - (cck_agc_rpt & 0x3e); - break; - case 0x40: - rx_status->signal = -12 - (cck_agc_rpt & 0x3e); - break; - case 0x00: - rx_status->signal = 16 - (cck_agc_rpt & 0x3e); - break; - } + rx_status->signal = priv->fops->cck_rssi(priv, cck_agc_rpt); } else { bool parse_cfo = priv->fops->set_crystal_cap && priv->vif && diff --git a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_regs.h b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_regs.h index 190bc0e8dc33..04bf77959fba 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_regs.h +++ b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_regs.h @@ -960,6 +960,9 @@ #define CCK_PD_TYPE1_LV3_TH 0xdd #define CCK_PD_TYPE1_LV4_TH 0xed +#define REG_AGC_RPT 0xa80 +#define AGC_RPT_CCK BIT(7) + #define REG_CONFIG_ANT_A 0x0b68 #define REG_CONFIG_ANT_B 0x0b6c From 7b0ac469e331d9f9fd77f4ebb7a6322f5562db67 Mon Sep 17 00:00:00 2001 From: Bitterblue Smith Date: Tue, 1 Nov 2022 22:33:49 +0200 Subject: [PATCH 0628/2002] wifi: rtl8xxxu: Recognise all possible chip cuts The chip cut, also known as the chip version, is a letter from A (0) to P (15). Recognise them all instead of printing "unknown" when it's greater than E. Signed-off-by: Bitterblue Smith Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/1559c705-0b0b-8dcb-7596-fbb85844d3d9@gmail.com --- .../wireless/realtek/rtl8xxxu/rtl8xxxu_core.c | 26 ++++--------------- 1 file changed, 5 insertions(+), 21 deletions(-) diff --git a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c index 51cb22ee55cd..823e50811b0d 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c +++ b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c @@ -1574,30 +1574,14 @@ rtl8xxxu_set_spec_sifs(struct rtl8xxxu_priv *priv, u16 cck, u16 ofdm) static void rtl8xxxu_print_chipinfo(struct rtl8xxxu_priv *priv) { struct device *dev = &priv->udev->dev; - char *cut; + char cut = '?'; - switch (priv->chip_cut) { - case 0: - cut = "A"; - break; - case 1: - cut = "B"; - break; - case 2: - cut = "C"; - break; - case 3: - cut = "D"; - break; - case 4: - cut = "E"; - break; - default: - cut = "unknown"; - } + /* Currently always true: chip_cut is 4 bits. */ + if (priv->chip_cut <= 15) + cut = 'A' + priv->chip_cut; dev_info(dev, - "RTL%s rev %s (%s) %iT%iR, TX queues %i, WiFi=%i, BT=%i, GPS=%i, HI PA=%i\n", + "RTL%s rev %c (%s) %iT%iR, TX queues %i, WiFi=%i, BT=%i, GPS=%i, HI PA=%i\n", priv->chip_name, cut, priv->chip_vendor, priv->tx_paths, priv->rx_paths, priv->ep_tx_count, priv->has_wifi, priv->has_bluetooth, priv->has_gps, priv->hi_pa); From e952deaa95bae89663a75d855a7e61a40f534fb0 Mon Sep 17 00:00:00 2001 From: Bitterblue Smith Date: Tue, 1 Nov 2022 22:34:37 +0200 Subject: [PATCH 0629/2002] wifi: rtl8xxxu: Set IEEE80211_HW_SUPPORT_FAST_XMIT According to commit 60d7900dcb98 ("wlcore: enable IEEE80211_HW_SUPPORT_FAST_XMIT"), we can use this because all the chips have hardware rate control. This is one of the things mac80211 requires before it will handle MSDU aggregation for us. Signed-off-by: Bitterblue Smith Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/9b59e735-6b89-a557-fafc-2da87fdd5b48@gmail.com --- drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c index 823e50811b0d..58e6416787df 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c +++ b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c @@ -6998,6 +6998,7 @@ static int rtl8xxxu_probe(struct usb_interface *interface, * The firmware handles rate control */ ieee80211_hw_set(hw, HAS_RATE_CONTROL); + ieee80211_hw_set(hw, SUPPORT_FAST_XMIT); ieee80211_hw_set(hw, AMPDU_AGGREGATION); wiphy_ext_feature_set(hw->wiphy, NL80211_EXT_FEATURE_CQM_RSSI_LIST); From bd954a7e4aa0739630884fc572d2d0e4dcd16e25 Mon Sep 17 00:00:00 2001 From: Bitterblue Smith Date: Tue, 1 Nov 2022 22:35:36 +0200 Subject: [PATCH 0630/2002] wifi: rtl8xxxu: Use dev_* instead of pr_info Replace two instances of bare pr_info with dev_info and dev_warn. Also make their messages a little more informative. Signed-off-by: Bitterblue Smith Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/c9f3ebb2-769b-7d80-cac2-5a9d1bcc010a@gmail.com --- drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c index 58e6416787df..019f8ddd418b 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c +++ b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c @@ -2015,7 +2015,8 @@ static int rtl8xxxu_download_firmware(struct rtl8xxxu_priv *priv) val8 = rtl8xxxu_read8(priv, REG_MCU_FW_DL); if (val8 & MCU_FW_RAM_SEL) { - pr_info("do the RAM reset\n"); + dev_info(&priv->udev->dev, + "Firmware is already running, resetting the MCU.\n"); rtl8xxxu_write8(priv, REG_MCU_FW_DL, 0x00); priv->fops->reset_8051(priv); } @@ -5413,7 +5414,8 @@ static void rtl8xxxu_rx_urb_work(struct work_struct *work) rtl8xxxu_queue_rx_urb(priv, rx_urb); break; default: - pr_info("failed to requeue urb %i\n", ret); + dev_warn(&priv->udev->dev, + "failed to requeue urb with error %i\n", ret); skb = (struct sk_buff *)rx_urb->urb.context; dev_kfree_skb(skb); usb_free_urb(&rx_urb->urb); From 864f88884c42c838e2ca6473b9003e0f34d7fd15 Mon Sep 17 00:00:00 2001 From: Daniel Willenson Date: Mon, 17 Oct 2022 11:52:59 -0400 Subject: [PATCH 0631/2002] ixgbe: change MAX_RXD/MAX_TXD based on adapter type Set the length limit for the receive descriptor buffer and transmit descriptor buffer based on the controller type. The values used are called out in the controller datasheets as a 'Note:' in the RDLEN and TDLEN register descriptions. This allows the user to use ethtool to allocate larger descriptor buffers in the case where data is received or transmitted too quickly for the driver to keep up. Signed-off-by: Daniel Willenson Tested-by: Gurucharan (A Contingent worker at Intel) Signed-off-by: Tony Nguyen --- drivers/net/ethernet/intel/ixgbe/ixgbe.h | 10 ++++- .../net/ethernet/intel/ixgbe/ixgbe_ethtool.c | 44 +++++++++++++++++-- 2 files changed, 48 insertions(+), 6 deletions(-) diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe.h b/drivers/net/ethernet/intel/ixgbe/ixgbe.h index 5369a97ff5ec..bc68b8f2176d 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe.h +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe.h @@ -39,7 +39,10 @@ /* TX/RX descriptor defines */ #define IXGBE_DEFAULT_TXD 512 #define IXGBE_DEFAULT_TX_WORK 256 -#define IXGBE_MAX_TXD 4096 +#define IXGBE_MAX_TXD_82598 4096 +#define IXGBE_MAX_TXD_82599 8192 +#define IXGBE_MAX_TXD_X540 8192 +#define IXGBE_MAX_TXD_X550 32768 #define IXGBE_MIN_TXD 64 #if (PAGE_SIZE < 8192) @@ -47,7 +50,10 @@ #else #define IXGBE_DEFAULT_RXD 128 #endif -#define IXGBE_MAX_RXD 4096 +#define IXGBE_MAX_RXD_82598 4096 +#define IXGBE_MAX_RXD_82599 8192 +#define IXGBE_MAX_RXD_X540 8192 +#define IXGBE_MAX_RXD_X550 32768 #define IXGBE_MIN_RXD 64 /* flow control */ diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c index eda7188e8df4..d737e851a9e0 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c @@ -1117,6 +1117,42 @@ static void ixgbe_get_drvinfo(struct net_device *netdev, drvinfo->n_priv_flags = IXGBE_PRIV_FLAGS_STR_LEN; } +static u32 ixgbe_get_max_rxd(struct ixgbe_adapter *adapter) +{ + switch (adapter->hw.mac.type) { + case ixgbe_mac_82598EB: + return IXGBE_MAX_RXD_82598; + case ixgbe_mac_82599EB: + return IXGBE_MAX_RXD_82599; + case ixgbe_mac_X540: + return IXGBE_MAX_RXD_X540; + case ixgbe_mac_X550: + case ixgbe_mac_X550EM_x: + case ixgbe_mac_x550em_a: + return IXGBE_MAX_RXD_X550; + default: + return IXGBE_MAX_RXD_82598; + } +} + +static u32 ixgbe_get_max_txd(struct ixgbe_adapter *adapter) +{ + switch (adapter->hw.mac.type) { + case ixgbe_mac_82598EB: + return IXGBE_MAX_TXD_82598; + case ixgbe_mac_82599EB: + return IXGBE_MAX_TXD_82599; + case ixgbe_mac_X540: + return IXGBE_MAX_TXD_X540; + case ixgbe_mac_X550: + case ixgbe_mac_X550EM_x: + case ixgbe_mac_x550em_a: + return IXGBE_MAX_TXD_X550; + default: + return IXGBE_MAX_TXD_82598; + } +} + static void ixgbe_get_ringparam(struct net_device *netdev, struct ethtool_ringparam *ring, struct kernel_ethtool_ringparam *kernel_ring, @@ -1126,8 +1162,8 @@ static void ixgbe_get_ringparam(struct net_device *netdev, struct ixgbe_ring *tx_ring = adapter->tx_ring[0]; struct ixgbe_ring *rx_ring = adapter->rx_ring[0]; - ring->rx_max_pending = IXGBE_MAX_RXD; - ring->tx_max_pending = IXGBE_MAX_TXD; + ring->rx_max_pending = ixgbe_get_max_rxd(adapter); + ring->tx_max_pending = ixgbe_get_max_txd(adapter); ring->rx_pending = rx_ring->count; ring->tx_pending = tx_ring->count; } @@ -1146,11 +1182,11 @@ static int ixgbe_set_ringparam(struct net_device *netdev, return -EINVAL; new_tx_count = clamp_t(u32, ring->tx_pending, - IXGBE_MIN_TXD, IXGBE_MAX_TXD); + IXGBE_MIN_TXD, ixgbe_get_max_txd(adapter)); new_tx_count = ALIGN(new_tx_count, IXGBE_REQ_TX_DESCRIPTOR_MULTIPLE); new_rx_count = clamp_t(u32, ring->rx_pending, - IXGBE_MIN_RXD, IXGBE_MAX_RXD); + IXGBE_MIN_RXD, ixgbe_get_max_rxd(adapter)); new_rx_count = ALIGN(new_rx_count, IXGBE_REQ_RX_DESCRIPTOR_MULTIPLE); if ((new_tx_count == adapter->tx_ring_count) && From 6a6f9e3e03aec34fa0eb3c3efca6da777c57187a Mon Sep 17 00:00:00 2001 From: Anirudh Venkataramanan Date: Wed, 28 Sep 2022 15:42:10 -0700 Subject: [PATCH 0632/2002] ixgbe: Remove local variable Remove local variable "match" and directly return evaluated conditional instead. Suggested-by: Alexander Duyck Signed-off-by: Anirudh Venkataramanan Tested-by: Gurucharan (A Contingent worker at Intel) Signed-off-by: Tony Nguyen --- drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c index d737e851a9e0..6cfc9dc16537 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c @@ -1996,18 +1996,13 @@ static bool ixgbe_check_lbtest_frame(struct ixgbe_rx_buffer *rx_buffer, unsigned int frame_size) { unsigned char *data; - bool match = true; frame_size >>= 1; data = page_address(rx_buffer->page) + rx_buffer->page_offset; - if (data[3] != 0xFF || - data[frame_size + 10] != 0xBE || - data[frame_size + 12] != 0xAF) - match = false; - - return match; + return data[3] == 0xFF && data[frame_size + 10] == 0xBE && + data[frame_size + 12] == 0xAF; } static u16 ixgbe_clean_test_rings(struct ixgbe_ring *rx_ring, From f49fafa504d521f8f33bffef23bd4c4a801eec8f Mon Sep 17 00:00:00 2001 From: Yang Li Date: Wed, 2 Nov 2022 13:31:39 +0800 Subject: [PATCH 0633/2002] ixgbe: Remove unneeded semicolon ./drivers/net/ethernet/intel/ixgbe/ixgbe_ptp.c:1305:2-3: Unneeded semicolon Link: https://bugzilla.openanolis.cn/show_bug.cgi?id=2688 Reported-by: Abaci Robot Signed-off-by: Yang Li Signed-off-by: Tony Nguyen --- drivers/net/ethernet/intel/ixgbe/ixgbe_ptp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_ptp.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_ptp.c index fd3f77a9e28d..0310af851086 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_ptp.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_ptp.c @@ -1302,7 +1302,7 @@ static void ixgbe_ptp_init_systime(struct ixgbe_adapter *adapter) default: /* Other devices aren't supported */ return; - }; + } IXGBE_WRITE_FLUSH(hw); } From eac0b6804b74dd77dd33ee4a0639b717481ebf54 Mon Sep 17 00:00:00 2001 From: Jan Sokolowski Date: Thu, 6 Oct 2022 14:44:08 +0200 Subject: [PATCH 0634/2002] ixgbevf: Add error messages on vlan error ixgbevf did not provide an error in dmesg if VLAN addition failed. Add two descriptive failure messages in the kernel log. Signed-off-by: Jan Sokolowski Tested-by: Konrad Jankowski Signed-off-by: Tony Nguyen --- .../net/ethernet/intel/ixgbevf/ixgbevf_main.c | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c b/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c index be733677bdc8..0aaf70c063da 100644 --- a/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c +++ b/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c @@ -2044,12 +2044,16 @@ static int ixgbevf_vlan_rx_add_vid(struct net_device *netdev, spin_unlock_bh(&adapter->mbx_lock); - /* translate error return types so error makes sense */ - if (err == IXGBE_ERR_MBX) - return -EIO; + if (err) { + netdev_err(netdev, "VF could not set VLAN %d\n", vid); - if (err == IXGBE_ERR_INVALID_ARGUMENT) - return -EACCES; + /* translate error return types so error makes sense */ + if (err == IXGBE_ERR_MBX) + return -EIO; + + if (err == IXGBE_ERR_INVALID_ARGUMENT) + return -EACCES; + } set_bit(vid, adapter->active_vlans); @@ -2070,6 +2074,9 @@ static int ixgbevf_vlan_rx_kill_vid(struct net_device *netdev, spin_unlock_bh(&adapter->mbx_lock); + if (err) + netdev_err(netdev, "Could not remove VLAN %d\n", vid); + clear_bit(vid, adapter->active_vlans); return err; From 0668716506ca66f90d395f36ccdaebc3e0e84801 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Tue, 18 Oct 2022 02:25:24 -0700 Subject: [PATCH 0635/2002] igb: Do not free q_vector unless new one was allocated Avoid potential use-after-free condition under memory pressure. If the kzalloc() fails, q_vector will be freed but left in the original adapter->q_vector[v_idx] array position. Cc: Jesse Brandeburg Cc: Tony Nguyen Cc: "David S. Miller" Cc: Eric Dumazet Cc: Jakub Kicinski Cc: Paolo Abeni Cc: intel-wired-lan@lists.osuosl.org Cc: netdev@vger.kernel.org Signed-off-by: Kees Cook Reviewed-by: Michael J. Ruhl Reviewed-by: Jacob Keller Tested-by: Gurucharan (A Contingent worker at Intel) Signed-off-by: Tony Nguyen --- drivers/net/ethernet/intel/igb/igb_main.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/intel/igb/igb_main.c b/drivers/net/ethernet/intel/igb/igb_main.c index d6c1c2e66f26..c2bb658198bf 100644 --- a/drivers/net/ethernet/intel/igb/igb_main.c +++ b/drivers/net/ethernet/intel/igb/igb_main.c @@ -1202,8 +1202,12 @@ static int igb_alloc_q_vector(struct igb_adapter *adapter, if (!q_vector) { q_vector = kzalloc(size, GFP_KERNEL); } else if (size > ksize(q_vector)) { - kfree_rcu(q_vector, rcu); - q_vector = kzalloc(size, GFP_KERNEL); + struct igb_q_vector *new_q_vector; + + new_q_vector = kzalloc(size, GFP_KERNEL); + if (new_q_vector) + kfree_rcu(q_vector, rcu); + q_vector = new_q_vector; } else { memset(q_vector, 0, size); } From e9d696cb44e31184ba3b4b95a4e87442f5198607 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Tue, 18 Oct 2022 02:25:25 -0700 Subject: [PATCH 0636/2002] igb: Proactively round up to kmalloc bucket size In preparation for removing the "silently change allocation size" users of ksize(), explicitly round up all q_vector allocations so that allocations can be correctly compared to ksize(). Cc: Jesse Brandeburg Cc: Tony Nguyen Cc: "David S. Miller" Cc: Eric Dumazet Cc: Jakub Kicinski Cc: Paolo Abeni Cc: intel-wired-lan@lists.osuosl.org Cc: netdev@vger.kernel.org Signed-off-by: Kees Cook Tested-by: Gurucharan (A Contingent worker at Intel) Reviewed-by: Michael J. Ruhl Signed-off-by: Tony Nguyen --- drivers/net/ethernet/intel/igb/igb_main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/intel/igb/igb_main.c b/drivers/net/ethernet/intel/igb/igb_main.c index c2bb658198bf..97290fc0fddd 100644 --- a/drivers/net/ethernet/intel/igb/igb_main.c +++ b/drivers/net/ethernet/intel/igb/igb_main.c @@ -1195,7 +1195,7 @@ static int igb_alloc_q_vector(struct igb_adapter *adapter, return -ENOMEM; ring_count = txr_count + rxr_count; - size = struct_size(q_vector, ring, ring_count); + size = kmalloc_size_roundup(struct_size(q_vector, ring, ring_count)); /* allocate q_vector and rings */ q_vector = adapter->q_vector[v_idx]; From a3b666bfa9c9edc05bca62a87abafe0936bd7f97 Mon Sep 17 00:00:00 2001 From: Andrii Nakryiko Date: Fri, 4 Nov 2022 09:36:44 -0700 Subject: [PATCH 0637/2002] bpf: propagate precision in ALU/ALU64 operations When processing ALU/ALU64 operations (apart from BPF_MOV, which is handled correctly already; and BPF_NEG and BPF_END are special and don't have source register), if destination register is already marked precise, this causes problem with potentially missing precision tracking for the source register. E.g., when we have r1 >>= r5 and r1 is marked precise, but r5 isn't, this will lead to r5 staying as imprecise. This is due to the precision backtracking logic stopping early when it sees r1 is already marked precise. If r1 wasn't precise, we'd keep backtracking and would add r5 to the set of registers that need to be marked precise. So there is a discrepancy here which can lead to invalid and incompatible states matched due to lack of precision marking on r5. If r1 wasn't precise, precision backtracking would correctly mark both r1 and r5 as precise. This is simple to fix, though. During the forward instruction simulation pass, for arithmetic operations of `scalar = scalar` form (where is ALU or ALU64 operations), if destination register is already precise, mark source register as precise. This applies only when both involved registers are SCALARs. `ptr += scalar` and `scalar += ptr` cases are already handled correctly. This does have (negative) effect on some selftest programs and few Cilium programs. ~/baseline-tmp-results.csv are veristat results with this patch, while ~/baseline-results.csv is without it. See post scriptum for instructions on how to make Cilium programs testable with veristat. Correctness has a price. $ ./veristat -C -e file,prog,insns,states ~/baseline-results.csv ~/baseline-tmp-results.csv | grep -v '+0' File Program Total insns (A) Total insns (B) Total insns (DIFF) Total states (A) Total states (B) Total states (DIFF) ----------------------- -------------------- --------------- --------------- ------------------ ---------------- ---------------- ------------------- bpf_cubic.bpf.linked1.o bpf_cubic_cong_avoid 997 1700 +703 (+70.51%) 62 90 +28 (+45.16%) test_l4lb.bpf.linked1.o balancer_ingress 4559 5469 +910 (+19.96%) 118 126 +8 (+6.78%) ----------------------- -------------------- --------------- --------------- ------------------ ---------------- ---------------- ------------------- $ ./veristat -C -e file,prog,verdict,insns,states ~/baseline-results-cilium.csv ~/baseline-tmp-results-cilium.csv | grep -v '+0' File Program Total insns (A) Total insns (B) Total insns (DIFF) Total states (A) Total states (B) Total states (DIFF) ------------- ------------------------------ --------------- --------------- ------------------ ---------------- ---------------- ------------------- bpf_host.o tail_nodeport_nat_ingress_ipv6 4448 5261 +813 (+18.28%) 234 247 +13 (+5.56%) bpf_host.o tail_nodeport_nat_ipv6_egress 3396 3446 +50 (+1.47%) 201 203 +2 (+1.00%) bpf_lxc.o tail_nodeport_nat_ingress_ipv6 4448 5261 +813 (+18.28%) 234 247 +13 (+5.56%) bpf_overlay.o tail_nodeport_nat_ingress_ipv6 4448 5261 +813 (+18.28%) 234 247 +13 (+5.56%) bpf_xdp.o tail_lb_ipv4 71736 73442 +1706 (+2.38%) 4295 4370 +75 (+1.75%) ------------- ------------------------------ --------------- --------------- ------------------ ---------------- ---------------- ------------------- P.S. To make Cilium ([0]) programs libbpf-compatible and thus veristat-loadable, apply changes from topmost commit in [1], which does minimal changes to Cilium source code, mostly around SEC() annotations and BPF map definitions. [0] https://github.com/cilium/cilium/ [1] https://github.com/anakryiko/cilium/commits/libbpf-friendliness Fixes: b5dc0163d8fd ("bpf: precise scalar_value tracking") Signed-off-by: Andrii Nakryiko Link: https://lore.kernel.org/r/20221104163649.121784-2-andrii@kernel.org Signed-off-by: Alexei Starovoitov --- kernel/bpf/verifier.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 73a3516f1a48..ddfb4b0ab35f 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -9153,6 +9153,11 @@ static int adjust_reg_min_max_vals(struct bpf_verifier_env *env, return err; return adjust_ptr_min_max_vals(env, insn, dst_reg, src_reg); + } else if (dst_reg->precise) { + /* if dst_reg is precise, src_reg should be precise as well */ + err = mark_chain_precision(env, insn->src_reg); + if (err) + return err; } } else { /* Pretend the src is a reg with a known value, since we only From 529409ea92d590659be487ba0839710329bd8074 Mon Sep 17 00:00:00 2001 From: Andrii Nakryiko Date: Fri, 4 Nov 2022 09:36:45 -0700 Subject: [PATCH 0638/2002] bpf: propagate precision across all frames, not just the last one When equivalent completed state is found and it has additional precision restrictions, BPF verifier propagates precision to currently-being-verified state chain (i.e., including parent states) so that if some of the states in the chain are not yet completed, necessary precision restrictions are enforced. Unfortunately, right now this happens only for the last frame (deepest active subprogram's frame), not all the frames. This can lead to incorrect matching of states due to missing precision marker. Currently this doesn't seem possible as BPF verifier forces everything to precise when validated BPF program has any subprograms. But with the next patch lifting this restriction, this becomes problematic. In fact, without this fix, we'll start getting failure in one of the existing test_verifier test cases: #906/p precise: cross frame pruning FAIL Unexpected success to load! verification time 48 usec stack depth 0+0 processed 26 insns (limit 1000000) max_states_per_insn 3 total_states 17 peak_states 17 mark_read 8 This patch adds precision propagation across all frames. Fixes: a3ce685dd01a ("bpf: fix precision tracking") Signed-off-by: Andrii Nakryiko Link: https://lore.kernel.org/r/20221104163649.121784-3-andrii@kernel.org Signed-off-by: Alexei Starovoitov --- kernel/bpf/verifier.c | 71 ++++++++++++++++++++++++------------------- 1 file changed, 39 insertions(+), 32 deletions(-) diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index ddfb4b0ab35f..5c708eb30664 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -2756,7 +2756,7 @@ static void mark_all_scalars_precise(struct bpf_verifier_env *env, } } -static int __mark_chain_precision(struct bpf_verifier_env *env, int regno, +static int __mark_chain_precision(struct bpf_verifier_env *env, int frame, int regno, int spi) { struct bpf_verifier_state *st = env->cur_state; @@ -2773,7 +2773,7 @@ static int __mark_chain_precision(struct bpf_verifier_env *env, int regno, if (!env->bpf_capable) return 0; - func = st->frame[st->curframe]; + func = st->frame[frame]; if (regno >= 0) { reg = &func->regs[regno]; if (reg->type != SCALAR_VALUE) { @@ -2854,7 +2854,7 @@ static int __mark_chain_precision(struct bpf_verifier_env *env, int regno, break; new_marks = false; - func = st->frame[st->curframe]; + func = st->frame[frame]; bitmap_from_u64(mask, reg_mask); for_each_set_bit(i, mask, 32) { reg = &func->regs[i]; @@ -2920,12 +2920,17 @@ static int __mark_chain_precision(struct bpf_verifier_env *env, int regno, int mark_chain_precision(struct bpf_verifier_env *env, int regno) { - return __mark_chain_precision(env, regno, -1); + return __mark_chain_precision(env, env->cur_state->curframe, regno, -1); } -static int mark_chain_precision_stack(struct bpf_verifier_env *env, int spi) +static int mark_chain_precision_frame(struct bpf_verifier_env *env, int frame, int regno) { - return __mark_chain_precision(env, -1, spi); + return __mark_chain_precision(env, frame, regno, -1); +} + +static int mark_chain_precision_stack_frame(struct bpf_verifier_env *env, int frame, int spi) +{ + return __mark_chain_precision(env, frame, -1, spi); } static bool is_spillable_regtype(enum bpf_reg_type type) @@ -11794,34 +11799,36 @@ static int propagate_precision(struct bpf_verifier_env *env, { struct bpf_reg_state *state_reg; struct bpf_func_state *state; - int i, err = 0; + int i, err = 0, fr; - state = old->frame[old->curframe]; - state_reg = state->regs; - for (i = 0; i < BPF_REG_FP; i++, state_reg++) { - if (state_reg->type != SCALAR_VALUE || - !state_reg->precise) - continue; - if (env->log.level & BPF_LOG_LEVEL2) - verbose(env, "propagating r%d\n", i); - err = mark_chain_precision(env, i); - if (err < 0) - return err; - } + for (fr = old->curframe; fr >= 0; fr--) { + state = old->frame[fr]; + state_reg = state->regs; + for (i = 0; i < BPF_REG_FP; i++, state_reg++) { + if (state_reg->type != SCALAR_VALUE || + !state_reg->precise) + continue; + if (env->log.level & BPF_LOG_LEVEL2) + verbose(env, "frame %d: propagating r%d\n", i, fr); + err = mark_chain_precision_frame(env, fr, i); + if (err < 0) + return err; + } - for (i = 0; i < state->allocated_stack / BPF_REG_SIZE; i++) { - if (!is_spilled_reg(&state->stack[i])) - continue; - state_reg = &state->stack[i].spilled_ptr; - if (state_reg->type != SCALAR_VALUE || - !state_reg->precise) - continue; - if (env->log.level & BPF_LOG_LEVEL2) - verbose(env, "propagating fp%d\n", - (-i - 1) * BPF_REG_SIZE); - err = mark_chain_precision_stack(env, i); - if (err < 0) - return err; + for (i = 0; i < state->allocated_stack / BPF_REG_SIZE; i++) { + if (!is_spilled_reg(&state->stack[i])) + continue; + state_reg = &state->stack[i].spilled_ptr; + if (state_reg->type != SCALAR_VALUE || + !state_reg->precise) + continue; + if (env->log.level & BPF_LOG_LEVEL2) + verbose(env, "frame %d: propagating fp%d\n", + (-i - 1) * BPF_REG_SIZE, fr); + err = mark_chain_precision_stack_frame(env, fr, i); + if (err < 0) + return err; + } } return 0; } From be2ef8161572ec1973124ebc50f56dafc2925e07 Mon Sep 17 00:00:00 2001 From: Andrii Nakryiko Date: Fri, 4 Nov 2022 09:36:46 -0700 Subject: [PATCH 0639/2002] bpf: allow precision tracking for programs with subprogs Stop forcing precise=true for SCALAR registers when BPF program has any subprograms. Current restriction means that any BPF program, as soon as it uses subprograms, will end up not getting any of the precision tracking benefits in reduction of number of verified states. This patch keeps the fallback mark_all_scalars_precise() behavior if precise marking has to cross function frames. E.g., if subprogram requires R1 (first input arg) to be marked precise, ideally we'd need to backtrack to the parent function and keep marking R1 and its dependencies as precise. But right now we give up and force all the SCALARs in any of the current and parent states to be forced to precise=true. We can lift that restriction in the future. But this patch fixes two issues identified when trying to enable precision tracking for subprogs. First, prevent "escaping" from top-most state in a global subprog. While with entry-level BPF program we never end up requesting precision for R1-R5 registers, because R2-R5 are not initialized (and so not readable in correct BPF program), and R1 is PTR_TO_CTX, not SCALAR, and so is implicitly precise. With global subprogs, though, it's different, as global subprog a) can have up to 5 SCALAR input arguments, which might get marked as precise=true and b) it is validated in isolation from its main entry BPF program. b) means that we can end up exhausting parent state chain and still not mark all registers in reg_mask as precise, which would lead to verifier bug warning. To handle that, we need to consider two cases. First, if the very first state is not immediately "checkpointed" (i.e., stored in state lookup hashtable), it will get correct first_insn_idx and last_insn_idx instruction set during state checkpointing. As such, this case is already handled and __mark_chain_precision() already handles that by just doing nothing when we reach to the very first parent state. st->parent will be NULL and we'll just stop. Perhaps some extra check for reg_mask and stack_mask is due here, but this patch doesn't address that issue. More problematic second case is when global function's initial state is immediately checkpointed before we manage to process the very first instruction. This is happening because when there is a call to global subprog from the main program the very first subprog's instruction is marked as pruning point, so before we manage to process first instruction we have to check and checkpoint state. This patch adds a special handling for such "empty" state, which is identified by having st->last_insn_idx set to -1. In such case, we check that we are indeed validating global subprog, and with some sanity checking we mark input args as precise if requested. Note that we also initialize state->first_insn_idx with correct start insn_idx offset. For main program zero is correct value, but for any subprog it's quite confusing to not have first_insn_idx set. This doesn't have any functional impact, but helps with debugging and state printing. We also explicitly initialize state->last_insns_idx instead of relying on is_state_visited() to do this with env->prev_insns_idx, which will be -1 on the very first instruction. This concludes necessary changes to handle specifically global subprog's precision tracking. Second identified problem was missed handling of BPF helper functions that call into subprogs (e.g., bpf_loop and few others). From precision tracking and backtracking logic's standpoint those are effectively calls into subprogs and should be called as BPF_PSEUDO_CALL calls. This patch takes the least intrusive way and just checks against a short list of current BPF helpers that do call subprogs, encapsulated in is_callback_calling_function() function. But to prevent accidentally forgetting to add new BPF helpers to this "list", we also do a sanity check in __check_func_call, which has to be called for each such special BPF helper, to validate that BPF helper is indeed recognized as callback-calling one. This should catch any missed checks in the future. Adding some special flags to be added in function proto definitions seemed like an overkill in this case. With the above changes, it's possible to remove forceful setting of reg->precise to true in __mark_reg_unknown, which turns on precision tracking both inside subprogs and entry progs that have subprogs. No warnings or errors were detected across all the selftests, but also when validating with veristat against internal Meta BPF objects and Cilium objects. Further, in some BPF programs there are noticeable reduction in number of states and instructions validated due to more effective precision tracking, especially benefiting syncookie test. $ ./veristat -C -e file,prog,insns,states ~/baseline-results.csv ~/subprog-precise-results.csv | grep -v '+0' File Program Total insns (A) Total insns (B) Total insns (DIFF) Total states (A) Total states (B) Total states (DIFF) ---------------------------------------- -------------------------- --------------- --------------- ------------------ ---------------- ---------------- ------------------- pyperf600_bpf_loop.bpf.linked1.o on_event 3966 3678 -288 (-7.26%) 306 276 -30 (-9.80%) pyperf_global.bpf.linked1.o on_event 7563 7530 -33 (-0.44%) 520 517 -3 (-0.58%) pyperf_subprogs.bpf.linked1.o on_event 36358 36934 +576 (+1.58%) 2499 2531 +32 (+1.28%) setget_sockopt.bpf.linked1.o skops_sockopt 3965 4038 +73 (+1.84%) 343 347 +4 (+1.17%) test_cls_redirect_subprogs.bpf.linked1.o cls_redirect 64965 64901 -64 (-0.10%) 4619 4612 -7 (-0.15%) test_misc_tcp_hdr_options.bpf.linked1.o misc_estab 1491 1307 -184 (-12.34%) 110 100 -10 (-9.09%) test_pkt_access.bpf.linked1.o test_pkt_access 354 349 -5 (-1.41%) 25 24 -1 (-4.00%) test_sock_fields.bpf.linked1.o egress_read_sock_fields 435 375 -60 (-13.79%) 22 20 -2 (-9.09%) test_sysctl_loop2.bpf.linked1.o sysctl_tcp_mem 1508 1501 -7 (-0.46%) 29 28 -1 (-3.45%) test_tc_dtime.bpf.linked1.o egress_fwdns_prio100 468 435 -33 (-7.05%) 45 41 -4 (-8.89%) test_tc_dtime.bpf.linked1.o ingress_fwdns_prio100 398 408 +10 (+2.51%) 42 39 -3 (-7.14%) test_tc_dtime.bpf.linked1.o ingress_fwdns_prio101 1096 842 -254 (-23.18%) 97 73 -24 (-24.74%) test_tcp_hdr_options.bpf.linked1.o estab 2758 2408 -350 (-12.69%) 208 181 -27 (-12.98%) test_urandom_usdt.bpf.linked1.o urand_read_with_sema 466 448 -18 (-3.86%) 31 28 -3 (-9.68%) test_urandom_usdt.bpf.linked1.o urand_read_without_sema 466 448 -18 (-3.86%) 31 28 -3 (-9.68%) test_urandom_usdt.bpf.linked1.o urandlib_read_with_sema 466 448 -18 (-3.86%) 31 28 -3 (-9.68%) test_urandom_usdt.bpf.linked1.o urandlib_read_without_sema 466 448 -18 (-3.86%) 31 28 -3 (-9.68%) test_xdp_noinline.bpf.linked1.o balancer_ingress_v6 4302 4294 -8 (-0.19%) 257 256 -1 (-0.39%) xdp_synproxy_kern.bpf.linked1.o syncookie_tc 583722 405757 -177965 (-30.49%) 35846 25735 -10111 (-28.21%) xdp_synproxy_kern.bpf.linked1.o syncookie_xdp 609123 479055 -130068 (-21.35%) 35452 29145 -6307 (-17.79%) ---------------------------------------- -------------------------- --------------- --------------- ------------------ ---------------- ---------------- ------------------- Signed-off-by: Andrii Nakryiko Link: https://lore.kernel.org/r/20221104163649.121784-4-andrii@kernel.org Signed-off-by: Alexei Starovoitov --- kernel/bpf/verifier.c | 62 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 61 insertions(+), 1 deletion(-) diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 5c708eb30664..c1169ee1bc7c 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -504,6 +504,15 @@ static bool is_dynptr_ref_function(enum bpf_func_id func_id) return func_id == BPF_FUNC_dynptr_data; } +static bool is_callback_calling_function(enum bpf_func_id func_id) +{ + return func_id == BPF_FUNC_for_each_map_elem || + func_id == BPF_FUNC_timer_set_callback || + func_id == BPF_FUNC_find_vma || + func_id == BPF_FUNC_loop || + func_id == BPF_FUNC_user_ringbuf_drain; +} + static bool helper_multiple_ref_obj_use(enum bpf_func_id func_id, const struct bpf_map *map) { @@ -1677,7 +1686,7 @@ static void __mark_reg_unknown(const struct bpf_verifier_env *env, reg->type = SCALAR_VALUE; reg->var_off = tnum_unknown; reg->frameno = 0; - reg->precise = env->subprog_cnt > 1 || !env->bpf_capable; + reg->precise = !env->bpf_capable; __mark_reg_unbounded(reg); } @@ -2646,6 +2655,11 @@ static int backtrack_insn(struct bpf_verifier_env *env, int idx, if (opcode == BPF_CALL) { if (insn->src_reg == BPF_PSEUDO_CALL) return -ENOTSUPP; + /* BPF helpers that invoke callback subprogs are + * equivalent to BPF_PSEUDO_CALL above + */ + if (insn->src_reg == 0 && is_callback_calling_function(insn->imm)) + return -ENOTSUPP; /* regular helper call sets R0 */ *reg_mask &= ~1; if (*reg_mask & 0x3f) { @@ -2809,12 +2823,42 @@ static int __mark_chain_precision(struct bpf_verifier_env *env, int frame, int r return 0; if (!reg_mask && !stack_mask) return 0; + for (;;) { DECLARE_BITMAP(mask, 64); u32 history = st->jmp_history_cnt; if (env->log.level & BPF_LOG_LEVEL2) verbose(env, "last_idx %d first_idx %d\n", last_idx, first_idx); + + if (last_idx < 0) { + /* we are at the entry into subprog, which + * is expected for global funcs, but only if + * requested precise registers are R1-R5 + * (which are global func's input arguments) + */ + if (st->curframe == 0 && + st->frame[0]->subprogno > 0 && + st->frame[0]->callsite == BPF_MAIN_FUNC && + stack_mask == 0 && (reg_mask & ~0x3e) == 0) { + bitmap_from_u64(mask, reg_mask); + for_each_set_bit(i, mask, 32) { + reg = &st->frame[0]->regs[i]; + if (reg->type != SCALAR_VALUE) { + reg_mask &= ~(1u << i); + continue; + } + reg->precise = true; + } + return 0; + } + + verbose(env, "BUG backtracing func entry subprog %d reg_mask %x stack_mask %llx\n", + st->frame[0]->subprogno, reg_mask, stack_mask); + WARN_ONCE(1, "verifier backtracking bug"); + return -EFAULT; + } + for (i = last_idx;;) { if (skip_first) { err = 0; @@ -6602,6 +6646,10 @@ typedef int (*set_callee_state_fn)(struct bpf_verifier_env *env, struct bpf_func_state *callee, int insn_idx); +static int set_callee_state(struct bpf_verifier_env *env, + struct bpf_func_state *caller, + struct bpf_func_state *callee, int insn_idx); + static int __check_func_call(struct bpf_verifier_env *env, struct bpf_insn *insn, int *insn_idx, int subprog, set_callee_state_fn set_callee_state_cb) @@ -6652,6 +6700,16 @@ static int __check_func_call(struct bpf_verifier_env *env, struct bpf_insn *insn } } + /* set_callee_state is used for direct subprog calls, but we are + * interested in validating only BPF helpers that can call subprogs as + * callbacks + */ + if (set_callee_state_cb != set_callee_state && !is_callback_calling_function(insn->imm)) { + verbose(env, "verifier bug: helper %s#%d is not marked as callback-calling\n", + func_id_name(insn->imm), insn->imm); + return -EFAULT; + } + if (insn->code == (BPF_JMP | BPF_CALL) && insn->src_reg == 0 && insn->imm == BPF_FUNC_timer_set_callback) { @@ -14571,6 +14629,8 @@ static int do_check_common(struct bpf_verifier_env *env, int subprog) BPF_MAIN_FUNC /* callsite */, 0 /* frameno */, subprog); + state->first_insn_idx = env->subprog_info[subprog].start; + state->last_insn_idx = -1; regs = state->frame[state->curframe]->regs; if (subprog || env->prog->type == BPF_PROG_TYPE_EXT) { From f63181b6ae79fd3b034cde641db774268c2c3acf Mon Sep 17 00:00:00 2001 From: Andrii Nakryiko Date: Fri, 4 Nov 2022 09:36:47 -0700 Subject: [PATCH 0640/2002] bpf: stop setting precise in current state Setting reg->precise to true in current state is not necessary from correctness standpoint, but it does pessimise the whole precision (or rather "imprecision", because that's what we want to keep as much as possible) tracking. Why is somewhat subtle and my best attempt to explain this is recorded in an extensive comment for __mark_chain_precise() function. Some more careful thinking and code reading is probably required still to grok this completely, unfortunately. Whiteboarding and a bunch of extra handwaiving in person would be even more helpful, but is deemed impractical in Git commit. Next patch pushes this imprecision property even further, building on top of the insights described in this patch. End results are pretty nice, we get reduction in number of total instructions and states verified due to a better states reuse, as some of the states are now more generic and permissive due to less unnecessary precise=true requirements. SELFTESTS RESULTS ================= $ ./veristat -C -e file,prog,insns,states ~/subprog-precise-results.csv ~/imprecise-early-results.csv | grep -v '+0' File Program Total insns (A) Total insns (B) Total insns (DIFF) Total states (A) Total states (B) Total states (DIFF) --------------------------------------- ---------------------- --------------- --------------- ------------------ ---------------- ---------------- ------------------- bpf_iter_ksym.bpf.linked1.o dump_ksym 347 285 -62 (-17.87%) 20 19 -1 (-5.00%) pyperf600_bpf_loop.bpf.linked1.o on_event 3678 3736 +58 (+1.58%) 276 285 +9 (+3.26%) setget_sockopt.bpf.linked1.o skops_sockopt 4038 3947 -91 (-2.25%) 347 343 -4 (-1.15%) test_l4lb.bpf.linked1.o balancer_ingress 4559 2611 -1948 (-42.73%) 118 105 -13 (-11.02%) test_l4lb_noinline.bpf.linked1.o balancer_ingress 6279 6268 -11 (-0.18%) 237 236 -1 (-0.42%) test_misc_tcp_hdr_options.bpf.linked1.o misc_estab 1307 1303 -4 (-0.31%) 100 99 -1 (-1.00%) test_sk_lookup.bpf.linked1.o ctx_narrow_access 456 447 -9 (-1.97%) 39 38 -1 (-2.56%) test_sysctl_loop1.bpf.linked1.o sysctl_tcp_mem 1389 1384 -5 (-0.36%) 26 25 -1 (-3.85%) test_tc_dtime.bpf.linked1.o egress_fwdns_prio101 518 485 -33 (-6.37%) 51 46 -5 (-9.80%) test_tc_dtime.bpf.linked1.o egress_host 519 468 -51 (-9.83%) 50 44 -6 (-12.00%) test_tc_dtime.bpf.linked1.o ingress_fwdns_prio101 842 1000 +158 (+18.76%) 73 88 +15 (+20.55%) xdp_synproxy_kern.bpf.linked1.o syncookie_tc 405757 373173 -32584 (-8.03%) 25735 22882 -2853 (-11.09%) xdp_synproxy_kern.bpf.linked1.o syncookie_xdp 479055 371590 -107465 (-22.43%) 29145 22207 -6938 (-23.81%) --------------------------------------- ---------------------- --------------- --------------- ------------------ ---------------- ---------------- ------------------- Slight regression in test_tc_dtime.bpf.linked1.o/ingress_fwdns_prio101 is left for a follow up, there might be some more precision-related bugs in existing BPF verifier logic. CILIUM RESULTS ============== $ ./veristat -C -e file,prog,insns,states ~/subprog-precise-results-cilium.csv ~/imprecise-early-results-cilium.csv | grep -v '+0' File Program Total insns (A) Total insns (B) Total insns (DIFF) Total states (A) Total states (B) Total states (DIFF) ------------- ------------------------------ --------------- --------------- ------------------ ---------------- ---------------- ------------------- bpf_host.o cil_from_host 762 556 -206 (-27.03%) 43 37 -6 (-13.95%) bpf_host.o tail_handle_nat_fwd_ipv4 23541 23426 -115 (-0.49%) 1538 1537 -1 (-0.07%) bpf_host.o tail_nodeport_nat_egress_ipv4 33592 33566 -26 (-0.08%) 2163 2161 -2 (-0.09%) bpf_lxc.o tail_handle_nat_fwd_ipv4 23541 23426 -115 (-0.49%) 1538 1537 -1 (-0.07%) bpf_overlay.o tail_nodeport_nat_egress_ipv4 33581 33543 -38 (-0.11%) 2160 2157 -3 (-0.14%) bpf_xdp.o tail_handle_nat_fwd_ipv4 21659 20920 -739 (-3.41%) 1440 1376 -64 (-4.44%) bpf_xdp.o tail_handle_nat_fwd_ipv6 17084 17039 -45 (-0.26%) 907 905 -2 (-0.22%) bpf_xdp.o tail_lb_ipv4 73442 73430 -12 (-0.02%) 4370 4369 -1 (-0.02%) bpf_xdp.o tail_lb_ipv6 152114 151895 -219 (-0.14%) 6493 6479 -14 (-0.22%) bpf_xdp.o tail_nodeport_nat_egress_ipv4 17377 17200 -177 (-1.02%) 1125 1111 -14 (-1.24%) bpf_xdp.o tail_nodeport_nat_ingress_ipv6 6405 6397 -8 (-0.12%) 309 308 -1 (-0.32%) bpf_xdp.o tail_rev_nodeport_lb4 7126 6934 -192 (-2.69%) 414 402 -12 (-2.90%) bpf_xdp.o tail_rev_nodeport_lb6 18059 17905 -154 (-0.85%) 1105 1096 -9 (-0.81%) ------------- ------------------------------ --------------- --------------- ------------------ ---------------- ---------------- ------------------- Signed-off-by: Andrii Nakryiko Link: https://lore.kernel.org/r/20221104163649.121784-5-andrii@kernel.org Signed-off-by: Alexei Starovoitov --- kernel/bpf/verifier.c | 103 +++++++++++++++++++++++++++++++++++++----- 1 file changed, 91 insertions(+), 12 deletions(-) diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index c1169ee1bc7c..ff3fc21ce99b 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -2749,8 +2749,11 @@ static void mark_all_scalars_precise(struct bpf_verifier_env *env, /* big hammer: mark all scalars precise in this path. * pop_stack may still get !precise scalars. + * We also skip current state and go straight to first parent state, + * because precision markings in current non-checkpointed state are + * not needed. See why in the comment in __mark_chain_precision below. */ - for (; st; st = st->parent) + for (st = st->parent; st; st = st->parent) { for (i = 0; i <= st->curframe; i++) { func = st->frame[i]; for (j = 0; j < BPF_REG_FP; j++) { @@ -2768,8 +2771,88 @@ static void mark_all_scalars_precise(struct bpf_verifier_env *env, reg->precise = true; } } + } } +/* + * __mark_chain_precision() backtracks BPF program instruction sequence and + * chain of verifier states making sure that register *regno* (if regno >= 0) + * and/or stack slot *spi* (if spi >= 0) are marked as precisely tracked + * SCALARS, as well as any other registers and slots that contribute to + * a tracked state of given registers/stack slots, depending on specific BPF + * assembly instructions (see backtrack_insns() for exact instruction handling + * logic). This backtracking relies on recorded jmp_history and is able to + * traverse entire chain of parent states. This process ends only when all the + * necessary registers/slots and their transitive dependencies are marked as + * precise. + * + * One important and subtle aspect is that precise marks *do not matter* in + * the currently verified state (current state). It is important to understand + * why this is the case. + * + * First, note that current state is the state that is not yet "checkpointed", + * i.e., it is not yet put into env->explored_states, and it has no children + * states as well. It's ephemeral, and can end up either a) being discarded if + * compatible explored state is found at some point or BPF_EXIT instruction is + * reached or b) checkpointed and put into env->explored_states, branching out + * into one or more children states. + * + * In the former case, precise markings in current state are completely + * ignored by state comparison code (see regsafe() for details). Only + * checkpointed ("old") state precise markings are important, and if old + * state's register/slot is precise, regsafe() assumes current state's + * register/slot as precise and checks value ranges exactly and precisely. If + * states turn out to be compatible, current state's necessary precise + * markings and any required parent states' precise markings are enforced + * after the fact with propagate_precision() logic, after the fact. But it's + * important to realize that in this case, even after marking current state + * registers/slots as precise, we immediately discard current state. So what + * actually matters is any of the precise markings propagated into current + * state's parent states, which are always checkpointed (due to b) case above). + * As such, for scenario a) it doesn't matter if current state has precise + * markings set or not. + * + * Now, for the scenario b), checkpointing and forking into child(ren) + * state(s). Note that before current state gets to checkpointing step, any + * processed instruction always assumes precise SCALAR register/slot + * knowledge: if precise value or range is useful to prune jump branch, BPF + * verifier takes this opportunity enthusiastically. Similarly, when + * register's value is used to calculate offset or memory address, exact + * knowledge of SCALAR range is assumed, checked, and enforced. So, similar to + * what we mentioned above about state comparison ignoring precise markings + * during state comparison, BPF verifier ignores and also assumes precise + * markings *at will* during instruction verification process. But as verifier + * assumes precision, it also propagates any precision dependencies across + * parent states, which are not yet finalized, so can be further restricted + * based on new knowledge gained from restrictions enforced by their children + * states. This is so that once those parent states are finalized, i.e., when + * they have no more active children state, state comparison logic in + * is_state_visited() would enforce strict and precise SCALAR ranges, if + * required for correctness. + * + * To build a bit more intuition, note also that once a state is checkpointed, + * the path we took to get to that state is not important. This is crucial + * property for state pruning. When state is checkpointed and finalized at + * some instruction index, it can be correctly and safely used to "short + * circuit" any *compatible* state that reaches exactly the same instruction + * index. I.e., if we jumped to that instruction from a completely different + * code path than original finalized state was derived from, it doesn't + * matter, current state can be discarded because from that instruction + * forward having a compatible state will ensure we will safely reach the + * exit. States describe preconditions for further exploration, but completely + * forget the history of how we got here. + * + * This also means that even if we needed precise SCALAR range to get to + * finalized state, but from that point forward *that same* SCALAR register is + * never used in a precise context (i.e., it's precise value is not needed for + * correctness), it's correct and safe to mark such register as "imprecise" + * (i.e., precise marking set to false). This is what we rely on when we do + * not set precise marking in current state. If no child state requires + * precision for any given SCALAR register, it's safe to dictate that it can + * be imprecise. If any child state does require this register to be precise, + * we'll mark it precise later retroactively during precise markings + * propagation from child state to parent states. + */ static int __mark_chain_precision(struct bpf_verifier_env *env, int frame, int regno, int spi) { @@ -2787,6 +2870,10 @@ static int __mark_chain_precision(struct bpf_verifier_env *env, int frame, int r if (!env->bpf_capable) return 0; + /* Do sanity checks against current state of register and/or stack + * slot, but don't set precise flag in current state, as precision + * tracking in the current state is unnecessary. + */ func = st->frame[frame]; if (regno >= 0) { reg = &func->regs[regno]; @@ -2794,11 +2881,7 @@ static int __mark_chain_precision(struct bpf_verifier_env *env, int frame, int r WARN_ONCE(1, "backtracing misuse"); return -EFAULT; } - if (!reg->precise) - new_marks = true; - else - reg_mask = 0; - reg->precise = true; + new_marks = true; } while (spi >= 0) { @@ -2811,11 +2894,7 @@ static int __mark_chain_precision(struct bpf_verifier_env *env, int frame, int r stack_mask = 0; break; } - if (!reg->precise) - new_marks = true; - else - stack_mask = 0; - reg->precise = true; + new_marks = true; break; } @@ -11534,7 +11613,7 @@ static bool regsafe(struct bpf_verifier_env *env, struct bpf_reg_state *rold, if (env->explore_alu_limits) return false; if (rcur->type == SCALAR_VALUE) { - if (!rold->precise && !rcur->precise) + if (!rold->precise) return true; /* new val must satisfy old val knowledge */ return range_within(rold, rcur) && From 7a830b53c17bbadcf99f778f28aaaa4e6c41df5f Mon Sep 17 00:00:00 2001 From: Andrii Nakryiko Date: Fri, 4 Nov 2022 09:36:48 -0700 Subject: [PATCH 0641/2002] bpf: aggressively forget precise markings during state checkpointing Exploit the property of about-to-be-checkpointed state to be able to forget all precise markings up to that point even more aggressively. We now clear all potentially inherited precise markings right before checkpointing and branching off into child state. If any of children states require precise knowledge of any SCALAR register, those will be propagated backwards later on before this state is finalized, preserving correctness. There is a single selftests BPF program change, but tremendous one: 25x reduction in number of verified instructions and states in trace_virtqueue_add_sgs. Cilium results are more modest, but happen across wider range of programs. SELFTESTS RESULTS ================= $ ./veristat -C -e file,prog,insns,states ~/imprecise-early-results.csv ~/imprecise-aggressive-results.csv | grep -v '+0' File Program Total insns (A) Total insns (B) Total insns (DIFF) Total states (A) Total states (B) Total states (DIFF) ------------------- ----------------------- --------------- --------------- ------------------ ---------------- ---------------- ------------------- loop6.bpf.linked1.o trace_virtqueue_add_sgs 398057 15114 -382943 (-96.20%) 8717 336 -8381 (-96.15%) ------------------- ----------------------- --------------- --------------- ------------------ ---------------- ---------------- ------------------- CILIUM RESULTS ============== $ ./veristat -C -e file,prog,insns,states ~/imprecise-early-results-cilium.csv ~/imprecise-aggressive-results-cilium.csv | grep -v '+0' File Program Total insns (A) Total insns (B) Total insns (DIFF) Total states (A) Total states (B) Total states (DIFF) ------------- -------------------------------- --------------- --------------- ------------------ ---------------- ---------------- ------------------- bpf_host.o tail_handle_nat_fwd_ipv4 23426 23221 -205 (-0.88%) 1537 1515 -22 (-1.43%) bpf_host.o tail_handle_nat_fwd_ipv6 13009 12904 -105 (-0.81%) 719 708 -11 (-1.53%) bpf_host.o tail_nodeport_nat_ingress_ipv6 5261 5196 -65 (-1.24%) 247 243 -4 (-1.62%) bpf_host.o tail_nodeport_nat_ipv6_egress 3446 3406 -40 (-1.16%) 203 198 -5 (-2.46%) bpf_lxc.o tail_handle_nat_fwd_ipv4 23426 23221 -205 (-0.88%) 1537 1515 -22 (-1.43%) bpf_lxc.o tail_handle_nat_fwd_ipv6 13009 12904 -105 (-0.81%) 719 708 -11 (-1.53%) bpf_lxc.o tail_ipv4_ct_egress 5074 4897 -177 (-3.49%) 255 248 -7 (-2.75%) bpf_lxc.o tail_ipv4_ct_ingress 5100 4923 -177 (-3.47%) 255 248 -7 (-2.75%) bpf_lxc.o tail_ipv4_ct_ingress_policy_only 5100 4923 -177 (-3.47%) 255 248 -7 (-2.75%) bpf_lxc.o tail_ipv6_ct_egress 4558 4536 -22 (-0.48%) 188 187 -1 (-0.53%) bpf_lxc.o tail_ipv6_ct_ingress 4578 4556 -22 (-0.48%) 188 187 -1 (-0.53%) bpf_lxc.o tail_ipv6_ct_ingress_policy_only 4578 4556 -22 (-0.48%) 188 187 -1 (-0.53%) bpf_lxc.o tail_nodeport_nat_ingress_ipv6 5261 5196 -65 (-1.24%) 247 243 -4 (-1.62%) bpf_overlay.o tail_nodeport_nat_ingress_ipv6 5261 5196 -65 (-1.24%) 247 243 -4 (-1.62%) bpf_overlay.o tail_nodeport_nat_ipv6_egress 3482 3442 -40 (-1.15%) 204 201 -3 (-1.47%) bpf_xdp.o tail_nodeport_nat_egress_ipv4 17200 15619 -1581 (-9.19%) 1111 1010 -101 (-9.09%) ------------- -------------------------------- --------------- --------------- ------------------ ---------------- ---------------- ------------------- Signed-off-by: Andrii Nakryiko Link: https://lore.kernel.org/r/20221104163649.121784-6-andrii@kernel.org Signed-off-by: Alexei Starovoitov --- kernel/bpf/verifier.c | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index ff3fc21ce99b..d3b75aa0c54d 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -2774,6 +2774,31 @@ static void mark_all_scalars_precise(struct bpf_verifier_env *env, } } +static void mark_all_scalars_imprecise(struct bpf_verifier_env *env, struct bpf_verifier_state *st) +{ + struct bpf_func_state *func; + struct bpf_reg_state *reg; + int i, j; + + for (i = 0; i <= st->curframe; i++) { + func = st->frame[i]; + for (j = 0; j < BPF_REG_FP; j++) { + reg = &func->regs[j]; + if (reg->type != SCALAR_VALUE) + continue; + reg->precise = false; + } + for (j = 0; j < func->allocated_stack / BPF_REG_SIZE; j++) { + if (!is_spilled_reg(&func->stack[j])) + continue; + reg = &func->stack[j].spilled_ptr; + if (reg->type != SCALAR_VALUE) + continue; + reg->precise = false; + } + } +} + /* * __mark_chain_precision() backtracks BPF program instruction sequence and * chain of verifier states making sure that register *regno* (if regno >= 0) @@ -2852,6 +2877,14 @@ static void mark_all_scalars_precise(struct bpf_verifier_env *env, * be imprecise. If any child state does require this register to be precise, * we'll mark it precise later retroactively during precise markings * propagation from child state to parent states. + * + * Skipping precise marking setting in current state is a mild version of + * relying on the above observation. But we can utilize this property even + * more aggressively by proactively forgetting any precise marking in the + * current state (which we inherited from the parent state), right before we + * checkpoint it and branch off into new child state. This is done by + * mark_all_scalars_imprecise() to hopefully get more permissive and generic + * finalized states which help in short circuiting more future states. */ static int __mark_chain_precision(struct bpf_verifier_env *env, int frame, int regno, int spi) @@ -12160,6 +12193,10 @@ next: env->prev_jmps_processed = env->jmps_processed; env->prev_insn_processed = env->insn_processed; + /* forget precise markings we inherited, see __mark_chain_precision */ + if (env->bpf_capable) + mark_all_scalars_imprecise(env, cur); + /* add new state to the head of linked list */ new = &new_sl->state; err = copy_verifier_state(new, cur); From 4f999b767769b76378c3616c624afd6f4bb0d99f Mon Sep 17 00:00:00 2001 From: Andrii Nakryiko Date: Fri, 4 Nov 2022 09:36:49 -0700 Subject: [PATCH 0642/2002] selftests/bpf: make test_align selftest more robust test_align selftest relies on BPF verifier log emitting register states for specific instructions in expected format. Unfortunately, BPF verifier precision backtracking log interferes with such expectations. And instruction on which precision propagation happens sometimes don't output full expected register states. This does indeed look like something to be improved in BPF verifier, but is beyond the scope of this patch set. So to make test_align a bit more robust, inject few dummy R4 = R5 instructions which capture desired state of R5 and won't have precision tracking logs on them. This fixes tests until we can improve BPF verifier output in the presence of precision tracking. Signed-off-by: Andrii Nakryiko Link: https://lore.kernel.org/r/20221104163649.121784-7-andrii@kernel.org Signed-off-by: Alexei Starovoitov --- .../testing/selftests/bpf/prog_tests/align.c | 38 ++++++++++++------- 1 file changed, 24 insertions(+), 14 deletions(-) diff --git a/tools/testing/selftests/bpf/prog_tests/align.c b/tools/testing/selftests/bpf/prog_tests/align.c index 970f09156eb4..4666f88f2bb4 100644 --- a/tools/testing/selftests/bpf/prog_tests/align.c +++ b/tools/testing/selftests/bpf/prog_tests/align.c @@ -2,7 +2,7 @@ #include #define MAX_INSNS 512 -#define MAX_MATCHES 16 +#define MAX_MATCHES 24 struct bpf_reg_match { unsigned int line; @@ -267,6 +267,7 @@ static struct bpf_align_test tests[] = { */ BPF_MOV64_REG(BPF_REG_5, BPF_REG_2), BPF_ALU64_REG(BPF_ADD, BPF_REG_5, BPF_REG_6), + BPF_MOV64_REG(BPF_REG_4, BPF_REG_5), BPF_ALU64_IMM(BPF_ADD, BPF_REG_5, 14), BPF_MOV64_REG(BPF_REG_4, BPF_REG_5), BPF_ALU64_IMM(BPF_ADD, BPF_REG_4, 4), @@ -280,6 +281,7 @@ static struct bpf_align_test tests[] = { BPF_MOV64_REG(BPF_REG_5, BPF_REG_2), BPF_ALU64_IMM(BPF_ADD, BPF_REG_5, 14), BPF_ALU64_REG(BPF_ADD, BPF_REG_5, BPF_REG_6), + BPF_MOV64_REG(BPF_REG_4, BPF_REG_5), BPF_ALU64_IMM(BPF_ADD, BPF_REG_5, 4), BPF_ALU64_REG(BPF_ADD, BPF_REG_5, BPF_REG_6), BPF_MOV64_REG(BPF_REG_4, BPF_REG_5), @@ -311,44 +313,52 @@ static struct bpf_align_test tests[] = { {15, "R4=pkt(id=1,off=18,r=18,umax=1020,var_off=(0x0; 0x3fc))"}, {15, "R5=pkt(id=1,off=14,r=18,umax=1020,var_off=(0x0; 0x3fc))"}, /* Variable offset is added to R5 packet pointer, - * resulting in auxiliary alignment of 4. + * resulting in auxiliary alignment of 4. To avoid BPF + * verifier's precision backtracking logging + * interfering we also have a no-op R4 = R5 + * instruction to validate R5 state. We also check + * that R4 is what it should be in such case. */ - {17, "R5_w=pkt(id=2,off=0,r=0,umax=1020,var_off=(0x0; 0x3fc))"}, + {18, "R4_w=pkt(id=2,off=0,r=0,umax=1020,var_off=(0x0; 0x3fc))"}, + {18, "R5_w=pkt(id=2,off=0,r=0,umax=1020,var_off=(0x0; 0x3fc))"}, /* Constant offset is added to R5, resulting in * reg->off of 14. */ - {18, "R5_w=pkt(id=2,off=14,r=0,umax=1020,var_off=(0x0; 0x3fc))"}, + {19, "R5_w=pkt(id=2,off=14,r=0,umax=1020,var_off=(0x0; 0x3fc))"}, /* At the time the word size load is performed from R5, * its total fixed offset is NET_IP_ALIGN + reg->off * (14) which is 16. Then the variable offset is 4-byte * aligned, so the total offset is 4-byte aligned and * meets the load's requirements. */ - {23, "R4=pkt(id=2,off=18,r=18,umax=1020,var_off=(0x0; 0x3fc))"}, - {23, "R5=pkt(id=2,off=14,r=18,umax=1020,var_off=(0x0; 0x3fc))"}, + {24, "R4=pkt(id=2,off=18,r=18,umax=1020,var_off=(0x0; 0x3fc))"}, + {24, "R5=pkt(id=2,off=14,r=18,umax=1020,var_off=(0x0; 0x3fc))"}, /* Constant offset is added to R5 packet pointer, * resulting in reg->off value of 14. */ - {25, "R5_w=pkt(off=14,r=8"}, + {26, "R5_w=pkt(off=14,r=8"}, /* Variable offset is added to R5, resulting in a - * variable offset of (4n). + * variable offset of (4n). See comment for insn #18 + * for R4 = R5 trick. */ - {26, "R5_w=pkt(id=3,off=14,r=0,umax=1020,var_off=(0x0; 0x3fc))"}, + {28, "R4_w=pkt(id=3,off=14,r=0,umax=1020,var_off=(0x0; 0x3fc))"}, + {28, "R5_w=pkt(id=3,off=14,r=0,umax=1020,var_off=(0x0; 0x3fc))"}, /* Constant is added to R5 again, setting reg->off to 18. */ - {27, "R5_w=pkt(id=3,off=18,r=0,umax=1020,var_off=(0x0; 0x3fc))"}, + {29, "R5_w=pkt(id=3,off=18,r=0,umax=1020,var_off=(0x0; 0x3fc))"}, /* And once more we add a variable; resulting var_off * is still (4n), fixed offset is not changed. * Also, we create a new reg->id. */ - {28, "R5_w=pkt(id=4,off=18,r=0,umax=2040,var_off=(0x0; 0x7fc)"}, + {31, "R4_w=pkt(id=4,off=18,r=0,umax=2040,var_off=(0x0; 0x7fc)"}, + {31, "R5_w=pkt(id=4,off=18,r=0,umax=2040,var_off=(0x0; 0x7fc)"}, /* At the time the word size load is performed from R5, * its total fixed offset is NET_IP_ALIGN + reg->off (18) * which is 20. Then the variable offset is (4n), so * the total offset is 4-byte aligned and meets the * load's requirements. */ - {33, "R4=pkt(id=4,off=22,r=22,umax=2040,var_off=(0x0; 0x7fc)"}, - {33, "R5=pkt(id=4,off=18,r=22,umax=2040,var_off=(0x0; 0x7fc)"}, + {35, "R4=pkt(id=4,off=22,r=22,umax=2040,var_off=(0x0; 0x7fc)"}, + {35, "R5=pkt(id=4,off=18,r=22,umax=2040,var_off=(0x0; 0x7fc)"}, }, }, { @@ -681,6 +691,6 @@ void test_align(void) if (!test__start_subtest(test->descr)) continue; - CHECK_FAIL(do_test_single(test)); + ASSERT_OK(do_test_single(test), test->descr); } } From de048b6ee86597c6079a5ed3a1cca996089a83d3 Mon Sep 17 00:00:00 2001 From: Eduard Zingerman Date: Wed, 2 Nov 2022 01:54:12 +0200 Subject: [PATCH 0643/2002] libbpf: Resolve enum fwd as full enum64 and vice versa Changes de-duplication logic for enums in the following way: - update btf_hash_enum to ignore size and kind fields to get ENUM and ENUM64 types in a same hash bucket; - update btf_compat_enum to consider enum fwd to be compatible with full enum64 (and vice versa); This allows BTF de-duplication in the following case: // CU #1 enum foo; struct s { enum foo *a; } *x; // CU #2 enum foo { x = 0xfffffffff // big enough to force enum64 }; struct s { enum foo *a; } *y; De-duplicated BTF prior to this commit: [1] ENUM64 'foo' encoding=UNSIGNED size=8 vlen=1 'x' val=68719476735ULL [2] INT 'long unsigned int' size=8 bits_offset=0 nr_bits=64 encoding=(none) [3] STRUCT 's' size=8 vlen=1 'a' type_id=4 bits_offset=0 [4] PTR '(anon)' type_id=1 [5] PTR '(anon)' type_id=3 [6] STRUCT 's' size=8 vlen=1 'a' type_id=8 bits_offset=0 [7] ENUM 'foo' encoding=UNSIGNED size=4 vlen=0 [8] PTR '(anon)' type_id=7 [9] PTR '(anon)' type_id=6 De-duplicated BTF after this commit: [1] ENUM64 'foo' encoding=UNSIGNED size=8 vlen=1 'x' val=68719476735ULL [2] INT 'long unsigned int' size=8 bits_offset=0 nr_bits=64 encoding=(none) [3] STRUCT 's' size=8 vlen=1 'a' type_id=4 bits_offset=0 [4] PTR '(anon)' type_id=1 [5] PTR '(anon)' type_id=3 Enum forward declarations in C do not provide information about enumeration values range. Thus the `btf_type->size` field is meaningless for forward enum declarations. In fact, GCC does not encode size in DWARF for forward enum declarations (but dwarves sets enumeration size to a default value of `sizeof(int) * 8` when size is not specified see dwarf_loader.c:die__create_new_enumeration). Signed-off-by: Eduard Zingerman Signed-off-by: Andrii Nakryiko Link: https://lore.kernel.org/bpf/20221101235413.1824260-1-eddyz87@gmail.com --- tools/lib/bpf/btf.c | 75 +++++++++++++++------------------------------ 1 file changed, 25 insertions(+), 50 deletions(-) diff --git a/tools/lib/bpf/btf.c b/tools/lib/bpf/btf.c index 675a0df5c840..71d68bf7788c 100644 --- a/tools/lib/bpf/btf.c +++ b/tools/lib/bpf/btf.c @@ -3404,23 +3404,17 @@ static long btf_hash_enum(struct btf_type *t) { long h; - /* don't hash vlen and enum members to support enum fwd resolving */ + /* don't hash vlen, enum members and size to support enum fwd resolving */ h = hash_combine(0, t->name_off); - h = hash_combine(h, t->info & ~0xffff); - h = hash_combine(h, t->size); return h; } -/* Check structural equality of two ENUMs. */ -static bool btf_equal_enum(struct btf_type *t1, struct btf_type *t2) +static bool btf_equal_enum_members(struct btf_type *t1, struct btf_type *t2) { const struct btf_enum *m1, *m2; __u16 vlen; int i; - if (!btf_equal_common(t1, t2)) - return false; - vlen = btf_vlen(t1); m1 = btf_enum(t1); m2 = btf_enum(t2); @@ -3433,15 +3427,12 @@ static bool btf_equal_enum(struct btf_type *t1, struct btf_type *t2) return true; } -static bool btf_equal_enum64(struct btf_type *t1, struct btf_type *t2) +static bool btf_equal_enum64_members(struct btf_type *t1, struct btf_type *t2) { const struct btf_enum64 *m1, *m2; __u16 vlen; int i; - if (!btf_equal_common(t1, t2)) - return false; - vlen = btf_vlen(t1); m1 = btf_enum64(t1); m2 = btf_enum64(t2); @@ -3455,6 +3446,19 @@ static bool btf_equal_enum64(struct btf_type *t1, struct btf_type *t2) return true; } +/* Check structural equality of two ENUMs or ENUM64s. */ +static bool btf_equal_enum(struct btf_type *t1, struct btf_type *t2) +{ + if (!btf_equal_common(t1, t2)) + return false; + + /* t1 & t2 kinds are identical because of btf_equal_common */ + if (btf_kind(t1) == BTF_KIND_ENUM) + return btf_equal_enum_members(t1, t2); + else + return btf_equal_enum64_members(t1, t2); +} + static inline bool btf_is_enum_fwd(struct btf_type *t) { return btf_is_any_enum(t) && btf_vlen(t) == 0; @@ -3464,21 +3468,14 @@ static bool btf_compat_enum(struct btf_type *t1, struct btf_type *t2) { if (!btf_is_enum_fwd(t1) && !btf_is_enum_fwd(t2)) return btf_equal_enum(t1, t2); - /* ignore vlen when comparing */ + /* At this point either t1 or t2 or both are forward declarations, thus: + * - skip comparing vlen because it is zero for forward declarations; + * - skip comparing size to allow enum forward declarations + * to be compatible with enum64 full declarations; + * - skip comparing kind for the same reason. + */ return t1->name_off == t2->name_off && - (t1->info & ~0xffff) == (t2->info & ~0xffff) && - t1->size == t2->size; -} - -static bool btf_compat_enum64(struct btf_type *t1, struct btf_type *t2) -{ - if (!btf_is_enum_fwd(t1) && !btf_is_enum_fwd(t2)) - return btf_equal_enum64(t1, t2); - - /* ignore vlen when comparing */ - return t1->name_off == t2->name_off && - (t1->info & ~0xffff) == (t2->info & ~0xffff) && - t1->size == t2->size; + btf_is_any_enum(t1) && btf_is_any_enum(t2); } /* @@ -3763,6 +3760,7 @@ static int btf_dedup_prim_type(struct btf_dedup *d, __u32 type_id) break; case BTF_KIND_ENUM: + case BTF_KIND_ENUM64: h = btf_hash_enum(t); for_each_dedup_cand(d, hash_entry, h) { cand_id = (__u32)(long)hash_entry->value; @@ -3783,27 +3781,6 @@ static int btf_dedup_prim_type(struct btf_dedup *d, __u32 type_id) } break; - case BTF_KIND_ENUM64: - h = btf_hash_enum(t); - for_each_dedup_cand(d, hash_entry, h) { - cand_id = (__u32)(long)hash_entry->value; - cand = btf_type_by_id(d->btf, cand_id); - if (btf_equal_enum64(t, cand)) { - new_id = cand_id; - break; - } - if (btf_compat_enum64(t, cand)) { - if (btf_is_enum_fwd(t)) { - /* resolve fwd to full enum */ - new_id = cand_id; - break; - } - /* resolve canonical enum fwd to full enum */ - d->map[cand_id] = type_id; - } - } - break; - case BTF_KIND_FWD: case BTF_KIND_FLOAT: h = btf_hash_common(t); @@ -4099,10 +4076,8 @@ static int btf_dedup_is_equiv(struct btf_dedup *d, __u32 cand_id, return btf_equal_int_tag(cand_type, canon_type); case BTF_KIND_ENUM: - return btf_compat_enum(cand_type, canon_type); - case BTF_KIND_ENUM64: - return btf_compat_enum64(cand_type, canon_type); + return btf_compat_enum(cand_type, canon_type); case BTF_KIND_FWD: case BTF_KIND_FLOAT: From 2e20f50ff84903964bcfca10ecdab0fa08cd6a4c Mon Sep 17 00:00:00 2001 From: Eduard Zingerman Date: Wed, 2 Nov 2022 01:54:13 +0200 Subject: [PATCH 0644/2002] selftests/bpf: Tests for enum fwd resolved as full enum64 A set of test cases to verify enum fwd resolution logic: - verify that enum fwd can be resolved as full enum64; - verify that enum64 fwd can be resolved as full enum; - verify that enum size is considered when enums are compared for equivalence. Signed-off-by: Eduard Zingerman Signed-off-by: Andrii Nakryiko Link: https://lore.kernel.org/bpf/20221101235413.1824260-2-eddyz87@gmail.com --- tools/testing/selftests/bpf/prog_tests/btf.c | 88 ++++++++++++++++++-- 1 file changed, 83 insertions(+), 5 deletions(-) diff --git a/tools/testing/selftests/bpf/prog_tests/btf.c b/tools/testing/selftests/bpf/prog_tests/btf.c index 24dd6214394e..c3e1cea9abae 100644 --- a/tools/testing/selftests/bpf/prog_tests/btf.c +++ b/tools/testing/selftests/bpf/prog_tests/btf.c @@ -7133,7 +7133,7 @@ static struct btf_dedup_test dedup_tests[] = { BTF_ENUM_ENC(NAME_NTH(4), 456), /* [4] fwd enum 'e2' after full enum */ BTF_TYPE_ENC(NAME_NTH(3), BTF_INFO_ENC(BTF_KIND_ENUM, 0, 0), 4), - /* [5] incompatible fwd enum with different size */ + /* [5] fwd enum with different size, size does not matter for fwd */ BTF_TYPE_ENC(NAME_NTH(1), BTF_INFO_ENC(BTF_KIND_ENUM, 0, 0), 1), /* [6] incompatible full enum with different value */ BTF_TYPE_ENC(NAME_NTH(1), BTF_INFO_ENC(BTF_KIND_ENUM, 0, 1), 4), @@ -7150,9 +7150,7 @@ static struct btf_dedup_test dedup_tests[] = { /* [2] full enum 'e2' */ BTF_TYPE_ENC(NAME_NTH(3), BTF_INFO_ENC(BTF_KIND_ENUM, 0, 1), 4), BTF_ENUM_ENC(NAME_NTH(4), 456), - /* [3] incompatible fwd enum with different size */ - BTF_TYPE_ENC(NAME_NTH(1), BTF_INFO_ENC(BTF_KIND_ENUM, 0, 0), 1), - /* [4] incompatible full enum with different value */ + /* [3] incompatible full enum with different value */ BTF_TYPE_ENC(NAME_NTH(1), BTF_INFO_ENC(BTF_KIND_ENUM, 0, 1), 4), BTF_ENUM_ENC(NAME_NTH(2), 321), BTF_END_RAW, @@ -7611,7 +7609,87 @@ static struct btf_dedup_test dedup_tests[] = { BTF_STR_SEC("\0e1\0e1_val"), }, }, - +{ + .descr = "dedup: enum of different size: no dedup", + .input = { + .raw_types = { + /* [1] enum 'e1' */ + BTF_TYPE_ENC(NAME_NTH(1), BTF_INFO_ENC(BTF_KIND_ENUM, 0, 1), 4), + BTF_ENUM_ENC(NAME_NTH(2), 1), + /* [2] enum 'e1' */ + BTF_TYPE_ENC(NAME_NTH(1), BTF_INFO_ENC(BTF_KIND_ENUM, 0, 1), 2), + BTF_ENUM_ENC(NAME_NTH(2), 1), + BTF_END_RAW, + }, + BTF_STR_SEC("\0e1\0e1_val"), + }, + .expect = { + .raw_types = { + /* [1] enum 'e1' */ + BTF_TYPE_ENC(NAME_NTH(1), BTF_INFO_ENC(BTF_KIND_ENUM, 0, 1), 4), + BTF_ENUM_ENC(NAME_NTH(2), 1), + /* [2] enum 'e1' */ + BTF_TYPE_ENC(NAME_NTH(1), BTF_INFO_ENC(BTF_KIND_ENUM, 0, 1), 2), + BTF_ENUM_ENC(NAME_NTH(2), 1), + BTF_END_RAW, + }, + BTF_STR_SEC("\0e1\0e1_val"), + }, +}, +{ + .descr = "dedup: enum fwd to enum64", + .input = { + .raw_types = { + /* [1] enum64 'e1' */ + BTF_TYPE_ENC(NAME_NTH(1), BTF_INFO_ENC(BTF_KIND_ENUM64, 0, 1), 8), + BTF_ENUM64_ENC(NAME_NTH(2), 1, 0), + /* [2] enum 'e1' fwd */ + BTF_TYPE_ENC(NAME_NTH(1), BTF_INFO_ENC(BTF_KIND_ENUM, 0, 0), 4), + /* [3] typedef enum 'e1' td */ + BTF_TYPE_ENC(NAME_NTH(3), BTF_INFO_ENC(BTF_KIND_TYPEDEF, 0, 0), 2), + BTF_END_RAW, + }, + BTF_STR_SEC("\0e1\0e1_val\0td"), + }, + .expect = { + .raw_types = { + /* [1] enum64 'e1' */ + BTF_TYPE_ENC(NAME_NTH(1), BTF_INFO_ENC(BTF_KIND_ENUM64, 0, 1), 8), + BTF_ENUM64_ENC(NAME_NTH(2), 1, 0), + /* [2] typedef enum 'e1' td */ + BTF_TYPE_ENC(NAME_NTH(3), BTF_INFO_ENC(BTF_KIND_TYPEDEF, 0, 0), 1), + BTF_END_RAW, + }, + BTF_STR_SEC("\0e1\0e1_val\0td"), + }, +}, +{ + .descr = "dedup: enum64 fwd to enum", + .input = { + .raw_types = { + /* [1] enum 'e1' */ + BTF_TYPE_ENC(NAME_NTH(1), BTF_INFO_ENC(BTF_KIND_ENUM, 0, 1), 4), + BTF_ENUM_ENC(NAME_NTH(2), 1), + /* [2] enum64 'e1' fwd */ + BTF_TYPE_ENC(NAME_NTH(1), BTF_INFO_ENC(BTF_KIND_ENUM64, 0, 0), 8), + /* [3] typedef enum 'e1' td */ + BTF_TYPE_ENC(NAME_NTH(3), BTF_INFO_ENC(BTF_KIND_TYPEDEF, 0, 0), 2), + BTF_END_RAW, + }, + BTF_STR_SEC("\0e1\0e1_val\0td"), + }, + .expect = { + .raw_types = { + /* [1] enum 'e1' */ + BTF_TYPE_ENC(NAME_NTH(1), BTF_INFO_ENC(BTF_KIND_ENUM, 0, 1), 4), + BTF_ENUM_ENC(NAME_NTH(2), 1), + /* [2] typedef enum 'e1' td */ + BTF_TYPE_ENC(NAME_NTH(3), BTF_INFO_ENC(BTF_KIND_TYPEDEF, 0, 0), 1), + BTF_END_RAW, + }, + BTF_STR_SEC("\0e1\0e1_val\0td"), + }, +}, }; static int btf_type_size(const struct btf_type *t) From 1baa7e38002111aee62b489ac343960ae75ce2e9 Mon Sep 17 00:00:00 2001 From: Rong Tao Date: Sat, 29 Oct 2022 17:11:13 +0800 Subject: [PATCH 0645/2002] samples/bpf: Fix tracex2 error: No such file or directory since commit c504e5c2f964("net: skb: introduce kfree_skb_reason()") kfree_skb() is replaced by kfree_skb_reason() and kfree_skb() is set to the inline function. So, we replace kprobe/kfree_skb with kprobe/kfree_skb_reason to solve the tracex2 error. $ cd samples/bpf $ sudo ./tracex2 libbpf: prog 'bpf_prog2': failed to create kprobe 'kfree_skb+0x0' perf event: No such file or directory ERROR: bpf_program__attach failed Signed-off-by: Rong Tao Signed-off-by: Andrii Nakryiko Link: https://lore.kernel.org/bpf/tencent_0F0DAE84C0B3C42E0B550E5E9F47A9114D09@qq.com --- samples/bpf/tracex2_kern.c | 4 ++-- samples/bpf/tracex2_user.c | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/samples/bpf/tracex2_kern.c b/samples/bpf/tracex2_kern.c index 5bc696bac27d..93e0b7680b4f 100644 --- a/samples/bpf/tracex2_kern.c +++ b/samples/bpf/tracex2_kern.c @@ -22,14 +22,14 @@ struct { /* kprobe is NOT a stable ABI. If kernel internals change this bpf+kprobe * example will no longer be meaningful */ -SEC("kprobe/kfree_skb") +SEC("kprobe/kfree_skb_reason") int bpf_prog2(struct pt_regs *ctx) { long loc = 0; long init_val = 1; long *value; - /* read ip of kfree_skb caller. + /* read ip of kfree_skb_reason caller. * non-portable version of __builtin_return_address(0) */ BPF_KPROBE_READ_RET_IP(loc, ctx); diff --git a/samples/bpf/tracex2_user.c b/samples/bpf/tracex2_user.c index dd6205c6b6a7..089e408abd7a 100644 --- a/samples/bpf/tracex2_user.c +++ b/samples/bpf/tracex2_user.c @@ -146,7 +146,8 @@ int main(int ac, char **argv) signal(SIGINT, int_exit); signal(SIGTERM, int_exit); - /* start 'ping' in the background to have some kfree_skb events */ + /* start 'ping' in the background to have some kfree_skb_reason + * events */ f = popen("ping -4 -c5 localhost", "r"); (void) f; From b3c09fdca1135167bd0682128cd51864c5aa5625 Mon Sep 17 00:00:00 2001 From: Rong Tao Date: Fri, 4 Nov 2022 09:27:54 +0800 Subject: [PATCH 0646/2002] selftests/bpf: cgroup_helpers.c: Fix strncpy() fortify warning MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Copy libbpf_strlcpy() from libbpf_internal.h to bpf_util.h, and rename it to bpf_strlcpy(), then replace selftests strncpy()/libbpf_strlcpy() with bpf_strlcpy(), fix compile warning. The libbpf_internal.h header cannot be used directly here, because references to cgroup_helpers.c in samples/bpf will generate compilation errors. We also can't add libbpf_strlcpy() directly to bpf_util.h, because the definition of libbpf_strlcpy() in libbpf_internal.h is duplicated. In order not to modify the libbpf code, add a new function bpf_strlcpy() to selftests bpf_util.h. How to reproduce this compilation warning: $ make -C samples/bpf cgroup_helpers.c: In function ‘__enable_controllers’: cgroup_helpers.c:80:17: warning: ‘strncpy’ specified bound 4097 equals destination size [-Wstringop-truncation] 80 | strncpy(enable, controllers, sizeof(enable)); | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Signed-off-by: Rong Tao Signed-off-by: Andrii Nakryiko Link: https://lore.kernel.org/bpf/tencent_469D8AF32BD56816A29981BED06E96D22506@qq.com --- tools/testing/selftests/bpf/bpf_util.h | 19 ++++++++++++++ tools/testing/selftests/bpf/cgroup_helpers.c | 3 ++- tools/testing/selftests/bpf/xsk.c | 26 +++----------------- 3 files changed, 25 insertions(+), 23 deletions(-) diff --git a/tools/testing/selftests/bpf/bpf_util.h b/tools/testing/selftests/bpf/bpf_util.h index a3352a64c067..10587a29b967 100644 --- a/tools/testing/selftests/bpf/bpf_util.h +++ b/tools/testing/selftests/bpf/bpf_util.h @@ -20,6 +20,25 @@ static inline unsigned int bpf_num_possible_cpus(void) return possible_cpus; } +/* Copy up to sz - 1 bytes from zero-terminated src string and ensure that dst + * is zero-terminated string no matter what (unless sz == 0, in which case + * it's a no-op). It's conceptually close to FreeBSD's strlcpy(), but differs + * in what is returned. Given this is internal helper, it's trivial to extend + * this, when necessary. Use this instead of strncpy inside libbpf source code. + */ +static inline void bpf_strlcpy(char *dst, const char *src, size_t sz) +{ + size_t i; + + if (sz == 0) + return; + + sz--; + for (i = 0; i < sz && src[i]; i++) + dst[i] = src[i]; + dst[i] = '\0'; +} + #define __bpf_percpu_val_align __attribute__((__aligned__(8))) #define BPF_DECLARE_PERCPU(type, name) \ diff --git a/tools/testing/selftests/bpf/cgroup_helpers.c b/tools/testing/selftests/bpf/cgroup_helpers.c index e914cc45b766..dd1aa5afcf5a 100644 --- a/tools/testing/selftests/bpf/cgroup_helpers.c +++ b/tools/testing/selftests/bpf/cgroup_helpers.c @@ -13,6 +13,7 @@ #include #include "cgroup_helpers.h" +#include "bpf_util.h" /* * To avoid relying on the system setup, when setup_cgroup_env is called @@ -77,7 +78,7 @@ static int __enable_controllers(const char *cgroup_path, const char *controllers enable[len] = 0; close(fd); } else { - strncpy(enable, controllers, sizeof(enable)); + bpf_strlcpy(enable, controllers, sizeof(enable)); } snprintf(path, sizeof(path), "%s/cgroup.subtree_control", cgroup_path); diff --git a/tools/testing/selftests/bpf/xsk.c b/tools/testing/selftests/bpf/xsk.c index 0b3ff49c740d..39d349509ba4 100644 --- a/tools/testing/selftests/bpf/xsk.c +++ b/tools/testing/selftests/bpf/xsk.c @@ -33,6 +33,7 @@ #include #include #include "xsk.h" +#include "bpf_util.h" #ifndef SOL_XDP #define SOL_XDP 283 @@ -521,25 +522,6 @@ static int xsk_create_bpf_link(struct xsk_socket *xsk) return 0; } -/* Copy up to sz - 1 bytes from zero-terminated src string and ensure that dst - * is zero-terminated string no matter what (unless sz == 0, in which case - * it's a no-op). It's conceptually close to FreeBSD's strlcpy(), but differs - * in what is returned. Given this is internal helper, it's trivial to extend - * this, when necessary. Use this instead of strncpy inside libbpf source code. - */ -static inline void libbpf_strlcpy(char *dst, const char *src, size_t sz) -{ - size_t i; - - if (sz == 0) - return; - - sz--; - for (i = 0; i < sz && src[i]; i++) - dst[i] = src[i]; - dst[i] = '\0'; -} - static int xsk_get_max_queues(struct xsk_socket *xsk) { struct ethtool_channels channels = { .cmd = ETHTOOL_GCHANNELS }; @@ -552,7 +534,7 @@ static int xsk_get_max_queues(struct xsk_socket *xsk) return -errno; ifr.ifr_data = (void *)&channels; - libbpf_strlcpy(ifr.ifr_name, ctx->ifname, IFNAMSIZ); + bpf_strlcpy(ifr.ifr_name, ctx->ifname, IFNAMSIZ); err = ioctl(fd, SIOCETHTOOL, &ifr); if (err && errno != EOPNOTSUPP) { ret = -errno; @@ -771,7 +753,7 @@ static int xsk_create_xsk_struct(int ifindex, struct xsk_socket *xsk) } ctx->ifindex = ifindex; - libbpf_strlcpy(ctx->ifname, ifname, IFNAMSIZ); + bpf_strlcpy(ctx->ifname, ifname, IFNAMSIZ); xsk->ctx = ctx; xsk->ctx->has_bpf_link = xsk_probe_bpf_link(); @@ -958,7 +940,7 @@ static struct xsk_ctx *xsk_create_ctx(struct xsk_socket *xsk, ctx->refcount = 1; ctx->umem = umem; ctx->queue_id = queue_id; - libbpf_strlcpy(ctx->ifname, ifname, IFNAMSIZ); + bpf_strlcpy(ctx->ifname, ifname, IFNAMSIZ); ctx->fill = fill; ctx->comp = comp; From 61fc5e66f755db24d27ba37ce1ee4873def1a074 Mon Sep 17 00:00:00 2001 From: Artem Savkov Date: Fri, 4 Nov 2022 10:40:16 +0100 Subject: [PATCH 0647/2002] selftests/bpf: Use consistent build-id type for liburandom_read.so lld produces "fast" style build-ids by default, which is inconsistent with ld's "sha1" style. Explicitly specify build-id style to be "sha1" when linking liburandom_read.so the same way it is already done for urandom_read. Signed-off-by: Artem Savkov Signed-off-by: Andrii Nakryiko Acked-by: KP Singh Link: https://lore.kernel.org/bpf/20221104094016.102049-1-asavkov@redhat.com --- tools/testing/selftests/bpf/Makefile | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/tools/testing/selftests/bpf/Makefile b/tools/testing/selftests/bpf/Makefile index 79edef1dbda4..f3cd17026ee5 100644 --- a/tools/testing/selftests/bpf/Makefile +++ b/tools/testing/selftests/bpf/Makefile @@ -182,14 +182,15 @@ endif $(OUTPUT)/liburandom_read.so: urandom_read_lib1.c urandom_read_lib2.c $(call msg,LIB,,$@) $(Q)$(CLANG) $(filter-out -static,$(CFLAGS) $(LDFLAGS)) $^ $(LDLIBS) \ - -fuse-ld=$(LLD) -Wl,-znoseparate-code -fPIC -shared -o $@ + -fuse-ld=$(LLD) -Wl,-znoseparate-code -Wl,--build-id=sha1 \ + -fPIC -shared -o $@ $(OUTPUT)/urandom_read: urandom_read.c urandom_read_aux.c $(OUTPUT)/liburandom_read.so $(call msg,BINARY,,$@) $(Q)$(CLANG) $(filter-out -static,$(CFLAGS) $(LDFLAGS)) $(filter %.c,$^) \ liburandom_read.so $(LDLIBS) \ - -fuse-ld=$(LLD) -Wl,-znoseparate-code \ - -Wl,-rpath=. -Wl,--build-id=sha1 -o $@ + -fuse-ld=$(LLD) -Wl,-znoseparate-code -Wl,--build-id=sha1 \ + -Wl,-rpath=. -o $@ $(OUTPUT)/sign-file: ../../../../scripts/sign-file.c $(call msg,SIGN-FILE,,$@) From 25906092edb4bcf94cb669bd1ed03a0ef2f4120c Mon Sep 17 00:00:00 2001 From: Bagas Sanjaya Date: Fri, 4 Nov 2022 19:39:14 +0700 Subject: [PATCH 0648/2002] Documentation: bpf: Escape underscore in BPF type name prefix Sphinx reported unknown target warning: Documentation/bpf/bpf_design_QA.rst:329: WARNING: Unknown target name: "bpf". The warning is caused by BPF type name prefix ("bpf_") which is written without escaping the trailing underscore. Escape the underscore to fix the warning. While at it, wrap the containing paragraph in less than 80 characters. Fixes: 9805af8d8a5b17 ("bpf: Document UAPI details for special BPF types") Signed-off-by: Bagas Sanjaya Signed-off-by: Andrii Nakryiko Acked-by: KP Singh Acked-by: David Vernet Link: https://lore.kernel.org/bpf/20221104123913.50610-1-bagasdotme@gmail.com --- Documentation/bpf/bpf_design_QA.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Documentation/bpf/bpf_design_QA.rst b/Documentation/bpf/bpf_design_QA.rst index 4e4af398607b..17e774d96c5e 100644 --- a/Documentation/bpf/bpf_design_QA.rst +++ b/Documentation/bpf/bpf_design_QA.rst @@ -326,11 +326,11 @@ size, type, and alignment, or any other user visible API or ABI detail across kernel releases. The users must adapt their BPF programs to the new changes and update them to make sure their programs continue to work correctly. -NOTE: BPF subsystem specially reserves the 'bpf_' prefix for type names, in +NOTE: BPF subsystem specially reserves the 'bpf\_' prefix for type names, in order to introduce more special fields in the future. Hence, user programs must -avoid defining types with 'bpf_' prefix to not be broken in future releases. In -other words, no backwards compatibility is guaranteed if one using a type in BTF -with 'bpf_' prefix. +avoid defining types with 'bpf\_' prefix to not be broken in future releases. +In other words, no backwards compatibility is guaranteed if one using a type +in BTF with 'bpf\_' prefix. Q: What is the compatibility story for special BPF types in local kptrs? ------------------------------------------------------------------------ From 552acbf576fb7cb7ec70e978ca148dfbdae12a0e Mon Sep 17 00:00:00 2001 From: Zhengchao Shao Date: Fri, 4 Nov 2022 10:25:13 +0800 Subject: [PATCH 0649/2002] net: remove redundant check in ip_metrics_convert() Now ip_metrics_convert() is only called by ip_fib_metrics_init(). Before ip_fib_metrics_init() invokes ip_metrics_convert(), it checks whether input parameter fc_mx is NULL. Therefore, ip_metrics_convert() doesn't need to check fc_mx. Signed-off-by: Zhengchao Shao Reviewed-by: David Ahern Link: https://lore.kernel.org/r/20221104022513.168868-1-shaozhengchao@huawei.com Signed-off-by: Jakub Kicinski --- net/ipv4/metrics.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/net/ipv4/metrics.c b/net/ipv4/metrics.c index 25ea6ac44db9..7fcfdfd8f9de 100644 --- a/net/ipv4/metrics.c +++ b/net/ipv4/metrics.c @@ -14,9 +14,6 @@ static int ip_metrics_convert(struct net *net, struct nlattr *fc_mx, struct nlattr *nla; int remaining; - if (!fc_mx) - return 0; - nla_for_each_attr(nla, fc_mx, fc_mx_len, remaining) { int type = nla_type(nla); u32 val; From 8e0aa1ff44ca30b0b9df95dfa72003522d9a088d Mon Sep 17 00:00:00 2001 From: Nathan Chancellor Date: Thu, 3 Nov 2022 15:00:32 -0700 Subject: [PATCH 0650/2002] net: ethernet: renesas: Fix return type of rswitch_start_xmit() With clang's kernel control flow integrity (kCFI, CONFIG_CFI_CLANG), indirect call targets are validated against the expected function pointer prototype to make sure the call target is valid to help mitigate ROP attacks. If they are not identical, there is a failure at run time, which manifests as either a kernel panic or thread getting killed. A proposed warning in clang aims to catch these at compile time, which reveals: drivers/net/ethernet/renesas/rswitch.c:1533:20: error: incompatible function pointer types initializing 'netdev_tx_t (*)(struct sk_buff *, struct net_device *)' (aka 'enum netdev_tx (*)(struct sk_buff *, struct net_device *)') with an expression of type 'int (struct sk_buff *, struct net_device *)' [-Werror,-Wincompatible-function-pointer-types-strict] .ndo_start_xmit = rswitch_start_xmit, ^~~~~~~~~~~~~~~~~~ 1 error generated. ->ndo_start_xmit() in 'struct net_device_ops' expects a return type of 'netdev_tx_t', not 'int'. Adjust the return type of rswitch_start_xmit() to match the prototype's to resolve the warning and CFI failure. Link: https://github.com/ClangBuiltLinux/linux/issues/1750 Signed-off-by: Nathan Chancellor Reviewed-by: Yoshihiro Shimoda Reviewed-by: Kees Cook Link: https://lore.kernel.org/r/20221103220032.2142122-1-nathan@kernel.org Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/renesas/rswitch.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/renesas/rswitch.c b/drivers/net/ethernet/renesas/rswitch.c index 20df2020d3e5..f0168fedfef9 100644 --- a/drivers/net/ethernet/renesas/rswitch.c +++ b/drivers/net/ethernet/renesas/rswitch.c @@ -1390,7 +1390,7 @@ static int rswitch_stop(struct net_device *ndev) return 0; }; -static int rswitch_start_xmit(struct sk_buff *skb, struct net_device *ndev) +static netdev_tx_t rswitch_start_xmit(struct sk_buff *skb, struct net_device *ndev) { struct rswitch_device *rdev = netdev_priv(ndev); struct rswitch_gwca_queue *gq = rdev->tx_queue; From f0b59ad11e29fa45beab7a14370f530f90aa764c Mon Sep 17 00:00:00 2001 From: Edward Cree Date: Thu, 3 Nov 2022 15:27:27 +0000 Subject: [PATCH 0651/2002] sfc: check recirc_id match caps before MAE offload Offloaded TC rules always match on recirc_id in the MAE, so we should check that the MAE reported support for this match before attempting to insert the rule. These checks allow us to fail early, avoiding the PCIe round-trip to firmware for an MC_CMD_MAE_ACTION_RULE_INSERT that will only fail, and more importantly providing a more informative error message that identifies which match field is unsupported. Signed-off-by: Edward Cree Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/sfc/mae.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/drivers/net/ethernet/sfc/mae.c b/drivers/net/ethernet/sfc/mae.c index 6f472ea0638a..cd014957a236 100644 --- a/drivers/net/ethernet/sfc/mae.c +++ b/drivers/net/ethernet/sfc/mae.c @@ -250,6 +250,20 @@ static int efx_mae_match_check_cap_typ(u8 support, enum mask_type typ) } } +/* Validate field mask against hardware capabilities. Captures caller's 'rc' */ +#define CHECK(_mcdi, _field) ({ \ + enum mask_type typ = classify_mask((const u8 *)&mask->_field, \ + sizeof(mask->_field)); \ + \ + rc = efx_mae_match_check_cap_typ(supported_fields[MAE_FIELD_ ## _mcdi],\ + typ); \ + if (rc) \ + NL_SET_ERR_MSG_FMT_MOD(extack, \ + "No support for %s mask in field %s", \ + mask_type_name(typ), #_field); \ + rc; \ +}) + int efx_mae_match_check_caps(struct efx_nic *efx, const struct efx_tc_match_fields *mask, struct netlink_ext_ack *extack) @@ -269,8 +283,11 @@ int efx_mae_match_check_caps(struct efx_nic *efx, mask_type_name(ingress_port_mask_type)); return rc; } + if (CHECK(RECIRC_ID, recirc_id)) + return rc; return 0; } +#undef CHECK static bool efx_mae_asl_id(u32 id) { From 6d1c604d1098d5c1d355950989945941fa3b315e Mon Sep 17 00:00:00 2001 From: Edward Cree Date: Thu, 3 Nov 2022 15:27:28 +0000 Subject: [PATCH 0652/2002] sfc: add Layer 2 matches to ef100 TC offload Support matching on EtherType, VLANs and ethernet source/destination addresses, with masking if supported by the hardware. Signed-off-by: Edward Cree Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/sfc/mae.c | 37 +++++++++++++++++++- drivers/net/ethernet/sfc/mcdi.h | 6 ++++ drivers/net/ethernet/sfc/tc.c | 62 ++++++++++++++++++++++++++++++--- drivers/net/ethernet/sfc/tc.h | 4 +++ 4 files changed, 103 insertions(+), 6 deletions(-) diff --git a/drivers/net/ethernet/sfc/mae.c b/drivers/net/ethernet/sfc/mae.c index cd014957a236..6dfcf87f9659 100644 --- a/drivers/net/ethernet/sfc/mae.c +++ b/drivers/net/ethernet/sfc/mae.c @@ -283,7 +283,14 @@ int efx_mae_match_check_caps(struct efx_nic *efx, mask_type_name(ingress_port_mask_type)); return rc; } - if (CHECK(RECIRC_ID, recirc_id)) + if (CHECK(ETHER_TYPE, eth_proto) || + CHECK(VLAN0_TCI, vlan_tci[0]) || + CHECK(VLAN0_PROTO, vlan_proto[0]) || + CHECK(VLAN1_TCI, vlan_tci[1]) || + CHECK(VLAN1_PROTO, vlan_proto[1]) || + CHECK(ETH_SADDR, eth_saddr) || + CHECK(ETH_DADDR, eth_daddr) || + CHECK(RECIRC_ID, recirc_id)) return rc; return 0; } @@ -460,6 +467,34 @@ static int efx_mae_populate_match_criteria(MCDI_DECLARE_STRUCT_PTR(match_crit), match->value.recirc_id); MCDI_STRUCT_SET_BYTE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_RECIRC_ID_MASK, match->mask.recirc_id); + MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_ETHER_TYPE_BE, + match->value.eth_proto); + MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_ETHER_TYPE_BE_MASK, + match->mask.eth_proto); + MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_VLAN0_TCI_BE, + match->value.vlan_tci[0]); + MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_VLAN0_TCI_BE_MASK, + match->mask.vlan_tci[0]); + MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_VLAN0_PROTO_BE, + match->value.vlan_proto[0]); + MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_VLAN0_PROTO_BE_MASK, + match->mask.vlan_proto[0]); + MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_VLAN1_TCI_BE, + match->value.vlan_tci[1]); + MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_VLAN1_TCI_BE_MASK, + match->mask.vlan_tci[1]); + MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_VLAN1_PROTO_BE, + match->value.vlan_proto[1]); + MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_VLAN1_PROTO_BE_MASK, + match->mask.vlan_proto[1]); + memcpy(MCDI_STRUCT_PTR(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_ETH_SADDR_BE), + match->value.eth_saddr, ETH_ALEN); + memcpy(MCDI_STRUCT_PTR(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_ETH_SADDR_BE_MASK), + match->mask.eth_saddr, ETH_ALEN); + memcpy(MCDI_STRUCT_PTR(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_ETH_DADDR_BE), + match->value.eth_daddr, ETH_ALEN); + memcpy(MCDI_STRUCT_PTR(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_ETH_DADDR_BE_MASK), + match->mask.eth_daddr, ETH_ALEN); return 0; } diff --git a/drivers/net/ethernet/sfc/mcdi.h b/drivers/net/ethernet/sfc/mcdi.h index 1f18e9dc62e8..883a4db695e2 100644 --- a/drivers/net/ethernet/sfc/mcdi.h +++ b/drivers/net/ethernet/sfc/mcdi.h @@ -224,6 +224,12 @@ void efx_mcdi_sensor_event(struct efx_nic *efx, efx_qword_t *ev); #define MCDI_WORD(_buf, _field) \ ((u16)BUILD_BUG_ON_ZERO(MC_CMD_ ## _field ## _LEN != 2) + \ le16_to_cpu(*(__force const __le16 *)MCDI_PTR(_buf, _field))) +/* Write a 16-bit field defined in the protocol as being big-endian. */ +#define MCDI_STRUCT_SET_WORD_BE(_buf, _field, _value) do { \ + BUILD_BUG_ON(_field ## _LEN != 2); \ + BUILD_BUG_ON(_field ## _OFST & 1); \ + *(__force __be16 *)MCDI_STRUCT_PTR(_buf, _field) = (_value); \ + } while (0) #define MCDI_SET_DWORD(_buf, _field, _value) \ EFX_POPULATE_DWORD_1(*_MCDI_DWORD(_buf, _field), EFX_DWORD_0, _value) #define MCDI_STRUCT_SET_DWORD(_buf, _field, _value) \ diff --git a/drivers/net/ethernet/sfc/tc.c b/drivers/net/ethernet/sfc/tc.c index b21a961eabb1..b469a1263211 100644 --- a/drivers/net/ethernet/sfc/tc.c +++ b/drivers/net/ethernet/sfc/tc.c @@ -124,6 +124,20 @@ static void efx_tc_flow_free(void *ptr, void *arg) kfree(rule); } +/* Boilerplate for the simple 'copy a field' cases */ +#define _MAP_KEY_AND_MASK(_name, _type, _tcget, _tcfield, _field) \ +if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_##_name)) { \ + struct flow_match_##_type fm; \ + \ + flow_rule_match_##_tcget(rule, &fm); \ + match->value._field = fm.key->_tcfield; \ + match->mask._field = fm.mask->_tcfield; \ +} +#define MAP_KEY_AND_MASK(_name, _type, _tcfield, _field) \ + _MAP_KEY_AND_MASK(_name, _type, _type, _tcfield, _field) +#define MAP_ENC_KEY_AND_MASK(_name, _type, _tcget, _tcfield, _field) \ + _MAP_KEY_AND_MASK(ENC_##_name, _type, _tcget, _tcfield, _field) + static int efx_tc_flower_parse_match(struct efx_nic *efx, struct flow_rule *rule, struct efx_tc_match *match, @@ -144,26 +158,64 @@ static int efx_tc_flower_parse_match(struct efx_nic *efx, } if (dissector->used_keys & ~(BIT(FLOW_DISSECTOR_KEY_CONTROL) | - BIT(FLOW_DISSECTOR_KEY_BASIC))) { + BIT(FLOW_DISSECTOR_KEY_BASIC) | + BIT(FLOW_DISSECTOR_KEY_ETH_ADDRS) | + BIT(FLOW_DISSECTOR_KEY_VLAN) | + BIT(FLOW_DISSECTOR_KEY_CVLAN))) { NL_SET_ERR_MSG_FMT_MOD(extack, "Unsupported flower keys %#x", dissector->used_keys); return -EOPNOTSUPP; } + MAP_KEY_AND_MASK(BASIC, basic, n_proto, eth_proto); if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_BASIC)) { struct flow_match_basic fm; flow_rule_match_basic(rule, &fm); - if (fm.mask->n_proto) { - NL_SET_ERR_MSG_MOD(extack, "Unsupported eth_proto match"); - return -EOPNOTSUPP; - } if (fm.mask->ip_proto) { NL_SET_ERR_MSG_MOD(extack, "Unsupported ip_proto match"); return -EOPNOTSUPP; } } + if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_VLAN)) { + struct flow_match_vlan fm; + + flow_rule_match_vlan(rule, &fm); + if (fm.mask->vlan_id || fm.mask->vlan_priority || fm.mask->vlan_tpid) { + match->value.vlan_proto[0] = fm.key->vlan_tpid; + match->mask.vlan_proto[0] = fm.mask->vlan_tpid; + match->value.vlan_tci[0] = cpu_to_be16(fm.key->vlan_priority << 13 | + fm.key->vlan_id); + match->mask.vlan_tci[0] = cpu_to_be16(fm.mask->vlan_priority << 13 | + fm.mask->vlan_id); + } + } + + if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_CVLAN)) { + struct flow_match_vlan fm; + + flow_rule_match_cvlan(rule, &fm); + if (fm.mask->vlan_id || fm.mask->vlan_priority || fm.mask->vlan_tpid) { + match->value.vlan_proto[1] = fm.key->vlan_tpid; + match->mask.vlan_proto[1] = fm.mask->vlan_tpid; + match->value.vlan_tci[1] = cpu_to_be16(fm.key->vlan_priority << 13 | + fm.key->vlan_id); + match->mask.vlan_tci[1] = cpu_to_be16(fm.mask->vlan_priority << 13 | + fm.mask->vlan_id); + } + } + + if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ETH_ADDRS)) { + struct flow_match_eth_addrs fm; + + flow_rule_match_eth_addrs(rule, &fm); + ether_addr_copy(match->value.eth_saddr, fm.key->src); + ether_addr_copy(match->value.eth_daddr, fm.key->dst); + ether_addr_copy(match->mask.eth_saddr, fm.mask->src); + ether_addr_copy(match->mask.eth_daddr, fm.mask->dst); + } + return 0; } diff --git a/drivers/net/ethernet/sfc/tc.h b/drivers/net/ethernet/sfc/tc.h index 4373c3243e3c..272efbabd6be 100644 --- a/drivers/net/ethernet/sfc/tc.h +++ b/drivers/net/ethernet/sfc/tc.h @@ -26,6 +26,10 @@ struct efx_tc_match_fields { /* L1 */ u32 ingress_port; u8 recirc_id; + /* L2 (inner when encap) */ + __be16 eth_proto; + __be16 vlan_tci[2], vlan_proto[2]; + u8 eth_saddr[ETH_ALEN], eth_daddr[ETH_ALEN]; }; struct efx_tc_match { From c178dff3f92d4416b43f3a376174357549a0d347 Mon Sep 17 00:00:00 2001 From: Edward Cree Date: Thu, 3 Nov 2022 15:27:29 +0000 Subject: [PATCH 0653/2002] sfc: add Layer 3 matches to ef100 TC offload Support matching on IP protocol, Type of Service, Time To Live, source and destination addresses, with masking if supported by the hardware. Signed-off-by: Edward Cree Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/sfc/mae.c | 39 +++++++++++++++++++++++ drivers/net/ethernet/sfc/mcdi.h | 6 ++++ drivers/net/ethernet/sfc/tc.c | 56 +++++++++++++++++++++++++++------ drivers/net/ethernet/sfc/tc.h | 8 +++++ 4 files changed, 100 insertions(+), 9 deletions(-) diff --git a/drivers/net/ethernet/sfc/mae.c b/drivers/net/ethernet/sfc/mae.c index 6dfcf87f9659..b894fc658867 100644 --- a/drivers/net/ethernet/sfc/mae.c +++ b/drivers/net/ethernet/sfc/mae.c @@ -290,6 +290,15 @@ int efx_mae_match_check_caps(struct efx_nic *efx, CHECK(VLAN1_PROTO, vlan_proto[1]) || CHECK(ETH_SADDR, eth_saddr) || CHECK(ETH_DADDR, eth_daddr) || + CHECK(IP_PROTO, ip_proto) || + CHECK(IP_TOS, ip_tos) || + CHECK(IP_TTL, ip_ttl) || + CHECK(SRC_IP4, src_ip) || + CHECK(DST_IP4, dst_ip) || +#ifdef CONFIG_IPV6 + CHECK(SRC_IP6, src_ip6) || + CHECK(DST_IP6, dst_ip6) || +#endif CHECK(RECIRC_ID, recirc_id)) return rc; return 0; @@ -495,6 +504,36 @@ static int efx_mae_populate_match_criteria(MCDI_DECLARE_STRUCT_PTR(match_crit), match->value.eth_daddr, ETH_ALEN); memcpy(MCDI_STRUCT_PTR(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_ETH_DADDR_BE_MASK), match->mask.eth_daddr, ETH_ALEN); + MCDI_STRUCT_SET_BYTE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_IP_PROTO, + match->value.ip_proto); + MCDI_STRUCT_SET_BYTE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_IP_PROTO_MASK, + match->mask.ip_proto); + MCDI_STRUCT_SET_BYTE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_IP_TOS, + match->value.ip_tos); + MCDI_STRUCT_SET_BYTE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_IP_TOS_MASK, + match->mask.ip_tos); + MCDI_STRUCT_SET_BYTE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_IP_TTL, + match->value.ip_ttl); + MCDI_STRUCT_SET_BYTE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_IP_TTL_MASK, + match->mask.ip_ttl); + MCDI_STRUCT_SET_DWORD_BE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_SRC_IP4_BE, + match->value.src_ip); + MCDI_STRUCT_SET_DWORD_BE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_SRC_IP4_BE_MASK, + match->mask.src_ip); + MCDI_STRUCT_SET_DWORD_BE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_DST_IP4_BE, + match->value.dst_ip); + MCDI_STRUCT_SET_DWORD_BE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_DST_IP4_BE_MASK, + match->mask.dst_ip); +#ifdef CONFIG_IPV6 + memcpy(MCDI_STRUCT_PTR(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_SRC_IP6_BE), + &match->value.src_ip6, sizeof(struct in6_addr)); + memcpy(MCDI_STRUCT_PTR(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_SRC_IP6_BE_MASK), + &match->mask.src_ip6, sizeof(struct in6_addr)); + memcpy(MCDI_STRUCT_PTR(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_DST_IP6_BE), + &match->value.dst_ip6, sizeof(struct in6_addr)); + memcpy(MCDI_STRUCT_PTR(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_DST_IP6_BE_MASK), + &match->mask.dst_ip6, sizeof(struct in6_addr)); +#endif return 0; } diff --git a/drivers/net/ethernet/sfc/mcdi.h b/drivers/net/ethernet/sfc/mcdi.h index 883a4db695e2..fbeb58104936 100644 --- a/drivers/net/ethernet/sfc/mcdi.h +++ b/drivers/net/ethernet/sfc/mcdi.h @@ -236,6 +236,12 @@ void efx_mcdi_sensor_event(struct efx_nic *efx, efx_qword_t *ev); EFX_POPULATE_DWORD_1(*_MCDI_STRUCT_DWORD(_buf, _field), EFX_DWORD_0, _value) #define MCDI_DWORD(_buf, _field) \ EFX_DWORD_FIELD(*_MCDI_DWORD(_buf, _field), EFX_DWORD_0) +/* Write a 32-bit field defined in the protocol as being big-endian. */ +#define MCDI_STRUCT_SET_DWORD_BE(_buf, _field, _value) do { \ + BUILD_BUG_ON(_field ## _LEN != 4); \ + BUILD_BUG_ON(_field ## _OFST & 3); \ + *(__force __be32 *)MCDI_STRUCT_PTR(_buf, _field) = (_value); \ + } while (0) #define MCDI_POPULATE_DWORD_1(_buf, _field, _name1, _value1) \ EFX_POPULATE_DWORD_1(*_MCDI_DWORD(_buf, _field), \ MC_CMD_ ## _name1, _value1) diff --git a/drivers/net/ethernet/sfc/tc.c b/drivers/net/ethernet/sfc/tc.c index b469a1263211..d992fafc844e 100644 --- a/drivers/net/ethernet/sfc/tc.c +++ b/drivers/net/ethernet/sfc/tc.c @@ -144,11 +144,29 @@ static int efx_tc_flower_parse_match(struct efx_nic *efx, struct netlink_ext_ack *extack) { struct flow_dissector *dissector = rule->match.dissector; + unsigned char ipv = 0; + /* Owing to internal TC infelicities, the IPV6_ADDRS key might be set + * even on IPv4 filters; so rather than relying on dissector->used_keys + * we check the addr_type in the CONTROL key. If we don't find it (or + * it's masked, which should never happen), we treat both IPV4_ADDRS + * and IPV6_ADDRS as absent. + */ if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_CONTROL)) { struct flow_match_control fm; flow_rule_match_control(rule, &fm); + if (IS_ALL_ONES(fm.mask->addr_type)) + switch (fm.key->addr_type) { + case FLOW_DISSECTOR_KEY_IPV4_ADDRS: + ipv = 4; + break; + case FLOW_DISSECTOR_KEY_IPV6_ADDRS: + ipv = 6; + break; + default: + break; + } if (fm.mask->flags) { NL_SET_ERR_MSG_FMT_MOD(extack, "Unsupported match on control.flags %#x", @@ -161,22 +179,28 @@ static int efx_tc_flower_parse_match(struct efx_nic *efx, BIT(FLOW_DISSECTOR_KEY_BASIC) | BIT(FLOW_DISSECTOR_KEY_ETH_ADDRS) | BIT(FLOW_DISSECTOR_KEY_VLAN) | - BIT(FLOW_DISSECTOR_KEY_CVLAN))) { + BIT(FLOW_DISSECTOR_KEY_CVLAN) | + BIT(FLOW_DISSECTOR_KEY_IPV4_ADDRS) | + BIT(FLOW_DISSECTOR_KEY_IPV6_ADDRS) | + BIT(FLOW_DISSECTOR_KEY_IP))) { NL_SET_ERR_MSG_FMT_MOD(extack, "Unsupported flower keys %#x", dissector->used_keys); return -EOPNOTSUPP; } MAP_KEY_AND_MASK(BASIC, basic, n_proto, eth_proto); - if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_BASIC)) { - struct flow_match_basic fm; - - flow_rule_match_basic(rule, &fm); - if (fm.mask->ip_proto) { - NL_SET_ERR_MSG_MOD(extack, "Unsupported ip_proto match"); - return -EOPNOTSUPP; + /* Make sure we're IP if any L3/L4 keys used. */ + if (!IS_ALL_ONES(match->mask.eth_proto) || + !(match->value.eth_proto == htons(ETH_P_IP) || + match->value.eth_proto == htons(ETH_P_IPV6))) + if (dissector->used_keys & + (BIT(FLOW_DISSECTOR_KEY_IPV4_ADDRS) | + BIT(FLOW_DISSECTOR_KEY_IPV6_ADDRS) | + BIT(FLOW_DISSECTOR_KEY_IP))) { + NL_SET_ERR_MSG_FMT_MOD(extack, "L3 flower keys %#x require protocol ipv[46]", + dissector->used_keys); + return -EINVAL; } - } if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_VLAN)) { struct flow_match_vlan fm; @@ -216,6 +240,20 @@ static int efx_tc_flower_parse_match(struct efx_nic *efx, ether_addr_copy(match->mask.eth_daddr, fm.mask->dst); } + MAP_KEY_AND_MASK(BASIC, basic, ip_proto, ip_proto); + MAP_KEY_AND_MASK(IP, ip, tos, ip_tos); + MAP_KEY_AND_MASK(IP, ip, ttl, ip_ttl); + if (ipv == 4) { + MAP_KEY_AND_MASK(IPV4_ADDRS, ipv4_addrs, src, src_ip); + MAP_KEY_AND_MASK(IPV4_ADDRS, ipv4_addrs, dst, dst_ip); + } +#ifdef CONFIG_IPV6 + else if (ipv == 6) { + MAP_KEY_AND_MASK(IPV6_ADDRS, ipv6_addrs, src, src_ip6); + MAP_KEY_AND_MASK(IPV6_ADDRS, ipv6_addrs, dst, dst_ip6); + } +#endif + return 0; } diff --git a/drivers/net/ethernet/sfc/tc.h b/drivers/net/ethernet/sfc/tc.h index 272efbabd6be..aebe9c251b2c 100644 --- a/drivers/net/ethernet/sfc/tc.h +++ b/drivers/net/ethernet/sfc/tc.h @@ -15,6 +15,8 @@ #include #include "net_driver.h" +#define IS_ALL_ONES(v) (!(typeof (v))~(v)) + struct efx_tc_action_set { u16 deliver:1; u32 dest_mport; @@ -30,6 +32,12 @@ struct efx_tc_match_fields { __be16 eth_proto; __be16 vlan_tci[2], vlan_proto[2]; u8 eth_saddr[ETH_ALEN], eth_daddr[ETH_ALEN]; + /* L3 (when IP) */ + u8 ip_proto, ip_tos, ip_ttl; + __be32 src_ip, dst_ip; +#ifdef CONFIG_IPV6 + struct in6_addr src_ip6, dst_ip6; +#endif }; struct efx_tc_match { From 5ca7ef293866f034c9a21a567a498dadfd1d57a8 Mon Sep 17 00:00:00 2001 From: Edward Cree Date: Thu, 3 Nov 2022 15:27:30 +0000 Subject: [PATCH 0654/2002] sfc: add Layer 3 flag matches to ef100 TC offload Support matching on ip_frag and ip_firstfrag. Signed-off-by: Edward Cree Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/sfc/mae.c | 25 +++++++++++++++++++++++++ drivers/net/ethernet/sfc/tc.c | 10 +++++++++- drivers/net/ethernet/sfc/tc.h | 1 + 3 files changed, 35 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/sfc/mae.c b/drivers/net/ethernet/sfc/mae.c index b894fc658867..e24436ba699c 100644 --- a/drivers/net/ethernet/sfc/mae.c +++ b/drivers/net/ethernet/sfc/mae.c @@ -263,6 +263,18 @@ static int efx_mae_match_check_cap_typ(u8 support, enum mask_type typ) mask_type_name(typ), #_field); \ rc; \ }) +/* Booleans need special handling */ +#define CHECK_BIT(_mcdi, _field) ({ \ + enum mask_type typ = mask->_field ? MASK_ONES : MASK_ZEROES; \ + \ + rc = efx_mae_match_check_cap_typ(supported_fields[MAE_FIELD_ ## _mcdi],\ + typ); \ + if (rc) \ + NL_SET_ERR_MSG_FMT_MOD(extack, \ + "No support for %s mask in field %s", \ + mask_type_name(typ), #_field); \ + rc; \ +}) int efx_mae_match_check_caps(struct efx_nic *efx, const struct efx_tc_match_fields *mask, @@ -299,10 +311,13 @@ int efx_mae_match_check_caps(struct efx_nic *efx, CHECK(SRC_IP6, src_ip6) || CHECK(DST_IP6, dst_ip6) || #endif + CHECK_BIT(IS_IP_FRAG, ip_frag) || + CHECK_BIT(IP_FIRST_FRAG, ip_firstfrag) || CHECK(RECIRC_ID, recirc_id)) return rc; return 0; } +#undef CHECK_BIT #undef CHECK static bool efx_mae_asl_id(u32 id) @@ -472,6 +487,16 @@ static int efx_mae_populate_match_criteria(MCDI_DECLARE_STRUCT_PTR(match_crit), } MCDI_STRUCT_SET_DWORD(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_INGRESS_MPORT_SELECTOR_MASK, match->mask.ingress_port); + EFX_POPULATE_DWORD_2(*_MCDI_STRUCT_DWORD(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_FLAGS), + MAE_FIELD_MASK_VALUE_PAIRS_V2_IS_IP_FRAG, + match->value.ip_frag, + MAE_FIELD_MASK_VALUE_PAIRS_V2_IP_FIRST_FRAG, + match->value.ip_firstfrag); + EFX_POPULATE_DWORD_2(*_MCDI_STRUCT_DWORD(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_FLAGS_MASK), + MAE_FIELD_MASK_VALUE_PAIRS_V2_IS_IP_FRAG, + match->mask.ip_frag, + MAE_FIELD_MASK_VALUE_PAIRS_V2_IP_FIRST_FRAG, + match->mask.ip_firstfrag); MCDI_STRUCT_SET_BYTE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_RECIRC_ID, match->value.recirc_id); MCDI_STRUCT_SET_BYTE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_RECIRC_ID_MASK, diff --git a/drivers/net/ethernet/sfc/tc.c b/drivers/net/ethernet/sfc/tc.c index d992fafc844e..1a9cc2ad1335 100644 --- a/drivers/net/ethernet/sfc/tc.c +++ b/drivers/net/ethernet/sfc/tc.c @@ -168,7 +168,15 @@ static int efx_tc_flower_parse_match(struct efx_nic *efx, break; } - if (fm.mask->flags) { + if (fm.mask->flags & FLOW_DIS_IS_FRAGMENT) { + match->value.ip_frag = fm.key->flags & FLOW_DIS_IS_FRAGMENT; + match->mask.ip_frag = true; + } + if (fm.mask->flags & FLOW_DIS_FIRST_FRAG) { + match->value.ip_firstfrag = fm.key->flags & FLOW_DIS_FIRST_FRAG; + match->mask.ip_firstfrag = true; + } + if (fm.mask->flags & ~(FLOW_DIS_IS_FRAGMENT | FLOW_DIS_FIRST_FRAG)) { NL_SET_ERR_MSG_FMT_MOD(extack, "Unsupported match on control.flags %#x", fm.mask->flags); return -EOPNOTSUPP; diff --git a/drivers/net/ethernet/sfc/tc.h b/drivers/net/ethernet/sfc/tc.h index aebe9c251b2c..d2b61926657b 100644 --- a/drivers/net/ethernet/sfc/tc.h +++ b/drivers/net/ethernet/sfc/tc.h @@ -38,6 +38,7 @@ struct efx_tc_match_fields { #ifdef CONFIG_IPV6 struct in6_addr src_ip6, dst_ip6; #endif + bool ip_frag, ip_firstfrag; }; struct efx_tc_match { From 5d1d24da00dba75f94f1640717b3fd2a243e4b5f Mon Sep 17 00:00:00 2001 From: Edward Cree Date: Thu, 3 Nov 2022 15:27:31 +0000 Subject: [PATCH 0655/2002] sfc: add Layer 4 matches to ef100 TC offload Support matching on UDP/TCP source and destination ports and TCP flags, with masking if supported by the hardware. Signed-off-by: Edward Cree Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/sfc/mae.c | 15 +++++++++++++++ drivers/net/ethernet/sfc/tc.c | 21 +++++++++++++++++++-- drivers/net/ethernet/sfc/tc.h | 3 +++ 3 files changed, 37 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/sfc/mae.c b/drivers/net/ethernet/sfc/mae.c index e24436ba699c..1e605e2a08c5 100644 --- a/drivers/net/ethernet/sfc/mae.c +++ b/drivers/net/ethernet/sfc/mae.c @@ -311,6 +311,9 @@ int efx_mae_match_check_caps(struct efx_nic *efx, CHECK(SRC_IP6, src_ip6) || CHECK(DST_IP6, dst_ip6) || #endif + CHECK(L4_SPORT, l4_sport) || + CHECK(L4_DPORT, l4_dport) || + CHECK(TCP_FLAGS, tcp_flags) || CHECK_BIT(IS_IP_FRAG, ip_frag) || CHECK_BIT(IP_FIRST_FRAG, ip_firstfrag) || CHECK(RECIRC_ID, recirc_id)) @@ -559,6 +562,18 @@ static int efx_mae_populate_match_criteria(MCDI_DECLARE_STRUCT_PTR(match_crit), memcpy(MCDI_STRUCT_PTR(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_DST_IP6_BE_MASK), &match->mask.dst_ip6, sizeof(struct in6_addr)); #endif + MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_L4_SPORT_BE, + match->value.l4_sport); + MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_L4_SPORT_BE_MASK, + match->mask.l4_sport); + MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_L4_DPORT_BE, + match->value.l4_dport); + MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_L4_DPORT_BE_MASK, + match->mask.l4_dport); + MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_TCP_FLAGS_BE, + match->value.tcp_flags); + MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_TCP_FLAGS_BE_MASK, + match->mask.tcp_flags); return 0; } diff --git a/drivers/net/ethernet/sfc/tc.c b/drivers/net/ethernet/sfc/tc.c index 1a9cc2ad1335..17e1a3447554 100644 --- a/drivers/net/ethernet/sfc/tc.c +++ b/drivers/net/ethernet/sfc/tc.c @@ -190,6 +190,8 @@ static int efx_tc_flower_parse_match(struct efx_nic *efx, BIT(FLOW_DISSECTOR_KEY_CVLAN) | BIT(FLOW_DISSECTOR_KEY_IPV4_ADDRS) | BIT(FLOW_DISSECTOR_KEY_IPV6_ADDRS) | + BIT(FLOW_DISSECTOR_KEY_PORTS) | + BIT(FLOW_DISSECTOR_KEY_TCP) | BIT(FLOW_DISSECTOR_KEY_IP))) { NL_SET_ERR_MSG_FMT_MOD(extack, "Unsupported flower keys %#x", dissector->used_keys); @@ -204,8 +206,10 @@ static int efx_tc_flower_parse_match(struct efx_nic *efx, if (dissector->used_keys & (BIT(FLOW_DISSECTOR_KEY_IPV4_ADDRS) | BIT(FLOW_DISSECTOR_KEY_IPV6_ADDRS) | - BIT(FLOW_DISSECTOR_KEY_IP))) { - NL_SET_ERR_MSG_FMT_MOD(extack, "L3 flower keys %#x require protocol ipv[46]", + BIT(FLOW_DISSECTOR_KEY_PORTS) | + BIT(FLOW_DISSECTOR_KEY_IP) | + BIT(FLOW_DISSECTOR_KEY_TCP))) { + NL_SET_ERR_MSG_FMT_MOD(extack, "L3/L4 flower keys %#x require protocol ipv[46]", dissector->used_keys); return -EINVAL; } @@ -249,6 +253,16 @@ static int efx_tc_flower_parse_match(struct efx_nic *efx, } MAP_KEY_AND_MASK(BASIC, basic, ip_proto, ip_proto); + /* Make sure we're TCP/UDP if any L4 keys used. */ + if ((match->value.ip_proto != IPPROTO_UDP && + match->value.ip_proto != IPPROTO_TCP) || !IS_ALL_ONES(match->mask.ip_proto)) + if (dissector->used_keys & + (BIT(FLOW_DISSECTOR_KEY_PORTS) | + BIT(FLOW_DISSECTOR_KEY_TCP))) { + NL_SET_ERR_MSG_FMT_MOD(extack, "L4 flower keys %#x require ipproto udp or tcp", + dissector->used_keys); + return -EINVAL; + } MAP_KEY_AND_MASK(IP, ip, tos, ip_tos); MAP_KEY_AND_MASK(IP, ip, ttl, ip_ttl); if (ipv == 4) { @@ -261,6 +275,9 @@ static int efx_tc_flower_parse_match(struct efx_nic *efx, MAP_KEY_AND_MASK(IPV6_ADDRS, ipv6_addrs, dst, dst_ip6); } #endif + MAP_KEY_AND_MASK(PORTS, ports, src, l4_sport); + MAP_KEY_AND_MASK(PORTS, ports, dst, l4_dport); + MAP_KEY_AND_MASK(TCP, tcp, flags, tcp_flags); return 0; } diff --git a/drivers/net/ethernet/sfc/tc.h b/drivers/net/ethernet/sfc/tc.h index d2b61926657b..4240c375a8e6 100644 --- a/drivers/net/ethernet/sfc/tc.h +++ b/drivers/net/ethernet/sfc/tc.h @@ -39,6 +39,9 @@ struct efx_tc_match_fields { struct in6_addr src_ip6, dst_ip6; #endif bool ip_frag, ip_firstfrag; + /* L4 */ + __be16 l4_sport, l4_dport; /* Ports (UDP, TCP) */ + __be16 tcp_flags; }; struct efx_tc_match { From 63d9e12914840400e9f96c2ae9a51cd9702c2daf Mon Sep 17 00:00:00 2001 From: Veerasenareddy Burru Date: Wed, 2 Nov 2022 23:05:57 -0700 Subject: [PATCH 0656/2002] octeon_ep: support Octeon device CNF95N Add support for Octeon device CNF95N. CNF95N is a Octeon Fusion family product with same PCI NIC characteristics as CN93 which is currently supported by the driver. update supported device list in Documentation. Signed-off-by: Veerasenareddy Burru Link: https://lore.kernel.org/r/20221103060600.1858-1-vburru@marvell.com Signed-off-by: Jakub Kicinski --- .../ethernet/marvell/octeon_ep.rst | 1 + .../ethernet/marvell/octeon_ep/octep_main.c | 20 ++++++++++++++++--- .../ethernet/marvell/octeon_ep/octep_main.h | 2 ++ 3 files changed, 20 insertions(+), 3 deletions(-) diff --git a/Documentation/networking/device_drivers/ethernet/marvell/octeon_ep.rst b/Documentation/networking/device_drivers/ethernet/marvell/octeon_ep.rst index bc562c49011b..cad96c8d1f97 100644 --- a/Documentation/networking/device_drivers/ethernet/marvell/octeon_ep.rst +++ b/Documentation/networking/device_drivers/ethernet/marvell/octeon_ep.rst @@ -23,6 +23,7 @@ Supported Devices ================= Currently, this driver support following devices: * Network controller: Cavium, Inc. Device b200 + * Network controller: Cavium, Inc. Device b400 Interface Control ================= diff --git a/drivers/net/ethernet/marvell/octeon_ep/octep_main.c b/drivers/net/ethernet/marvell/octeon_ep/octep_main.c index 9089adcb75f9..1cbfa800a8af 100644 --- a/drivers/net/ethernet/marvell/octeon_ep/octep_main.c +++ b/drivers/net/ethernet/marvell/octeon_ep/octep_main.c @@ -23,6 +23,7 @@ struct workqueue_struct *octep_wq; /* Supported Devices */ static const struct pci_device_id octep_pci_id_tbl[] = { {PCI_DEVICE(PCI_VENDOR_ID_CAVIUM, OCTEP_PCI_DEVICE_ID_CN93_PF)}, + {PCI_DEVICE(PCI_VENDOR_ID_CAVIUM, OCTEP_PCI_DEVICE_ID_CNF95N_PF)}, {0, }, }; MODULE_DEVICE_TABLE(pci, octep_pci_id_tbl); @@ -907,6 +908,18 @@ static void octep_ctrl_mbox_task(struct work_struct *work) } } +static const char *octep_devid_to_str(struct octep_device *oct) +{ + switch (oct->chip_id) { + case OCTEP_PCI_DEVICE_ID_CN93_PF: + return "CN93XX"; + case OCTEP_PCI_DEVICE_ID_CNF95N_PF: + return "CNF95N"; + default: + return "Unsupported"; + } +} + /** * octep_device_setup() - Setup Octeon Device. * @@ -939,9 +952,10 @@ int octep_device_setup(struct octep_device *oct) switch (oct->chip_id) { case OCTEP_PCI_DEVICE_ID_CN93_PF: - dev_info(&pdev->dev, - "Setting up OCTEON CN93XX PF PASS%d.%d\n", - OCTEP_MAJOR_REV(oct), OCTEP_MINOR_REV(oct)); + case OCTEP_PCI_DEVICE_ID_CNF95N_PF: + dev_info(&pdev->dev, "Setting up OCTEON %s PF PASS%d.%d\n", + octep_devid_to_str(oct), OCTEP_MAJOR_REV(oct), + OCTEP_MINOR_REV(oct)); octep_device_setup_cn93_pf(oct); break; default: diff --git a/drivers/net/ethernet/marvell/octeon_ep/octep_main.h b/drivers/net/ethernet/marvell/octeon_ep/octep_main.h index 025626a61383..123ffc13754d 100644 --- a/drivers/net/ethernet/marvell/octeon_ep/octep_main.h +++ b/drivers/net/ethernet/marvell/octeon_ep/octep_main.h @@ -21,6 +21,8 @@ #define OCTEP_PCI_DEVICE_ID_CN93_PF 0xB200 #define OCTEP_PCI_DEVICE_ID_CN93_VF 0xB203 +#define OCTEP_PCI_DEVICE_ID_CNF95N_PF 0xB400 //95N PF + #define OCTEP_MAX_QUEUES 63 #define OCTEP_MAX_IQ OCTEP_MAX_QUEUES #define OCTEP_MAX_OQ OCTEP_MAX_QUEUES From d08cb25556773c65efb21761b75f92c804ad0975 Mon Sep 17 00:00:00 2001 From: David Yang Date: Fri, 28 Oct 2022 10:01:01 +0800 Subject: [PATCH 0657/2002] net: mv643xx_eth: support MII/GMII/RGMII modes for Kirkwood Support mode switch properly, which is not available before. If SoC has two Ethernet controllers, by setting both of them into MII mode, the first controller enters GMII mode, while the second controller is effectively disabled. This requires configuring (and maybe enabling) the second controller in the device tree, even though it cannot be used. Signed-off-by: David Yang Signed-off-by: David S. Miller --- drivers/net/ethernet/marvell/mv643xx_eth.c | 49 ++++++++++++++++++---- include/linux/mv643xx_eth.h | 2 + 2 files changed, 44 insertions(+), 7 deletions(-) diff --git a/drivers/net/ethernet/marvell/mv643xx_eth.c b/drivers/net/ethernet/marvell/mv643xx_eth.c index 707993b445d1..4389a33a2ea6 100644 --- a/drivers/net/ethernet/marvell/mv643xx_eth.c +++ b/drivers/net/ethernet/marvell/mv643xx_eth.c @@ -108,6 +108,7 @@ static char mv643xx_eth_driver_version[] = "1.4"; #define TXQ_COMMAND 0x0048 #define TXQ_FIX_PRIO_CONF 0x004c #define PORT_SERIAL_CONTROL1 0x004c +#define RGMII_EN 0x00000008 #define CLK125_BYPASS_EN 0x00000010 #define TX_BW_RATE 0x0050 #define TX_BW_MTU 0x0058 @@ -2761,6 +2762,8 @@ static int mv643xx_eth_shared_of_add_port(struct platform_device *pdev, mv643xx_eth_property(pnp, "rx-sram-addr", ppd.rx_sram_addr); mv643xx_eth_property(pnp, "rx-sram-size", ppd.rx_sram_size); + of_get_phy_mode(pnp, &ppd.interface); + ppd.phy_node = of_parse_phandle(pnp, "phy-handle", 0); if (!ppd.phy_node) { ppd.phy_addr = MV643XX_ETH_PHY_NONE; @@ -3092,6 +3095,7 @@ static int mv643xx_eth_probe(struct platform_device *pdev) struct mv643xx_eth_private *mp; struct net_device *dev; struct phy_device *phydev = NULL; + u32 psc1r; int err, irq; pd = dev_get_platdata(&pdev->dev); @@ -3119,14 +3123,45 @@ static int mv643xx_eth_probe(struct platform_device *pdev) mp->dev = dev; - /* Kirkwood resets some registers on gated clocks. Especially - * CLK125_BYPASS_EN must be cleared but is not available on - * all other SoCs/System Controllers using this driver. - */ if (of_device_is_compatible(pdev->dev.of_node, - "marvell,kirkwood-eth-port")) - wrlp(mp, PORT_SERIAL_CONTROL1, - rdlp(mp, PORT_SERIAL_CONTROL1) & ~CLK125_BYPASS_EN); + "marvell,kirkwood-eth-port")) { + psc1r = rdlp(mp, PORT_SERIAL_CONTROL1); + + /* Kirkwood resets some registers on gated clocks. Especially + * CLK125_BYPASS_EN must be cleared but is not available on + * all other SoCs/System Controllers using this driver. + */ + psc1r &= ~CLK125_BYPASS_EN; + + /* On Kirkwood with two Ethernet controllers, if both of them + * have RGMII_EN disabled, the first controller will be in GMII + * mode and the second one is effectively disabled, instead of + * two MII interfaces. + * + * To enable GMII in the first controller, the second one must + * also be configured (and may be enabled) with RGMII_EN + * disabled too, even though it cannot be used at all. + */ + switch (pd->interface) { + /* Use internal to denote second controller being disabled */ + case PHY_INTERFACE_MODE_INTERNAL: + case PHY_INTERFACE_MODE_MII: + case PHY_INTERFACE_MODE_GMII: + psc1r &= ~RGMII_EN; + break; + case PHY_INTERFACE_MODE_RGMII: + case PHY_INTERFACE_MODE_RGMII_ID: + case PHY_INTERFACE_MODE_RGMII_RXID: + case PHY_INTERFACE_MODE_RGMII_TXID: + psc1r |= RGMII_EN; + break; + default: + /* Unknown; don't touch */ + break; + } + + wrlp(mp, PORT_SERIAL_CONTROL1, psc1r); + } /* * Start with a default rate, and if there is a clock, allow diff --git a/include/linux/mv643xx_eth.h b/include/linux/mv643xx_eth.h index 3682ae75c7aa..145169be2ed8 100644 --- a/include/linux/mv643xx_eth.h +++ b/include/linux/mv643xx_eth.h @@ -8,6 +8,7 @@ #include #include +#include #define MV643XX_ETH_SHARED_NAME "mv643xx_eth" #define MV643XX_ETH_NAME "mv643xx_eth_port" @@ -59,6 +60,7 @@ struct mv643xx_eth_platform_data { */ int speed; int duplex; + phy_interface_t interface; /* * How many RX/TX queues to use. From a3de357b087e50e2b2465346f5f67ac7aa4f6b93 Mon Sep 17 00:00:00 2001 From: Andy Chiu Date: Tue, 1 Nov 2022 09:11:39 +0800 Subject: [PATCH 0658/2002] net: axiemac: add PM callbacks to support suspend/resume Support basic system-wide suspend and resume functions Signed-off-by: Andy Chiu Signed-off-by: David S. Miller --- .../net/ethernet/xilinx/xilinx_axienet_main.c | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/drivers/net/ethernet/xilinx/xilinx_axienet_main.c b/drivers/net/ethernet/xilinx/xilinx_axienet_main.c index 441e1058104f..d082f2b10f4d 100644 --- a/drivers/net/ethernet/xilinx/xilinx_axienet_main.c +++ b/drivers/net/ethernet/xilinx/xilinx_axienet_main.c @@ -2217,12 +2217,48 @@ static void axienet_shutdown(struct platform_device *pdev) rtnl_unlock(); } +static int axienet_suspend(struct device *dev) +{ + struct net_device *ndev = dev_get_drvdata(dev); + + if (!netif_running(ndev)) + return 0; + + netif_device_detach(ndev); + + rtnl_lock(); + axienet_stop(ndev); + rtnl_unlock(); + + return 0; +} + +static int axienet_resume(struct device *dev) +{ + struct net_device *ndev = dev_get_drvdata(dev); + + if (!netif_running(ndev)) + return 0; + + rtnl_lock(); + axienet_open(ndev); + rtnl_unlock(); + + netif_device_attach(ndev); + + return 0; +} + +static DEFINE_SIMPLE_DEV_PM_OPS(axienet_pm_ops, + axienet_suspend, axienet_resume); + static struct platform_driver axienet_driver = { .probe = axienet_probe, .remove = axienet_remove, .shutdown = axienet_shutdown, .driver = { .name = "xilinx_axienet", + .pm = &axienet_pm_ops, .of_match_table = axienet_of_match, }, }; From bd881b44fdbcce1fb899601e026a4cebfe38b2df Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Wed, 2 Nov 2022 12:15:11 -0400 Subject: [PATCH 0659/2002] dt-bindings: net: constrain number of 'reg' in ethernet ports 'reg' without any constraints allows multiple items which is not the intention for Ethernet controller's port number. Constrain the 'reg' on AX88178 and LAN95xx USB Ethernet Controllers. Signed-off-by: Krzysztof Kozlowski Reviewed-by: Oleksij Rempel Reviewed-by: Rob Herring Reviewed-by: Florian Fainelli Signed-off-by: David S. Miller --- Documentation/devicetree/bindings/net/asix,ax88178.yaml | 4 +++- Documentation/devicetree/bindings/net/microchip,lan95xx.yaml | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/net/asix,ax88178.yaml b/Documentation/devicetree/bindings/net/asix,ax88178.yaml index 1af52358de4c..a81dbc4792f6 100644 --- a/Documentation/devicetree/bindings/net/asix,ax88178.yaml +++ b/Documentation/devicetree/bindings/net/asix,ax88178.yaml @@ -27,7 +27,9 @@ properties: - usbb95,772b # ASIX AX88772B - usbb95,7e2b # ASIX AX88772B - reg: true + reg: + maxItems: 1 + local-mac-address: true mac-address: true diff --git a/Documentation/devicetree/bindings/net/microchip,lan95xx.yaml b/Documentation/devicetree/bindings/net/microchip,lan95xx.yaml index cf91fecd8909..3715c5f8f0e0 100644 --- a/Documentation/devicetree/bindings/net/microchip,lan95xx.yaml +++ b/Documentation/devicetree/bindings/net/microchip,lan95xx.yaml @@ -39,7 +39,9 @@ properties: - usb424,9e08 # SMSC LAN89530 USB Ethernet Device - usb424,ec00 # SMSC9512/9514 USB Hub & Ethernet Device - reg: true + reg: + maxItems: 1 + local-mac-address: true mac-address: true From a352a2c5d2a504f17c7096c9085177a54917012d Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Wed, 2 Nov 2022 12:15:12 -0400 Subject: [PATCH 0660/2002] dt-bindings: net: dsa-port: constrain number of 'reg' in ports 'reg' without any constraints allows multiple items which is not the intention in DSA port schema (as physical port is expected to have only one address). Signed-off-by: Krzysztof Kozlowski Reviewed-by: Rob Herring Reviewed-by: Florian Fainelli Signed-off-by: David S. Miller --- Documentation/devicetree/bindings/net/dsa/dsa-port.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/net/dsa/dsa-port.yaml b/Documentation/devicetree/bindings/net/dsa/dsa-port.yaml index 10ad7e71097b..9abb8eba5fad 100644 --- a/Documentation/devicetree/bindings/net/dsa/dsa-port.yaml +++ b/Documentation/devicetree/bindings/net/dsa/dsa-port.yaml @@ -19,7 +19,8 @@ allOf: properties: reg: - description: Port number + items: + - description: Port number label: description: From ac33d7ae8f7117df63267207f889cd535402381b Mon Sep 17 00:00:00 2001 From: Cai Huoqing Date: Thu, 3 Nov 2022 16:05:09 +0800 Subject: [PATCH 0661/2002] net: hinic: Convert the cmd code from decimal to hex to be more readable The print cmd code is in hex, so using hex cmd code intead of decimal is easy to check the value with print info. Signed-off-by: Cai Huoqing Signed-off-by: David S. Miller --- .../net/ethernet/huawei/hinic/hinic_hw_dev.h | 104 +++++++++--------- 1 file changed, 52 insertions(+), 52 deletions(-) diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_dev.h b/drivers/net/ethernet/huawei/hinic/hinic_hw_dev.h index d2d89b0a5ef0..6dae116a11f8 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_hw_dev.h +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_dev.h @@ -46,104 +46,104 @@ enum hinic_port_cmd { HINIC_PORT_CMD_VF_REGISTER = 0x0, HINIC_PORT_CMD_VF_UNREGISTER = 0x1, - HINIC_PORT_CMD_CHANGE_MTU = 2, + HINIC_PORT_CMD_CHANGE_MTU = 0x2, - HINIC_PORT_CMD_ADD_VLAN = 3, - HINIC_PORT_CMD_DEL_VLAN = 4, + HINIC_PORT_CMD_ADD_VLAN = 0x3, + HINIC_PORT_CMD_DEL_VLAN = 0x4, - HINIC_PORT_CMD_SET_PFC = 5, + HINIC_PORT_CMD_SET_PFC = 0x5, - HINIC_PORT_CMD_SET_MAC = 9, - HINIC_PORT_CMD_GET_MAC = 10, - HINIC_PORT_CMD_DEL_MAC = 11, + HINIC_PORT_CMD_SET_MAC = 0x9, + HINIC_PORT_CMD_GET_MAC = 0xA, + HINIC_PORT_CMD_DEL_MAC = 0xB, - HINIC_PORT_CMD_SET_RX_MODE = 12, + HINIC_PORT_CMD_SET_RX_MODE = 0xC, - HINIC_PORT_CMD_GET_PAUSE_INFO = 20, - HINIC_PORT_CMD_SET_PAUSE_INFO = 21, + HINIC_PORT_CMD_GET_PAUSE_INFO = 0x14, + HINIC_PORT_CMD_SET_PAUSE_INFO = 0x15, - HINIC_PORT_CMD_GET_LINK_STATE = 24, + HINIC_PORT_CMD_GET_LINK_STATE = 0x18, - HINIC_PORT_CMD_SET_LRO = 25, + HINIC_PORT_CMD_SET_LRO = 0x19, - HINIC_PORT_CMD_SET_RX_CSUM = 26, + HINIC_PORT_CMD_SET_RX_CSUM = 0x1A, - HINIC_PORT_CMD_SET_RX_VLAN_OFFLOAD = 27, + HINIC_PORT_CMD_SET_RX_VLAN_OFFLOAD = 0x1B, - HINIC_PORT_CMD_GET_PORT_STATISTICS = 28, + HINIC_PORT_CMD_GET_PORT_STATISTICS = 0x1C, - HINIC_PORT_CMD_CLEAR_PORT_STATISTICS = 29, + HINIC_PORT_CMD_CLEAR_PORT_STATISTICS = 0x1D, - HINIC_PORT_CMD_GET_VPORT_STAT = 30, + HINIC_PORT_CMD_GET_VPORT_STAT = 0x1E, - HINIC_PORT_CMD_CLEAN_VPORT_STAT = 31, + HINIC_PORT_CMD_CLEAN_VPORT_STAT = 0x1F, - HINIC_PORT_CMD_GET_RSS_TEMPLATE_INDIR_TBL = 37, + HINIC_PORT_CMD_GET_RSS_TEMPLATE_INDIR_TBL = 0x25, - HINIC_PORT_CMD_SET_PORT_STATE = 41, + HINIC_PORT_CMD_SET_PORT_STATE = 0x29, - HINIC_PORT_CMD_SET_RSS_TEMPLATE_TBL = 43, + HINIC_PORT_CMD_SET_RSS_TEMPLATE_TBL = 0x2B, - HINIC_PORT_CMD_GET_RSS_TEMPLATE_TBL = 44, + HINIC_PORT_CMD_GET_RSS_TEMPLATE_TBL = 0x2C, - HINIC_PORT_CMD_SET_RSS_HASH_ENGINE = 45, + HINIC_PORT_CMD_SET_RSS_HASH_ENGINE = 0x2D, - HINIC_PORT_CMD_GET_RSS_HASH_ENGINE = 46, + HINIC_PORT_CMD_GET_RSS_HASH_ENGINE = 0x2E, - HINIC_PORT_CMD_GET_RSS_CTX_TBL = 47, + HINIC_PORT_CMD_GET_RSS_CTX_TBL = 0x2F, - HINIC_PORT_CMD_SET_RSS_CTX_TBL = 48, + HINIC_PORT_CMD_SET_RSS_CTX_TBL = 0x30, - HINIC_PORT_CMD_RSS_TEMP_MGR = 49, + HINIC_PORT_CMD_RSS_TEMP_MGR = 0x31, - HINIC_PORT_CMD_RD_LINE_TBL = 57, + HINIC_PORT_CMD_RD_LINE_TBL = 0x39, - HINIC_PORT_CMD_RSS_CFG = 66, + HINIC_PORT_CMD_RSS_CFG = 0x42, - HINIC_PORT_CMD_FWCTXT_INIT = 69, + HINIC_PORT_CMD_FWCTXT_INIT = 0x45, - HINIC_PORT_CMD_GET_LOOPBACK_MODE = 72, - HINIC_PORT_CMD_SET_LOOPBACK_MODE, + HINIC_PORT_CMD_GET_LOOPBACK_MODE = 0x48, + HINIC_PORT_CMD_SET_LOOPBACK_MODE = 0x49, - HINIC_PORT_CMD_ENABLE_SPOOFCHK = 78, + HINIC_PORT_CMD_ENABLE_SPOOFCHK = 0x4E, - HINIC_PORT_CMD_GET_MGMT_VERSION = 88, + HINIC_PORT_CMD_GET_MGMT_VERSION = 0x58, - HINIC_PORT_CMD_SET_FUNC_STATE = 93, + HINIC_PORT_CMD_SET_FUNC_STATE = 0x5D, - HINIC_PORT_CMD_GET_GLOBAL_QPN = 102, + HINIC_PORT_CMD_GET_GLOBAL_QPN = 0x66, - HINIC_PORT_CMD_SET_VF_RATE = 105, + HINIC_PORT_CMD_SET_VF_RATE = 0x69, - HINIC_PORT_CMD_SET_VF_VLAN = 106, + HINIC_PORT_CMD_SET_VF_VLAN = 0x6A, - HINIC_PORT_CMD_CLR_VF_VLAN, + HINIC_PORT_CMD_CLR_VF_VLAN = 0x6B, - HINIC_PORT_CMD_SET_TSO = 112, + HINIC_PORT_CMD_SET_TSO = 0x70, - HINIC_PORT_CMD_UPDATE_FW = 114, + HINIC_PORT_CMD_UPDATE_FW = 0x72, - HINIC_PORT_CMD_SET_RQ_IQ_MAP = 115, + HINIC_PORT_CMD_SET_RQ_IQ_MAP = 0x73, - HINIC_PORT_CMD_LINK_STATUS_REPORT = 160, + HINIC_PORT_CMD_LINK_STATUS_REPORT = 0xA0, - HINIC_PORT_CMD_UPDATE_MAC = 164, + HINIC_PORT_CMD_UPDATE_MAC = 0xA4, - HINIC_PORT_CMD_GET_CAP = 170, + HINIC_PORT_CMD_GET_CAP = 0xAA, - HINIC_PORT_CMD_GET_LINK_MODE = 217, + HINIC_PORT_CMD_GET_LINK_MODE = 0xD9, - HINIC_PORT_CMD_SET_SPEED = 218, + HINIC_PORT_CMD_SET_SPEED = 0xDA, - HINIC_PORT_CMD_SET_AUTONEG = 219, + HINIC_PORT_CMD_SET_AUTONEG = 0xDB, - HINIC_PORT_CMD_GET_STD_SFP_INFO = 240, + HINIC_PORT_CMD_GET_STD_SFP_INFO = 0xF0, - HINIC_PORT_CMD_SET_LRO_TIMER = 244, + HINIC_PORT_CMD_SET_LRO_TIMER = 0xF4, - HINIC_PORT_CMD_SET_VF_MAX_MIN_RATE = 249, + HINIC_PORT_CMD_SET_VF_MAX_MIN_RATE = 0xF9, - HINIC_PORT_CMD_GET_SFP_ABS = 251, + HINIC_PORT_CMD_GET_SFP_ABS = 0xFB, }; /* cmd of mgmt CPU message for HILINK module */ From 13265568a8633ab0d22e9def8cd9b8b0edb3ca85 Mon Sep 17 00:00:00 2001 From: Cai Huoqing Date: Thu, 3 Nov 2022 16:05:10 +0800 Subject: [PATCH 0662/2002] net: hinic: Add control command support for VF PMD driver in DPDK HINIC has a mailbox for PF-VF communication and the VF driver could send port control command to PF driver via mailbox. The control command only can be set to register in PF, so add support in PF driver for VF PMD driver control command when VF PMD driver work with linux PF driver. Then, no need to add handlers to nic_vf_cmd_msg_handler[], because the host driver just forwards it to the firmware. Actually the firmware works on a coprocessor MGMT_CPU(inside the NIC) which will recv and deal with these commands. Signed-off-by: Cai Huoqing Signed-off-by: David S. Miller --- .../net/ethernet/huawei/hinic/hinic_hw_dev.h | 66 +++++++++++++++++++ .../net/ethernet/huawei/hinic/hinic_sriov.c | 18 +++++ 2 files changed, 84 insertions(+) diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_dev.h b/drivers/net/ethernet/huawei/hinic/hinic_hw_dev.h index 6dae116a11f8..6b5797e69781 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_hw_dev.h +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_dev.h @@ -51,6 +51,9 @@ enum hinic_port_cmd { HINIC_PORT_CMD_ADD_VLAN = 0x3, HINIC_PORT_CMD_DEL_VLAN = 0x4, + HINIC_PORT_CMD_SET_ETS = 0x7, + HINIC_PORT_CMD_GET_ETS = 0x8, + HINIC_PORT_CMD_SET_PFC = 0x5, HINIC_PORT_CMD_SET_MAC = 0x9, @@ -59,6 +62,8 @@ enum hinic_port_cmd { HINIC_PORT_CMD_SET_RX_MODE = 0xC, + HINIC_PORT_CMD_SET_ANTI_ATTACK_RATE = 0xD, + HINIC_PORT_CMD_GET_PAUSE_INFO = 0x14, HINIC_PORT_CMD_SET_PAUSE_INFO = 0x15, @@ -81,6 +86,7 @@ enum hinic_port_cmd { HINIC_PORT_CMD_GET_RSS_TEMPLATE_INDIR_TBL = 0x25, HINIC_PORT_CMD_SET_PORT_STATE = 0x29, + HINIC_PORT_CMD_GET_PORT_STATE = 0x30, HINIC_PORT_CMD_SET_RSS_TEMPLATE_TBL = 0x2B, @@ -100,17 +106,29 @@ enum hinic_port_cmd { HINIC_PORT_CMD_RSS_CFG = 0x42, + HINIC_PORT_CMD_GET_PHY_TYPE = 0x44, + HINIC_PORT_CMD_FWCTXT_INIT = 0x45, HINIC_PORT_CMD_GET_LOOPBACK_MODE = 0x48, HINIC_PORT_CMD_SET_LOOPBACK_MODE = 0x49, + HINIC_PORT_CMD_GET_JUMBO_FRAME_SIZE = 0x4A, + HINIC_PORT_CMD_SET_JUMBO_FRAME_SIZE = 0x4B, + HINIC_PORT_CMD_ENABLE_SPOOFCHK = 0x4E, HINIC_PORT_CMD_GET_MGMT_VERSION = 0x58, + HINIC_PORT_CMD_GET_PORT_TYPE = 0x5B, + HINIC_PORT_CMD_SET_FUNC_STATE = 0x5D, + HINIC_PORT_CMD_GET_PORT_ID_BY_FUNC_ID = 0x5E, + + HINIC_PORT_CMD_GET_DMA_CS = 0x64, + HINIC_PORT_CMD_SET_DMA_CS = 0x65, + HINIC_PORT_CMD_GET_GLOBAL_QPN = 0x66, HINIC_PORT_CMD_SET_VF_RATE = 0x69, @@ -125,25 +143,73 @@ enum hinic_port_cmd { HINIC_PORT_CMD_SET_RQ_IQ_MAP = 0x73, + HINIC_PORT_CMD_SET_PFC_THD = 0x75, + HINIC_PORT_CMD_LINK_STATUS_REPORT = 0xA0, + HINIC_PORT_CMD_SET_LOSSLESS_ETH = 0xA3, + HINIC_PORT_CMD_UPDATE_MAC = 0xA4, HINIC_PORT_CMD_GET_CAP = 0xAA, + HINIC_PORT_CMD_UP_TC_ADD_FLOW = 0xAF, + HINIC_PORT_CMD_UP_TC_DEL_FLOW = 0xB0, + HINIC_PORT_CMD_UP_TC_GET_FLOW = 0xB1, + + HINIC_PORT_CMD_UP_TC_FLUSH_TCAM = 0xB2, + + HINIC_PORT_CMD_UP_TC_CTRL_TCAM_BLOCK = 0xB3, + + HINIC_PORT_CMD_UP_TC_ENABLE = 0xB4, + + HINIC_PORT_CMD_UP_TC_GET_TCAM_BLOCK = 0xB5, + + HINIC_PORT_CMD_SET_IPSU_MAC = 0xCB, + HINIC_PORT_CMD_GET_IPSU_MAC = 0xCC, + + HINIC_PORT_CMD_SET_XSFP_STATUS = 0xD4, + HINIC_PORT_CMD_GET_LINK_MODE = 0xD9, HINIC_PORT_CMD_SET_SPEED = 0xDA, HINIC_PORT_CMD_SET_AUTONEG = 0xDB, + HINIC_PORT_CMD_CLEAR_QP_RES = 0xDD, + + HINIC_PORT_CMD_SET_SUPER_CQE = 0xDE, + + HINIC_PORT_CMD_SET_VF_COS = 0xDF, + HINIC_PORT_CMD_GET_VF_COS = 0xE1, + + HINIC_PORT_CMD_CABLE_PLUG_EVENT = 0xE5, + + HINIC_PORT_CMD_LINK_ERR_EVENT = 0xE6, + + HINIC_PORT_CMD_SET_COS_UP_MAP = 0xE8, + + HINIC_PORT_CMD_RESET_LINK_CFG = 0xEB, + HINIC_PORT_CMD_GET_STD_SFP_INFO = 0xF0, + HINIC_PORT_CMD_FORCE_PKT_DROP = 0xF3, + HINIC_PORT_CMD_SET_LRO_TIMER = 0xF4, + HINIC_PORT_CMD_SET_VHD_CFG = 0xF7, + + HINIC_PORT_CMD_SET_LINK_FOLLOW = 0xF8, + HINIC_PORT_CMD_SET_VF_MAX_MIN_RATE = 0xF9, HINIC_PORT_CMD_GET_SFP_ABS = 0xFB, + + HINIC_PORT_CMD_Q_FILTER = 0xFC, + + HINIC_PORT_CMD_TCAM_FILTER = 0xFE, + + HINIC_PORT_CMD_SET_VLAN_FILTER = 0xFF, }; /* cmd of mgmt CPU message for HILINK module */ diff --git a/drivers/net/ethernet/huawei/hinic/hinic_sriov.c b/drivers/net/ethernet/huawei/hinic/hinic_sriov.c index f7e05b41385b..ee357088d021 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_sriov.c +++ b/drivers/net/ethernet/huawei/hinic/hinic_sriov.c @@ -489,6 +489,24 @@ static struct vf_cmd_check_handle nic_cmd_support_vf[] = { {HINIC_PORT_CMD_UPDATE_MAC, hinic_mbox_check_func_id_8B}, {HINIC_PORT_CMD_GET_CAP, hinic_mbox_check_func_id_8B}, {HINIC_PORT_CMD_GET_LINK_MODE, hinic_mbox_check_func_id_8B}, + {HINIC_PORT_CMD_GET_VF_COS, NULL}, + {HINIC_PORT_CMD_SET_VHD_CFG, hinic_mbox_check_func_id_8B}, + {HINIC_PORT_CMD_SET_VLAN_FILTER, hinic_mbox_check_func_id_8B}, + {HINIC_PORT_CMD_Q_FILTER, hinic_mbox_check_func_id_8B}, + {HINIC_PORT_CMD_TCAM_FILTER, NULL}, + {HINIC_PORT_CMD_UP_TC_ADD_FLOW, NULL}, + {HINIC_PORT_CMD_UP_TC_DEL_FLOW, NULL}, + {HINIC_PORT_CMD_UP_TC_FLUSH_TCAM, hinic_mbox_check_func_id_8B}, + {HINIC_PORT_CMD_UP_TC_CTRL_TCAM_BLOCK, hinic_mbox_check_func_id_8B}, + {HINIC_PORT_CMD_UP_TC_ENABLE, hinic_mbox_check_func_id_8B}, + {HINIC_PORT_CMD_CABLE_PLUG_EVENT, NULL}, + {HINIC_PORT_CMD_LINK_ERR_EVENT, NULL}, + {HINIC_PORT_CMD_SET_PORT_STATE, hinic_mbox_check_func_id_8B}, + {HINIC_PORT_CMD_SET_ETS, NULL}, + {HINIC_PORT_CMD_SET_ANTI_ATTACK_RATE, NULL}, + {HINIC_PORT_CMD_RESET_LINK_CFG, hinic_mbox_check_func_id_8B}, + {HINIC_PORT_CMD_SET_LINK_FOLLOW, NULL}, + {HINIC_PORT_CMD_CLEAR_QP_RES, NULL}, }; #define CHECK_IPSU_15BIT 0X8000 From 2acf960e3be6177f437baade6d1883ebe4c641b2 Mon Sep 17 00:00:00 2001 From: Cai Huoqing Date: Thu, 3 Nov 2022 16:05:11 +0800 Subject: [PATCH 0663/2002] net: hinic: Add support for configuration of rx-vlan-filter by ethtool When ethtool config rx-vlan-filter, the driver will send control command to firmware, then set to hardware in this patch. Signed-off-by: Cai Huoqing Signed-off-by: David S. Miller --- .../net/ethernet/huawei/hinic/hinic_main.c | 10 ++++++ .../net/ethernet/huawei/hinic/hinic_port.c | 33 +++++++++++++++++++ .../net/ethernet/huawei/hinic/hinic_port.h | 12 +++++++ 3 files changed, 55 insertions(+) diff --git a/drivers/net/ethernet/huawei/hinic/hinic_main.c b/drivers/net/ethernet/huawei/hinic/hinic_main.c index 9d4d795e1081..977c41473ab7 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_main.c +++ b/drivers/net/ethernet/huawei/hinic/hinic_main.c @@ -1092,6 +1092,16 @@ static int set_features(struct hinic_dev *nic_dev, } } + if (changed & NETIF_F_HW_VLAN_CTAG_FILTER) { + ret = hinic_set_vlan_fliter(nic_dev, + !!(features & + NETIF_F_HW_VLAN_CTAG_FILTER)); + if (ret) { + err = ret; + failed_features |= NETIF_F_HW_VLAN_CTAG_FILTER; + } + } + if (err) { nic_dev->netdev->features = features ^ failed_features; return -EIO; diff --git a/drivers/net/ethernet/huawei/hinic/hinic_port.c b/drivers/net/ethernet/huawei/hinic/hinic_port.c index 0a39c3dffa9a..9406237c461e 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_port.c +++ b/drivers/net/ethernet/huawei/hinic/hinic_port.c @@ -447,6 +447,39 @@ int hinic_set_rx_vlan_offload(struct hinic_dev *nic_dev, u8 en) return 0; } +int hinic_set_vlan_fliter(struct hinic_dev *nic_dev, u32 en) +{ + struct hinic_hwdev *hwdev = nic_dev->hwdev; + struct hinic_hwif *hwif = hwdev->hwif; + struct pci_dev *pdev = hwif->pdev; + struct hinic_vlan_filter vlan_filter; + u16 out_size = sizeof(vlan_filter); + int err; + + if (!hwdev) + return -EINVAL; + + vlan_filter.func_idx = HINIC_HWIF_FUNC_IDX(hwif); + vlan_filter.enable = en; + + err = hinic_port_msg_cmd(hwdev, HINIC_PORT_CMD_SET_VLAN_FILTER, + &vlan_filter, sizeof(vlan_filter), + &vlan_filter, &out_size); + if (vlan_filter.status == HINIC_MGMT_CMD_UNSUPPORTED) { + err = HINIC_MGMT_CMD_UNSUPPORTED; + } else if ((err == HINIC_MBOX_VF_CMD_ERROR) && + HINIC_IS_VF(hwif)) { + err = HINIC_MGMT_CMD_UNSUPPORTED; + } else if (err || !out_size || vlan_filter.status) { + dev_err(&pdev->dev, + "Failed to set vlan fliter, err: %d, status: 0x%x, out size: 0x%x\n", + err, vlan_filter.status, out_size); + err = -EINVAL; + } + + return err; +} + int hinic_set_max_qnum(struct hinic_dev *nic_dev, u8 num_rqs) { struct hinic_hwdev *hwdev = nic_dev->hwdev; diff --git a/drivers/net/ethernet/huawei/hinic/hinic_port.h b/drivers/net/ethernet/huawei/hinic/hinic_port.h index c9ae3d4dc547..c8694ac7c702 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_port.h +++ b/drivers/net/ethernet/huawei/hinic/hinic_port.h @@ -351,6 +351,16 @@ struct hinic_vlan_cfg { u8 rsvd1[5]; }; +struct hinic_vlan_filter { + u8 status; + u8 version; + u8 rsvd0[6]; + + u16 func_idx; + u8 rsvd1[2]; + u32 enable; +}; + struct hinic_rss_template_mgmt { u8 status; u8 version; @@ -831,6 +841,8 @@ int hinic_get_vport_stats(struct hinic_dev *nic_dev, int hinic_set_rx_vlan_offload(struct hinic_dev *nic_dev, u8 en); +int hinic_set_vlan_fliter(struct hinic_dev *nic_dev, u32 en); + int hinic_get_mgmt_version(struct hinic_dev *nic_dev, u8 *mgmt_ver); int hinic_set_link_settings(struct hinic_hwdev *hwdev, From c053d7b6bdcb45780036b32be6a950f71a78bf52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilpo=20J=C3=A4rvinen?= Date: Thu, 3 Nov 2022 14:48:28 +0530 Subject: [PATCH 0664/2002] net: wwan: t7xx: Use needed_headroom instead of hard_header_len MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit hard_header_len is used by gro_list_prepare() but on Rx, there is no header so use needed_headroom instead. Signed-off-by: Ilpo Järvinen Signed-off-by: Sreehari Kancharla Reviewed-by: Sergey Ryazanov Signed-off-by: David S. Miller --- drivers/net/wwan/t7xx/t7xx_netdev.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wwan/t7xx/t7xx_netdev.c b/drivers/net/wwan/t7xx/t7xx_netdev.c index f71d3bc3b237..7639846fa3df 100644 --- a/drivers/net/wwan/t7xx/t7xx_netdev.c +++ b/drivers/net/wwan/t7xx/t7xx_netdev.c @@ -161,7 +161,7 @@ static void t7xx_ccmni_post_stop(struct t7xx_ccmni_ctrl *ctlb) static void t7xx_ccmni_wwan_setup(struct net_device *dev) { - dev->hard_header_len += sizeof(struct ccci_header); + dev->needed_headroom += sizeof(struct ccci_header); dev->mtu = ETH_DATA_LEN; dev->max_mtu = CCMNI_MTU_MAX; From 5545b7b9f294de7f95ec6a7cb1de0db52296001c Mon Sep 17 00:00:00 2001 From: Haijun Liu Date: Thu, 3 Nov 2022 14:48:29 +0530 Subject: [PATCH 0665/2002] net: wwan: t7xx: Add NAPI support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace the work queue based RX flow with a NAPI implementation Remove rx_thread and dpmaif_rxq_work. Enable GRO on RX path. Introduce dummy network device. its responsibility is - Binds one NAPI object for each DL HW queue and acts as the agent of all those network devices. - Use NAPI object to poll DL packets. - Helps to dispatch each packet to the network interface. Signed-off-by: Haijun Liu Co-developed-by: Sreehari Kancharla Signed-off-by: Sreehari Kancharla Signed-off-by: Chandrashekar Devegowda Acked-by: Ricardo Martinez Acked-by: M Chetan Kumar Reviewed-by: Ilpo Järvinen Signed-off-by: David S. Miller --- drivers/net/wwan/t7xx/t7xx_hif_dpmaif.h | 14 +- drivers/net/wwan/t7xx/t7xx_hif_dpmaif_rx.c | 218 +++++++-------------- drivers/net/wwan/t7xx/t7xx_hif_dpmaif_rx.h | 1 + drivers/net/wwan/t7xx/t7xx_netdev.c | 89 ++++++++- drivers/net/wwan/t7xx/t7xx_netdev.h | 5 + 5 files changed, 161 insertions(+), 166 deletions(-) diff --git a/drivers/net/wwan/t7xx/t7xx_hif_dpmaif.h b/drivers/net/wwan/t7xx/t7xx_hif_dpmaif.h index 1225ca0ed51e..0ce4505e813d 100644 --- a/drivers/net/wwan/t7xx/t7xx_hif_dpmaif.h +++ b/drivers/net/wwan/t7xx/t7xx_hif_dpmaif.h @@ -20,6 +20,7 @@ #include #include +#include #include #include #include @@ -109,20 +110,14 @@ struct dpmaif_rx_queue { struct dpmaif_bat_request *bat_req; struct dpmaif_bat_request *bat_frag; - wait_queue_head_t rx_wq; - struct task_struct *rx_thread; - struct sk_buff_head skb_list; - unsigned int skb_list_max_len; - - struct workqueue_struct *worker; - struct work_struct dpmaif_rxq_work; - atomic_t rx_processing; struct dpmaif_ctrl *dpmaif_ctrl; unsigned int expect_pit_seq; unsigned int pit_remain_release_cnt; struct dpmaif_cur_rx_skb_info rx_data_info; + struct napi_struct napi; + bool sleep_lock_pending; }; struct dpmaif_tx_queue { @@ -168,7 +163,8 @@ enum dpmaif_txq_state { struct dpmaif_callbacks { void (*state_notify)(struct t7xx_pci_dev *t7xx_dev, enum dpmaif_txq_state state, int txq_number); - void (*recv_skb)(struct t7xx_pci_dev *t7xx_dev, struct sk_buff *skb); + void (*recv_skb)(struct t7xx_ccmni_ctrl *ccmni_ctlb, struct sk_buff *skb, + struct napi_struct *napi); }; struct dpmaif_ctrl { diff --git a/drivers/net/wwan/t7xx/t7xx_hif_dpmaif_rx.c b/drivers/net/wwan/t7xx/t7xx_hif_dpmaif_rx.c index 91a0eb19e0d8..aa2174a10437 100644 --- a/drivers/net/wwan/t7xx/t7xx_hif_dpmaif_rx.c +++ b/drivers/net/wwan/t7xx/t7xx_hif_dpmaif_rx.c @@ -45,6 +45,7 @@ #include "t7xx_dpmaif.h" #include "t7xx_hif_dpmaif.h" #include "t7xx_hif_dpmaif_rx.h" +#include "t7xx_netdev.h" #include "t7xx_pci.h" #define DPMAIF_BAT_COUNT 8192 @@ -76,43 +77,6 @@ static unsigned int t7xx_normal_pit_bid(const struct dpmaif_pit *pit_info) return value; } -static int t7xx_dpmaif_net_rx_push_thread(void *arg) -{ - struct dpmaif_rx_queue *q = arg; - struct dpmaif_ctrl *hif_ctrl; - struct dpmaif_callbacks *cb; - - hif_ctrl = q->dpmaif_ctrl; - cb = hif_ctrl->callbacks; - - while (!kthread_should_stop()) { - struct sk_buff *skb; - unsigned long flags; - - if (skb_queue_empty(&q->skb_list)) { - if (wait_event_interruptible(q->rx_wq, - !skb_queue_empty(&q->skb_list) || - kthread_should_stop())) - continue; - - if (kthread_should_stop()) - break; - } - - spin_lock_irqsave(&q->skb_list.lock, flags); - skb = __skb_dequeue(&q->skb_list); - spin_unlock_irqrestore(&q->skb_list.lock, flags); - - if (!skb) - continue; - - cb->recv_skb(hif_ctrl->t7xx_dev, skb); - cond_resched(); - } - - return 0; -} - static int t7xx_dpmaif_update_bat_wr_idx(struct dpmaif_ctrl *dpmaif_ctrl, const unsigned int q_num, const unsigned int bat_cnt) { @@ -726,21 +690,10 @@ static int t7xx_dpmaifq_rx_notify_hw(struct dpmaif_rx_queue *rxq) return ret; } -static void t7xx_dpmaif_rx_skb_enqueue(struct dpmaif_rx_queue *rxq, struct sk_buff *skb) -{ - unsigned long flags; - - spin_lock_irqsave(&rxq->skb_list.lock, flags); - if (rxq->skb_list.qlen < rxq->skb_list_max_len) - __skb_queue_tail(&rxq->skb_list, skb); - else - dev_kfree_skb_any(skb); - spin_unlock_irqrestore(&rxq->skb_list.lock, flags); -} - static void t7xx_dpmaif_rx_skb(struct dpmaif_rx_queue *rxq, struct dpmaif_cur_rx_skb_info *skb_info) { + struct dpmaif_ctrl *dpmaif_ctrl = rxq->dpmaif_ctrl; struct sk_buff *skb = skb_info->cur_skb; struct t7xx_skb_cb *skb_cb; u8 netif_id; @@ -758,11 +711,11 @@ static void t7xx_dpmaif_rx_skb(struct dpmaif_rx_queue *rxq, skb_cb = T7XX_SKB_CB(skb); skb_cb->netif_idx = netif_id; skb_cb->rx_pkt_type = skb_info->pkt_type; - t7xx_dpmaif_rx_skb_enqueue(rxq, skb); + dpmaif_ctrl->callbacks->recv_skb(dpmaif_ctrl->t7xx_dev->ccmni_ctlb, skb, &rxq->napi); } static int t7xx_dpmaif_rx_start(struct dpmaif_rx_queue *rxq, const unsigned int pit_cnt, - const unsigned long timeout) + const unsigned int budget, int *once_more) { unsigned int cur_pit, pit_len, rx_cnt, recv_skb_cnt = 0; struct device *dev = rxq->dpmaif_ctrl->dev; @@ -777,13 +730,14 @@ static int t7xx_dpmaif_rx_start(struct dpmaif_rx_queue *rxq, const unsigned int struct dpmaif_pit *pkt_info; u32 val; - if (!skb_info->msg_pit_received && time_after_eq(jiffies, timeout)) + if (!skb_info->msg_pit_received && recv_skb_cnt >= budget) break; pkt_info = (struct dpmaif_pit *)rxq->pit_base + cur_pit; if (t7xx_dpmaif_check_pit_seq(rxq, pkt_info)) { dev_err_ratelimited(dev, "RXQ%u checks PIT SEQ fail\n", rxq->index); - return -EAGAIN; + *once_more = 1; + return recv_skb_cnt; } val = FIELD_GET(PD_PIT_PACKET_TYPE, le32_to_cpu(pkt_info->header)); @@ -817,12 +771,7 @@ static int t7xx_dpmaif_rx_start(struct dpmaif_rx_queue *rxq, const unsigned int } memset(skb_info, 0, sizeof(*skb_info)); - recv_skb_cnt++; - if (!(recv_skb_cnt & DPMAIF_RX_PUSH_THRESHOLD_MASK)) { - wake_up_all(&rxq->rx_wq); - recv_skb_cnt = 0; - } } } @@ -837,16 +786,13 @@ static int t7xx_dpmaif_rx_start(struct dpmaif_rx_queue *rxq, const unsigned int } } - if (recv_skb_cnt) - wake_up_all(&rxq->rx_wq); - if (!ret) ret = t7xx_dpmaifq_rx_notify_hw(rxq); if (ret) return ret; - return rx_cnt; + return recv_skb_cnt; } static unsigned int t7xx_dpmaifq_poll_pit(struct dpmaif_rx_queue *rxq) @@ -863,53 +809,30 @@ static unsigned int t7xx_dpmaifq_poll_pit(struct dpmaif_rx_queue *rxq) return pit_cnt; } -static int t7xx_dpmaif_rx_data_collect(struct dpmaif_ctrl *dpmaif_ctrl, - const unsigned int q_num, const unsigned int budget) +static int t7xx_dpmaif_napi_rx_data_collect(struct dpmaif_ctrl *dpmaif_ctrl, + const unsigned int q_num, + const unsigned int budget, int *once_more) { struct dpmaif_rx_queue *rxq = &dpmaif_ctrl->rxq[q_num]; - unsigned long time_limit; unsigned int cnt; + int ret = 0; - time_limit = jiffies + msecs_to_jiffies(DPMAIF_WQ_TIME_LIMIT_MS); + cnt = t7xx_dpmaifq_poll_pit(rxq); + if (!cnt) + return ret; - while ((cnt = t7xx_dpmaifq_poll_pit(rxq))) { - unsigned int rd_cnt; - int real_cnt; + ret = t7xx_dpmaif_rx_start(rxq, cnt, budget, once_more); + if (ret < 0) + dev_err(dpmaif_ctrl->dev, "dlq%u rx ERR:%d\n", rxq->index, ret); - rd_cnt = min(cnt, budget); - - real_cnt = t7xx_dpmaif_rx_start(rxq, rd_cnt, time_limit); - if (real_cnt < 0) - return real_cnt; - - if (real_cnt < cnt) - return -EAGAIN; - } - - return 0; + return ret; } -static void t7xx_dpmaif_do_rx(struct dpmaif_ctrl *dpmaif_ctrl, struct dpmaif_rx_queue *rxq) +int t7xx_dpmaif_napi_rx_poll(struct napi_struct *napi, const int budget) { - struct dpmaif_hw_info *hw_info = &dpmaif_ctrl->hw_info; - int ret; - - ret = t7xx_dpmaif_rx_data_collect(dpmaif_ctrl, rxq->index, rxq->budget); - if (ret < 0) { - /* Try one more time */ - queue_work(rxq->worker, &rxq->dpmaif_rxq_work); - t7xx_dpmaif_clr_ip_busy_sts(hw_info); - } else { - t7xx_dpmaif_clr_ip_busy_sts(hw_info); - t7xx_dpmaif_dlq_unmask_rx_done(hw_info, rxq->index); - } -} - -static void t7xx_dpmaif_rxq_work(struct work_struct *work) -{ - struct dpmaif_rx_queue *rxq = container_of(work, struct dpmaif_rx_queue, dpmaif_rxq_work); - struct dpmaif_ctrl *dpmaif_ctrl = rxq->dpmaif_ctrl; - int ret; + struct dpmaif_rx_queue *rxq = container_of(napi, struct dpmaif_rx_queue, napi); + struct t7xx_pci_dev *t7xx_dev = rxq->dpmaif_ctrl->t7xx_dev; + int ret, once_more = 0, work_done = 0; atomic_set(&rxq->rx_processing, 1); /* Ensure rx_processing is changed to 1 before actually begin RX flow */ @@ -917,22 +840,52 @@ static void t7xx_dpmaif_rxq_work(struct work_struct *work) if (!rxq->que_started) { atomic_set(&rxq->rx_processing, 0); - dev_err(dpmaif_ctrl->dev, "Work RXQ: %d has not been started\n", rxq->index); - return; + dev_err(rxq->dpmaif_ctrl->dev, "Work RXQ: %d has not been started\n", rxq->index); + return work_done; } - ret = pm_runtime_resume_and_get(dpmaif_ctrl->dev); - if (ret < 0 && ret != -EACCES) - return; + if (!rxq->sleep_lock_pending) { + pm_runtime_get_noresume(rxq->dpmaif_ctrl->dev); + t7xx_pci_disable_sleep(t7xx_dev); + } - t7xx_pci_disable_sleep(dpmaif_ctrl->t7xx_dev); - if (t7xx_pci_sleep_disable_complete(dpmaif_ctrl->t7xx_dev)) - t7xx_dpmaif_do_rx(dpmaif_ctrl, rxq); + ret = try_wait_for_completion(&t7xx_dev->sleep_lock_acquire); + if (!ret) { + napi_complete_done(napi, work_done); + rxq->sleep_lock_pending = true; + napi_reschedule(napi); + return work_done; + } - t7xx_pci_enable_sleep(dpmaif_ctrl->t7xx_dev); - pm_runtime_mark_last_busy(dpmaif_ctrl->dev); - pm_runtime_put_autosuspend(dpmaif_ctrl->dev); + rxq->sleep_lock_pending = false; + while (work_done < budget) { + int each_budget = budget - work_done; + int rx_cnt = t7xx_dpmaif_napi_rx_data_collect(rxq->dpmaif_ctrl, rxq->index, + each_budget, &once_more); + if (rx_cnt > 0) + work_done += rx_cnt; + else + break; + } + + if (once_more) { + napi_gro_flush(napi, false); + work_done = budget; + t7xx_dpmaif_clr_ip_busy_sts(&rxq->dpmaif_ctrl->hw_info); + } else if (work_done < budget) { + napi_complete_done(napi, work_done); + t7xx_dpmaif_clr_ip_busy_sts(&rxq->dpmaif_ctrl->hw_info); + t7xx_dpmaif_dlq_unmask_rx_done(&rxq->dpmaif_ctrl->hw_info, rxq->index); + } else { + t7xx_dpmaif_clr_ip_busy_sts(&rxq->dpmaif_ctrl->hw_info); + } + + t7xx_pci_enable_sleep(rxq->dpmaif_ctrl->t7xx_dev); + pm_runtime_mark_last_busy(rxq->dpmaif_ctrl->dev); + pm_runtime_put_noidle(rxq->dpmaif_ctrl->dev); atomic_set(&rxq->rx_processing, 0); + + return work_done; } void t7xx_dpmaif_irq_rx_done(struct dpmaif_ctrl *dpmaif_ctrl, const unsigned int que_mask) @@ -947,7 +900,7 @@ void t7xx_dpmaif_irq_rx_done(struct dpmaif_ctrl *dpmaif_ctrl, const unsigned int } rxq = &dpmaif_ctrl->rxq[qno]; - queue_work(rxq->worker, &rxq->dpmaif_rxq_work); + napi_schedule(&rxq->napi); } static void t7xx_dpmaif_base_free(const struct dpmaif_ctrl *dpmaif_ctrl, @@ -1082,50 +1035,14 @@ int t7xx_dpmaif_rxq_init(struct dpmaif_rx_queue *queue) int ret; ret = t7xx_dpmaif_rx_alloc(queue); - if (ret < 0) { + if (ret < 0) dev_err(queue->dpmaif_ctrl->dev, "Failed to allocate RX buffers: %d\n", ret); - return ret; - } - - INIT_WORK(&queue->dpmaif_rxq_work, t7xx_dpmaif_rxq_work); - - queue->worker = alloc_workqueue("dpmaif_rx%d_worker", - WQ_UNBOUND | WQ_MEM_RECLAIM | WQ_HIGHPRI, 1, queue->index); - if (!queue->worker) { - ret = -ENOMEM; - goto err_free_rx_buffer; - } - - init_waitqueue_head(&queue->rx_wq); - skb_queue_head_init(&queue->skb_list); - queue->skb_list_max_len = queue->bat_req->pkt_buf_sz; - queue->rx_thread = kthread_run(t7xx_dpmaif_net_rx_push_thread, - queue, "dpmaif_rx%d_push", queue->index); - - ret = PTR_ERR_OR_ZERO(queue->rx_thread); - if (ret) - goto err_free_workqueue; - - return 0; - -err_free_workqueue: - destroy_workqueue(queue->worker); - -err_free_rx_buffer: - t7xx_dpmaif_rx_buf_free(queue); return ret; } void t7xx_dpmaif_rxq_free(struct dpmaif_rx_queue *queue) { - if (queue->worker) - destroy_workqueue(queue->worker); - - if (queue->rx_thread) - kthread_stop(queue->rx_thread); - - skb_queue_purge(&queue->skb_list); t7xx_dpmaif_rx_buf_free(queue); } @@ -1188,8 +1105,6 @@ void t7xx_dpmaif_rx_stop(struct dpmaif_ctrl *dpmaif_ctrl) struct dpmaif_rx_queue *rxq = &dpmaif_ctrl->rxq[i]; int timeout, value; - flush_work(&rxq->dpmaif_rxq_work); - timeout = readx_poll_timeout_atomic(atomic_read, &rxq->rx_processing, value, !value, 0, DPMAIF_CHECK_INIT_TIMEOUT_US); if (timeout) @@ -1205,7 +1120,6 @@ static void t7xx_dpmaif_stop_rxq(struct dpmaif_rx_queue *rxq) { int cnt, j = 0; - flush_work(&rxq->dpmaif_rxq_work); rxq->que_started = false; do { diff --git a/drivers/net/wwan/t7xx/t7xx_hif_dpmaif_rx.h b/drivers/net/wwan/t7xx/t7xx_hif_dpmaif_rx.h index 182f62dfe398..f4e1b69ad426 100644 --- a/drivers/net/wwan/t7xx/t7xx_hif_dpmaif_rx.h +++ b/drivers/net/wwan/t7xx/t7xx_hif_dpmaif_rx.h @@ -112,5 +112,6 @@ int t7xx_dpmaif_bat_alloc(const struct dpmaif_ctrl *dpmaif_ctrl, struct dpmaif_b const enum bat_type buf_type); void t7xx_dpmaif_bat_free(const struct dpmaif_ctrl *dpmaif_ctrl, struct dpmaif_bat_request *bat_req); +int t7xx_dpmaif_napi_rx_poll(struct napi_struct *napi, const int budget); #endif /* __T7XX_HIF_DPMA_RX_H__ */ diff --git a/drivers/net/wwan/t7xx/t7xx_netdev.c b/drivers/net/wwan/t7xx/t7xx_netdev.c index 7639846fa3df..494a28e386a3 100644 --- a/drivers/net/wwan/t7xx/t7xx_netdev.c +++ b/drivers/net/wwan/t7xx/t7xx_netdev.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -29,6 +30,7 @@ #include #include #include +#include #include #include "t7xx_hif_dpmaif_rx.h" @@ -39,13 +41,47 @@ #include "t7xx_state_monitor.h" #define IP_MUX_SESSION_DEFAULT 0 +#define SBD_PACKET_TYPE_MASK GENMASK(7, 4) + +static void t7xx_ccmni_enable_napi(struct t7xx_ccmni_ctrl *ctlb) +{ + int i; + + if (ctlb->is_napi_en) + return; + + for (i = 0; i < RXQ_NUM; i++) { + napi_enable(ctlb->napi[i]); + napi_schedule(ctlb->napi[i]); + } + ctlb->is_napi_en = true; +} + +static void t7xx_ccmni_disable_napi(struct t7xx_ccmni_ctrl *ctlb) +{ + int i; + + if (!ctlb->is_napi_en) + return; + + for (i = 0; i < RXQ_NUM; i++) { + napi_synchronize(ctlb->napi[i]); + napi_disable(ctlb->napi[i]); + } + + ctlb->is_napi_en = false; +} static int t7xx_ccmni_open(struct net_device *dev) { struct t7xx_ccmni *ccmni = wwan_netdev_drvpriv(dev); + struct t7xx_ccmni_ctrl *ccmni_ctl = ccmni->ctlb; netif_carrier_on(dev); netif_tx_start_all_queues(dev); + if (!atomic_fetch_inc(&ccmni_ctl->napi_usr_refcnt)) + t7xx_ccmni_enable_napi(ccmni_ctl); + atomic_inc(&ccmni->usage); return 0; } @@ -53,8 +89,12 @@ static int t7xx_ccmni_open(struct net_device *dev) static int t7xx_ccmni_close(struct net_device *dev) { struct t7xx_ccmni *ccmni = wwan_netdev_drvpriv(dev); + struct t7xx_ccmni_ctrl *ccmni_ctl = ccmni->ctlb; atomic_dec(&ccmni->usage); + if (atomic_dec_and_test(&ccmni_ctl->napi_usr_refcnt)) + t7xx_ccmni_disable_napi(ccmni_ctl); + netif_carrier_off(dev); netif_tx_disable(dev); return 0; @@ -127,6 +167,9 @@ static void t7xx_ccmni_start(struct t7xx_ccmni_ctrl *ctlb) netif_carrier_on(ccmni->dev); } } + + if (atomic_read(&ctlb->napi_usr_refcnt)) + t7xx_ccmni_enable_napi(ctlb); } static void t7xx_ccmni_pre_stop(struct t7xx_ccmni_ctrl *ctlb) @@ -149,6 +192,9 @@ static void t7xx_ccmni_post_stop(struct t7xx_ccmni_ctrl *ctlb) struct t7xx_ccmni *ccmni; int i; + if (atomic_read(&ctlb->napi_usr_refcnt)) + t7xx_ccmni_disable_napi(ctlb); + for (i = 0; i < ctlb->nic_dev_num; i++) { ccmni = ctlb->ccmni_inst[i]; if (!ccmni) @@ -183,6 +229,9 @@ static void t7xx_ccmni_wwan_setup(struct net_device *dev) dev->features |= NETIF_F_RXCSUM; dev->hw_features |= NETIF_F_RXCSUM; + dev->features |= NETIF_F_GRO; + dev->hw_features |= NETIF_F_GRO; + dev->needs_free_netdev = true; dev->type = ARPHRD_NONE; @@ -190,6 +239,34 @@ static void t7xx_ccmni_wwan_setup(struct net_device *dev) dev->netdev_ops = &ccmni_netdev_ops; } +static void t7xx_init_netdev_napi(struct t7xx_ccmni_ctrl *ctlb) +{ + int i; + + /* one HW, but shared with multiple net devices, + * so add a dummy device for NAPI. + */ + init_dummy_netdev(&ctlb->dummy_dev); + atomic_set(&ctlb->napi_usr_refcnt, 0); + ctlb->is_napi_en = false; + + for (i = 0; i < RXQ_NUM; i++) { + ctlb->napi[i] = &ctlb->hif_ctrl->rxq[i].napi; + netif_napi_add_weight(&ctlb->dummy_dev, ctlb->napi[i], t7xx_dpmaif_napi_rx_poll, + NIC_NAPI_POLL_BUDGET); + } +} + +static void t7xx_uninit_netdev_napi(struct t7xx_ccmni_ctrl *ctlb) +{ + int i; + + for (i = 0; i < RXQ_NUM; i++) { + netif_napi_del(ctlb->napi[i]); + ctlb->napi[i] = NULL; + } +} + static int t7xx_ccmni_wwan_newlink(void *ctxt, struct net_device *dev, u32 if_id, struct netlink_ext_ack *extack) { @@ -311,7 +388,8 @@ static void init_md_status_notifier(struct t7xx_pci_dev *t7xx_dev) t7xx_fsm_notifier_register(t7xx_dev->md, md_status_notifier); } -static void t7xx_ccmni_recv_skb(struct t7xx_pci_dev *t7xx_dev, struct sk_buff *skb) +static void t7xx_ccmni_recv_skb(struct t7xx_ccmni_ctrl *ccmni_ctlb, struct sk_buff *skb, + struct napi_struct *napi) { struct t7xx_skb_cb *skb_cb; struct net_device *net_dev; @@ -321,23 +399,22 @@ static void t7xx_ccmni_recv_skb(struct t7xx_pci_dev *t7xx_dev, struct sk_buff *s skb_cb = T7XX_SKB_CB(skb); netif_id = skb_cb->netif_idx; - ccmni = t7xx_dev->ccmni_ctlb->ccmni_inst[netif_id]; + ccmni = ccmni_ctlb->ccmni_inst[netif_id]; if (!ccmni) { dev_kfree_skb(skb); return; } net_dev = ccmni->dev; - skb->dev = net_dev; - pkt_type = skb_cb->rx_pkt_type; + skb->dev = net_dev; if (pkt_type == PKT_TYPE_IP6) skb->protocol = htons(ETH_P_IPV6); else skb->protocol = htons(ETH_P_IP); skb_len = skb->len; - netif_rx(skb); + napi_gro_receive(napi, skb); net_dev->stats.rx_packets++; net_dev->stats.rx_bytes += skb_len; } @@ -404,6 +481,7 @@ int t7xx_ccmni_init(struct t7xx_pci_dev *t7xx_dev) if (!ctlb->hif_ctrl) return -ENOMEM; + t7xx_init_netdev_napi(ctlb); init_md_status_notifier(t7xx_dev); return 0; } @@ -419,5 +497,6 @@ void t7xx_ccmni_exit(struct t7xx_pci_dev *t7xx_dev) ctlb->wwan_is_registered = false; } + t7xx_uninit_netdev_napi(ctlb); t7xx_dpmaif_hif_exit(ctlb->hif_ctrl); } diff --git a/drivers/net/wwan/t7xx/t7xx_netdev.h b/drivers/net/wwan/t7xx/t7xx_netdev.h index f5ad49ca12a7..f5ed6f99a145 100644 --- a/drivers/net/wwan/t7xx/t7xx_netdev.h +++ b/drivers/net/wwan/t7xx/t7xx_netdev.h @@ -30,6 +30,7 @@ #define CCMNI_NETDEV_WDT_TO (1 * HZ) #define CCMNI_MTU_MAX 3000 +#define NIC_NAPI_POLL_BUDGET 128 struct t7xx_ccmni { u8 index; @@ -47,6 +48,10 @@ struct t7xx_ccmni_ctrl { unsigned int md_sta; struct t7xx_fsm_notifier md_status_notify; bool wwan_is_registered; + struct net_device dummy_dev; + struct napi_struct *napi[RXQ_NUM]; + atomic_t napi_usr_refcnt; + bool is_napi_en; }; int t7xx_ccmni_init(struct t7xx_pci_dev *t7xx_dev); From aa5bf80c3c067b82b4362cd6e8e2194623bcaca6 Mon Sep 17 00:00:00 2001 From: Nathan Chancellor Date: Thu, 3 Nov 2022 10:01:28 -0700 Subject: [PATCH 0666/2002] s390/ctcm: Fix return type of ctc{mp,}m_tx() With clang's kernel control flow integrity (kCFI, CONFIG_CFI_CLANG), indirect call targets are validated against the expected function pointer prototype to make sure the call target is valid to help mitigate ROP attacks. If they are not identical, there is a failure at run time, which manifests as either a kernel panic or thread getting killed. A proposed warning in clang aims to catch these at compile time, which reveals: drivers/s390/net/ctcm_main.c:1064:21: error: incompatible function pointer types initializing 'netdev_tx_t (*)(struct sk_buff *, struct net_device *)' (aka 'enum netdev_tx (*)(struct sk_buff *, struct net_device *)') with an expression of type 'int (struct sk_buff *, struct net_device *)' [-Werror,-Wincompatible-function-pointer-types-strict] .ndo_start_xmit = ctcm_tx, ^~~~~~~ drivers/s390/net/ctcm_main.c:1072:21: error: incompatible function pointer types initializing 'netdev_tx_t (*)(struct sk_buff *, struct net_device *)' (aka 'enum netdev_tx (*)(struct sk_buff *, struct net_device *)') with an expression of type 'int (struct sk_buff *, struct net_device *)' [-Werror,-Wincompatible-function-pointer-types-strict] .ndo_start_xmit = ctcmpc_tx, ^~~~~~~~~ ->ndo_start_xmit() in 'struct net_device_ops' expects a return type of 'netdev_tx_t', not 'int'. Adjust the return type of ctc{mp,}m_tx() to match the prototype's to resolve the warning and potential CFI failure, should s390 select ARCH_SUPPORTS_CFI_CLANG in the future. Additionally, while in the area, remove a comment block that is no longer relevant. Link: https://github.com/ClangBuiltLinux/linux/issues/1750 Reviewed-by: Alexandra Winter Reviewed-by: Kees Cook Signed-off-by: Nathan Chancellor Signed-off-by: David S. Miller --- drivers/s390/net/ctcm_main.c | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/drivers/s390/net/ctcm_main.c b/drivers/s390/net/ctcm_main.c index 37b551bd43bf..bdfab9ea0046 100644 --- a/drivers/s390/net/ctcm_main.c +++ b/drivers/s390/net/ctcm_main.c @@ -825,16 +825,9 @@ done: /* * Start transmission of a packet. * Called from generic network device layer. - * - * skb Pointer to buffer containing the packet. - * dev Pointer to interface struct. - * - * returns 0 if packet consumed, !0 if packet rejected. - * Note: If we return !0, then the packet is free'd by - * the generic network layer. */ /* first merge version - leaving both functions separated */ -static int ctcm_tx(struct sk_buff *skb, struct net_device *dev) +static netdev_tx_t ctcm_tx(struct sk_buff *skb, struct net_device *dev) { struct ctcm_priv *priv = dev->ml_priv; @@ -877,7 +870,7 @@ static int ctcm_tx(struct sk_buff *skb, struct net_device *dev) } /* unmerged MPC variant of ctcm_tx */ -static int ctcmpc_tx(struct sk_buff *skb, struct net_device *dev) +static netdev_tx_t ctcmpc_tx(struct sk_buff *skb, struct net_device *dev) { int len = 0; struct ctcm_priv *priv = dev->ml_priv; From 88d86d18d7cf7e9137c95f9d212bb9fff8a1b4be Mon Sep 17 00:00:00 2001 From: Nathan Chancellor Date: Thu, 3 Nov 2022 10:01:29 -0700 Subject: [PATCH 0667/2002] s390/netiucv: Fix return type of netiucv_tx() With clang's kernel control flow integrity (kCFI, CONFIG_CFI_CLANG), indirect call targets are validated against the expected function pointer prototype to make sure the call target is valid to help mitigate ROP attacks. If they are not identical, there is a failure at run time, which manifests as either a kernel panic or thread getting killed. A proposed warning in clang aims to catch these at compile time, which reveals: drivers/s390/net/netiucv.c:1854:21: error: incompatible function pointer types initializing 'netdev_tx_t (*)(struct sk_buff *, struct net_device *)' (aka 'enum netdev_tx (*)(struct sk_buff *, struct net_device *)') with an expression of type 'int (struct sk_buff *, struct net_device *)' [-Werror,-Wincompatible-function-pointer-types-strict] .ndo_start_xmit = netiucv_tx, ^~~~~~~~~~ ->ndo_start_xmit() in 'struct net_device_ops' expects a return type of 'netdev_tx_t', not 'int'. Adjust the return type of netiucv_tx() to match the prototype's to resolve the warning and potential CFI failure, should s390 select ARCH_SUPPORTS_CFI_CLANG in the future. Additionally, while in the area, remove a comment block that is no longer relevant. Link: https://github.com/ClangBuiltLinux/linux/issues/1750 Reviewed-by: Alexandra Winter Reviewed-by: Kees Cook Signed-off-by: Nathan Chancellor Signed-off-by: David S. Miller --- drivers/s390/net/netiucv.c | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/drivers/s390/net/netiucv.c b/drivers/s390/net/netiucv.c index 65aa0a96c21d..66076cada8ae 100644 --- a/drivers/s390/net/netiucv.c +++ b/drivers/s390/net/netiucv.c @@ -1248,15 +1248,8 @@ static int netiucv_close(struct net_device *dev) /* * Start transmission of a packet. * Called from generic network device layer. - * - * @param skb Pointer to buffer containing the packet. - * @param dev Pointer to interface struct. - * - * @return 0 if packet consumed, !0 if packet rejected. - * Note: If we return !0, then the packet is free'd by - * the generic network layer. */ -static int netiucv_tx(struct sk_buff *skb, struct net_device *dev) +static netdev_tx_t netiucv_tx(struct sk_buff *skb, struct net_device *dev) { struct netiucv_priv *privptr = netdev_priv(dev); int rc; From bb16db8393658e0978c3f0d30ae069e878264fa3 Mon Sep 17 00:00:00 2001 From: Nathan Chancellor Date: Thu, 3 Nov 2022 10:01:30 -0700 Subject: [PATCH 0668/2002] s390/lcs: Fix return type of lcs_start_xmit() With clang's kernel control flow integrity (kCFI, CONFIG_CFI_CLANG), indirect call targets are validated against the expected function pointer prototype to make sure the call target is valid to help mitigate ROP attacks. If they are not identical, there is a failure at run time, which manifests as either a kernel panic or thread getting killed. A proposed warning in clang aims to catch these at compile time, which reveals: drivers/s390/net/lcs.c:2090:21: error: incompatible function pointer types initializing 'netdev_tx_t (*)(struct sk_buff *, struct net_device *)' (aka 'enum netdev_tx (*)(struct sk_buff *, struct net_device *)') with an expression of type 'int (struct sk_buff *, struct net_device *)' [-Werror,-Wincompatible-function-pointer-types-strict] .ndo_start_xmit = lcs_start_xmit, ^~~~~~~~~~~~~~ drivers/s390/net/lcs.c:2097:21: error: incompatible function pointer types initializing 'netdev_tx_t (*)(struct sk_buff *, struct net_device *)' (aka 'enum netdev_tx (*)(struct sk_buff *, struct net_device *)') with an expression of type 'int (struct sk_buff *, struct net_device *)' [-Werror,-Wincompatible-function-pointer-types-strict] .ndo_start_xmit = lcs_start_xmit, ^~~~~~~~~~~~~~ ->ndo_start_xmit() in 'struct net_device_ops' expects a return type of 'netdev_tx_t', not 'int'. Adjust the return type of lcs_start_xmit() to match the prototype's to resolve the warning and potential CFI failure, should s390 select ARCH_SUPPORTS_CFI_CLANG in the future. Link: https://github.com/ClangBuiltLinux/linux/issues/1750 Reviewed-by: Alexandra Winter Reviewed-by: Kees Cook Signed-off-by: Nathan Chancellor Signed-off-by: David S. Miller --- drivers/s390/net/lcs.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/drivers/s390/net/lcs.c b/drivers/s390/net/lcs.c index 84c8981317b4..38f312664ce7 100644 --- a/drivers/s390/net/lcs.c +++ b/drivers/s390/net/lcs.c @@ -1519,9 +1519,8 @@ lcs_txbuffer_cb(struct lcs_channel *channel, struct lcs_buffer *buffer) /* * Packet transmit function called by network stack */ -static int -__lcs_start_xmit(struct lcs_card *card, struct sk_buff *skb, - struct net_device *dev) +static netdev_tx_t __lcs_start_xmit(struct lcs_card *card, struct sk_buff *skb, + struct net_device *dev) { struct lcs_header *header; int rc = NETDEV_TX_OK; @@ -1582,8 +1581,7 @@ out: return rc; } -static int -lcs_start_xmit(struct sk_buff *skb, struct net_device *dev) +static netdev_tx_t lcs_start_xmit(struct sk_buff *skb, struct net_device *dev) { struct lcs_card *card; int rc; From 6c8fae0caf5dd893987a66772b12aacdf88dd848 Mon Sep 17 00:00:00 2001 From: Shenwei Wang Date: Thu, 3 Nov 2022 21:47:54 -0500 Subject: [PATCH 0669/2002] net: fec: simplify the code logic of quirks Simplify the code logic of handling the quirk of FEC_QUIRK_HAS_RACC. If a SoC has the RACC quirk, the driver will enable the 16bit shift by default in the probe function for a better performance. This patch handles the logic in one place to make the logic simple and clean. The patch optimizes the fec_enet_xdp_get_tx_queue function according to Paolo Abeni's comments, and it also exludes the SoCs that require to do frame swap from XDP support. Signed-off-by: Shenwei Wang Signed-off-by: David S. Miller --- drivers/net/ethernet/freescale/fec_main.c | 49 ++++++++++++++--------- 1 file changed, 30 insertions(+), 19 deletions(-) diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c index f7a5584efe22..616eea712ca8 100644 --- a/drivers/net/ethernet/freescale/fec_main.c +++ b/drivers/net/ethernet/freescale/fec_main.c @@ -1581,8 +1581,20 @@ fec_enet_rx_queue(struct net_device *ndev, int budget, u16 queue_id) bool need_swap = fep->quirks & FEC_QUIRK_SWAP_FRAME; struct bpf_prog *xdp_prog = READ_ONCE(fep->xdp_prog); u32 ret, xdp_result = FEC_ENET_XDP_PASS; + u32 data_start = FEC_ENET_XDP_HEADROOM; struct xdp_buff xdp; struct page *page; + u32 sub_len = 4; + +#if !defined(CONFIG_M5272) + /*If it has the FEC_QUIRK_HAS_RACC quirk property, the bit of + * FEC_RACC_SHIFT16 is set by default in the probe function. + */ + if (fep->quirks & FEC_QUIRK_HAS_RACC) { + data_start += 2; + sub_len += 2; + } +#endif #ifdef CONFIG_M532x flush_cache_all(); @@ -1645,9 +1657,9 @@ fec_enet_rx_queue(struct net_device *ndev, int budget, u16 queue_id) if (xdp_prog) { xdp_buff_clear_frags_flag(&xdp); + /* subtract 16bit shift and FCS */ xdp_prepare_buff(&xdp, page_address(page), - FEC_ENET_XDP_HEADROOM, pkt_len, false); - + data_start, pkt_len - sub_len, false); ret = fec_enet_run_xdp(fep, xdp_prog, &xdp, rxq, index); xdp_result |= ret; if (ret != FEC_ENET_XDP_PASS) @@ -1659,18 +1671,15 @@ fec_enet_rx_queue(struct net_device *ndev, int budget, u16 queue_id) * bridging applications. */ skb = build_skb(page_address(page), PAGE_SIZE); - skb_reserve(skb, FEC_ENET_XDP_HEADROOM); - skb_put(skb, pkt_len - 4); + skb_reserve(skb, data_start); + skb_put(skb, pkt_len - sub_len); skb_mark_for_recycle(skb); - data = skb->data; - if (need_swap) + if (unlikely(need_swap)) { + data = page_address(page) + FEC_ENET_XDP_HEADROOM; swap_buffer(data, pkt_len); - -#if !defined(CONFIG_M5272) - if (fep->quirks & FEC_QUIRK_HAS_RACC) - data = skb_pull_inline(skb, 2); -#endif + } + data = skb->data; /* Extract the enhanced buffer descriptor */ ebdp = NULL; @@ -3604,6 +3613,13 @@ static int fec_enet_bpf(struct net_device *dev, struct netdev_bpf *bpf) switch (bpf->command) { case XDP_SETUP_PROG: + /* No need to support the SoCs that require to + * do the frame swap because the performance wouldn't be + * better than the skb mode. + */ + if (fep->quirks & FEC_QUIRK_SWAP_FRAME) + return -EOPNOTSUPP; + if (is_run) { napi_disable(&fep->napi); netif_tx_disable(dev); @@ -3631,17 +3647,12 @@ static int fec_enet_bpf(struct net_device *dev, struct netdev_bpf *bpf) } static int -fec_enet_xdp_get_tx_queue(struct fec_enet_private *fep, int cpu) +fec_enet_xdp_get_tx_queue(struct fec_enet_private *fep, int index) { - int index = cpu; - if (unlikely(index < 0)) - index = 0; + return 0; - while (index >= fep->num_tx_queues) - index -= fep->num_tx_queues; - - return index; + return (index % fep->num_tx_queues); } static int fec_enet_txq_xmit_frame(struct fec_enet_private *fep, From 64c426dfbbd2710e4bcac42aaced21fe10f05fde Mon Sep 17 00:00:00 2001 From: Yang Yingliang Date: Fri, 4 Nov 2022 14:17:36 +0800 Subject: [PATCH 0670/2002] gve: Fix error return code in gve_prefill_rx_pages() If alloc_page() fails in gve_prefill_rx_pages(), it should return an error code in the error path. Fixes: 82fd151d38d9 ("gve: Reduce alloc and copy costs in the GQ rx path") Cc: Jeroen de Borst Cc: Catherine Sullivan Cc: Shailend Chand Cc: "David S. Miller" Signed-off-by: Yang Yingliang Signed-off-by: David S. Miller --- drivers/net/ethernet/google/gve/gve_rx.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/google/gve/gve_rx.c b/drivers/net/ethernet/google/gve/gve_rx.c index e2f4494c65fb..1f55137722b0 100644 --- a/drivers/net/ethernet/google/gve/gve_rx.c +++ b/drivers/net/ethernet/google/gve/gve_rx.c @@ -150,8 +150,10 @@ static int gve_prefill_rx_pages(struct gve_rx_ring *rx) for (j = 0; j < rx->qpl_copy_pool_mask + 1; j++) { struct page *page = alloc_page(GFP_KERNEL); - if (!page) + if (!page) { + err = -ENOMEM; goto alloc_err_qpl; + } rx->qpl_copy_pool[j].page = page; rx->qpl_copy_pool[j].page_offset = 0; From 4e0243e7128c9b25ea2739136076a95d6adaba5e Mon Sep 17 00:00:00 2001 From: Frank Date: Fri, 4 Nov 2022 16:44:41 +0800 Subject: [PATCH 0671/2002] net: phy: fix yt8521 duplicated argument to & or | cocci warnings: (new ones prefixed by >>) >> drivers/net/phy/motorcomm.c:1122:8-35: duplicated argument to & or | drivers/net/phy/motorcomm.c:1126:8-35: duplicated argument to & or | drivers/net/phy/motorcomm.c:1130:8-34: duplicated argument to & or | drivers/net/phy/motorcomm.c:1134:8-34: duplicated argument to & or | The second YT8521_RC1R_GE_TX_DELAY_xx should be YT8521_RC1R_FE_TX_DELAY_xx. Fixes: 70479a40954c ("net: phy: Add driver for Motorcomm yt8521 gigabit ethernet phy") Reported-by: kernel test robot Reported-by: Julia Lawall Signed-off-by: Frank Reviewed-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/phy/motorcomm.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/net/phy/motorcomm.c b/drivers/net/phy/motorcomm.c index c7593f224177..bd1ab5d0631f 100644 --- a/drivers/net/phy/motorcomm.c +++ b/drivers/net/phy/motorcomm.c @@ -1119,19 +1119,19 @@ static int yt8521_config_init(struct phy_device *phydev) switch (phydev->interface) { case PHY_INTERFACE_MODE_RGMII: - val = YT8521_RC1R_GE_TX_DELAY_DIS | YT8521_RC1R_GE_TX_DELAY_DIS; + val = YT8521_RC1R_GE_TX_DELAY_DIS | YT8521_RC1R_FE_TX_DELAY_DIS; val |= YT8521_RC1R_RX_DELAY_DIS; break; case PHY_INTERFACE_MODE_RGMII_RXID: - val = YT8521_RC1R_GE_TX_DELAY_DIS | YT8521_RC1R_GE_TX_DELAY_DIS; + val = YT8521_RC1R_GE_TX_DELAY_DIS | YT8521_RC1R_FE_TX_DELAY_DIS; val |= YT8521_RC1R_RX_DELAY_EN; break; case PHY_INTERFACE_MODE_RGMII_TXID: - val = YT8521_RC1R_GE_TX_DELAY_EN | YT8521_RC1R_GE_TX_DELAY_EN; + val = YT8521_RC1R_GE_TX_DELAY_EN | YT8521_RC1R_FE_TX_DELAY_EN; val |= YT8521_RC1R_RX_DELAY_DIS; break; case PHY_INTERFACE_MODE_RGMII_ID: - val = YT8521_RC1R_GE_TX_DELAY_EN | YT8521_RC1R_GE_TX_DELAY_EN; + val = YT8521_RC1R_GE_TX_DELAY_EN | YT8521_RC1R_FE_TX_DELAY_EN; val |= YT8521_RC1R_RX_DELAY_EN; break; case PHY_INTERFACE_MODE_SGMII: From cef122d4cf5b6463d16e71ed04330f32a526a6ab Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Fri, 4 Nov 2022 15:23:06 +0200 Subject: [PATCH 0672/2002] net: ethernet: ti: am65-cpsw/cpts: Add suspend/resume helpers CPTS looses context on suspend (e.g. on AM62). Provide suspend/resume hooks in CPTS driver. These will be invoked by CPSW driver if CPTS was instantiated by CPSW. Signed-off-by: Roger Quadros Signed-off-by: David S. Miller --- drivers/net/ethernet/ti/am65-cpts.c | 76 +++++++++++++++++++++++++++++ drivers/net/ethernet/ti/am65-cpts.h | 10 ++++ 2 files changed, 86 insertions(+) diff --git a/drivers/net/ethernet/ti/am65-cpts.c b/drivers/net/ethernet/ti/am65-cpts.c index e2f0fb286143..7f928c343426 100644 --- a/drivers/net/ethernet/ti/am65-cpts.c +++ b/drivers/net/ethernet/ti/am65-cpts.c @@ -176,6 +176,16 @@ struct am65_cpts { u32 genf_enable; u32 hw_ts_enable; struct sk_buff_head txq; + /* context save/restore */ + u64 sr_cpts_ns; + u64 sr_ktime_ns; + u32 sr_control; + u32 sr_int_enable; + u32 sr_rftclk_sel; + u32 sr_ts_ppm_hi; + u32 sr_ts_ppm_low; + struct am65_genf_regs sr_genf[AM65_CPTS_GENF_MAX_NUM]; + struct am65_genf_regs sr_estf[AM65_CPTS_ESTF_MAX_NUM]; }; struct am65_cpts_skb_cb_data { @@ -1029,6 +1039,72 @@ refclk_disable: } EXPORT_SYMBOL_GPL(am65_cpts_create); +void am65_cpts_suspend(struct am65_cpts *cpts) +{ + /* save state and disable CPTS */ + cpts->sr_control = am65_cpts_read32(cpts, control); + cpts->sr_int_enable = am65_cpts_read32(cpts, int_enable); + cpts->sr_rftclk_sel = am65_cpts_read32(cpts, rftclk_sel); + cpts->sr_ts_ppm_hi = am65_cpts_read32(cpts, ts_ppm_hi); + cpts->sr_ts_ppm_low = am65_cpts_read32(cpts, ts_ppm_low); + cpts->sr_cpts_ns = am65_cpts_gettime(cpts, NULL); + cpts->sr_ktime_ns = ktime_to_ns(ktime_get_real()); + am65_cpts_disable(cpts); + clk_disable(cpts->refclk); + + /* Save GENF state */ + memcpy_fromio(&cpts->sr_genf, &cpts->reg->genf, sizeof(cpts->sr_genf)); + + /* Save ESTF state */ + memcpy_fromio(&cpts->sr_estf, &cpts->reg->estf, sizeof(cpts->sr_estf)); +} +EXPORT_SYMBOL_GPL(am65_cpts_suspend); + +void am65_cpts_resume(struct am65_cpts *cpts) +{ + int i; + s64 ktime_ns; + + /* restore state and enable CPTS */ + clk_enable(cpts->refclk); + am65_cpts_write32(cpts, cpts->sr_rftclk_sel, rftclk_sel); + am65_cpts_set_add_val(cpts); + am65_cpts_write32(cpts, cpts->sr_control, control); + am65_cpts_write32(cpts, cpts->sr_int_enable, int_enable); + + /* Restore time to saved CPTS time + time in suspend/resume */ + ktime_ns = ktime_to_ns(ktime_get_real()); + ktime_ns -= cpts->sr_ktime_ns; + am65_cpts_settime(cpts, cpts->sr_cpts_ns + ktime_ns); + + /* Restore compensation (PPM) */ + am65_cpts_write32(cpts, cpts->sr_ts_ppm_hi, ts_ppm_hi); + am65_cpts_write32(cpts, cpts->sr_ts_ppm_low, ts_ppm_low); + + /* Restore GENF state */ + for (i = 0; i < AM65_CPTS_GENF_MAX_NUM; i++) { + am65_cpts_write32(cpts, 0, genf[i].length); /* TRM sequence */ + am65_cpts_write32(cpts, cpts->sr_genf[i].comp_hi, genf[i].comp_hi); + am65_cpts_write32(cpts, cpts->sr_genf[i].comp_lo, genf[i].comp_lo); + am65_cpts_write32(cpts, cpts->sr_genf[i].length, genf[i].length); + am65_cpts_write32(cpts, cpts->sr_genf[i].control, genf[i].control); + am65_cpts_write32(cpts, cpts->sr_genf[i].ppm_hi, genf[i].ppm_hi); + am65_cpts_write32(cpts, cpts->sr_genf[i].ppm_low, genf[i].ppm_low); + } + + /* Restore ESTTF state */ + for (i = 0; i < AM65_CPTS_ESTF_MAX_NUM; i++) { + am65_cpts_write32(cpts, 0, estf[i].length); /* TRM sequence */ + am65_cpts_write32(cpts, cpts->sr_estf[i].comp_hi, estf[i].comp_hi); + am65_cpts_write32(cpts, cpts->sr_estf[i].comp_lo, estf[i].comp_lo); + am65_cpts_write32(cpts, cpts->sr_estf[i].length, estf[i].length); + am65_cpts_write32(cpts, cpts->sr_estf[i].control, estf[i].control); + am65_cpts_write32(cpts, cpts->sr_estf[i].ppm_hi, estf[i].ppm_hi); + am65_cpts_write32(cpts, cpts->sr_estf[i].ppm_low, estf[i].ppm_low); + } +} +EXPORT_SYMBOL_GPL(am65_cpts_resume); + static int am65_cpts_probe(struct platform_device *pdev) { struct device_node *node = pdev->dev.of_node; diff --git a/drivers/net/ethernet/ti/am65-cpts.h b/drivers/net/ethernet/ti/am65-cpts.h index cf9fbc28fd03..bd08f4b2edd2 100644 --- a/drivers/net/ethernet/ti/am65-cpts.h +++ b/drivers/net/ethernet/ti/am65-cpts.h @@ -28,6 +28,8 @@ u64 am65_cpts_ns_gettime(struct am65_cpts *cpts); int am65_cpts_estf_enable(struct am65_cpts *cpts, int idx, struct am65_cpts_estf_cfg *cfg); void am65_cpts_estf_disable(struct am65_cpts *cpts, int idx); +void am65_cpts_suspend(struct am65_cpts *cpts); +void am65_cpts_resume(struct am65_cpts *cpts); #else static inline struct am65_cpts *am65_cpts_create(struct device *dev, void __iomem *regs, @@ -69,6 +71,14 @@ static inline int am65_cpts_estf_enable(struct am65_cpts *cpts, int idx, static inline void am65_cpts_estf_disable(struct am65_cpts *cpts, int idx) { } + +static inline void am65_cpts_suspend(struct am65_cpts *cpts) +{ +} + +static inline void am65_cpts_resume(struct am65_cpts *cpts) +{ +} #endif #endif /* K3_CPTS_H_ */ From fd23df72f2be317d38d9fde0a8996b8e7454fd2a Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Fri, 4 Nov 2022 15:23:07 +0200 Subject: [PATCH 0673/2002] net: ethernet: ti: am65-cpsw: Add suspend/resume support Add PM handlers for System suspend/resume. As DMA driver doesn't yet support suspend/resume we free up the DMA channels at suspend and acquire and initialize them at resume. Move the init/free dma calls to ndo_open/close() hooks so it is symmetric and easier to invoke from suspend/resume handler. As CPTS looses contect during suspend/resume, invoke the necessary CPTS suspend/resume helpers. Signed-off-by: Roger Quadros Signed-off-by: David S. Miller --- drivers/net/ethernet/ti/am65-cpsw-nuss.c | 221 +++++++++++++++++------ 1 file changed, 166 insertions(+), 55 deletions(-) diff --git a/drivers/net/ethernet/ti/am65-cpsw-nuss.c b/drivers/net/ethernet/ti/am65-cpsw-nuss.c index 0dbdce4dc077..5a842d9fbb8e 100644 --- a/drivers/net/ethernet/ti/am65-cpsw-nuss.c +++ b/drivers/net/ethernet/ti/am65-cpsw-nuss.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -132,6 +133,11 @@ NETIF_MSG_IFUP | NETIF_MSG_PROBE | NETIF_MSG_IFDOWN | \ NETIF_MSG_RX_ERR | NETIF_MSG_TX_ERR) +static int am65_cpsw_nuss_init_tx_chns(struct am65_cpsw_common *common); +static int am65_cpsw_nuss_init_rx_chns(struct am65_cpsw_common *common); +static void am65_cpsw_nuss_free_tx_chns(struct am65_cpsw_common *common); +static void am65_cpsw_nuss_free_rx_chns(struct am65_cpsw_common *common); + static void am65_cpsw_port_set_sl_mac(struct am65_cpsw_port *slave, const u8 *dev_addr) { @@ -373,6 +379,20 @@ static int am65_cpsw_nuss_common_open(struct am65_cpsw_common *common) if (common->usage_count) return 0; + /* init tx/rx channels */ + ret = am65_cpsw_nuss_init_tx_chns(common); + if (ret) { + dev_err(common->dev, "init_tx_chns failed\n"); + return ret; + } + + ret = am65_cpsw_nuss_init_rx_chns(common); + if (ret) { + dev_err(common->dev, "init_rx_chns failed\n"); + am65_cpsw_nuss_free_tx_chns(common); + return ret; + } + /* Control register */ writel(AM65_CPSW_CTL_P0_ENABLE | AM65_CPSW_CTL_P0_TX_CRC_REMOVE | AM65_CPSW_CTL_VLAN_AWARE | AM65_CPSW_CTL_P0_RX_PAD, @@ -401,6 +421,7 @@ static int am65_cpsw_nuss_common_open(struct am65_cpsw_common *common) /* disable priority elevation */ writel(0, common->cpsw_base + AM65_CPSW_REG_PTYPE); + cpsw_ale_control_set(common->ale, 0, ALE_CLEAR, 1); cpsw_ale_start(common->ale); /* limit to one RX flow only */ @@ -432,7 +453,8 @@ static int am65_cpsw_nuss_common_open(struct am65_cpsw_common *common) GFP_KERNEL); if (!skb) { dev_err(common->dev, "cannot allocate skb\n"); - return -ENOMEM; + ret = -ENOMEM; + goto err; } ret = am65_cpsw_nuss_rx_push(common, skb); @@ -441,7 +463,7 @@ static int am65_cpsw_nuss_common_open(struct am65_cpsw_common *common) "cannot submit skb to channel rx, error %d\n", ret); kfree_skb(skb); - return ret; + goto err; } kmemleak_not_leak(skb); } @@ -450,7 +472,7 @@ static int am65_cpsw_nuss_common_open(struct am65_cpsw_common *common) for (i = 0; i < common->tx_ch_num; i++) { ret = k3_udma_glue_enable_tx_chn(common->tx_chns[i].tx_chn); if (ret) - return ret; + goto err; napi_enable(&common->tx_chns[i].napi_tx); } @@ -462,6 +484,12 @@ static int am65_cpsw_nuss_common_open(struct am65_cpsw_common *common) dev_dbg(common->dev, "cpsw_nuss started\n"); return 0; + +err: + am65_cpsw_nuss_free_tx_chns(common); + am65_cpsw_nuss_free_rx_chns(common); + + return ret; } static void am65_cpsw_nuss_tx_cleanup(void *data, dma_addr_t desc_dma); @@ -515,6 +543,9 @@ static int am65_cpsw_nuss_common_stop(struct am65_cpsw_common *common) writel(0, common->cpsw_base + AM65_CPSW_REG_CTL); writel(0, common->cpsw_base + AM65_CPSW_REG_STAT_PORT_EN); + am65_cpsw_nuss_free_tx_chns(common); + am65_cpsw_nuss_free_rx_chns(common); + dev_dbg(common->dev, "cpsw_nuss stopped\n"); return 0; } @@ -555,11 +586,29 @@ static int am65_cpsw_nuss_ndo_slave_open(struct net_device *ndev) struct am65_cpsw_common *common = am65_ndev_to_common(ndev); struct am65_cpsw_port *port = am65_ndev_to_port(ndev); int ret, i; + u32 reg; + int tmo; ret = pm_runtime_resume_and_get(common->dev); if (ret < 0) return ret; + /* Idle MAC port */ + cpsw_sl_ctl_set(port->slave.mac_sl, CPSW_SL_CTL_CMD_IDLE); + + tmo = cpsw_sl_wait_for_idle(port->slave.mac_sl, 100); + dev_info(common->dev, "down msc_sl %08x tmo %d\n", + cpsw_sl_reg_read(port->slave.mac_sl, CPSW_SL_MACSTATUS), tmo); + + cpsw_sl_ctl_reset(port->slave.mac_sl); + + /* soft reset MAC */ + cpsw_sl_reg_write(port->slave.mac_sl, CPSW_SL_SOFT_RESET, 1); + mdelay(1); + reg = cpsw_sl_reg_read(port->slave.mac_sl, CPSW_SL_SOFT_RESET); + if (reg) + dev_info(common->dev, "mac reset not yet done\n"); + /* Notify the stack of the actual queue counts. */ ret = netif_set_real_num_tx_queues(ndev, common->tx_ch_num); if (ret) { @@ -1491,9 +1540,9 @@ static void am65_cpsw_nuss_slave_disable_unused(struct am65_cpsw_port *port) cpsw_sl_ctl_reset(port->slave.mac_sl); } -static void am65_cpsw_nuss_free_tx_chns(void *data) +static void am65_cpsw_nuss_free_tx_chns(struct am65_cpsw_common *common) { - struct am65_cpsw_common *common = data; + struct device *dev = common->dev; int i; for (i = 0; i < common->tx_ch_num; i++) { @@ -1505,7 +1554,11 @@ static void am65_cpsw_nuss_free_tx_chns(void *data) if (!IS_ERR_OR_NULL(tx_chn->tx_chn)) k3_udma_glue_release_tx_chn(tx_chn->tx_chn); - memset(tx_chn, 0, sizeof(*tx_chn)); + /* Don't clear tx_chn memory as we need to preserve + * data between suspend/resume + */ + if (!(tx_chn->irq < 0)) + devm_free_irq(dev, tx_chn->irq, tx_chn); } } @@ -1514,12 +1567,10 @@ void am65_cpsw_nuss_remove_tx_chns(struct am65_cpsw_common *common) struct device *dev = common->dev; int i; - devm_remove_action(dev, am65_cpsw_nuss_free_tx_chns, common); - for (i = 0; i < common->tx_ch_num; i++) { struct am65_cpsw_tx_chn *tx_chn = &common->tx_chns[i]; - if (tx_chn->irq) + if (!(tx_chn->irq < 0)) devm_free_irq(dev, tx_chn->irq, tx_chn); netif_napi_del(&tx_chn->napi_tx); @@ -1589,7 +1640,7 @@ static int am65_cpsw_nuss_init_tx_chns(struct am65_cpsw_common *common) } tx_chn->irq = k3_udma_glue_tx_get_irq(tx_chn->tx_chn); - if (tx_chn->irq <= 0) { + if (tx_chn->irq < 0) { dev_err(dev, "Failed to get tx dma irq %d\n", tx_chn->irq); goto err; @@ -1598,25 +1649,36 @@ static int am65_cpsw_nuss_init_tx_chns(struct am65_cpsw_common *common) snprintf(tx_chn->tx_chn_name, sizeof(tx_chn->tx_chn_name), "%s-tx%d", dev_name(dev), tx_chn->id); + + ret = devm_request_irq(dev, tx_chn->irq, + am65_cpsw_nuss_tx_irq, + IRQF_TRIGGER_HIGH, + tx_chn->tx_chn_name, tx_chn); + if (ret) { + dev_err(dev, "failure requesting tx%u irq %u, %d\n", + tx_chn->id, tx_chn->irq, ret); + tx_chn->irq = -EINVAL; + goto err; + } } + return 0; + err: - i = devm_add_action(dev, am65_cpsw_nuss_free_tx_chns, common); - if (i) { - dev_err(dev, "Failed to add free_tx_chns action %d\n", i); - return i; - } + am65_cpsw_nuss_free_tx_chns(common); return ret; } -static void am65_cpsw_nuss_free_rx_chns(void *data) +static void am65_cpsw_nuss_free_rx_chns(struct am65_cpsw_common *common) { - struct am65_cpsw_common *common = data; struct am65_cpsw_rx_chn *rx_chn; rx_chn = &common->rx_chns; + if (!(rx_chn->irq < 0)) + devm_free_irq(common->dev, rx_chn->irq, common); + if (!IS_ERR_OR_NULL(rx_chn->desc_pool)) k3_cppi_desc_pool_destroy(rx_chn->desc_pool); @@ -1639,7 +1701,7 @@ static int am65_cpsw_nuss_init_rx_chns(struct am65_cpsw_common *common) rx_cfg.swdata_size = AM65_CPSW_NAV_SW_DATA_SIZE; rx_cfg.flow_id_num = AM65_CPSW_MAX_RX_FLOWS; - rx_cfg.flow_id_base = common->rx_flow_id_base; + rx_cfg.flow_id_base = -1; /* init all flows */ rx_chn->dev = dev; @@ -1711,13 +1773,21 @@ static int am65_cpsw_nuss_init_rx_chns(struct am65_cpsw_common *common) } } -err: - i = devm_add_action(dev, am65_cpsw_nuss_free_rx_chns, common); - if (i) { - dev_err(dev, "Failed to add free_rx_chns action %d\n", i); - return i; + ret = devm_request_irq(dev, rx_chn->irq, + am65_cpsw_nuss_rx_irq, + IRQF_TRIGGER_HIGH, dev_name(dev), common); + if (ret) { + dev_err(dev, "failure requesting rx irq %u, %d\n", + rx_chn->irq, ret); + rx_chn->irq = -EINVAL; + goto err; } + return 0; + +err: + am65_cpsw_nuss_free_rx_chns(common); + return ret; } @@ -1982,6 +2052,7 @@ am65_cpsw_nuss_init_port_ndev(struct am65_cpsw_common *common, u32 port_idx) port->slave.phylink_config.dev = &port->ndev->dev; port->slave.phylink_config.type = PHYLINK_NETDEV; port->slave.phylink_config.mac_capabilities = MAC_SYM_PAUSE | MAC_10 | MAC_100 | MAC_1000FD; + port->slave.phylink_config.mac_managed_pm = true; /* MAC does PM */ if (phy_interface_mode_is_rgmii(port->slave.phy_if)) { phy_interface_set_rgmii(port->slave.phylink_config.supported_interfaces); @@ -2043,28 +2114,16 @@ static int am65_cpsw_nuss_init_ndevs(struct am65_cpsw_common *common) static int am65_cpsw_nuss_ndev_add_tx_napi(struct am65_cpsw_common *common) { - struct device *dev = common->dev; - int i, ret = 0; + int i; for (i = 0; i < common->tx_ch_num; i++) { struct am65_cpsw_tx_chn *tx_chn = &common->tx_chns[i]; netif_napi_add_tx(common->dma_ndev, &tx_chn->napi_tx, am65_cpsw_nuss_tx_poll); - - ret = devm_request_irq(dev, tx_chn->irq, - am65_cpsw_nuss_tx_irq, - IRQF_TRIGGER_HIGH, - tx_chn->tx_chn_name, tx_chn); - if (ret) { - dev_err(dev, "failure requesting tx%u irq %u, %d\n", - tx_chn->id, tx_chn->irq, ret); - goto err; - } } -err: - return ret; + return 0; } static void am65_cpsw_nuss_cleanup_ndev(struct am65_cpsw_common *common) @@ -2533,15 +2592,6 @@ static int am65_cpsw_nuss_register_ndevs(struct am65_cpsw_common *common) if (ret) return ret; - ret = devm_request_irq(dev, common->rx_chns.irq, - am65_cpsw_nuss_rx_irq, - IRQF_TRIGGER_HIGH, dev_name(dev), common); - if (ret) { - dev_err(dev, "failure requesting rx irq %u, %d\n", - common->rx_chns.irq, ret); - return ret; - } - ret = am65_cpsw_nuss_register_devlink(common); if (ret) return ret; @@ -2695,7 +2745,6 @@ static int am65_cpsw_nuss_probe(struct platform_device *pdev) if (common->port_num < 1 || common->port_num > AM65_CPSW_MAX_PORTS) return -ENOENT; - common->rx_flow_id_base = -1; init_completion(&common->tdown_complete); common->tx_ch_num = 1; common->pf_p0_rx_ptype_rrobin = false; @@ -2737,14 +2786,6 @@ static int am65_cpsw_nuss_probe(struct platform_device *pdev) am65_cpsw_nuss_get_ver(common); - /* init tx channels */ - ret = am65_cpsw_nuss_init_tx_chns(common); - if (ret) - goto err_of_clear; - ret = am65_cpsw_nuss_init_rx_chns(common); - if (ret) - goto err_of_clear; - ret = am65_cpsw_nuss_init_host_p(common); if (ret) goto err_of_clear; @@ -2829,10 +2870,80 @@ static int am65_cpsw_nuss_remove(struct platform_device *pdev) return 0; } +#ifdef CONFIG_PM_SLEEP +static int am65_cpsw_nuss_suspend(struct device *dev) +{ + struct am65_cpsw_common *common = dev_get_drvdata(dev); + struct am65_cpsw_port *port; + struct net_device *ndev; + int i, ret; + + for (i = 0; i < common->port_num; i++) { + port = &common->ports[i]; + ndev = port->ndev; + + if (!ndev) + continue; + + netif_device_detach(ndev); + if (netif_running(ndev)) { + rtnl_lock(); + ret = am65_cpsw_nuss_ndo_slave_stop(ndev); + rtnl_unlock(); + if (ret < 0) { + netdev_err(ndev, "failed to stop: %d", ret); + return ret; + } + } + } + + am65_cpts_suspend(common->cpts); + + return 0; +} + +static int am65_cpsw_nuss_resume(struct device *dev) +{ + struct am65_cpsw_common *common = dev_get_drvdata(dev); + struct am65_cpsw_port *port; + struct net_device *ndev; + int i, ret; + + am65_cpts_resume(common->cpts); + + for (i = 0; i < common->port_num; i++) { + port = &common->ports[i]; + ndev = port->ndev; + + if (!ndev) + continue; + + if (netif_running(ndev)) { + rtnl_lock(); + ret = am65_cpsw_nuss_ndo_slave_open(ndev); + rtnl_unlock(); + if (ret < 0) { + netdev_err(ndev, "failed to start: %d", ret); + return ret; + } + } + + netif_device_attach(ndev); + } + + return 0; +} +#endif /* CONFIG_PM_SLEEP */ + +static const struct dev_pm_ops am65_cpsw_nuss_dev_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(am65_cpsw_nuss_suspend, am65_cpsw_nuss_resume) +}; + static struct platform_driver am65_cpsw_nuss_driver = { .driver = { .name = AM65_CPSW_DRV_NAME, .of_match_table = am65_cpsw_nuss_of_mtable, + .pm = &am65_cpsw_nuss_dev_pm_ops, }, .probe = am65_cpsw_nuss_probe, .remove = am65_cpsw_nuss_remove, From eadb434374075d3a09bcd983d340dad8d6a5138a Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Fri, 4 Nov 2022 15:23:08 +0200 Subject: [PATCH 0674/2002] net: ethernet: ti: cpsw_ale: Add cpsw_ale_restore() helper This can be used by device driver to restore ALE context. The data produced by cpsw_ale_dump() can be passed to cpsw_ale_restore(). This is required as on certain platforms the ALE context is lost on low power suspend/resume. Signed-off-by: Roger Quadros Signed-off-by: David S. Miller --- drivers/net/ethernet/ti/cpsw_ale.c | 10 ++++++++++ drivers/net/ethernet/ti/cpsw_ale.h | 1 + 2 files changed, 11 insertions(+) diff --git a/drivers/net/ethernet/ti/cpsw_ale.c b/drivers/net/ethernet/ti/cpsw_ale.c index 231370e9a801..0c5e783e574c 100644 --- a/drivers/net/ethernet/ti/cpsw_ale.c +++ b/drivers/net/ethernet/ti/cpsw_ale.c @@ -1452,6 +1452,16 @@ void cpsw_ale_dump(struct cpsw_ale *ale, u32 *data) } } +void cpsw_ale_restore(struct cpsw_ale *ale, u32 *data) +{ + int i; + + for (i = 0; i < ale->params.ale_entries; i++) { + cpsw_ale_write(ale, i, data); + data += ALE_ENTRY_WORDS; + } +} + u32 cpsw_ale_get_num_entries(struct cpsw_ale *ale) { return ale ? ale->params.ale_entries : 0; diff --git a/drivers/net/ethernet/ti/cpsw_ale.h b/drivers/net/ethernet/ti/cpsw_ale.h index aba4572cfa3b..6779ee111d57 100644 --- a/drivers/net/ethernet/ti/cpsw_ale.h +++ b/drivers/net/ethernet/ti/cpsw_ale.h @@ -127,6 +127,7 @@ int cpsw_ale_control_get(struct cpsw_ale *ale, int port, int control); int cpsw_ale_control_set(struct cpsw_ale *ale, int port, int control, int value); void cpsw_ale_dump(struct cpsw_ale *ale, u32 *data); +void cpsw_ale_restore(struct cpsw_ale *ale, u32 *data); u32 cpsw_ale_get_num_entries(struct cpsw_ale *ale); static inline int cpsw_ale_get_vlan_p0_untag(struct cpsw_ale *ale, u16 vid) From 643cf0e3ab5ccee37b3c53c018bd476c45c4b70e Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Fri, 4 Nov 2022 15:23:09 +0200 Subject: [PATCH 0675/2002] net: ethernet: ti: am65-cpsw: retain PORT_VLAN_REG after suspend/resume During suspend resume the context of PORT_VLAN_REG is lost so save it during suspend and restore it during resume for host port and slave ports. Signed-off-by: Roger Quadros Signed-off-by: David S. Miller --- drivers/net/ethernet/ti/am65-cpsw-nuss.c | 7 +++++++ drivers/net/ethernet/ti/am65-cpsw-nuss.h | 4 ++++ 2 files changed, 11 insertions(+) diff --git a/drivers/net/ethernet/ti/am65-cpsw-nuss.c b/drivers/net/ethernet/ti/am65-cpsw-nuss.c index 5a842d9fbb8e..beff21da0514 100644 --- a/drivers/net/ethernet/ti/am65-cpsw-nuss.c +++ b/drivers/net/ethernet/ti/am65-cpsw-nuss.c @@ -2877,7 +2877,9 @@ static int am65_cpsw_nuss_suspend(struct device *dev) struct am65_cpsw_port *port; struct net_device *ndev; int i, ret; + struct am65_cpsw_host *host_p = am65_common_get_host(common); + host_p->vid_context = readl(host_p->port_base + AM65_CPSW_PORT_VLAN_REG_OFFSET); for (i = 0; i < common->port_num; i++) { port = &common->ports[i]; ndev = port->ndev; @@ -2885,6 +2887,7 @@ static int am65_cpsw_nuss_suspend(struct device *dev) if (!ndev) continue; + port->vid_context = readl(port->port_base + AM65_CPSW_PORT_VLAN_REG_OFFSET); netif_device_detach(ndev); if (netif_running(ndev)) { rtnl_lock(); @@ -2908,6 +2911,7 @@ static int am65_cpsw_nuss_resume(struct device *dev) struct am65_cpsw_port *port; struct net_device *ndev; int i, ret; + struct am65_cpsw_host *host_p = am65_common_get_host(common); am65_cpts_resume(common->cpts); @@ -2929,8 +2933,11 @@ static int am65_cpsw_nuss_resume(struct device *dev) } netif_device_attach(ndev); + writel(port->vid_context, port->port_base + AM65_CPSW_PORT_VLAN_REG_OFFSET); } + writel(host_p->vid_context, host_p->port_base + AM65_CPSW_PORT_VLAN_REG_OFFSET); + return 0; } #endif /* CONFIG_PM_SLEEP */ diff --git a/drivers/net/ethernet/ti/am65-cpsw-nuss.h b/drivers/net/ethernet/ti/am65-cpsw-nuss.h index 2c9850fdfcb6..e95cc37a7286 100644 --- a/drivers/net/ethernet/ti/am65-cpsw-nuss.h +++ b/drivers/net/ethernet/ti/am65-cpsw-nuss.h @@ -55,12 +55,16 @@ struct am65_cpsw_port { bool rx_ts_enabled; struct am65_cpsw_qos qos; struct devlink_port devlink_port; + /* Only for suspend resume context */ + u32 vid_context; }; struct am65_cpsw_host { struct am65_cpsw_common *common; void __iomem *port_base; void __iomem *stat_base; + /* Only for suspend resume context */ + u32 vid_context; }; struct am65_cpsw_tx_chn { From 1af3cb3702d02167926a2bd18580cecb2d64fd94 Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Fri, 4 Nov 2022 15:23:10 +0200 Subject: [PATCH 0676/2002] net: ethernet: ti: am65-cpsw: Fix hardware switch mode on suspend/resume On low power during system suspend the ALE table context is lost. Save the ALE contect before suspend and restore it after resume. Signed-off-by: Roger Quadros Signed-off-by: David S. Miller --- drivers/net/ethernet/ti/am65-cpsw-nuss.c | 7 +++++++ drivers/net/ethernet/ti/am65-cpsw-nuss.h | 2 ++ 2 files changed, 9 insertions(+) diff --git a/drivers/net/ethernet/ti/am65-cpsw-nuss.c b/drivers/net/ethernet/ti/am65-cpsw-nuss.c index beff21da0514..c785e01d025f 100644 --- a/drivers/net/ethernet/ti/am65-cpsw-nuss.c +++ b/drivers/net/ethernet/ti/am65-cpsw-nuss.c @@ -2715,6 +2715,7 @@ static int am65_cpsw_nuss_probe(struct platform_device *pdev) struct clk *clk; u64 id_temp; int ret, i; + int ale_entries; common = devm_kzalloc(dev, sizeof(struct am65_cpsw_common), GFP_KERNEL); if (!common) @@ -2809,6 +2810,10 @@ static int am65_cpsw_nuss_probe(struct platform_device *pdev) goto err_of_clear; } + ale_entries = common->ale->params.ale_entries; + common->ale_context = devm_kzalloc(dev, + ale_entries * ALE_ENTRY_WORDS * sizeof(u32), + GFP_KERNEL); ret = am65_cpsw_init_cpts(common); if (ret) goto err_of_clear; @@ -2879,6 +2884,7 @@ static int am65_cpsw_nuss_suspend(struct device *dev) int i, ret; struct am65_cpsw_host *host_p = am65_common_get_host(common); + cpsw_ale_dump(common->ale, common->ale_context); host_p->vid_context = readl(host_p->port_base + AM65_CPSW_PORT_VLAN_REG_OFFSET); for (i = 0; i < common->port_num; i++) { port = &common->ports[i]; @@ -2937,6 +2943,7 @@ static int am65_cpsw_nuss_resume(struct device *dev) } writel(host_p->vid_context, host_p->port_base + AM65_CPSW_PORT_VLAN_REG_OFFSET); + cpsw_ale_restore(common->ale, common->ale_context); return 0; } diff --git a/drivers/net/ethernet/ti/am65-cpsw-nuss.h b/drivers/net/ethernet/ti/am65-cpsw-nuss.h index e95cc37a7286..4b75620f8d28 100644 --- a/drivers/net/ethernet/ti/am65-cpsw-nuss.h +++ b/drivers/net/ethernet/ti/am65-cpsw-nuss.h @@ -149,6 +149,8 @@ struct am65_cpsw_common { struct net_device *hw_bridge_dev; struct notifier_block am65_cpsw_netdevice_nb; unsigned char switch_id[MAX_PHYS_ITEM_ID_LEN]; + /* only for suspend/resume context restore */ + u32 *ale_context; }; struct am65_cpsw_ndev_stats { From ff14adbd8779f098576ffdb98381809ed8e40dfe Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Fri, 4 Nov 2022 12:13:31 -0700 Subject: [PATCH 0677/2002] genetlink: refactor the cmd <> policy mapping dump The code at the top of ctrl_dumppolicy() dumps mappings between ops and policies. It supports dumping both the entire family and single op if dump is filtered. But both of those cases are handled inside a loop, which makes the logic harder to follow and change. Refactor to split the two cases more clearly. Signed-off-by: Jakub Kicinski Reviewed-by: Jacob Keller Signed-off-by: David S. Miller --- net/netlink/genetlink.c | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/net/netlink/genetlink.c b/net/netlink/genetlink.c index 3e16527beb91..0a7a856e9ce0 100644 --- a/net/netlink/genetlink.c +++ b/net/netlink/genetlink.c @@ -1319,21 +1319,24 @@ static int ctrl_dumppolicy(struct sk_buff *skb, struct netlink_callback *cb) void *hdr; if (!ctx->policies) { + struct genl_ops op; + + if (ctx->single_op) { + int err; + + err = genl_get_cmd(ctx->op, ctx->rt, &op); + if (WARN_ON(err)) + return err; + + if (ctrl_dumppolicy_put_op(skb, cb, &op)) + return skb->len; + + /* don't enter the loop below */ + ctx->opidx = genl_get_cmd_cnt(ctx->rt); + } + while (ctx->opidx < genl_get_cmd_cnt(ctx->rt)) { - struct genl_ops op; - - if (ctx->single_op) { - int err; - - err = genl_get_cmd(ctx->op, ctx->rt, &op); - if (WARN_ON(err)) - return skb->len; - - /* break out of the loop after this one */ - ctx->opidx = genl_get_cmd_cnt(ctx->rt); - } else { - genl_get_cmd_by_index(ctx->opidx, ctx->rt, &op); - } + genl_get_cmd_by_index(ctx->opidx, ctx->rt, &op); if (ctrl_dumppolicy_put_op(skb, cb, &op)) return skb->len; From 7c3eaa022261d79d273d73ac68ff02cbe2839c10 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Fri, 4 Nov 2022 12:13:32 -0700 Subject: [PATCH 0678/2002] genetlink: move the private fields in struct genl_family Move the private fields down to form a "private section". Use the kdoc "private:" label comment thing to hide them from the main kdoc comment. Signed-off-by: Jakub Kicinski Reviewed-by: Johannes Berg Reviewed-by: Jacob Keller Signed-off-by: David S. Miller --- include/net/genetlink.h | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/include/net/genetlink.h b/include/net/genetlink.h index 9f97f73615b6..81180fc6526a 100644 --- a/include/net/genetlink.h +++ b/include/net/genetlink.h @@ -23,7 +23,6 @@ struct genl_info; /** * struct genl_family - generic netlink family - * @id: protocol family identifier (private) * @hdrsize: length of user specific header in bytes * @name: name of family * @version: protocol version @@ -43,8 +42,6 @@ struct genl_info; * @resv_start_op: first operation for which reserved fields of the header * can be validated and policies are required (see below); * new families should leave this field at zero - * @mcgrp_offset: starting number of multicast group IDs in this family - * (private) * @ops: the operations supported by this family * @n_ops: number of operations supported by this family * @small_ops: the small-struct operations supported by this family @@ -58,12 +55,10 @@ struct genl_info; * if policy is not provided core will reject all TLV attributes. */ struct genl_family { - int id; /* private */ unsigned int hdrsize; char name[GENL_NAMSIZ]; unsigned int version; unsigned int maxattr; - unsigned int mcgrp_offset; /* private */ u8 netnsok:1; u8 parallel_ops:1; u8 n_ops; @@ -81,6 +76,12 @@ struct genl_family { const struct genl_small_ops *small_ops; const struct genl_multicast_group *mcgrps; struct module *module; + +/* private: internal use only */ + /* protocol family identifier */ + int id; + /* starting number of multicast group IDs in this family */ + unsigned int mcgrp_offset; }; /** From 20b0b53aca436af9fece9428ca3ab7c7b9cf4583 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Fri, 4 Nov 2022 12:13:33 -0700 Subject: [PATCH 0679/2002] genetlink: introduce split op representation We currently have two forms of operations - small ops and "full" ops (or just ops). The former does not have pointers for some of the less commonly used features (namely dump start/done and policy). The "full" ops, however, still don't contain all the necessary information. In particular the policy is per command ID, while do and dump often accept different attributes. It's also not possible to define different pre_doit and post_doit callbacks for different commands within the family. At the same time a lot of commands do not support dumping and therefore all the dump-related information is wasted space. Create a new command representation which can hold info about a do implementation or a dump implementation, but not both at the same time. Use this new representation on the command execution path (genl_family_rcv_msg) as we either run a do or a dump and don't have to create a "full" op there. Signed-off-by: Jakub Kicinski Reviewed-by: Jacob Keller Signed-off-by: David S. Miller --- include/net/genetlink.h | 60 +++++++++++++++++++++++++++-- net/batman-adv/netlink.c | 6 ++- net/core/devlink.c | 4 +- net/core/drop_monitor.c | 4 +- net/ieee802154/nl802154.c | 6 ++- net/netlink/genetlink.c | 79 +++++++++++++++++++++++++++++++-------- net/wireless/nl80211.c | 6 ++- 7 files changed, 136 insertions(+), 29 deletions(-) diff --git a/include/net/genetlink.h b/include/net/genetlink.h index 81180fc6526a..4be7989c451b 100644 --- a/include/net/genetlink.h +++ b/include/net/genetlink.h @@ -18,7 +18,7 @@ struct genl_multicast_group { u8 flags; }; -struct genl_ops; +struct genl_split_ops; struct genl_info; /** @@ -66,10 +66,10 @@ struct genl_family { u8 n_mcgrps; u8 resv_start_op; const struct nla_policy *policy; - int (*pre_doit)(const struct genl_ops *ops, + int (*pre_doit)(const struct genl_split_ops *ops, struct sk_buff *skb, struct genl_info *info); - void (*post_doit)(const struct genl_ops *ops, + void (*post_doit)(const struct genl_split_ops *ops, struct sk_buff *skb, struct genl_info *info); const struct genl_ops * ops; @@ -182,6 +182,58 @@ struct genl_ops { u8 validate; }; +/** + * struct genl_split_ops - generic netlink operations (do/dump split version) + * @cmd: command identifier + * @internal_flags: flags used by the family + * @flags: GENL_* flags (%GENL_ADMIN_PERM or %GENL_UNS_ADMIN_PERM) + * @validate: validation flags from enum genl_validate_flags + * @policy: netlink policy (takes precedence over family policy) + * @maxattr: maximum number of attributes supported + * + * Do callbacks: + * @pre_doit: called before an operation's @doit callback, it may + * do additional, common, filtering and return an error + * @doit: standard command callback + * @post_doit: called after an operation's @doit callback, it may + * undo operations done by pre_doit, for example release locks + * + * Dump callbacks: + * @start: start callback for dumps + * @dumpit: callback for dumpers + * @done: completion callback for dumps + * + * Do callbacks can be used if %GENL_CMD_CAP_DO is set in @flags. + * Dump callbacks can be used if %GENL_CMD_CAP_DUMP is set in @flags. + * Exactly one of those flags must be set. + */ +struct genl_split_ops { + union { + struct { + int (*pre_doit)(const struct genl_split_ops *ops, + struct sk_buff *skb, + struct genl_info *info); + int (*doit)(struct sk_buff *skb, + struct genl_info *info); + void (*post_doit)(const struct genl_split_ops *ops, + struct sk_buff *skb, + struct genl_info *info); + }; + struct { + int (*start)(struct netlink_callback *cb); + int (*dumpit)(struct sk_buff *skb, + struct netlink_callback *cb); + int (*done)(struct netlink_callback *cb); + }; + }; + const struct nla_policy *policy; + unsigned int maxattr; + u8 cmd; + u8 internal_flags; + u8 flags; + u8 validate; +}; + /** * struct genl_dumpit_info - info that is available during dumpit op call * @family: generic netlink family - for internal genl code usage @@ -190,7 +242,7 @@ struct genl_ops { */ struct genl_dumpit_info { const struct genl_family *family; - struct genl_ops op; + struct genl_split_ops op; struct nlattr **attrs; }; diff --git a/net/batman-adv/netlink.c b/net/batman-adv/netlink.c index a5e4a4e976cf..ad5714f737be 100644 --- a/net/batman-adv/netlink.c +++ b/net/batman-adv/netlink.c @@ -1267,7 +1267,8 @@ batadv_get_vlan_from_info(struct batadv_priv *bat_priv, struct net *net, * * Return: 0 on success or negative error number in case of failure */ -static int batadv_pre_doit(const struct genl_ops *ops, struct sk_buff *skb, +static int batadv_pre_doit(const struct genl_split_ops *ops, + struct sk_buff *skb, struct genl_info *info) { struct net *net = genl_info_net(info); @@ -1332,7 +1333,8 @@ err_put_softif: * @skb: Netlink message with request data * @info: receiver information */ -static void batadv_post_doit(const struct genl_ops *ops, struct sk_buff *skb, +static void batadv_post_doit(const struct genl_split_ops *ops, + struct sk_buff *skb, struct genl_info *info) { struct batadv_hard_iface *hard_iface; diff --git a/net/core/devlink.c b/net/core/devlink.c index 2dcf2bcc3527..40fcdded57e6 100644 --- a/net/core/devlink.c +++ b/net/core/devlink.c @@ -770,7 +770,7 @@ devlink_region_snapshot_get_by_id(struct devlink_region *region, u32 id) #define DEVLINK_NL_FLAG_NEED_RATE_NODE BIT(3) #define DEVLINK_NL_FLAG_NEED_LINECARD BIT(4) -static int devlink_nl_pre_doit(const struct genl_ops *ops, +static int devlink_nl_pre_doit(const struct genl_split_ops *ops, struct sk_buff *skb, struct genl_info *info) { struct devlink_linecard *linecard; @@ -828,7 +828,7 @@ unlock: return err; } -static void devlink_nl_post_doit(const struct genl_ops *ops, +static void devlink_nl_post_doit(const struct genl_split_ops *ops, struct sk_buff *skb, struct genl_info *info) { struct devlink_linecard *linecard; diff --git a/net/core/drop_monitor.c b/net/core/drop_monitor.c index 11aa6e8a3098..5a782d1d8fd3 100644 --- a/net/core/drop_monitor.c +++ b/net/core/drop_monitor.c @@ -1620,7 +1620,7 @@ static const struct genl_small_ops dropmon_ops[] = { }, }; -static int net_dm_nl_pre_doit(const struct genl_ops *ops, +static int net_dm_nl_pre_doit(const struct genl_split_ops *ops, struct sk_buff *skb, struct genl_info *info) { mutex_lock(&net_dm_mutex); @@ -1628,7 +1628,7 @@ static int net_dm_nl_pre_doit(const struct genl_ops *ops, return 0; } -static void net_dm_nl_post_doit(const struct genl_ops *ops, +static void net_dm_nl_post_doit(const struct genl_split_ops *ops, struct sk_buff *skb, struct genl_info *info) { mutex_unlock(&net_dm_mutex); diff --git a/net/ieee802154/nl802154.c b/net/ieee802154/nl802154.c index 38c4f3cb010e..b33d1b5eda87 100644 --- a/net/ieee802154/nl802154.c +++ b/net/ieee802154/nl802154.c @@ -2157,7 +2157,8 @@ static int nl802154_del_llsec_seclevel(struct sk_buff *skb, #define NL802154_FLAG_CHECK_NETDEV_UP 0x08 #define NL802154_FLAG_NEED_WPAN_DEV 0x10 -static int nl802154_pre_doit(const struct genl_ops *ops, struct sk_buff *skb, +static int nl802154_pre_doit(const struct genl_split_ops *ops, + struct sk_buff *skb, struct genl_info *info) { struct cfg802154_registered_device *rdev; @@ -2219,7 +2220,8 @@ static int nl802154_pre_doit(const struct genl_ops *ops, struct sk_buff *skb, return 0; } -static void nl802154_post_doit(const struct genl_ops *ops, struct sk_buff *skb, +static void nl802154_post_doit(const struct genl_split_ops *ops, + struct sk_buff *skb, struct genl_info *info) { if (info->user_ptr[1]) { diff --git a/net/netlink/genetlink.c b/net/netlink/genetlink.c index 0a7a856e9ce0..c66299740c05 100644 --- a/net/netlink/genetlink.c +++ b/net/netlink/genetlink.c @@ -189,6 +189,51 @@ static int genl_get_cmd(u32 cmd, const struct genl_family *family, return genl_get_cmd_small(cmd, family, op); } +static void +genl_cmd_full_to_split(struct genl_split_ops *op, + const struct genl_family *family, + const struct genl_ops *full, u8 flags) +{ + if (flags & GENL_CMD_CAP_DUMP) { + op->start = full->start; + op->dumpit = full->dumpit; + op->done = full->done; + } else { + op->pre_doit = family->pre_doit; + op->doit = full->doit; + op->post_doit = family->post_doit; + } + + op->policy = full->policy; + op->maxattr = full->maxattr; + + op->cmd = full->cmd; + op->internal_flags = full->internal_flags; + op->flags = full->flags; + op->validate = full->validate; + + /* Make sure flags include the GENL_CMD_CAP_DO / GENL_CMD_CAP_DUMP */ + op->flags |= flags; +} + +static int +genl_get_cmd_split(u32 cmd, u8 flags, const struct genl_family *family, + struct genl_split_ops *op) +{ + struct genl_ops full; + int err; + + err = genl_get_cmd(cmd, family, &full); + if (err) { + memset(op, 0, sizeof(*op)); + return err; + } + + genl_cmd_full_to_split(op, family, &full, flags); + + return 0; +} + static void genl_get_cmd_by_index(unsigned int i, const struct genl_family *family, struct genl_ops *op) @@ -544,7 +589,7 @@ static struct nlattr ** genl_family_rcv_msg_attrs_parse(const struct genl_family *family, struct nlmsghdr *nlh, struct netlink_ext_ack *extack, - const struct genl_ops *ops, + const struct genl_split_ops *ops, int hdrlen, enum genl_validate_flags no_strict_flag) { @@ -580,18 +625,19 @@ struct genl_start_context { const struct genl_family *family; struct nlmsghdr *nlh; struct netlink_ext_ack *extack; - const struct genl_ops *ops; + const struct genl_split_ops *ops; int hdrlen; }; static int genl_start(struct netlink_callback *cb) { struct genl_start_context *ctx = cb->data; - const struct genl_ops *ops = ctx->ops; + const struct genl_split_ops *ops; struct genl_dumpit_info *info; struct nlattr **attrs = NULL; int rc = 0; + ops = ctx->ops; if (ops->validate & GENL_DONT_VALIDATE_DUMP) goto no_attrs; @@ -633,7 +679,7 @@ no_attrs: static int genl_lock_dumpit(struct sk_buff *skb, struct netlink_callback *cb) { - const struct genl_ops *ops = &genl_dumpit_info(cb)->op; + const struct genl_split_ops *ops = &genl_dumpit_info(cb)->op; int rc; genl_lock(); @@ -645,7 +691,7 @@ static int genl_lock_dumpit(struct sk_buff *skb, struct netlink_callback *cb) static int genl_lock_done(struct netlink_callback *cb) { const struct genl_dumpit_info *info = genl_dumpit_info(cb); - const struct genl_ops *ops = &info->op; + const struct genl_split_ops *ops = &info->op; int rc = 0; if (ops->done) { @@ -661,7 +707,7 @@ static int genl_lock_done(struct netlink_callback *cb) static int genl_parallel_done(struct netlink_callback *cb) { const struct genl_dumpit_info *info = genl_dumpit_info(cb); - const struct genl_ops *ops = &info->op; + const struct genl_split_ops *ops = &info->op; int rc = 0; if (ops->done) @@ -675,7 +721,7 @@ static int genl_family_rcv_msg_dumpit(const struct genl_family *family, struct sk_buff *skb, struct nlmsghdr *nlh, struct netlink_ext_ack *extack, - const struct genl_ops *ops, + const struct genl_split_ops *ops, int hdrlen, struct net *net) { struct genl_start_context ctx; @@ -721,7 +767,7 @@ static int genl_family_rcv_msg_doit(const struct genl_family *family, struct sk_buff *skb, struct nlmsghdr *nlh, struct netlink_ext_ack *extack, - const struct genl_ops *ops, + const struct genl_split_ops *ops, int hdrlen, struct net *net) { struct nlattr **attrbuf; @@ -747,16 +793,16 @@ static int genl_family_rcv_msg_doit(const struct genl_family *family, genl_info_net_set(&info, net); memset(&info.user_ptr, 0, sizeof(info.user_ptr)); - if (family->pre_doit) { - err = family->pre_doit(ops, skb, &info); + if (ops->pre_doit) { + err = ops->pre_doit(ops, skb, &info); if (err) goto out; } err = ops->doit(skb, &info); - if (family->post_doit) - family->post_doit(ops, skb, &info); + if (ops->post_doit) + ops->post_doit(ops, skb, &info); out: genl_family_rcv_msg_attrs_free(attrbuf); @@ -801,8 +847,9 @@ static int genl_family_rcv_msg(const struct genl_family *family, { struct net *net = sock_net(skb->sk); struct genlmsghdr *hdr = nlmsg_data(nlh); - struct genl_ops op; + struct genl_split_ops op; int hdrlen; + u8 flags; /* this family doesn't exist in this netns */ if (!family->netnsok && !net_eq(net, &init_net)) @@ -815,7 +862,9 @@ static int genl_family_rcv_msg(const struct genl_family *family, if (genl_header_check(family, nlh, hdr, extack)) return -EINVAL; - if (genl_get_cmd(hdr->cmd, family, &op)) + flags = (nlh->nlmsg_flags & NLM_F_DUMP) == NLM_F_DUMP ? + GENL_CMD_CAP_DUMP : GENL_CMD_CAP_DO; + if (genl_get_cmd_split(hdr->cmd, flags, family, &op)) return -EOPNOTSUPP; if ((op.flags & GENL_ADMIN_PERM) && @@ -826,7 +875,7 @@ static int genl_family_rcv_msg(const struct genl_family *family, !netlink_ns_capable(skb, net->user_ns, CAP_NET_ADMIN)) return -EPERM; - if ((nlh->nlmsg_flags & NLM_F_DUMP) == NLM_F_DUMP) + if (flags & GENL_CMD_CAP_DUMP) return genl_family_rcv_msg_dumpit(family, skb, nlh, extack, &op, hdrlen, net); else diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 148f66edb015..1ad0326ff4dc 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -16140,7 +16140,8 @@ static u32 nl80211_internal_flags[] = { #undef SELECTOR }; -static int nl80211_pre_doit(const struct genl_ops *ops, struct sk_buff *skb, +static int nl80211_pre_doit(const struct genl_split_ops *ops, + struct sk_buff *skb, struct genl_info *info) { struct cfg80211_registered_device *rdev = NULL; @@ -16241,7 +16242,8 @@ out_unlock: return err; } -static void nl80211_post_doit(const struct genl_ops *ops, struct sk_buff *skb, +static void nl80211_post_doit(const struct genl_split_ops *ops, + struct sk_buff *skb, struct genl_info *info) { u32 internal_flags = nl80211_internal_flags[ops->internal_flags]; From 7747eb75f6185a92db2e4a2643fdf6e4b6d87153 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Fri, 4 Nov 2022 12:13:34 -0700 Subject: [PATCH 0680/2002] genetlink: load policy based on validation flags Set the policy and maxattr pointers based on validation flags. genl_family_rcv_msg_attrs_parse() will do nothing and return NULL if maxattrs is zero, so no behavior change is expected. Signed-off-by: Jakub Kicinski Reviewed-by: Jacob Keller Signed-off-by: David S. Miller --- net/netlink/genetlink.c | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/net/netlink/genetlink.c b/net/netlink/genetlink.c index c66299740c05..770726ac491e 100644 --- a/net/netlink/genetlink.c +++ b/net/netlink/genetlink.c @@ -204,8 +204,14 @@ genl_cmd_full_to_split(struct genl_split_ops *op, op->post_doit = family->post_doit; } - op->policy = full->policy; - op->maxattr = full->maxattr; + if (flags & GENL_CMD_CAP_DUMP && + full->validate & GENL_DONT_VALIDATE_DUMP) { + op->policy = NULL; + op->maxattr = 0; + } else { + op->policy = full->policy; + op->maxattr = full->maxattr; + } op->cmd = full->cmd; op->internal_flags = full->internal_flags; @@ -638,10 +644,8 @@ static int genl_start(struct netlink_callback *cb) int rc = 0; ops = ctx->ops; - if (ops->validate & GENL_DONT_VALIDATE_DUMP) - goto no_attrs; - - if (ctx->nlh->nlmsg_len < nlmsg_msg_size(ctx->hdrlen)) + if (!(ops->validate & GENL_DONT_VALIDATE_DUMP) && + ctx->nlh->nlmsg_len < nlmsg_msg_size(ctx->hdrlen)) return -EINVAL; attrs = genl_family_rcv_msg_attrs_parse(ctx->family, ctx->nlh, ctx->extack, @@ -650,7 +654,6 @@ static int genl_start(struct netlink_callback *cb) if (IS_ERR(attrs)) return PTR_ERR(attrs); -no_attrs: info = genl_dumpit_info_alloc(); if (!info) { genl_family_rcv_msg_attrs_free(attrs); From e1a248911d0694c8f95510fcaa920c0b8e99c26c Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Fri, 4 Nov 2022 12:13:35 -0700 Subject: [PATCH 0681/2002] genetlink: check for callback type at op load time Now that genl_get_cmd_split() is informed what type of callback user is trying to access (do or dump) we can check that this callback is indeed available and return an error early. Signed-off-by: Jakub Kicinski Reviewed-by: Jacob Keller Signed-off-by: David S. Miller --- net/netlink/genetlink.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/net/netlink/genetlink.c b/net/netlink/genetlink.c index 770726ac491e..7c04df1bee2b 100644 --- a/net/netlink/genetlink.c +++ b/net/netlink/genetlink.c @@ -189,11 +189,17 @@ static int genl_get_cmd(u32 cmd, const struct genl_family *family, return genl_get_cmd_small(cmd, family, op); } -static void +static int genl_cmd_full_to_split(struct genl_split_ops *op, const struct genl_family *family, const struct genl_ops *full, u8 flags) { + if ((flags & GENL_CMD_CAP_DO && !full->doit) || + (flags & GENL_CMD_CAP_DUMP && !full->dumpit)) { + memset(op, 0, sizeof(*op)); + return -ENOENT; + } + if (flags & GENL_CMD_CAP_DUMP) { op->start = full->start; op->dumpit = full->dumpit; @@ -220,6 +226,8 @@ genl_cmd_full_to_split(struct genl_split_ops *op, /* Make sure flags include the GENL_CMD_CAP_DO / GENL_CMD_CAP_DUMP */ op->flags |= flags; + + return 0; } static int @@ -235,9 +243,7 @@ genl_get_cmd_split(u32 cmd, u8 flags, const struct genl_family *family, return err; } - genl_cmd_full_to_split(op, family, &full, flags); - - return 0; + return genl_cmd_full_to_split(op, family, &full, flags); } static void genl_get_cmd_by_index(unsigned int i, @@ -730,9 +736,6 @@ static int genl_family_rcv_msg_dumpit(const struct genl_family *family, struct genl_start_context ctx; int err; - if (!ops->dumpit) - return -EOPNOTSUPP; - ctx.family = family; ctx.nlh = nlh; ctx.extack = extack; @@ -777,9 +780,6 @@ static int genl_family_rcv_msg_doit(const struct genl_family *family, struct genl_info info; int err; - if (!ops->doit) - return -EOPNOTSUPP; - attrbuf = genl_family_rcv_msg_attrs_parse(family, nlh, extack, ops, hdrlen, GENL_DONT_VALIDATE_STRICT); From 92d3d9ba9bb3ab17b5bd4fd46032ced0264872c9 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Fri, 4 Nov 2022 12:13:36 -0700 Subject: [PATCH 0682/2002] genetlink: add policies for both doit and dumpit in ctrl_dumppolicy_start() Separate adding doit and dumpit policies for CTRL_CMD_GETPOLICY. This has no effect until we actually allow do and dump to come from different sources as netlink_policy_dump_add_policy() does deduplication. Signed-off-by: Jakub Kicinski Reviewed-by: Jacob Keller Signed-off-by: David S. Miller --- net/netlink/genetlink.c | 48 ++++++++++++++++++++++++++++++++--------- 1 file changed, 38 insertions(+), 10 deletions(-) diff --git a/net/netlink/genetlink.c b/net/netlink/genetlink.c index 7c04df1bee2b..d0c35738839b 100644 --- a/net/netlink/genetlink.c +++ b/net/netlink/genetlink.c @@ -1260,29 +1260,57 @@ static int ctrl_dumppolicy_start(struct netlink_callback *cb) ctx->rt = rt; if (tb[CTRL_ATTR_OP]) { + struct genl_split_ops doit, dump; + ctx->single_op = true; ctx->op = nla_get_u32(tb[CTRL_ATTR_OP]); - err = genl_get_cmd(ctx->op, rt, &op); - if (err) { + if (genl_get_cmd_split(ctx->op, GENL_CMD_CAP_DO, rt, &doit) && + genl_get_cmd_split(ctx->op, GENL_CMD_CAP_DUMP, rt, &dump)) { NL_SET_BAD_ATTR(cb->extack, tb[CTRL_ATTR_OP]); - return err; + return -ENOENT; } - if (!op.policy) - return -ENODATA; + if (doit.policy) { + err = netlink_policy_dump_add_policy(&ctx->state, + doit.policy, + doit.maxattr); + if (err) + goto err_free_state; + } + if (dump.policy) { + err = netlink_policy_dump_add_policy(&ctx->state, + dump.policy, + dump.maxattr); + if (err) + goto err_free_state; + } - return netlink_policy_dump_add_policy(&ctx->state, op.policy, - op.maxattr); + if (!ctx->state) + return -ENODATA; + return 0; } for (i = 0; i < genl_get_cmd_cnt(rt); i++) { + struct genl_split_ops doit, dumpit; + genl_get_cmd_by_index(i, rt, &op); - if (op.policy) { + genl_cmd_full_to_split(&doit, ctx->rt, &op, GENL_CMD_CAP_DO); + genl_cmd_full_to_split(&dumpit, ctx->rt, + &op, GENL_CMD_CAP_DUMP); + + if (doit.policy) { err = netlink_policy_dump_add_policy(&ctx->state, - op.policy, - op.maxattr); + doit.policy, + doit.maxattr); + if (err) + goto err_free_state; + } + if (dumpit.policy) { + err = netlink_policy_dump_add_policy(&ctx->state, + dumpit.policy, + dumpit.maxattr); if (err) goto err_free_state; } From 26588edbef6094c41734b4443c2d7b4c4bd0085f Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Fri, 4 Nov 2022 12:13:37 -0700 Subject: [PATCH 0683/2002] genetlink: support split policies in ctrl_dumppolicy_put_op() Pass do and dump versions of the op to ctrl_dumppolicy_put_op() so that it can provide a different policy index for the two. Since we now look at policies, and those are set appropriately there's no need to look at the GENL_DONT_VALIDATE_DUMP flag. Signed-off-by: Jakub Kicinski Reviewed-by: Jacob Keller Signed-off-by: David S. Miller --- net/netlink/genetlink.c | 55 ++++++++++++++++++++++++----------------- 1 file changed, 33 insertions(+), 22 deletions(-) diff --git a/net/netlink/genetlink.c b/net/netlink/genetlink.c index d0c35738839b..93e33e20a0e8 100644 --- a/net/netlink/genetlink.c +++ b/net/netlink/genetlink.c @@ -1345,7 +1345,8 @@ static void *ctrl_dumppolicy_prep(struct sk_buff *skb, static int ctrl_dumppolicy_put_op(struct sk_buff *skb, struct netlink_callback *cb, - struct genl_ops *op) + struct genl_split_ops *doit, + struct genl_split_ops *dumpit) { struct ctrl_dump_policy_ctx *ctx = (void *)cb->ctx; struct nlattr *nest_pol, *nest_op; @@ -1353,10 +1354,7 @@ static int ctrl_dumppolicy_put_op(struct sk_buff *skb, int idx; /* skip if we have nothing to show */ - if (!op->policy) - return 0; - if (!op->doit && - (!op->dumpit || op->validate & GENL_DONT_VALIDATE_DUMP)) + if (!doit->policy && !dumpit->policy) return 0; hdr = ctrl_dumppolicy_prep(skb, cb); @@ -1367,21 +1365,26 @@ static int ctrl_dumppolicy_put_op(struct sk_buff *skb, if (!nest_pol) goto err; - nest_op = nla_nest_start(skb, op->cmd); + nest_op = nla_nest_start(skb, doit->cmd); if (!nest_op) goto err; - /* for now both do/dump are always the same */ - idx = netlink_policy_dump_get_policy_idx(ctx->state, - op->policy, - op->maxattr); + if (doit->policy) { + idx = netlink_policy_dump_get_policy_idx(ctx->state, + doit->policy, + doit->maxattr); - if (op->doit && nla_put_u32(skb, CTRL_ATTR_POLICY_DO, idx)) - goto err; + if (nla_put_u32(skb, CTRL_ATTR_POLICY_DO, idx)) + goto err; + } + if (dumpit->policy) { + idx = netlink_policy_dump_get_policy_idx(ctx->state, + dumpit->policy, + dumpit->maxattr); - if (op->dumpit && !(op->validate & GENL_DONT_VALIDATE_DUMP) && - nla_put_u32(skb, CTRL_ATTR_POLICY_DUMP, idx)) - goto err; + if (nla_put_u32(skb, CTRL_ATTR_POLICY_DUMP, idx)) + goto err; + } nla_nest_end(skb, nest_op); nla_nest_end(skb, nest_pol); @@ -1399,16 +1402,19 @@ static int ctrl_dumppolicy(struct sk_buff *skb, struct netlink_callback *cb) void *hdr; if (!ctx->policies) { + struct genl_split_ops doit, dumpit; struct genl_ops op; if (ctx->single_op) { - int err; + if (genl_get_cmd_split(ctx->op, GENL_CMD_CAP_DO, + ctx->rt, &doit) && + genl_get_cmd_split(ctx->op, GENL_CMD_CAP_DUMP, + ctx->rt, &dumpit)) { + WARN_ON(1); + return -ENOENT; + } - err = genl_get_cmd(ctx->op, ctx->rt, &op); - if (WARN_ON(err)) - return err; - - if (ctrl_dumppolicy_put_op(skb, cb, &op)) + if (ctrl_dumppolicy_put_op(skb, cb, &doit, &dumpit)) return skb->len; /* don't enter the loop below */ @@ -1418,7 +1424,12 @@ static int ctrl_dumppolicy(struct sk_buff *skb, struct netlink_callback *cb) while (ctx->opidx < genl_get_cmd_cnt(ctx->rt)) { genl_get_cmd_by_index(ctx->opidx, ctx->rt, &op); - if (ctrl_dumppolicy_put_op(skb, cb, &op)) + genl_cmd_full_to_split(&doit, ctx->rt, + &op, GENL_CMD_CAP_DO); + genl_cmd_full_to_split(&dumpit, ctx->rt, + &op, GENL_CMD_CAP_DUMP); + + if (ctrl_dumppolicy_put_op(skb, cb, &doit, &dumpit)) return skb->len; ctx->opidx++; From 8d84322ae6d7921c461b6a5ef39be0b48c177b8a Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Fri, 4 Nov 2022 12:13:38 -0700 Subject: [PATCH 0684/2002] genetlink: inline genl_get_cmd() All callers go via genl_get_cmd_split() now, so rename it to genl_get_cmd() remove the original. Reviewed-by: Jacob Keller Signed-off-by: Jakub Kicinski Signed-off-by: David S. Miller --- net/netlink/genetlink.c | 30 ++++++++++++------------------ 1 file changed, 12 insertions(+), 18 deletions(-) diff --git a/net/netlink/genetlink.c b/net/netlink/genetlink.c index 93e33e20a0e8..ec32b6063a3f 100644 --- a/net/netlink/genetlink.c +++ b/net/netlink/genetlink.c @@ -181,14 +181,6 @@ static int genl_get_cmd_small(u32 cmd, const struct genl_family *family, return -ENOENT; } -static int genl_get_cmd(u32 cmd, const struct genl_family *family, - struct genl_ops *op) -{ - if (!genl_get_cmd_full(cmd, family, op)) - return 0; - return genl_get_cmd_small(cmd, family, op); -} - static int genl_cmd_full_to_split(struct genl_split_ops *op, const struct genl_family *family, @@ -231,13 +223,15 @@ genl_cmd_full_to_split(struct genl_split_ops *op, } static int -genl_get_cmd_split(u32 cmd, u8 flags, const struct genl_family *family, - struct genl_split_ops *op) +genl_get_cmd(u32 cmd, u8 flags, const struct genl_family *family, + struct genl_split_ops *op) { struct genl_ops full; int err; - err = genl_get_cmd(cmd, family, &full); + err = genl_get_cmd_full(cmd, family, &full); + if (err == -ENOENT) + err = genl_get_cmd_small(cmd, family, &full); if (err) { memset(op, 0, sizeof(*op)); return err; @@ -867,7 +861,7 @@ static int genl_family_rcv_msg(const struct genl_family *family, flags = (nlh->nlmsg_flags & NLM_F_DUMP) == NLM_F_DUMP ? GENL_CMD_CAP_DUMP : GENL_CMD_CAP_DO; - if (genl_get_cmd_split(hdr->cmd, flags, family, &op)) + if (genl_get_cmd(hdr->cmd, flags, family, &op)) return -EOPNOTSUPP; if ((op.flags & GENL_ADMIN_PERM) && @@ -1265,8 +1259,8 @@ static int ctrl_dumppolicy_start(struct netlink_callback *cb) ctx->single_op = true; ctx->op = nla_get_u32(tb[CTRL_ATTR_OP]); - if (genl_get_cmd_split(ctx->op, GENL_CMD_CAP_DO, rt, &doit) && - genl_get_cmd_split(ctx->op, GENL_CMD_CAP_DUMP, rt, &dump)) { + if (genl_get_cmd(ctx->op, GENL_CMD_CAP_DO, rt, &doit) && + genl_get_cmd(ctx->op, GENL_CMD_CAP_DUMP, rt, &dump)) { NL_SET_BAD_ATTR(cb->extack, tb[CTRL_ATTR_OP]); return -ENOENT; } @@ -1406,10 +1400,10 @@ static int ctrl_dumppolicy(struct sk_buff *skb, struct netlink_callback *cb) struct genl_ops op; if (ctx->single_op) { - if (genl_get_cmd_split(ctx->op, GENL_CMD_CAP_DO, - ctx->rt, &doit) && - genl_get_cmd_split(ctx->op, GENL_CMD_CAP_DUMP, - ctx->rt, &dumpit)) { + if (genl_get_cmd(ctx->op, GENL_CMD_CAP_DO, + ctx->rt, &doit) && + genl_get_cmd(ctx->op, GENL_CMD_CAP_DUMP, + ctx->rt, &dumpit)) { WARN_ON(1); return -ENOENT; } From 6557461cd27843213ca19a7918bbaa8c3b7c5f5c Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Fri, 4 Nov 2022 12:13:39 -0700 Subject: [PATCH 0685/2002] genetlink: add iterator for walking family ops Subsequent changes will expose split op structures to users, so walking the family ops with just an index will get harder. Add a structured iterator, convert the simple cases. Policy dumping needs more careful conversion. Signed-off-by: Jakub Kicinski Reviewed-by: Jacob Keller Signed-off-by: David S. Miller --- net/netlink/genetlink.c | 119 ++++++++++++++++++++++++++-------------- 1 file changed, 77 insertions(+), 42 deletions(-) diff --git a/net/netlink/genetlink.c b/net/netlink/genetlink.c index ec32b6063a3f..06bf091c1b8a 100644 --- a/net/netlink/genetlink.c +++ b/net/netlink/genetlink.c @@ -252,6 +252,57 @@ static void genl_get_cmd_by_index(unsigned int i, WARN_ON_ONCE(1); } +struct genl_op_iter { + const struct genl_family *family; + struct genl_split_ops doit; + struct genl_split_ops dumpit; + int i; + u32 cmd; + u8 flags; +}; + +static bool +genl_op_iter_init(const struct genl_family *family, struct genl_op_iter *iter) +{ + iter->family = family; + iter->i = 0; + + iter->flags = 0; + + return genl_get_cmd_cnt(iter->family); +} + +static bool genl_op_iter_next(struct genl_op_iter *iter) +{ + struct genl_ops op; + + if (iter->i >= genl_get_cmd_cnt(iter->family)) + return false; + + genl_get_cmd_by_index(iter->i, iter->family, &op); + iter->i++; + + genl_cmd_full_to_split(&iter->doit, iter->family, &op, GENL_CMD_CAP_DO); + genl_cmd_full_to_split(&iter->dumpit, iter->family, + &op, GENL_CMD_CAP_DUMP); + + iter->cmd = iter->doit.cmd | iter->dumpit.cmd; + iter->flags = iter->doit.flags | iter->dumpit.flags; + + return true; +} + +static void +genl_op_iter_copy(struct genl_op_iter *dst, struct genl_op_iter *src) +{ + *dst = *src; +} + +static unsigned int genl_op_iter_idx(struct genl_op_iter *iter) +{ + return iter->i; +} + static int genl_allocate_reserve_groups(int n_groups, int *first_id) { unsigned long *new_groups; @@ -419,25 +470,23 @@ static void genl_unregister_mc_groups(const struct genl_family *family) static int genl_validate_ops(const struct genl_family *family) { - int i, j; + struct genl_op_iter i, j; if (WARN_ON(family->n_ops && !family->ops) || WARN_ON(family->n_small_ops && !family->small_ops)) return -EINVAL; - for (i = 0; i < genl_get_cmd_cnt(family); i++) { - struct genl_ops op; - - genl_get_cmd_by_index(i, family, &op); - if (op.dumpit == NULL && op.doit == NULL) + for (genl_op_iter_init(family, &i); genl_op_iter_next(&i); ) { + if (!(i.flags & (GENL_CMD_CAP_DO | GENL_CMD_CAP_DUMP))) return -EINVAL; - if (WARN_ON(op.cmd >= family->resv_start_op && op.validate)) - return -EINVAL; - for (j = i + 1; j < genl_get_cmd_cnt(family); j++) { - struct genl_ops op2; - genl_get_cmd_by_index(j, family, &op2); - if (op.cmd == op2.cmd) + if (WARN_ON(i.cmd >= family->resv_start_op && + (i.doit.validate || i.dumpit.validate))) + return -EINVAL; + + genl_op_iter_copy(&j, &i); + while (genl_op_iter_next(&j)) { + if (i.cmd == j.cmd) return -EINVAL; } } @@ -917,6 +966,7 @@ static struct genl_family genl_ctrl; static int ctrl_fill_info(const struct genl_family *family, u32 portid, u32 seq, u32 flags, struct sk_buff *skb, u8 cmd) { + struct genl_op_iter i; void *hdr; hdr = genlmsg_put(skb, portid, seq, &genl_ctrl, flags, cmd); @@ -930,33 +980,26 @@ static int ctrl_fill_info(const struct genl_family *family, u32 portid, u32 seq, nla_put_u32(skb, CTRL_ATTR_MAXATTR, family->maxattr)) goto nla_put_failure; - if (genl_get_cmd_cnt(family)) { + if (genl_op_iter_init(family, &i)) { struct nlattr *nla_ops; - int i; nla_ops = nla_nest_start_noflag(skb, CTRL_ATTR_OPS); if (nla_ops == NULL) goto nla_put_failure; - for (i = 0; i < genl_get_cmd_cnt(family); i++) { + while (genl_op_iter_next(&i)) { struct nlattr *nest; - struct genl_ops op; u32 op_flags; - genl_get_cmd_by_index(i, family, &op); - op_flags = op.flags; - if (op.dumpit) - op_flags |= GENL_CMD_CAP_DUMP; - if (op.doit) - op_flags |= GENL_CMD_CAP_DO; - if (op.policy) + op_flags = i.flags; + if (i.doit.policy || i.dumpit.policy) op_flags |= GENL_CMD_CAP_HASPOL; - nest = nla_nest_start_noflag(skb, i + 1); + nest = nla_nest_start_noflag(skb, genl_op_iter_idx(&i)); if (nest == NULL) goto nla_put_failure; - if (nla_put_u32(skb, CTRL_ATTR_OP_ID, op.cmd) || + if (nla_put_u32(skb, CTRL_ATTR_OP_ID, i.cmd) || nla_put_u32(skb, CTRL_ATTR_OP_FLAGS, op_flags)) goto nla_put_failure; @@ -1229,8 +1272,8 @@ static int ctrl_dumppolicy_start(struct netlink_callback *cb) struct ctrl_dump_policy_ctx *ctx = (void *)cb->ctx; struct nlattr **tb = info->attrs; const struct genl_family *rt; - struct genl_ops op; - int err, i; + struct genl_op_iter i; + int err; BUILD_BUG_ON(sizeof(*ctx) > sizeof(cb->ctx)); @@ -1285,26 +1328,18 @@ static int ctrl_dumppolicy_start(struct netlink_callback *cb) return 0; } - for (i = 0; i < genl_get_cmd_cnt(rt); i++) { - struct genl_split_ops doit, dumpit; - - genl_get_cmd_by_index(i, rt, &op); - - genl_cmd_full_to_split(&doit, ctx->rt, &op, GENL_CMD_CAP_DO); - genl_cmd_full_to_split(&dumpit, ctx->rt, - &op, GENL_CMD_CAP_DUMP); - - if (doit.policy) { + for (genl_op_iter_init(rt, &i); genl_op_iter_next(&i); ) { + if (i.doit.policy) { err = netlink_policy_dump_add_policy(&ctx->state, - doit.policy, - doit.maxattr); + i.doit.policy, + i.doit.maxattr); if (err) goto err_free_state; } - if (dumpit.policy) { + if (i.dumpit.policy) { err = netlink_policy_dump_add_policy(&ctx->state, - dumpit.policy, - dumpit.maxattr); + i.dumpit.policy, + i.dumpit.maxattr); if (err) goto err_free_state; } From b502b3185cd68f24510f8a253bf31a1b34605bb6 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Fri, 4 Nov 2022 12:13:40 -0700 Subject: [PATCH 0686/2002] genetlink: use iterator in the op to policy map dumping We can't put the full iterator in the struct ctrl_dump_policy_ctx because dump context is statically sized by netlink core. Allocate it dynamically. Rename policy to dump_map to make the logic a little easier to follow. Signed-off-by: Jakub Kicinski Reviewed-by: Jacob Keller Signed-off-by: David S. Miller --- net/netlink/genetlink.c | 49 ++++++++++++++++++++++------------------- 1 file changed, 26 insertions(+), 23 deletions(-) diff --git a/net/netlink/genetlink.c b/net/netlink/genetlink.c index 06bf091c1b8a..4b8c65d9e9d3 100644 --- a/net/netlink/genetlink.c +++ b/net/netlink/genetlink.c @@ -1252,10 +1252,10 @@ static int genl_ctrl_event(int event, const struct genl_family *family, struct ctrl_dump_policy_ctx { struct netlink_policy_dump_state *state; const struct genl_family *rt; - unsigned int opidx; + struct genl_op_iter *op_iter; u32 op; u16 fam_id; - u8 policies:1, + u8 dump_map:1, single_op:1; }; @@ -1325,9 +1325,16 @@ static int ctrl_dumppolicy_start(struct netlink_callback *cb) if (!ctx->state) return -ENODATA; + + ctx->dump_map = 1; return 0; } + ctx->op_iter = kmalloc(sizeof(*ctx->op_iter), GFP_KERNEL); + if (!ctx->op_iter) + return -ENOMEM; + ctx->dump_map = genl_op_iter_init(rt, ctx->op_iter); + for (genl_op_iter_init(rt, &i); genl_op_iter_next(&i); ) { if (i.doit.policy) { err = netlink_policy_dump_add_policy(&ctx->state, @@ -1345,12 +1352,16 @@ static int ctrl_dumppolicy_start(struct netlink_callback *cb) } } - if (!ctx->state) - return -ENODATA; + if (!ctx->state) { + err = -ENODATA; + goto err_free_op_iter; + } return 0; err_free_state: netlink_policy_dump_free(ctx->state); +err_free_op_iter: + kfree(ctx->op_iter); return err; } @@ -1430,11 +1441,10 @@ static int ctrl_dumppolicy(struct sk_buff *skb, struct netlink_callback *cb) struct ctrl_dump_policy_ctx *ctx = (void *)cb->ctx; void *hdr; - if (!ctx->policies) { - struct genl_split_ops doit, dumpit; - struct genl_ops op; - + if (ctx->dump_map) { if (ctx->single_op) { + struct genl_split_ops doit, dumpit; + if (genl_get_cmd(ctx->op, GENL_CMD_CAP_DO, ctx->rt, &doit) && genl_get_cmd(ctx->op, GENL_CMD_CAP_DUMP, @@ -1446,26 +1456,18 @@ static int ctrl_dumppolicy(struct sk_buff *skb, struct netlink_callback *cb) if (ctrl_dumppolicy_put_op(skb, cb, &doit, &dumpit)) return skb->len; - /* don't enter the loop below */ - ctx->opidx = genl_get_cmd_cnt(ctx->rt); + /* done with the per-op policy index list */ + ctx->dump_map = 0; } - while (ctx->opidx < genl_get_cmd_cnt(ctx->rt)) { - genl_get_cmd_by_index(ctx->opidx, ctx->rt, &op); - - genl_cmd_full_to_split(&doit, ctx->rt, - &op, GENL_CMD_CAP_DO); - genl_cmd_full_to_split(&dumpit, ctx->rt, - &op, GENL_CMD_CAP_DUMP); - - if (ctrl_dumppolicy_put_op(skb, cb, &doit, &dumpit)) + while (ctx->dump_map) { + if (ctrl_dumppolicy_put_op(skb, cb, + &ctx->op_iter->doit, + &ctx->op_iter->dumpit)) return skb->len; - ctx->opidx++; + ctx->dump_map = genl_op_iter_next(ctx->op_iter); } - - /* completed with the per-op policy index list */ - ctx->policies = true; } while (netlink_policy_dump_loop(ctx->state)) { @@ -1498,6 +1500,7 @@ static int ctrl_dumppolicy_done(struct netlink_callback *cb) { struct ctrl_dump_policy_ctx *ctx = (void *)cb->ctx; + kfree(ctx->op_iter); netlink_policy_dump_free(ctx->state); return 0; } From 7acfbbe17c18554960b812a95c26d7ef9b7b28ac Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Fri, 4 Nov 2022 12:13:41 -0700 Subject: [PATCH 0687/2002] genetlink: inline old iteration helpers All dumpers use the iterators now, inline the cmd by index stuff into iterator code. Reviewed-by: Jacob Keller Signed-off-by: Jakub Kicinski Signed-off-by: David S. Miller --- net/netlink/genetlink.c | 32 +++++++++----------------------- 1 file changed, 9 insertions(+), 23 deletions(-) diff --git a/net/netlink/genetlink.c b/net/netlink/genetlink.c index 4b8c65d9e9d3..0a4f1470f442 100644 --- a/net/netlink/genetlink.c +++ b/net/netlink/genetlink.c @@ -118,11 +118,6 @@ static const struct genl_family *genl_family_find_byname(char *name) return NULL; } -static int genl_get_cmd_cnt(const struct genl_family *family) -{ - return family->n_ops + family->n_small_ops; -} - static void genl_op_from_full(const struct genl_family *family, unsigned int i, struct genl_ops *op) { @@ -240,18 +235,6 @@ genl_get_cmd(u32 cmd, u8 flags, const struct genl_family *family, return genl_cmd_full_to_split(op, family, &full, flags); } -static void genl_get_cmd_by_index(unsigned int i, - const struct genl_family *family, - struct genl_ops *op) -{ - if (i < family->n_ops) - genl_op_from_full(family, i, op); - else if (i < family->n_ops + family->n_small_ops) - genl_op_from_small(family, i - family->n_ops, op); - else - WARN_ON_ONCE(1); -} - struct genl_op_iter { const struct genl_family *family; struct genl_split_ops doit; @@ -269,22 +252,25 @@ genl_op_iter_init(const struct genl_family *family, struct genl_op_iter *iter) iter->flags = 0; - return genl_get_cmd_cnt(iter->family); + return iter->family->n_ops + iter->family->n_small_ops; } static bool genl_op_iter_next(struct genl_op_iter *iter) { + const struct genl_family *family = iter->family; struct genl_ops op; - if (iter->i >= genl_get_cmd_cnt(iter->family)) + if (iter->i < family->n_ops) + genl_op_from_full(family, iter->i, &op); + else if (iter->i < family->n_ops + family->n_small_ops) + genl_op_from_small(family, iter->i - family->n_ops, &op); + else return false; - genl_get_cmd_by_index(iter->i, iter->family, &op); iter->i++; - genl_cmd_full_to_split(&iter->doit, iter->family, &op, GENL_CMD_CAP_DO); - genl_cmd_full_to_split(&iter->dumpit, iter->family, - &op, GENL_CMD_CAP_DUMP); + genl_cmd_full_to_split(&iter->doit, family, &op, GENL_CMD_CAP_DO); + genl_cmd_full_to_split(&iter->dumpit, family, &op, GENL_CMD_CAP_DUMP); iter->cmd = iter->doit.cmd | iter->dumpit.cmd; iter->flags = iter->doit.flags | iter->dumpit.flags; From b8fd60c36a44351f773432e24efd8bb92f8ba0c6 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Fri, 4 Nov 2022 12:13:42 -0700 Subject: [PATCH 0688/2002] genetlink: allow families to use split ops directly Let families to hook in the new split ops. They are more flexible and should not be much larger than full ops. Each split op is 40B while full op is 48B. Devlink for example has 54 dos and 19 dumps, 2 of the dumps do not have a do -> 56 full commands = 2688B. Split ops would have taken 2920B, so 9% more space while allowing individual per/post doit and per-type policies. Signed-off-by: Jakub Kicinski Reviewed-by: Jacob Keller Signed-off-by: David S. Miller --- include/net/genetlink.h | 5 ++ net/netlink/genetlink.c | 170 ++++++++++++++++++++++++++++++++++------ 2 files changed, 149 insertions(+), 26 deletions(-) diff --git a/include/net/genetlink.h b/include/net/genetlink.h index 4be7989c451b..d21210709f84 100644 --- a/include/net/genetlink.h +++ b/include/net/genetlink.h @@ -46,6 +46,9 @@ struct genl_info; * @n_ops: number of operations supported by this family * @small_ops: the small-struct operations supported by this family * @n_small_ops: number of small-struct operations supported by this family + * @split_ops: the split do/dump form of operation definition + * @n_split_ops: number of entries in @split_ops, not that with split do/dump + * ops the number of entries is not the same as number of commands * * Attribute policies (the combination of @policy and @maxattr fields) * can be attached at the family level or at the operation level. @@ -63,6 +66,7 @@ struct genl_family { u8 parallel_ops:1; u8 n_ops; u8 n_small_ops; + u8 n_split_ops; u8 n_mcgrps; u8 resv_start_op; const struct nla_policy *policy; @@ -74,6 +78,7 @@ struct genl_family { struct genl_info *info); const struct genl_ops * ops; const struct genl_small_ops *small_ops; + const struct genl_split_ops *split_ops; const struct genl_multicast_group *mcgrps; struct module *module; diff --git a/net/netlink/genetlink.c b/net/netlink/genetlink.c index 0a4f1470f442..90b0feb5eb73 100644 --- a/net/netlink/genetlink.c +++ b/net/netlink/genetlink.c @@ -101,6 +101,17 @@ genl_op_fill_in_reject_policy(const struct genl_family *family, op->maxattr = 1; } +static void +genl_op_fill_in_reject_policy_split(const struct genl_family *family, + struct genl_split_ops *op) +{ + if (op->policy) + return; + + op->policy = genl_policy_reject_all; + op->maxattr = 1; +} + static const struct genl_family *genl_family_find_byid(unsigned int id) { return idr_find(&genl_fam_idr, id); @@ -118,6 +129,16 @@ static const struct genl_family *genl_family_find_byname(char *name) return NULL; } +struct genl_op_iter { + const struct genl_family *family; + struct genl_split_ops doit; + struct genl_split_ops dumpit; + int cmd_idx; + int entry_idx; + u32 cmd; + u8 flags; +}; + static void genl_op_from_full(const struct genl_family *family, unsigned int i, struct genl_ops *op) { @@ -176,6 +197,50 @@ static int genl_get_cmd_small(u32 cmd, const struct genl_family *family, return -ENOENT; } +static void genl_op_from_split(struct genl_op_iter *iter) +{ + const struct genl_family *family = iter->family; + int i, cnt = 0; + + i = iter->entry_idx - family->n_ops - family->n_small_ops; + + if (family->split_ops[i + cnt].flags & GENL_CMD_CAP_DO) { + iter->doit = family->split_ops[i + cnt]; + genl_op_fill_in_reject_policy_split(family, &iter->doit); + cnt++; + } else { + memset(&iter->doit, 0, sizeof(iter->doit)); + } + + if (i + cnt < family->n_split_ops && + family->split_ops[i + cnt].flags & GENL_CMD_CAP_DUMP) { + iter->dumpit = family->split_ops[i + cnt]; + genl_op_fill_in_reject_policy_split(family, &iter->dumpit); + cnt++; + } else { + memset(&iter->dumpit, 0, sizeof(iter->dumpit)); + } + + WARN_ON(!cnt); + iter->entry_idx += cnt; +} + +static int +genl_get_cmd_split(u32 cmd, u8 flag, const struct genl_family *family, + struct genl_split_ops *op) +{ + int i; + + for (i = 0; i < family->n_split_ops; i++) + if (family->split_ops[i].cmd == cmd && + family->split_ops[i].flags & flag) { + *op = family->split_ops[i]; + return 0; + } + + return -ENOENT; +} + static int genl_cmd_full_to_split(struct genl_split_ops *op, const struct genl_family *family, @@ -227,50 +292,60 @@ genl_get_cmd(u32 cmd, u8 flags, const struct genl_family *family, err = genl_get_cmd_full(cmd, family, &full); if (err == -ENOENT) err = genl_get_cmd_small(cmd, family, &full); - if (err) { + /* Found one of legacy forms */ + if (err == 0) + return genl_cmd_full_to_split(op, family, &full, flags); + + err = genl_get_cmd_split(cmd, flags, family, op); + if (err) memset(op, 0, sizeof(*op)); - return err; - } - - return genl_cmd_full_to_split(op, family, &full, flags); + return err; } -struct genl_op_iter { - const struct genl_family *family; - struct genl_split_ops doit; - struct genl_split_ops dumpit; - int i; - u32 cmd; - u8 flags; -}; - static bool genl_op_iter_init(const struct genl_family *family, struct genl_op_iter *iter) { iter->family = family; - iter->i = 0; + iter->cmd_idx = 0; + iter->entry_idx = 0; iter->flags = 0; - return iter->family->n_ops + iter->family->n_small_ops; + return iter->family->n_ops + + iter->family->n_small_ops + + iter->family->n_split_ops; } static bool genl_op_iter_next(struct genl_op_iter *iter) { const struct genl_family *family = iter->family; + bool legacy_op = true; struct genl_ops op; - if (iter->i < family->n_ops) - genl_op_from_full(family, iter->i, &op); - else if (iter->i < family->n_ops + family->n_small_ops) - genl_op_from_small(family, iter->i - family->n_ops, &op); - else + if (iter->entry_idx < family->n_ops) { + genl_op_from_full(family, iter->entry_idx, &op); + } else if (iter->entry_idx < family->n_ops + family->n_small_ops) { + genl_op_from_small(family, iter->entry_idx - family->n_ops, + &op); + } else if (iter->entry_idx < + family->n_ops + family->n_small_ops + family->n_split_ops) { + legacy_op = false; + /* updates entry_idx */ + genl_op_from_split(iter); + } else { return false; + } - iter->i++; + iter->cmd_idx++; - genl_cmd_full_to_split(&iter->doit, family, &op, GENL_CMD_CAP_DO); - genl_cmd_full_to_split(&iter->dumpit, family, &op, GENL_CMD_CAP_DUMP); + if (legacy_op) { + iter->entry_idx++; + + genl_cmd_full_to_split(&iter->doit, family, + &op, GENL_CMD_CAP_DO); + genl_cmd_full_to_split(&iter->dumpit, family, + &op, GENL_CMD_CAP_DUMP); + } iter->cmd = iter->doit.cmd | iter->dumpit.cmd; iter->flags = iter->doit.flags | iter->dumpit.flags; @@ -286,7 +361,7 @@ genl_op_iter_copy(struct genl_op_iter *dst, struct genl_op_iter *src) static unsigned int genl_op_iter_idx(struct genl_op_iter *iter) { - return iter->i; + return iter->cmd_idx; } static int genl_allocate_reserve_groups(int n_groups, int *first_id) @@ -454,12 +529,22 @@ static void genl_unregister_mc_groups(const struct genl_family *family) } } +static bool genl_split_op_check(const struct genl_split_ops *op) +{ + if (WARN_ON(hweight8(op->flags & (GENL_CMD_CAP_DO | + GENL_CMD_CAP_DUMP)) != 1)) + return true; + return false; +} + static int genl_validate_ops(const struct genl_family *family) { struct genl_op_iter i, j; + unsigned int s; if (WARN_ON(family->n_ops && !family->ops) || - WARN_ON(family->n_small_ops && !family->small_ops)) + WARN_ON(family->n_small_ops && !family->small_ops) || + WARN_ON(family->n_split_ops && !family->split_ops)) return -EINVAL; for (genl_op_iter_init(family, &i); genl_op_iter_next(&i); ) { @@ -477,6 +562,39 @@ static int genl_validate_ops(const struct genl_family *family) } } + if (family->n_split_ops) { + if (genl_split_op_check(&family->split_ops[0])) + return -EINVAL; + } + + for (s = 1; s < family->n_split_ops; s++) { + const struct genl_split_ops *a, *b; + + a = &family->split_ops[s - 1]; + b = &family->split_ops[s]; + + if (genl_split_op_check(b)) + return -EINVAL; + + /* Check sort order */ + if (a->cmd < b->cmd) + continue; + + if (a->internal_flags != b->internal_flags || + ((a->flags ^ b->flags) & ~(GENL_CMD_CAP_DO | + GENL_CMD_CAP_DUMP))) { + WARN_ON(1); + return -EINVAL; + } + + if ((a->flags & GENL_CMD_CAP_DO) && + (b->flags & GENL_CMD_CAP_DUMP)) + continue; + + WARN_ON(1); + return -EINVAL; + } + return 0; } From aba22ca8ccd6954ffbb1a7a671f34300887b4727 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Fri, 4 Nov 2022 12:13:43 -0700 Subject: [PATCH 0689/2002] genetlink: convert control family to split ops Prove that the split ops work. Sadly we need to keep bug-wards compatibility and specify the same policy for dump as do, even tho we don't parse inputs for the dump. Signed-off-by: Jakub Kicinski Reviewed-by: Jacob Keller Signed-off-by: David S. Miller --- net/netlink/genetlink.c | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/net/netlink/genetlink.c b/net/netlink/genetlink.c index 90b0feb5eb73..362a61179036 100644 --- a/net/netlink/genetlink.c +++ b/net/netlink/genetlink.c @@ -1609,14 +1609,22 @@ static int ctrl_dumppolicy_done(struct netlink_callback *cb) return 0; } -static const struct genl_ops genl_ctrl_ops[] = { +static const struct genl_split_ops genl_ctrl_ops[] = { { .cmd = CTRL_CMD_GETFAMILY, - .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, + .validate = GENL_DONT_VALIDATE_STRICT, .policy = ctrl_policy_family, .maxattr = ARRAY_SIZE(ctrl_policy_family) - 1, .doit = ctrl_getfamily, + .flags = GENL_CMD_CAP_DO, + }, + { + .cmd = CTRL_CMD_GETFAMILY, + .validate = GENL_DONT_VALIDATE_DUMP, + .policy = ctrl_policy_family, + .maxattr = ARRAY_SIZE(ctrl_policy_family) - 1, .dumpit = ctrl_dumpfamily, + .flags = GENL_CMD_CAP_DUMP, }, { .cmd = CTRL_CMD_GETPOLICY, @@ -1625,6 +1633,7 @@ static const struct genl_ops genl_ctrl_ops[] = { .start = ctrl_dumppolicy_start, .dumpit = ctrl_dumppolicy, .done = ctrl_dumppolicy_done, + .flags = GENL_CMD_CAP_DUMP, }, }; @@ -1634,8 +1643,8 @@ static const struct genl_multicast_group genl_ctrl_groups[] = { static struct genl_family genl_ctrl __ro_after_init = { .module = THIS_MODULE, - .ops = genl_ctrl_ops, - .n_ops = ARRAY_SIZE(genl_ctrl_ops), + .split_ops = genl_ctrl_ops, + .n_split_ops = ARRAY_SIZE(genl_ctrl_ops), .resv_start_op = CTRL_CMD_GETPOLICY + 1, .mcgrps = genl_ctrl_groups, .n_mcgrps = ARRAY_SIZE(genl_ctrl_groups), From e8f50c4f0c14214b6430b8b3493654a286681a2b Mon Sep 17 00:00:00 2001 From: Kang Minchul Date: Sun, 6 Nov 2022 03:36:56 +0900 Subject: [PATCH 0690/2002] selftests/bpf: Fix u32 variable compared with less than zero MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Variable ret is compared with less than zero even though it was set as u32. So u32 to int conversion is needed. Signed-off-by: Kang Minchul Acked-by: Randy Dunlap Acked-by: Björn Töpel Link: https://lore.kernel.org/r/20221105183656.86077-1-tegongkang@gmail.com Signed-off-by: Martin KaFai Lau --- tools/testing/selftests/bpf/xskxceiver.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tools/testing/selftests/bpf/xskxceiver.c b/tools/testing/selftests/bpf/xskxceiver.c index 681a5db80dae..162d3a516f2c 100644 --- a/tools/testing/selftests/bpf/xskxceiver.c +++ b/tools/testing/selftests/bpf/xskxceiver.c @@ -1006,7 +1006,8 @@ static int __send_pkts(struct ifobject *ifobject, u32 *pkt_nb, struct pollfd *fd { struct xsk_socket_info *xsk = ifobject->xsk; bool use_poll = ifobject->use_poll; - u32 i, idx = 0, ret, valid_pkts = 0; + u32 i, idx = 0, valid_pkts = 0; + int ret; while (xsk_ring_prod__reserve(&xsk->tx, BATCH_SIZE, &idx) < BATCH_SIZE) { if (use_poll) { From e5659e4e19e49f1eac58bb07ce8bc2d78a89fe65 Mon Sep 17 00:00:00 2001 From: Rong Tao Date: Sat, 5 Nov 2022 14:48:00 +0800 Subject: [PATCH 0691/2002] samples/bpf: Fix sockex3 error: Missing BPF prog type since commit 450b167fb9be("libbpf: clean up SEC() handling"), sec_def_matches() does not recognize "socket/xxx" as "socket", therefore, the BPF program type is not recognized. Instead of sockex3_user.c parsing section names to get the BPF program fd. We use the program array map to assign a static index to each BPF program (get inspired by selftests/bpf progs/test_prog_array_init.c). Therefore, use SEC("socket") as section name instead of SEC("socket/xxx"), so that the BPF program is parsed to SOCKET_FILTER type. The "missing BPF prog type" problem is solved. How to reproduce this error: $ cd samples/bpf $ sudo ./sockex3 libbpf: prog 'bpf_func_PARSE_IP': missing BPF prog type, check ELF section name 'socket/3' libbpf: prog 'bpf_func_PARSE_IP': failed to load: -22 libbpf: failed to load object './sockex3_kern.o' ERROR: loading BPF object file failed Signed-off-by: Rong Tao Signed-off-by: Andrii Nakryiko Link: https://lore.kernel.org/bpf/tencent_EBA3C18864069E42175946973C2ACBAF5408@qq.com --- samples/bpf/sockex3_kern.c | 95 +++++++++++++++++++++----------------- samples/bpf/sockex3_user.c | 23 ++++----- 2 files changed, 63 insertions(+), 55 deletions(-) diff --git a/samples/bpf/sockex3_kern.c b/samples/bpf/sockex3_kern.c index b363503357e5..822c13242251 100644 --- a/samples/bpf/sockex3_kern.c +++ b/samples/bpf/sockex3_kern.c @@ -17,48 +17,11 @@ #define IP_MF 0x2000 #define IP_OFFSET 0x1FFF -#define PROG(F) SEC("socket/"__stringify(F)) int bpf_func_##F - -struct { - __uint(type, BPF_MAP_TYPE_PROG_ARRAY); - __uint(key_size, sizeof(u32)); - __uint(value_size, sizeof(u32)); - __uint(max_entries, 8); -} jmp_table SEC(".maps"); - #define PARSE_VLAN 1 #define PARSE_MPLS 2 #define PARSE_IP 3 #define PARSE_IPV6 4 -/* Protocol dispatch routine. It tail-calls next BPF program depending - * on eth proto. Note, we could have used ... - * - * bpf_tail_call(skb, &jmp_table, proto); - * - * ... but it would need large prog_array and cannot be optimised given - * the map key is not static. - */ -static inline void parse_eth_proto(struct __sk_buff *skb, u32 proto) -{ - switch (proto) { - case ETH_P_8021Q: - case ETH_P_8021AD: - bpf_tail_call(skb, &jmp_table, PARSE_VLAN); - break; - case ETH_P_MPLS_UC: - case ETH_P_MPLS_MC: - bpf_tail_call(skb, &jmp_table, PARSE_MPLS); - break; - case ETH_P_IP: - bpf_tail_call(skb, &jmp_table, PARSE_IP); - break; - case ETH_P_IPV6: - bpf_tail_call(skb, &jmp_table, PARSE_IPV6); - break; - } -} - struct vlan_hdr { __be16 h_vlan_TCI; __be16 h_vlan_encapsulated_proto; @@ -74,6 +37,8 @@ struct flow_key_record { __u32 ip_proto; }; +static inline void parse_eth_proto(struct __sk_buff *skb, u32 proto); + static inline int ip_is_fragment(struct __sk_buff *ctx, __u64 nhoff) { return load_half(ctx, nhoff + offsetof(struct iphdr, frag_off)) @@ -189,7 +154,8 @@ static __always_inline void parse_ip_proto(struct __sk_buff *skb, } } -PROG(PARSE_IP)(struct __sk_buff *skb) +SEC("socket") +int bpf_func_ip(struct __sk_buff *skb) { struct globals *g = this_cpu_globals(); __u32 nhoff, verlen, ip_proto; @@ -217,7 +183,8 @@ PROG(PARSE_IP)(struct __sk_buff *skb) return 0; } -PROG(PARSE_IPV6)(struct __sk_buff *skb) +SEC("socket") +int bpf_func_ipv6(struct __sk_buff *skb) { struct globals *g = this_cpu_globals(); __u32 nhoff, ip_proto; @@ -240,7 +207,8 @@ PROG(PARSE_IPV6)(struct __sk_buff *skb) return 0; } -PROG(PARSE_VLAN)(struct __sk_buff *skb) +SEC("socket") +int bpf_func_vlan(struct __sk_buff *skb) { __u32 nhoff, proto; @@ -256,7 +224,8 @@ PROG(PARSE_VLAN)(struct __sk_buff *skb) return 0; } -PROG(PARSE_MPLS)(struct __sk_buff *skb) +SEC("socket") +int bpf_func_mpls(struct __sk_buff *skb) { __u32 nhoff, label; @@ -279,7 +248,49 @@ PROG(PARSE_MPLS)(struct __sk_buff *skb) return 0; } -SEC("socket/0") +struct { + __uint(type, BPF_MAP_TYPE_PROG_ARRAY); + __uint(key_size, sizeof(u32)); + __uint(max_entries, 8); + __array(values, u32 (void *)); +} prog_array_init SEC(".maps") = { + .values = { + [PARSE_VLAN] = (void *)&bpf_func_vlan, + [PARSE_IP] = (void *)&bpf_func_ip, + [PARSE_IPV6] = (void *)&bpf_func_ipv6, + [PARSE_MPLS] = (void *)&bpf_func_mpls, + }, +}; + +/* Protocol dispatch routine. It tail-calls next BPF program depending + * on eth proto. Note, we could have used ... + * + * bpf_tail_call(skb, &prog_array_init, proto); + * + * ... but it would need large prog_array and cannot be optimised given + * the map key is not static. + */ +static inline void parse_eth_proto(struct __sk_buff *skb, u32 proto) +{ + switch (proto) { + case ETH_P_8021Q: + case ETH_P_8021AD: + bpf_tail_call(skb, &prog_array_init, PARSE_VLAN); + break; + case ETH_P_MPLS_UC: + case ETH_P_MPLS_MC: + bpf_tail_call(skb, &prog_array_init, PARSE_MPLS); + break; + case ETH_P_IP: + bpf_tail_call(skb, &prog_array_init, PARSE_IP); + break; + case ETH_P_IPV6: + bpf_tail_call(skb, &prog_array_init, PARSE_IPV6); + break; + } +} + +SEC("socket") int main_prog(struct __sk_buff *skb) { __u32 nhoff = ETH_HLEN; diff --git a/samples/bpf/sockex3_user.c b/samples/bpf/sockex3_user.c index cd6fa79df900..56044acbd25d 100644 --- a/samples/bpf/sockex3_user.c +++ b/samples/bpf/sockex3_user.c @@ -24,10 +24,9 @@ struct pair { int main(int argc, char **argv) { - int i, sock, key, fd, main_prog_fd, jmp_table_fd, hash_map_fd; + int i, sock, fd, main_prog_fd, hash_map_fd; struct bpf_program *prog; struct bpf_object *obj; - const char *section; char filename[256]; FILE *f; @@ -45,26 +44,24 @@ int main(int argc, char **argv) goto cleanup; } - jmp_table_fd = bpf_object__find_map_fd_by_name(obj, "jmp_table"); hash_map_fd = bpf_object__find_map_fd_by_name(obj, "hash_map"); - if (jmp_table_fd < 0 || hash_map_fd < 0) { + if (hash_map_fd < 0) { fprintf(stderr, "ERROR: finding a map in obj file failed\n"); goto cleanup; } + /* find BPF main program */ + main_prog_fd = 0; bpf_object__for_each_program(prog, obj) { fd = bpf_program__fd(prog); - section = bpf_program__section_name(prog); - if (sscanf(section, "socket/%d", &key) != 1) { - fprintf(stderr, "ERROR: finding prog failed\n"); - goto cleanup; - } - - if (key == 0) + if (!strcmp(bpf_program__name(prog), "main_prog")) main_prog_fd = fd; - else - bpf_map_update_elem(jmp_table_fd, &key, &fd, BPF_ANY); + } + + if (main_prog_fd == 0) { + fprintf(stderr, "ERROR: can't find main_prog\n"); + goto cleanup; } sock = open_raw_sock("lo"); From 7ea8104d9e3d5984ce9dba08fde0b849b88007ee Mon Sep 17 00:00:00 2001 From: "Russell King (Oracle)" Date: Fri, 4 Nov 2022 16:53:59 +0000 Subject: [PATCH 0692/2002] net: lan966x: move unnecessary linux/sfp.h include lan966x_phylink.c doesn't make use of anything from linux/sfp.h, so remove this unnecessary include. Signed-off-by: Russell King (Oracle) Link: https://lore.kernel.org/r/E1oqzx9-001r9g-HV@rmk-PC.armlinux.org.uk Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/microchip/lan966x/lan966x_phylink.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_phylink.c b/drivers/net/ethernet/microchip/lan966x/lan966x_phylink.c index e4ac59480514..f9579a2ae676 100644 --- a/drivers/net/ethernet/microchip/lan966x/lan966x_phylink.c +++ b/drivers/net/ethernet/microchip/lan966x/lan966x_phylink.c @@ -5,7 +5,6 @@ #include #include #include -#include #include "lan966x_main.h" From e1f4ecab19338ec7079830e8700e4869f991fd45 Mon Sep 17 00:00:00 2001 From: "Russell King (Oracle)" Date: Fri, 4 Nov 2022 17:13:01 +0000 Subject: [PATCH 0693/2002] net: remove explicit phylink_generic_validate() references Virtually all conventional network drivers are now converted to use phylink_generic_validate() - only DSA drivers and fman_memac remain, so lets remove the necessity for network drivers to explicitly set this member, and default to phylink_generic_validate() when unset. This is possible as .validate must currently be set. Any remaining instances that have not been addressed by this patch can be fixed up later. Signed-off-by: Russell King (Oracle) Reviewed-by: Vladimir Oltean Link: https://lore.kernel.org/r/E1or0FZ-001tRa-DI@rmk-PC.armlinux.org.uk Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/altera/altera_tse_main.c | 1 - drivers/net/ethernet/atheros/ag71xx.c | 1 - drivers/net/ethernet/cadence/macb_main.c | 1 - drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c | 1 - drivers/net/ethernet/freescale/enetc/enetc_pf.c | 1 - drivers/net/ethernet/freescale/fman/fman_dtsec.c | 1 - drivers/net/ethernet/freescale/fman/fman_tgec.c | 1 - drivers/net/ethernet/marvell/mvneta.c | 1 - drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c | 1 - drivers/net/ethernet/marvell/prestera/prestera_main.c | 1 - drivers/net/ethernet/mediatek/mtk_eth_soc.c | 1 - drivers/net/ethernet/microchip/lan966x/lan966x_phylink.c | 1 - drivers/net/ethernet/microchip/sparx5/sparx5_phylink.c | 1 - drivers/net/ethernet/mscc/ocelot_net.c | 1 - drivers/net/ethernet/renesas/rswitch.c | 1 - drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 1 - drivers/net/ethernet/ti/am65-cpsw-nuss.c | 1 - drivers/net/ethernet/xilinx/xilinx_axienet_main.c | 1 - drivers/net/phy/phylink.c | 5 ++++- drivers/net/usb/asix_devices.c | 1 - include/linux/phylink.h | 5 +++++ 21 files changed, 9 insertions(+), 20 deletions(-) diff --git a/drivers/net/ethernet/altera/altera_tse_main.c b/drivers/net/ethernet/altera/altera_tse_main.c index 7633b227b2ca..28b5cae60eb5 100644 --- a/drivers/net/ethernet/altera/altera_tse_main.c +++ b/drivers/net/ethernet/altera/altera_tse_main.c @@ -1095,7 +1095,6 @@ static struct phylink_pcs *alt_tse_select_pcs(struct phylink_config *config, } static const struct phylink_mac_ops alt_tse_phylink_ops = { - .validate = phylink_generic_validate, .mac_an_restart = alt_tse_mac_an_restart, .mac_config = alt_tse_mac_config, .mac_link_down = alt_tse_mac_link_down, diff --git a/drivers/net/ethernet/atheros/ag71xx.c b/drivers/net/ethernet/atheros/ag71xx.c index cc932b3cf873..a5de1bd8538c 100644 --- a/drivers/net/ethernet/atheros/ag71xx.c +++ b/drivers/net/ethernet/atheros/ag71xx.c @@ -1086,7 +1086,6 @@ static void ag71xx_mac_link_up(struct phylink_config *config, } static const struct phylink_mac_ops ag71xx_phylink_mac_ops = { - .validate = phylink_generic_validate, .mac_config = ag71xx_mac_config, .mac_link_down = ag71xx_mac_link_down, .mac_link_up = ag71xx_mac_link_up, diff --git a/drivers/net/ethernet/cadence/macb_main.c b/drivers/net/ethernet/cadence/macb_main.c index 4f63f1ba3161..5d618814bb12 100644 --- a/drivers/net/ethernet/cadence/macb_main.c +++ b/drivers/net/ethernet/cadence/macb_main.c @@ -742,7 +742,6 @@ static struct phylink_pcs *macb_mac_select_pcs(struct phylink_config *config, } static const struct phylink_mac_ops macb_phylink_ops = { - .validate = phylink_generic_validate, .mac_select_pcs = macb_mac_select_pcs, .mac_config = macb_mac_config, .mac_link_down = macb_mac_link_down, diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c b/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c index 2bbab28f763d..51c9da8e1be2 100644 --- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c +++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c @@ -236,7 +236,6 @@ static void dpaa2_mac_link_down(struct phylink_config *config, } static const struct phylink_mac_ops dpaa2_mac_phylink_ops = { - .validate = phylink_generic_validate, .mac_select_pcs = dpaa2_mac_select_pcs, .mac_config = dpaa2_mac_config, .mac_link_up = dpaa2_mac_link_up, diff --git a/drivers/net/ethernet/freescale/enetc/enetc_pf.c b/drivers/net/ethernet/freescale/enetc/enetc_pf.c index bdf94335ee99..9f6c4f5c0a6c 100644 --- a/drivers/net/ethernet/freescale/enetc/enetc_pf.c +++ b/drivers/net/ethernet/freescale/enetc/enetc_pf.c @@ -1111,7 +1111,6 @@ static void enetc_pl_mac_link_down(struct phylink_config *config, } static const struct phylink_mac_ops enetc_mac_phylink_ops = { - .validate = phylink_generic_validate, .mac_select_pcs = enetc_pl_mac_select_pcs, .mac_config = enetc_pl_mac_config, .mac_link_up = enetc_pl_mac_link_up, diff --git a/drivers/net/ethernet/freescale/fman/fman_dtsec.c b/drivers/net/ethernet/freescale/fman/fman_dtsec.c index 3c87820ca202..d00bae15a901 100644 --- a/drivers/net/ethernet/freescale/fman/fman_dtsec.c +++ b/drivers/net/ethernet/freescale/fman/fman_dtsec.c @@ -986,7 +986,6 @@ static void dtsec_link_down(struct phylink_config *config, unsigned int mode, } static const struct phylink_mac_ops dtsec_mac_ops = { - .validate = phylink_generic_validate, .mac_select_pcs = dtsec_select_pcs, .mac_config = dtsec_mac_config, .mac_link_up = dtsec_link_up, diff --git a/drivers/net/ethernet/freescale/fman/fman_tgec.c b/drivers/net/ethernet/freescale/fman/fman_tgec.c index c265b7f19a4d..c2261d26db5b 100644 --- a/drivers/net/ethernet/freescale/fman/fman_tgec.c +++ b/drivers/net/ethernet/freescale/fman/fman_tgec.c @@ -469,7 +469,6 @@ static void tgec_link_down(struct phylink_config *config, unsigned int mode, } static const struct phylink_mac_ops tgec_mac_ops = { - .validate = phylink_generic_validate, .mac_config = tgec_mac_config, .mac_link_up = tgec_link_up, .mac_link_down = tgec_link_down, diff --git a/drivers/net/ethernet/marvell/mvneta.c b/drivers/net/ethernet/marvell/mvneta.c index 05ff3c58087e..c2cb98d24f5c 100644 --- a/drivers/net/ethernet/marvell/mvneta.c +++ b/drivers/net/ethernet/marvell/mvneta.c @@ -4228,7 +4228,6 @@ static void mvneta_mac_link_up(struct phylink_config *config, } static const struct phylink_mac_ops mvneta_phylink_ops = { - .validate = phylink_generic_validate, .mac_select_pcs = mvneta_mac_select_pcs, .mac_prepare = mvneta_mac_prepare, .mac_config = mvneta_mac_config, diff --git a/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c b/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c index 116e53172072..d98f7e9a480e 100644 --- a/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c +++ b/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c @@ -6603,7 +6603,6 @@ static void mvpp2_mac_link_down(struct phylink_config *config, } static const struct phylink_mac_ops mvpp2_phylink_ops = { - .validate = phylink_generic_validate, .mac_select_pcs = mvpp2_select_pcs, .mac_prepare = mvpp2_mac_prepare, .mac_config = mvpp2_mac_config, diff --git a/drivers/net/ethernet/marvell/prestera/prestera_main.c b/drivers/net/ethernet/marvell/prestera/prestera_main.c index edbdda8f958d..f8deaee84398 100644 --- a/drivers/net/ethernet/marvell/prestera/prestera_main.c +++ b/drivers/net/ethernet/marvell/prestera/prestera_main.c @@ -360,7 +360,6 @@ static void prestera_pcs_an_restart(struct phylink_pcs *pcs) } static const struct phylink_mac_ops prestera_mac_ops = { - .validate = phylink_generic_validate, .mac_select_pcs = prestera_mac_select_pcs, .mac_config = prestera_mac_config, .mac_link_down = prestera_mac_link_down, diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c index 789268b15106..1cf76fd7afbc 100644 --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c @@ -653,7 +653,6 @@ static void mtk_mac_link_up(struct phylink_config *config, } static const struct phylink_mac_ops mtk_phylink_ops = { - .validate = phylink_generic_validate, .mac_select_pcs = mtk_mac_select_pcs, .mac_pcs_get_state = mtk_mac_pcs_get_state, .mac_config = mtk_mac_config, diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_phylink.c b/drivers/net/ethernet/microchip/lan966x/lan966x_phylink.c index f9579a2ae676..c5f9803e6e63 100644 --- a/drivers/net/ethernet/microchip/lan966x/lan966x_phylink.c +++ b/drivers/net/ethernet/microchip/lan966x/lan966x_phylink.c @@ -124,7 +124,6 @@ static void lan966x_pcs_aneg_restart(struct phylink_pcs *pcs) } const struct phylink_mac_ops lan966x_phylink_mac_ops = { - .validate = phylink_generic_validate, .mac_select_pcs = lan966x_phylink_mac_select, .mac_config = lan966x_phylink_mac_config, .mac_prepare = lan966x_phylink_mac_prepare, diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_phylink.c b/drivers/net/ethernet/microchip/sparx5/sparx5_phylink.c index 830da0e5ff27..bb97d27a1da4 100644 --- a/drivers/net/ethernet/microchip/sparx5/sparx5_phylink.c +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_phylink.c @@ -138,7 +138,6 @@ const struct phylink_pcs_ops sparx5_phylink_pcs_ops = { }; const struct phylink_mac_ops sparx5_phylink_mac_ops = { - .validate = phylink_generic_validate, .mac_select_pcs = sparx5_phylink_mac_select_pcs, .mac_config = sparx5_phylink_mac_config, .mac_link_down = sparx5_phylink_mac_link_down, diff --git a/drivers/net/ethernet/mscc/ocelot_net.c b/drivers/net/ethernet/mscc/ocelot_net.c index f50dada2bb8e..ca4bde861397 100644 --- a/drivers/net/ethernet/mscc/ocelot_net.c +++ b/drivers/net/ethernet/mscc/ocelot_net.c @@ -1727,7 +1727,6 @@ static void vsc7514_phylink_mac_link_up(struct phylink_config *config, } static const struct phylink_mac_ops ocelot_phylink_ops = { - .validate = phylink_generic_validate, .mac_config = vsc7514_phylink_mac_config, .mac_link_down = vsc7514_phylink_mac_link_down, .mac_link_up = vsc7514_phylink_mac_link_up, diff --git a/drivers/net/ethernet/renesas/rswitch.c b/drivers/net/ethernet/renesas/rswitch.c index f0168fedfef9..4f7af6393c9e 100644 --- a/drivers/net/ethernet/renesas/rswitch.c +++ b/drivers/net/ethernet/renesas/rswitch.c @@ -1190,7 +1190,6 @@ static void rswitch_mac_link_up(struct phylink_config *config, } static const struct phylink_mac_ops rswitch_phylink_ops = { - .validate = phylink_generic_validate, .mac_config = rswitch_mac_config, .mac_link_down = rswitch_mac_link_down, .mac_link_up = rswitch_mac_link_up, diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index 8273e6a175c8..2fea8785aaf4 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -1080,7 +1080,6 @@ static void stmmac_mac_link_up(struct phylink_config *config, } static const struct phylink_mac_ops stmmac_phylink_mac_ops = { - .validate = phylink_generic_validate, .mac_select_pcs = stmmac_mac_select_pcs, .mac_config = stmmac_mac_config, .mac_link_down = stmmac_mac_link_down, diff --git a/drivers/net/ethernet/ti/am65-cpsw-nuss.c b/drivers/net/ethernet/ti/am65-cpsw-nuss.c index c785e01d025f..75429ff0c200 100644 --- a/drivers/net/ethernet/ti/am65-cpsw-nuss.c +++ b/drivers/net/ethernet/ti/am65-cpsw-nuss.c @@ -1520,7 +1520,6 @@ static void am65_cpsw_nuss_mac_link_up(struct phylink_config *config, struct phy } static const struct phylink_mac_ops am65_cpsw_phylink_mac_ops = { - .validate = phylink_generic_validate, .mac_config = am65_cpsw_nuss_mac_config, .mac_link_down = am65_cpsw_nuss_mac_link_down, .mac_link_up = am65_cpsw_nuss_mac_link_up, diff --git a/drivers/net/ethernet/xilinx/xilinx_axienet_main.c b/drivers/net/ethernet/xilinx/xilinx_axienet_main.c index d082f2b10f4d..3e310b55bce2 100644 --- a/drivers/net/ethernet/xilinx/xilinx_axienet_main.c +++ b/drivers/net/ethernet/xilinx/xilinx_axienet_main.c @@ -1736,7 +1736,6 @@ static void axienet_mac_link_up(struct phylink_config *config, } static const struct phylink_mac_ops axienet_phylink_ops = { - .validate = phylink_generic_validate, .mac_select_pcs = axienet_mac_select_pcs, .mac_config = axienet_mac_config, .mac_link_down = axienet_mac_link_down, diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c index 88f60e98b760..25feab1802ee 100644 --- a/drivers/net/phy/phylink.c +++ b/drivers/net/phy/phylink.c @@ -649,7 +649,10 @@ static int phylink_validate_mac_and_pcs(struct phylink *pl, } /* Then validate the link parameters with the MAC */ - pl->mac_ops->validate(pl->config, supported, state); + if (pl->mac_ops->validate) + pl->mac_ops->validate(pl->config, supported, state); + else + phylink_generic_validate(pl->config, supported, state); return phylink_is_empty_linkmode(supported) ? -EINVAL : 0; } diff --git a/drivers/net/usb/asix_devices.c b/drivers/net/usb/asix_devices.c index 02941d97d034..0fe3773c5bca 100644 --- a/drivers/net/usb/asix_devices.c +++ b/drivers/net/usb/asix_devices.c @@ -773,7 +773,6 @@ static void ax88772_mac_link_up(struct phylink_config *config, } static const struct phylink_mac_ops ax88772_phylink_mac_ops = { - .validate = phylink_generic_validate, .mac_config = ax88772_mac_config, .mac_link_down = ax88772_mac_link_down, .mac_link_up = ax88772_mac_link_up, diff --git a/include/linux/phylink.h b/include/linux/phylink.h index 1df3e5e316e8..c492c26202b5 100644 --- a/include/linux/phylink.h +++ b/include/linux/phylink.h @@ -207,6 +207,11 @@ struct phylink_mac_ops { * * If the @state->interface mode is not supported, then the @supported * mask must be cleared. + * + * This member is optional; if not set, the generic validator will be + * used making use of @config->mac_capabilities and + * @config->supported_interfaces to determine which link modes are + * supported. */ void validate(struct phylink_config *config, unsigned long *supported, struct phylink_link_state *state); From 14ef5c39891ede40e40797f7e9da845896c86670 Mon Sep 17 00:00:00 2001 From: Roman Gushchin Date: Fri, 4 Nov 2022 13:48:37 -0700 Subject: [PATCH 0694/2002] net: macb: implement live mac addr change Implement live mac addr change for the macb ethernet driver. Signed-off-by: Roman Gushchin Reviewed-by: Leon Romanovsky Link: https://lore.kernel.org/r/20221104204837.614459-1-roman.gushchin@linux.dev Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/cadence/macb_main.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/cadence/macb_main.c b/drivers/net/ethernet/cadence/macb_main.c index 5d618814bb12..95667b979fab 100644 --- a/drivers/net/ethernet/cadence/macb_main.c +++ b/drivers/net/ethernet/cadence/macb_main.c @@ -2946,6 +2946,18 @@ static int macb_change_mtu(struct net_device *dev, int new_mtu) return 0; } +static int macb_set_mac_addr(struct net_device *dev, void *addr) +{ + int err; + + err = eth_mac_addr(dev, addr); + if (err < 0) + return err; + + macb_set_hwaddr(netdev_priv(dev)); + return 0; +} + static void gem_update_stats(struct macb *bp) { struct macb_queue *queue; @@ -3785,7 +3797,7 @@ static const struct net_device_ops macb_netdev_ops = { .ndo_eth_ioctl = macb_ioctl, .ndo_validate_addr = eth_validate_addr, .ndo_change_mtu = macb_change_mtu, - .ndo_set_mac_address = eth_mac_addr, + .ndo_set_mac_address = macb_set_mac_addr, #ifdef CONFIG_NET_POLL_CONTROLLER .ndo_poll_controller = macb_poll_controller, #endif @@ -4048,6 +4060,8 @@ static int macb_init(struct platform_device *pdev) dev->ethtool_ops = &macb_ethtool_ops; } + dev->priv_flags |= IFF_LIVE_ADDR_CHANGE; + /* Set features */ dev->hw_features = NETIF_F_SG; From e69761483361f3df455bc493c99af0ef1744a14f Mon Sep 17 00:00:00 2001 From: Tao Chen Date: Sat, 5 Nov 2022 17:05:04 +0800 Subject: [PATCH 0695/2002] netlink: Fix potential skb memleak in netlink_ack Fix coverity issue 'Resource leak'. We should clean the skb resource if nlmsg_put/append failed. Fixes: 738136a0e375 ("netlink: split up copies in the ack construction") Signed-off-by: Tao Chen Link: https://lore.kernel.org/r/bff442d62c87de6299817fe1897cc5a5694ba9cc.1667638204.git.chentao.kernel@linux.alibaba.com Signed-off-by: Jakub Kicinski --- net/netlink/af_netlink.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c index b10d5e50b99d..9ebdf3262015 100644 --- a/net/netlink/af_netlink.c +++ b/net/netlink/af_netlink.c @@ -2500,7 +2500,7 @@ void netlink_ack(struct sk_buff *in_skb, struct nlmsghdr *nlh, int err, skb = nlmsg_new(payload + tlvlen, GFP_KERNEL); if (!skb) - goto err_bad_put; + goto err_skb; rep = nlmsg_put(skb, NETLINK_CB(in_skb).portid, nlh->nlmsg_seq, NLMSG_ERROR, sizeof(*errmsg), flags); @@ -2528,6 +2528,8 @@ void netlink_ack(struct sk_buff *in_skb, struct nlmsghdr *nlh, int err, return; err_bad_put: + nlmsg_free(skb); +err_skb: NETLINK_CB(in_skb).sk->sk_err = ENOBUFS; sk_error_report(NETLINK_CB(in_skb).sk); } From a068d33e542f5d5be2e5de3cf1122c4347d3e2af Mon Sep 17 00:00:00 2001 From: YueHaibing Date: Sat, 5 Nov 2022 16:07:21 +0800 Subject: [PATCH 0696/2002] net: txgbe: Fix memleak in txgbe_calc_eeprom_checksum() eeprom_ptrs should be freed before returned. Fixes: 049fe5365324 ("net: txgbe: Add operations to interact with firmware") Signed-off-by: YueHaibing Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/wangxun/txgbe/txgbe_hw.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_hw.c b/drivers/net/ethernet/wangxun/txgbe/txgbe_hw.c index 0b1032195859..9cf5fe33118e 100644 --- a/drivers/net/ethernet/wangxun/txgbe/txgbe_hw.c +++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_hw.c @@ -183,6 +183,7 @@ static int txgbe_calc_eeprom_checksum(struct txgbe_hw *hw, u16 *checksum) eeprom_ptrs); if (status != 0) { wx_err(wxhw, "Failed to read EEPROM image\n"); + kvfree(eeprom_ptrs); return status; } local_buffer = eeprom_ptrs; @@ -196,13 +197,13 @@ static int txgbe_calc_eeprom_checksum(struct txgbe_hw *hw, u16 *checksum) if (i != wxhw->eeprom.sw_region_offset + TXGBE_EEPROM_CHECKSUM) *checksum += local_buffer[i]; + if (eeprom_ptrs) + kvfree(eeprom_ptrs); + *checksum = TXGBE_EEPROM_SUM - *checksum; if (*checksum < 0) return -EINVAL; - if (eeprom_ptrs) - kvfree(eeprom_ptrs); - return 0; } From 5e2ea7801faccc011359db5ad6af5987b513f314 Mon Sep 17 00:00:00 2001 From: YueHaibing Date: Sat, 5 Nov 2022 16:07:22 +0800 Subject: [PATCH 0697/2002] net: txgbe: Fix unsigned comparison to zero in txgbe_calc_eeprom_checksum() The error checks on checksum for a negative error return always fails because it is unsigned and can never be negative. Fixes: 049fe5365324 ("net: txgbe: Add operations to interact with firmware") Signed-off-by: YueHaibing Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/wangxun/txgbe/txgbe_hw.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_hw.c b/drivers/net/ethernet/wangxun/txgbe/txgbe_hw.c index 9cf5fe33118e..167f7ff73192 100644 --- a/drivers/net/ethernet/wangxun/txgbe/txgbe_hw.c +++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_hw.c @@ -200,10 +200,11 @@ static int txgbe_calc_eeprom_checksum(struct txgbe_hw *hw, u16 *checksum) if (eeprom_ptrs) kvfree(eeprom_ptrs); - *checksum = TXGBE_EEPROM_SUM - *checksum; - if (*checksum < 0) + if (*checksum > TXGBE_EEPROM_SUM) return -EINVAL; + *checksum = TXGBE_EEPROM_SUM - *checksum; + return 0; } From c7caaa6f727871ff00b49c3bb7260137a05491bf Mon Sep 17 00:00:00 2001 From: Brian Henriquez Date: Mon, 24 Oct 2022 03:52:13 -0500 Subject: [PATCH 0698/2002] wifi: brcmfmac: correctly remove all p2p vif When deleting a P2P AGO interface we should make sure that relevant entry in bss_idx[] array is removed. We were always removing only 'vif' at P2PAPI_BSSCFG_CONNECTION before, regardless of the number of created P2P AGO interfaces. brcmfmac: correctly remove all p2p vif Signed-off-by: Brian Henriquez Signed-off-by: Chi-hsien Lin Signed-off-by: Ian Lin Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20221024085215.27616-2-ian.lin@infineon.com --- drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.c index 10d9d9c63b28..af8843507f3d 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.c @@ -2424,8 +2424,12 @@ int brcmf_p2p_del_vif(struct wiphy *wiphy, struct wireless_dev *wdev) brcmf_remove_interface(vif->ifp, true); brcmf_cfg80211_arm_vif_event(cfg, NULL); - if (iftype != NL80211_IFTYPE_P2P_DEVICE) - p2p->bss_idx[P2PAPI_BSSCFG_CONNECTION].vif = NULL; + if (iftype != NL80211_IFTYPE_P2P_DEVICE) { + if (vif == p2p->bss_idx[P2PAPI_BSSCFG_CONNECTION].vif) + p2p->bss_idx[P2PAPI_BSSCFG_CONNECTION].vif = NULL; + if (vif == p2p->bss_idx[P2PAPI_BSSCFG_CONNECTION2].vif) + p2p->bss_idx[P2PAPI_BSSCFG_CONNECTION2].vif = NULL; + } return err; } From 52617bee3718d1315159569973d35107ea6b9a34 Mon Sep 17 00:00:00 2001 From: Wataru Gohda Date: Mon, 24 Oct 2022 03:52:14 -0500 Subject: [PATCH 0699/2002] wifi: brcmfmac: Fix for when connect request is not success Currently brcmfmac is expecting to be set for both BRCMF_VIF_STATUS_EAP_SUCCESS and BRCMF_VIF_STATUS_EAP status bit based on dongle event and those bits are cleared to complete connect request successfully. But when connect request is finished unsuccessfully, either BRCMF_VIF_STATUS_EAP_SUCCESS / BRCMF_VIF_STATUS_EAP bits are not cleared depending on how the connect fail event happens. These status bits are carried over to following new connect request and this will lead to generate below kernel warning for some case. Worst case status mismatch happens between dongle and wpa_supplicant. WARNING: ../net/wireless/sme.c:756 __cfg80211_connect_result+0x42c/0x4a0 [cfg80211] The fix is to clear the BRCMF_VIF_STATUS_EAP_SUCCESS / BRCMF_VIF_STATUS_EAP bits during the link down process and add to call link down process when link down event received during BRCMF_VIF_STATUS_CONNECTING as well as BRCMF_VIF_STATUS_CONNECTED state. Signed-off-by: Wataru Gohda Signed-off-by: Chi-hsien Lin Signed-off-by: Ian Lin Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20221024085215.27616-3-ian.lin@infineon.com --- .../broadcom/brcm80211/brcmfmac/cfg80211.c | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c index 3f2336062217..14cbc30a3229 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c @@ -1683,6 +1683,8 @@ static void brcmf_link_down(struct brcmf_cfg80211_vif *vif, u16 reason, locally_generated, GFP_KERNEL); } clear_bit(BRCMF_VIF_STATUS_CONNECTING, &vif->sme_state); + clear_bit(BRCMF_VIF_STATUS_EAP_SUCCESS, &vif->sme_state); + clear_bit(BRCMF_VIF_STATUS_ASSOC_SUCCESS, &vif->sme_state); clear_bit(BRCMF_SCAN_STATUS_SUPPRESS, &cfg->scan_status); brcmf_btcoex_set_mode(vif, BRCMF_BTCOEX_ENABLED, 0); if (vif->profile.use_fwsup != BRCMF_PROFILE_FWSUP_NONE) { @@ -2535,6 +2537,8 @@ brcmf_cfg80211_disconnect(struct wiphy *wiphy, struct net_device *ndev, clear_bit(BRCMF_VIF_STATUS_CONNECTED, &ifp->vif->sme_state); clear_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state); + clear_bit(BRCMF_VIF_STATUS_EAP_SUCCESS, &ifp->vif->sme_state); + clear_bit(BRCMF_VIF_STATUS_ASSOC_SUCCESS, &ifp->vif->sme_state); cfg80211_disconnected(ndev, reason_code, NULL, 0, true, GFP_KERNEL); memcpy(&scbval.ea, &profile->bssid, ETH_ALEN); @@ -6299,6 +6303,10 @@ brcmf_bss_connect_done(struct brcmf_cfg80211_info *cfg, &ifp->vif->sme_state); conn_params.status = WLAN_STATUS_SUCCESS; } else { + clear_bit(BRCMF_VIF_STATUS_EAP_SUCCESS, + &ifp->vif->sme_state); + clear_bit(BRCMF_VIF_STATUS_ASSOC_SUCCESS, + &ifp->vif->sme_state); conn_params.status = WLAN_STATUS_AUTH_TIMEOUT; } conn_params.links[0].bssid = profile->bssid; @@ -6396,9 +6404,13 @@ brcmf_notify_connect_status(struct brcmf_if *ifp, } else if (brcmf_is_linkdown(ifp->vif, e)) { brcmf_dbg(CONN, "Linkdown\n"); if (!brcmf_is_ibssmode(ifp->vif) && - test_bit(BRCMF_VIF_STATUS_CONNECTED, - &ifp->vif->sme_state)) { - if (memcmp(profile->bssid, e->addr, ETH_ALEN)) + (test_bit(BRCMF_VIF_STATUS_CONNECTED, + &ifp->vif->sme_state) || + test_bit(BRCMF_VIF_STATUS_CONNECTING, + &ifp->vif->sme_state))) { + if (test_bit(BRCMF_VIF_STATUS_CONNECTED, + &ifp->vif->sme_state) && + memcmp(profile->bssid, e->addr, ETH_ALEN)) return err; brcmf_bss_connect_done(cfg, ndev, e, false); From c81c1fd4e907be6ac1da35ab062c8fa6dde000a3 Mon Sep 17 00:00:00 2001 From: Prasanna Kerekoppa Date: Mon, 24 Oct 2022 03:52:15 -0500 Subject: [PATCH 0700/2002] wifi: brcmfmac: Avoiding Connection delay Channel info passed by supplicant is not given to firmware. This causes delay (about 3seconds) due to full scan. Supplicant already provides the channel info for the specific SSID. channel_hint carries this channel info for the connect call back. Patch has been verified on 43012 and 43455. Signed-off-by: Prasanna Kerekoppa Signed-off-by: Chung-Hsien Hsu Signed-off-by: Chi-hsien Lin Signed-off-by: Ian Lin Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20221024085215.27616-4-ian.lin@infineon.com --- drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c index 14cbc30a3229..ae9507dec74a 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c @@ -2315,6 +2315,12 @@ brcmf_cfg80211_connect(struct wiphy *wiphy, struct net_device *ndev, return -EOPNOTSUPP; } + if (sme->channel_hint) + chan = sme->channel_hint; + + if (sme->bssid_hint) + sme->bssid = sme->bssid_hint; + if (ifp->vif == cfg->p2p.bss_idx[P2PAPI_BSSCFG_PRIMARY].vif) { /* A normal (non P2P) connection request setup. */ ie = NULL; From 417f173532cc1398e17a22bff50f93be40a8a7a8 Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Tue, 1 Nov 2022 22:13:50 +0100 Subject: [PATCH 0701/2002] wifi: Use kstrtobool() instead of strtobool() strtobool() is the same as kstrtobool(). However, the latter is more used within the kernel. In order to remove strtobool() and slightly simplify kstrtox.h, switch to the other function name. While at it, include the corresponding header file () Signed-off-by: Christophe JAILLET Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/1ff34549af5ad6f7c80d5b9e11872b5499065fc1.1667336095.git.christophe.jaillet@wanadoo.fr --- drivers/net/wireless/ath/ath10k/debug.c | 5 +++-- drivers/net/wireless/ath/ath9k/ath9k.h | 1 + drivers/net/wireless/ath/ath9k/tx99.c | 2 +- drivers/net/wireless/marvell/mwifiex/debugfs.c | 2 +- drivers/net/wireless/marvell/mwifiex/main.h | 1 + 5 files changed, 7 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/ath/ath10k/debug.c b/drivers/net/wireless/ath/ath10k/debug.c index c861e66ef6bc..b9aea1510f7b 100644 --- a/drivers/net/wireless/ath/ath10k/debug.c +++ b/drivers/net/wireless/ath/ath10k/debug.c @@ -10,6 +10,7 @@ #include #include #include +#include #include "core.h" #include "debug.h" @@ -1975,7 +1976,7 @@ static ssize_t ath10k_write_btcoex(struct file *file, buf[buf_size] = '\0'; - if (strtobool(buf, &val) != 0) + if (kstrtobool(buf, &val) != 0) return -EINVAL; if (!ar->coex_support) @@ -2113,7 +2114,7 @@ static ssize_t ath10k_write_peer_stats(struct file *file, buf[buf_size] = '\0'; - if (strtobool(buf, &val) != 0) + if (kstrtobool(buf, &val) != 0) return -EINVAL; mutex_lock(&ar->conf_mutex); diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h index 3ccf8cfc6b63..2cc23605c9fc 100644 --- a/drivers/net/wireless/ath/ath9k/ath9k.h +++ b/drivers/net/wireless/ath/ath9k/ath9k.h @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include diff --git a/drivers/net/wireless/ath/ath9k/tx99.c b/drivers/net/wireless/ath/ath9k/tx99.c index 95544ce05acf..8a996ed9a3be 100644 --- a/drivers/net/wireless/ath/ath9k/tx99.c +++ b/drivers/net/wireless/ath/ath9k/tx99.c @@ -189,7 +189,7 @@ static ssize_t write_file_tx99(struct file *file, const char __user *user_buf, buf[len] = '\0'; - if (strtobool(buf, &start)) + if (kstrtobool(buf, &start)) return -EINVAL; mutex_lock(&sc->mutex); diff --git a/drivers/net/wireless/marvell/mwifiex/debugfs.c b/drivers/net/wireless/marvell/mwifiex/debugfs.c index bda53cb91f37..52b18f4a774b 100644 --- a/drivers/net/wireless/marvell/mwifiex/debugfs.c +++ b/drivers/net/wireless/marvell/mwifiex/debugfs.c @@ -874,7 +874,7 @@ mwifiex_timeshare_coex_write(struct file *file, const char __user *ubuf, if (copy_from_user(&kbuf, ubuf, min_t(size_t, sizeof(kbuf) - 1, count))) return -EFAULT; - if (strtobool(kbuf, ×hare_coex)) + if (kstrtobool(kbuf, ×hare_coex)) return -EINVAL; ret = mwifiex_send_cmd(priv, HostCmd_CMD_ROBUST_COEX, diff --git a/drivers/net/wireless/marvell/mwifiex/main.h b/drivers/net/wireless/marvell/mwifiex/main.h index 63f861e6b28a..b95886e1413e 100644 --- a/drivers/net/wireless/marvell/mwifiex/main.h +++ b/drivers/net/wireless/marvell/mwifiex/main.h @@ -10,6 +10,7 @@ #include #include +#include #include #include #include From d6197c9121dd72f35e5702aa55ee5c1583e0bfdb Mon Sep 17 00:00:00 2001 From: Chia-Yuan Li Date: Wed, 2 Nov 2022 09:42:59 +0800 Subject: [PATCH 0702/2002] wifi: rtw89: dump dispatch status via debug port Dispatch is a component to decide packets forward to host, DMAC or HAXIDMA. It contains CDT standing for CPU dispatcher, HDT standing for host dispatcher, WDE standing for descriptor engine and PLE standing for payload engine. STF is one kind of modes, it can be used if packet send to hardware and doesn't need release report. These debug port information can help to clarify the reason if packets stuck in dispatch. Signed-off-by: Chia-Yuan Li Signed-off-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20221102014300.14091-2-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/debug.c | 575 +++++++++++++++++++++ drivers/net/wireless/realtek/rtw89/mac.h | 45 ++ drivers/net/wireless/realtek/rtw89/reg.h | 5 + 3 files changed, 625 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/debug.c b/drivers/net/wireless/realtek/rtw89/debug.c index cd44d9aa37ca..afd10e91d3ea 100644 --- a/drivers/net/wireless/realtek/rtw89/debug.c +++ b/drivers/net/wireless/realtek/rtw89/debug.c @@ -1114,6 +1114,303 @@ static const struct rtw89_mac_dbg_port_info dbg_port_ptcl_c1 = { .rd_msk = B_AX_PTCL_DBG_INFO_MASK }; +static const struct rtw89_mac_dbg_port_info dbg_port_dspt_hdt_tx0_5 = { + .sel_addr = R_AX_DISPATCHER_DBG_PORT, + .sel_byte = 2, + .sel_msk = B_AX_DISPATCHER_DBG_SEL_MASK, + .srt = 0x0, + .end = 0xD, + .rd_addr = R_AX_DBG_PORT_SEL, + .rd_byte = 4, + .rd_msk = B_AX_DEBUG_ST_MASK +}; + +static const struct rtw89_mac_dbg_port_info dbg_port_dspt_hdt_tx6 = { + .sel_addr = R_AX_DISPATCHER_DBG_PORT, + .sel_byte = 2, + .sel_msk = B_AX_DISPATCHER_DBG_SEL_MASK, + .srt = 0x0, + .end = 0x5, + .rd_addr = R_AX_DBG_PORT_SEL, + .rd_byte = 4, + .rd_msk = B_AX_DEBUG_ST_MASK +}; + +static const struct rtw89_mac_dbg_port_info dbg_port_dspt_hdt_tx7 = { + .sel_addr = R_AX_DISPATCHER_DBG_PORT, + .sel_byte = 2, + .sel_msk = B_AX_DISPATCHER_DBG_SEL_MASK, + .srt = 0x0, + .end = 0x9, + .rd_addr = R_AX_DBG_PORT_SEL, + .rd_byte = 4, + .rd_msk = B_AX_DEBUG_ST_MASK +}; + +static const struct rtw89_mac_dbg_port_info dbg_port_dspt_hdt_tx8 = { + .sel_addr = R_AX_DISPATCHER_DBG_PORT, + .sel_byte = 2, + .sel_msk = B_AX_DISPATCHER_DBG_SEL_MASK, + .srt = 0x0, + .end = 0x3, + .rd_addr = R_AX_DBG_PORT_SEL, + .rd_byte = 4, + .rd_msk = B_AX_DEBUG_ST_MASK +}; + +static const struct rtw89_mac_dbg_port_info dbg_port_dspt_hdt_tx9_C = { + .sel_addr = R_AX_DISPATCHER_DBG_PORT, + .sel_byte = 2, + .sel_msk = B_AX_DISPATCHER_DBG_SEL_MASK, + .srt = 0x0, + .end = 0x1, + .rd_addr = R_AX_DBG_PORT_SEL, + .rd_byte = 4, + .rd_msk = B_AX_DEBUG_ST_MASK +}; + +static const struct rtw89_mac_dbg_port_info dbg_port_dspt_hdt_txD = { + .sel_addr = R_AX_DISPATCHER_DBG_PORT, + .sel_byte = 2, + .sel_msk = B_AX_DISPATCHER_DBG_SEL_MASK, + .srt = 0x0, + .end = 0x0, + .rd_addr = R_AX_DBG_PORT_SEL, + .rd_byte = 4, + .rd_msk = B_AX_DEBUG_ST_MASK +}; + +static const struct rtw89_mac_dbg_port_info dbg_port_dspt_cdt_tx0 = { + .sel_addr = R_AX_DISPATCHER_DBG_PORT, + .sel_byte = 2, + .sel_msk = B_AX_DISPATCHER_DBG_SEL_MASK, + .srt = 0x0, + .end = 0xB, + .rd_addr = R_AX_DBG_PORT_SEL, + .rd_byte = 4, + .rd_msk = B_AX_DEBUG_ST_MASK +}; + +static const struct rtw89_mac_dbg_port_info dbg_port_dspt_cdt_tx1 = { + .sel_addr = R_AX_DISPATCHER_DBG_PORT, + .sel_byte = 2, + .sel_msk = B_AX_DISPATCHER_DBG_SEL_MASK, + .srt = 0x0, + .end = 0x4, + .rd_addr = R_AX_DBG_PORT_SEL, + .rd_byte = 4, + .rd_msk = B_AX_DEBUG_ST_MASK +}; + +static const struct rtw89_mac_dbg_port_info dbg_port_dspt_cdt_tx3 = { + .sel_addr = R_AX_DISPATCHER_DBG_PORT, + .sel_byte = 2, + .sel_msk = B_AX_DISPATCHER_DBG_SEL_MASK, + .srt = 0x0, + .end = 0x8, + .rd_addr = R_AX_DBG_PORT_SEL, + .rd_byte = 4, + .rd_msk = B_AX_DEBUG_ST_MASK +}; + +static const struct rtw89_mac_dbg_port_info dbg_port_dspt_cdt_tx4 = { + .sel_addr = R_AX_DISPATCHER_DBG_PORT, + .sel_byte = 2, + .sel_msk = B_AX_DISPATCHER_DBG_SEL_MASK, + .srt = 0x0, + .end = 0x7, + .rd_addr = R_AX_DBG_PORT_SEL, + .rd_byte = 4, + .rd_msk = B_AX_DEBUG_ST_MASK +}; + +static const struct rtw89_mac_dbg_port_info dbg_port_dspt_cdt_tx5_8 = { + .sel_addr = R_AX_DISPATCHER_DBG_PORT, + .sel_byte = 2, + .sel_msk = B_AX_DISPATCHER_DBG_SEL_MASK, + .srt = 0x0, + .end = 0x1, + .rd_addr = R_AX_DBG_PORT_SEL, + .rd_byte = 4, + .rd_msk = B_AX_DEBUG_ST_MASK +}; + +static const struct rtw89_mac_dbg_port_info dbg_port_dspt_cdt_tx9 = { + .sel_addr = R_AX_DISPATCHER_DBG_PORT, + .sel_byte = 2, + .sel_msk = B_AX_DISPATCHER_DBG_SEL_MASK, + .srt = 0x0, + .end = 0x3, + .rd_addr = R_AX_DBG_PORT_SEL, + .rd_byte = 4, + .rd_msk = B_AX_DEBUG_ST_MASK +}; + +static const struct rtw89_mac_dbg_port_info dbg_port_dspt_cdt_txA_C = { + .sel_addr = R_AX_DISPATCHER_DBG_PORT, + .sel_byte = 2, + .sel_msk = B_AX_DISPATCHER_DBG_SEL_MASK, + .srt = 0x0, + .end = 0x0, + .rd_addr = R_AX_DBG_PORT_SEL, + .rd_byte = 4, + .rd_msk = B_AX_DEBUG_ST_MASK +}; + +static const struct rtw89_mac_dbg_port_info dbg_port_dspt_hdt_rx0 = { + .sel_addr = R_AX_DISPATCHER_DBG_PORT, + .sel_byte = 2, + .sel_msk = B_AX_DISPATCHER_DBG_SEL_MASK, + .srt = 0x0, + .end = 0x8, + .rd_addr = R_AX_DBG_PORT_SEL, + .rd_byte = 4, + .rd_msk = B_AX_DEBUG_ST_MASK +}; + +static const struct rtw89_mac_dbg_port_info dbg_port_dspt_hdt_rx1_2 = { + .sel_addr = R_AX_DISPATCHER_DBG_PORT, + .sel_byte = 2, + .sel_msk = B_AX_DISPATCHER_DBG_SEL_MASK, + .srt = 0x0, + .end = 0x0, + .rd_addr = R_AX_DBG_PORT_SEL, + .rd_byte = 4, + .rd_msk = B_AX_DEBUG_ST_MASK +}; + +static const struct rtw89_mac_dbg_port_info dbg_port_dspt_hdt_rx3 = { + .sel_addr = R_AX_DISPATCHER_DBG_PORT, + .sel_byte = 2, + .sel_msk = B_AX_DISPATCHER_DBG_SEL_MASK, + .srt = 0x0, + .end = 0x6, + .rd_addr = R_AX_DBG_PORT_SEL, + .rd_byte = 4, + .rd_msk = B_AX_DEBUG_ST_MASK +}; + +static const struct rtw89_mac_dbg_port_info dbg_port_dspt_hdt_rx4 = { + .sel_addr = R_AX_DISPATCHER_DBG_PORT, + .sel_byte = 2, + .sel_msk = B_AX_DISPATCHER_DBG_SEL_MASK, + .srt = 0x0, + .end = 0x0, + .rd_addr = R_AX_DBG_PORT_SEL, + .rd_byte = 4, + .rd_msk = B_AX_DEBUG_ST_MASK +}; + +static const struct rtw89_mac_dbg_port_info dbg_port_dspt_hdt_rx5 = { + .sel_addr = R_AX_DISPATCHER_DBG_PORT, + .sel_byte = 2, + .sel_msk = B_AX_DISPATCHER_DBG_SEL_MASK, + .srt = 0x0, + .end = 0x0, + .rd_addr = R_AX_DBG_PORT_SEL, + .rd_byte = 4, + .rd_msk = B_AX_DEBUG_ST_MASK +}; + +static const struct rtw89_mac_dbg_port_info dbg_port_dspt_cdt_rx_p0_0 = { + .sel_addr = R_AX_DISPATCHER_DBG_PORT, + .sel_byte = 1, + .sel_msk = B_AX_DISPATCHER_CH_SEL_MASK, + .srt = 0x0, + .end = 0x3, + .rd_addr = R_AX_DBG_PORT_SEL, + .rd_byte = 4, + .rd_msk = B_AX_DEBUG_ST_MASK +}; + +static const struct rtw89_mac_dbg_port_info dbg_port_dspt_cdt_rx_p0_1 = { + .sel_addr = R_AX_DISPATCHER_DBG_PORT, + .sel_byte = 1, + .sel_msk = B_AX_DISPATCHER_CH_SEL_MASK, + .srt = 0x0, + .end = 0x6, + .rd_addr = R_AX_DBG_PORT_SEL, + .rd_byte = 4, + .rd_msk = B_AX_DEBUG_ST_MASK +}; + +static const struct rtw89_mac_dbg_port_info dbg_port_dspt_cdt_rx_p0_2 = { + .sel_addr = R_AX_DISPATCHER_DBG_PORT, + .sel_byte = 1, + .sel_msk = B_AX_DISPATCHER_CH_SEL_MASK, + .srt = 0x0, + .end = 0x0, + .rd_addr = R_AX_DBG_PORT_SEL, + .rd_byte = 4, + .rd_msk = B_AX_DEBUG_ST_MASK +}; + +static const struct rtw89_mac_dbg_port_info dbg_port_dspt_cdt_rx_p1 = { + .sel_addr = R_AX_DISPATCHER_DBG_PORT, + .sel_byte = 1, + .sel_msk = B_AX_DISPATCHER_CH_SEL_MASK, + .srt = 0x8, + .end = 0xE, + .rd_addr = R_AX_DBG_PORT_SEL, + .rd_byte = 4, + .rd_msk = B_AX_DEBUG_ST_MASK +}; + +static const struct rtw89_mac_dbg_port_info dbg_port_dspt_stf_ctrl = { + .sel_addr = R_AX_DISPATCHER_DBG_PORT, + .sel_byte = 1, + .sel_msk = B_AX_DISPATCHER_CH_SEL_MASK, + .srt = 0x0, + .end = 0x5, + .rd_addr = R_AX_DBG_PORT_SEL, + .rd_byte = 4, + .rd_msk = B_AX_DEBUG_ST_MASK +}; + +static const struct rtw89_mac_dbg_port_info dbg_port_dspt_addr_ctrl = { + .sel_addr = R_AX_DISPATCHER_DBG_PORT, + .sel_byte = 1, + .sel_msk = B_AX_DISPATCHER_CH_SEL_MASK, + .srt = 0x0, + .end = 0x6, + .rd_addr = R_AX_DBG_PORT_SEL, + .rd_byte = 4, + .rd_msk = B_AX_DEBUG_ST_MASK +}; + +static const struct rtw89_mac_dbg_port_info dbg_port_dspt_wde_intf = { + .sel_addr = R_AX_DISPATCHER_DBG_PORT, + .sel_byte = 1, + .sel_msk = B_AX_DISPATCHER_CH_SEL_MASK, + .srt = 0x0, + .end = 0xF, + .rd_addr = R_AX_DBG_PORT_SEL, + .rd_byte = 4, + .rd_msk = B_AX_DEBUG_ST_MASK +}; + +static const struct rtw89_mac_dbg_port_info dbg_port_dspt_ple_intf = { + .sel_addr = R_AX_DISPATCHER_DBG_PORT, + .sel_byte = 1, + .sel_msk = B_AX_DISPATCHER_CH_SEL_MASK, + .srt = 0x0, + .end = 0x9, + .rd_addr = R_AX_DBG_PORT_SEL, + .rd_byte = 4, + .rd_msk = B_AX_DEBUG_ST_MASK +}; + +static const struct rtw89_mac_dbg_port_info dbg_port_dspt_flow_ctrl = { + .sel_addr = R_AX_DISPATCHER_DBG_PORT, + .sel_byte = 1, + .sel_msk = B_AX_DISPATCHER_CH_SEL_MASK, + .srt = 0x0, + .end = 0x3, + .rd_addr = R_AX_DBG_PORT_SEL, + .rd_byte = 4, + .rd_msk = B_AX_DEBUG_ST_MASK +}; + static const struct rtw89_mac_dbg_port_info dbg_port_sch_c0 = { .sel_addr = R_AX_SCH_DBG_SEL, .sel_byte = 1, @@ -1603,6 +1900,7 @@ rtw89_debug_mac_dbg_port_sel(struct seq_file *m, struct rtw89_dev *rtwdev, u32 sel) { const struct rtw89_mac_dbg_port_info *info; + u32 index; u32 val32; u16 val16; u8 val8; @@ -1878,6 +2176,235 @@ rtw89_debug_mac_dbg_port_sel(struct seq_file *m, info = &dbg_port_pktinfo; seq_puts(m, "Enable pktinfo dump.\n"); break; + case RTW89_DBG_PORT_SEL_DSPT_HDT_TX0: + rtw89_write32_mask(rtwdev, R_AX_DBG_CTRL, + B_AX_DBG_SEL0, 0x80); + rtw89_write32_mask(rtwdev, R_AX_SYS_STATUS1, + B_AX_SEL_0XC0_MASK, 1); + fallthrough; + case RTW89_DBG_PORT_SEL_DSPT_HDT_TX1: + case RTW89_DBG_PORT_SEL_DSPT_HDT_TX2: + case RTW89_DBG_PORT_SEL_DSPT_HDT_TX3: + case RTW89_DBG_PORT_SEL_DSPT_HDT_TX4: + case RTW89_DBG_PORT_SEL_DSPT_HDT_TX5: + info = &dbg_port_dspt_hdt_tx0_5; + index = sel - RTW89_DBG_PORT_SEL_DSPT_HDT_TX0; + rtw89_write16_mask(rtwdev, info->sel_addr, + B_AX_DISPATCHER_INTN_SEL_MASK, 0); + rtw89_write16_mask(rtwdev, info->sel_addr, + B_AX_DISPATCHER_CH_SEL_MASK, index); + seq_printf(m, "Enable Dispatcher hdt tx%x dump.\n", index); + break; + case RTW89_DBG_PORT_SEL_DSPT_HDT_TX6: + info = &dbg_port_dspt_hdt_tx6; + rtw89_write16_mask(rtwdev, info->sel_addr, + B_AX_DISPATCHER_INTN_SEL_MASK, 0); + rtw89_write16_mask(rtwdev, info->sel_addr, + B_AX_DISPATCHER_CH_SEL_MASK, 6); + seq_puts(m, "Enable Dispatcher hdt tx6 dump.\n"); + break; + case RTW89_DBG_PORT_SEL_DSPT_HDT_TX7: + info = &dbg_port_dspt_hdt_tx7; + rtw89_write16_mask(rtwdev, info->sel_addr, + B_AX_DISPATCHER_INTN_SEL_MASK, 0); + rtw89_write16_mask(rtwdev, info->sel_addr, + B_AX_DISPATCHER_CH_SEL_MASK, 7); + seq_puts(m, "Enable Dispatcher hdt tx7 dump.\n"); + break; + case RTW89_DBG_PORT_SEL_DSPT_HDT_TX8: + info = &dbg_port_dspt_hdt_tx8; + rtw89_write16_mask(rtwdev, info->sel_addr, + B_AX_DISPATCHER_INTN_SEL_MASK, 0); + rtw89_write16_mask(rtwdev, info->sel_addr, + B_AX_DISPATCHER_CH_SEL_MASK, 8); + seq_puts(m, "Enable Dispatcher hdt tx8 dump.\n"); + break; + case RTW89_DBG_PORT_SEL_DSPT_HDT_TX9: + case RTW89_DBG_PORT_SEL_DSPT_HDT_TXA: + case RTW89_DBG_PORT_SEL_DSPT_HDT_TXB: + case RTW89_DBG_PORT_SEL_DSPT_HDT_TXC: + info = &dbg_port_dspt_hdt_tx9_C; + index = sel + 9 - RTW89_DBG_PORT_SEL_DSPT_HDT_TX9; + rtw89_write16_mask(rtwdev, info->sel_addr, + B_AX_DISPATCHER_INTN_SEL_MASK, 0); + rtw89_write16_mask(rtwdev, info->sel_addr, + B_AX_DISPATCHER_CH_SEL_MASK, index); + seq_printf(m, "Enable Dispatcher hdt tx%x dump.\n", index); + break; + case RTW89_DBG_PORT_SEL_DSPT_HDT_TXD: + info = &dbg_port_dspt_hdt_txD; + rtw89_write16_mask(rtwdev, info->sel_addr, + B_AX_DISPATCHER_INTN_SEL_MASK, 0); + rtw89_write16_mask(rtwdev, info->sel_addr, + B_AX_DISPATCHER_CH_SEL_MASK, 0xD); + seq_puts(m, "Enable Dispatcher hdt txD dump.\n"); + break; + case RTW89_DBG_PORT_SEL_DSPT_CDT_TX0: + info = &dbg_port_dspt_cdt_tx0; + rtw89_write16_mask(rtwdev, info->sel_addr, + B_AX_DISPATCHER_INTN_SEL_MASK, 1); + rtw89_write16_mask(rtwdev, info->sel_addr, + B_AX_DISPATCHER_CH_SEL_MASK, 0); + seq_puts(m, "Enable Dispatcher cdt tx0 dump.\n"); + break; + case RTW89_DBG_PORT_SEL_DSPT_CDT_TX1: + info = &dbg_port_dspt_cdt_tx1; + rtw89_write16_mask(rtwdev, info->sel_addr, + B_AX_DISPATCHER_INTN_SEL_MASK, 1); + rtw89_write16_mask(rtwdev, info->sel_addr, + B_AX_DISPATCHER_CH_SEL_MASK, 1); + seq_puts(m, "Enable Dispatcher cdt tx1 dump.\n"); + break; + case RTW89_DBG_PORT_SEL_DSPT_CDT_TX3: + info = &dbg_port_dspt_cdt_tx3; + rtw89_write16_mask(rtwdev, info->sel_addr, + B_AX_DISPATCHER_INTN_SEL_MASK, 1); + rtw89_write16_mask(rtwdev, info->sel_addr, + B_AX_DISPATCHER_CH_SEL_MASK, 3); + seq_puts(m, "Enable Dispatcher cdt tx3 dump.\n"); + break; + case RTW89_DBG_PORT_SEL_DSPT_CDT_TX4: + info = &dbg_port_dspt_cdt_tx4; + rtw89_write16_mask(rtwdev, info->sel_addr, + B_AX_DISPATCHER_INTN_SEL_MASK, 1); + rtw89_write16_mask(rtwdev, info->sel_addr, + B_AX_DISPATCHER_CH_SEL_MASK, 4); + seq_puts(m, "Enable Dispatcher cdt tx4 dump.\n"); + break; + case RTW89_DBG_PORT_SEL_DSPT_CDT_TX5: + case RTW89_DBG_PORT_SEL_DSPT_CDT_TX6: + case RTW89_DBG_PORT_SEL_DSPT_CDT_TX7: + case RTW89_DBG_PORT_SEL_DSPT_CDT_TX8: + info = &dbg_port_dspt_cdt_tx5_8; + index = sel + 5 - RTW89_DBG_PORT_SEL_DSPT_CDT_TX5; + rtw89_write16_mask(rtwdev, info->sel_addr, + B_AX_DISPATCHER_INTN_SEL_MASK, 1); + rtw89_write16_mask(rtwdev, info->sel_addr, + B_AX_DISPATCHER_CH_SEL_MASK, index); + seq_printf(m, "Enable Dispatcher cdt tx%x dump.\n", index); + break; + case RTW89_DBG_PORT_SEL_DSPT_CDT_TX9: + info = &dbg_port_dspt_cdt_tx9; + rtw89_write16_mask(rtwdev, info->sel_addr, + B_AX_DISPATCHER_INTN_SEL_MASK, 1); + rtw89_write16_mask(rtwdev, info->sel_addr, + B_AX_DISPATCHER_CH_SEL_MASK, 9); + seq_puts(m, "Enable Dispatcher cdt tx9 dump.\n"); + break; + case RTW89_DBG_PORT_SEL_DSPT_CDT_TXA: + case RTW89_DBG_PORT_SEL_DSPT_CDT_TXB: + case RTW89_DBG_PORT_SEL_DSPT_CDT_TXC: + info = &dbg_port_dspt_cdt_txA_C; + index = sel + 0xA - RTW89_DBG_PORT_SEL_DSPT_CDT_TXA; + rtw89_write16_mask(rtwdev, info->sel_addr, + B_AX_DISPATCHER_INTN_SEL_MASK, 1); + rtw89_write16_mask(rtwdev, info->sel_addr, + B_AX_DISPATCHER_CH_SEL_MASK, index); + seq_printf(m, "Enable Dispatcher cdt tx%x dump.\n", index); + break; + case RTW89_DBG_PORT_SEL_DSPT_HDT_RX0: + info = &dbg_port_dspt_hdt_rx0; + rtw89_write16_mask(rtwdev, info->sel_addr, + B_AX_DISPATCHER_INTN_SEL_MASK, 2); + rtw89_write16_mask(rtwdev, info->sel_addr, + B_AX_DISPATCHER_CH_SEL_MASK, 0); + seq_puts(m, "Enable Dispatcher hdt rx0 dump.\n"); + break; + case RTW89_DBG_PORT_SEL_DSPT_HDT_RX1: + case RTW89_DBG_PORT_SEL_DSPT_HDT_RX2: + info = &dbg_port_dspt_hdt_rx1_2; + index = sel + 1 - RTW89_DBG_PORT_SEL_DSPT_HDT_RX1; + rtw89_write16_mask(rtwdev, info->sel_addr, + B_AX_DISPATCHER_INTN_SEL_MASK, 2); + rtw89_write16_mask(rtwdev, info->sel_addr, + B_AX_DISPATCHER_CH_SEL_MASK, index); + seq_printf(m, "Enable Dispatcher hdt rx%x dump.\n", index); + break; + case RTW89_DBG_PORT_SEL_DSPT_HDT_RX3: + info = &dbg_port_dspt_hdt_rx3; + rtw89_write16_mask(rtwdev, info->sel_addr, + B_AX_DISPATCHER_INTN_SEL_MASK, 2); + rtw89_write16_mask(rtwdev, info->sel_addr, + B_AX_DISPATCHER_CH_SEL_MASK, 3); + seq_puts(m, "Enable Dispatcher hdt rx3 dump.\n"); + break; + case RTW89_DBG_PORT_SEL_DSPT_HDT_RX4: + info = &dbg_port_dspt_hdt_rx4; + rtw89_write16_mask(rtwdev, info->sel_addr, + B_AX_DISPATCHER_INTN_SEL_MASK, 2); + rtw89_write16_mask(rtwdev, info->sel_addr, + B_AX_DISPATCHER_CH_SEL_MASK, 4); + seq_puts(m, "Enable Dispatcher hdt rx4 dump.\n"); + break; + case RTW89_DBG_PORT_SEL_DSPT_HDT_RX5: + info = &dbg_port_dspt_hdt_rx5; + rtw89_write16_mask(rtwdev, info->sel_addr, + B_AX_DISPATCHER_INTN_SEL_MASK, 2); + rtw89_write16_mask(rtwdev, info->sel_addr, + B_AX_DISPATCHER_CH_SEL_MASK, 5); + seq_puts(m, "Enable Dispatcher hdt rx5 dump.\n"); + break; + case RTW89_DBG_PORT_SEL_DSPT_CDT_RX_P0_0: + info = &dbg_port_dspt_cdt_rx_p0_0; + rtw89_write16_mask(rtwdev, info->sel_addr, + B_AX_DISPATCHER_INTN_SEL_MASK, 3); + rtw89_write16_mask(rtwdev, info->sel_addr, + B_AX_DISPATCHER_CH_SEL_MASK, 0); + seq_puts(m, "Enable Dispatcher cdt rx part0 0 dump.\n"); + break; + case RTW89_DBG_PORT_SEL_DSPT_CDT_RX_P0: + case RTW89_DBG_PORT_SEL_DSPT_CDT_RX_P0_1: + info = &dbg_port_dspt_cdt_rx_p0_1; + rtw89_write16_mask(rtwdev, info->sel_addr, + B_AX_DISPATCHER_INTN_SEL_MASK, 3); + rtw89_write16_mask(rtwdev, info->sel_addr, + B_AX_DISPATCHER_CH_SEL_MASK, 1); + seq_puts(m, "Enable Dispatcher cdt rx part0 1 dump.\n"); + break; + case RTW89_DBG_PORT_SEL_DSPT_CDT_RX_P0_2: + info = &dbg_port_dspt_cdt_rx_p0_2; + rtw89_write16_mask(rtwdev, info->sel_addr, + B_AX_DISPATCHER_INTN_SEL_MASK, 3); + rtw89_write16_mask(rtwdev, info->sel_addr, + B_AX_DISPATCHER_CH_SEL_MASK, 2); + seq_puts(m, "Enable Dispatcher cdt rx part0 2 dump.\n"); + break; + case RTW89_DBG_PORT_SEL_DSPT_CDT_RX_P1: + info = &dbg_port_dspt_cdt_rx_p1; + rtw89_write8_mask(rtwdev, info->sel_addr, + B_AX_DISPATCHER_INTN_SEL_MASK, 3); + seq_puts(m, "Enable Dispatcher cdt rx part1 dump.\n"); + break; + case RTW89_DBG_PORT_SEL_DSPT_STF_CTRL: + info = &dbg_port_dspt_stf_ctrl; + rtw89_write8_mask(rtwdev, info->sel_addr, + B_AX_DISPATCHER_INTN_SEL_MASK, 4); + seq_puts(m, "Enable Dispatcher stf control dump.\n"); + break; + case RTW89_DBG_PORT_SEL_DSPT_ADDR_CTRL: + info = &dbg_port_dspt_addr_ctrl; + rtw89_write8_mask(rtwdev, info->sel_addr, + B_AX_DISPATCHER_INTN_SEL_MASK, 5); + seq_puts(m, "Enable Dispatcher addr control dump.\n"); + break; + case RTW89_DBG_PORT_SEL_DSPT_WDE_INTF: + info = &dbg_port_dspt_wde_intf; + rtw89_write8_mask(rtwdev, info->sel_addr, + B_AX_DISPATCHER_INTN_SEL_MASK, 6); + seq_puts(m, "Enable Dispatcher wde interface dump.\n"); + break; + case RTW89_DBG_PORT_SEL_DSPT_PLE_INTF: + info = &dbg_port_dspt_ple_intf; + rtw89_write8_mask(rtwdev, info->sel_addr, + B_AX_DISPATCHER_INTN_SEL_MASK, 7); + seq_puts(m, "Enable Dispatcher ple interface dump.\n"); + break; + case RTW89_DBG_PORT_SEL_DSPT_FLOW_CTRL: + info = &dbg_port_dspt_flow_ctrl; + rtw89_write8_mask(rtwdev, info->sel_addr, + B_AX_DISPATCHER_INTN_SEL_MASK, 8); + seq_puts(m, "Enable Dispatcher flow control dump.\n"); + break; case RTW89_DBG_PORT_SEL_PCIE_TXDMA: info = &dbg_port_pcie_txdma; val32 = rtw89_read32(rtwdev, R_AX_DBG_CTRL); @@ -1956,6 +2483,10 @@ static bool is_dbg_port_valid(struct rtw89_dev *rtwdev, u32 sel) sel >= RTW89_DBG_PORT_SEL_WDE_BUFMGN_FREEPG && sel <= RTW89_DBG_PORT_SEL_PKTINFO) return false; + if (rtw89_mac_check_mac_en(rtwdev, 0, RTW89_DMAC_SEL) && + sel >= RTW89_DBG_PORT_SEL_DSPT_HDT_TX0 && + sel <= RTW89_DBG_PORT_SEL_DSPT_FLOW_CTRL) + return false; if (rtw89_mac_check_mac_en(rtwdev, 0, RTW89_CMAC_SEL) && sel >= RTW89_DBG_PORT_SEL_PTCL_C0 && sel <= RTW89_DBG_PORT_SEL_TXTF_INFOH_C0) @@ -2026,6 +2557,50 @@ static int rtw89_debug_mac_dbg_port_dump(struct rtw89_dev *rtwdev, case_DBG_SEL(PLE_QUEMGN_QLNKTBL); case_DBG_SEL(PLE_QUEMGN_QEMPTY); case_DBG_SEL(PKTINFO); + case_DBG_SEL(DSPT_HDT_TX0); + case_DBG_SEL(DSPT_HDT_TX1); + case_DBG_SEL(DSPT_HDT_TX2); + case_DBG_SEL(DSPT_HDT_TX3); + case_DBG_SEL(DSPT_HDT_TX4); + case_DBG_SEL(DSPT_HDT_TX5); + case_DBG_SEL(DSPT_HDT_TX6); + case_DBG_SEL(DSPT_HDT_TX7); + case_DBG_SEL(DSPT_HDT_TX8); + case_DBG_SEL(DSPT_HDT_TX9); + case_DBG_SEL(DSPT_HDT_TXA); + case_DBG_SEL(DSPT_HDT_TXB); + case_DBG_SEL(DSPT_HDT_TXC); + case_DBG_SEL(DSPT_HDT_TXD); + case_DBG_SEL(DSPT_HDT_TXE); + case_DBG_SEL(DSPT_HDT_TXF); + case_DBG_SEL(DSPT_CDT_TX0); + case_DBG_SEL(DSPT_CDT_TX1); + case_DBG_SEL(DSPT_CDT_TX3); + case_DBG_SEL(DSPT_CDT_TX4); + case_DBG_SEL(DSPT_CDT_TX5); + case_DBG_SEL(DSPT_CDT_TX6); + case_DBG_SEL(DSPT_CDT_TX7); + case_DBG_SEL(DSPT_CDT_TX8); + case_DBG_SEL(DSPT_CDT_TX9); + case_DBG_SEL(DSPT_CDT_TXA); + case_DBG_SEL(DSPT_CDT_TXB); + case_DBG_SEL(DSPT_CDT_TXC); + case_DBG_SEL(DSPT_HDT_RX0); + case_DBG_SEL(DSPT_HDT_RX1); + case_DBG_SEL(DSPT_HDT_RX2); + case_DBG_SEL(DSPT_HDT_RX3); + case_DBG_SEL(DSPT_HDT_RX4); + case_DBG_SEL(DSPT_HDT_RX5); + case_DBG_SEL(DSPT_CDT_RX_P0); + case_DBG_SEL(DSPT_CDT_RX_P0_0); + case_DBG_SEL(DSPT_CDT_RX_P0_1); + case_DBG_SEL(DSPT_CDT_RX_P0_2); + case_DBG_SEL(DSPT_CDT_RX_P1); + case_DBG_SEL(DSPT_STF_CTRL); + case_DBG_SEL(DSPT_ADDR_CTRL); + case_DBG_SEL(DSPT_WDE_INTF); + case_DBG_SEL(DSPT_PLE_INTF); + case_DBG_SEL(DSPT_FLOW_CTRL); case_DBG_SEL(PCIE_TXDMA); case_DBG_SEL(PCIE_RXDMA); case_DBG_SEL(PCIE_CVT); diff --git a/drivers/net/wireless/realtek/rtw89/mac.h b/drivers/net/wireless/realtek/rtw89/mac.h index 34f7d51be3f9..46d9cad2b5ec 100644 --- a/drivers/net/wireless/realtek/rtw89/mac.h +++ b/drivers/net/wireless/realtek/rtw89/mac.h @@ -211,6 +211,51 @@ enum rtw89_mac_dbg_port_sel { RTW89_DBG_PORT_SEL_PLE_QUEMGN_QLNKTBL, RTW89_DBG_PORT_SEL_PLE_QUEMGN_QEMPTY, RTW89_DBG_PORT_SEL_PKTINFO, + /* DISPATCHER related */ + RTW89_DBG_PORT_SEL_DSPT_HDT_TX0, + RTW89_DBG_PORT_SEL_DSPT_HDT_TX1, + RTW89_DBG_PORT_SEL_DSPT_HDT_TX2, + RTW89_DBG_PORT_SEL_DSPT_HDT_TX3, + RTW89_DBG_PORT_SEL_DSPT_HDT_TX4, + RTW89_DBG_PORT_SEL_DSPT_HDT_TX5, + RTW89_DBG_PORT_SEL_DSPT_HDT_TX6, + RTW89_DBG_PORT_SEL_DSPT_HDT_TX7, + RTW89_DBG_PORT_SEL_DSPT_HDT_TX8, + RTW89_DBG_PORT_SEL_DSPT_HDT_TX9, + RTW89_DBG_PORT_SEL_DSPT_HDT_TXA, + RTW89_DBG_PORT_SEL_DSPT_HDT_TXB, + RTW89_DBG_PORT_SEL_DSPT_HDT_TXC, + RTW89_DBG_PORT_SEL_DSPT_HDT_TXD, + RTW89_DBG_PORT_SEL_DSPT_HDT_TXE, + RTW89_DBG_PORT_SEL_DSPT_HDT_TXF, + RTW89_DBG_PORT_SEL_DSPT_CDT_TX0, + RTW89_DBG_PORT_SEL_DSPT_CDT_TX1, + RTW89_DBG_PORT_SEL_DSPT_CDT_TX3, + RTW89_DBG_PORT_SEL_DSPT_CDT_TX4, + RTW89_DBG_PORT_SEL_DSPT_CDT_TX5, + RTW89_DBG_PORT_SEL_DSPT_CDT_TX6, + RTW89_DBG_PORT_SEL_DSPT_CDT_TX7, + RTW89_DBG_PORT_SEL_DSPT_CDT_TX8, + RTW89_DBG_PORT_SEL_DSPT_CDT_TX9, + RTW89_DBG_PORT_SEL_DSPT_CDT_TXA, + RTW89_DBG_PORT_SEL_DSPT_CDT_TXB, + RTW89_DBG_PORT_SEL_DSPT_CDT_TXC, + RTW89_DBG_PORT_SEL_DSPT_HDT_RX0, + RTW89_DBG_PORT_SEL_DSPT_HDT_RX1, + RTW89_DBG_PORT_SEL_DSPT_HDT_RX2, + RTW89_DBG_PORT_SEL_DSPT_HDT_RX3, + RTW89_DBG_PORT_SEL_DSPT_HDT_RX4, + RTW89_DBG_PORT_SEL_DSPT_HDT_RX5, + RTW89_DBG_PORT_SEL_DSPT_CDT_RX_P0, + RTW89_DBG_PORT_SEL_DSPT_CDT_RX_P0_0, + RTW89_DBG_PORT_SEL_DSPT_CDT_RX_P0_1, + RTW89_DBG_PORT_SEL_DSPT_CDT_RX_P0_2, + RTW89_DBG_PORT_SEL_DSPT_CDT_RX_P1, + RTW89_DBG_PORT_SEL_DSPT_STF_CTRL, + RTW89_DBG_PORT_SEL_DSPT_ADDR_CTRL, + RTW89_DBG_PORT_SEL_DSPT_WDE_INTF, + RTW89_DBG_PORT_SEL_DSPT_PLE_INTF, + RTW89_DBG_PORT_SEL_DSPT_FLOW_CTRL, /* PCIE related */ RTW89_DBG_PORT_SEL_PCIE_TXDMA, RTW89_DBG_PORT_SEL_PCIE_RXDMA, diff --git a/drivers/net/wireless/realtek/rtw89/reg.h b/drivers/net/wireless/realtek/rtw89/reg.h index 2f6358244934..8a1cb8f4aa16 100644 --- a/drivers/net/wireless/realtek/rtw89/reg.h +++ b/drivers/net/wireless/realtek/rtw89/reg.h @@ -952,6 +952,11 @@ B_AX_STF_OQT_OVERFLOW_ERR_INT_EN | \ B_AX_STF_OQT_UNDERFLOW_ERR_INT_EN) +#define R_AX_DISPATCHER_DBG_PORT 0x8860 +#define B_AX_DISPATCHER_DBG_SEL_MASK GENMASK(11, 8) +#define B_AX_DISPATCHER_INTN_SEL_MASK GENMASK(7, 4) +#define B_AX_DISPATCHER_CH_SEL_MASK GENMASK(3, 0) + #define R_AX_RX_FUNCTION_STOP 0x8920 #define B_AX_HDR_RX_STOP BIT(0) From f7333fc2135b96dc36965a8e711a9275432256df Mon Sep 17 00:00:00 2001 From: Chia-Yuan Li Date: Wed, 2 Nov 2022 09:43:00 +0800 Subject: [PATCH 0703/2002] wifi: rtw89: update D-MAC and C-MAC dump to diagnose SER To detect TX or RX stuck, we implement SER (system error recovery) in firmware to recover abnormal states of hardware, and report events to driver. This kind of events could happen rarely per day. SER might be true-positive or false-negative cases, and it could be failed to recover true-positive case. We dump related registers to kernel message at that moment and collect them from users, because they occur rarely, randomly and hard to make sure we reproduce the same symptom. To address problems accurately, add more registers by this patch. It also might be false-positive cases that looks like TX or RX get stuck, we need to dump registers from debugfs manually, so also add similar things to debugfs as well. Signed-off-by: Chia-Yuan Li Signed-off-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20221102014300.14091-3-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/debug.c | 438 ++++++++++++++++----- drivers/net/wireless/realtek/rtw89/mac.c | 376 +++++++++++++----- drivers/net/wireless/realtek/rtw89/mac.h | 1 + drivers/net/wireless/realtek/rtw89/pci.h | 12 + drivers/net/wireless/realtek/rtw89/reg.h | 229 ++++++++++- 5 files changed, 852 insertions(+), 204 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/debug.c b/drivers/net/wireless/realtek/rtw89/debug.c index afd10e91d3ea..8297e35bfa52 100644 --- a/drivers/net/wireless/realtek/rtw89/debug.c +++ b/drivers/net/wireless/realtek/rtw89/debug.c @@ -8,6 +8,7 @@ #include "debug.h" #include "fw.h" #include "mac.h" +#include "pci.h" #include "ps.h" #include "reg.h" #include "sar.h" @@ -988,7 +989,9 @@ static int rtw89_debug_mac_dump_dle_dbg(struct rtw89_dev *rtwdev, static int rtw89_debug_mac_dump_dmac_dbg(struct rtw89_dev *rtwdev, struct seq_file *m) { - int ret; + const struct rtw89_chip_info *chip = rtwdev->chip; + u32 dmac_err; + int i, ret; ret = rtw89_mac_check_mac_en(rtwdev, 0, RTW89_DMAC_SEL); if (ret) { @@ -996,44 +999,337 @@ static int rtw89_debug_mac_dump_dmac_dbg(struct rtw89_dev *rtwdev, return ret; } - seq_printf(m, "R_AX_DMAC_ERR_ISR=0x%08x\n", - rtw89_read32(rtwdev, R_AX_DMAC_ERR_ISR)); - seq_printf(m, "[0]R_AX_WDRLS_ERR_ISR=0x%08x\n", - rtw89_read32(rtwdev, R_AX_WDRLS_ERR_ISR)); - seq_printf(m, "[1]R_AX_SEC_ERR_IMR_ISR=0x%08x\n", - rtw89_read32(rtwdev, R_AX_SEC_ERR_IMR_ISR)); - seq_printf(m, "[2.1]R_AX_MPDU_TX_ERR_ISR=0x%08x\n", - rtw89_read32(rtwdev, R_AX_MPDU_TX_ERR_ISR)); - seq_printf(m, "[2.2]R_AX_MPDU_RX_ERR_ISR=0x%08x\n", - rtw89_read32(rtwdev, R_AX_MPDU_RX_ERR_ISR)); - seq_printf(m, "[3]R_AX_STA_SCHEDULER_ERR_ISR=0x%08x\n", - rtw89_read32(rtwdev, R_AX_STA_SCHEDULER_ERR_ISR)); - seq_printf(m, "[4]R_AX_WDE_ERR_ISR=0x%08x\n", - rtw89_read32(rtwdev, R_AX_WDE_ERR_ISR)); - seq_printf(m, "[5.1]R_AX_TXPKTCTL_ERR_IMR_ISR=0x%08x\n", - rtw89_read32(rtwdev, R_AX_TXPKTCTL_ERR_IMR_ISR)); - seq_printf(m, "[5.2]R_AX_TXPKTCTL_ERR_IMR_ISR_B1=0x%08x\n", - rtw89_read32(rtwdev, R_AX_TXPKTCTL_ERR_IMR_ISR_B1)); - seq_printf(m, "[6]R_AX_PLE_ERR_FLAG_ISR=0x%08x\n", - rtw89_read32(rtwdev, R_AX_PLE_ERR_FLAG_ISR)); - seq_printf(m, "[7]R_AX_PKTIN_ERR_ISR=0x%08x\n", - rtw89_read32(rtwdev, R_AX_PKTIN_ERR_ISR)); - seq_printf(m, "[8.1]R_AX_OTHER_DISPATCHER_ERR_ISR=0x%08x\n", - rtw89_read32(rtwdev, R_AX_OTHER_DISPATCHER_ERR_ISR)); - seq_printf(m, "[8.2]R_AX_HOST_DISPATCHER_ERR_ISR=0x%08x\n", - rtw89_read32(rtwdev, R_AX_HOST_DISPATCHER_ERR_ISR)); - seq_printf(m, "[8.3]R_AX_CPU_DISPATCHER_ERR_ISR=0x%08x\n", - rtw89_read32(rtwdev, R_AX_CPU_DISPATCHER_ERR_ISR)); - seq_printf(m, "[10]R_AX_CPUIO_ERR_ISR=0x%08x\n", - rtw89_read32(rtwdev, R_AX_CPUIO_ERR_ISR)); - seq_printf(m, "[11.1]R_AX_BBRPT_COM_ERR_IMR_ISR=0x%08x\n", - rtw89_read32(rtwdev, R_AX_BBRPT_COM_ERR_IMR_ISR)); - seq_printf(m, "[11.2]R_AX_BBRPT_CHINFO_ERR_IMR_ISR=0x%08x\n", - rtw89_read32(rtwdev, R_AX_BBRPT_CHINFO_ERR_IMR_ISR)); - seq_printf(m, "[11.3]R_AX_BBRPT_DFS_ERR_IMR_ISR=0x%08x\n", - rtw89_read32(rtwdev, R_AX_BBRPT_DFS_ERR_IMR_ISR)); - seq_printf(m, "[11.4]R_AX_LA_ERRFLAG=0x%08x\n", - rtw89_read32(rtwdev, R_AX_LA_ERRFLAG)); + dmac_err = rtw89_read32(rtwdev, R_AX_DMAC_ERR_ISR); + seq_printf(m, "R_AX_DMAC_ERR_ISR=0x%08x\n", dmac_err); + seq_printf(m, "R_AX_DMAC_ERR_IMR=0x%08x\n", + rtw89_read32(rtwdev, R_AX_DMAC_ERR_IMR)); + + if (dmac_err) { + seq_printf(m, "R_AX_WDE_ERR_FLAG_CFG=0x%08x\n", + rtw89_read32(rtwdev, R_AX_WDE_ERR_FLAG_CFG_NUM1)); + seq_printf(m, "R_AX_PLE_ERR_FLAG_CFG=0x%08x\n", + rtw89_read32(rtwdev, R_AX_PLE_ERR_FLAG_CFG_NUM1)); + if (chip->chip_id == RTL8852C) { + seq_printf(m, "R_AX_PLE_ERRFLAG_MSG=0x%08x\n", + rtw89_read32(rtwdev, R_AX_PLE_ERRFLAG_MSG)); + seq_printf(m, "R_AX_WDE_ERRFLAG_MSG=0x%08x\n", + rtw89_read32(rtwdev, R_AX_WDE_ERRFLAG_MSG)); + seq_printf(m, "R_AX_PLE_DBGERR_LOCKEN=0x%08x\n", + rtw89_read32(rtwdev, R_AX_PLE_DBGERR_LOCKEN)); + seq_printf(m, "R_AX_PLE_DBGERR_STS=0x%08x\n", + rtw89_read32(rtwdev, R_AX_PLE_DBGERR_STS)); + } + } + + if (dmac_err & B_AX_WDRLS_ERR_FLAG) { + seq_printf(m, "R_AX_WDRLS_ERR_IMR=0x%08x\n", + rtw89_read32(rtwdev, R_AX_WDRLS_ERR_IMR)); + seq_printf(m, "R_AX_WDRLS_ERR_ISR=0x%08x\n", + rtw89_read32(rtwdev, R_AX_WDRLS_ERR_ISR)); + if (chip->chip_id == RTL8852C) + seq_printf(m, "R_AX_RPQ_RXBD_IDX=0x%08x\n", + rtw89_read32(rtwdev, R_AX_RPQ_RXBD_IDX_V1)); + else + seq_printf(m, "R_AX_RPQ_RXBD_IDX=0x%08x\n", + rtw89_read32(rtwdev, R_AX_RPQ_RXBD_IDX)); + } + + if (dmac_err & B_AX_WSEC_ERR_FLAG) { + if (chip->chip_id == RTL8852C) { + seq_printf(m, "R_AX_SEC_ERR_IMR=0x%08x\n", + rtw89_read32(rtwdev, R_AX_SEC_ERROR_FLAG_IMR)); + seq_printf(m, "R_AX_SEC_ERR_ISR=0x%08x\n", + rtw89_read32(rtwdev, R_AX_SEC_ERROR_FLAG)); + seq_printf(m, "R_AX_SEC_ENG_CTRL=0x%08x\n", + rtw89_read32(rtwdev, R_AX_SEC_ENG_CTRL)); + seq_printf(m, "R_AX_SEC_MPDU_PROC=0x%08x\n", + rtw89_read32(rtwdev, R_AX_SEC_MPDU_PROC)); + seq_printf(m, "R_AX_SEC_CAM_ACCESS=0x%08x\n", + rtw89_read32(rtwdev, R_AX_SEC_CAM_ACCESS)); + seq_printf(m, "R_AX_SEC_CAM_RDATA=0x%08x\n", + rtw89_read32(rtwdev, R_AX_SEC_CAM_RDATA)); + seq_printf(m, "R_AX_SEC_DEBUG1=0x%08x\n", + rtw89_read32(rtwdev, R_AX_SEC_DEBUG1)); + seq_printf(m, "R_AX_SEC_TX_DEBUG=0x%08x\n", + rtw89_read32(rtwdev, R_AX_SEC_TX_DEBUG)); + seq_printf(m, "R_AX_SEC_RX_DEBUG=0x%08x\n", + rtw89_read32(rtwdev, R_AX_SEC_RX_DEBUG)); + + rtw89_write32_mask(rtwdev, R_AX_DBG_CTRL, + B_AX_DBG_SEL0, 0x8B); + rtw89_write32_mask(rtwdev, R_AX_DBG_CTRL, + B_AX_DBG_SEL1, 0x8B); + rtw89_write32_mask(rtwdev, R_AX_SYS_STATUS1, + B_AX_SEL_0XC0_MASK, 1); + for (i = 0; i < 0x10; i++) { + rtw89_write32_mask(rtwdev, R_AX_SEC_ENG_CTRL, + B_AX_SEC_DBG_PORT_FIELD_MASK, i); + seq_printf(m, "sel=%x,R_AX_SEC_DEBUG2=0x%08x\n", + i, rtw89_read32(rtwdev, R_AX_SEC_DEBUG2)); + } + } else { + seq_printf(m, "R_AX_SEC_ERR_IMR_ISR=0x%08x\n", + rtw89_read32(rtwdev, R_AX_SEC_DEBUG)); + seq_printf(m, "R_AX_SEC_ENG_CTRL=0x%08x\n", + rtw89_read32(rtwdev, R_AX_SEC_ENG_CTRL)); + seq_printf(m, "R_AX_SEC_MPDU_PROC=0x%08x\n", + rtw89_read32(rtwdev, R_AX_SEC_MPDU_PROC)); + seq_printf(m, "R_AX_SEC_CAM_ACCESS=0x%08x\n", + rtw89_read32(rtwdev, R_AX_SEC_CAM_ACCESS)); + seq_printf(m, "R_AX_SEC_CAM_RDATA=0x%08x\n", + rtw89_read32(rtwdev, R_AX_SEC_CAM_RDATA)); + seq_printf(m, "R_AX_SEC_CAM_WDATA=0x%08x\n", + rtw89_read32(rtwdev, R_AX_SEC_CAM_WDATA)); + seq_printf(m, "R_AX_SEC_TX_DEBUG=0x%08x\n", + rtw89_read32(rtwdev, R_AX_SEC_TX_DEBUG)); + seq_printf(m, "R_AX_SEC_RX_DEBUG=0x%08x\n", + rtw89_read32(rtwdev, R_AX_SEC_RX_DEBUG)); + seq_printf(m, "R_AX_SEC_TRX_PKT_CNT=0x%08x\n", + rtw89_read32(rtwdev, R_AX_SEC_TRX_PKT_CNT)); + seq_printf(m, "R_AX_SEC_TRX_BLK_CNT=0x%08x\n", + rtw89_read32(rtwdev, R_AX_SEC_TRX_BLK_CNT)); + } + } + + if (dmac_err & B_AX_MPDU_ERR_FLAG) { + seq_printf(m, "R_AX_MPDU_TX_ERR_IMR=0x%08x\n", + rtw89_read32(rtwdev, R_AX_MPDU_TX_ERR_IMR)); + seq_printf(m, "R_AX_MPDU_TX_ERR_ISR=0x%08x\n", + rtw89_read32(rtwdev, R_AX_MPDU_TX_ERR_ISR)); + seq_printf(m, "R_AX_MPDU_RX_ERR_IMR=0x%08x\n", + rtw89_read32(rtwdev, R_AX_MPDU_RX_ERR_IMR)); + seq_printf(m, "R_AX_MPDU_RX_ERR_ISR=0x%08x\n", + rtw89_read32(rtwdev, R_AX_MPDU_RX_ERR_ISR)); + } + + if (dmac_err & B_AX_STA_SCHEDULER_ERR_FLAG) { + seq_printf(m, "R_AX_STA_SCHEDULER_ERR_IMR=0x%08x\n", + rtw89_read32(rtwdev, R_AX_STA_SCHEDULER_ERR_IMR)); + seq_printf(m, "R_AX_STA_SCHEDULER_ERR_ISR=0x%08x\n", + rtw89_read32(rtwdev, R_AX_STA_SCHEDULER_ERR_ISR)); + } + + if (dmac_err & B_AX_WDE_DLE_ERR_FLAG) { + seq_printf(m, "R_AX_WDE_ERR_IMR=0x%08x\n", + rtw89_read32(rtwdev, R_AX_WDE_ERR_IMR)); + seq_printf(m, "R_AX_WDE_ERR_ISR=0x%08x\n", + rtw89_read32(rtwdev, R_AX_WDE_ERR_ISR)); + seq_printf(m, "R_AX_PLE_ERR_IMR=0x%08x\n", + rtw89_read32(rtwdev, R_AX_PLE_ERR_IMR)); + seq_printf(m, "R_AX_PLE_ERR_FLAG_ISR=0x%08x\n", + rtw89_read32(rtwdev, R_AX_PLE_ERR_FLAG_ISR)); + } + + if (dmac_err & B_AX_TXPKTCTRL_ERR_FLAG) { + if (chip->chip_id == RTL8852C) { + seq_printf(m, "R_AX_TXPKTCTL_B0_ERRFLAG_IMR=0x%08x\n", + rtw89_read32(rtwdev, R_AX_TXPKTCTL_B0_ERRFLAG_IMR)); + seq_printf(m, "R_AX_TXPKTCTL_B0_ERRFLAG_ISR=0x%08x\n", + rtw89_read32(rtwdev, R_AX_TXPKTCTL_B0_ERRFLAG_ISR)); + seq_printf(m, "R_AX_TXPKTCTL_B1_ERRFLAG_IMR=0x%08x\n", + rtw89_read32(rtwdev, R_AX_TXPKTCTL_B1_ERRFLAG_IMR)); + seq_printf(m, "R_AX_TXPKTCTL_B1_ERRFLAG_ISR=0x%08x\n", + rtw89_read32(rtwdev, R_AX_TXPKTCTL_B1_ERRFLAG_ISR)); + } else { + seq_printf(m, "R_AX_TXPKTCTL_ERR_IMR_ISR=0x%08x\n", + rtw89_read32(rtwdev, R_AX_TXPKTCTL_ERR_IMR_ISR)); + seq_printf(m, "R_AX_TXPKTCTL_ERR_IMR_ISR_B1=0x%08x\n", + rtw89_read32(rtwdev, R_AX_TXPKTCTL_ERR_IMR_ISR_B1)); + } + } + + if (dmac_err & B_AX_PLE_DLE_ERR_FLAG) { + seq_printf(m, "R_AX_WDE_ERR_IMR=0x%08x\n", + rtw89_read32(rtwdev, R_AX_WDE_ERR_IMR)); + seq_printf(m, "R_AX_WDE_ERR_ISR=0x%08x\n", + rtw89_read32(rtwdev, R_AX_WDE_ERR_ISR)); + seq_printf(m, "R_AX_PLE_ERR_IMR=0x%08x\n", + rtw89_read32(rtwdev, R_AX_PLE_ERR_IMR)); + seq_printf(m, "R_AX_PLE_ERR_FLAG_ISR=0x%08x\n", + rtw89_read32(rtwdev, R_AX_PLE_ERR_FLAG_ISR)); + seq_printf(m, "R_AX_WD_CPUQ_OP_0=0x%08x\n", + rtw89_read32(rtwdev, R_AX_WD_CPUQ_OP_0)); + seq_printf(m, "R_AX_WD_CPUQ_OP_1=0x%08x\n", + rtw89_read32(rtwdev, R_AX_WD_CPUQ_OP_1)); + seq_printf(m, "R_AX_WD_CPUQ_OP_2=0x%08x\n", + rtw89_read32(rtwdev, R_AX_WD_CPUQ_OP_2)); + seq_printf(m, "R_AX_WD_CPUQ_OP_STATUS=0x%08x\n", + rtw89_read32(rtwdev, R_AX_WD_CPUQ_OP_STATUS)); + seq_printf(m, "R_AX_PL_CPUQ_OP_0=0x%08x\n", + rtw89_read32(rtwdev, R_AX_PL_CPUQ_OP_0)); + seq_printf(m, "R_AX_PL_CPUQ_OP_1=0x%08x\n", + rtw89_read32(rtwdev, R_AX_PL_CPUQ_OP_1)); + seq_printf(m, "R_AX_PL_CPUQ_OP_2=0x%08x\n", + rtw89_read32(rtwdev, R_AX_PL_CPUQ_OP_2)); + seq_printf(m, "R_AX_PL_CPUQ_OP_STATUS=0x%08x\n", + rtw89_read32(rtwdev, R_AX_PL_CPUQ_OP_STATUS)); + if (chip->chip_id == RTL8852C) { + seq_printf(m, "R_AX_RX_CTRL0=0x%08x\n", + rtw89_read32(rtwdev, R_AX_RX_CTRL0)); + seq_printf(m, "R_AX_RX_CTRL1=0x%08x\n", + rtw89_read32(rtwdev, R_AX_RX_CTRL1)); + seq_printf(m, "R_AX_RX_CTRL2=0x%08x\n", + rtw89_read32(rtwdev, R_AX_RX_CTRL2)); + } else { + seq_printf(m, "R_AX_RXDMA_PKT_INFO_0=0x%08x\n", + rtw89_read32(rtwdev, R_AX_RXDMA_PKT_INFO_0)); + seq_printf(m, "R_AX_RXDMA_PKT_INFO_1=0x%08x\n", + rtw89_read32(rtwdev, R_AX_RXDMA_PKT_INFO_1)); + seq_printf(m, "R_AX_RXDMA_PKT_INFO_2=0x%08x\n", + rtw89_read32(rtwdev, R_AX_RXDMA_PKT_INFO_2)); + } + } + + if (dmac_err & B_AX_PKTIN_ERR_FLAG) { + seq_printf(m, "R_AX_PKTIN_ERR_IMR=0x%08x\n", + rtw89_read32(rtwdev, R_AX_PKTIN_ERR_IMR)); + seq_printf(m, "R_AX_PKTIN_ERR_ISR=0x%08x\n", + rtw89_read32(rtwdev, R_AX_PKTIN_ERR_ISR)); + } + + if (dmac_err & B_AX_DISPATCH_ERR_FLAG) { + seq_printf(m, "R_AX_HOST_DISPATCHER_ERR_IMR=0x%08x\n", + rtw89_read32(rtwdev, R_AX_HOST_DISPATCHER_ERR_IMR)); + seq_printf(m, "R_AX_HOST_DISPATCHER_ERR_ISR=0x%08x\n", + rtw89_read32(rtwdev, R_AX_HOST_DISPATCHER_ERR_ISR)); + seq_printf(m, "R_AX_CPU_DISPATCHER_ERR_IMR=0x%08x\n", + rtw89_read32(rtwdev, R_AX_CPU_DISPATCHER_ERR_IMR)); + seq_printf(m, "R_AX_CPU_DISPATCHER_ERR_ISR=0x%08x\n", + rtw89_read32(rtwdev, R_AX_CPU_DISPATCHER_ERR_ISR)); + seq_printf(m, "R_AX_OTHER_DISPATCHER_ERR_IMR=0x%08x\n", + rtw89_read32(rtwdev, R_AX_OTHER_DISPATCHER_ERR_IMR)); + seq_printf(m, "R_AX_OTHER_DISPATCHER_ERR_ISR=0x%08x\n", + rtw89_read32(rtwdev, R_AX_OTHER_DISPATCHER_ERR_ISR)); + } + + if (dmac_err & B_AX_BBRPT_ERR_FLAG) { + if (chip->chip_id == RTL8852C) { + seq_printf(m, "R_AX_BBRPT_COM_ERR_IMR=0x%08x\n", + rtw89_read32(rtwdev, R_AX_BBRPT_COM_ERR_IMR)); + seq_printf(m, "R_AX_BBRPT_COM_ERR_ISR=0x%08x\n", + rtw89_read32(rtwdev, R_AX_BBRPT_COM_ERR_ISR)); + seq_printf(m, "R_AX_BBRPT_CHINFO_ERR_ISR=0x%08x\n", + rtw89_read32(rtwdev, R_AX_BBRPT_CHINFO_ERR_ISR)); + seq_printf(m, "R_AX_BBRPT_CHINFO_ERR_IMR=0x%08x\n", + rtw89_read32(rtwdev, R_AX_BBRPT_CHINFO_ERR_IMR)); + seq_printf(m, "R_AX_BBRPT_DFS_ERR_IMR=0x%08x\n", + rtw89_read32(rtwdev, R_AX_BBRPT_DFS_ERR_IMR)); + seq_printf(m, "R_AX_BBRPT_DFS_ERR_ISR=0x%08x\n", + rtw89_read32(rtwdev, R_AX_BBRPT_DFS_ERR_ISR)); + } else { + seq_printf(m, "R_AX_BBRPT_COM_ERR_IMR_ISR=0x%08x\n", + rtw89_read32(rtwdev, R_AX_BBRPT_COM_ERR_IMR_ISR)); + seq_printf(m, "R_AX_BBRPT_CHINFO_ERR_ISR=0x%08x\n", + rtw89_read32(rtwdev, R_AX_BBRPT_CHINFO_ERR_ISR)); + seq_printf(m, "R_AX_BBRPT_CHINFO_ERR_IMR=0x%08x\n", + rtw89_read32(rtwdev, R_AX_BBRPT_CHINFO_ERR_IMR)); + seq_printf(m, "R_AX_BBRPT_DFS_ERR_IMR=0x%08x\n", + rtw89_read32(rtwdev, R_AX_BBRPT_DFS_ERR_IMR)); + seq_printf(m, "R_AX_BBRPT_DFS_ERR_ISR=0x%08x\n", + rtw89_read32(rtwdev, R_AX_BBRPT_DFS_ERR_ISR)); + } + } + + if (dmac_err & B_AX_HAXIDMA_ERR_FLAG && chip->chip_id == RTL8852C) { + seq_printf(m, "R_AX_HAXIDMA_ERR_IMR=0x%08x\n", + rtw89_read32(rtwdev, R_AX_HAXI_IDCT_MSK)); + seq_printf(m, "R_AX_HAXIDMA_ERR_ISR=0x%08x\n", + rtw89_read32(rtwdev, R_AX_HAXI_IDCT)); + } + + return 0; +} + +static int rtw89_debug_mac_dump_cmac_err(struct rtw89_dev *rtwdev, + struct seq_file *m, + enum rtw89_mac_idx band) +{ + const struct rtw89_chip_info *chip = rtwdev->chip; + u32 offset = 0; + u32 cmac_err; + int ret; + + ret = rtw89_mac_check_mac_en(rtwdev, band, RTW89_CMAC_SEL); + if (ret) { + if (band) + seq_puts(m, "[CMAC] : CMAC1 not enabled\n"); + else + seq_puts(m, "[CMAC] : CMAC0 not enabled\n"); + return ret; + } + + if (band) + offset = RTW89_MAC_AX_BAND_REG_OFFSET; + + cmac_err = rtw89_read32(rtwdev, R_AX_CMAC_ERR_ISR + offset); + seq_printf(m, "R_AX_CMAC_ERR_ISR [%d]=0x%08x\n", band, + rtw89_read32(rtwdev, R_AX_CMAC_ERR_ISR + offset)); + seq_printf(m, "R_AX_CMAC_FUNC_EN [%d]=0x%08x\n", band, + rtw89_read32(rtwdev, R_AX_CMAC_FUNC_EN + offset)); + seq_printf(m, "R_AX_CK_EN [%d]=0x%08x\n", band, + rtw89_read32(rtwdev, R_AX_CK_EN + offset)); + + if (cmac_err & B_AX_SCHEDULE_TOP_ERR_IND) { + seq_printf(m, "R_AX_SCHEDULE_ERR_IMR [%d]=0x%08x\n", band, + rtw89_read32(rtwdev, R_AX_SCHEDULE_ERR_IMR + offset)); + seq_printf(m, "R_AX_SCHEDULE_ERR_ISR [%d]=0x%08x\n", band, + rtw89_read32(rtwdev, R_AX_SCHEDULE_ERR_ISR + offset)); + } + + if (cmac_err & B_AX_PTCL_TOP_ERR_IND) { + seq_printf(m, "R_AX_PTCL_IMR0 [%d]=0x%08x\n", band, + rtw89_read32(rtwdev, R_AX_PTCL_IMR0 + offset)); + seq_printf(m, "R_AX_PTCL_ISR0 [%d]=0x%08x\n", band, + rtw89_read32(rtwdev, R_AX_PTCL_ISR0 + offset)); + } + + if (cmac_err & B_AX_DMA_TOP_ERR_IND) { + if (chip->chip_id == RTL8852C) { + seq_printf(m, "R_AX_RX_ERR_FLAG [%d]=0x%08x\n", band, + rtw89_read32(rtwdev, R_AX_RX_ERR_FLAG + offset)); + seq_printf(m, "R_AX_RX_ERR_FLAG_IMR [%d]=0x%08x\n", band, + rtw89_read32(rtwdev, R_AX_RX_ERR_FLAG_IMR + offset)); + } else { + seq_printf(m, "R_AX_DLE_CTRL [%d]=0x%08x\n", band, + rtw89_read32(rtwdev, R_AX_DLE_CTRL + offset)); + } + } + + if (cmac_err & B_AX_DMA_TOP_ERR_IND || cmac_err & B_AX_WMAC_RX_ERR_IND) { + if (chip->chip_id == RTL8852C) { + seq_printf(m, "R_AX_PHYINFO_ERR_ISR [%d]=0x%08x\n", band, + rtw89_read32(rtwdev, R_AX_PHYINFO_ERR_ISR + offset)); + seq_printf(m, "R_AX_PHYINFO_ERR_IMR [%d]=0x%08x\n", band, + rtw89_read32(rtwdev, R_AX_PHYINFO_ERR_IMR + offset)); + } else { + seq_printf(m, "R_AX_PHYINFO_ERR_IMR [%d]=0x%08x\n", band, + rtw89_read32(rtwdev, R_AX_PHYINFO_ERR_IMR + offset)); + } + } + + if (cmac_err & B_AX_TXPWR_CTRL_ERR_IND) { + seq_printf(m, "R_AX_TXPWR_IMR [%d]=0x%08x\n", band, + rtw89_read32(rtwdev, R_AX_TXPWR_IMR + offset)); + seq_printf(m, "R_AX_TXPWR_ISR [%d]=0x%08x\n", band, + rtw89_read32(rtwdev, R_AX_TXPWR_ISR + offset)); + } + + if (cmac_err & B_AX_WMAC_TX_ERR_IND) { + if (chip->chip_id == RTL8852C) { + seq_printf(m, "R_AX_TRXPTCL_ERROR_INDICA [%d]=0x%08x\n", band, + rtw89_read32(rtwdev, R_AX_TRXPTCL_ERROR_INDICA + offset)); + seq_printf(m, "R_AX_TRXPTCL_ERROR_INDICA_MASK [%d]=0x%08x\n", band, + rtw89_read32(rtwdev, R_AX_TRXPTCL_ERROR_INDICA_MASK + offset)); + } else { + seq_printf(m, "R_AX_TMAC_ERR_IMR_ISR [%d]=0x%08x\n", band, + rtw89_read32(rtwdev, R_AX_TMAC_ERR_IMR_ISR + offset)); + } + seq_printf(m, "R_AX_DBGSEL_TRXPTCL [%d]=0x%08x\n", band, + rtw89_read32(rtwdev, R_AX_DBGSEL_TRXPTCL + offset)); + } + + seq_printf(m, "R_AX_CMAC_ERR_IMR [%d]=0x%08x\n", band, + rtw89_read32(rtwdev, R_AX_CMAC_ERR_IMR + offset)); return 0; } @@ -1041,53 +1337,9 @@ static int rtw89_debug_mac_dump_dmac_dbg(struct rtw89_dev *rtwdev, static int rtw89_debug_mac_dump_cmac_dbg(struct rtw89_dev *rtwdev, struct seq_file *m) { - int ret; - - ret = rtw89_mac_check_mac_en(rtwdev, 0, RTW89_CMAC_SEL); - if (ret) { - seq_puts(m, "[CMAC] : CMAC 0 not enabled\n"); - return ret; - } - - seq_printf(m, "R_AX_CMAC_ERR_ISR=0x%08x\n", - rtw89_read32(rtwdev, R_AX_CMAC_ERR_ISR)); - seq_printf(m, "[0]R_AX_SCHEDULE_ERR_ISR=0x%08x\n", - rtw89_read32(rtwdev, R_AX_SCHEDULE_ERR_ISR)); - seq_printf(m, "[1]R_AX_PTCL_ISR0=0x%08x\n", - rtw89_read32(rtwdev, R_AX_PTCL_ISR0)); - seq_printf(m, "[3]R_AX_DLE_CTRL=0x%08x\n", - rtw89_read32(rtwdev, R_AX_DLE_CTRL)); - seq_printf(m, "[4]R_AX_PHYINFO_ERR_ISR=0x%08x\n", - rtw89_read32(rtwdev, R_AX_PHYINFO_ERR_ISR)); - seq_printf(m, "[5]R_AX_TXPWR_ISR=0x%08x\n", - rtw89_read32(rtwdev, R_AX_TXPWR_ISR)); - seq_printf(m, "[6]R_AX_RMAC_ERR_ISR=0x%08x\n", - rtw89_read32(rtwdev, R_AX_RMAC_ERR_ISR)); - seq_printf(m, "[7]R_AX_TMAC_ERR_IMR_ISR=0x%08x\n", - rtw89_read32(rtwdev, R_AX_TMAC_ERR_IMR_ISR)); - - ret = rtw89_mac_check_mac_en(rtwdev, 1, RTW89_CMAC_SEL); - if (ret) { - seq_puts(m, "[CMAC] : CMAC 1 not enabled\n"); - return ret; - } - - seq_printf(m, "R_AX_CMAC_ERR_ISR_C1=0x%08x\n", - rtw89_read32(rtwdev, R_AX_CMAC_ERR_ISR_C1)); - seq_printf(m, "[0]R_AX_SCHEDULE_ERR_ISR_C1=0x%08x\n", - rtw89_read32(rtwdev, R_AX_SCHEDULE_ERR_ISR_C1)); - seq_printf(m, "[1]R_AX_PTCL_ISR0_C1=0x%08x\n", - rtw89_read32(rtwdev, R_AX_PTCL_ISR0_C1)); - seq_printf(m, "[3]R_AX_DLE_CTRL_C1=0x%08x\n", - rtw89_read32(rtwdev, R_AX_DLE_CTRL_C1)); - seq_printf(m, "[4]R_AX_PHYINFO_ERR_ISR_C1=0x%02x\n", - rtw89_read32(rtwdev, R_AX_PHYINFO_ERR_ISR_C1)); - seq_printf(m, "[5]R_AX_TXPWR_ISR_C1=0x%08x\n", - rtw89_read32(rtwdev, R_AX_TXPWR_ISR_C1)); - seq_printf(m, "[6]R_AX_RMAC_ERR_ISR_C1=0x%08x\n", - rtw89_read32(rtwdev, R_AX_RMAC_ERR_ISR_C1)); - seq_printf(m, "[7]R_AX_TMAC_ERR_IMR_ISR_C1=0x%08x\n", - rtw89_read32(rtwdev, R_AX_TMAC_ERR_IMR_ISR_C1)); + rtw89_debug_mac_dump_cmac_err(rtwdev, m, RTW89_MAC_0); + if (rtwdev->dbcc_en) + rtw89_debug_mac_dump_cmac_err(rtwdev, m, RTW89_MAC_1); return 0; } @@ -1821,7 +2073,7 @@ static const struct rtw89_mac_dbg_port_info dbg_port_pktinfo = { static const struct rtw89_mac_dbg_port_info dbg_port_pcie_txdma = { .sel_addr = R_AX_PCIE_DBG_CTRL, .sel_byte = 2, - .sel_msk = B_AX_DBG_SEL_MASK, + .sel_msk = B_AX_PCIE_DBG_SEL_MASK, .srt = 0x00, .end = 0x03, .rd_addr = R_AX_DBG_PORT_SEL, @@ -1832,7 +2084,7 @@ static const struct rtw89_mac_dbg_port_info dbg_port_pcie_txdma = { static const struct rtw89_mac_dbg_port_info dbg_port_pcie_rxdma = { .sel_addr = R_AX_PCIE_DBG_CTRL, .sel_byte = 2, - .sel_msk = B_AX_DBG_SEL_MASK, + .sel_msk = B_AX_PCIE_DBG_SEL_MASK, .srt = 0x00, .end = 0x04, .rd_addr = R_AX_DBG_PORT_SEL, @@ -1843,7 +2095,7 @@ static const struct rtw89_mac_dbg_port_info dbg_port_pcie_rxdma = { static const struct rtw89_mac_dbg_port_info dbg_port_pcie_cvt = { .sel_addr = R_AX_PCIE_DBG_CTRL, .sel_byte = 2, - .sel_msk = B_AX_DBG_SEL_MASK, + .sel_msk = B_AX_PCIE_DBG_SEL_MASK, .srt = 0x00, .end = 0x01, .rd_addr = R_AX_DBG_PORT_SEL, @@ -1854,7 +2106,7 @@ static const struct rtw89_mac_dbg_port_info dbg_port_pcie_cvt = { static const struct rtw89_mac_dbg_port_info dbg_port_pcie_cxpl = { .sel_addr = R_AX_PCIE_DBG_CTRL, .sel_byte = 2, - .sel_msk = B_AX_DBG_SEL_MASK, + .sel_msk = B_AX_PCIE_DBG_SEL_MASK, .srt = 0x00, .end = 0x05, .rd_addr = R_AX_DBG_PORT_SEL, @@ -1865,7 +2117,7 @@ static const struct rtw89_mac_dbg_port_info dbg_port_pcie_cxpl = { static const struct rtw89_mac_dbg_port_info dbg_port_pcie_io = { .sel_addr = R_AX_PCIE_DBG_CTRL, .sel_byte = 2, - .sel_msk = B_AX_DBG_SEL_MASK, + .sel_msk = B_AX_PCIE_DBG_SEL_MASK, .srt = 0x00, .end = 0x05, .rd_addr = R_AX_DBG_PORT_SEL, @@ -1876,7 +2128,7 @@ static const struct rtw89_mac_dbg_port_info dbg_port_pcie_io = { static const struct rtw89_mac_dbg_port_info dbg_port_pcie_misc = { .sel_addr = R_AX_PCIE_DBG_CTRL, .sel_byte = 2, - .sel_msk = B_AX_DBG_SEL_MASK, + .sel_msk = B_AX_PCIE_DBG_SEL_MASK, .srt = 0x00, .end = 0x06, .rd_addr = R_AX_DBG_PORT_SEL, @@ -2457,7 +2709,7 @@ rtw89_debug_mac_dbg_port_sel(struct seq_file *m, info = &dbg_port_pcie_misc2; val16 = rtw89_read16(rtwdev, R_AX_PCIE_DBG_CTRL); val16 = u16_replace_bits(val16, PCIE_MISC2_DBG_SEL, - B_AX_DBG_SEL_MASK); + B_AX_PCIE_DBG_SEL_MASK); rtw89_write16(rtwdev, R_AX_PCIE_DBG_CTRL, val16); seq_puts(m, "Enable pcie misc2 dump.\n"); break; diff --git a/drivers/net/wireless/realtek/rtw89/mac.c b/drivers/net/wireless/realtek/rtw89/mac.c index bb49033b587d..ac8e66be327f 100644 --- a/drivers/net/wireless/realtek/rtw89/mac.c +++ b/drivers/net/wireless/realtek/rtw89/mac.c @@ -7,6 +7,7 @@ #include "debug.h" #include "fw.h" #include "mac.h" +#include "pci.h" #include "ps.h" #include "reg.h" #include "util.h" @@ -274,106 +275,163 @@ static void rtw89_mac_dump_l0_to_l1(struct rtw89_dev *rtwdev, } } -static void rtw89_mac_dump_err_status(struct rtw89_dev *rtwdev, - enum mac_ax_err_info err) +static void rtw89_mac_dump_dmac_err_status(struct rtw89_dev *rtwdev) { - u32 dmac_err, cmac_err; + const struct rtw89_chip_info *chip = rtwdev->chip; + u32 dmac_err; + int i, ret; - if (err != MAC_AX_ERR_L1_ERR_DMAC && - err != MAC_AX_ERR_L0_PROMOTE_TO_L1 && - err != MAC_AX_ERR_L0_ERR_CMAC0 && - err != MAC_AX_ERR_L0_ERR_CMAC1) + ret = rtw89_mac_check_mac_en(rtwdev, 0, RTW89_DMAC_SEL); + if (ret) { + rtw89_warn(rtwdev, "[DMAC] : DMAC not enabled\n"); return; + } - rtw89_info(rtwdev, "--->\nerr=0x%x\n", err); - rtw89_info(rtwdev, "R_AX_SER_DBG_INFO =0x%08x\n", - rtw89_read32(rtwdev, R_AX_SER_DBG_INFO)); - - cmac_err = rtw89_read32(rtwdev, R_AX_CMAC_ERR_ISR); - rtw89_info(rtwdev, "R_AX_CMAC_ERR_ISR =0x%08x\n", cmac_err); dmac_err = rtw89_read32(rtwdev, R_AX_DMAC_ERR_ISR); - rtw89_info(rtwdev, "R_AX_DMAC_ERR_ISR =0x%08x\n", dmac_err); + rtw89_info(rtwdev, "R_AX_DMAC_ERR_ISR=0x%08x\n", dmac_err); + rtw89_info(rtwdev, "R_AX_DMAC_ERR_IMR=0x%08x\n", + rtw89_read32(rtwdev, R_AX_DMAC_ERR_IMR)); if (dmac_err) { - rtw89_info(rtwdev, "R_AX_WDE_ERR_FLAG_CFG =0x%08x ", - rtw89_read32(rtwdev, R_AX_WDE_ERR_FLAG_CFG)); - rtw89_info(rtwdev, "R_AX_PLE_ERR_FLAG_CFG =0x%08x\n", - rtw89_read32(rtwdev, R_AX_PLE_ERR_FLAG_CFG)); + rtw89_info(rtwdev, "R_AX_WDE_ERR_FLAG_CFG=0x%08x\n", + rtw89_read32(rtwdev, R_AX_WDE_ERR_FLAG_CFG_NUM1)); + rtw89_info(rtwdev, "R_AX_PLE_ERR_FLAG_CFG=0x%08x\n", + rtw89_read32(rtwdev, R_AX_PLE_ERR_FLAG_CFG_NUM1)); + if (chip->chip_id == RTL8852C) { + rtw89_info(rtwdev, "R_AX_PLE_ERRFLAG_MSG=0x%08x\n", + rtw89_read32(rtwdev, R_AX_PLE_ERRFLAG_MSG)); + rtw89_info(rtwdev, "R_AX_WDE_ERRFLAG_MSG=0x%08x\n", + rtw89_read32(rtwdev, R_AX_WDE_ERRFLAG_MSG)); + rtw89_info(rtwdev, "R_AX_PLE_DBGERR_LOCKEN=0x%08x\n", + rtw89_read32(rtwdev, R_AX_PLE_DBGERR_LOCKEN)); + rtw89_info(rtwdev, "R_AX_PLE_DBGERR_STS=0x%08x\n", + rtw89_read32(rtwdev, R_AX_PLE_DBGERR_STS)); + } } if (dmac_err & B_AX_WDRLS_ERR_FLAG) { - rtw89_info(rtwdev, "R_AX_WDRLS_ERR_IMR =0x%08x ", + rtw89_info(rtwdev, "R_AX_WDRLS_ERR_IMR=0x%08x\n", rtw89_read32(rtwdev, R_AX_WDRLS_ERR_IMR)); - rtw89_info(rtwdev, "R_AX_WDRLS_ERR_ISR =0x%08x\n", + rtw89_info(rtwdev, "R_AX_WDRLS_ERR_ISR=0x%08x\n", rtw89_read32(rtwdev, R_AX_WDRLS_ERR_ISR)); + if (chip->chip_id == RTL8852C) + rtw89_info(rtwdev, "R_AX_RPQ_RXBD_IDX=0x%08x\n", + rtw89_read32(rtwdev, R_AX_RPQ_RXBD_IDX_V1)); + else + rtw89_info(rtwdev, "R_AX_RPQ_RXBD_IDX=0x%08x\n", + rtw89_read32(rtwdev, R_AX_RPQ_RXBD_IDX)); } if (dmac_err & B_AX_WSEC_ERR_FLAG) { - rtw89_info(rtwdev, "R_AX_SEC_ERR_IMR_ISR =0x%08x\n", - rtw89_read32(rtwdev, R_AX_SEC_DEBUG)); - rtw89_info(rtwdev, "SEC_local_Register 0x9D00 =0x%08x\n", - rtw89_read32(rtwdev, R_AX_SEC_ENG_CTRL)); - rtw89_info(rtwdev, "SEC_local_Register 0x9D04 =0x%08x\n", - rtw89_read32(rtwdev, R_AX_SEC_MPDU_PROC)); - rtw89_info(rtwdev, "SEC_local_Register 0x9D10 =0x%08x\n", - rtw89_read32(rtwdev, R_AX_SEC_CAM_ACCESS)); - rtw89_info(rtwdev, "SEC_local_Register 0x9D14 =0x%08x\n", - rtw89_read32(rtwdev, R_AX_SEC_CAM_RDATA)); - rtw89_info(rtwdev, "SEC_local_Register 0x9D18 =0x%08x\n", - rtw89_read32(rtwdev, R_AX_SEC_CAM_WDATA)); - rtw89_info(rtwdev, "SEC_local_Register 0x9D20 =0x%08x\n", - rtw89_read32(rtwdev, R_AX_SEC_TX_DEBUG)); - rtw89_info(rtwdev, "SEC_local_Register 0x9D24 =0x%08x\n", - rtw89_read32(rtwdev, R_AX_SEC_RX_DEBUG)); - rtw89_info(rtwdev, "SEC_local_Register 0x9D28 =0x%08x\n", - rtw89_read32(rtwdev, R_AX_SEC_TRX_PKT_CNT)); - rtw89_info(rtwdev, "SEC_local_Register 0x9D2C =0x%08x\n", - rtw89_read32(rtwdev, R_AX_SEC_TRX_BLK_CNT)); + if (chip->chip_id == RTL8852C) { + rtw89_info(rtwdev, "R_AX_SEC_ERR_IMR=0x%08x\n", + rtw89_read32(rtwdev, R_AX_SEC_ERROR_FLAG_IMR)); + rtw89_info(rtwdev, "R_AX_SEC_ERR_ISR=0x%08x\n", + rtw89_read32(rtwdev, R_AX_SEC_ERROR_FLAG)); + rtw89_info(rtwdev, "R_AX_SEC_ENG_CTRL=0x%08x\n", + rtw89_read32(rtwdev, R_AX_SEC_ENG_CTRL)); + rtw89_info(rtwdev, "R_AX_SEC_MPDU_PROC=0x%08x\n", + rtw89_read32(rtwdev, R_AX_SEC_MPDU_PROC)); + rtw89_info(rtwdev, "R_AX_SEC_CAM_ACCESS=0x%08x\n", + rtw89_read32(rtwdev, R_AX_SEC_CAM_ACCESS)); + rtw89_info(rtwdev, "R_AX_SEC_CAM_RDATA=0x%08x\n", + rtw89_read32(rtwdev, R_AX_SEC_CAM_RDATA)); + rtw89_info(rtwdev, "R_AX_SEC_DEBUG1=0x%08x\n", + rtw89_read32(rtwdev, R_AX_SEC_DEBUG1)); + rtw89_info(rtwdev, "R_AX_SEC_TX_DEBUG=0x%08x\n", + rtw89_read32(rtwdev, R_AX_SEC_TX_DEBUG)); + rtw89_info(rtwdev, "R_AX_SEC_RX_DEBUG=0x%08x\n", + rtw89_read32(rtwdev, R_AX_SEC_RX_DEBUG)); + + rtw89_write32_mask(rtwdev, R_AX_DBG_CTRL, + B_AX_DBG_SEL0, 0x8B); + rtw89_write32_mask(rtwdev, R_AX_DBG_CTRL, + B_AX_DBG_SEL1, 0x8B); + rtw89_write32_mask(rtwdev, R_AX_SYS_STATUS1, + B_AX_SEL_0XC0_MASK, 1); + for (i = 0; i < 0x10; i++) { + rtw89_write32_mask(rtwdev, R_AX_SEC_ENG_CTRL, + B_AX_SEC_DBG_PORT_FIELD_MASK, i); + rtw89_info(rtwdev, "sel=%x,R_AX_SEC_DEBUG2=0x%08x\n", + i, rtw89_read32(rtwdev, R_AX_SEC_DEBUG2)); + } + } else { + rtw89_info(rtwdev, "R_AX_SEC_ERR_IMR_ISR=0x%08x\n", + rtw89_read32(rtwdev, R_AX_SEC_DEBUG)); + rtw89_info(rtwdev, "R_AX_SEC_ENG_CTRL=0x%08x\n", + rtw89_read32(rtwdev, R_AX_SEC_ENG_CTRL)); + rtw89_info(rtwdev, "R_AX_SEC_MPDU_PROC=0x%08x\n", + rtw89_read32(rtwdev, R_AX_SEC_MPDU_PROC)); + rtw89_info(rtwdev, "R_AX_SEC_CAM_ACCESS=0x%08x\n", + rtw89_read32(rtwdev, R_AX_SEC_CAM_ACCESS)); + rtw89_info(rtwdev, "R_AX_SEC_CAM_RDATA=0x%08x\n", + rtw89_read32(rtwdev, R_AX_SEC_CAM_RDATA)); + rtw89_info(rtwdev, "R_AX_SEC_CAM_WDATA=0x%08x\n", + rtw89_read32(rtwdev, R_AX_SEC_CAM_WDATA)); + rtw89_info(rtwdev, "R_AX_SEC_TX_DEBUG=0x%08x\n", + rtw89_read32(rtwdev, R_AX_SEC_TX_DEBUG)); + rtw89_info(rtwdev, "R_AX_SEC_RX_DEBUG=0x%08x\n", + rtw89_read32(rtwdev, R_AX_SEC_RX_DEBUG)); + rtw89_info(rtwdev, "R_AX_SEC_TRX_PKT_CNT=0x%08x\n", + rtw89_read32(rtwdev, R_AX_SEC_TRX_PKT_CNT)); + rtw89_info(rtwdev, "R_AX_SEC_TRX_BLK_CNT=0x%08x\n", + rtw89_read32(rtwdev, R_AX_SEC_TRX_BLK_CNT)); + } } if (dmac_err & B_AX_MPDU_ERR_FLAG) { - rtw89_info(rtwdev, "R_AX_MPDU_TX_ERR_IMR =0x%08x ", + rtw89_info(rtwdev, "R_AX_MPDU_TX_ERR_IMR=0x%08x\n", rtw89_read32(rtwdev, R_AX_MPDU_TX_ERR_IMR)); - rtw89_info(rtwdev, "R_AX_MPDU_TX_ERR_ISR =0x%08x\n", + rtw89_info(rtwdev, "R_AX_MPDU_TX_ERR_ISR=0x%08x\n", rtw89_read32(rtwdev, R_AX_MPDU_TX_ERR_ISR)); - rtw89_info(rtwdev, "R_AX_MPDU_RX_ERR_IMR =0x%08x ", + rtw89_info(rtwdev, "R_AX_MPDU_RX_ERR_IMR=0x%08x\n", rtw89_read32(rtwdev, R_AX_MPDU_RX_ERR_IMR)); - rtw89_info(rtwdev, "R_AX_MPDU_RX_ERR_ISR =0x%08x\n", + rtw89_info(rtwdev, "R_AX_MPDU_RX_ERR_ISR=0x%08x\n", rtw89_read32(rtwdev, R_AX_MPDU_RX_ERR_ISR)); } if (dmac_err & B_AX_STA_SCHEDULER_ERR_FLAG) { - rtw89_info(rtwdev, "R_AX_STA_SCHEDULER_ERR_IMR =0x%08x ", + rtw89_info(rtwdev, "R_AX_STA_SCHEDULER_ERR_IMR=0x%08x\n", rtw89_read32(rtwdev, R_AX_STA_SCHEDULER_ERR_IMR)); - rtw89_info(rtwdev, "R_AX_STA_SCHEDULER_ERR_ISR= 0x%08x\n", + rtw89_info(rtwdev, "R_AX_STA_SCHEDULER_ERR_ISR=0x%08x\n", rtw89_read32(rtwdev, R_AX_STA_SCHEDULER_ERR_ISR)); } if (dmac_err & B_AX_WDE_DLE_ERR_FLAG) { - rtw89_info(rtwdev, "R_AX_WDE_ERR_IMR=0x%08x ", + rtw89_info(rtwdev, "R_AX_WDE_ERR_IMR=0x%08x\n", rtw89_read32(rtwdev, R_AX_WDE_ERR_IMR)); rtw89_info(rtwdev, "R_AX_WDE_ERR_ISR=0x%08x\n", rtw89_read32(rtwdev, R_AX_WDE_ERR_ISR)); - rtw89_info(rtwdev, "R_AX_PLE_ERR_IMR=0x%08x ", + rtw89_info(rtwdev, "R_AX_PLE_ERR_IMR=0x%08x\n", rtw89_read32(rtwdev, R_AX_PLE_ERR_IMR)); rtw89_info(rtwdev, "R_AX_PLE_ERR_FLAG_ISR=0x%08x\n", rtw89_read32(rtwdev, R_AX_PLE_ERR_FLAG_ISR)); - dump_err_status_dispatcher(rtwdev); } if (dmac_err & B_AX_TXPKTCTRL_ERR_FLAG) { - rtw89_info(rtwdev, "R_AX_TXPKTCTL_ERR_IMR_ISR=0x%08x\n", - rtw89_read32(rtwdev, R_AX_TXPKTCTL_ERR_IMR_ISR)); - rtw89_info(rtwdev, "R_AX_TXPKTCTL_ERR_IMR_ISR_B1=0x%08x\n", - rtw89_read32(rtwdev, R_AX_TXPKTCTL_ERR_IMR_ISR_B1)); + if (chip->chip_id == RTL8852C) { + rtw89_info(rtwdev, "R_AX_TXPKTCTL_B0_ERRFLAG_IMR=0x%08x\n", + rtw89_read32(rtwdev, R_AX_TXPKTCTL_B0_ERRFLAG_IMR)); + rtw89_info(rtwdev, "R_AX_TXPKTCTL_B0_ERRFLAG_ISR=0x%08x\n", + rtw89_read32(rtwdev, R_AX_TXPKTCTL_B0_ERRFLAG_ISR)); + rtw89_info(rtwdev, "R_AX_TXPKTCTL_B1_ERRFLAG_IMR=0x%08x\n", + rtw89_read32(rtwdev, R_AX_TXPKTCTL_B1_ERRFLAG_IMR)); + rtw89_info(rtwdev, "R_AX_TXPKTCTL_B1_ERRFLAG_ISR=0x%08x\n", + rtw89_read32(rtwdev, R_AX_TXPKTCTL_B1_ERRFLAG_ISR)); + } else { + rtw89_info(rtwdev, "R_AX_TXPKTCTL_ERR_IMR_ISR=0x%08x\n", + rtw89_read32(rtwdev, R_AX_TXPKTCTL_ERR_IMR_ISR)); + rtw89_info(rtwdev, "R_AX_TXPKTCTL_ERR_IMR_ISR_B1=0x%08x\n", + rtw89_read32(rtwdev, R_AX_TXPKTCTL_ERR_IMR_ISR_B1)); + } } if (dmac_err & B_AX_PLE_DLE_ERR_FLAG) { - rtw89_info(rtwdev, "R_AX_WDE_ERR_IMR=0x%08x ", + rtw89_info(rtwdev, "R_AX_WDE_ERR_IMR=0x%08x\n", rtw89_read32(rtwdev, R_AX_WDE_ERR_IMR)); rtw89_info(rtwdev, "R_AX_WDE_ERR_ISR=0x%08x\n", rtw89_read32(rtwdev, R_AX_WDE_ERR_ISR)); - rtw89_info(rtwdev, "R_AX_PLE_ERR_IMR=0x%08x ", + rtw89_info(rtwdev, "R_AX_PLE_ERR_IMR=0x%08x\n", rtw89_read32(rtwdev, R_AX_PLE_ERR_IMR)); rtw89_info(rtwdev, "R_AX_PLE_ERR_FLAG_ISR=0x%08x\n", rtw89_read32(rtwdev, R_AX_PLE_ERR_FLAG_ISR)); @@ -393,86 +451,190 @@ static void rtw89_mac_dump_err_status(struct rtw89_dev *rtwdev, rtw89_read32(rtwdev, R_AX_PL_CPUQ_OP_2)); rtw89_info(rtwdev, "R_AX_PL_CPUQ_OP_STATUS=0x%08x\n", rtw89_read32(rtwdev, R_AX_PL_CPUQ_OP_STATUS)); - rtw89_info(rtwdev, "R_AX_RXDMA_PKT_INFO_0=0x%08x\n", - rtw89_read32(rtwdev, R_AX_RXDMA_PKT_INFO_0)); - rtw89_info(rtwdev, "R_AX_RXDMA_PKT_INFO_1=0x%08x\n", - rtw89_read32(rtwdev, R_AX_RXDMA_PKT_INFO_1)); - rtw89_info(rtwdev, "R_AX_RXDMA_PKT_INFO_2=0x%08x\n", - rtw89_read32(rtwdev, R_AX_RXDMA_PKT_INFO_2)); - dump_err_status_dispatcher(rtwdev); + if (chip->chip_id == RTL8852C) { + rtw89_info(rtwdev, "R_AX_RX_CTRL0=0x%08x\n", + rtw89_read32(rtwdev, R_AX_RX_CTRL0)); + rtw89_info(rtwdev, "R_AX_RX_CTRL1=0x%08x\n", + rtw89_read32(rtwdev, R_AX_RX_CTRL1)); + rtw89_info(rtwdev, "R_AX_RX_CTRL2=0x%08x\n", + rtw89_read32(rtwdev, R_AX_RX_CTRL2)); + } else { + rtw89_info(rtwdev, "R_AX_RXDMA_PKT_INFO_0=0x%08x\n", + rtw89_read32(rtwdev, R_AX_RXDMA_PKT_INFO_0)); + rtw89_info(rtwdev, "R_AX_RXDMA_PKT_INFO_1=0x%08x\n", + rtw89_read32(rtwdev, R_AX_RXDMA_PKT_INFO_1)); + rtw89_info(rtwdev, "R_AX_RXDMA_PKT_INFO_2=0x%08x\n", + rtw89_read32(rtwdev, R_AX_RXDMA_PKT_INFO_2)); + } } if (dmac_err & B_AX_PKTIN_ERR_FLAG) { - rtw89_info(rtwdev, "R_AX_PKTIN_ERR_IMR =0x%08x ", + rtw89_info(rtwdev, "R_AX_PKTIN_ERR_IMR=0x%08x\n", rtw89_read32(rtwdev, R_AX_PKTIN_ERR_IMR)); - rtw89_info(rtwdev, "R_AX_PKTIN_ERR_ISR =0x%08x\n", - rtw89_read32(rtwdev, R_AX_PKTIN_ERR_ISR)); - rtw89_info(rtwdev, "R_AX_PKTIN_ERR_IMR =0x%08x ", - rtw89_read32(rtwdev, R_AX_PKTIN_ERR_IMR)); - rtw89_info(rtwdev, "R_AX_PKTIN_ERR_ISR =0x%08x\n", + rtw89_info(rtwdev, "R_AX_PKTIN_ERR_ISR=0x%08x\n", rtw89_read32(rtwdev, R_AX_PKTIN_ERR_ISR)); } - if (dmac_err & B_AX_DISPATCH_ERR_FLAG) - dump_err_status_dispatcher(rtwdev); - - if (dmac_err & B_AX_DLE_CPUIO_ERR_FLAG) { - rtw89_info(rtwdev, "R_AX_CPUIO_ERR_IMR=0x%08x ", - rtw89_read32(rtwdev, R_AX_CPUIO_ERR_IMR)); - rtw89_info(rtwdev, "R_AX_CPUIO_ERR_ISR=0x%08x\n", - rtw89_read32(rtwdev, R_AX_CPUIO_ERR_ISR)); + if (dmac_err & B_AX_DISPATCH_ERR_FLAG) { + rtw89_info(rtwdev, "R_AX_HOST_DISPATCHER_ERR_IMR=0x%08x\n", + rtw89_read32(rtwdev, R_AX_HOST_DISPATCHER_ERR_IMR)); + rtw89_info(rtwdev, "R_AX_HOST_DISPATCHER_ERR_ISR=0x%08x\n", + rtw89_read32(rtwdev, R_AX_HOST_DISPATCHER_ERR_ISR)); + rtw89_info(rtwdev, "R_AX_CPU_DISPATCHER_ERR_IMR=0x%08x\n", + rtw89_read32(rtwdev, R_AX_CPU_DISPATCHER_ERR_IMR)); + rtw89_info(rtwdev, "R_AX_CPU_DISPATCHER_ERR_ISR=0x%08x\n", + rtw89_read32(rtwdev, R_AX_CPU_DISPATCHER_ERR_ISR)); + rtw89_info(rtwdev, "R_AX_OTHER_DISPATCHER_ERR_IMR=0x%08x\n", + rtw89_read32(rtwdev, R_AX_OTHER_DISPATCHER_ERR_IMR)); + rtw89_info(rtwdev, "R_AX_OTHER_DISPATCHER_ERR_ISR=0x%08x\n", + rtw89_read32(rtwdev, R_AX_OTHER_DISPATCHER_ERR_ISR)); } - if (dmac_err & BIT(11)) { - rtw89_info(rtwdev, "R_AX_BBRPT_COM_ERR_IMR_ISR=0x%08x\n", - rtw89_read32(rtwdev, R_AX_BBRPT_COM_ERR_IMR_ISR)); + if (dmac_err & B_AX_BBRPT_ERR_FLAG) { + if (chip->chip_id == RTL8852C) { + rtw89_info(rtwdev, "R_AX_BBRPT_COM_ERR_IMR=0x%08x\n", + rtw89_read32(rtwdev, R_AX_BBRPT_COM_ERR_IMR)); + rtw89_info(rtwdev, "R_AX_BBRPT_COM_ERR_ISR=0x%08x\n", + rtw89_read32(rtwdev, R_AX_BBRPT_COM_ERR_ISR)); + rtw89_info(rtwdev, "R_AX_BBRPT_CHINFO_ERR_ISR=0x%08x\n", + rtw89_read32(rtwdev, R_AX_BBRPT_CHINFO_ERR_ISR)); + rtw89_info(rtwdev, "R_AX_BBRPT_CHINFO_ERR_IMR=0x%08x\n", + rtw89_read32(rtwdev, R_AX_BBRPT_CHINFO_ERR_IMR)); + rtw89_info(rtwdev, "R_AX_BBRPT_DFS_ERR_IMR=0x%08x\n", + rtw89_read32(rtwdev, R_AX_BBRPT_DFS_ERR_IMR)); + rtw89_info(rtwdev, "R_AX_BBRPT_DFS_ERR_ISR=0x%08x\n", + rtw89_read32(rtwdev, R_AX_BBRPT_DFS_ERR_ISR)); + } else { + rtw89_info(rtwdev, "R_AX_BBRPT_COM_ERR_IMR_ISR=0x%08x\n", + rtw89_read32(rtwdev, R_AX_BBRPT_COM_ERR_IMR_ISR)); + rtw89_info(rtwdev, "R_AX_BBRPT_CHINFO_ERR_ISR=0x%08x\n", + rtw89_read32(rtwdev, R_AX_BBRPT_CHINFO_ERR_ISR)); + rtw89_info(rtwdev, "R_AX_BBRPT_CHINFO_ERR_IMR=0x%08x\n", + rtw89_read32(rtwdev, R_AX_BBRPT_CHINFO_ERR_IMR)); + rtw89_info(rtwdev, "R_AX_BBRPT_DFS_ERR_IMR=0x%08x\n", + rtw89_read32(rtwdev, R_AX_BBRPT_DFS_ERR_IMR)); + rtw89_info(rtwdev, "R_AX_BBRPT_DFS_ERR_ISR=0x%08x\n", + rtw89_read32(rtwdev, R_AX_BBRPT_DFS_ERR_ISR)); + } } + if (dmac_err & B_AX_HAXIDMA_ERR_FLAG && chip->chip_id == RTL8852C) { + rtw89_info(rtwdev, "R_AX_HAXIDMA_ERR_IMR=0x%08x\n", + rtw89_read32(rtwdev, R_AX_HAXI_IDCT_MSK)); + rtw89_info(rtwdev, "R_AX_HAXIDMA_ERR_ISR=0x%08x\n", + rtw89_read32(rtwdev, R_AX_HAXI_IDCT)); + } +} + +static void rtw89_mac_dump_cmac_err_status(struct rtw89_dev *rtwdev, + u8 band) +{ + const struct rtw89_chip_info *chip = rtwdev->chip; + u32 offset = 0; + u32 cmac_err; + int ret; + + ret = rtw89_mac_check_mac_en(rtwdev, band, RTW89_CMAC_SEL); + if (ret) { + if (band) + rtw89_warn(rtwdev, "[CMAC] : CMAC1 not enabled\n"); + else + rtw89_warn(rtwdev, "[CMAC] : CMAC0 not enabled\n"); + return; + } + + if (band) + offset = RTW89_MAC_AX_BAND_REG_OFFSET; + + cmac_err = rtw89_read32(rtwdev, R_AX_CMAC_ERR_ISR + offset); + rtw89_info(rtwdev, "R_AX_CMAC_ERR_ISR [%d]=0x%08x\n", band, + rtw89_read32(rtwdev, R_AX_CMAC_ERR_ISR + offset)); + rtw89_info(rtwdev, "R_AX_CMAC_FUNC_EN [%d]=0x%08x\n", band, + rtw89_read32(rtwdev, R_AX_CMAC_FUNC_EN + offset)); + rtw89_info(rtwdev, "R_AX_CK_EN [%d]=0x%08x\n", band, + rtw89_read32(rtwdev, R_AX_CK_EN + offset)); + if (cmac_err & B_AX_SCHEDULE_TOP_ERR_IND) { - rtw89_info(rtwdev, "R_AX_SCHEDULE_ERR_IMR=0x%08x ", - rtw89_read32(rtwdev, R_AX_SCHEDULE_ERR_IMR)); - rtw89_info(rtwdev, "R_AX_SCHEDULE_ERR_ISR=0x%04x\n", - rtw89_read16(rtwdev, R_AX_SCHEDULE_ERR_ISR)); + rtw89_info(rtwdev, "R_AX_SCHEDULE_ERR_IMR [%d]=0x%08x\n", band, + rtw89_read32(rtwdev, R_AX_SCHEDULE_ERR_IMR + offset)); + rtw89_info(rtwdev, "R_AX_SCHEDULE_ERR_ISR [%d]=0x%08x\n", band, + rtw89_read32(rtwdev, R_AX_SCHEDULE_ERR_ISR + offset)); } if (cmac_err & B_AX_PTCL_TOP_ERR_IND) { - rtw89_info(rtwdev, "R_AX_PTCL_IMR0=0x%08x ", - rtw89_read32(rtwdev, R_AX_PTCL_IMR0)); - rtw89_info(rtwdev, "R_AX_PTCL_ISR0=0x%08x\n", - rtw89_read32(rtwdev, R_AX_PTCL_ISR0)); + rtw89_info(rtwdev, "R_AX_PTCL_IMR0 [%d]=0x%08x\n", band, + rtw89_read32(rtwdev, R_AX_PTCL_IMR0 + offset)); + rtw89_info(rtwdev, "R_AX_PTCL_ISR0 [%d]=0x%08x\n", band, + rtw89_read32(rtwdev, R_AX_PTCL_ISR0 + offset)); } if (cmac_err & B_AX_DMA_TOP_ERR_IND) { - rtw89_info(rtwdev, "R_AX_DLE_CTRL=0x%08x\n", - rtw89_read32(rtwdev, R_AX_DLE_CTRL)); + if (chip->chip_id == RTL8852C) { + rtw89_info(rtwdev, "R_AX_RX_ERR_FLAG [%d]=0x%08x\n", band, + rtw89_read32(rtwdev, R_AX_RX_ERR_FLAG + offset)); + rtw89_info(rtwdev, "R_AX_RX_ERR_FLAG_IMR [%d]=0x%08x\n", band, + rtw89_read32(rtwdev, R_AX_RX_ERR_FLAG_IMR + offset)); + } else { + rtw89_info(rtwdev, "R_AX_DLE_CTRL [%d]=0x%08x\n", band, + rtw89_read32(rtwdev, R_AX_DLE_CTRL + offset)); + } } - if (cmac_err & B_AX_PHYINTF_ERR_IND) { - rtw89_info(rtwdev, "R_AX_PHYINFO_ERR_IMR=0x%08x\n", - rtw89_read32(rtwdev, R_AX_PHYINFO_ERR_IMR)); + if (cmac_err & B_AX_DMA_TOP_ERR_IND || cmac_err & B_AX_WMAC_RX_ERR_IND) { + if (chip->chip_id == RTL8852C) { + rtw89_info(rtwdev, "R_AX_PHYINFO_ERR_ISR [%d]=0x%08x\n", band, + rtw89_read32(rtwdev, R_AX_PHYINFO_ERR_ISR + offset)); + rtw89_info(rtwdev, "R_AX_PHYINFO_ERR_IMR [%d]=0x%08x\n", band, + rtw89_read32(rtwdev, R_AX_PHYINFO_ERR_IMR + offset)); + } else { + rtw89_info(rtwdev, "R_AX_PHYINFO_ERR_IMR [%d]=0x%08x\n", band, + rtw89_read32(rtwdev, R_AX_PHYINFO_ERR_IMR + offset)); + } } if (cmac_err & B_AX_TXPWR_CTRL_ERR_IND) { - rtw89_info(rtwdev, "R_AX_TXPWR_IMR=0x%08x ", - rtw89_read32(rtwdev, R_AX_TXPWR_IMR)); - rtw89_info(rtwdev, "R_AX_TXPWR_ISR=0x%08x\n", - rtw89_read32(rtwdev, R_AX_TXPWR_ISR)); - } - - if (cmac_err & B_AX_WMAC_RX_ERR_IND) { - rtw89_info(rtwdev, "R_AX_DBGSEL_TRXPTCL=0x%08x ", - rtw89_read32(rtwdev, R_AX_DBGSEL_TRXPTCL)); - rtw89_info(rtwdev, "R_AX_PHYINFO_ERR_ISR=0x%08x\n", - rtw89_read32(rtwdev, R_AX_PHYINFO_ERR_ISR)); + rtw89_info(rtwdev, "R_AX_TXPWR_IMR [%d]=0x%08x\n", band, + rtw89_read32(rtwdev, R_AX_TXPWR_IMR + offset)); + rtw89_info(rtwdev, "R_AX_TXPWR_ISR [%d]=0x%08x\n", band, + rtw89_read32(rtwdev, R_AX_TXPWR_ISR + offset)); } if (cmac_err & B_AX_WMAC_TX_ERR_IND) { - rtw89_info(rtwdev, "R_AX_TMAC_ERR_IMR_ISR=0x%08x ", - rtw89_read32(rtwdev, R_AX_TMAC_ERR_IMR_ISR)); - rtw89_info(rtwdev, "R_AX_DBGSEL_TRXPTCL=0x%08x\n", - rtw89_read32(rtwdev, R_AX_DBGSEL_TRXPTCL)); + if (chip->chip_id == RTL8852C) { + rtw89_info(rtwdev, "R_AX_TRXPTCL_ERROR_INDICA [%d]=0x%08x\n", band, + rtw89_read32(rtwdev, R_AX_TRXPTCL_ERROR_INDICA + offset)); + rtw89_info(rtwdev, "R_AX_TRXPTCL_ERROR_INDICA_MASK [%d]=0x%08x\n", band, + rtw89_read32(rtwdev, R_AX_TRXPTCL_ERROR_INDICA_MASK + offset)); + } else { + rtw89_info(rtwdev, "R_AX_TMAC_ERR_IMR_ISR [%d]=0x%08x\n", band, + rtw89_read32(rtwdev, R_AX_TMAC_ERR_IMR_ISR + offset)); + } + rtw89_info(rtwdev, "R_AX_DBGSEL_TRXPTCL [%d]=0x%08x\n", band, + rtw89_read32(rtwdev, R_AX_DBGSEL_TRXPTCL + offset)); } + rtw89_info(rtwdev, "R_AX_CMAC_ERR_IMR [%d]=0x%08x\n", band, + rtw89_read32(rtwdev, R_AX_CMAC_ERR_IMR + offset)); +} + +static void rtw89_mac_dump_err_status(struct rtw89_dev *rtwdev, + enum mac_ax_err_info err) +{ + if (err != MAC_AX_ERR_L1_ERR_DMAC && + err != MAC_AX_ERR_L0_PROMOTE_TO_L1 && + err != MAC_AX_ERR_L0_ERR_CMAC0 && + err != MAC_AX_ERR_L0_ERR_CMAC1) + return; + + rtw89_info(rtwdev, "--->\nerr=0x%x\n", err); + rtw89_info(rtwdev, "R_AX_SER_DBG_INFO =0x%08x\n", + rtw89_read32(rtwdev, R_AX_SER_DBG_INFO)); + + rtw89_mac_dump_dmac_err_status(rtwdev); + rtw89_mac_dump_cmac_err_status(rtwdev, RTW89_MAC_0); + if (rtwdev->dbcc_en) + rtw89_mac_dump_cmac_err_status(rtwdev, RTW89_MAC_1); + rtwdev->hci.ops->dump_err_status(rtwdev); if (err == MAC_AX_ERR_L0_PROMOTE_TO_L1) diff --git a/drivers/net/wireless/realtek/rtw89/mac.h b/drivers/net/wireless/realtek/rtw89/mac.h index 46d9cad2b5ec..045e8ec61a41 100644 --- a/drivers/net/wireless/realtek/rtw89/mac.h +++ b/drivers/net/wireless/realtek/rtw89/mac.h @@ -440,6 +440,7 @@ enum rtw89_mac_bf_rrsc_rate { #define ACCESS_CMAC(_addr) \ ({typeof(_addr) __addr = (_addr); \ __addr >= R_AX_CMAC_REG_START && __addr <= R_AX_CMAC_REG_END; }) +#define RTW89_MAC_AX_BAND_REG_OFFSET 0x2000 #define PTCL_IDLE_POLL_CNT 10000 #define SW_CVR_DUR_US 8 diff --git a/drivers/net/wireless/realtek/rtw89/pci.h b/drivers/net/wireless/realtek/rtw89/pci.h index 179740607778..7d033501d4d9 100644 --- a/drivers/net/wireless/realtek/rtw89/pci.h +++ b/drivers/net/wireless/realtek/rtw89/pci.h @@ -202,6 +202,18 @@ #define B_AX_RXP1DMA_INT BIT(1) #define B_AX_RXDMA_INT BIT(0) +#define R_AX_HAXI_IDCT_MSK 0x10B8 +#define B_AX_TXBD_LEN0_ERR_IDCT_MSK BIT(3) +#define B_AX_TXBD_4KBOUND_ERR_IDCT_MSK BIT(2) +#define B_AX_RXMDA_STUCK_IDCT_MSK BIT(1) +#define B_AX_TXMDA_STUCK_IDCT_MSK BIT(0) + +#define R_AX_HAXI_IDCT 0x10BC +#define B_AX_TXBD_LEN0_ERR_IDCT BIT(3) +#define B_AX_TXBD_4KBOUND_ERR_IDCT BIT(2) +#define B_AX_RXMDA_STUCK_IDCT BIT(1) +#define B_AX_TXMDA_STUCK_IDCT BIT(0) + #define R_AX_HAXI_HIMR10 0x11E0 #define B_AX_TXDMA_CH11_INT_EN_V1 BIT(1) #define B_AX_TXDMA_CH10_INT_EN_V1 BIT(0) diff --git a/drivers/net/wireless/realtek/rtw89/reg.h b/drivers/net/wireless/realtek/rtw89/reg.h index 8a1cb8f4aa16..dbe06542f443 100644 --- a/drivers/net/wireless/realtek/rtw89/reg.h +++ b/drivers/net/wireless/realtek/rtw89/reg.h @@ -326,8 +326,7 @@ #define R_AX_PCIE_DBG_CTRL 0x11C0 #define B_AX_DBG_DUMMY_MASK GENMASK(23, 16) -#define B_AX_DBG_SEL_MASK GENMASK(15, 13) -#define B_AX_PCIE_DBG_SEL BIT(12) +#define B_AX_PCIE_DBG_SEL_MASK GENMASK(15, 13) #define B_AX_MRD_TIMEOUT_EN BIT(10) #define B_AX_ASFF_FULL_NO_STK BIT(1) #define B_AX_EN_STUCK_DBG BIT(0) @@ -574,6 +573,10 @@ #define DMAC_ERR_IMR_DIS 0 #define R_AX_DMAC_ERR_ISR 0x8524 +#define B_AX_HAXIDMA_ERR_FLAG BIT(14) +#define B_AX_PAXIDMA_ERR_FLAG BIT(13) +#define B_AX_HCI_BUF_ERR_FLAG BIT(12) +#define B_AX_BBRPT_ERR_FLAG BIT(11) #define B_AX_DLE_CPUIO_ERR_FLAG BIT(10) #define B_AX_APB_BRIDGE_ERR_FLAG BIT(9) #define B_AX_DISPATCH_ERR_FLAG BIT(8) @@ -1041,7 +1044,13 @@ #define R_AX_WDE_ERRFLAG_MSG 0x8C30 #define B_AX_WDE_ERR_FLAG_MSG_MASK GENMASK(31, 0) -#define R_AX_WDE_ERR_FLAG_CFG 0x8C34 +#define R_AX_WDE_ERR_FLAG_CFG_NUM1 0x8C34 +#define B_AX_WDE_ERR_FLAG_NUM1_VLD BIT(31) +#define B_AX_WDE_ERR_FLAG_NUM1_MSTIDX_MASK GENMASK(27, 24) +#define B_AX_WDE_ERR_FLAG_NUM1_ISRIDX_MASK GENMASK(20, 16) +#define B_AX_WDE_DATCHN_FRZTMR_MODE BIT(2) +#define B_AX_WDE_QUEMGN_FRZTMR_MODE BIT(1) +#define B_AX_WDE_BUFMGN_FRZTMR_MODE BIT(0) #define R_AX_WDE_ERR_IMR 0x8C38 #define B_AX_WDE_DATCHN_RRDY_ERR_INT_EN BIT(27) @@ -1225,7 +1234,59 @@ #define B_AX_PLE_START_BOUND_MASK GENMASK(13, 8) #define B_AX_PLE_PAGE_SEL_MASK GENMASK(1, 0) #define B_AX_PLE_FREE_PAGE_NUM_MASK GENMASK(28, 16) -#define R_AX_PLE_ERR_FLAG_CFG 0x9034 + +#define R_AX_PLE_DBGERR_LOCKEN 0x9020 +#define B_AX_PLE_LOCKEN_DLEPIF07 BIT(7) +#define B_AX_PLE_LOCKEN_DLEPIF06 BIT(6) +#define B_AX_PLE_LOCKEN_DLEPIF05 BIT(5) +#define B_AX_PLE_LOCKEN_DLEPIF04 BIT(4) +#define B_AX_PLE_LOCKEN_DLEPIF03 BIT(3) +#define B_AX_PLE_LOCKEN_DLEPIF02 BIT(2) +#define B_AX_PLE_LOCKEN_DLEPIF01 BIT(1) +#define B_AX_PLE_LOCKEN_DLEPIF00 BIT(0) + +#define R_AX_PLE_DBGERR_STS 0x9024 +#define B_AX_PLE_LOCKON_DLEPIF07 BIT(7) +#define B_AX_PLE_LOCKON_DLEPIF06 BIT(6) +#define B_AX_PLE_LOCKON_DLEPIF05 BIT(5) +#define B_AX_PLE_LOCKON_DLEPIF04 BIT(4) +#define B_AX_PLE_LOCKON_DLEPIF03 BIT(3) +#define B_AX_PLE_LOCKON_DLEPIF02 BIT(2) +#define B_AX_PLE_LOCKON_DLEPIF01 BIT(1) +#define B_AX_PLE_LOCKON_DLEPIF00 BIT(0) + +#define R_AX_PLE_ERR_FLAG_CFG_NUM1 0x9034 +#define B_AX_PLE_ERR_FLAG_NUM1_VLD BIT(31) +#define B_AX_PLE_ERR_FLAG_NUM1_MSTIDX_MASK GENMASK(27, 24) +#define B_AX_PLE_ERR_FLAG_NUM1_ISRIDX_MASK GENMASK(20, 16) +#define B_AX_PLE_DATCHN_FRZTMR_MODE BIT(2) +#define B_AX_PLE_QUEMGN_FRZTMR_MODE BIT(1) +#define B_AX_PLE_BUFMGN_FRZTMR_MODE BIT(0) + +#define R_AX_PLE_ERRFLAG_MSG 0x9030 +#define B_AX_PLE_ERR_FLAG_MSG_MASK GENMASK(31, 0) +#define B_AX_PLE_DATCHN_CAMREQ_ERR_INT_EN BIT(29) +#define B_AX_PLE_DATCHN_ADRERR_ERR_INT_EN BIT(28) +#define B_AX_PLE_BUFMGN_FRZTO_ERR_INT_EN_V1 BIT(9) +#define B_AX_PLE_GETNPG_PGOFST_ERR_INT_EN_V1 BIT(8) +#define B_AX_PLE_GETNPG_STRPG_ERR_INT_EN_V1 BIT(7) +#define B_AX_PLE_BUFREQ_SRCHTAILPG_ERR_INT_EN_V1 BIT(6) +#define B_AX_PLE_BUFRTN_SIZE_ERR_INT_EN_V1 BIT(5) +#define B_AX_PLE_BUFRTN_INVLD_PKTID_ERR_INT_EN_V1 BIT(4) +#define B_AX_PLE_BUFREQ_UNAVAL_ERR_INT_EN_V1 BIT(3) +#define B_AX_PLE_BUFREQ_SIZELMT_INT_EN BIT(2) +#define B_AX_PLE_BUFREQ_SIZE0_INT_EN BIT(1) +#define B_AX_PLE_DATCHN_CAMREQ_ERR BIT(29) +#define B_AX_PLE_DATCHN_ADRERR_ERR BIT(28) +#define B_AX_PLE_BUFMGN_FRZTO_ERR_V1 BIT(9) +#define B_AX_PLE_GETNPG_PGOFST_ERR_V1 BIT(8) +#define B_AX_PLE_GETNPG_STRPG_ERR_V1 BIT(7) +#define B_AX_PLE_BUFREQ_SRCHTAILPG_ERR_V1 BIT(6) +#define B_AX_PLE_BUFRTN_SIZE_ERR_V1 BIT(5) +#define B_AX_PLE_BUFRTN_INVLD_PKTID_ERR_V1 BIT(4) +#define B_AX_PLE_BUFREQ_UNAVAL_ERR_V1 BIT(3) +#define B_AX_PLE_BUFREQ_SIZELMT_ERR BIT(2) +#define B_AX_PLE_BUFREQ_SIZE0_ERR BIT(1) #define R_AX_PLE_ERR_IMR 0x9038 #define B_AX_PLE_DATCHN_RRDY_ERR_INT_EN BIT(27) @@ -1436,6 +1497,19 @@ #define B_AX_BBRPT_COM_NULL_PLPKTID_ERR BIT(16) #define B_AX_BBRPT_COM_NULL_PLPKTID_ERR_INT_EN BIT(0) +#define R_AX_BBRPT_COM_ERR_ISR 0x960C +#define B_AX_BBRPT_COM_NULL_PLPKTID_ERR_INT_V1 BIT(0) + +#define R_AX_BBRPT_CHINFO_ERR_ISR 0x962C +#define B_AX_BBPRT_CHIF_TO_ERR_V1 BIT(7) +#define B_AX_BBPRT_CHIF_NULL_ERR_V1 BIT(6) +#define B_AX_BBPRT_CHIF_LEFT2_ERR_V1 BIT(5) +#define B_AX_BBPRT_CHIF_LEFT1_ERR_V1 BIT(4) +#define B_AX_BBPRT_CHIF_HDRL_ERR_V1 BIT(3) +#define B_AX_BBPRT_CHIF_BOVF_ERR_V1 BIT(2) +#define B_AX_BBPRT_CHIF_OVF_ERR_V1 BIT(1) +#define B_AX_BBPRT_CHIF_BB_TO_ERR_V1 BIT(0) + #define R_AX_BBRPT_CHINFO_ERR_IMR 0x9628 #define B_AX_BBPRT_CHIF_TO_ERR_INT_EN BIT(7) #define B_AX_BBPRT_CHIF_NULL_ERR_INT_EN BIT(6) @@ -1487,6 +1561,9 @@ #define B_AX_BBRPT_DFS_TO_ERR BIT(16) #define B_AX_BBRPT_DFS_TO_ERR_INT_EN BIT(0) +#define R_AX_BBRPT_DFS_ERR_ISR 0x963C +#define B_AX_BBRPT_DFS_TO_ERR_V1 BIT(0) + #define R_AX_LA_ERRFLAG 0x966C #define B_AX_LA_ISR_DATA_LOSS_ERR BIT(16) #define B_AX_LA_IMR_DATA_LOSS_ERR BIT(0) @@ -1602,6 +1679,7 @@ #define B_AX_MPDU_RX_IMR_SET_V1 B_AX_RPT_ERR_INT_EN #define R_AX_SEC_ENG_CTRL 0x9D00 +#define B_AX_SEC_DBG_PORT_FIELD_MASK GENMASK(19, 16) #define B_AX_TX_PARTIAL_MODE BIT(11) #define B_AX_CLK_EN_CGCMP BIT(10) #define B_AX_CLK_EN_WAPI BIT(9) @@ -1631,12 +1709,21 @@ #define R_AX_SEC_TX_DEBUG 0x9D20 #define R_AX_SEC_RX_DEBUG 0x9D24 #define R_AX_SEC_TRX_PKT_CNT 0x9D28 + +#define R_AX_SEC_DEBUG2 0x9D28 +#define B_AX_DBG_READ_SH 2 +#define B_AX_DBG_READ_MSK 0x3fffffff + #define R_AX_SEC_TRX_BLK_CNT 0x9D2C #define R_AX_SEC_ERROR_FLAG_IMR 0x9D2C #define B_AX_RX_HANG_IMR BIT(1) #define B_AX_TX_HANG_IMR BIT(0) +#define R_AX_SEC_ERROR_FLAG 0x9D30 +#define B_AX_RX_HANG_ERROR_V1 BIT(1) +#define B_AX_TX_HANG_ERROR_V1 BIT(0) + #define R_AX_SS_CTRL 0x9E10 #define B_AX_SS_INIT_DONE_1 BIT(31) #define B_AX_SS_WARM_INIT_FLG BIT(29) @@ -1771,6 +1858,28 @@ B_AX_B0_IMR_ERR_PRELD_RLSPKTSZERR | \ B_AX_B0_IMR_ERR_PRELD_ENTNUMCFG) +#define R_AX_TXPKTCTL_B0_ERRFLAG_ISR 0x9F7C +#define B_AX_B0_ISR_ERR_PRELD_EVT3 BIT(23) +#define B_AX_B0_ISR_ERR_PRELD_EVT2 BIT(22) +#define B_AX_B0_ISR_ERR_PRELD_ENTNUMCFG BIT(21) +#define B_AX_B0_ISR_ERR_PRELD_RLSPKTSZERR BIT(20) +#define B_AX_B0_ISR_ERR_MPDUIF_ERR1 BIT(19) +#define B_AX_B0_ISR_ERR_MPDUIF_DATAERR BIT(18) +#define B_AX_B0_ISR_ERR_MPDUINFO_ERR1 BIT(17) +#define B_AX_B0_ISR_ERR_MPDUINFO_RECFG BIT(16) +#define B_AX_B0_ISR_ERR_CMDPSR_TBLSZ BIT(11) +#define B_AX_B0_ISR_ERR_CMDPSR_FRZTO BIT(10) +#define B_AX_B0_ISR_ERR_CMDPSR_CMDTYPE BIT(9) +#define B_AX_B0_ISR_ERR_CMDPSR_1STCMDERR BIT(8) +#define B_AX_B0_ISR_ERR_USRCTL_EVT7 BIT(7) +#define B_AX_B0_ISR_ERR_USRCTL_EVT6 BIT(6) +#define B_AX_B0_ISR_ERR_USRCTL_EVT5 BIT(5) +#define B_AX_B0_ISR_ERR_USRCTL_EVT4 BIT(4) +#define B_AX_B0_ISR_ERR_USRCTL_RLSBMPLEN BIT(3) +#define B_AX_B0_ISR_ERR_USRCTL_RDNRLSCMD BIT(2) +#define B_AX_B0_ISR_ERR_USRCTL_NOINIT BIT(1) +#define B_AX_B0_ISR_ERR_USRCTL_REINIT BIT(0) + #define R_AX_TXPKTCTL_B1_PRELD_CFG0 0x9F88 #define B_AX_B1_PRELD_FEN BIT(31) #define B_AX_B1_PRELD_USEMAXSZ_MASK GENMASK(25, 16) @@ -1818,6 +1927,28 @@ B_AX_B1_IMR_ERR_PRELD_RLSPKTSZERR | \ B_AX_B1_IMR_ERR_PRELD_ENTNUMCFG) +#define R_AX_TXPKTCTL_B1_ERRFLAG_ISR 0x9FBC +#define B_AX_B1_ISR_ERR_PRELD_EVT3 BIT(23) +#define B_AX_B1_ISR_ERR_PRELD_EVT2 BIT(22) +#define B_AX_B1_ISR_ERR_PRELD_ENTNUMCFG BIT(21) +#define B_AX_B1_ISR_ERR_PRELD_RLSPKTSZERR BIT(20) +#define B_AX_B1_ISR_ERR_MPDUIF_ERR1 BIT(19) +#define B_AX_B1_ISR_ERR_MPDUIF_DATAERR BIT(18) +#define B_AX_B1_ISR_ERR_MPDUINFO_ERR1 BIT(17) +#define B_AX_B1_ISR_ERR_MPDUINFO_RECFG BIT(16) +#define B_AX_B1_ISR_ERR_CMDPSR_TBLSZ BIT(11) +#define B_AX_B1_ISR_ERR_CMDPSR_FRZTO BIT(10) +#define B_AX_B1_ISR_ERR_CMDPSR_CMDTYPE BIT(9) +#define B_AX_B1_ISR_ERR_CMDPSR_1STCMDERR BIT(8) +#define B_AX_B1_ISR_ERR_USRCTL_EVT7 BIT(7) +#define B_AX_B1_ISR_ERR_USRCTL_EVT6 BIT(6) +#define B_AX_B1_ISR_ERR_USRCTL_EVT5 BIT(5) +#define B_AX_B1_ISR_ERR_USRCTL_EVT4 BIT(4) +#define B_AX_B1_ISR_ERR_USRCTL_RLSBMPLEN BIT(3) +#define B_AX_B1_ISR_ERR_USRCTL_RDNRLSCMD BIT(2) +#define B_AX_B1_ISR_ERR_USRCTL_NOINIT BIT(1) +#define B_AX_B1_ISR_ERR_USRCTL_REINIT BIT(0) + #define R_AX_AFE_CTRL1 0x0024 #define B_AX_R_SYM_WLCMAC1_P4_PC_EN BIT(4) @@ -2386,6 +2517,41 @@ #define B_AX_DLE_IMR_SET (B_AX_RXSTS_FSM_HANG_ERROR_IMR | \ B_AX_RXDATA_FSM_HANG_ERROR_IMR) +#define R_AX_RX_ERR_FLAG 0xC800 +#define R_AX_RX_ERR_FLAG_C1 0xE800 +#define B_AX_RX_GET_NO_PAGE_ERR BIT(31) +#define B_AX_RX_GET_NULL_PKT_ERR BIT(30) +#define B_AX_RX_RU0_FSM_HANG_ERR BIT(29) +#define B_AX_RX_RU1_FSM_HANG_ERR BIT(28) +#define B_AX_RX_RU2_FSM_HANG_ERR BIT(27) +#define B_AX_RX_RU3_FSM_HANG_ERR BIT(26) +#define B_AX_RX_RU4_FSM_HANG_ERR BIT(25) +#define B_AX_RX_RU5_FSM_HANG_ERR BIT(24) +#define B_AX_RX_RU6_FSM_HANG_ERR BIT(23) +#define B_AX_RX_RU7_FSM_HANG_ERR BIT(22) +#define B_AX_RX_RXSTS_FSM_HANG_ERR BIT(21) +#define B_AX_RX_CSI_FSM_HANG_ERR BIT(20) +#define B_AX_RX_TXRPT_FSM_HANG_ERR BIT(19) +#define B_AX_RX_F2PCMD_FSM_HANG_ERR BIT(18) +#define B_AX_RX_RU0_ZERO_LEN_ERR BIT(17) +#define B_AX_RX_RU1_ZERO_LEN_ERR BIT(16) +#define B_AX_RX_RU2_ZERO_LEN_ERR BIT(15) +#define B_AX_RX_RU3_ZERO_LEN_ERR BIT(14) +#define B_AX_RX_RU4_ZERO_LEN_ERR BIT(13) +#define B_AX_RX_RU5_ZERO_LEN_ERR BIT(12) +#define B_AX_RX_RU6_ZERO_LEN_ERR BIT(11) +#define B_AX_RX_RU7_ZERO_LEN_ERR BIT(10) +#define B_AX_RX_RXSTS_ZERO_LEN_ERR BIT(9) +#define B_AX_RX_CSI_ZERO_LEN_ERR BIT(8) +#define B_AX_PLE_DATA_OPT_FSM_HANG BIT(7) +#define B_AX_PLE_RXDATA_REQ_BUF_FSM_HANG BIT(6) +#define B_AX_PLE_TXRPT_REQ_BUF_FSM_HANG BIT(5) +#define B_AX_PLE_WD_OPT_FSM_HANG BIT(4) +#define B_AX_PLE_ENQ_FSM_HANG BIT(3) +#define B_AX_RXDATA_ENQUE_ORDER_ERR BIT(2) +#define B_AX_RXSTS_ENQUE_ORDER_ERR BIT(1) +#define B_AX_RX_CSI_PKT_NUM_ERR BIT(0) + #define R_AX_RXDMA_CTRL_0 0xC804 #define R_AX_RXDMA_CTRL_0_C1 0xE804 #define B_AX_RXDMA_DBGOUT_EN BIT(31) @@ -2408,6 +2574,49 @@ B_AX_RU2_PTR_FULL_MODE | B_AX_RU3_PTR_FULL_MODE | \ B_AX_CSI_PTR_FULL_MODE | B_AX_RXSTS_PTR_FULL_MODE) +#define R_AX_RX_CTRL0 0xC808 +#define R_AX_RX_CTRL0_C1 0xE808 +#define B_AX_DLE_CLOCK_FORCE_V1 BIT(31) +#define B_AX_TXDMA_CLOCK_FORCE_V1 BIT(30) +#define B_AX_RXDMA_CLOCK_FORCE_V1 BIT(29) +#define B_AX_RXDMA_DEFAULT_PAGE_V1_MASK GENMASK(28, 24) +#define B_AX_RXDMA_CSI_TGT_QUEID_MASK GENMASK(23, 18) +#define B_AX_RXDMA_CSI_TGT_PRID_MASK GENMASK(17, 15) +#define B_AX_RXDMA_DIS_CSI_RELEASE_V1 BIT(14) +#define B_AX_CSI_PTR_FULL_MODE_V1 BIT(13) +#define B_AX_RXDATA_PTR_FULL_MODE BIT(12) +#define B_AX_RXSTS_PTR_FULL_MODE_V1 BIT(11) +#define B_AX_TXRPT_FULL_RSV_DEPTH_V1_MASK GENMASK(10, 8) +#define B_AX_RXDATA_FULL_RSV_DEPTH_MASK GENMASK(7, 5) +#define B_AX_RXSTS_FULL_RSV_DEPTH_V1_MASK GENMASK(4, 2) +#define B_AX_ORDER_FIFO_MASK GENMASK(1, 0) + +#define R_AX_RX_CTRL1 0xC80C +#define R_AX_RX_CTRL1_C1 0xE80C +#define B_AX_RXDMA_TXRPT_QUEUE_ID_SW_EN BIT(31) +#define B_AX_RXDMA_TXRPT_QUEUE_ID_SW_V1_MASK GENMASK(30, 25) +#define B_AX_RXDMA_F2PCMD_QUEUE_ID_SW_EN BIT(24) +#define B_AX_RXDMA_F2PCMD_QUEUE_ID_SW_V1_MASK GENMASK(23, 18) +#define B_AX_RXDMA_TXRPT_QUEUE_ID_TGT_SW_EN BIT(17) +#define B_AX_RXDMA_TXRPT_QUEUE_ID_TGT_SW_1_MASK GENMASK(16, 11) +#define B_AX_RXDMA_F2PCMD_QUEUE_ID_TGT_SW_EN BIT(10) +#define B_AX_RXDMA_F2PCMD_QUEUE_ID_TGT_SW_1_MASK GENMASK(9, 4) +#define B_AX_ORDER_FIFO_OUT BIT(3) +#define B_AX_ORDER_FIFO_EMPTY BIT(2) +#define B_AX_DBG_SEL_MASK GENMASK(1, 0) + +#define R_AX_RX_CTRL2 0xC810 +#define R_AX_RX_CTRL2_C1 0xE810 +#define B_AX_DLE_WDE_STATE_V1_MASK GENMASK(31, 30) +#define B_AX_DLE_PLE_STATE_V1_MASK GENMASK(29, 28) +#define B_AX_DLE_REQ_BUF_STATE_MASK GENMASK(27, 26) +#define B_AX_DLE_ENQ_STATE_V1 BIT(25) +#define B_AX_RX_DBG_SEL_MASK GENMASK(24, 19) +#define B_AX_MACRX_CS_MASK GENMASK(18, 14) +#define B_AX_RXSTS_CS_MASK GENMASK(13, 9) +#define B_AX_ERR_INDICATOR BIT(5) +#define B_AX_TXRPT_CS_MASK GENMASK(4, 0) + #define R_AX_RXDMA_PKT_INFO_0 0xC814 #define R_AX_RXDMA_PKT_INFO_1 0xC818 #define R_AX_RXDMA_PKT_INFO_2 0xC81C @@ -2715,6 +2924,18 @@ B_AX_TMAC_MIMO_CTRL | \ B_AX_RMAC_FTM) +#define R_AX_TRXPTCL_ERROR_INDICA 0xCCC0 +#define R_AX_TRXPTCL_ERROR_INDICA_C1 0xECC0 +#define B_AX_FTM_ERROR_FLAG_CLR BIT(8) +#define B_AX_CSI_ERROR_FLAG_CLR BIT(7) +#define B_AX_MIMOCTRL_ERROR_FLAG_CLR BIT(6) +#define B_AX_RXTB_ERROR_FLAG_CLR BIT(5) +#define B_AX_HWSIGB_GEN_ERROR_FLAG_CLR BIT(4) +#define B_AX_TXPLCP_ERROR_FLAG_CLR BIT(3) +#define B_AX_RESP_ERROR_FLAG_CLR BIT(2) +#define B_AX_TXCTL_ERROR_FLAG_CLR BIT(1) +#define B_AX_MACTX_ERROR_FLAG_CLR BIT(0) + #define R_AX_WMAC_TX_TF_INFO_0 0xCCD0 #define R_AX_WMAC_TX_TF_INFO_0_C1 0xECD0 #define B_AX_WMAC_TX_TF_INFO_SEL_MASK GENMASK(2, 0) From 9db485ce098fda396151253b34c5aa6a4fbf6c1b Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Fri, 4 Nov 2022 13:50:36 +0000 Subject: [PATCH 0704/2002] wifi: iwlegacy: remove redundant variable len Variable len is being assigned and modified but it is never used. The variable is redundant and can be removed. Cleans up clang scan build warning: warning: variable 'len' set but not used [-Wunused-but-set-variable] Signed-off-by: Colin Ian King Acked-by: Stanislaw Gruszka Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20221104135036.225628-1-colin.i.king@gmail.com --- drivers/net/wireless/intel/iwlegacy/3945-mac.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/drivers/net/wireless/intel/iwlegacy/3945-mac.c b/drivers/net/wireless/intel/iwlegacy/3945-mac.c index c4503308f3b0..d7e99d50b287 100644 --- a/drivers/net/wireless/intel/iwlegacy/3945-mac.c +++ b/drivers/net/wireless/intel/iwlegacy/3945-mac.c @@ -1202,8 +1202,6 @@ il3945_rx_handle(struct il_priv *il) D_RX("r = %d, i = %d\n", r, i); while (i != r) { - int len; - rxb = rxq->queue[i]; /* If an RXB doesn't have a Rx queue slot associated with it, @@ -1217,10 +1215,6 @@ il3945_rx_handle(struct il_priv *il) PAGE_SIZE << il->hw_params.rx_page_order, DMA_FROM_DEVICE); pkt = rxb_addr(rxb); - - len = le32_to_cpu(pkt->len_n_flags) & IL_RX_FRAME_SIZE_MSK; - len += sizeof(u32); /* account for status word */ - reclaim = il_need_reclaim(il, pkt); /* Based on type of command response or notification, From b8f6efccbb9dc0ff5dee7e20d69a4747298ee603 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Fri, 4 Nov 2022 17:33:39 +0100 Subject: [PATCH 0705/2002] wifi: rsi: Fix handling of 802.3 EAPOL frames sent via control port When using wpa_supplicant v2.10, this driver is no longer able to associate with any AP and fails in the EAPOL 4-way handshake while sending the 2/4 message to the AP. The problem is not present in wpa_supplicant v2.9 or older. The problem stems from HostAP commit 144314eaa ("wpa_supplicant: Send EAPOL frames over nl80211 where available") which changes the way EAPOL frames are sent, from them being send at L2 frames to them being sent via nl80211 control port. An EAPOL frame sent as L2 frame is passed to the WiFi driver with skb->protocol ETH_P_PAE, while EAPOL frame sent via nl80211 control port has skb->protocol set to ETH_P_802_3 . The later happens in ieee80211_tx_control_port(), where the EAPOL frame is encapsulated into 802.3 frame. The rsi_91x driver handles ETH_P_PAE EAPOL frames as high-priority frames and sends them via highest-priority transmit queue, while the ETH_P_802_3 frames are sent as regular frames. The EAPOL 4-way handshake frames must be sent as highest-priority, otherwise the 4-way handshake times out. Therefore, to fix this problem, inspect the skb control flags and if flag IEEE80211_TX_CTRL_PORT_CTRL_PROTO is set, assume this is an EAPOL frame and transmit the frame via high-priority queue just like other ETH_P_PAE frames. Fixes: 0eb42586cf87 ("rsi: data packet descriptor enhancements") Signed-off-by: Marek Vasut Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20221104163339.227432-1-marex@denx.de --- drivers/net/wireless/rsi/rsi_91x_core.c | 4 +++- drivers/net/wireless/rsi/rsi_91x_hal.c | 6 +++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/rsi/rsi_91x_core.c b/drivers/net/wireless/rsi/rsi_91x_core.c index 0f3a80f66b61..ead4d4e04328 100644 --- a/drivers/net/wireless/rsi/rsi_91x_core.c +++ b/drivers/net/wireless/rsi/rsi_91x_core.c @@ -466,7 +466,9 @@ void rsi_core_xmit(struct rsi_common *common, struct sk_buff *skb) tid, 0); } } - if (skb->protocol == cpu_to_be16(ETH_P_PAE)) { + + if (IEEE80211_SKB_CB(skb)->control.flags & + IEEE80211_TX_CTRL_PORT_CTRL_PROTO) { q_num = MGMT_SOFT_Q; skb->priority = q_num; } diff --git a/drivers/net/wireless/rsi/rsi_91x_hal.c b/drivers/net/wireless/rsi/rsi_91x_hal.c index c61f83a7333b..c7460fbba014 100644 --- a/drivers/net/wireless/rsi/rsi_91x_hal.c +++ b/drivers/net/wireless/rsi/rsi_91x_hal.c @@ -162,12 +162,16 @@ int rsi_prepare_data_desc(struct rsi_common *common, struct sk_buff *skb) u8 header_size; u8 vap_id = 0; u8 dword_align_bytes; + bool tx_eapol; u16 seq_num; info = IEEE80211_SKB_CB(skb); vif = info->control.vif; tx_params = (struct skb_info *)info->driver_data; + tx_eapol = IEEE80211_SKB_CB(skb)->control.flags & + IEEE80211_TX_CTRL_PORT_CTRL_PROTO; + header_size = FRAME_DESC_SZ + sizeof(struct rsi_xtended_desc); if (header_size > skb_headroom(skb)) { rsi_dbg(ERR_ZONE, "%s: Unable to send pkt\n", __func__); @@ -231,7 +235,7 @@ int rsi_prepare_data_desc(struct rsi_common *common, struct sk_buff *skb) } } - if (skb->protocol == cpu_to_be16(ETH_P_PAE)) { + if (tx_eapol) { rsi_dbg(INFO_ZONE, "*** Tx EAPOL ***\n"); data_desc->frame_info = cpu_to_le16(RATE_INFO_ENABLE); From 9a0f830f80265bd1ef816e1541ac24bee80e9a3c Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Fri, 4 Nov 2022 12:01:25 -0700 Subject: [PATCH 0706/2002] ethtool: linkstate: add a statistic for PHY down events The previous attempt to augment carrier_down (see Link) was not met with much enthusiasm so let's do the simple thing of exposing what some devices already maintain. Add a common ethtool statistic for link going down. Currently users have to maintain per-driver mapping to extract the right stat from the vendor-specific ethtool -S stats. carrier_down does not fit the bill because it counts a lot of software related false positives. Add the statistic to the extended link state API to steer vendors towards implementing all of it. Implement for bnxt and all Linux-controlled PHYs. mlx5 and (possibly) enic also have a counter for this but I leave the implementation to their maintainers. Link: https://lore.kernel.org/r/20220520004500.2250674-1-kuba@kernel.org Reviewed-by: Florian Fainelli Reviewed-by: Michael Chan Reviewed-by: Andrew Lunn Signed-off-by: Jakub Kicinski Link: https://lore.kernel.org/r/20221104190125.684910-1-kuba@kernel.org Signed-off-by: Paolo Abeni --- Documentation/networking/ethtool-netlink.rst | 1 + .../net/ethernet/broadcom/bnxt/bnxt_ethtool.c | 15 ++++++++++++ drivers/net/phy/phy.c | 1 + include/linux/ethtool.h | 17 +++++++++++++ include/linux/phy.h | 3 +++ include/uapi/linux/ethtool_netlink.h | 1 + net/ethtool/linkstate.c | 24 ++++++++++++++++++- 7 files changed, 61 insertions(+), 1 deletion(-) diff --git a/Documentation/networking/ethtool-netlink.rst b/Documentation/networking/ethtool-netlink.rst index d578b8bcd8a4..bede24ef44fd 100644 --- a/Documentation/networking/ethtool-netlink.rst +++ b/Documentation/networking/ethtool-netlink.rst @@ -491,6 +491,7 @@ Kernel response contents: ``ETHTOOL_A_LINKSTATE_SQI_MAX`` u32 Max support SQI value ``ETHTOOL_A_LINKSTATE_EXT_STATE`` u8 link extended state ``ETHTOOL_A_LINKSTATE_EXT_SUBSTATE`` u8 link extended substate + ``ETHTOOL_A_LINKSTATE_EXT_DOWN_CNT`` u32 count of link down events ==================================== ====== ============================ For most NIC drivers, the value of ``ETHTOOL_A_LINKSTATE_LINK`` returns diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c index cc89e5eabcb9..d8f0351df954 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c @@ -4112,6 +4112,20 @@ static void bnxt_get_rmon_stats(struct net_device *dev, *ranges = bnxt_rmon_ranges; } +static void bnxt_get_link_ext_stats(struct net_device *dev, + struct ethtool_link_ext_stats *stats) +{ + struct bnxt *bp = netdev_priv(dev); + u64 *rx; + + if (BNXT_VF(bp) || !(bp->flags & BNXT_FLAG_PORT_STATS_EXT)) + return; + + rx = bp->rx_port_stats_ext.sw_stats; + stats->link_down_events = + *(rx + BNXT_RX_STATS_EXT_OFFSET(link_down_events)); +} + void bnxt_ethtool_free(struct bnxt *bp) { kfree(bp->test_info); @@ -4161,6 +4175,7 @@ const struct ethtool_ops bnxt_ethtool_ops = { .get_eeprom = bnxt_get_eeprom, .set_eeprom = bnxt_set_eeprom, .get_link = bnxt_get_link, + .get_link_ext_stats = bnxt_get_link_ext_stats, .get_eee = bnxt_get_eee, .set_eee = bnxt_set_eee, .get_module_info = bnxt_get_module_info, diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index e741d8aebffe..e5b6cb1a77f9 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -67,6 +67,7 @@ static void phy_link_down(struct phy_device *phydev) { phydev->phy_link_change(phydev, false); phy_led_trigger_change_speed(phydev); + WRITE_ONCE(phydev->link_down_events, phydev->link_down_events + 1); } static const char *phy_pause_str(struct phy_device *phydev) diff --git a/include/linux/ethtool.h b/include/linux/ethtool.h index 99dc7bfbcd3c..5c51c7fda32a 100644 --- a/include/linux/ethtool.h +++ b/include/linux/ethtool.h @@ -125,6 +125,20 @@ struct ethtool_link_ext_state_info { }; }; +struct ethtool_link_ext_stats { + /* Custom Linux statistic for PHY level link down events. + * In a simpler world it should be equal to netdev->carrier_down_count + * unfortunately netdev also counts local reconfigurations which don't + * actually take the physical link down, not to mention NC-SI which, + * if present, keeps the link up regardless of host state. + * This statistic counts when PHY _actually_ went down, or lost link. + * + * Note that we need u64 for ethtool_stats_init() and comparisons + * to ETHTOOL_STAT_NOT_SET, but only u32 is exposed to the user. + */ + u64 link_down_events; +}; + /** * ethtool_rxfh_indir_default - get default value for RX flow hash indirection * @index: Index in RX flow hash indirection table @@ -481,6 +495,7 @@ struct ethtool_module_power_mode_params { * do not attach ext_substate attribute to netlink message). If link_ext_state * and link_ext_substate are unknown, return -ENODATA. If not implemented, * link_ext_state and link_ext_substate will not be sent to userspace. + * @get_link_ext_stats: Read extra link-related counters. * @get_eeprom_len: Read range of EEPROM addresses for validation of * @get_eeprom and @set_eeprom requests. * Returns 0 if device does not support EEPROM access. @@ -652,6 +667,8 @@ struct ethtool_ops { u32 (*get_link)(struct net_device *); int (*get_link_ext_state)(struct net_device *, struct ethtool_link_ext_state_info *); + void (*get_link_ext_stats)(struct net_device *dev, + struct ethtool_link_ext_stats *stats); int (*get_eeprom_len)(struct net_device *); int (*get_eeprom)(struct net_device *, struct ethtool_eeprom *, u8 *); diff --git a/include/linux/phy.h b/include/linux/phy.h index ddf66198f751..9a3752c0c444 100644 --- a/include/linux/phy.h +++ b/include/linux/phy.h @@ -600,6 +600,7 @@ struct macsec_ops; * @psec: Pointer to Power Sourcing Equipment control struct * @lock: Mutex for serialization access to PHY * @state_queue: Work queue for state machine + * @link_down_events: Number of times link was lost * @shared: Pointer to private data shared by phys in one package * @priv: Pointer to driver private data * @@ -723,6 +724,8 @@ struct phy_device { int pma_extable; + unsigned int link_down_events; + void (*phy_link_change)(struct phy_device *phydev, bool up); void (*adjust_link)(struct net_device *dev); diff --git a/include/uapi/linux/ethtool_netlink.h b/include/uapi/linux/ethtool_netlink.h index bb57084ac524..aaf7c6963d61 100644 --- a/include/uapi/linux/ethtool_netlink.h +++ b/include/uapi/linux/ethtool_netlink.h @@ -262,6 +262,7 @@ enum { ETHTOOL_A_LINKSTATE_SQI_MAX, /* u32 */ ETHTOOL_A_LINKSTATE_EXT_STATE, /* u8 */ ETHTOOL_A_LINKSTATE_EXT_SUBSTATE, /* u8 */ + ETHTOOL_A_LINKSTATE_EXT_DOWN_CNT, /* u32 */ /* add new constants above here */ __ETHTOOL_A_LINKSTATE_CNT, diff --git a/net/ethtool/linkstate.c b/net/ethtool/linkstate.c index fb676f349455..2158c17a0b32 100644 --- a/net/ethtool/linkstate.c +++ b/net/ethtool/linkstate.c @@ -13,6 +13,7 @@ struct linkstate_reply_data { int link; int sqi; int sqi_max; + struct ethtool_link_ext_stats link_stats; bool link_ext_state_provided; struct ethtool_link_ext_state_info ethtool_link_ext_state_info; }; @@ -22,7 +23,7 @@ struct linkstate_reply_data { const struct nla_policy ethnl_linkstate_get_policy[] = { [ETHTOOL_A_LINKSTATE_HEADER] = - NLA_POLICY_NESTED(ethnl_header_policy), + NLA_POLICY_NESTED(ethnl_header_policy_stats), }; static int linkstate_get_sqi(struct net_device *dev) @@ -107,6 +108,19 @@ static int linkstate_prepare_data(const struct ethnl_req_info *req_base, goto out; } + ethtool_stats_init((u64 *)&data->link_stats, + sizeof(data->link_stats) / 8); + + if (req_base->flags & ETHTOOL_FLAG_STATS) { + if (dev->phydev) + data->link_stats.link_down_events = + READ_ONCE(dev->phydev->link_down_events); + + if (dev->ethtool_ops->get_link_ext_stats) + dev->ethtool_ops->get_link_ext_stats(dev, + &data->link_stats); + } + ret = 0; out: ethnl_ops_complete(dev); @@ -134,6 +148,9 @@ static int linkstate_reply_size(const struct ethnl_req_info *req_base, if (data->ethtool_link_ext_state_info.__link_ext_substate) len += nla_total_size(sizeof(u8)); /* LINKSTATE_EXT_SUBSTATE */ + if (data->link_stats.link_down_events != ETHTOOL_STAT_NOT_SET) + len += nla_total_size(sizeof(u32)); + return len; } @@ -166,6 +183,11 @@ static int linkstate_fill_reply(struct sk_buff *skb, return -EMSGSIZE; } + if (data->link_stats.link_down_events != ETHTOOL_STAT_NOT_SET) + if (nla_put_u32(skb, ETHTOOL_A_LINKSTATE_EXT_DOWN_CNT, + data->link_stats.link_down_events)) + return -EMSGSIZE; + return 0; } From a018750a2cceaf4427c4ee3d9ce3e83a171d5bd6 Mon Sep 17 00:00:00 2001 From: Youghandhar Chintala Date: Fri, 4 Nov 2022 14:24:03 +0530 Subject: [PATCH 0707/2002] wifi: ath11k: Trigger sta disconnect on hardware restart Currently after the hardware restart triggered from the driver, the station interface connection remains intact, since a disconnect trigger is not sent to userspace. This can lead to a problem in targets where the wifi mac sequence is added by the firmware. After the target restart, its wifi mac sequence number gets reset to zero. Hence AP to which our device is connected will receive frames with a wifi mac sequence number jump to the past, thereby resulting in the AP dropping all these frames, until the frame arrives with a wifi mac sequence number which AP was expecting. To avoid such frame drops, its better to trigger a station disconnect upon target hardware restart which can be done with API ieee80211_reconfig_disconnect exposed to mac80211. The other targets are not affected by this change, since the hardware params flag is not set. Reported-by: kernel test robot Tested-on: WCN6750 hw1.0 AHB WLAN.MSL.1.0.1-00887-QCAMSLSWPLZ-1 Signed-off-by: Youghandhar Chintala Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20221104085403.11025-1-quic_youghand@quicinc.com --- drivers/net/wireless/ath/ath11k/core.c | 6 ++++++ drivers/net/wireless/ath/ath11k/hw.h | 1 + drivers/net/wireless/ath/ath11k/mac.c | 7 +++++++ 3 files changed, 14 insertions(+) diff --git a/drivers/net/wireless/ath/ath11k/core.c b/drivers/net/wireless/ath/ath11k/core.c index 0ab2f190f1ec..edf78df9b12f 100644 --- a/drivers/net/wireless/ath/ath11k/core.c +++ b/drivers/net/wireless/ath/ath11k/core.c @@ -195,6 +195,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = { .tcl_ring_retry = true, .tx_ring_size = DP_TCL_DATA_RING_SIZE, .smp2p_wow_exit = false, + .support_fw_mac_sequence = false, }, { .name = "qca6390 hw2.0", @@ -277,6 +278,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = { .tcl_ring_retry = true, .tx_ring_size = DP_TCL_DATA_RING_SIZE, .smp2p_wow_exit = false, + .support_fw_mac_sequence = true, }, { .name = "qcn9074 hw1.0", @@ -356,6 +358,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = { .tcl_ring_retry = true, .tx_ring_size = DP_TCL_DATA_RING_SIZE, .smp2p_wow_exit = false, + .support_fw_mac_sequence = false, }, { .name = "wcn6855 hw2.0", @@ -438,6 +441,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = { .tcl_ring_retry = true, .tx_ring_size = DP_TCL_DATA_RING_SIZE, .smp2p_wow_exit = false, + .support_fw_mac_sequence = true, }, { .name = "wcn6855 hw2.1", @@ -519,6 +523,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = { .tcl_ring_retry = true, .tx_ring_size = DP_TCL_DATA_RING_SIZE, .smp2p_wow_exit = false, + .support_fw_mac_sequence = true, }, { .name = "wcn6750 hw1.0", @@ -597,6 +602,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = { .tcl_ring_retry = false, .tx_ring_size = DP_TCL_DATA_RING_SIZE_WCN6750, .smp2p_wow_exit = true, + .support_fw_mac_sequence = true, }, }; diff --git a/drivers/net/wireless/ath/ath11k/hw.h b/drivers/net/wireless/ath/ath11k/hw.h index 8a3f24862edc..0c5ef8a526d8 100644 --- a/drivers/net/wireless/ath/ath11k/hw.h +++ b/drivers/net/wireless/ath/ath11k/hw.h @@ -219,6 +219,7 @@ struct ath11k_hw_params { bool tcl_ring_retry; u32 tx_ring_size; bool smp2p_wow_exit; + bool support_fw_mac_sequence; }; struct ath11k_hw_ops { diff --git a/drivers/net/wireless/ath/ath11k/mac.c b/drivers/net/wireless/ath/ath11k/mac.c index 6426a38f8961..9e923ecb0891 100644 --- a/drivers/net/wireless/ath/ath11k/mac.c +++ b/drivers/net/wireless/ath/ath11k/mac.c @@ -8010,6 +8010,7 @@ ath11k_mac_op_reconfig_complete(struct ieee80211_hw *hw, struct ath11k *ar = hw->priv; struct ath11k_base *ab = ar->ab; int recovery_count; + struct ath11k_vif *arvif; if (reconfig_type != IEEE80211_RECONFIG_TYPE_RESTART) return; @@ -8045,6 +8046,12 @@ ath11k_mac_op_reconfig_complete(struct ieee80211_hw *hw, ath11k_dbg(ab, ATH11K_DBG_BOOT, "reset success\n"); } } + if (ar->ab->hw_params.support_fw_mac_sequence) { + list_for_each_entry(arvif, &ar->arvifs, list) { + if (arvif->is_up && arvif->vdev_type == WMI_VDEV_TYPE_STA) + ieee80211_hw_restart_disconnect(arvif->vif); + } + } } mutex_unlock(&ar->conf_mutex); From 47f3ecf4763d3fea37d3453c9ee1f9f2169d71b3 Mon Sep 17 00:00:00 2001 From: Gal Pressman Date: Sun, 6 Nov 2022 14:31:27 +0200 Subject: [PATCH 0708/2002] ethtool: Fail number of channels change when it conflicts with rxnfc Similar to what we do with the hash indirection table [1], when network flow classification rules are forwarding traffic to channels greater than the requested number of channels, fail the operation. Without this, traffic could be directed to channels which no longer exist (dropped) after changing number of channels. [1] commit d4ab4286276f ("ethtool: correctly ensure {GS}CHANNELS doesn't conflict with GS{RXFH}") Reviewed-by: Tariq Toukan Signed-off-by: Gal Pressman Link: https://lore.kernel.org/r/20221106123127.522985-1-gal@nvidia.com Signed-off-by: Paolo Abeni --- net/ethtool/channels.c | 19 ++++++++---- net/ethtool/common.c | 66 ++++++++++++++++++++++++++++++++++++++++++ net/ethtool/common.h | 1 + net/ethtool/ioctl.c | 17 +++++++---- 4 files changed, 92 insertions(+), 11 deletions(-) diff --git a/net/ethtool/channels.c b/net/ethtool/channels.c index 403158862011..c7e37130647e 100644 --- a/net/ethtool/channels.c +++ b/net/ethtool/channels.c @@ -116,9 +116,10 @@ int ethnl_set_channels(struct sk_buff *skb, struct genl_info *info) struct ethtool_channels channels = {}; struct ethnl_req_info req_info = {}; struct nlattr **tb = info->attrs; - u32 err_attr, max_rx_in_use = 0; + u32 err_attr, max_rxfh_in_use; const struct ethtool_ops *ops; struct net_device *dev; + u64 max_rxnfc_in_use; int ret; ret = ethnl_parse_header_dev_get(&req_info, @@ -189,15 +190,23 @@ int ethnl_set_channels(struct sk_buff *skb, struct genl_info *info) } /* ensure the new Rx count fits within the configured Rx flow - * indirection table settings + * indirection table/rxnfc settings */ - if (netif_is_rxfh_configured(dev) && - !ethtool_get_max_rxfh_channel(dev, &max_rx_in_use) && - (channels.combined_count + channels.rx_count) <= max_rx_in_use) { + if (ethtool_get_max_rxnfc_channel(dev, &max_rxnfc_in_use)) + max_rxnfc_in_use = 0; + if (!netif_is_rxfh_configured(dev) || + ethtool_get_max_rxfh_channel(dev, &max_rxfh_in_use)) + max_rxfh_in_use = 0; + if (channels.combined_count + channels.rx_count <= max_rxfh_in_use) { ret = -EINVAL; GENL_SET_ERR_MSG(info, "requested channel counts are too low for existing indirection table settings"); goto out_ops; } + if (channels.combined_count + channels.rx_count <= max_rxnfc_in_use) { + ret = -EINVAL; + GENL_SET_ERR_MSG(info, "requested channel counts are too low for existing ntuple filter settings"); + goto out_ops; + } /* Disabling channels, query zero-copy AF_XDP sockets */ from_channel = channels.combined_count + diff --git a/net/ethtool/common.c b/net/ethtool/common.c index ee3e02da0013..21cfe8557205 100644 --- a/net/ethtool/common.c +++ b/net/ethtool/common.c @@ -512,6 +512,72 @@ int __ethtool_get_link(struct net_device *dev) return netif_running(dev) && dev->ethtool_ops->get_link(dev); } +static int ethtool_get_rxnfc_rule_count(struct net_device *dev) +{ + const struct ethtool_ops *ops = dev->ethtool_ops; + struct ethtool_rxnfc info = { + .cmd = ETHTOOL_GRXCLSRLCNT, + }; + int err; + + err = ops->get_rxnfc(dev, &info, NULL); + if (err) + return err; + + return info.rule_cnt; +} + +int ethtool_get_max_rxnfc_channel(struct net_device *dev, u64 *max) +{ + const struct ethtool_ops *ops = dev->ethtool_ops; + struct ethtool_rxnfc *info; + int err, i, rule_cnt; + u64 max_ring = 0; + + if (!ops->get_rxnfc) + return -EOPNOTSUPP; + + rule_cnt = ethtool_get_rxnfc_rule_count(dev); + if (rule_cnt <= 0) + return -EINVAL; + + info = kvzalloc(struct_size(info, rule_locs, rule_cnt), GFP_KERNEL); + if (!info) + return -ENOMEM; + + info->cmd = ETHTOOL_GRXCLSRLALL; + info->rule_cnt = rule_cnt; + err = ops->get_rxnfc(dev, info, info->rule_locs); + if (err) + goto err_free_info; + + for (i = 0; i < rule_cnt; i++) { + struct ethtool_rxnfc rule_info = { + .cmd = ETHTOOL_GRXCLSRULE, + .fs.location = info->rule_locs[i], + }; + + err = ops->get_rxnfc(dev, &rule_info, NULL); + if (err) + goto err_free_info; + + if (rule_info.fs.ring_cookie != RX_CLS_FLOW_DISC && + rule_info.fs.ring_cookie != RX_CLS_FLOW_WAKE && + !(rule_info.flow_type & FLOW_RSS) && + !ethtool_get_flow_spec_ring_vf(rule_info.fs.ring_cookie)) + max_ring = + max_t(u64, max_ring, rule_info.fs.ring_cookie); + } + + kvfree(info); + *max = max_ring; + return 0; + +err_free_info: + kvfree(info); + return err; +} + int ethtool_get_max_rxfh_channel(struct net_device *dev, u32 *max) { u32 dev_size, current_max = 0; diff --git a/net/ethtool/common.h b/net/ethtool/common.h index c1779657e074..b1b9db810eca 100644 --- a/net/ethtool/common.h +++ b/net/ethtool/common.h @@ -43,6 +43,7 @@ bool convert_legacy_settings_to_link_ksettings( struct ethtool_link_ksettings *link_ksettings, const struct ethtool_cmd *legacy_settings); int ethtool_get_max_rxfh_channel(struct net_device *dev, u32 *max); +int ethtool_get_max_rxnfc_channel(struct net_device *dev, u64 *max); int __ethtool_get_ts_info(struct net_device *dev, struct ethtool_ts_info *info); extern const struct ethtool_phy_ops *ethtool_phy_ops; diff --git a/net/ethtool/ioctl.c b/net/ethtool/ioctl.c index b6835136c53f..e40f5e9e109b 100644 --- a/net/ethtool/ioctl.c +++ b/net/ethtool/ioctl.c @@ -1789,7 +1789,8 @@ static noinline_for_stack int ethtool_set_channels(struct net_device *dev, { struct ethtool_channels channels, curr = { .cmd = ETHTOOL_GCHANNELS }; u16 from_channel, to_channel; - u32 max_rx_in_use = 0; + u64 max_rxnfc_in_use; + u32 max_rxfh_in_use; unsigned int i; int ret; @@ -1820,11 +1821,15 @@ static noinline_for_stack int ethtool_set_channels(struct net_device *dev, return -EINVAL; /* ensure the new Rx count fits within the configured Rx flow - * indirection table settings */ - if (netif_is_rxfh_configured(dev) && - !ethtool_get_max_rxfh_channel(dev, &max_rx_in_use) && - (channels.combined_count + channels.rx_count) <= max_rx_in_use) - return -EINVAL; + * indirection table/rxnfc settings */ + if (ethtool_get_max_rxnfc_channel(dev, &max_rxnfc_in_use)) + max_rxnfc_in_use = 0; + if (!netif_is_rxfh_configured(dev) || + ethtool_get_max_rxfh_channel(dev, &max_rxfh_in_use)) + max_rxfh_in_use = 0; + if (channels.combined_count + channels.rx_count <= + max_t(u64, max_rxnfc_in_use, max_rxfh_in_use)) + return -EINVAL; /* Disabling channels, query zero-copy AF_XDP sockets */ from_channel = channels.combined_count + From ca71277f36e0781db663aedeb5fc1e26e7c144c4 Mon Sep 17 00:00:00 2001 From: Xin Long Date: Sun, 6 Nov 2022 15:34:14 -0500 Subject: [PATCH 0709/2002] net: move the ct helper function to nf_conntrack_helper for ovs and tc Move ovs_ct_helper from openvswitch to nf_conntrack_helper and rename as nf_ct_helper so that it can be used in TC act_ct in the next patch. Note that it also adds the checks for the family and proto, as in TC act_ct, the packets with correct family and proto are not guaranteed. Acked-by: Marcelo Ricardo Leitner Signed-off-by: Xin Long Signed-off-by: Paolo Abeni --- include/net/netfilter/nf_conntrack_helper.h | 3 + net/netfilter/nf_conntrack_helper.c | 69 +++++++++++++++++++++ net/openvswitch/conntrack.c | 61 +----------------- 3 files changed, 73 insertions(+), 60 deletions(-) diff --git a/include/net/netfilter/nf_conntrack_helper.h b/include/net/netfilter/nf_conntrack_helper.h index 9939c366f720..b6676249eeeb 100644 --- a/include/net/netfilter/nf_conntrack_helper.h +++ b/include/net/netfilter/nf_conntrack_helper.h @@ -115,6 +115,9 @@ struct nf_conn_help *nf_ct_helper_ext_add(struct nf_conn *ct, gfp_t gfp); int __nf_ct_try_assign_helper(struct nf_conn *ct, struct nf_conn *tmpl, gfp_t flags); +int nf_ct_helper(struct sk_buff *skb, struct nf_conn *ct, + enum ip_conntrack_info ctinfo, u16 proto); + void nf_ct_helper_destroy(struct nf_conn *ct); static inline struct nf_conn_help *nfct_help(const struct nf_conn *ct) diff --git a/net/netfilter/nf_conntrack_helper.c b/net/netfilter/nf_conntrack_helper.c index ff737a76052e..88039eedadea 100644 --- a/net/netfilter/nf_conntrack_helper.c +++ b/net/netfilter/nf_conntrack_helper.c @@ -26,7 +26,9 @@ #include #include #include +#include #include +#include static DEFINE_MUTEX(nf_ct_helper_mutex); struct hlist_head *nf_ct_helper_hash __read_mostly; @@ -240,6 +242,73 @@ int __nf_ct_try_assign_helper(struct nf_conn *ct, struct nf_conn *tmpl, } EXPORT_SYMBOL_GPL(__nf_ct_try_assign_helper); +/* 'skb' should already be pulled to nh_ofs. */ +int nf_ct_helper(struct sk_buff *skb, struct nf_conn *ct, + enum ip_conntrack_info ctinfo, u16 proto) +{ + const struct nf_conntrack_helper *helper; + const struct nf_conn_help *help; + unsigned int protoff; + int err; + + if (ctinfo == IP_CT_RELATED_REPLY) + return NF_ACCEPT; + + help = nfct_help(ct); + if (!help) + return NF_ACCEPT; + + helper = rcu_dereference(help->helper); + if (!helper) + return NF_ACCEPT; + + if (helper->tuple.src.l3num != NFPROTO_UNSPEC && + helper->tuple.src.l3num != proto) + return NF_ACCEPT; + + switch (proto) { + case NFPROTO_IPV4: + protoff = ip_hdrlen(skb); + proto = ip_hdr(skb)->protocol; + break; + case NFPROTO_IPV6: { + u8 nexthdr = ipv6_hdr(skb)->nexthdr; + __be16 frag_off; + int ofs; + + ofs = ipv6_skip_exthdr(skb, sizeof(struct ipv6hdr), &nexthdr, + &frag_off); + if (ofs < 0 || (frag_off & htons(~0x7)) != 0) { + pr_debug("proto header not found\n"); + return NF_ACCEPT; + } + protoff = ofs; + proto = nexthdr; + break; + } + default: + WARN_ONCE(1, "helper invoked on non-IP family!"); + return NF_DROP; + } + + if (helper->tuple.dst.protonum != proto) + return NF_ACCEPT; + + err = helper->help(skb, protoff, ct, ctinfo); + if (err != NF_ACCEPT) + return err; + + /* Adjust seqs after helper. This is needed due to some helpers (e.g., + * FTP with NAT) adusting the TCP payload size when mangling IP + * addresses and/or port numbers in the text-based control connection. + */ + if (test_bit(IPS_SEQ_ADJUST_BIT, &ct->status) && + !nf_ct_seq_adjust(skb, ct, ctinfo, protoff)) + return NF_DROP; + return NF_ACCEPT; +} +EXPORT_SYMBOL_GPL(nf_ct_helper); + /* appropriate ct lock protecting must be taken by caller */ static int unhelp(struct nf_conn *ct, void *me) { diff --git a/net/openvswitch/conntrack.c b/net/openvswitch/conntrack.c index c7b10234cf7c..18f54fa38e8f 100644 --- a/net/openvswitch/conntrack.c +++ b/net/openvswitch/conntrack.c @@ -434,65 +434,6 @@ static int ovs_ct_set_labels(struct nf_conn *ct, struct sw_flow_key *key, return 0; } -/* 'skb' should already be pulled to nh_ofs. */ -static int ovs_ct_helper(struct sk_buff *skb, u16 proto) -{ - const struct nf_conntrack_helper *helper; - const struct nf_conn_help *help; - enum ip_conntrack_info ctinfo; - unsigned int protoff; - struct nf_conn *ct; - int err; - - ct = nf_ct_get(skb, &ctinfo); - if (!ct || ctinfo == IP_CT_RELATED_REPLY) - return NF_ACCEPT; - - help = nfct_help(ct); - if (!help) - return NF_ACCEPT; - - helper = rcu_dereference(help->helper); - if (!helper) - return NF_ACCEPT; - - switch (proto) { - case NFPROTO_IPV4: - protoff = ip_hdrlen(skb); - break; - case NFPROTO_IPV6: { - u8 nexthdr = ipv6_hdr(skb)->nexthdr; - __be16 frag_off; - int ofs; - - ofs = ipv6_skip_exthdr(skb, sizeof(struct ipv6hdr), &nexthdr, - &frag_off); - if (ofs < 0 || (frag_off & htons(~0x7)) != 0) { - pr_debug("proto header not found\n"); - return NF_ACCEPT; - } - protoff = ofs; - break; - } - default: - WARN_ONCE(1, "helper invoked on non-IP family!"); - return NF_DROP; - } - - err = helper->help(skb, protoff, ct, ctinfo); - if (err != NF_ACCEPT) - return err; - - /* Adjust seqs after helper. This is needed due to some helpers (e.g., - * FTP with NAT) adusting the TCP payload size when mangling IP - * addresses and/or port numbers in the text-based control connection. - */ - if (test_bit(IPS_SEQ_ADJUST_BIT, &ct->status) && - !nf_ct_seq_adjust(skb, ct, ctinfo, protoff)) - return NF_DROP; - return NF_ACCEPT; -} - /* Returns 0 on success, -EINPROGRESS if 'skb' is stolen, or other nonzero * value if 'skb' is freed. */ @@ -1038,7 +979,7 @@ static int __ovs_ct_lookup(struct net *net, struct sw_flow_key *key, */ if ((nf_ct_is_confirmed(ct) ? !cached || add_helper : info->commit) && - ovs_ct_helper(skb, info->family) != NF_ACCEPT) { + nf_ct_helper(skb, ct, ctinfo, info->family) != NF_ACCEPT) { return -EINVAL; } From f96cba2eb923c025014fe74a50e104b7c5234feb Mon Sep 17 00:00:00 2001 From: Xin Long Date: Sun, 6 Nov 2022 15:34:15 -0500 Subject: [PATCH 0710/2002] net: move add ct helper function to nf_conntrack_helper for ovs and tc Move ovs_ct_add_helper from openvswitch to nf_conntrack_helper and rename as nf_ct_add_helper, so that it can be used in TC act_ct in the next patch. Acked-by: Marcelo Ricardo Leitner Signed-off-by: Xin Long Signed-off-by: Paolo Abeni --- include/net/netfilter/nf_conntrack_helper.h | 2 + net/netfilter/nf_conntrack_helper.c | 31 +++++++++++++++ net/openvswitch/conntrack.c | 44 +++------------------ 3 files changed, 38 insertions(+), 39 deletions(-) diff --git a/include/net/netfilter/nf_conntrack_helper.h b/include/net/netfilter/nf_conntrack_helper.h index b6676249eeeb..f30b1694b690 100644 --- a/include/net/netfilter/nf_conntrack_helper.h +++ b/include/net/netfilter/nf_conntrack_helper.h @@ -117,6 +117,8 @@ int __nf_ct_try_assign_helper(struct nf_conn *ct, struct nf_conn *tmpl, int nf_ct_helper(struct sk_buff *skb, struct nf_conn *ct, enum ip_conntrack_info ctinfo, u16 proto); +int nf_ct_add_helper(struct nf_conn *ct, const char *name, u8 family, + u8 proto, bool nat, struct nf_conntrack_helper **hp); void nf_ct_helper_destroy(struct nf_conn *ct); diff --git a/net/netfilter/nf_conntrack_helper.c b/net/netfilter/nf_conntrack_helper.c index 88039eedadea..48ea6d0264b5 100644 --- a/net/netfilter/nf_conntrack_helper.c +++ b/net/netfilter/nf_conntrack_helper.c @@ -309,6 +309,37 @@ int nf_ct_helper(struct sk_buff *skb, struct nf_conn *ct, } EXPORT_SYMBOL_GPL(nf_ct_helper); +int nf_ct_add_helper(struct nf_conn *ct, const char *name, u8 family, + u8 proto, bool nat, struct nf_conntrack_helper **hp) +{ + struct nf_conntrack_helper *helper; + struct nf_conn_help *help; + int ret = 0; + + helper = nf_conntrack_helper_try_module_get(name, family, proto); + if (!helper) + return -EINVAL; + + help = nf_ct_helper_ext_add(ct, GFP_KERNEL); + if (!help) { + nf_conntrack_helper_put(helper); + return -ENOMEM; + } +#if IS_ENABLED(CONFIG_NF_NAT) + if (nat) { + ret = nf_nat_helper_try_module_get(name, family, proto); + if (ret) { + nf_conntrack_helper_put(helper); + return ret; + } + } +#endif + rcu_assign_pointer(help->helper, helper); + *hp = helper; + return ret; +} +EXPORT_SYMBOL_GPL(nf_ct_add_helper); + /* appropriate ct lock protecting must be taken by caller */ static int unhelp(struct nf_conn *ct, void *me) { diff --git a/net/openvswitch/conntrack.c b/net/openvswitch/conntrack.c index 18f54fa38e8f..4348321856af 100644 --- a/net/openvswitch/conntrack.c +++ b/net/openvswitch/conntrack.c @@ -1291,43 +1291,6 @@ int ovs_ct_clear(struct sk_buff *skb, struct sw_flow_key *key) return 0; } -static int ovs_ct_add_helper(struct ovs_conntrack_info *info, const char *name, - const struct sw_flow_key *key, bool log) -{ - struct nf_conntrack_helper *helper; - struct nf_conn_help *help; - int ret = 0; - - helper = nf_conntrack_helper_try_module_get(name, info->family, - key->ip.proto); - if (!helper) { - OVS_NLERR(log, "Unknown helper \"%s\"", name); - return -EINVAL; - } - - help = nf_ct_helper_ext_add(info->ct, GFP_KERNEL); - if (!help) { - nf_conntrack_helper_put(helper); - return -ENOMEM; - } - -#if IS_ENABLED(CONFIG_NF_NAT) - if (info->nat) { - ret = nf_nat_helper_try_module_get(name, info->family, - key->ip.proto); - if (ret) { - nf_conntrack_helper_put(helper); - OVS_NLERR(log, "Failed to load \"%s\" NAT helper, error: %d", - name, ret); - return ret; - } - } -#endif - rcu_assign_pointer(help->helper, helper); - info->helper = helper; - return ret; -} - #if IS_ENABLED(CONFIG_NF_NAT) static int parse_nat(const struct nlattr *attr, struct ovs_conntrack_info *info, bool log) @@ -1661,9 +1624,12 @@ int ovs_ct_copy_action(struct net *net, const struct nlattr *attr, } if (helper) { - err = ovs_ct_add_helper(&ct_info, helper, key, log); - if (err) + err = nf_ct_add_helper(ct_info.ct, helper, ct_info.family, + key->ip.proto, ct_info.nat, &ct_info.helper); + if (err) { + OVS_NLERR(log, "Failed to add %s helper %d", helper, err); goto err_free_ct; + } } err = ovs_nla_add_action(sfa, OVS_ACTION_ATTR_CT, &ct_info, From 1913894100ca53205f2d56091cb34b8eba1de217 Mon Sep 17 00:00:00 2001 From: Xin Long Date: Sun, 6 Nov 2022 15:34:16 -0500 Subject: [PATCH 0711/2002] net: sched: call tcf_ct_params_free to free params in tcf_ct_init This patch is to make the err path simple by calling tcf_ct_params_free(), so that it won't cause problems when more members are added into param and need freeing on the err path. Acked-by: Marcelo Ricardo Leitner Signed-off-by: Xin Long Signed-off-by: Paolo Abeni --- net/sched/act_ct.c | 35 ++++++++++++++++++----------------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/net/sched/act_ct.c b/net/sched/act_ct.c index b38d91d6b249..193a460a9d7f 100644 --- a/net/sched/act_ct.c +++ b/net/sched/act_ct.c @@ -345,11 +345,9 @@ static void tcf_ct_flow_table_cleanup_work(struct work_struct *work) module_put(THIS_MODULE); } -static void tcf_ct_flow_table_put(struct tcf_ct_params *params) +static void tcf_ct_flow_table_put(struct tcf_ct_flow_table *ct_ft) { - struct tcf_ct_flow_table *ct_ft = params->ct_ft; - - if (refcount_dec_and_test(¶ms->ct_ft->ref)) { + if (refcount_dec_and_test(&ct_ft->ref)) { rhashtable_remove_fast(&zones_ht, &ct_ft->node, zones_params); INIT_RCU_WORK(&ct_ft->rwork, tcf_ct_flow_table_cleanup_work); queue_rcu_work(act_ct_wq, &ct_ft->rwork); @@ -832,18 +830,23 @@ out_free: return err; } -static void tcf_ct_params_free(struct rcu_head *head) +static void tcf_ct_params_free(struct tcf_ct_params *params) { - struct tcf_ct_params *params = container_of(head, - struct tcf_ct_params, rcu); - - tcf_ct_flow_table_put(params); - + if (params->ct_ft) + tcf_ct_flow_table_put(params->ct_ft); if (params->tmpl) nf_ct_put(params->tmpl); kfree(params); } +static void tcf_ct_params_free_rcu(struct rcu_head *head) +{ + struct tcf_ct_params *params; + + params = container_of(head, struct tcf_ct_params, rcu); + tcf_ct_params_free(params); +} + #if IS_ENABLED(CONFIG_NF_NAT) /* Modelled after nf_nat_ipv[46]_fn(). * range is only used for new, uninitialized NAT state. @@ -1390,7 +1393,7 @@ static int tcf_ct_init(struct net *net, struct nlattr *nla, err = tcf_ct_flow_table_get(net, params); if (err) - goto cleanup_params; + goto cleanup; spin_lock_bh(&c->tcf_lock); goto_ch = tcf_action_set_ctrlact(*a, parm->action, goto_ch); @@ -1401,17 +1404,15 @@ static int tcf_ct_init(struct net *net, struct nlattr *nla, if (goto_ch) tcf_chain_put_by_act(goto_ch); if (params) - call_rcu(¶ms->rcu, tcf_ct_params_free); + call_rcu(¶ms->rcu, tcf_ct_params_free_rcu); return res; -cleanup_params: - if (params->tmpl) - nf_ct_put(params->tmpl); cleanup: if (goto_ch) tcf_chain_put_by_act(goto_ch); - kfree(params); + if (params) + tcf_ct_params_free(params); tcf_idr_release(*a, bind); return err; } @@ -1423,7 +1424,7 @@ static void tcf_ct_cleanup(struct tc_action *a) params = rcu_dereference_protected(c->params, 1); if (params) - call_rcu(¶ms->rcu, tcf_ct_params_free); + call_rcu(¶ms->rcu, tcf_ct_params_free_rcu); } static int tcf_ct_dump_key_val(struct sk_buff *skb, From a21b06e7319129994f339ed47f512bbe57b77f5b Mon Sep 17 00:00:00 2001 From: Xin Long Date: Sun, 6 Nov 2022 15:34:17 -0500 Subject: [PATCH 0712/2002] net: sched: add helper support in act_ct This patch is to add helper support in act_ct for OVS actions=ct(alg=xxx) offloading, which is corresponding to Commit cae3a2627520 ("openvswitch: Allow attaching helpers to ct action") in OVS kernel part. The difference is when adding TC actions family and proto cannot be got from the filter/match, other than helper name in tb[TCA_CT_HELPER_NAME], we also need to send the family in tb[TCA_CT_HELPER_FAMILY] and the proto in tb[TCA_CT_HELPER_PROTO] to kernel. Acked-by: Marcelo Ricardo Leitner Signed-off-by: Xin Long Signed-off-by: Paolo Abeni --- include/net/tc_act/tc_ct.h | 1 + include/uapi/linux/tc_act/tc_ct.h | 3 ++ net/sched/act_ct.c | 89 ++++++++++++++++++++++++++++--- 3 files changed, 85 insertions(+), 8 deletions(-) diff --git a/include/net/tc_act/tc_ct.h b/include/net/tc_act/tc_ct.h index 8250d6f0a462..b24ea2d9400b 100644 --- a/include/net/tc_act/tc_ct.h +++ b/include/net/tc_act/tc_ct.h @@ -10,6 +10,7 @@ #include struct tcf_ct_params { + struct nf_conntrack_helper *helper; struct nf_conn *tmpl; u16 zone; diff --git a/include/uapi/linux/tc_act/tc_ct.h b/include/uapi/linux/tc_act/tc_ct.h index 5fb1d7ac1027..6c5200f0ed38 100644 --- a/include/uapi/linux/tc_act/tc_ct.h +++ b/include/uapi/linux/tc_act/tc_ct.h @@ -22,6 +22,9 @@ enum { TCA_CT_NAT_PORT_MIN, /* be16 */ TCA_CT_NAT_PORT_MAX, /* be16 */ TCA_CT_PAD, + TCA_CT_HELPER_NAME, /* string */ + TCA_CT_HELPER_FAMILY, /* u8 */ + TCA_CT_HELPER_PROTO, /* u8 */ __TCA_CT_MAX }; diff --git a/net/sched/act_ct.c b/net/sched/act_ct.c index 193a460a9d7f..da0b7f665277 100644 --- a/net/sched/act_ct.c +++ b/net/sched/act_ct.c @@ -33,6 +33,7 @@ #include #include #include +#include #include static struct workqueue_struct *act_ct_wq; @@ -655,7 +656,7 @@ struct tc_ct_action_net { /* Determine whether skb->_nfct is equal to the result of conntrack lookup. */ static bool tcf_ct_skb_nfct_cached(struct net *net, struct sk_buff *skb, - u16 zone_id, bool force) + struct tcf_ct_params *p) { enum ip_conntrack_info ctinfo; struct nf_conn *ct; @@ -665,11 +666,19 @@ static bool tcf_ct_skb_nfct_cached(struct net *net, struct sk_buff *skb, return false; if (!net_eq(net, read_pnet(&ct->ct_net))) goto drop_ct; - if (nf_ct_zone(ct)->id != zone_id) + if (nf_ct_zone(ct)->id != p->zone) goto drop_ct; + if (p->helper) { + struct nf_conn_help *help; + + help = nf_ct_ext_find(ct, NF_CT_EXT_HELPER); + if (help && rcu_access_pointer(help->helper) != p->helper) + goto drop_ct; + } /* Force conntrack entry direction. */ - if (force && CTINFO2DIR(ctinfo) != IP_CT_DIR_ORIGINAL) { + if ((p->ct_action & TCA_CT_ACT_FORCE) && + CTINFO2DIR(ctinfo) != IP_CT_DIR_ORIGINAL) { if (nf_ct_is_confirmed(ct)) nf_ct_kill(ct); @@ -832,6 +841,13 @@ out_free: static void tcf_ct_params_free(struct tcf_ct_params *params) { + if (params->helper) { +#if IS_ENABLED(CONFIG_NF_NAT) + if (params->ct_action & TCA_CT_ACT_NAT) + nf_nat_helper_put(params->helper); +#endif + nf_conntrack_helper_put(params->helper); + } if (params->ct_ft) tcf_ct_flow_table_put(params->ct_ft); if (params->tmpl) @@ -1026,13 +1042,14 @@ static int tcf_ct_act(struct sk_buff *skb, const struct tc_action *a, struct tcf_result *res) { struct net *net = dev_net(skb->dev); - bool cached, commit, clear, force; enum ip_conntrack_info ctinfo; struct tcf_ct *c = to_ct(a); struct nf_conn *tmpl = NULL; struct nf_hook_state state; + bool cached, commit, clear; int nh_ofs, err, retval; struct tcf_ct_params *p; + bool add_helper = false; bool skip_add = false; bool defrag = false; struct nf_conn *ct; @@ -1043,7 +1060,6 @@ static int tcf_ct_act(struct sk_buff *skb, const struct tc_action *a, retval = READ_ONCE(c->tcf_action); commit = p->ct_action & TCA_CT_ACT_COMMIT; clear = p->ct_action & TCA_CT_ACT_CLEAR; - force = p->ct_action & TCA_CT_ACT_FORCE; tmpl = p->tmpl; tcf_lastuse_update(&c->tcf_tm); @@ -1086,7 +1102,7 @@ static int tcf_ct_act(struct sk_buff *skb, const struct tc_action *a, * actually run the packet through conntrack twice unless it's for a * different zone. */ - cached = tcf_ct_skb_nfct_cached(net, skb, p->zone, force); + cached = tcf_ct_skb_nfct_cached(net, skb, p); if (!cached) { if (tcf_ct_flow_table_lookup(p, skb, family)) { skip_add = true; @@ -1119,6 +1135,22 @@ do_nat: if (err != NF_ACCEPT) goto drop; + if (!nf_ct_is_confirmed(ct) && commit && p->helper && !nfct_help(ct)) { + err = __nf_ct_try_assign_helper(ct, p->tmpl, GFP_ATOMIC); + if (err) + goto drop; + add_helper = true; + if (p->ct_action & TCA_CT_ACT_NAT && !nfct_seqadj(ct)) { + if (!nfct_seqadj_ext_add(ct)) + goto drop; + } + } + + if (nf_ct_is_confirmed(ct) ? ((!cached && !skip_add) || add_helper) : commit) { + if (nf_ct_helper(skb, ct, ctinfo, family) != NF_ACCEPT) + goto drop; + } + if (commit) { tcf_ct_act_set_mark(ct, p->mark, p->mark_mask); tcf_ct_act_set_labels(ct, p->labels, p->labels_mask); @@ -1167,6 +1199,9 @@ static const struct nla_policy ct_policy[TCA_CT_MAX + 1] = { [TCA_CT_NAT_IPV6_MAX] = NLA_POLICY_EXACT_LEN(sizeof(struct in6_addr)), [TCA_CT_NAT_PORT_MIN] = { .type = NLA_U16 }, [TCA_CT_NAT_PORT_MAX] = { .type = NLA_U16 }, + [TCA_CT_HELPER_NAME] = { .type = NLA_STRING, .len = NF_CT_HELPER_NAME_LEN }, + [TCA_CT_HELPER_FAMILY] = { .type = NLA_U8 }, + [TCA_CT_HELPER_PROTO] = { .type = NLA_U8 }, }; static int tcf_ct_fill_params_nat(struct tcf_ct_params *p, @@ -1256,8 +1291,9 @@ static int tcf_ct_fill_params(struct net *net, { struct tc_ct_action_net *tn = net_generic(net, act_ct_ops.net_id); struct nf_conntrack_zone zone; + int err, family, proto, len; struct nf_conn *tmpl; - int err; + char *name; p->zone = NF_CT_DEFAULT_ZONE_ID; @@ -1318,10 +1354,31 @@ static int tcf_ct_fill_params(struct net *net, NL_SET_ERR_MSG_MOD(extack, "Failed to allocate conntrack template"); return -ENOMEM; } - __set_bit(IPS_CONFIRMED_BIT, &tmpl->status); p->tmpl = tmpl; + if (tb[TCA_CT_HELPER_NAME]) { + name = nla_data(tb[TCA_CT_HELPER_NAME]); + len = nla_len(tb[TCA_CT_HELPER_NAME]); + if (len > 16 || name[len - 1] != '\0') { + NL_SET_ERR_MSG_MOD(extack, "Failed to parse helper name."); + err = -EINVAL; + goto err; + } + family = tb[TCA_CT_HELPER_FAMILY] ? nla_get_u8(tb[TCA_CT_HELPER_FAMILY]) : AF_INET; + proto = tb[TCA_CT_HELPER_PROTO] ? nla_get_u8(tb[TCA_CT_HELPER_PROTO]) : IPPROTO_TCP; + err = nf_ct_add_helper(tmpl, name, family, proto, + p->ct_action & TCA_CT_ACT_NAT, &p->helper); + if (err) { + NL_SET_ERR_MSG_MOD(extack, "Failed to add helper"); + goto err; + } + } + __set_bit(IPS_CONFIRMED_BIT, &tmpl->status); return 0; +err: + nf_ct_put(p->tmpl); + p->tmpl = NULL; + return err; } static int tcf_ct_init(struct net *net, struct nlattr *nla, @@ -1490,6 +1547,19 @@ static int tcf_ct_dump_nat(struct sk_buff *skb, struct tcf_ct_params *p) return 0; } +static int tcf_ct_dump_helper(struct sk_buff *skb, struct nf_conntrack_helper *helper) +{ + if (!helper) + return 0; + + if (nla_put_string(skb, TCA_CT_HELPER_NAME, helper->name) || + nla_put_u8(skb, TCA_CT_HELPER_FAMILY, helper->tuple.src.l3num) || + nla_put_u8(skb, TCA_CT_HELPER_PROTO, helper->tuple.dst.protonum)) + return -1; + + return 0; +} + static inline int tcf_ct_dump(struct sk_buff *skb, struct tc_action *a, int bind, int ref) { @@ -1542,6 +1612,9 @@ static inline int tcf_ct_dump(struct sk_buff *skb, struct tc_action *a, if (tcf_ct_dump_nat(skb, p)) goto nla_put_failure; + if (tcf_ct_dump_helper(skb, p->helper)) + goto nla_put_failure; + skip_dump: if (nla_put(skb, TCA_CT_PARMS, sizeof(opt), &opt)) goto nla_put_failure; From 41d2dd42bfa17035c3b0429b6b0e46305607fcc7 Mon Sep 17 00:00:00 2001 From: Edwin Peer Date: Sun, 6 Nov 2022 19:16:30 -0500 Subject: [PATCH 0713/2002] bnxt_en: refactor VNIC RSS update functions Extract common code into a new function. This will avoid duplication in the next patch, which changes the update algorithm for both the P5 and legacy code paths. No functional changes. Signed-off-by: Edwin Peer Signed-off-by: Michael Chan Reviewed-by: Leon Romanovsky Signed-off-by: Paolo Abeni --- drivers/net/ethernet/broadcom/bnxt/bnxt.c | 34 +++++++++++------------ 1 file changed, 16 insertions(+), 18 deletions(-) diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c index 03272aa48511..15edb6cb3656 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c @@ -5250,7 +5250,7 @@ int bnxt_get_nr_rss_ctxs(struct bnxt *bp, int rx_rings) return 1; } -static void __bnxt_fill_hw_rss_tbl(struct bnxt *bp, struct bnxt_vnic_info *vnic) +static void bnxt_fill_hw_rss_tbl(struct bnxt *bp, struct bnxt_vnic_info *vnic) { bool no_rss = !(vnic->flags & BNXT_VNIC_RSS_FLAG); u16 i, j; @@ -5263,8 +5263,8 @@ static void __bnxt_fill_hw_rss_tbl(struct bnxt *bp, struct bnxt_vnic_info *vnic) } } -static void __bnxt_fill_hw_rss_tbl_p5(struct bnxt *bp, - struct bnxt_vnic_info *vnic) +static void bnxt_fill_hw_rss_tbl_p5(struct bnxt *bp, + struct bnxt_vnic_info *vnic) { __le16 *ring_tbl = vnic->rss_table; struct bnxt_rx_ring_info *rxr; @@ -5285,12 +5285,19 @@ static void __bnxt_fill_hw_rss_tbl_p5(struct bnxt *bp, } } -static void bnxt_fill_hw_rss_tbl(struct bnxt *bp, struct bnxt_vnic_info *vnic) +static void +__bnxt_hwrm_vnic_set_rss(struct bnxt *bp, struct hwrm_vnic_rss_cfg_input *req, + struct bnxt_vnic_info *vnic) { if (bp->flags & BNXT_FLAG_CHIP_P5) - __bnxt_fill_hw_rss_tbl_p5(bp, vnic); + bnxt_fill_hw_rss_tbl_p5(bp, vnic); else - __bnxt_fill_hw_rss_tbl(bp, vnic); + bnxt_fill_hw_rss_tbl(bp, vnic); + + req->hash_type = cpu_to_le32(bp->rss_hash_cfg); + req->hash_mode_flags = VNIC_RSS_CFG_REQ_HASH_MODE_FLAGS_DEFAULT; + req->ring_grp_tbl_addr = cpu_to_le64(vnic->rss_table_dma_addr); + req->hash_key_tbl_addr = cpu_to_le64(vnic->rss_hash_key_dma_addr); } static int bnxt_hwrm_vnic_set_rss(struct bnxt *bp, u16 vnic_id, bool set_rss) @@ -5307,14 +5314,8 @@ static int bnxt_hwrm_vnic_set_rss(struct bnxt *bp, u16 vnic_id, bool set_rss) if (rc) return rc; - if (set_rss) { - bnxt_fill_hw_rss_tbl(bp, vnic); - req->hash_type = cpu_to_le32(bp->rss_hash_cfg); - req->hash_mode_flags = VNIC_RSS_CFG_REQ_HASH_MODE_FLAGS_DEFAULT; - req->ring_grp_tbl_addr = cpu_to_le64(vnic->rss_table_dma_addr); - req->hash_key_tbl_addr = - cpu_to_le64(vnic->rss_hash_key_dma_addr); - } + if (set_rss) + __bnxt_hwrm_vnic_set_rss(bp, req, vnic); req->rss_ctx_idx = cpu_to_le16(vnic->fw_rss_cos_lb_ctx[0]); return hwrm_req_send(bp, req); } @@ -5335,10 +5336,7 @@ static int bnxt_hwrm_vnic_set_rss_p5(struct bnxt *bp, u16 vnic_id, bool set_rss) if (!set_rss) return hwrm_req_send(bp, req); - bnxt_fill_hw_rss_tbl(bp, vnic); - req->hash_type = cpu_to_le32(bp->rss_hash_cfg); - req->hash_mode_flags = VNIC_RSS_CFG_REQ_HASH_MODE_FLAGS_DEFAULT; - req->hash_key_tbl_addr = cpu_to_le64(vnic->rss_hash_key_dma_addr); + __bnxt_hwrm_vnic_set_rss(bp, req, vnic); ring_tbl_map = vnic->rss_table_dma_addr; nr_ctxs = bnxt_get_nr_rss_ctxs(bp, bp->rx_nr_rings); From 98a4322b70e817f0663adb61b8272f7b995ed41a Mon Sep 17 00:00:00 2001 From: Edwin Peer Date: Sun, 6 Nov 2022 19:16:31 -0500 Subject: [PATCH 0714/2002] bnxt_en: update RSS config using difference algorithm Hardware is unable to realize all legal firmware interface state values for hash_type. For example, if 4-tuple TCP_IPV4 hash is enabled, 4-tuple UDP_IPV4 hash must also be enabled. By providing the bits the user intended to change instead of the possible illegal intermediate states, the firmware is able to make better compromises when deciding which bits to ignore. With this new mechansim, we can now report the actual configured hash back to the user. Add bnxt_hwrm_update_rss_hash_cfg() to report the actual hash after user configuration. Signed-off-by: Edwin Peer Signed-off-by: Michael Chan Reviewed-by: Leon Romanovsky Signed-off-by: Paolo Abeni --- drivers/net/ethernet/broadcom/bnxt/bnxt.c | 35 +++++++++++++- drivers/net/ethernet/broadcom/bnxt/bnxt.h | 2 + .../net/ethernet/broadcom/bnxt/bnxt_ethtool.c | 2 + drivers/net/ethernet/broadcom/bnxt/bnxt_hsi.h | 47 +++++++++++++++++++ 4 files changed, 85 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c index 15edb6cb3656..d18e55b16375 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c @@ -5294,7 +5294,15 @@ __bnxt_hwrm_vnic_set_rss(struct bnxt *bp, struct hwrm_vnic_rss_cfg_input *req, else bnxt_fill_hw_rss_tbl(bp, vnic); - req->hash_type = cpu_to_le32(bp->rss_hash_cfg); + if (bp->rss_hash_delta) { + req->hash_type = cpu_to_le32(bp->rss_hash_delta); + if (bp->rss_hash_cfg & bp->rss_hash_delta) + req->flags |= VNIC_RSS_CFG_REQ_FLAGS_HASH_TYPE_INCLUDE; + else + req->flags |= VNIC_RSS_CFG_REQ_FLAGS_HASH_TYPE_EXCLUDE; + } else { + req->hash_type = cpu_to_le32(bp->rss_hash_cfg); + } req->hash_mode_flags = VNIC_RSS_CFG_REQ_HASH_MODE_FLAGS_DEFAULT; req->ring_grp_tbl_addr = cpu_to_le64(vnic->rss_table_dma_addr); req->hash_key_tbl_addr = cpu_to_le64(vnic->rss_hash_key_dma_addr); @@ -5355,6 +5363,25 @@ exit: return rc; } +static void bnxt_hwrm_update_rss_hash_cfg(struct bnxt *bp) +{ + struct bnxt_vnic_info *vnic = &bp->vnic_info[0]; + struct hwrm_vnic_rss_qcfg_output *resp; + struct hwrm_vnic_rss_qcfg_input *req; + + if (hwrm_req_init(bp, req, HWRM_VNIC_RSS_QCFG)) + return; + + /* all contexts configured to same hash_type, zero always exists */ + req->rss_ctx_idx = cpu_to_le16(vnic->fw_rss_cos_lb_ctx[0]); + resp = hwrm_req_hold(bp, req); + if (!hwrm_req_send(bp, req)) { + bp->rss_hash_cfg = le32_to_cpu(resp->hash_type) ?: bp->rss_hash_cfg; + bp->rss_hash_delta = 0; + } + hwrm_req_drop(bp, req); +} + static int bnxt_hwrm_vnic_set_hds(struct bnxt *bp, u16 vnic_id) { struct bnxt_vnic_info *vnic = &bp->vnic_info[vnic_id]; @@ -5612,6 +5639,8 @@ static int bnxt_hwrm_vnic_qcaps(struct bnxt *bp) (BNXT_CHIP_P5_THOR(bp) && !(bp->fw_cap & BNXT_FW_CAP_EXT_HW_STATS_SUPPORTED))) bp->fw_cap |= BNXT_FW_CAP_VLAN_RX_STRIP; + if (flags & VNIC_QCAPS_RESP_FLAGS_RSS_HASH_TYPE_DELTA_CAP) + bp->fw_cap |= BNXT_FW_CAP_RSS_HASH_TYPE_DELTA; bp->max_tpa_v2 = le16_to_cpu(resp->max_aggs_supported); if (bp->max_tpa_v2) { if (BNXT_CHIP_P5_THOR(bp)) @@ -8806,6 +8835,8 @@ static int bnxt_init_chip(struct bnxt *bp, bool irq_re_init) rc = bnxt_setup_vnic(bp, 0); if (rc) goto err_out; + if (bp->fw_cap & BNXT_FW_CAP_RSS_HASH_TYPE_DELTA) + bnxt_hwrm_update_rss_hash_cfg(bp); if (bp->flags & BNXT_FLAG_RFS) { rc = bnxt_alloc_rfs_vnics(bp); @@ -12241,6 +12272,8 @@ static void bnxt_set_dflt_rss_hash_type(struct bnxt *bp) VNIC_RSS_CFG_REQ_HASH_TYPE_TCP_IPV4 | VNIC_RSS_CFG_REQ_HASH_TYPE_IPV6 | VNIC_RSS_CFG_REQ_HASH_TYPE_TCP_IPV6; + if (bp->fw_cap & BNXT_FW_CAP_RSS_HASH_TYPE_DELTA) + bp->rss_hash_delta = bp->rss_hash_cfg; if (BNXT_CHIP_P4_PLUS(bp) && bp->hwrm_spec_code >= 0x10501) { bp->flags |= BNXT_FLAG_UDP_RSS_CAP; bp->rss_hash_cfg |= VNIC_RSS_CFG_REQ_HASH_TYPE_UDP_IPV4 | diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.h b/drivers/net/ethernet/broadcom/bnxt/bnxt.h index 91a1ba0a914d..7611bed4a196 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.h +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.h @@ -1900,6 +1900,7 @@ struct bnxt { u16 *rss_indir_tbl; u16 rss_indir_tbl_entries; u32 rss_hash_cfg; + u32 rss_hash_delta; u16 max_mtu; u8 max_tc; @@ -1965,6 +1966,7 @@ struct bnxt { #define BNXT_FW_CAP_CFA_RFS_RING_TBL_IDX_V2 0x00010000 #define BNXT_FW_CAP_PCIE_STATS_SUPPORTED 0x00020000 #define BNXT_FW_CAP_EXT_STATS_SUPPORTED 0x00040000 + #define BNXT_FW_CAP_RSS_HASH_TYPE_DELTA 0x00080000 #define BNXT_FW_CAP_ERR_RECOVER_RELOAD 0x00100000 #define BNXT_FW_CAP_HOT_RESET 0x00200000 #define BNXT_FW_CAP_PTP_RTC 0x00400000 diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c index d8f0351df954..132e4f56816e 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c @@ -1234,6 +1234,8 @@ static int bnxt_srxfh(struct bnxt *bp, struct ethtool_rxnfc *cmd) if (bp->rss_hash_cfg == rss_hash_cfg) return 0; + if (bp->fw_cap & BNXT_FW_CAP_RSS_HASH_TYPE_DELTA) + bp->rss_hash_delta = bp->rss_hash_cfg ^ rss_hash_cfg; bp->rss_hash_cfg = rss_hash_cfg; if (netif_running(bp->dev)) { bnxt_close_nic(bp, false, false); diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_hsi.h b/drivers/net/ethernet/broadcom/bnxt/bnxt_hsi.h index 184dd8d11cd3..2686a714a59f 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_hsi.h +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_hsi.h @@ -6768,6 +6768,53 @@ struct hwrm_vnic_rss_cfg_cmd_err { u8 unused_0[7]; }; +/* hwrm_vnic_rss_qcfg_input (size:192b/24B) */ +struct hwrm_vnic_rss_qcfg_input { + __le16 req_type; + __le16 cmpl_ring; + __le16 seq_id; + __le16 target_id; + __le64 resp_addr; + __le16 rss_ctx_idx; + __le16 vnic_id; + u8 unused_0[4]; +}; + +/* hwrm_vnic_rss_qcfg_output (size:512b/64B) */ +struct hwrm_vnic_rss_qcfg_output { + __le16 error_code; + __le16 req_type; + __le16 seq_id; + __le16 resp_len; + __le32 hash_type; + #define VNIC_RSS_QCFG_RESP_HASH_TYPE_IPV4 0x1UL + #define VNIC_RSS_QCFG_RESP_HASH_TYPE_TCP_IPV4 0x2UL + #define VNIC_RSS_QCFG_RESP_HASH_TYPE_UDP_IPV4 0x4UL + #define VNIC_RSS_QCFG_RESP_HASH_TYPE_IPV6 0x8UL + #define VNIC_RSS_QCFG_RESP_HASH_TYPE_TCP_IPV6 0x10UL + #define VNIC_RSS_QCFG_RESP_HASH_TYPE_UDP_IPV6 0x20UL + #define VNIC_RSS_QCFG_RESP_HASH_TYPE_IPV6_FLOW_LABEL 0x40UL + #define VNIC_RSS_QCFG_RESP_HASH_TYPE_AH_SPI_IPV4 0x80UL + #define VNIC_RSS_QCFG_RESP_HASH_TYPE_ESP_SPI_IPV4 0x100UL + #define VNIC_RSS_QCFG_RESP_HASH_TYPE_AH_SPI_IPV6 0x200UL + #define VNIC_RSS_QCFG_RESP_HASH_TYPE_ESP_SPI_IPV6 0x400UL + u8 unused_0[4]; + __le32 hash_key[10]; + u8 hash_mode_flags; + #define VNIC_RSS_QCFG_RESP_HASH_MODE_FLAGS_DEFAULT 0x1UL + #define VNIC_RSS_QCFG_RESP_HASH_MODE_FLAGS_INNERMOST_4 0x2UL + #define VNIC_RSS_QCFG_RESP_HASH_MODE_FLAGS_INNERMOST_2 0x4UL + #define VNIC_RSS_QCFG_RESP_HASH_MODE_FLAGS_OUTERMOST_4 0x8UL + #define VNIC_RSS_QCFG_RESP_HASH_MODE_FLAGS_OUTERMOST_2 0x10UL + u8 ring_select_mode; + #define VNIC_RSS_QCFG_RESP_RING_SELECT_MODE_TOEPLITZ 0x0UL + #define VNIC_RSS_QCFG_RESP_RING_SELECT_MODE_XOR 0x1UL + #define VNIC_RSS_QCFG_RESP_RING_SELECT_MODE_TOEPLITZ_CHECKSUM 0x2UL + #define VNIC_RSS_QCFG_RESP_RING_SELECT_MODE_LAST VNIC_RSS_QCFG_RESP_RING_SELECT_MODE_TOEPLITZ_CHECKSUM + u8 unused_1[5]; + u8 valid; +}; + /* hwrm_vnic_plcmodes_cfg_input (size:320b/40B) */ struct hwrm_vnic_plcmodes_cfg_input { __le16 req_type; From 85036aee1938d65da4be6ae1bc7e5e7e30b567b9 Mon Sep 17 00:00:00 2001 From: Pavan Chebbi Date: Sun, 6 Nov 2022 19:16:32 -0500 Subject: [PATCH 0715/2002] bnxt_en: Add a non-real time mode to access NIC clock When using a PHC that is shared between multiple hosts, in order to achieve consistent timestamps across all hosts, we need to isolate the PHC from any host making frequency adjustments. This patch adds a non-real time mode for this purpose. The implementation is based on a free running NIC hardware timer which is used as the timestamper time-base. Each host implements individual adjustments to a local timecounter based on the NIC free running timer. Cc: Richard Cochran Signed-off-by: Pavan Chebbi Reviewed-by: Andy Gospodarek Signed-off-by: Michael Chan Reviewed-by: Leon Romanovsky Signed-off-by: Paolo Abeni --- drivers/net/ethernet/broadcom/bnxt/bnxt.c | 5 ++- drivers/net/ethernet/broadcom/bnxt/bnxt_ptp.c | 45 ++++++++++++++----- drivers/net/ethernet/broadcom/bnxt/bnxt_ptp.h | 7 ++- 3 files changed, 42 insertions(+), 15 deletions(-) diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c index d18e55b16375..d9ad325f7840 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c @@ -6985,8 +6985,11 @@ static int bnxt_hwrm_func_qcfg(struct bnxt *bp) if (flags & FUNC_QCFG_RESP_FLAGS_FW_DCBX_AGENT_ENABLED) bp->fw_cap |= BNXT_FW_CAP_DCBX_AGENT; } - if (BNXT_PF(bp) && (flags & FUNC_QCFG_RESP_FLAGS_MULTI_HOST)) + if (BNXT_PF(bp) && (flags & FUNC_QCFG_RESP_FLAGS_MULTI_HOST)) { bp->flags |= BNXT_FLAG_MULTI_HOST; + if (bp->fw_cap & BNXT_FW_CAP_PTP_RTC) + bp->fw_cap &= ~BNXT_FW_CAP_PTP_RTC; + } if (flags & FUNC_QCFG_RESP_FLAGS_RING_MONITOR_ENABLED) bp->fw_cap |= BNXT_FW_CAP_RING_MONITOR; diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_ptp.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_ptp.c index 2132ce63193c..460cb20599f6 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_ptp.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_ptp.c @@ -14,6 +14,7 @@ #include #include #include +#include #include "bnxt_hsi.h" #include "bnxt.h" #include "bnxt_hwrm.h" @@ -210,18 +211,37 @@ static int bnxt_ptp_adjfreq(struct ptp_clock_info *ptp_info, s32 ppb) ptp_info); struct hwrm_port_mac_cfg_input *req; struct bnxt *bp = ptp->bp; - int rc; + int rc = 0; - rc = hwrm_req_init(bp, req, HWRM_PORT_MAC_CFG); - if (rc) - return rc; + if (!(ptp->bp->fw_cap & BNXT_FW_CAP_PTP_RTC)) { + int neg_adj = 0; + u32 diff; + u64 adj; - req->ptp_freq_adj_ppb = cpu_to_le32(ppb); - req->enables = cpu_to_le32(PORT_MAC_CFG_REQ_ENABLES_PTP_FREQ_ADJ_PPB); - rc = hwrm_req_send(ptp->bp, req); - if (rc) - netdev_err(ptp->bp->dev, - "ptp adjfreq failed. rc = %d\n", rc); + if (ppb < 0) { + neg_adj = 1; + ppb = -ppb; + } + adj = ptp->cmult; + adj *= ppb; + diff = div_u64(adj, 1000000000ULL); + + spin_lock_bh(&ptp->ptp_lock); + timecounter_read(&ptp->tc); + ptp->cc.mult = neg_adj ? ptp->cmult - diff : ptp->cmult + diff; + spin_unlock_bh(&ptp->ptp_lock); + } else { + rc = hwrm_req_init(bp, req, HWRM_PORT_MAC_CFG); + if (rc) + return rc; + + req->ptp_freq_adj_ppb = cpu_to_le32(ppb); + req->enables = cpu_to_le32(PORT_MAC_CFG_REQ_ENABLES_PTP_FREQ_ADJ_PPB); + rc = hwrm_req_send(ptp->bp, req); + if (rc) + netdev_err(ptp->bp->dev, + "ptp adjfreq failed. rc = %d\n", rc); + } return rc; } @@ -846,8 +866,9 @@ static void bnxt_ptp_timecounter_init(struct bnxt *bp, bool init_tc) memset(&ptp->cc, 0, sizeof(ptp->cc)); ptp->cc.read = bnxt_cc_read; ptp->cc.mask = CYCLECOUNTER_MASK(48); - ptp->cc.shift = 0; - ptp->cc.mult = 1; + ptp->cc.shift = BNXT_CYCLES_SHIFT; + ptp->cc.mult = clocksource_khz2mult(BNXT_DEVCLK_FREQ, ptp->cc.shift); + ptp->cmult = ptp->cc.mult; ptp->next_overflow_check = jiffies + BNXT_PHC_OVERFLOW_PERIOD; } if (init_tc) diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_ptp.h b/drivers/net/ethernet/broadcom/bnxt/bnxt_ptp.h index 4ce0a14c1e23..34162e07a119 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_ptp.h +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_ptp.h @@ -17,6 +17,8 @@ #define BNXT_PTP_GRC_WIN_BASE 0x6000 #define BNXT_MAX_PHC_DRIFT 31000000 +#define BNXT_CYCLES_SHIFT 23 +#define BNXT_DEVCLK_FREQ 1000000 #define BNXT_LO_TIMER_MASK 0x0000ffffffffUL #define BNXT_HI_TIMER_MASK 0xffff00000000UL @@ -88,8 +90,9 @@ struct bnxt_ptp_cfg { u64 old_time; unsigned long next_period; unsigned long next_overflow_check; - /* 48-bit PHC overflows in 78 hours. Check overflow every 19 hours. */ - #define BNXT_PHC_OVERFLOW_PERIOD (19 * 3600 * HZ) + u32 cmult; + /* a 23b shift cyclecounter will overflow in ~36 mins. Check overflow every 18 mins. */ + #define BNXT_PHC_OVERFLOW_PERIOD (18 * 60 * HZ) u16 tx_seqid; u16 tx_hdr_off; From c3d96f690a790074b508fe183a41e36a00cd7ddd Mon Sep 17 00:00:00 2001 From: David Howells Date: Mon, 3 Oct 2022 07:34:21 +0100 Subject: [PATCH 0716/2002] net, proc: Provide PROC_FS=n fallback for proc_create_net_single_write() Provide a CONFIG_PROC_FS=n fallback for proc_create_net_single_write(). Also provide a fallback for proc_create_net_data_write(). Fixes: 564def71765c ("proc: Add a way to make network proc files writable") Reported-by: kernel test robot Signed-off-by: David Howells cc: Marc Dionne cc: linux-afs@lists.infradead.org cc: netdev@vger.kernel.org --- include/linux/proc_fs.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/linux/proc_fs.h b/include/linux/proc_fs.h index 81d6e4ec2294..0260f5ea98fe 100644 --- a/include/linux/proc_fs.h +++ b/include/linux/proc_fs.h @@ -208,8 +208,10 @@ static inline void proc_remove(struct proc_dir_entry *de) {} static inline int remove_proc_subtree(const char *name, struct proc_dir_entry *parent) { return 0; } #define proc_create_net_data(name, mode, parent, ops, state_size, data) ({NULL;}) +#define proc_create_net_data_write(name, mode, parent, ops, write, state_size, data) ({NULL;}) #define proc_create_net(name, mode, parent, state_size, ops) ({NULL;}) #define proc_create_net_single(name, mode, parent, show, data) ({NULL;}) +#define proc_create_net_single_write(name, mode, parent, show, write, data) ({NULL;}) static inline struct pid *tgid_pidfd_to_pid(const struct file *file) { From 4d843be56ba6a8c0e566afd58775742d9e721505 Mon Sep 17 00:00:00 2001 From: David Howells Date: Tue, 5 Apr 2022 21:48:48 +0100 Subject: [PATCH 0717/2002] rxrpc: Trace setting of the request-ack flag Add a tracepoint to log why the request-ack flag is set on an outgoing DATA packet, allowing debugging as to why. Signed-off-by: David Howells cc: Marc Dionne cc: linux-afs@lists.infradead.org --- include/trace/events/rxrpc.h | 36 ++++++++++++++++++++++++++++++++++++ net/rxrpc/output.c | 32 +++++++++++++++++++++++--------- 2 files changed, 59 insertions(+), 9 deletions(-) diff --git a/include/trace/events/rxrpc.h b/include/trace/events/rxrpc.h index d20bf4aa0204..4c501c660123 100644 --- a/include/trace/events/rxrpc.h +++ b/include/trace/events/rxrpc.h @@ -242,6 +242,16 @@ EM(rxrpc_tx_point_version_keepalive, "VerKeepalive") \ E_(rxrpc_tx_point_version_reply, "VerReply") +#define rxrpc_req_ack_traces \ + EM(rxrpc_reqack_ack_lost, "ACK-LOST ") \ + EM(rxrpc_reqack_already_on, "ALREADY-ON") \ + EM(rxrpc_reqack_more_rtt, "MORE-RTT ") \ + EM(rxrpc_reqack_no_srv_last, "NO-SRVLAST") \ + EM(rxrpc_reqack_old_rtt, "OLD-RTT ") \ + EM(rxrpc_reqack_retrans, "RETRANS ") \ + EM(rxrpc_reqack_slow_start, "SLOW-START") \ + E_(rxrpc_reqack_small_txwin, "SMALL-TXWN") + /* * Generate enums for tracing information. */ @@ -263,6 +273,7 @@ enum rxrpc_propose_ack_outcome { rxrpc_propose_ack_outcomes } __mode(byte); enum rxrpc_propose_ack_trace { rxrpc_propose_ack_traces } __mode(byte); enum rxrpc_receive_trace { rxrpc_receive_traces } __mode(byte); enum rxrpc_recvmsg_trace { rxrpc_recvmsg_traces } __mode(byte); +enum rxrpc_req_ack_trace { rxrpc_req_ack_traces } __mode(byte); enum rxrpc_rtt_rx_trace { rxrpc_rtt_rx_traces } __mode(byte); enum rxrpc_rtt_tx_trace { rxrpc_rtt_tx_traces } __mode(byte); enum rxrpc_skb_trace { rxrpc_skb_traces } __mode(byte); @@ -290,6 +301,7 @@ rxrpc_propose_ack_outcomes; rxrpc_propose_ack_traces; rxrpc_receive_traces; rxrpc_recvmsg_traces; +rxrpc_req_ack_traces; rxrpc_rtt_rx_traces; rxrpc_rtt_tx_traces; rxrpc_skb_traces; @@ -1395,6 +1407,30 @@ TRACE_EVENT(rxrpc_rx_discard_ack, __entry->call_ackr_prev) ); +TRACE_EVENT(rxrpc_req_ack, + TP_PROTO(unsigned int call_debug_id, rxrpc_seq_t seq, + enum rxrpc_req_ack_trace why), + + TP_ARGS(call_debug_id, seq, why), + + TP_STRUCT__entry( + __field(unsigned int, call_debug_id ) + __field(rxrpc_seq_t, seq ) + __field(enum rxrpc_req_ack_trace, why ) + ), + + TP_fast_assign( + __entry->call_debug_id = call_debug_id; + __entry->seq = seq; + __entry->why = why; + ), + + TP_printk("c=%08x q=%08x REQ-%s", + __entry->call_debug_id, + __entry->seq, + __print_symbolic(__entry->why, rxrpc_req_ack_traces)) + ); + #undef EM #undef E_ #endif /* _TRACE_RXRPC_H */ diff --git a/net/rxrpc/output.c b/net/rxrpc/output.c index 9683617db704..2922c10bd500 100644 --- a/net/rxrpc/output.c +++ b/net/rxrpc/output.c @@ -350,6 +350,7 @@ int rxrpc_send_abort_packet(struct rxrpc_call *call) int rxrpc_send_data_packet(struct rxrpc_call *call, struct sk_buff *skb, bool retrans) { + enum rxrpc_req_ack_trace why; struct rxrpc_connection *conn = call->conn; struct rxrpc_wire_header whdr; struct rxrpc_skb_priv *sp = rxrpc_skb(skb); @@ -405,16 +406,29 @@ int rxrpc_send_data_packet(struct rxrpc_call *call, struct sk_buff *skb, * service call, lest OpenAFS incorrectly send us an ACK with some * soft-ACKs in it and then never follow up with a proper hard ACK. */ - if ((!(sp->hdr.flags & RXRPC_LAST_PACKET) || - rxrpc_to_server(sp) - ) && - (test_and_clear_bit(RXRPC_CALL_EV_ACK_LOST, &call->events) || - retrans || - call->cong_mode == RXRPC_CALL_SLOW_START || - (call->peer->rtt_count < 3 && sp->hdr.seq & 1) || - ktime_before(ktime_add_ms(call->peer->rtt_last_req, 1000), - ktime_get_real()))) + if (whdr.flags & RXRPC_REQUEST_ACK) + why = rxrpc_reqack_already_on; + else if ((whdr.flags & RXRPC_LAST_PACKET) && rxrpc_to_client(sp)) + why = rxrpc_reqack_no_srv_last; + else if (test_and_clear_bit(RXRPC_CALL_EV_ACK_LOST, &call->events)) + why = rxrpc_reqack_ack_lost; + else if (retrans) + why = rxrpc_reqack_retrans; + else if (call->cong_mode == RXRPC_CALL_SLOW_START && call->cong_cwnd <= 2) + why = rxrpc_reqack_slow_start; + else if (call->tx_winsize <= 2) + why = rxrpc_reqack_small_txwin; + else if (call->peer->rtt_count < 3 && sp->hdr.seq & 1) + why = rxrpc_reqack_more_rtt; + else if (ktime_before(ktime_add_ms(call->peer->rtt_last_req, 1000), ktime_get_real())) + why = rxrpc_reqack_old_rtt; + else + goto dont_set_request_ack; + + trace_rxrpc_req_ack(call->debug_id, sp->hdr.seq, why); + if (why != rxrpc_reqack_no_srv_last) whdr.flags |= RXRPC_REQUEST_ACK; +dont_set_request_ack: if (IS_ENABLED(CONFIG_AF_RXRPC_INJECT_LOSS)) { static int lose; From 334dfbfc5a7187c99761df2392dd4cc49c453bea Mon Sep 17 00:00:00 2001 From: David Howells Date: Fri, 22 Apr 2022 00:20:49 +0100 Subject: [PATCH 0718/2002] rxrpc: Split call timer-expiration from call timer-set tracepoint Split the tracepoint for call timer-set to separate out the call timer-expiration event Signed-off-by: David Howells cc: Marc Dionne cc: linux-afs@lists.infradead.org --- include/trace/events/rxrpc.h | 42 +++++++++++++++++++++++++++++++++++- net/rxrpc/call_object.c | 2 +- 2 files changed, 42 insertions(+), 2 deletions(-) diff --git a/include/trace/events/rxrpc.h b/include/trace/events/rxrpc.h index 4c501c660123..a72f04e3d264 100644 --- a/include/trace/events/rxrpc.h +++ b/include/trace/events/rxrpc.h @@ -133,7 +133,6 @@ #define rxrpc_timer_traces \ EM(rxrpc_timer_begin, "Begin ") \ - EM(rxrpc_timer_expired, "*EXPR*") \ EM(rxrpc_timer_exp_ack, "ExpAck") \ EM(rxrpc_timer_exp_hard, "ExpHrd") \ EM(rxrpc_timer_exp_idle, "ExpIdl") \ @@ -1019,6 +1018,47 @@ TRACE_EVENT(rxrpc_timer, __entry->timer - __entry->now) ); +TRACE_EVENT(rxrpc_timer_expired, + TP_PROTO(struct rxrpc_call *call, unsigned long now), + + TP_ARGS(call, now), + + TP_STRUCT__entry( + __field(unsigned int, call ) + __field(long, now ) + __field(long, ack_at ) + __field(long, ack_lost_at ) + __field(long, resend_at ) + __field(long, ping_at ) + __field(long, expect_rx_by ) + __field(long, expect_req_by ) + __field(long, expect_term_by ) + __field(long, timer ) + ), + + TP_fast_assign( + __entry->call = call->debug_id; + __entry->now = now; + __entry->ack_at = call->ack_at; + __entry->ack_lost_at = call->ack_lost_at; + __entry->resend_at = call->resend_at; + __entry->expect_rx_by = call->expect_rx_by; + __entry->expect_req_by = call->expect_req_by; + __entry->expect_term_by = call->expect_term_by; + __entry->timer = call->timer.expires; + ), + + TP_printk("c=%08x EXPIRED a=%ld la=%ld r=%ld xr=%ld xq=%ld xt=%ld t=%ld", + __entry->call, + __entry->ack_at - __entry->now, + __entry->ack_lost_at - __entry->now, + __entry->resend_at - __entry->now, + __entry->expect_rx_by - __entry->now, + __entry->expect_req_by - __entry->now, + __entry->expect_term_by - __entry->now, + __entry->timer - __entry->now) + ); + TRACE_EVENT(rxrpc_rx_lose, TP_PROTO(struct rxrpc_skb_priv *sp), diff --git a/net/rxrpc/call_object.c b/net/rxrpc/call_object.c index 6401cdf7a624..a75861a0c477 100644 --- a/net/rxrpc/call_object.c +++ b/net/rxrpc/call_object.c @@ -52,7 +52,7 @@ static void rxrpc_call_timer_expired(struct timer_list *t) _enter("%d", call->debug_id); if (call->state < RXRPC_CALL_COMPLETE) { - trace_rxrpc_timer(call, rxrpc_timer_expired, jiffies); + trace_rxrpc_timer_expired(call, jiffies); __rxrpc_queue_call(call); } else { rxrpc_put_call(call, rxrpc_call_put); From 589a0c1e0ac31ccba49b214762e444dc015ee1e2 Mon Sep 17 00:00:00 2001 From: David Howells Date: Thu, 28 Apr 2022 08:30:47 +0100 Subject: [PATCH 0719/2002] rxrpc: Track highest acked serial Keep track of the highest DATA serial number that has been acked by the peer for future purposes. Signed-off-by: David Howells cc: Marc Dionne cc: linux-afs@lists.infradead.org --- net/rxrpc/ar-internal.h | 1 + net/rxrpc/input.c | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/net/rxrpc/ar-internal.h b/net/rxrpc/ar-internal.h index 1ad0ec5afb50..6c93a2fa9628 100644 --- a/net/rxrpc/ar-internal.h +++ b/net/rxrpc/ar-internal.h @@ -690,6 +690,7 @@ struct rxrpc_call { rxrpc_seq_t acks_lowest_nak; /* Lowest NACK in the buffer (or ==tx_hard_ack) */ rxrpc_seq_t acks_lost_top; /* tx_top at the time lost-ack ping sent */ rxrpc_serial_t acks_lost_ping; /* Serial number of probe ACK */ + rxrpc_serial_t acks_highest_serial; /* Highest serial number ACK'd */ }; /* diff --git a/net/rxrpc/input.c b/net/rxrpc/input.c index 721d847ba92b..4ba678f0c384 100644 --- a/net/rxrpc/input.c +++ b/net/rxrpc/input.c @@ -967,6 +967,10 @@ static void rxrpc_input_ack(struct rxrpc_call *call, struct sk_buff *skb) call->acks_first_seq = first_soft_ack; call->acks_prev_seq = prev_pkt; + if (buf.ack.reason != RXRPC_ACK_PING && + after(acked_serial, call->acks_highest_serial)) + call->acks_highest_serial = acked_serial; + /* Parse rwind and mtu sizes if provided. */ if (buf.info.rxMTU) rxrpc_input_ackinfo(call, skb, &buf.info); From b015424695f03a9fa5862d09c267ed458e256300 Mon Sep 17 00:00:00 2001 From: David Howells Date: Wed, 11 May 2022 14:01:25 +0100 Subject: [PATCH 0720/2002] rxrpc: Add stats procfile and DATA packet stats Add a procfile, /proc/net/rxrpc/stats, to display some statistics about what rxrpc has been doing. Writing a blank line to the stats file will clear the increment-only counters. Allocated resource counters don't get cleared. Add some counters to count various things about DATA packets, including the number created, transmitted and retransmitted and the number received, the number of ACK-requests markings and the number of jumbo packets received. Signed-off-by: David Howells cc: Marc Dionne cc: linux-afs@lists.infradead.org --- net/rxrpc/ar-internal.h | 17 +++++++++++++++ net/rxrpc/call_event.c | 1 + net/rxrpc/input.c | 6 ++++++ net/rxrpc/net_ns.c | 2 ++ net/rxrpc/output.c | 2 ++ net/rxrpc/proc.c | 48 +++++++++++++++++++++++++++++++++++++++++ net/rxrpc/sendmsg.c | 2 ++ 7 files changed, 78 insertions(+) diff --git a/net/rxrpc/ar-internal.h b/net/rxrpc/ar-internal.h index 6c93a2fa9628..ed406a5f939b 100644 --- a/net/rxrpc/ar-internal.h +++ b/net/rxrpc/ar-internal.h @@ -93,6 +93,14 @@ struct rxrpc_net { struct list_head peer_keepalive_new; struct timer_list peer_keepalive_timer; struct work_struct peer_keepalive_work; + + atomic_t stat_tx_data; + atomic_t stat_tx_data_retrans; + atomic_t stat_tx_data_send; + atomic_t stat_tx_data_send_frag; + atomic_t stat_rx_data; + atomic_t stat_rx_data_reqack; + atomic_t stat_rx_data_jumbo; }; /* @@ -1092,6 +1100,15 @@ void rxrpc_get_skb(struct sk_buff *, enum rxrpc_skb_trace); void rxrpc_free_skb(struct sk_buff *, enum rxrpc_skb_trace); void rxrpc_purge_queue(struct sk_buff_head *); +/* + * stats.c + */ +int rxrpc_stats_show(struct seq_file *seq, void *v); +int rxrpc_stats_clear(struct file *file, char *buf, size_t size); + +#define rxrpc_inc_stat(rxnet, s) atomic_inc(&(rxnet)->s) +#define rxrpc_dec_stat(rxnet, s) atomic_dec(&(rxnet)->s) + /* * sysctl.c */ diff --git a/net/rxrpc/call_event.c b/net/rxrpc/call_event.c index 2a93e7b5fbd0..c5b3ae1fe80b 100644 --- a/net/rxrpc/call_event.c +++ b/net/rxrpc/call_event.c @@ -261,6 +261,7 @@ static void rxrpc_resend(struct rxrpc_call *call, unsigned long now_j) rxrpc_get_skb(skb, rxrpc_skb_got); spin_unlock_bh(&call->lock); + rxrpc_inc_stat(call->rxnet, stat_tx_data_retrans); if (rxrpc_send_data_packet(call, skb, true) < 0) { rxrpc_free_skb(skb, rxrpc_skb_freed); return; diff --git a/net/rxrpc/input.c b/net/rxrpc/input.c index 4ba678f0c384..e7586d5ea2c3 100644 --- a/net/rxrpc/input.c +++ b/net/rxrpc/input.c @@ -443,6 +443,12 @@ static void rxrpc_input_data(struct rxrpc_call *call, struct sk_buff *skb) } } + rxrpc_inc_stat(call->rxnet, stat_rx_data); + if (sp->hdr.flags & RXRPC_REQUEST_ACK) + rxrpc_inc_stat(call->rxnet, stat_rx_data_reqack); + if (sp->hdr.flags & RXRPC_JUMBO_PACKET) + rxrpc_inc_stat(call->rxnet, stat_rx_data_jumbo); + spin_lock(&call->input_lock); /* Received data implicitly ACKs all of the request packets we sent diff --git a/net/rxrpc/net_ns.c b/net/rxrpc/net_ns.c index bb4c25d6df64..84242c0e467c 100644 --- a/net/rxrpc/net_ns.c +++ b/net/rxrpc/net_ns.c @@ -101,6 +101,8 @@ static __net_init int rxrpc_init_net(struct net *net) proc_create_net("locals", 0444, rxnet->proc_net, &rxrpc_local_seq_ops, sizeof(struct seq_net_private)); + proc_create_net_single_write("stats", S_IFREG | 0644, rxnet->proc_net, + rxrpc_stats_show, rxrpc_stats_clear, NULL); return 0; err_proc: diff --git a/net/rxrpc/output.c b/net/rxrpc/output.c index 2922c10bd500..8fddad2f63fc 100644 --- a/net/rxrpc/output.c +++ b/net/rxrpc/output.c @@ -462,6 +462,7 @@ dont_set_request_ack: * - in which case, we'll have processed the ICMP error * message and update the peer record */ + rxrpc_inc_stat(call->rxnet, stat_tx_data_send); ret = kernel_sendmsg(conn->params.local->socket, &msg, iov, 2, len); conn->params.peer->last_tx_at = ktime_get_seconds(); @@ -537,6 +538,7 @@ send_fragmentable: case AF_INET: ip_sock_set_mtu_discover(conn->params.local->socket->sk, IP_PMTUDISC_DONT); + rxrpc_inc_stat(call->rxnet, stat_tx_data_send_frag); ret = kernel_sendmsg(conn->params.local->socket, &msg, iov, 2, len); conn->params.peer->last_tx_at = ktime_get_seconds(); diff --git a/net/rxrpc/proc.c b/net/rxrpc/proc.c index 245418943e01..102744411932 100644 --- a/net/rxrpc/proc.c +++ b/net/rxrpc/proc.c @@ -397,3 +397,51 @@ const struct seq_operations rxrpc_local_seq_ops = { .stop = rxrpc_local_seq_stop, .show = rxrpc_local_seq_show, }; + +/* + * Display stats in /proc/net/rxrpc/stats + */ +int rxrpc_stats_show(struct seq_file *seq, void *v) +{ + struct rxrpc_net *rxnet = rxrpc_net(seq_file_single_net(seq)); + + seq_printf(seq, + "Data : send=%u sendf=%u\n", + atomic_read(&rxnet->stat_tx_data_send), + atomic_read(&rxnet->stat_tx_data_send_frag)); + seq_printf(seq, + "Data-Tx : nr=%u retrans=%u\n", + atomic_read(&rxnet->stat_tx_data), + atomic_read(&rxnet->stat_tx_data_retrans)); + seq_printf(seq, + "Data-Rx : nr=%u reqack=%u jumbo=%u\n", + atomic_read(&rxnet->stat_rx_data), + atomic_read(&rxnet->stat_rx_data_reqack), + atomic_read(&rxnet->stat_rx_data_jumbo)); + seq_printf(seq, + "Buffers : txb=%u rxb=%u\n", + atomic_read(&rxrpc_n_tx_skbs), + atomic_read(&rxrpc_n_rx_skbs)); + return 0; +} + +/* + * Clear stats if /proc/net/rxrpc/stats is written to. + */ +int rxrpc_stats_clear(struct file *file, char *buf, size_t size) +{ + struct seq_file *m = file->private_data; + struct rxrpc_net *rxnet = rxrpc_net(seq_file_single_net(m)); + + if (size > 1 || (size == 1 && buf[0] != '\n')) + return -EINVAL; + + atomic_set(&rxnet->stat_tx_data, 0); + atomic_set(&rxnet->stat_tx_data_retrans, 0); + atomic_set(&rxnet->stat_tx_data_send, 0); + atomic_set(&rxnet->stat_tx_data_send_frag, 0); + atomic_set(&rxnet->stat_rx_data, 0); + atomic_set(&rxnet->stat_rx_data_reqack, 0); + atomic_set(&rxnet->stat_rx_data_jumbo, 0); + return size; +} diff --git a/net/rxrpc/sendmsg.c b/net/rxrpc/sendmsg.c index 3c3a626459de..ad6f2cd08916 100644 --- a/net/rxrpc/sendmsg.c +++ b/net/rxrpc/sendmsg.c @@ -200,6 +200,8 @@ static int rxrpc_queue_packet(struct rxrpc_sock *rx, struct rxrpc_call *call, _net("queue skb %p [%d]", skb, seq); + rxrpc_inc_stat(call->rxnet, stat_tx_data); + ASSERTCMP(seq, ==, call->tx_top + 1); if (last) From f2a676d10038e8f3913dc576397b9c9efb190afd Mon Sep 17 00:00:00 2001 From: David Howells Date: Wed, 11 May 2022 14:01:25 +0100 Subject: [PATCH 0721/2002] rxrpc: Record statistics about ACK types Record statistics about the different types of ACKs that have been transmitted and received and the number of ACKs that have been filled out and transmitted or that have been skipped. Signed-off-by: David Howells cc: Marc Dionne cc: linux-afs@lists.infradead.org --- net/rxrpc/ar-internal.h | 6 ++++++ net/rxrpc/call_event.c | 2 ++ net/rxrpc/input.c | 1 + net/rxrpc/output.c | 7 ++++++- net/rxrpc/proc.c | 33 +++++++++++++++++++++++++++++++++ 5 files changed, 48 insertions(+), 1 deletion(-) diff --git a/net/rxrpc/ar-internal.h b/net/rxrpc/ar-internal.h index ed406a5f939b..8ed707a11d43 100644 --- a/net/rxrpc/ar-internal.h +++ b/net/rxrpc/ar-internal.h @@ -101,6 +101,12 @@ struct rxrpc_net { atomic_t stat_rx_data; atomic_t stat_rx_data_reqack; atomic_t stat_rx_data_jumbo; + + atomic_t stat_tx_ack_fill; + atomic_t stat_tx_ack_send; + atomic_t stat_tx_ack_skip; + atomic_t stat_tx_acks[256]; + atomic_t stat_rx_acks[256]; }; /* diff --git a/net/rxrpc/call_event.c b/net/rxrpc/call_event.c index c5b3ae1fe80b..70f70a0393f7 100644 --- a/net/rxrpc/call_event.c +++ b/net/rxrpc/call_event.c @@ -50,6 +50,8 @@ static void __rxrpc_propose_ACK(struct rxrpc_call *call, u8 ack_reason, unsigned long expiry = rxrpc_soft_ack_delay; s8 prior = rxrpc_ack_priority[ack_reason]; + rxrpc_inc_stat(call->rxnet, stat_tx_acks[ack_reason]); + /* Pings are handled specially because we don't want to accidentally * lose a ping response by subsuming it into a ping. */ diff --git a/net/rxrpc/input.c b/net/rxrpc/input.c index e7586d5ea2c3..7810b7d4f871 100644 --- a/net/rxrpc/input.c +++ b/net/rxrpc/input.c @@ -888,6 +888,7 @@ static void rxrpc_input_ack(struct rxrpc_call *call, struct sk_buff *skb) trace_rxrpc_rx_ack(call, ack_serial, acked_serial, first_soft_ack, prev_pkt, summary.ack_reason, nr_acks); + rxrpc_inc_stat(call->rxnet, stat_rx_acks[buf.ack.reason]); switch (buf.ack.reason) { case RXRPC_ACK_PING_RESPONSE: diff --git a/net/rxrpc/output.c b/net/rxrpc/output.c index 8fddad2f63fc..f350d39e3a60 100644 --- a/net/rxrpc/output.c +++ b/net/rxrpc/output.c @@ -83,8 +83,12 @@ static size_t rxrpc_fill_out_ack(struct rxrpc_connection *conn, tmp = atomic_xchg(&call->ackr_nr_unacked, 0); tmp |= atomic_xchg(&call->ackr_nr_consumed, 0); if (!tmp && (reason == RXRPC_ACK_DELAY || - reason == RXRPC_ACK_IDLE)) + reason == RXRPC_ACK_IDLE)) { + rxrpc_inc_stat(call->rxnet, stat_tx_ack_skip); return 0; + } + + rxrpc_inc_stat(call->rxnet, stat_tx_ack_fill); /* Barrier against rxrpc_input_data(). */ serial = call->ackr_serial; @@ -253,6 +257,7 @@ int rxrpc_send_ack_packet(struct rxrpc_call *call, bool ping, if (ping) rtt_slot = rxrpc_begin_rtt_probe(call, serial, rxrpc_rtt_tx_ping); + rxrpc_inc_stat(call->rxnet, stat_tx_ack_send); ret = kernel_sendmsg(conn->params.local->socket, &msg, iov, 2, len); conn->params.peer->last_tx_at = ktime_get_seconds(); if (ret < 0) diff --git a/net/rxrpc/proc.c b/net/rxrpc/proc.c index 102744411932..488c403f1d33 100644 --- a/net/rxrpc/proc.c +++ b/net/rxrpc/proc.c @@ -418,6 +418,33 @@ int rxrpc_stats_show(struct seq_file *seq, void *v) atomic_read(&rxnet->stat_rx_data), atomic_read(&rxnet->stat_rx_data_reqack), atomic_read(&rxnet->stat_rx_data_jumbo)); + seq_printf(seq, + "Ack : fill=%u send=%u skip=%u\n", + atomic_read(&rxnet->stat_tx_ack_fill), + atomic_read(&rxnet->stat_tx_ack_send), + atomic_read(&rxnet->stat_tx_ack_skip)); + seq_printf(seq, + "Ack-Tx : req=%u dup=%u oos=%u exw=%u nos=%u png=%u prs=%u dly=%u idl=%u\n", + atomic_read(&rxnet->stat_tx_acks[RXRPC_ACK_REQUESTED]), + atomic_read(&rxnet->stat_tx_acks[RXRPC_ACK_DUPLICATE]), + atomic_read(&rxnet->stat_tx_acks[RXRPC_ACK_OUT_OF_SEQUENCE]), + atomic_read(&rxnet->stat_tx_acks[RXRPC_ACK_EXCEEDS_WINDOW]), + atomic_read(&rxnet->stat_tx_acks[RXRPC_ACK_NOSPACE]), + atomic_read(&rxnet->stat_tx_acks[RXRPC_ACK_PING]), + atomic_read(&rxnet->stat_tx_acks[RXRPC_ACK_PING_RESPONSE]), + atomic_read(&rxnet->stat_tx_acks[RXRPC_ACK_DELAY]), + atomic_read(&rxnet->stat_tx_acks[RXRPC_ACK_IDLE])); + seq_printf(seq, + "Ack-Rx : req=%u dup=%u oos=%u exw=%u nos=%u png=%u prs=%u dly=%u idl=%u\n", + atomic_read(&rxnet->stat_rx_acks[RXRPC_ACK_REQUESTED]), + atomic_read(&rxnet->stat_rx_acks[RXRPC_ACK_DUPLICATE]), + atomic_read(&rxnet->stat_rx_acks[RXRPC_ACK_OUT_OF_SEQUENCE]), + atomic_read(&rxnet->stat_rx_acks[RXRPC_ACK_EXCEEDS_WINDOW]), + atomic_read(&rxnet->stat_rx_acks[RXRPC_ACK_NOSPACE]), + atomic_read(&rxnet->stat_rx_acks[RXRPC_ACK_PING]), + atomic_read(&rxnet->stat_rx_acks[RXRPC_ACK_PING_RESPONSE]), + atomic_read(&rxnet->stat_rx_acks[RXRPC_ACK_DELAY]), + atomic_read(&rxnet->stat_rx_acks[RXRPC_ACK_IDLE])); seq_printf(seq, "Buffers : txb=%u rxb=%u\n", atomic_read(&rxrpc_n_tx_skbs), @@ -443,5 +470,11 @@ int rxrpc_stats_clear(struct file *file, char *buf, size_t size) atomic_set(&rxnet->stat_rx_data, 0); atomic_set(&rxnet->stat_rx_data_reqack, 0); atomic_set(&rxnet->stat_rx_data_jumbo, 0); + + atomic_set(&rxnet->stat_tx_ack_fill, 0); + atomic_set(&rxnet->stat_tx_ack_send, 0); + atomic_set(&rxnet->stat_tx_ack_skip, 0); + memset(&rxnet->stat_tx_acks, 0, sizeof(rxnet->stat_tx_acks)); + memset(&rxnet->stat_rx_acks, 0, sizeof(rxnet->stat_rx_acks)); return size; } From f7fa52421f76309c574f2575701660bc3ea3a705 Mon Sep 17 00:00:00 2001 From: David Howells Date: Thu, 18 Aug 2022 11:52:36 +0100 Subject: [PATCH 0722/2002] rxrpc: Record stats for why the REQUEST-ACK flag is being set Record stats for why the REQUEST-ACK flag is being set. Signed-off-by: David Howells cc: Marc Dionne cc: linux-afs@lists.infradead.org --- include/trace/events/rxrpc.h | 1 + net/rxrpc/ar-internal.h | 2 ++ net/rxrpc/output.c | 1 + net/rxrpc/proc.c | 14 ++++++++++++++ 4 files changed, 18 insertions(+) diff --git a/include/trace/events/rxrpc.h b/include/trace/events/rxrpc.h index a72f04e3d264..794523d15321 100644 --- a/include/trace/events/rxrpc.h +++ b/include/trace/events/rxrpc.h @@ -250,6 +250,7 @@ EM(rxrpc_reqack_retrans, "RETRANS ") \ EM(rxrpc_reqack_slow_start, "SLOW-START") \ E_(rxrpc_reqack_small_txwin, "SMALL-TXWN") +/* ---- Must update size of stat_why_req_ack[] if more are added! */ /* * Generate enums for tracing information. diff --git a/net/rxrpc/ar-internal.h b/net/rxrpc/ar-internal.h index 8ed707a11d43..436a1e8d0abd 100644 --- a/net/rxrpc/ar-internal.h +++ b/net/rxrpc/ar-internal.h @@ -107,6 +107,8 @@ struct rxrpc_net { atomic_t stat_tx_ack_skip; atomic_t stat_tx_acks[256]; atomic_t stat_rx_acks[256]; + + atomic_t stat_why_req_ack[8]; }; /* diff --git a/net/rxrpc/output.c b/net/rxrpc/output.c index f350d39e3a60..77ed46ab33c5 100644 --- a/net/rxrpc/output.c +++ b/net/rxrpc/output.c @@ -430,6 +430,7 @@ int rxrpc_send_data_packet(struct rxrpc_call *call, struct sk_buff *skb, else goto dont_set_request_ack; + rxrpc_inc_stat(call->rxnet, stat_why_req_ack[why]); trace_rxrpc_req_ack(call->debug_id, sp->hdr.seq, why); if (why != rxrpc_reqack_no_srv_last) whdr.flags |= RXRPC_REQUEST_ACK; diff --git a/net/rxrpc/proc.c b/net/rxrpc/proc.c index 488c403f1d33..9bd357f39c39 100644 --- a/net/rxrpc/proc.c +++ b/net/rxrpc/proc.c @@ -445,6 +445,18 @@ int rxrpc_stats_show(struct seq_file *seq, void *v) atomic_read(&rxnet->stat_rx_acks[RXRPC_ACK_PING_RESPONSE]), atomic_read(&rxnet->stat_rx_acks[RXRPC_ACK_DELAY]), atomic_read(&rxnet->stat_rx_acks[RXRPC_ACK_IDLE])); + seq_printf(seq, + "Why-Req-A: acklost=%u already=%u mrtt=%u ortt=%u\n", + atomic_read(&rxnet->stat_why_req_ack[rxrpc_reqack_ack_lost]), + atomic_read(&rxnet->stat_why_req_ack[rxrpc_reqack_already_on]), + atomic_read(&rxnet->stat_why_req_ack[rxrpc_reqack_more_rtt]), + atomic_read(&rxnet->stat_why_req_ack[rxrpc_reqack_old_rtt])); + seq_printf(seq, + "Why-Req-A: nolast=%u retx=%u slows=%u smtxw=%u\n", + atomic_read(&rxnet->stat_why_req_ack[rxrpc_reqack_no_srv_last]), + atomic_read(&rxnet->stat_why_req_ack[rxrpc_reqack_retrans]), + atomic_read(&rxnet->stat_why_req_ack[rxrpc_reqack_slow_start]), + atomic_read(&rxnet->stat_why_req_ack[rxrpc_reqack_small_txwin])); seq_printf(seq, "Buffers : txb=%u rxb=%u\n", atomic_read(&rxrpc_n_tx_skbs), @@ -476,5 +488,7 @@ int rxrpc_stats_clear(struct file *file, char *buf, size_t size) atomic_set(&rxnet->stat_tx_ack_skip, 0); memset(&rxnet->stat_tx_acks, 0, sizeof(rxnet->stat_tx_acks)); memset(&rxnet->stat_rx_acks, 0, sizeof(rxnet->stat_rx_acks)); + + memset(&rxnet->stat_why_req_ack, 0, sizeof(rxnet->stat_why_req_ack)); return size; } From 8889a711f9b4dcf4dd1330fa493081beebd118c9 Mon Sep 17 00:00:00 2001 From: David Howells Date: Wed, 7 Sep 2022 19:17:29 +0100 Subject: [PATCH 0723/2002] rxrpc: Fix ack.bufferSize to be 0 when generating an ack ack.bufferSize should be set to 0 when generating an ack. Fixes: 8d94aa381dab ("rxrpc: Calls shouldn't hold socket refs") Reported-by: Jeffrey Altman Signed-off-by: David Howells cc: Marc Dionne cc: linux-afs@lists.infradead.org --- net/rxrpc/output.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/rxrpc/output.c b/net/rxrpc/output.c index 77ed46ab33c5..b803bfe146de 100644 --- a/net/rxrpc/output.c +++ b/net/rxrpc/output.c @@ -97,7 +97,7 @@ static size_t rxrpc_fill_out_ack(struct rxrpc_connection *conn, *_hard_ack = hard_ack; *_top = top; - pkt->ack.bufferSpace = htons(8); + pkt->ack.bufferSpace = htons(0); pkt->ack.maxSkew = htons(0); pkt->ack.firstPacket = htonl(hard_ack + 1); pkt->ack.previousPacket = htonl(call->ackr_highest_seq); From 42fb06b391ace2aec5cdb1ebb8ff668f0a34332f Mon Sep 17 00:00:00 2001 From: David Howells Date: Wed, 12 Oct 2022 08:49:29 +0100 Subject: [PATCH 0724/2002] net: Change the udp encap_err_rcv to allow use of {ip,ipv6}_icmp_error() Change the udp encap_err_rcv signature to match ip_icmp_error() and ipv6_icmp_error() so that those can be used from the called function and export them. Signed-off-by: David Howells cc: Marc Dionne cc: linux-afs@lists.infradead.org cc: netdev@vger.kernel.org --- include/linux/udp.h | 3 +- include/net/udp_tunnel.h | 4 +-- net/ipv4/ip_sockglue.c | 1 + net/ipv4/udp.c | 3 +- net/ipv6/datagram.c | 1 + net/ipv6/udp.c | 3 +- net/rxrpc/ar-internal.h | 2 +- net/rxrpc/peer_event.c | 71 ++++++++++------------------------------ 8 files changed, 28 insertions(+), 60 deletions(-) diff --git a/include/linux/udp.h b/include/linux/udp.h index 5cdba00a904a..dea57aa37df6 100644 --- a/include/linux/udp.h +++ b/include/linux/udp.h @@ -70,7 +70,8 @@ struct udp_sock { * For encapsulation sockets. */ int (*encap_rcv)(struct sock *sk, struct sk_buff *skb); - void (*encap_err_rcv)(struct sock *sk, struct sk_buff *skb, unsigned int udp_offset); + void (*encap_err_rcv)(struct sock *sk, struct sk_buff *skb, int err, + __be16 port, u32 info, u8 *payload); int (*encap_err_lookup)(struct sock *sk, struct sk_buff *skb); void (*encap_destroy)(struct sock *sk); diff --git a/include/net/udp_tunnel.h b/include/net/udp_tunnel.h index 72394f441dad..0ca9b7a11baf 100644 --- a/include/net/udp_tunnel.h +++ b/include/net/udp_tunnel.h @@ -68,8 +68,8 @@ typedef int (*udp_tunnel_encap_rcv_t)(struct sock *sk, struct sk_buff *skb); typedef int (*udp_tunnel_encap_err_lookup_t)(struct sock *sk, struct sk_buff *skb); typedef void (*udp_tunnel_encap_err_rcv_t)(struct sock *sk, - struct sk_buff *skb, - unsigned int udp_offset); + struct sk_buff *skb, int err, + __be16 port, u32 info, u8 *payload); typedef void (*udp_tunnel_encap_destroy_t)(struct sock *sk); typedef struct sk_buff *(*udp_tunnel_gro_receive_t)(struct sock *sk, struct list_head *head, diff --git a/net/ipv4/ip_sockglue.c b/net/ipv4/ip_sockglue.c index 5f16807d3235..9f92ae35bb01 100644 --- a/net/ipv4/ip_sockglue.c +++ b/net/ipv4/ip_sockglue.c @@ -433,6 +433,7 @@ void ip_icmp_error(struct sock *sk, struct sk_buff *skb, int err, } kfree_skb(skb); } +EXPORT_SYMBOL_GPL(ip_icmp_error); void ip_local_error(struct sock *sk, int err, __be32 daddr, __be16 port, u32 info) { diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index 89accc3c8bb3..b859d6c8298e 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c @@ -784,7 +784,8 @@ int __udp4_lib_err(struct sk_buff *skb, u32 info, struct udp_table *udptable) if (tunnel) { /* ...not for tunnels though: we don't have a sending socket */ if (udp_sk(sk)->encap_err_rcv) - udp_sk(sk)->encap_err_rcv(sk, skb, iph->ihl << 2); + udp_sk(sk)->encap_err_rcv(sk, skb, err, uh->dest, info, + (u8 *)(uh+1)); goto out; } if (!inet->recverr) { diff --git a/net/ipv6/datagram.c b/net/ipv6/datagram.c index df7e032ce87d..7c7155b48f17 100644 --- a/net/ipv6/datagram.c +++ b/net/ipv6/datagram.c @@ -334,6 +334,7 @@ void ipv6_icmp_error(struct sock *sk, struct sk_buff *skb, int err, if (sock_queue_err_skb(sk, skb)) kfree_skb(skb); } +EXPORT_SYMBOL_GPL(ipv6_icmp_error); void ipv6_local_error(struct sock *sk, int err, struct flowi6 *fl6, u32 info) { diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c index 297f7cc06044..4bc3fc27ec78 100644 --- a/net/ipv6/udp.c +++ b/net/ipv6/udp.c @@ -631,7 +631,8 @@ int __udp6_lib_err(struct sk_buff *skb, struct inet6_skb_parm *opt, /* Tunnels don't have an application socket: don't pass errors back */ if (tunnel) { if (udp_sk(sk)->encap_err_rcv) - udp_sk(sk)->encap_err_rcv(sk, skb, offset); + udp_sk(sk)->encap_err_rcv(sk, skb, err, uh->dest, + ntohl(info), (u8 *)(uh+1)); goto out; } diff --git a/net/rxrpc/ar-internal.h b/net/rxrpc/ar-internal.h index 436a1e8d0abd..51270b2e49c3 100644 --- a/net/rxrpc/ar-internal.h +++ b/net/rxrpc/ar-internal.h @@ -998,7 +998,7 @@ void rxrpc_send_keepalive(struct rxrpc_peer *); /* * peer_event.c */ -void rxrpc_encap_err_rcv(struct sock *sk, struct sk_buff *skb, unsigned int udp_offset); +void rxrpc_encap_err_rcv(struct sock *, struct sk_buff *, int, __be16, u32, u8 *); void rxrpc_error_report(struct sock *); void rxrpc_peer_keepalive_worker(struct work_struct *); diff --git a/net/rxrpc/peer_event.c b/net/rxrpc/peer_event.c index 32561e9567fe..d7d6d7aff985 100644 --- a/net/rxrpc/peer_event.c +++ b/net/rxrpc/peer_event.c @@ -29,20 +29,16 @@ static void rxrpc_distribute_error(struct rxrpc_peer *, int, */ static struct rxrpc_peer *rxrpc_lookup_peer_icmp_rcu(struct rxrpc_local *local, struct sk_buff *skb, - unsigned int udp_offset, - unsigned int *info, + __be16 udp_port, struct sockaddr_rxrpc *srx) { struct iphdr *ip, *ip0 = ip_hdr(skb); struct icmphdr *icmp = icmp_hdr(skb); - struct udphdr *udp = (struct udphdr *)(skb->data + udp_offset); _enter("%u,%u,%u", ip0->protocol, icmp->type, icmp->code); switch (icmp->type) { case ICMP_DEST_UNREACH: - *info = ntohs(icmp->un.frag.mtu); - fallthrough; case ICMP_TIME_EXCEEDED: case ICMP_PARAMETERPROB: ip = (struct iphdr *)((void *)icmp + 8); @@ -63,7 +59,7 @@ static struct rxrpc_peer *rxrpc_lookup_peer_icmp_rcu(struct rxrpc_local *local, case AF_INET: srx->transport_len = sizeof(srx->transport.sin); srx->transport.family = AF_INET; - srx->transport.sin.sin_port = udp->dest; + srx->transport.sin.sin_port = udp_port; memcpy(&srx->transport.sin.sin_addr, &ip->daddr, sizeof(struct in_addr)); break; @@ -72,7 +68,7 @@ static struct rxrpc_peer *rxrpc_lookup_peer_icmp_rcu(struct rxrpc_local *local, case AF_INET6: srx->transport_len = sizeof(srx->transport.sin); srx->transport.family = AF_INET; - srx->transport.sin.sin_port = udp->dest; + srx->transport.sin.sin_port = udp_port; memcpy(&srx->transport.sin.sin_addr, &ip->daddr, sizeof(struct in_addr)); break; @@ -93,20 +89,16 @@ static struct rxrpc_peer *rxrpc_lookup_peer_icmp_rcu(struct rxrpc_local *local, */ static struct rxrpc_peer *rxrpc_lookup_peer_icmp6_rcu(struct rxrpc_local *local, struct sk_buff *skb, - unsigned int udp_offset, - unsigned int *info, + __be16 udp_port, struct sockaddr_rxrpc *srx) { struct icmp6hdr *icmp = icmp6_hdr(skb); struct ipv6hdr *ip, *ip0 = ipv6_hdr(skb); - struct udphdr *udp = (struct udphdr *)(skb->data + udp_offset); _enter("%u,%u,%u", ip0->nexthdr, icmp->icmp6_type, icmp->icmp6_code); switch (icmp->icmp6_type) { case ICMPV6_DEST_UNREACH: - *info = ntohl(icmp->icmp6_mtu); - fallthrough; case ICMPV6_PKT_TOOBIG: case ICMPV6_TIME_EXCEED: case ICMPV6_PARAMPROB: @@ -129,13 +121,13 @@ static struct rxrpc_peer *rxrpc_lookup_peer_icmp6_rcu(struct rxrpc_local *local, _net("Rx ICMP6 on v4 sock"); srx->transport_len = sizeof(srx->transport.sin); srx->transport.family = AF_INET; - srx->transport.sin.sin_port = udp->dest; + srx->transport.sin.sin_port = udp_port; memcpy(&srx->transport.sin.sin_addr, &ip->daddr.s6_addr32[3], sizeof(struct in_addr)); break; case AF_INET6: _net("Rx ICMP6"); - srx->transport.sin.sin_port = udp->dest; + srx->transport.sin.sin_port = udp_port; memcpy(&srx->transport.sin6.sin6_addr, &ip->daddr, sizeof(struct in6_addr)); break; @@ -152,15 +144,13 @@ static struct rxrpc_peer *rxrpc_lookup_peer_icmp6_rcu(struct rxrpc_local *local, /* * Handle an error received on the local endpoint as a tunnel. */ -void rxrpc_encap_err_rcv(struct sock *sk, struct sk_buff *skb, - unsigned int udp_offset) +void rxrpc_encap_err_rcv(struct sock *sk, struct sk_buff *skb, int err, + __be16 port, u32 info, u8 *payload) { struct sock_extended_err ee; struct sockaddr_rxrpc srx; struct rxrpc_local *local; struct rxrpc_peer *peer; - unsigned int info = 0; - int err; u8 version = ip_hdr(skb)->version; u8 type = icmp_hdr(skb)->type; u8 code = icmp_hdr(skb)->code; @@ -176,13 +166,11 @@ void rxrpc_encap_err_rcv(struct sock *sk, struct sk_buff *skb, switch (ip_hdr(skb)->version) { case IPVERSION: - peer = rxrpc_lookup_peer_icmp_rcu(local, skb, udp_offset, - &info, &srx); + peer = rxrpc_lookup_peer_icmp_rcu(local, skb, port, &srx); break; #ifdef CONFIG_AF_RXRPC_IPV6 case 6: - peer = rxrpc_lookup_peer_icmp6_rcu(local, skb, udp_offset, - &info, &srx); + peer = rxrpc_lookup_peer_icmp6_rcu(local, skb, port, &srx); break; #endif default: @@ -201,34 +189,12 @@ void rxrpc_encap_err_rcv(struct sock *sk, struct sk_buff *skb, switch (version) { case IPVERSION: - switch (type) { - case ICMP_DEST_UNREACH: - switch (code) { - case ICMP_FRAG_NEEDED: - rxrpc_adjust_mtu(peer, info); - rcu_read_unlock(); - rxrpc_put_peer(peer); - return; - default: - break; - } - - err = EHOSTUNREACH; - if (code <= NR_ICMP_UNREACH) { - /* Might want to do something different with - * non-fatal errors - */ - //harderr = icmp_err_convert[code].fatal; - err = icmp_err_convert[code].errno; - } - break; - - case ICMP_TIME_EXCEEDED: - err = EHOSTUNREACH; - break; - default: - err = EPROTO; - break; + if (type == ICMP_DEST_UNREACH && + code == ICMP_FRAG_NEEDED) { + rxrpc_adjust_mtu(peer, info); + rcu_read_unlock(); + rxrpc_put_peer(peer); + return; } ee.ee_origin = SO_EE_ORIGIN_ICMP; @@ -239,16 +205,13 @@ void rxrpc_encap_err_rcv(struct sock *sk, struct sk_buff *skb, #ifdef CONFIG_AF_RXRPC_IPV6 case 6: - switch (type) { - case ICMPV6_PKT_TOOBIG: + if (type == ICMPV6_PKT_TOOBIG) { rxrpc_adjust_mtu(peer, info); rcu_read_unlock(); rxrpc_put_peer(peer); return; } - icmpv6_err_convert(type, code, &err); - if (err == EACCES) err = EHOSTUNREACH; From b6c66c4324e7dd66a06a6a034204ae7d4e95c28c Mon Sep 17 00:00:00 2001 From: David Howells Date: Wed, 12 Oct 2022 09:51:12 +0100 Subject: [PATCH 0725/2002] rxrpc: Use the core ICMP/ICMP6 parsers Make rxrpc_encap_rcv_err() pass the ICMP/ICMP6 skbuff to ip_icmp_error() or ipv6_icmp_error() as appropriate to do the parsing rather than trying to do it in rxrpc. This pushes an error report onto the UDP socket's error queue and calls ->sk_error_report() from which point rxrpc can pick it up. It would be preferable to steal the packet directly from ip*_icmp_error() rather than letting it get queued, but this is probably good enough. Also note that __udp4_lib_err() calls sk_error_report() twice in some cases. Signed-off-by: David Howells cc: Marc Dionne cc: linux-afs@lists.infradead.org --- net/rxrpc/ar-internal.h | 1 - net/rxrpc/local_object.c | 13 +++ net/rxrpc/peer_event.c | 247 +++++---------------------------------- 3 files changed, 43 insertions(+), 218 deletions(-) diff --git a/net/rxrpc/ar-internal.h b/net/rxrpc/ar-internal.h index 51270b2e49c3..ba0ee5d1c723 100644 --- a/net/rxrpc/ar-internal.h +++ b/net/rxrpc/ar-internal.h @@ -998,7 +998,6 @@ void rxrpc_send_keepalive(struct rxrpc_peer *); /* * peer_event.c */ -void rxrpc_encap_err_rcv(struct sock *, struct sk_buff *, int, __be16, u32, u8 *); void rxrpc_error_report(struct sock *); void rxrpc_peer_keepalive_worker(struct work_struct *); diff --git a/net/rxrpc/local_object.c b/net/rxrpc/local_object.c index 38ea98ff426b..e95e75e785fb 100644 --- a/net/rxrpc/local_object.c +++ b/net/rxrpc/local_object.c @@ -23,6 +23,19 @@ static void rxrpc_local_processor(struct work_struct *); static void rxrpc_local_rcu(struct rcu_head *); +/* + * Handle an ICMP/ICMP6 error turning up at the tunnel. Push it through the + * usual mechanism so that it gets parsed and presented through the UDP + * socket's error_report(). + */ +static void rxrpc_encap_err_rcv(struct sock *sk, struct sk_buff *skb, int err, + __be16 port, u32 info, u8 *payload) +{ + if (ip_hdr(skb)->version == IPVERSION) + return ip_icmp_error(sk, skb, err, port, info, payload); + return ipv6_icmp_error(sk, skb, err, port, info, payload); +} + /* * Compare a local to an address. Return -ve, 0 or +ve to indicate less than, * same or greater than. diff --git a/net/rxrpc/peer_event.c b/net/rxrpc/peer_event.c index d7d6d7aff985..cda3890657a9 100644 --- a/net/rxrpc/peer_event.c +++ b/net/rxrpc/peer_event.c @@ -16,220 +16,12 @@ #include #include #include -#include #include "ar-internal.h" -static void rxrpc_adjust_mtu(struct rxrpc_peer *, unsigned int); static void rxrpc_store_error(struct rxrpc_peer *, struct sock_exterr_skb *); static void rxrpc_distribute_error(struct rxrpc_peer *, int, enum rxrpc_call_completion); -/* - * Find the peer associated with an ICMPv4 packet. - */ -static struct rxrpc_peer *rxrpc_lookup_peer_icmp_rcu(struct rxrpc_local *local, - struct sk_buff *skb, - __be16 udp_port, - struct sockaddr_rxrpc *srx) -{ - struct iphdr *ip, *ip0 = ip_hdr(skb); - struct icmphdr *icmp = icmp_hdr(skb); - - _enter("%u,%u,%u", ip0->protocol, icmp->type, icmp->code); - - switch (icmp->type) { - case ICMP_DEST_UNREACH: - case ICMP_TIME_EXCEEDED: - case ICMP_PARAMETERPROB: - ip = (struct iphdr *)((void *)icmp + 8); - break; - default: - return NULL; - } - - memset(srx, 0, sizeof(*srx)); - srx->transport_type = local->srx.transport_type; - srx->transport_len = local->srx.transport_len; - srx->transport.family = local->srx.transport.family; - - /* Can we see an ICMP4 packet on an ICMP6 listening socket? and vice - * versa? - */ - switch (srx->transport.family) { - case AF_INET: - srx->transport_len = sizeof(srx->transport.sin); - srx->transport.family = AF_INET; - srx->transport.sin.sin_port = udp_port; - memcpy(&srx->transport.sin.sin_addr, &ip->daddr, - sizeof(struct in_addr)); - break; - -#ifdef CONFIG_AF_RXRPC_IPV6 - case AF_INET6: - srx->transport_len = sizeof(srx->transport.sin); - srx->transport.family = AF_INET; - srx->transport.sin.sin_port = udp_port; - memcpy(&srx->transport.sin.sin_addr, &ip->daddr, - sizeof(struct in_addr)); - break; -#endif - - default: - WARN_ON_ONCE(1); - return NULL; - } - - _net("ICMP {%pISp}", &srx->transport); - return rxrpc_lookup_peer_rcu(local, srx); -} - -#ifdef CONFIG_AF_RXRPC_IPV6 -/* - * Find the peer associated with an ICMPv6 packet. - */ -static struct rxrpc_peer *rxrpc_lookup_peer_icmp6_rcu(struct rxrpc_local *local, - struct sk_buff *skb, - __be16 udp_port, - struct sockaddr_rxrpc *srx) -{ - struct icmp6hdr *icmp = icmp6_hdr(skb); - struct ipv6hdr *ip, *ip0 = ipv6_hdr(skb); - - _enter("%u,%u,%u", ip0->nexthdr, icmp->icmp6_type, icmp->icmp6_code); - - switch (icmp->icmp6_type) { - case ICMPV6_DEST_UNREACH: - case ICMPV6_PKT_TOOBIG: - case ICMPV6_TIME_EXCEED: - case ICMPV6_PARAMPROB: - ip = (struct ipv6hdr *)((void *)icmp + 8); - break; - default: - return NULL; - } - - memset(srx, 0, sizeof(*srx)); - srx->transport_type = local->srx.transport_type; - srx->transport_len = local->srx.transport_len; - srx->transport.family = local->srx.transport.family; - - /* Can we see an ICMP4 packet on an ICMP6 listening socket? and vice - * versa? - */ - switch (srx->transport.family) { - case AF_INET: - _net("Rx ICMP6 on v4 sock"); - srx->transport_len = sizeof(srx->transport.sin); - srx->transport.family = AF_INET; - srx->transport.sin.sin_port = udp_port; - memcpy(&srx->transport.sin.sin_addr, - &ip->daddr.s6_addr32[3], sizeof(struct in_addr)); - break; - case AF_INET6: - _net("Rx ICMP6"); - srx->transport.sin.sin_port = udp_port; - memcpy(&srx->transport.sin6.sin6_addr, &ip->daddr, - sizeof(struct in6_addr)); - break; - default: - WARN_ON_ONCE(1); - return NULL; - } - - _net("ICMP {%pISp}", &srx->transport); - return rxrpc_lookup_peer_rcu(local, srx); -} -#endif /* CONFIG_AF_RXRPC_IPV6 */ - -/* - * Handle an error received on the local endpoint as a tunnel. - */ -void rxrpc_encap_err_rcv(struct sock *sk, struct sk_buff *skb, int err, - __be16 port, u32 info, u8 *payload) -{ - struct sock_extended_err ee; - struct sockaddr_rxrpc srx; - struct rxrpc_local *local; - struct rxrpc_peer *peer; - u8 version = ip_hdr(skb)->version; - u8 type = icmp_hdr(skb)->type; - u8 code = icmp_hdr(skb)->code; - - rcu_read_lock(); - local = rcu_dereference_sk_user_data(sk); - if (unlikely(!local)) { - rcu_read_unlock(); - return; - } - - rxrpc_new_skb(skb, rxrpc_skb_received); - - switch (ip_hdr(skb)->version) { - case IPVERSION: - peer = rxrpc_lookup_peer_icmp_rcu(local, skb, port, &srx); - break; -#ifdef CONFIG_AF_RXRPC_IPV6 - case 6: - peer = rxrpc_lookup_peer_icmp6_rcu(local, skb, port, &srx); - break; -#endif - default: - rcu_read_unlock(); - return; - } - - if (peer && !rxrpc_get_peer_maybe(peer)) - peer = NULL; - if (!peer) { - rcu_read_unlock(); - return; - } - - memset(&ee, 0, sizeof(ee)); - - switch (version) { - case IPVERSION: - if (type == ICMP_DEST_UNREACH && - code == ICMP_FRAG_NEEDED) { - rxrpc_adjust_mtu(peer, info); - rcu_read_unlock(); - rxrpc_put_peer(peer); - return; - } - - ee.ee_origin = SO_EE_ORIGIN_ICMP; - ee.ee_type = type; - ee.ee_code = code; - ee.ee_errno = err; - break; - -#ifdef CONFIG_AF_RXRPC_IPV6 - case 6: - if (type == ICMPV6_PKT_TOOBIG) { - rxrpc_adjust_mtu(peer, info); - rcu_read_unlock(); - rxrpc_put_peer(peer); - return; - } - - if (err == EACCES) - err = EHOSTUNREACH; - - ee.ee_origin = SO_EE_ORIGIN_ICMP6; - ee.ee_type = type; - ee.ee_code = code; - ee.ee_errno = err; - break; -#endif - } - - trace_rxrpc_rx_icmp(peer, &ee, &srx); - - rxrpc_distribute_error(peer, err, RXRPC_CALL_NETWORK_ERROR); - rcu_read_unlock(); - rxrpc_put_peer(peer); -} - /* * Find the peer associated with a local error. */ @@ -246,6 +38,9 @@ static struct rxrpc_peer *rxrpc_lookup_peer_local_rcu(struct rxrpc_local *local, srx->transport_len = local->srx.transport_len; srx->transport.family = local->srx.transport.family; + /* Can we see an ICMP4 packet on an ICMP6 listening socket? and vice + * versa? + */ switch (srx->transport.family) { case AF_INET: srx->transport_len = sizeof(srx->transport.sin); @@ -375,20 +170,38 @@ void rxrpc_error_report(struct sock *sk) } rxrpc_new_skb(skb, rxrpc_skb_received); serr = SKB_EXT_ERR(skb); - - if (serr->ee.ee_origin == SO_EE_ORIGIN_LOCAL) { - peer = rxrpc_lookup_peer_local_rcu(local, skb, &srx); - if (peer && !rxrpc_get_peer_maybe(peer)) - peer = NULL; - if (peer) { - trace_rxrpc_rx_icmp(peer, &serr->ee, &srx); - rxrpc_store_error(peer, serr); - } + if (!skb->len && serr->ee.ee_origin == SO_EE_ORIGIN_TIMESTAMPING) { + _leave("UDP empty message"); + rcu_read_unlock(); + rxrpc_free_skb(skb, rxrpc_skb_freed); + return; } + peer = rxrpc_lookup_peer_local_rcu(local, skb, &srx); + if (peer && !rxrpc_get_peer_maybe(peer)) + peer = NULL; + if (!peer) { + rcu_read_unlock(); + rxrpc_free_skb(skb, rxrpc_skb_freed); + _leave(" [no peer]"); + return; + } + + trace_rxrpc_rx_icmp(peer, &serr->ee, &srx); + + if ((serr->ee.ee_origin == SO_EE_ORIGIN_ICMP && + serr->ee.ee_type == ICMP_DEST_UNREACH && + serr->ee.ee_code == ICMP_FRAG_NEEDED)) { + rxrpc_adjust_mtu(peer, serr->ee.ee_info); + goto out; + } + + rxrpc_store_error(peer, serr); +out: rcu_read_unlock(); rxrpc_free_skb(skb, rxrpc_skb_freed); rxrpc_put_peer(peer); + _leave(""); } From ed472b0c8783e7e3896a8fb4382f2187aae427e1 Mon Sep 17 00:00:00 2001 From: David Howells Date: Tue, 22 Mar 2022 11:07:20 +0000 Subject: [PATCH 0726/2002] rxrpc: Call udp_sendmsg() directly Call udp_sendmsg() and udpv6_sendmsg() directly rather than calling kernel_sendmsg() as the latter assumes we want a kvec-class iterator. However, zerocopy explicitly doesn't work with such an iterator. Signed-off-by: David Howells cc: Marc Dionne cc: linux-afs@lists.infradead.org --- net/ipv6/udp.c | 1 + net/rxrpc/output.c | 37 +++++++++++++++++++++++++++---------- 2 files changed, 28 insertions(+), 10 deletions(-) diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c index 4bc3fc27ec78..331d699355f3 100644 --- a/net/ipv6/udp.c +++ b/net/ipv6/udp.c @@ -1639,6 +1639,7 @@ do_confirm: err = 0; goto out; } +EXPORT_SYMBOL(udpv6_sendmsg); void udpv6_destroy_sock(struct sock *sk) { diff --git a/net/rxrpc/output.c b/net/rxrpc/output.c index b803bfe146de..71885d741987 100644 --- a/net/rxrpc/output.c +++ b/net/rxrpc/output.c @@ -13,6 +13,7 @@ #include #include #include +#include #include "ar-internal.h" struct rxrpc_ack_buffer { @@ -23,6 +24,19 @@ struct rxrpc_ack_buffer { struct rxrpc_ackinfo ackinfo; }; +extern int udpv6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len); + +static ssize_t do_udp_sendmsg(struct socket *sk, struct msghdr *msg, size_t len) +{ +#if IS_ENABLED(CONFIG_AF_RXRPC_IPV6) + struct sockaddr *sa = msg->msg_name; + + if (sa->sa_family == AF_INET6) + return udpv6_sendmsg(sk->sk, msg, len); +#endif + return udp_sendmsg(sk->sk, msg, len); +} + struct rxrpc_abort_buffer { struct rxrpc_wire_header whdr; __be32 abort_code; @@ -258,8 +272,10 @@ int rxrpc_send_ack_packet(struct rxrpc_call *call, bool ping, rtt_slot = rxrpc_begin_rtt_probe(call, serial, rxrpc_rtt_tx_ping); rxrpc_inc_stat(call->rxnet, stat_tx_ack_send); - ret = kernel_sendmsg(conn->params.local->socket, &msg, iov, 2, len); - conn->params.peer->last_tx_at = ktime_get_seconds(); + + iov_iter_kvec(&msg.msg_iter, WRITE, iov, 2, len); + ret = do_udp_sendmsg(conn->params.local->socket, &msg, len); + call->peer->last_tx_at = ktime_get_seconds(); if (ret < 0) trace_rxrpc_tx_fail(call->debug_id, serial, ret, rxrpc_tx_point_call_ack); @@ -336,8 +352,8 @@ int rxrpc_send_abort_packet(struct rxrpc_call *call) serial = atomic_inc_return(&conn->serial); pkt.whdr.serial = htonl(serial); - ret = kernel_sendmsg(conn->params.local->socket, - &msg, iov, 1, sizeof(pkt)); + iov_iter_kvec(&msg.msg_iter, WRITE, iov, 1, sizeof(pkt)); + ret = do_udp_sendmsg(conn->params.local->socket, &msg, sizeof(pkt)); conn->params.peer->last_tx_at = ktime_get_seconds(); if (ret < 0) trace_rxrpc_tx_fail(call->debug_id, serial, ret, @@ -397,6 +413,7 @@ int rxrpc_send_data_packet(struct rxrpc_call *call, struct sk_buff *skb, iov[1].iov_base = skb->head; iov[1].iov_len = skb->len; len = iov[0].iov_len + iov[1].iov_len; + iov_iter_kvec(&msg.msg_iter, WRITE, iov, 2, len); msg.msg_name = &call->peer->srx.transport; msg.msg_namelen = call->peer->srx.transport_len; @@ -469,7 +486,7 @@ dont_set_request_ack: * message and update the peer record */ rxrpc_inc_stat(call->rxnet, stat_tx_data_send); - ret = kernel_sendmsg(conn->params.local->socket, &msg, iov, 2, len); + ret = do_udp_sendmsg(conn->params.local->socket, &msg, len); conn->params.peer->last_tx_at = ktime_get_seconds(); up_read(&conn->params.local->defrag_sem); @@ -545,8 +562,7 @@ send_fragmentable: ip_sock_set_mtu_discover(conn->params.local->socket->sk, IP_PMTUDISC_DONT); rxrpc_inc_stat(call->rxnet, stat_tx_data_send_frag); - ret = kernel_sendmsg(conn->params.local->socket, &msg, - iov, 2, len); + ret = do_udp_sendmsg(conn->params.local->socket, &msg, len); conn->params.peer->last_tx_at = ktime_get_seconds(); ip_sock_set_mtu_discover(conn->params.local->socket->sk, @@ -632,8 +648,8 @@ void rxrpc_reject_packets(struct rxrpc_local *local) whdr.flags ^= RXRPC_CLIENT_INITIATED; whdr.flags &= RXRPC_CLIENT_INITIATED; - ret = kernel_sendmsg(local->socket, &msg, - iov, ioc, size); + iov_iter_kvec(&msg.msg_iter, WRITE, iov, ioc, size); + ret = do_udp_sendmsg(local->socket, &msg, size); if (ret < 0) trace_rxrpc_tx_fail(local->debug_id, 0, ret, rxrpc_tx_point_reject); @@ -688,7 +704,8 @@ void rxrpc_send_keepalive(struct rxrpc_peer *peer) _proto("Tx VERSION (keepalive)"); - ret = kernel_sendmsg(peer->local->socket, &msg, iov, 2, len); + iov_iter_kvec(&msg.msg_iter, WRITE, iov, 2, len); + ret = do_udp_sendmsg(peer->local->socket, &msg, len); if (ret < 0) trace_rxrpc_tx_fail(peer->debug_id, 0, ret, rxrpc_tx_point_version_keepalive); From 23b237f3259299b75dd2ffefc7a4af889ba308c8 Mon Sep 17 00:00:00 2001 From: David Howells Date: Thu, 23 Jan 2020 21:36:21 +0000 Subject: [PATCH 0727/2002] rxrpc: Remove unnecessary header inclusions Remove a bunch of unnecessary header inclusions. Signed-off-by: David Howells cc: Marc Dionne cc: linux-afs@lists.infradead.org --- net/rxrpc/input.c | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/net/rxrpc/input.c b/net/rxrpc/input.c index 7810b7d4f871..59a0b8aee2a2 100644 --- a/net/rxrpc/input.c +++ b/net/rxrpc/input.c @@ -7,20 +7,6 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include #include "ar-internal.h" static void rxrpc_proto_abort(const char *why, From 27f699ccb89d65165175525254fec3d9d6b8d500 Mon Sep 17 00:00:00 2001 From: David Howells Date: Fri, 7 Oct 2022 13:52:06 +0100 Subject: [PATCH 0728/2002] rxrpc: Remove the flags from the rxrpc_skb tracepoint Remove the flags from the rxrpc_skb tracepoint as we're no longer going to be using this for the transmission buffers and so marking which are transmission buffers isn't going to be necessary. Note that this also remove the rxrpc skb flag that indicates if this is a transmission buffer and so the count is not updated for the moment. Signed-off-by: David Howells cc: Marc Dionne cc: linux-afs@lists.infradead.org --- include/trace/events/rxrpc.h | 9 +++------ net/rxrpc/ar-internal.h | 1 - net/rxrpc/sendmsg.c | 1 - net/rxrpc/skbuff.c | 20 +++++++------------- 4 files changed, 10 insertions(+), 21 deletions(-) diff --git a/include/trace/events/rxrpc.h b/include/trace/events/rxrpc.h index 794523d15321..484c8d032ab8 100644 --- a/include/trace/events/rxrpc.h +++ b/include/trace/events/rxrpc.h @@ -461,14 +461,13 @@ TRACE_EVENT(rxrpc_call, TRACE_EVENT(rxrpc_skb, TP_PROTO(struct sk_buff *skb, enum rxrpc_skb_trace op, - int usage, int mod_count, u8 flags, const void *where), + int usage, int mod_count, const void *where), - TP_ARGS(skb, op, usage, mod_count, flags, where), + TP_ARGS(skb, op, usage, mod_count, where), TP_STRUCT__entry( __field(struct sk_buff *, skb ) __field(enum rxrpc_skb_trace, op ) - __field(u8, flags ) __field(int, usage ) __field(int, mod_count ) __field(const void *, where ) @@ -476,16 +475,14 @@ TRACE_EVENT(rxrpc_skb, TP_fast_assign( __entry->skb = skb; - __entry->flags = flags; __entry->op = op; __entry->usage = usage; __entry->mod_count = mod_count; __entry->where = where; ), - TP_printk("s=%p %cx %s u=%d m=%d p=%pSR", + TP_printk("s=%p Rx %s u=%d m=%d p=%pSR", __entry->skb, - __entry->flags & RXRPC_SKB_TX_BUFFER ? 'T' : 'R', __print_symbolic(__entry->op, rxrpc_skb_traces), __entry->usage, __entry->mod_count, diff --git a/net/rxrpc/ar-internal.h b/net/rxrpc/ar-internal.h index ba0ee5d1c723..92f20b6c23ca 100644 --- a/net/rxrpc/ar-internal.h +++ b/net/rxrpc/ar-internal.h @@ -198,7 +198,6 @@ struct rxrpc_skb_priv { u8 nr_subpackets; /* Number of subpackets */ u8 rx_flags; /* Received packet flags */ #define RXRPC_SKB_INCL_LAST 0x01 /* - Includes last packet */ -#define RXRPC_SKB_TX_BUFFER 0x02 /* - Is transmit buffer */ union { int remain; /* amount of space remaining for next write */ diff --git a/net/rxrpc/sendmsg.c b/net/rxrpc/sendmsg.c index ad6f2cd08916..b2d28aa12e10 100644 --- a/net/rxrpc/sendmsg.c +++ b/net/rxrpc/sendmsg.c @@ -363,7 +363,6 @@ reload: goto maybe_error; sp = rxrpc_skb(skb); - sp->rx_flags |= RXRPC_SKB_TX_BUFFER; rxrpc_new_skb(skb, rxrpc_skb_new); _debug("ALLOC SEND %p", skb); diff --git a/net/rxrpc/skbuff.c b/net/rxrpc/skbuff.c index 580a5acffee7..0c827d5bb2b8 100644 --- a/net/rxrpc/skbuff.c +++ b/net/rxrpc/skbuff.c @@ -14,8 +14,7 @@ #include #include "ar-internal.h" -#define is_tx_skb(skb) (rxrpc_skb(skb)->rx_flags & RXRPC_SKB_TX_BUFFER) -#define select_skb_count(skb) (is_tx_skb(skb) ? &rxrpc_n_tx_skbs : &rxrpc_n_rx_skbs) +#define select_skb_count(skb) (&rxrpc_n_rx_skbs) /* * Note the allocation or reception of a socket buffer. @@ -24,8 +23,7 @@ void rxrpc_new_skb(struct sk_buff *skb, enum rxrpc_skb_trace op) { const void *here = __builtin_return_address(0); int n = atomic_inc_return(select_skb_count(skb)); - trace_rxrpc_skb(skb, op, refcount_read(&skb->users), n, - rxrpc_skb(skb)->rx_flags, here); + trace_rxrpc_skb(skb, op, refcount_read(&skb->users), n, here); } /* @@ -36,8 +34,7 @@ void rxrpc_see_skb(struct sk_buff *skb, enum rxrpc_skb_trace op) const void *here = __builtin_return_address(0); if (skb) { int n = atomic_read(select_skb_count(skb)); - trace_rxrpc_skb(skb, op, refcount_read(&skb->users), n, - rxrpc_skb(skb)->rx_flags, here); + trace_rxrpc_skb(skb, op, refcount_read(&skb->users), n, here); } } @@ -48,8 +45,7 @@ void rxrpc_get_skb(struct sk_buff *skb, enum rxrpc_skb_trace op) { const void *here = __builtin_return_address(0); int n = atomic_inc_return(select_skb_count(skb)); - trace_rxrpc_skb(skb, op, refcount_read(&skb->users), n, - rxrpc_skb(skb)->rx_flags, here); + trace_rxrpc_skb(skb, op, refcount_read(&skb->users), n, here); skb_get(skb); } @@ -60,7 +56,7 @@ void rxrpc_eaten_skb(struct sk_buff *skb, enum rxrpc_skb_trace op) { const void *here = __builtin_return_address(0); int n = atomic_inc_return(&rxrpc_n_rx_skbs); - trace_rxrpc_skb(skb, op, 0, n, 0, here); + trace_rxrpc_skb(skb, op, 0, n, here); } /* @@ -72,8 +68,7 @@ void rxrpc_free_skb(struct sk_buff *skb, enum rxrpc_skb_trace op) if (skb) { int n; n = atomic_dec_return(select_skb_count(skb)); - trace_rxrpc_skb(skb, op, refcount_read(&skb->users), n, - rxrpc_skb(skb)->rx_flags, here); + trace_rxrpc_skb(skb, op, refcount_read(&skb->users), n, here); kfree_skb(skb); } } @@ -88,8 +83,7 @@ void rxrpc_purge_queue(struct sk_buff_head *list) while ((skb = skb_dequeue((list))) != NULL) { int n = atomic_dec_return(select_skb_count(skb)); trace_rxrpc_skb(skb, rxrpc_skb_purged, - refcount_read(&skb->users), n, - rxrpc_skb(skb)->rx_flags, here); + refcount_read(&skb->users), n, here); kfree_skb(skb); } } From a11e6ff961a01884482b2a70ced74a5c62d96801 Mon Sep 17 00:00:00 2001 From: David Howells Date: Fri, 7 Oct 2022 14:46:08 +0100 Subject: [PATCH 0729/2002] rxrpc: Remove call->tx_phase Remove call->tx_phase as it's only ever set. Signed-off-by: David Howells cc: Marc Dionne cc: linux-afs@lists.infradead.org --- net/rxrpc/ar-internal.h | 1 - net/rxrpc/call_object.c | 1 - net/rxrpc/input.c | 5 +---- net/rxrpc/recvmsg.c | 1 - 4 files changed, 1 insertion(+), 7 deletions(-) diff --git a/net/rxrpc/ar-internal.h b/net/rxrpc/ar-internal.h index 92f20b6c23ca..282cb986f8a7 100644 --- a/net/rxrpc/ar-internal.h +++ b/net/rxrpc/ar-internal.h @@ -678,7 +678,6 @@ struct rxrpc_call { rxrpc_serial_t rx_serial; /* Highest serial received for this call */ u8 rx_winsize; /* Size of Rx window */ u8 tx_winsize; /* Maximum size of Tx window */ - bool tx_phase; /* T if transmission phase, F if receive phase */ u8 nr_jumbo_bad; /* Number of jumbo dups/exceeds-windows */ spinlock_t input_lock; /* Lock for packet input to this call */ diff --git a/net/rxrpc/call_object.c b/net/rxrpc/call_object.c index a75861a0c477..8290d94e9233 100644 --- a/net/rxrpc/call_object.c +++ b/net/rxrpc/call_object.c @@ -206,7 +206,6 @@ static struct rxrpc_call *rxrpc_alloc_client_call(struct rxrpc_sock *rx, return ERR_PTR(-ENOMEM); call->state = RXRPC_CALL_CLIENT_AWAIT_CONN; call->service_id = srx->srx_service; - call->tx_phase = true; now = ktime_get_real(); call->acks_latest_ts = now; call->cong_tstamp = now; diff --git a/net/rxrpc/input.c b/net/rxrpc/input.c index 59a0b8aee2a2..0cb23ba79e3b 100644 --- a/net/rxrpc/input.c +++ b/net/rxrpc/input.c @@ -309,10 +309,7 @@ static bool rxrpc_receiving_reply(struct rxrpc_call *call) return false; } } - if (!rxrpc_end_tx_phase(call, true, "ETD")) - return false; - call->tx_phase = false; - return true; + return rxrpc_end_tx_phase(call, true, "ETD"); } /* diff --git a/net/rxrpc/recvmsg.c b/net/rxrpc/recvmsg.c index 7e39c262fd79..a378e7a672a8 100644 --- a/net/rxrpc/recvmsg.c +++ b/net/rxrpc/recvmsg.c @@ -203,7 +203,6 @@ static void rxrpc_end_rx_phase(struct rxrpc_call *call, rxrpc_serial_t serial) break; case RXRPC_CALL_SERVER_RECV_REQUEST: - call->tx_phase = true; call->state = RXRPC_CALL_SERVER_ACK_REQUEST; call->expect_req_by = jiffies + MAX_JIFFY_OFFSET; write_unlock_bh(&call->state_lock); From 02a1935640f8f8539b8f2dbd6eeb539de93b2ce4 Mon Sep 17 00:00:00 2001 From: David Howells Date: Tue, 5 Apr 2022 21:16:32 +0100 Subject: [PATCH 0730/2002] rxrpc: Define rxrpc_txbuf struct to carry data to be transmitted Define a struct, rxrpc_txbuf, to carry data to be transmitted instead of a socket buffer so that it can be placed onto multiple queues at once. This also allows the data buffer to be in the same allocation as the internal data. Signed-off-by: David Howells cc: Marc Dionne cc: linux-afs@lists.infradead.org --- include/trace/events/rxrpc.h | 45 ++++++++++++++++ net/rxrpc/Makefile | 1 + net/rxrpc/ar-internal.h | 53 +++++++++++++++++++ net/rxrpc/proc.c | 3 +- net/rxrpc/txbuf.c | 100 +++++++++++++++++++++++++++++++++++ 5 files changed, 201 insertions(+), 1 deletion(-) create mode 100644 net/rxrpc/txbuf.c diff --git a/include/trace/events/rxrpc.h b/include/trace/events/rxrpc.h index 484c8d032ab8..47b157b1d32b 100644 --- a/include/trace/events/rxrpc.h +++ b/include/trace/events/rxrpc.h @@ -252,6 +252,18 @@ E_(rxrpc_reqack_small_txwin, "SMALL-TXWN") /* ---- Must update size of stat_why_req_ack[] if more are added! */ +#define rxrpc_txbuf_traces \ + EM(rxrpc_txbuf_alloc_ack, "ALLOC ACK ") \ + EM(rxrpc_txbuf_alloc_data, "ALLOC DATA ") \ + EM(rxrpc_txbuf_free, "FREE ") \ + EM(rxrpc_txbuf_get_trans, "GET TRANS ") \ + EM(rxrpc_txbuf_get_retrans, "GET RETRANS") \ + EM(rxrpc_txbuf_put_cleaned, "PUT CLEANED") \ + EM(rxrpc_txbuf_put_rotated, "PUT ROTATED") \ + EM(rxrpc_txbuf_put_send_aborted, "PUT SEND-X ") \ + EM(rxrpc_txbuf_see_send_more, "SEE SEND+ ") \ + E_(rxrpc_txbuf_see_unacked, "SEE UNACKED") + /* * Generate enums for tracing information. */ @@ -280,6 +292,7 @@ enum rxrpc_skb_trace { rxrpc_skb_traces } __mode(byte); enum rxrpc_timer_trace { rxrpc_timer_traces } __mode(byte); enum rxrpc_transmit_trace { rxrpc_transmit_traces } __mode(byte); enum rxrpc_tx_point { rxrpc_tx_points } __mode(byte); +enum rxrpc_txbuf_trace { rxrpc_txbuf_traces } __mode(byte); #endif /* end __RXRPC_DECLARE_TRACE_ENUMS_ONCE_ONLY */ @@ -308,6 +321,7 @@ rxrpc_skb_traces; rxrpc_timer_traces; rxrpc_transmit_traces; rxrpc_tx_points; +rxrpc_txbuf_traces; /* * Now redefine the EM() and E_() macros to map the enums to the strings that @@ -1469,6 +1483,37 @@ TRACE_EVENT(rxrpc_req_ack, __print_symbolic(__entry->why, rxrpc_req_ack_traces)) ); +TRACE_EVENT(rxrpc_txbuf, + TP_PROTO(unsigned int debug_id, + unsigned int call_debug_id, rxrpc_seq_t seq, + int ref, enum rxrpc_txbuf_trace what), + + TP_ARGS(debug_id, call_debug_id, seq, ref, what), + + TP_STRUCT__entry( + __field(unsigned int, debug_id ) + __field(unsigned int, call_debug_id ) + __field(rxrpc_seq_t, seq ) + __field(int, ref ) + __field(enum rxrpc_txbuf_trace, what ) + ), + + TP_fast_assign( + __entry->debug_id = debug_id; + __entry->call_debug_id = call_debug_id; + __entry->seq = seq; + __entry->ref = ref; + __entry->what = what; + ), + + TP_printk("B=%08x c=%08x q=%08x %s r=%d", + __entry->debug_id, + __entry->call_debug_id, + __entry->seq, + __print_symbolic(__entry->what, rxrpc_txbuf_traces), + __entry->ref) + ); + #undef EM #undef E_ #endif /* _TRACE_RXRPC_H */ diff --git a/net/rxrpc/Makefile b/net/rxrpc/Makefile index b11281bed2a4..fdeba488fc6e 100644 --- a/net/rxrpc/Makefile +++ b/net/rxrpc/Makefile @@ -30,6 +30,7 @@ rxrpc-y := \ sendmsg.o \ server_key.o \ skbuff.o \ + txbuf.o \ utils.o rxrpc-$(CONFIG_PROC_FS) += proc.o diff --git a/net/rxrpc/ar-internal.h b/net/rxrpc/ar-internal.h index 282cb986f8a7..49e90614d5e5 100644 --- a/net/rxrpc/ar-internal.h +++ b/net/rxrpc/ar-internal.h @@ -29,6 +29,7 @@ struct rxrpc_crypt { struct key_preparsed_payload; struct rxrpc_connection; +struct rxrpc_txbuf; /* * Mark applied to socket buffers in skb->mark. skb->priority is used @@ -759,6 +760,48 @@ struct rxrpc_send_params { bool upgrade; /* If the connection is upgradeable */ }; +/* + * Buffer of data to be output as a packet. + */ +struct rxrpc_txbuf { + struct rcu_head rcu; + struct list_head call_link; /* Link in call->tx_queue */ + struct list_head tx_link; /* Link in live Enc queue or Tx queue */ + struct rxrpc_call *call; /* Call to which belongs */ + ktime_t last_sent; /* Time at which last transmitted */ + refcount_t ref; + rxrpc_seq_t seq; /* Sequence number of this packet */ + unsigned int call_debug_id; + unsigned int debug_id; + unsigned int len; /* Amount of data in buffer */ + unsigned int space; /* Remaining data space */ + unsigned int offset; /* Offset of fill point */ + unsigned long flags; +#define RXRPC_TXBUF_ACKED 0 /* Set if ACK'd */ +#define RXRPC_TXBUF_NACKED 1 /* Set if NAK'd */ +#define RXRPC_TXBUF_LAST 2 /* Set if last packet in Tx phase */ +#define RXRPC_TXBUF_RESENT 3 /* Set if has been resent */ +#define RXRPC_TXBUF_RETRANS 4 /* Set if should be retransmitted */ + struct { + /* The packet for encrypting and DMA'ing. We align it such + * that data[] aligns correctly for any crypto blocksize. + */ + u8 pad[64 - sizeof(struct rxrpc_wire_header)]; + struct rxrpc_wire_header wire; /* Network-ready header */ + u8 data[RXRPC_JUMBO_DATALEN]; /* Data packet */ + } __aligned(64); +}; + +static inline bool rxrpc_sending_to_server(const struct rxrpc_txbuf *txb) +{ + return txb->wire.flags & RXRPC_CLIENT_INITIATED; +} + +static inline bool rxrpc_sending_to_client(const struct rxrpc_txbuf *txb) +{ + return !rxrpc_sending_to_server(txb); +} + #include /* @@ -1125,6 +1168,16 @@ static inline int __init rxrpc_sysctl_init(void) { return 0; } static inline void rxrpc_sysctl_exit(void) {} #endif +/* + * txbuf.c + */ +extern atomic_t rxrpc_nr_txbuf; +struct rxrpc_txbuf *rxrpc_alloc_txbuf(struct rxrpc_call *call, u8 packet_type, + gfp_t gfp); +void rxrpc_get_txbuf(struct rxrpc_txbuf *txb, enum rxrpc_txbuf_trace what); +void rxrpc_see_txbuf(struct rxrpc_txbuf *txb, enum rxrpc_txbuf_trace what); +void rxrpc_put_txbuf(struct rxrpc_txbuf *txb, enum rxrpc_txbuf_trace what); + /* * utils.c */ diff --git a/net/rxrpc/proc.c b/net/rxrpc/proc.c index 9bd357f39c39..3877afd23598 100644 --- a/net/rxrpc/proc.c +++ b/net/rxrpc/proc.c @@ -458,7 +458,8 @@ int rxrpc_stats_show(struct seq_file *seq, void *v) atomic_read(&rxnet->stat_why_req_ack[rxrpc_reqack_slow_start]), atomic_read(&rxnet->stat_why_req_ack[rxrpc_reqack_small_txwin])); seq_printf(seq, - "Buffers : txb=%u rxb=%u\n", + "Buffers : txb=%u,%u rxb=%u\n", + atomic_read(&rxrpc_nr_txbuf), atomic_read(&rxrpc_n_tx_skbs), atomic_read(&rxrpc_n_rx_skbs)); return 0; diff --git a/net/rxrpc/txbuf.c b/net/rxrpc/txbuf.c new file mode 100644 index 000000000000..66d922ad65d0 --- /dev/null +++ b/net/rxrpc/txbuf.c @@ -0,0 +1,100 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* RxRPC Tx data buffering. + * + * Copyright (C) 2022 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include "ar-internal.h" + +static atomic_t rxrpc_txbuf_debug_ids; +atomic_t rxrpc_nr_txbuf; + +/* + * Allocate and partially initialise an I/O request structure. + */ +struct rxrpc_txbuf *rxrpc_alloc_txbuf(struct rxrpc_call *call, u8 packet_type, + gfp_t gfp) +{ + struct rxrpc_txbuf *txb; + + txb = kmalloc(sizeof(*txb), gfp); + if (txb) { + INIT_LIST_HEAD(&txb->call_link); + INIT_LIST_HEAD(&txb->tx_link); + refcount_set(&txb->ref, 1); + txb->call = call; + txb->call_debug_id = call->debug_id; + txb->debug_id = atomic_inc_return(&rxrpc_txbuf_debug_ids); + txb->space = sizeof(txb->data); + txb->len = 0; + txb->offset = 0; + txb->flags = 0; + txb->seq = call->tx_top + 1; + txb->wire.epoch = htonl(call->conn->proto.epoch); + txb->wire.cid = htonl(call->cid); + txb->wire.callNumber = htonl(call->call_id); + txb->wire.seq = htonl(txb->seq); + txb->wire.type = packet_type; + txb->wire.flags = call->conn->out_clientflag; + txb->wire.userStatus = 0; + txb->wire.securityIndex = call->security_ix; + txb->wire._rsvd = 0; + txb->wire.serviceId = htons(call->service_id); + + trace_rxrpc_txbuf(txb->debug_id, + txb->call_debug_id, txb->seq, 1, + packet_type == RXRPC_PACKET_TYPE_DATA ? + rxrpc_txbuf_alloc_data : + rxrpc_txbuf_alloc_ack); + atomic_inc(&rxrpc_nr_txbuf); + } + + return txb; +} + +void rxrpc_get_txbuf(struct rxrpc_txbuf *txb, enum rxrpc_txbuf_trace what) +{ + int r; + + __refcount_inc(&txb->ref, &r); + trace_rxrpc_txbuf(txb->debug_id, txb->call_debug_id, txb->seq, r + 1, what); +} + +void rxrpc_see_txbuf(struct rxrpc_txbuf *txb, enum rxrpc_txbuf_trace what) +{ + int r = refcount_read(&txb->ref); + + trace_rxrpc_txbuf(txb->debug_id, txb->call_debug_id, txb->seq, r, what); +} + +static void rxrpc_free_txbuf(struct rcu_head *rcu) +{ + struct rxrpc_txbuf *txb = container_of(rcu, struct rxrpc_txbuf, rcu); + + trace_rxrpc_txbuf(txb->debug_id, txb->call_debug_id, txb->seq, 0, + rxrpc_txbuf_free); + kfree(txb); + atomic_dec(&rxrpc_nr_txbuf); +} + +void rxrpc_put_txbuf(struct rxrpc_txbuf *txb, enum rxrpc_txbuf_trace what) +{ + unsigned int debug_id, call_debug_id; + rxrpc_seq_t seq; + bool dead; + int r; + + if (txb) { + debug_id = txb->debug_id; + call_debug_id = txb->call_debug_id; + seq = txb->seq; + dead = __refcount_dec_and_test(&txb->ref, &r); + trace_rxrpc_txbuf(debug_id, call_debug_id, seq, r - 1, what); + if (dead) + call_rcu(&txb->rcu, rxrpc_free_txbuf); + } +} From 72f0c6fb057971864fe4d42b289b8e6ede836ef1 Mon Sep 17 00:00:00 2001 From: David Howells Date: Thu, 30 Jan 2020 21:48:13 +0000 Subject: [PATCH 0731/2002] rxrpc: Allocate ACK records at proposal and queue for transmission Allocate rxrpc_txbuf records for ACKs and put onto a queue for the transmitter thread to dispatch. Signed-off-by: David Howells cc: Marc Dionne cc: linux-afs@lists.infradead.org --- include/trace/events/rxrpc.h | 47 ++++++--- net/rxrpc/ar-internal.h | 21 ++-- net/rxrpc/call_accept.c | 5 +- net/rxrpc/call_event.c | 185 +++++++++++++++++++---------------- net/rxrpc/input.c | 89 +++++++---------- net/rxrpc/local_object.c | 7 ++ net/rxrpc/output.c | 157 ++++++++++++++--------------- net/rxrpc/recvmsg.c | 33 ++----- net/rxrpc/sendmsg.c | 4 +- net/rxrpc/txbuf.c | 1 + 10 files changed, 283 insertions(+), 266 deletions(-) diff --git a/include/trace/events/rxrpc.h b/include/trace/events/rxrpc.h index 47b157b1d32b..1597ff7ad97e 100644 --- a/include/trace/events/rxrpc.h +++ b/include/trace/events/rxrpc.h @@ -34,7 +34,8 @@ EM(rxrpc_local_new, "NEW") \ EM(rxrpc_local_processing, "PRO") \ EM(rxrpc_local_put, "PUT") \ - E_(rxrpc_local_queued, "QUE") + EM(rxrpc_local_queued, "QUE") \ + E_(rxrpc_local_tx_ack, "TAK") #define rxrpc_peer_traces \ EM(rxrpc_peer_got, "GOT") \ @@ -258,7 +259,9 @@ EM(rxrpc_txbuf_free, "FREE ") \ EM(rxrpc_txbuf_get_trans, "GET TRANS ") \ EM(rxrpc_txbuf_get_retrans, "GET RETRANS") \ + EM(rxrpc_txbuf_put_ack_tx, "PUT ACK TX ") \ EM(rxrpc_txbuf_put_cleaned, "PUT CLEANED") \ + EM(rxrpc_txbuf_put_nomem, "PUT NOMEM ") \ EM(rxrpc_txbuf_put_rotated, "PUT ROTATED") \ EM(rxrpc_txbuf_put_send_aborted, "PUT SEND-X ") \ EM(rxrpc_txbuf_see_send_more, "SEE SEND+ ") \ @@ -1095,19 +1098,16 @@ TRACE_EVENT(rxrpc_rx_lose, TRACE_EVENT(rxrpc_propose_ack, TP_PROTO(struct rxrpc_call *call, enum rxrpc_propose_ack_trace why, - u8 ack_reason, rxrpc_serial_t serial, bool immediate, - bool background, enum rxrpc_propose_ack_outcome outcome), + u8 ack_reason, rxrpc_serial_t serial, + enum rxrpc_propose_ack_outcome outcome), - TP_ARGS(call, why, ack_reason, serial, immediate, background, - outcome), + TP_ARGS(call, why, ack_reason, serial, outcome), TP_STRUCT__entry( __field(unsigned int, call ) __field(enum rxrpc_propose_ack_trace, why ) __field(rxrpc_serial_t, serial ) __field(u8, ack_reason ) - __field(bool, immediate ) - __field(bool, background ) __field(enum rxrpc_propose_ack_outcome, outcome ) ), @@ -1116,21 +1116,44 @@ TRACE_EVENT(rxrpc_propose_ack, __entry->why = why; __entry->serial = serial; __entry->ack_reason = ack_reason; - __entry->immediate = immediate; - __entry->background = background; __entry->outcome = outcome; ), - TP_printk("c=%08x %s %s r=%08x i=%u b=%u%s", + TP_printk("c=%08x %s %s r=%08x%s", __entry->call, __print_symbolic(__entry->why, rxrpc_propose_ack_traces), __print_symbolic(__entry->ack_reason, rxrpc_ack_names), __entry->serial, - __entry->immediate, - __entry->background, __print_symbolic(__entry->outcome, rxrpc_propose_ack_outcomes)) ); +TRACE_EVENT(rxrpc_send_ack, + TP_PROTO(struct rxrpc_call *call, enum rxrpc_propose_ack_trace why, + u8 ack_reason, rxrpc_serial_t serial), + + TP_ARGS(call, why, ack_reason, serial), + + TP_STRUCT__entry( + __field(unsigned int, call ) + __field(enum rxrpc_propose_ack_trace, why ) + __field(rxrpc_serial_t, serial ) + __field(u8, ack_reason ) + ), + + TP_fast_assign( + __entry->call = call->debug_id; + __entry->why = why; + __entry->serial = serial; + __entry->ack_reason = ack_reason; + ), + + TP_printk("c=%08x %s %s r=%08x", + __entry->call, + __print_symbolic(__entry->why, rxrpc_propose_ack_traces), + __print_symbolic(__entry->ack_reason, rxrpc_ack_names), + __entry->serial) + ); + TRACE_EVENT(rxrpc_retransmit, TP_PROTO(struct rxrpc_call *call, rxrpc_seq_t seq, u8 annotation, s64 expiry), diff --git a/net/rxrpc/ar-internal.h b/net/rxrpc/ar-internal.h index 49e90614d5e5..802a8f372af2 100644 --- a/net/rxrpc/ar-internal.h +++ b/net/rxrpc/ar-internal.h @@ -292,6 +292,8 @@ struct rxrpc_local { struct hlist_node link; struct socket *socket; /* my UDP socket */ struct work_struct processor; + struct list_head ack_tx_queue; /* List of ACKs that need sending */ + spinlock_t ack_tx_lock; /* ACK list lock */ struct rxrpc_sock __rcu *service; /* Service(s) listening on this endpoint */ struct rw_semaphore defrag_sem; /* control re-enablement of IP DF bit */ struct sk_buff_head reject_queue; /* packets awaiting rejection */ @@ -520,10 +522,8 @@ enum rxrpc_call_flag { * Events that can be raised on a call. */ enum rxrpc_call_event { - RXRPC_CALL_EV_ACK, /* need to generate ACK */ RXRPC_CALL_EV_ABORT, /* need to generate abort */ RXRPC_CALL_EV_RESEND, /* Tx resend required */ - RXRPC_CALL_EV_PING, /* Ping send required */ RXRPC_CALL_EV_EXPIRED, /* Expiry occurred */ RXRPC_CALL_EV_ACK_LOST, /* ACK may be lost, send ping */ }; @@ -782,13 +782,20 @@ struct rxrpc_txbuf { #define RXRPC_TXBUF_LAST 2 /* Set if last packet in Tx phase */ #define RXRPC_TXBUF_RESENT 3 /* Set if has been resent */ #define RXRPC_TXBUF_RETRANS 4 /* Set if should be retransmitted */ + u8 /*enum rxrpc_propose_ack_trace*/ ack_why; /* If ack, why */ struct { /* The packet for encrypting and DMA'ing. We align it such * that data[] aligns correctly for any crypto blocksize. */ u8 pad[64 - sizeof(struct rxrpc_wire_header)]; struct rxrpc_wire_header wire; /* Network-ready header */ - u8 data[RXRPC_JUMBO_DATALEN]; /* Data packet */ + union { + u8 data[RXRPC_JUMBO_DATALEN]; /* Data packet */ + struct { + struct rxrpc_ackpacket ack; + u8 acks[0]; + }; + }; } __aligned(64); }; @@ -824,8 +831,10 @@ int rxrpc_user_charge_accept(struct rxrpc_sock *, unsigned long); /* * call_event.c */ -void rxrpc_propose_ACK(struct rxrpc_call *, u8, u32, bool, bool, - enum rxrpc_propose_ack_trace); +void rxrpc_propose_ping(struct rxrpc_call *call, u32 serial, + enum rxrpc_propose_ack_trace why); +void rxrpc_send_ACK(struct rxrpc_call *, u8, rxrpc_serial_t, enum rxrpc_propose_ack_trace); +void rxrpc_propose_ACK(struct rxrpc_call *, u8, u32, enum rxrpc_propose_ack_trace); void rxrpc_process_call(struct work_struct *); void rxrpc_reduce_call_timer(struct rxrpc_call *call, @@ -1030,7 +1039,7 @@ static inline struct rxrpc_net *rxrpc_net(struct net *net) /* * output.c */ -int rxrpc_send_ack_packet(struct rxrpc_call *, bool, rxrpc_serial_t *); +void rxrpc_transmit_ack_packets(struct rxrpc_local *); int rxrpc_send_abort_packet(struct rxrpc_call *); int rxrpc_send_data_packet(struct rxrpc_call *, struct sk_buff *, bool); void rxrpc_reject_packets(struct rxrpc_local *); diff --git a/net/rxrpc/call_accept.c b/net/rxrpc/call_accept.c index 99e10eea3732..d8db277d5ebe 100644 --- a/net/rxrpc/call_accept.c +++ b/net/rxrpc/call_accept.c @@ -248,9 +248,8 @@ static void rxrpc_send_ping(struct rxrpc_call *call, struct sk_buff *skb) if (call->peer->rtt_count < 3 || ktime_before(ktime_add_ms(call->peer->rtt_last_req, 1000), now)) - rxrpc_propose_ACK(call, RXRPC_ACK_PING, sp->hdr.serial, - true, true, - rxrpc_propose_ack_ping_for_params); + rxrpc_send_ACK(call, RXRPC_ACK_PING, sp->hdr.serial, + rxrpc_propose_ack_ping_for_params); } /* diff --git a/net/rxrpc/call_event.c b/net/rxrpc/call_event.c index 70f70a0393f7..67b54ad914a1 100644 --- a/net/rxrpc/call_event.c +++ b/net/rxrpc/call_event.c @@ -20,46 +20,39 @@ /* * Propose a PING ACK be sent. */ -static void rxrpc_propose_ping(struct rxrpc_call *call, - bool immediate, bool background) +void rxrpc_propose_ping(struct rxrpc_call *call, u32 serial, + enum rxrpc_propose_ack_trace why) { - if (immediate) { - if (background && - !test_and_set_bit(RXRPC_CALL_EV_PING, &call->events)) - rxrpc_queue_call(call); - } else { - unsigned long now = jiffies; - unsigned long ping_at = now + rxrpc_idle_ack_delay; + unsigned long now = jiffies; + unsigned long ping_at = now + rxrpc_idle_ack_delay; - if (time_before(ping_at, call->ping_at)) { - WRITE_ONCE(call->ping_at, ping_at); - rxrpc_reduce_call_timer(call, ping_at, now, - rxrpc_timer_set_for_ping); - } + spin_lock_bh(&call->lock); + + if (time_before(ping_at, call->ping_at)) { + rxrpc_inc_stat(call->rxnet, stat_tx_acks[RXRPC_ACK_PING]); + WRITE_ONCE(call->ping_at, ping_at); + rxrpc_reduce_call_timer(call, ping_at, now, + rxrpc_timer_set_for_ping); + trace_rxrpc_propose_ack(call, why, RXRPC_ACK_PING, serial, + rxrpc_propose_ack_use); } + + spin_unlock_bh(&call->lock); } /* * propose an ACK be sent */ static void __rxrpc_propose_ACK(struct rxrpc_call *call, u8 ack_reason, - u32 serial, bool immediate, bool background, - enum rxrpc_propose_ack_trace why) + u32 serial, enum rxrpc_propose_ack_trace why) { enum rxrpc_propose_ack_outcome outcome = rxrpc_propose_ack_use; unsigned long expiry = rxrpc_soft_ack_delay; + unsigned long now = jiffies, ack_at; s8 prior = rxrpc_ack_priority[ack_reason]; rxrpc_inc_stat(call->rxnet, stat_tx_acks[ack_reason]); - /* Pings are handled specially because we don't want to accidentally - * lose a ping response by subsuming it into a ping. - */ - if (ack_reason == RXRPC_ACK_PING) { - rxrpc_propose_ping(call, immediate, background); - goto trace; - } - /* Update DELAY, IDLE, REQUESTED and PING_RESPONSE ACK serial * numbers, but we don't alter the timeout. */ @@ -71,8 +64,6 @@ static void __rxrpc_propose_ACK(struct rxrpc_call *call, u8 ack_reason, outcome = rxrpc_propose_ack_update; call->ackr_serial = serial; } - if (!immediate) - goto trace; } else if (prior > rxrpc_ack_priority[call->ackr_reason]) { call->ackr_reason = ack_reason; call->ackr_serial = serial; @@ -84,8 +75,6 @@ static void __rxrpc_propose_ACK(struct rxrpc_call *call, u8 ack_reason, case RXRPC_ACK_REQUESTED: if (rxrpc_requested_ack_delay < expiry) expiry = rxrpc_requested_ack_delay; - if (serial == 1) - immediate = false; break; case RXRPC_ACK_DELAY: @@ -99,52 +88,89 @@ static void __rxrpc_propose_ACK(struct rxrpc_call *call, u8 ack_reason, break; default: - immediate = true; - break; + WARN_ON(1); + return; } - if (test_bit(RXRPC_CALL_EV_ACK, &call->events)) { - _debug("already scheduled"); - } else if (immediate || expiry == 0) { - _debug("immediate ACK %lx", call->events); - if (!test_and_set_bit(RXRPC_CALL_EV_ACK, &call->events) && - background) - rxrpc_queue_call(call); - } else { - unsigned long now = jiffies, ack_at; - if (call->peer->srtt_us != 0) - ack_at = usecs_to_jiffies(call->peer->srtt_us >> 3); - else - ack_at = expiry; + if (call->peer->srtt_us != 0) + ack_at = usecs_to_jiffies(call->peer->srtt_us >> 3); + else + ack_at = expiry; - ack_at += READ_ONCE(call->tx_backoff); - ack_at += now; - if (time_before(ack_at, call->ack_at)) { - WRITE_ONCE(call->ack_at, ack_at); - rxrpc_reduce_call_timer(call, ack_at, now, - rxrpc_timer_set_for_ack); - } + ack_at += READ_ONCE(call->tx_backoff); + ack_at += now; + if (time_before(ack_at, call->ack_at)) { + WRITE_ONCE(call->ack_at, ack_at); + rxrpc_reduce_call_timer(call, ack_at, now, + rxrpc_timer_set_for_ack); } -trace: - trace_rxrpc_propose_ack(call, why, ack_reason, serial, immediate, - background, outcome); + trace_rxrpc_propose_ack(call, why, ack_reason, serial, outcome); } /* * propose an ACK be sent, locking the call structure */ -void rxrpc_propose_ACK(struct rxrpc_call *call, u8 ack_reason, - u32 serial, bool immediate, bool background, +void rxrpc_propose_ACK(struct rxrpc_call *call, u8 ack_reason, u32 serial, enum rxrpc_propose_ack_trace why) { spin_lock_bh(&call->lock); - __rxrpc_propose_ACK(call, ack_reason, serial, - immediate, background, why); + __rxrpc_propose_ACK(call, ack_reason, serial, why); spin_unlock_bh(&call->lock); } +/* + * Queue an ACK for immediate transmission. + */ +void rxrpc_send_ACK(struct rxrpc_call *call, u8 ack_reason, + rxrpc_serial_t serial, enum rxrpc_propose_ack_trace why) +{ + struct rxrpc_local *local = call->conn->params.local; + struct rxrpc_txbuf *txb; + + if (test_bit(RXRPC_CALL_DISCONNECTED, &call->flags)) + return; + + rxrpc_inc_stat(call->rxnet, stat_tx_acks[ack_reason]); + + txb = rxrpc_alloc_txbuf(call, RXRPC_PACKET_TYPE_ACK, + in_softirq() ? GFP_ATOMIC | __GFP_NOWARN : GFP_NOFS); + if (!txb) { + kleave(" = -ENOMEM"); + return; + } + + txb->ack_why = why; + txb->wire.seq = 0; + txb->wire.type = RXRPC_PACKET_TYPE_ACK; + txb->wire.flags |= RXRPC_SLOW_START_OK; + txb->ack.bufferSpace = 0; + txb->ack.maxSkew = 0; + txb->ack.firstPacket = 0; + txb->ack.previousPacket = 0; + txb->ack.serial = htonl(serial); + txb->ack.reason = ack_reason; + txb->ack.nAcks = 0; + + if (!rxrpc_try_get_call(call, rxrpc_call_got)) { + rxrpc_put_txbuf(txb, rxrpc_txbuf_put_nomem); + return; + } + + spin_lock_bh(&local->ack_tx_lock); + list_add_tail(&txb->tx_link, &local->ack_tx_queue); + spin_unlock_bh(&local->ack_tx_lock); + trace_rxrpc_send_ack(call, why, ack_reason, serial); + + if (in_task()) { + rxrpc_transmit_ack_packets(call->peer->local); + } else { + rxrpc_get_local(local); + rxrpc_queue_local(local); + } +} + /* * Handle congestion being detected by the retransmit timeout. */ @@ -230,9 +256,8 @@ static void rxrpc_resend(struct rxrpc_call *call, unsigned long now_j) ack_ts = ktime_sub(now, call->acks_latest_ts); if (ktime_to_us(ack_ts) < (call->peer->srtt_us >> 3)) goto out; - rxrpc_propose_ACK(call, RXRPC_ACK_PING, 0, true, false, - rxrpc_propose_ack_ping_for_lost_ack); - rxrpc_send_ack_packet(call, true, NULL); + rxrpc_send_ACK(call, RXRPC_ACK_PING, 0, + rxrpc_propose_ack_ping_for_lost_ack); goto out; } @@ -291,9 +316,10 @@ void rxrpc_process_call(struct work_struct *work) { struct rxrpc_call *call = container_of(work, struct rxrpc_call, processor); - rxrpc_serial_t *send_ack; unsigned long now, next, t; unsigned int iterations = 0; + rxrpc_serial_t ackr_serial; + u8 ackr_reason; rxrpc_see_call(call); @@ -342,7 +368,15 @@ recheck_state: if (time_after_eq(now, t)) { trace_rxrpc_timer(call, rxrpc_timer_exp_ack, now); cmpxchg(&call->ack_at, t, now + MAX_JIFFY_OFFSET); - set_bit(RXRPC_CALL_EV_ACK, &call->events); + spin_lock_bh(&call->lock); + ackr_reason = call->ackr_reason; + ackr_serial = call->ackr_serial; + call->ackr_reason = 0; + call->ackr_serial = 0; + spin_unlock_bh(&call->lock); + if (ackr_reason) + rxrpc_send_ACK(call, ackr_reason, ackr_serial, + rxrpc_propose_ack_ping_for_lost_ack); } t = READ_ONCE(call->ack_lost_at); @@ -356,16 +390,16 @@ recheck_state: if (time_after_eq(now, t)) { trace_rxrpc_timer(call, rxrpc_timer_exp_keepalive, now); cmpxchg(&call->keepalive_at, t, now + MAX_JIFFY_OFFSET); - rxrpc_propose_ACK(call, RXRPC_ACK_PING, 0, true, true, - rxrpc_propose_ack_ping_for_keepalive); - set_bit(RXRPC_CALL_EV_PING, &call->events); + rxrpc_send_ACK(call, RXRPC_ACK_PING, 0, + rxrpc_propose_ack_ping_for_keepalive); } t = READ_ONCE(call->ping_at); if (time_after_eq(now, t)) { trace_rxrpc_timer(call, rxrpc_timer_exp_ping, now); cmpxchg(&call->ping_at, t, now + MAX_JIFFY_OFFSET); - set_bit(RXRPC_CALL_EV_PING, &call->events); + rxrpc_send_ACK(call, RXRPC_ACK_PING, 0, + rxrpc_propose_ack_ping_for_keepalive); } t = READ_ONCE(call->resend_at); @@ -388,25 +422,10 @@ recheck_state: goto recheck_state; } - send_ack = NULL; if (test_and_clear_bit(RXRPC_CALL_EV_ACK_LOST, &call->events)) { call->acks_lost_top = call->tx_top; - rxrpc_propose_ACK(call, RXRPC_ACK_PING, 0, true, false, - rxrpc_propose_ack_ping_for_lost_ack); - send_ack = &call->acks_lost_ping; - } - - if (test_and_clear_bit(RXRPC_CALL_EV_ACK, &call->events) || - send_ack) { - if (call->ackr_reason) { - rxrpc_send_ack_packet(call, false, send_ack); - goto recheck_state; - } - } - - if (test_and_clear_bit(RXRPC_CALL_EV_PING, &call->events)) { - rxrpc_send_ack_packet(call, true, NULL); - goto recheck_state; + rxrpc_send_ACK(call, RXRPC_ACK_PING, 0, + rxrpc_propose_ack_ping_for_lost_ack); } if (test_and_clear_bit(RXRPC_CALL_EV_RESEND, &call->events) && diff --git a/net/rxrpc/input.c b/net/rxrpc/input.c index 0cb23ba79e3b..e23f66ef8c06 100644 --- a/net/rxrpc/input.c +++ b/net/rxrpc/input.c @@ -398,8 +398,7 @@ static void rxrpc_input_data(struct rxrpc_call *call, struct sk_buff *skb) unsigned int j, nr_subpackets, nr_unacked = 0; rxrpc_serial_t serial = sp->hdr.serial, ack_serial = serial; rxrpc_seq_t seq0 = sp->hdr.seq, hard_ack; - bool immediate_ack = false, jumbo_bad = false; - u8 ack = 0; + bool jumbo_bad = false; _enter("{%u,%u},{%u,%u}", call->rx_hard_ack, call->rx_top, skb->len, seq0); @@ -447,9 +446,9 @@ static void rxrpc_input_data(struct rxrpc_call *call, struct sk_buff *skb) nr_subpackets = sp->nr_subpackets; if (nr_subpackets > 1) { if (call->nr_jumbo_bad > 3) { - ack = RXRPC_ACK_NOSPACE; - ack_serial = serial; - goto ack; + rxrpc_send_ACK(call, RXRPC_ACK_NOSPACE, serial, + rxrpc_propose_ack_input_data); + goto unlock; } } @@ -459,6 +458,7 @@ static void rxrpc_input_data(struct rxrpc_call *call, struct sk_buff *skb) unsigned int ix = seq & RXRPC_RXTX_BUFF_MASK; bool terminal = (j == nr_subpackets - 1); bool last = terminal && (sp->rx_flags & RXRPC_SKB_INCL_LAST); + bool acked = false; u8 flags, annotation = j; _proto("Rx DATA+%u %%%u { #%x t=%u l=%u }", @@ -488,25 +488,22 @@ static void rxrpc_input_data(struct rxrpc_call *call, struct sk_buff *skb) trace_rxrpc_rx_data(call->debug_id, seq, serial, flags, annotation); if (before_eq(seq, hard_ack)) { - ack = RXRPC_ACK_DUPLICATE; - ack_serial = serial; + rxrpc_send_ACK(call, RXRPC_ACK_DUPLICATE, serial, + rxrpc_propose_ack_input_data); continue; } if (call->rxtx_buffer[ix]) { rxrpc_input_dup_data(call, seq, nr_subpackets > 1, &jumbo_bad); - if (ack != RXRPC_ACK_DUPLICATE) { - ack = RXRPC_ACK_DUPLICATE; - ack_serial = serial; - } - immediate_ack = true; + rxrpc_send_ACK(call, RXRPC_ACK_DUPLICATE, serial, + rxrpc_propose_ack_input_data); continue; } if (after(seq, hard_ack + call->rx_winsize)) { - ack = RXRPC_ACK_EXCEEDS_WINDOW; - ack_serial = serial; + rxrpc_send_ACK(call, RXRPC_ACK_EXCEEDS_WINDOW, serial, + rxrpc_propose_ack_input_data); if (flags & RXRPC_JUMBO_PACKET) { if (!jumbo_bad) { call->nr_jumbo_bad++; @@ -514,12 +511,13 @@ static void rxrpc_input_data(struct rxrpc_call *call, struct sk_buff *skb) } } - goto ack; + goto unlock; } - if (flags & RXRPC_REQUEST_ACK && !ack) { - ack = RXRPC_ACK_REQUESTED; - ack_serial = serial; + if (flags & RXRPC_REQUEST_ACK) { + rxrpc_send_ACK(call, RXRPC_ACK_REQUESTED, serial, + rxrpc_propose_ack_input_data); + acked = true; } if (after(seq0, call->ackr_highest_seq)) @@ -542,11 +540,11 @@ static void rxrpc_input_data(struct rxrpc_call *call, struct sk_buff *skb) smp_store_release(&call->rx_top, seq); } else if (before(seq, call->rx_top)) { /* Send an immediate ACK if we fill in a hole */ - if (!ack) { - ack = RXRPC_ACK_DELAY; - ack_serial = serial; + if (!acked) { + rxrpc_send_ACK(call, RXRPC_ACK_DELAY, serial, + rxrpc_propose_ack_input_data); + acked = true; } - immediate_ack = true; } if (terminal) { @@ -558,14 +556,8 @@ static void rxrpc_input_data(struct rxrpc_call *call, struct sk_buff *skb) sp = NULL; } - nr_unacked++; - if (last) { set_bit(RXRPC_CALL_RX_LAST, &call->flags); - if (!ack) { - ack = RXRPC_ACK_DELAY; - ack_serial = serial; - } trace_rxrpc_receive(call, rxrpc_receive_queue_last, serial, seq); } else { trace_rxrpc_receive(call, rxrpc_receive_queue, serial, seq); @@ -574,32 +566,30 @@ static void rxrpc_input_data(struct rxrpc_call *call, struct sk_buff *skb) if (after_eq(seq, call->rx_expect_next)) { if (after(seq, call->rx_expect_next)) { _net("OOS %u > %u", seq, call->rx_expect_next); - ack = RXRPC_ACK_OUT_OF_SEQUENCE; - ack_serial = serial; + rxrpc_send_ACK(call, RXRPC_ACK_OUT_OF_SEQUENCE, serial, + rxrpc_propose_ack_input_data); + acked = true; } call->rx_expect_next = seq + 1; } - if (!ack) + + if (!acked) { + nr_unacked++; ack_serial = serial; + } } -ack: - if (atomic_add_return(nr_unacked, &call->ackr_nr_unacked) > 2 && !ack) - ack = RXRPC_ACK_IDLE; - - if (ack) - rxrpc_propose_ACK(call, ack, ack_serial, - immediate_ack, true, - rxrpc_propose_ack_input_data); +unlock: + if (atomic_add_return(nr_unacked, &call->ackr_nr_unacked) > 2) + rxrpc_send_ACK(call, RXRPC_ACK_IDLE, ack_serial, + rxrpc_propose_ack_input_data); else - rxrpc_propose_ACK(call, RXRPC_ACK_DELAY, serial, - false, true, + rxrpc_propose_ACK(call, RXRPC_ACK_DELAY, ack_serial, rxrpc_propose_ack_input_data); trace_rxrpc_notify_socket(call->debug_id, serial); rxrpc_notify_socket(call); -unlock: spin_unlock(&call->input_lock); rxrpc_free_skb(skb, rxrpc_skb_freed); _leave(" [queued]"); @@ -893,13 +883,11 @@ static void rxrpc_input_ack(struct rxrpc_call *call, struct sk_buff *skb) if (buf.ack.reason == RXRPC_ACK_PING) { _proto("Rx ACK %%%u PING Request", ack_serial); - rxrpc_propose_ACK(call, RXRPC_ACK_PING_RESPONSE, - ack_serial, true, true, - rxrpc_propose_ack_respond_to_ping); + rxrpc_send_ACK(call, RXRPC_ACK_PING_RESPONSE, ack_serial, + rxrpc_propose_ack_respond_to_ping); } else if (sp->hdr.flags & RXRPC_REQUEST_ACK) { - rxrpc_propose_ACK(call, RXRPC_ACK_REQUESTED, - ack_serial, true, true, - rxrpc_propose_ack_respond_to_ack); + rxrpc_send_ACK(call, RXRPC_ACK_REQUESTED, ack_serial, + rxrpc_propose_ack_respond_to_ack); } /* If we get an EXCEEDS_WINDOW ACK from the server, it probably @@ -1011,9 +999,8 @@ static void rxrpc_input_ack(struct rxrpc_call *call, struct sk_buff *skb) RXRPC_TX_ANNO_LAST && summary.nr_acks == call->tx_top - hard_ack && rxrpc_is_client_call(call)) - rxrpc_propose_ACK(call, RXRPC_ACK_PING, ack_serial, - false, true, - rxrpc_propose_ack_ping_for_lost_reply); + rxrpc_propose_ping(call, ack_serial, + rxrpc_propose_ack_ping_for_lost_reply); rxrpc_congestion_management(call, skb, &summary, acked_serial); out: diff --git a/net/rxrpc/local_object.c b/net/rxrpc/local_object.c index e95e75e785fb..a178f71e5082 100644 --- a/net/rxrpc/local_object.c +++ b/net/rxrpc/local_object.c @@ -97,6 +97,8 @@ static struct rxrpc_local *rxrpc_alloc_local(struct rxrpc_net *rxnet, local->rxnet = rxnet; INIT_HLIST_NODE(&local->link); INIT_WORK(&local->processor, rxrpc_local_processor); + INIT_LIST_HEAD(&local->ack_tx_queue); + spin_lock_init(&local->ack_tx_lock); init_rwsem(&local->defrag_sem); skb_queue_head_init(&local->reject_queue); skb_queue_head_init(&local->event_queue); @@ -432,6 +434,11 @@ static void rxrpc_local_processor(struct work_struct *work) break; } + if (!list_empty(&local->ack_tx_queue)) { + rxrpc_transmit_ack_packets(local); + again = true; + } + if (!skb_queue_empty(&local->reject_queue)) { rxrpc_reject_packets(local); again = true; diff --git a/net/rxrpc/output.c b/net/rxrpc/output.c index 71885d741987..1edbc678e8be 100644 --- a/net/rxrpc/output.c +++ b/net/rxrpc/output.c @@ -16,14 +16,6 @@ #include #include "ar-internal.h" -struct rxrpc_ack_buffer { - struct rxrpc_wire_header whdr; - struct rxrpc_ackpacket ack; - u8 acks[255]; - u8 pad[3]; - struct rxrpc_ackinfo ackinfo; -}; - extern int udpv6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len); static ssize_t do_udp_sendmsg(struct socket *sk, struct msghdr *msg, size_t len) @@ -82,22 +74,21 @@ static void rxrpc_set_keepalive(struct rxrpc_call *call) */ static size_t rxrpc_fill_out_ack(struct rxrpc_connection *conn, struct rxrpc_call *call, - struct rxrpc_ack_buffer *pkt, + struct rxrpc_txbuf *txb, rxrpc_seq_t *_hard_ack, - rxrpc_seq_t *_top, - u8 reason) + rxrpc_seq_t *_top) { - rxrpc_serial_t serial; + struct rxrpc_ackinfo ackinfo; unsigned int tmp; rxrpc_seq_t hard_ack, top, seq; int ix; u32 mtu, jmax; - u8 *ackp = pkt->acks; + u8 *ackp = txb->acks; tmp = atomic_xchg(&call->ackr_nr_unacked, 0); tmp |= atomic_xchg(&call->ackr_nr_consumed, 0); - if (!tmp && (reason == RXRPC_ACK_DELAY || - reason == RXRPC_ACK_IDLE)) { + if (!tmp && (txb->ack.reason == RXRPC_ACK_DELAY || + txb->ack.reason == RXRPC_ACK_IDLE)) { rxrpc_inc_stat(call->rxnet, stat_tx_ack_skip); return 0; } @@ -105,24 +96,16 @@ static size_t rxrpc_fill_out_ack(struct rxrpc_connection *conn, rxrpc_inc_stat(call->rxnet, stat_tx_ack_fill); /* Barrier against rxrpc_input_data(). */ - serial = call->ackr_serial; hard_ack = READ_ONCE(call->rx_hard_ack); top = smp_load_acquire(&call->rx_top); *_hard_ack = hard_ack; *_top = top; - pkt->ack.bufferSpace = htons(0); - pkt->ack.maxSkew = htons(0); - pkt->ack.firstPacket = htonl(hard_ack + 1); - pkt->ack.previousPacket = htonl(call->ackr_highest_seq); - pkt->ack.serial = htonl(serial); - pkt->ack.reason = reason; - pkt->ack.nAcks = top - hard_ack; + txb->ack.firstPacket = htonl(hard_ack + 1); + txb->ack.previousPacket = htonl(call->ackr_highest_seq); + txb->ack.nAcks = top - hard_ack; - if (reason == RXRPC_ACK_PING) - pkt->whdr.flags |= RXRPC_REQUEST_ACK; - - if (after(top, hard_ack)) { + if (txb->ack.nAcks) { seq = hard_ack + 1; do { ix = seq & RXRPC_RXTX_BUFF_MASK; @@ -137,15 +120,16 @@ static size_t rxrpc_fill_out_ack(struct rxrpc_connection *conn, mtu = conn->params.peer->if_mtu; mtu -= conn->params.peer->hdrsize; jmax = (call->nr_jumbo_bad > 3) ? 1 : rxrpc_rx_jumbo_max; - pkt->ackinfo.rxMTU = htonl(rxrpc_rx_mtu); - pkt->ackinfo.maxMTU = htonl(mtu); - pkt->ackinfo.rwind = htonl(call->rx_winsize); - pkt->ackinfo.jumbo_max = htonl(jmax); + ackinfo.rxMTU = htonl(rxrpc_rx_mtu); + ackinfo.maxMTU = htonl(mtu); + ackinfo.rwind = htonl(call->rx_winsize); + ackinfo.jumbo_max = htonl(jmax); *ackp++ = 0; *ackp++ = 0; *ackp++ = 0; - return top - hard_ack + 3; + memcpy(ackp, &ackinfo, sizeof(ackinfo)); + return top - hard_ack + 3 + sizeof(ackinfo); } /* @@ -194,26 +178,21 @@ static void rxrpc_cancel_rtt_probe(struct rxrpc_call *call, /* * Send an ACK call packet. */ -int rxrpc_send_ack_packet(struct rxrpc_call *call, bool ping, - rxrpc_serial_t *_serial) +static int rxrpc_send_ack_packet(struct rxrpc_local *local, struct rxrpc_txbuf *txb) { struct rxrpc_connection *conn; struct rxrpc_ack_buffer *pkt; + struct rxrpc_call *call = txb->call; struct msghdr msg; - struct kvec iov[2]; + struct kvec iov[1]; rxrpc_serial_t serial; rxrpc_seq_t hard_ack, top; size_t len, n; int ret, rtt_slot = -1; - u8 reason; if (test_bit(RXRPC_CALL_DISCONNECTED, &call->flags)) return -ECONNRESET; - pkt = kzalloc(sizeof(*pkt), GFP_KERNEL); - if (!pkt) - return -ENOMEM; - conn = call->conn; msg.msg_name = &call->peer->srx.transport; @@ -222,85 +201,93 @@ int rxrpc_send_ack_packet(struct rxrpc_call *call, bool ping, msg.msg_controllen = 0; msg.msg_flags = 0; - pkt->whdr.epoch = htonl(conn->proto.epoch); - pkt->whdr.cid = htonl(call->cid); - pkt->whdr.callNumber = htonl(call->call_id); - pkt->whdr.seq = 0; - pkt->whdr.type = RXRPC_PACKET_TYPE_ACK; - pkt->whdr.flags = RXRPC_SLOW_START_OK | conn->out_clientflag; - pkt->whdr.userStatus = 0; - pkt->whdr.securityIndex = call->security_ix; - pkt->whdr._rsvd = 0; - pkt->whdr.serviceId = htons(call->service_id); + if (txb->ack.reason == RXRPC_ACK_PING) + txb->wire.flags |= RXRPC_REQUEST_ACK; spin_lock_bh(&call->lock); - if (ping) { - reason = RXRPC_ACK_PING; - } else { - reason = call->ackr_reason; - if (!call->ackr_reason) { - spin_unlock_bh(&call->lock); - ret = 0; - goto out; - } - call->ackr_reason = 0; - } - n = rxrpc_fill_out_ack(conn, call, pkt, &hard_ack, &top, reason); - + n = rxrpc_fill_out_ack(conn, call, txb, &hard_ack, &top); spin_unlock_bh(&call->lock); if (n == 0) { kfree(pkt); return 0; } - iov[0].iov_base = pkt; - iov[0].iov_len = sizeof(pkt->whdr) + sizeof(pkt->ack) + n; - iov[1].iov_base = &pkt->ackinfo; - iov[1].iov_len = sizeof(pkt->ackinfo); - len = iov[0].iov_len + iov[1].iov_len; + iov[0].iov_base = &txb->wire; + iov[0].iov_len = sizeof(txb->wire) + sizeof(txb->ack) + n; + len = iov[0].iov_len; serial = atomic_inc_return(&conn->serial); - pkt->whdr.serial = htonl(serial); + txb->wire.serial = htonl(serial); trace_rxrpc_tx_ack(call->debug_id, serial, - ntohl(pkt->ack.firstPacket), - ntohl(pkt->ack.serial), - pkt->ack.reason, pkt->ack.nAcks); - if (_serial) - *_serial = serial; + ntohl(txb->ack.firstPacket), + ntohl(txb->ack.serial), txb->ack.reason, txb->ack.nAcks); + if (txb->ack_why == rxrpc_propose_ack_ping_for_lost_ack) + call->acks_lost_ping = serial; - if (ping) + if (txb->ack.reason == RXRPC_ACK_PING) rtt_slot = rxrpc_begin_rtt_probe(call, serial, rxrpc_rtt_tx_ping); rxrpc_inc_stat(call->rxnet, stat_tx_ack_send); - iov_iter_kvec(&msg.msg_iter, WRITE, iov, 2, len); + iov_iter_kvec(&msg.msg_iter, WRITE, iov, 1, len); ret = do_udp_sendmsg(conn->params.local->socket, &msg, len); call->peer->last_tx_at = ktime_get_seconds(); if (ret < 0) trace_rxrpc_tx_fail(call->debug_id, serial, ret, rxrpc_tx_point_call_ack); else - trace_rxrpc_tx_packet(call->debug_id, &pkt->whdr, + trace_rxrpc_tx_packet(call->debug_id, &txb->wire, rxrpc_tx_point_call_ack); rxrpc_tx_backoff(call, ret); if (call->state < RXRPC_CALL_COMPLETE) { - if (ret < 0) { + if (ret < 0) rxrpc_cancel_rtt_probe(call, serial, rtt_slot); - rxrpc_propose_ACK(call, pkt->ack.reason, - ntohl(pkt->ack.serial), - false, true, - rxrpc_propose_ack_retry_tx); - } - rxrpc_set_keepalive(call); } -out: kfree(pkt); return ret; } +/* + * ACK transmitter for a local endpoint. The UDP socket locks around each + * transmission, so we can only transmit one packet at a time, ACK, DATA or + * otherwise. + */ +void rxrpc_transmit_ack_packets(struct rxrpc_local *local) +{ + LIST_HEAD(queue); + int ret; + + trace_rxrpc_local(local->debug_id, rxrpc_local_tx_ack, + refcount_read(&local->ref), NULL); + + if (list_empty(&local->ack_tx_queue)) + return; + + spin_lock_bh(&local->ack_tx_lock); + list_splice_tail_init(&local->ack_tx_queue, &queue); + spin_unlock_bh(&local->ack_tx_lock); + + while (!list_empty(&queue)) { + struct rxrpc_txbuf *txb = + list_entry(queue.next, struct rxrpc_txbuf, tx_link); + + ret = rxrpc_send_ack_packet(local, txb); + if (ret < 0 && ret != -ECONNRESET) { + spin_lock_bh(&local->ack_tx_lock); + list_splice_init(&queue, &local->ack_tx_queue); + spin_unlock_bh(&local->ack_tx_lock); + break; + } + + list_del_init(&txb->tx_link); + rxrpc_put_call(txb->call, rxrpc_call_put); + rxrpc_put_txbuf(txb, rxrpc_txbuf_put_ack_tx); + } +} + /* * Send an ABORT call packet. */ diff --git a/net/rxrpc/recvmsg.c b/net/rxrpc/recvmsg.c index a378e7a672a8..104dd4a29f05 100644 --- a/net/rxrpc/recvmsg.c +++ b/net/rxrpc/recvmsg.c @@ -189,7 +189,7 @@ static void rxrpc_end_rx_phase(struct rxrpc_call *call, rxrpc_serial_t serial) ASSERTCMP(call->rx_hard_ack, ==, call->rx_top); if (call->state == RXRPC_CALL_CLIENT_RECV_REPLY) { - rxrpc_propose_ACK(call, RXRPC_ACK_IDLE, serial, false, true, + rxrpc_propose_ACK(call, RXRPC_ACK_IDLE, serial, rxrpc_propose_ack_terminal_ack); //rxrpc_send_ack_packet(call, false, NULL); } @@ -206,7 +206,7 @@ static void rxrpc_end_rx_phase(struct rxrpc_call *call, rxrpc_serial_t serial) call->state = RXRPC_CALL_SERVER_ACK_REQUEST; call->expect_req_by = jiffies + MAX_JIFFY_OFFSET; write_unlock_bh(&call->state_lock); - rxrpc_propose_ACK(call, RXRPC_ACK_DELAY, serial, false, true, + rxrpc_propose_ACK(call, RXRPC_ACK_DELAY, serial, rxrpc_propose_ack_processing_op); break; default: @@ -259,12 +259,11 @@ static void rxrpc_rotate_rx_window(struct rxrpc_call *call) rxrpc_end_rx_phase(call, serial); } else { /* Check to see if there's an ACK that needs sending. */ - if (atomic_inc_return(&call->ackr_nr_consumed) > 2) - rxrpc_propose_ACK(call, RXRPC_ACK_IDLE, serial, - true, false, - rxrpc_propose_ack_rotate_rx); - if (call->ackr_reason && call->ackr_reason != RXRPC_ACK_DELAY) - rxrpc_send_ack_packet(call, false, NULL); + if (atomic_inc_return(&call->ackr_nr_consumed) > 2) { + rxrpc_send_ACK(call, RXRPC_ACK_IDLE, serial, + rxrpc_propose_ack_rotate_rx); + rxrpc_transmit_ack_packets(call->peer->local); + } } } @@ -363,10 +362,6 @@ static int rxrpc_recvmsg_data(struct socket *sock, struct rxrpc_call *call, unsigned int rx_pkt_offset, rx_pkt_len; int ix, copy, ret = -EAGAIN, ret2; - if (test_and_clear_bit(RXRPC_CALL_RX_UNDERRUN, &call->flags) && - call->ackr_reason) - rxrpc_send_ack_packet(call, false, NULL); - rx_pkt_offset = call->rx_pkt_offset; rx_pkt_len = call->rx_pkt_len; rx_pkt_last = call->rx_pkt_last; @@ -389,6 +384,7 @@ static int rxrpc_recvmsg_data(struct socket *sock, struct rxrpc_call *call, if (!skb) { trace_rxrpc_recvmsg(call, rxrpc_recvmsg_hole, seq, rx_pkt_offset, rx_pkt_len, 0); + rxrpc_transmit_ack_packets(call->peer->local); break; } smp_rmb(); @@ -604,6 +600,7 @@ try_again: if (ret == -EAGAIN) ret = 0; + rxrpc_transmit_ack_packets(call->peer->local); if (after(call->rx_top, call->rx_hard_ack) && call->rxtx_buffer[(call->rx_hard_ack + 1) & RXRPC_RXTX_BUFF_MASK]) rxrpc_notify_socket(call); @@ -734,17 +731,7 @@ int rxrpc_kernel_recv_data(struct socket *sock, struct rxrpc_call *call, read_phase_complete: ret = 1; out: - switch (call->ackr_reason) { - case RXRPC_ACK_IDLE: - break; - case RXRPC_ACK_DELAY: - if (ret != -EAGAIN) - break; - fallthrough; - default: - rxrpc_send_ack_packet(call, false, NULL); - } - + rxrpc_transmit_ack_packets(call->peer->local); if (_service) *_service = call->service_id; mutex_unlock(&call->user_mutex); diff --git a/net/rxrpc/sendmsg.c b/net/rxrpc/sendmsg.c index b2d28aa12e10..ef4949259020 100644 --- a/net/rxrpc/sendmsg.c +++ b/net/rxrpc/sendmsg.c @@ -332,9 +332,7 @@ reload: rxrpc_see_skb(skb, rxrpc_skb_seen); do { - /* Check to see if there's a ping ACK to reply to. */ - if (call->ackr_reason == RXRPC_ACK_PING_RESPONSE) - rxrpc_send_ack_packet(call, false, NULL); + rxrpc_transmit_ack_packets(call->peer->local); if (!skb) { size_t remain, bufsize, chunk, offset; diff --git a/net/rxrpc/txbuf.c b/net/rxrpc/txbuf.c index 66d922ad65d0..45a7b48a5e10 100644 --- a/net/rxrpc/txbuf.c +++ b/net/rxrpc/txbuf.c @@ -33,6 +33,7 @@ struct rxrpc_txbuf *rxrpc_alloc_txbuf(struct rxrpc_call *call, u8 packet_type, txb->len = 0; txb->offset = 0; txb->flags = 0; + txb->ack_why = 0; txb->seq = call->tx_top + 1; txb->wire.epoch = htonl(call->conn->proto.epoch); txb->wire.cid = htonl(call->cid); From 530403d9ba1c3f51c721a394f642e56309072295 Mon Sep 17 00:00:00 2001 From: David Howells Date: Thu, 30 Jan 2020 21:48:14 +0000 Subject: [PATCH 0732/2002] rxrpc: Clean up ACK handling Clean up the rxrpc_propose_ACK() function. If deferred PING ACK proposal is split out, it's only really needed for deferred DELAY ACKs. All other ACKs, bar terminal IDLE ACK are sent immediately. The deferred IDLE ACK submission can be handled by conversion of a DELAY ACK into an IDLE ACK if there's nothing to be SACK'd. Also, because there's a delay between an ACK being generated and being transmitted, it's possible that other ACKs of the same type will be generated during that interval. Apart from the ACK time and the serial number responded to, most of the ACK body, including window and SACK parameters, are not filled out till the point of transmission - so we can avoid generating a new ACK if there's one pending that will cover the SACK data we need to convey. Therefore, don't propose a new DELAY or IDLE ACK for a call if there's one already pending. Signed-off-by: David Howells cc: Marc Dionne cc: linux-afs@lists.infradead.org --- include/trace/events/rxrpc.h | 52 ++++++++++++++------ net/rxrpc/ar-internal.h | 10 ++-- net/rxrpc/call_event.c | 95 ++++++++++-------------------------- net/rxrpc/call_object.c | 2 +- net/rxrpc/input.c | 8 +-- net/rxrpc/misc.c | 18 ------- net/rxrpc/output.c | 7 +++ net/rxrpc/protocol.h | 7 --- net/rxrpc/recvmsg.c | 14 +++--- net/rxrpc/sendmsg.c | 2 +- net/rxrpc/sysctl.c | 9 ---- 11 files changed, 86 insertions(+), 138 deletions(-) diff --git a/include/trace/events/rxrpc.h b/include/trace/events/rxrpc.h index 1597ff7ad97e..d32e9858c682 100644 --- a/include/trace/events/rxrpc.h +++ b/include/trace/events/rxrpc.h @@ -158,6 +158,7 @@ #define rxrpc_propose_ack_traces \ EM(rxrpc_propose_ack_client_tx_end, "ClTxEnd") \ EM(rxrpc_propose_ack_input_data, "DataIn ") \ + EM(rxrpc_propose_ack_input_data_hole, "DataInH") \ EM(rxrpc_propose_ack_ping_for_check_life, "ChkLife") \ EM(rxrpc_propose_ack_ping_for_keepalive, "KeepAlv") \ EM(rxrpc_propose_ack_ping_for_lost_ack, "LostAck") \ @@ -170,11 +171,6 @@ EM(rxrpc_propose_ack_rotate_rx, "RxAck ") \ E_(rxrpc_propose_ack_terminal_ack, "ClTerm ") -#define rxrpc_propose_ack_outcomes \ - EM(rxrpc_propose_ack_subsume, " Subsume") \ - EM(rxrpc_propose_ack_update, " Update") \ - E_(rxrpc_propose_ack_use, " New") - #define rxrpc_congest_modes \ EM(RXRPC_CALL_CONGEST_AVOIDANCE, "CongAvoid") \ EM(RXRPC_CALL_FAST_RETRANSMIT, "FastReTx ") \ @@ -313,7 +309,6 @@ rxrpc_congest_changes; rxrpc_congest_modes; rxrpc_conn_traces; rxrpc_local_traces; -rxrpc_propose_ack_outcomes; rxrpc_propose_ack_traces; rxrpc_receive_traces; rxrpc_recvmsg_traces; @@ -1012,7 +1007,7 @@ TRACE_EVENT(rxrpc_timer, __entry->call = call->debug_id; __entry->why = why; __entry->now = now; - __entry->ack_at = call->ack_at; + __entry->ack_at = call->delay_ack_at; __entry->ack_lost_at = call->ack_lost_at; __entry->resend_at = call->resend_at; __entry->expect_rx_by = call->expect_rx_by; @@ -1054,7 +1049,7 @@ TRACE_EVENT(rxrpc_timer_expired, TP_fast_assign( __entry->call = call->debug_id; __entry->now = now; - __entry->ack_at = call->ack_at; + __entry->ack_at = call->delay_ack_at; __entry->ack_lost_at = call->ack_lost_at; __entry->resend_at = call->resend_at; __entry->expect_rx_by = call->expect_rx_by; @@ -1098,17 +1093,15 @@ TRACE_EVENT(rxrpc_rx_lose, TRACE_EVENT(rxrpc_propose_ack, TP_PROTO(struct rxrpc_call *call, enum rxrpc_propose_ack_trace why, - u8 ack_reason, rxrpc_serial_t serial, - enum rxrpc_propose_ack_outcome outcome), + u8 ack_reason, rxrpc_serial_t serial), - TP_ARGS(call, why, ack_reason, serial, outcome), + TP_ARGS(call, why, ack_reason, serial), TP_STRUCT__entry( __field(unsigned int, call ) __field(enum rxrpc_propose_ack_trace, why ) __field(rxrpc_serial_t, serial ) __field(u8, ack_reason ) - __field(enum rxrpc_propose_ack_outcome, outcome ) ), TP_fast_assign( @@ -1116,15 +1109,13 @@ TRACE_EVENT(rxrpc_propose_ack, __entry->why = why; __entry->serial = serial; __entry->ack_reason = ack_reason; - __entry->outcome = outcome; ), - TP_printk("c=%08x %s %s r=%08x%s", + TP_printk("c=%08x %s %s r=%08x", __entry->call, __print_symbolic(__entry->why, rxrpc_propose_ack_traces), __print_symbolic(__entry->ack_reason, rxrpc_ack_names), - __entry->serial, - __print_symbolic(__entry->outcome, rxrpc_propose_ack_outcomes)) + __entry->serial) ); TRACE_EVENT(rxrpc_send_ack, @@ -1154,6 +1145,35 @@ TRACE_EVENT(rxrpc_send_ack, __entry->serial) ); +TRACE_EVENT(rxrpc_drop_ack, + TP_PROTO(struct rxrpc_call *call, enum rxrpc_propose_ack_trace why, + u8 ack_reason, rxrpc_serial_t serial, bool nobuf), + + TP_ARGS(call, why, ack_reason, serial, nobuf), + + TP_STRUCT__entry( + __field(unsigned int, call ) + __field(enum rxrpc_propose_ack_trace, why ) + __field(rxrpc_serial_t, serial ) + __field(u8, ack_reason ) + __field(bool, nobuf ) + ), + + TP_fast_assign( + __entry->call = call->debug_id; + __entry->why = why; + __entry->serial = serial; + __entry->ack_reason = ack_reason; + __entry->nobuf = nobuf; + ), + + TP_printk("c=%08x %s %s r=%08x nbf=%u", + __entry->call, + __print_symbolic(__entry->why, rxrpc_propose_ack_traces), + __print_symbolic(__entry->ack_reason, rxrpc_ack_names), + __entry->serial, __entry->nobuf) + ); + TRACE_EVENT(rxrpc_retransmit, TP_PROTO(struct rxrpc_call *call, rxrpc_seq_t seq, u8 annotation, s64 expiry), diff --git a/net/rxrpc/ar-internal.h b/net/rxrpc/ar-internal.h index 802a8f372af2..5a81545a58d5 100644 --- a/net/rxrpc/ar-internal.h +++ b/net/rxrpc/ar-internal.h @@ -516,6 +516,8 @@ enum rxrpc_call_flag { RXRPC_CALL_DISCONNECTED, /* The call has been disconnected */ RXRPC_CALL_KERNEL, /* The call was made by the kernel */ RXRPC_CALL_UPGRADE, /* Service upgrade was requested for the call */ + RXRPC_CALL_DELAY_ACK_PENDING, /* DELAY ACK generation is pending */ + RXRPC_CALL_IDLE_ACK_PENDING, /* IDLE ACK generation is pending */ }; /* @@ -582,7 +584,7 @@ struct rxrpc_call { struct rxrpc_net *rxnet; /* Network namespace to which call belongs */ const struct rxrpc_security *security; /* applied security module */ struct mutex user_mutex; /* User access mutex */ - unsigned long ack_at; /* When deferred ACK needs to happen */ + unsigned long delay_ack_at; /* When DELAY ACK needs to happen */ unsigned long ack_lost_at; /* When ACK is figured as lost */ unsigned long resend_at; /* When next resend needs to happen */ unsigned long ping_at; /* When next to send a ping */ @@ -834,7 +836,8 @@ int rxrpc_user_charge_accept(struct rxrpc_sock *, unsigned long); void rxrpc_propose_ping(struct rxrpc_call *call, u32 serial, enum rxrpc_propose_ack_trace why); void rxrpc_send_ACK(struct rxrpc_call *, u8, rxrpc_serial_t, enum rxrpc_propose_ack_trace); -void rxrpc_propose_ACK(struct rxrpc_call *, u8, u32, enum rxrpc_propose_ack_trace); +void rxrpc_propose_delay_ACK(struct rxrpc_call *, rxrpc_serial_t, + enum rxrpc_propose_ack_trace); void rxrpc_process_call(struct work_struct *); void rxrpc_reduce_call_timer(struct rxrpc_call *call, @@ -1016,15 +1019,12 @@ static inline bool __rxrpc_use_local(struct rxrpc_local *local) * misc.c */ extern unsigned int rxrpc_max_backlog __read_mostly; -extern unsigned long rxrpc_requested_ack_delay; extern unsigned long rxrpc_soft_ack_delay; extern unsigned long rxrpc_idle_ack_delay; extern unsigned int rxrpc_rx_window_size; extern unsigned int rxrpc_rx_mtu; extern unsigned int rxrpc_rx_jumbo_max; -extern const s8 rxrpc_ack_priority[]; - /* * net_ns.c */ diff --git a/net/rxrpc/call_event.c b/net/rxrpc/call_event.c index 67b54ad914a1..36f60ac1d95d 100644 --- a/net/rxrpc/call_event.c +++ b/net/rxrpc/call_event.c @@ -29,70 +29,29 @@ void rxrpc_propose_ping(struct rxrpc_call *call, u32 serial, spin_lock_bh(&call->lock); if (time_before(ping_at, call->ping_at)) { - rxrpc_inc_stat(call->rxnet, stat_tx_acks[RXRPC_ACK_PING]); WRITE_ONCE(call->ping_at, ping_at); rxrpc_reduce_call_timer(call, ping_at, now, rxrpc_timer_set_for_ping); - trace_rxrpc_propose_ack(call, why, RXRPC_ACK_PING, serial, - rxrpc_propose_ack_use); + trace_rxrpc_propose_ack(call, why, RXRPC_ACK_PING, serial); } spin_unlock_bh(&call->lock); } /* - * propose an ACK be sent + * Propose a DELAY ACK be sent in the future. */ -static void __rxrpc_propose_ACK(struct rxrpc_call *call, u8 ack_reason, - u32 serial, enum rxrpc_propose_ack_trace why) +static void __rxrpc_propose_delay_ACK(struct rxrpc_call *call, + rxrpc_serial_t serial, + enum rxrpc_propose_ack_trace why) { - enum rxrpc_propose_ack_outcome outcome = rxrpc_propose_ack_use; unsigned long expiry = rxrpc_soft_ack_delay; unsigned long now = jiffies, ack_at; - s8 prior = rxrpc_ack_priority[ack_reason]; - - rxrpc_inc_stat(call->rxnet, stat_tx_acks[ack_reason]); - - /* Update DELAY, IDLE, REQUESTED and PING_RESPONSE ACK serial - * numbers, but we don't alter the timeout. - */ - _debug("prior %u %u vs %u %u", - ack_reason, prior, - call->ackr_reason, rxrpc_ack_priority[call->ackr_reason]); - if (ack_reason == call->ackr_reason) { - if (RXRPC_ACK_UPDATEABLE & (1 << ack_reason)) { - outcome = rxrpc_propose_ack_update; - call->ackr_serial = serial; - } - } else if (prior > rxrpc_ack_priority[call->ackr_reason]) { - call->ackr_reason = ack_reason; - call->ackr_serial = serial; - } else { - outcome = rxrpc_propose_ack_subsume; - } - - switch (ack_reason) { - case RXRPC_ACK_REQUESTED: - if (rxrpc_requested_ack_delay < expiry) - expiry = rxrpc_requested_ack_delay; - break; - - case RXRPC_ACK_DELAY: - if (rxrpc_soft_ack_delay < expiry) - expiry = rxrpc_soft_ack_delay; - break; - - case RXRPC_ACK_IDLE: - if (rxrpc_idle_ack_delay < expiry) - expiry = rxrpc_idle_ack_delay; - break; - - default: - WARN_ON(1); - return; - } + call->ackr_serial = serial; + if (rxrpc_soft_ack_delay < expiry) + expiry = rxrpc_soft_ack_delay; if (call->peer->srtt_us != 0) ack_at = usecs_to_jiffies(call->peer->srtt_us >> 3); else @@ -100,23 +59,23 @@ static void __rxrpc_propose_ACK(struct rxrpc_call *call, u8 ack_reason, ack_at += READ_ONCE(call->tx_backoff); ack_at += now; - if (time_before(ack_at, call->ack_at)) { - WRITE_ONCE(call->ack_at, ack_at); + if (time_before(ack_at, call->delay_ack_at)) { + WRITE_ONCE(call->delay_ack_at, ack_at); rxrpc_reduce_call_timer(call, ack_at, now, rxrpc_timer_set_for_ack); } - trace_rxrpc_propose_ack(call, why, ack_reason, serial, outcome); + trace_rxrpc_propose_ack(call, why, RXRPC_ACK_DELAY, serial); } /* - * propose an ACK be sent, locking the call structure + * Propose a DELAY ACK be sent, locking the call structure */ -void rxrpc_propose_ACK(struct rxrpc_call *call, u8 ack_reason, u32 serial, - enum rxrpc_propose_ack_trace why) +void rxrpc_propose_delay_ACK(struct rxrpc_call *call, rxrpc_serial_t serial, + enum rxrpc_propose_ack_trace why) { spin_lock_bh(&call->lock); - __rxrpc_propose_ACK(call, ack_reason, serial, why); + __rxrpc_propose_delay_ACK(call, serial, why); spin_unlock_bh(&call->lock); } @@ -131,6 +90,11 @@ void rxrpc_send_ACK(struct rxrpc_call *call, u8 ack_reason, if (test_bit(RXRPC_CALL_DISCONNECTED, &call->flags)) return; + if (ack_reason == RXRPC_ACK_DELAY && + test_and_set_bit(RXRPC_CALL_DELAY_ACK_PENDING, &call->flags)) { + trace_rxrpc_drop_ack(call, why, ack_reason, serial, false); + return; + } rxrpc_inc_stat(call->rxnet, stat_tx_acks[ack_reason]); @@ -319,7 +283,6 @@ void rxrpc_process_call(struct work_struct *work) unsigned long now, next, t; unsigned int iterations = 0; rxrpc_serial_t ackr_serial; - u8 ackr_reason; rxrpc_see_call(call); @@ -364,19 +327,13 @@ recheck_state: set_bit(RXRPC_CALL_EV_EXPIRED, &call->events); } - t = READ_ONCE(call->ack_at); + t = READ_ONCE(call->delay_ack_at); if (time_after_eq(now, t)) { trace_rxrpc_timer(call, rxrpc_timer_exp_ack, now); - cmpxchg(&call->ack_at, t, now + MAX_JIFFY_OFFSET); - spin_lock_bh(&call->lock); - ackr_reason = call->ackr_reason; - ackr_serial = call->ackr_serial; - call->ackr_reason = 0; - call->ackr_serial = 0; - spin_unlock_bh(&call->lock); - if (ackr_reason) - rxrpc_send_ACK(call, ackr_reason, ackr_serial, - rxrpc_propose_ack_ping_for_lost_ack); + cmpxchg(&call->delay_ack_at, t, now + MAX_JIFFY_OFFSET); + ackr_serial = xchg(&call->ackr_serial, 0); + rxrpc_send_ACK(call, RXRPC_ACK_DELAY, ackr_serial, + rxrpc_propose_ack_ping_for_lost_ack); } t = READ_ONCE(call->ack_lost_at); @@ -441,7 +398,7 @@ recheck_state: set(call->expect_req_by); set(call->expect_term_by); - set(call->ack_at); + set(call->delay_ack_at); set(call->ack_lost_at); set(call->resend_at); set(call->keepalive_at); diff --git a/net/rxrpc/call_object.c b/net/rxrpc/call_object.c index 8290d94e9233..8f9e88897197 100644 --- a/net/rxrpc/call_object.c +++ b/net/rxrpc/call_object.c @@ -222,7 +222,7 @@ static void rxrpc_start_call_timer(struct rxrpc_call *call) unsigned long now = jiffies; unsigned long j = now + MAX_JIFFY_OFFSET; - call->ack_at = j; + call->delay_ack_at = j; call->ack_lost_at = j; call->resend_at = j; call->ping_at = j; diff --git a/net/rxrpc/input.c b/net/rxrpc/input.c index e23f66ef8c06..4df1bdc4de6c 100644 --- a/net/rxrpc/input.c +++ b/net/rxrpc/input.c @@ -299,7 +299,7 @@ static bool rxrpc_receiving_reply(struct rxrpc_call *call) now = jiffies; timo = now + MAX_JIFFY_OFFSET; WRITE_ONCE(call->resend_at, timo); - WRITE_ONCE(call->ack_at, timo); + WRITE_ONCE(call->delay_ack_at, timo); trace_rxrpc_timer(call, rxrpc_timer_init_for_reply, now); } @@ -542,7 +542,7 @@ static void rxrpc_input_data(struct rxrpc_call *call, struct sk_buff *skb) /* Send an immediate ACK if we fill in a hole */ if (!acked) { rxrpc_send_ACK(call, RXRPC_ACK_DELAY, serial, - rxrpc_propose_ack_input_data); + rxrpc_propose_ack_input_data_hole); acked = true; } } @@ -584,8 +584,8 @@ unlock: rxrpc_send_ACK(call, RXRPC_ACK_IDLE, ack_serial, rxrpc_propose_ack_input_data); else - rxrpc_propose_ACK(call, RXRPC_ACK_DELAY, ack_serial, - rxrpc_propose_ack_input_data); + rxrpc_propose_delay_ACK(call, ack_serial, + rxrpc_propose_ack_input_data); trace_rxrpc_notify_socket(call->debug_id, serial); rxrpc_notify_socket(call); diff --git a/net/rxrpc/misc.c b/net/rxrpc/misc.c index d4144fd86f84..f5f03f27bd6c 100644 --- a/net/rxrpc/misc.c +++ b/net/rxrpc/misc.c @@ -16,12 +16,6 @@ */ unsigned int rxrpc_max_backlog __read_mostly = 10; -/* - * How long to wait before scheduling ACK generation after seeing a - * packet with RXRPC_REQUEST_ACK set (in jiffies). - */ -unsigned long rxrpc_requested_ack_delay = 1; - /* * How long to wait before scheduling an ACK with subtype DELAY (in jiffies). * @@ -62,15 +56,3 @@ unsigned int rxrpc_rx_mtu = 5692; * sender that we're willing to handle. */ unsigned int rxrpc_rx_jumbo_max = 4; - -const s8 rxrpc_ack_priority[] = { - [0] = 0, - [RXRPC_ACK_DELAY] = 1, - [RXRPC_ACK_REQUESTED] = 2, - [RXRPC_ACK_IDLE] = 3, - [RXRPC_ACK_DUPLICATE] = 4, - [RXRPC_ACK_OUT_OF_SEQUENCE] = 5, - [RXRPC_ACK_EXCEEDS_WINDOW] = 6, - [RXRPC_ACK_NOSPACE] = 7, - [RXRPC_ACK_PING_RESPONSE] = 8, -}; diff --git a/net/rxrpc/output.c b/net/rxrpc/output.c index 1edbc678e8be..d35657b659ad 100644 --- a/net/rxrpc/output.c +++ b/net/rxrpc/output.c @@ -115,6 +115,8 @@ static size_t rxrpc_fill_out_ack(struct rxrpc_connection *conn, *ackp++ = RXRPC_ACK_TYPE_NACK; seq++; } while (before_eq(seq, top)); + } else if (txb->ack.reason == RXRPC_ACK_DELAY) { + txb->ack.reason = RXRPC_ACK_IDLE; } mtu = conn->params.peer->if_mtu; @@ -204,6 +206,11 @@ static int rxrpc_send_ack_packet(struct rxrpc_local *local, struct rxrpc_txbuf * if (txb->ack.reason == RXRPC_ACK_PING) txb->wire.flags |= RXRPC_REQUEST_ACK; + if (txb->ack.reason == RXRPC_ACK_DELAY) + clear_bit(RXRPC_CALL_DELAY_ACK_PENDING, &call->flags); + if (txb->ack.reason == RXRPC_ACK_IDLE) + clear_bit(RXRPC_CALL_IDLE_ACK_PENDING, &call->flags); + spin_lock_bh(&call->lock); n = rxrpc_fill_out_ack(conn, call, txb, &hard_ack, &top); spin_unlock_bh(&call->lock); diff --git a/net/rxrpc/protocol.h b/net/rxrpc/protocol.h index d2cf8e1d218f..f3d4e2700901 100644 --- a/net/rxrpc/protocol.h +++ b/net/rxrpc/protocol.h @@ -132,13 +132,6 @@ struct rxrpc_ackpacket { } __packed; -/* Some ACKs refer to specific packets and some are general and can be updated. */ -#define RXRPC_ACK_UPDATEABLE ((1 << RXRPC_ACK_REQUESTED) | \ - (1 << RXRPC_ACK_PING_RESPONSE) | \ - (1 << RXRPC_ACK_DELAY) | \ - (1 << RXRPC_ACK_IDLE)) - - /* * ACK packets can have a further piece of information tagged on the end */ diff --git a/net/rxrpc/recvmsg.c b/net/rxrpc/recvmsg.c index 104dd4a29f05..46e784edc0f4 100644 --- a/net/rxrpc/recvmsg.c +++ b/net/rxrpc/recvmsg.c @@ -188,11 +188,8 @@ static void rxrpc_end_rx_phase(struct rxrpc_call *call, rxrpc_serial_t serial) trace_rxrpc_receive(call, rxrpc_receive_end, 0, call->rx_top); ASSERTCMP(call->rx_hard_ack, ==, call->rx_top); - if (call->state == RXRPC_CALL_CLIENT_RECV_REPLY) { - rxrpc_propose_ACK(call, RXRPC_ACK_IDLE, serial, - rxrpc_propose_ack_terminal_ack); - //rxrpc_send_ack_packet(call, false, NULL); - } + if (call->state == RXRPC_CALL_CLIENT_RECV_REPLY) + rxrpc_propose_delay_ACK(call, serial, rxrpc_propose_ack_terminal_ack); write_lock_bh(&call->state_lock); @@ -206,8 +203,8 @@ static void rxrpc_end_rx_phase(struct rxrpc_call *call, rxrpc_serial_t serial) call->state = RXRPC_CALL_SERVER_ACK_REQUEST; call->expect_req_by = jiffies + MAX_JIFFY_OFFSET; write_unlock_bh(&call->state_lock); - rxrpc_propose_ACK(call, RXRPC_ACK_DELAY, serial, - rxrpc_propose_ack_processing_op); + rxrpc_propose_delay_ACK(call, serial, + rxrpc_propose_ack_processing_op); break; default: write_unlock_bh(&call->state_lock); @@ -259,7 +256,8 @@ static void rxrpc_rotate_rx_window(struct rxrpc_call *call) rxrpc_end_rx_phase(call, serial); } else { /* Check to see if there's an ACK that needs sending. */ - if (atomic_inc_return(&call->ackr_nr_consumed) > 2) { + if (atomic_inc_return(&call->ackr_nr_consumed) > 2 && + !test_and_set_bit(RXRPC_CALL_IDLE_ACK_PENDING, &call->flags)) { rxrpc_send_ACK(call, RXRPC_ACK_IDLE, serial, rxrpc_propose_ack_rotate_rx); rxrpc_transmit_ack_packets(call->peer->local); diff --git a/net/rxrpc/sendmsg.c b/net/rxrpc/sendmsg.c index ef4949259020..e32805a49324 100644 --- a/net/rxrpc/sendmsg.c +++ b/net/rxrpc/sendmsg.c @@ -234,7 +234,7 @@ static int rxrpc_queue_packet(struct rxrpc_sock *rx, struct rxrpc_call *call, case RXRPC_CALL_SERVER_ACK_REQUEST: call->state = RXRPC_CALL_SERVER_SEND_REPLY; now = jiffies; - WRITE_ONCE(call->ack_at, now + MAX_JIFFY_OFFSET); + WRITE_ONCE(call->delay_ack_at, now + MAX_JIFFY_OFFSET); if (call->ackr_reason == RXRPC_ACK_DELAY) call->ackr_reason = 0; trace_rxrpc_timer(call, rxrpc_timer_init_for_send_reply, now); diff --git a/net/rxrpc/sysctl.c b/net/rxrpc/sysctl.c index 555e0910786b..2bd987364e44 100644 --- a/net/rxrpc/sysctl.c +++ b/net/rxrpc/sysctl.c @@ -26,15 +26,6 @@ static const unsigned long max_jiffies = MAX_JIFFY_OFFSET; */ static struct ctl_table rxrpc_sysctl_table[] = { /* Values measured in milliseconds but used in jiffies */ - { - .procname = "req_ack_delay", - .data = &rxrpc_requested_ack_delay, - .maxlen = sizeof(unsigned long), - .mode = 0644, - .proc_handler = proc_doulongvec_ms_jiffies_minmax, - .extra1 = (void *)&one_jiffy, - .extra2 = (void *)&max_jiffies, - }, { .procname = "soft_ack_delay", .data = &rxrpc_soft_ack_delay, From faf92e8d53f5f03842da25af971a3f0ef88ffba2 Mon Sep 17 00:00:00 2001 From: David Howells Date: Fri, 7 Oct 2022 17:22:40 +0100 Subject: [PATCH 0733/2002] rxrpc: Split the rxrpc_recvmsg tracepoint Split the rxrpc_recvmsg tracepoint so that the tracepoints that are about data packet processing (and which have extra pieces of information) are separate from the tracepoint that shows the general flow of recvmsg(). Signed-off-by: David Howells cc: Marc Dionne cc: linux-afs@lists.infradead.org --- include/trace/events/rxrpc.h | 24 +++++++++++++++++++++++ net/rxrpc/recvmsg.c | 37 ++++++++++++++++++------------------ 2 files changed, 42 insertions(+), 19 deletions(-) diff --git a/include/trace/events/rxrpc.h b/include/trace/events/rxrpc.h index d32e9858c682..84464b29e54a 100644 --- a/include/trace/events/rxrpc.h +++ b/include/trace/events/rxrpc.h @@ -885,6 +885,30 @@ TRACE_EVENT(rxrpc_receive, ); TRACE_EVENT(rxrpc_recvmsg, + TP_PROTO(struct rxrpc_call *call, enum rxrpc_recvmsg_trace why, + int ret), + + TP_ARGS(call, why, ret), + + TP_STRUCT__entry( + __field(unsigned int, call ) + __field(enum rxrpc_recvmsg_trace, why ) + __field(int, ret ) + ), + + TP_fast_assign( + __entry->call = call ? call->debug_id : 0; + __entry->why = why; + __entry->ret = ret; + ), + + TP_printk("c=%08x %s ret=%d", + __entry->call, + __print_symbolic(__entry->why, rxrpc_recvmsg_traces), + __entry->ret) + ); + +TRACE_EVENT(rxrpc_recvdata, TP_PROTO(struct rxrpc_call *call, enum rxrpc_recvmsg_trace why, rxrpc_seq_t seq, unsigned int offset, unsigned int len, int ret), diff --git a/net/rxrpc/recvmsg.c b/net/rxrpc/recvmsg.c index 46e784edc0f4..77cde6311559 100644 --- a/net/rxrpc/recvmsg.c +++ b/net/rxrpc/recvmsg.c @@ -173,8 +173,8 @@ static int rxrpc_recvmsg_term(struct rxrpc_call *call, struct msghdr *msg) break; } - trace_rxrpc_recvmsg(call, rxrpc_recvmsg_terminal, call->rx_hard_ack, - call->rx_pkt_offset, call->rx_pkt_len, ret); + trace_rxrpc_recvdata(call, rxrpc_recvmsg_terminal, call->rx_hard_ack, + call->rx_pkt_offset, call->rx_pkt_len, ret); return ret; } @@ -380,8 +380,8 @@ static int rxrpc_recvmsg_data(struct socket *sock, struct rxrpc_call *call, ix = seq & RXRPC_RXTX_BUFF_MASK; skb = call->rxtx_buffer[ix]; if (!skb) { - trace_rxrpc_recvmsg(call, rxrpc_recvmsg_hole, seq, - rx_pkt_offset, rx_pkt_len, 0); + trace_rxrpc_recvdata(call, rxrpc_recvmsg_hole, seq, + rx_pkt_offset, rx_pkt_len, 0); rxrpc_transmit_ack_packets(call->peer->local); break; } @@ -404,15 +404,15 @@ static int rxrpc_recvmsg_data(struct socket *sock, struct rxrpc_call *call, &call->rxtx_annotations[ix], &rx_pkt_offset, &rx_pkt_len, &rx_pkt_last); - trace_rxrpc_recvmsg(call, rxrpc_recvmsg_next, seq, - rx_pkt_offset, rx_pkt_len, ret2); + trace_rxrpc_recvdata(call, rxrpc_recvmsg_next, seq, + rx_pkt_offset, rx_pkt_len, ret2); if (ret2 < 0) { ret = ret2; goto out; } } else { - trace_rxrpc_recvmsg(call, rxrpc_recvmsg_cont, seq, - rx_pkt_offset, rx_pkt_len, 0); + trace_rxrpc_recvdata(call, rxrpc_recvmsg_cont, seq, + rx_pkt_offset, rx_pkt_len, 0); } /* We have to handle short, empty and used-up DATA packets. */ @@ -435,8 +435,8 @@ static int rxrpc_recvmsg_data(struct socket *sock, struct rxrpc_call *call, } if (rx_pkt_len > 0) { - trace_rxrpc_recvmsg(call, rxrpc_recvmsg_full, seq, - rx_pkt_offset, rx_pkt_len, 0); + trace_rxrpc_recvdata(call, rxrpc_recvmsg_full, seq, + rx_pkt_offset, rx_pkt_len, 0); ASSERTCMP(*_offset, ==, len); ret = 0; break; @@ -464,8 +464,8 @@ out: call->rx_pkt_last = rx_pkt_last; } done: - trace_rxrpc_recvmsg(call, rxrpc_recvmsg_data_return, seq, - rx_pkt_offset, rx_pkt_len, ret); + trace_rxrpc_recvdata(call, rxrpc_recvmsg_data_return, seq, + rx_pkt_offset, rx_pkt_len, ret); if (ret == -EAGAIN) set_bit(RXRPC_CALL_RX_UNDERRUN, &call->flags); return ret; @@ -488,7 +488,7 @@ int rxrpc_recvmsg(struct socket *sock, struct msghdr *msg, size_t len, DEFINE_WAIT(wait); - trace_rxrpc_recvmsg(NULL, rxrpc_recvmsg_enter, 0, 0, 0, 0); + trace_rxrpc_recvmsg(NULL, rxrpc_recvmsg_enter, 0); if (flags & (MSG_OOB | MSG_TRUNC)) return -EOPNOTSUPP; @@ -525,8 +525,7 @@ try_again: if (list_empty(&rx->recvmsg_q)) { if (signal_pending(current)) goto wait_interrupted; - trace_rxrpc_recvmsg(NULL, rxrpc_recvmsg_wait, - 0, 0, 0, 0); + trace_rxrpc_recvmsg(NULL, rxrpc_recvmsg_wait, 0); timeo = schedule_timeout(timeo); } finish_wait(sk_sleep(&rx->sk), &wait); @@ -545,7 +544,7 @@ try_again: rxrpc_get_call(call, rxrpc_call_got); write_unlock_bh(&rx->recvmsg_lock); - trace_rxrpc_recvmsg(call, rxrpc_recvmsg_dequeue, 0, 0, 0, 0); + trace_rxrpc_recvmsg(call, rxrpc_recvmsg_dequeue, 0); /* We're going to drop the socket lock, so we need to lock the call * against interference by sendmsg. @@ -630,7 +629,7 @@ try_again: error_unlock_call: mutex_unlock(&call->user_mutex); rxrpc_put_call(call, rxrpc_call_put); - trace_rxrpc_recvmsg(call, rxrpc_recvmsg_return, 0, 0, 0, ret); + trace_rxrpc_recvmsg(call, rxrpc_recvmsg_return, ret); return ret; error_requeue_call: @@ -638,14 +637,14 @@ error_requeue_call: write_lock_bh(&rx->recvmsg_lock); list_add(&call->recvmsg_link, &rx->recvmsg_q); write_unlock_bh(&rx->recvmsg_lock); - trace_rxrpc_recvmsg(call, rxrpc_recvmsg_requeue, 0, 0, 0, 0); + trace_rxrpc_recvmsg(call, rxrpc_recvmsg_requeue, 0); } else { rxrpc_put_call(call, rxrpc_call_put); } error_no_call: release_sock(&rx->sk); error_trace: - trace_rxrpc_recvmsg(call, rxrpc_recvmsg_return, 0, 0, 0, ret); + trace_rxrpc_recvmsg(call, rxrpc_recvmsg_return, ret); return ret; wait_interrupted: From d4d02d8bb5c412d977af7ea7c7ea91977a6a64dc Mon Sep 17 00:00:00 2001 From: David Howells Date: Fri, 7 Oct 2022 17:44:39 +0100 Subject: [PATCH 0734/2002] rxrpc: Clone received jumbo subpackets and queue separately Split up received jumbo packets into separate skbuffs by cloning the original skbuff for each subpacket and setting the offset and length of the data in that subpacket in the skbuff's private data. The subpackets are then placed on the recvmsg queue separately. The security class then gets to revise the offset and length to remove its metadata. If we fail to clone a packet, we just drop it and let the peer resend it. The original packet gets used for the final subpacket. This should make it easier to handle parallel decryption of the subpackets. It also simplifies the handling of lost or misordered packets in the queuing/buffering loop as the possibility of overlapping jumbo packets no longer needs to be considered. Signed-off-by: David Howells cc: Marc Dionne cc: linux-afs@lists.infradead.org --- include/trace/events/rxrpc.h | 12 +- net/rxrpc/ar-internal.h | 30 +-- net/rxrpc/input.c | 409 ++++++++++++++++------------------- net/rxrpc/insecure.c | 13 +- net/rxrpc/output.c | 2 +- net/rxrpc/protocol.h | 2 +- net/rxrpc/recvmsg.c | 104 ++------- net/rxrpc/rxkad.c | 96 +++----- 8 files changed, 248 insertions(+), 420 deletions(-) diff --git a/include/trace/events/rxrpc.h b/include/trace/events/rxrpc.h index 84464b29e54a..03a984e661bc 100644 --- a/include/trace/events/rxrpc.h +++ b/include/trace/events/rxrpc.h @@ -18,6 +18,7 @@ */ #define rxrpc_skb_traces \ EM(rxrpc_skb_cleaned, "CLN") \ + EM(rxrpc_skb_cloned_jumbo, "CLJ") \ EM(rxrpc_skb_freed, "FRE") \ EM(rxrpc_skb_got, "GOT") \ EM(rxrpc_skb_lost, "*L*") \ @@ -630,16 +631,15 @@ TRACE_EVENT(rxrpc_transmit, TRACE_EVENT(rxrpc_rx_data, TP_PROTO(unsigned int call, rxrpc_seq_t seq, - rxrpc_serial_t serial, u8 flags, u8 anno), + rxrpc_serial_t serial, u8 flags), - TP_ARGS(call, seq, serial, flags, anno), + TP_ARGS(call, seq, serial, flags), TP_STRUCT__entry( __field(unsigned int, call ) __field(rxrpc_seq_t, seq ) __field(rxrpc_serial_t, serial ) __field(u8, flags ) - __field(u8, anno ) ), TP_fast_assign( @@ -647,15 +647,13 @@ TRACE_EVENT(rxrpc_rx_data, __entry->seq = seq; __entry->serial = serial; __entry->flags = flags; - __entry->anno = anno; ), - TP_printk("c=%08x DATA %08x q=%08x fl=%02x a=%02x", + TP_printk("c=%08x DATA %08x q=%08x fl=%02x", __entry->call, __entry->serial, __entry->seq, - __entry->flags, - __entry->anno) + __entry->flags) ); TRACE_EVENT(rxrpc_rx_ack, diff --git a/net/rxrpc/ar-internal.h b/net/rxrpc/ar-internal.h index 5a81545a58d5..e93ed18816b1 100644 --- a/net/rxrpc/ar-internal.h +++ b/net/rxrpc/ar-internal.h @@ -195,19 +195,14 @@ struct rxrpc_host_header { * - max 48 bytes (struct sk_buff::cb) */ struct rxrpc_skb_priv { - atomic_t nr_ring_pins; /* Number of rxtx ring pins */ - u8 nr_subpackets; /* Number of subpackets */ - u8 rx_flags; /* Received packet flags */ -#define RXRPC_SKB_INCL_LAST 0x01 /* - Includes last packet */ - union { - int remain; /* amount of space remaining for next write */ + u16 remain; + u16 offset; /* Offset of data */ + u16 len; /* Length of data */ + u8 rx_flags; /* Received packet flags */ + u8 flags; +#define RXRPC_RX_VERIFIED 0x01 - /* List of requested ACKs on subpackets */ - unsigned long rx_req_ack[(RXRPC_MAX_NR_JUMBO + BITS_PER_LONG - 1) / - BITS_PER_LONG]; - }; - - struct rxrpc_host_header hdr; /* RxRPC packet header from this packet */ + struct rxrpc_host_header hdr; /* RxRPC packet header from this packet */ }; #define rxrpc_skb(__skb) ((struct rxrpc_skb_priv *) &(__skb)->cb) @@ -252,16 +247,11 @@ struct rxrpc_security { int (*secure_packet)(struct rxrpc_call *, struct sk_buff *, size_t); /* verify the security on a received packet */ - int (*verify_packet)(struct rxrpc_call *, struct sk_buff *, - unsigned int, unsigned int, rxrpc_seq_t, u16); + int (*verify_packet)(struct rxrpc_call *, struct sk_buff *); /* Free crypto request on a call */ void (*free_call_crypto)(struct rxrpc_call *); - /* Locate the data in a received packet that has been verified. */ - void (*locate_data)(struct rxrpc_call *, struct sk_buff *, - unsigned int *, unsigned int *); - /* issue a challenge */ int (*issue_challenge)(struct rxrpc_connection *); @@ -628,7 +618,6 @@ struct rxrpc_call { int debug_id; /* debug ID for printks */ unsigned short rx_pkt_offset; /* Current recvmsg packet offset */ unsigned short rx_pkt_len; /* Current recvmsg packet len */ - bool rx_pkt_last; /* Current recvmsg packet is last */ /* Rx/Tx circular buffer, depending on phase. * @@ -652,8 +641,6 @@ struct rxrpc_call { #define RXRPC_TX_ANNO_LAST 0x04 #define RXRPC_TX_ANNO_RESENT 0x08 -#define RXRPC_RX_ANNO_SUBPACKET 0x3f /* Subpacket number in jumbogram */ -#define RXRPC_RX_ANNO_VERIFIED 0x80 /* Set if verified and decrypted */ rxrpc_seq_t tx_hard_ack; /* Dead slot in buffer; the first transmitted but * not hard-ACK'd packet follows this. */ @@ -681,7 +668,6 @@ struct rxrpc_call { rxrpc_serial_t rx_serial; /* Highest serial received for this call */ u8 rx_winsize; /* Size of Rx window */ u8 tx_winsize; /* Maximum size of Tx window */ - u8 nr_jumbo_bad; /* Number of jumbo dups/exceeds-windows */ spinlock_t input_lock; /* Lock for packet input to this call */ diff --git a/net/rxrpc/input.c b/net/rxrpc/input.c index 4df1bdc4de6c..0e7545ed0128 100644 --- a/net/rxrpc/input.c +++ b/net/rxrpc/input.c @@ -313,80 +313,180 @@ static bool rxrpc_receiving_reply(struct rxrpc_call *call) } /* - * Scan a data packet to validate its structure and to work out how many - * subpackets it contains. - * - * A jumbo packet is a collection of consecutive packets glued together with - * little headers between that indicate how to change the initial header for - * each subpacket. - * - * RXRPC_JUMBO_PACKET must be set on all but the last subpacket - and all but - * the last are RXRPC_JUMBO_DATALEN in size. The last subpacket may be of any - * size. + * Process a DATA packet, adding the packet to the Rx ring. The caller's + * packet ref must be passed on or discarded. */ -static bool rxrpc_validate_data(struct sk_buff *skb) +static void rxrpc_input_data_one(struct rxrpc_call *call, struct sk_buff *skb) { struct rxrpc_skb_priv *sp = rxrpc_skb(skb); - unsigned int offset = sizeof(struct rxrpc_wire_header); - unsigned int len = skb->len; - u8 flags = sp->hdr.flags; + rxrpc_serial_t serial = sp->hdr.serial; + rxrpc_seq_t seq = sp->hdr.seq, hard_ack; + unsigned int ix = seq & RXRPC_RXTX_BUFF_MASK; + bool last = sp->hdr.flags & RXRPC_LAST_PACKET; + bool acked = false; - for (;;) { - if (flags & RXRPC_REQUEST_ACK) - __set_bit(sp->nr_subpackets, sp->rx_req_ack); - sp->nr_subpackets++; + rxrpc_inc_stat(call->rxnet, stat_rx_data); + if (sp->hdr.flags & RXRPC_REQUEST_ACK) + rxrpc_inc_stat(call->rxnet, stat_rx_data_reqack); + if (sp->hdr.flags & RXRPC_JUMBO_PACKET) + rxrpc_inc_stat(call->rxnet, stat_rx_data_jumbo); - if (!(flags & RXRPC_JUMBO_PACKET)) - break; + hard_ack = READ_ONCE(call->rx_hard_ack); - if (len - offset < RXRPC_JUMBO_SUBPKTLEN) - goto protocol_error; - if (flags & RXRPC_LAST_PACKET) - goto protocol_error; - offset += RXRPC_JUMBO_DATALEN; - if (skb_copy_bits(skb, offset, &flags, 1) < 0) - goto protocol_error; - offset += sizeof(struct rxrpc_jumbo_header); + _proto("Rx DATA %%%u { #%x l=%u }", serial, seq, last); + + if (last) { + if (test_bit(RXRPC_CALL_RX_LAST, &call->flags) && + seq != call->rx_top) { + rxrpc_proto_abort("LSN", call, seq); + goto out; + } + } else { + if (test_bit(RXRPC_CALL_RX_LAST, &call->flags) && + after_eq(seq, call->rx_top)) { + rxrpc_proto_abort("LSA", call, seq); + goto out; + } } - if (flags & RXRPC_LAST_PACKET) - sp->rx_flags |= RXRPC_SKB_INCL_LAST; + trace_rxrpc_rx_data(call->debug_id, seq, serial, sp->hdr.flags); + + if (before_eq(seq, hard_ack)) { + rxrpc_send_ACK(call, RXRPC_ACK_DUPLICATE, serial, + rxrpc_propose_ack_input_data); + goto out; + } + + if (call->rxtx_buffer[ix]) { + rxrpc_send_ACK(call, RXRPC_ACK_DUPLICATE, serial, + rxrpc_propose_ack_input_data); + goto out; + } + + if (after(seq, hard_ack + call->rx_winsize)) { + rxrpc_send_ACK(call, RXRPC_ACK_EXCEEDS_WINDOW, serial, + rxrpc_propose_ack_input_data); + goto out; + } + + if (sp->hdr.flags & RXRPC_REQUEST_ACK) { + rxrpc_send_ACK(call, RXRPC_ACK_REQUESTED, serial, + rxrpc_propose_ack_input_data); + acked = true; + } + + if (after(seq, call->ackr_highest_seq)) + call->ackr_highest_seq = seq; + + /* Queue the packet. We use a couple of memory barriers here as need + * to make sure that rx_top is perceived to be set after the buffer + * pointer and that the buffer pointer is set after the annotation and + * the skb data. + * + * Barriers against rxrpc_recvmsg_data() and rxrpc_rotate_rx_window() + * and also rxrpc_fill_out_ack(). + */ + call->rxtx_annotations[ix] = 1; + smp_wmb(); + call->rxtx_buffer[ix] = skb; + if (after(seq, call->rx_top)) { + smp_store_release(&call->rx_top, seq); + } else if (before(seq, call->rx_top)) { + /* Send an immediate ACK if we fill in a hole */ + if (!acked) { + rxrpc_send_ACK(call, RXRPC_ACK_DELAY, serial, + rxrpc_propose_ack_input_data_hole); + acked = true; + } + } + + /* From this point on, we're not allowed to touch the packet any longer + * as its ref now belongs to the Rx ring. + */ + skb = NULL; + sp = NULL; + + if (last) { + set_bit(RXRPC_CALL_RX_LAST, &call->flags); + trace_rxrpc_receive(call, rxrpc_receive_queue_last, serial, seq); + } else { + trace_rxrpc_receive(call, rxrpc_receive_queue, serial, seq); + } + + if (after_eq(seq, call->rx_expect_next)) { + if (after(seq, call->rx_expect_next)) { + _net("OOS %u > %u", seq, call->rx_expect_next); + rxrpc_send_ACK(call, RXRPC_ACK_OUT_OF_SEQUENCE, serial, + rxrpc_propose_ack_input_data); + acked = true; + } + call->rx_expect_next = seq + 1; + } + +out: + if (!acked && + atomic_inc_return(&call->ackr_nr_unacked) > 2) + rxrpc_send_ACK(call, RXRPC_ACK_IDLE, serial, + rxrpc_propose_ack_input_data); + else + rxrpc_propose_delay_ACK(call, serial, + rxrpc_propose_ack_input_data); + + trace_rxrpc_notify_socket(call->debug_id, serial); + rxrpc_notify_socket(call); + + rxrpc_free_skb(skb, rxrpc_skb_freed); + _leave(" [queued]"); +} + +/* + * Split a jumbo packet and file the bits separately. + */ +static bool rxrpc_input_split_jumbo(struct rxrpc_call *call, struct sk_buff *skb) +{ + struct rxrpc_jumbo_header jhdr; + struct rxrpc_skb_priv *sp = rxrpc_skb(skb), *jsp; + struct sk_buff *jskb; + unsigned int offset = sizeof(struct rxrpc_wire_header); + unsigned int len = skb->len - offset; + + while (sp->hdr.flags & RXRPC_JUMBO_PACKET) { + if (len < RXRPC_JUMBO_SUBPKTLEN) + goto protocol_error; + if (sp->hdr.flags & RXRPC_LAST_PACKET) + goto protocol_error; + if (skb_copy_bits(skb, offset + RXRPC_JUMBO_DATALEN, + &jhdr, sizeof(jhdr)) < 0) + goto protocol_error; + + jskb = skb_clone(skb, GFP_ATOMIC); + if (!jskb) { + kdebug("couldn't clone"); + return false; + } + rxrpc_new_skb(jskb, rxrpc_skb_cloned_jumbo); + jsp = rxrpc_skb(jskb); + jsp->offset = offset; + jsp->len = RXRPC_JUMBO_DATALEN; + rxrpc_input_data_one(call, jskb); + + sp->hdr.flags = jhdr.flags; + sp->hdr._rsvd = ntohs(jhdr._rsvd); + sp->hdr.seq++; + sp->hdr.serial++; + offset += RXRPC_JUMBO_SUBPKTLEN; + len -= RXRPC_JUMBO_SUBPKTLEN; + } + + sp->offset = offset; + sp->len = len; + rxrpc_input_data_one(call, skb); return true; protocol_error: return false; } -/* - * Handle reception of a duplicate packet. - * - * We have to take care to avoid an attack here whereby we're given a series of - * jumbograms, each with a sequence number one before the preceding one and - * filled up to maximum UDP size. If they never send us the first packet in - * the sequence, they can cause us to have to hold on to around 2MiB of kernel - * space until the call times out. - * - * We limit the space usage by only accepting three duplicate jumbo packets per - * call. After that, we tell the other side we're no longer accepting jumbos - * (that information is encoded in the ACK packet). - */ -static void rxrpc_input_dup_data(struct rxrpc_call *call, rxrpc_seq_t seq, - bool is_jumbo, bool *_jumbo_bad) -{ - /* Discard normal packets that are duplicates. */ - if (is_jumbo) - return; - - /* Skip jumbo subpackets that are duplicates. When we've had three or - * more partially duplicate jumbo packets, we refuse to take any more - * jumbos for this call. - */ - if (!*_jumbo_bad) { - call->nr_jumbo_bad++; - *_jumbo_bad = true; - } -} - /* * Process a DATA packet, adding the packet to the Rx ring. The caller's * packet ref must be passed on or discarded. @@ -395,16 +495,14 @@ static void rxrpc_input_data(struct rxrpc_call *call, struct sk_buff *skb) { struct rxrpc_skb_priv *sp = rxrpc_skb(skb); enum rxrpc_call_state state; - unsigned int j, nr_subpackets, nr_unacked = 0; - rxrpc_serial_t serial = sp->hdr.serial, ack_serial = serial; - rxrpc_seq_t seq0 = sp->hdr.seq, hard_ack; - bool jumbo_bad = false; + rxrpc_serial_t serial = sp->hdr.serial; + rxrpc_seq_t seq0 = sp->hdr.seq; _enter("{%u,%u},{%u,%u}", call->rx_hard_ack, call->rx_top, skb->len, seq0); - _proto("Rx DATA %%%u { #%u f=%02x n=%u }", - sp->hdr.serial, seq0, sp->hdr.flags, sp->nr_subpackets); + _proto("Rx DATA %%%u { #%u f=%02x }", + sp->hdr.serial, seq0, sp->hdr.flags); state = READ_ONCE(call->state); if (state >= RXRPC_CALL_COMPLETE) { @@ -412,6 +510,24 @@ static void rxrpc_input_data(struct rxrpc_call *call, struct sk_buff *skb) return; } + /* Unshare the packet so that it can be modified for in-place + * decryption. + */ + if (sp->hdr.securityIndex != 0) { + struct sk_buff *nskb = skb_unshare(skb, GFP_ATOMIC); + if (!nskb) { + rxrpc_eaten_skb(skb, rxrpc_skb_unshared_nomem); + return; + } + + if (nskb != skb) { + rxrpc_eaten_skb(skb, rxrpc_skb_received); + skb = nskb; + rxrpc_new_skb(skb, rxrpc_skb_unshared); + sp = rxrpc_skb(skb); + } + } + if (state == RXRPC_CALL_SERVER_RECV_REQUEST) { unsigned long timo = READ_ONCE(call->next_req_timo); unsigned long now, expect_req_by; @@ -425,12 +541,6 @@ static void rxrpc_input_data(struct rxrpc_call *call, struct sk_buff *skb) } } - rxrpc_inc_stat(call->rxnet, stat_rx_data); - if (sp->hdr.flags & RXRPC_REQUEST_ACK) - rxrpc_inc_stat(call->rxnet, stat_rx_data_reqack); - if (sp->hdr.flags & RXRPC_JUMBO_PACKET) - rxrpc_inc_stat(call->rxnet, stat_rx_data_jumbo); - spin_lock(&call->input_lock); /* Received data implicitly ACKs all of the request packets we sent @@ -439,154 +549,15 @@ static void rxrpc_input_data(struct rxrpc_call *call, struct sk_buff *skb) if ((state == RXRPC_CALL_CLIENT_SEND_REQUEST || state == RXRPC_CALL_CLIENT_AWAIT_REPLY) && !rxrpc_receiving_reply(call)) - goto unlock; + goto out; - hard_ack = READ_ONCE(call->rx_hard_ack); - - nr_subpackets = sp->nr_subpackets; - if (nr_subpackets > 1) { - if (call->nr_jumbo_bad > 3) { - rxrpc_send_ACK(call, RXRPC_ACK_NOSPACE, serial, - rxrpc_propose_ack_input_data); - goto unlock; - } + if (!rxrpc_input_split_jumbo(call, skb)) { + rxrpc_proto_abort("VLD", call, sp->hdr.seq); + goto out; } + skb = NULL; - for (j = 0; j < nr_subpackets; j++) { - rxrpc_serial_t serial = sp->hdr.serial + j; - rxrpc_seq_t seq = seq0 + j; - unsigned int ix = seq & RXRPC_RXTX_BUFF_MASK; - bool terminal = (j == nr_subpackets - 1); - bool last = terminal && (sp->rx_flags & RXRPC_SKB_INCL_LAST); - bool acked = false; - u8 flags, annotation = j; - - _proto("Rx DATA+%u %%%u { #%x t=%u l=%u }", - j, serial, seq, terminal, last); - - if (last) { - if (test_bit(RXRPC_CALL_RX_LAST, &call->flags) && - seq != call->rx_top) { - rxrpc_proto_abort("LSN", call, seq); - goto unlock; - } - } else { - if (test_bit(RXRPC_CALL_RX_LAST, &call->flags) && - after_eq(seq, call->rx_top)) { - rxrpc_proto_abort("LSA", call, seq); - goto unlock; - } - } - - flags = 0; - if (last) - flags |= RXRPC_LAST_PACKET; - if (!terminal) - flags |= RXRPC_JUMBO_PACKET; - if (test_bit(j, sp->rx_req_ack)) - flags |= RXRPC_REQUEST_ACK; - trace_rxrpc_rx_data(call->debug_id, seq, serial, flags, annotation); - - if (before_eq(seq, hard_ack)) { - rxrpc_send_ACK(call, RXRPC_ACK_DUPLICATE, serial, - rxrpc_propose_ack_input_data); - continue; - } - - if (call->rxtx_buffer[ix]) { - rxrpc_input_dup_data(call, seq, nr_subpackets > 1, - &jumbo_bad); - rxrpc_send_ACK(call, RXRPC_ACK_DUPLICATE, serial, - rxrpc_propose_ack_input_data); - continue; - } - - if (after(seq, hard_ack + call->rx_winsize)) { - rxrpc_send_ACK(call, RXRPC_ACK_EXCEEDS_WINDOW, serial, - rxrpc_propose_ack_input_data); - if (flags & RXRPC_JUMBO_PACKET) { - if (!jumbo_bad) { - call->nr_jumbo_bad++; - jumbo_bad = true; - } - } - - goto unlock; - } - - if (flags & RXRPC_REQUEST_ACK) { - rxrpc_send_ACK(call, RXRPC_ACK_REQUESTED, serial, - rxrpc_propose_ack_input_data); - acked = true; - } - - if (after(seq0, call->ackr_highest_seq)) - call->ackr_highest_seq = seq0; - - /* Queue the packet. We use a couple of memory barriers here as need - * to make sure that rx_top is perceived to be set after the buffer - * pointer and that the buffer pointer is set after the annotation and - * the skb data. - * - * Barriers against rxrpc_recvmsg_data() and rxrpc_rotate_rx_window() - * and also rxrpc_fill_out_ack(). - */ - if (!terminal) - rxrpc_get_skb(skb, rxrpc_skb_got); - call->rxtx_annotations[ix] = annotation; - smp_wmb(); - call->rxtx_buffer[ix] = skb; - if (after(seq, call->rx_top)) { - smp_store_release(&call->rx_top, seq); - } else if (before(seq, call->rx_top)) { - /* Send an immediate ACK if we fill in a hole */ - if (!acked) { - rxrpc_send_ACK(call, RXRPC_ACK_DELAY, serial, - rxrpc_propose_ack_input_data_hole); - acked = true; - } - } - - if (terminal) { - /* From this point on, we're not allowed to touch the - * packet any longer as its ref now belongs to the Rx - * ring. - */ - skb = NULL; - sp = NULL; - } - - if (last) { - set_bit(RXRPC_CALL_RX_LAST, &call->flags); - trace_rxrpc_receive(call, rxrpc_receive_queue_last, serial, seq); - } else { - trace_rxrpc_receive(call, rxrpc_receive_queue, serial, seq); - } - - if (after_eq(seq, call->rx_expect_next)) { - if (after(seq, call->rx_expect_next)) { - _net("OOS %u > %u", seq, call->rx_expect_next); - rxrpc_send_ACK(call, RXRPC_ACK_OUT_OF_SEQUENCE, serial, - rxrpc_propose_ack_input_data); - acked = true; - } - call->rx_expect_next = seq + 1; - } - - if (!acked) { - nr_unacked++; - ack_serial = serial; - } - } - -unlock: - if (atomic_add_return(nr_unacked, &call->ackr_nr_unacked) > 2) - rxrpc_send_ACK(call, RXRPC_ACK_IDLE, ack_serial, - rxrpc_propose_ack_input_data); - else - rxrpc_propose_delay_ACK(call, ack_serial, - rxrpc_propose_ack_input_data); - +out: trace_rxrpc_notify_socket(call->debug_id, serial); rxrpc_notify_socket(call); @@ -1288,8 +1259,6 @@ int rxrpc_input_packet(struct sock *udp_sk, struct sk_buff *skb) if (sp->hdr.callNumber == 0 || sp->hdr.seq == 0) goto bad_message; - if (!rxrpc_validate_data(skb)) - goto bad_message; /* Unshare the packet so that it can be modified for in-place * decryption. @@ -1403,7 +1372,7 @@ int rxrpc_input_packet(struct sock *udp_sk, struct sk_buff *skb) trace_rxrpc_rx_data(chan->call_debug_id, sp->hdr.seq, sp->hdr.serial, - sp->hdr.flags, 0); + sp->hdr.flags); rxrpc_post_packet_to_conn(conn, skb); goto out; } diff --git a/net/rxrpc/insecure.c b/net/rxrpc/insecure.c index 9aae99d67833..fd68f0e3af27 100644 --- a/net/rxrpc/insecure.c +++ b/net/rxrpc/insecure.c @@ -31,10 +31,11 @@ static int none_secure_packet(struct rxrpc_call *call, struct sk_buff *skb, return 0; } -static int none_verify_packet(struct rxrpc_call *call, struct sk_buff *skb, - unsigned int offset, unsigned int len, - rxrpc_seq_t seq, u16 expected_cksum) +static int none_verify_packet(struct rxrpc_call *call, struct sk_buff *skb) { + struct rxrpc_skb_priv *sp = rxrpc_skb(skb); + + sp->flags |= RXRPC_RX_VERIFIED; return 0; } @@ -42,11 +43,6 @@ static void none_free_call_crypto(struct rxrpc_call *call) { } -static void none_locate_data(struct rxrpc_call *call, struct sk_buff *skb, - unsigned int *_offset, unsigned int *_len) -{ -} - static int none_respond_to_challenge(struct rxrpc_connection *conn, struct sk_buff *skb, u32 *_abort_code) @@ -95,7 +91,6 @@ const struct rxrpc_security rxrpc_no_security = { .how_much_data = none_how_much_data, .secure_packet = none_secure_packet, .verify_packet = none_verify_packet, - .locate_data = none_locate_data, .respond_to_challenge = none_respond_to_challenge, .verify_response = none_verify_response, .clear = none_clear, diff --git a/net/rxrpc/output.c b/net/rxrpc/output.c index d35657b659ad..f7bb792c8aa1 100644 --- a/net/rxrpc/output.c +++ b/net/rxrpc/output.c @@ -121,7 +121,7 @@ static size_t rxrpc_fill_out_ack(struct rxrpc_connection *conn, mtu = conn->params.peer->if_mtu; mtu -= conn->params.peer->hdrsize; - jmax = (call->nr_jumbo_bad > 3) ? 1 : rxrpc_rx_jumbo_max; + jmax = rxrpc_rx_jumbo_max; ackinfo.rxMTU = htonl(rxrpc_rx_mtu); ackinfo.maxMTU = htonl(mtu); ackinfo.rwind = htonl(call->rx_winsize); diff --git a/net/rxrpc/protocol.h b/net/rxrpc/protocol.h index f3d4e2700901..6760cb99c6d6 100644 --- a/net/rxrpc/protocol.h +++ b/net/rxrpc/protocol.h @@ -84,7 +84,7 @@ struct rxrpc_jumbo_header { __be16 _rsvd; /* reserved */ __be16 cksum; /* kerberos security checksum */ }; -}; +} __packed; #define RXRPC_JUMBO_DATALEN 1412 /* non-terminal jumbo packet data length */ #define RXRPC_JUMBO_SUBPKTLEN (RXRPC_JUMBO_DATALEN + sizeof(struct rxrpc_jumbo_header)) diff --git a/net/rxrpc/recvmsg.c b/net/rxrpc/recvmsg.c index 77cde6311559..401aae687830 100644 --- a/net/rxrpc/recvmsg.c +++ b/net/rxrpc/recvmsg.c @@ -222,7 +222,6 @@ static void rxrpc_rotate_rx_window(struct rxrpc_call *call) rxrpc_serial_t serial; rxrpc_seq_t hard_ack, top; bool last = false; - u8 subpacket; int ix; _enter("%d", call->debug_id); @@ -237,11 +236,9 @@ static void rxrpc_rotate_rx_window(struct rxrpc_call *call) rxrpc_see_skb(skb, rxrpc_skb_rotated); sp = rxrpc_skb(skb); - subpacket = call->rxtx_annotations[ix] & RXRPC_RX_ANNO_SUBPACKET; - serial = sp->hdr.serial + subpacket; + serial = sp->hdr.serial; - if (subpacket == sp->nr_subpackets - 1 && - sp->rx_flags & RXRPC_SKB_INCL_LAST) + if (sp->hdr.flags & RXRPC_LAST_PACKET) last = true; call->rxtx_buffer[ix] = NULL; @@ -266,80 +263,15 @@ static void rxrpc_rotate_rx_window(struct rxrpc_call *call) } /* - * Decrypt and verify a (sub)packet. The packet's length may be changed due to - * padding, but if this is the case, the packet length will be resident in the - * socket buffer. Note that we can't modify the master skb info as the skb may - * be the home to multiple subpackets. + * Decrypt and verify a DATA packet. */ -static int rxrpc_verify_packet(struct rxrpc_call *call, struct sk_buff *skb, - u8 annotation, - unsigned int offset, unsigned int len) +static int rxrpc_verify_data(struct rxrpc_call *call, struct sk_buff *skb) { struct rxrpc_skb_priv *sp = rxrpc_skb(skb); - rxrpc_seq_t seq = sp->hdr.seq; - u16 cksum = sp->hdr.cksum; - u8 subpacket = annotation & RXRPC_RX_ANNO_SUBPACKET; - _enter(""); - - /* For all but the head jumbo subpacket, the security checksum is in a - * jumbo header immediately prior to the data. - */ - if (subpacket > 0) { - __be16 tmp; - if (skb_copy_bits(skb, offset - 2, &tmp, 2) < 0) - BUG(); - cksum = ntohs(tmp); - seq += subpacket; - } - - return call->security->verify_packet(call, skb, offset, len, - seq, cksum); -} - -/* - * Locate the data within a packet. This is complicated by: - * - * (1) An skb may contain a jumbo packet - so we have to find the appropriate - * subpacket. - * - * (2) The (sub)packets may be encrypted and, if so, the encrypted portion - * contains an extra header which includes the true length of the data, - * excluding any encrypted padding. - */ -static int rxrpc_locate_data(struct rxrpc_call *call, struct sk_buff *skb, - u8 *_annotation, - unsigned int *_offset, unsigned int *_len, - bool *_last) -{ - struct rxrpc_skb_priv *sp = rxrpc_skb(skb); - unsigned int offset = sizeof(struct rxrpc_wire_header); - unsigned int len; - bool last = false; - int ret; - u8 annotation = *_annotation; - u8 subpacket = annotation & RXRPC_RX_ANNO_SUBPACKET; - - /* Locate the subpacket */ - offset += subpacket * RXRPC_JUMBO_SUBPKTLEN; - len = skb->len - offset; - if (subpacket < sp->nr_subpackets - 1) - len = RXRPC_JUMBO_DATALEN; - else if (sp->rx_flags & RXRPC_SKB_INCL_LAST) - last = true; - - if (!(annotation & RXRPC_RX_ANNO_VERIFIED)) { - ret = rxrpc_verify_packet(call, skb, annotation, offset, len); - if (ret < 0) - return ret; - *_annotation |= RXRPC_RX_ANNO_VERIFIED; - } - - *_offset = offset; - *_len = len; - *_last = last; - call->security->locate_data(call, skb, _offset, _len); - return 0; + if (sp->flags & RXRPC_RX_VERIFIED) + return 0; + return call->security->verify_packet(call, skb); } /* @@ -356,13 +288,11 @@ static int rxrpc_recvmsg_data(struct socket *sock, struct rxrpc_call *call, rxrpc_serial_t serial; rxrpc_seq_t hard_ack, top, seq; size_t remain; - bool rx_pkt_last; unsigned int rx_pkt_offset, rx_pkt_len; int ix, copy, ret = -EAGAIN, ret2; rx_pkt_offset = call->rx_pkt_offset; rx_pkt_len = call->rx_pkt_len; - rx_pkt_last = call->rx_pkt_last; if (call->state >= RXRPC_CALL_SERVER_ACK_REQUEST) { seq = call->rx_hard_ack; @@ -391,7 +321,6 @@ static int rxrpc_recvmsg_data(struct socket *sock, struct rxrpc_call *call, if (!(flags & MSG_PEEK)) { serial = sp->hdr.serial; - serial += call->rxtx_annotations[ix] & RXRPC_RX_ANNO_SUBPACKET; trace_rxrpc_receive(call, rxrpc_receive_front, serial, seq); } @@ -400,10 +329,9 @@ static int rxrpc_recvmsg_data(struct socket *sock, struct rxrpc_call *call, sock_recv_timestamp(msg, sock->sk, skb); if (rx_pkt_offset == 0) { - ret2 = rxrpc_locate_data(call, skb, - &call->rxtx_annotations[ix], - &rx_pkt_offset, &rx_pkt_len, - &rx_pkt_last); + ret2 = rxrpc_verify_data(call, skb); + rx_pkt_offset = sp->offset; + rx_pkt_len = sp->len; trace_rxrpc_recvdata(call, rxrpc_recvmsg_next, seq, rx_pkt_offset, rx_pkt_len, ret2); if (ret2 < 0) { @@ -443,16 +371,13 @@ static int rxrpc_recvmsg_data(struct socket *sock, struct rxrpc_call *call, } /* The whole packet has been transferred. */ - if (!(flags & MSG_PEEK)) - rxrpc_rotate_rx_window(call); + if (sp->hdr.flags & RXRPC_LAST_PACKET) + ret = 1; rx_pkt_offset = 0; rx_pkt_len = 0; - if (rx_pkt_last) { - ASSERTCMP(seq, ==, READ_ONCE(call->rx_top)); - ret = 1; - goto out; - } + if (!(flags & MSG_PEEK)) + rxrpc_rotate_rx_window(call); seq++; } @@ -461,7 +386,6 @@ out: if (!(flags & MSG_PEEK)) { call->rx_pkt_offset = rx_pkt_offset; call->rx_pkt_len = rx_pkt_len; - call->rx_pkt_last = rx_pkt_last; } done: trace_rxrpc_recvdata(call, rxrpc_recvmsg_data_return, seq, diff --git a/net/rxrpc/rxkad.c b/net/rxrpc/rxkad.c index 78fa0524156f..b19937776aa9 100644 --- a/net/rxrpc/rxkad.c +++ b/net/rxrpc/rxkad.c @@ -439,11 +439,11 @@ static int rxkad_secure_packet(struct rxrpc_call *call, * decrypt partial encryption on a packet (level 1 security) */ static int rxkad_verify_packet_1(struct rxrpc_call *call, struct sk_buff *skb, - unsigned int offset, unsigned int len, rxrpc_seq_t seq, struct skcipher_request *req) { struct rxkad_level1_hdr sechdr; + struct rxrpc_skb_priv *sp = rxrpc_skb(skb); struct rxrpc_crypt iv; struct scatterlist sg[16]; bool aborted; @@ -453,9 +453,9 @@ static int rxkad_verify_packet_1(struct rxrpc_call *call, struct sk_buff *skb, _enter(""); - if (len < 8) { + if (sp->len < 8) { aborted = rxrpc_abort_eproto(call, skb, "rxkad_1_hdr", "V1H", - RXKADSEALEDINCON); + RXKADSEALEDINCON); goto protocol_error; } @@ -463,7 +463,7 @@ static int rxkad_verify_packet_1(struct rxrpc_call *call, struct sk_buff *skb, * directly into the target buffer. */ sg_init_table(sg, ARRAY_SIZE(sg)); - ret = skb_to_sgvec(skb, sg, offset, 8); + ret = skb_to_sgvec(skb, sg, sp->offset, 8); if (unlikely(ret < 0)) return ret; @@ -477,12 +477,13 @@ static int rxkad_verify_packet_1(struct rxrpc_call *call, struct sk_buff *skb, skcipher_request_zero(req); /* Extract the decrypted packet length */ - if (skb_copy_bits(skb, offset, &sechdr, sizeof(sechdr)) < 0) { + if (skb_copy_bits(skb, sp->offset, &sechdr, sizeof(sechdr)) < 0) { aborted = rxrpc_abort_eproto(call, skb, "rxkad_1_len", "XV1", RXKADDATALEN); goto protocol_error; } - len -= sizeof(sechdr); + sp->offset += sizeof(sechdr); + sp->len -= sizeof(sechdr); buf = ntohl(sechdr.data_size); data_size = buf & 0xffff; @@ -496,11 +497,12 @@ static int rxkad_verify_packet_1(struct rxrpc_call *call, struct sk_buff *skb, goto protocol_error; } - if (data_size > len) { + if (data_size > sp->len) { aborted = rxrpc_abort_eproto(call, skb, "rxkad_1_datalen", "V1L", RXKADDATALEN); goto protocol_error; } + sp->len = data_size; _leave(" = 0 [dlen=%x]", data_size); return 0; @@ -515,12 +517,12 @@ protocol_error: * wholly decrypt a packet (level 2 security) */ static int rxkad_verify_packet_2(struct rxrpc_call *call, struct sk_buff *skb, - unsigned int offset, unsigned int len, rxrpc_seq_t seq, struct skcipher_request *req) { const struct rxrpc_key_token *token; struct rxkad_level2_hdr sechdr; + struct rxrpc_skb_priv *sp = rxrpc_skb(skb); struct rxrpc_crypt iv; struct scatterlist _sg[4], *sg; bool aborted; @@ -528,9 +530,9 @@ static int rxkad_verify_packet_2(struct rxrpc_call *call, struct sk_buff *skb, u16 check; int nsg, ret; - _enter(",{%d}", skb->len); + _enter(",{%d}", sp->len); - if (len < 8) { + if (sp->len < 8) { aborted = rxrpc_abort_eproto(call, skb, "rxkad_2_hdr", "V2H", RXKADSEALEDINCON); goto protocol_error; @@ -550,7 +552,7 @@ static int rxkad_verify_packet_2(struct rxrpc_call *call, struct sk_buff *skb, } sg_init_table(sg, nsg); - ret = skb_to_sgvec(skb, sg, offset, len); + ret = skb_to_sgvec(skb, sg, sp->offset, sp->len); if (unlikely(ret < 0)) { if (sg != _sg) kfree(sg); @@ -563,19 +565,20 @@ static int rxkad_verify_packet_2(struct rxrpc_call *call, struct sk_buff *skb, skcipher_request_set_sync_tfm(req, call->conn->rxkad.cipher); skcipher_request_set_callback(req, 0, NULL, NULL); - skcipher_request_set_crypt(req, sg, sg, len, iv.x); + skcipher_request_set_crypt(req, sg, sg, sp->len, iv.x); crypto_skcipher_decrypt(req); skcipher_request_zero(req); if (sg != _sg) kfree(sg); /* Extract the decrypted packet length */ - if (skb_copy_bits(skb, offset, &sechdr, sizeof(sechdr)) < 0) { + if (skb_copy_bits(skb, sp->offset, &sechdr, sizeof(sechdr)) < 0) { aborted = rxrpc_abort_eproto(call, skb, "rxkad_2_len", "XV2", RXKADDATALEN); goto protocol_error; } - len -= sizeof(sechdr); + sp->offset += sizeof(sechdr); + sp->len -= sizeof(sechdr); buf = ntohl(sechdr.data_size); data_size = buf & 0xffff; @@ -589,12 +592,13 @@ static int rxkad_verify_packet_2(struct rxrpc_call *call, struct sk_buff *skb, goto protocol_error; } - if (data_size > len) { + if (data_size > sp->len) { aborted = rxrpc_abort_eproto(call, skb, "rxkad_2_datalen", "V2L", RXKADDATALEN); goto protocol_error; } + sp->len = data_size; _leave(" = 0 [dlen=%x]", data_size); return 0; @@ -609,16 +613,15 @@ nomem: } /* - * Verify the security on a received packet or subpacket (if part of a - * jumbo packet). + * Verify the security on a received packet and the subpackets therein. */ -static int rxkad_verify_packet(struct rxrpc_call *call, struct sk_buff *skb, - unsigned int offset, unsigned int len, - rxrpc_seq_t seq, u16 expected_cksum) +static int rxkad_verify_packet(struct rxrpc_call *call, struct sk_buff *skb) { + struct rxrpc_skb_priv *sp = rxrpc_skb(skb); struct skcipher_request *req; struct rxrpc_crypt iv; struct scatterlist sg; + rxrpc_seq_t seq = sp->hdr.seq; bool aborted; u16 cksum; u32 x, y; @@ -654,7 +657,7 @@ static int rxkad_verify_packet(struct rxrpc_call *call, struct sk_buff *skb, if (cksum == 0) cksum = 1; /* zero checksums are not permitted */ - if (cksum != expected_cksum) { + if (cksum != sp->hdr.cksum) { aborted = rxrpc_abort_eproto(call, skb, "rxkad_csum", "VCK", RXKADSEALEDINCON); goto protocol_error; @@ -664,9 +667,9 @@ static int rxkad_verify_packet(struct rxrpc_call *call, struct sk_buff *skb, case RXRPC_SECURITY_PLAIN: return 0; case RXRPC_SECURITY_AUTH: - return rxkad_verify_packet_1(call, skb, offset, len, seq, req); + return rxkad_verify_packet_1(call, skb, seq, req); case RXRPC_SECURITY_ENCRYPT: - return rxkad_verify_packet_2(call, skb, offset, len, seq, req); + return rxkad_verify_packet_2(call, skb, seq, req); default: return -ENOANO; } @@ -677,52 +680,6 @@ protocol_error: return -EPROTO; } -/* - * Locate the data contained in a packet that was partially encrypted. - */ -static void rxkad_locate_data_1(struct rxrpc_call *call, struct sk_buff *skb, - unsigned int *_offset, unsigned int *_len) -{ - struct rxkad_level1_hdr sechdr; - - if (skb_copy_bits(skb, *_offset, &sechdr, sizeof(sechdr)) < 0) - BUG(); - *_offset += sizeof(sechdr); - *_len = ntohl(sechdr.data_size) & 0xffff; -} - -/* - * Locate the data contained in a packet that was completely encrypted. - */ -static void rxkad_locate_data_2(struct rxrpc_call *call, struct sk_buff *skb, - unsigned int *_offset, unsigned int *_len) -{ - struct rxkad_level2_hdr sechdr; - - if (skb_copy_bits(skb, *_offset, &sechdr, sizeof(sechdr)) < 0) - BUG(); - *_offset += sizeof(sechdr); - *_len = ntohl(sechdr.data_size) & 0xffff; -} - -/* - * Locate the data contained in an already decrypted packet. - */ -static void rxkad_locate_data(struct rxrpc_call *call, struct sk_buff *skb, - unsigned int *_offset, unsigned int *_len) -{ - switch (call->conn->params.security_level) { - case RXRPC_SECURITY_AUTH: - rxkad_locate_data_1(call, skb, _offset, _len); - return; - case RXRPC_SECURITY_ENCRYPT: - rxkad_locate_data_2(call, skb, _offset, _len); - return; - default: - return; - } -} - /* * issue a challenge */ @@ -1397,7 +1354,6 @@ const struct rxrpc_security rxkad = { .secure_packet = rxkad_secure_packet, .verify_packet = rxkad_verify_packet, .free_call_crypto = rxkad_free_call_crypto, - .locate_data = rxkad_locate_data, .issue_challenge = rxkad_issue_challenge, .respond_to_challenge = rxkad_respond_to_challenge, .verify_response = rxkad_verify_response, From 5d7edbc9231ec6b60f9c5b7e7980e9a1cd92e6bb Mon Sep 17 00:00:00 2001 From: David Howells Date: Sat, 27 Aug 2022 14:27:56 +0100 Subject: [PATCH 0735/2002] rxrpc: Get rid of the Rx ring Get rid of the Rx ring and replace it with a pair of queues instead. One queue gets the packets that are in-sequence and are ready for processing by recvmsg(); the other queue gets the out-of-sequence packets for addition to the first queue as the holes get filled. The annotation ring is removed and replaced with a SACK table. The SACK table has the bits set that correspond exactly to the sequence number of the packet being acked. The SACK ring is copied when an ACK packet is being assembled and rotated so that the first ACK is in byte 0. Flow control handling is altered so that packets that are moved to the in-sequence queue are hard-ACK'd even before they're consumed - and then the Rx window size in the ACK packet (rsize) is shrunk down to compensate (even going to 0 if the window is full). Signed-off-by: David Howells cc: Marc Dionne cc: linux-afs@lists.infradead.org --- include/trace/events/rxrpc.h | 19 +-- net/rxrpc/ar-internal.h | 29 +++-- net/rxrpc/call_object.c | 7 +- net/rxrpc/conn_object.c | 2 +- net/rxrpc/input.c | 217 +++++++++++++++++++++-------------- net/rxrpc/misc.c | 5 +- net/rxrpc/output.c | 91 +++++++++------ net/rxrpc/proc.c | 7 +- net/rxrpc/recvmsg.c | 117 ++++++++++--------- net/rxrpc/rxkad.c | 1 - net/rxrpc/sysctl.c | 2 +- 11 files changed, 290 insertions(+), 207 deletions(-) diff --git a/include/trace/events/rxrpc.h b/include/trace/events/rxrpc.h index 03a984e661bc..284a1560b0a8 100644 --- a/include/trace/events/rxrpc.h +++ b/include/trace/events/rxrpc.h @@ -104,7 +104,12 @@ EM(rxrpc_receive_incoming, "INC") \ EM(rxrpc_receive_queue, "QUE") \ EM(rxrpc_receive_queue_last, "QLS") \ - E_(rxrpc_receive_rotate, "ROT") + EM(rxrpc_receive_queue_oos, "QUO") \ + EM(rxrpc_receive_queue_oos_last, "QOL") \ + EM(rxrpc_receive_oos, "OOS") \ + EM(rxrpc_receive_oos_last, "OSL") \ + EM(rxrpc_receive_rotate, "ROT") \ + E_(rxrpc_receive_rotate_last, "RLS") #define rxrpc_recvmsg_traces \ EM(rxrpc_recvmsg_cont, "CONT") \ @@ -860,8 +865,7 @@ TRACE_EVENT(rxrpc_receive, __field(enum rxrpc_receive_trace, why ) __field(rxrpc_serial_t, serial ) __field(rxrpc_seq_t, seq ) - __field(rxrpc_seq_t, hard_ack ) - __field(rxrpc_seq_t, top ) + __field(u64, window ) ), TP_fast_assign( @@ -869,8 +873,7 @@ TRACE_EVENT(rxrpc_receive, __entry->why = why; __entry->serial = serial; __entry->seq = seq; - __entry->hard_ack = call->rx_hard_ack; - __entry->top = call->rx_top; + __entry->window = atomic64_read(&call->ackr_window); ), TP_printk("c=%08x %s r=%08x q=%08x w=%08x-%08x", @@ -878,8 +881,8 @@ TRACE_EVENT(rxrpc_receive, __print_symbolic(__entry->why, rxrpc_receive_traces), __entry->serial, __entry->seq, - __entry->hard_ack, - __entry->top) + lower_32_bits(__entry->window), + upper_32_bits(__entry->window)) ); TRACE_EVENT(rxrpc_recvmsg, @@ -1459,7 +1462,7 @@ TRACE_EVENT(rxrpc_call_reset, __entry->call_serial = call->rx_serial; __entry->conn_serial = call->conn->hi_serial; __entry->tx_seq = call->tx_hard_ack; - __entry->rx_seq = call->rx_hard_ack; + __entry->rx_seq = call->rx_highest_seq; ), TP_printk("c=%08x %08x:%08x r=%08x/%08x tx=%08x rx=%08x", diff --git a/net/rxrpc/ar-internal.h b/net/rxrpc/ar-internal.h index e93ed18816b1..c6c5bb3d3688 100644 --- a/net/rxrpc/ar-internal.h +++ b/net/rxrpc/ar-internal.h @@ -198,7 +198,6 @@ struct rxrpc_skb_priv { u16 remain; u16 offset; /* Offset of data */ u16 len; /* Length of data */ - u8 rx_flags; /* Received packet flags */ u8 flags; #define RXRPC_RX_VERIFIED 0x01 @@ -644,8 +643,20 @@ struct rxrpc_call { rxrpc_seq_t tx_hard_ack; /* Dead slot in buffer; the first transmitted but * not hard-ACK'd packet follows this. */ + + /* Transmitted data tracking. */ rxrpc_seq_t tx_top; /* Highest Tx slot allocated. */ u16 tx_backoff; /* Delay to insert due to Tx failure */ + u8 tx_winsize; /* Maximum size of Tx window */ + + /* Received data tracking */ + struct sk_buff_head recvmsg_queue; /* Queue of packets ready for recvmsg() */ + struct sk_buff_head rx_oos_queue; /* Queue of out of sequence packets */ + + rxrpc_seq_t rx_highest_seq; /* Higest sequence number received */ + rxrpc_seq_t rx_consumed; /* Highest packet consumed */ + rxrpc_serial_t rx_serial; /* Highest serial received for this call */ + u8 rx_winsize; /* Size of Rx window */ /* TCP-style slow-start congestion control [RFC5681]. Since the SMSS * is fixed, we keep these numbers in terms of segments (ie. DATA @@ -660,23 +671,19 @@ struct rxrpc_call { u8 cong_cumul_acks; /* Cumulative ACK count */ ktime_t cong_tstamp; /* Last time cwnd was changed */ - rxrpc_seq_t rx_hard_ack; /* Dead slot in buffer; the first received but not - * consumed packet follows this. - */ - rxrpc_seq_t rx_top; /* Highest Rx slot allocated. */ - rxrpc_seq_t rx_expect_next; /* Expected next packet sequence number */ - rxrpc_serial_t rx_serial; /* Highest serial received for this call */ - u8 rx_winsize; /* Size of Rx window */ - u8 tx_winsize; /* Maximum size of Tx window */ - spinlock_t input_lock; /* Lock for packet input to this call */ /* Receive-phase ACK management (ACKs we send). */ u8 ackr_reason; /* reason to ACK */ rxrpc_serial_t ackr_serial; /* serial of packet being ACK'd */ - rxrpc_seq_t ackr_highest_seq; /* Higest sequence number received */ + atomic64_t ackr_window; /* Base (in LSW) and top (in MSW) of SACK window */ atomic_t ackr_nr_unacked; /* Number of unacked packets */ atomic_t ackr_nr_consumed; /* Number of packets needing hard ACK */ + struct { +#define RXRPC_SACK_SIZE 256 + /* SACK table for soft-acked packets */ + u8 ackr_sack_table[RXRPC_SACK_SIZE]; + } __aligned(8); /* RTT management */ rxrpc_serial_t rtt_serial[4]; /* Serial number of DATA or PING sent */ diff --git a/net/rxrpc/call_object.c b/net/rxrpc/call_object.c index 8f9e88897197..4d8601c6a32d 100644 --- a/net/rxrpc/call_object.c +++ b/net/rxrpc/call_object.c @@ -155,6 +155,8 @@ struct rxrpc_call *rxrpc_alloc_call(struct rxrpc_sock *rx, gfp_t gfp, INIT_LIST_HEAD(&call->accept_link); INIT_LIST_HEAD(&call->recvmsg_link); INIT_LIST_HEAD(&call->sock_link); + skb_queue_head_init(&call->recvmsg_queue); + skb_queue_head_init(&call->rx_oos_queue); init_waitqueue_head(&call->waitq); spin_lock_init(&call->lock); spin_lock_init(&call->notify_lock); @@ -165,13 +167,12 @@ struct rxrpc_call *rxrpc_alloc_call(struct rxrpc_sock *rx, gfp_t gfp, call->tx_total_len = -1; call->next_rx_timo = 20 * HZ; call->next_req_timo = 1 * HZ; + atomic64_set(&call->ackr_window, 0x100000001ULL); memset(&call->sock_node, 0xed, sizeof(call->sock_node)); - /* Leave space in the ring to handle a maxed-out jumbo packet */ call->rx_winsize = rxrpc_rx_window_size; call->tx_winsize = 16; - call->rx_expect_next = 1; call->cong_cwnd = 2; call->cong_ssthresh = RXRPC_RXTX_BUFF_SIZE - 1; @@ -519,6 +520,8 @@ static void rxrpc_cleanup_ring(struct rxrpc_call *call) rxrpc_free_skb(call->rxtx_buffer[i], rxrpc_skb_cleaned); call->rxtx_buffer[i] = NULL; } + skb_queue_purge(&call->recvmsg_queue); + skb_queue_purge(&call->rx_oos_queue); } /* diff --git a/net/rxrpc/conn_object.c b/net/rxrpc/conn_object.c index 22089e37e97f..f7ea71ae6159 100644 --- a/net/rxrpc/conn_object.c +++ b/net/rxrpc/conn_object.c @@ -175,7 +175,7 @@ void __rxrpc_disconnect_call(struct rxrpc_connection *conn, trace_rxrpc_disconnect_call(call); switch (call->completion) { case RXRPC_CALL_SUCCEEDED: - chan->last_seq = call->rx_hard_ack; + chan->last_seq = call->rx_highest_seq; chan->last_type = RXRPC_PACKET_TYPE_ACK; break; case RXRPC_CALL_LOCALLY_ABORTED: diff --git a/net/rxrpc/input.c b/net/rxrpc/input.c index 0e7545ed0128..947e7196e2be 100644 --- a/net/rxrpc/input.c +++ b/net/rxrpc/input.c @@ -312,18 +312,43 @@ static bool rxrpc_receiving_reply(struct rxrpc_call *call) return rxrpc_end_tx_phase(call, true, "ETD"); } +static void rxrpc_input_update_ack_window(struct rxrpc_call *call, + rxrpc_seq_t window, rxrpc_seq_t wtop) +{ + atomic64_set_release(&call->ackr_window, ((u64)wtop) << 32 | window); +} + /* - * Process a DATA packet, adding the packet to the Rx ring. The caller's - * packet ref must be passed on or discarded. + * Push a DATA packet onto the Rx queue. + */ +static void rxrpc_input_queue_data(struct rxrpc_call *call, struct sk_buff *skb, + rxrpc_seq_t window, rxrpc_seq_t wtop, + enum rxrpc_receive_trace why) +{ + struct rxrpc_skb_priv *sp = rxrpc_skb(skb); + bool last = sp->hdr.flags & RXRPC_LAST_PACKET; + + __skb_queue_tail(&call->recvmsg_queue, skb); + rxrpc_input_update_ack_window(call, window, wtop); + + trace_rxrpc_receive(call, last ? why + 1 : why, sp->hdr.serial, sp->hdr.seq); +} + +/* + * Process a DATA packet. */ static void rxrpc_input_data_one(struct rxrpc_call *call, struct sk_buff *skb) { struct rxrpc_skb_priv *sp = rxrpc_skb(skb); + struct sk_buff *oos; rxrpc_serial_t serial = sp->hdr.serial; - rxrpc_seq_t seq = sp->hdr.seq, hard_ack; - unsigned int ix = seq & RXRPC_RXTX_BUFF_MASK; + u64 win = atomic64_read(&call->ackr_window); + rxrpc_seq_t window = lower_32_bits(win); + rxrpc_seq_t wtop = upper_32_bits(win); + rxrpc_seq_t wlimit = window + call->rx_winsize - 1; + rxrpc_seq_t seq = sp->hdr.seq; bool last = sp->hdr.flags & RXRPC_LAST_PACKET; - bool acked = false; + int ack_reason = -1; rxrpc_inc_stat(call->rxnet, stat_rx_data); if (sp->hdr.flags & RXRPC_REQUEST_ACK) @@ -331,112 +356,135 @@ static void rxrpc_input_data_one(struct rxrpc_call *call, struct sk_buff *skb) if (sp->hdr.flags & RXRPC_JUMBO_PACKET) rxrpc_inc_stat(call->rxnet, stat_rx_data_jumbo); - hard_ack = READ_ONCE(call->rx_hard_ack); - - _proto("Rx DATA %%%u { #%x l=%u }", serial, seq, last); - if (last) { - if (test_bit(RXRPC_CALL_RX_LAST, &call->flags) && - seq != call->rx_top) { + if (test_and_set_bit(RXRPC_CALL_RX_LAST, &call->flags) && + seq + 1 != wtop) { rxrpc_proto_abort("LSN", call, seq); - goto out; + goto err_free; } } else { if (test_bit(RXRPC_CALL_RX_LAST, &call->flags) && - after_eq(seq, call->rx_top)) { + after_eq(seq, wtop)) { + pr_warn("Packet beyond last: c=%x q=%x window=%x-%x wlimit=%x\n", + call->debug_id, seq, window, wtop, wlimit); rxrpc_proto_abort("LSA", call, seq); - goto out; + goto err_free; } } + if (after(seq, call->rx_highest_seq)) + call->rx_highest_seq = seq; + trace_rxrpc_rx_data(call->debug_id, seq, serial, sp->hdr.flags); - if (before_eq(seq, hard_ack)) { - rxrpc_send_ACK(call, RXRPC_ACK_DUPLICATE, serial, - rxrpc_propose_ack_input_data); - goto out; + if (before(seq, window)) { + ack_reason = RXRPC_ACK_DUPLICATE; + goto send_ack; + } + if (after(seq, wlimit)) { + ack_reason = RXRPC_ACK_EXCEEDS_WINDOW; + goto send_ack; } - if (call->rxtx_buffer[ix]) { - rxrpc_send_ACK(call, RXRPC_ACK_DUPLICATE, serial, - rxrpc_propose_ack_input_data); - goto out; - } + /* Queue the packet. */ + if (seq == window) { + rxrpc_seq_t reset_from; + bool reset_sack = false; - if (after(seq, hard_ack + call->rx_winsize)) { - rxrpc_send_ACK(call, RXRPC_ACK_EXCEEDS_WINDOW, serial, - rxrpc_propose_ack_input_data); - goto out; - } - - if (sp->hdr.flags & RXRPC_REQUEST_ACK) { - rxrpc_send_ACK(call, RXRPC_ACK_REQUESTED, serial, - rxrpc_propose_ack_input_data); - acked = true; - } - - if (after(seq, call->ackr_highest_seq)) - call->ackr_highest_seq = seq; - - /* Queue the packet. We use a couple of memory barriers here as need - * to make sure that rx_top is perceived to be set after the buffer - * pointer and that the buffer pointer is set after the annotation and - * the skb data. - * - * Barriers against rxrpc_recvmsg_data() and rxrpc_rotate_rx_window() - * and also rxrpc_fill_out_ack(). - */ - call->rxtx_annotations[ix] = 1; - smp_wmb(); - call->rxtx_buffer[ix] = skb; - if (after(seq, call->rx_top)) { - smp_store_release(&call->rx_top, seq); - } else if (before(seq, call->rx_top)) { + if (sp->hdr.flags & RXRPC_REQUEST_ACK) + ack_reason = RXRPC_ACK_REQUESTED; /* Send an immediate ACK if we fill in a hole */ - if (!acked) { - rxrpc_send_ACK(call, RXRPC_ACK_DELAY, serial, - rxrpc_propose_ack_input_data_hole); - acked = true; + else if (!skb_queue_empty(&call->rx_oos_queue)) + ack_reason = RXRPC_ACK_DELAY; + + window++; + if (after(window, wtop)) + wtop = window; + + spin_lock(&call->recvmsg_queue.lock); + rxrpc_input_queue_data(call, skb, window, wtop, rxrpc_receive_queue); + skb = NULL; + + while ((oos = skb_peek(&call->rx_oos_queue))) { + struct rxrpc_skb_priv *osp = rxrpc_skb(oos); + + if (after(osp->hdr.seq, window)) + break; + + __skb_unlink(oos, &call->rx_oos_queue); + last = osp->hdr.flags & RXRPC_LAST_PACKET; + seq = osp->hdr.seq; + if (!reset_sack) { + reset_from = seq; + reset_sack = true; + } + + window++; + rxrpc_input_queue_data(call, oos, window, wtop, + rxrpc_receive_queue_oos); } - } - /* From this point on, we're not allowed to touch the packet any longer - * as its ref now belongs to the Rx ring. - */ - skb = NULL; - sp = NULL; + spin_unlock(&call->recvmsg_queue.lock); - if (last) { - set_bit(RXRPC_CALL_RX_LAST, &call->flags); - trace_rxrpc_receive(call, rxrpc_receive_queue_last, serial, seq); + if (reset_sack) { + do { + call->ackr_sack_table[reset_from % RXRPC_SACK_SIZE] = 0; + } while (reset_from++, before(reset_from, window)); + } } else { - trace_rxrpc_receive(call, rxrpc_receive_queue, serial, seq); - } + bool keep = false; - if (after_eq(seq, call->rx_expect_next)) { - if (after(seq, call->rx_expect_next)) { - _net("OOS %u > %u", seq, call->rx_expect_next); - rxrpc_send_ACK(call, RXRPC_ACK_OUT_OF_SEQUENCE, serial, - rxrpc_propose_ack_input_data); - acked = true; + ack_reason = RXRPC_ACK_OUT_OF_SEQUENCE; + + if (!call->ackr_sack_table[seq % RXRPC_SACK_SIZE]) { + call->ackr_sack_table[seq % RXRPC_SACK_SIZE] = 1; + keep = 1; } - call->rx_expect_next = seq + 1; + + if (after(seq + 1, wtop)) { + wtop = seq + 1; + rxrpc_input_update_ack_window(call, window, wtop); + } + + if (!keep) { + ack_reason = RXRPC_ACK_DUPLICATE; + goto send_ack; + } + + skb_queue_walk(&call->rx_oos_queue, oos) { + struct rxrpc_skb_priv *osp = rxrpc_skb(oos); + + if (after(osp->hdr.seq, seq)) { + __skb_queue_before(&call->rx_oos_queue, oos, skb); + goto oos_queued; + } + } + + __skb_queue_tail(&call->rx_oos_queue, skb); + oos_queued: + trace_rxrpc_receive(call, last ? rxrpc_receive_oos_last : rxrpc_receive_oos, + sp->hdr.serial, sp->hdr.seq); + skb = NULL; } -out: - if (!acked && - atomic_inc_return(&call->ackr_nr_unacked) > 2) - rxrpc_send_ACK(call, RXRPC_ACK_IDLE, serial, +send_ack: + if (ack_reason < 0 && + atomic_inc_return(&call->ackr_nr_unacked) > 2 && + test_and_set_bit(RXRPC_CALL_IDLE_ACK_PENDING, &call->flags)) { + ack_reason = RXRPC_ACK_IDLE; + } else if (ack_reason >= 0) { + set_bit(RXRPC_CALL_IDLE_ACK_PENDING, &call->flags); + } + + if (ack_reason >= 0) + rxrpc_send_ACK(call, ack_reason, serial, rxrpc_propose_ack_input_data); else rxrpc_propose_delay_ACK(call, serial, rxrpc_propose_ack_input_data); - trace_rxrpc_notify_socket(call->debug_id, serial); - rxrpc_notify_socket(call); - +err_free: rxrpc_free_skb(skb, rxrpc_skb_freed); - _leave(" [queued]"); } /* @@ -498,8 +546,9 @@ static void rxrpc_input_data(struct rxrpc_call *call, struct sk_buff *skb) rxrpc_serial_t serial = sp->hdr.serial; rxrpc_seq_t seq0 = sp->hdr.seq; - _enter("{%u,%u},{%u,%u}", - call->rx_hard_ack, call->rx_top, skb->len, seq0); + _enter("{%llx,%x},{%u,%x}", + atomic64_read(&call->ackr_window), call->rx_highest_seq, + skb->len, seq0); _proto("Rx DATA %%%u { #%u f=%02x }", sp->hdr.serial, seq0, sp->hdr.flags); diff --git a/net/rxrpc/misc.c b/net/rxrpc/misc.c index f5f03f27bd6c..056c428d8bf3 100644 --- a/net/rxrpc/misc.c +++ b/net/rxrpc/misc.c @@ -40,10 +40,7 @@ unsigned long rxrpc_idle_ack_delay = HZ / 2; * limit is hit, we should generate an EXCEEDS_WINDOW ACK and discard further * packets. */ -unsigned int rxrpc_rx_window_size = RXRPC_INIT_RX_WINDOW_SIZE; -#if (RXRPC_RXTX_BUFF_SIZE - 1) < RXRPC_INIT_RX_WINDOW_SIZE -#error Need to reduce RXRPC_INIT_RX_WINDOW_SIZE -#endif +unsigned int rxrpc_rx_window_size = 255; /* * Maximum Rx MTU size. This indicates to the sender the size of jumbo packet diff --git a/net/rxrpc/output.c b/net/rxrpc/output.c index f7bb792c8aa1..0a4f37d7b6b5 100644 --- a/net/rxrpc/output.c +++ b/net/rxrpc/output.c @@ -74,47 +74,64 @@ static void rxrpc_set_keepalive(struct rxrpc_call *call) */ static size_t rxrpc_fill_out_ack(struct rxrpc_connection *conn, struct rxrpc_call *call, - struct rxrpc_txbuf *txb, - rxrpc_seq_t *_hard_ack, - rxrpc_seq_t *_top) + struct rxrpc_txbuf *txb) { struct rxrpc_ackinfo ackinfo; - unsigned int tmp; - rxrpc_seq_t hard_ack, top, seq; - int ix; + unsigned int qsize; + rxrpc_seq_t window, wtop, wrap_point, ix, first; + int rsize; + u64 wtmp; u32 mtu, jmax; u8 *ackp = txb->acks; + u8 sack_buffer[sizeof(call->ackr_sack_table)] __aligned(8); - tmp = atomic_xchg(&call->ackr_nr_unacked, 0); - tmp |= atomic_xchg(&call->ackr_nr_consumed, 0); - if (!tmp && (txb->ack.reason == RXRPC_ACK_DELAY || - txb->ack.reason == RXRPC_ACK_IDLE)) { - rxrpc_inc_stat(call->rxnet, stat_tx_ack_skip); - return 0; - } - + atomic_set(&call->ackr_nr_unacked, 0); + atomic_set(&call->ackr_nr_consumed, 0); rxrpc_inc_stat(call->rxnet, stat_tx_ack_fill); /* Barrier against rxrpc_input_data(). */ - hard_ack = READ_ONCE(call->rx_hard_ack); - top = smp_load_acquire(&call->rx_top); - *_hard_ack = hard_ack; - *_top = top; +retry: + wtmp = atomic64_read_acquire(&call->ackr_window); + window = lower_32_bits(wtmp); + wtop = upper_32_bits(wtmp); + txb->ack.firstPacket = htonl(window); + txb->ack.nAcks = 0; - txb->ack.firstPacket = htonl(hard_ack + 1); - txb->ack.previousPacket = htonl(call->ackr_highest_seq); - txb->ack.nAcks = top - hard_ack; + if (after(wtop, window)) { + /* Try to copy the SACK ring locklessly. We can use the copy, + * only if the now-current top of the window didn't go past the + * previously read base - otherwise we can't know whether we + * have old data or new data. + */ + memcpy(sack_buffer, call->ackr_sack_table, sizeof(sack_buffer)); + wrap_point = window + RXRPC_SACK_SIZE - 1; + wtmp = atomic64_read_acquire(&call->ackr_window); + window = lower_32_bits(wtmp); + wtop = upper_32_bits(wtmp); + if (after(wtop, wrap_point)) { + cond_resched(); + goto retry; + } - if (txb->ack.nAcks) { - seq = hard_ack + 1; - do { - ix = seq & RXRPC_RXTX_BUFF_MASK; - if (call->rxtx_buffer[ix]) - *ackp++ = RXRPC_ACK_TYPE_ACK; - else - *ackp++ = RXRPC_ACK_TYPE_NACK; - seq++; - } while (before_eq(seq, top)); + /* The buffer is maintained as a ring with an invariant mapping + * between bit position and sequence number, so we'll probably + * need to rotate it. + */ + txb->ack.nAcks = wtop - window; + ix = window % RXRPC_SACK_SIZE; + first = sizeof(sack_buffer) - ix; + + if (ix + txb->ack.nAcks <= RXRPC_SACK_SIZE) { + memcpy(txb->acks, sack_buffer + ix, txb->ack.nAcks); + } else { + memcpy(txb->acks, sack_buffer + ix, first); + memcpy(txb->acks + first, sack_buffer, + txb->ack.nAcks - first); + } + + ackp += txb->ack.nAcks; + } else if (before(wtop, window)) { + pr_warn("ack window backward %x %x", window, wtop); } else if (txb->ack.reason == RXRPC_ACK_DELAY) { txb->ack.reason = RXRPC_ACK_IDLE; } @@ -122,16 +139,18 @@ static size_t rxrpc_fill_out_ack(struct rxrpc_connection *conn, mtu = conn->params.peer->if_mtu; mtu -= conn->params.peer->hdrsize; jmax = rxrpc_rx_jumbo_max; + qsize = (window - 1) - call->rx_consumed; + rsize = max_t(int, call->rx_winsize - qsize, 0); ackinfo.rxMTU = htonl(rxrpc_rx_mtu); ackinfo.maxMTU = htonl(mtu); - ackinfo.rwind = htonl(call->rx_winsize); + ackinfo.rwind = htonl(rsize); ackinfo.jumbo_max = htonl(jmax); *ackp++ = 0; *ackp++ = 0; *ackp++ = 0; memcpy(ackp, &ackinfo, sizeof(ackinfo)); - return top - hard_ack + 3 + sizeof(ackinfo); + return txb->ack.nAcks + 3 + sizeof(ackinfo); } /* @@ -188,7 +207,6 @@ static int rxrpc_send_ack_packet(struct rxrpc_local *local, struct rxrpc_txbuf * struct msghdr msg; struct kvec iov[1]; rxrpc_serial_t serial; - rxrpc_seq_t hard_ack, top; size_t len, n; int ret, rtt_slot = -1; @@ -212,7 +230,7 @@ static int rxrpc_send_ack_packet(struct rxrpc_local *local, struct rxrpc_txbuf * clear_bit(RXRPC_CALL_IDLE_ACK_PENDING, &call->flags); spin_lock_bh(&call->lock); - n = rxrpc_fill_out_ack(conn, call, txb, &hard_ack, &top); + n = rxrpc_fill_out_ack(conn, call, txb); spin_unlock_bh(&call->lock); if (n == 0) { kfree(pkt); @@ -236,6 +254,9 @@ static int rxrpc_send_ack_packet(struct rxrpc_local *local, struct rxrpc_txbuf * rxrpc_inc_stat(call->rxnet, stat_tx_ack_send); + /* Grab the highest received seq as late as possible */ + txb->ack.previousPacket = htonl(call->rx_highest_seq); + iov_iter_kvec(&msg.msg_iter, WRITE, iov, 1, len); ret = do_udp_sendmsg(conn->params.local->socket, &msg, len); call->peer->last_tx_at = ktime_get_seconds(); diff --git a/net/rxrpc/proc.c b/net/rxrpc/proc.c index 3877afd23598..d48af0178866 100644 --- a/net/rxrpc/proc.c +++ b/net/rxrpc/proc.c @@ -54,8 +54,9 @@ static int rxrpc_call_seq_show(struct seq_file *seq, void *v) struct rxrpc_call *call; struct rxrpc_net *rxnet = rxrpc_net(seq_file_net(seq)); unsigned long timeout = 0; - rxrpc_seq_t tx_hard_ack, rx_hard_ack; + rxrpc_seq_t tx_hard_ack; char lbuff[50], rbuff[50]; + u64 wtmp; if (v == &rxnet->calls) { seq_puts(seq, @@ -91,7 +92,7 @@ static int rxrpc_call_seq_show(struct seq_file *seq, void *v) } tx_hard_ack = READ_ONCE(call->tx_hard_ack); - rx_hard_ack = READ_ONCE(call->rx_hard_ack); + wtmp = atomic64_read_acquire(&call->ackr_window); seq_printf(seq, "UDP %-47.47s %-47.47s %4x %08x %08x %s %3u" " %-8.8s %08x %08x %08x %02x %08x %02x %08x %06lx\n", @@ -106,7 +107,7 @@ static int rxrpc_call_seq_show(struct seq_file *seq, void *v) call->abort_code, call->debug_id, tx_hard_ack, READ_ONCE(call->tx_top) - tx_hard_ack, - rx_hard_ack, READ_ONCE(call->rx_top) - rx_hard_ack, + lower_32_bits(wtmp), upper_32_bits(wtmp) - lower_32_bits(wtmp), call->rx_serial, timeout); diff --git a/net/rxrpc/recvmsg.c b/net/rxrpc/recvmsg.c index 401aae687830..efb85f983657 100644 --- a/net/rxrpc/recvmsg.c +++ b/net/rxrpc/recvmsg.c @@ -173,7 +173,8 @@ static int rxrpc_recvmsg_term(struct rxrpc_call *call, struct msghdr *msg) break; } - trace_rxrpc_recvdata(call, rxrpc_recvmsg_terminal, call->rx_hard_ack, + trace_rxrpc_recvdata(call, rxrpc_recvmsg_terminal, + lower_32_bits(atomic64_read(&call->ackr_window)) - 1, call->rx_pkt_offset, call->rx_pkt_len, ret); return ret; } @@ -183,10 +184,11 @@ static int rxrpc_recvmsg_term(struct rxrpc_call *call, struct msghdr *msg) */ static void rxrpc_end_rx_phase(struct rxrpc_call *call, rxrpc_serial_t serial) { + rxrpc_seq_t whigh = READ_ONCE(call->rx_highest_seq); + _enter("%d,%s", call->debug_id, rxrpc_call_states[call->state]); - trace_rxrpc_receive(call, rxrpc_receive_end, 0, call->rx_top); - ASSERTCMP(call->rx_hard_ack, ==, call->rx_top); + trace_rxrpc_receive(call, rxrpc_receive_end, 0, whigh); if (call->state == RXRPC_CALL_CLIENT_RECV_REPLY) rxrpc_propose_delay_ACK(call, serial, rxrpc_propose_ack_terminal_ack); @@ -220,45 +222,53 @@ static void rxrpc_rotate_rx_window(struct rxrpc_call *call) struct rxrpc_skb_priv *sp; struct sk_buff *skb; rxrpc_serial_t serial; - rxrpc_seq_t hard_ack, top; - bool last = false; - int ix; + rxrpc_seq_t old_consumed = call->rx_consumed, tseq; + bool last; + int acked; _enter("%d", call->debug_id); - hard_ack = call->rx_hard_ack; - top = smp_load_acquire(&call->rx_top); - ASSERT(before(hard_ack, top)); - - hard_ack++; - ix = hard_ack & RXRPC_RXTX_BUFF_MASK; - skb = call->rxtx_buffer[ix]; +further_rotation: + skb = skb_dequeue(&call->recvmsg_queue); rxrpc_see_skb(skb, rxrpc_skb_rotated); + sp = rxrpc_skb(skb); - + tseq = sp->hdr.seq; serial = sp->hdr.serial; + last = sp->hdr.flags & RXRPC_LAST_PACKET; - if (sp->hdr.flags & RXRPC_LAST_PACKET) - last = true; - - call->rxtx_buffer[ix] = NULL; - call->rxtx_annotations[ix] = 0; /* Barrier against rxrpc_input_data(). */ - smp_store_release(&call->rx_hard_ack, hard_ack); + if (after(tseq, call->rx_consumed)) + smp_store_release(&call->rx_consumed, tseq); rxrpc_free_skb(skb, rxrpc_skb_freed); - trace_rxrpc_receive(call, rxrpc_receive_rotate, serial, hard_ack); + trace_rxrpc_receive(call, last ? rxrpc_receive_rotate_last : rxrpc_receive_rotate, + serial, call->rx_consumed); if (last) { rxrpc_end_rx_phase(call, serial); - } else { - /* Check to see if there's an ACK that needs sending. */ - if (atomic_inc_return(&call->ackr_nr_consumed) > 2 && - !test_and_set_bit(RXRPC_CALL_IDLE_ACK_PENDING, &call->flags)) { - rxrpc_send_ACK(call, RXRPC_ACK_IDLE, serial, - rxrpc_propose_ack_rotate_rx); - rxrpc_transmit_ack_packets(call->peer->local); - } + return; + } + + /* The next packet on the queue might entirely overlap with the one we + * just consumed; if so, rotate that away also. + */ + skb = skb_peek(&call->recvmsg_queue); + if (skb) { + sp = rxrpc_skb(skb); + if (sp->hdr.seq != call->rx_consumed && + after_eq(call->rx_consumed, sp->hdr.seq)) + goto further_rotation; + } + + /* Check to see if there's an ACK that needs sending. */ + acked = atomic_add_return(call->rx_consumed - old_consumed, + &call->ackr_nr_consumed); + if (acked > 2 && + !test_and_set_bit(RXRPC_CALL_IDLE_ACK_PENDING, &call->flags)) { + rxrpc_send_ACK(call, RXRPC_ACK_IDLE, serial, + rxrpc_propose_ack_rotate_rx); + rxrpc_transmit_ack_packets(call->peer->local); } } @@ -285,46 +295,38 @@ static int rxrpc_recvmsg_data(struct socket *sock, struct rxrpc_call *call, { struct rxrpc_skb_priv *sp; struct sk_buff *skb; - rxrpc_serial_t serial; - rxrpc_seq_t hard_ack, top, seq; + rxrpc_seq_t seq = 0; size_t remain; unsigned int rx_pkt_offset, rx_pkt_len; - int ix, copy, ret = -EAGAIN, ret2; + int copy, ret = -EAGAIN, ret2; rx_pkt_offset = call->rx_pkt_offset; rx_pkt_len = call->rx_pkt_len; if (call->state >= RXRPC_CALL_SERVER_ACK_REQUEST) { - seq = call->rx_hard_ack; + seq = lower_32_bits(atomic64_read(&call->ackr_window)) - 1; ret = 1; goto done; } - /* Barriers against rxrpc_input_data(). */ - hard_ack = call->rx_hard_ack; - seq = hard_ack + 1; - - while (top = smp_load_acquire(&call->rx_top), - before_eq(seq, top) - ) { - ix = seq & RXRPC_RXTX_BUFF_MASK; - skb = call->rxtx_buffer[ix]; - if (!skb) { - trace_rxrpc_recvdata(call, rxrpc_recvmsg_hole, seq, - rx_pkt_offset, rx_pkt_len, 0); - rxrpc_transmit_ack_packets(call->peer->local); - break; - } - smp_rmb(); + /* No one else can be removing stuff from the queue, so we shouldn't + * need the Rx lock to walk it. + */ + skb = skb_peek(&call->recvmsg_queue); + while (skb) { rxrpc_see_skb(skb, rxrpc_skb_seen); sp = rxrpc_skb(skb); + seq = sp->hdr.seq; - if (!(flags & MSG_PEEK)) { - serial = sp->hdr.serial; - trace_rxrpc_receive(call, rxrpc_receive_front, - serial, seq); + if (after_eq(call->rx_consumed, seq)) { + kdebug("obsolete %x %x", call->rx_consumed, seq); + goto skip_obsolete; } + if (!(flags & MSG_PEEK)) + trace_rxrpc_receive(call, rxrpc_receive_front, + sp->hdr.serial, seq); + if (msg) sock_recv_timestamp(msg, sock->sk, skb); @@ -338,6 +340,7 @@ static int rxrpc_recvmsg_data(struct socket *sock, struct rxrpc_call *call, ret = ret2; goto out; } + rxrpc_transmit_ack_packets(call->peer->local); } else { trace_rxrpc_recvdata(call, rxrpc_recvmsg_cont, seq, rx_pkt_offset, rx_pkt_len, 0); @@ -370,16 +373,17 @@ static int rxrpc_recvmsg_data(struct socket *sock, struct rxrpc_call *call, break; } + skip_obsolete: /* The whole packet has been transferred. */ if (sp->hdr.flags & RXRPC_LAST_PACKET) ret = 1; rx_pkt_offset = 0; rx_pkt_len = 0; + skb = skb_peek_next(skb, &call->recvmsg_queue); + if (!(flags & MSG_PEEK)) rxrpc_rotate_rx_window(call); - - seq++; } out: @@ -522,8 +526,7 @@ try_again: ret = 0; rxrpc_transmit_ack_packets(call->peer->local); - if (after(call->rx_top, call->rx_hard_ack) && - call->rxtx_buffer[(call->rx_hard_ack + 1) & RXRPC_RXTX_BUFF_MASK]) + if (!skb_queue_empty(&call->recvmsg_queue)) rxrpc_notify_socket(call); break; default: diff --git a/net/rxrpc/rxkad.c b/net/rxrpc/rxkad.c index b19937776aa9..d87c99b36e01 100644 --- a/net/rxrpc/rxkad.c +++ b/net/rxrpc/rxkad.c @@ -1191,7 +1191,6 @@ static int rxkad_verify_response(struct rxrpc_connection *conn, abort_code = RXKADPACKETSHORT; if (skb_copy_bits(skb, sizeof(struct rxrpc_wire_header) + sizeof(*response), ticket, ticket_len) < 0) - goto protocol_error_free; ret = rxkad_decrypt_ticket(conn, server_key, skb, ticket, ticket_len, &session_key, &expiry, _abort_code); diff --git a/net/rxrpc/sysctl.c b/net/rxrpc/sysctl.c index 2bd987364e44..cde3224a5cd2 100644 --- a/net/rxrpc/sysctl.c +++ b/net/rxrpc/sysctl.c @@ -14,7 +14,7 @@ static struct ctl_table_header *rxrpc_sysctl_reg_table; static const unsigned int four = 4; static const unsigned int max_backlog = RXRPC_BACKLOG_MAX - 1; static const unsigned int n_65535 = 65535; -static const unsigned int n_max_acks = RXRPC_RXTX_BUFF_SIZE - 1; +static const unsigned int n_max_acks = 255; static const unsigned long one_jiffy = 1; static const unsigned long max_jiffies = MAX_JIFFY_OFFSET; From a4ea4c47761943d90cd5d1688b3c3c65922ff2b1 Mon Sep 17 00:00:00 2001 From: David Howells Date: Thu, 31 Mar 2022 23:55:08 +0100 Subject: [PATCH 0736/2002] rxrpc: Don't use a ring buffer for call Tx queue Change the way the Tx queueing works to make the following ends easier to achieve: (1) The filling of packets, the encryption of packets and the transmission of packets can be handled in parallel by separate threads, rather than rxrpc_sendmsg() allocating, filling, encrypting and transmitting each packet before moving onto the next one. (2) Get rid of the fixed-size ring which sets a hard limit on the number of packets that can be retained in the ring. This allows the number of packets to increase without having to allocate a very large ring or having variable-sized rings. [Note: the downside of this is that it's then less efficient to locate a packet for retransmission as we then have to step through a list and examine each buffer in the list.] (3) Allow the filler/encrypter to run ahead of the transmission window. (4) Make it easier to do zero copy UDP from the packet buffers. (5) Make it easier to do zero copy from userspace to the packet buffers - and thence to UDP (only if for unauthenticated connections). To that end, the following changes are made: (1) Use the new rxrpc_txbuf struct instead of sk_buff for keeping packets to be transmitted in. This allows them to be placed on multiple queues simultaneously. An sk_buff isn't really necessary as it's never passed on to lower-level networking code. (2) Keep the transmissable packets in a linked list on the call struct rather than in a ring. As a consequence, the annotation buffer isn't used either; rather a flag is set on the packet to indicate ackedness. (3) Use the RXRPC_CALL_TX_LAST flag to indicate that the last packet to be transmitted has been queued. Add RXRPC_CALL_TX_ALL_ACKED to indicate that all packets up to and including the last got hard acked. (4) Wire headers are now stored in the txbuf rather than being concocted on the stack and they're stored immediately before the data, thereby allowing zerocopy of a single span. (5) Don't bother with instant-resend on transmission failure; rather, leave it for a timer or an ACK packet to trigger. Signed-off-by: David Howells cc: Marc Dionne cc: linux-afs@lists.infradead.org --- include/trace/events/rxrpc.h | 78 ++++++++------- net/rxrpc/af_rxrpc.c | 5 +- net/rxrpc/ar-internal.h | 31 +++--- net/rxrpc/call_event.c | 111 +++++++-------------- net/rxrpc/call_object.c | 15 ++- net/rxrpc/input.c | 145 ++++++++++++--------------- net/rxrpc/insecure.c | 3 +- net/rxrpc/output.c | 89 +++++++---------- net/rxrpc/proc.c | 9 +- net/rxrpc/rxkad.c | 102 +++++++------------ net/rxrpc/sendmsg.c | 186 +++++++++++++---------------------- net/rxrpc/txbuf.c | 34 +++++++ 12 files changed, 348 insertions(+), 460 deletions(-) diff --git a/include/trace/events/rxrpc.h b/include/trace/events/rxrpc.h index 284a1560b0a8..71ca74e40ec8 100644 --- a/include/trace/events/rxrpc.h +++ b/include/trace/events/rxrpc.h @@ -75,6 +75,7 @@ EM(rxrpc_call_got, "GOT") \ EM(rxrpc_call_got_kernel, "Gke") \ EM(rxrpc_call_got_timer, "GTM") \ + EM(rxrpc_call_got_tx, "Gtx") \ EM(rxrpc_call_got_userid, "Gus") \ EM(rxrpc_call_new_client, "NWc") \ EM(rxrpc_call_new_service, "NWs") \ @@ -83,20 +84,22 @@ EM(rxrpc_call_put_noqueue, "PnQ") \ EM(rxrpc_call_put_notimer, "PnT") \ EM(rxrpc_call_put_timer, "PTM") \ + EM(rxrpc_call_put_tx, "Ptx") \ EM(rxrpc_call_put_userid, "Pus") \ EM(rxrpc_call_queued, "QUE") \ EM(rxrpc_call_queued_ref, "QUR") \ EM(rxrpc_call_release, "RLS") \ E_(rxrpc_call_seen, "SEE") -#define rxrpc_transmit_traces \ - EM(rxrpc_transmit_await_reply, "AWR") \ - EM(rxrpc_transmit_end, "END") \ - EM(rxrpc_transmit_queue, "QUE") \ - EM(rxrpc_transmit_queue_last, "QLS") \ - EM(rxrpc_transmit_rotate, "ROT") \ - EM(rxrpc_transmit_rotate_last, "RLS") \ - E_(rxrpc_transmit_wait, "WAI") +#define rxrpc_txqueue_traces \ + EM(rxrpc_txqueue_await_reply, "AWR") \ + EM(rxrpc_txqueue_dequeue, "DEQ") \ + EM(rxrpc_txqueue_end, "END") \ + EM(rxrpc_txqueue_queue, "QUE") \ + EM(rxrpc_txqueue_queue_last, "QLS") \ + EM(rxrpc_txqueue_rotate, "ROT") \ + EM(rxrpc_txqueue_rotate_last, "RLS") \ + E_(rxrpc_txqueue_wait, "WAI") #define rxrpc_receive_traces \ EM(rxrpc_receive_end, "END") \ @@ -259,6 +262,7 @@ EM(rxrpc_txbuf_alloc_ack, "ALLOC ACK ") \ EM(rxrpc_txbuf_alloc_data, "ALLOC DATA ") \ EM(rxrpc_txbuf_free, "FREE ") \ + EM(rxrpc_txbuf_get_buffer, "GET BUFFER ") \ EM(rxrpc_txbuf_get_trans, "GET TRANS ") \ EM(rxrpc_txbuf_get_retrans, "GET RETRANS") \ EM(rxrpc_txbuf_put_ack_tx, "PUT ACK TX ") \ @@ -266,6 +270,7 @@ EM(rxrpc_txbuf_put_nomem, "PUT NOMEM ") \ EM(rxrpc_txbuf_put_rotated, "PUT ROTATED") \ EM(rxrpc_txbuf_put_send_aborted, "PUT SEND-X ") \ + EM(rxrpc_txbuf_put_trans, "PUT TRANS ") \ EM(rxrpc_txbuf_see_send_more, "SEE SEND+ ") \ E_(rxrpc_txbuf_see_unacked, "SEE UNACKED") @@ -295,9 +300,9 @@ enum rxrpc_rtt_rx_trace { rxrpc_rtt_rx_traces } __mode(byte); enum rxrpc_rtt_tx_trace { rxrpc_rtt_tx_traces } __mode(byte); enum rxrpc_skb_trace { rxrpc_skb_traces } __mode(byte); enum rxrpc_timer_trace { rxrpc_timer_traces } __mode(byte); -enum rxrpc_transmit_trace { rxrpc_transmit_traces } __mode(byte); enum rxrpc_tx_point { rxrpc_tx_points } __mode(byte); enum rxrpc_txbuf_trace { rxrpc_txbuf_traces } __mode(byte); +enum rxrpc_txqueue_trace { rxrpc_txqueue_traces } __mode(byte); #endif /* end __RXRPC_DECLARE_TRACE_ENUMS_ONCE_ONLY */ @@ -323,9 +328,9 @@ rxrpc_rtt_rx_traces; rxrpc_rtt_tx_traces; rxrpc_skb_traces; rxrpc_timer_traces; -rxrpc_transmit_traces; rxrpc_tx_points; rxrpc_txbuf_traces; +rxrpc_txqueue_traces; /* * Now redefine the EM() and E_() macros to map the enums to the strings that @@ -605,15 +610,16 @@ TRACE_EVENT(rxrpc_call_complete, __entry->abort_code) ); -TRACE_EVENT(rxrpc_transmit, - TP_PROTO(struct rxrpc_call *call, enum rxrpc_transmit_trace why), +TRACE_EVENT(rxrpc_txqueue, + TP_PROTO(struct rxrpc_call *call, enum rxrpc_txqueue_trace why), TP_ARGS(call, why), TP_STRUCT__entry( __field(unsigned int, call ) - __field(enum rxrpc_transmit_trace, why ) - __field(rxrpc_seq_t, tx_hard_ack ) + __field(enum rxrpc_txqueue_trace, why ) + __field(rxrpc_seq_t, acks_hard_ack ) + __field(rxrpc_seq_t, tx_bottom ) __field(rxrpc_seq_t, tx_top ) __field(int, tx_winsize ) ), @@ -621,16 +627,19 @@ TRACE_EVENT(rxrpc_transmit, TP_fast_assign( __entry->call = call->debug_id; __entry->why = why; - __entry->tx_hard_ack = call->tx_hard_ack; + __entry->acks_hard_ack = call->acks_hard_ack; + __entry->tx_bottom = call->tx_bottom; __entry->tx_top = call->tx_top; __entry->tx_winsize = call->tx_winsize; ), - TP_printk("c=%08x %s f=%08x n=%u/%u", + TP_printk("c=%08x %s f=%08x h=%08x n=%u/%u/%u", __entry->call, - __print_symbolic(__entry->why, rxrpc_transmit_traces), - __entry->tx_hard_ack + 1, - __entry->tx_top - __entry->tx_hard_ack, + __print_symbolic(__entry->why, rxrpc_txqueue_traces), + __entry->tx_bottom, + __entry->acks_hard_ack, + __entry->tx_top - __entry->tx_bottom, + __entry->tx_top - __entry->acks_hard_ack, __entry->tx_winsize) ); @@ -1200,29 +1209,25 @@ TRACE_EVENT(rxrpc_drop_ack, ); TRACE_EVENT(rxrpc_retransmit, - TP_PROTO(struct rxrpc_call *call, rxrpc_seq_t seq, u8 annotation, - s64 expiry), + TP_PROTO(struct rxrpc_call *call, rxrpc_seq_t seq, s64 expiry), - TP_ARGS(call, seq, annotation, expiry), + TP_ARGS(call, seq, expiry), TP_STRUCT__entry( __field(unsigned int, call ) __field(rxrpc_seq_t, seq ) - __field(u8, annotation ) __field(s64, expiry ) ), TP_fast_assign( __entry->call = call->debug_id; __entry->seq = seq; - __entry->annotation = annotation; __entry->expiry = expiry; ), - TP_printk("c=%08x q=%x a=%02x xp=%lld", + TP_printk("c=%08x q=%x xp=%lld", __entry->call, __entry->seq, - __entry->annotation, __entry->expiry) ); @@ -1245,14 +1250,14 @@ TRACE_EVENT(rxrpc_congest, TP_fast_assign( __entry->call = call->debug_id; __entry->change = change; - __entry->hard_ack = call->tx_hard_ack; + __entry->hard_ack = call->acks_hard_ack; __entry->top = call->tx_top; __entry->lowest_nak = call->acks_lowest_nak; __entry->ack_serial = ack_serial; memcpy(&__entry->sum, summary, sizeof(__entry->sum)); ), - TP_printk("c=%08x r=%08x %s q=%08x %s cw=%u ss=%u nr=%u,%u nw=%u,%u r=%u b=%u u=%u d=%u l=%x%s%s%s", + TP_printk("c=%08x r=%08x %s q=%08x %s cw=%u ss=%u nA=%u,%u+%u,%u r=%u b=%u u=%u d=%u l=%x%s%s%s", __entry->call, __entry->ack_serial, __print_symbolic(__entry->sum.ack_reason, rxrpc_ack_names), @@ -1362,26 +1367,23 @@ TRACE_EVENT(rxrpc_connect_call, ); TRACE_EVENT(rxrpc_resend, - TP_PROTO(struct rxrpc_call *call, int ix), + TP_PROTO(struct rxrpc_call *call), - TP_ARGS(call, ix), + TP_ARGS(call), TP_STRUCT__entry( __field(unsigned int, call ) - __field(int, ix ) - __array(u8, anno, 64 ) + __field(rxrpc_seq_t, seq ) ), TP_fast_assign( __entry->call = call->debug_id; - __entry->ix = ix; - memcpy(__entry->anno, call->rxtx_annotations, 64); + __entry->seq = call->acks_hard_ack; ), - TP_printk("c=%08x ix=%u a=%64phN", + TP_printk("c=%08x q=%x", __entry->call, - __entry->ix, - __entry->anno) + __entry->seq) ); TRACE_EVENT(rxrpc_rx_icmp, @@ -1461,7 +1463,7 @@ TRACE_EVENT(rxrpc_call_reset, __entry->call_id = call->call_id; __entry->call_serial = call->rx_serial; __entry->conn_serial = call->conn->hi_serial; - __entry->tx_seq = call->tx_hard_ack; + __entry->tx_seq = call->acks_hard_ack; __entry->rx_seq = call->rx_highest_seq; ), diff --git a/net/rxrpc/af_rxrpc.c b/net/rxrpc/af_rxrpc.c index ceba28e9dce6..2f3991cf8715 100644 --- a/net/rxrpc/af_rxrpc.c +++ b/net/rxrpc/af_rxrpc.c @@ -39,7 +39,7 @@ atomic_t rxrpc_debug_id; EXPORT_SYMBOL(rxrpc_debug_id); /* count of skbs currently in use */ -atomic_t rxrpc_n_tx_skbs, rxrpc_n_rx_skbs; +atomic_t rxrpc_n_rx_skbs; struct workqueue_struct *rxrpc_workqueue; @@ -979,7 +979,7 @@ static int __init af_rxrpc_init(void) goto error_call_jar; } - rxrpc_workqueue = alloc_workqueue("krxrpcd", 0, 1); + rxrpc_workqueue = alloc_workqueue("krxrpcd", WQ_HIGHPRI | WQ_MEM_RECLAIM | WQ_UNBOUND, 1); if (!rxrpc_workqueue) { pr_notice("Failed to allocate work queue\n"); goto error_work_queue; @@ -1059,7 +1059,6 @@ static void __exit af_rxrpc_exit(void) sock_unregister(PF_RXRPC); proto_unregister(&rxrpc_proto); unregister_pernet_device(&rxrpc_net_ops); - ASSERTCMP(atomic_read(&rxrpc_n_tx_skbs), ==, 0); ASSERTCMP(atomic_read(&rxrpc_n_rx_skbs), ==, 0); /* Make sure the local and peer records pinned by any dying connections diff --git a/net/rxrpc/ar-internal.h b/net/rxrpc/ar-internal.h index c6c5bb3d3688..d7aebe82e17c 100644 --- a/net/rxrpc/ar-internal.h +++ b/net/rxrpc/ar-internal.h @@ -195,7 +195,6 @@ struct rxrpc_host_header { * - max 48 bytes (struct sk_buff::cb) */ struct rxrpc_skb_priv { - u16 remain; u16 offset; /* Offset of data */ u16 len; /* Length of data */ u8 flags; @@ -243,7 +242,7 @@ struct rxrpc_security { size_t *, size_t *, size_t *); /* impose security on a packet */ - int (*secure_packet)(struct rxrpc_call *, struct sk_buff *, size_t); + int (*secure_packet)(struct rxrpc_call *, struct rxrpc_txbuf *); /* verify the security on a received packet */ int (*verify_packet)(struct rxrpc_call *, struct sk_buff *); @@ -497,6 +496,7 @@ enum rxrpc_call_flag { RXRPC_CALL_EXPOSED, /* The call was exposed to the world */ RXRPC_CALL_RX_LAST, /* Received the last packet (at rxtx_top) */ RXRPC_CALL_TX_LAST, /* Last packet in Tx buffer (at rxtx_top) */ + RXRPC_CALL_TX_ALL_ACKED, /* Last packet has been hard-acked */ RXRPC_CALL_SEND_PING, /* A ping will need to be sent */ RXRPC_CALL_RETRANS_TIMEOUT, /* Retransmission due to timeout occurred */ RXRPC_CALL_BEGAN_RX_TIMER, /* We began the expect_rx_by timer */ @@ -594,7 +594,7 @@ struct rxrpc_call { struct list_head recvmsg_link; /* Link in rx->recvmsg_q */ struct list_head sock_link; /* Link in rx->sock_calls */ struct rb_node sock_node; /* Node in rx->calls */ - struct sk_buff *tx_pending; /* Tx socket buffer being filled */ + struct rxrpc_txbuf *tx_pending; /* Tx buffer being filled */ wait_queue_head_t waitq; /* Wait queue for channel or Tx */ s64 tx_total_len; /* Total length left to be transmitted (or -1) */ __be32 crypto_buf[2]; /* Temporary packet crypto buffer */ @@ -632,22 +632,16 @@ struct rxrpc_call { #define RXRPC_INIT_RX_WINDOW_SIZE 63 struct sk_buff **rxtx_buffer; u8 *rxtx_annotations; -#define RXRPC_TX_ANNO_ACK 0 -#define RXRPC_TX_ANNO_UNACK 1 -#define RXRPC_TX_ANNO_NAK 2 -#define RXRPC_TX_ANNO_RETRANS 3 -#define RXRPC_TX_ANNO_MASK 0x03 -#define RXRPC_TX_ANNO_LAST 0x04 -#define RXRPC_TX_ANNO_RESENT 0x08 - - rxrpc_seq_t tx_hard_ack; /* Dead slot in buffer; the first transmitted but - * not hard-ACK'd packet follows this. - */ /* Transmitted data tracking. */ + spinlock_t tx_lock; /* Transmit queue lock */ + struct list_head tx_buffer; /* Buffer of transmissible packets */ + rxrpc_seq_t tx_bottom; /* First packet in buffer */ + rxrpc_seq_t tx_transmitted; /* Highest packet transmitted */ rxrpc_seq_t tx_top; /* Highest Tx slot allocated. */ u16 tx_backoff; /* Delay to insert due to Tx failure */ u8 tx_winsize; /* Maximum size of Tx window */ +#define RXRPC_TX_MAX_WINDOW 128 /* Received data tracking */ struct sk_buff_head recvmsg_queue; /* Queue of packets ready for recvmsg() */ @@ -657,6 +651,7 @@ struct rxrpc_call { rxrpc_seq_t rx_consumed; /* Highest packet consumed */ rxrpc_serial_t rx_serial; /* Highest serial received for this call */ u8 rx_winsize; /* Size of Rx window */ + spinlock_t input_lock; /* Lock for packet input to this call */ /* TCP-style slow-start congestion control [RFC5681]. Since the SMSS * is fixed, we keep these numbers in terms of segments (ie. DATA @@ -671,8 +666,6 @@ struct rxrpc_call { u8 cong_cumul_acks; /* Cumulative ACK count */ ktime_t cong_tstamp; /* Last time cwnd was changed */ - spinlock_t input_lock; /* Lock for packet input to this call */ - /* Receive-phase ACK management (ACKs we send). */ u8 ackr_reason; /* reason to ACK */ rxrpc_serial_t ackr_serial; /* serial of packet being ACK'd */ @@ -697,6 +690,7 @@ struct rxrpc_call { ktime_t acks_latest_ts; /* Timestamp of latest ACK received */ rxrpc_seq_t acks_first_seq; /* first sequence number received */ rxrpc_seq_t acks_prev_seq; /* Highest previousPacket received */ + rxrpc_seq_t acks_hard_ack; /* Latest hard-ack point */ rxrpc_seq_t acks_lowest_nak; /* Lowest NACK in the buffer (or ==tx_hard_ack) */ rxrpc_seq_t acks_lost_top; /* tx_top at the time lost-ack ping sent */ rxrpc_serial_t acks_lost_ping; /* Serial number of probe ACK */ @@ -809,7 +803,7 @@ static inline bool rxrpc_sending_to_client(const struct rxrpc_txbuf *txb) /* * af_rxrpc.c */ -extern atomic_t rxrpc_n_tx_skbs, rxrpc_n_rx_skbs; +extern atomic_t rxrpc_n_rx_skbs; extern struct workqueue_struct *rxrpc_workqueue; /* @@ -831,6 +825,7 @@ void rxrpc_propose_ping(struct rxrpc_call *call, u32 serial, void rxrpc_send_ACK(struct rxrpc_call *, u8, rxrpc_serial_t, enum rxrpc_propose_ack_trace); void rxrpc_propose_delay_ACK(struct rxrpc_call *, rxrpc_serial_t, enum rxrpc_propose_ack_trace); +void rxrpc_shrink_call_tx_buffer(struct rxrpc_call *); void rxrpc_process_call(struct work_struct *); void rxrpc_reduce_call_timer(struct rxrpc_call *call, @@ -1034,7 +1029,7 @@ static inline struct rxrpc_net *rxrpc_net(struct net *net) */ void rxrpc_transmit_ack_packets(struct rxrpc_local *); int rxrpc_send_abort_packet(struct rxrpc_call *); -int rxrpc_send_data_packet(struct rxrpc_call *, struct sk_buff *, bool); +int rxrpc_send_data_packet(struct rxrpc_call *, struct rxrpc_txbuf *); void rxrpc_reject_packets(struct rxrpc_local *); void rxrpc_send_keepalive(struct rxrpc_peer *); diff --git a/net/rxrpc/call_event.c b/net/rxrpc/call_event.c index 36f60ac1d95d..3c37b280eb20 100644 --- a/net/rxrpc/call_event.c +++ b/net/rxrpc/call_event.c @@ -148,62 +148,52 @@ static void rxrpc_congestion_timeout(struct rxrpc_call *call) */ static void rxrpc_resend(struct rxrpc_call *call, unsigned long now_j) { - struct sk_buff *skb; + struct rxrpc_txbuf *txb; unsigned long resend_at; - rxrpc_seq_t cursor, seq, top; + rxrpc_seq_t transmitted = READ_ONCE(call->tx_transmitted); ktime_t now, max_age, oldest, ack_ts; - int ix; - u8 annotation, anno_type, retrans = 0, unacked = 0; + bool unacked = false; + LIST_HEAD(retrans_queue); - _enter("{%d,%d}", call->tx_hard_ack, call->tx_top); + _enter("{%d,%d}", call->acks_hard_ack, call->tx_top); now = ktime_get_real(); max_age = ktime_sub_us(now, jiffies_to_usecs(call->peer->rto_j)); - spin_lock_bh(&call->lock); - - cursor = call->tx_hard_ack; - top = call->tx_top; - ASSERT(before_eq(cursor, top)); - if (cursor == top) - goto out_unlock; + spin_lock(&call->tx_lock); /* Scan the packet list without dropping the lock and decide which of * the packets in the Tx buffer we're going to resend and what the new * resend timeout will be. */ - trace_rxrpc_resend(call, (cursor + 1) & RXRPC_RXTX_BUFF_MASK); + trace_rxrpc_resend(call); oldest = now; - for (seq = cursor + 1; before_eq(seq, top); seq++) { - ix = seq & RXRPC_RXTX_BUFF_MASK; - annotation = call->rxtx_annotations[ix]; - anno_type = annotation & RXRPC_TX_ANNO_MASK; - annotation &= ~RXRPC_TX_ANNO_MASK; - if (anno_type == RXRPC_TX_ANNO_ACK) + list_for_each_entry(txb, &call->tx_buffer, call_link) { + if (test_bit(RXRPC_TXBUF_ACKED, &txb->flags)) continue; + if (after(txb->seq, transmitted)) + break; - skb = call->rxtx_buffer[ix]; - rxrpc_see_skb(skb, rxrpc_skb_seen); + rxrpc_see_txbuf(txb, rxrpc_txbuf_see_unacked); - if (anno_type == RXRPC_TX_ANNO_UNACK) { - if (ktime_after(skb->tstamp, max_age)) { - if (ktime_before(skb->tstamp, oldest)) - oldest = skb->tstamp; + if (test_bit(RXRPC_TXBUF_RESENT, &txb->flags)) { + if (ktime_after(txb->last_sent, max_age)) { + if (ktime_before(txb->last_sent, oldest)) + oldest = txb->last_sent; continue; } - if (!(annotation & RXRPC_TX_ANNO_RESENT)) - unacked++; + unacked = true; } - /* Okay, we need to retransmit a packet. */ - call->rxtx_annotations[ix] = RXRPC_TX_ANNO_RETRANS | annotation; - retrans++; - trace_rxrpc_retransmit(call, seq, annotation | anno_type, - ktime_to_ns(ktime_sub(skb->tstamp, max_age))); + rxrpc_get_txbuf(txb, rxrpc_txbuf_get_retrans); + list_move_tail(&txb->tx_link, &retrans_queue); } + spin_unlock(&call->tx_lock); + resend_at = nsecs_to_jiffies(ktime_to_ns(ktime_sub(now, oldest))); - resend_at += jiffies + rxrpc_get_rto_backoff(call->peer, retrans); + resend_at += jiffies + rxrpc_get_rto_backoff(call->peer, + !list_empty(&retrans_queue)); WRITE_ONCE(call->resend_at, resend_at); if (unacked) @@ -213,7 +203,8 @@ static void rxrpc_resend(struct rxrpc_call *call, unsigned long now_j) * that an ACK got lost somewhere. Send a ping to find out instead of * retransmitting data. */ - if (!retrans) { + if (list_empty(&retrans_queue)) { + spin_lock_bh(&call->lock); rxrpc_reduce_call_timer(call, resend_at, now_j, rxrpc_timer_set_for_resend); spin_unlock_bh(&call->lock); @@ -225,50 +216,19 @@ static void rxrpc_resend(struct rxrpc_call *call, unsigned long now_j) goto out; } - /* Now go through the Tx window and perform the retransmissions. We - * have to drop the lock for each send. If an ACK comes in whilst the - * lock is dropped, it may clear some of the retransmission markers for - * packets that it soft-ACKs. - */ - for (seq = cursor + 1; before_eq(seq, top); seq++) { - ix = seq & RXRPC_RXTX_BUFF_MASK; - annotation = call->rxtx_annotations[ix]; - anno_type = annotation & RXRPC_TX_ANNO_MASK; - if (anno_type != RXRPC_TX_ANNO_RETRANS) - continue; - - /* We need to reset the retransmission state, but we need to do - * so before we drop the lock as a new ACK/NAK may come in and - * confuse things - */ - annotation &= ~RXRPC_TX_ANNO_MASK; - annotation |= RXRPC_TX_ANNO_UNACK | RXRPC_TX_ANNO_RESENT; - call->rxtx_annotations[ix] = annotation; - - skb = call->rxtx_buffer[ix]; - if (!skb) - continue; - - rxrpc_get_skb(skb, rxrpc_skb_got); - spin_unlock_bh(&call->lock); - + while ((txb = list_first_entry_or_null(&retrans_queue, + struct rxrpc_txbuf, tx_link))) { + list_del_init(&txb->tx_link); + set_bit(RXRPC_TXBUF_RESENT, &txb->flags); rxrpc_inc_stat(call->rxnet, stat_tx_data_retrans); - if (rxrpc_send_data_packet(call, skb, true) < 0) { - rxrpc_free_skb(skb, rxrpc_skb_freed); - return; - } + rxrpc_send_data_packet(call, txb); + rxrpc_put_txbuf(txb, rxrpc_txbuf_put_trans); - if (rxrpc_is_client_call(call)) - rxrpc_expose_client_call(call); - - rxrpc_free_skb(skb, rxrpc_skb_freed); - spin_lock_bh(&call->lock); - if (after(call->tx_hard_ack, seq)) - seq = call->tx_hard_ack; + trace_rxrpc_retransmit(call, txb->seq, + ktime_to_ns(ktime_sub(txb->last_sent, + max_age))); } -out_unlock: - spin_unlock_bh(&call->lock); out: _leave(""); } @@ -301,6 +261,9 @@ recheck_state: goto recheck_state; } + if (READ_ONCE(call->acks_hard_ack) != call->tx_bottom) + rxrpc_shrink_call_tx_buffer(call); + if (call->state == RXRPC_CALL_COMPLETE) { rxrpc_delete_call_timer(call); goto out_put; diff --git a/net/rxrpc/call_object.c b/net/rxrpc/call_object.c index 4d8601c6a32d..ed5f6f0e4286 100644 --- a/net/rxrpc/call_object.c +++ b/net/rxrpc/call_object.c @@ -155,11 +155,13 @@ struct rxrpc_call *rxrpc_alloc_call(struct rxrpc_sock *rx, gfp_t gfp, INIT_LIST_HEAD(&call->accept_link); INIT_LIST_HEAD(&call->recvmsg_link); INIT_LIST_HEAD(&call->sock_link); + INIT_LIST_HEAD(&call->tx_buffer); skb_queue_head_init(&call->recvmsg_queue); skb_queue_head_init(&call->rx_oos_queue); init_waitqueue_head(&call->waitq); spin_lock_init(&call->lock); spin_lock_init(&call->notify_lock); + spin_lock_init(&call->tx_lock); spin_lock_init(&call->input_lock); rwlock_init(&call->state_lock); refcount_set(&call->ref, 1); @@ -175,7 +177,7 @@ struct rxrpc_call *rxrpc_alloc_call(struct rxrpc_sock *rx, gfp_t gfp, call->tx_winsize = 16; call->cong_cwnd = 2; - call->cong_ssthresh = RXRPC_RXTX_BUFF_SIZE - 1; + call->cong_ssthresh = RXRPC_TX_MAX_WINDOW; call->rxnet = rxnet; call->rtt_avail = RXRPC_CALL_RTT_AVAIL_MASK; @@ -510,7 +512,7 @@ void rxrpc_get_call(struct rxrpc_call *call, enum rxrpc_call_trace op) } /* - * Clean up the RxTx skb ring. + * Clean up the Rx skb ring. */ static void rxrpc_cleanup_ring(struct rxrpc_call *call) { @@ -686,6 +688,8 @@ static void rxrpc_rcu_destroy_call(struct rcu_head *rcu) */ void rxrpc_cleanup_call(struct rxrpc_call *call) { + struct rxrpc_txbuf *txb; + _net("DESTROY CALL %d", call->debug_id); memset(&call->sock_node, 0xcd, sizeof(call->sock_node)); @@ -694,7 +698,12 @@ void rxrpc_cleanup_call(struct rxrpc_call *call) ASSERT(test_bit(RXRPC_CALL_RELEASED, &call->flags)); rxrpc_cleanup_ring(call); - rxrpc_free_skb(call->tx_pending, rxrpc_skb_cleaned); + while ((txb = list_first_entry_or_null(&call->tx_buffer, + struct rxrpc_txbuf, call_link))) { + list_del(&txb->call_link); + rxrpc_put_txbuf(txb, rxrpc_txbuf_put_cleaned); + } + rxrpc_put_txbuf(call->tx_pending, rxrpc_txbuf_put_cleaned); call_rcu(&call->rcu, rxrpc_rcu_destroy_call); } diff --git a/net/rxrpc/input.c b/net/rxrpc/input.c index 947e7196e2be..b1f7debd4f3e 100644 --- a/net/rxrpc/input.c +++ b/net/rxrpc/input.c @@ -32,7 +32,7 @@ static void rxrpc_congestion_management(struct rxrpc_call *call, bool resend = false; summary->flight_size = - (call->tx_top - call->tx_hard_ack) - summary->nr_acks; + (call->tx_top - call->acks_hard_ack) - summary->nr_acks; if (test_and_clear_bit(RXRPC_CALL_RETRANS_TIMEOUT, &call->flags)) { summary->retrans_timeo = true; @@ -150,8 +150,8 @@ resume_normality: out: cumulative_acks = 0; out_no_clear_ca: - if (cwnd >= RXRPC_RXTX_BUFF_SIZE - 1) - cwnd = RXRPC_RXTX_BUFF_SIZE - 1; + if (cwnd >= RXRPC_TX_MAX_WINDOW) + cwnd = RXRPC_TX_MAX_WINDOW; call->cong_cwnd = cwnd; call->cong_cumul_acks = cumulative_acks; trace_rxrpc_congest(call, summary, acked_serial, change); @@ -169,9 +169,8 @@ send_extra_data: /* Send some previously unsent DATA if we have some to advance the ACK * state. */ - if (call->rxtx_annotations[call->tx_top & RXRPC_RXTX_BUFF_MASK] & - RXRPC_TX_ANNO_LAST || - summary->nr_acks != call->tx_top - call->tx_hard_ack) { + if (test_bit(RXRPC_CALL_TX_LAST, &call->flags) || + summary->nr_acks != call->tx_top - call->acks_hard_ack) { call->cong_extra++; wake_up(&call->waitq); } @@ -184,53 +183,40 @@ send_extra_data: static bool rxrpc_rotate_tx_window(struct rxrpc_call *call, rxrpc_seq_t to, struct rxrpc_ack_summary *summary) { - struct sk_buff *skb, *list = NULL; + struct rxrpc_txbuf *txb; bool rot_last = false; - int ix; - u8 annotation; - if (call->acks_lowest_nak == call->tx_hard_ack) { + list_for_each_entry_rcu(txb, &call->tx_buffer, call_link, false) { + if (before_eq(txb->seq, call->acks_hard_ack)) + continue; + if (!test_bit(RXRPC_TXBUF_ACKED, &txb->flags)) + summary->nr_rot_new_acks++; + if (test_bit(RXRPC_TXBUF_LAST, &txb->flags)) { + set_bit(RXRPC_CALL_TX_LAST, &call->flags); + rot_last = true; + } + if (txb->seq == to) + break; + } + + if (rot_last) + set_bit(RXRPC_CALL_TX_ALL_ACKED, &call->flags); + + _enter("%x,%x,%x,%d", to, call->acks_hard_ack, call->tx_top, rot_last); + + if (call->acks_lowest_nak == call->acks_hard_ack) { call->acks_lowest_nak = to; } else if (before_eq(call->acks_lowest_nak, to)) { summary->new_low_nack = true; call->acks_lowest_nak = to; } - spin_lock(&call->lock); + smp_store_release(&call->acks_hard_ack, to); - while (before(call->tx_hard_ack, to)) { - call->tx_hard_ack++; - ix = call->tx_hard_ack & RXRPC_RXTX_BUFF_MASK; - skb = call->rxtx_buffer[ix]; - annotation = call->rxtx_annotations[ix]; - rxrpc_see_skb(skb, rxrpc_skb_rotated); - call->rxtx_buffer[ix] = NULL; - call->rxtx_annotations[ix] = 0; - skb->next = list; - list = skb; - - if (annotation & RXRPC_TX_ANNO_LAST) { - set_bit(RXRPC_CALL_TX_LAST, &call->flags); - rot_last = true; - } - if ((annotation & RXRPC_TX_ANNO_MASK) != RXRPC_TX_ANNO_ACK) - summary->nr_rot_new_acks++; - } - - spin_unlock(&call->lock); - - trace_rxrpc_transmit(call, (rot_last ? - rxrpc_transmit_rotate_last : - rxrpc_transmit_rotate)); + trace_rxrpc_txqueue(call, (rot_last ? + rxrpc_txqueue_rotate_last : + rxrpc_txqueue_rotate)); wake_up(&call->waitq); - - while (list) { - skb = list; - list = skb->next; - skb_mark_not_on_list(skb); - rxrpc_free_skb(skb, rxrpc_skb_freed); - } - return rot_last; } @@ -270,9 +256,9 @@ static bool rxrpc_end_tx_phase(struct rxrpc_call *call, bool reply_begun, write_unlock(&call->state_lock); if (state == RXRPC_CALL_CLIENT_AWAIT_REPLY) - trace_rxrpc_transmit(call, rxrpc_transmit_await_reply); + trace_rxrpc_txqueue(call, rxrpc_txqueue_await_reply); else - trace_rxrpc_transmit(call, rxrpc_transmit_end); + trace_rxrpc_txqueue(call, rxrpc_txqueue_end); _leave(" = ok"); return true; @@ -678,30 +664,21 @@ static void rxrpc_complete_rtt_probe(struct rxrpc_call *call, */ static void rxrpc_input_check_for_lost_ack(struct rxrpc_call *call) { - rxrpc_seq_t top, bottom, seq; + struct rxrpc_txbuf *txb; + rxrpc_seq_t top, bottom; bool resend = false; - spin_lock_bh(&call->lock); - - bottom = call->tx_hard_ack + 1; - top = call->acks_lost_top; + bottom = READ_ONCE(call->acks_hard_ack) + 1; + top = READ_ONCE(call->acks_lost_top); if (before(bottom, top)) { - for (seq = bottom; before_eq(seq, top); seq++) { - int ix = seq & RXRPC_RXTX_BUFF_MASK; - u8 annotation = call->rxtx_annotations[ix]; - u8 anno_type = annotation & RXRPC_TX_ANNO_MASK; - - if (anno_type != RXRPC_TX_ANNO_UNACK) + list_for_each_entry_rcu(txb, &call->tx_buffer, call_link, false) { + if (test_bit(RXRPC_TXBUF_ACKED, &txb->flags)) continue; - annotation &= ~RXRPC_TX_ANNO_MASK; - annotation |= RXRPC_TX_ANNO_RETRANS; - call->rxtx_annotations[ix] = annotation; + set_bit(RXRPC_TXBUF_RETRANS, &txb->flags); resend = true; } } - spin_unlock_bh(&call->lock); - if (resend && !test_and_set_bit(RXRPC_CALL_EV_RESEND, &call->events)) rxrpc_queue_call(call); } @@ -735,8 +712,8 @@ static void rxrpc_input_ackinfo(struct rxrpc_call *call, struct sk_buff *skb, ntohl(ackinfo->rxMTU), ntohl(ackinfo->maxMTU), rwind, ntohl(ackinfo->jumbo_max)); - if (rwind > RXRPC_RXTX_BUFF_SIZE - 1) - rwind = RXRPC_RXTX_BUFF_SIZE - 1; + if (rwind > RXRPC_TX_MAX_WINDOW) + rwind = RXRPC_TX_MAX_WINDOW; if (call->tx_winsize != rwind) { if (rwind > call->tx_winsize) wake = true; @@ -775,22 +752,24 @@ static void rxrpc_input_soft_acks(struct rxrpc_call *call, u8 *acks, rxrpc_seq_t seq, int nr_acks, struct rxrpc_ack_summary *summary) { - int ix; - u8 annotation, anno_type; + struct rxrpc_txbuf *txb; - for (; nr_acks > 0; nr_acks--, seq++) { - ix = seq & RXRPC_RXTX_BUFF_MASK; - annotation = call->rxtx_annotations[ix]; - anno_type = annotation & RXRPC_TX_ANNO_MASK; - annotation &= ~RXRPC_TX_ANNO_MASK; - switch (*acks++) { + list_for_each_entry_rcu(txb, &call->tx_buffer, call_link, false) { + if (before(txb->seq, seq)) + continue; + if (after_eq(txb->seq, seq + nr_acks)) + break; + switch (acks[txb->seq - seq]) { case RXRPC_ACK_TYPE_ACK: summary->nr_acks++; - if (anno_type == RXRPC_TX_ANNO_ACK) + if (test_bit(RXRPC_TXBUF_ACKED, &txb->flags)) continue; + /* A lot of the time the packet is going to + * have been ACK.'d already. + */ + clear_bit(RXRPC_TXBUF_NACKED, &txb->flags); + set_bit(RXRPC_TXBUF_ACKED, &txb->flags); summary->nr_new_acks++; - call->rxtx_annotations[ix] = - RXRPC_TX_ANNO_ACK | annotation; break; case RXRPC_ACK_TYPE_NACK: if (!summary->nr_nacks && @@ -799,13 +778,12 @@ static void rxrpc_input_soft_acks(struct rxrpc_call *call, u8 *acks, summary->new_low_nack = true; } summary->nr_nacks++; - if (anno_type == RXRPC_TX_ANNO_NAK) + if (test_bit(RXRPC_TXBUF_NACKED, &txb->flags)) continue; summary->nr_new_nacks++; - if (anno_type == RXRPC_TX_ANNO_RETRANS) - continue; - call->rxtx_annotations[ix] = - RXRPC_TX_ANNO_NAK | annotation; + clear_bit(RXRPC_TXBUF_ACKED, &txb->flags); + set_bit(RXRPC_TXBUF_NACKED, &txb->flags); + set_bit(RXRPC_TXBUF_RETRANS, &txb->flags); break; default: return rxrpc_proto_abort("SFT", call, 0); @@ -930,7 +908,7 @@ static void rxrpc_input_ack(struct rxrpc_call *call, struct sk_buff *skb) if (unlikely(buf.ack.reason == RXRPC_ACK_OUT_OF_SEQUENCE) && first_soft_ack == 1 && prev_pkt == 0 && - call->tx_hard_ack == 0 && + call->acks_hard_ack == 0 && rxrpc_is_client_call(call)) { rxrpc_set_call_completion(call, RXRPC_CALL_REMOTELY_ABORTED, 0, -ENETRESET); @@ -989,7 +967,7 @@ static void rxrpc_input_ack(struct rxrpc_call *call, struct sk_buff *skb) goto out; } - if (before(hard_ack, call->tx_hard_ack) || + if (before(hard_ack, call->acks_hard_ack) || after(hard_ack, call->tx_top)) { rxrpc_proto_abort("AKW", call, 0); goto out; @@ -999,7 +977,7 @@ static void rxrpc_input_ack(struct rxrpc_call *call, struct sk_buff *skb) goto out; } - if (after(hard_ack, call->tx_hard_ack)) { + if (after(hard_ack, call->acks_hard_ack)) { if (rxrpc_rotate_tx_window(call, hard_ack, &summary)) { rxrpc_end_tx_phase(call, false, "ETA"); goto out; @@ -1015,8 +993,7 @@ static void rxrpc_input_ack(struct rxrpc_call *call, struct sk_buff *skb) &summary); } - if (call->rxtx_annotations[call->tx_top & RXRPC_RXTX_BUFF_MASK] & - RXRPC_TX_ANNO_LAST && + if (test_bit(RXRPC_CALL_TX_LAST, &call->flags) && summary.nr_acks == call->tx_top - hard_ack && rxrpc_is_client_call(call)) rxrpc_propose_ping(call, ack_serial, diff --git a/net/rxrpc/insecure.c b/net/rxrpc/insecure.c index fd68f0e3af27..0eb8471bfc53 100644 --- a/net/rxrpc/insecure.c +++ b/net/rxrpc/insecure.c @@ -25,8 +25,7 @@ static int none_how_much_data(struct rxrpc_call *call, size_t remain, return 0; } -static int none_secure_packet(struct rxrpc_call *call, struct sk_buff *skb, - size_t data_size) +static int none_secure_packet(struct rxrpc_call *call, struct rxrpc_txbuf *txb) { return 0; } diff --git a/net/rxrpc/output.c b/net/rxrpc/output.c index 0a4f37d7b6b5..e2f501cef040 100644 --- a/net/rxrpc/output.c +++ b/net/rxrpc/output.c @@ -335,7 +335,7 @@ int rxrpc_send_abort_packet(struct rxrpc_call *call) * channel instead, thereby closing off this call. */ if (rxrpc_is_client_call(call) && - test_bit(RXRPC_CALL_TX_LAST, &call->flags)) + test_bit(RXRPC_CALL_TX_ALL_ACKED, &call->flags)) return 0; if (test_bit(RXRPC_CALL_DISCONNECTED, &call->flags)) @@ -383,20 +383,17 @@ int rxrpc_send_abort_packet(struct rxrpc_call *call) /* * send a packet through the transport endpoint */ -int rxrpc_send_data_packet(struct rxrpc_call *call, struct sk_buff *skb, - bool retrans) +int rxrpc_send_data_packet(struct rxrpc_call *call, struct rxrpc_txbuf *txb) { enum rxrpc_req_ack_trace why; struct rxrpc_connection *conn = call->conn; - struct rxrpc_wire_header whdr; - struct rxrpc_skb_priv *sp = rxrpc_skb(skb); struct msghdr msg; - struct kvec iov[2]; + struct kvec iov[1]; rxrpc_serial_t serial; size_t len; int ret, rtt_slot = -1; - _enter(",{%d}", skb->len); + _enter("%x,{%d}", txb->seq, txb->len); if (hlist_unhashed(&call->error_link)) { spin_lock_bh(&call->peer->lock); @@ -406,29 +403,16 @@ int rxrpc_send_data_packet(struct rxrpc_call *call, struct sk_buff *skb, /* Each transmission of a Tx packet needs a new serial number */ serial = atomic_inc_return(&conn->serial); - - whdr.epoch = htonl(conn->proto.epoch); - whdr.cid = htonl(call->cid); - whdr.callNumber = htonl(call->call_id); - whdr.seq = htonl(sp->hdr.seq); - whdr.serial = htonl(serial); - whdr.type = RXRPC_PACKET_TYPE_DATA; - whdr.flags = sp->hdr.flags; - whdr.userStatus = 0; - whdr.securityIndex = call->security_ix; - whdr._rsvd = htons(sp->hdr._rsvd); - whdr.serviceId = htons(call->service_id); + txb->wire.serial = htonl(serial); if (test_bit(RXRPC_CONN_PROBING_FOR_UPGRADE, &conn->flags) && - sp->hdr.seq == 1) - whdr.userStatus = RXRPC_USERSTATUS_SERVICE_UPGRADE; + txb->seq == 1) + txb->wire.userStatus = RXRPC_USERSTATUS_SERVICE_UPGRADE; - iov[0].iov_base = &whdr; - iov[0].iov_len = sizeof(whdr); - iov[1].iov_base = skb->head; - iov[1].iov_len = skb->len; - len = iov[0].iov_len + iov[1].iov_len; - iov_iter_kvec(&msg.msg_iter, WRITE, iov, 2, len); + iov[0].iov_base = &txb->wire; + iov[0].iov_len = sizeof(txb->wire) + txb->len; + len = iov[0].iov_len; + iov_iter_kvec(&msg.msg_iter, WRITE, iov, 1, len); msg.msg_name = &call->peer->srx.transport; msg.msg_namelen = call->peer->srx.transport_len; @@ -443,19 +427,19 @@ int rxrpc_send_data_packet(struct rxrpc_call *call, struct sk_buff *skb, * service call, lest OpenAFS incorrectly send us an ACK with some * soft-ACKs in it and then never follow up with a proper hard ACK. */ - if (whdr.flags & RXRPC_REQUEST_ACK) + if (txb->wire.flags & RXRPC_REQUEST_ACK) why = rxrpc_reqack_already_on; - else if ((whdr.flags & RXRPC_LAST_PACKET) && rxrpc_to_client(sp)) + else if (test_bit(RXRPC_TXBUF_LAST, &txb->flags) && rxrpc_sending_to_client(txb)) why = rxrpc_reqack_no_srv_last; else if (test_and_clear_bit(RXRPC_CALL_EV_ACK_LOST, &call->events)) why = rxrpc_reqack_ack_lost; - else if (retrans) + else if (test_bit(RXRPC_TXBUF_RESENT, &txb->flags)) why = rxrpc_reqack_retrans; else if (call->cong_mode == RXRPC_CALL_SLOW_START && call->cong_cwnd <= 2) why = rxrpc_reqack_slow_start; else if (call->tx_winsize <= 2) why = rxrpc_reqack_small_txwin; - else if (call->peer->rtt_count < 3 && sp->hdr.seq & 1) + else if (call->peer->rtt_count < 3 && txb->seq & 1) why = rxrpc_reqack_more_rtt; else if (ktime_before(ktime_add_ms(call->peer->rtt_last_req, 1000), ktime_get_real())) why = rxrpc_reqack_old_rtt; @@ -463,35 +447,36 @@ int rxrpc_send_data_packet(struct rxrpc_call *call, struct sk_buff *skb, goto dont_set_request_ack; rxrpc_inc_stat(call->rxnet, stat_why_req_ack[why]); - trace_rxrpc_req_ack(call->debug_id, sp->hdr.seq, why); + trace_rxrpc_req_ack(call->debug_id, txb->seq, why); if (why != rxrpc_reqack_no_srv_last) - whdr.flags |= RXRPC_REQUEST_ACK; + txb->wire.flags |= RXRPC_REQUEST_ACK; dont_set_request_ack: if (IS_ENABLED(CONFIG_AF_RXRPC_INJECT_LOSS)) { static int lose; if ((lose++ & 7) == 7) { ret = 0; - trace_rxrpc_tx_data(call, sp->hdr.seq, serial, - whdr.flags, retrans, true); + trace_rxrpc_tx_data(call, txb->seq, serial, + txb->wire.flags, + test_bit(RXRPC_TXBUF_RESENT, &txb->flags), + true); goto done; } } - trace_rxrpc_tx_data(call, sp->hdr.seq, serial, whdr.flags, retrans, - false); + trace_rxrpc_tx_data(call, txb->seq, serial, txb->wire.flags, + test_bit(RXRPC_TXBUF_RESENT, &txb->flags), false); + cmpxchg(&call->tx_transmitted, txb->seq - 1, txb->seq); /* send the packet with the don't fragment bit set if we currently * think it's small enough */ - if (iov[1].iov_len >= call->peer->maxdata) + if (txb->len >= call->peer->maxdata) goto send_fragmentable; down_read(&conn->params.local->defrag_sem); - sp->hdr.serial = serial; - smp_wmb(); /* Set serial before timestamp */ - skb->tstamp = ktime_get_real(); - if (whdr.flags & RXRPC_REQUEST_ACK) + txb->last_sent = ktime_get_real(); + if (txb->wire.flags & RXRPC_REQUEST_ACK) rtt_slot = rxrpc_begin_rtt_probe(call, serial, rxrpc_rtt_tx_data); /* send the packet by UDP @@ -510,7 +495,7 @@ dont_set_request_ack: trace_rxrpc_tx_fail(call->debug_id, serial, ret, rxrpc_tx_point_call_data_nofrag); } else { - trace_rxrpc_tx_packet(call->debug_id, &whdr, + trace_rxrpc_tx_packet(call->debug_id, &txb->wire, rxrpc_tx_point_call_data_nofrag); } @@ -520,8 +505,8 @@ dont_set_request_ack: done: if (ret >= 0) { - if (whdr.flags & RXRPC_REQUEST_ACK) { - call->peer->rtt_last_req = skb->tstamp; + if (txb->wire.flags & RXRPC_REQUEST_ACK) { + call->peer->rtt_last_req = txb->last_sent; if (call->peer->rtt_count > 1) { unsigned long nowj = jiffies, ack_lost_at; @@ -533,7 +518,7 @@ done: } } - if (sp->hdr.seq == 1 && + if (txb->seq == 1 && !test_and_set_bit(RXRPC_CALL_BEGAN_RX_TIMER, &call->flags)) { unsigned long nowj = jiffies, expect_rx_by; @@ -565,23 +550,21 @@ send_fragmentable: down_write(&conn->params.local->defrag_sem); - sp->hdr.serial = serial; - smp_wmb(); /* Set serial before timestamp */ - skb->tstamp = ktime_get_real(); - if (whdr.flags & RXRPC_REQUEST_ACK) + txb->last_sent = ktime_get_real(); + if (txb->wire.flags & RXRPC_REQUEST_ACK) rtt_slot = rxrpc_begin_rtt_probe(call, serial, rxrpc_rtt_tx_data); switch (conn->params.local->srx.transport.family) { case AF_INET6: case AF_INET: ip_sock_set_mtu_discover(conn->params.local->socket->sk, - IP_PMTUDISC_DONT); + IP_PMTUDISC_DONT); rxrpc_inc_stat(call->rxnet, stat_tx_data_send_frag); ret = do_udp_sendmsg(conn->params.local->socket, &msg, len); conn->params.peer->last_tx_at = ktime_get_seconds(); ip_sock_set_mtu_discover(conn->params.local->socket->sk, - IP_PMTUDISC_DO); + IP_PMTUDISC_DO); break; default: @@ -593,7 +576,7 @@ send_fragmentable: trace_rxrpc_tx_fail(call->debug_id, serial, ret, rxrpc_tx_point_call_data_frag); } else { - trace_rxrpc_tx_packet(call->debug_id, &whdr, + trace_rxrpc_tx_packet(call->debug_id, &txb->wire, rxrpc_tx_point_call_data_frag); } rxrpc_tx_backoff(call, ret); diff --git a/net/rxrpc/proc.c b/net/rxrpc/proc.c index d48af0178866..0807753ec2dc 100644 --- a/net/rxrpc/proc.c +++ b/net/rxrpc/proc.c @@ -54,7 +54,7 @@ static int rxrpc_call_seq_show(struct seq_file *seq, void *v) struct rxrpc_call *call; struct rxrpc_net *rxnet = rxrpc_net(seq_file_net(seq)); unsigned long timeout = 0; - rxrpc_seq_t tx_hard_ack; + rxrpc_seq_t acks_hard_ack; char lbuff[50], rbuff[50]; u64 wtmp; @@ -91,7 +91,7 @@ static int rxrpc_call_seq_show(struct seq_file *seq, void *v) timeout -= jiffies; } - tx_hard_ack = READ_ONCE(call->tx_hard_ack); + acks_hard_ack = READ_ONCE(call->acks_hard_ack); wtmp = atomic64_read_acquire(&call->ackr_window); seq_printf(seq, "UDP %-47.47s %-47.47s %4x %08x %08x %s %3u" @@ -106,7 +106,7 @@ static int rxrpc_call_seq_show(struct seq_file *seq, void *v) rxrpc_call_states[call->state], call->abort_code, call->debug_id, - tx_hard_ack, READ_ONCE(call->tx_top) - tx_hard_ack, + acks_hard_ack, READ_ONCE(call->tx_top) - acks_hard_ack, lower_32_bits(wtmp), upper_32_bits(wtmp) - lower_32_bits(wtmp), call->rx_serial, timeout); @@ -459,9 +459,8 @@ int rxrpc_stats_show(struct seq_file *seq, void *v) atomic_read(&rxnet->stat_why_req_ack[rxrpc_reqack_slow_start]), atomic_read(&rxnet->stat_why_req_ack[rxrpc_reqack_small_txwin])); seq_printf(seq, - "Buffers : txb=%u,%u rxb=%u\n", + "Buffers : txb=%u rxb=%u\n", atomic_read(&rxrpc_nr_txbuf), - atomic_read(&rxrpc_n_tx_skbs), atomic_read(&rxrpc_n_rx_skbs)); return 0; } diff --git a/net/rxrpc/rxkad.c b/net/rxrpc/rxkad.c index d87c99b36e01..8fc055587f0e 100644 --- a/net/rxrpc/rxkad.c +++ b/net/rxrpc/rxkad.c @@ -259,11 +259,10 @@ static void rxkad_free_call_crypto(struct rxrpc_call *call) * partially encrypt a packet (level 1 security) */ static int rxkad_secure_packet_auth(const struct rxrpc_call *call, - struct sk_buff *skb, u32 data_size, + struct rxrpc_txbuf *txb, struct skcipher_request *req) { - struct rxrpc_skb_priv *sp = rxrpc_skb(skb); - struct rxkad_level1_hdr hdr; + struct rxkad_level1_hdr *hdr = (void *)txb->data; struct rxrpc_crypt iv; struct scatterlist sg; size_t pad; @@ -271,22 +270,22 @@ static int rxkad_secure_packet_auth(const struct rxrpc_call *call, _enter(""); - check = sp->hdr.seq ^ call->call_id; - data_size |= (u32)check << 16; + check = txb->seq ^ ntohl(txb->wire.callNumber); + hdr->data_size = htonl((u32)check << 16 | txb->len); - hdr.data_size = htonl(data_size); - memcpy(skb->head, &hdr, sizeof(hdr)); - - pad = sizeof(struct rxkad_level1_hdr) + data_size; + txb->len += sizeof(struct rxkad_level1_hdr); + pad = txb->len; pad = RXKAD_ALIGN - pad; pad &= RXKAD_ALIGN - 1; - if (pad) - skb_put_zero(skb, pad); + if (pad) { + memset(txb->data + txb->offset, 0, pad); + txb->len += pad; + } /* start the encryption afresh */ memset(&iv, 0, sizeof(iv)); - sg_init_one(&sg, skb->head, 8); + sg_init_one(&sg, txb->data, 8); skcipher_request_set_sync_tfm(req, call->conn->rxkad.cipher); skcipher_request_set_callback(req, 0, NULL, NULL); skcipher_request_set_crypt(req, &sg, &sg, 8, iv.x); @@ -301,87 +300,60 @@ static int rxkad_secure_packet_auth(const struct rxrpc_call *call, * wholly encrypt a packet (level 2 security) */ static int rxkad_secure_packet_encrypt(const struct rxrpc_call *call, - struct sk_buff *skb, - u32 data_size, + struct rxrpc_txbuf *txb, struct skcipher_request *req) { const struct rxrpc_key_token *token; - struct rxkad_level2_hdr rxkhdr; - struct rxrpc_skb_priv *sp; + struct rxkad_level2_hdr *rxkhdr = (void *)txb->data; struct rxrpc_crypt iv; - struct scatterlist sg[16]; - unsigned int len; + struct scatterlist sg; size_t pad; u16 check; - int err; - - sp = rxrpc_skb(skb); + int ret; _enter(""); - check = sp->hdr.seq ^ call->call_id; + check = txb->seq ^ ntohl(txb->wire.callNumber); - rxkhdr.data_size = htonl(data_size | (u32)check << 16); - rxkhdr.checksum = 0; - memcpy(skb->head, &rxkhdr, sizeof(rxkhdr)); + rxkhdr->data_size = htonl(txb->len | (u32)check << 16); + rxkhdr->checksum = 0; - pad = sizeof(struct rxkad_level2_hdr) + data_size; + txb->len += sizeof(struct rxkad_level2_hdr); + pad = txb->len; pad = RXKAD_ALIGN - pad; pad &= RXKAD_ALIGN - 1; - if (pad) - skb_put_zero(skb, pad); + if (pad) { + memset(txb->data + txb->offset, 0, pad); + txb->len += pad; + } /* encrypt from the session key */ token = call->conn->params.key->payload.data[0]; memcpy(&iv, token->kad->session_key, sizeof(iv)); - sg_init_one(&sg[0], skb->head, sizeof(rxkhdr)); + sg_init_one(&sg, txb->data, txb->len); skcipher_request_set_sync_tfm(req, call->conn->rxkad.cipher); skcipher_request_set_callback(req, 0, NULL, NULL); - skcipher_request_set_crypt(req, &sg[0], &sg[0], sizeof(rxkhdr), iv.x); - crypto_skcipher_encrypt(req); - - /* we want to encrypt the skbuff in-place */ - err = -EMSGSIZE; - if (skb_shinfo(skb)->nr_frags > 16) - goto out; - - len = round_up(data_size, RXKAD_ALIGN); - - sg_init_table(sg, ARRAY_SIZE(sg)); - err = skb_to_sgvec(skb, sg, 8, len); - if (unlikely(err < 0)) - goto out; - skcipher_request_set_crypt(req, sg, sg, len, iv.x); - crypto_skcipher_encrypt(req); - - _leave(" = 0"); - err = 0; - -out: + skcipher_request_set_crypt(req, &sg, &sg, txb->len, iv.x); + ret = crypto_skcipher_encrypt(req); skcipher_request_zero(req); - return err; + return ret; } /* * checksum an RxRPC packet header */ -static int rxkad_secure_packet(struct rxrpc_call *call, - struct sk_buff *skb, - size_t data_size) +static int rxkad_secure_packet(struct rxrpc_call *call, struct rxrpc_txbuf *txb) { - struct rxrpc_skb_priv *sp; struct skcipher_request *req; struct rxrpc_crypt iv; struct scatterlist sg; u32 x, y; int ret; - sp = rxrpc_skb(skb); - - _enter("{%d{%x}},{#%u},%zu,", + _enter("{%d{%x}},{#%u},%u,", call->debug_id, key_serial(call->conn->params.key), - sp->hdr.seq, data_size); + txb->seq, txb->len); if (!call->conn->rxkad.cipher) return 0; @@ -398,9 +370,9 @@ static int rxkad_secure_packet(struct rxrpc_call *call, memcpy(&iv, call->conn->rxkad.csum_iv.x, sizeof(iv)); /* calculate the security checksum */ - x = (call->cid & RXRPC_CHANNELMASK) << (32 - RXRPC_CIDSHIFT); - x |= sp->hdr.seq & 0x3fffffff; - call->crypto_buf[0] = htonl(call->call_id); + x = (ntohl(txb->wire.cid) & RXRPC_CHANNELMASK) << (32 - RXRPC_CIDSHIFT); + x |= txb->seq & 0x3fffffff; + call->crypto_buf[0] = txb->wire.callNumber; call->crypto_buf[1] = htonl(x); sg_init_one(&sg, call->crypto_buf, 8); @@ -414,17 +386,17 @@ static int rxkad_secure_packet(struct rxrpc_call *call, y = (y >> 16) & 0xffff; if (y == 0) y = 1; /* zero checksums are not permitted */ - sp->hdr.cksum = y; + txb->wire.cksum = htons(y); switch (call->conn->params.security_level) { case RXRPC_SECURITY_PLAIN: ret = 0; break; case RXRPC_SECURITY_AUTH: - ret = rxkad_secure_packet_auth(call, skb, data_size, req); + ret = rxkad_secure_packet_auth(call, txb, req); break; case RXRPC_SECURITY_ENCRYPT: - ret = rxkad_secure_packet_encrypt(call, skb, data_size, req); + ret = rxkad_secure_packet_encrypt(call, txb, req); break; default: ret = -EPERM; diff --git a/net/rxrpc/sendmsg.c b/net/rxrpc/sendmsg.c index e32805a49324..a96ae7f58148 100644 --- a/net/rxrpc/sendmsg.c +++ b/net/rxrpc/sendmsg.c @@ -25,7 +25,7 @@ static bool rxrpc_check_tx_space(struct rxrpc_call *call, rxrpc_seq_t *_tx_win) unsigned int win_size = min_t(unsigned int, call->tx_winsize, call->cong_cwnd + call->cong_extra); - rxrpc_seq_t tx_win = READ_ONCE(call->tx_hard_ack); + rxrpc_seq_t tx_win = smp_load_acquire(&call->acks_hard_ack); if (_tx_win) *_tx_win = tx_win; @@ -50,7 +50,12 @@ static int rxrpc_wait_for_tx_window_intr(struct rxrpc_sock *rx, if (signal_pending(current)) return sock_intr_errno(*timeo); - trace_rxrpc_transmit(call, rxrpc_transmit_wait); + if (READ_ONCE(call->acks_hard_ack) != call->tx_bottom) { + rxrpc_shrink_call_tx_buffer(call); + continue; + } + + trace_rxrpc_txqueue(call, rxrpc_txqueue_wait); *timeo = schedule_timeout(*timeo); } } @@ -71,12 +76,11 @@ static int rxrpc_wait_for_tx_window_waitall(struct rxrpc_sock *rx, rtt = 2; timeout = rtt; - tx_start = READ_ONCE(call->tx_hard_ack); + tx_start = smp_load_acquire(&call->acks_hard_ack); for (;;) { set_current_state(TASK_UNINTERRUPTIBLE); - tx_win = READ_ONCE(call->tx_hard_ack); if (rxrpc_check_tx_space(call, &tx_win)) return 0; @@ -87,12 +91,17 @@ static int rxrpc_wait_for_tx_window_waitall(struct rxrpc_sock *rx, tx_win == tx_start && signal_pending(current)) return -EINTR; + if (READ_ONCE(call->acks_hard_ack) != call->tx_bottom) { + rxrpc_shrink_call_tx_buffer(call); + continue; + } + if (tx_win != tx_start) { timeout = rtt; tx_start = tx_win; } - trace_rxrpc_transmit(call, rxrpc_transmit_wait); + trace_rxrpc_txqueue(call, rxrpc_txqueue_wait); timeout = schedule_timeout(timeout); } } @@ -112,7 +121,12 @@ static int rxrpc_wait_for_tx_window_nonintr(struct rxrpc_sock *rx, if (call->state >= RXRPC_CALL_COMPLETE) return call->error; - trace_rxrpc_transmit(call, rxrpc_transmit_wait); + if (READ_ONCE(call->acks_hard_ack) != call->tx_bottom) { + rxrpc_shrink_call_tx_buffer(call); + continue; + } + + trace_rxrpc_txqueue(call, rxrpc_txqueue_wait); *timeo = schedule_timeout(*timeo); } } @@ -129,8 +143,8 @@ static int rxrpc_wait_for_tx_window(struct rxrpc_sock *rx, DECLARE_WAITQUEUE(myself, current); int ret; - _enter(",{%u,%u,%u}", - call->tx_hard_ack, call->tx_top, call->tx_winsize); + _enter(",{%u,%u,%u,%u}", + call->tx_bottom, call->acks_hard_ack, call->tx_top, call->tx_winsize); add_wait_queue(&call->waitq, &myself); @@ -154,24 +168,6 @@ static int rxrpc_wait_for_tx_window(struct rxrpc_sock *rx, return ret; } -/* - * Schedule an instant Tx resend. - */ -static inline void rxrpc_instant_resend(struct rxrpc_call *call, int ix) -{ - spin_lock_bh(&call->lock); - - if (call->state < RXRPC_CALL_COMPLETE) { - call->rxtx_annotations[ix] = - (call->rxtx_annotations[ix] & RXRPC_TX_ANNO_LAST) | - RXRPC_TX_ANNO_RETRANS; - if (!test_and_set_bit(RXRPC_CALL_EV_RESEND, &call->events)) - rxrpc_queue_call(call); - } - - spin_unlock_bh(&call->lock); -} - /* * Notify the owner of the call that the transmit phase is ended and the last * packet has been queued. @@ -188,40 +184,35 @@ static void rxrpc_notify_end_tx(struct rxrpc_sock *rx, struct rxrpc_call *call, * the packet immediately. Returns the error from rxrpc_send_data_packet() * in case the caller wants to do something with it. */ -static int rxrpc_queue_packet(struct rxrpc_sock *rx, struct rxrpc_call *call, - struct sk_buff *skb, bool last, - rxrpc_notify_end_tx_t notify_end_tx) +static void rxrpc_queue_packet(struct rxrpc_sock *rx, struct rxrpc_call *call, + struct rxrpc_txbuf *txb, + rxrpc_notify_end_tx_t notify_end_tx) { - struct rxrpc_skb_priv *sp = rxrpc_skb(skb); unsigned long now; - rxrpc_seq_t seq = sp->hdr.seq; - int ret, ix; - u8 annotation = RXRPC_TX_ANNO_UNACK; - - _net("queue skb %p [%d]", skb, seq); + rxrpc_seq_t seq = txb->seq; + bool last = test_bit(RXRPC_TXBUF_LAST, &txb->flags); + int ret; rxrpc_inc_stat(call->rxnet, stat_tx_data); ASSERTCMP(seq, ==, call->tx_top + 1); - if (last) - annotation |= RXRPC_TX_ANNO_LAST; - /* We have to set the timestamp before queueing as the retransmit * algorithm can see the packet as soon as we queue it. */ - skb->tstamp = ktime_get_real(); + txb->last_sent = ktime_get_real(); - ix = seq & RXRPC_RXTX_BUFF_MASK; - rxrpc_get_skb(skb, rxrpc_skb_got); - call->rxtx_annotations[ix] = annotation; - smp_wmb(); - call->rxtx_buffer[ix] = skb; + /* Add the packet to the call's output buffer */ + rxrpc_get_txbuf(txb, rxrpc_txbuf_get_buffer); + spin_lock(&call->tx_lock); + list_add_tail(&txb->call_link, &call->tx_buffer); call->tx_top = seq; + spin_unlock(&call->tx_lock); + if (last) - trace_rxrpc_transmit(call, rxrpc_transmit_queue_last); + trace_rxrpc_txqueue(call, rxrpc_txqueue_queue_last); else - trace_rxrpc_transmit(call, rxrpc_transmit_queue); + trace_rxrpc_txqueue(call, rxrpc_txqueue_queue); if (last || call->state == RXRPC_CALL_SERVER_ACK_REQUEST) { _debug("________awaiting reply/ACK__________"); @@ -254,7 +245,7 @@ static int rxrpc_queue_packet(struct rxrpc_sock *rx, struct rxrpc_call *call, if (seq == 1 && rxrpc_is_client_call(call)) rxrpc_expose_client_call(call); - ret = rxrpc_send_data_packet(call, skb, false); + ret = rxrpc_send_data_packet(call, txb); if (ret < 0) { switch (ret) { case -ENETUNREACH: @@ -264,8 +255,6 @@ static int rxrpc_queue_packet(struct rxrpc_sock *rx, struct rxrpc_call *call, 0, ret); goto out; } - _debug("need instant resend %d", ret); - rxrpc_instant_resend(call, ix); } else { unsigned long now = jiffies; unsigned long resend_at = now + call->peer->rto_j; @@ -276,9 +265,7 @@ static int rxrpc_queue_packet(struct rxrpc_sock *rx, struct rxrpc_call *call, } out: - rxrpc_free_skb(skb, rxrpc_skb_freed); - _leave(" = %d", ret); - return ret; + rxrpc_put_txbuf(txb, rxrpc_txbuf_put_trans); } /* @@ -292,8 +279,7 @@ static int rxrpc_send_data(struct rxrpc_sock *rx, rxrpc_notify_end_tx_t notify_end_tx, bool *_dropped_lock) { - struct rxrpc_skb_priv *sp; - struct sk_buff *skb; + struct rxrpc_txbuf *txb; struct sock *sk = &rx->sk; enum rxrpc_call_state state; long timeo; @@ -327,14 +313,15 @@ reload: goto maybe_error; } - skb = call->tx_pending; + txb = call->tx_pending; call->tx_pending = NULL; - rxrpc_see_skb(skb, rxrpc_skb_seen); + if (txb) + rxrpc_see_txbuf(txb, rxrpc_txbuf_see_send_more); do { rxrpc_transmit_ack_packets(call->peer->local); - if (!skb) { + if (!txb) { size_t remain, bufsize, chunk, offset; _debug("alloc"); @@ -355,52 +342,31 @@ reload: _debug("SIZE: %zu/%zu @%zu", chunk, bufsize, offset); /* create a buffer that we can retain until it's ACK'd */ - skb = sock_alloc_send_skb( - sk, bufsize, msg->msg_flags & MSG_DONTWAIT, &ret); - if (!skb) + ret = -ENOMEM; + txb = rxrpc_alloc_txbuf(call, RXRPC_PACKET_TYPE_DATA, + GFP_KERNEL); + if (!txb) goto maybe_error; - sp = rxrpc_skb(skb); - rxrpc_new_skb(skb, rxrpc_skb_new); - - _debug("ALLOC SEND %p", skb); - - ASSERTCMP(skb->mark, ==, 0); - - __skb_put(skb, offset); - - sp->remain = chunk; - if (sp->remain > skb_tailroom(skb)) - sp->remain = skb_tailroom(skb); - - _net("skb: hr %d, tr %d, hl %d, rm %d", - skb_headroom(skb), - skb_tailroom(skb), - skb_headlen(skb), - sp->remain); - - skb->ip_summed = CHECKSUM_UNNECESSARY; + txb->offset = offset; + txb->space -= offset; + txb->space = min_t(size_t, chunk, txb->space); } _debug("append"); - sp = rxrpc_skb(skb); /* append next segment of data to the current buffer */ if (msg_data_left(msg) > 0) { - int copy = skb_tailroom(skb); - ASSERTCMP(copy, >, 0); - if (copy > msg_data_left(msg)) - copy = msg_data_left(msg); - if (copy > sp->remain) - copy = sp->remain; + size_t copy = min_t(size_t, txb->space, msg_data_left(msg)); - _debug("add"); - ret = skb_add_data(skb, &msg->msg_iter, copy); - _debug("added"); - if (ret < 0) + _debug("add %zu", copy); + if (!copy_from_iter_full(txb->data + txb->offset, copy, + &msg->msg_iter)) goto efault; - sp->remain -= copy; - skb->mark += copy; + _debug("added"); + txb->space -= copy; + txb->len += copy; + txb->offset += copy; copied += copy; if (call->tx_total_len != -1) call->tx_total_len -= copy; @@ -412,32 +378,22 @@ reload: goto call_terminated; /* add the packet to the send queue if it's now full */ - if (sp->remain <= 0 || + if (!txb->space || (msg_data_left(msg) == 0 && !more)) { - struct rxrpc_connection *conn = call->conn; - uint32_t seq; - - seq = call->tx_top + 1; - - sp->hdr.seq = seq; - sp->hdr._rsvd = 0; - sp->hdr.flags = conn->out_clientflag; - - if (msg_data_left(msg) == 0 && !more) - sp->hdr.flags |= RXRPC_LAST_PACKET; - else if (call->tx_top - call->tx_hard_ack < + if (msg_data_left(msg) == 0 && !more) { + txb->wire.flags |= RXRPC_LAST_PACKET; + __set_bit(RXRPC_TXBUF_LAST, &txb->flags); + } + else if (call->tx_top - call->acks_hard_ack < call->tx_winsize) - sp->hdr.flags |= RXRPC_MORE_PACKETS; + txb->wire.flags |= RXRPC_MORE_PACKETS; - ret = call->security->secure_packet(call, skb, skb->mark); + ret = call->security->secure_packet(call, txb); if (ret < 0) goto out; - ret = rxrpc_queue_packet(rx, call, skb, - !msg_data_left(msg) && !more, - notify_end_tx); - /* Should check for failure here */ - skb = NULL; + rxrpc_queue_packet(rx, call, txb, notify_end_tx); + txb = NULL; } } while (msg_data_left(msg) > 0); @@ -450,12 +406,12 @@ success: read_unlock_bh(&call->state_lock); } out: - call->tx_pending = skb; + call->tx_pending = txb; _leave(" = %d", ret); return ret; call_terminated: - rxrpc_free_skb(skb, rxrpc_skb_freed); + rxrpc_put_txbuf(txb, rxrpc_txbuf_put_send_aborted); _leave(" = %d", call->error); return call->error; diff --git a/net/rxrpc/txbuf.c b/net/rxrpc/txbuf.c index 45a7b48a5e10..96bfee89927b 100644 --- a/net/rxrpc/txbuf.c +++ b/net/rxrpc/txbuf.c @@ -99,3 +99,37 @@ void rxrpc_put_txbuf(struct rxrpc_txbuf *txb, enum rxrpc_txbuf_trace what) call_rcu(&txb->rcu, rxrpc_free_txbuf); } } + +/* + * Shrink the transmit buffer. + */ +void rxrpc_shrink_call_tx_buffer(struct rxrpc_call *call) +{ + struct rxrpc_txbuf *txb; + rxrpc_seq_t hard_ack = smp_load_acquire(&call->acks_hard_ack); + + _enter("%x/%x/%x", call->tx_bottom, call->acks_hard_ack, call->tx_top); + + for (;;) { + spin_lock(&call->tx_lock); + txb = list_first_entry_or_null(&call->tx_buffer, + struct rxrpc_txbuf, call_link); + if (!txb) + break; + hard_ack = smp_load_acquire(&call->acks_hard_ack); + if (before(hard_ack, txb->seq)) + break; + + ASSERTCMP(txb->seq, ==, call->tx_bottom + 1); + call->tx_bottom++; + list_del_rcu(&txb->call_link); + + trace_rxrpc_txqueue(call, rxrpc_txqueue_dequeue); + + spin_unlock(&call->tx_lock); + + rxrpc_put_txbuf(txb, rxrpc_txbuf_put_rotated); + } + + spin_unlock(&call->tx_lock); +} From 4e76bd406d6e9208ea558953862a47524829688c Mon Sep 17 00:00:00 2001 From: David Howells Date: Fri, 6 May 2022 16:13:13 +0100 Subject: [PATCH 0737/2002] rxrpc: Remove call->lock call->lock is no longer necessary, so remove it. Signed-off-by: David Howells cc: Marc Dionne cc: linux-afs@lists.infradead.org --- net/rxrpc/ar-internal.h | 1 - net/rxrpc/call_event.c | 22 ++-------------------- net/rxrpc/call_object.c | 3 --- net/rxrpc/input.c | 3 --- net/rxrpc/output.c | 6 +----- 5 files changed, 3 insertions(+), 32 deletions(-) diff --git a/net/rxrpc/ar-internal.h b/net/rxrpc/ar-internal.h index d7aebe82e17c..5ec30e84360b 100644 --- a/net/rxrpc/ar-internal.h +++ b/net/rxrpc/ar-internal.h @@ -601,7 +601,6 @@ struct rxrpc_call { unsigned long user_call_ID; /* user-defined call ID */ unsigned long flags; unsigned long events; - spinlock_t lock; spinlock_t notify_lock; /* Kernel notification lock */ rwlock_t state_lock; /* lock for state transition */ u32 abort_code; /* Local/remote abort code */ diff --git a/net/rxrpc/call_event.c b/net/rxrpc/call_event.c index 3c37b280eb20..dbfaf8170929 100644 --- a/net/rxrpc/call_event.c +++ b/net/rxrpc/call_event.c @@ -26,24 +26,19 @@ void rxrpc_propose_ping(struct rxrpc_call *call, u32 serial, unsigned long now = jiffies; unsigned long ping_at = now + rxrpc_idle_ack_delay; - spin_lock_bh(&call->lock); - if (time_before(ping_at, call->ping_at)) { WRITE_ONCE(call->ping_at, ping_at); rxrpc_reduce_call_timer(call, ping_at, now, rxrpc_timer_set_for_ping); trace_rxrpc_propose_ack(call, why, RXRPC_ACK_PING, serial); } - - spin_unlock_bh(&call->lock); } /* * Propose a DELAY ACK be sent in the future. */ -static void __rxrpc_propose_delay_ACK(struct rxrpc_call *call, - rxrpc_serial_t serial, - enum rxrpc_propose_ack_trace why) +void rxrpc_propose_delay_ACK(struct rxrpc_call *call, rxrpc_serial_t serial, + enum rxrpc_propose_ack_trace why) { unsigned long expiry = rxrpc_soft_ack_delay; unsigned long now = jiffies, ack_at; @@ -68,17 +63,6 @@ static void __rxrpc_propose_delay_ACK(struct rxrpc_call *call, trace_rxrpc_propose_ack(call, why, RXRPC_ACK_DELAY, serial); } -/* - * Propose a DELAY ACK be sent, locking the call structure - */ -void rxrpc_propose_delay_ACK(struct rxrpc_call *call, rxrpc_serial_t serial, - enum rxrpc_propose_ack_trace why) -{ - spin_lock_bh(&call->lock); - __rxrpc_propose_delay_ACK(call, serial, why); - spin_unlock_bh(&call->lock); -} - /* * Queue an ACK for immediate transmission. */ @@ -204,10 +188,8 @@ static void rxrpc_resend(struct rxrpc_call *call, unsigned long now_j) * retransmitting data. */ if (list_empty(&retrans_queue)) { - spin_lock_bh(&call->lock); rxrpc_reduce_call_timer(call, resend_at, now_j, rxrpc_timer_set_for_resend); - spin_unlock_bh(&call->lock); ack_ts = ktime_sub(now, call->acks_latest_ts); if (ktime_to_us(ack_ts) < (call->peer->srtt_us >> 3)) goto out; diff --git a/net/rxrpc/call_object.c b/net/rxrpc/call_object.c index ed5f6f0e4286..a3ae2ab45f9e 100644 --- a/net/rxrpc/call_object.c +++ b/net/rxrpc/call_object.c @@ -159,7 +159,6 @@ struct rxrpc_call *rxrpc_alloc_call(struct rxrpc_sock *rx, gfp_t gfp, skb_queue_head_init(&call->recvmsg_queue); skb_queue_head_init(&call->rx_oos_queue); init_waitqueue_head(&call->waitq); - spin_lock_init(&call->lock); spin_lock_init(&call->notify_lock); spin_lock_init(&call->tx_lock); spin_lock_init(&call->input_lock); @@ -543,10 +542,8 @@ void rxrpc_release_call(struct rxrpc_sock *rx, struct rxrpc_call *call) ASSERTCMP(call->state, ==, RXRPC_CALL_COMPLETE); - spin_lock_bh(&call->lock); if (test_and_set_bit(RXRPC_CALL_RELEASED, &call->flags)) BUG(); - spin_unlock_bh(&call->lock); rxrpc_put_call_slot(call); rxrpc_delete_call_timer(call); diff --git a/net/rxrpc/input.c b/net/rxrpc/input.c index b1f7debd4f3e..e6e1267915de 100644 --- a/net/rxrpc/input.c +++ b/net/rxrpc/input.c @@ -279,9 +279,6 @@ static bool rxrpc_receiving_reply(struct rxrpc_call *call) rxrpc_seq_t top = READ_ONCE(call->tx_top); if (call->ackr_reason) { - spin_lock_bh(&call->lock); - call->ackr_reason = 0; - spin_unlock_bh(&call->lock); now = jiffies; timo = now + MAX_JIFFY_OFFSET; WRITE_ONCE(call->resend_at, timo); diff --git a/net/rxrpc/output.c b/net/rxrpc/output.c index e2f501cef040..2c3f7e4e30d7 100644 --- a/net/rxrpc/output.c +++ b/net/rxrpc/output.c @@ -229,13 +229,9 @@ static int rxrpc_send_ack_packet(struct rxrpc_local *local, struct rxrpc_txbuf * if (txb->ack.reason == RXRPC_ACK_IDLE) clear_bit(RXRPC_CALL_IDLE_ACK_PENDING, &call->flags); - spin_lock_bh(&call->lock); n = rxrpc_fill_out_ack(conn, call, txb); - spin_unlock_bh(&call->lock); - if (n == 0) { - kfree(pkt); + if (n == 0) return 0; - } iov[0].iov_base = &txb->wire; iov[0].iov_len = sizeof(txb->wire) + sizeof(txb->ack) + n; From d57a3a151660902091491ac2633134e1be92557f Mon Sep 17 00:00:00 2001 From: David Howells Date: Sat, 7 May 2022 10:06:13 +0100 Subject: [PATCH 0738/2002] rxrpc: Save last ACK's SACK table rather than marking txbufs Improve the tracking of which packets need to be transmitted by saving the last ACK packet that we receive that has a populated soft-ACK table rather than marking packets. Then we can step through the soft-ACK table and look at the packets we've transmitted beyond that to determine which packets we might want to retransmit. We also look at the highest serial number that has been acked to try and guess which packets we've transmitted the peer is likely to have seen. If necessary, we send a ping to retrieve that number. One downside that might be a problem is that we can't then compare the previous acked/unacked state so easily in rxrpc_input_soft_acks() - which is a potential problem for the slow-start algorithm. Signed-off-by: David Howells cc: Marc Dionne cc: linux-afs@lists.infradead.org --- include/trace/events/rxrpc.h | 7 +- net/core/skbuff.c | 1 + net/rxrpc/ar-internal.h | 12 ++- net/rxrpc/call_event.c | 119 ++++++++++++++++++++----- net/rxrpc/call_object.c | 2 + net/rxrpc/input.c | 164 ++++++++++++++++------------------- net/rxrpc/sendmsg.c | 1 - 7 files changed, 186 insertions(+), 120 deletions(-) diff --git a/include/trace/events/rxrpc.h b/include/trace/events/rxrpc.h index 71ca74e40ec8..a11de55c3c14 100644 --- a/include/trace/events/rxrpc.h +++ b/include/trace/events/rxrpc.h @@ -17,6 +17,7 @@ * Declare tracing information enums and their string mappings for display. */ #define rxrpc_skb_traces \ + EM(rxrpc_skb_ack, "ACK") \ EM(rxrpc_skb_cleaned, "CLN") \ EM(rxrpc_skb_cloned_jumbo, "CLJ") \ EM(rxrpc_skb_freed, "FRE") \ @@ -1257,7 +1258,7 @@ TRACE_EVENT(rxrpc_congest, memcpy(&__entry->sum, summary, sizeof(__entry->sum)); ), - TP_printk("c=%08x r=%08x %s q=%08x %s cw=%u ss=%u nA=%u,%u+%u,%u r=%u b=%u u=%u d=%u l=%x%s%s%s", + TP_printk("c=%08x r=%08x %s q=%08x %s cw=%u ss=%u nA=%u,%u+%u r=%u b=%u u=%u d=%u l=%x%s%s%s", __entry->call, __entry->ack_serial, __print_symbolic(__entry->sum.ack_reason, rxrpc_ack_names), @@ -1265,8 +1266,8 @@ TRACE_EVENT(rxrpc_congest, __print_symbolic(__entry->sum.mode, rxrpc_congest_modes), __entry->sum.cwnd, __entry->sum.ssthresh, - __entry->sum.nr_acks, __entry->sum.nr_nacks, - __entry->sum.nr_new_acks, __entry->sum.nr_new_nacks, + __entry->sum.nr_acks, __entry->sum.saw_nacks, + __entry->sum.nr_new_acks, __entry->sum.nr_rot_new_acks, __entry->top - __entry->hard_ack, __entry->sum.cumulative_acks, diff --git a/net/core/skbuff.c b/net/core/skbuff.c index 1d84a17eada5..2143244e8ec3 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -6431,6 +6431,7 @@ void skb_condense(struct sk_buff *skb) */ skb->truesize = SKB_TRUESIZE(skb_end_offset(skb)); } +EXPORT_SYMBOL(skb_condense); #ifdef CONFIG_SKB_EXTENSIONS static void *skb_ext_get_ptr(struct skb_ext *ext, enum skb_ext_id id) diff --git a/net/rxrpc/ar-internal.h b/net/rxrpc/ar-internal.h index 5ec30e84360b..168d03b56ada 100644 --- a/net/rxrpc/ar-internal.h +++ b/net/rxrpc/ar-internal.h @@ -694,6 +694,8 @@ struct rxrpc_call { rxrpc_seq_t acks_lost_top; /* tx_top at the time lost-ack ping sent */ rxrpc_serial_t acks_lost_ping; /* Serial number of probe ACK */ rxrpc_serial_t acks_highest_serial; /* Highest serial number ACK'd */ + struct sk_buff *acks_soft_tbl; /* The last ACK packet with NAKs in it */ + spinlock_t acks_ack_lock; /* Access to ->acks_last_ack */ }; /* @@ -702,10 +704,9 @@ struct rxrpc_call { struct rxrpc_ack_summary { u8 ack_reason; u8 nr_acks; /* Number of ACKs in packet */ - u8 nr_nacks; /* Number of NACKs in packet */ u8 nr_new_acks; /* Number of new ACKs in packet */ - u8 nr_new_nacks; /* Number of new NACKs in packet */ u8 nr_rot_new_acks; /* Number of rotated new ACKs */ + bool saw_nacks; /* Saw NACKs in packet */ bool new_low_nack; /* T if new low NACK found */ bool retrans_timeo; /* T if reTx due to timeout happened */ u8 flight_size; /* Number of unreceived transmissions */ @@ -765,11 +766,8 @@ struct rxrpc_txbuf { unsigned int space; /* Remaining data space */ unsigned int offset; /* Offset of fill point */ unsigned long flags; -#define RXRPC_TXBUF_ACKED 0 /* Set if ACK'd */ -#define RXRPC_TXBUF_NACKED 1 /* Set if NAK'd */ -#define RXRPC_TXBUF_LAST 2 /* Set if last packet in Tx phase */ -#define RXRPC_TXBUF_RESENT 3 /* Set if has been resent */ -#define RXRPC_TXBUF_RETRANS 4 /* Set if should be retransmitted */ +#define RXRPC_TXBUF_LAST 0 /* Set if last packet in Tx phase */ +#define RXRPC_TXBUF_RESENT 1 /* Set if has been resent */ u8 /*enum rxrpc_propose_ack_trace*/ ack_why; /* If ack, why */ struct { /* The packet for encrypting and DMA'ing. We align it such diff --git a/net/rxrpc/call_event.c b/net/rxrpc/call_event.c index dbfaf8170929..1e21a708390e 100644 --- a/net/rxrpc/call_event.c +++ b/net/rxrpc/call_event.c @@ -132,48 +132,127 @@ static void rxrpc_congestion_timeout(struct rxrpc_call *call) */ static void rxrpc_resend(struct rxrpc_call *call, unsigned long now_j) { + struct rxrpc_ackpacket *ack = NULL; struct rxrpc_txbuf *txb; + struct sk_buff *ack_skb = NULL; unsigned long resend_at; rxrpc_seq_t transmitted = READ_ONCE(call->tx_transmitted); ktime_t now, max_age, oldest, ack_ts; bool unacked = false; + unsigned int i; LIST_HEAD(retrans_queue); _enter("{%d,%d}", call->acks_hard_ack, call->tx_top); now = ktime_get_real(); max_age = ktime_sub_us(now, jiffies_to_usecs(call->peer->rto_j)); + oldest = now; + + /* See if there's an ACK saved with a soft-ACK table in it. */ + if (call->acks_soft_tbl) { + spin_lock_bh(&call->acks_ack_lock); + ack_skb = call->acks_soft_tbl; + if (ack_skb) { + rxrpc_get_skb(ack_skb, rxrpc_skb_ack); + ack = (void *)ack_skb->data + sizeof(struct rxrpc_wire_header); + } + spin_unlock_bh(&call->acks_ack_lock); + } + + if (list_empty(&call->tx_buffer)) + goto no_resend; spin_lock(&call->tx_lock); - /* Scan the packet list without dropping the lock and decide which of - * the packets in the Tx buffer we're going to resend and what the new - * resend timeout will be. - */ + if (list_empty(&call->tx_buffer)) + goto no_further_resend; + trace_rxrpc_resend(call); - oldest = now; - list_for_each_entry(txb, &call->tx_buffer, call_link) { - if (test_bit(RXRPC_TXBUF_ACKED, &txb->flags)) - continue; - if (after(txb->seq, transmitted)) - break; + txb = list_first_entry(&call->tx_buffer, struct rxrpc_txbuf, call_link); - rxrpc_see_txbuf(txb, rxrpc_txbuf_see_unacked); + /* Scan the soft ACK table without dropping the lock and resend any + * explicitly NAK'd packets. + */ + if (ack) { + for (i = 0; i < ack->nAcks; i++) { + rxrpc_seq_t seq; - if (test_bit(RXRPC_TXBUF_RESENT, &txb->flags)) { - if (ktime_after(txb->last_sent, max_age)) { - if (ktime_before(txb->last_sent, oldest)) - oldest = txb->last_sent; + if (ack->acks[i] & 1) continue; + seq = ntohl(ack->firstPacket) + i; + if (after(txb->seq, transmitted)) + break; + if (after(txb->seq, seq)) + continue; /* A new hard ACK probably came in */ + list_for_each_entry_from(txb, &call->tx_buffer, call_link) { + if (txb->seq == seq) + goto found_txb; } - unacked = true; - } + goto no_further_resend; - rxrpc_get_txbuf(txb, rxrpc_txbuf_get_retrans); - list_move_tail(&txb->tx_link, &retrans_queue); + found_txb: + if (after(ntohl(txb->wire.serial), call->acks_highest_serial)) + continue; /* Ack point not yet reached */ + + rxrpc_see_txbuf(txb, rxrpc_txbuf_see_unacked); + + if (list_empty(&txb->tx_link)) { + rxrpc_get_txbuf(txb, rxrpc_txbuf_get_retrans); + rxrpc_get_call(call, rxrpc_call_got_tx); + list_add_tail(&txb->tx_link, &retrans_queue); + set_bit(RXRPC_TXBUF_RESENT, &txb->flags); + } + + trace_rxrpc_retransmit(call, txb->seq, + ktime_to_ns(ktime_sub(txb->last_sent, + max_age))); + + if (list_is_last(&txb->call_link, &call->tx_buffer)) + goto no_further_resend; + txb = list_next_entry(txb, call_link); + } } + /* Fast-forward through the Tx queue to the point the peer says it has + * seen. Anything between the soft-ACK table and that point will get + * ACK'd or NACK'd in due course, so don't worry about it here; here we + * need to consider retransmitting anything beyond that point. + * + * Note that ACK for a packet can beat the update of tx_transmitted. + */ + if (after_eq(READ_ONCE(call->acks_prev_seq), READ_ONCE(call->tx_transmitted))) + goto no_further_resend; + + list_for_each_entry_from(txb, &call->tx_buffer, call_link) { + if (before_eq(txb->seq, READ_ONCE(call->acks_prev_seq))) + continue; + if (after(txb->seq, READ_ONCE(call->tx_transmitted))) + break; /* Not transmitted yet */ + + if (ack && ack->reason == RXRPC_ACK_PING_RESPONSE && + before(ntohl(txb->wire.serial), ntohl(ack->serial))) + goto do_resend; /* Wasn't accounted for by a more recent ping. */ + + if (ktime_after(txb->last_sent, max_age)) { + if (ktime_before(txb->last_sent, oldest)) + oldest = txb->last_sent; + continue; + } + + do_resend: + unacked = true; + if (list_empty(&txb->tx_link)) { + rxrpc_get_txbuf(txb, rxrpc_txbuf_get_retrans); + list_add_tail(&txb->tx_link, &retrans_queue); + set_bit(RXRPC_TXBUF_RESENT, &txb->flags); + rxrpc_inc_stat(call->rxnet, stat_tx_data_retrans); + } + } + +no_further_resend: spin_unlock(&call->tx_lock); +no_resend: + rxrpc_free_skb(ack_skb, rxrpc_skb_freed); resend_at = nsecs_to_jiffies(ktime_to_ns(ktime_sub(now, oldest))); resend_at += jiffies + rxrpc_get_rto_backoff(call->peer, @@ -201,8 +280,6 @@ static void rxrpc_resend(struct rxrpc_call *call, unsigned long now_j) while ((txb = list_first_entry_or_null(&retrans_queue, struct rxrpc_txbuf, tx_link))) { list_del_init(&txb->tx_link); - set_bit(RXRPC_TXBUF_RESENT, &txb->flags); - rxrpc_inc_stat(call->rxnet, stat_tx_data_retrans); rxrpc_send_data_packet(call, txb); rxrpc_put_txbuf(txb, rxrpc_txbuf_put_trans); diff --git a/net/rxrpc/call_object.c b/net/rxrpc/call_object.c index a3ae2ab45f9e..91771031ad3c 100644 --- a/net/rxrpc/call_object.c +++ b/net/rxrpc/call_object.c @@ -162,6 +162,7 @@ struct rxrpc_call *rxrpc_alloc_call(struct rxrpc_sock *rx, gfp_t gfp, spin_lock_init(&call->notify_lock); spin_lock_init(&call->tx_lock); spin_lock_init(&call->input_lock); + spin_lock_init(&call->acks_ack_lock); rwlock_init(&call->state_lock); refcount_set(&call->ref, 1); call->debug_id = debug_id; @@ -701,6 +702,7 @@ void rxrpc_cleanup_call(struct rxrpc_call *call) rxrpc_put_txbuf(txb, rxrpc_txbuf_put_cleaned); } rxrpc_put_txbuf(call->tx_pending, rxrpc_txbuf_put_cleaned); + rxrpc_free_skb(call->acks_soft_tbl, rxrpc_skb_cleaned); call_rcu(&call->rcu, rxrpc_rcu_destroy_call); } diff --git a/net/rxrpc/input.c b/net/rxrpc/input.c index e6e1267915de..5c17fed4b60f 100644 --- a/net/rxrpc/input.c +++ b/net/rxrpc/input.c @@ -60,7 +60,7 @@ static void rxrpc_congestion_management(struct rxrpc_call *call, switch (call->cong_mode) { case RXRPC_CALL_SLOW_START: - if (summary->nr_nacks > 0) + if (summary->saw_nacks) goto packet_loss_detected; if (summary->cumulative_acks > 0) cwnd += 1; @@ -71,7 +71,7 @@ static void rxrpc_congestion_management(struct rxrpc_call *call, goto out; case RXRPC_CALL_CONGEST_AVOIDANCE: - if (summary->nr_nacks > 0) + if (summary->saw_nacks) goto packet_loss_detected; /* We analyse the number of packets that get ACK'd per RTT @@ -90,7 +90,7 @@ static void rxrpc_congestion_management(struct rxrpc_call *call, goto out; case RXRPC_CALL_PACKET_LOSS: - if (summary->nr_nacks == 0) + if (!summary->saw_nacks) goto resume_normality; if (summary->new_low_nack) { @@ -128,7 +128,7 @@ static void rxrpc_congestion_management(struct rxrpc_call *call, } else { change = rxrpc_cong_progress; cwnd = call->cong_ssthresh; - if (summary->nr_nacks == 0) + if (!summary->saw_nacks) goto resume_normality; } goto out; @@ -189,8 +189,7 @@ static bool rxrpc_rotate_tx_window(struct rxrpc_call *call, rxrpc_seq_t to, list_for_each_entry_rcu(txb, &call->tx_buffer, call_link, false) { if (before_eq(txb->seq, call->acks_hard_ack)) continue; - if (!test_bit(RXRPC_TXBUF_ACKED, &txb->flags)) - summary->nr_rot_new_acks++; + summary->nr_rot_new_acks++; if (test_bit(RXRPC_TXBUF_LAST, &txb->flags)) { set_bit(RXRPC_CALL_TX_LAST, &call->flags); rot_last = true; @@ -661,22 +660,8 @@ static void rxrpc_complete_rtt_probe(struct rxrpc_call *call, */ static void rxrpc_input_check_for_lost_ack(struct rxrpc_call *call) { - struct rxrpc_txbuf *txb; - rxrpc_seq_t top, bottom; - bool resend = false; - - bottom = READ_ONCE(call->acks_hard_ack) + 1; - top = READ_ONCE(call->acks_lost_top); - if (before(bottom, top)) { - list_for_each_entry_rcu(txb, &call->tx_buffer, call_link, false) { - if (test_bit(RXRPC_TXBUF_ACKED, &txb->flags)) - continue; - set_bit(RXRPC_TXBUF_RETRANS, &txb->flags); - resend = true; - } - } - - if (resend && !test_and_set_bit(RXRPC_CALL_EV_RESEND, &call->events)) + if (after(call->acks_lost_top, call->acks_prev_seq) && + !test_and_set_bit(RXRPC_CALL_EV_RESEND, &call->events)) rxrpc_queue_call(call); } @@ -749,41 +734,19 @@ static void rxrpc_input_soft_acks(struct rxrpc_call *call, u8 *acks, rxrpc_seq_t seq, int nr_acks, struct rxrpc_ack_summary *summary) { - struct rxrpc_txbuf *txb; + unsigned int i; - list_for_each_entry_rcu(txb, &call->tx_buffer, call_link, false) { - if (before(txb->seq, seq)) - continue; - if (after_eq(txb->seq, seq + nr_acks)) - break; - switch (acks[txb->seq - seq]) { - case RXRPC_ACK_TYPE_ACK: + for (i = 0; i < nr_acks; i++) { + if (acks[i] == RXRPC_ACK_TYPE_ACK) { summary->nr_acks++; - if (test_bit(RXRPC_TXBUF_ACKED, &txb->flags)) - continue; - /* A lot of the time the packet is going to - * have been ACK.'d already. - */ - clear_bit(RXRPC_TXBUF_NACKED, &txb->flags); - set_bit(RXRPC_TXBUF_ACKED, &txb->flags); summary->nr_new_acks++; - break; - case RXRPC_ACK_TYPE_NACK: - if (!summary->nr_nacks && - call->acks_lowest_nak != seq) { - call->acks_lowest_nak = seq; + } else { + if (!summary->saw_nacks && + call->acks_lowest_nak != seq + i) { + call->acks_lowest_nak = seq + i; summary->new_low_nack = true; } - summary->nr_nacks++; - if (test_bit(RXRPC_TXBUF_NACKED, &txb->flags)) - continue; - summary->nr_new_nacks++; - clear_bit(RXRPC_TXBUF_ACKED, &txb->flags); - set_bit(RXRPC_TXBUF_NACKED, &txb->flags); - set_bit(RXRPC_TXBUF_RETRANS, &txb->flags); - break; - default: - return rxrpc_proto_abort("SFT", call, 0); + summary->saw_nacks = true; } } } @@ -825,12 +788,10 @@ static bool rxrpc_is_ack_valid(struct rxrpc_call *call, static void rxrpc_input_ack(struct rxrpc_call *call, struct sk_buff *skb) { struct rxrpc_ack_summary summary = { 0 }; + struct rxrpc_ackpacket ack; struct rxrpc_skb_priv *sp = rxrpc_skb(skb); - union { - struct rxrpc_ackpacket ack; - struct rxrpc_ackinfo info; - u8 acks[RXRPC_MAXACKS]; - } buf; + struct rxrpc_ackinfo info; + struct sk_buff *skb_old = NULL, *skb_put = skb; rxrpc_serial_t ack_serial, acked_serial; rxrpc_seq_t first_soft_ack, hard_ack, prev_pkt; int nr_acks, offset, ioffset; @@ -838,30 +799,28 @@ static void rxrpc_input_ack(struct rxrpc_call *call, struct sk_buff *skb) _enter(""); offset = sizeof(struct rxrpc_wire_header); - if (skb_copy_bits(skb, offset, &buf.ack, sizeof(buf.ack)) < 0) { - _debug("extraction failure"); - return rxrpc_proto_abort("XAK", call, 0); + if (skb_copy_bits(skb, offset, &ack, sizeof(ack)) < 0) { + rxrpc_proto_abort("XAK", call, 0); + goto out_not_locked; } - offset += sizeof(buf.ack); + offset += sizeof(ack); ack_serial = sp->hdr.serial; - acked_serial = ntohl(buf.ack.serial); - first_soft_ack = ntohl(buf.ack.firstPacket); - prev_pkt = ntohl(buf.ack.previousPacket); + acked_serial = ntohl(ack.serial); + first_soft_ack = ntohl(ack.firstPacket); + prev_pkt = ntohl(ack.previousPacket); hard_ack = first_soft_ack - 1; - nr_acks = buf.ack.nAcks; - summary.ack_reason = (buf.ack.reason < RXRPC_ACK__INVALID ? - buf.ack.reason : RXRPC_ACK__INVALID); + nr_acks = ack.nAcks; + summary.ack_reason = (ack.reason < RXRPC_ACK__INVALID ? + ack.reason : RXRPC_ACK__INVALID); trace_rxrpc_rx_ack(call, ack_serial, acked_serial, first_soft_ack, prev_pkt, summary.ack_reason, nr_acks); - rxrpc_inc_stat(call->rxnet, stat_rx_acks[buf.ack.reason]); + rxrpc_inc_stat(call->rxnet, stat_rx_acks[ack.reason]); - switch (buf.ack.reason) { + switch (ack.reason) { case RXRPC_ACK_PING_RESPONSE: - rxrpc_input_ping_response(call, skb->tstamp, acked_serial, - ack_serial); rxrpc_complete_rtt_probe(call, skb->tstamp, acked_serial, ack_serial, rxrpc_rtt_rx_ping_response); break; @@ -876,7 +835,7 @@ static void rxrpc_input_ack(struct rxrpc_call *call, struct sk_buff *skb) break; } - if (buf.ack.reason == RXRPC_ACK_PING) { + if (ack.reason == RXRPC_ACK_PING) { _proto("Rx ACK %%%u PING Request", ack_serial); rxrpc_send_ACK(call, RXRPC_ACK_PING_RESPONSE, ack_serial, rxrpc_propose_ack_respond_to_ping); @@ -889,7 +848,7 @@ static void rxrpc_input_ack(struct rxrpc_call *call, struct sk_buff *skb) * indicates that the client address changed due to NAT. The server * lost the call because it switched to a different peer. */ - if (unlikely(buf.ack.reason == RXRPC_ACK_EXCEEDS_WINDOW) && + if (unlikely(ack.reason == RXRPC_ACK_EXCEEDS_WINDOW) && first_soft_ack == 1 && prev_pkt == 0 && rxrpc_is_client_call(call)) { @@ -902,7 +861,7 @@ static void rxrpc_input_ack(struct rxrpc_call *call, struct sk_buff *skb) * indicate a change of address. However, we can retransmit the call * if we still have it buffered to the beginning. */ - if (unlikely(buf.ack.reason == RXRPC_ACK_OUT_OF_SEQUENCE) && + if (unlikely(ack.reason == RXRPC_ACK_OUT_OF_SEQUENCE) && first_soft_ack == 1 && prev_pkt == 0 && call->acks_hard_ack == 0 && @@ -917,14 +876,19 @@ static void rxrpc_input_ack(struct rxrpc_call *call, struct sk_buff *skb) trace_rxrpc_rx_discard_ack(call->debug_id, ack_serial, first_soft_ack, call->acks_first_seq, prev_pkt, call->acks_prev_seq); - return; + goto out_not_locked; } - buf.info.rxMTU = 0; + info.rxMTU = 0; ioffset = offset + nr_acks + 3; - if (skb->len >= ioffset + sizeof(buf.info) && - skb_copy_bits(skb, ioffset, &buf.info, sizeof(buf.info)) < 0) - return rxrpc_proto_abort("XAI", call, 0); + if (skb->len >= ioffset + sizeof(info) && + skb_copy_bits(skb, ioffset, &info, sizeof(info)) < 0) { + rxrpc_proto_abort("XAI", call, 0); + goto out_not_locked; + } + + if (nr_acks > 0) + skb_condense(skb); spin_lock(&call->input_lock); @@ -940,13 +904,22 @@ static void rxrpc_input_ack(struct rxrpc_call *call, struct sk_buff *skb) call->acks_first_seq = first_soft_ack; call->acks_prev_seq = prev_pkt; - if (buf.ack.reason != RXRPC_ACK_PING && - after(acked_serial, call->acks_highest_serial)) - call->acks_highest_serial = acked_serial; + switch (ack.reason) { + case RXRPC_ACK_PING: + break; + case RXRPC_ACK_PING_RESPONSE: + rxrpc_input_ping_response(call, skb->tstamp, acked_serial, + ack_serial); + fallthrough; + default: + if (after(acked_serial, call->acks_highest_serial)) + call->acks_highest_serial = acked_serial; + break; + } /* Parse rwind and mtu sizes if provided. */ - if (buf.info.rxMTU) - rxrpc_input_ackinfo(call, skb, &buf.info); + if (info.rxMTU) + rxrpc_input_ackinfo(call, skb, &info); if (first_soft_ack == 0) { rxrpc_proto_abort("AK0", call, 0); @@ -982,12 +955,24 @@ static void rxrpc_input_ack(struct rxrpc_call *call, struct sk_buff *skb) } if (nr_acks > 0) { - if (skb_copy_bits(skb, offset, buf.acks, nr_acks) < 0) { + if (offset > (int)skb->len - nr_acks) { rxrpc_proto_abort("XSA", call, 0); goto out; } - rxrpc_input_soft_acks(call, buf.acks, first_soft_ack, nr_acks, - &summary); + + spin_lock(&call->acks_ack_lock); + skb_old = call->acks_soft_tbl; + call->acks_soft_tbl = skb; + spin_unlock(&call->acks_ack_lock); + + rxrpc_input_soft_acks(call, skb->data + offset, first_soft_ack, + nr_acks, &summary); + skb_put = NULL; + } else if (call->acks_soft_tbl) { + spin_lock(&call->acks_ack_lock); + skb_old = call->acks_soft_tbl; + call->acks_soft_tbl = NULL; + spin_unlock(&call->acks_ack_lock); } if (test_bit(RXRPC_CALL_TX_LAST, &call->flags) && @@ -999,6 +984,9 @@ static void rxrpc_input_ack(struct rxrpc_call *call, struct sk_buff *skb) rxrpc_congestion_management(call, skb, &summary, acked_serial); out: spin_unlock(&call->input_lock); +out_not_locked: + rxrpc_free_skb(skb_put, rxrpc_skb_freed); + rxrpc_free_skb(skb_old, rxrpc_skb_freed); } /* @@ -1071,7 +1059,7 @@ static void rxrpc_input_call_packet(struct rxrpc_call *call, case RXRPC_PACKET_TYPE_ACK: rxrpc_input_ack(call, skb); - break; + goto no_free; case RXRPC_PACKET_TYPE_BUSY: _proto("Rx BUSY %%%u", sp->hdr.serial); diff --git a/net/rxrpc/sendmsg.c b/net/rxrpc/sendmsg.c index a96ae7f58148..9b567aff3e84 100644 --- a/net/rxrpc/sendmsg.c +++ b/net/rxrpc/sendmsg.c @@ -600,7 +600,6 @@ rxrpc_new_client_call_for_sendmsg(struct rxrpc_sock *rx, struct msghdr *msg, */ int rxrpc_do_sendmsg(struct rxrpc_sock *rx, struct msghdr *msg, size_t len) __releases(&rx->sk.sk_lock.slock) - __releases(&call->user_mutex) { enum rxrpc_call_state state; struct rxrpc_call *call; From 6869ddb87d475bde2da0dbd4d71270996d65cd47 Mon Sep 17 00:00:00 2001 From: David Howells Date: Wed, 15 Jun 2022 14:49:26 +0100 Subject: [PATCH 0739/2002] rxrpc: Remove the rxtx ring The Rx/Tx ring is no longer used, so remove it. Signed-off-by: David Howells cc: Marc Dionne cc: linux-afs@lists.infradead.org --- net/rxrpc/ar-internal.h | 15 --------------- net/rxrpc/call_object.c | 24 ------------------------ 2 files changed, 39 deletions(-) diff --git a/net/rxrpc/ar-internal.h b/net/rxrpc/ar-internal.h index 168d03b56ada..775eb91aabb2 100644 --- a/net/rxrpc/ar-internal.h +++ b/net/rxrpc/ar-internal.h @@ -617,21 +617,6 @@ struct rxrpc_call { unsigned short rx_pkt_offset; /* Current recvmsg packet offset */ unsigned short rx_pkt_len; /* Current recvmsg packet len */ - /* Rx/Tx circular buffer, depending on phase. - * - * In the Rx phase, packets are annotated with 0 or the number of the - * segment of a jumbo packet each buffer refers to. There can be up to - * 47 segments in a maximum-size UDP packet. - * - * In the Tx phase, packets are annotated with which buffers have been - * acked. - */ -#define RXRPC_RXTX_BUFF_SIZE 64 -#define RXRPC_RXTX_BUFF_MASK (RXRPC_RXTX_BUFF_SIZE - 1) -#define RXRPC_INIT_RX_WINDOW_SIZE 63 - struct sk_buff **rxtx_buffer; - u8 *rxtx_annotations; - /* Transmitted data tracking. */ spinlock_t tx_lock; /* Transmit queue lock */ struct list_head tx_buffer; /* Buffer of transmissible packets */ diff --git a/net/rxrpc/call_object.c b/net/rxrpc/call_object.c index 91771031ad3c..aa19daaa487b 100644 --- a/net/rxrpc/call_object.c +++ b/net/rxrpc/call_object.c @@ -129,16 +129,6 @@ struct rxrpc_call *rxrpc_alloc_call(struct rxrpc_sock *rx, gfp_t gfp, if (!call) return NULL; - call->rxtx_buffer = kcalloc(RXRPC_RXTX_BUFF_SIZE, - sizeof(struct sk_buff *), - gfp); - if (!call->rxtx_buffer) - goto nomem; - - call->rxtx_annotations = kcalloc(RXRPC_RXTX_BUFF_SIZE, sizeof(u8), gfp); - if (!call->rxtx_annotations) - goto nomem_2; - mutex_init(&call->user_mutex); /* Prevent lockdep reporting a deadlock false positive between the afs @@ -183,12 +173,6 @@ struct rxrpc_call *rxrpc_alloc_call(struct rxrpc_sock *rx, gfp_t gfp, call->rtt_avail = RXRPC_CALL_RTT_AVAIL_MASK; atomic_inc(&rxnet->nr_calls); return call; - -nomem_2: - kfree(call->rxtx_buffer); -nomem: - kmem_cache_free(rxrpc_call_jar, call); - return NULL; } /* @@ -516,12 +500,6 @@ void rxrpc_get_call(struct rxrpc_call *call, enum rxrpc_call_trace op) */ static void rxrpc_cleanup_ring(struct rxrpc_call *call) { - int i; - - for (i = 0; i < RXRPC_RXTX_BUFF_SIZE; i++) { - rxrpc_free_skb(call->rxtx_buffer[i], rxrpc_skb_cleaned); - call->rxtx_buffer[i] = NULL; - } skb_queue_purge(&call->recvmsg_queue); skb_queue_purge(&call->rx_oos_queue); } @@ -658,8 +636,6 @@ static void rxrpc_destroy_call(struct work_struct *work) rxrpc_put_connection(call->conn); rxrpc_put_peer(call->peer); - kfree(call->rxtx_buffer); - kfree(call->rxtx_annotations); kmem_cache_free(rxrpc_call_jar, call); if (atomic_dec_and_test(&rxnet->nr_calls)) wake_up_var(&rxnet->nr_calls); From 1fc4fa2ac93dcf3542f2dc6f7ff88fb022da5116 Mon Sep 17 00:00:00 2001 From: David Howells Date: Mon, 3 Oct 2022 18:49:11 +0100 Subject: [PATCH 0740/2002] rxrpc: Fix congestion management rxrpc has a problem in its congestion management in that it saves the congestion window size (cwnd) from one call to another, but if this is 0 at the time is saved, then the next call may not actually manage to ever transmit anything. To this end: (1) Don't save cwnd between calls, but rather reset back down to the initial cwnd and re-enter slow-start if data transmission is idle for more than an RTT. (2) Preserve ssthresh instead, as that is a handy estimate of pipe capacity. Knowing roughly when to stop slow start and enter congestion avoidance can reduce the tendency to overshoot and drop larger amounts of packets when probing. In future, cwind growth also needs to be constrained when the window isn't being filled due to being application limited. Reported-by: Simon Wilkinson cc: Marc Dionne cc: linux-afs@lists.infradead.org --- include/trace/events/rxrpc.h | 1 + net/rxrpc/ar-internal.h | 9 +++++---- net/rxrpc/call_accept.c | 3 ++- net/rxrpc/call_object.c | 7 ++++++- net/rxrpc/conn_client.c | 3 ++- net/rxrpc/conn_object.c | 2 +- net/rxrpc/input.c | 21 ++++++++++++++++++++- net/rxrpc/output.c | 1 + net/rxrpc/peer_object.c | 7 +------ net/rxrpc/proc.c | 4 ++-- net/rxrpc/sendmsg.c | 22 +++++++++++++++++++--- 11 files changed, 60 insertions(+), 20 deletions(-) diff --git a/include/trace/events/rxrpc.h b/include/trace/events/rxrpc.h index a11de55c3c14..b9886d1df825 100644 --- a/include/trace/events/rxrpc.h +++ b/include/trace/events/rxrpc.h @@ -193,6 +193,7 @@ EM(rxrpc_cong_new_low_nack, " NewLowN") \ EM(rxrpc_cong_no_change, " -") \ EM(rxrpc_cong_progress, " Progres") \ + EM(rxrpc_cong_idle_reset, " IdleRes") \ EM(rxrpc_cong_retransmit_again, " ReTxAgn") \ EM(rxrpc_cong_rtt_window_end, " RttWinE") \ E_(rxrpc_cong_saw_nack, " SawNack") diff --git a/net/rxrpc/ar-internal.h b/net/rxrpc/ar-internal.h index 775eb91aabb2..6bbe28ecf583 100644 --- a/net/rxrpc/ar-internal.h +++ b/net/rxrpc/ar-internal.h @@ -332,7 +332,7 @@ struct rxrpc_peer { u32 rto_j; /* Retransmission timeout in jiffies */ u8 backoff; /* Backoff timeout */ - u8 cong_cwnd; /* Congestion window size */ + u8 cong_ssthresh; /* Congestion slow-start threshold */ }; /* @@ -626,6 +626,7 @@ struct rxrpc_call { u16 tx_backoff; /* Delay to insert due to Tx failure */ u8 tx_winsize; /* Maximum size of Tx window */ #define RXRPC_TX_MAX_WINDOW 128 + ktime_t tx_last_sent; /* Last time a transmission occurred */ /* Received data tracking */ struct sk_buff_head recvmsg_queue; /* Queue of packets ready for recvmsg() */ @@ -687,10 +688,10 @@ struct rxrpc_call { * Summary of a new ACK and the changes it made to the Tx buffer packet states. */ struct rxrpc_ack_summary { + u16 nr_acks; /* Number of ACKs in packet */ + u16 nr_new_acks; /* Number of new ACKs in packet */ + u16 nr_rot_new_acks; /* Number of rotated new ACKs */ u8 ack_reason; - u8 nr_acks; /* Number of ACKs in packet */ - u8 nr_new_acks; /* Number of new ACKs in packet */ - u8 nr_rot_new_acks; /* Number of rotated new ACKs */ bool saw_nacks; /* Saw NACKs in packet */ bool new_low_nack; /* T if new low NACK found */ bool retrans_timeo; /* T if reTx due to timeout happened */ diff --git a/net/rxrpc/call_accept.c b/net/rxrpc/call_accept.c index d8db277d5ebe..48790ee77019 100644 --- a/net/rxrpc/call_accept.c +++ b/net/rxrpc/call_accept.c @@ -324,7 +324,8 @@ static struct rxrpc_call *rxrpc_alloc_incoming_call(struct rxrpc_sock *rx, call->security = conn->security; call->security_ix = conn->security_ix; call->peer = rxrpc_get_peer(conn->params.peer); - call->cong_cwnd = call->peer->cong_cwnd; + call->cong_ssthresh = call->peer->cong_ssthresh; + call->tx_last_sent = ktime_get_real(); return call; } diff --git a/net/rxrpc/call_object.c b/net/rxrpc/call_object.c index aa19daaa487b..1befe22cd301 100644 --- a/net/rxrpc/call_object.c +++ b/net/rxrpc/call_object.c @@ -166,7 +166,12 @@ struct rxrpc_call *rxrpc_alloc_call(struct rxrpc_sock *rx, gfp_t gfp, call->rx_winsize = rxrpc_rx_window_size; call->tx_winsize = 16; - call->cong_cwnd = 2; + if (RXRPC_TX_SMSS > 2190) + call->cong_cwnd = 2; + else if (RXRPC_TX_SMSS > 1095) + call->cong_cwnd = 3; + else + call->cong_cwnd = 4; call->cong_ssthresh = RXRPC_TX_MAX_WINDOW; call->rxnet = rxnet; diff --git a/net/rxrpc/conn_client.c b/net/rxrpc/conn_client.c index 3c9eeb5b750c..f020f308ed9e 100644 --- a/net/rxrpc/conn_client.c +++ b/net/rxrpc/conn_client.c @@ -363,7 +363,8 @@ static struct rxrpc_bundle *rxrpc_prep_call(struct rxrpc_sock *rx, if (!cp->peer) goto error; - call->cong_cwnd = cp->peer->cong_cwnd; + call->tx_last_sent = ktime_get_real(); + call->cong_ssthresh = cp->peer->cong_ssthresh; if (call->cong_cwnd >= call->cong_ssthresh) call->cong_mode = RXRPC_CALL_CONGEST_AVOIDANCE; else diff --git a/net/rxrpc/conn_object.c b/net/rxrpc/conn_object.c index f7ea71ae6159..156bd26daf74 100644 --- a/net/rxrpc/conn_object.c +++ b/net/rxrpc/conn_object.c @@ -207,7 +207,7 @@ void rxrpc_disconnect_call(struct rxrpc_call *call) { struct rxrpc_connection *conn = call->conn; - call->peer->cong_cwnd = call->cong_cwnd; + call->peer->cong_ssthresh = call->cong_ssthresh; if (!hlist_unhashed(&call->error_link)) { spin_lock_bh(&call->peer->lock); diff --git a/net/rxrpc/input.c b/net/rxrpc/input.c index 5c17fed4b60f..bdf70b81addc 100644 --- a/net/rxrpc/input.c +++ b/net/rxrpc/input.c @@ -58,6 +58,25 @@ static void rxrpc_congestion_management(struct rxrpc_call *call, summary->cumulative_acks = cumulative_acks; summary->dup_acks = call->cong_dup_acks; + /* If we haven't transmitted anything for >1RTT, we should reset the + * congestion management state. + */ + if ((call->cong_mode == RXRPC_CALL_SLOW_START || + call->cong_mode == RXRPC_CALL_CONGEST_AVOIDANCE) && + ktime_before(ktime_add_us(call->tx_last_sent, + call->peer->srtt_us >> 3), + ktime_get_real()) + ) { + change = rxrpc_cong_idle_reset; + summary->mode = RXRPC_CALL_SLOW_START; + if (RXRPC_TX_SMSS > 2190) + summary->cwnd = 2; + else if (RXRPC_TX_SMSS > 1095) + summary->cwnd = 3; + else + summary->cwnd = 4; + } + switch (call->cong_mode) { case RXRPC_CALL_SLOW_START: if (summary->saw_nacks) @@ -205,7 +224,7 @@ static bool rxrpc_rotate_tx_window(struct rxrpc_call *call, rxrpc_seq_t to, if (call->acks_lowest_nak == call->acks_hard_ack) { call->acks_lowest_nak = to; - } else if (before_eq(call->acks_lowest_nak, to)) { + } else if (after(to, call->acks_lowest_nak)) { summary->new_low_nack = true; call->acks_lowest_nak = to; } diff --git a/net/rxrpc/output.c b/net/rxrpc/output.c index 2c3f7e4e30d7..46432e70a16b 100644 --- a/net/rxrpc/output.c +++ b/net/rxrpc/output.c @@ -501,6 +501,7 @@ dont_set_request_ack: done: if (ret >= 0) { + call->tx_last_sent = txb->last_sent; if (txb->wire.flags & RXRPC_REQUEST_ACK) { call->peer->rtt_last_req = txb->last_sent; if (call->peer->rtt_count > 1) { diff --git a/net/rxrpc/peer_object.c b/net/rxrpc/peer_object.c index 26d2ae9baaf2..041a51225c5f 100644 --- a/net/rxrpc/peer_object.c +++ b/net/rxrpc/peer_object.c @@ -227,12 +227,7 @@ struct rxrpc_peer *rxrpc_alloc_peer(struct rxrpc_local *local, gfp_t gfp) rxrpc_peer_init_rtt(peer); - if (RXRPC_TX_SMSS > 2190) - peer->cong_cwnd = 2; - else if (RXRPC_TX_SMSS > 1095) - peer->cong_cwnd = 3; - else - peer->cong_cwnd = 4; + peer->cong_ssthresh = RXRPC_TX_MAX_WINDOW; trace_rxrpc_peer(peer->debug_id, rxrpc_peer_new, 1, here); } diff --git a/net/rxrpc/proc.c b/net/rxrpc/proc.c index 0807753ec2dc..fae22a8b38d6 100644 --- a/net/rxrpc/proc.c +++ b/net/rxrpc/proc.c @@ -217,7 +217,7 @@ static int rxrpc_peer_seq_show(struct seq_file *seq, void *v) seq_puts(seq, "Proto Local " " Remote " - " Use CW MTU LastUse RTT RTO\n" + " Use SST MTU LastUse RTT RTO\n" ); return 0; } @@ -235,7 +235,7 @@ static int rxrpc_peer_seq_show(struct seq_file *seq, void *v) lbuff, rbuff, refcount_read(&peer->ref), - peer->cong_cwnd, + peer->cong_ssthresh, peer->mtu, now - peer->last_tx_at, peer->srtt_us >> 3, diff --git a/net/rxrpc/sendmsg.c b/net/rxrpc/sendmsg.c index 9b567aff3e84..e5fd8a95bf71 100644 --- a/net/rxrpc/sendmsg.c +++ b/net/rxrpc/sendmsg.c @@ -22,11 +22,27 @@ */ static bool rxrpc_check_tx_space(struct rxrpc_call *call, rxrpc_seq_t *_tx_win) { - unsigned int win_size = - min_t(unsigned int, call->tx_winsize, - call->cong_cwnd + call->cong_extra); + unsigned int win_size; rxrpc_seq_t tx_win = smp_load_acquire(&call->acks_hard_ack); + /* If we haven't transmitted anything for >1RTT, we should reset the + * congestion management state. + */ + if (ktime_before(ktime_add_us(call->tx_last_sent, + call->peer->srtt_us >> 3), + ktime_get_real())) { + if (RXRPC_TX_SMSS > 2190) + win_size = 2; + else if (RXRPC_TX_SMSS > 1095) + win_size = 3; + else + win_size = 4; + win_size += call->cong_extra; + } else { + win_size = min_t(unsigned int, call->tx_winsize, + call->cong_cwnd + call->cong_extra); + } + if (_tx_win) *_tx_win = tx_win; return call->tx_top - tx_win < win_size; From 30d95efe06e18bd55691902bb4ec873e4b21a754 Mon Sep 17 00:00:00 2001 From: David Howells Date: Fri, 4 Nov 2022 23:13:40 +0000 Subject: [PATCH 0741/2002] rxrpc: Allocate an skcipher each time needed rather than reusing In the rxkad security class, allocate the skcipher used to do packet encryption and decription rather than allocating one up front and reusing it for each packet. Reusing the skcipher precludes doing crypto in parallel. Signed-off-by: David Howells cc: Marc Dionne cc: linux-afs@lists.infradead.org --- net/rxrpc/ar-internal.h | 2 -- net/rxrpc/rxkad.c | 52 ++++++++++++++++++++++------------------- 2 files changed, 28 insertions(+), 26 deletions(-) diff --git a/net/rxrpc/ar-internal.h b/net/rxrpc/ar-internal.h index 6bbe28ecf583..0273a9029229 100644 --- a/net/rxrpc/ar-internal.h +++ b/net/rxrpc/ar-internal.h @@ -583,7 +583,6 @@ struct rxrpc_call { unsigned long expect_term_by; /* When we expect call termination by */ u32 next_rx_timo; /* Timeout for next Rx packet (jif) */ u32 next_req_timo; /* Timeout for next Rx request packet (jif) */ - struct skcipher_request *cipher_req; /* Packet cipher request buffer */ struct timer_list timer; /* Combined event timer */ struct work_struct processor; /* Event processor */ rxrpc_notify_rx_t notify_rx; /* kernel service Rx notification function */ @@ -597,7 +596,6 @@ struct rxrpc_call { struct rxrpc_txbuf *tx_pending; /* Tx buffer being filled */ wait_queue_head_t waitq; /* Wait queue for channel or Tx */ s64 tx_total_len; /* Total length left to be transmitted (or -1) */ - __be32 crypto_buf[2]; /* Temporary packet crypto buffer */ unsigned long user_call_ID; /* user-defined call ID */ unsigned long flags; unsigned long events; diff --git a/net/rxrpc/rxkad.c b/net/rxrpc/rxkad.c index 8fc055587f0e..2706e59bf992 100644 --- a/net/rxrpc/rxkad.c +++ b/net/rxrpc/rxkad.c @@ -233,16 +233,8 @@ static int rxkad_prime_packet_security(struct rxrpc_connection *conn, static struct skcipher_request *rxkad_get_call_crypto(struct rxrpc_call *call) { struct crypto_skcipher *tfm = &call->conn->rxkad.cipher->base; - struct skcipher_request *cipher_req = call->cipher_req; - if (!cipher_req) { - cipher_req = skcipher_request_alloc(tfm, GFP_NOFS); - if (!cipher_req) - return NULL; - call->cipher_req = cipher_req; - } - - return cipher_req; + return skcipher_request_alloc(tfm, GFP_NOFS); } /* @@ -250,9 +242,6 @@ static struct skcipher_request *rxkad_get_call_crypto(struct rxrpc_call *call) */ static void rxkad_free_call_crypto(struct rxrpc_call *call) { - if (call->cipher_req) - skcipher_request_free(call->cipher_req); - call->cipher_req = NULL; } /* @@ -348,6 +337,9 @@ static int rxkad_secure_packet(struct rxrpc_call *call, struct rxrpc_txbuf *txb) struct skcipher_request *req; struct rxrpc_crypt iv; struct scatterlist sg; + union { + __be32 buf[2]; + } crypto __aligned(8); u32 x, y; int ret; @@ -372,17 +364,17 @@ static int rxkad_secure_packet(struct rxrpc_call *call, struct rxrpc_txbuf *txb) /* calculate the security checksum */ x = (ntohl(txb->wire.cid) & RXRPC_CHANNELMASK) << (32 - RXRPC_CIDSHIFT); x |= txb->seq & 0x3fffffff; - call->crypto_buf[0] = txb->wire.callNumber; - call->crypto_buf[1] = htonl(x); + crypto.buf[0] = txb->wire.callNumber; + crypto.buf[1] = htonl(x); - sg_init_one(&sg, call->crypto_buf, 8); + sg_init_one(&sg, crypto.buf, 8); skcipher_request_set_sync_tfm(req, call->conn->rxkad.cipher); skcipher_request_set_callback(req, 0, NULL, NULL); skcipher_request_set_crypt(req, &sg, &sg, 8, iv.x); crypto_skcipher_encrypt(req); skcipher_request_zero(req); - y = ntohl(call->crypto_buf[1]); + y = ntohl(crypto.buf[1]); y = (y >> 16) & 0xffff; if (y == 0) y = 1; /* zero checksums are not permitted */ @@ -403,6 +395,7 @@ static int rxkad_secure_packet(struct rxrpc_call *call, struct rxrpc_txbuf *txb) break; } + skcipher_request_free(req); _leave(" = %d [set %x]", ret, y); return ret; } @@ -593,8 +586,12 @@ static int rxkad_verify_packet(struct rxrpc_call *call, struct sk_buff *skb) struct skcipher_request *req; struct rxrpc_crypt iv; struct scatterlist sg; + union { + __be32 buf[2]; + } crypto __aligned(8); rxrpc_seq_t seq = sp->hdr.seq; bool aborted; + int ret; u16 cksum; u32 x, y; @@ -614,17 +611,17 @@ static int rxkad_verify_packet(struct rxrpc_call *call, struct sk_buff *skb) /* validate the security checksum */ x = (call->cid & RXRPC_CHANNELMASK) << (32 - RXRPC_CIDSHIFT); x |= seq & 0x3fffffff; - call->crypto_buf[0] = htonl(call->call_id); - call->crypto_buf[1] = htonl(x); + crypto.buf[0] = htonl(call->call_id); + crypto.buf[1] = htonl(x); - sg_init_one(&sg, call->crypto_buf, 8); + sg_init_one(&sg, crypto.buf, 8); skcipher_request_set_sync_tfm(req, call->conn->rxkad.cipher); skcipher_request_set_callback(req, 0, NULL, NULL); skcipher_request_set_crypt(req, &sg, &sg, 8, iv.x); crypto_skcipher_encrypt(req); skcipher_request_zero(req); - y = ntohl(call->crypto_buf[1]); + y = ntohl(crypto.buf[1]); cksum = (y >> 16) & 0xffff; if (cksum == 0) cksum = 1; /* zero checksums are not permitted */ @@ -637,15 +634,22 @@ static int rxkad_verify_packet(struct rxrpc_call *call, struct sk_buff *skb) switch (call->conn->params.security_level) { case RXRPC_SECURITY_PLAIN: - return 0; + ret = 0; + break; case RXRPC_SECURITY_AUTH: - return rxkad_verify_packet_1(call, skb, seq, req); + ret = rxkad_verify_packet_1(call, skb, seq, req); + break; case RXRPC_SECURITY_ENCRYPT: - return rxkad_verify_packet_2(call, skb, seq, req); + ret = rxkad_verify_packet_2(call, skb, seq, req); + break; default: - return -ENOANO; + ret = -ENOANO; + break; } + skcipher_request_free(req); + return ret; + protocol_error: if (aborted) rxrpc_send_abort_packet(call); From 8e18be7610aebea50e9327c11afcd5eeaaa06644 Mon Sep 17 00:00:00 2001 From: Yang Li Date: Mon, 7 Nov 2022 14:26:23 +0800 Subject: [PATCH 0742/2002] lib: Fix some kernel-doc comments Make the description of @policy to @p in nla_policy_len() to clear the below warnings: lib/nlattr.c:660: warning: Function parameter or member 'p' not described in 'nla_policy_len' lib/nlattr.c:660: warning: Excess function parameter 'policy' description in 'nla_policy_len' Link: https://bugzilla.openanolis.cn/show_bug.cgi?id=2736 Reported-by: Abaci Robot Signed-off-by: Yang Li Link: https://lore.kernel.org/r/20221107062623.6709-1-yang.lee@linux.alibaba.com Signed-off-by: Jakub Kicinski --- lib/nlattr.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/nlattr.c b/lib/nlattr.c index b67a53e29b8f..9055e8b4d144 100644 --- a/lib/nlattr.c +++ b/lib/nlattr.c @@ -646,7 +646,7 @@ EXPORT_SYMBOL(__nla_validate); /** * nla_policy_len - Determine the max. length of a policy - * @policy: policy to use + * @p: policy to use * @n: number of policies * * Determines the max. length of the policy. It is currently used From 380f9acdf74729d79972b947820f00b42cc26dae Mon Sep 17 00:00:00 2001 From: Yoshihiro Shimoda Date: Mon, 7 Nov 2022 17:10:21 +0900 Subject: [PATCH 0743/2002] net: ethernet: renesas: rswitch: Fix endless loop in error paths Coverity reported that the error path in rswitch_gwca_queue_alloc_skb() has an issue to cause endless loop. So, fix the issue by changing variables' types from u32 to int. After changed the types, rswitch_tx_free() should use rswitch_get_num_cur_queues() to calculate number of current queues. Reported-by: coverity-bot Addresses-Coverity-ID: 1527147 ("Control flow issues") Fixes: 3590918b5d07 ("net: ethernet: renesas: Add support for "Ethernet Switch"") Signed-off-by: Yoshihiro Shimoda Link: https://lore.kernel.org/r/20221107081021.2955122-1-yoshihiro.shimoda.uh@renesas.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/renesas/rswitch.c | 17 +++++++++-------- drivers/net/ethernet/renesas/rswitch.h | 6 +++--- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/drivers/net/ethernet/renesas/rswitch.c b/drivers/net/ethernet/renesas/rswitch.c index 4f7af6393c9e..f3d27aef1286 100644 --- a/drivers/net/ethernet/renesas/rswitch.c +++ b/drivers/net/ethernet/renesas/rswitch.c @@ -219,9 +219,9 @@ static void rswitch_ack_data_irq(struct rswitch_private *priv, int index) iowrite32(BIT(index % 32), priv->addr + offs); } -static u32 rswitch_next_queue_index(struct rswitch_gwca_queue *gq, bool cur, u32 num) +static int rswitch_next_queue_index(struct rswitch_gwca_queue *gq, bool cur, int num) { - u32 index = cur ? gq->cur : gq->dirty; + int index = cur ? gq->cur : gq->dirty; if (index + num >= gq->ring_size) index = (index + num) % gq->ring_size; @@ -231,7 +231,7 @@ static u32 rswitch_next_queue_index(struct rswitch_gwca_queue *gq, bool cur, u32 return index; } -static u32 rswitch_get_num_cur_queues(struct rswitch_gwca_queue *gq) +static int rswitch_get_num_cur_queues(struct rswitch_gwca_queue *gq) { if (gq->cur >= gq->dirty) return gq->cur - gq->dirty; @@ -250,9 +250,9 @@ static bool rswitch_is_queue_rxed(struct rswitch_gwca_queue *gq) } static int rswitch_gwca_queue_alloc_skb(struct rswitch_gwca_queue *gq, - u32 start_index, u32 num) + int start_index, int num) { - u32 i, index; + int i, index; for (i = 0; i < num; i++) { index = (i + start_index) % gq->ring_size; @@ -410,12 +410,12 @@ err: static int rswitch_gwca_queue_ts_fill(struct net_device *ndev, struct rswitch_gwca_queue *gq, - u32 start_index, u32 num) + int start_index, int num) { struct rswitch_device *rdev = netdev_priv(ndev); struct rswitch_ext_ts_desc *desc; dma_addr_t dma_addr; - u32 i, index; + int i, index; for (i = 0; i < num; i++) { index = (i + start_index) % gq->ring_size; @@ -736,7 +736,8 @@ static int rswitch_tx_free(struct net_device *ndev, bool free_txed_only) int free_num = 0; int size; - for (; gq->cur - gq->dirty > 0; gq->dirty = rswitch_next_queue_index(gq, false, 1)) { + for (; rswitch_get_num_cur_queues(gq) > 0; + gq->dirty = rswitch_next_queue_index(gq, false, 1)) { desc = &gq->ring[gq->dirty]; if (free_txed_only && (desc->desc.die_dt & DT_MASK) != DT_FEMPTY) break; diff --git a/drivers/net/ethernet/renesas/rswitch.h b/drivers/net/ethernet/renesas/rswitch.h index 778177ec8d4f..edbdd1b98d3d 100644 --- a/drivers/net/ethernet/renesas/rswitch.h +++ b/drivers/net/ethernet/renesas/rswitch.h @@ -908,9 +908,9 @@ struct rswitch_gwca_queue { struct rswitch_ext_ts_desc *ts_ring; }; dma_addr_t ring_dma; - u32 ring_size; - u32 cur; - u32 dirty; + int ring_size; + int cur; + int dirty; struct sk_buff **skbs; struct net_device *ndev; /* queue to ndev for irq */ From 525c06c81d75690a9b795cc62a758838c1a6b6fe Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Tue, 8 Nov 2022 09:38:58 +0800 Subject: [PATCH 0744/2002] wifi: rtw89: use u32_encode_bits() to fill MAC quota value Coverity reported shift 16 bits could cause sign extension and might get an unexpected value. Since the input values are predefined and no this kind of case, original code is safe so far. But, still changing them to use u32_encode_bits() will be more clear and prevent mistakes in the future. The original message of Coverity is: Suspicious implicit sign extension: "max_cfg->cma0_dma" with type "u16" (16 bits, unsigned) is promoted in "max_cfg->cma0_dma << 16" to type "int" (32 bits, signed), then sign-extended to type "unsigned long" (64 bits, unsigned). If "max_cfg->cma0_dma << 16" is greater than 0x7FFFFFFF, the upper bits of the result will all be 1." Reported-by: coverity-bot Addresses-Coverity-ID: 1527095 ("Integer handling issues") Fixes: e3ec7017f6a2 ("rtw89: add Realtek 802.11ax driver") Signed-off-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20221108013858.10806-1-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/mac.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/mac.c b/drivers/net/wireless/realtek/rtw89/mac.c index ac8e66be327f..ecd603a88134 100644 --- a/drivers/net/wireless/realtek/rtw89/mac.c +++ b/drivers/net/wireless/realtek/rtw89/mac.c @@ -1649,10 +1649,8 @@ static int dle_mix_cfg(struct rtw89_dev *rtwdev, const struct rtw89_dle_mem *cfg #define INVALID_QT_WCPU U16_MAX #define SET_QUOTA_VAL(_min_x, _max_x, _module, _idx) \ do { \ - val = ((_min_x) & \ - B_AX_ ## _module ## _MIN_SIZE_MASK) | \ - (((_max_x) << 16) & \ - B_AX_ ## _module ## _MAX_SIZE_MASK); \ + val = u32_encode_bits(_min_x, B_AX_ ## _module ## _MIN_SIZE_MASK) | \ + u32_encode_bits(_max_x, B_AX_ ## _module ## _MAX_SIZE_MASK); \ rtw89_write32(rtwdev, \ R_AX_ ## _module ## _QTA ## _idx ## _CFG, \ val); \ From 901c247f9687b5aecc950a931a3b0e1930d02bfd Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Tue, 8 Nov 2022 09:42:30 +0800 Subject: [PATCH 0745/2002] wifi: rtw89: 8852b: change debug mask of message of no TX resource 8852B has smaller TX FIFO than others in WiFi chip, so it would be buffer full frequently, but it doesn't affect TX performance. However, it shows verbose debug messages with RTW89_DBG_UNEXP mask that is used to indicate abnormal behavior, so change debug mask to RTW89_DBG_TXRX for 8852B. Signed-off-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20221108014230.11068-1-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/pci.c | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/pci.c b/drivers/net/wireless/realtek/rtw89/pci.c index 07a2e23759f0..7aa0af18cdd5 100644 --- a/drivers/net/wireless/realtek/rtw89/pci.c +++ b/drivers/net/wireless/realtek/rtw89/pci.c @@ -971,8 +971,10 @@ static u32 __rtw89_pci_check_and_reclaim_tx_resource(struct rtw89_dev *rtwdev, struct rtw89_pci *rtwpci = (struct rtw89_pci *)rtwdev->priv; struct rtw89_pci_tx_ring *tx_ring = &rtwpci->tx_rings[txch]; struct rtw89_pci_tx_wd_ring *wd_ring = &tx_ring->wd_ring; + const struct rtw89_chip_info *chip = rtwdev->chip; u32 bd_cnt, wd_cnt, min_cnt = 0; struct rtw89_pci_rx_ring *rx_ring; + enum rtw89_debug_mask debug_mask; u32 cnt; rx_ring = &rtwpci->rx_rings[RTW89_RXCH_RPQ]; @@ -996,10 +998,20 @@ static u32 __rtw89_pci_check_and_reclaim_tx_resource(struct rtw89_dev *rtwdev, bd_cnt = rtw89_pci_get_avail_txbd_num(tx_ring); wd_cnt = wd_ring->curr_num; min_cnt = min(bd_cnt, wd_cnt); - if (min_cnt == 0) - rtw89_debug(rtwdev, rtwpci->low_power ? RTW89_DBG_TXRX : RTW89_DBG_UNEXP, + if (min_cnt == 0) { + /* This message can be frequently shown in low power mode or + * high traffic with 8852B, and we have recognized it as normal + * behavior, so print with mask RTW89_DBG_TXRX in these situations. + */ + if (rtwpci->low_power || chip->chip_id == RTL8852B) + debug_mask = RTW89_DBG_TXRX; + else + debug_mask = RTW89_DBG_UNEXP; + + rtw89_debug(rtwdev, debug_mask, "still no tx resource after reclaim: wd_cnt=%d bd_cnt=%d\n", wd_cnt, bd_cnt); + } out_unlock: spin_unlock_bh(&rtwpci->trx_lock); From dc45398446be10c35e4272537605b75f87c5037d Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Fri, 4 Nov 2022 14:07:23 +0000 Subject: [PATCH 0746/2002] wifi: ath9k: Remove unused variable mismatch MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Variable mismatch is just being incremented and it's never used anywhere else. The variable and the increment are redundant so remove it. Signed-off-by: Colin Ian King Acked-by: Toke Høiland-Jørgensen Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20221104140723.226857-1-colin.i.king@gmail.com --- drivers/net/wireless/ath/ath9k/ar9003_mci.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/net/wireless/ath/ath9k/ar9003_mci.c b/drivers/net/wireless/ath/ath9k/ar9003_mci.c index 9899661f9a60..8d7efd80d97a 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_mci.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_mci.c @@ -585,7 +585,7 @@ static u32 ar9003_mci_wait_for_gpm(struct ath_hw *ah, u8 gpm_type, { struct ath_common *common = ath9k_hw_common(ah); struct ath9k_hw_mci *mci = &ah->btcoex_hw.mci; - u32 *p_gpm = NULL, mismatch = 0, more_data; + u32 *p_gpm = NULL, more_data; u32 offset; u8 recv_type = 0, recv_opcode = 0; bool b_is_bt_cal_done = (gpm_type == MCI_GPM_BT_CAL_DONE); @@ -656,7 +656,6 @@ static u32 ar9003_mci_wait_for_gpm(struct ath_hw *ah, u8 gpm_type, } else { ath_dbg(common, MCI, "MCI GPM subtype not match 0x%x\n", *(p_gpm + 1)); - mismatch++; ar9003_mci_process_gpm_extra(ah, recv_type, recv_opcode, p_gpm); } From a60c0401729851bfe637c2c8af5f759ea985336f Mon Sep 17 00:00:00 2001 From: Zhi-Jun You Date: Mon, 7 Nov 2022 00:22:27 +0800 Subject: [PATCH 0747/2002] wifi: ath10k: Use IEEE80211_SEQ_TO_SN() for seq_ctrl conversion Use IEEE80211_SEQ_TO_SN() macro to convert seq_ctrl to sn for better readability. Signed-off-by: Zhi-Jun You Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20221106162227.1119-1-hujy652@gmail.com --- drivers/net/wireless/ath/ath10k/htt_rx.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/ath/ath10k/htt_rx.c b/drivers/net/wireless/ath/ath10k/htt_rx.c index e76aab973320..3df52906b356 100644 --- a/drivers/net/wireless/ath/ath10k/htt_rx.c +++ b/drivers/net/wireless/ath/ath10k/htt_rx.c @@ -1379,7 +1379,7 @@ static void ath10k_process_rx(struct ath10k *ar, struct sk_buff *skb) ath10k_get_tid(hdr, tid, sizeof(tid)), is_multicast_ether_addr(ieee80211_get_DA(hdr)) ? "mcast" : "ucast", - (__le16_to_cpu(hdr->seq_ctrl) & IEEE80211_SCTL_SEQ) >> 4, + IEEE80211_SEQ_TO_SN(__le16_to_cpu(hdr->seq_ctrl)), (status->encoding == RX_ENC_LEGACY) ? "legacy" : "", (status->encoding == RX_ENC_HT) ? "ht" : "", (status->encoding == RX_ENC_VHT) ? "vht" : "", @@ -1902,7 +1902,7 @@ static bool ath10k_htt_rx_h_frag_pn_check(struct ath10k *ar, last_pn = &peer->frag_tids_last_pn[tid]; new_pn.pn48 = ath10k_htt_rx_h_get_pn(ar, skb, offset, enctype); frag_number = le16_to_cpu(hdr->seq_ctrl) & IEEE80211_SCTL_FRAG; - seq = (__le16_to_cpu(hdr->seq_ctrl) & IEEE80211_SCTL_SEQ) >> 4; + seq = IEEE80211_SEQ_TO_SN(__le16_to_cpu(hdr->seq_ctrl)); if (frag_number == 0) { last_pn->pn48 = new_pn.pn48; @@ -2824,7 +2824,7 @@ static bool ath10k_htt_rx_proc_rx_frag_ind_hl(struct ath10k_htt *htt, hdr_space = ieee80211_hdrlen(hdr->frame_control); sc = __le16_to_cpu(hdr->seq_ctrl); - seq = (sc & IEEE80211_SCTL_SEQ) >> 4; + seq = IEEE80211_SEQ_TO_SN(sc); frag = sc & IEEE80211_SCTL_FRAG; sec_index = MS(rx_desc_info, HTT_RX_DESC_HL_INFO_MCAST_BCAST) ? From ef912fe443adfa15b88645b3bf58c92e5a364167 Mon Sep 17 00:00:00 2001 From: Rakesh Sankaranarayanan Date: Mon, 7 Nov 2022 14:59:18 +0530 Subject: [PATCH 0748/2002] net: dsa: microchip: add ksz9563 in ksz_switch_ops and select based on compatible string Add KSZ9563 inside ksz_switch_chips structure with port_nirq as 3. KSZ9563 use KSZ9893 switch parameters but port_nirq count is 3 for KSZ9563 whereas 2 for KSZ9893. Add KSZ9563 inside ksz_switch_chips as a separate member and from device tree map compatible string into KSZ9563 inside ksz_spi.c and ksz9477_i2c.c. Global Chip ID 1 and 2 registers read value 9893, select sku based on Global Chip ID 4 Register which read 0x1c for KSZ9563. Signed-off-by: Rakesh Sankaranarayanan Signed-off-by: David S. Miller --- drivers/net/dsa/microchip/ksz9477.c | 3 ++- drivers/net/dsa/microchip/ksz9477_i2c.c | 2 +- drivers/net/dsa/microchip/ksz_common.c | 33 +++++++++++++++++++++++-- drivers/net/dsa/microchip/ksz_common.h | 3 +++ drivers/net/dsa/microchip/ksz_spi.c | 2 +- 5 files changed, 38 insertions(+), 5 deletions(-) diff --git a/drivers/net/dsa/microchip/ksz9477.c b/drivers/net/dsa/microchip/ksz9477.c index a6a0321a8931..0d6b40968657 100644 --- a/drivers/net/dsa/microchip/ksz9477.c +++ b/drivers/net/dsa/microchip/ksz9477.c @@ -195,7 +195,8 @@ int ksz9477_reset_switch(struct ksz_device *dev) /* KSZ9893 compatible chips do not support refclk configuration */ if (dev->chip_id == KSZ9893_CHIP_ID || - dev->chip_id == KSZ8563_CHIP_ID) + dev->chip_id == KSZ8563_CHIP_ID || + dev->chip_id == KSZ9563_CHIP_ID) return 0; data8 = SW_ENABLE_REFCLKO; diff --git a/drivers/net/dsa/microchip/ksz9477_i2c.c b/drivers/net/dsa/microchip/ksz9477_i2c.c index 3763930dc6fc..55146584e9b5 100644 --- a/drivers/net/dsa/microchip/ksz9477_i2c.c +++ b/drivers/net/dsa/microchip/ksz9477_i2c.c @@ -101,7 +101,7 @@ static const struct of_device_id ksz9477_dt_ids[] = { }, { .compatible = "microchip,ksz9563", - .data = &ksz_switch_chips[KSZ9893] + .data = &ksz_switch_chips[KSZ9563] }, { .compatible = "microchip,ksz8563", diff --git a/drivers/net/dsa/microchip/ksz_common.c b/drivers/net/dsa/microchip/ksz_common.c index d612181b3226..f7e414e747ed 100644 --- a/drivers/net/dsa/microchip/ksz_common.c +++ b/drivers/net/dsa/microchip/ksz_common.c @@ -1282,6 +1282,31 @@ const struct ksz_chip_data ksz_switch_chips[] = { .gbit_capable = {true, true, true}, }, + [KSZ9563] = { + .chip_id = KSZ9563_CHIP_ID, + .dev_name = "KSZ9563", + .num_vlans = 4096, + .num_alus = 4096, + .num_statics = 16, + .cpu_ports = 0x07, /* can be configured as cpu port */ + .port_cnt = 3, /* total port count */ + .port_nirqs = 3, + .ops = &ksz9477_dev_ops, + .mib_names = ksz9477_mib_names, + .mib_cnt = ARRAY_SIZE(ksz9477_mib_names), + .reg_mib_cnt = MIB_COUNTER_NUM, + .regs = ksz9477_regs, + .masks = ksz9477_masks, + .shifts = ksz9477_shifts, + .xmii_ctrl0 = ksz9477_xmii_ctrl0, + .xmii_ctrl1 = ksz8795_xmii_ctrl1, /* Same as ksz8795 */ + .supports_mii = {false, false, true}, + .supports_rmii = {false, false, true}, + .supports_rgmii = {false, false, true}, + .internal_phy = {true, true, false}, + .gbit_capable = {true, true, true}, + }, + [KSZ9567] = { .chip_id = KSZ9567_CHIP_ID, .dev_name = "KSZ9567", @@ -2389,7 +2414,8 @@ static enum dsa_tag_protocol ksz_get_tag_protocol(struct dsa_switch *ds, if (dev->chip_id == KSZ8830_CHIP_ID || dev->chip_id == KSZ8563_CHIP_ID || - dev->chip_id == KSZ9893_CHIP_ID) + dev->chip_id == KSZ9893_CHIP_ID || + dev->chip_id == KSZ9563_CHIP_ID) proto = DSA_TAG_PROTO_KSZ9893; if (dev->chip_id == KSZ9477_CHIP_ID || @@ -2509,7 +2535,8 @@ static void ksz_set_xmii(struct ksz_device *dev, int port, data8 |= bitval[P_RGMII_SEL]; /* On KSZ9893, disable RGMII in-band status support */ if (dev->chip_id == KSZ9893_CHIP_ID || - dev->chip_id == KSZ8563_CHIP_ID) + dev->chip_id == KSZ8563_CHIP_ID || + dev->chip_id == KSZ9563_CHIP_ID) data8 &= ~P_MII_MAC_MODE; break; default: @@ -2782,6 +2809,8 @@ static int ksz_switch_detect(struct ksz_device *dev) if (id4 == SKU_ID_KSZ8563) dev->chip_id = KSZ8563_CHIP_ID; + else if (id4 == SKU_ID_KSZ9563) + dev->chip_id = KSZ9563_CHIP_ID; else dev->chip_id = KSZ9893_CHIP_ID; diff --git a/drivers/net/dsa/microchip/ksz_common.h b/drivers/net/dsa/microchip/ksz_common.h index 9cfa179575ce..c6726cbd5465 100644 --- a/drivers/net/dsa/microchip/ksz_common.h +++ b/drivers/net/dsa/microchip/ksz_common.h @@ -154,6 +154,7 @@ enum ksz_model { KSZ9896, KSZ9897, KSZ9893, + KSZ9563, KSZ9567, LAN9370, LAN9371, @@ -172,6 +173,7 @@ enum ksz_chip_id { KSZ9896_CHIP_ID = 0x00989600, KSZ9897_CHIP_ID = 0x00989700, KSZ9893_CHIP_ID = 0x00989300, + KSZ9563_CHIP_ID = 0x00956300, KSZ9567_CHIP_ID = 0x00956700, LAN9370_CHIP_ID = 0x00937000, LAN9371_CHIP_ID = 0x00937100, @@ -551,6 +553,7 @@ static inline int is_lan937x(struct ksz_device *dev) /* KSZ9893, KSZ9563, KSZ8563 specific register */ #define REG_CHIP_ID4 0x0f #define SKU_ID_KSZ8563 0x3c +#define SKU_ID_KSZ9563 0x1c /* Driver set switch broadcast storm protection at 10% rate. */ #define BROADCAST_STORM_PROT_RATE 10 diff --git a/drivers/net/dsa/microchip/ksz_spi.c b/drivers/net/dsa/microchip/ksz_spi.c index 1b6ab891b986..4f2186779082 100644 --- a/drivers/net/dsa/microchip/ksz_spi.c +++ b/drivers/net/dsa/microchip/ksz_spi.c @@ -163,7 +163,7 @@ static const struct of_device_id ksz_dt_ids[] = { }, { .compatible = "microchip,ksz9563", - .data = &ksz_switch_chips[KSZ9893] + .data = &ksz_switch_chips[KSZ9563] }, { .compatible = "microchip,ksz8563", From a9c6db3bc9d872a7b808928c5df342670aac3336 Mon Sep 17 00:00:00 2001 From: Rakesh Sankaranarayanan Date: Mon, 7 Nov 2022 14:59:19 +0530 Subject: [PATCH 0749/2002] net: dsa: microchip: add irq in i2c probe add device irq in i2c probe function. Signed-off-by: Rakesh Sankaranarayanan Signed-off-by: David S. Miller --- drivers/net/dsa/microchip/ksz9477_i2c.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/dsa/microchip/ksz9477_i2c.c b/drivers/net/dsa/microchip/ksz9477_i2c.c index 55146584e9b5..caa9acf1495c 100644 --- a/drivers/net/dsa/microchip/ksz9477_i2c.c +++ b/drivers/net/dsa/microchip/ksz9477_i2c.c @@ -41,6 +41,8 @@ static int ksz9477_i2c_probe(struct i2c_client *i2c, if (i2c->dev.platform_data) dev->pdata = i2c->dev.platform_data; + dev->irq = i2c->irq; + ret = ksz_switch_register(dev); /* Main DSA driver may not be started yet. */ From e06999c3dc62c79fa897eec1293faa3c2bd8892f Mon Sep 17 00:00:00 2001 From: Rakesh Sankaranarayanan Date: Mon, 7 Nov 2022 14:59:20 +0530 Subject: [PATCH 0750/2002] net: dsa: microchip: add error checking for ksz_pwrite Add status validation for port register write inside lan937x_change_mtu. ksz_pwrite and ksz_pread api's are updated with return type int (Reference patch mentioned below). Update lan937x_change_mtu with status validation for ksz_pwrite16(). Link: https://patchwork.kernel.org/project/netdevbpf/patch/20220826105634.3855578-6-o.rempel@pengutronix.de/ Signed-off-by: Rakesh Sankaranarayanan Signed-off-by: David S. Miller --- drivers/net/dsa/microchip/lan937x_main.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/net/dsa/microchip/lan937x_main.c b/drivers/net/dsa/microchip/lan937x_main.c index 7e4f307a0387..06d3d0308cba 100644 --- a/drivers/net/dsa/microchip/lan937x_main.c +++ b/drivers/net/dsa/microchip/lan937x_main.c @@ -242,7 +242,11 @@ int lan937x_change_mtu(struct ksz_device *dev, int port, int new_mtu) } /* Write the frame size in PORT_MAX_FR_SIZE register */ - ksz_pwrite16(dev, port, PORT_MAX_FR_SIZE, new_mtu); + ret = ksz_pwrite16(dev, port, PORT_MAX_FR_SIZE, new_mtu); + if (ret) { + dev_err(ds->dev, "failed to update mtu for port %d\n", port); + return ret; + } return 0; } From 4630d1420f841b2b112928359c4dc148d9a7d1f8 Mon Sep 17 00:00:00 2001 From: Rakesh Sankaranarayanan Date: Mon, 7 Nov 2022 14:59:21 +0530 Subject: [PATCH 0751/2002] net: dsa: microchip: ksz8563: Add number of port irq KSZ8563 have three port interrupts: PTP, PHY and ACL. Add port_nirq as 3 for KSZ8563 inside ksz_chip_data. Signed-off-by: Rakesh Sankaranarayanan Signed-off-by: David S. Miller --- drivers/net/dsa/microchip/ksz_common.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/dsa/microchip/ksz_common.c b/drivers/net/dsa/microchip/ksz_common.c index f7e414e747ed..8c8db315317d 100644 --- a/drivers/net/dsa/microchip/ksz_common.c +++ b/drivers/net/dsa/microchip/ksz_common.c @@ -1039,6 +1039,7 @@ const struct ksz_chip_data ksz_switch_chips[] = { .num_statics = 16, .cpu_ports = 0x07, /* can be configured as cpu port */ .port_cnt = 3, /* total port count */ + .port_nirqs = 3, .ops = &ksz9477_dev_ops, .mib_names = ksz9477_mib_names, .mib_cnt = ARRAY_SIZE(ksz9477_mib_names), From 9b1833170632e278971358c8c055d5960c6c7a05 Mon Sep 17 00:00:00 2001 From: Rakesh Sankaranarayanan Date: Mon, 7 Nov 2022 14:59:22 +0530 Subject: [PATCH 0752/2002] net: dsa: microchip: add dev_err_probe in probe functions Probe functions uses normal dev_err() to check error conditions and print messages. Replace dev_err() with dev_err_probe() to have more standardized format and error logging. Signed-off-by: Rakesh Sankaranarayanan Signed-off-by: David S. Miller --- drivers/net/dsa/microchip/ksz8863_smi.c | 9 ++++----- drivers/net/dsa/microchip/ksz9477_i2c.c | 8 +++----- drivers/net/dsa/microchip/ksz_spi.c | 8 +++----- 3 files changed, 10 insertions(+), 15 deletions(-) diff --git a/drivers/net/dsa/microchip/ksz8863_smi.c b/drivers/net/dsa/microchip/ksz8863_smi.c index ddb40838181e..2f4623f3bd85 100644 --- a/drivers/net/dsa/microchip/ksz8863_smi.c +++ b/drivers/net/dsa/microchip/ksz8863_smi.c @@ -152,11 +152,10 @@ static int ksz8863_smi_probe(struct mdio_device *mdiodev) ®map_smi[i], dev, &rc); if (IS_ERR(dev->regmap[i])) { - ret = PTR_ERR(dev->regmap[i]); - dev_err(&mdiodev->dev, - "Failed to initialize regmap%i: %d\n", - ksz8863_regmap_config[i].val_bits, ret); - return ret; + return dev_err_probe(&mdiodev->dev, + PTR_ERR(dev->regmap[i]), + "Failed to initialize regmap%i\n", + ksz8863_regmap_config[i].val_bits); } } diff --git a/drivers/net/dsa/microchip/ksz9477_i2c.c b/drivers/net/dsa/microchip/ksz9477_i2c.c index caa9acf1495c..db4aec0a51dc 100644 --- a/drivers/net/dsa/microchip/ksz9477_i2c.c +++ b/drivers/net/dsa/microchip/ksz9477_i2c.c @@ -30,11 +30,9 @@ static int ksz9477_i2c_probe(struct i2c_client *i2c, rc.lock_arg = &dev->regmap_mutex; dev->regmap[i] = devm_regmap_init_i2c(i2c, &rc); if (IS_ERR(dev->regmap[i])) { - ret = PTR_ERR(dev->regmap[i]); - dev_err(&i2c->dev, - "Failed to initialize regmap%i: %d\n", - ksz9477_regmap_config[i].val_bits, ret); - return ret; + return dev_err_probe(&i2c->dev, PTR_ERR(dev->regmap[i]), + "Failed to initialize regmap%i\n", + ksz9477_regmap_config[i].val_bits); } } diff --git a/drivers/net/dsa/microchip/ksz_spi.c b/drivers/net/dsa/microchip/ksz_spi.c index 4f2186779082..96c52e8fb51b 100644 --- a/drivers/net/dsa/microchip/ksz_spi.c +++ b/drivers/net/dsa/microchip/ksz_spi.c @@ -71,11 +71,9 @@ static int ksz_spi_probe(struct spi_device *spi) dev->regmap[i] = devm_regmap_init_spi(spi, &rc); if (IS_ERR(dev->regmap[i])) { - ret = PTR_ERR(dev->regmap[i]); - dev_err(&spi->dev, - "Failed to initialize regmap%i: %d\n", - regmap_config[i].val_bits, ret); - return ret; + return dev_err_probe(&spi->dev, PTR_ERR(dev->regmap[i]), + "Failed to initialize regmap%i\n", + regmap_config[i].val_bits); } } From bd039b5ea2a91ea707ee8539df26456bd5be80af Mon Sep 17 00:00:00 2001 From: Andy Ren Date: Mon, 7 Nov 2022 09:42:42 -0800 Subject: [PATCH 0753/2002] net/core: Allow live renaming when an interface is up Allow a network interface to be renamed when the interface is up. As described in the netconsole documentation [1], when netconsole is used as a built-in, it will bring up the specified interface as soon as possible. As a result, user space will not be able to rename the interface since the kernel disallows renaming of interfaces that are administratively up unless the 'IFF_LIVE_RENAME_OK' private flag was set by the kernel. The original solution [2] to this problem was to add a new parameter to the netconsole configuration parameters that allows renaming of the interface used by netconsole while it is administratively up. However, during the discussion that followed, it became apparent that we have no reason to keep the current restriction and instead we should allow user space to rename interfaces regardless of their administrative state: 1. The restriction was put in place over 20 years ago when renaming was only possible via IOCTL and before rtnetlink started notifying user space about such changes like it does today. 2. The 'IFF_LIVE_RENAME_OK' flag was added over 3 years ago in version 5.2 and no regressions were reported. 3. In-kernel listeners to 'NETDEV_CHANGENAME' do not seem to care about the administrative state of interface. Therefore, allow user space to rename running interfaces by removing the restriction and the associated 'IFF_LIVE_RENAME_OK' flag. Help in possible triage by emitting a message to the kernel log that an interface was renamed while UP. [1] https://www.kernel.org/doc/Documentation/networking/netconsole.rst [2] https://lore.kernel.org/netdev/20221102002420.2613004-1-andy.ren@getcruise.com/ Signed-off-by: Andy Ren Reviewed-by: Ido Schimmel Reviewed-by: David Ahern Signed-off-by: David S. Miller --- include/linux/netdevice.h | 4 +--- net/core/dev.c | 19 ++----------------- net/core/failover.c | 6 +++--- 3 files changed, 6 insertions(+), 23 deletions(-) diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index d45713a06568..4be87b89e481 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -1650,7 +1650,6 @@ struct net_device_ops { * @IFF_FAILOVER: device is a failover master device * @IFF_FAILOVER_SLAVE: device is lower dev of a failover master device * @IFF_L3MDEV_RX_HANDLER: only invoke the rx handler of L3 master device - * @IFF_LIVE_RENAME_OK: rename is allowed while device is up and running * @IFF_TX_SKB_NO_LINEAR: device/driver is capable of xmitting frames with * skb_headlen(skb) == 0 (data starts from frag0) * @IFF_CHANGE_PROTO_DOWN: device supports setting carrier via IFLA_PROTO_DOWN @@ -1686,7 +1685,7 @@ enum netdev_priv_flags { IFF_FAILOVER = 1<<27, IFF_FAILOVER_SLAVE = 1<<28, IFF_L3MDEV_RX_HANDLER = 1<<29, - IFF_LIVE_RENAME_OK = 1<<30, + /* was IFF_LIVE_RENAME_OK */ IFF_TX_SKB_NO_LINEAR = BIT_ULL(31), IFF_CHANGE_PROTO_DOWN = BIT_ULL(32), }; @@ -1721,7 +1720,6 @@ enum netdev_priv_flags { #define IFF_FAILOVER IFF_FAILOVER #define IFF_FAILOVER_SLAVE IFF_FAILOVER_SLAVE #define IFF_L3MDEV_RX_HANDLER IFF_L3MDEV_RX_HANDLER -#define IFF_LIVE_RENAME_OK IFF_LIVE_RENAME_OK #define IFF_TX_SKB_NO_LINEAR IFF_TX_SKB_NO_LINEAR /* Specifies the type of the struct net_device::ml_priv pointer */ diff --git a/net/core/dev.c b/net/core/dev.c index 3bacee3bee78..707de6b841d0 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -1163,22 +1163,6 @@ int dev_change_name(struct net_device *dev, const char *newname) net = dev_net(dev); - /* Some auto-enslaved devices e.g. failover slaves are - * special, as userspace might rename the device after - * the interface had been brought up and running since - * the point kernel initiated auto-enslavement. Allow - * live name change even when these slave devices are - * up and running. - * - * Typically, users of these auto-enslaving devices - * don't actually care about slave name change, as - * they are supposed to operate on master interface - * directly. - */ - if (dev->flags & IFF_UP && - likely(!(dev->priv_flags & IFF_LIVE_RENAME_OK))) - return -EBUSY; - down_write(&devnet_rename_sem); if (strncmp(newname, dev->name, IFNAMSIZ) == 0) { @@ -1195,7 +1179,8 @@ int dev_change_name(struct net_device *dev, const char *newname) } if (oldname[0] && !strchr(oldname, '%')) - netdev_info(dev, "renamed from %s\n", oldname); + netdev_info(dev, "renamed from %s%s\n", oldname, + dev->flags & IFF_UP ? " (while UP)" : ""); old_assign_type = dev->name_assign_type; dev->name_assign_type = NET_NAME_RENAMED; diff --git a/net/core/failover.c b/net/core/failover.c index 864d2d83eff4..655411c4ca51 100644 --- a/net/core/failover.c +++ b/net/core/failover.c @@ -80,14 +80,14 @@ static int failover_slave_register(struct net_device *slave_dev) goto err_upper_link; } - slave_dev->priv_flags |= (IFF_FAILOVER_SLAVE | IFF_LIVE_RENAME_OK); + slave_dev->priv_flags |= IFF_FAILOVER_SLAVE; if (fops && fops->slave_register && !fops->slave_register(slave_dev, failover_dev)) return NOTIFY_OK; netdev_upper_dev_unlink(slave_dev, failover_dev); - slave_dev->priv_flags &= ~(IFF_FAILOVER_SLAVE | IFF_LIVE_RENAME_OK); + slave_dev->priv_flags &= ~IFF_FAILOVER_SLAVE; err_upper_link: netdev_rx_handler_unregister(slave_dev); done: @@ -121,7 +121,7 @@ int failover_slave_unregister(struct net_device *slave_dev) netdev_rx_handler_unregister(slave_dev); netdev_upper_dev_unlink(slave_dev, failover_dev); - slave_dev->priv_flags &= ~(IFF_FAILOVER_SLAVE | IFF_LIVE_RENAME_OK); + slave_dev->priv_flags &= ~IFF_FAILOVER_SLAVE; if (fops && fops->slave_unregister && !fops->slave_unregister(slave_dev, failover_dev)) From 154ba79c9f160e652a2c9c46435b928b3bfae11f Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Tue, 8 Nov 2022 12:41:28 -0800 Subject: [PATCH 0754/2002] genetlink: correctly begin the iteration over policies The return value from genl_op_iter_init() only tells us if there are any policies but to begin the iteration (and therefore load the first entry) we need to call genl_op_iter_next(). Note that it's safe to call genl_op_iter_next() on a family with no ops, it will just return false. This may lead to various crashes, a warning in netlink_policy_dump_get_policy_idx() when policy is not found or.. no problem at all if the kmalloc'ed memory happens to be zeroed. Fixes: b502b3185cd6 ("genetlink: use iterator in the op to policy map dumping") Link: https://lore.kernel.org/r/20221108204128.330287-1-kuba@kernel.org Signed-off-by: Jakub Kicinski --- net/netlink/genetlink.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/net/netlink/genetlink.c b/net/netlink/genetlink.c index 362a61179036..9b7dfc45dd67 100644 --- a/net/netlink/genetlink.c +++ b/net/netlink/genetlink.c @@ -1437,7 +1437,9 @@ static int ctrl_dumppolicy_start(struct netlink_callback *cb) ctx->op_iter = kmalloc(sizeof(*ctx->op_iter), GFP_KERNEL); if (!ctx->op_iter) return -ENOMEM; - ctx->dump_map = genl_op_iter_init(rt, ctx->op_iter); + + genl_op_iter_init(rt, ctx->op_iter); + ctx->dump_map = genl_op_iter_next(ctx->op_iter); for (genl_op_iter_init(rt, &i); genl_op_iter_next(&i); ) { if (i.doit.policy) { From 3e52fba03a20234abc65a656cef063a1045d9723 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Tue, 8 Nov 2022 14:22:06 +0100 Subject: [PATCH 0755/2002] net: introduce a helper to move notifier block to different namespace Currently, net_dev() netdev notifier variant follows the netdev with per-net notifier from namespace to namespace. This is implemented by move_netdevice_notifiers_dev_net() helper. For devlink it is needed to re-register per-net notifier during devlink reload. Introduce a new helper called move_netdevice_notifier_net() and share the unregister/register code with existing move_netdevice_notifiers_dev_net() helper. Signed-off-by: Jiri Pirko Reviewed-by: Ido Schimmel Signed-off-by: Jakub Kicinski --- include/linux/netdevice.h | 2 ++ net/core/dev.c | 22 ++++++++++++++++++---- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 4be87b89e481..02a2318da7c7 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -2826,6 +2826,8 @@ int unregister_netdevice_notifier(struct notifier_block *nb); int register_netdevice_notifier_net(struct net *net, struct notifier_block *nb); int unregister_netdevice_notifier_net(struct net *net, struct notifier_block *nb); +void move_netdevice_notifier_net(struct net *src_net, struct net *dst_net, + struct notifier_block *nb); int register_netdevice_notifier_dev_net(struct net_device *dev, struct notifier_block *nb, struct netdev_net_notifier *nn); diff --git a/net/core/dev.c b/net/core/dev.c index 707de6b841d0..117e830cabb0 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -1861,6 +1861,22 @@ int unregister_netdevice_notifier_net(struct net *net, } EXPORT_SYMBOL(unregister_netdevice_notifier_net); +static void __move_netdevice_notifier_net(struct net *src_net, + struct net *dst_net, + struct notifier_block *nb) +{ + __unregister_netdevice_notifier_net(src_net, nb); + __register_netdevice_notifier_net(dst_net, nb, true); +} + +void move_netdevice_notifier_net(struct net *src_net, struct net *dst_net, + struct notifier_block *nb) +{ + rtnl_lock(); + __move_netdevice_notifier_net(src_net, dst_net, nb); + rtnl_unlock(); +} + int register_netdevice_notifier_dev_net(struct net_device *dev, struct notifier_block *nb, struct netdev_net_notifier *nn) @@ -1897,10 +1913,8 @@ static void move_netdevice_notifiers_dev_net(struct net_device *dev, { struct netdev_net_notifier *nn; - list_for_each_entry(nn, &dev->net_notifier_list, list) { - __unregister_netdevice_notifier_net(dev_net(dev), nn->nb); - __register_netdevice_notifier_net(net, nn->nb, true); - } + list_for_each_entry(nn, &dev->net_notifier_list, list) + __move_netdevice_notifier_net(dev_net(dev), net, nn->nb); } /** From 15feb56e30efea95992f5c572cee753db205eb7b Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Tue, 8 Nov 2022 14:22:07 +0100 Subject: [PATCH 0756/2002] net: devlink: move netdev notifier block to dest namespace during reload The notifier block tracking netdev changes in devlink is registered during devlink_alloc() per-net, it is then unregistered in devlink_free(). When devlink moves from net namespace to another one, the notifier block needs to move along. Fix this by adding forgotten call to move the block. Reported-by: Ido Schimmel Fixes: 02a68a47eade ("net: devlink: track netdev with devlink_port assigned") Reviewed-by: Ido Schimmel Tested-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: Jakub Kicinski --- net/core/devlink.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/net/core/devlink.c b/net/core/devlink.c index 40fcdded57e6..ea0b319385fc 100644 --- a/net/core/devlink.c +++ b/net/core/devlink.c @@ -4502,8 +4502,11 @@ static int devlink_reload(struct devlink *devlink, struct net *dest_net, if (err) return err; - if (dest_net && !net_eq(dest_net, curr_net)) + if (dest_net && !net_eq(dest_net, curr_net)) { + move_netdevice_notifier_net(curr_net, dest_net, + &devlink->netdevice_nb); write_pnet(&devlink->_net, dest_net); + } err = devlink->ops->reload_up(devlink, action, limit, actions_performed, extack); devlink_reload_failed_set(devlink, !!err); From 9baedc3c8780256bfc68d318c31d0081cefe92a3 Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Tue, 8 Nov 2022 11:47:07 +0100 Subject: [PATCH 0757/2002] bridge: switchdev: Let device drivers determine FDB offload indication Currently, FDB entries that are notified to the bridge via 'SWITCHDEV_FDB_ADD_TO_BRIDGE' are always marked as offloaded. With MAB enabled, this will no longer be universally true. Device drivers will report locked FDB entries to the bridge to let it know that the corresponding hosts required authorization, but it does not mean that these entries are necessarily programmed in the underlying hardware. Solve this by determining the offload indication based of the 'offloaded' bit in the FDB notification. Signed-off-by: Ido Schimmel Reviewed-by: Petr Machata Signed-off-by: Petr Machata Reviewed-by: Vladimir Oltean Acked-by: Nikolay Aleksandrov Signed-off-by: Jakub Kicinski --- net/bridge/br.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/bridge/br.c b/net/bridge/br.c index 96e91d69a9a8..145999b8c355 100644 --- a/net/bridge/br.c +++ b/net/bridge/br.c @@ -172,7 +172,7 @@ static int br_switchdev_event(struct notifier_block *unused, break; } br_fdb_offloaded_set(br, p, fdb_info->addr, - fdb_info->vid, true); + fdb_info->vid, fdb_info->offloaded); break; case SWITCHDEV_FDB_DEL_TO_BRIDGE: fdb_info = ptr; From 27fabd02abf30a9df9899f92d467591c7eabb1ba Mon Sep 17 00:00:00 2001 From: "Hans J. Schultz" Date: Tue, 8 Nov 2022 11:47:08 +0100 Subject: [PATCH 0758/2002] bridge: switchdev: Allow device drivers to install locked FDB entries When the bridge is offloaded to hardware, FDB entries are learned and aged-out by the hardware. Some device drivers synchronize the hardware and software FDBs by generating switchdev events towards the bridge. When a port is locked, the hardware must not learn autonomously, as otherwise any host will blindly gain authorization. Instead, the hardware should generate events regarding hosts that are trying to gain authorization and their MAC addresses should be notified by the device driver as locked FDB entries towards the bridge driver. Allow device drivers to notify the bridge driver about such entries by extending the 'switchdev_notifier_fdb_info' structure with the 'locked' bit. The bit can only be set by device drivers and not by the bridge driver. Prevent a locked entry from being installed if MAB is not enabled on the bridge port. If an entry already exists in the bridge driver, reject the locked entry if the current entry does not have the "locked" flag set or if it points to a different port. The same semantics are implemented in the software data path. Signed-off-by: Hans J. Schultz Signed-off-by: Ido Schimmel Reviewed-by: Petr Machata Signed-off-by: Petr Machata Reviewed-by: Vladimir Oltean Acked-by: Nikolay Aleksandrov Signed-off-by: Jakub Kicinski --- include/net/switchdev.h | 1 + net/bridge/br.c | 3 ++- net/bridge/br_fdb.c | 22 ++++++++++++++++++++-- net/bridge/br_private.h | 2 +- net/bridge/br_switchdev.c | 4 ++++ 5 files changed, 28 insertions(+), 4 deletions(-) diff --git a/include/net/switchdev.h b/include/net/switchdev.h index 7dcdc97c0bc3..ca0312b78294 100644 --- a/include/net/switchdev.h +++ b/include/net/switchdev.h @@ -248,6 +248,7 @@ struct switchdev_notifier_fdb_info { u16 vid; u8 added_by_user:1, is_local:1, + locked:1, offloaded:1; }; diff --git a/net/bridge/br.c b/net/bridge/br.c index 145999b8c355..4f5098d33a46 100644 --- a/net/bridge/br.c +++ b/net/bridge/br.c @@ -166,7 +166,8 @@ static int br_switchdev_event(struct notifier_block *unused, case SWITCHDEV_FDB_ADD_TO_BRIDGE: fdb_info = ptr; err = br_fdb_external_learn_add(br, p, fdb_info->addr, - fdb_info->vid, false); + fdb_info->vid, + fdb_info->locked, false); if (err) { err = notifier_from_errno(err); break; diff --git a/net/bridge/br_fdb.c b/net/bridge/br_fdb.c index 3b83af4458b8..e69a872bfc1d 100644 --- a/net/bridge/br_fdb.c +++ b/net/bridge/br_fdb.c @@ -1139,7 +1139,7 @@ static int __br_fdb_add(struct ndmsg *ndm, struct net_bridge *br, "FDB entry towards bridge must be permanent"); return -EINVAL; } - err = br_fdb_external_learn_add(br, p, addr, vid, true); + err = br_fdb_external_learn_add(br, p, addr, vid, false, true); } else { spin_lock_bh(&br->hash_lock); err = fdb_add_entry(br, p, addr, ndm, nlh_flags, vid, nfea_tb); @@ -1377,7 +1377,7 @@ void br_fdb_unsync_static(struct net_bridge *br, struct net_bridge_port *p) } int br_fdb_external_learn_add(struct net_bridge *br, struct net_bridge_port *p, - const unsigned char *addr, u16 vid, + const unsigned char *addr, u16 vid, bool locked, bool swdev_notify) { struct net_bridge_fdb_entry *fdb; @@ -1386,6 +1386,9 @@ int br_fdb_external_learn_add(struct net_bridge *br, struct net_bridge_port *p, trace_br_fdb_external_learn_add(br, p, addr, vid); + if (locked && (!p || !(p->flags & BR_PORT_MAB))) + return -EINVAL; + spin_lock_bh(&br->hash_lock); fdb = br_fdb_find(br, addr, vid); @@ -1398,6 +1401,9 @@ int br_fdb_external_learn_add(struct net_bridge *br, struct net_bridge_port *p, if (!p) flags |= BIT(BR_FDB_LOCAL); + if (locked) + flags |= BIT(BR_FDB_LOCKED); + fdb = fdb_create(br, p, addr, vid, flags); if (!fdb) { err = -ENOMEM; @@ -1405,6 +1411,13 @@ int br_fdb_external_learn_add(struct net_bridge *br, struct net_bridge_port *p, } fdb_notify(br, fdb, RTM_NEWNEIGH, swdev_notify); } else { + if (locked && + (!test_bit(BR_FDB_LOCKED, &fdb->flags) || + READ_ONCE(fdb->dst) != p)) { + err = -EINVAL; + goto err_unlock; + } + fdb->updated = jiffies; if (READ_ONCE(fdb->dst) != p) { @@ -1421,6 +1434,11 @@ int br_fdb_external_learn_add(struct net_bridge *br, struct net_bridge_port *p, modified = true; } + if (locked != test_bit(BR_FDB_LOCKED, &fdb->flags)) { + change_bit(BR_FDB_LOCKED, &fdb->flags); + modified = true; + } + if (swdev_notify) set_bit(BR_FDB_ADDED_BY_USER, &fdb->flags); diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h index 4ce8b8e5ae0b..4c4fda930068 100644 --- a/net/bridge/br_private.h +++ b/net/bridge/br_private.h @@ -811,7 +811,7 @@ int br_fdb_sync_static(struct net_bridge *br, struct net_bridge_port *p); void br_fdb_unsync_static(struct net_bridge *br, struct net_bridge_port *p); int br_fdb_external_learn_add(struct net_bridge *br, struct net_bridge_port *p, const unsigned char *addr, u16 vid, - bool swdev_notify); + bool locked, bool swdev_notify); int br_fdb_external_learn_del(struct net_bridge *br, struct net_bridge_port *p, const unsigned char *addr, u16 vid, bool swdev_notify); diff --git a/net/bridge/br_switchdev.c b/net/bridge/br_switchdev.c index 8f3d76c751dd..8a0abe35137d 100644 --- a/net/bridge/br_switchdev.c +++ b/net/bridge/br_switchdev.c @@ -136,6 +136,7 @@ static void br_switchdev_fdb_populate(struct net_bridge *br, item->added_by_user = test_bit(BR_FDB_ADDED_BY_USER, &fdb->flags); item->offloaded = test_bit(BR_FDB_OFFLOADED, &fdb->flags); item->is_local = test_bit(BR_FDB_LOCAL, &fdb->flags); + item->locked = false; item->info.dev = (!p || item->is_local) ? br->dev : p->dev; item->info.ctx = ctx; } @@ -146,6 +147,9 @@ br_switchdev_fdb_notify(struct net_bridge *br, { struct switchdev_notifier_fdb_info item; + if (test_bit(BR_FDB_LOCKED, &fdb->flags)) + return; + br_switchdev_fdb_populate(br, &item, fdb, NULL); switch (type) { From 9c0ca02bace4837d123e1a5a30f6f44dbdc5fb92 Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Tue, 8 Nov 2022 11:47:09 +0100 Subject: [PATCH 0759/2002] bridge: switchdev: Reflect MAB bridge port flag to device drivers Reflect the 'BR_PORT_MAB' flag to device drivers so that: * Drivers that support MAB could act upon the flag being toggled. * Drivers that do not support MAB will prevent MAB from being enabled. Signed-off-by: Ido Schimmel Reviewed-by: Petr Machata Signed-off-by: Petr Machata Reviewed-by: Vladimir Oltean Acked-by: Nikolay Aleksandrov Signed-off-by: Jakub Kicinski --- net/bridge/br_switchdev.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/bridge/br_switchdev.c b/net/bridge/br_switchdev.c index 8a0abe35137d..7eb6fd5bb917 100644 --- a/net/bridge/br_switchdev.c +++ b/net/bridge/br_switchdev.c @@ -71,7 +71,7 @@ bool nbp_switchdev_allowed_egress(const struct net_bridge_port *p, } /* Flags that can be offloaded to hardware */ -#define BR_PORT_FLAGS_HW_OFFLOAD (BR_LEARNING | BR_FLOOD | \ +#define BR_PORT_FLAGS_HW_OFFLOAD (BR_LEARNING | BR_FLOOD | BR_PORT_MAB | \ BR_MCAST_FLOOD | BR_BCAST_FLOOD | BR_PORT_LOCKED | \ BR_HAIRPIN_MODE | BR_ISOLATED | BR_MULTICAST_TO_UNICAST) From 2640a82bbc08393c846c7b55178079bb8ca31a8c Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Tue, 8 Nov 2022 11:47:10 +0100 Subject: [PATCH 0760/2002] devlink: Add packet traps for 802.1X operation Add packet traps for 802.1X operation. The "eapol" control trap is used to trap EAPOL packets and is required for the correct operation of the control plane. The "locked_port" drop trap can be enabled to gain visibility into packets that were dropped by the device due to the locked bridge port check. Signed-off-by: Ido Schimmel Reviewed-by: Petr Machata Signed-off-by: Petr Machata Signed-off-by: Jakub Kicinski --- Documentation/networking/devlink/devlink-trap.rst | 13 +++++++++++++ include/net/devlink.h | 9 +++++++++ net/core/devlink.c | 3 +++ 3 files changed, 25 insertions(+) diff --git a/Documentation/networking/devlink/devlink-trap.rst b/Documentation/networking/devlink/devlink-trap.rst index 90d1381b88de..2c14dfe69b3a 100644 --- a/Documentation/networking/devlink/devlink-trap.rst +++ b/Documentation/networking/devlink/devlink-trap.rst @@ -485,6 +485,16 @@ be added to the following table: - Traps incoming packets that the device decided to drop because the destination MAC is not configured in the MAC table and the interface is not in promiscuous mode + * - ``eapol`` + - ``control`` + - Traps "Extensible Authentication Protocol over LAN" (EAPOL) packets + specified in IEEE 802.1X + * - ``locked_port`` + - ``drop`` + - Traps packets that the device decided to drop because they failed the + locked bridge port check. That is, packets that were received via a + locked port and whose {SMAC, VID} does not correspond to an FDB entry + pointing to the port Driver-specific Packet Traps ============================ @@ -589,6 +599,9 @@ narrow. The description of these groups must be added to the following table: * - ``parser_error_drops`` - Contains packet traps for packets that were marked by the device during parsing as erroneous + * - ``eapol`` + - Contains packet traps for "Extensible Authentication Protocol over LAN" + (EAPOL) packets specified in IEEE 802.1X Packet Trap Policers ==================== diff --git a/include/net/devlink.h b/include/net/devlink.h index fa6e936af1a5..611a23a3deb2 100644 --- a/include/net/devlink.h +++ b/include/net/devlink.h @@ -894,6 +894,8 @@ enum devlink_trap_generic_id { DEVLINK_TRAP_GENERIC_ID_ESP_PARSING, DEVLINK_TRAP_GENERIC_ID_BLACKHOLE_NEXTHOP, DEVLINK_TRAP_GENERIC_ID_DMAC_FILTER, + DEVLINK_TRAP_GENERIC_ID_EAPOL, + DEVLINK_TRAP_GENERIC_ID_LOCKED_PORT, /* Add new generic trap IDs above */ __DEVLINK_TRAP_GENERIC_ID_MAX, @@ -930,6 +932,7 @@ enum devlink_trap_group_generic_id { DEVLINK_TRAP_GROUP_GENERIC_ID_ACL_SAMPLE, DEVLINK_TRAP_GROUP_GENERIC_ID_ACL_TRAP, DEVLINK_TRAP_GROUP_GENERIC_ID_PARSER_ERROR_DROPS, + DEVLINK_TRAP_GROUP_GENERIC_ID_EAPOL, /* Add new generic trap group IDs above */ __DEVLINK_TRAP_GROUP_GENERIC_ID_MAX, @@ -1121,6 +1124,10 @@ enum devlink_trap_group_generic_id { "blackhole_nexthop" #define DEVLINK_TRAP_GENERIC_NAME_DMAC_FILTER \ "dmac_filter" +#define DEVLINK_TRAP_GENERIC_NAME_EAPOL \ + "eapol" +#define DEVLINK_TRAP_GENERIC_NAME_LOCKED_PORT \ + "locked_port" #define DEVLINK_TRAP_GROUP_GENERIC_NAME_L2_DROPS \ "l2_drops" @@ -1174,6 +1181,8 @@ enum devlink_trap_group_generic_id { "acl_trap" #define DEVLINK_TRAP_GROUP_GENERIC_NAME_PARSER_ERROR_DROPS \ "parser_error_drops" +#define DEVLINK_TRAP_GROUP_GENERIC_NAME_EAPOL \ + "eapol" #define DEVLINK_TRAP_GENERIC(_type, _init_action, _id, _group_id, \ _metadata_cap) \ diff --git a/net/core/devlink.c b/net/core/devlink.c index ea0b319385fc..6bbe230c4ec5 100644 --- a/net/core/devlink.c +++ b/net/core/devlink.c @@ -11734,6 +11734,8 @@ static const struct devlink_trap devlink_trap_generic[] = { DEVLINK_TRAP(ESP_PARSING, DROP), DEVLINK_TRAP(BLACKHOLE_NEXTHOP, DROP), DEVLINK_TRAP(DMAC_FILTER, DROP), + DEVLINK_TRAP(EAPOL, CONTROL), + DEVLINK_TRAP(LOCKED_PORT, DROP), }; #define DEVLINK_TRAP_GROUP(_id) \ @@ -11769,6 +11771,7 @@ static const struct devlink_trap_group devlink_trap_group_generic[] = { DEVLINK_TRAP_GROUP(ACL_SAMPLE), DEVLINK_TRAP_GROUP(ACL_TRAP), DEVLINK_TRAP_GROUP(PARSER_ERROR_DROPS), + DEVLINK_TRAP_GROUP(EAPOL), }; static int devlink_trap_generic_verify(const struct devlink_trap *trap) From d85be0f5fd7c9e7aaf4a1e50cd65d447377eeb49 Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Tue, 8 Nov 2022 11:47:11 +0100 Subject: [PATCH 0761/2002] mlxsw: spectrum_trap: Register 802.1X packet traps with devlink Register the previously added packet traps with devlink. This allows user space to tune their policers and in the case of the locked port trap, user space can set its action to "trap" in order to gain visibility into packets that were discarded by the device due to the locked port check failure. Signed-off-by: Ido Schimmel Reviewed-by: Petr Machata Signed-off-by: Petr Machata Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/mellanox/mlxsw/reg.h | 1 + .../ethernet/mellanox/mlxsw/spectrum_trap.c | 25 +++++++++++++++++++ drivers/net/ethernet/mellanox/mlxsw/trap.h | 2 ++ 3 files changed, 28 insertions(+) diff --git a/drivers/net/ethernet/mellanox/mlxsw/reg.h b/drivers/net/ethernet/mellanox/mlxsw/reg.h index b74f30ec629a..7240af45ade5 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/reg.h +++ b/drivers/net/ethernet/mellanox/mlxsw/reg.h @@ -6316,6 +6316,7 @@ enum mlxsw_reg_htgt_trap_group { MLXSW_REG_HTGT_TRAP_GROUP_SP_TUNNEL_DISCARDS, MLXSW_REG_HTGT_TRAP_GROUP_SP_ACL_DISCARDS, MLXSW_REG_HTGT_TRAP_GROUP_SP_BUFFER_DISCARDS, + MLXSW_REG_HTGT_TRAP_GROUP_SP_EAPOL, __MLXSW_REG_HTGT_TRAP_GROUP_MAX, MLXSW_REG_HTGT_TRAP_GROUP_MAX = __MLXSW_REG_HTGT_TRAP_GROUP_MAX - 1 diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_trap.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_trap.c index f4bfdb6dab9c..899c954e0e5f 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_trap.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_trap.c @@ -510,6 +510,9 @@ mlxsw_sp_trap_policer_items_arr[] = { { .policer = MLXSW_SP_TRAP_POLICER(20, 10240, 4096), }, + { + .policer = MLXSW_SP_TRAP_POLICER(21, 128, 128), + }, }; static const struct mlxsw_sp_trap_group_item mlxsw_sp_trap_group_items_arr[] = { @@ -628,6 +631,11 @@ static const struct mlxsw_sp_trap_group_item mlxsw_sp_trap_group_items_arr[] = { .hw_group_id = MLXSW_REG_HTGT_TRAP_GROUP_SP_FLOW_LOGGING, .priority = 4, }, + { + .group = DEVLINK_TRAP_GROUP_GENERIC(EAPOL, 21), + .hw_group_id = MLXSW_REG_HTGT_TRAP_GROUP_SP_EAPOL, + .priority = 5, + }, }; static const struct mlxsw_sp_trap_item mlxsw_sp_trap_items_arr[] = { @@ -1160,6 +1168,23 @@ static const struct mlxsw_sp_trap_item mlxsw_sp_trap_items_arr[] = { MLXSW_SP_RXL_DISCARD(ROUTER3, L3_DISCARDS), }, }, + { + .trap = MLXSW_SP_TRAP_CONTROL(EAPOL, EAPOL, TRAP), + .listeners_arr = { + MLXSW_SP_RXL_NO_MARK(EAPOL, EAPOL, TRAP_TO_CPU, true), + }, + }, + { + .trap = MLXSW_SP_TRAP_DROP(LOCKED_PORT, L2_DROPS), + .listeners_arr = { + MLXSW_RXL_DIS(mlxsw_sp_rx_drop_listener, FDB_MISS, + TRAP_EXCEPTION_TO_CPU, false, + SP_L2_DISCARDS, DISCARD, SP_L2_DISCARDS), + MLXSW_RXL_DIS(mlxsw_sp_rx_drop_listener, FDB_MISMATCH, + TRAP_EXCEPTION_TO_CPU, false, + SP_L2_DISCARDS, DISCARD, SP_L2_DISCARDS), + }, + }, }; static struct mlxsw_sp_trap_policer_item * diff --git a/drivers/net/ethernet/mellanox/mlxsw/trap.h b/drivers/net/ethernet/mellanox/mlxsw/trap.h index 8da169663bda..83477c8e6971 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/trap.h +++ b/drivers/net/ethernet/mellanox/mlxsw/trap.h @@ -25,6 +25,8 @@ enum { MLXSW_TRAP_ID_IGMP_V2_LEAVE = 0x33, MLXSW_TRAP_ID_IGMP_V3_REPORT = 0x34, MLXSW_TRAP_ID_PKT_SAMPLE = 0x38, + MLXSW_TRAP_ID_FDB_MISS = 0x3A, + MLXSW_TRAP_ID_FDB_MISMATCH = 0x3B, MLXSW_TRAP_ID_FID_MISS = 0x3D, MLXSW_TRAP_ID_DECAP_ECN0 = 0x40, MLXSW_TRAP_ID_MTUERROR = 0x52, From 0b31fb9ba2b54896b0fe4b1ec0102031c40bdf4f Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Tue, 8 Nov 2022 11:47:12 +0100 Subject: [PATCH 0762/2002] mlxsw: reg: Add Switch Port FDB Security Register Add the Switch Port FDB Security Register (SPFSR) that allows enabling and disabling security checks on a given local port. In Linux terms, it allows locking / unlocking a port. Signed-off-by: Ido Schimmel Reviewed-by: Petr Machata Signed-off-by: Petr Machata Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/mellanox/mlxsw/reg.h | 34 +++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/drivers/net/ethernet/mellanox/mlxsw/reg.h b/drivers/net/ethernet/mellanox/mlxsw/reg.h index 7240af45ade5..f2d6f8654e04 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/reg.h +++ b/drivers/net/ethernet/mellanox/mlxsw/reg.h @@ -2046,6 +2046,39 @@ static inline void mlxsw_reg_spvmlr_pack(char *payload, u16 local_port, } } +/* SPFSR - Switch Port FDB Security Register + * ----------------------------------------- + * Configures the security mode per port. + */ +#define MLXSW_REG_SPFSR_ID 0x2023 +#define MLXSW_REG_SPFSR_LEN 0x08 + +MLXSW_REG_DEFINE(spfsr, MLXSW_REG_SPFSR_ID, MLXSW_REG_SPFSR_LEN); + +/* reg_spfsr_local_port + * Local port. + * Access: Index + * + * Note: not supported for CPU port. + */ +MLXSW_ITEM32_LP(reg, spfsr, 0x00, 16, 0x00, 12); + +/* reg_spfsr_security + * Security checks. + * 0: disabled (default) + * 1: enabled + * Access: RW + */ +MLXSW_ITEM32(reg, spfsr, security, 0x04, 31, 1); + +static inline void mlxsw_reg_spfsr_pack(char *payload, u16 local_port, + bool security) +{ + MLXSW_REG_ZERO(spfsr, payload); + mlxsw_reg_spfsr_local_port_set(payload, local_port); + mlxsw_reg_spfsr_security_set(payload, security); +} + /* SPVC - Switch Port VLAN Classification Register * ----------------------------------------------- * Configures the port to identify packets as untagged / single tagged / @@ -12762,6 +12795,7 @@ static const struct mlxsw_reg_info *mlxsw_reg_infos[] = { MLXSW_REG(svpe), MLXSW_REG(sfmr), MLXSW_REG(spvmlr), + MLXSW_REG(spfsr), MLXSW_REG(spvc), MLXSW_REG(spevet), MLXSW_REG(smpe), From dc0d1a8b7f84ca09b53af356eb401df9e3d976a0 Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Tue, 8 Nov 2022 11:47:13 +0100 Subject: [PATCH 0763/2002] mlxsw: spectrum: Add an API to configure security checks Add an API to enable or disable security checks on a local port. It will be used by subsequent patches when the 'BR_PORT_LOCKED' flag is toggled. Signed-off-by: Ido Schimmel Reviewed-by: Petr Machata Signed-off-by: Petr Machata Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/mellanox/mlxsw/spectrum.c | 18 ++++++++++++++++++ drivers/net/ethernet/mellanox/mlxsw/spectrum.h | 5 ++++- 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c index 04dc79da6024..b34366521914 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c @@ -466,6 +466,24 @@ int mlxsw_sp_port_vid_learning_set(struct mlxsw_sp_port *mlxsw_sp_port, u16 vid, return err; } +int mlxsw_sp_port_security_set(struct mlxsw_sp_port *mlxsw_sp_port, bool enable) +{ + struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; + char spfsr_pl[MLXSW_REG_SPFSR_LEN]; + int err; + + if (mlxsw_sp_port->security == enable) + return 0; + + mlxsw_reg_spfsr_pack(spfsr_pl, mlxsw_sp_port->local_port, enable); + err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(spfsr), spfsr_pl); + if (err) + return err; + + mlxsw_sp_port->security = enable; + return 0; +} + int mlxsw_sp_ethtype_to_sver_type(u16 ethtype, u8 *p_sver_type) { switch (ethtype) { diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.h b/drivers/net/ethernet/mellanox/mlxsw/spectrum.h index c8ff2a6d7e90..bbc73324451d 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.h +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.h @@ -321,7 +321,8 @@ struct mlxsw_sp_port { struct mlxsw_sp *mlxsw_sp; u16 local_port; u8 lagged:1, - split:1; + split:1, + security:1; u16 pvid; u16 lag_id; struct { @@ -687,6 +688,8 @@ int mlxsw_sp_port_vid_stp_set(struct mlxsw_sp_port *mlxsw_sp_port, u16 vid, int mlxsw_sp_port_vp_mode_set(struct mlxsw_sp_port *mlxsw_sp_port, bool enable); int mlxsw_sp_port_vid_learning_set(struct mlxsw_sp_port *mlxsw_sp_port, u16 vid, bool learn_enable); +int mlxsw_sp_port_security_set(struct mlxsw_sp_port *mlxsw_sp_port, + bool enable); int mlxsw_sp_ethtype_to_sver_type(u16 ethtype, u8 *p_sver_type); int mlxsw_sp_port_egress_ethtype_set(struct mlxsw_sp_port *mlxsw_sp_port, u16 ethtype); From b72cb660b26be4aaa89bb597a0ce83dd68da07e2 Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Tue, 8 Nov 2022 11:47:14 +0100 Subject: [PATCH 0764/2002] mlxsw: spectrum_switchdev: Prepare for locked FDB notifications Subsequent patches will need to report locked FDB entries to the bridge driver. Prepare for that by adding a 'locked' argument to mlxsw_sp_fdb_call_notifiers() according to which the 'locked' bit is set in the FDB notification info. For now, always pass 'false'. Signed-off-by: Ido Schimmel Reviewed-by: Petr Machata Signed-off-by: Petr Machata Signed-off-by: Jakub Kicinski --- .../mellanox/mlxsw/spectrum_switchdev.c | 21 ++++++++++++------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c index 4efccd942fb8..0fbefa43f9b1 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c @@ -2888,13 +2888,14 @@ static void mlxsw_sp_fdb_nve_call_notifiers(struct net_device *dev, static void mlxsw_sp_fdb_call_notifiers(enum switchdev_notifier_type type, const char *mac, u16 vid, - struct net_device *dev, bool offloaded) + struct net_device *dev, bool offloaded, bool locked) { struct switchdev_notifier_fdb_info info = {}; info.addr = mac; info.vid = vid; info.offloaded = offloaded; + info.locked = locked; call_switchdev_notifiers(type, dev, &info.info, NULL); } @@ -2952,7 +2953,8 @@ do_fdb_op: if (!do_notification) return; type = adding ? SWITCHDEV_FDB_ADD_TO_BRIDGE : SWITCHDEV_FDB_DEL_TO_BRIDGE; - mlxsw_sp_fdb_call_notifiers(type, mac, vid, bridge_port->dev, adding); + mlxsw_sp_fdb_call_notifiers(type, mac, vid, bridge_port->dev, adding, + false); return; @@ -3015,7 +3017,8 @@ do_fdb_op: if (!do_notification) return; type = adding ? SWITCHDEV_FDB_ADD_TO_BRIDGE : SWITCHDEV_FDB_DEL_TO_BRIDGE; - mlxsw_sp_fdb_call_notifiers(type, mac, vid, bridge_port->dev, adding); + mlxsw_sp_fdb_call_notifiers(type, mac, vid, bridge_port->dev, adding, + false); return; @@ -3122,7 +3125,7 @@ static void mlxsw_sp_fdb_notify_mac_uc_tunnel_process(struct mlxsw_sp *mlxsw_sp, type = adding ? SWITCHDEV_FDB_ADD_TO_BRIDGE : SWITCHDEV_FDB_DEL_TO_BRIDGE; - mlxsw_sp_fdb_call_notifiers(type, mac, vid, nve_dev, adding); + mlxsw_sp_fdb_call_notifiers(type, mac, vid, nve_dev, adding, false); mlxsw_sp_fid_put(fid); @@ -3264,7 +3267,7 @@ mlxsw_sp_switchdev_bridge_vxlan_fdb_event(struct mlxsw_sp *mlxsw_sp, &vxlan_fdb_info.info, NULL); mlxsw_sp_fdb_call_notifiers(SWITCHDEV_FDB_OFFLOADED, vxlan_fdb_info.eth_addr, - fdb_info->vid, dev, true); + fdb_info->vid, dev, true, false); break; case SWITCHDEV_FDB_DEL_TO_DEVICE: err = mlxsw_sp_port_fdb_tunnel_uc_op(mlxsw_sp, @@ -3359,7 +3362,7 @@ static void mlxsw_sp_switchdev_bridge_fdb_event_work(struct work_struct *work) break; mlxsw_sp_fdb_call_notifiers(SWITCHDEV_FDB_OFFLOADED, fdb_info->addr, - fdb_info->vid, dev, true); + fdb_info->vid, dev, true, false); break; case SWITCHDEV_FDB_DEL_TO_DEVICE: fdb_info = &switchdev_work->fdb_info; @@ -3443,7 +3446,8 @@ mlxsw_sp_switchdev_vxlan_fdb_add(struct mlxsw_sp *mlxsw_sp, call_switchdev_notifiers(SWITCHDEV_VXLAN_FDB_OFFLOADED, dev, &vxlan_fdb_info->info, NULL); mlxsw_sp_fdb_call_notifiers(SWITCHDEV_FDB_OFFLOADED, - vxlan_fdb_info->eth_addr, vid, dev, true); + vxlan_fdb_info->eth_addr, vid, dev, true, + false); mlxsw_sp_fid_put(fid); @@ -3493,7 +3497,8 @@ mlxsw_sp_switchdev_vxlan_fdb_del(struct mlxsw_sp *mlxsw_sp, false, false); vid = bridge_device->ops->fid_vid(bridge_device, fid); mlxsw_sp_fdb_call_notifiers(SWITCHDEV_FDB_OFFLOADED, - vxlan_fdb_info->eth_addr, vid, dev, false); + vxlan_fdb_info->eth_addr, vid, dev, false, + false); mlxsw_sp_fid_put(fid); } From 5a660e43f8b9f6e48c980dd3fed519152d5c68ca Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Tue, 8 Nov 2022 11:47:15 +0100 Subject: [PATCH 0765/2002] mlxsw: spectrum_switchdev: Add support for locked FDB notifications In Spectrum, learning happens in parallel to the security checks. Therefore, regardless of the result of the security checks, a learning notification will be generated by the device and polled later on by the driver. Currently, the driver reacts to learning notifications by programming corresponding FDB entries to the device. When a port is locked (i.e., has security checks enabled), this can no longer happen, as otherwise any host will blindly gain authorization. Instead, notify the learned entry as a locked entry to the bridge driver that will in turn notify it to user space, in case MAB is enabled. User space can then decide to authorize the host by clearing the "locked" flag, which will cause the entry to be programmed to the device. Signed-off-by: Ido Schimmel Reviewed-by: Petr Machata Signed-off-by: Petr Machata Signed-off-by: Jakub Kicinski --- .../net/ethernet/mellanox/mlxsw/spectrum_switchdev.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c index 0fbefa43f9b1..f336be77019f 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c @@ -2942,6 +2942,12 @@ static void mlxsw_sp_fdb_notify_mac_process(struct mlxsw_sp *mlxsw_sp, vid = bridge_device->vlan_enabled ? mlxsw_sp_port_vlan->vid : 0; evid = mlxsw_sp_port_vlan->vid; + if (adding && mlxsw_sp_port->security) { + mlxsw_sp_fdb_call_notifiers(SWITCHDEV_FDB_ADD_TO_BRIDGE, mac, + vid, bridge_port->dev, false, true); + return; + } + do_fdb_op: err = mlxsw_sp_port_fdb_uc_op(mlxsw_sp, local_port, mac, fid, evid, adding, true); @@ -3006,6 +3012,12 @@ static void mlxsw_sp_fdb_notify_mac_lag_process(struct mlxsw_sp *mlxsw_sp, vid = bridge_device->vlan_enabled ? mlxsw_sp_port_vlan->vid : 0; lag_vid = mlxsw_sp_port_vlan->vid; + if (adding && mlxsw_sp_port->security) { + mlxsw_sp_fdb_call_notifiers(SWITCHDEV_FDB_ADD_TO_BRIDGE, mac, + vid, bridge_port->dev, false, true); + return; + } + do_fdb_op: err = mlxsw_sp_port_fdb_uc_lag_op(mlxsw_sp, lag_id, mac, fid, lag_vid, adding, true); From 136b8dfbd784e8aa86e1bb9ade7fc41927a3839e Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Tue, 8 Nov 2022 11:47:16 +0100 Subject: [PATCH 0766/2002] mlxsw: spectrum_switchdev: Use extack in bridge port flag validation Propagate extack to mlxsw_sp_port_attr_br_pre_flags_set() in order to communicate error messages related to bridge port flag validation. Example: # bridge link set dev swp1 locked on Error: mlxsw_spectrum: Unsupported bridge port flag. More error messages will be added in subsequent patches. Signed-off-by: Ido Schimmel Reviewed-by: Petr Machata Signed-off-by: Petr Machata Signed-off-by: Jakub Kicinski --- .../net/ethernet/mellanox/mlxsw/spectrum_switchdev.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c index f336be77019f..db149af7c888 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c @@ -782,10 +782,13 @@ err_port_bridge_vlan_learning_set: static int mlxsw_sp_port_attr_br_pre_flags_set(struct mlxsw_sp_port *mlxsw_sp_port, - struct switchdev_brport_flags flags) + struct switchdev_brport_flags flags, + struct netlink_ext_ack *extack) { - if (flags.mask & ~(BR_LEARNING | BR_FLOOD | BR_MCAST_FLOOD)) + if (flags.mask & ~(BR_LEARNING | BR_FLOOD | BR_MCAST_FLOOD)) { + NL_SET_ERR_MSG_MOD(extack, "Unsupported bridge port flag"); return -EINVAL; + } return 0; } @@ -1186,7 +1189,8 @@ static int mlxsw_sp_port_attr_set(struct net_device *dev, const void *ctx, break; case SWITCHDEV_ATTR_ID_PORT_PRE_BRIDGE_FLAGS: err = mlxsw_sp_port_attr_br_pre_flags_set(mlxsw_sp_port, - attr->u.brport_flags); + attr->u.brport_flags, + extack); break; case SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS: err = mlxsw_sp_port_attr_br_flags_set(mlxsw_sp_port, From 25ed80884ce11e38d1d611c49f1d1aa3e49a1e08 Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Tue, 8 Nov 2022 11:47:17 +0100 Subject: [PATCH 0767/2002] mlxsw: spectrum_switchdev: Add locked bridge port support Add locked bridge port support by reacting to changes in the 'BR_PORT_LOCKED' flag. When set, enable security checks on the local port via the previously added SPFSR register. When security checks are enabled, an incoming packet will trigger an FDB lookup with the packet's source MAC and the FID it was classified to. If an FDB entry was not found or was found to be pointing to a different port, the packet will be dropped. Such packets increment the "discard_ingress_general" ethtool counter. For added visibility, user space can trap such packets to the CPU by enabling the "locked_port" trap. Example: # devlink trap set pci/0000:06:00.0 trap locked_port action trap Unlike other configurations done via bridge port flags (e.g., learning, flooding), security checks are enabled in the device on a per-port basis and not on a per-{port, VLAN} basis. As such, scenarios where user space can configure different locking settings for different VLANs configured on a port need to be vetoed. To that end, veto the following scenarios: 1. Locking is set on a bridge port that is a VLAN upper 2. Locking is set on a bridge port that has VLAN uppers 3. VLAN upper is configured on a locked bridge port Examples: # bridge link set dev swp1.10 locked on Error: mlxsw_spectrum: Locked flag cannot be set on a VLAN upper. # ip link add link swp1 name swp1.10 type vlan id 10 # bridge link set dev swp1 locked on Error: mlxsw_spectrum: Locked flag cannot be set on a bridge port that has VLAN uppers. # bridge link set dev swp1 locked on # ip link add link swp1 name swp1.10 type vlan id 10 Error: mlxsw_spectrum: VLAN uppers are not supported on a locked port. Signed-off-by: Ido Schimmel Reviewed-by: Petr Machata Signed-off-by: Petr Machata Signed-off-by: Jakub Kicinski --- .../net/ethernet/mellanox/mlxsw/spectrum.c | 4 ++++ .../mellanox/mlxsw/spectrum_switchdev.c | 23 ++++++++++++++++++- 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c index b34366521914..f5b2d965d476 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c @@ -4760,6 +4760,10 @@ static int mlxsw_sp_netdevice_port_upper_event(struct net_device *lower_dev, NL_SET_ERR_MSG_MOD(extack, "VLAN uppers are only supported with 802.1q VLAN protocol"); return -EOPNOTSUPP; } + if (is_vlan_dev(upper_dev) && mlxsw_sp_port->security) { + NL_SET_ERR_MSG_MOD(extack, "VLAN uppers are not supported on a locked port"); + return -EOPNOTSUPP; + } break; case NETDEV_CHANGEUPPER: upper_dev = info->upper_dev; diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c index db149af7c888..accea95cae5d 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c @@ -782,14 +782,26 @@ err_port_bridge_vlan_learning_set: static int mlxsw_sp_port_attr_br_pre_flags_set(struct mlxsw_sp_port *mlxsw_sp_port, + const struct net_device *orig_dev, struct switchdev_brport_flags flags, struct netlink_ext_ack *extack) { - if (flags.mask & ~(BR_LEARNING | BR_FLOOD | BR_MCAST_FLOOD)) { + if (flags.mask & ~(BR_LEARNING | BR_FLOOD | BR_MCAST_FLOOD | + BR_PORT_LOCKED | BR_PORT_MAB)) { NL_SET_ERR_MSG_MOD(extack, "Unsupported bridge port flag"); return -EINVAL; } + if ((flags.mask & BR_PORT_LOCKED) && is_vlan_dev(orig_dev)) { + NL_SET_ERR_MSG_MOD(extack, "Locked flag cannot be set on a VLAN upper"); + return -EINVAL; + } + + if ((flags.mask & BR_PORT_LOCKED) && vlan_uses_dev(orig_dev)) { + NL_SET_ERR_MSG_MOD(extack, "Locked flag cannot be set on a bridge port that has VLAN uppers"); + return -EINVAL; + } + return 0; } @@ -822,6 +834,13 @@ static int mlxsw_sp_port_attr_br_flags_set(struct mlxsw_sp_port *mlxsw_sp_port, return err; } + if (flags.mask & BR_PORT_LOCKED) { + err = mlxsw_sp_port_security_set(mlxsw_sp_port, + flags.val & BR_PORT_LOCKED); + if (err) + return err; + } + if (bridge_port->bridge_device->multicast_enabled) goto out; @@ -1189,6 +1208,7 @@ static int mlxsw_sp_port_attr_set(struct net_device *dev, const void *ctx, break; case SWITCHDEV_ATTR_ID_PORT_PRE_BRIDGE_FLAGS: err = mlxsw_sp_port_attr_br_pre_flags_set(mlxsw_sp_port, + attr->orig_dev, attr->u.brport_flags, extack); break; @@ -2787,6 +2807,7 @@ void mlxsw_sp_port_bridge_leave(struct mlxsw_sp_port *mlxsw_sp_port, bridge_device->ops->port_leave(bridge_device, bridge_port, mlxsw_sp_port); + mlxsw_sp_port_security_set(mlxsw_sp_port, false); mlxsw_sp_bridge_port_put(mlxsw_sp->bridge, bridge_port); } From da23a713d1de1f07675fd96418c0dd060c3633b6 Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Tue, 8 Nov 2022 11:47:18 +0100 Subject: [PATCH 0768/2002] selftests: devlink_lib: Split out helper Merely checking whether a trap counter incremented or not without logging a test result is useful on its own. Split this functionality to a helper which will be used by subsequent patches. Signed-off-by: Ido Schimmel Reviewed-by: Petr Machata Signed-off-by: Petr Machata Signed-off-by: Jakub Kicinski --- .../selftests/net/forwarding/devlink_lib.sh | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/tools/testing/selftests/net/forwarding/devlink_lib.sh b/tools/testing/selftests/net/forwarding/devlink_lib.sh index 601990c6881b..f1de525cfa55 100644 --- a/tools/testing/selftests/net/forwarding/devlink_lib.sh +++ b/tools/testing/selftests/net/forwarding/devlink_lib.sh @@ -503,25 +503,30 @@ devlink_trap_drop_cleanup() tc filter del dev $dev egress protocol $proto pref $pref handle $handle flower } -devlink_trap_stats_test() +devlink_trap_stats_check() { - local test_name=$1; shift local trap_name=$1; shift local send_one="$@" local t0_packets local t1_packets - RET=0 - t0_packets=$(devlink_trap_rx_packets_get $trap_name) $send_one && sleep 1 t1_packets=$(devlink_trap_rx_packets_get $trap_name) - if [[ $t1_packets -eq $t0_packets ]]; then - check_err 1 "Trap stats did not increase" - fi + [[ $t1_packets -ne $t0_packets ]] +} + +devlink_trap_stats_test() +{ + local test_name=$1; shift + + RET=0 + + devlink_trap_stats_check "$@" + check_err $? "Trap stats did not increase" log_test "$test_name" } From 25a26f0c2015be5e23ac467ba825085aaa01a0f0 Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Tue, 8 Nov 2022 11:47:19 +0100 Subject: [PATCH 0769/2002] selftests: mlxsw: Add a test for EAPOL trap Test that packets with a destination MAC of 01:80:C2:00:00:03 trigger the "eapol" packet trap. Signed-off-by: Ido Schimmel Reviewed-by: Petr Machata Signed-off-by: Petr Machata Reviewed-by: Vladimir Oltean Signed-off-by: Jakub Kicinski --- .../drivers/net/mlxsw/devlink_trap_control.sh | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/tools/testing/selftests/drivers/net/mlxsw/devlink_trap_control.sh b/tools/testing/selftests/drivers/net/mlxsw/devlink_trap_control.sh index d3a891d421ab..64153bbf95df 100755 --- a/tools/testing/selftests/drivers/net/mlxsw/devlink_trap_control.sh +++ b/tools/testing/selftests/drivers/net/mlxsw/devlink_trap_control.sh @@ -83,6 +83,7 @@ ALL_TESTS=" ptp_general_test flow_action_sample_test flow_action_trap_test + eapol_test " NUM_NETIFS=4 source $lib_dir/lib.sh @@ -677,6 +678,27 @@ flow_action_trap_test() tc qdisc del dev $rp1 clsact } +eapol_payload_get() +{ + local source_mac=$1; shift + local p + + p=$(: + )"01:80:C2:00:00:03:"$( : ETH daddr + )"$source_mac:"$( : ETH saddr + )"88:8E:"$( : ETH type + ) + echo $p +} + +eapol_test() +{ + local h1mac=$(mac_get $h1) + + devlink_trap_stats_test "EAPOL" "eapol" $MZ $h1 -c 1 \ + $(eapol_payload_get $h1mac) -p 100 -q +} + trap cleanup EXIT setup_prepare From fb398432db2f66f46714b855354c79223649e3ab Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Tue, 8 Nov 2022 11:47:20 +0100 Subject: [PATCH 0770/2002] selftests: mlxsw: Add a test for locked port trap Test that packets received via a locked bridge port whose {SMAC, VID} does not appear in the bridge's FDB or appears with a different port, trigger the "locked_port" packet trap. Signed-off-by: Ido Schimmel Reviewed-by: Petr Machata Signed-off-by: Petr Machata Reviewed-by: Vladimir Oltean Signed-off-by: Jakub Kicinski --- .../net/mlxsw/devlink_trap_l2_drops.sh | 105 ++++++++++++++++++ 1 file changed, 105 insertions(+) diff --git a/tools/testing/selftests/drivers/net/mlxsw/devlink_trap_l2_drops.sh b/tools/testing/selftests/drivers/net/mlxsw/devlink_trap_l2_drops.sh index a4c2812e9807..8d4b2c6265b3 100755 --- a/tools/testing/selftests/drivers/net/mlxsw/devlink_trap_l2_drops.sh +++ b/tools/testing/selftests/drivers/net/mlxsw/devlink_trap_l2_drops.sh @@ -14,6 +14,7 @@ ALL_TESTS=" ingress_stp_filter_test port_list_is_empty_test port_loopback_filter_test + locked_port_test " NUM_NETIFS=4 source $lib_dir/tc_common.sh @@ -420,6 +421,110 @@ port_loopback_filter_test() port_loopback_filter_uc_test } +locked_port_miss_test() +{ + local trap_name="locked_port" + local smac=00:11:22:33:44:55 + + bridge link set dev $swp1 learning off + bridge link set dev $swp1 locked on + + RET=0 + + devlink_trap_stats_check $trap_name $MZ $h1 -c 1 \ + -a $smac -b $(mac_get $h2) -A 192.0.2.1 -B 192.0.2.2 -p 100 -q + check_fail $? "Trap stats increased before setting action to \"trap\"" + + devlink_trap_action_set $trap_name "trap" + + devlink_trap_stats_check $trap_name $MZ $h1 -c 1 \ + -a $smac -b $(mac_get $h2) -A 192.0.2.1 -B 192.0.2.2 -p 100 -q + check_err $? "Trap stats did not increase when should" + + devlink_trap_action_set $trap_name "drop" + + devlink_trap_stats_check $trap_name $MZ $h1 -c 1 \ + -a $smac -b $(mac_get $h2) -A 192.0.2.1 -B 192.0.2.2 -p 100 -q + check_fail $? "Trap stats increased after setting action to \"drop\"" + + devlink_trap_action_set $trap_name "trap" + + bridge fdb replace $smac dev $swp1 master static vlan 1 + + devlink_trap_stats_check $trap_name $MZ $h1 -c 1 \ + -a $smac -b $(mac_get $h2) -A 192.0.2.1 -B 192.0.2.2 -p 100 -q + check_fail $? "Trap stats increased after adding an FDB entry" + + bridge fdb del $smac dev $swp1 master static vlan 1 + bridge link set dev $swp1 locked off + + devlink_trap_stats_check $trap_name $MZ $h1 -c 1 \ + -a $smac -b $(mac_get $h2) -A 192.0.2.1 -B 192.0.2.2 -p 100 -q + check_fail $? "Trap stats increased after unlocking port" + + log_test "Locked port - FDB miss" + + devlink_trap_action_set $trap_name "drop" + bridge link set dev $swp1 learning on +} + +locked_port_mismatch_test() +{ + local trap_name="locked_port" + local smac=00:11:22:33:44:55 + + bridge link set dev $swp1 learning off + bridge link set dev $swp1 locked on + + RET=0 + + bridge fdb replace $smac dev $swp2 master static vlan 1 + + devlink_trap_stats_check $trap_name $MZ $h1 -c 1 \ + -a $smac -b $(mac_get $h2) -A 192.0.2.1 -B 192.0.2.2 -p 100 -q + check_fail $? "Trap stats increased before setting action to \"trap\"" + + devlink_trap_action_set $trap_name "trap" + + devlink_trap_stats_check $trap_name $MZ $h1 -c 1 \ + -a $smac -b $(mac_get $h2) -A 192.0.2.1 -B 192.0.2.2 -p 100 -q + check_err $? "Trap stats did not increase when should" + + devlink_trap_action_set $trap_name "drop" + + devlink_trap_stats_check $trap_name $MZ $h1 -c 1 \ + -a $smac -b $(mac_get $h2) -A 192.0.2.1 -B 192.0.2.2 -p 100 -q + check_fail $? "Trap stats increased after setting action to \"drop\"" + + devlink_trap_action_set $trap_name "trap" + bridge link set dev $swp1 locked off + + devlink_trap_stats_check $trap_name $MZ $h1 -c 1 \ + -a $smac -b $(mac_get $h2) -A 192.0.2.1 -B 192.0.2.2 -p 100 -q + check_fail $? "Trap stats increased after unlocking port" + + bridge link set dev $swp1 locked on + bridge fdb replace $smac dev $swp1 master static vlan 1 + + devlink_trap_stats_check $trap_name $MZ $h1 -c 1 \ + -a $smac -b $(mac_get $h2) -A 192.0.2.1 -B 192.0.2.2 -p 100 -q + check_fail $? "Trap stats increased after replacing an FDB entry" + + bridge fdb del $smac dev $swp1 master static vlan 1 + devlink_trap_action_set $trap_name "drop" + + log_test "Locked port - FDB mismatch" + + bridge link set dev $swp1 locked off + bridge link set dev $swp1 learning on +} + +locked_port_test() +{ + locked_port_miss_test + locked_port_mismatch_test +} + trap cleanup EXIT setup_prepare From cdbde7edf0e53b643b1323e05bc1ca99661f6bcd Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Tue, 8 Nov 2022 11:47:21 +0100 Subject: [PATCH 0771/2002] selftests: mlxsw: Add a test for invalid locked bridge port configurations Test that locked bridge port configurations that are not supported by mlxsw are rejected. Signed-off-by: Ido Schimmel Reviewed-by: Petr Machata Signed-off-by: Petr Machata Reviewed-by: Vladimir Oltean Signed-off-by: Jakub Kicinski --- .../selftests/drivers/net/mlxsw/rtnetlink.sh | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/tools/testing/selftests/drivers/net/mlxsw/rtnetlink.sh b/tools/testing/selftests/drivers/net/mlxsw/rtnetlink.sh index 04f03ae9d8fb..5e89657857c7 100755 --- a/tools/testing/selftests/drivers/net/mlxsw/rtnetlink.sh +++ b/tools/testing/selftests/drivers/net/mlxsw/rtnetlink.sh @@ -34,6 +34,7 @@ ALL_TESTS=" nexthop_obj_bucket_offload_test nexthop_obj_blackhole_offload_test nexthop_obj_route_offload_test + bridge_locked_port_test devlink_reload_test " NUM_NETIFS=2 @@ -917,6 +918,36 @@ nexthop_obj_route_offload_test() simple_if_fini $swp1 192.0.2.1/24 2001:db8:1::1/64 } +bridge_locked_port_test() +{ + RET=0 + + ip link add name br1 up type bridge vlan_filtering 0 + + ip link add link $swp1 name $swp1.10 type vlan id 10 + ip link set dev $swp1.10 master br1 + + bridge link set dev $swp1.10 locked on + check_fail $? "managed to set locked flag on a VLAN upper" + + ip link set dev $swp1.10 nomaster + ip link set dev $swp1 master br1 + + bridge link set dev $swp1 locked on + check_fail $? "managed to set locked flag on a bridge port that has a VLAN upper" + + ip link del dev $swp1.10 + bridge link set dev $swp1 locked on + + ip link add link $swp1 name $swp1.10 type vlan id 10 + check_fail $? "managed to configure a VLAN upper on a locked port" + + log_test "bridge locked port" + + ip link del dev $swp1.10 &> /dev/null + ip link del dev br1 +} + devlink_reload_test() { # Test that after executing all the above configuration tests, a From 925638a2a0372f3d9d447fa714dfd0561b87a058 Mon Sep 17 00:00:00 2001 From: Raju Lakkaraju Date: Mon, 7 Nov 2022 14:26:49 +0530 Subject: [PATCH 0772/2002] net: lan743x: Remove unused argument in lan743x_common_regs( ) Remove the unused argument (i.e. struct ethtool_regs *regs) in lan743x_common_regs( ) function arguments. Signed-off-by: Raju Lakkaraju Reviewed-by: Horatiu Vultur Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/microchip/lan743x_ethtool.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/net/ethernet/microchip/lan743x_ethtool.c b/drivers/net/ethernet/microchip/lan743x_ethtool.c index 88f9484cc2a7..aa1d79a9a1f2 100644 --- a/drivers/net/ethernet/microchip/lan743x_ethtool.c +++ b/drivers/net/ethernet/microchip/lan743x_ethtool.c @@ -1190,9 +1190,7 @@ static int lan743x_ethtool_set_wol(struct net_device *netdev, } #endif /* CONFIG_PM */ -static void lan743x_common_regs(struct net_device *dev, - struct ethtool_regs *regs, void *p) - +static void lan743x_common_regs(struct net_device *dev, void *p) { struct lan743x_adapter *adapter = netdev_priv(dev); u32 *rb = p; @@ -1230,7 +1228,7 @@ static void lan743x_get_regs(struct net_device *dev, { regs->version = LAN743X_ETH_REG_VERSION; - lan743x_common_regs(dev, regs, p); + lan743x_common_regs(dev, p); } static void lan743x_get_pauseparam(struct net_device *dev, From 9045220581fc2b39bfe9a410c7b5fcd491128f23 Mon Sep 17 00:00:00 2001 From: Raju Lakkaraju Date: Mon, 7 Nov 2022 14:26:50 +0530 Subject: [PATCH 0773/2002] net: lan743x: Add support to SGMII register dump for PCI11010/PCI11414 chips Add support to SGMII register dump Signed-off-by: Raju Lakkaraju Reviewed-by: Horatiu Vultur Signed-off-by: Jakub Kicinski --- .../net/ethernet/microchip/lan743x_ethtool.c | 107 +++++++++++++++++- .../net/ethernet/microchip/lan743x_ethtool.h | 71 +++++++++++- drivers/net/ethernet/microchip/lan743x_main.c | 2 +- drivers/net/ethernet/microchip/lan743x_main.h | 1 + 4 files changed, 177 insertions(+), 4 deletions(-) diff --git a/drivers/net/ethernet/microchip/lan743x_ethtool.c b/drivers/net/ethernet/microchip/lan743x_ethtool.c index aa1d79a9a1f2..2db5949b4c7e 100644 --- a/drivers/net/ethernet/microchip/lan743x_ethtool.c +++ b/drivers/net/ethernet/microchip/lan743x_ethtool.c @@ -1195,7 +1195,7 @@ static void lan743x_common_regs(struct net_device *dev, void *p) struct lan743x_adapter *adapter = netdev_priv(dev); u32 *rb = p; - memset(p, 0, (MAX_LAN743X_ETH_REGS * sizeof(u32))); + memset(p, 0, (MAX_LAN743X_ETH_COMMON_REGS * sizeof(u32))); rb[ETH_PRIV_FLAGS] = adapter->flags; rb[ETH_ID_REV] = lan743x_csr_read(adapter, ID_REV); @@ -1218,17 +1218,120 @@ static void lan743x_common_regs(struct net_device *dev, void *p) rb[ETH_WK_SRC] = lan743x_csr_read(adapter, MAC_WK_SRC); } +static void lan743x_sgmii_regs(struct net_device *dev, void *p) +{ + struct lan743x_adapter *adp = netdev_priv(dev); + u32 *rb = p; + u16 idx; + int val; + struct { + u8 id; + u8 dev; + u16 addr; + } regs[] = { + { ETH_SR_VSMMD_DEV_ID1, MDIO_MMD_VEND1, 0x0002}, + { ETH_SR_VSMMD_DEV_ID2, MDIO_MMD_VEND1, 0x0003}, + { ETH_SR_VSMMD_PCS_ID1, MDIO_MMD_VEND1, 0x0004}, + { ETH_SR_VSMMD_PCS_ID2, MDIO_MMD_VEND1, 0x0005}, + { ETH_SR_VSMMD_STS, MDIO_MMD_VEND1, 0x0008}, + { ETH_SR_VSMMD_CTRL, MDIO_MMD_VEND1, 0x0009}, + { ETH_SR_MII_CTRL, MDIO_MMD_VEND2, 0x0000}, + { ETH_SR_MII_STS, MDIO_MMD_VEND2, 0x0001}, + { ETH_SR_MII_DEV_ID1, MDIO_MMD_VEND2, 0x0002}, + { ETH_SR_MII_DEV_ID2, MDIO_MMD_VEND2, 0x0003}, + { ETH_SR_MII_AN_ADV, MDIO_MMD_VEND2, 0x0004}, + { ETH_SR_MII_LP_BABL, MDIO_MMD_VEND2, 0x0005}, + { ETH_SR_MII_EXPN, MDIO_MMD_VEND2, 0x0006}, + { ETH_SR_MII_EXT_STS, MDIO_MMD_VEND2, 0x000F}, + { ETH_SR_MII_TIME_SYNC_ABL, MDIO_MMD_VEND2, 0x0708}, + { ETH_SR_MII_TIME_SYNC_TX_MAX_DLY_LWR, MDIO_MMD_VEND2, 0x0709}, + { ETH_SR_MII_TIME_SYNC_TX_MAX_DLY_UPR, MDIO_MMD_VEND2, 0x070A}, + { ETH_SR_MII_TIME_SYNC_TX_MIN_DLY_LWR, MDIO_MMD_VEND2, 0x070B}, + { ETH_SR_MII_TIME_SYNC_TX_MIN_DLY_UPR, MDIO_MMD_VEND2, 0x070C}, + { ETH_SR_MII_TIME_SYNC_RX_MAX_DLY_LWR, MDIO_MMD_VEND2, 0x070D}, + { ETH_SR_MII_TIME_SYNC_RX_MAX_DLY_UPR, MDIO_MMD_VEND2, 0x070E}, + { ETH_SR_MII_TIME_SYNC_RX_MIN_DLY_LWR, MDIO_MMD_VEND2, 0x070F}, + { ETH_SR_MII_TIME_SYNC_RX_MIN_DLY_UPR, MDIO_MMD_VEND2, 0x0710}, + { ETH_VR_MII_DIG_CTRL1, MDIO_MMD_VEND2, 0x8000}, + { ETH_VR_MII_AN_CTRL, MDIO_MMD_VEND2, 0x8001}, + { ETH_VR_MII_AN_INTR_STS, MDIO_MMD_VEND2, 0x8002}, + { ETH_VR_MII_TC, MDIO_MMD_VEND2, 0x8003}, + { ETH_VR_MII_DBG_CTRL, MDIO_MMD_VEND2, 0x8005}, + { ETH_VR_MII_EEE_MCTRL0, MDIO_MMD_VEND2, 0x8006}, + { ETH_VR_MII_EEE_TXTIMER, MDIO_MMD_VEND2, 0x8008}, + { ETH_VR_MII_EEE_RXTIMER, MDIO_MMD_VEND2, 0x8009}, + { ETH_VR_MII_LINK_TIMER_CTRL, MDIO_MMD_VEND2, 0x800A}, + { ETH_VR_MII_EEE_MCTRL1, MDIO_MMD_VEND2, 0x800B}, + { ETH_VR_MII_DIG_STS, MDIO_MMD_VEND2, 0x8010}, + { ETH_VR_MII_ICG_ERRCNT1, MDIO_MMD_VEND2, 0x8011}, + { ETH_VR_MII_GPIO, MDIO_MMD_VEND2, 0x8015}, + { ETH_VR_MII_EEE_LPI_STATUS, MDIO_MMD_VEND2, 0x8016}, + { ETH_VR_MII_EEE_WKERR, MDIO_MMD_VEND2, 0x8017}, + { ETH_VR_MII_MISC_STS, MDIO_MMD_VEND2, 0x8018}, + { ETH_VR_MII_RX_LSTS, MDIO_MMD_VEND2, 0x8020}, + { ETH_VR_MII_GEN2_GEN4_TX_BSTCTRL0, MDIO_MMD_VEND2, 0x8038}, + { ETH_VR_MII_GEN2_GEN4_TX_LVLCTRL0, MDIO_MMD_VEND2, 0x803A}, + { ETH_VR_MII_GEN2_GEN4_TXGENCTRL0, MDIO_MMD_VEND2, 0x803C}, + { ETH_VR_MII_GEN2_GEN4_TXGENCTRL1, MDIO_MMD_VEND2, 0x803D}, + { ETH_VR_MII_GEN4_TXGENCTRL2, MDIO_MMD_VEND2, 0x803E}, + { ETH_VR_MII_GEN2_GEN4_TX_STS, MDIO_MMD_VEND2, 0x8048}, + { ETH_VR_MII_GEN2_GEN4_RXGENCTRL0, MDIO_MMD_VEND2, 0x8058}, + { ETH_VR_MII_GEN2_GEN4_RXGENCTRL1, MDIO_MMD_VEND2, 0x8059}, + { ETH_VR_MII_GEN4_RXEQ_CTRL, MDIO_MMD_VEND2, 0x805B}, + { ETH_VR_MII_GEN4_RXLOS_CTRL0, MDIO_MMD_VEND2, 0x805D}, + { ETH_VR_MII_GEN2_GEN4_MPLL_CTRL0, MDIO_MMD_VEND2, 0x8078}, + { ETH_VR_MII_GEN2_GEN4_MPLL_CTRL1, MDIO_MMD_VEND2, 0x8079}, + { ETH_VR_MII_GEN2_GEN4_MPLL_STS, MDIO_MMD_VEND2, 0x8088}, + { ETH_VR_MII_GEN2_GEN4_LVL_CTRL, MDIO_MMD_VEND2, 0x8090}, + { ETH_VR_MII_GEN4_MISC_CTRL2, MDIO_MMD_VEND2, 0x8093}, + { ETH_VR_MII_GEN2_GEN4_MISC_CTRL0, MDIO_MMD_VEND2, 0x8099}, + { ETH_VR_MII_GEN2_GEN4_MISC_CTRL1, MDIO_MMD_VEND2, 0x809A}, + { ETH_VR_MII_SNPS_CR_CTRL, MDIO_MMD_VEND2, 0x80A0}, + { ETH_VR_MII_SNPS_CR_ADDR, MDIO_MMD_VEND2, 0x80A1}, + { ETH_VR_MII_SNPS_CR_DATA, MDIO_MMD_VEND2, 0x80A2}, + { ETH_VR_MII_DIG_CTRL2, MDIO_MMD_VEND2, 0x80E1}, + { ETH_VR_MII_DIG_ERRCNT, MDIO_MMD_VEND2, 0x80E2}, + }; + + for (idx = 0; idx < ARRAY_SIZE(regs); idx++) { + val = lan743x_sgmii_read(adp, regs[idx].dev, regs[idx].addr); + if (val < 0) + rb[regs[idx].id] = 0xFFFF; + else + rb[regs[idx].id] = val; + } +} + static int lan743x_get_regs_len(struct net_device *dev) { - return MAX_LAN743X_ETH_REGS * sizeof(u32); + struct lan743x_adapter *adapter = netdev_priv(dev); + u32 num_regs = MAX_LAN743X_ETH_COMMON_REGS; + + if (adapter->is_sgmii_en) + num_regs += MAX_LAN743X_ETH_SGMII_REGS; + + return num_regs * sizeof(u32); } static void lan743x_get_regs(struct net_device *dev, struct ethtool_regs *regs, void *p) { + struct lan743x_adapter *adapter = netdev_priv(dev); + int regs_len; + + regs_len = lan743x_get_regs_len(dev); + memset(p, 0, regs_len); + regs->version = LAN743X_ETH_REG_VERSION; + regs->len = regs_len; lan743x_common_regs(dev, p); + p = (u32 *)p + MAX_LAN743X_ETH_COMMON_REGS; + + if (adapter->is_sgmii_en) { + lan743x_sgmii_regs(dev, p); + p = (u32 *)p + MAX_LAN743X_ETH_SGMII_REGS; + } } static void lan743x_get_pauseparam(struct net_device *dev, diff --git a/drivers/net/ethernet/microchip/lan743x_ethtool.h b/drivers/net/ethernet/microchip/lan743x_ethtool.h index 7f5996a52488..267d5035b8ad 100644 --- a/drivers/net/ethernet/microchip/lan743x_ethtool.h +++ b/drivers/net/ethernet/microchip/lan743x_ethtool.h @@ -29,7 +29,76 @@ enum { ETH_WK_SRC, /* Add new registers above */ - MAX_LAN743X_ETH_REGS + MAX_LAN743X_ETH_COMMON_REGS +}; + +enum { + /* SGMII Register */ + ETH_SR_VSMMD_DEV_ID1, + ETH_SR_VSMMD_DEV_ID2, + ETH_SR_VSMMD_PCS_ID1, + ETH_SR_VSMMD_PCS_ID2, + ETH_SR_VSMMD_STS, + ETH_SR_VSMMD_CTRL, + ETH_SR_MII_CTRL, + ETH_SR_MII_STS, + ETH_SR_MII_DEV_ID1, + ETH_SR_MII_DEV_ID2, + ETH_SR_MII_AN_ADV, + ETH_SR_MII_LP_BABL, + ETH_SR_MII_EXPN, + ETH_SR_MII_EXT_STS, + ETH_SR_MII_TIME_SYNC_ABL, + ETH_SR_MII_TIME_SYNC_TX_MAX_DLY_LWR, + ETH_SR_MII_TIME_SYNC_TX_MAX_DLY_UPR, + ETH_SR_MII_TIME_SYNC_TX_MIN_DLY_LWR, + ETH_SR_MII_TIME_SYNC_TX_MIN_DLY_UPR, + ETH_SR_MII_TIME_SYNC_RX_MAX_DLY_LWR, + ETH_SR_MII_TIME_SYNC_RX_MAX_DLY_UPR, + ETH_SR_MII_TIME_SYNC_RX_MIN_DLY_LWR, + ETH_SR_MII_TIME_SYNC_RX_MIN_DLY_UPR, + ETH_VR_MII_DIG_CTRL1, + ETH_VR_MII_AN_CTRL, + ETH_VR_MII_AN_INTR_STS, + ETH_VR_MII_TC, + ETH_VR_MII_DBG_CTRL, + ETH_VR_MII_EEE_MCTRL0, + ETH_VR_MII_EEE_TXTIMER, + ETH_VR_MII_EEE_RXTIMER, + ETH_VR_MII_LINK_TIMER_CTRL, + ETH_VR_MII_EEE_MCTRL1, + ETH_VR_MII_DIG_STS, + ETH_VR_MII_ICG_ERRCNT1, + ETH_VR_MII_GPIO, + ETH_VR_MII_EEE_LPI_STATUS, + ETH_VR_MII_EEE_WKERR, + ETH_VR_MII_MISC_STS, + ETH_VR_MII_RX_LSTS, + ETH_VR_MII_GEN2_GEN4_TX_BSTCTRL0, + ETH_VR_MII_GEN2_GEN4_TX_LVLCTRL0, + ETH_VR_MII_GEN2_GEN4_TXGENCTRL0, + ETH_VR_MII_GEN2_GEN4_TXGENCTRL1, + ETH_VR_MII_GEN4_TXGENCTRL2, + ETH_VR_MII_GEN2_GEN4_TX_STS, + ETH_VR_MII_GEN2_GEN4_RXGENCTRL0, + ETH_VR_MII_GEN2_GEN4_RXGENCTRL1, + ETH_VR_MII_GEN4_RXEQ_CTRL, + ETH_VR_MII_GEN4_RXLOS_CTRL0, + ETH_VR_MII_GEN2_GEN4_MPLL_CTRL0, + ETH_VR_MII_GEN2_GEN4_MPLL_CTRL1, + ETH_VR_MII_GEN2_GEN4_MPLL_STS, + ETH_VR_MII_GEN2_GEN4_LVL_CTRL, + ETH_VR_MII_GEN4_MISC_CTRL2, + ETH_VR_MII_GEN2_GEN4_MISC_CTRL0, + ETH_VR_MII_GEN2_GEN4_MISC_CTRL1, + ETH_VR_MII_SNPS_CR_CTRL, + ETH_VR_MII_SNPS_CR_ADDR, + ETH_VR_MII_SNPS_CR_DATA, + ETH_VR_MII_DIG_CTRL2, + ETH_VR_MII_DIG_ERRCNT, + + /* Add new registers above */ + MAX_LAN743X_ETH_SGMII_REGS }; extern const struct ethtool_ops lan743x_ethtool_ops; diff --git a/drivers/net/ethernet/microchip/lan743x_main.c b/drivers/net/ethernet/microchip/lan743x_main.c index c0f8ba601c01..534840f9a7ca 100644 --- a/drivers/net/ethernet/microchip/lan743x_main.c +++ b/drivers/net/ethernet/microchip/lan743x_main.c @@ -939,7 +939,7 @@ static int lan743x_sgmii_wait_till_not_busy(struct lan743x_adapter *adapter) return ret; } -static int lan743x_sgmii_read(struct lan743x_adapter *adapter, u8 mmd, u16 addr) +int lan743x_sgmii_read(struct lan743x_adapter *adapter, u8 mmd, u16 addr) { u32 mmd_access; int ret; diff --git a/drivers/net/ethernet/microchip/lan743x_main.h b/drivers/net/ethernet/microchip/lan743x_main.h index bc5eea4c7b40..8438c3dbcf36 100644 --- a/drivers/net/ethernet/microchip/lan743x_main.h +++ b/drivers/net/ethernet/microchip/lan743x_main.h @@ -1161,5 +1161,6 @@ int lan743x_hs_syslock_acquire(struct lan743x_adapter *adapter, u16 timeout); void lan743x_hs_syslock_release(struct lan743x_adapter *adapter); void lan743x_mac_flow_ctrl_set_enables(struct lan743x_adapter *adapter, bool tx_enable, bool rx_enable); +int lan743x_sgmii_read(struct lan743x_adapter *adapter, u8 mmd, u16 addr); #endif /* _LAN743X_H */ From 13bd85580b85768238cf726dec0ddd89c06a230a Mon Sep 17 00:00:00 2001 From: Tan Tee Min Date: Tue, 8 Nov 2022 18:15:27 +0800 Subject: [PATCH 0774/2002] net: phy: dp83867: add TI PHY loopback The existing genphy_loopback() is not working for TI DP83867 PHY as it will disable autoneg support while another side is still enabling autoneg. This is causing the link is not established and results in timeout error in genphy_loopback() function. Thus, based on TI PHY datasheet, introduce a TI PHY loopback function by just configuring BMCR_LOOPBACK(Bit-9) in MII_BMCR register (0x0). Tested working on TI DP83867 PHY for all speeds (10/100/1000Mbps). Signed-off-by: Tan Tee Min Reviewed-by: Andrew Lunn Link: https://lore.kernel.org/r/20221108101527.612723-1-michael.wei.hong.sit@intel.com Signed-off-by: Jakub Kicinski --- drivers/net/phy/dp83867.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/net/phy/dp83867.c b/drivers/net/phy/dp83867.c index 417527f8bbf5..29e95b34781f 100644 --- a/drivers/net/phy/dp83867.c +++ b/drivers/net/phy/dp83867.c @@ -933,6 +933,12 @@ static void dp83867_link_change_notify(struct phy_device *phydev) } } +static int dp83867_loopback(struct phy_device *phydev, bool enable) +{ + return phy_modify(phydev, MII_BMCR, BMCR_LOOPBACK, + enable ? BMCR_LOOPBACK : 0); +} + static struct phy_driver dp83867_driver[] = { { .phy_id = DP83867_PHY_ID, @@ -959,6 +965,7 @@ static struct phy_driver dp83867_driver[] = { .resume = genphy_resume, .link_change_notify = dp83867_link_change_notify, + .set_loopback = dp83867_loopback, }, }; module_phy_driver(dp83867_driver); From 21780f89d65837e23fef825c79aa836c1cb3a8e9 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 8 Nov 2022 16:11:08 +0200 Subject: [PATCH 0775/2002] mac_pton: Don't access memory over expected length The strlen() may go too far when estimating the length of the given string. In some cases it may go over the boundary and crash the system which is the case according to the commit 13a55372b64e ("ARM: orion5x: Revert commit 4904dbda41c8."). Rectify this by switching to strnlen() for the expected maximum length of the string. Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20221108141108.62974-1-andriy.shevchenko@linux.intel.com Signed-off-by: Jakub Kicinski --- lib/net_utils.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/net_utils.c b/lib/net_utils.c index af525353395d..c17201df3d08 100644 --- a/lib/net_utils.c +++ b/lib/net_utils.c @@ -6,10 +6,11 @@ bool mac_pton(const char *s, u8 *mac) { + size_t maxlen = 3 * ETH_ALEN - 1; int i; /* XX:XX:XX:XX:XX:XX */ - if (strlen(s) < 3 * ETH_ALEN - 1) + if (strnlen(s, maxlen) < maxlen) return false; /* Don't dirty result unless string is valid MAC. */ From f6479ea4e5990e17b9690d66474198dcdba1b2c1 Mon Sep 17 00:00:00 2001 From: "Russell King (Oracle)" Date: Tue, 8 Nov 2022 14:25:56 +0000 Subject: [PATCH 0776/2002] net: mdio: add mdiodev_c45_(read|write) Signed-off-by: Russell King (Oracle) Reviewed-by: Andrew Lunn Signed-off-by: Jakub Kicinski --- include/linux/mdio.h | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/include/linux/mdio.h b/include/linux/mdio.h index 00177567cfef..f7fbbf3069e7 100644 --- a/include/linux/mdio.h +++ b/include/linux/mdio.h @@ -488,6 +488,19 @@ static inline int mdiobus_c45_write(struct mii_bus *bus, int prtad, int devad, return mdiobus_write(bus, prtad, mdiobus_c45_addr(devad, regnum), val); } +static inline int mdiodev_c45_read(struct mdio_device *mdiodev, int devad, + u16 regnum) +{ + return mdiobus_c45_read(mdiodev->bus, mdiodev->addr, devad, regnum); +} + +static inline int mdiodev_c45_write(struct mdio_device *mdiodev, u32 devad, + u16 regnum, u16 val) +{ + return mdiobus_c45_write(mdiodev->bus, mdiodev->addr, devad, regnum, + val); +} + int mdiobus_register_device(struct mdio_device *mdiodev); int mdiobus_unregister_device(struct mdio_device *mdiodev); bool mdiobus_is_registered_device(struct mii_bus *bus, int addr); From 85a2b4ac34449ed5610301e05dab0f7f9efe278b Mon Sep 17 00:00:00 2001 From: "Russell King (Oracle)" Date: Tue, 8 Nov 2022 14:26:01 +0000 Subject: [PATCH 0777/2002] net: pcs: xpcs: use mdiodev accessors Use mdiodev accessors rather than accessing the bus and address in the mdio_device structure and using the mdiobus accessors. Signed-off-by: Russell King (Oracle) Reviewed-by: Andrew Lunn Signed-off-by: Jakub Kicinski --- drivers/net/pcs/pcs-xpcs.c | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/drivers/net/pcs/pcs-xpcs.c b/drivers/net/pcs/pcs-xpcs.c index 70f88eae2a9e..f6a038a1d51e 100644 --- a/drivers/net/pcs/pcs-xpcs.c +++ b/drivers/net/pcs/pcs-xpcs.c @@ -188,18 +188,12 @@ static bool __xpcs_linkmode_supported(const struct xpcs_compat *compat, int xpcs_read(struct dw_xpcs *xpcs, int dev, u32 reg) { - struct mii_bus *bus = xpcs->mdiodev->bus; - int addr = xpcs->mdiodev->addr; - - return mdiobus_c45_read(bus, addr, dev, reg); + return mdiodev_c45_read(xpcs->mdiodev, dev, reg); } int xpcs_write(struct dw_xpcs *xpcs, int dev, u32 reg, u16 val) { - struct mii_bus *bus = xpcs->mdiodev->bus; - int addr = xpcs->mdiodev->addr; - - return mdiobus_c45_write(bus, addr, dev, reg, val); + return mdiodev_c45_write(xpcs->mdiodev, dev, reg, val); } static int xpcs_modify_changed(struct dw_xpcs *xpcs, int dev, u32 reg, From c302378bc157f6a73b6cae4ca67f5f6aa931dcec Mon Sep 17 00:00:00 2001 From: Eduard Zingerman Date: Wed, 9 Nov 2022 16:26:09 +0200 Subject: [PATCH 0778/2002] libbpf: Hashmap interface update to allow both long and void* keys/values An update for libbpf's hashmap interface from void* -> void* to a polymorphic one, allowing both long and void* keys and values. This simplifies many use cases in libbpf as hashmaps there are mostly integer to integer. Perf copies hashmap implementation from libbpf and has to be updated as well. Changes to libbpf, selftests/bpf and perf are packed as a single commit to avoid compilation issues with any future bisect. Polymorphic interface is acheived by hiding hashmap interface functions behind auxiliary macros that take care of necessary type casts, for example: #define hashmap_cast_ptr(p) \ ({ \ _Static_assert((p) == NULL || sizeof(*(p)) == sizeof(long),\ #p " pointee should be a long-sized integer or a pointer"); \ (long *)(p); \ }) bool hashmap_find(const struct hashmap *map, long key, long *value); #define hashmap__find(map, key, value) \ hashmap_find((map), (long)(key), hashmap_cast_ptr(value)) - hashmap__find macro casts key and value parameters to long and long* respectively - hashmap_cast_ptr ensures that value pointer points to a memory of appropriate size. This hack was suggested by Andrii Nakryiko in [1]. This is a follow up for [2]. [1] https://lore.kernel.org/bpf/CAEf4BzZ8KFneEJxFAaNCCFPGqp20hSpS2aCj76uRk3-qZUH5xg@mail.gmail.com/ [2] https://lore.kernel.org/bpf/af1facf9-7bc8-8a3d-0db4-7b3f333589a2@meta.com/T/#m65b28f1d6d969fcd318b556db6a3ad499a42607d Signed-off-by: Eduard Zingerman Signed-off-by: Andrii Nakryiko Link: https://lore.kernel.org/bpf/20221109142611.879983-2-eddyz87@gmail.com --- tools/bpf/bpftool/btf.c | 25 +-- tools/bpf/bpftool/common.c | 10 +- tools/bpf/bpftool/gen.c | 19 +- tools/bpf/bpftool/link.c | 10 +- tools/bpf/bpftool/main.h | 14 +- tools/bpf/bpftool/map.c | 10 +- tools/bpf/bpftool/pids.c | 16 +- tools/bpf/bpftool/prog.c | 10 +- tools/lib/bpf/btf.c | 41 ++-- tools/lib/bpf/btf_dump.c | 15 +- tools/lib/bpf/hashmap.c | 18 +- tools/lib/bpf/hashmap.h | 90 +++++---- tools/lib/bpf/libbpf.c | 18 +- tools/lib/bpf/strset.c | 18 +- tools/lib/bpf/usdt.c | 28 ++- tools/perf/tests/expr.c | 28 +-- tools/perf/tests/pmu-events.c | 6 +- tools/perf/util/bpf-loader.c | 11 +- tools/perf/util/evsel.c | 2 +- tools/perf/util/expr.c | 36 ++-- tools/perf/util/hashmap.c | 18 +- tools/perf/util/hashmap.h | 90 +++++---- tools/perf/util/metricgroup.c | 10 +- tools/perf/util/stat-shadow.c | 2 +- tools/perf/util/stat.c | 9 +- .../selftests/bpf/prog_tests/hashmap.c | 190 +++++++++++++----- .../bpf/prog_tests/kprobe_multi_test.c | 6 +- 27 files changed, 410 insertions(+), 340 deletions(-) diff --git a/tools/bpf/bpftool/btf.c b/tools/bpf/bpftool/btf.c index 68a70ac03c80..b87e4a7fd689 100644 --- a/tools/bpf/bpftool/btf.c +++ b/tools/bpf/bpftool/btf.c @@ -815,8 +815,7 @@ build_btf_type_table(struct hashmap *tab, enum bpf_obj_type type, if (!btf_id) continue; - err = hashmap__append(tab, u32_as_hash_field(btf_id), - u32_as_hash_field(id)); + err = hashmap__append(tab, btf_id, id); if (err) { p_err("failed to append entry to hashmap for BTF ID %u, object ID %u: %s", btf_id, id, strerror(-err)); @@ -875,17 +874,13 @@ show_btf_plain(struct bpf_btf_info *info, int fd, printf("size %uB", info->btf_size); n = 0; - hashmap__for_each_key_entry(btf_prog_table, entry, - u32_as_hash_field(info->id)) { - printf("%s%u", n++ == 0 ? " prog_ids " : ",", - hash_field_as_u32(entry->value)); + hashmap__for_each_key_entry(btf_prog_table, entry, info->id) { + printf("%s%lu", n++ == 0 ? " prog_ids " : ",", entry->value); } n = 0; - hashmap__for_each_key_entry(btf_map_table, entry, - u32_as_hash_field(info->id)) { - printf("%s%u", n++ == 0 ? " map_ids " : ",", - hash_field_as_u32(entry->value)); + hashmap__for_each_key_entry(btf_map_table, entry, info->id) { + printf("%s%lu", n++ == 0 ? " map_ids " : ",", entry->value); } emit_obj_refs_plain(refs_table, info->id, "\n\tpids "); @@ -907,17 +902,15 @@ show_btf_json(struct bpf_btf_info *info, int fd, jsonw_name(json_wtr, "prog_ids"); jsonw_start_array(json_wtr); /* prog_ids */ - hashmap__for_each_key_entry(btf_prog_table, entry, - u32_as_hash_field(info->id)) { - jsonw_uint(json_wtr, hash_field_as_u32(entry->value)); + hashmap__for_each_key_entry(btf_prog_table, entry, info->id) { + jsonw_uint(json_wtr, entry->value); } jsonw_end_array(json_wtr); /* prog_ids */ jsonw_name(json_wtr, "map_ids"); jsonw_start_array(json_wtr); /* map_ids */ - hashmap__for_each_key_entry(btf_map_table, entry, - u32_as_hash_field(info->id)) { - jsonw_uint(json_wtr, hash_field_as_u32(entry->value)); + hashmap__for_each_key_entry(btf_map_table, entry, info->id) { + jsonw_uint(json_wtr, entry->value); } jsonw_end_array(json_wtr); /* map_ids */ diff --git a/tools/bpf/bpftool/common.c b/tools/bpf/bpftool/common.c index e4d33bc8bbbf..d9f7b1299b03 100644 --- a/tools/bpf/bpftool/common.c +++ b/tools/bpf/bpftool/common.c @@ -494,7 +494,7 @@ static int do_build_table_cb(const char *fpath, const struct stat *sb, goto out_close; } - err = hashmap__append(build_fn_table, u32_as_hash_field(pinned_info.id), path); + err = hashmap__append(build_fn_table, pinned_info.id, path); if (err) { p_err("failed to append entry to hashmap for ID %u, path '%s': %s", pinned_info.id, path, strerror(errno)); @@ -545,7 +545,7 @@ void delete_pinned_obj_table(struct hashmap *map) return; hashmap__for_each_entry(map, entry, bkt) - free(entry->value); + free(entry->pvalue); hashmap__free(map); } @@ -1041,12 +1041,12 @@ int map_parse_fd_and_info(int *argc, char ***argv, void *info, __u32 *info_len) return fd; } -size_t hash_fn_for_key_as_id(const void *key, void *ctx) +size_t hash_fn_for_key_as_id(long key, void *ctx) { - return (size_t)key; + return key; } -bool equal_fn_for_key_as_id(const void *k1, const void *k2, void *ctx) +bool equal_fn_for_key_as_id(long k1, long k2, void *ctx) { return k1 == k2; } diff --git a/tools/bpf/bpftool/gen.c b/tools/bpf/bpftool/gen.c index cf8b4e525c88..01bb8d8f5568 100644 --- a/tools/bpf/bpftool/gen.c +++ b/tools/bpf/bpftool/gen.c @@ -1660,21 +1660,16 @@ struct btfgen_info { struct btf *marked_btf; /* btf structure used to mark used types */ }; -static size_t btfgen_hash_fn(const void *key, void *ctx) +static size_t btfgen_hash_fn(long key, void *ctx) { - return (size_t)key; + return key; } -static bool btfgen_equal_fn(const void *k1, const void *k2, void *ctx) +static bool btfgen_equal_fn(long k1, long k2, void *ctx) { return k1 == k2; } -static void *u32_as_hash_key(__u32 x) -{ - return (void *)(uintptr_t)x; -} - static void btfgen_free_info(struct btfgen_info *info) { if (!info) @@ -2086,18 +2081,18 @@ static int btfgen_record_obj(struct btfgen_info *info, const char *obj_path) struct bpf_core_spec specs_scratch[3] = {}; struct bpf_core_relo_res targ_res = {}; struct bpf_core_cand_list *cands = NULL; - const void *type_key = u32_as_hash_key(relo->type_id); const char *sec_name = btf__name_by_offset(btf, sec->sec_name_off); if (relo->kind != BPF_CORE_TYPE_ID_LOCAL && - !hashmap__find(cand_cache, type_key, (void **)&cands)) { + !hashmap__find(cand_cache, relo->type_id, &cands)) { cands = btfgen_find_cands(btf, info->src_btf, relo->type_id); if (!cands) { err = -errno; goto out; } - err = hashmap__set(cand_cache, type_key, cands, NULL, NULL); + err = hashmap__set(cand_cache, relo->type_id, cands, + NULL, NULL); if (err) goto out; } @@ -2120,7 +2115,7 @@ out: if (!IS_ERR_OR_NULL(cand_cache)) { hashmap__for_each_entry(cand_cache, entry, i) { - bpf_core_free_cands(entry->value); + bpf_core_free_cands(entry->pvalue); } hashmap__free(cand_cache); } diff --git a/tools/bpf/bpftool/link.c b/tools/bpf/bpftool/link.c index 2863639706dd..6f4cfe01cad4 100644 --- a/tools/bpf/bpftool/link.c +++ b/tools/bpf/bpftool/link.c @@ -204,9 +204,8 @@ static int show_link_close_json(int fd, struct bpf_link_info *info) jsonw_name(json_wtr, "pinned"); jsonw_start_array(json_wtr); - hashmap__for_each_key_entry(link_table, entry, - u32_as_hash_field(info->id)) - jsonw_string(json_wtr, entry->value); + hashmap__for_each_key_entry(link_table, entry, info->id) + jsonw_string(json_wtr, entry->pvalue); jsonw_end_array(json_wtr); } @@ -309,9 +308,8 @@ static int show_link_close_plain(int fd, struct bpf_link_info *info) if (!hashmap__empty(link_table)) { struct hashmap_entry *entry; - hashmap__for_each_key_entry(link_table, entry, - u32_as_hash_field(info->id)) - printf("\n\tpinned %s", (char *)entry->value); + hashmap__for_each_key_entry(link_table, entry, info->id) + printf("\n\tpinned %s", (char *)entry->pvalue); } emit_obj_refs_plain(refs_table, info->id, "\n\tpids "); diff --git a/tools/bpf/bpftool/main.h b/tools/bpf/bpftool/main.h index 467d8472df0c..d4e8a1aef787 100644 --- a/tools/bpf/bpftool/main.h +++ b/tools/bpf/bpftool/main.h @@ -240,8 +240,8 @@ int do_filter_dump(struct tcmsg *ifinfo, struct nlattr **tb, const char *kind, int print_all_levels(__maybe_unused enum libbpf_print_level level, const char *format, va_list args); -size_t hash_fn_for_key_as_id(const void *key, void *ctx); -bool equal_fn_for_key_as_id(const void *k1, const void *k2, void *ctx); +size_t hash_fn_for_key_as_id(long key, void *ctx); +bool equal_fn_for_key_as_id(long k1, long k2, void *ctx); /* bpf_attach_type_input_str - convert the provided attach type value into a * textual representation that we accept for input purposes. @@ -257,16 +257,6 @@ bool equal_fn_for_key_as_id(const void *k1, const void *k2, void *ctx); */ const char *bpf_attach_type_input_str(enum bpf_attach_type t); -static inline void *u32_as_hash_field(__u32 x) -{ - return (void *)(uintptr_t)x; -} - -static inline __u32 hash_field_as_u32(const void *x) -{ - return (__u32)(uintptr_t)x; -} - static inline bool hashmap__empty(struct hashmap *map) { return map ? hashmap__size(map) == 0 : true; diff --git a/tools/bpf/bpftool/map.c b/tools/bpf/bpftool/map.c index f941ac5c7b73..d884070a2314 100644 --- a/tools/bpf/bpftool/map.c +++ b/tools/bpf/bpftool/map.c @@ -518,9 +518,8 @@ static int show_map_close_json(int fd, struct bpf_map_info *info) jsonw_name(json_wtr, "pinned"); jsonw_start_array(json_wtr); - hashmap__for_each_key_entry(map_table, entry, - u32_as_hash_field(info->id)) - jsonw_string(json_wtr, entry->value); + hashmap__for_each_key_entry(map_table, entry, info->id) + jsonw_string(json_wtr, entry->pvalue); jsonw_end_array(json_wtr); } @@ -595,9 +594,8 @@ static int show_map_close_plain(int fd, struct bpf_map_info *info) if (!hashmap__empty(map_table)) { struct hashmap_entry *entry; - hashmap__for_each_key_entry(map_table, entry, - u32_as_hash_field(info->id)) - printf("\n\tpinned %s", (char *)entry->value); + hashmap__for_each_key_entry(map_table, entry, info->id) + printf("\n\tpinned %s", (char *)entry->pvalue); } if (frozen_str) { diff --git a/tools/bpf/bpftool/pids.c b/tools/bpf/bpftool/pids.c index bb6c969a114a..00c77edb6331 100644 --- a/tools/bpf/bpftool/pids.c +++ b/tools/bpf/bpftool/pids.c @@ -36,8 +36,8 @@ static void add_ref(struct hashmap *map, struct pid_iter_entry *e) int err, i; void *tmp; - hashmap__for_each_key_entry(map, entry, u32_as_hash_field(e->id)) { - refs = entry->value; + hashmap__for_each_key_entry(map, entry, e->id) { + refs = entry->pvalue; for (i = 0; i < refs->ref_cnt; i++) { if (refs->refs[i].pid == e->pid) @@ -81,7 +81,7 @@ static void add_ref(struct hashmap *map, struct pid_iter_entry *e) refs->has_bpf_cookie = e->has_bpf_cookie; refs->bpf_cookie = e->bpf_cookie; - err = hashmap__append(map, u32_as_hash_field(e->id), refs); + err = hashmap__append(map, e->id, refs); if (err) p_err("failed to append entry to hashmap for ID %u: %s", e->id, strerror(errno)); @@ -183,7 +183,7 @@ void delete_obj_refs_table(struct hashmap *map) return; hashmap__for_each_entry(map, entry, bkt) { - struct obj_refs *refs = entry->value; + struct obj_refs *refs = entry->pvalue; free(refs->refs); free(refs); @@ -200,8 +200,8 @@ void emit_obj_refs_json(struct hashmap *map, __u32 id, if (hashmap__empty(map)) return; - hashmap__for_each_key_entry(map, entry, u32_as_hash_field(id)) { - struct obj_refs *refs = entry->value; + hashmap__for_each_key_entry(map, entry, id) { + struct obj_refs *refs = entry->pvalue; int i; if (refs->ref_cnt == 0) @@ -232,8 +232,8 @@ void emit_obj_refs_plain(struct hashmap *map, __u32 id, const char *prefix) if (hashmap__empty(map)) return; - hashmap__for_each_key_entry(map, entry, u32_as_hash_field(id)) { - struct obj_refs *refs = entry->value; + hashmap__for_each_key_entry(map, entry, id) { + struct obj_refs *refs = entry->pvalue; int i; if (refs->ref_cnt == 0) diff --git a/tools/bpf/bpftool/prog.c b/tools/bpf/bpftool/prog.c index a858b907da16..9d32ffb9f22e 100644 --- a/tools/bpf/bpftool/prog.c +++ b/tools/bpf/bpftool/prog.c @@ -486,9 +486,8 @@ static void print_prog_json(struct bpf_prog_info *info, int fd) jsonw_name(json_wtr, "pinned"); jsonw_start_array(json_wtr); - hashmap__for_each_key_entry(prog_table, entry, - u32_as_hash_field(info->id)) - jsonw_string(json_wtr, entry->value); + hashmap__for_each_key_entry(prog_table, entry, info->id) + jsonw_string(json_wtr, entry->pvalue); jsonw_end_array(json_wtr); } @@ -561,9 +560,8 @@ static void print_prog_plain(struct bpf_prog_info *info, int fd) if (!hashmap__empty(prog_table)) { struct hashmap_entry *entry; - hashmap__for_each_key_entry(prog_table, entry, - u32_as_hash_field(info->id)) - printf("\n\tpinned %s", (char *)entry->value); + hashmap__for_each_key_entry(prog_table, entry, info->id) + printf("\n\tpinned %s", (char *)entry->pvalue); } if (info->btf_id) diff --git a/tools/lib/bpf/btf.c b/tools/lib/bpf/btf.c index 71d68bf7788c..442d4d0f98b8 100644 --- a/tools/lib/bpf/btf.c +++ b/tools/lib/bpf/btf.c @@ -1559,15 +1559,15 @@ struct btf_pipe { static int btf_rewrite_str(__u32 *str_off, void *ctx) { struct btf_pipe *p = ctx; - void *mapped_off; + long mapped_off; int off, err; if (!*str_off) /* nothing to do for empty strings */ return 0; if (p->str_off_map && - hashmap__find(p->str_off_map, (void *)(long)*str_off, &mapped_off)) { - *str_off = (__u32)(long)mapped_off; + hashmap__find(p->str_off_map, *str_off, &mapped_off)) { + *str_off = mapped_off; return 0; } @@ -1579,7 +1579,7 @@ static int btf_rewrite_str(__u32 *str_off, void *ctx) * performing expensive string comparisons. */ if (p->str_off_map) { - err = hashmap__append(p->str_off_map, (void *)(long)*str_off, (void *)(long)off); + err = hashmap__append(p->str_off_map, *str_off, off); if (err) return err; } @@ -1630,8 +1630,8 @@ static int btf_rewrite_type_ids(__u32 *type_id, void *ctx) return 0; } -static size_t btf_dedup_identity_hash_fn(const void *key, void *ctx); -static bool btf_dedup_equal_fn(const void *k1, const void *k2, void *ctx); +static size_t btf_dedup_identity_hash_fn(long key, void *ctx); +static bool btf_dedup_equal_fn(long k1, long k2, void *ctx); int btf__add_btf(struct btf *btf, const struct btf *src_btf) { @@ -3126,12 +3126,11 @@ static long hash_combine(long h, long value) } #define for_each_dedup_cand(d, node, hash) \ - hashmap__for_each_key_entry(d->dedup_table, node, (void *)hash) + hashmap__for_each_key_entry(d->dedup_table, node, hash) static int btf_dedup_table_add(struct btf_dedup *d, long hash, __u32 type_id) { - return hashmap__append(d->dedup_table, - (void *)hash, (void *)(long)type_id); + return hashmap__append(d->dedup_table, hash, type_id); } static int btf_dedup_hypot_map_add(struct btf_dedup *d, @@ -3178,17 +3177,17 @@ static void btf_dedup_free(struct btf_dedup *d) free(d); } -static size_t btf_dedup_identity_hash_fn(const void *key, void *ctx) +static size_t btf_dedup_identity_hash_fn(long key, void *ctx) { - return (size_t)key; + return key; } -static size_t btf_dedup_collision_hash_fn(const void *key, void *ctx) +static size_t btf_dedup_collision_hash_fn(long key, void *ctx) { return 0; } -static bool btf_dedup_equal_fn(const void *k1, const void *k2, void *ctx) +static bool btf_dedup_equal_fn(long k1, long k2, void *ctx) { return k1 == k2; } @@ -3750,7 +3749,7 @@ static int btf_dedup_prim_type(struct btf_dedup *d, __u32 type_id) case BTF_KIND_INT: h = btf_hash_int_decl_tag(t); for_each_dedup_cand(d, hash_entry, h) { - cand_id = (__u32)(long)hash_entry->value; + cand_id = hash_entry->value; cand = btf_type_by_id(d->btf, cand_id); if (btf_equal_int_tag(t, cand)) { new_id = cand_id; @@ -3763,7 +3762,7 @@ static int btf_dedup_prim_type(struct btf_dedup *d, __u32 type_id) case BTF_KIND_ENUM64: h = btf_hash_enum(t); for_each_dedup_cand(d, hash_entry, h) { - cand_id = (__u32)(long)hash_entry->value; + cand_id = hash_entry->value; cand = btf_type_by_id(d->btf, cand_id); if (btf_equal_enum(t, cand)) { new_id = cand_id; @@ -3785,7 +3784,7 @@ static int btf_dedup_prim_type(struct btf_dedup *d, __u32 type_id) case BTF_KIND_FLOAT: h = btf_hash_common(t); for_each_dedup_cand(d, hash_entry, h) { - cand_id = (__u32)(long)hash_entry->value; + cand_id = hash_entry->value; cand = btf_type_by_id(d->btf, cand_id); if (btf_equal_common(t, cand)) { new_id = cand_id; @@ -4288,7 +4287,7 @@ static int btf_dedup_struct_type(struct btf_dedup *d, __u32 type_id) h = btf_hash_struct(t); for_each_dedup_cand(d, hash_entry, h) { - __u32 cand_id = (__u32)(long)hash_entry->value; + __u32 cand_id = hash_entry->value; int eq; /* @@ -4393,7 +4392,7 @@ static int btf_dedup_ref_type(struct btf_dedup *d, __u32 type_id) h = btf_hash_common(t); for_each_dedup_cand(d, hash_entry, h) { - cand_id = (__u32)(long)hash_entry->value; + cand_id = hash_entry->value; cand = btf_type_by_id(d->btf, cand_id); if (btf_equal_common(t, cand)) { new_id = cand_id; @@ -4410,7 +4409,7 @@ static int btf_dedup_ref_type(struct btf_dedup *d, __u32 type_id) h = btf_hash_int_decl_tag(t); for_each_dedup_cand(d, hash_entry, h) { - cand_id = (__u32)(long)hash_entry->value; + cand_id = hash_entry->value; cand = btf_type_by_id(d->btf, cand_id); if (btf_equal_int_tag(t, cand)) { new_id = cand_id; @@ -4434,7 +4433,7 @@ static int btf_dedup_ref_type(struct btf_dedup *d, __u32 type_id) h = btf_hash_array(t); for_each_dedup_cand(d, hash_entry, h) { - cand_id = (__u32)(long)hash_entry->value; + cand_id = hash_entry->value; cand = btf_type_by_id(d->btf, cand_id); if (btf_equal_array(t, cand)) { new_id = cand_id; @@ -4466,7 +4465,7 @@ static int btf_dedup_ref_type(struct btf_dedup *d, __u32 type_id) h = btf_hash_fnproto(t); for_each_dedup_cand(d, hash_entry, h) { - cand_id = (__u32)(long)hash_entry->value; + cand_id = hash_entry->value; cand = btf_type_by_id(d->btf, cand_id); if (btf_equal_fnproto(t, cand)) { new_id = cand_id; diff --git a/tools/lib/bpf/btf_dump.c b/tools/lib/bpf/btf_dump.c index bf0cc0e986dd..12f7039e0ab2 100644 --- a/tools/lib/bpf/btf_dump.c +++ b/tools/lib/bpf/btf_dump.c @@ -117,14 +117,14 @@ struct btf_dump { struct btf_dump_data *typed_dump; }; -static size_t str_hash_fn(const void *key, void *ctx) +static size_t str_hash_fn(long key, void *ctx) { - return str_hash(key); + return str_hash((void *)key); } -static bool str_equal_fn(const void *a, const void *b, void *ctx) +static bool str_equal_fn(long a, long b, void *ctx) { - return strcmp(a, b) == 0; + return strcmp((void *)a, (void *)b) == 0; } static const char *btf_name_of(const struct btf_dump *d, __u32 name_off) @@ -225,7 +225,7 @@ static void btf_dump_free_names(struct hashmap *map) struct hashmap_entry *cur; hashmap__for_each_entry(map, cur, bkt) - free((void *)cur->key); + free((void *)cur->pkey); hashmap__free(map); } @@ -1543,11 +1543,10 @@ static size_t btf_dump_name_dups(struct btf_dump *d, struct hashmap *name_map, if (!new_name) return 1; - hashmap__find(name_map, orig_name, (void **)&dup_cnt); + hashmap__find(name_map, orig_name, &dup_cnt); dup_cnt++; - err = hashmap__set(name_map, new_name, (void *)dup_cnt, - (const void **)&old_name, NULL); + err = hashmap__set(name_map, new_name, dup_cnt, &old_name, NULL); if (err) free(new_name); diff --git a/tools/lib/bpf/hashmap.c b/tools/lib/bpf/hashmap.c index aeb09c288716..140ee4055676 100644 --- a/tools/lib/bpf/hashmap.c +++ b/tools/lib/bpf/hashmap.c @@ -128,7 +128,7 @@ static int hashmap_grow(struct hashmap *map) } static bool hashmap_find_entry(const struct hashmap *map, - const void *key, size_t hash, + const long key, size_t hash, struct hashmap_entry ***pprev, struct hashmap_entry **entry) { @@ -151,18 +151,18 @@ static bool hashmap_find_entry(const struct hashmap *map, return false; } -int hashmap__insert(struct hashmap *map, const void *key, void *value, - enum hashmap_insert_strategy strategy, - const void **old_key, void **old_value) +int hashmap_insert(struct hashmap *map, long key, long value, + enum hashmap_insert_strategy strategy, + long *old_key, long *old_value) { struct hashmap_entry *entry; size_t h; int err; if (old_key) - *old_key = NULL; + *old_key = 0; if (old_value) - *old_value = NULL; + *old_value = 0; h = hash_bits(map->hash_fn(key, map->ctx), map->cap_bits); if (strategy != HASHMAP_APPEND && @@ -203,7 +203,7 @@ int hashmap__insert(struct hashmap *map, const void *key, void *value, return 0; } -bool hashmap__find(const struct hashmap *map, const void *key, void **value) +bool hashmap_find(const struct hashmap *map, long key, long *value) { struct hashmap_entry *entry; size_t h; @@ -217,8 +217,8 @@ bool hashmap__find(const struct hashmap *map, const void *key, void **value) return true; } -bool hashmap__delete(struct hashmap *map, const void *key, - const void **old_key, void **old_value) +bool hashmap_delete(struct hashmap *map, long key, + long *old_key, long *old_value) { struct hashmap_entry **pprev, *entry; size_t h; diff --git a/tools/lib/bpf/hashmap.h b/tools/lib/bpf/hashmap.h index 10a4c4cd13cf..3fe647477bad 100644 --- a/tools/lib/bpf/hashmap.h +++ b/tools/lib/bpf/hashmap.h @@ -40,12 +40,32 @@ static inline size_t str_hash(const char *s) return h; } -typedef size_t (*hashmap_hash_fn)(const void *key, void *ctx); -typedef bool (*hashmap_equal_fn)(const void *key1, const void *key2, void *ctx); +typedef size_t (*hashmap_hash_fn)(long key, void *ctx); +typedef bool (*hashmap_equal_fn)(long key1, long key2, void *ctx); +/* + * Hashmap interface is polymorphic, keys and values could be either + * long-sized integers or pointers, this is achieved as follows: + * - interface functions that operate on keys and values are hidden + * behind auxiliary macros, e.g. hashmap_insert <-> hashmap__insert; + * - these auxiliary macros cast the key and value parameters as + * long or long *, so the user does not have to specify the casts explicitly; + * - for pointer parameters (e.g. old_key) the size of the pointed + * type is verified by hashmap_cast_ptr using _Static_assert; + * - when iterating using hashmap__for_each_* forms + * hasmap_entry->key should be used for integer keys and + * hasmap_entry->pkey should be used for pointer keys, + * same goes for values. + */ struct hashmap_entry { - const void *key; - void *value; + union { + long key; + const void *pkey; + }; + union { + long value; + void *pvalue; + }; struct hashmap_entry *next; }; @@ -102,6 +122,12 @@ enum hashmap_insert_strategy { HASHMAP_APPEND, }; +#define hashmap_cast_ptr(p) ({ \ + _Static_assert((p) == NULL || sizeof(*(p)) == sizeof(long), \ + #p " pointee should be a long-sized integer or a pointer"); \ + (long *)(p); \ +}) + /* * hashmap__insert() adds key/value entry w/ various semantics, depending on * provided strategy value. If a given key/value pair replaced already @@ -109,42 +135,38 @@ enum hashmap_insert_strategy { * through old_key and old_value to allow calling code do proper memory * management. */ -int hashmap__insert(struct hashmap *map, const void *key, void *value, - enum hashmap_insert_strategy strategy, - const void **old_key, void **old_value); +int hashmap_insert(struct hashmap *map, long key, long value, + enum hashmap_insert_strategy strategy, + long *old_key, long *old_value); -static inline int hashmap__add(struct hashmap *map, - const void *key, void *value) -{ - return hashmap__insert(map, key, value, HASHMAP_ADD, NULL, NULL); -} +#define hashmap__insert(map, key, value, strategy, old_key, old_value) \ + hashmap_insert((map), (long)(key), (long)(value), (strategy), \ + hashmap_cast_ptr(old_key), \ + hashmap_cast_ptr(old_value)) -static inline int hashmap__set(struct hashmap *map, - const void *key, void *value, - const void **old_key, void **old_value) -{ - return hashmap__insert(map, key, value, HASHMAP_SET, - old_key, old_value); -} +#define hashmap__add(map, key, value) \ + hashmap__insert((map), (key), (value), HASHMAP_ADD, NULL, NULL) -static inline int hashmap__update(struct hashmap *map, - const void *key, void *value, - const void **old_key, void **old_value) -{ - return hashmap__insert(map, key, value, HASHMAP_UPDATE, - old_key, old_value); -} +#define hashmap__set(map, key, value, old_key, old_value) \ + hashmap__insert((map), (key), (value), HASHMAP_SET, (old_key), (old_value)) -static inline int hashmap__append(struct hashmap *map, - const void *key, void *value) -{ - return hashmap__insert(map, key, value, HASHMAP_APPEND, NULL, NULL); -} +#define hashmap__update(map, key, value, old_key, old_value) \ + hashmap__insert((map), (key), (value), HASHMAP_UPDATE, (old_key), (old_value)) -bool hashmap__delete(struct hashmap *map, const void *key, - const void **old_key, void **old_value); +#define hashmap__append(map, key, value) \ + hashmap__insert((map), (key), (value), HASHMAP_APPEND, NULL, NULL) -bool hashmap__find(const struct hashmap *map, const void *key, void **value); +bool hashmap_delete(struct hashmap *map, long key, long *old_key, long *old_value); + +#define hashmap__delete(map, key, old_key, old_value) \ + hashmap_delete((map), (long)(key), \ + hashmap_cast_ptr(old_key), \ + hashmap_cast_ptr(old_value)) + +bool hashmap_find(const struct hashmap *map, long key, long *value); + +#define hashmap__find(map, key, value) \ + hashmap_find((map), (long)(key), hashmap_cast_ptr(value)) /* * hashmap__for_each_entry - iterate over all entries in hashmap diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c index 5d7819edf074..1d263885d635 100644 --- a/tools/lib/bpf/libbpf.c +++ b/tools/lib/bpf/libbpf.c @@ -5601,21 +5601,16 @@ int bpf_core_types_match(const struct btf *local_btf, __u32 local_id, return __bpf_core_types_match(local_btf, local_id, targ_btf, targ_id, false, 32); } -static size_t bpf_core_hash_fn(const void *key, void *ctx) +static size_t bpf_core_hash_fn(const long key, void *ctx) { - return (size_t)key; + return key; } -static bool bpf_core_equal_fn(const void *k1, const void *k2, void *ctx) +static bool bpf_core_equal_fn(const long k1, const long k2, void *ctx) { return k1 == k2; } -static void *u32_as_hash_key(__u32 x) -{ - return (void *)(uintptr_t)x; -} - static int record_relo_core(struct bpf_program *prog, const struct bpf_core_relo *core_relo, int insn_idx) { @@ -5658,7 +5653,6 @@ static int bpf_core_resolve_relo(struct bpf_program *prog, struct bpf_core_relo_res *targ_res) { struct bpf_core_spec specs_scratch[3] = {}; - const void *type_key = u32_as_hash_key(relo->type_id); struct bpf_core_cand_list *cands = NULL; const char *prog_name = prog->name; const struct btf_type *local_type; @@ -5675,7 +5669,7 @@ static int bpf_core_resolve_relo(struct bpf_program *prog, return -EINVAL; if (relo->kind != BPF_CORE_TYPE_ID_LOCAL && - !hashmap__find(cand_cache, type_key, (void **)&cands)) { + !hashmap__find(cand_cache, local_id, &cands)) { cands = bpf_core_find_cands(prog->obj, local_btf, local_id); if (IS_ERR(cands)) { pr_warn("prog '%s': relo #%d: target candidate search failed for [%d] %s %s: %ld\n", @@ -5683,7 +5677,7 @@ static int bpf_core_resolve_relo(struct bpf_program *prog, local_name, PTR_ERR(cands)); return PTR_ERR(cands); } - err = hashmap__set(cand_cache, type_key, cands, NULL, NULL); + err = hashmap__set(cand_cache, local_id, cands, NULL, NULL); if (err) { bpf_core_free_cands(cands); return err; @@ -5806,7 +5800,7 @@ out: if (!IS_ERR_OR_NULL(cand_cache)) { hashmap__for_each_entry(cand_cache, entry, i) { - bpf_core_free_cands(entry->value); + bpf_core_free_cands(entry->pvalue); } hashmap__free(cand_cache); } diff --git a/tools/lib/bpf/strset.c b/tools/lib/bpf/strset.c index ea655318153f..2464bcbd04e0 100644 --- a/tools/lib/bpf/strset.c +++ b/tools/lib/bpf/strset.c @@ -19,19 +19,19 @@ struct strset { struct hashmap *strs_hash; }; -static size_t strset_hash_fn(const void *key, void *ctx) +static size_t strset_hash_fn(long key, void *ctx) { const struct strset *s = ctx; - const char *str = s->strs_data + (long)key; + const char *str = s->strs_data + key; return str_hash(str); } -static bool strset_equal_fn(const void *key1, const void *key2, void *ctx) +static bool strset_equal_fn(long key1, long key2, void *ctx) { const struct strset *s = ctx; - const char *str1 = s->strs_data + (long)key1; - const char *str2 = s->strs_data + (long)key2; + const char *str1 = s->strs_data + key1; + const char *str2 = s->strs_data + key2; return strcmp(str1, str2) == 0; } @@ -67,7 +67,7 @@ struct strset *strset__new(size_t max_data_sz, const char *init_data, size_t ini /* hashmap__add() returns EEXIST if string with the same * content already is in the hash map */ - err = hashmap__add(hash, (void *)off, (void *)off); + err = hashmap__add(hash, off, off); if (err == -EEXIST) continue; /* duplicate */ if (err) @@ -127,7 +127,7 @@ int strset__find_str(struct strset *set, const char *s) new_off = set->strs_data_len; memcpy(p, s, len); - if (hashmap__find(set->strs_hash, (void *)new_off, (void **)&old_off)) + if (hashmap__find(set->strs_hash, new_off, &old_off)) return old_off; return -ENOENT; @@ -165,8 +165,8 @@ int strset__add_str(struct strset *set, const char *s) * contents doesn't exist already (HASHMAP_ADD strategy). If such * string exists, we'll get its offset in old_off (that's old_key). */ - err = hashmap__insert(set->strs_hash, (void *)new_off, (void *)new_off, - HASHMAP_ADD, (const void **)&old_off, NULL); + err = hashmap__insert(set->strs_hash, new_off, new_off, + HASHMAP_ADD, &old_off, NULL); if (err == -EEXIST) return old_off; /* duplicated string, return existing offset */ if (err) diff --git a/tools/lib/bpf/usdt.c b/tools/lib/bpf/usdt.c index 28fa1b2283de..b8daae265f99 100644 --- a/tools/lib/bpf/usdt.c +++ b/tools/lib/bpf/usdt.c @@ -873,31 +873,27 @@ static void bpf_link_usdt_dealloc(struct bpf_link *link) free(usdt_link); } -static size_t specs_hash_fn(const void *key, void *ctx) +static size_t specs_hash_fn(long key, void *ctx) { - const char *s = key; - - return str_hash(s); + return str_hash((char *)key); } -static bool specs_equal_fn(const void *key1, const void *key2, void *ctx) +static bool specs_equal_fn(long key1, long key2, void *ctx) { - const char *s1 = key1; - const char *s2 = key2; - - return strcmp(s1, s2) == 0; + return strcmp((char *)key1, (char *)key2) == 0; } static int allocate_spec_id(struct usdt_manager *man, struct hashmap *specs_hash, struct bpf_link_usdt *link, struct usdt_target *target, int *spec_id, bool *is_new) { - void *tmp; + long tmp; + void *new_ids; int err; /* check if we already allocated spec ID for this spec string */ if (hashmap__find(specs_hash, target->spec_str, &tmp)) { - *spec_id = (long)tmp; + *spec_id = tmp; *is_new = false; return 0; } @@ -905,17 +901,17 @@ static int allocate_spec_id(struct usdt_manager *man, struct hashmap *specs_hash /* otherwise it's a new ID that needs to be set up in specs map and * returned back to usdt_manager when USDT link is detached */ - tmp = libbpf_reallocarray(link->spec_ids, link->spec_cnt + 1, sizeof(*link->spec_ids)); - if (!tmp) + new_ids = libbpf_reallocarray(link->spec_ids, link->spec_cnt + 1, sizeof(*link->spec_ids)); + if (!new_ids) return -ENOMEM; - link->spec_ids = tmp; + link->spec_ids = new_ids; /* get next free spec ID, giving preference to free list, if not empty */ if (man->free_spec_cnt) { *spec_id = man->free_spec_ids[man->free_spec_cnt - 1]; /* cache spec ID for current spec string for future lookups */ - err = hashmap__add(specs_hash, target->spec_str, (void *)(long)*spec_id); + err = hashmap__add(specs_hash, target->spec_str, *spec_id); if (err) return err; @@ -928,7 +924,7 @@ static int allocate_spec_id(struct usdt_manager *man, struct hashmap *specs_hash *spec_id = man->next_free_spec_id; /* cache spec ID for current spec string for future lookups */ - err = hashmap__add(specs_hash, target->spec_str, (void *)(long)*spec_id); + err = hashmap__add(specs_hash, target->spec_str, *spec_id); if (err) return err; diff --git a/tools/perf/tests/expr.c b/tools/perf/tests/expr.c index 6512f5e22045..c598f95aebf3 100644 --- a/tools/perf/tests/expr.c +++ b/tools/perf/tests/expr.c @@ -130,12 +130,9 @@ static int test__expr(struct test_suite *t __maybe_unused, int subtest __maybe_u expr__find_ids("FOO + BAR + BAZ + BOZO", "FOO", ctx) == 0); TEST_ASSERT_VAL("find ids", hashmap__size(ctx->ids) == 3); - TEST_ASSERT_VAL("find ids", hashmap__find(ctx->ids, "BAR", - (void **)&val_ptr)); - TEST_ASSERT_VAL("find ids", hashmap__find(ctx->ids, "BAZ", - (void **)&val_ptr)); - TEST_ASSERT_VAL("find ids", hashmap__find(ctx->ids, "BOZO", - (void **)&val_ptr)); + TEST_ASSERT_VAL("find ids", hashmap__find(ctx->ids, "BAR", &val_ptr)); + TEST_ASSERT_VAL("find ids", hashmap__find(ctx->ids, "BAZ", &val_ptr)); + TEST_ASSERT_VAL("find ids", hashmap__find(ctx->ids, "BOZO", &val_ptr)); expr__ctx_clear(ctx); ctx->sctx.runtime = 3; @@ -143,20 +140,16 @@ static int test__expr(struct test_suite *t __maybe_unused, int subtest __maybe_u expr__find_ids("EVENT1\\,param\\=?@ + EVENT2\\,param\\=?@", NULL, ctx) == 0); TEST_ASSERT_VAL("find ids", hashmap__size(ctx->ids) == 2); - TEST_ASSERT_VAL("find ids", hashmap__find(ctx->ids, "EVENT1,param=3@", - (void **)&val_ptr)); - TEST_ASSERT_VAL("find ids", hashmap__find(ctx->ids, "EVENT2,param=3@", - (void **)&val_ptr)); + TEST_ASSERT_VAL("find ids", hashmap__find(ctx->ids, "EVENT1,param=3@", &val_ptr)); + TEST_ASSERT_VAL("find ids", hashmap__find(ctx->ids, "EVENT2,param=3@", &val_ptr)); expr__ctx_clear(ctx); TEST_ASSERT_VAL("find ids", expr__find_ids("dash\\-event1 - dash\\-event2", NULL, ctx) == 0); TEST_ASSERT_VAL("find ids", hashmap__size(ctx->ids) == 2); - TEST_ASSERT_VAL("find ids", hashmap__find(ctx->ids, "dash-event1", - (void **)&val_ptr)); - TEST_ASSERT_VAL("find ids", hashmap__find(ctx->ids, "dash-event2", - (void **)&val_ptr)); + TEST_ASSERT_VAL("find ids", hashmap__find(ctx->ids, "dash-event1", &val_ptr)); + TEST_ASSERT_VAL("find ids", hashmap__find(ctx->ids, "dash-event2", &val_ptr)); /* Only EVENT1 or EVENT2 need be measured depending on the value of smt_on. */ { @@ -174,7 +167,7 @@ static int test__expr(struct test_suite *t __maybe_unused, int subtest __maybe_u TEST_ASSERT_VAL("find ids", hashmap__size(ctx->ids) == 1); TEST_ASSERT_VAL("find ids", hashmap__find(ctx->ids, smton ? "EVENT1" : "EVENT2", - (void **)&val_ptr)); + &val_ptr)); expr__ctx_clear(ctx); TEST_ASSERT_VAL("find ids", @@ -183,7 +176,7 @@ static int test__expr(struct test_suite *t __maybe_unused, int subtest __maybe_u TEST_ASSERT_VAL("find ids", hashmap__size(ctx->ids) == 1); TEST_ASSERT_VAL("find ids", hashmap__find(ctx->ids, corewide ? "EVENT1" : "EVENT2", - (void **)&val_ptr)); + &val_ptr)); } /* The expression is a constant 1.0 without needing to evaluate EVENT1. */ @@ -220,8 +213,7 @@ static int test__expr(struct test_suite *t __maybe_unused, int subtest __maybe_u expr__find_ids("source_count(EVENT1)", NULL, ctx) == 0); TEST_ASSERT_VAL("source count", hashmap__size(ctx->ids) == 1); - TEST_ASSERT_VAL("source count", hashmap__find(ctx->ids, "EVENT1", - (void **)&val_ptr)); + TEST_ASSERT_VAL("source count", hashmap__find(ctx->ids, "EVENT1", &val_ptr)); expr__ctx_free(ctx); diff --git a/tools/perf/tests/pmu-events.c b/tools/perf/tests/pmu-events.c index 097e05c796ab..3c2ee55e75c7 100644 --- a/tools/perf/tests/pmu-events.c +++ b/tools/perf/tests/pmu-events.c @@ -986,10 +986,10 @@ static int metric_parse_fake(const char *str) */ i = 1; hashmap__for_each_entry(ctx->ids, cur, bkt) - expr__add_id_val(ctx, strdup(cur->key), i++); + expr__add_id_val(ctx, strdup(cur->pkey), i++); hashmap__for_each_entry(ctx->ids, cur, bkt) { - if (check_parse_fake(cur->key)) { + if (check_parse_fake(cur->pkey)) { pr_err("check_parse_fake failed\n"); goto out; } @@ -1003,7 +1003,7 @@ static int metric_parse_fake(const char *str) */ i = 1024; hashmap__for_each_entry(ctx->ids, cur, bkt) - expr__add_id_val(ctx, strdup(cur->key), i--); + expr__add_id_val(ctx, strdup(cur->pkey), i--); if (expr__parse(&result, ctx, str)) { pr_err("expr__parse failed\n"); ret = -1; diff --git a/tools/perf/util/bpf-loader.c b/tools/perf/util/bpf-loader.c index f4adeccdbbcb..a5dbd71cb9ab 100644 --- a/tools/perf/util/bpf-loader.c +++ b/tools/perf/util/bpf-loader.c @@ -318,7 +318,7 @@ static void bpf_program_hash_free(void) return; hashmap__for_each_entry(bpf_program_hash, cur, bkt) - clear_prog_priv(cur->key, cur->value); + clear_prog_priv(cur->pkey, cur->pvalue); hashmap__free(bpf_program_hash); bpf_program_hash = NULL; @@ -339,13 +339,12 @@ void bpf__clear(void) bpf_map_hash_free(); } -static size_t ptr_hash(const void *__key, void *ctx __maybe_unused) +static size_t ptr_hash(const long __key, void *ctx __maybe_unused) { - return (size_t) __key; + return __key; } -static bool ptr_equal(const void *key1, const void *key2, - void *ctx __maybe_unused) +static bool ptr_equal(long key1, long key2, void *ctx __maybe_unused) { return key1 == key2; } @@ -1185,7 +1184,7 @@ static void bpf_map_hash_free(void) return; hashmap__for_each_entry(bpf_map_hash, cur, bkt) - bpf_map_priv__clear(cur->key, cur->value); + bpf_map_priv__clear(cur->pkey, cur->pvalue); hashmap__free(bpf_map_hash); bpf_map_hash = NULL; diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index 76605fde3507..9e8a1294c981 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c @@ -3123,7 +3123,7 @@ void evsel__zero_per_pkg(struct evsel *evsel) if (evsel->per_pkg_mask) { hashmap__for_each_entry(evsel->per_pkg_mask, cur, bkt) - free((char *)cur->key); + free((void *)cur->pkey); hashmap__clear(evsel->per_pkg_mask); } diff --git a/tools/perf/util/expr.c b/tools/perf/util/expr.c index aaacf514dc09..2f05ecdcfe9a 100644 --- a/tools/perf/util/expr.c +++ b/tools/perf/util/expr.c @@ -46,7 +46,7 @@ struct expr_id_data { } kind; }; -static size_t key_hash(const void *key, void *ctx __maybe_unused) +static size_t key_hash(long key, void *ctx __maybe_unused) { const char *str = (const char *)key; size_t hash = 0; @@ -59,8 +59,7 @@ static size_t key_hash(const void *key, void *ctx __maybe_unused) return hash; } -static bool key_equal(const void *key1, const void *key2, - void *ctx __maybe_unused) +static bool key_equal(long key1, long key2, void *ctx __maybe_unused) { return !strcmp((const char *)key1, (const char *)key2); } @@ -84,8 +83,8 @@ void ids__free(struct hashmap *ids) return; hashmap__for_each_entry(ids, cur, bkt) { - free((char *)cur->key); - free(cur->value); + free((void *)cur->pkey); + free((void *)cur->pvalue); } hashmap__free(ids); @@ -97,8 +96,7 @@ int ids__insert(struct hashmap *ids, const char *id) char *old_key = NULL; int ret; - ret = hashmap__set(ids, id, data_ptr, - (const void **)&old_key, (void **)&old_data); + ret = hashmap__set(ids, id, data_ptr, &old_key, &old_data); if (ret) free(data_ptr); free(old_key); @@ -127,8 +125,7 @@ struct hashmap *ids__union(struct hashmap *ids1, struct hashmap *ids2) ids2 = tmp; } hashmap__for_each_entry(ids2, cur, bkt) { - ret = hashmap__set(ids1, cur->key, cur->value, - (const void **)&old_key, (void **)&old_data); + ret = hashmap__set(ids1, cur->key, cur->value, &old_key, &old_data); free(old_key); free(old_data); @@ -169,8 +166,7 @@ int expr__add_id_val_source_count(struct expr_parse_ctx *ctx, const char *id, data_ptr->val.source_count = source_count; data_ptr->kind = EXPR_ID_DATA__VALUE; - ret = hashmap__set(ctx->ids, id, data_ptr, - (const void **)&old_key, (void **)&old_data); + ret = hashmap__set(ctx->ids, id, data_ptr, &old_key, &old_data); if (ret) free(data_ptr); free(old_key); @@ -205,8 +201,7 @@ int expr__add_ref(struct expr_parse_ctx *ctx, struct metric_ref *ref) data_ptr->ref.metric_expr = ref->metric_expr; data_ptr->kind = EXPR_ID_DATA__REF; - ret = hashmap__set(ctx->ids, name, data_ptr, - (const void **)&old_key, (void **)&old_data); + ret = hashmap__set(ctx->ids, name, data_ptr, &old_key, &old_data); if (ret) free(data_ptr); @@ -221,7 +216,7 @@ int expr__add_ref(struct expr_parse_ctx *ctx, struct metric_ref *ref) int expr__get_id(struct expr_parse_ctx *ctx, const char *id, struct expr_id_data **data) { - return hashmap__find(ctx->ids, id, (void **)data) ? 0 : -1; + return hashmap__find(ctx->ids, id, data) ? 0 : -1; } bool expr__subset_of_ids(struct expr_parse_ctx *haystack, @@ -232,7 +227,7 @@ bool expr__subset_of_ids(struct expr_parse_ctx *haystack, struct expr_id_data *data; hashmap__for_each_entry(needles->ids, cur, bkt) { - if (expr__get_id(haystack, cur->key, &data)) + if (expr__get_id(haystack, cur->pkey, &data)) return false; } return true; @@ -282,8 +277,7 @@ void expr__del_id(struct expr_parse_ctx *ctx, const char *id) struct expr_id_data *old_val = NULL; char *old_key = NULL; - hashmap__delete(ctx->ids, id, - (const void **)&old_key, (void **)&old_val); + hashmap__delete(ctx->ids, id, &old_key, &old_val); free(old_key); free(old_val); } @@ -314,8 +308,8 @@ void expr__ctx_clear(struct expr_parse_ctx *ctx) size_t bkt; hashmap__for_each_entry(ctx->ids, cur, bkt) { - free((char *)cur->key); - free(cur->value); + free((void *)cur->pkey); + free(cur->pvalue); } hashmap__clear(ctx->ids); } @@ -330,8 +324,8 @@ void expr__ctx_free(struct expr_parse_ctx *ctx) free(ctx->sctx.user_requested_cpu_list); hashmap__for_each_entry(ctx->ids, cur, bkt) { - free((char *)cur->key); - free(cur->value); + free((void *)cur->pkey); + free(cur->pvalue); } hashmap__free(ctx->ids); free(ctx); diff --git a/tools/perf/util/hashmap.c b/tools/perf/util/hashmap.c index aeb09c288716..140ee4055676 100644 --- a/tools/perf/util/hashmap.c +++ b/tools/perf/util/hashmap.c @@ -128,7 +128,7 @@ static int hashmap_grow(struct hashmap *map) } static bool hashmap_find_entry(const struct hashmap *map, - const void *key, size_t hash, + const long key, size_t hash, struct hashmap_entry ***pprev, struct hashmap_entry **entry) { @@ -151,18 +151,18 @@ static bool hashmap_find_entry(const struct hashmap *map, return false; } -int hashmap__insert(struct hashmap *map, const void *key, void *value, - enum hashmap_insert_strategy strategy, - const void **old_key, void **old_value) +int hashmap_insert(struct hashmap *map, long key, long value, + enum hashmap_insert_strategy strategy, + long *old_key, long *old_value) { struct hashmap_entry *entry; size_t h; int err; if (old_key) - *old_key = NULL; + *old_key = 0; if (old_value) - *old_value = NULL; + *old_value = 0; h = hash_bits(map->hash_fn(key, map->ctx), map->cap_bits); if (strategy != HASHMAP_APPEND && @@ -203,7 +203,7 @@ int hashmap__insert(struct hashmap *map, const void *key, void *value, return 0; } -bool hashmap__find(const struct hashmap *map, const void *key, void **value) +bool hashmap_find(const struct hashmap *map, long key, long *value) { struct hashmap_entry *entry; size_t h; @@ -217,8 +217,8 @@ bool hashmap__find(const struct hashmap *map, const void *key, void **value) return true; } -bool hashmap__delete(struct hashmap *map, const void *key, - const void **old_key, void **old_value) +bool hashmap_delete(struct hashmap *map, long key, + long *old_key, long *old_value) { struct hashmap_entry **pprev, *entry; size_t h; diff --git a/tools/perf/util/hashmap.h b/tools/perf/util/hashmap.h index 10a4c4cd13cf..3fe647477bad 100644 --- a/tools/perf/util/hashmap.h +++ b/tools/perf/util/hashmap.h @@ -40,12 +40,32 @@ static inline size_t str_hash(const char *s) return h; } -typedef size_t (*hashmap_hash_fn)(const void *key, void *ctx); -typedef bool (*hashmap_equal_fn)(const void *key1, const void *key2, void *ctx); +typedef size_t (*hashmap_hash_fn)(long key, void *ctx); +typedef bool (*hashmap_equal_fn)(long key1, long key2, void *ctx); +/* + * Hashmap interface is polymorphic, keys and values could be either + * long-sized integers or pointers, this is achieved as follows: + * - interface functions that operate on keys and values are hidden + * behind auxiliary macros, e.g. hashmap_insert <-> hashmap__insert; + * - these auxiliary macros cast the key and value parameters as + * long or long *, so the user does not have to specify the casts explicitly; + * - for pointer parameters (e.g. old_key) the size of the pointed + * type is verified by hashmap_cast_ptr using _Static_assert; + * - when iterating using hashmap__for_each_* forms + * hasmap_entry->key should be used for integer keys and + * hasmap_entry->pkey should be used for pointer keys, + * same goes for values. + */ struct hashmap_entry { - const void *key; - void *value; + union { + long key; + const void *pkey; + }; + union { + long value; + void *pvalue; + }; struct hashmap_entry *next; }; @@ -102,6 +122,12 @@ enum hashmap_insert_strategy { HASHMAP_APPEND, }; +#define hashmap_cast_ptr(p) ({ \ + _Static_assert((p) == NULL || sizeof(*(p)) == sizeof(long), \ + #p " pointee should be a long-sized integer or a pointer"); \ + (long *)(p); \ +}) + /* * hashmap__insert() adds key/value entry w/ various semantics, depending on * provided strategy value. If a given key/value pair replaced already @@ -109,42 +135,38 @@ enum hashmap_insert_strategy { * through old_key and old_value to allow calling code do proper memory * management. */ -int hashmap__insert(struct hashmap *map, const void *key, void *value, - enum hashmap_insert_strategy strategy, - const void **old_key, void **old_value); +int hashmap_insert(struct hashmap *map, long key, long value, + enum hashmap_insert_strategy strategy, + long *old_key, long *old_value); -static inline int hashmap__add(struct hashmap *map, - const void *key, void *value) -{ - return hashmap__insert(map, key, value, HASHMAP_ADD, NULL, NULL); -} +#define hashmap__insert(map, key, value, strategy, old_key, old_value) \ + hashmap_insert((map), (long)(key), (long)(value), (strategy), \ + hashmap_cast_ptr(old_key), \ + hashmap_cast_ptr(old_value)) -static inline int hashmap__set(struct hashmap *map, - const void *key, void *value, - const void **old_key, void **old_value) -{ - return hashmap__insert(map, key, value, HASHMAP_SET, - old_key, old_value); -} +#define hashmap__add(map, key, value) \ + hashmap__insert((map), (key), (value), HASHMAP_ADD, NULL, NULL) -static inline int hashmap__update(struct hashmap *map, - const void *key, void *value, - const void **old_key, void **old_value) -{ - return hashmap__insert(map, key, value, HASHMAP_UPDATE, - old_key, old_value); -} +#define hashmap__set(map, key, value, old_key, old_value) \ + hashmap__insert((map), (key), (value), HASHMAP_SET, (old_key), (old_value)) -static inline int hashmap__append(struct hashmap *map, - const void *key, void *value) -{ - return hashmap__insert(map, key, value, HASHMAP_APPEND, NULL, NULL); -} +#define hashmap__update(map, key, value, old_key, old_value) \ + hashmap__insert((map), (key), (value), HASHMAP_UPDATE, (old_key), (old_value)) -bool hashmap__delete(struct hashmap *map, const void *key, - const void **old_key, void **old_value); +#define hashmap__append(map, key, value) \ + hashmap__insert((map), (key), (value), HASHMAP_APPEND, NULL, NULL) -bool hashmap__find(const struct hashmap *map, const void *key, void **value); +bool hashmap_delete(struct hashmap *map, long key, long *old_key, long *old_value); + +#define hashmap__delete(map, key, old_key, old_value) \ + hashmap_delete((map), (long)(key), \ + hashmap_cast_ptr(old_key), \ + hashmap_cast_ptr(old_value)) + +bool hashmap_find(const struct hashmap *map, long key, long *value); + +#define hashmap__find(map, key, value) \ + hashmap_find((map), (long)(key), hashmap_cast_ptr(value)) /* * hashmap__for_each_entry - iterate over all entries in hashmap diff --git a/tools/perf/util/metricgroup.c b/tools/perf/util/metricgroup.c index 4c98ac29ee13..6b3505b1b6ac 100644 --- a/tools/perf/util/metricgroup.c +++ b/tools/perf/util/metricgroup.c @@ -288,7 +288,7 @@ static int setup_metric_events(struct hashmap *ids, * combined or shared groups, this metric may not care * about this event. */ - if (hashmap__find(ids, metric_id, (void **)&val_ptr)) { + if (hashmap__find(ids, metric_id, &val_ptr)) { metric_events[matched_events++] = ev; if (matched_events >= ids_size) @@ -764,7 +764,7 @@ static int metricgroup__build_event_string(struct strbuf *events, #define RETURN_IF_NON_ZERO(x) do { if (x) return x; } while (0) hashmap__for_each_entry(ctx->ids, cur, bkt) { - const char *sep, *rsep, *id = cur->key; + const char *sep, *rsep, *id = cur->pkey; enum perf_tool_event ev; pr_debug("found event %s\n", id); @@ -945,14 +945,14 @@ static int resolve_metric(struct list_head *metric_list, hashmap__for_each_entry(root_metric->pctx->ids, cur, bkt) { struct pmu_event pe; - if (metricgroup__find_metric(cur->key, table, &pe)) { + if (metricgroup__find_metric(cur->pkey, table, &pe)) { pending = realloc(pending, (pending_cnt + 1) * sizeof(struct to_resolve)); if (!pending) return -ENOMEM; memcpy(&pending[pending_cnt].pe, &pe, sizeof(pe)); - pending[pending_cnt].key = cur->key; + pending[pending_cnt].key = cur->pkey; pending_cnt++; } } @@ -1433,7 +1433,7 @@ static int build_combined_expr_ctx(const struct list_head *metric_list, list_for_each_entry(m, metric_list, nd) { if (m->has_constraint && !m->modifier) { hashmap__for_each_entry(m->pctx->ids, cur, bkt) { - dup = strdup(cur->key); + dup = strdup(cur->pkey); if (!dup) { ret = -ENOMEM; goto err_out; diff --git a/tools/perf/util/stat-shadow.c b/tools/perf/util/stat-shadow.c index 07b29fe272c7..0bf71b02aa06 100644 --- a/tools/perf/util/stat-shadow.c +++ b/tools/perf/util/stat-shadow.c @@ -398,7 +398,7 @@ void perf_stat__collect_metric_expr(struct evlist *evsel_list) i = 0; hashmap__for_each_entry(ctx->ids, cur, bkt) { - const char *metric_name = (const char *)cur->key; + const char *metric_name = cur->pkey; found = false; if (leader) { diff --git a/tools/perf/util/stat.c b/tools/perf/util/stat.c index 8ec8bb4a9912..c0656f85bfa5 100644 --- a/tools/perf/util/stat.c +++ b/tools/perf/util/stat.c @@ -278,15 +278,14 @@ void evlist__save_aggr_prev_raw_counts(struct evlist *evlist) } } -static size_t pkg_id_hash(const void *__key, void *ctx __maybe_unused) +static size_t pkg_id_hash(long __key, void *ctx __maybe_unused) { uint64_t *key = (uint64_t *) __key; return *key & 0xffffffff; } -static bool pkg_id_equal(const void *__key1, const void *__key2, - void *ctx __maybe_unused) +static bool pkg_id_equal(long __key1, long __key2, void *ctx __maybe_unused) { uint64_t *key1 = (uint64_t *) __key1; uint64_t *key2 = (uint64_t *) __key2; @@ -347,11 +346,11 @@ static int check_per_pkg(struct evsel *counter, struct perf_counts_values *vals, return -ENOMEM; *key = (uint64_t)d << 32 | s; - if (hashmap__find(mask, (void *)key, NULL)) { + if (hashmap__find(mask, key, NULL)) { *skip = true; free(key); } else - ret = hashmap__add(mask, (void *)key, (void *)1); + ret = hashmap__add(mask, key, 1); return ret; } diff --git a/tools/testing/selftests/bpf/prog_tests/hashmap.c b/tools/testing/selftests/bpf/prog_tests/hashmap.c index 4747ab18f97f..d358a223fd2d 100644 --- a/tools/testing/selftests/bpf/prog_tests/hashmap.c +++ b/tools/testing/selftests/bpf/prog_tests/hashmap.c @@ -7,17 +7,18 @@ */ #include "test_progs.h" #include "bpf/hashmap.h" +#include static int duration = 0; -static size_t hash_fn(const void *k, void *ctx) +static size_t hash_fn(long k, void *ctx) { - return (long)k; + return k; } -static bool equal_fn(const void *a, const void *b, void *ctx) +static bool equal_fn(long a, long b, void *ctx) { - return (long)a == (long)b; + return a == b; } static inline size_t next_pow_2(size_t n) @@ -52,8 +53,8 @@ static void test_hashmap_generic(void) return; for (i = 0; i < ELEM_CNT; i++) { - const void *oldk, *k = (const void *)(long)i; - void *oldv, *v = (void *)(long)(1024 + i); + long oldk, k = i; + long oldv, v = 1024 + i; err = hashmap__update(map, k, v, &oldk, &oldv); if (CHECK(err != -ENOENT, "hashmap__update", @@ -64,20 +65,18 @@ static void test_hashmap_generic(void) err = hashmap__add(map, k, v); } else { err = hashmap__set(map, k, v, &oldk, &oldv); - if (CHECK(oldk != NULL || oldv != NULL, "check_kv", - "unexpected k/v: %p=%p\n", oldk, oldv)) + if (CHECK(oldk != 0 || oldv != 0, "check_kv", + "unexpected k/v: %ld=%ld\n", oldk, oldv)) goto cleanup; } - if (CHECK(err, "elem_add", "failed to add k/v %ld = %ld: %d\n", - (long)k, (long)v, err)) + if (CHECK(err, "elem_add", "failed to add k/v %ld = %ld: %d\n", k, v, err)) goto cleanup; if (CHECK(!hashmap__find(map, k, &oldv), "elem_find", - "failed to find key %ld\n", (long)k)) + "failed to find key %ld\n", k)) goto cleanup; - if (CHECK(oldv != v, "elem_val", - "found value is wrong: %ld\n", (long)oldv)) + if (CHECK(oldv != v, "elem_val", "found value is wrong: %ld\n", oldv)) goto cleanup; } @@ -91,8 +90,8 @@ static void test_hashmap_generic(void) found_msk = 0; hashmap__for_each_entry(map, entry, bkt) { - long k = (long)entry->key; - long v = (long)entry->value; + long k = entry->key; + long v = entry->value; found_msk |= 1ULL << k; if (CHECK(v - k != 1024, "check_kv", @@ -104,8 +103,8 @@ static void test_hashmap_generic(void) goto cleanup; for (i = 0; i < ELEM_CNT; i++) { - const void *oldk, *k = (const void *)(long)i; - void *oldv, *v = (void *)(long)(256 + i); + long oldk, k = i; + long oldv, v = 256 + i; err = hashmap__add(map, k, v); if (CHECK(err != -EEXIST, "hashmap__add", @@ -119,13 +118,13 @@ static void test_hashmap_generic(void) if (CHECK(err, "elem_upd", "failed to update k/v %ld = %ld: %d\n", - (long)k, (long)v, err)) + k, v, err)) goto cleanup; if (CHECK(!hashmap__find(map, k, &oldv), "elem_find", - "failed to find key %ld\n", (long)k)) + "failed to find key %ld\n", k)) goto cleanup; if (CHECK(oldv != v, "elem_val", - "found value is wrong: %ld\n", (long)oldv)) + "found value is wrong: %ld\n", oldv)) goto cleanup; } @@ -139,8 +138,8 @@ static void test_hashmap_generic(void) found_msk = 0; hashmap__for_each_entry_safe(map, entry, tmp, bkt) { - long k = (long)entry->key; - long v = (long)entry->value; + long k = entry->key; + long v = entry->value; found_msk |= 1ULL << k; if (CHECK(v - k != 256, "elem_check", @@ -152,7 +151,7 @@ static void test_hashmap_generic(void) goto cleanup; found_cnt = 0; - hashmap__for_each_key_entry(map, entry, (void *)0) { + hashmap__for_each_key_entry(map, entry, 0) { found_cnt++; } if (CHECK(!found_cnt, "found_cnt", @@ -161,27 +160,25 @@ static void test_hashmap_generic(void) found_msk = 0; found_cnt = 0; - hashmap__for_each_key_entry_safe(map, entry, tmp, (void *)0) { - const void *oldk, *k; - void *oldv, *v; + hashmap__for_each_key_entry_safe(map, entry, tmp, 0) { + long oldk, k; + long oldv, v; k = entry->key; v = entry->value; found_cnt++; - found_msk |= 1ULL << (long)k; + found_msk |= 1ULL << k; if (CHECK(!hashmap__delete(map, k, &oldk, &oldv), "elem_del", - "failed to delete k/v %ld = %ld\n", - (long)k, (long)v)) + "failed to delete k/v %ld = %ld\n", k, v)) goto cleanup; if (CHECK(oldk != k || oldv != v, "check_old", "invalid deleted k/v: expected %ld = %ld, got %ld = %ld\n", - (long)k, (long)v, (long)oldk, (long)oldv)) + k, v, oldk, oldv)) goto cleanup; if (CHECK(hashmap__delete(map, k, &oldk, &oldv), "elem_del", - "unexpectedly deleted k/v %ld = %ld\n", - (long)oldk, (long)oldv)) + "unexpectedly deleted k/v %ld = %ld\n", oldk, oldv)) goto cleanup; } @@ -198,26 +195,24 @@ static void test_hashmap_generic(void) goto cleanup; hashmap__for_each_entry_safe(map, entry, tmp, bkt) { - const void *oldk, *k; - void *oldv, *v; + long oldk, k; + long oldv, v; k = entry->key; v = entry->value; found_cnt++; - found_msk |= 1ULL << (long)k; + found_msk |= 1ULL << k; if (CHECK(!hashmap__delete(map, k, &oldk, &oldv), "elem_del", - "failed to delete k/v %ld = %ld\n", - (long)k, (long)v)) + "failed to delete k/v %ld = %ld\n", k, v)) goto cleanup; if (CHECK(oldk != k || oldv != v, "elem_check", "invalid old k/v: expect %ld = %ld, got %ld = %ld\n", - (long)k, (long)v, (long)oldk, (long)oldv)) + k, v, oldk, oldv)) goto cleanup; if (CHECK(hashmap__delete(map, k, &oldk, &oldv), "elem_del", - "unexpectedly deleted k/v %ld = %ld\n", - (long)k, (long)v)) + "unexpectedly deleted k/v %ld = %ld\n", k, v)) goto cleanup; } @@ -235,7 +230,7 @@ static void test_hashmap_generic(void) hashmap__for_each_entry(map, entry, bkt) { CHECK(false, "elem_exists", "unexpected map entries left: %ld = %ld\n", - (long)entry->key, (long)entry->value); + entry->key, entry->value); goto cleanup; } @@ -243,7 +238,7 @@ static void test_hashmap_generic(void) hashmap__for_each_entry(map, entry, bkt) { CHECK(false, "elem_exists", "unexpected map entries left: %ld = %ld\n", - (long)entry->key, (long)entry->value); + entry->key, entry->value); goto cleanup; } @@ -251,14 +246,99 @@ cleanup: hashmap__free(map); } -static size_t collision_hash_fn(const void *k, void *ctx) +static size_t str_hash_fn(long a, void *ctx) +{ + return str_hash((char *)a); +} + +static bool str_equal_fn(long a, long b, void *ctx) +{ + return strcmp((char *)a, (char *)b) == 0; +} + +/* Verify that hashmap interface works with pointer keys and values */ +static void test_hashmap_ptr_iface(void) +{ + const char *key, *value, *old_key, *old_value; + struct hashmap_entry *cur; + struct hashmap *map; + int err, i, bkt; + + map = hashmap__new(str_hash_fn, str_equal_fn, NULL); + if (CHECK(!map, "hashmap__new", "can't allocate hashmap\n")) + goto cleanup; + +#define CHECK_STR(fn, var, expected) \ + CHECK(strcmp(var, (expected)), (fn), \ + "wrong value of " #var ": '%s' instead of '%s'\n", var, (expected)) + + err = hashmap__insert(map, "a", "apricot", HASHMAP_ADD, NULL, NULL); + if (CHECK(err, "hashmap__insert", "unexpected error: %d\n", err)) + goto cleanup; + + err = hashmap__insert(map, "a", "apple", HASHMAP_SET, &old_key, &old_value); + if (CHECK(err, "hashmap__insert", "unexpected error: %d\n", err)) + goto cleanup; + CHECK_STR("hashmap__update", old_key, "a"); + CHECK_STR("hashmap__update", old_value, "apricot"); + + err = hashmap__add(map, "b", "banana"); + if (CHECK(err, "hashmap__add", "unexpected error: %d\n", err)) + goto cleanup; + + err = hashmap__set(map, "b", "breadfruit", &old_key, &old_value); + if (CHECK(err, "hashmap__set", "unexpected error: %d\n", err)) + goto cleanup; + CHECK_STR("hashmap__set", old_key, "b"); + CHECK_STR("hashmap__set", old_value, "banana"); + + err = hashmap__update(map, "b", "blueberry", &old_key, &old_value); + if (CHECK(err, "hashmap__update", "unexpected error: %d\n", err)) + goto cleanup; + CHECK_STR("hashmap__update", old_key, "b"); + CHECK_STR("hashmap__update", old_value, "breadfruit"); + + err = hashmap__append(map, "c", "cherry"); + if (CHECK(err, "hashmap__append", "unexpected error: %d\n", err)) + goto cleanup; + + if (CHECK(!hashmap__delete(map, "c", &old_key, &old_value), + "hashmap__delete", "expected to have entry for 'c'\n")) + goto cleanup; + CHECK_STR("hashmap__delete", old_key, "c"); + CHECK_STR("hashmap__delete", old_value, "cherry"); + + CHECK(!hashmap__find(map, "b", &value), "hashmap__find", "can't find value for 'b'\n"); + CHECK_STR("hashmap__find", value, "blueberry"); + + if (CHECK(!hashmap__delete(map, "b", NULL, NULL), + "hashmap__delete", "expected to have entry for 'b'\n")) + goto cleanup; + + i = 0; + hashmap__for_each_entry(map, cur, bkt) { + if (CHECK(i != 0, "hashmap__for_each_entry", "too many entries")) + goto cleanup; + key = cur->pkey; + value = cur->pvalue; + CHECK_STR("entry", key, "a"); + CHECK_STR("entry", value, "apple"); + i++; + } +#undef CHECK_STR + +cleanup: + hashmap__free(map); +} + +static size_t collision_hash_fn(long k, void *ctx) { return 0; } static void test_hashmap_multimap(void) { - void *k1 = (void *)0, *k2 = (void *)1; + long k1 = 0, k2 = 1; struct hashmap_entry *entry; struct hashmap *map; long found_msk; @@ -273,23 +353,23 @@ static void test_hashmap_multimap(void) * [0] -> 1, 2, 4; * [1] -> 8, 16, 32; */ - err = hashmap__append(map, k1, (void *)1); + err = hashmap__append(map, k1, 1); if (CHECK(err, "elem_add", "failed to add k/v: %d\n", err)) goto cleanup; - err = hashmap__append(map, k1, (void *)2); + err = hashmap__append(map, k1, 2); if (CHECK(err, "elem_add", "failed to add k/v: %d\n", err)) goto cleanup; - err = hashmap__append(map, k1, (void *)4); + err = hashmap__append(map, k1, 4); if (CHECK(err, "elem_add", "failed to add k/v: %d\n", err)) goto cleanup; - err = hashmap__append(map, k2, (void *)8); + err = hashmap__append(map, k2, 8); if (CHECK(err, "elem_add", "failed to add k/v: %d\n", err)) goto cleanup; - err = hashmap__append(map, k2, (void *)16); + err = hashmap__append(map, k2, 16); if (CHECK(err, "elem_add", "failed to add k/v: %d\n", err)) goto cleanup; - err = hashmap__append(map, k2, (void *)32); + err = hashmap__append(map, k2, 32); if (CHECK(err, "elem_add", "failed to add k/v: %d\n", err)) goto cleanup; @@ -300,7 +380,7 @@ static void test_hashmap_multimap(void) /* verify global iteration still works and sees all values */ found_msk = 0; hashmap__for_each_entry(map, entry, bkt) { - found_msk |= (long)entry->value; + found_msk |= entry->value; } if (CHECK(found_msk != (1 << 6) - 1, "found_msk", "not all keys iterated: %lx\n", found_msk)) @@ -309,7 +389,7 @@ static void test_hashmap_multimap(void) /* iterate values for key 1 */ found_msk = 0; hashmap__for_each_key_entry(map, entry, k1) { - found_msk |= (long)entry->value; + found_msk |= entry->value; } if (CHECK(found_msk != (1 | 2 | 4), "found_msk", "invalid k1 values: %lx\n", found_msk)) @@ -318,7 +398,7 @@ static void test_hashmap_multimap(void) /* iterate values for key 2 */ found_msk = 0; hashmap__for_each_key_entry(map, entry, k2) { - found_msk |= (long)entry->value; + found_msk |= entry->value; } if (CHECK(found_msk != (8 | 16 | 32), "found_msk", "invalid k2 values: %lx\n", found_msk)) @@ -333,7 +413,7 @@ static void test_hashmap_empty() struct hashmap_entry *entry; int bkt; struct hashmap *map; - void *k = (void *)0; + long k = 0; /* force collisions */ map = hashmap__new(hash_fn, equal_fn, NULL); @@ -374,4 +454,6 @@ void test_hashmap() test_hashmap_multimap(); if (test__start_subtest("empty")) test_hashmap_empty(); + if (test__start_subtest("ptr_iface")) + test_hashmap_ptr_iface(); } diff --git a/tools/testing/selftests/bpf/prog_tests/kprobe_multi_test.c b/tools/testing/selftests/bpf/prog_tests/kprobe_multi_test.c index 287b3ac40227..eedbf1937fc4 100644 --- a/tools/testing/selftests/bpf/prog_tests/kprobe_multi_test.c +++ b/tools/testing/selftests/bpf/prog_tests/kprobe_multi_test.c @@ -312,12 +312,12 @@ static inline __u64 get_time_ns(void) return (__u64) t.tv_sec * 1000000000 + t.tv_nsec; } -static size_t symbol_hash(const void *key, void *ctx __maybe_unused) +static size_t symbol_hash(long key, void *ctx __maybe_unused) { return str_hash((const char *) key); } -static bool symbol_equal(const void *key1, const void *key2, void *ctx __maybe_unused) +static bool symbol_equal(long key1, long key2, void *ctx __maybe_unused) { return strcmp((const char *) key1, (const char *) key2) == 0; } @@ -372,7 +372,7 @@ static int get_syms(char ***symsp, size_t *cntp) sizeof("__ftrace_invalid_address__") - 1)) continue; - err = hashmap__add(map, name, NULL); + err = hashmap__add(map, name, 0); if (err == -EEXIST) continue; if (err) From 082108fd6932772deb7e329f118687b4c03fc6a5 Mon Sep 17 00:00:00 2001 From: Eduard Zingerman Date: Wed, 9 Nov 2022 16:26:10 +0200 Subject: [PATCH 0779/2002] libbpf: Resolve unambigous forward declarations Resolve forward declarations that don't take part in type graphs comparisons if declaration name is unambiguous. Example: CU #1: struct foo; // standalone forward declaration struct foo *some_global; CU #2: struct foo { int x; }; struct foo *another_global; The `struct foo` from CU #1 is not a part of any definition that is compared against another definition while `btf_dedup_struct_types` processes structural types. The the BTF after `btf_dedup_struct_types` the BTF looks as follows: [1] STRUCT 'foo' size=4 vlen=1 ... [2] INT 'int' size=4 ... [3] PTR '(anon)' type_id=1 [4] FWD 'foo' fwd_kind=struct [5] PTR '(anon)' type_id=4 This commit adds a new pass `btf_dedup_resolve_fwds`, that maps such forward declarations to structs or unions with identical name in case if the name is not ambiguous. The pass is positioned before `btf_dedup_ref_types` so that types [3] and [5] could be merged as a same type after [1] and [4] are merged. The final result for the example above looks as follows: [1] STRUCT 'foo' size=4 vlen=1 'x' type_id=2 bits_offset=0 [2] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED [3] PTR '(anon)' type_id=1 For defconfig kernel with BTF enabled this removes 63 forward declarations. Examples of removed declarations: `pt_regs`, `in6_addr`. The running time of `btf__dedup` function is increased by about 3%. Signed-off-by: Eduard Zingerman Signed-off-by: Andrii Nakryiko Reviewed-by: Alan Maguire Acked-by: Andrii Nakryiko Link: https://lore.kernel.org/bpf/20221109142611.879983-3-eddyz87@gmail.com --- tools/lib/bpf/btf.c | 143 ++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 139 insertions(+), 4 deletions(-) diff --git a/tools/lib/bpf/btf.c b/tools/lib/bpf/btf.c index 442d4d0f98b8..3bd812bf88ff 100644 --- a/tools/lib/bpf/btf.c +++ b/tools/lib/bpf/btf.c @@ -2881,6 +2881,7 @@ static int btf_dedup_strings(struct btf_dedup *d); static int btf_dedup_prim_types(struct btf_dedup *d); static int btf_dedup_struct_types(struct btf_dedup *d); static int btf_dedup_ref_types(struct btf_dedup *d); +static int btf_dedup_resolve_fwds(struct btf_dedup *d); static int btf_dedup_compact_types(struct btf_dedup *d); static int btf_dedup_remap_types(struct btf_dedup *d); @@ -2988,15 +2989,16 @@ static int btf_dedup_remap_types(struct btf_dedup *d); * Algorithm summary * ================= * - * Algorithm completes its work in 6 separate passes: + * Algorithm completes its work in 7 separate passes: * * 1. Strings deduplication. * 2. Primitive types deduplication (int, enum, fwd). * 3. Struct/union types deduplication. - * 4. Reference types deduplication (pointers, typedefs, arrays, funcs, func + * 4. Resolve unambiguous forward declarations. + * 5. Reference types deduplication (pointers, typedefs, arrays, funcs, func * protos, and const/volatile/restrict modifiers). - * 5. Types compaction. - * 6. Types remapping. + * 6. Types compaction. + * 7. Types remapping. * * Algorithm determines canonical type descriptor, which is a single * representative type for each truly unique type. This canonical type is the @@ -3060,6 +3062,11 @@ int btf__dedup(struct btf *btf, const struct btf_dedup_opts *opts) pr_debug("btf_dedup_struct_types failed:%d\n", err); goto done; } + err = btf_dedup_resolve_fwds(d); + if (err < 0) { + pr_debug("btf_dedup_resolve_fwds failed:%d\n", err); + goto done; + } err = btf_dedup_ref_types(d); if (err < 0) { pr_debug("btf_dedup_ref_types failed:%d\n", err); @@ -4501,6 +4508,134 @@ static int btf_dedup_ref_types(struct btf_dedup *d) return 0; } +/* + * Collect a map from type names to type ids for all canonical structs + * and unions. If the same name is shared by several canonical types + * use a special value 0 to indicate this fact. + */ +static int btf_dedup_fill_unique_names_map(struct btf_dedup *d, struct hashmap *names_map) +{ + __u32 nr_types = btf__type_cnt(d->btf); + struct btf_type *t; + __u32 type_id; + __u16 kind; + int err; + + /* + * Iterate over base and split module ids in order to get all + * available structs in the map. + */ + for (type_id = 1; type_id < nr_types; ++type_id) { + t = btf_type_by_id(d->btf, type_id); + kind = btf_kind(t); + + if (kind != BTF_KIND_STRUCT && kind != BTF_KIND_UNION) + continue; + + /* Skip non-canonical types */ + if (type_id != d->map[type_id]) + continue; + + err = hashmap__add(names_map, t->name_off, type_id); + if (err == -EEXIST) + err = hashmap__set(names_map, t->name_off, 0, NULL, NULL); + + if (err) + return err; + } + + return 0; +} + +static int btf_dedup_resolve_fwd(struct btf_dedup *d, struct hashmap *names_map, __u32 type_id) +{ + struct btf_type *t = btf_type_by_id(d->btf, type_id); + enum btf_fwd_kind fwd_kind = btf_kflag(t); + __u16 cand_kind, kind = btf_kind(t); + struct btf_type *cand_t; + uintptr_t cand_id; + + if (kind != BTF_KIND_FWD) + return 0; + + /* Skip if this FWD already has a mapping */ + if (type_id != d->map[type_id]) + return 0; + + if (!hashmap__find(names_map, t->name_off, &cand_id)) + return 0; + + /* Zero is a special value indicating that name is not unique */ + if (!cand_id) + return 0; + + cand_t = btf_type_by_id(d->btf, cand_id); + cand_kind = btf_kind(cand_t); + if ((cand_kind == BTF_KIND_STRUCT && fwd_kind != BTF_FWD_STRUCT) || + (cand_kind == BTF_KIND_UNION && fwd_kind != BTF_FWD_UNION)) + return 0; + + d->map[type_id] = cand_id; + + return 0; +} + +/* + * Resolve unambiguous forward declarations. + * + * The lion's share of all FWD declarations is resolved during + * `btf_dedup_struct_types` phase when different type graphs are + * compared against each other. However, if in some compilation unit a + * FWD declaration is not a part of a type graph compared against + * another type graph that declaration's canonical type would not be + * changed. Example: + * + * CU #1: + * + * struct foo; + * struct foo *some_global; + * + * CU #2: + * + * struct foo { int u; }; + * struct foo *another_global; + * + * After `btf_dedup_struct_types` the BTF looks as follows: + * + * [1] STRUCT 'foo' size=4 vlen=1 ... + * [2] INT 'int' size=4 ... + * [3] PTR '(anon)' type_id=1 + * [4] FWD 'foo' fwd_kind=struct + * [5] PTR '(anon)' type_id=4 + * + * This pass assumes that such FWD declarations should be mapped to + * structs or unions with identical name in case if the name is not + * ambiguous. + */ +static int btf_dedup_resolve_fwds(struct btf_dedup *d) +{ + int i, err; + struct hashmap *names_map; + + names_map = hashmap__new(btf_dedup_identity_hash_fn, btf_dedup_equal_fn, NULL); + if (IS_ERR(names_map)) + return PTR_ERR(names_map); + + err = btf_dedup_fill_unique_names_map(d, names_map); + if (err < 0) + goto exit; + + for (i = 0; i < d->btf->nr_types; i++) { + err = btf_dedup_resolve_fwd(d, names_map, d->btf->start_id + i); + if (err < 0) + break; + } + +exit: + hashmap__free(names_map); + return err; +} + /* * Compact types. * From 99e18fad5ff79eea2ea9ec7e6d800b286552e005 Mon Sep 17 00:00:00 2001 From: Eduard Zingerman Date: Wed, 9 Nov 2022 16:26:11 +0200 Subject: [PATCH 0780/2002] selftests/bpf: Tests for btf_dedup_resolve_fwds Tests to verify the following behavior of `btf_dedup_resolve_fwds`: - remapping for struct forward declarations; - remapping for union forward declarations; - no remapping if forward declaration kind does not match similarly named struct or union declaration; - no remapping if forward declaration name is ambiguous; - base ids are considered for fwd resolution in split btf scenario. Signed-off-by: Eduard Zingerman Signed-off-by: Andrii Nakryiko Reviewed-by: Alan Maguire Link: https://lore.kernel.org/bpf/20221109142611.879983-4-eddyz87@gmail.com --- tools/testing/selftests/bpf/prog_tests/btf.c | 176 ++++++++++++++++++ .../bpf/prog_tests/btf_dedup_split.c | 45 +++-- 2 files changed, 206 insertions(+), 15 deletions(-) diff --git a/tools/testing/selftests/bpf/prog_tests/btf.c b/tools/testing/selftests/bpf/prog_tests/btf.c index c3e1cea9abae..95a2b80f0d17 100644 --- a/tools/testing/selftests/bpf/prog_tests/btf.c +++ b/tools/testing/selftests/bpf/prog_tests/btf.c @@ -7690,6 +7690,182 @@ static struct btf_dedup_test dedup_tests[] = { BTF_STR_SEC("\0e1\0e1_val\0td"), }, }, +{ + .descr = "dedup: standalone fwd declaration struct", + /* + * Verify that CU1:foo and CU2:foo would be unified and that + * typedef/ptr would be updated to point to CU1:foo. + * + * // CU 1: + * struct foo { int x; }; + * + * // CU 2: + * struct foo; + * typedef struct foo *foo_ptr; + */ + .input = { + .raw_types = { + /* CU 1 */ + BTF_STRUCT_ENC(NAME_NTH(1), 1, 4), /* [1] */ + BTF_MEMBER_ENC(NAME_NTH(2), 2, 0), + BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4), /* [2] */ + /* CU 2 */ + BTF_FWD_ENC(NAME_NTH(1), 0), /* [3] */ + BTF_PTR_ENC(3), /* [4] */ + BTF_TYPEDEF_ENC(NAME_NTH(3), 4), /* [5] */ + BTF_END_RAW, + }, + BTF_STR_SEC("\0foo\0x\0foo_ptr"), + }, + .expect = { + .raw_types = { + BTF_STRUCT_ENC(NAME_NTH(1), 1, 4), /* [1] */ + BTF_MEMBER_ENC(NAME_NTH(2), 2, 0), + BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4), /* [2] */ + BTF_PTR_ENC(1), /* [3] */ + BTF_TYPEDEF_ENC(NAME_NTH(3), 3), /* [4] */ + BTF_END_RAW, + }, + BTF_STR_SEC("\0foo\0x\0foo_ptr"), + }, +}, +{ + .descr = "dedup: standalone fwd declaration union", + /* + * Verify that CU1:foo and CU2:foo would be unified and that + * typedef/ptr would be updated to point to CU1:foo. + * Same as "dedup: standalone fwd declaration struct" but for unions. + * + * // CU 1: + * union foo { int x; }; + * + * // CU 2: + * union foo; + * typedef union foo *foo_ptr; + */ + .input = { + .raw_types = { + /* CU 1 */ + BTF_UNION_ENC(NAME_NTH(1), 1, 4), /* [1] */ + BTF_MEMBER_ENC(NAME_NTH(2), 2, 0), + BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4), /* [2] */ + /* CU 2 */ + BTF_FWD_ENC(NAME_TBD, 1), /* [3] */ + BTF_PTR_ENC(3), /* [4] */ + BTF_TYPEDEF_ENC(NAME_NTH(3), 4), /* [5] */ + BTF_END_RAW, + }, + BTF_STR_SEC("\0foo\0x\0foo_ptr"), + }, + .expect = { + .raw_types = { + BTF_UNION_ENC(NAME_NTH(1), 1, 4), /* [1] */ + BTF_MEMBER_ENC(NAME_NTH(2), 2, 0), + BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4), /* [2] */ + BTF_PTR_ENC(1), /* [3] */ + BTF_TYPEDEF_ENC(NAME_NTH(3), 3), /* [4] */ + BTF_END_RAW, + }, + BTF_STR_SEC("\0foo\0x\0foo_ptr"), + }, +}, +{ + .descr = "dedup: standalone fwd declaration wrong kind", + /* + * Negative test for btf_dedup_resolve_fwds: + * - CU1:foo is a struct, C2:foo is a union, thus CU2:foo is not deduped; + * - typedef/ptr should remain unchanged as well. + * + * // CU 1: + * struct foo { int x; }; + * + * // CU 2: + * union foo; + * typedef union foo *foo_ptr; + */ + .input = { + .raw_types = { + /* CU 1 */ + BTF_STRUCT_ENC(NAME_NTH(1), 1, 4), /* [1] */ + BTF_MEMBER_ENC(NAME_NTH(2), 2, 0), + BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4), /* [2] */ + /* CU 2 */ + BTF_FWD_ENC(NAME_NTH(3), 1), /* [3] */ + BTF_PTR_ENC(3), /* [4] */ + BTF_TYPEDEF_ENC(NAME_NTH(3), 4), /* [5] */ + BTF_END_RAW, + }, + BTF_STR_SEC("\0foo\0x\0foo_ptr"), + }, + .expect = { + .raw_types = { + /* CU 1 */ + BTF_STRUCT_ENC(NAME_NTH(1), 1, 4), /* [1] */ + BTF_MEMBER_ENC(NAME_NTH(2), 2, 0), + BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4), /* [2] */ + /* CU 2 */ + BTF_FWD_ENC(NAME_NTH(3), 1), /* [3] */ + BTF_PTR_ENC(3), /* [4] */ + BTF_TYPEDEF_ENC(NAME_NTH(3), 4), /* [5] */ + BTF_END_RAW, + }, + BTF_STR_SEC("\0foo\0x\0foo_ptr"), + }, +}, +{ + .descr = "dedup: standalone fwd declaration name conflict", + /* + * Negative test for btf_dedup_resolve_fwds: + * - two candidates for CU2:foo dedup, thus it is unchanged; + * - typedef/ptr should remain unchanged as well. + * + * // CU 1: + * struct foo { int x; }; + * + * // CU 2: + * struct foo; + * typedef struct foo *foo_ptr; + * + * // CU 3: + * struct foo { int x; int y; }; + */ + .input = { + .raw_types = { + /* CU 1 */ + BTF_STRUCT_ENC(NAME_NTH(1), 1, 4), /* [1] */ + BTF_MEMBER_ENC(NAME_NTH(2), 2, 0), + BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4), /* [2] */ + /* CU 2 */ + BTF_FWD_ENC(NAME_NTH(1), 0), /* [3] */ + BTF_PTR_ENC(3), /* [4] */ + BTF_TYPEDEF_ENC(NAME_NTH(4), 4), /* [5] */ + /* CU 3 */ + BTF_STRUCT_ENC(NAME_NTH(1), 2, 8), /* [6] */ + BTF_MEMBER_ENC(NAME_NTH(2), 2, 0), + BTF_MEMBER_ENC(NAME_NTH(3), 2, 0), + BTF_END_RAW, + }, + BTF_STR_SEC("\0foo\0x\0y\0foo_ptr"), + }, + .expect = { + .raw_types = { + /* CU 1 */ + BTF_STRUCT_ENC(NAME_NTH(1), 1, 4), /* [1] */ + BTF_MEMBER_ENC(NAME_NTH(2), 2, 0), + BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4), /* [2] */ + /* CU 2 */ + BTF_FWD_ENC(NAME_NTH(1), 0), /* [3] */ + BTF_PTR_ENC(3), /* [4] */ + BTF_TYPEDEF_ENC(NAME_NTH(4), 4), /* [5] */ + /* CU 3 */ + BTF_STRUCT_ENC(NAME_NTH(1), 2, 8), /* [6] */ + BTF_MEMBER_ENC(NAME_NTH(2), 2, 0), + BTF_MEMBER_ENC(NAME_NTH(3), 2, 0), + BTF_END_RAW, + }, + BTF_STR_SEC("\0foo\0x\0y\0foo_ptr"), + }, +}, }; static int btf_type_size(const struct btf_type *t) diff --git a/tools/testing/selftests/bpf/prog_tests/btf_dedup_split.c b/tools/testing/selftests/bpf/prog_tests/btf_dedup_split.c index 90aac437576d..d9024c7a892a 100644 --- a/tools/testing/selftests/bpf/prog_tests/btf_dedup_split.c +++ b/tools/testing/selftests/bpf/prog_tests/btf_dedup_split.c @@ -141,6 +141,10 @@ static void test_split_fwd_resolve() { btf__add_field(btf1, "f2", 3, 64, 0); /* struct s2 *f2; */ /* } */ btf__add_struct(btf1, "s2", 4); /* [5] struct s2 { */ + btf__add_field(btf1, "f1", 1, 0, 0); /* int f1; */ + /* } */ + /* keep this not a part of type the graph to test btf_dedup_resolve_fwds */ + btf__add_struct(btf1, "s3", 4); /* [6] struct s3 { */ btf__add_field(btf1, "f1", 1, 0, 0); /* int f1; */ /* } */ @@ -153,20 +157,24 @@ static void test_split_fwd_resolve() { "\t'f1' type_id=2 bits_offset=0\n" "\t'f2' type_id=3 bits_offset=64", "[5] STRUCT 's2' size=4 vlen=1\n" + "\t'f1' type_id=1 bits_offset=0", + "[6] STRUCT 's3' size=4 vlen=1\n" "\t'f1' type_id=1 bits_offset=0"); btf2 = btf__new_empty_split(btf1); if (!ASSERT_OK_PTR(btf2, "empty_split_btf")) goto cleanup; - btf__add_int(btf2, "int", 4, BTF_INT_SIGNED); /* [6] int */ - btf__add_ptr(btf2, 10); /* [7] ptr to struct s1 */ - btf__add_fwd(btf2, "s2", BTF_FWD_STRUCT); /* [8] fwd for struct s2 */ - btf__add_ptr(btf2, 8); /* [9] ptr to fwd struct s2 */ - btf__add_struct(btf2, "s1", 16); /* [10] struct s1 { */ - btf__add_field(btf2, "f1", 7, 0, 0); /* struct s1 *f1; */ - btf__add_field(btf2, "f2", 9, 64, 0); /* struct s2 *f2; */ + btf__add_int(btf2, "int", 4, BTF_INT_SIGNED); /* [7] int */ + btf__add_ptr(btf2, 11); /* [8] ptr to struct s1 */ + btf__add_fwd(btf2, "s2", BTF_FWD_STRUCT); /* [9] fwd for struct s2 */ + btf__add_ptr(btf2, 9); /* [10] ptr to fwd struct s2 */ + btf__add_struct(btf2, "s1", 16); /* [11] struct s1 { */ + btf__add_field(btf2, "f1", 8, 0, 0); /* struct s1 *f1; */ + btf__add_field(btf2, "f2", 10, 64, 0); /* struct s2 *f2; */ /* } */ + btf__add_fwd(btf2, "s3", BTF_FWD_STRUCT); /* [12] fwd for struct s3 */ + btf__add_ptr(btf2, 12); /* [13] ptr to struct s1 */ VALIDATE_RAW_BTF( btf2, @@ -178,13 +186,17 @@ static void test_split_fwd_resolve() { "\t'f2' type_id=3 bits_offset=64", "[5] STRUCT 's2' size=4 vlen=1\n" "\t'f1' type_id=1 bits_offset=0", - "[6] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED", - "[7] PTR '(anon)' type_id=10", - "[8] FWD 's2' fwd_kind=struct", - "[9] PTR '(anon)' type_id=8", - "[10] STRUCT 's1' size=16 vlen=2\n" - "\t'f1' type_id=7 bits_offset=0\n" - "\t'f2' type_id=9 bits_offset=64"); + "[6] STRUCT 's3' size=4 vlen=1\n" + "\t'f1' type_id=1 bits_offset=0", + "[7] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED", + "[8] PTR '(anon)' type_id=11", + "[9] FWD 's2' fwd_kind=struct", + "[10] PTR '(anon)' type_id=9", + "[11] STRUCT 's1' size=16 vlen=2\n" + "\t'f1' type_id=8 bits_offset=0\n" + "\t'f2' type_id=10 bits_offset=64", + "[12] FWD 's3' fwd_kind=struct", + "[13] PTR '(anon)' type_id=12"); err = btf__dedup(btf2, NULL); if (!ASSERT_OK(err, "btf_dedup")) @@ -199,7 +211,10 @@ static void test_split_fwd_resolve() { "\t'f1' type_id=2 bits_offset=0\n" "\t'f2' type_id=3 bits_offset=64", "[5] STRUCT 's2' size=4 vlen=1\n" - "\t'f1' type_id=1 bits_offset=0"); + "\t'f1' type_id=1 bits_offset=0", + "[6] STRUCT 's3' size=4 vlen=1\n" + "\t'f1' type_id=1 bits_offset=0", + "[7] PTR '(anon)' type_id=6"); cleanup: btf__free(btf2); From fd74b79df0d1c9506583d0b52e3d7562a38ac34a Mon Sep 17 00:00:00 2001 From: Domenico Cerasuolo Date: Wed, 9 Nov 2022 10:40:39 -0800 Subject: [PATCH 0781/2002] selftests: Fix test group SKIPPED result When showing the result of a test group, if one of the subtests was skipped, while still having passing subtests, the group result was marked as SKIP. E.g.: 223/1 usdt/basic:SKIP 223/2 usdt/multispec:OK 223/3 usdt/urand_auto_attach:OK 223/4 usdt/urand_pid_attach:OK 223 usdt:SKIP The test result of usdt in the example above should be OK instead of SKIP, because the test group did have passing tests and it would be considered in "normal" state. With this change, only if all of the subtests were skipped, the group test is marked as SKIP. When only some of the subtests are skipped, a more detailed result is given, stating how many of the subtests were skipped. E.g: 223/1 usdt/basic:SKIP 223/2 usdt/multispec:OK 223/3 usdt/urand_auto_attach:OK 223/4 usdt/urand_pid_attach:OK 223 usdt:OK (SKIP: 1/4) Signed-off-by: Domenico Cerasuolo Signed-off-by: Andrii Nakryiko Acked-by: Yonghong Song Link: https://lore.kernel.org/bpf/20221109184039.3514033-1-cerasuolodomenico@gmail.com --- tools/testing/selftests/bpf/test_progs.c | 38 ++++++++++++++---------- 1 file changed, 22 insertions(+), 16 deletions(-) diff --git a/tools/testing/selftests/bpf/test_progs.c b/tools/testing/selftests/bpf/test_progs.c index 0e9a47f97890..c34f37d7a523 100644 --- a/tools/testing/selftests/bpf/test_progs.c +++ b/tools/testing/selftests/bpf/test_progs.c @@ -222,6 +222,26 @@ static char *test_result(bool failed, bool skipped) return failed ? "FAIL" : (skipped ? "SKIP" : "OK"); } +#define TEST_NUM_WIDTH 7 + +static void print_test_result(const struct prog_test_def *test, const struct test_state *test_state) +{ + int skipped_cnt = test_state->skip_cnt; + int subtests_cnt = test_state->subtest_num; + + fprintf(env.stdout, "#%-*d %s:", TEST_NUM_WIDTH, test->test_num, test->test_name); + if (test_state->error_cnt) + fprintf(env.stdout, "FAIL"); + else if (!skipped_cnt) + fprintf(env.stdout, "OK"); + else if (skipped_cnt == subtests_cnt || !subtests_cnt) + fprintf(env.stdout, "SKIP"); + else + fprintf(env.stdout, "OK (SKIP: %d/%d)", skipped_cnt, subtests_cnt); + + fprintf(env.stdout, "\n"); +} + static void print_test_log(char *log_buf, size_t log_cnt) { log_buf[log_cnt] = '\0'; @@ -230,18 +250,6 @@ static void print_test_log(char *log_buf, size_t log_cnt) fprintf(env.stdout, "\n"); } -#define TEST_NUM_WIDTH 7 - -static void print_test_name(int test_num, const char *test_name, char *result) -{ - fprintf(env.stdout, "#%-*d %s", TEST_NUM_WIDTH, test_num, test_name); - - if (result) - fprintf(env.stdout, ":%s", result); - - fprintf(env.stdout, "\n"); -} - static void print_subtest_name(int test_num, int subtest_num, const char *test_name, char *subtest_name, char *result) @@ -307,8 +315,7 @@ static void dump_test_log(const struct prog_test_def *test, subtest_state->skipped)); } - print_test_name(test->test_num, test->test_name, - test_result(test_failed, test_state->skip_cnt)); + print_test_result(test, test_state); } static void stdio_restore(void); @@ -1070,8 +1077,7 @@ static void run_one_test(int test_num) state->tested = true; if (verbose() && env.worker_id == -1) - print_test_name(test_num + 1, test->test_name, - test_result(state->error_cnt, state->skip_cnt)); + print_test_result(test, state); reset_affinity(); restore_netns(); From 5e5dc33d5dacb34b0165061bc5a10efd2fd3b66f Mon Sep 17 00:00:00 2001 From: John Fastabend Date: Tue, 8 Nov 2022 14:16:50 -0800 Subject: [PATCH 0782/2002] bpf: veth driver panics when xdp prog attached before veth_open The following panic is observed when bringing up (veth_open) a veth device that has an XDP program attached. [ 61.519185] kernel BUG at net/core/dev.c:6442! [ 61.519456] invalid opcode: 0000 [#1] PREEMPT SMP PTI [ 61.519752] CPU: 0 PID: 408 Comm: ip Tainted: G W 6.1.0-rc2-185930-gd9095f92950b-dirty #26 [ 61.520288] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.15.0-1 04/01/2014 [ 61.520806] RIP: 0010:napi_enable+0x3d/0x40 [ 61.521077] Code: f6 f6 80 61 08 00 00 02 74 0d 48 83 bf 88 01 00 00 00 74 03 80 cd 01 48 89 d0 f0 48 0f b1 4f 10 48 39 c2 75 c8 c3 cc cc cc cc <0f> 0b 90 48 8b 87 b0 00 00 00 48 81 c7 b0 00 00 00 45 31 c0 48 39 [ 61.522226] RSP: 0018:ffffbc9800cc36f8 EFLAGS: 00010246 [ 61.522557] RAX: 0000000000000001 RBX: 0000000000000300 RCX: 0000000000000001 [ 61.523004] RDX: 0000000000000010 RSI: ffffffff8b0de852 RDI: ffff9f03848e5000 [ 61.523452] RBP: 0000000000000000 R08: 0000000000000800 R09: 0000000000000000 [ 61.523899] R10: ffff9f0384a96800 R11: ffffffffffa48061 R12: ffff9f03849c3000 [ 61.524345] R13: 0000000000000300 R14: ffff9f03848e5000 R15: 0000001000000100 [ 61.524792] FS: 00007f58cb64d2c0(0000) GS:ffff9f03bbc00000(0000) knlGS:0000000000000000 [ 61.525301] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 [ 61.525673] CR2: 00007f6cc629b498 CR3: 000000010498c000 CR4: 00000000000006f0 [ 61.526121] Call Trace: [ 61.526284] [ 61.526425] __veth_napi_enable_range+0xd6/0x230 [ 61.526723] veth_enable_xdp+0xd0/0x160 [ 61.526969] veth_open+0x2e/0xc0 [ 61.527180] __dev_open+0xe2/0x1b0 [ 61.527405] __dev_change_flags+0x1a1/0x210 [ 61.527673] dev_change_flags+0x1c/0x60 This happens because we are calling veth_napi_enable() on already enabled queues. The root cause is in commit 2e0de6366ac16 changed the control logic dropping this case, if (priv->_xdp_prog) { err = veth_enable_xdp(dev); if (err) return err; - } else if (veth_gro_requested(dev)) { + /* refer to the logic in veth_xdp_set() */ + if (!rtnl_dereference(peer_rq->napi)) { + err = veth_napi_enable(peer); + if (err) + return err; + } so that now veth_napi_enable is called if the peer has not yet initialiazed its peer_rq->napi. The issue is this will happen even if the NIC is not up. Then in veth_enable_xdp just above we have similar path, veth_enable_xdp napi_already_on = (dev->flags & IFF_UP) && rcu_access_pointer(rq->napi) err = veth_enable_xdp_range(dev, 0, dev->real_num_rx_queues, napi_already_on); The trouble is an xdp prog is assigned before bringing the device up each of the veth_open path will enable the peers xdp napi structs. But then when we bring the peer up it will similar try to enable again because from veth_open the IFF_UP flag is not set until after the op in __dev_open so we believe napi_alread_on = false. To fix this just drop the IFF_UP test and rely on checking if the napi struct is enabled. This also matches the peer check in veth_xdp for disabling. To reproduce run ./test_xdp_meta.sh I found adding Cilium/Tetragon tests for XDP. Fixes: 2e0de6366ac16 ("veth: Avoid drop packets when xdp_redirect performs") Signed-off-by: John Fastabend Link: https://lore.kernel.org/r/20221108221650.808950-2-john.fastabend@gmail.com Acked-by: Jakub Kicinski Signed-off-by: Martin KaFai Lau --- drivers/net/veth.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/veth.c b/drivers/net/veth.c index b1ed5a93b6c5..2a4592780141 100644 --- a/drivers/net/veth.c +++ b/drivers/net/veth.c @@ -1125,7 +1125,7 @@ static int veth_enable_xdp(struct net_device *dev) int err, i; rq = &priv->rq[0]; - napi_already_on = (dev->flags & IFF_UP) && rcu_access_pointer(rq->napi); + napi_already_on = rcu_access_pointer(rq->napi); if (!xdp_rxq_info_is_reg(&priv->rq[0].xdp_rxq)) { err = veth_enable_xdp_range(dev, 0, dev->real_num_rx_queues, napi_already_on); From a69839d4327d053b18d8e1b0e7ddeee78db78f4f Mon Sep 17 00:00:00 2001 From: Long Li Date: Thu, 3 Nov 2022 12:16:19 -0700 Subject: [PATCH 0783/2002] net: mana: Add support for auxiliary device In preparation for supporting MANA RDMA driver, add support for auxiliary device in the Ethernet driver. The RDMA device is modeled as an auxiliary device to the Ethernet device. Reviewed-by: Dexuan Cui Signed-off-by: Long Li Link: https://lore.kernel.org/r/1667502990-2559-2-git-send-email-longli@linuxonhyperv.com Acked-by: Haiyang Zhang Signed-off-by: Leon Romanovsky --- drivers/net/ethernet/microsoft/Kconfig | 1 + drivers/net/ethernet/microsoft/mana/gdma.h | 2 + .../ethernet/microsoft/mana/mana_auxiliary.h | 10 +++ drivers/net/ethernet/microsoft/mana/mana_en.c | 83 ++++++++++++++++++- 4 files changed, 95 insertions(+), 1 deletion(-) create mode 100644 drivers/net/ethernet/microsoft/mana/mana_auxiliary.h diff --git a/drivers/net/ethernet/microsoft/Kconfig b/drivers/net/ethernet/microsoft/Kconfig index fe4e7a7d9c0b..090e6b983243 100644 --- a/drivers/net/ethernet/microsoft/Kconfig +++ b/drivers/net/ethernet/microsoft/Kconfig @@ -19,6 +19,7 @@ config MICROSOFT_MANA tristate "Microsoft Azure Network Adapter (MANA) support" depends on PCI_MSI && X86_64 depends on PCI_HYPERV + select AUXILIARY_BUS help This driver supports Microsoft Azure Network Adapter (MANA). So far, the driver is only supported on X86_64. diff --git a/drivers/net/ethernet/microsoft/mana/gdma.h b/drivers/net/ethernet/microsoft/mana/gdma.h index 4a6efe6ada08..f321a2616d03 100644 --- a/drivers/net/ethernet/microsoft/mana/gdma.h +++ b/drivers/net/ethernet/microsoft/mana/gdma.h @@ -204,6 +204,8 @@ struct gdma_dev { /* GDMA driver specific pointer */ void *driver_data; + + struct auxiliary_device *adev; }; #define MINIMUM_SUPPORTED_PAGE_SIZE PAGE_SIZE diff --git a/drivers/net/ethernet/microsoft/mana/mana_auxiliary.h b/drivers/net/ethernet/microsoft/mana/mana_auxiliary.h new file mode 100644 index 000000000000..373d59756846 --- /dev/null +++ b/drivers/net/ethernet/microsoft/mana/mana_auxiliary.h @@ -0,0 +1,10 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* Copyright (c) 2022, Microsoft Corporation. */ + +#include "mana.h" +#include + +struct mana_adev { + struct auxiliary_device adev; + struct gdma_dev *mdev; +}; diff --git a/drivers/net/ethernet/microsoft/mana/mana_en.c b/drivers/net/ethernet/microsoft/mana/mana_en.c index 9259a74eca40..8751e475d1ba 100644 --- a/drivers/net/ethernet/microsoft/mana/mana_en.c +++ b/drivers/net/ethernet/microsoft/mana/mana_en.c @@ -13,6 +13,19 @@ #include #include "mana.h" +#include "mana_auxiliary.h" + +static DEFINE_IDA(mana_adev_ida); + +static int mana_adev_idx_alloc(void) +{ + return ida_alloc(&mana_adev_ida, GFP_KERNEL); +} + +static void mana_adev_idx_free(int idx) +{ + ida_free(&mana_adev_ida, idx); +} /* Microsoft Azure Network Adapter (MANA) functions */ @@ -2106,6 +2119,69 @@ free_net: return err; } +static void adev_release(struct device *dev) +{ + struct mana_adev *madev = container_of(dev, struct mana_adev, adev.dev); + + kfree(madev); +} + +static void remove_adev(struct gdma_dev *gd) +{ + struct auxiliary_device *adev = gd->adev; + int id = adev->id; + + auxiliary_device_delete(adev); + auxiliary_device_uninit(adev); + + mana_adev_idx_free(id); + gd->adev = NULL; +} + +static int add_adev(struct gdma_dev *gd) +{ + struct auxiliary_device *adev; + struct mana_adev *madev; + int ret; + + madev = kzalloc(sizeof(*madev), GFP_KERNEL); + if (!madev) + return -ENOMEM; + + adev = &madev->adev; + ret = mana_adev_idx_alloc(); + if (ret < 0) + goto idx_fail; + adev->id = ret; + + adev->name = "rdma"; + adev->dev.parent = gd->gdma_context->dev; + adev->dev.release = adev_release; + madev->mdev = gd; + + ret = auxiliary_device_init(adev); + if (ret) + goto init_fail; + + ret = auxiliary_device_add(adev); + if (ret) + goto add_fail; + + gd->adev = adev; + return 0; + +add_fail: + auxiliary_device_uninit(adev); + +init_fail: + mana_adev_idx_free(adev->id); + +idx_fail: + kfree(madev); + + return ret; +} + int mana_probe(struct gdma_dev *gd, bool resuming) { struct gdma_context *gc = gd->gdma_context; @@ -2173,6 +2249,8 @@ int mana_probe(struct gdma_dev *gd, bool resuming) break; } } + + err = add_adev(gd); out: if (err) mana_remove(gd, false); @@ -2189,6 +2267,10 @@ void mana_remove(struct gdma_dev *gd, bool suspending) int err; int i; + /* adev currently doesn't support suspending, always remove it */ + if (gd->adev) + remove_adev(gd); + for (i = 0; i < ac->num_ports; i++) { ndev = ac->ports[i]; if (!ndev) { @@ -2221,7 +2303,6 @@ void mana_remove(struct gdma_dev *gd, bool suspending) } mana_destroy_eq(ac); - out: mana_gd_deregister_device(gd); From f3dc096246091048677c45cfc0e24ad512927b52 Mon Sep 17 00:00:00 2001 From: Long Li Date: Thu, 3 Nov 2022 12:16:20 -0700 Subject: [PATCH 0784/2002] net: mana: Record the physical address for doorbell page region For supporting RDMA device with multiple user contexts with their individual doorbell pages, record the start address of doorbell page region for use by the RDMA driver to allocate user context doorbell IDs. Reviewed-by: Dexuan Cui Signed-off-by: Long Li Link: https://lore.kernel.org/r/1667502990-2559-3-git-send-email-longli@linuxonhyperv.com Acked-by: Haiyang Zhang Signed-off-by: Leon Romanovsky --- drivers/net/ethernet/microsoft/mana/gdma.h | 2 ++ drivers/net/ethernet/microsoft/mana/gdma_main.c | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/drivers/net/ethernet/microsoft/mana/gdma.h b/drivers/net/ethernet/microsoft/mana/gdma.h index f321a2616d03..72eaec2470c0 100644 --- a/drivers/net/ethernet/microsoft/mana/gdma.h +++ b/drivers/net/ethernet/microsoft/mana/gdma.h @@ -351,9 +351,11 @@ struct gdma_context { u32 test_event_eq_id; bool is_pf; + phys_addr_t bar0_pa; void __iomem *bar0_va; void __iomem *shm_base; void __iomem *db_page_base; + phys_addr_t phys_db_page_base; u32 db_page_size; /* Shared memory chanenl (used to bootstrap HWC) */ diff --git a/drivers/net/ethernet/microsoft/mana/gdma_main.c b/drivers/net/ethernet/microsoft/mana/gdma_main.c index a6f99b4344d9..9a698526e2a3 100644 --- a/drivers/net/ethernet/microsoft/mana/gdma_main.c +++ b/drivers/net/ethernet/microsoft/mana/gdma_main.c @@ -44,6 +44,9 @@ static void mana_gd_init_vf_regs(struct pci_dev *pdev) gc->db_page_base = gc->bar0_va + mana_gd_r64(gc, GDMA_REG_DB_PAGE_OFFSET); + gc->phys_db_page_base = gc->bar0_pa + + mana_gd_r64(gc, GDMA_REG_DB_PAGE_OFFSET); + gc->shm_base = gc->bar0_va + mana_gd_r64(gc, GDMA_REG_SHM_OFFSET); } @@ -1377,6 +1380,7 @@ static int mana_gd_probe(struct pci_dev *pdev, const struct pci_device_id *ent) mutex_init(&gc->eq_test_event_mutex); pci_set_drvdata(pdev, gc); + gc->bar0_pa = pci_resource_start(pdev, 0); bar0_va = pci_iomap(pdev, bar, 0); if (!bar0_va) From b5c1c9855be3b5b978fde975a63df3cabc273faa Mon Sep 17 00:00:00 2001 From: Long Li Date: Thu, 3 Nov 2022 12:16:21 -0700 Subject: [PATCH 0785/2002] net: mana: Handle vport sharing between devices For outgoing packets, the PF requires the VF to configure the vport with corresponding protection domain and doorbell ID for the kernel or user context. The vport can't be shared between different contexts. Implement the logic to exclusively take over the vport by either the Ethernet device or RDMA device. Reviewed-by: Dexuan Cui Signed-off-by: Long Li Link: https://lore.kernel.org/r/1667502990-2559-4-git-send-email-longli@linuxonhyperv.com Acked-by: Haiyang Zhang Signed-off-by: Leon Romanovsky --- drivers/net/ethernet/microsoft/mana/mana.h | 7 +++ drivers/net/ethernet/microsoft/mana/mana_en.c | 53 ++++++++++++++++++- 2 files changed, 58 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/microsoft/mana/mana.h b/drivers/net/ethernet/microsoft/mana/mana.h index d58be64374c8..2883a08dbfb5 100644 --- a/drivers/net/ethernet/microsoft/mana/mana.h +++ b/drivers/net/ethernet/microsoft/mana/mana.h @@ -380,6 +380,10 @@ struct mana_port_context { mana_handle_t port_handle; mana_handle_t pf_filter_handle; + /* Mutex for sharing access to vport_use_count */ + struct mutex vport_mutex; + int vport_use_count; + u16 port_idx; bool port_is_up; @@ -631,4 +635,7 @@ struct mana_tx_package { struct gdma_posted_wqe_info wqe_info; }; +int mana_cfg_vport(struct mana_port_context *apc, u32 protection_dom_id, + u32 doorbell_pg_id); +void mana_uncfg_vport(struct mana_port_context *apc); #endif /* _MANA_H */ diff --git a/drivers/net/ethernet/microsoft/mana/mana_en.c b/drivers/net/ethernet/microsoft/mana/mana_en.c index 8751e475d1ba..efe14a343fd1 100644 --- a/drivers/net/ethernet/microsoft/mana/mana_en.c +++ b/drivers/net/ethernet/microsoft/mana/mana_en.c @@ -646,13 +646,48 @@ static int mana_query_vport_cfg(struct mana_port_context *apc, u32 vport_index, return 0; } -static int mana_cfg_vport(struct mana_port_context *apc, u32 protection_dom_id, - u32 doorbell_pg_id) +void mana_uncfg_vport(struct mana_port_context *apc) +{ + mutex_lock(&apc->vport_mutex); + apc->vport_use_count--; + WARN_ON(apc->vport_use_count < 0); + mutex_unlock(&apc->vport_mutex); +} +EXPORT_SYMBOL_NS(mana_uncfg_vport, NET_MANA); + +int mana_cfg_vport(struct mana_port_context *apc, u32 protection_dom_id, + u32 doorbell_pg_id) { struct mana_config_vport_resp resp = {}; struct mana_config_vport_req req = {}; int err; + /* This function is used to program the Ethernet port in the hardware + * table. It can be called from the Ethernet driver or the RDMA driver. + * + * For Ethernet usage, the hardware supports only one active user on a + * physical port. The driver checks on the port usage before programming + * the hardware when creating the RAW QP (RDMA driver) or exposing the + * device to kernel NET layer (Ethernet driver). + * + * Because the RDMA driver doesn't know in advance which QP type the + * user will create, it exposes the device with all its ports. The user + * may not be able to create RAW QP on a port if this port is already + * in used by the Ethernet driver from the kernel. + * + * This physical port limitation only applies to the RAW QP. For RC QP, + * the hardware doesn't have this limitation. The user can create RC + * QPs on a physical port up to the hardware limits independent of the + * Ethernet usage on the same port. + */ + mutex_lock(&apc->vport_mutex); + if (apc->vport_use_count > 0) { + mutex_unlock(&apc->vport_mutex); + return -EBUSY; + } + apc->vport_use_count++; + mutex_unlock(&apc->vport_mutex); + mana_gd_init_req_hdr(&req.hdr, MANA_CONFIG_VPORT_TX, sizeof(req), sizeof(resp)); req.vport = apc->port_handle; @@ -679,9 +714,16 @@ static int mana_cfg_vport(struct mana_port_context *apc, u32 protection_dom_id, apc->tx_shortform_allowed = resp.short_form_allowed; apc->tx_vp_offset = resp.tx_vport_offset; + + netdev_info(apc->ndev, "Configured vPort %llu PD %u DB %u\n", + apc->port_handle, protection_dom_id, doorbell_pg_id); out: + if (err) + mana_uncfg_vport(apc); + return err; } +EXPORT_SYMBOL_NS(mana_cfg_vport, NET_MANA); static int mana_cfg_vport_steering(struct mana_port_context *apc, enum TRI_STATE rx, @@ -742,6 +784,9 @@ static int mana_cfg_vport_steering(struct mana_port_context *apc, resp.hdr.status); err = -EPROTO; } + + netdev_info(ndev, "Configured steering vPort %llu entries %u\n", + apc->port_handle, num_entries); out: kfree(req); return err; @@ -1804,6 +1849,7 @@ static void mana_destroy_vport(struct mana_port_context *apc) } mana_destroy_txq(apc); + mana_uncfg_vport(apc); if (gd->gdma_context->is_pf) mana_pf_deregister_hw_vport(apc); @@ -2076,6 +2122,9 @@ static int mana_probe_port(struct mana_context *ac, int port_idx, apc->pf_filter_handle = INVALID_MANA_HANDLE; apc->port_idx = port_idx; + mutex_init(&apc->vport_mutex); + apc->vport_use_count = 0; + ndev->netdev_ops = &mana_devops; ndev->ethtool_ops = &mana_ethtool_ops; ndev->mtu = ETH_DATA_LEN; From 6fe254160bd033a1e62dbad9b734183b31144678 Mon Sep 17 00:00:00 2001 From: Ajay Sharma Date: Thu, 3 Nov 2022 12:16:22 -0700 Subject: [PATCH 0786/2002] net: mana: Set the DMA device max segment size MANA hardware doesn't have any restrictions on the DMA segment size, set it to the max allowed value. Signed-off-by: Ajay Sharma Reviewed-by: Dexuan Cui Signed-off-by: Long Li Link: https://lore.kernel.org/r/1667502990-2559-5-git-send-email-longli@linuxonhyperv.com Acked-by: Haiyang Zhang Signed-off-by: Leon Romanovsky --- drivers/net/ethernet/microsoft/mana/gdma_main.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/net/ethernet/microsoft/mana/gdma_main.c b/drivers/net/ethernet/microsoft/mana/gdma_main.c index 9a698526e2a3..62089e112c05 100644 --- a/drivers/net/ethernet/microsoft/mana/gdma_main.c +++ b/drivers/net/ethernet/microsoft/mana/gdma_main.c @@ -1373,6 +1373,12 @@ static int mana_gd_probe(struct pci_dev *pdev, const struct pci_device_id *ent) if (err) goto release_region; + err = dma_set_max_seg_size(&pdev->dev, UINT_MAX); + if (err) { + dev_err(&pdev->dev, "Failed to set dma device segment size\n"); + goto release_region; + } + err = -ENOMEM; gc = vzalloc(sizeof(*gc)); if (!gc) From 4c0ff7a106e16ab63e0b597557255c012f179578 Mon Sep 17 00:00:00 2001 From: Long Li Date: Thu, 3 Nov 2022 12:16:23 -0700 Subject: [PATCH 0787/2002] net: mana: Export Work Queue functions for use by RDMA driver RDMA device may need to create Ethernet device queues for use by Queue Pair type RAW. This allows a user-mode context accesses Ethernet hardware queues. Export the supporting functions for use by the RDMA driver. Reviewed-by: Dexuan Cui Signed-off-by: Long Li Link: https://lore.kernel.org/r/1667502990-2559-6-git-send-email-longli@linuxonhyperv.com Acked-by: Haiyang Zhang Signed-off-by: Leon Romanovsky --- drivers/net/ethernet/microsoft/mana/gdma_main.c | 1 + drivers/net/ethernet/microsoft/mana/mana.h | 9 +++++++++ drivers/net/ethernet/microsoft/mana/mana_en.c | 16 +++++++++------- 3 files changed, 19 insertions(+), 7 deletions(-) diff --git a/drivers/net/ethernet/microsoft/mana/gdma_main.c b/drivers/net/ethernet/microsoft/mana/gdma_main.c index 62089e112c05..f0e22954d5c0 100644 --- a/drivers/net/ethernet/microsoft/mana/gdma_main.c +++ b/drivers/net/ethernet/microsoft/mana/gdma_main.c @@ -152,6 +152,7 @@ int mana_gd_send_request(struct gdma_context *gc, u32 req_len, const void *req, return mana_hwc_send_request(hwc, req_len, req, resp_len, resp); } +EXPORT_SYMBOL_NS(mana_gd_send_request, NET_MANA); int mana_gd_alloc_memory(struct gdma_context *gc, unsigned int length, struct gdma_mem_info *gmi) diff --git a/drivers/net/ethernet/microsoft/mana/mana.h b/drivers/net/ethernet/microsoft/mana/mana.h index 2883a08dbfb5..6e9e86fb4c02 100644 --- a/drivers/net/ethernet/microsoft/mana/mana.h +++ b/drivers/net/ethernet/microsoft/mana/mana.h @@ -635,6 +635,15 @@ struct mana_tx_package { struct gdma_posted_wqe_info wqe_info; }; +int mana_create_wq_obj(struct mana_port_context *apc, + mana_handle_t vport, + u32 wq_type, struct mana_obj_spec *wq_spec, + struct mana_obj_spec *cq_spec, + mana_handle_t *wq_obj); + +void mana_destroy_wq_obj(struct mana_port_context *apc, u32 wq_type, + mana_handle_t wq_obj); + int mana_cfg_vport(struct mana_port_context *apc, u32 protection_dom_id, u32 doorbell_pg_id); void mana_uncfg_vport(struct mana_port_context *apc); diff --git a/drivers/net/ethernet/microsoft/mana/mana_en.c b/drivers/net/ethernet/microsoft/mana/mana_en.c index efe14a343fd1..6ad4bc8cbc99 100644 --- a/drivers/net/ethernet/microsoft/mana/mana_en.c +++ b/drivers/net/ethernet/microsoft/mana/mana_en.c @@ -792,11 +792,11 @@ out: return err; } -static int mana_create_wq_obj(struct mana_port_context *apc, - mana_handle_t vport, - u32 wq_type, struct mana_obj_spec *wq_spec, - struct mana_obj_spec *cq_spec, - mana_handle_t *wq_obj) +int mana_create_wq_obj(struct mana_port_context *apc, + mana_handle_t vport, + u32 wq_type, struct mana_obj_spec *wq_spec, + struct mana_obj_spec *cq_spec, + mana_handle_t *wq_obj) { struct mana_create_wqobj_resp resp = {}; struct mana_create_wqobj_req req = {}; @@ -845,9 +845,10 @@ static int mana_create_wq_obj(struct mana_port_context *apc, out: return err; } +EXPORT_SYMBOL_NS(mana_create_wq_obj, NET_MANA); -static void mana_destroy_wq_obj(struct mana_port_context *apc, u32 wq_type, - mana_handle_t wq_obj) +void mana_destroy_wq_obj(struct mana_port_context *apc, u32 wq_type, + mana_handle_t wq_obj) { struct mana_destroy_wqobj_resp resp = {}; struct mana_destroy_wqobj_req req = {}; @@ -872,6 +873,7 @@ static void mana_destroy_wq_obj(struct mana_port_context *apc, u32 wq_type, netdev_err(ndev, "Failed to destroy WQ object: %d, 0x%x\n", err, resp.hdr.status); } +EXPORT_SYMBOL_NS(mana_destroy_wq_obj, NET_MANA); static void mana_destroy_eq(struct mana_context *ac) { From d44089e555ffe63a49cc6e94d0c03d933e413059 Mon Sep 17 00:00:00 2001 From: Long Li Date: Thu, 3 Nov 2022 12:16:24 -0700 Subject: [PATCH 0788/2002] net: mana: Record port number in netdev The port number is useful for user-mode application to identify this net device based on port index. Set to the correct value in ndev. Reviewed-by: Dexuan Cui Signed-off-by: Long Li Link: https://lore.kernel.org/r/1667502990-2559-7-git-send-email-longli@linuxonhyperv.com Acked-by: Haiyang Zhang Signed-off-by: Leon Romanovsky --- drivers/net/ethernet/microsoft/mana/mana_en.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/ethernet/microsoft/mana/mana_en.c b/drivers/net/ethernet/microsoft/mana/mana_en.c index 6ad4bc8cbc99..b6303a43fa7c 100644 --- a/drivers/net/ethernet/microsoft/mana/mana_en.c +++ b/drivers/net/ethernet/microsoft/mana/mana_en.c @@ -2133,6 +2133,7 @@ static int mana_probe_port(struct mana_context *ac, int port_idx, ndev->max_mtu = ndev->mtu; ndev->min_mtu = ndev->mtu; ndev->needed_headroom = MANA_HEADROOM; + ndev->dev_port = port_idx; SET_NETDEV_DEV(ndev, gc->dev); netif_carrier_off(ndev); From fd325cd648f15eb9a8b32a68de3bafc72bcfe753 Mon Sep 17 00:00:00 2001 From: Long Li Date: Thu, 3 Nov 2022 12:16:25 -0700 Subject: [PATCH 0789/2002] net: mana: Move header files to a common location In preparation to add MANA RDMA driver, move all the required header files to a common location for use by both Ethernet and RDMA drivers. Reviewed-by: Dexuan Cui Signed-off-by: Long Li Link: https://lore.kernel.org/r/1667502990-2559-8-git-send-email-longli@linuxonhyperv.com Acked-by: Haiyang Zhang Signed-off-by: Leon Romanovsky --- MAINTAINERS | 1 + drivers/net/ethernet/microsoft/mana/gdma_main.c | 2 +- drivers/net/ethernet/microsoft/mana/hw_channel.c | 4 ++-- drivers/net/ethernet/microsoft/mana/mana_bpf.c | 2 +- drivers/net/ethernet/microsoft/mana/mana_en.c | 4 ++-- drivers/net/ethernet/microsoft/mana/mana_ethtool.c | 2 +- drivers/net/ethernet/microsoft/mana/shm_channel.c | 2 +- {drivers/net/ethernet/microsoft => include/net}/mana/gdma.h | 0 .../net/ethernet/microsoft => include/net}/mana/hw_channel.h | 0 {drivers/net/ethernet/microsoft => include/net}/mana/mana.h | 0 .../ethernet/microsoft => include/net}/mana/mana_auxiliary.h | 0 .../net/ethernet/microsoft => include/net}/mana/shm_channel.h | 0 12 files changed, 9 insertions(+), 8 deletions(-) rename {drivers/net/ethernet/microsoft => include/net}/mana/gdma.h (100%) rename {drivers/net/ethernet/microsoft => include/net}/mana/hw_channel.h (100%) rename {drivers/net/ethernet/microsoft => include/net}/mana/mana.h (100%) rename {drivers/net/ethernet/microsoft => include/net}/mana/mana_auxiliary.h (100%) rename {drivers/net/ethernet/microsoft => include/net}/mana/shm_channel.h (100%) diff --git a/MAINTAINERS b/MAINTAINERS index 379945f82a64..441a65d41eb4 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -9541,6 +9541,7 @@ F: include/asm-generic/hyperv-tlfs.h F: include/asm-generic/mshyperv.h F: include/clocksource/hyperv_timer.h F: include/linux/hyperv.h +F: include/net/mana F: include/uapi/linux/hyperv.h F: net/vmw_vsock/hyperv_transport.c F: tools/hv/ diff --git a/drivers/net/ethernet/microsoft/mana/gdma_main.c b/drivers/net/ethernet/microsoft/mana/gdma_main.c index f0e22954d5c0..69795bc679e7 100644 --- a/drivers/net/ethernet/microsoft/mana/gdma_main.c +++ b/drivers/net/ethernet/microsoft/mana/gdma_main.c @@ -6,7 +6,7 @@ #include #include -#include "mana.h" +#include static u32 mana_gd_r32(struct gdma_context *g, u64 offset) { diff --git a/drivers/net/ethernet/microsoft/mana/hw_channel.c b/drivers/net/ethernet/microsoft/mana/hw_channel.c index 543a5d5c304f..76829ab43d40 100644 --- a/drivers/net/ethernet/microsoft/mana/hw_channel.c +++ b/drivers/net/ethernet/microsoft/mana/hw_channel.c @@ -1,8 +1,8 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* Copyright (c) 2021, Microsoft Corporation. */ -#include "gdma.h" -#include "hw_channel.h" +#include +#include static int mana_hwc_get_msg_index(struct hw_channel_context *hwc, u16 *msg_id) { diff --git a/drivers/net/ethernet/microsoft/mana/mana_bpf.c b/drivers/net/ethernet/microsoft/mana/mana_bpf.c index 421fd39ff3a8..3caea631229c 100644 --- a/drivers/net/ethernet/microsoft/mana/mana_bpf.c +++ b/drivers/net/ethernet/microsoft/mana/mana_bpf.c @@ -8,7 +8,7 @@ #include #include -#include "mana.h" +#include void mana_xdp_tx(struct sk_buff *skb, struct net_device *ndev) { diff --git a/drivers/net/ethernet/microsoft/mana/mana_en.c b/drivers/net/ethernet/microsoft/mana/mana_en.c index b6303a43fa7c..ffa2a0e2c213 100644 --- a/drivers/net/ethernet/microsoft/mana/mana_en.c +++ b/drivers/net/ethernet/microsoft/mana/mana_en.c @@ -12,8 +12,8 @@ #include #include -#include "mana.h" -#include "mana_auxiliary.h" +#include +#include static DEFINE_IDA(mana_adev_ida); diff --git a/drivers/net/ethernet/microsoft/mana/mana_ethtool.c b/drivers/net/ethernet/microsoft/mana/mana_ethtool.c index c530db76880f..6f98de6d7440 100644 --- a/drivers/net/ethernet/microsoft/mana/mana_ethtool.c +++ b/drivers/net/ethernet/microsoft/mana/mana_ethtool.c @@ -5,7 +5,7 @@ #include #include -#include "mana.h" +#include static const struct { char name[ETH_GSTRING_LEN]; diff --git a/drivers/net/ethernet/microsoft/mana/shm_channel.c b/drivers/net/ethernet/microsoft/mana/shm_channel.c index da255da62176..5553af9c8085 100644 --- a/drivers/net/ethernet/microsoft/mana/shm_channel.c +++ b/drivers/net/ethernet/microsoft/mana/shm_channel.c @@ -6,7 +6,7 @@ #include #include -#include "shm_channel.h" +#include #define PAGE_FRAME_L48_WIDTH_BYTES 6 #define PAGE_FRAME_L48_WIDTH_BITS (PAGE_FRAME_L48_WIDTH_BYTES * 8) diff --git a/drivers/net/ethernet/microsoft/mana/gdma.h b/include/net/mana/gdma.h similarity index 100% rename from drivers/net/ethernet/microsoft/mana/gdma.h rename to include/net/mana/gdma.h diff --git a/drivers/net/ethernet/microsoft/mana/hw_channel.h b/include/net/mana/hw_channel.h similarity index 100% rename from drivers/net/ethernet/microsoft/mana/hw_channel.h rename to include/net/mana/hw_channel.h diff --git a/drivers/net/ethernet/microsoft/mana/mana.h b/include/net/mana/mana.h similarity index 100% rename from drivers/net/ethernet/microsoft/mana/mana.h rename to include/net/mana/mana.h diff --git a/drivers/net/ethernet/microsoft/mana/mana_auxiliary.h b/include/net/mana/mana_auxiliary.h similarity index 100% rename from drivers/net/ethernet/microsoft/mana/mana_auxiliary.h rename to include/net/mana/mana_auxiliary.h diff --git a/drivers/net/ethernet/microsoft/mana/shm_channel.h b/include/net/mana/shm_channel.h similarity index 100% rename from drivers/net/ethernet/microsoft/mana/shm_channel.h rename to include/net/mana/shm_channel.h From aa56549792fb348892fbbae67f6f0c71bb750b65 Mon Sep 17 00:00:00 2001 From: Long Li Date: Thu, 3 Nov 2022 12:16:26 -0700 Subject: [PATCH 0790/2002] net: mana: Define max values for SGL entries The number of maximum SGl entries should be computed from the maximum WQE size for the intended queue type and the corresponding OOB data size. This guarantees the hardware queue can successfully queue requests up to the queue depth exposed to the upper layer. Reviewed-by: Dexuan Cui Signed-off-by: Long Li Link: https://lore.kernel.org/r/1667502990-2559-9-git-send-email-longli@linuxonhyperv.com Acked-by: Haiyang Zhang Signed-off-by: Leon Romanovsky --- drivers/net/ethernet/microsoft/mana/mana_en.c | 2 +- include/net/mana/gdma.h | 7 +++++++ include/net/mana/mana.h | 4 +--- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/drivers/net/ethernet/microsoft/mana/mana_en.c b/drivers/net/ethernet/microsoft/mana/mana_en.c index ffa2a0e2c213..f6bcd0cc6cda 100644 --- a/drivers/net/ethernet/microsoft/mana/mana_en.c +++ b/drivers/net/ethernet/microsoft/mana/mana_en.c @@ -189,7 +189,7 @@ int mana_start_xmit(struct sk_buff *skb, struct net_device *ndev) pkg.wqe_req.client_data_unit = 0; pkg.wqe_req.num_sge = 1 + skb_shinfo(skb)->nr_frags; - WARN_ON_ONCE(pkg.wqe_req.num_sge > 30); + WARN_ON_ONCE(pkg.wqe_req.num_sge > MAX_TX_WQE_SGL_ENTRIES); if (pkg.wqe_req.num_sge <= ARRAY_SIZE(pkg.sgl_array)) { pkg.wqe_req.sgl = pkg.sgl_array; diff --git a/include/net/mana/gdma.h b/include/net/mana/gdma.h index 72eaec2470c0..0b0c0cd0b6bd 100644 --- a/include/net/mana/gdma.h +++ b/include/net/mana/gdma.h @@ -427,6 +427,13 @@ struct gdma_wqe { #define MAX_TX_WQE_SIZE 512 #define MAX_RX_WQE_SIZE 256 +#define MAX_TX_WQE_SGL_ENTRIES ((GDMA_MAX_SQE_SIZE - \ + sizeof(struct gdma_sge) - INLINE_OOB_SMALL_SIZE) / \ + sizeof(struct gdma_sge)) + +#define MAX_RX_WQE_SGL_ENTRIES ((GDMA_MAX_RQE_SIZE - \ + sizeof(struct gdma_sge)) / sizeof(struct gdma_sge)) + struct gdma_cqe { u32 cqe_data[GDMA_COMP_DATA_SIZE / 4]; diff --git a/include/net/mana/mana.h b/include/net/mana/mana.h index 6e9e86fb4c02..713a8f8cca9a 100644 --- a/include/net/mana/mana.h +++ b/include/net/mana/mana.h @@ -265,8 +265,6 @@ struct mana_cq { int budget; }; -#define GDMA_MAX_RQE_SGES 15 - struct mana_recv_buf_oob { /* A valid GDMA work request representing the data buffer. */ struct gdma_wqe_request wqe_req; @@ -276,7 +274,7 @@ struct mana_recv_buf_oob { /* SGL of the buffer going to be sent has part of the work request. */ u32 num_sge; - struct gdma_sge sgl[GDMA_MAX_RQE_SGES]; + struct gdma_sge sgl[MAX_RX_WQE_SGL_ENTRIES]; /* Required to store the result of mana_gd_post_work_request. * gdma_posted_wqe_info.wqe_size_in_bu is required for progressing the From de372f2a9ca7ada2698ecac7df8f02407cd98fa0 Mon Sep 17 00:00:00 2001 From: Ajay Sharma Date: Thu, 3 Nov 2022 12:16:27 -0700 Subject: [PATCH 0791/2002] net: mana: Define and process GDMA response code GDMA_STATUS_MORE_ENTRIES When doing memory registration, the PF may respond with GDMA_STATUS_MORE_ENTRIES to indicate a follow request is needed. This is not an error and should be processed as expected. Signed-off-by: Ajay Sharma Reviewed-by: Dexuan Cui Signed-off-by: Long Li Link: https://lore.kernel.org/r/1667502990-2559-10-git-send-email-longli@linuxonhyperv.com Acked-by: Haiyang Zhang Signed-off-by: Leon Romanovsky --- drivers/net/ethernet/microsoft/mana/hw_channel.c | 2 +- include/net/mana/gdma.h | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/microsoft/mana/hw_channel.c b/drivers/net/ethernet/microsoft/mana/hw_channel.c index 76829ab43d40..9d1507eba5b9 100644 --- a/drivers/net/ethernet/microsoft/mana/hw_channel.c +++ b/drivers/net/ethernet/microsoft/mana/hw_channel.c @@ -836,7 +836,7 @@ int mana_hwc_send_request(struct hw_channel_context *hwc, u32 req_len, goto out; } - if (ctx->status_code) { + if (ctx->status_code && ctx->status_code != GDMA_STATUS_MORE_ENTRIES) { dev_err(hwc->dev, "HWC: Failed hw_channel req: 0x%x\n", ctx->status_code); err = -EPROTO; diff --git a/include/net/mana/gdma.h b/include/net/mana/gdma.h index 0b0c0cd0b6bd..a9b7930dfbf8 100644 --- a/include/net/mana/gdma.h +++ b/include/net/mana/gdma.h @@ -9,6 +9,8 @@ #include "shm_channel.h" +#define GDMA_STATUS_MORE_ENTRIES 0x00000105 + /* Structures labeled with "HW DATA" are exchanged with the hardware. All of * them are naturally aligned and hence don't need __packed. */ From f72ececfc197e9b0bbb5595294908a950cf444fa Mon Sep 17 00:00:00 2001 From: Long Li Date: Thu, 3 Nov 2022 12:16:28 -0700 Subject: [PATCH 0792/2002] net: mana: Define data structures for allocating doorbell page from GDMA The RDMA device needs to allocate doorbell pages for each user context. Define the GDMA data structures for use by the RDMA driver. Reviewed-by: Dexuan Cui Signed-off-by: Long Li Link: https://lore.kernel.org/r/1667502990-2559-11-git-send-email-longli@linuxonhyperv.com Acked-by: Haiyang Zhang Signed-off-by: Leon Romanovsky --- include/net/mana/gdma.h | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/include/net/mana/gdma.h b/include/net/mana/gdma.h index a9b7930dfbf8..055408a5baf3 100644 --- a/include/net/mana/gdma.h +++ b/include/net/mana/gdma.h @@ -24,11 +24,15 @@ enum gdma_request_type { GDMA_GENERATE_TEST_EQE = 10, GDMA_CREATE_QUEUE = 12, GDMA_DISABLE_QUEUE = 13, + GDMA_ALLOCATE_RESOURCE_RANGE = 22, + GDMA_DESTROY_RESOURCE_RANGE = 24, GDMA_CREATE_DMA_REGION = 25, GDMA_DMA_REGION_ADD_PAGES = 26, GDMA_DESTROY_DMA_REGION = 27, }; +#define GDMA_RESOURCE_DOORBELL_PAGE 27 + enum gdma_queue_type { GDMA_INVALID_QUEUE, GDMA_SQ, @@ -587,6 +591,26 @@ struct gdma_register_device_resp { u32 db_id; }; /* HW DATA */ +struct gdma_allocate_resource_range_req { + struct gdma_req_hdr hdr; + u32 resource_type; + u32 num_resources; + u32 alignment; + u32 allocated_resources; +}; + +struct gdma_allocate_resource_range_resp { + struct gdma_resp_hdr hdr; + u32 allocated_resources; +}; + +struct gdma_destroy_resource_range_req { + struct gdma_req_hdr hdr; + u32 resource_type; + u32 num_resources; + u32 allocated_resources; +}; + /* GDMA_CREATE_QUEUE */ struct gdma_create_queue_req { struct gdma_req_hdr hdr; From 28c66cfa45388af1126985d1114e0ed762eb2abd Mon Sep 17 00:00:00 2001 From: Ajay Sharma Date: Thu, 3 Nov 2022 12:16:29 -0700 Subject: [PATCH 0793/2002] net: mana: Define data structures for protection domain and memory registration The MANA hardware support protection domain and memory registration for use in RDMA environment. Add those definitions and expose them for use by the RDMA driver. Signed-off-by: Ajay Sharma Signed-off-by: Long Li Link: https://lore.kernel.org/r/1667502990-2559-12-git-send-email-longli@linuxonhyperv.com Reviewed-by: Dexuan Cui Acked-by: Haiyang Zhang Signed-off-by: Leon Romanovsky --- .../net/ethernet/microsoft/mana/gdma_main.c | 27 ++-- drivers/net/ethernet/microsoft/mana/mana_en.c | 18 +-- include/net/mana/gdma.h | 121 +++++++++++++++++- 3 files changed, 143 insertions(+), 23 deletions(-) diff --git a/drivers/net/ethernet/microsoft/mana/gdma_main.c b/drivers/net/ethernet/microsoft/mana/gdma_main.c index 69795bc679e7..46a7d1e6ece9 100644 --- a/drivers/net/ethernet/microsoft/mana/gdma_main.c +++ b/drivers/net/ethernet/microsoft/mana/gdma_main.c @@ -198,7 +198,7 @@ static int mana_gd_create_hw_eq(struct gdma_context *gc, req.type = queue->type; req.pdid = queue->gdma_dev->pdid; req.doolbell_id = queue->gdma_dev->doorbell; - req.gdma_region = queue->mem_info.gdma_region; + req.gdma_region = queue->mem_info.dma_region_handle; req.queue_size = queue->queue_size; req.log2_throttle_limit = queue->eq.log2_throttle_limit; req.eq_pci_msix_index = queue->eq.msix_index; @@ -212,7 +212,7 @@ static int mana_gd_create_hw_eq(struct gdma_context *gc, queue->id = resp.queue_index; queue->eq.disable_needed = true; - queue->mem_info.gdma_region = GDMA_INVALID_DMA_REGION; + queue->mem_info.dma_region_handle = GDMA_INVALID_DMA_REGION; return 0; } @@ -671,24 +671,30 @@ free_q: return err; } -static void mana_gd_destroy_dma_region(struct gdma_context *gc, u64 gdma_region) +int mana_gd_destroy_dma_region(struct gdma_context *gc, + gdma_obj_handle_t dma_region_handle) { struct gdma_destroy_dma_region_req req = {}; struct gdma_general_resp resp = {}; int err; - if (gdma_region == GDMA_INVALID_DMA_REGION) - return; + if (dma_region_handle == GDMA_INVALID_DMA_REGION) + return 0; mana_gd_init_req_hdr(&req.hdr, GDMA_DESTROY_DMA_REGION, sizeof(req), sizeof(resp)); - req.gdma_region = gdma_region; + req.dma_region_handle = dma_region_handle; err = mana_gd_send_request(gc, sizeof(req), &req, sizeof(resp), &resp); - if (err || resp.hdr.status) + if (err || resp.hdr.status) { dev_err(gc->dev, "Failed to destroy DMA region: %d, 0x%x\n", err, resp.hdr.status); + return -EPROTO; + } + + return 0; } +EXPORT_SYMBOL_NS(mana_gd_destroy_dma_region, NET_MANA); static int mana_gd_create_dma_region(struct gdma_dev *gd, struct gdma_mem_info *gmi) @@ -733,14 +739,15 @@ static int mana_gd_create_dma_region(struct gdma_dev *gd, if (err) goto out; - if (resp.hdr.status || resp.gdma_region == GDMA_INVALID_DMA_REGION) { + if (resp.hdr.status || + resp.dma_region_handle == GDMA_INVALID_DMA_REGION) { dev_err(gc->dev, "Failed to create DMA region: 0x%x\n", resp.hdr.status); err = -EPROTO; goto out; } - gmi->gdma_region = resp.gdma_region; + gmi->dma_region_handle = resp.dma_region_handle; out: kfree(req); return err; @@ -863,7 +870,7 @@ void mana_gd_destroy_queue(struct gdma_context *gc, struct gdma_queue *queue) return; } - mana_gd_destroy_dma_region(gc, gmi->gdma_region); + mana_gd_destroy_dma_region(gc, gmi->dma_region_handle); mana_gd_free_memory(gmi); kfree(queue); } diff --git a/drivers/net/ethernet/microsoft/mana/mana_en.c b/drivers/net/ethernet/microsoft/mana/mana_en.c index f6bcd0cc6cda..1c59502d34b5 100644 --- a/drivers/net/ethernet/microsoft/mana/mana_en.c +++ b/drivers/net/ethernet/microsoft/mana/mana_en.c @@ -1523,10 +1523,10 @@ static int mana_create_txq(struct mana_port_context *apc, memset(&wq_spec, 0, sizeof(wq_spec)); memset(&cq_spec, 0, sizeof(cq_spec)); - wq_spec.gdma_region = txq->gdma_sq->mem_info.gdma_region; + wq_spec.gdma_region = txq->gdma_sq->mem_info.dma_region_handle; wq_spec.queue_size = txq->gdma_sq->queue_size; - cq_spec.gdma_region = cq->gdma_cq->mem_info.gdma_region; + cq_spec.gdma_region = cq->gdma_cq->mem_info.dma_region_handle; cq_spec.queue_size = cq->gdma_cq->queue_size; cq_spec.modr_ctx_id = 0; cq_spec.attached_eq = cq->gdma_cq->cq.parent->id; @@ -1541,8 +1541,10 @@ static int mana_create_txq(struct mana_port_context *apc, txq->gdma_sq->id = wq_spec.queue_index; cq->gdma_cq->id = cq_spec.queue_index; - txq->gdma_sq->mem_info.gdma_region = GDMA_INVALID_DMA_REGION; - cq->gdma_cq->mem_info.gdma_region = GDMA_INVALID_DMA_REGION; + txq->gdma_sq->mem_info.dma_region_handle = + GDMA_INVALID_DMA_REGION; + cq->gdma_cq->mem_info.dma_region_handle = + GDMA_INVALID_DMA_REGION; txq->gdma_txq_id = txq->gdma_sq->id; @@ -1753,10 +1755,10 @@ static struct mana_rxq *mana_create_rxq(struct mana_port_context *apc, memset(&wq_spec, 0, sizeof(wq_spec)); memset(&cq_spec, 0, sizeof(cq_spec)); - wq_spec.gdma_region = rxq->gdma_rq->mem_info.gdma_region; + wq_spec.gdma_region = rxq->gdma_rq->mem_info.dma_region_handle; wq_spec.queue_size = rxq->gdma_rq->queue_size; - cq_spec.gdma_region = cq->gdma_cq->mem_info.gdma_region; + cq_spec.gdma_region = cq->gdma_cq->mem_info.dma_region_handle; cq_spec.queue_size = cq->gdma_cq->queue_size; cq_spec.modr_ctx_id = 0; cq_spec.attached_eq = cq->gdma_cq->cq.parent->id; @@ -1769,8 +1771,8 @@ static struct mana_rxq *mana_create_rxq(struct mana_port_context *apc, rxq->gdma_rq->id = wq_spec.queue_index; cq->gdma_cq->id = cq_spec.queue_index; - rxq->gdma_rq->mem_info.gdma_region = GDMA_INVALID_DMA_REGION; - cq->gdma_cq->mem_info.gdma_region = GDMA_INVALID_DMA_REGION; + rxq->gdma_rq->mem_info.dma_region_handle = GDMA_INVALID_DMA_REGION; + cq->gdma_cq->mem_info.dma_region_handle = GDMA_INVALID_DMA_REGION; rxq->gdma_id = rxq->gdma_rq->id; cq->gdma_id = cq->gdma_cq->id; diff --git a/include/net/mana/gdma.h b/include/net/mana/gdma.h index 055408a5baf3..221adc96340c 100644 --- a/include/net/mana/gdma.h +++ b/include/net/mana/gdma.h @@ -29,6 +29,10 @@ enum gdma_request_type { GDMA_CREATE_DMA_REGION = 25, GDMA_DMA_REGION_ADD_PAGES = 26, GDMA_DESTROY_DMA_REGION = 27, + GDMA_CREATE_PD = 29, + GDMA_DESTROY_PD = 30, + GDMA_CREATE_MR = 31, + GDMA_DESTROY_MR = 32, }; #define GDMA_RESOURCE_DOORBELL_PAGE 27 @@ -61,6 +65,8 @@ enum { GDMA_DEVICE_MANA = 2, }; +typedef u64 gdma_obj_handle_t; + struct gdma_resource { /* Protect the bitmap */ spinlock_t lock; @@ -194,7 +200,7 @@ struct gdma_mem_info { u64 length; /* Allocated by the PF driver */ - u64 gdma_region; + gdma_obj_handle_t dma_region_handle; }; #define REGISTER_ATB_MST_MKEY_LOWER_SIZE 8 @@ -618,7 +624,7 @@ struct gdma_create_queue_req { u32 reserved1; u32 pdid; u32 doolbell_id; - u64 gdma_region; + gdma_obj_handle_t gdma_region; u32 reserved2; u32 queue_size; u32 log2_throttle_limit; @@ -645,6 +651,28 @@ struct gdma_disable_queue_req { u32 alloc_res_id_on_creation; }; /* HW DATA */ +enum atb_page_size { + ATB_PAGE_SIZE_4K, + ATB_PAGE_SIZE_8K, + ATB_PAGE_SIZE_16K, + ATB_PAGE_SIZE_32K, + ATB_PAGE_SIZE_64K, + ATB_PAGE_SIZE_128K, + ATB_PAGE_SIZE_256K, + ATB_PAGE_SIZE_512K, + ATB_PAGE_SIZE_1M, + ATB_PAGE_SIZE_2M, + ATB_PAGE_SIZE_MAX, +}; + +enum gdma_mr_access_flags { + GDMA_ACCESS_FLAG_LOCAL_READ = BIT_ULL(0), + GDMA_ACCESS_FLAG_LOCAL_WRITE = BIT_ULL(1), + GDMA_ACCESS_FLAG_REMOTE_READ = BIT_ULL(2), + GDMA_ACCESS_FLAG_REMOTE_WRITE = BIT_ULL(3), + GDMA_ACCESS_FLAG_REMOTE_ATOMIC = BIT_ULL(4), +}; + /* GDMA_CREATE_DMA_REGION */ struct gdma_create_dma_region_req { struct gdma_req_hdr hdr; @@ -671,14 +699,14 @@ struct gdma_create_dma_region_req { struct gdma_create_dma_region_resp { struct gdma_resp_hdr hdr; - u64 gdma_region; + gdma_obj_handle_t dma_region_handle; }; /* HW DATA */ /* GDMA_DMA_REGION_ADD_PAGES */ struct gdma_dma_region_add_pages_req { struct gdma_req_hdr hdr; - u64 gdma_region; + gdma_obj_handle_t dma_region_handle; u32 page_addr_list_len; u32 reserved3; @@ -690,9 +718,88 @@ struct gdma_dma_region_add_pages_req { struct gdma_destroy_dma_region_req { struct gdma_req_hdr hdr; - u64 gdma_region; + gdma_obj_handle_t dma_region_handle; }; /* HW DATA */ +enum gdma_pd_flags { + GDMA_PD_FLAG_INVALID = 0, +}; + +struct gdma_create_pd_req { + struct gdma_req_hdr hdr; + enum gdma_pd_flags flags; + u32 reserved; +};/* HW DATA */ + +struct gdma_create_pd_resp { + struct gdma_resp_hdr hdr; + gdma_obj_handle_t pd_handle; + u32 pd_id; + u32 reserved; +};/* HW DATA */ + +struct gdma_destroy_pd_req { + struct gdma_req_hdr hdr; + gdma_obj_handle_t pd_handle; +};/* HW DATA */ + +struct gdma_destory_pd_resp { + struct gdma_resp_hdr hdr; +};/* HW DATA */ + +enum gdma_mr_type { + /* Guest Virtual Address - MRs of this type allow access + * to memory mapped by PTEs associated with this MR using a virtual + * address that is set up in the MST + */ + GDMA_MR_TYPE_GVA = 2, +}; + +struct gdma_create_mr_params { + gdma_obj_handle_t pd_handle; + enum gdma_mr_type mr_type; + union { + struct { + gdma_obj_handle_t dma_region_handle; + u64 virtual_address; + enum gdma_mr_access_flags access_flags; + } gva; + }; +}; + +struct gdma_create_mr_request { + struct gdma_req_hdr hdr; + gdma_obj_handle_t pd_handle; + enum gdma_mr_type mr_type; + u32 reserved_1; + + union { + struct { + gdma_obj_handle_t dma_region_handle; + u64 virtual_address; + enum gdma_mr_access_flags access_flags; + } gva; + + }; + u32 reserved_2; +};/* HW DATA */ + +struct gdma_create_mr_response { + struct gdma_resp_hdr hdr; + gdma_obj_handle_t mr_handle; + u32 lkey; + u32 rkey; +};/* HW DATA */ + +struct gdma_destroy_mr_request { + struct gdma_req_hdr hdr; + gdma_obj_handle_t mr_handle; +};/* HW DATA */ + +struct gdma_destroy_mr_response { + struct gdma_resp_hdr hdr; +};/* HW DATA */ + int mana_gd_verify_vf_version(struct pci_dev *pdev); int mana_gd_register_device(struct gdma_dev *gd); @@ -719,4 +826,8 @@ void mana_gd_free_memory(struct gdma_mem_info *gmi); int mana_gd_send_request(struct gdma_context *gc, u32 req_len, const void *req, u32 resp_len, void *resp); + +int mana_gd_destroy_dma_region(struct gdma_context *gc, + gdma_obj_handle_t dma_region_handle); + #endif /* _GDMA_H */ From e081ecf084d31809242fb0b9f35484d5fb3a161a Mon Sep 17 00:00:00 2001 From: Richard Gobert Date: Tue, 8 Nov 2022 13:33:28 +0100 Subject: [PATCH 0794/2002] gro: avoid checking for a failed search After searching for a protocol handler in dev_gro_receive, checking for failure is redundant. Skip the failure code after finding the corresponding handler. Suggested-by: Eric Dumazet Signed-off-by: Richard Gobert Reviewed-by: Eric Dumazet Link: https://lore.kernel.org/r/20221108123320.GA59373@debian Signed-off-by: Paolo Abeni --- net/core/gro.c | 72 +++++++++++++++++++++++++------------------------- 1 file changed, 36 insertions(+), 36 deletions(-) diff --git a/net/core/gro.c b/net/core/gro.c index bc9451743307..8e0fe85a647d 100644 --- a/net/core/gro.c +++ b/net/core/gro.c @@ -489,45 +489,45 @@ static enum gro_result dev_gro_receive(struct napi_struct *napi, struct sk_buff rcu_read_lock(); list_for_each_entry_rcu(ptype, head, list) { - if (ptype->type != type || !ptype->callbacks.gro_receive) - continue; - - skb_set_network_header(skb, skb_gro_offset(skb)); - skb_reset_mac_len(skb); - BUILD_BUG_ON(sizeof_field(struct napi_gro_cb, zeroed) != sizeof(u32)); - BUILD_BUG_ON(!IS_ALIGNED(offsetof(struct napi_gro_cb, zeroed), - sizeof(u32))); /* Avoid slow unaligned acc */ - *(u32 *)&NAPI_GRO_CB(skb)->zeroed = 0; - NAPI_GRO_CB(skb)->flush = skb_has_frag_list(skb); - NAPI_GRO_CB(skb)->is_atomic = 1; - NAPI_GRO_CB(skb)->count = 1; - if (unlikely(skb_is_gso(skb))) { - NAPI_GRO_CB(skb)->count = skb_shinfo(skb)->gso_segs; - /* Only support TCP at the moment. */ - if (!skb_is_gso_tcp(skb)) - NAPI_GRO_CB(skb)->flush = 1; - } - - /* Setup for GRO checksum validation */ - switch (skb->ip_summed) { - case CHECKSUM_COMPLETE: - NAPI_GRO_CB(skb)->csum = skb->csum; - NAPI_GRO_CB(skb)->csum_valid = 1; - break; - case CHECKSUM_UNNECESSARY: - NAPI_GRO_CB(skb)->csum_cnt = skb->csum_level + 1; - break; - } - - pp = INDIRECT_CALL_INET(ptype->callbacks.gro_receive, - ipv6_gro_receive, inet_gro_receive, - &gro_list->list, skb); - break; + if (ptype->type == type && ptype->callbacks.gro_receive) + goto found_ptype; } rcu_read_unlock(); + goto normal; - if (&ptype->list == head) - goto normal; +found_ptype: + skb_set_network_header(skb, skb_gro_offset(skb)); + skb_reset_mac_len(skb); + BUILD_BUG_ON(sizeof_field(struct napi_gro_cb, zeroed) != sizeof(u32)); + BUILD_BUG_ON(!IS_ALIGNED(offsetof(struct napi_gro_cb, zeroed), + sizeof(u32))); /* Avoid slow unaligned acc */ + *(u32 *)&NAPI_GRO_CB(skb)->zeroed = 0; + NAPI_GRO_CB(skb)->flush = skb_has_frag_list(skb); + NAPI_GRO_CB(skb)->is_atomic = 1; + NAPI_GRO_CB(skb)->count = 1; + if (unlikely(skb_is_gso(skb))) { + NAPI_GRO_CB(skb)->count = skb_shinfo(skb)->gso_segs; + /* Only support TCP at the moment. */ + if (!skb_is_gso_tcp(skb)) + NAPI_GRO_CB(skb)->flush = 1; + } + + /* Setup for GRO checksum validation */ + switch (skb->ip_summed) { + case CHECKSUM_COMPLETE: + NAPI_GRO_CB(skb)->csum = skb->csum; + NAPI_GRO_CB(skb)->csum_valid = 1; + break; + case CHECKSUM_UNNECESSARY: + NAPI_GRO_CB(skb)->csum_cnt = skb->csum_level + 1; + break; + } + + pp = INDIRECT_CALL_INET(ptype->callbacks.gro_receive, + ipv6_gro_receive, inet_gro_receive, + &gro_list->list, skb); + + rcu_read_unlock(); if (PTR_ERR(pp) == -EINPROGRESS) { ret = GRO_CONSUMED; From e5d3a64e650c721f9e9b1f76e5df8c62f16b734d Mon Sep 17 00:00:00 2001 From: Avraham Stern Date: Sun, 30 Oct 2022 19:17:40 +0200 Subject: [PATCH 0795/2002] wifi: iwlwifi: mvm: send TKIP connection status to csme Notify csme when associated with TKIP cipher. TKIP is supported by csme. Signed-off-by: Avraham Stern Signed-off-by: Gregory Greenman Link: https://lore.kernel.org/r/20221030191011.cea63e4a355c.If6cdfa52529a79b923191c89dad7ed871d2ad2c6@changeid --- drivers/net/wireless/intel/iwlwifi/mei/iwl-mei.h | 2 ++ drivers/net/wireless/intel/iwlwifi/mei/sap.h | 4 +++- drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c | 3 +++ 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mei/iwl-mei.h b/drivers/net/wireless/intel/iwlwifi/mei/iwl-mei.h index 67122cfa2292..bea61c8fb526 100644 --- a/drivers/net/wireless/intel/iwlwifi/mei/iwl-mei.h +++ b/drivers/net/wireless/intel/iwlwifi/mei/iwl-mei.h @@ -220,6 +220,7 @@ struct iwl_mei_nvm { /** * enum iwl_mei_pairwise_cipher - cipher for UCAST key * @IWL_MEI_CIPHER_NONE: none + * @IWL_MEI_CIPHER_TKIP: tkip * @IWL_MEI_CIPHER_CCMP: ccmp * @IWL_MEI_CIPHER_GCMP: gcmp * @IWL_MEI_CIPHER_GCMP_256: gcmp 256 @@ -228,6 +229,7 @@ struct iwl_mei_nvm { */ enum iwl_mei_pairwise_cipher { IWL_MEI_CIPHER_NONE = 0, + IWL_MEI_CIPHER_TKIP = 2, IWL_MEI_CIPHER_CCMP = 4, IWL_MEI_CIPHER_GCMP = 8, IWL_MEI_CIPHER_GCMP_256 = 9, diff --git a/drivers/net/wireless/intel/iwlwifi/mei/sap.h b/drivers/net/wireless/intel/iwlwifi/mei/sap.h index be1456dea484..ef2664589fc1 100644 --- a/drivers/net/wireless/intel/iwlwifi/mei/sap.h +++ b/drivers/net/wireless/intel/iwlwifi/mei/sap.h @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Copyright (C) 2021 Intel Corporation + * Copyright (C) 2021 - 2022 Intel Corporation */ #ifndef __sap_h__ @@ -334,12 +334,14 @@ enum iwl_sap_wifi_auth_type { /** * enum iwl_sap_wifi_cipher_alg * @SAP_WIFI_CIPHER_ALG_NONE: TBD + * @SAP_WIFI_CIPHER_ALG_TKIP: TBD * @SAP_WIFI_CIPHER_ALG_CCMP: TBD * @SAP_WIFI_CIPHER_ALG_GCMP: TBD * @SAP_WIFI_CIPHER_ALG_GCMP_256: TBD */ enum iwl_sap_wifi_cipher_alg { SAP_WIFI_CIPHER_ALG_NONE = IWL_MEI_CIPHER_NONE, + SAP_WIFI_CIPHER_ALG_TKIP = IWL_MEI_CIPHER_TKIP, SAP_WIFI_CIPHER_ALG_CCMP = IWL_MEI_CIPHER_CCMP, SAP_WIFI_CIPHER_ALG_GCMP = IWL_MEI_CIPHER_GCMP, SAP_WIFI_CIPHER_ALG_GCMP_256 = IWL_MEI_CIPHER_GCMP_256, diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c index 8464c9b7baf1..156283237e2a 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c @@ -3059,6 +3059,9 @@ static void iwl_mvm_mei_host_associated(struct iwl_mvm *mvm, return; switch (mvm_sta->pairwise_cipher) { + case WLAN_CIPHER_SUITE_TKIP: + conn_info.pairwise_cipher = IWL_MEI_CIPHER_TKIP; + break; case WLAN_CIPHER_SUITE_CCMP: conn_info.pairwise_cipher = IWL_MEI_CIPHER_CCMP; break; From 5aa7ce31bd84c2f4f059200f06c537c920cbb458 Mon Sep 17 00:00:00 2001 From: Avraham Stern Date: Sun, 30 Oct 2022 19:17:41 +0200 Subject: [PATCH 0796/2002] wifi: iwlwifi: mei: make sure ownership confirmed message is sent It is possible that CSME will try to take ownership while the driver is stopping. In this case, if the CSME takes ownership message arrives after the driver started unregistering, the iwl_mei_cache->ops is already invalid, so the host will not answer with the ownership confirmed message. Similarly, if the take ownership message arrived after the mac was stopped or when iwl_mvm_up() failed, setting rfkill will not trigger sending the confirm message. As a result, CSME will not take ownership, which will result in a disconnection. Fix it by sending the ownership confirmed message immediately in such cases. Fixes: 2da4366f9e2c ("iwlwifi: mei: add the driver to allow cooperation with CSME") Signed-off-by: Avraham Stern Signed-off-by: Gregory Greenman Link: https://lore.kernel.org/r/20221030191011.b2a4c009e3e6.I7f931b7ee8b168e8ac88b11f23bff98b7ed3cb19@changeid --- .../net/wireless/intel/iwlwifi/mei/iwl-mei.h | 7 +++-- drivers/net/wireless/intel/iwlwifi/mei/main.c | 30 ++++++++++++------- drivers/net/wireless/intel/iwlwifi/mvm/fw.c | 2 ++ drivers/net/wireless/intel/iwlwifi/mvm/mvm.h | 4 +-- drivers/net/wireless/intel/iwlwifi/mvm/ops.c | 2 +- 5 files changed, 29 insertions(+), 16 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mei/iwl-mei.h b/drivers/net/wireless/intel/iwlwifi/mei/iwl-mei.h index bea61c8fb526..2e57438a70f0 100644 --- a/drivers/net/wireless/intel/iwlwifi/mei/iwl-mei.h +++ b/drivers/net/wireless/intel/iwlwifi/mei/iwl-mei.h @@ -448,9 +448,10 @@ void iwl_mei_host_associated(const struct iwl_mei_conn_info *conn_info, void iwl_mei_host_disassociated(void); /** - * iwl_mei_device_down() - must be called when the device is down + * iwl_mei_device_state() - must be called when the device changes up/down state + * @up: true if the device is up, false otherwise. */ -void iwl_mei_device_down(void); +void iwl_mei_device_state(bool up); #else @@ -499,7 +500,7 @@ static inline void iwl_mei_host_associated(const struct iwl_mei_conn_info *conn_ static inline void iwl_mei_host_disassociated(void) {} -static inline void iwl_mei_device_down(void) +static inline void iwl_mei_device_state(bool up) {} #endif /* CONFIG_IWLMEI */ diff --git a/drivers/net/wireless/intel/iwlwifi/mei/main.c b/drivers/net/wireless/intel/iwlwifi/mei/main.c index 357f14626cf4..90646c54a3c5 100644 --- a/drivers/net/wireless/intel/iwlwifi/mei/main.c +++ b/drivers/net/wireless/intel/iwlwifi/mei/main.c @@ -147,6 +147,8 @@ struct iwl_mei_filters { * to send CSME_OWNERSHIP_CONFIRMED when the driver completes its down * flow. * @link_prot_state: true when we are in link protection PASSIVE + * @device_down: true if the device is down. Used to remember to send + * CSME_OWNERSHIP_CONFIRMED when the driver is already down. * @csa_throttle_end_wk: used when &csa_throttled is true * @data_q_lock: protects the access to the data queues which are * accessed without the mutex. @@ -167,6 +169,7 @@ struct iwl_mei { bool csa_throttled; bool csme_taking_ownership; bool link_prot_state; + bool device_down; struct delayed_work csa_throttle_end_wk; spinlock_t data_q_lock; @@ -798,14 +801,18 @@ static void iwl_mei_handle_csme_taking_ownership(struct mei_cl_device *cldev, mei->got_ownership = false; - /* - * Remember to send CSME_OWNERSHIP_CONFIRMED when the wifi driver - * is finished taking the device down. - */ - mei->csme_taking_ownership = true; + if (iwl_mei_cache.ops && !mei->device_down) { + /* + * Remember to send CSME_OWNERSHIP_CONFIRMED when the wifi + * driver is finished taking the device down. + */ + mei->csme_taking_ownership = true; - if (iwl_mei_cache.ops) - iwl_mei_cache.ops->rfkill(iwl_mei_cache.priv, true); + iwl_mei_cache.ops->rfkill(iwl_mei_cache.priv, true, true); + } else { + iwl_mei_send_sap_msg(cldev, + SAP_MSG_NOTIF_CSME_OWNERSHIP_CONFIRMED); + } } static void iwl_mei_handle_nvm(struct mei_cl_device *cldev, @@ -1616,7 +1623,7 @@ out: } EXPORT_SYMBOL_GPL(iwl_mei_set_netdev); -void iwl_mei_device_down(void) +void iwl_mei_device_state(bool up) { struct iwl_mei *mei; @@ -1630,7 +1637,9 @@ void iwl_mei_device_down(void) if (!mei) goto out; - if (!mei->csme_taking_ownership) + mei->device_down = !up; + + if (up || !mei->csme_taking_ownership) goto out; iwl_mei_send_sap_msg(mei->cldev, @@ -1639,7 +1648,7 @@ void iwl_mei_device_down(void) out: mutex_unlock(&iwl_mei_mutex); } -EXPORT_SYMBOL_GPL(iwl_mei_device_down); +EXPORT_SYMBOL_GPL(iwl_mei_device_state); int iwl_mei_register(void *priv, const struct iwl_mei_ops *ops) { @@ -1821,6 +1830,7 @@ static int iwl_mei_probe(struct mei_cl_device *cldev, mei_cldev_set_drvdata(cldev, mei); mei->cldev = cldev; + mei->device_down = true; do { ret = iwl_mei_alloc_shared_mem(cldev); diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c index f041e77af059..5de34edc51fe 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c @@ -1665,6 +1665,8 @@ int iwl_mvm_up(struct iwl_mvm *mvm) iwl_rfi_send_config_cmd(mvm, NULL); } + iwl_mvm_mei_device_state(mvm, true); + IWL_DEBUG_INFO(mvm, "RT uCode started.\n"); return 0; error: diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h index 97cba526e465..1ccb3cad7cdc 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h @@ -2201,10 +2201,10 @@ static inline void iwl_mvm_mei_host_disassociated(struct iwl_mvm *mvm) iwl_mei_host_disassociated(); } -static inline void iwl_mvm_mei_device_down(struct iwl_mvm *mvm) +static inline void iwl_mvm_mei_device_state(struct iwl_mvm *mvm, bool up) { if (mvm->mei_registered) - iwl_mei_device_down(); + iwl_mei_device_state(up); } static inline void iwl_mvm_mei_set_sw_rfkill_state(struct iwl_mvm *mvm) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c index d2d42cd48af2..5b8e9a06f6d4 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c @@ -1375,7 +1375,7 @@ void iwl_mvm_stop_device(struct iwl_mvm *mvm) iwl_trans_stop_device(mvm->trans); iwl_free_fw_paging(&mvm->fwrt); iwl_fw_dump_conf_clear(&mvm->fwrt); - iwl_mvm_mei_device_down(mvm); + iwl_mvm_mei_device_state(mvm, false); } static void iwl_op_mode_mvm_stop(struct iwl_op_mode *op_mode) From 95170a46b7dddbc3ac31b20ef2e8fa9d556d783d Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Sun, 30 Oct 2022 19:17:42 +0200 Subject: [PATCH 0797/2002] wifi: iwlwifi: mei: don't send SAP commands if AMT is disabled MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We should not send any SAP command to CSME if AMT is disabled. Reported-by: Toke Høiland-Jørgensen Fixes: 2da4366f9e2c ("iwlwifi: mei: add the driver to allow cooperation with CSME") Signed-off-by: Emmanuel Grumbach Signed-off-by: Gregory Greenman Link: https://lore.kernel.org/r/20221030191011.ea222d41c781.Ifc90ddc3e35187683ff7f59371d792b61c8854c8@changeid --- drivers/net/wireless/intel/iwlwifi/mei/main.c | 85 ++++++++++--------- 1 file changed, 44 insertions(+), 41 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mei/main.c b/drivers/net/wireless/intel/iwlwifi/mei/main.c index 90646c54a3c5..64a637ef199c 100644 --- a/drivers/net/wireless/intel/iwlwifi/mei/main.c +++ b/drivers/net/wireless/intel/iwlwifi/mei/main.c @@ -596,8 +596,6 @@ iwl_mei_handle_rx_start_ok(struct mei_cl_device *cldev, const struct iwl_sap_me_msg_start_ok *rsp, ssize_t len) { - struct iwl_mei *mei = mei_cldev_get_drvdata(cldev); - if (len != sizeof(*rsp)) { dev_err(&cldev->dev, "got invalid SAP_ME_MSG_START_OK from CSME firmware\n"); @@ -616,13 +614,10 @@ iwl_mei_handle_rx_start_ok(struct mei_cl_device *cldev, mutex_lock(&iwl_mei_mutex); set_bit(IWL_MEI_STATUS_SAP_CONNECTED, &iwl_mei_status); - /* wifi driver has registered already */ - if (iwl_mei_cache.ops) { - iwl_mei_send_sap_msg(mei->cldev, - SAP_MSG_NOTIF_WIFIDR_UP); - iwl_mei_cache.ops->sap_connected(iwl_mei_cache.priv); - } - + /* + * We'll receive AMT_STATE SAP message in a bit and + * that will continue the flow + */ mutex_unlock(&iwl_mei_mutex); } @@ -715,6 +710,13 @@ static void iwl_mei_set_init_conf(struct iwl_mei *mei) .val = cpu_to_le32(iwl_mei_cache.rf_kill), }; + /* wifi driver has registered already */ + if (iwl_mei_cache.ops) { + iwl_mei_send_sap_msg(mei->cldev, + SAP_MSG_NOTIF_WIFIDR_UP); + iwl_mei_cache.ops->sap_connected(iwl_mei_cache.priv); + } + iwl_mei_send_sap_msg(mei->cldev, SAP_MSG_NOTIF_WHO_OWNS_NIC); if (iwl_mei_cache.conn_info) { @@ -1420,10 +1422,7 @@ void iwl_mei_host_associated(const struct iwl_mei_conn_info *conn_info, mei = mei_cldev_get_drvdata(iwl_mei_global_cldev); - if (!mei) - goto out; - - if (!mei->amt_enabled) + if (!mei && !mei->amt_enabled) goto out; iwl_mei_send_sap_msg_payload(mei->cldev, &msg.hdr); @@ -1452,7 +1451,7 @@ void iwl_mei_host_disassociated(void) mei = mei_cldev_get_drvdata(iwl_mei_global_cldev); - if (!mei) + if (!mei && !mei->amt_enabled) goto out; iwl_mei_send_sap_msg_payload(mei->cldev, &msg.hdr); @@ -1488,7 +1487,7 @@ void iwl_mei_set_rfkill_state(bool hw_rfkill, bool sw_rfkill) mei = mei_cldev_get_drvdata(iwl_mei_global_cldev); - if (!mei) + if (!mei && !mei->amt_enabled) goto out; iwl_mei_send_sap_msg_payload(mei->cldev, &msg.hdr); @@ -1517,7 +1516,7 @@ void iwl_mei_set_nic_info(const u8 *mac_address, const u8 *nvm_address) mei = mei_cldev_get_drvdata(iwl_mei_global_cldev); - if (!mei) + if (!mei && !mei->amt_enabled) goto out; iwl_mei_send_sap_msg_payload(mei->cldev, &msg.hdr); @@ -1545,7 +1544,7 @@ void iwl_mei_set_country_code(u16 mcc) mei = mei_cldev_get_drvdata(iwl_mei_global_cldev); - if (!mei) + if (!mei && !mei->amt_enabled) goto out; iwl_mei_send_sap_msg_payload(mei->cldev, &msg.hdr); @@ -1571,7 +1570,7 @@ void iwl_mei_set_power_limit(const __le16 *power_limit) mei = mei_cldev_get_drvdata(iwl_mei_global_cldev); - if (!mei) + if (!mei && !mei->amt_enabled) goto out; memcpy(msg.sar_chain_info_table, power_limit, sizeof(msg.sar_chain_info_table)); @@ -1678,9 +1677,10 @@ int iwl_mei_register(void *priv, const struct iwl_mei_ops *ops) /* we have already a SAP connection */ if (iwl_mei_is_connected()) { - iwl_mei_send_sap_msg(mei->cldev, - SAP_MSG_NOTIF_WIFIDR_UP); - ops->rfkill(priv, mei->link_prot_state); + if (mei->amt_enabled) + iwl_mei_send_sap_msg(mei->cldev, + SAP_MSG_NOTIF_WIFIDR_UP); + ops->rfkill(priv, mei->link_prot_state, false); } } ret = 0; @@ -1931,30 +1931,33 @@ static void iwl_mei_remove(struct mei_cl_device *cldev) mutex_lock(&iwl_mei_mutex); - /* - * Tell CSME that we are going down so that it won't access the - * memory anymore, make sure this message goes through immediately. - */ - mei->csa_throttled = false; - iwl_mei_send_sap_msg(mei->cldev, - SAP_MSG_NOTIF_HOST_GOES_DOWN); + if (mei->amt_enabled) { + /* + * Tell CSME that we are going down so that it won't access the + * memory anymore, make sure this message goes through immediately. + */ + mei->csa_throttled = false; + iwl_mei_send_sap_msg(mei->cldev, + SAP_MSG_NOTIF_HOST_GOES_DOWN); - for (i = 0; i < SEND_SAP_MAX_WAIT_ITERATION; i++) { - if (!iwl_mei_host_to_me_data_pending(mei)) - break; + for (i = 0; i < SEND_SAP_MAX_WAIT_ITERATION; i++) { + if (!iwl_mei_host_to_me_data_pending(mei)) + break; - msleep(5); + msleep(20); + } + + /* + * If we couldn't make sure that CSME saw the HOST_GOES_DOWN + * message, it means that it will probably keep reading memory + * that we are going to unmap and free, expect IOMMU error + * messages. + */ + if (i == SEND_SAP_MAX_WAIT_ITERATION) + dev_err(&mei->cldev->dev, + "Couldn't get ACK from CSME on HOST_GOES_DOWN message\n"); } - /* - * If we couldn't make sure that CSME saw the HOST_GOES_DOWN message, - * it means that it will probably keep reading memory that we are going - * to unmap and free, expect IOMMU error messages. - */ - if (i == SEND_SAP_MAX_WAIT_ITERATION) - dev_err(&mei->cldev->dev, - "Couldn't get ACK from CSME on HOST_GOES_DOWN message\n"); - mutex_unlock(&iwl_mei_mutex); /* From bcd68b3dbe78b7b0f7b6b55162cf1eff1e7fff9e Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Sun, 30 Oct 2022 19:17:43 +0200 Subject: [PATCH 0798/2002] wifi: iwlwifi: mei: fix tx DHCP packet for devices with new Tx API Devices with new Tx API have the IV introduced by the HW and it is not present in the skb at all. Hence we don't need to tell iwl_mvm_mei_tx_copy_to_csme to jump over 8 bytes to get to the ethernet header. Fixes: 2da4366f9e2c ("iwlwifi: mei: add the driver to allow cooperation with CSME") Signed-off-by: Emmanuel Grumbach Signed-off-by: Gregory Greenman Link: https://lore.kernel.org/r/20221030191011.12dc42133502.Idd744ffeeb84b880eb497963ee02563cbb959a42@changeid --- drivers/net/wireless/intel/iwlwifi/mvm/tx.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/tx.c b/drivers/net/wireless/intel/iwlwifi/mvm/tx.c index 86d20e13bf47..ba335f57771c 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/tx.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/tx.c @@ -1171,9 +1171,15 @@ static int iwl_mvm_tx_mpdu(struct iwl_mvm *mvm, struct sk_buff *skb, /* From now on, we cannot access info->control */ iwl_mvm_skb_prepare_status(skb, dev_cmd); + /* + * The IV is introduced by the HW for new tx api, and it is not present + * in the skb, hence, don't tell iwl_mvm_mei_tx_copy_to_csme about the + * IV for those devices. + */ if (ieee80211_is_data(fc)) iwl_mvm_mei_tx_copy_to_csme(mvm, skb, - info->control.hw_key ? + info->control.hw_key && + !iwl_mvm_has_new_tx_api(mvm) ? info->control.hw_key->iv_len : 0); if (iwl_trans_tx(mvm->trans, skb, dev_cmd, txq_id)) From d288067ede4b375e72daf7f9a98d937ede11a311 Mon Sep 17 00:00:00 2001 From: Avraham Stern Date: Sun, 30 Oct 2022 19:17:44 +0200 Subject: [PATCH 0799/2002] wifi: iwlwifi: mei: avoid blocking sap messages handling due to rtnl lock The AMT_STATE sap message handler tries to take the rtnl lock. This means that in case the rtnl lock is already taken, sap messages will not be processed. When an interface is brought up, the host requests ownership from csme. However, since the rtnl lock is already held, if there is a pending amt state message, the host will not be able to read the ownership confirm message because the amt state message handler is pending. As a result, the host fails to get ownership although csme granted it. Fix it by moving the part that needs the rtnl lock into a dedicated worker, so handling sap messages can continue. Fixes: 2da4366f9e2c ("iwlwifi: mei: add the driver to allow cooperation with CSME") Signed-off-by: Avraham Stern Signed-off-by: Gregory Greenman Link: https://lore.kernel.org/r/20221030191011.8599f2b4e9dd.I518f79e9099bf815c5f8d90235b4ce3250f59970@changeid --- drivers/net/wireless/intel/iwlwifi/mei/main.c | 57 ++++++++++++------- 1 file changed, 37 insertions(+), 20 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mei/main.c b/drivers/net/wireless/intel/iwlwifi/mei/main.c index 64a637ef199c..c0142093c768 100644 --- a/drivers/net/wireless/intel/iwlwifi/mei/main.c +++ b/drivers/net/wireless/intel/iwlwifi/mei/main.c @@ -152,6 +152,8 @@ struct iwl_mei_filters { * @csa_throttle_end_wk: used when &csa_throttled is true * @data_q_lock: protects the access to the data queues which are * accessed without the mutex. + * @netdev_work: used to defer registering and unregistering of the netdev to + * avoid taking the rtnl lock in the SAP messages handlers. * @sap_seq_no: the sequence number for the SAP messages * @seq_no: the sequence number for the SAP messages * @dbgfs_dir: the debugfs dir entry @@ -172,6 +174,7 @@ struct iwl_mei { bool device_down; struct delayed_work csa_throttle_end_wk; spinlock_t data_q_lock; + struct work_struct netdev_work; atomic_t sap_seq_no; atomic_t seq_no; @@ -591,6 +594,33 @@ static rx_handler_result_t iwl_mei_rx_handler(struct sk_buff **pskb) return res; } +static void iwl_mei_netdev_work(struct work_struct *wk) +{ + struct iwl_mei *mei = + container_of(wk, struct iwl_mei, netdev_work); + struct net_device *netdev; + + /* + * First take rtnl and only then the mutex to avoid an ABBA + * with iwl_mei_set_netdev() + */ + rtnl_lock(); + mutex_lock(&iwl_mei_mutex); + + netdev = rcu_dereference_protected(iwl_mei_cache.netdev, + lockdep_is_held(&iwl_mei_mutex)); + if (netdev) { + if (mei->amt_enabled) + netdev_rx_handler_register(netdev, iwl_mei_rx_handler, + mei); + else + netdev_rx_handler_unregister(netdev); + } + + mutex_unlock(&iwl_mei_mutex); + rtnl_unlock(); +} + static void iwl_mei_handle_rx_start_ok(struct mei_cl_device *cldev, const struct iwl_sap_me_msg_start_ok *rsp, @@ -743,38 +773,23 @@ static void iwl_mei_handle_amt_state(struct mei_cl_device *cldev, const struct iwl_sap_msg_dw *dw) { struct iwl_mei *mei = mei_cldev_get_drvdata(cldev); - struct net_device *netdev; - /* - * First take rtnl and only then the mutex to avoid an ABBA - * with iwl_mei_set_netdev() - */ - rtnl_lock(); mutex_lock(&iwl_mei_mutex); - netdev = rcu_dereference_protected(iwl_mei_cache.netdev, - lockdep_is_held(&iwl_mei_mutex)); - if (mei->amt_enabled == !!le32_to_cpu(dw->val)) goto out; mei->amt_enabled = dw->val; - if (mei->amt_enabled) { - if (netdev) - netdev_rx_handler_register(netdev, iwl_mei_rx_handler, mei); - + if (mei->amt_enabled) iwl_mei_set_init_conf(mei); - } else { - if (iwl_mei_cache.ops) - iwl_mei_cache.ops->rfkill(iwl_mei_cache.priv, false); - if (netdev) - netdev_rx_handler_unregister(netdev); - } + else if (iwl_mei_cache.ops) + iwl_mei_cache.ops->rfkill(iwl_mei_cache.priv, false, false); + + schedule_work(&mei->netdev_work); out: mutex_unlock(&iwl_mei_mutex); - rtnl_unlock(); } static void iwl_mei_handle_nic_owner(struct mei_cl_device *cldev, @@ -1827,6 +1842,7 @@ static int iwl_mei_probe(struct mei_cl_device *cldev, iwl_mei_csa_throttle_end_wk); init_waitqueue_head(&mei->get_ownership_wq); spin_lock_init(&mei->data_q_lock); + INIT_WORK(&mei->netdev_work, iwl_mei_netdev_work); mei_cldev_set_drvdata(cldev, mei); mei->cldev = cldev; @@ -1989,6 +2005,7 @@ static void iwl_mei_remove(struct mei_cl_device *cldev) */ cancel_work_sync(&mei->send_csa_msg_wk); cancel_delayed_work_sync(&mei->csa_throttle_end_wk); + cancel_work_sync(&mei->netdev_work); /* * If someone waits for the ownership, let him know that we are going From d3df49dda431f7ae4132a9a0ac25a5134c04e812 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Sun, 30 Oct 2022 19:17:45 +0200 Subject: [PATCH 0800/2002] wifi: iwlwifi: mei: fix potential NULL-ptr deref after clone If cloning the SKB fails, don't try to use it, but rather return as if we should pass it. Coverity CID: 1503456 Fixes: 2da4366f9e2c ("iwlwifi: mei: add the driver to allow cooperation with CSME") Signed-off-by: Johannes Berg Signed-off-by: Gregory Greenman Link: https://lore.kernel.org/r/20221030191011.0ce03ba99601.I87960b7cb0a3d16b9fd8d9144027e7e2587f5a58@changeid --- drivers/net/wireless/intel/iwlwifi/mei/net.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mei/net.c b/drivers/net/wireless/intel/iwlwifi/mei/net.c index 3472167c8370..eac46d1a397a 100644 --- a/drivers/net/wireless/intel/iwlwifi/mei/net.c +++ b/drivers/net/wireless/intel/iwlwifi/mei/net.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Copyright (C) 2021 Intel Corporation + * Copyright (C) 2021-2022 Intel Corporation */ #include @@ -337,10 +337,14 @@ rx_handler_result_t iwl_mei_rx_filter(struct sk_buff *orig_skb, if (!*pass_to_csme) return RX_HANDLER_PASS; - if (ret == RX_HANDLER_PASS) + if (ret == RX_HANDLER_PASS) { skb = skb_copy(orig_skb, GFP_ATOMIC); - else + + if (!skb) + return RX_HANDLER_PASS; + } else { skb = orig_skb; + } /* CSME wants the MAC header as well, push it back */ skb_push(skb, skb->data - skb_mac_header(skb)); From ec18e7d4d20dc6be85aaf02dfa3cebbb136a7c26 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 2 Nov 2022 16:59:48 +0200 Subject: [PATCH 0801/2002] wifi: iwlwifi: mvm: use old checksum for Bz A-step For Bz A-step hardware, the checksum offload is broken and we need to use the old way, which is still there. Do that, which requires taking the checksum capability bits out of the IWL_DEVICE_BZ macro and listing them individually. Signed-off-by: Johannes Berg Signed-off-by: Gregory Greenman Link: https://lore.kernel.org/r/20221102165239.6bc379f1b0b1.I204223f1b1c2fe26f414aea6679ef7fce681c33a@changeid --- drivers/net/wireless/intel/iwlwifi/cfg/22000.c | 16 +++++++++++++++- drivers/net/wireless/intel/iwlwifi/mvm/tx.c | 7 +++++-- 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/cfg/22000.c b/drivers/net/wireless/intel/iwlwifi/cfg/22000.c index 110fda65bd21..b46f65b6d595 100644 --- a/drivers/net/wireless/intel/iwlwifi/cfg/22000.c +++ b/drivers/net/wireless/intel/iwlwifi/cfg/22000.c @@ -261,7 +261,6 @@ static const struct iwl_ht_params iwl_22000_ht_params = { .dccm2_len = IWL_22000_DCCM2_LEN, \ .smem_offset = IWL_22000_SMEM_OFFSET, \ .smem_len = IWL_22000_SMEM_LEN, \ - .features = IWL_TX_CSUM_NETIF_FLAGS_BZ | NETIF_F_RXCSUM, \ .apmg_not_supported = true, \ .trans.mq_rx_supported = true, \ .vht_mu_mimo_supported = true, \ @@ -901,6 +900,7 @@ const struct iwl_cfg iwl_cfg_bz_a0_hr_b0 = { .fw_name_pre = IWL_BZ_A_HR_B_FW_PRE, .uhb_supported = true, IWL_DEVICE_BZ, + .features = IWL_TX_CSUM_NETIF_FLAGS_BZ | NETIF_F_RXCSUM, .num_rbds = IWL_NUM_RBDS_AX210_HE, }; @@ -908,6 +908,7 @@ const struct iwl_cfg iwl_cfg_bz_a0_gf_a0 = { .fw_name_pre = IWL_BZ_A_GF_A_FW_PRE, .uhb_supported = true, IWL_DEVICE_BZ, + .features = IWL_TX_CSUM_NETIF_FLAGS_BZ | NETIF_F_RXCSUM, .num_rbds = IWL_NUM_RBDS_AX210_HE, }; @@ -915,6 +916,7 @@ const struct iwl_cfg iwl_cfg_bz_a0_gf4_a0 = { .fw_name_pre = IWL_BZ_A_GF4_A_FW_PRE, .uhb_supported = true, IWL_DEVICE_BZ, + .features = IWL_TX_CSUM_NETIF_FLAGS_BZ | NETIF_F_RXCSUM, .num_rbds = IWL_NUM_RBDS_AX210_HE, }; @@ -922,6 +924,7 @@ const struct iwl_cfg iwl_cfg_bz_a0_mr_a0 = { .fw_name_pre = IWL_BZ_A_MR_A_FW_PRE, .uhb_supported = true, IWL_DEVICE_BZ, + .features = IWL_TX_CSUM_NETIF_FLAGS_BZ | NETIF_F_RXCSUM, .num_rbds = IWL_NUM_RBDS_AX210_HE, }; @@ -929,6 +932,7 @@ const struct iwl_cfg iwl_cfg_bz_a0_fm_a0 = { .fw_name_pre = IWL_BZ_A_FM_A_FW_PRE, .uhb_supported = true, IWL_DEVICE_BZ, + .features = IWL_TX_CSUM_NETIF_FLAGS_BZ | NETIF_F_RXCSUM, .num_rbds = IWL_NUM_RBDS_AX210_HE, }; @@ -936,6 +940,7 @@ const struct iwl_cfg iwl_cfg_bz_a0_fm4_a0 = { .fw_name_pre = IWL_BZ_A_FM4_A_FW_PRE, .uhb_supported = true, IWL_DEVICE_BZ, + .features = IWL_TX_CSUM_NETIF_FLAGS_BZ | NETIF_F_RXCSUM, .num_rbds = IWL_NUM_RBDS_AX210_HE, }; @@ -943,6 +948,7 @@ const struct iwl_cfg iwl_cfg_gl_a0_fm_a0 = { .fw_name_pre = IWL_GL_A_FM_A_FW_PRE, .uhb_supported = true, IWL_DEVICE_BZ, + .features = IWL_TX_CSUM_NETIF_FLAGS | NETIF_F_RXCSUM, .num_rbds = IWL_NUM_RBDS_AX210_HE, }; @@ -950,6 +956,7 @@ const struct iwl_cfg iwl_cfg_gl_b0_fm_b0 = { .fw_name_pre = IWL_GL_B_FM_B_FW_PRE, .uhb_supported = true, IWL_DEVICE_BZ, + .features = IWL_TX_CSUM_NETIF_FLAGS_BZ | NETIF_F_RXCSUM, .num_rbds = IWL_NUM_RBDS_AX210_HE, }; @@ -957,6 +964,7 @@ const struct iwl_cfg iwl_cfg_bz_z0_gf_a0 = { .fw_name_pre = IWL_BZ_Z_GF_A_FW_PRE, .uhb_supported = true, IWL_DEVICE_BZ, + .features = IWL_TX_CSUM_NETIF_FLAGS_BZ | NETIF_F_RXCSUM, .num_rbds = IWL_NUM_RBDS_AX210_HE, }; @@ -964,6 +972,7 @@ const struct iwl_cfg iwl_cfg_bnj_a0_fm_a0 = { .fw_name_pre = IWL_BNJ_A_FM_A_FW_PRE, .uhb_supported = true, IWL_DEVICE_BZ, + .features = IWL_TX_CSUM_NETIF_FLAGS | NETIF_F_RXCSUM, .num_rbds = IWL_NUM_RBDS_AX210_HE, }; @@ -971,6 +980,7 @@ const struct iwl_cfg iwl_cfg_bnj_a0_fm4_a0 = { .fw_name_pre = IWL_BNJ_A_FM4_A_FW_PRE, .uhb_supported = true, IWL_DEVICE_BZ, + .features = IWL_TX_CSUM_NETIF_FLAGS | NETIF_F_RXCSUM, .num_rbds = IWL_NUM_RBDS_AX210_HE, }; @@ -978,6 +988,7 @@ const struct iwl_cfg iwl_cfg_bnj_a0_gf_a0 = { .fw_name_pre = IWL_BNJ_A_GF_A_FW_PRE, .uhb_supported = true, IWL_DEVICE_BZ, + .features = IWL_TX_CSUM_NETIF_FLAGS | NETIF_F_RXCSUM, .num_rbds = IWL_NUM_RBDS_AX210_HE, }; @@ -985,6 +996,7 @@ const struct iwl_cfg iwl_cfg_bnj_a0_gf4_a0 = { .fw_name_pre = IWL_BNJ_A_GF4_A_FW_PRE, .uhb_supported = true, IWL_DEVICE_BZ, + .features = IWL_TX_CSUM_NETIF_FLAGS | NETIF_F_RXCSUM, .num_rbds = IWL_NUM_RBDS_AX210_HE, }; @@ -992,6 +1004,7 @@ const struct iwl_cfg iwl_cfg_bnj_a0_hr_b0 = { .fw_name_pre = IWL_BNJ_A_HR_B_FW_PRE, .uhb_supported = true, IWL_DEVICE_BZ, + .features = IWL_TX_CSUM_NETIF_FLAGS | NETIF_F_RXCSUM, .num_rbds = IWL_NUM_RBDS_AX210_HE, }; @@ -999,6 +1012,7 @@ const struct iwl_cfg iwl_cfg_bnj_b0_fm_b0 = { .fw_name_pre = IWL_BNJ_B_FM_B_FW_PRE, .uhb_supported = true, IWL_DEVICE_BZ, + .features = IWL_TX_CSUM_NETIF_FLAGS_BZ | NETIF_F_RXCSUM, .num_rbds = IWL_NUM_RBDS_AX210_HE, }; MODULE_FIRMWARE(IWL_QU_B_HR_B_MODULE_FIRMWARE(IWL_22000_UCODE_API_MAX)); diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/tx.c b/drivers/net/wireless/intel/iwlwifi/mvm/tx.c index ba335f57771c..f460332333a7 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/tx.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/tx.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2012-2014, 2018-2021 Intel Corporation + * Copyright (C) 2012-2014, 2018-2022 Intel Corporation * Copyright (C) 2013-2015 Intel Mobile Communications GmbH * Copyright (C) 2016-2017 Intel Deutschland GmbH */ @@ -183,7 +183,10 @@ static u32 iwl_mvm_tx_csum(struct iwl_mvm *mvm, struct sk_buff *skb, struct ieee80211_tx_info *info, bool amsdu) { - if (mvm->trans->trans_cfg->device_family < IWL_DEVICE_FAMILY_BZ) + if (mvm->trans->trans_cfg->device_family < IWL_DEVICE_FAMILY_BZ || + (mvm->trans->trans_cfg->device_family == IWL_DEVICE_FAMILY_BZ && + CSR_HW_REV_TYPE(mvm->trans->hw_rev) == IWL_CFG_MAC_TYPE_GL && + mvm->trans->hw_rev_step == SILICON_A_STEP)) return iwl_mvm_tx_csum_pre_bz(mvm, skb, info, amsdu); return iwl_mvm_tx_csum_bz(mvm, skb, amsdu); } From 0323f194c0da764b339048b45149e48851e7ea68 Mon Sep 17 00:00:00 2001 From: Rotem Saado Date: Wed, 2 Nov 2022 16:59:49 +0200 Subject: [PATCH 0802/2002] wifi: iwlwifi: dbg: add support for DBGC4 on BZ family and above Update the for loop that iterates over all the DBGCs to include DBGC4. DBGC4 is only supported staring from the BZ family of devices, so check the family before accepting it. Signed-off-by: Rotem Saado Signed-off-by: Gregory Greenman Link: https://lore.kernel.org/r/20221102165239.0b6050b41060.Id662c716c2e3c2c850740d1f71e2801e02aeddaf@changeid --- drivers/net/wireless/intel/iwlwifi/iwl-dbg-tlv.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-dbg-tlv.c b/drivers/net/wireless/intel/iwlwifi/iwl-dbg-tlv.c index 3237d4b528b5..cd7a84c553e9 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-dbg-tlv.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-dbg-tlv.c @@ -590,6 +590,9 @@ static int iwl_dbg_tlv_alloc_fragments(struct iwl_fw_runtime *fwrt, if (alloc_id != IWL_FW_INI_ALLOCATION_ID_DBGC1) return -EIO; num_frags = 1; + } else if (fwrt->trans->trans_cfg->device_family < IWL_DEVICE_FAMILY_BZ && + alloc_id > IWL_FW_INI_ALLOCATION_ID_DBGC3) { + return -EIO; } remain_pages = DIV_ROUND_UP(le32_to_cpu(fw_mon_cfg->req_size), @@ -789,7 +792,7 @@ static void iwl_dbg_tlv_update_drams(struct iwl_fw_runtime *fwrt) dram_info->second_word = cpu_to_le32(DRAM_INFO_SECOND_MAGIC_WORD); for (i = IWL_FW_INI_ALLOCATION_ID_DBGC1; - i <= IWL_FW_INI_ALLOCATION_ID_DBGC3; i++) { + i < IWL_FW_INI_ALLOCATION_NUM; i++) { ret = iwl_dbg_tlv_update_dram(fwrt, i, dram_info); if (!ret) dram_alloc = true; From 06ffeebbfbd4458778d6ba41689f66ad35b26219 Mon Sep 17 00:00:00 2001 From: Luca Coelho Date: Wed, 2 Nov 2022 16:59:50 +0200 Subject: [PATCH 0803/2002] wifi: iwlwifi: cfg: disable STBC for BL step A devices The A step of BL devices doesn't support STBC, so we need to disable it. Create new HT params and a macro to use specifically with BL A-step devices where STBC is disabled. Signed-off-by: Luca Coelho Signed-off-by: Gregory Greenman Link: https://lore.kernel.org/r/20221102165239.806dbfa53f40.I4b14a8be30795bdcd73686bd1f9222e7ab93b505@changeid --- .../net/wireless/intel/iwlwifi/cfg/22000.c | 20 ++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/cfg/22000.c b/drivers/net/wireless/intel/iwlwifi/cfg/22000.c index b46f65b6d595..ec6198f1b38c 100644 --- a/drivers/net/wireless/intel/iwlwifi/cfg/22000.c +++ b/drivers/net/wireless/intel/iwlwifi/cfg/22000.c @@ -172,6 +172,13 @@ static const struct iwl_ht_params iwl_22000_ht_params = { BIT(NL80211_BAND_6GHZ), }; +static const struct iwl_ht_params iwl_gl_a_ht_params = { + .stbc = false, /* we explicitly disable STBC for GL step A */ + .ldpc = true, + .ht40_bands = BIT(NL80211_BAND_2GHZ) | BIT(NL80211_BAND_5GHZ) | + BIT(NL80211_BAND_6GHZ), +}; + #define IWL_DEVICE_22000_COMMON \ .ucode_api_max = IWL_22000_UCODE_API_MAX, \ .ucode_api_min = IWL_22000_UCODE_API_MIN, \ @@ -249,7 +256,7 @@ static const struct iwl_ht_params iwl_22000_ht_params = { }, \ } -#define IWL_DEVICE_BZ \ +#define IWL_DEVICE_BZ_COMMON \ .ucode_api_max = IWL_22000_UCODE_API_MAX, \ .ucode_api_min = IWL_22000_UCODE_API_MIN, \ .led_mode = IWL_LED_RF_STATE, \ @@ -265,7 +272,6 @@ static const struct iwl_ht_params iwl_22000_ht_params = { .trans.mq_rx_supported = true, \ .vht_mu_mimo_supported = true, \ .mac_addr_from_csr = 0x30, \ - .ht_params = &iwl_22000_ht_params, \ .nvm_ver = IWL_22000_NVM_VERSION, \ .trans.use_tfh = true, \ .trans.rf_id = true, \ @@ -312,6 +318,14 @@ static const struct iwl_ht_params iwl_22000_ht_params = { }, \ } +#define IWL_DEVICE_BZ \ + IWL_DEVICE_BZ_COMMON, \ + .ht_params = &iwl_22000_ht_params + +#define IWL_DEVICE_GL_A \ + IWL_DEVICE_BZ_COMMON, \ + .ht_params = &iwl_gl_a_ht_params + const struct iwl_cfg_trans_params iwl_qnj_trans_cfg = { .mq_rx_supported = true, .use_tfh = true, @@ -947,7 +961,7 @@ const struct iwl_cfg iwl_cfg_bz_a0_fm4_a0 = { const struct iwl_cfg iwl_cfg_gl_a0_fm_a0 = { .fw_name_pre = IWL_GL_A_FM_A_FW_PRE, .uhb_supported = true, - IWL_DEVICE_BZ, + IWL_DEVICE_GL_A, .features = IWL_TX_CSUM_NETIF_FLAGS | NETIF_F_RXCSUM, .num_rbds = IWL_NUM_RBDS_AX210_HE, }; From b79d2219e9352f7972035c9e07da3e1f4d2b2572 Mon Sep 17 00:00:00 2001 From: Luca Coelho Date: Wed, 2 Nov 2022 16:59:51 +0200 Subject: [PATCH 0804/2002] wifi: iwlwifi: mvm: print an error instead of a warning on invalid rate In some rare occasions, the firmware may let some frames with invalid rates, such as CCK rates on the high band, come through. This causes the driver to issue a warning, but since this is a possible issue and it's not really a bug in the driver, convert the warning into an error. Signed-off-by: Luca Coelho Signed-off-by: Gregory Greenman Link: https://lore.kernel.org/r/20221102165239.3d3673c70556.I13464b11d405fd6021618b0a32404cecb7e9ac51@changeid --- drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c b/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c index 1aadccd8841f..5f782ca1e254 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c @@ -1747,10 +1747,12 @@ static void iwl_mvm_rx_fill_status(struct iwl_mvm *mvm, rx_status->rate_idx = rate; - if (WARN_ONCE(rate < 0 || rate > 0xFF, - "Invalid rate flags 0x%x, band %d,\n", - rate_n_flags, rx_status->band)) + if ((rate < 0 || rate > 0xFF) && net_ratelimit()) { + IWL_ERR(mvm, "Invalid rate flags 0x%x, band %d,\n", + rate_n_flags, rx_status->band); rx_status->rate_idx = 0; + } + break; } } From ae5ecbb0c39e4a5872b9e6f31fb0ce350edd833a Mon Sep 17 00:00:00 2001 From: Rotem Saado Date: Wed, 2 Nov 2022 16:59:52 +0200 Subject: [PATCH 0805/2002] wifi: iwlwifi: dbg: use bit of DRAM alloc ID to store failed allocs The failed_alloc variable is used as a bitmask in the loop where we check DRAM allocations. But erroneously, we were clearing the DRAM alloc IDs we removed as an integer. Fix that by clearing them as bits instead. Signed-off-by: Rotem Saado Signed-off-by: Gregory Greenman Link: https://lore.kernel.org/r/20221102165239.688dec28b1d9.I470b8d29c28d16f25f4192773f075940de7ed33c@changeid --- drivers/net/wireless/intel/iwlwifi/iwl-dbg-tlv.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-dbg-tlv.c b/drivers/net/wireless/intel/iwlwifi/iwl-dbg-tlv.c index cd7a84c553e9..6d6c12999645 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-dbg-tlv.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-dbg-tlv.c @@ -1327,7 +1327,7 @@ static void iwl_dbg_tlv_init_cfg(struct iwl_fw_runtime *fwrt) "WRT: removing allocation id %d from region id %d\n", le32_to_cpu(reg->dram_alloc_id), i); - failed_alloc &= ~le32_to_cpu(reg->dram_alloc_id); + failed_alloc &= ~BIT(le32_to_cpu(reg->dram_alloc_id)); fwrt->trans->dbg.unsupported_region_msk |= BIT(i); kfree(*active_reg); From 5c75a208c2449c6ea24f07610cc052f6a352246c Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 2 Nov 2022 16:59:53 +0200 Subject: [PATCH 0806/2002] wifi: iwlwifi: mvm: support new key API In order to support MLD, the key API is also changing to have station masks instead of just the station ID etc. Change the driver to support this, and add the new code in a new file so it's more clearly separated. For now this isn't separated at the mac80211 ops level, which we wanted to do, but we're calling these functions in a place when pre-start keys are installed in iwl_mvm_start_ap_ibss(), and the function has some glue logic to mac80211. We may want to change that later. Signed-off-by: Johannes Berg Signed-off-by: Gregory Greenman Link: https://lore.kernel.org/r/20221102165239.ed9ccd814abc.Iacc7360de68807fbac19e5b67c86504b39cc15df@changeid --- .../wireless/intel/iwlwifi/fw/api/datapath.h | 79 ++++++ .../net/wireless/intel/iwlwifi/mvm/Makefile | 1 + .../net/wireless/intel/iwlwifi/mvm/mac80211.c | 17 +- .../net/wireless/intel/iwlwifi/mvm/mld-key.c | 226 ++++++++++++++++++ drivers/net/wireless/intel/iwlwifi/mvm/mvm.h | 12 + drivers/net/wireless/intel/iwlwifi/mvm/ops.c | 1 + drivers/net/wireless/intel/iwlwifi/mvm/sta.c | 3 + 7 files changed, 337 insertions(+), 2 deletions(-) create mode 100644 drivers/net/wireless/intel/iwlwifi/mvm/mld-key.c diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/datapath.h b/drivers/net/wireless/intel/iwlwifi/fw/api/datapath.h index 43619acc29fd..d07982d8c897 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/datapath.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/datapath.h @@ -71,6 +71,11 @@ enum iwl_data_path_subcmd_ids { */ SCD_QUEUE_CONFIG_CMD = 0x17, + /** + * @SEC_KEY_CMD: security key command, uses &struct iwl_sec_key_cmd + */ + SEC_KEY_CMD = 0x18, + /** * @MONITOR_NOTIF: Datapath monitoring notification, using * &struct iwl_datapath_monitor_notif @@ -403,4 +408,78 @@ struct iwl_scd_queue_cfg_cmd { } __packed u; /* TX_QUEUE_CFG_CMD_OPERATION_API_U_VER_1 */ } __packed; /* TX_QUEUE_CFG_CMD_API_S_VER_3 */ +/** + * enum iwl_sec_key_flags - security key command key flags + * @IWL_SEC_KEY_FLAG_CIPHER_MASK: cipher mask + * @IWL_SEC_KEY_FLAG_CIPHER_WEP: WEP cipher + * @IWL_SEC_KEY_FLAG_CIPHER_CCMP: CCMP/CMAC cipher + * @IWL_SEC_KEY_FLAG_CIPHER_TKIP: TKIP cipher + * @IWL_SEC_KEY_FLAG_CIPHER_GCMP: GCMP/GMAC cipher + * @IWL_SEC_KEY_FLAG_NO_TX: don't install for TX + * @IWL_SEC_KEY_FLAG_KEY_SIZE: large key size (WEP-104, GCMP-256, GMAC-256) + * @IWL_SEC_KEY_FLAG_MFP: MFP is in used for this key + * @IWL_SEC_KEY_FLAG_MCAST_KEY: this is a multicast key + * @IWL_SEC_KEY_FLAG_SPP_AMSDU: SPP A-MSDU should be used + */ +enum iwl_sec_key_flags { + IWL_SEC_KEY_FLAG_CIPHER_MASK = 0x07, + IWL_SEC_KEY_FLAG_CIPHER_WEP = 0x01, + IWL_SEC_KEY_FLAG_CIPHER_CCMP = 0x02, + IWL_SEC_KEY_FLAG_CIPHER_TKIP = 0x03, + IWL_SEC_KEY_FLAG_CIPHER_GCMP = 0x05, + IWL_SEC_KEY_FLAG_NO_TX = 0x08, + IWL_SEC_KEY_FLAG_KEY_SIZE = 0x10, + IWL_SEC_KEY_FLAG_MFP = 0x20, + IWL_SEC_KEY_FLAG_MCAST_KEY = 0x40, + IWL_SEC_KEY_FLAG_SPP_AMSDU = 0x80, +}; + +#define IWL_SEC_WEP_KEY_OFFSET 3 + +/** + * struct iwl_sec_key_cmd - security key command + * @action: action from &enum iwl_ctxt_action + * @u.add.sta_mask: station mask for the new key + * @u.add.key_id: key ID (0-7) for the new key + * @u.add.key_flags: key flags per &enum iwl_sec_key_flags + * @u.add.key: key material. WEP keys should start from &IWL_SEC_WEP_KEY_OFFSET. + * @u.add.tkip_mic_rx_key: TKIP MIC RX key + * @u.add.tkip_mic_tx_key: TKIP MIC TX key + * @u.add.rx_seq: RX sequence counter value + * @u.add.tx_seq: TX sequence counter value + * @u.modify.old_sta_mask: old station mask + * @u.modify.new_sta_mask: new station mask + * @u.modify.key_id: key ID + * @u.modify.key_flags: new key flags + * @u.remove.sta_mask: station mask + * @u.remove.key_id: key ID + * @u.remove.key_flags: key flags + */ +struct iwl_sec_key_cmd { + __le32 action; + union { + struct { + __le32 sta_mask; + __le32 key_id; + __le32 key_flags; + u8 key[32]; + u8 tkip_mic_rx_key[8]; + u8 tkip_mic_tx_key[8]; + __le64 rx_seq; + __le64 tx_seq; + } __packed add; /* SEC_KEY_ADD_CMD_API_S_VER_1 */ + struct { + __le32 old_sta_mask; + __le32 new_sta_mask; + __le32 key_id; + __le32 key_flags; + } __packed modify; /* SEC_KEY_MODIFY_CMD_API_S_VER_1 */ + struct { + __le32 sta_mask; + __le32 key_id; + __le32 key_flags; + } __packed remove; /* SEC_KEY_REMOVE_CMD_API_S_VER_1 */ + } __packed u; /* SEC_KEY_OPERATION_API_U_VER_1 */ +} __packed; /* SEC_KEY_CMD_API_S_VER_1 */ + #endif /* __iwl_fw_api_datapath_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/Makefile b/drivers/net/wireless/intel/iwlwifi/mvm/Makefile index 11e814b7cad0..b28fcf0cf9cf 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/Makefile +++ b/drivers/net/wireless/intel/iwlwifi/mvm/Makefile @@ -7,6 +7,7 @@ iwlmvm-y += power.o coex.o iwlmvm-y += tt.o offloading.o tdls.o iwlmvm-y += ftm-responder.o ftm-initiator.o iwlmvm-y += rfi.o +iwlmvm-y += mld-key.o iwlmvm-$(CONFIG_IWLWIFI_DEBUGFS) += debugfs.o debugfs-vif.o iwlmvm-$(CONFIG_IWLWIFI_LEDS) += led.o iwlmvm-$(CONFIG_PM) += d3.o diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c index 156283237e2a..e923e9a75c92 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c @@ -2306,6 +2306,9 @@ static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm, */ if (!test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) { + /* first remove remaining keys */ + iwl_mvm_sec_key_remove_ap(mvm, vif); + /* * Remove AP station now that * the MAC is unassoc @@ -3464,6 +3467,8 @@ static int __iwl_mvm_mac_set_key(struct ieee80211_hw *hw, struct iwl_mvm_sta *mvmsta = NULL; struct iwl_mvm_key_pn *ptk_pn; int keyidx = key->keyidx; + u32 sec_key_id = WIDE_ID(DATA_PATH_GROUP, SEC_KEY_CMD); + u8 sec_key_ver = iwl_fw_lookup_cmd_ver(mvm->fw, sec_key_id, 0); int ret, i; u8 key_offset; @@ -3603,7 +3608,12 @@ static int __iwl_mvm_mac_set_key(struct ieee80211_hw *hw, mvmsta->pairwise_cipher = key->cipher; IWL_DEBUG_MAC80211(mvm, "set hwcrypto key\n"); - ret = iwl_mvm_set_sta_key(mvm, vif, sta, key, key_offset); + + if (sec_key_ver) + ret = iwl_mvm_sec_key_add(mvm, vif, sta, key); + else + ret = iwl_mvm_set_sta_key(mvm, vif, sta, key, key_offset); + if (ret) { IWL_WARN(mvm, "set key failed\n"); key->hw_key_idx = STA_KEY_IDX_INVALID; @@ -3656,7 +3666,10 @@ static int __iwl_mvm_mac_set_key(struct ieee80211_hw *hw, } IWL_DEBUG_MAC80211(mvm, "disable hwcrypto key\n"); - ret = iwl_mvm_remove_sta_key(mvm, vif, sta, key); + if (sec_key_ver) + ret = iwl_mvm_sec_key_del(mvm, vif, sta, key); + else + ret = iwl_mvm_remove_sta_key(mvm, vif, sta, key); break; default: ret = -EINVAL; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mld-key.c b/drivers/net/wireless/intel/iwlwifi/mvm/mld-key.c new file mode 100644 index 000000000000..e27c893502f7 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mld-key.c @@ -0,0 +1,226 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* + * Copyright (C) 2022 Intel Corporation + */ +#include +#include +#include "mvm.h" +#include "fw/api/context.h" +#include "fw/api/datapath.h" + +static u32 iwl_mvm_get_sec_sta_mask(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct ieee80211_key_conf *keyconf) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + + if (vif->type == NL80211_IFTYPE_AP && + !(keyconf->flags & IEEE80211_KEY_FLAG_PAIRWISE)) + return BIT(mvmvif->mcast_sta.sta_id); + + if (sta) { + struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); + + return BIT(mvmsta->sta_id); + } + + if (vif->type == NL80211_IFTYPE_STATION && + mvmvif->ap_sta_id != IWL_MVM_INVALID_STA) + return BIT(mvmvif->ap_sta_id); + + /* invalid */ + return 0; +} + +static u32 iwl_mvm_get_sec_flags(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct ieee80211_key_conf *keyconf) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + u32 flags = 0; + + if (!(keyconf->flags & IEEE80211_KEY_FLAG_PAIRWISE)) + flags |= IWL_SEC_KEY_FLAG_MCAST_KEY; + + switch (keyconf->cipher) { + case WLAN_CIPHER_SUITE_WEP104: + flags |= IWL_SEC_KEY_FLAG_KEY_SIZE; + fallthrough; + case WLAN_CIPHER_SUITE_WEP40: + flags |= IWL_SEC_KEY_FLAG_CIPHER_WEP; + break; + case WLAN_CIPHER_SUITE_TKIP: + flags |= IWL_SEC_KEY_FLAG_CIPHER_TKIP; + break; + case WLAN_CIPHER_SUITE_AES_CMAC: + case WLAN_CIPHER_SUITE_CCMP: + flags |= IWL_SEC_KEY_FLAG_CIPHER_CCMP; + break; + case WLAN_CIPHER_SUITE_GCMP_256: + case WLAN_CIPHER_SUITE_BIP_GMAC_256: + flags |= IWL_SEC_KEY_FLAG_KEY_SIZE; + fallthrough; + case WLAN_CIPHER_SUITE_GCMP: + case WLAN_CIPHER_SUITE_BIP_GMAC_128: + flags |= IWL_SEC_KEY_FLAG_CIPHER_GCMP; + break; + } + + rcu_read_lock(); + if (!sta && vif->type == NL80211_IFTYPE_STATION && + mvmvif->ap_sta_id != IWL_MVM_INVALID_STA) { + u8 sta_id = mvmvif->ap_sta_id; + + sta = rcu_dereference_check(mvm->fw_id_to_mac_id[sta_id], + lockdep_is_held(&mvm->mutex)); + } + + if (!IS_ERR_OR_NULL(sta) && sta->mfp) + flags |= IWL_SEC_KEY_FLAG_MFP; + rcu_read_unlock(); + + return flags; +} + +static int __iwl_mvm_sec_key_del(struct iwl_mvm *mvm, u32 sta_mask, + u32 key_flags, u32 keyidx, u32 flags) +{ + u32 cmd_id = WIDE_ID(DATA_PATH_GROUP, SEC_KEY_CMD); + struct iwl_sec_key_cmd cmd = { + .action = cpu_to_le32(FW_CTXT_ACTION_REMOVE), + .u.remove.sta_mask = cpu_to_le32(sta_mask), + .u.remove.key_id = cpu_to_le32(keyidx), + .u.remove.key_flags = cpu_to_le32(key_flags), + }; + + return iwl_mvm_send_cmd_pdu(mvm, cmd_id, flags, sizeof(cmd), &cmd); +} + +int iwl_mvm_sec_key_add(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct ieee80211_key_conf *keyconf) +{ + u32 sta_mask = iwl_mvm_get_sec_sta_mask(mvm, vif, sta, keyconf); + u32 key_flags = iwl_mvm_get_sec_flags(mvm, vif, sta, keyconf); + u32 cmd_id = WIDE_ID(DATA_PATH_GROUP, SEC_KEY_CMD); + struct iwl_sec_key_cmd cmd = { + .action = cpu_to_le32(FW_CTXT_ACTION_ADD), + .u.add.sta_mask = cpu_to_le32(sta_mask), + .u.add.key_id = cpu_to_le32(keyconf->keyidx), + .u.add.key_flags = cpu_to_le32(key_flags), + .u.add.tx_seq = cpu_to_le64(atomic64_read(&keyconf->tx_pn)), + }; + int ret; + + if (WARN_ON(keyconf->keylen > sizeof(cmd.u.add.key))) + return -EINVAL; + + if (keyconf->cipher == WLAN_CIPHER_SUITE_WEP40 || + keyconf->cipher == WLAN_CIPHER_SUITE_WEP104) + memcpy(cmd.u.add.key + IWL_SEC_WEP_KEY_OFFSET, keyconf->key, + keyconf->keylen); + else + memcpy(cmd.u.add.key, keyconf->key, keyconf->keylen); + + if (keyconf->cipher == WLAN_CIPHER_SUITE_TKIP) { + memcpy(cmd.u.add.tkip_mic_rx_key, + keyconf->key + NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY, + 8); + memcpy(cmd.u.add.tkip_mic_tx_key, + keyconf->key + NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY, + 8); + } + + ret = iwl_mvm_send_cmd_pdu(mvm, cmd_id, 0, sizeof(cmd), &cmd); + if (ret) + return ret; + + /* + * For WEP, the same key is used for multicast and unicast so need to + * upload it again. If this fails, remove the original as well. + */ + if (keyconf->cipher == WLAN_CIPHER_SUITE_WEP40 || + keyconf->cipher == WLAN_CIPHER_SUITE_WEP104) { + cmd.u.add.key_flags ^= cpu_to_le32(IWL_SEC_KEY_FLAG_MCAST_KEY); + ret = iwl_mvm_send_cmd_pdu(mvm, cmd_id, 0, sizeof(cmd), &cmd); + if (ret) + __iwl_mvm_sec_key_del(mvm, sta_mask, key_flags, + keyconf->keyidx, 0); + } + + return ret; +} + +static int _iwl_mvm_sec_key_del(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct ieee80211_key_conf *keyconf, + u32 flags) +{ + u32 sta_mask = iwl_mvm_get_sec_sta_mask(mvm, vif, sta, keyconf); + u32 key_flags = iwl_mvm_get_sec_flags(mvm, vif, sta, keyconf); + int ret; + + ret = __iwl_mvm_sec_key_del(mvm, sta_mask, key_flags, keyconf->keyidx, + flags); + if (ret) + return ret; + + /* For WEP, delete the key again as unicast */ + if (keyconf->cipher == WLAN_CIPHER_SUITE_WEP40 || + keyconf->cipher == WLAN_CIPHER_SUITE_WEP104) { + key_flags ^= IWL_SEC_KEY_FLAG_MCAST_KEY; + ret = __iwl_mvm_sec_key_del(mvm, sta_mask, key_flags, + keyconf->keyidx, flags); + } + + return ret; +} + +int iwl_mvm_sec_key_del(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct ieee80211_key_conf *keyconf) +{ + return _iwl_mvm_sec_key_del(mvm, vif, sta, keyconf, 0); +} + +static void iwl_mvm_sec_key_remove_ap_iter(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct ieee80211_key_conf *key, + void *data) +{ + struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); + + if (key->hw_key_idx == STA_KEY_IDX_INVALID) + return; + + if (sta) + return; + + _iwl_mvm_sec_key_del(mvm, vif, NULL, key, CMD_ASYNC); + key->hw_key_idx = STA_KEY_IDX_INVALID; +} + +void iwl_mvm_sec_key_remove_ap(struct iwl_mvm *mvm, + struct ieee80211_vif *vif) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + u32 sec_key_id = WIDE_ID(DATA_PATH_GROUP, SEC_KEY_CMD); + u8 sec_key_ver = iwl_fw_lookup_cmd_ver(mvm->fw, sec_key_id, 0); + + if (WARN_ON(vif->type != NL80211_IFTYPE_STATION || + mvmvif->ap_sta_id == IWL_MVM_INVALID_STA)) + return; + + if (!sec_key_ver) + return; + + ieee80211_iter_keys_rcu(mvm->hw, vif, + iwl_mvm_sec_key_remove_ap_iter, + NULL); +} diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h index 1ccb3cad7cdc..19d7a4f4ccdc 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h @@ -2079,6 +2079,18 @@ void iwl_mvm_sta_add_debugfs(struct ieee80211_hw *hw, struct dentry *dir); #endif +/* new MLD related APIs */ +int iwl_mvm_sec_key_add(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct ieee80211_key_conf *keyconf); +int iwl_mvm_sec_key_del(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct ieee80211_key_conf *keyconf); +void iwl_mvm_sec_key_remove_ap(struct iwl_mvm *mvm, + struct ieee80211_vif *vif); + int iwl_rfi_send_config_cmd(struct iwl_mvm *mvm, struct iwl_rfi_lut_entry *rfi_table); struct iwl_rfi_freq_table_resp_cmd *iwl_rfi_get_freq_table(struct iwl_mvm *mvm); diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c index 5b8e9a06f6d4..9699433137bc 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c @@ -547,6 +547,7 @@ static const struct iwl_hcmd_names iwl_mvm_data_path_names[] = { HCMD_NAME(TLC_MNG_CONFIG_CMD), HCMD_NAME(CHEST_COLLECTOR_FILTER_CONFIG_CMD), HCMD_NAME(SCD_QUEUE_CONFIG_CMD), + HCMD_NAME(SEC_KEY_CMD), HCMD_NAME(MONITOR_NOTIF), HCMD_NAME(THERMAL_DUAL_CHAIN_REQUEST), HCMD_NAME(STA_PM_NOTIF), diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/sta.c b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c index cbd8053a9e35..515dd3e0730d 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/sta.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c @@ -1954,6 +1954,9 @@ int iwl_mvm_rm_sta(struct iwl_mvm *mvm, if (vif->cfg.assoc) return ret; + /* first remove remaining keys */ + iwl_mvm_sec_key_remove_ap(mvm, vif); + /* unassoc - go ahead - remove the AP STA now */ mvmvif->ap_sta_id = IWL_MVM_INVALID_STA; } From ef2e7a51095012673b9b72dabe343333f537c826 Mon Sep 17 00:00:00 2001 From: Ilan Peer Date: Wed, 2 Nov 2022 16:59:54 +0200 Subject: [PATCH 0807/2002] wifi: iwlwifi: mvm: Fix getting the lowest rate When setting the rate for Tx, the code tried to get the lowest allowed rate but without considering the BSS basic rates. Fix this by considering the basic rates. In addition, declare support for configuring beacon Tx rate and when configured use the configured Tx beacon rate to set beacon tx command rate. Signed-off-by: Ilan Peer Signed-off-by: Gregory Greenman Link: https://lore.kernel.org/r/20221102165239.be8403f0d5c0.I7d141646746b96310efd75fc77ca9aebc61aefcc@changeid --- .../net/wireless/intel/iwlwifi/mvm/debugfs.c | 4 +- .../net/wireless/intel/iwlwifi/mvm/mac-ctxt.c | 60 ++++++++++++++++--- .../net/wireless/intel/iwlwifi/mvm/mac80211.c | 3 + drivers/net/wireless/intel/iwlwifi/mvm/mvm.h | 3 +- 4 files changed, 59 insertions(+), 11 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c b/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c index 1e8123140973..1ce9450e5add 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2012-2014, 2018-2021 Intel Corporation + * Copyright (C) 2012-2014, 2018-2022 Intel Corporation * Copyright (C) 2013-2015 Intel Mobile Communications GmbH * Copyright (C) 2016-2017 Intel Deutschland GmbH */ @@ -1248,7 +1248,7 @@ static int _iwl_dbgfs_inject_beacon_ie(struct iwl_mvm *mvm, char *bin, int len) mvmvif = iwl_mvm_vif_from_mac80211(vif); info = IEEE80211_SKB_CB(beacon); - rate = iwl_mvm_mac_ctxt_get_lowest_rate(info, vif); + rate = iwl_mvm_mac_ctxt_get_beacon_rate(mvm, info, vif); beacon_cmd.flags = cpu_to_le16(iwl_mvm_mac_ctxt_get_beacon_flags(mvm->fw, rate)); diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c index de0c545d50fd..83abfe996138 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c @@ -788,14 +788,40 @@ static u32 iwl_mvm_find_ie_offset(u8 *beacon, u8 eid, u32 frame_size) return ie - beacon; } -u8 iwl_mvm_mac_ctxt_get_lowest_rate(struct ieee80211_tx_info *info, - struct ieee80211_vif *vif) +static u8 iwl_mvm_mac_ctxt_get_lowest_rate(struct iwl_mvm *mvm, + struct ieee80211_tx_info *info, + struct ieee80211_vif *vif) { + struct ieee80211_supported_band *sband; + unsigned long basic = vif->bss_conf.basic_rates; + u16 lowest_cck = IWL_RATE_COUNT, lowest_ofdm = IWL_RATE_COUNT; u8 rate; - if (info->band == NL80211_BAND_2GHZ && !vif->p2p) - rate = IWL_FIRST_CCK_RATE; - else - rate = IWL_FIRST_OFDM_RATE; + u32 i; + + sband = mvm->hw->wiphy->bands[info->band]; + for_each_set_bit(i, &basic, BITS_PER_LONG) { + u16 hw = sband->bitrates[i].hw_value; + + if (hw >= IWL_FIRST_OFDM_RATE) { + if (lowest_ofdm > hw) + lowest_ofdm = hw; + } else if (lowest_cck > hw) { + lowest_cck = hw; + } + } + + if (info->band == NL80211_BAND_2GHZ && !vif->p2p) { + if (lowest_cck != IWL_RATE_COUNT) + rate = lowest_cck; + else if (lowest_ofdm != IWL_RATE_COUNT) + rate = lowest_ofdm; + else + rate = IWL_RATE_1M_INDEX; + } else if (lowest_ofdm != IWL_RATE_COUNT) { + rate = lowest_ofdm; + } else { + rate = IWL_RATE_6M_INDEX; + } return rate; } @@ -812,6 +838,24 @@ u16 iwl_mvm_mac_ctxt_get_beacon_flags(const struct iwl_fw *fw, u8 rate_idx) return flags; } +u8 iwl_mvm_mac_ctxt_get_beacon_rate(struct iwl_mvm *mvm, + struct ieee80211_tx_info *info, + struct ieee80211_vif *vif) +{ + struct ieee80211_supported_band *sband = + mvm->hw->wiphy->bands[info->band]; + u32 legacy = vif->bss_conf.beacon_tx_rate.control[info->band].legacy; + + /* if beacon rate was configured try using it */ + if (hweight32(legacy) == 1) { + u32 rate = ffs(legacy) - 1; + + return sband->bitrates[rate].hw_value; + } + + return iwl_mvm_mac_ctxt_get_lowest_rate(mvm, info, vif); +} + static void iwl_mvm_mac_ctxt_set_tx(struct iwl_mvm *mvm, struct ieee80211_vif *vif, struct sk_buff *beacon, @@ -842,7 +886,7 @@ static void iwl_mvm_mac_ctxt_set_tx(struct iwl_mvm *mvm, cpu_to_le32(BIT(mvm->mgmt_last_antenna_idx) << RATE_MCS_ANT_POS); - rate = iwl_mvm_mac_ctxt_get_lowest_rate(info, vif); + rate = iwl_mvm_mac_ctxt_get_beacon_rate(mvm, info, vif); tx->rate_n_flags |= cpu_to_le32(iwl_mvm_mac80211_idx_to_hwrate(mvm->fw, rate)); @@ -926,7 +970,7 @@ static int iwl_mvm_mac_ctxt_send_beacon_v9(struct iwl_mvm *mvm, struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); struct ieee80211_tx_info *info = IEEE80211_SKB_CB(beacon); struct iwl_mac_beacon_cmd beacon_cmd = {}; - u8 rate = iwl_mvm_mac_ctxt_get_lowest_rate(info, vif); + u8 rate = iwl_mvm_mac_ctxt_get_beacon_rate(mvm, info, vif); u16 flags; struct ieee80211_chanctx_conf *ctx; int channel; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c index e923e9a75c92..a81dd488caa8 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c @@ -374,6 +374,9 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm) hw->wiphy->n_cipher_suites++; } + wiphy_ext_feature_set(hw->wiphy, + NL80211_EXT_FEATURE_BEACON_RATE_LEGACY); + if (fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_FTM_CALIBRATED)) { wiphy_ext_feature_set(hw->wiphy, diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h index 19d7a4f4ccdc..962e304fc2b1 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h @@ -1644,7 +1644,8 @@ int iwl_mvm_mac_ctxt_send_beacon(struct iwl_mvm *mvm, int iwl_mvm_mac_ctxt_send_beacon_cmd(struct iwl_mvm *mvm, struct sk_buff *beacon, void *data, int len); -u8 iwl_mvm_mac_ctxt_get_lowest_rate(struct ieee80211_tx_info *info, +u8 iwl_mvm_mac_ctxt_get_beacon_rate(struct iwl_mvm *mvm, + struct ieee80211_tx_info *info, struct ieee80211_vif *vif); u16 iwl_mvm_mac_ctxt_get_beacon_flags(const struct iwl_fw *fw, u8 rate_idx); From 733eb54f62c6f07938c83cac6ef69afc28ca7e6c Mon Sep 17 00:00:00 2001 From: Avraham Stern Date: Wed, 2 Nov 2022 16:59:55 +0200 Subject: [PATCH 0808/2002] wifi: iwlwifi: mei: implement PLDR flow If the FW needs to do OTP re-read, the driver must notify CSME before loading the FW so CSME will not try to access the NIC during the re-read. Once the alive notification is received, CSME is notified that NIC access is allowed again. Signed-off-by: Avraham Stern Signed-off-by: Gregory Greenman Link: https://lore.kernel.org/r/20221102165239.49eb8c6d455f.I7f0a5debb2d3d662a4151199bbec24613f324c13@changeid --- drivers/net/wireless/intel/iwlwifi/iwl-prph.h | 4 + .../net/wireless/intel/iwlwifi/mei/iwl-mei.h | 21 ++++ drivers/net/wireless/intel/iwlwifi/mei/main.c | 100 ++++++++++++++++++ drivers/net/wireless/intel/iwlwifi/mei/sap.h | 51 +++++++++ drivers/net/wireless/intel/iwlwifi/mvm/fw.c | 7 ++ 5 files changed, 183 insertions(+) diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-prph.h b/drivers/net/wireless/intel/iwlwifi/iwl-prph.h index 157d1f31c487..82cf904e0b6d 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-prph.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-prph.h @@ -377,6 +377,7 @@ enum { #define PREG_PRPH_WPROT_22000 0xA04D00 #define SB_MODIFY_CFG_FLAG 0xA03088 +#define SB_CFG_RESIDES_IN_OTP_MASK 0x10 #define SB_CPU_1_STATUS 0xA01E30 #define SB_CPU_2_STATUS 0xA01E34 #define UMAG_SB_CPU_1_STATUS 0xA038C0 @@ -500,4 +501,7 @@ enum { #define REG_OTP_MINOR 0xA0333C +#define WFPM_LMAC2_PD_NOTIFICATION 0xA033CC +#define WFPM_LMAC2_PD_RE_READ BIT(31) + #endif /* __iwl_prph_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/mei/iwl-mei.h b/drivers/net/wireless/intel/iwlwifi/mei/iwl-mei.h index 2e57438a70f0..2b639eef595d 100644 --- a/drivers/net/wireless/intel/iwlwifi/mei/iwl-mei.h +++ b/drivers/net/wireless/intel/iwlwifi/mei/iwl-mei.h @@ -453,6 +453,21 @@ void iwl_mei_host_disassociated(void); */ void iwl_mei_device_state(bool up); +/** + * iwl_mei_pldr_req() - must be called before loading the fw + * + * Return: 0 if the PLDR flow was successful and the fw can be loaded, negative + * value otherwise. + */ +int iwl_mei_pldr_req(void); + +/** + * iwl_mei_alive_notif() - must be called when alive notificaiton is received + * @success: true if received alive notification, false if waiting for the + * notificaiton timed out. + */ +void iwl_mei_alive_notif(bool success); + #else static inline bool iwl_mei_is_connected(void) @@ -503,6 +518,12 @@ static inline void iwl_mei_host_disassociated(void) static inline void iwl_mei_device_state(bool up) {} +static inline int iwl_mei_pldr_req(void) +{ return 0; } + +static inline void iwl_mei_alive_notif(bool success) +{} + #endif /* CONFIG_IWLMEI */ #endif /* __iwl_mei_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/mei/main.c b/drivers/net/wireless/intel/iwlwifi/mei/main.c index c0142093c768..a467da8b2aed 100644 --- a/drivers/net/wireless/intel/iwlwifi/mei/main.c +++ b/drivers/net/wireless/intel/iwlwifi/mei/main.c @@ -150,6 +150,8 @@ struct iwl_mei_filters { * @device_down: true if the device is down. Used to remember to send * CSME_OWNERSHIP_CONFIRMED when the driver is already down. * @csa_throttle_end_wk: used when &csa_throttled is true + * @pldr_wq: the wait queue for PLDR flow + * @pldr_active: PLDR flow is in progress * @data_q_lock: protects the access to the data queues which are * accessed without the mutex. * @netdev_work: used to defer registering and unregistering of the netdev to @@ -173,6 +175,8 @@ struct iwl_mei { bool link_prot_state; bool device_down; struct delayed_work csa_throttle_end_wk; + wait_queue_head_t pldr_wq; + bool pldr_active; spinlock_t data_q_lock; struct work_struct netdev_work; @@ -881,6 +885,15 @@ static void iwl_mei_handle_rx_host_own_req(struct mei_cl_device *cldev, iwl_mei_cache.ops->rfkill(iwl_mei_cache.priv, false); } +static void iwl_mei_handle_pldr_ack(struct mei_cl_device *cldev, + const struct iwl_sap_pldr_ack_data *ack) +{ + struct iwl_mei *mei = mei_cldev_get_drvdata(cldev); + + mei->pldr_active = le32_to_cpu(ack->status) == SAP_PLDR_STATUS_SUCCESS; + wake_up_all(&mei->pldr_wq); +} + static void iwl_mei_handle_ping(struct mei_cl_device *cldev, const struct iwl_sap_hdr *hdr) { @@ -961,6 +974,8 @@ static void iwl_mei_handle_sap_msg(struct mei_cl_device *cldev, iwl_mei_handle_can_release_ownership, 0); SAP_MSG_HANDLER(CSME_TAKING_OWNERSHIP, iwl_mei_handle_csme_taking_ownership, 0); + SAP_MSG_HANDLER(PLDR_ACK, iwl_mei_handle_pldr_ack, + sizeof(struct iwl_sap_pldr_ack_data)); default: /* * This is not really an error, there are message that we decided @@ -1337,6 +1352,62 @@ out: } EXPORT_SYMBOL_GPL(iwl_mei_get_nvm); +#define IWL_MEI_PLDR_NUM_RETRIES 3 + +int iwl_mei_pldr_req(void) +{ + struct iwl_mei *mei; + int ret; + struct iwl_sap_pldr_data msg = { + .hdr.type = cpu_to_le16(SAP_MSG_NOTIF_PLDR), + .hdr.len = cpu_to_le16(sizeof(msg) - sizeof(msg.hdr)), + }; + int i; + + mutex_lock(&iwl_mei_mutex); + + /* In case we didn't have a bind */ + if (!iwl_mei_is_connected()) { + ret = 0; + goto out; + } + + mei = mei_cldev_get_drvdata(iwl_mei_global_cldev); + + if (!mei) { + ret = -ENODEV; + goto out; + } + + if (!mei->amt_enabled) { + ret = 0; + goto out; + } + + for (i = 0; i < IWL_MEI_PLDR_NUM_RETRIES; i++) { + ret = iwl_mei_send_sap_msg_payload(mei->cldev, &msg.hdr); + mutex_unlock(&iwl_mei_mutex); + if (ret) + return ret; + + ret = wait_event_timeout(mei->pldr_wq, mei->pldr_active, HZ / 2); + if (ret) + break; + + /* Take the mutex for the next iteration */ + mutex_lock(&iwl_mei_mutex); + } + + if (ret) + return 0; + + ret = -ETIMEDOUT; +out: + mutex_unlock(&iwl_mei_mutex); + return ret; +} +EXPORT_SYMBOL_GPL(iwl_mei_pldr_req); + int iwl_mei_get_ownership(void) { struct iwl_mei *mei; @@ -1402,6 +1473,33 @@ out: } EXPORT_SYMBOL_GPL(iwl_mei_get_ownership); +void iwl_mei_alive_notif(bool success) +{ + struct iwl_mei *mei; + struct iwl_sap_pldr_end_data msg = { + .hdr.type = cpu_to_le16(SAP_MSG_NOTIF_PLDR_END), + .hdr.len = cpu_to_le16(sizeof(msg) - sizeof(msg.hdr)), + .status = success ? cpu_to_le32(SAP_PLDR_STATUS_SUCCESS) : + cpu_to_le32(SAP_PLDR_STATUS_FAILURE), + }; + + mutex_lock(&iwl_mei_mutex); + + if (!iwl_mei_is_connected()) + goto out; + + mei = mei_cldev_get_drvdata(iwl_mei_global_cldev); + if (!mei || !mei->pldr_active) + goto out; + + mei->pldr_active = false; + + iwl_mei_send_sap_msg_payload(mei->cldev, &msg.hdr); +out: + mutex_unlock(&iwl_mei_mutex); +} +EXPORT_SYMBOL_GPL(iwl_mei_alive_notif); + void iwl_mei_host_associated(const struct iwl_mei_conn_info *conn_info, const struct iwl_mei_colloc_info *colloc_info) { @@ -1841,6 +1939,7 @@ static int iwl_mei_probe(struct mei_cl_device *cldev, INIT_DELAYED_WORK(&mei->csa_throttle_end_wk, iwl_mei_csa_throttle_end_wk); init_waitqueue_head(&mei->get_ownership_wq); + init_waitqueue_head(&mei->pldr_wq); spin_lock_init(&mei->data_q_lock); INIT_WORK(&mei->netdev_work, iwl_mei_netdev_work); @@ -2013,6 +2112,7 @@ static void iwl_mei_remove(struct mei_cl_device *cldev) * the device. */ wake_up_all(&mei->get_ownership_wq); + wake_up_all(&mei->pldr_wq); mutex_lock(&iwl_mei_mutex); diff --git a/drivers/net/wireless/intel/iwlwifi/mei/sap.h b/drivers/net/wireless/intel/iwlwifi/mei/sap.h index ef2664589fc1..6c0ad4adbf32 100644 --- a/drivers/net/wireless/intel/iwlwifi/mei/sap.h +++ b/drivers/net/wireless/intel/iwlwifi/mei/sap.h @@ -203,6 +203,7 @@ struct iwl_sap_me_msg_start_ok { * @SAP_MSG_NOTIF_NIC_OWNER: Payload is a DW. See &enum iwl_sap_nic_owner. * @SAP_MSG_NOTIF_CSME_CONN_STATUS: See &struct iwl_sap_notif_conn_status. * @SAP_MSG_NOTIF_NVM: See &struct iwl_sap_nvm. + * @SAP_MSG_NOTIF_PLDR_ACK: See &struct iwl_sap_pldr_ack_data. * @SAP_MSG_NOTIF_FROM_CSME_MAX: Not used. * * @SAP_MSG_NOTIF_FROM_HOST_MIN: Not used. @@ -226,6 +227,8 @@ struct iwl_sap_me_msg_start_ok { * @SAP_MSG_NOTIF_HOST_OWNERSHIP_CONFIRMED: No payload. * @SAP_MSG_NOTIF_SAR_LIMITS: See &struct iwl_sap_notif_sar_limits. * @SAP_MSG_NOTIF_GET_NVM: No payload. Triggers %SAP_MSG_NOTIF_NVM. + * @SAP_MSG_NOTIF_PLDR: See &struct iwl_sap_pldr_data. + * @SAP_MSG_NOTIF_PLDR_END: See &struct iwl_sap_pldr_end_data. * @SAP_MSG_NOTIF_FROM_HOST_MAX: Not used. * * @SAP_MSG_DATA_MIN: Not used. @@ -258,6 +261,8 @@ enum iwl_sap_msg { SAP_MSG_NOTIF_NIC_OWNER = 511, SAP_MSG_NOTIF_CSME_CONN_STATUS = 512, SAP_MSG_NOTIF_NVM = 513, + /* 514 - 517 not supported */ + SAP_MSG_NOTIF_PLDR_ACK = 518, SAP_MSG_NOTIF_FROM_CSME_MAX, SAP_MSG_NOTIF_FROM_HOST_MIN = 1000, @@ -279,6 +284,9 @@ enum iwl_sap_msg { SAP_MSG_NOTIF_HOST_OWNERSHIP_CONFIRMED = 1015, SAP_MSG_NOTIF_SAR_LIMITS = 1016, SAP_MSG_NOTIF_GET_NVM = 1017, + /* 1018 - 1023 not supported */ + SAP_MSG_NOTIF_PLDR = 1024, + SAP_MSG_NOTIF_PLDR_END = 1025, SAP_MSG_NOTIF_FROM_HOST_MAX, SAP_MSG_DATA_MIN = 2000, @@ -732,4 +740,47 @@ struct iwl_sap_cb_data { u8 payload[]; }; +/** + * struct iwl_sap_pldr_data - payload of %SAP_MSG_NOTIF_PLDR + * @hdr: The SAP header. + * @version: SAP message version + */ +struct iwl_sap_pldr_data { + struct iwl_sap_hdr hdr; + __le32 version; +} __packed; + +/** + * enum iwl_sap_pldr_status - + * @SAP_PLDR_STATUS_SUCCESS: PLDR started/ended successfully + * @SAP_PLDR_STATUS_FAILURE: PLDR failed to start/end + */ +enum iwl_sap_pldr_status { + SAP_PLDR_STATUS_SUCCESS = 0, + SAP_PLDR_STATUS_FAILURE = 1, +}; + +/* + * struct iwl_sap_pldr_end_data - payload of %SAP_MSG_NOTIF_PLDR_END + * @hdr: The SAP header. + * @version: SAP message version + * @status: PLDR end status + */ +struct iwl_sap_pldr_end_data { + struct iwl_sap_hdr hdr; + __le32 version; + __le32 status; +} __packed; + +/* + * struct iwl_sap_pldr_ack_data - payload of %SAP_MSG_NOTIF_PLDR_ACK + * @version: SAP message version + * @status: CSME accept/refuse to the PLDR request + */ +struct iwl_sap_pldr_ack_data { + struct iwl_sap_hdr hdr; + __le32 version; + __le32 status; +} __packed; + #endif /* __sap_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c index 5de34edc51fe..ef43f6971cd9 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c @@ -404,6 +404,8 @@ static int iwl_mvm_load_ucode_wait_alive(struct iwl_mvm *mvm, return -EIO; } + iwl_mei_alive_notif(!ret); + ret = iwl_pnvm_load(mvm->trans, &mvm->notif_wait); if (ret) { IWL_ERR(mvm, "Timeout waiting for PNVM load!\n"); @@ -1456,6 +1458,7 @@ int iwl_mvm_up(struct iwl_mvm *mvm) struct ieee80211_channel *chan; struct cfg80211_chan_def chandef; struct ieee80211_supported_band *sband = NULL; + u32 sb_cfg; lockdep_assert_held(&mvm->mutex); @@ -1463,6 +1466,10 @@ int iwl_mvm_up(struct iwl_mvm *mvm) if (ret) return ret; + sb_cfg = iwl_read_umac_prph(mvm->trans, SB_MODIFY_CFG_FLAG); + if (!(sb_cfg & SB_CFG_RESIDES_IN_OTP_MASK) && iwl_mei_pldr_req()) + return ret; + ret = iwl_mvm_load_rt_fw(mvm); if (ret) { IWL_ERR(mvm, "Failed to start RT ucode: %d\n", ret); From 6564f3b8621e380114732644be4c65064361eb6f Mon Sep 17 00:00:00 2001 From: Avraham Stern Date: Wed, 2 Nov 2022 16:59:56 +0200 Subject: [PATCH 0809/2002] wifi: iwlwifi: mei: use wait_event_timeout() return value wait_event_timeout() return value indicates whether the condition evaluated to true or not, so no need to re-take the lock and check the got_ownership flag. Signed-off-by: Avraham Stern Signed-off-by: Gregory Greenman Link: https://lore.kernel.org/r/20221102165239.33159054626f.Ief9e2dc42f96f8044c197d32172003a5ead0f8d3@changeid --- drivers/net/wireless/intel/iwlwifi/mei/main.c | 21 +------------------ 1 file changed, 1 insertion(+), 20 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mei/main.c b/drivers/net/wireless/intel/iwlwifi/mei/main.c index a467da8b2aed..ef5d2938deb5 100644 --- a/drivers/net/wireless/intel/iwlwifi/mei/main.c +++ b/drivers/net/wireless/intel/iwlwifi/mei/main.c @@ -1447,26 +1447,7 @@ int iwl_mei_get_ownership(void) ret = wait_event_timeout(mei->get_ownership_wq, mei->got_ownership, HZ / 2); - if (!ret) - return -ETIMEDOUT; - - mutex_lock(&iwl_mei_mutex); - - /* In case we didn't have a bind */ - if (!iwl_mei_is_connected()) { - ret = 0; - goto out; - } - - mei = mei_cldev_get_drvdata(iwl_mei_global_cldev); - - if (!mei) { - ret = -ENODEV; - goto out; - } - - ret = !mei->got_ownership; - + return (!ret) ? -ETIMEDOUT : 0; out: mutex_unlock(&iwl_mei_mutex); return ret; From c4bb943e0ad5829ec387e30bcb4b30cb15a572b9 Mon Sep 17 00:00:00 2001 From: Avraham Stern Date: Wed, 2 Nov 2022 16:59:57 +0200 Subject: [PATCH 0810/2002] wifi: iwlwifi: iwlmei: report disconnection as temporary Disconnections are reported to CSME as long link down. As a result, CSME will take ownership right away so the host doesn't have a chance to reconnect, although in many cases it could quickly reconnect and preserve host connectivity. Report disconnections as temporary so CSME will give the host some grace time to reconnect. Signed-off-by: Avraham Stern Signed-off-by: Gregory Greenman Link: https://lore.kernel.org/r/20221102165239.401665457652.Ie5de9eacc5a51cf0965a08c202caf3fbc3a91ec4@changeid --- drivers/net/wireless/intel/iwlwifi/mei/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mei/main.c b/drivers/net/wireless/intel/iwlwifi/mei/main.c index ef5d2938deb5..9a49361cd059 100644 --- a/drivers/net/wireless/intel/iwlwifi/mei/main.c +++ b/drivers/net/wireless/intel/iwlwifi/mei/main.c @@ -1535,7 +1535,7 @@ void iwl_mei_host_disassociated(void) struct iwl_sap_notif_host_link_down msg = { .hdr.type = cpu_to_le16(SAP_MSG_NOTIF_HOST_LINK_DOWN), .hdr.len = cpu_to_le16(sizeof(msg) - sizeof(msg.hdr)), - .type = HOST_LINK_DOWN_TYPE_LONG, + .type = HOST_LINK_DOWN_TYPE_TEMPORARY, }; mutex_lock(&iwl_mei_mutex); From b0b9b80599a30ee8862c0294b8a3b69c9c6df4f9 Mon Sep 17 00:00:00 2001 From: Avraham Stern Date: Wed, 2 Nov 2022 16:59:58 +0200 Subject: [PATCH 0811/2002] wifi: iwlwifi: mei: wait for the mac to stop on suspend When iwlmei driver is removed, it removes all the interfaces since CSME will take over the NIC while there is no SAP connection. When the iwlmei driver is removed due to the host being suspended, this results in the interface being down after resume since the interface was removed on suspend so mac80211 will not try to bring it up on resume. Since on suspend the mac is stopped anyway by mac80211, there is no need to remove the interface in this case. Wait for the mac to stop instead. Once the mac is stopped, the driver will not access the NIC anymore so it is safe for CSME to take over. Signed-off-by: Avraham Stern Signed-off-by: Gregory Greenman Link: https://lore.kernel.org/r/20221102165239.371102f04586.I813a964607b3e92ea275a0cf56df57227c11ae88@changeid --- drivers/net/wireless/intel/iwlwifi/mei/main.c | 23 +++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mei/main.c b/drivers/net/wireless/intel/iwlwifi/mei/main.c index 9a49361cd059..b89989b6399a 100644 --- a/drivers/net/wireless/intel/iwlwifi/mei/main.c +++ b/drivers/net/wireless/intel/iwlwifi/mei/main.c @@ -1990,6 +1990,7 @@ free: } #define SEND_SAP_MAX_WAIT_ITERATION 10 +#define IWLMEI_DEVICE_DOWN_WAIT_ITERATION 50 static void iwl_mei_remove(struct mei_cl_device *cldev) { @@ -2000,8 +2001,26 @@ static void iwl_mei_remove(struct mei_cl_device *cldev) * We are being removed while the bus is active, it means we are * going to suspend/ shutdown, so the NIC will disappear. */ - if (mei_cldev_enabled(cldev) && iwl_mei_cache.ops) - iwl_mei_cache.ops->nic_stolen(iwl_mei_cache.priv); + if (mei_cldev_enabled(cldev) && iwl_mei_cache.ops) { + unsigned int iter = IWLMEI_DEVICE_DOWN_WAIT_ITERATION; + bool down = false; + + /* + * In case of suspend, wait for the mac to stop and don't remove + * the interface. This will allow the interface to come back + * on resume. + */ + while (!down && iter--) { + mdelay(1); + + mutex_lock(&iwl_mei_mutex); + down = mei->device_down; + mutex_unlock(&iwl_mei_mutex); + } + + if (!down) + iwl_mei_cache.ops->nic_stolen(iwl_mei_cache.priv); + } if (rcu_access_pointer(iwl_mei_cache.netdev)) { struct net_device *dev; From 0c9ef08a4d0fd6c5e6000597b506235d71a85a61 Mon Sep 17 00:00:00 2001 From: Nathan Huckleberry Date: Tue, 8 Nov 2022 17:26:30 -0700 Subject: [PATCH 0812/2002] net: mana: Fix return type of mana_start_xmit() The ndo_start_xmit field in net_device_ops is expected to be of type netdev_tx_t (*ndo_start_xmit)(struct sk_buff *skb, struct net_device *dev). The mismatched return type breaks forward edge kCFI since the underlying function definition does not match the function hook definition. A new warning in clang will catch this at compile time: drivers/net/ethernet/microsoft/mana/mana_en.c:382:21: error: incompatible function pointer types initializing 'netdev_tx_t (*)(struct sk_buff *, struct net_device *)' (aka 'enum netdev_tx (*)(struct sk_buff *, struct net_device *)') with an expression of type 'int (struct sk_buff *, struct net_device *)' [-Werror,-Wincompatible-function-pointer-types-strict] .ndo_start_xmit = mana_start_xmit, ^~~~~~~~~~~~~~~ 1 error generated. The return type of mana_start_xmit should be changed from int to netdev_tx_t. Reported-by: Dan Carpenter Link: https://github.com/ClangBuiltLinux/linux/issues/1703 Link: https://github.com/ClangBuiltLinux/linux/issues/1750 Signed-off-by: Nathan Huckleberry Reviewed-by: Dexuan Cui [nathan: Rebase on net-next and resolve conflicts Add note about new clang warning] Signed-off-by: Nathan Chancellor Link: https://lore.kernel.org/r/20221109002629.1446680-1-nathan@kernel.org Signed-off-by: Paolo Abeni --- drivers/net/ethernet/microsoft/mana/mana.h | 2 +- drivers/net/ethernet/microsoft/mana/mana_en.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/microsoft/mana/mana.h b/drivers/net/ethernet/microsoft/mana/mana.h index d58be64374c8..ffcd27cde17b 100644 --- a/drivers/net/ethernet/microsoft/mana/mana.h +++ b/drivers/net/ethernet/microsoft/mana/mana.h @@ -388,7 +388,7 @@ struct mana_port_context { struct mana_ethtool_stats eth_stats; }; -int mana_start_xmit(struct sk_buff *skb, struct net_device *ndev); +netdev_tx_t mana_start_xmit(struct sk_buff *skb, struct net_device *ndev); int mana_config_rss(struct mana_port_context *ac, enum TRI_STATE rx, bool update_hash, bool update_tab); diff --git a/drivers/net/ethernet/microsoft/mana/mana_en.c b/drivers/net/ethernet/microsoft/mana/mana_en.c index 318dbbb48279..91a8d3577c14 100644 --- a/drivers/net/ethernet/microsoft/mana/mana_en.c +++ b/drivers/net/ethernet/microsoft/mana/mana_en.c @@ -128,7 +128,7 @@ frag_err: return -ENOMEM; } -int mana_start_xmit(struct sk_buff *skb, struct net_device *ndev) +netdev_tx_t mana_start_xmit(struct sk_buff *skb, struct net_device *ndev) { enum mana_tx_pkt_format pkt_fmt = MANA_SHORT_PKT_FMT; struct mana_port_context *apc = netdev_priv(ndev); From edaf5df22cb8e7e849773ce69fcc9bc20ca92160 Mon Sep 17 00:00:00 2001 From: Vincent Mailhol Date: Tue, 8 Nov 2022 12:57:54 +0900 Subject: [PATCH 0813/2002] ethtool: ethtool_get_drvinfo: populate drvinfo fields even if callback exits If ethtool_ops::get_drvinfo() callback isn't set, ethtool_get_drvinfo() will fill the ethtool_drvinfo::name and ethtool_drvinfo::bus_info fields. However, if the driver provides the callback function, those two fields are not touched. This means that the driver has to fill these itself. Allow the driver to leave those two fields empty and populate them in such case. This way, the driver can rely on the default values for the name and the bus_info. If the driver provides values, do nothing. Signed-off-by: Vincent Mailhol Link: https://lore.kernel.org/r/20221108035754.2143-1-mailhol.vincent@wanadoo.fr Signed-off-by: Jakub Kicinski --- net/ethtool/ioctl.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/net/ethtool/ioctl.c b/net/ethtool/ioctl.c index e40f5e9e109b..99272a67525c 100644 --- a/net/ethtool/ioctl.c +++ b/net/ethtool/ioctl.c @@ -706,15 +706,22 @@ static int ethtool_get_drvinfo(struct net_device *dev, struct ethtool_devlink_compat *rsp) { const struct ethtool_ops *ops = dev->ethtool_ops; + struct device *parent = dev->dev.parent; rsp->info.cmd = ETHTOOL_GDRVINFO; strscpy(rsp->info.version, UTS_RELEASE, sizeof(rsp->info.version)); if (ops->get_drvinfo) { ops->get_drvinfo(dev, &rsp->info); - } else if (dev->dev.parent && dev->dev.parent->driver) { - strscpy(rsp->info.bus_info, dev_name(dev->dev.parent), + if (!rsp->info.bus_info[0] && parent) + strscpy(rsp->info.bus_info, dev_name(parent), + sizeof(rsp->info.bus_info)); + if (!rsp->info.driver[0] && parent && parent->driver) + strscpy(rsp->info.driver, parent->driver->name, + sizeof(rsp->info.driver)); + } else if (parent && parent->driver) { + strscpy(rsp->info.bus_info, dev_name(parent), sizeof(rsp->info.bus_info)); - strscpy(rsp->info.driver, dev->dev.parent->driver->name, + strscpy(rsp->info.driver, parent->driver->name, sizeof(rsp->info.driver)); } else if (dev->rtnl_link_ops) { strscpy(rsp->info.driver, dev->rtnl_link_ops->kind, From 1fb22ed671950bbc0c402034d206fcb334d1fd3a Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Thu, 10 Nov 2022 10:51:50 +0200 Subject: [PATCH 0814/2002] devlink: Fix warning when unregistering a port When a devlink port is unregistered, its type is expected to be unset or otherwise a WARNING is generated [1]. This was supposed to be handled by cited commit by clearing the type upon 'NETDEV_PRE_UNINIT'. The assumption was that no other events can be generated for the netdev after this event, but this proved to be wrong. After the event is generated, netdev_wait_allrefs_any() will rebroadcast a 'NETDEV_UNREGISTER' until the netdev's reference count drops to 1. This causes devlink to set the port type back to Ethernet. Fix by only setting and clearing the port type upon 'NETDEV_POST_INIT' and 'NETDEV_PRE_UNINIT', respectively. For all other events, preserve the port type. [1] WARNING: CPU: 0 PID: 11 at net/core/devlink.c:9998 devl_port_unregister+0x2f6/0x390 net/core/devlink.c:9998 Modules linked in: CPU: 1 PID: 11 Comm: kworker/u4:1 Not tainted 6.1.0-rc3-next-20221107-syzkaller #0 Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 10/26/2022 Workqueue: netns cleanup_net RIP: 0010:devl_port_unregister+0x2f6/0x390 net/core/devlink.c:9998 [...] Call Trace: __nsim_dev_port_del+0x1bb/0x240 drivers/net/netdevsim/dev.c:1433 nsim_dev_port_del_all drivers/net/netdevsim/dev.c:1443 [inline] nsim_dev_reload_destroy+0x171/0x510 drivers/net/netdevsim/dev.c:1660 nsim_dev_reload_down+0x6b/0xd0 drivers/net/netdevsim/dev.c:968 devlink_reload+0x1c2/0x6b0 net/core/devlink.c:4501 devlink_pernet_pre_exit+0x104/0x1c0 net/core/devlink.c:12609 ops_pre_exit_list net/core/net_namespace.c:159 [inline] cleanup_net+0x451/0xb10 net/core/net_namespace.c:594 process_one_work+0x9bf/0x1710 kernel/workqueue.c:2289 worker_thread+0x665/0x1080 kernel/workqueue.c:2436 kthread+0x2e4/0x3a0 kernel/kthread.c:376 ret_from_fork+0x1f/0x30 arch/x86/entry/entry_64.S:308 Fixes: 02a68a47eade ("net: devlink: track netdev with devlink_port assigned") Reported-by: syzbot+85e47e1a08b3e159b159@syzkaller.appspotmail.com Reported-by: syzbot+c2ca18f0fccdd1f09c66@syzkaller.appspotmail.com Reviewed-by: Jiri Pirko Signed-off-by: Ido Schimmel Link: https://lore.kernel.org/r/20221110085150.520800-1-idosch@nvidia.com Signed-off-by: Jakub Kicinski --- net/core/devlink.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/core/devlink.c b/net/core/devlink.c index 6bbe230c4ec5..7f789bbcbbd7 100644 --- a/net/core/devlink.c +++ b/net/core/devlink.c @@ -10177,7 +10177,7 @@ static int devlink_netdevice_event(struct notifier_block *nb, * we take into account netdev pointer appearing in this * namespace. */ - __devlink_port_type_set(devlink_port, DEVLINK_PORT_TYPE_ETH, + __devlink_port_type_set(devlink_port, devlink_port->type, netdev); break; case NETDEV_UNREGISTER: @@ -10185,7 +10185,7 @@ static int devlink_netdevice_event(struct notifier_block *nb, * also during net namespace change so we need to clear * pointer to netdev that is going to another net namespace. */ - __devlink_port_type_set(devlink_port, DEVLINK_PORT_TYPE_ETH, + __devlink_port_type_set(devlink_port, devlink_port->type, NULL); break; case NETDEV_PRE_UNINIT: From c1b05105573b2cd5845921eb0d2caa26e2144a34 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Wed, 9 Nov 2022 10:32:54 -0800 Subject: [PATCH 0815/2002] genetlink: fix single op policy dump when do is present Jonathan reports crashes when running net-next in Meta's fleet. Stats collection uses ethtool -I which does a per-op policy dump to check if stats are supported. We don't initialize the dumpit information if doit succeeds due to evaluation short-circuiting. The crash may look like this: BUG: kernel NULL pointer dereference, address: 0000000000000cc0 RIP: 0010:netlink_policy_dump_add_policy+0x174/0x2a0 ctrl_dumppolicy_start+0x19f/0x2f0 genl_start+0xe7/0x140 Or we may trigger a warning: WARNING: CPU: 1 PID: 785 at net/netlink/policy.c:87 netlink_policy_dump_get_policy_idx+0x79/0x80 RIP: 0010:netlink_policy_dump_get_policy_idx+0x79/0x80 ctrl_dumppolicy_put_op+0x214/0x360 depending on what garbage we pick up from the stack. Reported-by: Jonathan Lemon Fixes: 26588edbef60 ("genetlink: support split policies in ctrl_dumppolicy_put_op()") Reviewed-by: Jacob Keller Tested-by: Leon Romanovsky Link: https://lore.kernel.org/r/20221109183254.554051-1-kuba@kernel.org Signed-off-by: Jakub Kicinski --- net/netlink/genetlink.c | 30 +++++++++++++++++++++--------- 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/net/netlink/genetlink.c b/net/netlink/genetlink.c index 9b7dfc45dd67..600993c80050 100644 --- a/net/netlink/genetlink.c +++ b/net/netlink/genetlink.c @@ -282,6 +282,7 @@ genl_cmd_full_to_split(struct genl_split_ops *op, return 0; } +/* Must make sure that op is initialized to 0 on failure */ static int genl_get_cmd(u32 cmd, u8 flags, const struct genl_family *family, struct genl_split_ops *op) @@ -302,6 +303,21 @@ genl_get_cmd(u32 cmd, u8 flags, const struct genl_family *family, return err; } +/* For policy dumping only, get ops of both do and dump. + * Fail if both are missing, genl_get_cmd() will zero-init in case of failure. + */ +static int +genl_get_cmd_both(u32 cmd, const struct genl_family *family, + struct genl_split_ops *doit, struct genl_split_ops *dumpit) +{ + int err1, err2; + + err1 = genl_get_cmd(cmd, GENL_CMD_CAP_DO, family, doit); + err2 = genl_get_cmd(cmd, GENL_CMD_CAP_DUMP, family, dumpit); + + return err1 && err2 ? -ENOENT : 0; +} + static bool genl_op_iter_init(const struct genl_family *family, struct genl_op_iter *iter) { @@ -1406,10 +1422,10 @@ static int ctrl_dumppolicy_start(struct netlink_callback *cb) ctx->single_op = true; ctx->op = nla_get_u32(tb[CTRL_ATTR_OP]); - if (genl_get_cmd(ctx->op, GENL_CMD_CAP_DO, rt, &doit) && - genl_get_cmd(ctx->op, GENL_CMD_CAP_DUMP, rt, &dump)) { + err = genl_get_cmd_both(ctx->op, rt, &doit, &dump); + if (err) { NL_SET_BAD_ATTR(cb->extack, tb[CTRL_ATTR_OP]); - return -ENOENT; + return err; } if (doit.policy) { @@ -1551,13 +1567,9 @@ static int ctrl_dumppolicy(struct sk_buff *skb, struct netlink_callback *cb) if (ctx->single_op) { struct genl_split_ops doit, dumpit; - if (genl_get_cmd(ctx->op, GENL_CMD_CAP_DO, - ctx->rt, &doit) && - genl_get_cmd(ctx->op, GENL_CMD_CAP_DUMP, - ctx->rt, &dumpit)) { - WARN_ON(1); + if (WARN_ON(genl_get_cmd_both(ctx->op, ctx->rt, + &doit, &dumpit))) return -ENOENT; - } if (ctrl_dumppolicy_put_op(skb, cb, &doit, &dumpit)) return skb->len; From a07b3835b895b0ef772f88c4b7c38e9fed52b1ff Mon Sep 17 00:00:00 2001 From: Cai Huoqing Date: Wed, 9 Nov 2022 15:26:41 +0800 Subject: [PATCH 0816/2002] MAINTAINERS: Update hinic maintainers from orphan HINIC is marked orphan for 14 months from the commit "5cfe5109a1d7", but there are lots of HINIC in use. I have a SP582 NIC (hi1822 inside which is a kind of HINIC SOC), and implement based on hinic driver, and if there are some patches for HINIC, I can test and do some code review. I'm active in linux contribution, if possible, I want to take the hinic maintainership. Add my email here to receive patches. Signed-off-by: Cai Huoqing Signed-off-by: David S. Miller --- MAINTAINERS | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/MAINTAINERS b/MAINTAINERS index eb5fe2fd4d7a..6aea837ff9d4 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -9438,8 +9438,9 @@ F: Documentation/devicetree/bindings/iio/humidity/st,hts221.yaml F: drivers/iio/humidity/hts221* HUAWEI ETHERNET DRIVER +M: Cai Huoqing L: netdev@vger.kernel.org -S: Orphan +S: Maintained F: Documentation/networking/device_drivers/ethernet/huawei/hinic.rst F: drivers/net/ethernet/huawei/hinic/ From eed4f1ddad8c5ad7596b229caec8bd7b477b81ee Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Sat, 5 Nov 2022 23:36:16 +0100 Subject: [PATCH 0817/2002] arm64: dts: mediatek: mt7986: add support for RX Wireless Ethernet Dispatch Similar to TX Wireless Ethernet Dispatch, introduce RX Wireless Ethernet Dispatch to offload traffic received by the wlan interface to lan/wan one. Co-developed-by: Sujuan Chen Signed-off-by: Sujuan Chen Signed-off-by: Lorenzo Bianconi Signed-off-by: David S. Miller --- arch/arm64/boot/dts/mediatek/mt7986a.dtsi | 65 +++++++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/arch/arm64/boot/dts/mediatek/mt7986a.dtsi b/arch/arm64/boot/dts/mediatek/mt7986a.dtsi index 72e0d9722e07..07dee1c0d281 100644 --- a/arch/arm64/boot/dts/mediatek/mt7986a.dtsi +++ b/arch/arm64/boot/dts/mediatek/mt7986a.dtsi @@ -76,6 +76,47 @@ no-map; reg = <0 0x4fc00000 0 0x00100000>; }; + + wo_emi0: wo-emi@4fd00000 { + reg = <0 0x4fd00000 0 0x40000>; + no-map; + }; + + wo_emi1: wo-emi@4fd40000 { + reg = <0 0x4fd40000 0 0x40000>; + no-map; + }; + + wo_ilm0: wo-ilm@151e0000 { + reg = <0 0x151e0000 0 0x8000>; + no-map; + }; + + wo_ilm1: wo-ilm@151f0000 { + reg = <0 0x151f0000 0 0x8000>; + no-map; + }; + + wo_data: wo-data@4fd80000 { + reg = <0 0x4fd80000 0 0x240000>; + no-map; + }; + + wo_dlm0: wo-dlm@151e8000 { + reg = <0 0x151e8000 0 0x2000>; + no-map; + }; + + wo_dlm1: wo-dlm@151f8000 { + reg = <0 0x151f8000 0 0x2000>; + no-map; + }; + + wo_boot: wo-boot@15194000 { + reg = <0 0x15194000 0 0x1000>; + no-map; + }; + }; timer { @@ -240,6 +281,11 @@ reg = <0 0x15010000 0 0x1000>; interrupt-parent = <&gic>; interrupts = ; + memory-region = <&wo_emi0>, <&wo_ilm0>, <&wo_dlm0>, + <&wo_data>, <&wo_boot>; + memory-region-names = "wo-emi", "wo-ilm", "wo-dlm", + "wo-data", "wo-boot"; + mediatek,wo-ccif = <&wo_ccif0>; }; wed1: wed@15011000 { @@ -248,6 +294,25 @@ reg = <0 0x15011000 0 0x1000>; interrupt-parent = <&gic>; interrupts = ; + memory-region = <&wo_emi1>, <&wo_ilm1>, <&wo_dlm1>, + <&wo_data>, <&wo_boot>; + memory-region-names = "wo-emi", "wo-ilm", "wo-dlm", + "wo-data", "wo-boot"; + mediatek,wo-ccif = <&wo_ccif1>; + }; + + wo_ccif0: syscon@151a5000 { + compatible = "mediatek,mt7986-wo-ccif", "syscon"; + reg = <0 0x151a5000 0 0x1000>; + interrupt-parent = <&gic>; + interrupts = ; + }; + + wo_ccif1: syscon@151ad000 { + compatible = "mediatek,mt7986-wo-ccif", "syscon"; + reg = <0 0x151ad000 0 0x1000>; + interrupt-parent = <&gic>; + interrupts = ; }; eth: ethernet@15100000 { From ceb82ac2e745aec252516a9f6bd78f5199f52f46 Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Sat, 5 Nov 2022 23:36:17 +0100 Subject: [PATCH 0818/2002] dt-bindings: net: mediatek: add WED RX binding for MT7986 eth driver Document the binding for the RX Wireless Ethernet Dispatch core on the MT7986 ethernet driver used to offload traffic received by WLAN NIC and forwarded to LAN/WAN one. Signed-off-by: Lorenzo Bianconi Reviewed-by: Rob Herring Signed-off-by: David S. Miller --- .../arm/mediatek/mediatek,mt7622-wed.yaml | 52 +++++++++++++++++++ .../soc/mediatek/mediatek,mt7986-wo-ccif.yaml | 51 ++++++++++++++++++ 2 files changed, 103 insertions(+) create mode 100644 Documentation/devicetree/bindings/soc/mediatek/mediatek,mt7986-wo-ccif.yaml diff --git a/Documentation/devicetree/bindings/arm/mediatek/mediatek,mt7622-wed.yaml b/Documentation/devicetree/bindings/arm/mediatek/mediatek,mt7622-wed.yaml index 84fb0a146b6e..5c223cb063d4 100644 --- a/Documentation/devicetree/bindings/arm/mediatek/mediatek,mt7622-wed.yaml +++ b/Documentation/devicetree/bindings/arm/mediatek/mediatek,mt7622-wed.yaml @@ -29,6 +29,38 @@ properties: interrupts: maxItems: 1 + memory-region: + items: + - description: firmware EMI region + - description: firmware ILM region + - description: firmware DLM region + - description: firmware CPU DATA region + - description: firmware BOOT region + + memory-region-names: + items: + - const: wo-emi + - const: wo-ilm + - const: wo-dlm + - const: wo-data + - const: wo-boot + + mediatek,wo-ccif: + $ref: /schemas/types.yaml#/definitions/phandle + description: mediatek wed-wo controller interface. + +allOf: + - if: + properties: + compatible: + contains: + const: mediatek,mt7622-wed + then: + properties: + memory-region-names: false + memory-region: false + mediatek,wo-ccif: false + required: - compatible - reg @@ -49,3 +81,23 @@ examples: interrupts = ; }; }; + + - | + #include + #include + soc { + #address-cells = <2>; + #size-cells = <2>; + + wed@15010000 { + compatible = "mediatek,mt7986-wed", "syscon"; + reg = <0 0x15010000 0 0x1000>; + interrupts = ; + + memory-region = <&wo_emi>, <&wo_ilm>, <&wo_dlm>, + <&wo_data>, <&wo_boot>; + memory-region-names = "wo-emi", "wo-ilm", "wo-dlm", + "wo-data", "wo-boot"; + mediatek,wo-ccif = <&wo_ccif0>; + }; + }; diff --git a/Documentation/devicetree/bindings/soc/mediatek/mediatek,mt7986-wo-ccif.yaml b/Documentation/devicetree/bindings/soc/mediatek/mediatek,mt7986-wo-ccif.yaml new file mode 100644 index 000000000000..8e6ba2ec8a43 --- /dev/null +++ b/Documentation/devicetree/bindings/soc/mediatek/mediatek,mt7986-wo-ccif.yaml @@ -0,0 +1,51 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/soc/mediatek/mediatek,mt7986-wo-ccif.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: MediaTek Wireless Ethernet Dispatch (WED) WO controller interface for MT7986 + +maintainers: + - Lorenzo Bianconi + - Felix Fietkau + +description: + The MediaTek wo-ccif provides a configuration interface for WED WO + controller used to perfrom offload rx packet processing (e.g. 802.11 + aggregation packet reordering or rx header translation) on MT7986 soc. + +properties: + compatible: + items: + - enum: + - mediatek,mt7986-wo-ccif + - const: syscon + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + +required: + - compatible + - reg + - interrupts + +additionalProperties: false + +examples: + - | + #include + #include + soc { + #address-cells = <2>; + #size-cells = <2>; + + syscon@151a5000 { + compatible = "mediatek,mt7986-wo-ccif", "syscon"; + reg = <0 0x151a5000 0 0x1000>; + interrupts = ; + }; + }; From cc514101a97e3fb48f8617c8f291db798d10d831 Mon Sep 17 00:00:00 2001 From: Sujuan Chen Date: Sat, 5 Nov 2022 23:36:18 +0100 Subject: [PATCH 0819/2002] net: ethernet: mtk_wed: introduce wed mcu support Introduce WED mcu support used to configure WED WO chip. This is a preliminary patch in order to add RX Wireless Ethernet Dispatch available on MT7986 SoC. Tested-by: Daniel Golle Co-developed-by: Lorenzo Bianconi Signed-off-by: Lorenzo Bianconi Signed-off-by: Sujuan Chen Signed-off-by: David S. Miller --- drivers/net/ethernet/mediatek/Makefile | 2 +- drivers/net/ethernet/mediatek/mtk_wed_mcu.c | 359 +++++++++++++++++++ drivers/net/ethernet/mediatek/mtk_wed_regs.h | 1 + drivers/net/ethernet/mediatek/mtk_wed_wo.h | 150 ++++++++ include/linux/soc/mediatek/mtk_wed.h | 29 ++ 5 files changed, 540 insertions(+), 1 deletion(-) create mode 100644 drivers/net/ethernet/mediatek/mtk_wed_mcu.c create mode 100644 drivers/net/ethernet/mediatek/mtk_wed_wo.h diff --git a/drivers/net/ethernet/mediatek/Makefile b/drivers/net/ethernet/mediatek/Makefile index 45ba0970504a..d4bdefa77159 100644 --- a/drivers/net/ethernet/mediatek/Makefile +++ b/drivers/net/ethernet/mediatek/Makefile @@ -5,7 +5,7 @@ obj-$(CONFIG_NET_MEDIATEK_SOC) += mtk_eth.o mtk_eth-y := mtk_eth_soc.o mtk_sgmii.o mtk_eth_path.o mtk_ppe.o mtk_ppe_debugfs.o mtk_ppe_offload.o -mtk_eth-$(CONFIG_NET_MEDIATEK_SOC_WED) += mtk_wed.o +mtk_eth-$(CONFIG_NET_MEDIATEK_SOC_WED) += mtk_wed.o mtk_wed_mcu.o ifdef CONFIG_DEBUG_FS mtk_eth-$(CONFIG_NET_MEDIATEK_SOC_WED) += mtk_wed_debugfs.o endif diff --git a/drivers/net/ethernet/mediatek/mtk_wed_mcu.c b/drivers/net/ethernet/mediatek/mtk_wed_mcu.c new file mode 100644 index 000000000000..ce40d58000a0 --- /dev/null +++ b/drivers/net/ethernet/mediatek/mtk_wed_mcu.c @@ -0,0 +1,359 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* Copyright (C) 2022 MediaTek Inc. + * + * Author: Lorenzo Bianconi + * Sujuan Chen + */ + +#include +#include +#include +#include +#include + +#include "mtk_wed_regs.h" +#include "mtk_wed_wo.h" +#include "mtk_wed.h" + +static u32 wo_r32(struct mtk_wed_wo *wo, u32 reg) +{ + return readl(wo->boot.addr + reg); +} + +static void wo_w32(struct mtk_wed_wo *wo, u32 reg, u32 val) +{ + writel(val, wo->boot.addr + reg); +} + +static struct sk_buff * +mtk_wed_mcu_msg_alloc(const void *data, int data_len) +{ + int length = sizeof(struct mtk_wed_mcu_hdr) + data_len; + struct sk_buff *skb; + + skb = alloc_skb(length, GFP_KERNEL); + if (!skb) + return NULL; + + memset(skb->head, 0, length); + skb_reserve(skb, sizeof(struct mtk_wed_mcu_hdr)); + if (data && data_len) + skb_put_data(skb, data, data_len); + + return skb; +} + +static struct sk_buff * +mtk_wed_mcu_get_response(struct mtk_wed_wo *wo, unsigned long expires) +{ + if (!time_is_after_jiffies(expires)) + return NULL; + + wait_event_timeout(wo->mcu.wait, !skb_queue_empty(&wo->mcu.res_q), + expires - jiffies); + return skb_dequeue(&wo->mcu.res_q); +} + +void mtk_wed_mcu_rx_event(struct mtk_wed_wo *wo, struct sk_buff *skb) +{ + skb_queue_tail(&wo->mcu.res_q, skb); + wake_up(&wo->mcu.wait); +} + +void mtk_wed_mcu_rx_unsolicited_event(struct mtk_wed_wo *wo, + struct sk_buff *skb) +{ + struct mtk_wed_mcu_hdr *hdr = (struct mtk_wed_mcu_hdr *)skb->data; + + switch (hdr->cmd) { + case MTK_WED_WO_EVT_LOG_DUMP: { + const char *msg = (const char *)(skb->data + sizeof(*hdr)); + + dev_notice(wo->hw->dev, "%s\n", msg); + break; + } + case MTK_WED_WO_EVT_PROFILING: { + struct mtk_wed_wo_log_info *info; + u32 count = (skb->len - sizeof(*hdr)) / sizeof(*info); + int i; + + info = (struct mtk_wed_wo_log_info *)(skb->data + sizeof(*hdr)); + for (i = 0 ; i < count ; i++) + dev_notice(wo->hw->dev, + "SN:%u latency: total=%u, rro:%u, mod:%u\n", + le32_to_cpu(info[i].sn), + le32_to_cpu(info[i].total), + le32_to_cpu(info[i].rro), + le32_to_cpu(info[i].mod)); + break; + } + case MTK_WED_WO_EVT_RXCNT_INFO: + break; + default: + break; + } + + dev_kfree_skb(skb); +} + +static int +mtk_wed_mcu_skb_send_msg(struct mtk_wed_wo *wo, struct sk_buff *skb, + int id, int cmd, u16 *wait_seq, bool wait_resp) +{ + struct mtk_wed_mcu_hdr *hdr; + + /* TODO: make it dynamic based on cmd */ + wo->mcu.timeout = 20 * HZ; + + hdr = (struct mtk_wed_mcu_hdr *)skb_push(skb, sizeof(*hdr)); + hdr->cmd = cmd; + hdr->length = cpu_to_le16(skb->len); + + if (wait_resp && wait_seq) { + u16 seq = ++wo->mcu.seq; + + if (!seq) + seq = ++wo->mcu.seq; + *wait_seq = seq; + + hdr->flag |= cpu_to_le16(MTK_WED_WARP_CMD_FLAG_NEED_RSP); + hdr->seq = cpu_to_le16(seq); + } + if (id == MTK_WED_MODULE_ID_WO) + hdr->flag |= cpu_to_le16(MTK_WED_WARP_CMD_FLAG_FROM_TO_WO); + + dev_kfree_skb(skb); + return 0; +} + +static int +mtk_wed_mcu_parse_response(struct mtk_wed_wo *wo, struct sk_buff *skb, + int cmd, int seq) +{ + struct mtk_wed_mcu_hdr *hdr; + + if (!skb) { + dev_err(wo->hw->dev, "Message %08x (seq %d) timeout\n", + cmd, seq); + return -ETIMEDOUT; + } + + hdr = (struct mtk_wed_mcu_hdr *)skb->data; + if (le16_to_cpu(hdr->seq) != seq) + return -EAGAIN; + + skb_pull(skb, sizeof(*hdr)); + switch (cmd) { + case MTK_WED_WO_CMD_RXCNT_INFO: + default: + break; + } + + return 0; +} + +int mtk_wed_mcu_send_msg(struct mtk_wed_wo *wo, int id, int cmd, + const void *data, int len, bool wait_resp) +{ + unsigned long expires; + struct sk_buff *skb; + u16 seq; + int ret; + + skb = mtk_wed_mcu_msg_alloc(data, len); + if (!skb) + return -ENOMEM; + + mutex_lock(&wo->mcu.mutex); + + ret = mtk_wed_mcu_skb_send_msg(wo, skb, id, cmd, &seq, wait_resp); + if (ret || !wait_resp) + goto unlock; + + expires = jiffies + wo->mcu.timeout; + do { + skb = mtk_wed_mcu_get_response(wo, expires); + ret = mtk_wed_mcu_parse_response(wo, skb, cmd, seq); + dev_kfree_skb(skb); + } while (ret == -EAGAIN); + +unlock: + mutex_unlock(&wo->mcu.mutex); + + return ret; +} + +static int +mtk_wed_get_memory_region(struct mtk_wed_wo *wo, + struct mtk_wed_wo_memory_region *region) +{ + struct reserved_mem *rmem; + struct device_node *np; + int index; + + index = of_property_match_string(wo->hw->node, "memory-region-names", + region->name); + if (index < 0) + return index; + + np = of_parse_phandle(wo->hw->node, "memory-region", index); + if (!np) + return -ENODEV; + + rmem = of_reserved_mem_lookup(np); + of_node_put(np); + + if (!rmem) + return -ENODEV; + + region->phy_addr = rmem->base; + region->size = rmem->size; + region->addr = devm_ioremap(wo->hw->dev, region->phy_addr, region->size); + + return !region->addr ? -EINVAL : 0; +} + +static int +mtk_wed_mcu_run_firmware(struct mtk_wed_wo *wo, const struct firmware *fw, + struct mtk_wed_wo_memory_region *region) +{ + const u8 *first_region_ptr, *region_ptr, *trailer_ptr, *ptr = fw->data; + const struct mtk_wed_fw_trailer *trailer; + const struct mtk_wed_fw_region *fw_region; + + trailer_ptr = fw->data + fw->size - sizeof(*trailer); + trailer = (const struct mtk_wed_fw_trailer *)trailer_ptr; + region_ptr = trailer_ptr - trailer->num_region * sizeof(*fw_region); + first_region_ptr = region_ptr; + + while (region_ptr < trailer_ptr) { + u32 length; + + fw_region = (const struct mtk_wed_fw_region *)region_ptr; + length = le32_to_cpu(fw_region->len); + + if (region->phy_addr != le32_to_cpu(fw_region->addr)) + goto next; + + if (region->size < length) + goto next; + + if (first_region_ptr < ptr + length) + goto next; + + if (region->shared && region->consumed) + return 0; + + if (!region->shared || !region->consumed) { + memcpy_toio(region->addr, ptr, length); + region->consumed = true; + return 0; + } +next: + region_ptr += sizeof(*fw_region); + ptr += length; + } + + return -EINVAL; +} + +static int +mtk_wed_mcu_load_firmware(struct mtk_wed_wo *wo) +{ + static struct mtk_wed_wo_memory_region mem_region[] = { + [MTK_WED_WO_REGION_EMI] = { + .name = "wo-emi", + }, + [MTK_WED_WO_REGION_ILM] = { + .name = "wo-ilm", + }, + [MTK_WED_WO_REGION_DATA] = { + .name = "wo-data", + .shared = true, + }, + }; + const struct mtk_wed_fw_trailer *trailer; + const struct firmware *fw; + const char *fw_name; + u32 val, boot_cr; + int ret, i; + + /* load firmware region metadata */ + for (i = 0; i < ARRAY_SIZE(mem_region); i++) { + ret = mtk_wed_get_memory_region(wo, &mem_region[i]); + if (ret) + return ret; + } + + wo->boot.name = "wo-boot"; + ret = mtk_wed_get_memory_region(wo, &wo->boot); + if (ret) + return ret; + + /* set dummy cr */ + wed_w32(wo->hw->wed_dev, MTK_WED_SCR0 + 4 * MTK_WED_DUMMY_CR_FWDL, + wo->hw->index + 1); + + /* load firmware */ + fw_name = wo->hw->index ? MT7986_FIRMWARE_WO1 : MT7986_FIRMWARE_WO0; + ret = request_firmware(&fw, fw_name, wo->hw->dev); + if (ret) + return ret; + + trailer = (void *)(fw->data + fw->size - + sizeof(struct mtk_wed_fw_trailer)); + dev_info(wo->hw->dev, + "MTK WED WO Firmware Version: %.10s, Build Time: %.15s\n", + trailer->fw_ver, trailer->build_date); + dev_info(wo->hw->dev, "MTK WED WO Chip ID %02x Region %d\n", + trailer->chip_id, trailer->num_region); + + for (i = 0; i < ARRAY_SIZE(mem_region); i++) { + ret = mtk_wed_mcu_run_firmware(wo, fw, &mem_region[i]); + if (ret) + goto out; + } + + /* set the start address */ + boot_cr = wo->hw->index ? MTK_WO_MCU_CFG_LS_WA_BOOT_ADDR_ADDR + : MTK_WO_MCU_CFG_LS_WM_BOOT_ADDR_ADDR; + wo_w32(wo, boot_cr, mem_region[MTK_WED_WO_REGION_EMI].phy_addr >> 16); + /* wo firmware reset */ + wo_w32(wo, MTK_WO_MCU_CFG_LS_WF_MCCR_CLR_ADDR, 0xc00); + + val = wo_r32(wo, MTK_WO_MCU_CFG_LS_WF_MCU_CFG_WM_WA_ADDR); + val |= wo->hw->index ? MTK_WO_MCU_CFG_LS_WF_WM_WA_WA_CPU_RSTB_MASK + : MTK_WO_MCU_CFG_LS_WF_WM_WA_WM_CPU_RSTB_MASK; + wo_w32(wo, MTK_WO_MCU_CFG_LS_WF_MCU_CFG_WM_WA_ADDR, val); +out: + release_firmware(fw); + + return ret; +} + +static u32 +mtk_wed_mcu_read_fw_dl(struct mtk_wed_wo *wo) +{ + return wed_r32(wo->hw->wed_dev, + MTK_WED_SCR0 + 4 * MTK_WED_DUMMY_CR_FWDL); +} + +int mtk_wed_mcu_init(struct mtk_wed_wo *wo) +{ + u32 val; + int ret; + + skb_queue_head_init(&wo->mcu.res_q); + init_waitqueue_head(&wo->mcu.wait); + mutex_init(&wo->mcu.mutex); + + ret = mtk_wed_mcu_load_firmware(wo); + if (ret) + return ret; + + return readx_poll_timeout(mtk_wed_mcu_read_fw_dl, wo, val, !val, + 100, MTK_FW_DL_TIMEOUT); +} + +MODULE_FIRMWARE(MT7986_FIRMWARE_WO0); +MODULE_FIRMWARE(MT7986_FIRMWARE_WO1); diff --git a/drivers/net/ethernet/mediatek/mtk_wed_regs.h b/drivers/net/ethernet/mediatek/mtk_wed_regs.h index e270fb336143..c940b3bb215b 100644 --- a/drivers/net/ethernet/mediatek/mtk_wed_regs.h +++ b/drivers/net/ethernet/mediatek/mtk_wed_regs.h @@ -152,6 +152,7 @@ struct mtk_wdma_desc { #define MTK_WED_RING_RX(_n) (0x400 + (_n) * 0x10) +#define MTK_WED_SCR0 0x3c0 #define MTK_WED_WPDMA_INT_TRIGGER 0x504 #define MTK_WED_WPDMA_INT_TRIGGER_RX_DONE BIT(1) #define MTK_WED_WPDMA_INT_TRIGGER_TX_DONE GENMASK(5, 4) diff --git a/drivers/net/ethernet/mediatek/mtk_wed_wo.h b/drivers/net/ethernet/mediatek/mtk_wed_wo.h new file mode 100644 index 000000000000..5413a2308084 --- /dev/null +++ b/drivers/net/ethernet/mediatek/mtk_wed_wo.h @@ -0,0 +1,150 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* Copyright (C) 2022 Lorenzo Bianconi */ + +#ifndef __MTK_WED_WO_H +#define __MTK_WED_WO_H + +#include +#include + +struct mtk_wed_hw; + +struct mtk_wed_mcu_hdr { + /* DW0 */ + u8 version; + u8 cmd; + __le16 length; + + /* DW1 */ + __le16 seq; + __le16 flag; + + /* DW2 */ + __le32 status; + + /* DW3 */ + u8 rsv[20]; +}; + +struct mtk_wed_wo_log_info { + __le32 sn; + __le32 total; + __le32 rro; + __le32 mod; +}; + +enum mtk_wed_wo_event { + MTK_WED_WO_EVT_LOG_DUMP = 0x1, + MTK_WED_WO_EVT_PROFILING = 0x2, + MTK_WED_WO_EVT_RXCNT_INFO = 0x3, +}; + +#define MTK_WED_MODULE_ID_WO 1 +#define MTK_FW_DL_TIMEOUT 4000000 /* us */ +#define MTK_WOCPU_TIMEOUT 2000000 /* us */ + +enum { + MTK_WED_WARP_CMD_FLAG_RSP = BIT(0), + MTK_WED_WARP_CMD_FLAG_NEED_RSP = BIT(1), + MTK_WED_WARP_CMD_FLAG_FROM_TO_WO = BIT(2), +}; + +enum { + MTK_WED_WO_REGION_EMI, + MTK_WED_WO_REGION_ILM, + MTK_WED_WO_REGION_DATA, + MTK_WED_WO_REGION_BOOT, + __MTK_WED_WO_REGION_MAX, +}; + +enum mtk_wed_dummy_cr_idx { + MTK_WED_DUMMY_CR_FWDL, + MTK_WED_DUMMY_CR_WO_STATUS, +}; + +#define MT7986_FIRMWARE_WO0 "mediatek/mt7986_wo_0.bin" +#define MT7986_FIRMWARE_WO1 "mediatek/mt7986_wo_1.bin" + +#define MTK_WO_MCU_CFG_LS_BASE 0 +#define MTK_WO_MCU_CFG_LS_HW_VER_ADDR (MTK_WO_MCU_CFG_LS_BASE + 0x000) +#define MTK_WO_MCU_CFG_LS_FW_VER_ADDR (MTK_WO_MCU_CFG_LS_BASE + 0x004) +#define MTK_WO_MCU_CFG_LS_CFG_DBG1_ADDR (MTK_WO_MCU_CFG_LS_BASE + 0x00c) +#define MTK_WO_MCU_CFG_LS_CFG_DBG2_ADDR (MTK_WO_MCU_CFG_LS_BASE + 0x010) +#define MTK_WO_MCU_CFG_LS_WF_MCCR_ADDR (MTK_WO_MCU_CFG_LS_BASE + 0x014) +#define MTK_WO_MCU_CFG_LS_WF_MCCR_SET_ADDR (MTK_WO_MCU_CFG_LS_BASE + 0x018) +#define MTK_WO_MCU_CFG_LS_WF_MCCR_CLR_ADDR (MTK_WO_MCU_CFG_LS_BASE + 0x01c) +#define MTK_WO_MCU_CFG_LS_WF_MCU_CFG_WM_WA_ADDR (MTK_WO_MCU_CFG_LS_BASE + 0x050) +#define MTK_WO_MCU_CFG_LS_WM_BOOT_ADDR_ADDR (MTK_WO_MCU_CFG_LS_BASE + 0x060) +#define MTK_WO_MCU_CFG_LS_WA_BOOT_ADDR_ADDR (MTK_WO_MCU_CFG_LS_BASE + 0x064) + +#define MTK_WO_MCU_CFG_LS_WF_WM_WA_WM_CPU_RSTB_MASK BIT(5) +#define MTK_WO_MCU_CFG_LS_WF_WM_WA_WA_CPU_RSTB_MASK BIT(0) + +struct mtk_wed_wo_memory_region { + const char *name; + void __iomem *addr; + phys_addr_t phy_addr; + u32 size; + bool shared:1; + bool consumed:1; +}; + +struct mtk_wed_fw_region { + __le32 decomp_crc; + __le32 decomp_len; + __le32 decomp_blk_sz; + u8 rsv0[4]; + __le32 addr; + __le32 len; + u8 feature_set; + u8 rsv1[15]; +} __packed; + +struct mtk_wed_fw_trailer { + u8 chip_id; + u8 eco_code; + u8 num_region; + u8 format_ver; + u8 format_flag; + u8 rsv[2]; + char fw_ver[10]; + char build_date[15]; + u32 crc; +}; + +struct mtk_wed_wo { + struct mtk_wed_hw *hw; + struct mtk_wed_wo_memory_region boot; + + struct { + struct mutex mutex; + int timeout; + u16 seq; + + struct sk_buff_head res_q; + wait_queue_head_t wait; + } mcu; +}; + +static inline int +mtk_wed_mcu_check_msg(struct mtk_wed_wo *wo, struct sk_buff *skb) +{ + struct mtk_wed_mcu_hdr *hdr = (struct mtk_wed_mcu_hdr *)skb->data; + + if (hdr->version) + return -EINVAL; + + if (skb->len < sizeof(*hdr) || skb->len != le16_to_cpu(hdr->length)) + return -EINVAL; + + return 0; +} + +void mtk_wed_mcu_rx_event(struct mtk_wed_wo *wo, struct sk_buff *skb); +void mtk_wed_mcu_rx_unsolicited_event(struct mtk_wed_wo *wo, + struct sk_buff *skb); +int mtk_wed_mcu_send_msg(struct mtk_wed_wo *wo, int id, int cmd, + const void *data, int len, bool wait_resp); +int mtk_wed_mcu_init(struct mtk_wed_wo *wo); + +#endif /* __MTK_WED_WO_H */ diff --git a/include/linux/soc/mediatek/mtk_wed.h b/include/linux/soc/mediatek/mtk_wed.h index 4450c8b7a1cb..2cc2f1e43ba9 100644 --- a/include/linux/soc/mediatek/mtk_wed.h +++ b/include/linux/soc/mediatek/mtk_wed.h @@ -11,6 +11,35 @@ struct mtk_wed_hw; struct mtk_wdma_desc; +enum mtk_wed_wo_cmd { + MTK_WED_WO_CMD_WED_CFG, + MTK_WED_WO_CMD_WED_RX_STAT, + MTK_WED_WO_CMD_RRO_SER, + MTK_WED_WO_CMD_DBG_INFO, + MTK_WED_WO_CMD_DEV_INFO, + MTK_WED_WO_CMD_BSS_INFO, + MTK_WED_WO_CMD_STA_REC, + MTK_WED_WO_CMD_DEV_INFO_DUMP, + MTK_WED_WO_CMD_BSS_INFO_DUMP, + MTK_WED_WO_CMD_STA_REC_DUMP, + MTK_WED_WO_CMD_BA_INFO_DUMP, + MTK_WED_WO_CMD_FBCMD_Q_DUMP, + MTK_WED_WO_CMD_FW_LOG_CTRL, + MTK_WED_WO_CMD_LOG_FLUSH, + MTK_WED_WO_CMD_CHANGE_STATE, + MTK_WED_WO_CMD_CPU_STATS_ENABLE, + MTK_WED_WO_CMD_CPU_STATS_DUMP, + MTK_WED_WO_CMD_EXCEPTION_INIT, + MTK_WED_WO_CMD_PROF_CTRL, + MTK_WED_WO_CMD_STA_BA_DUMP, + MTK_WED_WO_CMD_BA_CTRL_DUMP, + MTK_WED_WO_CMD_RXCNT_CTRL, + MTK_WED_WO_CMD_RXCNT_INFO, + MTK_WED_WO_CMD_SET_CAP, + MTK_WED_WO_CMD_CCIF_RING_DUMP, + MTK_WED_WO_CMD_WED_END +}; + enum mtk_wed_bus_tye { MTK_WED_BUS_PCIE, MTK_WED_BUS_AXI, From 799684448e3e1f57257a6155541e53510488f67b Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Sat, 5 Nov 2022 23:36:19 +0100 Subject: [PATCH 0820/2002] net: ethernet: mtk_wed: introduce wed wo support Introduce WO chip support to mtk wed driver. MTK WED WO is used to implement RX Wireless Ethernet Dispatch and offload traffic received by wlan nic to the wired interface. Tested-by: Daniel Golle Co-developed-by: Sujuan Chen Signed-off-by: Sujuan Chen Signed-off-by: Lorenzo Bianconi Signed-off-by: David S. Miller --- drivers/net/ethernet/mediatek/Makefile | 2 +- drivers/net/ethernet/mediatek/mtk_wed.c | 7 +- drivers/net/ethernet/mediatek/mtk_wed.h | 2 + drivers/net/ethernet/mediatek/mtk_wed_mcu.c | 3 +- drivers/net/ethernet/mediatek/mtk_wed_wo.c | 508 ++++++++++++++++++++ drivers/net/ethernet/mediatek/mtk_wed_wo.h | 104 ++++ 6 files changed, 622 insertions(+), 4 deletions(-) create mode 100644 drivers/net/ethernet/mediatek/mtk_wed_wo.c diff --git a/drivers/net/ethernet/mediatek/Makefile b/drivers/net/ethernet/mediatek/Makefile index d4bdefa77159..8e0c61c33ff8 100644 --- a/drivers/net/ethernet/mediatek/Makefile +++ b/drivers/net/ethernet/mediatek/Makefile @@ -5,7 +5,7 @@ obj-$(CONFIG_NET_MEDIATEK_SOC) += mtk_eth.o mtk_eth-y := mtk_eth_soc.o mtk_sgmii.o mtk_eth_path.o mtk_ppe.o mtk_ppe_debugfs.o mtk_ppe_offload.o -mtk_eth-$(CONFIG_NET_MEDIATEK_SOC_WED) += mtk_wed.o mtk_wed_mcu.o +mtk_eth-$(CONFIG_NET_MEDIATEK_SOC_WED) += mtk_wed.o mtk_wed_mcu.o mtk_wed_wo.o ifdef CONFIG_DEBUG_FS mtk_eth-$(CONFIG_NET_MEDIATEK_SOC_WED) += mtk_wed_debugfs.o endif diff --git a/drivers/net/ethernet/mediatek/mtk_wed.c b/drivers/net/ethernet/mediatek/mtk_wed.c index 65e01bf4b4d2..9c9dd17332b6 100644 --- a/drivers/net/ethernet/mediatek/mtk_wed.c +++ b/drivers/net/ethernet/mediatek/mtk_wed.c @@ -16,6 +16,7 @@ #include "mtk_wed_regs.h" #include "mtk_wed.h" #include "mtk_ppe.h" +#include "mtk_wed_wo.h" #define MTK_PCIE_BASE(n) (0x1a143000 + (n) * 0x2000) @@ -355,6 +356,8 @@ mtk_wed_detach(struct mtk_wed_device *dev) mtk_wed_free_buffer(dev); mtk_wed_free_tx_rings(dev); + if (hw->version != 1) + mtk_wed_wo_deinit(hw); if (dev->wlan.bus_type == MTK_WED_BUS_PCIE) { struct device_node *wlan_node; @@ -878,9 +881,11 @@ mtk_wed_attach(struct mtk_wed_device *dev) } mtk_wed_hw_init_early(dev); - if (hw->hifsys) + if (hw->version == 1) regmap_update_bits(hw->hifsys, HIFSYS_DMA_AG_MAP, BIT(hw->index), 0); + else + ret = mtk_wed_wo_init(hw); out: mutex_unlock(&hw_lock); diff --git a/drivers/net/ethernet/mediatek/mtk_wed.h b/drivers/net/ethernet/mediatek/mtk_wed.h index ae420ca01a48..af656fd31ff9 100644 --- a/drivers/net/ethernet/mediatek/mtk_wed.h +++ b/drivers/net/ethernet/mediatek/mtk_wed.h @@ -10,6 +10,7 @@ #include struct mtk_eth; +struct mtk_wed_wo; struct mtk_wed_hw { struct device_node *node; @@ -22,6 +23,7 @@ struct mtk_wed_hw { struct regmap *mirror; struct dentry *debugfs_dir; struct mtk_wed_device *wed_dev; + struct mtk_wed_wo *wed_wo; u32 debugfs_reg; u32 num_flows; u8 version; diff --git a/drivers/net/ethernet/mediatek/mtk_wed_mcu.c b/drivers/net/ethernet/mediatek/mtk_wed_mcu.c index ce40d58000a0..b657328a1d9d 100644 --- a/drivers/net/ethernet/mediatek/mtk_wed_mcu.c +++ b/drivers/net/ethernet/mediatek/mtk_wed_mcu.c @@ -122,8 +122,7 @@ mtk_wed_mcu_skb_send_msg(struct mtk_wed_wo *wo, struct sk_buff *skb, if (id == MTK_WED_MODULE_ID_WO) hdr->flag |= cpu_to_le16(MTK_WED_WARP_CMD_FLAG_FROM_TO_WO); - dev_kfree_skb(skb); - return 0; + return mtk_wed_wo_queue_tx_skb(wo, &wo->q_tx, skb); } static int diff --git a/drivers/net/ethernet/mediatek/mtk_wed_wo.c b/drivers/net/ethernet/mediatek/mtk_wed_wo.c new file mode 100644 index 000000000000..4754b6db009e --- /dev/null +++ b/drivers/net/ethernet/mediatek/mtk_wed_wo.c @@ -0,0 +1,508 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* Copyright (C) 2022 MediaTek Inc. + * + * Author: Lorenzo Bianconi + * Sujuan Chen + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "mtk_wed.h" +#include "mtk_wed_regs.h" +#include "mtk_wed_wo.h" + +static u32 +mtk_wed_mmio_r32(struct mtk_wed_wo *wo, u32 reg) +{ + u32 val; + + if (regmap_read(wo->mmio.regs, reg, &val)) + val = ~0; + + return val; +} + +static void +mtk_wed_mmio_w32(struct mtk_wed_wo *wo, u32 reg, u32 val) +{ + regmap_write(wo->mmio.regs, reg, val); +} + +static u32 +mtk_wed_wo_get_isr(struct mtk_wed_wo *wo) +{ + u32 val = mtk_wed_mmio_r32(wo, MTK_WED_WO_CCIF_RCHNUM); + + return val & MTK_WED_WO_CCIF_RCHNUM_MASK; +} + +static void +mtk_wed_wo_set_isr(struct mtk_wed_wo *wo, u32 mask) +{ + mtk_wed_mmio_w32(wo, MTK_WED_WO_CCIF_IRQ0_MASK, mask); +} + +static void +mtk_wed_wo_set_ack(struct mtk_wed_wo *wo, u32 mask) +{ + mtk_wed_mmio_w32(wo, MTK_WED_WO_CCIF_ACK, mask); +} + +static void +mtk_wed_wo_set_isr_mask(struct mtk_wed_wo *wo, u32 mask, u32 val, bool set) +{ + unsigned long flags; + + spin_lock_irqsave(&wo->mmio.lock, flags); + wo->mmio.irq_mask &= ~mask; + wo->mmio.irq_mask |= val; + if (set) + mtk_wed_wo_set_isr(wo, wo->mmio.irq_mask); + spin_unlock_irqrestore(&wo->mmio.lock, flags); +} + +static void +mtk_wed_wo_irq_enable(struct mtk_wed_wo *wo, u32 mask) +{ + mtk_wed_wo_set_isr_mask(wo, 0, mask, false); + tasklet_schedule(&wo->mmio.irq_tasklet); +} + +static void +mtk_wed_wo_irq_disable(struct mtk_wed_wo *wo, u32 mask) +{ + mtk_wed_wo_set_isr_mask(wo, mask, 0, true); +} + +static void +mtk_wed_wo_kickout(struct mtk_wed_wo *wo) +{ + mtk_wed_mmio_w32(wo, MTK_WED_WO_CCIF_BUSY, 1 << MTK_WED_WO_TXCH_NUM); + mtk_wed_mmio_w32(wo, MTK_WED_WO_CCIF_TCHNUM, MTK_WED_WO_TXCH_NUM); +} + +static void +mtk_wed_wo_queue_kick(struct mtk_wed_wo *wo, struct mtk_wed_wo_queue *q, + u32 val) +{ + wmb(); + mtk_wed_mmio_w32(wo, q->regs.cpu_idx, val); +} + +static void * +mtk_wed_wo_dequeue(struct mtk_wed_wo *wo, struct mtk_wed_wo_queue *q, u32 *len, + bool flush) +{ + int buf_len = SKB_WITH_OVERHEAD(q->buf_size); + int index = (q->tail + 1) % q->n_desc; + struct mtk_wed_wo_queue_entry *entry; + struct mtk_wed_wo_queue_desc *desc; + void *buf; + + if (!q->queued) + return NULL; + + if (flush) + q->desc[index].ctrl |= cpu_to_le32(MTK_WED_WO_CTL_DMA_DONE); + else if (!(q->desc[index].ctrl & cpu_to_le32(MTK_WED_WO_CTL_DMA_DONE))) + return NULL; + + q->tail = index; + q->queued--; + + desc = &q->desc[index]; + entry = &q->entry[index]; + buf = entry->buf; + if (len) + *len = FIELD_GET(MTK_WED_WO_CTL_SD_LEN0, + le32_to_cpu(READ_ONCE(desc->ctrl))); + if (buf) + dma_unmap_single(wo->hw->dev, entry->addr, buf_len, + DMA_FROM_DEVICE); + entry->buf = NULL; + + return buf; +} + +static int +mtk_wed_wo_queue_refill(struct mtk_wed_wo *wo, struct mtk_wed_wo_queue *q, + gfp_t gfp, bool rx) +{ + enum dma_data_direction dir = rx ? DMA_FROM_DEVICE : DMA_TO_DEVICE; + int n_buf = 0; + + spin_lock_bh(&q->lock); + while (q->queued < q->n_desc) { + void *buf = page_frag_alloc(&q->cache, q->buf_size, gfp); + struct mtk_wed_wo_queue_entry *entry; + dma_addr_t addr; + + if (!buf) + break; + + addr = dma_map_single(wo->hw->dev, buf, q->buf_size, dir); + if (unlikely(dma_mapping_error(wo->hw->dev, addr))) { + skb_free_frag(buf); + break; + } + + q->head = (q->head + 1) % q->n_desc; + entry = &q->entry[q->head]; + entry->addr = addr; + entry->len = q->buf_size; + q->entry[q->head].buf = buf; + + if (rx) { + struct mtk_wed_wo_queue_desc *desc = &q->desc[q->head]; + u32 ctrl = MTK_WED_WO_CTL_LAST_SEC0 | + FIELD_PREP(MTK_WED_WO_CTL_SD_LEN0, + entry->len); + + WRITE_ONCE(desc->buf0, cpu_to_le32(addr)); + WRITE_ONCE(desc->ctrl, cpu_to_le32(ctrl)); + } + q->queued++; + n_buf++; + } + spin_unlock_bh(&q->lock); + + return n_buf; +} + +static void +mtk_wed_wo_rx_complete(struct mtk_wed_wo *wo) +{ + mtk_wed_wo_set_ack(wo, MTK_WED_WO_RXCH_INT_MASK); + mtk_wed_wo_irq_enable(wo, MTK_WED_WO_RXCH_INT_MASK); +} + +static void +mtk_wed_wo_rx_run_queue(struct mtk_wed_wo *wo, struct mtk_wed_wo_queue *q) +{ + for (;;) { + struct mtk_wed_mcu_hdr *hdr; + struct sk_buff *skb; + void *data; + u32 len; + + data = mtk_wed_wo_dequeue(wo, q, &len, false); + if (!data) + break; + + skb = build_skb(data, q->buf_size); + if (!skb) { + skb_free_frag(data); + continue; + } + + __skb_put(skb, len); + if (mtk_wed_mcu_check_msg(wo, skb)) { + dev_kfree_skb(skb); + continue; + } + + hdr = (struct mtk_wed_mcu_hdr *)skb->data; + if (hdr->flag & cpu_to_le16(MTK_WED_WARP_CMD_FLAG_RSP)) + mtk_wed_mcu_rx_event(wo, skb); + else + mtk_wed_mcu_rx_unsolicited_event(wo, skb); + } + + if (mtk_wed_wo_queue_refill(wo, q, GFP_ATOMIC, true)) { + u32 index = (q->head - 1) % q->n_desc; + + mtk_wed_wo_queue_kick(wo, q, index); + } +} + +static irqreturn_t +mtk_wed_wo_irq_handler(int irq, void *data) +{ + struct mtk_wed_wo *wo = data; + + mtk_wed_wo_set_isr(wo, 0); + tasklet_schedule(&wo->mmio.irq_tasklet); + + return IRQ_HANDLED; +} + +static void mtk_wed_wo_irq_tasklet(struct tasklet_struct *t) +{ + struct mtk_wed_wo *wo = from_tasklet(wo, t, mmio.irq_tasklet); + u32 intr, mask; + + /* disable interrupts */ + mtk_wed_wo_set_isr(wo, 0); + + intr = mtk_wed_wo_get_isr(wo); + intr &= wo->mmio.irq_mask; + mask = intr & (MTK_WED_WO_RXCH_INT_MASK | MTK_WED_WO_EXCEPTION_INT_MASK); + mtk_wed_wo_irq_disable(wo, mask); + + if (intr & MTK_WED_WO_RXCH_INT_MASK) { + mtk_wed_wo_rx_run_queue(wo, &wo->q_rx); + mtk_wed_wo_rx_complete(wo); + } +} + +/* mtk wed wo hw queues */ + +static int +mtk_wed_wo_queue_alloc(struct mtk_wed_wo *wo, struct mtk_wed_wo_queue *q, + int n_desc, int buf_size, int index, + struct mtk_wed_wo_queue_regs *regs) +{ + spin_lock_init(&q->lock); + q->regs = *regs; + q->n_desc = n_desc; + q->buf_size = buf_size; + + q->desc = dmam_alloc_coherent(wo->hw->dev, n_desc * sizeof(*q->desc), + &q->desc_dma, GFP_KERNEL); + if (!q->desc) + return -ENOMEM; + + q->entry = devm_kzalloc(wo->hw->dev, n_desc * sizeof(*q->entry), + GFP_KERNEL); + if (!q->entry) + return -ENOMEM; + + return 0; +} + +static void +mtk_wed_wo_queue_free(struct mtk_wed_wo *wo, struct mtk_wed_wo_queue *q) +{ + mtk_wed_mmio_w32(wo, q->regs.cpu_idx, 0); + dma_free_coherent(wo->hw->dev, q->n_desc * sizeof(*q->desc), q->desc, + q->desc_dma); +} + +static void +mtk_wed_wo_queue_tx_clean(struct mtk_wed_wo *wo, struct mtk_wed_wo_queue *q) +{ + struct page *page; + int i; + + spin_lock_bh(&q->lock); + for (i = 0; i < q->n_desc; i++) { + struct mtk_wed_wo_queue_entry *entry = &q->entry[i]; + + dma_unmap_single(wo->hw->dev, entry->addr, entry->len, + DMA_TO_DEVICE); + skb_free_frag(entry->buf); + entry->buf = NULL; + } + spin_unlock_bh(&q->lock); + + if (!q->cache.va) + return; + + page = virt_to_page(q->cache.va); + __page_frag_cache_drain(page, q->cache.pagecnt_bias); + memset(&q->cache, 0, sizeof(q->cache)); +} + +static void +mtk_wed_wo_queue_rx_clean(struct mtk_wed_wo *wo, struct mtk_wed_wo_queue *q) +{ + struct page *page; + + spin_lock_bh(&q->lock); + for (;;) { + void *buf = mtk_wed_wo_dequeue(wo, q, NULL, true); + + if (!buf) + break; + + skb_free_frag(buf); + } + spin_unlock_bh(&q->lock); + + if (!q->cache.va) + return; + + page = virt_to_page(q->cache.va); + __page_frag_cache_drain(page, q->cache.pagecnt_bias); + memset(&q->cache, 0, sizeof(q->cache)); +} + +static void +mtk_wed_wo_queue_reset(struct mtk_wed_wo *wo, struct mtk_wed_wo_queue *q) +{ + mtk_wed_mmio_w32(wo, q->regs.cpu_idx, 0); + mtk_wed_mmio_w32(wo, q->regs.desc_base, q->desc_dma); + mtk_wed_mmio_w32(wo, q->regs.ring_size, q->n_desc); +} + +int mtk_wed_wo_queue_tx_skb(struct mtk_wed_wo *wo, struct mtk_wed_wo_queue *q, + struct sk_buff *skb) +{ + struct mtk_wed_wo_queue_entry *entry; + struct mtk_wed_wo_queue_desc *desc; + int ret = 0, index; + u32 ctrl; + + spin_lock_bh(&q->lock); + + q->tail = mtk_wed_mmio_r32(wo, q->regs.dma_idx); + index = (q->head + 1) % q->n_desc; + if (q->tail == index) { + ret = -ENOMEM; + goto out; + } + + entry = &q->entry[index]; + if (skb->len > entry->len) { + ret = -ENOMEM; + goto out; + } + + desc = &q->desc[index]; + q->head = index; + + dma_sync_single_for_cpu(wo->hw->dev, entry->addr, skb->len, + DMA_TO_DEVICE); + memcpy(entry->buf, skb->data, skb->len); + dma_sync_single_for_device(wo->hw->dev, entry->addr, skb->len, + DMA_TO_DEVICE); + + ctrl = FIELD_PREP(MTK_WED_WO_CTL_SD_LEN0, skb->len) | + MTK_WED_WO_CTL_LAST_SEC0 | MTK_WED_WO_CTL_DMA_DONE; + WRITE_ONCE(desc->buf0, cpu_to_le32(entry->addr)); + WRITE_ONCE(desc->ctrl, cpu_to_le32(ctrl)); + + mtk_wed_wo_queue_kick(wo, q, q->head); + mtk_wed_wo_kickout(wo); +out: + spin_unlock_bh(&q->lock); + + dev_kfree_skb(skb); + + return ret; +} + +static int +mtk_wed_wo_exception_init(struct mtk_wed_wo *wo) +{ + return 0; +} + +static int +mtk_wed_wo_hardware_init(struct mtk_wed_wo *wo) +{ + struct mtk_wed_wo_queue_regs regs; + struct device_node *np; + int ret; + + np = of_parse_phandle(wo->hw->node, "mediatek,wo-ccif", 0); + if (!np) + return -ENODEV; + + wo->mmio.regs = syscon_regmap_lookup_by_phandle(np, NULL); + if (IS_ERR_OR_NULL(wo->mmio.regs)) + return PTR_ERR(wo->mmio.regs); + + wo->mmio.irq = irq_of_parse_and_map(np, 0); + wo->mmio.irq_mask = MTK_WED_WO_ALL_INT_MASK; + spin_lock_init(&wo->mmio.lock); + tasklet_setup(&wo->mmio.irq_tasklet, mtk_wed_wo_irq_tasklet); + + ret = devm_request_irq(wo->hw->dev, wo->mmio.irq, + mtk_wed_wo_irq_handler, IRQF_TRIGGER_HIGH, + KBUILD_MODNAME, wo); + if (ret) + goto error; + + regs.desc_base = MTK_WED_WO_CCIF_DUMMY1; + regs.ring_size = MTK_WED_WO_CCIF_DUMMY2; + regs.dma_idx = MTK_WED_WO_CCIF_SHADOW4; + regs.cpu_idx = MTK_WED_WO_CCIF_DUMMY3; + + ret = mtk_wed_wo_queue_alloc(wo, &wo->q_tx, MTK_WED_WO_RING_SIZE, + MTK_WED_WO_CMD_LEN, MTK_WED_WO_TXCH_NUM, + ®s); + if (ret) + goto error; + + mtk_wed_wo_queue_refill(wo, &wo->q_tx, GFP_KERNEL, false); + mtk_wed_wo_queue_reset(wo, &wo->q_tx); + + regs.desc_base = MTK_WED_WO_CCIF_DUMMY5; + regs.ring_size = MTK_WED_WO_CCIF_DUMMY6; + regs.dma_idx = MTK_WED_WO_CCIF_SHADOW8; + regs.cpu_idx = MTK_WED_WO_CCIF_DUMMY7; + + ret = mtk_wed_wo_queue_alloc(wo, &wo->q_rx, MTK_WED_WO_RING_SIZE, + MTK_WED_WO_CMD_LEN, MTK_WED_WO_RXCH_NUM, + ®s); + if (ret) + goto error; + + mtk_wed_wo_queue_refill(wo, &wo->q_rx, GFP_KERNEL, true); + mtk_wed_wo_queue_reset(wo, &wo->q_rx); + + /* rx queue irqmask */ + mtk_wed_wo_set_isr(wo, wo->mmio.irq_mask); + + return 0; + +error: + devm_free_irq(wo->hw->dev, wo->mmio.irq, wo); + + return ret; +} + +static void +mtk_wed_wo_hw_deinit(struct mtk_wed_wo *wo) +{ + /* disable interrupts */ + mtk_wed_wo_set_isr(wo, 0); + + tasklet_disable(&wo->mmio.irq_tasklet); + + disable_irq(wo->mmio.irq); + devm_free_irq(wo->hw->dev, wo->mmio.irq, wo); + + mtk_wed_wo_queue_tx_clean(wo, &wo->q_tx); + mtk_wed_wo_queue_rx_clean(wo, &wo->q_rx); + mtk_wed_wo_queue_free(wo, &wo->q_tx); + mtk_wed_wo_queue_free(wo, &wo->q_rx); +} + +int mtk_wed_wo_init(struct mtk_wed_hw *hw) +{ + struct mtk_wed_wo *wo; + int ret; + + wo = devm_kzalloc(hw->dev, sizeof(*wo), GFP_KERNEL); + if (!wo) + return -ENOMEM; + + hw->wed_wo = wo; + wo->hw = hw; + + ret = mtk_wed_wo_hardware_init(wo); + if (ret) + return ret; + + ret = mtk_wed_mcu_init(wo); + if (ret) + return ret; + + return mtk_wed_wo_exception_init(wo); +} + +void mtk_wed_wo_deinit(struct mtk_wed_hw *hw) +{ + struct mtk_wed_wo *wo = hw->wed_wo; + + mtk_wed_wo_hw_deinit(wo); +} diff --git a/drivers/net/ethernet/mediatek/mtk_wed_wo.h b/drivers/net/ethernet/mediatek/mtk_wed_wo.h index 5413a2308084..ed7e69d420dc 100644 --- a/drivers/net/ethernet/mediatek/mtk_wed_wo.h +++ b/drivers/net/ethernet/mediatek/mtk_wed_wo.h @@ -80,6 +80,54 @@ enum mtk_wed_dummy_cr_idx { #define MTK_WO_MCU_CFG_LS_WF_WM_WA_WM_CPU_RSTB_MASK BIT(5) #define MTK_WO_MCU_CFG_LS_WF_WM_WA_WA_CPU_RSTB_MASK BIT(0) +#define MTK_WED_WO_RING_SIZE 256 +#define MTK_WED_WO_CMD_LEN 1504 + +#define MTK_WED_WO_TXCH_NUM 0 +#define MTK_WED_WO_RXCH_NUM 1 +#define MTK_WED_WO_RXCH_WO_EXCEPTION 7 + +#define MTK_WED_WO_TXCH_INT_MASK BIT(0) +#define MTK_WED_WO_RXCH_INT_MASK BIT(1) +#define MTK_WED_WO_EXCEPTION_INT_MASK BIT(7) +#define MTK_WED_WO_ALL_INT_MASK (MTK_WED_WO_RXCH_INT_MASK | \ + MTK_WED_WO_EXCEPTION_INT_MASK) + +#define MTK_WED_WO_CCIF_BUSY 0x004 +#define MTK_WED_WO_CCIF_START 0x008 +#define MTK_WED_WO_CCIF_TCHNUM 0x00c +#define MTK_WED_WO_CCIF_RCHNUM 0x010 +#define MTK_WED_WO_CCIF_RCHNUM_MASK GENMASK(7, 0) + +#define MTK_WED_WO_CCIF_ACK 0x014 +#define MTK_WED_WO_CCIF_IRQ0_MASK 0x018 +#define MTK_WED_WO_CCIF_IRQ1_MASK 0x01c +#define MTK_WED_WO_CCIF_DUMMY1 0x020 +#define MTK_WED_WO_CCIF_DUMMY2 0x024 +#define MTK_WED_WO_CCIF_DUMMY3 0x028 +#define MTK_WED_WO_CCIF_DUMMY4 0x02c +#define MTK_WED_WO_CCIF_SHADOW1 0x030 +#define MTK_WED_WO_CCIF_SHADOW2 0x034 +#define MTK_WED_WO_CCIF_SHADOW3 0x038 +#define MTK_WED_WO_CCIF_SHADOW4 0x03c +#define MTK_WED_WO_CCIF_DUMMY5 0x050 +#define MTK_WED_WO_CCIF_DUMMY6 0x054 +#define MTK_WED_WO_CCIF_DUMMY7 0x058 +#define MTK_WED_WO_CCIF_DUMMY8 0x05c +#define MTK_WED_WO_CCIF_SHADOW5 0x060 +#define MTK_WED_WO_CCIF_SHADOW6 0x064 +#define MTK_WED_WO_CCIF_SHADOW7 0x068 +#define MTK_WED_WO_CCIF_SHADOW8 0x06c + +#define MTK_WED_WO_CTL_SD_LEN1 GENMASK(13, 0) +#define MTK_WED_WO_CTL_LAST_SEC1 BIT(14) +#define MTK_WED_WO_CTL_BURST BIT(15) +#define MTK_WED_WO_CTL_SD_LEN0_SHIFT 16 +#define MTK_WED_WO_CTL_SD_LEN0 GENMASK(29, 16) +#define MTK_WED_WO_CTL_LAST_SEC0 BIT(30) +#define MTK_WED_WO_CTL_DMA_DONE BIT(31) +#define MTK_WED_WO_INFO_WINFO GENMASK(15, 0) + struct mtk_wed_wo_memory_region { const char *name; void __iomem *addr; @@ -112,10 +160,53 @@ struct mtk_wed_fw_trailer { u32 crc; }; +struct mtk_wed_wo_queue_regs { + u32 desc_base; + u32 ring_size; + u32 cpu_idx; + u32 dma_idx; +}; + +struct mtk_wed_wo_queue_desc { + __le32 buf0; + __le32 ctrl; + __le32 buf1; + __le32 info; + __le32 reserved[4]; +} __packed __aligned(32); + +struct mtk_wed_wo_queue_entry { + dma_addr_t addr; + void *buf; + u32 len; +}; + +struct mtk_wed_wo_queue { + struct mtk_wed_wo_queue_regs regs; + + struct page_frag_cache cache; + spinlock_t lock; + + struct mtk_wed_wo_queue_desc *desc; + dma_addr_t desc_dma; + + struct mtk_wed_wo_queue_entry *entry; + + u16 head; + u16 tail; + int n_desc; + int queued; + int buf_size; + +}; + struct mtk_wed_wo { struct mtk_wed_hw *hw; struct mtk_wed_wo_memory_region boot; + struct mtk_wed_wo_queue q_tx; + struct mtk_wed_wo_queue q_rx; + struct { struct mutex mutex; int timeout; @@ -124,6 +215,15 @@ struct mtk_wed_wo { struct sk_buff_head res_q; wait_queue_head_t wait; } mcu; + + struct { + struct regmap *regs; + + spinlock_t lock; + struct tasklet_struct irq_tasklet; + int irq; + u32 irq_mask; + } mmio; }; static inline int @@ -146,5 +246,9 @@ void mtk_wed_mcu_rx_unsolicited_event(struct mtk_wed_wo *wo, int mtk_wed_mcu_send_msg(struct mtk_wed_wo *wo, int id, int cmd, const void *data, int len, bool wait_resp); int mtk_wed_mcu_init(struct mtk_wed_wo *wo); +int mtk_wed_wo_init(struct mtk_wed_hw *hw); +void mtk_wed_wo_deinit(struct mtk_wed_hw *hw); +int mtk_wed_wo_queue_tx_skb(struct mtk_wed_wo *dev, struct mtk_wed_wo_queue *q, + struct sk_buff *skb); #endif /* __MTK_WED_WO_H */ From 084d60ce0c6cef96024d53a58b92d7ff2d8b9318 Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Sat, 5 Nov 2022 23:36:20 +0100 Subject: [PATCH 0821/2002] net: ethernet: mtk_wed: rename tx_wdma array in rx_wdma Rename tx_wdma queue array in rx_wdma since this is rx side of wdma soc. Moreover rename mtk_wed_wdma_ring_setup routine in mtk_wed_wdma_rx_ring_setup() Signed-off-by: Lorenzo Bianconi Signed-off-by: David S. Miller --- drivers/net/ethernet/mediatek/mtk_wed.c | 16 ++++++++-------- include/linux/soc/mediatek/mtk_wed.h | 3 ++- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/drivers/net/ethernet/mediatek/mtk_wed.c b/drivers/net/ethernet/mediatek/mtk_wed.c index 9c9dd17332b6..e904596e67de 100644 --- a/drivers/net/ethernet/mediatek/mtk_wed.c +++ b/drivers/net/ethernet/mediatek/mtk_wed.c @@ -253,8 +253,8 @@ mtk_wed_free_tx_rings(struct mtk_wed_device *dev) for (i = 0; i < ARRAY_SIZE(dev->tx_ring); i++) mtk_wed_free_ring(dev, &dev->tx_ring[i]); - for (i = 0; i < ARRAY_SIZE(dev->tx_wdma); i++) - mtk_wed_free_ring(dev, &dev->tx_wdma[i]); + for (i = 0; i < ARRAY_SIZE(dev->rx_wdma); i++) + mtk_wed_free_ring(dev, &dev->rx_wdma[i]); } static void @@ -688,10 +688,10 @@ mtk_wed_ring_alloc(struct mtk_wed_device *dev, struct mtk_wed_ring *ring, } static int -mtk_wed_wdma_ring_setup(struct mtk_wed_device *dev, int idx, int size) +mtk_wed_wdma_rx_ring_setup(struct mtk_wed_device *dev, int idx, int size) { u32 desc_size = sizeof(struct mtk_wdma_desc) * dev->hw->version; - struct mtk_wed_ring *wdma = &dev->tx_wdma[idx]; + struct mtk_wed_ring *wdma = &dev->rx_wdma[idx]; if (mtk_wed_ring_alloc(dev, wdma, MTK_WED_WDMA_RING_SIZE, desc_size)) return -ENOMEM; @@ -805,9 +805,9 @@ mtk_wed_start(struct mtk_wed_device *dev, u32 irq_mask) { int i; - for (i = 0; i < ARRAY_SIZE(dev->tx_wdma); i++) - if (!dev->tx_wdma[i].desc) - mtk_wed_wdma_ring_setup(dev, i, 16); + for (i = 0; i < ARRAY_SIZE(dev->rx_wdma); i++) + if (!dev->rx_wdma[i].desc) + mtk_wed_wdma_rx_ring_setup(dev, i, 16); mtk_wed_hw_init(dev); mtk_wed_configure_irq(dev, irq_mask); @@ -916,7 +916,7 @@ mtk_wed_tx_ring_setup(struct mtk_wed_device *dev, int idx, void __iomem *regs) sizeof(*ring->desc))) return -ENOMEM; - if (mtk_wed_wdma_ring_setup(dev, idx, MTK_WED_WDMA_RING_SIZE)) + if (mtk_wed_wdma_rx_ring_setup(dev, idx, MTK_WED_WDMA_RING_SIZE)) return -ENOMEM; ring->reg_base = MTK_WED_RING_TX(idx); diff --git a/include/linux/soc/mediatek/mtk_wed.h b/include/linux/soc/mediatek/mtk_wed.h index 2cc2f1e43ba9..956978320f8b 100644 --- a/include/linux/soc/mediatek/mtk_wed.h +++ b/include/linux/soc/mediatek/mtk_wed.h @@ -7,6 +7,7 @@ #include #define MTK_WED_TX_QUEUES 2 +#define MTK_WED_RX_QUEUES 2 struct mtk_wed_hw; struct mtk_wdma_desc; @@ -66,7 +67,7 @@ struct mtk_wed_device { struct mtk_wed_ring tx_ring[MTK_WED_TX_QUEUES]; struct mtk_wed_ring txfree_ring; - struct mtk_wed_ring tx_wdma[MTK_WED_TX_QUEUES]; + struct mtk_wed_ring rx_wdma[MTK_WED_RX_QUEUES]; struct { int size; From 4c5de09eb0d05fc9e73ff8f0e052622f1f3df6d8 Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Sat, 5 Nov 2022 23:36:21 +0100 Subject: [PATCH 0822/2002] net: ethernet: mtk_wed: add configure wed wo support Enable RX Wireless Ethernet Dispatch available on MT7986 Soc. Tested-by: Daniel Golle Co-developed-by: Sujuan Chen Signed-off-by: Sujuan Chen Signed-off-by: Lorenzo Bianconi Signed-off-by: David S. Miller --- drivers/net/ethernet/mediatek/mtk_wed.c | 600 +++++++++++++++++-- drivers/net/ethernet/mediatek/mtk_wed.h | 19 + drivers/net/ethernet/mediatek/mtk_wed_mcu.c | 45 +- drivers/net/ethernet/mediatek/mtk_wed_regs.h | 128 +++- drivers/net/ethernet/mediatek/mtk_wed_wo.h | 28 + include/linux/soc/mediatek/mtk_wed.h | 76 ++- 6 files changed, 842 insertions(+), 54 deletions(-) diff --git a/drivers/net/ethernet/mediatek/mtk_wed.c b/drivers/net/ethernet/mediatek/mtk_wed.c index e904596e67de..7d8842378c2b 100644 --- a/drivers/net/ethernet/mediatek/mtk_wed.c +++ b/drivers/net/ethernet/mediatek/mtk_wed.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -23,6 +24,7 @@ #define MTK_WED_PKT_SIZE 1900 #define MTK_WED_BUF_SIZE 2048 #define MTK_WED_BUF_PER_PAGE (PAGE_SIZE / 2048) +#define MTK_WED_RX_RING_SIZE 1536 #define MTK_WED_TX_RING_SIZE 2048 #define MTK_WED_WDMA_RING_SIZE 1024 @@ -31,6 +33,10 @@ #define MTK_WED_PER_GROUP_PKT 128 #define MTK_WED_FBUF_SIZE 128 +#define MTK_WED_MIOD_CNT 16 +#define MTK_WED_FB_CMD_CNT 1024 +#define MTK_WED_RRO_QUE_CNT 8192 +#define MTK_WED_MIOD_ENTRY_CNT 128 static struct mtk_wed_hw *hw_list[2]; static DEFINE_MUTEX(hw_lock); @@ -65,12 +71,76 @@ wdma_set(struct mtk_wed_device *dev, u32 reg, u32 mask) wdma_m32(dev, reg, 0, mask); } +static void +wdma_clr(struct mtk_wed_device *dev, u32 reg, u32 mask) +{ + wdma_m32(dev, reg, mask, 0); +} + +static u32 +wifi_r32(struct mtk_wed_device *dev, u32 reg) +{ + return readl(dev->wlan.base + reg); +} + +static void +wifi_w32(struct mtk_wed_device *dev, u32 reg, u32 val) +{ + writel(val, dev->wlan.base + reg); +} + static u32 mtk_wed_read_reset(struct mtk_wed_device *dev) { return wed_r32(dev, MTK_WED_RESET); } +static u32 +mtk_wdma_read_reset(struct mtk_wed_device *dev) +{ + return wdma_r32(dev, MTK_WDMA_GLO_CFG); +} + +static void +mtk_wdma_rx_reset(struct mtk_wed_device *dev) +{ + u32 status, mask = MTK_WDMA_GLO_CFG_RX_DMA_BUSY; + int i; + + wdma_clr(dev, MTK_WDMA_GLO_CFG, MTK_WDMA_GLO_CFG_RX_DMA_EN); + if (readx_poll_timeout(mtk_wdma_read_reset, dev, status, + !(status & mask), 0, 1000)) + dev_err(dev->hw->dev, "rx reset failed\n"); + + for (i = 0; i < ARRAY_SIZE(dev->rx_wdma); i++) { + if (dev->rx_wdma[i].desc) + continue; + + wdma_w32(dev, + MTK_WDMA_RING_RX(i) + MTK_WED_RING_OFS_CPU_IDX, 0); + } +} + +static void +mtk_wdma_tx_reset(struct mtk_wed_device *dev) +{ + u32 status, mask = MTK_WDMA_GLO_CFG_TX_DMA_BUSY; + int i; + + wdma_clr(dev, MTK_WDMA_GLO_CFG, MTK_WDMA_GLO_CFG_TX_DMA_EN); + if (readx_poll_timeout(mtk_wdma_read_reset, dev, status, + !(status & mask), 0, 1000)) + dev_err(dev->hw->dev, "tx reset failed\n"); + + for (i = 0; i < ARRAY_SIZE(dev->tx_wdma); i++) { + if (dev->tx_wdma[i].desc) + continue; + + wdma_w32(dev, + MTK_WDMA_RING_TX(i) + MTK_WED_RING_OFS_CPU_IDX, 0); + } +} + static void mtk_wed_reset(struct mtk_wed_device *dev, u32 mask) { @@ -82,6 +152,54 @@ mtk_wed_reset(struct mtk_wed_device *dev, u32 mask) WARN_ON_ONCE(1); } +static u32 +mtk_wed_wo_read_status(struct mtk_wed_device *dev) +{ + return wed_r32(dev, MTK_WED_SCR0 + 4 * MTK_WED_DUMMY_CR_WO_STATUS); +} + +static void +mtk_wed_wo_reset(struct mtk_wed_device *dev) +{ + struct mtk_wed_wo *wo = dev->hw->wed_wo; + u8 state = MTK_WED_WO_STATE_DISABLE; + void __iomem *reg; + u32 val; + + mtk_wdma_tx_reset(dev); + mtk_wed_reset(dev, MTK_WED_RESET_WED); + + mtk_wed_mcu_send_msg(wo, MTK_WED_MODULE_ID_WO, + MTK_WED_WO_CMD_CHANGE_STATE, &state, + sizeof(state), false); + + if (readx_poll_timeout(mtk_wed_wo_read_status, dev, val, + val == MTK_WED_WOIF_DISABLE_DONE, + 100, MTK_WOCPU_TIMEOUT)) + dev_err(dev->hw->dev, "failed to disable wed-wo\n"); + + reg = ioremap(MTK_WED_WO_CPU_MCUSYS_RESET_ADDR, 4); + + val = readl(reg); + switch (dev->hw->index) { + case 0: + val |= MTK_WED_WO_CPU_WO0_MCUSYS_RESET_MASK; + writel(val, reg); + val &= ~MTK_WED_WO_CPU_WO0_MCUSYS_RESET_MASK; + writel(val, reg); + break; + case 1: + val |= MTK_WED_WO_CPU_WO1_MCUSYS_RESET_MASK; + writel(val, reg); + val &= ~MTK_WED_WO_CPU_WO1_MCUSYS_RESET_MASK; + writel(val, reg); + break; + default: + break; + } + iounmap(reg); +} + static struct mtk_wed_hw * mtk_wed_assign(struct mtk_wed_device *dev) { @@ -116,7 +234,7 @@ out: } static int -mtk_wed_buffer_alloc(struct mtk_wed_device *dev) +mtk_wed_tx_buffer_alloc(struct mtk_wed_device *dev) { struct mtk_wdma_desc *desc; dma_addr_t desc_phys; @@ -133,16 +251,16 @@ mtk_wed_buffer_alloc(struct mtk_wed_device *dev) if (!page_list) return -ENOMEM; - dev->buf_ring.size = ring_size; - dev->buf_ring.pages = page_list; + dev->tx_buf_ring.size = ring_size; + dev->tx_buf_ring.pages = page_list; desc = dma_alloc_coherent(dev->hw->dev, ring_size * sizeof(*desc), &desc_phys, GFP_KERNEL); if (!desc) return -ENOMEM; - dev->buf_ring.desc = desc; - dev->buf_ring.desc_phys = desc_phys; + dev->tx_buf_ring.desc = desc; + dev->tx_buf_ring.desc_phys = desc_phys; for (i = 0, page_idx = 0; i < ring_size; i += MTK_WED_BUF_PER_PAGE) { dma_addr_t page_phys, buf_phys; @@ -203,10 +321,10 @@ mtk_wed_buffer_alloc(struct mtk_wed_device *dev) } static void -mtk_wed_free_buffer(struct mtk_wed_device *dev) +mtk_wed_free_tx_buffer(struct mtk_wed_device *dev) { - struct mtk_wdma_desc *desc = dev->buf_ring.desc; - void **page_list = dev->buf_ring.pages; + struct mtk_wdma_desc *desc = dev->tx_buf_ring.desc; + void **page_list = dev->tx_buf_ring.pages; int page_idx; int i; @@ -216,7 +334,8 @@ mtk_wed_free_buffer(struct mtk_wed_device *dev) if (!desc) goto free_pagelist; - for (i = 0, page_idx = 0; i < dev->buf_ring.size; i += MTK_WED_BUF_PER_PAGE) { + for (i = 0, page_idx = 0; i < dev->tx_buf_ring.size; + i += MTK_WED_BUF_PER_PAGE) { void *page = page_list[page_idx++]; dma_addr_t buf_addr; @@ -229,13 +348,59 @@ mtk_wed_free_buffer(struct mtk_wed_device *dev) __free_page(page); } - dma_free_coherent(dev->hw->dev, dev->buf_ring.size * sizeof(*desc), - desc, dev->buf_ring.desc_phys); + dma_free_coherent(dev->hw->dev, dev->tx_buf_ring.size * sizeof(*desc), + desc, dev->tx_buf_ring.desc_phys); free_pagelist: kfree(page_list); } +static int +mtk_wed_rx_buffer_alloc(struct mtk_wed_device *dev) +{ + struct mtk_rxbm_desc *desc; + dma_addr_t desc_phys; + + dev->rx_buf_ring.size = dev->wlan.rx_nbuf; + desc = dma_alloc_coherent(dev->hw->dev, + dev->wlan.rx_nbuf * sizeof(*desc), + &desc_phys, GFP_KERNEL); + if (!desc) + return -ENOMEM; + + dev->rx_buf_ring.desc = desc; + dev->rx_buf_ring.desc_phys = desc_phys; + dev->wlan.init_rx_buf(dev, dev->wlan.rx_npkt); + + return 0; +} + +static void +mtk_wed_free_rx_buffer(struct mtk_wed_device *dev) +{ + struct mtk_rxbm_desc *desc = dev->rx_buf_ring.desc; + + if (!desc) + return; + + dev->wlan.release_rx_buf(dev); + dma_free_coherent(dev->hw->dev, dev->rx_buf_ring.size * sizeof(*desc), + desc, dev->rx_buf_ring.desc_phys); +} + +static void +mtk_wed_rx_buffer_hw_init(struct mtk_wed_device *dev) +{ + wed_w32(dev, MTK_WED_RX_BM_RX_DMAD, + FIELD_PREP(MTK_WED_RX_BM_RX_DMAD_SDL0, dev->wlan.rx_size)); + wed_w32(dev, MTK_WED_RX_BM_BASE, dev->rx_buf_ring.desc_phys); + wed_w32(dev, MTK_WED_RX_BM_INIT_PTR, MTK_WED_RX_BM_INIT_SW_TAIL | + FIELD_PREP(MTK_WED_RX_BM_SW_TAIL, dev->wlan.rx_npkt)); + wed_w32(dev, MTK_WED_RX_BM_DYN_ALLOC_TH, + FIELD_PREP(MTK_WED_RX_BM_DYN_ALLOC_TH_H, 0xffff)); + wed_set(dev, MTK_WED_CTRL, MTK_WED_CTRL_WED_RX_BM_EN); +} + static void mtk_wed_free_ring(struct mtk_wed_device *dev, struct mtk_wed_ring *ring) { @@ -246,6 +411,13 @@ mtk_wed_free_ring(struct mtk_wed_device *dev, struct mtk_wed_ring *ring) ring->desc, ring->desc_phys); } +static void +mtk_wed_free_rx_rings(struct mtk_wed_device *dev) +{ + mtk_wed_free_rx_buffer(dev); + mtk_wed_free_ring(dev, &dev->rro.ring); +} + static void mtk_wed_free_tx_rings(struct mtk_wed_device *dev) { @@ -291,6 +463,38 @@ mtk_wed_set_512_support(struct mtk_wed_device *dev, bool enable) } } +#define MTK_WFMDA_RX_DMA_EN BIT(2) +static void +mtk_wed_check_wfdma_rx_fill(struct mtk_wed_device *dev, int idx) +{ + u32 val; + int i; + + if (!(dev->rx_ring[idx].flags & MTK_WED_RING_CONFIGURED)) + return; /* queue is not configured by mt76 */ + + for (i = 0; i < 3; i++) { + u32 cur_idx; + + cur_idx = wed_r32(dev, + MTK_WED_WPDMA_RING_RX_DATA(idx) + + MTK_WED_RING_OFS_CPU_IDX); + if (cur_idx == MTK_WED_RX_RING_SIZE - 1) + break; + + usleep_range(100000, 200000); + } + + if (i == 3) { + dev_err(dev->hw->dev, "rx dma enable failed\n"); + return; + } + + val = wifi_r32(dev, dev->wlan.wpdma_rx_glo - dev->wlan.phy_base) | + MTK_WFMDA_RX_DMA_EN; + wifi_w32(dev, dev->wlan.wpdma_rx_glo - dev->wlan.phy_base, val); +} + static void mtk_wed_dma_disable(struct mtk_wed_device *dev) { @@ -304,20 +508,25 @@ mtk_wed_dma_disable(struct mtk_wed_device *dev) MTK_WED_GLO_CFG_TX_DMA_EN | MTK_WED_GLO_CFG_RX_DMA_EN); - wdma_m32(dev, MTK_WDMA_GLO_CFG, + wdma_clr(dev, MTK_WDMA_GLO_CFG, MTK_WDMA_GLO_CFG_TX_DMA_EN | MTK_WDMA_GLO_CFG_RX_INFO1_PRERES | - MTK_WDMA_GLO_CFG_RX_INFO2_PRERES, 0); + MTK_WDMA_GLO_CFG_RX_INFO2_PRERES); if (dev->hw->version == 1) { regmap_write(dev->hw->mirror, dev->hw->index * 4, 0); - wdma_m32(dev, MTK_WDMA_GLO_CFG, - MTK_WDMA_GLO_CFG_RX_INFO3_PRERES, 0); + wdma_clr(dev, MTK_WDMA_GLO_CFG, + MTK_WDMA_GLO_CFG_RX_INFO3_PRERES); } else { wed_clr(dev, MTK_WED_WPDMA_GLO_CFG, MTK_WED_WPDMA_GLO_CFG_RX_DRV_R0_PKT_PROC | MTK_WED_WPDMA_GLO_CFG_RX_DRV_R0_CRX_SYNC); + wed_clr(dev, MTK_WED_WPDMA_RX_D_GLO_CFG, + MTK_WED_WPDMA_RX_D_RX_DRV_EN); + wed_clr(dev, MTK_WED_WDMA_GLO_CFG, + MTK_WED_WDMA_GLO_CFG_TX_DDONE_CHK); + mtk_wed_set_512_support(dev, false); } } @@ -338,6 +547,13 @@ mtk_wed_stop(struct mtk_wed_device *dev) wdma_w32(dev, MTK_WDMA_INT_MASK, 0); wdma_w32(dev, MTK_WDMA_INT_GRP2, 0); wed_w32(dev, MTK_WED_WPDMA_INT_MASK, 0); + + if (dev->hw->version == 1) + return; + + wed_w32(dev, MTK_WED_EXT_INT_MASK1, 0); + wed_w32(dev, MTK_WED_EXT_INT_MASK2, 0); + wed_clr(dev, MTK_WED_CTRL, MTK_WED_CTRL_WED_RX_BM_EN); } static void @@ -353,11 +569,21 @@ mtk_wed_detach(struct mtk_wed_device *dev) wdma_w32(dev, MTK_WDMA_RESET_IDX, 0); mtk_wed_reset(dev, MTK_WED_RESET_WED); + if (mtk_wed_get_rx_capa(dev)) { + wdma_clr(dev, MTK_WDMA_GLO_CFG, MTK_WDMA_GLO_CFG_TX_DMA_EN); + wdma_w32(dev, MTK_WDMA_RESET_IDX, MTK_WDMA_RESET_IDX_TX); + wdma_w32(dev, MTK_WDMA_RESET_IDX, 0); + } - mtk_wed_free_buffer(dev); + mtk_wed_free_tx_buffer(dev); mtk_wed_free_tx_rings(dev); - if (hw->version != 1) + + if (mtk_wed_get_rx_capa(dev)) { + mtk_wed_wo_reset(dev); + mtk_wed_free_rx_rings(dev); mtk_wed_wo_deinit(hw); + mtk_wdma_rx_reset(dev); + } if (dev->wlan.bus_type == MTK_WED_BUS_PCIE) { struct device_node *wlan_node; @@ -434,10 +660,12 @@ mtk_wed_set_wpdma(struct mtk_wed_device *dev) } else { mtk_wed_bus_init(dev); - wed_w32(dev, MTK_WED_WPDMA_CFG_BASE, dev->wlan.wpdma_int); - wed_w32(dev, MTK_WED_WPDMA_CFG_INT_MASK, dev->wlan.wpdma_mask); - wed_w32(dev, MTK_WED_WPDMA_CFG_TX, dev->wlan.wpdma_tx); - wed_w32(dev, MTK_WED_WPDMA_CFG_TX_FREE, dev->wlan.wpdma_txfree); + wed_w32(dev, MTK_WED_WPDMA_CFG_BASE, dev->wlan.wpdma_int); + wed_w32(dev, MTK_WED_WPDMA_CFG_INT_MASK, dev->wlan.wpdma_mask); + wed_w32(dev, MTK_WED_WPDMA_CFG_TX, dev->wlan.wpdma_tx); + wed_w32(dev, MTK_WED_WPDMA_CFG_TX_FREE, dev->wlan.wpdma_txfree); + wed_w32(dev, MTK_WED_WPDMA_RX_GLO_CFG, dev->wlan.wpdma_rx_glo); + wed_w32(dev, MTK_WED_WPDMA_RX_RING, dev->wlan.wpdma_rx); } } @@ -487,6 +715,132 @@ mtk_wed_hw_init_early(struct mtk_wed_device *dev) } } +static int +mtk_wed_rro_ring_alloc(struct mtk_wed_device *dev, struct mtk_wed_ring *ring, + int size) +{ + ring->desc = dma_alloc_coherent(dev->hw->dev, + size * sizeof(*ring->desc), + &ring->desc_phys, GFP_KERNEL); + if (!ring->desc) + return -ENOMEM; + + ring->desc_size = sizeof(*ring->desc); + ring->size = size; + memset(ring->desc, 0, size); + + return 0; +} + +#define MTK_WED_MIOD_COUNT (MTK_WED_MIOD_ENTRY_CNT * MTK_WED_MIOD_CNT) +static int +mtk_wed_rro_alloc(struct mtk_wed_device *dev) +{ + struct reserved_mem *rmem; + struct device_node *np; + int index; + + index = of_property_match_string(dev->hw->node, "memory-region-names", + "wo-dlm"); + if (index < 0) + return index; + + np = of_parse_phandle(dev->hw->node, "memory-region", index); + if (!np) + return -ENODEV; + + rmem = of_reserved_mem_lookup(np); + of_node_put(np); + + if (!rmem) + return -ENODEV; + + dev->rro.miod_phys = rmem->base; + dev->rro.fdbk_phys = MTK_WED_MIOD_COUNT + dev->rro.miod_phys; + + return mtk_wed_rro_ring_alloc(dev, &dev->rro.ring, + MTK_WED_RRO_QUE_CNT); +} + +static int +mtk_wed_rro_cfg(struct mtk_wed_device *dev) +{ + struct mtk_wed_wo *wo = dev->hw->wed_wo; + struct { + struct { + __le32 base; + __le32 cnt; + __le32 unit; + } ring[2]; + __le32 wed; + u8 version; + } req = { + .ring[0] = { + .base = cpu_to_le32(MTK_WED_WOCPU_VIEW_MIOD_BASE), + .cnt = cpu_to_le32(MTK_WED_MIOD_CNT), + .unit = cpu_to_le32(MTK_WED_MIOD_ENTRY_CNT), + }, + .ring[1] = { + .base = cpu_to_le32(MTK_WED_WOCPU_VIEW_MIOD_BASE + + MTK_WED_MIOD_COUNT), + .cnt = cpu_to_le32(MTK_WED_FB_CMD_CNT), + .unit = cpu_to_le32(4), + }, + }; + + return mtk_wed_mcu_send_msg(wo, MTK_WED_MODULE_ID_WO, + MTK_WED_WO_CMD_WED_CFG, + &req, sizeof(req), true); +} + +static void +mtk_wed_rro_hw_init(struct mtk_wed_device *dev) +{ + wed_w32(dev, MTK_WED_RROQM_MIOD_CFG, + FIELD_PREP(MTK_WED_RROQM_MIOD_MID_DW, 0x70 >> 2) | + FIELD_PREP(MTK_WED_RROQM_MIOD_MOD_DW, 0x10 >> 2) | + FIELD_PREP(MTK_WED_RROQM_MIOD_ENTRY_DW, + MTK_WED_MIOD_ENTRY_CNT >> 2)); + + wed_w32(dev, MTK_WED_RROQM_MIOD_CTRL0, dev->rro.miod_phys); + wed_w32(dev, MTK_WED_RROQM_MIOD_CTRL1, + FIELD_PREP(MTK_WED_RROQM_MIOD_CNT, MTK_WED_MIOD_CNT)); + wed_w32(dev, MTK_WED_RROQM_FDBK_CTRL0, dev->rro.fdbk_phys); + wed_w32(dev, MTK_WED_RROQM_FDBK_CTRL1, + FIELD_PREP(MTK_WED_RROQM_FDBK_CNT, MTK_WED_FB_CMD_CNT)); + wed_w32(dev, MTK_WED_RROQM_FDBK_CTRL2, 0); + wed_w32(dev, MTK_WED_RROQ_BASE_L, dev->rro.ring.desc_phys); + + wed_set(dev, MTK_WED_RROQM_RST_IDX, + MTK_WED_RROQM_RST_IDX_MIOD | + MTK_WED_RROQM_RST_IDX_FDBK); + + wed_w32(dev, MTK_WED_RROQM_RST_IDX, 0); + wed_w32(dev, MTK_WED_RROQM_MIOD_CTRL2, MTK_WED_MIOD_CNT - 1); + wed_set(dev, MTK_WED_CTRL, MTK_WED_CTRL_RX_RRO_QM_EN); +} + +static void +mtk_wed_route_qm_hw_init(struct mtk_wed_device *dev) +{ + wed_w32(dev, MTK_WED_RESET, MTK_WED_RESET_RX_ROUTE_QM); + + for (;;) { + usleep_range(100, 200); + if (!(wed_r32(dev, MTK_WED_RESET) & MTK_WED_RESET_RX_ROUTE_QM)) + break; + } + + /* configure RX_ROUTE_QM */ + wed_clr(dev, MTK_WED_RTQM_GLO_CFG, MTK_WED_RTQM_Q_RST); + wed_clr(dev, MTK_WED_RTQM_GLO_CFG, MTK_WED_RTQM_TXDMAD_FPORT); + wed_set(dev, MTK_WED_RTQM_GLO_CFG, + FIELD_PREP(MTK_WED_RTQM_TXDMAD_FPORT, 0x3 + dev->hw->index)); + wed_clr(dev, MTK_WED_RTQM_GLO_CFG, MTK_WED_RTQM_Q_RST); + /* enable RX_ROUTE_QM */ + wed_set(dev, MTK_WED_CTRL, MTK_WED_CTRL_RX_ROUTE_QM_EN); +} + static void mtk_wed_hw_init(struct mtk_wed_device *dev) { @@ -498,11 +852,11 @@ mtk_wed_hw_init(struct mtk_wed_device *dev) wed_w32(dev, MTK_WED_TX_BM_CTRL, MTK_WED_TX_BM_CTRL_PAUSE | FIELD_PREP(MTK_WED_TX_BM_CTRL_VLD_GRP_NUM, - dev->buf_ring.size / 128) | + dev->tx_buf_ring.size / 128) | FIELD_PREP(MTK_WED_TX_BM_CTRL_RSV_GRP_NUM, MTK_WED_TX_RING_SIZE / 256)); - wed_w32(dev, MTK_WED_TX_BM_BASE, dev->buf_ring.desc_phys); + wed_w32(dev, MTK_WED_TX_BM_BASE, dev->tx_buf_ring.desc_phys); wed_w32(dev, MTK_WED_TX_BM_BUF_LEN, MTK_WED_PKT_SIZE); @@ -529,9 +883,9 @@ mtk_wed_hw_init(struct mtk_wed_device *dev) wed_w32(dev, MTK_WED_TX_TKID_CTRL, MTK_WED_TX_TKID_CTRL_PAUSE | FIELD_PREP(MTK_WED_TX_TKID_CTRL_VLD_GRP_NUM, - dev->buf_ring.size / 128) | + dev->tx_buf_ring.size / 128) | FIELD_PREP(MTK_WED_TX_TKID_CTRL_RSV_GRP_NUM, - dev->buf_ring.size / 128)); + dev->tx_buf_ring.size / 128)); wed_w32(dev, MTK_WED_TX_TKID_DYN_THR, FIELD_PREP(MTK_WED_TX_TKID_DYN_THR_LO, 0) | MTK_WED_TX_TKID_DYN_THR_HI); @@ -539,18 +893,28 @@ mtk_wed_hw_init(struct mtk_wed_device *dev) mtk_wed_reset(dev, MTK_WED_RESET_TX_BM); - if (dev->hw->version == 1) + if (dev->hw->version == 1) { wed_set(dev, MTK_WED_CTRL, MTK_WED_CTRL_WED_TX_BM_EN | MTK_WED_CTRL_WED_TX_FREE_AGENT_EN); - else + } else { wed_clr(dev, MTK_WED_TX_TKID_CTRL, MTK_WED_TX_TKID_CTRL_PAUSE); + /* rx hw init */ + wed_w32(dev, MTK_WED_WPDMA_RX_D_RST_IDX, + MTK_WED_WPDMA_RX_D_RST_CRX_IDX | + MTK_WED_WPDMA_RX_D_RST_DRV_IDX); + wed_w32(dev, MTK_WED_WPDMA_RX_D_RST_IDX, 0); + + mtk_wed_rx_buffer_hw_init(dev); + mtk_wed_rro_hw_init(dev); + mtk_wed_route_qm_hw_init(dev); + } wed_clr(dev, MTK_WED_TX_BM_CTRL, MTK_WED_TX_BM_CTRL_PAUSE); } static void -mtk_wed_ring_reset(struct mtk_wed_ring *ring, int size) +mtk_wed_ring_reset(struct mtk_wed_ring *ring, int size, bool tx) { void *head = (void *)ring->desc; int i; @@ -560,7 +924,10 @@ mtk_wed_ring_reset(struct mtk_wed_ring *ring, int size) desc = (struct mtk_wdma_desc *)(head + i * ring->desc_size); desc->buf0 = 0; - desc->ctrl = cpu_to_le32(MTK_WDMA_DESC_CTRL_DMA_DONE); + if (tx) + desc->ctrl = cpu_to_le32(MTK_WDMA_DESC_CTRL_DMA_DONE); + else + desc->ctrl = cpu_to_le32(MTK_WFDMA_DESC_CTRL_TO_HOST); desc->buf1 = 0; desc->info = 0; } @@ -616,7 +983,8 @@ mtk_wed_reset_dma(struct mtk_wed_device *dev) if (!dev->tx_ring[i].desc) continue; - mtk_wed_ring_reset(&dev->tx_ring[i], MTK_WED_TX_RING_SIZE); + mtk_wed_ring_reset(&dev->tx_ring[i], MTK_WED_TX_RING_SIZE, + true); } if (mtk_wed_poll_busy(dev)) @@ -634,6 +1002,9 @@ mtk_wed_reset_dma(struct mtk_wed_device *dev) wdma_w32(dev, MTK_WDMA_RESET_IDX, MTK_WDMA_RESET_IDX_RX); wdma_w32(dev, MTK_WDMA_RESET_IDX, 0); + if (mtk_wed_get_rx_capa(dev)) + mtk_wdma_rx_reset(dev); + if (busy) { mtk_wed_reset(dev, MTK_WED_RESET_WDMA_INT_AGENT); mtk_wed_reset(dev, MTK_WED_RESET_WDMA_RX_DRV); @@ -668,12 +1039,11 @@ mtk_wed_reset_dma(struct mtk_wed_device *dev) MTK_WED_WPDMA_RESET_IDX_RX); wed_w32(dev, MTK_WED_WPDMA_RESET_IDX, 0); } - } static int mtk_wed_ring_alloc(struct mtk_wed_device *dev, struct mtk_wed_ring *ring, - int size, u32 desc_size) + int size, u32 desc_size, bool tx) { ring->desc = dma_alloc_coherent(dev->hw->dev, size * desc_size, &ring->desc_phys, GFP_KERNEL); @@ -682,7 +1052,7 @@ mtk_wed_ring_alloc(struct mtk_wed_device *dev, struct mtk_wed_ring *ring, ring->desc_size = desc_size; ring->size = size; - mtk_wed_ring_reset(ring, size); + mtk_wed_ring_reset(ring, size, tx); return 0; } @@ -691,9 +1061,14 @@ static int mtk_wed_wdma_rx_ring_setup(struct mtk_wed_device *dev, int idx, int size) { u32 desc_size = sizeof(struct mtk_wdma_desc) * dev->hw->version; - struct mtk_wed_ring *wdma = &dev->rx_wdma[idx]; + struct mtk_wed_ring *wdma; - if (mtk_wed_ring_alloc(dev, wdma, MTK_WED_WDMA_RING_SIZE, desc_size)) + if (idx >= ARRAY_SIZE(dev->rx_wdma)) + return -EINVAL; + + wdma = &dev->rx_wdma[idx]; + if (mtk_wed_ring_alloc(dev, wdma, MTK_WED_WDMA_RING_SIZE, desc_size, + true)) return -ENOMEM; wdma_w32(dev, MTK_WDMA_RING_RX(idx) + MTK_WED_RING_OFS_BASE, @@ -710,6 +1085,60 @@ mtk_wed_wdma_rx_ring_setup(struct mtk_wed_device *dev, int idx, int size) return 0; } +static int +mtk_wed_wdma_tx_ring_setup(struct mtk_wed_device *dev, int idx, int size) +{ + u32 desc_size = sizeof(struct mtk_wdma_desc) * dev->hw->version; + struct mtk_wed_ring *wdma; + + if (idx >= ARRAY_SIZE(dev->tx_wdma)) + return -EINVAL; + + wdma = &dev->tx_wdma[idx]; + if (mtk_wed_ring_alloc(dev, wdma, MTK_WED_WDMA_RING_SIZE, desc_size, + true)) + return -ENOMEM; + + wdma_w32(dev, MTK_WDMA_RING_TX(idx) + MTK_WED_RING_OFS_BASE, + wdma->desc_phys); + wdma_w32(dev, MTK_WDMA_RING_TX(idx) + MTK_WED_RING_OFS_COUNT, + size); + wdma_w32(dev, MTK_WDMA_RING_TX(idx) + MTK_WED_RING_OFS_CPU_IDX, 0); + wdma_w32(dev, MTK_WDMA_RING_TX(idx) + MTK_WED_RING_OFS_DMA_IDX, 0); + + if (!idx) { + wed_w32(dev, MTK_WED_WDMA_RING_TX + MTK_WED_RING_OFS_BASE, + wdma->desc_phys); + wed_w32(dev, MTK_WED_WDMA_RING_TX + MTK_WED_RING_OFS_COUNT, + size); + wed_w32(dev, MTK_WED_WDMA_RING_TX + MTK_WED_RING_OFS_CPU_IDX, + 0); + wed_w32(dev, MTK_WED_WDMA_RING_TX + MTK_WED_RING_OFS_DMA_IDX, + 0); + } + + return 0; +} + +static void +mtk_wed_ppe_check(struct mtk_wed_device *dev, struct sk_buff *skb, + u32 reason, u32 hash) +{ + struct mtk_eth *eth = dev->hw->eth; + struct ethhdr *eh; + + if (!skb) + return; + + if (reason != MTK_PPE_CPU_REASON_HIT_UNBIND_RATE_REACHED) + return; + + skb_set_mac_header(skb, 0); + eh = eth_hdr(skb); + skb->protocol = eh->h_proto; + mtk_ppe_check_skb(eth->ppe[dev->hw->index], skb, hash); +} + static void mtk_wed_configure_irq(struct mtk_wed_device *dev, u32 irq_mask) { @@ -732,6 +1161,8 @@ mtk_wed_configure_irq(struct mtk_wed_device *dev, u32 irq_mask) wed_clr(dev, MTK_WED_WDMA_INT_CTRL, wdma_mask); } else { + wdma_mask |= FIELD_PREP(MTK_WDMA_INT_MASK_TX_DONE, + GENMASK(1, 0)); /* initail tx interrupt trigger */ wed_w32(dev, MTK_WED_WPDMA_INT_CTRL_TX, MTK_WED_WPDMA_INT_CTRL_TX0_DONE_EN | @@ -750,6 +1181,16 @@ mtk_wed_configure_irq(struct mtk_wed_device *dev, u32 irq_mask) FIELD_PREP(MTK_WED_WPDMA_INT_CTRL_TX_FREE_DONE_TRIG, dev->wlan.txfree_tbit)); + wed_w32(dev, MTK_WED_WPDMA_INT_CTRL_RX, + MTK_WED_WPDMA_INT_CTRL_RX0_EN | + MTK_WED_WPDMA_INT_CTRL_RX0_CLR | + MTK_WED_WPDMA_INT_CTRL_RX1_EN | + MTK_WED_WPDMA_INT_CTRL_RX1_CLR | + FIELD_PREP(MTK_WED_WPDMA_INT_CTRL_RX0_DONE_TRIG, + dev->wlan.rx_tbit[0]) | + FIELD_PREP(MTK_WED_WPDMA_INT_CTRL_RX1_DONE_TRIG, + dev->wlan.rx_tbit[1])); + wed_w32(dev, MTK_WED_WDMA_INT_CLR, wdma_mask); wed_set(dev, MTK_WED_WDMA_INT_CTRL, FIELD_PREP(MTK_WED_WDMA_INT_CTRL_POLL_SRC_SEL, @@ -787,9 +1228,15 @@ mtk_wed_dma_enable(struct mtk_wed_device *dev) wdma_set(dev, MTK_WDMA_GLO_CFG, MTK_WDMA_GLO_CFG_RX_INFO3_PRERES); } else { + int i; + wed_set(dev, MTK_WED_WPDMA_CTRL, MTK_WED_WPDMA_CTRL_SDL1_FIXED); + wed_set(dev, MTK_WED_WDMA_GLO_CFG, + MTK_WED_WDMA_GLO_CFG_TX_DRV_EN | + MTK_WED_WDMA_GLO_CFG_TX_DDONE_CHK); + wed_set(dev, MTK_WED_WPDMA_GLO_CFG, MTK_WED_WPDMA_GLO_CFG_RX_DRV_R0_PKT_PROC | MTK_WED_WPDMA_GLO_CFG_RX_DRV_R0_CRX_SYNC); @@ -797,6 +1244,15 @@ mtk_wed_dma_enable(struct mtk_wed_device *dev) wed_clr(dev, MTK_WED_WPDMA_GLO_CFG, MTK_WED_WPDMA_GLO_CFG_TX_TKID_KEEP | MTK_WED_WPDMA_GLO_CFG_TX_DMAD_DW3_PREV); + + wed_set(dev, MTK_WED_WPDMA_RX_D_GLO_CFG, + MTK_WED_WPDMA_RX_D_RX_DRV_EN | + FIELD_PREP(MTK_WED_WPDMA_RX_D_RXD_READ_LEN, 0x18) | + FIELD_PREP(MTK_WED_WPDMA_RX_D_INIT_PHASE_RXEN_SEL, + 0x2)); + + for (i = 0; i < MTK_WED_RX_QUEUES; i++) + mtk_wed_check_wfdma_rx_fill(dev, i); } } @@ -822,7 +1278,19 @@ mtk_wed_start(struct mtk_wed_device *dev, u32 irq_mask) val |= BIT(0) | (BIT(1) * !!dev->hw->index); regmap_write(dev->hw->mirror, dev->hw->index * 4, val); } else { - mtk_wed_set_512_support(dev, true); + /* driver set mid ready and only once */ + wed_w32(dev, MTK_WED_EXT_INT_MASK1, + MTK_WED_EXT_INT_STATUS_WPDMA_MID_RDY); + wed_w32(dev, MTK_WED_EXT_INT_MASK2, + MTK_WED_EXT_INT_STATUS_WPDMA_MID_RDY); + + wed_r32(dev, MTK_WED_EXT_INT_MASK1); + wed_r32(dev, MTK_WED_EXT_INT_MASK2); + + if (mtk_wed_rro_cfg(dev)) + return; + + mtk_wed_set_512_support(dev, dev->wlan.wcid_512); } mtk_wed_dma_enable(dev); @@ -856,7 +1324,7 @@ mtk_wed_attach(struct mtk_wed_device *dev) if (!hw) { module_put(THIS_MODULE); ret = -ENODEV; - goto out; + goto unlock; } device = dev->wlan.bus_type == MTK_WED_BUS_PCIE @@ -869,15 +1337,24 @@ mtk_wed_attach(struct mtk_wed_device *dev) dev->dev = hw->dev; dev->irq = hw->irq; dev->wdma_idx = hw->index; + dev->version = hw->version; if (hw->eth->dma_dev == hw->eth->dev && of_dma_is_coherent(hw->eth->dev->of_node)) mtk_eth_set_dma_device(hw->eth, hw->dev); - ret = mtk_wed_buffer_alloc(dev); - if (ret) { - mtk_wed_detach(dev); + ret = mtk_wed_tx_buffer_alloc(dev); + if (ret) goto out; + + if (mtk_wed_get_rx_capa(dev)) { + ret = mtk_wed_rx_buffer_alloc(dev); + if (ret) + goto out; + + ret = mtk_wed_rro_alloc(dev); + if (ret) + goto out; } mtk_wed_hw_init_early(dev); @@ -886,8 +1363,10 @@ mtk_wed_attach(struct mtk_wed_device *dev) BIT(hw->index), 0); else ret = mtk_wed_wo_init(hw); - out: + if (ret) + mtk_wed_detach(dev); +unlock: mutex_unlock(&hw_lock); return ret; @@ -910,10 +1389,11 @@ mtk_wed_tx_ring_setup(struct mtk_wed_device *dev, int idx, void __iomem *regs) * WDMA RX. */ - BUG_ON(idx >= ARRAY_SIZE(dev->tx_ring)); + if (WARN_ON(idx >= ARRAY_SIZE(dev->tx_ring))) + return -EINVAL; if (mtk_wed_ring_alloc(dev, ring, MTK_WED_TX_RING_SIZE, - sizeof(*ring->desc))) + sizeof(*ring->desc), true)) return -ENOMEM; if (mtk_wed_wdma_rx_ring_setup(dev, idx, MTK_WED_WDMA_RING_SIZE)) @@ -960,6 +1440,37 @@ mtk_wed_txfree_ring_setup(struct mtk_wed_device *dev, void __iomem *regs) return 0; } +static int +mtk_wed_rx_ring_setup(struct mtk_wed_device *dev, int idx, void __iomem *regs) +{ + struct mtk_wed_ring *ring = &dev->rx_ring[idx]; + + if (WARN_ON(idx >= ARRAY_SIZE(dev->rx_ring))) + return -EINVAL; + + if (mtk_wed_ring_alloc(dev, ring, MTK_WED_RX_RING_SIZE, + sizeof(*ring->desc), false)) + return -ENOMEM; + + if (mtk_wed_wdma_tx_ring_setup(dev, idx, MTK_WED_WDMA_RING_SIZE)) + return -ENOMEM; + + ring->reg_base = MTK_WED_RING_RX_DATA(idx); + ring->wpdma = regs; + ring->flags |= MTK_WED_RING_CONFIGURED; + + /* WPDMA -> WED */ + wpdma_rx_w32(dev, idx, MTK_WED_RING_OFS_BASE, ring->desc_phys); + wpdma_rx_w32(dev, idx, MTK_WED_RING_OFS_COUNT, MTK_WED_RX_RING_SIZE); + + wed_w32(dev, MTK_WED_WPDMA_RING_RX_DATA(idx) + MTK_WED_RING_OFS_BASE, + ring->desc_phys); + wed_w32(dev, MTK_WED_WPDMA_RING_RX_DATA(idx) + MTK_WED_RING_OFS_COUNT, + MTK_WED_RX_RING_SIZE); + + return 0; +} + static u32 mtk_wed_irq_get(struct mtk_wed_device *dev, u32 mask) { @@ -1056,7 +1567,9 @@ void mtk_wed_add_hw(struct device_node *np, struct mtk_eth *eth, static const struct mtk_wed_ops wed_ops = { .attach = mtk_wed_attach, .tx_ring_setup = mtk_wed_tx_ring_setup, + .rx_ring_setup = mtk_wed_rx_ring_setup, .txfree_ring_setup = mtk_wed_txfree_ring_setup, + .msg_update = mtk_wed_mcu_msg_update, .start = mtk_wed_start, .stop = mtk_wed_stop, .reset_dma = mtk_wed_reset_dma, @@ -1065,6 +1578,7 @@ void mtk_wed_add_hw(struct device_node *np, struct mtk_eth *eth, .irq_get = mtk_wed_irq_get, .irq_set_mask = mtk_wed_irq_set_mask, .detach = mtk_wed_detach, + .ppe_check = mtk_wed_ppe_check, }; struct device_node *eth_np = eth->dev->of_node; struct platform_device *pdev; diff --git a/drivers/net/ethernet/mediatek/mtk_wed.h b/drivers/net/ethernet/mediatek/mtk_wed.h index af656fd31ff9..e012b8a82133 100644 --- a/drivers/net/ethernet/mediatek/mtk_wed.h +++ b/drivers/net/ethernet/mediatek/mtk_wed.h @@ -86,6 +86,24 @@ wpdma_tx_w32(struct mtk_wed_device *dev, int ring, u32 reg, u32 val) writel(val, dev->tx_ring[ring].wpdma + reg); } +static inline u32 +wpdma_rx_r32(struct mtk_wed_device *dev, int ring, u32 reg) +{ + if (!dev->rx_ring[ring].wpdma) + return 0; + + return readl(dev->rx_ring[ring].wpdma + reg); +} + +static inline void +wpdma_rx_w32(struct mtk_wed_device *dev, int ring, u32 reg, u32 val) +{ + if (!dev->rx_ring[ring].wpdma) + return; + + writel(val, dev->rx_ring[ring].wpdma + reg); +} + static inline u32 wpdma_txfree_r32(struct mtk_wed_device *dev, u32 reg) { @@ -128,6 +146,7 @@ static inline int mtk_wed_flow_add(int index) static inline void mtk_wed_flow_remove(int index) { } + #endif #ifdef CONFIG_DEBUG_FS diff --git a/drivers/net/ethernet/mediatek/mtk_wed_mcu.c b/drivers/net/ethernet/mediatek/mtk_wed_mcu.c index b657328a1d9d..f9539e6233c9 100644 --- a/drivers/net/ethernet/mediatek/mtk_wed_mcu.c +++ b/drivers/net/ethernet/mediatek/mtk_wed_mcu.c @@ -10,6 +10,7 @@ #include #include #include +#include #include "mtk_wed_regs.h" #include "mtk_wed_wo.h" @@ -60,24 +61,37 @@ void mtk_wed_mcu_rx_event(struct mtk_wed_wo *wo, struct sk_buff *skb) wake_up(&wo->mcu.wait); } +static void +mtk_wed_update_rx_stats(struct mtk_wed_device *wed, struct sk_buff *skb) +{ + u32 count = get_unaligned_le32(skb->data); + struct mtk_wed_wo_rx_stats *stats; + int i; + + if (count * sizeof(*stats) > skb->len - sizeof(u32)) + return; + + stats = (struct mtk_wed_wo_rx_stats *)(skb->data + sizeof(u32)); + for (i = 0 ; i < count ; i++) + wed->wlan.update_wo_rx_stats(wed, &stats[i]); +} + void mtk_wed_mcu_rx_unsolicited_event(struct mtk_wed_wo *wo, struct sk_buff *skb) { struct mtk_wed_mcu_hdr *hdr = (struct mtk_wed_mcu_hdr *)skb->data; - switch (hdr->cmd) { - case MTK_WED_WO_EVT_LOG_DUMP: { - const char *msg = (const char *)(skb->data + sizeof(*hdr)); + skb_pull(skb, sizeof(*hdr)); - dev_notice(wo->hw->dev, "%s\n", msg); + switch (hdr->cmd) { + case MTK_WED_WO_EVT_LOG_DUMP: + dev_notice(wo->hw->dev, "%s\n", skb->data); break; - } case MTK_WED_WO_EVT_PROFILING: { - struct mtk_wed_wo_log_info *info; - u32 count = (skb->len - sizeof(*hdr)) / sizeof(*info); + struct mtk_wed_wo_log_info *info = (void *)skb->data; + u32 count = skb->len / sizeof(*info); int i; - info = (struct mtk_wed_wo_log_info *)(skb->data + sizeof(*hdr)); for (i = 0 ; i < count ; i++) dev_notice(wo->hw->dev, "SN:%u latency: total=%u, rro:%u, mod:%u\n", @@ -88,6 +102,7 @@ void mtk_wed_mcu_rx_unsolicited_event(struct mtk_wed_wo *wo, break; } case MTK_WED_WO_EVT_RXCNT_INFO: + mtk_wed_update_rx_stats(wo->hw->wed_dev, skb); break; default: break; @@ -144,6 +159,8 @@ mtk_wed_mcu_parse_response(struct mtk_wed_wo *wo, struct sk_buff *skb, skb_pull(skb, sizeof(*hdr)); switch (cmd) { case MTK_WED_WO_CMD_RXCNT_INFO: + mtk_wed_update_rx_stats(wo->hw->wed_dev, skb); + break; default: break; } @@ -182,6 +199,18 @@ unlock: return ret; } +int mtk_wed_mcu_msg_update(struct mtk_wed_device *dev, int id, void *data, + int len) +{ + struct mtk_wed_wo *wo = dev->hw->wed_wo; + + if (dev->hw->version == 1) + return 0; + + return mtk_wed_mcu_send_msg(wo, MTK_WED_MODULE_ID_WO, id, data, len, + true); +} + static int mtk_wed_get_memory_region(struct mtk_wed_wo *wo, struct mtk_wed_wo_memory_region *region) diff --git a/drivers/net/ethernet/mediatek/mtk_wed_regs.h b/drivers/net/ethernet/mediatek/mtk_wed_regs.h index c940b3bb215b..9e39dace95eb 100644 --- a/drivers/net/ethernet/mediatek/mtk_wed_regs.h +++ b/drivers/net/ethernet/mediatek/mtk_wed_regs.h @@ -4,6 +4,7 @@ #ifndef __MTK_WED_REGS_H #define __MTK_WED_REGS_H +#define MTK_WFDMA_DESC_CTRL_TO_HOST BIT(8) #define MTK_WDMA_DESC_CTRL_LEN1 GENMASK(14, 0) #define MTK_WDMA_DESC_CTRL_LEN1_V2 GENMASK(13, 0) #define MTK_WDMA_DESC_CTRL_LAST_SEG1 BIT(15) @@ -28,6 +29,8 @@ struct mtk_wdma_desc { #define MTK_WED_RESET_WED_TX_DMA BIT(12) #define MTK_WED_RESET_WDMA_RX_DRV BIT(17) #define MTK_WED_RESET_WDMA_INT_AGENT BIT(19) +#define MTK_WED_RESET_RX_RRO_QM BIT(20) +#define MTK_WED_RESET_RX_ROUTE_QM BIT(21) #define MTK_WED_RESET_WED BIT(31) #define MTK_WED_CTRL 0x00c @@ -39,8 +42,12 @@ struct mtk_wdma_desc { #define MTK_WED_CTRL_WED_TX_BM_BUSY BIT(9) #define MTK_WED_CTRL_WED_TX_FREE_AGENT_EN BIT(10) #define MTK_WED_CTRL_WED_TX_FREE_AGENT_BUSY BIT(11) -#define MTK_WED_CTRL_RESERVE_EN BIT(12) -#define MTK_WED_CTRL_RESERVE_BUSY BIT(13) +#define MTK_WED_CTRL_WED_RX_BM_EN BIT(12) +#define MTK_WED_CTRL_WED_RX_BM_BUSY BIT(13) +#define MTK_WED_CTRL_RX_RRO_QM_EN BIT(14) +#define MTK_WED_CTRL_RX_RRO_QM_BUSY BIT(15) +#define MTK_WED_CTRL_RX_ROUTE_QM_EN BIT(16) +#define MTK_WED_CTRL_RX_ROUTE_QM_BUSY BIT(17) #define MTK_WED_CTRL_FINAL_DIDX_READ BIT(24) #define MTK_WED_CTRL_ETH_DMAD_FMT BIT(25) #define MTK_WED_CTRL_MIB_READ_CLEAR BIT(28) @@ -62,6 +69,9 @@ struct mtk_wdma_desc { #define MTK_WED_EXT_INT_STATUS_TX_DMA_R_RESP_ERR BIT(22) #define MTK_WED_EXT_INT_STATUS_TX_DMA_W_RESP_ERR BIT(23) #define MTK_WED_EXT_INT_STATUS_RX_DRV_DMA_RECYCLE BIT(24) +#define MTK_WED_EXT_INT_STATUS_RX_DRV_GET_BM_DMAD_SKIP BIT(25) +#define MTK_WED_EXT_INT_STATUS_WPDMA_RX_D_DRV_ERR BIT(26) +#define MTK_WED_EXT_INT_STATUS_WPDMA_MID_RDY BIT(27) #define MTK_WED_EXT_INT_STATUS_ERROR_MASK (MTK_WED_EXT_INT_STATUS_TF_LEN_ERR | \ MTK_WED_EXT_INT_STATUS_TKID_WO_PYLD | \ MTK_WED_EXT_INT_STATUS_TKID_TITO_INVALID | \ @@ -71,6 +81,8 @@ struct mtk_wdma_desc { MTK_WED_EXT_INT_STATUS_TX_DMA_R_RESP_ERR) #define MTK_WED_EXT_INT_MASK 0x028 +#define MTK_WED_EXT_INT_MASK1 0x02c +#define MTK_WED_EXT_INT_MASK2 0x030 #define MTK_WED_STATUS 0x060 #define MTK_WED_STATUS_TX GENMASK(15, 8) @@ -151,6 +163,7 @@ struct mtk_wdma_desc { #define MTK_WED_RING_TX(_n) (0x300 + (_n) * 0x10) #define MTK_WED_RING_RX(_n) (0x400 + (_n) * 0x10) +#define MTK_WED_RING_RX_DATA(_n) (0x420 + (_n) * 0x10) #define MTK_WED_SCR0 0x3c0 #define MTK_WED_WPDMA_INT_TRIGGER 0x504 @@ -213,6 +226,12 @@ struct mtk_wdma_desc { #define MTK_WED_WPDMA_INT_CTRL_TX1_DONE_TRIG GENMASK(14, 10) #define MTK_WED_WPDMA_INT_CTRL_RX 0x534 +#define MTK_WED_WPDMA_INT_CTRL_RX0_EN BIT(0) +#define MTK_WED_WPDMA_INT_CTRL_RX0_CLR BIT(1) +#define MTK_WED_WPDMA_INT_CTRL_RX0_DONE_TRIG GENMASK(6, 2) +#define MTK_WED_WPDMA_INT_CTRL_RX1_EN BIT(8) +#define MTK_WED_WPDMA_INT_CTRL_RX1_CLR BIT(9) +#define MTK_WED_WPDMA_INT_CTRL_RX1_DONE_TRIG GENMASK(14, 10) #define MTK_WED_WPDMA_INT_CTRL_TX_FREE 0x538 #define MTK_WED_WPDMA_INT_CTRL_TX_FREE_DONE_EN BIT(0) @@ -242,11 +261,34 @@ struct mtk_wdma_desc { #define MTK_WED_WPDMA_RING_TX(_n) (0x600 + (_n) * 0x10) #define MTK_WED_WPDMA_RING_RX(_n) (0x700 + (_n) * 0x10) +#define MTK_WED_WPDMA_RING_RX_DATA(_n) (0x730 + (_n) * 0x10) + +#define MTK_WED_WPDMA_RX_D_GLO_CFG 0x75c +#define MTK_WED_WPDMA_RX_D_RX_DRV_EN BIT(0) +#define MTK_WED_WPDMA_RX_D_INIT_PHASE_RXEN_SEL GENMASK(11, 7) +#define MTK_WED_WPDMA_RX_D_RXD_READ_LEN GENMASK(31, 24) + +#define MTK_WED_WPDMA_RX_D_RST_IDX 0x760 +#define MTK_WED_WPDMA_RX_D_RST_CRX_IDX GENMASK(17, 16) +#define MTK_WED_WPDMA_RX_D_RST_DRV_IDX GENMASK(25, 24) + +#define MTK_WED_WPDMA_RX_GLO_CFG 0x76c +#define MTK_WED_WPDMA_RX_RING 0x770 + +#define MTK_WED_WPDMA_RX_D_MIB(_n) (0x774 + (_n) * 4) +#define MTK_WED_WPDMA_RX_D_PROCESSED_MIB(_n) (0x784 + (_n) * 4) +#define MTK_WED_WPDMA_RX_D_COHERENT_MIB 0x78c + +#define MTK_WED_WDMA_RING_TX 0x800 + +#define MTK_WED_WDMA_TX_MIB 0x810 + #define MTK_WED_WDMA_RING_RX(_n) (0x900 + (_n) * 0x10) #define MTK_WED_WDMA_RX_THRES(_n) (0x940 + (_n) * 0x4) #define MTK_WED_WDMA_GLO_CFG 0xa04 #define MTK_WED_WDMA_GLO_CFG_TX_DRV_EN BIT(0) +#define MTK_WED_WDMA_GLO_CFG_TX_DDONE_CHK BIT(1) #define MTK_WED_WDMA_GLO_CFG_RX_DRV_EN BIT(2) #define MTK_WED_WDMA_GLO_CFG_RX_DRV_BUSY BIT(3) #define MTK_WED_WDMA_GLO_CFG_BT_SIZE GENMASK(5, 4) @@ -291,6 +333,20 @@ struct mtk_wdma_desc { #define MTK_WED_WDMA_RX_RECYCLE_MIB(_n) (0xae8 + (_n) * 4) #define MTK_WED_WDMA_RX_PROCESSED_MIB(_n) (0xaf0 + (_n) * 4) +#define MTK_WED_RX_BM_RX_DMAD 0xd80 +#define MTK_WED_RX_BM_RX_DMAD_SDL0 GENMASK(13, 0) + +#define MTK_WED_RX_BM_BASE 0xd84 +#define MTK_WED_RX_BM_INIT_PTR 0xd88 +#define MTK_WED_RX_BM_SW_TAIL GENMASK(15, 0) +#define MTK_WED_RX_BM_INIT_SW_TAIL BIT(16) + +#define MTK_WED_RX_PTR 0xd8c + +#define MTK_WED_RX_BM_DYN_ALLOC_TH 0xdb4 +#define MTK_WED_RX_BM_DYN_ALLOC_TH_H GENMASK(31, 16) +#define MTK_WED_RX_BM_DYN_ALLOC_TH_L GENMASK(15, 0) + #define MTK_WED_RING_OFS_BASE 0x00 #define MTK_WED_RING_OFS_COUNT 0x04 #define MTK_WED_RING_OFS_CPU_IDX 0x08 @@ -301,7 +357,9 @@ struct mtk_wdma_desc { #define MTK_WDMA_GLO_CFG 0x204 #define MTK_WDMA_GLO_CFG_TX_DMA_EN BIT(0) +#define MTK_WDMA_GLO_CFG_TX_DMA_BUSY BIT(1) #define MTK_WDMA_GLO_CFG_RX_DMA_EN BIT(2) +#define MTK_WDMA_GLO_CFG_RX_DMA_BUSY BIT(3) #define MTK_WDMA_GLO_CFG_RX_INFO3_PRERES BIT(26) #define MTK_WDMA_GLO_CFG_RX_INFO2_PRERES BIT(27) #define MTK_WDMA_GLO_CFG_RX_INFO1_PRERES BIT(28) @@ -330,4 +388,70 @@ struct mtk_wdma_desc { /* DMA channel mapping */ #define HIFSYS_DMA_AG_MAP 0x008 +#define MTK_WED_RTQM_GLO_CFG 0xb00 +#define MTK_WED_RTQM_BUSY BIT(1) +#define MTK_WED_RTQM_Q_RST BIT(2) +#define MTK_WED_RTQM_Q_DBG_BYPASS BIT(5) +#define MTK_WED_RTQM_TXDMAD_FPORT GENMASK(23, 20) + +#define MTK_WED_RTQM_R2H_MIB(_n) (0xb70 + (_n) * 0x4) +#define MTK_WED_RTQM_R2Q_MIB(_n) (0xb78 + (_n) * 0x4) +#define MTK_WED_RTQM_Q2N_MIB 0xb80 +#define MTK_WED_RTQM_Q2H_MIB(_n) (0xb84 + (_n) * 0x4) + +#define MTK_WED_RTQM_Q2B_MIB 0xb8c +#define MTK_WED_RTQM_PFDBK_MIB 0xb90 + +#define MTK_WED_RROQM_GLO_CFG 0xc04 +#define MTK_WED_RROQM_RST_IDX 0xc08 +#define MTK_WED_RROQM_RST_IDX_MIOD BIT(0) +#define MTK_WED_RROQM_RST_IDX_FDBK BIT(4) + +#define MTK_WED_RROQM_MIOD_CTRL0 0xc40 +#define MTK_WED_RROQM_MIOD_CTRL1 0xc44 +#define MTK_WED_RROQM_MIOD_CNT GENMASK(11, 0) + +#define MTK_WED_RROQM_MIOD_CTRL2 0xc48 +#define MTK_WED_RROQM_MIOD_CTRL3 0xc4c + +#define MTK_WED_RROQM_FDBK_CTRL0 0xc50 +#define MTK_WED_RROQM_FDBK_CTRL1 0xc54 +#define MTK_WED_RROQM_FDBK_CNT GENMASK(11, 0) + +#define MTK_WED_RROQM_FDBK_CTRL2 0xc58 + +#define MTK_WED_RROQ_BASE_L 0xc80 +#define MTK_WED_RROQ_BASE_H 0xc84 + +#define MTK_WED_RROQM_MIOD_CFG 0xc8c +#define MTK_WED_RROQM_MIOD_MID_DW GENMASK(5, 0) +#define MTK_WED_RROQM_MIOD_MOD_DW GENMASK(13, 8) +#define MTK_WED_RROQM_MIOD_ENTRY_DW GENMASK(22, 16) + +#define MTK_WED_RROQM_MID_MIB 0xcc0 +#define MTK_WED_RROQM_MOD_MIB 0xcc4 +#define MTK_WED_RROQM_MOD_COHERENT_MIB 0xcc8 +#define MTK_WED_RROQM_FDBK_MIB 0xcd0 +#define MTK_WED_RROQM_FDBK_COHERENT_MIB 0xcd4 +#define MTK_WED_RROQM_FDBK_IND_MIB 0xce0 +#define MTK_WED_RROQM_FDBK_ENQ_MIB 0xce4 +#define MTK_WED_RROQM_FDBK_ANC_MIB 0xce8 +#define MTK_WED_RROQM_FDBK_ANC2H_MIB 0xcec + +#define MTK_WED_RX_BM_RX_DMAD 0xd80 +#define MTK_WED_RX_BM_BASE 0xd84 +#define MTK_WED_RX_BM_INIT_PTR 0xd88 +#define MTK_WED_RX_BM_PTR 0xd8c +#define MTK_WED_RX_BM_PTR_HEAD GENMASK(32, 16) +#define MTK_WED_RX_BM_PTR_TAIL GENMASK(15, 0) + +#define MTK_WED_RX_BM_BLEN 0xd90 +#define MTK_WED_RX_BM_STS 0xd94 +#define MTK_WED_RX_BM_INTF2 0xd98 +#define MTK_WED_RX_BM_INTF 0xd9c +#define MTK_WED_RX_BM_ERR_STS 0xda8 + +#define MTK_WED_WOCPU_VIEW_MIOD_BASE 0x8000 +#define MTK_WED_PCIE_INT_MASK 0x0 + #endif diff --git a/drivers/net/ethernet/mediatek/mtk_wed_wo.h b/drivers/net/ethernet/mediatek/mtk_wed_wo.h index ed7e69d420dc..c8fb85795864 100644 --- a/drivers/net/ethernet/mediatek/mtk_wed_wo.h +++ b/drivers/net/ethernet/mediatek/mtk_wed_wo.h @@ -49,6 +49,10 @@ enum { MTK_WED_WARP_CMD_FLAG_FROM_TO_WO = BIT(2), }; +#define MTK_WED_WO_CPU_MCUSYS_RESET_ADDR 0x15194050 +#define MTK_WED_WO_CPU_WO0_MCUSYS_RESET_MASK 0x20 +#define MTK_WED_WO_CPU_WO1_MCUSYS_RESET_MASK 0x1 + enum { MTK_WED_WO_REGION_EMI, MTK_WED_WO_REGION_ILM, @@ -57,6 +61,28 @@ enum { __MTK_WED_WO_REGION_MAX, }; +enum mtk_wed_wo_state { + MTK_WED_WO_STATE_UNDEFINED, + MTK_WED_WO_STATE_INIT, + MTK_WED_WO_STATE_ENABLE, + MTK_WED_WO_STATE_DISABLE, + MTK_WED_WO_STATE_HALT, + MTK_WED_WO_STATE_GATING, + MTK_WED_WO_STATE_SER_RESET, + MTK_WED_WO_STATE_WF_RESET, +}; + +enum mtk_wed_wo_done_state { + MTK_WED_WOIF_UNDEFINED, + MTK_WED_WOIF_DISABLE_DONE, + MTK_WED_WOIF_TRIGGER_ENABLE, + MTK_WED_WOIF_ENABLE_DONE, + MTK_WED_WOIF_TRIGGER_GATING, + MTK_WED_WOIF_GATING_DONE, + MTK_WED_WOIF_TRIGGER_HALT, + MTK_WED_WOIF_HALT_DONE, +}; + enum mtk_wed_dummy_cr_idx { MTK_WED_DUMMY_CR_FWDL, MTK_WED_DUMMY_CR_WO_STATUS, @@ -245,6 +271,8 @@ void mtk_wed_mcu_rx_unsolicited_event(struct mtk_wed_wo *wo, struct sk_buff *skb); int mtk_wed_mcu_send_msg(struct mtk_wed_wo *wo, int id, int cmd, const void *data, int len, bool wait_resp); +int mtk_wed_mcu_msg_update(struct mtk_wed_device *dev, int id, void *data, + int len); int mtk_wed_mcu_init(struct mtk_wed_wo *wo); int mtk_wed_wo_init(struct mtk_wed_hw *hw); void mtk_wed_wo_deinit(struct mtk_wed_hw *hw); diff --git a/include/linux/soc/mediatek/mtk_wed.h b/include/linux/soc/mediatek/mtk_wed.h index 956978320f8b..8294978f4bca 100644 --- a/include/linux/soc/mediatek/mtk_wed.h +++ b/include/linux/soc/mediatek/mtk_wed.h @@ -5,10 +5,13 @@ #include #include #include +#include #define MTK_WED_TX_QUEUES 2 #define MTK_WED_RX_QUEUES 2 +#define WED_WO_STA_REC 0x6 + struct mtk_wed_hw; struct mtk_wdma_desc; @@ -41,21 +44,37 @@ enum mtk_wed_wo_cmd { MTK_WED_WO_CMD_WED_END }; +struct mtk_rxbm_desc { + __le32 buf0; + __le32 token; +} __packed __aligned(4); + enum mtk_wed_bus_tye { MTK_WED_BUS_PCIE, MTK_WED_BUS_AXI, }; +#define MTK_WED_RING_CONFIGURED BIT(0) struct mtk_wed_ring { struct mtk_wdma_desc *desc; dma_addr_t desc_phys; u32 desc_size; int size; + u32 flags; u32 reg_base; void __iomem *wpdma; }; +struct mtk_wed_wo_rx_stats { + __le16 wlan_idx; + __le16 tid; + __le32 rx_pkt_cnt; + __le32 rx_byte_cnt; + __le32 rx_err_cnt; + __le32 rx_drop_cnt; +}; + struct mtk_wed_device { #ifdef CONFIG_NET_MEDIATEK_SOC_WED const struct mtk_wed_ops *ops; @@ -64,9 +83,12 @@ struct mtk_wed_device { bool init_done, running; int wdma_idx; int irq; + u8 version; struct mtk_wed_ring tx_ring[MTK_WED_TX_QUEUES]; + struct mtk_wed_ring rx_ring[MTK_WED_RX_QUEUES]; struct mtk_wed_ring txfree_ring; + struct mtk_wed_ring tx_wdma[MTK_WED_TX_QUEUES]; struct mtk_wed_ring rx_wdma[MTK_WED_RX_QUEUES]; struct { @@ -74,7 +96,20 @@ struct mtk_wed_device { void **pages; struct mtk_wdma_desc *desc; dma_addr_t desc_phys; - } buf_ring; + } tx_buf_ring; + + struct { + int size; + struct page_frag_cache rx_page; + struct mtk_rxbm_desc *desc; + dma_addr_t desc_phys; + } rx_buf_ring; + + struct { + struct mtk_wed_ring ring; + dma_addr_t miod_phys; + dma_addr_t fdbk_phys; + } rro; /* filled by driver: */ struct { @@ -83,22 +118,36 @@ struct mtk_wed_device { struct pci_dev *pci_dev; }; enum mtk_wed_bus_tye bus_type; + void __iomem *base; + u32 phy_base; u32 wpdma_phys; u32 wpdma_int; u32 wpdma_mask; u32 wpdma_tx; u32 wpdma_txfree; + u32 wpdma_rx_glo; + u32 wpdma_rx; + + bool wcid_512; u16 token_start; unsigned int nbuf; + unsigned int rx_nbuf; + unsigned int rx_npkt; + unsigned int rx_size; u8 tx_tbit[MTK_WED_TX_QUEUES]; + u8 rx_tbit[MTK_WED_RX_QUEUES]; u8 txfree_tbit; u32 (*init_buf)(void *ptr, dma_addr_t phys, int token_id); int (*offload_enable)(struct mtk_wed_device *wed); void (*offload_disable)(struct mtk_wed_device *wed); + u32 (*init_rx_buf)(struct mtk_wed_device *wed, int size); + void (*release_rx_buf)(struct mtk_wed_device *wed); + void (*update_wo_rx_stats)(struct mtk_wed_device *wed, + struct mtk_wed_wo_rx_stats *stats); } wlan; #endif }; @@ -107,9 +156,15 @@ struct mtk_wed_ops { int (*attach)(struct mtk_wed_device *dev); int (*tx_ring_setup)(struct mtk_wed_device *dev, int ring, void __iomem *regs); + int (*rx_ring_setup)(struct mtk_wed_device *dev, int ring, + void __iomem *regs); int (*txfree_ring_setup)(struct mtk_wed_device *dev, void __iomem *regs); + int (*msg_update)(struct mtk_wed_device *dev, int cmd_id, + void *data, int len); void (*detach)(struct mtk_wed_device *dev); + void (*ppe_check)(struct mtk_wed_device *dev, struct sk_buff *skb, + u32 reason, u32 hash); void (*stop)(struct mtk_wed_device *dev); void (*start)(struct mtk_wed_device *dev, u32 irq_mask); @@ -144,6 +199,16 @@ mtk_wed_device_attach(struct mtk_wed_device *dev) return ret; } +static inline bool +mtk_wed_get_rx_capa(struct mtk_wed_device *dev) +{ +#ifdef CONFIG_NET_MEDIATEK_SOC_WED + return dev->version != 1; +#else + return false; +#endif +} + #ifdef CONFIG_NET_MEDIATEK_SOC_WED #define mtk_wed_device_active(_dev) !!(_dev)->ops #define mtk_wed_device_detach(_dev) (_dev)->ops->detach(_dev) @@ -160,6 +225,12 @@ mtk_wed_device_attach(struct mtk_wed_device *dev) (_dev)->ops->irq_get(_dev, _mask) #define mtk_wed_device_irq_set_mask(_dev, _mask) \ (_dev)->ops->irq_set_mask(_dev, _mask) +#define mtk_wed_device_rx_ring_setup(_dev, _ring, _regs) \ + (_dev)->ops->rx_ring_setup(_dev, _ring, _regs) +#define mtk_wed_device_ppe_check(_dev, _skb, _reason, _hash) \ + (_dev)->ops->ppe_check(_dev, _skb, _reason, _hash) +#define mtk_wed_device_update_msg(_dev, _id, _msg, _len) \ + (_dev)->ops->msg_update(_dev, _id, _msg, _len) #else static inline bool mtk_wed_device_active(struct mtk_wed_device *dev) { @@ -173,6 +244,9 @@ static inline bool mtk_wed_device_active(struct mtk_wed_device *dev) #define mtk_wed_device_reg_write(_dev, _reg, _val) do {} while (0) #define mtk_wed_device_irq_get(_dev, _mask) 0 #define mtk_wed_device_irq_set_mask(_dev, _mask) do {} while (0) +#define mtk_wed_device_rx_ring_setup(_dev, _ring, _regs) -ENODEV +#define mtk_wed_device_ppe_check(_dev, _skb, _reason, _hash) do {} while (0) +#define mtk_wed_device_update_msg(_dev, _id, _msg, _len) -ENODEV #endif #endif From 51ef685584e24e1112a77a47d7280d60325c5f4e Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Sat, 5 Nov 2022 23:36:22 +0100 Subject: [PATCH 0823/2002] net: ethernet: mtk_wed: add rx mib counters Introduce WED RX MIB counters support available on MT7986a SoC. Tested-by: Daniel Golle Co-developed-by: Sujuan Chen Signed-off-by: Sujuan Chen Signed-off-by: Lorenzo Bianconi Signed-off-by: David S. Miller --- .../net/ethernet/mediatek/mtk_wed_debugfs.c | 87 +++++++++++++++++++ 1 file changed, 87 insertions(+) diff --git a/drivers/net/ethernet/mediatek/mtk_wed_debugfs.c b/drivers/net/ethernet/mediatek/mtk_wed_debugfs.c index f420f187e837..56f663439721 100644 --- a/drivers/net/ethernet/mediatek/mtk_wed_debugfs.c +++ b/drivers/net/ethernet/mediatek/mtk_wed_debugfs.c @@ -2,6 +2,7 @@ /* Copyright (C) 2021 Felix Fietkau */ #include +#include #include "mtk_wed.h" #include "mtk_wed_regs.h" @@ -18,6 +19,8 @@ enum { DUMP_TYPE_WDMA, DUMP_TYPE_WPDMA_TX, DUMP_TYPE_WPDMA_TXFREE, + DUMP_TYPE_WPDMA_RX, + DUMP_TYPE_WED_RRO, }; #define DUMP_STR(_str) { _str, 0, DUMP_TYPE_STRING } @@ -36,6 +39,9 @@ enum { #define DUMP_WPDMA_TX_RING(_n) DUMP_RING("WPDMA_TX" #_n, 0, DUMP_TYPE_WPDMA_TX, _n) #define DUMP_WPDMA_TXFREE_RING DUMP_RING("WPDMA_RX1", 0, DUMP_TYPE_WPDMA_TXFREE) +#define DUMP_WPDMA_RX_RING(_n) DUMP_RING("WPDMA_RX" #_n, 0, DUMP_TYPE_WPDMA_RX, _n) +#define DUMP_WED_RRO_RING(_base)DUMP_RING("WED_RRO_MIOD", MTK_##_base, DUMP_TYPE_WED_RRO) +#define DUMP_WED_RRO_FDBK(_base)DUMP_RING("WED_RRO_FDBK", MTK_##_base, DUMP_TYPE_WED_RRO) static void print_reg_val(struct seq_file *s, const char *name, u32 val) @@ -57,6 +63,7 @@ dump_wed_regs(struct seq_file *s, struct mtk_wed_device *dev, cur > regs ? "\n" : "", cur->name); continue; + case DUMP_TYPE_WED_RRO: case DUMP_TYPE_WED: val = wed_r32(dev, cur->offset); break; @@ -69,6 +76,9 @@ dump_wed_regs(struct seq_file *s, struct mtk_wed_device *dev, case DUMP_TYPE_WPDMA_TXFREE: val = wpdma_txfree_r32(dev, cur->offset); break; + case DUMP_TYPE_WPDMA_RX: + val = wpdma_rx_r32(dev, cur->base, cur->offset); + break; } print_reg_val(s, cur->name, val); } @@ -132,6 +142,80 @@ wed_txinfo_show(struct seq_file *s, void *data) } DEFINE_SHOW_ATTRIBUTE(wed_txinfo); +static int +wed_rxinfo_show(struct seq_file *s, void *data) +{ + static const struct reg_dump regs[] = { + DUMP_STR("WPDMA RX"), + DUMP_WPDMA_RX_RING(0), + DUMP_WPDMA_RX_RING(1), + + DUMP_STR("WPDMA RX"), + DUMP_WED(WED_WPDMA_RX_D_MIB(0)), + DUMP_WED_RING(WED_WPDMA_RING_RX_DATA(0)), + DUMP_WED(WED_WPDMA_RX_D_PROCESSED_MIB(0)), + DUMP_WED(WED_WPDMA_RX_D_MIB(1)), + DUMP_WED_RING(WED_WPDMA_RING_RX_DATA(1)), + DUMP_WED(WED_WPDMA_RX_D_PROCESSED_MIB(1)), + DUMP_WED(WED_WPDMA_RX_D_COHERENT_MIB), + + DUMP_STR("WED RX"), + DUMP_WED_RING(WED_RING_RX_DATA(0)), + DUMP_WED_RING(WED_RING_RX_DATA(1)), + + DUMP_STR("WED RRO"), + DUMP_WED_RRO_RING(WED_RROQM_MIOD_CTRL0), + DUMP_WED(WED_RROQM_MID_MIB), + DUMP_WED(WED_RROQM_MOD_MIB), + DUMP_WED(WED_RROQM_MOD_COHERENT_MIB), + DUMP_WED_RRO_FDBK(WED_RROQM_FDBK_CTRL0), + DUMP_WED(WED_RROQM_FDBK_IND_MIB), + DUMP_WED(WED_RROQM_FDBK_ENQ_MIB), + DUMP_WED(WED_RROQM_FDBK_ANC_MIB), + DUMP_WED(WED_RROQM_FDBK_ANC2H_MIB), + + DUMP_STR("WED Route QM"), + DUMP_WED(WED_RTQM_R2H_MIB(0)), + DUMP_WED(WED_RTQM_R2Q_MIB(0)), + DUMP_WED(WED_RTQM_Q2H_MIB(0)), + DUMP_WED(WED_RTQM_R2H_MIB(1)), + DUMP_WED(WED_RTQM_R2Q_MIB(1)), + DUMP_WED(WED_RTQM_Q2H_MIB(1)), + DUMP_WED(WED_RTQM_Q2N_MIB), + DUMP_WED(WED_RTQM_Q2B_MIB), + DUMP_WED(WED_RTQM_PFDBK_MIB), + + DUMP_STR("WED WDMA TX"), + DUMP_WED(WED_WDMA_TX_MIB), + DUMP_WED_RING(WED_WDMA_RING_TX), + + DUMP_STR("WDMA TX"), + DUMP_WDMA(WDMA_GLO_CFG), + DUMP_WDMA_RING(WDMA_RING_TX(0)), + DUMP_WDMA_RING(WDMA_RING_TX(1)), + + DUMP_STR("WED RX BM"), + DUMP_WED(WED_RX_BM_BASE), + DUMP_WED(WED_RX_BM_RX_DMAD), + DUMP_WED(WED_RX_BM_PTR), + DUMP_WED(WED_RX_BM_TKID_MIB), + DUMP_WED(WED_RX_BM_BLEN), + DUMP_WED(WED_RX_BM_STS), + DUMP_WED(WED_RX_BM_INTF2), + DUMP_WED(WED_RX_BM_INTF), + DUMP_WED(WED_RX_BM_ERR_STS), + }; + struct mtk_wed_hw *hw = s->private; + struct mtk_wed_device *dev = hw->wed_dev; + + if (!dev) + return 0; + + dump_wed_regs(s, dev, regs, ARRAY_SIZE(regs)); + + return 0; +} +DEFINE_SHOW_ATTRIBUTE(wed_rxinfo); static int mtk_wed_reg_set(void *data, u64 val) @@ -175,4 +259,7 @@ void mtk_wed_hw_add_debugfs(struct mtk_wed_hw *hw) debugfs_create_u32("regidx", 0600, dir, &hw->debugfs_reg); debugfs_create_file_unsafe("regval", 0600, dir, hw, &fops_regval); debugfs_create_file_unsafe("txinfo", 0400, dir, hw, &wed_txinfo_fops); + if (hw->version != 1) + debugfs_create_file_unsafe("rxinfo", 0400, dir, hw, + &wed_rxinfo_fops); } From 90050f80509c7e97cd957b025b62e6e49c2c6bc0 Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Sat, 5 Nov 2022 23:36:23 +0100 Subject: [PATCH 0824/2002] MAINTAINERS: update MEDIATEK ETHERNET entry Update MEDIATEK ETHERNET driver maintainer file enty adding myself to maintainers list Acked-by: Felix Fietkau Acked-by: Sean Wang Signed-off-by: Lorenzo Bianconi Signed-off-by: David S. Miller --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS b/MAINTAINERS index 6aea837ff9d4..14ee1c72d01a 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -12926,6 +12926,7 @@ M: Felix Fietkau M: John Crispin M: Sean Wang M: Mark Lee +M: Lorenzo Bianconi L: netdev@vger.kernel.org S: Maintained F: drivers/net/ethernet/mediatek/ From 30172a7241f84a7b173652698b189614672fc46f Mon Sep 17 00:00:00 2001 From: Steen Hegelund Date: Wed, 9 Nov 2022 12:41:09 +0100 Subject: [PATCH 0825/2002] net: microchip: sparx5: Differentiate IPv4 and IPv6 traffic in keyset config This changes the port keyset configuration for Sparx5 IS2 so that - IPv4 generates a IP4_TCP_UDP keyset for IPv4 TCP/UDP frames and a IP4_OTHER keyset for other IPv4 frames (both UC and MC) - IPv6 generates a IP_7TUPLE keyset (both UC and MC) ARP and non-IP traffic continues to generate the MAC_ETYPE keyset Signed-off-by: Steen Hegelund Signed-off-by: David S. Miller --- .../microchip/sparx5/sparx5_vcap_impl.c | 21 ++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_vcap_impl.c b/drivers/net/ethernet/microchip/sparx5/sparx5_vcap_impl.c index 50153264179e..e4428d55af2b 100644 --- a/drivers/net/ethernet/microchip/sparx5/sparx5_vcap_impl.c +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_vcap_impl.c @@ -21,6 +21,14 @@ #define STREAMSIZE (64 * 4) /* bytes in the VCAP cache area */ #define SPARX5_IS2_LOOKUPS 4 +#define VCAP_IS2_KEYSEL(_ena, _noneth, _v4_mc, _v4_uc, _v6_mc, _v6_uc, _arp) \ + (ANA_ACL_VCAP_S2_KEY_SEL_KEY_SEL_ENA_SET(_ena) | \ + ANA_ACL_VCAP_S2_KEY_SEL_NON_ETH_KEY_SEL_SET(_noneth) | \ + ANA_ACL_VCAP_S2_KEY_SEL_IP4_MC_KEY_SEL_SET(_v4_mc) | \ + ANA_ACL_VCAP_S2_KEY_SEL_IP4_UC_KEY_SEL_SET(_v4_uc) | \ + ANA_ACL_VCAP_S2_KEY_SEL_IP6_MC_KEY_SEL_SET(_v6_mc) | \ + ANA_ACL_VCAP_S2_KEY_SEL_IP6_UC_KEY_SEL_SET(_v6_uc) | \ + ANA_ACL_VCAP_S2_KEY_SEL_ARP_KEY_SEL_SET(_arp)) /* IS2 port keyset selection control */ @@ -368,13 +376,12 @@ static void sparx5_vcap_port_key_selection(struct sparx5 *sparx5, /* all traffic types generate the MAC_ETYPE keyset for now in all * lookups on all ports */ - keysel = ANA_ACL_VCAP_S2_KEY_SEL_KEY_SEL_ENA_SET(true) | - ANA_ACL_VCAP_S2_KEY_SEL_NON_ETH_KEY_SEL_SET(VCAP_IS2_PS_NONETH_MAC_ETYPE) | - ANA_ACL_VCAP_S2_KEY_SEL_IP4_MC_KEY_SEL_SET(VCAP_IS2_PS_IPV4_MC_MAC_ETYPE) | - ANA_ACL_VCAP_S2_KEY_SEL_IP4_UC_KEY_SEL_SET(VCAP_IS2_PS_IPV4_UC_MAC_ETYPE) | - ANA_ACL_VCAP_S2_KEY_SEL_IP6_MC_KEY_SEL_SET(VCAP_IS2_PS_IPV6_MC_MAC_ETYPE) | - ANA_ACL_VCAP_S2_KEY_SEL_IP6_UC_KEY_SEL_SET(VCAP_IS2_PS_IPV6_UC_MAC_ETYPE) | - ANA_ACL_VCAP_S2_KEY_SEL_ARP_KEY_SEL_SET(VCAP_IS2_PS_ARP_MAC_ETYPE); + keysel = VCAP_IS2_KEYSEL(true, VCAP_IS2_PS_NONETH_MAC_ETYPE, + VCAP_IS2_PS_IPV4_MC_IP4_TCP_UDP_OTHER, + VCAP_IS2_PS_IPV4_UC_IP4_TCP_UDP_OTHER, + VCAP_IS2_PS_IPV6_MC_IP_7TUPLE, + VCAP_IS2_PS_IPV6_UC_IP_7TUPLE, + VCAP_IS2_PS_ARP_MAC_ETYPE); for (lookup = 0; lookup < admin->lookups; ++lookup) { for (portno = 0; portno < SPX5_PORTS; ++portno) { spx5_wr(keysel, sparx5, From d6c2964db3fee8b4512eb9e430f2081364c2fb90 Mon Sep 17 00:00:00 2001 From: Steen Hegelund Date: Wed, 9 Nov 2022 12:41:10 +0100 Subject: [PATCH 0826/2002] net: microchip: sparx5: Adding more tc flower keys for the IS2 VCAP This adds the following TC flower filter keys to Sparx5 for IS2: - ipv4_addr (sip and dip) - ipv6_addr (sip and dip) - control (IPv4 fragments) - portnum (tcp and udp port numbers) - basic (L3 and L4 protocol) - vlan (outer vlan tag info) - tcp (tcp flags) - ip (tos field) as well as an 128 bit keyfield interface on the VCAP API to set the IPv6 addresses. IS2 supports the classified VLAN information which amounts to the outer VLAN info in case of multiple tags. Here are some examples of the tc flower filter operations that are now supported for the IS2 VCAP: - IPv4 Addresses tc filter add dev eth12 ingress chain 8000000 prio 12 handle 12 \ protocol ip flower skip_sw dst_ip 1.0.1.1 src_ip 2.0.2.2 \ action trap action goto chain 81000000 - IPv6 Addresses tc filter add dev eth12 ingress chain 8000000 prio 13 handle 13 \ protocol ipv6 flower skip_sw dst_ip 1::1:1 src_ip 2::2:2 \ action trap action goto chain 81000000 - IPv4 fragments tc filter add dev eth12 ingress chain 8000000 prio 14 handle 14 \ protocol ip flower skip_sw dst_ip 3.0.3.3 src_ip 2.0.2.2 \ ip_flags frag/nofirstfrag action trap action goto chain 81000000 - TCP and UDP portnumbers tc filter add dev eth12 ingress chain 8000000 prio 21 handle 21 \ protocol ip flower skip_sw dst_ip 8.8.8.8 src_ip 2.0.2.2 \ ip_proto tcp dst_port 100 src_port 12000 action trap action goto chain 81000000 tc filter add dev eth12 ingress chain 8000000 prio 23 handle 23 \ protocol ipv6 flower skip_sw dst_ip 5::5:5 src_ip 2::2:2 \ ip_proto tcp dst_port 300 src_port 13000 action trap action goto chain 81000000 - Layer 3 and Layer 4 protocol info tc filter add dev eth12 ingress chain 8000000 prio 28 handle 28 \ protocol ipv4 flower skip_sw dst_ip 9.0.9.9 src_ip 2.0.2.2 \ ip_proto icmp action trap action goto chain 81000000 - VLAN tag info (outer tag) tc filter add dev eth12 ingress chain 8000000 prio 29 handle 29 \ protocol 802.1q flower skip_sw vlan_id 600 vlan_prio 6 \ vlan_ethtype ipv4 action trap action goto chain 81000000 tc filter add dev eth12 ingress chain 8000000 prio 31 handle 31 \ protocol 802.1q flower skip_sw vlan_id 600 vlan_prio 5 \ vlan_ethtype ipv6 action trap action goto chain 81000000 - TCP flags tc filter add dev eth12 ingress chain 8000000 prio 15 handle 15 \ protocol ip flower skip_sw dst_ip 4.0.4.4 src_ip 2.0.2.2 \ ip_proto tcp tcp_flags 0x2a/0x3f action trap action goto chain 81000000 - IP info (IPv4 TOS field) tc filter add dev eth12 ingress chain 8000000 prio 16 handle 16 \ protocol ip flower skip_sw ip_tos 0x35 dst_ip 5.0.5.5 \ src_ip 2.0.2.2 action trap action goto chain 81000000 Notes: - The "protocol all" selection is not supported yet. - The MAC address rule now needs to use non-ip and non "protocol all". Here is an example: tc filter add dev eth12 ingress chain 8000000 prio 10 handle 10 \ protocol 0xbeef flower skip_sw \ dst_mac 0a:0b:0c:0d:0e:0f \ src_mac 2:0:0:0:0:1 \ action trap action goto chain 81000000 - The VLAN rules use classified VLAN information, and to get the classification information into the frame metadata, the ingress port need to be added to a bridge with the VID and vlan filtering enabled, like this (using VID 600 and four ports eth12, eth13, eth14 and eth15): ip link add name br5 type bridge ip link set dev br5 up ip link set eth12 master br5 ip link set eth13 master br5 ip link set eth14 master br5 ip link set eth15 master br5 sysctl -w net.ipv6.conf.eth12.disable_ipv6=1 sysctl -w net.ipv6.conf.eth13.disable_ipv6=1 sysctl -w net.ipv6.conf.eth14.disable_ipv6=1 sysctl -w net.ipv6.conf.eth15.disable_ipv6=1 sysctl -w net.ipv6.conf.br5.disable_ipv6=1 ip link set dev br5 type bridge vlan_filtering 1 bridge vlan add dev eth12 vid 600 bridge vlan add dev eth13 vid 600 bridge vlan add dev eth14 vid 600 bridge vlan add dev eth15 vid 600 bridge vlan add dev br5 vid 600 self Signed-off-by: Steen Hegelund Tested-by: Casper Andersson Signed-off-by: David S. Miller --- .../microchip/sparx5/sparx5_tc_flower.c | 384 +++++++++++++++++- .../net/ethernet/microchip/vcap/vcap_api.c | 11 + .../ethernet/microchip/vcap/vcap_api_client.h | 2 + 3 files changed, 396 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_tc_flower.c b/drivers/net/ethernet/microchip/sparx5/sparx5_tc_flower.c index 626558a5c850..13bc6bff4c1e 100644 --- a/drivers/net/ethernet/microchip/sparx5/sparx5_tc_flower.c +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_tc_flower.c @@ -16,9 +16,32 @@ struct sparx5_tc_flower_parse_usage { struct flow_cls_offload *fco; struct flow_rule *frule; struct vcap_rule *vrule; + u16 l3_proto; + u8 l4_proto; unsigned int used_keys; }; +/* These protocols have dedicated keysets in IS2 and a TC dissector + * ETH_P_ARP does not have a TC dissector + */ +static u16 sparx5_tc_known_etypes[] = { + ETH_P_ALL, + ETH_P_IP, + ETH_P_IPV6, +}; + +static bool sparx5_tc_is_known_etype(u16 etype) +{ + int idx; + + /* For now this only knows about IS2 traffic classification */ + for (idx = 0; idx < ARRAY_SIZE(sparx5_tc_known_etypes); ++idx) + if (sparx5_tc_known_etypes[idx] == etype) + return true; + + return false; +} + static int sparx5_tc_flower_handler_ethaddr_usage(struct sparx5_tc_flower_parse_usage *st) { enum vcap_key_field smac_key = VCAP_KF_L2_SMAC; @@ -54,9 +77,368 @@ out: return err; } +static int +sparx5_tc_flower_handler_ipv4_usage(struct sparx5_tc_flower_parse_usage *st) +{ + int err = 0; + + if (st->l3_proto == ETH_P_IP) { + struct flow_match_ipv4_addrs mt; + + flow_rule_match_ipv4_addrs(st->frule, &mt); + if (mt.mask->src) { + err = vcap_rule_add_key_u32(st->vrule, + VCAP_KF_L3_IP4_SIP, + be32_to_cpu(mt.key->src), + be32_to_cpu(mt.mask->src)); + if (err) + goto out; + } + if (mt.mask->dst) { + err = vcap_rule_add_key_u32(st->vrule, + VCAP_KF_L3_IP4_DIP, + be32_to_cpu(mt.key->dst), + be32_to_cpu(mt.mask->dst)); + if (err) + goto out; + } + } + + st->used_keys |= BIT(FLOW_DISSECTOR_KEY_IPV4_ADDRS); + + return err; + +out: + NL_SET_ERR_MSG_MOD(st->fco->common.extack, "ipv4_addr parse error"); + return err; +} + +static int +sparx5_tc_flower_handler_ipv6_usage(struct sparx5_tc_flower_parse_usage *st) +{ + int err = 0; + + if (st->l3_proto == ETH_P_IPV6) { + struct flow_match_ipv6_addrs mt; + struct vcap_u128_key sip; + struct vcap_u128_key dip; + + flow_rule_match_ipv6_addrs(st->frule, &mt); + /* Check if address masks are non-zero */ + if (!ipv6_addr_any(&mt.mask->src)) { + vcap_netbytes_copy(sip.value, mt.key->src.s6_addr, 16); + vcap_netbytes_copy(sip.mask, mt.mask->src.s6_addr, 16); + err = vcap_rule_add_key_u128(st->vrule, + VCAP_KF_L3_IP6_SIP, &sip); + if (err) + goto out; + } + if (!ipv6_addr_any(&mt.mask->dst)) { + vcap_netbytes_copy(dip.value, mt.key->dst.s6_addr, 16); + vcap_netbytes_copy(dip.mask, mt.mask->dst.s6_addr, 16); + err = vcap_rule_add_key_u128(st->vrule, + VCAP_KF_L3_IP6_DIP, &dip); + if (err) + goto out; + } + } + st->used_keys |= BIT(FLOW_DISSECTOR_KEY_IPV6_ADDRS); + return err; +out: + NL_SET_ERR_MSG_MOD(st->fco->common.extack, "ipv6_addr parse error"); + return err; +} + +static int +sparx5_tc_flower_handler_control_usage(struct sparx5_tc_flower_parse_usage *st) +{ + struct flow_match_control mt; + u32 value, mask; + int err = 0; + + flow_rule_match_control(st->frule, &mt); + + if (mt.mask->flags) { + if (mt.mask->flags & FLOW_DIS_FIRST_FRAG) { + if (mt.key->flags & FLOW_DIS_FIRST_FRAG) { + value = 1; /* initial fragment */ + mask = 0x3; + } else { + if (mt.mask->flags & FLOW_DIS_IS_FRAGMENT) { + value = 3; /* follow up fragment */ + mask = 0x3; + } else { + value = 0; /* no fragment */ + mask = 0x3; + } + } + } else { + if (mt.mask->flags & FLOW_DIS_IS_FRAGMENT) { + value = 3; /* follow up fragment */ + mask = 0x3; + } else { + value = 0; /* no fragment */ + mask = 0x3; + } + } + + err = vcap_rule_add_key_u32(st->vrule, + VCAP_KF_L3_FRAGMENT_TYPE, + value, mask); + if (err) + goto out; + } + + st->used_keys |= BIT(FLOW_DISSECTOR_KEY_CONTROL); + + return err; + +out: + NL_SET_ERR_MSG_MOD(st->fco->common.extack, "ip_frag parse error"); + return err; +} + +static int +sparx5_tc_flower_handler_portnum_usage(struct sparx5_tc_flower_parse_usage *st) +{ + struct flow_match_ports mt; + u16 value, mask; + int err = 0; + + flow_rule_match_ports(st->frule, &mt); + + if (mt.mask->src) { + value = be16_to_cpu(mt.key->src); + mask = be16_to_cpu(mt.mask->src); + err = vcap_rule_add_key_u32(st->vrule, VCAP_KF_L4_SPORT, value, + mask); + if (err) + goto out; + } + + if (mt.mask->dst) { + value = be16_to_cpu(mt.key->dst); + mask = be16_to_cpu(mt.mask->dst); + err = vcap_rule_add_key_u32(st->vrule, VCAP_KF_L4_DPORT, value, + mask); + if (err) + goto out; + } + + st->used_keys |= BIT(FLOW_DISSECTOR_KEY_PORTS); + + return err; + +out: + NL_SET_ERR_MSG_MOD(st->fco->common.extack, "port parse error"); + return err; +} + +static int +sparx5_tc_flower_handler_basic_usage(struct sparx5_tc_flower_parse_usage *st) +{ + struct flow_match_basic mt; + int err = 0; + + flow_rule_match_basic(st->frule, &mt); + + if (mt.mask->n_proto) { + st->l3_proto = be16_to_cpu(mt.key->n_proto); + if (!sparx5_tc_is_known_etype(st->l3_proto)) { + err = vcap_rule_add_key_u32(st->vrule, VCAP_KF_ETYPE, + st->l3_proto, ~0); + if (err) + goto out; + } else if (st->l3_proto == ETH_P_IP) { + err = vcap_rule_add_key_bit(st->vrule, VCAP_KF_IP4_IS, + VCAP_BIT_1); + if (err) + goto out; + } else if (st->l3_proto == ETH_P_IPV6) { + err = vcap_rule_add_key_bit(st->vrule, VCAP_KF_IP4_IS, + VCAP_BIT_0); + if (err) + goto out; + } + } + + if (mt.mask->ip_proto) { + st->l4_proto = mt.key->ip_proto; + if (st->l4_proto == IPPROTO_TCP) { + err = vcap_rule_add_key_bit(st->vrule, + VCAP_KF_TCP_IS, + VCAP_BIT_1); + if (err) + goto out; + } else if (st->l4_proto == IPPROTO_UDP) { + err = vcap_rule_add_key_bit(st->vrule, + VCAP_KF_TCP_IS, + VCAP_BIT_0); + if (err) + goto out; + } else { + err = vcap_rule_add_key_u32(st->vrule, + VCAP_KF_L3_IP_PROTO, + st->l4_proto, ~0); + if (err) + goto out; + } + } + + st->used_keys |= BIT(FLOW_DISSECTOR_KEY_BASIC); + + return err; + +out: + NL_SET_ERR_MSG_MOD(st->fco->common.extack, "ip_proto parse error"); + return err; +} + +static int +sparx5_tc_flower_handler_vlan_usage(struct sparx5_tc_flower_parse_usage *st) +{ + enum vcap_key_field vid_key = VCAP_KF_8021Q_VID_CLS; + enum vcap_key_field pcp_key = VCAP_KF_8021Q_PCP_CLS; + struct flow_match_vlan mt; + int err; + + flow_rule_match_vlan(st->frule, &mt); + + if (mt.mask->vlan_id) { + err = vcap_rule_add_key_u32(st->vrule, vid_key, + mt.key->vlan_id, + mt.mask->vlan_id); + if (err) + goto out; + } + + if (mt.mask->vlan_priority) { + err = vcap_rule_add_key_u32(st->vrule, pcp_key, + mt.key->vlan_priority, + mt.mask->vlan_priority); + if (err) + goto out; + } + + st->used_keys |= BIT(FLOW_DISSECTOR_KEY_VLAN); + + return err; +out: + NL_SET_ERR_MSG_MOD(st->fco->common.extack, "vlan parse error"); + return err; +} + +static int +sparx5_tc_flower_handler_tcp_usage(struct sparx5_tc_flower_parse_usage *st) +{ + struct flow_match_tcp mt; + u16 tcp_flags_mask; + u16 tcp_flags_key; + enum vcap_bit val; + int err = 0; + + flow_rule_match_tcp(st->frule, &mt); + tcp_flags_key = be16_to_cpu(mt.key->flags); + tcp_flags_mask = be16_to_cpu(mt.mask->flags); + + if (tcp_flags_mask & TCPHDR_FIN) { + val = VCAP_BIT_0; + if (tcp_flags_key & TCPHDR_FIN) + val = VCAP_BIT_1; + err = vcap_rule_add_key_bit(st->vrule, VCAP_KF_L4_FIN, val); + if (err) + goto out; + } + + if (tcp_flags_mask & TCPHDR_SYN) { + val = VCAP_BIT_0; + if (tcp_flags_key & TCPHDR_SYN) + val = VCAP_BIT_1; + err = vcap_rule_add_key_bit(st->vrule, VCAP_KF_L4_SYN, val); + if (err) + goto out; + } + + if (tcp_flags_mask & TCPHDR_RST) { + val = VCAP_BIT_0; + if (tcp_flags_key & TCPHDR_RST) + val = VCAP_BIT_1; + err = vcap_rule_add_key_bit(st->vrule, VCAP_KF_L4_RST, val); + if (err) + goto out; + } + + if (tcp_flags_mask & TCPHDR_PSH) { + val = VCAP_BIT_0; + if (tcp_flags_key & TCPHDR_PSH) + val = VCAP_BIT_1; + err = vcap_rule_add_key_bit(st->vrule, VCAP_KF_L4_PSH, val); + if (err) + goto out; + } + + if (tcp_flags_mask & TCPHDR_ACK) { + val = VCAP_BIT_0; + if (tcp_flags_key & TCPHDR_ACK) + val = VCAP_BIT_1; + err = vcap_rule_add_key_bit(st->vrule, VCAP_KF_L4_ACK, val); + if (err) + goto out; + } + + if (tcp_flags_mask & TCPHDR_URG) { + val = VCAP_BIT_0; + if (tcp_flags_key & TCPHDR_URG) + val = VCAP_BIT_1; + err = vcap_rule_add_key_bit(st->vrule, VCAP_KF_L4_URG, val); + if (err) + goto out; + } + + st->used_keys |= BIT(FLOW_DISSECTOR_KEY_TCP); + + return err; + +out: + NL_SET_ERR_MSG_MOD(st->fco->common.extack, "tcp_flags parse error"); + return err; +} + +static int +sparx5_tc_flower_handler_ip_usage(struct sparx5_tc_flower_parse_usage *st) +{ + struct flow_match_ip mt; + int err = 0; + + flow_rule_match_ip(st->frule, &mt); + + if (mt.mask->tos) { + err = vcap_rule_add_key_u32(st->vrule, VCAP_KF_L3_TOS, + mt.key->tos, + mt.mask->tos); + if (err) + goto out; + } + + st->used_keys |= BIT(FLOW_DISSECTOR_KEY_IP); + + return err; + +out: + NL_SET_ERR_MSG_MOD(st->fco->common.extack, "ip_tos parse error"); + return err; +} + static int (*sparx5_tc_flower_usage_handlers[])(struct sparx5_tc_flower_parse_usage *st) = { - /* More dissector handlers will be added here later */ [FLOW_DISSECTOR_KEY_ETH_ADDRS] = sparx5_tc_flower_handler_ethaddr_usage, + [FLOW_DISSECTOR_KEY_IPV4_ADDRS] = sparx5_tc_flower_handler_ipv4_usage, + [FLOW_DISSECTOR_KEY_IPV6_ADDRS] = sparx5_tc_flower_handler_ipv6_usage, + [FLOW_DISSECTOR_KEY_CONTROL] = sparx5_tc_flower_handler_control_usage, + [FLOW_DISSECTOR_KEY_PORTS] = sparx5_tc_flower_handler_portnum_usage, + [FLOW_DISSECTOR_KEY_BASIC] = sparx5_tc_flower_handler_basic_usage, + [FLOW_DISSECTOR_KEY_VLAN] = sparx5_tc_flower_handler_vlan_usage, + [FLOW_DISSECTOR_KEY_TCP] = sparx5_tc_flower_handler_tcp_usage, + [FLOW_DISSECTOR_KEY_IP] = sparx5_tc_flower_handler_ip_usage, }; static int sparx5_tc_use_dissectors(struct flow_cls_offload *fco, diff --git a/drivers/net/ethernet/microchip/vcap/vcap_api.c b/drivers/net/ethernet/microchip/vcap/vcap_api.c index d255bc7deae7..ace2582d8552 100644 --- a/drivers/net/ethernet/microchip/vcap/vcap_api.c +++ b/drivers/net/ethernet/microchip/vcap/vcap_api.c @@ -1073,6 +1073,17 @@ int vcap_rule_add_key_u72(struct vcap_rule *rule, enum vcap_key_field key, } EXPORT_SYMBOL_GPL(vcap_rule_add_key_u72); +/* Add a 128 bit key with value and mask to the rule */ +int vcap_rule_add_key_u128(struct vcap_rule *rule, enum vcap_key_field key, + struct vcap_u128_key *fieldval) +{ + struct vcap_client_keyfield_data data; + + memcpy(&data.u128, fieldval, sizeof(data.u128)); + return vcap_rule_add_key(rule, key, VCAP_FIELD_U128, &data); +} +EXPORT_SYMBOL_GPL(vcap_rule_add_key_u128); + static void vcap_copy_from_client_actionfield(struct vcap_rule *rule, struct vcap_client_actionfield *field, struct vcap_client_actionfield_data *data) diff --git a/drivers/net/ethernet/microchip/vcap/vcap_api_client.h b/drivers/net/ethernet/microchip/vcap/vcap_api_client.h index 5df6808679ff..577395402a9a 100644 --- a/drivers/net/ethernet/microchip/vcap/vcap_api_client.h +++ b/drivers/net/ethernet/microchip/vcap/vcap_api_client.h @@ -176,6 +176,8 @@ int vcap_rule_add_key_u48(struct vcap_rule *rule, enum vcap_key_field key, struct vcap_u48_key *fieldval); int vcap_rule_add_key_u72(struct vcap_rule *rule, enum vcap_key_field key, struct vcap_u72_key *fieldval); +int vcap_rule_add_key_u128(struct vcap_rule *rule, enum vcap_key_field key, + struct vcap_u128_key *fieldval); int vcap_rule_add_action_bit(struct vcap_rule *rule, enum vcap_action_field action, enum vcap_bit val); int vcap_rule_add_action_u32(struct vcap_rule *rule, From 7de1dcadfaf9ada0d205fd50e3b0bde0e11e625b Mon Sep 17 00:00:00 2001 From: Steen Hegelund Date: Wed, 9 Nov 2022 12:41:11 +0100 Subject: [PATCH 0827/2002] net: microchip: sparx5: Find VCAP lookup from chain id Add a helper function that finds the lookup index in a VCAP instance from the chain id. Signed-off-by: Steen Hegelund Signed-off-by: David S. Miller --- drivers/net/ethernet/microchip/vcap/vcap_api.c | 17 +++++++++++++++++ .../ethernet/microchip/vcap/vcap_api_client.h | 2 ++ 2 files changed, 19 insertions(+) diff --git a/drivers/net/ethernet/microchip/vcap/vcap_api.c b/drivers/net/ethernet/microchip/vcap/vcap_api.c index ace2582d8552..d5b62e43d83f 100644 --- a/drivers/net/ethernet/microchip/vcap/vcap_api.c +++ b/drivers/net/ethernet/microchip/vcap/vcap_api.c @@ -644,6 +644,23 @@ static int vcap_write_rule(struct vcap_rule_internal *ri) return 0; } +/* Convert a chain id to a VCAP lookup index */ +int vcap_chain_id_to_lookup(struct vcap_admin *admin, int cur_cid) +{ + int lookup_first = admin->vinst * admin->lookups_per_instance; + int lookup_last = lookup_first + admin->lookups_per_instance; + int cid_next = admin->first_cid + VCAP_CID_LOOKUP_SIZE; + int cid = admin->first_cid; + int lookup; + + for (lookup = lookup_first; lookup < lookup_last; ++lookup, + cid += VCAP_CID_LOOKUP_SIZE, cid_next += VCAP_CID_LOOKUP_SIZE) + if (cur_cid >= cid && cur_cid < cid_next) + return lookup; + return 0; +} +EXPORT_SYMBOL_GPL(vcap_chain_id_to_lookup); + /* Lookup a vcap instance using chain id */ struct vcap_admin *vcap_find_admin(struct vcap_control *vctrl, int cid) { diff --git a/drivers/net/ethernet/microchip/vcap/vcap_api_client.h b/drivers/net/ethernet/microchip/vcap/vcap_api_client.h index 577395402a9a..7d9a227ef834 100644 --- a/drivers/net/ethernet/microchip/vcap/vcap_api_client.h +++ b/drivers/net/ethernet/microchip/vcap/vcap_api_client.h @@ -184,6 +184,8 @@ int vcap_rule_add_action_u32(struct vcap_rule *rule, enum vcap_action_field action, u32 value); /* VCAP lookup operations */ +/* Convert a chain id to a VCAP lookup index */ +int vcap_chain_id_to_lookup(struct vcap_admin *admin, int cur_cid); /* Lookup a vcap instance using chain id */ struct vcap_admin *vcap_find_admin(struct vcap_control *vctrl, int cid); /* Find information on a key field in a rule */ From 392d0ab048273c4d121a431de8fe030421794d9e Mon Sep 17 00:00:00 2001 From: Steen Hegelund Date: Wed, 9 Nov 2022 12:41:12 +0100 Subject: [PATCH 0828/2002] net: microchip: sparx5: Adding TC goto action and action checking Add support for a goto action and ensure that a HW offloaded TC flower filter has a valid goto action and that pass and trap actions are not both used in the same filter. Signed-off-by: Steen Hegelund Signed-off-by: David S. Miller --- .../microchip/sparx5/sparx5_tc_flower.c | 72 ++++++++++++++++--- .../net/ethernet/microchip/vcap/vcap_api.c | 36 ++++++++++ .../ethernet/microchip/vcap/vcap_api_client.h | 2 + 3 files changed, 101 insertions(+), 9 deletions(-) diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_tc_flower.c b/drivers/net/ethernet/microchip/sparx5/sparx5_tc_flower.c index 13bc6bff4c1e..6cd29d3c9250 100644 --- a/drivers/net/ethernet/microchip/sparx5/sparx5_tc_flower.c +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_tc_flower.c @@ -464,6 +464,60 @@ static int sparx5_tc_use_dissectors(struct flow_cls_offload *fco, return err; } +static int sparx5_tc_flower_action_check(struct vcap_control *vctrl, + struct flow_cls_offload *fco, + struct vcap_admin *admin) +{ + struct flow_rule *rule = flow_cls_offload_flow_rule(fco); + struct flow_action_entry *actent, *last_actent = NULL; + struct flow_action *act = &rule->action; + u64 action_mask = 0; + int idx; + + if (!flow_action_has_entries(act)) { + NL_SET_ERR_MSG_MOD(fco->common.extack, "No actions"); + return -EINVAL; + } + + if (!flow_action_basic_hw_stats_check(act, fco->common.extack)) + return -EOPNOTSUPP; + + flow_action_for_each(idx, actent, act) { + if (action_mask & BIT(actent->id)) { + NL_SET_ERR_MSG_MOD(fco->common.extack, + "More actions of the same type"); + return -EINVAL; + } + action_mask |= BIT(actent->id); + last_actent = actent; /* Save last action for later check */ + } + + /* Check that last action is a goto */ + if (last_actent->id != FLOW_ACTION_GOTO) { + NL_SET_ERR_MSG_MOD(fco->common.extack, + "Last action must be 'goto'"); + return -EINVAL; + } + + /* Check if the goto chain is in the next lookup */ + if (!vcap_is_next_lookup(vctrl, fco->common.chain_index, + last_actent->chain_index)) { + NL_SET_ERR_MSG_MOD(fco->common.extack, + "Invalid goto chain"); + return -EINVAL; + } + + /* Catch unsupported combinations of actions */ + if (action_mask & BIT(FLOW_ACTION_TRAP) && + action_mask & BIT(FLOW_ACTION_ACCEPT)) { + NL_SET_ERR_MSG_MOD(fco->common.extack, + "Cannot combine pass and trap action"); + return -EOPNOTSUPP; + } + + return 0; +} + static int sparx5_tc_flower_replace(struct net_device *ndev, struct flow_cls_offload *fco, struct vcap_admin *admin) @@ -475,16 +529,12 @@ static int sparx5_tc_flower_replace(struct net_device *ndev, struct vcap_rule *vrule; int err, idx; - frule = flow_cls_offload_flow_rule(fco); - if (!flow_action_has_entries(&frule->action)) { - NL_SET_ERR_MSG_MOD(fco->common.extack, "No actions"); - return -EINVAL; - } - - if (!flow_action_basic_hw_stats_check(&frule->action, fco->common.extack)) - return -EOPNOTSUPP; - vctrl = port->sparx5->vcap_ctrl; + + err = sparx5_tc_flower_action_check(vctrl, fco, admin); + if (err) + return err; + vrule = vcap_alloc_rule(vctrl, ndev, fco->common.chain_index, VCAP_USER_TC, fco->common.prio, 0); if (IS_ERR(vrule)) @@ -492,6 +542,7 @@ static int sparx5_tc_flower_replace(struct net_device *ndev, vrule->cookie = fco->cookie; sparx5_tc_use_dissectors(fco, admin, vrule); + frule = flow_cls_offload_flow_rule(fco); flow_action_for_each(idx, act, &frule->action) { switch (act->id) { case FLOW_ACTION_TRAP: @@ -521,6 +572,9 @@ static int sparx5_tc_flower_replace(struct net_device *ndev, if (err) goto out; break; + case FLOW_ACTION_GOTO: + /* Links between VCAPs will be added later */ + break; default: NL_SET_ERR_MSG_MOD(fco->common.extack, "Unsupported TC action"); diff --git a/drivers/net/ethernet/microchip/vcap/vcap_api.c b/drivers/net/ethernet/microchip/vcap/vcap_api.c index d5b62e43d83f..0dd9637933b2 100644 --- a/drivers/net/ethernet/microchip/vcap/vcap_api.c +++ b/drivers/net/ethernet/microchip/vcap/vcap_api.c @@ -677,6 +677,42 @@ struct vcap_admin *vcap_find_admin(struct vcap_control *vctrl, int cid) } EXPORT_SYMBOL_GPL(vcap_find_admin); +/* Is the next chain id in the following lookup, possible in another VCAP */ +bool vcap_is_next_lookup(struct vcap_control *vctrl, int cur_cid, int next_cid) +{ + struct vcap_admin *admin, *next_admin; + int lookup, next_lookup; + + /* The offset must be at least one lookup */ + if (next_cid < cur_cid + VCAP_CID_LOOKUP_SIZE) + return false; + + if (vcap_api_check(vctrl)) + return false; + + admin = vcap_find_admin(vctrl, cur_cid); + if (!admin) + return false; + + /* If no VCAP contains the next chain, the next chain must be beyond + * the last chain in the current VCAP + */ + next_admin = vcap_find_admin(vctrl, next_cid); + if (!next_admin) + return next_cid > admin->last_cid; + + lookup = vcap_chain_id_to_lookup(admin, cur_cid); + next_lookup = vcap_chain_id_to_lookup(next_admin, next_cid); + + /* Next lookup must be the following lookup */ + if (admin == next_admin || admin->vtype == next_admin->vtype) + return next_lookup == lookup + 1; + + /* Must be the first lookup in the next VCAP instance */ + return next_lookup == 0; +} +EXPORT_SYMBOL_GPL(vcap_is_next_lookup); + /* Check if there is room for a new rule */ static int vcap_rule_space(struct vcap_admin *admin, int size) { diff --git a/drivers/net/ethernet/microchip/vcap/vcap_api_client.h b/drivers/net/ethernet/microchip/vcap/vcap_api_client.h index 7d9a227ef834..5cecb12edec2 100644 --- a/drivers/net/ethernet/microchip/vcap/vcap_api_client.h +++ b/drivers/net/ethernet/microchip/vcap/vcap_api_client.h @@ -193,6 +193,8 @@ const struct vcap_field *vcap_lookup_keyfield(struct vcap_rule *rule, enum vcap_key_field key); /* Find a rule id with a provided cookie */ int vcap_lookup_rule_by_cookie(struct vcap_control *vctrl, u64 cookie); +/* Is the next chain id in the following lookup, possible in another VCAP */ +bool vcap_is_next_lookup(struct vcap_control *vctrl, int cur_cid, int next_cid); /* Copy to host byte order */ void vcap_netbytes_copy(u8 *dst, u8 *src, int count); From abc4010d1f6ec6a112bc273c41db13a8fb272d6b Mon Sep 17 00:00:00 2001 From: Steen Hegelund Date: Wed, 9 Nov 2022 12:41:13 +0100 Subject: [PATCH 0829/2002] net: microchip: sparx5: Match keys in configured port keysets This tries to match the keys in a rule with the keysets supported by the VCAP instance, and generate a list of keysets. This list is then validated against the list of keysets that is currently selected for the lookups (per port) in the VCAP configuration. The Sparx5 IS2 only has one actionset, so there is no actionset matching performed for now. Signed-off-by: Steen Hegelund Signed-off-by: David S. Miller --- .../microchip/sparx5/sparx5_tc_flower.c | 26 ++-- .../microchip/sparx5/sparx5_vcap_impl.c | 147 +++++++++++++++++- .../net/ethernet/microchip/vcap/vcap_api.c | 137 ++++++++++++++-- .../ethernet/microchip/vcap/vcap_api_client.h | 11 ++ 4 files changed, 298 insertions(+), 23 deletions(-) diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_tc_flower.c b/drivers/net/ethernet/microchip/sparx5/sparx5_tc_flower.c index 6cd29d3c9250..7b364f6b4546 100644 --- a/drivers/net/ethernet/microchip/sparx5/sparx5_tc_flower.c +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_tc_flower.c @@ -443,11 +443,13 @@ static int (*sparx5_tc_flower_usage_handlers[])(struct sparx5_tc_flower_parse_us static int sparx5_tc_use_dissectors(struct flow_cls_offload *fco, struct vcap_admin *admin, - struct vcap_rule *vrule) + struct vcap_rule *vrule, + u16 *l3_proto) { struct sparx5_tc_flower_parse_usage state = { .fco = fco, .vrule = vrule, + .l3_proto = ETH_P_ALL, }; int idx, err = 0; @@ -461,6 +463,15 @@ static int sparx5_tc_use_dissectors(struct flow_cls_offload *fco, if (err) return err; } + + if (state.frule->match.dissector->used_keys ^ state.used_keys) { + NL_SET_ERR_MSG_MOD(fco->common.extack, + "Unsupported match item"); + return -ENOENT; + } + + if (l3_proto) + *l3_proto = state.l3_proto; return err; } @@ -527,6 +538,7 @@ static int sparx5_tc_flower_replace(struct net_device *ndev, struct vcap_control *vctrl; struct flow_rule *frule; struct vcap_rule *vrule; + u16 l3_proto; int err, idx; vctrl = port->sparx5->vcap_ctrl; @@ -541,7 +553,7 @@ static int sparx5_tc_flower_replace(struct net_device *ndev, return PTR_ERR(vrule); vrule->cookie = fco->cookie; - sparx5_tc_use_dissectors(fco, admin, vrule); + sparx5_tc_use_dissectors(fco, admin, vrule, &l3_proto); frule = flow_cls_offload_flow_rule(fco); flow_action_for_each(idx, act, &frule->action) { switch (act->id) { @@ -582,14 +594,8 @@ static int sparx5_tc_flower_replace(struct net_device *ndev, goto out; } } - /* For now the keyset is hardcoded */ - err = vcap_set_rule_set_keyset(vrule, VCAP_KFS_MAC_ETYPE); - if (err) { - NL_SET_ERR_MSG_MOD(fco->common.extack, - "No matching port keyset for filter protocol and keys"); - goto out; - } - err = vcap_val_rule(vrule, ETH_P_ALL); + /* provide the l3 protocol to guide the keyset selection */ + err = vcap_val_rule(vrule, l3_proto); if (err) { vcap_set_tc_exterr(fco, vrule); goto out; diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_vcap_impl.c b/drivers/net/ethernet/microchip/sparx5/sparx5_vcap_impl.c index e4428d55af2b..642c27299e22 100644 --- a/drivers/net/ethernet/microchip/sparx5/sparx5_vcap_impl.c +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_vcap_impl.c @@ -160,7 +160,7 @@ static const char *sparx5_vcap_keyset_name(struct net_device *ndev, { struct sparx5_port *port = netdev_priv(ndev); - return port->sparx5->vcap_ctrl->stats->keyfield_set_names[keyset]; + return vcap_keyset_name(port->sparx5->vcap_ctrl, keyset); } /* Check if this is the first lookup of IS2 */ @@ -204,6 +204,127 @@ static void sparx5_vcap_add_wide_port_mask(struct vcap_rule *rule, vcap_rule_add_key_u72(rule, VCAP_KF_IF_IGR_PORT_MASK, &port_mask); } +/* Convert chain id to vcap lookup id */ +static int sparx5_vcap_cid_to_lookup(int cid) +{ + int lookup = 0; + + /* For now only handle IS2 */ + if (cid >= SPARX5_VCAP_CID_IS2_L1 && cid < SPARX5_VCAP_CID_IS2_L2) + lookup = 1; + else if (cid >= SPARX5_VCAP_CID_IS2_L2 && cid < SPARX5_VCAP_CID_IS2_L3) + lookup = 2; + else if (cid >= SPARX5_VCAP_CID_IS2_L3 && cid < SPARX5_VCAP_CID_IS2_MAX) + lookup = 3; + + return lookup; +} + +/* Return the list of keysets for the vcap port configuration */ +static int sparx5_vcap_is2_get_port_keysets(struct net_device *ndev, + int lookup, + struct vcap_keyset_list *keysetlist, + u16 l3_proto) +{ + struct sparx5_port *port = netdev_priv(ndev); + struct sparx5 *sparx5 = port->sparx5; + int portno = port->portno; + u32 value; + + /* Check if the port keyset selection is enabled */ + value = spx5_rd(sparx5, ANA_ACL_VCAP_S2_KEY_SEL(portno, lookup)); + if (!ANA_ACL_VCAP_S2_KEY_SEL_KEY_SEL_ENA_GET(value)) + return -ENOENT; + + /* Collect all keysets for the port in a list */ + if (l3_proto == ETH_P_ALL || l3_proto == ETH_P_ARP) { + switch (ANA_ACL_VCAP_S2_KEY_SEL_ARP_KEY_SEL_GET(value)) { + case VCAP_IS2_PS_ARP_MAC_ETYPE: + vcap_keyset_list_add(keysetlist, VCAP_KFS_MAC_ETYPE); + break; + case VCAP_IS2_PS_ARP_ARP: + vcap_keyset_list_add(keysetlist, VCAP_KFS_ARP); + break; + } + } + + if (l3_proto == ETH_P_ALL || l3_proto == ETH_P_IP) { + switch (ANA_ACL_VCAP_S2_KEY_SEL_IP4_UC_KEY_SEL_GET(value)) { + case VCAP_IS2_PS_IPV4_UC_MAC_ETYPE: + vcap_keyset_list_add(keysetlist, VCAP_KFS_MAC_ETYPE); + break; + case VCAP_IS2_PS_IPV4_UC_IP4_TCP_UDP_OTHER: + vcap_keyset_list_add(keysetlist, VCAP_KFS_IP4_TCP_UDP); + vcap_keyset_list_add(keysetlist, VCAP_KFS_IP4_OTHER); + break; + case VCAP_IS2_PS_IPV4_UC_IP_7TUPLE: + vcap_keyset_list_add(keysetlist, VCAP_KFS_IP_7TUPLE); + break; + } + + switch (ANA_ACL_VCAP_S2_KEY_SEL_IP4_MC_KEY_SEL_GET(value)) { + case VCAP_IS2_PS_IPV4_MC_MAC_ETYPE: + vcap_keyset_list_add(keysetlist, VCAP_KFS_MAC_ETYPE); + break; + case VCAP_IS2_PS_IPV4_MC_IP4_TCP_UDP_OTHER: + vcap_keyset_list_add(keysetlist, VCAP_KFS_IP4_TCP_UDP); + vcap_keyset_list_add(keysetlist, VCAP_KFS_IP4_OTHER); + break; + case VCAP_IS2_PS_IPV4_MC_IP_7TUPLE: + vcap_keyset_list_add(keysetlist, VCAP_KFS_IP_7TUPLE); + break; + } + } + + if (l3_proto == ETH_P_ALL || l3_proto == ETH_P_IPV6) { + switch (ANA_ACL_VCAP_S2_KEY_SEL_IP6_UC_KEY_SEL_GET(value)) { + case VCAP_IS2_PS_IPV6_UC_MAC_ETYPE: + vcap_keyset_list_add(keysetlist, VCAP_KFS_MAC_ETYPE); + break; + case VCAP_IS2_PS_IPV6_UC_IP_7TUPLE: + vcap_keyset_list_add(keysetlist, VCAP_KFS_IP_7TUPLE); + break; + case VCAP_IS2_PS_IPV6_UC_IP6_STD: + vcap_keyset_list_add(keysetlist, VCAP_KFS_IP6_STD); + break; + case VCAP_IS2_PS_IPV6_UC_IP4_TCP_UDP_OTHER: + vcap_keyset_list_add(keysetlist, VCAP_KFS_IP4_TCP_UDP); + vcap_keyset_list_add(keysetlist, VCAP_KFS_IP4_OTHER); + break; + } + + switch (ANA_ACL_VCAP_S2_KEY_SEL_IP6_MC_KEY_SEL_GET(value)) { + case VCAP_IS2_PS_IPV6_MC_MAC_ETYPE: + vcap_keyset_list_add(keysetlist, VCAP_KFS_MAC_ETYPE); + break; + case VCAP_IS2_PS_IPV6_MC_IP_7TUPLE: + vcap_keyset_list_add(keysetlist, VCAP_KFS_IP_7TUPLE); + break; + case VCAP_IS2_PS_IPV6_MC_IP6_STD: + vcap_keyset_list_add(keysetlist, VCAP_KFS_IP6_STD); + break; + case VCAP_IS2_PS_IPV6_MC_IP4_TCP_UDP_OTHER: + vcap_keyset_list_add(keysetlist, VCAP_KFS_IP4_TCP_UDP); + vcap_keyset_list_add(keysetlist, VCAP_KFS_IP4_OTHER); + break; + case VCAP_IS2_PS_IPV6_MC_IP6_VID: + /* Not used */ + break; + } + } + + if (l3_proto != ETH_P_ARP && l3_proto != ETH_P_IP && + l3_proto != ETH_P_IPV6) { + switch (ANA_ACL_VCAP_S2_KEY_SEL_NON_ETH_KEY_SEL_GET(value)) { + case VCAP_IS2_PS_NONETH_MAC_ETYPE: + /* IS2 non-classified frames generate MAC_ETYPE */ + vcap_keyset_list_add(keysetlist, VCAP_KFS_MAC_ETYPE); + break; + } + } + return 0; +} + /* API callback used for validating a field keyset (check the port keysets) */ static enum vcap_keyfield_set sparx5_vcap_validate_keyset(struct net_device *ndev, @@ -212,10 +333,30 @@ sparx5_vcap_validate_keyset(struct net_device *ndev, struct vcap_keyset_list *kslist, u16 l3_proto) { + struct vcap_keyset_list keysetlist = {}; + enum vcap_keyfield_set keysets[10] = {}; + int idx, jdx, lookup; + if (!kslist || kslist->cnt == 0) return VCAP_KFS_NO_VALUE; - /* for now just return whatever the API suggests */ - return kslist->keysets[0]; + + /* Get a list of currently configured keysets in the lookups */ + lookup = sparx5_vcap_cid_to_lookup(rule->vcap_chain_id); + keysetlist.max = ARRAY_SIZE(keysets); + keysetlist.keysets = keysets; + sparx5_vcap_is2_get_port_keysets(ndev, lookup, &keysetlist, l3_proto); + + /* Check if there is a match and return the match */ + for (idx = 0; idx < kslist->cnt; ++idx) + for (jdx = 0; jdx < keysetlist.cnt; ++jdx) + if (kslist->keysets[idx] == keysets[jdx]) + return kslist->keysets[idx]; + + pr_err("%s:%d: %s not supported in port key selection\n", + __func__, __LINE__, + sparx5_vcap_keyset_name(ndev, kslist->keysets[0])); + + return -ENOENT; } /* API callback used for adding default fields to a rule */ diff --git a/drivers/net/ethernet/microchip/vcap/vcap_api.c b/drivers/net/ethernet/microchip/vcap/vcap_api.c index 0dd9637933b2..37122ba1e201 100644 --- a/drivers/net/ethernet/microchip/vcap/vcap_api.c +++ b/drivers/net/ethernet/microchip/vcap/vcap_api.c @@ -757,15 +757,115 @@ static int vcap_add_type_keyfield(struct vcap_rule *rule) return 0; } +/* Add a keyset to a keyset list */ +bool vcap_keyset_list_add(struct vcap_keyset_list *keysetlist, + enum vcap_keyfield_set keyset) +{ + int idx; + + if (keysetlist->cnt < keysetlist->max) { + /* Avoid duplicates */ + for (idx = 0; idx < keysetlist->cnt; ++idx) + if (keysetlist->keysets[idx] == keyset) + return keysetlist->cnt < keysetlist->max; + keysetlist->keysets[keysetlist->cnt++] = keyset; + } + return keysetlist->cnt < keysetlist->max; +} +EXPORT_SYMBOL_GPL(vcap_keyset_list_add); + +/* map keyset id to a string with the keyset name */ +const char *vcap_keyset_name(struct vcap_control *vctrl, + enum vcap_keyfield_set keyset) +{ + return vctrl->stats->keyfield_set_names[keyset]; +} +EXPORT_SYMBOL_GPL(vcap_keyset_name); + +/* map key field id to a string with the key name */ +const char *vcap_keyfield_name(struct vcap_control *vctrl, + enum vcap_key_field key) +{ + return vctrl->stats->keyfield_names[key]; +} +EXPORT_SYMBOL_GPL(vcap_keyfield_name); + +/* Return the keyfield that matches a key in a keyset */ +static const struct vcap_field * +vcap_find_keyset_keyfield(struct vcap_control *vctrl, + enum vcap_type vtype, + enum vcap_keyfield_set keyset, + enum vcap_key_field key) +{ + const struct vcap_field *fields; + int idx, count; + + fields = vcap_keyfields(vctrl, vtype, keyset); + if (!fields) + return NULL; + + /* Iterate the keyfields of the keyset */ + count = vcap_keyfield_count(vctrl, vtype, keyset); + for (idx = 0; idx < count; ++idx) { + if (fields[idx].width == 0) + continue; + + if (key == idx) + return &fields[idx]; + } + + return NULL; +} + +/* Match a list of keys against the keysets available in a vcap type */ +static bool vcap_rule_find_keysets(struct vcap_rule_internal *ri, + struct vcap_keyset_list *matches) +{ + const struct vcap_client_keyfield *ckf; + int keyset, found, keycount, map_size; + const struct vcap_field **map; + enum vcap_type vtype; + + vtype = ri->admin->vtype; + map = ri->vctrl->vcaps[vtype].keyfield_set_map; + map_size = ri->vctrl->vcaps[vtype].keyfield_set_size; + + /* Get a count of the keyfields we want to match */ + keycount = 0; + list_for_each_entry(ckf, &ri->data.keyfields, ctrl.list) + ++keycount; + + matches->cnt = 0; + /* Iterate the keysets of the VCAP */ + for (keyset = 0; keyset < map_size; ++keyset) { + if (!map[keyset]) + continue; + + /* Iterate the keys in the rule */ + found = 0; + list_for_each_entry(ckf, &ri->data.keyfields, ctrl.list) + if (vcap_find_keyset_keyfield(ri->vctrl, vtype, + keyset, ckf->ctrl.key)) + ++found; + + /* Save the keyset if all keyfields were found */ + if (found == keycount) + if (!vcap_keyset_list_add(matches, keyset)) + /* bail out when the quota is filled */ + break; + } + + return matches->cnt > 0; +} + /* Validate a rule with respect to available port keys */ int vcap_val_rule(struct vcap_rule *rule, u16 l3_proto) { struct vcap_rule_internal *ri = to_intrule(rule); + struct vcap_keyset_list matches = {}; enum vcap_keyfield_set keysets[10]; - struct vcap_keyset_list kslist; int ret; - /* This validation will be much expanded later */ ret = vcap_api_check(ri->vctrl); if (ret) return ret; @@ -777,24 +877,41 @@ int vcap_val_rule(struct vcap_rule *rule, u16 l3_proto) ri->data.exterr = VCAP_ERR_NO_NETDEV; return -EINVAL; } + + matches.keysets = keysets; + matches.max = ARRAY_SIZE(keysets); if (ri->data.keyset == VCAP_KFS_NO_VALUE) { - ri->data.exterr = VCAP_ERR_NO_KEYSET_MATCH; - return -EINVAL; + /* Iterate over rule keyfields and select keysets that fits */ + if (!vcap_rule_find_keysets(ri, &matches)) { + ri->data.exterr = VCAP_ERR_NO_KEYSET_MATCH; + return -EINVAL; + } + } else { + /* prepare for keyset validation */ + keysets[0] = ri->data.keyset; + matches.cnt = 1; } - /* prepare for keyset validation */ - keysets[0] = ri->data.keyset; - kslist.keysets = keysets; - kslist.cnt = 1; + /* Pick a keyset that is supported in the port lookups */ - ret = ri->vctrl->ops->validate_keyset(ri->ndev, ri->admin, rule, &kslist, - l3_proto); + ret = ri->vctrl->ops->validate_keyset(ri->ndev, ri->admin, rule, + &matches, l3_proto); if (ret < 0) { pr_err("%s:%d: keyset validation failed: %d\n", __func__, __LINE__, ret); ri->data.exterr = VCAP_ERR_NO_PORT_KEYSET_MATCH; return ret; } + /* use the keyset that is supported in the port lookups */ + ret = vcap_set_rule_set_keyset(rule, ret); + if (ret < 0) { + pr_err("%s:%d: keyset was not updated: %d\n", + __func__, __LINE__, ret); + return ret; + } if (ri->data.actionset == VCAP_AFS_NO_VALUE) { + /* Later also actionsets will be matched against actions in + * the rule, and the type will be set accordingly + */ ri->data.exterr = VCAP_ERR_NO_ACTIONSET_MATCH; return -EINVAL; } diff --git a/drivers/net/ethernet/microchip/vcap/vcap_api_client.h b/drivers/net/ethernet/microchip/vcap/vcap_api_client.h index 5cecb12edec2..077e49c4f3be 100644 --- a/drivers/net/ethernet/microchip/vcap/vcap_api_client.h +++ b/drivers/net/ethernet/microchip/vcap/vcap_api_client.h @@ -205,4 +205,15 @@ void vcap_set_tc_exterr(struct flow_cls_offload *fco, struct vcap_rule *vrule); /* Cleanup a VCAP instance */ int vcap_del_rules(struct vcap_control *vctrl, struct vcap_admin *admin); +/* Add a keyset to a keyset list */ +bool vcap_keyset_list_add(struct vcap_keyset_list *keysetlist, + enum vcap_keyfield_set keyset); + +/* map keyset id to a string with the keyset name */ +const char *vcap_keyset_name(struct vcap_control *vctrl, + enum vcap_keyfield_set keyset); +/* map key field id to a string with the key name */ +const char *vcap_keyfield_name(struct vcap_control *vctrl, + enum vcap_key_field key); + #endif /* __VCAP_API_CLIENT__ */ From 242df4f7f2cd6d17a445d43852496ad706da344b Mon Sep 17 00:00:00 2001 From: Steen Hegelund Date: Wed, 9 Nov 2022 12:41:14 +0100 Subject: [PATCH 0830/2002] net: microchip: sparx5: Let VCAP API validate added key- and actionfields Add support for validating keyfields and actionfields when they are added to a VCAP rule. We need to ensure that the field is not already present and that the field is in the key- or actionset, if the client has added a key- or actionset to the rule at this point. Signed-off-by: Steen Hegelund Signed-off-by: David S. Miller --- .../net/ethernet/microchip/vcap/vcap_api.c | 103 +++++++++++++++++- 1 file changed, 101 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/microchip/vcap/vcap_api.c b/drivers/net/ethernet/microchip/vcap/vcap_api.c index 37122ba1e201..73ec7744c21f 100644 --- a/drivers/net/ethernet/microchip/vcap/vcap_api.c +++ b/drivers/net/ethernet/microchip/vcap/vcap_api.c @@ -790,6 +790,13 @@ const char *vcap_keyfield_name(struct vcap_control *vctrl, } EXPORT_SYMBOL_GPL(vcap_keyfield_name); +/* map action field id to a string with the action name */ +static const char *vcap_actionfield_name(struct vcap_control *vctrl, + enum vcap_action_field action) +{ + return vctrl->stats->actionfield_names[action]; +} + /* Return the keyfield that matches a key in a keyset */ static const struct vcap_field * vcap_find_keyset_keyfield(struct vcap_control *vctrl, @@ -1162,14 +1169,60 @@ static void vcap_copy_from_client_keyfield(struct vcap_rule *rule, memcpy(&field->data, data, sizeof(field->data)); } +/* Check if the keyfield is already in the rule */ +static bool vcap_keyfield_unique(struct vcap_rule *rule, + enum vcap_key_field key) +{ + struct vcap_rule_internal *ri = to_intrule(rule); + const struct vcap_client_keyfield *ckf; + + list_for_each_entry(ckf, &ri->data.keyfields, ctrl.list) + if (ckf->ctrl.key == key) + return false; + return true; +} + +/* Check if the keyfield is in the keyset */ +static bool vcap_keyfield_match_keyset(struct vcap_rule *rule, + enum vcap_key_field key) +{ + struct vcap_rule_internal *ri = to_intrule(rule); + enum vcap_keyfield_set keyset = rule->keyset; + enum vcap_type vt = ri->admin->vtype; + const struct vcap_field *fields; + + /* the field is accepted if the rule has no keyset yet */ + if (keyset == VCAP_KFS_NO_VALUE) + return true; + fields = vcap_keyfields(ri->vctrl, vt, keyset); + if (!fields) + return false; + /* if there is a width there is a way */ + return fields[key].width > 0; +} + static int vcap_rule_add_key(struct vcap_rule *rule, enum vcap_key_field key, enum vcap_field_type ftype, struct vcap_client_keyfield_data *data) { + struct vcap_rule_internal *ri = to_intrule(rule); struct vcap_client_keyfield *field; - /* More validation will be added here later */ + if (!vcap_keyfield_unique(rule, key)) { + pr_warn("%s:%d: keyfield %s is already in the rule\n", + __func__, __LINE__, + vcap_keyfield_name(ri->vctrl, key)); + return -EINVAL; + } + + if (!vcap_keyfield_match_keyset(rule, key)) { + pr_err("%s:%d: keyfield %s does not belong in the rule keyset\n", + __func__, __LINE__, + vcap_keyfield_name(ri->vctrl, key)); + return -EINVAL; + } + field = kzalloc(sizeof(*field), GFP_KERNEL); if (!field) return -ENOMEM; @@ -1262,14 +1315,60 @@ static void vcap_copy_from_client_actionfield(struct vcap_rule *rule, memcpy(&field->data, data, sizeof(field->data)); } +/* Check if the actionfield is already in the rule */ +static bool vcap_actionfield_unique(struct vcap_rule *rule, + enum vcap_action_field act) +{ + struct vcap_rule_internal *ri = to_intrule(rule); + const struct vcap_client_actionfield *caf; + + list_for_each_entry(caf, &ri->data.actionfields, ctrl.list) + if (caf->ctrl.action == act) + return false; + return true; +} + +/* Check if the actionfield is in the actionset */ +static bool vcap_actionfield_match_actionset(struct vcap_rule *rule, + enum vcap_action_field action) +{ + enum vcap_actionfield_set actionset = rule->actionset; + struct vcap_rule_internal *ri = to_intrule(rule); + enum vcap_type vt = ri->admin->vtype; + const struct vcap_field *fields; + + /* the field is accepted if the rule has no actionset yet */ + if (actionset == VCAP_AFS_NO_VALUE) + return true; + fields = vcap_actionfields(ri->vctrl, vt, actionset); + if (!fields) + return false; + /* if there is a width there is a way */ + return fields[action].width > 0; +} + static int vcap_rule_add_action(struct vcap_rule *rule, enum vcap_action_field action, enum vcap_field_type ftype, struct vcap_client_actionfield_data *data) { + struct vcap_rule_internal *ri = to_intrule(rule); struct vcap_client_actionfield *field; - /* More validation will be added here later */ + if (!vcap_actionfield_unique(rule, action)) { + pr_warn("%s:%d: actionfield %s is already in the rule\n", + __func__, __LINE__, + vcap_actionfield_name(ri->vctrl, action)); + return -EINVAL; + } + + if (!vcap_actionfield_match_actionset(rule, action)) { + pr_err("%s:%d: actionfield %s does not belong in the rule actionset\n", + __func__, __LINE__, + vcap_actionfield_name(ri->vctrl, action)); + return -EINVAL; + } + field = kzalloc(sizeof(*field), GFP_KERNEL); if (!field) return -ENOMEM; From 67456717012cac9aa952b308f71f0dd023998463 Mon Sep 17 00:00:00 2001 From: Steen Hegelund Date: Wed, 9 Nov 2022 12:41:15 +0100 Subject: [PATCH 0831/2002] net: microchip: sparx5: Add tc matchall filter and enable VCAP lookups Use a tc matchall rule with a goto action to the VCAP specific chain to enable the VCAP lookups. If the matchall rule is removed the VCAP lookups will be disabled again using its cookie as lookup to find the VCAP instance. To enable the Sparx5 IS2 VCAP on eth0 you would use this command: tc filter add dev eth0 ingress prio 5 handle 5 matchall \ skip_sw action goto chain 8000000 as the first lookup in IS2 has chain id 8000000 Signed-off-by: Steen Hegelund Signed-off-by: David S. Miller --- .../net/ethernet/microchip/sparx5/Makefile | 2 +- .../net/ethernet/microchip/sparx5/sparx5_tc.c | 9 +- .../net/ethernet/microchip/sparx5/sparx5_tc.h | 5 + .../microchip/sparx5/sparx5_tc_matchall.c | 97 ++++++++++++++ .../microchip/sparx5/sparx5_vcap_impl.c | 29 ++++- .../net/ethernet/microchip/vcap/vcap_api.c | 120 +++++++++++++++++- .../net/ethernet/microchip/vcap/vcap_api.h | 6 + .../ethernet/microchip/vcap/vcap_api_client.h | 4 + 8 files changed, 263 insertions(+), 9 deletions(-) create mode 100644 drivers/net/ethernet/microchip/sparx5/sparx5_tc_matchall.c diff --git a/drivers/net/ethernet/microchip/sparx5/Makefile b/drivers/net/ethernet/microchip/sparx5/Makefile index 38adf917bc09..cff07b8841bd 100644 --- a/drivers/net/ethernet/microchip/sparx5/Makefile +++ b/drivers/net/ethernet/microchip/sparx5/Makefile @@ -9,7 +9,7 @@ sparx5-switch-y := sparx5_main.o sparx5_packet.o \ sparx5_netdev.o sparx5_phylink.o sparx5_port.o sparx5_mactable.o sparx5_vlan.o \ sparx5_switchdev.o sparx5_calendar.o sparx5_ethtool.o sparx5_fdma.o \ sparx5_ptp.o sparx5_pgid.o sparx5_tc.o sparx5_qos.o \ - sparx5_vcap_impl.o sparx5_vcap_ag_api.o sparx5_tc_flower.o + sparx5_vcap_impl.o sparx5_vcap_ag_api.o sparx5_tc_flower.o sparx5_tc_matchall.o sparx5-switch-$(CONFIG_SPARX5_DCB) += sparx5_dcb.o diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_tc.c b/drivers/net/ethernet/microchip/sparx5/sparx5_tc.c index 9432251b8322..edd4c53dcce2 100644 --- a/drivers/net/ethernet/microchip/sparx5/sparx5_tc.c +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_tc.c @@ -19,9 +19,14 @@ static int sparx5_tc_block_cb(enum tc_setup_type type, { struct net_device *ndev = cb_priv; - if (type == TC_SETUP_CLSFLOWER) + switch (type) { + case TC_SETUP_CLSMATCHALL: + return sparx5_tc_matchall(ndev, type_data, ingress); + case TC_SETUP_CLSFLOWER: return sparx5_tc_flower(ndev, type_data, ingress); - return -EOPNOTSUPP; + default: + return -EOPNOTSUPP; + } } static int sparx5_tc_block_cb_ingress(enum tc_setup_type type, diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_tc.h b/drivers/net/ethernet/microchip/sparx5/sparx5_tc.h index 2b07a93fc9b7..adab88e6b21f 100644 --- a/drivers/net/ethernet/microchip/sparx5/sparx5_tc.h +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_tc.h @@ -8,6 +8,7 @@ #define __SPARX5_TC_H__ #include +#include #include /* Controls how PORT_MASK is applied */ @@ -23,6 +24,10 @@ enum SPX5_PORT_MASK_MODE { int sparx5_port_setup_tc(struct net_device *ndev, enum tc_setup_type type, void *type_data); +int sparx5_tc_matchall(struct net_device *ndev, + struct tc_cls_matchall_offload *tmo, + bool ingress); + int sparx5_tc_flower(struct net_device *ndev, struct flow_cls_offload *fco, bool ingress); diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_tc_matchall.c b/drivers/net/ethernet/microchip/sparx5/sparx5_tc_matchall.c new file mode 100644 index 000000000000..30dd61e5d150 --- /dev/null +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_tc_matchall.c @@ -0,0 +1,97 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* Microchip VCAP API + * + * Copyright (c) 2022 Microchip Technology Inc. and its subsidiaries. + */ + +#include "sparx5_tc.h" +#include "vcap_api.h" +#include "vcap_api_client.h" +#include "sparx5_main_regs.h" +#include "sparx5_main.h" +#include "sparx5_vcap_impl.h" + +static int sparx5_tc_matchall_replace(struct net_device *ndev, + struct tc_cls_matchall_offload *tmo, + bool ingress) +{ + struct sparx5_port *port = netdev_priv(ndev); + struct flow_action_entry *action; + struct sparx5 *sparx5; + int err; + + if (!flow_offload_has_one_action(&tmo->rule->action)) { + NL_SET_ERR_MSG_MOD(tmo->common.extack, + "Only one action per filter is supported"); + return -EOPNOTSUPP; + } + action = &tmo->rule->action.entries[0]; + + sparx5 = port->sparx5; + switch (action->id) { + case FLOW_ACTION_GOTO: + err = vcap_enable_lookups(sparx5->vcap_ctrl, ndev, + action->chain_index, tmo->cookie, + true); + if (err == -EFAULT) { + NL_SET_ERR_MSG_MOD(tmo->common.extack, + "Unsupported goto chain"); + return -EOPNOTSUPP; + } + if (err == -EADDRINUSE) { + NL_SET_ERR_MSG_MOD(tmo->common.extack, + "VCAP already enabled"); + return -EOPNOTSUPP; + } + if (err) { + NL_SET_ERR_MSG_MOD(tmo->common.extack, + "Could not enable VCAP lookups"); + return err; + } + break; + default: + NL_SET_ERR_MSG_MOD(tmo->common.extack, "Unsupported action"); + return -EOPNOTSUPP; + } + return 0; +} + +static int sparx5_tc_matchall_destroy(struct net_device *ndev, + struct tc_cls_matchall_offload *tmo, + bool ingress) +{ + struct sparx5_port *port = netdev_priv(ndev); + struct sparx5 *sparx5; + int err; + + sparx5 = port->sparx5; + if (!tmo->rule && tmo->cookie) { + err = vcap_enable_lookups(sparx5->vcap_ctrl, ndev, 0, + tmo->cookie, false); + if (err) + return err; + return 0; + } + NL_SET_ERR_MSG_MOD(tmo->common.extack, "Unsupported action"); + return -EOPNOTSUPP; +} + +int sparx5_tc_matchall(struct net_device *ndev, + struct tc_cls_matchall_offload *tmo, + bool ingress) +{ + if (!tc_cls_can_offload_and_chain0(ndev, &tmo->common)) { + NL_SET_ERR_MSG_MOD(tmo->common.extack, + "Only chain zero is supported"); + return -EOPNOTSUPP; + } + + switch (tmo->command) { + case TC_CLSMATCHALL_REPLACE: + return sparx5_tc_matchall_replace(ndev, tmo, ingress); + case TC_CLSMATCHALL_DESTROY: + return sparx5_tc_matchall_destroy(ndev, tmo, ingress); + default: + return -EOPNOTSUPP; + } +} diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_vcap_impl.c b/drivers/net/ethernet/microchip/sparx5/sparx5_vcap_impl.c index 642c27299e22..10bc56cd0045 100644 --- a/drivers/net/ethernet/microchip/sparx5/sparx5_vcap_impl.c +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_vcap_impl.c @@ -489,6 +489,28 @@ static int sparx5_port_info(struct net_device *ndev, enum vcap_type vtype, return 0; } +/* Enable all lookups in the VCAP instance */ +static int sparx5_vcap_enable(struct net_device *ndev, + struct vcap_admin *admin, + bool enable) +{ + struct sparx5_port *port = netdev_priv(ndev); + struct sparx5 *sparx5; + int portno; + + sparx5 = port->sparx5; + portno = port->portno; + + /* For now we only consider IS2 */ + if (enable) + spx5_wr(ANA_ACL_VCAP_S2_CFG_SEC_ENA_SET(0xf), sparx5, + ANA_ACL_VCAP_S2_CFG(portno)); + else + spx5_wr(ANA_ACL_VCAP_S2_CFG_SEC_ENA_SET(0), sparx5, + ANA_ACL_VCAP_S2_CFG(portno)); + return 0; +} + /* API callback operations: only IS2 is supported for now */ static struct vcap_operations sparx5_vcap_ops = { .validate_keyset = sparx5_vcap_validate_keyset, @@ -500,6 +522,7 @@ static struct vcap_operations sparx5_vcap_ops = { .update = sparx5_vcap_update, .move = sparx5_vcap_move, .port_info = sparx5_port_info, + .enable = sparx5_vcap_enable, }; /* Enable lookups per port and set the keyset generation: only IS2 for now */ @@ -509,11 +532,6 @@ static void sparx5_vcap_port_key_selection(struct sparx5 *sparx5, int portno, lookup; u32 keysel; - /* enable all 4 lookups on all ports */ - for (portno = 0; portno < SPX5_PORTS; ++portno) - spx5_wr(ANA_ACL_VCAP_S2_CFG_SEC_ENA_SET(0xf), sparx5, - ANA_ACL_VCAP_S2_CFG(portno)); - /* all traffic types generate the MAC_ETYPE keyset for now in all * lookups on all ports */ @@ -566,6 +584,7 @@ sparx5_vcap_admin_alloc(struct sparx5 *sparx5, struct vcap_control *ctrl, return ERR_PTR(-ENOMEM); INIT_LIST_HEAD(&admin->list); INIT_LIST_HEAD(&admin->rules); + INIT_LIST_HEAD(&admin->enabled); admin->vtype = cfg->vtype; admin->vinst = cfg->vinst; admin->lookups = cfg->lookups; diff --git a/drivers/net/ethernet/microchip/vcap/vcap_api.c b/drivers/net/ethernet/microchip/vcap/vcap_api.c index 73ec7744c21f..b6ab6bae28c0 100644 --- a/drivers/net/ethernet/microchip/vcap/vcap_api.c +++ b/drivers/net/ethernet/microchip/vcap/vcap_api.c @@ -44,6 +44,13 @@ struct vcap_stream_iter { const struct vcap_typegroup *tg; /* current typegroup */ }; +/* Stores the filter cookie that enabled the port */ +struct vcap_enabled_port { + struct list_head list; /* for insertion in enabled ports list */ + struct net_device *ndev; /* the enabled port */ + unsigned long cookie; /* filter that enabled the port */ +}; + static void vcap_iter_set(struct vcap_stream_iter *itr, int sw_width, const struct vcap_typegroup *tg, u32 offset) { @@ -516,7 +523,7 @@ static int vcap_api_check(struct vcap_control *ctrl) !ctrl->ops->add_default_fields || !ctrl->ops->cache_erase || !ctrl->ops->cache_write || !ctrl->ops->cache_read || !ctrl->ops->init || !ctrl->ops->update || !ctrl->ops->move || - !ctrl->ops->port_info) { + !ctrl->ops->port_info || !ctrl->ops->enable) { pr_err("%s:%d: client operations are missing\n", __func__, __LINE__); return -ENOENT; @@ -1128,6 +1135,7 @@ EXPORT_SYMBOL_GPL(vcap_del_rule); /* Delete all rules in the VCAP instance */ int vcap_del_rules(struct vcap_control *vctrl, struct vcap_admin *admin) { + struct vcap_enabled_port *eport, *next_eport; struct vcap_rule_internal *ri, *next_ri; int ret = vcap_api_check(vctrl); @@ -1139,6 +1147,13 @@ int vcap_del_rules(struct vcap_control *vctrl, struct vcap_admin *admin) kfree(ri); } admin->last_used_addr = admin->last_valid_addr; + + /* Remove list of enabled ports */ + list_for_each_entry_safe(eport, next_eport, &admin->enabled, list) { + list_del(&eport->list); + kfree(eport); + } + return 0; } EXPORT_SYMBOL_GPL(vcap_del_rules); @@ -1459,6 +1474,109 @@ void vcap_set_tc_exterr(struct flow_cls_offload *fco, struct vcap_rule *vrule) } EXPORT_SYMBOL_GPL(vcap_set_tc_exterr); +/* Check if this port is already enabled for this VCAP instance */ +static bool vcap_is_enabled(struct vcap_admin *admin, struct net_device *ndev, + unsigned long cookie) +{ + struct vcap_enabled_port *eport; + + list_for_each_entry(eport, &admin->enabled, list) + if (eport->cookie == cookie || eport->ndev == ndev) + return true; + + return false; +} + +/* Enable this port for this VCAP instance */ +static int vcap_enable(struct vcap_admin *admin, struct net_device *ndev, + unsigned long cookie) +{ + struct vcap_enabled_port *eport; + + eport = kzalloc(sizeof(*eport), GFP_KERNEL); + if (!eport) + return -ENOMEM; + + eport->ndev = ndev; + eport->cookie = cookie; + list_add_tail(&eport->list, &admin->enabled); + + return 0; +} + +/* Disable this port for this VCAP instance */ +static int vcap_disable(struct vcap_admin *admin, struct net_device *ndev, + unsigned long cookie) +{ + struct vcap_enabled_port *eport; + + list_for_each_entry(eport, &admin->enabled, list) { + if (eport->cookie == cookie && eport->ndev == ndev) { + list_del(&eport->list); + kfree(eport); + return 0; + } + } + + return -ENOENT; +} + +/* Find the VCAP instance that enabled the port using a specific filter */ +static struct vcap_admin *vcap_find_admin_by_cookie(struct vcap_control *vctrl, + unsigned long cookie) +{ + struct vcap_enabled_port *eport; + struct vcap_admin *admin; + + list_for_each_entry(admin, &vctrl->list, list) + list_for_each_entry(eport, &admin->enabled, list) + if (eport->cookie == cookie) + return admin; + + return NULL; +} + +/* Enable/Disable the VCAP instance lookups. Chain id 0 means disable */ +int vcap_enable_lookups(struct vcap_control *vctrl, struct net_device *ndev, + int chain_id, unsigned long cookie, bool enable) +{ + struct vcap_admin *admin; + int err; + + err = vcap_api_check(vctrl); + if (err) + return err; + + if (!ndev) + return -ENODEV; + + if (chain_id) + admin = vcap_find_admin(vctrl, chain_id); + else + admin = vcap_find_admin_by_cookie(vctrl, cookie); + if (!admin) + return -ENOENT; + + /* first instance and first chain */ + if (admin->vinst || chain_id > admin->first_cid) + return -EFAULT; + + err = vctrl->ops->enable(ndev, admin, enable); + if (err) + return err; + + if (chain_id) { + if (vcap_is_enabled(admin, ndev, cookie)) + return -EADDRINUSE; + vcap_enable(admin, ndev, cookie); + } else { + vcap_disable(admin, ndev, cookie); + } + + return 0; +} +EXPORT_SYMBOL_GPL(vcap_enable_lookups); + #ifdef CONFIG_VCAP_KUNIT_TEST #include "vcap_api_kunit.c" #endif diff --git a/drivers/net/ethernet/microchip/vcap/vcap_api.h b/drivers/net/ethernet/microchip/vcap/vcap_api.h index eb2eae75c7e8..bfb8ad535074 100644 --- a/drivers/net/ethernet/microchip/vcap/vcap_api.h +++ b/drivers/net/ethernet/microchip/vcap/vcap_api.h @@ -166,6 +166,7 @@ enum vcap_rule_error { struct vcap_admin { struct list_head list; /* for insertion in vcap_control */ struct list_head rules; /* list of rules */ + struct list_head enabled; /* list of enabled ports */ enum vcap_type vtype; /* type of vcap */ int vinst; /* instance number within the same type */ int first_cid; /* first chain id in this vcap */ @@ -255,6 +256,11 @@ struct vcap_operations { int (*pf)(void *out, int arg, const char *fmt, ...), void *out, int arg); + /* enable/disable the lookups in a vcap instance */ + int (*enable) + (struct net_device *ndev, + struct vcap_admin *admin, + bool enable); }; /* VCAP API Client control interface */ diff --git a/drivers/net/ethernet/microchip/vcap/vcap_api_client.h b/drivers/net/ethernet/microchip/vcap/vcap_api_client.h index 077e49c4f3be..0ea5ec96adc8 100644 --- a/drivers/net/ethernet/microchip/vcap/vcap_api_client.h +++ b/drivers/net/ethernet/microchip/vcap/vcap_api_client.h @@ -143,6 +143,10 @@ enum vcap_bit { VCAP_BIT_1 }; +/* Enable/Disable the VCAP instance lookups. Chain id 0 means disable */ +int vcap_enable_lookups(struct vcap_control *vctrl, struct net_device *ndev, + int chain_id, unsigned long cookie, bool enable); + /* VCAP rule operations */ /* Allocate a rule and fill in the basic information */ struct vcap_rule *vcap_alloc_rule(struct vcap_control *vctrl, From c956b9b318d9036701c471dd458f9ed31defc629 Mon Sep 17 00:00:00 2001 From: Steen Hegelund Date: Wed, 9 Nov 2022 12:41:16 +0100 Subject: [PATCH 0832/2002] net: microchip: sparx5: Adding KUNIT tests of key/action values in VCAP API This tests that the available keyfield and actionfield add methods are doing the exepected work: adding the value (and mask) to the keyfield/actionfield list item in the rule. The test also covers the functionality that matches a rule to a keyset. Signed-off-by: Steen Hegelund Reported-by: kernel test robot Signed-off-by: David S. Miller --- .../ethernet/microchip/vcap/vcap_api_kunit.c | 592 ++++++++++++++++++ 1 file changed, 592 insertions(+) diff --git a/drivers/net/ethernet/microchip/vcap/vcap_api_kunit.c b/drivers/net/ethernet/microchip/vcap/vcap_api_kunit.c index d142ed660338..b0ec51b37683 100644 --- a/drivers/net/ethernet/microchip/vcap/vcap_api_kunit.c +++ b/drivers/net/ethernet/microchip/vcap/vcap_api_kunit.c @@ -22,6 +22,7 @@ static u32 test_init_start; static u32 test_init_count; static u32 test_hw_counter_id; static struct vcap_cache_data test_hw_cache; +static struct net_device test_netdev = {}; /* Callback used by the VCAP API */ static enum vcap_keyfield_set test_val_keyset(struct net_device *ndev, @@ -204,6 +205,13 @@ static int vcap_test_port_info(struct net_device *ndev, enum vcap_type vtype, return 0; } +static int vcap_test_enable(struct net_device *ndev, + struct vcap_admin *admin, + bool enable) +{ + return 0; +} + static struct vcap_operations test_callbacks = { .validate_keyset = test_val_keyset, .add_default_fields = test_add_def_fields, @@ -214,6 +222,7 @@ static struct vcap_operations test_callbacks = { .update = test_cache_update, .move = test_cache_move, .port_info = vcap_test_port_info, + .enable = vcap_test_enable, }; static struct vcap_control test_vctrl = { @@ -904,6 +913,586 @@ static void vcap_api_encode_rule_actionset_test(struct kunit *test) KUNIT_EXPECT_EQ(test, (u32)0x00000000, actwords[11]); } +static void vcap_api_rule_add_keyvalue_test(struct kunit *test) +{ + struct vcap_admin admin = { + .vtype = VCAP_TYPE_IS2, + }; + struct vcap_rule_internal ri = { + .admin = &admin, + .data = { + .keyset = VCAP_KFS_NO_VALUE, + }, + .vctrl = &test_vctrl, + }; + struct vcap_rule *rule = (struct vcap_rule *)&ri; + struct vcap_client_keyfield *kf; + int ret; + struct vcap_u128_key dip = { + .value = {0x17, 0x26, 0x35, 0x44, 0x63, 0x62, 0x71}, + .mask = {0xf1, 0xf2, 0xf3, 0xf4, 0x4f, 0x3f, 0x2f, 0x1f}, + }; + int idx; + + INIT_LIST_HEAD(&rule->keyfields); + ret = vcap_rule_add_key_bit(rule, VCAP_KF_LOOKUP_FIRST_IS, VCAP_BIT_0); + KUNIT_EXPECT_EQ(test, 0, ret); + ret = list_empty(&rule->keyfields); + KUNIT_EXPECT_EQ(test, false, ret); + kf = list_first_entry(&rule->keyfields, struct vcap_client_keyfield, + ctrl.list); + KUNIT_EXPECT_EQ(test, VCAP_KF_LOOKUP_FIRST_IS, kf->ctrl.key); + KUNIT_EXPECT_EQ(test, VCAP_FIELD_BIT, kf->ctrl.type); + KUNIT_EXPECT_EQ(test, 0x0, kf->data.u1.value); + KUNIT_EXPECT_EQ(test, 0x1, kf->data.u1.mask); + + INIT_LIST_HEAD(&rule->keyfields); + ret = vcap_rule_add_key_bit(rule, VCAP_KF_LOOKUP_FIRST_IS, VCAP_BIT_1); + KUNIT_EXPECT_EQ(test, 0, ret); + ret = list_empty(&rule->keyfields); + KUNIT_EXPECT_EQ(test, false, ret); + kf = list_first_entry(&rule->keyfields, struct vcap_client_keyfield, + ctrl.list); + KUNIT_EXPECT_EQ(test, VCAP_KF_LOOKUP_FIRST_IS, kf->ctrl.key); + KUNIT_EXPECT_EQ(test, VCAP_FIELD_BIT, kf->ctrl.type); + KUNIT_EXPECT_EQ(test, 0x1, kf->data.u1.value); + KUNIT_EXPECT_EQ(test, 0x1, kf->data.u1.mask); + + INIT_LIST_HEAD(&rule->keyfields); + ret = vcap_rule_add_key_bit(rule, VCAP_KF_LOOKUP_FIRST_IS, + VCAP_BIT_ANY); + KUNIT_EXPECT_EQ(test, 0, ret); + ret = list_empty(&rule->keyfields); + KUNIT_EXPECT_EQ(test, false, ret); + kf = list_first_entry(&rule->keyfields, struct vcap_client_keyfield, + ctrl.list); + KUNIT_EXPECT_EQ(test, VCAP_KF_LOOKUP_FIRST_IS, kf->ctrl.key); + KUNIT_EXPECT_EQ(test, VCAP_FIELD_BIT, kf->ctrl.type); + KUNIT_EXPECT_EQ(test, 0x0, kf->data.u1.value); + KUNIT_EXPECT_EQ(test, 0x0, kf->data.u1.mask); + + INIT_LIST_HEAD(&rule->keyfields); + ret = vcap_rule_add_key_u32(rule, VCAP_KF_TYPE, 0x98765432, 0xff00ffab); + KUNIT_EXPECT_EQ(test, 0, ret); + ret = list_empty(&rule->keyfields); + KUNIT_EXPECT_EQ(test, false, ret); + kf = list_first_entry(&rule->keyfields, struct vcap_client_keyfield, + ctrl.list); + KUNIT_EXPECT_EQ(test, VCAP_KF_TYPE, kf->ctrl.key); + KUNIT_EXPECT_EQ(test, VCAP_FIELD_U32, kf->ctrl.type); + KUNIT_EXPECT_EQ(test, 0x98765432, kf->data.u32.value); + KUNIT_EXPECT_EQ(test, 0xff00ffab, kf->data.u32.mask); + + INIT_LIST_HEAD(&rule->keyfields); + ret = vcap_rule_add_key_u128(rule, VCAP_KF_L3_IP6_SIP, &dip); + KUNIT_EXPECT_EQ(test, 0, ret); + ret = list_empty(&rule->keyfields); + KUNIT_EXPECT_EQ(test, false, ret); + kf = list_first_entry(&rule->keyfields, struct vcap_client_keyfield, + ctrl.list); + KUNIT_EXPECT_EQ(test, VCAP_KF_L3_IP6_SIP, kf->ctrl.key); + KUNIT_EXPECT_EQ(test, VCAP_FIELD_U128, kf->ctrl.type); + for (idx = 0; idx < ARRAY_SIZE(dip.value); ++idx) + KUNIT_EXPECT_EQ(test, dip.value[idx], kf->data.u128.value[idx]); + for (idx = 0; idx < ARRAY_SIZE(dip.mask); ++idx) + KUNIT_EXPECT_EQ(test, dip.mask[idx], kf->data.u128.mask[idx]); +} + +static void vcap_api_rule_add_actionvalue_test(struct kunit *test) +{ + struct vcap_admin admin = { + .vtype = VCAP_TYPE_IS2, + }; + struct vcap_rule_internal ri = { + .admin = &admin, + .data = { + .actionset = VCAP_AFS_NO_VALUE, + }, + }; + struct vcap_rule *rule = (struct vcap_rule *)&ri; + struct vcap_client_actionfield *af; + int ret; + + INIT_LIST_HEAD(&rule->actionfields); + ret = vcap_rule_add_action_bit(rule, VCAP_AF_POLICE_ENA, VCAP_BIT_0); + KUNIT_EXPECT_EQ(test, 0, ret); + ret = list_empty(&rule->actionfields); + KUNIT_EXPECT_EQ(test, false, ret); + af = list_first_entry(&rule->actionfields, + struct vcap_client_actionfield, ctrl.list); + KUNIT_EXPECT_EQ(test, VCAP_AF_POLICE_ENA, af->ctrl.action); + KUNIT_EXPECT_EQ(test, VCAP_FIELD_BIT, af->ctrl.type); + KUNIT_EXPECT_EQ(test, 0x0, af->data.u1.value); + + INIT_LIST_HEAD(&rule->actionfields); + ret = vcap_rule_add_action_bit(rule, VCAP_AF_POLICE_ENA, VCAP_BIT_1); + KUNIT_EXPECT_EQ(test, 0, ret); + ret = list_empty(&rule->actionfields); + KUNIT_EXPECT_EQ(test, false, ret); + af = list_first_entry(&rule->actionfields, + struct vcap_client_actionfield, ctrl.list); + KUNIT_EXPECT_EQ(test, VCAP_AF_POLICE_ENA, af->ctrl.action); + KUNIT_EXPECT_EQ(test, VCAP_FIELD_BIT, af->ctrl.type); + KUNIT_EXPECT_EQ(test, 0x1, af->data.u1.value); + + INIT_LIST_HEAD(&rule->actionfields); + ret = vcap_rule_add_action_bit(rule, VCAP_AF_POLICE_ENA, VCAP_BIT_ANY); + KUNIT_EXPECT_EQ(test, 0, ret); + ret = list_empty(&rule->actionfields); + KUNIT_EXPECT_EQ(test, false, ret); + af = list_first_entry(&rule->actionfields, + struct vcap_client_actionfield, ctrl.list); + KUNIT_EXPECT_EQ(test, VCAP_AF_POLICE_ENA, af->ctrl.action); + KUNIT_EXPECT_EQ(test, VCAP_FIELD_BIT, af->ctrl.type); + KUNIT_EXPECT_EQ(test, 0x0, af->data.u1.value); + + INIT_LIST_HEAD(&rule->actionfields); + ret = vcap_rule_add_action_u32(rule, VCAP_AF_TYPE, 0x98765432); + KUNIT_EXPECT_EQ(test, 0, ret); + ret = list_empty(&rule->actionfields); + KUNIT_EXPECT_EQ(test, false, ret); + af = list_first_entry(&rule->actionfields, + struct vcap_client_actionfield, ctrl.list); + KUNIT_EXPECT_EQ(test, VCAP_AF_TYPE, af->ctrl.action); + KUNIT_EXPECT_EQ(test, VCAP_FIELD_U32, af->ctrl.type); + KUNIT_EXPECT_EQ(test, 0x98765432, af->data.u32.value); + + INIT_LIST_HEAD(&rule->actionfields); + ret = vcap_rule_add_action_u32(rule, VCAP_AF_MASK_MODE, 0xaabbccdd); + KUNIT_EXPECT_EQ(test, 0, ret); + ret = list_empty(&rule->actionfields); + KUNIT_EXPECT_EQ(test, false, ret); + af = list_first_entry(&rule->actionfields, + struct vcap_client_actionfield, ctrl.list); + KUNIT_EXPECT_EQ(test, VCAP_AF_MASK_MODE, af->ctrl.action); + KUNIT_EXPECT_EQ(test, VCAP_FIELD_U32, af->ctrl.type); + KUNIT_EXPECT_EQ(test, 0xaabbccdd, af->data.u32.value); +} + +static void vcap_api_rule_find_keyset_basic_test(struct kunit *test) +{ + struct vcap_keyset_list matches = {}; + struct vcap_admin admin = { + .vtype = VCAP_TYPE_IS2, + }; + struct vcap_rule_internal ri = { + .admin = &admin, + .vctrl = &test_vctrl, + }; + struct vcap_client_keyfield ckf[] = { + { + .ctrl.key = VCAP_KF_TYPE, + }, { + .ctrl.key = VCAP_KF_LOOKUP_FIRST_IS, + }, { + .ctrl.key = VCAP_KF_IF_IGR_PORT_MASK_L3, + }, { + .ctrl.key = VCAP_KF_IF_IGR_PORT_MASK_RNG, + }, { + .ctrl.key = VCAP_KF_IF_IGR_PORT_MASK, + }, { + .ctrl.key = VCAP_KF_L2_DMAC, + }, { + .ctrl.key = VCAP_KF_ETYPE_LEN_IS, + }, { + .ctrl.key = VCAP_KF_ETYPE, + }, + }; + int idx; + bool ret; + enum vcap_keyfield_set keysets[10] = {}; + + matches.keysets = keysets; + matches.max = ARRAY_SIZE(keysets); + + INIT_LIST_HEAD(&ri.data.keyfields); + for (idx = 0; idx < ARRAY_SIZE(ckf); idx++) + list_add_tail(&ckf[idx].ctrl.list, &ri.data.keyfields); + + ret = vcap_rule_find_keysets(&ri, &matches); + + KUNIT_EXPECT_EQ(test, true, ret); + KUNIT_EXPECT_EQ(test, 1, matches.cnt); + KUNIT_EXPECT_EQ(test, VCAP_KFS_MAC_ETYPE, matches.keysets[0]); +} + +static void vcap_api_rule_find_keyset_failed_test(struct kunit *test) +{ + struct vcap_keyset_list matches = {}; + struct vcap_admin admin = { + .vtype = VCAP_TYPE_IS2, + }; + struct vcap_rule_internal ri = { + .admin = &admin, + .vctrl = &test_vctrl, + }; + struct vcap_client_keyfield ckf[] = { + { + .ctrl.key = VCAP_KF_TYPE, + }, { + .ctrl.key = VCAP_KF_LOOKUP_FIRST_IS, + }, { + .ctrl.key = VCAP_KF_ARP_OPCODE, + }, { + .ctrl.key = VCAP_KF_L3_IP4_SIP, + }, { + .ctrl.key = VCAP_KF_L3_IP4_DIP, + }, { + .ctrl.key = VCAP_KF_8021Q_PCP_CLS, + }, { + .ctrl.key = VCAP_KF_ETYPE_LEN_IS, /* Not with ARP */ + }, { + .ctrl.key = VCAP_KF_ETYPE, /* Not with ARP */ + }, + }; + int idx; + bool ret; + enum vcap_keyfield_set keysets[10] = {}; + + matches.keysets = keysets; + matches.max = ARRAY_SIZE(keysets); + + INIT_LIST_HEAD(&ri.data.keyfields); + for (idx = 0; idx < ARRAY_SIZE(ckf); idx++) + list_add_tail(&ckf[idx].ctrl.list, &ri.data.keyfields); + + ret = vcap_rule_find_keysets(&ri, &matches); + + KUNIT_EXPECT_EQ(test, false, ret); + KUNIT_EXPECT_EQ(test, 0, matches.cnt); + KUNIT_EXPECT_EQ(test, VCAP_KFS_NO_VALUE, matches.keysets[0]); +} + +static void vcap_api_rule_find_keyset_many_test(struct kunit *test) +{ + struct vcap_keyset_list matches = {}; + struct vcap_admin admin = { + .vtype = VCAP_TYPE_IS2, + }; + struct vcap_rule_internal ri = { + .admin = &admin, + .vctrl = &test_vctrl, + }; + struct vcap_client_keyfield ckf[] = { + { + .ctrl.key = VCAP_KF_TYPE, + }, { + .ctrl.key = VCAP_KF_LOOKUP_FIRST_IS, + }, { + .ctrl.key = VCAP_KF_8021Q_DEI_CLS, + }, { + .ctrl.key = VCAP_KF_8021Q_PCP_CLS, + }, { + .ctrl.key = VCAP_KF_8021Q_VID_CLS, + }, { + .ctrl.key = VCAP_KF_ISDX_CLS, + }, { + .ctrl.key = VCAP_KF_L2_MC_IS, + }, { + .ctrl.key = VCAP_KF_L2_BC_IS, + }, + }; + int idx; + bool ret; + enum vcap_keyfield_set keysets[10] = {}; + + matches.keysets = keysets; + matches.max = ARRAY_SIZE(keysets); + + INIT_LIST_HEAD(&ri.data.keyfields); + for (idx = 0; idx < ARRAY_SIZE(ckf); idx++) + list_add_tail(&ckf[idx].ctrl.list, &ri.data.keyfields); + + ret = vcap_rule_find_keysets(&ri, &matches); + + KUNIT_EXPECT_EQ(test, true, ret); + KUNIT_EXPECT_EQ(test, 6, matches.cnt); + KUNIT_EXPECT_EQ(test, VCAP_KFS_ARP, matches.keysets[0]); + KUNIT_EXPECT_EQ(test, VCAP_KFS_IP4_OTHER, matches.keysets[1]); + KUNIT_EXPECT_EQ(test, VCAP_KFS_IP4_TCP_UDP, matches.keysets[2]); + KUNIT_EXPECT_EQ(test, VCAP_KFS_IP6_STD, matches.keysets[3]); + KUNIT_EXPECT_EQ(test, VCAP_KFS_IP_7TUPLE, matches.keysets[4]); + KUNIT_EXPECT_EQ(test, VCAP_KFS_MAC_ETYPE, matches.keysets[5]); +} + +static void vcap_api_encode_rule_test(struct kunit *test) +{ + /* Data used by VCAP Library callback */ + static u32 keydata[32] = {}; + static u32 mskdata[32] = {}; + static u32 actdata[32] = {}; + + struct vcap_admin is2_admin = { + .vtype = VCAP_TYPE_IS2, + .first_cid = 10000, + .last_cid = 19999, + .lookups = 4, + .last_valid_addr = 3071, + .first_valid_addr = 0, + .last_used_addr = 800, + .cache = { + .keystream = keydata, + .maskstream = mskdata, + .actionstream = actdata, + }, + }; + struct vcap_rule *rule = 0; + struct vcap_rule_internal *ri = 0; + int vcap_chain_id = 10005; + enum vcap_user user = VCAP_USER_VCAP_UTIL; + u16 priority = 10; + int id = 100; + int ret; + struct vcap_u48_key smac = { + .value = { 0x88, 0x75, 0x32, 0x34, 0x9e, 0xb1 }, + .mask = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff } + }; + struct vcap_u48_key dmac = { + .value = { 0x06, 0x05, 0x04, 0x03, 0x02, 0x01 }, + .mask = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff } + }; + u32 port_mask_rng_value = 0x05; + u32 port_mask_rng_mask = 0x0f; + u32 igr_port_mask_value = 0xffabcd01; + u32 igr_port_mask_mask = ~0; + /* counter is not written yet, so it is not in expwriteaddr */ + u32 expwriteaddr[] = {792, 793, 794, 795, 796, 797, 0}; + int idx; + + vcap_test_api_init(&is2_admin); + + /* Allocate the rule */ + rule = vcap_alloc_rule(&test_vctrl, &test_netdev, vcap_chain_id, user, + priority, id); + KUNIT_EXPECT_PTR_NE(test, NULL, rule); + ri = (struct vcap_rule_internal *)rule; + + /* Add rule keys */ + ret = vcap_rule_add_key_u48(rule, VCAP_KF_L2_DMAC, &dmac); + KUNIT_EXPECT_EQ(test, 0, ret); + ret = vcap_rule_add_key_u48(rule, VCAP_KF_L2_SMAC, &smac); + KUNIT_EXPECT_EQ(test, 0, ret); + ret = vcap_rule_add_key_bit(rule, VCAP_KF_ETYPE_LEN_IS, VCAP_BIT_1); + KUNIT_EXPECT_EQ(test, 0, ret); + /* Cannot add the same field twice */ + ret = vcap_rule_add_key_bit(rule, VCAP_KF_ETYPE_LEN_IS, VCAP_BIT_1); + KUNIT_EXPECT_EQ(test, -EINVAL, ret); + ret = vcap_rule_add_key_bit(rule, VCAP_KF_IF_IGR_PORT_MASK_L3, + VCAP_BIT_ANY); + KUNIT_EXPECT_EQ(test, 0, ret); + ret = vcap_rule_add_key_u32(rule, VCAP_KF_IF_IGR_PORT_MASK_RNG, + port_mask_rng_value, port_mask_rng_mask); + KUNIT_EXPECT_EQ(test, 0, ret); + ret = vcap_rule_add_key_u32(rule, VCAP_KF_IF_IGR_PORT_MASK, + igr_port_mask_value, igr_port_mask_mask); + KUNIT_EXPECT_EQ(test, 0, ret); + + /* Add rule actions */ + ret = vcap_rule_add_action_bit(rule, VCAP_AF_POLICE_ENA, VCAP_BIT_1); + KUNIT_EXPECT_EQ(test, 0, ret); + ret = vcap_rule_add_action_u32(rule, VCAP_AF_CNT_ID, id); + KUNIT_EXPECT_EQ(test, 0, ret); + ret = vcap_rule_add_action_u32(rule, VCAP_AF_MATCH_ID, 1); + KUNIT_EXPECT_EQ(test, 0, ret); + ret = vcap_rule_add_action_u32(rule, VCAP_AF_MATCH_ID_MASK, 1); + KUNIT_EXPECT_EQ(test, 0, ret); + + /* For now the actionset is hardcoded */ + ret = vcap_set_rule_set_actionset(rule, VCAP_AFS_BASE_TYPE); + KUNIT_EXPECT_EQ(test, 0, ret); + + /* Validation with validate keyset callback */ + ret = vcap_val_rule(rule, ETH_P_ALL); + KUNIT_EXPECT_EQ(test, 0, ret); + KUNIT_EXPECT_EQ(test, VCAP_KFS_MAC_ETYPE, rule->keyset); + KUNIT_EXPECT_EQ(test, VCAP_AFS_BASE_TYPE, rule->actionset); + KUNIT_EXPECT_EQ(test, 6, ri->size); + KUNIT_EXPECT_EQ(test, 2, ri->keyset_sw_regs); + KUNIT_EXPECT_EQ(test, 4, ri->actionset_sw_regs); + + /* Add rule with write callback */ + ret = vcap_add_rule(rule); + KUNIT_EXPECT_EQ(test, 0, ret); + KUNIT_EXPECT_EQ(test, 792, is2_admin.last_used_addr); + for (idx = 0; idx < ARRAY_SIZE(expwriteaddr); ++idx) + KUNIT_EXPECT_EQ(test, expwriteaddr[idx], test_updateaddr[idx]); + + /* Check that the rule has been added */ + ret = list_empty(&is2_admin.rules); + KUNIT_EXPECT_EQ(test, false, ret); + KUNIT_EXPECT_EQ(test, 0, ret); + vcap_free_rule(rule); + + /* Check that the rule has been freed: tricky to access since this + * memory should not be accessible anymore + */ + KUNIT_EXPECT_PTR_NE(test, NULL, rule); + ret = list_empty(&rule->keyfields); + KUNIT_EXPECT_EQ(test, true, ret); + ret = list_empty(&rule->actionfields); + KUNIT_EXPECT_EQ(test, true, ret); +} + +static void vcap_api_next_lookup_basic_test(struct kunit *test) +{ + struct vcap_admin admin1 = { + .vtype = VCAP_TYPE_IS2, + .vinst = 0, + .first_cid = 8000000, + .last_cid = 8199999, + .lookups = 4, + .lookups_per_instance = 2, + }; + struct vcap_admin admin2 = { + .vtype = VCAP_TYPE_IS2, + .vinst = 1, + .first_cid = 8200000, + .last_cid = 8399999, + .lookups = 4, + .lookups_per_instance = 2, + }; + bool ret; + + vcap_test_api_init(&admin1); + list_add_tail(&admin2.list, &test_vctrl.list); + + ret = vcap_is_next_lookup(&test_vctrl, 8000000, 1001000); + KUNIT_EXPECT_EQ(test, false, ret); + ret = vcap_is_next_lookup(&test_vctrl, 8000000, 8001000); + KUNIT_EXPECT_EQ(test, false, ret); + ret = vcap_is_next_lookup(&test_vctrl, 8000000, 8101000); + KUNIT_EXPECT_EQ(test, true, ret); + + ret = vcap_is_next_lookup(&test_vctrl, 8100000, 8101000); + KUNIT_EXPECT_EQ(test, false, ret); + ret = vcap_is_next_lookup(&test_vctrl, 8100000, 8201000); + KUNIT_EXPECT_EQ(test, true, ret); + + ret = vcap_is_next_lookup(&test_vctrl, 8200000, 8201000); + KUNIT_EXPECT_EQ(test, false, ret); + ret = vcap_is_next_lookup(&test_vctrl, 8200000, 8301000); + KUNIT_EXPECT_EQ(test, true, ret); + + ret = vcap_is_next_lookup(&test_vctrl, 8300000, 8301000); + KUNIT_EXPECT_EQ(test, false, ret); + ret = vcap_is_next_lookup(&test_vctrl, 8300000, 8401000); + KUNIT_EXPECT_EQ(test, true, ret); +} + +static void vcap_api_next_lookup_advanced_test(struct kunit *test) +{ + struct vcap_admin admin1 = { + .vtype = VCAP_TYPE_IS0, + .vinst = 0, + .first_cid = 1000000, + .last_cid = 1199999, + .lookups = 6, + .lookups_per_instance = 2, + }; + struct vcap_admin admin2 = { + .vtype = VCAP_TYPE_IS0, + .vinst = 1, + .first_cid = 1200000, + .last_cid = 1399999, + .lookups = 6, + .lookups_per_instance = 2, + }; + struct vcap_admin admin3 = { + .vtype = VCAP_TYPE_IS0, + .vinst = 2, + .first_cid = 1400000, + .last_cid = 1599999, + .lookups = 6, + .lookups_per_instance = 2, + }; + struct vcap_admin admin4 = { + .vtype = VCAP_TYPE_IS2, + .vinst = 0, + .first_cid = 8000000, + .last_cid = 8199999, + .lookups = 4, + .lookups_per_instance = 2, + }; + struct vcap_admin admin5 = { + .vtype = VCAP_TYPE_IS2, + .vinst = 1, + .first_cid = 8200000, + .last_cid = 8399999, + .lookups = 4, + .lookups_per_instance = 2, + }; + bool ret; + + vcap_test_api_init(&admin1); + list_add_tail(&admin2.list, &test_vctrl.list); + list_add_tail(&admin3.list, &test_vctrl.list); + list_add_tail(&admin4.list, &test_vctrl.list); + list_add_tail(&admin5.list, &test_vctrl.list); + + ret = vcap_is_next_lookup(&test_vctrl, 1000000, 1001000); + KUNIT_EXPECT_EQ(test, false, ret); + ret = vcap_is_next_lookup(&test_vctrl, 1000000, 1101000); + KUNIT_EXPECT_EQ(test, true, ret); + + ret = vcap_is_next_lookup(&test_vctrl, 1100000, 1201000); + KUNIT_EXPECT_EQ(test, true, ret); + ret = vcap_is_next_lookup(&test_vctrl, 1100000, 1301000); + KUNIT_EXPECT_EQ(test, false, ret); + ret = vcap_is_next_lookup(&test_vctrl, 1100000, 8101000); + KUNIT_EXPECT_EQ(test, false, ret); + ret = vcap_is_next_lookup(&test_vctrl, 1300000, 1401000); + KUNIT_EXPECT_EQ(test, true, ret); + ret = vcap_is_next_lookup(&test_vctrl, 1400000, 1501000); + KUNIT_EXPECT_EQ(test, true, ret); + ret = vcap_is_next_lookup(&test_vctrl, 1500000, 8001000); + KUNIT_EXPECT_EQ(test, true, ret); + + ret = vcap_is_next_lookup(&test_vctrl, 8000000, 8001000); + KUNIT_EXPECT_EQ(test, false, ret); + ret = vcap_is_next_lookup(&test_vctrl, 8000000, 8101000); + KUNIT_EXPECT_EQ(test, true, ret); + + ret = vcap_is_next_lookup(&test_vctrl, 8300000, 8301000); + KUNIT_EXPECT_EQ(test, false, ret); + ret = vcap_is_next_lookup(&test_vctrl, 8300000, 8401000); + KUNIT_EXPECT_EQ(test, true, ret); +} + +static struct kunit_case vcap_api_support_test_cases[] = { + KUNIT_CASE(vcap_api_next_lookup_basic_test), + KUNIT_CASE(vcap_api_next_lookup_advanced_test), + {} +}; + +static struct kunit_suite vcap_api_support_test_suite = { + .name = "VCAP_API_Support_Testsuite", + .test_cases = vcap_api_support_test_cases, +}; + +static struct kunit_case vcap_api_full_rule_test_cases[] = { + KUNIT_CASE(vcap_api_rule_find_keyset_basic_test), + KUNIT_CASE(vcap_api_rule_find_keyset_failed_test), + KUNIT_CASE(vcap_api_rule_find_keyset_many_test), + KUNIT_CASE(vcap_api_encode_rule_test), + {} +}; + +static struct kunit_suite vcap_api_full_rule_test_suite = { + .name = "VCAP_API_Full_Rule_Testsuite", + .test_cases = vcap_api_full_rule_test_cases, +}; + +static struct kunit_case vcap_api_rule_value_test_cases[] = { + KUNIT_CASE(vcap_api_rule_add_keyvalue_test), + KUNIT_CASE(vcap_api_rule_add_actionvalue_test), + {} +}; + +static struct kunit_suite vcap_api_rule_value_test_suite = { + .name = "VCAP_API_Rule_Value_Testsuite", + .test_cases = vcap_api_rule_value_test_cases, +}; + static struct kunit_case vcap_api_encoding_test_cases[] = { KUNIT_CASE(vcap_api_set_bit_1_test), KUNIT_CASE(vcap_api_set_bit_0_test), @@ -930,4 +1519,7 @@ static struct kunit_suite vcap_api_encoding_test_suite = { .test_cases = vcap_api_encoding_test_cases, }; +kunit_test_suite(vcap_api_support_test_suite); +kunit_test_suite(vcap_api_full_rule_test_suite); +kunit_test_suite(vcap_api_rule_value_test_suite); kunit_test_suite(vcap_api_encoding_test_suite); From 42ba9654acad33efe34e5125fb373889f46a3f1c Mon Sep 17 00:00:00 2001 From: Yinjun Zhang Date: Wed, 9 Nov 2022 15:04:42 -0500 Subject: [PATCH 0833/2002] nfp: take numa node into account when setting irq affinity MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Set irq affinity to cpus that belong to the same numa node with NIC device first. Signed-off-by: Yinjun Zhang Reviewed-by: Louis Peens Reviewed-by: Niklas Söderlund Signed-off-by: Simon Horman Signed-off-by: David S. Miller --- drivers/net/ethernet/netronome/nfp/nfp_net_common.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c index 8c1a870bc0e5..184ffae2ac94 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c @@ -735,8 +735,9 @@ static unsigned int nfp_net_calc_fl_bufsz_xsk(struct nfp_net_dp *dp) */ static void nfp_net_vecs_init(struct nfp_net *nn) { + int numa_node = dev_to_node(&nn->pdev->dev); struct nfp_net_r_vector *r_vec; - int r; + unsigned int r; nn->lsc_handler = nfp_net_irq_lsc; nn->exn_handler = nfp_net_irq_exn; @@ -762,7 +763,7 @@ static void nfp_net_vecs_init(struct nfp_net *nn) tasklet_disable(&r_vec->tasklet); } - cpumask_set_cpu(r, &r_vec->affinity_mask); + cpumask_set_cpu(cpumask_local_spread(r, numa_node), &r_vec->affinity_mask); } } From e83163b66a37501cc11ba1c53e018f8236e87a2c Mon Sep 17 00:00:00 2001 From: Horatiu Vultur Date: Wed, 9 Nov 2022 21:46:10 +0100 Subject: [PATCH 0834/2002] net: lan966x: Add define IFH_LEN_BYTES The total length of IFH(inter frame header) in bytes is calculated as IFH_LEN * sizeof(u32). Because IFH_LEN describes the length in words and not in bytes. As the length of IFH in bytes is used quite often, add a define for this. This is just to simplify the things. Signed-off-by: Horatiu Vultur Signed-off-by: David S. Miller --- drivers/net/ethernet/microchip/lan966x/lan966x_fdma.c | 10 +++++----- drivers/net/ethernet/microchip/lan966x/lan966x_ifh.h | 1 + drivers/net/ethernet/microchip/lan966x/lan966x_main.c | 2 +- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_fdma.c b/drivers/net/ethernet/microchip/lan966x/lan966x_fdma.c index e6948939ccc2..6c102ee20f1d 100644 --- a/drivers/net/ethernet/microchip/lan966x/lan966x_fdma.c +++ b/drivers/net/ethernet/microchip/lan966x/lan966x_fdma.c @@ -436,7 +436,7 @@ static struct sk_buff *lan966x_fdma_rx_get_frame(struct lan966x_rx *rx) DMA_ATTR_SKIP_CPU_SYNC); skb->dev = lan966x->ports[src_port]->dev; - skb_pull(skb, IFH_LEN * sizeof(u32)); + skb_pull(skb, IFH_LEN_BYTES); if (likely(!(skb->dev->features & NETIF_F_RXFCS))) skb_trim(skb, skb->len - ETH_FCS_LEN); @@ -592,7 +592,7 @@ int lan966x_fdma_xmit(struct sk_buff *skb, __be32 *ifh, struct net_device *dev) } /* skb processing */ - needed_headroom = max_t(int, IFH_LEN * sizeof(u32) - skb_headroom(skb), 0); + needed_headroom = max_t(int, IFH_LEN_BYTES - skb_headroom(skb), 0); needed_tailroom = max_t(int, ETH_FCS_LEN - skb_tailroom(skb), 0); if (needed_headroom || needed_tailroom || skb_header_cloned(skb)) { err = pskb_expand_head(skb, needed_headroom, needed_tailroom, @@ -605,8 +605,8 @@ int lan966x_fdma_xmit(struct sk_buff *skb, __be32 *ifh, struct net_device *dev) } skb_tx_timestamp(skb); - skb_push(skb, IFH_LEN * sizeof(u32)); - memcpy(skb->data, ifh, IFH_LEN * sizeof(u32)); + skb_push(skb, IFH_LEN_BYTES); + memcpy(skb->data, ifh, IFH_LEN_BYTES); skb_put(skb, 4); dma_addr = dma_map_single(lan966x->dev, skb->data, skb->len, @@ -740,7 +740,7 @@ int lan966x_fdma_change_mtu(struct lan966x *lan966x) u32 val; max_mtu = lan966x_fdma_get_max_mtu(lan966x); - max_mtu += IFH_LEN * sizeof(u32); + max_mtu += IFH_LEN_BYTES; max_mtu += SKB_DATA_ALIGN(sizeof(struct skb_shared_info)); max_mtu += VLAN_HLEN * 2; diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_ifh.h b/drivers/net/ethernet/microchip/lan966x/lan966x_ifh.h index ca3314789d18..f3b1e0d31826 100644 --- a/drivers/net/ethernet/microchip/lan966x/lan966x_ifh.h +++ b/drivers/net/ethernet/microchip/lan966x/lan966x_ifh.h @@ -8,6 +8,7 @@ */ #define IFH_LEN 7 +#define IFH_LEN_BYTES (IFH_LEN * sizeof(u32)) /* Timestamp for frame */ #define IFH_POS_TIMESTAMP 192 diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_main.c b/drivers/net/ethernet/microchip/lan966x/lan966x_main.c index 20ee5b28f70a..1a27946ccaf4 100644 --- a/drivers/net/ethernet/microchip/lan966x/lan966x_main.c +++ b/drivers/net/ethernet/microchip/lan966x/lan966x_main.c @@ -760,7 +760,7 @@ static int lan966x_probe_port(struct lan966x *lan966x, u32 p, NETIF_F_HW_VLAN_STAG_TX | NETIF_F_HW_TC; dev->hw_features |= NETIF_F_HW_TC; - dev->needed_headroom = IFH_LEN * sizeof(u32); + dev->needed_headroom = IFH_LEN_BYTES; eth_hw_addr_gen(dev, lan966x->base_mac, p + 1); From 4a00b0c712e3248e28bcc3026b5b957fe7514607 Mon Sep 17 00:00:00 2001 From: Horatiu Vultur Date: Wed, 9 Nov 2022 21:46:11 +0100 Subject: [PATCH 0835/2002] net: lan966x: Split function lan966x_fdma_rx_get_frame The function lan966x_fdma_rx_get_frame was unmapping the frame from device and check also if the frame was received on a valid port. And only after that it tried to generate the skb. Move this check in a different function, in preparation for xdp support. Such that xdp to be added here and the lan966x_fdma_rx_get_frame to be used only when giving the skb to upper layers. Signed-off-by: Horatiu Vultur Signed-off-by: David S. Miller --- .../ethernet/microchip/lan966x/lan966x_fdma.c | 93 +++++++++++++------ .../ethernet/microchip/lan966x/lan966x_main.h | 9 ++ 2 files changed, 73 insertions(+), 29 deletions(-) diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_fdma.c b/drivers/net/ethernet/microchip/lan966x/lan966x_fdma.c index 6c102ee20f1d..d37765ddd53a 100644 --- a/drivers/net/ethernet/microchip/lan966x/lan966x_fdma.c +++ b/drivers/net/ethernet/microchip/lan966x/lan966x_fdma.c @@ -54,6 +54,17 @@ static void lan966x_fdma_rx_free_pages(struct lan966x_rx *rx) } } +static void lan966x_fdma_rx_free_page(struct lan966x_rx *rx) +{ + struct page *page; + + page = rx->page[rx->dcb_index][rx->db_index]; + if (unlikely(!page)) + return; + + __free_pages(page, rx->page_order); +} + static void lan966x_fdma_rx_add_dcb(struct lan966x_rx *rx, struct lan966x_rx_dcb *dcb, u64 nextptr) @@ -116,6 +127,12 @@ static int lan966x_fdma_rx_alloc(struct lan966x_rx *rx) return 0; } +static void lan966x_fdma_rx_advance_dcb(struct lan966x_rx *rx) +{ + rx->dcb_index++; + rx->dcb_index &= FDMA_DCB_MAX - 1; +} + static void lan966x_fdma_rx_free(struct lan966x_rx *rx) { struct lan966x *lan966x = rx->lan966x; @@ -403,38 +420,53 @@ static bool lan966x_fdma_rx_more_frames(struct lan966x_rx *rx) return true; } -static struct sk_buff *lan966x_fdma_rx_get_frame(struct lan966x_rx *rx) +static int lan966x_fdma_rx_check_frame(struct lan966x_rx *rx, u64 *src_port) { struct lan966x *lan966x = rx->lan966x; - u64 src_port, timestamp; struct lan966x_db *db; - struct sk_buff *skb; struct page *page; - /* Get the received frame and unmap it */ db = &rx->dcbs[rx->dcb_index].db[rx->db_index]; page = rx->page[rx->dcb_index][rx->db_index]; + if (unlikely(!page)) + return FDMA_ERROR; dma_sync_single_for_cpu(lan966x->dev, (dma_addr_t)db->dataptr, FDMA_DCB_STATUS_BLOCKL(db->status), DMA_FROM_DEVICE); - skb = build_skb(page_address(page), PAGE_SIZE << rx->page_order); - if (unlikely(!skb)) - goto unmap_page; - - skb_put(skb, FDMA_DCB_STATUS_BLOCKL(db->status)); - - lan966x_ifh_get_src_port(skb->data, &src_port); - lan966x_ifh_get_timestamp(skb->data, ×tamp); - - if (WARN_ON(src_port >= lan966x->num_phys_ports)) - goto free_skb; - dma_unmap_single_attrs(lan966x->dev, (dma_addr_t)db->dataptr, PAGE_SIZE << rx->page_order, DMA_FROM_DEVICE, DMA_ATTR_SKIP_CPU_SYNC); + lan966x_ifh_get_src_port(page_address(page), src_port); + if (WARN_ON(*src_port >= lan966x->num_phys_ports)) + return FDMA_ERROR; + + return FDMA_PASS; +} + +static struct sk_buff *lan966x_fdma_rx_get_frame(struct lan966x_rx *rx, + u64 src_port) +{ + struct lan966x *lan966x = rx->lan966x; + struct lan966x_db *db; + struct sk_buff *skb; + struct page *page; + u64 timestamp; + + /* Get the received frame and unmap it */ + db = &rx->dcbs[rx->dcb_index].db[rx->db_index]; + page = rx->page[rx->dcb_index][rx->db_index]; + + skb = build_skb(page_address(page), PAGE_SIZE << rx->page_order); + if (unlikely(!skb)) + goto free_page; + + skb_put(skb, FDMA_DCB_STATUS_BLOCKL(db->status)); + + lan966x_ifh_get_timestamp(skb->data, ×tamp); + skb->dev = lan966x->ports[src_port]->dev; skb_pull(skb, IFH_LEN_BYTES); @@ -457,12 +489,7 @@ static struct sk_buff *lan966x_fdma_rx_get_frame(struct lan966x_rx *rx) return skb; -free_skb: - kfree_skb(skb); -unmap_page: - dma_unmap_single_attrs(lan966x->dev, (dma_addr_t)db->dataptr, - PAGE_SIZE << rx->page_order, DMA_FROM_DEVICE, - DMA_ATTR_SKIP_CPU_SYNC); +free_page: __free_pages(page, rx->page_order); return NULL; @@ -478,6 +505,7 @@ static int lan966x_fdma_napi_poll(struct napi_struct *napi, int weight) struct sk_buff *skb; struct page *page; int counter = 0; + u64 src_port; u64 nextptr; lan966x_fdma_tx_clear_buf(lan966x, weight); @@ -487,19 +515,26 @@ static int lan966x_fdma_napi_poll(struct napi_struct *napi, int weight) if (!lan966x_fdma_rx_more_frames(rx)) break; - skb = lan966x_fdma_rx_get_frame(rx); + counter++; - rx->page[rx->dcb_index][rx->db_index] = NULL; - rx->dcb_index++; - rx->dcb_index &= FDMA_DCB_MAX - 1; - - if (!skb) + switch (lan966x_fdma_rx_check_frame(rx, &src_port)) { + case FDMA_PASS: break; + case FDMA_ERROR: + lan966x_fdma_rx_free_page(rx); + lan966x_fdma_rx_advance_dcb(rx); + goto allocate_new; + } + + skb = lan966x_fdma_rx_get_frame(rx, src_port); + lan966x_fdma_rx_advance_dcb(rx); + if (!skb) + goto allocate_new; napi_gro_receive(&lan966x->napi, skb); - counter++; } +allocate_new: /* Allocate new pages and map them */ while (dcb_reload != rx->dcb_index) { db = &rx->dcbs[dcb_reload].db[rx->db_index]; diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_main.h b/drivers/net/ethernet/microchip/lan966x/lan966x_main.h index 4ec33999e4df..464fb5e4a8ff 100644 --- a/drivers/net/ethernet/microchip/lan966x/lan966x_main.h +++ b/drivers/net/ethernet/microchip/lan966x/lan966x_main.h @@ -100,6 +100,15 @@ enum macaccess_entry_type { ENTRYTYPE_MACV6, }; +/* FDMA return action codes for checking if the frame is valid + * FDMA_PASS, frame is valid and can be used + * FDMA_ERROR, something went wrong, stop getting more frames + */ +enum lan966x_fdma_action { + FDMA_PASS = 0, + FDMA_ERROR, +}; + struct lan966x_port; struct lan966x_db { From 6a2159be7604f5cdd7f574f4e0922f61e63c3f16 Mon Sep 17 00:00:00 2001 From: Horatiu Vultur Date: Wed, 9 Nov 2022 21:46:12 +0100 Subject: [PATCH 0836/2002] net: lan966x: Add basic XDP support Introduce basic XDP support to lan966x driver. Currently the driver supports only the actions XDP_PASS, XDP_DROP and XDP_ABORTED. Signed-off-by: Horatiu Vultur Signed-off-by: David S. Miller --- .../net/ethernet/microchip/lan966x/Makefile | 3 +- .../ethernet/microchip/lan966x/lan966x_fdma.c | 11 ++- .../ethernet/microchip/lan966x/lan966x_main.c | 5 ++ .../ethernet/microchip/lan966x/lan966x_main.h | 16 ++++ .../ethernet/microchip/lan966x/lan966x_xdp.c | 76 +++++++++++++++++++ 5 files changed, 109 insertions(+), 2 deletions(-) create mode 100644 drivers/net/ethernet/microchip/lan966x/lan966x_xdp.c diff --git a/drivers/net/ethernet/microchip/lan966x/Makefile b/drivers/net/ethernet/microchip/lan966x/Makefile index 962f7c5f9e7d..251a7d561d63 100644 --- a/drivers/net/ethernet/microchip/lan966x/Makefile +++ b/drivers/net/ethernet/microchip/lan966x/Makefile @@ -11,4 +11,5 @@ lan966x-switch-objs := lan966x_main.o lan966x_phylink.o lan966x_port.o \ lan966x_ptp.o lan966x_fdma.o lan966x_lag.o \ lan966x_tc.o lan966x_mqprio.o lan966x_taprio.o \ lan966x_tbf.o lan966x_cbs.o lan966x_ets.o \ - lan966x_tc_matchall.o lan966x_police.o lan966x_mirror.o + lan966x_tc_matchall.o lan966x_police.o lan966x_mirror.o \ + lan966x_xdp.o diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_fdma.c b/drivers/net/ethernet/microchip/lan966x/lan966x_fdma.c index d37765ddd53a..fa4198c61766 100644 --- a/drivers/net/ethernet/microchip/lan966x/lan966x_fdma.c +++ b/drivers/net/ethernet/microchip/lan966x/lan966x_fdma.c @@ -423,6 +423,7 @@ static bool lan966x_fdma_rx_more_frames(struct lan966x_rx *rx) static int lan966x_fdma_rx_check_frame(struct lan966x_rx *rx, u64 *src_port) { struct lan966x *lan966x = rx->lan966x; + struct lan966x_port *port; struct lan966x_db *db; struct page *page; @@ -443,7 +444,11 @@ static int lan966x_fdma_rx_check_frame(struct lan966x_rx *rx, u64 *src_port) if (WARN_ON(*src_port >= lan966x->num_phys_ports)) return FDMA_ERROR; - return FDMA_PASS; + port = lan966x->ports[*src_port]; + if (!lan966x_xdp_port_present(port)) + return FDMA_PASS; + + return lan966x_xdp_run(port, page, FDMA_DCB_STATUS_BLOCKL(db->status)); } static struct sk_buff *lan966x_fdma_rx_get_frame(struct lan966x_rx *rx, @@ -524,6 +529,10 @@ static int lan966x_fdma_napi_poll(struct napi_struct *napi, int weight) lan966x_fdma_rx_free_page(rx); lan966x_fdma_rx_advance_dcb(rx); goto allocate_new; + case FDMA_DROP: + lan966x_fdma_rx_free_page(rx); + lan966x_fdma_rx_advance_dcb(rx); + continue; } skb = lan966x_fdma_rx_get_frame(rx, src_port); diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_main.c b/drivers/net/ethernet/microchip/lan966x/lan966x_main.c index 1a27946ccaf4..42be5d0f1f01 100644 --- a/drivers/net/ethernet/microchip/lan966x/lan966x_main.c +++ b/drivers/net/ethernet/microchip/lan966x/lan966x_main.c @@ -468,6 +468,7 @@ static const struct net_device_ops lan966x_port_netdev_ops = { .ndo_get_port_parent_id = lan966x_port_get_parent_id, .ndo_eth_ioctl = lan966x_port_ioctl, .ndo_setup_tc = lan966x_tc_setup, + .ndo_bpf = lan966x_xdp, }; bool lan966x_netdevice_check(const struct net_device *dev) @@ -694,6 +695,7 @@ static void lan966x_cleanup_ports(struct lan966x *lan966x) if (port->dev) unregister_netdev(port->dev); + lan966x_xdp_port_deinit(port); if (lan966x->fdma && lan966x->fdma_ndev == port->dev) lan966x_fdma_netdev_deinit(lan966x, port->dev); @@ -1136,6 +1138,9 @@ static int lan966x_probe(struct platform_device *pdev) lan966x->ports[p]->serdes = serdes; lan966x_port_init(lan966x->ports[p]); + err = lan966x_xdp_port_init(lan966x->ports[p]); + if (err) + goto cleanup_ports; } lan966x_mdb_init(lan966x); diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_main.h b/drivers/net/ethernet/microchip/lan966x/lan966x_main.h index 464fb5e4a8ff..b3b0619b17c6 100644 --- a/drivers/net/ethernet/microchip/lan966x/lan966x_main.h +++ b/drivers/net/ethernet/microchip/lan966x/lan966x_main.h @@ -103,10 +103,12 @@ enum macaccess_entry_type { /* FDMA return action codes for checking if the frame is valid * FDMA_PASS, frame is valid and can be used * FDMA_ERROR, something went wrong, stop getting more frames + * FDMA_DROP, frame is dropped, but continue to get more frames */ enum lan966x_fdma_action { FDMA_PASS = 0, FDMA_ERROR, + FDMA_DROP, }; struct lan966x_port; @@ -329,6 +331,9 @@ struct lan966x_port { enum netdev_lag_hash hash_type; struct lan966x_port_tc tc; + + struct bpf_prog *xdp_prog; + struct xdp_rxq_info xdp_rxq; }; extern const struct phylink_mac_ops lan966x_phylink_mac_ops; @@ -536,6 +541,17 @@ void lan966x_mirror_port_stats(struct lan966x_port *port, struct flow_stats *stats, bool ingress); +int lan966x_xdp_port_init(struct lan966x_port *port); +void lan966x_xdp_port_deinit(struct lan966x_port *port); +int lan966x_xdp(struct net_device *dev, struct netdev_bpf *xdp); +int lan966x_xdp_run(struct lan966x_port *port, + struct page *page, + u32 data_len); +static inline bool lan966x_xdp_port_present(struct lan966x_port *port) +{ + return !!port->xdp_prog; +} + static inline void __iomem *lan_addr(void __iomem *base[], int id, int tinst, int tcnt, int gbase, int ginst, diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_xdp.c b/drivers/net/ethernet/microchip/lan966x/lan966x_xdp.c new file mode 100644 index 000000000000..e77d9f2aad2b --- /dev/null +++ b/drivers/net/ethernet/microchip/lan966x/lan966x_xdp.c @@ -0,0 +1,76 @@ +// SPDX-License-Identifier: GPL-2.0+ + +#include +#include +#include + +#include "lan966x_main.h" + +static int lan966x_xdp_setup(struct net_device *dev, struct netdev_bpf *xdp) +{ + struct lan966x_port *port = netdev_priv(dev); + struct lan966x *lan966x = port->lan966x; + struct bpf_prog *old_prog; + + if (!lan966x->fdma) { + NL_SET_ERR_MSG_MOD(xdp->extack, + "Allow to set xdp only when using fdma"); + return -EOPNOTSUPP; + } + + old_prog = xchg(&port->xdp_prog, xdp->prog); + if (old_prog) + bpf_prog_put(old_prog); + + return 0; +} + +int lan966x_xdp(struct net_device *dev, struct netdev_bpf *xdp) +{ + switch (xdp->command) { + case XDP_SETUP_PROG: + return lan966x_xdp_setup(dev, xdp); + default: + return -EINVAL; + } +} + +int lan966x_xdp_run(struct lan966x_port *port, struct page *page, u32 data_len) +{ + struct bpf_prog *xdp_prog = port->xdp_prog; + struct lan966x *lan966x = port->lan966x; + struct xdp_buff xdp; + u32 act; + + xdp_init_buff(&xdp, PAGE_SIZE << lan966x->rx.page_order, + &port->xdp_rxq); + xdp_prepare_buff(&xdp, page_address(page), IFH_LEN_BYTES, + data_len - IFH_LEN_BYTES, false); + act = bpf_prog_run_xdp(xdp_prog, &xdp); + switch (act) { + case XDP_PASS: + return FDMA_PASS; + default: + bpf_warn_invalid_xdp_action(port->dev, xdp_prog, act); + fallthrough; + case XDP_ABORTED: + trace_xdp_exception(port->dev, xdp_prog, act); + fallthrough; + case XDP_DROP: + return FDMA_DROP; + } +} + +int lan966x_xdp_port_init(struct lan966x_port *port) +{ + struct lan966x *lan966x = port->lan966x; + + return xdp_rxq_info_reg(&port->xdp_rxq, port->dev, 0, + lan966x->napi.napi_id); +} + +void lan966x_xdp_port_deinit(struct lan966x_port *port) +{ + if (xdp_rxq_info_is_reg(&port->xdp_rxq)) + xdp_rxq_info_unreg(&port->xdp_rxq); +} From 11871aba19748b3387e83a2db6360aa7119e9a1a Mon Sep 17 00:00:00 2001 From: Horatiu Vultur Date: Wed, 9 Nov 2022 21:46:13 +0100 Subject: [PATCH 0837/2002] net: lan96x: Use page_pool API Use the page_pool API for allocation, freeing and DMA handling instead of dev_alloc_pages, __free_pages and dma_map_page. Signed-off-by: Horatiu Vultur Signed-off-by: David S. Miller --- .../net/ethernet/microchip/lan966x/Kconfig | 1 + .../ethernet/microchip/lan966x/lan966x_fdma.c | 89 ++++++++++--------- .../ethernet/microchip/lan966x/lan966x_main.h | 8 ++ 3 files changed, 58 insertions(+), 40 deletions(-) diff --git a/drivers/net/ethernet/microchip/lan966x/Kconfig b/drivers/net/ethernet/microchip/lan966x/Kconfig index 49e1464a4313..b7ae5ce7d3f7 100644 --- a/drivers/net/ethernet/microchip/lan966x/Kconfig +++ b/drivers/net/ethernet/microchip/lan966x/Kconfig @@ -7,5 +7,6 @@ config LAN966X_SWITCH depends on BRIDGE || BRIDGE=n select PHYLINK select PACKING + select PAGE_POOL help This driver supports the Lan966x network switch device. diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_fdma.c b/drivers/net/ethernet/microchip/lan966x/lan966x_fdma.c index fa4198c61766..5fbbd479cfb0 100644 --- a/drivers/net/ethernet/microchip/lan966x/lan966x_fdma.c +++ b/drivers/net/ethernet/microchip/lan966x/lan966x_fdma.c @@ -10,47 +10,25 @@ static int lan966x_fdma_channel_active(struct lan966x *lan966x) static struct page *lan966x_fdma_rx_alloc_page(struct lan966x_rx *rx, struct lan966x_db *db) { - struct lan966x *lan966x = rx->lan966x; - dma_addr_t dma_addr; struct page *page; - page = dev_alloc_pages(rx->page_order); + page = page_pool_dev_alloc_pages(rx->page_pool); if (unlikely(!page)) return NULL; - dma_addr = dma_map_page(lan966x->dev, page, 0, - PAGE_SIZE << rx->page_order, - DMA_FROM_DEVICE); - if (unlikely(dma_mapping_error(lan966x->dev, dma_addr))) - goto free_page; - - db->dataptr = dma_addr; + db->dataptr = page_pool_get_dma_addr(page); return page; - -free_page: - __free_pages(page, rx->page_order); - return NULL; } static void lan966x_fdma_rx_free_pages(struct lan966x_rx *rx) { - struct lan966x *lan966x = rx->lan966x; - struct lan966x_rx_dcb *dcb; - struct lan966x_db *db; int i, j; for (i = 0; i < FDMA_DCB_MAX; ++i) { - dcb = &rx->dcbs[i]; - - for (j = 0; j < FDMA_RX_DCB_MAX_DBS; ++j) { - db = &dcb->db[j]; - dma_unmap_single(lan966x->dev, - (dma_addr_t)db->dataptr, - PAGE_SIZE << rx->page_order, - DMA_FROM_DEVICE); - __free_pages(rx->page[i][j], rx->page_order); - } + for (j = 0; j < FDMA_RX_DCB_MAX_DBS; ++j) + page_pool_put_full_page(rx->page_pool, + rx->page[i][j], false); } } @@ -62,7 +40,7 @@ static void lan966x_fdma_rx_free_page(struct lan966x_rx *rx) if (unlikely(!page)) return; - __free_pages(page, rx->page_order); + page_pool_recycle_direct(rx->page_pool, page); } static void lan966x_fdma_rx_add_dcb(struct lan966x_rx *rx, @@ -84,6 +62,25 @@ static void lan966x_fdma_rx_add_dcb(struct lan966x_rx *rx, rx->last_entry = dcb; } +static int lan966x_fdma_rx_alloc_page_pool(struct lan966x_rx *rx) +{ + struct lan966x *lan966x = rx->lan966x; + struct page_pool_params pp_params = { + .order = rx->page_order, + .flags = PP_FLAG_DMA_MAP | PP_FLAG_DMA_SYNC_DEV, + .pool_size = FDMA_DCB_MAX, + .nid = NUMA_NO_NODE, + .dev = lan966x->dev, + .dma_dir = DMA_FROM_DEVICE, + .offset = 0, + .max_len = rx->max_mtu - + SKB_DATA_ALIGN(sizeof(struct skb_shared_info)), + }; + + rx->page_pool = page_pool_create(&pp_params); + return PTR_ERR_OR_ZERO(rx->page_pool); +} + static int lan966x_fdma_rx_alloc(struct lan966x_rx *rx) { struct lan966x *lan966x = rx->lan966x; @@ -93,6 +90,9 @@ static int lan966x_fdma_rx_alloc(struct lan966x_rx *rx) int i, j; int size; + if (lan966x_fdma_rx_alloc_page_pool(rx)) + return PTR_ERR(rx->page_pool); + /* calculate how many pages are needed to allocate the dcbs */ size = sizeof(struct lan966x_rx_dcb) * FDMA_DCB_MAX; size = ALIGN(size, PAGE_SIZE); @@ -436,10 +436,6 @@ static int lan966x_fdma_rx_check_frame(struct lan966x_rx *rx, u64 *src_port) FDMA_DCB_STATUS_BLOCKL(db->status), DMA_FROM_DEVICE); - dma_unmap_single_attrs(lan966x->dev, (dma_addr_t)db->dataptr, - PAGE_SIZE << rx->page_order, DMA_FROM_DEVICE, - DMA_ATTR_SKIP_CPU_SYNC); - lan966x_ifh_get_src_port(page_address(page), src_port); if (WARN_ON(*src_port >= lan966x->num_phys_ports)) return FDMA_ERROR; @@ -468,6 +464,8 @@ static struct sk_buff *lan966x_fdma_rx_get_frame(struct lan966x_rx *rx, if (unlikely(!skb)) goto free_page; + skb_mark_for_recycle(skb); + skb_put(skb, FDMA_DCB_STATUS_BLOCKL(db->status)); lan966x_ifh_get_timestamp(skb->data, ×tamp); @@ -495,7 +493,7 @@ static struct sk_buff *lan966x_fdma_rx_get_frame(struct lan966x_rx *rx, return skb; free_page: - __free_pages(page, rx->page_order); + page_pool_recycle_direct(rx->page_pool, page); return NULL; } @@ -740,6 +738,7 @@ static int lan966x_qsys_sw_status(struct lan966x *lan966x) static int lan966x_fdma_reload(struct lan966x *lan966x, int new_mtu) { + struct page_pool *page_pool; dma_addr_t rx_dma; void *rx_dcbs; u32 size; @@ -748,6 +747,7 @@ static int lan966x_fdma_reload(struct lan966x *lan966x, int new_mtu) /* Store these for later to free them */ rx_dma = lan966x->rx.dma; rx_dcbs = lan966x->rx.dcbs; + page_pool = lan966x->rx.page_pool; napi_synchronize(&lan966x->napi); napi_disable(&lan966x->napi); @@ -756,6 +756,7 @@ static int lan966x_fdma_reload(struct lan966x *lan966x, int new_mtu) lan966x_fdma_rx_disable(&lan966x->rx); lan966x_fdma_rx_free_pages(&lan966x->rx); lan966x->rx.page_order = round_up(new_mtu, PAGE_SIZE) / PAGE_SIZE - 1; + lan966x->rx.max_mtu = new_mtu; err = lan966x_fdma_rx_alloc(&lan966x->rx); if (err) goto restore; @@ -765,11 +766,14 @@ static int lan966x_fdma_reload(struct lan966x *lan966x, int new_mtu) size = ALIGN(size, PAGE_SIZE); dma_free_coherent(lan966x->dev, size, rx_dcbs, rx_dma); + page_pool_destroy(page_pool); + lan966x_fdma_wakeup_netdev(lan966x); napi_enable(&lan966x->napi); return err; restore: + lan966x->rx.page_pool = page_pool; lan966x->rx.dma = rx_dma; lan966x->rx.dcbs = rx_dcbs; lan966x_fdma_rx_start(&lan966x->rx); @@ -777,19 +781,22 @@ restore: return err; } +static int lan966x_fdma_get_max_frame(struct lan966x *lan966x) +{ + return lan966x_fdma_get_max_mtu(lan966x) + + IFH_LEN_BYTES + + SKB_DATA_ALIGN(sizeof(struct skb_shared_info)) + + VLAN_HLEN * 2; +} + int lan966x_fdma_change_mtu(struct lan966x *lan966x) { int max_mtu; int err; u32 val; - max_mtu = lan966x_fdma_get_max_mtu(lan966x); - max_mtu += IFH_LEN_BYTES; - max_mtu += SKB_DATA_ALIGN(sizeof(struct skb_shared_info)); - max_mtu += VLAN_HLEN * 2; - - if (round_up(max_mtu, PAGE_SIZE) / PAGE_SIZE - 1 == - lan966x->rx.page_order) + max_mtu = lan966x_fdma_get_max_frame(lan966x); + if (max_mtu == lan966x->rx.max_mtu) return 0; /* Disable the CPU port */ @@ -844,6 +851,7 @@ int lan966x_fdma_init(struct lan966x *lan966x) lan966x->rx.lan966x = lan966x; lan966x->rx.channel_id = FDMA_XTR_CHANNEL; + lan966x->rx.max_mtu = lan966x_fdma_get_max_frame(lan966x); lan966x->tx.lan966x = lan966x; lan966x->tx.channel_id = FDMA_INJ_CHANNEL; lan966x->tx.last_in_use = -1; @@ -876,5 +884,6 @@ void lan966x_fdma_deinit(struct lan966x *lan966x) lan966x_fdma_rx_free_pages(&lan966x->rx); lan966x_fdma_rx_free(&lan966x->rx); + page_pool_destroy(lan966x->rx.page_pool); lan966x_fdma_tx_free(&lan966x->tx); } diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_main.h b/drivers/net/ethernet/microchip/lan966x/lan966x_main.h index b3b0619b17c6..bc93051aa079 100644 --- a/drivers/net/ethernet/microchip/lan966x/lan966x_main.h +++ b/drivers/net/ethernet/microchip/lan966x/lan966x_main.h @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -161,7 +162,14 @@ struct lan966x_rx { */ u8 page_order; + /* Represents the max size frame that it can receive to the CPU. This + * includes the IFH + VLAN tags + frame + skb_shared_info + */ + u32 max_mtu; + u8 channel_id; + + struct page_pool *page_pool; }; struct lan966x_tx_dcb_buf { From c334ac6461d516c6d79dd10fd84cd69a00422966 Mon Sep 17 00:00:00 2001 From: Oleksandr Mazur Date: Thu, 10 Nov 2022 00:25:20 +0200 Subject: [PATCH 0838/2002] net: marvell: prestera: pci: use device-id defines Use defines with proper device names instead of device-id in pci-devices listing. Signed-off-by: Oleksandr Mazur Signed-off-by: David S. Miller --- drivers/net/ethernet/marvell/prestera/prestera_pci.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/marvell/prestera/prestera_pci.c b/drivers/net/ethernet/marvell/prestera/prestera_pci.c index 59470d99f522..14639966e53e 100644 --- a/drivers/net/ethernet/marvell/prestera/prestera_pci.c +++ b/drivers/net/ethernet/marvell/prestera/prestera_pci.c @@ -184,6 +184,10 @@ struct prestera_fw_regs { #define PRESTERA_FW_CMD_DEFAULT_WAIT_MS 30000 #define PRESTERA_FW_READY_WAIT_MS 20000 +#define PRESTERA_DEV_ID_AC3X_98DX_55 0xC804 +#define PRESTERA_DEV_ID_AC3X_98DX_65 0xC80C +#define PRESTERA_DEV_ID_ALDRIN2 0xCC1E + struct prestera_fw_evtq { u8 __iomem *addr; size_t len; @@ -870,9 +874,9 @@ static void prestera_pci_remove(struct pci_dev *pdev) } static const struct pci_device_id prestera_pci_devices[] = { - { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0xC804) }, - { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0xC80C) }, - { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0xCC1E) }, + { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, PRESTERA_DEV_ID_AC3X_98DX_55) }, + { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, PRESTERA_DEV_ID_AC3X_98DX_65) }, + { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, PRESTERA_DEV_ID_ALDRIN2) }, { } }; MODULE_DEVICE_TABLE(pci, prestera_pci_devices); From 075c881be29bd8647af2281c9db798f405476b55 Mon Sep 17 00:00:00 2001 From: Maksym Glubokiy Date: Thu, 10 Nov 2022 00:25:21 +0200 Subject: [PATCH 0839/2002] net: marvell: prestera: pci: add support for AC5X family devices Add support for the following AC5x Marvell Prestera PP family devices: 98DX7312M (12x25G / 8x25G + 1x100G); 98DX3500 (24x1G + 6x25G); 98DX3501 (16x1G + 6x10G); 98DX3510 (48x1G + 6x25G); 98DX3520 (24x2.5G + 6x25G); Known issues: - FW reload doesn't work (rmmod/modprobe sequence). Co-developed-by: Vadym Kochan Signed-off-by: Vadym Kochan Signed-off-by: Maksym Glubokiy Signed-off-by: Oleksandr Mazur Signed-off-by: David S. Miller --- .../ethernet/marvell/prestera/prestera_pci.c | 107 ++++++++++++++++-- 1 file changed, 97 insertions(+), 10 deletions(-) diff --git a/drivers/net/ethernet/marvell/prestera/prestera_pci.c b/drivers/net/ethernet/marvell/prestera/prestera_pci.c index 14639966e53e..9475499069e6 100644 --- a/drivers/net/ethernet/marvell/prestera/prestera_pci.c +++ b/drivers/net/ethernet/marvell/prestera/prestera_pci.c @@ -21,6 +21,7 @@ #define PRESTERA_PREV_FW_MIN_VER 0 #define PRESTERA_FW_PATH_FMT "mrvl/prestera/mvsw_prestera_fw-v%u.%u.img" +#define PRESTERA_FW_ARM64_PATH_FMT "mrvl/prestera/mvsw_prestera_fw_arm64-v%u.%u.img" #define PRESTERA_FW_HDR_MAGIC 0x351D9D06 #define PRESTERA_FW_DL_TIMEOUT_MS 50000 @@ -187,6 +188,11 @@ struct prestera_fw_regs { #define PRESTERA_DEV_ID_AC3X_98DX_55 0xC804 #define PRESTERA_DEV_ID_AC3X_98DX_65 0xC80C #define PRESTERA_DEV_ID_ALDRIN2 0xCC1E +#define PRESTERA_DEV_ID_98DX7312M 0x981F +#define PRESTERA_DEV_ID_98DX3500 0x9820 +#define PRESTERA_DEV_ID_98DX3501 0x9826 +#define PRESTERA_DEV_ID_98DX3510 0x9821 +#define PRESTERA_DEV_ID_98DX3520 0x9822 struct prestera_fw_evtq { u8 __iomem *addr; @@ -205,6 +211,7 @@ struct prestera_fw { const struct firmware *bin; struct workqueue_struct *wq; struct prestera_device dev; + struct pci_dev *pci_dev; u8 __iomem *ldr_regs; u8 __iomem *ldr_ring_buf; u32 ldr_buf_len; @@ -693,6 +700,20 @@ static int prestera_fw_hdr_parse(struct prestera_fw *fw) return prestera_fw_rev_check(fw); } +static const char *prestera_fw_path_fmt_get(struct prestera_fw *fw) +{ + switch (fw->pci_dev->device) { + case PRESTERA_DEV_ID_98DX3500: + case PRESTERA_DEV_ID_98DX3501: + case PRESTERA_DEV_ID_98DX3510: + case PRESTERA_DEV_ID_98DX3520: + return PRESTERA_FW_ARM64_PATH_FMT; + + default: + return PRESTERA_FW_PATH_FMT; + } +} + static int prestera_fw_get(struct prestera_fw *fw) { int ver_maj = PRESTERA_SUPP_FW_MAJ_VER; @@ -701,7 +722,7 @@ static int prestera_fw_get(struct prestera_fw *fw) int err; pick_fw_ver: - snprintf(fw_path, sizeof(fw_path), PRESTERA_FW_PATH_FMT, + snprintf(fw_path, sizeof(fw_path), prestera_fw_path_fmt_get(fw), ver_maj, ver_min); err = request_firmware_direct(&fw->bin, fw_path, fw->dev.dev); @@ -778,22 +799,56 @@ out_release: return err; } +static bool prestera_pci_pp_use_bar2(struct pci_dev *pdev) +{ + switch (pdev->device) { + case PRESTERA_DEV_ID_98DX7312M: + case PRESTERA_DEV_ID_98DX3500: + case PRESTERA_DEV_ID_98DX3501: + case PRESTERA_DEV_ID_98DX3510: + case PRESTERA_DEV_ID_98DX3520: + return true; + + default: + return false; + } +} + +static u32 prestera_pci_pp_bar2_offs(struct pci_dev *pdev) +{ + if (pci_resource_len(pdev, 2) == 0x1000000) + return 0x0; + else + return (pci_resource_len(pdev, 2) / 2); +} + +static u32 prestera_pci_fw_bar2_offs(struct pci_dev *pdev) +{ + if (pci_resource_len(pdev, 2) == 0x1000000) + return 0x400000; + else + return 0x0; +} + static int prestera_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) { const char *driver_name = dev_driver_string(&pdev->dev); + u8 __iomem *mem_addr, *pp_addr = NULL; struct prestera_fw *fw; int err; err = pcim_enable_device(pdev); - if (err) - return err; + if (err) { + dev_err(&pdev->dev, "pci_enable_device failed\n"); + goto err_pci_enable_device; + } - err = pcim_iomap_regions(pdev, BIT(PRESTERA_PCI_BAR_FW) | - BIT(PRESTERA_PCI_BAR_PP), - pci_name(pdev)); - if (err) - return err; + err = pci_request_regions(pdev, driver_name); + if (err) { + dev_err(&pdev->dev, "pci_request_regions failed\n"); + goto err_pci_request_regions; + } err = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(30)); if (err) { @@ -801,6 +856,26 @@ static int prestera_pci_probe(struct pci_dev *pdev, goto err_dma_mask; } + mem_addr = pcim_iomap(pdev, 2, 0); + if (!mem_addr) { + dev_err(&pdev->dev, "pci mem ioremap failed\n"); + err = -EIO; + goto err_mem_ioremap; + } + + /* AC5X devices use second half of BAR2 */ + if (prestera_pci_pp_use_bar2(pdev)) { + pp_addr = mem_addr + prestera_pci_pp_bar2_offs(pdev); + mem_addr = mem_addr + prestera_pci_fw_bar2_offs(pdev); + } else { + pp_addr = pcim_iomap(pdev, 4, 0); + if (!pp_addr) { + dev_err(&pdev->dev, "pp regs ioremap failed\n"); + err = -EIO; + goto err_pp_ioremap; + } + } + pci_set_master(pdev); fw = devm_kzalloc(&pdev->dev, sizeof(*fw), GFP_KERNEL); @@ -809,8 +884,9 @@ static int prestera_pci_probe(struct pci_dev *pdev, goto err_pci_dev_alloc; } - fw->dev.ctl_regs = pcim_iomap_table(pdev)[PRESTERA_PCI_BAR_FW]; - fw->dev.pp_regs = pcim_iomap_table(pdev)[PRESTERA_PCI_BAR_PP]; + fw->pci_dev = pdev; + fw->dev.ctl_regs = mem_addr; + fw->dev.pp_regs = pp_addr; fw->dev.dev = &pdev->dev; pci_set_drvdata(pdev, fw); @@ -858,7 +934,12 @@ err_wq_alloc: prestera_fw_uninit(fw); err_prestera_fw_init: err_pci_dev_alloc: +err_pp_ioremap: +err_mem_ioremap: err_dma_mask: + pci_release_regions(pdev); +err_pci_request_regions: +err_pci_enable_device: return err; } @@ -871,12 +952,18 @@ static void prestera_pci_remove(struct pci_dev *pdev) pci_free_irq_vectors(pdev); destroy_workqueue(fw->wq); prestera_fw_uninit(fw); + pci_release_regions(pdev); } static const struct pci_device_id prestera_pci_devices[] = { { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, PRESTERA_DEV_ID_AC3X_98DX_55) }, { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, PRESTERA_DEV_ID_AC3X_98DX_65) }, { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, PRESTERA_DEV_ID_ALDRIN2) }, + { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, PRESTERA_DEV_ID_98DX7312M) }, + { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, PRESTERA_DEV_ID_98DX3500) }, + { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, PRESTERA_DEV_ID_98DX3501) }, + { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, PRESTERA_DEV_ID_98DX3510) }, + { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, PRESTERA_DEV_ID_98DX3520) }, { } }; MODULE_DEVICE_TABLE(pci, prestera_pci_devices); From a35608ed8cfd41583d7a9b2da1155b91511f391a Mon Sep 17 00:00:00 2001 From: Oleksandr Mazur Date: Thu, 10 Nov 2022 00:25:22 +0200 Subject: [PATCH 0840/2002] net: marvell: prestera: pci: bump supported FW min version Bump MIN version to reflect support of new platform (AC5X family devices). Signed-off-by: Oleksandr Mazur Signed-off-by: David S. Miller --- drivers/net/ethernet/marvell/prestera/prestera_pci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/marvell/prestera/prestera_pci.c b/drivers/net/ethernet/marvell/prestera/prestera_pci.c index 9475499069e6..f328d957b2db 100644 --- a/drivers/net/ethernet/marvell/prestera/prestera_pci.c +++ b/drivers/net/ethernet/marvell/prestera/prestera_pci.c @@ -15,7 +15,7 @@ #define PRESTERA_MSG_MAX_SIZE 1500 #define PRESTERA_SUPP_FW_MAJ_VER 4 -#define PRESTERA_SUPP_FW_MIN_VER 0 +#define PRESTERA_SUPP_FW_MIN_VER 1 #define PRESTERA_PREV_FW_MAJ_VER 4 #define PRESTERA_PREV_FW_MIN_VER 0 From 2e77eded8ec34768618f9ab724b9092e09ddcca2 Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Wed, 9 Nov 2022 15:09:37 -0800 Subject: [PATCH 0841/2002] ptp_phc: convert .adjfreq to .adjfine The ptp_phc implementation of .adjfreq is implemented in terms of a straight forward "base * ppb / 1 billion" calculation. Convert this to the newer .adjfine, updating the driver to use the recently introduced adjust_by_scaled_ppm helper function. Signed-off-by: Jacob Keller Cc: Richard Cochran Signed-off-by: David S. Miller --- drivers/ptp/ptp_pch.c | 19 ++++--------------- 1 file changed, 4 insertions(+), 15 deletions(-) diff --git a/drivers/ptp/ptp_pch.c b/drivers/ptp/ptp_pch.c index 7d4da9e605ef..33355d5eb033 100644 --- a/drivers/ptp/ptp_pch.c +++ b/drivers/ptp/ptp_pch.c @@ -336,24 +336,13 @@ static irqreturn_t isr(int irq, void *priv) * PTP clock operations */ -static int ptp_pch_adjfreq(struct ptp_clock_info *ptp, s32 ppb) +static int ptp_pch_adjfine(struct ptp_clock_info *ptp, long scaled_ppm) { - u64 adj; - u32 diff, addend; - int neg_adj = 0; + u32 addend; struct pch_dev *pch_dev = container_of(ptp, struct pch_dev, caps); struct pch_ts_regs __iomem *regs = pch_dev->regs; - if (ppb < 0) { - neg_adj = 1; - ppb = -ppb; - } - addend = DEFAULT_ADDEND; - adj = addend; - adj *= ppb; - diff = div_u64(adj, 1000000000ULL); - - addend = neg_adj ? addend - diff : addend + diff; + addend = adjust_by_scaled_ppm(DEFAULT_ADDEND, scaled_ppm); iowrite32(addend, ®s->addend); @@ -440,7 +429,7 @@ static const struct ptp_clock_info ptp_pch_caps = { .n_ext_ts = N_EXT_TS, .n_pins = 0, .pps = 0, - .adjfreq = ptp_pch_adjfreq, + .adjfine = ptp_pch_adjfine, .adjtime = ptp_pch_adjtime, .gettime64 = ptp_pch_gettime, .settime64 = ptp_pch_settime, From 319d77e9d059d4e7b0aba923578faed038d9038c Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Wed, 9 Nov 2022 15:09:38 -0800 Subject: [PATCH 0842/2002] ptp_ixp46x: convert .adjfreq to .adjfine The ptp_ixp46x implementation of .adjfreq is implemented in terms of a straight forward "base * ppb / 1 billion" calculation. Convert this to the newer .adjfine, using the recently added adjust_by_scaled_ppm helper function. Signed-off-by: Jacob Keller Cc: Paolo Abeni Cc: Richard Cochran Cc: Linus Walleij Reviewed-by: Linus Walleij Signed-off-by: David S. Miller --- drivers/net/ethernet/xscale/ptp_ixp46x.c | 19 ++++--------------- 1 file changed, 4 insertions(+), 15 deletions(-) diff --git a/drivers/net/ethernet/xscale/ptp_ixp46x.c b/drivers/net/ethernet/xscale/ptp_ixp46x.c index 9abbdb71e629..94203eb46e6b 100644 --- a/drivers/net/ethernet/xscale/ptp_ixp46x.c +++ b/drivers/net/ethernet/xscale/ptp_ixp46x.c @@ -120,24 +120,13 @@ static irqreturn_t isr(int irq, void *priv) * PTP clock operations */ -static int ptp_ixp_adjfreq(struct ptp_clock_info *ptp, s32 ppb) +static int ptp_ixp_adjfine(struct ptp_clock_info *ptp, long scaled_ppm) { - u64 adj; - u32 diff, addend; - int neg_adj = 0; + u32 addend; struct ixp_clock *ixp_clock = container_of(ptp, struct ixp_clock, caps); struct ixp46x_ts_regs *regs = ixp_clock->regs; - if (ppb < 0) { - neg_adj = 1; - ppb = -ppb; - } - addend = DEFAULT_ADDEND; - adj = addend; - adj *= ppb; - diff = div_u64(adj, 1000000000ULL); - - addend = neg_adj ? addend - diff : addend + diff; + addend = adjust_by_scaled_ppm(DEFAULT_ADDEND, scaled_ppm); __raw_writel(addend, ®s->addend); @@ -230,7 +219,7 @@ static const struct ptp_clock_info ptp_ixp_caps = { .n_ext_ts = N_EXT_TS, .n_pins = 0, .pps = 0, - .adjfreq = ptp_ixp_adjfreq, + .adjfine = ptp_ixp_adjfine, .adjtime = ptp_ixp_adjtime, .gettime64 = ptp_ixp_gettime, .settime64 = ptp_ixp_settime, From e3f18e9d353aa8bd9e4db10acab21df380e212f5 Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Wed, 9 Nov 2022 15:09:39 -0800 Subject: [PATCH 0843/2002] ptp: tg3: convert .adjfreq to .adjfine The tg3 implementation of .adjfreq is implemented in terms of a straight forward "base * ppb / 1 billion" calculation. Convert this to the newer .adjfine, using the recently added diff_by_scaled_ppm helper function to calculate the difference and direction of the adjustment. Signed-off-by: Jacob Keller Cc: Siva Reddy Kallam Cc: Prashant Sreedharan Cc: Michael Chan Cc: Richard Cochran Reviewed-by: Pavan Chebbi Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/tg3.c | 22 +++++++--------------- 1 file changed, 7 insertions(+), 15 deletions(-) diff --git a/drivers/net/ethernet/broadcom/tg3.c b/drivers/net/ethernet/broadcom/tg3.c index 4179a12fc881..59debdc344a5 100644 --- a/drivers/net/ethernet/broadcom/tg3.c +++ b/drivers/net/ethernet/broadcom/tg3.c @@ -6179,34 +6179,26 @@ static int tg3_get_ts_info(struct net_device *dev, struct ethtool_ts_info *info) return 0; } -static int tg3_ptp_adjfreq(struct ptp_clock_info *ptp, s32 ppb) +static int tg3_ptp_adjfine(struct ptp_clock_info *ptp, long scaled_ppm) { struct tg3 *tp = container_of(ptp, struct tg3, ptp_info); - bool neg_adj = false; - u32 correction = 0; - - if (ppb < 0) { - neg_adj = true; - ppb = -ppb; - } + u64 correction; + bool neg_adj; /* Frequency adjustment is performed using hardware with a 24 bit * accumulator and a programmable correction value. On each clk, the * correction value gets added to the accumulator and when it * overflows, the time counter is incremented/decremented. - * - * So conversion from ppb to correction value is - * ppb * (1 << 24) / 1000000000 */ - correction = div_u64((u64)ppb * (1 << 24), 1000000000ULL) & - TG3_EAV_REF_CLK_CORRECT_MASK; + neg_adj = diff_by_scaled_ppm(1 << 24, scaled_ppm, &correction); tg3_full_lock(tp, 0); if (correction) tw32(TG3_EAV_REF_CLK_CORRECT_CTL, TG3_EAV_REF_CLK_CORRECT_EN | - (neg_adj ? TG3_EAV_REF_CLK_CORRECT_NEG : 0) | correction); + (neg_adj ? TG3_EAV_REF_CLK_CORRECT_NEG : 0) | + ((u32)correction & TG3_EAV_REF_CLK_CORRECT_MASK)); else tw32(TG3_EAV_REF_CLK_CORRECT_CTL, 0); @@ -6330,7 +6322,7 @@ static const struct ptp_clock_info tg3_ptp_caps = { .n_per_out = 1, .n_pins = 0, .pps = 0, - .adjfreq = tg3_ptp_adjfreq, + .adjfine = tg3_ptp_adjfine, .adjtime = tg3_ptp_adjtime, .gettimex64 = tg3_ptp_gettimex, .settime64 = tg3_ptp_settime, From 974557020a5917f73fb7d7218402ef744b109c41 Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Wed, 9 Nov 2022 15:09:40 -0800 Subject: [PATCH 0844/2002] ptp: hclge: convert .adjfreq to .adjfine The hclge implementation of .adjfreq is implemented in terms of a straight forward "base * ppb / 1 billion" calculation. Convert this to the newer .adjfine, using the recently added adjust_by_scaled_ppm helper function. Signed-off-by: Jacob Keller Cc: Yisen Zhuang Cc: Salil Mehta Cc: Richard Cochran Signed-off-by: David S. Miller --- .../hisilicon/hns3/hns3pf/hclge_ptp.c | 22 +++++-------------- 1 file changed, 5 insertions(+), 17 deletions(-) diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_ptp.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_ptp.c index a40b1583f114..80a2a0073d97 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_ptp.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_ptp.c @@ -22,28 +22,16 @@ static int hclge_ptp_get_cycle(struct hclge_dev *hdev) return 0; } -static int hclge_ptp_adjfreq(struct ptp_clock_info *ptp, s32 ppb) +static int hclge_ptp_adjfine(struct ptp_clock_info *ptp, long scaled_ppm) { struct hclge_dev *hdev = hclge_ptp_get_hdev(ptp); struct hclge_ptp_cycle *cycle = &hdev->ptp->cycle; - u64 adj_val, adj_base, diff; + u64 adj_val, adj_base; unsigned long flags; - bool is_neg = false; u32 quo, numerator; - if (ppb < 0) { - ppb = -ppb; - is_neg = true; - } - adj_base = (u64)cycle->quo * (u64)cycle->den + (u64)cycle->numer; - adj_val = adj_base * ppb; - diff = div_u64(adj_val, 1000000000ULL); - - if (is_neg) - adj_val = adj_base - diff; - else - adj_val = adj_base + diff; + adj_val = adjust_by_scaled_ppm(adj_base, scaled_ppm); /* This clock cycle is defined by three part: quotient, numerator * and denominator. For example, 2.5ns, the quotient is 2, @@ -446,7 +434,7 @@ static int hclge_ptp_create_clock(struct hclge_dev *hdev) ptp->info.max_adj = HCLGE_PTP_CYCLE_ADJ_MAX; ptp->info.n_ext_ts = 0; ptp->info.pps = 0; - ptp->info.adjfreq = hclge_ptp_adjfreq; + ptp->info.adjfine = hclge_ptp_adjfine; ptp->info.adjtime = hclge_ptp_adjtime; ptp->info.gettimex64 = hclge_ptp_gettimex; ptp->info.settime64 = hclge_ptp_settime; @@ -504,7 +492,7 @@ int hclge_ptp_init(struct hclge_dev *hdev) goto out; set_bit(HCLGE_PTP_FLAG_EN, &hdev->ptp->flags); - ret = hclge_ptp_adjfreq(&hdev->ptp->info, 0); + ret = hclge_ptp_adjfine(&hdev->ptp->info, 0); if (ret) { dev_err(&hdev->pdev->dev, "failed to init freq, ret = %d\n", ret); From 2d96099f50f0ee7e38789c767c93e712737520b5 Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Wed, 9 Nov 2022 15:09:41 -0800 Subject: [PATCH 0845/2002] ptp: stmac: convert .adjfreq to .adjfine The stmac implementation of .adjfreq is implemented in terms of a straight forward "base * ppb / 1 billion" calculation. Convert this to the newer .adjfine, using the recently added adjust_by_scaled_ppm helper function. Signed-off-by: Jacob Keller Cc: Giuseppe Cavallaro Cc: Alexandre Torgue Cc: Jose Abreu Cc: Richard Cochran Signed-off-by: David S. Miller --- .../net/ethernet/stmicro/stmmac/stmmac_ptp.c | 23 ++++++------------- 1 file changed, 7 insertions(+), 16 deletions(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_ptp.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_ptp.c index 4d11980dcd64..fc06ddeac0d5 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_ptp.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_ptp.c @@ -15,29 +15,20 @@ * stmmac_adjust_freq * * @ptp: pointer to ptp_clock_info structure - * @ppb: desired period change in parts ber billion + * @scaled_ppm: desired period change in scaled parts per million * * Description: this function will adjust the frequency of hardware clock. + * + * Scaled parts per million is ppm with a 16-bit binary fractional field. */ -static int stmmac_adjust_freq(struct ptp_clock_info *ptp, s32 ppb) +static int stmmac_adjust_freq(struct ptp_clock_info *ptp, long scaled_ppm) { struct stmmac_priv *priv = container_of(ptp, struct stmmac_priv, ptp_clock_ops); unsigned long flags; - u32 diff, addend; - int neg_adj = 0; - u64 adj; + u32 addend; - if (ppb < 0) { - neg_adj = 1; - ppb = -ppb; - } - - addend = priv->default_addend; - adj = addend; - adj *= ppb; - diff = div_u64(adj, 1000000000ULL); - addend = neg_adj ? (addend - diff) : (addend + diff); + addend = adjust_by_scaled_ppm(priv->default_addend, scaled_ppm); write_lock_irqsave(&priv->ptp_lock, flags); stmmac_config_addend(priv, priv->ptpaddr, addend); @@ -269,7 +260,7 @@ static struct ptp_clock_info stmmac_ptp_clock_ops = { .n_per_out = 0, /* will be overwritten in stmmac_ptp_register */ .n_pins = 0, .pps = 0, - .adjfreq = stmmac_adjust_freq, + .adjfine = stmmac_adjust_freq, .adjtime = stmmac_adjust_time, .gettime64 = stmmac_get_time, .settime64 = stmmac_set_time, From a45392071ceeebe85622705a1d7500ed434785a7 Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Wed, 9 Nov 2022 15:09:42 -0800 Subject: [PATCH 0846/2002] ptp: cpts: convert .adjfreq to .adjfine The cpts implementation of .adjfreq is implemented in terms of a straight forward "base * ppb / 1 billion" calculation. Convert this to the newer .adjfine, using the recently added adjust_by_scaled_ppm helper function. Signed-off-by: Jacob Keller Richard Cochran Signed-off-by: David S. Miller --- drivers/net/ethernet/ti/cpts.c | 20 ++++---------------- 1 file changed, 4 insertions(+), 16 deletions(-) diff --git a/drivers/net/ethernet/ti/cpts.c b/drivers/net/ethernet/ti/cpts.c index 92ca739fac01..bcccf43d368b 100644 --- a/drivers/net/ethernet/ti/cpts.c +++ b/drivers/net/ethernet/ti/cpts.c @@ -213,25 +213,13 @@ static void cpts_update_cur_time(struct cpts *cpts, int match, /* PTP clock operations */ -static int cpts_ptp_adjfreq(struct ptp_clock_info *ptp, s32 ppb) +static int cpts_ptp_adjfine(struct ptp_clock_info *ptp, long scaled_ppm) { struct cpts *cpts = container_of(ptp, struct cpts, info); - int neg_adj = 0; - u32 diff, mult; - u64 adj; - - if (ppb < 0) { - neg_adj = 1; - ppb = -ppb; - } - mult = cpts->cc_mult; - adj = mult; - adj *= ppb; - diff = div_u64(adj, 1000000000ULL); mutex_lock(&cpts->ptp_clk_mutex); - cpts->mult_new = neg_adj ? mult - diff : mult + diff; + cpts->mult_new = adjust_by_scaled_ppm(cpts->cc_mult, scaled_ppm); cpts_update_cur_time(cpts, CPTS_EV_PUSH, NULL); @@ -435,7 +423,7 @@ static const struct ptp_clock_info cpts_info = { .n_ext_ts = 0, .n_pins = 0, .pps = 0, - .adjfreq = cpts_ptp_adjfreq, + .adjfine = cpts_ptp_adjfine, .adjtime = cpts_ptp_adjtime, .gettimex64 = cpts_ptp_gettimeex, .settime64 = cpts_ptp_settime, @@ -794,7 +782,7 @@ struct cpts *cpts_create(struct device *dev, void __iomem *regs, cpts_calc_mult_shift(cpts); /* save cc.mult original value as it can be modified - * by cpts_ptp_adjfreq(). + * by cpts_ptp_adjfine(). */ cpts->cc_mult = cpts->cc.mult; From a29c132f92ed5af6e7116966b7e9899d4c22783c Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Wed, 9 Nov 2022 15:09:43 -0800 Subject: [PATCH 0847/2002] ptp: bnxt: convert .adjfreq to .adjfine When the BNXT_FW_CAP_PTP_RTC flag is not set, the bnxt driver implements .adjfreq on a cyclecounter in terms of the straightforward "base * ppb / 1 billion" calculation. When BNXT_FW_CAP_PTP_RTC is set, the driver forwards the ppb value to firmware for configuration. Convert the driver to the newer .adjfine interface, updating the cyclecounter calculation to use adjust_by_scaled_ppm to perform the calculation. Use scaled_ppm_to_ppb when forwarding the correction to firmware. Signed-off-by: Jacob Keller Cc: Michael Chan Cc: Richard Cochran Reviewed-by: Pavan Chebbi Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bnxt/bnxt_ptp.c | 22 +++++-------------- 1 file changed, 6 insertions(+), 16 deletions(-) diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_ptp.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_ptp.c index 460cb20599f6..4ec8bba18cdd 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_ptp.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_ptp.c @@ -205,7 +205,7 @@ static int bnxt_ptp_adjtime(struct ptp_clock_info *ptp_info, s64 delta) return 0; } -static int bnxt_ptp_adjfreq(struct ptp_clock_info *ptp_info, s32 ppb) +static int bnxt_ptp_adjfine(struct ptp_clock_info *ptp_info, long scaled_ppm) { struct bnxt_ptp_cfg *ptp = container_of(ptp_info, struct bnxt_ptp_cfg, ptp_info); @@ -214,23 +214,13 @@ static int bnxt_ptp_adjfreq(struct ptp_clock_info *ptp_info, s32 ppb) int rc = 0; if (!(ptp->bp->fw_cap & BNXT_FW_CAP_PTP_RTC)) { - int neg_adj = 0; - u32 diff; - u64 adj; - - if (ppb < 0) { - neg_adj = 1; - ppb = -ppb; - } - adj = ptp->cmult; - adj *= ppb; - diff = div_u64(adj, 1000000000ULL); - spin_lock_bh(&ptp->ptp_lock); timecounter_read(&ptp->tc); - ptp->cc.mult = neg_adj ? ptp->cmult - diff : ptp->cmult + diff; + ptp->cc.mult = adjust_by_scaled_ppm(ptp->cmult, scaled_ppm); spin_unlock_bh(&ptp->ptp_lock); } else { + s32 ppb = scaled_ppm_to_ppb(scaled_ppm); + rc = hwrm_req_init(bp, req, HWRM_PORT_MAC_CFG); if (rc) return rc; @@ -240,7 +230,7 @@ static int bnxt_ptp_adjfreq(struct ptp_clock_info *ptp_info, s32 ppb) rc = hwrm_req_send(ptp->bp, req); if (rc) netdev_err(ptp->bp->dev, - "ptp adjfreq failed. rc = %d\n", rc); + "ptp adjfine failed. rc = %d\n", rc); } return rc; } @@ -769,7 +759,7 @@ static const struct ptp_clock_info bnxt_ptp_caps = { .n_per_out = 0, .n_pins = 0, .pps = 0, - .adjfreq = bnxt_ptp_adjfreq, + .adjfine = bnxt_ptp_adjfine, .adjtime = bnxt_ptp_adjtime, .do_aux_work = bnxt_ptp_ts_aux_work, .gettimex64 = bnxt_ptp_gettimex, From e2bd9c76c89fbe25df351fc5902cbbcca6a7d372 Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Wed, 9 Nov 2022 15:09:44 -0800 Subject: [PATCH 0848/2002] ptp: convert remaining drivers to adjfine interface Convert all remaining drivers that still use .adjfreq to the newer .adjfine implementation. These drivers are not straightforward, as they use non-standard methods of programming their hardware. They are all converted to use scaled_ppm_to_ppb to get the parts per billion value that their logic depends on. Signed-off-by: Jacob Keller Cc: Ariel Elior Cc: Sudarsana Kalluru Cc: Manish Chopra Cc: Derek Chickles Cc: Satanand Burla Cc: Felix Manlunas Cc: Raju Rangoju Cc: Joakim Zhang Cc: Edward Cree Cc: Martin Habets Cc: Richard Cochran Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c | 9 +++++---- drivers/net/ethernet/cavium/liquidio/lio_main.c | 11 +++++++---- drivers/net/ethernet/chelsio/cxgb4/cxgb4_ptp.c | 13 ++++++++----- drivers/net/ethernet/freescale/fec_ptp.c | 13 ++++++++----- drivers/net/ethernet/qlogic/qede/qede_ptp.c | 13 ++++++++----- drivers/net/ethernet/sfc/ptp.c | 7 ++++--- drivers/net/ethernet/sfc/siena/ptp.c | 7 ++++--- drivers/net/ethernet/ti/am65-cpts.c | 5 +++-- drivers/ptp/ptp_dte.c | 5 +++-- 9 files changed, 50 insertions(+), 33 deletions(-) diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c index 51b1690fd045..5d1e4fe335aa 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c @@ -13671,19 +13671,20 @@ static int bnx2x_send_update_drift_ramrod(struct bnx2x *bp, int drift_dir, return bnx2x_func_state_change(bp, &func_params); } -static int bnx2x_ptp_adjfreq(struct ptp_clock_info *ptp, s32 ppb) +static int bnx2x_ptp_adjfine(struct ptp_clock_info *ptp, long scaled_ppm) { struct bnx2x *bp = container_of(ptp, struct bnx2x, ptp_clock_info); int rc; int drift_dir = 1; int val, period, period1, period2, dif, dif1, dif2; int best_dif = BNX2X_MAX_PHC_DRIFT, best_period = 0, best_val = 0; + s32 ppb = scaled_ppm_to_ppb(scaled_ppm); - DP(BNX2X_MSG_PTP, "PTP adjfreq called, ppb = %d\n", ppb); + DP(BNX2X_MSG_PTP, "PTP adjfine called, ppb = %d\n", ppb); if (!netif_running(bp->dev)) { DP(BNX2X_MSG_PTP, - "PTP adjfreq called while the interface is down\n"); + "PTP adjfine called while the interface is down\n"); return -ENETDOWN; } @@ -13818,7 +13819,7 @@ void bnx2x_register_phc(struct bnx2x *bp) bp->ptp_clock_info.n_ext_ts = 0; bp->ptp_clock_info.n_per_out = 0; bp->ptp_clock_info.pps = 0; - bp->ptp_clock_info.adjfreq = bnx2x_ptp_adjfreq; + bp->ptp_clock_info.adjfine = bnx2x_ptp_adjfine; bp->ptp_clock_info.adjtime = bnx2x_ptp_adjtime; bp->ptp_clock_info.gettime64 = bnx2x_ptp_gettime; bp->ptp_clock_info.settime64 = bnx2x_ptp_settime; diff --git a/drivers/net/ethernet/cavium/liquidio/lio_main.c b/drivers/net/ethernet/cavium/liquidio/lio_main.c index d312bd594935..cc88e743d963 100644 --- a/drivers/net/ethernet/cavium/liquidio/lio_main.c +++ b/drivers/net/ethernet/cavium/liquidio/lio_main.c @@ -1512,14 +1512,17 @@ static void free_netsgbuf_with_resp(void *buf) } /** - * liquidio_ptp_adjfreq - Adjust ptp frequency + * liquidio_ptp_adjfine - Adjust ptp frequency * @ptp: PTP clock info - * @ppb: how much to adjust by, in parts-per-billion + * @scaled_ppm: how much to adjust by, in scaled parts-per-million + * + * Scaled parts per million is ppm with a 16-bit binary fractional field. */ -static int liquidio_ptp_adjfreq(struct ptp_clock_info *ptp, s32 ppb) +static int liquidio_ptp_adjfine(struct ptp_clock_info *ptp, long scaled_ppm) { struct lio *lio = container_of(ptp, struct lio, ptp_info); struct octeon_device *oct = (struct octeon_device *)lio->oct_dev; + s32 ppb = scaled_ppm_to_ppb(scaled_ppm); u64 comp, delta; unsigned long flags; bool neg_adj = false; @@ -1643,7 +1646,7 @@ static void oct_ptp_open(struct net_device *netdev) lio->ptp_info.n_ext_ts = 0; lio->ptp_info.n_per_out = 0; lio->ptp_info.pps = 0; - lio->ptp_info.adjfreq = liquidio_ptp_adjfreq; + lio->ptp_info.adjfine = liquidio_ptp_adjfine; lio->ptp_info.adjtime = liquidio_ptp_adjtime; lio->ptp_info.gettime64 = liquidio_ptp_gettime; lio->ptp_info.settime64 = liquidio_ptp_settime; diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_ptp.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_ptp.c index 5bf117d2179f..cbd06d9b95d4 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_ptp.c +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_ptp.c @@ -194,17 +194,20 @@ int cxgb4_ptp_redirect_rx_packet(struct adapter *adapter, struct port_info *pi) } /** - * cxgb4_ptp_adjfreq - Adjust frequency of PHC cycle counter + * cxgb4_ptp_adjfine - Adjust frequency of PHC cycle counter * @ptp: ptp clock structure - * @ppb: Desired frequency change in parts per billion + * @scaled_ppm: Desired frequency in scaled parts per billion * - * Adjust the frequency of the PHC cycle counter by the indicated ppb from + * Adjust the frequency of the PHC cycle counter by the indicated amount from * the base frequency. + * + * Scaled parts per million is ppm with a 16-bit binary fractional field. */ -static int cxgb4_ptp_adjfreq(struct ptp_clock_info *ptp, s32 ppb) +static int cxgb4_ptp_adjfine(struct ptp_clock_info *ptp, long scaled_ppm) { struct adapter *adapter = (struct adapter *)container_of(ptp, struct adapter, ptp_clock_info); + s32 ppb = scaled_ppm_to_ppb(scaled_ppm); struct fw_ptp_cmd c; int err; @@ -404,7 +407,7 @@ static const struct ptp_clock_info cxgb4_ptp_clock_info = { .n_ext_ts = 0, .n_per_out = 0, .pps = 0, - .adjfreq = cxgb4_ptp_adjfreq, + .adjfine = cxgb4_ptp_adjfine, .adjtime = cxgb4_ptp_adjtime, .gettime64 = cxgb4_ptp_gettime, .settime64 = cxgb4_ptp_settime, diff --git a/drivers/net/ethernet/freescale/fec_ptp.c b/drivers/net/ethernet/freescale/fec_ptp.c index 67aa694a62ec..ab86bb8562ef 100644 --- a/drivers/net/ethernet/freescale/fec_ptp.c +++ b/drivers/net/ethernet/freescale/fec_ptp.c @@ -338,18 +338,21 @@ void fec_ptp_start_cyclecounter(struct net_device *ndev) } /** - * fec_ptp_adjfreq - adjust ptp cycle frequency + * fec_ptp_adjfine - adjust ptp cycle frequency * @ptp: the ptp clock structure - * @ppb: parts per billion adjustment from base + * @scaled_ppm: scaled parts per million adjustment from base * * Adjust the frequency of the ptp cycle counter by the - * indicated ppb from the base frequency. + * indicated amount from the base frequency. + * + * Scaled parts per million is ppm with a 16-bit binary fractional field. * * Because ENET hardware frequency adjust is complex, * using software method to do that. */ -static int fec_ptp_adjfreq(struct ptp_clock_info *ptp, s32 ppb) +static int fec_ptp_adjfine(struct ptp_clock_info *ptp, long scaled_ppm) { + s32 ppb = scaled_ppm_to_ppb(scaled_ppm); unsigned long flags; int neg_adj = 0; u32 i, tmp; @@ -742,7 +745,7 @@ void fec_ptp_init(struct platform_device *pdev, int irq_idx) fep->ptp_caps.n_per_out = 1; fep->ptp_caps.n_pins = 0; fep->ptp_caps.pps = 1; - fep->ptp_caps.adjfreq = fec_ptp_adjfreq; + fep->ptp_caps.adjfine = fec_ptp_adjfine; fep->ptp_caps.adjtime = fec_ptp_adjtime; fep->ptp_caps.gettime64 = fec_ptp_gettime; fep->ptp_caps.settime64 = fec_ptp_settime; diff --git a/drivers/net/ethernet/qlogic/qede/qede_ptp.c b/drivers/net/ethernet/qlogic/qede/qede_ptp.c index c9c8225f04d6..747cc5e2bb78 100644 --- a/drivers/net/ethernet/qlogic/qede/qede_ptp.c +++ b/drivers/net/ethernet/qlogic/qede/qede_ptp.c @@ -28,16 +28,19 @@ struct qede_ptp { }; /** - * qede_ptp_adjfreq() - Adjust the frequency of the PTP cycle counter. + * qede_ptp_adjfine() - Adjust the frequency of the PTP cycle counter. * * @info: The PTP clock info structure. - * @ppb: Parts per billion adjustment from base. + * @scaled_ppm: Scaled parts per million adjustment from base. + * + * Scaled parts per million is ppm with a 16-bit binary fractional field. * * Return: Zero on success, negative errno otherwise. */ -static int qede_ptp_adjfreq(struct ptp_clock_info *info, s32 ppb) +static int qede_ptp_adjfine(struct ptp_clock_info *info, long scaled_ppm) { struct qede_ptp *ptp = container_of(info, struct qede_ptp, clock_info); + s32 ppb = scaled_ppm_to_ppb(scaled_ppm); struct qede_dev *edev = ptp->edev; int rc; @@ -47,7 +50,7 @@ static int qede_ptp_adjfreq(struct ptp_clock_info *info, s32 ppb) rc = ptp->ops->adjfreq(edev->cdev, ppb); spin_unlock_bh(&ptp->lock); } else { - DP_ERR(edev, "PTP adjfreq called while interface is down\n"); + DP_ERR(edev, "PTP adjfine called while interface is down\n"); rc = -EFAULT; } __qede_unlock(edev); @@ -462,7 +465,7 @@ int qede_ptp_enable(struct qede_dev *edev) ptp->clock_info.n_ext_ts = 0; ptp->clock_info.n_per_out = 0; ptp->clock_info.pps = 0; - ptp->clock_info.adjfreq = qede_ptp_adjfreq; + ptp->clock_info.adjfine = qede_ptp_adjfine; ptp->clock_info.adjtime = qede_ptp_adjtime; ptp->clock_info.gettime64 = qede_ptp_gettime; ptp->clock_info.settime64 = qede_ptp_settime; diff --git a/drivers/net/ethernet/sfc/ptp.c b/drivers/net/ethernet/sfc/ptp.c index eaef4a15008a..9f07e1ba7780 100644 --- a/drivers/net/ethernet/sfc/ptp.c +++ b/drivers/net/ethernet/sfc/ptp.c @@ -351,7 +351,7 @@ struct efx_ptp_data { void (*xmit_skb)(struct efx_nic *efx, struct sk_buff *skb); }; -static int efx_phc_adjfreq(struct ptp_clock_info *ptp, s32 delta); +static int efx_phc_adjfine(struct ptp_clock_info *ptp, long scaled_ppm); static int efx_phc_adjtime(struct ptp_clock_info *ptp, s64 delta); static int efx_phc_gettime(struct ptp_clock_info *ptp, struct timespec64 *ts); static int efx_phc_settime(struct ptp_clock_info *ptp, @@ -1508,7 +1508,7 @@ static const struct ptp_clock_info efx_phc_clock_info = { .n_per_out = 0, .n_pins = 0, .pps = 1, - .adjfreq = efx_phc_adjfreq, + .adjfine = efx_phc_adjfine, .adjtime = efx_phc_adjtime, .gettime64 = efx_phc_gettime, .settime64 = efx_phc_settime, @@ -2137,11 +2137,12 @@ void __efx_rx_skb_attach_timestamp(struct efx_channel *channel, ptp->ts_corrections.general_rx); } -static int efx_phc_adjfreq(struct ptp_clock_info *ptp, s32 delta) +static int efx_phc_adjfine(struct ptp_clock_info *ptp, long scaled_ppm) { struct efx_ptp_data *ptp_data = container_of(ptp, struct efx_ptp_data, phc_clock_info); + s32 delta = scaled_ppm_to_ppb(scaled_ppm); struct efx_nic *efx = ptp_data->efx; MCDI_DECLARE_BUF(inadj, MC_CMD_PTP_IN_ADJUST_LEN); s64 adjustment_ns; diff --git a/drivers/net/ethernet/sfc/siena/ptp.c b/drivers/net/ethernet/sfc/siena/ptp.c index 7c46752e6eae..38e666561bcd 100644 --- a/drivers/net/ethernet/sfc/siena/ptp.c +++ b/drivers/net/ethernet/sfc/siena/ptp.c @@ -347,7 +347,7 @@ struct efx_ptp_data { void (*xmit_skb)(struct efx_nic *efx, struct sk_buff *skb); }; -static int efx_phc_adjfreq(struct ptp_clock_info *ptp, s32 delta); +static int efx_phc_adjfine(struct ptp_clock_info *ptp, long scaled_ppm); static int efx_phc_adjtime(struct ptp_clock_info *ptp, s64 delta); static int efx_phc_gettime(struct ptp_clock_info *ptp, struct timespec64 *ts); static int efx_phc_settime(struct ptp_clock_info *ptp, @@ -1429,7 +1429,7 @@ static const struct ptp_clock_info efx_phc_clock_info = { .n_per_out = 0, .n_pins = 0, .pps = 1, - .adjfreq = efx_phc_adjfreq, + .adjfine = efx_phc_adjfine, .adjtime = efx_phc_adjtime, .gettime64 = efx_phc_gettime, .settime64 = efx_phc_settime, @@ -2044,11 +2044,12 @@ void __efx_siena_rx_skb_attach_timestamp(struct efx_channel *channel, ptp->ts_corrections.general_rx); } -static int efx_phc_adjfreq(struct ptp_clock_info *ptp, s32 delta) +static int efx_phc_adjfine(struct ptp_clock_info *ptp, long scaled_ppm) { struct efx_ptp_data *ptp_data = container_of(ptp, struct efx_ptp_data, phc_clock_info); + s32 delta = scaled_ppm_to_ppb(scaled_ppm); struct efx_nic *efx = ptp_data->efx; MCDI_DECLARE_BUF(inadj, MC_CMD_PTP_IN_ADJUST_LEN); s64 adjustment_ns; diff --git a/drivers/net/ethernet/ti/am65-cpts.c b/drivers/net/ethernet/ti/am65-cpts.c index 7f928c343426..9535396b28cd 100644 --- a/drivers/net/ethernet/ti/am65-cpts.c +++ b/drivers/net/ethernet/ti/am65-cpts.c @@ -391,9 +391,10 @@ static irqreturn_t am65_cpts_interrupt(int irq, void *dev_id) } /* PTP clock operations */ -static int am65_cpts_ptp_adjfreq(struct ptp_clock_info *ptp, s32 ppb) +static int am65_cpts_ptp_adjfine(struct ptp_clock_info *ptp, long scaled_ppm) { struct am65_cpts *cpts = container_of(ptp, struct am65_cpts, ptp_info); + s32 ppb = scaled_ppm_to_ppb(scaled_ppm); int neg_adj = 0; u64 adj_period; u32 val; @@ -625,7 +626,7 @@ static long am65_cpts_ts_work(struct ptp_clock_info *ptp); static struct ptp_clock_info am65_ptp_info = { .owner = THIS_MODULE, .name = "CTPS timer", - .adjfreq = am65_cpts_ptp_adjfreq, + .adjfine = am65_cpts_ptp_adjfine, .adjtime = am65_cpts_ptp_adjtime, .gettimex64 = am65_cpts_ptp_gettimex, .settime64 = am65_cpts_ptp_settime, diff --git a/drivers/ptp/ptp_dte.c b/drivers/ptp/ptp_dte.c index 8641fd060491..7cc5a00e625b 100644 --- a/drivers/ptp/ptp_dte.c +++ b/drivers/ptp/ptp_dte.c @@ -134,8 +134,9 @@ static s64 dte_read_nco_with_ovf(struct ptp_dte *ptp_dte) return ns; } -static int ptp_dte_adjfreq(struct ptp_clock_info *ptp, s32 ppb) +static int ptp_dte_adjfine(struct ptp_clock_info *ptp, long scaled_ppm) { + s32 ppb = scaled_ppm_to_ppb(scaled_ppm); u32 nco_incr; unsigned long flags; struct ptp_dte *ptp_dte = container_of(ptp, struct ptp_dte, caps); @@ -219,7 +220,7 @@ static const struct ptp_clock_info ptp_dte_caps = { .n_ext_ts = 0, .n_pins = 0, .pps = 0, - .adjfreq = ptp_dte_adjfreq, + .adjfine = ptp_dte_adjfine, .adjtime = ptp_dte_adjtime, .gettime64 = ptp_dte_gettime, .settime64 = ptp_dte_settime, From 75ab70ec5cefade915a999886e4863fe6a38f8b5 Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Wed, 9 Nov 2022 15:09:45 -0800 Subject: [PATCH 0849/2002] ptp: remove the .adjfreq interface function Now that all drivers have been converted to .adjfine, we can remove the .adjfreq from the interface structure. Signed-off-by: Jacob Keller Cc: Richard Cochran Signed-off-by: David S. Miller --- drivers/ptp/ptp_clock.c | 5 +---- include/linux/ptp_clock_kernel.h | 7 ------- 2 files changed, 1 insertion(+), 11 deletions(-) diff --git a/drivers/ptp/ptp_clock.c b/drivers/ptp/ptp_clock.c index 51cae72bb6db..62d4d29e7c05 100644 --- a/drivers/ptp/ptp_clock.c +++ b/drivers/ptp/ptp_clock.c @@ -131,10 +131,7 @@ static int ptp_clock_adjtime(struct posix_clock *pc, struct __kernel_timex *tx) long ppb = scaled_ppm_to_ppb(tx->freq); if (ppb > ops->max_adj || ppb < -ops->max_adj) return -ERANGE; - if (ops->adjfine) - err = ops->adjfine(ops, tx->freq); - else - err = ops->adjfreq(ops, ppb); + err = ops->adjfine(ops, tx->freq); ptp->dialed_frequency = tx->freq; } else if (tx->modes & ADJ_OFFSET) { if (ops->adjphase) { diff --git a/include/linux/ptp_clock_kernel.h b/include/linux/ptp_clock_kernel.h index f4781c5766d6..fdffa6a98d79 100644 --- a/include/linux/ptp_clock_kernel.h +++ b/include/linux/ptp_clock_kernel.h @@ -77,12 +77,6 @@ struct ptp_system_timestamp { * nominal frequency in parts per million, but with a * 16 bit binary fractional field. * - * @adjfreq: Adjusts the frequency of the hardware clock. - * This method is deprecated. New drivers should implement - * the @adjfine method instead. - * parameter delta: Desired frequency offset from nominal frequency - * in parts per billion - * * @adjphase: Adjusts the phase offset of the hardware clock. * parameter delta: Desired change in nanoseconds. * @@ -174,7 +168,6 @@ struct ptp_clock_info { int pps; struct ptp_pin_desc *pin_config; int (*adjfine)(struct ptp_clock_info *ptp, long scaled_ppm); - int (*adjfreq)(struct ptp_clock_info *ptp, s32 delta); int (*adjphase)(struct ptp_clock_info *ptp, s32 phase); int (*adjtime)(struct ptp_clock_info *ptp, s64 delta); int (*gettime64)(struct ptp_clock_info *ptp, struct timespec64 *ts); From 42597aa372f5d2f26f209c5db36b1cd098b27147 Mon Sep 17 00:00:00 2001 From: Eduard Zingerman Date: Fri, 11 Nov 2022 00:32:40 +0200 Subject: [PATCH 0850/2002] libbpf: Hashmap.h update to fix build issues using LLVM14 A fix for the LLVM compilation error while building bpftool. Replaces the expression: _Static_assert((p) == NULL || ...) by expression: _Static_assert((__builtin_constant_p((p)) ? (p) == NULL : 0) || ...) When "p" is not a constant the former is not considered to be a constant expression by LLVM 14. The error was introduced in the following patch-set: [1]. The error was reported here: [2]. [1] https://lore.kernel.org/bpf/20221109142611.879983-1-eddyz87@gmail.com/ [2] https://lore.kernel.org/all/202211110355.BcGcbZxP-lkp@intel.com/ Reported-by: kernel test robot Fixes: c302378bc157 ("libbpf: Hashmap interface update to allow both long and void* keys/values") Signed-off-by: Eduard Zingerman Signed-off-by: Andrii Nakryiko Acked-by: Stanislav Fomichev Link: https://lore.kernel.org/bpf/20221110223240.1350810-1-eddyz87@gmail.com --- tools/lib/bpf/hashmap.h | 3 ++- tools/perf/util/hashmap.h | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/tools/lib/bpf/hashmap.h b/tools/lib/bpf/hashmap.h index 3fe647477bad..0a5bf1937a7c 100644 --- a/tools/lib/bpf/hashmap.h +++ b/tools/lib/bpf/hashmap.h @@ -123,7 +123,8 @@ enum hashmap_insert_strategy { }; #define hashmap_cast_ptr(p) ({ \ - _Static_assert((p) == NULL || sizeof(*(p)) == sizeof(long), \ + _Static_assert((__builtin_constant_p((p)) ? (p) == NULL : 0) || \ + sizeof(*(p)) == sizeof(long), \ #p " pointee should be a long-sized integer or a pointer"); \ (long *)(p); \ }) diff --git a/tools/perf/util/hashmap.h b/tools/perf/util/hashmap.h index 3fe647477bad..0a5bf1937a7c 100644 --- a/tools/perf/util/hashmap.h +++ b/tools/perf/util/hashmap.h @@ -123,7 +123,8 @@ enum hashmap_insert_strategy { }; #define hashmap_cast_ptr(p) ({ \ - _Static_assert((p) == NULL || sizeof(*(p)) == sizeof(long), \ + _Static_assert((__builtin_constant_p((p)) ? (p) == NULL : 0) || \ + sizeof(*(p)) == sizeof(long), \ #p " pointee should be a long-sized integer or a pointer"); \ (long *)(p); \ }) From 83177c0dca3811faa051124731a692609caee7c7 Mon Sep 17 00:00:00 2001 From: Donald Hunter Date: Tue, 1 Nov 2022 11:45:42 +0000 Subject: [PATCH 0851/2002] docs/bpf: Document BPF_MAP_TYPE_LPM_TRIE map Add documentation for BPF_MAP_TYPE_LPM_TRIE including kernel BPF helper usage, userspace usage and examples. Signed-off-by: Donald Hunter Signed-off-by: Andrii Nakryiko Link: https://lore.kernel.org/bpf/20221101114542.24481-2-donald.hunter@gmail.com --- Documentation/bpf/map_lpm_trie.rst | 181 +++++++++++++++++++++++++++++ 1 file changed, 181 insertions(+) create mode 100644 Documentation/bpf/map_lpm_trie.rst diff --git a/Documentation/bpf/map_lpm_trie.rst b/Documentation/bpf/map_lpm_trie.rst new file mode 100644 index 000000000000..31be1aa7ba2c --- /dev/null +++ b/Documentation/bpf/map_lpm_trie.rst @@ -0,0 +1,181 @@ +.. SPDX-License-Identifier: GPL-2.0-only +.. Copyright (C) 2022 Red Hat, Inc. + +===================== +BPF_MAP_TYPE_LPM_TRIE +===================== + +.. note:: + - ``BPF_MAP_TYPE_LPM_TRIE`` was introduced in kernel version 4.11 + +``BPF_MAP_TYPE_LPM_TRIE`` provides a longest prefix match algorithm that +can be used to match IP addresses to a stored set of prefixes. +Internally, data is stored in an unbalanced trie of nodes that uses +``prefixlen,data`` pairs as its keys. The ``data`` is interpreted in +network byte order, i.e. big endian, so ``data[0]`` stores the most +significant byte. + +LPM tries may be created with a maximum prefix length that is a multiple +of 8, in the range from 8 to 2048. The key used for lookup and update +operations is a ``struct bpf_lpm_trie_key``, extended by +``max_prefixlen/8`` bytes. + +- For IPv4 addresses the data length is 4 bytes +- For IPv6 addresses the data length is 16 bytes + +The value type stored in the LPM trie can be any user defined type. + +.. note:: + When creating a map of type ``BPF_MAP_TYPE_LPM_TRIE`` you must set the + ``BPF_F_NO_PREALLOC`` flag. + +Usage +===== + +Kernel BPF +---------- + +.. c:function:: + void *bpf_map_lookup_elem(struct bpf_map *map, const void *key) + +The longest prefix entry for a given data value can be found using the +``bpf_map_lookup_elem()`` helper. This helper returns a pointer to the +value associated with the longest matching ``key``, or ``NULL`` if no +entry was found. + +The ``key`` should have ``prefixlen`` set to ``max_prefixlen`` when +performing longest prefix lookups. For example, when searching for the +longest prefix match for an IPv4 address, ``prefixlen`` should be set to +``32``. + +.. c:function:: + long bpf_map_update_elem(struct bpf_map *map, const void *key, const void *value, u64 flags) + +Prefix entries can be added or updated using the ``bpf_map_update_elem()`` +helper. This helper replaces existing elements atomically. + +``bpf_map_update_elem()`` returns ``0`` on success, or negative error in +case of failure. + + .. note:: + The flags parameter must be one of BPF_ANY, BPF_NOEXIST or BPF_EXIST, + but the value is ignored, giving BPF_ANY semantics. + +.. c:function:: + long bpf_map_delete_elem(struct bpf_map *map, const void *key) + +Prefix entries can be deleted using the ``bpf_map_delete_elem()`` +helper. This helper will return 0 on success, or negative error in case +of failure. + +Userspace +--------- + +Access from userspace uses libbpf APIs with the same names as above, with +the map identified by ``fd``. + +.. c:function:: + int bpf_map_get_next_key (int fd, const void *cur_key, void *next_key) + +A userspace program can iterate through the entries in an LPM trie using +libbpf's ``bpf_map_get_next_key()`` function. The first key can be +fetched by calling ``bpf_map_get_next_key()`` with ``cur_key`` set to +``NULL``. Subsequent calls will fetch the next key that follows the +current key. ``bpf_map_get_next_key()`` returns ``0`` on success, +``-ENOENT`` if ``cur_key`` is the last key in the trie, or negative +error in case of failure. + +``bpf_map_get_next_key()`` will iterate through the LPM trie elements +from leftmost leaf first. This means that iteration will return more +specific keys before less specific ones. + +Examples +======== + +Please see ``tools/testing/selftests/bpf/test_lpm_map.c`` for examples +of LPM trie usage from userspace. The code snippets below demonstrate +API usage. + +Kernel BPF +---------- + +The following BPF code snippet shows how to declare a new LPM trie for IPv4 +address prefixes: + +.. code-block:: c + + #include + #include + + struct ipv4_lpm_key { + __u32 prefixlen; + __u32 data; + }; + + struct { + __uint(type, BPF_MAP_TYPE_LPM_TRIE); + __type(key, struct ipv4_lpm_key); + __type(value, __u32); + __uint(map_flags, BPF_F_NO_PREALLOC); + __uint(max_entries, 255); + } ipv4_lpm_map SEC(".maps"); + +The following BPF code snippet shows how to lookup by IPv4 address: + +.. code-block:: c + + void *lookup(__u32 ipaddr) + { + struct ipv4_lpm_key key = { + .prefixlen = 32, + .data = ipaddr + }; + + return bpf_map_lookup_elem(&ipv4_lpm_map, &key); + } + +Userspace +--------- + +The following snippet shows how to insert an IPv4 prefix entry into an +LPM trie: + +.. code-block:: c + + int add_prefix_entry(int lpm_fd, __u32 addr, __u32 prefixlen, struct value *value) + { + struct ipv4_lpm_key ipv4_key = { + .prefixlen = prefixlen, + .data = addr + }; + return bpf_map_update_elem(lpm_fd, &ipv4_key, value, BPF_ANY); + } + +The following snippet shows a userspace program walking through the entries +of an LPM trie: + + +.. code-block:: c + + #include + #include + + void iterate_lpm_trie(int map_fd) + { + struct ipv4_lpm_key *cur_key = NULL; + struct ipv4_lpm_key next_key; + struct value value; + int err; + + for (;;) { + err = bpf_map_get_next_key(map_fd, cur_key, &next_key); + if (err) + break; + + bpf_map_lookup_elem(map_fd, &next_key, &value); + + /* Use key and value here */ + + cur_key = &next_key; + } + } From 161939abc80ba855ee1739be4a807c6b0f1b8c6c Mon Sep 17 00:00:00 2001 From: Maryam Tahhan Date: Mon, 7 Nov 2022 11:52:07 -0500 Subject: [PATCH 0852/2002] docs/bpf: Document BPF_MAP_TYPE_CPUMAP map Add documentation for BPF_MAP_TYPE_CPUMAP including kernel version introduced, usage and examples. Co-developed-by: Lorenzo Bianconi Signed-off-by: Maryam Tahhan Signed-off-by: Lorenzo Bianconi Signed-off-by: Andrii Nakryiko Acked-by: Yonghong Song Link: https://lore.kernel.org/bpf/20221107165207.2682075-2-mtahhan@redhat.com --- Documentation/bpf/map_cpumap.rst | 166 +++++++++++++++++++++++++++++++ kernel/bpf/cpumap.c | 9 +- 2 files changed, 172 insertions(+), 3 deletions(-) create mode 100644 Documentation/bpf/map_cpumap.rst diff --git a/Documentation/bpf/map_cpumap.rst b/Documentation/bpf/map_cpumap.rst new file mode 100644 index 000000000000..eaf57b38cafd --- /dev/null +++ b/Documentation/bpf/map_cpumap.rst @@ -0,0 +1,166 @@ +.. SPDX-License-Identifier: GPL-2.0-only +.. Copyright (C) 2022 Red Hat, Inc. + +=================== +BPF_MAP_TYPE_CPUMAP +=================== + +.. note:: + - ``BPF_MAP_TYPE_CPUMAP`` was introduced in kernel version 4.15 + +.. kernel-doc:: kernel/bpf/cpumap.c + :doc: cpu map + +An example use-case for this map type is software based Receive Side Scaling (RSS). + +The CPUMAP represents the CPUs in the system indexed as the map-key, and the +map-value is the config setting (per CPUMAP entry). Each CPUMAP entry has a dedicated +kernel thread bound to the given CPU to represent the remote CPU execution unit. + +Starting from Linux kernel version 5.9 the CPUMAP can run a second XDP program +on the remote CPU. This allows an XDP program to split its processing across +multiple CPUs. For example, a scenario where the initial CPU (that sees/receives +the packets) needs to do minimal packet processing and the remote CPU (to which +the packet is directed) can afford to spend more cycles processing the frame. The +initial CPU is where the XDP redirect program is executed. The remote CPU +receives raw ``xdp_frame`` objects. + +Usage +===== + +Kernel BPF +---------- +.. c:function:: + long bpf_redirect_map(struct bpf_map *map, u32 key, u64 flags) + + Redirect the packet to the endpoint referenced by ``map`` at index ``key``. + For ``BPF_MAP_TYPE_CPUMAP`` this map contains references to CPUs. + + The lower two bits of ``flags`` are used as the return code if the map lookup + fails. This is so that the return value can be one of the XDP program return + codes up to ``XDP_TX``, as chosen by the caller. + +Userspace +--------- +.. note:: + CPUMAP entries can only be updated/looked up/deleted from user space and not + from an eBPF program. Trying to call these functions from a kernel eBPF + program will result in the program failing to load and a verifier warning. + +.. c:function:: + int bpf_map_update_elem(int fd, const void *key, const void *value, + __u64 flags); + + CPU entries can be added or updated using the ``bpf_map_update_elem()`` + helper. This helper replaces existing elements atomically. The ``value`` parameter + can be ``struct bpf_cpumap_val``. + + .. code-block:: c + + struct bpf_cpumap_val { + __u32 qsize; /* queue size to remote target CPU */ + union { + int fd; /* prog fd on map write */ + __u32 id; /* prog id on map read */ + } bpf_prog; + }; + + The flags argument can be one of the following: + - BPF_ANY: Create a new element or update an existing element. + - BPF_NOEXIST: Create a new element only if it did not exist. + - BPF_EXIST: Update an existing element. + +.. c:function:: + int bpf_map_lookup_elem(int fd, const void *key, void *value); + + CPU entries can be retrieved using the ``bpf_map_lookup_elem()`` + helper. + +.. c:function:: + int bpf_map_delete_elem(int fd, const void *key); + + CPU entries can be deleted using the ``bpf_map_delete_elem()`` + helper. This helper will return 0 on success, or negative error in case of + failure. + +Examples +======== +Kernel +------ + +The following code snippet shows how to declare a ``BPF_MAP_TYPE_CPUMAP`` called +``cpu_map`` and how to redirect packets to a remote CPU using a round robin scheme. + +.. code-block:: c + + struct { + __uint(type, BPF_MAP_TYPE_CPUMAP); + __type(key, __u32); + __type(value, struct bpf_cpumap_val); + __uint(max_entries, 12); + } cpu_map SEC(".maps"); + + struct { + __uint(type, BPF_MAP_TYPE_ARRAY); + __type(key, __u32); + __type(value, __u32); + __uint(max_entries, 12); + } cpus_available SEC(".maps"); + + struct { + __uint(type, BPF_MAP_TYPE_PERCPU_ARRAY); + __type(key, __u32); + __type(value, __u32); + __uint(max_entries, 1); + } cpus_iterator SEC(".maps"); + + SEC("xdp") + int xdp_redir_cpu_round_robin(struct xdp_md *ctx) + { + __u32 key = 0; + __u32 cpu_dest = 0; + __u32 *cpu_selected, *cpu_iterator; + __u32 cpu_idx; + + cpu_iterator = bpf_map_lookup_elem(&cpus_iterator, &key); + if (!cpu_iterator) + return XDP_ABORTED; + cpu_idx = *cpu_iterator; + + *cpu_iterator += 1; + if (*cpu_iterator == bpf_num_possible_cpus()) + *cpu_iterator = 0; + + cpu_selected = bpf_map_lookup_elem(&cpus_available, &cpu_idx); + if (!cpu_selected) + return XDP_ABORTED; + cpu_dest = *cpu_selected; + + if (cpu_dest >= bpf_num_possible_cpus()) + return XDP_ABORTED; + + return bpf_redirect_map(&cpu_map, cpu_dest, 0); + } + +Userspace +--------- + +The following code snippet shows how to dynamically set the max_entries for a +CPUMAP to the max number of cpus available on the system. + +.. code-block:: c + + int set_max_cpu_entries(struct bpf_map *cpu_map) + { + if (bpf_map__set_max_entries(cpu_map, libbpf_num_possible_cpus()) < 0) { + fprintf(stderr, "Failed to set max entries for cpu_map map: %s", + strerror(errno)); + return -1; + } + return 0; + } + +References +=========== + +- https://developers.redhat.com/blog/2021/05/13/receive-side-scaling-rss-with-ebpf-and-cpumap#redirecting_into_a_cpumap diff --git a/kernel/bpf/cpumap.c b/kernel/bpf/cpumap.c index bb03fdba73bb..6b6a78c04b90 100644 --- a/kernel/bpf/cpumap.c +++ b/kernel/bpf/cpumap.c @@ -4,13 +4,16 @@ * Copyright (c) 2017 Jesper Dangaard Brouer, Red Hat Inc. */ -/* The 'cpumap' is primarily used as a backend map for XDP BPF helper +/** + * DOC: cpu map + * The 'cpumap' is primarily used as a backend map for XDP BPF helper * call bpf_redirect_map() and XDP_REDIRECT action, like 'devmap'. * - * Unlike devmap which redirects XDP frames out another NIC device, + * Unlike devmap which redirects XDP frames out to another NIC device, * this map type redirects raw XDP frames to another CPU. The remote * CPU will do SKB-allocation and call the normal network stack. - * + */ +/* * This is a scalability and isolation mechanism, that allow * separating the early driver network XDP layer, from the rest of the * netstack, and assigning dedicated CPUs for this stage. This From f720b84811b2133116e402090369d1be06de7ad4 Mon Sep 17 00:00:00 2001 From: Donald Hunter Date: Tue, 8 Nov 2022 10:22:15 +0000 Subject: [PATCH 0853/2002] docs/bpf: Document BPF ARRAY_OF_MAPS and HASH_OF_MAPS Add documentation for the ARRAY_OF_MAPS and HASH_OF_MAPS map types, including usage and examples. Reported-by: kernel test robot Signed-off-by: Donald Hunter Signed-off-by: Andrii Nakryiko Acked-by: Yonghong Song Link: https://lore.kernel.org/bpf/20221108102215.47297-1-donald.hunter@gmail.com --- Documentation/bpf/map_of_maps.rst | 126 ++++++++++++++++++++++++++++++ 1 file changed, 126 insertions(+) create mode 100644 Documentation/bpf/map_of_maps.rst diff --git a/Documentation/bpf/map_of_maps.rst b/Documentation/bpf/map_of_maps.rst new file mode 100644 index 000000000000..07212b9227a9 --- /dev/null +++ b/Documentation/bpf/map_of_maps.rst @@ -0,0 +1,126 @@ +.. SPDX-License-Identifier: GPL-2.0-only +.. Copyright (C) 2022 Red Hat, Inc. + +======================================================== +BPF_MAP_TYPE_ARRAY_OF_MAPS and BPF_MAP_TYPE_HASH_OF_MAPS +======================================================== + +.. note:: + - ``BPF_MAP_TYPE_ARRAY_OF_MAPS`` and ``BPF_MAP_TYPE_HASH_OF_MAPS`` were + introduced in kernel version 4.12 + +``BPF_MAP_TYPE_ARRAY_OF_MAPS`` and ``BPF_MAP_TYPE_HASH_OF_MAPS`` provide general +purpose support for map in map storage. One level of nesting is supported, where +an outer map contains instances of a single type of inner map, for example +``array_of_maps->sock_map``. + +When creating an outer map, an inner map instance is used to initialize the +metadata that the outer map holds about its inner maps. This inner map has a +separate lifetime from the outer map and can be deleted after the outer map has +been created. + +The outer map supports element lookup, update and delete from user space using +the syscall API. A BPF program is only allowed to do element lookup in the outer +map. + +.. note:: + - Multi-level nesting is not supported. + - Any BPF map type can be used as an inner map, except for + ``BPF_MAP_TYPE_PROG_ARRAY``. + - A BPF program cannot update or delete outer map entries. + +For ``BPF_MAP_TYPE_ARRAY_OF_MAPS`` the key is an unsigned 32-bit integer index +into the array. The array is a fixed size with ``max_entries`` elements that are +zero initialized when created. + +For ``BPF_MAP_TYPE_HASH_OF_MAPS`` the key type can be chosen when defining the +map. The kernel is responsible for allocating and freeing key/value pairs, up to +the max_entries limit that you specify. Hash maps use pre-allocation of hash +table elements by default. The ``BPF_F_NO_PREALLOC`` flag can be used to disable +pre-allocation when it is too memory expensive. + +Usage +===== + +Kernel BPF Helper +----------------- + +.. c:function:: + void *bpf_map_lookup_elem(struct bpf_map *map, const void *key) + +Inner maps can be retrieved using the ``bpf_map_lookup_elem()`` helper. This +helper returns a pointer to the inner map, or ``NULL`` if no entry was found. + +Examples +======== + +Kernel BPF Example +------------------ + +This snippet shows how to create and initialise an array of devmaps in a BPF +program. Note that the outer array can only be modified from user space using +the syscall API. + +.. code-block:: c + + struct inner_map { + __uint(type, BPF_MAP_TYPE_DEVMAP); + __uint(max_entries, 10); + __type(key, __u32); + __type(value, __u32); + } inner_map1 SEC(".maps"), inner_map2 SEC(".maps"); + + struct { + __uint(type, BPF_MAP_TYPE_ARRAY_OF_MAPS); + __uint(max_entries, 2); + __type(key, __u32); + __array(values, struct inner_map); + } outer_map SEC(".maps") = { + .values = { &inner_map1, + &inner_map2 } + }; + +See ``progs/test_btf_map_in_map.c`` in ``tools/testing/selftests/bpf`` for more +examples of declarative initialisation of outer maps. + +User Space +---------- + +This snippet shows how to create an array based outer map: + +.. code-block:: c + + int create_outer_array(int inner_fd) { + LIBBPF_OPTS(bpf_map_create_opts, opts, .inner_map_fd = inner_fd); + int fd; + + fd = bpf_map_create(BPF_MAP_TYPE_ARRAY_OF_MAPS, + "example_array", /* name */ + sizeof(__u32), /* key size */ + sizeof(__u32), /* value size */ + 256, /* max entries */ + &opts); /* create opts */ + return fd; + } + + +This snippet shows how to add an inner map to an outer map: + +.. code-block:: c + + int add_devmap(int outer_fd, int index, const char *name) { + int fd; + + fd = bpf_map_create(BPF_MAP_TYPE_DEVMAP, name, + sizeof(__u32), sizeof(__u32), 256, NULL); + if (fd < 0) + return fd; + + return bpf_map_update_elem(outer_fd, &index, &fd, BPF_ANY); + } + +References +========== + +- https://lore.kernel.org/netdev/20170322170035.923581-3-kafai@fb.com/ +- https://lore.kernel.org/netdev/20170322170035.923581-4-kafai@fb.com/ From 64488ca57ab8645fa1b69fa130ec0aa4d93cdc9e Mon Sep 17 00:00:00 2001 From: Donald Hunter Date: Tue, 8 Nov 2022 09:33:14 +0000 Subject: [PATCH 0854/2002] docs/bpf: Document BPF map types QUEUE and STACK Add documentation for BPF_MAP_TYPE_QUEUE and BPF_MAP_TYPE_STACK, including usage and examples. Signed-off-by: Donald Hunter Signed-off-by: Andrii Nakryiko Acked-by: Yonghong Song Link: https://lore.kernel.org/bpf/20221108093314.44851-1-donald.hunter@gmail.com --- Documentation/bpf/map_queue_stack.rst | 122 ++++++++++++++++++++++++++ 1 file changed, 122 insertions(+) create mode 100644 Documentation/bpf/map_queue_stack.rst diff --git a/Documentation/bpf/map_queue_stack.rst b/Documentation/bpf/map_queue_stack.rst new file mode 100644 index 000000000000..f20e31a647b9 --- /dev/null +++ b/Documentation/bpf/map_queue_stack.rst @@ -0,0 +1,122 @@ +.. SPDX-License-Identifier: GPL-2.0-only +.. Copyright (C) 2022 Red Hat, Inc. + +========================================= +BPF_MAP_TYPE_QUEUE and BPF_MAP_TYPE_STACK +========================================= + +.. note:: + - ``BPF_MAP_TYPE_QUEUE`` and ``BPF_MAP_TYPE_STACK`` were introduced + in kernel version 4.20 + +``BPF_MAP_TYPE_QUEUE`` provides FIFO storage and ``BPF_MAP_TYPE_STACK`` +provides LIFO storage for BPF programs. These maps support peek, pop and +push operations that are exposed to BPF programs through the respective +helpers. These operations are exposed to userspace applications using +the existing ``bpf`` syscall in the following way: + +- ``BPF_MAP_LOOKUP_ELEM`` -> peek +- ``BPF_MAP_LOOKUP_AND_DELETE_ELEM`` -> pop +- ``BPF_MAP_UPDATE_ELEM`` -> push + +``BPF_MAP_TYPE_QUEUE`` and ``BPF_MAP_TYPE_STACK`` do not support +``BPF_F_NO_PREALLOC``. + +Usage +===== + +Kernel BPF +---------- + +.. c:function:: + long bpf_map_push_elem(struct bpf_map *map, const void *value, u64 flags) + +An element ``value`` can be added to a queue or stack using the +``bpf_map_push_elem`` helper. The ``flags`` parameter must be set to +``BPF_ANY`` or ``BPF_EXIST``. If ``flags`` is set to ``BPF_EXIST`` then, +when the queue or stack is full, the oldest element will be removed to +make room for ``value`` to be added. Returns ``0`` on success, or +negative error in case of failure. + +.. c:function:: + long bpf_map_peek_elem(struct bpf_map *map, void *value) + +This helper fetches an element ``value`` from a queue or stack without +removing it. Returns ``0`` on success, or negative error in case of +failure. + +.. c:function:: + long bpf_map_pop_elem(struct bpf_map *map, void *value) + +This helper removes an element into ``value`` from a queue or +stack. Returns ``0`` on success, or negative error in case of failure. + + +Userspace +--------- + +.. c:function:: + int bpf_map_update_elem (int fd, const void *key, const void *value, __u64 flags) + +A userspace program can push ``value`` onto a queue or stack using libbpf's +``bpf_map_update_elem`` function. The ``key`` parameter must be set to +``NULL`` and ``flags`` must be set to ``BPF_ANY`` or ``BPF_EXIST``, with the +same semantics as the ``bpf_map_push_elem`` kernel helper. Returns ``0`` on +success, or negative error in case of failure. + +.. c:function:: + int bpf_map_lookup_elem (int fd, const void *key, void *value) + +A userspace program can peek at the ``value`` at the head of a queue or stack +using the libbpf ``bpf_map_lookup_elem`` function. The ``key`` parameter must be +set to ``NULL``. Returns ``0`` on success, or negative error in case of +failure. + +.. c:function:: + int bpf_map_lookup_and_delete_elem (int fd, const void *key, void *value) + +A userspace program can pop a ``value`` from the head of a queue or stack using +the libbpf ``bpf_map_lookup_and_delete_elem`` function. The ``key`` parameter +must be set to ``NULL``. Returns ``0`` on success, or negative error in case of +failure. + +Examples +======== + +Kernel BPF +---------- + +This snippet shows how to declare a queue in a BPF program: + +.. code-block:: c + + struct { + __uint(type, BPF_MAP_TYPE_QUEUE); + __type(value, __u32); + __uint(max_entries, 10); + } queue SEC(".maps"); + + +Userspace +--------- + +This snippet shows how to use libbpf's low-level API to create a queue from +userspace: + +.. code-block:: c + + int create_queue() + { + return bpf_map_create(BPF_MAP_TYPE_QUEUE, + "sample_queue", /* name */ + 0, /* key size, must be zero */ + sizeof(__u32), /* value size */ + 10, /* max entries */ + NULL); /* create options */ + } + + +References +========== + +https://lwn.net/ml/netdev/153986858555.9127.14517764371945179514.stgit@kernel/ From 1cfa97b30c5ad1f4b9b97e67c10d28d677f258ea Mon Sep 17 00:00:00 2001 From: Dave Tucker Date: Wed, 9 Nov 2022 17:46:04 +0000 Subject: [PATCH 0855/2002] bpf, docs: Document BPF_MAP_TYPE_ARRAY Add documentation for the BPF_MAP_TYPE_ARRAY including kernel version introduced, usage and examples. Also document BPF_MAP_TYPE_PERCPU_ARRAY which is similar. Co-developed-by: Donald Hunter Signed-off-by: Dave Tucker Signed-off-by: Donald Hunter Signed-off-by: Andrii Nakryiko Reviewed-by: Maryam Tahhan Reviewed-by: Bagas Sanjaya Link: https://lore.kernel.org/bpf/20221109174604.31673-2-donald.hunter@gmail.com --- Documentation/bpf/map_array.rst | 250 ++++++++++++++++++++++++++++++++ 1 file changed, 250 insertions(+) create mode 100644 Documentation/bpf/map_array.rst diff --git a/Documentation/bpf/map_array.rst b/Documentation/bpf/map_array.rst new file mode 100644 index 000000000000..b2cceb6c696b --- /dev/null +++ b/Documentation/bpf/map_array.rst @@ -0,0 +1,250 @@ +.. SPDX-License-Identifier: GPL-2.0-only +.. Copyright (C) 2022 Red Hat, Inc. + +================================================ +BPF_MAP_TYPE_ARRAY and BPF_MAP_TYPE_PERCPU_ARRAY +================================================ + +.. note:: + - ``BPF_MAP_TYPE_ARRAY`` was introduced in kernel version 3.19 + - ``BPF_MAP_TYPE_PERCPU_ARRAY`` was introduced in version 4.6 + +``BPF_MAP_TYPE_ARRAY`` and ``BPF_MAP_TYPE_PERCPU_ARRAY`` provide generic array +storage. The key type is an unsigned 32-bit integer (4 bytes) and the map is +of constant size. The size of the array is defined in ``max_entries`` at +creation time. All array elements are pre-allocated and zero initialized when +created. ``BPF_MAP_TYPE_PERCPU_ARRAY`` uses a different memory region for each +CPU whereas ``BPF_MAP_TYPE_ARRAY`` uses the same memory region. The value +stored can be of any size, however, all array elements are aligned to 8 +bytes. + +Since kernel 5.5, memory mapping may be enabled for ``BPF_MAP_TYPE_ARRAY`` by +setting the flag ``BPF_F_MMAPABLE``. The map definition is page-aligned and +starts on the first page. Sufficient page-sized and page-aligned blocks of +memory are allocated to store all array values, starting on the second page, +which in some cases will result in over-allocation of memory. The benefit of +using this is increased performance and ease of use since userspace programs +would not be required to use helper functions to access and mutate data. + +Usage +===== + +Kernel BPF +---------- + +.. c:function:: + void *bpf_map_lookup_elem(struct bpf_map *map, const void *key) + +Array elements can be retrieved using the ``bpf_map_lookup_elem()`` helper. +This helper returns a pointer into the array element, so to avoid data races +with userspace reading the value, the user must use primitives like +``__sync_fetch_and_add()`` when updating the value in-place. + +.. c:function:: + long bpf_map_update_elem(struct bpf_map *map, const void *key, const void *value, u64 flags) + +Array elements can be updated using the ``bpf_map_update_elem()`` helper. + +``bpf_map_update_elem()`` returns 0 on success, or negative error in case of +failure. + +Since the array is of constant size, ``bpf_map_delete_elem()`` is not supported. +To clear an array element, you may use ``bpf_map_update_elem()`` to insert a +zero value to that index. + +Per CPU Array +~~~~~~~~~~~~~ + +Values stored in ``BPF_MAP_TYPE_ARRAY`` can be accessed by multiple programs +across different CPUs. To restrict storage to a single CPU, you may use a +``BPF_MAP_TYPE_PERCPU_ARRAY``. + +When using a ``BPF_MAP_TYPE_PERCPU_ARRAY`` the ``bpf_map_update_elem()`` and +``bpf_map_lookup_elem()`` helpers automatically access the slot for the current +CPU. + +.. c:function:: + void *bpf_map_lookup_percpu_elem(struct bpf_map *map, const void *key, u32 cpu) + +The ``bpf_map_lookup_percpu_elem()`` helper can be used to lookup the array +value for a specific CPU. Returns value on success , or ``NULL`` if no entry was +found or ``cpu`` is invalid. + +Concurrency +----------- + +Since kernel version 5.1, the BPF infrastructure provides ``struct bpf_spin_lock`` +to synchronize access. + +Userspace +--------- + +Access from userspace uses libbpf APIs with the same names as above, with +the map identified by its ``fd``. + +Examples +======== + +Please see the ``tools/testing/selftests/bpf`` directory for functional +examples. The code samples below demonstrate API usage. + +Kernel BPF +---------- + +This snippet shows how to declare an array in a BPF program. + +.. code-block:: c + + struct { + __uint(type, BPF_MAP_TYPE_ARRAY); + __type(key, u32); + __type(value, long); + __uint(max_entries, 256); + } my_map SEC(".maps"); + + +This example BPF program shows how to access an array element. + +.. code-block:: c + + int bpf_prog(struct __sk_buff *skb) + { + struct iphdr ip; + int index; + long *value; + + if (bpf_skb_load_bytes(skb, ETH_HLEN, &ip, sizeof(ip)) < 0) + return 0; + + index = ip.protocol; + value = bpf_map_lookup_elem(&my_map, &index); + if (value) + __sync_fetch_and_add(&value, skb->len); + + return 0; + } + +Userspace +--------- + +BPF_MAP_TYPE_ARRAY +~~~~~~~~~~~~~~~~~~ + +This snippet shows how to create an array, using ``bpf_map_create_opts`` to +set flags. + +.. code-block:: c + + #include + #include + + int create_array() + { + int fd; + LIBBPF_OPTS(bpf_map_create_opts, opts, .map_flags = BPF_F_MMAPABLE); + + fd = bpf_map_create(BPF_MAP_TYPE_ARRAY, + "example_array", /* name */ + sizeof(__u32), /* key size */ + sizeof(long), /* value size */ + 256, /* max entries */ + &opts); /* create opts */ + return fd; + } + +This snippet shows how to initialize the elements of an array. + +.. code-block:: c + + int initialize_array(int fd) + { + __u32 i; + long value; + int ret; + + for (i = 0; i < 256; i++) { + value = i; + ret = bpf_map_update_elem(fd, &i, &value, BPF_ANY); + if (ret < 0) + return ret; + } + + return ret; + } + +This snippet shows how to retrieve an element value from an array. + +.. code-block:: c + + int lookup(int fd) + { + __u32 index = 42; + long value; + int ret; + + ret = bpf_map_lookup_elem(fd, &index, &value); + if (ret < 0) + return ret; + + /* use value here */ + assert(value == 42); + + return ret; + } + +BPF_MAP_TYPE_PERCPU_ARRAY +~~~~~~~~~~~~~~~~~~~~~~~~~ + +This snippet shows how to initialize the elements of a per CPU array. + +.. code-block:: c + + int initialize_array(int fd) + { + int ncpus = libbpf_num_possible_cpus(); + long values[ncpus]; + __u32 i, j; + int ret; + + for (i = 0; i < 256 ; i++) { + for (j = 0; j < ncpus; j++) + values[j] = i; + ret = bpf_map_update_elem(fd, &i, &values, BPF_ANY); + if (ret < 0) + return ret; + } + + return ret; + } + +This snippet shows how to access the per CPU elements of an array value. + +.. code-block:: c + + int lookup(int fd) + { + int ncpus = libbpf_num_possible_cpus(); + __u32 index = 42, j; + long values[ncpus]; + int ret; + + ret = bpf_map_lookup_elem(fd, &index, &values); + if (ret < 0) + return ret; + + for (j = 0; j < ncpus; j++) { + /* Use per CPU value here */ + assert(values[j] == 42); + } + + return ret; + } + +Semantics +========= + +As shown in the example above, when accessing a ``BPF_MAP_TYPE_PERCPU_ARRAY`` +in userspace, each value is an array with ``ncpus`` elements. + +When calling ``bpf_map_update_elem()`` the flag ``BPF_NOEXIST`` can not be used +for these maps. From e4c9cf0ce8c413c2030e8fb215551d7e0582ee7b Mon Sep 17 00:00:00 2001 From: Yang Jihong Date: Fri, 11 Nov 2022 11:08:36 +0800 Subject: [PATCH 0856/2002] selftests/bpf: Fix xdp_synproxy compilation failure in 32-bit arch xdp_synproxy fails to be compiled in the 32-bit arch, log is as follows: xdp_synproxy.c: In function 'parse_options': xdp_synproxy.c:175:36: error: left shift count >= width of type [-Werror=shift-count-overflow] 175 | *tcpipopts = (mss6 << 32) | (ttl << 24) | (wscale << 16) | mss4; | ^~ xdp_synproxy.c: In function 'syncookie_open_bpf_maps': xdp_synproxy.c:289:28: error: cast from pointer to integer of different size [-Werror=pointer-to-int-cast] 289 | .map_ids = (__u64)map_ids, | ^ Fix it. Fixes: fb5cd0ce70d4 ("selftests/bpf: Add selftests for raw syncookie helpers") Signed-off-by: Yang Jihong Signed-off-by: Andrii Nakryiko Acked-by: Yonghong Song Link: https://lore.kernel.org/bpf/20221111030836.37632-1-yangjihong1@huawei.com --- tools/testing/selftests/bpf/xdp_synproxy.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tools/testing/selftests/bpf/xdp_synproxy.c b/tools/testing/selftests/bpf/xdp_synproxy.c index ff35320d2be9..410a1385a01d 100644 --- a/tools/testing/selftests/bpf/xdp_synproxy.c +++ b/tools/testing/selftests/bpf/xdp_synproxy.c @@ -104,7 +104,8 @@ static void parse_options(int argc, char *argv[], unsigned int *ifindex, __u32 * { "tc", no_argument, NULL, 'c' }, { NULL, 0, NULL, 0 }, }; - unsigned long mss4, mss6, wscale, ttl; + unsigned long mss4, wscale, ttl; + unsigned long long mss6; unsigned int tcpipopts_mask = 0; if (argc < 2) @@ -286,7 +287,7 @@ static int syncookie_open_bpf_maps(__u32 prog_id, int *values_map_fd, int *ports prog_info = (struct bpf_prog_info) { .nr_map_ids = 8, - .map_ids = (__u64)map_ids, + .map_ids = (__u64)(unsigned long)map_ids, }; info_len = sizeof(prog_info); From 9bb053490f1a5a0914eb9f7b4116a0e4a95d4f8e Mon Sep 17 00:00:00 2001 From: Martin KaFai Lau Date: Mon, 7 Nov 2022 15:04:18 -0800 Subject: [PATCH 0857/2002] bpf: Add hwtstamp field for the sockops prog The bpf-tc prog has already been able to access the skb_hwtstamps(skb)->hwtstamp. This patch extends the same hwtstamp access to the sockops prog. In sockops, the skb is also available to the bpf prog during the BPF_SOCK_OPS_PARSE_HDR_OPT_CB event. There is a use case that the hwtstamp will be useful to the sockops prog to better measure the one-way-delay when the sender has put the tx timestamp in the tcp header option. Signed-off-by: Martin KaFai Lau Signed-off-by: Andrii Nakryiko Acked-by: Yonghong Song Link: https://lore.kernel.org/bpf/20221107230420.4192307-2-martin.lau@linux.dev --- include/uapi/linux/bpf.h | 1 + net/core/filter.c | 39 +++++++++++++++++++++++++++------- tools/include/uapi/linux/bpf.h | 1 + 3 files changed, 33 insertions(+), 8 deletions(-) diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h index 94659f6b3395..fb4c911d2a03 100644 --- a/include/uapi/linux/bpf.h +++ b/include/uapi/linux/bpf.h @@ -6445,6 +6445,7 @@ struct bpf_sock_ops { * the outgoing header has not * been written yet. */ + __u64 skb_hwtstamp; }; /* Definitions for bpf_sock_ops_cb_flags */ diff --git a/net/core/filter.c b/net/core/filter.c index cb3b635e35be..cd667cdbdb26 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -8925,6 +8925,10 @@ static bool sock_ops_is_valid_access(int off, int size, bpf_ctx_record_field_size(info, size_default); return bpf_ctx_narrow_access_ok(off, size, size_default); + case offsetof(struct bpf_sock_ops, skb_hwtstamp): + if (size != sizeof(__u64)) + return false; + break; default: if (size != size_default) return false; @@ -9108,21 +9112,21 @@ static struct bpf_insn *bpf_convert_tstamp_type_read(const struct bpf_insn *si, return insn; } -static struct bpf_insn *bpf_convert_shinfo_access(const struct bpf_insn *si, +static struct bpf_insn *bpf_convert_shinfo_access(__u8 dst_reg, __u8 skb_reg, struct bpf_insn *insn) { /* si->dst_reg = skb_shinfo(SKB); */ #ifdef NET_SKBUFF_DATA_USES_OFFSET *insn++ = BPF_LDX_MEM(BPF_FIELD_SIZEOF(struct sk_buff, end), - BPF_REG_AX, si->src_reg, + BPF_REG_AX, skb_reg, offsetof(struct sk_buff, end)); *insn++ = BPF_LDX_MEM(BPF_FIELD_SIZEOF(struct sk_buff, head), - si->dst_reg, si->src_reg, + dst_reg, skb_reg, offsetof(struct sk_buff, head)); - *insn++ = BPF_ALU64_REG(BPF_ADD, si->dst_reg, BPF_REG_AX); + *insn++ = BPF_ALU64_REG(BPF_ADD, dst_reg, BPF_REG_AX); #else *insn++ = BPF_LDX_MEM(BPF_FIELD_SIZEOF(struct sk_buff, end), - si->dst_reg, si->src_reg, + dst_reg, skb_reg, offsetof(struct sk_buff, end)); #endif @@ -9515,7 +9519,7 @@ static u32 bpf_convert_ctx_access(enum bpf_access_type type, break; case offsetof(struct __sk_buff, gso_segs): - insn = bpf_convert_shinfo_access(si, insn); + insn = bpf_convert_shinfo_access(si->dst_reg, si->src_reg, insn); *insn++ = BPF_LDX_MEM(BPF_FIELD_SIZEOF(struct skb_shared_info, gso_segs), si->dst_reg, si->dst_reg, bpf_target_off(struct skb_shared_info, @@ -9523,7 +9527,7 @@ static u32 bpf_convert_ctx_access(enum bpf_access_type type, target_size)); break; case offsetof(struct __sk_buff, gso_size): - insn = bpf_convert_shinfo_access(si, insn); + insn = bpf_convert_shinfo_access(si->dst_reg, si->src_reg, insn); *insn++ = BPF_LDX_MEM(BPF_FIELD_SIZEOF(struct skb_shared_info, gso_size), si->dst_reg, si->dst_reg, bpf_target_off(struct skb_shared_info, @@ -9550,7 +9554,7 @@ static u32 bpf_convert_ctx_access(enum bpf_access_type type, BUILD_BUG_ON(sizeof_field(struct skb_shared_hwtstamps, hwtstamp) != 8); BUILD_BUG_ON(offsetof(struct skb_shared_hwtstamps, hwtstamp) != 0); - insn = bpf_convert_shinfo_access(si, insn); + insn = bpf_convert_shinfo_access(si->dst_reg, si->src_reg, insn); *insn++ = BPF_LDX_MEM(BPF_DW, si->dst_reg, si->dst_reg, bpf_target_off(struct skb_shared_info, @@ -10400,6 +10404,25 @@ static u32 sock_ops_convert_ctx_access(enum bpf_access_type type, tcp_flags), si->dst_reg, si->dst_reg, off); break; + case offsetof(struct bpf_sock_ops, skb_hwtstamp): { + struct bpf_insn *jmp_on_null_skb; + + *insn++ = BPF_LDX_MEM(BPF_FIELD_SIZEOF(struct bpf_sock_ops_kern, + skb), + si->dst_reg, si->src_reg, + offsetof(struct bpf_sock_ops_kern, + skb)); + /* Reserve one insn to test skb == NULL */ + jmp_on_null_skb = insn++; + insn = bpf_convert_shinfo_access(si->dst_reg, si->dst_reg, insn); + *insn++ = BPF_LDX_MEM(BPF_DW, si->dst_reg, si->dst_reg, + bpf_target_off(struct skb_shared_info, + hwtstamps, 8, + target_size)); + *jmp_on_null_skb = BPF_JMP_IMM(BPF_JEQ, si->dst_reg, 0, + insn - jmp_on_null_skb - 1); + break; + } } return insn - insn_buf; } diff --git a/tools/include/uapi/linux/bpf.h b/tools/include/uapi/linux/bpf.h index 94659f6b3395..fb4c911d2a03 100644 --- a/tools/include/uapi/linux/bpf.h +++ b/tools/include/uapi/linux/bpf.h @@ -6445,6 +6445,7 @@ struct bpf_sock_ops { * the outgoing header has not * been written yet. */ + __u64 skb_hwtstamp; }; /* Definitions for bpf_sock_ops_cb_flags */ From 52929912d7bda040b43538e8d88e8d231b76eb4e Mon Sep 17 00:00:00 2001 From: Martin KaFai Lau Date: Mon, 7 Nov 2022 15:04:19 -0800 Subject: [PATCH 0858/2002] selftests/bpf: Fix incorrect ASSERT in the tcp_hdr_options test This patch fixes the incorrect ASSERT test in tcp_hdr_options during the CHECK to ASSERT macro cleanup. Fixes: 3082f8cd4ba3 ("selftests/bpf: Convert tcp_hdr_options test to ASSERT_* macros") Signed-off-by: Martin KaFai Lau Signed-off-by: Andrii Nakryiko Acked-by: Wang Yufen Acked-by: Yonghong Song Link: https://lore.kernel.org/bpf/20221107230420.4192307-3-martin.lau@linux.dev --- tools/testing/selftests/bpf/prog_tests/tcp_hdr_options.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/testing/selftests/bpf/prog_tests/tcp_hdr_options.c b/tools/testing/selftests/bpf/prog_tests/tcp_hdr_options.c index 617bbce6ef8f..57191773572a 100644 --- a/tools/testing/selftests/bpf/prog_tests/tcp_hdr_options.c +++ b/tools/testing/selftests/bpf/prog_tests/tcp_hdr_options.c @@ -485,7 +485,7 @@ static void misc(void) goto check_linum; ret = read(sk_fds.passive_fd, recv_msg, sizeof(recv_msg)); - if (ASSERT_EQ(ret, sizeof(send_msg), "read(msg)")) + if (!ASSERT_EQ(ret, sizeof(send_msg), "read(msg)")) goto check_linum; } @@ -539,7 +539,7 @@ void test_tcp_hdr_options(void) goto skel_destroy; cg_fd = test__join_cgroup(CG_NAME); - if (ASSERT_GE(cg_fd, 0, "join_cgroup")) + if (!ASSERT_GE(cg_fd, 0, "join_cgroup")) goto skel_destroy; for (i = 0; i < ARRAY_SIZE(tests); i++) { From 8cac7a59b252732d8427ebc29c3fc6c36ff653e1 Mon Sep 17 00:00:00 2001 From: Martin KaFai Lau Date: Mon, 7 Nov 2022 15:04:20 -0800 Subject: [PATCH 0859/2002] selftests/bpf: Test skops->skb_hwtstamp This patch tests reading the skops->skb_hwtstamp field. A local test was also done such that the shinfo hwtstamp was temporary set to a non zero value in the kernel bpf_skops_parse_hdr() and the same value can be read by the skops test. An adjustment is needed to the btf_dump selftest because the changes in the 'struct bpf_sock_ops'. Signed-off-by: Martin KaFai Lau Signed-off-by: Andrii Nakryiko Acked-by: Yonghong Song Link: https://lore.kernel.org/bpf/20221107230420.4192307-4-martin.lau@linux.dev --- tools/testing/selftests/bpf/prog_tests/btf_dump.c | 4 ++-- tools/testing/selftests/bpf/prog_tests/tcp_hdr_options.c | 2 ++ tools/testing/selftests/bpf/progs/test_misc_tcp_hdr_options.c | 4 ++++ 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/tools/testing/selftests/bpf/prog_tests/btf_dump.c b/tools/testing/selftests/bpf/prog_tests/btf_dump.c index 24da335482d4..0ba2e8b9c6ac 100644 --- a/tools/testing/selftests/bpf/prog_tests/btf_dump.c +++ b/tools/testing/selftests/bpf/prog_tests/btf_dump.c @@ -791,11 +791,11 @@ static void test_btf_dump_struct_data(struct btf *btf, struct btf_dump *d, TEST_BTF_DUMP_DATA_OVER(btf, d, "struct", str, struct bpf_sock_ops, sizeof(struct bpf_sock_ops) - 1, "(struct bpf_sock_ops){\n\t.op = (__u32)1,\n", - { .op = 1, .skb_tcp_flags = 2}); + { .op = 1, .skb_hwtstamp = 2}); TEST_BTF_DUMP_DATA_OVER(btf, d, "struct", str, struct bpf_sock_ops, sizeof(struct bpf_sock_ops) - 1, "(struct bpf_sock_ops){\n\t.op = (__u32)1,\n", - { .op = 1, .skb_tcp_flags = 0}); + { .op = 1, .skb_hwtstamp = 0}); } static void test_btf_dump_var_data(struct btf *btf, struct btf_dump *d, diff --git a/tools/testing/selftests/bpf/prog_tests/tcp_hdr_options.c b/tools/testing/selftests/bpf/prog_tests/tcp_hdr_options.c index 57191773572a..5cf85d0f9827 100644 --- a/tools/testing/selftests/bpf/prog_tests/tcp_hdr_options.c +++ b/tools/testing/selftests/bpf/prog_tests/tcp_hdr_options.c @@ -505,6 +505,8 @@ static void misc(void) ASSERT_EQ(misc_skel->bss->nr_fin, 1, "unexpected nr_fin"); + ASSERT_EQ(misc_skel->bss->nr_hwtstamp, 0, "nr_hwtstamp"); + check_linum: ASSERT_FALSE(check_error_linum(&sk_fds), "check_error_linum"); sk_fds_close(&sk_fds); diff --git a/tools/testing/selftests/bpf/progs/test_misc_tcp_hdr_options.c b/tools/testing/selftests/bpf/progs/test_misc_tcp_hdr_options.c index 2c121c5d66a7..d487153a839d 100644 --- a/tools/testing/selftests/bpf/progs/test_misc_tcp_hdr_options.c +++ b/tools/testing/selftests/bpf/progs/test_misc_tcp_hdr_options.c @@ -27,6 +27,7 @@ unsigned int nr_pure_ack = 0; unsigned int nr_data = 0; unsigned int nr_syn = 0; unsigned int nr_fin = 0; +unsigned int nr_hwtstamp = 0; /* Check the header received from the active side */ static int __check_active_hdr_in(struct bpf_sock_ops *skops, bool check_syn) @@ -146,6 +147,9 @@ static int check_active_hdr_in(struct bpf_sock_ops *skops) if (th->ack && !th->fin && tcp_hdrlen(th) == skops->skb_len) nr_pure_ack++; + if (skops->skb_hwtstamp) + nr_hwtstamp++; + return CG_OK; } From eb6af4ceda2d885416d8382f096030d39896aafc Mon Sep 17 00:00:00 2001 From: Andrii Nakryiko Date: Fri, 11 Nov 2022 10:12:42 -0800 Subject: [PATCH 0860/2002] selftests/bpf: fix veristat's singular file-or-prog filter Fix the bug of filtering out filename too early, before we know the program name, if using unified file-or-prog filter (i.e., -f ). Because we try to filter BPF object file early without opening and parsing it, if any_glob (file-or-prog) filter is used we have to accept any filename just to get program name, which might match any_glob. Fixes: 10b1b3f3e56a ("selftests/bpf: consolidate and improve file/prog filtering in veristat") Signed-off-by: Andrii Nakryiko Acked-by: Stanislav Fomichev Link: https://lore.kernel.org/r/20221111181242.2101192-1-andrii@kernel.org Signed-off-by: Martin KaFai Lau --- tools/testing/selftests/bpf/veristat.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/tools/testing/selftests/bpf/veristat.c b/tools/testing/selftests/bpf/veristat.c index 9e3811ab4866..f961b49b8ef4 100644 --- a/tools/testing/selftests/bpf/veristat.c +++ b/tools/testing/selftests/bpf/veristat.c @@ -367,7 +367,13 @@ static bool should_process_file_prog(const char *filename, const char *prog_name if (f->any_glob) { if (glob_matches(filename, f->any_glob)) return true; - if (prog_name && glob_matches(prog_name, f->any_glob)) + /* If we don't know program name yet, any_glob filter + * has to assume that current BPF object file might be + * relevant; we'll check again later on after opening + * BPF object file, at which point program name will + * be known finally. + */ + if (!prog_name || glob_matches(prog_name, f->any_glob)) return true; } else { if (f->file_glob && !glob_matches(filename, f->file_glob)) From 354259fa73e2aac92ae5e19522adb69a92c15b49 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Wed, 9 Nov 2022 09:57:58 +0000 Subject: [PATCH 0861/2002] net: remove skb->vlan_present skb->vlan_present seems redundant. We can instead derive it from this boolean expression: vlan_present = skb->vlan_proto != 0 || skb->vlan_tci != 0 Add a new union, to access both fields in a single load/store when possible. union { u32 vlan_all; struct { __be16 vlan_proto; __u16 vlan_tci; }; }; This allows following patch to remove a conditional test in GRO stack. Note: We move remcsum_offload to keep TC_AT_INGRESS_MASK and SKB_MONO_DELIVERY_TIME_MASK unchanged. Signed-off-by: Eric Dumazet Acked-by: Yonghong Song Acked-by: Martin KaFai Lau Signed-off-by: Jakub Kicinski --- arch/sparc/net/bpf_jit_comp_32.c | 10 ++++----- .../ethernet/marvell/octeontx2/nic/otx2_pf.c | 2 +- include/linux/if_vlan.h | 9 +++----- include/linux/skbuff.h | 18 ++++++++------- lib/test_bpf.c | 1 - net/core/filter.c | 22 +++++++++---------- 6 files changed, 29 insertions(+), 33 deletions(-) diff --git a/arch/sparc/net/bpf_jit_comp_32.c b/arch/sparc/net/bpf_jit_comp_32.c index b1dbf2fa8c0a..a74e5004c6c8 100644 --- a/arch/sparc/net/bpf_jit_comp_32.c +++ b/arch/sparc/net/bpf_jit_comp_32.c @@ -555,11 +555,11 @@ void bpf_jit_compile(struct bpf_prog *fp) emit_skb_load16(vlan_tci, r_A); break; case BPF_ANC | SKF_AD_VLAN_TAG_PRESENT: - __emit_skb_load8(__pkt_vlan_present_offset, r_A); - if (PKT_VLAN_PRESENT_BIT) - emit_alu_K(SRL, PKT_VLAN_PRESENT_BIT); - if (PKT_VLAN_PRESENT_BIT < 7) - emit_andi(r_A, 1, r_A); + emit_skb_load32(vlan_all, r_A); + emit_cmpi(r_A, 0); + emit_branch_off(BE, 12); + emit_nop(); + emit_loadimm(1, r_A); break; case BPF_LD | BPF_W | BPF_LEN: emit_skb_load32(len, r_A); diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c index 303930499a4c..c1ea60bc2630 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c @@ -1973,7 +1973,7 @@ static u16 otx2_select_queue(struct net_device *netdev, struct sk_buff *skb, #endif #ifdef CONFIG_DCB - if (!skb->vlan_present) + if (!skb_vlan_tag_present(skb)) goto pick_tx; vlan_prio = skb->vlan_tci >> 13; diff --git a/include/linux/if_vlan.h b/include/linux/if_vlan.h index e00c4ee81ff7..6864b89ef868 100644 --- a/include/linux/if_vlan.h +++ b/include/linux/if_vlan.h @@ -76,7 +76,7 @@ static inline bool is_vlan_dev(const struct net_device *dev) return dev->priv_flags & IFF_802_1Q_VLAN; } -#define skb_vlan_tag_present(__skb) ((__skb)->vlan_present) +#define skb_vlan_tag_present(__skb) (!!(__skb)->vlan_all) #define skb_vlan_tag_get(__skb) ((__skb)->vlan_tci) #define skb_vlan_tag_get_id(__skb) ((__skb)->vlan_tci & VLAN_VID_MASK) #define skb_vlan_tag_get_cfi(__skb) (!!((__skb)->vlan_tci & VLAN_CFI_MASK)) @@ -471,7 +471,7 @@ static inline struct sk_buff *vlan_insert_tag_set_proto(struct sk_buff *skb, */ static inline void __vlan_hwaccel_clear_tag(struct sk_buff *skb) { - skb->vlan_present = 0; + skb->vlan_all = 0; } /** @@ -483,9 +483,7 @@ static inline void __vlan_hwaccel_clear_tag(struct sk_buff *skb) */ static inline void __vlan_hwaccel_copy_tag(struct sk_buff *dst, const struct sk_buff *src) { - dst->vlan_present = src->vlan_present; - dst->vlan_proto = src->vlan_proto; - dst->vlan_tci = src->vlan_tci; + dst->vlan_all = src->vlan_all; } /* @@ -519,7 +517,6 @@ static inline void __vlan_hwaccel_put_tag(struct sk_buff *skb, { skb->vlan_proto = vlan_proto; skb->vlan_tci = vlan_tci; - skb->vlan_present = 1; } /** diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index 59c9fd55699d..4e464a27adaf 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -818,7 +818,7 @@ typedef unsigned char *sk_buff_data_t; * @mark: Generic packet mark * @reserved_tailroom: (aka @mark) number of bytes of free space available * at the tail of an sk_buff - * @vlan_present: VLAN tag is present + * @vlan_all: vlan fields (proto & tci) * @vlan_proto: vlan encapsulation protocol * @vlan_tci: vlan tag control information * @inner_protocol: Protocol (encapsulation) @@ -951,7 +951,7 @@ struct sk_buff { /* private: */ __u8 __pkt_vlan_present_offset[0]; /* public: */ - __u8 vlan_present:1; /* See PKT_VLAN_PRESENT_BIT */ + __u8 remcsum_offload:1; __u8 csum_complete_sw:1; __u8 csum_level:2; __u8 dst_pending_confirm:1; @@ -966,7 +966,6 @@ struct sk_buff { __u8 ipvs_property:1; __u8 inner_protocol_type:1; - __u8 remcsum_offload:1; #ifdef CONFIG_NET_SWITCHDEV __u8 offload_fwd_mark:1; __u8 offload_l3_fwd_mark:1; @@ -999,8 +998,13 @@ struct sk_buff { __u32 priority; int skb_iif; __u32 hash; - __be16 vlan_proto; - __u16 vlan_tci; + union { + u32 vlan_all; + struct { + __be16 vlan_proto; + __u16 vlan_tci; + }; + }; #if defined(CONFIG_NET_RX_BUSY_POLL) || defined(CONFIG_XPS) union { unsigned int napi_id; @@ -1059,15 +1063,13 @@ struct sk_buff { #endif #define PKT_TYPE_OFFSET offsetof(struct sk_buff, __pkt_type_offset) -/* if you move pkt_vlan_present, tc_at_ingress, or mono_delivery_time +/* if you move tc_at_ingress or mono_delivery_time * around, you also must adapt these constants. */ #ifdef __BIG_ENDIAN_BITFIELD -#define PKT_VLAN_PRESENT_BIT 7 #define TC_AT_INGRESS_MASK (1 << 0) #define SKB_MONO_DELIVERY_TIME_MASK (1 << 2) #else -#define PKT_VLAN_PRESENT_BIT 0 #define TC_AT_INGRESS_MASK (1 << 7) #define SKB_MONO_DELIVERY_TIME_MASK (1 << 5) #endif diff --git a/lib/test_bpf.c b/lib/test_bpf.c index 5820704165a6..ade9ac672adb 100644 --- a/lib/test_bpf.c +++ b/lib/test_bpf.c @@ -14346,7 +14346,6 @@ static struct sk_buff *populate_skb(char *buf, int size) skb->hash = SKB_HASH; skb->queue_mapping = SKB_QUEUE_MAP; skb->vlan_tci = SKB_VLAN_TCI; - skb->vlan_present = SKB_VLAN_PRESENT; skb->vlan_proto = htons(ETH_P_IP); dev_net_set(&dev, &init_net); skb->dev = &dev; diff --git a/net/core/filter.c b/net/core/filter.c index bb0136e7a8e4..358d5e70671a 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -325,11 +325,11 @@ static u32 convert_skb_access(int skb_field, int dst_reg, int src_reg, offsetof(struct sk_buff, vlan_tci)); break; case SKF_AD_VLAN_TAG_PRESENT: - *insn++ = BPF_LDX_MEM(BPF_B, dst_reg, src_reg, PKT_VLAN_PRESENT_OFFSET); - if (PKT_VLAN_PRESENT_BIT) - *insn++ = BPF_ALU32_IMM(BPF_RSH, dst_reg, PKT_VLAN_PRESENT_BIT); - if (PKT_VLAN_PRESENT_BIT < 7) - *insn++ = BPF_ALU32_IMM(BPF_AND, dst_reg, 1); + BUILD_BUG_ON(sizeof_field(struct sk_buff, vlan_all) != 4); + *insn++ = BPF_LDX_MEM(BPF_W, dst_reg, src_reg, + offsetof(struct sk_buff, vlan_all)); + *insn++ = BPF_JMP_IMM(BPF_JEQ, dst_reg, 0, 1); + *insn++ = BPF_ALU32_IMM(BPF_MOV, dst_reg, 1); break; } @@ -9290,13 +9290,11 @@ static u32 bpf_convert_ctx_access(enum bpf_access_type type, break; case offsetof(struct __sk_buff, vlan_present): - *target_size = 1; - *insn++ = BPF_LDX_MEM(BPF_B, si->dst_reg, si->src_reg, - PKT_VLAN_PRESENT_OFFSET); - if (PKT_VLAN_PRESENT_BIT) - *insn++ = BPF_ALU32_IMM(BPF_RSH, si->dst_reg, PKT_VLAN_PRESENT_BIT); - if (PKT_VLAN_PRESENT_BIT < 7) - *insn++ = BPF_ALU32_IMM(BPF_AND, si->dst_reg, 1); + *insn++ = BPF_LDX_MEM(BPF_W, si->dst_reg, si->src_reg, + bpf_target_off(struct sk_buff, + vlan_all, 4, target_size)); + *insn++ = BPF_JMP_IMM(BPF_JEQ, si->dst_reg, 0, 1); + *insn++ = BPF_ALU32_IMM(BPF_MOV, si->dst_reg, 1); break; case offsetof(struct __sk_buff, vlan_tci): From be3ed48683f0d7ed808783fd7d919459b58b5b6b Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Wed, 9 Nov 2022 09:57:59 +0000 Subject: [PATCH 0862/2002] net: gro: no longer use skb_vlan_tag_present() We can remove a conditional test in gro_list_prepare() by comparing vlan_all fields of the two skbs. Notes: While comparing the vlan_proto is not strictly needed, because part of the following compare_ether_header() call, using 32bit word is actually faster than using 16bit values. napi_reuse_skb() makes sure to clear skb->vlan_all, as it already calls __vlan_hwaccel_clear_tag() Signed-off-by: Eric Dumazet Acked-by: Martin KaFai Lau Signed-off-by: Jakub Kicinski --- net/core/gro.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/net/core/gro.c b/net/core/gro.c index 8e0fe85a647d..fd8c6a7e8d3e 100644 --- a/net/core/gro.c +++ b/net/core/gro.c @@ -370,9 +370,7 @@ static void gro_list_prepare(const struct list_head *head, } diffs = (unsigned long)p->dev ^ (unsigned long)skb->dev; - diffs |= skb_vlan_tag_present(p) ^ skb_vlan_tag_present(skb); - if (skb_vlan_tag_present(p)) - diffs |= skb_vlan_tag_get(p) ^ skb_vlan_tag_get(skb); + diffs |= p->vlan_all ^ skb->vlan_all; diffs |= skb_metadata_differs(p, skb); if (maclen == ETH_HLEN) diffs |= compare_ether_header(skb_mac_header(p), From 00df24f19179c8e8c81fffa95c91d5c2eab8964e Mon Sep 17 00:00:00 2001 From: Geliang Tang Date: Thu, 10 Nov 2022 15:23:18 -0800 Subject: [PATCH 0863/2002] mptcp: use msk instead of mptcp_sk Use msk instead of mptcp_sk(sk) in the functions where the variable "msk = mptcp_sk(sk)" has been defined. Reviewed-by: Mat Martineau Signed-off-by: Geliang Tang Signed-off-by: Mat Martineau Signed-off-by: Jakub Kicinski --- net/mptcp/protocol.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/net/mptcp/protocol.c b/net/mptcp/protocol.c index 109eea2c65ff..64d7070de901 100644 --- a/net/mptcp/protocol.c +++ b/net/mptcp/protocol.c @@ -1625,7 +1625,7 @@ static void __mptcp_subflow_push_pending(struct sock *sk, struct sock *ssk) * check for a different subflow usage only after * spooling the first chunk of data */ - xmit_ssk = first ? ssk : mptcp_subflow_get_send(mptcp_sk(sk)); + xmit_ssk = first ? ssk : mptcp_subflow_get_send(msk); if (!xmit_ssk) goto out; if (xmit_ssk != ssk) { @@ -2275,7 +2275,7 @@ bool __mptcp_retransmit_pending_data(struct sock *sk) struct mptcp_data_frag *cur, *rtx_head; struct mptcp_sock *msk = mptcp_sk(sk); - if (__mptcp_check_fallback(mptcp_sk(sk))) + if (__mptcp_check_fallback(msk)) return false; if (tcp_rtx_and_write_queues_empty(sk)) @@ -2949,7 +2949,7 @@ cleanup: sock_hold(sk); pr_debug("msk=%p state=%d", sk, sk->sk_state); - if (mptcp_sk(sk)->token) + if (msk->token) mptcp_event(MPTCP_EVENT_CLOSED, msk, NULL, GFP_KERNEL); if (sk->sk_state == TCP_CLOSE) { @@ -3008,8 +3008,8 @@ static int mptcp_disconnect(struct sock *sk, int flags) mptcp_stop_timer(sk); sk_stop_timer(sk, &sk->sk_timer); - if (mptcp_sk(sk)->token) - mptcp_event(MPTCP_EVENT_CLOSED, mptcp_sk(sk), NULL, GFP_KERNEL); + if (msk->token) + mptcp_event(MPTCP_EVENT_CLOSED, msk, NULL, GFP_KERNEL); /* msk->subflow is still intact, the following will not free the first * subflow From 73a0052a61f98354f39f461e03f1a7e513b84578 Mon Sep 17 00:00:00 2001 From: Geliang Tang Date: Thu, 10 Nov 2022 15:23:19 -0800 Subject: [PATCH 0864/2002] mptcp: change 'first' as a parameter The function mptcp_subflow_process_delegated() uses the input ssk first, while __mptcp_check_push() invokes the packet scheduler first. So this patch adds a new parameter named 'first' for the function __mptcp_subflow_push_pending() to deal with these two cases separately. With this change, the code that invokes the packet scheduler in the function __mptcp_check_push() can be removed, and replaced by invoking __mptcp_subflow_push_pending() directly. Reviewed-by: Mat Martineau Signed-off-by: Geliang Tang Signed-off-by: Mat Martineau Signed-off-by: Jakub Kicinski --- net/mptcp/protocol.c | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/net/mptcp/protocol.c b/net/mptcp/protocol.c index 64d7070de901..5a344788f843 100644 --- a/net/mptcp/protocol.c +++ b/net/mptcp/protocol.c @@ -1602,7 +1602,7 @@ out: __mptcp_check_send_data_fin(sk); } -static void __mptcp_subflow_push_pending(struct sock *sk, struct sock *ssk) +static void __mptcp_subflow_push_pending(struct sock *sk, struct sock *ssk, bool first) { struct mptcp_sock *msk = mptcp_sk(sk); struct mptcp_sendmsg_info info = { @@ -1611,7 +1611,6 @@ static void __mptcp_subflow_push_pending(struct sock *sk, struct sock *ssk) struct mptcp_data_frag *dfrag; struct sock *xmit_ssk; int len, copied = 0; - bool first = true; info.flags = 0; while ((dfrag = mptcp_send_head(sk))) { @@ -1621,8 +1620,7 @@ static void __mptcp_subflow_push_pending(struct sock *sk, struct sock *ssk) while (len > 0) { int ret = 0; - /* the caller already invoked the packet scheduler, - * check for a different subflow usage only after + /* check for a different subflow usage only after * spooling the first chunk of data */ xmit_ssk = first ? ssk : mptcp_subflow_get_send(msk); @@ -3220,16 +3218,10 @@ void __mptcp_check_push(struct sock *sk, struct sock *ssk) if (!mptcp_send_head(sk)) return; - if (!sock_owned_by_user(sk)) { - struct sock *xmit_ssk = mptcp_subflow_get_send(mptcp_sk(sk)); - - if (xmit_ssk == ssk) - __mptcp_subflow_push_pending(sk, ssk); - else if (xmit_ssk) - mptcp_subflow_delegate(mptcp_subflow_ctx(xmit_ssk), MPTCP_DELEGATE_SEND); - } else { + if (!sock_owned_by_user(sk)) + __mptcp_subflow_push_pending(sk, ssk, false); + else __set_bit(MPTCP_PUSH_PENDING, &mptcp_sk(sk)->cb_flags); - } } #define MPTCP_FLAGS_PROCESS_CTX_NEED (BIT(MPTCP_PUSH_PENDING) | \ @@ -3320,7 +3312,7 @@ void mptcp_subflow_process_delegated(struct sock *ssk) if (test_bit(MPTCP_DELEGATE_SEND, &subflow->delegated_status)) { mptcp_data_lock(sk); if (!sock_owned_by_user(sk)) - __mptcp_subflow_push_pending(sk, ssk); + __mptcp_subflow_push_pending(sk, ssk, true); else __set_bit(MPTCP_PUSH_PENDING, &mptcp_sk(sk)->cb_flags); mptcp_data_unlock(sk); From 80638684e840684042325fa5a3fa1eb59fd123cc Mon Sep 17 00:00:00 2001 From: Geliang Tang Date: Thu, 10 Nov 2022 15:23:20 -0800 Subject: [PATCH 0865/2002] mptcp: get sk from msk directly Use '(struct sock *)msk' to get 'sk' from 'msk' in a more direct way instead of using '&msk->sk.icsk_inet.sk'. Reviewed-by: Mat Martineau Signed-off-by: Geliang Tang Signed-off-by: Mat Martineau Signed-off-by: Jakub Kicinski --- net/mptcp/pm_userspace.c | 4 ++-- net/mptcp/protocol.c | 4 ++-- net/mptcp/sockopt.c | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/net/mptcp/pm_userspace.c b/net/mptcp/pm_userspace.c index 9e82250cbb70..5cb65f0928f4 100644 --- a/net/mptcp/pm_userspace.c +++ b/net/mptcp/pm_userspace.c @@ -291,7 +291,7 @@ int mptcp_nl_cmd_sf_create(struct sk_buff *skb, struct genl_info *info) goto create_err; } - sk = &msk->sk.icsk_inet.sk; + sk = (struct sock *)msk; lock_sock(sk); err = __mptcp_subflow_connect(sk, &addr_l, &addr_r); @@ -403,7 +403,7 @@ int mptcp_nl_cmd_sf_destroy(struct sk_buff *skb, struct genl_info *info) goto destroy_err; } - sk = &msk->sk.icsk_inet.sk; + sk = (struct sock *)msk; lock_sock(sk); ssk = mptcp_nl_find_ssk(msk, &addr_l, &addr_r); if (ssk) { diff --git a/net/mptcp/protocol.c b/net/mptcp/protocol.c index 5a344788f843..3796d1bfef6b 100644 --- a/net/mptcp/protocol.c +++ b/net/mptcp/protocol.c @@ -2454,7 +2454,7 @@ static bool mptcp_check_close_timeout(const struct sock *sk) static void mptcp_check_fastclose(struct mptcp_sock *msk) { struct mptcp_subflow_context *subflow, *tmp; - struct sock *sk = &msk->sk.icsk_inet.sk; + struct sock *sk = (struct sock *)msk; if (likely(!READ_ONCE(msk->rcv_fastclose))) return; @@ -2616,7 +2616,7 @@ static void mptcp_do_fastclose(struct sock *sk) static void mptcp_worker(struct work_struct *work) { struct mptcp_sock *msk = container_of(work, struct mptcp_sock, work); - struct sock *sk = &msk->sk.icsk_inet.sk; + struct sock *sk = (struct sock *)msk; unsigned long fail_tout; int state; diff --git a/net/mptcp/sockopt.c b/net/mptcp/sockopt.c index f85e9bbfe86f..f62f6483ef77 100644 --- a/net/mptcp/sockopt.c +++ b/net/mptcp/sockopt.c @@ -987,7 +987,7 @@ static int mptcp_getsockopt_tcpinfo(struct mptcp_sock *msk, char __user *optval, int __user *optlen) { struct mptcp_subflow_context *subflow; - struct sock *sk = &msk->sk.icsk_inet.sk; + struct sock *sk = (struct sock *)msk; unsigned int sfcount = 0, copied = 0; struct mptcp_subflow_data sfd; char __user *infoptr; @@ -1078,8 +1078,8 @@ static void mptcp_get_sub_addrs(const struct sock *sk, struct mptcp_subflow_addr static int mptcp_getsockopt_subflow_addrs(struct mptcp_sock *msk, char __user *optval, int __user *optlen) { - struct sock *sk = &msk->sk.icsk_inet.sk; struct mptcp_subflow_context *subflow; + struct sock *sk = (struct sock *)msk; unsigned int sfcount = 0, copied = 0; struct mptcp_subflow_data sfd; char __user *addrptr; From 31b4e63eb24a81770b0e46a768f606a114787262 Mon Sep 17 00:00:00 2001 From: Geliang Tang Date: Thu, 10 Nov 2022 15:23:21 -0800 Subject: [PATCH 0866/2002] selftests: mptcp: use max_time instead of time 'time' is the local variable of run_test() function, while 'max_time' is the local variable of do_transfer() function. So in do_transfer(), $max_time should be used, not $time. Please note that here $time == $max_time so the behaviour is not changed but the right variable is used. Reviewed-by: Mat Martineau Signed-off-by: Geliang Tang Signed-off-by: Mat Martineau Signed-off-by: Jakub Kicinski --- tools/testing/selftests/net/mptcp/simult_flows.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/testing/selftests/net/mptcp/simult_flows.sh b/tools/testing/selftests/net/mptcp/simult_flows.sh index ffa13a957a36..af70c14e0bf9 100755 --- a/tools/testing/selftests/net/mptcp/simult_flows.sh +++ b/tools/testing/selftests/net/mptcp/simult_flows.sh @@ -173,7 +173,7 @@ do_transfer() timeout ${timeout_test} \ ip netns exec ${ns3} \ - ./mptcp_connect -jt ${timeout_poll} -l -p $port -T $time \ + ./mptcp_connect -jt ${timeout_poll} -l -p $port -T $max_time \ 0.0.0.0 < "$sin" > "$sout" & local spid=$! @@ -181,7 +181,7 @@ do_transfer() timeout ${timeout_test} \ ip netns exec ${ns1} \ - ./mptcp_connect -jt ${timeout_poll} -p $port -T $time \ + ./mptcp_connect -jt ${timeout_poll} -p $port -T $max_time \ 10.0.3.3 < "$cin" > "$cout" & local cpid=$! From 4373bf4b72f93af9d045450a44d294953c590e2d Mon Sep 17 00:00:00 2001 From: Mat Martineau Date: Thu, 10 Nov 2022 15:23:22 -0800 Subject: [PATCH 0867/2002] mptcp: Fix grammar in a comment We kept getting initial patches from new contributors to remove a duplicate 'the' (since grammar checking scripts flag it), but submitters never followed up after code review. Reviewed-by: Matthieu Baerts Signed-off-by: Mat Martineau Signed-off-by: Jakub Kicinski --- net/mptcp/token.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/mptcp/token.c b/net/mptcp/token.c index f52ee7b26aed..65430f314a68 100644 --- a/net/mptcp/token.c +++ b/net/mptcp/token.c @@ -287,8 +287,8 @@ EXPORT_SYMBOL_GPL(mptcp_token_get_sock); * This function returns the first mptcp connection structure found inside the * token container starting from the specified position, or NULL. * - * On successful iteration, the iterator is move to the next position and the - * the acquires a reference to the returned socket. + * On successful iteration, the iterator is moved to the next position and + * a reference to the returned socket is acquired. */ struct mptcp_sock *mptcp_token_iter_next(const struct net *net, long *s_slot, long *s_num) From 45f22f2fdc19b78bf8397fb9e535f8f9e90fbaf2 Mon Sep 17 00:00:00 2001 From: Angelo Dureghello Date: Thu, 10 Nov 2022 10:10:27 +0100 Subject: [PATCH 0868/2002] net: dsa: mv88e6xxx: enable set_policy Enabling set_policy capability for mv88e6321. Signed-off-by: Angelo Dureghello Reviewed-by: Andrew Lunn Link: https://lore.kernel.org/r/20221110091027.998073-1-angelo.dureghello@timesys.com Signed-off-by: Jakub Kicinski --- drivers/net/dsa/mv88e6xxx/chip.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index bf34c942db99..ccfa4751d3b7 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -4074,6 +4074,7 @@ static const struct mv88e6xxx_ops mv88e6085_ops = { .port_sync_link = mv88e6xxx_port_sync_link, .port_set_speed_duplex = mv88e6185_port_set_speed_duplex, .port_tag_remap = mv88e6095_port_tag_remap, + .port_set_policy = mv88e6352_port_set_policy, .port_set_frame_mode = mv88e6351_port_set_frame_mode, .port_set_ucast_flood = mv88e6352_port_set_ucast_flood, .port_set_mcast_flood = mv88e6352_port_set_mcast_flood, @@ -4346,6 +4347,7 @@ static const struct mv88e6xxx_ops mv88e6161_ops = { .port_sync_link = mv88e6xxx_port_sync_link, .port_set_speed_duplex = mv88e6185_port_set_speed_duplex, .port_tag_remap = mv88e6095_port_tag_remap, + .port_set_policy = mv88e6352_port_set_policy, .port_set_frame_mode = mv88e6351_port_set_frame_mode, .port_set_ucast_flood = mv88e6352_port_set_ucast_flood, .port_set_mcast_flood = mv88e6352_port_set_mcast_flood, From 9d26628a4ce2d0ba42d0632e345c8bfd45b26127 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Thu, 10 Nov 2022 13:56:17 -0600 Subject: [PATCH 0869/2002] dt-bindings: net: qcom,ipa: remove an unnecessary restriction Commit d8604b209e9b3 ("dt-bindings: net: qcom,ipa: add firmware-name property") added a requirement for a "firmware-name" property that is more restrictive than necessary. If the AP loads GSI firmware, the name of the firmware file to use may optionally be provided via a "firmware-name" property. If the *modem* loads GSI firmware, "firmware-name" doesn't need to be supplied--but it's harmless to do so (it will simply be ignored). Remove the unnecessary restriction, and allow "firware-name" to be supplied even if it's not needed. Signed-off-by: Alex Elder Reviewed-by: Krzysztof Kozlowski Signed-off-by: Jakub Kicinski --- Documentation/devicetree/bindings/net/qcom,ipa.yaml | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/Documentation/devicetree/bindings/net/qcom,ipa.yaml b/Documentation/devicetree/bindings/net/qcom,ipa.yaml index dd4bb2e74880..2d253ef48818 100644 --- a/Documentation/devicetree/bindings/net/qcom,ipa.yaml +++ b/Documentation/devicetree/bindings/net/qcom,ipa.yaml @@ -162,16 +162,6 @@ oneOf: - required: - memory-region -# If memory-region is present, firmware-name may optionally be present. -# But if modem-init is present, firmware-name must not be present. -if: - required: - - modem-init -then: - not: - required: - - firmware-name - additionalProperties: false examples: From 7a6ca44c1e6156ac49274ce44f67bdc4d86187cb Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Thu, 10 Nov 2022 13:56:18 -0600 Subject: [PATCH 0870/2002] dt-bindings: net: qcom,ipa: restate a requirement Either the AP or modem loads GSI firmware. If the modem-init property is present, the modem loads it. Otherwise, the AP loads it, and in that case the memory-region property must be defined. Currently this requirement is expressed as one or the other of the modem-init or the memory-region property being required. But it's harmless for the memory-region to be present if the modem is loading firmware (it'll just be ignored). Restate the requirement so that the memory-region property is required only if modem-init is not present. Signed-off-by: Alex Elder Reviewed-by: Krzysztof Kozlowski Signed-off-by: Jakub Kicinski --- Documentation/devicetree/bindings/net/qcom,ipa.yaml | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/Documentation/devicetree/bindings/net/qcom,ipa.yaml b/Documentation/devicetree/bindings/net/qcom,ipa.yaml index 2d253ef48818..e752b76192df 100644 --- a/Documentation/devicetree/bindings/net/qcom,ipa.yaml +++ b/Documentation/devicetree/bindings/net/qcom,ipa.yaml @@ -155,12 +155,15 @@ required: - interconnects - qcom,smem-states -# Either modem-init is present, or memory-region must be present. -oneOf: - - required: +# If modem-init is not present, the AP loads GSI firmware, and +# memory-region must be specified +if: + not: + required: - modem-init - - required: - - memory-region +then: + required: + - memory-region additionalProperties: false From 3e35f26d339718e155fa7cf5993a99924124a041 Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Thu, 10 Nov 2022 10:54:22 +0200 Subject: [PATCH 0871/2002] bridge: Add missing parentheses No changes in generated code. Reported-by: Petr Machata Reviewed-by: Petr Machata Signed-off-by: Ido Schimmel Link: https://lore.kernel.org/r/20221110085422.521059-1-idosch@nvidia.com Signed-off-by: Jakub Kicinski --- net/bridge/br_input.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/bridge/br_input.c b/net/bridge/br_input.c index d04d2205ad4e..3027e8f6be15 100644 --- a/net/bridge/br_input.c +++ b/net/bridge/br_input.c @@ -121,7 +121,7 @@ int br_handle_frame_finish(struct net *net, struct sock *sk, struct sk_buff *skb test_bit(BR_FDB_LOCAL, &fdb_src->flags)) { /* FDB mismatch. Drop the packet without roaming. */ goto drop; - } else if test_bit(BR_FDB_LOCKED, &fdb_src->flags) { + } else if (test_bit(BR_FDB_LOCKED, &fdb_src->flags)) { /* FDB match, but entry is locked. Refresh it and drop * the packet. */ From fac30731b9b8a0b7580bf73ddd25035ef0a733a5 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Thu, 10 Nov 2022 17:48:29 +0000 Subject: [PATCH 0872/2002] tcp: adopt try_cmpxchg() in tcp_release_cb() try_cmpxchg() is slighly more efficient (at least on x86), and smp_load_acquire(&sk->sk_tsq_flags) could avoid a KCSAN report. Signed-off-by: Eric Dumazet Link: https://lore.kernel.org/r/20221110174829.3403442-1-edumazet@google.com Signed-off-by: Jakub Kicinski --- net/ipv4/tcp_output.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index c69f4d966024..d1cb1ecf8f21 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -1077,15 +1077,15 @@ static void tcp_tasklet_func(struct tasklet_struct *t) */ void tcp_release_cb(struct sock *sk) { - unsigned long flags, nflags; + unsigned long flags = smp_load_acquire(&sk->sk_tsq_flags); + unsigned long nflags; /* perform an atomic operation only if at least one flag is set */ do { - flags = sk->sk_tsq_flags; if (!(flags & TCP_DEFERRED_ALL)) return; nflags = flags & ~TCP_DEFERRED_ALL; - } while (cmpxchg(&sk->sk_tsq_flags, flags, nflags) != flags); + } while (!try_cmpxchg(&sk->sk_tsq_flags, &flags, nflags)); if (flags & TCPF_TSQ_DEFERRED) { tcp_tsq_write(sk); From b548b17a93fd18357a5a6f535c10c1e68719ad32 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Thu, 10 Nov 2022 19:02:39 +0000 Subject: [PATCH 0873/2002] tcp: tcp_wfree() refactoring Use try_cmpxchg() (instead of cmpxchg()) in a more readable way. oval = smp_load_acquire(&sk->sk_tsq_flags); do { ... } while (!try_cmpxchg(&sk->sk_tsq_flags, &oval, nval)); Reduce indentation level. Signed-off-by: Eric Dumazet Link: https://lore.kernel.org/r/20221110190239.3531280-1-edumazet@google.com Signed-off-by: Jakub Kicinski --- net/ipv4/tcp_output.c | 31 ++++++++++++++----------------- 1 file changed, 14 insertions(+), 17 deletions(-) diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index d1cb1ecf8f21..894410dc9293 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -1139,6 +1139,8 @@ void tcp_wfree(struct sk_buff *skb) struct sock *sk = skb->sk; struct tcp_sock *tp = tcp_sk(sk); unsigned long flags, nval, oval; + struct tsq_tasklet *tsq; + bool empty; /* Keep one reference on sk_wmem_alloc. * Will be released by sk_free() from here or tcp_tasklet_func() @@ -1155,28 +1157,23 @@ void tcp_wfree(struct sk_buff *skb) if (refcount_read(&sk->sk_wmem_alloc) >= SKB_TRUESIZE(1) && this_cpu_ksoftirqd() == current) goto out; - for (oval = READ_ONCE(sk->sk_tsq_flags);; oval = nval) { - struct tsq_tasklet *tsq; - bool empty; - + oval = smp_load_acquire(&sk->sk_tsq_flags); + do { if (!(oval & TSQF_THROTTLED) || (oval & TSQF_QUEUED)) goto out; nval = (oval & ~TSQF_THROTTLED) | TSQF_QUEUED; - nval = cmpxchg(&sk->sk_tsq_flags, oval, nval); - if (nval != oval) - continue; + } while (!try_cmpxchg(&sk->sk_tsq_flags, &oval, nval)); - /* queue this socket to tasklet queue */ - local_irq_save(flags); - tsq = this_cpu_ptr(&tsq_tasklet); - empty = list_empty(&tsq->head); - list_add(&tp->tsq_node, &tsq->head); - if (empty) - tasklet_schedule(&tsq->tasklet); - local_irq_restore(flags); - return; - } + /* queue this socket to tasklet queue */ + local_irq_save(flags); + tsq = this_cpu_ptr(&tsq_tasklet); + empty = list_empty(&tsq->head); + list_add(&tp->tsq_node, &tsq->head); + if (empty) + tasklet_schedule(&tsq->tasklet); + local_irq_restore(flags); + return; out: sk_free(sk); } From ea645f97bcec90a818dc6915d84cd0f752d9ccfd Mon Sep 17 00:00:00 2001 From: Roi Dayan Date: Thu, 27 Oct 2022 11:35:12 +0300 Subject: [PATCH 0874/2002] net/mlx5: Bridge, Use debug instead of warn if entry doesn't exists There is no need for the warn if entry already removed. Use debug print like in the update flow. Also update the messages so user can identify if the it's from the update flow or remove flow. Signed-off-by: Roi Dayan Reviewed-by: Vlad Buslov Signed-off-by: Saeed Mahameed --- drivers/net/ethernet/mellanox/mlx5/core/esw/bridge.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/esw/bridge.c b/drivers/net/ethernet/mellanox/mlx5/core/esw/bridge.c index 4fbff7bcc155..b176648d1343 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/esw/bridge.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/esw/bridge.c @@ -1722,7 +1722,7 @@ void mlx5_esw_bridge_fdb_update_used(struct net_device *dev, u16 vport_num, u16 entry = mlx5_esw_bridge_fdb_lookup(bridge, fdb_info->addr, fdb_info->vid); if (!entry) { esw_debug(br_offloads->esw->dev, - "FDB entry with specified key not found (MAC=%pM,vid=%u,vport=%u)\n", + "FDB update entry with specified key not found (MAC=%pM,vid=%u,vport=%u)\n", fdb_info->addr, fdb_info->vid, vport_num); return; } @@ -1775,9 +1775,9 @@ void mlx5_esw_bridge_fdb_remove(struct net_device *dev, u16 vport_num, u16 esw_o bridge = port->bridge; entry = mlx5_esw_bridge_fdb_lookup(bridge, fdb_info->addr, fdb_info->vid); if (!entry) { - esw_warn(esw->dev, - "FDB entry with specified key not found (MAC=%pM,vid=%u,vport=%u)\n", - fdb_info->addr, fdb_info->vid, vport_num); + esw_debug(esw->dev, + "FDB remove entry with specified key not found (MAC=%pM,vid=%u,vport=%u)\n", + fdb_info->addr, fdb_info->vid, vport_num); return; } From d23b928befda3524f12d8da29921ea066d72bde7 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Mon, 31 Oct 2022 08:01:04 +0000 Subject: [PATCH 0875/2002] net/mlx5: Fix spelling mistake "destoy" -> "destroy" There is a spelling mistake in an error message. Fix it. Signed-off-by: Colin Ian King Reviewed-by: Leon Romanovsky Signed-off-by: Saeed Mahameed --- drivers/net/ethernet/mellanox/mlx5/core/steering/dr_table.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_table.c b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_table.c index eb81759244d5..9c3dfd68f8df 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_table.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_table.c @@ -292,7 +292,7 @@ int mlx5dr_table_destroy(struct mlx5dr_table *tbl) mlx5dr_dbg_tbl_del(tbl); ret = dr_table_destroy_sw_owned_tbl(tbl); if (ret) - mlx5dr_err(tbl->dmn, "Failed to destoy sw owned table\n"); + mlx5dr_err(tbl->dmn, "Failed to destroy sw owned table\n"); dr_table_uninit(tbl); From 71b75f0e02ed160ccbb4fe609036abe83aa32054 Mon Sep 17 00:00:00 2001 From: Moshe Shemesh Date: Mon, 8 Aug 2022 20:02:59 +0300 Subject: [PATCH 0876/2002] net/mlx5: Unregister traps on driver unload flow Before this patch, devlink traps are registered only on full driver probe and unregistered on driver removal. As devlink traps are not usable once driver functionality is unloaded, it should be unrgeistered also on flows that unload the driver and then registered when loaded back, e.g. devlink reload flow. Signed-off-by: Moshe Shemesh Reviewed-by: Aya Levin Signed-off-by: Saeed Mahameed --- drivers/net/ethernet/mellanox/mlx5/core/devlink.c | 11 ++--------- drivers/net/ethernet/mellanox/mlx5/core/devlink.h | 2 ++ drivers/net/ethernet/mellanox/mlx5/core/main.c | 8 ++++++++ 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/devlink.c b/drivers/net/ethernet/mellanox/mlx5/core/devlink.c index 66c6a7017695..cc2ae427dcb0 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/devlink.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/devlink.c @@ -840,7 +840,7 @@ static const struct devlink_trap_group mlx5_trap_groups_arr[] = { DEVLINK_TRAP_GROUP_GENERIC(L2_DROPS, 0), }; -static int mlx5_devlink_traps_register(struct devlink *devlink) +int mlx5_devlink_traps_register(struct devlink *devlink) { struct mlx5_core_dev *core_dev = devlink_priv(devlink); int err; @@ -862,7 +862,7 @@ err_trap_group: return err; } -static void mlx5_devlink_traps_unregister(struct devlink *devlink) +void mlx5_devlink_traps_unregister(struct devlink *devlink) { devl_traps_unregister(devlink, mlx5_traps_arr, ARRAY_SIZE(mlx5_traps_arr)); devl_trap_groups_unregister(devlink, mlx5_trap_groups_arr, @@ -889,17 +889,11 @@ int mlx5_devlink_register(struct devlink *devlink) if (err) goto max_uc_list_err; - err = mlx5_devlink_traps_register(devlink); - if (err) - goto traps_reg_err; - if (!mlx5_core_is_mp_slave(dev)) devlink_set_features(devlink, DEVLINK_F_RELOAD); return 0; -traps_reg_err: - mlx5_devlink_max_uc_list_param_unregister(devlink); max_uc_list_err: mlx5_devlink_auxdev_params_unregister(devlink); auxdev_reg_err: @@ -910,7 +904,6 @@ auxdev_reg_err: void mlx5_devlink_unregister(struct devlink *devlink) { - mlx5_devlink_traps_unregister(devlink); mlx5_devlink_max_uc_list_param_unregister(devlink); mlx5_devlink_auxdev_params_unregister(devlink); devlink_params_unregister(devlink, mlx5_devlink_params, diff --git a/drivers/net/ethernet/mellanox/mlx5/core/devlink.h b/drivers/net/ethernet/mellanox/mlx5/core/devlink.h index 30bf4882779b..fd033df24856 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/devlink.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/devlink.h @@ -30,6 +30,8 @@ void mlx5_devlink_trap_report(struct mlx5_core_dev *dev, int trap_id, struct sk_ int mlx5_devlink_trap_get_num_active(struct mlx5_core_dev *dev); int mlx5_devlink_traps_get_action(struct mlx5_core_dev *dev, int trap_id, enum devlink_trap_action *action); +int mlx5_devlink_traps_register(struct devlink *devlink); +void mlx5_devlink_traps_unregister(struct devlink *devlink); struct devlink *mlx5_devlink_alloc(struct device *dev); void mlx5_devlink_free(struct devlink *devlink); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/main.c b/drivers/net/ethernet/mellanox/mlx5/core/main.c index 283c4cc28944..9e6da51b7481 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/main.c @@ -1306,8 +1306,15 @@ static int mlx5_load(struct mlx5_core_dev *dev) mlx5_sf_dev_table_create(dev); + err = mlx5_devlink_traps_register(priv_to_devlink(dev)); + if (err) + goto err_traps_reg; + return 0; +err_traps_reg: + mlx5_sf_dev_table_destroy(dev); + mlx5_sriov_detach(dev); err_sriov: mlx5_lag_remove_mdev(dev); mlx5_ec_cleanup(dev); @@ -1336,6 +1343,7 @@ err_irq_table: static void mlx5_unload(struct mlx5_core_dev *dev) { + mlx5_devlink_traps_unregister(priv_to_devlink(dev)); mlx5_sf_dev_table_destroy(dev); mlx5_sriov_detach(dev); mlx5_eswitch_disable(dev->priv.eswitch); From dd3dd7263cdeac601d0ecc5f04d87eee0a8277aa Mon Sep 17 00:00:00 2001 From: Eli Cohen Date: Wed, 21 Sep 2022 13:33:29 +0300 Subject: [PATCH 0877/2002] net/mlx5: Expose vhca_id to debugfs hca_id is an identifier of an mlx5_core instance within the hardware. This identifier may be required for troubleshooting. Expose it to debugfs. Example: $ cat /sys/kernel/debug/mlx5/mlx5_core.sf.2/vhca_id 0x12 Signed-off-by: Eli Cohen Reviewed-by: Roi Dayan Signed-off-by: Saeed Mahameed --- drivers/net/ethernet/mellanox/mlx5/core/main.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/main.c b/drivers/net/ethernet/mellanox/mlx5/core/main.c index 9e6da51b7481..6d7c102861ea 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/main.c @@ -1588,6 +1588,16 @@ err: return -ENOMEM; } +static int vhca_id_show(struct seq_file *file, void *priv) +{ + struct mlx5_core_dev *dev = file->private; + + seq_printf(file, "0x%x\n", MLX5_CAP_GEN(dev, vhca_id)); + return 0; +} + +DEFINE_SHOW_ATTRIBUTE(vhca_id); + int mlx5_mdev_init(struct mlx5_core_dev *dev, int profile_idx) { struct mlx5_priv *priv = &dev->priv; @@ -1612,6 +1622,7 @@ int mlx5_mdev_init(struct mlx5_core_dev *dev, int profile_idx) priv->numa_node = dev_to_node(mlx5_core_dma_dev(dev)); priv->dbg.dbg_root = debugfs_create_dir(dev_name(dev->device), mlx5_debugfs_root); + debugfs_create_file("vhca_id", 0400, priv->dbg.dbg_root, dev, &vhca_id_fops); INIT_LIST_HEAD(&priv->traps); err = mlx5_tout_init(dev); From 60551e95a864f2ba82b98defcf85306599341918 Mon Sep 17 00:00:00 2001 From: Anisse Astier Date: Mon, 31 Oct 2022 17:56:04 +0100 Subject: [PATCH 0878/2002] net/mlx5e: remove unused list in arfs This is never used, and probably something that was intended to be used before per-protocol hash tables were chosen instead. Signed-off-by: Anisse Astier Reviewed-by: Leon Romanovsky Signed-off-by: Saeed Mahameed --- drivers/net/ethernet/mellanox/mlx5/core/en_arfs.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_arfs.c b/drivers/net/ethernet/mellanox/mlx5/core/en_arfs.c index 0ae1865086ff..bed0c2d043e7 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_arfs.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_arfs.c @@ -57,7 +57,6 @@ struct mlx5e_arfs_tables { struct arfs_table arfs_tables[ARFS_NUM_TYPES]; /* Protect aRFS rules list */ spinlock_t arfs_lock; - struct list_head rules; int last_filter_id; struct workqueue_struct *wq; }; @@ -376,7 +375,6 @@ int mlx5e_arfs_create_tables(struct mlx5e_flow_steering *fs, return -ENOMEM; spin_lock_init(&arfs->arfs_lock); - INIT_LIST_HEAD(&arfs->rules); arfs->wq = create_singlethread_workqueue("mlx5e_arfs"); if (!arfs->wq) goto err; From 9458108040b3c0980a02308ddf30568b9823349c Mon Sep 17 00:00:00 2001 From: Gal Pressman Date: Sun, 4 Sep 2022 13:29:26 +0300 Subject: [PATCH 0879/2002] net/mlx5e: Use clamp operation instead of open coding it Replace the min/max operations with a single clamp. Signed-off-by: Gal Pressman Reviewed-by: Tariq Toukan Signed-off-by: Saeed Mahameed --- drivers/net/ethernet/mellanox/mlx5/core/en_tc.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c index 5a6aa61ec82a..3782f0097292 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c @@ -1060,12 +1060,9 @@ static int mlx5e_hairpin_flow_add(struct mlx5e_priv *priv, hash_hairpin_info(peer_id, match_prio)); mutex_unlock(&tc->hairpin_tbl_lock); - params.log_data_size = 16; - params.log_data_size = min_t(u8, params.log_data_size, - MLX5_CAP_GEN(priv->mdev, log_max_hairpin_wq_data_sz)); - params.log_data_size = max_t(u8, params.log_data_size, - MLX5_CAP_GEN(priv->mdev, log_min_hairpin_wq_data_sz)); - + params.log_data_size = clamp_t(u8, 16, + MLX5_CAP_GEN(priv->mdev, log_min_hairpin_wq_data_sz), + MLX5_CAP_GEN(priv->mdev, log_max_hairpin_wq_data_sz)); params.log_num_packets = params.log_data_size - MLX5_MPWRQ_MIN_LOG_STRIDE_SZ(priv->mdev); params.log_num_packets = min_t(u8, params.log_num_packets, From 2c925db0a7d69b404d6bfe4c037935c2d367913d Mon Sep 17 00:00:00 2001 From: Ofer Levi Date: Tue, 9 Feb 2021 17:48:11 +0200 Subject: [PATCH 0880/2002] net/mlx5e: Support enhanced CQE compression CQE compression feature improves performance by reducing PCI bandwidth bottleneck on CQEs write. Enhanced CQE compression introduced in ConnectX-6 and it aims to reduce CPU utilization of SW side packets decompression by eliminating the need to rewrite ownership bit, which is likely to cost a cache-miss, is replaced by validity byte handled solely by HW. Another advantage of the enhanced feature is that session packets are available to SW as soon as a single CQE slot is filled, instead of waiting for session to close, this improves packet latency from NIC to host. Performance: Following are tested scenarios and reults comparing basic and enahnced CQE compression. setup: IXIA 100GbE connected directly to port 0 and port 1 of ConnectX-6 Dx 100GbE dual port. Case #1 RX only, single flow goes to single queue: IRQ rate reduced by ~ 30%, CPU utilization improved by 2%. Case #2 IP forwarding from port 1 to port 0 single flow goes to single queue: Avg latency improved from 60us to 21us, frame loss improved from 0.5% to 0.0%. Case #3 IP forwarding from port 1 to port 0 Max Throughput IXIA sends 100%, 8192 UDP flows, goes to 24 queues: Enhanced is equal or slightly better than basic. Testing the basic compression feature with this patch shows there is no perfrormance degradation of the basic compression feature. Signed-off-by: Ofer Levi Reviewed-by: Tariq Toukan Signed-off-by: Saeed Mahameed --- drivers/net/ethernet/mellanox/mlx5/core/en.h | 2 + .../ethernet/mellanox/mlx5/core/en/params.c | 10 +- .../net/ethernet/mellanox/mlx5/core/en_main.c | 8 + .../net/ethernet/mellanox/mlx5/core/en_rx.c | 154 +++++++++++++++--- drivers/net/ethernet/mellanox/mlx5/core/wq.h | 17 ++ include/linux/mlx5/device.h | 6 + 6 files changed, 172 insertions(+), 25 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en.h b/drivers/net/ethernet/mellanox/mlx5/core/en.h index 26a23047f1f3..ff5b302531d5 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en.h @@ -344,6 +344,7 @@ enum { MLX5E_RQ_STATE_CSUM_FULL, /* cqe_csum_full hw bit is set */ MLX5E_RQ_STATE_MINI_CQE_HW_STRIDX, /* set when mini_cqe_resp_stride_index cap is used */ MLX5E_RQ_STATE_SHAMPO, /* set when SHAMPO cap is used */ + MLX5E_RQ_STATE_MINI_CQE_ENHANCED, /* set when enhanced mini_cqe_cap is used */ }; struct mlx5e_cq { @@ -370,6 +371,7 @@ struct mlx5e_cq_decomp { u8 mini_arr_idx; u16 left; u16 wqe_counter; + bool last_cqe_title; } ____cacheline_aligned_in_smp; enum mlx5e_dma_map_type { diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/params.c b/drivers/net/ethernet/mellanox/mlx5/core/en/params.c index 29dd3a04c154..1a2de9bc6538 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/params.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/params.c @@ -608,13 +608,15 @@ void mlx5e_init_rq_type_params(struct mlx5_core_dev *mdev, MLX5E_PARAMS_MINIMUM_LOG_RQ_SIZE : MLX5E_PARAMS_DEFAULT_LOG_RQ_SIZE; - mlx5_core_info(mdev, "MLX5E: StrdRq(%d) RqSz(%ld) StrdSz(%ld) RxCqeCmprss(%d)\n", + mlx5_core_info(mdev, "MLX5E: StrdRq(%d) RqSz(%ld) StrdSz(%ld) RxCqeCmprss(%d %s)\n", params->rq_wq_type == MLX5_WQ_TYPE_LINKED_LIST_STRIDING_RQ, params->rq_wq_type == MLX5_WQ_TYPE_LINKED_LIST_STRIDING_RQ ? BIT(mlx5e_mpwqe_get_log_rq_size(mdev, params, NULL)) : BIT(params->log_rq_mtu_frames), BIT(mlx5e_mpwqe_get_log_stride_size(mdev, params, NULL)), - MLX5E_GET_PFLAG(params, MLX5E_PFLAG_RX_CQE_COMPRESS)); + MLX5E_GET_PFLAG(params, MLX5E_PFLAG_RX_CQE_COMPRESS), + MLX5_CAP_GEN(mdev, enhanced_cqe_compression) ? + "enhanced" : "basic"); } void mlx5e_set_rq_type(struct mlx5_core_dev *mdev, struct mlx5e_params *params) @@ -852,6 +854,10 @@ static void mlx5e_build_rx_cq_param(struct mlx5_core_dev *mdev, if (MLX5E_GET_PFLAG(params, MLX5E_PFLAG_RX_CQE_COMPRESS)) { MLX5_SET(cqc, cqc, mini_cqe_res_format, hw_stridx ? MLX5_CQE_FORMAT_CSUM_STRIDX : MLX5_CQE_FORMAT_CSUM); + MLX5_SET(cqc, cqc, cqe_compression_layout, + MLX5_CAP_GEN(mdev, enhanced_cqe_compression) ? + MLX5_CQE_COMPRESS_LAYOUT_ENHANCED : + MLX5_CQE_COMPRESS_LAYOUT_BASIC); MLX5_SET(cqc, cqc, cqe_comp_en, 1); } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c index 1669c7d7f285..c462b76743b6 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c @@ -1205,6 +1205,13 @@ int mlx5e_open_rq(struct mlx5e_params *params, struct mlx5e_rq_param *param, MLX5_CAP_GEN(mdev, mini_cqe_resp_stride_index)) __set_bit(MLX5E_RQ_STATE_MINI_CQE_HW_STRIDX, &rq->state); + /* For enhanced CQE compression packet processing. decompress + * session according to the enhanced layout. + */ + if (MLX5E_GET_PFLAG(params, MLX5E_PFLAG_RX_CQE_COMPRESS) && + MLX5_CAP_GEN(mdev, enhanced_cqe_compression)) + __set_bit(MLX5E_RQ_STATE_MINI_CQE_ENHANCED, &rq->state); + return 0; err_destroy_rq: @@ -1895,6 +1902,7 @@ static int mlx5e_alloc_cq_common(struct mlx5e_priv *priv, struct mlx5_cqe64 *cqe = mlx5_cqwq_get_wqe(&cq->wq, i); cqe->op_own = 0xf1; + cqe->validity_iteration_count = 0xff; } cq->mdev = mdev; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c b/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c index a61a43fc8d5c..b1ea0b995d9c 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c @@ -89,6 +89,25 @@ static inline void mlx5e_read_cqe_slot(struct mlx5_cqwq *wq, memcpy(data, mlx5_cqwq_get_wqe(wq, ci), sizeof(struct mlx5_cqe64)); } +static void mlx5e_read_enhanced_title_slot(struct mlx5e_rq *rq, + struct mlx5_cqe64 *cqe) +{ + struct mlx5e_cq_decomp *cqd = &rq->cqd; + struct mlx5_cqe64 *title = &cqd->title; + + memcpy(title, cqe, sizeof(struct mlx5_cqe64)); + + if (likely(test_bit(MLX5E_RQ_STATE_MINI_CQE_HW_STRIDX, &rq->state))) + return; + + if (rq->wq_type == MLX5_WQ_TYPE_LINKED_LIST_STRIDING_RQ) + cqd->wqe_counter = mpwrq_get_cqe_stride_index(title) + + mpwrq_get_cqe_consumed_strides(title); + else + cqd->wqe_counter = + mlx5_wq_cyc_ctr2ix(&rq->wqe.wq, be16_to_cpu(title->wqe_counter) + 1); +} + static inline void mlx5e_read_title_slot(struct mlx5e_rq *rq, struct mlx5_cqwq *wq, u32 cqcc) @@ -175,6 +194,38 @@ static inline void mlx5e_decompress_cqe_no_hash(struct mlx5e_rq *rq, cqd->title.rss_hash_result = 0; } +static u32 mlx5e_decompress_enhanced_cqe(struct mlx5e_rq *rq, + struct mlx5_cqwq *wq, + struct mlx5_cqe64 *cqe, + int budget_rem) +{ + struct mlx5e_cq_decomp *cqd = &rq->cqd; + u32 cqcc, left; + u32 i; + + left = get_cqe_enhanced_num_mini_cqes(cqe); + /* Here we avoid breaking the cqe compression session in the middle + * in case budget is not sufficient to handle all of it. In this case + * we return work_done == budget_rem to give 'busy' napi indication. + */ + if (unlikely(left > budget_rem)) + return budget_rem; + + cqcc = wq->cc; + cqd->mini_arr_idx = 0; + memcpy(cqd->mini_arr, cqe, sizeof(struct mlx5_cqe64)); + for (i = 0; i < left; i++, cqd->mini_arr_idx++, cqcc++) { + mlx5e_decompress_cqe_no_hash(rq, wq, cqcc); + INDIRECT_CALL_3(rq->handle_rx_cqe, mlx5e_handle_rx_cqe_mpwrq, + mlx5e_handle_rx_cqe, mlx5e_handle_rx_cqe_mpwrq_shampo, + rq, &cqd->title); + } + wq->cc = cqcc; + rq->stats->cqe_compress_pkts += left; + + return left; +} + static inline u32 mlx5e_decompress_cqes_cont(struct mlx5e_rq *rq, struct mlx5_cqwq *wq, int update_owner_only, @@ -220,7 +271,7 @@ static inline u32 mlx5e_decompress_cqes_start(struct mlx5e_rq *rq, rq, &cqd->title); cqd->mini_arr_idx++; - return mlx5e_decompress_cqes_cont(rq, wq, 1, budget_rem) - 1; + return mlx5e_decompress_cqes_cont(rq, wq, 1, budget_rem); } static inline bool mlx5e_rx_cache_put(struct mlx5e_rq *rq, struct page *page) @@ -2211,45 +2262,102 @@ mpwrq_cqe_out: mlx5_wq_ll_pop(wq, cqe->wqe_id, &wqe->next.next_wqe_index); } -int mlx5e_poll_rx_cq(struct mlx5e_cq *cq, int budget) +static int mlx5e_rx_cq_process_enhanced_cqe_comp(struct mlx5e_rq *rq, + struct mlx5_cqwq *cqwq, + int budget_rem) { - struct mlx5e_rq *rq = container_of(cq, struct mlx5e_rq, cq); - struct mlx5_cqwq *cqwq = &cq->wq; - struct mlx5_cqe64 *cqe; + struct mlx5_cqe64 *cqe, *title_cqe = NULL; + struct mlx5e_cq_decomp *cqd = &rq->cqd; int work_done = 0; - if (unlikely(!test_bit(MLX5E_RQ_STATE_ENABLED, &rq->state))) - return 0; + cqe = mlx5_cqwq_get_cqe_enahnced_comp(cqwq); + if (!cqe) + return work_done; - if (rq->cqd.left) { - work_done += mlx5e_decompress_cqes_cont(rq, cqwq, 0, budget); - if (work_done >= budget) - goto out; - } - - cqe = mlx5_cqwq_get_cqe(cqwq); - if (!cqe) { - if (unlikely(work_done)) - goto out; - return 0; + if (cqd->last_cqe_title && + (mlx5_get_cqe_format(cqe) == MLX5_COMPRESSED)) { + rq->stats->cqe_compress_blks++; + cqd->last_cqe_title = false; } do { if (mlx5_get_cqe_format(cqe) == MLX5_COMPRESSED) { + if (title_cqe) { + mlx5e_read_enhanced_title_slot(rq, title_cqe); + title_cqe = NULL; + rq->stats->cqe_compress_blks++; + } work_done += - mlx5e_decompress_cqes_start(rq, cqwq, - budget - work_done); + mlx5e_decompress_enhanced_cqe(rq, cqwq, cqe, + budget_rem - work_done); continue; } - + title_cqe = cqe; mlx5_cqwq_pop(cqwq); INDIRECT_CALL_3(rq->handle_rx_cqe, mlx5e_handle_rx_cqe_mpwrq, mlx5e_handle_rx_cqe, mlx5e_handle_rx_cqe_mpwrq_shampo, rq, cqe); - } while ((++work_done < budget) && (cqe = mlx5_cqwq_get_cqe(cqwq))); + work_done++; + } while (work_done < budget_rem && + (cqe = mlx5_cqwq_get_cqe_enahnced_comp(cqwq))); + + /* last cqe might be title on next poll bulk */ + if (title_cqe) { + mlx5e_read_enhanced_title_slot(rq, title_cqe); + cqd->last_cqe_title = true; + } + + return work_done; +} + +static int mlx5e_rx_cq_process_basic_cqe_comp(struct mlx5e_rq *rq, + struct mlx5_cqwq *cqwq, + int budget_rem) +{ + struct mlx5_cqe64 *cqe; + int work_done = 0; + + if (rq->cqd.left) + work_done += mlx5e_decompress_cqes_cont(rq, cqwq, 0, budget_rem); + + while (work_done < budget_rem && (cqe = mlx5_cqwq_get_cqe(cqwq))) { + if (mlx5_get_cqe_format(cqe) == MLX5_COMPRESSED) { + work_done += + mlx5e_decompress_cqes_start(rq, cqwq, + budget_rem - work_done); + continue; + } + + mlx5_cqwq_pop(cqwq); + INDIRECT_CALL_3(rq->handle_rx_cqe, mlx5e_handle_rx_cqe_mpwrq, + mlx5e_handle_rx_cqe, mlx5e_handle_rx_cqe_mpwrq_shampo, + rq, cqe); + work_done++; + } + + return work_done; +} + +int mlx5e_poll_rx_cq(struct mlx5e_cq *cq, int budget) +{ + struct mlx5e_rq *rq = container_of(cq, struct mlx5e_rq, cq); + struct mlx5_cqwq *cqwq = &cq->wq; + int work_done; + + if (unlikely(!test_bit(MLX5E_RQ_STATE_ENABLED, &rq->state))) + return 0; + + if (test_bit(MLX5E_RQ_STATE_MINI_CQE_ENHANCED, &rq->state)) + work_done = mlx5e_rx_cq_process_enhanced_cqe_comp(rq, cqwq, + budget); + else + work_done = mlx5e_rx_cq_process_basic_cqe_comp(rq, cqwq, + budget); + + if (work_done == 0) + return 0; -out: if (test_bit(MLX5E_RQ_STATE_SHAMPO, &rq->state) && rq->hw_gro_data->skb) mlx5e_shampo_flush_skb(rq, NULL, false); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/wq.h b/drivers/net/ethernet/mellanox/mlx5/core/wq.h index 4d629e5ddbc7..e4ef1d24a3ad 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/wq.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/wq.h @@ -243,6 +243,23 @@ static inline struct mlx5_cqe64 *mlx5_cqwq_get_cqe(struct mlx5_cqwq *wq) return cqe; } +static inline +struct mlx5_cqe64 *mlx5_cqwq_get_cqe_enahnced_comp(struct mlx5_cqwq *wq) +{ + u8 sw_validity_iteration_count = mlx5_cqwq_get_wrap_cnt(wq) & 0xff; + u32 ci = mlx5_cqwq_get_ci(wq); + struct mlx5_cqe64 *cqe; + + cqe = mlx5_cqwq_get_wqe(wq, ci); + if (cqe->validity_iteration_count != sw_validity_iteration_count) + return NULL; + + /* ensure cqe content is read after cqe ownership bit/validity byte */ + dma_rmb(); + + return cqe; +} + static inline u32 mlx5_wq_ll_get_size(struct mlx5_wq_ll *wq) { return (u32)wq->fbc.sz_m1 + 1; diff --git a/include/linux/mlx5/device.h b/include/linux/mlx5/device.h index 1ff91cb79ded..eb3fac30488b 100644 --- a/include/linux/mlx5/device.h +++ b/include/linux/mlx5/device.h @@ -882,6 +882,12 @@ static inline u8 get_cqe_opcode(struct mlx5_cqe64 *cqe) return cqe->op_own >> 4; } +static inline u8 get_cqe_enhanced_num_mini_cqes(struct mlx5_cqe64 *cqe) +{ + /* num_of_mini_cqes is zero based */ + return get_cqe_opcode(cqe) + 1; +} + static inline u8 get_cqe_lro_tcppsh(struct mlx5_cqe64 *cqe) { return (cqe->lro.tcppsh_abort_dupack >> 6) & 1; From 38438d39a9a001df8926f1b2b3087764dbbdc1aa Mon Sep 17 00:00:00 2001 From: Tariq Toukan Date: Wed, 18 May 2022 11:46:35 +0300 Subject: [PATCH 0881/2002] net/mlx5e: Move params kernel log print to probe function Params info print was meant to be printed on load. With time, new calls to mlx5e_init_rq_type_params and mlx5e_build_rq_params were added, mistakenly printing the params once again. Move the print to were it belongs, in mlx5e_probe. Signed-off-by: Tariq Toukan Reviewed-by: Gal Pressman Signed-off-by: Saeed Mahameed --- .../net/ethernet/mellanox/mlx5/core/en/params.c | 10 ---------- .../net/ethernet/mellanox/mlx5/core/en/params.h | 14 ++++++++++++++ drivers/net/ethernet/mellanox/mlx5/core/en_main.c | 1 + 3 files changed, 15 insertions(+), 10 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/params.c b/drivers/net/ethernet/mellanox/mlx5/core/en/params.c index 1a2de9bc6538..9ec9662d1d0b 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/params.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/params.c @@ -607,16 +607,6 @@ void mlx5e_init_rq_type_params(struct mlx5_core_dev *mdev, params->log_rq_mtu_frames = is_kdump_kernel() ? MLX5E_PARAMS_MINIMUM_LOG_RQ_SIZE : MLX5E_PARAMS_DEFAULT_LOG_RQ_SIZE; - - mlx5_core_info(mdev, "MLX5E: StrdRq(%d) RqSz(%ld) StrdSz(%ld) RxCqeCmprss(%d %s)\n", - params->rq_wq_type == MLX5_WQ_TYPE_LINKED_LIST_STRIDING_RQ, - params->rq_wq_type == MLX5_WQ_TYPE_LINKED_LIST_STRIDING_RQ ? - BIT(mlx5e_mpwqe_get_log_rq_size(mdev, params, NULL)) : - BIT(params->log_rq_mtu_frames), - BIT(mlx5e_mpwqe_get_log_stride_size(mdev, params, NULL)), - MLX5E_GET_PFLAG(params, MLX5E_PFLAG_RX_CQE_COMPRESS), - MLX5_CAP_GEN(mdev, enhanced_cqe_compression) ? - "enhanced" : "basic"); } void mlx5e_set_rq_type(struct mlx5_core_dev *mdev, struct mlx5e_params *params) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/params.h b/drivers/net/ethernet/mellanox/mlx5/core/en/params.h index 034debd140bc..c9be6eb88012 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/params.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/params.h @@ -154,4 +154,18 @@ int mlx5e_build_channel_param(struct mlx5_core_dev *mdev, u16 mlx5e_calc_sq_stop_room(struct mlx5_core_dev *mdev, struct mlx5e_params *params); int mlx5e_validate_params(struct mlx5_core_dev *mdev, struct mlx5e_params *params); +static inline void mlx5e_params_print_info(struct mlx5_core_dev *mdev, + struct mlx5e_params *params) +{ + mlx5_core_info(mdev, "MLX5E: StrdRq(%d) RqSz(%ld) StrdSz(%ld) RxCqeCmprss(%d %s)\n", + params->rq_wq_type == MLX5_WQ_TYPE_LINKED_LIST_STRIDING_RQ, + params->rq_wq_type == MLX5_WQ_TYPE_LINKED_LIST_STRIDING_RQ ? + BIT(mlx5e_mpwqe_get_log_rq_size(mdev, params, NULL)) : + BIT(params->log_rq_mtu_frames), + BIT(mlx5e_mpwqe_get_log_stride_size(mdev, params, NULL)), + MLX5E_GET_PFLAG(params, MLX5E_PFLAG_RX_CQE_COMPRESS), + MLX5_CAP_GEN(mdev, enhanced_cqe_compression) ? + "enhanced" : "basic"); +}; + #endif /* __MLX5_EN_PARAMS_H__ */ diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c index c462b76743b6..5df04e002589 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c @@ -5955,6 +5955,7 @@ static int mlx5e_probe(struct auxiliary_device *adev, mlx5e_dcbnl_init_app(priv); mlx5_uplink_netdev_set(mdev, netdev); + mlx5e_params_print_info(mdev, &priv->channels.params); return 0; err_resume: From e74ae1faeb71a0c264102a0aad66d65e5152a568 Mon Sep 17 00:00:00 2001 From: Guy Truzman Date: Thu, 29 Sep 2022 15:12:51 +0300 Subject: [PATCH 0882/2002] net/mlx5e: Add error flow when failing update_rx Up until now, return value of update_rx was ignored. Therefore, flow continues even if it fails. Add error flow in case of update_rx fails in mlx5e_open_locked, mlx5i_open and mlx5i_pkey_open. Signed-off-by: Guy Truzman Reviewed-by: Aya Levin Reviewed-by: Tariq Toukan Signed-off-by: Saeed Mahameed --- drivers/net/ethernet/mellanox/mlx5/core/en_main.c | 7 ++++++- drivers/net/ethernet/mellanox/mlx5/core/ipoib/ipoib.c | 7 ++++++- drivers/net/ethernet/mellanox/mlx5/core/ipoib/ipoib_vlan.c | 6 +++++- 3 files changed, 17 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c index 5df04e002589..14bd86e368d5 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c @@ -3069,7 +3069,10 @@ int mlx5e_open_locked(struct net_device *netdev) if (err) goto err_clear_state_opened_flag; - priv->profile->update_rx(priv); + err = priv->profile->update_rx(priv); + if (err) + goto err_close_channels; + mlx5e_selq_apply(&priv->selq); mlx5e_activate_priv_channels(priv); mlx5e_apply_traps(priv, true); @@ -3079,6 +3082,8 @@ int mlx5e_open_locked(struct net_device *netdev) mlx5e_queue_update_stats(priv); return 0; +err_close_channels: + mlx5e_close_channels(&priv->channels); err_clear_state_opened_flag: clear_bit(MLX5E_STATE_OPENED, &priv->state); mlx5e_selq_cancel(&priv->selq); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/ipoib/ipoib.c b/drivers/net/ethernet/mellanox/mlx5/core/ipoib/ipoib.c index 4e3a75496dd9..7c5c500fd215 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/ipoib/ipoib.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/ipoib/ipoib.c @@ -561,12 +561,17 @@ static int mlx5i_open(struct net_device *netdev) if (err) goto err_remove_fs_underlay_qp; - epriv->profile->update_rx(epriv); + err = epriv->profile->update_rx(epriv); + if (err) + goto err_close_channels; + mlx5e_activate_priv_channels(epriv); mutex_unlock(&epriv->state_lock); return 0; +err_close_channels: + mlx5e_close_channels(&epriv->channels); err_remove_fs_underlay_qp: mlx5_fs_remove_rx_underlay_qpn(mdev, ipriv->qpn); err_reset_qp: diff --git a/drivers/net/ethernet/mellanox/mlx5/core/ipoib/ipoib_vlan.c b/drivers/net/ethernet/mellanox/mlx5/core/ipoib/ipoib_vlan.c index 0227a521d301..4d9c9e49645c 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/ipoib/ipoib_vlan.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/ipoib/ipoib_vlan.c @@ -221,12 +221,16 @@ static int mlx5i_pkey_open(struct net_device *netdev) mlx5_core_warn(mdev, "opening child channels failed, %d\n", err); goto err_clear_state_opened_flag; } - epriv->profile->update_rx(epriv); + err = epriv->profile->update_rx(epriv); + if (err) + goto err_close_channels; mlx5e_activate_priv_channels(epriv); mutex_unlock(&epriv->state_lock); return 0; +err_close_channels: + mlx5e_close_channels(&epriv->channels); err_clear_state_opened_flag: mlx5e_destroy_tis(mdev, epriv->tisn[0][0]); err_remove_rx_uderlay_qp: From 989722906166173c703c1d73a26a0167fdad4136 Mon Sep 17 00:00:00 2001 From: Roi Dayan Date: Wed, 21 Sep 2022 09:17:15 +0300 Subject: [PATCH 0883/2002] net/mlx5e: TC, Remove redundant WARN_ON() The case where the packet is not offloaded and needs to be restored to slow path and couldn't find expected tunnel information should not dump a call trace to the user. there is a debug call. Signed-off-by: Roi Dayan Reviewed-by: Maor Dickman Signed-off-by: Saeed Mahameed --- drivers/net/ethernet/mellanox/mlx5/core/en/rep/tc.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/rep/tc.c b/drivers/net/ethernet/mellanox/mlx5/core/en/rep/tc.c index fac7e3ff2674..b08339d986d5 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/rep/tc.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/rep/tc.c @@ -690,7 +690,6 @@ static bool mlx5e_restore_tunnel(struct mlx5e_priv *priv, struct sk_buff *skb, err = mapping_find(uplink_priv->tunnel_mapping, tun_id, &key); if (err) { - WARN_ON_ONCE(true); netdev_dbg(priv->netdev, "Couldn't find tunnel for tun_id: %d, err: %d\n", tun_id, err); From 1f74399fd1edea2eefd8d340f5d1f7c5f5edec66 Mon Sep 17 00:00:00 2001 From: Tariq Toukan Date: Wed, 14 Sep 2022 11:02:34 +0300 Subject: [PATCH 0884/2002] net/mlx5e: kTLS, Remove unused work field Work field in struct mlx5e_async_ctx is not used. Remove it. Signed-off-by: Tariq Toukan Reviewed-by: Gal Pressman Signed-off-by: Saeed Mahameed --- drivers/net/ethernet/mellanox/mlx5/core/en_accel/ktls_tx.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ktls_tx.c b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ktls_tx.c index 2e0335246967..f8d9708feb7c 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ktls_tx.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ktls_tx.c @@ -126,7 +126,6 @@ mlx5e_get_ktls_tx_priv_ctx(struct tls_context *tls_ctx) struct mlx5e_async_ctx { struct mlx5_async_work context; struct mlx5_async_ctx async_ctx; - struct work_struct work; struct mlx5e_ktls_offload_context_tx *priv_tx; struct completion complete; int err; From 4d78a2ebbd2b40b4c14440b7417fc473120e15d6 Mon Sep 17 00:00:00 2001 From: Tariq Toukan Date: Mon, 12 Sep 2022 21:43:18 +0300 Subject: [PATCH 0885/2002] net/mlx5e: kTLS, Remove unnecessary per-callback completion Waiting on a completion object for each callback before cleaning up their async contexts is not necessary, as this is already implied in the mlx5_cmd_cleanup_async_ctx() API. Signed-off-by: Tariq Toukan Reviewed-by: Gal Pressman Signed-off-by: Saeed Mahameed --- .../ethernet/mellanox/mlx5/core/en_accel/ktls_tx.c | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ktls_tx.c b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ktls_tx.c index f8d9708feb7c..fcaa26847c8a 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ktls_tx.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ktls_tx.c @@ -127,7 +127,6 @@ struct mlx5e_async_ctx { struct mlx5_async_work context; struct mlx5_async_ctx async_ctx; struct mlx5e_ktls_offload_context_tx *priv_tx; - struct completion complete; int err; union { u32 out_create[MLX5_ST_SZ_DW(create_tis_out)]; @@ -148,7 +147,6 @@ static struct mlx5e_async_ctx *mlx5e_bulk_async_init(struct mlx5_core_dev *mdev, struct mlx5e_async_ctx *async = &bulk_async[i]; mlx5_cmd_init_async_ctx(mdev, &async->async_ctx); - init_completion(&async->complete); } return bulk_async; @@ -175,12 +173,10 @@ static void create_tis_callback(int status, struct mlx5_async_work *context) if (status) { async->err = status; priv_tx->create_err = 1; - goto out; + return; } priv_tx->tisn = MLX5_GET(create_tis_out, async->out_create, tisn); -out: - complete(&async->complete); } static void destroy_tis_callback(int status, struct mlx5_async_work *context) @@ -189,7 +185,6 @@ static void destroy_tis_callback(int status, struct mlx5_async_work *context) container_of(context, struct mlx5e_async_ctx, context); struct mlx5e_ktls_offload_context_tx *priv_tx = async->priv_tx; - complete(&async->complete); kfree(priv_tx); } @@ -231,7 +226,6 @@ static void mlx5e_tls_priv_tx_cleanup(struct mlx5e_ktls_offload_context_tx *priv struct mlx5e_async_ctx *async) { if (priv_tx->create_err) { - complete(&async->complete); kfree(priv_tx); return; } @@ -259,11 +253,6 @@ static void mlx5e_tls_priv_tx_list_cleanup(struct mlx5_core_dev *mdev, i++; } - for (i = 0; i < size; i++) { - struct mlx5e_async_ctx *async = &bulk_async[i]; - - wait_for_completion(&async->complete); - } mlx5e_bulk_async_cleanup(bulk_async, size); } @@ -310,7 +299,6 @@ static void create_work(struct work_struct *work) for (j = 0; j < i; j++) { struct mlx5e_async_ctx *async = &bulk_async[j]; - wait_for_completion(&async->complete); if (!err && async->err) err = async->err; } From 341361533011945f0a7da85632662a5879233853 Mon Sep 17 00:00:00 2001 From: Tariq Toukan Date: Wed, 14 Sep 2022 10:35:04 +0300 Subject: [PATCH 0886/2002] net/mlx5e: kTLS, Use a single async context object per a callback bulk A single async context object is sufficient to wait for the completions of many callbacks. Switch to using one instance per a bulk of commands. Signed-off-by: Tariq Toukan Reviewed-by: Gal Pressman Signed-off-by: Saeed Mahameed --- .../mellanox/mlx5/core/en_accel/ktls_tx.c | 50 +++++++++---------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ktls_tx.c b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ktls_tx.c index fcaa26847c8a..78072bf93f3f 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ktls_tx.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ktls_tx.c @@ -125,7 +125,7 @@ mlx5e_get_ktls_tx_priv_ctx(struct tls_context *tls_ctx) /* struct for callback API management */ struct mlx5e_async_ctx { struct mlx5_async_work context; - struct mlx5_async_ctx async_ctx; + struct mlx5_async_ctx *async_ctx; struct mlx5e_ktls_offload_context_tx *priv_tx; int err; union { @@ -134,33 +134,33 @@ struct mlx5e_async_ctx { }; }; -static struct mlx5e_async_ctx *mlx5e_bulk_async_init(struct mlx5_core_dev *mdev, int n) +struct mlx5e_bulk_async_ctx { + struct mlx5_async_ctx async_ctx; + DECLARE_FLEX_ARRAY(struct mlx5e_async_ctx, arr); +}; + +static struct mlx5e_bulk_async_ctx *mlx5e_bulk_async_init(struct mlx5_core_dev *mdev, int n) { - struct mlx5e_async_ctx *bulk_async; + struct mlx5e_bulk_async_ctx *bulk_async; + int sz; int i; - bulk_async = kvcalloc(n, sizeof(struct mlx5e_async_ctx), GFP_KERNEL); + sz = struct_size(bulk_async, arr, n); + bulk_async = kvzalloc(sz, GFP_KERNEL); if (!bulk_async) return NULL; - for (i = 0; i < n; i++) { - struct mlx5e_async_ctx *async = &bulk_async[i]; + mlx5_cmd_init_async_ctx(mdev, &bulk_async->async_ctx); - mlx5_cmd_init_async_ctx(mdev, &async->async_ctx); - } + for (i = 0; i < n; i++) + bulk_async->arr[i].async_ctx = &bulk_async->async_ctx; return bulk_async; } -static void mlx5e_bulk_async_cleanup(struct mlx5e_async_ctx *bulk_async, int n) +static void mlx5e_bulk_async_cleanup(struct mlx5e_bulk_async_ctx *bulk_async) { - int i; - - for (i = 0; i < n; i++) { - struct mlx5e_async_ctx *async = &bulk_async[i]; - - mlx5_cmd_cleanup_async_ctx(&async->async_ctx); - } + mlx5_cmd_cleanup_async_ctx(&bulk_async->async_ctx); kvfree(bulk_async); } @@ -208,7 +208,7 @@ mlx5e_tls_priv_tx_init(struct mlx5_core_dev *mdev, struct mlx5e_tls_sw_stats *sw goto err_out; } else { async->priv_tx = priv_tx; - err = mlx5e_ktls_create_tis_cb(mdev, &async->async_ctx, + err = mlx5e_ktls_create_tis_cb(mdev, async->async_ctx, async->out_create, sizeof(async->out_create), create_tis_callback, &async->context); if (err) @@ -231,7 +231,7 @@ static void mlx5e_tls_priv_tx_cleanup(struct mlx5e_ktls_offload_context_tx *priv } async->priv_tx = priv_tx; mlx5e_ktls_destroy_tis_cb(priv_tx->mdev, priv_tx->tisn, - &async->async_ctx, + async->async_ctx, async->out_destroy, sizeof(async->out_destroy), destroy_tis_callback, &async->context); } @@ -240,7 +240,7 @@ static void mlx5e_tls_priv_tx_list_cleanup(struct mlx5_core_dev *mdev, struct list_head *list, int size) { struct mlx5e_ktls_offload_context_tx *obj, *n; - struct mlx5e_async_ctx *bulk_async; + struct mlx5e_bulk_async_ctx *bulk_async; int i; bulk_async = mlx5e_bulk_async_init(mdev, size); @@ -249,11 +249,11 @@ static void mlx5e_tls_priv_tx_list_cleanup(struct mlx5_core_dev *mdev, i = 0; list_for_each_entry_safe(obj, n, list, list_node) { - mlx5e_tls_priv_tx_cleanup(obj, &bulk_async[i]); + mlx5e_tls_priv_tx_cleanup(obj, &bulk_async->arr[i]); i++; } - mlx5e_bulk_async_cleanup(bulk_async, size); + mlx5e_bulk_async_cleanup(bulk_async); } /* Recycling pool API */ @@ -279,7 +279,7 @@ static void create_work(struct work_struct *work) struct mlx5e_tls_tx_pool *pool = container_of(work, struct mlx5e_tls_tx_pool, create_work); struct mlx5e_ktls_offload_context_tx *obj; - struct mlx5e_async_ctx *bulk_async; + struct mlx5e_bulk_async_ctx *bulk_async; LIST_HEAD(local_list); int i, j, err = 0; @@ -288,7 +288,7 @@ static void create_work(struct work_struct *work) return; for (i = 0; i < MLX5E_TLS_TX_POOL_BULK; i++) { - obj = mlx5e_tls_priv_tx_init(pool->mdev, pool->sw_stats, &bulk_async[i]); + obj = mlx5e_tls_priv_tx_init(pool->mdev, pool->sw_stats, &bulk_async->arr[i]); if (IS_ERR(obj)) { err = PTR_ERR(obj); break; @@ -297,13 +297,13 @@ static void create_work(struct work_struct *work) } for (j = 0; j < i; j++) { - struct mlx5e_async_ctx *async = &bulk_async[j]; + struct mlx5e_async_ctx *async = &bulk_async->arr[j]; if (!err && async->err) err = async->err; } atomic64_add(i, &pool->sw_stats->tx_tls_pool_alloc); - mlx5e_bulk_async_cleanup(bulk_async, MLX5E_TLS_TX_POOL_BULK); + mlx5e_bulk_async_cleanup(bulk_async); if (err) goto err_out; From 05bb74c29d84dc6d26f57f5ed3970ab8aeb13551 Mon Sep 17 00:00:00 2001 From: Oz Shlomo Date: Mon, 31 Oct 2022 09:00:30 +0000 Subject: [PATCH 0887/2002] net/mlx5e: CT, optimize pre_ct table lookup The pre_ct table realizes in hardware the act_ct cache logic, bypassing the CT table if the ct state was already set by a previous ct lookup. As such, the pre_ct table will always miss for chain 0 filters. Optimize the pre_ct table lookup for rules installed on chain 0. Signed-off-by: Oz Shlomo Reviewed-by: Roi Dayan Signed-off-by: Saeed Mahameed --- .../ethernet/mellanox/mlx5/core/en/tc_ct.c | 89 ++++++++++++------- 1 file changed, 56 insertions(+), 33 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/tc_ct.c b/drivers/net/ethernet/mellanox/mlx5/core/en/tc_ct.c index 864ce0c393e6..a69849e0deed 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/tc_ct.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/tc_ct.c @@ -1774,35 +1774,42 @@ mlx5_tc_ct_del_ft_cb(struct mlx5_tc_ct_priv *ct_priv, struct mlx5_ct_ft *ft) /* We translate the tc filter with CT action to the following HW model: * - * +---------------------+ - * + ft prio (tc chain) + - * + original match + - * +---------------------+ - * | set chain miss mapping - * | set fte_id - * | set tunnel_id - * | do decap - * v - * +---------------------+ - * + pre_ct/pre_ct_nat + if matches +-------------------------+ - * + zone+nat match +---------------->+ post_act (see below) + - * +---------------------+ set zone +-------------------------+ - * | set zone - * v - * +--------------------+ - * + CT (nat or no nat) + - * + tuple + zone match + - * +--------------------+ - * | set mark - * | set labels_id - * | set established - * | set zone_restore - * | do nat (if needed) - * v - * +--------------+ - * + post_act + original filter actions - * + fte_id match +------------------------> - * +--------------+ + * +---------------------+ + * + ft prio (tc chain) + + * + original match + + * +---------------------+ + * | set chain miss mapping + * | set fte_id + * | set tunnel_id + * | do decap + * | + * +-------------+ + * | Chain 0 | + * | optimization| + * | v + * | +---------------------+ + * | + pre_ct/pre_ct_nat + if matches +----------------------+ + * | + zone+nat match +---------------->+ post_act (see below) + + * | +---------------------+ set zone +----------------------+ + * | | + * +-------------+ set zone + * | + * v + * +--------------------+ + * + CT (nat or no nat) + + * + tuple + zone match + + * +--------------------+ + * | set mark + * | set labels_id + * | set established + * | set zone_restore + * | do nat (if needed) + * v + * +--------------+ + * + post_act + original filter actions + * + fte_id match +------------------------> + * +--------------+ + * */ static struct mlx5_flow_handle * __mlx5_tc_ct_flow_offload(struct mlx5_tc_ct_priv *ct_priv, @@ -1818,6 +1825,7 @@ __mlx5_tc_ct_flow_offload(struct mlx5_tc_ct_priv *ct_priv, struct mlx5_ct_flow *ct_flow; int chain_mapping = 0, err; struct mlx5_ct_ft *ft; + u16 zone; ct_flow = kzalloc(sizeof(*ct_flow), GFP_KERNEL); if (!ct_flow) { @@ -1884,6 +1892,25 @@ __mlx5_tc_ct_flow_offload(struct mlx5_tc_ct_priv *ct_priv, } } + /* Change original rule point to ct table + * Chain 0 sets the zone and jumps to ct table + * Other chains jump to pre_ct table to align with act_ct cached logic + */ + pre_ct_attr->dest_chain = 0; + if (!attr->chain) { + zone = ft->zone & MLX5_CT_ZONE_MASK; + err = mlx5e_tc_match_to_reg_set(priv->mdev, pre_mod_acts, ct_priv->ns_type, + ZONE_TO_REG, zone); + if (err) { + ct_dbg("Failed to set zone register mapping"); + goto err_mapping; + } + + pre_ct_attr->dest_ft = nat ? ct_priv->ct_nat : ct_priv->ct; + } else { + pre_ct_attr->dest_ft = nat ? ft->pre_ct_nat.ft : ft->pre_ct.ft; + } + mod_hdr = mlx5_modify_header_alloc(priv->mdev, ct_priv->ns_type, pre_mod_acts->num_actions, pre_mod_acts->actions); @@ -1893,10 +1920,6 @@ __mlx5_tc_ct_flow_offload(struct mlx5_tc_ct_priv *ct_priv, goto err_mapping; } pre_ct_attr->modify_hdr = mod_hdr; - - /* Change original rule point to ct table */ - pre_ct_attr->dest_chain = 0; - pre_ct_attr->dest_ft = nat ? ft->pre_ct_nat.ft : ft->pre_ct.ft; ct_flow->pre_ct_rule = mlx5_tc_rule_insert(priv, orig_spec, pre_ct_attr); if (IS_ERR(ct_flow->pre_ct_rule)) { From e07c4924a77dbf21bf1973411149784cfa5d3b27 Mon Sep 17 00:00:00 2001 From: Saeed Mahameed Date: Wed, 26 Oct 2022 11:26:31 +0100 Subject: [PATCH 0888/2002] net/mlx5e: ethtool: get_link_ext_stats for PHY down events Implement ethtool_op get_link_ext_stats for PHY down events Signed-off-by: Saeed Mahameed Reviewed-by: Gal Pressman --- .../ethernet/mellanox/mlx5/core/en_ethtool.c | 1 + .../net/ethernet/mellanox/mlx5/core/en_stats.c | 17 +++++++++++++++++ .../net/ethernet/mellanox/mlx5/core/en_stats.h | 2 ++ 3 files changed, 20 insertions(+) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c b/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c index 24aa25da482b..d9397ffb396b 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c @@ -2463,4 +2463,5 @@ const struct ethtool_ops mlx5e_ethtool_ops = { .get_eth_mac_stats = mlx5e_get_eth_mac_stats, .get_eth_ctrl_stats = mlx5e_get_eth_ctrl_stats, .get_rmon_stats = mlx5e_get_rmon_stats, + .get_link_ext_stats = mlx5e_get_link_ext_stats }; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_stats.c b/drivers/net/ethernet/mellanox/mlx5/core/en_stats.c index 03c1841970f1..70c4ea3841d7 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_stats.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_stats.c @@ -1241,6 +1241,23 @@ static MLX5E_DECLARE_STATS_GRP_OP_UPDATE_STATS(phy) mlx5_core_access_reg(mdev, in, sz, out, sz, MLX5_REG_PPCNT, 0, 0); } +void mlx5e_get_link_ext_stats(struct net_device *dev, + struct ethtool_link_ext_stats *stats) +{ + struct mlx5e_priv *priv = netdev_priv(dev); + u32 out[MLX5_ST_SZ_DW(ppcnt_reg)] = {}; + u32 in[MLX5_ST_SZ_DW(ppcnt_reg)] = {}; + int sz = MLX5_ST_SZ_BYTES(ppcnt_reg); + + MLX5_SET(ppcnt_reg, in, local_port, 1); + MLX5_SET(ppcnt_reg, in, grp, MLX5_PHYSICAL_LAYER_COUNTERS_GROUP); + mlx5_core_access_reg(priv->mdev, in, sz, out, + MLX5_ST_SZ_BYTES(ppcnt_reg), MLX5_REG_PPCNT, 0, 0); + + stats->link_down_events = MLX5_GET(ppcnt_reg, out, + counter_set.phys_layer_cntrs.link_down_events); +} + static int fec_num_lanes(struct mlx5_core_dev *dev) { u32 out[MLX5_ST_SZ_DW(pmlp_reg)] = {}; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_stats.h b/drivers/net/ethernet/mellanox/mlx5/core/en_stats.h index 9f781085be47..cbc831ca646b 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_stats.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_stats.h @@ -126,6 +126,8 @@ void mlx5e_stats_eth_ctrl_get(struct mlx5e_priv *priv, void mlx5e_stats_rmon_get(struct mlx5e_priv *priv, struct ethtool_rmon_stats *rmon, const struct ethtool_rmon_hist_range **ranges); +void mlx5e_get_link_ext_stats(struct net_device *dev, + struct ethtool_link_ext_stats *stats); /* Concrete NIC Stats */ From 41cf3a9156ba8e13e557e7908f9e22563b1f2828 Mon Sep 17 00:00:00 2001 From: David Howells Date: Thu, 10 Nov 2022 09:39:39 +0000 Subject: [PATCH 0889/2002] rxrpc: Fix missing IPV6 #ifdef Fix rxrpc_encap_err_rcv() to make the call to ipv6_icmp_error conditional on IPV6 support being enabled. Fixes: b6c66c4324e7 ("rxrpc: Use the core ICMP/ICMP6 parsers") Reported-by: kernel test robot Signed-off-by: David Howells cc: Marc Dionne cc: linux-afs@lists.infradead.org cc: netdev@vger.kernel.org --- net/rxrpc/local_object.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/net/rxrpc/local_object.c b/net/rxrpc/local_object.c index a178f71e5082..a943fdf91e24 100644 --- a/net/rxrpc/local_object.c +++ b/net/rxrpc/local_object.c @@ -33,7 +33,8 @@ static void rxrpc_encap_err_rcv(struct sock *sk, struct sk_buff *skb, int err, { if (ip_hdr(skb)->version == IPVERSION) return ip_icmp_error(sk, skb, err, port, info, payload); - return ipv6_icmp_error(sk, skb, err, port, info, payload); + if (IS_ENABLED(CONFIG_AF_RXRPC_IPV6)) + return ipv6_icmp_error(sk, skb, err, port, info, payload); } /* From ab00af85d2f886a8e4ace1342d9cc2b232eab6a8 Mon Sep 17 00:00:00 2001 From: Chuang Wang Date: Thu, 10 Nov 2022 15:31:25 +0800 Subject: [PATCH 0890/2002] net: tun: rebuild error handling in tun_get_user The error handling in tun_get_user is very scattered. This patch unifies error handling, reduces duplication of code, and makes the logic clearer. Signed-off-by: Chuang Wang Signed-off-by: David S. Miller --- drivers/net/tun.c | 65 +++++++++++++++++++++-------------------------- 1 file changed, 29 insertions(+), 36 deletions(-) diff --git a/drivers/net/tun.c b/drivers/net/tun.c index addef005ba37..f5f412ef39b3 100644 --- a/drivers/net/tun.c +++ b/drivers/net/tun.c @@ -1746,7 +1746,7 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile, u32 rxhash = 0; int skb_xdp = 1; bool frags = tun_napi_frags_enabled(tfile); - enum skb_drop_reason drop_reason; + enum skb_drop_reason drop_reason = SKB_DROP_REASON_NOT_SPECIFIED; if (!(tun->flags & IFF_NO_PI)) { if (len < sizeof(pi)) @@ -1807,10 +1807,9 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile, * skb was created with generic XDP routine. */ skb = tun_build_skb(tun, tfile, from, &gso, len, &skb_xdp); - if (IS_ERR(skb)) { - dev_core_stats_rx_dropped_inc(tun->dev); - return PTR_ERR(skb); - } + err = PTR_ERR_OR_ZERO(skb); + if (err) + goto drop; if (!skb) return total_len; } else { @@ -1835,13 +1834,9 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile, noblock); } - if (IS_ERR(skb)) { - if (PTR_ERR(skb) != -EAGAIN) - dev_core_stats_rx_dropped_inc(tun->dev); - if (frags) - mutex_unlock(&tfile->napi_mutex); - return PTR_ERR(skb); - } + err = PTR_ERR_OR_ZERO(skb); + if (err) + goto drop; if (zerocopy) err = zerocopy_sg_from_iter(skb, from); @@ -1851,27 +1846,14 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile, if (err) { err = -EFAULT; drop_reason = SKB_DROP_REASON_SKB_UCOPY_FAULT; -drop: - dev_core_stats_rx_dropped_inc(tun->dev); - kfree_skb_reason(skb, drop_reason); - if (frags) { - tfile->napi.skb = NULL; - mutex_unlock(&tfile->napi_mutex); - } - - return err; + goto drop; } } if (virtio_net_hdr_to_skb(skb, &gso, tun_is_little_endian(tun))) { atomic_long_inc(&tun->rx_frame_errors); - kfree_skb(skb); - if (frags) { - tfile->napi.skb = NULL; - mutex_unlock(&tfile->napi_mutex); - } - - return -EINVAL; + err = -EINVAL; + goto free_skb; } switch (tun->flags & TUN_TYPE_MASK) { @@ -1887,9 +1869,8 @@ drop: pi.proto = htons(ETH_P_IPV6); break; default: - dev_core_stats_rx_dropped_inc(tun->dev); - kfree_skb(skb); - return -EINVAL; + err = -EINVAL; + goto drop; } } @@ -1931,11 +1912,7 @@ drop: if (ret != XDP_PASS) { rcu_read_unlock(); local_bh_enable(); - if (frags) { - tfile->napi.skb = NULL; - mutex_unlock(&tfile->napi_mutex); - } - return total_len; + goto unlock_frags; } } rcu_read_unlock(); @@ -2015,6 +1992,22 @@ napi_busy: tun_flow_update(tun, rxhash, tfile); return total_len; + +drop: + if (err != -EAGAIN) + dev_core_stats_rx_dropped_inc(tun->dev); + +free_skb: + if (!IS_ERR_OR_NULL(skb)) + kfree_skb_reason(skb, drop_reason); + +unlock_frags: + if (frags) { + tfile->napi.skb = NULL; + mutex_unlock(&tfile->napi_mutex); + } + + return err ?: total_len; } static ssize_t tun_chr_write_iter(struct kiocb *iocb, struct iov_iter *from) From e384cf35bf0c83284eb37785d21d39bbe2568adf Mon Sep 17 00:00:00 2001 From: Md Fahad Iqbal Polash Date: Thu, 10 Nov 2022 14:03:53 +0100 Subject: [PATCH 0891/2002] ice: virtchnl rss hena support Add support for 2 virtchnl msgs: VIRTCHNL_OP_SET_RSS_HENA VIRTCHNL_OP_GET_RSS_HENA_CAPS The first one allows VFs to clear all previously programmed RSS configuration and customize it. The second one returns the RSS HENA bits allowed by the hardware. Introduce ice_err_to_virt_err which converts kernel specific errors to virtchnl errors. Signed-off-by: Md Fahad Iqbal Polash Signed-off-by: Wojciech Drewek Reviewed-by: Michal Swiatkowski Signed-off-by: David S. Miller --- drivers/net/ethernet/intel/ice/ice_vf_lib.c | 24 ++++ .../ethernet/intel/ice/ice_vf_lib_private.h | 1 + drivers/net/ethernet/intel/ice/ice_virtchnl.c | 112 ++++++++++++++++++ drivers/net/ethernet/intel/ice/ice_virtchnl.h | 2 + 4 files changed, 139 insertions(+) diff --git a/drivers/net/ethernet/intel/ice/ice_vf_lib.c b/drivers/net/ethernet/intel/ice/ice_vf_lib.c index 1c51778db951..375eb6493f0f 100644 --- a/drivers/net/ethernet/intel/ice/ice_vf_lib.c +++ b/drivers/net/ethernet/intel/ice/ice_vf_lib.c @@ -699,6 +699,30 @@ void ice_dis_vf_qs(struct ice_vf *vf) ice_set_vf_state_qs_dis(vf); } +/** + * ice_err_to_virt_err - translate errors for VF return code + * @err: error return code + */ +enum virtchnl_status_code ice_err_to_virt_err(int err) +{ + switch (err) { + case 0: + return VIRTCHNL_STATUS_SUCCESS; + case -EINVAL: + case -ENODEV: + return VIRTCHNL_STATUS_ERR_PARAM; + case -ENOMEM: + return VIRTCHNL_STATUS_ERR_NO_MEMORY; + case -EALREADY: + case -EBUSY: + case -EIO: + case -ENOSPC: + return VIRTCHNL_STATUS_ERR_ADMIN_QUEUE_ERROR; + default: + return VIRTCHNL_STATUS_ERR_NOT_SUPPORTED; + } +} + /** * ice_check_vf_init - helper to check if VF init complete * @vf: the pointer to the VF to check diff --git a/drivers/net/ethernet/intel/ice/ice_vf_lib_private.h b/drivers/net/ethernet/intel/ice/ice_vf_lib_private.h index 15887e772c76..9c8ef2b01f0f 100644 --- a/drivers/net/ethernet/intel/ice/ice_vf_lib_private.h +++ b/drivers/net/ethernet/intel/ice/ice_vf_lib_private.h @@ -25,6 +25,7 @@ void ice_dis_vf_qs(struct ice_vf *vf); int ice_check_vf_init(struct ice_vf *vf); +enum virtchnl_status_code ice_err_to_virt_err(int err); struct ice_port_info *ice_vf_get_port_info(struct ice_vf *vf); int ice_vsi_apply_spoofchk(struct ice_vsi *vsi, bool enable); bool ice_is_vf_trusted(struct ice_vf *vf); diff --git a/drivers/net/ethernet/intel/ice/ice_virtchnl.c b/drivers/net/ethernet/intel/ice/ice_virtchnl.c index c1fa94381f4e..ec90e594986e 100644 --- a/drivers/net/ethernet/intel/ice/ice_virtchnl.c +++ b/drivers/net/ethernet/intel/ice/ice_virtchnl.c @@ -2481,6 +2481,108 @@ error_param: v_ret, NULL, 0); } +/** + * ice_vc_get_rss_hena - return the RSS HENA bits allowed by the hardware + * @vf: pointer to the VF info + */ +static int ice_vc_get_rss_hena(struct ice_vf *vf) +{ + enum virtchnl_status_code v_ret = VIRTCHNL_STATUS_SUCCESS; + struct virtchnl_rss_hena *vrh = NULL; + int len = 0, ret; + + if (!test_bit(ICE_VF_STATE_ACTIVE, vf->vf_states)) { + v_ret = VIRTCHNL_STATUS_ERR_PARAM; + goto err; + } + + if (!test_bit(ICE_FLAG_RSS_ENA, vf->pf->flags)) { + dev_err(ice_pf_to_dev(vf->pf), "RSS not supported by PF\n"); + v_ret = VIRTCHNL_STATUS_ERR_PARAM; + goto err; + } + + len = sizeof(struct virtchnl_rss_hena); + vrh = kzalloc(len, GFP_KERNEL); + if (!vrh) { + v_ret = VIRTCHNL_STATUS_ERR_NO_MEMORY; + len = 0; + goto err; + } + + vrh->hena = ICE_DEFAULT_RSS_HENA; +err: + /* send the response back to the VF */ + ret = ice_vc_send_msg_to_vf(vf, VIRTCHNL_OP_GET_RSS_HENA_CAPS, v_ret, + (u8 *)vrh, len); + kfree(vrh); + return ret; +} + +/** + * ice_vc_set_rss_hena - set RSS HENA bits for the VF + * @vf: pointer to the VF info + * @msg: pointer to the msg buffer + */ +static int ice_vc_set_rss_hena(struct ice_vf *vf, u8 *msg) +{ + struct virtchnl_rss_hena *vrh = (struct virtchnl_rss_hena *)msg; + enum virtchnl_status_code v_ret = VIRTCHNL_STATUS_SUCCESS; + struct ice_pf *pf = vf->pf; + struct ice_vsi *vsi; + struct device *dev; + int status; + + dev = ice_pf_to_dev(pf); + + if (!test_bit(ICE_VF_STATE_ACTIVE, vf->vf_states)) { + v_ret = VIRTCHNL_STATUS_ERR_PARAM; + goto err; + } + + if (!test_bit(ICE_FLAG_RSS_ENA, pf->flags)) { + dev_err(dev, "RSS not supported by PF\n"); + v_ret = VIRTCHNL_STATUS_ERR_PARAM; + goto err; + } + + vsi = ice_get_vf_vsi(vf); + if (!vsi) { + v_ret = VIRTCHNL_STATUS_ERR_PARAM; + goto err; + } + + /* clear all previously programmed RSS configuration to allow VF drivers + * the ability to customize the RSS configuration and/or completely + * disable RSS + */ + status = ice_rem_vsi_rss_cfg(&pf->hw, vsi->idx); + if (status && !vrh->hena) { + /* only report failure to clear the current RSS configuration if + * that was clearly the VF's intention (i.e. vrh->hena = 0) + */ + v_ret = ice_err_to_virt_err(status); + goto err; + } else if (status) { + /* allow the VF to update the RSS configuration even on failure + * to clear the current RSS confguration in an attempt to keep + * RSS in a working state + */ + dev_warn(dev, "Failed to clear the RSS configuration for VF %u\n", + vf->vf_id); + } + + if (vrh->hena) { + status = ice_add_avf_rss_cfg(&pf->hw, vsi->idx, vrh->hena); + v_ret = ice_err_to_virt_err(status); + } + + /* send the response to the VF */ +err: + return ice_vc_send_msg_to_vf(vf, VIRTCHNL_OP_SET_RSS_HENA, v_ret, + NULL, 0); +} + /** * ice_vc_query_rxdid - query RXDID supported by DDP package * @vf: pointer to VF info @@ -3572,6 +3674,8 @@ static const struct ice_virtchnl_ops ice_virtchnl_dflt_ops = { .add_vlan_msg = ice_vc_add_vlan_msg, .remove_vlan_msg = ice_vc_remove_vlan_msg, .query_rxdid = ice_vc_query_rxdid, + .get_rss_hena = ice_vc_get_rss_hena, + .set_rss_hena_msg = ice_vc_set_rss_hena, .ena_vlan_stripping = ice_vc_ena_vlan_stripping, .dis_vlan_stripping = ice_vc_dis_vlan_stripping, .handle_rss_cfg_msg = ice_vc_handle_rss_cfg, @@ -3707,6 +3811,8 @@ static const struct ice_virtchnl_ops ice_virtchnl_repr_ops = { .add_vlan_msg = ice_vc_add_vlan_msg, .remove_vlan_msg = ice_vc_remove_vlan_msg, .query_rxdid = ice_vc_query_rxdid, + .get_rss_hena = ice_vc_get_rss_hena, + .set_rss_hena_msg = ice_vc_set_rss_hena, .ena_vlan_stripping = ice_vc_ena_vlan_stripping, .dis_vlan_stripping = ice_vc_dis_vlan_stripping, .handle_rss_cfg_msg = ice_vc_handle_rss_cfg, @@ -3850,6 +3956,12 @@ error_handler: case VIRTCHNL_OP_GET_SUPPORTED_RXDIDS: err = ops->query_rxdid(vf); break; + case VIRTCHNL_OP_GET_RSS_HENA_CAPS: + err = ops->get_rss_hena(vf); + break; + case VIRTCHNL_OP_SET_RSS_HENA: + err = ops->set_rss_hena_msg(vf, msg); + break; case VIRTCHNL_OP_ENABLE_VLAN_STRIPPING: err = ops->ena_vlan_stripping(vf); break; diff --git a/drivers/net/ethernet/intel/ice/ice_virtchnl.h b/drivers/net/ethernet/intel/ice/ice_virtchnl.h index 4867a92ebefb..b454654d7b0c 100644 --- a/drivers/net/ethernet/intel/ice/ice_virtchnl.h +++ b/drivers/net/ethernet/intel/ice/ice_virtchnl.h @@ -37,6 +37,8 @@ struct ice_virtchnl_ops { int (*add_vlan_msg)(struct ice_vf *vf, u8 *msg); int (*remove_vlan_msg)(struct ice_vf *vf, u8 *msg); int (*query_rxdid)(struct ice_vf *vf); + int (*get_rss_hena)(struct ice_vf *vf); + int (*set_rss_hena_msg)(struct ice_vf *vf, u8 *msg); int (*ena_vlan_stripping)(struct ice_vf *vf); int (*dis_vlan_stripping)(struct ice_vf *vf); int (*handle_rss_cfg_msg)(struct ice_vf *vf, u8 *msg, bool add); From 44fbc1b6e0e291df1d31f5080777ce66a72ef37a Mon Sep 17 00:00:00 2001 From: Nick Child Date: Thu, 10 Nov 2022 15:32:16 -0600 Subject: [PATCH 0892/2002] ibmvnic: Assign IRQ affinity hints to device queues Assign affinity hints to ibmvnic device queue interrupts. Affinity hints are assigned and removed during sub-crq init and teardown, respectively. This update should improve latency if utilized as interrupt lines and processing are more equally distributed among CPU's. This implementation is based on the virtio_net driver. Signed-off-by: Thomas Falcon Signed-off-by: Dany Madden Signed-off-by: Nick Child Reviewed-by: Rick Lindsley Reviewed-by: Haren Myneni Signed-off-by: David S. Miller --- drivers/net/ethernet/ibm/ibmvnic.c | 140 +++++++++++++++++++++++++++++ drivers/net/ethernet/ibm/ibmvnic.h | 1 + 2 files changed, 141 insertions(+) diff --git a/drivers/net/ethernet/ibm/ibmvnic.c b/drivers/net/ethernet/ibm/ibmvnic.c index 9282381a438f..0c969bdaf94d 100644 --- a/drivers/net/ethernet/ibm/ibmvnic.c +++ b/drivers/net/ethernet/ibm/ibmvnic.c @@ -68,6 +68,7 @@ #include #include #include +#include #include "ibmvnic.h" @@ -171,6 +172,132 @@ static int send_version_xchg(struct ibmvnic_adapter *adapter) return ibmvnic_send_crq(adapter, &crq); } +static void ibmvnic_clean_queue_affinity(struct ibmvnic_adapter *adapter, + struct ibmvnic_sub_crq_queue *queue) +{ + if (!(queue && queue->irq)) + return; + + cpumask_clear(queue->affinity_mask); + + if (irq_set_affinity_and_hint(queue->irq, NULL)) + netdev_warn(adapter->netdev, + "%s: Clear affinity failed, queue addr = %p, IRQ = %d\n", + __func__, queue, queue->irq); +} + +static void ibmvnic_clean_affinity(struct ibmvnic_adapter *adapter) +{ + struct ibmvnic_sub_crq_queue **rxqs; + struct ibmvnic_sub_crq_queue **txqs; + int num_rxqs, num_txqs; + int rc, i; + + rc = 0; + rxqs = adapter->rx_scrq; + txqs = adapter->tx_scrq; + num_txqs = adapter->num_active_tx_scrqs; + num_rxqs = adapter->num_active_rx_scrqs; + + netdev_dbg(adapter->netdev, "%s: Cleaning irq affinity hints", __func__); + if (txqs) { + for (i = 0; i < num_txqs; i++) + ibmvnic_clean_queue_affinity(adapter, txqs[i]); + } + if (rxqs) { + for (i = 0; i < num_rxqs; i++) + ibmvnic_clean_queue_affinity(adapter, rxqs[i]); + } +} + +static int ibmvnic_set_queue_affinity(struct ibmvnic_sub_crq_queue *queue, + unsigned int *cpu, int *stragglers, + int stride) +{ + cpumask_var_t mask; + int i; + int rc = 0; + + if (!(queue && queue->irq)) + return rc; + + /* cpumask_var_t is either a pointer or array, allocation works here */ + if (!zalloc_cpumask_var(&mask, GFP_KERNEL)) + return -ENOMEM; + + /* while we have extra cpu give one extra to this irq */ + if (*stragglers) { + stride++; + (*stragglers)--; + } + /* atomic write is safer than writing bit by bit directly */ + for (i = 0; i < stride; i++) { + cpumask_set_cpu(*cpu, mask); + *cpu = cpumask_next_wrap(*cpu, cpu_online_mask, + nr_cpu_ids, false); + } + /* set queue affinity mask */ + cpumask_copy(queue->affinity_mask, mask); + rc = irq_set_affinity_and_hint(queue->irq, queue->affinity_mask); + free_cpumask_var(mask); + + return rc; +} + +/* assumes cpu read lock is held */ +static void ibmvnic_set_affinity(struct ibmvnic_adapter *adapter) +{ + struct ibmvnic_sub_crq_queue **rxqs = adapter->rx_scrq; + struct ibmvnic_sub_crq_queue **txqs = adapter->tx_scrq; + struct ibmvnic_sub_crq_queue *queue; + int num_rxqs = adapter->num_active_rx_scrqs; + int num_txqs = adapter->num_active_tx_scrqs; + int total_queues, stride, stragglers, i; + unsigned int num_cpu, cpu; + int rc = 0; + + netdev_dbg(adapter->netdev, "%s: Setting irq affinity hints", __func__); + if (!(adapter->rx_scrq && adapter->tx_scrq)) { + netdev_warn(adapter->netdev, + "%s: Set affinity failed, queues not allocated\n", + __func__); + return; + } + + total_queues = num_rxqs + num_txqs; + num_cpu = num_online_cpus(); + /* number of cpu's assigned per irq */ + stride = max_t(int, num_cpu / total_queues, 1); + /* number of leftover cpu's */ + stragglers = num_cpu >= total_queues ? num_cpu % total_queues : 0; + /* next available cpu to assign irq to */ + cpu = cpumask_next(-1, cpu_online_mask); + + for (i = 0; i < num_txqs; i++) { + queue = txqs[i]; + rc = ibmvnic_set_queue_affinity(queue, &cpu, &stragglers, + stride); + if (rc) + goto out; + } + + for (i = 0; i < num_rxqs; i++) { + queue = rxqs[i]; + rc = ibmvnic_set_queue_affinity(queue, &cpu, &stragglers, + stride); + if (rc) + goto out; + } + +out: + if (rc) { + netdev_warn(adapter->netdev, + "%s: Set affinity failed, queue addr = %p, IRQ = %d, rc = %d.\n", + __func__, queue, queue->irq, rc); + ibmvnic_clean_affinity(adapter); + } +} + static long h_reg_sub_crq(unsigned long unit_address, unsigned long token, unsigned long length, unsigned long *number, unsigned long *irq) @@ -3626,6 +3753,8 @@ static int reset_sub_crq_queues(struct ibmvnic_adapter *adapter) if (!adapter->tx_scrq || !adapter->rx_scrq) return -EINVAL; + ibmvnic_clean_affinity(adapter); + for (i = 0; i < adapter->req_tx_queues; i++) { netdev_dbg(adapter->netdev, "Re-setting tx_scrq[%d]\n", i); rc = reset_one_sub_crq_queue(adapter, adapter->tx_scrq[i]); @@ -3675,6 +3804,7 @@ static void release_sub_crq_queue(struct ibmvnic_adapter *adapter, dma_unmap_single(dev, scrq->msg_token, 4 * PAGE_SIZE, DMA_BIDIRECTIONAL); free_pages((unsigned long)scrq->msgs, 2); + free_cpumask_var(scrq->affinity_mask); kfree(scrq); } @@ -3695,6 +3825,8 @@ static struct ibmvnic_sub_crq_queue *init_sub_crq_queue(struct ibmvnic_adapter dev_warn(dev, "Couldn't allocate crq queue messages page\n"); goto zero_page_failed; } + if (!zalloc_cpumask_var(&scrq->affinity_mask, GFP_KERNEL)) + goto cpumask_alloc_failed; scrq->msg_token = dma_map_single(dev, scrq->msgs, 4 * PAGE_SIZE, DMA_BIDIRECTIONAL); @@ -3747,6 +3879,8 @@ reg_failed: dma_unmap_single(dev, scrq->msg_token, 4 * PAGE_SIZE, DMA_BIDIRECTIONAL); map_failed: + free_cpumask_var(scrq->affinity_mask); +cpumask_alloc_failed: free_pages((unsigned long)scrq->msgs, 2); zero_page_failed: kfree(scrq); @@ -3758,6 +3892,7 @@ static void release_sub_crqs(struct ibmvnic_adapter *adapter, bool do_h_free) { int i; + ibmvnic_clean_affinity(adapter); if (adapter->tx_scrq) { for (i = 0; i < adapter->num_active_tx_scrqs; i++) { if (!adapter->tx_scrq[i]) @@ -4035,6 +4170,11 @@ static int init_sub_crq_irqs(struct ibmvnic_adapter *adapter) goto req_rx_irq_failed; } } + + cpus_read_lock(); + ibmvnic_set_affinity(adapter); + cpus_read_unlock(); + return rc; req_rx_irq_failed: diff --git a/drivers/net/ethernet/ibm/ibmvnic.h b/drivers/net/ethernet/ibm/ibmvnic.h index e5c6ff3d0c47..6720fec1ae67 100644 --- a/drivers/net/ethernet/ibm/ibmvnic.h +++ b/drivers/net/ethernet/ibm/ibmvnic.h @@ -825,6 +825,7 @@ struct ibmvnic_sub_crq_queue { atomic_t used; char name[32]; u64 handle; + cpumask_var_t affinity_mask; } ____cacheline_aligned; struct ibmvnic_long_term_buff { From 92125c3a602454824d70edb1b2abb382811cab4f Mon Sep 17 00:00:00 2001 From: Nick Child Date: Thu, 10 Nov 2022 15:32:17 -0600 Subject: [PATCH 0893/2002] ibmvnic: Add hotpluggable CPU callbacks to reassign affinity hints When CPU's are added and removed, ibmvnic devices will reassign hint values. Introduce a new cpu hotplug state CPUHP_IBMVNIC_DEAD to signal to ibmvnic devices that the CPU has been removed and it is time to reset affinity hint assignments. On the other hand, when CPU's are being added, add a state instance to CPUHP_AP_ONLINE_DYN which will trigger a reassignment of affinity hints once the new CPU's are online. This implementation is based on the virtio_net driver. Signed-off-by: Thomas Falcon Signed-off-by: Dany Madden Signed-off-by: Nick Child Reviewed-by: Rick Lindsley Reviewed-by: Haren Myneni Signed-off-by: David S. Miller --- drivers/net/ethernet/ibm/ibmvnic.c | 89 +++++++++++++++++++++++++++++- drivers/net/ethernet/ibm/ibmvnic.h | 4 ++ include/linux/cpuhotplug.h | 1 + 3 files changed, 93 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/ibm/ibmvnic.c b/drivers/net/ethernet/ibm/ibmvnic.c index 0c969bdaf94d..2fc0d50dbb86 100644 --- a/drivers/net/ethernet/ibm/ibmvnic.c +++ b/drivers/net/ethernet/ibm/ibmvnic.c @@ -298,6 +298,57 @@ out: } } +static int ibmvnic_cpu_online(unsigned int cpu, struct hlist_node *node) +{ + struct ibmvnic_adapter *adapter; + + adapter = hlist_entry_safe(node, struct ibmvnic_adapter, node); + ibmvnic_set_affinity(adapter); + return 0; +} + +static int ibmvnic_cpu_dead(unsigned int cpu, struct hlist_node *node) +{ + struct ibmvnic_adapter *adapter; + + adapter = hlist_entry_safe(node, struct ibmvnic_adapter, node_dead); + ibmvnic_set_affinity(adapter); + return 0; +} + +static int ibmvnic_cpu_down_prep(unsigned int cpu, struct hlist_node *node) +{ + struct ibmvnic_adapter *adapter; + + adapter = hlist_entry_safe(node, struct ibmvnic_adapter, node); + ibmvnic_clean_affinity(adapter); + return 0; +} + +static enum cpuhp_state ibmvnic_online; + +static int ibmvnic_cpu_notif_add(struct ibmvnic_adapter *adapter) +{ + int ret; + + ret = cpuhp_state_add_instance_nocalls(ibmvnic_online, &adapter->node); + if (ret) + return ret; + ret = cpuhp_state_add_instance_nocalls(CPUHP_IBMVNIC_DEAD, + &adapter->node_dead); + if (!ret) + return ret; + cpuhp_state_remove_instance_nocalls(ibmvnic_online, &adapter->node); + return ret; +} + +static void ibmvnic_cpu_notif_remove(struct ibmvnic_adapter *adapter) +{ + cpuhp_state_remove_instance_nocalls(ibmvnic_online, &adapter->node); + cpuhp_state_remove_instance_nocalls(CPUHP_IBMVNIC_DEAD, + &adapter->node_dead); +} + static long h_reg_sub_crq(unsigned long unit_address, unsigned long token, unsigned long length, unsigned long *number, unsigned long *irq) @@ -6292,10 +6343,19 @@ static int ibmvnic_probe(struct vio_dev *dev, const struct vio_device_id *id) } dev_info(&dev->dev, "ibmvnic registered\n"); + rc = ibmvnic_cpu_notif_add(adapter); + if (rc) { + netdev_err(netdev, "Registering cpu notifier failed\n"); + goto cpu_notif_add_failed; + } + complete(&adapter->probe_done); return 0; +cpu_notif_add_failed: + unregister_netdev(netdev); + ibmvnic_register_fail: device_remove_file(&dev->dev, &dev_attr_failover); @@ -6346,6 +6406,8 @@ static void ibmvnic_remove(struct vio_dev *dev) spin_unlock_irqrestore(&adapter->state_lock, flags); + ibmvnic_cpu_notif_remove(adapter); + flush_work(&adapter->ibmvnic_reset); flush_delayed_work(&adapter->ibmvnic_delayed_reset); @@ -6476,15 +6538,40 @@ static struct vio_driver ibmvnic_driver = { /* module functions */ static int __init ibmvnic_module_init(void) { + int ret; + + ret = cpuhp_setup_state_multi(CPUHP_AP_ONLINE_DYN, "net/ibmvnic:online", + ibmvnic_cpu_online, + ibmvnic_cpu_down_prep); + if (ret < 0) + goto out; + ibmvnic_online = ret; + ret = cpuhp_setup_state_multi(CPUHP_IBMVNIC_DEAD, "net/ibmvnic:dead", + NULL, ibmvnic_cpu_dead); + if (ret) + goto err_dead; + + ret = vio_register_driver(&ibmvnic_driver); + if (ret) + goto err_vio_register; + pr_info("%s: %s %s\n", ibmvnic_driver_name, ibmvnic_driver_string, IBMVNIC_DRIVER_VERSION); - return vio_register_driver(&ibmvnic_driver); + return 0; +err_vio_register: + cpuhp_remove_multi_state(CPUHP_IBMVNIC_DEAD); +err_dead: + cpuhp_remove_multi_state(ibmvnic_online); +out: + return ret; } static void __exit ibmvnic_module_exit(void) { vio_unregister_driver(&ibmvnic_driver); + cpuhp_remove_multi_state(CPUHP_IBMVNIC_DEAD); + cpuhp_remove_multi_state(ibmvnic_online); } module_init(ibmvnic_module_init); diff --git a/drivers/net/ethernet/ibm/ibmvnic.h b/drivers/net/ethernet/ibm/ibmvnic.h index 6720fec1ae67..b35c9b6f913b 100644 --- a/drivers/net/ethernet/ibm/ibmvnic.h +++ b/drivers/net/ethernet/ibm/ibmvnic.h @@ -984,6 +984,10 @@ struct ibmvnic_adapter { int reset_done_rc; bool wait_for_reset; + /* CPU hotplug instances for online & dead */ + struct hlist_node node; + struct hlist_node node_dead; + /* partner capabilities */ u64 min_tx_queues; u64 min_rx_queues; diff --git a/include/linux/cpuhotplug.h b/include/linux/cpuhotplug.h index f61447913db9..c8bc85a87b1e 100644 --- a/include/linux/cpuhotplug.h +++ b/include/linux/cpuhotplug.h @@ -69,6 +69,7 @@ enum cpuhp_state { CPUHP_X86_APB_DEAD, CPUHP_X86_MCE_DEAD, CPUHP_VIRT_NET_DEAD, + CPUHP_IBMVNIC_DEAD, CPUHP_SLUB_DEAD, CPUHP_DEBUG_OBJ_DEAD, CPUHP_MM_WRITEBACK_DEAD, From df8f66d02df7b44516635edbc8c17b1311cfa0d8 Mon Sep 17 00:00:00 2001 From: Nick Child Date: Thu, 10 Nov 2022 15:32:18 -0600 Subject: [PATCH 0894/2002] ibmvnic: Update XPS assignments during affinity binding Transmit Packet Steering (XPS) maps cpu numbers to transmit queues. By running the same connection on the same set of cpu's, contention for the queue and cache miss rate can be minimized. When assigning a cpu mask for a tranmit queues irq number, assign the same cpu mask as the set of cpu's that XPS should use for that queue. Signed-off-by: Thomas Falcon Signed-off-by: Dany Madden Signed-off-by: Nick Child Reviewed-by: Rick Lindsley Reviewed-by: Haren Myneni Signed-off-by: David S. Miller --- drivers/net/ethernet/ibm/ibmvnic.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/net/ethernet/ibm/ibmvnic.c b/drivers/net/ethernet/ibm/ibmvnic.c index 2fc0d50dbb86..e19a6bb3f444 100644 --- a/drivers/net/ethernet/ibm/ibmvnic.c +++ b/drivers/net/ethernet/ibm/ibmvnic.c @@ -279,6 +279,16 @@ static void ibmvnic_set_affinity(struct ibmvnic_adapter *adapter) stride); if (rc) goto out; + + if (!queue) + continue; + + rc = __netif_set_xps_queue(adapter->netdev, + cpumask_bits(queue->affinity_mask), + i, XPS_CPUS); + if (rc) + netdev_warn(adapter->netdev, "%s: Set XPS on queue %d failed, rc = %d.\n", + __func__, i, rc); } for (i = 0; i < num_rxqs; i++) { From 2fd450cd83e3c978a4902aceb4aac36fa341067c Mon Sep 17 00:00:00 2001 From: xu xin Date: Fri, 11 Nov 2022 09:04:20 +0000 Subject: [PATCH 0895/2002] ipasdv4/tcp_ipv4: remove redundant assignment The value of 'st->state' has been verified as "TCP_SEQ_STATE_LISTENING", it's unnecessary to assign TCP_SEQ_STATE_LISTENING to it, so we can remove it. Signed-off-by: xu xin Signed-off-by: David S. Miller --- net/ipv4/tcp_ipv4.c | 1 - 1 file changed, 1 deletion(-) diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index ebab9e8b184c..f0343538d1f8 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -2480,7 +2480,6 @@ static void *tcp_seek_last_pos(struct seq_file *seq) case TCP_SEQ_STATE_LISTENING: if (st->bucket > hinfo->lhash2_mask) break; - st->state = TCP_SEQ_STATE_LISTENING; rc = listening_get_first(seq); while (offset-- && rc && bucket == st->bucket) rc = listening_get_next(seq, rc); From 70ea86a0dfed10e00ee2666dadeb563bab00efea Mon Sep 17 00:00:00 2001 From: Steen Hegelund Date: Fri, 11 Nov 2022 14:05:14 +0100 Subject: [PATCH 0896/2002] net: flow_offload: add support for ARP frame matching This adds a new flow_rule_match_arp function that allows drivers to be able to dissect ARP frames. Signed-off-by: Steen Hegelund Signed-off-by: David S. Miller --- include/net/flow_offload.h | 6 ++++++ net/core/flow_offload.c | 7 +++++++ 2 files changed, 13 insertions(+) diff --git a/include/net/flow_offload.h b/include/net/flow_offload.h index 7a60bc6d72c9..0400a0ac8a29 100644 --- a/include/net/flow_offload.h +++ b/include/net/flow_offload.h @@ -32,6 +32,10 @@ struct flow_match_vlan { struct flow_dissector_key_vlan *key, *mask; }; +struct flow_match_arp { + struct flow_dissector_key_arp *key, *mask; +}; + struct flow_match_ipv4_addrs { struct flow_dissector_key_ipv4_addrs *key, *mask; }; @@ -98,6 +102,8 @@ void flow_rule_match_vlan(const struct flow_rule *rule, struct flow_match_vlan *out); void flow_rule_match_cvlan(const struct flow_rule *rule, struct flow_match_vlan *out); +void flow_rule_match_arp(const struct flow_rule *rule, + struct flow_match_arp *out); void flow_rule_match_ipv4_addrs(const struct flow_rule *rule, struct flow_match_ipv4_addrs *out); void flow_rule_match_ipv6_addrs(const struct flow_rule *rule, diff --git a/net/core/flow_offload.c b/net/core/flow_offload.c index abe423fd5736..acfc1f88ea79 100644 --- a/net/core/flow_offload.c +++ b/net/core/flow_offload.c @@ -97,6 +97,13 @@ void flow_rule_match_cvlan(const struct flow_rule *rule, } EXPORT_SYMBOL(flow_rule_match_cvlan); +void flow_rule_match_arp(const struct flow_rule *rule, + struct flow_match_arp *out) +{ + FLOW_DISSECTOR_MATCH(rule, FLOW_DISSECTOR_KEY_ARP, out); +} +EXPORT_SYMBOL(flow_rule_match_arp); + void flow_rule_match_ipv4_addrs(const struct flow_rule *rule, struct flow_match_ipv4_addrs *out) { From 3a344f99bb55cdaa15b5eccb1a9b9b88e5ce2f48 Mon Sep 17 00:00:00 2001 From: Steen Hegelund Date: Fri, 11 Nov 2022 14:05:15 +0100 Subject: [PATCH 0897/2002] net: microchip: sparx5: Add support for TC flower ARP dissector This add support for Sparx5 for dissecting TC ARP flower filter keys and sets up the Sparx5 IS2 VCAP to generate the ARP keyset for ARP frames. Signed-off-by: Steen Hegelund Signed-off-by: David S. Miller --- .../microchip/sparx5/sparx5_tc_flower.c | 76 +++++++++++++++++++ .../microchip/sparx5/sparx5_vcap_impl.c | 2 +- 2 files changed, 77 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_tc_flower.c b/drivers/net/ethernet/microchip/sparx5/sparx5_tc_flower.c index 7b364f6b4546..b76b8fc567bb 100644 --- a/drivers/net/ethernet/microchip/sparx5/sparx5_tc_flower.c +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_tc_flower.c @@ -26,10 +26,24 @@ struct sparx5_tc_flower_parse_usage { */ static u16 sparx5_tc_known_etypes[] = { ETH_P_ALL, + ETH_P_ARP, ETH_P_IP, ETH_P_IPV6, }; +enum sparx5_is2_arp_opcode { + SPX5_IS2_ARP_REQUEST, + SPX5_IS2_ARP_REPLY, + SPX5_IS2_RARP_REQUEST, + SPX5_IS2_RARP_REPLY, +}; + +enum tc_arp_opcode { + TC_ARP_OP_RESERVED, + TC_ARP_OP_REQUEST, + TC_ARP_OP_REPLY, +}; + static bool sparx5_tc_is_known_etype(u16 etype) { int idx; @@ -404,6 +418,67 @@ out: return err; } +static int +sparx5_tc_flower_handler_arp_usage(struct sparx5_tc_flower_parse_usage *st) +{ + struct flow_match_arp mt; + u16 value, mask; + u32 ipval, ipmsk; + int err; + + flow_rule_match_arp(st->frule, &mt); + + if (mt.mask->op) { + mask = 0x3; + if (st->l3_proto == ETH_P_ARP) { + value = mt.key->op == TC_ARP_OP_REQUEST ? + SPX5_IS2_ARP_REQUEST : + SPX5_IS2_ARP_REPLY; + } else { /* RARP */ + value = mt.key->op == TC_ARP_OP_REQUEST ? + SPX5_IS2_RARP_REQUEST : + SPX5_IS2_RARP_REPLY; + } + err = vcap_rule_add_key_u32(st->vrule, VCAP_KF_ARP_OPCODE, + value, mask); + if (err) + goto out; + } + + /* The IS2 ARP keyset does not support ARP hardware addresses */ + if (!is_zero_ether_addr(mt.mask->sha) || + !is_zero_ether_addr(mt.mask->tha)) + goto out; + + if (mt.mask->sip) { + ipval = be32_to_cpu((__force __be32)mt.key->sip); + ipmsk = be32_to_cpu((__force __be32)mt.mask->sip); + + err = vcap_rule_add_key_u32(st->vrule, VCAP_KF_L3_IP4_SIP, + ipval, ipmsk); + if (err) + goto out; + } + + if (mt.mask->tip) { + ipval = be32_to_cpu((__force __be32)mt.key->tip); + ipmsk = be32_to_cpu((__force __be32)mt.mask->tip); + + err = vcap_rule_add_key_u32(st->vrule, VCAP_KF_L3_IP4_DIP, + ipval, ipmsk); + if (err) + goto out; + } + + st->used_keys |= BIT(FLOW_DISSECTOR_KEY_ARP); + + return err; + +out: + NL_SET_ERR_MSG_MOD(st->fco->common.extack, "arp parse error"); + return err; +} + static int sparx5_tc_flower_handler_ip_usage(struct sparx5_tc_flower_parse_usage *st) { @@ -438,6 +513,7 @@ static int (*sparx5_tc_flower_usage_handlers[])(struct sparx5_tc_flower_parse_us [FLOW_DISSECTOR_KEY_BASIC] = sparx5_tc_flower_handler_basic_usage, [FLOW_DISSECTOR_KEY_VLAN] = sparx5_tc_flower_handler_vlan_usage, [FLOW_DISSECTOR_KEY_TCP] = sparx5_tc_flower_handler_tcp_usage, + [FLOW_DISSECTOR_KEY_ARP] = sparx5_tc_flower_handler_arp_usage, [FLOW_DISSECTOR_KEY_IP] = sparx5_tc_flower_handler_ip_usage, }; diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_vcap_impl.c b/drivers/net/ethernet/microchip/sparx5/sparx5_vcap_impl.c index 10bc56cd0045..db73e2b19dc5 100644 --- a/drivers/net/ethernet/microchip/sparx5/sparx5_vcap_impl.c +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_vcap_impl.c @@ -540,7 +540,7 @@ static void sparx5_vcap_port_key_selection(struct sparx5 *sparx5, VCAP_IS2_PS_IPV4_UC_IP4_TCP_UDP_OTHER, VCAP_IS2_PS_IPV6_MC_IP_7TUPLE, VCAP_IS2_PS_IPV6_UC_IP_7TUPLE, - VCAP_IS2_PS_ARP_MAC_ETYPE); + VCAP_IS2_PS_ARP_ARP); for (lookup = 0; lookup < admin->lookups; ++lookup) { for (portno = 0; portno < SPX5_PORTS; ++portno) { spx5_wr(keysel, sparx5, From 990e483981ea739b1064eadc426d986ab8880169 Mon Sep 17 00:00:00 2001 From: Steen Hegelund Date: Fri, 11 Nov 2022 14:05:16 +0100 Subject: [PATCH 0898/2002] net: microchip: sparx5: Add/delete rules in sorted order This adds a sorting criteria to rule insertion and deletion. The criteria is (in the listed order): - Rule size (largest size first) - User (based on an enumerated user value) - Priority (highest priority first, aka lowest value) When a rule is deleted the other rules may need to be moved to fill the gap to use the available VCAP address space in the best possible way. Signed-off-by: Steen Hegelund Signed-off-by: David S. Miller --- .../microchip/sparx5/sparx5_vcap_impl.c | 27 +++- .../net/ethernet/microchip/vcap/vcap_api.c | 139 ++++++++++++++++-- 2 files changed, 152 insertions(+), 14 deletions(-) diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_vcap_impl.c b/drivers/net/ethernet/microchip/sparx5/sparx5_vcap_impl.c index db73e2b19dc5..b62c48a3fc45 100644 --- a/drivers/net/ethernet/microchip/sparx5/sparx5_vcap_impl.c +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_vcap_impl.c @@ -477,7 +477,32 @@ static void sparx5_vcap_update(struct net_device *ndev, static void sparx5_vcap_move(struct net_device *ndev, struct vcap_admin *admin, u32 addr, int offset, int count) { - /* this will be added later */ + struct sparx5_port *port = netdev_priv(ndev); + struct sparx5 *sparx5 = port->sparx5; + enum vcap_command cmd; + u16 mv_num_pos; + u16 mv_size; + + mv_size = count - 1; + if (offset > 0) { + mv_num_pos = offset - 1; + cmd = VCAP_CMD_MOVE_DOWN; + } else { + mv_num_pos = -offset - 1; + cmd = VCAP_CMD_MOVE_UP; + } + spx5_wr(VCAP_SUPER_CFG_MV_NUM_POS_SET(mv_num_pos) | + VCAP_SUPER_CFG_MV_SIZE_SET(mv_size), + sparx5, VCAP_SUPER_CFG); + spx5_wr(VCAP_SUPER_CTRL_UPDATE_CMD_SET(cmd) | + VCAP_SUPER_CTRL_UPDATE_ENTRY_DIS_SET(0) | + VCAP_SUPER_CTRL_UPDATE_ACTION_DIS_SET(0) | + VCAP_SUPER_CTRL_UPDATE_CNT_DIS_SET(0) | + VCAP_SUPER_CTRL_UPDATE_ADDR_SET(addr) | + VCAP_SUPER_CTRL_CLEAR_CACHE_SET(false) | + VCAP_SUPER_CTRL_UPDATE_SHOT_SET(true), + sparx5, VCAP_SUPER_CTRL); + sparx5_vcap_wait_super_update(sparx5); } /* Provide port information via a callback interface */ diff --git a/drivers/net/ethernet/microchip/vcap/vcap_api.c b/drivers/net/ethernet/microchip/vcap/vcap_api.c index b6ab6bae28c0..62b675a37a96 100644 --- a/drivers/net/ethernet/microchip/vcap/vcap_api.c +++ b/drivers/net/ethernet/microchip/vcap/vcap_api.c @@ -941,6 +941,16 @@ int vcap_val_rule(struct vcap_rule *rule, u16 l3_proto) } EXPORT_SYMBOL_GPL(vcap_val_rule); +/* Entries are sorted with increasing values of sort_key. + * I.e. Lowest numerical sort_key is first in list. + * In order to locate largest keys first in list we negate the key size with + * (max_size - size). + */ +static u32 vcap_sort_key(u32 max_size, u32 size, u8 user, u16 prio) +{ + return ((max_size - size) << 24) | (user << 16) | prio; +} + /* calculate the address of the next rule after this (lower address and prio) */ static u32 vcap_next_rule_addr(u32 addr, struct vcap_rule_internal *ri) { @@ -970,17 +980,67 @@ static u32 vcap_set_rule_id(struct vcap_rule_internal *ri) static int vcap_insert_rule(struct vcap_rule_internal *ri, struct vcap_rule_move *move) { + int sw_count = ri->vctrl->vcaps[ri->admin->vtype].sw_count; + struct vcap_rule_internal *duprule, *iter, *elem = NULL; struct vcap_admin *admin = ri->admin; - struct vcap_rule_internal *duprule; + u32 addr; + + ri->sort_key = vcap_sort_key(sw_count, ri->size, ri->data.user, + ri->data.priority); + + /* Insert the new rule in the list of rule based on the sort key + * If the rule needs to be inserted between existing rules then move + * these rules to make room for the new rule and update their start + * address. + */ + list_for_each_entry(iter, &admin->rules, list) { + if (ri->sort_key < iter->sort_key) { + elem = iter; + break; + } + } + + if (!elem) { + ri->addr = vcap_next_rule_addr(admin->last_used_addr, ri); + admin->last_used_addr = ri->addr; + + /* Add a shallow copy of the rule to the VCAP list */ + duprule = vcap_dup_rule(ri); + if (IS_ERR(duprule)) + return PTR_ERR(duprule); + + list_add_tail(&duprule->list, &admin->rules); + return 0; + } + + /* Reuse the space of the current rule */ + addr = elem->addr + elem->size; + ri->addr = vcap_next_rule_addr(addr, ri); + addr = ri->addr; - /* Only support appending rules for now */ - ri->addr = vcap_next_rule_addr(admin->last_used_addr, ri); - admin->last_used_addr = ri->addr; /* Add a shallow copy of the rule to the VCAP list */ duprule = vcap_dup_rule(ri); if (IS_ERR(duprule)) return PTR_ERR(duprule); - list_add_tail(&duprule->list, &admin->rules); + + /* Add before the current entry */ + list_add_tail(&duprule->list, &elem->list); + + /* Update the current rule */ + elem->addr = vcap_next_rule_addr(addr, elem); + addr = elem->addr; + + /* Update the address in the remaining rules in the list */ + list_for_each_entry_continue(elem, &admin->rules, list) { + elem->addr = vcap_next_rule_addr(addr, elem); + addr = elem->addr; + } + + /* Update the move info */ + move->addr = admin->last_used_addr; + move->count = ri->addr - addr; + move->offset = admin->last_used_addr - addr; + admin->last_used_addr = addr; return 0; } @@ -1032,8 +1092,11 @@ struct vcap_rule *vcap_alloc_rule(struct vcap_control *vctrl, { struct vcap_rule_internal *ri; struct vcap_admin *admin; - int maxsize; + int err, maxsize; + err = vcap_api_check(vctrl); + if (err) + return ERR_PTR(err); if (!ndev) return ERR_PTR(-ENODEV); /* Get the VCAP instance */ @@ -1098,12 +1161,57 @@ void vcap_free_rule(struct vcap_rule *rule) } EXPORT_SYMBOL_GPL(vcap_free_rule); +/* Return the alignment offset for a new rule address */ +static int vcap_valid_rule_move(struct vcap_rule_internal *el, int offset) +{ + return (el->addr + offset) % el->size; +} + +/* Update the rule address with an offset */ +static void vcap_adjust_rule_addr(struct vcap_rule_internal *el, int offset) +{ + el->addr += offset; +} + +/* Rules needs to be moved to fill the gap of the deleted rule */ +static int vcap_fill_rule_gap(struct vcap_rule_internal *ri) +{ + struct vcap_admin *admin = ri->admin; + struct vcap_rule_internal *elem; + struct vcap_rule_move move; + int gap = 0, offset = 0; + + /* If the first rule is deleted: Move other rules to the top */ + if (list_is_first(&ri->list, &admin->rules)) + offset = admin->last_valid_addr + 1 - ri->addr - ri->size; + + /* Locate gaps between odd size rules and adjust the move */ + elem = ri; + list_for_each_entry_continue(elem, &admin->rules, list) + gap += vcap_valid_rule_move(elem, ri->size); + + /* Update the address in the remaining rules in the list */ + elem = ri; + list_for_each_entry_continue(elem, &admin->rules, list) + vcap_adjust_rule_addr(elem, ri->size + gap + offset); + + /* Update the move info */ + move.addr = admin->last_used_addr; + move.count = ri->addr - admin->last_used_addr - gap; + move.offset = -(ri->size + gap + offset); + + /* Do the actual move operation */ + vcap_move_rules(ri, &move); + + return gap + offset; +} + /* Delete rule in a VCAP instance */ int vcap_del_rule(struct vcap_control *vctrl, struct net_device *ndev, u32 id) { struct vcap_rule_internal *ri, *elem; struct vcap_admin *admin; - int err; + int gap = 0, err; /* This will later also handle rule moving */ if (!ndev) @@ -1116,18 +1224,23 @@ int vcap_del_rule(struct vcap_control *vctrl, struct net_device *ndev, u32 id) if (!ri) return -EINVAL; admin = ri->admin; - list_del(&ri->list); - /* delete the rule in the cache */ - vctrl->ops->init(ndev, admin, ri->addr, ri->size); + if (ri->addr > admin->last_used_addr) + gap = vcap_fill_rule_gap(ri); + + /* Delete the rule from the list of rules and the cache */ + list_del(&ri->list); + vctrl->ops->init(ndev, admin, admin->last_used_addr, ri->size + gap); + kfree(ri); + + /* Update the last used address */ if (list_empty(&admin->rules)) { admin->last_used_addr = admin->last_valid_addr; } else { - /* update the address range end marker from the last rule in the list */ - elem = list_last_entry(&admin->rules, struct vcap_rule_internal, list); + elem = list_last_entry(&admin->rules, struct vcap_rule_internal, + list); admin->last_used_addr = elem->addr; } - kfree(ri); return 0; } EXPORT_SYMBOL_GPL(vcap_del_rule); From f13230a474774f2b38dab61eb22b3e494d9f5dc7 Mon Sep 17 00:00:00 2001 From: Steen Hegelund Date: Fri, 11 Nov 2022 14:05:17 +0100 Subject: [PATCH 0899/2002] net: microchip: sparx5: Add support for IS2 VCAP rule counters This adds API methods to set and get a rule counter. A VCAP instance may contain the counter as part of the VCAP cache area, and this counter may be one or more bits in width. This type of counter automatically increments it value when the rule is hit. Other VCAP instances have a dedicated counter area outside of the VCAP and in this case the rule must contain the counter id to be able to locate the counter value. In this case there must also be a rule action that updates the counter using the rule id when the rule is hit. The Sparx5 IS2 VCAP uses a dedicated counter area. Signed-off-by: Steen Hegelund Signed-off-by: David S. Miller --- .../microchip/sparx5/sparx5_vcap_impl.c | 47 +++++++++++- .../net/ethernet/microchip/vcap/vcap_api.c | 71 +++++++++++++++++++ .../ethernet/microchip/vcap/vcap_api_client.h | 11 +++ 3 files changed, 127 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_vcap_impl.c b/drivers/net/ethernet/microchip/sparx5/sparx5_vcap_impl.c index b62c48a3fc45..e8f3d030eba2 100644 --- a/drivers/net/ethernet/microchip/sparx5/sparx5_vcap_impl.c +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_vcap_impl.c @@ -428,15 +428,58 @@ static void sparx5_vcap_cache_write(struct net_device *ndev, default: break; } + if (sel & VCAP_SEL_COUNTER) { + start = start & 0xfff; /* counter limit */ + if (admin->vinst == 0) + spx5_wr(admin->cache.counter, sparx5, + ANA_ACL_CNT_A(start)); + else + spx5_wr(admin->cache.counter, sparx5, + ANA_ACL_CNT_B(start)); + spx5_wr(admin->cache.sticky, sparx5, + VCAP_SUPER_VCAP_CNT_DAT(0)); + } } /* API callback used for reading from the VCAP into the VCAP cache */ static void sparx5_vcap_cache_read(struct net_device *ndev, struct vcap_admin *admin, - enum vcap_selection sel, u32 start, + enum vcap_selection sel, + u32 start, u32 count) { - /* this will be added later */ + struct sparx5_port *port = netdev_priv(ndev); + struct sparx5 *sparx5 = port->sparx5; + u32 *keystr, *mskstr, *actstr; + int idx; + + keystr = &admin->cache.keystream[start]; + mskstr = &admin->cache.maskstream[start]; + actstr = &admin->cache.actionstream[start]; + if (sel & VCAP_SEL_ENTRY) { + for (idx = 0; idx < count; ++idx) { + keystr[idx] = spx5_rd(sparx5, + VCAP_SUPER_VCAP_ENTRY_DAT(idx)); + mskstr[idx] = ~spx5_rd(sparx5, + VCAP_SUPER_VCAP_MASK_DAT(idx)); + } + } + if (sel & VCAP_SEL_ACTION) { + for (idx = 0; idx < count; ++idx) + actstr[idx] = spx5_rd(sparx5, + VCAP_SUPER_VCAP_ACTION_DAT(idx)); + } + if (sel & VCAP_SEL_COUNTER) { + start = start & 0xfff; /* counter limit */ + if (admin->vinst == 0) + admin->cache.counter = + spx5_rd(sparx5, ANA_ACL_CNT_A(start)); + else + admin->cache.counter = + spx5_rd(sparx5, ANA_ACL_CNT_B(start)); + admin->cache.sticky = + spx5_rd(sparx5, VCAP_SUPER_VCAP_CNT_DAT(0)); + } } /* API callback used for initializing a VCAP address range */ diff --git a/drivers/net/ethernet/microchip/vcap/vcap_api.c b/drivers/net/ethernet/microchip/vcap/vcap_api.c index 62b675a37a96..9c660e718526 100644 --- a/drivers/net/ethernet/microchip/vcap/vcap_api.c +++ b/drivers/net/ethernet/microchip/vcap/vcap_api.c @@ -25,6 +25,8 @@ struct vcap_rule_internal { int actionset_sw_regs; /* registers in a subword in an actionset */ int size; /* the size of the rule: max(entry, action) */ u32 addr; /* address in the VCAP at insertion */ + u32 counter_id; /* counter id (if a dedicated counter is available) */ + struct vcap_counter counter; /* last read counter value */ }; /* Moving a rule in the VCAP address space */ @@ -651,6 +653,20 @@ static int vcap_write_rule(struct vcap_rule_internal *ri) return 0; } +static int vcap_write_counter(struct vcap_rule_internal *ri, + struct vcap_counter *ctr) +{ + struct vcap_admin *admin = ri->admin; + + admin->cache.counter = ctr->value; + admin->cache.sticky = ctr->sticky; + ri->vctrl->ops->cache_write(ri->ndev, admin, VCAP_SEL_COUNTER, + ri->counter_id, 0); + ri->vctrl->ops->update(ri->ndev, admin, VCAP_CMD_WRITE, + VCAP_SEL_COUNTER, ri->addr); + return 0; +} + /* Convert a chain id to a VCAP lookup index */ int vcap_chain_id_to_lookup(struct vcap_admin *admin, int cur_cid) { @@ -1547,6 +1563,20 @@ int vcap_rule_add_action_u32(struct vcap_rule *rule, } EXPORT_SYMBOL_GPL(vcap_rule_add_action_u32); +static int vcap_read_counter(struct vcap_rule_internal *ri, + struct vcap_counter *ctr) +{ + struct vcap_admin *admin = ri->admin; + + ri->vctrl->ops->update(ri->ndev, admin, VCAP_CMD_READ, VCAP_SEL_COUNTER, + ri->addr); + ri->vctrl->ops->cache_read(ri->ndev, admin, VCAP_SEL_COUNTER, + ri->counter_id, 0); + ctr->value = admin->cache.counter; + ctr->sticky = admin->cache.sticky; + return 0; +} + /* Copy to host byte order */ void vcap_netbytes_copy(u8 *dst, u8 *src, int count) { @@ -1690,6 +1720,47 @@ int vcap_enable_lookups(struct vcap_control *vctrl, struct net_device *ndev, } EXPORT_SYMBOL_GPL(vcap_enable_lookups); +/* Set a rule counter id (for certain vcaps only) */ +void vcap_rule_set_counter_id(struct vcap_rule *rule, u32 counter_id) +{ + struct vcap_rule_internal *ri = to_intrule(rule); + + ri->counter_id = counter_id; +} +EXPORT_SYMBOL_GPL(vcap_rule_set_counter_id); + +int vcap_rule_set_counter(struct vcap_rule *rule, struct vcap_counter *ctr) +{ + struct vcap_rule_internal *ri = to_intrule(rule); + int err; + + err = vcap_api_check(ri->vctrl); + if (err) + return err; + if (!ctr) { + pr_err("%s:%d: counter is missing\n", __func__, __LINE__); + return -EINVAL; + } + return vcap_write_counter(ri, ctr); +} +EXPORT_SYMBOL_GPL(vcap_rule_set_counter); + +int vcap_rule_get_counter(struct vcap_rule *rule, struct vcap_counter *ctr) +{ + struct vcap_rule_internal *ri = to_intrule(rule); + int err; + + err = vcap_api_check(ri->vctrl); + if (err) + return err; + if (!ctr) { + pr_err("%s:%d: counter is missing\n", __func__, __LINE__); + return -EINVAL; + } + return vcap_read_counter(ri, ctr); +} +EXPORT_SYMBOL_GPL(vcap_rule_get_counter); + #ifdef CONFIG_VCAP_KUNIT_TEST #include "vcap_api_kunit.c" #endif diff --git a/drivers/net/ethernet/microchip/vcap/vcap_api_client.h b/drivers/net/ethernet/microchip/vcap/vcap_api_client.h index 0ea5ec96adc8..c2655045d6d4 100644 --- a/drivers/net/ethernet/microchip/vcap/vcap_api_client.h +++ b/drivers/net/ethernet/microchip/vcap/vcap_api_client.h @@ -143,6 +143,11 @@ enum vcap_bit { VCAP_BIT_1 }; +struct vcap_counter { + u32 value; + bool sticky; +}; + /* Enable/Disable the VCAP instance lookups. Chain id 0 means disable */ int vcap_enable_lookups(struct vcap_control *vctrl, struct net_device *ndev, int chain_id, unsigned long cookie, bool enable); @@ -170,6 +175,8 @@ int vcap_set_rule_set_keyset(struct vcap_rule *rule, /* Update the actionset for the rule */ int vcap_set_rule_set_actionset(struct vcap_rule *rule, enum vcap_actionfield_set actionset); +/* Set a rule counter id (for certain VCAPs only) */ +void vcap_rule_set_counter_id(struct vcap_rule *rule, u32 counter_id); /* VCAP rule field operations */ int vcap_rule_add_key_bit(struct vcap_rule *rule, enum vcap_key_field key, @@ -187,6 +194,10 @@ int vcap_rule_add_action_bit(struct vcap_rule *rule, int vcap_rule_add_action_u32(struct vcap_rule *rule, enum vcap_action_field action, u32 value); +/* VCAP rule counter operations */ +int vcap_rule_set_counter(struct vcap_rule *rule, struct vcap_counter *ctr); +int vcap_rule_get_counter(struct vcap_rule *rule, struct vcap_counter *ctr); + /* VCAP lookup operations */ /* Convert a chain id to a VCAP lookup index */ int vcap_chain_id_to_lookup(struct vcap_admin *admin, int cur_cid); From 40e7fe18abab3880e54e86521307c40ea92676d1 Mon Sep 17 00:00:00 2001 From: Steen Hegelund Date: Fri, 11 Nov 2022 14:05:18 +0100 Subject: [PATCH 0900/2002] net: microchip: sparx5: Add support for TC flower filter statistics This provides flower filter packet statistics (bytes are not supported) via the dedicated IS2 counter feature. All rules having the same TC cookie will contribute to the packet statistics for the filter as they are considered to be part of the same TC flower filter. Signed-off-by: Steen Hegelund Signed-off-by: David S. Miller --- .../microchip/sparx5/sparx5_tc_flower.c | 68 +++++++++++++++++++ .../net/ethernet/microchip/vcap/vcap_api.c | 25 +++++++ .../ethernet/microchip/vcap/vcap_api_client.h | 3 + 3 files changed, 96 insertions(+) diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_tc_flower.c b/drivers/net/ethernet/microchip/sparx5/sparx5_tc_flower.c index b76b8fc567bb..a48baeacc1d2 100644 --- a/drivers/net/ethernet/microchip/sparx5/sparx5_tc_flower.c +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_tc_flower.c @@ -21,6 +21,11 @@ struct sparx5_tc_flower_parse_usage { unsigned int used_keys; }; +struct sparx5_tc_rule_pkt_cnt { + u64 cookie; + u32 pkts; +}; + /* These protocols have dedicated keysets in IS2 and a TC dissector * ETH_P_ARP does not have a TC dissector */ @@ -605,6 +610,20 @@ static int sparx5_tc_flower_action_check(struct vcap_control *vctrl, return 0; } +/* Add a rule counter action - only IS2 is considered for now */ +static int sparx5_tc_add_rule_counter(struct vcap_admin *admin, + struct vcap_rule *vrule) +{ + int err; + + err = vcap_rule_add_action_u32(vrule, VCAP_AF_CNT_ID, vrule->id); + if (err) + return err; + + vcap_rule_set_counter_id(vrule, vrule->id); + return err; +} + static int sparx5_tc_flower_replace(struct net_device *ndev, struct flow_cls_offload *fco, struct vcap_admin *admin) @@ -630,6 +649,11 @@ static int sparx5_tc_flower_replace(struct net_device *ndev, vrule->cookie = fco->cookie; sparx5_tc_use_dissectors(fco, admin, vrule, &l3_proto); + + err = sparx5_tc_add_rule_counter(admin, vrule); + if (err) + goto out; + frule = flow_cls_offload_flow_rule(fco); flow_action_for_each(idx, act, &frule->action) { switch (act->id) { @@ -708,6 +732,48 @@ static int sparx5_tc_flower_destroy(struct net_device *ndev, return err; } +/* Collect packet counts from all rules with the same cookie */ +static int sparx5_tc_rule_counter_cb(void *arg, struct vcap_rule *rule) +{ + struct sparx5_tc_rule_pkt_cnt *rinfo = arg; + struct vcap_counter counter; + int err = 0; + + if (rule->cookie == rinfo->cookie) { + err = vcap_rule_get_counter(rule, &counter); + if (err) + return err; + rinfo->pkts += counter.value; + /* Reset the rule counter */ + counter.value = 0; + vcap_rule_set_counter(rule, &counter); + } + return err; +} + +static int sparx5_tc_flower_stats(struct net_device *ndev, + struct flow_cls_offload *fco, + struct vcap_admin *admin) +{ + struct sparx5_port *port = netdev_priv(ndev); + struct sparx5_tc_rule_pkt_cnt rinfo = {}; + struct vcap_control *vctrl; + ulong lastused = 0; + u64 drops = 0; + u32 pkts = 0; + int err; + + rinfo.cookie = fco->cookie; + vctrl = port->sparx5->vcap_ctrl; + err = vcap_rule_iter(vctrl, sparx5_tc_rule_counter_cb, &rinfo); + if (err) + return err; + pkts = rinfo.pkts; + flow_stats_update(&fco->stats, 0x0, pkts, drops, lastused, + FLOW_ACTION_HW_STATS_IMMEDIATE); + return err; +} + int sparx5_tc_flower(struct net_device *ndev, struct flow_cls_offload *fco, bool ingress) { @@ -729,6 +795,8 @@ int sparx5_tc_flower(struct net_device *ndev, struct flow_cls_offload *fco, return sparx5_tc_flower_replace(ndev, fco, admin); case FLOW_CLS_DESTROY: return sparx5_tc_flower_destroy(ndev, fco, admin); + case FLOW_CLS_STATS: + return sparx5_tc_flower_stats(ndev, fco, admin); default: return -EOPNOTSUPP; } diff --git a/drivers/net/ethernet/microchip/vcap/vcap_api.c b/drivers/net/ethernet/microchip/vcap/vcap_api.c index 9c660e718526..d12c8ec40fe2 100644 --- a/drivers/net/ethernet/microchip/vcap/vcap_api.c +++ b/drivers/net/ethernet/microchip/vcap/vcap_api.c @@ -1729,6 +1729,31 @@ void vcap_rule_set_counter_id(struct vcap_rule *rule, u32 counter_id) } EXPORT_SYMBOL_GPL(vcap_rule_set_counter_id); +/* Provide all rules via a callback interface */ +int vcap_rule_iter(struct vcap_control *vctrl, + int (*callback)(void *, struct vcap_rule *), void *arg) +{ + struct vcap_rule_internal *ri; + struct vcap_admin *admin; + int ret; + + ret = vcap_api_check(vctrl); + if (ret) + return ret; + + /* Iterate all rules in each VCAP instance */ + list_for_each_entry(admin, &vctrl->list, list) { + list_for_each_entry(ri, &admin->rules, list) { + ret = callback(arg, &ri->data); + if (ret) + return ret; + } + } + + return 0; +} +EXPORT_SYMBOL_GPL(vcap_rule_iter); + int vcap_rule_set_counter(struct vcap_rule *rule, struct vcap_counter *ctr) { struct vcap_rule_internal *ri = to_intrule(rule); diff --git a/drivers/net/ethernet/microchip/vcap/vcap_api_client.h b/drivers/net/ethernet/microchip/vcap/vcap_api_client.h index c2655045d6d4..654ef8fa6d62 100644 --- a/drivers/net/ethernet/microchip/vcap/vcap_api_client.h +++ b/drivers/net/ethernet/microchip/vcap/vcap_api_client.h @@ -210,6 +210,9 @@ const struct vcap_field *vcap_lookup_keyfield(struct vcap_rule *rule, int vcap_lookup_rule_by_cookie(struct vcap_control *vctrl, u64 cookie); /* Is the next chain id in the following lookup, possible in another VCAP */ bool vcap_is_next_lookup(struct vcap_control *vctrl, int cur_cid, int next_cid); +/* Provide all rules via a callback interface */ +int vcap_rule_iter(struct vcap_control *vctrl, + int (*callback)(void *, struct vcap_rule *), void *arg); /* Copy to host byte order */ void vcap_netbytes_copy(u8 *dst, u8 *src, int count); From dccc30cc4906b5ec0569979fbef591f84e6cb37d Mon Sep 17 00:00:00 2001 From: Steen Hegelund Date: Fri, 11 Nov 2022 14:05:19 +0100 Subject: [PATCH 0901/2002] net: microchip: sparx5: Add KUNIT test of counters and sorted rules This tests the insert, move and deleting of rules and checks that the unused VCAP addresses are initialized correctly. Signed-off-by: Steen Hegelund Signed-off-by: David S. Miller --- .../ethernet/microchip/vcap/vcap_api_kunit.c | 526 ++++++++++++++++++ 1 file changed, 526 insertions(+) diff --git a/drivers/net/ethernet/microchip/vcap/vcap_api_kunit.c b/drivers/net/ethernet/microchip/vcap/vcap_api_kunit.c index b0ec51b37683..6858e44ce4a5 100644 --- a/drivers/net/ethernet/microchip/vcap/vcap_api_kunit.c +++ b/drivers/net/ethernet/microchip/vcap/vcap_api_kunit.c @@ -23,6 +23,9 @@ static u32 test_init_count; static u32 test_hw_counter_id; static struct vcap_cache_data test_hw_cache; static struct net_device test_netdev = {}; +static int test_move_addr; +static int test_move_offset; +static int test_move_count; /* Callback used by the VCAP API */ static enum vcap_keyfield_set test_val_keyset(struct net_device *ndev, @@ -195,6 +198,9 @@ static void test_cache_update(struct net_device *ndev, struct vcap_admin *admin, static void test_cache_move(struct net_device *ndev, struct vcap_admin *admin, u32 addr, int offset, int count) { + test_move_addr = addr; + test_move_offset = offset; + test_move_count = count; } /* Provide port information via a callback interface */ @@ -242,6 +248,88 @@ static void vcap_test_api_init(struct vcap_admin *admin) test_updateaddridx = 0; } +/* Helper function to create a rule of a specific size */ +static struct vcap_rule * +test_vcap_xn_rule_creator(struct kunit *test, int cid, enum vcap_user user, + u16 priority, + int id, int size, int expected_addr) +{ + struct vcap_rule *rule = 0; + struct vcap_rule_internal *ri = 0; + enum vcap_keyfield_set keyset = VCAP_KFS_NO_VALUE; + enum vcap_actionfield_set actionset = VCAP_AFS_NO_VALUE; + int ret; + + /* init before testing */ + memset(test_updateaddr, 0, sizeof(test_updateaddr)); + test_updateaddridx = 0; + test_move_addr = 0; + test_move_offset = 0; + test_move_count = 0; + + switch (size) { + case 2: + keyset = VCAP_KFS_ETAG; + actionset = VCAP_AFS_CLASS_REDUCED; + break; + case 3: + keyset = VCAP_KFS_PURE_5TUPLE_IP4; + actionset = VCAP_AFS_CLASSIFICATION; + break; + case 6: + keyset = VCAP_KFS_NORMAL_5TUPLE_IP4; + actionset = VCAP_AFS_CLASSIFICATION; + break; + case 12: + keyset = VCAP_KFS_NORMAL_7TUPLE; + actionset = VCAP_AFS_FULL; + break; + default: + break; + } + + /* Check that a valid size was used */ + KUNIT_ASSERT_NE(test, VCAP_KFS_NO_VALUE, keyset); + + /* Allocate the rule */ + rule = vcap_alloc_rule(&test_vctrl, &test_netdev, cid, user, priority, + id); + KUNIT_EXPECT_PTR_NE(test, NULL, rule); + + ri = (struct vcap_rule_internal *)rule; + + /* Override rule keyset */ + ret = vcap_set_rule_set_keyset(rule, keyset); + + /* Add rule actions : there must be at least one action */ + ret = vcap_rule_add_action_u32(rule, VCAP_AF_COSID_VAL, 0); + + /* Override rule actionset */ + ret = vcap_set_rule_set_actionset(rule, actionset); + + ret = vcap_val_rule(rule, ETH_P_ALL); + KUNIT_EXPECT_EQ(test, 0, ret); + KUNIT_EXPECT_EQ(test, keyset, rule->keyset); + KUNIT_EXPECT_EQ(test, actionset, rule->actionset); + KUNIT_EXPECT_EQ(test, size, ri->size); + + /* Add rule with write callback */ + ret = vcap_add_rule(rule); + KUNIT_EXPECT_EQ(test, 0, ret); + KUNIT_EXPECT_EQ(test, expected_addr, ri->addr); + return rule; +} + +/* Prepare testing rule deletion */ +static void test_init_rule_deletion(void) +{ + test_move_addr = 0; + test_move_offset = 0; + test_move_count = 0; + test_init_start = 0; + test_init_count = 0; +} + /* Define the test cases. */ static void vcap_api_set_bit_1_test(struct kunit *test) @@ -1333,6 +1421,414 @@ static void vcap_api_encode_rule_test(struct kunit *test) KUNIT_EXPECT_EQ(test, true, ret); } +static void vcap_api_set_rule_counter_test(struct kunit *test) +{ + struct vcap_admin is2_admin = { + .cache = { + .counter = 100, + .sticky = true, + }, + }; + struct vcap_rule_internal ri = { + .data = { + .id = 1001, + }, + .addr = 600, + .admin = &is2_admin, + .counter_id = 1002, + .vctrl = &test_vctrl, + }; + struct vcap_rule_internal ri2 = { + .data = { + .id = 2001, + }, + .addr = 700, + .admin = &is2_admin, + .counter_id = 2002, + .vctrl = &test_vctrl, + }; + struct vcap_counter ctr = { .value = 0, .sticky = false}; + struct vcap_counter ctr2 = { .value = 101, .sticky = true}; + int ret; + + vcap_test_api_init(&is2_admin); + list_add_tail(&ri.list, &is2_admin.rules); + list_add_tail(&ri2.list, &is2_admin.rules); + + pr_info("%s:%d\n", __func__, __LINE__); + ret = vcap_rule_set_counter(&ri.data, &ctr); + pr_info("%s:%d\n", __func__, __LINE__); + KUNIT_EXPECT_EQ(test, 0, ret); + + KUNIT_EXPECT_EQ(test, 1002, test_hw_counter_id); + KUNIT_EXPECT_EQ(test, 0, test_hw_cache.counter); + KUNIT_EXPECT_EQ(test, false, test_hw_cache.sticky); + KUNIT_EXPECT_EQ(test, 600, test_updateaddr[0]); + + ret = vcap_rule_set_counter(&ri2.data, &ctr2); + KUNIT_EXPECT_EQ(test, 0, ret); + + KUNIT_EXPECT_EQ(test, 2002, test_hw_counter_id); + KUNIT_EXPECT_EQ(test, 101, test_hw_cache.counter); + KUNIT_EXPECT_EQ(test, true, test_hw_cache.sticky); + KUNIT_EXPECT_EQ(test, 700, test_updateaddr[1]); +} + +static void vcap_api_get_rule_counter_test(struct kunit *test) +{ + struct vcap_admin is2_admin = { + .cache = { + .counter = 100, + .sticky = true, + }, + }; + struct vcap_rule_internal ri = { + .data = { + .id = 1010, + }, + .addr = 400, + .admin = &is2_admin, + .counter_id = 1011, + .vctrl = &test_vctrl, + }; + struct vcap_rule_internal ri2 = { + .data = { + .id = 2011, + }, + .addr = 300, + .admin = &is2_admin, + .counter_id = 2012, + .vctrl = &test_vctrl, + }; + struct vcap_counter ctr = {}; + struct vcap_counter ctr2 = {}; + int ret; + + vcap_test_api_init(&is2_admin); + test_hw_cache.counter = 55; + test_hw_cache.sticky = true; + + list_add_tail(&ri.list, &is2_admin.rules); + list_add_tail(&ri2.list, &is2_admin.rules); + + ret = vcap_rule_get_counter(&ri.data, &ctr); + KUNIT_EXPECT_EQ(test, 0, ret); + + KUNIT_EXPECT_EQ(test, 1011, test_hw_counter_id); + KUNIT_EXPECT_EQ(test, 55, ctr.value); + KUNIT_EXPECT_EQ(test, true, ctr.sticky); + KUNIT_EXPECT_EQ(test, 400, test_updateaddr[0]); + + test_hw_cache.counter = 22; + test_hw_cache.sticky = false; + + ret = vcap_rule_get_counter(&ri2.data, &ctr2); + KUNIT_EXPECT_EQ(test, 0, ret); + + KUNIT_EXPECT_EQ(test, 2012, test_hw_counter_id); + KUNIT_EXPECT_EQ(test, 22, ctr2.value); + KUNIT_EXPECT_EQ(test, false, ctr2.sticky); + KUNIT_EXPECT_EQ(test, 300, test_updateaddr[1]); +} + +static void vcap_api_rule_insert_in_order_test(struct kunit *test) +{ + /* Data used by VCAP Library callback */ + static u32 keydata[32] = {}; + static u32 mskdata[32] = {}; + static u32 actdata[32] = {}; + + struct vcap_admin admin = { + .vtype = VCAP_TYPE_IS0, + .first_cid = 10000, + .last_cid = 19999, + .lookups = 4, + .last_valid_addr = 3071, + .first_valid_addr = 0, + .last_used_addr = 800, + .cache = { + .keystream = keydata, + .maskstream = mskdata, + .actionstream = actdata, + }, + }; + + vcap_test_api_init(&admin); + + /* Create rules with different sizes and check that they are placed + * at the correct address in the VCAP according to size + */ + test_vcap_xn_rule_creator(test, 10000, VCAP_USER_QOS, 10, 500, 12, 780); + test_vcap_xn_rule_creator(test, 10000, VCAP_USER_QOS, 20, 400, 6, 774); + test_vcap_xn_rule_creator(test, 10000, VCAP_USER_QOS, 30, 300, 3, 771); + test_vcap_xn_rule_creator(test, 10000, VCAP_USER_QOS, 40, 200, 2, 768); +} + +static void vcap_api_rule_insert_reverse_order_test(struct kunit *test) +{ + /* Data used by VCAP Library callback */ + static u32 keydata[32] = {}; + static u32 mskdata[32] = {}; + static u32 actdata[32] = {}; + + struct vcap_admin admin = { + .vtype = VCAP_TYPE_IS0, + .first_cid = 10000, + .last_cid = 19999, + .lookups = 4, + .last_valid_addr = 3071, + .first_valid_addr = 0, + .last_used_addr = 800, + .cache = { + .keystream = keydata, + .maskstream = mskdata, + .actionstream = actdata, + }, + }; + struct vcap_rule_internal *elem; + u32 exp_addr[] = {780, 774, 771, 768, 767}; + int idx; + + vcap_test_api_init(&admin); + + /* Create rules with different sizes and check that they are placed + * at the correct address in the VCAP according to size + */ + test_vcap_xn_rule_creator(test, 10000, VCAP_USER_QOS, 20, 200, 2, 798); + KUNIT_EXPECT_EQ(test, 0, test_move_offset); + KUNIT_EXPECT_EQ(test, 0, test_move_count); + KUNIT_EXPECT_EQ(test, 0, test_move_addr); + + test_vcap_xn_rule_creator(test, 10000, VCAP_USER_QOS, 30, 300, 3, 795); + KUNIT_EXPECT_EQ(test, 6, test_move_offset); + KUNIT_EXPECT_EQ(test, 3, test_move_count); + KUNIT_EXPECT_EQ(test, 798, test_move_addr); + + test_vcap_xn_rule_creator(test, 10000, VCAP_USER_QOS, 40, 400, 6, 792); + KUNIT_EXPECT_EQ(test, 6, test_move_offset); + KUNIT_EXPECT_EQ(test, 6, test_move_count); + KUNIT_EXPECT_EQ(test, 792, test_move_addr); + + test_vcap_xn_rule_creator(test, 10000, VCAP_USER_QOS, 50, 500, 12, 780); + KUNIT_EXPECT_EQ(test, 18, test_move_offset); + KUNIT_EXPECT_EQ(test, 12, test_move_count); + KUNIT_EXPECT_EQ(test, 786, test_move_addr); + + idx = 0; + list_for_each_entry(elem, &admin.rules, list) { + KUNIT_EXPECT_EQ(test, exp_addr[idx], elem->addr); + ++idx; + } + KUNIT_EXPECT_EQ(test, 768, admin.last_used_addr); +} + +static void vcap_api_rule_remove_at_end_test(struct kunit *test) +{ + /* Data used by VCAP Library callback */ + static u32 keydata[32] = {}; + static u32 mskdata[32] = {}; + static u32 actdata[32] = {}; + + struct vcap_admin admin = { + .vtype = VCAP_TYPE_IS0, + .first_cid = 10000, + .last_cid = 19999, + .lookups = 4, + .last_valid_addr = 3071, + .first_valid_addr = 0, + .last_used_addr = 800, + .cache = { + .keystream = keydata, + .maskstream = mskdata, + .actionstream = actdata, + }, + }; + int ret; + + vcap_test_api_init(&admin); + test_init_rule_deletion(); + + /* Create rules with different sizes and check that they are placed + * at the correct address in the VCAP according to size + */ + test_vcap_xn_rule_creator(test, 10000, VCAP_USER_QOS, 10, 500, 12, 780); + test_vcap_xn_rule_creator(test, 10000, VCAP_USER_QOS, 20, 400, 6, 774); + test_vcap_xn_rule_creator(test, 10000, VCAP_USER_QOS, 30, 300, 3, 771); + test_vcap_xn_rule_creator(test, 10000, VCAP_USER_QOS, 40, 200, 2, 768); + + /* Remove rules again from the end */ + ret = vcap_del_rule(&test_vctrl, &test_netdev, 200); + KUNIT_EXPECT_EQ(test, 0, ret); + KUNIT_EXPECT_EQ(test, 0, test_move_addr); + KUNIT_EXPECT_EQ(test, 0, test_move_offset); + KUNIT_EXPECT_EQ(test, 0, test_move_count); + KUNIT_EXPECT_EQ(test, 768, test_init_start); + KUNIT_EXPECT_EQ(test, 2, test_init_count); + KUNIT_EXPECT_EQ(test, 771, admin.last_used_addr); + + ret = vcap_del_rule(&test_vctrl, &test_netdev, 300); + KUNIT_EXPECT_EQ(test, ret, 0); + KUNIT_EXPECT_EQ(test, 0, test_move_addr); + KUNIT_EXPECT_EQ(test, 0, test_move_offset); + KUNIT_EXPECT_EQ(test, 0, test_move_count); + KUNIT_EXPECT_EQ(test, 771, test_init_start); + KUNIT_EXPECT_EQ(test, 3, test_init_count); + KUNIT_EXPECT_EQ(test, 774, admin.last_used_addr); + + ret = vcap_del_rule(&test_vctrl, &test_netdev, 400); + KUNIT_EXPECT_EQ(test, ret, 0); + KUNIT_EXPECT_EQ(test, 0, test_move_addr); + KUNIT_EXPECT_EQ(test, 0, test_move_offset); + KUNIT_EXPECT_EQ(test, 0, test_move_count); + KUNIT_EXPECT_EQ(test, 774, test_init_start); + KUNIT_EXPECT_EQ(test, 6, test_init_count); + KUNIT_EXPECT_EQ(test, 780, admin.last_used_addr); + + ret = vcap_del_rule(&test_vctrl, &test_netdev, 500); + KUNIT_EXPECT_EQ(test, ret, 0); + KUNIT_EXPECT_EQ(test, 0, test_move_addr); + KUNIT_EXPECT_EQ(test, 0, test_move_offset); + KUNIT_EXPECT_EQ(test, 0, test_move_count); + KUNIT_EXPECT_EQ(test, 780, test_init_start); + KUNIT_EXPECT_EQ(test, 12, test_init_count); + KUNIT_EXPECT_EQ(test, 3071, admin.last_used_addr); +} + +static void vcap_api_rule_remove_in_middle_test(struct kunit *test) +{ + /* Data used by VCAP Library callback */ + static u32 keydata[32] = {}; + static u32 mskdata[32] = {}; + static u32 actdata[32] = {}; + + struct vcap_admin admin = { + .vtype = VCAP_TYPE_IS0, + .first_cid = 10000, + .last_cid = 19999, + .lookups = 4, + .first_valid_addr = 0, + .last_used_addr = 800, + .last_valid_addr = 800 - 1, + .cache = { + .keystream = keydata, + .maskstream = mskdata, + .actionstream = actdata, + }, + }; + int ret; + + vcap_test_api_init(&admin); + + /* Create rules with different sizes and check that they are placed + * at the correct address in the VCAP according to size + */ + test_vcap_xn_rule_creator(test, 10000, VCAP_USER_QOS, 10, 500, 12, 780); + test_vcap_xn_rule_creator(test, 10000, VCAP_USER_QOS, 20, 400, 6, 774); + test_vcap_xn_rule_creator(test, 10000, VCAP_USER_QOS, 30, 300, 3, 771); + test_vcap_xn_rule_creator(test, 10000, VCAP_USER_QOS, 40, 200, 2, 768); + + /* Remove rules in the middle */ + test_init_rule_deletion(); + ret = vcap_del_rule(&test_vctrl, &test_netdev, 400); + KUNIT_EXPECT_EQ(test, 0, ret); + KUNIT_EXPECT_EQ(test, 768, test_move_addr); + KUNIT_EXPECT_EQ(test, -6, test_move_offset); + KUNIT_EXPECT_EQ(test, 6, test_move_count); + KUNIT_EXPECT_EQ(test, 768, test_init_start); + KUNIT_EXPECT_EQ(test, 6, test_init_count); + KUNIT_EXPECT_EQ(test, 774, admin.last_used_addr); + + test_init_rule_deletion(); + ret = vcap_del_rule(&test_vctrl, &test_netdev, 300); + KUNIT_EXPECT_EQ(test, 0, ret); + KUNIT_EXPECT_EQ(test, 774, test_move_addr); + KUNIT_EXPECT_EQ(test, -4, test_move_offset); + KUNIT_EXPECT_EQ(test, 2, test_move_count); + KUNIT_EXPECT_EQ(test, 774, test_init_start); + KUNIT_EXPECT_EQ(test, 4, test_init_count); + KUNIT_EXPECT_EQ(test, 778, admin.last_used_addr); + + test_init_rule_deletion(); + ret = vcap_del_rule(&test_vctrl, &test_netdev, 500); + KUNIT_EXPECT_EQ(test, 0, ret); + KUNIT_EXPECT_EQ(test, 778, test_move_addr); + KUNIT_EXPECT_EQ(test, -20, test_move_offset); + KUNIT_EXPECT_EQ(test, 2, test_move_count); + KUNIT_EXPECT_EQ(test, 778, test_init_start); + KUNIT_EXPECT_EQ(test, 20, test_init_count); + KUNIT_EXPECT_EQ(test, 798, admin.last_used_addr); + + test_init_rule_deletion(); + ret = vcap_del_rule(&test_vctrl, &test_netdev, 200); + KUNIT_EXPECT_EQ(test, 0, ret); + KUNIT_EXPECT_EQ(test, 0, test_move_addr); + KUNIT_EXPECT_EQ(test, 0, test_move_offset); + KUNIT_EXPECT_EQ(test, 0, test_move_count); + KUNIT_EXPECT_EQ(test, 798, test_init_start); + KUNIT_EXPECT_EQ(test, 2, test_init_count); + KUNIT_EXPECT_EQ(test, 799, admin.last_used_addr); +} + +static void vcap_api_rule_remove_in_front_test(struct kunit *test) +{ + /* Data used by VCAP Library callback */ + static u32 keydata[32] = {}; + static u32 mskdata[32] = {}; + static u32 actdata[32] = {}; + + struct vcap_admin admin = { + .vtype = VCAP_TYPE_IS0, + .first_cid = 10000, + .last_cid = 19999, + .lookups = 4, + .first_valid_addr = 0, + .last_used_addr = 800, + .last_valid_addr = 800 - 1, + .cache = { + .keystream = keydata, + .maskstream = mskdata, + .actionstream = actdata, + }, + }; + int ret; + + vcap_test_api_init(&admin); + + test_vcap_xn_rule_creator(test, 10000, VCAP_USER_QOS, 10, 500, 12, 780); + KUNIT_EXPECT_EQ(test, 780, admin.last_used_addr); + + test_init_rule_deletion(); + ret = vcap_del_rule(&test_vctrl, &test_netdev, 500); + KUNIT_EXPECT_EQ(test, 0, ret); + KUNIT_EXPECT_EQ(test, 0, test_move_addr); + KUNIT_EXPECT_EQ(test, 0, test_move_offset); + KUNIT_EXPECT_EQ(test, 0, test_move_count); + KUNIT_EXPECT_EQ(test, 780, test_init_start); + KUNIT_EXPECT_EQ(test, 12, test_init_count); + KUNIT_EXPECT_EQ(test, 799, admin.last_used_addr); + + test_vcap_xn_rule_creator(test, 10000, VCAP_USER_QOS, 20, 400, 6, 792); + test_vcap_xn_rule_creator(test, 10000, VCAP_USER_QOS, 30, 300, 3, 789); + test_vcap_xn_rule_creator(test, 10000, VCAP_USER_QOS, 40, 200, 2, 786); + + test_init_rule_deletion(); + ret = vcap_del_rule(&test_vctrl, &test_netdev, 400); + KUNIT_EXPECT_EQ(test, 0, ret); + KUNIT_EXPECT_EQ(test, 786, test_move_addr); + KUNIT_EXPECT_EQ(test, -8, test_move_offset); + KUNIT_EXPECT_EQ(test, 6, test_move_count); + KUNIT_EXPECT_EQ(test, 786, test_init_start); + KUNIT_EXPECT_EQ(test, 8, test_init_count); + KUNIT_EXPECT_EQ(test, 794, admin.last_used_addr); +} + +static struct kunit_case vcap_api_rule_remove_test_cases[] = { + KUNIT_CASE(vcap_api_rule_remove_at_end_test), + KUNIT_CASE(vcap_api_rule_remove_in_middle_test), + KUNIT_CASE(vcap_api_rule_remove_in_front_test), + {} +}; + static void vcap_api_next_lookup_basic_test(struct kunit *test) { struct vcap_admin admin1 = { @@ -1458,6 +1954,33 @@ static void vcap_api_next_lookup_advanced_test(struct kunit *test) KUNIT_EXPECT_EQ(test, true, ret); } +static struct kunit_suite vcap_api_rule_remove_test_suite = { + .name = "VCAP_API_Rule_Remove_Testsuite", + .test_cases = vcap_api_rule_remove_test_cases, +}; + +static struct kunit_case vcap_api_rule_insert_test_cases[] = { + KUNIT_CASE(vcap_api_rule_insert_in_order_test), + KUNIT_CASE(vcap_api_rule_insert_reverse_order_test), + {} +}; + +static struct kunit_suite vcap_api_rule_insert_test_suite = { + .name = "VCAP_API_Rule_Insert_Testsuite", + .test_cases = vcap_api_rule_insert_test_cases, +}; + +static struct kunit_case vcap_api_rule_counter_test_cases[] = { + KUNIT_CASE(vcap_api_set_rule_counter_test), + KUNIT_CASE(vcap_api_get_rule_counter_test), + {} +}; + +static struct kunit_suite vcap_api_rule_counter_test_suite = { + .name = "VCAP_API_Rule_Counter_Testsuite", + .test_cases = vcap_api_rule_counter_test_cases, +}; + static struct kunit_case vcap_api_support_test_cases[] = { KUNIT_CASE(vcap_api_next_lookup_basic_test), KUNIT_CASE(vcap_api_next_lookup_advanced_test), @@ -1519,6 +2042,9 @@ static struct kunit_suite vcap_api_encoding_test_suite = { .test_cases = vcap_api_encoding_test_cases, }; +kunit_test_suite(vcap_api_rule_remove_test_suite); +kunit_test_suite(vcap_api_rule_insert_test_suite); +kunit_test_suite(vcap_api_rule_counter_test_suite); kunit_test_suite(vcap_api_support_test_suite); kunit_test_suite(vcap_api_full_rule_test_suite); kunit_test_suite(vcap_api_rule_value_test_suite); From 6970ef27ff7fd1ce3455b2c696081503d0c0f8ac Mon Sep 17 00:00:00 2001 From: Shenwei Wang Date: Fri, 11 Nov 2022 09:35:05 -0600 Subject: [PATCH 0902/2002] net: fec: add xdp and page pool statistics Added xdp and page pool statistics. In order to make the implementation simple and compatible, the patch uses the 32bit integer to record the XDP statistics. Signed-off-by: Shenwei Wang Reported-by: kernel test robot Signed-off-by: David S. Miller --- drivers/net/ethernet/freescale/Kconfig | 1 + drivers/net/ethernet/freescale/fec.h | 14 ++++ drivers/net/ethernet/freescale/fec_main.c | 85 +++++++++++++++++++++-- 3 files changed, 95 insertions(+), 5 deletions(-) diff --git a/drivers/net/ethernet/freescale/Kconfig b/drivers/net/ethernet/freescale/Kconfig index ce866ae3df03..f1e80d6996ef 100644 --- a/drivers/net/ethernet/freescale/Kconfig +++ b/drivers/net/ethernet/freescale/Kconfig @@ -29,6 +29,7 @@ config FEC select CRC32 select PHYLIB select PAGE_POOL + select PAGE_POOL_STATS imply NET_SELFTESTS help Say Y here if you want to use the built-in 10/100 Fast ethernet diff --git a/drivers/net/ethernet/freescale/fec.h b/drivers/net/ethernet/freescale/fec.h index 61e847b18343..5ba1e0d71c68 100644 --- a/drivers/net/ethernet/freescale/fec.h +++ b/drivers/net/ethernet/freescale/fec.h @@ -526,6 +526,19 @@ struct fec_enet_priv_txrx_info { struct sk_buff *skb; }; +enum { + RX_XDP_REDIRECT = 0, + RX_XDP_PASS, + RX_XDP_DROP, + RX_XDP_TX, + RX_XDP_TX_ERRORS, + TX_XDP_XMIT, + TX_XDP_XMIT_ERRORS, + + /* The following must be the last one */ + XDP_STATS_TOTAL, +}; + struct fec_enet_priv_tx_q { struct bufdesc_prop bd; unsigned char *tx_bounce[TX_RING_SIZE]; @@ -546,6 +559,7 @@ struct fec_enet_priv_rx_q { /* page_pool */ struct page_pool *page_pool; struct xdp_rxq_info xdp_rxq; + u32 stats[XDP_STATS_TOTAL]; /* rx queue number, in the range 0-7 */ u8 id; diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c index 616eea712ca8..879dfee3dc41 100644 --- a/drivers/net/ethernet/freescale/fec_main.c +++ b/drivers/net/ethernet/freescale/fec_main.c @@ -1523,10 +1523,12 @@ fec_enet_run_xdp(struct fec_enet_private *fep, struct bpf_prog *prog, switch (act) { case XDP_PASS: + rxq->stats[RX_XDP_PASS]++; ret = FEC_ENET_XDP_PASS; break; case XDP_REDIRECT: + rxq->stats[RX_XDP_REDIRECT]++; err = xdp_do_redirect(fep->netdev, xdp, prog); if (!err) { ret = FEC_ENET_XDP_REDIR; @@ -1549,6 +1551,7 @@ fec_enet_run_xdp(struct fec_enet_private *fep, struct bpf_prog *prog, fallthrough; /* handle aborts by dropping packet */ case XDP_DROP: + rxq->stats[RX_XDP_DROP]++; ret = FEC_ENET_XDP_CONSUMED; page = virt_to_head_page(xdp->data); page_pool_put_page(rxq->page_pool, page, sync, true); @@ -2701,6 +2704,16 @@ static const struct fec_stat { #define FEC_STATS_SIZE (ARRAY_SIZE(fec_stats) * sizeof(u64)) +static const char *fec_xdp_stat_strs[XDP_STATS_TOTAL] = { + "rx_xdp_redirect", /* RX_XDP_REDIRECT = 0, */ + "rx_xdp_pass", /* RX_XDP_PASS, */ + "rx_xdp_drop", /* RX_XDP_DROP, */ + "rx_xdp_tx", /* RX_XDP_TX, */ + "rx_xdp_tx_errors", /* RX_XDP_TX_ERRORS, */ + "tx_xdp_xmit", /* TX_XDP_XMIT, */ + "tx_xdp_xmit_errors", /* TX_XDP_XMIT_ERRORS, */ +}; + static void fec_enet_update_ethtool_stats(struct net_device *dev) { struct fec_enet_private *fep = netdev_priv(dev); @@ -2710,6 +2723,40 @@ static void fec_enet_update_ethtool_stats(struct net_device *dev) fep->ethtool_stats[i] = readl(fep->hwp + fec_stats[i].offset); } +static void fec_enet_get_xdp_stats(struct fec_enet_private *fep, u64 *data) +{ + u64 xdp_stats[XDP_STATS_TOTAL] = { 0 }; + struct fec_enet_priv_rx_q *rxq; + int i, j; + + for (i = fep->num_rx_queues - 1; i >= 0; i--) { + rxq = fep->rx_queue[i]; + + for (j = 0; j < XDP_STATS_TOTAL; j++) + xdp_stats[j] += rxq->stats[j]; + } + + memcpy(data, xdp_stats, sizeof(xdp_stats)); +} + +static void fec_enet_page_pool_stats(struct fec_enet_private *fep, u64 *data) +{ + struct page_pool_stats stats = {}; + struct fec_enet_priv_rx_q *rxq; + int i; + + for (i = fep->num_rx_queues - 1; i >= 0; i--) { + rxq = fep->rx_queue[i]; + + if (!rxq->page_pool) + continue; + + page_pool_get_stats(rxq->page_pool, &stats); + } + + page_pool_ethtool_stats_get(data, &stats); +} + static void fec_enet_get_ethtool_stats(struct net_device *dev, struct ethtool_stats *stats, u64 *data) { @@ -2719,6 +2766,12 @@ static void fec_enet_get_ethtool_stats(struct net_device *dev, fec_enet_update_ethtool_stats(dev); memcpy(data, fep->ethtool_stats, FEC_STATS_SIZE); + data += FEC_STATS_SIZE / sizeof(u64); + + fec_enet_get_xdp_stats(fep, data); + data += XDP_STATS_TOTAL; + + fec_enet_page_pool_stats(fep, data); } static void fec_enet_get_strings(struct net_device *netdev, @@ -2727,9 +2780,16 @@ static void fec_enet_get_strings(struct net_device *netdev, int i; switch (stringset) { case ETH_SS_STATS: - for (i = 0; i < ARRAY_SIZE(fec_stats); i++) - memcpy(data + i * ETH_GSTRING_LEN, - fec_stats[i].name, ETH_GSTRING_LEN); + for (i = 0; i < ARRAY_SIZE(fec_stats); i++) { + memcpy(data, fec_stats[i].name, ETH_GSTRING_LEN); + data += ETH_GSTRING_LEN; + } + for (i = 0; i < ARRAY_SIZE(fec_xdp_stat_strs); i++) { + strncpy(data, fec_xdp_stat_strs[i], ETH_GSTRING_LEN); + data += ETH_GSTRING_LEN; + } + page_pool_ethtool_stats_get_strings(data); + break; case ETH_SS_TEST: net_selftest_get_strings(data); @@ -2739,9 +2799,14 @@ static void fec_enet_get_strings(struct net_device *netdev, static int fec_enet_get_sset_count(struct net_device *dev, int sset) { + int count; + switch (sset) { case ETH_SS_STATS: - return ARRAY_SIZE(fec_stats); + count = ARRAY_SIZE(fec_stats) + XDP_STATS_TOTAL; + count += page_pool_ethtool_stats_get_count(); + return count; + case ETH_SS_TEST: return net_selftest_get_count(); default: @@ -2752,7 +2817,8 @@ static int fec_enet_get_sset_count(struct net_device *dev, int sset) static void fec_enet_clear_ethtool_stats(struct net_device *dev) { struct fec_enet_private *fep = netdev_priv(dev); - int i; + struct fec_enet_priv_rx_q *rxq; + int i, j; /* Disable MIB statistics counters */ writel(FEC_MIB_CTRLSTAT_DISABLE, fep->hwp + FEC_MIB_CTRLSTAT); @@ -2760,6 +2826,12 @@ static void fec_enet_clear_ethtool_stats(struct net_device *dev) for (i = 0; i < ARRAY_SIZE(fec_stats); i++) writel(0, fep->hwp + fec_stats[i].offset); + for (i = fep->num_rx_queues - 1; i >= 0; i--) { + rxq = fep->rx_queue[i]; + for (j = 0; j < XDP_STATS_TOTAL; j++) + rxq->stats[j] = 0; + } + /* Don't disable MIB statistics counters */ writel(0, fep->hwp + FEC_MIB_CTRLSTAT); } @@ -3126,6 +3198,9 @@ static void fec_enet_free_buffers(struct net_device *ndev) for (i = 0; i < rxq->bd.ring_size; i++) page_pool_release_page(rxq->page_pool, rxq->rx_skb_info[i].page); + for (i = 0; i < XDP_STATS_TOTAL; i++) + rxq->stats[i] = 0; + if (xdp_rxq_info_is_reg(&rxq->xdp_rxq)) xdp_rxq_info_unreg(&rxq->xdp_rxq); page_pool_destroy(rxq->page_pool); From dfd0afbf151d85411b371e841f62b81ee5d1ca54 Mon Sep 17 00:00:00 2001 From: David Michael Date: Sun, 13 Nov 2022 15:52:17 -0500 Subject: [PATCH 0903/2002] libbpf: Fix uninitialized warning in btf_dump_dump_type_data MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit GCC 11.3.0 fails to compile btf_dump.c due to the following error, which seems to originate in btf_dump_struct_data where the returned value would be uninitialized if btf_vlen returns zero. btf_dump.c: In function ‘btf_dump_dump_type_data’: btf_dump.c:2363:12: error: ‘err’ may be used uninitialized in this function [-Werror=maybe-uninitialized] 2363 | if (err < 0) | ^ Fixes: 920d16af9b42 ("libbpf: BTF dumper support for typed data") Signed-off-by: David Michael Signed-off-by: Daniel Borkmann Acked-by: Stanislav Fomichev Acked-by: Alan Maguire Link: https://lore.kernel.org/bpf/87zgcu60hq.fsf@gmail.com --- tools/lib/bpf/btf_dump.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/lib/bpf/btf_dump.c b/tools/lib/bpf/btf_dump.c index 12f7039e0ab2..e9f849d82124 100644 --- a/tools/lib/bpf/btf_dump.c +++ b/tools/lib/bpf/btf_dump.c @@ -1989,7 +1989,7 @@ static int btf_dump_struct_data(struct btf_dump *d, { const struct btf_member *m = btf_members(t); __u16 n = btf_vlen(t); - int i, err; + int i, err = 0; /* note that we increment depth before calling btf_dump_print() below; * this is intentional. btf_dump_data_newline() will not print a From e662c7753668bbfb95e25043c6064088cc3a996d Mon Sep 17 00:00:00 2001 From: Maryam Tahhan Date: Sun, 13 Nov 2022 05:33:27 -0500 Subject: [PATCH 0904/2002] bpf, docs: Fixup cpumap sphinx >= 3.1 warning Fixup bpf_map_update_elem() declaration to use a single line. Reported-by: Akira Yokosawa Signed-off-by: Maryam Tahhan Signed-off-by: Daniel Borkmann Tested-by: Akira Yokosawa Acked-by: Stanislav Fomichev Link: https://lore.kernel.org/bpf/20221113103327.3287482-1-mtahhan@redhat.com --- Documentation/bpf/map_cpumap.rst | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Documentation/bpf/map_cpumap.rst b/Documentation/bpf/map_cpumap.rst index eaf57b38cafd..61a797a86342 100644 --- a/Documentation/bpf/map_cpumap.rst +++ b/Documentation/bpf/map_cpumap.rst @@ -48,8 +48,7 @@ Userspace program will result in the program failing to load and a verifier warning. .. c:function:: - int bpf_map_update_elem(int fd, const void *key, const void *value, - __u64 flags); + int bpf_map_update_elem(int fd, const void *key, const void *value, __u64 flags); CPU entries can be added or updated using the ``bpf_map_update_elem()`` helper. This helper replaces existing elements atomically. The ``value`` parameter From c7694ac340b0394afba079fc3d1072f4c99bcfc9 Mon Sep 17 00:00:00 2001 From: Kang Minchul Date: Mon, 14 Nov 2022 04:06:46 +0900 Subject: [PATCH 0905/2002] libbpf: checkpatch: Fixed code alignments in btf.c Fixed some checkpatch issues in btf.c Signed-off-by: Kang Minchul Signed-off-by: Andrii Nakryiko Acked-by: Stanislav Fomichev Link: https://lore.kernel.org/bpf/20221113190648.38556-2-tegongkang@gmail.com --- tools/lib/bpf/btf.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tools/lib/bpf/btf.c b/tools/lib/bpf/btf.c index 3bd812bf88ff..71e165b09ed5 100644 --- a/tools/lib/bpf/btf.c +++ b/tools/lib/bpf/btf.c @@ -1724,7 +1724,8 @@ err_out: memset(btf->strs_data + old_strs_len, 0, btf->hdr->str_len - old_strs_len); /* and now restore original strings section size; types data size - * wasn't modified, so doesn't need restoring, see big comment above */ + * wasn't modified, so doesn't need restoring, see big comment above + */ btf->hdr->str_len = old_strs_len; hashmap__free(p.str_off_map); @@ -2329,7 +2330,7 @@ int btf__add_restrict(struct btf *btf, int ref_type_id) */ int btf__add_type_tag(struct btf *btf, const char *value, int ref_type_id) { - if (!value|| !value[0]) + if (!value || !value[0]) return libbpf_err(-EINVAL); return btf_add_ref_kind(btf, BTF_KIND_TYPE_TAG, value, ref_type_id); From e3ba8e4e8c19a9b4d1866bc72d81afb10c043560 Mon Sep 17 00:00:00 2001 From: Kang Minchul Date: Mon, 14 Nov 2022 04:06:47 +0900 Subject: [PATCH 0906/2002] libbpf: Fixed various checkpatch issues in libbpf.c Fixed following checkpatch issues: WARNING: Block comments use a trailing */ on a separate line + * other BPF program's BTF object */ WARNING: Possible repeated word: 'be' + * name. This is important to be be able to find corresponding BTF ERROR: switch and case should be at the same indent + switch (ext->kcfg.sz) { + case 1: *(__u8 *)ext_val = value; break; + case 2: *(__u16 *)ext_val = value; break; + case 4: *(__u32 *)ext_val = value; break; + case 8: *(__u64 *)ext_val = value; break; + default: ERROR: trailing statements should be on next line + case 1: *(__u8 *)ext_val = value; break; ERROR: trailing statements should be on next line + case 2: *(__u16 *)ext_val = value; break; ERROR: trailing statements should be on next line + case 4: *(__u32 *)ext_val = value; break; ERROR: trailing statements should be on next line + case 8: *(__u64 *)ext_val = value; break; ERROR: code indent should use tabs where possible + }$ WARNING: please, no spaces at the start of a line + }$ WARNING: Block comments use a trailing */ on a separate line + * for faster search */ ERROR: code indent should use tabs where possible +^I^I^I^I^I^I &ext->kcfg.is_signed);$ WARNING: braces {} are not necessary for single statement blocks + if (err) { + return err; + } ERROR: code indent should use tabs where possible +^I^I^I^I sizeof(*obj->btf_modules), obj->btf_module_cnt + 1);$ Signed-off-by: Kang Minchul Signed-off-by: Andrii Nakryiko Acked-by: Stanislav Fomichev Link: https://lore.kernel.org/bpf/20221113190648.38556-3-tegongkang@gmail.com --- tools/lib/bpf/libbpf.c | 45 ++++++++++++++++++++++++++---------------- 1 file changed, 28 insertions(+), 17 deletions(-) diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c index 1d263885d635..b5df6aca06ea 100644 --- a/tools/lib/bpf/libbpf.c +++ b/tools/lib/bpf/libbpf.c @@ -347,7 +347,8 @@ enum sec_def_flags { SEC_ATTACHABLE = 2, SEC_ATTACHABLE_OPT = SEC_ATTACHABLE | SEC_EXP_ATTACH_OPT, /* attachment target is specified through BTF ID in either kernel or - * other BPF program's BTF object */ + * other BPF program's BTF object + */ SEC_ATTACH_BTF = 4, /* BPF program type allows sleeping/blocking in kernel */ SEC_SLEEPABLE = 8, @@ -488,7 +489,7 @@ struct bpf_map { char *name; /* real_name is defined for special internal maps (.rodata*, * .data*, .bss, .kconfig) and preserves their original ELF section - * name. This is important to be be able to find corresponding BTF + * name. This is important to be able to find corresponding BTF * DATASEC information. */ char *real_name; @@ -1863,12 +1864,20 @@ static int set_kcfg_value_num(struct extern_desc *ext, void *ext_val, return -ERANGE; } switch (ext->kcfg.sz) { - case 1: *(__u8 *)ext_val = value; break; - case 2: *(__u16 *)ext_val = value; break; - case 4: *(__u32 *)ext_val = value; break; - case 8: *(__u64 *)ext_val = value; break; - default: - return -EINVAL; + case 1: + *(__u8 *)ext_val = value; + break; + case 2: + *(__u16 *)ext_val = value; + break; + case 4: + *(__u32 *)ext_val = value; + break; + case 8: + *(__u64 *)ext_val = value; + break; + default: + return -EINVAL; } ext->is_set = true; return 0; @@ -2770,7 +2779,7 @@ static int bpf_object__sanitize_btf(struct bpf_object *obj, struct btf *btf) m->type = enum64_placeholder_id; m->offset = 0; } - } + } } return 0; @@ -3518,7 +3527,8 @@ static int bpf_object__elf_collect(struct bpf_object *obj) } /* sort BPF programs by section name and in-section instruction offset - * for faster search */ + * for faster search + */ if (obj->nr_programs) qsort(obj->programs, obj->nr_programs, sizeof(*obj->programs), cmp_progs); @@ -3817,7 +3827,7 @@ static int bpf_object__collect_externs(struct bpf_object *obj) return -EINVAL; } ext->kcfg.type = find_kcfg_type(obj->btf, t->type, - &ext->kcfg.is_signed); + &ext->kcfg.is_signed); if (ext->kcfg.type == KCFG_UNKNOWN) { pr_warn("extern (kcfg) '%s': type is unsupported\n", ext_name); return -ENOTSUP; @@ -4965,9 +4975,9 @@ bpf_object__reuse_map(struct bpf_map *map) err = bpf_map__reuse_fd(map, pin_fd); close(pin_fd); - if (err) { + if (err) return err; - } + map->pinned = true; pr_debug("reused pinned map at '%s'\n", map->pin_path); @@ -5485,7 +5495,7 @@ static int load_module_btfs(struct bpf_object *obj) } err = libbpf_ensure_mem((void **)&obj->btf_modules, &obj->btf_module_cap, - sizeof(*obj->btf_modules), obj->btf_module_cnt + 1); + sizeof(*obj->btf_modules), obj->btf_module_cnt + 1); if (err) goto err_out; @@ -6237,7 +6247,8 @@ bpf_object__reloc_code(struct bpf_object *obj, struct bpf_program *main_prog, * prog; each main prog can have a different set of * subprograms appended (potentially in different order as * well), so position of any subprog can be different for - * different main programs */ + * different main programs + */ insn->imm = subprog->sub_insn_off - (prog->sub_insn_off + insn_idx) - 1; pr_debug("prog '%s': insn #%zu relocated, imm %d points to subprog '%s' (now at %zu offset)\n", @@ -10995,7 +11006,7 @@ struct bpf_link *bpf_program__attach_usdt(const struct bpf_program *prog, usdt_cookie = OPTS_GET(opts, usdt_cookie, 0); link = usdt_manager_attach_usdt(obj->usdt_man, prog, pid, binary_path, - usdt_provider, usdt_name, usdt_cookie); + usdt_provider, usdt_name, usdt_cookie); err = libbpf_get_error(link); if (err) return libbpf_err_ptr(err); @@ -12304,7 +12315,7 @@ int bpf_object__open_subskeleton(struct bpf_object_subskeleton *s) btf = bpf_object__btf(s->obj); if (!btf) { pr_warn("subskeletons require BTF at runtime (object %s)\n", - bpf_object__name(s->obj)); + bpf_object__name(s->obj)); return libbpf_err(-errno); } From b486d19a0ab097eecf3ee679369b216d2cb6c34e Mon Sep 17 00:00:00 2001 From: Kang Minchul Date: Mon, 14 Nov 2022 04:06:48 +0900 Subject: [PATCH 0907/2002] libbpf: checkpatch: Fixed code alignments in ringbuf.c Fixed some checkpatch issues in ringbuf.c Signed-off-by: Kang Minchul Signed-off-by: Andrii Nakryiko Acked-by: Stanislav Fomichev Link: https://lore.kernel.org/bpf/20221113190648.38556-4-tegongkang@gmail.com --- tools/lib/bpf/ringbuf.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/lib/bpf/ringbuf.c b/tools/lib/bpf/ringbuf.c index d285171d4b69..51808c5f0014 100644 --- a/tools/lib/bpf/ringbuf.c +++ b/tools/lib/bpf/ringbuf.c @@ -128,7 +128,7 @@ int ring_buffer__add(struct ring_buffer *rb, int map_fd, /* Map read-only producer page and data pages. We map twice as big * data size to allow simple reading of samples that wrap around the * end of a ring buffer. See kernel implementation for details. - * */ + */ tmp = mmap(NULL, rb->page_size + 2 * info.max_entries, PROT_READ, MAP_SHARED, map_fd, rb->page_size); if (tmp == MAP_FAILED) { @@ -220,7 +220,7 @@ static inline int roundup_len(__u32 len) return (len + 7) / 8 * 8; } -static int64_t ringbuf_process_ring(struct ring* r) +static int64_t ringbuf_process_ring(struct ring *r) { int *len_ptr, len, err; /* 64-bit to avoid overflow in case of extreme application behavior */ From 76ad97e150f1079163c8503c7b8e249db994b244 Mon Sep 17 00:00:00 2001 From: Yoshihiro Shimoda Date: Thu, 10 Nov 2022 10:27:20 +0900 Subject: [PATCH 0908/2002] net: ethernet: renesas: rswitch: Fix build error about ptp If CONFIG_PTP_1588_CLOCK_OPTIONAL=m and CONFIG_RENESAS_ETHER_SWITCH=y, the following build error happened: aarch64-linux-ld: DWARF error: could not find abbrev number 60 drivers/net/ethernet/renesas/rswitch.o: in function `rswitch_get_ts_info': rswitch.c:(.text+0x408): undefined reference to `ptp_clock_index' aarch64-linux-ld: DWARF error: could not find abbrev number 1190123 drivers/net/ethernet/renesas/rcar_gen4_ptp.o: in function `rcar_gen4_ptp_register': rcar_gen4_ptp.c:(.text+0x4dc): undefined reference to `ptp_clock_register' aarch64-linux-ld: drivers/net/ethernet/renesas/rcar_gen4_ptp.o: in function `rcar_gen4_ptp_unregister': rcar_gen4_ptp.c:(.text+0x584): undefined reference to `ptp_clock_unregister' To fix the issue, add "depends on PTP_1588_CLOCK_OPTIONAL" into the Kconfig. Reported-by: kernel test robot Suggested-by: Arnd Bergmann Fixes: 6c6fa1a00ad3 ("net: ethernet: renesas: rswitch: Add R-Car Gen4 gPTP support") Signed-off-by: Yoshihiro Shimoda Link: https://lore.kernel.org/r/20221110012720.3552060-1-yoshihiro.shimoda.uh@renesas.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/renesas/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/ethernet/renesas/Kconfig b/drivers/net/ethernet/renesas/Kconfig index 7a5e26b6ea9b..3ceb57408ed0 100644 --- a/drivers/net/ethernet/renesas/Kconfig +++ b/drivers/net/ethernet/renesas/Kconfig @@ -45,6 +45,7 @@ config RAVB config RENESAS_ETHER_SWITCH tristate "Renesas Ethernet Switch support" depends on ARCH_RENESAS || COMPILE_TEST + depends on PTP_1588_CLOCK_OPTIONAL select CRC32 select MII select PHYLINK From 515e5fb6a95ed53edd76730071fc919f0ef78562 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Sun, 13 Nov 2022 21:34:04 +0100 Subject: [PATCH 0909/2002] net: dpaa2: Remove linux/msi.h includes Nothing in these file needs anything from linux/msi.h Signed-off-by: Thomas Gleixner Cc: Ioana Ciornei Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c | 1 - drivers/net/ethernet/freescale/dpaa2/dpaa2-ptp.c | 1 - drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c | 1 - 3 files changed, 3 deletions(-) diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c index 273f1d77c012..97e1856641b4 100644 --- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c +++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c @@ -8,7 +8,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-ptp.c b/drivers/net/ethernet/freescale/dpaa2/dpaa2-ptp.c index c8cb541572ff..90d23ab1ce9d 100644 --- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-ptp.c +++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-ptp.c @@ -8,7 +8,6 @@ #include #include #include -#include #include #include "dpaa2-ptp.h" diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c b/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c index 2b5909fa93cf..14f739e04a3c 100644 --- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c +++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c @@ -10,7 +10,6 @@ #include #include -#include #include #include #include From 5fd66a0b3bb473287099346cd92e54bf0ea19b6d Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Sun, 13 Nov 2022 21:34:05 +0100 Subject: [PATCH 0910/2002] net: nfp: Remove linux/msi.h includes Nothing in these files needs anything from linux/msi.h Signed-off-by: Thomas Gleixner Cc: oss-drivers@corigine.com Acked-by: Simon Horman Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/netronome/nfp/nfp_main.h | 1 - drivers/net/ethernet/netronome/nfp/nfp_net_common.c | 1 - drivers/net/ethernet/netronome/nfp/nfp_net_main.c | 1 - 3 files changed, 3 deletions(-) diff --git a/drivers/net/ethernet/netronome/nfp/nfp_main.h b/drivers/net/ethernet/netronome/nfp/nfp_main.h index afd3edfa2428..5a18af672e6b 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_main.h +++ b/drivers/net/ethernet/netronome/nfp/nfp_main.h @@ -12,7 +12,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c index 184ffae2ac94..5620faa63c7e 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c @@ -27,7 +27,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_main.c b/drivers/net/ethernet/netronome/nfp/nfp_net_main.c index 7abf0c576868..abfe788d558f 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_main.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_main.c @@ -16,7 +16,6 @@ #include #include #include -#include #include #include From d9282e48c6088105a98b98153a707fdbcdbf75b1 Mon Sep 17 00:00:00 2001 From: Jamie Bainbridge Date: Mon, 14 Nov 2022 12:00:08 +1100 Subject: [PATCH 0911/2002] tcp: Add listening address to SYN flood message The SYN flood message prints the listening port number, but with many processes bound to the same port on different IPs, it's impossible to tell which socket is the problem. Add the listen IP address to the SYN flood message. For IPv6 use "[IP]:port" as per RFC-5952 and to provide ease of copy-paste to "ss" filters. For IPv4 use "IP:port" to match. Each protcol's "any" address and a host address now look like: Possible SYN flooding on port 0.0.0.0:9001. Possible SYN flooding on port 127.0.0.1:9001. Possible SYN flooding on port [::]:9001. Possible SYN flooding on port [fc00::1]:9001. Signed-off-by: Jamie Bainbridge Reviewed-by: Eric Dumazet Reviewed-by: Stephen Hemminger Link: https://lore.kernel.org/r/4fedab7ce54a389aeadbdc639f6b4f4988e9d2d7.1668386107.git.jamie.bainbridge@gmail.com Signed-off-by: Jakub Kicinski --- net/ipv4/tcp_input.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index d764b5854dfc..94024fdc2da1 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -6842,9 +6842,17 @@ static bool tcp_syn_flood_action(const struct sock *sk, const char *proto) __NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPREQQFULLDROP); if (!queue->synflood_warned && syncookies != 2 && - xchg(&queue->synflood_warned, 1) == 0) - net_info_ratelimited("%s: Possible SYN flooding on port %d. %s. Check SNMP counters.\n", - proto, sk->sk_num, msg); + xchg(&queue->synflood_warned, 1) == 0) { + if (IS_ENABLED(CONFIG_IPV6) && sk->sk_family == AF_INET6) { + net_info_ratelimited("%s: Possible SYN flooding on port [%pI6c]:%u. %s.\n", + proto, &sk->sk_v6_rcv_saddr, + sk->sk_num, msg); + } else { + net_info_ratelimited("%s: Possible SYN flooding on port %pI4:%u. %s.\n", + proto, &sk->sk_rcv_saddr, + sk->sk_num, msg); + } + } return want_cookie; } From 1f6d52f1a8947436dc5b2575e2fffb831f240141 Mon Sep 17 00:00:00 2001 From: Kumar Kartikeya Dwivedi Date: Tue, 15 Nov 2022 00:45:22 +0530 Subject: [PATCH 0912/2002] bpf: Remove local kptr references in documentation We don't want to commit to a specific name for these. Simply call them allocated objects coming from bpf_obj_new, which is completely clear in itself. Signed-off-by: Kumar Kartikeya Dwivedi Link: https://lore.kernel.org/r/20221114191547.1694267-2-memxor@gmail.com Signed-off-by: Alexei Starovoitov --- Documentation/bpf/bpf_design_QA.rst | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/Documentation/bpf/bpf_design_QA.rst b/Documentation/bpf/bpf_design_QA.rst index 17e774d96c5e..cec2371173d7 100644 --- a/Documentation/bpf/bpf_design_QA.rst +++ b/Documentation/bpf/bpf_design_QA.rst @@ -332,13 +332,14 @@ avoid defining types with 'bpf\_' prefix to not be broken in future releases. In other words, no backwards compatibility is guaranteed if one using a type in BTF with 'bpf\_' prefix. -Q: What is the compatibility story for special BPF types in local kptrs? ------------------------------------------------------------------------- -Q: Same as above, but for local kptrs (i.e. pointers to objects allocated using -bpf_obj_new for user defined structures). Will the kernel preserve backwards +Q: What is the compatibility story for special BPF types in allocated objects? +------------------------------------------------------------------------------ +Q: Same as above, but for allocated objects (i.e. objects allocated using +bpf_obj_new for user defined types). Will the kernel preserve backwards compatibility for these features? A: NO. Unlike map value types, there are no stability guarantees for this case. The -whole local kptr API itself is unstable (since it is exposed through kfuncs). +whole API to work with allocated objects and any support for special fields +inside them is unstable (since it is exposed through kfuncs). From 2d577252579b3efb9e934b68948a2edfa9920110 Mon Sep 17 00:00:00 2001 From: Kumar Kartikeya Dwivedi Date: Tue, 15 Nov 2022 00:45:23 +0530 Subject: [PATCH 0913/2002] bpf: Remove BPF_MAP_OFF_ARR_MAX In f71b2f64177a ("bpf: Refactor map->off_arr handling"), map->off_arr was refactored to be btf_field_offs. The number of field offsets is equal to maximum possible fields limited by BTF_FIELDS_MAX. Hence, reuse BTF_FIELDS_MAX as spin_lock and timer no longer are to be handled specially for offset sorting, fix the comment, and remove incorrect WARN_ON as its rec->cnt can never exceed this value. The reason to keep separate constant was the it was always more 2 more than total kptrs. This is no longer the case. Signed-off-by: Kumar Kartikeya Dwivedi Link: https://lore.kernel.org/r/20221114191547.1694267-3-memxor@gmail.com Signed-off-by: Alexei Starovoitov --- include/linux/bpf.h | 9 ++++----- kernel/bpf/btf.c | 2 +- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/include/linux/bpf.h b/include/linux/bpf.h index 798aec816970..1a66a1df1af1 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -165,9 +165,8 @@ struct bpf_map_ops { }; enum { - /* Support at most 8 pointers in a BTF type */ - BTF_FIELDS_MAX = 10, - BPF_MAP_OFF_ARR_MAX = BTF_FIELDS_MAX, + /* Support at most 10 fields in a BTF type */ + BTF_FIELDS_MAX = 10, }; enum btf_field_type { @@ -203,8 +202,8 @@ struct btf_record { struct btf_field_offs { u32 cnt; - u32 field_off[BPF_MAP_OFF_ARR_MAX]; - u8 field_sz[BPF_MAP_OFF_ARR_MAX]; + u32 field_off[BTF_FIELDS_MAX]; + u8 field_sz[BTF_FIELDS_MAX]; }; struct bpf_map { diff --git a/kernel/bpf/btf.c b/kernel/bpf/btf.c index 5579ff3a5b54..12361d7b2498 100644 --- a/kernel/bpf/btf.c +++ b/kernel/bpf/btf.c @@ -3584,7 +3584,7 @@ struct btf_field_offs *btf_parse_field_offs(struct btf_record *rec) u8 *sz; BUILD_BUG_ON(ARRAY_SIZE(foffs->field_off) != ARRAY_SIZE(foffs->field_sz)); - if (IS_ERR_OR_NULL(rec) || WARN_ON_ONCE(rec->cnt > sizeof(foffs->field_off))) + if (IS_ERR_OR_NULL(rec)) return NULL; foffs = kzalloc(sizeof(*foffs), GFP_KERNEL | __GFP_NOWARN); From e5feed0f64f73e167ef70755d3dc2db959d8fd5c Mon Sep 17 00:00:00 2001 From: Kumar Kartikeya Dwivedi Date: Tue, 15 Nov 2022 00:45:24 +0530 Subject: [PATCH 0914/2002] bpf: Fix copy_map_value, zero_map_value The current offset needs to also skip over the already copied region in addition to the size of the next field. This case manifests where there are gaps between adjacent special fields. It was observed that for a map value with size 48, having fields at: off: 0, 16, 32 size: 4, 16, 16 The current code does: memcpy(dst + 0, src + 0, 0) memcpy(dst + 4, src + 4, 12) memcpy(dst + 20, src + 20, 12) memcpy(dst + 36, src + 36, 12) With the fix, it is done correctly as: memcpy(dst + 0, src + 0, 0) memcpy(dst + 4, src + 4, 12) memcpy(dst + 32, src + 32, 0) memcpy(dst + 48, src + 48, 0) Fixes: 4d7d7f69f4b1 ("bpf: Adapt copy_map_value for multiple offset case") Signed-off-by: Kumar Kartikeya Dwivedi Link: https://lore.kernel.org/r/20221114191547.1694267-4-memxor@gmail.com Signed-off-by: Alexei Starovoitov --- include/linux/bpf.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/linux/bpf.h b/include/linux/bpf.h index 1a66a1df1af1..f08eb2d27de0 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -360,7 +360,7 @@ static inline void bpf_obj_memcpy(struct btf_field_offs *foffs, u32 sz = next_off - curr_off; memcpy(dst + curr_off, src + curr_off, sz); - curr_off += foffs->field_sz[i]; + curr_off += foffs->field_sz[i] + sz; } memcpy(dst + curr_off, src + curr_off, size - curr_off); } @@ -390,7 +390,7 @@ static inline void bpf_obj_memzero(struct btf_field_offs *foffs, void *dst, u32 u32 sz = next_off - curr_off; memset(dst + curr_off, 0, sz); - curr_off += foffs->field_sz[i]; + curr_off += foffs->field_sz[i] + sz; } memset(dst + curr_off, 0, size - curr_off); } From f0c5941ff5b255413d31425bb327c2aec3625673 Mon Sep 17 00:00:00 2001 From: Kumar Kartikeya Dwivedi Date: Tue, 15 Nov 2022 00:45:25 +0530 Subject: [PATCH 0915/2002] bpf: Support bpf_list_head in map values Add the support on the map side to parse, recognize, verify, and build metadata table for a new special field of the type struct bpf_list_head. To parameterize the bpf_list_head for a certain value type and the list_node member it will accept in that value type, we use BTF declaration tags. The definition of bpf_list_head in a map value will be done as follows: struct foo { struct bpf_list_node node; int data; }; struct map_value { struct bpf_list_head head __contains(foo, node); }; Then, the bpf_list_head only allows adding to the list 'head' using the bpf_list_node 'node' for the type struct foo. The 'contains' annotation is a BTF declaration tag composed of four parts, "contains:name:node" where the name is then used to look up the type in the map BTF, with its kind hardcoded to BTF_KIND_STRUCT during the lookup. The node defines name of the member in this type that has the type struct bpf_list_node, which is actually used for linking into the linked list. For now, 'kind' part is hardcoded as struct. This allows building intrusive linked lists in BPF, using container_of to obtain pointer to entry, while being completely type safe from the perspective of the verifier. The verifier knows exactly the type of the nodes, and knows that list helpers return that type at some fixed offset where the bpf_list_node member used for this list exists. The verifier also uses this information to disallow adding types that are not accepted by a certain list. For now, no elements can be added to such lists. Support for that is coming in future patches, hence draining and freeing items is done with a TODO that will be resolved in a future patch. Note that the bpf_list_head_free function moves the list out to a local variable under the lock and releases it, doing the actual draining of the list items outside the lock. While this helps with not holding the lock for too long pessimizing other concurrent list operations, it is also necessary for deadlock prevention: unless every function called in the critical section would be notrace, a fentry/fexit program could attach and call bpf_map_update_elem again on the map, leading to the same lock being acquired if the key matches and lead to a deadlock. While this requires some special effort on part of the BPF programmer to trigger and is highly unlikely to occur in practice, it is always better if we can avoid such a condition. While notrace would prevent this, doing the draining outside the lock has advantages of its own, hence it is used to also fix the deadlock related problem. Signed-off-by: Kumar Kartikeya Dwivedi Link: https://lore.kernel.org/r/20221114191547.1694267-5-memxor@gmail.com Signed-off-by: Alexei Starovoitov --- include/linux/bpf.h | 17 ++++ include/uapi/linux/bpf.h | 10 +++ kernel/bpf/btf.c | 145 ++++++++++++++++++++++++++++++++- kernel/bpf/helpers.c | 32 ++++++++ kernel/bpf/syscall.c | 22 ++++- kernel/bpf/verifier.c | 7 ++ tools/include/uapi/linux/bpf.h | 10 +++ 7 files changed, 239 insertions(+), 4 deletions(-) diff --git a/include/linux/bpf.h b/include/linux/bpf.h index f08eb2d27de0..05f98e9e5c48 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -175,6 +175,7 @@ enum btf_field_type { BPF_KPTR_UNREF = (1 << 2), BPF_KPTR_REF = (1 << 3), BPF_KPTR = BPF_KPTR_UNREF | BPF_KPTR_REF, + BPF_LIST_HEAD = (1 << 4), }; struct btf_field_kptr { @@ -184,11 +185,18 @@ struct btf_field_kptr { u32 btf_id; }; +struct btf_field_list_head { + struct btf *btf; + u32 value_btf_id; + u32 node_offset; +}; + struct btf_field { u32 offset; enum btf_field_type type; union { struct btf_field_kptr kptr; + struct btf_field_list_head list_head; }; }; @@ -266,6 +274,8 @@ static inline const char *btf_field_type_name(enum btf_field_type type) case BPF_KPTR_UNREF: case BPF_KPTR_REF: return "kptr"; + case BPF_LIST_HEAD: + return "bpf_list_head"; default: WARN_ON_ONCE(1); return "unknown"; @@ -282,6 +292,8 @@ static inline u32 btf_field_type_size(enum btf_field_type type) case BPF_KPTR_UNREF: case BPF_KPTR_REF: return sizeof(u64); + case BPF_LIST_HEAD: + return sizeof(struct bpf_list_head); default: WARN_ON_ONCE(1); return 0; @@ -298,6 +310,8 @@ static inline u32 btf_field_type_align(enum btf_field_type type) case BPF_KPTR_UNREF: case BPF_KPTR_REF: return __alignof__(u64); + case BPF_LIST_HEAD: + return __alignof__(struct bpf_list_head); default: WARN_ON_ONCE(1); return 0; @@ -403,6 +417,9 @@ static inline void zero_map_value(struct bpf_map *map, void *dst) void copy_map_value_locked(struct bpf_map *map, void *dst, void *src, bool lock_src); void bpf_timer_cancel_and_free(void *timer); +void bpf_list_head_free(const struct btf_field *field, void *list_head, + struct bpf_spin_lock *spin_lock); + int bpf_obj_name_cpy(char *dst, const char *src, unsigned int size); struct bpf_offload_dev; diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h index fb4c911d2a03..6580448e9f77 100644 --- a/include/uapi/linux/bpf.h +++ b/include/uapi/linux/bpf.h @@ -6888,6 +6888,16 @@ struct bpf_dynptr { __u64 :64; } __attribute__((aligned(8))); +struct bpf_list_head { + __u64 :64; + __u64 :64; +} __attribute__((aligned(8))); + +struct bpf_list_node { + __u64 :64; + __u64 :64; +} __attribute__((aligned(8))); + struct bpf_sysctl { __u32 write; /* Sysctl is being read (= 0) or written (= 1). * Allows 1,2,4-byte read, but no write. diff --git a/kernel/bpf/btf.c b/kernel/bpf/btf.c index 12361d7b2498..c0d73d71c539 100644 --- a/kernel/bpf/btf.c +++ b/kernel/bpf/btf.c @@ -3205,9 +3205,15 @@ enum { struct btf_field_info { enum btf_field_type type; u32 off; - struct { - u32 type_id; - } kptr; + union { + struct { + u32 type_id; + } kptr; + struct { + const char *node_name; + u32 value_btf_id; + } list_head; + }; }; static int btf_find_struct(const struct btf *btf, const struct btf_type *t, @@ -3261,6 +3267,63 @@ static int btf_find_kptr(const struct btf *btf, const struct btf_type *t, return BTF_FIELD_FOUND; } +static const char *btf_find_decl_tag_value(const struct btf *btf, + const struct btf_type *pt, + int comp_idx, const char *tag_key) +{ + int i; + + for (i = 1; i < btf_nr_types(btf); i++) { + const struct btf_type *t = btf_type_by_id(btf, i); + int len = strlen(tag_key); + + if (!btf_type_is_decl_tag(t)) + continue; + if (pt != btf_type_by_id(btf, t->type) || + btf_type_decl_tag(t)->component_idx != comp_idx) + continue; + if (strncmp(__btf_name_by_offset(btf, t->name_off), tag_key, len)) + continue; + return __btf_name_by_offset(btf, t->name_off) + len; + } + return NULL; +} + +static int btf_find_list_head(const struct btf *btf, const struct btf_type *pt, + const struct btf_type *t, int comp_idx, + u32 off, int sz, struct btf_field_info *info) +{ + const char *value_type; + const char *list_node; + s32 id; + + if (!__btf_type_is_struct(t)) + return BTF_FIELD_IGNORE; + if (t->size != sz) + return BTF_FIELD_IGNORE; + value_type = btf_find_decl_tag_value(btf, pt, comp_idx, "contains:"); + if (!value_type) + return -EINVAL; + list_node = strstr(value_type, ":"); + if (!list_node) + return -EINVAL; + value_type = kstrndup(value_type, list_node - value_type, GFP_KERNEL | __GFP_NOWARN); + if (!value_type) + return -ENOMEM; + id = btf_find_by_name_kind(btf, value_type, BTF_KIND_STRUCT); + kfree(value_type); + if (id < 0) + return id; + list_node++; + if (str_is_empty(list_node)) + return -EINVAL; + info->type = BPF_LIST_HEAD; + info->off = off; + info->list_head.value_btf_id = id; + info->list_head.node_name = list_node; + return BTF_FIELD_FOUND; +} + static int btf_get_field_type(const char *name, u32 field_mask, u32 *seen_mask, int *align, int *sz) { @@ -3284,6 +3347,12 @@ static int btf_get_field_type(const char *name, u32 field_mask, u32 *seen_mask, goto end; } } + if (field_mask & BPF_LIST_HEAD) { + if (!strcmp(name, "bpf_list_head")) { + type = BPF_LIST_HEAD; + goto end; + } + } /* Only return BPF_KPTR when all other types with matchable names fail */ if (field_mask & BPF_KPTR) { type = BPF_KPTR_REF; @@ -3339,6 +3408,12 @@ static int btf_find_struct_field(const struct btf *btf, if (ret < 0) return ret; break; + case BPF_LIST_HEAD: + ret = btf_find_list_head(btf, t, member_type, i, off, sz, + idx < info_cnt ? &info[idx] : &tmp); + if (ret < 0) + return ret; + break; default: return -EFAULT; } @@ -3393,6 +3468,12 @@ static int btf_find_datasec_var(const struct btf *btf, const struct btf_type *t, if (ret < 0) return ret; break; + case BPF_LIST_HEAD: + ret = btf_find_list_head(btf, var, var_type, -1, off, sz, + idx < info_cnt ? &info[idx] : &tmp); + if (ret < 0) + return ret; + break; default: return -EFAULT; } @@ -3491,11 +3572,52 @@ end_btf: return ret; } +static int btf_parse_list_head(const struct btf *btf, struct btf_field *field, + struct btf_field_info *info) +{ + const struct btf_type *t, *n = NULL; + const struct btf_member *member; + u32 offset; + int i; + + t = btf_type_by_id(btf, info->list_head.value_btf_id); + /* We've already checked that value_btf_id is a struct type. We + * just need to figure out the offset of the list_node, and + * verify its type. + */ + for_each_member(i, t, member) { + if (strcmp(info->list_head.node_name, __btf_name_by_offset(btf, member->name_off))) + continue; + /* Invalid BTF, two members with same name */ + if (n) + return -EINVAL; + n = btf_type_by_id(btf, member->type); + if (!__btf_type_is_struct(n)) + return -EINVAL; + if (strcmp("bpf_list_node", __btf_name_by_offset(btf, n->name_off))) + return -EINVAL; + offset = __btf_member_bit_offset(n, member); + if (offset % 8) + return -EINVAL; + offset /= 8; + if (offset % __alignof__(struct bpf_list_node)) + return -EINVAL; + + field->list_head.btf = (struct btf *)btf; + field->list_head.value_btf_id = info->list_head.value_btf_id; + field->list_head.node_offset = offset; + } + if (!n) + return -ENOENT; + return 0; +} + struct btf_record *btf_parse_fields(const struct btf *btf, const struct btf_type *t, u32 field_mask, u32 value_size) { struct btf_field_info info_arr[BTF_FIELDS_MAX]; struct btf_record *rec; + u32 next_off = 0; int ret, i, cnt; ret = btf_find_field(btf, t, field_mask, info_arr, ARRAY_SIZE(info_arr)); @@ -3517,6 +3639,11 @@ struct btf_record *btf_parse_fields(const struct btf *btf, const struct btf_type ret = -EFAULT; goto end; } + if (info_arr[i].off < next_off) { + ret = -EEXIST; + goto end; + } + next_off = info_arr[i].off + btf_field_type_size(info_arr[i].type); rec->field_mask |= info_arr[i].type; rec->fields[i].offset = info_arr[i].off; @@ -3539,12 +3666,24 @@ struct btf_record *btf_parse_fields(const struct btf *btf, const struct btf_type if (ret < 0) goto end; break; + case BPF_LIST_HEAD: + ret = btf_parse_list_head(btf, &rec->fields[i], &info_arr[i]); + if (ret < 0) + goto end; + break; default: ret = -EFAULT; goto end; } rec->cnt++; } + + /* bpf_list_head requires bpf_spin_lock */ + if (btf_record_has_field(rec, BPF_LIST_HEAD) && rec->spin_lock_off < 0) { + ret = -EINVAL; + goto end; + } + return rec; end: btf_record_free(rec); diff --git a/kernel/bpf/helpers.c b/kernel/bpf/helpers.c index 283f55bbeb70..7bc71995f17c 100644 --- a/kernel/bpf/helpers.c +++ b/kernel/bpf/helpers.c @@ -1706,6 +1706,38 @@ bpf_base_func_proto(enum bpf_func_id func_id) } } +void bpf_list_head_free(const struct btf_field *field, void *list_head, + struct bpf_spin_lock *spin_lock) +{ + struct list_head *head = list_head, *orig_head = list_head; + + BUILD_BUG_ON(sizeof(struct list_head) > sizeof(struct bpf_list_head)); + BUILD_BUG_ON(__alignof__(struct list_head) > __alignof__(struct bpf_list_head)); + + /* Do the actual list draining outside the lock to not hold the lock for + * too long, and also prevent deadlocks if tracing programs end up + * executing on entry/exit of functions called inside the critical + * section, and end up doing map ops that call bpf_list_head_free for + * the same map value again. + */ + __bpf_spin_lock_irqsave(spin_lock); + if (!head->next || list_empty(head)) + goto unlock; + head = head->next; +unlock: + INIT_LIST_HEAD(orig_head); + __bpf_spin_unlock_irqrestore(spin_lock); + + while (head != orig_head) { + void *obj = head; + + obj -= field->list_head.node_offset; + head = head->next; + /* TODO: Rework later */ + kfree(obj); + } +} + BTF_SET8_START(tracing_btf_ids) #ifdef CONFIG_KEXEC_CORE BTF_ID_FLAGS(func, crash_kexec, KF_DESTRUCTIVE) diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c index 85532d301124..fdbae52f463f 100644 --- a/kernel/bpf/syscall.c +++ b/kernel/bpf/syscall.c @@ -536,6 +536,9 @@ void btf_record_free(struct btf_record *rec) module_put(rec->fields[i].kptr.module); btf_put(rec->fields[i].kptr.btf); break; + case BPF_LIST_HEAD: + /* Nothing to release for bpf_list_head */ + break; default: WARN_ON_ONCE(1); continue; @@ -578,6 +581,9 @@ struct btf_record *btf_record_dup(const struct btf_record *rec) goto free; } break; + case BPF_LIST_HEAD: + /* Nothing to acquire for bpf_list_head */ + break; default: ret = -EFAULT; WARN_ON_ONCE(1); @@ -637,6 +643,11 @@ void bpf_obj_free_fields(const struct btf_record *rec, void *obj) case BPF_KPTR_REF: field->kptr.dtor((void *)xchg((unsigned long *)field_ptr, 0)); break; + case BPF_LIST_HEAD: + if (WARN_ON_ONCE(rec->spin_lock_off < 0)) + continue; + bpf_list_head_free(field, field_ptr, obj + rec->spin_lock_off); + break; default: WARN_ON_ONCE(1); continue; @@ -965,7 +976,8 @@ static int map_check_btf(struct bpf_map *map, const struct btf *btf, if (!value_type || value_size != map->value_size) return -EINVAL; - map->record = btf_parse_fields(btf, value_type, BPF_SPIN_LOCK | BPF_TIMER | BPF_KPTR, + map->record = btf_parse_fields(btf, value_type, + BPF_SPIN_LOCK | BPF_TIMER | BPF_KPTR | BPF_LIST_HEAD, map->value_size); if (!IS_ERR_OR_NULL(map->record)) { int i; @@ -1012,6 +1024,14 @@ static int map_check_btf(struct bpf_map *map, const struct btf *btf, goto free_map_tab; } break; + case BPF_LIST_HEAD: + if (map->map_type != BPF_MAP_TYPE_HASH && + map->map_type != BPF_MAP_TYPE_LRU_HASH && + map->map_type != BPF_MAP_TYPE_ARRAY) { + ret = -EOPNOTSUPP; + goto free_map_tab; + } + break; default: /* Fail if map_type checks are missing for a field type */ ret = -EOPNOTSUPP; diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 07c0259dfc1a..a50018e2d4a0 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -12814,6 +12814,13 @@ static int check_map_prog_compatibility(struct bpf_verifier_env *env, { enum bpf_prog_type prog_type = resolve_prog_type(prog); + if (btf_record_has_field(map->record, BPF_LIST_HEAD)) { + if (is_tracing_prog_type(prog_type)) { + verbose(env, "tracing progs cannot use bpf_list_head yet\n"); + return -EINVAL; + } + } + if (btf_record_has_field(map->record, BPF_SPIN_LOCK)) { if (prog_type == BPF_PROG_TYPE_SOCKET_FILTER) { verbose(env, "socket filter progs cannot use bpf_spin_lock yet\n"); diff --git a/tools/include/uapi/linux/bpf.h b/tools/include/uapi/linux/bpf.h index fb4c911d2a03..6580448e9f77 100644 --- a/tools/include/uapi/linux/bpf.h +++ b/tools/include/uapi/linux/bpf.h @@ -6888,6 +6888,16 @@ struct bpf_dynptr { __u64 :64; } __attribute__((aligned(8))); +struct bpf_list_head { + __u64 :64; + __u64 :64; +} __attribute__((aligned(8))); + +struct bpf_list_node { + __u64 :64; + __u64 :64; +} __attribute__((aligned(8))); + struct bpf_sysctl { __u32 write; /* Sysctl is being read (= 0) or written (= 1). * Allows 1,2,4-byte read, but no write. From 2de2669b4e52b2ae2f118bfc310004f50b47f0f5 Mon Sep 17 00:00:00 2001 From: Kumar Kartikeya Dwivedi Date: Tue, 15 Nov 2022 00:45:26 +0530 Subject: [PATCH 0916/2002] bpf: Rename RET_PTR_TO_ALLOC_MEM Currently, the verifier has two return types, RET_PTR_TO_ALLOC_MEM, and RET_PTR_TO_ALLOC_MEM_OR_NULL, however the former is confusingly named to imply that it carries MEM_ALLOC, while only the latter does. This causes confusion during code review leading to conclusions like that the return value of RET_PTR_TO_DYNPTR_MEM_OR_NULL (which is RET_PTR_TO_ALLOC_MEM | PTR_MAYBE_NULL) may be consumable by bpf_ringbuf_{submit,commit}. Rename it to make it clear MEM_ALLOC needs to be tacked on top of RET_PTR_TO_MEM. Signed-off-by: Kumar Kartikeya Dwivedi Link: https://lore.kernel.org/r/20221114191547.1694267-6-memxor@gmail.com Signed-off-by: Alexei Starovoitov --- include/linux/bpf.h | 6 +++--- kernel/bpf/verifier.c | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/include/linux/bpf.h b/include/linux/bpf.h index 05f98e9e5c48..2fe3ec620d54 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -607,7 +607,7 @@ enum bpf_return_type { RET_PTR_TO_SOCKET, /* returns a pointer to a socket */ RET_PTR_TO_TCP_SOCK, /* returns a pointer to a tcp_sock */ RET_PTR_TO_SOCK_COMMON, /* returns a pointer to a sock_common */ - RET_PTR_TO_ALLOC_MEM, /* returns a pointer to dynamically allocated memory */ + RET_PTR_TO_MEM, /* returns a pointer to memory */ RET_PTR_TO_MEM_OR_BTF_ID, /* returns a pointer to a valid memory or a btf_id */ RET_PTR_TO_BTF_ID, /* returns a pointer to a btf_id */ __BPF_RET_TYPE_MAX, @@ -617,8 +617,8 @@ enum bpf_return_type { RET_PTR_TO_SOCKET_OR_NULL = PTR_MAYBE_NULL | RET_PTR_TO_SOCKET, RET_PTR_TO_TCP_SOCK_OR_NULL = PTR_MAYBE_NULL | RET_PTR_TO_TCP_SOCK, RET_PTR_TO_SOCK_COMMON_OR_NULL = PTR_MAYBE_NULL | RET_PTR_TO_SOCK_COMMON, - RET_PTR_TO_ALLOC_MEM_OR_NULL = PTR_MAYBE_NULL | MEM_ALLOC | RET_PTR_TO_ALLOC_MEM, - RET_PTR_TO_DYNPTR_MEM_OR_NULL = PTR_MAYBE_NULL | RET_PTR_TO_ALLOC_MEM, + RET_PTR_TO_ALLOC_MEM_OR_NULL = PTR_MAYBE_NULL | MEM_ALLOC | RET_PTR_TO_MEM, + RET_PTR_TO_DYNPTR_MEM_OR_NULL = PTR_MAYBE_NULL | RET_PTR_TO_MEM, RET_PTR_TO_BTF_ID_OR_NULL = PTR_MAYBE_NULL | RET_PTR_TO_BTF_ID, /* This must be the last entry. Its purpose is to ensure the enum is diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index a50018e2d4a0..c88da7e3ca74 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -7630,7 +7630,7 @@ static int check_helper_call(struct bpf_verifier_env *env, struct bpf_insn *insn mark_reg_known_zero(env, regs, BPF_REG_0); regs[BPF_REG_0].type = PTR_TO_TCP_SOCK | ret_flag; break; - case RET_PTR_TO_ALLOC_MEM: + case RET_PTR_TO_MEM: mark_reg_known_zero(env, regs, BPF_REG_0); regs[BPF_REG_0].type = PTR_TO_MEM | ret_flag; regs[BPF_REG_0].mem_size = meta.mem_size; From 894f2a8b1673a355a1a7507a4dfa6a3c836d07c1 Mon Sep 17 00:00:00 2001 From: Kumar Kartikeya Dwivedi Date: Tue, 15 Nov 2022 00:45:27 +0530 Subject: [PATCH 0917/2002] bpf: Rename MEM_ALLOC to MEM_RINGBUF Currently, verifier uses MEM_ALLOC type tag to specially tag memory returned from bpf_ringbuf_reserve helper. However, this is currently only used for this purpose and there is an implicit assumption that it only refers to ringbuf memory (e.g. the check for ARG_PTR_TO_ALLOC_MEM in check_func_arg_reg_off). Hence, rename MEM_ALLOC to MEM_RINGBUF to indicate this special relationship and instead open the use of MEM_ALLOC for more generic allocations made for user types. Also, since ARG_PTR_TO_ALLOC_MEM_OR_NULL is unused, simply drop it. Finally, update selftests using 'alloc_' verifier string to 'ringbuf_'. Signed-off-by: Kumar Kartikeya Dwivedi Link: https://lore.kernel.org/r/20221114191547.1694267-7-memxor@gmail.com Signed-off-by: Alexei Starovoitov --- include/linux/bpf.h | 11 ++++------- kernel/bpf/ringbuf.c | 6 +++--- kernel/bpf/verifier.c | 14 +++++++------- tools/testing/selftests/bpf/prog_tests/dynptr.c | 2 +- tools/testing/selftests/bpf/verifier/ringbuf.c | 2 +- tools/testing/selftests/bpf/verifier/spill_fill.c | 2 +- 6 files changed, 17 insertions(+), 20 deletions(-) diff --git a/include/linux/bpf.h b/include/linux/bpf.h index 2fe3ec620d54..afc1c51b59ff 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -488,10 +488,8 @@ enum bpf_type_flag { */ MEM_RDONLY = BIT(1 + BPF_BASE_TYPE_BITS), - /* MEM was "allocated" from a different helper, and cannot be mixed - * with regular non-MEM_ALLOC'ed MEM types. - */ - MEM_ALLOC = BIT(2 + BPF_BASE_TYPE_BITS), + /* MEM points to BPF ring buffer reservation. */ + MEM_RINGBUF = BIT(2 + BPF_BASE_TYPE_BITS), /* MEM is in user address space. */ MEM_USER = BIT(3 + BPF_BASE_TYPE_BITS), @@ -565,7 +563,7 @@ enum bpf_arg_type { ARG_PTR_TO_LONG, /* pointer to long */ ARG_PTR_TO_SOCKET, /* pointer to bpf_sock (fullsock) */ ARG_PTR_TO_BTF_ID, /* pointer to in-kernel struct */ - ARG_PTR_TO_ALLOC_MEM, /* pointer to dynamically allocated memory */ + ARG_PTR_TO_RINGBUF_MEM, /* pointer to dynamically reserved ringbuf memory */ ARG_CONST_ALLOC_SIZE_OR_ZERO, /* number of allocated bytes requested */ ARG_PTR_TO_BTF_ID_SOCK_COMMON, /* pointer to in-kernel sock_common or bpf-mirrored bpf_sock */ ARG_PTR_TO_PERCPU_BTF_ID, /* pointer to in-kernel percpu type */ @@ -582,7 +580,6 @@ enum bpf_arg_type { ARG_PTR_TO_MEM_OR_NULL = PTR_MAYBE_NULL | ARG_PTR_TO_MEM, ARG_PTR_TO_CTX_OR_NULL = PTR_MAYBE_NULL | ARG_PTR_TO_CTX, ARG_PTR_TO_SOCKET_OR_NULL = PTR_MAYBE_NULL | ARG_PTR_TO_SOCKET, - ARG_PTR_TO_ALLOC_MEM_OR_NULL = PTR_MAYBE_NULL | ARG_PTR_TO_ALLOC_MEM, ARG_PTR_TO_STACK_OR_NULL = PTR_MAYBE_NULL | ARG_PTR_TO_STACK, ARG_PTR_TO_BTF_ID_OR_NULL = PTR_MAYBE_NULL | ARG_PTR_TO_BTF_ID, /* pointer to memory does not need to be initialized, helper function must fill @@ -617,7 +614,7 @@ enum bpf_return_type { RET_PTR_TO_SOCKET_OR_NULL = PTR_MAYBE_NULL | RET_PTR_TO_SOCKET, RET_PTR_TO_TCP_SOCK_OR_NULL = PTR_MAYBE_NULL | RET_PTR_TO_TCP_SOCK, RET_PTR_TO_SOCK_COMMON_OR_NULL = PTR_MAYBE_NULL | RET_PTR_TO_SOCK_COMMON, - RET_PTR_TO_ALLOC_MEM_OR_NULL = PTR_MAYBE_NULL | MEM_ALLOC | RET_PTR_TO_MEM, + RET_PTR_TO_RINGBUF_MEM_OR_NULL = PTR_MAYBE_NULL | MEM_RINGBUF | RET_PTR_TO_MEM, RET_PTR_TO_DYNPTR_MEM_OR_NULL = PTR_MAYBE_NULL | RET_PTR_TO_MEM, RET_PTR_TO_BTF_ID_OR_NULL = PTR_MAYBE_NULL | RET_PTR_TO_BTF_ID, diff --git a/kernel/bpf/ringbuf.c b/kernel/bpf/ringbuf.c index 9e832acf4692..80f4b4d88aaf 100644 --- a/kernel/bpf/ringbuf.c +++ b/kernel/bpf/ringbuf.c @@ -447,7 +447,7 @@ BPF_CALL_3(bpf_ringbuf_reserve, struct bpf_map *, map, u64, size, u64, flags) const struct bpf_func_proto bpf_ringbuf_reserve_proto = { .func = bpf_ringbuf_reserve, - .ret_type = RET_PTR_TO_ALLOC_MEM_OR_NULL, + .ret_type = RET_PTR_TO_RINGBUF_MEM_OR_NULL, .arg1_type = ARG_CONST_MAP_PTR, .arg2_type = ARG_CONST_ALLOC_SIZE_OR_ZERO, .arg3_type = ARG_ANYTHING, @@ -490,7 +490,7 @@ BPF_CALL_2(bpf_ringbuf_submit, void *, sample, u64, flags) const struct bpf_func_proto bpf_ringbuf_submit_proto = { .func = bpf_ringbuf_submit, .ret_type = RET_VOID, - .arg1_type = ARG_PTR_TO_ALLOC_MEM | OBJ_RELEASE, + .arg1_type = ARG_PTR_TO_RINGBUF_MEM | OBJ_RELEASE, .arg2_type = ARG_ANYTHING, }; @@ -503,7 +503,7 @@ BPF_CALL_2(bpf_ringbuf_discard, void *, sample, u64, flags) const struct bpf_func_proto bpf_ringbuf_discard_proto = { .func = bpf_ringbuf_discard, .ret_type = RET_VOID, - .arg1_type = ARG_PTR_TO_ALLOC_MEM | OBJ_RELEASE, + .arg1_type = ARG_PTR_TO_RINGBUF_MEM | OBJ_RELEASE, .arg2_type = ARG_ANYTHING, }; diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index c88da7e3ca74..c588e5483540 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -577,8 +577,8 @@ static const char *reg_type_str(struct bpf_verifier_env *env, if (type & MEM_RDONLY) strncpy(prefix, "rdonly_", 32); - if (type & MEM_ALLOC) - strncpy(prefix, "alloc_", 32); + if (type & MEM_RINGBUF) + strncpy(prefix, "ringbuf_", 32); if (type & MEM_USER) strncpy(prefix, "user_", 32); if (type & MEM_PERCPU) @@ -5785,7 +5785,7 @@ static const struct bpf_reg_types mem_types = { PTR_TO_MAP_KEY, PTR_TO_MAP_VALUE, PTR_TO_MEM, - PTR_TO_MEM | MEM_ALLOC, + PTR_TO_MEM | MEM_RINGBUF, PTR_TO_BUF, }, }; @@ -5803,7 +5803,7 @@ static const struct bpf_reg_types int_ptr_types = { static const struct bpf_reg_types fullsock_types = { .types = { PTR_TO_SOCKET } }; static const struct bpf_reg_types scalar_types = { .types = { SCALAR_VALUE } }; static const struct bpf_reg_types context_types = { .types = { PTR_TO_CTX } }; -static const struct bpf_reg_types alloc_mem_types = { .types = { PTR_TO_MEM | MEM_ALLOC } }; +static const struct bpf_reg_types ringbuf_mem_types = { .types = { PTR_TO_MEM | MEM_RINGBUF } }; static const struct bpf_reg_types const_map_ptr_types = { .types = { CONST_PTR_TO_MAP } }; static const struct bpf_reg_types btf_ptr_types = { .types = { PTR_TO_BTF_ID } }; static const struct bpf_reg_types spin_lock_types = { .types = { PTR_TO_MAP_VALUE } }; @@ -5836,7 +5836,7 @@ static const struct bpf_reg_types *compatible_reg_types[__BPF_ARG_TYPE_MAX] = { [ARG_PTR_TO_BTF_ID] = &btf_ptr_types, [ARG_PTR_TO_SPIN_LOCK] = &spin_lock_types, [ARG_PTR_TO_MEM] = &mem_types, - [ARG_PTR_TO_ALLOC_MEM] = &alloc_mem_types, + [ARG_PTR_TO_RINGBUF_MEM] = &ringbuf_mem_types, [ARG_PTR_TO_INT] = &int_ptr_types, [ARG_PTR_TO_LONG] = &int_ptr_types, [ARG_PTR_TO_PERCPU_BTF_ID] = &percpu_btf_ptr_types, @@ -5957,14 +5957,14 @@ int check_func_arg_reg_off(struct bpf_verifier_env *env, case PTR_TO_MAP_VALUE: case PTR_TO_MEM: case PTR_TO_MEM | MEM_RDONLY: - case PTR_TO_MEM | MEM_ALLOC: + case PTR_TO_MEM | MEM_RINGBUF: case PTR_TO_BUF: case PTR_TO_BUF | MEM_RDONLY: case SCALAR_VALUE: /* Some of the argument types nevertheless require a * zero register offset. */ - if (base_type(arg_type) != ARG_PTR_TO_ALLOC_MEM) + if (base_type(arg_type) != ARG_PTR_TO_RINGBUF_MEM) return 0; break; /* All the rest must be rejected, except PTR_TO_BTF_ID which allows diff --git a/tools/testing/selftests/bpf/prog_tests/dynptr.c b/tools/testing/selftests/bpf/prog_tests/dynptr.c index 8fc4e6c02bfd..b0c06f821cb8 100644 --- a/tools/testing/selftests/bpf/prog_tests/dynptr.c +++ b/tools/testing/selftests/bpf/prog_tests/dynptr.c @@ -17,7 +17,7 @@ static struct { {"ringbuf_missing_release2", "Unreleased reference id=2"}, {"ringbuf_missing_release_callback", "Unreleased reference id"}, {"use_after_invalid", "Expected an initialized dynptr as arg #3"}, - {"ringbuf_invalid_api", "type=mem expected=alloc_mem"}, + {"ringbuf_invalid_api", "type=mem expected=ringbuf_mem"}, {"add_dynptr_to_map1", "invalid indirect read from stack"}, {"add_dynptr_to_map2", "invalid indirect read from stack"}, {"data_slice_out_of_bounds_ringbuf", "value is outside of the allowed memory range"}, diff --git a/tools/testing/selftests/bpf/verifier/ringbuf.c b/tools/testing/selftests/bpf/verifier/ringbuf.c index b64d33e4833c..84838feba47f 100644 --- a/tools/testing/selftests/bpf/verifier/ringbuf.c +++ b/tools/testing/selftests/bpf/verifier/ringbuf.c @@ -28,7 +28,7 @@ }, .fixup_map_ringbuf = { 1 }, .result = REJECT, - .errstr = "dereference of modified alloc_mem ptr R1", + .errstr = "dereference of modified ringbuf_mem ptr R1", }, { "ringbuf: invalid reservation offset 2", diff --git a/tools/testing/selftests/bpf/verifier/spill_fill.c b/tools/testing/selftests/bpf/verifier/spill_fill.c index e23f07175e1b..9bb302dade23 100644 --- a/tools/testing/selftests/bpf/verifier/spill_fill.c +++ b/tools/testing/selftests/bpf/verifier/spill_fill.c @@ -84,7 +84,7 @@ }, .fixup_map_ringbuf = { 1 }, .result = REJECT, - .errstr = "R0 pointer arithmetic on alloc_mem_or_null prohibited", + .errstr = "R0 pointer arithmetic on ringbuf_mem_or_null prohibited", }, { "check corrupted spill/fill", From 6728aea7216c0c06c98e2e58d753a5e8b2ae1c6f Mon Sep 17 00:00:00 2001 From: Kumar Kartikeya Dwivedi Date: Tue, 15 Nov 2022 00:45:28 +0530 Subject: [PATCH 0918/2002] bpf: Refactor btf_struct_access Instead of having to pass multiple arguments that describe the register, pass the bpf_reg_state into the btf_struct_access callback. Currently, all call sites simply reuse the btf and btf_id of the reg they want to check the access of. The only exception to this pattern is the callsite in check_ptr_to_map_access, hence for that case create a dummy reg to simulate PTR_TO_BTF_ID access. Signed-off-by: Kumar Kartikeya Dwivedi Link: https://lore.kernel.org/r/20221114191547.1694267-8-memxor@gmail.com Signed-off-by: Alexei Starovoitov --- include/linux/bpf.h | 17 ++++++++-------- include/linux/filter.h | 8 ++++---- kernel/bpf/btf.c | 11 +++++++---- kernel/bpf/verifier.c | 12 ++++++----- net/bpf/bpf_dummy_struct_ops.c | 14 ++++++------- net/core/filter.c | 34 +++++++++++++------------------- net/ipv4/bpf_tcp_ca.c | 13 ++++++------ net/netfilter/nf_conntrack_bpf.c | 17 +++++++--------- 8 files changed, 60 insertions(+), 66 deletions(-) diff --git a/include/linux/bpf.h b/include/linux/bpf.h index afc1c51b59ff..49f9d2bec401 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -771,6 +771,7 @@ struct bpf_prog_ops { union bpf_attr __user *uattr); }; +struct bpf_reg_state; struct bpf_verifier_ops { /* return eBPF function prototype for verification */ const struct bpf_func_proto * @@ -792,9 +793,8 @@ struct bpf_verifier_ops { struct bpf_insn *dst, struct bpf_prog *prog, u32 *target_size); int (*btf_struct_access)(struct bpf_verifier_log *log, - const struct btf *btf, - const struct btf_type *t, int off, int size, - enum bpf_access_type atype, + const struct bpf_reg_state *reg, + int off, int size, enum bpf_access_type atype, u32 *next_btf_id, enum bpf_type_flag *flag); }; @@ -2080,9 +2080,9 @@ static inline bool bpf_tracing_btf_ctx_access(int off, int size, return btf_ctx_access(off, size, type, prog, info); } -int btf_struct_access(struct bpf_verifier_log *log, const struct btf *btf, - const struct btf_type *t, int off, int size, - enum bpf_access_type atype, +int btf_struct_access(struct bpf_verifier_log *log, + const struct bpf_reg_state *reg, + int off, int size, enum bpf_access_type atype, u32 *next_btf_id, enum bpf_type_flag *flag); bool btf_struct_ids_match(struct bpf_verifier_log *log, const struct btf *btf, u32 id, int off, @@ -2333,9 +2333,8 @@ static inline struct bpf_prog *bpf_prog_by_id(u32 id) } static inline int btf_struct_access(struct bpf_verifier_log *log, - const struct btf *btf, - const struct btf_type *t, int off, int size, - enum bpf_access_type atype, + const struct bpf_reg_state *reg, + int off, int size, enum bpf_access_type atype, u32 *next_btf_id, enum bpf_type_flag *flag) { return -EACCES; diff --git a/include/linux/filter.h b/include/linux/filter.h index efc42a6e3aed..787d35dbf5b0 100644 --- a/include/linux/filter.h +++ b/include/linux/filter.h @@ -568,10 +568,10 @@ struct sk_filter { DECLARE_STATIC_KEY_FALSE(bpf_stats_enabled_key); extern struct mutex nf_conn_btf_access_lock; -extern int (*nfct_btf_struct_access)(struct bpf_verifier_log *log, const struct btf *btf, - const struct btf_type *t, int off, int size, - enum bpf_access_type atype, u32 *next_btf_id, - enum bpf_type_flag *flag); +extern int (*nfct_btf_struct_access)(struct bpf_verifier_log *log, + const struct bpf_reg_state *reg, + int off, int size, enum bpf_access_type atype, + u32 *next_btf_id, enum bpf_type_flag *flag); typedef unsigned int (*bpf_dispatcher_fn)(const void *ctx, const struct bpf_insn *insnsi, diff --git a/kernel/bpf/btf.c b/kernel/bpf/btf.c index c0d73d71c539..875355ff3718 100644 --- a/kernel/bpf/btf.c +++ b/kernel/bpf/btf.c @@ -6017,15 +6017,18 @@ error: return -EINVAL; } -int btf_struct_access(struct bpf_verifier_log *log, const struct btf *btf, - const struct btf_type *t, int off, int size, - enum bpf_access_type atype __maybe_unused, +int btf_struct_access(struct bpf_verifier_log *log, + const struct bpf_reg_state *reg, + int off, int size, enum bpf_access_type atype __maybe_unused, u32 *next_btf_id, enum bpf_type_flag *flag) { + const struct btf *btf = reg->btf; enum bpf_type_flag tmp_flag = 0; + const struct btf_type *t; + u32 id = reg->btf_id; int err; - u32 id; + t = btf_type_by_id(btf, id); do { err = btf_struct_walk(log, btf, t, off, size, &id, &tmp_flag); diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index c588e5483540..5e74f460dfd0 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -4688,16 +4688,14 @@ static int check_ptr_to_btf_access(struct bpf_verifier_env *env, } if (env->ops->btf_struct_access) { - ret = env->ops->btf_struct_access(&env->log, reg->btf, t, - off, size, atype, &btf_id, &flag); + ret = env->ops->btf_struct_access(&env->log, reg, off, size, atype, &btf_id, &flag); } else { if (atype != BPF_READ) { verbose(env, "only read is supported\n"); return -EACCES; } - ret = btf_struct_access(&env->log, reg->btf, t, off, size, - atype, &btf_id, &flag); + ret = btf_struct_access(&env->log, reg, off, size, atype, &btf_id, &flag); } if (ret < 0) @@ -4723,6 +4721,7 @@ static int check_ptr_to_map_access(struct bpf_verifier_env *env, { struct bpf_reg_state *reg = regs + regno; struct bpf_map *map = reg->map_ptr; + struct bpf_reg_state map_reg; enum bpf_type_flag flag = 0; const struct btf_type *t; const char *tname; @@ -4761,7 +4760,10 @@ static int check_ptr_to_map_access(struct bpf_verifier_env *env, return -EACCES; } - ret = btf_struct_access(&env->log, btf_vmlinux, t, off, size, atype, &btf_id, &flag); + /* Simulate access to a PTR_TO_BTF_ID */ + memset(&map_reg, 0, sizeof(map_reg)); + mark_btf_ld_reg(env, &map_reg, 0, PTR_TO_BTF_ID, btf_vmlinux, *map->ops->map_btf_id, 0); + ret = btf_struct_access(&env->log, &map_reg, off, size, atype, &btf_id, &flag); if (ret < 0) return ret; diff --git a/net/bpf/bpf_dummy_struct_ops.c b/net/bpf/bpf_dummy_struct_ops.c index e78dadfc5829..2d434c1f4617 100644 --- a/net/bpf/bpf_dummy_struct_ops.c +++ b/net/bpf/bpf_dummy_struct_ops.c @@ -156,29 +156,29 @@ static bool bpf_dummy_ops_is_valid_access(int off, int size, } static int bpf_dummy_ops_btf_struct_access(struct bpf_verifier_log *log, - const struct btf *btf, - const struct btf_type *t, int off, - int size, enum bpf_access_type atype, + const struct bpf_reg_state *reg, + int off, int size, enum bpf_access_type atype, u32 *next_btf_id, enum bpf_type_flag *flag) { const struct btf_type *state; + const struct btf_type *t; s32 type_id; int err; - type_id = btf_find_by_name_kind(btf, "bpf_dummy_ops_state", + type_id = btf_find_by_name_kind(reg->btf, "bpf_dummy_ops_state", BTF_KIND_STRUCT); if (type_id < 0) return -EINVAL; - state = btf_type_by_id(btf, type_id); + t = btf_type_by_id(reg->btf, reg->btf_id); + state = btf_type_by_id(reg->btf, type_id); if (t != state) { bpf_log(log, "only access to bpf_dummy_ops_state is supported\n"); return -EACCES; } - err = btf_struct_access(log, btf, t, off, size, atype, next_btf_id, - flag); + err = btf_struct_access(log, reg, off, size, atype, next_btf_id, flag); if (err < 0) return err; diff --git a/net/core/filter.c b/net/core/filter.c index 6dd2baf5eeb2..37fad5a9b752 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -8651,28 +8651,25 @@ static bool tc_cls_act_is_valid_access(int off, int size, DEFINE_MUTEX(nf_conn_btf_access_lock); EXPORT_SYMBOL_GPL(nf_conn_btf_access_lock); -int (*nfct_btf_struct_access)(struct bpf_verifier_log *log, const struct btf *btf, - const struct btf_type *t, int off, int size, - enum bpf_access_type atype, u32 *next_btf_id, - enum bpf_type_flag *flag); +int (*nfct_btf_struct_access)(struct bpf_verifier_log *log, + const struct bpf_reg_state *reg, + int off, int size, enum bpf_access_type atype, + u32 *next_btf_id, enum bpf_type_flag *flag); EXPORT_SYMBOL_GPL(nfct_btf_struct_access); static int tc_cls_act_btf_struct_access(struct bpf_verifier_log *log, - const struct btf *btf, - const struct btf_type *t, int off, - int size, enum bpf_access_type atype, - u32 *next_btf_id, - enum bpf_type_flag *flag) + const struct bpf_reg_state *reg, + int off, int size, enum bpf_access_type atype, + u32 *next_btf_id, enum bpf_type_flag *flag) { int ret = -EACCES; if (atype == BPF_READ) - return btf_struct_access(log, btf, t, off, size, atype, next_btf_id, - flag); + return btf_struct_access(log, reg, off, size, atype, next_btf_id, flag); mutex_lock(&nf_conn_btf_access_lock); if (nfct_btf_struct_access) - ret = nfct_btf_struct_access(log, btf, t, off, size, atype, next_btf_id, flag); + ret = nfct_btf_struct_access(log, reg, off, size, atype, next_btf_id, flag); mutex_unlock(&nf_conn_btf_access_lock); return ret; @@ -8738,21 +8735,18 @@ void bpf_warn_invalid_xdp_action(struct net_device *dev, struct bpf_prog *prog, EXPORT_SYMBOL_GPL(bpf_warn_invalid_xdp_action); static int xdp_btf_struct_access(struct bpf_verifier_log *log, - const struct btf *btf, - const struct btf_type *t, int off, - int size, enum bpf_access_type atype, - u32 *next_btf_id, - enum bpf_type_flag *flag) + const struct bpf_reg_state *reg, + int off, int size, enum bpf_access_type atype, + u32 *next_btf_id, enum bpf_type_flag *flag) { int ret = -EACCES; if (atype == BPF_READ) - return btf_struct_access(log, btf, t, off, size, atype, next_btf_id, - flag); + return btf_struct_access(log, reg, off, size, atype, next_btf_id, flag); mutex_lock(&nf_conn_btf_access_lock); if (nfct_btf_struct_access) - ret = nfct_btf_struct_access(log, btf, t, off, size, atype, next_btf_id, flag); + ret = nfct_btf_struct_access(log, reg, off, size, atype, next_btf_id, flag); mutex_unlock(&nf_conn_btf_access_lock); return ret; diff --git a/net/ipv4/bpf_tcp_ca.c b/net/ipv4/bpf_tcp_ca.c index 6da16ae6a962..d15c91de995f 100644 --- a/net/ipv4/bpf_tcp_ca.c +++ b/net/ipv4/bpf_tcp_ca.c @@ -69,18 +69,17 @@ static bool bpf_tcp_ca_is_valid_access(int off, int size, } static int bpf_tcp_ca_btf_struct_access(struct bpf_verifier_log *log, - const struct btf *btf, - const struct btf_type *t, int off, - int size, enum bpf_access_type atype, - u32 *next_btf_id, - enum bpf_type_flag *flag) + const struct bpf_reg_state *reg, + int off, int size, enum bpf_access_type atype, + u32 *next_btf_id, enum bpf_type_flag *flag) { + const struct btf_type *t; size_t end; if (atype == BPF_READ) - return btf_struct_access(log, btf, t, off, size, atype, next_btf_id, - flag); + return btf_struct_access(log, reg, off, size, atype, next_btf_id, flag); + t = btf_type_by_id(reg->btf, reg->btf_id); if (t != tcp_sock_type) { bpf_log(log, "only read is supported\n"); return -EACCES; diff --git a/net/netfilter/nf_conntrack_bpf.c b/net/netfilter/nf_conntrack_bpf.c index 8639e7efd0e2..24002bc61e07 100644 --- a/net/netfilter/nf_conntrack_bpf.c +++ b/net/netfilter/nf_conntrack_bpf.c @@ -191,19 +191,16 @@ BTF_ID(struct, nf_conn___init) /* Check writes into `struct nf_conn` */ static int _nf_conntrack_btf_struct_access(struct bpf_verifier_log *log, - const struct btf *btf, - const struct btf_type *t, int off, - int size, enum bpf_access_type atype, - u32 *next_btf_id, - enum bpf_type_flag *flag) + const struct bpf_reg_state *reg, + int off, int size, enum bpf_access_type atype, + u32 *next_btf_id, enum bpf_type_flag *flag) { - const struct btf_type *ncit; - const struct btf_type *nct; + const struct btf_type *ncit, *nct, *t; size_t end; - ncit = btf_type_by_id(btf, btf_nf_conn_ids[1]); - nct = btf_type_by_id(btf, btf_nf_conn_ids[0]); - + ncit = btf_type_by_id(reg->btf, btf_nf_conn_ids[1]); + nct = btf_type_by_id(reg->btf, btf_nf_conn_ids[0]); + t = btf_type_by_id(reg->btf, reg->btf_id); if (t != nct && t != ncit) { bpf_log(log, "only read is supported\n"); return -EACCES; From 7d34aa3e03b6a56306296bd98b26c6a1710cd57b Mon Sep 17 00:00:00 2001 From: Phil Sutter Date: Fri, 14 Oct 2022 23:45:58 +0200 Subject: [PATCH 0919/2002] netfilter: nf_tables: Extend nft_expr_ops::dump callback parameters Add a 'reset' flag just like with nft_object_ops::dump. This will be useful to reset "anonymous stateful objects", e.g. simple rule counters. No functional change intended. Signed-off-by: Phil Sutter Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/nf_tables.h | 3 ++- include/net/netfilter/nft_fib.h | 2 +- include/net/netfilter/nft_meta.h | 4 ++-- include/net/netfilter/nft_reject.h | 3 ++- net/ipv4/netfilter/nft_dup_ipv4.c | 3 ++- net/ipv6/netfilter/nft_dup_ipv6.c | 3 ++- net/netfilter/nf_tables_api.c | 2 +- net/netfilter/nft_bitwise.c | 6 ++++-- net/netfilter/nft_byteorder.c | 3 ++- net/netfilter/nft_cmp.c | 9 ++++++--- net/netfilter/nft_compat.c | 9 ++++++--- net/netfilter/nft_connlimit.c | 3 ++- net/netfilter/nft_counter.c | 3 ++- net/netfilter/nft_ct.c | 6 ++++-- net/netfilter/nft_dup_netdev.c | 3 ++- net/netfilter/nft_dynset.c | 3 ++- net/netfilter/nft_exthdr.c | 9 ++++++--- net/netfilter/nft_fib.c | 2 +- net/netfilter/nft_flow_offload.c | 3 ++- net/netfilter/nft_fwd_netdev.c | 6 ++++-- net/netfilter/nft_hash.c | 4 ++-- net/netfilter/nft_immediate.c | 3 ++- net/netfilter/nft_inner.c | 3 ++- net/netfilter/nft_last.c | 3 ++- net/netfilter/nft_limit.c | 5 +++-- net/netfilter/nft_log.c | 3 ++- net/netfilter/nft_lookup.c | 3 ++- net/netfilter/nft_masq.c | 3 ++- net/netfilter/nft_meta.c | 5 +++-- net/netfilter/nft_nat.c | 3 ++- net/netfilter/nft_numgen.c | 6 ++++-- net/netfilter/nft_objref.c | 6 ++++-- net/netfilter/nft_osf.c | 3 ++- net/netfilter/nft_payload.c | 6 ++++-- net/netfilter/nft_queue.c | 6 ++++-- net/netfilter/nft_quota.c | 3 ++- net/netfilter/nft_range.c | 3 ++- net/netfilter/nft_redir.c | 3 ++- net/netfilter/nft_reject.c | 3 ++- net/netfilter/nft_rt.c | 2 +- net/netfilter/nft_socket.c | 2 +- net/netfilter/nft_synproxy.c | 3 ++- net/netfilter/nft_tproxy.c | 2 +- net/netfilter/nft_tunnel.c | 2 +- net/netfilter/nft_xfrm.c | 2 +- 45 files changed, 110 insertions(+), 62 deletions(-) diff --git a/include/net/netfilter/nf_tables.h b/include/net/netfilter/nf_tables.h index 38e2b396e38a..c557a57fb0f1 100644 --- a/include/net/netfilter/nf_tables.h +++ b/include/net/netfilter/nf_tables.h @@ -927,7 +927,8 @@ struct nft_expr_ops { void (*destroy_clone)(const struct nft_ctx *ctx, const struct nft_expr *expr); int (*dump)(struct sk_buff *skb, - const struct nft_expr *expr); + const struct nft_expr *expr, + bool reset); int (*validate)(const struct nft_ctx *ctx, const struct nft_expr *expr, const struct nft_data **data); diff --git a/include/net/netfilter/nft_fib.h b/include/net/netfilter/nft_fib.h index eed099eae672..167640b843ef 100644 --- a/include/net/netfilter/nft_fib.h +++ b/include/net/netfilter/nft_fib.h @@ -18,7 +18,7 @@ nft_fib_is_loopback(const struct sk_buff *skb, const struct net_device *in) return skb->pkt_type == PACKET_LOOPBACK || in->flags & IFF_LOOPBACK; } -int nft_fib_dump(struct sk_buff *skb, const struct nft_expr *expr); +int nft_fib_dump(struct sk_buff *skb, const struct nft_expr *expr, bool reset); int nft_fib_init(const struct nft_ctx *ctx, const struct nft_expr *expr, const struct nlattr * const tb[]); int nft_fib_validate(const struct nft_ctx *ctx, const struct nft_expr *expr, diff --git a/include/net/netfilter/nft_meta.h b/include/net/netfilter/nft_meta.h index f3a5285a511c..ba1238f12a48 100644 --- a/include/net/netfilter/nft_meta.h +++ b/include/net/netfilter/nft_meta.h @@ -24,10 +24,10 @@ int nft_meta_set_init(const struct nft_ctx *ctx, const struct nlattr * const tb[]); int nft_meta_get_dump(struct sk_buff *skb, - const struct nft_expr *expr); + const struct nft_expr *expr, bool reset); int nft_meta_set_dump(struct sk_buff *skb, - const struct nft_expr *expr); + const struct nft_expr *expr, bool reset); void nft_meta_get_eval(const struct nft_expr *expr, struct nft_regs *regs, diff --git a/include/net/netfilter/nft_reject.h b/include/net/netfilter/nft_reject.h index 56b123a42220..6d9ba62efd75 100644 --- a/include/net/netfilter/nft_reject.h +++ b/include/net/netfilter/nft_reject.h @@ -22,7 +22,8 @@ int nft_reject_init(const struct nft_ctx *ctx, const struct nft_expr *expr, const struct nlattr * const tb[]); -int nft_reject_dump(struct sk_buff *skb, const struct nft_expr *expr); +int nft_reject_dump(struct sk_buff *skb, + const struct nft_expr *expr, bool reset); int nft_reject_icmp_code(u8 code); int nft_reject_icmpv6_code(u8 code); diff --git a/net/ipv4/netfilter/nft_dup_ipv4.c b/net/ipv4/netfilter/nft_dup_ipv4.c index 0bcd6aee6000..a522c3a3be52 100644 --- a/net/ipv4/netfilter/nft_dup_ipv4.c +++ b/net/ipv4/netfilter/nft_dup_ipv4.c @@ -52,7 +52,8 @@ static int nft_dup_ipv4_init(const struct nft_ctx *ctx, return err; } -static int nft_dup_ipv4_dump(struct sk_buff *skb, const struct nft_expr *expr) +static int nft_dup_ipv4_dump(struct sk_buff *skb, + const struct nft_expr *expr, bool reset) { struct nft_dup_ipv4 *priv = nft_expr_priv(expr); diff --git a/net/ipv6/netfilter/nft_dup_ipv6.c b/net/ipv6/netfilter/nft_dup_ipv6.c index 70a405b4006f..c82f3fdd4a65 100644 --- a/net/ipv6/netfilter/nft_dup_ipv6.c +++ b/net/ipv6/netfilter/nft_dup_ipv6.c @@ -50,7 +50,8 @@ static int nft_dup_ipv6_init(const struct nft_ctx *ctx, return err; } -static int nft_dup_ipv6_dump(struct sk_buff *skb, const struct nft_expr *expr) +static int nft_dup_ipv6_dump(struct sk_buff *skb, + const struct nft_expr *expr, bool reset) { struct nft_dup_ipv6 *priv = nft_expr_priv(expr); diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c index 62da204eed41..741a0e386406 100644 --- a/net/netfilter/nf_tables_api.c +++ b/net/netfilter/nf_tables_api.c @@ -2769,7 +2769,7 @@ static int nf_tables_fill_expr_info(struct sk_buff *skb, NFTA_EXPR_DATA); if (data == NULL) goto nla_put_failure; - if (expr->ops->dump(skb, expr) < 0) + if (expr->ops->dump(skb, expr, false) < 0) goto nla_put_failure; nla_nest_end(skb, data); } diff --git a/net/netfilter/nft_bitwise.c b/net/netfilter/nft_bitwise.c index e6e402b247d0..84eae7cabc67 100644 --- a/net/netfilter/nft_bitwise.c +++ b/net/netfilter/nft_bitwise.c @@ -232,7 +232,8 @@ static int nft_bitwise_dump_shift(struct sk_buff *skb, return 0; } -static int nft_bitwise_dump(struct sk_buff *skb, const struct nft_expr *expr) +static int nft_bitwise_dump(struct sk_buff *skb, + const struct nft_expr *expr, bool reset) { const struct nft_bitwise *priv = nft_expr_priv(expr); int err = 0; @@ -393,7 +394,8 @@ static int nft_bitwise_fast_init(const struct nft_ctx *ctx, } static int -nft_bitwise_fast_dump(struct sk_buff *skb, const struct nft_expr *expr) +nft_bitwise_fast_dump(struct sk_buff *skb, + const struct nft_expr *expr, bool reset) { const struct nft_bitwise_fast_expr *priv = nft_expr_priv(expr); struct nft_data data; diff --git a/net/netfilter/nft_byteorder.c b/net/netfilter/nft_byteorder.c index f952a80275a8..b66647a5a171 100644 --- a/net/netfilter/nft_byteorder.c +++ b/net/netfilter/nft_byteorder.c @@ -148,7 +148,8 @@ static int nft_byteorder_init(const struct nft_ctx *ctx, priv->len); } -static int nft_byteorder_dump(struct sk_buff *skb, const struct nft_expr *expr) +static int nft_byteorder_dump(struct sk_buff *skb, + const struct nft_expr *expr, bool reset) { const struct nft_byteorder *priv = nft_expr_priv(expr); diff --git a/net/netfilter/nft_cmp.c b/net/netfilter/nft_cmp.c index 963cf831799c..6eb21a4f5698 100644 --- a/net/netfilter/nft_cmp.c +++ b/net/netfilter/nft_cmp.c @@ -92,7 +92,8 @@ static int nft_cmp_init(const struct nft_ctx *ctx, const struct nft_expr *expr, return 0; } -static int nft_cmp_dump(struct sk_buff *skb, const struct nft_expr *expr) +static int nft_cmp_dump(struct sk_buff *skb, + const struct nft_expr *expr, bool reset) { const struct nft_cmp_expr *priv = nft_expr_priv(expr); @@ -253,7 +254,8 @@ static int nft_cmp_fast_offload(struct nft_offload_ctx *ctx, return __nft_cmp_offload(ctx, flow, &cmp); } -static int nft_cmp_fast_dump(struct sk_buff *skb, const struct nft_expr *expr) +static int nft_cmp_fast_dump(struct sk_buff *skb, + const struct nft_expr *expr, bool reset) { const struct nft_cmp_fast_expr *priv = nft_expr_priv(expr); enum nft_cmp_ops op = priv->inv ? NFT_CMP_NEQ : NFT_CMP_EQ; @@ -347,7 +349,8 @@ static int nft_cmp16_fast_offload(struct nft_offload_ctx *ctx, return __nft_cmp_offload(ctx, flow, &cmp); } -static int nft_cmp16_fast_dump(struct sk_buff *skb, const struct nft_expr *expr) +static int nft_cmp16_fast_dump(struct sk_buff *skb, + const struct nft_expr *expr, bool reset) { const struct nft_cmp16_fast_expr *priv = nft_expr_priv(expr); enum nft_cmp_ops op = priv->inv ? NFT_CMP_NEQ : NFT_CMP_EQ; diff --git a/net/netfilter/nft_compat.c b/net/netfilter/nft_compat.c index c16172427622..5284cd2ad532 100644 --- a/net/netfilter/nft_compat.c +++ b/net/netfilter/nft_compat.c @@ -324,7 +324,8 @@ static int nft_extension_dump_info(struct sk_buff *skb, int attr, return 0; } -static int nft_target_dump(struct sk_buff *skb, const struct nft_expr *expr) +static int nft_target_dump(struct sk_buff *skb, + const struct nft_expr *expr, bool reset) { const struct xt_target *target = expr->ops->data; void *info = nft_expr_priv(expr); @@ -572,12 +573,14 @@ nla_put_failure: return -1; } -static int nft_match_dump(struct sk_buff *skb, const struct nft_expr *expr) +static int nft_match_dump(struct sk_buff *skb, + const struct nft_expr *expr, bool reset) { return __nft_match_dump(skb, expr, nft_expr_priv(expr)); } -static int nft_match_large_dump(struct sk_buff *skb, const struct nft_expr *e) +static int nft_match_large_dump(struct sk_buff *skb, + const struct nft_expr *e, bool reset) { struct nft_xt_match_priv *priv = nft_expr_priv(e); diff --git a/net/netfilter/nft_connlimit.c b/net/netfilter/nft_connlimit.c index d657f999a11b..de9d1980df69 100644 --- a/net/netfilter/nft_connlimit.c +++ b/net/netfilter/nft_connlimit.c @@ -185,7 +185,8 @@ static void nft_connlimit_eval(const struct nft_expr *expr, nft_connlimit_do_eval(priv, regs, pkt, NULL); } -static int nft_connlimit_dump(struct sk_buff *skb, const struct nft_expr *expr) +static int nft_connlimit_dump(struct sk_buff *skb, + const struct nft_expr *expr, bool reset) { struct nft_connlimit *priv = nft_expr_priv(expr); diff --git a/net/netfilter/nft_counter.c b/net/netfilter/nft_counter.c index f4d3573e8782..06482fb9c145 100644 --- a/net/netfilter/nft_counter.c +++ b/net/netfilter/nft_counter.c @@ -201,7 +201,8 @@ void nft_counter_eval(const struct nft_expr *expr, struct nft_regs *regs, nft_counter_do_eval(priv, regs, pkt); } -static int nft_counter_dump(struct sk_buff *skb, const struct nft_expr *expr) +static int nft_counter_dump(struct sk_buff *skb, + const struct nft_expr *expr, bool reset) { struct nft_counter_percpu_priv *priv = nft_expr_priv(expr); diff --git a/net/netfilter/nft_ct.c b/net/netfilter/nft_ct.c index a3f01f209a53..a0696d7ea10c 100644 --- a/net/netfilter/nft_ct.c +++ b/net/netfilter/nft_ct.c @@ -641,7 +641,8 @@ static void nft_ct_set_destroy(const struct nft_ctx *ctx, nf_ct_netns_put(ctx->net, ctx->family); } -static int nft_ct_get_dump(struct sk_buff *skb, const struct nft_expr *expr) +static int nft_ct_get_dump(struct sk_buff *skb, + const struct nft_expr *expr, bool reset) { const struct nft_ct *priv = nft_expr_priv(expr); @@ -703,7 +704,8 @@ static bool nft_ct_get_reduce(struct nft_regs_track *track, return nft_expr_reduce_bitwise(track, expr); } -static int nft_ct_set_dump(struct sk_buff *skb, const struct nft_expr *expr) +static int nft_ct_set_dump(struct sk_buff *skb, + const struct nft_expr *expr, bool reset) { const struct nft_ct *priv = nft_expr_priv(expr); diff --git a/net/netfilter/nft_dup_netdev.c b/net/netfilter/nft_dup_netdev.c index 63507402716d..e5739a59ebf1 100644 --- a/net/netfilter/nft_dup_netdev.c +++ b/net/netfilter/nft_dup_netdev.c @@ -44,7 +44,8 @@ static int nft_dup_netdev_init(const struct nft_ctx *ctx, sizeof(int)); } -static int nft_dup_netdev_dump(struct sk_buff *skb, const struct nft_expr *expr) +static int nft_dup_netdev_dump(struct sk_buff *skb, + const struct nft_expr *expr, bool reset) { struct nft_dup_netdev *priv = nft_expr_priv(expr); diff --git a/net/netfilter/nft_dynset.c b/net/netfilter/nft_dynset.c index 6983e6ddeef9..01c61e090639 100644 --- a/net/netfilter/nft_dynset.c +++ b/net/netfilter/nft_dynset.c @@ -357,7 +357,8 @@ static void nft_dynset_destroy(const struct nft_ctx *ctx, nf_tables_destroy_set(ctx, priv->set); } -static int nft_dynset_dump(struct sk_buff *skb, const struct nft_expr *expr) +static int nft_dynset_dump(struct sk_buff *skb, + const struct nft_expr *expr, bool reset) { const struct nft_dynset *priv = nft_expr_priv(expr); u32 flags = priv->invert ? NFT_DYNSET_F_INV : 0; diff --git a/net/netfilter/nft_exthdr.c b/net/netfilter/nft_exthdr.c index a67ea9c3ae57..ed929d0d37ce 100644 --- a/net/netfilter/nft_exthdr.c +++ b/net/netfilter/nft_exthdr.c @@ -576,7 +576,8 @@ nla_put_failure: return -1; } -static int nft_exthdr_dump(struct sk_buff *skb, const struct nft_expr *expr) +static int nft_exthdr_dump(struct sk_buff *skb, + const struct nft_expr *expr, bool reset) { const struct nft_exthdr *priv = nft_expr_priv(expr); @@ -586,7 +587,8 @@ static int nft_exthdr_dump(struct sk_buff *skb, const struct nft_expr *expr) return nft_exthdr_dump_common(skb, priv); } -static int nft_exthdr_dump_set(struct sk_buff *skb, const struct nft_expr *expr) +static int nft_exthdr_dump_set(struct sk_buff *skb, + const struct nft_expr *expr, bool reset) { const struct nft_exthdr *priv = nft_expr_priv(expr); @@ -596,7 +598,8 @@ static int nft_exthdr_dump_set(struct sk_buff *skb, const struct nft_expr *expr) return nft_exthdr_dump_common(skb, priv); } -static int nft_exthdr_dump_strip(struct sk_buff *skb, const struct nft_expr *expr) +static int nft_exthdr_dump_strip(struct sk_buff *skb, + const struct nft_expr *expr, bool reset) { const struct nft_exthdr *priv = nft_expr_priv(expr); diff --git a/net/netfilter/nft_fib.c b/net/netfilter/nft_fib.c index 1f12d7ade606..6e049fd48760 100644 --- a/net/netfilter/nft_fib.c +++ b/net/netfilter/nft_fib.c @@ -118,7 +118,7 @@ int nft_fib_init(const struct nft_ctx *ctx, const struct nft_expr *expr, } EXPORT_SYMBOL_GPL(nft_fib_init); -int nft_fib_dump(struct sk_buff *skb, const struct nft_expr *expr) +int nft_fib_dump(struct sk_buff *skb, const struct nft_expr *expr, bool reset) { const struct nft_fib *priv = nft_expr_priv(expr); diff --git a/net/netfilter/nft_flow_offload.c b/net/netfilter/nft_flow_offload.c index a25c88bc8b75..e860d8fe0e5e 100644 --- a/net/netfilter/nft_flow_offload.c +++ b/net/netfilter/nft_flow_offload.c @@ -433,7 +433,8 @@ static void nft_flow_offload_destroy(const struct nft_ctx *ctx, nf_ct_netns_put(ctx->net, ctx->family); } -static int nft_flow_offload_dump(struct sk_buff *skb, const struct nft_expr *expr) +static int nft_flow_offload_dump(struct sk_buff *skb, + const struct nft_expr *expr, bool reset) { struct nft_flow_offload *priv = nft_expr_priv(expr); diff --git a/net/netfilter/nft_fwd_netdev.c b/net/netfilter/nft_fwd_netdev.c index 7c5876dc9ff2..7b9d4d1bd17c 100644 --- a/net/netfilter/nft_fwd_netdev.c +++ b/net/netfilter/nft_fwd_netdev.c @@ -56,7 +56,8 @@ static int nft_fwd_netdev_init(const struct nft_ctx *ctx, sizeof(int)); } -static int nft_fwd_netdev_dump(struct sk_buff *skb, const struct nft_expr *expr) +static int nft_fwd_netdev_dump(struct sk_buff *skb, + const struct nft_expr *expr, bool reset) { struct nft_fwd_netdev *priv = nft_expr_priv(expr); @@ -186,7 +187,8 @@ static int nft_fwd_neigh_init(const struct nft_ctx *ctx, addr_len); } -static int nft_fwd_neigh_dump(struct sk_buff *skb, const struct nft_expr *expr) +static int nft_fwd_neigh_dump(struct sk_buff *skb, + const struct nft_expr *expr, bool reset) { struct nft_fwd_neigh *priv = nft_expr_priv(expr); diff --git a/net/netfilter/nft_hash.c b/net/netfilter/nft_hash.c index e5631e88b285..ee8d487b69c0 100644 --- a/net/netfilter/nft_hash.c +++ b/net/netfilter/nft_hash.c @@ -139,7 +139,7 @@ static int nft_symhash_init(const struct nft_ctx *ctx, } static int nft_jhash_dump(struct sk_buff *skb, - const struct nft_expr *expr) + const struct nft_expr *expr, bool reset) { const struct nft_jhash *priv = nft_expr_priv(expr); @@ -176,7 +176,7 @@ static bool nft_jhash_reduce(struct nft_regs_track *track, } static int nft_symhash_dump(struct sk_buff *skb, - const struct nft_expr *expr) + const struct nft_expr *expr, bool reset) { const struct nft_symhash *priv = nft_expr_priv(expr); diff --git a/net/netfilter/nft_immediate.c b/net/netfilter/nft_immediate.c index 5f28b21abc7d..c9d2f7c29f53 100644 --- a/net/netfilter/nft_immediate.c +++ b/net/netfilter/nft_immediate.c @@ -147,7 +147,8 @@ static void nft_immediate_destroy(const struct nft_ctx *ctx, } } -static int nft_immediate_dump(struct sk_buff *skb, const struct nft_expr *expr) +static int nft_immediate_dump(struct sk_buff *skb, + const struct nft_expr *expr, bool reset) { const struct nft_immediate_expr *priv = nft_expr_priv(expr); diff --git a/net/netfilter/nft_inner.c b/net/netfilter/nft_inner.c index 809f0d0787ec..6d96b826db4e 100644 --- a/net/netfilter/nft_inner.c +++ b/net/netfilter/nft_inner.c @@ -347,7 +347,8 @@ static int nft_inner_init(const struct nft_ctx *ctx, return 0; } -static int nft_inner_dump(struct sk_buff *skb, const struct nft_expr *expr) +static int nft_inner_dump(struct sk_buff *skb, + const struct nft_expr *expr, bool reset) { const struct nft_inner *priv = nft_expr_priv(expr); diff --git a/net/netfilter/nft_last.c b/net/netfilter/nft_last.c index bb15a55dad5c..7f2bda6641bd 100644 --- a/net/netfilter/nft_last.c +++ b/net/netfilter/nft_last.c @@ -65,7 +65,8 @@ static void nft_last_eval(const struct nft_expr *expr, WRITE_ONCE(last->set, 1); } -static int nft_last_dump(struct sk_buff *skb, const struct nft_expr *expr) +static int nft_last_dump(struct sk_buff *skb, + const struct nft_expr *expr, bool reset) { struct nft_last_priv *priv = nft_expr_priv(expr); struct nft_last *last = priv->last; diff --git a/net/netfilter/nft_limit.c b/net/netfilter/nft_limit.c index 981addb2d051..145dc62c6247 100644 --- a/net/netfilter/nft_limit.c +++ b/net/netfilter/nft_limit.c @@ -193,7 +193,8 @@ static int nft_limit_pkts_init(const struct nft_ctx *ctx, return 0; } -static int nft_limit_pkts_dump(struct sk_buff *skb, const struct nft_expr *expr) +static int nft_limit_pkts_dump(struct sk_buff *skb, + const struct nft_expr *expr, bool reset) { const struct nft_limit_priv_pkts *priv = nft_expr_priv(expr); @@ -251,7 +252,7 @@ static int nft_limit_bytes_init(const struct nft_ctx *ctx, } static int nft_limit_bytes_dump(struct sk_buff *skb, - const struct nft_expr *expr) + const struct nft_expr *expr, bool reset) { const struct nft_limit_priv *priv = nft_expr_priv(expr); diff --git a/net/netfilter/nft_log.c b/net/netfilter/nft_log.c index 0e13c003f0c1..5defe6e4fd98 100644 --- a/net/netfilter/nft_log.c +++ b/net/netfilter/nft_log.c @@ -241,7 +241,8 @@ static void nft_log_destroy(const struct nft_ctx *ctx, nf_logger_put(ctx->family, li->type); } -static int nft_log_dump(struct sk_buff *skb, const struct nft_expr *expr) +static int nft_log_dump(struct sk_buff *skb, + const struct nft_expr *expr, bool reset) { const struct nft_log *priv = nft_expr_priv(expr); const struct nf_loginfo *li = &priv->loginfo; diff --git a/net/netfilter/nft_lookup.c b/net/netfilter/nft_lookup.c index dfae12759c7c..cae5a6724163 100644 --- a/net/netfilter/nft_lookup.c +++ b/net/netfilter/nft_lookup.c @@ -178,7 +178,8 @@ static void nft_lookup_destroy(const struct nft_ctx *ctx, nf_tables_destroy_set(ctx, priv->set); } -static int nft_lookup_dump(struct sk_buff *skb, const struct nft_expr *expr) +static int nft_lookup_dump(struct sk_buff *skb, + const struct nft_expr *expr, bool reset) { const struct nft_lookup *priv = nft_expr_priv(expr); u32 flags = priv->invert ? NFT_LOOKUP_F_INV : 0; diff --git a/net/netfilter/nft_masq.c b/net/netfilter/nft_masq.c index 2a0adc497bbb..e55e455275c4 100644 --- a/net/netfilter/nft_masq.c +++ b/net/netfilter/nft_masq.c @@ -73,7 +73,8 @@ static int nft_masq_init(const struct nft_ctx *ctx, return nf_ct_netns_get(ctx->net, ctx->family); } -static int nft_masq_dump(struct sk_buff *skb, const struct nft_expr *expr) +static int nft_masq_dump(struct sk_buff *skb, + const struct nft_expr *expr, bool reset) { const struct nft_masq *priv = nft_expr_priv(expr); diff --git a/net/netfilter/nft_meta.c b/net/netfilter/nft_meta.c index 8c39adeebb5c..e384e0de7a54 100644 --- a/net/netfilter/nft_meta.c +++ b/net/netfilter/nft_meta.c @@ -669,7 +669,7 @@ int nft_meta_set_init(const struct nft_ctx *ctx, EXPORT_SYMBOL_GPL(nft_meta_set_init); int nft_meta_get_dump(struct sk_buff *skb, - const struct nft_expr *expr) + const struct nft_expr *expr, bool reset) { const struct nft_meta *priv = nft_expr_priv(expr); @@ -684,7 +684,8 @@ nla_put_failure: } EXPORT_SYMBOL_GPL(nft_meta_get_dump); -int nft_meta_set_dump(struct sk_buff *skb, const struct nft_expr *expr) +int nft_meta_set_dump(struct sk_buff *skb, + const struct nft_expr *expr, bool reset) { const struct nft_meta *priv = nft_expr_priv(expr); diff --git a/net/netfilter/nft_nat.c b/net/netfilter/nft_nat.c index e5fd6995e4bf..047999150390 100644 --- a/net/netfilter/nft_nat.c +++ b/net/netfilter/nft_nat.c @@ -255,7 +255,8 @@ static int nft_nat_init(const struct nft_ctx *ctx, const struct nft_expr *expr, return nf_ct_netns_get(ctx->net, family); } -static int nft_nat_dump(struct sk_buff *skb, const struct nft_expr *expr) +static int nft_nat_dump(struct sk_buff *skb, + const struct nft_expr *expr, bool reset) { const struct nft_nat *priv = nft_expr_priv(expr); diff --git a/net/netfilter/nft_numgen.c b/net/netfilter/nft_numgen.c index 45d3dc9e96f2..7d29db7c2ac0 100644 --- a/net/netfilter/nft_numgen.c +++ b/net/netfilter/nft_numgen.c @@ -112,7 +112,8 @@ nla_put_failure: return -1; } -static int nft_ng_inc_dump(struct sk_buff *skb, const struct nft_expr *expr) +static int nft_ng_inc_dump(struct sk_buff *skb, + const struct nft_expr *expr, bool reset) { const struct nft_ng_inc *priv = nft_expr_priv(expr); @@ -168,7 +169,8 @@ static int nft_ng_random_init(const struct nft_ctx *ctx, NULL, NFT_DATA_VALUE, sizeof(u32)); } -static int nft_ng_random_dump(struct sk_buff *skb, const struct nft_expr *expr) +static int nft_ng_random_dump(struct sk_buff *skb, + const struct nft_expr *expr, bool reset) { const struct nft_ng_random *priv = nft_expr_priv(expr); diff --git a/net/netfilter/nft_objref.c b/net/netfilter/nft_objref.c index 74e0eea4abac..7b01aa2ef653 100644 --- a/net/netfilter/nft_objref.c +++ b/net/netfilter/nft_objref.c @@ -47,7 +47,8 @@ static int nft_objref_init(const struct nft_ctx *ctx, return 0; } -static int nft_objref_dump(struct sk_buff *skb, const struct nft_expr *expr) +static int nft_objref_dump(struct sk_buff *skb, + const struct nft_expr *expr, bool reset) { const struct nft_object *obj = nft_objref_priv(expr); @@ -155,7 +156,8 @@ static int nft_objref_map_init(const struct nft_ctx *ctx, return 0; } -static int nft_objref_map_dump(struct sk_buff *skb, const struct nft_expr *expr) +static int nft_objref_map_dump(struct sk_buff *skb, + const struct nft_expr *expr, bool reset) { const struct nft_objref_map *priv = nft_expr_priv(expr); diff --git a/net/netfilter/nft_osf.c b/net/netfilter/nft_osf.c index adacf95b6e2b..70820c66b591 100644 --- a/net/netfilter/nft_osf.c +++ b/net/netfilter/nft_osf.c @@ -92,7 +92,8 @@ static int nft_osf_init(const struct nft_ctx *ctx, return 0; } -static int nft_osf_dump(struct sk_buff *skb, const struct nft_expr *expr) +static int nft_osf_dump(struct sk_buff *skb, + const struct nft_expr *expr, bool reset) { const struct nft_osf *priv = nft_expr_priv(expr); diff --git a/net/netfilter/nft_payload.c b/net/netfilter/nft_payload.c index 53e64d8aa01f..336ac668cae3 100644 --- a/net/netfilter/nft_payload.c +++ b/net/netfilter/nft_payload.c @@ -231,7 +231,8 @@ static int nft_payload_init(const struct nft_ctx *ctx, priv->len); } -static int nft_payload_dump(struct sk_buff *skb, const struct nft_expr *expr) +static int nft_payload_dump(struct sk_buff *skb, + const struct nft_expr *expr, bool reset) { const struct nft_payload *priv = nft_expr_priv(expr); @@ -919,7 +920,8 @@ static int nft_payload_set_init(const struct nft_ctx *ctx, priv->len); } -static int nft_payload_set_dump(struct sk_buff *skb, const struct nft_expr *expr) +static int nft_payload_set_dump(struct sk_buff *skb, + const struct nft_expr *expr, bool reset) { const struct nft_payload_set *priv = nft_expr_priv(expr); diff --git a/net/netfilter/nft_queue.c b/net/netfilter/nft_queue.c index da29e92c03e2..b2b8127c8d43 100644 --- a/net/netfilter/nft_queue.c +++ b/net/netfilter/nft_queue.c @@ -152,7 +152,8 @@ static int nft_queue_sreg_init(const struct nft_ctx *ctx, return 0; } -static int nft_queue_dump(struct sk_buff *skb, const struct nft_expr *expr) +static int nft_queue_dump(struct sk_buff *skb, + const struct nft_expr *expr, bool reset) { const struct nft_queue *priv = nft_expr_priv(expr); @@ -168,7 +169,8 @@ nla_put_failure: } static int -nft_queue_sreg_dump(struct sk_buff *skb, const struct nft_expr *expr) +nft_queue_sreg_dump(struct sk_buff *skb, + const struct nft_expr *expr, bool reset) { const struct nft_queue *priv = nft_expr_priv(expr); diff --git a/net/netfilter/nft_quota.c b/net/netfilter/nft_quota.c index e6b0df68feea..b1a1217bca4c 100644 --- a/net/netfilter/nft_quota.c +++ b/net/netfilter/nft_quota.c @@ -217,7 +217,8 @@ static int nft_quota_init(const struct nft_ctx *ctx, return nft_quota_do_init(tb, priv); } -static int nft_quota_dump(struct sk_buff *skb, const struct nft_expr *expr) +static int nft_quota_dump(struct sk_buff *skb, + const struct nft_expr *expr, bool reset) { struct nft_quota *priv = nft_expr_priv(expr); diff --git a/net/netfilter/nft_range.c b/net/netfilter/nft_range.c index 832f0d725a9e..0566d6aaf1e5 100644 --- a/net/netfilter/nft_range.c +++ b/net/netfilter/nft_range.c @@ -111,7 +111,8 @@ err1: return err; } -static int nft_range_dump(struct sk_buff *skb, const struct nft_expr *expr) +static int nft_range_dump(struct sk_buff *skb, + const struct nft_expr *expr, bool reset) { const struct nft_range_expr *priv = nft_expr_priv(expr); diff --git a/net/netfilter/nft_redir.c b/net/netfilter/nft_redir.c index 5086adfe731c..5f7739987559 100644 --- a/net/netfilter/nft_redir.c +++ b/net/netfilter/nft_redir.c @@ -75,7 +75,8 @@ static int nft_redir_init(const struct nft_ctx *ctx, return nf_ct_netns_get(ctx->net, ctx->family); } -static int nft_redir_dump(struct sk_buff *skb, const struct nft_expr *expr) +static int nft_redir_dump(struct sk_buff *skb, + const struct nft_expr *expr, bool reset) { const struct nft_redir *priv = nft_expr_priv(expr); diff --git a/net/netfilter/nft_reject.c b/net/netfilter/nft_reject.c index 927ff8459bd9..f2addc844dd2 100644 --- a/net/netfilter/nft_reject.c +++ b/net/netfilter/nft_reject.c @@ -69,7 +69,8 @@ int nft_reject_init(const struct nft_ctx *ctx, } EXPORT_SYMBOL_GPL(nft_reject_init); -int nft_reject_dump(struct sk_buff *skb, const struct nft_expr *expr) +int nft_reject_dump(struct sk_buff *skb, + const struct nft_expr *expr, bool reset) { const struct nft_reject *priv = nft_expr_priv(expr); diff --git a/net/netfilter/nft_rt.c b/net/netfilter/nft_rt.c index 71931ec91721..5990fdd7b3cc 100644 --- a/net/netfilter/nft_rt.c +++ b/net/netfilter/nft_rt.c @@ -146,7 +146,7 @@ static int nft_rt_get_init(const struct nft_ctx *ctx, } static int nft_rt_get_dump(struct sk_buff *skb, - const struct nft_expr *expr) + const struct nft_expr *expr, bool reset) { const struct nft_rt *priv = nft_expr_priv(expr); diff --git a/net/netfilter/nft_socket.c b/net/netfilter/nft_socket.c index 49a5348a6a14..85f8df87efda 100644 --- a/net/netfilter/nft_socket.c +++ b/net/netfilter/nft_socket.c @@ -199,7 +199,7 @@ static int nft_socket_init(const struct nft_ctx *ctx, } static int nft_socket_dump(struct sk_buff *skb, - const struct nft_expr *expr) + const struct nft_expr *expr, bool reset) { const struct nft_socket *priv = nft_expr_priv(expr); diff --git a/net/netfilter/nft_synproxy.c b/net/netfilter/nft_synproxy.c index 6cf9a04fbfe2..13da882669a4 100644 --- a/net/netfilter/nft_synproxy.c +++ b/net/netfilter/nft_synproxy.c @@ -272,7 +272,8 @@ static void nft_synproxy_destroy(const struct nft_ctx *ctx, nft_synproxy_do_destroy(ctx); } -static int nft_synproxy_dump(struct sk_buff *skb, const struct nft_expr *expr) +static int nft_synproxy_dump(struct sk_buff *skb, + const struct nft_expr *expr, bool reset) { struct nft_synproxy *priv = nft_expr_priv(expr); diff --git a/net/netfilter/nft_tproxy.c b/net/netfilter/nft_tproxy.c index 62da25ad264b..ea83f661417e 100644 --- a/net/netfilter/nft_tproxy.c +++ b/net/netfilter/nft_tproxy.c @@ -294,7 +294,7 @@ static void nft_tproxy_destroy(const struct nft_ctx *ctx, } static int nft_tproxy_dump(struct sk_buff *skb, - const struct nft_expr *expr) + const struct nft_expr *expr, bool reset) { const struct nft_tproxy *priv = nft_expr_priv(expr); diff --git a/net/netfilter/nft_tunnel.c b/net/netfilter/nft_tunnel.c index 983ade4be3b3..b059aa541798 100644 --- a/net/netfilter/nft_tunnel.c +++ b/net/netfilter/nft_tunnel.c @@ -108,7 +108,7 @@ static int nft_tunnel_get_init(const struct nft_ctx *ctx, } static int nft_tunnel_get_dump(struct sk_buff *skb, - const struct nft_expr *expr) + const struct nft_expr *expr, bool reset) { const struct nft_tunnel *priv = nft_expr_priv(expr); diff --git a/net/netfilter/nft_xfrm.c b/net/netfilter/nft_xfrm.c index 1c5343c936a8..c88fd078a9ae 100644 --- a/net/netfilter/nft_xfrm.c +++ b/net/netfilter/nft_xfrm.c @@ -212,7 +212,7 @@ static void nft_xfrm_get_eval(const struct nft_expr *expr, } static int nft_xfrm_get_dump(struct sk_buff *skb, - const struct nft_expr *expr) + const struct nft_expr *expr, bool reset) { const struct nft_xfrm *priv = nft_expr_priv(expr); From 8daa8fde3fc3f069ff0b5c87079a5c1df7743113 Mon Sep 17 00:00:00 2001 From: Phil Sutter Date: Fri, 14 Oct 2022 23:45:59 +0200 Subject: [PATCH 0920/2002] netfilter: nf_tables: Introduce NFT_MSG_GETRULE_RESET Analogous to NFT_MSG_GETOBJ_RESET, but for rules: Reset stateful expressions like counters or quotas. The latter two are the only consumers, adjust their 'dump' callbacks to respect the parameter introduced earlier. Signed-off-by: Phil Sutter Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/nf_tables.h | 2 +- include/uapi/linux/netfilter/nf_tables.h | 2 + net/netfilter/nf_tables_api.c | 49 ++++++++++++++++-------- net/netfilter/nft_counter.c | 2 +- net/netfilter/nft_dynset.c | 4 +- net/netfilter/nft_inner.c | 2 +- net/netfilter/nft_quota.c | 2 +- 7 files changed, 41 insertions(+), 22 deletions(-) diff --git a/include/net/netfilter/nf_tables.h b/include/net/netfilter/nf_tables.h index c557a57fb0f1..e69ce23566ea 100644 --- a/include/net/netfilter/nf_tables.h +++ b/include/net/netfilter/nf_tables.h @@ -383,7 +383,7 @@ int nft_expr_inner_parse(const struct nft_ctx *ctx, const struct nlattr *nla, int nft_expr_clone(struct nft_expr *dst, struct nft_expr *src); void nft_expr_destroy(const struct nft_ctx *ctx, struct nft_expr *expr); int nft_expr_dump(struct sk_buff *skb, unsigned int attr, - const struct nft_expr *expr); + const struct nft_expr *expr, bool reset); bool nft_expr_reduce_bitwise(struct nft_regs_track *track, const struct nft_expr *expr); diff --git a/include/uapi/linux/netfilter/nf_tables.h b/include/uapi/linux/netfilter/nf_tables.h index e4b739d57480..cfa844da1ce6 100644 --- a/include/uapi/linux/netfilter/nf_tables.h +++ b/include/uapi/linux/netfilter/nf_tables.h @@ -97,6 +97,7 @@ enum nft_verdicts { * @NFT_MSG_NEWFLOWTABLE: add new flow table (enum nft_flowtable_attributes) * @NFT_MSG_GETFLOWTABLE: get flow table (enum nft_flowtable_attributes) * @NFT_MSG_DELFLOWTABLE: delete flow table (enum nft_flowtable_attributes) + * @NFT_MSG_GETRULE_RESET: get rules and reset stateful expressions (enum nft_obj_attributes) */ enum nf_tables_msg_types { NFT_MSG_NEWTABLE, @@ -124,6 +125,7 @@ enum nf_tables_msg_types { NFT_MSG_NEWFLOWTABLE, NFT_MSG_GETFLOWTABLE, NFT_MSG_DELFLOWTABLE, + NFT_MSG_GETRULE_RESET, NFT_MSG_MAX, }; diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c index 741a0e386406..80e613405f6f 100644 --- a/net/netfilter/nf_tables_api.c +++ b/net/netfilter/nf_tables_api.c @@ -2759,7 +2759,7 @@ static const struct nla_policy nft_expr_policy[NFTA_EXPR_MAX + 1] = { }; static int nf_tables_fill_expr_info(struct sk_buff *skb, - const struct nft_expr *expr) + const struct nft_expr *expr, bool reset) { if (nla_put_string(skb, NFTA_EXPR_NAME, expr->ops->type->name)) goto nla_put_failure; @@ -2769,7 +2769,7 @@ static int nf_tables_fill_expr_info(struct sk_buff *skb, NFTA_EXPR_DATA); if (data == NULL) goto nla_put_failure; - if (expr->ops->dump(skb, expr, false) < 0) + if (expr->ops->dump(skb, expr, reset) < 0) goto nla_put_failure; nla_nest_end(skb, data); } @@ -2781,14 +2781,14 @@ nla_put_failure: }; int nft_expr_dump(struct sk_buff *skb, unsigned int attr, - const struct nft_expr *expr) + const struct nft_expr *expr, bool reset) { struct nlattr *nest; nest = nla_nest_start_noflag(skb, attr); if (!nest) goto nla_put_failure; - if (nf_tables_fill_expr_info(skb, expr) < 0) + if (nf_tables_fill_expr_info(skb, expr, reset) < 0) goto nla_put_failure; nla_nest_end(skb, nest); return 0; @@ -3034,7 +3034,8 @@ static int nf_tables_fill_rule_info(struct sk_buff *skb, struct net *net, u32 flags, int family, const struct nft_table *table, const struct nft_chain *chain, - const struct nft_rule *rule, u64 handle) + const struct nft_rule *rule, u64 handle, + bool reset) { struct nlmsghdr *nlh; const struct nft_expr *expr, *next; @@ -3067,7 +3068,7 @@ static int nf_tables_fill_rule_info(struct sk_buff *skb, struct net *net, if (list == NULL) goto nla_put_failure; nft_rule_for_each_expr(expr, next, rule) { - if (nft_expr_dump(skb, NFTA_LIST_ELEM, expr) < 0) + if (nft_expr_dump(skb, NFTA_LIST_ELEM, expr, reset) < 0) goto nla_put_failure; } nla_nest_end(skb, list); @@ -3118,7 +3119,7 @@ static void nf_tables_rule_notify(const struct nft_ctx *ctx, err = nf_tables_fill_rule_info(skb, ctx->net, ctx->portid, ctx->seq, event, flags, ctx->family, ctx->table, - ctx->chain, rule, handle); + ctx->chain, rule, handle, false); if (err < 0) { kfree_skb(skb); goto err; @@ -3139,7 +3140,8 @@ static int __nf_tables_dump_rules(struct sk_buff *skb, unsigned int *idx, struct netlink_callback *cb, const struct nft_table *table, - const struct nft_chain *chain) + const struct nft_chain *chain, + bool reset) { struct net *net = sock_net(skb->sk); const struct nft_rule *rule, *prule; @@ -3166,7 +3168,7 @@ static int __nf_tables_dump_rules(struct sk_buff *skb, NFT_MSG_NEWRULE, NLM_F_MULTI | NLM_F_APPEND, table->family, - table, chain, rule, handle) < 0) + table, chain, rule, handle, reset) < 0) return 1; nl_dump_check_consistent(cb, nlmsg_hdr(skb)); @@ -3189,6 +3191,10 @@ static int nf_tables_dump_rules(struct sk_buff *skb, struct net *net = sock_net(skb->sk); int family = nfmsg->nfgen_family; struct nftables_pernet *nft_net; + bool reset = false; + + if (NFNL_MSG_TYPE(cb->nlh->nlmsg_type) == NFT_MSG_GETRULE_RESET) + reset = true; rcu_read_lock(); nft_net = nft_pernet(net); @@ -3213,14 +3219,15 @@ static int nf_tables_dump_rules(struct sk_buff *skb, if (!nft_is_active(net, chain)) continue; __nf_tables_dump_rules(skb, &idx, - cb, table, chain); + cb, table, chain, reset); break; } goto done; } list_for_each_entry_rcu(chain, &table->chains, list) { - if (__nf_tables_dump_rules(skb, &idx, cb, table, chain)) + if (__nf_tables_dump_rules(skb, &idx, + cb, table, chain, reset)) goto done; } @@ -3291,6 +3298,7 @@ static int nf_tables_getrule(struct sk_buff *skb, const struct nfnl_info *info, struct net *net = info->net; struct nft_table *table; struct sk_buff *skb2; + bool reset = false; int err; if (info->nlh->nlmsg_flags & NLM_F_DUMP) { @@ -3327,9 +3335,12 @@ static int nf_tables_getrule(struct sk_buff *skb, const struct nfnl_info *info, if (!skb2) return -ENOMEM; + if (NFNL_MSG_TYPE(info->nlh->nlmsg_type) == NFT_MSG_GETRULE_RESET) + reset = true; + err = nf_tables_fill_rule_info(skb2, net, NETLINK_CB(skb).portid, info->nlh->nlmsg_seq, NFT_MSG_NEWRULE, 0, - family, table, chain, rule, 0); + family, table, chain, rule, 0, reset); if (err < 0) goto err_fill_rule_info; @@ -4104,7 +4115,7 @@ static int nf_tables_fill_set(struct sk_buff *skb, const struct nft_ctx *ctx, if (set->num_exprs == 1) { nest = nla_nest_start_noflag(skb, NFTA_SET_EXPR); - if (nf_tables_fill_expr_info(skb, set->exprs[0]) < 0) + if (nf_tables_fill_expr_info(skb, set->exprs[0], false) < 0) goto nla_put_failure; nla_nest_end(skb, nest); @@ -4115,7 +4126,7 @@ static int nf_tables_fill_set(struct sk_buff *skb, const struct nft_ctx *ctx, for (i = 0; i < set->num_exprs; i++) { if (nft_expr_dump(skb, NFTA_LIST_ELEM, - set->exprs[i]) < 0) + set->exprs[i], false) < 0) goto nla_put_failure; } nla_nest_end(skb, nest); @@ -4946,7 +4957,7 @@ static int nft_set_elem_expr_dump(struct sk_buff *skb, if (num_exprs == 1) { expr = nft_setelem_expr_at(elem_expr, 0); - if (nft_expr_dump(skb, NFTA_SET_ELEM_EXPR, expr) < 0) + if (nft_expr_dump(skb, NFTA_SET_ELEM_EXPR, expr, false) < 0) return -1; return 0; @@ -4957,7 +4968,7 @@ static int nft_set_elem_expr_dump(struct sk_buff *skb, nft_setelem_expr_foreach(expr, elem_expr, size) { expr = nft_setelem_expr_at(elem_expr, size); - if (nft_expr_dump(skb, NFTA_LIST_ELEM, expr) < 0) + if (nft_expr_dump(skb, NFTA_LIST_ELEM, expr, false) < 0) goto nla_put_failure; } nla_nest_end(skb, nest); @@ -8311,6 +8322,12 @@ static const struct nfnl_callback nf_tables_cb[NFT_MSG_MAX] = { .attr_count = NFTA_RULE_MAX, .policy = nft_rule_policy, }, + [NFT_MSG_GETRULE_RESET] = { + .call = nf_tables_getrule, + .type = NFNL_CB_RCU, + .attr_count = NFTA_RULE_MAX, + .policy = nft_rule_policy, + }, [NFT_MSG_DELRULE] = { .call = nf_tables_delrule, .type = NFNL_CB_BATCH, diff --git a/net/netfilter/nft_counter.c b/net/netfilter/nft_counter.c index 06482fb9c145..dccc68a5135a 100644 --- a/net/netfilter/nft_counter.c +++ b/net/netfilter/nft_counter.c @@ -206,7 +206,7 @@ static int nft_counter_dump(struct sk_buff *skb, { struct nft_counter_percpu_priv *priv = nft_expr_priv(expr); - return nft_counter_do_dump(skb, priv, false); + return nft_counter_do_dump(skb, priv, reset); } static int nft_counter_init(const struct nft_ctx *ctx, diff --git a/net/netfilter/nft_dynset.c b/net/netfilter/nft_dynset.c index 01c61e090639..274579b1696e 100644 --- a/net/netfilter/nft_dynset.c +++ b/net/netfilter/nft_dynset.c @@ -380,7 +380,7 @@ static int nft_dynset_dump(struct sk_buff *skb, if (priv->set->num_exprs == 0) { if (priv->num_exprs == 1) { if (nft_expr_dump(skb, NFTA_DYNSET_EXPR, - priv->expr_array[0])) + priv->expr_array[0], reset)) goto nla_put_failure; } else if (priv->num_exprs > 1) { struct nlattr *nest; @@ -391,7 +391,7 @@ static int nft_dynset_dump(struct sk_buff *skb, for (i = 0; i < priv->num_exprs; i++) { if (nft_expr_dump(skb, NFTA_LIST_ELEM, - priv->expr_array[i])) + priv->expr_array[i], reset)) goto nla_put_failure; } nla_nest_end(skb, nest); diff --git a/net/netfilter/nft_inner.c b/net/netfilter/nft_inner.c index 6d96b826db4e..28e2873ba24e 100644 --- a/net/netfilter/nft_inner.c +++ b/net/netfilter/nft_inner.c @@ -359,7 +359,7 @@ static int nft_inner_dump(struct sk_buff *skb, goto nla_put_failure; if (nft_expr_dump(skb, NFTA_INNER_EXPR, - (struct nft_expr *)&priv->expr) < 0) + (struct nft_expr *)&priv->expr, reset) < 0) goto nla_put_failure; return 0; diff --git a/net/netfilter/nft_quota.c b/net/netfilter/nft_quota.c index b1a1217bca4c..123578e28917 100644 --- a/net/netfilter/nft_quota.c +++ b/net/netfilter/nft_quota.c @@ -222,7 +222,7 @@ static int nft_quota_dump(struct sk_buff *skb, { struct nft_quota *priv = nft_expr_priv(expr); - return nft_quota_do_dump(skb, priv, false); + return nft_quota_do_dump(skb, priv, reset); } static void nft_quota_destroy(const struct nft_ctx *ctx, From 971095c6fa4aebc0af923c32a28124871fe35136 Mon Sep 17 00:00:00 2001 From: Jiapeng Chong Date: Fri, 4 Nov 2022 11:55:04 +0800 Subject: [PATCH 0921/2002] netfilter: rpfilter/fib: clean up some inconsistent indenting No functional modification involved. net/ipv4/netfilter/nft_fib_ipv4.c:141 nft_fib4_eval() warn: inconsistent indenting. Link: https://bugzilla.openanolis.cn/show_bug.cgi?id=2733 Reported-by: Abaci Robot Signed-off-by: Jiapeng Chong Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- net/ipv4/netfilter/nft_fib_ipv4.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/net/ipv4/netfilter/nft_fib_ipv4.c b/net/ipv4/netfilter/nft_fib_ipv4.c index fc65d69f23e1..9eee535c64dd 100644 --- a/net/ipv4/netfilter/nft_fib_ipv4.c +++ b/net/ipv4/netfilter/nft_fib_ipv4.c @@ -138,12 +138,11 @@ void nft_fib4_eval(const struct nft_expr *expr, struct nft_regs *regs, break; } - if (!oif) { - found = FIB_RES_DEV(res); + if (!oif) { + found = FIB_RES_DEV(res); } else { if (!fib_info_nh_uses_dev(res.fi, oif)) return; - found = oif; } From d2c806abcf0b582131e1f93589d628dac0c07bf4 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Wed, 2 Nov 2022 13:46:33 +0100 Subject: [PATCH 0922/2002] netfilter: conntrack: use siphash_4u64 This function is used for every packet, siphash_4u64 is noticeably faster than using local buffer + siphash: Before: 1.23% kpktgend_0 [kernel.vmlinux] [k] __siphash_unaligned 0.14% kpktgend_0 [nf_conntrack] [k] hash_conntrack_raw After: 0.79% kpktgend_0 [kernel.vmlinux] [k] siphash_4u64 0.15% kpktgend_0 [nf_conntrack] [k] hash_conntrack_raw In the pktgen test this gives about ~2.4% performance improvement. Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- net/netfilter/nf_conntrack_core.c | 30 +++++++++++++----------------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c index f97bda06d2a9..057ebdcc25d7 100644 --- a/net/netfilter/nf_conntrack_core.c +++ b/net/netfilter/nf_conntrack_core.c @@ -211,28 +211,24 @@ static u32 hash_conntrack_raw(const struct nf_conntrack_tuple *tuple, unsigned int zoneid, const struct net *net) { - struct { - struct nf_conntrack_man src; - union nf_inet_addr dst_addr; - unsigned int zone; - u32 net_mix; - u16 dport; - u16 proto; - } __aligned(SIPHASH_ALIGNMENT) combined; + u64 a, b, c, d; get_random_once(&nf_conntrack_hash_rnd, sizeof(nf_conntrack_hash_rnd)); - memset(&combined, 0, sizeof(combined)); + /* The direction must be ignored, handle usable tuplehash members manually */ + a = (u64)tuple->src.u3.all[0] << 32 | tuple->src.u3.all[3]; + b = (u64)tuple->dst.u3.all[0] << 32 | tuple->dst.u3.all[3]; - /* The direction must be ignored, so handle usable members manually. */ - combined.src = tuple->src; - combined.dst_addr = tuple->dst.u3; - combined.zone = zoneid; - combined.net_mix = net_hash_mix(net); - combined.dport = (__force __u16)tuple->dst.u.all; - combined.proto = tuple->dst.protonum; + c = (__force u64)tuple->src.u.all << 32 | (__force u64)tuple->dst.u.all << 16; + c |= tuple->dst.protonum; - return (u32)siphash(&combined, sizeof(combined), &nf_conntrack_hash_rnd); + d = (u64)zoneid << 32 | net_hash_mix(net); + + /* IPv4: u3.all[1,2,3] == 0 */ + c ^= (u64)tuple->src.u3.all[1] << 32 | tuple->src.u3.all[2]; + d += (u64)tuple->dst.u3.all[1] << 32 | tuple->dst.u3.all[2]; + + return (u32)siphash_4u64(a, b, c, d, &nf_conntrack_hash_rnd); } static u32 scale_hash(u32 hash) From 7eba4505394e21df44dcace6b5d741a8e2deea3a Mon Sep 17 00:00:00 2001 From: Daniel Machon Date: Mon, 14 Nov 2022 10:29:50 +0100 Subject: [PATCH 0923/2002] net: dcb: move getapptrust to separate function This patch fixes a frame size warning, reported by kernel test robot. >> net/dcb/dcbnl.c:1230:1: warning: the frame size of 1244 bytes is >> larger than 1024 bytes [-Wframe-larger-than=] The getapptrust part of dcbnl_ieee_fill is moved to a separate function, and the selector array is now dynamically allocated, instead of stack allocated. Tested on microchip sparx5 driver. Fixes: 6182d5875c33 ("net: dcb: add new apptrust attribute") Reported-by: kernel test robot Signed-off-by: Daniel Machon Link: https://lore.kernel.org/r/20221114092950.2490451-1-daniel.machon@microchip.com Signed-off-by: Paolo Abeni --- net/dcb/dcbnl.c | 65 ++++++++++++++++++++++++++++++++----------------- 1 file changed, 43 insertions(+), 22 deletions(-) diff --git a/net/dcb/dcbnl.c b/net/dcb/dcbnl.c index cec0632f96db..f9949e051f49 100644 --- a/net/dcb/dcbnl.c +++ b/net/dcb/dcbnl.c @@ -1060,11 +1060,50 @@ nla_put_failure: return err; } +static int dcbnl_getapptrust(struct net_device *netdev, struct sk_buff *skb) +{ + const struct dcbnl_rtnl_ops *ops = netdev->dcbnl_ops; + enum ieee_attrs_app type; + struct nlattr *apptrust; + int nselectors, err, i; + u8 *selectors; + + selectors = kzalloc(IEEE_8021QAZ_APP_SEL_MAX + 1, GFP_KERNEL); + if (!selectors) + return -ENOMEM; + + err = ops->dcbnl_getapptrust(netdev, selectors, &nselectors); + if (err) { + err = 0; + goto out; + } + + apptrust = nla_nest_start(skb, DCB_ATTR_DCB_APP_TRUST_TABLE); + if (!apptrust) { + err = -EMSGSIZE; + goto out; + } + + for (i = 0; i < nselectors; i++) { + type = dcbnl_app_attr_type_get(selectors[i]); + err = nla_put_u8(skb, type, selectors[i]); + if (err) { + nla_nest_cancel(skb, apptrust); + goto out; + } + } + nla_nest_end(skb, apptrust); + +out: + kfree(selectors); + return err; +} + /* Handle IEEE 802.1Qaz/802.1Qau/802.1Qbb GET commands. */ static int dcbnl_ieee_fill(struct sk_buff *skb, struct net_device *netdev) { const struct dcbnl_rtnl_ops *ops = netdev->dcbnl_ops; - struct nlattr *ieee, *app, *apptrust; + struct nlattr *ieee, *app; struct dcb_app_type *itr; int dcbx; int err; @@ -1168,27 +1207,9 @@ static int dcbnl_ieee_fill(struct sk_buff *skb, struct net_device *netdev) nla_nest_end(skb, app); if (ops->dcbnl_getapptrust) { - u8 selectors[IEEE_8021QAZ_APP_SEL_MAX + 1] = {0}; - int nselectors, i; - - apptrust = nla_nest_start(skb, DCB_ATTR_DCB_APP_TRUST_TABLE); - if (!apptrust) - return -EMSGSIZE; - - err = ops->dcbnl_getapptrust(netdev, selectors, &nselectors); - if (!err) { - for (i = 0; i < nselectors; i++) { - enum ieee_attrs_app type = - dcbnl_app_attr_type_get(selectors[i]); - err = nla_put_u8(skb, type, selectors[i]); - if (err) { - nla_nest_cancel(skb, apptrust); - return err; - } - } - } - - nla_nest_end(skb, apptrust); + err = dcbnl_getapptrust(netdev, skb); + if (err) + return err; } /* get peer info if available */ From 26a9b433cf08adf2fdb50775128b283eb5201ab2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20M=C3=BCller?= Date: Mon, 14 Nov 2022 21:15:01 +0000 Subject: [PATCH 0924/2002] bpf/docs: Document how to run CI without patch submission MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This change documents the process for running the BPF CI before submitting a patch to the upstream mailing list, similar to what happens if a patch is send to bpf@vger.kernel.org: it builds kernel and selftests and runs the latter on different architecture (but it notably does not cover stylistic checks such as cover letter verification). Running BPF CI this way can help achieve better test coverage ahead of patch submission than merely running locally (say, using tools/testing/selftests/bpf/vmtest.sh), as additional architectures may be covered as well. Signed-off-by: Daniel Müller Signed-off-by: Daniel Borkmann Link: https://lore.kernel.org/bpf/20221114211501.2068684-1-deso@posteo.net --- Documentation/bpf/bpf_devel_QA.rst | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/Documentation/bpf/bpf_devel_QA.rst b/Documentation/bpf/bpf_devel_QA.rst index 761474bd7fe6..08572c75bdef 100644 --- a/Documentation/bpf/bpf_devel_QA.rst +++ b/Documentation/bpf/bpf_devel_QA.rst @@ -44,6 +44,30 @@ is a guarantee that the reported issue will be overlooked.** Submitting patches ================== +Q: How do I run BPF CI on my changes before sending them out for review? +------------------------------------------------------------------------ +A: BPF CI is GitHub based and hosted at https://github.com/kernel-patches/bpf. +While GitHub also provides a CLI that can be used to accomplish the same +results, here we focus on the UI based workflow. + +The following steps lay out how to start a CI run for your patches: +- Create a fork of the aforementioned repository in your own account (one time + action) +- Clone the fork locally, check out a new branch tracking either the bpf-next + or bpf branch, and apply your to-be-tested patches on top of it +- Push the local branch to your fork and create a pull request against + kernel-patches/bpf's bpf-next_base or bpf_base branch, respectively + +Shortly after the pull request has been created, the CI workflow will run. Note +that capacity is shared with patches submitted upstream being checked and so +depending on utilization the run can take a while to finish. + +Note furthermore that both base branches (bpf-next_base and bpf_base) will be +updated as patches are pushed to the respective upstream branches they track. As +such, your patch set will automatically (be attempted to) be rebased as well. +This behavior can result in a CI run being aborted and restarted with the new +base line. + Q: To which mailing list do I need to submit my BPF patches? ------------------------------------------------------------ A: Please submit your BPF patches to the bpf kernel mailing list: From 14d898f3c1b3bf9c4375ee3255ec9e9b89a35578 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Toke=20H=C3=B8iland-J=C3=B8rgensen?= Date: Tue, 8 Nov 2022 15:05:59 +0100 Subject: [PATCH 0925/2002] dev: Move received_rps counter next to RPS members in softnet data MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move the received_rps counter value next to the other RPS-related members in softnet_data. This closes two four-byte holes in the structure, making room for another pointer in the first two cache lines without bumping the xmit struct to its own line. Acked-by: Song Liu Reviewed-by: Stanislav Fomichev Signed-off-by: Toke Høiland-Jørgensen Link: https://lore.kernel.org/r/20221108140601.149971-2-toke@redhat.com Signed-off-by: Alexei Starovoitov --- include/linux/netdevice.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 02a2318da7c7..ddc59ef98500 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -3128,7 +3128,6 @@ struct softnet_data { /* stats */ unsigned int processed; unsigned int time_squeeze; - unsigned int received_rps; #ifdef CONFIG_RPS struct softnet_data *rps_ipi_list; #endif @@ -3161,6 +3160,7 @@ struct softnet_data { unsigned int cpu; unsigned int input_queue_tail; #endif + unsigned int received_rps; unsigned int dropped; struct sk_buff_head input_pkt_queue; struct napi_struct backlog; From 32637e33003f36e75e9147788cc0e2f21706ef99 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Toke=20H=C3=B8iland-J=C3=B8rgensen?= Date: Tue, 8 Nov 2022 15:06:00 +0100 Subject: [PATCH 0926/2002] bpf: Expand map key argument of bpf_redirect_map to u64 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For queueing packets in XDP we want to add a new redirect map type with support for 64-bit indexes. To prepare fore this, expand the width of the 'key' argument to the bpf_redirect_map() helper. Since BPF registers are always 64-bit, this should be safe to do after the fact. Acked-by: Song Liu Reviewed-by: Stanislav Fomichev Signed-off-by: Toke Høiland-Jørgensen Link: https://lore.kernel.org/r/20221108140601.149971-3-toke@redhat.com Signed-off-by: Alexei Starovoitov --- include/linux/bpf.h | 2 +- include/linux/filter.h | 12 ++++++------ include/uapi/linux/bpf.h | 2 +- kernel/bpf/cpumap.c | 4 ++-- kernel/bpf/devmap.c | 4 ++-- kernel/bpf/verifier.c | 2 +- net/core/filter.c | 4 ++-- net/xdp/xskmap.c | 4 ++-- 8 files changed, 17 insertions(+), 17 deletions(-) diff --git a/include/linux/bpf.h b/include/linux/bpf.h index 49f9d2bec401..54462dd28824 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -135,7 +135,7 @@ struct bpf_map_ops { struct bpf_local_storage __rcu ** (*map_owner_storage_ptr)(void *owner); /* Misc helpers.*/ - int (*map_redirect)(struct bpf_map *map, u32 ifindex, u64 flags); + int (*map_redirect)(struct bpf_map *map, u64 key, u64 flags); /* map_meta_equal must be implemented for maps that can be * used as an inner map. It is a runtime check to ensure diff --git a/include/linux/filter.h b/include/linux/filter.h index 787d35dbf5b0..bf701976056e 100644 --- a/include/linux/filter.h +++ b/include/linux/filter.h @@ -643,13 +643,13 @@ struct bpf_nh_params { }; struct bpf_redirect_info { - u32 flags; - u32 tgt_index; + u64 tgt_index; void *tgt_value; struct bpf_map *map; + u32 flags; + u32 kern_flags; u32 map_id; enum bpf_map_type map_type; - u32 kern_flags; struct bpf_nh_params nh; }; @@ -1504,7 +1504,7 @@ static inline bool bpf_sk_lookup_run_v6(struct net *net, int protocol, } #endif /* IS_ENABLED(CONFIG_IPV6) */ -static __always_inline int __bpf_xdp_redirect_map(struct bpf_map *map, u32 ifindex, +static __always_inline int __bpf_xdp_redirect_map(struct bpf_map *map, u64 index, u64 flags, const u64 flag_mask, void *lookup_elem(struct bpf_map *map, u32 key)) { @@ -1515,7 +1515,7 @@ static __always_inline int __bpf_xdp_redirect_map(struct bpf_map *map, u32 ifind if (unlikely(flags & ~(action_mask | flag_mask))) return XDP_ABORTED; - ri->tgt_value = lookup_elem(map, ifindex); + ri->tgt_value = lookup_elem(map, index); if (unlikely(!ri->tgt_value) && !(flags & BPF_F_BROADCAST)) { /* If the lookup fails we want to clear out the state in the * redirect_info struct completely, so that if an eBPF program @@ -1527,7 +1527,7 @@ static __always_inline int __bpf_xdp_redirect_map(struct bpf_map *map, u32 ifind return flags & action_mask; } - ri->tgt_index = ifindex; + ri->tgt_index = index; ri->map_id = map->id; ri->map_type = map->map_type; diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h index 6580448e9f77..ab86145df760 100644 --- a/include/uapi/linux/bpf.h +++ b/include/uapi/linux/bpf.h @@ -2647,7 +2647,7 @@ union bpf_attr { * Return * 0 on success, or a negative error in case of failure. * - * long bpf_redirect_map(struct bpf_map *map, u32 key, u64 flags) + * long bpf_redirect_map(struct bpf_map *map, u64 key, u64 flags) * Description * Redirect the packet to the endpoint referenced by *map* at * index *key*. Depending on its type, this *map* can contain diff --git a/kernel/bpf/cpumap.c b/kernel/bpf/cpumap.c index 6b6a78c04b90..e0b2d016f0bf 100644 --- a/kernel/bpf/cpumap.c +++ b/kernel/bpf/cpumap.c @@ -667,9 +667,9 @@ static int cpu_map_get_next_key(struct bpf_map *map, void *key, void *next_key) return 0; } -static int cpu_map_redirect(struct bpf_map *map, u32 ifindex, u64 flags) +static int cpu_map_redirect(struct bpf_map *map, u64 index, u64 flags) { - return __bpf_xdp_redirect_map(map, ifindex, flags, 0, + return __bpf_xdp_redirect_map(map, index, flags, 0, __cpu_map_lookup_elem); } diff --git a/kernel/bpf/devmap.c b/kernel/bpf/devmap.c index f9a87dcc5535..d01e4c55b376 100644 --- a/kernel/bpf/devmap.c +++ b/kernel/bpf/devmap.c @@ -992,14 +992,14 @@ static int dev_map_hash_update_elem(struct bpf_map *map, void *key, void *value, map, key, value, map_flags); } -static int dev_map_redirect(struct bpf_map *map, u32 ifindex, u64 flags) +static int dev_map_redirect(struct bpf_map *map, u64 ifindex, u64 flags) { return __bpf_xdp_redirect_map(map, ifindex, flags, BPF_F_BROADCAST | BPF_F_EXCLUDE_INGRESS, __dev_map_lookup_elem); } -static int dev_hash_map_redirect(struct bpf_map *map, u32 ifindex, u64 flags) +static int dev_hash_map_redirect(struct bpf_map *map, u64 ifindex, u64 flags) { return __bpf_xdp_redirect_map(map, ifindex, flags, BPF_F_BROADCAST | BPF_F_EXCLUDE_INGRESS, diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 5e74f460dfd0..be24774961ab 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -14384,7 +14384,7 @@ static int do_misc_fixups(struct bpf_verifier_env *env) BUILD_BUG_ON(!__same_type(ops->map_peek_elem, (int (*)(struct bpf_map *map, void *value))NULL)); BUILD_BUG_ON(!__same_type(ops->map_redirect, - (int (*)(struct bpf_map *map, u32 ifindex, u64 flags))NULL)); + (int (*)(struct bpf_map *map, u64 index, u64 flags))NULL)); BUILD_BUG_ON(!__same_type(ops->map_for_each_callback, (int (*)(struct bpf_map *map, bpf_callback_t callback_fn, diff --git a/net/core/filter.c b/net/core/filter.c index 37fad5a9b752..754dd01354d8 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -4414,10 +4414,10 @@ static const struct bpf_func_proto bpf_xdp_redirect_proto = { .arg2_type = ARG_ANYTHING, }; -BPF_CALL_3(bpf_xdp_redirect_map, struct bpf_map *, map, u32, ifindex, +BPF_CALL_3(bpf_xdp_redirect_map, struct bpf_map *, map, u64, key, u64, flags) { - return map->ops->map_redirect(map, ifindex, flags); + return map->ops->map_redirect(map, key, flags); } static const struct bpf_func_proto bpf_xdp_redirect_map_proto = { diff --git a/net/xdp/xskmap.c b/net/xdp/xskmap.c index acc8e52a4f5f..771d0fa90ef5 100644 --- a/net/xdp/xskmap.c +++ b/net/xdp/xskmap.c @@ -231,9 +231,9 @@ static int xsk_map_delete_elem(struct bpf_map *map, void *key) return 0; } -static int xsk_map_redirect(struct bpf_map *map, u32 ifindex, u64 flags) +static int xsk_map_redirect(struct bpf_map *map, u64 index, u64 flags) { - return __bpf_xdp_redirect_map(map, ifindex, flags, 0, + return __bpf_xdp_redirect_map(map, index, flags, 0, __xsk_map_lookup_elem); } From befae75856ab406a3f3fab2aa2118cf3b2dfe3e6 Mon Sep 17 00:00:00 2001 From: Eduard Zingerman Date: Wed, 16 Nov 2022 00:48:58 +0200 Subject: [PATCH 0927/2002] bpf: propagate nullness information for reg to reg comparisons Propagate nullness information for branches of register to register equality compare instructions. The following rules are used: - suppose register A maybe null - suppose register B is not null - for JNE A, B, ... - A is not null in the false branch - for JEQ A, B, ... - A is not null in the true branch E.g. for program like below: r6 = skb->sk; r7 = sk_fullsock(r6); r0 = sk_fullsock(r6); if (r0 == 0) return 0; (a) if (r0 != r7) return 0; (b) *r7->type; (c) return 0; It is safe to dereference r7 at point (c), because of (a) and (b). Signed-off-by: Eduard Zingerman Acked-by: Yonghong Song Link: https://lore.kernel.org/r/20221115224859.2452988-2-eddyz87@gmail.com Signed-off-by: Alexei Starovoitov --- kernel/bpf/verifier.c | 35 +++++++++++++++++++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index be24774961ab..0312d9ce292f 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -10267,6 +10267,7 @@ static int check_cond_jmp_op(struct bpf_verifier_env *env, struct bpf_verifier_state *other_branch; struct bpf_reg_state *regs = this_branch->frame[this_branch->curframe]->regs; struct bpf_reg_state *dst_reg, *other_branch_regs, *src_reg = NULL; + struct bpf_reg_state *eq_branch_regs; u8 opcode = BPF_OP(insn->code); bool is_jmp32; int pred = -1; @@ -10376,8 +10377,8 @@ static int check_cond_jmp_op(struct bpf_verifier_env *env, /* detect if we are comparing against a constant value so we can adjust * our min/max values for our dst register. * this is only legit if both are scalars (or pointers to the same - * object, I suppose, but we don't support that right now), because - * otherwise the different base pointers mean the offsets aren't + * object, I suppose, see the PTR_MAYBE_NULL related if block below), + * because otherwise the different base pointers mean the offsets aren't * comparable. */ if (BPF_SRC(insn->code) == BPF_X) { @@ -10426,6 +10427,36 @@ static int check_cond_jmp_op(struct bpf_verifier_env *env, find_equal_scalars(other_branch, &other_branch_regs[insn->dst_reg]); } + /* if one pointer register is compared to another pointer + * register check if PTR_MAYBE_NULL could be lifted. + * E.g. register A - maybe null + * register B - not null + * for JNE A, B, ... - A is not null in the false branch; + * for JEQ A, B, ... - A is not null in the true branch. + */ + if (!is_jmp32 && BPF_SRC(insn->code) == BPF_X && + __is_pointer_value(false, src_reg) && __is_pointer_value(false, dst_reg) && + type_may_be_null(src_reg->type) != type_may_be_null(dst_reg->type)) { + eq_branch_regs = NULL; + switch (opcode) { + case BPF_JEQ: + eq_branch_regs = other_branch_regs; + break; + case BPF_JNE: + eq_branch_regs = regs; + break; + default: + /* do nothing */ + break; + } + if (eq_branch_regs) { + if (type_may_be_null(src_reg->type)) + mark_ptr_not_null_reg(&eq_branch_regs[insn->src_reg]); + else + mark_ptr_not_null_reg(&eq_branch_regs[insn->dst_reg]); + } + } + /* detect if R == 0 where R is returned from bpf_map_lookup_elem(). * NOTE: these optimizations below are related with pointer comparison * which will never be JMP32. From 4741c371aa088a951bd65f995d2dfe7cd29b4b9a Mon Sep 17 00:00:00 2001 From: Eduard Zingerman Date: Wed, 16 Nov 2022 00:48:59 +0200 Subject: [PATCH 0928/2002] selftests/bpf: check nullness propagation for reg to reg comparisons Verify that nullness information is porpagated in the branches of register to register JEQ and JNE operations. Signed-off-by: Eduard Zingerman Acked-by: Yonghong Song Link: https://lore.kernel.org/r/20221115224859.2452988-3-eddyz87@gmail.com Signed-off-by: Alexei Starovoitov --- .../bpf/verifier/jeq_infer_not_null.c | 174 ++++++++++++++++++ 1 file changed, 174 insertions(+) create mode 100644 tools/testing/selftests/bpf/verifier/jeq_infer_not_null.c diff --git a/tools/testing/selftests/bpf/verifier/jeq_infer_not_null.c b/tools/testing/selftests/bpf/verifier/jeq_infer_not_null.c new file mode 100644 index 000000000000..67a1c07ead34 --- /dev/null +++ b/tools/testing/selftests/bpf/verifier/jeq_infer_not_null.c @@ -0,0 +1,174 @@ +{ + /* This is equivalent to the following program: + * + * r6 = skb->sk; + * r7 = sk_fullsock(r6); + * r0 = sk_fullsock(r6); + * if (r0 == 0) return 0; (a) + * if (r0 != r7) return 0; (b) + * *r7->type; (c) + * return 0; + * + * It is safe to dereference r7 at point (c), because of (a) and (b). + * The test verifies that relation r0 == r7 is propagated from (b) to (c). + */ + "jne/jeq infer not null, PTR_TO_SOCKET_OR_NULL -> PTR_TO_SOCKET for JNE false branch", + .insns = { + /* r6 = skb->sk; */ + BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1, offsetof(struct __sk_buff, sk)), + /* if (r6 == 0) return 0; */ + BPF_JMP_IMM(BPF_JEQ, BPF_REG_6, 0, 8), + /* r7 = sk_fullsock(skb); */ + BPF_MOV64_REG(BPF_REG_1, BPF_REG_6), + BPF_EMIT_CALL(BPF_FUNC_sk_fullsock), + BPF_MOV64_REG(BPF_REG_7, BPF_REG_0), + /* r0 = sk_fullsock(skb); */ + BPF_MOV64_REG(BPF_REG_1, BPF_REG_6), + BPF_EMIT_CALL(BPF_FUNC_sk_fullsock), + /* if (r0 == null) return 0; */ + BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 2), + /* if (r0 == r7) r0 = *(r7->type); */ + BPF_JMP_REG(BPF_JNE, BPF_REG_0, BPF_REG_7, 1), /* Use ! JNE ! */ + BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_7, offsetof(struct bpf_sock, type)), + /* return 0 */ + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + }, + .prog_type = BPF_PROG_TYPE_CGROUP_SKB, + .result = ACCEPT, + .result_unpriv = REJECT, + .errstr_unpriv = "R7 pointer comparison", +}, +{ + /* Same as above, but verify that another branch of JNE still + * prohibits access to PTR_MAYBE_NULL. + */ + "jne/jeq infer not null, PTR_TO_SOCKET_OR_NULL unchanged for JNE true branch", + .insns = { + /* r6 = skb->sk */ + BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1, offsetof(struct __sk_buff, sk)), + /* if (r6 == 0) return 0; */ + BPF_JMP_IMM(BPF_JEQ, BPF_REG_6, 0, 9), + /* r7 = sk_fullsock(skb); */ + BPF_MOV64_REG(BPF_REG_1, BPF_REG_6), + BPF_EMIT_CALL(BPF_FUNC_sk_fullsock), + BPF_MOV64_REG(BPF_REG_7, BPF_REG_0), + /* r0 = sk_fullsock(skb); */ + BPF_MOV64_REG(BPF_REG_1, BPF_REG_6), + BPF_EMIT_CALL(BPF_FUNC_sk_fullsock), + /* if (r0 == null) return 0; */ + BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 3), + /* if (r0 == r7) return 0; */ + BPF_JMP_REG(BPF_JNE, BPF_REG_0, BPF_REG_7, 1), /* Use ! JNE ! */ + BPF_JMP_IMM(BPF_JA, 0, 0, 1), + /* r0 = *(r7->type); */ + BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_7, offsetof(struct bpf_sock, type)), + /* return 0 */ + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + }, + .prog_type = BPF_PROG_TYPE_CGROUP_SKB, + .result = REJECT, + .errstr = "R7 invalid mem access 'sock_or_null'", + .result_unpriv = REJECT, + .errstr_unpriv = "R7 pointer comparison", +}, +{ + /* Same as a first test, but not null should be inferred for JEQ branch */ + "jne/jeq infer not null, PTR_TO_SOCKET_OR_NULL -> PTR_TO_SOCKET for JEQ true branch", + .insns = { + /* r6 = skb->sk; */ + BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1, offsetof(struct __sk_buff, sk)), + /* if (r6 == null) return 0; */ + BPF_JMP_IMM(BPF_JEQ, BPF_REG_6, 0, 9), + /* r7 = sk_fullsock(skb); */ + BPF_MOV64_REG(BPF_REG_1, BPF_REG_6), + BPF_EMIT_CALL(BPF_FUNC_sk_fullsock), + BPF_MOV64_REG(BPF_REG_7, BPF_REG_0), + /* r0 = sk_fullsock(skb); */ + BPF_MOV64_REG(BPF_REG_1, BPF_REG_6), + BPF_EMIT_CALL(BPF_FUNC_sk_fullsock), + /* if (r0 == null) return 0; */ + BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 3), + /* if (r0 != r7) return 0; */ + BPF_JMP_REG(BPF_JEQ, BPF_REG_0, BPF_REG_7, 1), /* Use ! JEQ ! */ + BPF_JMP_IMM(BPF_JA, 0, 0, 1), + /* r0 = *(r7->type); */ + BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_7, offsetof(struct bpf_sock, type)), + /* return 0; */ + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + }, + .prog_type = BPF_PROG_TYPE_CGROUP_SKB, + .result = ACCEPT, + .result_unpriv = REJECT, + .errstr_unpriv = "R7 pointer comparison", +}, +{ + /* Same as above, but verify that another branch of JNE still + * prohibits access to PTR_MAYBE_NULL. + */ + "jne/jeq infer not null, PTR_TO_SOCKET_OR_NULL unchanged for JEQ false branch", + .insns = { + /* r6 = skb->sk; */ + BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1, offsetof(struct __sk_buff, sk)), + /* if (r6 == null) return 0; */ + BPF_JMP_IMM(BPF_JEQ, BPF_REG_6, 0, 8), + /* r7 = sk_fullsock(skb); */ + BPF_MOV64_REG(BPF_REG_1, BPF_REG_6), + BPF_EMIT_CALL(BPF_FUNC_sk_fullsock), + BPF_MOV64_REG(BPF_REG_7, BPF_REG_0), + /* r0 = sk_fullsock(skb); */ + BPF_MOV64_REG(BPF_REG_1, BPF_REG_6), + BPF_EMIT_CALL(BPF_FUNC_sk_fullsock), + /* if (r0 == null) return 0; */ + BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 2), + /* if (r0 != r7) r0 = *(r7->type); */ + BPF_JMP_REG(BPF_JEQ, BPF_REG_0, BPF_REG_7, 1), /* Use ! JEQ ! */ + BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_7, offsetof(struct bpf_sock, type)), + /* return 0; */ + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + }, + .prog_type = BPF_PROG_TYPE_CGROUP_SKB, + .result = REJECT, + .errstr = "R7 invalid mem access 'sock_or_null'", + .result_unpriv = REJECT, + .errstr_unpriv = "R7 pointer comparison", +}, +{ + /* Maps are treated in a different branch of `mark_ptr_not_null_reg`, + * so separate test for maps case. + */ + "jne/jeq infer not null, PTR_TO_MAP_VALUE_OR_NULL -> PTR_TO_MAP_VALUE", + .insns = { + /* r9 = &some stack to use as key */ + BPF_ST_MEM(BPF_W, BPF_REG_10, -8, 0), + BPF_MOV64_REG(BPF_REG_9, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_9, -8), + /* r8 = process local map */ + BPF_LD_MAP_FD(BPF_REG_8, 0), + /* r6 = map_lookup_elem(r8, r9); */ + BPF_MOV64_REG(BPF_REG_1, BPF_REG_8), + BPF_MOV64_REG(BPF_REG_2, BPF_REG_9), + BPF_EMIT_CALL(BPF_FUNC_map_lookup_elem), + BPF_MOV64_REG(BPF_REG_6, BPF_REG_0), + /* r7 = map_lookup_elem(r8, r9); */ + BPF_MOV64_REG(BPF_REG_1, BPF_REG_8), + BPF_MOV64_REG(BPF_REG_2, BPF_REG_9), + BPF_EMIT_CALL(BPF_FUNC_map_lookup_elem), + BPF_MOV64_REG(BPF_REG_7, BPF_REG_0), + /* if (r6 == 0) return 0; */ + BPF_JMP_IMM(BPF_JEQ, BPF_REG_6, 0, 2), + /* if (r6 != r7) return 0; */ + BPF_JMP_REG(BPF_JNE, BPF_REG_6, BPF_REG_7, 1), + /* read *r7; */ + BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_7, offsetof(struct bpf_xdp_sock, queue_id)), + /* return 0; */ + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + }, + .fixup_map_xskmap = { 3 }, + .prog_type = BPF_PROG_TYPE_XDP, + .result = ACCEPT, +}, From 570d0a588dfbb11f9ef80ed933d0403a0e995688 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Mon, 14 Nov 2022 13:42:11 +0100 Subject: [PATCH 0929/2002] net: dsa: add support for DSA rx offloading via metadata dst If a metadata dst is present with the type METADATA_HW_PORT_MUX on a dsa cpu port netdev, assume that it carries the port number and that there is no DSA tag present in the skb data. Signed-off-by: Felix Fietkau Reviewed-by: Vladimir Oltean Reviewed-by: Florian Fainelli Signed-off-by: Jakub Kicinski --- net/core/flow_dissector.c | 4 +++- net/dsa/dsa.c | 19 ++++++++++++++++++- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/net/core/flow_dissector.c b/net/core/flow_dissector.c index 25cd35f5922e..3e81798ed3e0 100644 --- a/net/core/flow_dissector.c +++ b/net/core/flow_dissector.c @@ -971,12 +971,14 @@ bool __skb_flow_dissect(const struct net *net, #if IS_ENABLED(CONFIG_NET_DSA) if (unlikely(skb->dev && netdev_uses_dsa(skb->dev) && proto == htons(ETH_P_XDSA))) { + struct metadata_dst *md_dst = skb_metadata_dst(skb); const struct dsa_device_ops *ops; int offset = 0; ops = skb->dev->dsa_ptr->tag_ops; /* Only DSA header taggers break flow dissection */ - if (ops->needed_headroom) { + if (ops->needed_headroom && + (!md_dst || md_dst->type != METADATA_HW_PORT_MUX)) { if (ops->flow_dissect) ops->flow_dissect(skb, &proto, &offset); else diff --git a/net/dsa/dsa.c b/net/dsa/dsa.c index 64b14f655b23..6caf2ec648fd 100644 --- a/net/dsa/dsa.c +++ b/net/dsa/dsa.c @@ -11,6 +11,7 @@ #include #include #include +#include #include "dsa_priv.h" @@ -216,6 +217,7 @@ static bool dsa_skb_defer_rx_timestamp(struct dsa_slave_priv *p, static int dsa_switch_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt, struct net_device *unused) { + struct metadata_dst *md_dst = skb_metadata_dst(skb); struct dsa_port *cpu_dp = dev->dsa_ptr; struct sk_buff *nskb = NULL; struct dsa_slave_priv *p; @@ -229,7 +231,22 @@ static int dsa_switch_rcv(struct sk_buff *skb, struct net_device *dev, if (!skb) return 0; - nskb = cpu_dp->rcv(skb, dev); + if (md_dst && md_dst->type == METADATA_HW_PORT_MUX) { + unsigned int port = md_dst->u.port_info.port_id; + + skb_dst_drop(skb); + if (!skb_has_extensions(skb)) + skb->slow_gro = 0; + + skb->dev = dsa_master_find_slave(dev, 0, port); + if (likely(skb->dev)) { + dsa_default_offload_fwd_mark(skb); + nskb = skb; + } + } else { + nskb = cpu_dp->rcv(skb, dev); + } + if (!nskb) { kfree_skb(skb); return 0; From 190487031584a1d24c92e0d82717a30169ac8929 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Mon, 14 Nov 2022 13:42:12 +0100 Subject: [PATCH 0930/2002] net: ethernet: mtk_eth_soc: pass correct VLAN protocol ID to the network stack Use the id from the DMA descriptor instead of hardcoding 802.1q Signed-off-by: Felix Fietkau Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/mediatek/mtk_eth_soc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c index 1cf76fd7afbc..891dd6937f89 100644 --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c @@ -1936,7 +1936,7 @@ static int mtk_poll_rx(struct napi_struct *napi, int budget, htons(RX_DMA_VPID(trxd.rxd4)), RX_DMA_VID(trxd.rxd4)); } else if (trxd.rxd2 & RX_DMA_VTAG) { - __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), + __vlan_hwaccel_put_tag(skb, htons(RX_DMA_VPID(trxd.rxd3)), RX_DMA_VID(trxd.rxd3)); } From 08666cbb7dd5ab1b5143c6c8d9b359b99caadd81 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Mon, 14 Nov 2022 13:42:13 +0100 Subject: [PATCH 0931/2002] net: ethernet: mtk_eth_soc: add support for configuring vlan rx offload Keep the vlan rx offload feature in sync across all netdevs belonging to the device, since the feature is global and can't be turned off per MAC Signed-off-by: Felix Fietkau Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/mediatek/mtk_eth_soc.c | 27 ++++++++++++++++----- 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c index 891dd6937f89..a118c715b09b 100644 --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c @@ -2724,15 +2724,30 @@ static netdev_features_t mtk_fix_features(struct net_device *dev, static int mtk_set_features(struct net_device *dev, netdev_features_t features) { - int err = 0; + struct mtk_mac *mac = netdev_priv(dev); + struct mtk_eth *eth = mac->hw; + netdev_features_t diff = dev->features ^ features; + int i; - if (!((dev->features ^ features) & NETIF_F_LRO)) - return 0; - - if (!(features & NETIF_F_LRO)) + if ((diff & NETIF_F_LRO) && !(features & NETIF_F_LRO)) mtk_hwlro_netdev_disable(dev); - return err; + /* Set RX VLAN offloading */ + if (!(diff & NETIF_F_HW_VLAN_CTAG_RX)) + return 0; + + mtk_w32(eth, !!(features & NETIF_F_HW_VLAN_CTAG_RX), + MTK_CDMP_EG_CTRL); + + /* sync features with other MAC */ + for (i = 0; i < MTK_MAC_COUNT; i++) { + if (!eth->netdev[i] || eth->netdev[i] == dev) + continue; + eth->netdev[i]->features &= ~NETIF_F_HW_VLAN_CTAG_RX; + eth->netdev[i]->features |= features & NETIF_F_HW_VLAN_CTAG_RX; + } + + return 0; } /* wait for DMA to finish whatever it is doing before we start using it again */ From 2d7605a729062bb554f03c5983d8cfb8c0b42e9c Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Mon, 14 Nov 2022 13:42:14 +0100 Subject: [PATCH 0932/2002] net: ethernet: mtk_eth_soc: enable hardware DSA untagging - pass the tag to DSA via metadata dst - disabled on 7986 for now, since it's not working yet - disabled if a MAC is enabled that does not use DSA This improves performance by bypassing the DSA tag driver and avoiding extra skb data mangling Signed-off-by: Felix Fietkau Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/mediatek/mtk_eth_soc.c | 66 ++++++++++++++++++--- drivers/net/ethernet/mediatek/mtk_eth_soc.h | 8 +++ 2 files changed, 67 insertions(+), 7 deletions(-) diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c index a118c715b09b..3e9f5536f0f3 100644 --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c @@ -23,6 +23,7 @@ #include #include #include +#include #include "mtk_eth_soc.h" #include "mtk_wed.h" @@ -1939,13 +1940,19 @@ static int mtk_poll_rx(struct napi_struct *napi, int budget, __vlan_hwaccel_put_tag(skb, htons(RX_DMA_VPID(trxd.rxd3)), RX_DMA_VID(trxd.rxd3)); } + } - /* If the device is attached to a dsa switch, the special - * tag inserted in VLAN field by hw switch can * be offloaded - * by RX HW VLAN offload. Clear vlan info. - */ - if (netdev_uses_dsa(netdev)) - __vlan_hwaccel_clear_tag(skb); + /* When using VLAN untagging in combination with DSA, the + * hardware treats the MTK special tag as a VLAN and untags it. + */ + if (skb_vlan_tag_present(skb) && netdev_uses_dsa(netdev)) { + unsigned int port = ntohs(skb->vlan_proto) & GENMASK(2, 0); + + if (port < ARRAY_SIZE(eth->dsa_meta) && + eth->dsa_meta[port]) + skb_dst_set_noref(skb, ð->dsa_meta[port]->dst); + + __vlan_hwaccel_clear_tag(skb); } skb_record_rx_queue(skb, 0); @@ -2990,11 +2997,46 @@ static void mtk_gdm_config(struct mtk_eth *eth, u32 config) mtk_w32(eth, 0, MTK_RST_GL); } + +static bool mtk_uses_dsa(struct net_device *dev) +{ +#if IS_ENABLED(CONFIG_NET_DSA) + return netdev_uses_dsa(dev) && + dev->dsa_ptr->tag_ops->proto == DSA_TAG_PROTO_MTK; +#else + return false; +#endif +} + static int mtk_open(struct net_device *dev) { struct mtk_mac *mac = netdev_priv(dev); struct mtk_eth *eth = mac->hw; - int err; + int i, err; + + if (mtk_uses_dsa(dev) && !eth->prog) { + for (i = 0; i < ARRAY_SIZE(eth->dsa_meta); i++) { + struct metadata_dst *md_dst = eth->dsa_meta[i]; + + if (md_dst) + continue; + + md_dst = metadata_dst_alloc(0, METADATA_HW_PORT_MUX, + GFP_KERNEL); + if (!md_dst) + return -ENOMEM; + + md_dst->u.port_info.port_id = i; + eth->dsa_meta[i] = md_dst; + } + } else { + /* Hardware special tag parsing needs to be disabled if at least + * one MAC does not use DSA. + */ + u32 val = mtk_r32(eth, MTK_CDMP_IG_CTRL); + val &= ~MTK_CDMP_STAG_EN; + mtk_w32(eth, val, MTK_CDMP_IG_CTRL); + } err = phylink_of_phy_connect(mac->phylink, mac->of_node, 0); if (err) { @@ -3321,6 +3363,10 @@ static int mtk_hw_init(struct mtk_eth *eth) */ val = mtk_r32(eth, MTK_CDMQ_IG_CTRL); mtk_w32(eth, val | MTK_CDMQ_STAG_EN, MTK_CDMQ_IG_CTRL); + if (!MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) { + val = mtk_r32(eth, MTK_CDMP_IG_CTRL); + mtk_w32(eth, val | MTK_CDMP_STAG_EN, MTK_CDMP_IG_CTRL); + } /* Enable RX VLan Offloading */ mtk_w32(eth, 1, MTK_CDMP_EG_CTRL); @@ -3538,6 +3584,12 @@ static int mtk_free_dev(struct mtk_eth *eth) free_netdev(eth->netdev[i]); } + for (i = 0; i < ARRAY_SIZE(eth->dsa_meta); i++) { + if (!eth->dsa_meta[i]) + break; + metadata_dst_free(eth->dsa_meta[i]); + } + return 0; } diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.h b/drivers/net/ethernet/mediatek/mtk_eth_soc.h index 589f27ddc401..a572416e25de 100644 --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h @@ -22,6 +22,9 @@ #include #include "mtk_ppe.h" +#define MTK_MAX_DSA_PORTS 7 +#define MTK_DSA_PORT_MASK GENMASK(2, 0) + #define MTK_QDMA_PAGE_SIZE 2048 #define MTK_MAX_RX_LENGTH 1536 #define MTK_MAX_RX_LENGTH_2K 2048 @@ -91,6 +94,9 @@ #define MTK_CDMQ_IG_CTRL 0x1400 #define MTK_CDMQ_STAG_EN BIT(0) +/* CDMQ Exgress Control Register */ +#define MTK_CDMQ_EG_CTRL 0x1404 + /* CDMP Ingress Control Register */ #define MTK_CDMP_IG_CTRL 0x400 #define MTK_CDMP_STAG_EN BIT(0) @@ -1121,6 +1127,8 @@ struct mtk_eth { int ip_align; + struct metadata_dst *dsa_meta[MTK_MAX_DSA_PORTS]; + struct mtk_ppe *ppe[2]; struct rhashtable flow_table; From 1ec6360ddb83a753b0eb8505fc957e3d7d74f785 Mon Sep 17 00:00:00 2001 From: Walter Heymans Date: Tue, 15 Nov 2022 10:08:34 +0100 Subject: [PATCH 0933/2002] Documentation: nfp: update documentation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The NFP documentation is updated to include information about Corigine, and the new NFP3800 chips. The 'Acquiring Firmware' section is updated with new information about where to find firmware. Two new sections are added to expand the coverage of the documentation. The new sections include: - Devlink Info - Configure Device Signed-off-by: Walter Heymans Reviewed-by: Niklas Söderlund Reviewed-by: Louis Peens Signed-off-by: Simon Horman Link: https://lore.kernel.org/r/20221115090834.738645-1-simon.horman@corigine.com Signed-off-by: Jakub Kicinski --- .../device_drivers/ethernet/netronome/nfp.rst | 165 +++++++++++++++--- 1 file changed, 145 insertions(+), 20 deletions(-) diff --git a/Documentation/networking/device_drivers/ethernet/netronome/nfp.rst b/Documentation/networking/device_drivers/ethernet/netronome/nfp.rst index ada611fb427c..650b57742d51 100644 --- a/Documentation/networking/device_drivers/ethernet/netronome/nfp.rst +++ b/Documentation/networking/device_drivers/ethernet/netronome/nfp.rst @@ -1,50 +1,57 @@ .. SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +.. include:: -============================================= -Netronome Flow Processor (NFP) Kernel Drivers -============================================= +=========================================== +Network Flow Processor (NFP) Kernel Drivers +=========================================== -Copyright (c) 2019, Netronome Systems, Inc. +:Copyright: |copy| 2019, Netronome Systems, Inc. +:Copyright: |copy| 2022, Corigine, Inc. Contents ======== - `Overview`_ - `Acquiring Firmware`_ +- `Devlink Info`_ +- `Configure Device`_ +- `Statistics`_ Overview ======== -This driver supports Netronome's line of Flow Processor devices, -including the NFP4000, NFP5000, and NFP6000 models, which are also -incorporated in the company's family of Agilio SmartNICs. The SR-IOV -physical and virtual functions for these devices are supported by -the driver. +This driver supports Netronome and Corigine's line of Network Flow Processor +devices, including the NFP3800, NFP4000, NFP5000, and NFP6000 models, which +are also incorporated in the companies' family of Agilio SmartNICs. The SR-IOV +physical and virtual functions for these devices are supported by the driver. Acquiring Firmware ================== -The NFP4000 and NFP6000 devices require application specific firmware -to function. Application firmware can be located either on the host file system +The NFP3800, NFP4000 and NFP6000 devices require application specific firmware +to function. Application firmware can be located either on the host file system or in the device flash (if supported by management firmware). Firmware files on the host filesystem contain card type (`AMDA-*` string), media -config etc. They should be placed in `/lib/firmware/netronome` directory to +config etc. They should be placed in `/lib/firmware/netronome` directory to load firmware from the host file system. Firmware for basic NIC operation is available in the upstream `linux-firmware.git` repository. +A more comprehensive list of firmware can be downloaded from the +`Corigine Support site `_. + Firmware in NVRAM ----------------- Recent versions of management firmware supports loading application -firmware from flash when the host driver gets probed. The firmware loading +firmware from flash when the host driver gets probed. The firmware loading policy configuration may be used to configure this feature appropriately. Devlink or ethtool can be used to update the application firmware on the device flash by providing the appropriate `nic_AMDA*.nffw` file to the respective -command. Users need to take care to write the correct firmware image for the +command. Users need to take care to write the correct firmware image for the card and media configuration to flash. Available storage space in flash depends on the card being used. @@ -79,9 +86,9 @@ You may need to use hard instead of symbolic links on distributions which use old `mkinitrd` command instead of `dracut` (e.g. Ubuntu). After changing firmware files you may need to regenerate the initramfs -image. Initramfs contains drivers and firmware files your system may -need to boot. Refer to the documentation of your distribution to find -out how to update initramfs. Good indication of stale initramfs +image. Initramfs contains drivers and firmware files your system may +need to boot. Refer to the documentation of your distribution to find +out how to update initramfs. Good indication of stale initramfs is system loading wrong driver or firmware on boot, but when driver is later reloaded manually everything works correctly. @@ -89,9 +96,9 @@ Selecting firmware per device ----------------------------- Most commonly all cards on the system use the same type of firmware. -If you want to load specific firmware image for a specific card, you -can use either the PCI bus address or serial number. Driver will print -which files it's looking for when it recognizes a NFP device:: +If you want to load a specific firmware image for a specific card, you +can use either the PCI bus address or serial number. The driver will +print which files it's looking for when it recognizes a NFP device:: nfp: Looking for firmware file in order of priority: nfp: netronome/serial-00-12-34-aa-bb-cc-10-ff.nffw: not found @@ -106,6 +113,15 @@ Note that `serial-*` and `pci-*` files are **not** automatically included in initramfs, you will have to refer to documentation of appropriate tools to find out how to include them. +Running firmware version +------------------------ + +The version of the loaded firmware for a particular interface, +(e.g. enp4s0), or an interface's port (e.g. enp4s0np0) can +be displayed with the ethtool command:: + + $ ethtool -i + Firmware loading policy ----------------------- @@ -132,6 +148,115 @@ abi_drv_load_ifc Defines a list of PF devices allowed to load FW on the device. This variable is not currently user configurable. +Devlink Info +============ + +The devlink info command displays the running and stored firmware versions +on the device, serial number and board information. + +Devlink info command example (replace PCI address):: + + $ devlink dev info pci/0000:03:00.0 + pci/0000:03:00.0: + driver nfp + serial_number CSAAMDA2001-1003000111 + versions: + fixed: + board.id AMDA2001-1003 + board.rev 01 + board.manufacture CSA + board.model mozart + running: + fw.mgmt 22.10.0-rc3 + fw.cpld 0x1000003 + fw.app nic-22.09.0 + chip.init AMDA-2001-1003 1003000111 + stored: + fw.bundle_id bspbundle_1003000111 + fw.mgmt 22.10.0-rc3 + fw.cpld 0x0 + chip.init AMDA-2001-1003 1003000111 + +Configure Device +================ + +This section explains how to use Agilio SmartNICs running basic NIC firmware. + +Configure interface link-speed +------------------------------ +The following steps explains how to change between 10G mode and 25G mode on +Agilio CX 2x25GbE cards. The changing of port speed must be done in order, +port 0 (p0) must be set to 10G before port 1 (p1) may be set to 10G. + +Down the respective interface(s):: + + $ ip link set dev down + $ ip link set dev down + +Set interface link-speed to 10G:: + + $ ethtool -s speed 10000 + $ ethtool -s speed 10000 + +Set interface link-speed to 25G:: + + $ ethtool -s speed 25000 + $ ethtool -s speed 25000 + +Reload driver for changes to take effect:: + + $ rmmod nfp; modprobe nfp + +Configure interface Maximum Transmission Unit (MTU) +--------------------------------------------------- + +The MTU of interfaces can temporarily be set using the iproute2, ip link or +ifconfig tools. Note that this change will not persist. Setting this via +Network Manager, or another appropriate OS configuration tool, is +recommended as changes to the MTU using Network Manager can be made to +persist. + +Set interface MTU to 9000 bytes:: + + $ ip link set dev mtu 9000 + +It is the responsibility of the user or the orchestration layer to set +appropriate MTU values when handling jumbo frames or utilizing tunnels. For +example, if packets sent from a VM are to be encapsulated on the card and +egress a physical port, then the MTU of the VF should be set to lower than +that of the physical port to account for the extra bytes added by the +additional header. If a setup is expected to see fallback traffic between +the SmartNIC and the kernel then the user should also ensure that the PF MTU +is appropriately set to avoid unexpected drops on this path. + +Configure Forward Error Correction (FEC) modes +---------------------------------------------- + +Agilio SmartNICs support FEC mode configuration, e.g. Auto, Firecode Base-R, +ReedSolomon and Off modes. Each physical port's FEC mode can be set +independently using ethtool. The supported FEC modes for an interface can +be viewed using:: + + $ ethtool + +The currently configured FEC mode can be viewed using:: + + $ ethtool --show-fec + +To force the FEC mode for a particular port, auto-negotiation must be disabled +(see the `Auto-negotiation`_ section). An example of how to set the FEC mode +to Reed-Solomon is:: + + $ ethtool --set-fec encoding rs + +Auto-negotiation +---------------- + +To change auto-negotiation settings, the link must first be put down. After the +link is down, auto-negotiation can be enabled or disabled using:: + + ethtool -s autoneg + Statistics ========== From 973fbe68df39a315bf1233ea7f50e87f8a649947 Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Mon, 14 Nov 2022 19:07:27 +0200 Subject: [PATCH 0934/2002] net: phy: aquantia: add AQR112 and AQR412 PHY IDs These are Gen3 Aquantia N-BASET PHYs which support 5GBASE-T, 2.5GBASE-T, 1000BASE-T and 100BASE-TX (not 10G); also EEE, Sync-E, PTP, PoE. The 112 is a single PHY package, the 412 is a quad PHY package. The system-side SERDES interface of these PHYs selects its protocol depending on the negotiated media side link speed. That protocol can be 1000BASE-X, 2500BASE-X, 10GBASE-R, SGMII, USXGMII. The configuration of which SERDES protocol to use for which link speed is made by firmware; even though it could be overwritten over MDIO by Linux, we assume that the firmware provisioning is ok for the board on which the driver probes. For cases when the system side runs at a fixed rate, we want phylib/phylink to detect the PAUSE rate matching ability of these PHYs, so we need to use the Aquantia rather than the generic C45 driver. This needs aqr107_read_status() -> aqr107_read_rate() to set phydev->rate_matching, as well as the aqr107_get_rate_matching() method. I am a bit unsure about the naming convention in the driver. Since AQR107 is a Gen2 PHY, I assume all functions prefixed with "aqr107_" rather than "aqr_" mean Gen2+ features. So I've reused this naming convention. I've tested PHY "SGMII" statistics as well as the .link_change_notify method, which prints: Aquantia AQR412 mdio_mux-0.4:00: Link partner is Aquantia PHY, FW 4.3, fast-retrain downshift advertised, fast reframe advertised Tested SERDES protocols are usxgmii and 2500base-x (the latter with PAUSE rate matching). Tested link modes are 100/1000/2500 Base-T (with Aquantia link partner and with other link partners). No notable events observed. The placement of these PHY IDs in the driver is right before AQR113C, a Gen4 PHY. Signed-off-by: Vladimir Oltean Signed-off-by: Jakub Kicinski --- drivers/net/phy/aquantia_main.c | 40 +++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/drivers/net/phy/aquantia_main.c b/drivers/net/phy/aquantia_main.c index 47a76df36b74..334a6904ca5a 100644 --- a/drivers/net/phy/aquantia_main.c +++ b/drivers/net/phy/aquantia_main.c @@ -22,6 +22,8 @@ #define PHY_ID_AQR107 0x03a1b4e0 #define PHY_ID_AQCS109 0x03a1b5c2 #define PHY_ID_AQR405 0x03a1b4b0 +#define PHY_ID_AQR112 0x03a1b662 +#define PHY_ID_AQR412 0x03a1b712 #define PHY_ID_AQR113C 0x31c31c12 #define MDIO_PHYXS_VEND_IF_STATUS 0xe812 @@ -800,6 +802,42 @@ static struct phy_driver aqr_driver[] = { .handle_interrupt = aqr_handle_interrupt, .read_status = aqr_read_status, }, +{ + PHY_ID_MATCH_MODEL(PHY_ID_AQR112), + .name = "Aquantia AQR112", + .probe = aqr107_probe, + .config_aneg = aqr_config_aneg, + .config_intr = aqr_config_intr, + .handle_interrupt = aqr_handle_interrupt, + .get_tunable = aqr107_get_tunable, + .set_tunable = aqr107_set_tunable, + .suspend = aqr107_suspend, + .resume = aqr107_resume, + .read_status = aqr107_read_status, + .get_rate_matching = aqr107_get_rate_matching, + .get_sset_count = aqr107_get_sset_count, + .get_strings = aqr107_get_strings, + .get_stats = aqr107_get_stats, + .link_change_notify = aqr107_link_change_notify, +}, +{ + PHY_ID_MATCH_MODEL(PHY_ID_AQR412), + .name = "Aquantia AQR412", + .probe = aqr107_probe, + .config_aneg = aqr_config_aneg, + .config_intr = aqr_config_intr, + .handle_interrupt = aqr_handle_interrupt, + .get_tunable = aqr107_get_tunable, + .set_tunable = aqr107_set_tunable, + .suspend = aqr107_suspend, + .resume = aqr107_resume, + .read_status = aqr107_read_status, + .get_rate_matching = aqr107_get_rate_matching, + .get_sset_count = aqr107_get_sset_count, + .get_strings = aqr107_get_strings, + .get_stats = aqr107_get_stats, + .link_change_notify = aqr107_link_change_notify, +}, { PHY_ID_MATCH_MODEL(PHY_ID_AQR113C), .name = "Aquantia AQR113C", @@ -831,6 +869,8 @@ static struct mdio_device_id __maybe_unused aqr_tbl[] = { { PHY_ID_MATCH_MODEL(PHY_ID_AQR107) }, { PHY_ID_MATCH_MODEL(PHY_ID_AQCS109) }, { PHY_ID_MATCH_MODEL(PHY_ID_AQR405) }, + { PHY_ID_MATCH_MODEL(PHY_ID_AQR112) }, + { PHY_ID_MATCH_MODEL(PHY_ID_AQR412) }, { PHY_ID_MATCH_MODEL(PHY_ID_AQR113C) }, { } }; From 3e7e783291b42f95756dcf54cd8c67973e6ddefd Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Mon, 14 Nov 2022 19:07:28 +0200 Subject: [PATCH 0935/2002] net: dsa: felix: use phylink_generic_validate() Drop the custom implementation of phylink_validate() in favor of the generic one, which requires config->mac_capabilities to be set. This was used up until now because of the possibility of being paired with Aquantia PHYs with support for rate matching. The phylink framework gained generic support for these, and knows to advertise all 10/100/1000 lower speed link modes when our SERDES protocol is 2500base-x (fixed speed). Signed-off-by: Vladimir Oltean Reviewed-by: Russell King (Oracle) Signed-off-by: Jakub Kicinski --- drivers/net/dsa/ocelot/felix.c | 16 ++++--------- drivers/net/dsa/ocelot/felix.h | 3 --- drivers/net/dsa/ocelot/felix_vsc9959.c | 30 ------------------------ drivers/net/dsa/ocelot/seville_vsc9953.c | 27 --------------------- 4 files changed, 4 insertions(+), 72 deletions(-) diff --git a/drivers/net/dsa/ocelot/felix.c b/drivers/net/dsa/ocelot/felix.c index dd3a18cc89dd..44e160f32067 100644 --- a/drivers/net/dsa/ocelot/felix.c +++ b/drivers/net/dsa/ocelot/felix.c @@ -1048,21 +1048,14 @@ static void felix_phylink_get_caps(struct dsa_switch *ds, int port, */ config->legacy_pre_march2020 = false; + config->mac_capabilities = MAC_ASYM_PAUSE | MAC_SYM_PAUSE | + MAC_10 | MAC_100 | MAC_1000FD | + MAC_2500FD; + __set_bit(ocelot->ports[port]->phy_mode, config->supported_interfaces); } -static void felix_phylink_validate(struct dsa_switch *ds, int port, - unsigned long *supported, - struct phylink_link_state *state) -{ - struct ocelot *ocelot = ds->priv; - struct felix *felix = ocelot_to_felix(ocelot); - - if (felix->info->phylink_validate) - felix->info->phylink_validate(ocelot, port, supported, state); -} - static struct phylink_pcs *felix_phylink_mac_select_pcs(struct dsa_switch *ds, int port, phy_interface_t iface) @@ -2050,7 +2043,6 @@ const struct dsa_switch_ops felix_switch_ops = { .get_sset_count = felix_get_sset_count, .get_ts_info = felix_get_ts_info, .phylink_get_caps = felix_phylink_get_caps, - .phylink_validate = felix_phylink_validate, .phylink_mac_select_pcs = felix_phylink_mac_select_pcs, .phylink_mac_link_down = felix_phylink_mac_link_down, .phylink_mac_link_up = felix_phylink_mac_link_up, diff --git a/drivers/net/dsa/ocelot/felix.h b/drivers/net/dsa/ocelot/felix.h index c9c29999c336..42338116eed0 100644 --- a/drivers/net/dsa/ocelot/felix.h +++ b/drivers/net/dsa/ocelot/felix.h @@ -52,9 +52,6 @@ struct felix_info { int (*mdio_bus_alloc)(struct ocelot *ocelot); void (*mdio_bus_free)(struct ocelot *ocelot); - void (*phylink_validate)(struct ocelot *ocelot, int port, - unsigned long *supported, - struct phylink_link_state *state); int (*port_setup_tc)(struct dsa_switch *ds, int port, enum tc_setup_type type, void *type_data); void (*tas_guard_bands_update)(struct ocelot *ocelot, int port); diff --git a/drivers/net/dsa/ocelot/felix_vsc9959.c b/drivers/net/dsa/ocelot/felix_vsc9959.c index 26a35ae322d1..b0ae8d6156f6 100644 --- a/drivers/net/dsa/ocelot/felix_vsc9959.c +++ b/drivers/net/dsa/ocelot/felix_vsc9959.c @@ -885,35 +885,6 @@ static int vsc9959_reset(struct ocelot *ocelot) return 0; } -static void vsc9959_phylink_validate(struct ocelot *ocelot, int port, - unsigned long *supported, - struct phylink_link_state *state) -{ - __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, }; - - phylink_set_port_modes(mask); - phylink_set(mask, Autoneg); - phylink_set(mask, Pause); - phylink_set(mask, Asym_Pause); - phylink_set(mask, 10baseT_Half); - phylink_set(mask, 10baseT_Full); - phylink_set(mask, 100baseT_Half); - phylink_set(mask, 100baseT_Full); - phylink_set(mask, 1000baseT_Half); - phylink_set(mask, 1000baseT_Full); - phylink_set(mask, 1000baseX_Full); - - if (state->interface == PHY_INTERFACE_MODE_INTERNAL || - state->interface == PHY_INTERFACE_MODE_2500BASEX || - state->interface == PHY_INTERFACE_MODE_USXGMII) { - phylink_set(mask, 2500baseT_Full); - phylink_set(mask, 2500baseX_Full); - } - - linkmode_and(supported, supported, mask); - linkmode_and(state->advertising, state->advertising, mask); -} - /* Watermark encode * Bit 8: Unit; 0:1, 1:16 * Bit 7-0: Value to be multiplied with unit @@ -2588,7 +2559,6 @@ static const struct felix_info felix_info_vsc9959 = { .ptp_caps = &vsc9959_ptp_caps, .mdio_bus_alloc = vsc9959_mdio_bus_alloc, .mdio_bus_free = vsc9959_mdio_bus_free, - .phylink_validate = vsc9959_phylink_validate, .port_modes = vsc9959_port_modes, .port_setup_tc = vsc9959_port_setup_tc, .port_sched_speed_set = vsc9959_sched_speed_set, diff --git a/drivers/net/dsa/ocelot/seville_vsc9953.c b/drivers/net/dsa/ocelot/seville_vsc9953.c index 7af33b2c685d..6500c1697dd6 100644 --- a/drivers/net/dsa/ocelot/seville_vsc9953.c +++ b/drivers/net/dsa/ocelot/seville_vsc9953.c @@ -840,32 +840,6 @@ static int vsc9953_reset(struct ocelot *ocelot) return 0; } -static void vsc9953_phylink_validate(struct ocelot *ocelot, int port, - unsigned long *supported, - struct phylink_link_state *state) -{ - __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, }; - - phylink_set_port_modes(mask); - phylink_set(mask, Autoneg); - phylink_set(mask, Pause); - phylink_set(mask, Asym_Pause); - phylink_set(mask, 10baseT_Full); - phylink_set(mask, 10baseT_Half); - phylink_set(mask, 100baseT_Full); - phylink_set(mask, 100baseT_Half); - phylink_set(mask, 1000baseT_Full); - phylink_set(mask, 1000baseX_Full); - - if (state->interface == PHY_INTERFACE_MODE_INTERNAL) { - phylink_set(mask, 2500baseT_Full); - phylink_set(mask, 2500baseX_Full); - } - - linkmode_and(supported, supported, mask); - linkmode_and(state->advertising, state->advertising, mask); -} - /* Watermark encode * Bit 9: Unit; 0:1, 1:16 * Bit 8-0: Value to be multiplied with unit @@ -1007,7 +981,6 @@ static const struct felix_info seville_info_vsc9953 = { .num_tx_queues = OCELOT_NUM_TC, .mdio_bus_alloc = vsc9953_mdio_bus_alloc, .mdio_bus_free = vsc9953_mdio_bus_free, - .phylink_validate = vsc9953_phylink_validate, .port_modes = vsc9953_port_modes, }; From de8586ed4311286a7fb17c3b0166076c1a548518 Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Mon, 14 Nov 2022 19:07:29 +0200 Subject: [PATCH 0936/2002] net: mscc: ocelot: drop workaround for forcing RX flow control As phylink gained generic support for PHYs with rate matching via PAUSE frames, the phylink_mac_link_up() method will be called with the maximum speed and with rx_pause=true if rate matching is in use. This means that setups with 2500base-x as the SERDES protocol between the MAC/PCS and the PHY now work with no need for the driver to do anything special. Tested with fsl-ls1028a-qds-7777.dts. Signed-off-by: Vladimir Oltean Reviewed-by: Russell King (Oracle) Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/mscc/ocelot.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/net/ethernet/mscc/ocelot.c b/drivers/net/ethernet/mscc/ocelot.c index 13b14110a060..da56f9bfeaf0 100644 --- a/drivers/net/ethernet/mscc/ocelot.c +++ b/drivers/net/ethernet/mscc/ocelot.c @@ -872,10 +872,8 @@ void ocelot_phylink_mac_link_up(struct ocelot *ocelot, int port, return; } - /* Handle RX pause in all cases, with 2500base-X this is used for rate - * adaptation. - */ - mac_fc_cfg |= SYS_MAC_FC_CFG_RX_FC_ENA; + if (rx_pause) + mac_fc_cfg |= SYS_MAC_FC_CFG_RX_FC_ENA; if (tx_pause) mac_fc_cfg |= SYS_MAC_FC_CFG_TX_FC_ENA | From 53d04b9811107633f25be02a5d981a6070d09e6e Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Mon, 14 Nov 2022 19:07:30 +0200 Subject: [PATCH 0937/2002] net: dsa: remove phylink_validate() method As of now, no DSA driver uses a custom link mode validation procedure anymore. So remove this DSA operation and let phylink determine what is supported based on config->mac_capabilities (if provided by the driver). Leave a comment why we left the code that we did, and that there is more work to do. Signed-off-by: Vladimir Oltean Reviewed-by: Russell King (Oracle) Signed-off-by: Jakub Kicinski --- include/net/dsa.h | 3 --- net/dsa/port.c | 18 ++++++++---------- 2 files changed, 8 insertions(+), 13 deletions(-) diff --git a/include/net/dsa.h b/include/net/dsa.h index ee369670e20e..dde364688739 100644 --- a/include/net/dsa.h +++ b/include/net/dsa.h @@ -880,9 +880,6 @@ struct dsa_switch_ops { */ void (*phylink_get_caps)(struct dsa_switch *ds, int port, struct phylink_config *config); - void (*phylink_validate)(struct dsa_switch *ds, int port, - unsigned long *supported, - struct phylink_link_state *state); struct phylink_pcs *(*phylink_mac_select_pcs)(struct dsa_switch *ds, int port, phy_interface_t iface); diff --git a/net/dsa/port.c b/net/dsa/port.c index 208168276995..48c9eaa74aee 100644 --- a/net/dsa/port.c +++ b/net/dsa/port.c @@ -1536,16 +1536,14 @@ static void dsa_port_phylink_validate(struct phylink_config *config, unsigned long *supported, struct phylink_link_state *state) { - struct dsa_port *dp = container_of(config, struct dsa_port, pl_config); - struct dsa_switch *ds = dp->ds; - - if (!ds->ops->phylink_validate) { - if (config->mac_capabilities) - phylink_generic_validate(config, supported, state); - return; - } - - ds->ops->phylink_validate(ds, dp->index, supported, state); + /* Skip call for drivers which don't yet set mac_capabilities, + * since validating in that case would mean their PHY will advertise + * nothing. In turn, skipping validation makes them advertise + * everything that the PHY supports, so those drivers should be + * converted ASAP. + */ + if (config->mac_capabilities) + phylink_generic_validate(config, supported, state); } static void dsa_port_phylink_mac_pcs_get_state(struct phylink_config *config, From e0eb60829a6eb249d7c6cccef0605c3004004bef Mon Sep 17 00:00:00 2001 From: Donald Hunter Date: Tue, 15 Nov 2022 09:59:10 +0000 Subject: [PATCH 0938/2002] docs/bpf: Fix sample code in MAP_TYPE_ARRAY docs Remove mistaken & from code example in MAP_TYPE_ARRAY docs Fixes: 1cfa97b30c5a ("bpf, docs: Document BPF_MAP_TYPE_ARRAY") Signed-off-by: Donald Hunter Link: https://lore.kernel.org/r/20221115095910.86407-1-donald.hunter@gmail.com Signed-off-by: Alexei Starovoitov --- Documentation/bpf/map_array.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/bpf/map_array.rst b/Documentation/bpf/map_array.rst index b2cceb6c696b..97bb80333254 100644 --- a/Documentation/bpf/map_array.rst +++ b/Documentation/bpf/map_array.rst @@ -119,7 +119,7 @@ This example BPF program shows how to access an array element. index = ip.protocol; value = bpf_map_lookup_elem(&my_map, &index); if (value) - __sync_fetch_and_add(&value, skb->len); + __sync_fetch_and_add(value, skb->len); return 0; } From df9c41e9db2d0fc9feba0423c21e4319d8acd033 Mon Sep 17 00:00:00 2001 From: Tiezhu Yang Date: Tue, 15 Nov 2022 21:00:07 +0800 Subject: [PATCH 0939/2002] bpftool: Check argc first before "file" in do_batch() If the parameters for batch are more than 2, check argc first can return immediately, no need to use is_prefix() to check "file" with a little overhead and then check argc, it is better to check "file" only when the parameters for batch are 2. Signed-off-by: Tiezhu Yang Acked-by: Stanislav Fomichev Reviewed-by: Quentin Monnet Link: https://lore.kernel.org/r/1668517207-11822-1-git-send-email-yangtiezhu@loongson.cn Signed-off-by: Alexei Starovoitov --- tools/bpf/bpftool/main.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/bpf/bpftool/main.c b/tools/bpf/bpftool/main.c index 741e50ee0b6c..337ab7977ea4 100644 --- a/tools/bpf/bpftool/main.c +++ b/tools/bpf/bpftool/main.c @@ -337,12 +337,12 @@ static int do_batch(int argc, char **argv) if (argc < 2) { p_err("too few parameters for batch"); return -1; - } else if (!is_prefix(*argv, "file")) { - p_err("expected 'file', got: %s", *argv); - return -1; } else if (argc > 2) { p_err("too many parameters for batch"); return -1; + } else if (!is_prefix(*argv, "file")) { + p_err("expected 'file', got: %s", *argv); + return -1; } NEXT_ARG(); From 5b1d640800de7fe02d68bf592d9d101de24c87f2 Mon Sep 17 00:00:00 2001 From: Eduard Zingerman Date: Wed, 16 Nov 2022 03:54:56 +0200 Subject: [PATCH 0940/2002] selftests/bpf: allow unpriv bpf for selftests by default Enable unprivileged bpf for selftests kernel by default. This forces CI to run test_verifier tests in both privileged and unprivileged modes. The test_verifier.c:do_test uses sysctl kernel.unprivileged_bpf_disabled to decide whether to run or to skip test cases in unprivileged mode. The CONFIG_BPF_UNPRIV_DEFAULT_OFF controls the default value of the kernel.unprivileged_bpf_disabled. Signed-off-by: Eduard Zingerman Link: https://lore.kernel.org/r/20221116015456.2461135-1-eddyz87@gmail.com Signed-off-by: Alexei Starovoitov --- tools/testing/selftests/bpf/config | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/testing/selftests/bpf/config b/tools/testing/selftests/bpf/config index 7a99a6728169..f9034ea00bc9 100644 --- a/tools/testing/selftests/bpf/config +++ b/tools/testing/selftests/bpf/config @@ -8,6 +8,7 @@ CONFIG_BPF_LIRC_MODE2=y CONFIG_BPF_LSM=y CONFIG_BPF_STREAM_PARSER=y CONFIG_BPF_SYSCALL=y +CONFIG_BPF_UNPRIV_DEFAULT_OFF=n CONFIG_CGROUP_BPF=y CONFIG_CRYPTO_HMAC=y CONFIG_CRYPTO_SHA256=y From 6423ac2eb31ec33f8526dc48f1e541b665333970 Mon Sep 17 00:00:00 2001 From: David Howells Date: Fri, 11 Nov 2022 16:00:21 +0000 Subject: [PATCH 0941/2002] rxrpc: Fix oops from calling udpv6_sendmsg() on AF_INET socket If rxrpc sees an IPv6 address, it assumes it can call udpv6_sendmsg() on it - even if it got it on an IPv4 socket. Fix do_udp_sendmsg() to give an error in such a case. general protection fault, probably for non-canonical address 0xdffffc0000000000: 0000 [#1] PREEMPT SMP KASAN KASAN: null-ptr-deref in range [0x0000000000000000-0x0000000000000007] ... RIP: 0010:ipv6_addr_v4mapped include/net/ipv6.h:749 [inline] RIP: 0010:udpv6_sendmsg+0xd0a/0x2c70 net/ipv6/udp.c:1361 ... Call Trace: do_udp_sendmsg net/rxrpc/output.c:27 [inline] do_udp_sendmsg net/rxrpc/output.c:21 [inline] rxrpc_send_abort_packet+0x73b/0x860 net/rxrpc/output.c:367 rxrpc_release_calls_on_socket+0x211/0x300 net/rxrpc/call_object.c:595 rxrpc_release_sock net/rxrpc/af_rxrpc.c:886 [inline] rxrpc_release+0x263/0x5a0 net/rxrpc/af_rxrpc.c:917 __sock_release+0xcd/0x280 net/socket.c:650 sock_close+0x18/0x20 net/socket.c:1365 __fput+0x27c/0xa90 fs/file_table.c:320 task_work_run+0x16b/0x270 kernel/task_work.c:179 exit_task_work include/linux/task_work.h:38 [inline] do_exit+0xb35/0x2a20 kernel/exit.c:820 do_group_exit+0xd0/0x2a0 kernel/exit.c:950 __do_sys_exit_group kernel/exit.c:961 [inline] __se_sys_exit_group kernel/exit.c:959 [inline] __x64_sys_exit_group+0x3a/0x50 kernel/exit.c:959 Fixes: ed472b0c8783 ("rxrpc: Call udp_sendmsg() directly") Reported-by: Eric Dumazet Suggested-by: Eric Dumazet Signed-off-by: David Howells cc: Marc Dionne cc: linux-afs@lists.infradead.org --- net/rxrpc/output.c | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/net/rxrpc/output.c b/net/rxrpc/output.c index 46432e70a16b..a2fe1a262f8a 100644 --- a/net/rxrpc/output.c +++ b/net/rxrpc/output.c @@ -18,15 +18,21 @@ extern int udpv6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len); -static ssize_t do_udp_sendmsg(struct socket *sk, struct msghdr *msg, size_t len) +static ssize_t do_udp_sendmsg(struct socket *socket, struct msghdr *msg, size_t len) { -#if IS_ENABLED(CONFIG_AF_RXRPC_IPV6) struct sockaddr *sa = msg->msg_name; + struct sock *sk = socket->sk; - if (sa->sa_family == AF_INET6) - return udpv6_sendmsg(sk->sk, msg, len); -#endif - return udp_sendmsg(sk->sk, msg, len); + if (IS_ENABLED(CONFIG_AF_RXRPC_IPV6)) { + if (sa->sa_family == AF_INET6) { + if (sk->sk_family != AF_INET6) { + pr_warn("AF_INET6 address on AF_INET socket\n"); + return -ENOPROTOOPT; + } + return udpv6_sendmsg(sk, msg, len); + } + } + return udp_sendmsg(sk, msg, len); } struct rxrpc_abort_buffer { From 66f6fd278c6780ea8c8bb7dac839132d8e76dd53 Mon Sep 17 00:00:00 2001 From: David Howells Date: Mon, 14 Nov 2022 11:24:09 +0000 Subject: [PATCH 0942/2002] rxrpc: Fix network address validation Fix network address validation on entry to uapi functions such as connect() for AF_RXRPC. The check for address compatibility with the transport socket isn't correct and allows an AF_INET6 address to be given to an AF_INET socket, resulting in an oops now that rxrpc is calling udp_sendmsg() directly. Sample program: #define _GNU_SOURCE #include #include #include #include #include static unsigned char ctrl[256] = "\x18\x00\x00\x00\x00\x00\x00\x00\x10\x01\x00\x00\x01"; int main(void) { struct sockaddr_rxrpc srx = { .srx_family = AF_RXRPC, .transport_type = SOCK_DGRAM, .transport_len = 28, .transport.sin6.sin6_family = AF_INET6, }; struct mmsghdr vec = { .msg_hdr.msg_control = ctrl, .msg_hdr.msg_controllen = 0x18, }; int s; s = socket(AF_RXRPC, SOCK_DGRAM, AF_INET); if (s < 0) { perror("socket"); exit(1); } if (connect(s, (struct sockaddr *)&srx, sizeof(srx)) < 0) { perror("connect"); exit(1); } if (sendmmsg(s, &vec, 1, MSG_NOSIGNAL | MSG_MORE) < 0) { perror("sendmmsg"); exit(1); } return 0; } If working properly, connect() should fail with EAFNOSUPPORT. Fixes: ed472b0c8783 ("rxrpc: Call udp_sendmsg() directly") Reported-by: Eric Dumazet Signed-off-by: David Howells cc: Marc Dionne cc: linux-afs@lists.infradead.org --- net/rxrpc/af_rxrpc.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/net/rxrpc/af_rxrpc.c b/net/rxrpc/af_rxrpc.c index 2f3991cf8715..aacdd96a9886 100644 --- a/net/rxrpc/af_rxrpc.c +++ b/net/rxrpc/af_rxrpc.c @@ -93,12 +93,11 @@ static int rxrpc_validate_address(struct rxrpc_sock *rx, srx->transport_len > len) return -EINVAL; - if (srx->transport.family != rx->family && - srx->transport.family == AF_INET && rx->family != AF_INET6) - return -EAFNOSUPPORT; - switch (srx->transport.family) { case AF_INET: + if (rx->family != AF_INET && + rx->family != AF_INET6) + return -EAFNOSUPPORT; if (srx->transport_len < sizeof(struct sockaddr_in)) return -EINVAL; tail = offsetof(struct sockaddr_rxrpc, transport.sin.__pad); @@ -106,6 +105,8 @@ static int rxrpc_validate_address(struct rxrpc_sock *rx, #ifdef CONFIG_AF_RXRPC_IPV6 case AF_INET6: + if (rx->family != AF_INET6) + return -EAFNOSUPPORT; if (srx->transport_len < sizeof(struct sockaddr_in6)) return -EINVAL; tail = offsetof(struct sockaddr_rxrpc, transport) + From 5ae0c22634029da089dc7ce2602679ba9005dc51 Mon Sep 17 00:00:00 2001 From: Edward Cree Date: Mon, 14 Nov 2022 13:15:50 +0000 Subject: [PATCH 0943/2002] sfc: fix ef100 RX prefix macro Macro PREFIX_WIDTH_MASK uses unsigned long arithmetic for a shift of up to 32 bits, which breaks on 32-bit systems. This did not previously show up as we weren't using any fields of width 32, but we now need to access ESF_GZ_RX_PREFIX_USER_MARK. Change it to unsigned long long. Signed-off-by: Edward Cree Signed-off-by: David S. Miller --- drivers/net/ethernet/sfc/ef100_rx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/sfc/ef100_rx.c b/drivers/net/ethernet/sfc/ef100_rx.c index 65bbe37753e6..0721260cf2da 100644 --- a/drivers/net/ethernet/sfc/ef100_rx.c +++ b/drivers/net/ethernet/sfc/ef100_rx.c @@ -21,7 +21,7 @@ /* Get the value of a field in the RX prefix */ #define PREFIX_OFFSET_W(_f) (ESF_GZ_RX_PREFIX_ ## _f ## _LBN / 32) #define PREFIX_OFFSET_B(_f) (ESF_GZ_RX_PREFIX_ ## _f ## _LBN % 32) -#define PREFIX_WIDTH_MASK(_f) ((1UL << ESF_GZ_RX_PREFIX_ ## _f ## _WIDTH) - 1) +#define PREFIX_WIDTH_MASK(_f) ((1ULL << ESF_GZ_RX_PREFIX_ ## _f ## _WIDTH) - 1) #define PREFIX_WORD(_p, _f) le32_to_cpu((__force __le32)(_p)[PREFIX_OFFSET_W(_f)]) #define PREFIX_FIELD(_p, _f) ((PREFIX_WORD(_p, _f) >> PREFIX_OFFSET_B(_f)) & \ PREFIX_WIDTH_MASK(_f)) From e395153984871e33624ce5e3f72038de979f5b3e Mon Sep 17 00:00:00 2001 From: Edward Cree Date: Mon, 14 Nov 2022 13:15:51 +0000 Subject: [PATCH 0944/2002] sfc: add ability for an RXQ to grant credits on refill EF100 hardware streams MAE counter updates to the driver over a dedicated RX queue; however, the MCPU is not able to detect when RX buffers have been posted to the ring. Thus, the driver must call MC_CMD_MAE_COUNTERS_STREAM_GIVE_CREDITS; this patch adds the infrastructure to support that to the core RXQ handling code. Signed-off-by: Edward Cree Signed-off-by: David S. Miller --- drivers/net/ethernet/sfc/ef100_rx.c | 14 +++++++++++--- drivers/net/ethernet/sfc/net_driver.h | 8 ++++++++ drivers/net/ethernet/sfc/rx_common.c | 3 +++ 3 files changed, 22 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/sfc/ef100_rx.c b/drivers/net/ethernet/sfc/ef100_rx.c index 0721260cf2da..735f50385919 100644 --- a/drivers/net/ethernet/sfc/ef100_rx.c +++ b/drivers/net/ethernet/sfc/ef100_rx.c @@ -183,24 +183,32 @@ void efx_ef100_ev_rx(struct efx_channel *channel, const efx_qword_t *p_event) void ef100_rx_write(struct efx_rx_queue *rx_queue) { + unsigned int notified_count = rx_queue->notified_count; struct efx_rx_buffer *rx_buf; unsigned int idx; efx_qword_t *rxd; efx_dword_t rxdb; - while (rx_queue->notified_count != rx_queue->added_count) { - idx = rx_queue->notified_count & rx_queue->ptr_mask; + while (notified_count != rx_queue->added_count) { + idx = notified_count & rx_queue->ptr_mask; rx_buf = efx_rx_buffer(rx_queue, idx); rxd = efx_rx_desc(rx_queue, idx); EFX_POPULATE_QWORD_1(*rxd, ESF_GZ_RX_BUF_ADDR, rx_buf->dma_addr); - ++rx_queue->notified_count; + ++notified_count; } + if (notified_count == rx_queue->notified_count) + return; wmb(); EFX_POPULATE_DWORD_1(rxdb, ERF_GZ_RX_RING_PIDX, rx_queue->added_count & rx_queue->ptr_mask); efx_writed_page(rx_queue->efx, &rxdb, ER_GZ_RX_RING_DOORBELL, efx_rx_queue_index(rx_queue)); + if (rx_queue->grant_credits) + wmb(); + rx_queue->notified_count = notified_count; + if (rx_queue->grant_credits) + schedule_work(&rx_queue->grant_work); } diff --git a/drivers/net/ethernet/sfc/net_driver.h b/drivers/net/ethernet/sfc/net_driver.h index 7ef823d7a89a..efb867b6556a 100644 --- a/drivers/net/ethernet/sfc/net_driver.h +++ b/drivers/net/ethernet/sfc/net_driver.h @@ -363,8 +363,12 @@ struct efx_rx_page_state { * @refill_enabled: Enable refill whenever fill level is low * @flush_pending: Set when a RX flush is pending. Has the same lifetime as * @rxq_flush_pending. + * @grant_credits: Posted RX descriptors need to be granted to the MAE with + * %MC_CMD_MAE_COUNTERS_STREAM_GIVE_CREDITS. For %EFX_EXTRA_CHANNEL_TC, + * and only supported on EF100. * @added_count: Number of buffers added to the receive queue. * @notified_count: Number of buffers given to NIC (<= @added_count). + * @granted_count: Number of buffers granted to the MAE (<= @notified_count). * @removed_count: Number of buffers removed from the receive queue. * @scatter_n: Used by NIC specific receive code. * @scatter_len: Used by NIC specific receive code. @@ -385,6 +389,7 @@ struct efx_rx_page_state { * refill was triggered. * @recycle_count: RX buffer recycle counter. * @slow_fill: Timer used to defer efx_nic_generate_fill_event(). + * @grant_work: workitem used to grant credits to the MAE if @grant_credits * @xdp_rxq_info: XDP specific RX queue information. * @xdp_rxq_info_valid: Is xdp_rxq_info valid data?. */ @@ -396,9 +401,11 @@ struct efx_rx_queue { unsigned int ptr_mask; bool refill_enabled; bool flush_pending; + bool grant_credits; unsigned int added_count; unsigned int notified_count; + unsigned int granted_count; unsigned int removed_count; unsigned int scatter_n; unsigned int scatter_len; @@ -416,6 +423,7 @@ struct efx_rx_queue { unsigned int recycle_count; struct timer_list slow_fill; unsigned int slow_fill_count; + struct work_struct grant_work; /* Statistics to supplement MAC stats */ unsigned long rx_packets; struct xdp_rxq_info xdp_rxq_info; diff --git a/drivers/net/ethernet/sfc/rx_common.c b/drivers/net/ethernet/sfc/rx_common.c index 9220afeddee8..d2f35ee15eff 100644 --- a/drivers/net/ethernet/sfc/rx_common.c +++ b/drivers/net/ethernet/sfc/rx_common.c @@ -229,6 +229,7 @@ void efx_init_rx_queue(struct efx_rx_queue *rx_queue) /* Initialise ptr fields */ rx_queue->added_count = 0; rx_queue->notified_count = 0; + rx_queue->granted_count = 0; rx_queue->removed_count = 0; rx_queue->min_fill = -1U; efx_init_rx_recycle_ring(rx_queue); @@ -281,6 +282,8 @@ void efx_fini_rx_queue(struct efx_rx_queue *rx_queue) "shutting down RX queue %d\n", efx_rx_queue_index(rx_queue)); del_timer_sync(&rx_queue->slow_fill); + if (rx_queue->grant_credits) + flush_work(&rx_queue->grant_work); /* Release RX buffers from the current read ptr to the write ptr */ if (rx_queue->buffer) { From 85697f97fd3cdb1766ba53ac2d129bbc34e6df98 Mon Sep 17 00:00:00 2001 From: Edward Cree Date: Mon, 14 Nov 2022 13:15:52 +0000 Subject: [PATCH 0945/2002] sfc: add start and stop methods to channels The TC extra channel needs to do extra work in efx_{start,stop}_channels() to start/stop MAE counter streaming from the hardware. Add callbacks for it to implement. Signed-off-by: Edward Cree Signed-off-by: David S. Miller --- drivers/net/ethernet/sfc/efx_channels.c | 9 ++++++++- drivers/net/ethernet/sfc/net_driver.h | 4 ++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/sfc/efx_channels.c b/drivers/net/ethernet/sfc/efx_channels.c index aaa381743bca..fcea3ea809d7 100644 --- a/drivers/net/ethernet/sfc/efx_channels.c +++ b/drivers/net/ethernet/sfc/efx_channels.c @@ -1119,6 +1119,8 @@ void efx_start_channels(struct efx_nic *efx) struct efx_channel *channel; efx_for_each_channel_rev(channel, efx) { + if (channel->type->start) + channel->type->start(channel); efx_for_each_channel_tx_queue(tx_queue, channel) { efx_init_tx_queue(tx_queue); atomic_inc(&efx->active_queues); @@ -1143,8 +1145,13 @@ void efx_stop_channels(struct efx_nic *efx) struct efx_channel *channel; int rc = 0; - /* Stop RX refill */ + /* Stop special channels and RX refill. + * The channel's stop has to be called first, since it might wait + * for a sentinel RX to indicate the channel has fully drained. + */ efx_for_each_channel(channel, efx) { + if (channel->type->stop) + channel->type->stop(channel); efx_for_each_channel_rx_queue(rx_queue, channel) rx_queue->refill_enabled = false; } diff --git a/drivers/net/ethernet/sfc/net_driver.h b/drivers/net/ethernet/sfc/net_driver.h index efb867b6556a..b3d413896230 100644 --- a/drivers/net/ethernet/sfc/net_driver.h +++ b/drivers/net/ethernet/sfc/net_driver.h @@ -585,6 +585,8 @@ struct efx_msi_context { * struct efx_channel_type - distinguishes traffic and extra channels * @handle_no_channel: Handle failure to allocate an extra channel * @pre_probe: Set up extra state prior to initialisation + * @start: called early in efx_start_channels() + * @stop: called early in efx_stop_channels() * @post_remove: Tear down extra state after finalisation, if allocated. * May be called on channels that have not been probed. * @get_name: Generate the channel's name (used for its IRQ handler) @@ -601,6 +603,8 @@ struct efx_msi_context { struct efx_channel_type { void (*handle_no_channel)(struct efx_nic *); int (*pre_probe)(struct efx_channel *); + int (*start)(struct efx_channel *); + void (*stop)(struct efx_channel *); void (*post_remove)(struct efx_channel *); void (*get_name)(struct efx_channel *, char *buf, size_t len); struct efx_channel *(*copy)(const struct efx_channel *); From 36df6136a7d0a32f6b7e5862aeef4702b8b087d2 Mon Sep 17 00:00:00 2001 From: Edward Cree Date: Mon, 14 Nov 2022 13:15:53 +0000 Subject: [PATCH 0946/2002] sfc: add ability for extra channels to receive raw RX buffers The TC extra channel will need its own special RX handling, which must operate before any code that expects the RX buffer to contain a network packet; buffers on this RX queue contain MAE counter packets in a special format that does not resemble an Ethernet frame, and many fields of the RX packet prefix are not populated. The USER_MARK field, however, is populated with the generation count from the counter subsystem, which needs to be passed on to the RX handler. Signed-off-by: Edward Cree Signed-off-by: David S. Miller --- drivers/net/ethernet/sfc/ef100_rx.c | 7 +++++++ drivers/net/ethernet/sfc/net_driver.h | 2 ++ 2 files changed, 9 insertions(+) diff --git a/drivers/net/ethernet/sfc/ef100_rx.c b/drivers/net/ethernet/sfc/ef100_rx.c index 735f50385919..83d9db71d7d7 100644 --- a/drivers/net/ethernet/sfc/ef100_rx.c +++ b/drivers/net/ethernet/sfc/ef100_rx.c @@ -67,6 +67,13 @@ void __ef100_rx_packet(struct efx_channel *channel) prefix = (u32 *)(eh - ESE_GZ_RX_PKT_PREFIX_LEN); + if (channel->type->receive_raw) { + u32 mark = PREFIX_FIELD(prefix, USER_MARK); + + if (channel->type->receive_raw(rx_queue, mark)) + return; /* packet was consumed */ + } + if (ef100_has_fcs_error(channel, prefix) && unlikely(!(efx->net_dev->features & NETIF_F_RXALL))) goto out; diff --git a/drivers/net/ethernet/sfc/net_driver.h b/drivers/net/ethernet/sfc/net_driver.h index b3d413896230..1e42f3447b24 100644 --- a/drivers/net/ethernet/sfc/net_driver.h +++ b/drivers/net/ethernet/sfc/net_driver.h @@ -593,6 +593,7 @@ struct efx_msi_context { * @copy: Copy the channel state prior to reallocation. May be %NULL if * reallocation is not supported. * @receive_skb: Handle an skb ready to be passed to netif_receive_skb() + * @receive_raw: Handle an RX buffer ready to be passed to __efx_rx_packet() * @want_txqs: Determine whether this channel should have TX queues * created. If %NULL, TX queues are not created. * @keep_eventq: Flag for whether event queue should be kept initialised @@ -609,6 +610,7 @@ struct efx_channel_type { void (*get_name)(struct efx_channel *, char *buf, size_t len); struct efx_channel *(*copy)(const struct efx_channel *); bool (*receive_skb)(struct efx_channel *, struct sk_buff *); + bool (*receive_raw)(struct efx_rx_queue *, u32); bool (*want_txqs)(struct efx_channel *); bool keep_eventq; bool want_pio; From e5731274cdd186a362beba1bbf0cc35e1dd5f547 Mon Sep 17 00:00:00 2001 From: Edward Cree Date: Mon, 14 Nov 2022 13:15:54 +0000 Subject: [PATCH 0947/2002] sfc: add ef100 MAE counter support functions Start and stop MAE counter streaming, and grant credits. Signed-off-by: Edward Cree Signed-off-by: David S. Miller --- drivers/net/ethernet/sfc/mae.c | 111 ++++++++++++++++++++++++++++++++ drivers/net/ethernet/sfc/mae.h | 4 ++ drivers/net/ethernet/sfc/mcdi.h | 5 ++ drivers/net/ethernet/sfc/tc.c | 1 + drivers/net/ethernet/sfc/tc.h | 18 ++++++ 5 files changed, 139 insertions(+) diff --git a/drivers/net/ethernet/sfc/mae.c b/drivers/net/ethernet/sfc/mae.c index 1e605e2a08c5..37722344c1cd 100644 --- a/drivers/net/ethernet/sfc/mae.c +++ b/drivers/net/ethernet/sfc/mae.c @@ -112,6 +112,117 @@ int efx_mae_lookup_mport(struct efx_nic *efx, u32 selector, u32 *id) return 0; } +int efx_mae_start_counters(struct efx_nic *efx, struct efx_rx_queue *rx_queue) +{ + MCDI_DECLARE_BUF(inbuf, MC_CMD_MAE_COUNTERS_STREAM_START_V2_IN_LEN); + MCDI_DECLARE_BUF(outbuf, MC_CMD_MAE_COUNTERS_STREAM_START_OUT_LEN); + u32 out_flags; + size_t outlen; + int rc; + + MCDI_SET_WORD(inbuf, MAE_COUNTERS_STREAM_START_V2_IN_QID, + efx_rx_queue_index(rx_queue)); + MCDI_SET_WORD(inbuf, MAE_COUNTERS_STREAM_START_V2_IN_PACKET_SIZE, + efx->net_dev->mtu); + MCDI_SET_DWORD(inbuf, MAE_COUNTERS_STREAM_START_V2_IN_COUNTER_TYPES_MASK, + BIT(MAE_COUNTER_TYPE_AR) | BIT(MAE_COUNTER_TYPE_CT) | + BIT(MAE_COUNTER_TYPE_OR)); + rc = efx_mcdi_rpc(efx, MC_CMD_MAE_COUNTERS_STREAM_START, + inbuf, sizeof(inbuf), outbuf, sizeof(outbuf), &outlen); + if (rc) + return rc; + if (outlen < sizeof(outbuf)) + return -EIO; + out_flags = MCDI_DWORD(outbuf, MAE_COUNTERS_STREAM_START_OUT_FLAGS); + if (out_flags & BIT(MC_CMD_MAE_COUNTERS_STREAM_START_OUT_USES_CREDITS_OFST)) { + netif_dbg(efx, drv, efx->net_dev, + "MAE counter stream uses credits\n"); + rx_queue->grant_credits = true; + out_flags &= ~BIT(MC_CMD_MAE_COUNTERS_STREAM_START_OUT_USES_CREDITS_OFST); + } + if (out_flags) { + netif_err(efx, drv, efx->net_dev, + "MAE counter stream start: unrecognised flags %x\n", + out_flags); + goto out_stop; + } + return 0; +out_stop: + efx_mae_stop_counters(efx, rx_queue); + return -EOPNOTSUPP; +} + +static bool efx_mae_counters_flushed(u32 *flush_gen, u32 *seen_gen) +{ + int i; + + for (i = 0; i < EFX_TC_COUNTER_TYPE_MAX; i++) + if ((s32)(flush_gen[i] - seen_gen[i]) > 0) + return false; + return true; +} + +int efx_mae_stop_counters(struct efx_nic *efx, struct efx_rx_queue *rx_queue) +{ + MCDI_DECLARE_BUF(outbuf, MC_CMD_MAE_COUNTERS_STREAM_STOP_V2_OUT_LENMAX); + MCDI_DECLARE_BUF(inbuf, MC_CMD_MAE_COUNTERS_STREAM_STOP_IN_LEN); + size_t outlen; + int rc, i; + + MCDI_SET_WORD(inbuf, MAE_COUNTERS_STREAM_STOP_IN_QID, + efx_rx_queue_index(rx_queue)); + rc = efx_mcdi_rpc(efx, MC_CMD_MAE_COUNTERS_STREAM_STOP, + inbuf, sizeof(inbuf), outbuf, sizeof(outbuf), &outlen); + + if (rc) + return rc; + + netif_dbg(efx, drv, efx->net_dev, "Draining counters:\n"); + /* Only process received generation counts */ + for (i = 0; (i < (outlen / 4)) && (i < EFX_TC_COUNTER_TYPE_MAX); i++) { + efx->tc->flush_gen[i] = MCDI_ARRAY_DWORD(outbuf, + MAE_COUNTERS_STREAM_STOP_V2_OUT_GENERATION_COUNT, + i); + netif_dbg(efx, drv, efx->net_dev, + "\ttype %u, awaiting gen %u\n", i, + efx->tc->flush_gen[i]); + } + + efx->tc->flush_counters = true; + + /* Drain can take up to 2 seconds owing to FWRIVERHD-2884; whatever + * timeout we use, that delay is added to unload on nonresponsive + * hardware, so 2500ms seems like a reasonable compromise. + */ + if (!wait_event_timeout(efx->tc->flush_wq, + efx_mae_counters_flushed(efx->tc->flush_gen, + efx->tc->seen_gen), + msecs_to_jiffies(2500))) + netif_warn(efx, drv, efx->net_dev, + "Failed to drain counters RXQ, FW may be unhappy\n"); + + efx->tc->flush_counters = false; + + return rc; +} + +void efx_mae_counters_grant_credits(struct work_struct *work) +{ + MCDI_DECLARE_BUF(inbuf, MC_CMD_MAE_COUNTERS_STREAM_GIVE_CREDITS_IN_LEN); + struct efx_rx_queue *rx_queue = container_of(work, struct efx_rx_queue, + grant_work); + struct efx_nic *efx = rx_queue->efx; + unsigned int credits; + + BUILD_BUG_ON(MC_CMD_MAE_COUNTERS_STREAM_GIVE_CREDITS_OUT_LEN); + credits = READ_ONCE(rx_queue->notified_count) - rx_queue->granted_count; + MCDI_SET_DWORD(inbuf, MAE_COUNTERS_STREAM_GIVE_CREDITS_IN_NUM_CREDITS, + credits); + if (!efx_mcdi_rpc(efx, MC_CMD_MAE_COUNTERS_STREAM_GIVE_CREDITS, + inbuf, sizeof(inbuf), NULL, 0, NULL)) + rx_queue->granted_count += credits; +} + static int efx_mae_get_basic_caps(struct efx_nic *efx, struct mae_caps *caps) { MCDI_DECLARE_BUF(outbuf, MC_CMD_MAE_GET_CAPS_OUT_LEN); diff --git a/drivers/net/ethernet/sfc/mae.h b/drivers/net/ethernet/sfc/mae.h index 3e0cd238d523..8f5de01dd962 100644 --- a/drivers/net/ethernet/sfc/mae.h +++ b/drivers/net/ethernet/sfc/mae.h @@ -27,6 +27,10 @@ void efx_mae_mport_mport(struct efx_nic *efx, u32 mport_id, u32 *out); int efx_mae_lookup_mport(struct efx_nic *efx, u32 selector, u32 *id); +int efx_mae_start_counters(struct efx_nic *efx, struct efx_rx_queue *rx_queue); +int efx_mae_stop_counters(struct efx_nic *efx, struct efx_rx_queue *rx_queue); +void efx_mae_counters_grant_credits(struct work_struct *work); + #define MAE_NUM_FIELDS (MAE_FIELD_ENC_VNET_ID + 1) struct mae_caps { diff --git a/drivers/net/ethernet/sfc/mcdi.h b/drivers/net/ethernet/sfc/mcdi.h index fbeb58104936..7e35fec9da35 100644 --- a/drivers/net/ethernet/sfc/mcdi.h +++ b/drivers/net/ethernet/sfc/mcdi.h @@ -221,6 +221,11 @@ void efx_mcdi_sensor_event(struct efx_nic *efx, efx_qword_t *ev); #define MCDI_BYTE(_buf, _field) \ ((void)BUILD_BUG_ON_ZERO(MC_CMD_ ## _field ## _LEN != 1), \ *MCDI_PTR(_buf, _field)) +#define MCDI_SET_WORD(_buf, _field, _value) do { \ + BUILD_BUG_ON(MC_CMD_ ## _field ## _LEN != 2); \ + BUILD_BUG_ON(MC_CMD_ ## _field ## _OFST & 1); \ + *(__force __le16 *)MCDI_PTR(_buf, _field) = cpu_to_le16(_value);\ + } while (0) #define MCDI_WORD(_buf, _field) \ ((u16)BUILD_BUG_ON_ZERO(MC_CMD_ ## _field ## _LEN != 2) + \ le16_to_cpu(*(__force const __le16 *)MCDI_PTR(_buf, _field))) diff --git a/drivers/net/ethernet/sfc/tc.c b/drivers/net/ethernet/sfc/tc.c index 17e1a3447554..894f578b3296 100644 --- a/drivers/net/ethernet/sfc/tc.c +++ b/drivers/net/ethernet/sfc/tc.c @@ -751,6 +751,7 @@ int efx_init_struct_tc(struct efx_nic *efx) INIT_LIST_HEAD(&efx->tc->block_list); mutex_init(&efx->tc->mutex); + init_waitqueue_head(&efx->tc->flush_wq); rc = rhashtable_init(&efx->tc->match_action_ht, &efx_tc_match_action_ht_params); if (rc < 0) goto fail_match_action_ht; diff --git a/drivers/net/ethernet/sfc/tc.h b/drivers/net/ethernet/sfc/tc.h index 4240c375a8e6..464fc92e2d37 100644 --- a/drivers/net/ethernet/sfc/tc.h +++ b/drivers/net/ethernet/sfc/tc.h @@ -14,6 +14,14 @@ #include #include #include "net_driver.h" +#include "mcdi_pcol.h" /* for MAE_COUNTER_TYPE_* */ + +enum efx_tc_counter_type { + EFX_TC_COUNTER_TYPE_AR = MAE_COUNTER_TYPE_AR, + EFX_TC_COUNTER_TYPE_CT = MAE_COUNTER_TYPE_CT, + EFX_TC_COUNTER_TYPE_OR = MAE_COUNTER_TYPE_OR, + EFX_TC_COUNTER_TYPE_MAX +}; #define IS_ALL_ONES(v) (!(typeof (v))~(v)) @@ -79,6 +87,12 @@ enum efx_tc_rule_prios { * @reps_filter_uc: VNIC filter for representor unicast RX (promisc) * @reps_filter_mc: VNIC filter for representor multicast RX (allmulti) * @reps_mport_vport_id: vport_id for representor RX filters + * @flush_counters: counters have been stopped, waiting for drain + * @flush_gen: final generation count per type array as reported by + * MC_CMD_MAE_COUNTERS_STREAM_STOP + * @seen_gen: most recent generation count per type as seen by efx_tc_rx() + * @flush_wq: wait queue used by efx_mae_stop_counters() to wait for + * MAE counters RXQ to finish draining * @dflt: Match-action rules for default switching; at priority * %EFX_TC_PRIO_DFLT. Named by *ingress* port * @dflt.pf: rule for traffic ingressing from PF (egresses to wire) @@ -92,6 +106,10 @@ struct efx_tc_state { struct rhashtable match_action_ht; u32 reps_mport_id, reps_mport_vport_id; s32 reps_filter_uc, reps_filter_mc; + bool flush_counters; + u32 flush_gen[EFX_TC_COUNTER_TYPE_MAX]; + u32 seen_gen[EFX_TC_COUNTER_TYPE_MAX]; + wait_queue_head_t flush_wq; struct { struct efx_tc_flow_rule pf; struct efx_tc_flow_rule wire; From 25730d8be5d8bc691ed89dca065f2b229b244ae0 Mon Sep 17 00:00:00 2001 From: Edward Cree Date: Mon, 14 Nov 2022 13:15:55 +0000 Subject: [PATCH 0948/2002] sfc: add extra RX channel to receive MAE counter updates on ef100 Currently there is no counter-allocating machinery to connect the resulting counter update values to; that will be added in a subsequent patch. Signed-off-by: Edward Cree Signed-off-by: David S. Miller --- drivers/net/ethernet/sfc/Makefile | 2 +- drivers/net/ethernet/sfc/mae_counter_format.h | 73 +++++ drivers/net/ethernet/sfc/net_driver.h | 3 +- drivers/net/ethernet/sfc/tc.c | 1 + drivers/net/ethernet/sfc/tc.h | 9 +- drivers/net/ethernet/sfc/tc_counters.c | 269 ++++++++++++++++++ drivers/net/ethernet/sfc/tc_counters.h | 26 ++ 7 files changed, 373 insertions(+), 10 deletions(-) create mode 100644 drivers/net/ethernet/sfc/mae_counter_format.h create mode 100644 drivers/net/ethernet/sfc/tc_counters.c create mode 100644 drivers/net/ethernet/sfc/tc_counters.h diff --git a/drivers/net/ethernet/sfc/Makefile b/drivers/net/ethernet/sfc/Makefile index b5e45fc6337e..712a48d00069 100644 --- a/drivers/net/ethernet/sfc/Makefile +++ b/drivers/net/ethernet/sfc/Makefile @@ -9,7 +9,7 @@ sfc-y += efx.o efx_common.o efx_channels.o nic.o \ ef100_ethtool.o ef100_rx.o ef100_tx.o sfc-$(CONFIG_SFC_MTD) += mtd.o sfc-$(CONFIG_SFC_SRIOV) += sriov.o ef10_sriov.o ef100_sriov.o ef100_rep.o \ - mae.o tc.o tc_bindings.o + mae.o tc.o tc_bindings.o tc_counters.o obj-$(CONFIG_SFC) += sfc.o diff --git a/drivers/net/ethernet/sfc/mae_counter_format.h b/drivers/net/ethernet/sfc/mae_counter_format.h new file mode 100644 index 000000000000..7e252e393fbe --- /dev/null +++ b/drivers/net/ethernet/sfc/mae_counter_format.h @@ -0,0 +1,73 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/**************************************************************************** + * Driver for Solarflare network controllers and boards + * Copyright 2020 Xilinx, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation, incorporated herein by reference. + */ + +/* Format of counter packets (version 2) from the ef100 Match-Action Engine */ + +#ifndef EFX_MAE_COUNTER_FORMAT_H +#define EFX_MAE_COUNTER_FORMAT_H + + +/*------------------------------------------------------------*/ +/* + * ER_RX_SL_PACKETISER_HEADER_WORD(160bit): + * + */ +#define ER_RX_SL_PACKETISER_HEADER_WORD_SIZE 20 +#define ER_RX_SL_PACKETISER_HEADER_WORD_WIDTH 160 + +#define ERF_SC_PACKETISER_HEADER_VERSION_LBN 0 +#define ERF_SC_PACKETISER_HEADER_VERSION_WIDTH 8 +#define ERF_SC_PACKETISER_HEADER_VERSION_VALUE 2 +#define ERF_SC_PACKETISER_HEADER_IDENTIFIER_LBN 8 +#define ERF_SC_PACKETISER_HEADER_IDENTIFIER_WIDTH 8 +#define ERF_SC_PACKETISER_HEADER_IDENTIFIER_AR 0 +#define ERF_SC_PACKETISER_HEADER_IDENTIFIER_CT 1 +#define ERF_SC_PACKETISER_HEADER_IDENTIFIER_OR 2 +#define ERF_SC_PACKETISER_HEADER_HEADER_OFFSET_LBN 16 +#define ERF_SC_PACKETISER_HEADER_HEADER_OFFSET_WIDTH 8 +#define ERF_SC_PACKETISER_HEADER_HEADER_OFFSET_DEFAULT 0x4 +#define ERF_SC_PACKETISER_HEADER_PAYLOAD_OFFSET_LBN 24 +#define ERF_SC_PACKETISER_HEADER_PAYLOAD_OFFSET_WIDTH 8 +#define ERF_SC_PACKETISER_HEADER_PAYLOAD_OFFSET_DEFAULT 0x14 +#define ERF_SC_PACKETISER_HEADER_INDEX_LBN 32 +#define ERF_SC_PACKETISER_HEADER_INDEX_WIDTH 16 +#define ERF_SC_PACKETISER_HEADER_COUNT_LBN 48 +#define ERF_SC_PACKETISER_HEADER_COUNT_WIDTH 16 +#define ERF_SC_PACKETISER_HEADER_RESERVED_0_LBN 64 +#define ERF_SC_PACKETISER_HEADER_RESERVED_0_WIDTH 32 +#define ERF_SC_PACKETISER_HEADER_RESERVED_1_LBN 96 +#define ERF_SC_PACKETISER_HEADER_RESERVED_1_WIDTH 32 +#define ERF_SC_PACKETISER_HEADER_RESERVED_2_LBN 128 +#define ERF_SC_PACKETISER_HEADER_RESERVED_2_WIDTH 32 + + +/*------------------------------------------------------------*/ +/* + * ER_RX_SL_PACKETISER_PAYLOAD_WORD(128bit): + * + */ +#define ER_RX_SL_PACKETISER_PAYLOAD_WORD_SIZE 16 +#define ER_RX_SL_PACKETISER_PAYLOAD_WORD_WIDTH 128 + +#define ERF_SC_PACKETISER_PAYLOAD_COUNTER_INDEX_LBN 0 +#define ERF_SC_PACKETISER_PAYLOAD_COUNTER_INDEX_WIDTH 24 +#define ERF_SC_PACKETISER_PAYLOAD_RESERVED_LBN 24 +#define ERF_SC_PACKETISER_PAYLOAD_RESERVED_WIDTH 8 +#define ERF_SC_PACKETISER_PAYLOAD_PACKET_COUNT_OFST 4 +#define ERF_SC_PACKETISER_PAYLOAD_PACKET_COUNT_SIZE 6 +#define ERF_SC_PACKETISER_PAYLOAD_PACKET_COUNT_LBN 32 +#define ERF_SC_PACKETISER_PAYLOAD_PACKET_COUNT_WIDTH 48 +#define ERF_SC_PACKETISER_PAYLOAD_BYTE_COUNT_OFST 10 +#define ERF_SC_PACKETISER_PAYLOAD_BYTE_COUNT_SIZE 6 +#define ERF_SC_PACKETISER_PAYLOAD_BYTE_COUNT_LBN 80 +#define ERF_SC_PACKETISER_PAYLOAD_BYTE_COUNT_WIDTH 48 + + +#endif /* EFX_MAE_COUNTER_FORMAT_H */ diff --git a/drivers/net/ethernet/sfc/net_driver.h b/drivers/net/ethernet/sfc/net_driver.h index 1e42f3447b24..3b49e216768b 100644 --- a/drivers/net/ethernet/sfc/net_driver.h +++ b/drivers/net/ethernet/sfc/net_driver.h @@ -56,7 +56,8 @@ #define EFX_MAX_RX_QUEUES EFX_MAX_CHANNELS #define EFX_EXTRA_CHANNEL_IOV 0 #define EFX_EXTRA_CHANNEL_PTP 1 -#define EFX_MAX_EXTRA_CHANNELS 2U +#define EFX_EXTRA_CHANNEL_TC 2 +#define EFX_MAX_EXTRA_CHANNELS 3U /* Checksum generation is a per-queue option in hardware, so each * queue visible to the networking core is backed by two hardware TX diff --git a/drivers/net/ethernet/sfc/tc.c b/drivers/net/ethernet/sfc/tc.c index 894f578b3296..37d56a1ba958 100644 --- a/drivers/net/ethernet/sfc/tc.c +++ b/drivers/net/ethernet/sfc/tc.c @@ -761,6 +761,7 @@ int efx_init_struct_tc(struct efx_nic *efx) efx->tc->dflt.pf.fw_id = MC_CMD_MAE_ACTION_RULE_INSERT_OUT_ACTION_RULE_ID_NULL; INIT_LIST_HEAD(&efx->tc->dflt.wire.acts.list); efx->tc->dflt.wire.fw_id = MC_CMD_MAE_ACTION_RULE_INSERT_OUT_ACTION_RULE_ID_NULL; + efx->extra_channel_type[EFX_EXTRA_CHANNEL_TC] = &efx_tc_channel_type; return 0; fail_match_action_ht: mutex_destroy(&efx->tc->mutex); diff --git a/drivers/net/ethernet/sfc/tc.h b/drivers/net/ethernet/sfc/tc.h index 464fc92e2d37..97dc06f2e694 100644 --- a/drivers/net/ethernet/sfc/tc.h +++ b/drivers/net/ethernet/sfc/tc.h @@ -14,14 +14,7 @@ #include #include #include "net_driver.h" -#include "mcdi_pcol.h" /* for MAE_COUNTER_TYPE_* */ - -enum efx_tc_counter_type { - EFX_TC_COUNTER_TYPE_AR = MAE_COUNTER_TYPE_AR, - EFX_TC_COUNTER_TYPE_CT = MAE_COUNTER_TYPE_CT, - EFX_TC_COUNTER_TYPE_OR = MAE_COUNTER_TYPE_OR, - EFX_TC_COUNTER_TYPE_MAX -}; +#include "tc_counters.h" #define IS_ALL_ONES(v) (!(typeof (v))~(v)) diff --git a/drivers/net/ethernet/sfc/tc_counters.c b/drivers/net/ethernet/sfc/tc_counters.c new file mode 100644 index 000000000000..4a310cd7f17f --- /dev/null +++ b/drivers/net/ethernet/sfc/tc_counters.c @@ -0,0 +1,269 @@ +// SPDX-License-Identifier: GPL-2.0-only +/**************************************************************************** + * Driver for Solarflare network controllers and boards + * Copyright 2022 Advanced Micro Devices, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation, incorporated herein by reference. + */ + +#include "tc_counters.h" +#include "mae_counter_format.h" +#include "mae.h" +#include "rx_common.h" + +/* TC Channel. Counter updates are delivered on this channel's RXQ. */ + +static void efx_tc_handle_no_channel(struct efx_nic *efx) +{ + netif_warn(efx, drv, efx->net_dev, + "MAE counters require MSI-X and 1 additional interrupt vector.\n"); +} + +static int efx_tc_probe_channel(struct efx_channel *channel) +{ + struct efx_rx_queue *rx_queue = &channel->rx_queue; + + channel->irq_moderation_us = 0; + rx_queue->core_index = 0; + + INIT_WORK(&rx_queue->grant_work, efx_mae_counters_grant_credits); + + return 0; +} + +static int efx_tc_start_channel(struct efx_channel *channel) +{ + struct efx_rx_queue *rx_queue = efx_channel_get_rx_queue(channel); + struct efx_nic *efx = channel->efx; + + return efx_mae_start_counters(efx, rx_queue); +} + +static void efx_tc_stop_channel(struct efx_channel *channel) +{ + struct efx_rx_queue *rx_queue = efx_channel_get_rx_queue(channel); + struct efx_nic *efx = channel->efx; + int rc; + + rc = efx_mae_stop_counters(efx, rx_queue); + if (rc) + netif_warn(efx, drv, efx->net_dev, + "Failed to stop MAE counters streaming, rc=%d.\n", + rc); + rx_queue->grant_credits = false; + flush_work(&rx_queue->grant_work); +} + +static void efx_tc_remove_channel(struct efx_channel *channel) +{ +} + +static void efx_tc_get_channel_name(struct efx_channel *channel, + char *buf, size_t len) +{ + snprintf(buf, len, "%s-mae", channel->efx->name); +} + +static void efx_tc_counter_update(struct efx_nic *efx, + enum efx_tc_counter_type counter_type, + u32 counter_idx, u64 packets, u64 bytes, + u32 mark) +{ + /* Software counter objects do not exist yet, for now we ignore this */ +} + +static void efx_tc_rx_version_1(struct efx_nic *efx, const u8 *data, u32 mark) +{ + u16 n_counters, i; + + /* Header format: + * + | 0 | 1 | 2 | 3 | + * 0 |version | reserved | + * 4 | seq_index | n_counters | + */ + + n_counters = le16_to_cpu(*(const __le16 *)(data + 6)); + + /* Counter update entry format: + * | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | a | b | c | d | e | f | + * | counter_idx | packet_count | byte_count | + */ + for (i = 0; i < n_counters; i++) { + const void *entry = data + 8 + 16 * i; + u64 packet_count, byte_count; + u32 counter_idx; + + counter_idx = le32_to_cpu(*(const __le32 *)entry); + packet_count = le32_to_cpu(*(const __le32 *)(entry + 4)) | + ((u64)le16_to_cpu(*(const __le16 *)(entry + 8)) << 32); + byte_count = le16_to_cpu(*(const __le16 *)(entry + 10)) | + ((u64)le32_to_cpu(*(const __le32 *)(entry + 12)) << 16); + efx_tc_counter_update(efx, EFX_TC_COUNTER_TYPE_AR, counter_idx, + packet_count, byte_count, mark); + } +} + +#define TCV2_HDR_PTR(pkt, field) \ + ((void)BUILD_BUG_ON_ZERO(ERF_SC_PACKETISER_HEADER_##field##_LBN & 7), \ + (pkt) + ERF_SC_PACKETISER_HEADER_##field##_LBN / 8) +#define TCV2_HDR_BYTE(pkt, field) \ + ((void)BUILD_BUG_ON_ZERO(ERF_SC_PACKETISER_HEADER_##field##_WIDTH != 8),\ + *TCV2_HDR_PTR(pkt, field)) +#define TCV2_HDR_WORD(pkt, field) \ + ((void)BUILD_BUG_ON_ZERO(ERF_SC_PACKETISER_HEADER_##field##_WIDTH != 16),\ + (void)BUILD_BUG_ON_ZERO(ERF_SC_PACKETISER_HEADER_##field##_LBN & 15), \ + *(__force const __le16 *)TCV2_HDR_PTR(pkt, field)) +#define TCV2_PKT_PTR(pkt, poff, i, field) \ + ((void)BUILD_BUG_ON_ZERO(ERF_SC_PACKETISER_PAYLOAD_##field##_LBN & 7), \ + (pkt) + ERF_SC_PACKETISER_PAYLOAD_##field##_LBN/8 + poff + \ + i * ER_RX_SL_PACKETISER_PAYLOAD_WORD_SIZE) + +/* Read a little-endian 48-bit field with 16-bit alignment */ +static u64 efx_tc_read48(const __le16 *field) +{ + u64 out = 0; + int i; + + for (i = 0; i < 3; i++) + out |= (u64)le16_to_cpu(field[i]) << (i * 16); + return out; +} + +static enum efx_tc_counter_type efx_tc_rx_version_2(struct efx_nic *efx, + const u8 *data, u32 mark) +{ + u8 payload_offset, header_offset, ident; + enum efx_tc_counter_type type; + u16 n_counters, i; + + ident = TCV2_HDR_BYTE(data, IDENTIFIER); + switch (ident) { + case ERF_SC_PACKETISER_HEADER_IDENTIFIER_AR: + type = EFX_TC_COUNTER_TYPE_AR; + break; + case ERF_SC_PACKETISER_HEADER_IDENTIFIER_CT: + type = EFX_TC_COUNTER_TYPE_CT; + break; + case ERF_SC_PACKETISER_HEADER_IDENTIFIER_OR: + type = EFX_TC_COUNTER_TYPE_OR; + break; + default: + if (net_ratelimit()) + netif_err(efx, drv, efx->net_dev, + "ignored v2 MAE counter packet (bad identifier %u" + "), counters may be inaccurate\n", ident); + return EFX_TC_COUNTER_TYPE_MAX; + } + header_offset = TCV2_HDR_BYTE(data, HEADER_OFFSET); + /* mae_counter_format.h implies that this offset is fixed, since it + * carries on with SOP-based LBNs for the fields in this header + */ + if (header_offset != ERF_SC_PACKETISER_HEADER_HEADER_OFFSET_DEFAULT) { + if (net_ratelimit()) + netif_err(efx, drv, efx->net_dev, + "choked on v2 MAE counter packet (bad header_offset %u" + "), counters may be inaccurate\n", header_offset); + return EFX_TC_COUNTER_TYPE_MAX; + } + payload_offset = TCV2_HDR_BYTE(data, PAYLOAD_OFFSET); + n_counters = le16_to_cpu(TCV2_HDR_WORD(data, COUNT)); + + for (i = 0; i < n_counters; i++) { + const void *counter_idx_p, *packet_count_p, *byte_count_p; + u64 packet_count, byte_count; + u32 counter_idx; + + /* 24-bit field with 32-bit alignment */ + counter_idx_p = TCV2_PKT_PTR(data, payload_offset, i, COUNTER_INDEX); + BUILD_BUG_ON(ERF_SC_PACKETISER_PAYLOAD_COUNTER_INDEX_WIDTH != 24); + BUILD_BUG_ON(ERF_SC_PACKETISER_PAYLOAD_COUNTER_INDEX_LBN & 31); + counter_idx = le32_to_cpu(*(const __le32 *)counter_idx_p) & 0xffffff; + /* 48-bit field with 16-bit alignment */ + packet_count_p = TCV2_PKT_PTR(data, payload_offset, i, PACKET_COUNT); + BUILD_BUG_ON(ERF_SC_PACKETISER_PAYLOAD_PACKET_COUNT_WIDTH != 48); + BUILD_BUG_ON(ERF_SC_PACKETISER_PAYLOAD_PACKET_COUNT_LBN & 15); + packet_count = efx_tc_read48((const __le16 *)packet_count_p); + /* 48-bit field with 16-bit alignment */ + byte_count_p = TCV2_PKT_PTR(data, payload_offset, i, BYTE_COUNT); + BUILD_BUG_ON(ERF_SC_PACKETISER_PAYLOAD_BYTE_COUNT_WIDTH != 48); + BUILD_BUG_ON(ERF_SC_PACKETISER_PAYLOAD_BYTE_COUNT_LBN & 15); + byte_count = efx_tc_read48((const __le16 *)byte_count_p); + + if (type == EFX_TC_COUNTER_TYPE_CT) { + /* CT counters are 1-bit saturating counters to update + * the lastuse time in CT stats. A received CT counter + * should have packet counter to 0 and only LSB bit on + * in byte counter. + */ + if (packet_count || byte_count != 1) + netdev_warn_once(efx->net_dev, + "CT counter with inconsistent state (%llu, %llu)\n", + packet_count, byte_count); + /* Do not increment the driver's byte counter */ + byte_count = 0; + } + + efx_tc_counter_update(efx, type, counter_idx, packet_count, + byte_count, mark); + } + return type; +} + +/* We always swallow the packet, whether successful or not, since it's not + * a network packet and shouldn't ever be forwarded to the stack. + * @mark is the generation count for counter allocations. + */ +static bool efx_tc_rx(struct efx_rx_queue *rx_queue, u32 mark) +{ + struct efx_channel *channel = efx_rx_queue_channel(rx_queue); + struct efx_rx_buffer *rx_buf = efx_rx_buffer(rx_queue, + channel->rx_pkt_index); + const u8 *data = efx_rx_buf_va(rx_buf); + struct efx_nic *efx = rx_queue->efx; + enum efx_tc_counter_type type; + u8 version; + + /* version is always first byte of packet */ + version = *data; + switch (version) { + case 1: + type = EFX_TC_COUNTER_TYPE_AR; + efx_tc_rx_version_1(efx, data, mark); + break; + case ERF_SC_PACKETISER_HEADER_VERSION_VALUE: // 2 + type = efx_tc_rx_version_2(efx, data, mark); + break; + default: + if (net_ratelimit()) + netif_err(efx, drv, efx->net_dev, + "choked on MAE counter packet (bad version %u" + "); counters may be inaccurate\n", + version); + goto out; + } + + /* Update seen_gen unconditionally, to avoid a missed wakeup if + * we race with efx_mae_stop_counters(). + */ + efx->tc->seen_gen[type] = mark; + if (efx->tc->flush_counters && + (s32)(efx->tc->flush_gen[type] - mark) <= 0) + wake_up(&efx->tc->flush_wq); +out: + efx_free_rx_buffers(rx_queue, rx_buf, 1); + channel->rx_pkt_n_frags = 0; + return true; +} + +const struct efx_channel_type efx_tc_channel_type = { + .handle_no_channel = efx_tc_handle_no_channel, + .pre_probe = efx_tc_probe_channel, + .start = efx_tc_start_channel, + .stop = efx_tc_stop_channel, + .post_remove = efx_tc_remove_channel, + .get_name = efx_tc_get_channel_name, + .receive_raw = efx_tc_rx, + .keep_eventq = true, +}; diff --git a/drivers/net/ethernet/sfc/tc_counters.h b/drivers/net/ethernet/sfc/tc_counters.h new file mode 100644 index 000000000000..400a39b00f01 --- /dev/null +++ b/drivers/net/ethernet/sfc/tc_counters.h @@ -0,0 +1,26 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/**************************************************************************** + * Driver for Solarflare network controllers and boards + * Copyright 2022 Advanced Micro Devices, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation, incorporated herein by reference. + */ + +#ifndef EFX_TC_COUNTERS_H +#define EFX_TC_COUNTERS_H +#include "net_driver.h" + +#include "mcdi_pcol.h" /* for MAE_COUNTER_TYPE_* */ + +enum efx_tc_counter_type { + EFX_TC_COUNTER_TYPE_AR = MAE_COUNTER_TYPE_AR, + EFX_TC_COUNTER_TYPE_CT = MAE_COUNTER_TYPE_CT, + EFX_TC_COUNTER_TYPE_OR = MAE_COUNTER_TYPE_OR, + EFX_TC_COUNTER_TYPE_MAX +}; + +extern const struct efx_channel_type efx_tc_channel_type; + +#endif /* EFX_TC_COUNTERS_H */ From 19a0c989104a08c07c97f8713d083cc635ef8d01 Mon Sep 17 00:00:00 2001 From: Edward Cree Date: Mon, 14 Nov 2022 13:15:56 +0000 Subject: [PATCH 0949/2002] sfc: add hashtables for MAE counters and counter ID mappings Nothing populates them yet. Signed-off-by: Edward Cree Signed-off-by: David S. Miller --- drivers/net/ethernet/sfc/tc.c | 6 +++ drivers/net/ethernet/sfc/tc.h | 4 ++ drivers/net/ethernet/sfc/tc_counters.c | 61 ++++++++++++++++++++++++++ drivers/net/ethernet/sfc/tc_counters.h | 19 ++++++++ 4 files changed, 90 insertions(+) diff --git a/drivers/net/ethernet/sfc/tc.c b/drivers/net/ethernet/sfc/tc.c index 37d56a1ba958..8ea7f5213049 100644 --- a/drivers/net/ethernet/sfc/tc.c +++ b/drivers/net/ethernet/sfc/tc.c @@ -752,6 +752,9 @@ int efx_init_struct_tc(struct efx_nic *efx) mutex_init(&efx->tc->mutex); init_waitqueue_head(&efx->tc->flush_wq); + rc = efx_tc_init_counters(efx); + if (rc < 0) + goto fail_counters; rc = rhashtable_init(&efx->tc->match_action_ht, &efx_tc_match_action_ht_params); if (rc < 0) goto fail_match_action_ht; @@ -764,6 +767,8 @@ int efx_init_struct_tc(struct efx_nic *efx) efx->extra_channel_type[EFX_EXTRA_CHANNEL_TC] = &efx_tc_channel_type; return 0; fail_match_action_ht: + efx_tc_destroy_counters(efx); +fail_counters: mutex_destroy(&efx->tc->mutex); kfree(efx->tc->caps); fail_alloc_caps: @@ -784,6 +789,7 @@ void efx_fini_struct_tc(struct efx_nic *efx) MC_CMD_MAE_ACTION_RULE_INSERT_OUT_ACTION_RULE_ID_NULL); rhashtable_free_and_destroy(&efx->tc->match_action_ht, efx_tc_flow_free, efx); + efx_tc_fini_counters(efx); mutex_unlock(&efx->tc->mutex); mutex_destroy(&efx->tc->mutex); kfree(efx->tc->caps); diff --git a/drivers/net/ethernet/sfc/tc.h b/drivers/net/ethernet/sfc/tc.h index 97dc06f2e694..6c8ebb2d79c0 100644 --- a/drivers/net/ethernet/sfc/tc.h +++ b/drivers/net/ethernet/sfc/tc.h @@ -75,6 +75,8 @@ enum efx_tc_rule_prios { * @caps: MAE capabilities reported by MCDI * @block_list: List of &struct efx_tc_block_binding * @mutex: Used to serialise operations on TC hashtables + * @counter_ht: Hashtable of TC counters (FW IDs and counter values) + * @counter_id_ht: Hashtable mapping TC counter cookies to counters * @match_action_ht: Hashtable of TC match-action rules * @reps_mport_id: MAE port allocated for representor RX * @reps_filter_uc: VNIC filter for representor unicast RX (promisc) @@ -96,6 +98,8 @@ struct efx_tc_state { struct mae_caps *caps; struct list_head block_list; struct mutex mutex; + struct rhashtable counter_ht; + struct rhashtable counter_id_ht; struct rhashtable match_action_ht; u32 reps_mport_id, reps_mport_vport_id; s32 reps_filter_uc, reps_filter_mc; diff --git a/drivers/net/ethernet/sfc/tc_counters.c b/drivers/net/ethernet/sfc/tc_counters.c index 4a310cd7f17f..9a4d1d2a1271 100644 --- a/drivers/net/ethernet/sfc/tc_counters.c +++ b/drivers/net/ethernet/sfc/tc_counters.c @@ -13,6 +13,67 @@ #include "mae.h" #include "rx_common.h" +/* Counter-management hashtables */ + +static const struct rhashtable_params efx_tc_counter_id_ht_params = { + .key_len = offsetof(struct efx_tc_counter_index, linkage), + .key_offset = 0, + .head_offset = offsetof(struct efx_tc_counter_index, linkage), +}; + +static const struct rhashtable_params efx_tc_counter_ht_params = { + .key_len = offsetof(struct efx_tc_counter, linkage), + .key_offset = 0, + .head_offset = offsetof(struct efx_tc_counter, linkage), +}; + +static void efx_tc_counter_free(void *ptr, void *__unused) +{ + struct efx_tc_counter *cnt = ptr; + + kfree(cnt); +} + +static void efx_tc_counter_id_free(void *ptr, void *__unused) +{ + struct efx_tc_counter_index *ctr = ptr; + + WARN_ON(refcount_read(&ctr->ref)); + kfree(ctr); +} + +int efx_tc_init_counters(struct efx_nic *efx) +{ + int rc; + + rc = rhashtable_init(&efx->tc->counter_id_ht, &efx_tc_counter_id_ht_params); + if (rc < 0) + goto fail_counter_id_ht; + rc = rhashtable_init(&efx->tc->counter_ht, &efx_tc_counter_ht_params); + if (rc < 0) + goto fail_counter_ht; + return 0; +fail_counter_ht: + rhashtable_destroy(&efx->tc->counter_id_ht); +fail_counter_id_ht: + return rc; +} + +/* Only call this in init failure teardown. + * Normal exit should fini instead as there may be entries in the table. + */ +void efx_tc_destroy_counters(struct efx_nic *efx) +{ + rhashtable_destroy(&efx->tc->counter_ht); + rhashtable_destroy(&efx->tc->counter_id_ht); +} + +void efx_tc_fini_counters(struct efx_nic *efx) +{ + rhashtable_free_and_destroy(&efx->tc->counter_id_ht, efx_tc_counter_id_free, NULL); + rhashtable_free_and_destroy(&efx->tc->counter_ht, efx_tc_counter_free, NULL); +} + /* TC Channel. Counter updates are delivered on this channel's RXQ. */ static void efx_tc_handle_no_channel(struct efx_nic *efx) diff --git a/drivers/net/ethernet/sfc/tc_counters.h b/drivers/net/ethernet/sfc/tc_counters.h index 400a39b00f01..f998cee324c7 100644 --- a/drivers/net/ethernet/sfc/tc_counters.h +++ b/drivers/net/ethernet/sfc/tc_counters.h @@ -10,6 +10,7 @@ #ifndef EFX_TC_COUNTERS_H #define EFX_TC_COUNTERS_H +#include #include "net_driver.h" #include "mcdi_pcol.h" /* for MAE_COUNTER_TYPE_* */ @@ -21,6 +22,24 @@ enum efx_tc_counter_type { EFX_TC_COUNTER_TYPE_MAX }; +struct efx_tc_counter { + u32 fw_id; /* index in firmware counter table */ + enum efx_tc_counter_type type; + struct rhash_head linkage; /* efx->tc->counter_ht */ +}; + +struct efx_tc_counter_index { + unsigned long cookie; + struct rhash_head linkage; /* efx->tc->counter_id_ht */ + refcount_t ref; + struct efx_tc_counter *cnt; +}; + +/* create/uncreate/teardown hashtables */ +int efx_tc_init_counters(struct efx_nic *efx); +void efx_tc_destroy_counters(struct efx_nic *efx); +void efx_tc_fini_counters(struct efx_nic *efx); + extern const struct efx_channel_type efx_tc_channel_type; #endif /* EFX_TC_COUNTERS_H */ From 0363aa295781a3ad160b2b562dc0460b331db06c Mon Sep 17 00:00:00 2001 From: Edward Cree Date: Mon, 14 Nov 2022 13:15:57 +0000 Subject: [PATCH 0950/2002] sfc: add functions to allocate/free MAE counters efx_tc_flower_get_counter_index() will create an MAE counter mapped to the passed (TC filter) cookie, or increment the reference if one already exists for that cookie. Signed-off-by: Edward Cree Signed-off-by: David S. Miller --- drivers/net/ethernet/sfc/mae.c | 51 ++++++++++++ drivers/net/ethernet/sfc/mae.h | 3 + drivers/net/ethernet/sfc/tc_counters.c | 109 +++++++++++++++++++++++++ drivers/net/ethernet/sfc/tc_counters.h | 7 ++ 4 files changed, 170 insertions(+) diff --git a/drivers/net/ethernet/sfc/mae.c b/drivers/net/ethernet/sfc/mae.c index 37722344c1cd..f227b4f2a9a0 100644 --- a/drivers/net/ethernet/sfc/mae.c +++ b/drivers/net/ethernet/sfc/mae.c @@ -434,6 +434,57 @@ int efx_mae_match_check_caps(struct efx_nic *efx, #undef CHECK_BIT #undef CHECK +int efx_mae_allocate_counter(struct efx_nic *efx, struct efx_tc_counter *cnt) +{ + MCDI_DECLARE_BUF(outbuf, MC_CMD_MAE_COUNTER_ALLOC_OUT_LEN(1)); + MCDI_DECLARE_BUF(inbuf, MC_CMD_MAE_COUNTER_ALLOC_V2_IN_LEN); + size_t outlen; + int rc; + + if (!cnt) + return -EINVAL; + + MCDI_SET_DWORD(inbuf, MAE_COUNTER_ALLOC_V2_IN_REQUESTED_COUNT, 1); + MCDI_SET_DWORD(inbuf, MAE_COUNTER_ALLOC_V2_IN_COUNTER_TYPE, cnt->type); + rc = efx_mcdi_rpc(efx, MC_CMD_MAE_COUNTER_ALLOC, inbuf, sizeof(inbuf), + outbuf, sizeof(outbuf), &outlen); + if (rc) + return rc; + /* pcol says this can't happen, since count is 1 */ + if (outlen < sizeof(outbuf)) + return -EIO; + cnt->fw_id = MCDI_DWORD(outbuf, MAE_COUNTER_ALLOC_OUT_COUNTER_ID); + cnt->gen = MCDI_DWORD(outbuf, MAE_COUNTER_ALLOC_OUT_GENERATION_COUNT); + return 0; +} + +int efx_mae_free_counter(struct efx_nic *efx, struct efx_tc_counter *cnt) +{ + MCDI_DECLARE_BUF(outbuf, MC_CMD_MAE_COUNTER_FREE_OUT_LEN(1)); + MCDI_DECLARE_BUF(inbuf, MC_CMD_MAE_COUNTER_FREE_V2_IN_LEN); + size_t outlen; + int rc; + + MCDI_SET_DWORD(inbuf, MAE_COUNTER_FREE_V2_IN_COUNTER_ID_COUNT, 1); + MCDI_SET_DWORD(inbuf, MAE_COUNTER_FREE_V2_IN_FREE_COUNTER_ID, cnt->fw_id); + MCDI_SET_DWORD(inbuf, MAE_COUNTER_FREE_V2_IN_COUNTER_TYPE, cnt->type); + rc = efx_mcdi_rpc(efx, MC_CMD_MAE_COUNTER_FREE, inbuf, sizeof(inbuf), + outbuf, sizeof(outbuf), &outlen); + if (rc) + return rc; + /* pcol says this can't happen, since count is 1 */ + if (outlen < sizeof(outbuf)) + return -EIO; + /* FW freed a different ID than we asked for, should also never happen. + * Warn because it means we've now got a different idea to the FW of + * what counters exist, which could cause mayhem later. + */ + if (WARN_ON(MCDI_DWORD(outbuf, MAE_COUNTER_FREE_OUT_FREED_COUNTER_ID) != + cnt->fw_id)) + return -EIO; + return 0; +} + static bool efx_mae_asl_id(u32 id) { return !!(id & BIT(31)); diff --git a/drivers/net/ethernet/sfc/mae.h b/drivers/net/ethernet/sfc/mae.h index 8f5de01dd962..72343e90e222 100644 --- a/drivers/net/ethernet/sfc/mae.h +++ b/drivers/net/ethernet/sfc/mae.h @@ -45,6 +45,9 @@ int efx_mae_match_check_caps(struct efx_nic *efx, const struct efx_tc_match_fields *mask, struct netlink_ext_ack *extack); +int efx_mae_allocate_counter(struct efx_nic *efx, struct efx_tc_counter *cnt); +int efx_mae_free_counter(struct efx_nic *efx, struct efx_tc_counter *cnt); + int efx_mae_alloc_action_set(struct efx_nic *efx, struct efx_tc_action_set *act); int efx_mae_free_action_set(struct efx_nic *efx, u32 fw_id); diff --git a/drivers/net/ethernet/sfc/tc_counters.c b/drivers/net/ethernet/sfc/tc_counters.c index 9a4d1d2a1271..6fd07ce61eb7 100644 --- a/drivers/net/ethernet/sfc/tc_counters.c +++ b/drivers/net/ethernet/sfc/tc_counters.c @@ -74,6 +74,115 @@ void efx_tc_fini_counters(struct efx_nic *efx) rhashtable_free_and_destroy(&efx->tc->counter_ht, efx_tc_counter_free, NULL); } +/* Counter allocation */ + +static struct efx_tc_counter *efx_tc_flower_allocate_counter(struct efx_nic *efx, + int type) +{ + struct efx_tc_counter *cnt; + int rc, rc2; + + cnt = kzalloc(sizeof(*cnt), GFP_USER); + if (!cnt) + return ERR_PTR(-ENOMEM); + + cnt->type = type; + + rc = efx_mae_allocate_counter(efx, cnt); + if (rc) + goto fail1; + rc = rhashtable_insert_fast(&efx->tc->counter_ht, &cnt->linkage, + efx_tc_counter_ht_params); + if (rc) + goto fail2; + return cnt; +fail2: + /* If we get here, it implies that we couldn't insert into the table, + * which in turn probably means that the fw_id was already taken. + * In that case, it's unclear whether we really 'own' the fw_id; but + * the firmware seemed to think we did, so it's proper to free it. + */ + rc2 = efx_mae_free_counter(efx, cnt); + if (rc2) + netif_warn(efx, hw, efx->net_dev, + "Failed to free MAE counter %u, rc %d\n", + cnt->fw_id, rc2); +fail1: + kfree(cnt); + return ERR_PTR(rc > 0 ? -EIO : rc); +} + +static void efx_tc_flower_release_counter(struct efx_nic *efx, + struct efx_tc_counter *cnt) +{ + int rc; + + rhashtable_remove_fast(&efx->tc->counter_ht, &cnt->linkage, + efx_tc_counter_ht_params); + rc = efx_mae_free_counter(efx, cnt); + if (rc) + netif_warn(efx, hw, efx->net_dev, + "Failed to free MAE counter %u, rc %d\n", + cnt->fw_id, rc); + /* This doesn't protect counter updates coming in arbitrarily long + * after we deleted the counter. The RCU just ensures that we won't + * free the counter while another thread has a pointer to it. + * Ensuring we don't update the wrong counter if the ID gets re-used + * is handled by the generation count. + */ + synchronize_rcu(); + kfree(cnt); +} + +/* TC cookie to counter mapping */ + +void efx_tc_flower_put_counter_index(struct efx_nic *efx, + struct efx_tc_counter_index *ctr) +{ + if (!refcount_dec_and_test(&ctr->ref)) + return; /* still in use */ + rhashtable_remove_fast(&efx->tc->counter_id_ht, &ctr->linkage, + efx_tc_counter_id_ht_params); + efx_tc_flower_release_counter(efx, ctr->cnt); + kfree(ctr); +} + +struct efx_tc_counter_index *efx_tc_flower_get_counter_index( + struct efx_nic *efx, unsigned long cookie, + enum efx_tc_counter_type type) +{ + struct efx_tc_counter_index *ctr, *old; + struct efx_tc_counter *cnt; + + ctr = kzalloc(sizeof(*ctr), GFP_USER); + if (!ctr) + return ERR_PTR(-ENOMEM); + ctr->cookie = cookie; + old = rhashtable_lookup_get_insert_fast(&efx->tc->counter_id_ht, + &ctr->linkage, + efx_tc_counter_id_ht_params); + if (old) { + /* don't need our new entry */ + kfree(ctr); + if (!refcount_inc_not_zero(&old->ref)) + return ERR_PTR(-EAGAIN); + /* existing entry found */ + ctr = old; + } else { + cnt = efx_tc_flower_allocate_counter(efx, type); + if (IS_ERR(cnt)) { + rhashtable_remove_fast(&efx->tc->counter_id_ht, + &ctr->linkage, + efx_tc_counter_id_ht_params); + kfree(ctr); + return (void *)cnt; /* it's an ERR_PTR */ + } + ctr->cnt = cnt; + refcount_set(&ctr->ref, 1); + } + return ctr; +} + /* TC Channel. Counter updates are delivered on this channel's RXQ. */ static void efx_tc_handle_no_channel(struct efx_nic *efx) diff --git a/drivers/net/ethernet/sfc/tc_counters.h b/drivers/net/ethernet/sfc/tc_counters.h index f998cee324c7..85f4919271eb 100644 --- a/drivers/net/ethernet/sfc/tc_counters.h +++ b/drivers/net/ethernet/sfc/tc_counters.h @@ -26,6 +26,7 @@ struct efx_tc_counter { u32 fw_id; /* index in firmware counter table */ enum efx_tc_counter_type type; struct rhash_head linkage; /* efx->tc->counter_ht */ + u32 gen; /* Generation count at which this counter is current */ }; struct efx_tc_counter_index { @@ -40,6 +41,12 @@ int efx_tc_init_counters(struct efx_nic *efx); void efx_tc_destroy_counters(struct efx_nic *efx); void efx_tc_fini_counters(struct efx_nic *efx); +struct efx_tc_counter_index *efx_tc_flower_get_counter_index( + struct efx_nic *efx, unsigned long cookie, + enum efx_tc_counter_type type); +void efx_tc_flower_put_counter_index(struct efx_nic *efx, + struct efx_tc_counter_index *ctr); + extern const struct efx_channel_type efx_tc_channel_type; #endif /* EFX_TC_COUNTERS_H */ From c4bad432b95a91a4f123159e5b8220bd026dc166 Mon Sep 17 00:00:00 2001 From: Edward Cree Date: Mon, 14 Nov 2022 13:15:58 +0000 Subject: [PATCH 0951/2002] sfc: accumulate MAE counter values from update packets Add the packet and byte counts to the software running total, and store the latest jiffies every time the counter is bumped. Signed-off-by: Edward Cree Signed-off-by: David S. Miller --- drivers/net/ethernet/sfc/tc_counters.c | 54 +++++++++++++++++++++++++- drivers/net/ethernet/sfc/tc_counters.h | 4 ++ 2 files changed, 57 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/sfc/tc_counters.c b/drivers/net/ethernet/sfc/tc_counters.c index 6fd07ce61eb7..76a2e8ac517a 100644 --- a/drivers/net/ethernet/sfc/tc_counters.c +++ b/drivers/net/ethernet/sfc/tc_counters.c @@ -86,6 +86,8 @@ static struct efx_tc_counter *efx_tc_flower_allocate_counter(struct efx_nic *efx if (!cnt) return ERR_PTR(-ENOMEM); + spin_lock_init(&cnt->lock); + cnt->touched = jiffies; cnt->type = type; rc = efx_mae_allocate_counter(efx, cnt); @@ -131,9 +133,22 @@ static void efx_tc_flower_release_counter(struct efx_nic *efx, * is handled by the generation count. */ synchronize_rcu(); + EFX_WARN_ON_PARANOID(spin_is_locked(&cnt->lock)); kfree(cnt); } +static struct efx_tc_counter *efx_tc_flower_find_counter_by_fw_id( + struct efx_nic *efx, int type, u32 fw_id) +{ + struct efx_tc_counter key = {}; + + key.fw_id = fw_id; + key.type = type; + + return rhashtable_lookup_fast(&efx->tc->counter_ht, &key, + efx_tc_counter_ht_params); +} + /* TC cookie to counter mapping */ void efx_tc_flower_put_counter_index(struct efx_nic *efx, @@ -241,7 +256,44 @@ static void efx_tc_counter_update(struct efx_nic *efx, u32 counter_idx, u64 packets, u64 bytes, u32 mark) { - /* Software counter objects do not exist yet, for now we ignore this */ + struct efx_tc_counter *cnt; + + rcu_read_lock(); /* Protect against deletion of 'cnt' */ + cnt = efx_tc_flower_find_counter_by_fw_id(efx, counter_type, counter_idx); + if (!cnt) { + /* This can legitimately happen when a counter is removed, + * with updates for the counter still in-flight; however this + * should be an infrequent occurrence. + */ + if (net_ratelimit()) + netif_dbg(efx, drv, efx->net_dev, + "Got update for unwanted MAE counter %u type %u\n", + counter_idx, counter_type); + goto out; + } + + spin_lock_bh(&cnt->lock); + if ((s32)mark - (s32)cnt->gen < 0) { + /* This counter update packet is from before the counter was + * allocated; thus it must be for a previous counter with + * the same ID that has since been freed, and it should be + * ignored. + */ + } else { + /* Update latest seen generation count. This ensures that + * even a long-lived counter won't start getting ignored if + * the generation count wraps around, unless it somehow + * manages to go 1<<31 generations without an update. + */ + cnt->gen = mark; + /* update counter values */ + cnt->packets += packets; + cnt->bytes += bytes; + cnt->touched = jiffies; + } + spin_unlock_bh(&cnt->lock); +out: + rcu_read_unlock(); } static void efx_tc_rx_version_1(struct efx_nic *efx, const u8 *data, u32 mark) diff --git a/drivers/net/ethernet/sfc/tc_counters.h b/drivers/net/ethernet/sfc/tc_counters.h index 85f4919271eb..a5a6d8cb1365 100644 --- a/drivers/net/ethernet/sfc/tc_counters.h +++ b/drivers/net/ethernet/sfc/tc_counters.h @@ -26,7 +26,11 @@ struct efx_tc_counter { u32 fw_id; /* index in firmware counter table */ enum efx_tc_counter_type type; struct rhash_head linkage; /* efx->tc->counter_ht */ + spinlock_t lock; /* Serialises updates to counter values */ u32 gen; /* Generation count at which this counter is current */ + u64 packets, bytes; + /* jiffies of the last time we saw packets increase */ + unsigned long touched; }; struct efx_tc_counter_index { From 2e0f1eb05692b489bb9fdd30617b52022a93b2e3 Mon Sep 17 00:00:00 2001 From: Edward Cree Date: Mon, 14 Nov 2022 13:15:59 +0000 Subject: [PATCH 0952/2002] sfc: attach an MAE counter to TC actions that need it The only actions that expect stats (that sfc HW supports) are gact shot (drop), mirred redirect and mirred mirror. Since these are 'deliverish' actions that end an action-set, we only require at most one counter per action-set. Signed-off-by: Edward Cree Signed-off-by: David S. Miller --- drivers/net/ethernet/sfc/mae.c | 8 ++++++-- drivers/net/ethernet/sfc/tc.c | 25 +++++++++++++++++++++++++ drivers/net/ethernet/sfc/tc.h | 1 + 3 files changed, 32 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/sfc/mae.c b/drivers/net/ethernet/sfc/mae.c index f227b4f2a9a0..583baf69981c 100644 --- a/drivers/net/ethernet/sfc/mae.c +++ b/drivers/net/ethernet/sfc/mae.c @@ -501,8 +501,12 @@ int efx_mae_alloc_action_set(struct efx_nic *efx, struct efx_tc_action_set *act) MC_CMD_MAE_MAC_ADDR_ALLOC_OUT_MAC_ID_NULL); MCDI_SET_DWORD(inbuf, MAE_ACTION_SET_ALLOC_IN_DST_MAC_ID, MC_CMD_MAE_MAC_ADDR_ALLOC_OUT_MAC_ID_NULL); - MCDI_SET_DWORD(inbuf, MAE_ACTION_SET_ALLOC_IN_COUNTER_ID, - MC_CMD_MAE_COUNTER_ALLOC_OUT_COUNTER_ID_NULL); + if (act->count && !WARN_ON(!act->count->cnt)) + MCDI_SET_DWORD(inbuf, MAE_ACTION_SET_ALLOC_IN_COUNTER_ID, + act->count->cnt->fw_id); + else + MCDI_SET_DWORD(inbuf, MAE_ACTION_SET_ALLOC_IN_COUNTER_ID, + MC_CMD_MAE_COUNTER_ALLOC_OUT_COUNTER_ID_NULL); MCDI_SET_DWORD(inbuf, MAE_ACTION_SET_ALLOC_IN_COUNTER_LIST_ID, MC_CMD_MAE_COUNTER_LIST_ALLOC_OUT_COUNTER_LIST_ID_NULL); MCDI_SET_DWORD(inbuf, MAE_ACTION_SET_ALLOC_IN_ENCAP_HEADER_ID, diff --git a/drivers/net/ethernet/sfc/tc.c b/drivers/net/ethernet/sfc/tc.c index 8ea7f5213049..1cfc50f2398e 100644 --- a/drivers/net/ethernet/sfc/tc.c +++ b/drivers/net/ethernet/sfc/tc.c @@ -77,6 +77,8 @@ static void efx_tc_free_action_set(struct efx_nic *efx, */ list_del(&act->list); } + if (act->count) + efx_tc_flower_put_counter_index(efx, act->count); kfree(act); } @@ -376,6 +378,28 @@ static int efx_tc_flower_replace(struct efx_nic *efx, goto release; } + if ((fa->id == FLOW_ACTION_REDIRECT || + fa->id == FLOW_ACTION_MIRRED || + fa->id == FLOW_ACTION_DROP) && fa->hw_stats) { + struct efx_tc_counter_index *ctr; + + if (!(fa->hw_stats & FLOW_ACTION_HW_STATS_DELAYED)) { + NL_SET_ERR_MSG_FMT_MOD(extack, "hw_stats_type %u not supported (only 'delayed')", + fa->hw_stats); + rc = -EOPNOTSUPP; + goto release; + } + + ctr = efx_tc_flower_get_counter_index(efx, tc->cookie, + EFX_TC_COUNTER_TYPE_AR); + if (IS_ERR(ctr)) { + rc = PTR_ERR(ctr); + NL_SET_ERR_MSG_MOD(extack, "Failed to obtain a counter"); + goto release; + } + act->count = ctr; + } + switch (fa->id) { case FLOW_ACTION_DROP: rc = efx_mae_alloc_action_set(efx, act); @@ -412,6 +436,7 @@ static int efx_tc_flower_replace(struct efx_nic *efx, if (fa->id == FLOW_ACTION_REDIRECT) break; /* end of the line */ /* Mirror, so continue on with saved act */ + save.count = NULL; act = kzalloc(sizeof(*act), GFP_USER); if (!act) { rc = -ENOMEM; diff --git a/drivers/net/ethernet/sfc/tc.h b/drivers/net/ethernet/sfc/tc.h index 6c8ebb2d79c0..418ce8c13a06 100644 --- a/drivers/net/ethernet/sfc/tc.h +++ b/drivers/net/ethernet/sfc/tc.h @@ -20,6 +20,7 @@ struct efx_tc_action_set { u16 deliver:1; + struct efx_tc_counter_index *count; u32 dest_mport; u32 fw_id; /* index of this entry in firmware actions table */ struct list_head list; From 83a187a4eb3a8d7b747e7cfd48228dbc4dbb5c92 Mon Sep 17 00:00:00 2001 From: Edward Cree Date: Mon, 14 Nov 2022 13:16:00 +0000 Subject: [PATCH 0953/2002] sfc: validate MAE action order Currently the only actions supported are COUNT and DELIVER, which can only happen in the right order; but when more actions are added, it will be necessary to check that they are only used in the same order in which the hardware performs them (since the hardware API takes an action *set* in which the order is implicit). For instance, a VLAN pop must not follow a VLAN push. Most practical use-cases should be unaffected by these restrictions. Add a function efx_tc_flower_action_order_ok() that checks whether it is appropriate to add a specified action to the existing action-set. Signed-off-by: Edward Cree Signed-off-by: David S. Miller --- drivers/net/ethernet/sfc/tc.c | 50 +++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/drivers/net/ethernet/sfc/tc.c b/drivers/net/ethernet/sfc/tc.c index 1cfc50f2398e..bf4979007f31 100644 --- a/drivers/net/ethernet/sfc/tc.c +++ b/drivers/net/ethernet/sfc/tc.c @@ -284,6 +284,29 @@ static int efx_tc_flower_parse_match(struct efx_nic *efx, return 0; } +/* For details of action order constraints refer to SF-123102-TC-1§12.6.1 */ +enum efx_tc_action_order { + EFX_TC_AO_COUNT, + EFX_TC_AO_DELIVER +}; +/* Determine whether we can add @new action without violating order */ +static bool efx_tc_flower_action_order_ok(const struct efx_tc_action_set *act, + enum efx_tc_action_order new) +{ + switch (new) { + case EFX_TC_AO_COUNT: + if (act->count) + return false; + fallthrough; + case EFX_TC_AO_DELIVER: + return !act->deliver; + default: + /* Bad caller. Whatever they wanted to do, say they can't. */ + WARN_ON_ONCE(1); + return false; + } +} + static int efx_tc_flower_replace(struct efx_nic *efx, struct net_device *net_dev, struct flow_cls_offload *tc, @@ -383,6 +406,25 @@ static int efx_tc_flower_replace(struct efx_nic *efx, fa->id == FLOW_ACTION_DROP) && fa->hw_stats) { struct efx_tc_counter_index *ctr; + /* Currently the only actions that want stats are + * mirred and gact (ok, shot, trap, goto-chain), which + * means we want stats just before delivery. Also, + * note that tunnel_key set shouldn't change the length + * — it's only the subsequent mirred that does that, + * and the stats are taken _before_ the mirred action + * happens. + */ + if (!efx_tc_flower_action_order_ok(act, EFX_TC_AO_COUNT)) { + /* All supported actions that count either steal + * (gact shot, mirred redirect) or clone act + * (mirred mirror), so we should never get two + * count actions on one action_set. + */ + NL_SET_ERR_MSG_MOD(extack, "Count-action conflict (can't happen)"); + rc = -EOPNOTSUPP; + goto release; + } + if (!(fa->hw_stats & FLOW_ACTION_HW_STATS_DELAYED)) { NL_SET_ERR_MSG_FMT_MOD(extack, "hw_stats_type %u not supported (only 'delayed')", fa->hw_stats); @@ -413,6 +455,14 @@ static int efx_tc_flower_replace(struct efx_nic *efx, case FLOW_ACTION_REDIRECT: case FLOW_ACTION_MIRRED: save = *act; + + if (!efx_tc_flower_action_order_ok(act, EFX_TC_AO_DELIVER)) { + /* can't happen */ + rc = -EOPNOTSUPP; + NL_SET_ERR_MSG_MOD(extack, "Deliver action violates action order (can't happen)"); + goto release; + } + to_efv = efx_tc_flower_lookup_efv(efx, fa->dev); if (IS_ERR(to_efv)) { NL_SET_ERR_MSG_MOD(extack, "Mirred egress device not on switch"); From 50f8f2f7fbf2f237a7938890f46c3edce0f51501 Mon Sep 17 00:00:00 2001 From: Edward Cree Date: Mon, 14 Nov 2022 13:16:01 +0000 Subject: [PATCH 0954/2002] sfc: implement counters readout to TC stats On FLOW_CLS_STATS, look up the MAE counter by TC cookie, and report the change in packet and byte count since the last time FLOW_CLS_STATS read them. Signed-off-by: Edward Cree Signed-off-by: David S. Miller --- drivers/net/ethernet/sfc/tc.c | 39 ++++++++++++++++++++++++++ drivers/net/ethernet/sfc/tc_counters.c | 10 +++++++ drivers/net/ethernet/sfc/tc_counters.h | 3 ++ 3 files changed, 52 insertions(+) diff --git a/drivers/net/ethernet/sfc/tc.c b/drivers/net/ethernet/sfc/tc.c index bf4979007f31..deeaab9ee761 100644 --- a/drivers/net/ethernet/sfc/tc.c +++ b/drivers/net/ethernet/sfc/tc.c @@ -595,6 +595,42 @@ static int efx_tc_flower_destroy(struct efx_nic *efx, return 0; } +static int efx_tc_flower_stats(struct efx_nic *efx, struct net_device *net_dev, + struct flow_cls_offload *tc) +{ + struct netlink_ext_ack *extack = tc->common.extack; + struct efx_tc_counter_index *ctr; + struct efx_tc_counter *cnt; + u64 packets, bytes; + + ctr = efx_tc_flower_find_counter_index(efx, tc->cookie); + if (!ctr) { + /* See comment in efx_tc_flower_destroy() */ + if (!IS_ERR(efx_tc_flower_lookup_efv(efx, net_dev))) + if (net_ratelimit()) + netif_warn(efx, drv, efx->net_dev, + "Filter %lx not found for stats\n", + tc->cookie); + NL_SET_ERR_MSG_MOD(extack, "Flow cookie not found in offloaded rules"); + return -ENOENT; + } + if (WARN_ON(!ctr->cnt)) /* can't happen */ + return -EIO; + cnt = ctr->cnt; + + spin_lock_bh(&cnt->lock); + /* Report only new pkts/bytes since last time TC asked */ + packets = cnt->packets; + bytes = cnt->bytes; + flow_stats_update(&tc->stats, bytes - cnt->old_bytes, + packets - cnt->old_packets, 0, cnt->touched, + FLOW_ACTION_HW_STATS_DELAYED); + cnt->old_packets = packets; + cnt->old_bytes = bytes; + spin_unlock_bh(&cnt->lock); + return 0; +} + int efx_tc_flower(struct efx_nic *efx, struct net_device *net_dev, struct flow_cls_offload *tc, struct efx_rep *efv) { @@ -611,6 +647,9 @@ int efx_tc_flower(struct efx_nic *efx, struct net_device *net_dev, case FLOW_CLS_DESTROY: rc = efx_tc_flower_destroy(efx, net_dev, tc); break; + case FLOW_CLS_STATS: + rc = efx_tc_flower_stats(efx, net_dev, tc); + break; default: rc = -EOPNOTSUPP; break; diff --git a/drivers/net/ethernet/sfc/tc_counters.c b/drivers/net/ethernet/sfc/tc_counters.c index 76a2e8ac517a..2bba5d3a2fdb 100644 --- a/drivers/net/ethernet/sfc/tc_counters.c +++ b/drivers/net/ethernet/sfc/tc_counters.c @@ -198,6 +198,16 @@ struct efx_tc_counter_index *efx_tc_flower_get_counter_index( return ctr; } +struct efx_tc_counter_index *efx_tc_flower_find_counter_index( + struct efx_nic *efx, unsigned long cookie) +{ + struct efx_tc_counter_index key = {}; + + key.cookie = cookie; + return rhashtable_lookup_fast(&efx->tc->counter_id_ht, &key, + efx_tc_counter_id_ht_params); +} + /* TC Channel. Counter updates are delivered on this channel's RXQ. */ static void efx_tc_handle_no_channel(struct efx_nic *efx) diff --git a/drivers/net/ethernet/sfc/tc_counters.h b/drivers/net/ethernet/sfc/tc_counters.h index a5a6d8cb1365..8fc7c4bbb29c 100644 --- a/drivers/net/ethernet/sfc/tc_counters.h +++ b/drivers/net/ethernet/sfc/tc_counters.h @@ -29,6 +29,7 @@ struct efx_tc_counter { spinlock_t lock; /* Serialises updates to counter values */ u32 gen; /* Generation count at which this counter is current */ u64 packets, bytes; + u64 old_packets, old_bytes; /* Values last time passed to userspace */ /* jiffies of the last time we saw packets increase */ unsigned long touched; }; @@ -50,6 +51,8 @@ struct efx_tc_counter_index *efx_tc_flower_get_counter_index( enum efx_tc_counter_type type); void efx_tc_flower_put_counter_index(struct efx_nic *efx, struct efx_tc_counter_index *ctr); +struct efx_tc_counter_index *efx_tc_flower_find_counter_index( + struct efx_nic *efx, unsigned long cookie); extern const struct efx_channel_type efx_tc_channel_type; From bcfcb712e41401887930361946666549e8ba7747 Mon Sep 17 00:00:00 2001 From: Bitterblue Smith Date: Sun, 6 Nov 2022 00:53:17 +0200 Subject: [PATCH 0955/2002] wifi: rtl8xxxu: Move burst init to a function No changes to functionality, just moving code to make rtl8xxxu_init_device look nicer. Signed-off-by: Bitterblue Smith Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/bef90bf8-716f-c92f-9403-12ef2bfefc15@gmail.com --- .../net/wireless/realtek/rtl8xxxu/rtl8xxxu.h | 2 + .../realtek/rtl8xxxu/rtl8xxxu_8188f.c | 1 + .../realtek/rtl8xxxu/rtl8xxxu_8723b.c | 1 + .../wireless/realtek/rtl8xxxu/rtl8xxxu_core.c | 89 ++++++++++--------- 4 files changed, 52 insertions(+), 41 deletions(-) diff --git a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h index 6ff554ba0250..9f8b23160ed0 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h +++ b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h @@ -1485,6 +1485,7 @@ struct rtl8xxxu_fileops { int (*parse_rx_desc) (struct rtl8xxxu_priv *priv, struct sk_buff *skb); void (*init_aggregation) (struct rtl8xxxu_priv *priv); void (*init_statistics) (struct rtl8xxxu_priv *priv); + void (*init_burst) (struct rtl8xxxu_priv *priv); void (*enable_rf) (struct rtl8xxxu_priv *priv); void (*disable_rf) (struct rtl8xxxu_priv *priv); void (*usb_quirks) (struct rtl8xxxu_priv *priv); @@ -1592,6 +1593,7 @@ void rtl8xxxu_gen1_init_aggregation(struct rtl8xxxu_priv *priv); void rtl8xxxu_gen1_enable_rf(struct rtl8xxxu_priv *priv); void rtl8xxxu_gen1_disable_rf(struct rtl8xxxu_priv *priv); void rtl8xxxu_gen2_disable_rf(struct rtl8xxxu_priv *priv); +void rtl8xxxu_init_burst(struct rtl8xxxu_priv *priv); int rtl8xxxu_parse_rxdesc16(struct rtl8xxxu_priv *priv, struct sk_buff *skb); int rtl8xxxu_parse_rxdesc24(struct rtl8xxxu_priv *priv, struct sk_buff *skb); int rtl8xxxu_gen2_channel_to_group(int channel); diff --git a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8188f.c b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8188f.c index 5a5b7fa4283c..5eadeb02a762 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8188f.c +++ b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8188f.c @@ -1705,6 +1705,7 @@ struct rtl8xxxu_fileops rtl8188fu_fops = { .parse_rx_desc = rtl8xxxu_parse_rxdesc24, .init_aggregation = rtl8188fu_init_aggregation, .init_statistics = rtl8188fu_init_statistics, + .init_burst = rtl8xxxu_init_burst, .enable_rf = rtl8188f_enable_rf, .disable_rf = rtl8188f_disable_rf, .usb_quirks = rtl8188f_usb_quirks, diff --git a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8723b.c b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8723b.c index 9214c1d3b644..27df8805cb18 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8723b.c +++ b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8723b.c @@ -1683,6 +1683,7 @@ struct rtl8xxxu_fileops rtl8723bu_fops = { .parse_rx_desc = rtl8xxxu_parse_rxdesc24, .init_aggregation = rtl8723bu_init_aggregation, .init_statistics = rtl8723bu_init_statistics, + .init_burst = rtl8xxxu_init_burst, .enable_rf = rtl8723b_enable_rf, .disable_rf = rtl8xxxu_gen2_disable_rf, .usb_quirks = rtl8xxxu_gen2_usb_quirks, diff --git a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c index 019f8ddd418b..282ad8a9b73d 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c +++ b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c @@ -3886,6 +3886,52 @@ static void rtl8xxxu_init_queue_reserved_page(struct rtl8xxxu_priv *priv) rtl8xxxu_write32(priv, REG_RQPN, val32); } +void rtl8xxxu_init_burst(struct rtl8xxxu_priv *priv) +{ + u8 val8; + + /* + * For USB high speed set 512B packets + */ + val8 = rtl8xxxu_read8(priv, REG_RXDMA_PRO_8723B); + val8 &= ~(BIT(4) | BIT(5)); + val8 |= BIT(4); + val8 |= BIT(1) | BIT(2) | BIT(3); + rtl8xxxu_write8(priv, REG_RXDMA_PRO_8723B, val8); + + /* + * Enable single packet AMPDU + */ + val8 = rtl8xxxu_read8(priv, REG_HT_SINGLE_AMPDU_8723B); + val8 |= BIT(7); + rtl8xxxu_write8(priv, REG_HT_SINGLE_AMPDU_8723B, val8); + + rtl8xxxu_write16(priv, REG_MAX_AGGR_NUM, 0x0c14); + if (priv->rtl_chip == RTL8723B) + val8 = 0x5e; + else if (priv->rtl_chip == RTL8188F) + val8 = 0x70; /* 0x5e would make it very slow */ + rtl8xxxu_write8(priv, REG_AMPDU_MAX_TIME_8723B, val8); + rtl8xxxu_write32(priv, REG_AGGLEN_LMT, 0xffffffff); + rtl8xxxu_write8(priv, REG_RX_PKT_LIMIT, 0x18); + rtl8xxxu_write8(priv, REG_PIFS, 0x00); + if (priv->rtl_chip == RTL8188F) { + rtl8xxxu_write8(priv, REG_FWHW_TXQ_CTRL, FWHW_TXQ_CTRL_AMPDU_RETRY); + rtl8xxxu_write32(priv, REG_FAST_EDCA_CTRL, 0x03086666); + } + if (priv->rtl_chip == RTL8723B) + val8 = 0x50; + else if (priv->rtl_chip == RTL8188F) + val8 = 0x28; /* 0x50 would make the upload slow */ + rtl8xxxu_write8(priv, REG_USTIME_TSF_8723B, val8); + rtl8xxxu_write8(priv, REG_USTIME_EDCA, val8); + + /* to prevent mac is reseted by bus. */ + val8 = rtl8xxxu_read8(priv, REG_RSV_CTRL); + val8 |= BIT(5) | BIT(6); + rtl8xxxu_write8(priv, REG_RSV_CTRL, val8); +} + static int rtl8xxxu_init_device(struct ieee80211_hw *hw) { struct rtl8xxxu_priv *priv = hw->priv; @@ -4139,48 +4185,9 @@ static int rtl8xxxu_init_device(struct ieee80211_hw *hw) /* * Initialize burst parameters */ - if (priv->rtl_chip == RTL8723B || priv->rtl_chip == RTL8188F) { - /* - * For USB high speed set 512B packets - */ - val8 = rtl8xxxu_read8(priv, REG_RXDMA_PRO_8723B); - val8 &= ~(BIT(4) | BIT(5)); - val8 |= BIT(4); - val8 |= BIT(1) | BIT(2) | BIT(3); - rtl8xxxu_write8(priv, REG_RXDMA_PRO_8723B, val8); - /* - * For USB high speed set 512B packets - */ - val8 = rtl8xxxu_read8(priv, REG_HT_SINGLE_AMPDU_8723B); - val8 |= BIT(7); - rtl8xxxu_write8(priv, REG_HT_SINGLE_AMPDU_8723B, val8); - - rtl8xxxu_write16(priv, REG_MAX_AGGR_NUM, 0x0c14); - if (priv->rtl_chip == RTL8723B) - val8 = 0x5e; - else if (priv->rtl_chip == RTL8188F) - val8 = 0x70; /* 0x5e would make it very slow */ - rtl8xxxu_write8(priv, REG_AMPDU_MAX_TIME_8723B, val8); - rtl8xxxu_write32(priv, REG_AGGLEN_LMT, 0xffffffff); - rtl8xxxu_write8(priv, REG_RX_PKT_LIMIT, 0x18); - rtl8xxxu_write8(priv, REG_PIFS, 0x00); - if (priv->rtl_chip == RTL8188F) { - rtl8xxxu_write8(priv, REG_FWHW_TXQ_CTRL, FWHW_TXQ_CTRL_AMPDU_RETRY); - rtl8xxxu_write32(priv, REG_FAST_EDCA_CTRL, 0x03086666); - } - if (priv->rtl_chip == RTL8723B) - val8 = 0x50; - else if (priv->rtl_chip == RTL8188F) - val8 = 0x28; /* 0x50 would make the upload slow */ - rtl8xxxu_write8(priv, REG_USTIME_TSF_8723B, val8); - rtl8xxxu_write8(priv, REG_USTIME_EDCA, val8); - - /* to prevent mac is reseted by bus. */ - val8 = rtl8xxxu_read8(priv, REG_RSV_CTRL); - val8 |= BIT(5) | BIT(6); - rtl8xxxu_write8(priv, REG_RSV_CTRL, val8); - } + if (priv->fops->init_burst) + priv->fops->init_burst(priv); if (fops->init_aggregation) fops->init_aggregation(priv); From 14566bbfdfaf3784f43ea9ca6769463dd6bed1e2 Mon Sep 17 00:00:00 2001 From: Bitterblue Smith Date: Sun, 6 Nov 2022 00:54:22 +0200 Subject: [PATCH 0956/2002] wifi: rtl8xxxu: Split up rtl8xxxu_identify_chip Move the reusable parts into separate functions and create one identify_chip function for each chip type. This is preparation for supporting the RTL8710BU chip, which would need too many ugly changes to this function. Another reason to do this is to get rid of the long and scary if..else if..else block in the middle of the function. Everything should still work the same as before. Signed-off-by: Bitterblue Smith Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/b268b5cf-071c-6292-0d90-0573e4fb2228@gmail.com --- .../net/wireless/realtek/rtl8xxxu/rtl8xxxu.h | 5 + .../realtek/rtl8xxxu/rtl8xxxu_8188f.c | 32 +++ .../realtek/rtl8xxxu/rtl8xxxu_8192c.c | 60 +++++ .../realtek/rtl8xxxu/rtl8xxxu_8192e.c | 49 +++++ .../realtek/rtl8xxxu/rtl8xxxu_8723a.c | 51 +++++ .../realtek/rtl8xxxu/rtl8xxxu_8723b.c | 49 +++++ .../wireless/realtek/rtl8xxxu/rtl8xxxu_core.c | 207 +++++------------- 7 files changed, 297 insertions(+), 156 deletions(-) diff --git a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h index 9f8b23160ed0..136992f0200c 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h +++ b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h @@ -1470,6 +1470,7 @@ struct rtl8xxxu_tx_urb { }; struct rtl8xxxu_fileops { + int (*identify_chip) (struct rtl8xxxu_priv *priv); int (*parse_efuse) (struct rtl8xxxu_priv *priv); int (*load_firmware) (struct rtl8xxxu_priv *priv); int (*power_on) (struct rtl8xxxu_priv *priv); @@ -1562,6 +1563,10 @@ int rtl8xxxu_init_phy_regs(struct rtl8xxxu_priv *priv, int rtl8xxxu_load_firmware(struct rtl8xxxu_priv *priv, char *fw_name); void rtl8xxxu_firmware_self_reset(struct rtl8xxxu_priv *priv); void rtl8xxxu_power_off(struct rtl8xxxu_priv *priv); +void rtl8xxxu_identify_vendor_1bit(struct rtl8xxxu_priv *priv, u32 vendor); +void rtl8xxxu_identify_vendor_2bits(struct rtl8xxxu_priv *priv, u32 vendor); +void rtl8xxxu_config_endpoints_sie(struct rtl8xxxu_priv *priv); +int rtl8xxxu_config_endpoints_no_sie(struct rtl8xxxu_priv *priv); int rtl8xxxu_read_efuse8(struct rtl8xxxu_priv *priv, u16 offset, u8 *data); void rtl8xxxu_reset_8051(struct rtl8xxxu_priv *priv); int rtl8xxxu_auto_llt_table(struct rtl8xxxu_priv *priv); diff --git a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8188f.c b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8188f.c index 5eadeb02a762..4d044a8baa30 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8188f.c +++ b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8188f.c @@ -321,6 +321,37 @@ static const struct rtl8xxxu_rfregval rtl8188fu_cut_b_radioa_init_table[] = { {0xff, 0xffffffff} }; +static int rtl8188fu_identify_chip(struct rtl8xxxu_priv *priv) +{ + struct device *dev = &priv->udev->dev; + u32 sys_cfg, vendor; + int ret = 0; + + sprintf(priv->chip_name, "8188FU"); + priv->rtl_chip = RTL8188F; + priv->rf_paths = 1; + priv->rx_paths = 1; + priv->tx_paths = 1; + priv->has_wifi = 1; + + sys_cfg = rtl8xxxu_read32(priv, REG_SYS_CFG); + priv->chip_cut = (sys_cfg & SYS_CFG_CHIP_VERSION_MASK) >> + SYS_CFG_CHIP_VERSION_SHIFT; + if (sys_cfg & SYS_CFG_TRP_VAUX_EN) { + dev_info(dev, "Unsupported test chip\n"); + ret = -ENOTSUPP; + goto out; + } + + vendor = sys_cfg & SYS_CFG_VENDOR_EXT_MASK; + rtl8xxxu_identify_vendor_2bits(priv, vendor); + + ret = rtl8xxxu_config_endpoints_no_sie(priv); + +out: + return ret; +} + static void rtl8xxxu_8188f_channel_to_group(int channel, int *group, int *cck_group) { if (channel < 3) @@ -1690,6 +1721,7 @@ static s8 rtl8188f_cck_rssi(struct rtl8xxxu_priv *priv, u8 cck_agc_rpt) } struct rtl8xxxu_fileops rtl8188fu_fops = { + .identify_chip = rtl8188fu_identify_chip, .parse_efuse = rtl8188fu_parse_efuse, .load_firmware = rtl8188fu_load_firmware, .power_on = rtl8188fu_power_on, diff --git a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8192c.c b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8192c.c index 9dfeeaa74927..8f4396da0c10 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8192c.c +++ b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8192c.c @@ -326,6 +326,65 @@ static const struct rtl8xxxu_rfregval rtl8188ru_radioa_1t_highpa_table[] = { {0xff, 0xffffffff} }; +static int rtl8192cu_identify_chip(struct rtl8xxxu_priv *priv) +{ + struct device *dev = &priv->udev->dev; + u32 val32, bonding, sys_cfg, vendor; + int ret = 0; + + sys_cfg = rtl8xxxu_read32(priv, REG_SYS_CFG); + priv->chip_cut = (sys_cfg & SYS_CFG_CHIP_VERSION_MASK) >> + SYS_CFG_CHIP_VERSION_SHIFT; + if (sys_cfg & SYS_CFG_TRP_VAUX_EN) { + dev_info(dev, "Unsupported test chip\n"); + ret = -ENOTSUPP; + goto out; + } + + if (sys_cfg & SYS_CFG_TYPE_ID) { + bonding = rtl8xxxu_read32(priv, REG_HPON_FSM); + bonding &= HPON_FSM_BONDING_MASK; + if (bonding == HPON_FSM_BONDING_1T2R) { + sprintf(priv->chip_name, "8191CU"); + priv->tx_paths = 1; + priv->usb_interrupts = 1; + priv->rtl_chip = RTL8191C; + } else { + sprintf(priv->chip_name, "8192CU"); + priv->tx_paths = 2; + priv->usb_interrupts = 0; + priv->rtl_chip = RTL8192C; + } + priv->rf_paths = 2; + priv->rx_paths = 2; + } else { + sprintf(priv->chip_name, "8188CU"); + priv->rf_paths = 1; + priv->rx_paths = 1; + priv->tx_paths = 1; + priv->rtl_chip = RTL8188C; + priv->usb_interrupts = 0; + } + priv->has_wifi = 1; + + vendor = sys_cfg & SYS_CFG_VENDOR_ID; + rtl8xxxu_identify_vendor_1bit(priv, vendor); + + val32 = rtl8xxxu_read32(priv, REG_GPIO_OUTSTS); + priv->rom_rev = (val32 & GPIO_RF_RL_ID) >> 28; + + rtl8xxxu_config_endpoints_sie(priv); + + /* + * Fallback for devices that do not provide REG_NORMAL_SIE_EP_TX + */ + if (!priv->ep_tx_count) + ret = rtl8xxxu_config_endpoints_no_sie(priv); + +out: + return ret; +} + static int rtl8192cu_load_firmware(struct rtl8xxxu_priv *priv) { char *fw_name; @@ -541,6 +600,7 @@ static int rtl8192cu_power_on(struct rtl8xxxu_priv *priv) } struct rtl8xxxu_fileops rtl8192cu_fops = { + .identify_chip = rtl8192cu_identify_chip, .parse_efuse = rtl8192cu_parse_efuse, .load_firmware = rtl8192cu_load_firmware, .power_on = rtl8192cu_power_on, diff --git a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8192e.c b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8192e.c index 973a02afc82b..400810191d76 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8192e.c +++ b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8192e.c @@ -478,6 +478,54 @@ static const struct rtl8xxxu_rfregval rtl8192eu_radiob_init_table[] = { {0xff, 0xffffffff} }; +static int rtl8192eu_identify_chip(struct rtl8xxxu_priv *priv) +{ + struct device *dev = &priv->udev->dev; + u32 val32, bonding, sys_cfg, vendor; + int ret = 0; + + sys_cfg = rtl8xxxu_read32(priv, REG_SYS_CFG); + priv->chip_cut = (sys_cfg & SYS_CFG_CHIP_VERSION_MASK) >> + SYS_CFG_CHIP_VERSION_SHIFT; + if (sys_cfg & SYS_CFG_TRP_VAUX_EN) { + dev_info(dev, "Unsupported test chip\n"); + ret = -ENOTSUPP; + goto out; + } + + bonding = rtl8xxxu_read32(priv, REG_HPON_FSM); + bonding &= HPON_FSM_BONDING_MASK; + if (bonding == HPON_FSM_BONDING_1T2R) { + sprintf(priv->chip_name, "8191EU"); + priv->tx_paths = 1; + priv->rtl_chip = RTL8191E; + } else { + sprintf(priv->chip_name, "8192EU"); + priv->tx_paths = 2; + priv->rtl_chip = RTL8192E; + } + priv->rf_paths = 2; + priv->rx_paths = 2; + priv->has_wifi = 1; + + vendor = sys_cfg & SYS_CFG_VENDOR_EXT_MASK; + rtl8xxxu_identify_vendor_2bits(priv, vendor); + + val32 = rtl8xxxu_read32(priv, REG_GPIO_OUTSTS); + priv->rom_rev = (val32 & GPIO_RF_RL_ID) >> 28; + + rtl8xxxu_config_endpoints_sie(priv); + + /* + * Fallback for devices that do not provide REG_NORMAL_SIE_EP_TX + */ + if (!priv->ep_tx_count) + ret = rtl8xxxu_config_endpoints_no_sie(priv); + +out: + return ret; +} + static void rtl8192e_set_tx_power(struct rtl8xxxu_priv *priv, int channel, bool ht40) { @@ -1693,6 +1741,7 @@ static s8 rtl8192e_cck_rssi(struct rtl8xxxu_priv *priv, u8 cck_agc_rpt) } struct rtl8xxxu_fileops rtl8192eu_fops = { + .identify_chip = rtl8192eu_identify_chip, .parse_efuse = rtl8192eu_parse_efuse, .load_firmware = rtl8192eu_load_firmware, .power_on = rtl8192eu_power_on, diff --git a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8723a.c b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8723a.c index 8d8eb16a0970..33a1114a5853 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8723a.c +++ b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8723a.c @@ -129,6 +129,56 @@ static const struct rtl8xxxu_rfregval rtl8723au_radioa_1t_init_table[] = { {0xff, 0xffffffff} }; +static int rtl8723au_identify_chip(struct rtl8xxxu_priv *priv) +{ + struct device *dev = &priv->udev->dev; + u32 val32, sys_cfg, vendor; + int ret = 0; + + sys_cfg = rtl8xxxu_read32(priv, REG_SYS_CFG); + priv->chip_cut = (sys_cfg & SYS_CFG_CHIP_VERSION_MASK) >> + SYS_CFG_CHIP_VERSION_SHIFT; + if (sys_cfg & SYS_CFG_TRP_VAUX_EN) { + dev_info(dev, "Unsupported test chip\n"); + ret = -ENOTSUPP; + goto out; + } + + sprintf(priv->chip_name, "8723AU"); + priv->usb_interrupts = 1; + priv->rtl_chip = RTL8723A; + + priv->rf_paths = 1; + priv->rx_paths = 1; + priv->tx_paths = 1; + + val32 = rtl8xxxu_read32(priv, REG_MULTI_FUNC_CTRL); + if (val32 & MULTI_WIFI_FUNC_EN) + priv->has_wifi = 1; + if (val32 & MULTI_BT_FUNC_EN) + priv->has_bluetooth = 1; + if (val32 & MULTI_GPS_FUNC_EN) + priv->has_gps = 1; + priv->is_multi_func = 1; + + vendor = sys_cfg & SYS_CFG_VENDOR_ID; + rtl8xxxu_identify_vendor_1bit(priv, vendor); + + val32 = rtl8xxxu_read32(priv, REG_GPIO_OUTSTS); + priv->rom_rev = (val32 & GPIO_RF_RL_ID) >> 28; + + rtl8xxxu_config_endpoints_sie(priv); + + /* + * Fallback for devices that do not provide REG_NORMAL_SIE_EP_TX + */ + if (!priv->ep_tx_count) + ret = rtl8xxxu_config_endpoints_no_sie(priv); + +out: + return ret; +} + static int rtl8723au_parse_efuse(struct rtl8xxxu_priv *priv) { struct rtl8723au_efuse *efuse = &priv->efuse_wifi.efuse8723; @@ -409,6 +459,7 @@ s8 rtl8723a_cck_rssi(struct rtl8xxxu_priv *priv, u8 cck_agc_rpt) } struct rtl8xxxu_fileops rtl8723au_fops = { + .identify_chip = rtl8723au_identify_chip, .parse_efuse = rtl8723au_parse_efuse, .load_firmware = rtl8723au_load_firmware, .power_on = rtl8723au_power_on, diff --git a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8723b.c b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8723b.c index 27df8805cb18..558572bdd2c8 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8723b.c +++ b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8723b.c @@ -304,6 +304,54 @@ static const struct rtl8xxxu_rfregval rtl8723bu_radioa_1t_init_table[] = { {0xff, 0xffffffff} }; +static int rtl8723bu_identify_chip(struct rtl8xxxu_priv *priv) +{ + struct device *dev = &priv->udev->dev; + u32 val32, sys_cfg, vendor; + int ret = 0; + + sys_cfg = rtl8xxxu_read32(priv, REG_SYS_CFG); + priv->chip_cut = (sys_cfg & SYS_CFG_CHIP_VERSION_MASK) >> + SYS_CFG_CHIP_VERSION_SHIFT; + if (sys_cfg & SYS_CFG_TRP_VAUX_EN) { + dev_info(dev, "Unsupported test chip\n"); + ret = -ENOTSUPP; + goto out; + } + + sprintf(priv->chip_name, "8723BU"); + priv->rtl_chip = RTL8723B; + priv->rf_paths = 1; + priv->rx_paths = 1; + priv->tx_paths = 1; + + val32 = rtl8xxxu_read32(priv, REG_MULTI_FUNC_CTRL); + if (val32 & MULTI_WIFI_FUNC_EN) + priv->has_wifi = 1; + if (val32 & MULTI_BT_FUNC_EN) + priv->has_bluetooth = 1; + if (val32 & MULTI_GPS_FUNC_EN) + priv->has_gps = 1; + priv->is_multi_func = 1; + + vendor = sys_cfg & SYS_CFG_VENDOR_EXT_MASK; + rtl8xxxu_identify_vendor_2bits(priv, vendor); + + val32 = rtl8xxxu_read32(priv, REG_GPIO_OUTSTS); + priv->rom_rev = (val32 & GPIO_RF_RL_ID) >> 28; + + rtl8xxxu_config_endpoints_sie(priv); + + /* + * Fallback for devices that do not provide REG_NORMAL_SIE_EP_TX + */ + if (!priv->ep_tx_count) + ret = rtl8xxxu_config_endpoints_no_sie(priv); + +out: + return ret; +} + static void rtl8723bu_write_btreg(struct rtl8xxxu_priv *priv, u8 reg, u8 data) { struct h2c_cmd h2c; @@ -1668,6 +1716,7 @@ static s8 rtl8723b_cck_rssi(struct rtl8xxxu_priv *priv, u8 cck_agc_rpt) } struct rtl8xxxu_fileops rtl8723bu_fops = { + .identify_chip = rtl8723bu_identify_chip, .parse_efuse = rtl8723bu_parse_efuse, .load_firmware = rtl8723bu_load_firmware, .power_on = rtl8723bu_power_on, diff --git a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c index 282ad8a9b73d..839e0546f5ec 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c +++ b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c @@ -1589,148 +1589,40 @@ static void rtl8xxxu_print_chipinfo(struct rtl8xxxu_priv *priv) dev_info(dev, "RTL%s MAC: %pM\n", priv->chip_name, priv->mac_addr); } -static int rtl8xxxu_identify_chip(struct rtl8xxxu_priv *priv) +void rtl8xxxu_identify_vendor_1bit(struct rtl8xxxu_priv *priv, u32 vendor) { - const struct usb_device_descriptor *descriptor = &priv->udev->descriptor; - struct device *dev = &priv->udev->dev; - struct ieee80211_hw *hw = priv->hw; - u32 val32, bonding, sys_cfg; - u16 val16; - - sys_cfg = rtl8xxxu_read32(priv, REG_SYS_CFG); - priv->chip_cut = (sys_cfg & SYS_CFG_CHIP_VERSION_MASK) >> - SYS_CFG_CHIP_VERSION_SHIFT; - if (sys_cfg & SYS_CFG_TRP_VAUX_EN) { - dev_info(dev, "Unsupported test chip\n"); - return -ENOTSUPP; - } - - if (descriptor->idVendor == USB_VENDOR_ID_REALTEK && - descriptor->idProduct == 0xf179) { - sprintf(priv->chip_name, "8188FU"); - priv->rtl_chip = RTL8188F; - priv->rf_paths = 1; - priv->rx_paths = 1; - priv->tx_paths = 1; - priv->has_wifi = 1; - goto skip_complicated_chip_detection; - } - - if (sys_cfg & SYS_CFG_BT_FUNC) { - if (priv->chip_cut >= 3) { - sprintf(priv->chip_name, "8723BU"); - priv->rtl_chip = RTL8723B; - } else { - sprintf(priv->chip_name, "8723AU"); - priv->usb_interrupts = 1; - priv->rtl_chip = RTL8723A; - } - - priv->rf_paths = 1; - priv->rx_paths = 1; - priv->tx_paths = 1; - - val32 = rtl8xxxu_read32(priv, REG_MULTI_FUNC_CTRL); - if (val32 & MULTI_WIFI_FUNC_EN) - priv->has_wifi = 1; - if (val32 & MULTI_BT_FUNC_EN) - priv->has_bluetooth = 1; - if (val32 & MULTI_GPS_FUNC_EN) - priv->has_gps = 1; - priv->is_multi_func = 1; - } else if (sys_cfg & SYS_CFG_TYPE_ID) { - bonding = rtl8xxxu_read32(priv, REG_HPON_FSM); - bonding &= HPON_FSM_BONDING_MASK; - if (priv->fops->tx_desc_size == - sizeof(struct rtl8xxxu_txdesc40)) { - if (bonding == HPON_FSM_BONDING_1T2R) { - sprintf(priv->chip_name, "8191EU"); - priv->rf_paths = 2; - priv->rx_paths = 2; - priv->tx_paths = 1; - priv->rtl_chip = RTL8191E; - } else { - sprintf(priv->chip_name, "8192EU"); - priv->rf_paths = 2; - priv->rx_paths = 2; - priv->tx_paths = 2; - priv->rtl_chip = RTL8192E; - } - } else if (bonding == HPON_FSM_BONDING_1T2R) { - sprintf(priv->chip_name, "8191CU"); - priv->rf_paths = 2; - priv->rx_paths = 2; - priv->tx_paths = 1; - priv->usb_interrupts = 1; - priv->rtl_chip = RTL8191C; - } else { - sprintf(priv->chip_name, "8192CU"); - priv->rf_paths = 2; - priv->rx_paths = 2; - priv->tx_paths = 2; - priv->usb_interrupts = 0; - priv->rtl_chip = RTL8192C; - } - priv->has_wifi = 1; + if (vendor) { + sprintf(priv->chip_vendor, "UMC"); + priv->vendor_umc = 1; } else { - sprintf(priv->chip_name, "8188CU"); - priv->rf_paths = 1; - priv->rx_paths = 1; - priv->tx_paths = 1; - priv->rtl_chip = RTL8188C; - priv->usb_interrupts = 0; - priv->has_wifi = 1; + sprintf(priv->chip_vendor, "TSMC"); } +} -skip_complicated_chip_detection: - - hw->wiphy->available_antennas_tx = BIT(priv->tx_paths) - 1; - hw->wiphy->available_antennas_rx = BIT(priv->rx_paths) - 1; - - switch (priv->rtl_chip) { - case RTL8188E: - case RTL8188F: - case RTL8192E: - case RTL8723B: - switch (sys_cfg & SYS_CFG_VENDOR_EXT_MASK) { - case SYS_CFG_VENDOR_ID_TSMC: - sprintf(priv->chip_vendor, "TSMC"); - break; - case SYS_CFG_VENDOR_ID_SMIC: - sprintf(priv->chip_vendor, "SMIC"); - priv->vendor_smic = 1; - break; - case SYS_CFG_VENDOR_ID_UMC: - sprintf(priv->chip_vendor, "UMC"); - priv->vendor_umc = 1; - break; - default: - sprintf(priv->chip_vendor, "unknown"); - } +void rtl8xxxu_identify_vendor_2bits(struct rtl8xxxu_priv *priv, u32 vendor) +{ + switch (vendor) { + case SYS_CFG_VENDOR_ID_TSMC: + sprintf(priv->chip_vendor, "TSMC"); + break; + case SYS_CFG_VENDOR_ID_SMIC: + sprintf(priv->chip_vendor, "SMIC"); + priv->vendor_smic = 1; + break; + case SYS_CFG_VENDOR_ID_UMC: + sprintf(priv->chip_vendor, "UMC"); + priv->vendor_umc = 1; break; default: - if (sys_cfg & SYS_CFG_VENDOR_ID) { - sprintf(priv->chip_vendor, "UMC"); - priv->vendor_umc = 1; - } else { - sprintf(priv->chip_vendor, "TSMC"); - } + sprintf(priv->chip_vendor, "unknown"); } +} - val32 = rtl8xxxu_read32(priv, REG_GPIO_OUTSTS); - priv->rom_rev = (val32 & GPIO_RF_RL_ID) >> 28; +void rtl8xxxu_config_endpoints_sie(struct rtl8xxxu_priv *priv) +{ + u16 val16; - /* - * 8188FU vendor driver doesn't use REG_NORMAL_SIE_EP_TX, - * it just decides the queue mapping based on nr_out_eps. - * However, reading the register returns "0x321" which - * results in a wrong ep_tx_count of 3 and most frames - * not being transmitted. - */ - if (priv->rtl_chip == RTL8188F) - val16 = 0; - else - val16 = rtl8xxxu_read16(priv, REG_NORMAL_SIE_EP_TX); + val16 = rtl8xxxu_read16(priv, REG_NORMAL_SIE_EP_TX); if (val16 & NORMAL_SIE_EP_TX_HIGH_MASK) { priv->ep_tx_high_queue = 1; @@ -1746,29 +1638,29 @@ skip_complicated_chip_detection: priv->ep_tx_low_queue = 1; priv->ep_tx_count++; } +} - /* - * Fallback for devices that do not provide REG_NORMAL_SIE_EP_TX - */ - if (!priv->ep_tx_count) { - switch (priv->nr_out_eps) { - case 4: - case 3: - priv->ep_tx_low_queue = 1; - priv->ep_tx_count++; - fallthrough; - case 2: - priv->ep_tx_normal_queue = 1; - priv->ep_tx_count++; - fallthrough; - case 1: - priv->ep_tx_high_queue = 1; - priv->ep_tx_count++; - break; - default: - dev_info(dev, "Unsupported USB TX end-points\n"); - return -ENOTSUPP; - } +int rtl8xxxu_config_endpoints_no_sie(struct rtl8xxxu_priv *priv) +{ + struct device *dev = &priv->udev->dev; + + switch (priv->nr_out_eps) { + case 4: + case 3: + priv->ep_tx_low_queue = 1; + priv->ep_tx_count++; + fallthrough; + case 2: + priv->ep_tx_normal_queue = 1; + priv->ep_tx_count++; + fallthrough; + case 1: + priv->ep_tx_high_queue = 1; + priv->ep_tx_count++; + break; + default: + dev_info(dev, "Unsupported USB TX end-points\n"); + return -ENOTSUPP; } return 0; @@ -6937,12 +6829,15 @@ static int rtl8xxxu_probe(struct usb_interface *interface, if (ret) goto err_set_intfdata; - ret = rtl8xxxu_identify_chip(priv); + ret = priv->fops->identify_chip(priv); if (ret) { dev_err(&udev->dev, "Fatal - failed to identify chip\n"); goto err_set_intfdata; } + hw->wiphy->available_antennas_tx = BIT(priv->tx_paths) - 1; + hw->wiphy->available_antennas_rx = BIT(priv->rx_paths) - 1; + ret = rtl8xxxu_read_efuse(priv); if (ret) { dev_err(&udev->dev, "Fatal - failed to read EFuse\n"); From e948023d5fc8e4a5e79e5ded53c2a1d1d7041ee6 Mon Sep 17 00:00:00 2001 From: Bitterblue Smith Date: Sun, 6 Nov 2022 00:54:56 +0200 Subject: [PATCH 0957/2002] wifi: rtl8xxxu: Rename rtl8xxxu_8188f_channel_to_group This name is an anomaly. Change it to rtl8188f_channel_to_group to follow the same pattern as the other functions. Signed-off-by: Bitterblue Smith Reviewed-by: Ping-Ke Shih Acked-by: Jes Sorensen Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/ba8e3ea2-74f5-e1db-296e-4ae5f03084dc@gmail.com --- drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8188f.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8188f.c b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8188f.c index 4d044a8baa30..0f77312dd0ca 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8188f.c +++ b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8188f.c @@ -352,7 +352,7 @@ out: return ret; } -static void rtl8xxxu_8188f_channel_to_group(int channel, int *group, int *cck_group) +static void rtl8188f_channel_to_group(int channel, int *group, int *cck_group) { if (channel < 3) *group = 0; @@ -378,7 +378,7 @@ rtl8188f_set_tx_power(struct rtl8xxxu_priv *priv, int channel, bool ht40) u8 cck, ofdmbase, mcsbase; int group, cck_group; - rtl8xxxu_8188f_channel_to_group(channel, &group, &cck_group); + rtl8188f_channel_to_group(channel, &group, &cck_group); cck = priv->cck_tx_power_index_A[cck_group]; From 486e0315c4a1f4e8ea8da0e7de92d89ee8e1ba0a Mon Sep 17 00:00:00 2001 From: Bitterblue Smith Date: Thu, 10 Nov 2022 15:58:16 +0200 Subject: [PATCH 0958/2002] wifi: rtl8xxxu: Name some bits used in burst init Use descriptive names instead of magic numbers. Suggested-by: Ping-Ke Shih Signed-off-by: Bitterblue Smith Reviewed-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/e7d05bd9-e096-8361-f1b4-3c8b8599a7eb@gmail.com --- drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c | 10 +++++----- drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_regs.h | 6 ++++++ 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c index 839e0546f5ec..e4eb17d03cd7 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c +++ b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c @@ -3786,16 +3786,16 @@ void rtl8xxxu_init_burst(struct rtl8xxxu_priv *priv) * For USB high speed set 512B packets */ val8 = rtl8xxxu_read8(priv, REG_RXDMA_PRO_8723B); - val8 &= ~(BIT(4) | BIT(5)); - val8 |= BIT(4); - val8 |= BIT(1) | BIT(2) | BIT(3); + u8p_replace_bits(&val8, 1, RXDMA_PRO_DMA_BURST_SIZE); + u8p_replace_bits(&val8, 3, RXDMA_PRO_DMA_BURST_CNT); + val8 |= RXDMA_PRO_DMA_MODE; rtl8xxxu_write8(priv, REG_RXDMA_PRO_8723B, val8); /* * Enable single packet AMPDU */ val8 = rtl8xxxu_read8(priv, REG_HT_SINGLE_AMPDU_8723B); - val8 |= BIT(7); + val8 |= HT_SINGLE_AMPDU_ENABLE; rtl8xxxu_write8(priv, REG_HT_SINGLE_AMPDU_8723B, val8); rtl8xxxu_write16(priv, REG_MAX_AGGR_NUM, 0x0c14); @@ -3820,7 +3820,7 @@ void rtl8xxxu_init_burst(struct rtl8xxxu_priv *priv) /* to prevent mac is reseted by bus. */ val8 = rtl8xxxu_read8(priv, REG_RSV_CTRL); - val8 |= BIT(5) | BIT(6); + val8 |= RSV_CTRL_WLOCK_1C | RSV_CTRL_DIS_PRST; rtl8xxxu_write8(priv, REG_RSV_CTRL, val8); } diff --git a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_regs.h b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_regs.h index 04bf77959fba..5d4cac4f4c06 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_regs.h +++ b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_regs.h @@ -68,6 +68,8 @@ #define REG_SPS_OCP_CFG 0x0018 #define REG_8192E_LDOV12_CTRL 0x0014 #define REG_RSV_CTRL 0x001c +#define RSV_CTRL_WLOCK_1C BIT(5) +#define RSV_CTRL_DIS_PRST BIT(6) #define REG_RF_CTRL 0x001f #define RF_ENABLE BIT(0) @@ -472,6 +474,9 @@ /* Presumably only found on newer chips such as 8723bu */ #define REG_RX_DMA_CTRL_8723B 0x0286 #define REG_RXDMA_PRO_8723B 0x0290 +#define RXDMA_PRO_DMA_MODE BIT(1) /* Set to 0x1. */ +#define RXDMA_PRO_DMA_BURST_CNT GENMASK(3, 2) /* Set to 0x3. */ +#define RXDMA_PRO_DMA_BURST_SIZE GENMASK(5, 4) /* Set to 0x1. */ #define REG_RF_BB_CMD_ADDR 0x02c0 #define REG_RF_BB_CMD_DATA 0x02c4 @@ -577,6 +582,7 @@ #define REG_STBC_SETTING 0x04c4 #define REG_QUEUE_CTRL 0x04c6 #define REG_HT_SINGLE_AMPDU_8723B 0x04c7 +#define HT_SINGLE_AMPDU_ENABLE BIT(7) #define REG_PROT_MODE_CTRL 0x04c8 #define REG_MAX_AGGR_NUM 0x04ca #define REG_RTS_MAX_AGGR_NUM 0x04cb From 9b00565abf3f961b10b2fdf8f762e10ef4cf3acd Mon Sep 17 00:00:00 2001 From: Bitterblue Smith Date: Thu, 10 Nov 2022 15:59:29 +0200 Subject: [PATCH 0959/2002] wifi: rtl8xxxu: Use strscpy instead of sprintf Fill priv->chip_name and priv->chip_vendor with strscpy instead of sprintf. This is just to prevent future bugs in case the name of a chip/vendor becomes longer than the size of chip_name/chip_vendor. Suggested-by: Ping-Ke Shih Signed-off-by: Bitterblue Smith Reviewed-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/5fc9cc0e-eecb-8428-aeb1-f745791c0f16@gmail.com --- .../net/wireless/realtek/rtl8xxxu/rtl8xxxu_8188f.c | 2 +- .../net/wireless/realtek/rtl8xxxu/rtl8xxxu_8192c.c | 8 ++++---- .../net/wireless/realtek/rtl8xxxu/rtl8xxxu_8192e.c | 4 ++-- .../net/wireless/realtek/rtl8xxxu/rtl8xxxu_8723a.c | 2 +- .../net/wireless/realtek/rtl8xxxu/rtl8xxxu_8723b.c | 2 +- .../net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c | 12 ++++++------ 6 files changed, 15 insertions(+), 15 deletions(-) diff --git a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8188f.c b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8188f.c index 0f77312dd0ca..ef38891ddbd1 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8188f.c +++ b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8188f.c @@ -327,7 +327,7 @@ static int rtl8188fu_identify_chip(struct rtl8xxxu_priv *priv) u32 sys_cfg, vendor; int ret = 0; - sprintf(priv->chip_name, "8188FU"); + strscpy(priv->chip_name, "8188FU", sizeof(priv->chip_name)); priv->rtl_chip = RTL8188F; priv->rf_paths = 1; priv->rx_paths = 1; diff --git a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8192c.c b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8192c.c index 8f4396da0c10..df357b3b6fe0 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8192c.c +++ b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8192c.c @@ -345,12 +345,12 @@ static int rtl8192cu_identify_chip(struct rtl8xxxu_priv *priv) bonding = rtl8xxxu_read32(priv, REG_HPON_FSM); bonding &= HPON_FSM_BONDING_MASK; if (bonding == HPON_FSM_BONDING_1T2R) { - sprintf(priv->chip_name, "8191CU"); + strscpy(priv->chip_name, "8191CU", sizeof(priv->chip_name)); priv->tx_paths = 1; priv->usb_interrupts = 1; priv->rtl_chip = RTL8191C; } else { - sprintf(priv->chip_name, "8192CU"); + strscpy(priv->chip_name, "8192CU", sizeof(priv->chip_name)); priv->tx_paths = 2; priv->usb_interrupts = 0; priv->rtl_chip = RTL8192C; @@ -358,7 +358,7 @@ static int rtl8192cu_identify_chip(struct rtl8xxxu_priv *priv) priv->rf_paths = 2; priv->rx_paths = 2; } else { - sprintf(priv->chip_name, "8188CU"); + strscpy(priv->chip_name, "8188CU", sizeof(priv->chip_name)); priv->rf_paths = 1; priv->rx_paths = 1; priv->tx_paths = 1; @@ -451,7 +451,7 @@ static int rtl8192cu_parse_efuse(struct rtl8xxxu_priv *priv) priv->power_base = &rtl8192c_power_base; if (efuse->rf_regulatory & 0x20) { - sprintf(priv->chip_name, "8188RU"); + strscpy(priv->chip_name, "8188RU", sizeof(priv->chip_name)); priv->rtl_chip = RTL8188R; priv->hi_pa = 1; priv->no_pape = 1; diff --git a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8192e.c b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8192e.c index 400810191d76..802b9246c79d 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8192e.c +++ b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8192e.c @@ -496,11 +496,11 @@ static int rtl8192eu_identify_chip(struct rtl8xxxu_priv *priv) bonding = rtl8xxxu_read32(priv, REG_HPON_FSM); bonding &= HPON_FSM_BONDING_MASK; if (bonding == HPON_FSM_BONDING_1T2R) { - sprintf(priv->chip_name, "8191EU"); + strscpy(priv->chip_name, "8191EU", sizeof(priv->chip_name)); priv->tx_paths = 1; priv->rtl_chip = RTL8191E; } else { - sprintf(priv->chip_name, "8192EU"); + strscpy(priv->chip_name, "8192EU", sizeof(priv->chip_name)); priv->tx_paths = 2; priv->rtl_chip = RTL8192E; } diff --git a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8723a.c b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8723a.c index 33a1114a5853..c8ab71ed4ff4 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8723a.c +++ b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8723a.c @@ -144,7 +144,7 @@ static int rtl8723au_identify_chip(struct rtl8xxxu_priv *priv) goto out; } - sprintf(priv->chip_name, "8723AU"); + strscpy(priv->chip_name, "8723AU", sizeof(priv->chip_name)); priv->usb_interrupts = 1; priv->rtl_chip = RTL8723A; diff --git a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8723b.c b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8723b.c index 558572bdd2c8..ece82e613bf0 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8723b.c +++ b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8723b.c @@ -319,7 +319,7 @@ static int rtl8723bu_identify_chip(struct rtl8xxxu_priv *priv) goto out; } - sprintf(priv->chip_name, "8723BU"); + strscpy(priv->chip_name, "8723BU", sizeof(priv->chip_name)); priv->rtl_chip = RTL8723B; priv->rf_paths = 1; priv->rx_paths = 1; diff --git a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c index e4eb17d03cd7..e8fcd531c437 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c +++ b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c @@ -1592,10 +1592,10 @@ static void rtl8xxxu_print_chipinfo(struct rtl8xxxu_priv *priv) void rtl8xxxu_identify_vendor_1bit(struct rtl8xxxu_priv *priv, u32 vendor) { if (vendor) { - sprintf(priv->chip_vendor, "UMC"); + strscpy(priv->chip_vendor, "UMC", sizeof(priv->chip_vendor)); priv->vendor_umc = 1; } else { - sprintf(priv->chip_vendor, "TSMC"); + strscpy(priv->chip_vendor, "TSMC", sizeof(priv->chip_vendor)); } } @@ -1603,18 +1603,18 @@ void rtl8xxxu_identify_vendor_2bits(struct rtl8xxxu_priv *priv, u32 vendor) { switch (vendor) { case SYS_CFG_VENDOR_ID_TSMC: - sprintf(priv->chip_vendor, "TSMC"); + strscpy(priv->chip_vendor, "TSMC", sizeof(priv->chip_vendor)); break; case SYS_CFG_VENDOR_ID_SMIC: - sprintf(priv->chip_vendor, "SMIC"); + strscpy(priv->chip_vendor, "SMIC", sizeof(priv->chip_vendor)); priv->vendor_smic = 1; break; case SYS_CFG_VENDOR_ID_UMC: - sprintf(priv->chip_vendor, "UMC"); + strscpy(priv->chip_vendor, "UMC", sizeof(priv->chip_vendor)); priv->vendor_umc = 1; break; default: - sprintf(priv->chip_vendor, "unknown"); + strscpy(priv->chip_vendor, "unknown", sizeof(priv->chip_vendor)); } } From 60d18ddb53ced444a739b05473dcdc6048421f33 Mon Sep 17 00:00:00 2001 From: Bitterblue Smith Date: Thu, 10 Nov 2022 16:01:18 +0200 Subject: [PATCH 0960/2002] wifi: rtl8xxxu: Use u32_get_bits in *_identify_chip It simplifies the code a bit. Suggested-by: Ping-Ke Shih Signed-off-by: Bitterblue Smith Reviewed-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/68f94284-3728-7b75-2b7b-64fae8af6bc5@gmail.com --- drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8188f.c | 3 +-- drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8192c.c | 5 ++--- drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8192e.c | 5 ++--- drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8723a.c | 5 ++--- drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8723b.c | 5 ++--- drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_regs.h | 1 - 6 files changed, 9 insertions(+), 15 deletions(-) diff --git a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8188f.c b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8188f.c index ef38891ddbd1..2c4f403ba68f 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8188f.c +++ b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8188f.c @@ -335,8 +335,7 @@ static int rtl8188fu_identify_chip(struct rtl8xxxu_priv *priv) priv->has_wifi = 1; sys_cfg = rtl8xxxu_read32(priv, REG_SYS_CFG); - priv->chip_cut = (sys_cfg & SYS_CFG_CHIP_VERSION_MASK) >> - SYS_CFG_CHIP_VERSION_SHIFT; + priv->chip_cut = u32_get_bits(sys_cfg, SYS_CFG_CHIP_VERSION_MASK); if (sys_cfg & SYS_CFG_TRP_VAUX_EN) { dev_info(dev, "Unsupported test chip\n"); ret = -ENOTSUPP; diff --git a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8192c.c b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8192c.c index df357b3b6fe0..3bef9ffc8b02 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8192c.c +++ b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8192c.c @@ -333,8 +333,7 @@ static int rtl8192cu_identify_chip(struct rtl8xxxu_priv *priv) int ret = 0; sys_cfg = rtl8xxxu_read32(priv, REG_SYS_CFG); - priv->chip_cut = (sys_cfg & SYS_CFG_CHIP_VERSION_MASK) >> - SYS_CFG_CHIP_VERSION_SHIFT; + priv->chip_cut = u32_get_bits(sys_cfg, SYS_CFG_CHIP_VERSION_MASK); if (sys_cfg & SYS_CFG_TRP_VAUX_EN) { dev_info(dev, "Unsupported test chip\n"); ret = -ENOTSUPP; @@ -371,7 +370,7 @@ static int rtl8192cu_identify_chip(struct rtl8xxxu_priv *priv) rtl8xxxu_identify_vendor_1bit(priv, vendor); val32 = rtl8xxxu_read32(priv, REG_GPIO_OUTSTS); - priv->rom_rev = (val32 & GPIO_RF_RL_ID) >> 28; + priv->rom_rev = u32_get_bits(val32, GPIO_RF_RL_ID); rtl8xxxu_config_endpoints_sie(priv); diff --git a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8192e.c b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8192e.c index 802b9246c79d..6dc1e5858e77 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8192e.c +++ b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8192e.c @@ -485,8 +485,7 @@ static int rtl8192eu_identify_chip(struct rtl8xxxu_priv *priv) int ret = 0; sys_cfg = rtl8xxxu_read32(priv, REG_SYS_CFG); - priv->chip_cut = (sys_cfg & SYS_CFG_CHIP_VERSION_MASK) >> - SYS_CFG_CHIP_VERSION_SHIFT; + priv->chip_cut = u32_get_bits(sys_cfg, SYS_CFG_CHIP_VERSION_MASK); if (sys_cfg & SYS_CFG_TRP_VAUX_EN) { dev_info(dev, "Unsupported test chip\n"); ret = -ENOTSUPP; @@ -512,7 +511,7 @@ static int rtl8192eu_identify_chip(struct rtl8xxxu_priv *priv) rtl8xxxu_identify_vendor_2bits(priv, vendor); val32 = rtl8xxxu_read32(priv, REG_GPIO_OUTSTS); - priv->rom_rev = (val32 & GPIO_RF_RL_ID) >> 28; + priv->rom_rev = u32_get_bits(val32, GPIO_RF_RL_ID); rtl8xxxu_config_endpoints_sie(priv); diff --git a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8723a.c b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8723a.c index c8ab71ed4ff4..707ac48ecc83 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8723a.c +++ b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8723a.c @@ -136,8 +136,7 @@ static int rtl8723au_identify_chip(struct rtl8xxxu_priv *priv) int ret = 0; sys_cfg = rtl8xxxu_read32(priv, REG_SYS_CFG); - priv->chip_cut = (sys_cfg & SYS_CFG_CHIP_VERSION_MASK) >> - SYS_CFG_CHIP_VERSION_SHIFT; + priv->chip_cut = u32_get_bits(sys_cfg, SYS_CFG_CHIP_VERSION_MASK); if (sys_cfg & SYS_CFG_TRP_VAUX_EN) { dev_info(dev, "Unsupported test chip\n"); ret = -ENOTSUPP; @@ -165,7 +164,7 @@ static int rtl8723au_identify_chip(struct rtl8xxxu_priv *priv) rtl8xxxu_identify_vendor_1bit(priv, vendor); val32 = rtl8xxxu_read32(priv, REG_GPIO_OUTSTS); - priv->rom_rev = (val32 & GPIO_RF_RL_ID) >> 28; + priv->rom_rev = u32_get_bits(val32, GPIO_RF_RL_ID); rtl8xxxu_config_endpoints_sie(priv); diff --git a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8723b.c b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8723b.c index ece82e613bf0..a0ec895b61a4 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8723b.c +++ b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8723b.c @@ -311,8 +311,7 @@ static int rtl8723bu_identify_chip(struct rtl8xxxu_priv *priv) int ret = 0; sys_cfg = rtl8xxxu_read32(priv, REG_SYS_CFG); - priv->chip_cut = (sys_cfg & SYS_CFG_CHIP_VERSION_MASK) >> - SYS_CFG_CHIP_VERSION_SHIFT; + priv->chip_cut = u32_get_bits(sys_cfg, SYS_CFG_CHIP_VERSION_MASK); if (sys_cfg & SYS_CFG_TRP_VAUX_EN) { dev_info(dev, "Unsupported test chip\n"); ret = -ENOTSUPP; @@ -338,7 +337,7 @@ static int rtl8723bu_identify_chip(struct rtl8xxxu_priv *priv) rtl8xxxu_identify_vendor_2bits(priv, vendor); val32 = rtl8xxxu_read32(priv, REG_GPIO_OUTSTS); - priv->rom_rev = (val32 & GPIO_RF_RL_ID) >> 28; + priv->rom_rev = u32_get_bits(val32, GPIO_RF_RL_ID); rtl8xxxu_config_endpoints_sie(priv); diff --git a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_regs.h b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_regs.h index 5d4cac4f4c06..3e79efdfb4c2 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_regs.h +++ b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_regs.h @@ -315,7 +315,6 @@ #define SYS_CFG_SPS_SEL BIT(24) /* 1:LDO regulator mode; 0:Switching regulator mode*/ #define SYS_CFG_CHIP_VERSION_MASK 0xf000 /* Bit 12 - 15 */ -#define SYS_CFG_CHIP_VERSION_SHIFT 12 #define REG_GPIO_OUTSTS 0x00f4 /* For RTL8723 only. */ #define GPIO_EFS_HCI_SEL (BIT(0) | BIT(1)) From 2c0e077db65a9410a2f0a36526918c77098a85a6 Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Tue, 8 Nov 2022 14:21:24 -0600 Subject: [PATCH 0961/2002] wifi: orinoco: Avoid clashing function prototypes When built with Control Flow Integrity, function prototypes between caller and function declaration must match. These mismatches are visible at compile time with the new -Wcast-function-type-strict in Clang[1]. Fix a total of 43 warnings like these: drivers/net/wireless/intersil/orinoco/wext.c:1379:27: warning: cast from 'int (*)(struct net_device *, struct iw_request_info *, struct iw_param *, char *)' to 'iw_handler' (aka 'int (*)(struct net_device *, struct iw_request_info *, union iwreq_data *, char *)') converts to incompatible function type [-Wcast-function-type-strict] IW_HANDLER(SIOCGIWPOWER, (iw_handler)orinoco_ioctl_getpower), ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The orinoco Wireless Extension handler callbacks (iw_handler) use a union for the data argument. Actually use the union and perform explicit member selection in the function body instead of having a function prototype mismatch. No significant binary differences were seen before/after changes. These changes were made partly manually and partly with the help of Coccinelle. Link: https://github.com/KSPP/linux/issues/234 Link: https://reviews.llvm.org/D134831 [1] Signed-off-by: Gustavo A. R. Silva Reviewed-by: Kees Cook Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/e564003608a1f2ad86283370ef816805c92b30f6.1667934775.git.gustavoars@kernel.org --- drivers/net/wireless/intersil/orinoco/wext.c | 109 +++++++++++-------- 1 file changed, 62 insertions(+), 47 deletions(-) diff --git a/drivers/net/wireless/intersil/orinoco/wext.c b/drivers/net/wireless/intersil/orinoco/wext.c index 4a01260027bc..b8eb5d60192f 100644 --- a/drivers/net/wireless/intersil/orinoco/wext.c +++ b/drivers/net/wireless/intersil/orinoco/wext.c @@ -154,9 +154,10 @@ static struct iw_statistics *orinoco_get_wireless_stats(struct net_device *dev) static int orinoco_ioctl_setwap(struct net_device *dev, struct iw_request_info *info, - struct sockaddr *ap_addr, + union iwreq_data *wrqu, char *extra) { + struct sockaddr *ap_addr = &wrqu->ap_addr; struct orinoco_private *priv = ndev_priv(dev); int err = -EINPROGRESS; /* Call commit handler */ unsigned long flags; @@ -213,9 +214,10 @@ static int orinoco_ioctl_setwap(struct net_device *dev, static int orinoco_ioctl_getwap(struct net_device *dev, struct iw_request_info *info, - struct sockaddr *ap_addr, + union iwreq_data *wrqu, char *extra) { + struct sockaddr *ap_addr = &wrqu->ap_addr; struct orinoco_private *priv = ndev_priv(dev); int err = 0; @@ -234,9 +236,10 @@ static int orinoco_ioctl_getwap(struct net_device *dev, static int orinoco_ioctl_setiwencode(struct net_device *dev, struct iw_request_info *info, - struct iw_point *erq, + union iwreq_data *wrqu, char *keybuf) { + struct iw_point *erq = &wrqu->encoding; struct orinoco_private *priv = ndev_priv(dev); int index = (erq->flags & IW_ENCODE_INDEX) - 1; int setindex = priv->tx_key; @@ -325,9 +328,10 @@ static int orinoco_ioctl_setiwencode(struct net_device *dev, static int orinoco_ioctl_getiwencode(struct net_device *dev, struct iw_request_info *info, - struct iw_point *erq, + union iwreq_data *wrqu, char *keybuf) { + struct iw_point *erq = &wrqu->encoding; struct orinoco_private *priv = ndev_priv(dev); int index = (erq->flags & IW_ENCODE_INDEX) - 1; unsigned long flags; @@ -361,9 +365,10 @@ static int orinoco_ioctl_getiwencode(struct net_device *dev, static int orinoco_ioctl_setessid(struct net_device *dev, struct iw_request_info *info, - struct iw_point *erq, + union iwreq_data *wrqu, char *essidbuf) { + struct iw_point *erq = &wrqu->essid; struct orinoco_private *priv = ndev_priv(dev); unsigned long flags; @@ -392,9 +397,10 @@ static int orinoco_ioctl_setessid(struct net_device *dev, static int orinoco_ioctl_getessid(struct net_device *dev, struct iw_request_info *info, - struct iw_point *erq, + union iwreq_data *wrqu, char *essidbuf) { + struct iw_point *erq = &wrqu->essid; struct orinoco_private *priv = ndev_priv(dev); int active; int err = 0; @@ -420,9 +426,10 @@ static int orinoco_ioctl_getessid(struct net_device *dev, static int orinoco_ioctl_setfreq(struct net_device *dev, struct iw_request_info *info, - struct iw_freq *frq, + union iwreq_data *wrqu, char *extra) { + struct iw_freq *frq = &wrqu->freq; struct orinoco_private *priv = ndev_priv(dev); int chan = -1; unsigned long flags; @@ -469,9 +476,10 @@ static int orinoco_ioctl_setfreq(struct net_device *dev, static int orinoco_ioctl_getfreq(struct net_device *dev, struct iw_request_info *info, - struct iw_freq *frq, + union iwreq_data *wrqu, char *extra) { + struct iw_freq *frq = &wrqu->freq; struct orinoco_private *priv = ndev_priv(dev); int tmp; @@ -488,9 +496,10 @@ static int orinoco_ioctl_getfreq(struct net_device *dev, static int orinoco_ioctl_getsens(struct net_device *dev, struct iw_request_info *info, - struct iw_param *srq, + union iwreq_data *wrqu, char *extra) { + struct iw_param *srq = &wrqu->sens; struct orinoco_private *priv = ndev_priv(dev); struct hermes *hw = &priv->hw; u16 val; @@ -517,9 +526,10 @@ static int orinoco_ioctl_getsens(struct net_device *dev, static int orinoco_ioctl_setsens(struct net_device *dev, struct iw_request_info *info, - struct iw_param *srq, + union iwreq_data *wrqu, char *extra) { + struct iw_param *srq = &wrqu->sens; struct orinoco_private *priv = ndev_priv(dev); int val = srq->value; unsigned long flags; @@ -540,9 +550,10 @@ static int orinoco_ioctl_setsens(struct net_device *dev, static int orinoco_ioctl_setrate(struct net_device *dev, struct iw_request_info *info, - struct iw_param *rrq, + union iwreq_data *wrqu, char *extra) { + struct iw_param *rrq = &wrqu->bitrate; struct orinoco_private *priv = ndev_priv(dev); int ratemode; int bitrate; /* 100s of kilobits */ @@ -574,9 +585,10 @@ static int orinoco_ioctl_setrate(struct net_device *dev, static int orinoco_ioctl_getrate(struct net_device *dev, struct iw_request_info *info, - struct iw_param *rrq, + union iwreq_data *wrqu, char *extra) { + struct iw_param *rrq = &wrqu->bitrate; struct orinoco_private *priv = ndev_priv(dev); int err = 0; int bitrate, automatic; @@ -610,9 +622,10 @@ static int orinoco_ioctl_getrate(struct net_device *dev, static int orinoco_ioctl_setpower(struct net_device *dev, struct iw_request_info *info, - struct iw_param *prq, + union iwreq_data *wrqu, char *extra) { + struct iw_param *prq = &wrqu->power; struct orinoco_private *priv = ndev_priv(dev); int err = -EINPROGRESS; /* Call commit handler */ unsigned long flags; @@ -664,9 +677,10 @@ static int orinoco_ioctl_setpower(struct net_device *dev, static int orinoco_ioctl_getpower(struct net_device *dev, struct iw_request_info *info, - struct iw_param *prq, + union iwreq_data *wrqu, char *extra) { + struct iw_param *prq = &wrqu->power; struct orinoco_private *priv = ndev_priv(dev); struct hermes *hw = &priv->hw; int err = 0; @@ -1097,7 +1111,7 @@ static int orinoco_ioctl_set_mlme(struct net_device *dev, static int orinoco_ioctl_reset(struct net_device *dev, struct iw_request_info *info, - void *wrqu, + union iwreq_data *wrqu, char *extra) { struct orinoco_private *priv = ndev_priv(dev); @@ -1121,7 +1135,7 @@ static int orinoco_ioctl_reset(struct net_device *dev, static int orinoco_ioctl_setibssport(struct net_device *dev, struct iw_request_info *info, - void *wrqu, + union iwreq_data *wrqu, char *extra) { @@ -1143,7 +1157,7 @@ static int orinoco_ioctl_setibssport(struct net_device *dev, static int orinoco_ioctl_getibssport(struct net_device *dev, struct iw_request_info *info, - void *wrqu, + union iwreq_data *wrqu, char *extra) { struct orinoco_private *priv = ndev_priv(dev); @@ -1155,7 +1169,7 @@ static int orinoco_ioctl_getibssport(struct net_device *dev, static int orinoco_ioctl_setport3(struct net_device *dev, struct iw_request_info *info, - void *wrqu, + union iwreq_data *wrqu, char *extra) { struct orinoco_private *priv = ndev_priv(dev); @@ -1201,7 +1215,7 @@ static int orinoco_ioctl_setport3(struct net_device *dev, static int orinoco_ioctl_getport3(struct net_device *dev, struct iw_request_info *info, - void *wrqu, + union iwreq_data *wrqu, char *extra) { struct orinoco_private *priv = ndev_priv(dev); @@ -1213,7 +1227,7 @@ static int orinoco_ioctl_getport3(struct net_device *dev, static int orinoco_ioctl_setpreamble(struct net_device *dev, struct iw_request_info *info, - void *wrqu, + union iwreq_data *wrqu, char *extra) { struct orinoco_private *priv = ndev_priv(dev); @@ -1245,7 +1259,7 @@ static int orinoco_ioctl_setpreamble(struct net_device *dev, static int orinoco_ioctl_getpreamble(struct net_device *dev, struct iw_request_info *info, - void *wrqu, + union iwreq_data *wrqu, char *extra) { struct orinoco_private *priv = ndev_priv(dev); @@ -1265,9 +1279,10 @@ static int orinoco_ioctl_getpreamble(struct net_device *dev, * For Wireless Tools 25 and 26 append "dummy" are the end. */ static int orinoco_ioctl_getrid(struct net_device *dev, struct iw_request_info *info, - struct iw_point *data, + union iwreq_data *wrqu, char *extra) { + struct iw_point *data = &wrqu->data; struct orinoco_private *priv = ndev_priv(dev); struct hermes *hw = &priv->hw; int rid = data->flags; @@ -1303,7 +1318,7 @@ static int orinoco_ioctl_getrid(struct net_device *dev, /* Commit handler, called after set operations */ static int orinoco_ioctl_commit(struct net_device *dev, struct iw_request_info *info, - void *wrqu, + union iwreq_data *wrqu, char *extra) { struct orinoco_private *priv = ndev_priv(dev); @@ -1347,36 +1362,36 @@ static const struct iw_priv_args orinoco_privtab[] = { */ static const iw_handler orinoco_handler[] = { - IW_HANDLER(SIOCSIWCOMMIT, (iw_handler)orinoco_ioctl_commit), + IW_HANDLER(SIOCSIWCOMMIT, orinoco_ioctl_commit), IW_HANDLER(SIOCGIWNAME, (iw_handler)cfg80211_wext_giwname), - IW_HANDLER(SIOCSIWFREQ, (iw_handler)orinoco_ioctl_setfreq), - IW_HANDLER(SIOCGIWFREQ, (iw_handler)orinoco_ioctl_getfreq), + IW_HANDLER(SIOCSIWFREQ, orinoco_ioctl_setfreq), + IW_HANDLER(SIOCGIWFREQ, orinoco_ioctl_getfreq), IW_HANDLER(SIOCSIWMODE, (iw_handler)cfg80211_wext_siwmode), IW_HANDLER(SIOCGIWMODE, (iw_handler)cfg80211_wext_giwmode), - IW_HANDLER(SIOCSIWSENS, (iw_handler)orinoco_ioctl_setsens), - IW_HANDLER(SIOCGIWSENS, (iw_handler)orinoco_ioctl_getsens), + IW_HANDLER(SIOCSIWSENS, orinoco_ioctl_setsens), + IW_HANDLER(SIOCGIWSENS, orinoco_ioctl_getsens), IW_HANDLER(SIOCGIWRANGE, (iw_handler)cfg80211_wext_giwrange), IW_HANDLER(SIOCSIWSPY, iw_handler_set_spy), IW_HANDLER(SIOCGIWSPY, iw_handler_get_spy), IW_HANDLER(SIOCSIWTHRSPY, iw_handler_set_thrspy), IW_HANDLER(SIOCGIWTHRSPY, iw_handler_get_thrspy), - IW_HANDLER(SIOCSIWAP, (iw_handler)orinoco_ioctl_setwap), - IW_HANDLER(SIOCGIWAP, (iw_handler)orinoco_ioctl_getwap), + IW_HANDLER(SIOCSIWAP, orinoco_ioctl_setwap), + IW_HANDLER(SIOCGIWAP, orinoco_ioctl_getwap), IW_HANDLER(SIOCSIWSCAN, (iw_handler)cfg80211_wext_siwscan), IW_HANDLER(SIOCGIWSCAN, (iw_handler)cfg80211_wext_giwscan), - IW_HANDLER(SIOCSIWESSID, (iw_handler)orinoco_ioctl_setessid), - IW_HANDLER(SIOCGIWESSID, (iw_handler)orinoco_ioctl_getessid), - IW_HANDLER(SIOCSIWRATE, (iw_handler)orinoco_ioctl_setrate), - IW_HANDLER(SIOCGIWRATE, (iw_handler)orinoco_ioctl_getrate), + IW_HANDLER(SIOCSIWESSID, orinoco_ioctl_setessid), + IW_HANDLER(SIOCGIWESSID, orinoco_ioctl_getessid), + IW_HANDLER(SIOCSIWRATE, orinoco_ioctl_setrate), + IW_HANDLER(SIOCGIWRATE, orinoco_ioctl_getrate), IW_HANDLER(SIOCSIWRTS, (iw_handler)cfg80211_wext_siwrts), IW_HANDLER(SIOCGIWRTS, (iw_handler)cfg80211_wext_giwrts), IW_HANDLER(SIOCSIWFRAG, (iw_handler)cfg80211_wext_siwfrag), IW_HANDLER(SIOCGIWFRAG, (iw_handler)cfg80211_wext_giwfrag), IW_HANDLER(SIOCGIWRETRY, (iw_handler)cfg80211_wext_giwretry), - IW_HANDLER(SIOCSIWENCODE, (iw_handler)orinoco_ioctl_setiwencode), - IW_HANDLER(SIOCGIWENCODE, (iw_handler)orinoco_ioctl_getiwencode), - IW_HANDLER(SIOCSIWPOWER, (iw_handler)orinoco_ioctl_setpower), - IW_HANDLER(SIOCGIWPOWER, (iw_handler)orinoco_ioctl_getpower), + IW_HANDLER(SIOCSIWENCODE, orinoco_ioctl_setiwencode), + IW_HANDLER(SIOCGIWENCODE, orinoco_ioctl_getiwencode), + IW_HANDLER(SIOCSIWPOWER, orinoco_ioctl_setpower), + IW_HANDLER(SIOCGIWPOWER, orinoco_ioctl_getpower), IW_HANDLER(SIOCSIWGENIE, orinoco_ioctl_set_genie), IW_HANDLER(SIOCGIWGENIE, orinoco_ioctl_get_genie), IW_HANDLER(SIOCSIWMLME, orinoco_ioctl_set_mlme), @@ -1391,15 +1406,15 @@ static const iw_handler orinoco_handler[] = { Added typecasting since we no longer use iwreq_data -- Moustafa */ static const iw_handler orinoco_private_handler[] = { - [0] = (iw_handler)orinoco_ioctl_reset, - [1] = (iw_handler)orinoco_ioctl_reset, - [2] = (iw_handler)orinoco_ioctl_setport3, - [3] = (iw_handler)orinoco_ioctl_getport3, - [4] = (iw_handler)orinoco_ioctl_setpreamble, - [5] = (iw_handler)orinoco_ioctl_getpreamble, - [6] = (iw_handler)orinoco_ioctl_setibssport, - [7] = (iw_handler)orinoco_ioctl_getibssport, - [9] = (iw_handler)orinoco_ioctl_getrid, + [0] = orinoco_ioctl_reset, + [1] = orinoco_ioctl_reset, + [2] = orinoco_ioctl_setport3, + [3] = orinoco_ioctl_getport3, + [4] = orinoco_ioctl_setpreamble, + [5] = orinoco_ioctl_getpreamble, + [6] = orinoco_ioctl_setibssport, + [7] = orinoco_ioctl_getibssport, + [9] = orinoco_ioctl_getrid, }; const struct iw_handler_def orinoco_handler_def = { From 02ae6a7034d7b2e3d89e33d73da10a1f156789a0 Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Tue, 8 Nov 2022 14:23:55 -0600 Subject: [PATCH 0962/2002] wifi: cfg80211: Avoid clashing function prototypes When built with Control Flow Integrity, function prototypes between caller and function declaration must match. These mismatches are visible at compile time with the new -Wcast-function-type-strict in Clang[1]. Fix a total of 73 warnings like these: drivers/net/wireless/intersil/orinoco/wext.c:1379:27: warning: cast from 'int (*)(struct net_device *, struct iw_request_info *, struct iw_param *, char *)' to 'iw_handler' (aka 'int (*)(struct net_device *, struct iw_request_info *, union iwreq_data *, char *)') converts to incompatible function type [-Wcast-function-type-strict] IW_HANDLER(SIOCGIWPOWER, (iw_handler)orinoco_ioctl_getpower), ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ../net/wireless/wext-compat.c:1607:33: warning: cast from 'int (*)(struct net_device *, struct iw_request_info *, struct iw_point *, char *)' to 'iw_handler' (aka 'int (*)(struct net_device *, struct iw_request_info *, union iwreq_data *, char *)') converts to incompatible function type [-Wcast-function-type-strict] [IW_IOCTL_IDX(SIOCSIWGENIE)] = (iw_handler) cfg80211_wext_siwgenie, ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ../drivers/net/wireless/intersil/orinoco/wext.c:1390:27: error: incompatible function pointer types initializing 'const iw_handler' (aka 'int (*const)(struct net_device *, struct iw_request_info *, union iwreq_data *, char *)') with an expression of type 'int (struct net_device *, struct iw_request_info *, struct iw_param *, char *)' [-Wincompatible-function-pointer-types] IW_HANDLER(SIOCGIWRETRY, cfg80211_wext_giwretry), ^~~~~~~~~~~~~~~~~~~~~~ The cfg80211 Wireless Extension handler callbacks (iw_handler) use a union for the data argument. Actually use the union and perform explicit member selection in the function body instead of having a function prototype mismatch. There are no resulting binary differences before/after changes. These changes were made partly manually and partly with the help of Coccinelle. Link: https://github.com/KSPP/linux/issues/234 Link: https://reviews.llvm.org/D134831 [1] Signed-off-by: Gustavo A. R. Silva Reviewed-by: Kees Cook Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/a68822bf8dd587988131bb6a295280cb4293f05d.1667934775.git.gustavoars@kernel.org --- drivers/net/wireless/intel/ipw2x00/ipw2200.c | 2 +- drivers/net/wireless/intersil/orinoco/wext.c | 22 +-- include/net/cfg80211-wext.h | 20 +-- net/wireless/scan.c | 3 +- net/wireless/wext-compat.c | 180 +++++++++---------- net/wireless/wext-compat.h | 8 +- net/wireless/wext-sme.c | 5 +- 7 files changed, 113 insertions(+), 127 deletions(-) diff --git a/drivers/net/wireless/intel/ipw2x00/ipw2200.c b/drivers/net/wireless/intel/ipw2x00/ipw2200.c index 79d5c09757d4..ca802af8cddc 100644 --- a/drivers/net/wireless/intel/ipw2x00/ipw2200.c +++ b/drivers/net/wireless/intel/ipw2x00/ipw2200.c @@ -9856,7 +9856,7 @@ static int ipw_wx_sw_reset(struct net_device *dev, /* Rebase the WE IOCTLs to zero for the handler array */ static iw_handler ipw_wx_handlers[] = { - IW_HANDLER(SIOCGIWNAME, (iw_handler)cfg80211_wext_giwname), + IW_HANDLER(SIOCGIWNAME, cfg80211_wext_giwname), IW_HANDLER(SIOCSIWFREQ, ipw_wx_set_freq), IW_HANDLER(SIOCGIWFREQ, ipw_wx_get_freq), IW_HANDLER(SIOCSIWMODE, ipw_wx_set_mode), diff --git a/drivers/net/wireless/intersil/orinoco/wext.c b/drivers/net/wireless/intersil/orinoco/wext.c index b8eb5d60192f..dea1ff044342 100644 --- a/drivers/net/wireless/intersil/orinoco/wext.c +++ b/drivers/net/wireless/intersil/orinoco/wext.c @@ -1363,31 +1363,31 @@ static const struct iw_priv_args orinoco_privtab[] = { static const iw_handler orinoco_handler[] = { IW_HANDLER(SIOCSIWCOMMIT, orinoco_ioctl_commit), - IW_HANDLER(SIOCGIWNAME, (iw_handler)cfg80211_wext_giwname), + IW_HANDLER(SIOCGIWNAME, cfg80211_wext_giwname), IW_HANDLER(SIOCSIWFREQ, orinoco_ioctl_setfreq), IW_HANDLER(SIOCGIWFREQ, orinoco_ioctl_getfreq), - IW_HANDLER(SIOCSIWMODE, (iw_handler)cfg80211_wext_siwmode), - IW_HANDLER(SIOCGIWMODE, (iw_handler)cfg80211_wext_giwmode), + IW_HANDLER(SIOCSIWMODE, cfg80211_wext_siwmode), + IW_HANDLER(SIOCGIWMODE, cfg80211_wext_giwmode), IW_HANDLER(SIOCSIWSENS, orinoco_ioctl_setsens), IW_HANDLER(SIOCGIWSENS, orinoco_ioctl_getsens), - IW_HANDLER(SIOCGIWRANGE, (iw_handler)cfg80211_wext_giwrange), + IW_HANDLER(SIOCGIWRANGE, cfg80211_wext_giwrange), IW_HANDLER(SIOCSIWSPY, iw_handler_set_spy), IW_HANDLER(SIOCGIWSPY, iw_handler_get_spy), IW_HANDLER(SIOCSIWTHRSPY, iw_handler_set_thrspy), IW_HANDLER(SIOCGIWTHRSPY, iw_handler_get_thrspy), IW_HANDLER(SIOCSIWAP, orinoco_ioctl_setwap), IW_HANDLER(SIOCGIWAP, orinoco_ioctl_getwap), - IW_HANDLER(SIOCSIWSCAN, (iw_handler)cfg80211_wext_siwscan), - IW_HANDLER(SIOCGIWSCAN, (iw_handler)cfg80211_wext_giwscan), + IW_HANDLER(SIOCSIWSCAN, cfg80211_wext_siwscan), + IW_HANDLER(SIOCGIWSCAN, cfg80211_wext_giwscan), IW_HANDLER(SIOCSIWESSID, orinoco_ioctl_setessid), IW_HANDLER(SIOCGIWESSID, orinoco_ioctl_getessid), IW_HANDLER(SIOCSIWRATE, orinoco_ioctl_setrate), IW_HANDLER(SIOCGIWRATE, orinoco_ioctl_getrate), - IW_HANDLER(SIOCSIWRTS, (iw_handler)cfg80211_wext_siwrts), - IW_HANDLER(SIOCGIWRTS, (iw_handler)cfg80211_wext_giwrts), - IW_HANDLER(SIOCSIWFRAG, (iw_handler)cfg80211_wext_siwfrag), - IW_HANDLER(SIOCGIWFRAG, (iw_handler)cfg80211_wext_giwfrag), - IW_HANDLER(SIOCGIWRETRY, (iw_handler)cfg80211_wext_giwretry), + IW_HANDLER(SIOCSIWRTS, cfg80211_wext_siwrts), + IW_HANDLER(SIOCGIWRTS, cfg80211_wext_giwrts), + IW_HANDLER(SIOCSIWFRAG, cfg80211_wext_siwfrag), + IW_HANDLER(SIOCGIWFRAG, cfg80211_wext_giwfrag), + IW_HANDLER(SIOCGIWRETRY, cfg80211_wext_giwretry), IW_HANDLER(SIOCSIWENCODE, orinoco_ioctl_setiwencode), IW_HANDLER(SIOCGIWENCODE, orinoco_ioctl_getiwencode), IW_HANDLER(SIOCSIWPOWER, orinoco_ioctl_setpower), diff --git a/include/net/cfg80211-wext.h b/include/net/cfg80211-wext.h index ad77caf2ffde..0ee36d97e068 100644 --- a/include/net/cfg80211-wext.h +++ b/include/net/cfg80211-wext.h @@ -19,34 +19,34 @@ */ int cfg80211_wext_giwname(struct net_device *dev, struct iw_request_info *info, - char *name, char *extra); + union iwreq_data *wrqu, char *extra); int cfg80211_wext_siwmode(struct net_device *dev, struct iw_request_info *info, - u32 *mode, char *extra); + union iwreq_data *wrqu, char *extra); int cfg80211_wext_giwmode(struct net_device *dev, struct iw_request_info *info, - u32 *mode, char *extra); + union iwreq_data *wrqu, char *extra); int cfg80211_wext_siwscan(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra); int cfg80211_wext_giwscan(struct net_device *dev, struct iw_request_info *info, - struct iw_point *data, char *extra); + union iwreq_data *wrqu, char *extra); int cfg80211_wext_giwrange(struct net_device *dev, struct iw_request_info *info, - struct iw_point *data, char *extra); + union iwreq_data *wrqu, char *extra); int cfg80211_wext_siwrts(struct net_device *dev, struct iw_request_info *info, - struct iw_param *rts, char *extra); + union iwreq_data *wrqu, char *extra); int cfg80211_wext_giwrts(struct net_device *dev, struct iw_request_info *info, - struct iw_param *rts, char *extra); + union iwreq_data *wrqu, char *extra); int cfg80211_wext_siwfrag(struct net_device *dev, struct iw_request_info *info, - struct iw_param *frag, char *extra); + union iwreq_data *wrqu, char *extra); int cfg80211_wext_giwfrag(struct net_device *dev, struct iw_request_info *info, - struct iw_param *frag, char *extra); + union iwreq_data *wrqu, char *extra); int cfg80211_wext_giwretry(struct net_device *dev, struct iw_request_info *info, - struct iw_param *retry, char *extra); + union iwreq_data *wrqu, char *extra); #endif /* __NET_CFG80211_WEXT_H */ diff --git a/net/wireless/scan.c b/net/wireless/scan.c index 806a5f1330ff..853619bc0f1a 100644 --- a/net/wireless/scan.c +++ b/net/wireless/scan.c @@ -3229,8 +3229,9 @@ static int ieee80211_scan_results(struct cfg80211_registered_device *rdev, int cfg80211_wext_giwscan(struct net_device *dev, struct iw_request_info *info, - struct iw_point *data, char *extra) + union iwreq_data *wrqu, char *extra) { + struct iw_point *data = &wrqu->data; struct cfg80211_registered_device *rdev; int res; diff --git a/net/wireless/wext-compat.c b/net/wireless/wext-compat.c index ddf340bfa07a..8a24dfca75af 100644 --- a/net/wireless/wext-compat.c +++ b/net/wireless/wext-compat.c @@ -25,16 +25,17 @@ int cfg80211_wext_giwname(struct net_device *dev, struct iw_request_info *info, - char *name, char *extra) + union iwreq_data *wrqu, char *extra) { - strcpy(name, "IEEE 802.11"); + strcpy(wrqu->name, "IEEE 802.11"); return 0; } EXPORT_WEXT_HANDLER(cfg80211_wext_giwname); int cfg80211_wext_siwmode(struct net_device *dev, struct iw_request_info *info, - u32 *mode, char *extra) + union iwreq_data *wrqu, char *extra) { + __u32 *mode = &wrqu->mode; struct wireless_dev *wdev = dev->ieee80211_ptr; struct cfg80211_registered_device *rdev; struct vif_params vifparams; @@ -71,8 +72,9 @@ int cfg80211_wext_siwmode(struct net_device *dev, struct iw_request_info *info, EXPORT_WEXT_HANDLER(cfg80211_wext_siwmode); int cfg80211_wext_giwmode(struct net_device *dev, struct iw_request_info *info, - u32 *mode, char *extra) + union iwreq_data *wrqu, char *extra) { + __u32 *mode = &wrqu->mode; struct wireless_dev *wdev = dev->ieee80211_ptr; if (!wdev) @@ -108,8 +110,9 @@ EXPORT_WEXT_HANDLER(cfg80211_wext_giwmode); int cfg80211_wext_giwrange(struct net_device *dev, struct iw_request_info *info, - struct iw_point *data, char *extra) + union iwreq_data *wrqu, char *extra) { + struct iw_point *data = &wrqu->data; struct wireless_dev *wdev = dev->ieee80211_ptr; struct iw_range *range = (struct iw_range *) extra; enum nl80211_band band; @@ -251,8 +254,9 @@ int cfg80211_wext_freq(struct iw_freq *freq) int cfg80211_wext_siwrts(struct net_device *dev, struct iw_request_info *info, - struct iw_param *rts, char *extra) + union iwreq_data *wrqu, char *extra) { + struct iw_param *rts = &wrqu->rts; struct wireless_dev *wdev = dev->ieee80211_ptr; struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy); u32 orts = wdev->wiphy->rts_threshold; @@ -281,8 +285,9 @@ EXPORT_WEXT_HANDLER(cfg80211_wext_siwrts); int cfg80211_wext_giwrts(struct net_device *dev, struct iw_request_info *info, - struct iw_param *rts, char *extra) + union iwreq_data *wrqu, char *extra) { + struct iw_param *rts = &wrqu->rts; struct wireless_dev *wdev = dev->ieee80211_ptr; rts->value = wdev->wiphy->rts_threshold; @@ -295,8 +300,9 @@ EXPORT_WEXT_HANDLER(cfg80211_wext_giwrts); int cfg80211_wext_siwfrag(struct net_device *dev, struct iw_request_info *info, - struct iw_param *frag, char *extra) + union iwreq_data *wrqu, char *extra) { + struct iw_param *frag = &wrqu->frag; struct wireless_dev *wdev = dev->ieee80211_ptr; struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy); u32 ofrag = wdev->wiphy->frag_threshold; @@ -325,8 +331,9 @@ EXPORT_WEXT_HANDLER(cfg80211_wext_siwfrag); int cfg80211_wext_giwfrag(struct net_device *dev, struct iw_request_info *info, - struct iw_param *frag, char *extra) + union iwreq_data *wrqu, char *extra) { + struct iw_param *frag = &wrqu->frag; struct wireless_dev *wdev = dev->ieee80211_ptr; frag->value = wdev->wiphy->frag_threshold; @@ -339,8 +346,9 @@ EXPORT_WEXT_HANDLER(cfg80211_wext_giwfrag); static int cfg80211_wext_siwretry(struct net_device *dev, struct iw_request_info *info, - struct iw_param *retry, char *extra) + union iwreq_data *wrqu, char *extra) { + struct iw_param *retry = &wrqu->retry; struct wireless_dev *wdev = dev->ieee80211_ptr; struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy); u32 changed = 0; @@ -378,8 +386,9 @@ static int cfg80211_wext_siwretry(struct net_device *dev, int cfg80211_wext_giwretry(struct net_device *dev, struct iw_request_info *info, - struct iw_param *retry, char *extra) + union iwreq_data *wrqu, char *extra) { + struct iw_param *retry = &wrqu->retry; struct wireless_dev *wdev = dev->ieee80211_ptr; retry->disabled = 0; @@ -588,8 +597,9 @@ static int cfg80211_set_encryption(struct cfg80211_registered_device *rdev, static int cfg80211_wext_siwencode(struct net_device *dev, struct iw_request_info *info, - struct iw_point *erq, char *keybuf) + union iwreq_data *wrqu, char *keybuf) { + struct iw_point *erq = &wrqu->encoding; struct wireless_dev *wdev = dev->ieee80211_ptr; struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy); int idx, err; @@ -664,8 +674,9 @@ out: static int cfg80211_wext_siwencodeext(struct net_device *dev, struct iw_request_info *info, - struct iw_point *erq, char *extra) + union iwreq_data *wrqu, char *extra) { + struct iw_point *erq = &wrqu->encoding; struct wireless_dev *wdev = dev->ieee80211_ptr; struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy); struct iw_encode_ext *ext = (struct iw_encode_ext *) extra; @@ -767,8 +778,9 @@ static int cfg80211_wext_siwencodeext(struct net_device *dev, static int cfg80211_wext_giwencode(struct net_device *dev, struct iw_request_info *info, - struct iw_point *erq, char *keybuf) + union iwreq_data *wrqu, char *keybuf) { + struct iw_point *erq = &wrqu->encoding; struct wireless_dev *wdev = dev->ieee80211_ptr; int idx; @@ -804,8 +816,9 @@ static int cfg80211_wext_giwencode(struct net_device *dev, static int cfg80211_wext_siwfreq(struct net_device *dev, struct iw_request_info *info, - struct iw_freq *wextfreq, char *extra) + union iwreq_data *wrqu, char *extra) { + struct iw_freq *wextfreq = &wrqu->freq; struct wireless_dev *wdev = dev->ieee80211_ptr; struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy); struct cfg80211_chan_def chandef = { @@ -870,8 +883,9 @@ static int cfg80211_wext_siwfreq(struct net_device *dev, static int cfg80211_wext_giwfreq(struct net_device *dev, struct iw_request_info *info, - struct iw_freq *freq, char *extra) + union iwreq_data *wrqu, char *extra) { + struct iw_freq *freq = &wrqu->freq; struct wireless_dev *wdev = dev->ieee80211_ptr; struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy); struct cfg80211_chan_def chandef = {}; @@ -1147,8 +1161,9 @@ static int cfg80211_set_key_mgt(struct wireless_dev *wdev, u32 key_mgt) static int cfg80211_wext_siwauth(struct net_device *dev, struct iw_request_info *info, - struct iw_param *data, char *extra) + union iwreq_data *wrqu, char *extra) { + struct iw_param *data = &wrqu->param; struct wireless_dev *wdev = dev->ieee80211_ptr; if (wdev->iftype != NL80211_IFTYPE_STATION) @@ -1180,7 +1195,7 @@ static int cfg80211_wext_siwauth(struct net_device *dev, static int cfg80211_wext_giwauth(struct net_device *dev, struct iw_request_info *info, - struct iw_param *data, char *extra) + union iwreq_data *wrqu, char *extra) { /* XXX: what do we need? */ @@ -1189,8 +1204,9 @@ static int cfg80211_wext_giwauth(struct net_device *dev, static int cfg80211_wext_siwpower(struct net_device *dev, struct iw_request_info *info, - struct iw_param *wrq, char *extra) + union iwreq_data *wrqu, char *extra) { + struct iw_param *wrq = &wrqu->power; struct wireless_dev *wdev = dev->ieee80211_ptr; struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy); bool ps; @@ -1238,8 +1254,9 @@ static int cfg80211_wext_siwpower(struct net_device *dev, static int cfg80211_wext_giwpower(struct net_device *dev, struct iw_request_info *info, - struct iw_param *wrq, char *extra) + union iwreq_data *wrqu, char *extra) { + struct iw_param *wrq = &wrqu->power; struct wireless_dev *wdev = dev->ieee80211_ptr; wrq->disabled = !wdev->ps; @@ -1249,8 +1266,9 @@ static int cfg80211_wext_giwpower(struct net_device *dev, static int cfg80211_wext_siwrate(struct net_device *dev, struct iw_request_info *info, - struct iw_param *rate, char *extra) + union iwreq_data *wrqu, char *extra) { + struct iw_param *rate = &wrqu->bitrate; struct wireless_dev *wdev = dev->ieee80211_ptr; struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy); struct cfg80211_bitrate_mask mask; @@ -1307,8 +1325,9 @@ static int cfg80211_wext_siwrate(struct net_device *dev, static int cfg80211_wext_giwrate(struct net_device *dev, struct iw_request_info *info, - struct iw_param *rate, char *extra) + union iwreq_data *wrqu, char *extra) { + struct iw_param *rate = &wrqu->bitrate; struct wireless_dev *wdev = dev->ieee80211_ptr; struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy); struct station_info sinfo = {}; @@ -1430,8 +1449,9 @@ static struct iw_statistics *cfg80211_wireless_stats(struct net_device *dev) static int cfg80211_wext_siwap(struct net_device *dev, struct iw_request_info *info, - struct sockaddr *ap_addr, char *extra) + union iwreq_data *wrqu, char *extra) { + struct sockaddr *ap_addr = &wrqu->ap_addr; struct wireless_dev *wdev = dev->ieee80211_ptr; struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy); int ret; @@ -1455,8 +1475,9 @@ static int cfg80211_wext_siwap(struct net_device *dev, static int cfg80211_wext_giwap(struct net_device *dev, struct iw_request_info *info, - struct sockaddr *ap_addr, char *extra) + union iwreq_data *wrqu, char *extra) { + struct sockaddr *ap_addr = &wrqu->ap_addr; struct wireless_dev *wdev = dev->ieee80211_ptr; struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy); int ret; @@ -1480,8 +1501,9 @@ static int cfg80211_wext_giwap(struct net_device *dev, static int cfg80211_wext_siwessid(struct net_device *dev, struct iw_request_info *info, - struct iw_point *data, char *ssid) + union iwreq_data *wrqu, char *ssid) { + struct iw_point *data = &wrqu->data; struct wireless_dev *wdev = dev->ieee80211_ptr; struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy); int ret; @@ -1505,8 +1527,9 @@ static int cfg80211_wext_siwessid(struct net_device *dev, static int cfg80211_wext_giwessid(struct net_device *dev, struct iw_request_info *info, - struct iw_point *data, char *ssid) + union iwreq_data *wrqu, char *ssid) { + struct iw_point *data = &wrqu->data; struct wireless_dev *wdev = dev->ieee80211_ptr; struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy); int ret; @@ -1533,7 +1556,7 @@ static int cfg80211_wext_giwessid(struct net_device *dev, static int cfg80211_wext_siwpmksa(struct net_device *dev, struct iw_request_info *info, - struct iw_point *data, char *extra) + union iwreq_data *wrqu, char *extra) { struct wireless_dev *wdev = dev->ieee80211_ptr; struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy); @@ -1584,78 +1607,39 @@ static int cfg80211_wext_siwpmksa(struct net_device *dev, return ret; } -#define DEFINE_WEXT_COMPAT_STUB(func, type) \ - static int __ ## func(struct net_device *dev, \ - struct iw_request_info *info, \ - union iwreq_data *wrqu, \ - char *extra) \ - { \ - return func(dev, info, (type *)wrqu, extra); \ - } - -DEFINE_WEXT_COMPAT_STUB(cfg80211_wext_giwname, char) -DEFINE_WEXT_COMPAT_STUB(cfg80211_wext_siwfreq, struct iw_freq) -DEFINE_WEXT_COMPAT_STUB(cfg80211_wext_giwfreq, struct iw_freq) -DEFINE_WEXT_COMPAT_STUB(cfg80211_wext_siwmode, u32) -DEFINE_WEXT_COMPAT_STUB(cfg80211_wext_giwmode, u32) -DEFINE_WEXT_COMPAT_STUB(cfg80211_wext_giwrange, struct iw_point) -DEFINE_WEXT_COMPAT_STUB(cfg80211_wext_siwap, struct sockaddr) -DEFINE_WEXT_COMPAT_STUB(cfg80211_wext_giwap, struct sockaddr) -DEFINE_WEXT_COMPAT_STUB(cfg80211_wext_siwmlme, struct iw_point) -DEFINE_WEXT_COMPAT_STUB(cfg80211_wext_giwscan, struct iw_point) -DEFINE_WEXT_COMPAT_STUB(cfg80211_wext_siwessid, struct iw_point) -DEFINE_WEXT_COMPAT_STUB(cfg80211_wext_giwessid, struct iw_point) -DEFINE_WEXT_COMPAT_STUB(cfg80211_wext_siwrate, struct iw_param) -DEFINE_WEXT_COMPAT_STUB(cfg80211_wext_giwrate, struct iw_param) -DEFINE_WEXT_COMPAT_STUB(cfg80211_wext_siwrts, struct iw_param) -DEFINE_WEXT_COMPAT_STUB(cfg80211_wext_giwrts, struct iw_param) -DEFINE_WEXT_COMPAT_STUB(cfg80211_wext_siwfrag, struct iw_param) -DEFINE_WEXT_COMPAT_STUB(cfg80211_wext_giwfrag, struct iw_param) -DEFINE_WEXT_COMPAT_STUB(cfg80211_wext_siwretry, struct iw_param) -DEFINE_WEXT_COMPAT_STUB(cfg80211_wext_giwretry, struct iw_param) -DEFINE_WEXT_COMPAT_STUB(cfg80211_wext_siwencode, struct iw_point) -DEFINE_WEXT_COMPAT_STUB(cfg80211_wext_giwencode, struct iw_point) -DEFINE_WEXT_COMPAT_STUB(cfg80211_wext_giwpower, struct iw_param) -DEFINE_WEXT_COMPAT_STUB(cfg80211_wext_siwpower, struct iw_param) -DEFINE_WEXT_COMPAT_STUB(cfg80211_wext_siwgenie, struct iw_point) -DEFINE_WEXT_COMPAT_STUB(cfg80211_wext_giwauth, struct iw_param) -DEFINE_WEXT_COMPAT_STUB(cfg80211_wext_siwauth, struct iw_param) -DEFINE_WEXT_COMPAT_STUB(cfg80211_wext_siwencodeext, struct iw_point) -DEFINE_WEXT_COMPAT_STUB(cfg80211_wext_siwpmksa, struct iw_point) - static const iw_handler cfg80211_handlers[] = { - [IW_IOCTL_IDX(SIOCGIWNAME)] = __cfg80211_wext_giwname, - [IW_IOCTL_IDX(SIOCSIWFREQ)] = __cfg80211_wext_siwfreq, - [IW_IOCTL_IDX(SIOCGIWFREQ)] = __cfg80211_wext_giwfreq, - [IW_IOCTL_IDX(SIOCSIWMODE)] = __cfg80211_wext_siwmode, - [IW_IOCTL_IDX(SIOCGIWMODE)] = __cfg80211_wext_giwmode, - [IW_IOCTL_IDX(SIOCGIWRANGE)] = __cfg80211_wext_giwrange, - [IW_IOCTL_IDX(SIOCSIWAP)] = __cfg80211_wext_siwap, - [IW_IOCTL_IDX(SIOCGIWAP)] = __cfg80211_wext_giwap, - [IW_IOCTL_IDX(SIOCSIWMLME)] = __cfg80211_wext_siwmlme, - [IW_IOCTL_IDX(SIOCSIWSCAN)] = cfg80211_wext_siwscan, - [IW_IOCTL_IDX(SIOCGIWSCAN)] = __cfg80211_wext_giwscan, - [IW_IOCTL_IDX(SIOCSIWESSID)] = __cfg80211_wext_siwessid, - [IW_IOCTL_IDX(SIOCGIWESSID)] = __cfg80211_wext_giwessid, - [IW_IOCTL_IDX(SIOCSIWRATE)] = __cfg80211_wext_siwrate, - [IW_IOCTL_IDX(SIOCGIWRATE)] = __cfg80211_wext_giwrate, - [IW_IOCTL_IDX(SIOCSIWRTS)] = __cfg80211_wext_siwrts, - [IW_IOCTL_IDX(SIOCGIWRTS)] = __cfg80211_wext_giwrts, - [IW_IOCTL_IDX(SIOCSIWFRAG)] = __cfg80211_wext_siwfrag, - [IW_IOCTL_IDX(SIOCGIWFRAG)] = __cfg80211_wext_giwfrag, - [IW_IOCTL_IDX(SIOCSIWTXPOW)] = cfg80211_wext_siwtxpower, - [IW_IOCTL_IDX(SIOCGIWTXPOW)] = cfg80211_wext_giwtxpower, - [IW_IOCTL_IDX(SIOCSIWRETRY)] = __cfg80211_wext_siwretry, - [IW_IOCTL_IDX(SIOCGIWRETRY)] = __cfg80211_wext_giwretry, - [IW_IOCTL_IDX(SIOCSIWENCODE)] = __cfg80211_wext_siwencode, - [IW_IOCTL_IDX(SIOCGIWENCODE)] = __cfg80211_wext_giwencode, - [IW_IOCTL_IDX(SIOCSIWPOWER)] = __cfg80211_wext_siwpower, - [IW_IOCTL_IDX(SIOCGIWPOWER)] = __cfg80211_wext_giwpower, - [IW_IOCTL_IDX(SIOCSIWGENIE)] = __cfg80211_wext_siwgenie, - [IW_IOCTL_IDX(SIOCSIWAUTH)] = __cfg80211_wext_siwauth, - [IW_IOCTL_IDX(SIOCGIWAUTH)] = __cfg80211_wext_giwauth, - [IW_IOCTL_IDX(SIOCSIWENCODEEXT)]= __cfg80211_wext_siwencodeext, - [IW_IOCTL_IDX(SIOCSIWPMKSA)] = __cfg80211_wext_siwpmksa, + IW_HANDLER(SIOCGIWNAME, cfg80211_wext_giwname), + IW_HANDLER(SIOCSIWFREQ, cfg80211_wext_siwfreq), + IW_HANDLER(SIOCGIWFREQ, cfg80211_wext_giwfreq), + IW_HANDLER(SIOCSIWMODE, cfg80211_wext_siwmode), + IW_HANDLER(SIOCGIWMODE, cfg80211_wext_giwmode), + IW_HANDLER(SIOCGIWRANGE, cfg80211_wext_giwrange), + IW_HANDLER(SIOCSIWAP, cfg80211_wext_siwap), + IW_HANDLER(SIOCGIWAP, cfg80211_wext_giwap), + IW_HANDLER(SIOCSIWMLME, cfg80211_wext_siwmlme), + IW_HANDLER(SIOCSIWSCAN, cfg80211_wext_siwscan), + IW_HANDLER(SIOCGIWSCAN, cfg80211_wext_giwscan), + IW_HANDLER(SIOCSIWESSID, cfg80211_wext_siwessid), + IW_HANDLER(SIOCGIWESSID, cfg80211_wext_giwessid), + IW_HANDLER(SIOCSIWRATE, cfg80211_wext_siwrate), + IW_HANDLER(SIOCGIWRATE, cfg80211_wext_giwrate), + IW_HANDLER(SIOCSIWRTS, cfg80211_wext_siwrts), + IW_HANDLER(SIOCGIWRTS, cfg80211_wext_giwrts), + IW_HANDLER(SIOCSIWFRAG, cfg80211_wext_siwfrag), + IW_HANDLER(SIOCGIWFRAG, cfg80211_wext_giwfrag), + IW_HANDLER(SIOCSIWTXPOW, cfg80211_wext_siwtxpower), + IW_HANDLER(SIOCGIWTXPOW, cfg80211_wext_giwtxpower), + IW_HANDLER(SIOCSIWRETRY, cfg80211_wext_siwretry), + IW_HANDLER(SIOCGIWRETRY, cfg80211_wext_giwretry), + IW_HANDLER(SIOCSIWENCODE, cfg80211_wext_siwencode), + IW_HANDLER(SIOCGIWENCODE, cfg80211_wext_giwencode), + IW_HANDLER(SIOCSIWPOWER, cfg80211_wext_siwpower), + IW_HANDLER(SIOCGIWPOWER, cfg80211_wext_giwpower), + IW_HANDLER(SIOCSIWGENIE, cfg80211_wext_siwgenie), + IW_HANDLER(SIOCSIWAUTH, cfg80211_wext_siwauth), + IW_HANDLER(SIOCGIWAUTH, cfg80211_wext_giwauth), + IW_HANDLER(SIOCSIWENCODEEXT, cfg80211_wext_siwencodeext), + IW_HANDLER(SIOCSIWPMKSA, cfg80211_wext_siwpmksa), }; const struct iw_handler_def cfg80211_wext_handler = { diff --git a/net/wireless/wext-compat.h b/net/wireless/wext-compat.h index 8d3cc1552e2f..c02eb789e676 100644 --- a/net/wireless/wext-compat.h +++ b/net/wireless/wext-compat.h @@ -13,7 +13,7 @@ int cfg80211_ibss_wext_siwfreq(struct net_device *dev, struct iw_request_info *info, - struct iw_freq *freq, char *extra); + struct iw_freq *wextfreq, char *extra); int cfg80211_ibss_wext_giwfreq(struct net_device *dev, struct iw_request_info *info, struct iw_freq *freq, char *extra); @@ -32,7 +32,7 @@ int cfg80211_ibss_wext_giwessid(struct net_device *dev, int cfg80211_mgd_wext_siwfreq(struct net_device *dev, struct iw_request_info *info, - struct iw_freq *freq, char *extra); + struct iw_freq *wextfreq, char *extra); int cfg80211_mgd_wext_giwfreq(struct net_device *dev, struct iw_request_info *info, struct iw_freq *freq, char *extra); @@ -51,10 +51,10 @@ int cfg80211_mgd_wext_giwessid(struct net_device *dev, int cfg80211_wext_siwmlme(struct net_device *dev, struct iw_request_info *info, - struct iw_point *data, char *extra); + union iwreq_data *wrqu, char *extra); int cfg80211_wext_siwgenie(struct net_device *dev, struct iw_request_info *info, - struct iw_point *data, char *extra); + union iwreq_data *wrqu, char *extra); int cfg80211_wext_freq(struct iw_freq *freq); diff --git a/net/wireless/wext-sme.c b/net/wireless/wext-sme.c index 68f45afc352d..191c6d98c700 100644 --- a/net/wireless/wext-sme.c +++ b/net/wireless/wext-sme.c @@ -324,8 +324,9 @@ int cfg80211_mgd_wext_giwap(struct net_device *dev, int cfg80211_wext_siwgenie(struct net_device *dev, struct iw_request_info *info, - struct iw_point *data, char *extra) + union iwreq_data *wrqu, char *extra) { + struct iw_point *data = &wrqu->data; struct wireless_dev *wdev = dev->ieee80211_ptr; struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy); u8 *ie = extra; @@ -374,7 +375,7 @@ int cfg80211_wext_siwgenie(struct net_device *dev, int cfg80211_wext_siwmlme(struct net_device *dev, struct iw_request_info *info, - struct iw_point *data, char *extra) + union iwreq_data *wrqu, char *extra) { struct wireless_dev *wdev = dev->ieee80211_ptr; struct iw_mlme *mlme = (struct iw_mlme *)extra; From fd7ef879a9835f9374fd611f2d6118e4a8f9b919 Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Tue, 8 Nov 2022 14:25:17 -0600 Subject: [PATCH 0963/2002] wifi: hostap: Avoid clashing function prototypes When built with Control Flow Integrity, function prototypes between caller and function declaration must match. These mismatches are visible at compile time with the new -Wcast-function-type-strict in Clang[1]. Fix a total of 42 warnings like these: ../drivers/net/wireless/intersil/hostap/hostap_ioctl.c:3868:2: warning: cast from 'int (*)(struct net_device *, struct iw_request_info *, char *, char *)' to 'iw_handler' (aka 'int (*)(struct net_device *, struct iw_request_info *, union iwreq_data *, char *)') converts to incompatible function type [-Wcast-function-type-strict] (iw_handler) prism2_get_name, /* SIOCGIWNAME */ ^~~~~~~~~~~~~~~~~~~~~~~~~~~~ The hostap Wireless Extension handler callbacks (iw_handler) use a union for the data argument. Actually use the union and perform explicit member selection in the function body instead of having a function prototype mismatch. There are no resulting binary differences before/after changes. These changes were made partly manually and partly with the help of Coccinelle. Link: https://github.com/KSPP/linux/issues/235 Link: https://reviews.llvm.org/D134831 [1] Signed-off-by: Gustavo A. R. Silva Reviewed-by: Kees Cook Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/e480e7713f1a4909ae011068c8d793cc4a638fbd.1667934775.git.gustavoars@kernel.org --- .../wireless/intersil/hostap/hostap_ioctl.c | 244 ++++++++++-------- 1 file changed, 133 insertions(+), 111 deletions(-) diff --git a/drivers/net/wireless/intersil/hostap/hostap_ioctl.c b/drivers/net/wireless/intersil/hostap/hostap_ioctl.c index 4e0a0c881697..26287b129d18 100644 --- a/drivers/net/wireless/intersil/hostap/hostap_ioctl.c +++ b/drivers/net/wireless/intersil/hostap/hostap_ioctl.c @@ -91,7 +91,7 @@ static int prism2_get_datarates(struct net_device *dev, u8 *rates) static int prism2_get_name(struct net_device *dev, struct iw_request_info *info, - char *name, char *extra) + union iwreq_data *wrqu, char *extra) { u8 rates[10]; int len, i, over2 = 0; @@ -105,7 +105,7 @@ static int prism2_get_name(struct net_device *dev, } } - strcpy(name, over2 ? "IEEE 802.11b" : "IEEE 802.11-DS"); + strcpy(wrqu->name, over2 ? "IEEE 802.11b" : "IEEE 802.11-DS"); return 0; } @@ -113,8 +113,9 @@ static int prism2_get_name(struct net_device *dev, static int prism2_ioctl_siwencode(struct net_device *dev, struct iw_request_info *info, - struct iw_point *erq, char *keybuf) + union iwreq_data *wrqu, char *keybuf) { + struct iw_point *erq = &wrqu->encoding; struct hostap_interface *iface; local_info_t *local; int i; @@ -215,8 +216,9 @@ static int prism2_ioctl_siwencode(struct net_device *dev, static int prism2_ioctl_giwencode(struct net_device *dev, struct iw_request_info *info, - struct iw_point *erq, char *key) + union iwreq_data *wrqu, char *key) { + struct iw_point *erq = &wrqu->encoding; struct hostap_interface *iface; local_info_t *local; int i, len; @@ -321,8 +323,9 @@ static int hostap_set_rate(struct net_device *dev) static int prism2_ioctl_siwrate(struct net_device *dev, struct iw_request_info *info, - struct iw_param *rrq, char *extra) + union iwreq_data *wrqu, char *extra) { + struct iw_param *rrq = &wrqu->bitrate; struct hostap_interface *iface; local_info_t *local; @@ -381,8 +384,9 @@ static int prism2_ioctl_siwrate(struct net_device *dev, static int prism2_ioctl_giwrate(struct net_device *dev, struct iw_request_info *info, - struct iw_param *rrq, char *extra) + union iwreq_data *wrqu, char *extra) { + struct iw_param *rrq = &wrqu->bitrate; u16 val; struct hostap_interface *iface; local_info_t *local; @@ -440,8 +444,9 @@ static int prism2_ioctl_giwrate(struct net_device *dev, static int prism2_ioctl_siwsens(struct net_device *dev, struct iw_request_info *info, - struct iw_param *sens, char *extra) + union iwreq_data *wrqu, char *extra) { + struct iw_param *sens = &wrqu->sens; struct hostap_interface *iface; local_info_t *local; @@ -461,8 +466,9 @@ static int prism2_ioctl_siwsens(struct net_device *dev, static int prism2_ioctl_giwsens(struct net_device *dev, struct iw_request_info *info, - struct iw_param *sens, char *extra) + union iwreq_data *wrqu, char *extra) { + struct iw_param *sens = &wrqu->sens; struct hostap_interface *iface; local_info_t *local; __le16 val; @@ -485,8 +491,9 @@ static int prism2_ioctl_giwsens(struct net_device *dev, /* Deprecated in new wireless extension API */ static int prism2_ioctl_giwaplist(struct net_device *dev, struct iw_request_info *info, - struct iw_point *data, char *extra) + union iwreq_data *wrqu, char *extra) { + struct iw_point *data = &wrqu->data; struct hostap_interface *iface; local_info_t *local; struct sockaddr *addr; @@ -526,8 +533,9 @@ static int prism2_ioctl_giwaplist(struct net_device *dev, static int prism2_ioctl_siwrts(struct net_device *dev, struct iw_request_info *info, - struct iw_param *rts, char *extra) + union iwreq_data *wrqu, char *extra) { + struct iw_param *rts = &wrqu->rts; struct hostap_interface *iface; local_info_t *local; __le16 val; @@ -553,8 +561,9 @@ static int prism2_ioctl_siwrts(struct net_device *dev, static int prism2_ioctl_giwrts(struct net_device *dev, struct iw_request_info *info, - struct iw_param *rts, char *extra) + union iwreq_data *wrqu, char *extra) { + struct iw_param *rts = &wrqu->rts; struct hostap_interface *iface; local_info_t *local; __le16 val; @@ -576,8 +585,9 @@ static int prism2_ioctl_giwrts(struct net_device *dev, static int prism2_ioctl_siwfrag(struct net_device *dev, struct iw_request_info *info, - struct iw_param *rts, char *extra) + union iwreq_data *wrqu, char *extra) { + struct iw_param *rts = &wrqu->rts; struct hostap_interface *iface; local_info_t *local; __le16 val; @@ -603,8 +613,9 @@ static int prism2_ioctl_siwfrag(struct net_device *dev, static int prism2_ioctl_giwfrag(struct net_device *dev, struct iw_request_info *info, - struct iw_param *rts, char *extra) + union iwreq_data *wrqu, char *extra) { + struct iw_param *rts = &wrqu->rts; struct hostap_interface *iface; local_info_t *local; __le16 val; @@ -669,8 +680,9 @@ static int hostap_join_ap(struct net_device *dev) static int prism2_ioctl_siwap(struct net_device *dev, struct iw_request_info *info, - struct sockaddr *ap_addr, char *extra) + union iwreq_data *wrqu, char *extra) { + struct sockaddr *ap_addr = &wrqu->ap_addr; #ifdef PRISM2_NO_STATION_MODES return -EOPNOTSUPP; #else /* PRISM2_NO_STATION_MODES */ @@ -709,8 +721,9 @@ static int prism2_ioctl_siwap(struct net_device *dev, static int prism2_ioctl_giwap(struct net_device *dev, struct iw_request_info *info, - struct sockaddr *ap_addr, char *extra) + union iwreq_data *wrqu, char *extra) { + struct sockaddr *ap_addr = &wrqu->ap_addr; struct hostap_interface *iface; local_info_t *local; @@ -745,8 +758,9 @@ static int prism2_ioctl_giwap(struct net_device *dev, static int prism2_ioctl_siwnickn(struct net_device *dev, struct iw_request_info *info, - struct iw_point *data, char *nickname) + union iwreq_data *wrqu, char *nickname) { + struct iw_point *data = &wrqu->data; struct hostap_interface *iface; local_info_t *local; @@ -766,8 +780,9 @@ static int prism2_ioctl_siwnickn(struct net_device *dev, static int prism2_ioctl_giwnickn(struct net_device *dev, struct iw_request_info *info, - struct iw_point *data, char *nickname) + union iwreq_data *wrqu, char *nickname) { + struct iw_point *data = &wrqu->data; struct hostap_interface *iface; local_info_t *local; int len; @@ -793,8 +808,9 @@ static int prism2_ioctl_giwnickn(struct net_device *dev, static int prism2_ioctl_siwfreq(struct net_device *dev, struct iw_request_info *info, - struct iw_freq *freq, char *extra) + union iwreq_data *wrqu, char *extra) { + struct iw_freq *freq = &wrqu->freq; struct hostap_interface *iface; local_info_t *local; @@ -830,8 +846,9 @@ static int prism2_ioctl_siwfreq(struct net_device *dev, static int prism2_ioctl_giwfreq(struct net_device *dev, struct iw_request_info *info, - struct iw_freq *freq, char *extra) + union iwreq_data *wrqu, char *extra) { + struct iw_freq *freq = &wrqu->freq; struct hostap_interface *iface; local_info_t *local; u16 val; @@ -874,8 +891,9 @@ static void hostap_monitor_set_type(local_info_t *local) static int prism2_ioctl_siwessid(struct net_device *dev, struct iw_request_info *info, - struct iw_point *data, char *ssid) + union iwreq_data *wrqu, char *ssid) { + struct iw_point *data = &wrqu->data; struct hostap_interface *iface; local_info_t *local; @@ -910,8 +928,9 @@ static int prism2_ioctl_siwessid(struct net_device *dev, static int prism2_ioctl_giwessid(struct net_device *dev, struct iw_request_info *info, - struct iw_point *data, char *essid) + union iwreq_data *wrqu, char *essid) { + struct iw_point *data = &wrqu->data; struct hostap_interface *iface; local_info_t *local; u16 val; @@ -946,8 +965,9 @@ static int prism2_ioctl_giwessid(struct net_device *dev, static int prism2_ioctl_giwrange(struct net_device *dev, struct iw_request_info *info, - struct iw_point *data, char *extra) + union iwreq_data *wrqu, char *extra) { + struct iw_point *data = &wrqu->data; struct hostap_interface *iface; local_info_t *local; struct iw_range *range = (struct iw_range *) extra; @@ -1121,8 +1141,9 @@ static int hostap_monitor_mode_disable(local_info_t *local) static int prism2_ioctl_siwmode(struct net_device *dev, struct iw_request_info *info, - __u32 *mode, char *extra) + union iwreq_data *wrqu, char *extra) { + __u32 *mode = &wrqu->mode; struct hostap_interface *iface; local_info_t *local; int double_reset = 0; @@ -1197,8 +1218,9 @@ static int prism2_ioctl_siwmode(struct net_device *dev, static int prism2_ioctl_giwmode(struct net_device *dev, struct iw_request_info *info, - __u32 *mode, char *extra) + union iwreq_data *wrqu, char *extra) { + __u32 *mode = &wrqu->mode; struct hostap_interface *iface; local_info_t *local; @@ -1222,8 +1244,9 @@ static int prism2_ioctl_giwmode(struct net_device *dev, static int prism2_ioctl_siwpower(struct net_device *dev, struct iw_request_info *info, - struct iw_param *wrq, char *extra) + union iwreq_data *wrqu, char *extra) { + struct iw_param *wrq = &wrqu->power; #ifdef PRISM2_NO_STATION_MODES return -EOPNOTSUPP; #else /* PRISM2_NO_STATION_MODES */ @@ -1281,8 +1304,9 @@ static int prism2_ioctl_siwpower(struct net_device *dev, static int prism2_ioctl_giwpower(struct net_device *dev, struct iw_request_info *info, - struct iw_param *rrq, char *extra) + union iwreq_data *wrqu, char *extra) { + struct iw_param *rrq = &wrqu->power; #ifdef PRISM2_NO_STATION_MODES return -EOPNOTSUPP; #else /* PRISM2_NO_STATION_MODES */ @@ -1339,8 +1363,9 @@ static int prism2_ioctl_giwpower(struct net_device *dev, static int prism2_ioctl_siwretry(struct net_device *dev, struct iw_request_info *info, - struct iw_param *rrq, char *extra) + union iwreq_data *wrqu, char *extra) { + struct iw_param *rrq = &wrqu->retry; struct hostap_interface *iface; local_info_t *local; @@ -1400,8 +1425,9 @@ static int prism2_ioctl_siwretry(struct net_device *dev, static int prism2_ioctl_giwretry(struct net_device *dev, struct iw_request_info *info, - struct iw_param *rrq, char *extra) + union iwreq_data *wrqu, char *extra) { + struct iw_param *rrq = &wrqu->retry; struct hostap_interface *iface; local_info_t *local; __le16 shortretry, longretry, lifetime, altretry; @@ -1494,8 +1520,9 @@ static u16 prism2_txpower_dBm_to_hfa386x(int val) static int prism2_ioctl_siwtxpow(struct net_device *dev, struct iw_request_info *info, - struct iw_param *rrq, char *extra) + union iwreq_data *wrqu, char *extra) { + struct iw_param *rrq = &wrqu->txpower; struct hostap_interface *iface; local_info_t *local; #ifdef RAW_TXPOWER_SETTING @@ -1575,9 +1602,10 @@ static int prism2_ioctl_siwtxpow(struct net_device *dev, static int prism2_ioctl_giwtxpow(struct net_device *dev, struct iw_request_info *info, - struct iw_param *rrq, char *extra) + union iwreq_data *wrqu, char *extra) { #ifdef RAW_TXPOWER_SETTING + struct iw_param *rrq = &wrqu->txpower; struct hostap_interface *iface; local_info_t *local; u16 resp0; @@ -1710,8 +1738,9 @@ static inline int prism2_request_scan(struct net_device *dev) static int prism2_ioctl_siwscan(struct net_device *dev, struct iw_request_info *info, - struct iw_point *data, char *extra) + union iwreq_data *wrqu, char *extra) { + struct iw_point *data = &wrqu->data; struct hostap_interface *iface; local_info_t *local; int ret; @@ -2057,8 +2086,9 @@ static inline int prism2_ioctl_giwscan_sta(struct net_device *dev, static int prism2_ioctl_giwscan(struct net_device *dev, struct iw_request_info *info, - struct iw_point *data, char *extra) + union iwreq_data *wrqu, char *extra) { + struct iw_point *data = &wrqu->data; struct hostap_interface *iface; local_info_t *local; int res; @@ -2303,7 +2333,7 @@ static int prism2_ioctl_priv_inquire(struct net_device *dev, int *i) static int prism2_ioctl_priv_prism2_param(struct net_device *dev, struct iw_request_info *info, - void *wrqu, char *extra) + union iwreq_data *uwrq, char *extra) { struct hostap_interface *iface; local_info_t *local; @@ -2654,7 +2684,7 @@ static int prism2_ioctl_priv_prism2_param(struct net_device *dev, static int prism2_ioctl_priv_get_prism2_param(struct net_device *dev, struct iw_request_info *info, - void *wrqu, char *extra) + union iwreq_data *wrqu, char *extra) { struct hostap_interface *iface; local_info_t *local; @@ -2841,7 +2871,7 @@ static int prism2_ioctl_priv_get_prism2_param(struct net_device *dev, static int prism2_ioctl_priv_readmif(struct net_device *dev, struct iw_request_info *info, - void *wrqu, char *extra) + union iwreq_data *wrqu, char *extra) { struct hostap_interface *iface; local_info_t *local; @@ -2862,7 +2892,7 @@ static int prism2_ioctl_priv_readmif(struct net_device *dev, static int prism2_ioctl_priv_writemif(struct net_device *dev, struct iw_request_info *info, - void *wrqu, char *extra) + union iwreq_data *wrqu, char *extra) { struct hostap_interface *iface; local_info_t *local; @@ -2885,7 +2915,7 @@ static int prism2_ioctl_priv_monitor(struct net_device *dev, int *i) struct hostap_interface *iface; local_info_t *local; int ret = 0; - u32 mode; + union iwreq_data wrqu; iface = netdev_priv(dev); local = iface->local; @@ -2899,8 +2929,8 @@ static int prism2_ioctl_priv_monitor(struct net_device *dev, int *i) if (*i == 0) { /* Disable monitor mode - old mode was not saved, so go to * Master mode */ - mode = IW_MODE_MASTER; - ret = prism2_ioctl_siwmode(dev, NULL, &mode, NULL); + wrqu.mode = IW_MODE_MASTER; + ret = prism2_ioctl_siwmode(dev, NULL, &wrqu, NULL); } else if (*i == 1) { /* netlink socket mode is not supported anymore since it did * not separate different devices from each other and was not @@ -2916,8 +2946,8 @@ static int prism2_ioctl_priv_monitor(struct net_device *dev, int *i) local->monitor_type = PRISM2_MONITOR_PRISM; break; } - mode = IW_MODE_MONITOR; - ret = prism2_ioctl_siwmode(dev, NULL, &mode, NULL); + wrqu.mode = IW_MODE_MONITOR; + ret = prism2_ioctl_siwmode(dev, NULL, &wrqu, NULL); hostap_monitor_mode_enable(local); } else ret = -EINVAL; @@ -3079,8 +3109,9 @@ static int prism2_set_genericelement(struct net_device *dev, u8 *elem, static int prism2_ioctl_siwauth(struct net_device *dev, struct iw_request_info *info, - struct iw_param *data, char *extra) + union iwreq_data *wrqu, char *extra) { + struct iw_param *data = &wrqu->param; struct hostap_interface *iface = netdev_priv(dev); local_info_t *local = iface->local; @@ -3145,8 +3176,9 @@ static int prism2_ioctl_siwauth(struct net_device *dev, static int prism2_ioctl_giwauth(struct net_device *dev, struct iw_request_info *info, - struct iw_param *data, char *extra) + union iwreq_data *wrqu, char *extra) { + struct iw_param *data = &wrqu->param; struct hostap_interface *iface = netdev_priv(dev); local_info_t *local = iface->local; @@ -3184,8 +3216,9 @@ static int prism2_ioctl_giwauth(struct net_device *dev, static int prism2_ioctl_siwencodeext(struct net_device *dev, struct iw_request_info *info, - struct iw_point *erq, char *extra) + union iwreq_data *wrqu, char *extra) { + struct iw_point *erq = &wrqu->encoding; struct hostap_interface *iface = netdev_priv(dev); local_info_t *local = iface->local; struct iw_encode_ext *ext = (struct iw_encode_ext *) extra; @@ -3358,8 +3391,9 @@ static int prism2_ioctl_siwencodeext(struct net_device *dev, static int prism2_ioctl_giwencodeext(struct net_device *dev, struct iw_request_info *info, - struct iw_point *erq, char *extra) + union iwreq_data *wrqu, char *extra) { + struct iw_point *erq = &wrqu->encoding; struct hostap_interface *iface = netdev_priv(dev); local_info_t *local = iface->local; struct lib80211_crypt_data **crypt; @@ -3666,16 +3700,18 @@ static int prism2_ioctl_set_assoc_ap_addr(local_info_t *local, static int prism2_ioctl_siwgenie(struct net_device *dev, struct iw_request_info *info, - struct iw_point *data, char *extra) + union iwreq_data *wrqu, char *extra) { + struct iw_point *data = &wrqu->data; return prism2_set_genericelement(dev, extra, data->length); } static int prism2_ioctl_giwgenie(struct net_device *dev, struct iw_request_info *info, - struct iw_point *data, char *extra) + union iwreq_data *wrqu, char *extra) { + struct iw_point *data = &wrqu->data; struct hostap_interface *iface = netdev_priv(dev); local_info_t *local = iface->local; int len = local->generic_elem_len - 2; @@ -3713,7 +3749,7 @@ static int prism2_ioctl_set_generic_element(local_info_t *local, static int prism2_ioctl_siwmlme(struct net_device *dev, struct iw_request_info *info, - struct iw_point *data, char *extra) + union iwreq_data *wrqu, char *extra) { struct hostap_interface *iface = netdev_priv(dev); local_info_t *local = iface->local; @@ -3864,70 +3900,56 @@ const struct ethtool_ops prism2_ethtool_ops = { static const iw_handler prism2_handler[] = { - (iw_handler) NULL, /* SIOCSIWCOMMIT */ - (iw_handler) prism2_get_name, /* SIOCGIWNAME */ - (iw_handler) NULL, /* SIOCSIWNWID */ - (iw_handler) NULL, /* SIOCGIWNWID */ - (iw_handler) prism2_ioctl_siwfreq, /* SIOCSIWFREQ */ - (iw_handler) prism2_ioctl_giwfreq, /* SIOCGIWFREQ */ - (iw_handler) prism2_ioctl_siwmode, /* SIOCSIWMODE */ - (iw_handler) prism2_ioctl_giwmode, /* SIOCGIWMODE */ - (iw_handler) prism2_ioctl_siwsens, /* SIOCSIWSENS */ - (iw_handler) prism2_ioctl_giwsens, /* SIOCGIWSENS */ - (iw_handler) NULL /* not used */, /* SIOCSIWRANGE */ - (iw_handler) prism2_ioctl_giwrange, /* SIOCGIWRANGE */ - (iw_handler) NULL /* not used */, /* SIOCSIWPRIV */ - (iw_handler) NULL /* kernel code */, /* SIOCGIWPRIV */ - (iw_handler) NULL /* not used */, /* SIOCSIWSTATS */ - (iw_handler) NULL /* kernel code */, /* SIOCGIWSTATS */ - iw_handler_set_spy, /* SIOCSIWSPY */ - iw_handler_get_spy, /* SIOCGIWSPY */ - iw_handler_set_thrspy, /* SIOCSIWTHRSPY */ - iw_handler_get_thrspy, /* SIOCGIWTHRSPY */ - (iw_handler) prism2_ioctl_siwap, /* SIOCSIWAP */ - (iw_handler) prism2_ioctl_giwap, /* SIOCGIWAP */ - (iw_handler) prism2_ioctl_siwmlme, /* SIOCSIWMLME */ - (iw_handler) prism2_ioctl_giwaplist, /* SIOCGIWAPLIST */ - (iw_handler) prism2_ioctl_siwscan, /* SIOCSIWSCAN */ - (iw_handler) prism2_ioctl_giwscan, /* SIOCGIWSCAN */ - (iw_handler) prism2_ioctl_siwessid, /* SIOCSIWESSID */ - (iw_handler) prism2_ioctl_giwessid, /* SIOCGIWESSID */ - (iw_handler) prism2_ioctl_siwnickn, /* SIOCSIWNICKN */ - (iw_handler) prism2_ioctl_giwnickn, /* SIOCGIWNICKN */ - (iw_handler) NULL, /* -- hole -- */ - (iw_handler) NULL, /* -- hole -- */ - (iw_handler) prism2_ioctl_siwrate, /* SIOCSIWRATE */ - (iw_handler) prism2_ioctl_giwrate, /* SIOCGIWRATE */ - (iw_handler) prism2_ioctl_siwrts, /* SIOCSIWRTS */ - (iw_handler) prism2_ioctl_giwrts, /* SIOCGIWRTS */ - (iw_handler) prism2_ioctl_siwfrag, /* SIOCSIWFRAG */ - (iw_handler) prism2_ioctl_giwfrag, /* SIOCGIWFRAG */ - (iw_handler) prism2_ioctl_siwtxpow, /* SIOCSIWTXPOW */ - (iw_handler) prism2_ioctl_giwtxpow, /* SIOCGIWTXPOW */ - (iw_handler) prism2_ioctl_siwretry, /* SIOCSIWRETRY */ - (iw_handler) prism2_ioctl_giwretry, /* SIOCGIWRETRY */ - (iw_handler) prism2_ioctl_siwencode, /* SIOCSIWENCODE */ - (iw_handler) prism2_ioctl_giwencode, /* SIOCGIWENCODE */ - (iw_handler) prism2_ioctl_siwpower, /* SIOCSIWPOWER */ - (iw_handler) prism2_ioctl_giwpower, /* SIOCGIWPOWER */ - (iw_handler) NULL, /* -- hole -- */ - (iw_handler) NULL, /* -- hole -- */ - (iw_handler) prism2_ioctl_siwgenie, /* SIOCSIWGENIE */ - (iw_handler) prism2_ioctl_giwgenie, /* SIOCGIWGENIE */ - (iw_handler) prism2_ioctl_siwauth, /* SIOCSIWAUTH */ - (iw_handler) prism2_ioctl_giwauth, /* SIOCGIWAUTH */ - (iw_handler) prism2_ioctl_siwencodeext, /* SIOCSIWENCODEEXT */ - (iw_handler) prism2_ioctl_giwencodeext, /* SIOCGIWENCODEEXT */ - (iw_handler) NULL, /* SIOCSIWPMKSA */ - (iw_handler) NULL, /* -- hole -- */ + IW_HANDLER(SIOCGIWNAME, prism2_get_name), + IW_HANDLER(SIOCSIWFREQ, prism2_ioctl_siwfreq), + IW_HANDLER(SIOCGIWFREQ, prism2_ioctl_giwfreq), + IW_HANDLER(SIOCSIWMODE, prism2_ioctl_siwmode), + IW_HANDLER(SIOCGIWMODE, prism2_ioctl_giwmode), + IW_HANDLER(SIOCSIWSENS, prism2_ioctl_siwsens), + IW_HANDLER(SIOCGIWSENS, prism2_ioctl_giwsens), + IW_HANDLER(SIOCGIWRANGE, prism2_ioctl_giwrange), + IW_HANDLER(SIOCSIWSPY, iw_handler_set_spy), + IW_HANDLER(SIOCGIWSPY, iw_handler_get_spy), + IW_HANDLER(SIOCSIWTHRSPY, iw_handler_set_thrspy), + IW_HANDLER(SIOCGIWTHRSPY, iw_handler_get_thrspy), + IW_HANDLER(SIOCSIWAP, prism2_ioctl_siwap), + IW_HANDLER(SIOCGIWAP, prism2_ioctl_giwap), + IW_HANDLER(SIOCSIWMLME, prism2_ioctl_siwmlme), + IW_HANDLER(SIOCGIWAPLIST, prism2_ioctl_giwaplist), + IW_HANDLER(SIOCSIWSCAN, prism2_ioctl_siwscan), + IW_HANDLER(SIOCGIWSCAN, prism2_ioctl_giwscan), + IW_HANDLER(SIOCSIWESSID, prism2_ioctl_siwessid), + IW_HANDLER(SIOCGIWESSID, prism2_ioctl_giwessid), + IW_HANDLER(SIOCSIWNICKN, prism2_ioctl_siwnickn), + IW_HANDLER(SIOCGIWNICKN, prism2_ioctl_giwnickn), + IW_HANDLER(SIOCSIWRATE, prism2_ioctl_siwrate), + IW_HANDLER(SIOCGIWRATE, prism2_ioctl_giwrate), + IW_HANDLER(SIOCSIWRTS, prism2_ioctl_siwrts), + IW_HANDLER(SIOCGIWRTS, prism2_ioctl_giwrts), + IW_HANDLER(SIOCSIWFRAG, prism2_ioctl_siwfrag), + IW_HANDLER(SIOCGIWFRAG, prism2_ioctl_giwfrag), + IW_HANDLER(SIOCSIWTXPOW, prism2_ioctl_siwtxpow), + IW_HANDLER(SIOCGIWTXPOW, prism2_ioctl_giwtxpow), + IW_HANDLER(SIOCSIWRETRY, prism2_ioctl_siwretry), + IW_HANDLER(SIOCGIWRETRY, prism2_ioctl_giwretry), + IW_HANDLER(SIOCSIWENCODE, prism2_ioctl_siwencode), + IW_HANDLER(SIOCGIWENCODE, prism2_ioctl_giwencode), + IW_HANDLER(SIOCSIWPOWER, prism2_ioctl_siwpower), + IW_HANDLER(SIOCGIWPOWER, prism2_ioctl_giwpower), + IW_HANDLER(SIOCSIWGENIE, prism2_ioctl_siwgenie), + IW_HANDLER(SIOCGIWGENIE, prism2_ioctl_giwgenie), + IW_HANDLER(SIOCSIWAUTH, prism2_ioctl_siwauth), + IW_HANDLER(SIOCGIWAUTH, prism2_ioctl_giwauth), + IW_HANDLER(SIOCSIWENCODEEXT, prism2_ioctl_siwencodeext), + IW_HANDLER(SIOCGIWENCODEEXT, prism2_ioctl_giwencodeext), }; static const iw_handler prism2_private_handler[] = -{ /* SIOCIWFIRSTPRIV + */ - (iw_handler) prism2_ioctl_priv_prism2_param, /* 0 */ - (iw_handler) prism2_ioctl_priv_get_prism2_param, /* 1 */ - (iw_handler) prism2_ioctl_priv_writemif, /* 2 */ - (iw_handler) prism2_ioctl_priv_readmif, /* 3 */ +{ /* SIOCIWFIRSTPRIV + */ + prism2_ioctl_priv_prism2_param, /* 0 */ + prism2_ioctl_priv_get_prism2_param, /* 1 */ + prism2_ioctl_priv_writemif, /* 2 */ + prism2_ioctl_priv_readmif, /* 3 */ }; const struct iw_handler_def hostap_iw_handler_def = @@ -3935,8 +3957,8 @@ const struct iw_handler_def hostap_iw_handler_def = .num_standard = ARRAY_SIZE(prism2_handler), .num_private = ARRAY_SIZE(prism2_private_handler), .num_private_args = ARRAY_SIZE(prism2_priv), - .standard = (iw_handler *) prism2_handler, - .private = (iw_handler *) prism2_private_handler, + .standard = prism2_handler, + .private = prism2_private_handler, .private_args = (struct iw_priv_args *) prism2_priv, .get_wireless_stats = hostap_get_wireless_stats, }; From ff7efc66b7ea7bd2e575b3d56e24628b2f418315 Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Tue, 8 Nov 2022 14:26:47 -0600 Subject: [PATCH 0964/2002] wifi: zd1201: Avoid clashing function prototypes When built with Control Flow Integrity, function prototypes between caller and function declaration must match. These mismatches are visible at compile time with the new -Wcast-function-type-strict in Clang[1]. Fix a total of 30 warnings like these: ../drivers/net/wireless/zydas/zd1201.c:1560:2: warning: cast from 'int (*)(struct net_device *, struct iw_request_info *, struct iw_freq *, char *)' to 'iw_handler' (aka 'int (*)(struct net_device *, struct iw_request_info *, union iwreq_data *, char *)') converts to incompatible function type [-Wcast-function-type-strict] (iw_handler) zd1201_set_freq, /* SIOCSIWFREQ */ ^~~~~~~~~~~~~~~~~~~~~~~~~~~~ The zd1201 Wireless Extension handler callbacks (iw_handler) use a union for the data argument. Actually use the union and perform explicit member selection in the function body instead of having a function prototype mismatch.There are no resulting binary differences before/after changes. These changes were made partly manually and partly with the help of Coccinelle. Link: https://github.com/KSPP/linux/issues/233 Link: https://reviews.llvm.org/D134831 [1] Reviewed-by: Kees Cook Signed-off-by: Gustavo A. R. Silva Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/5b7fbb1a22d5bfaa872263ca20297de9b431d1ec.1667934775.git.gustavoars@kernel.org --- drivers/net/wireless/zydas/zd1201.c | 174 ++++++++++++++-------------- 1 file changed, 89 insertions(+), 85 deletions(-) diff --git a/drivers/net/wireless/zydas/zd1201.c b/drivers/net/wireless/zydas/zd1201.c index 82bc0d44212e..a85fe7e4c6d4 100644 --- a/drivers/net/wireless/zydas/zd1201.c +++ b/drivers/net/wireless/zydas/zd1201.c @@ -886,7 +886,7 @@ static void zd1201_set_multicast(struct net_device *dev) } static int zd1201_config_commit(struct net_device *dev, - struct iw_request_info *info, struct iw_point *data, char *essid) + struct iw_request_info *info, union iwreq_data *wrqu, char *essid) { struct zd1201 *zd = netdev_priv(dev); @@ -894,15 +894,16 @@ static int zd1201_config_commit(struct net_device *dev, } static int zd1201_get_name(struct net_device *dev, - struct iw_request_info *info, char *name, char *extra) + struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { - strcpy(name, "IEEE 802.11b"); + strcpy(wrqu->name, "IEEE 802.11b"); return 0; } static int zd1201_set_freq(struct net_device *dev, - struct iw_request_info *info, struct iw_freq *freq, char *extra) + struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { + struct iw_freq *freq = &wrqu->freq; struct zd1201 *zd = netdev_priv(dev); short channel = 0; int err; @@ -922,8 +923,9 @@ static int zd1201_set_freq(struct net_device *dev, } static int zd1201_get_freq(struct net_device *dev, - struct iw_request_info *info, struct iw_freq *freq, char *extra) + struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { + struct iw_freq *freq = &wrqu->freq; struct zd1201 *zd = netdev_priv(dev); short channel; int err; @@ -938,8 +940,9 @@ static int zd1201_get_freq(struct net_device *dev, } static int zd1201_set_mode(struct net_device *dev, - struct iw_request_info *info, __u32 *mode, char *extra) + struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { + __u32 *mode = &wrqu->mode; struct zd1201 *zd = netdev_priv(dev); short porttype, monitor = 0; unsigned char buffer[IW_ESSID_MAX_SIZE+2]; @@ -1001,8 +1004,9 @@ static int zd1201_set_mode(struct net_device *dev, } static int zd1201_get_mode(struct net_device *dev, - struct iw_request_info *info, __u32 *mode, char *extra) + struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { + __u32 *mode = &wrqu->mode; struct zd1201 *zd = netdev_priv(dev); short porttype; int err; @@ -1038,8 +1042,9 @@ static int zd1201_get_mode(struct net_device *dev, } static int zd1201_get_range(struct net_device *dev, - struct iw_request_info *info, struct iw_point *wrq, char *extra) + struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { + struct iw_point *wrq = &wrqu->data; struct iw_range *range = (struct iw_range *)extra; wrq->length = sizeof(struct iw_range); @@ -1077,8 +1082,9 @@ static int zd1201_get_range(struct net_device *dev, * the stats after asking the bssid. */ static int zd1201_get_wap(struct net_device *dev, - struct iw_request_info *info, struct sockaddr *ap_addr, char *extra) + struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { + struct sockaddr *ap_addr = &wrqu->ap_addr; struct zd1201 *zd = netdev_priv(dev); unsigned char buffer[6]; @@ -1098,15 +1104,16 @@ static int zd1201_get_wap(struct net_device *dev, } static int zd1201_set_scan(struct net_device *dev, - struct iw_request_info *info, struct iw_point *srq, char *extra) + struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { /* We do everything in get_scan */ return 0; } static int zd1201_get_scan(struct net_device *dev, - struct iw_request_info *info, struct iw_point *srq, char *extra) + struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { + struct iw_point *srq = &wrqu->data; struct zd1201 *zd = netdev_priv(dev); int err, i, j, enabled_save; struct iw_event iwe; @@ -1197,8 +1204,9 @@ static int zd1201_get_scan(struct net_device *dev, } static int zd1201_set_essid(struct net_device *dev, - struct iw_request_info *info, struct iw_point *data, char *essid) + struct iw_request_info *info, union iwreq_data *wrqu, char *essid) { + struct iw_point *data = &wrqu->data; struct zd1201 *zd = netdev_priv(dev); if (data->length > IW_ESSID_MAX_SIZE) @@ -1212,8 +1220,9 @@ static int zd1201_set_essid(struct net_device *dev, } static int zd1201_get_essid(struct net_device *dev, - struct iw_request_info *info, struct iw_point *data, char *essid) + struct iw_request_info *info, union iwreq_data *wrqu, char *essid) { + struct iw_point *data = &wrqu->data; struct zd1201 *zd = netdev_priv(dev); memcpy(essid, zd->essid, zd->essidlen); @@ -1224,8 +1233,9 @@ static int zd1201_get_essid(struct net_device *dev, } static int zd1201_get_nick(struct net_device *dev, struct iw_request_info *info, - struct iw_point *data, char *nick) + union iwreq_data *wrqu, char *nick) { + struct iw_point *data = &wrqu->data; strcpy(nick, "zd1201"); data->flags = 1; data->length = strlen(nick); @@ -1233,8 +1243,9 @@ static int zd1201_get_nick(struct net_device *dev, struct iw_request_info *info, } static int zd1201_set_rate(struct net_device *dev, - struct iw_request_info *info, struct iw_param *rrq, char *extra) + struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { + struct iw_param *rrq = &wrqu->bitrate; struct zd1201 *zd = netdev_priv(dev); short rate; int err; @@ -1266,8 +1277,9 @@ static int zd1201_set_rate(struct net_device *dev, } static int zd1201_get_rate(struct net_device *dev, - struct iw_request_info *info, struct iw_param *rrq, char *extra) + struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { + struct iw_param *rrq = &wrqu->bitrate; struct zd1201 *zd = netdev_priv(dev); short rate; int err; @@ -1299,8 +1311,9 @@ static int zd1201_get_rate(struct net_device *dev, } static int zd1201_set_rts(struct net_device *dev, struct iw_request_info *info, - struct iw_param *rts, char *extra) + union iwreq_data *wrqu, char *extra) { + struct iw_param *rts = &wrqu->rts; struct zd1201 *zd = netdev_priv(dev); int err; short val = rts->value; @@ -1319,8 +1332,9 @@ static int zd1201_set_rts(struct net_device *dev, struct iw_request_info *info, } static int zd1201_get_rts(struct net_device *dev, struct iw_request_info *info, - struct iw_param *rts, char *extra) + union iwreq_data *wrqu, char *extra) { + struct iw_param *rts = &wrqu->rts; struct zd1201 *zd = netdev_priv(dev); short rtst; int err; @@ -1336,8 +1350,9 @@ static int zd1201_get_rts(struct net_device *dev, struct iw_request_info *info, } static int zd1201_set_frag(struct net_device *dev, struct iw_request_info *info, - struct iw_param *frag, char *extra) + union iwreq_data *wrqu, char *extra) { + struct iw_param *frag = &wrqu->frag; struct zd1201 *zd = netdev_priv(dev); int err; short val = frag->value; @@ -1357,8 +1372,9 @@ static int zd1201_set_frag(struct net_device *dev, struct iw_request_info *info, } static int zd1201_get_frag(struct net_device *dev, struct iw_request_info *info, - struct iw_param *frag, char *extra) + union iwreq_data *wrqu, char *extra) { + struct iw_param *frag = &wrqu->frag; struct zd1201 *zd = netdev_priv(dev); short fragt; int err; @@ -1374,20 +1390,21 @@ static int zd1201_get_frag(struct net_device *dev, struct iw_request_info *info, } static int zd1201_set_retry(struct net_device *dev, - struct iw_request_info *info, struct iw_param *rrq, char *extra) + struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { return 0; } static int zd1201_get_retry(struct net_device *dev, - struct iw_request_info *info, struct iw_param *rrq, char *extra) + struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { return 0; } static int zd1201_set_encode(struct net_device *dev, - struct iw_request_info *info, struct iw_point *erq, char *key) + struct iw_request_info *info, union iwreq_data *wrqu, char *key) { + struct iw_point *erq = &wrqu->encoding; struct zd1201 *zd = netdev_priv(dev); short i; int err, rid; @@ -1443,8 +1460,9 @@ static int zd1201_set_encode(struct net_device *dev, } static int zd1201_get_encode(struct net_device *dev, - struct iw_request_info *info, struct iw_point *erq, char *key) + struct iw_request_info *info, union iwreq_data *wrqu, char *key) { + struct iw_point *erq = &wrqu->encoding; struct zd1201 *zd = netdev_priv(dev); short i; int err; @@ -1476,8 +1494,9 @@ static int zd1201_get_encode(struct net_device *dev, } static int zd1201_set_power(struct net_device *dev, - struct iw_request_info *info, struct iw_param *vwrq, char *extra) + struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { + struct iw_param *vwrq = &wrqu->power; struct zd1201 *zd = netdev_priv(dev); short enabled, duration, level; int err; @@ -1515,8 +1534,9 @@ out: } static int zd1201_get_power(struct net_device *dev, - struct iw_request_info *info, struct iw_param *vwrq, char *extra) + struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { + struct iw_param *vwrq = &wrqu->power; struct zd1201 *zd = netdev_priv(dev); short enabled, level, duration; int err; @@ -1553,57 +1573,37 @@ static int zd1201_get_power(struct net_device *dev, static const iw_handler zd1201_iw_handler[] = { - (iw_handler) zd1201_config_commit, /* SIOCSIWCOMMIT */ - (iw_handler) zd1201_get_name, /* SIOCGIWNAME */ - (iw_handler) NULL, /* SIOCSIWNWID */ - (iw_handler) NULL, /* SIOCGIWNWID */ - (iw_handler) zd1201_set_freq, /* SIOCSIWFREQ */ - (iw_handler) zd1201_get_freq, /* SIOCGIWFREQ */ - (iw_handler) zd1201_set_mode, /* SIOCSIWMODE */ - (iw_handler) zd1201_get_mode, /* SIOCGIWMODE */ - (iw_handler) NULL, /* SIOCSIWSENS */ - (iw_handler) NULL, /* SIOCGIWSENS */ - (iw_handler) NULL, /* SIOCSIWRANGE */ - (iw_handler) zd1201_get_range, /* SIOCGIWRANGE */ - (iw_handler) NULL, /* SIOCSIWPRIV */ - (iw_handler) NULL, /* SIOCGIWPRIV */ - (iw_handler) NULL, /* SIOCSIWSTATS */ - (iw_handler) NULL, /* SIOCGIWSTATS */ - (iw_handler) NULL, /* SIOCSIWSPY */ - (iw_handler) NULL, /* SIOCGIWSPY */ - (iw_handler) NULL, /* -- hole -- */ - (iw_handler) NULL, /* -- hole -- */ - (iw_handler) NULL/*zd1201_set_wap*/, /* SIOCSIWAP */ - (iw_handler) zd1201_get_wap, /* SIOCGIWAP */ - (iw_handler) NULL, /* -- hole -- */ - (iw_handler) NULL, /* SIOCGIWAPLIST */ - (iw_handler) zd1201_set_scan, /* SIOCSIWSCAN */ - (iw_handler) zd1201_get_scan, /* SIOCGIWSCAN */ - (iw_handler) zd1201_set_essid, /* SIOCSIWESSID */ - (iw_handler) zd1201_get_essid, /* SIOCGIWESSID */ - (iw_handler) NULL, /* SIOCSIWNICKN */ - (iw_handler) zd1201_get_nick, /* SIOCGIWNICKN */ - (iw_handler) NULL, /* -- hole -- */ - (iw_handler) NULL, /* -- hole -- */ - (iw_handler) zd1201_set_rate, /* SIOCSIWRATE */ - (iw_handler) zd1201_get_rate, /* SIOCGIWRATE */ - (iw_handler) zd1201_set_rts, /* SIOCSIWRTS */ - (iw_handler) zd1201_get_rts, /* SIOCGIWRTS */ - (iw_handler) zd1201_set_frag, /* SIOCSIWFRAG */ - (iw_handler) zd1201_get_frag, /* SIOCGIWFRAG */ - (iw_handler) NULL, /* SIOCSIWTXPOW */ - (iw_handler) NULL, /* SIOCGIWTXPOW */ - (iw_handler) zd1201_set_retry, /* SIOCSIWRETRY */ - (iw_handler) zd1201_get_retry, /* SIOCGIWRETRY */ - (iw_handler) zd1201_set_encode, /* SIOCSIWENCODE */ - (iw_handler) zd1201_get_encode, /* SIOCGIWENCODE */ - (iw_handler) zd1201_set_power, /* SIOCSIWPOWER */ - (iw_handler) zd1201_get_power, /* SIOCGIWPOWER */ + IW_HANDLER(SIOCSIWCOMMIT, zd1201_config_commit), + IW_HANDLER(SIOCGIWNAME, zd1201_get_name), + IW_HANDLER(SIOCSIWFREQ, zd1201_set_freq), + IW_HANDLER(SIOCGIWFREQ, zd1201_get_freq), + IW_HANDLER(SIOCSIWMODE, zd1201_set_mode), + IW_HANDLER(SIOCGIWMODE, zd1201_get_mode), + IW_HANDLER(SIOCGIWRANGE, zd1201_get_range), + IW_HANDLER(SIOCGIWAP, zd1201_get_wap), + IW_HANDLER(SIOCSIWSCAN, zd1201_set_scan), + IW_HANDLER(SIOCGIWSCAN, zd1201_get_scan), + IW_HANDLER(SIOCSIWESSID, zd1201_set_essid), + IW_HANDLER(SIOCGIWESSID, zd1201_get_essid), + IW_HANDLER(SIOCGIWNICKN, zd1201_get_nick), + IW_HANDLER(SIOCSIWRATE, zd1201_set_rate), + IW_HANDLER(SIOCGIWRATE, zd1201_get_rate), + IW_HANDLER(SIOCSIWRTS, zd1201_set_rts), + IW_HANDLER(SIOCGIWRTS, zd1201_get_rts), + IW_HANDLER(SIOCSIWFRAG, zd1201_set_frag), + IW_HANDLER(SIOCGIWFRAG, zd1201_get_frag), + IW_HANDLER(SIOCSIWRETRY, zd1201_set_retry), + IW_HANDLER(SIOCGIWRETRY, zd1201_get_retry), + IW_HANDLER(SIOCSIWENCODE, zd1201_set_encode), + IW_HANDLER(SIOCGIWENCODE, zd1201_get_encode), + IW_HANDLER(SIOCSIWPOWER, zd1201_set_power), + IW_HANDLER(SIOCGIWPOWER, zd1201_get_power), }; static int zd1201_set_hostauth(struct net_device *dev, - struct iw_request_info *info, struct iw_param *rrq, char *extra) + struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { + struct iw_param *rrq = &wrqu->param; struct zd1201 *zd = netdev_priv(dev); if (!zd->ap) @@ -1613,8 +1613,9 @@ static int zd1201_set_hostauth(struct net_device *dev, } static int zd1201_get_hostauth(struct net_device *dev, - struct iw_request_info *info, struct iw_param *rrq, char *extra) + struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { + struct iw_param *rrq = &wrqu->param; struct zd1201 *zd = netdev_priv(dev); short hostauth; int err; @@ -1632,8 +1633,9 @@ static int zd1201_get_hostauth(struct net_device *dev, } static int zd1201_auth_sta(struct net_device *dev, - struct iw_request_info *info, struct sockaddr *sta, char *extra) + struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { + struct sockaddr *sta = &wrqu->ap_addr; struct zd1201 *zd = netdev_priv(dev); unsigned char buffer[10]; @@ -1648,8 +1650,9 @@ static int zd1201_auth_sta(struct net_device *dev, } static int zd1201_set_maxassoc(struct net_device *dev, - struct iw_request_info *info, struct iw_param *rrq, char *extra) + struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { + struct iw_param *rrq = &wrqu->param; struct zd1201 *zd = netdev_priv(dev); if (!zd->ap) @@ -1659,8 +1662,9 @@ static int zd1201_set_maxassoc(struct net_device *dev, } static int zd1201_get_maxassoc(struct net_device *dev, - struct iw_request_info *info, struct iw_param *rrq, char *extra) + struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { + struct iw_param *rrq = &wrqu->param; struct zd1201 *zd = netdev_priv(dev); short maxassoc; int err; @@ -1678,12 +1682,12 @@ static int zd1201_get_maxassoc(struct net_device *dev, } static const iw_handler zd1201_private_handler[] = { - (iw_handler) zd1201_set_hostauth, /* ZD1201SIWHOSTAUTH */ - (iw_handler) zd1201_get_hostauth, /* ZD1201GIWHOSTAUTH */ - (iw_handler) zd1201_auth_sta, /* ZD1201SIWAUTHSTA */ - (iw_handler) NULL, /* nothing to get */ - (iw_handler) zd1201_set_maxassoc, /* ZD1201SIMAXASSOC */ - (iw_handler) zd1201_get_maxassoc, /* ZD1201GIMAXASSOC */ + zd1201_set_hostauth, /* ZD1201SIWHOSTAUTH */ + zd1201_get_hostauth, /* ZD1201GIWHOSTAUTH */ + zd1201_auth_sta, /* ZD1201SIWAUTHSTA */ + NULL, /* nothing to get */ + zd1201_set_maxassoc, /* ZD1201SIMAXASSOC */ + zd1201_get_maxassoc, /* ZD1201GIMAXASSOC */ }; static const struct iw_priv_args zd1201_private_args[] = { @@ -1703,8 +1707,8 @@ static const struct iw_handler_def zd1201_iw_handlers = { .num_standard = ARRAY_SIZE(zd1201_iw_handler), .num_private = ARRAY_SIZE(zd1201_private_handler), .num_private_args = ARRAY_SIZE(zd1201_private_args), - .standard = (iw_handler *)zd1201_iw_handler, - .private = (iw_handler *)zd1201_private_handler, + .standard = zd1201_iw_handler, + .private = zd1201_private_handler, .private_args = (struct iw_priv_args *) zd1201_private_args, .get_wireless_stats = zd1201_get_wireless_stats, }; From 89e706459848a7cc80f222a1999c0bfcdf63289f Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Tue, 8 Nov 2022 14:27:49 -0600 Subject: [PATCH 0965/2002] wifi: airo: Avoid clashing function prototypes When built with Control Flow Integrity, function prototypes between caller and function declaration must match. These mismatches are visible at compile time with the new -Wcast-function-type-strict in Clang[1]. Fix a total of 32 warnings like these: ../drivers/net/wireless/cisco/airo.c:7570:2: warning: cast from 'int (*)(struct net_device *, struct iw_request_info *, void *, char *)' to 'iw_handler' (aka 'int (*)(struct net_device *, struct iw_request_info *, union iwreq_data *, char *)') converts to incompatible function type [-Wcast-function-type-strict] (iw_handler) airo_config_commit, /* SIOCSIWCOMMIT */ ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The airo Wireless Extension handler callbacks (iw_handler) use a union for the data argument. Actually use the union and perform explicit member selection in the function body instead of having a function prototype mismatch. There are no resulting binary differences before/after changes. These changes were made partly manually and partly with the help of Coccinelle. Link: https://github.com/KSPP/linux/issues/236 Link: https://reviews.llvm.org/D134831 [1] Reviewed-by: Kees Cook Signed-off-by: Gustavo A. R. Silva Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/820abf91d12809904696ddb8925ec5e1e0da3e4c.1667934775.git.gustavoars@kernel.org --- drivers/net/wireless/cisco/airo.c | 204 ++++++++++++++++-------------- 1 file changed, 108 insertions(+), 96 deletions(-) diff --git a/drivers/net/wireless/cisco/airo.c b/drivers/net/wireless/cisco/airo.c index 10daef81c355..cfa8d3f53482 100644 --- a/drivers/net/wireless/cisco/airo.c +++ b/drivers/net/wireless/cisco/airo.c @@ -4807,7 +4807,8 @@ static int get_dec_u16(char *buffer, int *start, int limit) } static int airo_config_commit(struct net_device *dev, - struct iw_request_info *info, void *zwrq, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra); static inline int sniffing_mode(struct airo_info *ai) @@ -5804,10 +5805,10 @@ static int airo_get_quality (StatusRid *status_rid, CapabilityRid *cap_rid) */ static int airo_get_name(struct net_device *dev, struct iw_request_info *info, - char *cwrq, + union iwreq_data *cwrq, char *extra) { - strcpy(cwrq, "IEEE 802.11-DS"); + strcpy(cwrq->name, "IEEE 802.11-DS"); return 0; } @@ -5817,9 +5818,10 @@ static int airo_get_name(struct net_device *dev, */ static int airo_set_freq(struct net_device *dev, struct iw_request_info *info, - struct iw_freq *fwrq, + union iwreq_data *wrqu, char *extra) { + struct iw_freq *fwrq = &wrqu->freq; struct airo_info *local = dev->ml_priv; int rc = -EINPROGRESS; /* Call commit handler */ @@ -5858,9 +5860,10 @@ static int airo_set_freq(struct net_device *dev, */ static int airo_get_freq(struct net_device *dev, struct iw_request_info *info, - struct iw_freq *fwrq, + union iwreq_data *wrqu, char *extra) { + struct iw_freq *fwrq = &wrqu->freq; struct airo_info *local = dev->ml_priv; StatusRid status_rid; /* Card status info */ int ch; @@ -5890,9 +5893,10 @@ static int airo_get_freq(struct net_device *dev, */ static int airo_set_essid(struct net_device *dev, struct iw_request_info *info, - struct iw_point *dwrq, + union iwreq_data *wrqu, char *extra) { + struct iw_point *dwrq = &wrqu->essid; struct airo_info *local = dev->ml_priv; SsidRid SSID_rid; /* SSIDs */ @@ -5935,9 +5939,10 @@ static int airo_set_essid(struct net_device *dev, */ static int airo_get_essid(struct net_device *dev, struct iw_request_info *info, - struct iw_point *dwrq, + union iwreq_data *wrqu, char *extra) { + struct iw_point *dwrq = &wrqu->essid; struct airo_info *local = dev->ml_priv; StatusRid status_rid; /* Card status info */ @@ -5963,9 +5968,10 @@ static int airo_get_essid(struct net_device *dev, */ static int airo_set_wap(struct net_device *dev, struct iw_request_info *info, - struct sockaddr *awrq, + union iwreq_data *wrqu, char *extra) { + struct sockaddr *awrq = &wrqu->ap_addr; struct airo_info *local = dev->ml_priv; Cmd cmd; Resp rsp; @@ -5998,9 +6004,10 @@ static int airo_set_wap(struct net_device *dev, */ static int airo_get_wap(struct net_device *dev, struct iw_request_info *info, - struct sockaddr *awrq, + union iwreq_data *wrqu, char *extra) { + struct sockaddr *awrq = &wrqu->ap_addr; struct airo_info *local = dev->ml_priv; StatusRid status_rid; /* Card status info */ @@ -6019,9 +6026,10 @@ static int airo_get_wap(struct net_device *dev, */ static int airo_set_nick(struct net_device *dev, struct iw_request_info *info, - struct iw_point *dwrq, + union iwreq_data *wrqu, char *extra) { + struct iw_point *dwrq = &wrqu->data; struct airo_info *local = dev->ml_priv; /* Check the size of the string */ @@ -6042,9 +6050,10 @@ static int airo_set_nick(struct net_device *dev, */ static int airo_get_nick(struct net_device *dev, struct iw_request_info *info, - struct iw_point *dwrq, + union iwreq_data *wrqu, char *extra) { + struct iw_point *dwrq = &wrqu->data; struct airo_info *local = dev->ml_priv; readConfigRid(local, 1); @@ -6061,9 +6070,10 @@ static int airo_get_nick(struct net_device *dev, */ static int airo_set_rate(struct net_device *dev, struct iw_request_info *info, - struct iw_param *vwrq, + union iwreq_data *wrqu, char *extra) { + struct iw_param *vwrq = &wrqu->bitrate; struct airo_info *local = dev->ml_priv; CapabilityRid cap_rid; /* Card capability info */ u8 brate = 0; @@ -6131,9 +6141,10 @@ static int airo_set_rate(struct net_device *dev, */ static int airo_get_rate(struct net_device *dev, struct iw_request_info *info, - struct iw_param *vwrq, + union iwreq_data *wrqu, char *extra) { + struct iw_param *vwrq = &wrqu->bitrate; struct airo_info *local = dev->ml_priv; StatusRid status_rid; /* Card status info */ @@ -6153,9 +6164,10 @@ static int airo_get_rate(struct net_device *dev, */ static int airo_set_rts(struct net_device *dev, struct iw_request_info *info, - struct iw_param *vwrq, + union iwreq_data *wrqu, char *extra) { + struct iw_param *vwrq = &wrqu->rts; struct airo_info *local = dev->ml_priv; int rthr = vwrq->value; @@ -6177,9 +6189,10 @@ static int airo_set_rts(struct net_device *dev, */ static int airo_get_rts(struct net_device *dev, struct iw_request_info *info, - struct iw_param *vwrq, + union iwreq_data *wrqu, char *extra) { + struct iw_param *vwrq = &wrqu->rts; struct airo_info *local = dev->ml_priv; readConfigRid(local, 1); @@ -6196,9 +6209,9 @@ static int airo_get_rts(struct net_device *dev, */ static int airo_set_frag(struct net_device *dev, struct iw_request_info *info, - struct iw_param *vwrq, - char *extra) + union iwreq_data *wrqu, char *extra) { + struct iw_param *vwrq = &wrqu->frag; struct airo_info *local = dev->ml_priv; int fthr = vwrq->value; @@ -6221,9 +6234,10 @@ static int airo_set_frag(struct net_device *dev, */ static int airo_get_frag(struct net_device *dev, struct iw_request_info *info, - struct iw_param *vwrq, + union iwreq_data *wrqu, char *extra) { + struct iw_param *vwrq = &wrqu->frag; struct airo_info *local = dev->ml_priv; readConfigRid(local, 1); @@ -6240,9 +6254,10 @@ static int airo_get_frag(struct net_device *dev, */ static int airo_set_mode(struct net_device *dev, struct iw_request_info *info, - __u32 *uwrq, + union iwreq_data *uwrq, char *extra) { + __u32 mode = uwrq->mode; struct airo_info *local = dev->ml_priv; int reset = 0; @@ -6250,7 +6265,7 @@ static int airo_set_mode(struct net_device *dev, if (sniffing_mode(local)) reset = 1; - switch(*uwrq) { + switch (mode) { case IW_MODE_ADHOC: local->config.opmode &= ~MODE_CFG_MASK; local->config.opmode |= MODE_STA_IBSS; @@ -6303,7 +6318,7 @@ static int airo_set_mode(struct net_device *dev, */ static int airo_get_mode(struct net_device *dev, struct iw_request_info *info, - __u32 *uwrq, + union iwreq_data *uwrq, char *extra) { struct airo_info *local = dev->ml_priv; @@ -6312,16 +6327,16 @@ static int airo_get_mode(struct net_device *dev, /* If not managed, assume it's ad-hoc */ switch (local->config.opmode & MODE_CFG_MASK) { case MODE_STA_ESS: - *uwrq = IW_MODE_INFRA; + uwrq->mode = IW_MODE_INFRA; break; case MODE_AP: - *uwrq = IW_MODE_MASTER; + uwrq->mode = IW_MODE_MASTER; break; case MODE_AP_RPTR: - *uwrq = IW_MODE_REPEAT; + uwrq->mode = IW_MODE_REPEAT; break; default: - *uwrq = IW_MODE_ADHOC; + uwrq->mode = IW_MODE_ADHOC; } return 0; @@ -6338,9 +6353,10 @@ static inline int valid_index(struct airo_info *ai, int index) */ static int airo_set_encode(struct net_device *dev, struct iw_request_info *info, - struct iw_point *dwrq, + union iwreq_data *wrqu, char *extra) { + struct iw_point *dwrq = &wrqu->encoding; struct airo_info *local = dev->ml_priv; int perm = (dwrq->flags & IW_ENCODE_TEMP ? 0 : 1); __le16 currentAuthType = local->config.authType; @@ -6437,9 +6453,10 @@ static int airo_set_encode(struct net_device *dev, */ static int airo_get_encode(struct net_device *dev, struct iw_request_info *info, - struct iw_point *dwrq, + union iwreq_data *wrqu, char *extra) { + struct iw_point *dwrq = &wrqu->encoding; struct airo_info *local = dev->ml_priv; int index = (dwrq->flags & IW_ENCODE_INDEX) - 1; int wep_key_len; @@ -6784,9 +6801,10 @@ static int airo_get_auth(struct net_device *dev, */ static int airo_set_txpow(struct net_device *dev, struct iw_request_info *info, - struct iw_param *vwrq, + union iwreq_data *wrqu, char *extra) { + struct iw_param *vwrq = &wrqu->txpower; struct airo_info *local = dev->ml_priv; CapabilityRid cap_rid; /* Card capability info */ int i; @@ -6821,9 +6839,10 @@ static int airo_set_txpow(struct net_device *dev, */ static int airo_get_txpow(struct net_device *dev, struct iw_request_info *info, - struct iw_param *vwrq, + union iwreq_data *wrqu, char *extra) { + struct iw_param *vwrq = &wrqu->txpower; struct airo_info *local = dev->ml_priv; readConfigRid(local, 1); @@ -6841,9 +6860,10 @@ static int airo_get_txpow(struct net_device *dev, */ static int airo_set_retry(struct net_device *dev, struct iw_request_info *info, - struct iw_param *vwrq, + union iwreq_data *wrqu, char *extra) { + struct iw_param *vwrq = &wrqu->retry; struct airo_info *local = dev->ml_priv; int rc = -EINVAL; @@ -6879,9 +6899,10 @@ static int airo_set_retry(struct net_device *dev, */ static int airo_get_retry(struct net_device *dev, struct iw_request_info *info, - struct iw_param *vwrq, + union iwreq_data *wrqu, char *extra) { + struct iw_param *vwrq = &wrqu->retry; struct airo_info *local = dev->ml_priv; vwrq->disabled = 0; /* Can't be disabled */ @@ -6910,9 +6931,10 @@ static int airo_get_retry(struct net_device *dev, */ static int airo_get_range(struct net_device *dev, struct iw_request_info *info, - struct iw_point *dwrq, + union iwreq_data *wrqu, char *extra) { + struct iw_point *dwrq = &wrqu->data; struct airo_info *local = dev->ml_priv; struct iw_range *range = (struct iw_range *) extra; CapabilityRid cap_rid; /* Card capability info */ @@ -7036,9 +7058,9 @@ static int airo_get_range(struct net_device *dev, */ static int airo_set_power(struct net_device *dev, struct iw_request_info *info, - struct iw_param *vwrq, - char *extra) + union iwreq_data *wrqu, char *extra) { + struct iw_param *vwrq = &wrqu->power; struct airo_info *local = dev->ml_priv; readConfigRid(local, 1); @@ -7094,9 +7116,10 @@ static int airo_set_power(struct net_device *dev, */ static int airo_get_power(struct net_device *dev, struct iw_request_info *info, - struct iw_param *vwrq, + union iwreq_data *wrqu, char *extra) { + struct iw_param *vwrq = &wrqu->power; struct airo_info *local = dev->ml_priv; __le16 mode; @@ -7125,9 +7148,10 @@ static int airo_get_power(struct net_device *dev, */ static int airo_set_sens(struct net_device *dev, struct iw_request_info *info, - struct iw_param *vwrq, + union iwreq_data *wrqu, char *extra) { + struct iw_param *vwrq = &wrqu->sens; struct airo_info *local = dev->ml_priv; readConfigRid(local, 1); @@ -7144,9 +7168,10 @@ static int airo_set_sens(struct net_device *dev, */ static int airo_get_sens(struct net_device *dev, struct iw_request_info *info, - struct iw_param *vwrq, + union iwreq_data *wrqu, char *extra) { + struct iw_param *vwrq = &wrqu->sens; struct airo_info *local = dev->ml_priv; readConfigRid(local, 1); @@ -7164,9 +7189,10 @@ static int airo_get_sens(struct net_device *dev, */ static int airo_get_aplist(struct net_device *dev, struct iw_request_info *info, - struct iw_point *dwrq, + union iwreq_data *wrqu, char *extra) { + struct iw_point *dwrq = &wrqu->data; struct airo_info *local = dev->ml_priv; struct sockaddr *address = (struct sockaddr *) extra; struct iw_quality *qual; @@ -7242,7 +7268,7 @@ static int airo_get_aplist(struct net_device *dev, */ static int airo_set_scan(struct net_device *dev, struct iw_request_info *info, - struct iw_point *dwrq, + union iwreq_data *wrqu, char *extra) { struct airo_info *ai = dev->ml_priv; @@ -7473,9 +7499,10 @@ static inline char *airo_translate_scan(struct net_device *dev, */ static int airo_get_scan(struct net_device *dev, struct iw_request_info *info, - struct iw_point *dwrq, + union iwreq_data *wrqu, char *extra) { + struct iw_point *dwrq = &wrqu->data; struct airo_info *ai = dev->ml_priv; BSSListElement *net; int err = 0; @@ -7517,7 +7544,7 @@ out: */ static int airo_config_commit(struct net_device *dev, struct iw_request_info *info, /* NULL */ - void *zwrq, /* NULL */ + union iwreq_data *wrqu, /* NULL */ char *extra) /* NULL */ { struct airo_info *local = dev->ml_priv; @@ -7567,61 +7594,46 @@ static const struct iw_priv_args airo_private_args[] = { static const iw_handler airo_handler[] = { - (iw_handler) airo_config_commit, /* SIOCSIWCOMMIT */ - (iw_handler) airo_get_name, /* SIOCGIWNAME */ - (iw_handler) NULL, /* SIOCSIWNWID */ - (iw_handler) NULL, /* SIOCGIWNWID */ - (iw_handler) airo_set_freq, /* SIOCSIWFREQ */ - (iw_handler) airo_get_freq, /* SIOCGIWFREQ */ - (iw_handler) airo_set_mode, /* SIOCSIWMODE */ - (iw_handler) airo_get_mode, /* SIOCGIWMODE */ - (iw_handler) airo_set_sens, /* SIOCSIWSENS */ - (iw_handler) airo_get_sens, /* SIOCGIWSENS */ - (iw_handler) NULL, /* SIOCSIWRANGE */ - (iw_handler) airo_get_range, /* SIOCGIWRANGE */ - (iw_handler) NULL, /* SIOCSIWPRIV */ - (iw_handler) NULL, /* SIOCGIWPRIV */ - (iw_handler) NULL, /* SIOCSIWSTATS */ - (iw_handler) NULL, /* SIOCGIWSTATS */ - iw_handler_set_spy, /* SIOCSIWSPY */ - iw_handler_get_spy, /* SIOCGIWSPY */ - iw_handler_set_thrspy, /* SIOCSIWTHRSPY */ - iw_handler_get_thrspy, /* SIOCGIWTHRSPY */ - (iw_handler) airo_set_wap, /* SIOCSIWAP */ - (iw_handler) airo_get_wap, /* SIOCGIWAP */ - (iw_handler) NULL, /* -- hole -- */ - (iw_handler) airo_get_aplist, /* SIOCGIWAPLIST */ - (iw_handler) airo_set_scan, /* SIOCSIWSCAN */ - (iw_handler) airo_get_scan, /* SIOCGIWSCAN */ - (iw_handler) airo_set_essid, /* SIOCSIWESSID */ - (iw_handler) airo_get_essid, /* SIOCGIWESSID */ - (iw_handler) airo_set_nick, /* SIOCSIWNICKN */ - (iw_handler) airo_get_nick, /* SIOCGIWNICKN */ - (iw_handler) NULL, /* -- hole -- */ - (iw_handler) NULL, /* -- hole -- */ - (iw_handler) airo_set_rate, /* SIOCSIWRATE */ - (iw_handler) airo_get_rate, /* SIOCGIWRATE */ - (iw_handler) airo_set_rts, /* SIOCSIWRTS */ - (iw_handler) airo_get_rts, /* SIOCGIWRTS */ - (iw_handler) airo_set_frag, /* SIOCSIWFRAG */ - (iw_handler) airo_get_frag, /* SIOCGIWFRAG */ - (iw_handler) airo_set_txpow, /* SIOCSIWTXPOW */ - (iw_handler) airo_get_txpow, /* SIOCGIWTXPOW */ - (iw_handler) airo_set_retry, /* SIOCSIWRETRY */ - (iw_handler) airo_get_retry, /* SIOCGIWRETRY */ - (iw_handler) airo_set_encode, /* SIOCSIWENCODE */ - (iw_handler) airo_get_encode, /* SIOCGIWENCODE */ - (iw_handler) airo_set_power, /* SIOCSIWPOWER */ - (iw_handler) airo_get_power, /* SIOCGIWPOWER */ - (iw_handler) NULL, /* -- hole -- */ - (iw_handler) NULL, /* -- hole -- */ - (iw_handler) NULL, /* SIOCSIWGENIE */ - (iw_handler) NULL, /* SIOCGIWGENIE */ - (iw_handler) airo_set_auth, /* SIOCSIWAUTH */ - (iw_handler) airo_get_auth, /* SIOCGIWAUTH */ - (iw_handler) airo_set_encodeext, /* SIOCSIWENCODEEXT */ - (iw_handler) airo_get_encodeext, /* SIOCGIWENCODEEXT */ - (iw_handler) NULL, /* SIOCSIWPMKSA */ + IW_HANDLER(SIOCSIWCOMMIT, airo_config_commit), + IW_HANDLER(SIOCGIWNAME, airo_get_name), + IW_HANDLER(SIOCSIWFREQ, airo_set_freq), + IW_HANDLER(SIOCGIWFREQ, airo_get_freq), + IW_HANDLER(SIOCSIWMODE, airo_set_mode), + IW_HANDLER(SIOCGIWMODE, airo_get_mode), + IW_HANDLER(SIOCSIWSENS, airo_set_sens), + IW_HANDLER(SIOCGIWSENS, airo_get_sens), + IW_HANDLER(SIOCGIWRANGE, airo_get_range), + IW_HANDLER(SIOCSIWSPY, iw_handler_set_spy), + IW_HANDLER(SIOCGIWSPY, iw_handler_get_spy), + IW_HANDLER(SIOCSIWTHRSPY, iw_handler_set_thrspy), + IW_HANDLER(SIOCGIWTHRSPY, iw_handler_get_thrspy), + IW_HANDLER(SIOCSIWAP, airo_set_wap), + IW_HANDLER(SIOCGIWAP, airo_get_wap), + IW_HANDLER(SIOCGIWAPLIST, airo_get_aplist), + IW_HANDLER(SIOCSIWSCAN, airo_set_scan), + IW_HANDLER(SIOCGIWSCAN, airo_get_scan), + IW_HANDLER(SIOCSIWESSID, airo_set_essid), + IW_HANDLER(SIOCGIWESSID, airo_get_essid), + IW_HANDLER(SIOCSIWNICKN, airo_set_nick), + IW_HANDLER(SIOCGIWNICKN, airo_get_nick), + IW_HANDLER(SIOCSIWRATE, airo_set_rate), + IW_HANDLER(SIOCGIWRATE, airo_get_rate), + IW_HANDLER(SIOCSIWRTS, airo_set_rts), + IW_HANDLER(SIOCGIWRTS, airo_get_rts), + IW_HANDLER(SIOCSIWFRAG, airo_set_frag), + IW_HANDLER(SIOCGIWFRAG, airo_get_frag), + IW_HANDLER(SIOCSIWTXPOW, airo_set_txpow), + IW_HANDLER(SIOCGIWTXPOW, airo_get_txpow), + IW_HANDLER(SIOCSIWRETRY, airo_set_retry), + IW_HANDLER(SIOCGIWRETRY, airo_get_retry), + IW_HANDLER(SIOCSIWENCODE, airo_set_encode), + IW_HANDLER(SIOCGIWENCODE, airo_get_encode), + IW_HANDLER(SIOCSIWPOWER, airo_set_power), + IW_HANDLER(SIOCGIWPOWER, airo_get_power), + IW_HANDLER(SIOCSIWAUTH, airo_set_auth), + IW_HANDLER(SIOCGIWAUTH, airo_get_auth), + IW_HANDLER(SIOCSIWENCODEEXT, airo_set_encodeext), + IW_HANDLER(SIOCGIWENCODEEXT, airo_get_encodeext), }; /* Note : don't describe AIROIDIFC and AIROOLDIDIFC in here. From 61ec34dee266f1cea092986d861a2cb136571199 Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Sun, 13 Nov 2022 16:42:21 +0100 Subject: [PATCH 0966/2002] wifi: rtw89: Fix some error handling path in rtw89_wow_enable() 'ret' is not updated after several function calls in rtw89_wow_enable(). This prevent error handling from working. Add the missing assignments. Fixes: 19e28c7fcc74 ("wifi: rtw89: add WoWLAN function support") Signed-off-by: Christophe JAILLET Acked-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/32320176eeff1c635baeea25ef0e87d116859e65.1668354083.git.christophe.jaillet@wanadoo.fr --- drivers/net/wireless/realtek/rtw89/wow.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/wow.c b/drivers/net/wireless/realtek/rtw89/wow.c index 7de4dd047d6b..b2b826b2e09a 100644 --- a/drivers/net/wireless/realtek/rtw89/wow.c +++ b/drivers/net/wireless/realtek/rtw89/wow.c @@ -744,13 +744,13 @@ static int rtw89_wow_enable(struct rtw89_dev *rtwdev) goto out; } - rtw89_wow_swap_fw(rtwdev, true); + ret = rtw89_wow_swap_fw(rtwdev, true); if (ret) { rtw89_err(rtwdev, "wow: failed to swap to wow fw\n"); goto out; } - rtw89_wow_fw_start(rtwdev); + ret = rtw89_wow_fw_start(rtwdev); if (ret) { rtw89_err(rtwdev, "wow: failed to let wow fw start\n"); goto out; @@ -758,7 +758,7 @@ static int rtw89_wow_enable(struct rtw89_dev *rtwdev) rtw89_wow_enter_lps(rtwdev); - rtw89_wow_enable_trx_post(rtwdev); + ret = rtw89_wow_enable_trx_post(rtwdev); if (ret) { rtw89_err(rtwdev, "wow: failed to enable trx_post\n"); goto out; From 81c0b8928437ddfe87b0024c5bd817bc331d8524 Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Sun, 13 Nov 2022 16:49:18 +0100 Subject: [PATCH 0967/2002] wifi: rtw89: Fix some error handling path in rtw89_core_sta_assoc() 'ret' is not updated after a function call in rtw89_core_sta_assoc(). This prevent error handling from working. Add the missing assignment. Fixes: e3ec7017f6a2 ("rtw89: add Realtek 802.11ax driver") Signed-off-by: Christophe JAILLET Acked-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/7b1d82594635e4406d3438f33d8da29eaa056c5a.1668354547.git.christophe.jaillet@wanadoo.fr --- drivers/net/wireless/realtek/rtw89/core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c index e716b96d0f56..f30aadc41f2b 100644 --- a/drivers/net/wireless/realtek/rtw89/core.c +++ b/drivers/net/wireless/realtek/rtw89/core.c @@ -2535,7 +2535,7 @@ int rtw89_core_sta_assoc(struct rtw89_dev *rtwdev, } /* update cam aid mac_id net_type */ - rtw89_fw_h2c_cam(rtwdev, rtwvif, rtwsta, NULL); + ret = rtw89_fw_h2c_cam(rtwdev, rtwvif, rtwsta, NULL); if (ret) { rtw89_warn(rtwdev, "failed to send h2c cam\n"); return ret; From e14e4c933e0047653f835e30d7d740ebb2a530cc Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Wed, 9 Nov 2022 14:42:48 -0800 Subject: [PATCH 0968/2002] ARM: OMAP2+: pdata-quirks: stop including wl12xx.h As of commit 2398c41d6432 ("omap: pdata-quirks: remove openpandora quirks for mmc3 and wl1251") the code no longer creates an instance of wl1251_platform_data, so there is no need for including this header. Signed-off-by: Dmitry Torokhov Acked-by: Tony Lindgren Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20221109224250.2885119-1-dmitry.torokhov@gmail.com --- arch/arm/mach-omap2/pdata-quirks.c | 1 - 1 file changed, 1 deletion(-) diff --git a/arch/arm/mach-omap2/pdata-quirks.c b/arch/arm/mach-omap2/pdata-quirks.c index 5b99d602c87b..9409b9f5711c 100644 --- a/arch/arm/mach-omap2/pdata-quirks.c +++ b/arch/arm/mach-omap2/pdata-quirks.c @@ -10,7 +10,6 @@ #include #include #include -#include #include #include #include From 06463f6e98df34908c26aa8e7a31a279646b1f51 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Wed, 9 Nov 2022 14:42:49 -0800 Subject: [PATCH 0969/2002] wifi: wl1251: drop support for platform data Remove support for configuring the device via platform data because there are no users of wl1251_platform_data left in the mainline kernel. Signed-off-by: Dmitry Torokhov Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20221109224250.2885119-2-dmitry.torokhov@gmail.com --- MAINTAINERS | 1 - drivers/net/wireless/ti/Kconfig | 8 ---- .../net/wireless/ti/wilink_platform_data.c | 35 --------------- drivers/net/wireless/ti/wl1251/sdio.c | 8 +--- drivers/net/wireless/ti/wl1251/spi.c | 15 ++----- drivers/net/wireless/ti/wlcore/spi.c | 1 - include/linux/wl12xx.h | 44 ------------------- 7 files changed, 4 insertions(+), 108 deletions(-) delete mode 100644 drivers/net/wireless/ti/wilink_platform_data.c delete mode 100644 include/linux/wl12xx.h diff --git a/MAINTAINERS b/MAINTAINERS index 377aedb3808b..56764f76752b 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -20665,7 +20665,6 @@ W: https://wireless.wiki.kernel.org/en/users/Drivers/wl12xx W: https://wireless.wiki.kernel.org/en/users/Drivers/wl1251 T: git git://git.kernel.org/pub/scm/linux/kernel/git/luca/wl12xx.git F: drivers/net/wireless/ti/ -F: include/linux/wl12xx.h TIMEKEEPING, CLOCKSOURCE CORE, NTP, ALARMTIMER M: John Stultz diff --git a/drivers/net/wireless/ti/Kconfig b/drivers/net/wireless/ti/Kconfig index 7c0b17a76fe2..3fcd9e395f72 100644 --- a/drivers/net/wireless/ti/Kconfig +++ b/drivers/net/wireless/ti/Kconfig @@ -18,12 +18,4 @@ source "drivers/net/wireless/ti/wl18xx/Kconfig" # keep last for automatic dependencies source "drivers/net/wireless/ti/wlcore/Kconfig" -config WILINK_PLATFORM_DATA - bool "TI WiLink platform data" - depends on WLCORE_SDIO || WL1251_SDIO - default y - help - Small platform data bit needed to pass data to the sdio modules. - - endif # WLAN_VENDOR_TI diff --git a/drivers/net/wireless/ti/wilink_platform_data.c b/drivers/net/wireless/ti/wilink_platform_data.c deleted file mode 100644 index 1de6a62d526f..000000000000 --- a/drivers/net/wireless/ti/wilink_platform_data.c +++ /dev/null @@ -1,35 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * This file is part of wl12xx - * - * Copyright (C) 2010-2011 Texas Instruments, Inc. - */ - -#include -#include -#include - -static struct wl1251_platform_data *wl1251_platform_data; - -int __init wl1251_set_platform_data(const struct wl1251_platform_data *data) -{ - if (wl1251_platform_data) - return -EBUSY; - if (!data) - return -EINVAL; - - wl1251_platform_data = kmemdup(data, sizeof(*data), GFP_KERNEL); - if (!wl1251_platform_data) - return -ENOMEM; - - return 0; -} - -struct wl1251_platform_data *wl1251_get_platform_data(void) -{ - if (!wl1251_platform_data) - return ERR_PTR(-ENODEV); - - return wl1251_platform_data; -} -EXPORT_SYMBOL(wl1251_get_platform_data); diff --git a/drivers/net/wireless/ti/wl1251/sdio.c b/drivers/net/wireless/ti/wl1251/sdio.c index c9a4e9a43400..301bd0043a43 100644 --- a/drivers/net/wireless/ti/wl1251/sdio.c +++ b/drivers/net/wireless/ti/wl1251/sdio.c @@ -12,7 +12,6 @@ #include #include #include -#include #include #include #include @@ -197,7 +196,6 @@ static int wl1251_sdio_probe(struct sdio_func *func, struct wl1251 *wl; struct ieee80211_hw *hw; struct wl1251_sdio *wl_sdio; - const struct wl1251_platform_data *wl1251_board_data; struct device_node *np = func->dev.of_node; hw = wl1251_alloc_hw(); @@ -225,11 +223,7 @@ static int wl1251_sdio_probe(struct sdio_func *func, wl->if_priv = wl_sdio; wl->if_ops = &wl1251_sdio_ops; - wl1251_board_data = wl1251_get_platform_data(); - if (!IS_ERR(wl1251_board_data)) { - wl->irq = wl1251_board_data->irq; - wl->use_eeprom = wl1251_board_data->use_eeprom; - } else if (np) { + if (np) { wl->use_eeprom = of_property_read_bool(np, "ti,wl1251-has-eeprom"); wl->irq = of_irq_get(np, 0); if (wl->irq == -EPROBE_DEFER) { diff --git a/drivers/net/wireless/ti/wl1251/spi.c b/drivers/net/wireless/ti/wl1251/spi.c index 9df38726e8b0..08d9814b49c1 100644 --- a/drivers/net/wireless/ti/wl1251/spi.c +++ b/drivers/net/wireless/ti/wl1251/spi.c @@ -12,7 +12,6 @@ #include #include #include -#include #include #include #include @@ -226,16 +225,13 @@ static const struct wl1251_if_operations wl1251_spi_ops = { static int wl1251_spi_probe(struct spi_device *spi) { - struct wl1251_platform_data *pdata = dev_get_platdata(&spi->dev); struct device_node *np = spi->dev.of_node; struct ieee80211_hw *hw; struct wl1251 *wl; int ret; - if (!np && !pdata) { - wl1251_error("no platform data"); + if (!np) return -ENODEV; - } hw = wl1251_alloc_hw(); if (IS_ERR(hw)) @@ -259,14 +255,9 @@ static int wl1251_spi_probe(struct spi_device *spi) goto out_free; } - if (np) { - wl->use_eeprom = of_property_read_bool(np, "ti,wl1251-has-eeprom"); - wl->power_gpio = of_get_named_gpio(np, "ti,power-gpio", 0); - } else if (pdata) { - wl->power_gpio = pdata->power_gpio; - wl->use_eeprom = pdata->use_eeprom; - } + wl->use_eeprom = of_property_read_bool(np, "ti,wl1251-has-eeprom"); + wl->power_gpio = of_get_named_gpio(np, "ti,power-gpio", 0); if (wl->power_gpio == -EPROBE_DEFER) { ret = -EPROBE_DEFER; goto out_free; diff --git a/drivers/net/wireless/ti/wlcore/spi.c b/drivers/net/wireless/ti/wlcore/spi.c index 7eae1ec2eb2b..2d2edddc77bd 100644 --- a/drivers/net/wireless/ti/wlcore/spi.c +++ b/drivers/net/wireless/ti/wlcore/spi.c @@ -14,7 +14,6 @@ #include #include #include -#include #include #include #include diff --git a/include/linux/wl12xx.h b/include/linux/wl12xx.h deleted file mode 100644 index 03d61f1d23ab..000000000000 --- a/include/linux/wl12xx.h +++ /dev/null @@ -1,44 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * This file is part of wl12xx - * - * Copyright (C) 2009 Nokia Corporation - * - * Contact: Luciano Coelho - */ - -#ifndef _LINUX_WL12XX_H -#define _LINUX_WL12XX_H - -#include - -struct wl1251_platform_data { - int power_gpio; - /* SDIO only: IRQ number if WLAN_IRQ line is used, 0 for SDIO IRQs */ - int irq; - bool use_eeprom; -}; - -#ifdef CONFIG_WILINK_PLATFORM_DATA - -int wl1251_set_platform_data(const struct wl1251_platform_data *data); - -struct wl1251_platform_data *wl1251_get_platform_data(void); - -#else - -static inline -int wl1251_set_platform_data(const struct wl1251_platform_data *data) -{ - return -ENOSYS; -} - -static inline -struct wl1251_platform_data *wl1251_get_platform_data(void) -{ - return ERR_PTR(-ENODATA); -} - -#endif - -#endif From a1d0b9535cd8ce6f6c6870fcac1760cbaafc0f48 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Wed, 9 Nov 2022 14:42:50 -0800 Subject: [PATCH 0970/2002] wifi: wl1251: switch to using gpiod API Switch the driver from legacy gpio API to the newer gpiod API. Signed-off-by: Dmitry Torokhov Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20221109224250.2885119-3-dmitry.torokhov@gmail.com --- drivers/net/wireless/ti/wl1251/spi.c | 63 +++++++++++++------------ drivers/net/wireless/ti/wl1251/wl1251.h | 1 - 2 files changed, 34 insertions(+), 30 deletions(-) diff --git a/drivers/net/wireless/ti/wl1251/spi.c b/drivers/net/wireless/ti/wl1251/spi.c index 08d9814b49c1..29292f06bd3d 100644 --- a/drivers/net/wireless/ti/wl1251/spi.c +++ b/drivers/net/wireless/ti/wl1251/spi.c @@ -5,6 +5,7 @@ * Copyright (C) 2008 Nokia Corporation */ +#include #include #include #include @@ -12,15 +13,19 @@ #include #include #include -#include +#include #include -#include #include #include "wl1251.h" #include "reg.h" #include "spi.h" +struct wl1251_spi { + struct spi_device *spi; + struct gpio_desc *power_gpio; +}; + static irqreturn_t wl1251_irq(int irq, void *cookie) { struct wl1251 *wl; @@ -34,13 +39,9 @@ static irqreturn_t wl1251_irq(int irq, void *cookie) return IRQ_HANDLED; } -static struct spi_device *wl_to_spi(struct wl1251 *wl) -{ - return wl->if_priv; -} - static void wl1251_spi_reset(struct wl1251 *wl) { + struct wl1251_spi *wl_spi = wl->if_priv; u8 *cmd; struct spi_transfer t; struct spi_message m; @@ -60,7 +61,7 @@ static void wl1251_spi_reset(struct wl1251 *wl) t.len = WSPI_INIT_CMD_LEN; spi_message_add_tail(&t, &m); - spi_sync(wl_to_spi(wl), &m); + spi_sync(wl_spi->spi, &m); wl1251_dump(DEBUG_SPI, "spi reset -> ", cmd, WSPI_INIT_CMD_LEN); @@ -69,6 +70,7 @@ static void wl1251_spi_reset(struct wl1251 *wl) static void wl1251_spi_wake(struct wl1251 *wl) { + struct wl1251_spi *wl_spi = wl->if_priv; struct spi_transfer t; struct spi_message m; u8 *cmd = kzalloc(WSPI_INIT_CMD_LEN, GFP_KERNEL); @@ -112,7 +114,7 @@ static void wl1251_spi_wake(struct wl1251 *wl) t.len = WSPI_INIT_CMD_LEN; spi_message_add_tail(&t, &m); - spi_sync(wl_to_spi(wl), &m); + spi_sync(wl_spi->spi, &m); wl1251_dump(DEBUG_SPI, "spi init -> ", cmd, WSPI_INIT_CMD_LEN); @@ -128,6 +130,7 @@ static void wl1251_spi_reset_wake(struct wl1251 *wl) static void wl1251_spi_read(struct wl1251 *wl, int addr, void *buf, size_t len) { + struct wl1251_spi *wl_spi = wl->if_priv; struct spi_transfer t[3]; struct spi_message m; u8 *busy_buf; @@ -157,7 +160,7 @@ static void wl1251_spi_read(struct wl1251 *wl, int addr, void *buf, t[2].len = len; spi_message_add_tail(&t[2], &m); - spi_sync(wl_to_spi(wl), &m); + spi_sync(wl_spi->spi, &m); /* FIXME: check busy words */ @@ -168,6 +171,7 @@ static void wl1251_spi_read(struct wl1251 *wl, int addr, void *buf, static void wl1251_spi_write(struct wl1251 *wl, int addr, void *buf, size_t len) { + struct wl1251_spi *wl_spi = wl->if_priv; struct spi_transfer t[2]; struct spi_message m; u32 *cmd; @@ -190,7 +194,7 @@ static void wl1251_spi_write(struct wl1251 *wl, int addr, void *buf, t[1].len = len; spi_message_add_tail(&t[1], &m); - spi_sync(wl_to_spi(wl), &m); + spi_sync(wl_spi->spi, &m); wl1251_dump(DEBUG_SPI, "spi_write cmd -> ", cmd, sizeof(*cmd)); wl1251_dump(DEBUG_SPI, "spi_write buf -> ", buf, len); @@ -208,8 +212,10 @@ static void wl1251_spi_disable_irq(struct wl1251 *wl) static int wl1251_spi_set_power(struct wl1251 *wl, bool enable) { - if (gpio_is_valid(wl->power_gpio)) - gpio_set_value(wl->power_gpio, enable); + struct wl1251_spi *wl_spi = wl->if_priv; + + if (wl_spi->power_gpio) + gpiod_set_value_cansleep(wl_spi->power_gpio, enable); return 0; } @@ -227,12 +233,19 @@ static int wl1251_spi_probe(struct spi_device *spi) { struct device_node *np = spi->dev.of_node; struct ieee80211_hw *hw; + struct wl1251_spi *wl_spi; struct wl1251 *wl; int ret; if (!np) return -ENODEV; + wl_spi = devm_kzalloc(&spi->dev, sizeof(*wl_spi), GFP_KERNEL); + if (!wl_spi) + return -ENOMEM; + + wl_spi->spi = spi; + hw = wl1251_alloc_hw(); if (IS_ERR(hw)) return PTR_ERR(hw); @@ -241,7 +254,7 @@ static int wl1251_spi_probe(struct spi_device *spi) SET_IEEE80211_DEV(hw, &spi->dev); spi_set_drvdata(spi, wl); - wl->if_priv = spi; + wl->if_priv = wl_spi; wl->if_ops = &wl1251_spi_ops; /* This is the only SPI value that we need to set here, the rest @@ -257,24 +270,16 @@ static int wl1251_spi_probe(struct spi_device *spi) wl->use_eeprom = of_property_read_bool(np, "ti,wl1251-has-eeprom"); - wl->power_gpio = of_get_named_gpio(np, "ti,power-gpio", 0); - if (wl->power_gpio == -EPROBE_DEFER) { - ret = -EPROBE_DEFER; + wl_spi->power_gpio = devm_gpiod_get_optional(&spi->dev, "ti,power", + GPIOD_OUT_LOW); + ret = PTR_ERR_OR_ZERO(wl_spi->power_gpio); + if (ret) { + if (ret != -EPROBE_DEFER) + wl1251_error("Failed to request gpio: %d\n", ret); goto out_free; } - if (gpio_is_valid(wl->power_gpio)) { - ret = devm_gpio_request_one(&spi->dev, wl->power_gpio, - GPIOF_OUT_INIT_LOW, "wl1251 power"); - if (ret) { - wl1251_error("Failed to request gpio: %d\n", ret); - goto out_free; - } - } else { - wl1251_error("set power gpio missing in platform data"); - ret = -ENODEV; - goto out_free; - } + gpiod_set_consumer_name(wl_spi->power_gpio, "wl1251 power"); wl->irq = spi->irq; if (wl->irq < 0) { diff --git a/drivers/net/wireless/ti/wl1251/wl1251.h b/drivers/net/wireless/ti/wl1251/wl1251.h index 23ae07dd4c2e..83adbc3c25dc 100644 --- a/drivers/net/wireless/ti/wl1251/wl1251.h +++ b/drivers/net/wireless/ti/wl1251/wl1251.h @@ -262,7 +262,6 @@ struct wl1251 { void *if_priv; const struct wl1251_if_operations *if_ops; - int power_gpio; int irq; bool use_eeprom; From 919dfa0b20ae56060dce0436eb710717f8987d18 Mon Sep 17 00:00:00 2001 From: Kuniyuki Iwashima Date: Mon, 14 Nov 2022 13:57:53 -0800 Subject: [PATCH 0971/2002] udp: Clean up some functions. This patch adds no functional change and cleans up some functions that the following patches touch around so that we make them tidy and easy to review/revert. The change is mainly to keep reverse christmas tree order. Signed-off-by: Kuniyuki Iwashima Signed-off-by: David S. Miller --- net/ipv4/udp.c | 39 +++++++++++++++++++++++---------------- net/ipv6/udp.c | 12 ++++++++---- 2 files changed, 31 insertions(+), 20 deletions(-) diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index b859d6c8298e..a34de263e9ce 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c @@ -232,16 +232,16 @@ static int udp_reuseport_add_sock(struct sock *sk, struct udp_hslot *hslot) int udp_lib_get_port(struct sock *sk, unsigned short snum, unsigned int hash2_nulladdr) { - struct udp_hslot *hslot, *hslot2; struct udp_table *udptable = sk->sk_prot->h.udp_table; - int error = 1; + struct udp_hslot *hslot, *hslot2; struct net *net = sock_net(sk); + int error = 1; if (!snum) { + DECLARE_BITMAP(bitmap, PORTS_PER_CHAIN); + unsigned short first, last; int low, high, remaining; unsigned int rand; - unsigned short first, last; - DECLARE_BITMAP(bitmap, PORTS_PER_CHAIN); inet_get_local_port_range(net, &low, &high); remaining = (high - low) + 1; @@ -2519,10 +2519,13 @@ static struct sock *__udp4_lib_mcast_demux_lookup(struct net *net, __be16 rmt_port, __be32 rmt_addr, int dif, int sdif) { - struct sock *sk, *result; unsigned short hnum = ntohs(loc_port); - unsigned int slot = udp_hashfn(net, hnum, udp_table.mask); - struct udp_hslot *hslot = &udp_table.hash[slot]; + struct sock *sk, *result; + struct udp_hslot *hslot; + unsigned int slot; + + slot = udp_hashfn(net, hnum, udp_table.mask); + hslot = &udp_table.hash[slot]; /* Do not bother scanning a too big list */ if (hslot->count > 10) @@ -2550,14 +2553,18 @@ static struct sock *__udp4_lib_demux_lookup(struct net *net, __be16 rmt_port, __be32 rmt_addr, int dif, int sdif) { - unsigned short hnum = ntohs(loc_port); - unsigned int hash2 = ipv4_portaddr_hash(net, loc_addr, hnum); - unsigned int slot2 = hash2 & udp_table.mask; - struct udp_hslot *hslot2 = &udp_table.hash2[slot2]; INET_ADDR_COOKIE(acookie, rmt_addr, loc_addr); - const __portpair ports = INET_COMBINED_PORTS(rmt_port, hnum); + unsigned short hnum = ntohs(loc_port); + unsigned int hash2, slot2; + struct udp_hslot *hslot2; + __portpair ports; struct sock *sk; + hash2 = ipv4_portaddr_hash(net, loc_addr, hnum); + slot2 = hash2 & udp_table.mask; + hslot2 = &udp_table.hash2[slot2]; + ports = INET_COMBINED_PORTS(rmt_port, hnum); + udp_portaddr_for_each_entry_rcu(sk, &hslot2->head) { if (inet_match(net, sk, acookie, ports, dif, sdif)) return sk; @@ -2970,10 +2977,10 @@ EXPORT_SYMBOL(udp_prot); static struct sock *udp_get_first(struct seq_file *seq, int start) { - struct sock *sk; - struct udp_seq_afinfo *afinfo; struct udp_iter_state *state = seq->private; struct net *net = seq_file_net(seq); + struct udp_seq_afinfo *afinfo; + struct sock *sk; if (state->bpf_seq_afinfo) afinfo = state->bpf_seq_afinfo; @@ -3004,9 +3011,9 @@ found: static struct sock *udp_get_next(struct seq_file *seq, struct sock *sk) { - struct udp_seq_afinfo *afinfo; struct udp_iter_state *state = seq->private; struct net *net = seq_file_net(seq); + struct udp_seq_afinfo *afinfo; if (state->bpf_seq_afinfo) afinfo = state->bpf_seq_afinfo; @@ -3062,8 +3069,8 @@ EXPORT_SYMBOL(udp_seq_next); void udp_seq_stop(struct seq_file *seq, void *v) { - struct udp_seq_afinfo *afinfo; struct udp_iter_state *state = seq->private; + struct udp_seq_afinfo *afinfo; if (state->bpf_seq_afinfo) afinfo = state->bpf_seq_afinfo; diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c index e2de3d906c82..727de67e4c90 100644 --- a/net/ipv6/udp.c +++ b/net/ipv6/udp.c @@ -1064,12 +1064,16 @@ static struct sock *__udp6_lib_demux_lookup(struct net *net, int dif, int sdif) { unsigned short hnum = ntohs(loc_port); - unsigned int hash2 = ipv6_portaddr_hash(net, loc_addr, hnum); - unsigned int slot2 = hash2 & udp_table.mask; - struct udp_hslot *hslot2 = &udp_table.hash2[slot2]; - const __portpair ports = INET_COMBINED_PORTS(rmt_port, hnum); + unsigned int hash2, slot2; + struct udp_hslot *hslot2; + __portpair ports; struct sock *sk; + hash2 = ipv6_portaddr_hash(net, loc_addr, hnum); + slot2 = hash2 & udp_table.mask; + hslot2 = &udp_table.hash2[slot2]; + ports = INET_COMBINED_PORTS(rmt_port, hnum); + udp_portaddr_for_each_entry_rcu(sk, &hslot2->head) { if (sk->sk_state == TCP_ESTABLISHED && inet6_match(net, sk, rmt_addr, loc_addr, ports, dif, sdif)) From 67fb43308f4b354f13aabcc66dd5d99bfbb7e838 Mon Sep 17 00:00:00 2001 From: Kuniyuki Iwashima Date: Mon, 14 Nov 2022 13:57:54 -0800 Subject: [PATCH 0972/2002] udp: Set NULL to sk->sk_prot->h.udp_table. We will soon introduce an optional per-netns hash table for UDP. This means we cannot use the global sk->sk_prot->h.udp_table to fetch a UDP hash table. Instead, set NULL to sk->sk_prot->h.udp_table for UDP and get a proper table from net->ipv4.udp_table. Note that we still need sk->sk_prot->h.udp_table for UDP LITE. Signed-off-by: Kuniyuki Iwashima Signed-off-by: David S. Miller --- include/net/netns/ipv4.h | 1 + net/ipv4/udp.c | 15 +++++++++++---- net/ipv6/udp.c | 2 +- 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/include/net/netns/ipv4.h b/include/net/netns/ipv4.h index 25f90bba4889..e4cc4d3cacc4 100644 --- a/include/net/netns/ipv4.h +++ b/include/net/netns/ipv4.h @@ -43,6 +43,7 @@ struct tcp_fastopen_context; struct netns_ipv4 { struct inet_timewait_death_row tcp_death_row; + struct udp_table *udp_table; #ifdef CONFIG_SYSCTL struct ctl_table_header *forw_hdr; diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index a34de263e9ce..6206c27a1659 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c @@ -131,6 +131,11 @@ EXPORT_PER_CPU_SYMBOL_GPL(udp_memory_per_cpu_fw_alloc); #define MAX_UDP_PORTS 65536 #define PORTS_PER_CHAIN (MAX_UDP_PORTS / UDP_HTABLE_SIZE_MIN) +static struct udp_table *udp_get_table_prot(struct sock *sk) +{ + return sk->sk_prot->h.udp_table ? : sock_net(sk)->ipv4.udp_table; +} + static int udp_lib_lport_inuse(struct net *net, __u16 num, const struct udp_hslot *hslot, unsigned long *bitmap, @@ -232,7 +237,7 @@ static int udp_reuseport_add_sock(struct sock *sk, struct udp_hslot *hslot) int udp_lib_get_port(struct sock *sk, unsigned short snum, unsigned int hash2_nulladdr) { - struct udp_table *udptable = sk->sk_prot->h.udp_table; + struct udp_table *udptable = udp_get_table_prot(sk); struct udp_hslot *hslot, *hslot2; struct net *net = sock_net(sk); int error = 1; @@ -1999,7 +2004,7 @@ EXPORT_SYMBOL(udp_disconnect); void udp_lib_unhash(struct sock *sk) { if (sk_hashed(sk)) { - struct udp_table *udptable = sk->sk_prot->h.udp_table; + struct udp_table *udptable = udp_get_table_prot(sk); struct udp_hslot *hslot, *hslot2; hslot = udp_hashslot(udptable, sock_net(sk), @@ -2030,7 +2035,7 @@ EXPORT_SYMBOL(udp_lib_unhash); void udp_lib_rehash(struct sock *sk, u16 newhash) { if (sk_hashed(sk)) { - struct udp_table *udptable = sk->sk_prot->h.udp_table; + struct udp_table *udptable = udp_get_table_prot(sk); struct udp_hslot *hslot, *hslot2, *nhslot2; hslot2 = udp_hashslot2(udptable, udp_sk(sk)->udp_portaddr_hash); @@ -2967,7 +2972,7 @@ struct proto udp_prot = { .sysctl_wmem_offset = offsetof(struct net, ipv4.sysctl_udp_wmem_min), .sysctl_rmem_offset = offsetof(struct net, ipv4.sysctl_udp_rmem_min), .obj_size = sizeof(struct udp_sock), - .h.udp_table = &udp_table, + .h.udp_table = NULL, .diag_destroy = udp_abort, }; EXPORT_SYMBOL(udp_prot); @@ -3280,6 +3285,8 @@ EXPORT_SYMBOL(udp_flow_hashrnd); static int __net_init udp_sysctl_init(struct net *net) { + net->ipv4.udp_table = &udp_table; + net->ipv4.sysctl_udp_rmem_min = PAGE_SIZE; net->ipv4.sysctl_udp_wmem_min = PAGE_SIZE; diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c index 727de67e4c90..bbd6dc398f3b 100644 --- a/net/ipv6/udp.c +++ b/net/ipv6/udp.c @@ -1774,7 +1774,7 @@ struct proto udpv6_prot = { .sysctl_wmem_offset = offsetof(struct net, ipv4.sysctl_udp_wmem_min), .sysctl_rmem_offset = offsetof(struct net, ipv4.sysctl_udp_rmem_min), .obj_size = sizeof(struct udp6_sock), - .h.udp_table = &udp_table, + .h.udp_table = NULL, .diag_destroy = udp_abort, }; From 478aee5d6bf617c932f4e9c2981f17e86e093fc5 Mon Sep 17 00:00:00 2001 From: Kuniyuki Iwashima Date: Mon, 14 Nov 2022 13:57:55 -0800 Subject: [PATCH 0973/2002] udp: Set NULL to udp_seq_afinfo.udp_table. We will soon introduce an optional per-netns hash table for UDP. This means we cannot use the global udp_seq_afinfo.udp_table to fetch a UDP hash table. Instead, set NULL to udp_seq_afinfo.udp_table for UDP and get a proper table from net->ipv4.udp_table. Note that we still need udp_seq_afinfo.udp_table for UDP LITE. Signed-off-by: Kuniyuki Iwashima Signed-off-by: David S. Miller --- net/ipv4/udp.c | 32 ++++++++++++++++++++++++-------- net/ipv6/udp.c | 2 +- 2 files changed, 25 insertions(+), 9 deletions(-) diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index 6206c27a1659..a1a15eb76304 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c @@ -2980,11 +2980,18 @@ EXPORT_SYMBOL(udp_prot); /* ------------------------------------------------------------------------ */ #ifdef CONFIG_PROC_FS +static struct udp_table *udp_get_table_afinfo(struct udp_seq_afinfo *afinfo, + struct net *net) +{ + return afinfo->udp_table ? : net->ipv4.udp_table; +} + static struct sock *udp_get_first(struct seq_file *seq, int start) { struct udp_iter_state *state = seq->private; struct net *net = seq_file_net(seq); struct udp_seq_afinfo *afinfo; + struct udp_table *udptable; struct sock *sk; if (state->bpf_seq_afinfo) @@ -2992,9 +2999,11 @@ static struct sock *udp_get_first(struct seq_file *seq, int start) else afinfo = pde_data(file_inode(seq->file)); - for (state->bucket = start; state->bucket <= afinfo->udp_table->mask; + udptable = udp_get_table_afinfo(afinfo, net); + + for (state->bucket = start; state->bucket <= udptable->mask; ++state->bucket) { - struct udp_hslot *hslot = &afinfo->udp_table->hash[state->bucket]; + struct udp_hslot *hslot = &udptable->hash[state->bucket]; if (hlist_empty(&hslot->head)) continue; @@ -3019,6 +3028,7 @@ static struct sock *udp_get_next(struct seq_file *seq, struct sock *sk) struct udp_iter_state *state = seq->private; struct net *net = seq_file_net(seq); struct udp_seq_afinfo *afinfo; + struct udp_table *udptable; if (state->bpf_seq_afinfo) afinfo = state->bpf_seq_afinfo; @@ -3032,8 +3042,11 @@ static struct sock *udp_get_next(struct seq_file *seq, struct sock *sk) sk->sk_family != afinfo->family))); if (!sk) { - if (state->bucket <= afinfo->udp_table->mask) - spin_unlock_bh(&afinfo->udp_table->hash[state->bucket].lock); + udptable = udp_get_table_afinfo(afinfo, net); + + if (state->bucket <= udptable->mask) + spin_unlock_bh(&udptable->hash[state->bucket].lock); + return udp_get_first(seq, state->bucket + 1); } return sk; @@ -3076,14 +3089,17 @@ void udp_seq_stop(struct seq_file *seq, void *v) { struct udp_iter_state *state = seq->private; struct udp_seq_afinfo *afinfo; + struct udp_table *udptable; if (state->bpf_seq_afinfo) afinfo = state->bpf_seq_afinfo; else afinfo = pde_data(file_inode(seq->file)); - if (state->bucket <= afinfo->udp_table->mask) - spin_unlock_bh(&afinfo->udp_table->hash[state->bucket].lock); + udptable = udp_get_table_afinfo(afinfo, seq_file_net(seq)); + + if (state->bucket <= udptable->mask) + spin_unlock_bh(&udptable->hash[state->bucket].lock); } EXPORT_SYMBOL(udp_seq_stop); @@ -3196,7 +3212,7 @@ EXPORT_SYMBOL(udp_seq_ops); static struct udp_seq_afinfo udp4_seq_afinfo = { .family = AF_INET, - .udp_table = &udp_table, + .udp_table = NULL, }; static int __net_init udp4_proc_init_net(struct net *net) @@ -3316,7 +3332,7 @@ static int bpf_iter_init_udp(void *priv_data, struct bpf_iter_aux_info *aux) return -ENOMEM; afinfo->family = AF_UNSPEC; - afinfo->udp_table = &udp_table; + afinfo->udp_table = NULL; st->bpf_seq_afinfo = afinfo; ret = bpf_iter_init_seq_net(priv_data, aux); if (ret) diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c index bbd6dc398f3b..c3dee1f8d3bd 100644 --- a/net/ipv6/udp.c +++ b/net/ipv6/udp.c @@ -1724,7 +1724,7 @@ EXPORT_SYMBOL(udp6_seq_ops); static struct udp_seq_afinfo udp6_seq_afinfo = { .family = AF_INET6, - .udp_table = &udp_table, + .udp_table = NULL, }; int __net_init udp6_proc_init(struct net *net) From ba6aac1516779dd0ced22c136a2c2c4a9c70cf29 Mon Sep 17 00:00:00 2001 From: Kuniyuki Iwashima Date: Mon, 14 Nov 2022 13:57:56 -0800 Subject: [PATCH 0974/2002] udp: Access &udp_table via net. We will soon introduce an optional per-netns hash table for UDP. This means we cannot use udp_table directly in most places. Instead, access it via net->ipv4.udp_table. The access will be valid only while initialising udp_table itself and creating/destroying each netns. Signed-off-by: Kuniyuki Iwashima Signed-off-by: David S. Miller --- net/core/filter.c | 4 ++-- net/ipv4/udp.c | 23 +++++++++++++---------- net/ipv4/udp_diag.c | 6 +++--- net/ipv4/udp_offload.c | 5 +++-- net/ipv6/udp.c | 19 +++++++++++-------- net/ipv6/udp_offload.c | 5 +++-- 6 files changed, 35 insertions(+), 27 deletions(-) diff --git a/net/core/filter.c b/net/core/filter.c index 6dd2baf5eeb2..da4f697e4fa4 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -6432,7 +6432,7 @@ static struct sock *sk_lookup(struct net *net, struct bpf_sock_tuple *tuple, else sk = __udp4_lib_lookup(net, src4, tuple->ipv4.sport, dst4, tuple->ipv4.dport, - dif, sdif, &udp_table, NULL); + dif, sdif, net->ipv4.udp_table, NULL); #if IS_ENABLED(CONFIG_IPV6) } else { struct in6_addr *src6 = (struct in6_addr *)&tuple->ipv6.saddr; @@ -6448,7 +6448,7 @@ static struct sock *sk_lookup(struct net *net, struct bpf_sock_tuple *tuple, src6, tuple->ipv6.sport, dst6, tuple->ipv6.dport, dif, sdif, - &udp_table, NULL); + net->ipv4.udp_table, NULL); #endif } diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index a1a15eb76304..37e79158d145 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c @@ -472,7 +472,7 @@ static struct sock *udp4_lookup_run_bpf(struct net *net, struct sock *sk, *reuse_sk; bool no_reuseport; - if (udptable != &udp_table) + if (udptable != net->ipv4.udp_table) return NULL; /* only UDP is supported */ no_reuseport = bpf_sk_lookup_run_v4(net, IPPROTO_UDP, saddr, sport, @@ -553,10 +553,11 @@ struct sock *udp4_lib_lookup_skb(const struct sk_buff *skb, __be16 sport, __be16 dport) { const struct iphdr *iph = ip_hdr(skb); + struct net *net = dev_net(skb->dev); - return __udp4_lib_lookup(dev_net(skb->dev), iph->saddr, sport, + return __udp4_lib_lookup(net, iph->saddr, sport, iph->daddr, dport, inet_iif(skb), - inet_sdif(skb), &udp_table, NULL); + inet_sdif(skb), net->ipv4.udp_table, NULL); } /* Must be called under rcu_read_lock(). @@ -569,7 +570,7 @@ struct sock *udp4_lib_lookup(struct net *net, __be32 saddr, __be16 sport, struct sock *sk; sk = __udp4_lib_lookup(net, saddr, sport, daddr, dport, - dif, 0, &udp_table, NULL); + dif, 0, net->ipv4.udp_table, NULL); if (sk && !refcount_inc_not_zero(&sk->sk_refcnt)) sk = NULL; return sk; @@ -807,7 +808,7 @@ out: int udp_err(struct sk_buff *skb, u32 info) { - return __udp4_lib_err(skb, info, &udp_table); + return __udp4_lib_err(skb, info, dev_net(skb->dev)->ipv4.udp_table); } /* @@ -2524,13 +2525,14 @@ static struct sock *__udp4_lib_mcast_demux_lookup(struct net *net, __be16 rmt_port, __be32 rmt_addr, int dif, int sdif) { + struct udp_table *udptable = net->ipv4.udp_table; unsigned short hnum = ntohs(loc_port); struct sock *sk, *result; struct udp_hslot *hslot; unsigned int slot; - slot = udp_hashfn(net, hnum, udp_table.mask); - hslot = &udp_table.hash[slot]; + slot = udp_hashfn(net, hnum, udptable->mask); + hslot = &udptable->hash[slot]; /* Do not bother scanning a too big list */ if (hslot->count > 10) @@ -2558,6 +2560,7 @@ static struct sock *__udp4_lib_demux_lookup(struct net *net, __be16 rmt_port, __be32 rmt_addr, int dif, int sdif) { + struct udp_table *udptable = net->ipv4.udp_table; INET_ADDR_COOKIE(acookie, rmt_addr, loc_addr); unsigned short hnum = ntohs(loc_port); unsigned int hash2, slot2; @@ -2566,8 +2569,8 @@ static struct sock *__udp4_lib_demux_lookup(struct net *net, struct sock *sk; hash2 = ipv4_portaddr_hash(net, loc_addr, hnum); - slot2 = hash2 & udp_table.mask; - hslot2 = &udp_table.hash2[slot2]; + slot2 = hash2 & udptable->mask; + hslot2 = &udptable->hash2[slot2]; ports = INET_COMBINED_PORTS(rmt_port, hnum); udp_portaddr_for_each_entry_rcu(sk, &hslot2->head) { @@ -2649,7 +2652,7 @@ int udp_v4_early_demux(struct sk_buff *skb) int udp_rcv(struct sk_buff *skb) { - return __udp4_lib_rcv(skb, &udp_table, IPPROTO_UDP); + return __udp4_lib_rcv(skb, dev_net(skb->dev)->ipv4.udp_table, IPPROTO_UDP); } void udp_destroy_sock(struct sock *sk) diff --git a/net/ipv4/udp_diag.c b/net/ipv4/udp_diag.c index 1ed8c4d78e5c..de3f2d31f510 100644 --- a/net/ipv4/udp_diag.c +++ b/net/ipv4/udp_diag.c @@ -147,13 +147,13 @@ done: static void udp_diag_dump(struct sk_buff *skb, struct netlink_callback *cb, const struct inet_diag_req_v2 *r) { - udp_dump(&udp_table, skb, cb, r); + udp_dump(sock_net(cb->skb->sk)->ipv4.udp_table, skb, cb, r); } static int udp_diag_dump_one(struct netlink_callback *cb, const struct inet_diag_req_v2 *req) { - return udp_dump_one(&udp_table, cb, req); + return udp_dump_one(sock_net(cb->skb->sk)->ipv4.udp_table, cb, req); } static void udp_diag_get_info(struct sock *sk, struct inet_diag_msg *r, @@ -225,7 +225,7 @@ static int __udp_diag_destroy(struct sk_buff *in_skb, static int udp_diag_destroy(struct sk_buff *in_skb, const struct inet_diag_req_v2 *req) { - return __udp_diag_destroy(in_skb, req, &udp_table); + return __udp_diag_destroy(in_skb, req, sock_net(in_skb->sk)->ipv4.udp_table); } static int udplite_diag_destroy(struct sk_buff *in_skb, diff --git a/net/ipv4/udp_offload.c b/net/ipv4/udp_offload.c index 6d1a4bec2614..aedde65e2268 100644 --- a/net/ipv4/udp_offload.c +++ b/net/ipv4/udp_offload.c @@ -600,10 +600,11 @@ static struct sock *udp4_gro_lookup_skb(struct sk_buff *skb, __be16 sport, __be16 dport) { const struct iphdr *iph = skb_gro_network_header(skb); + struct net *net = dev_net(skb->dev); - return __udp4_lib_lookup(dev_net(skb->dev), iph->saddr, sport, + return __udp4_lib_lookup(net, iph->saddr, sport, iph->daddr, dport, inet_iif(skb), - inet_sdif(skb), &udp_table, NULL); + inet_sdif(skb), net->ipv4.udp_table, NULL); } INDIRECT_CALLABLE_SCOPE diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c index c3dee1f8d3bd..9fb2f33ee3a7 100644 --- a/net/ipv6/udp.c +++ b/net/ipv6/udp.c @@ -217,7 +217,7 @@ static inline struct sock *udp6_lookup_run_bpf(struct net *net, struct sock *sk, *reuse_sk; bool no_reuseport; - if (udptable != &udp_table) + if (udptable != net->ipv4.udp_table) return NULL; /* only UDP is supported */ no_reuseport = bpf_sk_lookup_run_v6(net, IPPROTO_UDP, saddr, sport, @@ -298,10 +298,11 @@ struct sock *udp6_lib_lookup_skb(const struct sk_buff *skb, __be16 sport, __be16 dport) { const struct ipv6hdr *iph = ipv6_hdr(skb); + struct net *net = dev_net(skb->dev); - return __udp6_lib_lookup(dev_net(skb->dev), &iph->saddr, sport, + return __udp6_lib_lookup(net, &iph->saddr, sport, &iph->daddr, dport, inet6_iif(skb), - inet6_sdif(skb), &udp_table, NULL); + inet6_sdif(skb), net->ipv4.udp_table, NULL); } /* Must be called under rcu_read_lock(). @@ -314,7 +315,7 @@ struct sock *udp6_lib_lookup(struct net *net, const struct in6_addr *saddr, __be struct sock *sk; sk = __udp6_lib_lookup(net, saddr, sport, daddr, dport, - dif, 0, &udp_table, NULL); + dif, 0, net->ipv4.udp_table, NULL); if (sk && !refcount_inc_not_zero(&sk->sk_refcnt)) sk = NULL; return sk; @@ -689,7 +690,8 @@ static __inline__ int udpv6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, u8 type, u8 code, int offset, __be32 info) { - return __udp6_lib_err(skb, opt, type, code, offset, info, &udp_table); + return __udp6_lib_err(skb, opt, type, code, offset, info, + dev_net(skb->dev)->ipv4.udp_table); } static int udpv6_queue_rcv_one_skb(struct sock *sk, struct sk_buff *skb) @@ -1063,6 +1065,7 @@ static struct sock *__udp6_lib_demux_lookup(struct net *net, __be16 rmt_port, const struct in6_addr *rmt_addr, int dif, int sdif) { + struct udp_table *udptable = net->ipv4.udp_table; unsigned short hnum = ntohs(loc_port); unsigned int hash2, slot2; struct udp_hslot *hslot2; @@ -1070,8 +1073,8 @@ static struct sock *__udp6_lib_demux_lookup(struct net *net, struct sock *sk; hash2 = ipv6_portaddr_hash(net, loc_addr, hnum); - slot2 = hash2 & udp_table.mask; - hslot2 = &udp_table.hash2[slot2]; + slot2 = hash2 & udptable->mask; + hslot2 = &udptable->hash2[slot2]; ports = INET_COMBINED_PORTS(rmt_port, hnum); udp_portaddr_for_each_entry_rcu(sk, &hslot2->head) { @@ -1127,7 +1130,7 @@ void udp_v6_early_demux(struct sk_buff *skb) INDIRECT_CALLABLE_SCOPE int udpv6_rcv(struct sk_buff *skb) { - return __udp6_lib_rcv(skb, &udp_table, IPPROTO_UDP); + return __udp6_lib_rcv(skb, dev_net(skb->dev)->ipv4.udp_table, IPPROTO_UDP); } /* diff --git a/net/ipv6/udp_offload.c b/net/ipv6/udp_offload.c index 7720d04ed396..e0e10f6bcdc1 100644 --- a/net/ipv6/udp_offload.c +++ b/net/ipv6/udp_offload.c @@ -116,10 +116,11 @@ static struct sock *udp6_gro_lookup_skb(struct sk_buff *skb, __be16 sport, __be16 dport) { const struct ipv6hdr *iph = skb_gro_network_header(skb); + struct net *net = dev_net(skb->dev); - return __udp6_lib_lookup(dev_net(skb->dev), &iph->saddr, sport, + return __udp6_lib_lookup(net, &iph->saddr, sport, &iph->daddr, dport, inet6_iif(skb), - inet6_sdif(skb), &udp_table, NULL); + inet6_sdif(skb), net->ipv4.udp_table, NULL); } INDIRECT_CALLABLE_SCOPE From 9804985bf27f8fbcf0d96c7435b5ad94a2a6ea20 Mon Sep 17 00:00:00 2001 From: Kuniyuki Iwashima Date: Mon, 14 Nov 2022 13:57:57 -0800 Subject: [PATCH 0975/2002] udp: Introduce optional per-netns hash table. The maximum hash table size is 64K due to the nature of the protocol. [0] It's smaller than TCP, and fewer sockets can cause a performance drop. On an EC2 c5.24xlarge instance (192 GiB memory), after running iperf3 in different netns, creating 32Mi sockets without data transfer in the root netns causes regression for the iperf3's connection. uhash_entries sockets length Gbps 64K 1 1 5.69 1Mi 16 5.27 2Mi 32 4.90 4Mi 64 4.09 8Mi 128 2.96 16Mi 256 2.06 32Mi 512 1.12 The per-netns hash table breaks the lengthy lists into shorter ones. It is useful on a multi-tenant system with thousands of netns. With smaller hash tables, we can look up sockets faster, isolate noisy neighbours, and reduce lock contention. The max size of the per-netns table is 64K as well. This is because the possible hash range by udp_hashfn() always fits in 64K within the same netns and we cannot make full use of the whole buckets larger than 64K. /* 0 < num < 64K -> X < hash < X + 64K */ (num + net_hash_mix(net)) & mask; Also, the min size is 128. We use a bitmap to search for an available port in udp_lib_get_port(). To keep the bitmap on the stack and not fire the CONFIG_FRAME_WARN error at build time, we round up the table size to 128. The sysctl usage is the same with TCP: $ dmesg | cut -d ' ' -f 6- | grep "UDP hash" UDP hash table entries: 65536 (order: 9, 2097152 bytes, vmalloc) # sysctl net.ipv4.udp_hash_entries net.ipv4.udp_hash_entries = 65536 # can be changed by uhash_entries # sysctl net.ipv4.udp_child_hash_entries net.ipv4.udp_child_hash_entries = 0 # disabled by default # ip netns add test1 # ip netns exec test1 sysctl net.ipv4.udp_hash_entries net.ipv4.udp_hash_entries = -65536 # share the global table # sysctl -w net.ipv4.udp_child_hash_entries=100 net.ipv4.udp_child_hash_entries = 100 # ip netns add test2 # ip netns exec test2 sysctl net.ipv4.udp_hash_entries net.ipv4.udp_hash_entries = 128 # own a per-netns table with 2^n buckets We could optimise the hash table lookup/iteration further by removing the netns comparison for the per-netns one in the future. Also, we could optimise the sparse udp_hslot layout by putting it in udp_table. [0]: https://lore.kernel.org/netdev/4ACC2815.7010101@gmail.com/ Signed-off-by: Kuniyuki Iwashima Signed-off-by: David S. Miller --- Documentation/networking/ip-sysctl.rst | 27 +++++++ include/linux/udp.h | 2 + include/net/netns/ipv4.h | 2 + net/ipv4/sysctl_net_ipv4.c | 40 ++++++++++ net/ipv4/udp.c | 101 +++++++++++++++++++++++-- 5 files changed, 166 insertions(+), 6 deletions(-) diff --git a/Documentation/networking/ip-sysctl.rst b/Documentation/networking/ip-sysctl.rst index 815efc89ad73..727b25cc7ec4 100644 --- a/Documentation/networking/ip-sysctl.rst +++ b/Documentation/networking/ip-sysctl.rst @@ -1177,6 +1177,33 @@ udp_rmem_min - INTEGER udp_wmem_min - INTEGER UDP does not have tx memory accounting and this tunable has no effect. +udp_hash_entries - INTEGER + Show the number of hash buckets for UDP sockets in the current + networking namespace. + + A negative value means the networking namespace does not own its + hash buckets and shares the initial networking namespace's one. + +udp_child_ehash_entries - INTEGER + Control the number of hash buckets for UDP sockets in the child + networking namespace, which must be set before clone() or unshare(). + + If the value is not 0, the kernel uses a value rounded up to 2^n + as the actual hash bucket size. 0 is a special value, meaning + the child networking namespace will share the initial networking + namespace's hash buckets. + + Note that the child will use the global one in case the kernel + fails to allocate enough memory. In addition, the global hash + buckets are spread over available NUMA nodes, but the allocation + of the child hash table depends on the current process's NUMA + policy, which could result in performance differences. + + Possible values: 0, 2^n (n: 7 (128) - 16 (64K)) + + Default: 0 + + RAW variables ============= diff --git a/include/linux/udp.h b/include/linux/udp.h index dea57aa37df6..a2892e151644 100644 --- a/include/linux/udp.h +++ b/include/linux/udp.h @@ -23,7 +23,9 @@ static inline struct udphdr *udp_hdr(const struct sk_buff *skb) return (struct udphdr *)skb_transport_header(skb); } +#define UDP_HTABLE_SIZE_MIN_PERNET 128 #define UDP_HTABLE_SIZE_MIN (CONFIG_BASE_SMALL ? 128 : 256) +#define UDP_HTABLE_SIZE_MAX 65536 static inline u32 udp_hashfn(const struct net *net, u32 num, u32 mask) { diff --git a/include/net/netns/ipv4.h b/include/net/netns/ipv4.h index e4cc4d3cacc4..db762e35aca9 100644 --- a/include/net/netns/ipv4.h +++ b/include/net/netns/ipv4.h @@ -208,6 +208,8 @@ struct netns_ipv4 { atomic_t dev_addr_genid; + unsigned int sysctl_udp_child_hash_entries; + #ifdef CONFIG_SYSCTL unsigned long *sysctl_local_reserved_ports; int sysctl_ip_prot_sock; diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c index 0af28cedd071..0d0cc4ef2b85 100644 --- a/net/ipv4/sysctl_net_ipv4.c +++ b/net/ipv4/sysctl_net_ipv4.c @@ -40,6 +40,7 @@ static int one_day_secs = 24 * 3600; static u32 fib_multipath_hash_fields_all_mask __maybe_unused = FIB_MULTIPATH_HASH_FIELD_ALL_MASK; static unsigned int tcp_child_ehash_entries_max = 16 * 1024 * 1024; +static unsigned int udp_child_hash_entries_max = UDP_HTABLE_SIZE_MAX; static int tcp_plb_max_rounds = 31; static int tcp_plb_max_cong_thresh = 256; @@ -402,12 +403,36 @@ static int proc_tcp_ehash_entries(struct ctl_table *table, int write, if (!net_eq(net, &init_net) && !hinfo->pernet) tcp_ehash_entries *= -1; + memset(&tbl, 0, sizeof(tbl)); tbl.data = &tcp_ehash_entries; tbl.maxlen = sizeof(int); return proc_dointvec(&tbl, write, buffer, lenp, ppos); } +static int proc_udp_hash_entries(struct ctl_table *table, int write, + void *buffer, size_t *lenp, loff_t *ppos) +{ + struct net *net = container_of(table->data, struct net, + ipv4.sysctl_udp_child_hash_entries); + int udp_hash_entries; + struct ctl_table tbl; + + udp_hash_entries = net->ipv4.udp_table->mask + 1; + + /* A negative number indicates that the child netns + * shares the global udp_table. + */ + if (!net_eq(net, &init_net) && net->ipv4.udp_table == &udp_table) + udp_hash_entries *= -1; + + memset(&tbl, 0, sizeof(tbl)); + tbl.data = &udp_hash_entries; + tbl.maxlen = sizeof(int); + + return proc_dointvec(&tbl, write, buffer, lenp, ppos); +} + #ifdef CONFIG_IP_ROUTE_MULTIPATH static int proc_fib_multipath_hash_policy(struct ctl_table *table, int write, void *buffer, size_t *lenp, @@ -1361,6 +1386,21 @@ static struct ctl_table ipv4_net_table[] = { .extra1 = SYSCTL_ZERO, .extra2 = &tcp_child_ehash_entries_max, }, + { + .procname = "udp_hash_entries", + .data = &init_net.ipv4.sysctl_udp_child_hash_entries, + .mode = 0444, + .proc_handler = proc_udp_hash_entries, + }, + { + .procname = "udp_child_hash_entries", + .data = &init_net.ipv4.sysctl_udp_child_hash_entries, + .maxlen = sizeof(unsigned int), + .mode = 0644, + .proc_handler = proc_douintvec_minmax, + .extra1 = SYSCTL_ZERO, + .extra2 = &udp_child_hash_entries_max, + }, { .procname = "udp_rmem_min", .data = &init_net.ipv4.sysctl_udp_rmem_min, diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index 37e79158d145..1fb7d1ed1cb1 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c @@ -129,7 +129,7 @@ DEFINE_PER_CPU(int, udp_memory_per_cpu_fw_alloc); EXPORT_PER_CPU_SYMBOL_GPL(udp_memory_per_cpu_fw_alloc); #define MAX_UDP_PORTS 65536 -#define PORTS_PER_CHAIN (MAX_UDP_PORTS / UDP_HTABLE_SIZE_MIN) +#define PORTS_PER_CHAIN (MAX_UDP_PORTS / UDP_HTABLE_SIZE_MIN_PERNET) static struct udp_table *udp_get_table_prot(struct sock *sk) { @@ -3277,7 +3277,7 @@ void __init udp_table_init(struct udp_table *table, const char *name) &table->log, &table->mask, UDP_HTABLE_SIZE_MIN, - 64 * 1024); + UDP_HTABLE_SIZE_MAX); table->hash2 = table->hash + (table->mask + 1); for (i = 0; i <= table->mask; i++) { @@ -3302,22 +3302,111 @@ u32 udp_flow_hashrnd(void) } EXPORT_SYMBOL(udp_flow_hashrnd); -static int __net_init udp_sysctl_init(struct net *net) +static void __net_init udp_sysctl_init(struct net *net) { - net->ipv4.udp_table = &udp_table; - net->ipv4.sysctl_udp_rmem_min = PAGE_SIZE; net->ipv4.sysctl_udp_wmem_min = PAGE_SIZE; #ifdef CONFIG_NET_L3_MASTER_DEV net->ipv4.sysctl_udp_l3mdev_accept = 0; #endif +} + +static struct udp_table __net_init *udp_pernet_table_alloc(unsigned int hash_entries) +{ + struct udp_table *udptable; + int i; + + udptable = kmalloc(sizeof(*udptable), GFP_KERNEL); + if (!udptable) + goto out; + + udptable->hash = vmalloc_huge(hash_entries * 2 * sizeof(struct udp_hslot), + GFP_KERNEL_ACCOUNT); + if (!udptable->hash) + goto free_table; + + udptable->hash2 = udptable->hash + hash_entries; + udptable->mask = hash_entries - 1; + udptable->log = ilog2(hash_entries); + + for (i = 0; i < hash_entries; i++) { + INIT_HLIST_HEAD(&udptable->hash[i].head); + udptable->hash[i].count = 0; + spin_lock_init(&udptable->hash[i].lock); + + INIT_HLIST_HEAD(&udptable->hash2[i].head); + udptable->hash2[i].count = 0; + spin_lock_init(&udptable->hash2[i].lock); + } + + return udptable; + +free_table: + kfree(udptable); +out: + return NULL; +} + +static void __net_exit udp_pernet_table_free(struct net *net) +{ + struct udp_table *udptable = net->ipv4.udp_table; + + if (udptable == &udp_table) + return; + + kvfree(udptable->hash); + kfree(udptable); +} + +static void __net_init udp_set_table(struct net *net) +{ + struct udp_table *udptable; + unsigned int hash_entries; + struct net *old_net; + + if (net_eq(net, &init_net)) + goto fallback; + + old_net = current->nsproxy->net_ns; + hash_entries = READ_ONCE(old_net->ipv4.sysctl_udp_child_hash_entries); + if (!hash_entries) + goto fallback; + + /* Set min to keep the bitmap on stack in udp_lib_get_port() */ + if (hash_entries < UDP_HTABLE_SIZE_MIN_PERNET) + hash_entries = UDP_HTABLE_SIZE_MIN_PERNET; + else + hash_entries = roundup_pow_of_two(hash_entries); + + udptable = udp_pernet_table_alloc(hash_entries); + if (udptable) { + net->ipv4.udp_table = udptable; + } else { + pr_warn("Failed to allocate UDP hash table (entries: %u) " + "for a netns, fallback to the global one\n", + hash_entries); +fallback: + net->ipv4.udp_table = &udp_table; + } +} + +static int __net_init udp_pernet_init(struct net *net) +{ + udp_sysctl_init(net); + udp_set_table(net); return 0; } +static void __net_exit udp_pernet_exit(struct net *net) +{ + udp_pernet_table_free(net); +} + static struct pernet_operations __net_initdata udp_sysctl_ops = { - .init = udp_sysctl_init, + .init = udp_pernet_init, + .exit = udp_pernet_exit, }; #if defined(CONFIG_BPF_SYSCALL) && defined(CONFIG_PROC_FS) From 8c55facecd7ade835287298ce325f930d888d8ec Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Mon, 14 Nov 2022 16:42:56 +0200 Subject: [PATCH 0976/2002] net: linkwatch: only report IF_OPER_LOWERLAYERDOWN if iflink is actually down RFC 2863 says: The lowerLayerDown state is also a refinement on the down state. This new state indicates that this interface runs "on top of" one or more other interfaces (see ifStackTable) and that this interface is down specifically because one or more of these lower-layer interfaces are down. DSA interfaces are virtual network devices, stacked on top of the DSA master, but they have a physical MAC, with a PHY that reports a real link status. But since DSA (perhaps improperly) uses an iflink to describe the relationship to its master since commit c084080151e1 ("dsa: set ->iflink on slave interfaces to the ifindex of the parent"), default_operstate() will misinterpret this to mean that every time the carrier of a DSA interface is not ok, it is because of the master being not ok. In fact, since commit c0a8a9c27493 ("net: dsa: automatically bring user ports down when master goes down"), DSA cannot even in theory be in the lowerLayerDown state, because it just calls dev_close_many(), thereby going down, when the master goes down. We could revert the commit that creates an iflink between a DSA user port and its master, especially since now we have an alternative IFLA_DSA_MASTER which has less side effects. But there may be tooling in use which relies on the iflink, which has existed since 2009. We could also probably do something local within DSA to overwrite what rfc2863_policy() did, in a way similar to hsr_set_operstate(), but this seems like a hack. What seems appropriate is to follow the iflink, and check the carrier status of that interface as well. If that's down too, yes, keep reporting lowerLayerDown, otherwise just down. Signed-off-by: Vladimir Oltean Signed-off-by: David S. Miller --- net/core/link_watch.c | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/net/core/link_watch.c b/net/core/link_watch.c index aa6cb1f90966..c469d1c4db5d 100644 --- a/net/core/link_watch.c +++ b/net/core/link_watch.c @@ -38,9 +38,23 @@ static unsigned char default_operstate(const struct net_device *dev) if (netif_testing(dev)) return IF_OPER_TESTING; - if (!netif_carrier_ok(dev)) - return (dev->ifindex != dev_get_iflink(dev) ? - IF_OPER_LOWERLAYERDOWN : IF_OPER_DOWN); + /* Some uppers (DSA) have additional sources for being down, so + * first check whether lower is indeed the source of its down state. + */ + if (!netif_carrier_ok(dev)) { + int iflink = dev_get_iflink(dev); + struct net_device *peer; + + if (iflink == dev->ifindex) + return IF_OPER_DOWN; + + peer = __dev_get_by_index(dev_net(dev), iflink); + if (!peer) + return IF_OPER_DOWN; + + return netif_carrier_ok(peer) ? IF_OPER_DOWN : + IF_OPER_LOWERLAYERDOWN; + } if (netif_dormant(dev)) return IF_OPER_DORMANT; From 57fc05e8e82d015d5d58572e146ac8579a66efea Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Tue, 15 Nov 2022 09:10:56 +0000 Subject: [PATCH 0977/2002] net: mm_account_pinned_pages() optimization Adopt atomic_long_try_cmpxchg() in mm_account_pinned_pages() as it is slightly more efficient. Signed-off-by: Eric Dumazet Cc: Willem de Bruijn Signed-off-by: David S. Miller --- net/core/skbuff.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/net/core/skbuff.c b/net/core/skbuff.c index 90d085290d49..4bf95e36ed16 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -1267,13 +1267,12 @@ int mm_account_pinned_pages(struct mmpin *mmp, size_t size) max_pg = rlimit(RLIMIT_MEMLOCK) >> PAGE_SHIFT; user = mmp->user ? : current_user(); + old_pg = atomic_long_read(&user->locked_vm); do { - old_pg = atomic_long_read(&user->locked_vm); new_pg = old_pg + num_pg; if (new_pg > max_pg) return -ENOBUFS; - } while (atomic_long_cmpxchg(&user->locked_vm, old_pg, new_pg) != - old_pg); + } while (!atomic_long_try_cmpxchg(&user->locked_vm, &old_pg, new_pg)); if (!mmp->user) { mmp->user = get_uid(user); From 30189806fbb945c4cb753878aab0aa5af07bc921 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Tue, 15 Nov 2022 09:10:57 +0000 Subject: [PATCH 0978/2002] ipv6: fib6_new_sernum() optimization Adopt atomic_try_cmpxchg() which is slightly more efficient. Signed-off-by: Eric Dumazet Cc: David Ahern Signed-off-by: David S. Miller --- net/ipv6/ip6_fib.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/net/ipv6/ip6_fib.c b/net/ipv6/ip6_fib.c index 413f66781e50..2438da5ff6da 100644 --- a/net/ipv6/ip6_fib.c +++ b/net/ipv6/ip6_fib.c @@ -91,13 +91,12 @@ static void fib6_walker_unlink(struct net *net, struct fib6_walker *w) static int fib6_new_sernum(struct net *net) { - int new, old; + int new, old = atomic_read(&net->ipv6.fib6_sernum); do { - old = atomic_read(&net->ipv6.fib6_sernum); new = old < INT_MAX ? old + 1 : 1; - } while (atomic_cmpxchg(&net->ipv6.fib6_sernum, - old, new) != old); + } while (!atomic_try_cmpxchg(&net->ipv6.fib6_sernum, &old, new)); + return new; } From 6af645a5b2da7898b2c37e8f39f1bc572c4ab85a Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Tue, 15 Nov 2022 09:10:58 +0000 Subject: [PATCH 0979/2002] net: net_{enable|disable}_timestamp() optimizations Adopting atomic_try_cmpxchg() makes the code cleaner. Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- net/core/dev.c | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/net/core/dev.c b/net/core/dev.c index 117e830cabb0..10b56648a9d4 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -2073,13 +2073,10 @@ static DECLARE_WORK(netstamp_work, netstamp_clear); void net_enable_timestamp(void) { #ifdef CONFIG_JUMP_LABEL - int wanted; + int wanted = atomic_read(&netstamp_wanted); - while (1) { - wanted = atomic_read(&netstamp_wanted); - if (wanted <= 0) - break; - if (atomic_cmpxchg(&netstamp_wanted, wanted, wanted + 1) == wanted) + while (wanted > 0) { + if (atomic_try_cmpxchg(&netstamp_wanted, &wanted, wanted + 1)) return; } atomic_inc(&netstamp_needed_deferred); @@ -2093,13 +2090,10 @@ EXPORT_SYMBOL(net_enable_timestamp); void net_disable_timestamp(void) { #ifdef CONFIG_JUMP_LABEL - int wanted; + int wanted = atomic_read(&netstamp_wanted); - while (1) { - wanted = atomic_read(&netstamp_wanted); - if (wanted <= 1) - break; - if (atomic_cmpxchg(&netstamp_wanted, wanted, wanted - 1) == wanted) + while (wanted > 1) { + if (atomic_try_cmpxchg(&netstamp_wanted, &wanted, wanted - 1)) return; } atomic_dec(&netstamp_needed_deferred); From 1462160c7455cfc495693e47d0eabb371ec4f7ff Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Tue, 15 Nov 2022 09:10:59 +0000 Subject: [PATCH 0980/2002] net: adopt try_cmpxchg() in napi_schedule_prep() and napi_complete_done() This makes the code slightly more efficient. Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- net/core/dev.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/net/core/dev.c b/net/core/dev.c index 10b56648a9d4..0c12c7ad04f2 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -5979,10 +5979,9 @@ EXPORT_SYMBOL(__napi_schedule); */ bool napi_schedule_prep(struct napi_struct *n) { - unsigned long val, new; + unsigned long new, val = READ_ONCE(n->state); do { - val = READ_ONCE(n->state); if (unlikely(val & NAPIF_STATE_DISABLE)) return false; new = val | NAPIF_STATE_SCHED; @@ -5995,7 +5994,7 @@ bool napi_schedule_prep(struct napi_struct *n) */ new |= (val & NAPIF_STATE_SCHED) / NAPIF_STATE_SCHED * NAPIF_STATE_MISSED; - } while (cmpxchg(&n->state, val, new) != val); + } while (!try_cmpxchg(&n->state, &val, new)); return !(val & NAPIF_STATE_SCHED); } @@ -6063,9 +6062,8 @@ bool napi_complete_done(struct napi_struct *n, int work_done) local_irq_restore(flags); } + val = READ_ONCE(n->state); do { - val = READ_ONCE(n->state); - WARN_ON_ONCE(!(val & NAPIF_STATE_SCHED)); new = val & ~(NAPIF_STATE_MISSED | NAPIF_STATE_SCHED | @@ -6078,7 +6076,7 @@ bool napi_complete_done(struct napi_struct *n, int work_done) */ new |= (val & NAPIF_STATE_MISSED) / NAPIF_STATE_MISSED * NAPIF_STATE_SCHED; - } while (cmpxchg(&n->state, val, new) != val); + } while (!try_cmpxchg(&n->state, &val, new)); if (unlikely(val & NAPIF_STATE_MISSED)) { __napi_schedule(n); From 4ffa1d1c6842a97e84cfbe56bfcf70edb23608e2 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Tue, 15 Nov 2022 09:11:00 +0000 Subject: [PATCH 0981/2002] net: adopt try_cmpxchg() in napi_{enable|disable}() This makes code a bit cleaner. Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- net/core/dev.c | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/net/core/dev.c b/net/core/dev.c index 0c12c7ad04f2..fb943dad9651 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -6397,8 +6397,8 @@ void napi_disable(struct napi_struct *n) might_sleep(); set_bit(NAPI_STATE_DISABLE, &n->state); - for ( ; ; ) { - val = READ_ONCE(n->state); + val = READ_ONCE(n->state); + do { if (val & (NAPIF_STATE_SCHED | NAPIF_STATE_NPSVC)) { usleep_range(20, 200); continue; @@ -6406,10 +6406,7 @@ void napi_disable(struct napi_struct *n) new = val | NAPIF_STATE_SCHED | NAPIF_STATE_NPSVC; new &= ~(NAPIF_STATE_THREADED | NAPIF_STATE_PREFER_BUSY_POLL); - - if (cmpxchg(&n->state, val, new) == val) - break; - } + } while (!try_cmpxchg(&n->state, &val, new)); hrtimer_cancel(&n->timer); @@ -6426,16 +6423,15 @@ EXPORT_SYMBOL(napi_disable); */ void napi_enable(struct napi_struct *n) { - unsigned long val, new; + unsigned long new, val = READ_ONCE(n->state); do { - val = READ_ONCE(n->state); BUG_ON(!test_bit(NAPI_STATE_SCHED, &val)); new = val & ~(NAPIF_STATE_SCHED | NAPIF_STATE_NPSVC); if (n->dev->threaded && n->thread) new |= NAPIF_STATE_THREADED; - } while (cmpxchg(&n->state, val, new) != val); + } while (!try_cmpxchg(&n->state, &val, new)); } EXPORT_SYMBOL(napi_enable); From 4ebf802cf1c6a87fd14e7936728c99e18d7ba794 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Tue, 15 Nov 2022 09:11:01 +0000 Subject: [PATCH 0982/2002] net: __sock_gen_cookie() cleanup Adopt atomic64_try_cmpxchg() and remove the loop, to make the intent more obvious. Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- net/core/sock_diag.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/net/core/sock_diag.c b/net/core/sock_diag.c index f7cf74cdd3db..b11593cae5a0 100644 --- a/net/core/sock_diag.c +++ b/net/core/sock_diag.c @@ -25,14 +25,14 @@ DEFINE_COOKIE(sock_cookie); u64 __sock_gen_cookie(struct sock *sk) { - while (1) { - u64 res = atomic64_read(&sk->sk_cookie); + u64 res = atomic64_read(&sk->sk_cookie); - if (res) - return res; - res = gen_cookie_next(&sock_cookie); - atomic64_cmpxchg(&sk->sk_cookie, 0, res); + if (!res) { + u64 new = gen_cookie_next(&sock_cookie); + + atomic64_try_cmpxchg(&sk->sk_cookie, &res, new); } + return res; } int sock_diag_check_cookie(struct sock *sk, const __u32 *cookie) From 6c1c5097781f563b70a81683ea6fdac21637573b Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Tue, 15 Nov 2022 08:53:55 +0000 Subject: [PATCH 0983/2002] net: add atomic_long_t to net_device_stats fields Long standing KCSAN issues are caused by data-race around some dev->stats changes. Most performance critical paths already use per-cpu variables, or per-queue ones. It is reasonable (and more correct) to use atomic operations for the slow paths. This patch adds an union for each field of net_device_stats, so that we can convert paths that are not yet protected by a spinlock or a mutex. netdev_stats_to_stats64() no longer has an #if BITS_PER_LONG==64 Note that the memcpy() we were using on 64bit arches had no provision to avoid load-tearing, while atomic_long_read() is providing the needed protection at no cost. Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- include/linux/netdevice.h | 58 +++++++++++++++++++++++---------------- include/net/dst.h | 5 ++-- net/core/dev.c | 14 ++-------- 3 files changed, 40 insertions(+), 37 deletions(-) diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 02a2318da7c7..23b3903b0678 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -171,31 +171,38 @@ static inline bool dev_xmit_complete(int rc) * (unsigned long) so they can be read and written atomically. */ +#define NET_DEV_STAT(FIELD) \ + union { \ + unsigned long FIELD; \ + atomic_long_t __##FIELD; \ + } + struct net_device_stats { - unsigned long rx_packets; - unsigned long tx_packets; - unsigned long rx_bytes; - unsigned long tx_bytes; - unsigned long rx_errors; - unsigned long tx_errors; - unsigned long rx_dropped; - unsigned long tx_dropped; - unsigned long multicast; - unsigned long collisions; - unsigned long rx_length_errors; - unsigned long rx_over_errors; - unsigned long rx_crc_errors; - unsigned long rx_frame_errors; - unsigned long rx_fifo_errors; - unsigned long rx_missed_errors; - unsigned long tx_aborted_errors; - unsigned long tx_carrier_errors; - unsigned long tx_fifo_errors; - unsigned long tx_heartbeat_errors; - unsigned long tx_window_errors; - unsigned long rx_compressed; - unsigned long tx_compressed; + NET_DEV_STAT(rx_packets); + NET_DEV_STAT(tx_packets); + NET_DEV_STAT(rx_bytes); + NET_DEV_STAT(tx_bytes); + NET_DEV_STAT(rx_errors); + NET_DEV_STAT(tx_errors); + NET_DEV_STAT(rx_dropped); + NET_DEV_STAT(tx_dropped); + NET_DEV_STAT(multicast); + NET_DEV_STAT(collisions); + NET_DEV_STAT(rx_length_errors); + NET_DEV_STAT(rx_over_errors); + NET_DEV_STAT(rx_crc_errors); + NET_DEV_STAT(rx_frame_errors); + NET_DEV_STAT(rx_fifo_errors); + NET_DEV_STAT(rx_missed_errors); + NET_DEV_STAT(tx_aborted_errors); + NET_DEV_STAT(tx_carrier_errors); + NET_DEV_STAT(tx_fifo_errors); + NET_DEV_STAT(tx_heartbeat_errors); + NET_DEV_STAT(tx_window_errors); + NET_DEV_STAT(rx_compressed); + NET_DEV_STAT(tx_compressed); }; +#undef NET_DEV_STAT /* per-cpu stats, allocated on demand. * Try to fit them in a single cache line, for dev_get_stats() sake. @@ -5171,4 +5178,9 @@ extern struct list_head ptype_base[PTYPE_HASH_SIZE] __read_mostly; extern struct net_device *blackhole_netdev; +/* Note: Avoid these macros in fast path, prefer per-cpu or per-queue counters. */ +#define DEV_STATS_INC(DEV, FIELD) atomic_long_inc(&(DEV)->stats.__##FIELD) +#define DEV_STATS_ADD(DEV, FIELD, VAL) \ + atomic_long_add((VAL), &(DEV)->stats.__##FIELD) + #endif /* _LINUX_NETDEVICE_H */ diff --git a/include/net/dst.h b/include/net/dst.h index 00b479ce6b99..d67fda89cd0f 100644 --- a/include/net/dst.h +++ b/include/net/dst.h @@ -356,9 +356,8 @@ static inline void __skb_tunnel_rx(struct sk_buff *skb, struct net_device *dev, static inline void skb_tunnel_rx(struct sk_buff *skb, struct net_device *dev, struct net *net) { - /* TODO : stats should be SMP safe */ - dev->stats.rx_packets++; - dev->stats.rx_bytes += skb->len; + DEV_STATS_INC(dev, rx_packets); + DEV_STATS_ADD(dev, rx_bytes, skb->len); __skb_tunnel_rx(skb, dev, net); } diff --git a/net/core/dev.c b/net/core/dev.c index fb943dad9651..d0fb4af9a126 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -10369,24 +10369,16 @@ void netdev_run_todo(void) void netdev_stats_to_stats64(struct rtnl_link_stats64 *stats64, const struct net_device_stats *netdev_stats) { -#if BITS_PER_LONG == 64 - BUILD_BUG_ON(sizeof(*stats64) < sizeof(*netdev_stats)); - memcpy(stats64, netdev_stats, sizeof(*netdev_stats)); - /* zero out counters that only exist in rtnl_link_stats64 */ - memset((char *)stats64 + sizeof(*netdev_stats), 0, - sizeof(*stats64) - sizeof(*netdev_stats)); -#else - size_t i, n = sizeof(*netdev_stats) / sizeof(unsigned long); - const unsigned long *src = (const unsigned long *)netdev_stats; + size_t i, n = sizeof(*netdev_stats) / sizeof(atomic_long_t); + const atomic_long_t *src = (atomic_long_t *)netdev_stats; u64 *dst = (u64 *)stats64; BUILD_BUG_ON(n > sizeof(*stats64) / sizeof(u64)); for (i = 0; i < n; i++) - dst[i] = src[i]; + dst[i] = atomic_long_read(&src[i]); /* zero out counters that only exist in rtnl_link_stats64 */ memset((char *)stats64 + n * sizeof(u64), 0, sizeof(*stats64) - n * sizeof(u64)); -#endif } EXPORT_SYMBOL(netdev_stats_to_stats64); From cb34b7cf17ecf33499c9298943f85af247abc1e9 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Tue, 15 Nov 2022 08:53:56 +0000 Subject: [PATCH 0984/2002] ipv6/sit: use DEV_STATS_INC() to avoid data-races syzbot/KCSAN reported that multiple cpus are updating dev->stats.tx_error concurrently. This is because sit tunnels are NETIF_F_LLTX, meaning their ndo_start_xmit() is not protected by a spinlock. While original KCSAN report was about tx path, rx path has the same issue. Reported-by: syzbot Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- net/ipv6/sit.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/net/ipv6/sit.c b/net/ipv6/sit.c index 5703d3cbea9b..70d81bba5093 100644 --- a/net/ipv6/sit.c +++ b/net/ipv6/sit.c @@ -694,7 +694,7 @@ static int ipip6_rcv(struct sk_buff *skb) skb->dev = tunnel->dev; if (packet_is_spoofed(skb, iph, tunnel)) { - tunnel->dev->stats.rx_errors++; + DEV_STATS_INC(tunnel->dev, rx_errors); goto out; } @@ -714,8 +714,8 @@ static int ipip6_rcv(struct sk_buff *skb) net_info_ratelimited("non-ECT from %pI4 with TOS=%#x\n", &iph->saddr, iph->tos); if (err > 1) { - ++tunnel->dev->stats.rx_frame_errors; - ++tunnel->dev->stats.rx_errors; + DEV_STATS_INC(tunnel->dev, rx_frame_errors); + DEV_STATS_INC(tunnel->dev, rx_errors); goto out; } } @@ -942,7 +942,7 @@ static netdev_tx_t ipip6_tunnel_xmit(struct sk_buff *skb, if (!rt) { rt = ip_route_output_flow(tunnel->net, &fl4, NULL); if (IS_ERR(rt)) { - dev->stats.tx_carrier_errors++; + DEV_STATS_INC(dev, tx_carrier_errors); goto tx_error_icmp; } dst_cache_set_ip4(&tunnel->dst_cache, &rt->dst, fl4.saddr); @@ -950,14 +950,14 @@ static netdev_tx_t ipip6_tunnel_xmit(struct sk_buff *skb, if (rt->rt_type != RTN_UNICAST && rt->rt_type != RTN_LOCAL) { ip_rt_put(rt); - dev->stats.tx_carrier_errors++; + DEV_STATS_INC(dev, tx_carrier_errors); goto tx_error_icmp; } tdev = rt->dst.dev; if (tdev == dev) { ip_rt_put(rt); - dev->stats.collisions++; + DEV_STATS_INC(dev, collisions); goto tx_error; } @@ -970,7 +970,7 @@ static netdev_tx_t ipip6_tunnel_xmit(struct sk_buff *skb, mtu = dst_mtu(&rt->dst) - t_hlen; if (mtu < IPV4_MIN_MTU) { - dev->stats.collisions++; + DEV_STATS_INC(dev, collisions); ip_rt_put(rt); goto tx_error; } @@ -1009,7 +1009,7 @@ static netdev_tx_t ipip6_tunnel_xmit(struct sk_buff *skb, struct sk_buff *new_skb = skb_realloc_headroom(skb, max_headroom); if (!new_skb) { ip_rt_put(rt); - dev->stats.tx_dropped++; + DEV_STATS_INC(dev, tx_dropped); kfree_skb(skb); return NETDEV_TX_OK; } @@ -1039,7 +1039,7 @@ tx_error_icmp: dst_link_failure(skb); tx_error: kfree_skb(skb); - dev->stats.tx_errors++; + DEV_STATS_INC(dev, tx_errors); return NETDEV_TX_OK; } @@ -1058,7 +1058,7 @@ static netdev_tx_t sit_tunnel_xmit__(struct sk_buff *skb, return NETDEV_TX_OK; tx_error: kfree_skb(skb); - dev->stats.tx_errors++; + DEV_STATS_INC(dev, tx_errors); return NETDEV_TX_OK; } @@ -1087,7 +1087,7 @@ static netdev_tx_t sit_tunnel_xmit(struct sk_buff *skb, return NETDEV_TX_OK; tx_err: - dev->stats.tx_errors++; + DEV_STATS_INC(dev, tx_errors); kfree_skb(skb); return NETDEV_TX_OK; From 2fad1ba354d4a82b1b635a78f25d9682d4febb5e Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Tue, 15 Nov 2022 08:53:57 +0000 Subject: [PATCH 0985/2002] ipv6: tunnels: use DEV_STATS_INC() Most of code paths in tunnels are lockless (eg NETIF_F_LLTX in tx). Adopt SMP safe DEV_STATS_{INC|ADD}() to update dev->stats fields. Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- net/ipv6/ip6_gre.c | 11 ++++------- net/ipv6/ip6_tunnel.c | 26 ++++++++++++-------------- net/ipv6/ip6_vti.c | 16 +++++++--------- net/ipv6/ip6mr.c | 10 +++++----- 4 files changed, 28 insertions(+), 35 deletions(-) diff --git a/net/ipv6/ip6_gre.c b/net/ipv6/ip6_gre.c index 7673e1dd1147..89f5f0f3f5d6 100644 --- a/net/ipv6/ip6_gre.c +++ b/net/ipv6/ip6_gre.c @@ -895,7 +895,6 @@ static netdev_tx_t ip6gre_tunnel_xmit(struct sk_buff *skb, struct net_device *dev) { struct ip6_tnl *t = netdev_priv(dev); - struct net_device_stats *stats = &t->dev->stats; __be16 payload_protocol; int ret; @@ -925,8 +924,8 @@ static netdev_tx_t ip6gre_tunnel_xmit(struct sk_buff *skb, tx_err: if (!t->parms.collect_md || !IS_ERR(skb_tunnel_info_txcheck(skb))) - stats->tx_errors++; - stats->tx_dropped++; + DEV_STATS_INC(dev, tx_errors); + DEV_STATS_INC(dev, tx_dropped); kfree_skb(skb); return NETDEV_TX_OK; } @@ -937,7 +936,6 @@ static netdev_tx_t ip6erspan_tunnel_xmit(struct sk_buff *skb, struct ip_tunnel_info *tun_info = NULL; struct ip6_tnl *t = netdev_priv(dev); struct dst_entry *dst = skb_dst(skb); - struct net_device_stats *stats; bool truncate = false; int encap_limit = -1; __u8 dsfield = false; @@ -1086,10 +1084,9 @@ static netdev_tx_t ip6erspan_tunnel_xmit(struct sk_buff *skb, return NETDEV_TX_OK; tx_err: - stats = &t->dev->stats; if (!IS_ERR(tun_info)) - stats->tx_errors++; - stats->tx_dropped++; + DEV_STATS_INC(dev, tx_errors); + DEV_STATS_INC(dev, tx_dropped); kfree_skb(skb); return NETDEV_TX_OK; } diff --git a/net/ipv6/ip6_tunnel.c b/net/ipv6/ip6_tunnel.c index 2fb4c6ad7243..47b6607a1370 100644 --- a/net/ipv6/ip6_tunnel.c +++ b/net/ipv6/ip6_tunnel.c @@ -803,8 +803,8 @@ static int __ip6_tnl_rcv(struct ip6_tnl *tunnel, struct sk_buff *skb, (tunnel->parms.i_flags & TUNNEL_CSUM)) || ((tpi->flags & TUNNEL_CSUM) && !(tunnel->parms.i_flags & TUNNEL_CSUM))) { - tunnel->dev->stats.rx_crc_errors++; - tunnel->dev->stats.rx_errors++; + DEV_STATS_INC(tunnel->dev, rx_crc_errors); + DEV_STATS_INC(tunnel->dev, rx_errors); goto drop; } @@ -812,8 +812,8 @@ static int __ip6_tnl_rcv(struct ip6_tnl *tunnel, struct sk_buff *skb, if (!(tpi->flags & TUNNEL_SEQ) || (tunnel->i_seqno && (s32)(ntohl(tpi->seq) - tunnel->i_seqno) < 0)) { - tunnel->dev->stats.rx_fifo_errors++; - tunnel->dev->stats.rx_errors++; + DEV_STATS_INC(tunnel->dev, rx_fifo_errors); + DEV_STATS_INC(tunnel->dev, rx_errors); goto drop; } tunnel->i_seqno = ntohl(tpi->seq) + 1; @@ -824,8 +824,8 @@ static int __ip6_tnl_rcv(struct ip6_tnl *tunnel, struct sk_buff *skb, /* Warning: All skb pointers will be invalidated! */ if (tunnel->dev->type == ARPHRD_ETHER) { if (!pskb_may_pull(skb, ETH_HLEN)) { - tunnel->dev->stats.rx_length_errors++; - tunnel->dev->stats.rx_errors++; + DEV_STATS_INC(tunnel->dev, rx_length_errors); + DEV_STATS_INC(tunnel->dev, rx_errors); goto drop; } @@ -849,8 +849,8 @@ static int __ip6_tnl_rcv(struct ip6_tnl *tunnel, struct sk_buff *skb, &ipv6h->saddr, ipv6_get_dsfield(ipv6h)); if (err > 1) { - ++tunnel->dev->stats.rx_frame_errors; - ++tunnel->dev->stats.rx_errors; + DEV_STATS_INC(tunnel->dev, rx_frame_errors); + DEV_STATS_INC(tunnel->dev, rx_errors); goto drop; } } @@ -1071,7 +1071,6 @@ int ip6_tnl_xmit(struct sk_buff *skb, struct net_device *dev, __u8 dsfield, { struct ip6_tnl *t = netdev_priv(dev); struct net *net = t->net; - struct net_device_stats *stats = &t->dev->stats; struct ipv6hdr *ipv6h; struct ipv6_tel_txoption opt; struct dst_entry *dst = NULL, *ndst = NULL; @@ -1166,7 +1165,7 @@ route_lookup: tdev = dst->dev; if (tdev == dev) { - stats->collisions++; + DEV_STATS_INC(dev, collisions); net_warn_ratelimited("%s: Local routing loop detected!\n", t->parms.name); goto tx_err_dst_release; @@ -1265,7 +1264,7 @@ route_lookup: ip6tunnel_xmit(NULL, skb, dev); return 0; tx_err_link_failure: - stats->tx_carrier_errors++; + DEV_STATS_INC(dev, tx_carrier_errors); dst_link_failure(skb); tx_err_dst_release: dst_release(dst); @@ -1408,7 +1407,6 @@ static netdev_tx_t ip6_tnl_start_xmit(struct sk_buff *skb, struct net_device *dev) { struct ip6_tnl *t = netdev_priv(dev); - struct net_device_stats *stats = &t->dev->stats; u8 ipproto; int ret; @@ -1438,8 +1436,8 @@ ip6_tnl_start_xmit(struct sk_buff *skb, struct net_device *dev) return NETDEV_TX_OK; tx_err: - stats->tx_errors++; - stats->tx_dropped++; + DEV_STATS_INC(dev, tx_errors); + DEV_STATS_INC(dev, tx_dropped); kfree_skb(skb); return NETDEV_TX_OK; } diff --git a/net/ipv6/ip6_vti.c b/net/ipv6/ip6_vti.c index 151337d7f67b..10b222865d46 100644 --- a/net/ipv6/ip6_vti.c +++ b/net/ipv6/ip6_vti.c @@ -317,7 +317,7 @@ static int vti6_input_proto(struct sk_buff *skb, int nexthdr, __be32 spi, ipv6h = ipv6_hdr(skb); if (!ip6_tnl_rcv_ctl(t, &ipv6h->daddr, &ipv6h->saddr)) { - t->dev->stats.rx_dropped++; + DEV_STATS_INC(t->dev, rx_dropped); rcu_read_unlock(); goto discard; } @@ -359,8 +359,8 @@ static int vti6_rcv_cb(struct sk_buff *skb, int err) dev = t->dev; if (err) { - dev->stats.rx_errors++; - dev->stats.rx_dropped++; + DEV_STATS_INC(dev, rx_errors); + DEV_STATS_INC(dev, rx_dropped); return 0; } @@ -446,7 +446,6 @@ static int vti6_xmit(struct sk_buff *skb, struct net_device *dev, struct flowi *fl) { struct ip6_tnl *t = netdev_priv(dev); - struct net_device_stats *stats = &t->dev->stats; struct dst_entry *dst = skb_dst(skb); struct net_device *tdev; struct xfrm_state *x; @@ -506,7 +505,7 @@ vti6_xmit(struct sk_buff *skb, struct net_device *dev, struct flowi *fl) tdev = dst->dev; if (tdev == dev) { - stats->collisions++; + DEV_STATS_INC(dev, collisions); net_warn_ratelimited("%s: Local routing loop detected!\n", t->parms.name); goto tx_err_dst_release; @@ -544,7 +543,7 @@ xmit: return 0; tx_err_link_failure: - stats->tx_carrier_errors++; + DEV_STATS_INC(dev, tx_carrier_errors); dst_link_failure(skb); tx_err_dst_release: dst_release(dst); @@ -555,7 +554,6 @@ static netdev_tx_t vti6_tnl_xmit(struct sk_buff *skb, struct net_device *dev) { struct ip6_tnl *t = netdev_priv(dev); - struct net_device_stats *stats = &t->dev->stats; struct flowi fl; int ret; @@ -591,8 +589,8 @@ vti6_tnl_xmit(struct sk_buff *skb, struct net_device *dev) return NETDEV_TX_OK; tx_err: - stats->tx_errors++; - stats->tx_dropped++; + DEV_STATS_INC(dev, tx_errors); + DEV_STATS_INC(dev, tx_dropped); kfree_skb(skb); return NETDEV_TX_OK; } diff --git a/net/ipv6/ip6mr.c b/net/ipv6/ip6mr.c index facdc78a43e5..23e766597f36 100644 --- a/net/ipv6/ip6mr.c +++ b/net/ipv6/ip6mr.c @@ -608,8 +608,8 @@ static netdev_tx_t reg_vif_xmit(struct sk_buff *skb, if (ip6mr_fib_lookup(net, &fl6, &mrt) < 0) goto tx_err; - dev->stats.tx_bytes += skb->len; - dev->stats.tx_packets++; + DEV_STATS_ADD(dev, tx_bytes, skb->len); + DEV_STATS_INC(dev, tx_packets); rcu_read_lock(); ip6mr_cache_report(mrt, skb, READ_ONCE(mrt->mroute_reg_vif_num), MRT6MSG_WHOLEPKT); @@ -618,7 +618,7 @@ static netdev_tx_t reg_vif_xmit(struct sk_buff *skb, return NETDEV_TX_OK; tx_err: - dev->stats.tx_errors++; + DEV_STATS_INC(dev, tx_errors); kfree_skb(skb); return NETDEV_TX_OK; } @@ -2044,8 +2044,8 @@ static int ip6mr_forward2(struct net *net, struct mr_table *mrt, if (vif->flags & MIFF_REGISTER) { WRITE_ONCE(vif->pkt_out, vif->pkt_out + 1); WRITE_ONCE(vif->bytes_out, vif->bytes_out + skb->len); - vif_dev->stats.tx_bytes += skb->len; - vif_dev->stats.tx_packets++; + DEV_STATS_ADD(vif_dev, tx_bytes, skb->len); + DEV_STATS_INC(vif_dev, tx_packets); ip6mr_cache_report(mrt, skb, vifi, MRT6MSG_WHOLEPKT); goto out_free; } From c4794d22251b979b12a6c8e2d3848b662a44fdb6 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Tue, 15 Nov 2022 08:53:58 +0000 Subject: [PATCH 0986/2002] ipv4: tunnels: use DEV_STATS_INC() Most of code paths in tunnels are lockless (eg NETIF_F_LLTX in tx). Adopt SMP safe DEV_STATS_INC() to update dev->stats fields. Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- net/ipv4/ip_gre.c | 10 +++++----- net/ipv4/ip_tunnel.c | 32 ++++++++++++++++---------------- net/ipv4/ip_vti.c | 20 ++++++++++---------- net/ipv4/ipip.c | 2 +- net/ipv4/ipmr.c | 12 ++++++------ 5 files changed, 38 insertions(+), 38 deletions(-) diff --git a/net/ipv4/ip_gre.c b/net/ipv4/ip_gre.c index d8ee5238c395..a4ccef3e6935 100644 --- a/net/ipv4/ip_gre.c +++ b/net/ipv4/ip_gre.c @@ -510,7 +510,7 @@ static void gre_fb_xmit(struct sk_buff *skb, struct net_device *dev, err_free_skb: kfree_skb(skb); - dev->stats.tx_dropped++; + DEV_STATS_INC(dev, tx_dropped); } static void erspan_fb_xmit(struct sk_buff *skb, struct net_device *dev) @@ -592,7 +592,7 @@ static void erspan_fb_xmit(struct sk_buff *skb, struct net_device *dev) err_free_skb: kfree_skb(skb); - dev->stats.tx_dropped++; + DEV_STATS_INC(dev, tx_dropped); } static int gre_fill_metadata_dst(struct net_device *dev, struct sk_buff *skb) @@ -663,7 +663,7 @@ static netdev_tx_t ipgre_xmit(struct sk_buff *skb, free_skb: kfree_skb(skb); - dev->stats.tx_dropped++; + DEV_STATS_INC(dev, tx_dropped); return NETDEV_TX_OK; } @@ -717,7 +717,7 @@ static netdev_tx_t erspan_xmit(struct sk_buff *skb, free_skb: kfree_skb(skb); - dev->stats.tx_dropped++; + DEV_STATS_INC(dev, tx_dropped); return NETDEV_TX_OK; } @@ -745,7 +745,7 @@ static netdev_tx_t gre_tap_xmit(struct sk_buff *skb, free_skb: kfree_skb(skb); - dev->stats.tx_dropped++; + DEV_STATS_INC(dev, tx_dropped); return NETDEV_TX_OK; } diff --git a/net/ipv4/ip_tunnel.c b/net/ipv4/ip_tunnel.c index 019f3b0839c5..de90b09dfe78 100644 --- a/net/ipv4/ip_tunnel.c +++ b/net/ipv4/ip_tunnel.c @@ -368,23 +368,23 @@ int ip_tunnel_rcv(struct ip_tunnel *tunnel, struct sk_buff *skb, #ifdef CONFIG_NET_IPGRE_BROADCAST if (ipv4_is_multicast(iph->daddr)) { - tunnel->dev->stats.multicast++; + DEV_STATS_INC(tunnel->dev, multicast); skb->pkt_type = PACKET_BROADCAST; } #endif if ((!(tpi->flags&TUNNEL_CSUM) && (tunnel->parms.i_flags&TUNNEL_CSUM)) || ((tpi->flags&TUNNEL_CSUM) && !(tunnel->parms.i_flags&TUNNEL_CSUM))) { - tunnel->dev->stats.rx_crc_errors++; - tunnel->dev->stats.rx_errors++; + DEV_STATS_INC(tunnel->dev, rx_crc_errors); + DEV_STATS_INC(tunnel->dev, rx_errors); goto drop; } if (tunnel->parms.i_flags&TUNNEL_SEQ) { if (!(tpi->flags&TUNNEL_SEQ) || (tunnel->i_seqno && (s32)(ntohl(tpi->seq) - tunnel->i_seqno) < 0)) { - tunnel->dev->stats.rx_fifo_errors++; - tunnel->dev->stats.rx_errors++; + DEV_STATS_INC(tunnel->dev, rx_fifo_errors); + DEV_STATS_INC(tunnel->dev, rx_errors); goto drop; } tunnel->i_seqno = ntohl(tpi->seq) + 1; @@ -398,8 +398,8 @@ int ip_tunnel_rcv(struct ip_tunnel *tunnel, struct sk_buff *skb, net_info_ratelimited("non-ECT from %pI4 with TOS=%#x\n", &iph->saddr, iph->tos); if (err > 1) { - ++tunnel->dev->stats.rx_frame_errors; - ++tunnel->dev->stats.rx_errors; + DEV_STATS_INC(tunnel->dev, rx_frame_errors); + DEV_STATS_INC(tunnel->dev, rx_errors); goto drop; } } @@ -581,7 +581,7 @@ void ip_md_tunnel_xmit(struct sk_buff *skb, struct net_device *dev, if (!rt) { rt = ip_route_output_key(tunnel->net, &fl4); if (IS_ERR(rt)) { - dev->stats.tx_carrier_errors++; + DEV_STATS_INC(dev, tx_carrier_errors); goto tx_error; } if (use_cache) @@ -590,7 +590,7 @@ void ip_md_tunnel_xmit(struct sk_buff *skb, struct net_device *dev, } if (rt->dst.dev == dev) { ip_rt_put(rt); - dev->stats.collisions++; + DEV_STATS_INC(dev, collisions); goto tx_error; } @@ -625,10 +625,10 @@ void ip_md_tunnel_xmit(struct sk_buff *skb, struct net_device *dev, df, !net_eq(tunnel->net, dev_net(dev))); return; tx_error: - dev->stats.tx_errors++; + DEV_STATS_INC(dev, tx_errors); goto kfree; tx_dropped: - dev->stats.tx_dropped++; + DEV_STATS_INC(dev, tx_dropped); kfree: kfree_skb(skb); } @@ -662,7 +662,7 @@ void ip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev, /* NBMA tunnel */ if (!skb_dst(skb)) { - dev->stats.tx_fifo_errors++; + DEV_STATS_INC(dev, tx_fifo_errors); goto tx_error; } @@ -749,7 +749,7 @@ void ip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev, rt = ip_route_output_key(tunnel->net, &fl4); if (IS_ERR(rt)) { - dev->stats.tx_carrier_errors++; + DEV_STATS_INC(dev, tx_carrier_errors); goto tx_error; } if (use_cache) @@ -762,7 +762,7 @@ void ip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev, if (rt->dst.dev == dev) { ip_rt_put(rt); - dev->stats.collisions++; + DEV_STATS_INC(dev, collisions); goto tx_error; } @@ -805,7 +805,7 @@ void ip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev, if (skb_cow_head(skb, dev->needed_headroom)) { ip_rt_put(rt); - dev->stats.tx_dropped++; + DEV_STATS_INC(dev, tx_dropped); kfree_skb(skb); return; } @@ -819,7 +819,7 @@ tx_error_icmp: dst_link_failure(skb); #endif tx_error: - dev->stats.tx_errors++; + DEV_STATS_INC(dev, tx_errors); kfree_skb(skb); } EXPORT_SYMBOL_GPL(ip_tunnel_xmit); diff --git a/net/ipv4/ip_vti.c b/net/ipv4/ip_vti.c index 8c2bd1d9ddce..53bfd8af6920 100644 --- a/net/ipv4/ip_vti.c +++ b/net/ipv4/ip_vti.c @@ -107,8 +107,8 @@ static int vti_rcv_cb(struct sk_buff *skb, int err) dev = tunnel->dev; if (err) { - dev->stats.rx_errors++; - dev->stats.rx_dropped++; + DEV_STATS_INC(dev, rx_errors); + DEV_STATS_INC(dev, rx_dropped); return 0; } @@ -183,7 +183,7 @@ static netdev_tx_t vti_xmit(struct sk_buff *skb, struct net_device *dev, fl->u.ip4.flowi4_flags |= FLOWI_FLAG_ANYSRC; rt = __ip_route_output_key(dev_net(dev), &fl->u.ip4); if (IS_ERR(rt)) { - dev->stats.tx_carrier_errors++; + DEV_STATS_INC(dev, tx_carrier_errors); goto tx_error_icmp; } dst = &rt->dst; @@ -198,14 +198,14 @@ static netdev_tx_t vti_xmit(struct sk_buff *skb, struct net_device *dev, if (dst->error) { dst_release(dst); dst = NULL; - dev->stats.tx_carrier_errors++; + DEV_STATS_INC(dev, tx_carrier_errors); goto tx_error_icmp; } skb_dst_set(skb, dst); break; #endif default: - dev->stats.tx_carrier_errors++; + DEV_STATS_INC(dev, tx_carrier_errors); goto tx_error_icmp; } } @@ -213,7 +213,7 @@ static netdev_tx_t vti_xmit(struct sk_buff *skb, struct net_device *dev, dst_hold(dst); dst = xfrm_lookup_route(tunnel->net, dst, fl, NULL, 0); if (IS_ERR(dst)) { - dev->stats.tx_carrier_errors++; + DEV_STATS_INC(dev, tx_carrier_errors); goto tx_error_icmp; } @@ -221,7 +221,7 @@ static netdev_tx_t vti_xmit(struct sk_buff *skb, struct net_device *dev, goto xmit; if (!vti_state_check(dst->xfrm, parms->iph.daddr, parms->iph.saddr)) { - dev->stats.tx_carrier_errors++; + DEV_STATS_INC(dev, tx_carrier_errors); dst_release(dst); goto tx_error_icmp; } @@ -230,7 +230,7 @@ static netdev_tx_t vti_xmit(struct sk_buff *skb, struct net_device *dev, if (tdev == dev) { dst_release(dst); - dev->stats.collisions++; + DEV_STATS_INC(dev, collisions); goto tx_error; } @@ -267,7 +267,7 @@ xmit: tx_error_icmp: dst_link_failure(skb); tx_error: - dev->stats.tx_errors++; + DEV_STATS_INC(dev, tx_errors); kfree_skb(skb); return NETDEV_TX_OK; } @@ -304,7 +304,7 @@ static netdev_tx_t vti_tunnel_xmit(struct sk_buff *skb, struct net_device *dev) return vti_xmit(skb, dev, &fl); tx_err: - dev->stats.tx_errors++; + DEV_STATS_INC(dev, tx_errors); kfree_skb(skb); return NETDEV_TX_OK; } diff --git a/net/ipv4/ipip.c b/net/ipv4/ipip.c index 180f9daf5bec..abea77759b7e 100644 --- a/net/ipv4/ipip.c +++ b/net/ipv4/ipip.c @@ -310,7 +310,7 @@ static netdev_tx_t ipip_tunnel_xmit(struct sk_buff *skb, tx_error: kfree_skb(skb); - dev->stats.tx_errors++; + DEV_STATS_INC(dev, tx_errors); return NETDEV_TX_OK; } diff --git a/net/ipv4/ipmr.c b/net/ipv4/ipmr.c index e04544ac4b45..b58df3c1bf7d 100644 --- a/net/ipv4/ipmr.c +++ b/net/ipv4/ipmr.c @@ -506,8 +506,8 @@ static netdev_tx_t reg_vif_xmit(struct sk_buff *skb, struct net_device *dev) return err; } - dev->stats.tx_bytes += skb->len; - dev->stats.tx_packets++; + DEV_STATS_ADD(dev, tx_bytes, skb->len); + DEV_STATS_INC(dev, tx_packets); rcu_read_lock(); /* Pairs with WRITE_ONCE() in vif_add() and vif_delete() */ @@ -1839,8 +1839,8 @@ static void ipmr_queue_xmit(struct net *net, struct mr_table *mrt, if (vif->flags & VIFF_REGISTER) { WRITE_ONCE(vif->pkt_out, vif->pkt_out + 1); WRITE_ONCE(vif->bytes_out, vif->bytes_out + skb->len); - vif_dev->stats.tx_bytes += skb->len; - vif_dev->stats.tx_packets++; + DEV_STATS_ADD(vif_dev, tx_bytes, skb->len); + DEV_STATS_INC(vif_dev, tx_packets); ipmr_cache_report(mrt, skb, vifi, IGMPMSG_WHOLEPKT); goto out_free; } @@ -1898,8 +1898,8 @@ static void ipmr_queue_xmit(struct net *net, struct mr_table *mrt, if (vif->flags & VIFF_TUNNEL) { ip_encap(net, skb, vif->local, vif->remote); /* FIXME: extra output firewall step used to be here. --RR */ - vif_dev->stats.tx_packets++; - vif_dev->stats.tx_bytes += skb->len; + DEV_STATS_INC(vif_dev, tx_packets); + DEV_STATS_ADD(vif_dev, tx_bytes, skb->len); } IPCB(skb)->flags |= IPSKB_FORWARDED; From 1d7322f28fde3f90f2a2ed0b62fafc004b3f822a Mon Sep 17 00:00:00 2001 From: Li zeming Date: Tue, 15 Nov 2022 10:14:24 +0800 Subject: [PATCH 0987/2002] ax25: af_ax25: Remove unnecessary (void*) conversions The valptr pointer is of (void *) type, so other pointers need not be forced to assign values to it. Signed-off-by: Li zeming Signed-off-by: David S. Miller --- net/ax25/af_ax25.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/ax25/af_ax25.c b/net/ax25/af_ax25.c index 6b4c25a92377..d8da400cb4de 100644 --- a/net/ax25/af_ax25.c +++ b/net/ax25/af_ax25.c @@ -723,7 +723,7 @@ static int ax25_getsockopt(struct socket *sock, int level, int optname, if (maxlen < 1) return -EFAULT; - valptr = (void *) &val; + valptr = &val; length = min_t(unsigned int, maxlen, sizeof(int)); lock_sock(sk); @@ -785,7 +785,7 @@ static int ax25_getsockopt(struct socket *sock, int level, int optname, length = 1; } - valptr = (void *) devname; + valptr = devname; break; default: From bf36267e3ad3df80a3a18eb0422723069a434934 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Tue, 15 Nov 2022 09:18:51 +0000 Subject: [PATCH 0988/2002] tcp: annotate data-race around queue->synflood_warned Annotate the lockless read of queue->synflood_warned. Following xchg() has the needed data-race resolution. Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- net/ipv4/tcp_input.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index 94024fdc2da1..0ae291e53eab 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -6841,7 +6841,7 @@ static bool tcp_syn_flood_action(const struct sock *sk, const char *proto) #endif __NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPREQQFULLDROP); - if (!queue->synflood_warned && syncookies != 2 && + if (!READ_ONCE(queue->synflood_warned) && syncookies != 2 && xchg(&queue->synflood_warned, 1) == 0) { if (IS_ENABLED(CONFIG_IPV6) && sk->sk_family == AF_INET6) { net_info_ratelimited("%s: Possible SYN flooding on port [%pI6c]:%u. %s.\n", From d82303df06481235fe7cbaf605075e0c2c87e99b Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Mon, 14 Nov 2022 13:37:01 -0800 Subject: [PATCH 0989/2002] mlxsw: update adjfine to use adjust_by_scaled_ppm The mlxsw adjfine implementation in the spectrum_ptp.c file converts scaled_ppm into ppb before updating a cyclecounter multiplier using the standard "base * ppb / 1billion" calculation. This can be re-written to use adjust_by_scaled_ppm, directly using the scaled parts per million and reducing the amount of code required to express this calculation. We still calculate the parts per billion for passing into mlxsw_sp_ptp_phc_adjfreq because this function requires the input to be in parts per billion. Signed-off-by: Jacob Keller Cc: Amit Cohen Cc: Petr Machata Reviewed-by: Ido Schimmel Tested-by: Ido Schimmel Link: https://lore.kernel.org/r/20221114213701.815132-1-jacob.e.keller@intel.com Signed-off-by: Jakub Kicinski --- .../net/ethernet/mellanox/mlxsw/spectrum_ptp.c | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_ptp.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_ptp.c index 7b01b9c20722..cbb6c75a6620 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_ptp.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_ptp.c @@ -189,29 +189,17 @@ mlxsw_sp1_ptp_phc_settime(struct mlxsw_sp1_ptp_clock *clock, u64 nsec) static int mlxsw_sp1_ptp_adjfine(struct ptp_clock_info *ptp, long scaled_ppm) { struct mlxsw_sp1_ptp_clock *clock = mlxsw_sp1_ptp_clock(ptp); - int neg_adj = 0; - u32 diff; - u64 adj; s32 ppb; ppb = scaled_ppm_to_ppb(scaled_ppm); - if (ppb < 0) { - neg_adj = 1; - ppb = -ppb; - } - - adj = clock->nominal_c_mult; - adj *= ppb; - diff = div_u64(adj, NSEC_PER_SEC); - spin_lock_bh(&clock->lock); timecounter_read(&clock->tc); - clock->cycles.mult = neg_adj ? clock->nominal_c_mult - diff : - clock->nominal_c_mult + diff; + clock->cycles.mult = adjust_by_scaled_ppm(clock->nominal_c_mult, + scaled_ppm); spin_unlock_bh(&clock->lock); - return mlxsw_sp_ptp_phc_adjfreq(&clock->common, neg_adj ? -ppb : ppb); + return mlxsw_sp_ptp_phc_adjfreq(&clock->common, ppb); } static int mlxsw_sp1_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta) From c453e64cbc9532c0c2edfa999c35d29dad16b8bb Mon Sep 17 00:00:00 2001 From: Wang Yufen Date: Tue, 15 Nov 2022 11:29:40 +0800 Subject: [PATCH 0990/2002] selftests/bpf: fix memory leak of lsm_cgroup kmemleak reports this issue: unreferenced object 0xffff88810b7835c0 (size 32): comm "test_progs", pid 270, jiffies 4294969007 (age 1621.315s) hex dump (first 32 bytes): 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 03 00 00 00 03 00 00 00 0f 00 00 00 00 00 00 00 ................ backtrace: [<00000000376cdeab>] kmalloc_trace+0x27/0x110 [<000000003bcdb3b6>] selinux_sk_alloc_security+0x66/0x110 [<000000003959008f>] security_sk_alloc+0x47/0x80 [<00000000e7bc6668>] sk_prot_alloc+0xbd/0x1a0 [<0000000002d6343a>] sk_alloc+0x3b/0x940 [<000000009812a46d>] unix_create1+0x8f/0x3d0 [<000000005ed0976b>] unix_create+0xa1/0x150 [<0000000086a1d27f>] __sock_create+0x233/0x4a0 [<00000000cffe3a73>] __sys_socket_create.part.0+0xaa/0x110 [<0000000007c63f20>] __sys_socket+0x49/0xf0 [<00000000b08753c8>] __x64_sys_socket+0x42/0x50 [<00000000b56e26b3>] do_syscall_64+0x3b/0x90 [<000000009b4871b8>] entry_SYSCALL_64_after_hwframe+0x63/0xcd The issue occurs in the following scenarios: unix_create1() sk_alloc() sk_prot_alloc() security_sk_alloc() call_int_hook() hlist_for_each_entry() entry1->hook.sk_alloc_security <-- selinux_sk_alloc_security() succeeded, <-- sk->security alloced here. entry2->hook.sk_alloc_security <-- bpf_lsm_sk_alloc_security() failed goto out_free; ... <-- the sk->security not freed, memleak The core problem is that the LSM is not yet fully stacked (work is actively going on in this space) which means that some LSM hooks do not support multiple LSMs at the same time. To fix, skip the "EPERM" test when it runs in the environments that already have non-bpf lsms installed Fixes: dca85aac8895 ("selftests/bpf: lsm_cgroup functional test") Signed-off-by: Wang Yufen Cc: Stanislav Fomichev Acked-by: Stanislav Fomichev Link: https://lore.kernel.org/r/1668482980-16163-1-git-send-email-wangyufen@huawei.com Signed-off-by: Martin KaFai Lau --- .../selftests/bpf/prog_tests/lsm_cgroup.c | 17 +++++++++++++---- tools/testing/selftests/bpf/progs/lsm_cgroup.c | 8 ++++++++ 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/tools/testing/selftests/bpf/prog_tests/lsm_cgroup.c b/tools/testing/selftests/bpf/prog_tests/lsm_cgroup.c index 1102e4f42d2d..f117bfef68a1 100644 --- a/tools/testing/selftests/bpf/prog_tests/lsm_cgroup.c +++ b/tools/testing/selftests/bpf/prog_tests/lsm_cgroup.c @@ -173,10 +173,12 @@ static void test_lsm_cgroup_functional(void) ASSERT_EQ(query_prog_cnt(cgroup_fd, NULL), 4, "total prog count"); ASSERT_EQ(query_prog_cnt(cgroup_fd2, NULL), 1, "total prog count"); - /* AF_UNIX is prohibited. */ - fd = socket(AF_UNIX, SOCK_STREAM, 0); - ASSERT_LT(fd, 0, "socket(AF_UNIX)"); + if (!(skel->kconfig->CONFIG_SECURITY_APPARMOR + || skel->kconfig->CONFIG_SECURITY_SELINUX + || skel->kconfig->CONFIG_SECURITY_SMACK)) + /* AF_UNIX is prohibited. */ + ASSERT_LT(fd, 0, "socket(AF_UNIX)"); close(fd); /* AF_INET6 gets default policy (sk_priority). */ @@ -233,11 +235,18 @@ static void test_lsm_cgroup_functional(void) /* AF_INET6+SOCK_STREAM * AF_PACKET+SOCK_RAW + * AF_UNIX+SOCK_RAW if already have non-bpf lsms installed * listen_fd * client_fd * accepted_fd */ - ASSERT_EQ(skel->bss->called_socket_post_create2, 5, "called_create2"); + if (skel->kconfig->CONFIG_SECURITY_APPARMOR + || skel->kconfig->CONFIG_SECURITY_SELINUX + || skel->kconfig->CONFIG_SECURITY_SMACK) + /* AF_UNIX+SOCK_RAW if already have non-bpf lsms installed */ + ASSERT_EQ(skel->bss->called_socket_post_create2, 6, "called_create2"); + else + ASSERT_EQ(skel->bss->called_socket_post_create2, 5, "called_create2"); /* start_server * bind(ETH_P_ALL) diff --git a/tools/testing/selftests/bpf/progs/lsm_cgroup.c b/tools/testing/selftests/bpf/progs/lsm_cgroup.c index 4f2d60b87b75..02c11d16b692 100644 --- a/tools/testing/selftests/bpf/progs/lsm_cgroup.c +++ b/tools/testing/selftests/bpf/progs/lsm_cgroup.c @@ -7,6 +7,10 @@ char _license[] SEC("license") = "GPL"; +extern bool CONFIG_SECURITY_SELINUX __kconfig __weak; +extern bool CONFIG_SECURITY_SMACK __kconfig __weak; +extern bool CONFIG_SECURITY_APPARMOR __kconfig __weak; + #ifndef AF_PACKET #define AF_PACKET 17 #endif @@ -140,6 +144,10 @@ SEC("lsm_cgroup/sk_alloc_security") int BPF_PROG(socket_alloc, struct sock *sk, int family, gfp_t priority) { called_socket_alloc++; + /* if already have non-bpf lsms installed, EPERM will cause memory leak of non-bpf lsms */ + if (CONFIG_SECURITY_SELINUX || CONFIG_SECURITY_SMACK || CONFIG_SECURITY_APPARMOR) + return 1; + if (family == AF_UNIX) return 0; /* EPERM */ From b0798310f84c97d91e02c950d54677cad91ec5dd Mon Sep 17 00:00:00 2001 From: Li zeming Date: Tue, 15 Nov 2022 10:07:05 +0800 Subject: [PATCH 0991/2002] sctp: sm_statefuns: Remove pointer casts of the same type The subh.addip_hdr pointer is also of type (struct sctp_addiphdr *), so it does not require a cast. Signed-off-by: Li zeming Link: https://lore.kernel.org/r/20221115020705.3220-1-zeming@nfschina.com Signed-off-by: Paolo Abeni --- net/sctp/sm_statefuns.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/sctp/sm_statefuns.c b/net/sctp/sm_statefuns.c index f6ee7f4040c1..ce5426171206 100644 --- a/net/sctp/sm_statefuns.c +++ b/net/sctp/sm_statefuns.c @@ -4044,7 +4044,7 @@ enum sctp_disposition sctp_sf_do_asconf_ack(struct net *net, (void *)err_param, commands); if (last_asconf) { - addip_hdr = (struct sctp_addiphdr *)last_asconf->subh.addip_hdr; + addip_hdr = last_asconf->subh.addip_hdr; sent_serial = ntohl(addip_hdr->serial); } else { sent_serial = asoc->addip_serial - 1; From 710cfc6ab4b85ac5388828b4be63a5f20c8a9dd9 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Tue, 15 Nov 2022 09:31:37 +0000 Subject: [PATCH 0992/2002] sundance: remove unused variable cnt Variable cnt is just being incremented and it's never used anywhere else. The variable and the increment are redundant so remove it. Signed-off-by: Colin Ian King Reviewed-by: Leon Romanovsky Link: https://lore.kernel.org/r/20221115093137.144002-1-colin.i.king@gmail.com Signed-off-by: Paolo Abeni --- drivers/net/ethernet/dlink/sundance.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/net/ethernet/dlink/sundance.c b/drivers/net/ethernet/dlink/sundance.c index 43def191f26f..aaf0eda96292 100644 --- a/drivers/net/ethernet/dlink/sundance.c +++ b/drivers/net/ethernet/dlink/sundance.c @@ -1414,7 +1414,6 @@ static void refill_rx (struct net_device *dev) { struct netdev_private *np = netdev_priv(dev); int entry; - int cnt = 0; /* Refill the Rx ring buffers. */ for (;(np->cur_rx - np->dirty_rx + RX_RING_SIZE) % RX_RING_SIZE > 0; @@ -1441,7 +1440,6 @@ static void refill_rx (struct net_device *dev) np->rx_ring[entry].frag.length = cpu_to_le32(np->rx_buf_sz | LastFrag); np->rx_ring[entry].status = 0; - cnt++; } } static void netdev_error(struct net_device *dev, int intr_status) From b2e44aac91b25abbed57d785089c4b7af926a7bd Mon Sep 17 00:00:00 2001 From: Dmitry Vyukov Date: Tue, 15 Nov 2022 11:00:17 +0100 Subject: [PATCH 0993/2002] NFC: nci: Allow to create multiple virtual nci devices The current virtual nci driver is great for testing and fuzzing. But it allows to create at most one "global" device which does not allow to run parallel tests and harms fuzzing isolation and reproducibility. Restructure the driver to allow creation of multiple independent devices. This should be backwards compatible for existing tests. Signed-off-by: Dmitry Vyukov Reviewed-by: Bongsu Jeon Cc: Bongsu Jeon Cc: Krzysztof Kozlowski Cc: Jakub Kicinski Cc: netdev@vger.kernel.org Link: https://lore.kernel.org/r/20221115100017.787929-1-dvyukov@google.com Signed-off-by: Paolo Abeni --- drivers/nfc/virtual_ncidev.c | 149 +++++++++++++++++------------------ 1 file changed, 72 insertions(+), 77 deletions(-) diff --git a/drivers/nfc/virtual_ncidev.c b/drivers/nfc/virtual_ncidev.c index 85c06dbb2c44..bb76c7c7cc82 100644 --- a/drivers/nfc/virtual_ncidev.c +++ b/drivers/nfc/virtual_ncidev.c @@ -13,12 +13,6 @@ #include #include -enum virtual_ncidev_mode { - virtual_ncidev_enabled, - virtual_ncidev_disabled, - virtual_ncidev_disabling, -}; - #define IOCTL_GET_NCIDEV_IDX 0 #define VIRTUAL_NFC_PROTOCOLS (NFC_PROTO_JEWEL_MASK | \ NFC_PROTO_MIFARE_MASK | \ @@ -27,12 +21,12 @@ enum virtual_ncidev_mode { NFC_PROTO_ISO14443_B_MASK | \ NFC_PROTO_ISO15693_MASK) -static enum virtual_ncidev_mode state; -static DECLARE_WAIT_QUEUE_HEAD(wq); -static struct miscdevice miscdev; -static struct sk_buff *send_buff; -static struct nci_dev *ndev; -static DEFINE_MUTEX(nci_mutex); +struct virtual_nci_dev { + struct nci_dev *ndev; + struct mutex mtx; + struct sk_buff *send_buff; + struct wait_queue_head wq; +}; static int virtual_nci_open(struct nci_dev *ndev) { @@ -41,31 +35,34 @@ static int virtual_nci_open(struct nci_dev *ndev) static int virtual_nci_close(struct nci_dev *ndev) { - mutex_lock(&nci_mutex); - kfree_skb(send_buff); - send_buff = NULL; - mutex_unlock(&nci_mutex); + struct virtual_nci_dev *vdev = nci_get_drvdata(ndev); + + mutex_lock(&vdev->mtx); + kfree_skb(vdev->send_buff); + vdev->send_buff = NULL; + mutex_unlock(&vdev->mtx); return 0; } static int virtual_nci_send(struct nci_dev *ndev, struct sk_buff *skb) { - mutex_lock(&nci_mutex); - if (state != virtual_ncidev_enabled) { - mutex_unlock(&nci_mutex); - kfree_skb(skb); - return 0; - } + struct virtual_nci_dev *vdev = nci_get_drvdata(ndev); - if (send_buff) { - mutex_unlock(&nci_mutex); + mutex_lock(&vdev->mtx); + if (vdev->send_buff) { + mutex_unlock(&vdev->mtx); kfree_skb(skb); return -1; } - send_buff = skb_copy(skb, GFP_KERNEL); - mutex_unlock(&nci_mutex); - wake_up_interruptible(&wq); + vdev->send_buff = skb_copy(skb, GFP_KERNEL); + if (!vdev->send_buff) { + mutex_unlock(&vdev->mtx); + kfree_skb(skb); + return -1; + } + mutex_unlock(&vdev->mtx); + wake_up_interruptible(&vdev->wq); consume_skb(skb); return 0; @@ -80,29 +77,30 @@ static const struct nci_ops virtual_nci_ops = { static ssize_t virtual_ncidev_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) { + struct virtual_nci_dev *vdev = file->private_data; size_t actual_len; - mutex_lock(&nci_mutex); - while (!send_buff) { - mutex_unlock(&nci_mutex); - if (wait_event_interruptible(wq, send_buff)) + mutex_lock(&vdev->mtx); + while (!vdev->send_buff) { + mutex_unlock(&vdev->mtx); + if (wait_event_interruptible(vdev->wq, vdev->send_buff)) return -EFAULT; - mutex_lock(&nci_mutex); + mutex_lock(&vdev->mtx); } - actual_len = min_t(size_t, count, send_buff->len); + actual_len = min_t(size_t, count, vdev->send_buff->len); - if (copy_to_user(buf, send_buff->data, actual_len)) { - mutex_unlock(&nci_mutex); + if (copy_to_user(buf, vdev->send_buff->data, actual_len)) { + mutex_unlock(&vdev->mtx); return -EFAULT; } - skb_pull(send_buff, actual_len); - if (send_buff->len == 0) { - consume_skb(send_buff); - send_buff = NULL; + skb_pull(vdev->send_buff, actual_len); + if (vdev->send_buff->len == 0) { + consume_skb(vdev->send_buff); + vdev->send_buff = NULL; } - mutex_unlock(&nci_mutex); + mutex_unlock(&vdev->mtx); return actual_len; } @@ -111,6 +109,7 @@ static ssize_t virtual_ncidev_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { + struct virtual_nci_dev *vdev = file->private_data; struct sk_buff *skb; skb = alloc_skb(count, GFP_KERNEL); @@ -122,63 +121,58 @@ static ssize_t virtual_ncidev_write(struct file *file, return -EFAULT; } - nci_recv_frame(ndev, skb); + nci_recv_frame(vdev->ndev, skb); return count; } static int virtual_ncidev_open(struct inode *inode, struct file *file) { int ret = 0; + struct virtual_nci_dev *vdev; - mutex_lock(&nci_mutex); - if (state != virtual_ncidev_disabled) { - mutex_unlock(&nci_mutex); - return -EBUSY; - } - - ndev = nci_allocate_device(&virtual_nci_ops, VIRTUAL_NFC_PROTOCOLS, - 0, 0); - if (!ndev) { - mutex_unlock(&nci_mutex); + vdev = kzalloc(sizeof(*vdev), GFP_KERNEL); + if (!vdev) + return -ENOMEM; + vdev->ndev = nci_allocate_device(&virtual_nci_ops, + VIRTUAL_NFC_PROTOCOLS, 0, 0); + if (!vdev->ndev) { + kfree(vdev); return -ENOMEM; } - ret = nci_register_device(ndev); + mutex_init(&vdev->mtx); + init_waitqueue_head(&vdev->wq); + file->private_data = vdev; + nci_set_drvdata(vdev->ndev, vdev); + + ret = nci_register_device(vdev->ndev); if (ret < 0) { - nci_free_device(ndev); - mutex_unlock(&nci_mutex); + nci_free_device(vdev->ndev); + mutex_destroy(&vdev->mtx); + kfree(vdev); return ret; } - state = virtual_ncidev_enabled; - mutex_unlock(&nci_mutex); return 0; } static int virtual_ncidev_close(struct inode *inode, struct file *file) { - mutex_lock(&nci_mutex); + struct virtual_nci_dev *vdev = file->private_data; - if (state == virtual_ncidev_enabled) { - state = virtual_ncidev_disabling; - mutex_unlock(&nci_mutex); - - nci_unregister_device(ndev); - nci_free_device(ndev); - - mutex_lock(&nci_mutex); - } - - state = virtual_ncidev_disabled; - mutex_unlock(&nci_mutex); + nci_unregister_device(vdev->ndev); + nci_free_device(vdev->ndev); + mutex_destroy(&vdev->mtx); + kfree(vdev); return 0; } -static long virtual_ncidev_ioctl(struct file *flip, unsigned int cmd, +static long virtual_ncidev_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { - const struct nfc_dev *nfc_dev = ndev->nfc_dev; + struct virtual_nci_dev *vdev = file->private_data; + const struct nfc_dev *nfc_dev = vdev->ndev->nfc_dev; void __user *p = (void __user *)arg; if (cmd != IOCTL_GET_NCIDEV_IDX) @@ -199,14 +193,15 @@ static const struct file_operations virtual_ncidev_fops = { .unlocked_ioctl = virtual_ncidev_ioctl }; +static struct miscdevice miscdev = { + .minor = MISC_DYNAMIC_MINOR, + .name = "virtual_nci", + .fops = &virtual_ncidev_fops, + .mode = 0600, +}; + static int __init virtual_ncidev_init(void) { - state = virtual_ncidev_disabled; - miscdev.minor = MISC_DYNAMIC_MINOR; - miscdev.name = "virtual_nci"; - miscdev.fops = &virtual_ncidev_fops; - miscdev.mode = 0600; - return misc_register(&miscdev); } From 2af7749047d8d6ad43feff69f555a13a6a6c2831 Mon Sep 17 00:00:00 2001 From: Xiu Jianfeng Date: Thu, 10 Nov 2022 14:19:26 +0800 Subject: [PATCH 0994/2002] wifi: ath10k: Fix return value in ath10k_pci_init() This driver is attempting to register to support two different buses. if either of these is successful then ath10k_pci_init() should return 0 so that hardware attached to the successful bus can be probed and supported. only if both of these are unsuccessful should ath10k_pci_init() return an errno. Fixes: 0b523ced9a3c ("ath10k: add basic skeleton to support ahb") Signed-off-by: Xiu Jianfeng Reviewed-by: Jeff Johnson Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20221110061926.18163-1-xiujianfeng@huawei.com --- drivers/net/wireless/ath/ath10k/pci.c | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/drivers/net/wireless/ath/ath10k/pci.c b/drivers/net/wireless/ath/ath10k/pci.c index e56c6a6b1379..728d607289c3 100644 --- a/drivers/net/wireless/ath/ath10k/pci.c +++ b/drivers/net/wireless/ath/ath10k/pci.c @@ -3792,18 +3792,22 @@ static struct pci_driver ath10k_pci_driver = { static int __init ath10k_pci_init(void) { - int ret; + int ret1, ret2; - ret = pci_register_driver(&ath10k_pci_driver); - if (ret) + ret1 = pci_register_driver(&ath10k_pci_driver); + if (ret1) printk(KERN_ERR "failed to register ath10k pci driver: %d\n", - ret); + ret1); - ret = ath10k_ahb_init(); - if (ret) - printk(KERN_ERR "ahb init failed: %d\n", ret); + ret2 = ath10k_ahb_init(); + if (ret2) + printk(KERN_ERR "ahb init failed: %d\n", ret2); - return ret; + if (ret1 && ret2) + return ret1; + + /* registered to at least one bus */ + return 0; } module_init(ath10k_pci_init); From d9e38350a7b10f2e1006cd85a664b8698d027e7c Mon Sep 17 00:00:00 2001 From: Zhi-Jun You Date: Fri, 11 Nov 2022 00:40:25 +0800 Subject: [PATCH 0995/2002] wifi: ath10k: Remove redundant argument offset This argument of ath10k_htt_rx_h_frag_pn_check() is always set to 0 so drop this. Signed-off-by: Zhi-Jun You Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20221110164025.14418-1-hujy652@gmail.com --- drivers/net/wireless/ath/ath10k/htt_rx.c | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/drivers/net/wireless/ath/ath10k/htt_rx.c b/drivers/net/wireless/ath/ath10k/htt_rx.c index 3df52906b356..438b0caaceb7 100644 --- a/drivers/net/wireless/ath/ath10k/htt_rx.c +++ b/drivers/net/wireless/ath/ath10k/htt_rx.c @@ -1844,15 +1844,14 @@ static void ath10k_htt_rx_h_csum_offload(struct ath10k_hw_params *hw, } static u64 ath10k_htt_rx_h_get_pn(struct ath10k *ar, struct sk_buff *skb, - u16 offset, enum htt_rx_mpdu_encrypt_type enctype) { struct ieee80211_hdr *hdr; u64 pn = 0; u8 *ehdr; - hdr = (struct ieee80211_hdr *)(skb->data + offset); - ehdr = skb->data + offset + ieee80211_hdrlen(hdr->frame_control); + hdr = (struct ieee80211_hdr *)skb->data; + ehdr = skb->data + ieee80211_hdrlen(hdr->frame_control); if (enctype == HTT_RX_MPDU_ENCRYPT_AES_CCM_WPA2) { pn = ehdr[0]; @@ -1866,19 +1865,17 @@ static u64 ath10k_htt_rx_h_get_pn(struct ath10k *ar, struct sk_buff *skb, } static bool ath10k_htt_rx_h_frag_multicast_check(struct ath10k *ar, - struct sk_buff *skb, - u16 offset) + struct sk_buff *skb) { struct ieee80211_hdr *hdr; - hdr = (struct ieee80211_hdr *)(skb->data + offset); + hdr = (struct ieee80211_hdr *)skb->data; return !is_multicast_ether_addr(hdr->addr1); } static bool ath10k_htt_rx_h_frag_pn_check(struct ath10k *ar, struct sk_buff *skb, u16 peer_id, - u16 offset, enum htt_rx_mpdu_encrypt_type enctype) { struct ath10k_peer *peer; @@ -1893,14 +1890,14 @@ static bool ath10k_htt_rx_h_frag_pn_check(struct ath10k *ar, return false; } - hdr = (struct ieee80211_hdr *)(skb->data + offset); + hdr = (struct ieee80211_hdr *)skb->data; if (ieee80211_is_data_qos(hdr->frame_control)) tid = ieee80211_get_tid(hdr); else tid = ATH10K_TXRX_NON_QOS_TID; last_pn = &peer->frag_tids_last_pn[tid]; - new_pn.pn48 = ath10k_htt_rx_h_get_pn(ar, skb, offset, enctype); + new_pn.pn48 = ath10k_htt_rx_h_get_pn(ar, skb, enctype); frag_number = le16_to_cpu(hdr->seq_ctrl) & IEEE80211_SCTL_FRAG; seq = IEEE80211_SEQ_TO_SN(__le16_to_cpu(hdr->seq_ctrl)); @@ -2059,13 +2056,11 @@ static void ath10k_htt_rx_h_mpdu(struct ath10k *ar, frag_pn_check = ath10k_htt_rx_h_frag_pn_check(ar, msdu, peer_id, - 0, enctype); if (frag) multicast_check = ath10k_htt_rx_h_frag_multicast_check(ar, - msdu, - 0); + msdu); if (!frag_pn_check || !multicast_check) { /* Discard the fragment with invalid PN or multicast DA From b4b221bd79a1c698d9653e3ae2c3cb61cdc9aee7 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Tue, 15 Nov 2022 16:09:55 +0300 Subject: [PATCH 0996/2002] net: ethernet: renesas: Fix return type in rswitch_etha_wait_link_verification() The rswitch_etha_wait_link_verification() is supposed to return zero on success or negative error codes. Unfortunately it is declared as a bool so the caller treats everything as success. Fixes: 3590918b5d07 ("net: ethernet: renesas: Add support for "Ethernet Switch"") Signed-off-by: Dan Carpenter Reviewed-by: Geert Uytterhoeven Reviewed-by: Yoshihiro Shimoda Link: https://lore.kernel.org/r/Y3OPo6AOL6PTvXFU@kili Signed-off-by: Paolo Abeni --- drivers/net/ethernet/renesas/rswitch.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/renesas/rswitch.c b/drivers/net/ethernet/renesas/rswitch.c index f3d27aef1286..c098b27093ea 100644 --- a/drivers/net/ethernet/renesas/rswitch.c +++ b/drivers/net/ethernet/renesas/rswitch.c @@ -920,7 +920,7 @@ static void rswitch_etha_write_mac_address(struct rswitch_etha *etha, const u8 * etha->addr + MRMAC1); } -static bool rswitch_etha_wait_link_verification(struct rswitch_etha *etha) +static int rswitch_etha_wait_link_verification(struct rswitch_etha *etha) { iowrite32(MLVC_PLV, etha->addr + MLVC); From 383f1a8df8faba88a3bafaddc02f59421bad6829 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20M=C3=BCller?= Date: Wed, 16 Nov 2022 17:43:58 +0000 Subject: [PATCH 0997/2002] bpf/docs: Include blank lines between bullet points in bpf_devel_QA.rst MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit 26a9b433cf08 ("bpf/docs: Document how to run CI without patch submission") caused a warning to be generated when compiling the documentation: > bpf_devel_QA.rst:55: WARNING: Unexpected indentation. > bpf_devel_QA.rst:56: WARNING: Block quote ends without a blank line This change fixes the problem by inserting the required blank lines. Fixes: 26a9b433cf08 ("bpf/docs: Document how to run CI without patch submission") Reported-by: Akira Yokosawa Signed-off-by: Daniel Müller Signed-off-by: Daniel Borkmann Reviewed-by: Akira Yokosawa Link: https://lore.kernel.org/bpf/20221116174358.2744613-1-deso@posteo.net --- Documentation/bpf/bpf_devel_QA.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Documentation/bpf/bpf_devel_QA.rst b/Documentation/bpf/bpf_devel_QA.rst index 08572c75bdef..03d4993eda6f 100644 --- a/Documentation/bpf/bpf_devel_QA.rst +++ b/Documentation/bpf/bpf_devel_QA.rst @@ -51,10 +51,13 @@ While GitHub also provides a CLI that can be used to accomplish the same results, here we focus on the UI based workflow. The following steps lay out how to start a CI run for your patches: + - Create a fork of the aforementioned repository in your own account (one time action) + - Clone the fork locally, check out a new branch tracking either the bpf-next or bpf branch, and apply your to-be-tested patches on top of it + - Push the local branch to your fork and create a pull request against kernel-patches/bpf's bpf-next_base or bpf_base branch, respectively From 3af43ba4c6019b29c048921eb8147eb010165329 Mon Sep 17 00:00:00 2001 From: Hou Tao Date: Wed, 16 Nov 2022 15:50:58 +0800 Subject: [PATCH 0998/2002] bpf: Pass map file to .map_update_batch directly Currently bpf_map_do_batch() first invokes fdget(batch.map_fd) to get the target map file, then it invokes generic_map_update_batch() to do batch update. generic_map_update_batch() will get the target map file by using fdget(batch.map_fd) again and pass it to bpf_map_update_value(). The problem is map file returned by the second fdget() may be NULL or a totally different file compared by map file in bpf_map_do_batch(). The reason is that the first fdget() only guarantees the liveness of struct file instead of file descriptor and the file description may be released by concurrent close() through pick_file(). It doesn't incur any problem as for now, because maps with batch update support don't use map file in .map_fd_get_ptr() ops. But it is better to fix the potential access of an invalid map file. Using __bpf_map_get() again in generic_map_update_batch() can not fix the problem, because batch.map_fd may be closed and reopened, and the returned map file may be different with map file got in bpf_map_do_batch(), so just passing the map file directly to .map_update_batch() in bpf_map_do_batch(). Signed-off-by: Hou Tao Signed-off-by: Daniel Borkmann Acked-by: Yonghong Song Link: https://lore.kernel.org/bpf/20221116075059.1551277-1-houtao@huaweicloud.com --- include/linux/bpf.h | 5 +++-- kernel/bpf/syscall.c | 32 ++++++++++++++------------------ 2 files changed, 17 insertions(+), 20 deletions(-) diff --git a/include/linux/bpf.h b/include/linux/bpf.h index 54462dd28824..e60a5c052473 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -85,7 +85,8 @@ struct bpf_map_ops { int (*map_lookup_and_delete_batch)(struct bpf_map *map, const union bpf_attr *attr, union bpf_attr __user *uattr); - int (*map_update_batch)(struct bpf_map *map, const union bpf_attr *attr, + int (*map_update_batch)(struct bpf_map *map, struct file *map_file, + const union bpf_attr *attr, union bpf_attr __user *uattr); int (*map_delete_batch)(struct bpf_map *map, const union bpf_attr *attr, union bpf_attr __user *uattr); @@ -1789,7 +1790,7 @@ void bpf_map_init_from_attr(struct bpf_map *map, union bpf_attr *attr); int generic_map_lookup_batch(struct bpf_map *map, const union bpf_attr *attr, union bpf_attr __user *uattr); -int generic_map_update_batch(struct bpf_map *map, +int generic_map_update_batch(struct bpf_map *map, struct file *map_file, const union bpf_attr *attr, union bpf_attr __user *uattr); int generic_map_delete_batch(struct bpf_map *map, diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c index fdbae52f463f..b078965999e6 100644 --- a/kernel/bpf/syscall.c +++ b/kernel/bpf/syscall.c @@ -175,8 +175,8 @@ static void maybe_wait_bpf_programs(struct bpf_map *map) synchronize_rcu(); } -static int bpf_map_update_value(struct bpf_map *map, struct fd f, void *key, - void *value, __u64 flags) +static int bpf_map_update_value(struct bpf_map *map, struct file *map_file, + void *key, void *value, __u64 flags) { int err; @@ -190,7 +190,7 @@ static int bpf_map_update_value(struct bpf_map *map, struct fd f, void *key, map->map_type == BPF_MAP_TYPE_SOCKMAP) { return sock_map_update_elem_sys(map, key, value, flags); } else if (IS_FD_PROG_ARRAY(map)) { - return bpf_fd_array_map_update_elem(map, f.file, key, value, + return bpf_fd_array_map_update_elem(map, map_file, key, value, flags); } @@ -205,12 +205,12 @@ static int bpf_map_update_value(struct bpf_map *map, struct fd f, void *key, flags); } else if (IS_FD_ARRAY(map)) { rcu_read_lock(); - err = bpf_fd_array_map_update_elem(map, f.file, key, value, + err = bpf_fd_array_map_update_elem(map, map_file, key, value, flags); rcu_read_unlock(); } else if (map->map_type == BPF_MAP_TYPE_HASH_OF_MAPS) { rcu_read_lock(); - err = bpf_fd_htab_map_update_elem(map, f.file, key, value, + err = bpf_fd_htab_map_update_elem(map, map_file, key, value, flags); rcu_read_unlock(); } else if (map->map_type == BPF_MAP_TYPE_REUSEPORT_SOCKARRAY) { @@ -1410,7 +1410,7 @@ static int map_update_elem(union bpf_attr *attr, bpfptr_t uattr) goto free_key; } - err = bpf_map_update_value(map, f, key, value, attr->flags); + err = bpf_map_update_value(map, f.file, key, value, attr->flags); kvfree(value); free_key: @@ -1596,16 +1596,14 @@ int generic_map_delete_batch(struct bpf_map *map, return err; } -int generic_map_update_batch(struct bpf_map *map, +int generic_map_update_batch(struct bpf_map *map, struct file *map_file, const union bpf_attr *attr, union bpf_attr __user *uattr) { void __user *values = u64_to_user_ptr(attr->batch.values); void __user *keys = u64_to_user_ptr(attr->batch.keys); u32 value_size, cp, max_count; - int ufd = attr->batch.map_fd; void *key, *value; - struct fd f; int err = 0; if (attr->batch.elem_flags & ~BPF_F_LOCK) @@ -1632,7 +1630,6 @@ int generic_map_update_batch(struct bpf_map *map, return -ENOMEM; } - f = fdget(ufd); /* bpf_map_do_batch() guarantees ufd is valid */ for (cp = 0; cp < max_count; cp++) { err = -EFAULT; if (copy_from_user(key, keys + cp * map->key_size, @@ -1640,7 +1637,7 @@ int generic_map_update_batch(struct bpf_map *map, copy_from_user(value, values + cp * value_size, value_size)) break; - err = bpf_map_update_value(map, f, key, value, + err = bpf_map_update_value(map, map_file, key, value, attr->batch.elem_flags); if (err) @@ -1653,7 +1650,6 @@ int generic_map_update_batch(struct bpf_map *map, kvfree(value); kvfree(key); - fdput(f); return err; } @@ -4446,13 +4442,13 @@ put_file: #define BPF_MAP_BATCH_LAST_FIELD batch.flags -#define BPF_DO_BATCH(fn) \ +#define BPF_DO_BATCH(fn, ...) \ do { \ if (!fn) { \ err = -ENOTSUPP; \ goto err_put; \ } \ - err = fn(map, attr, uattr); \ + err = fn(__VA_ARGS__); \ } while (0) static int bpf_map_do_batch(const union bpf_attr *attr, @@ -4486,13 +4482,13 @@ static int bpf_map_do_batch(const union bpf_attr *attr, } if (cmd == BPF_MAP_LOOKUP_BATCH) - BPF_DO_BATCH(map->ops->map_lookup_batch); + BPF_DO_BATCH(map->ops->map_lookup_batch, map, attr, uattr); else if (cmd == BPF_MAP_LOOKUP_AND_DELETE_BATCH) - BPF_DO_BATCH(map->ops->map_lookup_and_delete_batch); + BPF_DO_BATCH(map->ops->map_lookup_and_delete_batch, map, attr, uattr); else if (cmd == BPF_MAP_UPDATE_BATCH) - BPF_DO_BATCH(map->ops->map_update_batch); + BPF_DO_BATCH(map->ops->map_update_batch, map, f.file, attr, uattr); else - BPF_DO_BATCH(map->ops->map_delete_batch); + BPF_DO_BATCH(map->ops->map_delete_batch, map, attr, uattr); err_put: if (has_write) bpf_map_write_active_dec(map); From c4525f05ca3ca2795fc626252fb1c1cbb310111b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20T=C3=B6pel?= Date: Tue, 15 Nov 2022 19:20:50 +0100 Subject: [PATCH 0999/2002] selftests/bpf: Explicitly pass RESOLVE_BTFIDS to sub-make MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When cross-compiling selftests/bpf, the resolve_btfids binary end up in a different directory, than the regular resolve_btfids builds. Populate RESOLVE_BTFIDS for sub-make, so it can find the binary. Signed-off-by: Björn Töpel Signed-off-by: Andrii Nakryiko Acked-by: Jiri Olsa Link: https://lore.kernel.org/bpf/20221115182051.582962-1-bjorn@kernel.org --- tools/testing/selftests/bpf/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/testing/selftests/bpf/Makefile b/tools/testing/selftests/bpf/Makefile index f3cd17026ee5..aa4e29315f4a 100644 --- a/tools/testing/selftests/bpf/Makefile +++ b/tools/testing/selftests/bpf/Makefile @@ -201,7 +201,7 @@ $(OUTPUT)/sign-file: ../../../../scripts/sign-file.c $(OUTPUT)/bpf_testmod.ko: $(VMLINUX_BTF) $(wildcard bpf_testmod/Makefile bpf_testmod/*.[ch]) $(call msg,MOD,,$@) $(Q)$(RM) bpf_testmod/bpf_testmod.ko # force re-compilation - $(Q)$(MAKE) $(submake_extras) -C bpf_testmod + $(Q)$(MAKE) $(submake_extras) RESOLVE_BTFIDS=$(RESOLVE_BTFIDS) -C bpf_testmod $(Q)cp bpf_testmod/bpf_testmod.ko $@ DEFAULT_BPFTOOL := $(HOST_SCRATCH_DIR)/sbin/bpftool From 98b2afc8a67f651ed01fc7d5a7e2528e63dd4e08 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20T=C3=B6pel?= Date: Tue, 15 Nov 2022 19:20:51 +0100 Subject: [PATCH 1000/2002] selftests/bpf: Pass target triple to get_sys_includes macro MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When cross-compiling [1], the get_sys_includes make macro should use the target system include path, and not the build hosts system include path. Make clang honor the CROSS_COMPILE triple. [1] e.g. "ARCH=riscv CROSS_COMPILE=riscv64-linux-gnu- make" Signed-off-by: Björn Töpel Signed-off-by: Andrii Nakryiko Tested-by: Anders Roxell Link: https://lore.kernel.org/bpf/20221115182051.582962-2-bjorn@kernel.org --- tools/testing/selftests/bpf/Makefile | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/tools/testing/selftests/bpf/Makefile b/tools/testing/selftests/bpf/Makefile index aa4e29315f4a..6a0f043dc410 100644 --- a/tools/testing/selftests/bpf/Makefile +++ b/tools/testing/selftests/bpf/Makefile @@ -310,9 +310,9 @@ $(RESOLVE_BTFIDS): $(HOST_BPFOBJ) | $(HOST_BUILD_DIR)/resolve_btfids \ # Use '-idirafter': Don't interfere with include mechanics except where the # build would have failed anyways. define get_sys_includes -$(shell $(1) -v -E - &1 \ +$(shell $(1) $(2) -v -E - &1 \ | sed -n '/<...> search starts here:/,/End of search list./{ s| \(/.*\)|-idirafter \1|p }') \ -$(shell $(1) -dM -E - Date: Fri, 18 Nov 2022 07:25:51 +0530 Subject: [PATCH 1001/2002] bpf: Fix early return in map_check_btf Instead of returning directly with -EOPNOTSUPP for the timer case, we need to free the btf_record before returning to userspace. Fixes: db559117828d ("bpf: Consolidate spin_lock, timer management into btf_record") Reported-by: Dan Carpenter Signed-off-by: Kumar Kartikeya Dwivedi Link: https://lore.kernel.org/r/20221118015614.2013203-2-memxor@gmail.com Signed-off-by: Alexei Starovoitov --- kernel/bpf/syscall.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c index b078965999e6..8eff51a63af6 100644 --- a/kernel/bpf/syscall.c +++ b/kernel/bpf/syscall.c @@ -1010,7 +1010,7 @@ static int map_check_btf(struct bpf_map *map, const struct btf *btf, if (map->map_type != BPF_MAP_TYPE_HASH && map->map_type != BPF_MAP_TYPE_LRU_HASH && map->map_type != BPF_MAP_TYPE_ARRAY) { - return -EOPNOTSUPP; + ret = -EOPNOTSUPP; goto free_map_tab; } break; From d7f5ef653c3dd0c0d649cae6ef2708053bb1fb2b Mon Sep 17 00:00:00 2001 From: Kumar Kartikeya Dwivedi Date: Fri, 18 Nov 2022 07:25:52 +0530 Subject: [PATCH 1002/2002] bpf: Do btf_record_free outside map_free callback Since the commit being fixed, we now miss freeing btf_record for local storage maps which will have a btf_record populated in case they have bpf_spin_lock element. This was missed because I made the choice of offloading the job to free kptr_off_tab (now btf_record) to the map_free callback when adding support for kptrs. Revisiting the reason for this decision, there is the possibility that the btf_record gets used inside map_free callback (e.g. in case of maps embedding kptrs) to iterate over them and free them, hence doing it before the map_free callback would be leaking special field memory, and do invalid memory access. The btf_record keeps module references which is critical to ensure the dtor call made for referenced kptr is safe to do. If doing it after map_free callback, the map area is already freed, so we cannot access bpf_map structure anymore. To fix this and prevent such lapses in future, move bpf_map_free_record out of the map_free callback, and do it after map_free by remembering the btf_record pointer. There is no need to access bpf_map structure in that case, and we can avoid missing this case when support for new map types is added for other special fields. Since a btf_record and its btf_field_offs are used together, for consistency delay freeing of field_offs as well. While not a problem right now, a lot of code assumes that either both record and field_offs are set or none at once. Note that in case of map of maps (outer maps), inner_map_meta->record is only used during verification, not to free fields in map value, hence we simply keep the bpf_map_free_record call as is in bpf_map_meta_free and never touch map->inner_map_meta in bpf_map_free_deferred. Add a comment making note of these details. Fixes: db559117828d ("bpf: Consolidate spin_lock, timer management into btf_record") Signed-off-by: Kumar Kartikeya Dwivedi Link: https://lore.kernel.org/r/20221118015614.2013203-3-memxor@gmail.com Signed-off-by: Alexei Starovoitov --- kernel/bpf/arraymap.c | 1 - kernel/bpf/hashtab.c | 1 - kernel/bpf/syscall.c | 18 ++++++++++++++---- 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/kernel/bpf/arraymap.c b/kernel/bpf/arraymap.c index 672eb17ac421..484706959556 100644 --- a/kernel/bpf/arraymap.c +++ b/kernel/bpf/arraymap.c @@ -430,7 +430,6 @@ static void array_map_free(struct bpf_map *map) for (i = 0; i < array->map.max_entries; i++) bpf_obj_free_fields(map->record, array_map_elem_ptr(array, i)); } - bpf_map_free_record(map); } if (array->map.map_type == BPF_MAP_TYPE_PERCPU_ARRAY) diff --git a/kernel/bpf/hashtab.c b/kernel/bpf/hashtab.c index 50d254cd0709..5aa2b5525f79 100644 --- a/kernel/bpf/hashtab.c +++ b/kernel/bpf/hashtab.c @@ -1511,7 +1511,6 @@ static void htab_map_free(struct bpf_map *map) prealloc_destroy(htab); } - bpf_map_free_record(map); free_percpu(htab->extra_elems); bpf_map_area_free(htab->buckets); bpf_mem_alloc_destroy(&htab->pcpu_ma); diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c index 8eff51a63af6..4c20dcbc6526 100644 --- a/kernel/bpf/syscall.c +++ b/kernel/bpf/syscall.c @@ -659,14 +659,24 @@ void bpf_obj_free_fields(const struct btf_record *rec, void *obj) static void bpf_map_free_deferred(struct work_struct *work) { struct bpf_map *map = container_of(work, struct bpf_map, work); + struct btf_field_offs *foffs = map->field_offs; + struct btf_record *rec = map->record; security_bpf_map_free(map); - kfree(map->field_offs); bpf_map_release_memcg(map); - /* implementation dependent freeing, map_free callback also does - * bpf_map_free_record, if needed. - */ + /* implementation dependent freeing */ map->ops->map_free(map); + /* Delay freeing of field_offs and btf_record for maps, as map_free + * callback usually needs access to them. It is better to do it here + * than require each callback to do the free itself manually. + * + * Note that the btf_record stashed in map->inner_map_meta->record was + * already freed using the map_free callback for map in map case which + * eventually calls bpf_map_free_meta, since inner_map_meta is only a + * template bpf_map struct used during verification. + */ + kfree(foffs); + btf_record_free(rec); } static void bpf_map_put_uref(struct bpf_map *map) From d48995723c9a1c4896206be382c72d722accbfbc Mon Sep 17 00:00:00 2001 From: Kumar Kartikeya Dwivedi Date: Fri, 18 Nov 2022 07:25:53 +0530 Subject: [PATCH 1003/2002] bpf: Free inner_map_meta when btf_record_dup fails Whenever btf_record_dup fails, we must free inner_map_meta that was allocated before. This fixes a memory leak (in case of errors) during inner map creation. Fixes: aa3496accc41 ("bpf: Refactor kptr_off_tab into btf_record") Signed-off-by: Kumar Kartikeya Dwivedi Link: https://lore.kernel.org/r/20221118015614.2013203-4-memxor@gmail.com Signed-off-by: Alexei Starovoitov --- kernel/bpf/map_in_map.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/kernel/bpf/map_in_map.c b/kernel/bpf/map_in_map.c index 8ca0cca39d49..a423130a8720 100644 --- a/kernel/bpf/map_in_map.c +++ b/kernel/bpf/map_in_map.c @@ -52,12 +52,14 @@ struct bpf_map *bpf_map_meta_alloc(int inner_map_ufd) inner_map_meta->max_entries = inner_map->max_entries; inner_map_meta->record = btf_record_dup(inner_map->record); if (IS_ERR(inner_map_meta->record)) { + struct bpf_map *err_ptr = ERR_CAST(inner_map_meta->record); /* btf_record_dup returns NULL or valid pointer in case of * invalid/empty/valid, but ERR_PTR in case of errors. During * equality NULL or IS_ERR is equivalent. */ + kfree(inner_map_meta); fdput(f); - return ERR_CAST(inner_map_meta->record); + return err_ptr; } if (inner_map->btf) { btf_get(inner_map->btf); From f73e601aafb2ad9f2b2012b969f86f4a41141a7d Mon Sep 17 00:00:00 2001 From: Kumar Kartikeya Dwivedi Date: Fri, 18 Nov 2022 07:25:54 +0530 Subject: [PATCH 1004/2002] bpf: Populate field_offs for inner_map_meta Far too much code simply assumes that both btf_record and btf_field_offs are set to valid pointers together, or both are unset. They go together hand in hand as btf_record describes the special fields and btf_field_offs is compact representation for runtime copying/zeroing. It is very difficult to make this clear in the code when the only exception to this universal invariant is inner_map_meta which is used as reg->map_ptr in the verifier. This is simply a bug waiting to happen, as in verifier context we cannot easily distinguish if PTR_TO_MAP_VALUE is coming from an inner map, and if we ever end up using field_offs for any reason in the future, we will silently ignore the special fields for inner map case (as NULL is not an error but unset field_offs). Hence, simply copy field_offs from inner map together with btf_record. While at it, refactor code to unwind properly on errors with gotos. Signed-off-by: Kumar Kartikeya Dwivedi Link: https://lore.kernel.org/r/20221118015614.2013203-5-memxor@gmail.com Signed-off-by: Alexei Starovoitov --- kernel/bpf/map_in_map.c | 44 ++++++++++++++++++++++++++++++----------- 1 file changed, 32 insertions(+), 12 deletions(-) diff --git a/kernel/bpf/map_in_map.c b/kernel/bpf/map_in_map.c index a423130a8720..fae6a6c33e2d 100644 --- a/kernel/bpf/map_in_map.c +++ b/kernel/bpf/map_in_map.c @@ -12,6 +12,7 @@ struct bpf_map *bpf_map_meta_alloc(int inner_map_ufd) struct bpf_map *inner_map, *inner_map_meta; u32 inner_map_meta_size; struct fd f; + int ret; f = fdget(inner_map_ufd); inner_map = __bpf_map_get(f); @@ -20,18 +21,18 @@ struct bpf_map *bpf_map_meta_alloc(int inner_map_ufd) /* Does not support >1 level map-in-map */ if (inner_map->inner_map_meta) { - fdput(f); - return ERR_PTR(-EINVAL); + ret = -EINVAL; + goto put; } if (!inner_map->ops->map_meta_equal) { - fdput(f); - return ERR_PTR(-ENOTSUPP); + ret = -ENOTSUPP; + goto put; } if (btf_record_has_field(inner_map->record, BPF_SPIN_LOCK)) { - fdput(f); - return ERR_PTR(-ENOTSUPP); + ret = -ENOTSUPP; + goto put; } inner_map_meta_size = sizeof(*inner_map_meta); @@ -41,8 +42,8 @@ struct bpf_map *bpf_map_meta_alloc(int inner_map_ufd) inner_map_meta = kzalloc(inner_map_meta_size, GFP_USER); if (!inner_map_meta) { - fdput(f); - return ERR_PTR(-ENOMEM); + ret = -ENOMEM; + goto put; } inner_map_meta->map_type = inner_map->map_type; @@ -50,16 +51,27 @@ struct bpf_map *bpf_map_meta_alloc(int inner_map_ufd) inner_map_meta->value_size = inner_map->value_size; inner_map_meta->map_flags = inner_map->map_flags; inner_map_meta->max_entries = inner_map->max_entries; + inner_map_meta->record = btf_record_dup(inner_map->record); if (IS_ERR(inner_map_meta->record)) { - struct bpf_map *err_ptr = ERR_CAST(inner_map_meta->record); /* btf_record_dup returns NULL or valid pointer in case of * invalid/empty/valid, but ERR_PTR in case of errors. During * equality NULL or IS_ERR is equivalent. */ - kfree(inner_map_meta); - fdput(f); - return err_ptr; + ret = PTR_ERR(inner_map_meta->record); + goto free; + } + if (inner_map_meta->record) { + struct btf_field_offs *field_offs; + /* If btf_record is !IS_ERR_OR_NULL, then field_offs is always + * valid. + */ + field_offs = kmemdup(inner_map->field_offs, sizeof(*inner_map->field_offs), GFP_KERNEL | __GFP_NOWARN); + if (!field_offs) { + ret = -ENOMEM; + goto free_rec; + } + inner_map_meta->field_offs = field_offs; } if (inner_map->btf) { btf_get(inner_map->btf); @@ -76,10 +88,18 @@ struct bpf_map *bpf_map_meta_alloc(int inner_map_ufd) fdput(f); return inner_map_meta; +free_rec: + btf_record_free(inner_map_meta->record); +free: + kfree(inner_map_meta); +put: + fdput(f); + return ERR_PTR(ret); } void bpf_map_meta_free(struct bpf_map *map_meta) { + kfree(map_meta->field_offs); bpf_map_free_record(map_meta); btf_put(map_meta->btf); kfree(map_meta); From 282de143ead96a5d53331e946f31c977b4610a74 Mon Sep 17 00:00:00 2001 From: Kumar Kartikeya Dwivedi Date: Fri, 18 Nov 2022 07:25:55 +0530 Subject: [PATCH 1005/2002] bpf: Introduce allocated objects support Introduce support for representing pointers to objects allocated by the BPF program, i.e. PTR_TO_BTF_ID that point to a type in program BTF. This is indicated by the presence of MEM_ALLOC type flag in reg->type to avoid having to check btf_is_kernel when trying to match argument types in helpers. Whenever walking such types, any pointers being walked will always yield a SCALAR instead of pointer. In the future we might permit kptr inside such allocated objects (either kernel or program allocated), and it will then form a PTR_TO_BTF_ID of the respective type. For now, such allocated objects will always be referenced in verifier context, hence ref_obj_id == 0 for them is a bug. It is allowed to write to such objects, as long fields that are special are not touched (support for which will be added in subsequent patches). Note that once such a pointer is marked PTR_UNTRUSTED, it is no longer allowed to write to it. No PROBE_MEM handling is therefore done for loads into this type unless PTR_UNTRUSTED is part of the register type, since they can never be in an undefined state, and their lifetime will always be valid. Signed-off-by: Kumar Kartikeya Dwivedi Link: https://lore.kernel.org/r/20221118015614.2013203-6-memxor@gmail.com Signed-off-by: Alexei Starovoitov --- include/linux/bpf.h | 11 +++++++++++ kernel/bpf/btf.c | 5 +++++ kernel/bpf/verifier.c | 25 +++++++++++++++++++++++-- 3 files changed, 39 insertions(+), 2 deletions(-) diff --git a/include/linux/bpf.h b/include/linux/bpf.h index e60a5c052473..7440c20c4192 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -525,6 +525,11 @@ enum bpf_type_flag { /* Size is known at compile time. */ MEM_FIXED_SIZE = BIT(10 + BPF_BASE_TYPE_BITS), + /* MEM is of an allocated object of type in program BTF. This is used to + * tag PTR_TO_BTF_ID allocated using bpf_obj_new. + */ + MEM_ALLOC = BIT(11 + BPF_BASE_TYPE_BITS), + __BPF_TYPE_FLAG_MAX, __BPF_TYPE_LAST_FLAG = __BPF_TYPE_FLAG_MAX - 1, }; @@ -2792,4 +2797,10 @@ struct bpf_key { bool has_ref; }; #endif /* CONFIG_KEYS */ + +static inline bool type_is_alloc(u32 type) +{ + return type & MEM_ALLOC; +} + #endif /* _LINUX_BPF_H */ diff --git a/kernel/bpf/btf.c b/kernel/bpf/btf.c index 875355ff3718..9a596f430558 100644 --- a/kernel/bpf/btf.c +++ b/kernel/bpf/btf.c @@ -6034,6 +6034,11 @@ int btf_struct_access(struct bpf_verifier_log *log, switch (err) { case WALK_PTR: + /* For local types, the destination register cannot + * become a pointer again. + */ + if (type_is_alloc(reg->type)) + return SCALAR_VALUE; /* If we found the pointer or scalar on t+off, * we're done. */ diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 0312d9ce292f..49e08c1c2c61 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -4687,14 +4687,27 @@ static int check_ptr_to_btf_access(struct bpf_verifier_env *env, return -EACCES; } - if (env->ops->btf_struct_access) { + if (env->ops->btf_struct_access && !type_is_alloc(reg->type)) { + if (!btf_is_kernel(reg->btf)) { + verbose(env, "verifier internal error: reg->btf must be kernel btf\n"); + return -EFAULT; + } ret = env->ops->btf_struct_access(&env->log, reg, off, size, atype, &btf_id, &flag); } else { - if (atype != BPF_READ) { + /* Writes are permitted with default btf_struct_access for + * program allocated objects (which always have ref_obj_id > 0), + * but not for untrusted PTR_TO_BTF_ID | MEM_ALLOC. + */ + if (atype != BPF_READ && reg->type != (PTR_TO_BTF_ID | MEM_ALLOC)) { verbose(env, "only read is supported\n"); return -EACCES; } + if (type_is_alloc(reg->type) && !reg->ref_obj_id) { + verbose(env, "verifier internal error: ref_obj_id for allocated object must be non-zero\n"); + return -EFAULT; + } + ret = btf_struct_access(&env->log, reg, off, size, atype, &btf_id, &flag); } @@ -5973,6 +5986,7 @@ int check_func_arg_reg_off(struct bpf_verifier_env *env, * fixed offset. */ case PTR_TO_BTF_ID: + case PTR_TO_BTF_ID | MEM_ALLOC: /* When referenced PTR_TO_BTF_ID is passed to release function, * it's fixed offset must be 0. In the other cases, fixed offset * can be non-zero. @@ -13690,6 +13704,13 @@ static int convert_ctx_accesses(struct bpf_verifier_env *env) break; case PTR_TO_BTF_ID: case PTR_TO_BTF_ID | PTR_UNTRUSTED: + /* PTR_TO_BTF_ID | MEM_ALLOC always has a valid lifetime, unlike + * PTR_TO_BTF_ID, and an active ref_obj_id, but the same cannot + * be said once it is marked PTR_UNTRUSTED, hence we must handle + * any faults for loads into such types. BPF_WRITE is disallowed + * for this case. + */ + case PTR_TO_BTF_ID | MEM_ALLOC | PTR_UNTRUSTED: if (type == BPF_READ) { insn->code = BPF_LDX | BPF_PROBE_MEM | BPF_SIZE((insn)->code); From 8ffa5cc142137a59d6a10eb5273fa2ba5dcd4947 Mon Sep 17 00:00:00 2001 From: Kumar Kartikeya Dwivedi Date: Fri, 18 Nov 2022 07:25:56 +0530 Subject: [PATCH 1006/2002] bpf: Recognize lock and list fields in allocated objects Allow specifying bpf_spin_lock, bpf_list_head, bpf_list_node fields in a allocated object. Also update btf_struct_access to reject direct access to these special fields. A bpf_list_head allows implementing map-in-map style use cases, where an allocated object with bpf_list_head is linked into a list in a map value. This would require embedding a bpf_list_node, support for which is also included. The bpf_spin_lock is used to protect the bpf_list_head and other data. While we strictly don't require to hold a bpf_spin_lock while touching the bpf_list_head in such objects, as when have access to it, we have complete ownership of the object, the locking constraint is still kept and may be conditionally lifted in the future. Note that the specification of such types can be done just like map values, e.g.: struct bar { struct bpf_list_node node; }; struct foo { struct bpf_spin_lock lock; struct bpf_list_head head __contains(bar, node); struct bpf_list_node node; }; struct map_value { struct bpf_spin_lock lock; struct bpf_list_head head __contains(foo, node); }; To recognize such types in user BTF, we build a btf_struct_metas array of metadata items corresponding to each BTF ID. This is done once during the btf_parse stage to avoid having to do it each time during the verification process's requirement to inspect the metadata. Moreover, the computed metadata needs to be passed to some helpers in future patches which requires allocating them and storing them in the BTF that is pinned by the program itself, so that valid access can be assumed to such data during program runtime. A key thing to note is that once a btf_struct_meta is available for a type, both the btf_record and btf_field_offs should be available. It is critical that btf_field_offs is available in case special fields are present, as we extensively rely on special fields being zeroed out in map values and allocated objects in later patches. The code ensures that by bailing out in case of errors and ensuring both are available together. If the record is not available, the special fields won't be recognized, so not having both is also fine (in terms of being a verification error and not a runtime bug). Signed-off-by: Kumar Kartikeya Dwivedi Link: https://lore.kernel.org/r/20221118015614.2013203-7-memxor@gmail.com Signed-off-by: Alexei Starovoitov --- include/linux/bpf.h | 7 ++ include/linux/btf.h | 35 ++++++++ kernel/bpf/btf.c | 197 +++++++++++++++++++++++++++++++++++++++---- kernel/bpf/syscall.c | 4 + 4 files changed, 225 insertions(+), 18 deletions(-) diff --git a/include/linux/bpf.h b/include/linux/bpf.h index 7440c20c4192..eb6ea53fa5a2 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -177,6 +177,7 @@ enum btf_field_type { BPF_KPTR_REF = (1 << 3), BPF_KPTR = BPF_KPTR_UNREF | BPF_KPTR_REF, BPF_LIST_HEAD = (1 << 4), + BPF_LIST_NODE = (1 << 5), }; struct btf_field_kptr { @@ -277,6 +278,8 @@ static inline const char *btf_field_type_name(enum btf_field_type type) return "kptr"; case BPF_LIST_HEAD: return "bpf_list_head"; + case BPF_LIST_NODE: + return "bpf_list_node"; default: WARN_ON_ONCE(1); return "unknown"; @@ -295,6 +298,8 @@ static inline u32 btf_field_type_size(enum btf_field_type type) return sizeof(u64); case BPF_LIST_HEAD: return sizeof(struct bpf_list_head); + case BPF_LIST_NODE: + return sizeof(struct bpf_list_node); default: WARN_ON_ONCE(1); return 0; @@ -313,6 +318,8 @@ static inline u32 btf_field_type_align(enum btf_field_type type) return __alignof__(u64); case BPF_LIST_HEAD: return __alignof__(struct bpf_list_head); + case BPF_LIST_NODE: + return __alignof__(struct bpf_list_node); default: WARN_ON_ONCE(1); return 0; diff --git a/include/linux/btf.h b/include/linux/btf.h index d80345fa566b..a01a8da20021 100644 --- a/include/linux/btf.h +++ b/include/linux/btf.h @@ -6,6 +6,8 @@ #include #include +#include +#include #include #include @@ -78,6 +80,17 @@ struct btf_id_dtor_kfunc { u32 kfunc_btf_id; }; +struct btf_struct_meta { + u32 btf_id; + struct btf_record *record; + struct btf_field_offs *field_offs; +}; + +struct btf_struct_metas { + u32 cnt; + struct btf_struct_meta types[]; +}; + typedef void (*btf_dtor_kfunc_t)(void *); extern const struct file_operations btf_fops; @@ -408,6 +421,23 @@ static inline struct btf_param *btf_params(const struct btf_type *t) return (struct btf_param *)(t + 1); } +static inline int btf_id_cmp_func(const void *a, const void *b) +{ + const int *pa = a, *pb = b; + + return *pa - *pb; +} + +static inline bool btf_id_set_contains(const struct btf_id_set *set, u32 id) +{ + return bsearch(&id, set->ids, set->cnt, sizeof(u32), btf_id_cmp_func) != NULL; +} + +static inline void *btf_id_set8_contains(const struct btf_id_set8 *set, u32 id) +{ + return bsearch(&id, set->pairs, set->cnt, sizeof(set->pairs[0]), btf_id_cmp_func); +} + #ifdef CONFIG_BPF_SYSCALL struct bpf_prog; @@ -423,6 +453,7 @@ int register_btf_kfunc_id_set(enum bpf_prog_type prog_type, s32 btf_find_dtor_kfunc(struct btf *btf, u32 btf_id); int register_btf_id_dtor_kfuncs(const struct btf_id_dtor_kfunc *dtors, u32 add_cnt, struct module *owner); +struct btf_struct_meta *btf_find_struct_meta(const struct btf *btf, u32 btf_id); #else static inline const struct btf_type *btf_type_by_id(const struct btf *btf, u32 type_id) @@ -454,6 +485,10 @@ static inline int register_btf_id_dtor_kfuncs(const struct btf_id_dtor_kfunc *dt { return 0; } +static inline struct btf_struct_meta *btf_find_struct_meta(const struct btf *btf, u32 btf_id) +{ + return NULL; +} #endif static inline bool btf_type_is_struct_ptr(struct btf *btf, const struct btf_type *t) diff --git a/kernel/bpf/btf.c b/kernel/bpf/btf.c index 9a596f430558..a04e10477567 100644 --- a/kernel/bpf/btf.c +++ b/kernel/bpf/btf.c @@ -237,6 +237,7 @@ struct btf { struct rcu_head rcu; struct btf_kfunc_set_tab *kfunc_set_tab; struct btf_id_dtor_kfunc_tab *dtor_kfunc_tab; + struct btf_struct_metas *struct_meta_tab; /* split BTF support */ struct btf *base_btf; @@ -1642,8 +1643,30 @@ static void btf_free_dtor_kfunc_tab(struct btf *btf) btf->dtor_kfunc_tab = NULL; } +static void btf_struct_metas_free(struct btf_struct_metas *tab) +{ + int i; + + if (!tab) + return; + for (i = 0; i < tab->cnt; i++) { + btf_record_free(tab->types[i].record); + kfree(tab->types[i].field_offs); + } + kfree(tab); +} + +static void btf_free_struct_meta_tab(struct btf *btf) +{ + struct btf_struct_metas *tab = btf->struct_meta_tab; + + btf_struct_metas_free(tab); + btf->struct_meta_tab = NULL; +} + static void btf_free(struct btf *btf) { + btf_free_struct_meta_tab(btf); btf_free_dtor_kfunc_tab(btf); btf_free_kfunc_set_tab(btf); kvfree(btf->types); @@ -3353,6 +3376,12 @@ static int btf_get_field_type(const char *name, u32 field_mask, u32 *seen_mask, goto end; } } + if (field_mask & BPF_LIST_NODE) { + if (!strcmp(name, "bpf_list_node")) { + type = BPF_LIST_NODE; + goto end; + } + } /* Only return BPF_KPTR when all other types with matchable names fail */ if (field_mask & BPF_KPTR) { type = BPF_KPTR_REF; @@ -3396,6 +3425,7 @@ static int btf_find_struct_field(const struct btf *btf, switch (field_type) { case BPF_SPIN_LOCK: case BPF_TIMER: + case BPF_LIST_NODE: ret = btf_find_struct(btf, member_type, off, sz, field_type, idx < info_cnt ? &info[idx] : &tmp); if (ret < 0) @@ -3456,6 +3486,7 @@ static int btf_find_datasec_var(const struct btf *btf, const struct btf_type *t, switch (field_type) { case BPF_SPIN_LOCK: case BPF_TIMER: + case BPF_LIST_NODE: ret = btf_find_struct(btf, var_type, off, sz, field_type, idx < info_cnt ? &info[idx] : &tmp); if (ret < 0) @@ -3671,6 +3702,8 @@ struct btf_record *btf_parse_fields(const struct btf *btf, const struct btf_type if (ret < 0) goto end; break; + case BPF_LIST_NODE: + break; default: ret = -EFAULT; goto end; @@ -5141,6 +5174,119 @@ static int btf_parse_hdr(struct btf_verifier_env *env) return btf_check_sec_info(env, btf_data_size); } +static const char *alloc_obj_fields[] = { + "bpf_spin_lock", + "bpf_list_head", + "bpf_list_node", +}; + +static struct btf_struct_metas * +btf_parse_struct_metas(struct bpf_verifier_log *log, struct btf *btf) +{ + union { + struct btf_id_set set; + struct { + u32 _cnt; + u32 _ids[ARRAY_SIZE(alloc_obj_fields)]; + } _arr; + } aof; + struct btf_struct_metas *tab = NULL; + int i, n, id, ret; + + BUILD_BUG_ON(offsetof(struct btf_id_set, cnt) != 0); + BUILD_BUG_ON(sizeof(struct btf_id_set) != sizeof(u32)); + + memset(&aof, 0, sizeof(aof)); + for (i = 0; i < ARRAY_SIZE(alloc_obj_fields); i++) { + /* Try to find whether this special type exists in user BTF, and + * if so remember its ID so we can easily find it among members + * of structs that we iterate in the next loop. + */ + id = btf_find_by_name_kind(btf, alloc_obj_fields[i], BTF_KIND_STRUCT); + if (id < 0) + continue; + aof.set.ids[aof.set.cnt++] = id; + } + + if (!aof.set.cnt) + return NULL; + sort(&aof.set.ids, aof.set.cnt, sizeof(aof.set.ids[0]), btf_id_cmp_func, NULL); + + n = btf_nr_types(btf); + for (i = 1; i < n; i++) { + struct btf_struct_metas *new_tab; + const struct btf_member *member; + struct btf_field_offs *foffs; + struct btf_struct_meta *type; + struct btf_record *record; + const struct btf_type *t; + int j, tab_cnt; + + t = btf_type_by_id(btf, i); + if (!t) { + ret = -EINVAL; + goto free; + } + if (!__btf_type_is_struct(t)) + continue; + + cond_resched(); + + for_each_member(j, t, member) { + if (btf_id_set_contains(&aof.set, member->type)) + goto parse; + } + continue; + parse: + tab_cnt = tab ? tab->cnt : 0; + new_tab = krealloc(tab, offsetof(struct btf_struct_metas, types[tab_cnt + 1]), + GFP_KERNEL | __GFP_NOWARN); + if (!new_tab) { + ret = -ENOMEM; + goto free; + } + if (!tab) + new_tab->cnt = 0; + tab = new_tab; + + type = &tab->types[tab->cnt]; + type->btf_id = i; + record = btf_parse_fields(btf, t, BPF_SPIN_LOCK | BPF_LIST_HEAD | BPF_LIST_NODE, t->size); + /* The record cannot be unset, treat it as an error if so */ + if (IS_ERR_OR_NULL(record)) { + ret = PTR_ERR_OR_ZERO(record) ?: -EFAULT; + goto free; + } + foffs = btf_parse_field_offs(record); + /* We need the field_offs to be valid for a valid record, + * either both should be set or both should be unset. + */ + if (IS_ERR_OR_NULL(foffs)) { + btf_record_free(record); + ret = -EFAULT; + goto free; + } + type->record = record; + type->field_offs = foffs; + tab->cnt++; + } + return tab; +free: + btf_struct_metas_free(tab); + return ERR_PTR(ret); +} + +struct btf_struct_meta *btf_find_struct_meta(const struct btf *btf, u32 btf_id) +{ + struct btf_struct_metas *tab; + + BUILD_BUG_ON(offsetof(struct btf_struct_meta, btf_id) != 0); + tab = btf->struct_meta_tab; + if (!tab) + return NULL; + return bsearch(&btf_id, tab->types, tab->cnt, sizeof(tab->types[0]), btf_id_cmp_func); +} + static int btf_check_type_tags(struct btf_verifier_env *env, struct btf *btf, int start_id) { @@ -5191,6 +5337,7 @@ static int btf_check_type_tags(struct btf_verifier_env *env, static struct btf *btf_parse(bpfptr_t btf_data, u32 btf_data_size, u32 log_level, char __user *log_ubuf, u32 log_size) { + struct btf_struct_metas *struct_meta_tab; struct btf_verifier_env *env = NULL; struct bpf_verifier_log *log; struct btf *btf = NULL; @@ -5259,15 +5406,24 @@ static struct btf *btf_parse(bpfptr_t btf_data, u32 btf_data_size, if (err) goto errout; + struct_meta_tab = btf_parse_struct_metas(log, btf); + if (IS_ERR(struct_meta_tab)) { + err = PTR_ERR(struct_meta_tab); + goto errout; + } + btf->struct_meta_tab = struct_meta_tab; + if (log->level && bpf_verifier_log_full(log)) { err = -ENOSPC; - goto errout; + goto errout_meta; } btf_verifier_env_free(env); refcount_set(&btf->refcnt, 1); return btf; +errout_meta: + btf_free_struct_meta_tab(btf); errout: btf_verifier_env_free(env); if (btf) @@ -6028,6 +6184,28 @@ int btf_struct_access(struct bpf_verifier_log *log, u32 id = reg->btf_id; int err; + while (type_is_alloc(reg->type)) { + struct btf_struct_meta *meta; + struct btf_record *rec; + int i; + + meta = btf_find_struct_meta(btf, id); + if (!meta) + break; + rec = meta->record; + for (i = 0; i < rec->cnt; i++) { + struct btf_field *field = &rec->fields[i]; + u32 offset = field->offset; + if (off < offset + btf_field_type_size(field->type) && offset < off + size) { + bpf_log(log, + "direct access to %s is disallowed\n", + btf_field_type_name(field->type)); + return -EACCES; + } + } + break; + } + t = btf_type_by_id(btf, id); do { err = btf_struct_walk(log, btf, t, off, size, &id, &tmp_flag); @@ -7269,23 +7447,6 @@ bool btf_is_module(const struct btf *btf) return btf->kernel_btf && strcmp(btf->name, "vmlinux") != 0; } -static int btf_id_cmp_func(const void *a, const void *b) -{ - const int *pa = a, *pb = b; - - return *pa - *pb; -} - -bool btf_id_set_contains(const struct btf_id_set *set, u32 id) -{ - return bsearch(&id, set->ids, set->cnt, sizeof(u32), btf_id_cmp_func) != NULL; -} - -static void *btf_id_set8_contains(const struct btf_id_set8 *set, u32 id) -{ - return bsearch(&id, set->pairs, set->cnt, sizeof(set->pairs[0]), btf_id_cmp_func); -} - enum { BTF_MODULE_F_LIVE = (1 << 0), }; diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c index 4c20dcbc6526..56ae97d490f4 100644 --- a/kernel/bpf/syscall.c +++ b/kernel/bpf/syscall.c @@ -537,6 +537,7 @@ void btf_record_free(struct btf_record *rec) btf_put(rec->fields[i].kptr.btf); break; case BPF_LIST_HEAD: + case BPF_LIST_NODE: /* Nothing to release for bpf_list_head */ break; default: @@ -582,6 +583,7 @@ struct btf_record *btf_record_dup(const struct btf_record *rec) } break; case BPF_LIST_HEAD: + case BPF_LIST_NODE: /* Nothing to acquire for bpf_list_head */ break; default: @@ -648,6 +650,8 @@ void bpf_obj_free_fields(const struct btf_record *rec, void *obj) continue; bpf_list_head_free(field, field_ptr, obj + rec->spin_lock_off); break; + case BPF_LIST_NODE: + break; default: WARN_ON_ONCE(1); continue; From 865ce09a49d79d2b2c1d980f4c05ffc0b3517bdc Mon Sep 17 00:00:00 2001 From: Kumar Kartikeya Dwivedi Date: Fri, 18 Nov 2022 07:25:57 +0530 Subject: [PATCH 1007/2002] bpf: Verify ownership relationships for user BTF types Ensure that there can be no ownership cycles among different types by way of having owning objects that can hold some other type as their element. For instance, a map value can only hold allocated objects, but these are allowed to have another bpf_list_head. To prevent unbounded recursion while freeing resources, elements of bpf_list_head in local kptrs can never have a bpf_list_head which are part of list in a map value. Later patches will verify this by having dedicated BTF selftests. Also, to make runtime destruction easier, once btf_struct_metas is fully populated, we can stash the metadata of the value type directly in the metadata of the list_head fields, as that allows easier access to the value type's layout to destruct it at runtime from the btf_field entry of the list head itself. Signed-off-by: Kumar Kartikeya Dwivedi Link: https://lore.kernel.org/r/20221118015614.2013203-8-memxor@gmail.com Signed-off-by: Alexei Starovoitov --- include/linux/bpf.h | 1 + include/linux/btf.h | 1 + kernel/bpf/btf.c | 71 ++++++++++++++++++++++++++++++++++++++++++++ kernel/bpf/syscall.c | 4 +++ 4 files changed, 77 insertions(+) diff --git a/include/linux/bpf.h b/include/linux/bpf.h index eb6ea53fa5a2..323985a39ece 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -191,6 +191,7 @@ struct btf_field_list_head { struct btf *btf; u32 value_btf_id; u32 node_offset; + struct btf_record *value_rec; }; struct btf_field { diff --git a/include/linux/btf.h b/include/linux/btf.h index a01a8da20021..42d8f3730a8d 100644 --- a/include/linux/btf.h +++ b/include/linux/btf.h @@ -178,6 +178,7 @@ int btf_find_spin_lock(const struct btf *btf, const struct btf_type *t); int btf_find_timer(const struct btf *btf, const struct btf_type *t); struct btf_record *btf_parse_fields(const struct btf *btf, const struct btf_type *t, u32 field_mask, u32 value_size); +int btf_check_and_fixup_fields(const struct btf *btf, struct btf_record *rec); struct btf_field_offs *btf_parse_field_offs(struct btf_record *rec); bool btf_type_is_void(const struct btf_type *t); s32 btf_find_by_name_kind(const struct btf *btf, const char *name, u8 kind); diff --git a/kernel/bpf/btf.c b/kernel/bpf/btf.c index a04e10477567..91aa9c96621f 100644 --- a/kernel/bpf/btf.c +++ b/kernel/bpf/btf.c @@ -3723,6 +3723,67 @@ end: return ERR_PTR(ret); } +int btf_check_and_fixup_fields(const struct btf *btf, struct btf_record *rec) +{ + int i; + + /* There are two owning types, kptr_ref and bpf_list_head. The former + * only supports storing kernel types, which can never store references + * to program allocated local types, atleast not yet. Hence we only need + * to ensure that bpf_list_head ownership does not form cycles. + */ + if (IS_ERR_OR_NULL(rec) || !(rec->field_mask & BPF_LIST_HEAD)) + return 0; + for (i = 0; i < rec->cnt; i++) { + struct btf_struct_meta *meta; + u32 btf_id; + + if (!(rec->fields[i].type & BPF_LIST_HEAD)) + continue; + btf_id = rec->fields[i].list_head.value_btf_id; + meta = btf_find_struct_meta(btf, btf_id); + if (!meta) + return -EFAULT; + rec->fields[i].list_head.value_rec = meta->record; + + if (!(rec->field_mask & BPF_LIST_NODE)) + continue; + + /* We need to ensure ownership acyclicity among all types. The + * proper way to do it would be to topologically sort all BTF + * IDs based on the ownership edges, since there can be multiple + * bpf_list_head in a type. Instead, we use the following + * reasoning: + * + * - A type can only be owned by another type in user BTF if it + * has a bpf_list_node. + * - A type can only _own_ another type in user BTF if it has a + * bpf_list_head. + * + * We ensure that if a type has both bpf_list_head and + * bpf_list_node, its element types cannot be owning types. + * + * To ensure acyclicity: + * + * When A only has bpf_list_head, ownership chain can be: + * A -> B -> C + * Where: + * - B has both bpf_list_head and bpf_list_node. + * - C only has bpf_list_node. + * + * When A has both bpf_list_head and bpf_list_node, some other + * type already owns it in the BTF domain, hence it can not own + * another owning type through any of the bpf_list_head edges. + * A -> B + * Where: + * - B only has bpf_list_node. + */ + if (meta->record->field_mask & BPF_LIST_HEAD) + return -ELOOP; + } + return 0; +} + static int btf_field_offs_cmp(const void *_a, const void *_b, const void *priv) { const u32 a = *(const u32 *)_a; @@ -5413,6 +5474,16 @@ static struct btf *btf_parse(bpfptr_t btf_data, u32 btf_data_size, } btf->struct_meta_tab = struct_meta_tab; + if (struct_meta_tab) { + int i; + + for (i = 0; i < struct_meta_tab->cnt; i++) { + err = btf_check_and_fixup_fields(btf, struct_meta_tab->types[i].record); + if (err < 0) + goto errout_meta; + } + } + if (log->level && bpf_verifier_log_full(log)) { err = -ENOSPC; goto errout_meta; diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c index 56ae97d490f4..6140cbc3ed8a 100644 --- a/kernel/bpf/syscall.c +++ b/kernel/bpf/syscall.c @@ -1054,6 +1054,10 @@ static int map_check_btf(struct bpf_map *map, const struct btf *btf, } } + ret = btf_check_and_fixup_fields(btf, map->record); + if (ret < 0) + goto free_map_tab; + if (map->ops->map_check_btf) { ret = map->ops->map_check_btf(map, btf, key_type, value_type); if (ret < 0) From 4e814da0d59917c6d758a80e63e79b5ee212cf11 Mon Sep 17 00:00:00 2001 From: Kumar Kartikeya Dwivedi Date: Fri, 18 Nov 2022 07:25:58 +0530 Subject: [PATCH 1008/2002] bpf: Allow locking bpf_spin_lock in allocated objects Allow locking a bpf_spin_lock in an allocated object, in addition to already supported map value pointers. The handling is similar to that of map values, by just preserving the reg->id of PTR_TO_BTF_ID | MEM_ALLOC as well, and adjusting process_spin_lock to work with them and remember the id in verifier state. Refactor the existing process_spin_lock to work with PTR_TO_BTF_ID | MEM_ALLOC in addition to PTR_TO_MAP_VALUE. We need to update the reg_may_point_to_spin_lock which is used in mark_ptr_or_null_reg to preserve reg->id, that will be used in env->cur_state->active_spin_lock to remember the currently held spin lock. Also update the comment describing bpf_spin_lock implementation details to also talk about PTR_TO_BTF_ID | MEM_ALLOC type. Signed-off-by: Kumar Kartikeya Dwivedi Link: https://lore.kernel.org/r/20221118015614.2013203-9-memxor@gmail.com Signed-off-by: Alexei Starovoitov --- kernel/bpf/helpers.c | 2 + kernel/bpf/verifier.c | 92 +++++++++++++++++++++++++++++++------------ 2 files changed, 68 insertions(+), 26 deletions(-) diff --git a/kernel/bpf/helpers.c b/kernel/bpf/helpers.c index 7bc71995f17c..5bc0b9f0f306 100644 --- a/kernel/bpf/helpers.c +++ b/kernel/bpf/helpers.c @@ -336,6 +336,7 @@ const struct bpf_func_proto bpf_spin_lock_proto = { .gpl_only = false, .ret_type = RET_VOID, .arg1_type = ARG_PTR_TO_SPIN_LOCK, + .arg1_btf_id = BPF_PTR_POISON, }; static inline void __bpf_spin_unlock_irqrestore(struct bpf_spin_lock *lock) @@ -358,6 +359,7 @@ const struct bpf_func_proto bpf_spin_unlock_proto = { .gpl_only = false, .ret_type = RET_VOID, .arg1_type = ARG_PTR_TO_SPIN_LOCK, + .arg1_btf_id = BPF_PTR_POISON, }; void copy_map_value_locked(struct bpf_map *map, void *dst, void *src, diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 49e08c1c2c61..19467dda5dd9 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -451,10 +451,24 @@ static bool reg_type_not_null(enum bpf_reg_type type) type == PTR_TO_SOCK_COMMON; } +static struct btf_record *reg_btf_record(const struct bpf_reg_state *reg) +{ + struct btf_record *rec = NULL; + struct btf_struct_meta *meta; + + if (reg->type == PTR_TO_MAP_VALUE) { + rec = reg->map_ptr->record; + } else if (reg->type == (PTR_TO_BTF_ID | MEM_ALLOC)) { + meta = btf_find_struct_meta(reg->btf, reg->btf_id); + if (meta) + rec = meta->record; + } + return rec; +} + static bool reg_may_point_to_spin_lock(const struct bpf_reg_state *reg) { - return reg->type == PTR_TO_MAP_VALUE && - btf_record_has_field(reg->map_ptr->record, BPF_SPIN_LOCK); + return btf_record_has_field(reg_btf_record(reg), BPF_SPIN_LOCK); } static bool type_is_rdonly_mem(u32 type) @@ -5564,23 +5578,26 @@ int check_kfunc_mem_size_reg(struct bpf_verifier_env *env, struct bpf_reg_state } /* Implementation details: - * bpf_map_lookup returns PTR_TO_MAP_VALUE_OR_NULL + * bpf_map_lookup returns PTR_TO_MAP_VALUE_OR_NULL. + * bpf_obj_new returns PTR_TO_BTF_ID | MEM_ALLOC | PTR_MAYBE_NULL. * Two bpf_map_lookups (even with the same key) will have different reg->id. - * For traditional PTR_TO_MAP_VALUE the verifier clears reg->id after - * value_or_null->value transition, since the verifier only cares about - * the range of access to valid map value pointer and doesn't care about actual - * address of the map element. + * Two separate bpf_obj_new will also have different reg->id. + * For traditional PTR_TO_MAP_VALUE or PTR_TO_BTF_ID | MEM_ALLOC, the verifier + * clears reg->id after value_or_null->value transition, since the verifier only + * cares about the range of access to valid map value pointer and doesn't care + * about actual address of the map element. * For maps with 'struct bpf_spin_lock' inside map value the verifier keeps * reg->id > 0 after value_or_null->value transition. By doing so * two bpf_map_lookups will be considered two different pointers that - * point to different bpf_spin_locks. + * point to different bpf_spin_locks. Likewise for pointers to allocated objects + * returned from bpf_obj_new. * The verifier allows taking only one bpf_spin_lock at a time to avoid * dead-locks. * Since only one bpf_spin_lock is allowed the checks are simpler than * reg_is_refcounted() logic. The verifier needs to remember only * one spin_lock instead of array of acquired_refs. - * cur_state->active_spin_lock remembers which map value element got locked - * and clears it after bpf_spin_unlock. + * cur_state->active_spin_lock remembers which map value element or allocated + * object got locked and clears it after bpf_spin_unlock. */ static int process_spin_lock(struct bpf_verifier_env *env, int regno, bool is_lock) @@ -5588,8 +5605,10 @@ static int process_spin_lock(struct bpf_verifier_env *env, int regno, struct bpf_reg_state *regs = cur_regs(env), *reg = ®s[regno]; struct bpf_verifier_state *cur = env->cur_state; bool is_const = tnum_is_const(reg->var_off); - struct bpf_map *map = reg->map_ptr; u64 val = reg->var_off.value; + struct bpf_map *map = NULL; + struct btf *btf = NULL; + struct btf_record *rec; if (!is_const) { verbose(env, @@ -5597,19 +5616,27 @@ static int process_spin_lock(struct bpf_verifier_env *env, int regno, regno); return -EINVAL; } - if (!map->btf) { - verbose(env, - "map '%s' has to have BTF in order to use bpf_spin_lock\n", - map->name); + if (reg->type == PTR_TO_MAP_VALUE) { + map = reg->map_ptr; + if (!map->btf) { + verbose(env, + "map '%s' has to have BTF in order to use bpf_spin_lock\n", + map->name); + return -EINVAL; + } + } else { + btf = reg->btf; + } + + rec = reg_btf_record(reg); + if (!btf_record_has_field(rec, BPF_SPIN_LOCK)) { + verbose(env, "%s '%s' has no valid bpf_spin_lock\n", map ? "map" : "local", + map ? map->name : "kptr"); return -EINVAL; } - if (!btf_record_has_field(map->record, BPF_SPIN_LOCK)) { - verbose(env, "map '%s' has no valid bpf_spin_lock\n", map->name); - return -EINVAL; - } - if (map->record->spin_lock_off != val + reg->off) { + if (rec->spin_lock_off != val + reg->off) { verbose(env, "off %lld doesn't point to 'struct bpf_spin_lock' that is at %d\n", - val + reg->off, map->record->spin_lock_off); + val + reg->off, rec->spin_lock_off); return -EINVAL; } if (is_lock) { @@ -5815,13 +5842,19 @@ static const struct bpf_reg_types int_ptr_types = { }, }; +static const struct bpf_reg_types spin_lock_types = { + .types = { + PTR_TO_MAP_VALUE, + PTR_TO_BTF_ID | MEM_ALLOC, + } +}; + static const struct bpf_reg_types fullsock_types = { .types = { PTR_TO_SOCKET } }; static const struct bpf_reg_types scalar_types = { .types = { SCALAR_VALUE } }; static const struct bpf_reg_types context_types = { .types = { PTR_TO_CTX } }; static const struct bpf_reg_types ringbuf_mem_types = { .types = { PTR_TO_MEM | MEM_RINGBUF } }; static const struct bpf_reg_types const_map_ptr_types = { .types = { CONST_PTR_TO_MAP } }; static const struct bpf_reg_types btf_ptr_types = { .types = { PTR_TO_BTF_ID } }; -static const struct bpf_reg_types spin_lock_types = { .types = { PTR_TO_MAP_VALUE } }; static const struct bpf_reg_types percpu_btf_ptr_types = { .types = { PTR_TO_BTF_ID | MEM_PERCPU } }; static const struct bpf_reg_types func_ptr_types = { .types = { PTR_TO_FUNC } }; static const struct bpf_reg_types stack_ptr_types = { .types = { PTR_TO_STACK } }; @@ -5946,6 +5979,11 @@ found: return -EACCES; } } + } else if (type_is_alloc(reg->type)) { + if (meta->func_id != BPF_FUNC_spin_lock && meta->func_id != BPF_FUNC_spin_unlock) { + verbose(env, "verifier internal error: unimplemented handling of MEM_ALLOC\n"); + return -EFAULT; + } } return 0; @@ -6062,7 +6100,8 @@ static int check_func_arg(struct bpf_verifier_env *env, u32 arg, goto skip_type_check; /* arg_btf_id and arg_size are in a union. */ - if (base_type(arg_type) == ARG_PTR_TO_BTF_ID) + if (base_type(arg_type) == ARG_PTR_TO_BTF_ID || + base_type(arg_type) == ARG_PTR_TO_SPIN_LOCK) arg_btf_id = fn->arg_btf_id[arg]; err = check_reg_type(env, regno, arg_type, arg_btf_id, meta); @@ -6680,9 +6719,10 @@ static bool check_btf_id_ok(const struct bpf_func_proto *fn) int i; for (i = 0; i < ARRAY_SIZE(fn->arg_type); i++) { - if (base_type(fn->arg_type[i]) == ARG_PTR_TO_BTF_ID && !fn->arg_btf_id[i]) - return false; - + if (base_type(fn->arg_type[i]) == ARG_PTR_TO_BTF_ID) + return !!fn->arg_btf_id[i]; + if (base_type(fn->arg_type[i]) == ARG_PTR_TO_SPIN_LOCK) + return fn->arg_btf_id[i] == BPF_PTR_POISON; if (base_type(fn->arg_type[i]) != ARG_PTR_TO_BTF_ID && fn->arg_btf_id[i] && /* arg_btf_id and arg_size are in a union. */ (base_type(fn->arg_type[i]) != ARG_PTR_TO_MEM || From d0d78c1df9b1dbfb5e172de473561ce09d5e9d39 Mon Sep 17 00:00:00 2001 From: Kumar Kartikeya Dwivedi Date: Fri, 18 Nov 2022 07:25:59 +0530 Subject: [PATCH 1009/2002] bpf: Allow locking bpf_spin_lock global variables Global variables reside in maps accessible using direct_value_addr callbacks, so giving each load instruction's rewrite a unique reg->id disallows us from holding locks which are global. The reason for preserving reg->id as a unique value for registers that may point to spin lock is that two separate lookups are treated as two separate memory regions, and any possible aliasing is ignored for the purposes of spin lock correctness. This is not great especially for the global variable case, which are served from maps that have max_entries == 1, i.e. they always lead to map values pointing into the same map value. So refactor the active_spin_lock into a 'active_lock' structure which represents the lock identity, and instead of the reg->id, remember two fields, a pointer and the reg->id. The pointer will store reg->map_ptr or reg->btf. It's only necessary to distinguish for the id == 0 case of global variables, but always setting the pointer to a non-NULL value and using the pointer to check whether the lock is held simplifies code in the verifier. This is generic enough to allow it for global variables, map lookups, and allocated objects at the same time. Note that while whether a lock is held can be answered by just comparing active_lock.ptr to NULL, to determine whether the register is pointing to the same held lock requires comparing _both_ ptr and id. Finally, as a result of this refactoring, pseudo load instructions are not given a unique reg->id, as they are doing lookup for the same map value (max_entries is never greater than 1). Essentially, we consider that the tuple of (ptr, id) will always be unique for any kind of argument to bpf_spin_{lock,unlock}. Note that this can be extended in the future to also remember offset used for locking, so that we can introduce multiple bpf_spin_lock fields in the same allocation. Signed-off-by: Kumar Kartikeya Dwivedi Link: https://lore.kernel.org/r/20221118015614.2013203-10-memxor@gmail.com Signed-off-by: Alexei Starovoitov --- include/linux/bpf_verifier.h | 16 +++++++++++++- kernel/bpf/verifier.c | 41 ++++++++++++++++++++++++------------ 2 files changed, 43 insertions(+), 14 deletions(-) diff --git a/include/linux/bpf_verifier.h b/include/linux/bpf_verifier.h index 1a32baa78ce2..1db2b4dc7009 100644 --- a/include/linux/bpf_verifier.h +++ b/include/linux/bpf_verifier.h @@ -323,7 +323,21 @@ struct bpf_verifier_state { u32 branches; u32 insn_idx; u32 curframe; - u32 active_spin_lock; + /* For every reg representing a map value or allocated object pointer, + * we consider the tuple of (ptr, id) for them to be unique in verifier + * context and conside them to not alias each other for the purposes of + * tracking lock state. + */ + struct { + /* This can either be reg->map_ptr or reg->btf. If ptr is NULL, + * there's no active lock held, and other fields have no + * meaning. If non-NULL, it indicates that a lock is held and + * id member has the reg->id of the register which can be >= 0. + */ + void *ptr; + /* This will be reg->id */ + u32 id; + } active_lock; bool speculative; /* first and last insn idx of this verifier state */ diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 19467dda5dd9..c8f3abe9b08e 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -1221,7 +1221,8 @@ static int copy_verifier_state(struct bpf_verifier_state *dst_state, } dst_state->speculative = src->speculative; dst_state->curframe = src->curframe; - dst_state->active_spin_lock = src->active_spin_lock; + dst_state->active_lock.ptr = src->active_lock.ptr; + dst_state->active_lock.id = src->active_lock.id; dst_state->branches = src->branches; dst_state->parent = src->parent; dst_state->first_insn_idx = src->first_insn_idx; @@ -5596,7 +5597,7 @@ int check_kfunc_mem_size_reg(struct bpf_verifier_env *env, struct bpf_reg_state * Since only one bpf_spin_lock is allowed the checks are simpler than * reg_is_refcounted() logic. The verifier needs to remember only * one spin_lock instead of array of acquired_refs. - * cur_state->active_spin_lock remembers which map value element or allocated + * cur_state->active_lock remembers which map value element or allocated * object got locked and clears it after bpf_spin_unlock. */ static int process_spin_lock(struct bpf_verifier_env *env, int regno, @@ -5640,22 +5641,35 @@ static int process_spin_lock(struct bpf_verifier_env *env, int regno, return -EINVAL; } if (is_lock) { - if (cur->active_spin_lock) { + if (cur->active_lock.ptr) { verbose(env, "Locking two bpf_spin_locks are not allowed\n"); return -EINVAL; } - cur->active_spin_lock = reg->id; + if (map) + cur->active_lock.ptr = map; + else + cur->active_lock.ptr = btf; + cur->active_lock.id = reg->id; } else { - if (!cur->active_spin_lock) { + void *ptr; + + if (map) + ptr = map; + else + ptr = btf; + + if (!cur->active_lock.ptr) { verbose(env, "bpf_spin_unlock without taking a lock\n"); return -EINVAL; } - if (cur->active_spin_lock != reg->id) { + if (cur->active_lock.ptr != ptr || + cur->active_lock.id != reg->id) { verbose(env, "bpf_spin_unlock of different lock\n"); return -EINVAL; } - cur->active_spin_lock = 0; + cur->active_lock.ptr = NULL; + cur->active_lock.id = 0; } return 0; } @@ -10617,8 +10631,8 @@ static int check_ld_imm(struct bpf_verifier_env *env, struct bpf_insn *insn) insn->src_reg == BPF_PSEUDO_MAP_IDX_VALUE) { dst_reg->type = PTR_TO_MAP_VALUE; dst_reg->off = aux->map_off; - if (btf_record_has_field(map->record, BPF_SPIN_LOCK)) - dst_reg->id = ++env->id_gen; + WARN_ON_ONCE(map->max_entries != 1); + /* We want reg->id to be same (0) as map_value is not distinct */ } else if (insn->src_reg == BPF_PSEUDO_MAP_FD || insn->src_reg == BPF_PSEUDO_MAP_IDX) { dst_reg->type = CONST_PTR_TO_MAP; @@ -10696,7 +10710,7 @@ static int check_ld_abs(struct bpf_verifier_env *env, struct bpf_insn *insn) return err; } - if (env->cur_state->active_spin_lock) { + if (env->cur_state->active_lock.ptr) { verbose(env, "BPF_LD_[ABS|IND] cannot be used inside bpf_spin_lock-ed region\n"); return -EINVAL; } @@ -11962,7 +11976,8 @@ static bool states_equal(struct bpf_verifier_env *env, if (old->speculative && !cur->speculative) return false; - if (old->active_spin_lock != cur->active_spin_lock) + if (old->active_lock.ptr != cur->active_lock.ptr || + old->active_lock.id != cur->active_lock.id) return false; /* for states to be equal callsites have to be the same @@ -12607,7 +12622,7 @@ static int do_check(struct bpf_verifier_env *env) return -EINVAL; } - if (env->cur_state->active_spin_lock && + if (env->cur_state->active_lock.ptr && (insn->src_reg == BPF_PSEUDO_CALL || insn->imm != BPF_FUNC_spin_unlock)) { verbose(env, "function calls are not allowed while holding a lock\n"); @@ -12644,7 +12659,7 @@ static int do_check(struct bpf_verifier_env *env) return -EINVAL; } - if (env->cur_state->active_spin_lock) { + if (env->cur_state->active_lock.ptr) { verbose(env, "bpf_spin_unlock is missing\n"); return -EINVAL; } From b7ff97925b55a0603a7c215305df4b43ab632948 Mon Sep 17 00:00:00 2001 From: Kumar Kartikeya Dwivedi Date: Fri, 18 Nov 2022 07:26:00 +0530 Subject: [PATCH 1010/2002] bpf: Allow locking bpf_spin_lock in inner map values There is no need to restrict users from locking bpf_spin_lock in map values of inner maps. Each inner map lookup gets a unique reg->id assigned to the returned PTR_TO_MAP_VALUE which will be preserved after the NULL check. Distinct lookups into different inner map get unique IDs, and distinct lookups into same inner map also get unique IDs. Hence, lift the restriction by removing the check return -ENOTSUPP in map_in_map.c. Later commits will add comprehensive test cases to ensure that invalid cases are rejected. Signed-off-by: Kumar Kartikeya Dwivedi Link: https://lore.kernel.org/r/20221118015614.2013203-11-memxor@gmail.com Signed-off-by: Alexei Starovoitov --- kernel/bpf/map_in_map.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/kernel/bpf/map_in_map.c b/kernel/bpf/map_in_map.c index fae6a6c33e2d..7cce2047c6ef 100644 --- a/kernel/bpf/map_in_map.c +++ b/kernel/bpf/map_in_map.c @@ -30,11 +30,6 @@ struct bpf_map *bpf_map_meta_alloc(int inner_map_ufd) goto put; } - if (btf_record_has_field(inner_map->record, BPF_SPIN_LOCK)) { - ret = -ENOTSUPP; - goto put; - } - inner_map_meta_size = sizeof(*inner_map_meta); /* In some cases verifier needs to access beyond just base map. */ if (inner_map->ops == &array_map_ops) From 00b85860feb809852af9a88cb4ca8766d7dff6a3 Mon Sep 17 00:00:00 2001 From: Kumar Kartikeya Dwivedi Date: Fri, 18 Nov 2022 07:26:01 +0530 Subject: [PATCH 1011/2002] bpf: Rewrite kfunc argument handling As we continue to add more features, argument types, kfunc flags, and different extensions to kfuncs, the code to verify the correctness of the kfunc prototype wrt the passed in registers has become ad-hoc and ugly to read. To make life easier, and make a very clear split between different stages of argument processing, move all the code into verifier.c and refactor into easier to read helpers and functions. This also makes sharing code within the verifier easier with kfunc argument processing. This will be more and more useful in later patches as we are now moving to implement very core BPF helpers as kfuncs, to keep them experimental before baking into UAPI. Remove all kfunc related bits now from btf_check_func_arg_match, as users have been converted away to refactored kfunc argument handling. Signed-off-by: Kumar Kartikeya Dwivedi Link: https://lore.kernel.org/r/20221118015614.2013203-12-memxor@gmail.com Signed-off-by: Alexei Starovoitov --- include/linux/bpf.h | 11 - include/linux/bpf_verifier.h | 2 - include/linux/btf.h | 31 +- kernel/bpf/btf.c | 380 +----------- kernel/bpf/verifier.c | 545 +++++++++++++++++- .../bpf/prog_tests/kfunc_dynptr_param.c | 2 +- tools/testing/selftests/bpf/verifier/calls.c | 2 +- .../selftests/bpf/verifier/ref_tracking.c | 4 +- 8 files changed, 573 insertions(+), 404 deletions(-) diff --git a/include/linux/bpf.h b/include/linux/bpf.h index 323985a39ece..0a74df731eb8 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -2109,22 +2109,11 @@ int btf_distill_func_proto(struct bpf_verifier_log *log, const char *func_name, struct btf_func_model *m); -struct bpf_kfunc_arg_meta { - u64 r0_size; - bool r0_rdonly; - int ref_obj_id; - u32 flags; -}; - struct bpf_reg_state; int btf_check_subprog_arg_match(struct bpf_verifier_env *env, int subprog, struct bpf_reg_state *regs); int btf_check_subprog_call(struct bpf_verifier_env *env, int subprog, struct bpf_reg_state *regs); -int btf_check_kfunc_arg_match(struct bpf_verifier_env *env, - const struct btf *btf, u32 func_id, - struct bpf_reg_state *regs, - struct bpf_kfunc_arg_meta *meta); int btf_prepare_func_args(struct bpf_verifier_env *env, int subprog, struct bpf_reg_state *reg); int btf_check_type_match(struct bpf_verifier_log *log, const struct bpf_prog *prog, diff --git a/include/linux/bpf_verifier.h b/include/linux/bpf_verifier.h index 1db2b4dc7009..fb146b0ce006 100644 --- a/include/linux/bpf_verifier.h +++ b/include/linux/bpf_verifier.h @@ -603,8 +603,6 @@ int check_ptr_off_reg(struct bpf_verifier_env *env, int check_func_arg_reg_off(struct bpf_verifier_env *env, const struct bpf_reg_state *reg, int regno, enum bpf_arg_type arg_type); -int check_kfunc_mem_size_reg(struct bpf_verifier_env *env, struct bpf_reg_state *reg, - u32 regno); int check_mem_reg(struct bpf_verifier_env *env, struct bpf_reg_state *reg, u32 regno, u32 mem_size); bool is_dynptr_reg_valid_init(struct bpf_verifier_env *env, diff --git a/include/linux/btf.h b/include/linux/btf.h index 42d8f3730a8d..d5b26380a60f 100644 --- a/include/linux/btf.h +++ b/include/linux/btf.h @@ -338,6 +338,16 @@ static inline bool btf_type_is_struct(const struct btf_type *t) return kind == BTF_KIND_STRUCT || kind == BTF_KIND_UNION; } +static inline bool __btf_type_is_struct(const struct btf_type *t) +{ + return BTF_INFO_KIND(t->info) == BTF_KIND_STRUCT; +} + +static inline bool btf_type_is_array(const struct btf_type *t) +{ + return BTF_INFO_KIND(t->info) == BTF_KIND_ARRAY; +} + static inline u16 btf_type_vlen(const struct btf_type *t) { return BTF_INFO_VLEN(t->info); @@ -439,9 +449,10 @@ static inline void *btf_id_set8_contains(const struct btf_id_set8 *set, u32 id) return bsearch(&id, set->pairs, set->cnt, sizeof(set->pairs[0]), btf_id_cmp_func); } -#ifdef CONFIG_BPF_SYSCALL struct bpf_prog; +struct bpf_verifier_log; +#ifdef CONFIG_BPF_SYSCALL const struct btf_type *btf_type_by_id(const struct btf *btf, u32 type_id); const char *btf_name_by_offset(const struct btf *btf, u32 offset); struct btf *btf_parse_vmlinux(void); @@ -455,6 +466,12 @@ s32 btf_find_dtor_kfunc(struct btf *btf, u32 btf_id); int register_btf_id_dtor_kfuncs(const struct btf_id_dtor_kfunc *dtors, u32 add_cnt, struct module *owner); struct btf_struct_meta *btf_find_struct_meta(const struct btf *btf, u32 btf_id); +const struct btf_member * +btf_get_prog_ctx_type(struct bpf_verifier_log *log, const struct btf *btf, + const struct btf_type *t, enum bpf_prog_type prog_type, + int arg); +bool btf_types_are_same(const struct btf *btf1, u32 id1, + const struct btf *btf2, u32 id2); #else static inline const struct btf_type *btf_type_by_id(const struct btf *btf, u32 type_id) @@ -490,6 +507,18 @@ static inline struct btf_struct_meta *btf_find_struct_meta(const struct btf *btf { return NULL; } +static inline const struct btf_member * +btf_get_prog_ctx_type(struct bpf_verifier_log *log, const struct btf *btf, + const struct btf_type *t, enum bpf_prog_type prog_type, + int arg) +{ + return NULL; +} +static inline bool btf_types_are_same(const struct btf *btf1, u32 id1, + const struct btf *btf2, u32 id2) +{ + return false; +} #endif static inline bool btf_type_is_struct_ptr(struct btf *btf, const struct btf_type *t) diff --git a/kernel/bpf/btf.c b/kernel/bpf/btf.c index 91aa9c96621f..4dcda4ae48c1 100644 --- a/kernel/bpf/btf.c +++ b/kernel/bpf/btf.c @@ -478,16 +478,6 @@ static bool btf_type_nosize_or_null(const struct btf_type *t) return !t || btf_type_nosize(t); } -static bool __btf_type_is_struct(const struct btf_type *t) -{ - return BTF_INFO_KIND(t->info) == BTF_KIND_STRUCT; -} - -static bool btf_type_is_array(const struct btf_type *t) -{ - return BTF_INFO_KIND(t->info) == BTF_KIND_ARRAY; -} - static bool btf_type_is_datasec(const struct btf_type *t) { return BTF_INFO_KIND(t->info) == BTF_KIND_DATASEC; @@ -5536,7 +5526,7 @@ static u8 bpf_ctx_convert_map[] = { #undef BPF_MAP_TYPE #undef BPF_LINK_TYPE -static const struct btf_member * +const struct btf_member * btf_get_prog_ctx_type(struct bpf_verifier_log *log, const struct btf *btf, const struct btf_type *t, enum bpf_prog_type prog_type, int arg) @@ -6322,8 +6312,8 @@ int btf_struct_access(struct bpf_verifier_log *log, * end up with two different module BTFs, but IDs point to the common type in * vmlinux BTF. */ -static bool btf_types_are_same(const struct btf *btf1, u32 id1, - const struct btf *btf2, u32 id2) +bool btf_types_are_same(const struct btf *btf1, u32 id1, + const struct btf *btf2, u32 id2) { if (id1 != id2) return false; @@ -6605,122 +6595,19 @@ int btf_check_type_match(struct bpf_verifier_log *log, const struct bpf_prog *pr return btf_check_func_type_match(log, btf1, t1, btf2, t2); } -static u32 *reg2btf_ids[__BPF_REG_TYPE_MAX] = { -#ifdef CONFIG_NET - [PTR_TO_SOCKET] = &btf_sock_ids[BTF_SOCK_TYPE_SOCK], - [PTR_TO_SOCK_COMMON] = &btf_sock_ids[BTF_SOCK_TYPE_SOCK_COMMON], - [PTR_TO_TCP_SOCK] = &btf_sock_ids[BTF_SOCK_TYPE_TCP], -#endif -}; - -/* Returns true if struct is composed of scalars, 4 levels of nesting allowed */ -static bool __btf_type_is_scalar_struct(struct bpf_verifier_log *log, - const struct btf *btf, - const struct btf_type *t, int rec) -{ - const struct btf_type *member_type; - const struct btf_member *member; - u32 i; - - if (!btf_type_is_struct(t)) - return false; - - for_each_member(i, t, member) { - const struct btf_array *array; - - member_type = btf_type_skip_modifiers(btf, member->type, NULL); - if (btf_type_is_struct(member_type)) { - if (rec >= 3) { - bpf_log(log, "max struct nesting depth exceeded\n"); - return false; - } - if (!__btf_type_is_scalar_struct(log, btf, member_type, rec + 1)) - return false; - continue; - } - if (btf_type_is_array(member_type)) { - array = btf_type_array(member_type); - if (!array->nelems) - return false; - member_type = btf_type_skip_modifiers(btf, array->type, NULL); - if (!btf_type_is_scalar(member_type)) - return false; - continue; - } - if (!btf_type_is_scalar(member_type)) - return false; - } - return true; -} - -static bool is_kfunc_arg_mem_size(const struct btf *btf, - const struct btf_param *arg, - const struct bpf_reg_state *reg) -{ - int len, sfx_len = sizeof("__sz") - 1; - const struct btf_type *t; - const char *param_name; - - t = btf_type_skip_modifiers(btf, arg->type, NULL); - if (!btf_type_is_scalar(t) || reg->type != SCALAR_VALUE) - return false; - - /* In the future, this can be ported to use BTF tagging */ - param_name = btf_name_by_offset(btf, arg->name_off); - if (str_is_empty(param_name)) - return false; - len = strlen(param_name); - if (len < sfx_len) - return false; - param_name += len - sfx_len; - if (strncmp(param_name, "__sz", sfx_len)) - return false; - - return true; -} - -static bool btf_is_kfunc_arg_mem_size(const struct btf *btf, - const struct btf_param *arg, - const struct bpf_reg_state *reg, - const char *name) -{ - int len, target_len = strlen(name); - const struct btf_type *t; - const char *param_name; - - t = btf_type_skip_modifiers(btf, arg->type, NULL); - if (!btf_type_is_scalar(t) || reg->type != SCALAR_VALUE) - return false; - - param_name = btf_name_by_offset(btf, arg->name_off); - if (str_is_empty(param_name)) - return false; - len = strlen(param_name); - if (len != target_len) - return false; - if (strcmp(param_name, name)) - return false; - - return true; -} - static int btf_check_func_arg_match(struct bpf_verifier_env *env, const struct btf *btf, u32 func_id, struct bpf_reg_state *regs, bool ptr_to_mem_ok, - struct bpf_kfunc_arg_meta *kfunc_meta, bool processing_call) { enum bpf_prog_type prog_type = resolve_prog_type(env->prog); - bool rel = false, kptr_get = false, trusted_args = false; - bool sleepable = false; struct bpf_verifier_log *log = &env->log; - u32 i, nargs, ref_id, ref_obj_id = 0; - bool is_kfunc = btf_is_kernel(btf); const char *func_name, *ref_tname; const struct btf_type *t, *ref_t; const struct btf_param *args; - int ref_regno = 0, ret; + u32 i, nargs, ref_id; + int ret; t = btf_type_by_id(btf, func_id); if (!t || !btf_type_is_func(t)) { @@ -6746,14 +6633,6 @@ static int btf_check_func_arg_match(struct bpf_verifier_env *env, return -EINVAL; } - if (is_kfunc && kfunc_meta) { - /* Only kfunc can be release func */ - rel = kfunc_meta->flags & KF_RELEASE; - kptr_get = kfunc_meta->flags & KF_KPTR_GET; - trusted_args = kfunc_meta->flags & KF_TRUSTED_ARGS; - sleepable = kfunc_meta->flags & KF_SLEEPABLE; - } - /* check that BTF function arguments match actual types that the * verifier sees. */ @@ -6761,42 +6640,9 @@ static int btf_check_func_arg_match(struct bpf_verifier_env *env, enum bpf_arg_type arg_type = ARG_DONTCARE; u32 regno = i + 1; struct bpf_reg_state *reg = ®s[regno]; - bool obj_ptr = false; t = btf_type_skip_modifiers(btf, args[i].type, NULL); if (btf_type_is_scalar(t)) { - if (is_kfunc && kfunc_meta) { - bool is_buf_size = false; - - /* check for any const scalar parameter of name "rdonly_buf_size" - * or "rdwr_buf_size" - */ - if (btf_is_kfunc_arg_mem_size(btf, &args[i], reg, - "rdonly_buf_size")) { - kfunc_meta->r0_rdonly = true; - is_buf_size = true; - } else if (btf_is_kfunc_arg_mem_size(btf, &args[i], reg, - "rdwr_buf_size")) - is_buf_size = true; - - if (is_buf_size) { - if (kfunc_meta->r0_size) { - bpf_log(log, "2 or more rdonly/rdwr_buf_size parameters for kfunc"); - return -EINVAL; - } - - if (!tnum_is_const(reg->var_off)) { - bpf_log(log, "R%d is not a const\n", regno); - return -EINVAL; - } - - kfunc_meta->r0_size = reg->var_off.value; - ret = mark_chain_precision(env, regno); - if (ret) - return ret; - } - } - if (reg->type == SCALAR_VALUE) continue; bpf_log(log, "R%d is not a scalar\n", regno); @@ -6809,88 +6655,14 @@ static int btf_check_func_arg_match(struct bpf_verifier_env *env, return -EINVAL; } - /* These register types have special constraints wrt ref_obj_id - * and offset checks. The rest of trusted args don't. - */ - obj_ptr = reg->type == PTR_TO_CTX || reg->type == PTR_TO_BTF_ID || - reg2btf_ids[base_type(reg->type)]; - - /* Check if argument must be a referenced pointer, args + i has - * been verified to be a pointer (after skipping modifiers). - * PTR_TO_CTX is ok without having non-zero ref_obj_id. - */ - if (is_kfunc && trusted_args && (obj_ptr && reg->type != PTR_TO_CTX) && !reg->ref_obj_id) { - bpf_log(log, "R%d must be referenced\n", regno); - return -EINVAL; - } - ref_t = btf_type_skip_modifiers(btf, t->type, &ref_id); ref_tname = btf_name_by_offset(btf, ref_t->name_off); - /* Trusted args have the same offset checks as release arguments */ - if ((trusted_args && obj_ptr) || (rel && reg->ref_obj_id)) - arg_type |= OBJ_RELEASE; ret = check_func_arg_reg_off(env, reg, regno, arg_type); if (ret < 0) return ret; - if (is_kfunc && reg->ref_obj_id) { - /* Ensure only one argument is referenced PTR_TO_BTF_ID */ - if (ref_obj_id) { - bpf_log(log, "verifier internal error: more than one arg with ref_obj_id R%d %u %u\n", - regno, reg->ref_obj_id, ref_obj_id); - return -EFAULT; - } - ref_regno = regno; - ref_obj_id = reg->ref_obj_id; - } - - /* kptr_get is only true for kfunc */ - if (i == 0 && kptr_get) { - struct btf_field *kptr_field; - - if (reg->type != PTR_TO_MAP_VALUE) { - bpf_log(log, "arg#0 expected pointer to map value\n"); - return -EINVAL; - } - - /* check_func_arg_reg_off allows var_off for - * PTR_TO_MAP_VALUE, but we need fixed offset to find - * off_desc. - */ - if (!tnum_is_const(reg->var_off)) { - bpf_log(log, "arg#0 must have constant offset\n"); - return -EINVAL; - } - - kptr_field = btf_record_find(reg->map_ptr->record, reg->off + reg->var_off.value, BPF_KPTR); - if (!kptr_field || kptr_field->type != BPF_KPTR_REF) { - bpf_log(log, "arg#0 no referenced kptr at map value offset=%llu\n", - reg->off + reg->var_off.value); - return -EINVAL; - } - - if (!btf_type_is_ptr(ref_t)) { - bpf_log(log, "arg#0 BTF type must be a double pointer\n"); - return -EINVAL; - } - - ref_t = btf_type_skip_modifiers(btf, ref_t->type, &ref_id); - ref_tname = btf_name_by_offset(btf, ref_t->name_off); - - if (!btf_type_is_struct(ref_t)) { - bpf_log(log, "kernel function %s args#%d pointer type %s %s is not supported\n", - func_name, i, btf_type_str(ref_t), ref_tname); - return -EINVAL; - } - if (!btf_struct_ids_match(log, btf, ref_id, 0, kptr_field->kptr.btf, - kptr_field->kptr.btf_id, true)) { - bpf_log(log, "kernel function %s args#%d expected pointer to %s %s\n", - func_name, i, btf_type_str(ref_t), ref_tname); - return -EINVAL; - } - /* rest of the arguments can be anything, like normal kfunc */ - } else if (btf_get_prog_ctx_type(log, btf, t, prog_type, i)) { + if (btf_get_prog_ctx_type(log, btf, t, prog_type, i)) { /* If function expects ctx type in BTF check that caller * is passing PTR_TO_CTX. */ @@ -6900,109 +6672,10 @@ static int btf_check_func_arg_match(struct bpf_verifier_env *env, i, btf_type_str(t)); return -EINVAL; } - } else if (is_kfunc && (reg->type == PTR_TO_BTF_ID || - (reg2btf_ids[base_type(reg->type)] && !type_flag(reg->type)))) { - const struct btf_type *reg_ref_t; - const struct btf *reg_btf; - const char *reg_ref_tname; - u32 reg_ref_id; - - if (!btf_type_is_struct(ref_t)) { - bpf_log(log, "kernel function %s args#%d pointer type %s %s is not supported\n", - func_name, i, btf_type_str(ref_t), - ref_tname); - return -EINVAL; - } - - if (reg->type == PTR_TO_BTF_ID) { - reg_btf = reg->btf; - reg_ref_id = reg->btf_id; - } else { - reg_btf = btf_vmlinux; - reg_ref_id = *reg2btf_ids[base_type(reg->type)]; - } - - reg_ref_t = btf_type_skip_modifiers(reg_btf, reg_ref_id, - ®_ref_id); - reg_ref_tname = btf_name_by_offset(reg_btf, - reg_ref_t->name_off); - if (!btf_struct_ids_match(log, reg_btf, reg_ref_id, - reg->off, btf, ref_id, - trusted_args || (rel && reg->ref_obj_id))) { - bpf_log(log, "kernel function %s args#%d expected pointer to %s %s but R%d has a pointer to %s %s\n", - func_name, i, - btf_type_str(ref_t), ref_tname, - regno, btf_type_str(reg_ref_t), - reg_ref_tname); - return -EINVAL; - } } else if (ptr_to_mem_ok && processing_call) { const struct btf_type *resolve_ret; u32 type_size; - if (is_kfunc) { - bool arg_mem_size = i + 1 < nargs && is_kfunc_arg_mem_size(btf, &args[i + 1], ®s[regno + 1]); - bool arg_dynptr = btf_type_is_struct(ref_t) && - !strcmp(ref_tname, - stringify_struct(bpf_dynptr_kern)); - - /* Permit pointer to mem, but only when argument - * type is pointer to scalar, or struct composed - * (recursively) of scalars. - * When arg_mem_size is true, the pointer can be - * void *. - * Also permit initialized local dynamic pointers. - */ - if (!btf_type_is_scalar(ref_t) && - !__btf_type_is_scalar_struct(log, btf, ref_t, 0) && - !arg_dynptr && - (arg_mem_size ? !btf_type_is_void(ref_t) : 1)) { - bpf_log(log, - "arg#%d pointer type %s %s must point to %sscalar, or struct with scalar\n", - i, btf_type_str(ref_t), ref_tname, arg_mem_size ? "void, " : ""); - return -EINVAL; - } - - if (arg_dynptr) { - if (reg->type != PTR_TO_STACK) { - bpf_log(log, "arg#%d pointer type %s %s not to stack\n", - i, btf_type_str(ref_t), - ref_tname); - return -EINVAL; - } - - if (!is_dynptr_reg_valid_init(env, reg)) { - bpf_log(log, - "arg#%d pointer type %s %s must be valid and initialized\n", - i, btf_type_str(ref_t), - ref_tname); - return -EINVAL; - } - - if (!is_dynptr_type_expected(env, reg, - ARG_PTR_TO_DYNPTR | DYNPTR_TYPE_LOCAL)) { - bpf_log(log, - "arg#%d pointer type %s %s points to unsupported dynamic pointer type\n", - i, btf_type_str(ref_t), - ref_tname); - return -EINVAL; - } - - continue; - } - - /* Check for mem, len pair */ - if (arg_mem_size) { - if (check_kfunc_mem_size_reg(env, ®s[regno + 1], regno + 1)) { - bpf_log(log, "arg#%d arg#%d memory, len pair leads to invalid memory access\n", - i, i + 1); - return -EINVAL; - } - i++; - continue; - } - } - resolve_ret = btf_resolve_size(btf, ref_t, &type_size); if (IS_ERR(resolve_ret)) { bpf_log(log, @@ -7015,36 +6688,13 @@ static int btf_check_func_arg_match(struct bpf_verifier_env *env, if (check_mem_reg(env, reg, regno, type_size)) return -EINVAL; } else { - bpf_log(log, "reg type unsupported for arg#%d %sfunction %s#%d\n", i, - is_kfunc ? "kernel " : "", func_name, func_id); + bpf_log(log, "reg type unsupported for arg#%d function %s#%d\n", i, + func_name, func_id); return -EINVAL; } } - /* Either both are set, or neither */ - WARN_ON_ONCE((ref_obj_id && !ref_regno) || (!ref_obj_id && ref_regno)); - /* We already made sure ref_obj_id is set only for one argument. We do - * allow (!rel && ref_obj_id), so that passing such referenced - * PTR_TO_BTF_ID to other kfuncs works. Note that rel is only true when - * is_kfunc is true. - */ - if (rel && !ref_obj_id) { - bpf_log(log, "release kernel function %s expects refcounted PTR_TO_BTF_ID\n", - func_name); - return -EINVAL; - } - - if (sleepable && !env->prog->aux->sleepable) { - bpf_log(log, "kernel function %s is sleepable but the program is not\n", - func_name); - return -EINVAL; - } - - if (kfunc_meta && ref_obj_id) - kfunc_meta->ref_obj_id = ref_obj_id; - - /* returns argument register number > 0 in case of reference release kfunc */ - return rel ? ref_regno : 0; + return 0; } /* Compare BTF of a function declaration with given bpf_reg_state. @@ -7074,7 +6724,7 @@ int btf_check_subprog_arg_match(struct bpf_verifier_env *env, int subprog, return -EINVAL; is_global = prog->aux->func_info_aux[subprog].linkage == BTF_FUNC_GLOBAL; - err = btf_check_func_arg_match(env, btf, btf_id, regs, is_global, NULL, false); + err = btf_check_func_arg_match(env, btf, btf_id, regs, is_global, false); /* Compiler optimizations can remove arguments from static functions * or mismatched type can be passed into a global function. @@ -7117,7 +6767,7 @@ int btf_check_subprog_call(struct bpf_verifier_env *env, int subprog, return -EINVAL; is_global = prog->aux->func_info_aux[subprog].linkage == BTF_FUNC_GLOBAL; - err = btf_check_func_arg_match(env, btf, btf_id, regs, is_global, NULL, true); + err = btf_check_func_arg_match(env, btf, btf_id, regs, is_global, true); /* Compiler optimizations can remove arguments from static functions * or mismatched type can be passed into a global function. @@ -7128,14 +6778,6 @@ int btf_check_subprog_call(struct bpf_verifier_env *env, int subprog, return err; } -int btf_check_kfunc_arg_match(struct bpf_verifier_env *env, - const struct btf *btf, u32 func_id, - struct bpf_reg_state *regs, - struct bpf_kfunc_arg_meta *meta) -{ - return btf_check_func_arg_match(env, btf, func_id, regs, true, meta, true); -} - /* Convert BTF of a function into bpf_reg_state if possible * Returns: * EFAULT - there is a verifier bug. Abort verification. diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index c8f3abe9b08e..7d7a91c54709 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -5550,8 +5550,8 @@ int check_mem_reg(struct bpf_verifier_env *env, struct bpf_reg_state *reg, return err; } -int check_kfunc_mem_size_reg(struct bpf_verifier_env *env, struct bpf_reg_state *reg, - u32 regno) +static int check_kfunc_mem_size_reg(struct bpf_verifier_env *env, struct bpf_reg_state *reg, + u32 regno) { struct bpf_reg_state *mem_reg = &cur_regs(env)[regno - 1]; bool may_be_null = type_may_be_null(mem_reg->type); @@ -7863,19 +7863,517 @@ static void mark_btf_func_reg_size(struct bpf_verifier_env *env, u32 regno, } } +struct bpf_kfunc_call_arg_meta { + /* In parameters */ + struct btf *btf; + u32 func_id; + u32 kfunc_flags; + const struct btf_type *func_proto; + const char *func_name; + /* Out parameters */ + u32 ref_obj_id; + u8 release_regno; + bool r0_rdonly; + u64 r0_size; +}; + +static bool is_kfunc_acquire(struct bpf_kfunc_call_arg_meta *meta) +{ + return meta->kfunc_flags & KF_ACQUIRE; +} + +static bool is_kfunc_ret_null(struct bpf_kfunc_call_arg_meta *meta) +{ + return meta->kfunc_flags & KF_RET_NULL; +} + +static bool is_kfunc_release(struct bpf_kfunc_call_arg_meta *meta) +{ + return meta->kfunc_flags & KF_RELEASE; +} + +static bool is_kfunc_trusted_args(struct bpf_kfunc_call_arg_meta *meta) +{ + return meta->kfunc_flags & KF_TRUSTED_ARGS; +} + +static bool is_kfunc_sleepable(struct bpf_kfunc_call_arg_meta *meta) +{ + return meta->kfunc_flags & KF_SLEEPABLE; +} + +static bool is_kfunc_destructive(struct bpf_kfunc_call_arg_meta *meta) +{ + return meta->kfunc_flags & KF_DESTRUCTIVE; +} + +static bool is_kfunc_arg_kptr_get(struct bpf_kfunc_call_arg_meta *meta, int arg) +{ + return arg == 0 && (meta->kfunc_flags & KF_KPTR_GET); +} + +static bool is_kfunc_arg_mem_size(const struct btf *btf, + const struct btf_param *arg, + const struct bpf_reg_state *reg) +{ + int len, sfx_len = sizeof("__sz") - 1; + const struct btf_type *t; + const char *param_name; + + t = btf_type_skip_modifiers(btf, arg->type, NULL); + if (!btf_type_is_scalar(t) || reg->type != SCALAR_VALUE) + return false; + + /* In the future, this can be ported to use BTF tagging */ + param_name = btf_name_by_offset(btf, arg->name_off); + if (str_is_empty(param_name)) + return false; + len = strlen(param_name); + if (len < sfx_len) + return false; + param_name += len - sfx_len; + if (strncmp(param_name, "__sz", sfx_len)) + return false; + + return true; +} + +static bool is_kfunc_arg_scalar_with_name(const struct btf *btf, + const struct btf_param *arg, + const char *name) +{ + int len, target_len = strlen(name); + const char *param_name; + + param_name = btf_name_by_offset(btf, arg->name_off); + if (str_is_empty(param_name)) + return false; + len = strlen(param_name); + if (len != target_len) + return false; + if (strcmp(param_name, name)) + return false; + + return true; +} + +enum { + KF_ARG_DYNPTR_ID, +}; + +BTF_ID_LIST(kf_arg_btf_ids) +BTF_ID(struct, bpf_dynptr_kern) + +static bool is_kfunc_arg_dynptr(const struct btf *btf, + const struct btf_param *arg) +{ + const struct btf_type *t; + u32 res_id; + + t = btf_type_skip_modifiers(btf, arg->type, NULL); + if (!t) + return false; + if (!btf_type_is_ptr(t)) + return false; + t = btf_type_skip_modifiers(btf, t->type, &res_id); + if (!t) + return false; + return btf_types_are_same(btf, res_id, btf_vmlinux, kf_arg_btf_ids[KF_ARG_DYNPTR_ID]); +} + +/* Returns true if struct is composed of scalars, 4 levels of nesting allowed */ +static bool __btf_type_is_scalar_struct(struct bpf_verifier_env *env, + const struct btf *btf, + const struct btf_type *t, int rec) +{ + const struct btf_type *member_type; + const struct btf_member *member; + u32 i; + + if (!btf_type_is_struct(t)) + return false; + + for_each_member(i, t, member) { + const struct btf_array *array; + + member_type = btf_type_skip_modifiers(btf, member->type, NULL); + if (btf_type_is_struct(member_type)) { + if (rec >= 3) { + verbose(env, "max struct nesting depth exceeded\n"); + return false; + } + if (!__btf_type_is_scalar_struct(env, btf, member_type, rec + 1)) + return false; + continue; + } + if (btf_type_is_array(member_type)) { + array = btf_array(member_type); + if (!array->nelems) + return false; + member_type = btf_type_skip_modifiers(btf, array->type, NULL); + if (!btf_type_is_scalar(member_type)) + return false; + continue; + } + if (!btf_type_is_scalar(member_type)) + return false; + } + return true; +} + + +static u32 *reg2btf_ids[__BPF_REG_TYPE_MAX] = { +#ifdef CONFIG_NET + [PTR_TO_SOCKET] = &btf_sock_ids[BTF_SOCK_TYPE_SOCK], + [PTR_TO_SOCK_COMMON] = &btf_sock_ids[BTF_SOCK_TYPE_SOCK_COMMON], + [PTR_TO_TCP_SOCK] = &btf_sock_ids[BTF_SOCK_TYPE_TCP], +#endif +}; + +enum kfunc_ptr_arg_type { + KF_ARG_PTR_TO_CTX, + KF_ARG_PTR_TO_KPTR, /* PTR_TO_KPTR but type specific */ + KF_ARG_PTR_TO_DYNPTR, + KF_ARG_PTR_TO_BTF_ID, /* Also covers reg2btf_ids conversions */ + KF_ARG_PTR_TO_MEM, + KF_ARG_PTR_TO_MEM_SIZE, /* Size derived from next argument, skip it */ +}; + +static enum kfunc_ptr_arg_type +get_kfunc_ptr_arg_type(struct bpf_verifier_env *env, + struct bpf_kfunc_call_arg_meta *meta, + const struct btf_type *t, const struct btf_type *ref_t, + const char *ref_tname, const struct btf_param *args, + int argno, int nargs) +{ + u32 regno = argno + 1; + struct bpf_reg_state *regs = cur_regs(env); + struct bpf_reg_state *reg = ®s[regno]; + bool arg_mem_size = false; + + /* In this function, we verify the kfunc's BTF as per the argument type, + * leaving the rest of the verification with respect to the register + * type to our caller. When a set of conditions hold in the BTF type of + * arguments, we resolve it to a known kfunc_ptr_arg_type. + */ + if (btf_get_prog_ctx_type(&env->log, meta->btf, t, resolve_prog_type(env->prog), argno)) + return KF_ARG_PTR_TO_CTX; + + if (is_kfunc_arg_kptr_get(meta, argno)) { + if (!btf_type_is_ptr(ref_t)) { + verbose(env, "arg#0 BTF type must be a double pointer for kptr_get kfunc\n"); + return -EINVAL; + } + ref_t = btf_type_by_id(meta->btf, ref_t->type); + ref_tname = btf_name_by_offset(meta->btf, ref_t->name_off); + if (!btf_type_is_struct(ref_t)) { + verbose(env, "kernel function %s args#0 pointer type %s %s is not supported\n", + meta->func_name, btf_type_str(ref_t), ref_tname); + return -EINVAL; + } + return KF_ARG_PTR_TO_KPTR; + } + + if (is_kfunc_arg_dynptr(meta->btf, &args[argno])) + return KF_ARG_PTR_TO_DYNPTR; + + if ((base_type(reg->type) == PTR_TO_BTF_ID || reg2btf_ids[base_type(reg->type)])) { + if (!btf_type_is_struct(ref_t)) { + verbose(env, "kernel function %s args#%d pointer type %s %s is not supported\n", + meta->func_name, argno, btf_type_str(ref_t), ref_tname); + return -EINVAL; + } + return KF_ARG_PTR_TO_BTF_ID; + } + + if (argno + 1 < nargs && is_kfunc_arg_mem_size(meta->btf, &args[argno + 1], ®s[regno + 1])) + arg_mem_size = true; + + /* This is the catch all argument type of register types supported by + * check_helper_mem_access. However, we only allow when argument type is + * pointer to scalar, or struct composed (recursively) of scalars. When + * arg_mem_size is true, the pointer can be void *. + */ + if (!btf_type_is_scalar(ref_t) && !__btf_type_is_scalar_struct(env, meta->btf, ref_t, 0) && + (arg_mem_size ? !btf_type_is_void(ref_t) : 1)) { + verbose(env, "arg#%d pointer type %s %s must point to %sscalar, or struct with scalar\n", + argno, btf_type_str(ref_t), ref_tname, arg_mem_size ? "void, " : ""); + return -EINVAL; + } + return arg_mem_size ? KF_ARG_PTR_TO_MEM_SIZE : KF_ARG_PTR_TO_MEM; +} + +static int process_kf_arg_ptr_to_btf_id(struct bpf_verifier_env *env, + struct bpf_reg_state *reg, + const struct btf_type *ref_t, + const char *ref_tname, u32 ref_id, + struct bpf_kfunc_call_arg_meta *meta, + int argno) +{ + const struct btf_type *reg_ref_t; + bool strict_type_match = false; + const struct btf *reg_btf; + const char *reg_ref_tname; + u32 reg_ref_id; + + if (reg->type == PTR_TO_BTF_ID) { + reg_btf = reg->btf; + reg_ref_id = reg->btf_id; + } else { + reg_btf = btf_vmlinux; + reg_ref_id = *reg2btf_ids[base_type(reg->type)]; + } + + if (is_kfunc_trusted_args(meta) || (is_kfunc_release(meta) && reg->ref_obj_id)) + strict_type_match = true; + + reg_ref_t = btf_type_skip_modifiers(reg_btf, reg_ref_id, ®_ref_id); + reg_ref_tname = btf_name_by_offset(reg_btf, reg_ref_t->name_off); + if (!btf_struct_ids_match(&env->log, reg_btf, reg_ref_id, reg->off, meta->btf, ref_id, strict_type_match)) { + verbose(env, "kernel function %s args#%d expected pointer to %s %s but R%d has a pointer to %s %s\n", + meta->func_name, argno, btf_type_str(ref_t), ref_tname, argno + 1, + btf_type_str(reg_ref_t), reg_ref_tname); + return -EINVAL; + } + return 0; +} + +static int process_kf_arg_ptr_to_kptr(struct bpf_verifier_env *env, + struct bpf_reg_state *reg, + const struct btf_type *ref_t, + const char *ref_tname, + struct bpf_kfunc_call_arg_meta *meta, + int argno) +{ + struct btf_field *kptr_field; + + /* check_func_arg_reg_off allows var_off for + * PTR_TO_MAP_VALUE, but we need fixed offset to find + * off_desc. + */ + if (!tnum_is_const(reg->var_off)) { + verbose(env, "arg#0 must have constant offset\n"); + return -EINVAL; + } + + kptr_field = btf_record_find(reg->map_ptr->record, reg->off + reg->var_off.value, BPF_KPTR); + if (!kptr_field || kptr_field->type != BPF_KPTR_REF) { + verbose(env, "arg#0 no referenced kptr at map value offset=%llu\n", + reg->off + reg->var_off.value); + return -EINVAL; + } + + if (!btf_struct_ids_match(&env->log, meta->btf, ref_t->type, 0, kptr_field->kptr.btf, + kptr_field->kptr.btf_id, true)) { + verbose(env, "kernel function %s args#%d expected pointer to %s %s\n", + meta->func_name, argno, btf_type_str(ref_t), ref_tname); + return -EINVAL; + } + return 0; +} + +static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_kfunc_call_arg_meta *meta) +{ + const char *func_name = meta->func_name, *ref_tname; + const struct btf *btf = meta->btf; + const struct btf_param *args; + u32 i, nargs; + int ret; + + args = (const struct btf_param *)(meta->func_proto + 1); + nargs = btf_type_vlen(meta->func_proto); + if (nargs > MAX_BPF_FUNC_REG_ARGS) { + verbose(env, "Function %s has %d > %d args\n", func_name, nargs, + MAX_BPF_FUNC_REG_ARGS); + return -EINVAL; + } + + /* Check that BTF function arguments match actual types that the + * verifier sees. + */ + for (i = 0; i < nargs; i++) { + struct bpf_reg_state *regs = cur_regs(env), *reg = ®s[i + 1]; + const struct btf_type *t, *ref_t, *resolve_ret; + enum bpf_arg_type arg_type = ARG_DONTCARE; + u32 regno = i + 1, ref_id, type_size; + bool is_ret_buf_sz = false; + int kf_arg_type; + + t = btf_type_skip_modifiers(btf, args[i].type, NULL); + if (btf_type_is_scalar(t)) { + if (reg->type != SCALAR_VALUE) { + verbose(env, "R%d is not a scalar\n", regno); + return -EINVAL; + } + if (is_kfunc_arg_scalar_with_name(btf, &args[i], "rdonly_buf_size")) { + meta->r0_rdonly = true; + is_ret_buf_sz = true; + } else if (is_kfunc_arg_scalar_with_name(btf, &args[i], "rdwr_buf_size")) { + is_ret_buf_sz = true; + } + + if (is_ret_buf_sz) { + if (meta->r0_size) { + verbose(env, "2 or more rdonly/rdwr_buf_size parameters for kfunc"); + return -EINVAL; + } + + if (!tnum_is_const(reg->var_off)) { + verbose(env, "R%d is not a const\n", regno); + return -EINVAL; + } + + meta->r0_size = reg->var_off.value; + ret = mark_chain_precision(env, regno); + if (ret) + return ret; + } + continue; + } + + if (!btf_type_is_ptr(t)) { + verbose(env, "Unrecognized arg#%d type %s\n", i, btf_type_str(t)); + return -EINVAL; + } + + if (reg->ref_obj_id) { + if (is_kfunc_release(meta) && meta->ref_obj_id) { + verbose(env, "verifier internal error: more than one arg with ref_obj_id R%d %u %u\n", + regno, reg->ref_obj_id, + meta->ref_obj_id); + return -EFAULT; + } + meta->ref_obj_id = reg->ref_obj_id; + if (is_kfunc_release(meta)) + meta->release_regno = regno; + } + + ref_t = btf_type_skip_modifiers(btf, t->type, &ref_id); + ref_tname = btf_name_by_offset(btf, ref_t->name_off); + + kf_arg_type = get_kfunc_ptr_arg_type(env, meta, t, ref_t, ref_tname, args, i, nargs); + if (kf_arg_type < 0) + return kf_arg_type; + + switch (kf_arg_type) { + case KF_ARG_PTR_TO_BTF_ID: + if (!is_kfunc_trusted_args(meta)) + break; + if (!reg->ref_obj_id) { + verbose(env, "R%d must be referenced\n", regno); + return -EINVAL; + } + fallthrough; + case KF_ARG_PTR_TO_CTX: + /* Trusted arguments have the same offset checks as release arguments */ + arg_type |= OBJ_RELEASE; + break; + case KF_ARG_PTR_TO_KPTR: + case KF_ARG_PTR_TO_DYNPTR: + case KF_ARG_PTR_TO_MEM: + case KF_ARG_PTR_TO_MEM_SIZE: + /* Trusted by default */ + break; + default: + WARN_ON_ONCE(1); + return -EFAULT; + } + + if (is_kfunc_release(meta) && reg->ref_obj_id) + arg_type |= OBJ_RELEASE; + ret = check_func_arg_reg_off(env, reg, regno, arg_type); + if (ret < 0) + return ret; + + switch (kf_arg_type) { + case KF_ARG_PTR_TO_CTX: + if (reg->type != PTR_TO_CTX) { + verbose(env, "arg#%d expected pointer to ctx, but got %s\n", i, btf_type_str(t)); + return -EINVAL; + } + break; + case KF_ARG_PTR_TO_KPTR: + if (reg->type != PTR_TO_MAP_VALUE) { + verbose(env, "arg#0 expected pointer to map value\n"); + return -EINVAL; + } + ret = process_kf_arg_ptr_to_kptr(env, reg, ref_t, ref_tname, meta, i); + if (ret < 0) + return ret; + break; + case KF_ARG_PTR_TO_DYNPTR: + if (reg->type != PTR_TO_STACK) { + verbose(env, "arg#%d expected pointer to stack\n", i); + return -EINVAL; + } + + if (!is_dynptr_reg_valid_init(env, reg)) { + verbose(env, "arg#%d pointer type %s %s must be valid and initialized\n", + i, btf_type_str(ref_t), ref_tname); + return -EINVAL; + } + + if (!is_dynptr_type_expected(env, reg, ARG_PTR_TO_DYNPTR | DYNPTR_TYPE_LOCAL)) { + verbose(env, "arg#%d pointer type %s %s points to unsupported dynamic pointer type\n", + i, btf_type_str(ref_t), ref_tname); + return -EINVAL; + } + break; + case KF_ARG_PTR_TO_BTF_ID: + /* Only base_type is checked, further checks are done here */ + if (reg->type != PTR_TO_BTF_ID && + (!reg2btf_ids[base_type(reg->type)] || type_flag(reg->type))) { + verbose(env, "arg#%d expected pointer to btf or socket\n", i); + return -EINVAL; + } + ret = process_kf_arg_ptr_to_btf_id(env, reg, ref_t, ref_tname, ref_id, meta, i); + if (ret < 0) + return ret; + break; + case KF_ARG_PTR_TO_MEM: + resolve_ret = btf_resolve_size(btf, ref_t, &type_size); + if (IS_ERR(resolve_ret)) { + verbose(env, "arg#%d reference type('%s %s') size cannot be determined: %ld\n", + i, btf_type_str(ref_t), ref_tname, PTR_ERR(resolve_ret)); + return -EINVAL; + } + ret = check_mem_reg(env, reg, regno, type_size); + if (ret < 0) + return ret; + break; + case KF_ARG_PTR_TO_MEM_SIZE: + ret = check_kfunc_mem_size_reg(env, ®s[regno + 1], regno + 1); + if (ret < 0) { + verbose(env, "arg#%d arg#%d memory, len pair leads to invalid memory access\n", i, i + 1); + return ret; + } + /* Skip next '__sz' argument */ + i++; + break; + } + } + + if (is_kfunc_release(meta) && !meta->release_regno) { + verbose(env, "release kernel function %s expects refcounted PTR_TO_BTF_ID\n", + func_name); + return -EINVAL; + } + + return 0; +} + static int check_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn, int *insn_idx_p) { const struct btf_type *t, *func, *func_proto, *ptr_type; struct bpf_reg_state *regs = cur_regs(env); - struct bpf_kfunc_arg_meta meta = { 0 }; const char *func_name, *ptr_type_name; + struct bpf_kfunc_call_arg_meta meta; u32 i, nargs, func_id, ptr_type_id; int err, insn_idx = *insn_idx_p; const struct btf_param *args; struct btf *desc_btf; u32 *kfunc_flags; - bool acq; /* skip for now, but return error when we find this in fixup_kfunc_call */ if (!insn->imm) @@ -7896,24 +8394,34 @@ static int check_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn, func_name); return -EACCES; } - if (*kfunc_flags & KF_DESTRUCTIVE && !capable(CAP_SYS_BOOT)) { - verbose(env, "destructive kfunc calls require CAP_SYS_BOOT capabilities\n"); + + /* Prepare kfunc call metadata */ + memset(&meta, 0, sizeof(meta)); + meta.btf = desc_btf; + meta.func_id = func_id; + meta.kfunc_flags = *kfunc_flags; + meta.func_proto = func_proto; + meta.func_name = func_name; + + if (is_kfunc_destructive(&meta) && !capable(CAP_SYS_BOOT)) { + verbose(env, "destructive kfunc calls require CAP_SYS_BOOT capability\n"); return -EACCES; } - acq = *kfunc_flags & KF_ACQUIRE; - - meta.flags = *kfunc_flags; + if (is_kfunc_sleepable(&meta) && !env->prog->aux->sleepable) { + verbose(env, "program must be sleepable to call sleepable kfunc %s\n", func_name); + return -EACCES; + } /* Check the arguments */ - err = btf_check_kfunc_arg_match(env, desc_btf, func_id, regs, &meta); + err = check_kfunc_args(env, &meta); if (err < 0) return err; /* In case of release function, we get register number of refcounted - * PTR_TO_BTF_ID back from btf_check_kfunc_arg_match, do the release now + * PTR_TO_BTF_ID in bpf_kfunc_arg_meta, do the release now. */ - if (err) { - err = release_reference(env, regs[err].ref_obj_id); + if (meta.release_regno) { + err = release_reference(env, regs[meta.release_regno].ref_obj_id); if (err) { verbose(env, "kfunc %s#%d reference has not been acquired before\n", func_name, func_id); @@ -7927,7 +8435,7 @@ static int check_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn, /* Check return type */ t = btf_type_skip_modifiers(desc_btf, func_proto->type, NULL); - if (acq && !btf_type_is_struct_ptr(desc_btf, t)) { + if (is_kfunc_acquire(&meta) && !btf_type_is_struct_ptr(meta.btf, t)) { verbose(env, "acquire kernel function does not return PTR_TO_BTF_ID\n"); return -EINVAL; } @@ -7966,20 +8474,23 @@ static int check_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn, regs[BPF_REG_0].type = PTR_TO_BTF_ID; regs[BPF_REG_0].btf_id = ptr_type_id; } - if (*kfunc_flags & KF_RET_NULL) { + if (is_kfunc_ret_null(&meta)) { regs[BPF_REG_0].type |= PTR_MAYBE_NULL; /* For mark_ptr_or_null_reg, see 93c230e3f5bd6 */ regs[BPF_REG_0].id = ++env->id_gen; } mark_btf_func_reg_size(env, BPF_REG_0, sizeof(void *)); - if (acq) { + if (is_kfunc_acquire(&meta)) { int id = acquire_reference_state(env, insn_idx); if (id < 0) return id; - regs[BPF_REG_0].id = id; + if (is_kfunc_ret_null(&meta)) + regs[BPF_REG_0].id = id; regs[BPF_REG_0].ref_obj_id = id; } + if (reg_may_point_to_spin_lock(®s[BPF_REG_0]) && !regs[BPF_REG_0].id) + regs[BPF_REG_0].id = ++env->id_gen; } /* else { add_kfunc_call() ensures it is btf_type_is_void(t) } */ nargs = btf_type_vlen(func_proto); diff --git a/tools/testing/selftests/bpf/prog_tests/kfunc_dynptr_param.c b/tools/testing/selftests/bpf/prog_tests/kfunc_dynptr_param.c index c210657d4d0a..55d641c1f126 100644 --- a/tools/testing/selftests/bpf/prog_tests/kfunc_dynptr_param.c +++ b/tools/testing/selftests/bpf/prog_tests/kfunc_dynptr_param.c @@ -22,7 +22,7 @@ static struct { "arg#0 pointer type STRUCT bpf_dynptr_kern points to unsupported dynamic pointer type", 0}, {"not_valid_dynptr", "arg#0 pointer type STRUCT bpf_dynptr_kern must be valid and initialized", 0}, - {"not_ptr_to_stack", "arg#0 pointer type STRUCT bpf_dynptr_kern not to stack", 0}, + {"not_ptr_to_stack", "arg#0 expected pointer to stack", 0}, {"dynptr_data_null", NULL, -EBADMSG}, }; diff --git a/tools/testing/selftests/bpf/verifier/calls.c b/tools/testing/selftests/bpf/verifier/calls.c index e1a937277b54..86d6fef2e3b4 100644 --- a/tools/testing/selftests/bpf/verifier/calls.c +++ b/tools/testing/selftests/bpf/verifier/calls.c @@ -109,7 +109,7 @@ }, .prog_type = BPF_PROG_TYPE_SCHED_CLS, .result = REJECT, - .errstr = "arg#0 pointer type STRUCT prog_test_ref_kfunc must point", + .errstr = "arg#0 expected pointer to btf or socket", .fixup_kfunc_btf_id = { { "bpf_kfunc_call_test_acquire", 3 }, { "bpf_kfunc_call_test_release", 5 }, diff --git a/tools/testing/selftests/bpf/verifier/ref_tracking.c b/tools/testing/selftests/bpf/verifier/ref_tracking.c index fd683a32a276..55cba01c99d5 100644 --- a/tools/testing/selftests/bpf/verifier/ref_tracking.c +++ b/tools/testing/selftests/bpf/verifier/ref_tracking.c @@ -142,7 +142,7 @@ .kfunc = "bpf", .expected_attach_type = BPF_LSM_MAC, .flags = BPF_F_SLEEPABLE, - .errstr = "arg#0 pointer type STRUCT bpf_key must point to scalar, or struct with scalar", + .errstr = "arg#0 expected pointer to btf or socket", .fixup_kfunc_btf_id = { { "bpf_lookup_user_key", 2 }, { "bpf_key_put", 4 }, @@ -163,7 +163,7 @@ .kfunc = "bpf", .expected_attach_type = BPF_LSM_MAC, .flags = BPF_F_SLEEPABLE, - .errstr = "arg#0 pointer type STRUCT bpf_key must point to scalar, or struct with scalar", + .errstr = "arg#0 expected pointer to btf or socket", .fixup_kfunc_btf_id = { { "bpf_lookup_system_key", 1 }, { "bpf_key_put", 3 }, From a50388dbb328a4267c2b91ad4aefe81b08e49b2d Mon Sep 17 00:00:00 2001 From: Kumar Kartikeya Dwivedi Date: Fri, 18 Nov 2022 07:26:02 +0530 Subject: [PATCH 1012/2002] bpf: Support constant scalar arguments for kfuncs Allow passing known constant scalars as arguments to kfuncs that do not represent a size parameter. We use mark_chain_precision for the constant scalar argument to mark it precise. This makes the search pruning optimization of verifier more conservative for such kfunc calls, and each non-distinct argument is considered unequivalent. We will use this support to then expose a bpf_obj_new function where it takes the local type ID of a type in program BTF, and returns a PTR_TO_BTF_ID | MEM_ALLOC to the local type, and allows programs to allocate their own objects. Each type ID resolves to a distinct type with a possibly distinct size, hence the type ID constant matters in terms of program safety and its precision needs to be checked between old and cur states inside regsafe. The use of mark_chain_precision enables this. Signed-off-by: Kumar Kartikeya Dwivedi Link: https://lore.kernel.org/r/20221118015614.2013203-13-memxor@gmail.com Signed-off-by: Alexei Starovoitov --- Documentation/bpf/kfuncs.rst | 24 +++++++++++++++ kernel/bpf/verifier.c | 57 +++++++++++++++++++++++++++--------- 2 files changed, 67 insertions(+), 14 deletions(-) diff --git a/Documentation/bpf/kfuncs.rst b/Documentation/bpf/kfuncs.rst index 0f858156371d..3b1501c3b6cd 100644 --- a/Documentation/bpf/kfuncs.rst +++ b/Documentation/bpf/kfuncs.rst @@ -72,6 +72,30 @@ argument as its size. By default, without __sz annotation, the size of the type of the pointer is used. Without __sz annotation, a kfunc cannot accept a void pointer. +2.2.2 __k Annotation +-------------------- + +This annotation is only understood for scalar arguments, where it indicates that +the verifier must check the scalar argument to be a known constant, which does +not indicate a size parameter, and the value of the constant is relevant to the +safety of the program. + +An example is given below:: + + void *bpf_obj_new(u32 local_type_id__k, ...) + { + ... + } + +Here, bpf_obj_new uses local_type_id argument to find out the size of that type +ID in program's BTF and return a sized pointer to it. Each type ID will have a +distinct size, hence it is crucial to treat each such call as distinct when +values don't match during verifier state pruning checks. + +Hence, whenever a constant scalar argument is accepted by a kfunc which is not a +size parameter, and the value of the constant matters for program safety, __k +suffix should be used. + .. _BPF_kfunc_nodef: 2.3 Using an existing kernel function diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 7d7a91c54709..c8fcc2808d99 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -7875,6 +7875,10 @@ struct bpf_kfunc_call_arg_meta { u8 release_regno; bool r0_rdonly; u64 r0_size; + struct { + u64 value; + bool found; + } arg_constant; }; static bool is_kfunc_acquire(struct bpf_kfunc_call_arg_meta *meta) @@ -7912,30 +7916,40 @@ static bool is_kfunc_arg_kptr_get(struct bpf_kfunc_call_arg_meta *meta, int arg) return arg == 0 && (meta->kfunc_flags & KF_KPTR_GET); } -static bool is_kfunc_arg_mem_size(const struct btf *btf, - const struct btf_param *arg, - const struct bpf_reg_state *reg) +static bool __kfunc_param_match_suffix(const struct btf *btf, + const struct btf_param *arg, + const char *suffix) { - int len, sfx_len = sizeof("__sz") - 1; - const struct btf_type *t; + int suffix_len = strlen(suffix), len; const char *param_name; - t = btf_type_skip_modifiers(btf, arg->type, NULL); - if (!btf_type_is_scalar(t) || reg->type != SCALAR_VALUE) - return false; - /* In the future, this can be ported to use BTF tagging */ param_name = btf_name_by_offset(btf, arg->name_off); if (str_is_empty(param_name)) return false; len = strlen(param_name); - if (len < sfx_len) + if (len < suffix_len) return false; - param_name += len - sfx_len; - if (strncmp(param_name, "__sz", sfx_len)) + param_name += len - suffix_len; + return !strncmp(param_name, suffix, suffix_len); +} + +static bool is_kfunc_arg_mem_size(const struct btf *btf, + const struct btf_param *arg, + const struct bpf_reg_state *reg) +{ + const struct btf_type *t; + + t = btf_type_skip_modifiers(btf, arg->type, NULL); + if (!btf_type_is_scalar(t) || reg->type != SCALAR_VALUE) return false; - return true; + return __kfunc_param_match_suffix(btf, arg, "__sz"); +} + +static bool is_kfunc_arg_constant(const struct btf *btf, const struct btf_param *arg) +{ + return __kfunc_param_match_suffix(btf, arg, "__k"); } static bool is_kfunc_arg_scalar_with_name(const struct btf *btf, @@ -8205,7 +8219,22 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_kfunc_call_ verbose(env, "R%d is not a scalar\n", regno); return -EINVAL; } - if (is_kfunc_arg_scalar_with_name(btf, &args[i], "rdonly_buf_size")) { + + if (is_kfunc_arg_constant(meta->btf, &args[i])) { + if (meta->arg_constant.found) { + verbose(env, "verifier internal error: only one constant argument permitted\n"); + return -EFAULT; + } + if (!tnum_is_const(reg->var_off)) { + verbose(env, "R%d must be a known constant\n", regno); + return -EINVAL; + } + ret = mark_chain_precision(env, regno); + if (ret < 0) + return ret; + meta->arg_constant.found = true; + meta->arg_constant.value = reg->var_off.value; + } else if (is_kfunc_arg_scalar_with_name(btf, &args[i], "rdonly_buf_size")) { meta->r0_rdonly = true; is_ret_buf_sz = true; } else if (is_kfunc_arg_scalar_with_name(btf, &args[i], "rdwr_buf_size")) { From 958cf2e273f0929c66169e0788031310e8118722 Mon Sep 17 00:00:00 2001 From: Kumar Kartikeya Dwivedi Date: Fri, 18 Nov 2022 07:26:03 +0530 Subject: [PATCH 1013/2002] bpf: Introduce bpf_obj_new Introduce type safe memory allocator bpf_obj_new for BPF programs. The kernel side kfunc is named bpf_obj_new_impl, as passing hidden arguments to kfuncs still requires having them in prototype, unlike BPF helpers which always take 5 arguments and have them checked using bpf_func_proto in verifier, ignoring unset argument types. Introduce __ign suffix to ignore a specific kfunc argument during type checks, then use this to introduce support for passing type metadata to the bpf_obj_new_impl kfunc. The user passes BTF ID of the type it wants to allocates in program BTF, the verifier then rewrites the first argument as the size of this type, after performing some sanity checks (to ensure it exists and it is a struct type). The second argument is also fixed up and passed by the verifier. This is the btf_struct_meta for the type being allocated. It would be needed mostly for the offset array which is required for zero initializing special fields while leaving the rest of storage in unitialized state. It would also be needed in the next patch to perform proper destruction of the object's special fields. Under the hood, bpf_obj_new will call bpf_mem_alloc and bpf_mem_free, using the any context BPF memory allocator introduced recently. To this end, a global instance of the BPF memory allocator is initialized on boot to be used for this purpose. This 'bpf_global_ma' serves all allocations for bpf_obj_new. In the future, bpf_obj_new variants will allow specifying a custom allocator. Note that now that bpf_obj_new can be used to allocate objects that can be linked to BPF linked list (when future linked list helpers are available), we need to also free the elements using bpf_mem_free. However, since the draining of elements is done outside the bpf_spin_lock, we need to do migrate_disable around the call since bpf_list_head_free can be called from map free path where migration is enabled. Otherwise, when called from BPF programs migration is already disabled. A convenience macro is included in the bpf_experimental.h header to hide over the ugly details of the implementation, leading to user code looking similar to a language level extension which allocates and constructs fields of a user type. struct bar { struct bpf_list_node node; }; struct foo { struct bpf_spin_lock lock; struct bpf_list_head head __contains(bar, node); }; void prog(void) { struct foo *f; f = bpf_obj_new(typeof(*f)); if (!f) return; ... } A key piece of this story is still missing, i.e. the free function, which will come in the next patch. Signed-off-by: Kumar Kartikeya Dwivedi Link: https://lore.kernel.org/r/20221118015614.2013203-14-memxor@gmail.com Signed-off-by: Alexei Starovoitov --- include/linux/bpf.h | 21 ++-- include/linux/bpf_verifier.h | 2 + kernel/bpf/core.c | 16 +++ kernel/bpf/helpers.c | 47 ++++++-- kernel/bpf/verifier.c | 102 ++++++++++++++++-- .../testing/selftests/bpf/bpf_experimental.h | 25 +++++ 6 files changed, 190 insertions(+), 23 deletions(-) create mode 100644 tools/testing/selftests/bpf/bpf_experimental.h diff --git a/include/linux/bpf.h b/include/linux/bpf.h index 0a74df731eb8..8b32376ce746 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -54,6 +54,8 @@ struct cgroup; extern struct idr btf_idr; extern spinlock_t btf_idr_lock; extern struct kobject *btf_kobj; +extern struct bpf_mem_alloc bpf_global_ma; +extern bool bpf_global_ma_set; typedef u64 (*bpf_callback_t)(u64, u64, u64, u64, u64); typedef int (*bpf_iter_init_seq_priv_t)(void *private_data, @@ -334,16 +336,19 @@ static inline bool btf_record_has_field(const struct btf_record *rec, enum btf_f return rec->field_mask & type; } +static inline void bpf_obj_init(const struct btf_field_offs *foffs, void *obj) +{ + int i; + + if (!foffs) + return; + for (i = 0; i < foffs->cnt; i++) + memset(obj + foffs->field_off[i], 0, foffs->field_sz[i]); +} + static inline void check_and_init_map_value(struct bpf_map *map, void *dst) { - if (!IS_ERR_OR_NULL(map->record)) { - struct btf_field *fields = map->record->fields; - u32 cnt = map->record->cnt; - int i; - - for (i = 0; i < cnt; i++) - memset(dst + fields[i].offset, 0, btf_field_type_size(fields[i].type)); - } + bpf_obj_init(map->field_offs, dst); } /* memcpy that is used with 8-byte aligned pointers, power-of-8 size and diff --git a/include/linux/bpf_verifier.h b/include/linux/bpf_verifier.h index fb146b0ce006..3dc72d396dfc 100644 --- a/include/linux/bpf_verifier.h +++ b/include/linux/bpf_verifier.h @@ -433,6 +433,8 @@ struct bpf_insn_aux_data { */ struct bpf_loop_inline_state loop_inline_state; }; + u64 obj_new_size; /* remember the size of type passed to bpf_obj_new to rewrite R1 */ + struct btf_struct_meta *kptr_struct_meta; u64 map_key_state; /* constant (32 bit) key tracking for maps */ int ctx_field_size; /* the ctx field size for load insn, maybe 0 */ u32 seen; /* this insn was processed by the verifier at env->pass_cnt */ diff --git a/kernel/bpf/core.c b/kernel/bpf/core.c index 9c16338bcbe8..2e57fc839a5c 100644 --- a/kernel/bpf/core.c +++ b/kernel/bpf/core.c @@ -34,6 +34,7 @@ #include #include #include +#include #include #include @@ -60,6 +61,9 @@ #define CTX regs[BPF_REG_CTX] #define IMM insn->imm +struct bpf_mem_alloc bpf_global_ma; +bool bpf_global_ma_set; + /* No hurry in this branch * * Exported for the bpf jit load helper. @@ -2746,6 +2750,18 @@ int __weak bpf_arch_text_invalidate(void *dst, size_t len) return -ENOTSUPP; } +#ifdef CONFIG_BPF_SYSCALL +static int __init bpf_global_ma_init(void) +{ + int ret; + + ret = bpf_mem_alloc_init(&bpf_global_ma, 0, false); + bpf_global_ma_set = !ret; + return ret; +} +late_initcall(bpf_global_ma_init); +#endif + DEFINE_STATIC_KEY_FALSE(bpf_stats_enabled_key); EXPORT_SYMBOL(bpf_stats_enabled_key); diff --git a/kernel/bpf/helpers.c b/kernel/bpf/helpers.c index 5bc0b9f0f306..c4f1c22cc44c 100644 --- a/kernel/bpf/helpers.c +++ b/kernel/bpf/helpers.c @@ -19,6 +19,7 @@ #include #include #include +#include #include "../../lib/kstrtox.h" @@ -1735,25 +1736,57 @@ unlock: obj -= field->list_head.node_offset; head = head->next; - /* TODO: Rework later */ - kfree(obj); + /* The contained type can also have resources, including a + * bpf_list_head which needs to be freed. + */ + bpf_obj_free_fields(field->list_head.value_rec, obj); + /* bpf_mem_free requires migrate_disable(), since we can be + * called from map free path as well apart from BPF program (as + * part of map ops doing bpf_obj_free_fields). + */ + migrate_disable(); + bpf_mem_free(&bpf_global_ma, obj); + migrate_enable(); } } -BTF_SET8_START(tracing_btf_ids) +__diag_push(); +__diag_ignore_all("-Wmissing-prototypes", + "Global functions as their definitions will be in vmlinux BTF"); + +void *bpf_obj_new_impl(u64 local_type_id__k, void *meta__ign) +{ + struct btf_struct_meta *meta = meta__ign; + u64 size = local_type_id__k; + void *p; + + if (unlikely(!bpf_global_ma_set)) + return NULL; + p = bpf_mem_alloc(&bpf_global_ma, size); + if (!p) + return NULL; + if (meta) + bpf_obj_init(meta->field_offs, p); + return p; +} + +__diag_pop(); + +BTF_SET8_START(generic_btf_ids) #ifdef CONFIG_KEXEC_CORE BTF_ID_FLAGS(func, crash_kexec, KF_DESTRUCTIVE) #endif -BTF_SET8_END(tracing_btf_ids) +BTF_ID_FLAGS(func, bpf_obj_new_impl, KF_ACQUIRE | KF_RET_NULL) +BTF_SET8_END(generic_btf_ids) -static const struct btf_kfunc_id_set tracing_kfunc_set = { +static const struct btf_kfunc_id_set generic_kfunc_set = { .owner = THIS_MODULE, - .set = &tracing_btf_ids, + .set = &generic_btf_ids, }; static int __init kfunc_init(void) { - return register_btf_kfunc_id_set(BPF_PROG_TYPE_TRACING, &tracing_kfunc_set); + return register_btf_kfunc_id_set(BPF_PROG_TYPE_TRACING, &generic_kfunc_set); } late_initcall(kfunc_init); diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index c8fcc2808d99..804f3bca6c08 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -7952,6 +7952,11 @@ static bool is_kfunc_arg_constant(const struct btf *btf, const struct btf_param return __kfunc_param_match_suffix(btf, arg, "__k"); } +static bool is_kfunc_arg_ignore(const struct btf *btf, const struct btf_param *arg) +{ + return __kfunc_param_match_suffix(btf, arg, "__ign"); +} + static bool is_kfunc_arg_scalar_with_name(const struct btf *btf, const struct btf_param *arg, const char *name) @@ -8214,6 +8219,10 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_kfunc_call_ int kf_arg_type; t = btf_type_skip_modifiers(btf, args[i].type, NULL); + + if (is_kfunc_arg_ignore(btf, &args[i])) + continue; + if (btf_type_is_scalar(t)) { if (reg->type != SCALAR_VALUE) { verbose(env, "R%d is not a scalar\n", regno); @@ -8391,6 +8400,17 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_kfunc_call_ return 0; } +enum special_kfunc_type { + KF_bpf_obj_new_impl, +}; + +BTF_SET_START(special_kfunc_set) +BTF_ID(func, bpf_obj_new_impl) +BTF_SET_END(special_kfunc_set) + +BTF_ID_LIST(special_kfunc_list) +BTF_ID(func, bpf_obj_new_impl) + static int check_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn, int *insn_idx_p) { @@ -8465,17 +8485,59 @@ static int check_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn, t = btf_type_skip_modifiers(desc_btf, func_proto->type, NULL); if (is_kfunc_acquire(&meta) && !btf_type_is_struct_ptr(meta.btf, t)) { - verbose(env, "acquire kernel function does not return PTR_TO_BTF_ID\n"); - return -EINVAL; + /* Only exception is bpf_obj_new_impl */ + if (meta.btf != btf_vmlinux || meta.func_id != special_kfunc_list[KF_bpf_obj_new_impl]) { + verbose(env, "acquire kernel function does not return PTR_TO_BTF_ID\n"); + return -EINVAL; + } } if (btf_type_is_scalar(t)) { mark_reg_unknown(env, regs, BPF_REG_0); mark_btf_func_reg_size(env, BPF_REG_0, t->size); } else if (btf_type_is_ptr(t)) { - ptr_type = btf_type_skip_modifiers(desc_btf, t->type, - &ptr_type_id); - if (!btf_type_is_struct(ptr_type)) { + ptr_type = btf_type_skip_modifiers(desc_btf, t->type, &ptr_type_id); + + if (meta.btf == btf_vmlinux && btf_id_set_contains(&special_kfunc_set, meta.func_id)) { + if (meta.func_id == special_kfunc_list[KF_bpf_obj_new_impl]) { + const struct btf_type *ret_t; + struct btf *ret_btf; + u32 ret_btf_id; + + if (((u64)(u32)meta.arg_constant.value) != meta.arg_constant.value) { + verbose(env, "local type ID argument must be in range [0, U32_MAX]\n"); + return -EINVAL; + } + + ret_btf = env->prog->aux->btf; + ret_btf_id = meta.arg_constant.value; + + /* This may be NULL due to user not supplying a BTF */ + if (!ret_btf) { + verbose(env, "bpf_obj_new requires prog BTF\n"); + return -EINVAL; + } + + ret_t = btf_type_by_id(ret_btf, ret_btf_id); + if (!ret_t || !__btf_type_is_struct(ret_t)) { + verbose(env, "bpf_obj_new type ID argument must be of a struct\n"); + return -EINVAL; + } + + mark_reg_known_zero(env, regs, BPF_REG_0); + regs[BPF_REG_0].type = PTR_TO_BTF_ID | MEM_ALLOC; + regs[BPF_REG_0].btf = ret_btf; + regs[BPF_REG_0].btf_id = ret_btf_id; + + env->insn_aux_data[insn_idx].obj_new_size = ret_t->size; + env->insn_aux_data[insn_idx].kptr_struct_meta = + btf_find_struct_meta(ret_btf, ret_btf_id); + } else { + verbose(env, "kernel function %s unhandled dynamic return type\n", + meta.func_name); + return -EFAULT; + } + } else if (!__btf_type_is_struct(ptr_type)) { if (!meta.r0_size) { ptr_type_name = btf_name_by_offset(desc_btf, ptr_type->name_off); @@ -8503,6 +8565,7 @@ static int check_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn, regs[BPF_REG_0].type = PTR_TO_BTF_ID; regs[BPF_REG_0].btf_id = ptr_type_id; } + if (is_kfunc_ret_null(&meta)) { regs[BPF_REG_0].type |= PTR_MAYBE_NULL; /* For mark_ptr_or_null_reg, see 93c230e3f5bd6 */ @@ -14671,8 +14734,8 @@ static int fixup_call_args(struct bpf_verifier_env *env) return err; } -static int fixup_kfunc_call(struct bpf_verifier_env *env, - struct bpf_insn *insn) +static int fixup_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn, + struct bpf_insn *insn_buf, int insn_idx, int *cnt) { const struct bpf_kfunc_desc *desc; @@ -14691,8 +14754,21 @@ static int fixup_kfunc_call(struct bpf_verifier_env *env, return -EFAULT; } + *cnt = 0; insn->imm = desc->imm; + if (insn->off) + return 0; + if (desc->func_id == special_kfunc_list[KF_bpf_obj_new_impl]) { + struct btf_struct_meta *kptr_struct_meta = env->insn_aux_data[insn_idx].kptr_struct_meta; + struct bpf_insn addr[2] = { BPF_LD_IMM64(BPF_REG_2, (long)kptr_struct_meta) }; + u64 obj_new_size = env->insn_aux_data[insn_idx].obj_new_size; + insn_buf[0] = BPF_MOV64_IMM(BPF_REG_1, obj_new_size); + insn_buf[1] = addr[0]; + insn_buf[2] = addr[1]; + insn_buf[3] = *insn; + *cnt = 4; + } return 0; } @@ -14834,9 +14910,19 @@ static int do_misc_fixups(struct bpf_verifier_env *env) if (insn->src_reg == BPF_PSEUDO_CALL) continue; if (insn->src_reg == BPF_PSEUDO_KFUNC_CALL) { - ret = fixup_kfunc_call(env, insn); + ret = fixup_kfunc_call(env, insn, insn_buf, i + delta, &cnt); if (ret) return ret; + if (cnt == 0) + continue; + + new_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt); + if (!new_prog) + return -ENOMEM; + + delta += cnt - 1; + env->prog = prog = new_prog; + insn = new_prog->insnsi + i + delta; continue; } diff --git a/tools/testing/selftests/bpf/bpf_experimental.h b/tools/testing/selftests/bpf/bpf_experimental.h new file mode 100644 index 000000000000..aeb6a7fcb7c4 --- /dev/null +++ b/tools/testing/selftests/bpf/bpf_experimental.h @@ -0,0 +1,25 @@ +#ifndef __BPF_EXPERIMENTAL__ +#define __BPF_EXPERIMENTAL__ + +#include +#include +#include +#include + +/* Description + * Allocates an object of the type represented by 'local_type_id' in + * program BTF. User may use the bpf_core_type_id_local macro to pass the + * type ID of a struct in program BTF. + * + * The 'local_type_id' parameter must be a known constant. + * The 'meta' parameter is a hidden argument that is ignored. + * Returns + * A pointer to an object of the type corresponding to the passed in + * 'local_type_id', or NULL on failure. + */ +extern void *bpf_obj_new_impl(__u64 local_type_id, void *meta) __ksym; + +/* Convenience macro to wrap over bpf_obj_new_impl */ +#define bpf_obj_new(type) ((type *)bpf_obj_new_impl(bpf_core_type_id_local(type), NULL)) + +#endif From ac9f06050a3580cf4076a57a470cd71f12a81171 Mon Sep 17 00:00:00 2001 From: Kumar Kartikeya Dwivedi Date: Fri, 18 Nov 2022 07:26:04 +0530 Subject: [PATCH 1014/2002] bpf: Introduce bpf_obj_drop Introduce bpf_obj_drop, which is the kfunc used to free allocated objects (allocated using bpf_obj_new). Pairing with bpf_obj_new, it implicitly destructs the fields part of object automatically without user intervention. Just like the previous patch, btf_struct_meta that is needed to free up the special fields is passed as a hidden argument to the kfunc. For the user, a convenience macro hides over the kernel side kfunc which is named bpf_obj_drop_impl. Continuing the previous example: void prog(void) { struct foo *f; f = bpf_obj_new(typeof(*f)); if (!f) return; bpf_obj_drop(f); } Signed-off-by: Kumar Kartikeya Dwivedi Link: https://lore.kernel.org/r/20221118015614.2013203-15-memxor@gmail.com Signed-off-by: Alexei Starovoitov --- kernel/bpf/helpers.c | 11 ++++ kernel/bpf/verifier.c | 66 +++++++++++++++---- .../testing/selftests/bpf/bpf_experimental.h | 13 ++++ 3 files changed, 79 insertions(+), 11 deletions(-) diff --git a/kernel/bpf/helpers.c b/kernel/bpf/helpers.c index c4f1c22cc44c..71d803ca0c1d 100644 --- a/kernel/bpf/helpers.c +++ b/kernel/bpf/helpers.c @@ -1770,6 +1770,16 @@ void *bpf_obj_new_impl(u64 local_type_id__k, void *meta__ign) return p; } +void bpf_obj_drop_impl(void *p__alloc, void *meta__ign) +{ + struct btf_struct_meta *meta = meta__ign; + void *p = p__alloc; + + if (meta) + bpf_obj_free_fields(meta->record, p); + bpf_mem_free(&bpf_global_ma, p); +} + __diag_pop(); BTF_SET8_START(generic_btf_ids) @@ -1777,6 +1787,7 @@ BTF_SET8_START(generic_btf_ids) BTF_ID_FLAGS(func, crash_kexec, KF_DESTRUCTIVE) #endif BTF_ID_FLAGS(func, bpf_obj_new_impl, KF_ACQUIRE | KF_RET_NULL) +BTF_ID_FLAGS(func, bpf_obj_drop_impl, KF_RELEASE) BTF_SET8_END(generic_btf_ids) static const struct btf_kfunc_id_set generic_kfunc_set = { diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 804f3bca6c08..1fbb0b51c429 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -7879,6 +7879,10 @@ struct bpf_kfunc_call_arg_meta { u64 value; bool found; } arg_constant; + struct { + struct btf *btf; + u32 btf_id; + } arg_obj_drop; }; static bool is_kfunc_acquire(struct bpf_kfunc_call_arg_meta *meta) @@ -7957,6 +7961,11 @@ static bool is_kfunc_arg_ignore(const struct btf *btf, const struct btf_param *a return __kfunc_param_match_suffix(btf, arg, "__ign"); } +static bool is_kfunc_arg_alloc_obj(const struct btf *btf, const struct btf_param *arg) +{ + return __kfunc_param_match_suffix(btf, arg, "__alloc"); +} + static bool is_kfunc_arg_scalar_with_name(const struct btf *btf, const struct btf_param *arg, const char *name) @@ -8051,6 +8060,7 @@ static u32 *reg2btf_ids[__BPF_REG_TYPE_MAX] = { enum kfunc_ptr_arg_type { KF_ARG_PTR_TO_CTX, + KF_ARG_PTR_TO_ALLOC_BTF_ID, /* Allocated object */ KF_ARG_PTR_TO_KPTR, /* PTR_TO_KPTR but type specific */ KF_ARG_PTR_TO_DYNPTR, KF_ARG_PTR_TO_BTF_ID, /* Also covers reg2btf_ids conversions */ @@ -8058,6 +8068,20 @@ enum kfunc_ptr_arg_type { KF_ARG_PTR_TO_MEM_SIZE, /* Size derived from next argument, skip it */ }; +enum special_kfunc_type { + KF_bpf_obj_new_impl, + KF_bpf_obj_drop_impl, +}; + +BTF_SET_START(special_kfunc_set) +BTF_ID(func, bpf_obj_new_impl) +BTF_ID(func, bpf_obj_drop_impl) +BTF_SET_END(special_kfunc_set) + +BTF_ID_LIST(special_kfunc_list) +BTF_ID(func, bpf_obj_new_impl) +BTF_ID(func, bpf_obj_drop_impl) + static enum kfunc_ptr_arg_type get_kfunc_ptr_arg_type(struct bpf_verifier_env *env, struct bpf_kfunc_call_arg_meta *meta, @@ -8078,6 +8102,9 @@ get_kfunc_ptr_arg_type(struct bpf_verifier_env *env, if (btf_get_prog_ctx_type(&env->log, meta->btf, t, resolve_prog_type(env->prog), argno)) return KF_ARG_PTR_TO_CTX; + if (is_kfunc_arg_alloc_obj(meta->btf, &args[argno])) + return KF_ARG_PTR_TO_ALLOC_BTF_ID; + if (is_kfunc_arg_kptr_get(meta, argno)) { if (!btf_type_is_ptr(ref_t)) { verbose(env, "arg#0 BTF type must be a double pointer for kptr_get kfunc\n"); @@ -8294,6 +8321,7 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_kfunc_call_ return kf_arg_type; switch (kf_arg_type) { + case KF_ARG_PTR_TO_ALLOC_BTF_ID: case KF_ARG_PTR_TO_BTF_ID: if (!is_kfunc_trusted_args(meta)) break; @@ -8330,6 +8358,21 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_kfunc_call_ return -EINVAL; } break; + case KF_ARG_PTR_TO_ALLOC_BTF_ID: + if (reg->type != (PTR_TO_BTF_ID | MEM_ALLOC)) { + verbose(env, "arg#%d expected pointer to allocated object\n", i); + return -EINVAL; + } + if (!reg->ref_obj_id) { + verbose(env, "allocated object must be referenced\n"); + return -EINVAL; + } + if (meta->btf == btf_vmlinux && + meta->func_id == special_kfunc_list[KF_bpf_obj_drop_impl]) { + meta->arg_obj_drop.btf = reg->btf; + meta->arg_obj_drop.btf_id = reg->btf_id; + } + break; case KF_ARG_PTR_TO_KPTR: if (reg->type != PTR_TO_MAP_VALUE) { verbose(env, "arg#0 expected pointer to map value\n"); @@ -8400,17 +8443,6 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_kfunc_call_ return 0; } -enum special_kfunc_type { - KF_bpf_obj_new_impl, -}; - -BTF_SET_START(special_kfunc_set) -BTF_ID(func, bpf_obj_new_impl) -BTF_SET_END(special_kfunc_set) - -BTF_ID_LIST(special_kfunc_list) -BTF_ID(func, bpf_obj_new_impl) - static int check_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn, int *insn_idx_p) { @@ -8532,6 +8564,10 @@ static int check_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn, env->insn_aux_data[insn_idx].obj_new_size = ret_t->size; env->insn_aux_data[insn_idx].kptr_struct_meta = btf_find_struct_meta(ret_btf, ret_btf_id); + } else if (meta.func_id == special_kfunc_list[KF_bpf_obj_drop_impl]) { + env->insn_aux_data[insn_idx].kptr_struct_meta = + btf_find_struct_meta(meta.arg_obj_drop.btf, + meta.arg_obj_drop.btf_id); } else { verbose(env, "kernel function %s unhandled dynamic return type\n", meta.func_name); @@ -14768,6 +14804,14 @@ static int fixup_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn, insn_buf[2] = addr[1]; insn_buf[3] = *insn; *cnt = 4; + } else if (desc->func_id == special_kfunc_list[KF_bpf_obj_drop_impl]) { + struct btf_struct_meta *kptr_struct_meta = env->insn_aux_data[insn_idx].kptr_struct_meta; + struct bpf_insn addr[2] = { BPF_LD_IMM64(BPF_REG_2, (long)kptr_struct_meta) }; + + insn_buf[0] = addr[0]; + insn_buf[1] = addr[1]; + insn_buf[2] = *insn; + *cnt = 3; } return 0; } diff --git a/tools/testing/selftests/bpf/bpf_experimental.h b/tools/testing/selftests/bpf/bpf_experimental.h index aeb6a7fcb7c4..8473395a11af 100644 --- a/tools/testing/selftests/bpf/bpf_experimental.h +++ b/tools/testing/selftests/bpf/bpf_experimental.h @@ -22,4 +22,17 @@ extern void *bpf_obj_new_impl(__u64 local_type_id, void *meta) __ksym; /* Convenience macro to wrap over bpf_obj_new_impl */ #define bpf_obj_new(type) ((type *)bpf_obj_new_impl(bpf_core_type_id_local(type), NULL)) +/* Description + * Free an allocated object. All fields of the object that require + * destruction will be destructed before the storage is freed. + * + * The 'meta' parameter is a hidden argument that is ignored. + * Returns + * Void. + */ +extern void bpf_obj_drop_impl(void *kptr, void *meta) __ksym; + +/* Convenience macro to wrap over bpf_obj_drop_impl */ +#define bpf_obj_drop(kptr) bpf_obj_drop_impl(kptr, NULL) + #endif From df57f38a0d081f05ec48ea5aa7ca0564918ed915 Mon Sep 17 00:00:00 2001 From: Kumar Kartikeya Dwivedi Date: Fri, 18 Nov 2022 07:26:05 +0530 Subject: [PATCH 1015/2002] bpf: Permit NULL checking pointer with non-zero fixed offset Pointer increment on seeing PTR_MAYBE_NULL is already protected against, hence make an exception for PTR_TO_BTF_ID | MEM_ALLOC while still keeping the warning for other unintended cases that might creep in. bpf_list_pop_{front,_back} helpers planned to be introduced in next commit will return a MEM_ALLOC register with incremented offset pointing to bpf_list_node field. The user is supposed to then obtain the pointer to the entry using container_of after NULL checking it. The current restrictions trigger a warning when doing the NULL checking. Revisiting the reason, it is meant as an assertion which seems to actually work and catch the bad case. Hence, under no other circumstances can reg->off be non-zero for a register that has the PTR_MAYBE_NULL type flag set. Signed-off-by: Kumar Kartikeya Dwivedi Link: https://lore.kernel.org/r/20221118015614.2013203-16-memxor@gmail.com Signed-off-by: Alexei Starovoitov --- kernel/bpf/verifier.c | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 1fbb0b51c429..a339a39d895c 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -10791,16 +10791,19 @@ static void mark_ptr_or_null_reg(struct bpf_func_state *state, { if (type_may_be_null(reg->type) && reg->id == id && !WARN_ON_ONCE(!reg->id)) { - if (WARN_ON_ONCE(reg->smin_value || reg->smax_value || - !tnum_equals_const(reg->var_off, 0) || - reg->off)) { - /* Old offset (both fixed and variable parts) should - * have been known-zero, because we don't allow pointer - * arithmetic on pointers that might be NULL. If we - * see this happening, don't convert the register. - */ + /* Old offset (both fixed and variable parts) should have been + * known-zero, because we don't allow pointer arithmetic on + * pointers that might be NULL. If we see this happening, don't + * convert the register. + * + * But in some cases, some helpers that return local kptrs + * advance offset for the returned pointer. In those cases, it + * is fine to expect to see reg->off. + */ + if (WARN_ON_ONCE(reg->smin_value || reg->smax_value || !tnum_equals_const(reg->var_off, 0))) + return; + if (reg->type != (PTR_TO_BTF_ID | MEM_ALLOC | PTR_MAYBE_NULL) && WARN_ON_ONCE(reg->off)) return; - } if (is_null) { reg->type = SCALAR_VALUE; /* We don't need id and ref_obj_id from this point From 8cab76ec634995e59a8b6346bf8b835ab7fad3a3 Mon Sep 17 00:00:00 2001 From: Kumar Kartikeya Dwivedi Date: Fri, 18 Nov 2022 07:26:06 +0530 Subject: [PATCH 1016/2002] bpf: Introduce single ownership BPF linked list API Add a linked list API for use in BPF programs, where it expects protection from the bpf_spin_lock in the same allocation as the bpf_list_head. For now, only one bpf_spin_lock can be present hence that is assumed to be the one protecting the bpf_list_head. The following functions are added to kick things off: // Add node to beginning of list void bpf_list_push_front(struct bpf_list_head *head, struct bpf_list_node *node); // Add node to end of list void bpf_list_push_back(struct bpf_list_head *head, struct bpf_list_node *node); // Remove node at beginning of list and return it struct bpf_list_node *bpf_list_pop_front(struct bpf_list_head *head); // Remove node at end of list and return it struct bpf_list_node *bpf_list_pop_back(struct bpf_list_head *head); The lock protecting the bpf_list_head needs to be taken for all operations. The verifier ensures that the lock that needs to be taken is always held, and only the correct lock is taken for these operations. These checks are made statically by relying on the reg->id preserved for registers pointing into regions having both bpf_spin_lock and the objects protected by it. The comment over check_reg_allocation_locked in this change describes the logic in detail. Note that bpf_list_push_front and bpf_list_push_back are meant to consume the object containing the node in the 1st argument, however that specific mechanism is intended to not release the ref_obj_id directly until the bpf_spin_unlock is called. In this commit, nothing is done, but the next commit will be introducing logic to handle this case, so it has been left as is for now. bpf_list_pop_front and bpf_list_pop_back delete the first or last item of the list respectively, and return pointer to the element at the list_node offset. The user can then use container_of style macro to get the actual entry type. The verifier however statically knows the actual type, so the safety properties are still preserved. With these additions, programs can now manage their own linked lists and store their objects in them. Signed-off-by: Kumar Kartikeya Dwivedi Link: https://lore.kernel.org/r/20221118015614.2013203-17-memxor@gmail.com Signed-off-by: Alexei Starovoitov --- kernel/bpf/helpers.c | 55 +++- kernel/bpf/verifier.c | 275 +++++++++++++++++- .../testing/selftests/bpf/bpf_experimental.h | 28 ++ 3 files changed, 349 insertions(+), 9 deletions(-) diff --git a/kernel/bpf/helpers.c b/kernel/bpf/helpers.c index 71d803ca0c1d..212e791d7452 100644 --- a/kernel/bpf/helpers.c +++ b/kernel/bpf/helpers.c @@ -1780,6 +1780,50 @@ void bpf_obj_drop_impl(void *p__alloc, void *meta__ign) bpf_mem_free(&bpf_global_ma, p); } +static void __bpf_list_add(struct bpf_list_node *node, struct bpf_list_head *head, bool tail) +{ + struct list_head *n = (void *)node, *h = (void *)head; + + if (unlikely(!h->next)) + INIT_LIST_HEAD(h); + if (unlikely(!n->next)) + INIT_LIST_HEAD(n); + tail ? list_add_tail(n, h) : list_add(n, h); +} + +void bpf_list_push_front(struct bpf_list_head *head, struct bpf_list_node *node) +{ + return __bpf_list_add(node, head, false); +} + +void bpf_list_push_back(struct bpf_list_head *head, struct bpf_list_node *node) +{ + return __bpf_list_add(node, head, true); +} + +static struct bpf_list_node *__bpf_list_del(struct bpf_list_head *head, bool tail) +{ + struct list_head *n, *h = (void *)head; + + if (unlikely(!h->next)) + INIT_LIST_HEAD(h); + if (list_empty(h)) + return NULL; + n = tail ? h->prev : h->next; + list_del_init(n); + return (struct bpf_list_node *)n; +} + +struct bpf_list_node *bpf_list_pop_front(struct bpf_list_head *head) +{ + return __bpf_list_del(head, false); +} + +struct bpf_list_node *bpf_list_pop_back(struct bpf_list_head *head) +{ + return __bpf_list_del(head, true); +} + __diag_pop(); BTF_SET8_START(generic_btf_ids) @@ -1788,6 +1832,10 @@ BTF_ID_FLAGS(func, crash_kexec, KF_DESTRUCTIVE) #endif BTF_ID_FLAGS(func, bpf_obj_new_impl, KF_ACQUIRE | KF_RET_NULL) BTF_ID_FLAGS(func, bpf_obj_drop_impl, KF_RELEASE) +BTF_ID_FLAGS(func, bpf_list_push_front) +BTF_ID_FLAGS(func, bpf_list_push_back) +BTF_ID_FLAGS(func, bpf_list_pop_front, KF_ACQUIRE | KF_RET_NULL) +BTF_ID_FLAGS(func, bpf_list_pop_back, KF_ACQUIRE | KF_RET_NULL) BTF_SET8_END(generic_btf_ids) static const struct btf_kfunc_id_set generic_kfunc_set = { @@ -1797,7 +1845,12 @@ static const struct btf_kfunc_id_set generic_kfunc_set = { static int __init kfunc_init(void) { - return register_btf_kfunc_id_set(BPF_PROG_TYPE_TRACING, &generic_kfunc_set); + int ret; + + ret = register_btf_kfunc_id_set(BPF_PROG_TYPE_TRACING, &generic_kfunc_set); + if (ret) + return ret; + return register_btf_kfunc_id_set(BPF_PROG_TYPE_SCHED_CLS, &generic_kfunc_set); } late_initcall(kfunc_init); diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index a339a39d895c..1364df74129e 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -7883,6 +7883,9 @@ struct bpf_kfunc_call_arg_meta { struct btf *btf; u32 btf_id; } arg_obj_drop; + struct { + struct btf_field *field; + } arg_list_head; }; static bool is_kfunc_acquire(struct bpf_kfunc_call_arg_meta *meta) @@ -7987,13 +7990,17 @@ static bool is_kfunc_arg_scalar_with_name(const struct btf *btf, enum { KF_ARG_DYNPTR_ID, + KF_ARG_LIST_HEAD_ID, + KF_ARG_LIST_NODE_ID, }; BTF_ID_LIST(kf_arg_btf_ids) BTF_ID(struct, bpf_dynptr_kern) +BTF_ID(struct, bpf_list_head) +BTF_ID(struct, bpf_list_node) -static bool is_kfunc_arg_dynptr(const struct btf *btf, - const struct btf_param *arg) +static bool __is_kfunc_ptr_arg_type(const struct btf *btf, + const struct btf_param *arg, int type) { const struct btf_type *t; u32 res_id; @@ -8006,7 +8013,22 @@ static bool is_kfunc_arg_dynptr(const struct btf *btf, t = btf_type_skip_modifiers(btf, t->type, &res_id); if (!t) return false; - return btf_types_are_same(btf, res_id, btf_vmlinux, kf_arg_btf_ids[KF_ARG_DYNPTR_ID]); + return btf_types_are_same(btf, res_id, btf_vmlinux, kf_arg_btf_ids[type]); +} + +static bool is_kfunc_arg_dynptr(const struct btf *btf, const struct btf_param *arg) +{ + return __is_kfunc_ptr_arg_type(btf, arg, KF_ARG_DYNPTR_ID); +} + +static bool is_kfunc_arg_list_head(const struct btf *btf, const struct btf_param *arg) +{ + return __is_kfunc_ptr_arg_type(btf, arg, KF_ARG_LIST_HEAD_ID); +} + +static bool is_kfunc_arg_list_node(const struct btf *btf, const struct btf_param *arg) +{ + return __is_kfunc_ptr_arg_type(btf, arg, KF_ARG_LIST_NODE_ID); } /* Returns true if struct is composed of scalars, 4 levels of nesting allowed */ @@ -8063,6 +8085,8 @@ enum kfunc_ptr_arg_type { KF_ARG_PTR_TO_ALLOC_BTF_ID, /* Allocated object */ KF_ARG_PTR_TO_KPTR, /* PTR_TO_KPTR but type specific */ KF_ARG_PTR_TO_DYNPTR, + KF_ARG_PTR_TO_LIST_HEAD, + KF_ARG_PTR_TO_LIST_NODE, KF_ARG_PTR_TO_BTF_ID, /* Also covers reg2btf_ids conversions */ KF_ARG_PTR_TO_MEM, KF_ARG_PTR_TO_MEM_SIZE, /* Size derived from next argument, skip it */ @@ -8071,16 +8095,28 @@ enum kfunc_ptr_arg_type { enum special_kfunc_type { KF_bpf_obj_new_impl, KF_bpf_obj_drop_impl, + KF_bpf_list_push_front, + KF_bpf_list_push_back, + KF_bpf_list_pop_front, + KF_bpf_list_pop_back, }; BTF_SET_START(special_kfunc_set) BTF_ID(func, bpf_obj_new_impl) BTF_ID(func, bpf_obj_drop_impl) +BTF_ID(func, bpf_list_push_front) +BTF_ID(func, bpf_list_push_back) +BTF_ID(func, bpf_list_pop_front) +BTF_ID(func, bpf_list_pop_back) BTF_SET_END(special_kfunc_set) BTF_ID_LIST(special_kfunc_list) BTF_ID(func, bpf_obj_new_impl) BTF_ID(func, bpf_obj_drop_impl) +BTF_ID(func, bpf_list_push_front) +BTF_ID(func, bpf_list_push_back) +BTF_ID(func, bpf_list_pop_front) +BTF_ID(func, bpf_list_pop_back) static enum kfunc_ptr_arg_type get_kfunc_ptr_arg_type(struct bpf_verifier_env *env, @@ -8123,6 +8159,12 @@ get_kfunc_ptr_arg_type(struct bpf_verifier_env *env, if (is_kfunc_arg_dynptr(meta->btf, &args[argno])) return KF_ARG_PTR_TO_DYNPTR; + if (is_kfunc_arg_list_head(meta->btf, &args[argno])) + return KF_ARG_PTR_TO_LIST_HEAD; + + if (is_kfunc_arg_list_node(meta->btf, &args[argno])) + return KF_ARG_PTR_TO_LIST_NODE; + if ((base_type(reg->type) == PTR_TO_BTF_ID || reg2btf_ids[base_type(reg->type)])) { if (!btf_type_is_struct(ref_t)) { verbose(env, "kernel function %s args#%d pointer type %s %s is not supported\n", @@ -8218,6 +8260,182 @@ static int process_kf_arg_ptr_to_kptr(struct bpf_verifier_env *env, return 0; } +/* Implementation details: + * + * Each register points to some region of memory, which we define as an + * allocation. Each allocation may embed a bpf_spin_lock which protects any + * special BPF objects (bpf_list_head, bpf_rb_root, etc.) part of the same + * allocation. The lock and the data it protects are colocated in the same + * memory region. + * + * Hence, everytime a register holds a pointer value pointing to such + * allocation, the verifier preserves a unique reg->id for it. + * + * The verifier remembers the lock 'ptr' and the lock 'id' whenever + * bpf_spin_lock is called. + * + * To enable this, lock state in the verifier captures two values: + * active_lock.ptr = Register's type specific pointer + * active_lock.id = A unique ID for each register pointer value + * + * Currently, PTR_TO_MAP_VALUE and PTR_TO_BTF_ID | MEM_ALLOC are the two + * supported register types. + * + * The active_lock.ptr in case of map values is the reg->map_ptr, and in case of + * allocated objects is the reg->btf pointer. + * + * The active_lock.id is non-unique for maps supporting direct_value_addr, as we + * can establish the provenance of the map value statically for each distinct + * lookup into such maps. They always contain a single map value hence unique + * IDs for each pseudo load pessimizes the algorithm and rejects valid programs. + * + * So, in case of global variables, they use array maps with max_entries = 1, + * hence their active_lock.ptr becomes map_ptr and id = 0 (since they all point + * into the same map value as max_entries is 1, as described above). + * + * In case of inner map lookups, the inner map pointer has same map_ptr as the + * outer map pointer (in verifier context), but each lookup into an inner map + * assigns a fresh reg->id to the lookup, so while lookups into distinct inner + * maps from the same outer map share the same map_ptr as active_lock.ptr, they + * will get different reg->id assigned to each lookup, hence different + * active_lock.id. + * + * In case of allocated objects, active_lock.ptr is the reg->btf, and the + * reg->id is a unique ID preserved after the NULL pointer check on the pointer + * returned from bpf_obj_new. Each allocation receives a new reg->id. + */ +static int check_reg_allocation_locked(struct bpf_verifier_env *env, struct bpf_reg_state *reg) +{ + void *ptr; + u32 id; + + switch ((int)reg->type) { + case PTR_TO_MAP_VALUE: + ptr = reg->map_ptr; + break; + case PTR_TO_BTF_ID | MEM_ALLOC: + ptr = reg->btf; + break; + default: + verbose(env, "verifier internal error: unknown reg type for lock check\n"); + return -EFAULT; + } + id = reg->id; + + if (!env->cur_state->active_lock.ptr) + return -EINVAL; + if (env->cur_state->active_lock.ptr != ptr || + env->cur_state->active_lock.id != id) { + verbose(env, "held lock and object are not in the same allocation\n"); + return -EINVAL; + } + return 0; +} + +static bool is_bpf_list_api_kfunc(u32 btf_id) +{ + return btf_id == special_kfunc_list[KF_bpf_list_push_front] || + btf_id == special_kfunc_list[KF_bpf_list_push_back] || + btf_id == special_kfunc_list[KF_bpf_list_pop_front] || + btf_id == special_kfunc_list[KF_bpf_list_pop_back]; +} + +static int process_kf_arg_ptr_to_list_head(struct bpf_verifier_env *env, + struct bpf_reg_state *reg, u32 regno, + struct bpf_kfunc_call_arg_meta *meta) +{ + struct btf_field *field; + struct btf_record *rec; + u32 list_head_off; + + if (meta->btf != btf_vmlinux || !is_bpf_list_api_kfunc(meta->func_id)) { + verbose(env, "verifier internal error: bpf_list_head argument for unknown kfunc\n"); + return -EFAULT; + } + + if (!tnum_is_const(reg->var_off)) { + verbose(env, + "R%d doesn't have constant offset. bpf_list_head has to be at the constant offset\n", + regno); + return -EINVAL; + } + + rec = reg_btf_record(reg); + list_head_off = reg->off + reg->var_off.value; + field = btf_record_find(rec, list_head_off, BPF_LIST_HEAD); + if (!field) { + verbose(env, "bpf_list_head not found at offset=%u\n", list_head_off); + return -EINVAL; + } + + /* All functions require bpf_list_head to be protected using a bpf_spin_lock */ + if (check_reg_allocation_locked(env, reg)) { + verbose(env, "bpf_spin_lock at off=%d must be held for bpf_list_head\n", + rec->spin_lock_off); + return -EINVAL; + } + + if (meta->arg_list_head.field) { + verbose(env, "verifier internal error: repeating bpf_list_head arg\n"); + return -EFAULT; + } + meta->arg_list_head.field = field; + return 0; +} + +static int process_kf_arg_ptr_to_list_node(struct bpf_verifier_env *env, + struct bpf_reg_state *reg, u32 regno, + struct bpf_kfunc_call_arg_meta *meta) +{ + const struct btf_type *et, *t; + struct btf_field *field; + struct btf_record *rec; + u32 list_node_off; + + if (meta->btf != btf_vmlinux || + (meta->func_id != special_kfunc_list[KF_bpf_list_push_front] && + meta->func_id != special_kfunc_list[KF_bpf_list_push_back])) { + verbose(env, "verifier internal error: bpf_list_node argument for unknown kfunc\n"); + return -EFAULT; + } + + if (!tnum_is_const(reg->var_off)) { + verbose(env, + "R%d doesn't have constant offset. bpf_list_node has to be at the constant offset\n", + regno); + return -EINVAL; + } + + rec = reg_btf_record(reg); + list_node_off = reg->off + reg->var_off.value; + field = btf_record_find(rec, list_node_off, BPF_LIST_NODE); + if (!field || field->offset != list_node_off) { + verbose(env, "bpf_list_node not found at offset=%u\n", list_node_off); + return -EINVAL; + } + + field = meta->arg_list_head.field; + + et = btf_type_by_id(field->list_head.btf, field->list_head.value_btf_id); + t = btf_type_by_id(reg->btf, reg->btf_id); + if (!btf_struct_ids_match(&env->log, reg->btf, reg->btf_id, 0, field->list_head.btf, + field->list_head.value_btf_id, true)) { + verbose(env, "operation on bpf_list_head expects arg#1 bpf_list_node at offset=%d " + "in struct %s, but arg is at offset=%d in struct %s\n", + field->list_head.node_offset, btf_name_by_offset(field->list_head.btf, et->name_off), + list_node_off, btf_name_by_offset(reg->btf, t->name_off)); + return -EINVAL; + } + + if (list_node_off != field->list_head.node_offset) { + verbose(env, "arg#1 offset=%d, but expected bpf_list_node at offset=%d in struct %s\n", + list_node_off, field->list_head.node_offset, + btf_name_by_offset(field->list_head.btf, et->name_off)); + return -EINVAL; + } + return 0; +} + static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_kfunc_call_arg_meta *meta) { const char *func_name = meta->func_name, *ref_tname; @@ -8336,6 +8554,8 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_kfunc_call_ break; case KF_ARG_PTR_TO_KPTR: case KF_ARG_PTR_TO_DYNPTR: + case KF_ARG_PTR_TO_LIST_HEAD: + case KF_ARG_PTR_TO_LIST_NODE: case KF_ARG_PTR_TO_MEM: case KF_ARG_PTR_TO_MEM_SIZE: /* Trusted by default */ @@ -8400,6 +8620,33 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_kfunc_call_ return -EINVAL; } break; + case KF_ARG_PTR_TO_LIST_HEAD: + if (reg->type != PTR_TO_MAP_VALUE && + reg->type != (PTR_TO_BTF_ID | MEM_ALLOC)) { + verbose(env, "arg#%d expected pointer to map value or allocated object\n", i); + return -EINVAL; + } + if (reg->type == (PTR_TO_BTF_ID | MEM_ALLOC) && !reg->ref_obj_id) { + verbose(env, "allocated object must be referenced\n"); + return -EINVAL; + } + ret = process_kf_arg_ptr_to_list_head(env, reg, regno, meta); + if (ret < 0) + return ret; + break; + case KF_ARG_PTR_TO_LIST_NODE: + if (reg->type != (PTR_TO_BTF_ID | MEM_ALLOC)) { + verbose(env, "arg#%d expected pointer to allocated object\n", i); + return -EINVAL; + } + if (!reg->ref_obj_id) { + verbose(env, "allocated object must be referenced\n"); + return -EINVAL; + } + ret = process_kf_arg_ptr_to_list_node(env, reg, regno, meta); + if (ret < 0) + return ret; + break; case KF_ARG_PTR_TO_BTF_ID: /* Only base_type is checked, further checks are done here */ if (reg->type != PTR_TO_BTF_ID && @@ -8568,6 +8815,15 @@ static int check_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn, env->insn_aux_data[insn_idx].kptr_struct_meta = btf_find_struct_meta(meta.arg_obj_drop.btf, meta.arg_obj_drop.btf_id); + } else if (meta.func_id == special_kfunc_list[KF_bpf_list_pop_front] || + meta.func_id == special_kfunc_list[KF_bpf_list_pop_back]) { + struct btf_field *field = meta.arg_list_head.field; + + mark_reg_known_zero(env, regs, BPF_REG_0); + regs[BPF_REG_0].type = PTR_TO_BTF_ID | MEM_ALLOC; + regs[BPF_REG_0].btf = field->list_head.btf; + regs[BPF_REG_0].btf_id = field->list_head.value_btf_id; + regs[BPF_REG_0].off = field->list_head.node_offset; } else { verbose(env, "kernel function %s unhandled dynamic return type\n", meta.func_name); @@ -13264,11 +13520,14 @@ static int do_check(struct bpf_verifier_env *env) return -EINVAL; } - if (env->cur_state->active_lock.ptr && - (insn->src_reg == BPF_PSEUDO_CALL || - insn->imm != BPF_FUNC_spin_unlock)) { - verbose(env, "function calls are not allowed while holding a lock\n"); - return -EINVAL; + if (env->cur_state->active_lock.ptr) { + if ((insn->src_reg == BPF_REG_0 && insn->imm != BPF_FUNC_spin_unlock) || + (insn->src_reg == BPF_PSEUDO_CALL) || + (insn->src_reg == BPF_PSEUDO_KFUNC_CALL && + (insn->off != 0 || !is_bpf_list_api_kfunc(insn->imm)))) { + verbose(env, "function calls are not allowed while holding a lock\n"); + return -EINVAL; + } } if (insn->src_reg == BPF_PSEUDO_CALL) err = check_func_call(env, insn, &env->insn_idx); diff --git a/tools/testing/selftests/bpf/bpf_experimental.h b/tools/testing/selftests/bpf/bpf_experimental.h index 8473395a11af..d6b143275e82 100644 --- a/tools/testing/selftests/bpf/bpf_experimental.h +++ b/tools/testing/selftests/bpf/bpf_experimental.h @@ -35,4 +35,32 @@ extern void bpf_obj_drop_impl(void *kptr, void *meta) __ksym; /* Convenience macro to wrap over bpf_obj_drop_impl */ #define bpf_obj_drop(kptr) bpf_obj_drop_impl(kptr, NULL) +/* Description + * Add a new entry to the beginning of the BPF linked list. + * Returns + * Void. + */ +extern void bpf_list_push_front(struct bpf_list_head *head, struct bpf_list_node *node) __ksym; + +/* Description + * Add a new entry to the end of the BPF linked list. + * Returns + * Void. + */ +extern void bpf_list_push_back(struct bpf_list_head *head, struct bpf_list_node *node) __ksym; + +/* Description + * Remove the entry at the beginning of the BPF linked list. + * Returns + * Pointer to bpf_list_node of deleted entry, or NULL if list is empty. + */ +extern struct bpf_list_node *bpf_list_pop_front(struct bpf_list_head *head) __ksym; + +/* Description + * Remove the entry at the end of the BPF linked list. + * Returns + * Pointer to bpf_list_node of deleted entry, or NULL if list is empty. + */ +extern struct bpf_list_node *bpf_list_pop_back(struct bpf_list_head *head) __ksym; + #endif From 534e86bc6c66e1e0c798a1c0a6a680bb231c08db Mon Sep 17 00:00:00 2001 From: Kumar Kartikeya Dwivedi Date: Fri, 18 Nov 2022 07:26:07 +0530 Subject: [PATCH 1017/2002] bpf: Add 'release on unlock' logic for bpf_list_push_{front,back} This commit implements the delayed release logic for bpf_list_push_front and bpf_list_push_back. Once a node has been added to the list, it's pointer changes to PTR_UNTRUSTED. However, it is only released once the lock protecting the list is unlocked. For such PTR_TO_BTF_ID | MEM_ALLOC with PTR_UNTRUSTED set but an active ref_obj_id, it is still permitted to read them as long as the lock is held. Writing to them is not allowed. This allows having read access to push items we no longer own until we release the lock guarding the list, allowing a little more flexibility when working with these APIs. Note that enabling write support has fairly tricky interactions with what happens inside the critical section. Just as an example, currently, bpf_obj_drop is not permitted, but if it were, being able to write to the PTR_UNTRUSTED pointer while the object gets released back to the memory allocator would violate safety properties we wish to guarantee (i.e. not crashing the kernel). The memory could be reused for a different type in the BPF program or even in the kernel as it gets eventually kfree'd. Not enabling bpf_obj_drop inside the critical section would appear to prevent all of the above, but that is more of an artifical limitation right now. Since the write support is tangled with how we handle potential aliasing of nodes inside the critical section that may or may not be part of the list anymore, it has been deferred to a future patch. Acked-by: Dave Marchevsky Signed-off-by: Kumar Kartikeya Dwivedi Link: https://lore.kernel.org/r/20221118015614.2013203-18-memxor@gmail.com Signed-off-by: Alexei Starovoitov --- include/linux/bpf_verifier.h | 5 ++++ kernel/bpf/verifier.c | 58 +++++++++++++++++++++++++++++++++++- 2 files changed, 62 insertions(+), 1 deletion(-) diff --git a/include/linux/bpf_verifier.h b/include/linux/bpf_verifier.h index 3dc72d396dfc..23f30c685f28 100644 --- a/include/linux/bpf_verifier.h +++ b/include/linux/bpf_verifier.h @@ -223,6 +223,11 @@ struct bpf_reference_state { * exiting a callback function. */ int callback_ref; + /* Mark the reference state to release the registers sharing the same id + * on bpf_spin_unlock (for nodes that we will lose ownership to but are + * safe to access inside the critical section). + */ + bool release_on_unlock; }; /* state of the program: diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 1364df74129e..195d24316750 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -5652,7 +5652,9 @@ static int process_spin_lock(struct bpf_verifier_env *env, int regno, cur->active_lock.ptr = btf; cur->active_lock.id = reg->id; } else { + struct bpf_func_state *fstate = cur_func(env); void *ptr; + int i; if (map) ptr = map; @@ -5670,6 +5672,23 @@ static int process_spin_lock(struct bpf_verifier_env *env, int regno, } cur->active_lock.ptr = NULL; cur->active_lock.id = 0; + + for (i = 0; i < fstate->acquired_refs; i++) { + int err; + + /* Complain on error because this reference state cannot + * be freed before this point, as bpf_spin_lock critical + * section does not allow functions that release the + * allocated object immediately. + */ + if (!fstate->refs[i].release_on_unlock) + continue; + err = release_reference(env, fstate->refs[i].id); + if (err) { + verbose(env, "failed to release release_on_unlock reference"); + return err; + } + } } return 0; } @@ -8260,6 +8279,42 @@ static int process_kf_arg_ptr_to_kptr(struct bpf_verifier_env *env, return 0; } +static int ref_set_release_on_unlock(struct bpf_verifier_env *env, u32 ref_obj_id) +{ + struct bpf_func_state *state = cur_func(env); + struct bpf_reg_state *reg; + int i; + + /* bpf_spin_lock only allows calling list_push and list_pop, no BPF + * subprogs, no global functions. This means that the references would + * not be released inside the critical section but they may be added to + * the reference state, and the acquired_refs are never copied out for a + * different frame as BPF to BPF calls don't work in bpf_spin_lock + * critical sections. + */ + if (!ref_obj_id) { + verbose(env, "verifier internal error: ref_obj_id is zero for release_on_unlock\n"); + return -EFAULT; + } + for (i = 0; i < state->acquired_refs; i++) { + if (state->refs[i].id == ref_obj_id) { + if (state->refs[i].release_on_unlock) { + verbose(env, "verifier internal error: expected false release_on_unlock"); + return -EFAULT; + } + state->refs[i].release_on_unlock = true; + /* Now mark everyone sharing same ref_obj_id as untrusted */ + bpf_for_each_reg_in_vstate(env->cur_state, state, reg, ({ + if (reg->ref_obj_id == ref_obj_id) + reg->type |= PTR_UNTRUSTED; + })); + return 0; + } + } + verbose(env, "verifier internal error: ref state missing for ref_obj_id\n"); + return -EFAULT; +} + /* Implementation details: * * Each register points to some region of memory, which we define as an @@ -8433,7 +8488,8 @@ static int process_kf_arg_ptr_to_list_node(struct bpf_verifier_env *env, btf_name_by_offset(field->list_head.btf, et->name_off)); return -EINVAL; } - return 0; + /* Set arg#1 for expiration after unlock */ + return ref_set_release_on_unlock(env, reg->ref_obj_id); } static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_kfunc_call_arg_meta *meta) From c22dfdd21592c5d56b49d5fba8de300ad7bf293c Mon Sep 17 00:00:00 2001 From: Kumar Kartikeya Dwivedi Date: Fri, 18 Nov 2022 07:26:08 +0530 Subject: [PATCH 1018/2002] bpf: Add comments for map BTF matching requirement for bpf_list_head The old behavior of bpf_map_meta_equal was that it compared timer_off to be equal (but not spin_lock_off, because that was not allowed), and did memcmp of kptr_off_tab. Now, we memcmp the btf_record of two bpf_map structs, which has all fields. We preserve backwards compat as we kzalloc the array, so if only spin lock and timer exist in map, we only compare offset while the rest of unused members in the btf_field struct are zeroed out. In case of kptr, btf and everything else is of vmlinux or module, so as long type is same it will match, since kernel btf, module, dtor pointer will be same across maps. Now with list_head in the mix, things are a bit complicated. We implicitly add a requirement that both BTFs are same, because struct btf_field_list_head has btf and value_rec members. We obviously shouldn't force BTFs to be equal by default, as that breaks backwards compatibility. Currently it is only implicitly required due to list_head matching struct btf and value_rec member. value_rec points back into a btf_record stashed in the map BTF (btf member of btf_field_list_head). So that pointer and btf member has to match exactly. Document all these subtle details so that things don't break in the future when touching this code. Signed-off-by: Kumar Kartikeya Dwivedi Link: https://lore.kernel.org/r/20221118015614.2013203-19-memxor@gmail.com Signed-off-by: Alexei Starovoitov --- kernel/bpf/btf.c | 3 +++ kernel/bpf/map_in_map.c | 5 +++++ kernel/bpf/syscall.c | 14 ++++++++++++++ 3 files changed, 22 insertions(+) diff --git a/kernel/bpf/btf.c b/kernel/bpf/btf.c index 4dcda4ae48c1..f7d5fab61535 100644 --- a/kernel/bpf/btf.c +++ b/kernel/bpf/btf.c @@ -3648,6 +3648,9 @@ struct btf_record *btf_parse_fields(const struct btf *btf, const struct btf_type return NULL; cnt = ret; + /* This needs to be kzalloc to zero out padding and unused fields, see + * comment in btf_record_equal. + */ rec = kzalloc(offsetof(struct btf_record, fields[cnt]), GFP_KERNEL | __GFP_NOWARN); if (!rec) return ERR_PTR(-ENOMEM); diff --git a/kernel/bpf/map_in_map.c b/kernel/bpf/map_in_map.c index 7cce2047c6ef..38136ec4e095 100644 --- a/kernel/bpf/map_in_map.c +++ b/kernel/bpf/map_in_map.c @@ -68,6 +68,11 @@ struct bpf_map *bpf_map_meta_alloc(int inner_map_ufd) } inner_map_meta->field_offs = field_offs; } + /* Note: We must use the same BTF, as we also used btf_record_dup above + * which relies on BTF being same for both maps, as some members like + * record->fields.list_head have pointers like value_rec pointing into + * inner_map->btf. + */ if (inner_map->btf) { btf_get(inner_map->btf); inner_map_meta->btf = inner_map->btf; diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c index 6140cbc3ed8a..35972afb6850 100644 --- a/kernel/bpf/syscall.c +++ b/kernel/bpf/syscall.c @@ -611,6 +611,20 @@ bool btf_record_equal(const struct btf_record *rec_a, const struct btf_record *r if (rec_a->cnt != rec_b->cnt) return false; size = offsetof(struct btf_record, fields[rec_a->cnt]); + /* btf_parse_fields uses kzalloc to allocate a btf_record, so unused + * members are zeroed out. So memcmp is safe to do without worrying + * about padding/unused fields. + * + * While spin_lock, timer, and kptr have no relation to map BTF, + * list_head metadata is specific to map BTF, the btf and value_rec + * members in particular. btf is the map BTF, while value_rec points to + * btf_record in that map BTF. + * + * So while by default, we don't rely on the map BTF (which the records + * were parsed from) matching for both records, which is not backwards + * compatible, in case list_head is part of it, we implicitly rely on + * that by way of depending on memcmp succeeding for it. + */ return !memcmp(rec_a, rec_b, size); } From 64069c72b4b8e44f6876249cc8f2e2ee4d209a93 Mon Sep 17 00:00:00 2001 From: Kumar Kartikeya Dwivedi Date: Fri, 18 Nov 2022 07:26:09 +0530 Subject: [PATCH 1019/2002] selftests/bpf: Add __contains macro to bpf_experimental.h Add user facing __contains macro which provides a convenient wrapper over the verbose kernel specific BTF declaration tag required to annotate BPF list head structs in user types. Acked-by: Dave Marchevsky Signed-off-by: Kumar Kartikeya Dwivedi Link: https://lore.kernel.org/r/20221118015614.2013203-20-memxor@gmail.com Signed-off-by: Alexei Starovoitov --- tools/testing/selftests/bpf/bpf_experimental.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tools/testing/selftests/bpf/bpf_experimental.h b/tools/testing/selftests/bpf/bpf_experimental.h index d6b143275e82..424f7bbbfe9b 100644 --- a/tools/testing/selftests/bpf/bpf_experimental.h +++ b/tools/testing/selftests/bpf/bpf_experimental.h @@ -6,6 +6,8 @@ #include #include +#define __contains(name, node) __attribute__((btf_decl_tag("contains:" #name ":" #node))) + /* Description * Allocates an object of the type represented by 'local_type_id' in * program BTF. User may use the bpf_core_type_id_local macro to pass the From d85aedac4dc43deaba7aabc78198d0600bb84887 Mon Sep 17 00:00:00 2001 From: Kumar Kartikeya Dwivedi Date: Fri, 18 Nov 2022 07:26:10 +0530 Subject: [PATCH 1020/2002] selftests/bpf: Update spinlock selftest Make updates in preparation for adding more test cases to this selftest: - Convert from CHECK_ to ASSERT macros. - Use BPF skeleton - Fix typo sping -> spin - Rename spinlock.c -> spin_lock.c Signed-off-by: Kumar Kartikeya Dwivedi Link: https://lore.kernel.org/r/20221118015614.2013203-21-memxor@gmail.com Signed-off-by: Alexei Starovoitov --- .../selftests/bpf/prog_tests/spin_lock.c | 49 +++++++++++++++++++ .../selftests/bpf/prog_tests/spinlock.c | 45 ----------------- .../selftests/bpf/progs/test_spin_lock.c | 4 +- 3 files changed, 51 insertions(+), 47 deletions(-) create mode 100644 tools/testing/selftests/bpf/prog_tests/spin_lock.c delete mode 100644 tools/testing/selftests/bpf/prog_tests/spinlock.c diff --git a/tools/testing/selftests/bpf/prog_tests/spin_lock.c b/tools/testing/selftests/bpf/prog_tests/spin_lock.c new file mode 100644 index 000000000000..fab061e9d77c --- /dev/null +++ b/tools/testing/selftests/bpf/prog_tests/spin_lock.c @@ -0,0 +1,49 @@ +// SPDX-License-Identifier: GPL-2.0 +#include +#include + +#include "test_spin_lock.skel.h" + +static void *spin_lock_thread(void *arg) +{ + int err, prog_fd = *(u32 *) arg; + LIBBPF_OPTS(bpf_test_run_opts, topts, + .data_in = &pkt_v4, + .data_size_in = sizeof(pkt_v4), + .repeat = 10000, + ); + + err = bpf_prog_test_run_opts(prog_fd, &topts); + ASSERT_OK(err, "test_run"); + ASSERT_OK(topts.retval, "test_run retval"); + pthread_exit(arg); +} + +void test_spinlock(void) +{ + struct test_spin_lock *skel; + pthread_t thread_id[4]; + int prog_fd, i; + void *ret; + + skel = test_spin_lock__open_and_load(); + if (!ASSERT_OK_PTR(skel, "test_spin_lock__open_and_load")) + return; + prog_fd = bpf_program__fd(skel->progs.bpf_spin_lock_test); + for (i = 0; i < 4; i++) { + int err; + + err = pthread_create(&thread_id[i], NULL, &spin_lock_thread, &prog_fd); + if (!ASSERT_OK(err, "pthread_create")) + goto end; + } + + for (i = 0; i < 4; i++) { + if (!ASSERT_OK(pthread_join(thread_id[i], &ret), "pthread_join")) + goto end; + if (!ASSERT_EQ(ret, &prog_fd, "ret == prog_fd")) + goto end; + } +end: + test_spin_lock__destroy(skel); +} diff --git a/tools/testing/selftests/bpf/prog_tests/spinlock.c b/tools/testing/selftests/bpf/prog_tests/spinlock.c deleted file mode 100644 index 15eb1372d771..000000000000 --- a/tools/testing/selftests/bpf/prog_tests/spinlock.c +++ /dev/null @@ -1,45 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -#include -#include - -static void *spin_lock_thread(void *arg) -{ - int err, prog_fd = *(u32 *) arg; - LIBBPF_OPTS(bpf_test_run_opts, topts, - .data_in = &pkt_v4, - .data_size_in = sizeof(pkt_v4), - .repeat = 10000, - ); - - err = bpf_prog_test_run_opts(prog_fd, &topts); - ASSERT_OK(err, "test_run"); - ASSERT_OK(topts.retval, "test_run retval"); - pthread_exit(arg); -} - -void test_spinlock(void) -{ - const char *file = "./test_spin_lock.bpf.o"; - pthread_t thread_id[4]; - struct bpf_object *obj = NULL; - int prog_fd; - int err = 0, i; - void *ret; - - err = bpf_prog_test_load(file, BPF_PROG_TYPE_CGROUP_SKB, &obj, &prog_fd); - if (CHECK_FAIL(err)) { - printf("test_spin_lock:bpf_prog_test_load errno %d\n", errno); - goto close_prog; - } - for (i = 0; i < 4; i++) - if (CHECK_FAIL(pthread_create(&thread_id[i], NULL, - &spin_lock_thread, &prog_fd))) - goto close_prog; - - for (i = 0; i < 4; i++) - if (CHECK_FAIL(pthread_join(thread_id[i], &ret) || - ret != (void *)&prog_fd)) - goto close_prog; -close_prog: - bpf_object__close(obj); -} diff --git a/tools/testing/selftests/bpf/progs/test_spin_lock.c b/tools/testing/selftests/bpf/progs/test_spin_lock.c index 7e88309d3229..5bd10409285b 100644 --- a/tools/testing/selftests/bpf/progs/test_spin_lock.c +++ b/tools/testing/selftests/bpf/progs/test_spin_lock.c @@ -45,8 +45,8 @@ struct { #define CREDIT_PER_NS(delta, rate) (((delta) * rate) >> 20) -SEC("tc") -int bpf_sping_lock_test(struct __sk_buff *skb) +SEC("cgroup_skb/ingress") +int bpf_spin_lock_test(struct __sk_buff *skb) { volatile int credit = 0, max_credit = 100, pkt_len = 64; struct hmap_elem zero = {}, *val; From c48748aea4f806587813f02219ca0b4910646c5e Mon Sep 17 00:00:00 2001 From: Kumar Kartikeya Dwivedi Date: Fri, 18 Nov 2022 07:26:11 +0530 Subject: [PATCH 1021/2002] selftests/bpf: Add failure test cases for spin lock pairing First, ensure that whenever a bpf_spin_lock is present in an allocation, the reg->id is preserved. This won't be true for global variables however, since they have a single map value per map, hence the verifier harcodes it to 0 (so that multiple pseudo ldimm64 insns can yield the same lock object per map at a given offset). Next, add test cases for all possible combinations (kptr, global, map value, inner map value). Since we lifted restriction on locking in inner maps, also add test cases for them. Currently, each lookup into an inner map gets a fresh reg->id, so even if the reg->map_ptr is same, they will be treated as separate allocations and the incorrect unlock pairing will be rejected. Signed-off-by: Kumar Kartikeya Dwivedi Link: https://lore.kernel.org/r/20221118015614.2013203-22-memxor@gmail.com Signed-off-by: Alexei Starovoitov --- .../selftests/bpf/prog_tests/spin_lock.c | 89 +++++++- .../selftests/bpf/progs/test_spin_lock_fail.c | 204 ++++++++++++++++++ 2 files changed, 292 insertions(+), 1 deletion(-) create mode 100644 tools/testing/selftests/bpf/progs/test_spin_lock_fail.c diff --git a/tools/testing/selftests/bpf/prog_tests/spin_lock.c b/tools/testing/selftests/bpf/prog_tests/spin_lock.c index fab061e9d77c..72282e92a78a 100644 --- a/tools/testing/selftests/bpf/prog_tests/spin_lock.c +++ b/tools/testing/selftests/bpf/prog_tests/spin_lock.c @@ -3,6 +3,79 @@ #include #include "test_spin_lock.skel.h" +#include "test_spin_lock_fail.skel.h" + +static char log_buf[1024 * 1024]; + +static struct { + const char *prog_name; + const char *err_msg; +} spin_lock_fail_tests[] = { + { "lock_id_kptr_preserve", + "5: (bf) r1 = r0 ; R0_w=ptr_foo(id=2,ref_obj_id=2,off=0,imm=0) " + "R1_w=ptr_foo(id=2,ref_obj_id=2,off=0,imm=0) refs=2\n6: (85) call bpf_this_cpu_ptr#154\n" + "R1 type=ptr_ expected=percpu_ptr_" }, + { "lock_id_global_zero", + "; R1_w=map_value(off=0,ks=4,vs=4,imm=0)\n2: (85) call bpf_this_cpu_ptr#154\n" + "R1 type=map_value expected=percpu_ptr_" }, + { "lock_id_mapval_preserve", + "8: (bf) r1 = r0 ; R0_w=map_value(id=1,off=0,ks=4,vs=8,imm=0) " + "R1_w=map_value(id=1,off=0,ks=4,vs=8,imm=0)\n9: (85) call bpf_this_cpu_ptr#154\n" + "R1 type=map_value expected=percpu_ptr_" }, + { "lock_id_innermapval_preserve", + "13: (bf) r1 = r0 ; R0=map_value(id=2,off=0,ks=4,vs=8,imm=0) " + "R1_w=map_value(id=2,off=0,ks=4,vs=8,imm=0)\n14: (85) call bpf_this_cpu_ptr#154\n" + "R1 type=map_value expected=percpu_ptr_" }, + { "lock_id_mismatch_kptr_kptr", "bpf_spin_unlock of different lock" }, + { "lock_id_mismatch_kptr_global", "bpf_spin_unlock of different lock" }, + { "lock_id_mismatch_kptr_mapval", "bpf_spin_unlock of different lock" }, + { "lock_id_mismatch_kptr_innermapval", "bpf_spin_unlock of different lock" }, + { "lock_id_mismatch_global_global", "bpf_spin_unlock of different lock" }, + { "lock_id_mismatch_global_kptr", "bpf_spin_unlock of different lock" }, + { "lock_id_mismatch_global_mapval", "bpf_spin_unlock of different lock" }, + { "lock_id_mismatch_global_innermapval", "bpf_spin_unlock of different lock" }, + { "lock_id_mismatch_mapval_mapval", "bpf_spin_unlock of different lock" }, + { "lock_id_mismatch_mapval_kptr", "bpf_spin_unlock of different lock" }, + { "lock_id_mismatch_mapval_global", "bpf_spin_unlock of different lock" }, + { "lock_id_mismatch_mapval_innermapval", "bpf_spin_unlock of different lock" }, + { "lock_id_mismatch_innermapval_innermapval1", "bpf_spin_unlock of different lock" }, + { "lock_id_mismatch_innermapval_innermapval2", "bpf_spin_unlock of different lock" }, + { "lock_id_mismatch_innermapval_kptr", "bpf_spin_unlock of different lock" }, + { "lock_id_mismatch_innermapval_global", "bpf_spin_unlock of different lock" }, + { "lock_id_mismatch_innermapval_mapval", "bpf_spin_unlock of different lock" }, +}; + +static void test_spin_lock_fail_prog(const char *prog_name, const char *err_msg) +{ + LIBBPF_OPTS(bpf_object_open_opts, opts, .kernel_log_buf = log_buf, + .kernel_log_size = sizeof(log_buf), + .kernel_log_level = 1); + struct test_spin_lock_fail *skel; + struct bpf_program *prog; + int ret; + + skel = test_spin_lock_fail__open_opts(&opts); + if (!ASSERT_OK_PTR(skel, "test_spin_lock_fail__open_opts")) + return; + + prog = bpf_object__find_program_by_name(skel->obj, prog_name); + if (!ASSERT_OK_PTR(prog, "bpf_object__find_program_by_name")) + goto end; + + bpf_program__set_autoload(prog, true); + + ret = test_spin_lock_fail__load(skel); + if (!ASSERT_ERR(ret, "test_spin_lock_fail__load must fail")) + goto end; + + if (!ASSERT_OK_PTR(strstr(log_buf, err_msg), "expected error message")) { + fprintf(stderr, "Expected: %s\n", err_msg); + fprintf(stderr, "Verifier: %s\n", log_buf); + } + +end: + test_spin_lock_fail__destroy(skel); +} static void *spin_lock_thread(void *arg) { @@ -19,7 +92,7 @@ static void *spin_lock_thread(void *arg) pthread_exit(arg); } -void test_spinlock(void) +void test_spin_lock_success(void) { struct test_spin_lock *skel; pthread_t thread_id[4]; @@ -47,3 +120,17 @@ void test_spinlock(void) end: test_spin_lock__destroy(skel); } + +void test_spin_lock(void) +{ + int i; + + test_spin_lock_success(); + + for (i = 0; i < ARRAY_SIZE(spin_lock_fail_tests); i++) { + if (!test__start_subtest(spin_lock_fail_tests[i].prog_name)) + continue; + test_spin_lock_fail_prog(spin_lock_fail_tests[i].prog_name, + spin_lock_fail_tests[i].err_msg); + } +} diff --git a/tools/testing/selftests/bpf/progs/test_spin_lock_fail.c b/tools/testing/selftests/bpf/progs/test_spin_lock_fail.c new file mode 100644 index 000000000000..86cd183ef6dc --- /dev/null +++ b/tools/testing/selftests/bpf/progs/test_spin_lock_fail.c @@ -0,0 +1,204 @@ +// SPDX-License-Identifier: GPL-2.0 +#include +#include +#include +#include "bpf_experimental.h" + +struct foo { + struct bpf_spin_lock lock; + int data; +}; + +struct array_map { + __uint(type, BPF_MAP_TYPE_ARRAY); + __type(key, int); + __type(value, struct foo); + __uint(max_entries, 1); +} array_map SEC(".maps"); + +struct { + __uint(type, BPF_MAP_TYPE_ARRAY_OF_MAPS); + __uint(max_entries, 1); + __type(key, int); + __type(value, int); + __array(values, struct array_map); +} map_of_maps SEC(".maps") = { + .values = { + [0] = &array_map, + }, +}; + +SEC(".data.A") struct bpf_spin_lock lockA; +SEC(".data.B") struct bpf_spin_lock lockB; + +SEC("?tc") +int lock_id_kptr_preserve(void *ctx) +{ + struct foo *f; + + f = bpf_obj_new(typeof(*f)); + if (!f) + return 0; + bpf_this_cpu_ptr(f); + return 0; +} + +SEC("?tc") +int lock_id_global_zero(void *ctx) +{ + bpf_this_cpu_ptr(&lockA); + return 0; +} + +SEC("?tc") +int lock_id_mapval_preserve(void *ctx) +{ + struct foo *f; + int key = 0; + + f = bpf_map_lookup_elem(&array_map, &key); + if (!f) + return 0; + bpf_this_cpu_ptr(f); + return 0; +} + +SEC("?tc") +int lock_id_innermapval_preserve(void *ctx) +{ + struct foo *f; + int key = 0; + void *map; + + map = bpf_map_lookup_elem(&map_of_maps, &key); + if (!map) + return 0; + f = bpf_map_lookup_elem(map, &key); + if (!f) + return 0; + bpf_this_cpu_ptr(f); + return 0; +} + +#define CHECK(test, A, B) \ + SEC("?tc") \ + int lock_id_mismatch_##test(void *ctx) \ + { \ + struct foo *f1, *f2, *v, *iv; \ + int key = 0; \ + void *map; \ + \ + map = bpf_map_lookup_elem(&map_of_maps, &key); \ + if (!map) \ + return 0; \ + iv = bpf_map_lookup_elem(map, &key); \ + if (!iv) \ + return 0; \ + v = bpf_map_lookup_elem(&array_map, &key); \ + if (!v) \ + return 0; \ + f1 = bpf_obj_new(typeof(*f1)); \ + if (!f1) \ + return 0; \ + f2 = bpf_obj_new(typeof(*f2)); \ + if (!f2) { \ + bpf_obj_drop(f1); \ + return 0; \ + } \ + bpf_spin_lock(A); \ + bpf_spin_unlock(B); \ + return 0; \ + } + +CHECK(kptr_kptr, &f1->lock, &f2->lock); +CHECK(kptr_global, &f1->lock, &lockA); +CHECK(kptr_mapval, &f1->lock, &v->lock); +CHECK(kptr_innermapval, &f1->lock, &iv->lock); + +CHECK(global_global, &lockA, &lockB); +CHECK(global_kptr, &lockA, &f1->lock); +CHECK(global_mapval, &lockA, &v->lock); +CHECK(global_innermapval, &lockA, &iv->lock); + +SEC("?tc") +int lock_id_mismatch_mapval_mapval(void *ctx) +{ + struct foo *f1, *f2; + int key = 0; + + f1 = bpf_map_lookup_elem(&array_map, &key); + if (!f1) + return 0; + f2 = bpf_map_lookup_elem(&array_map, &key); + if (!f2) + return 0; + + bpf_spin_lock(&f1->lock); + f1->data = 42; + bpf_spin_unlock(&f2->lock); + + return 0; +} + +CHECK(mapval_kptr, &v->lock, &f1->lock); +CHECK(mapval_global, &v->lock, &lockB); +CHECK(mapval_innermapval, &v->lock, &iv->lock); + +SEC("?tc") +int lock_id_mismatch_innermapval_innermapval1(void *ctx) +{ + struct foo *f1, *f2; + int key = 0; + void *map; + + map = bpf_map_lookup_elem(&map_of_maps, &key); + if (!map) + return 0; + f1 = bpf_map_lookup_elem(map, &key); + if (!f1) + return 0; + f2 = bpf_map_lookup_elem(map, &key); + if (!f2) + return 0; + + bpf_spin_lock(&f1->lock); + f1->data = 42; + bpf_spin_unlock(&f2->lock); + + return 0; +} + +SEC("?tc") +int lock_id_mismatch_innermapval_innermapval2(void *ctx) +{ + struct foo *f1, *f2; + int key = 0; + void *map; + + map = bpf_map_lookup_elem(&map_of_maps, &key); + if (!map) + return 0; + f1 = bpf_map_lookup_elem(map, &key); + if (!f1) + return 0; + map = bpf_map_lookup_elem(&map_of_maps, &key); + if (!map) + return 0; + f2 = bpf_map_lookup_elem(map, &key); + if (!f2) + return 0; + + bpf_spin_lock(&f1->lock); + f1->data = 42; + bpf_spin_unlock(&f2->lock); + + return 0; +} + +CHECK(innermapval_kptr, &iv->lock, &f1->lock); +CHECK(innermapval_global, &iv->lock, &lockA); +CHECK(innermapval_mapval, &iv->lock, &v->lock); + +#undef CHECK + +char _license[] SEC("license") = "GPL"; From 300f19dcdb99b708353d9e46fd660a4765ab277d Mon Sep 17 00:00:00 2001 From: Kumar Kartikeya Dwivedi Date: Fri, 18 Nov 2022 07:26:12 +0530 Subject: [PATCH 1022/2002] selftests/bpf: Add BPF linked list API tests Include various tests covering the success and failure cases. Also, run the success cases at runtime to verify correctness of linked list manipulation routines, in addition to ensuring successful verification. Signed-off-by: Kumar Kartikeya Dwivedi Link: https://lore.kernel.org/r/20221118015614.2013203-23-memxor@gmail.com Signed-off-by: Alexei Starovoitov --- tools/testing/selftests/bpf/DENYLIST.aarch64 | 1 + tools/testing/selftests/bpf/DENYLIST.s390x | 1 + .../selftests/bpf/prog_tests/linked_list.c | 255 ++++++++ .../testing/selftests/bpf/progs/linked_list.c | 370 +++++++++++ .../testing/selftests/bpf/progs/linked_list.h | 56 ++ .../selftests/bpf/progs/linked_list_fail.c | 581 ++++++++++++++++++ 6 files changed, 1264 insertions(+) create mode 100644 tools/testing/selftests/bpf/prog_tests/linked_list.c create mode 100644 tools/testing/selftests/bpf/progs/linked_list.c create mode 100644 tools/testing/selftests/bpf/progs/linked_list.h create mode 100644 tools/testing/selftests/bpf/progs/linked_list_fail.c diff --git a/tools/testing/selftests/bpf/DENYLIST.aarch64 b/tools/testing/selftests/bpf/DENYLIST.aarch64 index 09416d5d2e33..affc5aebbf0f 100644 --- a/tools/testing/selftests/bpf/DENYLIST.aarch64 +++ b/tools/testing/selftests/bpf/DENYLIST.aarch64 @@ -38,6 +38,7 @@ kprobe_multi_test/skel_api # kprobe_multi__attach unexpect ksyms_module/libbpf # 'bpf_testmod_ksym_percpu': not found in kernel BTF ksyms_module/lskel # test_ksyms_module_lskel__open_and_load unexpected error: -2 libbpf_get_fd_by_id_opts # test_libbpf_get_fd_by_id_opts__attach unexpected error: -524 (errno 524) +linked_list lookup_key # test_lookup_key__attach unexpected error: -524 (errno 524) lru_bug # lru_bug__attach unexpected error: -524 (errno 524) modify_return # modify_return__attach failed unexpected error: -524 (errno 524) diff --git a/tools/testing/selftests/bpf/DENYLIST.s390x b/tools/testing/selftests/bpf/DENYLIST.s390x index be4e3d47ea3e..072243af93b0 100644 --- a/tools/testing/selftests/bpf/DENYLIST.s390x +++ b/tools/testing/selftests/bpf/DENYLIST.s390x @@ -33,6 +33,7 @@ ksyms_module # test_ksyms_module__open_and_load unex ksyms_module_libbpf # JIT does not support calling kernel function (kfunc) ksyms_module_lskel # test_ksyms_module_lskel__open_and_load unexpected error: -9 (?) libbpf_get_fd_by_id_opts # failed to attach: ERROR: strerror_r(-524)=22 (trampoline) +linked_list # JIT does not support calling kernel function (kfunc) lookup_key # JIT does not support calling kernel function (kfunc) lru_bug # prog 'printk': failed to auto-attach: -524 map_kptr # failed to open_and_load program: -524 (trampoline) diff --git a/tools/testing/selftests/bpf/prog_tests/linked_list.c b/tools/testing/selftests/bpf/prog_tests/linked_list.c new file mode 100644 index 000000000000..41e588807321 --- /dev/null +++ b/tools/testing/selftests/bpf/prog_tests/linked_list.c @@ -0,0 +1,255 @@ +// SPDX-License-Identifier: GPL-2.0 +#include +#include + +#include "linked_list.skel.h" +#include "linked_list_fail.skel.h" + +static char log_buf[1024 * 1024]; + +static struct { + const char *prog_name; + const char *err_msg; +} linked_list_fail_tests[] = { +#define TEST(test, off) \ + { #test "_missing_lock_push_front", \ + "bpf_spin_lock at off=" #off " must be held for bpf_list_head" }, \ + { #test "_missing_lock_push_back", \ + "bpf_spin_lock at off=" #off " must be held for bpf_list_head" }, \ + { #test "_missing_lock_pop_front", \ + "bpf_spin_lock at off=" #off " must be held for bpf_list_head" }, \ + { #test "_missing_lock_pop_back", \ + "bpf_spin_lock at off=" #off " must be held for bpf_list_head" }, + TEST(kptr, 32) + TEST(global, 16) + TEST(map, 0) + TEST(inner_map, 0) +#undef TEST +#define TEST(test, op) \ + { #test "_kptr_incorrect_lock_" #op, \ + "held lock and object are not in the same allocation\n" \ + "bpf_spin_lock at off=32 must be held for bpf_list_head" }, \ + { #test "_global_incorrect_lock_" #op, \ + "held lock and object are not in the same allocation\n" \ + "bpf_spin_lock at off=16 must be held for bpf_list_head" }, \ + { #test "_map_incorrect_lock_" #op, \ + "held lock and object are not in the same allocation\n" \ + "bpf_spin_lock at off=0 must be held for bpf_list_head" }, \ + { #test "_inner_map_incorrect_lock_" #op, \ + "held lock and object are not in the same allocation\n" \ + "bpf_spin_lock at off=0 must be held for bpf_list_head" }, + TEST(kptr, push_front) + TEST(kptr, push_back) + TEST(kptr, pop_front) + TEST(kptr, pop_back) + TEST(global, push_front) + TEST(global, push_back) + TEST(global, pop_front) + TEST(global, pop_back) + TEST(map, push_front) + TEST(map, push_back) + TEST(map, pop_front) + TEST(map, pop_back) + TEST(inner_map, push_front) + TEST(inner_map, push_back) + TEST(inner_map, pop_front) + TEST(inner_map, pop_back) +#undef TEST + { "map_compat_kprobe", "tracing progs cannot use bpf_list_head yet" }, + { "map_compat_kretprobe", "tracing progs cannot use bpf_list_head yet" }, + { "map_compat_tp", "tracing progs cannot use bpf_list_head yet" }, + { "map_compat_perf", "tracing progs cannot use bpf_list_head yet" }, + { "map_compat_raw_tp", "tracing progs cannot use bpf_list_head yet" }, + { "map_compat_raw_tp_w", "tracing progs cannot use bpf_list_head yet" }, + { "obj_type_id_oor", "local type ID argument must be in range [0, U32_MAX]" }, + { "obj_new_no_composite", "bpf_obj_new type ID argument must be of a struct" }, + { "obj_new_no_struct", "bpf_obj_new type ID argument must be of a struct" }, + { "obj_drop_non_zero_off", "R1 must have zero offset when passed to release func" }, + { "new_null_ret", "R0 invalid mem access 'ptr_or_null_'" }, + { "obj_new_acq", "Unreleased reference id=" }, + { "use_after_drop", "invalid mem access 'scalar'" }, + { "ptr_walk_scalar", "type=scalar expected=percpu_ptr_" }, + { "direct_read_lock", "direct access to bpf_spin_lock is disallowed" }, + { "direct_write_lock", "direct access to bpf_spin_lock is disallowed" }, + { "direct_read_head", "direct access to bpf_list_head is disallowed" }, + { "direct_write_head", "direct access to bpf_list_head is disallowed" }, + { "direct_read_node", "direct access to bpf_list_node is disallowed" }, + { "direct_write_node", "direct access to bpf_list_node is disallowed" }, + { "write_after_push_front", "only read is supported" }, + { "write_after_push_back", "only read is supported" }, + { "use_after_unlock_push_front", "invalid mem access 'scalar'" }, + { "use_after_unlock_push_back", "invalid mem access 'scalar'" }, + { "double_push_front", "arg#1 expected pointer to allocated object" }, + { "double_push_back", "arg#1 expected pointer to allocated object" }, + { "no_node_value_type", "bpf_list_node not found at offset=0" }, + { "incorrect_value_type", + "operation on bpf_list_head expects arg#1 bpf_list_node at offset=0 in struct foo, " + "but arg is at offset=0 in struct bar" }, + { "incorrect_node_var_off", "variable ptr_ access var_off=(0x0; 0xffffffff) disallowed" }, + { "incorrect_node_off1", "bpf_list_node not found at offset=1" }, + { "incorrect_node_off2", "arg#1 offset=40, but expected bpf_list_node at offset=0 in struct foo" }, + { "no_head_type", "bpf_list_head not found at offset=0" }, + { "incorrect_head_var_off1", "R1 doesn't have constant offset" }, + { "incorrect_head_var_off2", "variable ptr_ access var_off=(0x0; 0xffffffff) disallowed" }, + { "incorrect_head_off1", "bpf_list_head not found at offset=17" }, + { "incorrect_head_off2", "bpf_list_head not found at offset=1" }, + { "pop_front_off", + "15: (bf) r1 = r6 ; R1_w=ptr_or_null_foo(id=4,ref_obj_id=4,off=40,imm=0) " + "R6_w=ptr_or_null_foo(id=4,ref_obj_id=4,off=40,imm=0) refs=2,4\n" + "16: (85) call bpf_this_cpu_ptr#154\nR1 type=ptr_or_null_ expected=percpu_ptr_" }, + { "pop_back_off", + "15: (bf) r1 = r6 ; R1_w=ptr_or_null_foo(id=4,ref_obj_id=4,off=40,imm=0) " + "R6_w=ptr_or_null_foo(id=4,ref_obj_id=4,off=40,imm=0) refs=2,4\n" + "16: (85) call bpf_this_cpu_ptr#154\nR1 type=ptr_or_null_ expected=percpu_ptr_" }, +}; + +static void test_linked_list_fail_prog(const char *prog_name, const char *err_msg) +{ + LIBBPF_OPTS(bpf_object_open_opts, opts, .kernel_log_buf = log_buf, + .kernel_log_size = sizeof(log_buf), + .kernel_log_level = 1); + struct linked_list_fail *skel; + struct bpf_program *prog; + int ret; + + skel = linked_list_fail__open_opts(&opts); + if (!ASSERT_OK_PTR(skel, "linked_list_fail__open_opts")) + return; + + prog = bpf_object__find_program_by_name(skel->obj, prog_name); + if (!ASSERT_OK_PTR(prog, "bpf_object__find_program_by_name")) + goto end; + + bpf_program__set_autoload(prog, true); + + ret = linked_list_fail__load(skel); + if (!ASSERT_ERR(ret, "linked_list_fail__load must fail")) + goto end; + + if (!ASSERT_OK_PTR(strstr(log_buf, err_msg), "expected error message")) { + fprintf(stderr, "Expected: %s\n", err_msg); + fprintf(stderr, "Verifier: %s\n", log_buf); + } + +end: + linked_list_fail__destroy(skel); +} + +static void clear_fields(struct bpf_map *map) +{ + char buf[24]; + int key = 0; + + memset(buf, 0xff, sizeof(buf)); + ASSERT_OK(bpf_map__update_elem(map, &key, sizeof(key), buf, sizeof(buf), 0), "check_and_free_fields"); +} + +enum { + TEST_ALL, + PUSH_POP, + PUSH_POP_MULT, + LIST_IN_LIST, +}; + +static void test_linked_list_success(int mode, bool leave_in_map) +{ + LIBBPF_OPTS(bpf_test_run_opts, opts, + .data_in = &pkt_v4, + .data_size_in = sizeof(pkt_v4), + .repeat = 1, + ); + struct linked_list *skel; + int ret; + + skel = linked_list__open_and_load(); + if (!ASSERT_OK_PTR(skel, "linked_list__open_and_load")) + return; + + if (mode == LIST_IN_LIST) + goto lil; + if (mode == PUSH_POP_MULT) + goto ppm; + + ret = bpf_prog_test_run_opts(bpf_program__fd(skel->progs.map_list_push_pop), &opts); + ASSERT_OK(ret, "map_list_push_pop"); + ASSERT_OK(opts.retval, "map_list_push_pop retval"); + if (!leave_in_map) + clear_fields(skel->maps.array_map); + + ret = bpf_prog_test_run_opts(bpf_program__fd(skel->progs.inner_map_list_push_pop), &opts); + ASSERT_OK(ret, "inner_map_list_push_pop"); + ASSERT_OK(opts.retval, "inner_map_list_push_pop retval"); + if (!leave_in_map) + clear_fields(skel->maps.inner_map); + + ret = bpf_prog_test_run_opts(bpf_program__fd(skel->progs.global_list_push_pop), &opts); + ASSERT_OK(ret, "global_list_push_pop"); + ASSERT_OK(opts.retval, "global_list_push_pop retval"); + if (!leave_in_map) + clear_fields(skel->maps.data_A); + + if (mode == PUSH_POP) + goto end; + +ppm: + ret = bpf_prog_test_run_opts(bpf_program__fd(skel->progs.map_list_push_pop_multiple), &opts); + ASSERT_OK(ret, "map_list_push_pop_multiple"); + ASSERT_OK(opts.retval, "map_list_push_pop_multiple retval"); + if (!leave_in_map) + clear_fields(skel->maps.array_map); + + ret = bpf_prog_test_run_opts(bpf_program__fd(skel->progs.inner_map_list_push_pop_multiple), &opts); + ASSERT_OK(ret, "inner_map_list_push_pop_multiple"); + ASSERT_OK(opts.retval, "inner_map_list_push_pop_multiple retval"); + if (!leave_in_map) + clear_fields(skel->maps.inner_map); + + ret = bpf_prog_test_run_opts(bpf_program__fd(skel->progs.global_list_push_pop_multiple), &opts); + ASSERT_OK(ret, "global_list_push_pop_multiple"); + ASSERT_OK(opts.retval, "global_list_push_pop_multiple retval"); + if (!leave_in_map) + clear_fields(skel->maps.data_A); + + if (mode == PUSH_POP_MULT) + goto end; + +lil: + ret = bpf_prog_test_run_opts(bpf_program__fd(skel->progs.map_list_in_list), &opts); + ASSERT_OK(ret, "map_list_in_list"); + ASSERT_OK(opts.retval, "map_list_in_list retval"); + if (!leave_in_map) + clear_fields(skel->maps.array_map); + + ret = bpf_prog_test_run_opts(bpf_program__fd(skel->progs.inner_map_list_in_list), &opts); + ASSERT_OK(ret, "inner_map_list_in_list"); + ASSERT_OK(opts.retval, "inner_map_list_in_list retval"); + if (!leave_in_map) + clear_fields(skel->maps.inner_map); + + ret = bpf_prog_test_run_opts(bpf_program__fd(skel->progs.global_list_in_list), &opts); + ASSERT_OK(ret, "global_list_in_list"); + ASSERT_OK(opts.retval, "global_list_in_list retval"); + if (!leave_in_map) + clear_fields(skel->maps.data_A); +end: + linked_list__destroy(skel); +} + +void test_linked_list(void) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(linked_list_fail_tests); i++) { + if (!test__start_subtest(linked_list_fail_tests[i].prog_name)) + continue; + test_linked_list_fail_prog(linked_list_fail_tests[i].prog_name, + linked_list_fail_tests[i].err_msg); + } + test_linked_list_success(PUSH_POP, false); + test_linked_list_success(PUSH_POP, true); + test_linked_list_success(PUSH_POP_MULT, false); + test_linked_list_success(PUSH_POP_MULT, true); + test_linked_list_success(LIST_IN_LIST, false); + test_linked_list_success(LIST_IN_LIST, true); + test_linked_list_success(TEST_ALL, false); +} diff --git a/tools/testing/selftests/bpf/progs/linked_list.c b/tools/testing/selftests/bpf/progs/linked_list.c new file mode 100644 index 000000000000..2c7b615c6d41 --- /dev/null +++ b/tools/testing/selftests/bpf/progs/linked_list.c @@ -0,0 +1,370 @@ +// SPDX-License-Identifier: GPL-2.0 +#include +#include +#include +#include +#include "bpf_experimental.h" + +#ifndef ARRAY_SIZE +#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) +#endif + +#include "linked_list.h" + +static __always_inline +int list_push_pop(struct bpf_spin_lock *lock, struct bpf_list_head *head, bool leave_in_map) +{ + struct bpf_list_node *n; + struct foo *f; + + f = bpf_obj_new(typeof(*f)); + if (!f) + return 2; + + bpf_spin_lock(lock); + n = bpf_list_pop_front(head); + bpf_spin_unlock(lock); + if (n) { + bpf_obj_drop(container_of(n, struct foo, node)); + bpf_obj_drop(f); + return 3; + } + + bpf_spin_lock(lock); + n = bpf_list_pop_back(head); + bpf_spin_unlock(lock); + if (n) { + bpf_obj_drop(container_of(n, struct foo, node)); + bpf_obj_drop(f); + return 4; + } + + + bpf_spin_lock(lock); + f->data = 42; + bpf_list_push_front(head, &f->node); + bpf_spin_unlock(lock); + if (leave_in_map) + return 0; + bpf_spin_lock(lock); + n = bpf_list_pop_back(head); + bpf_spin_unlock(lock); + if (!n) + return 5; + f = container_of(n, struct foo, node); + if (f->data != 42) { + bpf_obj_drop(f); + return 6; + } + + bpf_spin_lock(lock); + f->data = 13; + bpf_list_push_front(head, &f->node); + bpf_spin_unlock(lock); + bpf_spin_lock(lock); + n = bpf_list_pop_front(head); + bpf_spin_unlock(lock); + if (!n) + return 7; + f = container_of(n, struct foo, node); + if (f->data != 13) { + bpf_obj_drop(f); + return 8; + } + bpf_obj_drop(f); + + bpf_spin_lock(lock); + n = bpf_list_pop_front(head); + bpf_spin_unlock(lock); + if (n) { + bpf_obj_drop(container_of(n, struct foo, node)); + return 9; + } + + bpf_spin_lock(lock); + n = bpf_list_pop_back(head); + bpf_spin_unlock(lock); + if (n) { + bpf_obj_drop(container_of(n, struct foo, node)); + return 10; + } + return 0; +} + + +static __always_inline +int list_push_pop_multiple(struct bpf_spin_lock *lock, struct bpf_list_head *head, bool leave_in_map) +{ + struct bpf_list_node *n; + struct foo *f[8], *pf; + int i; + + for (i = 0; i < ARRAY_SIZE(f); i++) { + f[i] = bpf_obj_new(typeof(**f)); + if (!f[i]) + return 2; + f[i]->data = i; + bpf_spin_lock(lock); + bpf_list_push_front(head, &f[i]->node); + bpf_spin_unlock(lock); + } + + for (i = 0; i < ARRAY_SIZE(f); i++) { + bpf_spin_lock(lock); + n = bpf_list_pop_front(head); + bpf_spin_unlock(lock); + if (!n) + return 3; + pf = container_of(n, struct foo, node); + if (pf->data != (ARRAY_SIZE(f) - i - 1)) { + bpf_obj_drop(pf); + return 4; + } + bpf_spin_lock(lock); + bpf_list_push_back(head, &pf->node); + bpf_spin_unlock(lock); + } + + if (leave_in_map) + return 0; + + for (i = 0; i < ARRAY_SIZE(f); i++) { + bpf_spin_lock(lock); + n = bpf_list_pop_back(head); + bpf_spin_unlock(lock); + if (!n) + return 5; + pf = container_of(n, struct foo, node); + if (pf->data != i) { + bpf_obj_drop(pf); + return 6; + } + bpf_obj_drop(pf); + } + bpf_spin_lock(lock); + n = bpf_list_pop_back(head); + bpf_spin_unlock(lock); + if (n) { + bpf_obj_drop(container_of(n, struct foo, node)); + return 7; + } + + bpf_spin_lock(lock); + n = bpf_list_pop_front(head); + bpf_spin_unlock(lock); + if (n) { + bpf_obj_drop(container_of(n, struct foo, node)); + return 8; + } + return 0; +} + +static __always_inline +int list_in_list(struct bpf_spin_lock *lock, struct bpf_list_head *head, bool leave_in_map) +{ + struct bpf_list_node *n; + struct bar *ba[8], *b; + struct foo *f; + int i; + + f = bpf_obj_new(typeof(*f)); + if (!f) + return 2; + for (i = 0; i < ARRAY_SIZE(ba); i++) { + b = bpf_obj_new(typeof(*b)); + if (!b) { + bpf_obj_drop(f); + return 3; + } + b->data = i; + bpf_spin_lock(&f->lock); + bpf_list_push_back(&f->head, &b->node); + bpf_spin_unlock(&f->lock); + } + + bpf_spin_lock(lock); + f->data = 42; + bpf_list_push_front(head, &f->node); + bpf_spin_unlock(lock); + + if (leave_in_map) + return 0; + + bpf_spin_lock(lock); + n = bpf_list_pop_front(head); + bpf_spin_unlock(lock); + if (!n) + return 4; + f = container_of(n, struct foo, node); + if (f->data != 42) { + bpf_obj_drop(f); + return 5; + } + + for (i = 0; i < ARRAY_SIZE(ba); i++) { + bpf_spin_lock(&f->lock); + n = bpf_list_pop_front(&f->head); + bpf_spin_unlock(&f->lock); + if (!n) { + bpf_obj_drop(f); + return 6; + } + b = container_of(n, struct bar, node); + if (b->data != i) { + bpf_obj_drop(f); + bpf_obj_drop(b); + return 7; + } + bpf_obj_drop(b); + } + bpf_spin_lock(&f->lock); + n = bpf_list_pop_front(&f->head); + bpf_spin_unlock(&f->lock); + if (n) { + bpf_obj_drop(f); + bpf_obj_drop(container_of(n, struct bar, node)); + return 8; + } + bpf_obj_drop(f); + return 0; +} + +static __always_inline +int test_list_push_pop(struct bpf_spin_lock *lock, struct bpf_list_head *head) +{ + int ret; + + ret = list_push_pop(lock, head, false); + if (ret) + return ret; + return list_push_pop(lock, head, true); +} + +static __always_inline +int test_list_push_pop_multiple(struct bpf_spin_lock *lock, struct bpf_list_head *head) +{ + int ret; + + ret = list_push_pop_multiple(lock ,head, false); + if (ret) + return ret; + return list_push_pop_multiple(lock, head, true); +} + +static __always_inline +int test_list_in_list(struct bpf_spin_lock *lock, struct bpf_list_head *head) +{ + int ret; + + ret = list_in_list(lock, head, false); + if (ret) + return ret; + return list_in_list(lock, head, true); +} + +SEC("tc") +int map_list_push_pop(void *ctx) +{ + struct map_value *v; + + v = bpf_map_lookup_elem(&array_map, &(int){0}); + if (!v) + return 1; + return test_list_push_pop(&v->lock, &v->head); +} + +SEC("tc") +int inner_map_list_push_pop(void *ctx) +{ + struct map_value *v; + void *map; + + map = bpf_map_lookup_elem(&map_of_maps, &(int){0}); + if (!map) + return 1; + v = bpf_map_lookup_elem(map, &(int){0}); + if (!v) + return 1; + return test_list_push_pop(&v->lock, &v->head); +} + +SEC("tc") +int global_list_push_pop(void *ctx) +{ + return test_list_push_pop(&glock, &ghead); +} + +SEC("tc") +int map_list_push_pop_multiple(void *ctx) +{ + struct map_value *v; + int ret; + + v = bpf_map_lookup_elem(&array_map, &(int){0}); + if (!v) + return 1; + return test_list_push_pop_multiple(&v->lock, &v->head); +} + +SEC("tc") +int inner_map_list_push_pop_multiple(void *ctx) +{ + struct map_value *v; + void *map; + int ret; + + map = bpf_map_lookup_elem(&map_of_maps, &(int){0}); + if (!map) + return 1; + v = bpf_map_lookup_elem(map, &(int){0}); + if (!v) + return 1; + return test_list_push_pop_multiple(&v->lock, &v->head); +} + +SEC("tc") +int global_list_push_pop_multiple(void *ctx) +{ + int ret; + + ret = list_push_pop_multiple(&glock, &ghead, false); + if (ret) + return ret; + return list_push_pop_multiple(&glock, &ghead, true); +} + +SEC("tc") +int map_list_in_list(void *ctx) +{ + struct map_value *v; + int ret; + + v = bpf_map_lookup_elem(&array_map, &(int){0}); + if (!v) + return 1; + return test_list_in_list(&v->lock, &v->head); +} + +SEC("tc") +int inner_map_list_in_list(void *ctx) +{ + struct map_value *v; + void *map; + int ret; + + map = bpf_map_lookup_elem(&map_of_maps, &(int){0}); + if (!map) + return 1; + v = bpf_map_lookup_elem(map, &(int){0}); + if (!v) + return 1; + return test_list_in_list(&v->lock, &v->head); +} + +SEC("tc") +int global_list_in_list(void *ctx) +{ + return test_list_in_list(&glock, &ghead); +} + +char _license[] SEC("license") = "GPL"; diff --git a/tools/testing/selftests/bpf/progs/linked_list.h b/tools/testing/selftests/bpf/progs/linked_list.h new file mode 100644 index 000000000000..8db80ed64db1 --- /dev/null +++ b/tools/testing/selftests/bpf/progs/linked_list.h @@ -0,0 +1,56 @@ +// SPDX-License-Identifier: GPL-2.0 +#ifndef LINKED_LIST_H +#define LINKED_LIST_H + +#include +#include +#include "bpf_experimental.h" + +struct bar { + struct bpf_list_node node; + int data; +}; + +struct foo { + struct bpf_list_node node; + struct bpf_list_head head __contains(bar, node); + struct bpf_spin_lock lock; + int data; + struct bpf_list_node node2; +}; + +struct map_value { + struct bpf_spin_lock lock; + int data; + struct bpf_list_head head __contains(foo, node); +}; + +struct array_map { + __uint(type, BPF_MAP_TYPE_ARRAY); + __type(key, int); + __type(value, struct map_value); + __uint(max_entries, 1); +}; + +struct array_map array_map SEC(".maps"); +struct array_map inner_map SEC(".maps"); + +struct { + __uint(type, BPF_MAP_TYPE_ARRAY_OF_MAPS); + __uint(max_entries, 1); + __type(key, int); + __type(value, int); + __array(values, struct array_map); +} map_of_maps SEC(".maps") = { + .values = { + [0] = &inner_map, + }, +}; + +#define private(name) SEC(".data." #name) __hidden __attribute__((aligned(8))) + +private(A) struct bpf_spin_lock glock; +private(A) struct bpf_list_head ghead __contains(foo, node); +private(B) struct bpf_spin_lock glock2; + +#endif diff --git a/tools/testing/selftests/bpf/progs/linked_list_fail.c b/tools/testing/selftests/bpf/progs/linked_list_fail.c new file mode 100644 index 000000000000..1d9017240e19 --- /dev/null +++ b/tools/testing/selftests/bpf/progs/linked_list_fail.c @@ -0,0 +1,581 @@ +// SPDX-License-Identifier: GPL-2.0 +#include +#include +#include +#include +#include "bpf_experimental.h" + +#include "linked_list.h" + +#define INIT \ + struct map_value *v, *v2, *iv, *iv2; \ + struct foo *f, *f1, *f2; \ + struct bar *b; \ + void *map; \ + \ + map = bpf_map_lookup_elem(&map_of_maps, &(int){ 0 }); \ + if (!map) \ + return 0; \ + v = bpf_map_lookup_elem(&array_map, &(int){ 0 }); \ + if (!v) \ + return 0; \ + v2 = bpf_map_lookup_elem(&array_map, &(int){ 0 }); \ + if (!v2) \ + return 0; \ + iv = bpf_map_lookup_elem(map, &(int){ 0 }); \ + if (!iv) \ + return 0; \ + iv2 = bpf_map_lookup_elem(map, &(int){ 0 }); \ + if (!iv2) \ + return 0; \ + f = bpf_obj_new(typeof(*f)); \ + if (!f) \ + return 0; \ + f1 = f; \ + f2 = bpf_obj_new(typeof(*f2)); \ + if (!f2) { \ + bpf_obj_drop(f1); \ + return 0; \ + } \ + b = bpf_obj_new(typeof(*b)); \ + if (!b) { \ + bpf_obj_drop(f2); \ + bpf_obj_drop(f1); \ + return 0; \ + } + +#define CHECK(test, op, hexpr) \ + SEC("?tc") \ + int test##_missing_lock_##op(void *ctx) \ + { \ + INIT; \ + void (*p)(void *) = (void *)&bpf_list_##op; \ + p(hexpr); \ + return 0; \ + } + +CHECK(kptr, push_front, &f->head); +CHECK(kptr, push_back, &f->head); +CHECK(kptr, pop_front, &f->head); +CHECK(kptr, pop_back, &f->head); + +CHECK(global, push_front, &ghead); +CHECK(global, push_back, &ghead); +CHECK(global, pop_front, &ghead); +CHECK(global, pop_back, &ghead); + +CHECK(map, push_front, &v->head); +CHECK(map, push_back, &v->head); +CHECK(map, pop_front, &v->head); +CHECK(map, pop_back, &v->head); + +CHECK(inner_map, push_front, &iv->head); +CHECK(inner_map, push_back, &iv->head); +CHECK(inner_map, pop_front, &iv->head); +CHECK(inner_map, pop_back, &iv->head); + +#undef CHECK + +#define CHECK(test, op, lexpr, hexpr) \ + SEC("?tc") \ + int test##_incorrect_lock_##op(void *ctx) \ + { \ + INIT; \ + void (*p)(void *) = (void *)&bpf_list_##op; \ + bpf_spin_lock(lexpr); \ + p(hexpr); \ + return 0; \ + } + +#define CHECK_OP(op) \ + CHECK(kptr_kptr, op, &f1->lock, &f2->head); \ + CHECK(kptr_global, op, &f1->lock, &ghead); \ + CHECK(kptr_map, op, &f1->lock, &v->head); \ + CHECK(kptr_inner_map, op, &f1->lock, &iv->head); \ + \ + CHECK(global_global, op, &glock2, &ghead); \ + CHECK(global_kptr, op, &glock, &f1->head); \ + CHECK(global_map, op, &glock, &v->head); \ + CHECK(global_inner_map, op, &glock, &iv->head); \ + \ + CHECK(map_map, op, &v->lock, &v2->head); \ + CHECK(map_kptr, op, &v->lock, &f2->head); \ + CHECK(map_global, op, &v->lock, &ghead); \ + CHECK(map_inner_map, op, &v->lock, &iv->head); \ + \ + CHECK(inner_map_inner_map, op, &iv->lock, &iv2->head); \ + CHECK(inner_map_kptr, op, &iv->lock, &f2->head); \ + CHECK(inner_map_global, op, &iv->lock, &ghead); \ + CHECK(inner_map_map, op, &iv->lock, &v->head); + +CHECK_OP(push_front); +CHECK_OP(push_back); +CHECK_OP(pop_front); +CHECK_OP(pop_back); + +#undef CHECK +#undef CHECK_OP +#undef INIT + +SEC("?kprobe/xyz") +int map_compat_kprobe(void *ctx) +{ + bpf_list_push_front(&ghead, NULL); + return 0; +} + +SEC("?kretprobe/xyz") +int map_compat_kretprobe(void *ctx) +{ + bpf_list_push_front(&ghead, NULL); + return 0; +} + +SEC("?tracepoint/xyz") +int map_compat_tp(void *ctx) +{ + bpf_list_push_front(&ghead, NULL); + return 0; +} + +SEC("?perf_event") +int map_compat_perf(void *ctx) +{ + bpf_list_push_front(&ghead, NULL); + return 0; +} + +SEC("?raw_tp/xyz") +int map_compat_raw_tp(void *ctx) +{ + bpf_list_push_front(&ghead, NULL); + return 0; +} + +SEC("?raw_tp.w/xyz") +int map_compat_raw_tp_w(void *ctx) +{ + bpf_list_push_front(&ghead, NULL); + return 0; +} + +SEC("?tc") +int obj_type_id_oor(void *ctx) +{ + bpf_obj_new_impl(~0UL, NULL); + return 0; +} + +SEC("?tc") +int obj_new_no_composite(void *ctx) +{ + bpf_obj_new_impl(bpf_core_type_id_local(int), (void *)42); + return 0; +} + +SEC("?tc") +int obj_new_no_struct(void *ctx) +{ + + bpf_obj_new(union { int data; unsigned udata; }); + return 0; +} + +SEC("?tc") +int obj_drop_non_zero_off(void *ctx) +{ + void *f; + + f = bpf_obj_new(struct foo); + if (!f) + return 0; + bpf_obj_drop(f+1); + return 0; +} + +SEC("?tc") +int new_null_ret(void *ctx) +{ + return bpf_obj_new(struct foo)->data; +} + +SEC("?tc") +int obj_new_acq(void *ctx) +{ + bpf_obj_new(struct foo); + return 0; +} + +SEC("?tc") +int use_after_drop(void *ctx) +{ + struct foo *f; + + f = bpf_obj_new(typeof(*f)); + if (!f) + return 0; + bpf_obj_drop(f); + return f->data; +} + +SEC("?tc") +int ptr_walk_scalar(void *ctx) +{ + struct test1 { + struct test2 { + struct test2 *next; + } *ptr; + } *p; + + p = bpf_obj_new(typeof(*p)); + if (!p) + return 0; + bpf_this_cpu_ptr(p->ptr); + return 0; +} + +SEC("?tc") +int direct_read_lock(void *ctx) +{ + struct foo *f; + + f = bpf_obj_new(typeof(*f)); + if (!f) + return 0; + return *(int *)&f->lock; +} + +SEC("?tc") +int direct_write_lock(void *ctx) +{ + struct foo *f; + + f = bpf_obj_new(typeof(*f)); + if (!f) + return 0; + *(int *)&f->lock = 0; + return 0; +} + +SEC("?tc") +int direct_read_head(void *ctx) +{ + struct foo *f; + + f = bpf_obj_new(typeof(*f)); + if (!f) + return 0; + return *(int *)&f->head; +} + +SEC("?tc") +int direct_write_head(void *ctx) +{ + struct foo *f; + + f = bpf_obj_new(typeof(*f)); + if (!f) + return 0; + *(int *)&f->head = 0; + return 0; +} + +SEC("?tc") +int direct_read_node(void *ctx) +{ + struct foo *f; + + f = bpf_obj_new(typeof(*f)); + if (!f) + return 0; + return *(int *)&f->node; +} + +SEC("?tc") +int direct_write_node(void *ctx) +{ + struct foo *f; + + f = bpf_obj_new(typeof(*f)); + if (!f) + return 0; + *(int *)&f->node = 0; + return 0; +} + +static __always_inline +int write_after_op(void (*push_op)(void *head, void *node)) +{ + struct foo *f; + + f = bpf_obj_new(typeof(*f)); + if (!f) + return 0; + bpf_spin_lock(&glock); + push_op(&ghead, &f->node); + f->data = 42; + bpf_spin_unlock(&glock); + + return 0; +} + +SEC("?tc") +int write_after_push_front(void *ctx) +{ + return write_after_op((void *)bpf_list_push_front); +} + +SEC("?tc") +int write_after_push_back(void *ctx) +{ + return write_after_op((void *)bpf_list_push_back); +} + +static __always_inline +int use_after_unlock(void (*op)(void *head, void *node)) +{ + struct foo *f; + + f = bpf_obj_new(typeof(*f)); + if (!f) + return 0; + bpf_spin_lock(&glock); + f->data = 42; + op(&ghead, &f->node); + bpf_spin_unlock(&glock); + + return f->data; +} + +SEC("?tc") +int use_after_unlock_push_front(void *ctx) +{ + return use_after_unlock((void *)bpf_list_push_front); +} + +SEC("?tc") +int use_after_unlock_push_back(void *ctx) +{ + return use_after_unlock((void *)bpf_list_push_back); +} + +static __always_inline +int list_double_add(void (*op)(void *head, void *node)) +{ + struct foo *f; + + f = bpf_obj_new(typeof(*f)); + if (!f) + return 0; + bpf_spin_lock(&glock); + op(&ghead, &f->node); + op(&ghead, &f->node); + bpf_spin_unlock(&glock); + + return 0; +} + +SEC("?tc") +int double_push_front(void *ctx) +{ + return list_double_add((void *)bpf_list_push_front); +} + +SEC("?tc") +int double_push_back(void *ctx) +{ + return list_double_add((void *)bpf_list_push_back); +} + +SEC("?tc") +int no_node_value_type(void *ctx) +{ + void *p; + + p = bpf_obj_new(struct { int data; }); + if (!p) + return 0; + bpf_spin_lock(&glock); + bpf_list_push_front(&ghead, p); + bpf_spin_unlock(&glock); + + return 0; +} + +SEC("?tc") +int incorrect_value_type(void *ctx) +{ + struct bar *b; + + b = bpf_obj_new(typeof(*b)); + if (!b) + return 0; + bpf_spin_lock(&glock); + bpf_list_push_front(&ghead, &b->node); + bpf_spin_unlock(&glock); + + return 0; +} + +SEC("?tc") +int incorrect_node_var_off(struct __sk_buff *ctx) +{ + struct foo *f; + + f = bpf_obj_new(typeof(*f)); + if (!f) + return 0; + bpf_spin_lock(&glock); + bpf_list_push_front(&ghead, (void *)&f->node + ctx->protocol); + bpf_spin_unlock(&glock); + + return 0; +} + +SEC("?tc") +int incorrect_node_off1(void *ctx) +{ + struct foo *f; + + f = bpf_obj_new(typeof(*f)); + if (!f) + return 0; + bpf_spin_lock(&glock); + bpf_list_push_front(&ghead, (void *)&f->node + 1); + bpf_spin_unlock(&glock); + + return 0; +} + +SEC("?tc") +int incorrect_node_off2(void *ctx) +{ + struct foo *f; + + f = bpf_obj_new(typeof(*f)); + if (!f) + return 0; + bpf_spin_lock(&glock); + bpf_list_push_front(&ghead, &f->node2); + bpf_spin_unlock(&glock); + + return 0; +} + +SEC("?tc") +int no_head_type(void *ctx) +{ + void *p; + + p = bpf_obj_new(typeof(struct { int data; })); + if (!p) + return 0; + bpf_spin_lock(&glock); + bpf_list_push_front(p, NULL); + bpf_spin_lock(&glock); + + return 0; +} + +SEC("?tc") +int incorrect_head_var_off1(struct __sk_buff *ctx) +{ + struct foo *f; + + f = bpf_obj_new(typeof(*f)); + if (!f) + return 0; + bpf_spin_lock(&glock); + bpf_list_push_front((void *)&ghead + ctx->protocol, &f->node); + bpf_spin_unlock(&glock); + + return 0; +} + +SEC("?tc") +int incorrect_head_var_off2(struct __sk_buff *ctx) +{ + struct foo *f; + + f = bpf_obj_new(typeof(*f)); + if (!f) + return 0; + bpf_spin_lock(&glock); + bpf_list_push_front((void *)&f->head + ctx->protocol, &f->node); + bpf_spin_unlock(&glock); + + return 0; +} + +SEC("?tc") +int incorrect_head_off1(void *ctx) +{ + struct foo *f; + struct bar *b; + + f = bpf_obj_new(typeof(*f)); + if (!f) + return 0; + b = bpf_obj_new(typeof(*b)); + if (!b) { + bpf_obj_drop(f); + return 0; + } + + bpf_spin_lock(&f->lock); + bpf_list_push_front((void *)&f->head + 1, &b->node); + bpf_spin_unlock(&f->lock); + + return 0; +} + +SEC("?tc") +int incorrect_head_off2(void *ctx) +{ + struct foo *f; + struct bar *b; + + f = bpf_obj_new(typeof(*f)); + if (!f) + return 0; + + bpf_spin_lock(&glock); + bpf_list_push_front((void *)&ghead + 1, &f->node); + bpf_spin_unlock(&glock); + + return 0; +} + +static __always_inline +int pop_ptr_off(void *(*op)(void *head)) +{ + struct { + struct bpf_list_head head __contains(foo, node2); + struct bpf_spin_lock lock; + } *p; + struct bpf_list_node *n; + + p = bpf_obj_new(typeof(*p)); + if (!p) + return 0; + bpf_spin_lock(&p->lock); + n = op(&p->head); + bpf_spin_unlock(&p->lock); + + bpf_this_cpu_ptr(n); + return 0; +} + +SEC("?tc") +int pop_front_off(void *ctx) +{ + return pop_ptr_off((void *)bpf_list_pop_front); +} + +SEC("?tc") +int pop_back_off(void *ctx) +{ + return pop_ptr_off((void *)bpf_list_pop_back); +} + +char _license[] SEC("license") = "GPL"; From dc2df7bf4c8a24a55ef02ef45dd3e49abc105f76 Mon Sep 17 00:00:00 2001 From: Kumar Kartikeya Dwivedi Date: Fri, 18 Nov 2022 07:26:13 +0530 Subject: [PATCH 1023/2002] selftests/bpf: Add BTF sanity tests Preparing the metadata for bpf_list_head involves a complicated parsing step and type resolution for the contained value. Ensure that corner cases are tested against and invalid specifications in source are duly rejected. Also include tests for incorrect ownership relationships in the BTF. Signed-off-by: Kumar Kartikeya Dwivedi Link: https://lore.kernel.org/r/20221118015614.2013203-24-memxor@gmail.com Signed-off-by: Alexei Starovoitov --- .../selftests/bpf/prog_tests/linked_list.c | 485 ++++++++++++++++++ 1 file changed, 485 insertions(+) diff --git a/tools/testing/selftests/bpf/prog_tests/linked_list.c b/tools/testing/selftests/bpf/prog_tests/linked_list.c index 41e588807321..dd73d0a62c6e 100644 --- a/tools/testing/selftests/bpf/prog_tests/linked_list.c +++ b/tools/testing/selftests/bpf/prog_tests/linked_list.c @@ -1,4 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 +#include +#include +#include #include #include @@ -235,6 +238,487 @@ end: linked_list__destroy(skel); } +#define SPIN_LOCK 2 +#define LIST_HEAD 3 +#define LIST_NODE 4 + +static struct btf *init_btf(void) +{ + int id, lid, hid, nid; + struct btf *btf; + + btf = btf__new_empty(); + if (!ASSERT_OK_PTR(btf, "btf__new_empty")) + return NULL; + id = btf__add_int(btf, "int", 4, BTF_INT_SIGNED); + if (!ASSERT_EQ(id, 1, "btf__add_int")) + goto end; + lid = btf__add_struct(btf, "bpf_spin_lock", 4); + if (!ASSERT_EQ(lid, SPIN_LOCK, "btf__add_struct bpf_spin_lock")) + goto end; + hid = btf__add_struct(btf, "bpf_list_head", 16); + if (!ASSERT_EQ(hid, LIST_HEAD, "btf__add_struct bpf_list_head")) + goto end; + nid = btf__add_struct(btf, "bpf_list_node", 16); + if (!ASSERT_EQ(nid, LIST_NODE, "btf__add_struct bpf_list_node")) + goto end; + return btf; +end: + btf__free(btf); + return NULL; +} + +static void test_btf(void) +{ + struct btf *btf = NULL; + int id, err; + + while (test__start_subtest("btf: too many locks")) { + btf = init_btf(); + if (!ASSERT_OK_PTR(btf, "init_btf")) + break; + id = btf__add_struct(btf, "foo", 24); + if (!ASSERT_EQ(id, 5, "btf__add_struct foo")) + break; + err = btf__add_field(btf, "a", SPIN_LOCK, 0, 0); + if (!ASSERT_OK(err, "btf__add_struct foo::a")) + break; + err = btf__add_field(btf, "b", SPIN_LOCK, 32, 0); + if (!ASSERT_OK(err, "btf__add_struct foo::a")) + break; + err = btf__add_field(btf, "c", LIST_HEAD, 64, 0); + if (!ASSERT_OK(err, "btf__add_struct foo::a")) + break; + + err = btf__load_into_kernel(btf); + ASSERT_EQ(err, -E2BIG, "check btf"); + btf__free(btf); + break; + } + + while (test__start_subtest("btf: missing lock")) { + btf = init_btf(); + if (!ASSERT_OK_PTR(btf, "init_btf")) + break; + id = btf__add_struct(btf, "foo", 16); + if (!ASSERT_EQ(id, 5, "btf__add_struct foo")) + break; + err = btf__add_field(btf, "a", LIST_HEAD, 0, 0); + if (!ASSERT_OK(err, "btf__add_struct foo::a")) + break; + id = btf__add_decl_tag(btf, "contains:baz:a", 5, 0); + if (!ASSERT_EQ(id, 6, "btf__add_decl_tag contains:baz:a")) + break; + id = btf__add_struct(btf, "baz", 16); + if (!ASSERT_EQ(id, 7, "btf__add_struct baz")) + break; + err = btf__add_field(btf, "a", LIST_NODE, 0, 0); + if (!ASSERT_OK(err, "btf__add_field baz::a")) + break; + + err = btf__load_into_kernel(btf); + ASSERT_EQ(err, -EINVAL, "check btf"); + btf__free(btf); + break; + } + + while (test__start_subtest("btf: bad offset")) { + btf = init_btf(); + if (!ASSERT_OK_PTR(btf, "init_btf")) + break; + id = btf__add_struct(btf, "foo", 36); + if (!ASSERT_EQ(id, 5, "btf__add_struct foo")) + break; + err = btf__add_field(btf, "a", LIST_HEAD, 0, 0); + if (!ASSERT_OK(err, "btf__add_field foo::a")) + break; + err = btf__add_field(btf, "b", LIST_NODE, 0, 0); + if (!ASSERT_OK(err, "btf__add_field foo::b")) + break; + err = btf__add_field(btf, "c", SPIN_LOCK, 0, 0); + if (!ASSERT_OK(err, "btf__add_field foo::c")) + break; + id = btf__add_decl_tag(btf, "contains:foo:b", 5, 0); + if (!ASSERT_EQ(id, 6, "btf__add_decl_tag contains:foo:b")) + break; + + err = btf__load_into_kernel(btf); + ASSERT_EQ(err, -EEXIST, "check btf"); + btf__free(btf); + break; + } + + while (test__start_subtest("btf: missing contains:")) { + btf = init_btf(); + if (!ASSERT_OK_PTR(btf, "init_btf")) + break; + id = btf__add_struct(btf, "foo", 24); + if (!ASSERT_EQ(id, 5, "btf__add_struct foo")) + break; + err = btf__add_field(btf, "a", SPIN_LOCK, 0, 0); + if (!ASSERT_OK(err, "btf__add_field foo::a")) + break; + err = btf__add_field(btf, "b", LIST_HEAD, 64, 0); + if (!ASSERT_OK(err, "btf__add_field foo::b")) + break; + + err = btf__load_into_kernel(btf); + ASSERT_EQ(err, -EINVAL, "check btf"); + btf__free(btf); + break; + } + + while (test__start_subtest("btf: missing struct")) { + btf = init_btf(); + if (!ASSERT_OK_PTR(btf, "init_btf")) + break; + id = btf__add_struct(btf, "foo", 24); + if (!ASSERT_EQ(id, 5, "btf__add_struct foo")) + break; + err = btf__add_field(btf, "a", SPIN_LOCK, 0, 0); + if (!ASSERT_OK(err, "btf__add_field foo::a")) + break; + err = btf__add_field(btf, "b", LIST_HEAD, 64, 0); + if (!ASSERT_OK(err, "btf__add_field foo::b")) + break; + id = btf__add_decl_tag(btf, "contains:bar:bar", 5, 1); + if (!ASSERT_EQ(id, 6, "btf__add_decl_tag contains:bar:bar")) + break; + + err = btf__load_into_kernel(btf); + ASSERT_EQ(err, -ENOENT, "check btf"); + btf__free(btf); + break; + } + + while (test__start_subtest("btf: missing node")) { + btf = init_btf(); + if (!ASSERT_OK_PTR(btf, "init_btf")) + break; + id = btf__add_struct(btf, "foo", 24); + if (!ASSERT_EQ(id, 5, "btf__add_struct foo")) + break; + err = btf__add_field(btf, "a", SPIN_LOCK, 0, 0); + if (!ASSERT_OK(err, "btf__add_field foo::a")) + break; + err = btf__add_field(btf, "b", LIST_HEAD, 64, 0); + if (!ASSERT_OK(err, "btf__add_field foo::b")) + break; + id = btf__add_decl_tag(btf, "contains:foo:c", 5, 1); + if (!ASSERT_EQ(id, 6, "btf__add_decl_tag contains:foo:c")) + break; + + err = btf__load_into_kernel(btf); + btf__free(btf); + ASSERT_EQ(err, -ENOENT, "check btf"); + break; + } + + while (test__start_subtest("btf: node incorrect type")) { + btf = init_btf(); + if (!ASSERT_OK_PTR(btf, "init_btf")) + break; + id = btf__add_struct(btf, "foo", 20); + if (!ASSERT_EQ(id, 5, "btf__add_struct foo")) + break; + err = btf__add_field(btf, "a", LIST_HEAD, 0, 0); + if (!ASSERT_OK(err, "btf__add_field foo::a")) + break; + err = btf__add_field(btf, "b", SPIN_LOCK, 128, 0); + if (!ASSERT_OK(err, "btf__add_field foo::b")) + break; + id = btf__add_decl_tag(btf, "contains:bar:a", 5, 0); + if (!ASSERT_EQ(id, 6, "btf__add_decl_tag contains:bar:a")) + break; + id = btf__add_struct(btf, "bar", 4); + if (!ASSERT_EQ(id, 7, "btf__add_struct bar")) + break; + err = btf__add_field(btf, "a", SPIN_LOCK, 0, 0); + if (!ASSERT_OK(err, "btf__add_field bar::a")) + break; + + err = btf__load_into_kernel(btf); + ASSERT_EQ(err, -EINVAL, "check btf"); + btf__free(btf); + break; + } + + while (test__start_subtest("btf: multiple bpf_list_node with name b")) { + btf = init_btf(); + if (!ASSERT_OK_PTR(btf, "init_btf")) + break; + id = btf__add_struct(btf, "foo", 52); + if (!ASSERT_EQ(id, 5, "btf__add_struct foo")) + break; + err = btf__add_field(btf, "a", LIST_HEAD, 0, 0); + if (!ASSERT_OK(err, "btf__add_field foo::a")) + break; + err = btf__add_field(btf, "b", LIST_NODE, 128, 0); + if (!ASSERT_OK(err, "btf__add_field foo::b")) + break; + err = btf__add_field(btf, "b", LIST_NODE, 256, 0); + if (!ASSERT_OK(err, "btf__add_field foo::c")) + break; + err = btf__add_field(btf, "d", SPIN_LOCK, 384, 0); + if (!ASSERT_OK(err, "btf__add_field foo::d")) + break; + id = btf__add_decl_tag(btf, "contains:foo:b", 5, 0); + if (!ASSERT_EQ(id, 6, "btf__add_decl_tag contains:foo:b")) + break; + + err = btf__load_into_kernel(btf); + ASSERT_EQ(err, -EINVAL, "check btf"); + btf__free(btf); + break; + } + + while (test__start_subtest("btf: owning | owned AA cycle")) { + btf = init_btf(); + if (!ASSERT_OK_PTR(btf, "init_btf")) + break; + id = btf__add_struct(btf, "foo", 36); + if (!ASSERT_EQ(id, 5, "btf__add_struct foo")) + break; + err = btf__add_field(btf, "a", LIST_HEAD, 0, 0); + if (!ASSERT_OK(err, "btf__add_field foo::a")) + break; + err = btf__add_field(btf, "b", LIST_NODE, 128, 0); + if (!ASSERT_OK(err, "btf__add_field foo::b")) + break; + err = btf__add_field(btf, "c", SPIN_LOCK, 256, 0); + if (!ASSERT_OK(err, "btf__add_field foo::c")) + break; + id = btf__add_decl_tag(btf, "contains:foo:b", 5, 0); + if (!ASSERT_EQ(id, 6, "btf__add_decl_tag contains:foo:b")) + break; + + err = btf__load_into_kernel(btf); + ASSERT_EQ(err, -ELOOP, "check btf"); + btf__free(btf); + break; + } + + while (test__start_subtest("btf: owning | owned ABA cycle")) { + btf = init_btf(); + if (!ASSERT_OK_PTR(btf, "init_btf")) + break; + id = btf__add_struct(btf, "foo", 36); + if (!ASSERT_EQ(id, 5, "btf__add_struct foo")) + break; + err = btf__add_field(btf, "a", LIST_HEAD, 0, 0); + if (!ASSERT_OK(err, "btf__add_field foo::a")) + break; + err = btf__add_field(btf, "b", LIST_NODE, 128, 0); + if (!ASSERT_OK(err, "btf__add_field foo::b")) + break; + err = btf__add_field(btf, "c", SPIN_LOCK, 256, 0); + if (!ASSERT_OK(err, "btf__add_field foo::c")) + break; + id = btf__add_decl_tag(btf, "contains:bar:b", 5, 0); + if (!ASSERT_EQ(id, 6, "btf__add_decl_tag contains:bar:b")) + break; + id = btf__add_struct(btf, "bar", 36); + if (!ASSERT_EQ(id, 7, "btf__add_struct bar")) + break; + err = btf__add_field(btf, "a", LIST_HEAD, 0, 0); + if (!ASSERT_OK(err, "btf__add_field bar::a")) + break; + err = btf__add_field(btf, "b", LIST_NODE, 128, 0); + if (!ASSERT_OK(err, "btf__add_field bar::b")) + break; + err = btf__add_field(btf, "c", SPIN_LOCK, 256, 0); + if (!ASSERT_OK(err, "btf__add_field bar::c")) + break; + id = btf__add_decl_tag(btf, "contains:foo:b", 7, 0); + if (!ASSERT_EQ(id, 8, "btf__add_decl_tag contains:foo:b")) + break; + + err = btf__load_into_kernel(btf); + ASSERT_EQ(err, -ELOOP, "check btf"); + btf__free(btf); + break; + } + + while (test__start_subtest("btf: owning -> owned")) { + btf = init_btf(); + if (!ASSERT_OK_PTR(btf, "init_btf")) + break; + id = btf__add_struct(btf, "foo", 20); + if (!ASSERT_EQ(id, 5, "btf__add_struct foo")) + break; + err = btf__add_field(btf, "a", LIST_HEAD, 0, 0); + if (!ASSERT_OK(err, "btf__add_field foo::a")) + break; + err = btf__add_field(btf, "b", SPIN_LOCK, 128, 0); + if (!ASSERT_OK(err, "btf__add_field foo::b")) + break; + id = btf__add_decl_tag(btf, "contains:bar:a", 5, 0); + if (!ASSERT_EQ(id, 6, "btf__add_decl_tag contains:bar:a")) + break; + id = btf__add_struct(btf, "bar", 16); + if (!ASSERT_EQ(id, 7, "btf__add_struct bar")) + break; + err = btf__add_field(btf, "a", LIST_NODE, 0, 0); + if (!ASSERT_OK(err, "btf__add_field bar::a")) + break; + + err = btf__load_into_kernel(btf); + ASSERT_EQ(err, 0, "check btf"); + btf__free(btf); + break; + } + + while (test__start_subtest("btf: owning -> owning | owned -> owned")) { + btf = init_btf(); + if (!ASSERT_OK_PTR(btf, "init_btf")) + break; + id = btf__add_struct(btf, "foo", 20); + if (!ASSERT_EQ(id, 5, "btf__add_struct foo")) + break; + err = btf__add_field(btf, "a", LIST_HEAD, 0, 0); + if (!ASSERT_OK(err, "btf__add_field foo::a")) + break; + err = btf__add_field(btf, "b", SPIN_LOCK, 128, 0); + if (!ASSERT_OK(err, "btf__add_field foo::b")) + break; + id = btf__add_decl_tag(btf, "contains:bar:b", 5, 0); + if (!ASSERT_EQ(id, 6, "btf__add_decl_tag contains:bar:b")) + break; + id = btf__add_struct(btf, "bar", 36); + if (!ASSERT_EQ(id, 7, "btf__add_struct bar")) + break; + err = btf__add_field(btf, "a", LIST_HEAD, 0, 0); + if (!ASSERT_OK(err, "btf__add_field bar::a")) + break; + err = btf__add_field(btf, "b", LIST_NODE, 128, 0); + if (!ASSERT_OK(err, "btf__add_field bar::b")) + break; + err = btf__add_field(btf, "c", SPIN_LOCK, 256, 0); + if (!ASSERT_OK(err, "btf__add_field bar::c")) + break; + id = btf__add_decl_tag(btf, "contains:baz:a", 7, 0); + if (!ASSERT_EQ(id, 8, "btf__add_decl_tag contains:baz:a")) + break; + id = btf__add_struct(btf, "baz", 16); + if (!ASSERT_EQ(id, 9, "btf__add_struct baz")) + break; + err = btf__add_field(btf, "a", LIST_NODE, 0, 0); + if (!ASSERT_OK(err, "btf__add_field baz:a")) + break; + + err = btf__load_into_kernel(btf); + ASSERT_EQ(err, 0, "check btf"); + btf__free(btf); + break; + } + + while (test__start_subtest("btf: owning | owned -> owning | owned -> owned")) { + btf = init_btf(); + if (!ASSERT_OK_PTR(btf, "init_btf")) + break; + id = btf__add_struct(btf, "foo", 36); + if (!ASSERT_EQ(id, 5, "btf__add_struct foo")) + break; + err = btf__add_field(btf, "a", LIST_HEAD, 0, 0); + if (!ASSERT_OK(err, "btf__add_field foo::a")) + break; + err = btf__add_field(btf, "b", LIST_NODE, 128, 0); + if (!ASSERT_OK(err, "btf__add_field foo::b")) + break; + err = btf__add_field(btf, "c", SPIN_LOCK, 256, 0); + if (!ASSERT_OK(err, "btf__add_field foo::c")) + break; + id = btf__add_decl_tag(btf, "contains:bar:b", 5, 0); + if (!ASSERT_EQ(id, 6, "btf__add_decl_tag contains:bar:b")) + break; + id = btf__add_struct(btf, "bar", 36); + if (!ASSERT_EQ(id, 7, "btf__add_struct bar")) + break; + err = btf__add_field(btf, "a", LIST_HEAD, 0, 0); + if (!ASSERT_OK(err, "btf__add_field bar:a")) + break; + err = btf__add_field(btf, "b", LIST_NODE, 128, 0); + if (!ASSERT_OK(err, "btf__add_field bar:b")) + break; + err = btf__add_field(btf, "c", SPIN_LOCK, 256, 0); + if (!ASSERT_OK(err, "btf__add_field bar:c")) + break; + id = btf__add_decl_tag(btf, "contains:baz:a", 7, 0); + if (!ASSERT_EQ(id, 8, "btf__add_decl_tag contains:baz:a")) + break; + id = btf__add_struct(btf, "baz", 16); + if (!ASSERT_EQ(id, 9, "btf__add_struct baz")) + break; + err = btf__add_field(btf, "a", LIST_NODE, 0, 0); + if (!ASSERT_OK(err, "btf__add_field baz:a")) + break; + + err = btf__load_into_kernel(btf); + ASSERT_EQ(err, -ELOOP, "check btf"); + btf__free(btf); + break; + } + + while (test__start_subtest("btf: owning -> owning | owned -> owning | owned -> owned")) { + btf = init_btf(); + if (!ASSERT_OK_PTR(btf, "init_btf")) + break; + id = btf__add_struct(btf, "foo", 20); + if (!ASSERT_EQ(id, 5, "btf__add_struct foo")) + break; + err = btf__add_field(btf, "a", LIST_HEAD, 0, 0); + if (!ASSERT_OK(err, "btf__add_field foo::a")) + break; + err = btf__add_field(btf, "b", SPIN_LOCK, 128, 0); + if (!ASSERT_OK(err, "btf__add_field foo::b")) + break; + id = btf__add_decl_tag(btf, "contains:bar:b", 5, 0); + if (!ASSERT_EQ(id, 6, "btf__add_decl_tag contains:bar:b")) + break; + id = btf__add_struct(btf, "bar", 36); + if (!ASSERT_EQ(id, 7, "btf__add_struct bar")) + break; + err = btf__add_field(btf, "a", LIST_HEAD, 0, 0); + if (!ASSERT_OK(err, "btf__add_field bar::a")) + break; + err = btf__add_field(btf, "b", LIST_NODE, 128, 0); + if (!ASSERT_OK(err, "btf__add_field bar::b")) + break; + err = btf__add_field(btf, "c", SPIN_LOCK, 256, 0); + if (!ASSERT_OK(err, "btf__add_field bar::c")) + break; + id = btf__add_decl_tag(btf, "contains:baz:b", 7, 0); + if (!ASSERT_EQ(id, 8, "btf__add_decl_tag")) + break; + id = btf__add_struct(btf, "baz", 36); + if (!ASSERT_EQ(id, 9, "btf__add_struct baz")) + break; + err = btf__add_field(btf, "a", LIST_HEAD, 0, 0); + if (!ASSERT_OK(err, "btf__add_field bar::a")) + break; + err = btf__add_field(btf, "b", LIST_NODE, 128, 0); + if (!ASSERT_OK(err, "btf__add_field bar::b")) + break; + err = btf__add_field(btf, "c", SPIN_LOCK, 256, 0); + if (!ASSERT_OK(err, "btf__add_field bar::c")) + break; + id = btf__add_decl_tag(btf, "contains:bam:a", 9, 0); + if (!ASSERT_EQ(id, 10, "btf__add_decl_tag contains:bam:a")) + break; + id = btf__add_struct(btf, "bam", 16); + if (!ASSERT_EQ(id, 11, "btf__add_struct bam")) + break; + err = btf__add_field(btf, "a", LIST_NODE, 0, 0); + if (!ASSERT_OK(err, "btf__add_field bam::a")) + break; + + err = btf__load_into_kernel(btf); + ASSERT_EQ(err, -ELOOP, "check btf"); + btf__free(btf); + break; + } +} + void test_linked_list(void) { int i; @@ -245,6 +729,7 @@ void test_linked_list(void) test_linked_list_fail_prog(linked_list_fail_tests[i].prog_name, linked_list_fail_tests[i].err_msg); } + test_btf(); test_linked_list_success(PUSH_POP, false); test_linked_list_success(PUSH_POP, true); test_linked_list_success(PUSH_POP_MULT, false); From 0a2f85a1be4328d29aefa54684d10c23a3298fef Mon Sep 17 00:00:00 2001 From: Kumar Kartikeya Dwivedi Date: Fri, 18 Nov 2022 07:26:14 +0530 Subject: [PATCH 1024/2002] selftests/bpf: Temporarily disable linked list tests The latest clang nightly as of writing crashes with the given test case for BPF linked lists wherever global glock, ghead, glock2 are used, hence comment out the parts that cause the crash, and prepare this commit so that it can be reverted when the fix has been made. More context in [0]. [0]: https://lore.kernel.org/bpf/d56223f9-483e-fbc1-4564-44c0858a1e3e@meta.com Signed-off-by: Kumar Kartikeya Dwivedi Link: https://lore.kernel.org/r/20221118015614.2013203-25-memxor@gmail.com Signed-off-by: Alexei Starovoitov --- .../selftests/bpf/prog_tests/linked_list.c | 21 ++++++++++++------- .../testing/selftests/bpf/progs/linked_list.c | 11 +++++++++- .../testing/selftests/bpf/progs/linked_list.h | 2 ++ .../selftests/bpf/progs/linked_list_fail.c | 16 +++++++------- 4 files changed, 34 insertions(+), 16 deletions(-) diff --git a/tools/testing/selftests/bpf/prog_tests/linked_list.c b/tools/testing/selftests/bpf/prog_tests/linked_list.c index dd73d0a62c6e..6170d36fe5fc 100644 --- a/tools/testing/selftests/bpf/prog_tests/linked_list.c +++ b/tools/testing/selftests/bpf/prog_tests/linked_list.c @@ -24,7 +24,9 @@ static struct { { #test "_missing_lock_pop_back", \ "bpf_spin_lock at off=" #off " must be held for bpf_list_head" }, TEST(kptr, 32) +/* FIXME TEST(global, 16) +*/ TEST(map, 0) TEST(inner_map, 0) #undef TEST @@ -32,9 +34,6 @@ static struct { { #test "_kptr_incorrect_lock_" #op, \ "held lock and object are not in the same allocation\n" \ "bpf_spin_lock at off=32 must be held for bpf_list_head" }, \ - { #test "_global_incorrect_lock_" #op, \ - "held lock and object are not in the same allocation\n" \ - "bpf_spin_lock at off=16 must be held for bpf_list_head" }, \ { #test "_map_incorrect_lock_" #op, \ "held lock and object are not in the same allocation\n" \ "bpf_spin_lock at off=0 must be held for bpf_list_head" }, \ @@ -45,10 +44,6 @@ static struct { TEST(kptr, push_back) TEST(kptr, pop_front) TEST(kptr, pop_back) - TEST(global, push_front) - TEST(global, push_back) - TEST(global, pop_front) - TEST(global, pop_back) TEST(map, push_front) TEST(map, push_back) TEST(map, pop_front) @@ -58,12 +53,14 @@ static struct { TEST(inner_map, pop_front) TEST(inner_map, pop_back) #undef TEST +/* FIXME { "map_compat_kprobe", "tracing progs cannot use bpf_list_head yet" }, { "map_compat_kretprobe", "tracing progs cannot use bpf_list_head yet" }, { "map_compat_tp", "tracing progs cannot use bpf_list_head yet" }, { "map_compat_perf", "tracing progs cannot use bpf_list_head yet" }, { "map_compat_raw_tp", "tracing progs cannot use bpf_list_head yet" }, { "map_compat_raw_tp_w", "tracing progs cannot use bpf_list_head yet" }, +*/ { "obj_type_id_oor", "local type ID argument must be in range [0, U32_MAX]" }, { "obj_new_no_composite", "bpf_obj_new type ID argument must be of a struct" }, { "obj_new_no_struct", "bpf_obj_new type ID argument must be of a struct" }, @@ -78,6 +75,7 @@ static struct { { "direct_write_head", "direct access to bpf_list_head is disallowed" }, { "direct_read_node", "direct access to bpf_list_node is disallowed" }, { "direct_write_node", "direct access to bpf_list_node is disallowed" }, +/* FIXME { "write_after_push_front", "only read is supported" }, { "write_after_push_back", "only read is supported" }, { "use_after_unlock_push_front", "invalid mem access 'scalar'" }, @@ -94,8 +92,11 @@ static struct { { "no_head_type", "bpf_list_head not found at offset=0" }, { "incorrect_head_var_off1", "R1 doesn't have constant offset" }, { "incorrect_head_var_off2", "variable ptr_ access var_off=(0x0; 0xffffffff) disallowed" }, +*/ { "incorrect_head_off1", "bpf_list_head not found at offset=17" }, +/* FIXME { "incorrect_head_off2", "bpf_list_head not found at offset=1" }, +*/ { "pop_front_off", "15: (bf) r1 = r6 ; R1_w=ptr_or_null_foo(id=4,ref_obj_id=4,off=40,imm=0) " "R6_w=ptr_or_null_foo(id=4,ref_obj_id=4,off=40,imm=0) refs=2,4\n" @@ -188,8 +189,10 @@ static void test_linked_list_success(int mode, bool leave_in_map) ret = bpf_prog_test_run_opts(bpf_program__fd(skel->progs.global_list_push_pop), &opts); ASSERT_OK(ret, "global_list_push_pop"); ASSERT_OK(opts.retval, "global_list_push_pop retval"); + /* FIXME: if (!leave_in_map) clear_fields(skel->maps.data_A); + */ if (mode == PUSH_POP) goto end; @@ -210,8 +213,10 @@ ppm: ret = bpf_prog_test_run_opts(bpf_program__fd(skel->progs.global_list_push_pop_multiple), &opts); ASSERT_OK(ret, "global_list_push_pop_multiple"); ASSERT_OK(opts.retval, "global_list_push_pop_multiple retval"); + /* FIXME: if (!leave_in_map) clear_fields(skel->maps.data_A); + */ if (mode == PUSH_POP_MULT) goto end; @@ -232,8 +237,10 @@ lil: ret = bpf_prog_test_run_opts(bpf_program__fd(skel->progs.global_list_in_list), &opts); ASSERT_OK(ret, "global_list_in_list"); ASSERT_OK(opts.retval, "global_list_in_list retval"); + /* FIXME: if (!leave_in_map) clear_fields(skel->maps.data_A); + */ end: linked_list__destroy(skel); } diff --git a/tools/testing/selftests/bpf/progs/linked_list.c b/tools/testing/selftests/bpf/progs/linked_list.c index 2c7b615c6d41..a99103c86e48 100644 --- a/tools/testing/selftests/bpf/progs/linked_list.c +++ b/tools/testing/selftests/bpf/progs/linked_list.c @@ -291,7 +291,10 @@ int inner_map_list_push_pop(void *ctx) SEC("tc") int global_list_push_pop(void *ctx) { - return test_list_push_pop(&glock, &ghead); + /* FIXME: + * return test_list_push_pop(&glock, &ghead); + */ + return 0; } SEC("tc") @@ -327,10 +330,13 @@ int global_list_push_pop_multiple(void *ctx) { int ret; + /* FIXME: ret = list_push_pop_multiple(&glock, &ghead, false); if (ret) return ret; return list_push_pop_multiple(&glock, &ghead, true); + */ + return 0; } SEC("tc") @@ -364,7 +370,10 @@ int inner_map_list_in_list(void *ctx) SEC("tc") int global_list_in_list(void *ctx) { + /* FIXME return test_list_in_list(&glock, &ghead); + */ + return 0; } char _license[] SEC("license") = "GPL"; diff --git a/tools/testing/selftests/bpf/progs/linked_list.h b/tools/testing/selftests/bpf/progs/linked_list.h index 8db80ed64db1..93157efc2d04 100644 --- a/tools/testing/selftests/bpf/progs/linked_list.h +++ b/tools/testing/selftests/bpf/progs/linked_list.h @@ -47,10 +47,12 @@ struct { }, }; +/* FIXME #define private(name) SEC(".data." #name) __hidden __attribute__((aligned(8))) private(A) struct bpf_spin_lock glock; private(A) struct bpf_list_head ghead __contains(foo, node); private(B) struct bpf_spin_lock glock2; +*/ #endif diff --git a/tools/testing/selftests/bpf/progs/linked_list_fail.c b/tools/testing/selftests/bpf/progs/linked_list_fail.c index 1d9017240e19..1b7ed1d3a9bb 100644 --- a/tools/testing/selftests/bpf/progs/linked_list_fail.c +++ b/tools/testing/selftests/bpf/progs/linked_list_fail.c @@ -59,10 +59,12 @@ CHECK(kptr, push_back, &f->head); CHECK(kptr, pop_front, &f->head); CHECK(kptr, pop_back, &f->head); +/* FIXME CHECK(global, push_front, &ghead); CHECK(global, push_back, &ghead); CHECK(global, pop_front, &ghead); CHECK(global, pop_back, &ghead); +*/ CHECK(map, push_front, &v->head); CHECK(map, push_back, &v->head); @@ -89,23 +91,15 @@ CHECK(inner_map, pop_back, &iv->head); #define CHECK_OP(op) \ CHECK(kptr_kptr, op, &f1->lock, &f2->head); \ - CHECK(kptr_global, op, &f1->lock, &ghead); \ CHECK(kptr_map, op, &f1->lock, &v->head); \ CHECK(kptr_inner_map, op, &f1->lock, &iv->head); \ \ - CHECK(global_global, op, &glock2, &ghead); \ - CHECK(global_kptr, op, &glock, &f1->head); \ - CHECK(global_map, op, &glock, &v->head); \ - CHECK(global_inner_map, op, &glock, &iv->head); \ - \ CHECK(map_map, op, &v->lock, &v2->head); \ CHECK(map_kptr, op, &v->lock, &f2->head); \ - CHECK(map_global, op, &v->lock, &ghead); \ CHECK(map_inner_map, op, &v->lock, &iv->head); \ \ CHECK(inner_map_inner_map, op, &iv->lock, &iv2->head); \ CHECK(inner_map_kptr, op, &iv->lock, &f2->head); \ - CHECK(inner_map_global, op, &iv->lock, &ghead); \ CHECK(inner_map_map, op, &iv->lock, &v->head); CHECK_OP(push_front); @@ -117,6 +111,7 @@ CHECK_OP(pop_back); #undef CHECK_OP #undef INIT +/* FIXME SEC("?kprobe/xyz") int map_compat_kprobe(void *ctx) { @@ -158,6 +153,7 @@ int map_compat_raw_tp_w(void *ctx) bpf_list_push_front(&ghead, NULL); return 0; } +*/ SEC("?tc") int obj_type_id_oor(void *ctx) @@ -303,6 +299,7 @@ int direct_write_node(void *ctx) return 0; } +/* FIXME static __always_inline int write_after_op(void (*push_op)(void *head, void *node)) { @@ -506,6 +503,7 @@ int incorrect_head_var_off2(struct __sk_buff *ctx) return 0; } +*/ SEC("?tc") int incorrect_head_off1(void *ctx) @@ -529,6 +527,7 @@ int incorrect_head_off1(void *ctx) return 0; } +/* FIXME SEC("?tc") int incorrect_head_off2(void *ctx) { @@ -545,6 +544,7 @@ int incorrect_head_off2(void *ctx) return 0; } +*/ static __always_inline int pop_ptr_off(void *(*op)(void *head)) From f20a0a0519f35a3a34236ed2d38b067c5cb22b9f Mon Sep 17 00:00:00 2001 From: Vincent Mailhol Date: Thu, 17 Nov 2022 02:18:28 +0900 Subject: [PATCH 1025/2002] ethtool: doc: clarify what drivers can implement in their get_drvinfo() Many of the drivers which implement ethtool_ops::get_drvinfo() will prints the .driver, .version or .bus_info of struct ethtool_drvinfo. To have a glance of current state, do: $ git grep -W "get_drvinfo(struct" Printing in those three fields is useless because: - since [1], the driver version should be the kernel version (at least for upstream drivers). Arguably, out of tree drivers might still want to set a custom version, but out of tree is not our focus. - since [2], the core is able to provide default values for .driver and .bus_info. In summary, drivers may provide .fw_version and .erom_version, the rest is expected to be done by the core. In struct ethtool_ops doc from linux/ethtool: rephrase field get_drvinfo() doc to discourage developers from implementing this callback. In struct ethtool_drvinfo doc from uapi/linux/ethtool.h: remove the paragraph mentioning what drivers should do. Rationale: no need to repeat what is already written in struct ethtool_ops doc. But add a note that .fw_version and .erom_version are driver defined. Also update the dummy driver and simply remove the callback in order not to confuse the newcomers: most of the drivers will not need this callback function any more. [1] commit 6a7e25c7fb48 ("net/core: Replace driver version to be kernel version") Link: https://git.kernel.org/torvalds/linux/c/6a7e25c7fb48 [2] commit edaf5df22cb8 ("ethtool: ethtool_get_drvinfo: populate drvinfo fields even if callback exits") Link: https://git.kernel.org/netdev/net-next/c/edaf5df22cb8 Reviewed-by: Leon Romanovsky Signed-off-by: Vincent Mailhol Link: https://lore.kernel.org/r/20221116171828.4093-1-mailhol.vincent@wanadoo.fr Signed-off-by: Jakub Kicinski --- drivers/net/dummy.c | 7 ------- include/linux/ethtool.h | 8 ++++---- include/uapi/linux/ethtool.h | 10 ++++------ 3 files changed, 8 insertions(+), 17 deletions(-) diff --git a/drivers/net/dummy.c b/drivers/net/dummy.c index aa0fc00faecb..c4b1b0aa438a 100644 --- a/drivers/net/dummy.c +++ b/drivers/net/dummy.c @@ -99,14 +99,7 @@ static const struct net_device_ops dummy_netdev_ops = { .ndo_change_carrier = dummy_change_carrier, }; -static void dummy_get_drvinfo(struct net_device *dev, - struct ethtool_drvinfo *info) -{ - strscpy(info->driver, DRV_NAME, sizeof(info->driver)); -} - static const struct ethtool_ops dummy_ethtool_ops = { - .get_drvinfo = dummy_get_drvinfo, .get_ts_info = ethtool_op_get_ts_info, }; diff --git a/include/linux/ethtool.h b/include/linux/ethtool.h index 5c51c7fda32a..9e0a76fc7de9 100644 --- a/include/linux/ethtool.h +++ b/include/linux/ethtool.h @@ -473,10 +473,10 @@ struct ethtool_module_power_mode_params { * parameter. * @supported_coalesce_params: supported types of interrupt coalescing. * @supported_ring_params: supported ring params. - * @get_drvinfo: Report driver/device information. Should only set the - * @driver, @version, @fw_version and @bus_info fields. If not - * implemented, the @driver and @bus_info fields will be filled in - * according to the netdev's parent device. + * @get_drvinfo: Report driver/device information. Modern drivers no + * longer have to implement this callback. Most fields are + * correctly filled in by the core using system information, or + * populated using other driver operations. * @get_regs_len: Get buffer length required for @get_regs * @get_regs: Get device registers * @get_wol: Report whether Wake-on-Lan is enabled diff --git a/include/uapi/linux/ethtool.h b/include/uapi/linux/ethtool.h index f341de2ae612..58e587ba0450 100644 --- a/include/uapi/linux/ethtool.h +++ b/include/uapi/linux/ethtool.h @@ -159,8 +159,10 @@ static inline __u32 ethtool_cmd_speed(const struct ethtool_cmd *ep) * in its bus driver structure (e.g. pci_driver::name). Must * not be an empty string. * @version: Driver version string; may be an empty string - * @fw_version: Firmware version string; may be an empty string - * @erom_version: Expansion ROM version string; may be an empty string + * @fw_version: Firmware version string; driver defined; may be an + * empty string + * @erom_version: Expansion ROM version string; driver defined; may be + * an empty string * @bus_info: Device bus address. This should match the dev_name() * string for the underlying bus device, if there is one. May be * an empty string. @@ -179,10 +181,6 @@ static inline __u32 ethtool_cmd_speed(const struct ethtool_cmd *ep) * * Users can use the %ETHTOOL_GSSET_INFO command to get the number of * strings in any string set (from Linux 2.6.34). - * - * Drivers should set at most @driver, @version, @fw_version and - * @bus_info in their get_drvinfo() implementation. The ethtool - * core fills in the other fields using other driver operations. */ struct ethtool_drvinfo { __u32 cmd; From 0171a1d22bb99174671484f409f66f5b96c073b4 Mon Sep 17 00:00:00 2001 From: Rasmus Villemoes Date: Wed, 16 Nov 2022 11:52:02 +0100 Subject: [PATCH 1026/2002] net: dsa: refactor name assignment for user ports The following two patches each have a (small) chance of causing regressions for userspace and will in that case of course need to be reverted. In order to prepare for that and make those two patches independent and individually revertable, refactor the code which sets the names for user ports by moving the "fall back to eth%d if no label is given in device tree" to dsa_slave_create(). No functional change (at least none intended). Signed-off-by: Rasmus Villemoes Reviewed-by: Andrew Lunn Reviewed-by: Florian Fainelli Signed-off-by: Jakub Kicinski --- net/dsa/dsa2.c | 3 --- net/dsa/slave.c | 13 +++++++++++-- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/net/dsa/dsa2.c b/net/dsa/dsa2.c index 12145c852902..d3055b49080f 100644 --- a/net/dsa/dsa2.c +++ b/net/dsa/dsa2.c @@ -1365,9 +1365,6 @@ static struct dsa_port *dsa_port_touch(struct dsa_switch *ds, int index) static int dsa_port_parse_user(struct dsa_port *dp, const char *name) { - if (!name) - name = "eth%d"; - dp->type = DSA_PORT_TYPE_USER; dp->name = name; diff --git a/net/dsa/slave.c b/net/dsa/slave.c index 4176482cd03f..2ae75d4d8389 100644 --- a/net/dsa/slave.c +++ b/net/dsa/slave.c @@ -2366,16 +2366,25 @@ int dsa_slave_create(struct dsa_port *port) { struct net_device *master = dsa_port_to_master(port); struct dsa_switch *ds = port->ds; - const char *name = port->name; struct net_device *slave_dev; struct dsa_slave_priv *p; + const char *name; + int assign_type; int ret; if (!ds->num_tx_queues) ds->num_tx_queues = 1; + if (port->name) { + name = port->name; + assign_type = NET_NAME_UNKNOWN; + } else { + name = "eth%d"; + assign_type = NET_NAME_UNKNOWN; + } + slave_dev = alloc_netdev_mqs(sizeof(struct dsa_slave_priv), name, - NET_NAME_UNKNOWN, ether_setup, + assign_type, ether_setup, ds->num_tx_queues, 1); if (slave_dev == NULL) return -ENOMEM; From 6fdb038420407d9781b2ba2429f836434a25e91a Mon Sep 17 00:00:00 2001 From: Rasmus Villemoes Date: Wed, 16 Nov 2022 11:52:03 +0100 Subject: [PATCH 1027/2002] net: dsa: use NET_NAME_PREDICTABLE for user ports with name given in DT When a user port has a label in device tree, the corresponding netdevice is, to quote include/uapi/linux/netdevice.h, "predictably named by the kernel". This is also explicitly one of the intended use cases for NET_NAME_PREDICTABLE, quoting 685343fc3ba6 ("net: add name_assign_type netdev attribute"): NET_NAME_PREDICTABLE: The ifname has been assigned by the kernel in a predictable way [...] Examples include [...] and names deduced from hardware properties (including being given explicitly by the firmware). Expose that information properly for the benefit of userspace tools that make decisions based on the name_assign_type attribute, e.g. a systemd-udev rule with "kernel" in NamePolicy. Signed-off-by: Rasmus Villemoes Reviewed-by: Andrew Lunn Reviewed-by: Florian Fainelli Signed-off-by: Jakub Kicinski --- net/dsa/slave.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/dsa/slave.c b/net/dsa/slave.c index 2ae75d4d8389..5405b730eb92 100644 --- a/net/dsa/slave.c +++ b/net/dsa/slave.c @@ -2377,7 +2377,7 @@ int dsa_slave_create(struct dsa_port *port) if (port->name) { name = port->name; - assign_type = NET_NAME_UNKNOWN; + assign_type = NET_NAME_PREDICTABLE; } else { name = "eth%d"; assign_type = NET_NAME_UNKNOWN; From b8790661d90d7bbfb745df40262d13a0e8ae6400 Mon Sep 17 00:00:00 2001 From: Rasmus Villemoes Date: Wed, 16 Nov 2022 11:52:04 +0100 Subject: [PATCH 1028/2002] net: dsa: set name_assign_type to NET_NAME_ENUM for enumerated user ports When a user port does not have a label in device tree, and we thus fall back to the eth%d scheme, the proper constant to use is NET_NAME_ENUM. See also commit e9f656b7a214 ("net: ethernet: set default assignment identifier to NET_NAME_ENUM"), which in turn quoted commit 685343fc3ba6 ("net: add name_assign_type netdev attribute"): ... when the kernel has given the interface a name using global device enumeration based on order of discovery (ethX, wlanY, etc) ... are labelled NET_NAME_ENUM. Signed-off-by: Rasmus Villemoes Reviewed-by: Andrew Lunn Reviewed-by: Florian Fainelli Signed-off-by: Jakub Kicinski --- net/dsa/slave.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/dsa/slave.c b/net/dsa/slave.c index 5405b730eb92..24d8ad36fc8b 100644 --- a/net/dsa/slave.c +++ b/net/dsa/slave.c @@ -2380,7 +2380,7 @@ int dsa_slave_create(struct dsa_port *port) assign_type = NET_NAME_PREDICTABLE; } else { name = "eth%d"; - assign_type = NET_NAME_UNKNOWN; + assign_type = NET_NAME_ENUM; } slave_dev = alloc_netdev_mqs(sizeof(struct dsa_slave_priv), name, From cbe5f7c0fbcd3184fe9a5c1880799e2b23ec9c6d Mon Sep 17 00:00:00 2001 From: Robert Marko Date: Mon, 14 Nov 2022 20:47:30 +0100 Subject: [PATCH 1029/2002] dt-bindings: net: ipq4019-mdio: document IPQ6018 compatible Document IPQ6018 compatible that is already being used in the DTS along with the fallback IPQ4019 compatible as driver itself only gets probed on IPQ4019 and IPQ5018 compatibles. This is also required in order to specify which platform require clock to be defined and validate it in schema. Signed-off-by: Robert Marko Reviewed-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20221114194734.3287854-1-robimarko@gmail.com Signed-off-by: Jakub Kicinski --- .../devicetree/bindings/net/qcom,ipq4019-mdio.yaml | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/Documentation/devicetree/bindings/net/qcom,ipq4019-mdio.yaml b/Documentation/devicetree/bindings/net/qcom,ipq4019-mdio.yaml index ad8b2b41c140..1ba8de982bd1 100644 --- a/Documentation/devicetree/bindings/net/qcom,ipq4019-mdio.yaml +++ b/Documentation/devicetree/bindings/net/qcom,ipq4019-mdio.yaml @@ -14,9 +14,15 @@ allOf: properties: compatible: - enum: - - qcom,ipq4019-mdio - - qcom,ipq5018-mdio + oneOf: + - enum: + - qcom,ipq4019-mdio + - qcom,ipq5018-mdio + + - items: + - enum: + - qcom,ipq6018-mdio + - const: qcom,ipq4019-mdio "#address-cells": const: 1 From 05c1cbb96f3d133ce52986aeab7a782684e408ce Mon Sep 17 00:00:00 2001 From: Robert Marko Date: Mon, 14 Nov 2022 20:47:31 +0100 Subject: [PATCH 1030/2002] dt-bindings: net: ipq4019-mdio: add IPQ8074 compatible Allow using IPQ8074 specific compatible along with the fallback IPQ4019 one in order to be able to specify which compatibles require clocks to be able to validate them via schema. Signed-off-by: Robert Marko Reviewed-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20221114194734.3287854-2-robimarko@gmail.com Signed-off-by: Jakub Kicinski --- Documentation/devicetree/bindings/net/qcom,ipq4019-mdio.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/net/qcom,ipq4019-mdio.yaml b/Documentation/devicetree/bindings/net/qcom,ipq4019-mdio.yaml index 1ba8de982bd1..f4918c9e6fd2 100644 --- a/Documentation/devicetree/bindings/net/qcom,ipq4019-mdio.yaml +++ b/Documentation/devicetree/bindings/net/qcom,ipq4019-mdio.yaml @@ -22,6 +22,7 @@ properties: - items: - enum: - qcom,ipq6018-mdio + - qcom,ipq8074-mdio - const: qcom,ipq4019-mdio "#address-cells": From e50c50367d98297703f8a02e3c9241e8b4c37b8f Mon Sep 17 00:00:00 2001 From: Robert Marko Date: Mon, 14 Nov 2022 20:47:32 +0100 Subject: [PATCH 1031/2002] dt-bindings: net: ipq4019-mdio: require and validate clocks Now that we can match the platforms requiring clocks by compatible start using those to allow clocks per compatible and make them required. Signed-off-by: Robert Marko Reviewed-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20221114194734.3287854-3-robimarko@gmail.com Signed-off-by: Jakub Kicinski --- .../bindings/net/qcom,ipq4019-mdio.yaml | 27 ++++++++++++++----- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/Documentation/devicetree/bindings/net/qcom,ipq4019-mdio.yaml b/Documentation/devicetree/bindings/net/qcom,ipq4019-mdio.yaml index f4918c9e6fd2..72561e3aeee3 100644 --- a/Documentation/devicetree/bindings/net/qcom,ipq4019-mdio.yaml +++ b/Documentation/devicetree/bindings/net/qcom,ipq4019-mdio.yaml @@ -9,9 +9,6 @@ title: Qualcomm IPQ40xx MDIO Controller maintainers: - Robert Marko -allOf: - - $ref: "mdio.yaml#" - properties: compatible: oneOf: @@ -40,10 +37,8 @@ properties: address range is only required by the platform IPQ50xx. clocks: - maxItems: 1 - description: | - MDIO clock source frequency fixed to 100MHZ, this clock should be specified - by the platform IPQ807x, IPQ60xx and IPQ50xx. + items: + - description: MDIO clock source frequency fixed to 100MHZ required: - compatible @@ -51,6 +46,24 @@ required: - "#address-cells" - "#size-cells" +allOf: + - $ref: "mdio.yaml#" + + - if: + properties: + compatible: + contains: + enum: + - qcom,ipq5018-mdio + - qcom,ipq6018-mdio + - qcom,ipq8074-mdio + then: + required: + - clocks + else: + properties: + clocks: false + unevaluatedProperties: false examples: From 4a8c14384fa96c0bd6c1a534667f2a72165faacf Mon Sep 17 00:00:00 2001 From: Robert Marko Date: Mon, 14 Nov 2022 20:47:33 +0100 Subject: [PATCH 1032/2002] dt-bindings: net: ipq4019-mdio: document required clock-names IPQ5018, IPQ6018 and IPQ8074 require clock-names to be set as driver is requesting the clock based on it and not index, so document that and make it required for the listed SoC-s. Signed-off-by: Robert Marko Reviewed-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20221114194734.3287854-4-robimarko@gmail.com Signed-off-by: Jakub Kicinski --- .../devicetree/bindings/net/qcom,ipq4019-mdio.yaml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Documentation/devicetree/bindings/net/qcom,ipq4019-mdio.yaml b/Documentation/devicetree/bindings/net/qcom,ipq4019-mdio.yaml index 72561e3aeee3..7631ecc8fd01 100644 --- a/Documentation/devicetree/bindings/net/qcom,ipq4019-mdio.yaml +++ b/Documentation/devicetree/bindings/net/qcom,ipq4019-mdio.yaml @@ -40,6 +40,10 @@ properties: items: - description: MDIO clock source frequency fixed to 100MHZ + clock-names: + items: + - const: gcc_mdio_ahb_clk + required: - compatible - reg @@ -60,9 +64,11 @@ allOf: then: required: - clocks + - clock-names else: properties: clocks: false + clock-names: false unevaluatedProperties: false From 9999f85ba34651726018e0f50d4afdf6c8cc8096 Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Tue, 15 Nov 2022 03:18:42 +0200 Subject: [PATCH 1033/2002] net: dsa: stop exposing tag proto module helpers to the world The DSA tagging protocol driver macros are in the public include/net/dsa.h probably because that's also where the DSA_TAG_PROTO_*_VALUE macros are (MODULE_ALIAS_DSA_TAG_DRIVER hinges on those macro definitions). But there is no reason to expose these helpers to . That header is shared between switch drivers (drivers/net/dsa/), tagging protocol drivers (net/dsa/tag_*.c), the DSA core (net/dsa/ sans tag_*.c), and the rest of the world (DSA master drivers, network stack, etc). Too much exposure. On the other hand, net/dsa/dsa_priv.h is included only by the DSA core and by DSA tagging protocol drivers (or IOW, "friend" modules). Also a bit too much exposure - I've contemplated creating a new header which is only included by tagging protocol drivers, but completely separating a new dsa_tag_proto.h from dsa_priv.h is not immediately trivial - for example dsa_slave_to_port() is used both from the fast path and from the control path. So for now, move these definitions to dsa_priv.h which at least hides them from the world. Signed-off-by: Vladimir Oltean Tested-by: Michael Walle Signed-off-by: Jakub Kicinski --- include/net/dsa.h | 70 ---------------------------------------------- net/dsa/dsa_priv.h | 70 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 70 insertions(+), 70 deletions(-) diff --git a/include/net/dsa.h b/include/net/dsa.h index dde364688739..82da44561f4c 100644 --- a/include/net/dsa.h +++ b/include/net/dsa.h @@ -118,10 +118,6 @@ struct dsa_netdevice_ops { int cmd); }; -#define DSA_TAG_DRIVER_ALIAS "dsa_tag-" -#define MODULE_ALIAS_DSA_TAG_DRIVER(__proto) \ - MODULE_ALIAS(DSA_TAG_DRIVER_ALIAS __stringify(__proto##_VALUE)) - struct dsa_lag { struct net_device *dev; unsigned int id; @@ -1400,70 +1396,4 @@ static inline bool dsa_slave_dev_check(const struct net_device *dev) netdev_tx_t dsa_enqueue_skb(struct sk_buff *skb, struct net_device *dev); void dsa_port_phylink_mac_change(struct dsa_switch *ds, int port, bool up); -struct dsa_tag_driver { - const struct dsa_device_ops *ops; - struct list_head list; - struct module *owner; -}; - -void dsa_tag_drivers_register(struct dsa_tag_driver *dsa_tag_driver_array[], - unsigned int count, - struct module *owner); -void dsa_tag_drivers_unregister(struct dsa_tag_driver *dsa_tag_driver_array[], - unsigned int count); - -#define dsa_tag_driver_module_drivers(__dsa_tag_drivers_array, __count) \ -static int __init dsa_tag_driver_module_init(void) \ -{ \ - dsa_tag_drivers_register(__dsa_tag_drivers_array, __count, \ - THIS_MODULE); \ - return 0; \ -} \ -module_init(dsa_tag_driver_module_init); \ - \ -static void __exit dsa_tag_driver_module_exit(void) \ -{ \ - dsa_tag_drivers_unregister(__dsa_tag_drivers_array, __count); \ -} \ -module_exit(dsa_tag_driver_module_exit) - -/** - * module_dsa_tag_drivers() - Helper macro for registering DSA tag - * drivers - * @__ops_array: Array of tag driver structures - * - * Helper macro for DSA tag drivers which do not do anything special - * in module init/exit. Each module may only use this macro once, and - * calling it replaces module_init() and module_exit(). - */ -#define module_dsa_tag_drivers(__ops_array) \ -dsa_tag_driver_module_drivers(__ops_array, ARRAY_SIZE(__ops_array)) - -#define DSA_TAG_DRIVER_NAME(__ops) dsa_tag_driver ## _ ## __ops - -/* Create a static structure we can build a linked list of dsa_tag - * drivers - */ -#define DSA_TAG_DRIVER(__ops) \ -static struct dsa_tag_driver DSA_TAG_DRIVER_NAME(__ops) = { \ - .ops = &__ops, \ -} - -/** - * module_dsa_tag_driver() - Helper macro for registering a single DSA tag - * driver - * @__ops: Single tag driver structures - * - * Helper macro for DSA tag drivers which do not do anything special - * in module init/exit. Each module may only use this macro once, and - * calling it replaces module_init() and module_exit(). - */ -#define module_dsa_tag_driver(__ops) \ -DSA_TAG_DRIVER(__ops); \ - \ -static struct dsa_tag_driver *dsa_tag_driver_array[] = { \ - &DSA_TAG_DRIVER_NAME(__ops) \ -}; \ -module_dsa_tag_drivers(dsa_tag_driver_array) #endif - diff --git a/net/dsa/dsa_priv.h b/net/dsa/dsa_priv.h index 71e9707d11d4..23d2dfdbc1ab 100644 --- a/net/dsa/dsa_priv.h +++ b/net/dsa/dsa_priv.h @@ -17,6 +17,76 @@ #define DSA_MAX_NUM_OFFLOADING_BRIDGES BITS_PER_LONG +#define DSA_TAG_DRIVER_ALIAS "dsa_tag-" +#define MODULE_ALIAS_DSA_TAG_DRIVER(__proto) \ + MODULE_ALIAS(DSA_TAG_DRIVER_ALIAS __stringify(__proto##_VALUE)) + +struct dsa_tag_driver { + const struct dsa_device_ops *ops; + struct list_head list; + struct module *owner; +}; + +void dsa_tag_drivers_register(struct dsa_tag_driver *dsa_tag_driver_array[], + unsigned int count, + struct module *owner); +void dsa_tag_drivers_unregister(struct dsa_tag_driver *dsa_tag_driver_array[], + unsigned int count); + +#define dsa_tag_driver_module_drivers(__dsa_tag_drivers_array, __count) \ +static int __init dsa_tag_driver_module_init(void) \ +{ \ + dsa_tag_drivers_register(__dsa_tag_drivers_array, __count, \ + THIS_MODULE); \ + return 0; \ +} \ +module_init(dsa_tag_driver_module_init); \ + \ +static void __exit dsa_tag_driver_module_exit(void) \ +{ \ + dsa_tag_drivers_unregister(__dsa_tag_drivers_array, __count); \ +} \ +module_exit(dsa_tag_driver_module_exit) + +/** + * module_dsa_tag_drivers() - Helper macro for registering DSA tag + * drivers + * @__ops_array: Array of tag driver structures + * + * Helper macro for DSA tag drivers which do not do anything special + * in module init/exit. Each module may only use this macro once, and + * calling it replaces module_init() and module_exit(). + */ +#define module_dsa_tag_drivers(__ops_array) \ +dsa_tag_driver_module_drivers(__ops_array, ARRAY_SIZE(__ops_array)) + +#define DSA_TAG_DRIVER_NAME(__ops) dsa_tag_driver ## _ ## __ops + +/* Create a static structure we can build a linked list of dsa_tag + * drivers + */ +#define DSA_TAG_DRIVER(__ops) \ +static struct dsa_tag_driver DSA_TAG_DRIVER_NAME(__ops) = { \ + .ops = &__ops, \ +} + +/** + * module_dsa_tag_driver() - Helper macro for registering a single DSA tag + * driver + * @__ops: Single tag driver structures + * + * Helper macro for DSA tag drivers which do not do anything special + * in module init/exit. Each module may only use this macro once, and + * calling it replaces module_init() and module_exit(). + */ +#define module_dsa_tag_driver(__ops) \ +DSA_TAG_DRIVER(__ops); \ + \ +static struct dsa_tag_driver *dsa_tag_driver_array[] = { \ + &DSA_TAG_DRIVER_NAME(__ops) \ +}; \ +module_dsa_tag_drivers(dsa_tag_driver_array) + enum { DSA_NOTIFIER_AGEING_TIME, DSA_NOTIFIER_BRIDGE_JOIN, From 2610937d7e95d010e7301277408457cd385a9288 Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Tue, 15 Nov 2022 03:18:43 +0200 Subject: [PATCH 1034/2002] net: dsa: rename tagging protocol driver modalias It's autumn cleanup time, and today's target are modaliases. Michael says that for users of modinfo, "dsa_tag-20" is not the most suggestive name, and recommends a change to "dsa_tag-id-20". Andrew points out that other modaliases have a prefix delimited by colons, so he recommends "dsa_tag:20" instead of "dsa_tag-20". To satisfy both proposals, Florian recommends "dsa_tag:id-20". The modaliases are not stable ABI, and the essential information (protocol ID) is still conveyed in the new string, which request_module() must be adapted to form. Link: 20221027210830.3577793-1-vladimir.oltean@nxp.com Suggested-by: Andrew Lunn Suggested-by: Michael Walle Suggested-by: Florian Fainelli Signed-off-by: Vladimir Oltean Tested-by: Michael Walle Signed-off-by: Jakub Kicinski --- net/dsa/dsa.c | 2 +- net/dsa/dsa_priv.h | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/net/dsa/dsa.c b/net/dsa/dsa.c index 6caf2ec648fd..cc6a497a0e50 100644 --- a/net/dsa/dsa.c +++ b/net/dsa/dsa.c @@ -108,7 +108,7 @@ const struct dsa_device_ops *dsa_tag_driver_get(int tag_protocol) const struct dsa_device_ops *ops; bool found = false; - request_module("%s%d", DSA_TAG_DRIVER_ALIAS, tag_protocol); + request_module("%sid-%d", DSA_TAG_DRIVER_ALIAS, tag_protocol); mutex_lock(&dsa_tag_drivers_lock); list_for_each_entry(dsa_tag_driver, &dsa_tag_drivers_list, list) { diff --git a/net/dsa/dsa_priv.h b/net/dsa/dsa_priv.h index 23d2dfdbc1ab..383d224c8143 100644 --- a/net/dsa/dsa_priv.h +++ b/net/dsa/dsa_priv.h @@ -17,9 +17,10 @@ #define DSA_MAX_NUM_OFFLOADING_BRIDGES BITS_PER_LONG -#define DSA_TAG_DRIVER_ALIAS "dsa_tag-" -#define MODULE_ALIAS_DSA_TAG_DRIVER(__proto) \ - MODULE_ALIAS(DSA_TAG_DRIVER_ALIAS __stringify(__proto##_VALUE)) +#define DSA_TAG_DRIVER_ALIAS "dsa_tag:" +#define MODULE_ALIAS_DSA_TAG_DRIVER(__proto) \ + MODULE_ALIAS(DSA_TAG_DRIVER_ALIAS "id-" \ + __stringify(__proto##_VALUE)) struct dsa_tag_driver { const struct dsa_device_ops *ops; From 94793a56b3df0ff2b8c5680f926c19effd8b9ccc Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Tue, 15 Nov 2022 03:18:44 +0200 Subject: [PATCH 1035/2002] net: dsa: provide a second modalias to tag proto drivers based on their name Currently, tagging protocol drivers have a modalias of "dsa_tag:id-", where the number is one of DSA_TAG_PROTO_*_VALUE. This modalias makes it possible for the request_module() call in dsa_tag_driver_get() to work, given the input it has - an integer returned by ds->ops->get_tag_protocol(). It is also possible to change tagging protocols at (pseudo-)runtime, via sysfs or via device tree, and this works via the name string of the tagging protocol rather than via its id (DSA_TAG_PROTO_*_VALUE). In the latter case, there is no request_module() call, because there is no association that the DSA core has between the string name and the ID, to construct the modalias. The module is simply assumed to have been inserted. This is actually slightly problematic when the tagging protocol change should take place at probe time, since it's expected that the dependency module should get autoloaded. For this purpose, let's introduce a second modalias, so that the DSA core can call request_module() by name. There is no reason to make the modalias by name optional, so just modify the MODULE_ALIAS_DSA_TAG_DRIVER() macro to take both the ID and the name as arguments, and generate two modaliases behind the scenes. Suggested-by: Michael Walle Signed-off-by: Vladimir Oltean Tested-by: Michael Walle # on kontron-sl28 w/ ocelot_8021q Tested-by: Michael Walle Signed-off-by: Jakub Kicinski --- net/dsa/dsa_priv.h | 6 +++++- net/dsa/tag_ar9331.c | 6 ++++-- net/dsa/tag_brcm.c | 16 ++++++++++------ net/dsa/tag_dsa.c | 11 +++++++---- net/dsa/tag_gswip.c | 6 ++++-- net/dsa/tag_hellcreek.c | 6 ++++-- net/dsa/tag_ksz.c | 21 +++++++++++++-------- net/dsa/tag_lan9303.c | 6 ++++-- net/dsa/tag_mtk.c | 6 ++++-- net/dsa/tag_ocelot.c | 11 +++++++---- net/dsa/tag_ocelot_8021q.c | 6 ++++-- net/dsa/tag_qca.c | 6 ++++-- net/dsa/tag_rtl4_a.c | 6 ++++-- net/dsa/tag_rtl8_4.c | 7 +++++-- net/dsa/tag_rzn1_a5psw.c | 6 ++++-- net/dsa/tag_sja1105.c | 11 +++++++---- net/dsa/tag_trailer.c | 6 ++++-- net/dsa/tag_xrs700x.c | 6 ++++-- 18 files changed, 98 insertions(+), 51 deletions(-) diff --git a/net/dsa/dsa_priv.h b/net/dsa/dsa_priv.h index 383d224c8143..020386ff0db9 100644 --- a/net/dsa/dsa_priv.h +++ b/net/dsa/dsa_priv.h @@ -17,8 +17,12 @@ #define DSA_MAX_NUM_OFFLOADING_BRIDGES BITS_PER_LONG +/* Create 2 modaliases per tagging protocol, one to auto-load the module + * given the ID reported by get_tag_protocol(), and the other by name. + */ #define DSA_TAG_DRIVER_ALIAS "dsa_tag:" -#define MODULE_ALIAS_DSA_TAG_DRIVER(__proto) \ +#define MODULE_ALIAS_DSA_TAG_DRIVER(__proto, __name) \ + MODULE_ALIAS(DSA_TAG_DRIVER_ALIAS __name); \ MODULE_ALIAS(DSA_TAG_DRIVER_ALIAS "id-" \ __stringify(__proto##_VALUE)) diff --git a/net/dsa/tag_ar9331.c b/net/dsa/tag_ar9331.c index 8a02ac44282f..bfa161a4f502 100644 --- a/net/dsa/tag_ar9331.c +++ b/net/dsa/tag_ar9331.c @@ -9,6 +9,8 @@ #include "dsa_priv.h" +#define AR9331_NAME "ar9331" + #define AR9331_HDR_LEN 2 #define AR9331_HDR_VERSION 1 @@ -80,7 +82,7 @@ static struct sk_buff *ar9331_tag_rcv(struct sk_buff *skb, } static const struct dsa_device_ops ar9331_netdev_ops = { - .name = "ar9331", + .name = AR9331_NAME, .proto = DSA_TAG_PROTO_AR9331, .xmit = ar9331_tag_xmit, .rcv = ar9331_tag_rcv, @@ -88,5 +90,5 @@ static const struct dsa_device_ops ar9331_netdev_ops = { }; MODULE_LICENSE("GPL v2"); -MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_AR9331); +MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_AR9331, AR9331_NAME); module_dsa_tag_driver(ar9331_netdev_ops); diff --git a/net/dsa/tag_brcm.c b/net/dsa/tag_brcm.c index 16889ea3e0a7..9e7477ed70f1 100644 --- a/net/dsa/tag_brcm.c +++ b/net/dsa/tag_brcm.c @@ -12,6 +12,10 @@ #include "dsa_priv.h" +#define BRCM_NAME "brcm" +#define BRCM_LEGACY_NAME "brcm-legacy" +#define BRCM_PREPEND_NAME "brcm-prepend" + /* Legacy Broadcom tag (6 bytes) */ #define BRCM_LEG_TAG_LEN 6 @@ -196,7 +200,7 @@ static struct sk_buff *brcm_tag_rcv(struct sk_buff *skb, struct net_device *dev) } static const struct dsa_device_ops brcm_netdev_ops = { - .name = "brcm", + .name = BRCM_NAME, .proto = DSA_TAG_PROTO_BRCM, .xmit = brcm_tag_xmit, .rcv = brcm_tag_rcv, @@ -204,7 +208,7 @@ static const struct dsa_device_ops brcm_netdev_ops = { }; DSA_TAG_DRIVER(brcm_netdev_ops); -MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_BRCM); +MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_BRCM, BRCM_NAME); #endif #if IS_ENABLED(CONFIG_NET_DSA_TAG_BRCM_LEGACY) @@ -273,7 +277,7 @@ static struct sk_buff *brcm_leg_tag_rcv(struct sk_buff *skb, } static const struct dsa_device_ops brcm_legacy_netdev_ops = { - .name = "brcm-legacy", + .name = BRCM_LEGACY_NAME, .proto = DSA_TAG_PROTO_BRCM_LEGACY, .xmit = brcm_leg_tag_xmit, .rcv = brcm_leg_tag_rcv, @@ -281,7 +285,7 @@ static const struct dsa_device_ops brcm_legacy_netdev_ops = { }; DSA_TAG_DRIVER(brcm_legacy_netdev_ops); -MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_BRCM_LEGACY); +MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_BRCM_LEGACY, BRCM_LEGACY_NAME); #endif /* CONFIG_NET_DSA_TAG_BRCM_LEGACY */ #if IS_ENABLED(CONFIG_NET_DSA_TAG_BRCM_PREPEND) @@ -300,7 +304,7 @@ static struct sk_buff *brcm_tag_rcv_prepend(struct sk_buff *skb, } static const struct dsa_device_ops brcm_prepend_netdev_ops = { - .name = "brcm-prepend", + .name = BRCM_PREPEND_NAME, .proto = DSA_TAG_PROTO_BRCM_PREPEND, .xmit = brcm_tag_xmit_prepend, .rcv = brcm_tag_rcv_prepend, @@ -308,7 +312,7 @@ static const struct dsa_device_ops brcm_prepend_netdev_ops = { }; DSA_TAG_DRIVER(brcm_prepend_netdev_ops); -MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_BRCM_PREPEND); +MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_BRCM_PREPEND, BRCM_PREPEND_NAME); #endif static struct dsa_tag_driver *dsa_tag_driver_array[] = { diff --git a/net/dsa/tag_dsa.c b/net/dsa/tag_dsa.c index e4b6e3f2a3db..9fe77f5cc759 100644 --- a/net/dsa/tag_dsa.c +++ b/net/dsa/tag_dsa.c @@ -52,6 +52,9 @@ #include "dsa_priv.h" +#define DSA_NAME "dsa" +#define EDSA_NAME "edsa" + #define DSA_HLEN 4 /** @@ -339,7 +342,7 @@ static struct sk_buff *dsa_rcv(struct sk_buff *skb, struct net_device *dev) } static const struct dsa_device_ops dsa_netdev_ops = { - .name = "dsa", + .name = DSA_NAME, .proto = DSA_TAG_PROTO_DSA, .xmit = dsa_xmit, .rcv = dsa_rcv, @@ -347,7 +350,7 @@ static const struct dsa_device_ops dsa_netdev_ops = { }; DSA_TAG_DRIVER(dsa_netdev_ops); -MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_DSA); +MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_DSA, DSA_NAME); #endif /* CONFIG_NET_DSA_TAG_DSA */ #if IS_ENABLED(CONFIG_NET_DSA_TAG_EDSA) @@ -381,7 +384,7 @@ static struct sk_buff *edsa_rcv(struct sk_buff *skb, struct net_device *dev) } static const struct dsa_device_ops edsa_netdev_ops = { - .name = "edsa", + .name = EDSA_NAME, .proto = DSA_TAG_PROTO_EDSA, .xmit = edsa_xmit, .rcv = edsa_rcv, @@ -389,7 +392,7 @@ static const struct dsa_device_ops edsa_netdev_ops = { }; DSA_TAG_DRIVER(edsa_netdev_ops); -MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_EDSA); +MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_EDSA, EDSA_NAME); #endif /* CONFIG_NET_DSA_TAG_EDSA */ static struct dsa_tag_driver *dsa_tag_drivers[] = { diff --git a/net/dsa/tag_gswip.c b/net/dsa/tag_gswip.c index df7140984da3..020050dff3e4 100644 --- a/net/dsa/tag_gswip.c +++ b/net/dsa/tag_gswip.c @@ -12,6 +12,8 @@ #include "dsa_priv.h" +#define GSWIP_NAME "gswip" + #define GSWIP_TX_HEADER_LEN 4 /* special tag in TX path header */ @@ -98,7 +100,7 @@ static struct sk_buff *gswip_tag_rcv(struct sk_buff *skb, } static const struct dsa_device_ops gswip_netdev_ops = { - .name = "gswip", + .name = GSWIP_NAME, .proto = DSA_TAG_PROTO_GSWIP, .xmit = gswip_tag_xmit, .rcv = gswip_tag_rcv, @@ -106,6 +108,6 @@ static const struct dsa_device_ops gswip_netdev_ops = { }; MODULE_LICENSE("GPL"); -MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_GSWIP); +MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_GSWIP, GSWIP_NAME); module_dsa_tag_driver(gswip_netdev_ops); diff --git a/net/dsa/tag_hellcreek.c b/net/dsa/tag_hellcreek.c index 846588c0070a..03fd5f2877c8 100644 --- a/net/dsa/tag_hellcreek.c +++ b/net/dsa/tag_hellcreek.c @@ -13,6 +13,8 @@ #include "dsa_priv.h" +#define HELLCREEK_NAME "hellcreek" + #define HELLCREEK_TAG_LEN 1 static struct sk_buff *hellcreek_xmit(struct sk_buff *skb, @@ -57,7 +59,7 @@ static struct sk_buff *hellcreek_rcv(struct sk_buff *skb, } static const struct dsa_device_ops hellcreek_netdev_ops = { - .name = "hellcreek", + .name = HELLCREEK_NAME, .proto = DSA_TAG_PROTO_HELLCREEK, .xmit = hellcreek_xmit, .rcv = hellcreek_rcv, @@ -65,6 +67,6 @@ static const struct dsa_device_ops hellcreek_netdev_ops = { }; MODULE_LICENSE("Dual MIT/GPL"); -MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_HELLCREEK); +MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_HELLCREEK, HELLCREEK_NAME); module_dsa_tag_driver(hellcreek_netdev_ops); diff --git a/net/dsa/tag_ksz.c b/net/dsa/tag_ksz.c index 38fa19c1e2d5..37db5156f9a3 100644 --- a/net/dsa/tag_ksz.c +++ b/net/dsa/tag_ksz.c @@ -9,6 +9,11 @@ #include #include "dsa_priv.h" +#define KSZ8795_NAME "ksz8795" +#define KSZ9477_NAME "ksz9477" +#define KSZ9893_NAME "ksz9893" +#define LAN937X_NAME "lan937x" + /* Typically only one byte is used for tail tag. */ #define KSZ_EGRESS_TAG_LEN 1 #define KSZ_INGRESS_TAG_LEN 1 @@ -74,7 +79,7 @@ static struct sk_buff *ksz8795_rcv(struct sk_buff *skb, struct net_device *dev) } static const struct dsa_device_ops ksz8795_netdev_ops = { - .name = "ksz8795", + .name = KSZ8795_NAME, .proto = DSA_TAG_PROTO_KSZ8795, .xmit = ksz8795_xmit, .rcv = ksz8795_rcv, @@ -82,7 +87,7 @@ static const struct dsa_device_ops ksz8795_netdev_ops = { }; DSA_TAG_DRIVER(ksz8795_netdev_ops); -MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_KSZ8795); +MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_KSZ8795, KSZ8795_NAME); /* * For Ingress (Host -> KSZ9477), 2 bytes are added before FCS. @@ -147,7 +152,7 @@ static struct sk_buff *ksz9477_rcv(struct sk_buff *skb, struct net_device *dev) } static const struct dsa_device_ops ksz9477_netdev_ops = { - .name = "ksz9477", + .name = KSZ9477_NAME, .proto = DSA_TAG_PROTO_KSZ9477, .xmit = ksz9477_xmit, .rcv = ksz9477_rcv, @@ -155,7 +160,7 @@ static const struct dsa_device_ops ksz9477_netdev_ops = { }; DSA_TAG_DRIVER(ksz9477_netdev_ops); -MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_KSZ9477); +MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_KSZ9477, KSZ9477_NAME); #define KSZ9893_TAIL_TAG_OVERRIDE BIT(5) #define KSZ9893_TAIL_TAG_LOOKUP BIT(6) @@ -183,7 +188,7 @@ static struct sk_buff *ksz9893_xmit(struct sk_buff *skb, } static const struct dsa_device_ops ksz9893_netdev_ops = { - .name = "ksz9893", + .name = KSZ9893_NAME, .proto = DSA_TAG_PROTO_KSZ9893, .xmit = ksz9893_xmit, .rcv = ksz9477_rcv, @@ -191,7 +196,7 @@ static const struct dsa_device_ops ksz9893_netdev_ops = { }; DSA_TAG_DRIVER(ksz9893_netdev_ops); -MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_KSZ9893); +MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_KSZ9893, KSZ9893_NAME); /* For xmit, 2 bytes are added before FCS. * --------------------------------------------------------------------------- @@ -241,7 +246,7 @@ static struct sk_buff *lan937x_xmit(struct sk_buff *skb, } static const struct dsa_device_ops lan937x_netdev_ops = { - .name = "lan937x", + .name = LAN937X_NAME, .proto = DSA_TAG_PROTO_LAN937X, .xmit = lan937x_xmit, .rcv = ksz9477_rcv, @@ -249,7 +254,7 @@ static const struct dsa_device_ops lan937x_netdev_ops = { }; DSA_TAG_DRIVER(lan937x_netdev_ops); -MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_LAN937X); +MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_LAN937X, LAN937X_NAME); static struct dsa_tag_driver *dsa_tag_driver_array[] = { &DSA_TAG_DRIVER_NAME(ksz8795_netdev_ops), diff --git a/net/dsa/tag_lan9303.c b/net/dsa/tag_lan9303.c index 98d7d7120bab..4118292ed218 100644 --- a/net/dsa/tag_lan9303.c +++ b/net/dsa/tag_lan9303.c @@ -30,6 +30,8 @@ * Required when no forwarding between the external ports should happen. */ +#define LAN9303_NAME "lan9303" + #define LAN9303_TAG_LEN 4 # define LAN9303_TAG_TX_USE_ALR BIT(3) # define LAN9303_TAG_TX_STP_OVERRIDE BIT(4) @@ -110,7 +112,7 @@ static struct sk_buff *lan9303_rcv(struct sk_buff *skb, struct net_device *dev) } static const struct dsa_device_ops lan9303_netdev_ops = { - .name = "lan9303", + .name = LAN9303_NAME, .proto = DSA_TAG_PROTO_LAN9303, .xmit = lan9303_xmit, .rcv = lan9303_rcv, @@ -118,6 +120,6 @@ static const struct dsa_device_ops lan9303_netdev_ops = { }; MODULE_LICENSE("GPL"); -MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_LAN9303); +MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_LAN9303, LAN9303_NAME); module_dsa_tag_driver(lan9303_netdev_ops); diff --git a/net/dsa/tag_mtk.c b/net/dsa/tag_mtk.c index 415d8ece242a..ba37495ab5f4 100644 --- a/net/dsa/tag_mtk.c +++ b/net/dsa/tag_mtk.c @@ -10,6 +10,8 @@ #include "dsa_priv.h" +#define MTK_NAME "mtk" + #define MTK_HDR_LEN 4 #define MTK_HDR_XMIT_UNTAGGED 0 #define MTK_HDR_XMIT_TAGGED_TPID_8100 1 @@ -91,7 +93,7 @@ static struct sk_buff *mtk_tag_rcv(struct sk_buff *skb, struct net_device *dev) } static const struct dsa_device_ops mtk_netdev_ops = { - .name = "mtk", + .name = MTK_NAME, .proto = DSA_TAG_PROTO_MTK, .xmit = mtk_tag_xmit, .rcv = mtk_tag_rcv, @@ -99,6 +101,6 @@ static const struct dsa_device_ops mtk_netdev_ops = { }; MODULE_LICENSE("GPL"); -MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_MTK); +MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_MTK, MTK_NAME); module_dsa_tag_driver(mtk_netdev_ops); diff --git a/net/dsa/tag_ocelot.c b/net/dsa/tag_ocelot.c index 0d81f172b7a6..8cc31ab47e28 100644 --- a/net/dsa/tag_ocelot.c +++ b/net/dsa/tag_ocelot.c @@ -4,6 +4,9 @@ #include #include "dsa_priv.h" +#define OCELOT_NAME "ocelot" +#define SEVILLE_NAME "seville" + /* If the port is under a VLAN-aware bridge, remove the VLAN header from the * payload and move it into the DSA tag, which will make the switch classify * the packet to the bridge VLAN. Otherwise, leave the classified VLAN at zero, @@ -183,7 +186,7 @@ static struct sk_buff *ocelot_rcv(struct sk_buff *skb, } static const struct dsa_device_ops ocelot_netdev_ops = { - .name = "ocelot", + .name = OCELOT_NAME, .proto = DSA_TAG_PROTO_OCELOT, .xmit = ocelot_xmit, .rcv = ocelot_rcv, @@ -192,10 +195,10 @@ static const struct dsa_device_ops ocelot_netdev_ops = { }; DSA_TAG_DRIVER(ocelot_netdev_ops); -MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_OCELOT); +MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_OCELOT, OCELOT_NAME); static const struct dsa_device_ops seville_netdev_ops = { - .name = "seville", + .name = SEVILLE_NAME, .proto = DSA_TAG_PROTO_SEVILLE, .xmit = seville_xmit, .rcv = ocelot_rcv, @@ -204,7 +207,7 @@ static const struct dsa_device_ops seville_netdev_ops = { }; DSA_TAG_DRIVER(seville_netdev_ops); -MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_SEVILLE); +MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_SEVILLE, SEVILLE_NAME); static struct dsa_tag_driver *ocelot_tag_driver_array[] = { &DSA_TAG_DRIVER_NAME(ocelot_netdev_ops), diff --git a/net/dsa/tag_ocelot_8021q.c b/net/dsa/tag_ocelot_8021q.c index 37ccf00404ea..d1ec68001487 100644 --- a/net/dsa/tag_ocelot_8021q.c +++ b/net/dsa/tag_ocelot_8021q.c @@ -12,6 +12,8 @@ #include #include "dsa_priv.h" +#define OCELOT_8021Q_NAME "ocelot-8021q" + struct ocelot_8021q_tagger_private { struct ocelot_8021q_tagger_data data; /* Must be first */ struct kthread_worker *xmit_worker; @@ -119,7 +121,7 @@ static int ocelot_connect(struct dsa_switch *ds) } static const struct dsa_device_ops ocelot_8021q_netdev_ops = { - .name = "ocelot-8021q", + .name = OCELOT_8021Q_NAME, .proto = DSA_TAG_PROTO_OCELOT_8021Q, .xmit = ocelot_xmit, .rcv = ocelot_rcv, @@ -130,6 +132,6 @@ static const struct dsa_device_ops ocelot_8021q_netdev_ops = { }; MODULE_LICENSE("GPL v2"); -MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_OCELOT_8021Q); +MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_OCELOT_8021Q, OCELOT_8021Q_NAME); module_dsa_tag_driver(ocelot_8021q_netdev_ops); diff --git a/net/dsa/tag_qca.c b/net/dsa/tag_qca.c index 57d2e00f1e5d..73d6e111228d 100644 --- a/net/dsa/tag_qca.c +++ b/net/dsa/tag_qca.c @@ -10,6 +10,8 @@ #include "dsa_priv.h" +#define QCA_NAME "qca" + static struct sk_buff *qca_tag_xmit(struct sk_buff *skb, struct net_device *dev) { struct dsa_port *dp = dsa_slave_to_port(dev); @@ -107,7 +109,7 @@ static void qca_tag_disconnect(struct dsa_switch *ds) } static const struct dsa_device_ops qca_netdev_ops = { - .name = "qca", + .name = QCA_NAME, .proto = DSA_TAG_PROTO_QCA, .connect = qca_tag_connect, .disconnect = qca_tag_disconnect, @@ -118,6 +120,6 @@ static const struct dsa_device_ops qca_netdev_ops = { }; MODULE_LICENSE("GPL"); -MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_QCA); +MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_QCA, QCA_NAME); module_dsa_tag_driver(qca_netdev_ops); diff --git a/net/dsa/tag_rtl4_a.c b/net/dsa/tag_rtl4_a.c index 6d928ee3ef7a..18b52d77d200 100644 --- a/net/dsa/tag_rtl4_a.c +++ b/net/dsa/tag_rtl4_a.c @@ -20,6 +20,8 @@ #include "dsa_priv.h" +#define RTL4_A_NAME "rtl4a" + #define RTL4_A_HDR_LEN 4 #define RTL4_A_ETHERTYPE 0x8899 #define RTL4_A_PROTOCOL_SHIFT 12 @@ -112,7 +114,7 @@ static struct sk_buff *rtl4a_tag_rcv(struct sk_buff *skb, } static const struct dsa_device_ops rtl4a_netdev_ops = { - .name = "rtl4a", + .name = RTL4_A_NAME, .proto = DSA_TAG_PROTO_RTL4_A, .xmit = rtl4a_tag_xmit, .rcv = rtl4a_tag_rcv, @@ -121,4 +123,4 @@ static const struct dsa_device_ops rtl4a_netdev_ops = { module_dsa_tag_driver(rtl4a_netdev_ops); MODULE_LICENSE("GPL"); -MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_RTL4_A); +MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_RTL4_A, RTL4_A_NAME); diff --git a/net/dsa/tag_rtl8_4.c b/net/dsa/tag_rtl8_4.c index a593ead7ff26..030a8cf0ad48 100644 --- a/net/dsa/tag_rtl8_4.c +++ b/net/dsa/tag_rtl8_4.c @@ -84,6 +84,9 @@ * 0x04 = RTL8365MB DSA protocol */ +#define RTL8_4_NAME "rtl8_4" +#define RTL8_4T_NAME "rtl8_4t" + #define RTL8_4_TAG_LEN 8 #define RTL8_4_PROTOCOL GENMASK(15, 8) @@ -234,7 +237,7 @@ static const struct dsa_device_ops rtl8_4_netdev_ops = { DSA_TAG_DRIVER(rtl8_4_netdev_ops); -MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_RTL8_4); +MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_RTL8_4, RTL8_4_NAME); /* Tail version */ static const struct dsa_device_ops rtl8_4t_netdev_ops = { @@ -247,7 +250,7 @@ static const struct dsa_device_ops rtl8_4t_netdev_ops = { DSA_TAG_DRIVER(rtl8_4t_netdev_ops); -MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_RTL8_4T); +MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_RTL8_4T, RTL8_4T_NAME); static struct dsa_tag_driver *dsa_tag_drivers[] = { &DSA_TAG_DRIVER_NAME(rtl8_4_netdev_ops), diff --git a/net/dsa/tag_rzn1_a5psw.c b/net/dsa/tag_rzn1_a5psw.c index e2a5ee6ae688..b9135069f9fc 100644 --- a/net/dsa/tag_rzn1_a5psw.c +++ b/net/dsa/tag_rzn1_a5psw.c @@ -22,6 +22,8 @@ * See struct a5psw_tag for layout */ +#define A5PSW_NAME "a5psw" + #define ETH_P_DSA_A5PSW 0xE001 #define A5PSW_TAG_LEN 8 #define A5PSW_CTRL_DATA_FORCE_FORWARD BIT(0) @@ -101,7 +103,7 @@ static struct sk_buff *a5psw_tag_rcv(struct sk_buff *skb, } static const struct dsa_device_ops a5psw_netdev_ops = { - .name = "a5psw", + .name = A5PSW_NAME, .proto = DSA_TAG_PROTO_RZN1_A5PSW, .xmit = a5psw_tag_xmit, .rcv = a5psw_tag_rcv, @@ -109,5 +111,5 @@ static const struct dsa_device_ops a5psw_netdev_ops = { }; MODULE_LICENSE("GPL v2"); -MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_A5PSW); +MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_A5PSW, A5PSW_NAME); module_dsa_tag_driver(a5psw_netdev_ops); diff --git a/net/dsa/tag_sja1105.c b/net/dsa/tag_sja1105.c index 83e4136516b0..3b6e642a90e9 100644 --- a/net/dsa/tag_sja1105.c +++ b/net/dsa/tag_sja1105.c @@ -7,6 +7,9 @@ #include #include "dsa_priv.h" +#define SJA1105_NAME "sja1105" +#define SJA1110_NAME "sja1110" + /* Is this a TX or an RX header? */ #define SJA1110_HEADER_HOST_TO_SWITCH BIT(15) @@ -786,7 +789,7 @@ static int sja1105_connect(struct dsa_switch *ds) } static const struct dsa_device_ops sja1105_netdev_ops = { - .name = "sja1105", + .name = SJA1105_NAME, .proto = DSA_TAG_PROTO_SJA1105, .xmit = sja1105_xmit, .rcv = sja1105_rcv, @@ -798,10 +801,10 @@ static const struct dsa_device_ops sja1105_netdev_ops = { }; DSA_TAG_DRIVER(sja1105_netdev_ops); -MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_SJA1105); +MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_SJA1105, SJA1105_NAME); static const struct dsa_device_ops sja1110_netdev_ops = { - .name = "sja1110", + .name = SJA1110_NAME, .proto = DSA_TAG_PROTO_SJA1110, .xmit = sja1110_xmit, .rcv = sja1110_rcv, @@ -813,7 +816,7 @@ static const struct dsa_device_ops sja1110_netdev_ops = { }; DSA_TAG_DRIVER(sja1110_netdev_ops); -MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_SJA1110); +MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_SJA1110, SJA1110_NAME); static struct dsa_tag_driver *sja1105_tag_driver_array[] = { &DSA_TAG_DRIVER_NAME(sja1105_netdev_ops), diff --git a/net/dsa/tag_trailer.c b/net/dsa/tag_trailer.c index 5749ba85c2b8..8754dfe680f6 100644 --- a/net/dsa/tag_trailer.c +++ b/net/dsa/tag_trailer.c @@ -10,6 +10,8 @@ #include "dsa_priv.h" +#define TRAILER_NAME "trailer" + static struct sk_buff *trailer_xmit(struct sk_buff *skb, struct net_device *dev) { struct dsa_port *dp = dsa_slave_to_port(dev); @@ -50,7 +52,7 @@ static struct sk_buff *trailer_rcv(struct sk_buff *skb, struct net_device *dev) } static const struct dsa_device_ops trailer_netdev_ops = { - .name = "trailer", + .name = TRAILER_NAME, .proto = DSA_TAG_PROTO_TRAILER, .xmit = trailer_xmit, .rcv = trailer_rcv, @@ -58,6 +60,6 @@ static const struct dsa_device_ops trailer_netdev_ops = { }; MODULE_LICENSE("GPL"); -MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_TRAILER); +MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_TRAILER, TRAILER_NAME); module_dsa_tag_driver(trailer_netdev_ops); diff --git a/net/dsa/tag_xrs700x.c b/net/dsa/tag_xrs700x.c index ff442b8af636..dc935dd90f98 100644 --- a/net/dsa/tag_xrs700x.c +++ b/net/dsa/tag_xrs700x.c @@ -9,6 +9,8 @@ #include "dsa_priv.h" +#define XRS700X_NAME "xrs700x" + static struct sk_buff *xrs700x_xmit(struct sk_buff *skb, struct net_device *dev) { struct dsa_port *partner, *dp = dsa_slave_to_port(dev); @@ -51,7 +53,7 @@ static struct sk_buff *xrs700x_rcv(struct sk_buff *skb, struct net_device *dev) } static const struct dsa_device_ops xrs700x_netdev_ops = { - .name = "xrs700x", + .name = XRS700X_NAME, .proto = DSA_TAG_PROTO_XRS700X, .xmit = xrs700x_xmit, .rcv = xrs700x_rcv, @@ -59,6 +61,6 @@ static const struct dsa_device_ops xrs700x_netdev_ops = { }; MODULE_LICENSE("GPL"); -MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_XRS700X); +MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_XRS700X, XRS700X_NAME); module_dsa_tag_driver(xrs700x_netdev_ops); From e8666130b995a6a1a99c319d33fae2046213c39b Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Tue, 15 Nov 2022 03:18:45 +0200 Subject: [PATCH 1036/2002] net: dsa: strip sysfs "tagging" string of trailing newline Currently, dsa_find_tagger_by_name() uses sysfs_streq() which works both with strings that contain \n at the end (echo ocelot > .../dsa/tagging) and with strings that don't (printf ocelot > .../dsa/tagging). There will be a problem once we'll want to construct the modalias string based on which we auto-load the protocol kernel module. If the sysfs buffer ends in a newline, we need to strip it first. This is a preparatory patch specifically for that. Signed-off-by: Vladimir Oltean Tested-by: Michael Walle Signed-off-by: Jakub Kicinski --- net/dsa/dsa.c | 4 ++-- net/dsa/dsa_priv.h | 2 +- net/dsa/master.c | 13 ++++++++++++- 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/net/dsa/dsa.c b/net/dsa/dsa.c index cc6a497a0e50..f74ddda670a8 100644 --- a/net/dsa/dsa.c +++ b/net/dsa/dsa.c @@ -79,7 +79,7 @@ const char *dsa_tag_protocol_to_str(const struct dsa_device_ops *ops) /* Function takes a reference on the module owning the tagger, * so dsa_tag_driver_put must be called afterwards. */ -const struct dsa_device_ops *dsa_find_tagger_by_name(const char *buf) +const struct dsa_device_ops *dsa_find_tagger_by_name(const char *name) { const struct dsa_device_ops *ops = ERR_PTR(-ENOPROTOOPT); struct dsa_tag_driver *dsa_tag_driver; @@ -88,7 +88,7 @@ const struct dsa_device_ops *dsa_find_tagger_by_name(const char *buf) list_for_each_entry(dsa_tag_driver, &dsa_tag_drivers_list, list) { const struct dsa_device_ops *tmp = dsa_tag_driver->ops; - if (!sysfs_streq(buf, tmp->name)) + if (strcmp(name, tmp->name)) continue; if (!try_module_get(dsa_tag_driver->owner)) diff --git a/net/dsa/dsa_priv.h b/net/dsa/dsa_priv.h index 020386ff0db9..4559c0ee39d0 100644 --- a/net/dsa/dsa_priv.h +++ b/net/dsa/dsa_priv.h @@ -245,7 +245,7 @@ struct dsa_slave_priv { /* dsa.c */ const struct dsa_device_ops *dsa_tag_driver_get(int tag_protocol); void dsa_tag_driver_put(const struct dsa_device_ops *ops); -const struct dsa_device_ops *dsa_find_tagger_by_name(const char *buf); +const struct dsa_device_ops *dsa_find_tagger_by_name(const char *name); bool dsa_db_equal(const struct dsa_db *a, const struct dsa_db *b); diff --git a/net/dsa/master.c b/net/dsa/master.c index 421de166515f..f443bf4a3c8c 100644 --- a/net/dsa/master.c +++ b/net/dsa/master.c @@ -299,12 +299,23 @@ static ssize_t tagging_store(struct device *d, struct device_attribute *attr, const char *buf, size_t count) { const struct dsa_device_ops *new_tag_ops, *old_tag_ops; + const char *end = strchrnul(buf, '\n'), *name; struct net_device *dev = to_net_dev(d); struct dsa_port *cpu_dp = dev->dsa_ptr; + size_t len = end - buf; int err; + /* Empty string passed */ + if (!len) + return -ENOPROTOOPT; + + name = kstrndup(buf, len, GFP_KERNEL); + if (!name) + return -ENOMEM; + old_tag_ops = cpu_dp->tag_ops; - new_tag_ops = dsa_find_tagger_by_name(buf); + new_tag_ops = dsa_find_tagger_by_name(name); + kfree(name); /* Bad tagger name, or module is not loaded? */ if (IS_ERR(new_tag_ops)) return PTR_ERR(new_tag_ops); From 54c087e83945eda014064f78c4807c5da1e09842 Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Tue, 15 Nov 2022 03:18:46 +0200 Subject: [PATCH 1037/2002] net: dsa: rename dsa_tag_driver_get() to dsa_tag_driver_get_by_id() A future patch will introduce one more way of getting a reference on a tagging protocl driver (by name). Rename the current method to "by_id". Signed-off-by: Vladimir Oltean Tested-by: Michael Walle Signed-off-by: Jakub Kicinski --- net/dsa/dsa.c | 2 +- net/dsa/dsa2.c | 2 +- net/dsa/dsa_priv.h | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/net/dsa/dsa.c b/net/dsa/dsa.c index f74ddda670a8..577765cfa986 100644 --- a/net/dsa/dsa.c +++ b/net/dsa/dsa.c @@ -102,7 +102,7 @@ const struct dsa_device_ops *dsa_find_tagger_by_name(const char *name) return ops; } -const struct dsa_device_ops *dsa_tag_driver_get(int tag_protocol) +const struct dsa_device_ops *dsa_tag_driver_get_by_id(int tag_protocol) { struct dsa_tag_driver *dsa_tag_driver; const struct dsa_device_ops *ops; diff --git a/net/dsa/dsa2.c b/net/dsa/dsa2.c index d3055b49080f..4b31b0258870 100644 --- a/net/dsa/dsa2.c +++ b/net/dsa/dsa2.c @@ -1441,7 +1441,7 @@ static int dsa_port_parse_cpu(struct dsa_port *dp, struct net_device *master, } if (!tag_ops) - tag_ops = dsa_tag_driver_get(default_proto); + tag_ops = dsa_tag_driver_get_by_id(default_proto); if (IS_ERR(tag_ops)) { if (PTR_ERR(tag_ops) == -ENOPROTOOPT) diff --git a/net/dsa/dsa_priv.h b/net/dsa/dsa_priv.h index 4559c0ee39d0..0d33a22e3087 100644 --- a/net/dsa/dsa_priv.h +++ b/net/dsa/dsa_priv.h @@ -243,7 +243,7 @@ struct dsa_slave_priv { }; /* dsa.c */ -const struct dsa_device_ops *dsa_tag_driver_get(int tag_protocol); +const struct dsa_device_ops *dsa_tag_driver_get_by_id(int tag_protocol); void dsa_tag_driver_put(const struct dsa_device_ops *ops); const struct dsa_device_ops *dsa_find_tagger_by_name(const char *name); From 0184c07a11a243569cb90104980237c8e101f363 Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Tue, 15 Nov 2022 03:18:47 +0200 Subject: [PATCH 1038/2002] net: dsa: autoload tag driver module on tagging protocol change Issue a request_module() call when an attempt to change the tagging protocol is made, either by sysfs or by device tree. In the case of ocelot (the only driver for which the default and the alternative tagging protocol are compiled as different modules), the user is now no longer required to insert tag_ocelot_8021q.ko manually. In the particular case of ocelot, this solves a problem where tag_ocelot_8021q.ko is built as module, and this is present in the device tree: &mscc_felix_port4 { dsa-tag-protocol = "ocelot-8021q"; }; &mscc_felix_port5 { dsa-tag-protocol = "ocelot-8021q"; }; Because no one attempts to load the module into the kernel at boot time, the switch driver will fail to probe (actually forever defer) until someone manually inserts tag_ocelot_8021q.ko. This is now no longer necessary and happens automatically. Rename dsa_find_tagger_by_name() to denote the change in functionality: there is now feature parity with dsa_tag_driver_get_by_id(), i.o.w. we also load the module if it's missing. Link: https://lore.kernel.org/lkml/20221027113248.420216-1-michael@walle.cc/ Suggested-by: Michael Walle Signed-off-by: Vladimir Oltean Tested-by: Michael Walle # on kontron-sl28 w/ ocelot_8021q Tested-by: Michael Walle Signed-off-by: Jakub Kicinski --- net/dsa/dsa.c | 4 +++- net/dsa/dsa2.c | 2 +- net/dsa/dsa_priv.h | 2 +- net/dsa/master.c | 4 ++-- 4 files changed, 7 insertions(+), 5 deletions(-) diff --git a/net/dsa/dsa.c b/net/dsa/dsa.c index 577765cfa986..4afd3edbd64d 100644 --- a/net/dsa/dsa.c +++ b/net/dsa/dsa.c @@ -79,11 +79,13 @@ const char *dsa_tag_protocol_to_str(const struct dsa_device_ops *ops) /* Function takes a reference on the module owning the tagger, * so dsa_tag_driver_put must be called afterwards. */ -const struct dsa_device_ops *dsa_find_tagger_by_name(const char *name) +const struct dsa_device_ops *dsa_tag_driver_get_by_name(const char *name) { const struct dsa_device_ops *ops = ERR_PTR(-ENOPROTOOPT); struct dsa_tag_driver *dsa_tag_driver; + request_module("%s%s", DSA_TAG_DRIVER_ALIAS, name); + mutex_lock(&dsa_tag_drivers_lock); list_for_each_entry(dsa_tag_driver, &dsa_tag_drivers_list, list) { const struct dsa_device_ops *tmp = dsa_tag_driver->ops; diff --git a/net/dsa/dsa2.c b/net/dsa/dsa2.c index 4b31b0258870..f8df55e2e23a 100644 --- a/net/dsa/dsa2.c +++ b/net/dsa/dsa2.c @@ -1431,7 +1431,7 @@ static int dsa_port_parse_cpu(struct dsa_port *dp, struct net_device *master, return -EINVAL; } - tag_ops = dsa_find_tagger_by_name(user_protocol); + tag_ops = dsa_tag_driver_get_by_name(user_protocol); if (IS_ERR(tag_ops)) { dev_warn(ds->dev, "Failed to find a tagging driver for protocol %s, using default\n", diff --git a/net/dsa/dsa_priv.h b/net/dsa/dsa_priv.h index 0d33a22e3087..24e0ea218a35 100644 --- a/net/dsa/dsa_priv.h +++ b/net/dsa/dsa_priv.h @@ -244,8 +244,8 @@ struct dsa_slave_priv { /* dsa.c */ const struct dsa_device_ops *dsa_tag_driver_get_by_id(int tag_protocol); +const struct dsa_device_ops *dsa_tag_driver_get_by_name(const char *name); void dsa_tag_driver_put(const struct dsa_device_ops *ops); -const struct dsa_device_ops *dsa_find_tagger_by_name(const char *name); bool dsa_db_equal(const struct dsa_db *a, const struct dsa_db *b); diff --git a/net/dsa/master.c b/net/dsa/master.c index f443bf4a3c8c..e24f02743c21 100644 --- a/net/dsa/master.c +++ b/net/dsa/master.c @@ -314,9 +314,9 @@ static ssize_t tagging_store(struct device *d, struct device_attribute *attr, return -ENOMEM; old_tag_ops = cpu_dp->tag_ops; - new_tag_ops = dsa_find_tagger_by_name(name); + new_tag_ops = dsa_tag_driver_get_by_name(name); kfree(name); - /* Bad tagger name, or module is not loaded? */ + /* Bad tagger name? */ if (IS_ERR(new_tag_ops)) return PTR_ERR(new_tag_ops); From cd502236835b678738810ecd501c85a3a7a11150 Mon Sep 17 00:00:00 2001 From: Michal Wilczynski Date: Tue, 15 Nov 2022 11:48:15 +0100 Subject: [PATCH 1039/2002] devlink: Introduce new attribute 'tx_priority' to devlink-rate To fully utilize offload capabilities of Intel 100G card QoS capabilities new attribute 'tx_priority' needs to be introduced. This attribute allows for usage of strict priority arbiter among siblings. This arbitration scheme attempts to schedule nodes based on their priority as long as the nodes remain within their bandwidth limit. Introduce new attribute in devlink-rate that will allow for configuration of strict priority. New attribute is optional. Signed-off-by: Michal Wilczynski Reviewed-by: Jiri Pirko Signed-off-by: Jakub Kicinski --- include/net/devlink.h | 6 ++++++ include/uapi/linux/devlink.h | 1 + net/core/devlink.c | 31 +++++++++++++++++++++++++++++++ 3 files changed, 38 insertions(+) diff --git a/include/net/devlink.h b/include/net/devlink.h index 611a23a3deb2..90d59d673cb1 100644 --- a/include/net/devlink.h +++ b/include/net/devlink.h @@ -114,6 +114,8 @@ struct devlink_rate { refcount_t refcnt; }; }; + + u32 tx_priority; }; struct devlink_port { @@ -1511,10 +1513,14 @@ struct devlink_ops { u64 tx_share, struct netlink_ext_ack *extack); int (*rate_leaf_tx_max_set)(struct devlink_rate *devlink_rate, void *priv, u64 tx_max, struct netlink_ext_ack *extack); + int (*rate_leaf_tx_priority_set)(struct devlink_rate *devlink_rate, void *priv, + u32 tx_priority, struct netlink_ext_ack *extack); int (*rate_node_tx_share_set)(struct devlink_rate *devlink_rate, void *priv, u64 tx_share, struct netlink_ext_ack *extack); int (*rate_node_tx_max_set)(struct devlink_rate *devlink_rate, void *priv, u64 tx_max, struct netlink_ext_ack *extack); + int (*rate_node_tx_priority_set)(struct devlink_rate *devlink_rate, void *priv, + u32 tx_priority, struct netlink_ext_ack *extack); int (*rate_node_new)(struct devlink_rate *rate_node, void **priv, struct netlink_ext_ack *extack); int (*rate_node_del)(struct devlink_rate *rate_node, void *priv, diff --git a/include/uapi/linux/devlink.h b/include/uapi/linux/devlink.h index 2f24b53a87a5..1a9214d35ef5 100644 --- a/include/uapi/linux/devlink.h +++ b/include/uapi/linux/devlink.h @@ -607,6 +607,7 @@ enum devlink_attr { DEVLINK_ATTR_SELFTESTS, /* nested */ + DEVLINK_ATTR_RATE_TX_PRIORITY, /* u32 */ /* add new attributes above here, update the policy in devlink.c */ __DEVLINK_ATTR_MAX, diff --git a/net/core/devlink.c b/net/core/devlink.c index 7f789bbcbbd7..bf6d3a3c28bb 100644 --- a/net/core/devlink.c +++ b/net/core/devlink.c @@ -1203,6 +1203,9 @@ static int devlink_nl_rate_fill(struct sk_buff *msg, devlink_rate->tx_max, DEVLINK_ATTR_PAD)) goto nla_put_failure; + if (nla_put_u32(msg, DEVLINK_ATTR_RATE_TX_PRIORITY, + devlink_rate->tx_priority)) + goto nla_put_failure; if (devlink_rate->parent) if (nla_put_string(msg, DEVLINK_ATTR_RATE_PARENT_NODE_NAME, devlink_rate->parent->name)) @@ -1936,6 +1939,7 @@ static int devlink_nl_rate_set(struct devlink_rate *devlink_rate, { struct nlattr *nla_parent, **attrs = info->attrs; int err = -EOPNOTSUPP; + u32 priority; u64 rate; if (attrs[DEVLINK_ATTR_RATE_TX_SHARE]) { @@ -1964,6 +1968,20 @@ static int devlink_nl_rate_set(struct devlink_rate *devlink_rate, devlink_rate->tx_max = rate; } + if (attrs[DEVLINK_ATTR_RATE_TX_PRIORITY]) { + priority = nla_get_u32(attrs[DEVLINK_ATTR_RATE_TX_PRIORITY]); + if (devlink_rate_is_leaf(devlink_rate)) + err = ops->rate_leaf_tx_priority_set(devlink_rate, devlink_rate->priv, + priority, info->extack); + else if (devlink_rate_is_node(devlink_rate)) + err = ops->rate_node_tx_priority_set(devlink_rate, devlink_rate->priv, + priority, info->extack); + + if (err) + return err; + devlink_rate->tx_priority = priority; + } + nla_parent = attrs[DEVLINK_ATTR_RATE_PARENT_NODE_NAME]; if (nla_parent) { err = devlink_nl_rate_parent_node_set(devlink_rate, info, @@ -1995,6 +2013,12 @@ static bool devlink_rate_set_ops_supported(const struct devlink_ops *ops, NL_SET_ERR_MSG_MOD(info->extack, "Parent set isn't supported for the leafs"); return false; } + if (attrs[DEVLINK_ATTR_RATE_TX_PRIORITY] && !ops->rate_leaf_tx_priority_set) { + NL_SET_ERR_MSG_ATTR(info->extack, + attrs[DEVLINK_ATTR_RATE_TX_PRIORITY], + "TX priority set isn't supported for the leafs"); + return false; + } } else if (type == DEVLINK_RATE_TYPE_NODE) { if (attrs[DEVLINK_ATTR_RATE_TX_SHARE] && !ops->rate_node_tx_share_set) { NL_SET_ERR_MSG_MOD(info->extack, "TX share set isn't supported for the nodes"); @@ -2009,6 +2033,12 @@ static bool devlink_rate_set_ops_supported(const struct devlink_ops *ops, NL_SET_ERR_MSG_MOD(info->extack, "Parent set isn't supported for the nodes"); return false; } + if (attrs[DEVLINK_ATTR_RATE_TX_PRIORITY] && !ops->rate_node_tx_priority_set) { + NL_SET_ERR_MSG_ATTR(info->extack, + attrs[DEVLINK_ATTR_RATE_TX_PRIORITY], + "TX priority set isn't supported for the nodes"); + return false; + } } else { WARN(1, "Unknown type of rate object"); return false; @@ -9187,6 +9217,7 @@ static const struct nla_policy devlink_nl_policy[DEVLINK_ATTR_MAX + 1] = { [DEVLINK_ATTR_LINECARD_INDEX] = { .type = NLA_U32 }, [DEVLINK_ATTR_LINECARD_TYPE] = { .type = NLA_NUL_STRING }, [DEVLINK_ATTR_SELFTESTS] = { .type = NLA_NESTED }, + [DEVLINK_ATTR_RATE_TX_PRIORITY] = { .type = NLA_U32 }, }; static const struct genl_small_ops devlink_nl_ops[] = { From 6e2d7e84fcfee62d70e62aa9e469d10b8b6a7dc7 Mon Sep 17 00:00:00 2001 From: Michal Wilczynski Date: Tue, 15 Nov 2022 11:48:16 +0100 Subject: [PATCH 1040/2002] devlink: Introduce new attribute 'tx_weight' to devlink-rate To fully utilize offload capabilities of Intel 100G card QoS capabilities new attribute 'tx_weight' needs to be introduced. This attribute allows for usage of Weighted Fair Queuing arbitration scheme among siblings. This arbitration scheme can be used simultaneously with the strict priority. Introduce new attribute in devlink-rate that will allow for configuration of Weighted Fair Queueing. New attribute is optional. Signed-off-by: Michal Wilczynski Reviewed-by: Jiri Pirko Signed-off-by: Jakub Kicinski --- include/net/devlink.h | 5 +++++ include/uapi/linux/devlink.h | 2 ++ net/core/devlink.c | 33 +++++++++++++++++++++++++++++++++ 3 files changed, 40 insertions(+) diff --git a/include/net/devlink.h b/include/net/devlink.h index 90d59d673cb1..366b23d3f973 100644 --- a/include/net/devlink.h +++ b/include/net/devlink.h @@ -116,6 +116,7 @@ struct devlink_rate { }; u32 tx_priority; + u32 tx_weight; }; struct devlink_port { @@ -1515,12 +1516,16 @@ struct devlink_ops { u64 tx_max, struct netlink_ext_ack *extack); int (*rate_leaf_tx_priority_set)(struct devlink_rate *devlink_rate, void *priv, u32 tx_priority, struct netlink_ext_ack *extack); + int (*rate_leaf_tx_weight_set)(struct devlink_rate *devlink_rate, void *priv, + u32 tx_weight, struct netlink_ext_ack *extack); int (*rate_node_tx_share_set)(struct devlink_rate *devlink_rate, void *priv, u64 tx_share, struct netlink_ext_ack *extack); int (*rate_node_tx_max_set)(struct devlink_rate *devlink_rate, void *priv, u64 tx_max, struct netlink_ext_ack *extack); int (*rate_node_tx_priority_set)(struct devlink_rate *devlink_rate, void *priv, u32 tx_priority, struct netlink_ext_ack *extack); + int (*rate_node_tx_weight_set)(struct devlink_rate *devlink_rate, void *priv, + u32 tx_weight, struct netlink_ext_ack *extack); int (*rate_node_new)(struct devlink_rate *rate_node, void **priv, struct netlink_ext_ack *extack); int (*rate_node_del)(struct devlink_rate *rate_node, void *priv, diff --git a/include/uapi/linux/devlink.h b/include/uapi/linux/devlink.h index 1a9214d35ef5..498d0d5d0957 100644 --- a/include/uapi/linux/devlink.h +++ b/include/uapi/linux/devlink.h @@ -608,6 +608,8 @@ enum devlink_attr { DEVLINK_ATTR_SELFTESTS, /* nested */ DEVLINK_ATTR_RATE_TX_PRIORITY, /* u32 */ + DEVLINK_ATTR_RATE_TX_WEIGHT, /* u32 */ + /* add new attributes above here, update the policy in devlink.c */ __DEVLINK_ATTR_MAX, diff --git a/net/core/devlink.c b/net/core/devlink.c index bf6d3a3c28bb..525bdf426163 100644 --- a/net/core/devlink.c +++ b/net/core/devlink.c @@ -1206,6 +1206,11 @@ static int devlink_nl_rate_fill(struct sk_buff *msg, if (nla_put_u32(msg, DEVLINK_ATTR_RATE_TX_PRIORITY, devlink_rate->tx_priority)) goto nla_put_failure; + + if (nla_put_u32(msg, DEVLINK_ATTR_RATE_TX_WEIGHT, + devlink_rate->tx_weight)) + goto nla_put_failure; + if (devlink_rate->parent) if (nla_put_string(msg, DEVLINK_ATTR_RATE_PARENT_NODE_NAME, devlink_rate->parent->name)) @@ -1940,6 +1945,7 @@ static int devlink_nl_rate_set(struct devlink_rate *devlink_rate, struct nlattr *nla_parent, **attrs = info->attrs; int err = -EOPNOTSUPP; u32 priority; + u32 weight; u64 rate; if (attrs[DEVLINK_ATTR_RATE_TX_SHARE]) { @@ -1982,6 +1988,20 @@ static int devlink_nl_rate_set(struct devlink_rate *devlink_rate, devlink_rate->tx_priority = priority; } + if (attrs[DEVLINK_ATTR_RATE_TX_WEIGHT]) { + weight = nla_get_u32(attrs[DEVLINK_ATTR_RATE_TX_WEIGHT]); + if (devlink_rate_is_leaf(devlink_rate)) + err = ops->rate_leaf_tx_weight_set(devlink_rate, devlink_rate->priv, + weight, info->extack); + else if (devlink_rate_is_node(devlink_rate)) + err = ops->rate_node_tx_weight_set(devlink_rate, devlink_rate->priv, + weight, info->extack); + + if (err) + return err; + devlink_rate->tx_weight = weight; + } + nla_parent = attrs[DEVLINK_ATTR_RATE_PARENT_NODE_NAME]; if (nla_parent) { err = devlink_nl_rate_parent_node_set(devlink_rate, info, @@ -2019,6 +2039,12 @@ static bool devlink_rate_set_ops_supported(const struct devlink_ops *ops, "TX priority set isn't supported for the leafs"); return false; } + if (attrs[DEVLINK_ATTR_RATE_TX_WEIGHT] && !ops->rate_leaf_tx_weight_set) { + NL_SET_ERR_MSG_ATTR(info->extack, + attrs[DEVLINK_ATTR_RATE_TX_WEIGHT], + "TX weight set isn't supported for the leafs"); + return false; + } } else if (type == DEVLINK_RATE_TYPE_NODE) { if (attrs[DEVLINK_ATTR_RATE_TX_SHARE] && !ops->rate_node_tx_share_set) { NL_SET_ERR_MSG_MOD(info->extack, "TX share set isn't supported for the nodes"); @@ -2039,6 +2065,12 @@ static bool devlink_rate_set_ops_supported(const struct devlink_ops *ops, "TX priority set isn't supported for the nodes"); return false; } + if (attrs[DEVLINK_ATTR_RATE_TX_WEIGHT] && !ops->rate_node_tx_weight_set) { + NL_SET_ERR_MSG_ATTR(info->extack, + attrs[DEVLINK_ATTR_RATE_TX_WEIGHT], + "TX weight set isn't supported for the nodes"); + return false; + } } else { WARN(1, "Unknown type of rate object"); return false; @@ -9218,6 +9250,7 @@ static const struct nla_policy devlink_nl_policy[DEVLINK_ATTR_MAX + 1] = { [DEVLINK_ATTR_LINECARD_TYPE] = { .type = NLA_NUL_STRING }, [DEVLINK_ATTR_SELFTESTS] = { .type = NLA_NESTED }, [DEVLINK_ATTR_RATE_TX_PRIORITY] = { .type = NLA_U32 }, + [DEVLINK_ATTR_RATE_TX_WEIGHT] = { .type = NLA_U32 }, }; static const struct genl_small_ops devlink_nl_ops[] = { From caba177d7f4d7693a9157ece8c9a30944c949e34 Mon Sep 17 00:00:00 2001 From: Michal Wilczynski Date: Tue, 15 Nov 2022 11:48:17 +0100 Subject: [PATCH 1041/2002] devlink: Enable creation of the devlink-rate nodes from the driver Intel 100G card internal firmware hierarchy for Hierarchicial QoS is very rigid and can't be easily removed. This requires an ability to export default hierarchy to allow user to modify it. Currently the driver is only able to create the 'leaf' nodes, which usually represent the vport. This is not enough for HQoS implemented in Intel hardware. Introduce new function devl_rate_node_create() that allows for creation of the devlink-rate nodes from the driver. Signed-off-by: Michal Wilczynski Reviewed-by: Jiri Pirko Signed-off-by: Jakub Kicinski --- include/net/devlink.h | 3 +++ net/core/devlink.c | 45 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+) diff --git a/include/net/devlink.h b/include/net/devlink.h index 366b23d3f973..339a2ed02d36 100644 --- a/include/net/devlink.h +++ b/include/net/devlink.h @@ -1618,6 +1618,9 @@ void devlink_port_attrs_pci_sf_set(struct devlink_port *devlink_port, u32 controller, u16 pf, u32 sf, bool external); int devl_rate_leaf_create(struct devlink_port *port, void *priv); +struct devlink_rate * +devl_rate_node_create(struct devlink *devlink, void *priv, char *node_name, + struct devlink_rate *parent); void devl_rate_leaf_destroy(struct devlink_port *devlink_port); void devl_rate_nodes_destroy(struct devlink *devlink); void devlink_port_linecard_set(struct devlink_port *devlink_port, diff --git a/net/core/devlink.c b/net/core/devlink.c index 525bdf426163..3dfee7cd9929 100644 --- a/net/core/devlink.c +++ b/net/core/devlink.c @@ -10384,6 +10384,51 @@ void devlink_port_attrs_pci_sf_set(struct devlink_port *devlink_port, u32 contro } EXPORT_SYMBOL_GPL(devlink_port_attrs_pci_sf_set); +/** + * devl_rate_node_create - create devlink rate node + * @devlink: devlink instance + * @priv: driver private data + * @node_name: name of the resulting node + * @parent: parent devlink_rate struct + * + * Create devlink rate object of type node + */ +struct devlink_rate * +devl_rate_node_create(struct devlink *devlink, void *priv, char *node_name, + struct devlink_rate *parent) +{ + struct devlink_rate *rate_node; + + rate_node = devlink_rate_node_get_by_name(devlink, node_name); + if (!IS_ERR(rate_node)) + return ERR_PTR(-EEXIST); + + rate_node = kzalloc(sizeof(*rate_node), GFP_KERNEL); + if (!rate_node) + return ERR_PTR(-ENOMEM); + + if (parent) { + rate_node->parent = parent; + refcount_inc(&rate_node->parent->refcnt); + } + + rate_node->type = DEVLINK_RATE_TYPE_NODE; + rate_node->devlink = devlink; + rate_node->priv = priv; + + rate_node->name = kstrdup(node_name, GFP_KERNEL); + if (!rate_node->name) { + kfree(rate_node); + return ERR_PTR(-ENOMEM); + } + + refcount_set(&rate_node->refcnt, 1); + list_add(&rate_node->list, &devlink->rate_list); + devlink_rate_notify(rate_node, DEVLINK_CMD_RATE_NEW); + return rate_node; +} +EXPORT_SYMBOL_GPL(devl_rate_node_create); + /** * devl_rate_leaf_create - create devlink rate leaf * @devlink_port: devlink port object to create rate object on From 04d674f04e32d4109f0ee505a8855a384481212d Mon Sep 17 00:00:00 2001 From: Michal Wilczynski Date: Tue, 15 Nov 2022 11:48:18 +0100 Subject: [PATCH 1042/2002] devlink: Allow for devlink-rate nodes parent reassignment Currently it's not possible to reassign the parent of the node using one command. As the previous commit introduced a way to export entire hierarchy from the driver, being able to modify and reassign parents become important. This way user might easily change QoS settings without interrupting traffic. Example command: devlink port function rate set pci/0000:4b:00.0/1 parent node_custom_1 This reassigns leaf node parent to node_custom_1. Signed-off-by: Michal Wilczynski Reviewed-by: Przemek Kitszel Reviewed-by: Jiri Pirko Signed-off-by: Jakub Kicinski --- net/core/devlink.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/net/core/devlink.c b/net/core/devlink.c index 3dfee7cd9929..61d431578f5f 100644 --- a/net/core/devlink.c +++ b/net/core/devlink.c @@ -1887,10 +1887,8 @@ devlink_nl_rate_parent_node_set(struct devlink_rate *devlink_rate, int err = -EOPNOTSUPP; parent = devlink_rate->parent; - if (parent && len) { - NL_SET_ERR_MSG_MOD(info->extack, "Rate object already has parent."); - return -EBUSY; - } else if (parent && !len) { + + if (parent && !len) { if (devlink_rate_is_leaf(devlink_rate)) err = ops->rate_leaf_parent_set(devlink_rate, NULL, devlink_rate->priv, NULL, @@ -1904,7 +1902,7 @@ devlink_nl_rate_parent_node_set(struct devlink_rate *devlink_rate, refcount_dec(&parent->refcnt); devlink_rate->parent = NULL; - } else if (!parent && len) { + } else if (len) { parent = devlink_rate_node_get_by_name(devlink, parent_name); if (IS_ERR(parent)) return -ENODEV; @@ -1931,6 +1929,10 @@ devlink_nl_rate_parent_node_set(struct devlink_rate *devlink_rate, if (err) return err; + if (devlink_rate->parent) + /* we're reassigning to other parent in this case */ + refcount_dec(&devlink_rate->parent->refcnt); + refcount_inc(&parent->refcnt); devlink_rate->parent = parent; } From f2fc15e271f2d17f2bee2c5a3b3e50252a7ba91f Mon Sep 17 00:00:00 2001 From: Michal Wilczynski Date: Tue, 15 Nov 2022 11:48:19 +0100 Subject: [PATCH 1043/2002] devlink: Allow to set up parent in devl_rate_leaf_create() Currently the driver is able to create leaf nodes for the devlink-rate, but is unable to set parent for them. This wasn't as issue before the possibility to export hierarchy from the driver. After adding the export feature, in order for the driver to supply correct hierarchy, it's necessary for it to be able to supply a parent name to devl_rate_leaf_create(). Introduce a new parameter 'parent_name' in devl_rate_leaf_create(). Signed-off-by: Michal Wilczynski Reviewed-by: Jiri Pirko Signed-off-by: Jakub Kicinski --- .../net/ethernet/mellanox/mlx5/core/esw/devlink_port.c | 4 ++-- drivers/net/netdevsim/dev.c | 2 +- include/net/devlink.h | 4 +++- net/core/devlink.c | 9 ++++++++- 4 files changed, 14 insertions(+), 5 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/esw/devlink_port.c b/drivers/net/ethernet/mellanox/mlx5/core/esw/devlink_port.c index 9bc7be95db54..084a910bb4e7 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/esw/devlink_port.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/esw/devlink_port.c @@ -91,7 +91,7 @@ int mlx5_esw_offloads_devlink_port_register(struct mlx5_eswitch *esw, u16 vport_ if (err) goto reg_err; - err = devl_rate_leaf_create(dl_port, vport); + err = devl_rate_leaf_create(dl_port, vport, NULL); if (err) goto rate_err; @@ -160,7 +160,7 @@ int mlx5_esw_devlink_sf_port_register(struct mlx5_eswitch *esw, struct devlink_p if (err) return err; - err = devl_rate_leaf_create(dl_port, vport); + err = devl_rate_leaf_create(dl_port, vport, NULL); if (err) goto rate_err; diff --git a/drivers/net/netdevsim/dev.c b/drivers/net/netdevsim/dev.c index 705872eb7564..e14686594a71 100644 --- a/drivers/net/netdevsim/dev.c +++ b/drivers/net/netdevsim/dev.c @@ -1401,7 +1401,7 @@ static int __nsim_dev_port_add(struct nsim_dev *nsim_dev, enum nsim_dev_port_typ if (nsim_dev_port_is_vf(nsim_dev_port)) { err = devl_rate_leaf_create(&nsim_dev_port->devlink_port, - nsim_dev_port); + nsim_dev_port, NULL); if (err) goto err_nsim_destroy; } diff --git a/include/net/devlink.h b/include/net/devlink.h index 339a2ed02d36..074a79b8933f 100644 --- a/include/net/devlink.h +++ b/include/net/devlink.h @@ -1617,10 +1617,12 @@ void devlink_port_attrs_pci_vf_set(struct devlink_port *devlink_port, u32 contro void devlink_port_attrs_pci_sf_set(struct devlink_port *devlink_port, u32 controller, u16 pf, u32 sf, bool external); -int devl_rate_leaf_create(struct devlink_port *port, void *priv); struct devlink_rate * devl_rate_node_create(struct devlink *devlink, void *priv, char *node_name, struct devlink_rate *parent); +int +devl_rate_leaf_create(struct devlink_port *devlink_port, void *priv, + struct devlink_rate *parent); void devl_rate_leaf_destroy(struct devlink_port *devlink_port); void devl_rate_nodes_destroy(struct devlink *devlink); void devlink_port_linecard_set(struct devlink_port *devlink_port, diff --git a/net/core/devlink.c b/net/core/devlink.c index 61d431578f5f..d93bc95cd7cb 100644 --- a/net/core/devlink.c +++ b/net/core/devlink.c @@ -10435,10 +10435,12 @@ EXPORT_SYMBOL_GPL(devl_rate_node_create); * devl_rate_leaf_create - create devlink rate leaf * @devlink_port: devlink port object to create rate object on * @priv: driver private data + * @parent: parent devlink_rate struct * * Create devlink rate object of type leaf on provided @devlink_port. */ -int devl_rate_leaf_create(struct devlink_port *devlink_port, void *priv) +int devl_rate_leaf_create(struct devlink_port *devlink_port, void *priv, + struct devlink_rate *parent) { struct devlink *devlink = devlink_port->devlink; struct devlink_rate *devlink_rate; @@ -10452,6 +10454,11 @@ int devl_rate_leaf_create(struct devlink_port *devlink_port, void *priv) if (!devlink_rate) return -ENOMEM; + if (parent) { + devlink_rate->parent = parent; + refcount_inc(&devlink_rate->parent->refcnt); + } + devlink_rate->type = DEVLINK_RATE_TYPE_LEAF; devlink_rate->devlink = devlink; devlink_rate->devlink_port = devlink_port; From 16dfa49406bc5e1f4cbb115027cbd719d7e6c930 Mon Sep 17 00:00:00 2001 From: Michal Wilczynski Date: Tue, 15 Nov 2022 11:48:20 +0100 Subject: [PATCH 1044/2002] ice: Introduce new parameters in ice_sched_node To support new devlink-rate API ice_sched_node struct needs to store a number of additional parameters. This includes tx_max, tx_share, tx_weight, and tx_priority. Add new fields to ice_sched_node struct. Add new functions to configure the hardware with new parameters. Introduce new xarray to identify nodes uniquely. Signed-off-by: Michal Wilczynski Signed-off-by: Jakub Kicinski --- .../net/ethernet/intel/ice/ice_adminq_cmd.h | 4 +- drivers/net/ethernet/intel/ice/ice_common.c | 3 + drivers/net/ethernet/intel/ice/ice_sched.c | 81 +++++++++++++++++-- drivers/net/ethernet/intel/ice/ice_sched.h | 27 +++++++ drivers/net/ethernet/intel/ice/ice_type.h | 8 ++ 5 files changed, 116 insertions(+), 7 deletions(-) diff --git a/drivers/net/ethernet/intel/ice/ice_adminq_cmd.h b/drivers/net/ethernet/intel/ice/ice_adminq_cmd.h index 1bdc70aa979d..958c1e435232 100644 --- a/drivers/net/ethernet/intel/ice/ice_adminq_cmd.h +++ b/drivers/net/ethernet/intel/ice/ice_adminq_cmd.h @@ -848,9 +848,9 @@ struct ice_aqc_txsched_elem { u8 generic; #define ICE_AQC_ELEM_GENERIC_MODE_M 0x1 #define ICE_AQC_ELEM_GENERIC_PRIO_S 0x1 -#define ICE_AQC_ELEM_GENERIC_PRIO_M (0x7 << ICE_AQC_ELEM_GENERIC_PRIO_S) +#define ICE_AQC_ELEM_GENERIC_PRIO_M GENMASK(3, 1) #define ICE_AQC_ELEM_GENERIC_SP_S 0x4 -#define ICE_AQC_ELEM_GENERIC_SP_M (0x1 << ICE_AQC_ELEM_GENERIC_SP_S) +#define ICE_AQC_ELEM_GENERIC_SP_M GENMASK(4, 4) #define ICE_AQC_ELEM_GENERIC_ADJUST_VAL_S 0x5 #define ICE_AQC_ELEM_GENERIC_ADJUST_VAL_M \ (0x3 << ICE_AQC_ELEM_GENERIC_ADJUST_VAL_S) diff --git a/drivers/net/ethernet/intel/ice/ice_common.c b/drivers/net/ethernet/intel/ice/ice_common.c index 039342a0ed15..e2e661010176 100644 --- a/drivers/net/ethernet/intel/ice/ice_common.c +++ b/drivers/net/ethernet/intel/ice/ice_common.c @@ -1105,6 +1105,9 @@ int ice_init_hw(struct ice_hw *hw) hw->evb_veb = true; + /* init xarray for identifying scheduling nodes uniquely */ + xa_init_flags(&hw->port_info->sched_node_ids, XA_FLAGS_ALLOC); + /* Query the allocated resources for Tx scheduler */ status = ice_sched_query_res_alloc(hw); if (status) { diff --git a/drivers/net/ethernet/intel/ice/ice_sched.c b/drivers/net/ethernet/intel/ice/ice_sched.c index 118595763bba..980543074ddb 100644 --- a/drivers/net/ethernet/intel/ice/ice_sched.c +++ b/drivers/net/ethernet/intel/ice/ice_sched.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 /* Copyright (c) 2018, Intel Corporation. */ +#include #include "ice_sched.h" /** @@ -355,6 +356,9 @@ void ice_free_sched_node(struct ice_port_info *pi, struct ice_sched_node *node) /* leaf nodes have no children */ if (node->children) devm_kfree(ice_hw_to_dev(hw), node->children); + + kfree(node->name); + xa_erase(&pi->sched_node_ids, node->id); devm_kfree(ice_hw_to_dev(hw), node); } @@ -875,7 +879,7 @@ void ice_sched_cleanup_all(struct ice_hw *hw) * * This function add nodes to HW as well as to SW DB for a given layer */ -static int +int ice_sched_add_elems(struct ice_port_info *pi, struct ice_sched_node *tc_node, struct ice_sched_node *parent, u8 layer, u16 num_nodes, u16 *num_nodes_added, u32 *first_node_teid) @@ -940,6 +944,22 @@ ice_sched_add_elems(struct ice_port_info *pi, struct ice_sched_node *tc_node, new_node->sibling = NULL; new_node->tc_num = tc_node->tc_num; + new_node->tx_weight = ICE_SCHED_DFLT_BW_WT; + new_node->tx_share = ICE_SCHED_DFLT_BW; + new_node->tx_max = ICE_SCHED_DFLT_BW; + new_node->name = kzalloc(SCHED_NODE_NAME_MAX_LEN, GFP_KERNEL); + if (!new_node->name) + return -ENOMEM; + + status = xa_alloc(&pi->sched_node_ids, &new_node->id, NULL, XA_LIMIT(0, UINT_MAX), + GFP_KERNEL); + if (status) { + ice_debug(hw, ICE_DBG_SCHED, "xa_alloc failed for sched node status =%d\n", + status); + break; + } + + snprintf(new_node->name, SCHED_NODE_NAME_MAX_LEN, "node_%u", new_node->id); /* add it to previous node sibling pointer */ /* Note: siblings are not linked across branches */ @@ -2154,7 +2174,7 @@ ice_sched_get_free_vsi_parent(struct ice_hw *hw, struct ice_sched_node *node, * This function removes the child from the old parent and adds it to a new * parent */ -static void +void ice_sched_update_parent(struct ice_sched_node *new_parent, struct ice_sched_node *node) { @@ -2188,7 +2208,7 @@ ice_sched_update_parent(struct ice_sched_node *new_parent, * * This function move the child nodes to a given parent. */ -static int +int ice_sched_move_nodes(struct ice_port_info *pi, struct ice_sched_node *parent, u16 num_items, u32 *list) { @@ -3560,7 +3580,7 @@ ice_sched_set_eir_srl_excl(struct ice_port_info *pi, * node's RL profile ID of type CIR, EIR, or SRL, and removes old profile * ID from local database. The caller needs to hold scheduler lock. */ -static int +int ice_sched_set_node_bw(struct ice_port_info *pi, struct ice_sched_node *node, enum ice_rl_type rl_type, u32 bw, u8 layer_num) { @@ -3596,6 +3616,57 @@ ice_sched_set_node_bw(struct ice_port_info *pi, struct ice_sched_node *node, ICE_AQC_RL_PROFILE_TYPE_M, old_id); } +/** + * ice_sched_set_node_priority - set node's priority + * @pi: port information structure + * @node: tree node + * @priority: number 0-7 representing priority among siblings + * + * This function sets priority of a node among it's siblings. + */ +int +ice_sched_set_node_priority(struct ice_port_info *pi, struct ice_sched_node *node, + u16 priority) +{ + struct ice_aqc_txsched_elem_data buf; + struct ice_aqc_txsched_elem *data; + + buf = node->info; + data = &buf.data; + + data->valid_sections |= ICE_AQC_ELEM_VALID_GENERIC; + data->generic |= FIELD_PREP(ICE_AQC_ELEM_GENERIC_PRIO_M, priority); + + return ice_sched_update_elem(pi->hw, node, &buf); +} + +/** + * ice_sched_set_node_weight - set node's weight + * @pi: port information structure + * @node: tree node + * @weight: number 1-200 representing weight for WFQ + * + * This function sets weight of the node for WFQ algorithm. + */ +int +ice_sched_set_node_weight(struct ice_port_info *pi, struct ice_sched_node *node, u16 weight) +{ + struct ice_aqc_txsched_elem_data buf; + struct ice_aqc_txsched_elem *data; + + buf = node->info; + data = &buf.data; + + data->valid_sections = ICE_AQC_ELEM_VALID_CIR | ICE_AQC_ELEM_VALID_EIR | + ICE_AQC_ELEM_VALID_GENERIC; + data->cir_bw.bw_alloc = cpu_to_le16(weight); + data->eir_bw.bw_alloc = cpu_to_le16(weight); + + data->generic |= FIELD_PREP(ICE_AQC_ELEM_GENERIC_SP_M, 0x0); + + return ice_sched_update_elem(pi->hw, node, &buf); +} + /** * ice_sched_set_node_bw_lmt - set node's BW limit * @pi: port information structure @@ -3606,7 +3677,7 @@ ice_sched_set_node_bw(struct ice_port_info *pi, struct ice_sched_node *node, * It updates node's BW limit parameters like BW RL profile ID of type CIR, * EIR, or SRL. The caller needs to hold scheduler lock. */ -static int +int ice_sched_set_node_bw_lmt(struct ice_port_info *pi, struct ice_sched_node *node, enum ice_rl_type rl_type, u32 bw) { diff --git a/drivers/net/ethernet/intel/ice/ice_sched.h b/drivers/net/ethernet/intel/ice/ice_sched.h index 4f91577fed56..920db43ed4fa 100644 --- a/drivers/net/ethernet/intel/ice/ice_sched.h +++ b/drivers/net/ethernet/intel/ice/ice_sched.h @@ -6,6 +6,8 @@ #include "ice_common.h" +#define SCHED_NODE_NAME_MAX_LEN 32 + #define ICE_QGRP_LAYER_OFFSET 2 #define ICE_VSI_LAYER_OFFSET 4 #define ICE_AGG_LAYER_OFFSET 6 @@ -69,6 +71,28 @@ int ice_aq_query_sched_elems(struct ice_hw *hw, u16 elems_req, struct ice_aqc_txsched_elem_data *buf, u16 buf_size, u16 *elems_ret, struct ice_sq_cd *cd); + +int +ice_sched_set_node_bw_lmt(struct ice_port_info *pi, struct ice_sched_node *node, + enum ice_rl_type rl_type, u32 bw); + +int +ice_sched_set_node_bw(struct ice_port_info *pi, struct ice_sched_node *node, + enum ice_rl_type rl_type, u32 bw, u8 layer_num); + +int +ice_sched_add_elems(struct ice_port_info *pi, struct ice_sched_node *tc_node, + struct ice_sched_node *parent, u8 layer, u16 num_nodes, + u16 *num_nodes_added, u32 *first_node_teid); + +int +ice_sched_move_nodes(struct ice_port_info *pi, struct ice_sched_node *parent, + u16 num_items, u32 *list); + +int ice_sched_set_node_priority(struct ice_port_info *pi, struct ice_sched_node *node, + u16 priority); +int ice_sched_set_node_weight(struct ice_port_info *pi, struct ice_sched_node *node, u16 weight); + int ice_sched_init_port(struct ice_port_info *pi); int ice_sched_query_res_alloc(struct ice_hw *hw); void ice_sched_get_psm_clk_freq(struct ice_hw *hw); @@ -82,6 +106,9 @@ ice_sched_find_node_by_teid(struct ice_sched_node *start_node, u32 teid); int ice_sched_add_node(struct ice_port_info *pi, u8 layer, struct ice_aqc_txsched_elem_data *info); +void +ice_sched_update_parent(struct ice_sched_node *new_parent, + struct ice_sched_node *node); void ice_free_sched_node(struct ice_port_info *pi, struct ice_sched_node *node); struct ice_sched_node *ice_sched_get_tc_node(struct ice_port_info *pi, u8 tc); struct ice_sched_node * diff --git a/drivers/net/ethernet/intel/ice/ice_type.h b/drivers/net/ethernet/intel/ice/ice_type.h index e1abfcee96dc..daf86cf561bc 100644 --- a/drivers/net/ethernet/intel/ice/ice_type.h +++ b/drivers/net/ethernet/intel/ice/ice_type.h @@ -524,7 +524,14 @@ struct ice_sched_node { struct ice_sched_node *sibling; /* next sibling in the same layer */ struct ice_sched_node **children; struct ice_aqc_txsched_elem_data info; + char *name; + struct devlink_rate *rate_node; + u64 tx_max; + u64 tx_share; u32 agg_id; /* aggregator group ID */ + u32 id; + u32 tx_priority; + u32 tx_weight; u16 vsi_handle; u8 in_use; /* suspended or in use */ u8 tx_sched_layer; /* Logical Layer (1-9) */ @@ -706,6 +713,7 @@ struct ice_port_info { /* List contain profile ID(s) and other params per layer */ struct list_head rl_prof_list[ICE_AQC_TOPO_MAX_LEVEL_NUM]; struct ice_qos_cfg qos_cfg; + struct xarray sched_node_ids; u8 is_vf:1; }; From bdf96d965a204dab8085b8f8e361b3844e6667f9 Mon Sep 17 00:00:00 2001 From: Michal Wilczynski Date: Tue, 15 Nov 2022 11:48:21 +0100 Subject: [PATCH 1045/2002] ice: Add an option to pre-allocate memory for ice_sched_node devlink-rate API requires a priv object to be allocated when node still doesn't have a parent. This is problematic, because ice_sched_node can't be currently created without a parent. Add an option to pre-allocate memory for ice_sched_node struct. Add new arguments to ice_sched_add() and ice_sched_add_elems() that allow for pre-allocation of memory for ice_sched_node struct. Signed-off-by: Michal Wilczynski Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/intel/ice/ice_common.c | 4 ++-- drivers/net/ethernet/intel/ice/ice_dcb.c | 2 +- drivers/net/ethernet/intel/ice/ice_sched.c | 23 +++++++++++++++------ drivers/net/ethernet/intel/ice/ice_sched.h | 6 ++++-- 4 files changed, 24 insertions(+), 11 deletions(-) diff --git a/drivers/net/ethernet/intel/ice/ice_common.c b/drivers/net/ethernet/intel/ice/ice_common.c index e2e661010176..216370ec60d4 100644 --- a/drivers/net/ethernet/intel/ice/ice_common.c +++ b/drivers/net/ethernet/intel/ice/ice_common.c @@ -4603,7 +4603,7 @@ ice_ena_vsi_txq(struct ice_port_info *pi, u16 vsi_handle, u8 tc, u16 q_handle, q_ctx->q_teid = le32_to_cpu(node.node_teid); /* add a leaf node into scheduler tree queue layer */ - status = ice_sched_add_node(pi, hw->num_tx_sched_layers - 1, &node); + status = ice_sched_add_node(pi, hw->num_tx_sched_layers - 1, &node, NULL); if (!status) status = ice_sched_replay_q_bw(pi, q_ctx); @@ -4838,7 +4838,7 @@ ice_ena_vsi_rdma_qset(struct ice_port_info *pi, u16 vsi_handle, u8 tc, for (i = 0; i < num_qsets; i++) { node.node_teid = buf->rdma_qsets[i].qset_teid; ret = ice_sched_add_node(pi, hw->num_tx_sched_layers - 1, - &node); + &node, NULL); if (ret) break; qset_teid[i] = le32_to_cpu(node.node_teid); diff --git a/drivers/net/ethernet/intel/ice/ice_dcb.c b/drivers/net/ethernet/intel/ice/ice_dcb.c index 0b146a0d4205..6be02f9b0b8c 100644 --- a/drivers/net/ethernet/intel/ice/ice_dcb.c +++ b/drivers/net/ethernet/intel/ice/ice_dcb.c @@ -1580,7 +1580,7 @@ ice_update_port_tc_tree_cfg(struct ice_port_info *pi, /* new TC */ status = ice_sched_query_elem(pi->hw, teid2, &elem); if (!status) - status = ice_sched_add_node(pi, 1, &elem); + status = ice_sched_add_node(pi, 1, &elem, NULL); if (status) break; /* update the TC number */ diff --git a/drivers/net/ethernet/intel/ice/ice_sched.c b/drivers/net/ethernet/intel/ice/ice_sched.c index 980543074ddb..6d08b397df2a 100644 --- a/drivers/net/ethernet/intel/ice/ice_sched.c +++ b/drivers/net/ethernet/intel/ice/ice_sched.c @@ -143,12 +143,14 @@ ice_aq_query_sched_elems(struct ice_hw *hw, u16 elems_req, * @pi: port information structure * @layer: Scheduler layer of the node * @info: Scheduler element information from firmware + * @prealloc_node: preallocated ice_sched_node struct for SW DB * * This function inserts a scheduler node to the SW DB. */ int ice_sched_add_node(struct ice_port_info *pi, u8 layer, - struct ice_aqc_txsched_elem_data *info) + struct ice_aqc_txsched_elem_data *info, + struct ice_sched_node *prealloc_node) { struct ice_aqc_txsched_elem_data elem; struct ice_sched_node *parent; @@ -177,7 +179,10 @@ ice_sched_add_node(struct ice_port_info *pi, u8 layer, if (status) return status; - node = devm_kzalloc(ice_hw_to_dev(hw), sizeof(*node), GFP_KERNEL); + if (prealloc_node) + node = prealloc_node; + else + node = devm_kzalloc(ice_hw_to_dev(hw), sizeof(*node), GFP_KERNEL); if (!node) return -ENOMEM; if (hw->max_children[layer]) { @@ -876,13 +881,15 @@ void ice_sched_cleanup_all(struct ice_hw *hw) * @num_nodes: number of nodes * @num_nodes_added: pointer to num nodes added * @first_node_teid: if new nodes are added then return the TEID of first node + * @prealloc_nodes: preallocated nodes struct for software DB * * This function add nodes to HW as well as to SW DB for a given layer */ int ice_sched_add_elems(struct ice_port_info *pi, struct ice_sched_node *tc_node, struct ice_sched_node *parent, u8 layer, u16 num_nodes, - u16 *num_nodes_added, u32 *first_node_teid) + u16 *num_nodes_added, u32 *first_node_teid, + struct ice_sched_node **prealloc_nodes) { struct ice_sched_node *prev, *new_node; struct ice_aqc_add_elem *buf; @@ -928,7 +935,11 @@ ice_sched_add_elems(struct ice_port_info *pi, struct ice_sched_node *tc_node, *num_nodes_added = num_nodes; /* add nodes to the SW DB */ for (i = 0; i < num_nodes; i++) { - status = ice_sched_add_node(pi, layer, &buf->generic[i]); + if (prealloc_nodes) + status = ice_sched_add_node(pi, layer, &buf->generic[i], prealloc_nodes[i]); + else + status = ice_sched_add_node(pi, layer, &buf->generic[i], NULL); + if (status) { ice_debug(hw, ICE_DBG_SCHED, "add nodes in SW DB failed status =%d\n", status); @@ -1023,7 +1034,7 @@ ice_sched_add_nodes_to_hw_layer(struct ice_port_info *pi, } return ice_sched_add_elems(pi, tc_node, parent, layer, num_nodes, - num_nodes_added, first_node_teid); + num_nodes_added, first_node_teid, NULL); } /** @@ -1288,7 +1299,7 @@ int ice_sched_init_port(struct ice_port_info *pi) ICE_AQC_ELEM_TYPE_ENTRY_POINT) hw->sw_entry_point_layer = j; - status = ice_sched_add_node(pi, j, &buf[i].generic[j]); + status = ice_sched_add_node(pi, j, &buf[i].generic[j], NULL); if (status) goto err_init_port; } diff --git a/drivers/net/ethernet/intel/ice/ice_sched.h b/drivers/net/ethernet/intel/ice/ice_sched.h index 920db43ed4fa..9c100747445a 100644 --- a/drivers/net/ethernet/intel/ice/ice_sched.h +++ b/drivers/net/ethernet/intel/ice/ice_sched.h @@ -83,7 +83,8 @@ ice_sched_set_node_bw(struct ice_port_info *pi, struct ice_sched_node *node, int ice_sched_add_elems(struct ice_port_info *pi, struct ice_sched_node *tc_node, struct ice_sched_node *parent, u8 layer, u16 num_nodes, - u16 *num_nodes_added, u32 *first_node_teid); + u16 *num_nodes_added, u32 *first_node_teid, + struct ice_sched_node **prealloc_node); int ice_sched_move_nodes(struct ice_port_info *pi, struct ice_sched_node *parent, @@ -105,7 +106,8 @@ struct ice_sched_node * ice_sched_find_node_by_teid(struct ice_sched_node *start_node, u32 teid); int ice_sched_add_node(struct ice_port_info *pi, u8 layer, - struct ice_aqc_txsched_elem_data *info); + struct ice_aqc_txsched_elem_data *info, + struct ice_sched_node *prealloc_node); void ice_sched_update_parent(struct ice_sched_node *new_parent, struct ice_sched_node *node); From 42c2eb6b1f43b1f5ac06b1a4dcae5fcb036b5b20 Mon Sep 17 00:00:00 2001 From: Michal Wilczynski Date: Tue, 15 Nov 2022 11:48:22 +0100 Subject: [PATCH 1046/2002] ice: Implement devlink-rate API There is a need to support modification of Tx scheduler tree, in the ice driver. This will allow user to control Tx settings of each node in the internal hierarchy of nodes. As a result user will be able to use Hierarchy QoS implemented entirely in the hardware. This patch implemenents devlink-rate API. It also exports initial default hierarchy. It's mostly dictated by the fact that the tree can't be removed entirely, all we can do is enable the user to modify it. For example root node shouldn't ever be removed, also nodes that have children are off-limits. Example initial tree with 2 VF's: [root@fedora ~]# devlink port function rate show pci/0000:4b:00.0/node_27: type node parent node_26 pci/0000:4b:00.0/node_26: type node parent node_0 pci/0000:4b:00.0/node_34: type node parent node_33 pci/0000:4b:00.0/node_33: type node parent node_32 pci/0000:4b:00.0/node_32: type node parent node_16 pci/0000:4b:00.0/node_19: type node parent node_18 pci/0000:4b:00.0/node_18: type node parent node_17 pci/0000:4b:00.0/node_17: type node parent node_16 pci/0000:4b:00.0/node_21: type node parent node_20 pci/0000:4b:00.0/node_20: type node parent node_3 pci/0000:4b:00.0/node_14: type node parent node_5 pci/0000:4b:00.0/node_5: type node parent node_3 pci/0000:4b:00.0/node_13: type node parent node_4 pci/0000:4b:00.0/node_12: type node parent node_4 pci/0000:4b:00.0/node_11: type node parent node_4 pci/0000:4b:00.0/node_10: type node parent node_4 pci/0000:4b:00.0/node_9: type node parent node_4 pci/0000:4b:00.0/node_8: type node parent node_4 pci/0000:4b:00.0/node_7: type node parent node_4 pci/0000:4b:00.0/node_6: type node parent node_4 pci/0000:4b:00.0/node_4: type node parent node_3 pci/0000:4b:00.0/node_3: type node parent node_16 pci/0000:4b:00.0/node_16: type node parent node_15 pci/0000:4b:00.0/node_15: type node parent node_0 pci/0000:4b:00.0/node_2: type node parent node_1 pci/0000:4b:00.0/node_1: type node parent node_0 pci/0000:4b:00.0/node_0: type node pci/0000:4b:00.0/1: type leaf parent node_27 pci/0000:4b:00.0/2: type leaf parent node_27 Let me visualize part of the tree: +---------+ | node_0 | +---------+ | +----v----+ | node_26 | +----+----+ | +----v----+ | node_27 | +----+----+ | |-----------------| +----v----+ +----v----+ | VF 1 | | VF 2 | +----+----+ +----+----+ So at this point there is a couple things that can be done. For example we could only assign parameters to VF's. [root@fedora ~]# devlink port function rate set pci/0000:4b:00.0/1 \ tx_max 5Gbps This would cap the VF 1 BW to 5Gbps. But let's say you would like to create a completely new branch. This can be done like this: [root@fedora ~]# devlink port function rate add \ pci/0000:4b:00.0/node_custom parent node_0 [root@fedora ~]# devlink port function rate add \ pci/0000:4b:00.0/node_custom_1 parent node_custom [root@fedora ~]# devlink port function rate set \ pci/0000:4b:00.0/1 parent node_custom_1 This creates a completely new branch and reassigns VF 1 to it. A number of parameters is supported per each node: tx_max, tx_share, tx_priority and tx_weight. Signed-off-by: Michal Wilczynski Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/intel/ice/ice_devlink.c | 411 +++++++++++++++++++ drivers/net/ethernet/intel/ice/ice_devlink.h | 2 + drivers/net/ethernet/intel/ice/ice_repr.c | 13 + 3 files changed, 426 insertions(+) diff --git a/drivers/net/ethernet/intel/ice/ice_devlink.c b/drivers/net/ethernet/intel/ice/ice_devlink.c index 455489e9457d..46f5b587b1c8 100644 --- a/drivers/net/ethernet/intel/ice/ice_devlink.c +++ b/drivers/net/ethernet/intel/ice/ice_devlink.c @@ -713,6 +713,400 @@ ice_devlink_port_unsplit(struct devlink *devlink, struct devlink_port *port, return ice_devlink_port_split(devlink, port, 1, extack); } +/** + * ice_traverse_tx_tree - traverse Tx scheduler tree + * @devlink: devlink struct + * @node: current node, used for recursion + * @tc_node: tc_node struct, that is treated as a root + * @pf: pf struct + * + * This function traverses Tx scheduler tree and exports + * entire structure to the devlink-rate. + */ +static void ice_traverse_tx_tree(struct devlink *devlink, struct ice_sched_node *node, + struct ice_sched_node *tc_node, struct ice_pf *pf) +{ + struct devlink_rate *rate_node = NULL; + struct ice_vf *vf; + int i; + + if (node->parent == tc_node) { + /* create root node */ + rate_node = devl_rate_node_create(devlink, node, node->name, NULL); + } else if (node->vsi_handle && + pf->vsi[node->vsi_handle]->vf) { + vf = pf->vsi[node->vsi_handle]->vf; + if (!vf->devlink_port.devlink_rate) + /* leaf nodes doesn't have children + * so we don't set rate_node + */ + devl_rate_leaf_create(&vf->devlink_port, node, + node->parent->rate_node); + } else if (node->info.data.elem_type != ICE_AQC_ELEM_TYPE_LEAF && + node->parent->rate_node) { + rate_node = devl_rate_node_create(devlink, node, node->name, + node->parent->rate_node); + } + + if (rate_node && !IS_ERR(rate_node)) + node->rate_node = rate_node; + + for (i = 0; i < node->num_children; i++) + ice_traverse_tx_tree(devlink, node->children[i], tc_node, pf); +} + +/** + * ice_devlink_rate_init_tx_topology - export Tx scheduler tree to devlink rate + * @devlink: devlink struct + * @vsi: main vsi struct + * + * This function finds a root node, then calls ice_traverse_tx tree, which + * traverses the tree and exports it's contents to devlink rate. + */ +int ice_devlink_rate_init_tx_topology(struct devlink *devlink, struct ice_vsi *vsi) +{ + struct ice_port_info *pi = vsi->port_info; + struct ice_sched_node *tc_node; + struct ice_pf *pf = vsi->back; + int i; + + tc_node = pi->root->children[0]; + mutex_lock(&pi->sched_lock); + devl_lock(devlink); + for (i = 0; i < tc_node->num_children; i++) + ice_traverse_tx_tree(devlink, tc_node->children[i], tc_node, pf); + devl_unlock(devlink); + mutex_unlock(&pi->sched_lock); + + return 0; +} + +/** + * ice_set_object_tx_share - sets node scheduling parameter + * @pi: devlink struct instance + * @node: node struct instance + * @bw: bandwidth in bytes per second + * @extack: extended netdev ack structure + * + * This function sets ICE_MIN_BW scheduling BW limit. + */ +static int ice_set_object_tx_share(struct ice_port_info *pi, struct ice_sched_node *node, + u64 bw, struct netlink_ext_ack *extack) +{ + int status; + + mutex_lock(&pi->sched_lock); + /* converts bytes per second to kilo bits per second */ + node->tx_share = div_u64(bw, 125); + status = ice_sched_set_node_bw_lmt(pi, node, ICE_MIN_BW, node->tx_share); + mutex_unlock(&pi->sched_lock); + + if (status) + NL_SET_ERR_MSG_MOD(extack, "Can't set scheduling node tx_share"); + + return status; +} + +/** + * ice_set_object_tx_max - sets node scheduling parameter + * @pi: devlink struct instance + * @node: node struct instance + * @bw: bandwidth in bytes per second + * @extack: extended netdev ack structure + * + * This function sets ICE_MAX_BW scheduling BW limit. + */ +static int ice_set_object_tx_max(struct ice_port_info *pi, struct ice_sched_node *node, + u64 bw, struct netlink_ext_ack *extack) +{ + int status; + + mutex_lock(&pi->sched_lock); + /* converts bytes per second value to kilo bits per second */ + node->tx_max = div_u64(bw, 125); + status = ice_sched_set_node_bw_lmt(pi, node, ICE_MAX_BW, node->tx_max); + mutex_unlock(&pi->sched_lock); + + if (status) + NL_SET_ERR_MSG_MOD(extack, "Can't set scheduling node tx_max"); + + return status; +} + +/** + * ice_set_object_tx_priority - sets node scheduling parameter + * @pi: devlink struct instance + * @node: node struct instance + * @priority: value representing priority for strict priority arbitration + * @extack: extended netdev ack structure + * + * This function sets priority of node among siblings. + */ +static int ice_set_object_tx_priority(struct ice_port_info *pi, struct ice_sched_node *node, + u32 priority, struct netlink_ext_ack *extack) +{ + int status; + + if (node->tx_priority >= 8) { + NL_SET_ERR_MSG_MOD(extack, "Priority should be less than 8"); + return -EINVAL; + } + + mutex_lock(&pi->sched_lock); + node->tx_priority = priority; + status = ice_sched_set_node_priority(pi, node, node->tx_priority); + mutex_unlock(&pi->sched_lock); + + if (status) + NL_SET_ERR_MSG_MOD(extack, "Can't set scheduling node tx_priority"); + + return status; +} + +/** + * ice_set_object_tx_weight - sets node scheduling parameter + * @pi: devlink struct instance + * @node: node struct instance + * @weight: value represeting relative weight for WFQ arbitration + * @extack: extended netdev ack structure + * + * This function sets node weight for WFQ algorithm. + */ +static int ice_set_object_tx_weight(struct ice_port_info *pi, struct ice_sched_node *node, + u32 weight, struct netlink_ext_ack *extack) +{ + int status; + + if (node->tx_weight > 200 || node->tx_weight < 1) { + NL_SET_ERR_MSG_MOD(extack, "Weight must be between 1 and 200"); + return -EINVAL; + } + + mutex_lock(&pi->sched_lock); + node->tx_weight = weight; + status = ice_sched_set_node_weight(pi, node, node->tx_weight); + mutex_unlock(&pi->sched_lock); + + if (status) + NL_SET_ERR_MSG_MOD(extack, "Can't set scheduling node tx_weight"); + + return status; +} + +/** + * ice_get_pi_from_dev_rate - get port info from devlink_rate + * @rate_node: devlink struct instance + * + * This function returns corresponding port_info struct of devlink_rate + */ +static struct ice_port_info *ice_get_pi_from_dev_rate(struct devlink_rate *rate_node) +{ + struct ice_pf *pf = devlink_priv(rate_node->devlink); + + return ice_get_main_vsi(pf)->port_info; +} + +static int ice_devlink_rate_node_new(struct devlink_rate *rate_node, void **priv, + struct netlink_ext_ack *extack) +{ + struct ice_sched_node *node; + struct ice_port_info *pi; + + pi = ice_get_pi_from_dev_rate(rate_node); + + /* preallocate memory for ice_sched_node */ + node = devm_kzalloc(ice_hw_to_dev(pi->hw), sizeof(*node), GFP_KERNEL); + *priv = node; + + return 0; +} + +static int ice_devlink_rate_node_del(struct devlink_rate *rate_node, void *priv, + struct netlink_ext_ack *extack) +{ + struct ice_sched_node *node, *tc_node; + struct ice_port_info *pi; + + pi = ice_get_pi_from_dev_rate(rate_node); + tc_node = pi->root->children[0]; + node = priv; + + if (!rate_node->parent || !node || tc_node == node || !extack) + return 0; + + /* can't allow to delete a node with children */ + if (node->num_children) + return -EINVAL; + + mutex_lock(&pi->sched_lock); + ice_free_sched_node(pi, node); + mutex_unlock(&pi->sched_lock); + + return 0; +} + +static int ice_devlink_rate_leaf_tx_max_set(struct devlink_rate *rate_leaf, void *priv, + u64 tx_max, struct netlink_ext_ack *extack) +{ + struct ice_sched_node *node = priv; + + if (!node) + return 0; + + return ice_set_object_tx_max(ice_get_pi_from_dev_rate(rate_leaf), + node, tx_max, extack); +} + +static int ice_devlink_rate_leaf_tx_share_set(struct devlink_rate *rate_leaf, void *priv, + u64 tx_share, struct netlink_ext_ack *extack) +{ + struct ice_sched_node *node = priv; + + if (!node) + return 0; + + return ice_set_object_tx_share(ice_get_pi_from_dev_rate(rate_leaf), node, + tx_share, extack); +} + +static int ice_devlink_rate_leaf_tx_priority_set(struct devlink_rate *rate_leaf, void *priv, + u32 tx_priority, struct netlink_ext_ack *extack) +{ + struct ice_sched_node *node = priv; + + if (!node) + return 0; + + return ice_set_object_tx_priority(ice_get_pi_from_dev_rate(rate_leaf), node, + tx_priority, extack); +} + +static int ice_devlink_rate_leaf_tx_weight_set(struct devlink_rate *rate_leaf, void *priv, + u32 tx_weight, struct netlink_ext_ack *extack) +{ + struct ice_sched_node *node = priv; + + if (!node) + return 0; + + return ice_set_object_tx_weight(ice_get_pi_from_dev_rate(rate_leaf), node, + tx_weight, extack); +} + +static int ice_devlink_rate_node_tx_max_set(struct devlink_rate *rate_node, void *priv, + u64 tx_max, struct netlink_ext_ack *extack) +{ + struct ice_sched_node *node = priv; + + if (!node) + return 0; + + return ice_set_object_tx_max(ice_get_pi_from_dev_rate(rate_node), + node, tx_max, extack); +} + +static int ice_devlink_rate_node_tx_share_set(struct devlink_rate *rate_node, void *priv, + u64 tx_share, struct netlink_ext_ack *extack) +{ + struct ice_sched_node *node = priv; + + if (!node) + return 0; + + return ice_set_object_tx_share(ice_get_pi_from_dev_rate(rate_node), + node, tx_share, extack); +} + +static int ice_devlink_rate_node_tx_priority_set(struct devlink_rate *rate_node, void *priv, + u32 tx_priority, struct netlink_ext_ack *extack) +{ + struct ice_sched_node *node = priv; + + if (!node) + return 0; + + return ice_set_object_tx_priority(ice_get_pi_from_dev_rate(rate_node), + node, tx_priority, extack); +} + +static int ice_devlink_rate_node_tx_weight_set(struct devlink_rate *rate_node, void *priv, + u32 tx_weight, struct netlink_ext_ack *extack) +{ + struct ice_sched_node *node = priv; + + if (!node) + return 0; + + return ice_set_object_tx_weight(ice_get_pi_from_dev_rate(rate_node), + node, tx_weight, extack); +} + +static int ice_devlink_set_parent(struct devlink_rate *devlink_rate, + struct devlink_rate *parent, + void *priv, void *parent_priv, + struct netlink_ext_ack *extack) +{ + struct ice_port_info *pi = ice_get_pi_from_dev_rate(devlink_rate); + struct ice_sched_node *tc_node, *node, *parent_node; + u16 num_nodes_added; + u32 first_node_teid; + u32 node_teid; + int status; + + tc_node = pi->root->children[0]; + node = priv; + + if (!extack) + return 0; + + if (!parent) { + if (!node || tc_node == node || node->num_children) + return -EINVAL; + + mutex_lock(&pi->sched_lock); + ice_free_sched_node(pi, node); + mutex_unlock(&pi->sched_lock); + + return 0; + } + + parent_node = parent_priv; + + /* if the node doesn't exist, create it */ + if (!node->parent) { + mutex_lock(&pi->sched_lock); + status = ice_sched_add_elems(pi, tc_node, parent_node, + parent_node->tx_sched_layer + 1, + 1, &num_nodes_added, &first_node_teid, + &node); + mutex_unlock(&pi->sched_lock); + + if (status) { + NL_SET_ERR_MSG_MOD(extack, "Can't add a new node"); + return status; + } + + if (devlink_rate->tx_share) + ice_set_object_tx_share(pi, node, devlink_rate->tx_share, extack); + if (devlink_rate->tx_max) + ice_set_object_tx_max(pi, node, devlink_rate->tx_max, extack); + if (devlink_rate->tx_priority) + ice_set_object_tx_priority(pi, node, devlink_rate->tx_priority, extack); + if (devlink_rate->tx_weight) + ice_set_object_tx_weight(pi, node, devlink_rate->tx_weight, extack); + } else { + node_teid = le32_to_cpu(node->info.node_teid); + mutex_lock(&pi->sched_lock); + status = ice_sched_move_nodes(pi, parent_node, 1, &node_teid); + mutex_unlock(&pi->sched_lock); + + if (status) + NL_SET_ERR_MSG_MOD(extack, "Can't move existing node to a new parent"); + } + + return status; +} + static const struct devlink_ops ice_devlink_ops = { .supported_flash_update_params = DEVLINK_SUPPORT_FLASH_UPDATE_OVERWRITE_MASK, .reload_actions = BIT(DEVLINK_RELOAD_ACTION_FW_ACTIVATE), @@ -725,6 +1119,22 @@ static const struct devlink_ops ice_devlink_ops = { .eswitch_mode_set = ice_eswitch_mode_set, .info_get = ice_devlink_info_get, .flash_update = ice_devlink_flash_update, + + .rate_node_new = ice_devlink_rate_node_new, + .rate_node_del = ice_devlink_rate_node_del, + + .rate_leaf_tx_max_set = ice_devlink_rate_leaf_tx_max_set, + .rate_leaf_tx_share_set = ice_devlink_rate_leaf_tx_share_set, + .rate_leaf_tx_priority_set = ice_devlink_rate_leaf_tx_priority_set, + .rate_leaf_tx_weight_set = ice_devlink_rate_leaf_tx_weight_set, + + .rate_node_tx_max_set = ice_devlink_rate_node_tx_max_set, + .rate_node_tx_share_set = ice_devlink_rate_node_tx_share_set, + .rate_node_tx_priority_set = ice_devlink_rate_node_tx_priority_set, + .rate_node_tx_weight_set = ice_devlink_rate_node_tx_weight_set, + + .rate_leaf_parent_set = ice_devlink_set_parent, + .rate_node_parent_set = ice_devlink_set_parent, }; static int @@ -1089,6 +1499,7 @@ int ice_devlink_create_vf_port(struct ice_vf *vf) */ void ice_devlink_destroy_vf_port(struct ice_vf *vf) { + devl_rate_leaf_destroy(&vf->devlink_port); devlink_port_unregister(&vf->devlink_port); } diff --git a/drivers/net/ethernet/intel/ice/ice_devlink.h b/drivers/net/ethernet/intel/ice/ice_devlink.h index fe006d9946f8..8bfed9ee2c4c 100644 --- a/drivers/net/ethernet/intel/ice/ice_devlink.h +++ b/drivers/net/ethernet/intel/ice/ice_devlink.h @@ -18,4 +18,6 @@ void ice_devlink_destroy_vf_port(struct ice_vf *vf); void ice_devlink_init_regions(struct ice_pf *pf); void ice_devlink_destroy_regions(struct ice_pf *pf); +int ice_devlink_rate_init_tx_topology(struct devlink *devlink, struct ice_vsi *vsi); + #endif /* _ICE_DEVLINK_H_ */ diff --git a/drivers/net/ethernet/intel/ice/ice_repr.c b/drivers/net/ethernet/intel/ice/ice_repr.c index 0483eb14c288..46f58d48318c 100644 --- a/drivers/net/ethernet/intel/ice/ice_repr.c +++ b/drivers/net/ethernet/intel/ice/ice_repr.c @@ -389,6 +389,7 @@ static void ice_repr_rem(struct ice_vf *vf) */ void ice_repr_rem_from_all_vfs(struct ice_pf *pf) { + struct devlink *devlink; struct ice_vf *vf; unsigned int bkt; @@ -396,6 +397,14 @@ void ice_repr_rem_from_all_vfs(struct ice_pf *pf) ice_for_each_vf(pf, bkt, vf) ice_repr_rem(vf); + + /* since all port representors are destroyed, there is + * no point in keeping the nodes + */ + devlink = priv_to_devlink(pf); + devl_lock(devlink); + devl_rate_nodes_destroy(devlink); + devl_unlock(devlink); } /** @@ -404,6 +413,7 @@ void ice_repr_rem_from_all_vfs(struct ice_pf *pf) */ int ice_repr_add_for_all_vfs(struct ice_pf *pf) { + struct devlink *devlink; struct ice_vf *vf; unsigned int bkt; int err; @@ -416,6 +426,9 @@ int ice_repr_add_for_all_vfs(struct ice_pf *pf) goto err; } + devlink = priv_to_devlink(pf); + ice_devlink_rate_init_tx_topology(devlink, ice_get_main_vsi(pf)); + return 0; err: From 80fe30a8c1f4d2177e1d25922cc6a8c439198103 Mon Sep 17 00:00:00 2001 From: Michal Wilczynski Date: Tue, 15 Nov 2022 11:48:23 +0100 Subject: [PATCH 1047/2002] ice: Prevent ADQ, DCB coexistence with Custom Tx scheduler ADQ, DCB might interfere with Custom Tx Scheduler changes that user might introduce using devlink-rate API. Check if ADQ, DCB is active, when user tries to change any setting in exported Tx scheduler tree. If any of those are active block the user from doing so, and log an appropriate message. Remove the exported hierarchy if user enable ADQ or DCB. Prevent ADQ or DCB from getting configured if user already made some changes using devlink-rate API. Signed-off-by: Michal Wilczynski Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/intel/ice/ice_dcb_lib.c | 7 ++ drivers/net/ethernet/intel/ice/ice_devlink.c | 91 ++++++++++++++++++++ drivers/net/ethernet/intel/ice/ice_devlink.h | 1 + drivers/net/ethernet/intel/ice/ice_main.c | 6 ++ drivers/net/ethernet/intel/ice/ice_repr.c | 5 ++ drivers/net/ethernet/intel/ice/ice_type.h | 1 + 6 files changed, 111 insertions(+) diff --git a/drivers/net/ethernet/intel/ice/ice_dcb_lib.c b/drivers/net/ethernet/intel/ice/ice_dcb_lib.c index add90e75f05c..9defb9d0fe88 100644 --- a/drivers/net/ethernet/intel/ice/ice_dcb_lib.c +++ b/drivers/net/ethernet/intel/ice/ice_dcb_lib.c @@ -3,6 +3,7 @@ #include "ice_dcb_lib.h" #include "ice_dcb_nl.h" +#include "ice_devlink.h" /** * ice_dcb_get_ena_tc - return bitmap of enabled TCs @@ -364,6 +365,12 @@ int ice_pf_dcb_cfg(struct ice_pf *pf, struct ice_dcbx_cfg *new_cfg, bool locked) /* Enable DCB tagging only when more than one TC */ if (ice_dcb_get_num_tc(new_cfg) > 1) { dev_dbg(dev, "DCB tagging enabled (num TC > 1)\n"); + if (pf->hw.port_info->is_custom_tx_enabled) { + dev_err(dev, "Custom Tx scheduler feature enabled, can't configure DCB\n"); + return -EBUSY; + } + ice_tear_down_devlink_rate_tree(pf); + set_bit(ICE_FLAG_DCB_ENA, pf->flags); } else { dev_dbg(dev, "DCB tagging disabled (num TC = 1)\n"); diff --git a/drivers/net/ethernet/intel/ice/ice_devlink.c b/drivers/net/ethernet/intel/ice/ice_devlink.c index 46f5b587b1c8..1d638216484d 100644 --- a/drivers/net/ethernet/intel/ice/ice_devlink.c +++ b/drivers/net/ethernet/intel/ice/ice_devlink.c @@ -8,6 +8,7 @@ #include "ice_devlink.h" #include "ice_eswitch.h" #include "ice_fw_update.h" +#include "ice_dcb_lib.h" static int ice_active_port_option = -1; @@ -713,6 +714,63 @@ ice_devlink_port_unsplit(struct devlink *devlink, struct devlink_port *port, return ice_devlink_port_split(devlink, port, 1, extack); } +/** + * ice_tear_down_devlink_rate_tree - removes devlink-rate exported tree + * @pf: pf struct + * + * This function tears down tree exported during VF's creation. + */ +void ice_tear_down_devlink_rate_tree(struct ice_pf *pf) +{ + struct devlink *devlink; + struct ice_vf *vf; + unsigned int bkt; + + devlink = priv_to_devlink(pf); + + devl_lock(devlink); + mutex_lock(&pf->vfs.table_lock); + ice_for_each_vf(pf, bkt, vf) { + if (vf->devlink_port.devlink_rate) + devl_rate_leaf_destroy(&vf->devlink_port); + } + mutex_unlock(&pf->vfs.table_lock); + + devl_rate_nodes_destroy(devlink); + devl_unlock(devlink); +} + +/** + * ice_enable_custom_tx - try to enable custom Tx feature + * @pf: pf struct + * + * This function tries to enable custom Tx feature, + * it's not possible to enable it, if DCB or ADQ is active. + */ +static bool ice_enable_custom_tx(struct ice_pf *pf) +{ + struct ice_port_info *pi = ice_get_main_vsi(pf)->port_info; + struct device *dev = ice_pf_to_dev(pf); + + if (pi->is_custom_tx_enabled) + /* already enabled, return true */ + return true; + + if (ice_is_adq_active(pf)) { + dev_err(dev, "ADQ active, can't modify Tx scheduler tree\n"); + return false; + } + + if (ice_is_dcb_active(pf)) { + dev_err(dev, "DCB active, can't modify Tx scheduler tree\n"); + return false; + } + + pi->is_custom_tx_enabled = true; + + return true; +} + /** * ice_traverse_tx_tree - traverse Tx scheduler tree * @devlink: devlink struct @@ -914,6 +972,9 @@ static int ice_devlink_rate_node_new(struct devlink_rate *rate_node, void **priv pi = ice_get_pi_from_dev_rate(rate_node); + if (!ice_enable_custom_tx(devlink_priv(rate_node->devlink))) + return -EBUSY; + /* preallocate memory for ice_sched_node */ node = devm_kzalloc(ice_hw_to_dev(pi->hw), sizeof(*node), GFP_KERNEL); *priv = node; @@ -934,6 +995,9 @@ static int ice_devlink_rate_node_del(struct devlink_rate *rate_node, void *priv, if (!rate_node->parent || !node || tc_node == node || !extack) return 0; + if (!ice_enable_custom_tx(devlink_priv(rate_node->devlink))) + return -EBUSY; + /* can't allow to delete a node with children */ if (node->num_children) return -EINVAL; @@ -950,6 +1014,9 @@ static int ice_devlink_rate_leaf_tx_max_set(struct devlink_rate *rate_leaf, void { struct ice_sched_node *node = priv; + if (!ice_enable_custom_tx(devlink_priv(rate_leaf->devlink))) + return -EBUSY; + if (!node) return 0; @@ -962,6 +1029,9 @@ static int ice_devlink_rate_leaf_tx_share_set(struct devlink_rate *rate_leaf, vo { struct ice_sched_node *node = priv; + if (!ice_enable_custom_tx(devlink_priv(rate_leaf->devlink))) + return -EBUSY; + if (!node) return 0; @@ -974,6 +1044,9 @@ static int ice_devlink_rate_leaf_tx_priority_set(struct devlink_rate *rate_leaf, { struct ice_sched_node *node = priv; + if (!ice_enable_custom_tx(devlink_priv(rate_leaf->devlink))) + return -EBUSY; + if (!node) return 0; @@ -986,6 +1059,9 @@ static int ice_devlink_rate_leaf_tx_weight_set(struct devlink_rate *rate_leaf, v { struct ice_sched_node *node = priv; + if (!ice_enable_custom_tx(devlink_priv(rate_leaf->devlink))) + return -EBUSY; + if (!node) return 0; @@ -998,6 +1074,9 @@ static int ice_devlink_rate_node_tx_max_set(struct devlink_rate *rate_node, void { struct ice_sched_node *node = priv; + if (!ice_enable_custom_tx(devlink_priv(rate_node->devlink))) + return -EBUSY; + if (!node) return 0; @@ -1010,6 +1089,9 @@ static int ice_devlink_rate_node_tx_share_set(struct devlink_rate *rate_node, vo { struct ice_sched_node *node = priv; + if (!ice_enable_custom_tx(devlink_priv(rate_node->devlink))) + return -EBUSY; + if (!node) return 0; @@ -1022,6 +1104,9 @@ static int ice_devlink_rate_node_tx_priority_set(struct devlink_rate *rate_node, { struct ice_sched_node *node = priv; + if (!ice_enable_custom_tx(devlink_priv(rate_node->devlink))) + return -EBUSY; + if (!node) return 0; @@ -1034,6 +1119,9 @@ static int ice_devlink_rate_node_tx_weight_set(struct devlink_rate *rate_node, v { struct ice_sched_node *node = priv; + if (!ice_enable_custom_tx(devlink_priv(rate_node->devlink))) + return -EBUSY; + if (!node) return 0; @@ -1059,6 +1147,9 @@ static int ice_devlink_set_parent(struct devlink_rate *devlink_rate, if (!extack) return 0; + if (!ice_enable_custom_tx(devlink_priv(devlink_rate->devlink))) + return -EBUSY; + if (!parent) { if (!node || tc_node == node || node->num_children) return -EINVAL; diff --git a/drivers/net/ethernet/intel/ice/ice_devlink.h b/drivers/net/ethernet/intel/ice/ice_devlink.h index 8bfed9ee2c4c..6ec96779f52e 100644 --- a/drivers/net/ethernet/intel/ice/ice_devlink.h +++ b/drivers/net/ethernet/intel/ice/ice_devlink.h @@ -19,5 +19,6 @@ void ice_devlink_init_regions(struct ice_pf *pf); void ice_devlink_destroy_regions(struct ice_pf *pf); int ice_devlink_rate_init_tx_topology(struct devlink *devlink, struct ice_vsi *vsi); +void ice_tear_down_devlink_rate_tree(struct ice_pf *pf); #endif /* _ICE_DEVLINK_H_ */ diff --git a/drivers/net/ethernet/intel/ice/ice_main.c b/drivers/net/ethernet/intel/ice/ice_main.c index a9fc89aebebe..d6f460ff1b72 100644 --- a/drivers/net/ethernet/intel/ice/ice_main.c +++ b/drivers/net/ethernet/intel/ice/ice_main.c @@ -8580,6 +8580,12 @@ static int ice_setup_tc_mqprio_qdisc(struct net_device *netdev, void *type_data) switch (mode) { case TC_MQPRIO_MODE_CHANNEL: + if (pf->hw.port_info->is_custom_tx_enabled) { + dev_err(dev, "Custom Tx scheduler feature enabled, can't configure ADQ\n"); + return -EBUSY; + } + ice_tear_down_devlink_rate_tree(pf); + ret = ice_validate_mqprio_qopt(vsi, mqprio_qopt); if (ret) { netdev_err(netdev, "failed to validate_mqprio_qopt(), ret %d\n", diff --git a/drivers/net/ethernet/intel/ice/ice_repr.c b/drivers/net/ethernet/intel/ice/ice_repr.c index 46f58d48318c..109761c8c858 100644 --- a/drivers/net/ethernet/intel/ice/ice_repr.c +++ b/drivers/net/ethernet/intel/ice/ice_repr.c @@ -6,6 +6,7 @@ #include "ice_devlink.h" #include "ice_sriov.h" #include "ice_tc_lib.h" +#include "ice_dcb_lib.h" /** * ice_repr_get_sw_port_id - get port ID associated with representor @@ -426,6 +427,10 @@ int ice_repr_add_for_all_vfs(struct ice_pf *pf) goto err; } + /* only export if ADQ and DCB disabled */ + if (ice_is_adq_active(pf) || ice_is_dcb_active(pf)) + return 0; + devlink = priv_to_devlink(pf); ice_devlink_rate_init_tx_topology(devlink, ice_get_main_vsi(pf)); diff --git a/drivers/net/ethernet/intel/ice/ice_type.h b/drivers/net/ethernet/intel/ice/ice_type.h index daf86cf561bc..e3f622cad425 100644 --- a/drivers/net/ethernet/intel/ice/ice_type.h +++ b/drivers/net/ethernet/intel/ice/ice_type.h @@ -715,6 +715,7 @@ struct ice_port_info { struct ice_qos_cfg qos_cfg; struct xarray sched_node_ids; u8 is_vf:1; + u8 is_custom_tx_enabled:1; }; struct ice_switch_info { From 16eb4afc5d80c40a62d26cd459e89da74d602246 Mon Sep 17 00:00:00 2001 From: Michal Wilczynski Date: Tue, 15 Nov 2022 11:48:24 +0100 Subject: [PATCH 1048/2002] ice: Add documentation for devlink-rate implementation Add documentation to a newly added devlink-rate feature. Provide some examples on how to use the commands, which netlink attributes are supported and descriptions of the attributes. Signed-off-by: Michal Wilczynski Signed-off-by: Jakub Kicinski --- Documentation/networking/devlink/ice.rst | 115 +++++++++++++++++++++++ 1 file changed, 115 insertions(+) diff --git a/Documentation/networking/devlink/ice.rst b/Documentation/networking/devlink/ice.rst index 0c89ceb8986d..890062da7820 100644 --- a/Documentation/networking/devlink/ice.rst +++ b/Documentation/networking/devlink/ice.rst @@ -254,3 +254,118 @@ Users can request an immediate capture of a snapshot via the 0000000000000210 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 $ devlink region delete pci/0000:01:00.0/device-caps snapshot 1 + +Devlink Rate +============ + +The ``ice`` driver implements devlink-rate API. It allows for offload of +the Hierarchical QoS to the hardware. It enables user to group Virtual +Functions in a tree structure and assign supported parameters: tx_share, +tx_max, tx_priority and tx_weight to each node in a tree. So effectively +user gains an ability to control how much bandwidth is allocated for each +VF group. This is later enforced by the HW. + +It is assumed that this feature is mutually exclusive with DCB performed +in FW and ADQ, or any driver feature that would trigger changes in QoS, +for example creation of the new traffic class. The driver will prevent DCB +or ADQ configuration if user started making any changes to the nodes using +devlink-rate API. To configure those features a driver reload is necessary. +Correspondingly if ADQ or DCB will get configured the driver won't export +hierarchy at all, or will remove the untouched hierarchy if those +features are enabled after the hierarchy is exported, but before any +changes are made. + +This feature is also dependent on switchdev being enabled in the system. +It's required bacause devlink-rate requires devlink-port objects to be +present, and those objects are only created in switchdev mode. + +If the driver is set to the switchdev mode, it will export internal +hierarchy the moment VF's are created. Root of the tree is always +represented by the node_0. This node can't be deleted by the user. Leaf +nodes and nodes with children also can't be deleted. + +.. list-table:: Attributes supported + :widths: 15 85 + + * - Name + - Description + * - ``tx_max`` + - maximum bandwidth to be consumed by the tree Node. Rate Limit is + an absolute number specifying a maximum amount of bytes a Node may + consume during the course of one second. Rate limit guarantees + that a link will not oversaturate the receiver on the remote end + and also enforces an SLA between the subscriber and network + provider. + * - ``tx_share`` + - minimum bandwidth allocated to a tree node when it is not blocked. + It specifies an absolute BW. While tx_max defines the maximum + bandwidth the node may consume, the tx_share marks committed BW + for the Node. + * - ``tx_priority`` + - allows for usage of strict priority arbiter among siblings. This + arbitration scheme attempts to schedule nodes based on their + priority as long as the nodes remain within their bandwidth limit. + Range 0-7. Nodes with priority 7 have the highest priority and are + selected first, while nodes with priority 0 have the lowest + priority. Nodes that have the same priority are treated equally. + * - ``tx_weight`` + - allows for usage of Weighted Fair Queuing arbitration scheme among + siblings. This arbitration scheme can be used simultaneously with + the strict priority. Range 1-200. Only relative values mater for + arbitration. + +``tx_priority`` and ``tx_weight`` can be used simultaneously. In that case +nodes with the same priority form a WFQ subgroup in the sibling group +and arbitration among them is based on assigned weights. + +.. code:: shell + + # enable switchdev + $ devlink dev eswitch set pci/0000:4b:00.0 mode switchdev + + # at this point driver should export internal hierarchy + $ echo 2 > /sys/class/net/ens785np0/device/sriov_numvfs + + $ devlink port function rate show + pci/0000:4b:00.0/node_25: type node parent node_24 + pci/0000:4b:00.0/node_24: type node parent node_0 + pci/0000:4b:00.0/node_32: type node parent node_31 + pci/0000:4b:00.0/node_31: type node parent node_30 + pci/0000:4b:00.0/node_30: type node parent node_16 + pci/0000:4b:00.0/node_19: type node parent node_18 + pci/0000:4b:00.0/node_18: type node parent node_17 + pci/0000:4b:00.0/node_17: type node parent node_16 + pci/0000:4b:00.0/node_14: type node parent node_5 + pci/0000:4b:00.0/node_5: type node parent node_3 + pci/0000:4b:00.0/node_13: type node parent node_4 + pci/0000:4b:00.0/node_12: type node parent node_4 + pci/0000:4b:00.0/node_11: type node parent node_4 + pci/0000:4b:00.0/node_10: type node parent node_4 + pci/0000:4b:00.0/node_9: type node parent node_4 + pci/0000:4b:00.0/node_8: type node parent node_4 + pci/0000:4b:00.0/node_7: type node parent node_4 + pci/0000:4b:00.0/node_6: type node parent node_4 + pci/0000:4b:00.0/node_4: type node parent node_3 + pci/0000:4b:00.0/node_3: type node parent node_16 + pci/0000:4b:00.0/node_16: type node parent node_15 + pci/0000:4b:00.0/node_15: type node parent node_0 + pci/0000:4b:00.0/node_2: type node parent node_1 + pci/0000:4b:00.0/node_1: type node parent node_0 + pci/0000:4b:00.0/node_0: type node + pci/0000:4b:00.0/1: type leaf parent node_25 + pci/0000:4b:00.0/2: type leaf parent node_25 + + # let's create some custom node + $ devlink port function rate add pci/0000:4b:00.0/node_custom parent node_0 + + # second custom node + $ devlink port function rate add pci/0000:4b:00.0/node_custom_1 parent node_custom + + # reassign second VF to newly created branch + $ devlink port function rate set pci/0000:4b:00.0/2 parent node_custom_1 + + # assign tx_weight to the VF + $ devlink port function rate set pci/0000:4b:00.0/2 tx_weight 5 + + # assign tx_share to the VF + $ devlink port function rate set pci/0000:4b:00.0/2 tx_share 500Mbps From 242dd64375b80aecd7eacb1e7c4b052c481c15e7 Mon Sep 17 00:00:00 2001 From: Michal Wilczynski Date: Tue, 15 Nov 2022 11:48:25 +0100 Subject: [PATCH 1049/2002] Documentation: Add documentation for new devlink-rate attributes Provide documentation for newly introduced netlink attributes for devlink-rate: tx_priority and tx_weight. Mention the possibility to export tree from the driver. Signed-off-by: Michal Wilczynski Signed-off-by: Jakub Kicinski --- .../networking/devlink/devlink-port.rst | 33 ++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/Documentation/networking/devlink/devlink-port.rst b/Documentation/networking/devlink/devlink-port.rst index 7627b1da01f2..643f5903d1d8 100644 --- a/Documentation/networking/devlink/devlink-port.rst +++ b/Documentation/networking/devlink/devlink-port.rst @@ -191,13 +191,44 @@ API allows to configure following rate object's parameters: ``tx_max`` Maximum TX rate value. +``tx_priority`` + Allows for usage of strict priority arbiter among siblings. This + arbitration scheme attempts to schedule nodes based on their priority + as long as the nodes remain within their bandwidth limit. The higher the + priority the higher the probability that the node will get selected for + scheduling. + +``tx_weight`` + Allows for usage of Weighted Fair Queuing arbitration scheme among + siblings. This arbitration scheme can be used simultaneously with the + strict priority. As a node is configured with a higher rate it gets more + BW relative to it's siblings. Values are relative like a percentage + points, they basically tell how much BW should node take relative to + it's siblings. + ``parent`` Parent node name. Parent node rate limits are considered as additional limits to all node children limits. ``tx_max`` is an upper limit for children. ``tx_share`` is a total bandwidth distributed among children. +``tx_priority`` and ``tx_weight`` can be used simultaneously. In that case +nodes with the same priority form a WFQ subgroup in the sibling group +and arbitration among them is based on assigned weights. + +Arbitration flow from the high level: +#. Choose a node, or group of nodes with the highest priority that stays + within the BW limit and are not blocked. Use ``tx_priority`` as a + parameter for this arbitration. +#. If group of nodes have the same priority perform WFQ arbitration on + that subgroup. Use ``tx_weight`` as a parameter for this arbitration. +#. Select the winner node, and continue arbitration flow among it's children, + until leaf node is reached, and the winner is established. +#. If all the nodes from the highest priority sub-group are satisfied, or + overused their assigned BW, move to the lower priority nodes. + Driver implementations are allowed to support both or either rate object types -and setting methods of their parameters. +and setting methods of their parameters. Additionally driver implementation +may export nodes/leafs and their child-parent relationships. Terms and Definitions ===================== From b78c4162823dddc621649edae704b14c5973298c Mon Sep 17 00:00:00 2001 From: Xin Long Date: Tue, 15 Nov 2022 10:39:53 -0500 Subject: [PATCH 1050/2002] sctp: change to include linux/sctp.h in net/sctp/checksum.h Currently "net/sctp/checksum.h" including "net/sctp/sctp.h" is included in quite some places in netfilter and openswitch and net/sched. It's not necessary to include "net/sctp/sctp.h" if a module does not have dependence on SCTP, "linux/sctp.h" is the right one to include. Signed-off-by: Xin Long Reviewed-by: Saeed Mahameed Link: https://lore.kernel.org/r/ca7ea96d62a26732f0491153c3979dc1c0d8d34a.1668526793.git.lucien.xin@gmail.com Signed-off-by: Jakub Kicinski --- include/net/sctp/checksum.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/net/sctp/checksum.h b/include/net/sctp/checksum.h index 5a9bb09f32b6..f514a0aa849e 100644 --- a/include/net/sctp/checksum.h +++ b/include/net/sctp/checksum.h @@ -24,7 +24,7 @@ #define __sctp_checksum_h__ #include -#include +#include #include #include From 647541ea06a7bbbcf941e501c726b3e26328c102 Mon Sep 17 00:00:00 2001 From: Xin Long Date: Tue, 15 Nov 2022 10:40:21 -0500 Subject: [PATCH 1051/2002] sctp: move SCTP_PAD4 and SCTP_TRUNC4 to linux/sctp.h Move these two macros from net/sctp/sctp.h to linux/sctp.h, so that it will be enough to include only linux/sctp.h in nft_exthdr.c and xt_sctp.c. It should not include "net/sctp/sctp.h" if a module does not have a dependence on SCTP module. Signed-off-by: Xin Long Reviewed-by: Saeed Mahameed Link: https://lore.kernel.org/r/ef6468a687f36da06f575c2131cd4612f6b7be88.1668526821.git.lucien.xin@gmail.com Signed-off-by: Jakub Kicinski --- include/linux/sctp.h | 5 +++++ include/net/sctp/sctp.h | 5 ----- net/netfilter/nft_exthdr.c | 1 - net/netfilter/xt_sctp.c | 1 - 4 files changed, 5 insertions(+), 7 deletions(-) diff --git a/include/linux/sctp.h b/include/linux/sctp.h index a86e852507b3..358dc08e0831 100644 --- a/include/linux/sctp.h +++ b/include/linux/sctp.h @@ -820,4 +820,9 @@ struct sctp_new_encap_port_hdr { __be16 new_port; }; +/* Round an int up to the next multiple of 4. */ +#define SCTP_PAD4(s) (((s)+3)&~3) +/* Truncate to the previous multiple of 4. */ +#define SCTP_TRUNC4(s) ((s)&~3) + #endif /* __LINUX_SCTP_H__ */ diff --git a/include/net/sctp/sctp.h b/include/net/sctp/sctp.h index a04999ee99b0..01d904b34cf0 100644 --- a/include/net/sctp/sctp.h +++ b/include/net/sctp/sctp.h @@ -67,11 +67,6 @@ #define SCTP_PROTOSW_FLAG INET_PROTOSW_PERMANENT #endif -/* Round an int up to the next multiple of 4. */ -#define SCTP_PAD4(s) (((s)+3)&~3) -/* Truncate to the previous multiple of 4. */ -#define SCTP_TRUNC4(s) ((s)&~3) - /* * Function declarations. */ diff --git a/net/netfilter/nft_exthdr.c b/net/netfilter/nft_exthdr.c index ed929d0d37ce..a54a7f772cec 100644 --- a/net/netfilter/nft_exthdr.c +++ b/net/netfilter/nft_exthdr.c @@ -13,7 +13,6 @@ #include #include #include -#include #include struct nft_exthdr { diff --git a/net/netfilter/xt_sctp.c b/net/netfilter/xt_sctp.c index 680015ba7cb6..e8961094a282 100644 --- a/net/netfilter/xt_sctp.c +++ b/net/netfilter/xt_sctp.c @@ -4,7 +4,6 @@ #include #include #include -#include #include #include From 4ca0c6474f71cb136acf103aa963715e1bd2939a Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Wed, 16 Nov 2022 01:32:52 -0600 Subject: [PATCH 1052/2002] dt-bindings: net: qcom,ipa: deprecate modem-init GSI firmware for IPA must be loaded during initialization, either by the AP or by the modem. The loader is currently specified based on whether the Boolean modem-init property is present. Instead, use a new property with an enumerated value to indicate explicitly how GSI firmware gets loaded. With this in place, a third approach can be added in an upcoming patch. The new qcom,gsi-loader property has two defined values: - self: The AP loads GSI firmware - modem: The modem loads GSI firmware The modem-init property must still be supported, but is now marked deprecated. Update the example so it represents the SC7180 SoC, and provide examples for the qcom,gsi-loader, memory-region, and firmware-name properties. Signed-off-by: Alex Elder Reviewed-by: Krzysztof Kozlowski Signed-off-by: Jakub Kicinski --- .../devicetree/bindings/net/qcom,ipa.yaml | 76 ++++++++++++++----- 1 file changed, 55 insertions(+), 21 deletions(-) diff --git a/Documentation/devicetree/bindings/net/qcom,ipa.yaml b/Documentation/devicetree/bindings/net/qcom,ipa.yaml index e752b76192df..d0f34763b938 100644 --- a/Documentation/devicetree/bindings/net/qcom,ipa.yaml +++ b/Documentation/devicetree/bindings/net/qcom,ipa.yaml @@ -124,19 +124,29 @@ properties: - const: ipa-clock-enabled-valid - const: ipa-clock-enabled + qcom,gsi-loader: + enum: + - self + - modem + description: + Indicates how GSI firmware should be loaded. If the AP loads + and validates GSI firmware, this property has value "self". + If the modem does this, this property has value "modem". + modem-init: + deprecated: true type: boolean description: - If present, it indicates that the modem is responsible for - performing early IPA initialization, including loading and - validating firwmare used by the GSI. + This is the older (deprecated) way of indicating how GSI firmware + should be loaded. If present, the modem loads GSI firmware; if + absent, the AP loads GSI firmware. memory-region: maxItems: 1 description: If present, a phandle for a reserved memory area that holds the firmware passed to Trust Zone for authentication. Required - when Trust Zone (not the modem) performs early initialization. + when the AP (not the modem) performs early initialization. firmware-name: $ref: /schemas/types.yaml#/definitions/string @@ -155,15 +165,36 @@ required: - interconnects - qcom,smem-states -# If modem-init is not present, the AP loads GSI firmware, and -# memory-region must be specified -if: - not: - required: - - modem-init -then: - required: - - memory-region +allOf: + # If qcom,gsi-loader is present, modem-init must not be present + - if: + required: + - qcom,gsi-loader + then: + properties: + modem-init: false + + # If qcom,gsi-loader is "self", the AP loads GSI firmware, and + # memory-region must be specified + if: + properties: + qcom,gsi-loader: + contains: + const: self + then: + required: + - memory-region + else: + # If qcom,gsi-loader is not present, we use deprecated behavior. + # If modem-init is not present, the AP loads GSI firmware, and + # memory-region must be specified. + if: + not: + required: + - modem-init + then: + required: + - memory-region additionalProperties: false @@ -194,14 +225,17 @@ examples: }; ipa@1e40000 { - compatible = "qcom,sdm845-ipa"; + compatible = "qcom,sc7180-ipa"; - modem-init; + qcom,gsi-loader = "self"; + memory-region = <&ipa_fw_mem>; + firmware-name = "qcom/sc7180-trogdor/modem/modem.mdt"; - iommus = <&apps_smmu 0x720 0x3>; + iommus = <&apps_smmu 0x440 0x0>, + <&apps_smmu 0x442 0x0>; reg = <0x1e40000 0x7000>, - <0x1e47000 0x2000>, - <0x1e04000 0x2c000>; + <0x1e47000 0x2000>, + <0x1e04000 0x2c000>; reg-names = "ipa-reg", "ipa-shared", "gsi"; @@ -219,9 +253,9 @@ examples: clock-names = "core"; interconnects = - <&rsc_hlos MASTER_IPA &rsc_hlos SLAVE_EBI1>, - <&rsc_hlos MASTER_IPA &rsc_hlos SLAVE_IMEM>, - <&rsc_hlos MASTER_APPSS_PROC &rsc_hlos SLAVE_IPA_CFG>; + <&aggre2_noc MASTER_IPA 0 &mc_virt SLAVE_EBI1 0>, + <&aggre2_noc MASTER_IPA 0 &system_noc SLAVE_IMEM 0>, + <&gem_noc MASTER_APPSS_PROC 0 &config_noc SLAVE_IPA_CFG 0>; interconnect-names = "memory", "imem", "config"; From 50f803d4aa7173d05a8d5106704dd1b2c68a4cb4 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Wed, 16 Nov 2022 01:32:53 -0600 Subject: [PATCH 1053/2002] net: ipa: encapsulate decision about firmware load The GSI layer used for IPA requires firmware to be loaded. Currently either the AP or the modem loads the firmware, distinguished by whether the "modem-init" Device Tree property is defined. Some newer systems implement a third option. In preparation for that, encapsulate the code that determines how the GSI firmware gets loaded in a new function, ipa_firmware_loader(). Signed-off-by: Alex Elder Signed-off-by: Jakub Kicinski --- drivers/net/ipa/ipa_main.c | 39 ++++++++++++++++++++++++++++++-------- 1 file changed, 31 insertions(+), 8 deletions(-) diff --git a/drivers/net/ipa/ipa_main.c b/drivers/net/ipa/ipa_main.c index ebb6c9b311eb..9e43b79d233e 100644 --- a/drivers/net/ipa/ipa_main.c +++ b/drivers/net/ipa/ipa_main.c @@ -81,6 +81,19 @@ /* Divider for 19.2 MHz crystal oscillator clock to get common timer clock */ #define IPA_XO_CLOCK_DIVIDER 192 /* 1 is subtracted where used */ +/** + * enum ipa_firmware_loader: How GSI firmware gets loaded + * + * @IPA_LOADER_DEFER: System not ready; try again later + * @IPA_LOADER_SELF: AP loads GSI firmware + * @IPA_LOADER_MODEM: Modem loads GSI firmware, signals when done + */ +enum ipa_firmware_loader { + IPA_LOADER_DEFER, + IPA_LOADER_SELF, + IPA_LOADER_MODEM, +}; + /** * ipa_setup() - Set up IPA hardware * @ipa: IPA pointer @@ -696,6 +709,18 @@ static void ipa_validate_build(void) BUILD_BUG_ON(!ipa_aggr_granularity_val(IPA_AGGR_GRANULARITY)); } +static enum ipa_firmware_loader ipa_firmware_loader(struct device *dev) +{ + if (of_property_read_bool(dev->of_node, "modem-init")) + return IPA_LOADER_MODEM; + + /* We need Trust Zone to load firmware; make sure it's available */ + if (qcom_scm_is_available()) + return IPA_LOADER_SELF; + + return IPA_LOADER_DEFER; +} + /** * ipa_probe() - IPA platform driver probe function * @pdev: Platform device pointer @@ -722,9 +747,9 @@ static void ipa_validate_build(void) static int ipa_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; + enum ipa_firmware_loader loader; const struct ipa_data *data; struct ipa_power *power; - bool modem_init; struct ipa *ipa; int ret; @@ -747,11 +772,9 @@ static int ipa_probe(struct platform_device *pdev) return -EINVAL; } - /* If we need Trust Zone, make sure it's available */ - modem_init = of_property_read_bool(dev->of_node, "modem-init"); - if (!modem_init) - if (!qcom_scm_is_available()) - return -EPROBE_DEFER; + loader = ipa_firmware_loader(dev); + if (loader == IPA_LOADER_DEFER) + return -EPROBE_DEFER; /* The clock and interconnects might not be ready when we're * probed, so might return -EPROBE_DEFER. @@ -796,7 +819,7 @@ static int ipa_probe(struct platform_device *pdev) if (ret) goto err_endpoint_exit; - ret = ipa_smp2p_init(ipa, modem_init); + ret = ipa_smp2p_init(ipa, loader == IPA_LOADER_MODEM); if (ret) goto err_table_exit; @@ -815,7 +838,7 @@ static int ipa_probe(struct platform_device *pdev) * call to ipa_setup() when it has finished. In that case we're * done here. */ - if (modem_init) + if (loader == IPA_LOADER_MODEM) goto done; /* Otherwise we need to load the firmware and have Trust Zone validate From 07f2f8e1b7478ff1e1b3186563199305af2cd86a Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Wed, 16 Nov 2022 01:32:54 -0600 Subject: [PATCH 1054/2002] net: ipa: introduce "qcom,gsi-loader" property Introduce a new way of specifying how the GSI firmware gets loaded for IPA. Currently, this is indicated by the presence or absence of the Boolean "modem-init" Device Tree property. The new property must have a value--either "self" or "modem"--which indicates whether the AP or modem is the GSI firmware loader, respectively. For legacy systems, the new property will not exist, and the "modem-init" property will be used. For newer systems, the "qcom,gsi-loader" property *must* exist, and must have one of the two prescribed values. It is an error to have both properties defined, and it is an error for the new property to have an unrecognized value. Signed-off-by: Alex Elder Signed-off-by: Jakub Kicinski --- drivers/net/ipa/ipa_main.c | 44 ++++++++++++++++++++++++++++++++------ 1 file changed, 37 insertions(+), 7 deletions(-) diff --git a/drivers/net/ipa/ipa_main.c b/drivers/net/ipa/ipa_main.c index 9e43b79d233e..214e524dce79 100644 --- a/drivers/net/ipa/ipa_main.c +++ b/drivers/net/ipa/ipa_main.c @@ -87,11 +87,13 @@ * @IPA_LOADER_DEFER: System not ready; try again later * @IPA_LOADER_SELF: AP loads GSI firmware * @IPA_LOADER_MODEM: Modem loads GSI firmware, signals when done + * @IPA_LOADER_INVALID: GSI firmware loader specification is invalid */ enum ipa_firmware_loader { IPA_LOADER_DEFER, IPA_LOADER_SELF, IPA_LOADER_MODEM, + IPA_LOADER_INVALID, }; /** @@ -711,9 +713,37 @@ static void ipa_validate_build(void) static enum ipa_firmware_loader ipa_firmware_loader(struct device *dev) { - if (of_property_read_bool(dev->of_node, "modem-init")) + bool modem_init; + const char *str; + int ret; + + /* Look up the old and new properties by name */ + modem_init = of_property_read_bool(dev->of_node, "modem-init"); + ret = of_property_read_string(dev->of_node, "qcom,gsi-loader", &str); + + /* If the new property doesn't exist, it's legacy behavior */ + if (ret == -EINVAL) { + if (modem_init) + return IPA_LOADER_MODEM; + goto out_self; + } + + /* Any other error on the new property means it's poorly defined */ + if (ret) + return IPA_LOADER_INVALID; + + /* New property value exists; if old one does too, that's invalid */ + if (modem_init) + return IPA_LOADER_INVALID; + + /* Modem loads GSI firmware for "modem" */ + if (!strcmp(str, "modem")) return IPA_LOADER_MODEM; + /* Any value other than "self" is an error */ + if (strcmp(str, "self")) + return IPA_LOADER_INVALID; +out_self: /* We need Trust Zone to load firmware; make sure it's available */ if (qcom_scm_is_available()) return IPA_LOADER_SELF; @@ -773,6 +803,8 @@ static int ipa_probe(struct platform_device *pdev) } loader = ipa_firmware_loader(dev); + if (loader == IPA_LOADER_INVALID) + return -EINVAL; if (loader == IPA_LOADER_DEFER) return -EPROBE_DEFER; @@ -834,20 +866,18 @@ static int ipa_probe(struct platform_device *pdev) dev_info(dev, "IPA driver initialized"); - /* If the modem is doing early initialization, it will trigger a - * call to ipa_setup() when it has finished. In that case we're - * done here. + /* If the modem is loading GSI firmware, it will trigger a call to + * ipa_setup() when it has finished. In that case we're done here. */ if (loader == IPA_LOADER_MODEM) goto done; - /* Otherwise we need to load the firmware and have Trust Zone validate - * and install it. If that succeeds we can proceed with setup. - */ + /* The AP is loading GSI firmware; do so now */ ret = ipa_firmware_load(dev); if (ret) goto err_deconfig; + /* GSI firmware is loaded; proceed to setup */ ret = ipa_setup(ipa); if (ret) goto err_deconfig; From a49c3ab7d75fe51bfaa82b7f307f1d79a827d914 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Wed, 16 Nov 2022 01:32:55 -0600 Subject: [PATCH 1055/2002] dt-bindings: net: qcom,ipa: support skipping GSI firmware load Add a new enumerated value to those defined for the qcom,gsi-loader property. If the qcom,gsi-loader is "skip", the GSI firmware will already be loaded, so neither the AP nor modem is required to load GSI firmware. Signed-off-by: Alex Elder Reviewed-by: Krzysztof Kozlowski Signed-off-by: Jakub Kicinski --- Documentation/devicetree/bindings/net/qcom,ipa.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Documentation/devicetree/bindings/net/qcom,ipa.yaml b/Documentation/devicetree/bindings/net/qcom,ipa.yaml index d0f34763b938..9e81b9ec7cfd 100644 --- a/Documentation/devicetree/bindings/net/qcom,ipa.yaml +++ b/Documentation/devicetree/bindings/net/qcom,ipa.yaml @@ -128,10 +128,12 @@ properties: enum: - self - modem + - skip description: Indicates how GSI firmware should be loaded. If the AP loads and validates GSI firmware, this property has value "self". If the modem does this, this property has value "modem". + Otherwise, "skip" means GSI firmware loading is not required. modem-init: deprecated: true From 7569805ec26e9df454ed97a4521003cde53899bb Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Wed, 16 Nov 2022 01:32:56 -0600 Subject: [PATCH 1056/2002] net: ipa: permit GSI firmware loading to be skipped Define a new value "skip" for the "qcom,gsi-loader" Device Tree property. If used, it indicates that neither the AP nor the modem need to load GSI firmware (because it has already been loaded--for example by the boot loader). Signed-off-by: Alex Elder Signed-off-by: Jakub Kicinski --- drivers/net/ipa/ipa_main.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/drivers/net/ipa/ipa_main.c b/drivers/net/ipa/ipa_main.c index 214e524dce79..8f20825675a1 100644 --- a/drivers/net/ipa/ipa_main.c +++ b/drivers/net/ipa/ipa_main.c @@ -87,12 +87,14 @@ * @IPA_LOADER_DEFER: System not ready; try again later * @IPA_LOADER_SELF: AP loads GSI firmware * @IPA_LOADER_MODEM: Modem loads GSI firmware, signals when done + * @IPA_LOADER_SKIP: Neither AP nor modem need to load GSI firmware * @IPA_LOADER_INVALID: GSI firmware loader specification is invalid */ enum ipa_firmware_loader { IPA_LOADER_DEFER, IPA_LOADER_SELF, IPA_LOADER_MODEM, + IPA_LOADER_SKIP, IPA_LOADER_INVALID, }; @@ -740,6 +742,10 @@ static enum ipa_firmware_loader ipa_firmware_loader(struct device *dev) if (!strcmp(str, "modem")) return IPA_LOADER_MODEM; + /* No GSI firmware load is needed for "skip" */ + if (!strcmp(str, "skip")) + return IPA_LOADER_SKIP; + /* Any value other than "self" is an error */ if (strcmp(str, "self")) return IPA_LOADER_INVALID; @@ -872,10 +878,12 @@ static int ipa_probe(struct platform_device *pdev) if (loader == IPA_LOADER_MODEM) goto done; - /* The AP is loading GSI firmware; do so now */ - ret = ipa_firmware_load(dev); - if (ret) - goto err_deconfig; + if (loader == IPA_LOADER_SELF) { + /* The AP is loading GSI firmware; do so now */ + ret = ipa_firmware_load(dev); + if (ret) + goto err_deconfig; + } /* Otherwise loader == IPA_LOADER_SKIP */ /* GSI firmware is loaded; proceed to setup */ ret = ipa_setup(ipa); From 0dc33c65835d456e42de509ba17563caa6a9d4c1 Mon Sep 17 00:00:00 2001 From: Antoine Tenart Date: Tue, 15 Nov 2022 16:44:51 +0100 Subject: [PATCH 1057/2002] net: phy: mscc: macsec: do not copy encryption keys Following 1b16b3fdf675 ("net: phy: mscc: macsec: clear encryption keys when freeing a flow"), go one step further and instead of calling memzero_explicit on the key when freeing a flow, simply not copy the key in the first place as it's only used when a new flow is set up. Signed-off-by: Antoine Tenart Signed-off-by: David S. Miller --- drivers/net/phy/mscc/mscc_macsec.c | 57 ++++++++++++++++-------------- drivers/net/phy/mscc/mscc_macsec.h | 2 -- 2 files changed, 30 insertions(+), 29 deletions(-) diff --git a/drivers/net/phy/mscc/mscc_macsec.c b/drivers/net/phy/mscc/mscc_macsec.c index f81b077618f4..018253a573b8 100644 --- a/drivers/net/phy/mscc/mscc_macsec.c +++ b/drivers/net/phy/mscc/mscc_macsec.c @@ -501,8 +501,7 @@ static u32 vsc8584_macsec_flow_context_id(struct macsec_flow *flow) } /* Derive the AES key to get a key for the hash autentication */ -static int vsc8584_macsec_derive_key(const u8 key[MACSEC_MAX_KEY_LEN], - u16 key_len, u8 hkey[16]) +static int vsc8584_macsec_derive_key(const u8 *key, u16 key_len, u8 hkey[16]) { const u8 input[AES_BLOCK_SIZE] = {0}; struct crypto_aes_ctx ctx; @@ -518,7 +517,8 @@ static int vsc8584_macsec_derive_key(const u8 key[MACSEC_MAX_KEY_LEN], } static int vsc8584_macsec_transformation(struct phy_device *phydev, - struct macsec_flow *flow) + struct macsec_flow *flow, + const u8 *key) { struct vsc8531_private *priv = phydev->priv; enum macsec_bank bank = flow->bank; @@ -527,7 +527,7 @@ static int vsc8584_macsec_transformation(struct phy_device *phydev, u8 hkey[16]; u64 sci; - ret = vsc8584_macsec_derive_key(flow->key, priv->secy->key_len, hkey); + ret = vsc8584_macsec_derive_key(key, priv->secy->key_len, hkey); if (ret) return ret; @@ -563,7 +563,7 @@ static int vsc8584_macsec_transformation(struct phy_device *phydev, for (i = 0; i < priv->secy->key_len / sizeof(u32); i++) vsc8584_macsec_phy_write(phydev, bank, MSCC_MS_XFORM_REC(index, rec++), - ((u32 *)flow->key)[i]); + ((u32 *)key)[i]); /* Set the authentication key */ for (i = 0; i < 4; i++) @@ -632,28 +632,14 @@ static void vsc8584_macsec_free_flow(struct vsc8531_private *priv, list_del(&flow->list); clear_bit(flow->index, bitmap); - memzero_explicit(flow->key, sizeof(flow->key)); kfree(flow); } -static int vsc8584_macsec_add_flow(struct phy_device *phydev, - struct macsec_flow *flow, bool update) +static void vsc8584_macsec_add_flow(struct phy_device *phydev, + struct macsec_flow *flow) { - int ret; - flow->port = MSCC_MS_PORT_CONTROLLED; vsc8584_macsec_flow(phydev, flow); - - if (update) - return 0; - - ret = vsc8584_macsec_transformation(phydev, flow); - if (ret) { - vsc8584_macsec_free_flow(phydev->priv, flow); - return ret; - } - - return 0; } static int vsc8584_macsec_default_flows(struct phy_device *phydev) @@ -706,6 +692,7 @@ static int __vsc8584_macsec_add_rxsa(struct macsec_context *ctx, { struct phy_device *phydev = ctx->phydev; struct vsc8531_private *priv = phydev->priv; + int ret; flow->assoc_num = ctx->sa.assoc_num; flow->rx_sa = ctx->sa.rx_sa; @@ -717,19 +704,39 @@ static int __vsc8584_macsec_add_rxsa(struct macsec_context *ctx, if (priv->secy->validate_frames != MACSEC_VALIDATE_DISABLED) flow->match.untagged = 1; - return vsc8584_macsec_add_flow(phydev, flow, update); + vsc8584_macsec_add_flow(phydev, flow); + + if (update) + return 0; + + ret = vsc8584_macsec_transformation(phydev, flow, ctx->sa.key); + if (ret) + vsc8584_macsec_free_flow(phydev->priv, flow); + + return ret; } static int __vsc8584_macsec_add_txsa(struct macsec_context *ctx, struct macsec_flow *flow, bool update) { + int ret; + flow->assoc_num = ctx->sa.assoc_num; flow->tx_sa = ctx->sa.tx_sa; /* Always match untagged packets on egress */ flow->match.untagged = 1; - return vsc8584_macsec_add_flow(ctx->phydev, flow, update); + vsc8584_macsec_add_flow(ctx->phydev, flow); + + if (update) + return 0; + + ret = vsc8584_macsec_transformation(ctx->phydev, flow, ctx->sa.key); + if (ret) + vsc8584_macsec_free_flow(ctx->phydev->priv, flow); + + return ret; } static int vsc8584_macsec_dev_open(struct macsec_context *ctx) @@ -829,8 +836,6 @@ static int vsc8584_macsec_add_rxsa(struct macsec_context *ctx) if (IS_ERR(flow)) return PTR_ERR(flow); - memcpy(flow->key, ctx->sa.key, priv->secy->key_len); - ret = __vsc8584_macsec_add_rxsa(ctx, flow, false); if (ret) return ret; @@ -882,8 +887,6 @@ static int vsc8584_macsec_add_txsa(struct macsec_context *ctx) if (IS_ERR(flow)) return PTR_ERR(flow); - memcpy(flow->key, ctx->sa.key, priv->secy->key_len); - ret = __vsc8584_macsec_add_txsa(ctx, flow, false); if (ret) return ret; diff --git a/drivers/net/phy/mscc/mscc_macsec.h b/drivers/net/phy/mscc/mscc_macsec.h index 453304bae778..21ce3b892f7f 100644 --- a/drivers/net/phy/mscc/mscc_macsec.h +++ b/drivers/net/phy/mscc/mscc_macsec.h @@ -81,8 +81,6 @@ struct macsec_flow { /* Highest takes precedence [0..15] */ u8 priority; - u8 key[MACSEC_MAX_KEY_LEN]; - union { struct macsec_rx_sa *rx_sa; struct macsec_tx_sa *tx_sa; From 0b6ffefbb018462856dd8ee22735f8c9aecb2c80 Mon Sep 17 00:00:00 2001 From: Jiawen Wu Date: Wed, 16 Nov 2022 09:58:35 +0800 Subject: [PATCH 1058/2002] net: libwx: Fix dead code for duplicate check Fix duplicate check on polling timeout. Fixes: 1efa9bfe58c5 ("net: libwx: Implement interaction with firmware") Signed-off-by: Jiawen Wu Reviewed-by: Leon Romanovsky Signed-off-by: David S. Miller --- drivers/net/ethernet/wangxun/libwx/wx_hw.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/net/ethernet/wangxun/libwx/wx_hw.c b/drivers/net/ethernet/wangxun/libwx/wx_hw.c index 1eb7388f1dd5..c57dc3238b3f 100644 --- a/drivers/net/ethernet/wangxun/libwx/wx_hw.c +++ b/drivers/net/ethernet/wangxun/libwx/wx_hw.c @@ -203,8 +203,6 @@ int wx_host_interface_command(struct wx_hw *wxhw, u32 *buffer, status = read_poll_timeout(rd32, hicr, hicr & WX_MNG_MBOX_CTL_FWRDY, 1000, timeout * 1000, false, wxhw, WX_MNG_MBOX_CTL); - if (status) - goto rel_out; /* Check command completion */ if (status) { From 26943aefa8704ca7871c34a2d1b2b2a418372666 Mon Sep 17 00:00:00 2001 From: Xin Long Date: Wed, 16 Nov 2022 15:01:16 -0500 Subject: [PATCH 1059/2002] sctp: verify the bind address with the tb_id from l3mdev After binding to a l3mdev, it should use the route table from the corresponding VRF to verify the addr when binding to an address. Note ipv6 doesn't need it, as binding to ipv6 address does not verify the addr with route lookup. Signed-off-by: Xin Long Signed-off-by: David S. Miller --- net/sctp/protocol.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/net/sctp/protocol.c b/net/sctp/protocol.c index bcd3384ab07a..dbfe7d1000c2 100644 --- a/net/sctp/protocol.c +++ b/net/sctp/protocol.c @@ -351,10 +351,13 @@ static int sctp_v4_addr_valid(union sctp_addr *addr, /* Should this be available for binding? */ static int sctp_v4_available(union sctp_addr *addr, struct sctp_sock *sp) { - struct net *net = sock_net(&sp->inet.sk); - int ret = inet_addr_type(net, addr->v4.sin_addr.s_addr); - + struct sock *sk = &sp->inet.sk; + struct net *net = sock_net(sk); + int tb_id = RT_TABLE_LOCAL; + int ret; + tb_id = l3mdev_fib_table_by_index(net, sk->sk_bound_dev_if) ?: tb_id; + ret = inet_addr_type_table(net, addr->v4.sin_addr.s_addr, tb_id); if (addr->v4.sin_addr.s_addr != htonl(INADDR_ANY) && ret != RTN_LOCAL && !sp->inet.freebind && From 6fe1e52490a91cb23f6b3aafc93e7c5beb99f862 Mon Sep 17 00:00:00 2001 From: Xin Long Date: Wed, 16 Nov 2022 15:01:17 -0500 Subject: [PATCH 1060/2002] sctp: check ipv6 addr with sk_bound_dev if set When binding to an ipv6 address, it calls ipv6_chk_addr() to check if this address is on any dev. If a socket binds to a l3mdev but no dev is passed to do this check, all l3mdev and slaves will be skipped and the check will fail. This patch is to pass the bound_dev to make sure the devices under the same l3mdev can be returned in ipv6_chk_addr(). When the bound_dev is not a l3mdev or l3slave, l3mdev_master_dev_rcu() will return NULL in __ipv6_chk_addr_and_flags(), it will keep compitable with before when NULL dev was passed. Signed-off-by: Xin Long Signed-off-by: David S. Miller --- net/sctp/ipv6.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/net/sctp/ipv6.c b/net/sctp/ipv6.c index d081858c2d07..e6274cdbdf6c 100644 --- a/net/sctp/ipv6.c +++ b/net/sctp/ipv6.c @@ -680,9 +680,11 @@ static int sctp_v6_is_any(const union sctp_addr *addr) /* Should this be available for binding? */ static int sctp_v6_available(union sctp_addr *addr, struct sctp_sock *sp) { - int type; - struct net *net = sock_net(&sp->inet.sk); const struct in6_addr *in6 = (const struct in6_addr *)&addr->v6.sin6_addr; + struct sock *sk = &sp->inet.sk; + struct net *net = sock_net(sk); + struct net_device *dev = NULL; + int type; type = ipv6_addr_type(in6); if (IPV6_ADDR_ANY == type) @@ -696,8 +698,14 @@ static int sctp_v6_available(union sctp_addr *addr, struct sctp_sock *sp) if (!(type & IPV6_ADDR_UNICAST)) return 0; + if (sk->sk_bound_dev_if) { + dev = dev_get_by_index_rcu(net, sk->sk_bound_dev_if); + if (!dev) + return 0; + } + return ipv6_can_nonlocal_bind(net, &sp->inet) || - ipv6_chk_addr(net, in6, NULL, 0); + ipv6_chk_addr(net, in6, dev, 0); } /* This function checks if the address is a valid address to be used for From f87b1ac06c887210782eab9f105ffd9045be74cc Mon Sep 17 00:00:00 2001 From: Xin Long Date: Wed, 16 Nov 2022 15:01:18 -0500 Subject: [PATCH 1061/2002] sctp: check sk_bound_dev_if when matching ep in get_port In sctp_get_port_local(), when binding to IP and PORT, it should also check sk_bound_dev_if to match listening sk if it's set by SO_BINDTOIFINDEX, so that multiple sockets with the same IP and PORT, but different sk_bound_dev_if can be listened at the same time. Signed-off-by: Xin Long Signed-off-by: David S. Miller --- net/sctp/socket.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/net/sctp/socket.c b/net/sctp/socket.c index 3e83963d1b8a..4306164238ef 100644 --- a/net/sctp/socket.c +++ b/net/sctp/socket.c @@ -8398,6 +8398,7 @@ pp_found: * in an endpoint. */ sk_for_each_bound(sk2, &pp->owner) { + int bound_dev_if2 = READ_ONCE(sk2->sk_bound_dev_if); struct sctp_sock *sp2 = sctp_sk(sk2); struct sctp_endpoint *ep2 = sp2->ep; @@ -8408,7 +8409,9 @@ pp_found: uid_eq(uid, sock_i_uid(sk2)))) continue; - if (sctp_bind_addr_conflict(&ep2->base.bind_addr, + if ((!sk->sk_bound_dev_if || !bound_dev_if2 || + sk->sk_bound_dev_if == bound_dev_if2) && + sctp_bind_addr_conflict(&ep2->base.bind_addr, addr, sp2, sp)) { ret = 1; goto fail_unlock; From 33e93ed2209d5971043bed41dd194bc583b57ef3 Mon Sep 17 00:00:00 2001 From: Xin Long Date: Wed, 16 Nov 2022 15:01:19 -0500 Subject: [PATCH 1062/2002] sctp: add skb_sdif in struct sctp_af Add skb_sdif function in struct sctp_af to get the enslaved device for both ipv4 and ipv6 when adding SCTP VRF support in sctp_rcv in the next patch. Signed-off-by: Xin Long Signed-off-by: David S. Miller --- include/net/sctp/structs.h | 1 + net/sctp/ipv6.c | 8 +++++++- net/sctp/protocol.c | 6 ++++++ 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/include/net/sctp/structs.h b/include/net/sctp/structs.h index 350f250b0dc7..7b4884c63b26 100644 --- a/include/net/sctp/structs.h +++ b/include/net/sctp/structs.h @@ -477,6 +477,7 @@ struct sctp_af { int (*available) (union sctp_addr *, struct sctp_sock *); int (*skb_iif) (const struct sk_buff *sk); + int (*skb_sdif)(const struct sk_buff *sk); int (*is_ce) (const struct sk_buff *sk); void (*seq_dump_addr)(struct seq_file *seq, union sctp_addr *addr); diff --git a/net/sctp/ipv6.c b/net/sctp/ipv6.c index e6274cdbdf6c..097bd60ce964 100644 --- a/net/sctp/ipv6.c +++ b/net/sctp/ipv6.c @@ -842,7 +842,12 @@ static int sctp_v6_addr_to_user(struct sctp_sock *sp, union sctp_addr *addr) /* Where did this skb come from? */ static int sctp_v6_skb_iif(const struct sk_buff *skb) { - return IP6CB(skb)->iif; + return inet6_iif(skb); +} + +static int sctp_v6_skb_sdif(const struct sk_buff *skb) +{ + return inet6_sdif(skb); } /* Was this packet marked by Explicit Congestion Notification? */ @@ -1142,6 +1147,7 @@ static struct sctp_af sctp_af_inet6 = { .is_any = sctp_v6_is_any, .available = sctp_v6_available, .skb_iif = sctp_v6_skb_iif, + .skb_sdif = sctp_v6_skb_sdif, .is_ce = sctp_v6_is_ce, .seq_dump_addr = sctp_v6_seq_dump_addr, .ecn_capable = sctp_v6_ecn_capable, diff --git a/net/sctp/protocol.c b/net/sctp/protocol.c index dbfe7d1000c2..a18cf0471a8d 100644 --- a/net/sctp/protocol.c +++ b/net/sctp/protocol.c @@ -567,6 +567,11 @@ static int sctp_v4_skb_iif(const struct sk_buff *skb) return inet_iif(skb); } +static int sctp_v4_skb_sdif(const struct sk_buff *skb) +{ + return inet_sdif(skb); +} + /* Was this packet marked by Explicit Congestion Notification? */ static int sctp_v4_is_ce(const struct sk_buff *skb) { @@ -1185,6 +1190,7 @@ static struct sctp_af sctp_af_inet = { .available = sctp_v4_available, .scope = sctp_v4_scope, .skb_iif = sctp_v4_skb_iif, + .skb_sdif = sctp_v4_skb_sdif, .is_ce = sctp_v4_is_ce, .seq_dump_addr = sctp_v4_seq_dump_addr, .ecn_capable = sctp_v4_ecn_capable, From 0af03170637f47fb5cc6501d4b2dcbf1c14772a9 Mon Sep 17 00:00:00 2001 From: Xin Long Date: Wed, 16 Nov 2022 15:01:20 -0500 Subject: [PATCH 1063/2002] sctp: add dif and sdif check in asoc and ep lookup This patch at first adds a pernet global l3mdev_accept to decide if it accepts the packets from a l3mdev when a SCTP socket doesn't bind to any interface. It's set to 1 to avoid any possible incompatible issue, and in next patch, a sysctl will be introduced to allow to change it. Then similar to inet/udp_sk_bound_dev_eq(), sctp_sk_bound_dev_eq() is added to check either dif or sdif is equal to sk_bound_dev_if, and to check sid is 0 or l3mdev_accept is 1 if sk_bound_dev_if is not set. This function is used to match a association or a endpoint, namely called by sctp_addrs_lookup_transport() and sctp_endpoint_is_match(). All functions that needs updating are: sctp_rcv(): asoc: __sctp_rcv_lookup() __sctp_lookup_association() -> sctp_addrs_lookup_transport() __sctp_rcv_lookup_harder() __sctp_rcv_init_lookup() __sctp_lookup_association() -> sctp_addrs_lookup_transport() __sctp_rcv_walk_lookup() __sctp_rcv_asconf_lookup() __sctp_lookup_association() -> sctp_addrs_lookup_transport() ep: __sctp_rcv_lookup_endpoint() -> sctp_endpoint_is_match() sctp_connect(): sctp_endpoint_is_peeled_off() __sctp_lookup_association() sctp_has_association() sctp_lookup_association() __sctp_lookup_association() -> sctp_addrs_lookup_transport() sctp_diag_dump_one(): sctp_transport_lookup_process() -> sctp_addrs_lookup_transport() Signed-off-by: Xin Long Signed-off-by: David S. Miller --- include/net/netns/sctp.h | 4 ++ include/net/sctp/sctp.h | 6 ++- include/net/sctp/structs.h | 8 +-- net/sctp/diag.c | 3 +- net/sctp/endpointola.c | 13 +++-- net/sctp/input.c | 108 ++++++++++++++++++++----------------- net/sctp/protocol.c | 4 ++ net/sctp/socket.c | 4 +- 8 files changed, 89 insertions(+), 61 deletions(-) diff --git a/include/net/netns/sctp.h b/include/net/netns/sctp.h index a681147aecd8..7eff3d981b89 100644 --- a/include/net/netns/sctp.h +++ b/include/net/netns/sctp.h @@ -175,6 +175,10 @@ struct netns_sctp { /* Threshold for autoclose timeout, in seconds. */ unsigned long max_autoclose; + +#ifdef CONFIG_NET_L3_MASTER_DEV + int l3mdev_accept; +#endif }; #endif /* __NETNS_SCTP_H__ */ diff --git a/include/net/sctp/sctp.h b/include/net/sctp/sctp.h index 01d904b34cf0..c335dd01a597 100644 --- a/include/net/sctp/sctp.h +++ b/include/net/sctp/sctp.h @@ -109,7 +109,7 @@ struct sctp_transport *sctp_transport_get_idx(struct net *net, struct rhashtable_iter *iter, int pos); int sctp_transport_lookup_process(sctp_callback_t cb, struct net *net, const union sctp_addr *laddr, - const union sctp_addr *paddr, void *p); + const union sctp_addr *paddr, void *p, int dif); int sctp_transport_traverse_process(sctp_callback_t cb, sctp_callback_t cb_done, struct net *net, int *pos, void *p); int sctp_for_each_endpoint(int (*cb)(struct sctp_endpoint *, void *), void *p); @@ -157,10 +157,12 @@ void sctp_unhash_transport(struct sctp_transport *t); struct sctp_transport *sctp_addrs_lookup_transport( struct net *net, const union sctp_addr *laddr, - const union sctp_addr *paddr); + const union sctp_addr *paddr, + int dif, int sdif); struct sctp_transport *sctp_epaddr_lookup_transport( const struct sctp_endpoint *ep, const union sctp_addr *paddr); +bool sctp_sk_bound_dev_eq(struct net *net, int bound_dev_if, int dif, int sdif); /* * sctp/proc.c diff --git a/include/net/sctp/structs.h b/include/net/sctp/structs.h index 7b4884c63b26..afa3781e3ca2 100644 --- a/include/net/sctp/structs.h +++ b/include/net/sctp/structs.h @@ -1379,10 +1379,12 @@ struct sctp_association *sctp_endpoint_lookup_assoc( struct sctp_transport **); bool sctp_endpoint_is_peeled_off(struct sctp_endpoint *ep, const union sctp_addr *paddr); -struct sctp_endpoint *sctp_endpoint_is_match(struct sctp_endpoint *, - struct net *, const union sctp_addr *); +struct sctp_endpoint *sctp_endpoint_is_match(struct sctp_endpoint *ep, + struct net *net, + const union sctp_addr *laddr, + int dif, int sdif); bool sctp_has_association(struct net *net, const union sctp_addr *laddr, - const union sctp_addr *paddr); + const union sctp_addr *paddr, int dif, int sdif); int sctp_verify_init(struct net *net, const struct sctp_endpoint *ep, const struct sctp_association *asoc, diff --git a/net/sctp/diag.c b/net/sctp/diag.c index d9c6d8f30f09..a557009e9832 100644 --- a/net/sctp/diag.c +++ b/net/sctp/diag.c @@ -426,6 +426,7 @@ static int sctp_diag_dump_one(struct netlink_callback *cb, struct net *net = sock_net(skb->sk); const struct nlmsghdr *nlh = cb->nlh; union sctp_addr laddr, paddr; + int dif = req->id.idiag_if; struct sctp_comm_param commp = { .skb = skb, .r = req, @@ -454,7 +455,7 @@ static int sctp_diag_dump_one(struct netlink_callback *cb, } return sctp_transport_lookup_process(sctp_sock_dump_one, - net, &laddr, &paddr, &commp); + net, &laddr, &paddr, &commp, dif); } static void sctp_diag_dump(struct sk_buff *skb, struct netlink_callback *cb, diff --git a/net/sctp/endpointola.c b/net/sctp/endpointola.c index efffde7f2328..7e77b450697c 100644 --- a/net/sctp/endpointola.c +++ b/net/sctp/endpointola.c @@ -246,12 +246,15 @@ void sctp_endpoint_put(struct sctp_endpoint *ep) /* Is this the endpoint we are looking for? */ struct sctp_endpoint *sctp_endpoint_is_match(struct sctp_endpoint *ep, struct net *net, - const union sctp_addr *laddr) + const union sctp_addr *laddr, + int dif, int sdif) { + int bound_dev_if = READ_ONCE(ep->base.sk->sk_bound_dev_if); struct sctp_endpoint *retval = NULL; - if ((htons(ep->base.bind_addr.port) == laddr->v4.sin_port) && - net_eq(ep->base.net, net)) { + if (net_eq(ep->base.net, net) && + sctp_sk_bound_dev_eq(net, bound_dev_if, dif, sdif) && + (htons(ep->base.bind_addr.port) == laddr->v4.sin_port)) { if (sctp_bind_addr_match(&ep->base.bind_addr, laddr, sctp_sk(ep->base.sk))) retval = ep; @@ -298,6 +301,7 @@ out: bool sctp_endpoint_is_peeled_off(struct sctp_endpoint *ep, const union sctp_addr *paddr) { + int bound_dev_if = READ_ONCE(ep->base.sk->sk_bound_dev_if); struct sctp_sockaddr_entry *addr; struct net *net = ep->base.net; struct sctp_bind_addr *bp; @@ -307,7 +311,8 @@ bool sctp_endpoint_is_peeled_off(struct sctp_endpoint *ep, * so the address_list can not change. */ list_for_each_entry(addr, &bp->address_list, list) { - if (sctp_has_association(net, &addr->a, paddr)) + if (sctp_has_association(net, &addr->a, paddr, + bound_dev_if, bound_dev_if)) return true; } diff --git a/net/sctp/input.c b/net/sctp/input.c index 4f43afa8678f..bf70371301ff 100644 --- a/net/sctp/input.c +++ b/net/sctp/input.c @@ -50,16 +50,19 @@ static struct sctp_association *__sctp_rcv_lookup(struct net *net, struct sk_buff *skb, const union sctp_addr *paddr, const union sctp_addr *laddr, - struct sctp_transport **transportp); + struct sctp_transport **transportp, + int dif, int sdif); static struct sctp_endpoint *__sctp_rcv_lookup_endpoint( struct net *net, struct sk_buff *skb, const union sctp_addr *laddr, - const union sctp_addr *daddr); + const union sctp_addr *daddr, + int dif, int sdif); static struct sctp_association *__sctp_lookup_association( struct net *net, const union sctp_addr *local, const union sctp_addr *peer, - struct sctp_transport **pt); + struct sctp_transport **pt, + int dif, int sdif); static int sctp_add_backlog(struct sock *sk, struct sk_buff *skb); @@ -92,11 +95,11 @@ int sctp_rcv(struct sk_buff *skb) struct sctp_chunk *chunk; union sctp_addr src; union sctp_addr dest; - int bound_dev_if; int family; struct sctp_af *af; struct net *net = dev_net(skb->dev); bool is_gso = skb_is_gso(skb) && skb_is_gso_sctp(skb); + int dif, sdif; if (skb->pkt_type != PACKET_HOST) goto discard_it; @@ -141,6 +144,8 @@ int sctp_rcv(struct sk_buff *skb) /* Initialize local addresses for lookups. */ af->from_skb(&src, skb, 1); af->from_skb(&dest, skb, 0); + dif = af->skb_iif(skb); + sdif = af->skb_sdif(skb); /* If the packet is to or from a non-unicast address, * silently discard the packet. @@ -157,35 +162,15 @@ int sctp_rcv(struct sk_buff *skb) !af->addr_valid(&dest, NULL, skb)) goto discard_it; - asoc = __sctp_rcv_lookup(net, skb, &src, &dest, &transport); + asoc = __sctp_rcv_lookup(net, skb, &src, &dest, &transport, dif, sdif); if (!asoc) - ep = __sctp_rcv_lookup_endpoint(net, skb, &dest, &src); + ep = __sctp_rcv_lookup_endpoint(net, skb, &dest, &src, dif, sdif); /* Retrieve the common input handling substructure. */ rcvr = asoc ? &asoc->base : &ep->base; sk = rcvr->sk; - /* - * If a frame arrives on an interface and the receiving socket is - * bound to another interface, via SO_BINDTODEVICE, treat it as OOTB - */ - bound_dev_if = READ_ONCE(sk->sk_bound_dev_if); - if (bound_dev_if && (bound_dev_if != af->skb_iif(skb))) { - if (transport) { - sctp_transport_put(transport); - asoc = NULL; - transport = NULL; - } else { - sctp_endpoint_put(ep); - ep = NULL; - } - sk = net->sctp.ctl_sock; - ep = sctp_sk(sk)->ep; - sctp_endpoint_hold(ep); - rcvr = &ep->base; - } - /* * RFC 2960, 8.4 - Handle "Out of the blue" Packets. * An SCTP packet is called an "out of the blue" (OOTB) @@ -485,6 +470,8 @@ struct sock *sctp_err_lookup(struct net *net, int family, struct sk_buff *skb, struct sctp_association *asoc; struct sctp_transport *transport = NULL; __u32 vtag = ntohl(sctphdr->vtag); + int sdif = inet_sdif(skb); + int dif = inet_iif(skb); *app = NULL; *tpp = NULL; @@ -500,7 +487,7 @@ struct sock *sctp_err_lookup(struct net *net, int family, struct sk_buff *skb, /* Look for an association that matches the incoming ICMP error * packet. */ - asoc = __sctp_lookup_association(net, &saddr, &daddr, &transport); + asoc = __sctp_lookup_association(net, &saddr, &daddr, &transport, dif, sdif); if (!asoc) return NULL; @@ -850,7 +837,8 @@ static inline __u32 sctp_hashfn(const struct net *net, __be16 lport, static struct sctp_endpoint *__sctp_rcv_lookup_endpoint( struct net *net, struct sk_buff *skb, const union sctp_addr *laddr, - const union sctp_addr *paddr) + const union sctp_addr *paddr, + int dif, int sdif) { struct sctp_hashbucket *head; struct sctp_endpoint *ep; @@ -863,7 +851,7 @@ static struct sctp_endpoint *__sctp_rcv_lookup_endpoint( head = &sctp_ep_hashtable[hash]; read_lock(&head->lock); sctp_for_each_hentry(ep, &head->chain) { - if (sctp_endpoint_is_match(ep, net, laddr)) + if (sctp_endpoint_is_match(ep, net, laddr, dif, sdif)) goto hit; } @@ -990,14 +978,26 @@ void sctp_unhash_transport(struct sctp_transport *t) sctp_hash_params); } +bool sctp_sk_bound_dev_eq(struct net *net, int bound_dev_if, int dif, int sdif) +{ + bool l3mdev_accept = true; + +#if IS_ENABLED(CONFIG_NET_L3_MASTER_DEV) + l3mdev_accept = !!READ_ONCE(net->sctp.l3mdev_accept); +#endif + return inet_bound_dev_eq(l3mdev_accept, bound_dev_if, dif, sdif); +} + /* return a transport with holding it */ struct sctp_transport *sctp_addrs_lookup_transport( struct net *net, const union sctp_addr *laddr, - const union sctp_addr *paddr) + const union sctp_addr *paddr, + int dif, int sdif) { struct rhlist_head *tmp, *list; struct sctp_transport *t; + int bound_dev_if; struct sctp_hash_cmp_arg arg = { .paddr = paddr, .net = net, @@ -1011,7 +1011,9 @@ struct sctp_transport *sctp_addrs_lookup_transport( if (!sctp_transport_hold(t)) continue; - if (sctp_bind_addr_match(&t->asoc->base.bind_addr, + bound_dev_if = READ_ONCE(t->asoc->base.sk->sk_bound_dev_if); + if (sctp_sk_bound_dev_eq(net, bound_dev_if, dif, sdif) && + sctp_bind_addr_match(&t->asoc->base.bind_addr, laddr, sctp_sk(t->asoc->base.sk))) return t; sctp_transport_put(t); @@ -1048,12 +1050,13 @@ static struct sctp_association *__sctp_lookup_association( struct net *net, const union sctp_addr *local, const union sctp_addr *peer, - struct sctp_transport **pt) + struct sctp_transport **pt, + int dif, int sdif) { struct sctp_transport *t; struct sctp_association *asoc = NULL; - t = sctp_addrs_lookup_transport(net, local, peer); + t = sctp_addrs_lookup_transport(net, local, peer, dif, sdif); if (!t) goto out; @@ -1069,12 +1072,13 @@ static struct sctp_association *sctp_lookup_association(struct net *net, const union sctp_addr *laddr, const union sctp_addr *paddr, - struct sctp_transport **transportp) + struct sctp_transport **transportp, + int dif, int sdif) { struct sctp_association *asoc; rcu_read_lock(); - asoc = __sctp_lookup_association(net, laddr, paddr, transportp); + asoc = __sctp_lookup_association(net, laddr, paddr, transportp, dif, sdif); rcu_read_unlock(); return asoc; @@ -1083,11 +1087,12 @@ struct sctp_association *sctp_lookup_association(struct net *net, /* Is there an association matching the given local and peer addresses? */ bool sctp_has_association(struct net *net, const union sctp_addr *laddr, - const union sctp_addr *paddr) + const union sctp_addr *paddr, + int dif, int sdif) { struct sctp_transport *transport; - if (sctp_lookup_association(net, laddr, paddr, &transport)) { + if (sctp_lookup_association(net, laddr, paddr, &transport, dif, sdif)) { sctp_transport_put(transport); return true; } @@ -1115,7 +1120,8 @@ bool sctp_has_association(struct net *net, */ static struct sctp_association *__sctp_rcv_init_lookup(struct net *net, struct sk_buff *skb, - const union sctp_addr *laddr, struct sctp_transport **transportp) + const union sctp_addr *laddr, struct sctp_transport **transportp, + int dif, int sdif) { struct sctp_association *asoc; union sctp_addr addr; @@ -1154,7 +1160,7 @@ static struct sctp_association *__sctp_rcv_init_lookup(struct net *net, if (!af->from_addr_param(paddr, params.addr, sh->source, 0)) continue; - asoc = __sctp_lookup_association(net, laddr, paddr, transportp); + asoc = __sctp_lookup_association(net, laddr, paddr, transportp, dif, sdif); if (asoc) return asoc; } @@ -1181,7 +1187,8 @@ static struct sctp_association *__sctp_rcv_asconf_lookup( struct sctp_chunkhdr *ch, const union sctp_addr *laddr, __be16 peer_port, - struct sctp_transport **transportp) + struct sctp_transport **transportp, + int dif, int sdif) { struct sctp_addip_chunk *asconf = (struct sctp_addip_chunk *)ch; struct sctp_af *af; @@ -1201,7 +1208,7 @@ static struct sctp_association *__sctp_rcv_asconf_lookup( if (!af->from_addr_param(&paddr, param, peer_port, 0)) return NULL; - return __sctp_lookup_association(net, laddr, &paddr, transportp); + return __sctp_lookup_association(net, laddr, &paddr, transportp, dif, sdif); } @@ -1217,7 +1224,8 @@ static struct sctp_association *__sctp_rcv_asconf_lookup( static struct sctp_association *__sctp_rcv_walk_lookup(struct net *net, struct sk_buff *skb, const union sctp_addr *laddr, - struct sctp_transport **transportp) + struct sctp_transport **transportp, + int dif, int sdif) { struct sctp_association *asoc = NULL; struct sctp_chunkhdr *ch; @@ -1260,7 +1268,7 @@ static struct sctp_association *__sctp_rcv_walk_lookup(struct net *net, asoc = __sctp_rcv_asconf_lookup( net, ch, laddr, sctp_hdr(skb)->source, - transportp); + transportp, dif, sdif); break; default: break; @@ -1285,7 +1293,8 @@ static struct sctp_association *__sctp_rcv_walk_lookup(struct net *net, static struct sctp_association *__sctp_rcv_lookup_harder(struct net *net, struct sk_buff *skb, const union sctp_addr *laddr, - struct sctp_transport **transportp) + struct sctp_transport **transportp, + int dif, int sdif) { struct sctp_chunkhdr *ch; @@ -1309,9 +1318,9 @@ static struct sctp_association *__sctp_rcv_lookup_harder(struct net *net, /* If this is INIT/INIT-ACK look inside the chunk too. */ if (ch->type == SCTP_CID_INIT || ch->type == SCTP_CID_INIT_ACK) - return __sctp_rcv_init_lookup(net, skb, laddr, transportp); + return __sctp_rcv_init_lookup(net, skb, laddr, transportp, dif, sdif); - return __sctp_rcv_walk_lookup(net, skb, laddr, transportp); + return __sctp_rcv_walk_lookup(net, skb, laddr, transportp, dif, sdif); } /* Lookup an association for an inbound skb. */ @@ -1319,11 +1328,12 @@ static struct sctp_association *__sctp_rcv_lookup(struct net *net, struct sk_buff *skb, const union sctp_addr *paddr, const union sctp_addr *laddr, - struct sctp_transport **transportp) + struct sctp_transport **transportp, + int dif, int sdif) { struct sctp_association *asoc; - asoc = __sctp_lookup_association(net, laddr, paddr, transportp); + asoc = __sctp_lookup_association(net, laddr, paddr, transportp, dif, sdif); if (asoc) goto out; @@ -1331,7 +1341,7 @@ static struct sctp_association *__sctp_rcv_lookup(struct net *net, * SCTP Implementors Guide, 2.18 Handling of address * parameters within the INIT or INIT-ACK. */ - asoc = __sctp_rcv_lookup_harder(net, skb, laddr, transportp); + asoc = __sctp_rcv_lookup_harder(net, skb, laddr, transportp, dif, sdif); if (asoc) goto out; diff --git a/net/sctp/protocol.c b/net/sctp/protocol.c index a18cf0471a8d..909a89a1cff4 100644 --- a/net/sctp/protocol.c +++ b/net/sctp/protocol.c @@ -1394,6 +1394,10 @@ static int __net_init sctp_defaults_init(struct net *net) /* Initialize maximum autoclose timeout. */ net->sctp.max_autoclose = INT_MAX / HZ; +#ifdef CONFIG_NET_L3_MASTER_DEV + net->sctp.l3mdev_accept = 1; +#endif + status = sctp_sysctl_net_register(net); if (status) goto err_sysctl_register; diff --git a/net/sctp/socket.c b/net/sctp/socket.c index 4306164238ef..5acbdf0d38f3 100644 --- a/net/sctp/socket.c +++ b/net/sctp/socket.c @@ -5315,14 +5315,14 @@ EXPORT_SYMBOL_GPL(sctp_for_each_endpoint); int sctp_transport_lookup_process(sctp_callback_t cb, struct net *net, const union sctp_addr *laddr, - const union sctp_addr *paddr, void *p) + const union sctp_addr *paddr, void *p, int dif) { struct sctp_transport *transport; struct sctp_endpoint *ep; int err = -ENOENT; rcu_read_lock(); - transport = sctp_addrs_lookup_transport(net, laddr, paddr); + transport = sctp_addrs_lookup_transport(net, laddr, paddr, dif, dif); if (!transport) { rcu_read_unlock(); return err; From b712d0328c2c3ab456847f29f711e785f70cd8a5 Mon Sep 17 00:00:00 2001 From: Xin Long Date: Wed, 16 Nov 2022 15:01:21 -0500 Subject: [PATCH 1064/2002] sctp: add sysctl net.sctp.l3mdev_accept This patch is to add sysctl net.sctp.l3mdev_accept to allow users to change the pernet global l3mdev_accept. Signed-off-by: Xin Long Signed-off-by: David S. Miller --- Documentation/networking/ip-sysctl.rst | 9 +++++++++ net/sctp/sysctl.c | 11 +++++++++++ 2 files changed, 20 insertions(+) diff --git a/Documentation/networking/ip-sysctl.rst b/Documentation/networking/ip-sysctl.rst index 727b25cc7ec4..7fbd060d6047 100644 --- a/Documentation/networking/ip-sysctl.rst +++ b/Documentation/networking/ip-sysctl.rst @@ -3127,6 +3127,15 @@ ecn_enable - BOOLEAN Default: 1 +l3mdev_accept - BOOLEAN + Enabling this option allows a "global" bound socket to work + across L3 master domains (e.g., VRFs) with packets capable of + being received regardless of the L3 domain in which they + originated. Only valid when the kernel was compiled with + CONFIG_NET_L3_MASTER_DEV. + + Default: 1 (enabled) + ``/proc/sys/net/core/*`` ======================== diff --git a/net/sctp/sysctl.c b/net/sctp/sysctl.c index b46a416787ec..7f40ed117fc7 100644 --- a/net/sctp/sysctl.c +++ b/net/sctp/sysctl.c @@ -347,6 +347,17 @@ static struct ctl_table sctp_net_table[] = { .extra1 = &max_autoclose_min, .extra2 = &max_autoclose_max, }, +#ifdef CONFIG_NET_L3_MASTER_DEV + { + .procname = "l3mdev_accept", + .data = &init_net.sctp.l3mdev_accept, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec_minmax, + .extra1 = SYSCTL_ZERO, + .extra2 = SYSCTL_ONE, + }, +#endif { .procname = "pf_enable", .data = &init_net.sctp.pf_enable, From a61bd7b9fef34484a3fe144a62f4ec78cc42e20e Mon Sep 17 00:00:00 2001 From: Xin Long Date: Wed, 16 Nov 2022 15:01:22 -0500 Subject: [PATCH 1065/2002] selftests: add a selftest for sctp vrf This patch adds 12 small test cases: 01-04 test for the sysctl net.sctp.l3mdev_accept. 05-10 test for only binding to a right l3mdev device, the connection can be created. 11-12 test for two socks binding to different l3mdev devices at the same time, each of them can process the packets from the corresponding peer. The tests run for both IPv4 and IPv6 SCTP. Signed-off-by: Xin Long Signed-off-by: David S. Miller --- tools/testing/selftests/net/Makefile | 2 + tools/testing/selftests/net/sctp_hello.c | 137 +++++++++++++++++ tools/testing/selftests/net/sctp_vrf.sh | 178 +++++++++++++++++++++++ 3 files changed, 317 insertions(+) create mode 100644 tools/testing/selftests/net/sctp_hello.c create mode 100755 tools/testing/selftests/net/sctp_vrf.sh diff --git a/tools/testing/selftests/net/Makefile b/tools/testing/selftests/net/Makefile index cec4800cb017..880e6ded6ed5 100644 --- a/tools/testing/selftests/net/Makefile +++ b/tools/testing/selftests/net/Makefile @@ -72,6 +72,8 @@ TEST_GEN_PROGS += sk_bind_sendto_listen TEST_GEN_PROGS += sk_connect_zero_addr TEST_PROGS += test_ingress_egress_chaining.sh TEST_GEN_PROGS += so_incoming_cpu +TEST_PROGS += sctp_vrf.sh +TEST_GEN_FILES += sctp_hello TEST_FILES := settings diff --git a/tools/testing/selftests/net/sctp_hello.c b/tools/testing/selftests/net/sctp_hello.c new file mode 100644 index 000000000000..f02f1f95d227 --- /dev/null +++ b/tools/testing/selftests/net/sctp_hello.c @@ -0,0 +1,137 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include +#include +#include +#include +#include +#include +#include + +static void set_addr(struct sockaddr_storage *ss, char *ip, char *port, int *len) +{ + if (ss->ss_family == AF_INET) { + struct sockaddr_in *a = (struct sockaddr_in *)ss; + + a->sin_addr.s_addr = inet_addr(ip); + a->sin_port = htons(atoi(port)); + *len = sizeof(*a); + } else { + struct sockaddr_in6 *a = (struct sockaddr_in6 *)ss; + + a->sin6_family = AF_INET6; + inet_pton(AF_INET6, ip, &a->sin6_addr); + a->sin6_port = htons(atoi(port)); + *len = sizeof(*a); + } +} + +static int do_client(int argc, char *argv[]) +{ + struct sockaddr_storage ss; + char buf[] = "hello"; + int csk, ret, len; + + if (argc < 5) { + printf("%s client -4|6 IP PORT [IP PORT]\n", argv[0]); + return -1; + } + + bzero((void *)&ss, sizeof(ss)); + ss.ss_family = !strcmp(argv[2], "-4") ? AF_INET : AF_INET6; + csk = socket(ss.ss_family, SOCK_STREAM, IPPROTO_SCTP); + if (csk < 0) { + printf("failed to create socket\n"); + return -1; + } + + if (argc >= 7) { + set_addr(&ss, argv[5], argv[6], &len); + ret = bind(csk, (struct sockaddr *)&ss, len); + if (ret < 0) { + printf("failed to bind to address\n"); + return -1; + } + } + + set_addr(&ss, argv[3], argv[4], &len); + ret = connect(csk, (struct sockaddr *)&ss, len); + if (ret < 0) { + printf("failed to connect to peer\n"); + return -1; + } + + ret = send(csk, buf, strlen(buf) + 1, 0); + if (ret < 0) { + printf("failed to send msg %d\n", ret); + return -1; + } + close(csk); + + return 0; +} + +int main(int argc, char *argv[]) +{ + struct sockaddr_storage ss; + int lsk, csk, ret, len; + char buf[20]; + + if (argc < 2 || (strcmp(argv[1], "server") && strcmp(argv[1], "client"))) { + printf("%s server|client ...\n", argv[0]); + return -1; + } + + if (!strcmp(argv[1], "client")) + return do_client(argc, argv); + + if (argc < 5) { + printf("%s server -4|6 IP PORT [IFACE]\n", argv[0]); + return -1; + } + + ss.ss_family = !strcmp(argv[2], "-4") ? AF_INET : AF_INET6; + lsk = socket(ss.ss_family, SOCK_STREAM, IPPROTO_SCTP); + if (lsk < 0) { + printf("failed to create lsk\n"); + return -1; + } + + if (argc >= 6) { + ret = setsockopt(lsk, SOL_SOCKET, SO_BINDTODEVICE, + argv[5], strlen(argv[5]) + 1); + if (ret < 0) { + printf("failed to bind to device\n"); + return -1; + } + } + + set_addr(&ss, argv[3], argv[4], &len); + ret = bind(lsk, (struct sockaddr *)&ss, len); + if (ret < 0) { + printf("failed to bind to address\n"); + return -1; + } + + ret = listen(lsk, 5); + if (ret < 0) { + printf("failed to listen on port\n"); + return -1; + } + + csk = accept(lsk, (struct sockaddr *)NULL, (socklen_t *)NULL); + if (csk < 0) { + printf("failed to accept new client\n"); + return -1; + } + + ret = recv(csk, buf, sizeof(buf), 0); + if (ret <= 0) { + printf("failed to recv msg %d\n", ret); + return -1; + } + close(csk); + close(lsk); + + return 0; +} diff --git a/tools/testing/selftests/net/sctp_vrf.sh b/tools/testing/selftests/net/sctp_vrf.sh new file mode 100755 index 000000000000..c721e952e5f3 --- /dev/null +++ b/tools/testing/selftests/net/sctp_vrf.sh @@ -0,0 +1,178 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0 +# +# Testing For SCTP VRF. +# TOPO: CLIENT_NS1 (veth1) <---> (veth1) -> vrf_s1 +# SERVER_NS +# CLIENT_NS2 (veth1) <---> (veth2) -> vrf_s2 + +CLIENT_NS1="client-ns1" +CLIENT_NS2="client-ns2" +CLIENT_IP4="10.0.0.1" +CLIENT_IP6="2000::1" +CLIENT_PORT=1234 + +SERVER_NS="server-ns" +SERVER_IP4="10.0.0.2" +SERVER_IP6="2000::2" +SERVER_PORT=1234 + +setup() { + modprobe sctp + modprobe sctp_diag + ip netns add $CLIENT_NS1 + ip netns add $CLIENT_NS2 + ip netns add $SERVER_NS + + ip net exec $CLIENT_NS1 sysctl -w net.ipv6.conf.default.accept_dad=0 2>&1 >/dev/null + ip net exec $CLIENT_NS2 sysctl -w net.ipv6.conf.default.accept_dad=0 2>&1 >/dev/null + ip net exec $SERVER_NS sysctl -w net.ipv6.conf.default.accept_dad=0 2>&1 >/dev/null + + ip -n $SERVER_NS link add veth1 type veth peer name veth1 netns $CLIENT_NS1 + ip -n $SERVER_NS link add veth2 type veth peer name veth1 netns $CLIENT_NS2 + + ip -n $CLIENT_NS1 link set veth1 up + ip -n $CLIENT_NS1 addr add $CLIENT_IP4/24 dev veth1 + ip -n $CLIENT_NS1 addr add $CLIENT_IP6/24 dev veth1 + + ip -n $CLIENT_NS2 link set veth1 up + ip -n $CLIENT_NS2 addr add $CLIENT_IP4/24 dev veth1 + ip -n $CLIENT_NS2 addr add $CLIENT_IP6/24 dev veth1 + + ip -n $SERVER_NS link add dummy1 type dummy + ip -n $SERVER_NS link set dummy1 up + ip -n $SERVER_NS link add vrf-1 type vrf table 10 + ip -n $SERVER_NS link add vrf-2 type vrf table 20 + ip -n $SERVER_NS link set vrf-1 up + ip -n $SERVER_NS link set vrf-2 up + ip -n $SERVER_NS link set veth1 master vrf-1 + ip -n $SERVER_NS link set veth2 master vrf-2 + + ip -n $SERVER_NS addr add $SERVER_IP4/24 dev dummy1 + ip -n $SERVER_NS addr add $SERVER_IP4/24 dev veth1 + ip -n $SERVER_NS addr add $SERVER_IP4/24 dev veth2 + ip -n $SERVER_NS addr add $SERVER_IP6/24 dev dummy1 + ip -n $SERVER_NS addr add $SERVER_IP6/24 dev veth1 + ip -n $SERVER_NS addr add $SERVER_IP6/24 dev veth2 + + ip -n $SERVER_NS link set veth1 up + ip -n $SERVER_NS link set veth2 up + ip -n $SERVER_NS route add table 10 $CLIENT_IP4 dev veth1 src $SERVER_IP4 + ip -n $SERVER_NS route add table 20 $CLIENT_IP4 dev veth2 src $SERVER_IP4 + ip -n $SERVER_NS route add $CLIENT_IP4 dev veth1 src $SERVER_IP4 + ip -n $SERVER_NS route add table 10 $CLIENT_IP6 dev veth1 src $SERVER_IP6 + ip -n $SERVER_NS route add table 20 $CLIENT_IP6 dev veth2 src $SERVER_IP6 + ip -n $SERVER_NS route add $CLIENT_IP6 dev veth1 src $SERVER_IP6 +} + +cleanup() { + ip netns exec $SERVER_NS pkill sctp_hello 2>&1 >/dev/null + ip netns del "$CLIENT_NS1" + ip netns del "$CLIENT_NS2" + ip netns del "$SERVER_NS" +} + +wait_server() { + local IFACE=$1 + local CNT=0 + + until ip netns exec $SERVER_NS ss -lS src $SERVER_IP:$SERVER_PORT | \ + grep LISTEN | grep "$IFACE" 2>&1 >/dev/null; do + [ $((CNT++)) = "20" ] && { RET=3; return $RET; } + sleep 0.1 + done +} + +do_test() { + local CLIENT_NS=$1 + local IFACE=$2 + + ip netns exec $SERVER_NS pkill sctp_hello 2>&1 >/dev/null + ip netns exec $SERVER_NS ./sctp_hello server $AF $SERVER_IP \ + $SERVER_PORT $IFACE 2>&1 >/dev/null & + disown + wait_server $IFACE || return $RET + timeout 3 ip netns exec $CLIENT_NS ./sctp_hello client $AF \ + $SERVER_IP $SERVER_PORT $CLIENT_IP $CLIENT_PORT 2>&1 >/dev/null + RET=$? + return $RET +} + +do_testx() { + local IFACE1=$1 + local IFACE2=$2 + + ip netns exec $SERVER_NS pkill sctp_hello 2>&1 >/dev/null + ip netns exec $SERVER_NS ./sctp_hello server $AF $SERVER_IP \ + $SERVER_PORT $IFACE1 2>&1 >/dev/null & + disown + wait_server $IFACE1 || return $RET + ip netns exec $SERVER_NS ./sctp_hello server $AF $SERVER_IP \ + $SERVER_PORT $IFACE2 2>&1 >/dev/null & + disown + wait_server $IFACE2 || return $RET + timeout 3 ip netns exec $CLIENT_NS1 ./sctp_hello client $AF \ + $SERVER_IP $SERVER_PORT $CLIENT_IP $CLIENT_PORT 2>&1 >/dev/null && \ + timeout 3 ip netns exec $CLIENT_NS2 ./sctp_hello client $AF \ + $SERVER_IP $SERVER_PORT $CLIENT_IP $CLIENT_PORT 2>&1 >/dev/null + RET=$? + return $RET +} + +testup() { + ip netns exec $SERVER_NS sysctl -w net.sctp.l3mdev_accept=1 2>&1 >/dev/null + echo -n "TEST 01: nobind, connect from client 1, l3mdev_accept=1, Y " + do_test $CLIENT_NS1 || { echo "[FAIL]"; return $RET; } + echo "[PASS]" + + echo -n "TEST 02: nobind, connect from client 2, l3mdev_accept=1, N " + do_test $CLIENT_NS2 && { echo "[FAIL]"; return $RET; } + echo "[PASS]" + + ip netns exec $SERVER_NS sysctl -w net.sctp.l3mdev_accept=0 2>&1 >/dev/null + echo -n "TEST 03: nobind, connect from client 1, l3mdev_accept=0, N " + do_test $CLIENT_NS1 && { echo "[FAIL]"; return $RET; } + echo "[PASS]" + + echo -n "TEST 04: nobind, connect from client 2, l3mdev_accept=0, N " + do_test $CLIENT_NS2 && { echo "[FAIL]"; return $RET; } + echo "[PASS]" + + echo -n "TEST 05: bind veth2 in server, connect from client 1, N " + do_test $CLIENT_NS1 veth2 && { echo "[FAIL]"; return $RET; } + echo "[PASS]" + + echo -n "TEST 06: bind veth1 in server, connect from client 1, Y " + do_test $CLIENT_NS1 veth1 || { echo "[FAIL]"; return $RET; } + echo "[PASS]" + + echo -n "TEST 07: bind vrf-1 in server, connect from client 1, Y " + do_test $CLIENT_NS1 vrf-1 || { echo "[FAIL]"; return $RET; } + echo "[PASS]" + + echo -n "TEST 08: bind vrf-2 in server, connect from client 1, N " + do_test $CLIENT_NS1 vrf-2 && { echo "[FAIL]"; return $RET; } + echo "[PASS]" + + echo -n "TEST 09: bind vrf-2 in server, connect from client 2, Y " + do_test $CLIENT_NS2 vrf-2 || { echo "[FAIL]"; return $RET; } + echo "[PASS]" + + echo -n "TEST 10: bind vrf-1 in server, connect from client 2, N " + do_test $CLIENT_NS2 vrf-1 && { echo "[FAIL]"; return $RET; } + echo "[PASS]" + + echo -n "TEST 11: bind vrf-1 & 2 in server, connect from client 1 & 2, Y " + do_testx vrf-1 vrf-2 || { echo "[FAIL]"; return $RET; } + echo "[PASS]" + + echo -n "TEST 12: bind vrf-2 & 1 in server, connect from client 1 & 2, N " + do_testx vrf-2 vrf-1 || { echo "[FAIL]"; return $RET; } + echo "[PASS]" +} + +trap cleanup EXIT +setup && echo "Testing For SCTP VRF:" && \ +CLIENT_IP=$CLIENT_IP4 SERVER_IP=$SERVER_IP4 AF="-4" testup && echo "***v4 Tests Done***" && +CLIENT_IP=$CLIENT_IP6 SERVER_IP=$SERVER_IP6 AF="-6" testup && echo "***v6 Tests Done***" +exit $? From 15b4f993d12b5bb81f50a1ce0693343fb7a94fcf Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Wed, 16 Nov 2022 16:37:18 -0600 Subject: [PATCH 1066/2002] net: ipa: avoid a null pointer dereference Dan Carpenter reported that Smatch found an instance where a pointer which had previously been assumed could be null (as indicated by a null check) was later dereferenced without a similar check. In practice this doesn't lead to a problem because currently the pointers used are all non-null. Nevertheless this patch addresses the reported problem. In addition, I spotted another bug that arose in the same commit. When the command to initialize a routing table memory region was added, the number of entries computed for the non-hashed table was wrong (it ended up being a Boolean rather than the count intended). This bug is fixed here as well. Reported-by: Dan Carpenter Link: https://lore.kernel.org/kernel-janitors/Y3OOP9dXK6oEydkf@kili Tested-by: Caleb Connolly Fixes: 5cb76899fb47 ("net: ipa: reduce arguments to ipa_table_init_add()") Signed-off-by: Alex Elder Signed-off-by: David S. Miller --- drivers/net/ipa/ipa_table.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/net/ipa/ipa_table.c b/drivers/net/ipa/ipa_table.c index cc9349a1d4df..b81e27b61354 100644 --- a/drivers/net/ipa/ipa_table.c +++ b/drivers/net/ipa/ipa_table.c @@ -382,6 +382,7 @@ static void ipa_table_init_add(struct gsi_trans *trans, bool filter, bool ipv6) const struct ipa_mem *mem; dma_addr_t hash_addr; dma_addr_t addr; + u32 hash_offset; u32 zero_offset; u16 hash_count; u32 zero_size; @@ -394,8 +395,10 @@ static void ipa_table_init_add(struct gsi_trans *trans, bool filter, bool ipv6) : ipv6 ? IPA_CMD_IP_V6_ROUTING_INIT : IPA_CMD_IP_V4_ROUTING_INIT; + /* The non-hashed region will exist (see ipa_table_mem_valid()) */ mem = ipa_table_mem(ipa, filter, false, ipv6); hash_mem = ipa_table_mem(ipa, filter, true, ipv6); + hash_offset = hash_mem ? hash_mem->offset : 0; /* Compute the number of table entries to initialize */ if (filter) { @@ -411,7 +414,7 @@ static void ipa_table_init_add(struct gsi_trans *trans, bool filter, bool ipv6) * of entries it has. */ count = mem->size / sizeof(__le64); - hash_count = hash_mem && hash_mem->size / sizeof(__le64); + hash_count = hash_mem ? hash_mem->size / sizeof(__le64) : 0; } size = count * sizeof(__le64); hash_size = hash_count * sizeof(__le64); @@ -420,7 +423,7 @@ static void ipa_table_init_add(struct gsi_trans *trans, bool filter, bool ipv6) hash_addr = ipa_table_addr(ipa, filter, hash_count); ipa_cmd_table_init_add(trans, opcode, size, mem->offset, addr, - hash_size, hash_mem->offset, hash_addr); + hash_size, hash_offset, hash_addr); if (!filter) return; @@ -433,7 +436,7 @@ static void ipa_table_init_add(struct gsi_trans *trans, bool filter, bool ipv6) return; /* Zero the unused space in the hashed filter table */ - zero_offset = hash_mem->offset + hash_size; + zero_offset = hash_offset + hash_size; zero_size = hash_mem->size - hash_size; ipa_cmd_dma_shared_mem_add(trans, zero_offset, zero_size, ipa->zero_addr, true); From b677d6c7a695dad1b02d2e0e428c39b3b344f270 Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Thu, 17 Nov 2022 00:35:04 +0100 Subject: [PATCH 1067/2002] net: ethernet: mtk_eth_soc: do not overwrite mtu configuration running reset routine Restore user configured MTU running mtk_hw_init() during tx timeout routine since it will be overwritten after a hw reset. Reported-by: Felix Fietkau Fixes: 9ea4d311509f ("net: ethernet: mediatek: add the whole ethernet reset into the reset process") Signed-off-by: Lorenzo Bianconi Signed-off-by: David S. Miller --- drivers/net/ethernet/mediatek/mtk_eth_soc.c | 53 +++++++++++++-------- 1 file changed, 34 insertions(+), 19 deletions(-) diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c index 3e9f5536f0f3..f3a03d6f1a92 100644 --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c @@ -3281,6 +3281,30 @@ static void mtk_dim_tx(struct work_struct *work) dim->state = DIM_START_MEASURE; } +static void mtk_set_mcr_max_rx(struct mtk_mac *mac, u32 val) +{ + struct mtk_eth *eth = mac->hw; + u32 mcr_cur, mcr_new; + + if (MTK_HAS_CAPS(eth->soc->caps, MTK_SOC_MT7628)) + return; + + mcr_cur = mtk_r32(mac->hw, MTK_MAC_MCR(mac->id)); + mcr_new = mcr_cur & ~MAC_MCR_MAX_RX_MASK; + + if (val <= 1518) + mcr_new |= MAC_MCR_MAX_RX(MAC_MCR_MAX_RX_1518); + else if (val <= 1536) + mcr_new |= MAC_MCR_MAX_RX(MAC_MCR_MAX_RX_1536); + else if (val <= 1552) + mcr_new |= MAC_MCR_MAX_RX(MAC_MCR_MAX_RX_1552); + else + mcr_new |= MAC_MCR_MAX_RX(MAC_MCR_MAX_RX_2048); + + if (mcr_new != mcr_cur) + mtk_w32(mac->hw, mcr_new, MTK_MAC_MCR(mac->id)); +} + static int mtk_hw_init(struct mtk_eth *eth) { u32 dma_mask = ETHSYS_DMA_AG_MAP_PDMA | ETHSYS_DMA_AG_MAP_QDMA | @@ -3355,8 +3379,16 @@ static int mtk_hw_init(struct mtk_eth *eth) * up with the more appropriate value when mtk_mac_config call is being * invoked. */ - for (i = 0; i < MTK_MAC_COUNT; i++) + for (i = 0; i < MTK_MAC_COUNT; i++) { + struct net_device *dev = eth->netdev[i]; + mtk_w32(eth, MAC_MCR_FORCE_LINK_DOWN, MTK_MAC_MCR(i)); + if (dev) { + struct mtk_mac *mac = netdev_priv(dev); + + mtk_set_mcr_max_rx(mac, dev->mtu + MTK_RX_ETH_HLEN); + } + } /* Indicates CDM to parse the MTK special tag from CPU * which also is working out for untag packets. @@ -3476,7 +3508,6 @@ static int mtk_change_mtu(struct net_device *dev, int new_mtu) int length = new_mtu + MTK_RX_ETH_HLEN; struct mtk_mac *mac = netdev_priv(dev); struct mtk_eth *eth = mac->hw; - u32 mcr_cur, mcr_new; if (rcu_access_pointer(eth->prog) && length > MTK_PP_MAX_BUF_SIZE) { @@ -3484,23 +3515,7 @@ static int mtk_change_mtu(struct net_device *dev, int new_mtu) return -EINVAL; } - if (!MTK_HAS_CAPS(eth->soc->caps, MTK_SOC_MT7628)) { - mcr_cur = mtk_r32(mac->hw, MTK_MAC_MCR(mac->id)); - mcr_new = mcr_cur & ~MAC_MCR_MAX_RX_MASK; - - if (length <= 1518) - mcr_new |= MAC_MCR_MAX_RX(MAC_MCR_MAX_RX_1518); - else if (length <= 1536) - mcr_new |= MAC_MCR_MAX_RX(MAC_MCR_MAX_RX_1536); - else if (length <= 1552) - mcr_new |= MAC_MCR_MAX_RX(MAC_MCR_MAX_RX_1552); - else - mcr_new |= MAC_MCR_MAX_RX(MAC_MCR_MAX_RX_2048); - - if (mcr_new != mcr_cur) - mtk_w32(mac->hw, mcr_new, MTK_MAC_MCR(mac->id)); - } - + mtk_set_mcr_max_rx(mac, length); dev->mtu = new_mtu; return 0; From ec8cd134eeeecef9cccb5431b1a3f140ff2a413a Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Thu, 17 Nov 2022 00:58:46 +0100 Subject: [PATCH 1068/2002] net: ethernet: mtk_eth_soc: remove cpu_relax in mtk_pending_work Get rid of cpu_relax in mtk_pending_work routine since MTK_RESETTING is set only in mtk_pending_work() and it runs holding rtnl lock Signed-off-by: Lorenzo Bianconi Signed-off-by: David S. Miller --- drivers/net/ethernet/mediatek/mtk_eth_soc.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c index f3a03d6f1a92..cdc0ff596196 100644 --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c @@ -3546,11 +3546,8 @@ static void mtk_pending_work(struct work_struct *work) rtnl_lock(); dev_dbg(eth->dev, "[%s][%d] reset\n", __func__, __LINE__); + set_bit(MTK_RESETTING, ð->state); - while (test_and_set_bit_lock(MTK_RESETTING, ð->state)) - cpu_relax(); - - dev_dbg(eth->dev, "[%s][%d] mtk_stop starts\n", __func__, __LINE__); /* stop all devices to make sure that dma is properly shut down */ for (i = 0; i < MTK_MAC_COUNT; i++) { if (!eth->netdev[i]) @@ -3584,7 +3581,7 @@ static void mtk_pending_work(struct work_struct *work) dev_dbg(eth->dev, "[%s][%d] reset done\n", __func__, __LINE__); - clear_bit_unlock(MTK_RESETTING, ð->state); + clear_bit(MTK_RESETTING, ð->state); rtnl_unlock(); } From 101c1bb6c55691d01c73915c118828f7ca17a049 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Thu, 17 Nov 2022 10:43:38 +0300 Subject: [PATCH 1069/2002] rxrpc: fix rxkad_verify_response() The error handling for if skb_copy_bits() fails was accidentally deleted so the rxkad_decrypt_ticket() function is not called. Fixes: 5d7edbc9231e ("rxrpc: Get rid of the Rx ring") Signed-off-by: Dan Carpenter Acked-by: David Howells Signed-off-by: David S. Miller --- net/rxrpc/rxkad.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/net/rxrpc/rxkad.c b/net/rxrpc/rxkad.c index 2706e59bf992..110a5550c0a6 100644 --- a/net/rxrpc/rxkad.c +++ b/net/rxrpc/rxkad.c @@ -1165,8 +1165,10 @@ static int rxkad_verify_response(struct rxrpc_connection *conn, eproto = tracepoint_string("rxkad_tkt_short"); abort_code = RXKADPACKETSHORT; - if (skb_copy_bits(skb, sizeof(struct rxrpc_wire_header) + sizeof(*response), - ticket, ticket_len) < 0) + ret = skb_copy_bits(skb, sizeof(struct rxrpc_wire_header) + sizeof(*response), + ticket, ticket_len); + if (ret < 0) + goto temporary_error_free_ticket; ret = rxkad_decrypt_ticket(conn, server_key, skb, ticket, ticket_len, &session_key, &expiry, _abort_code); From 38461894838bbbebab54cbd5a5459cc8d1b6dd9b Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Thu, 17 Nov 2022 10:44:02 +0300 Subject: [PATCH 1070/2002] rxrpc: uninitialized variable in rxrpc_send_ack_packet() The "pkt" was supposed to have been deleted in a previous patch. It leads to an uninitialized variable bug. Fixes: 72f0c6fb0579 ("rxrpc: Allocate ACK records at proposal and queue for transmission") Signed-off-by: Dan Carpenter Acked-by: David Howells Signed-off-by: David S. Miller --- net/rxrpc/output.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/net/rxrpc/output.c b/net/rxrpc/output.c index 46432e70a16b..04f945e042ab 100644 --- a/net/rxrpc/output.c +++ b/net/rxrpc/output.c @@ -202,7 +202,6 @@ static void rxrpc_cancel_rtt_probe(struct rxrpc_call *call, static int rxrpc_send_ack_packet(struct rxrpc_local *local, struct rxrpc_txbuf *txb) { struct rxrpc_connection *conn; - struct rxrpc_ack_buffer *pkt; struct rxrpc_call *call = txb->call; struct msghdr msg; struct kvec iov[1]; @@ -270,7 +269,6 @@ static int rxrpc_send_ack_packet(struct rxrpc_local *local, struct rxrpc_txbuf * rxrpc_set_keepalive(call); } - kfree(pkt); return ret; } From fd896e38e5df2c5b68c78eee2fc425c4dcd3b4dd Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Thu, 17 Nov 2022 09:26:41 +0000 Subject: [PATCH 1071/2002] net: fix napi_disable() logic error Dan reported a new warning after my recent patch: New smatch warnings: net/core/dev.c:6409 napi_disable() error: uninitialized symbol 'new'. Indeed, we must first wait for STATE_SCHED and STATE_NPSVC to be cleared, to make sure @new variable has been initialized properly. Fixes: 4ffa1d1c6842 ("net: adopt try_cmpxchg() in napi_{enable|disable}()") Reported-by: kernel test robot Reported-by: Dan Carpenter Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- net/core/dev.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/core/dev.c b/net/core/dev.c index d0fb4af9a126..7627c475d991 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -6399,9 +6399,9 @@ void napi_disable(struct napi_struct *n) val = READ_ONCE(n->state); do { - if (val & (NAPIF_STATE_SCHED | NAPIF_STATE_NPSVC)) { + while (val & (NAPIF_STATE_SCHED | NAPIF_STATE_NPSVC)) { usleep_range(20, 200); - continue; + val = READ_ONCE(n->state); } new = val | NAPIF_STATE_SCHED | NAPIF_STATE_NPSVC; From ab0377803dafc58f1e22296708c1c28e309414d6 Mon Sep 17 00:00:00 2001 From: Schspa Shi Date: Wed, 16 Nov 2022 19:45:11 +0800 Subject: [PATCH 1072/2002] mrp: introduce active flags to prevent UAF when applicant uninit The caller of del_timer_sync must prevent restarting of the timer, If we have no this synchronization, there is a small probability that the cancellation will not be successful. And syzbot report the fellowing crash: ================================================================== BUG: KASAN: use-after-free in hlist_add_head include/linux/list.h:929 [inline] BUG: KASAN: use-after-free in enqueue_timer+0x18/0xa4 kernel/time/timer.c:605 Write at addr f9ff000024df6058 by task syz-fuzzer/2256 Pointer tag: [f9], memory tag: [fe] CPU: 1 PID: 2256 Comm: syz-fuzzer Not tainted 6.1.0-rc5-syzkaller-00008- ge01d50cbd6ee #0 Hardware name: linux,dummy-virt (DT) Call trace: dump_backtrace.part.0+0xe0/0xf0 arch/arm64/kernel/stacktrace.c:156 dump_backtrace arch/arm64/kernel/stacktrace.c:162 [inline] show_stack+0x18/0x40 arch/arm64/kernel/stacktrace.c:163 __dump_stack lib/dump_stack.c:88 [inline] dump_stack_lvl+0x68/0x84 lib/dump_stack.c:106 print_address_description mm/kasan/report.c:284 [inline] print_report+0x1a8/0x4a0 mm/kasan/report.c:395 kasan_report+0x94/0xb4 mm/kasan/report.c:495 __do_kernel_fault+0x164/0x1e0 arch/arm64/mm/fault.c:320 do_bad_area arch/arm64/mm/fault.c:473 [inline] do_tag_check_fault+0x78/0x8c arch/arm64/mm/fault.c:749 do_mem_abort+0x44/0x94 arch/arm64/mm/fault.c:825 el1_abort+0x40/0x60 arch/arm64/kernel/entry-common.c:367 el1h_64_sync_handler+0xd8/0xe4 arch/arm64/kernel/entry-common.c:427 el1h_64_sync+0x64/0x68 arch/arm64/kernel/entry.S:576 hlist_add_head include/linux/list.h:929 [inline] enqueue_timer+0x18/0xa4 kernel/time/timer.c:605 mod_timer+0x14/0x20 kernel/time/timer.c:1161 mrp_periodic_timer_arm net/802/mrp.c:614 [inline] mrp_periodic_timer+0xa0/0xc0 net/802/mrp.c:627 call_timer_fn.constprop.0+0x24/0x80 kernel/time/timer.c:1474 expire_timers+0x98/0xc4 kernel/time/timer.c:1519 To fix it, we can introduce a new active flags to make sure the timer will not restart. Reported-by: syzbot+6fd64001c20aa99e34a4@syzkaller.appspotmail.com Signed-off-by: Schspa Shi Signed-off-by: David S. Miller --- include/net/mrp.h | 1 + net/802/mrp.c | 18 +++++++++++++----- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/include/net/mrp.h b/include/net/mrp.h index 92cd3fb6cf9d..b28915ffea28 100644 --- a/include/net/mrp.h +++ b/include/net/mrp.h @@ -124,6 +124,7 @@ struct mrp_applicant { struct sk_buff *pdu; struct rb_root mad; struct rcu_head rcu; + bool active; }; struct mrp_port { diff --git a/net/802/mrp.c b/net/802/mrp.c index 155f74d8b14f..6c927d4b35f0 100644 --- a/net/802/mrp.c +++ b/net/802/mrp.c @@ -606,7 +606,10 @@ static void mrp_join_timer(struct timer_list *t) spin_unlock(&app->lock); mrp_queue_xmit(app); - mrp_join_timer_arm(app); + spin_lock(&app->lock); + if (likely(app->active)) + mrp_join_timer_arm(app); + spin_unlock(&app->lock); } static void mrp_periodic_timer_arm(struct mrp_applicant *app) @@ -620,11 +623,12 @@ static void mrp_periodic_timer(struct timer_list *t) struct mrp_applicant *app = from_timer(app, t, periodic_timer); spin_lock(&app->lock); - mrp_mad_event(app, MRP_EVENT_PERIODIC); - mrp_pdu_queue(app); + if (likely(app->active)) { + mrp_mad_event(app, MRP_EVENT_PERIODIC); + mrp_pdu_queue(app); + mrp_periodic_timer_arm(app); + } spin_unlock(&app->lock); - - mrp_periodic_timer_arm(app); } static int mrp_pdu_parse_end_mark(struct sk_buff *skb, int *offset) @@ -872,6 +876,7 @@ int mrp_init_applicant(struct net_device *dev, struct mrp_application *appl) app->dev = dev; app->app = appl; app->mad = RB_ROOT; + app->active = true; spin_lock_init(&app->lock); skb_queue_head_init(&app->queue); rcu_assign_pointer(dev->mrp_port->applicants[appl->type], app); @@ -900,6 +905,9 @@ void mrp_uninit_applicant(struct net_device *dev, struct mrp_application *appl) RCU_INIT_POINTER(port->applicants[appl->type], NULL); + spin_lock_bh(&app->lock); + app->active = false; + spin_unlock_bh(&app->lock); /* Delete timer and generate a final TX event to flush out * all pending messages before the applicant is gone. */ From 97c11d6e31547183e2404087f0fb23b34dbe2cc3 Mon Sep 17 00:00:00 2001 From: Kumar Kartikeya Dwivedi Date: Sat, 19 Nov 2022 00:29:38 +0530 Subject: [PATCH 1073/2002] selftests/bpf: Skip spin lock failure test on s390x Instead of adding the whole test to DENYLIST.s390x, which also has success test cases that should be run, just skip over failure test cases in case the JIT does not support kfuncs. Signed-off-by: Kumar Kartikeya Dwivedi Link: https://lore.kernel.org/r/20221118185938.2139616-3-memxor@gmail.com Signed-off-by: Alexei Starovoitov --- tools/testing/selftests/bpf/prog_tests/spin_lock.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tools/testing/selftests/bpf/prog_tests/spin_lock.c b/tools/testing/selftests/bpf/prog_tests/spin_lock.c index 72282e92a78a..d9270bd3d920 100644 --- a/tools/testing/selftests/bpf/prog_tests/spin_lock.c +++ b/tools/testing/selftests/bpf/prog_tests/spin_lock.c @@ -68,6 +68,12 @@ static void test_spin_lock_fail_prog(const char *prog_name, const char *err_msg) if (!ASSERT_ERR(ret, "test_spin_lock_fail__load must fail")) goto end; + /* Skip check if JIT does not support kfuncs */ + if (strstr(log_buf, "JIT does not support calling kernel function")) { + test__skip(); + goto end; + } + if (!ASSERT_OK_PTR(strstr(log_buf, err_msg), "expected error message")) { fprintf(stderr, "Expected: %s\n", err_msg); fprintf(stderr, "Verifier: %s\n", log_buf); From f80e16b614f303b520465b7c704ff89fab800f2f Mon Sep 17 00:00:00 2001 From: Andrii Nakryiko Date: Thu, 17 Nov 2022 11:28:24 -0800 Subject: [PATCH 1074/2002] libbpf: Ignore hashmap__find() result explicitly in btf_dump Coverity is reporting that btf_dump_name_dups() doesn't check return result of hashmap__find() call. This is intentional, so make it explicit with (void) cast. Signed-off-by: Andrii Nakryiko Signed-off-by: Daniel Borkmann Link: https://lore.kernel.org/bpf/20221117192824.4093553-1-andrii@kernel.org --- tools/lib/bpf/btf_dump.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/lib/bpf/btf_dump.c b/tools/lib/bpf/btf_dump.c index e9f849d82124..deb2bc9a0a7b 100644 --- a/tools/lib/bpf/btf_dump.c +++ b/tools/lib/bpf/btf_dump.c @@ -1543,7 +1543,7 @@ static size_t btf_dump_name_dups(struct btf_dump *d, struct hashmap *name_map, if (!new_name) return 1; - hashmap__find(name_map, orig_name, &dup_cnt); + (void)hashmap__find(name_map, orig_name, &dup_cnt); dup_cnt++; err = hashmap__set(name_map, new_name, dup_cnt, &old_name, NULL); From d1e91173cd29ba13c5953d1820a7543140feec93 Mon Sep 17 00:00:00 2001 From: Maryam Tahhan Date: Tue, 15 Nov 2022 09:49:21 -0500 Subject: [PATCH 1075/2002] bpf, docs: DEVMAPs and XDP_REDIRECT MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add documentation for BPF_MAP_TYPE_DEVMAP and BPF_MAP_TYPE_DEVMAP_HASH including kernel version introduced, usage and examples. Add documentation that describes XDP_REDIRECT. Signed-off-by: Maryam Tahhan Signed-off-by: Daniel Borkmann Reviewed-by: Toke Høiland-Jørgensen Acked-by: Yonghong Song Link: https://lore.kernel.org/bpf/20221115144921.165483-1-mtahhan@redhat.com --- Documentation/bpf/index.rst | 1 + Documentation/bpf/map_devmap.rst | 222 +++++++++++++++++++++++++++++++ Documentation/bpf/redirect.rst | 81 +++++++++++ net/core/filter.c | 8 +- 4 files changed, 310 insertions(+), 2 deletions(-) create mode 100644 Documentation/bpf/map_devmap.rst create mode 100644 Documentation/bpf/redirect.rst diff --git a/Documentation/bpf/index.rst b/Documentation/bpf/index.rst index 1b50de1983ee..1088d44634d6 100644 --- a/Documentation/bpf/index.rst +++ b/Documentation/bpf/index.rst @@ -29,6 +29,7 @@ that goes into great technical depth about the BPF Architecture. clang-notes linux-notes other + redirect .. only:: subproject and html diff --git a/Documentation/bpf/map_devmap.rst b/Documentation/bpf/map_devmap.rst new file mode 100644 index 000000000000..f64da348dbfe --- /dev/null +++ b/Documentation/bpf/map_devmap.rst @@ -0,0 +1,222 @@ +.. SPDX-License-Identifier: GPL-2.0-only +.. Copyright (C) 2022 Red Hat, Inc. + +================================================= +BPF_MAP_TYPE_DEVMAP and BPF_MAP_TYPE_DEVMAP_HASH +================================================= + +.. note:: + - ``BPF_MAP_TYPE_DEVMAP`` was introduced in kernel version 4.14 + - ``BPF_MAP_TYPE_DEVMAP_HASH`` was introduced in kernel version 5.4 + +``BPF_MAP_TYPE_DEVMAP`` and ``BPF_MAP_TYPE_DEVMAP_HASH`` are BPF maps primarily +used as backend maps for the XDP BPF helper call ``bpf_redirect_map()``. +``BPF_MAP_TYPE_DEVMAP`` is backed by an array that uses the key as +the index to lookup a reference to a net device. While ``BPF_MAP_TYPE_DEVMAP_HASH`` +is backed by a hash table that uses a key to lookup a reference to a net device. +The user provides either <``key``/ ``ifindex``> or <``key``/ ``struct bpf_devmap_val``> +pairs to update the maps with new net devices. + +.. note:: + - The key to a hash map doesn't have to be an ``ifindex``. + - While ``BPF_MAP_TYPE_DEVMAP_HASH`` allows for densely packing the net devices + it comes at the cost of a hash of the key when performing a look up. + +The setup and packet enqueue/send code is shared between the two types of +devmap; only the lookup and insertion is different. + +Usage +===== +Kernel BPF +---------- +.. c:function:: + long bpf_redirect_map(struct bpf_map *map, u32 key, u64 flags) + +Redirect the packet to the endpoint referenced by ``map`` at index ``key``. +For ``BPF_MAP_TYPE_DEVMAP`` and ``BPF_MAP_TYPE_DEVMAP_HASH`` this map contains +references to net devices (for forwarding packets through other ports). + +The lower two bits of *flags* are used as the return code if the map lookup +fails. This is so that the return value can be one of the XDP program return +codes up to ``XDP_TX``, as chosen by the caller. The higher bits of ``flags`` +can be set to ``BPF_F_BROADCAST`` or ``BPF_F_EXCLUDE_INGRESS`` as defined +below. + +With ``BPF_F_BROADCAST`` the packet will be broadcast to all the interfaces +in the map, with ``BPF_F_EXCLUDE_INGRESS`` the ingress interface will be excluded +from the broadcast. + +.. note:: + - The key is ignored if BPF_F_BROADCAST is set. + - The broadcast feature can also be used to implement multicast forwarding: + simply create multiple DEVMAPs, each one corresponding to a single multicast group. + +This helper will return ``XDP_REDIRECT`` on success, or the value of the two +lower bits of the ``flags`` argument if the map lookup fails. + +More information about redirection can be found :doc:`redirect` + +.. c:function:: + void *bpf_map_lookup_elem(struct bpf_map *map, const void *key) + +Net device entries can be retrieved using the ``bpf_map_lookup_elem()`` +helper. + +Userspace +--------- +.. note:: + DEVMAP entries can only be updated/deleted from user space and not + from an eBPF program. Trying to call these functions from a kernel eBPF + program will result in the program failing to load and a verifier warning. + +.. c:function:: + int bpf_map_update_elem(int fd, const void *key, const void *value, __u64 flags); + + Net device entries can be added or updated using the ``bpf_map_update_elem()`` + helper. This helper replaces existing elements atomically. The ``value`` parameter + can be ``struct bpf_devmap_val`` or a simple ``int ifindex`` for backwards + compatibility. + + .. code-block:: c + + struct bpf_devmap_val { + __u32 ifindex; /* device index */ + union { + int fd; /* prog fd on map write */ + __u32 id; /* prog id on map read */ + } bpf_prog; + }; + + The ``flags`` argument can be one of the following: + + - ``BPF_ANY``: Create a new element or update an existing element. + - ``BPF_NOEXIST``: Create a new element only if it did not exist. + - ``BPF_EXIST``: Update an existing element. + + DEVMAPs can associate a program with a device entry by adding a ``bpf_prog.fd`` + to ``struct bpf_devmap_val``. Programs are run after ``XDP_REDIRECT`` and have + access to both Rx device and Tx device. The program associated with the ``fd`` + must have type XDP with expected attach type ``xdp_devmap``. + When a program is associated with a device index, the program is run on an + ``XDP_REDIRECT`` and before the buffer is added to the per-cpu queue. Examples + of how to attach/use xdp_devmap progs can be found in the kernel selftests: + + - ``tools/testing/selftests/bpf/prog_tests/xdp_devmap_attach.c`` + - ``tools/testing/selftests/bpf/progs/test_xdp_with_devmap_helpers.c`` + +.. c:function:: + int bpf_map_lookup_elem(int fd, const void *key, void *value); + + Net device entries can be retrieved using the ``bpf_map_lookup_elem()`` + helper. + +.. c:function:: + int bpf_map_delete_elem(int fd, const void *key); + + Net device entries can be deleted using the ``bpf_map_delete_elem()`` + helper. This helper will return 0 on success, or negative error in case of + failure. + +Examples +======== + +Kernel BPF +---------- + +The following code snippet shows how to declare a ``BPF_MAP_TYPE_DEVMAP`` +called tx_port. + +.. code-block:: c + + struct { + __uint(type, BPF_MAP_TYPE_DEVMAP); + __type(key, __u32); + __type(value, __u32); + __uint(max_entries, 256); + } tx_port SEC(".maps"); + +The following code snippet shows how to declare a ``BPF_MAP_TYPE_DEVMAP_HASH`` +called forward_map. + +.. code-block:: c + + struct { + __uint(type, BPF_MAP_TYPE_DEVMAP_HASH); + __type(key, __u32); + __type(value, struct bpf_devmap_val); + __uint(max_entries, 32); + } forward_map SEC(".maps"); + +.. note:: + + The value type in the DEVMAP above is a ``struct bpf_devmap_val`` + +The following code snippet shows a simple xdp_redirect_map program. This program +would work with a user space program that populates the devmap ``forward_map`` based +on ingress ifindexes. The BPF program (below) is redirecting packets using the +ingress ``ifindex`` as the ``key``. + +.. code-block:: c + + SEC("xdp") + int xdp_redirect_map_func(struct xdp_md *ctx) + { + int index = ctx->ingress_ifindex; + + return bpf_redirect_map(&forward_map, index, 0); + } + +The following code snippet shows a BPF program that is broadcasting packets to +all the interfaces in the ``tx_port`` devmap. + +.. code-block:: c + + SEC("xdp") + int xdp_redirect_map_func(struct xdp_md *ctx) + { + return bpf_redirect_map(&tx_port, 0, BPF_F_BROADCAST | BPF_F_EXCLUDE_INGRESS); + } + +User space +---------- + +The following code snippet shows how to update a devmap called ``tx_port``. + +.. code-block:: c + + int update_devmap(int ifindex, int redirect_ifindex) + { + int ret; + + ret = bpf_map_update_elem(bpf_map__fd(tx_port), &ifindex, &redirect_ifindex, 0); + if (ret < 0) { + fprintf(stderr, "Failed to update devmap_ value: %s\n", + strerror(errno)); + } + + return ret; + } + +The following code snippet shows how to update a hash_devmap called ``forward_map``. + +.. code-block:: c + + int update_devmap(int ifindex, int redirect_ifindex) + { + struct bpf_devmap_val devmap_val = { .ifindex = redirect_ifindex }; + int ret; + + ret = bpf_map_update_elem(bpf_map__fd(forward_map), &ifindex, &devmap_val, 0); + if (ret < 0) { + fprintf(stderr, "Failed to update devmap_ value: %s\n", + strerror(errno)); + } + return ret; + } + +References +=========== + +- https://lwn.net/Articles/728146/ +- https://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf-next.git/commit/?id=6f9d451ab1a33728adb72d7ff66a7b374d665176 +- https://elixir.bootlin.com/linux/latest/source/net/core/filter.c#L4106 diff --git a/Documentation/bpf/redirect.rst b/Documentation/bpf/redirect.rst new file mode 100644 index 000000000000..2fa2b0b05004 --- /dev/null +++ b/Documentation/bpf/redirect.rst @@ -0,0 +1,81 @@ +.. SPDX-License-Identifier: GPL-2.0-only +.. Copyright (C) 2022 Red Hat, Inc. + +======== +Redirect +======== +XDP_REDIRECT +############ +Supported maps +-------------- + +XDP_REDIRECT works with the following map types: + +- ``BPF_MAP_TYPE_DEVMAP`` +- ``BPF_MAP_TYPE_DEVMAP_HASH`` +- ``BPF_MAP_TYPE_CPUMAP`` +- ``BPF_MAP_TYPE_XSKMAP`` + +For more information on these maps, please see the specific map documentation. + +Process +------- + +.. kernel-doc:: net/core/filter.c + :doc: xdp redirect + +.. note:: + Not all drivers support transmitting frames after a redirect, and for + those that do, not all of them support non-linear frames. Non-linear xdp + bufs/frames are bufs/frames that contain more than one fragment. + +Debugging packet drops +---------------------- +Silent packet drops for XDP_REDIRECT can be debugged using: + +- bpf_trace +- perf_record + +bpf_trace +^^^^^^^^^ +The following bpftrace command can be used to capture and count all XDP tracepoints: + +.. code-block:: none + + sudo bpftrace -e 'tracepoint:xdp:* { @cnt[probe] = count(); }' + Attaching 12 probes... + ^C + + @cnt[tracepoint:xdp:mem_connect]: 18 + @cnt[tracepoint:xdp:mem_disconnect]: 18 + @cnt[tracepoint:xdp:xdp_exception]: 19605 + @cnt[tracepoint:xdp:xdp_devmap_xmit]: 1393604 + @cnt[tracepoint:xdp:xdp_redirect]: 22292200 + +.. note:: + The various xdp tracepoints can be found in ``source/include/trace/events/xdp.h`` + +The following bpftrace command can be used to extract the ``ERRNO`` being returned as +part of the err parameter: + +.. code-block:: none + + sudo bpftrace -e \ + 'tracepoint:xdp:xdp_redirect*_err {@redir_errno[-args->err] = count();} + tracepoint:xdp:xdp_devmap_xmit {@devmap_errno[-args->err] = count();}' + +perf record +^^^^^^^^^^^ +The perf tool also supports recording tracepoints: + +.. code-block:: none + + perf record -a -e xdp:xdp_redirect_err \ + -e xdp:xdp_redirect_map_err \ + -e xdp:xdp_exception \ + -e xdp:xdp_devmap_xmit + +References +=========== + +- https://github.com/xdp-project/xdp-tutorial/tree/master/tracing02-xdp-monitor diff --git a/net/core/filter.c b/net/core/filter.c index 754dd01354d8..2a105fb1ceb2 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -4108,7 +4108,10 @@ static const struct bpf_func_proto bpf_xdp_adjust_meta_proto = { .arg2_type = ARG_ANYTHING, }; -/* XDP_REDIRECT works by a three-step process, implemented in the functions +/** + * DOC: xdp redirect + * + * XDP_REDIRECT works by a three-step process, implemented in the functions * below: * * 1. The bpf_redirect() and bpf_redirect_map() helpers will lookup the target @@ -4123,7 +4126,8 @@ static const struct bpf_func_proto bpf_xdp_adjust_meta_proto = { * 3. Before exiting its NAPI poll loop, the driver will call xdp_do_flush(), * which will flush all the different bulk queues, thus completing the * redirect. - * + */ +/* * Pointers to the map entries will be kept around for this whole sequence of * steps, protected by RCU. However, there is no top-level rcu_read_lock() in * the core code; instead, the RCU protection relies on everything happening From ee748cd95e3adf4acdb05194b2ea68e4073e09b6 Mon Sep 17 00:00:00 2001 From: Tiezhu Yang Date: Fri, 18 Nov 2022 17:50:01 +0800 Subject: [PATCH 1076/2002] bpf, samples: Use "grep -E" instead of "egrep" The latest version of grep (3.8+) claims the egrep is now obsolete so the build now contains warnings that look like: egrep: warning: egrep is obsolescent; using grep -E Fix this up by moving the related file to use "grep -E" instead. Signed-off-by: Tiezhu Yang Signed-off-by: Daniel Borkmann Acked-by: Jiri Olsa Link: https://lore.kernel.org/bpf/1668765001-12477-1-git-send-email-yangtiezhu@loongson.cn --- samples/bpf/test_cgrp2_tc.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/bpf/test_cgrp2_tc.sh b/samples/bpf/test_cgrp2_tc.sh index 12faf5847e22..395573be6ae8 100755 --- a/samples/bpf/test_cgrp2_tc.sh +++ b/samples/bpf/test_cgrp2_tc.sh @@ -115,7 +115,7 @@ do_exit() { if [ "$DEBUG" == "yes" ] && [ "$MODE" != 'cleanuponly' ] then echo "------ DEBUG ------" - echo "mount: "; mount | egrep '(cgroup2|bpf)'; echo + echo "mount: "; mount | grep -E '(cgroup2|bpf)'; echo echo "$CGRP2_TC_LEAF: "; ls -l $CGRP2_TC_LEAF; echo if [ -d "$BPF_FS_TC_SHARE" ] then From c73a72f4cbb47672c8cc7f7d7aba52f1cb15baca Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Thu, 17 Nov 2022 19:39:03 -0800 Subject: [PATCH 1077/2002] netlink: remove the flex array from struct nlmsghdr I've added a flex array to struct nlmsghdr in commit 738136a0e375 ("netlink: split up copies in the ack construction") to allow accessing the data easily. It leads to warnings with clang, if user space wraps this structure into another struct and the flex array is not at the end of the container. Reviewed-by: Kees Cook Reviewed-by: David Ahern Link: https://lore.kernel.org/all/20221114023927.GA685@u2004-local/ Link: https://lore.kernel.org/r/20221118033903.1651026-1-kuba@kernel.org Signed-off-by: Jakub Kicinski --- include/uapi/linux/netlink.h | 2 -- net/netlink/af_netlink.c | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/include/uapi/linux/netlink.h b/include/uapi/linux/netlink.h index 5da0da59bf01..e2ae82e3f9f7 100644 --- a/include/uapi/linux/netlink.h +++ b/include/uapi/linux/netlink.h @@ -48,7 +48,6 @@ struct sockaddr_nl { * @nlmsg_flags: Additional flags * @nlmsg_seq: Sequence number * @nlmsg_pid: Sending process port ID - * @nlmsg_data: Message payload */ struct nlmsghdr { __u32 nlmsg_len; @@ -56,7 +55,6 @@ struct nlmsghdr { __u16 nlmsg_flags; __u32 nlmsg_seq; __u32 nlmsg_pid; - __u8 nlmsg_data[]; }; /* Flags values */ diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c index 9ebdf3262015..d73091f6bb0f 100644 --- a/net/netlink/af_netlink.c +++ b/net/netlink/af_netlink.c @@ -2514,7 +2514,7 @@ void netlink_ack(struct sk_buff *in_skb, struct nlmsghdr *nlh, int err, if (!nlmsg_append(skb, nlmsg_len(nlh))) goto err_bad_put; - memcpy(errmsg->msg.nlmsg_data, nlh->nlmsg_data, + memcpy(nlmsg_data(&errmsg->msg), nlmsg_data(nlh), nlmsg_len(nlh)); } From dbc4af768ba1670018c053b35a8613b811874f9c Mon Sep 17 00:00:00 2001 From: Lukas Bulwahn Date: Wed, 16 Nov 2022 11:24:50 +0100 Subject: [PATCH 1078/2002] net: fman: remove reference to non-existing config PCS Commit a7c2a32e7f22 ("net: fman: memac: Use lynx pcs driver") makes the Freescale Data-Path Acceleration Architecture Frame Manager use lynx pcs driver by selecting PCS_LYNX. It also selects the non-existing config PCS as well, which has no effect. Remove this select to a non-existing config. Signed-off-by: Lukas Bulwahn Link: https://lore.kernel.org/r/20221116102450.13928-1-lukas.bulwahn@gmail.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/freescale/fman/Kconfig | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/net/ethernet/freescale/fman/Kconfig b/drivers/net/ethernet/freescale/fman/Kconfig index e76a3d262b2b..a55542c1ad65 100644 --- a/drivers/net/ethernet/freescale/fman/Kconfig +++ b/drivers/net/ethernet/freescale/fman/Kconfig @@ -4,7 +4,6 @@ config FSL_FMAN depends on FSL_SOC || ARCH_LAYERSCAPE || COMPILE_TEST select GENERIC_ALLOCATOR select PHYLINK - select PCS select PCS_LYNX select CRC32 default n From c30e0b9b88b3cf460bd88d50784591fc97b05576 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Wed, 16 Nov 2022 09:07:29 +0100 Subject: [PATCH 1079/2002] net: ethernet: mtk_eth_soc: increase tx ring size for QDMA devices In order to use the hardware traffic shaper feature, a larger tx ring is needed, especially for the scratch ring, which the hardware shaper uses to reorder packets. Signed-off-by: Felix Fietkau Link: https://lore.kernel.org/r/20221116080734.44013-2-nbd@nbd.name Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/mediatek/mtk_eth_soc.c | 38 ++++++++++++--------- drivers/net/ethernet/mediatek/mtk_eth_soc.h | 1 + 2 files changed, 23 insertions(+), 16 deletions(-) diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c index cdc0ff596196..d8d894646c72 100644 --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c @@ -938,7 +938,7 @@ static int mtk_init_fq_dma(struct mtk_eth *eth) { const struct mtk_soc_data *soc = eth->soc; dma_addr_t phy_ring_tail; - int cnt = MTK_DMA_SIZE; + int cnt = MTK_QDMA_RING_SIZE; dma_addr_t dma_addr; int i; @@ -2208,19 +2208,25 @@ static int mtk_tx_alloc(struct mtk_eth *eth) struct mtk_tx_ring *ring = ð->tx_ring; int i, sz = soc->txrx.txd_size; struct mtk_tx_dma_v2 *txd; + int ring_size; - ring->buf = kcalloc(MTK_DMA_SIZE, sizeof(*ring->buf), + if (MTK_HAS_CAPS(soc->caps, MTK_QDMA)) + ring_size = MTK_QDMA_RING_SIZE; + else + ring_size = MTK_DMA_SIZE; + + ring->buf = kcalloc(ring_size, sizeof(*ring->buf), GFP_KERNEL); if (!ring->buf) goto no_tx_mem; - ring->dma = dma_alloc_coherent(eth->dma_dev, MTK_DMA_SIZE * sz, + ring->dma = dma_alloc_coherent(eth->dma_dev, ring_size * sz, &ring->phys, GFP_KERNEL); if (!ring->dma) goto no_tx_mem; - for (i = 0; i < MTK_DMA_SIZE; i++) { - int next = (i + 1) % MTK_DMA_SIZE; + for (i = 0; i < ring_size; i++) { + int next = (i + 1) % ring_size; u32 next_ptr = ring->phys + next * sz; txd = ring->dma + i * sz; @@ -2240,22 +2246,22 @@ static int mtk_tx_alloc(struct mtk_eth *eth) * descriptors in ring->dma_pdma. */ if (!MTK_HAS_CAPS(soc->caps, MTK_QDMA)) { - ring->dma_pdma = dma_alloc_coherent(eth->dma_dev, MTK_DMA_SIZE * sz, + ring->dma_pdma = dma_alloc_coherent(eth->dma_dev, ring_size * sz, &ring->phys_pdma, GFP_KERNEL); if (!ring->dma_pdma) goto no_tx_mem; - for (i = 0; i < MTK_DMA_SIZE; i++) { + for (i = 0; i < ring_size; i++) { ring->dma_pdma[i].txd2 = TX_DMA_DESP2_DEF; ring->dma_pdma[i].txd4 = 0; } } - ring->dma_size = MTK_DMA_SIZE; - atomic_set(&ring->free_count, MTK_DMA_SIZE - 2); + ring->dma_size = ring_size; + atomic_set(&ring->free_count, ring_size - 2); ring->next_free = ring->dma; ring->last_free = (void *)txd; - ring->last_free_ptr = (u32)(ring->phys + ((MTK_DMA_SIZE - 1) * sz)); + ring->last_free_ptr = (u32)(ring->phys + ((ring_size - 1) * sz)); ring->thresh = MAX_SKB_FRAGS; /* make sure that all changes to the dma ring are flushed before we @@ -2267,14 +2273,14 @@ static int mtk_tx_alloc(struct mtk_eth *eth) mtk_w32(eth, ring->phys, soc->reg_map->qdma.ctx_ptr); mtk_w32(eth, ring->phys, soc->reg_map->qdma.dtx_ptr); mtk_w32(eth, - ring->phys + ((MTK_DMA_SIZE - 1) * sz), + ring->phys + ((ring_size - 1) * sz), soc->reg_map->qdma.crx_ptr); mtk_w32(eth, ring->last_free_ptr, soc->reg_map->qdma.drx_ptr); mtk_w32(eth, (QDMA_RES_THRES << 8) | QDMA_RES_THRES, soc->reg_map->qdma.qtx_cfg); } else { mtk_w32(eth, ring->phys_pdma, MT7628_TX_BASE_PTR0); - mtk_w32(eth, MTK_DMA_SIZE, MT7628_TX_MAX_CNT0); + mtk_w32(eth, ring_size, MT7628_TX_MAX_CNT0); mtk_w32(eth, 0, MT7628_TX_CTX_IDX0); mtk_w32(eth, MT7628_PST_DTX_IDX0, soc->reg_map->pdma.rst_idx); } @@ -2292,7 +2298,7 @@ static void mtk_tx_clean(struct mtk_eth *eth) int i; if (ring->buf) { - for (i = 0; i < MTK_DMA_SIZE; i++) + for (i = 0; i < ring->dma_size; i++) mtk_tx_unmap(eth, &ring->buf[i], NULL, false); kfree(ring->buf); ring->buf = NULL; @@ -2300,14 +2306,14 @@ static void mtk_tx_clean(struct mtk_eth *eth) if (ring->dma) { dma_free_coherent(eth->dma_dev, - MTK_DMA_SIZE * soc->txrx.txd_size, + ring->dma_size * soc->txrx.txd_size, ring->dma, ring->phys); ring->dma = NULL; } if (ring->dma_pdma) { dma_free_coherent(eth->dma_dev, - MTK_DMA_SIZE * soc->txrx.txd_size, + ring->dma_size * soc->txrx.txd_size, ring->dma_pdma, ring->phys_pdma); ring->dma_pdma = NULL; } @@ -2842,7 +2848,7 @@ static void mtk_dma_free(struct mtk_eth *eth) netdev_reset_queue(eth->netdev[i]); if (eth->scratch_ring) { dma_free_coherent(eth->dma_dev, - MTK_DMA_SIZE * soc->txrx.txd_size, + MTK_QDMA_RING_SIZE * soc->txrx.txd_size, eth->scratch_ring, eth->phy_scratch_ring); eth->scratch_ring = NULL; eth->phy_scratch_ring = 0; diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.h b/drivers/net/ethernet/mediatek/mtk_eth_soc.h index a572416e25de..72d3bfc2323d 100644 --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h @@ -30,6 +30,7 @@ #define MTK_MAX_RX_LENGTH_2K 2048 #define MTK_TX_DMA_BUF_LEN 0x3fff #define MTK_TX_DMA_BUF_LEN_V2 0xffff +#define MTK_QDMA_RING_SIZE 2048 #define MTK_DMA_SIZE 512 #define MTK_MAC_COUNT 2 #define MTK_RX_ETH_HLEN (ETH_HLEN + ETH_FCS_LEN) From f4b2fa2c25e1ade78f766aa82e733a0b5198d484 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Wed, 16 Nov 2022 09:07:30 +0100 Subject: [PATCH 1080/2002] net: ethernet: mtk_eth_soc: drop packets to WDMA if the ring is full Improves handling of DMA ring overflow. Clarify other WDMA drop related comment. Signed-off-by: Felix Fietkau Link: https://lore.kernel.org/r/20221116080734.44013-3-nbd@nbd.name Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/mediatek/mtk_eth_soc.c | 5 ++++- drivers/net/ethernet/mediatek/mtk_eth_soc.h | 1 + 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c index d8d894646c72..db017ebdb967 100644 --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c @@ -3425,9 +3425,12 @@ static int mtk_hw_init(struct mtk_eth *eth) mtk_w32(eth, 0x21021000, MTK_FE_INT_GRP); if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) { - /* PSE should not drop port8 and port9 packets */ + /* PSE should not drop port8 and port9 packets from WDMA Tx */ mtk_w32(eth, 0x00000300, PSE_DROP_CFG); + /* PSE should drop packets to port 8/9 on WDMA Rx ring full */ + mtk_w32(eth, 0x00000300, PSE_PPE0_DROP); + /* PSE Free Queue Flow Control */ mtk_w32(eth, 0x01fa01f4, PSE_FQFC_CFG2); diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.h b/drivers/net/ethernet/mediatek/mtk_eth_soc.h index 72d3bfc2323d..eaaa0c67ef2a 100644 --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h @@ -127,6 +127,7 @@ #define PSE_FQFC_CFG1 0x100 #define PSE_FQFC_CFG2 0x104 #define PSE_DROP_CFG 0x108 +#define PSE_PPE0_DROP 0x110 /* PSE Input Queue Reservation Register*/ #define PSE_IQ_REV(x) (0x140 + (((x) - 1) << 2)) From 71ba8e4891c799f9f79ea219f155ac795750af41 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Wed, 16 Nov 2022 09:07:31 +0100 Subject: [PATCH 1081/2002] net: ethernet: mtk_eth_soc: avoid port_mg assignment on MT7622 and newer On newer chips, this field is unused and contains some bits related to queue assignment. Initialize it to 0 in those cases. Fix offload_version on MT7621 and MT7623, which still need the previous value. Signed-off-by: Felix Fietkau Link: https://lore.kernel.org/r/20221116080734.44013-4-nbd@nbd.name Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/mediatek/mtk_eth_soc.c | 4 ++-- drivers/net/ethernet/mediatek/mtk_ppe.c | 4 +++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c index db017ebdb967..0595d2744117 100644 --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c @@ -4324,7 +4324,7 @@ static const struct mtk_soc_data mt7621_data = { .hw_features = MTK_HW_FEATURES, .required_clks = MT7621_CLKS_BITMAP, .required_pctl = false, - .offload_version = 2, + .offload_version = 1, .hash_offset = 2, .foe_entry_size = sizeof(struct mtk_foe_entry) - 16, .txrx = { @@ -4363,7 +4363,7 @@ static const struct mtk_soc_data mt7623_data = { .hw_features = MTK_HW_FEATURES, .required_clks = MT7623_CLKS_BITMAP, .required_pctl = true, - .offload_version = 2, + .offload_version = 1, .hash_offset = 2, .foe_entry_size = sizeof(struct mtk_foe_entry) - 16, .txrx = { diff --git a/drivers/net/ethernet/mediatek/mtk_ppe.c b/drivers/net/ethernet/mediatek/mtk_ppe.c index 2d8ca99f2467..3ee2bf53f9e5 100644 --- a/drivers/net/ethernet/mediatek/mtk_ppe.c +++ b/drivers/net/ethernet/mediatek/mtk_ppe.c @@ -175,6 +175,8 @@ int mtk_foe_entry_prepare(struct mtk_eth *eth, struct mtk_foe_entry *entry, val = FIELD_PREP(MTK_FOE_IB2_DEST_PORT_V2, pse_port) | FIELD_PREP(MTK_FOE_IB2_PORT_AG_V2, 0xf); } else { + int port_mg = eth->soc->offload_version > 1 ? 0 : 0x3f; + val = FIELD_PREP(MTK_FOE_IB1_STATE, MTK_FOE_STATE_BIND) | FIELD_PREP(MTK_FOE_IB1_PACKET_TYPE, type) | FIELD_PREP(MTK_FOE_IB1_UDP, l4proto == IPPROTO_UDP) | @@ -182,7 +184,7 @@ int mtk_foe_entry_prepare(struct mtk_eth *eth, struct mtk_foe_entry *entry, entry->ib1 = val; val = FIELD_PREP(MTK_FOE_IB2_DEST_PORT, pse_port) | - FIELD_PREP(MTK_FOE_IB2_PORT_MG, 0x3f) | + FIELD_PREP(MTK_FOE_IB2_PORT_MG, port_mg) | FIELD_PREP(MTK_FOE_IB2_PORT_AG, 0x1f); } From f63959c7eec3151c30a2ee0d351827b62e742dcb Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Wed, 16 Nov 2022 09:07:32 +0100 Subject: [PATCH 1082/2002] net: ethernet: mtk_eth_soc: implement multi-queue support for per-port queues When sending traffic to multiple ports with different link speeds, queued packets to one port can drown out tx to other ports. In order to better handle transmission to multiple ports, use the hardware shaper feature to implement weighted fair queueing between ports. Weight and maximum rate are automatically adjusted based on the link speed of the port. The first 3 queues are unrestricted and reserved for non-DSA direct tx on GMAC ports. The following queues are automatically assigned by the MTK DSA tag driver based on the target port number. The PPE offload code configures the queues for offloaded traffic in the same way. This feature is only supported on devices supporting QDMA. All queues still share the same DMA ring and descriptor pool. Signed-off-by: Felix Fietkau Link: https://lore.kernel.org/r/20221116080734.44013-5-nbd@nbd.name Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/mediatek/mtk_eth_soc.c | 281 ++++++++++++++++---- drivers/net/ethernet/mediatek/mtk_eth_soc.h | 26 +- 2 files changed, 258 insertions(+), 49 deletions(-) diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c index 0595d2744117..41eca4887f18 100644 --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c @@ -55,6 +55,7 @@ static const struct mtk_reg_map mtk_reg_map = { }, .qdma = { .qtx_cfg = 0x1800, + .qtx_sch = 0x1804, .rx_ptr = 0x1900, .rx_cnt_cfg = 0x1904, .qcrx_ptr = 0x1908, @@ -62,6 +63,7 @@ static const struct mtk_reg_map mtk_reg_map = { .rst_idx = 0x1a08, .delay_irq = 0x1a0c, .fc_th = 0x1a10, + .tx_sch_rate = 0x1a14, .int_grp = 0x1a20, .hred = 0x1a44, .ctx_ptr = 0x1b00, @@ -114,6 +116,7 @@ static const struct mtk_reg_map mt7986_reg_map = { }, .qdma = { .qtx_cfg = 0x4400, + .qtx_sch = 0x4404, .rx_ptr = 0x4500, .rx_cnt_cfg = 0x4504, .qcrx_ptr = 0x4508, @@ -131,6 +134,7 @@ static const struct mtk_reg_map mt7986_reg_map = { .fq_tail = 0x4724, .fq_count = 0x4728, .fq_blen = 0x472c, + .tx_sch_rate = 0x4798, }, .gdm1_cnt = 0x1c00, .gdma_to_ppe = 0x3333, @@ -614,6 +618,75 @@ static void mtk_mac_link_down(struct phylink_config *config, unsigned int mode, mtk_w32(mac->hw, mcr, MTK_MAC_MCR(mac->id)); } +static void mtk_set_queue_speed(struct mtk_eth *eth, unsigned int idx, + int speed) +{ + const struct mtk_soc_data *soc = eth->soc; + u32 ofs, val; + + if (!MTK_HAS_CAPS(soc->caps, MTK_QDMA)) + return; + + val = MTK_QTX_SCH_MIN_RATE_EN | + /* minimum: 10 Mbps */ + FIELD_PREP(MTK_QTX_SCH_MIN_RATE_MAN, 1) | + FIELD_PREP(MTK_QTX_SCH_MIN_RATE_EXP, 4) | + MTK_QTX_SCH_LEAKY_BUCKET_SIZE; + if (!MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) + val |= MTK_QTX_SCH_LEAKY_BUCKET_EN; + + if (IS_ENABLED(CONFIG_SOC_MT7621)) { + switch (speed) { + case SPEED_10: + val |= MTK_QTX_SCH_MAX_RATE_EN | + FIELD_PREP(MTK_QTX_SCH_MAX_RATE_MAN, 103) | + FIELD_PREP(MTK_QTX_SCH_MAX_RATE_EXP, 2) | + FIELD_PREP(MTK_QTX_SCH_MAX_RATE_WEIGHT, 1); + break; + case SPEED_100: + val |= MTK_QTX_SCH_MAX_RATE_EN | + FIELD_PREP(MTK_QTX_SCH_MAX_RATE_MAN, 103) | + FIELD_PREP(MTK_QTX_SCH_MAX_RATE_EXP, 3); + FIELD_PREP(MTK_QTX_SCH_MAX_RATE_WEIGHT, 1); + break; + case SPEED_1000: + val |= MTK_QTX_SCH_MAX_RATE_EN | + FIELD_PREP(MTK_QTX_SCH_MAX_RATE_MAN, 105) | + FIELD_PREP(MTK_QTX_SCH_MAX_RATE_EXP, 4) | + FIELD_PREP(MTK_QTX_SCH_MAX_RATE_WEIGHT, 10); + break; + default: + break; + } + } else { + switch (speed) { + case SPEED_10: + val |= MTK_QTX_SCH_MAX_RATE_EN | + FIELD_PREP(MTK_QTX_SCH_MAX_RATE_MAN, 1) | + FIELD_PREP(MTK_QTX_SCH_MAX_RATE_EXP, 4) | + FIELD_PREP(MTK_QTX_SCH_MAX_RATE_WEIGHT, 1); + break; + case SPEED_100: + val |= MTK_QTX_SCH_MAX_RATE_EN | + FIELD_PREP(MTK_QTX_SCH_MAX_RATE_MAN, 1) | + FIELD_PREP(MTK_QTX_SCH_MAX_RATE_EXP, 5); + FIELD_PREP(MTK_QTX_SCH_MAX_RATE_WEIGHT, 1); + break; + case SPEED_1000: + val |= MTK_QTX_SCH_MAX_RATE_EN | + FIELD_PREP(MTK_QTX_SCH_MAX_RATE_MAN, 10) | + FIELD_PREP(MTK_QTX_SCH_MAX_RATE_EXP, 5) | + FIELD_PREP(MTK_QTX_SCH_MAX_RATE_WEIGHT, 10); + break; + default: + break; + } + } + + ofs = MTK_QTX_OFFSET * idx; + mtk_w32(eth, val, soc->reg_map->qdma.qtx_sch + ofs); +} + static void mtk_mac_link_up(struct phylink_config *config, struct phy_device *phy, unsigned int mode, phy_interface_t interface, @@ -639,6 +712,8 @@ static void mtk_mac_link_up(struct phylink_config *config, break; } + mtk_set_queue_speed(mac->hw, mac->id, speed); + /* Configure duplex */ if (duplex == DUPLEX_FULL) mcr |= MAC_MCR_FORCE_DPX; @@ -1099,7 +1174,8 @@ static void mtk_tx_set_dma_desc_v1(struct net_device *dev, void *txd, WRITE_ONCE(desc->txd1, info->addr); - data = TX_DMA_SWC | TX_DMA_PLEN0(info->size); + data = TX_DMA_SWC | TX_DMA_PLEN0(info->size) | + FIELD_PREP(TX_DMA_PQID, info->qid); if (info->last) data |= TX_DMA_LS0; WRITE_ONCE(desc->txd3, data); @@ -1133,9 +1209,6 @@ static void mtk_tx_set_dma_desc_v2(struct net_device *dev, void *txd, data |= TX_DMA_LS0; WRITE_ONCE(desc->txd3, data); - if (!info->qid && mac->id) - info->qid = MTK_QDMA_GMAC2_QID; - data = (mac->id + 1) << TX_DMA_FPORT_SHIFT_V2; /* forward port */ data |= TX_DMA_SWC_V2 | QID_BITS_V2(info->qid); WRITE_ONCE(desc->txd4, data); @@ -1179,11 +1252,12 @@ static int mtk_tx_map(struct sk_buff *skb, struct net_device *dev, .gso = gso, .csum = skb->ip_summed == CHECKSUM_PARTIAL, .vlan = skb_vlan_tag_present(skb), - .qid = skb->mark & MTK_QDMA_TX_MASK, + .qid = skb_get_queue_mapping(skb), .vlan_tci = skb_vlan_tag_get(skb), .first = true, .last = !skb_is_nonlinear(skb), }; + struct netdev_queue *txq; struct mtk_mac *mac = netdev_priv(dev); struct mtk_eth *eth = mac->hw; const struct mtk_soc_data *soc = eth->soc; @@ -1191,8 +1265,10 @@ static int mtk_tx_map(struct sk_buff *skb, struct net_device *dev, struct mtk_tx_dma *itxd_pdma, *txd_pdma; struct mtk_tx_buf *itx_buf, *tx_buf; int i, n_desc = 1; + int queue = skb_get_queue_mapping(skb); int k = 0; + txq = netdev_get_tx_queue(dev, queue); itxd = ring->next_free; itxd_pdma = qdma_to_pdma(ring, itxd); if (itxd == ring->last_free) @@ -1241,7 +1317,7 @@ static int mtk_tx_map(struct sk_buff *skb, struct net_device *dev, memset(&txd_info, 0, sizeof(struct mtk_tx_dma_desc_info)); txd_info.size = min_t(unsigned int, frag_size, soc->txrx.dma_max_len); - txd_info.qid = skb->mark & MTK_QDMA_TX_MASK; + txd_info.qid = queue; txd_info.last = i == skb_shinfo(skb)->nr_frags - 1 && !(frag_size - txd_info.size); txd_info.addr = skb_frag_dma_map(eth->dma_dev, frag, @@ -1280,7 +1356,7 @@ static int mtk_tx_map(struct sk_buff *skb, struct net_device *dev, txd_pdma->txd2 |= TX_DMA_LS1; } - netdev_sent_queue(dev, skb->len); + netdev_tx_sent_queue(txq, skb->len); skb_tx_timestamp(skb); ring->next_free = mtk_qdma_phys_to_virt(ring, txd->txd2); @@ -1292,8 +1368,7 @@ static int mtk_tx_map(struct sk_buff *skb, struct net_device *dev, wmb(); if (MTK_HAS_CAPS(soc->caps, MTK_QDMA)) { - if (netif_xmit_stopped(netdev_get_tx_queue(dev, 0)) || - !netdev_xmit_more()) + if (netif_xmit_stopped(txq) || !netdev_xmit_more()) mtk_w32(eth, txd->txd2, soc->reg_map->qdma.ctx_ptr); } else { int next_idx; @@ -1362,7 +1437,7 @@ static void mtk_wake_queue(struct mtk_eth *eth) for (i = 0; i < MTK_MAC_COUNT; i++) { if (!eth->netdev[i]) continue; - netif_wake_queue(eth->netdev[i]); + netif_tx_wake_all_queues(eth->netdev[i]); } } @@ -1386,7 +1461,7 @@ static netdev_tx_t mtk_start_xmit(struct sk_buff *skb, struct net_device *dev) tx_num = mtk_cal_txd_req(eth, skb); if (unlikely(atomic_read(&ring->free_count) <= tx_num)) { - netif_stop_queue(dev); + netif_tx_stop_all_queues(dev); netif_err(eth, tx_queued, dev, "Tx Ring full when queue awake!\n"); spin_unlock(ð->page_lock); @@ -1412,7 +1487,7 @@ static netdev_tx_t mtk_start_xmit(struct sk_buff *skb, struct net_device *dev) goto drop; if (unlikely(atomic_read(&ring->free_count) <= ring->thresh)) - netif_stop_queue(dev); + netif_tx_stop_all_queues(dev); spin_unlock(ð->page_lock); @@ -1579,10 +1654,12 @@ static int mtk_xdp_submit_frame(struct mtk_eth *eth, struct xdp_frame *xdpf, struct skb_shared_info *sinfo = xdp_get_shared_info_from_frame(xdpf); const struct mtk_soc_data *soc = eth->soc; struct mtk_tx_ring *ring = ð->tx_ring; + struct mtk_mac *mac = netdev_priv(dev); struct mtk_tx_dma_desc_info txd_info = { .size = xdpf->len, .first = true, .last = !xdp_frame_has_frags(xdpf), + .qid = mac->id, }; int err, index = 0, n_desc = 1, nr_frags; struct mtk_tx_buf *htx_buf, *tx_buf; @@ -1632,6 +1709,7 @@ static int mtk_xdp_submit_frame(struct mtk_eth *eth, struct xdp_frame *xdpf, memset(&txd_info, 0, sizeof(struct mtk_tx_dma_desc_info)); txd_info.size = skb_frag_size(&sinfo->frags[index]); txd_info.last = index + 1 == nr_frags; + txd_info.qid = mac->id; data = skb_frag_address(&sinfo->frags[index]); index++; @@ -1992,8 +2070,46 @@ rx_done: return done; } +struct mtk_poll_state { + struct netdev_queue *txq; + unsigned int total; + unsigned int done; + unsigned int bytes; +}; + +static void +mtk_poll_tx_done(struct mtk_eth *eth, struct mtk_poll_state *state, u8 mac, + struct sk_buff *skb) +{ + struct netdev_queue *txq; + struct net_device *dev; + unsigned int bytes = skb->len; + + state->total++; + eth->tx_packets++; + eth->tx_bytes += bytes; + + dev = eth->netdev[mac]; + if (!dev) + return; + + txq = netdev_get_tx_queue(dev, skb_get_queue_mapping(skb)); + if (state->txq == txq) { + state->done++; + state->bytes += bytes; + return; + } + + if (state->txq) + netdev_tx_completed_queue(state->txq, state->done, state->bytes); + + state->txq = txq; + state->done = 1; + state->bytes = bytes; +} + static int mtk_poll_tx_qdma(struct mtk_eth *eth, int budget, - unsigned int *done, unsigned int *bytes) + struct mtk_poll_state *state) { const struct mtk_reg_map *reg_map = eth->soc->reg_map; struct mtk_tx_ring *ring = ð->tx_ring; @@ -2025,12 +2141,9 @@ static int mtk_poll_tx_qdma(struct mtk_eth *eth, int budget, break; if (tx_buf->data != (void *)MTK_DMA_DUMMY_DESC) { - if (tx_buf->type == MTK_TYPE_SKB) { - struct sk_buff *skb = tx_buf->data; + if (tx_buf->type == MTK_TYPE_SKB) + mtk_poll_tx_done(eth, state, mac, tx_buf->data); - bytes[mac] += skb->len; - done[mac]++; - } budget--; } mtk_tx_unmap(eth, tx_buf, &bq, true); @@ -2049,7 +2162,7 @@ static int mtk_poll_tx_qdma(struct mtk_eth *eth, int budget, } static int mtk_poll_tx_pdma(struct mtk_eth *eth, int budget, - unsigned int *done, unsigned int *bytes) + struct mtk_poll_state *state) { struct mtk_tx_ring *ring = ð->tx_ring; struct mtk_tx_buf *tx_buf; @@ -2067,12 +2180,8 @@ static int mtk_poll_tx_pdma(struct mtk_eth *eth, int budget, break; if (tx_buf->data != (void *)MTK_DMA_DUMMY_DESC) { - if (tx_buf->type == MTK_TYPE_SKB) { - struct sk_buff *skb = tx_buf->data; - - bytes[0] += skb->len; - done[0]++; - } + if (tx_buf->type == MTK_TYPE_SKB) + mtk_poll_tx_done(eth, state, 0, tx_buf->data); budget--; } mtk_tx_unmap(eth, tx_buf, &bq, true); @@ -2094,26 +2203,15 @@ static int mtk_poll_tx(struct mtk_eth *eth, int budget) { struct mtk_tx_ring *ring = ð->tx_ring; struct dim_sample dim_sample = {}; - unsigned int done[MTK_MAX_DEVS]; - unsigned int bytes[MTK_MAX_DEVS]; - int total = 0, i; - - memset(done, 0, sizeof(done)); - memset(bytes, 0, sizeof(bytes)); + struct mtk_poll_state state = {}; if (MTK_HAS_CAPS(eth->soc->caps, MTK_QDMA)) - budget = mtk_poll_tx_qdma(eth, budget, done, bytes); + budget = mtk_poll_tx_qdma(eth, budget, &state); else - budget = mtk_poll_tx_pdma(eth, budget, done, bytes); + budget = mtk_poll_tx_pdma(eth, budget, &state); - for (i = 0; i < MTK_MAC_COUNT; i++) { - if (!eth->netdev[i] || !done[i]) - continue; - netdev_completed_queue(eth->netdev[i], done[i], bytes[i]); - total += done[i]; - eth->tx_packets += done[i]; - eth->tx_bytes += bytes[i]; - } + if (state.txq) + netdev_tx_completed_queue(state.txq, state.done, state.bytes); dim_update_sample(eth->tx_events, eth->tx_packets, eth->tx_bytes, &dim_sample); @@ -2123,7 +2221,7 @@ static int mtk_poll_tx(struct mtk_eth *eth, int budget) (atomic_read(&ring->free_count) > ring->thresh)) mtk_wake_queue(eth); - return total; + return state.total; } static void mtk_handle_status_irq(struct mtk_eth *eth) @@ -2209,6 +2307,7 @@ static int mtk_tx_alloc(struct mtk_eth *eth) int i, sz = soc->txrx.txd_size; struct mtk_tx_dma_v2 *txd; int ring_size; + u32 ofs, val; if (MTK_HAS_CAPS(soc->caps, MTK_QDMA)) ring_size = MTK_QDMA_RING_SIZE; @@ -2276,8 +2375,25 @@ static int mtk_tx_alloc(struct mtk_eth *eth) ring->phys + ((ring_size - 1) * sz), soc->reg_map->qdma.crx_ptr); mtk_w32(eth, ring->last_free_ptr, soc->reg_map->qdma.drx_ptr); - mtk_w32(eth, (QDMA_RES_THRES << 8) | QDMA_RES_THRES, - soc->reg_map->qdma.qtx_cfg); + + for (i = 0, ofs = 0; i < MTK_QDMA_NUM_QUEUES; i++) { + val = (QDMA_RES_THRES << 8) | QDMA_RES_THRES; + mtk_w32(eth, val, soc->reg_map->qdma.qtx_cfg + ofs); + + val = MTK_QTX_SCH_MIN_RATE_EN | + /* minimum: 10 Mbps */ + FIELD_PREP(MTK_QTX_SCH_MIN_RATE_MAN, 1) | + FIELD_PREP(MTK_QTX_SCH_MIN_RATE_EXP, 4) | + MTK_QTX_SCH_LEAKY_BUCKET_SIZE; + if (!MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) + val |= MTK_QTX_SCH_LEAKY_BUCKET_EN; + mtk_w32(eth, val, soc->reg_map->qdma.qtx_sch + ofs); + ofs += MTK_QTX_OFFSET; + } + val = MTK_QDMA_TX_SCH_MAX_WFQ | (MTK_QDMA_TX_SCH_MAX_WFQ << 16); + mtk_w32(eth, val, soc->reg_map->qdma.tx_sch_rate); + if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) + mtk_w32(eth, val, soc->reg_map->qdma.tx_sch_rate + 4); } else { mtk_w32(eth, ring->phys_pdma, MT7628_TX_BASE_PTR0); mtk_w32(eth, ring_size, MT7628_TX_MAX_CNT0); @@ -2957,7 +3073,7 @@ static int mtk_start_dma(struct mtk_eth *eth) if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) val |= MTK_MUTLI_CNT | MTK_RESV_BUF | MTK_WCOMP_EN | MTK_DMAD_WR_WDONE | - MTK_CHK_DDONE_EN; + MTK_CHK_DDONE_EN | MTK_LEAKY_BUCKET_EN; else val |= MTK_RX_BT_32DWORDS; mtk_w32(eth, val, reg_map->qdma.glo_cfg); @@ -3014,6 +3130,45 @@ static bool mtk_uses_dsa(struct net_device *dev) #endif } +static int mtk_device_event(struct notifier_block *n, unsigned long event, void *ptr) +{ + struct mtk_mac *mac = container_of(n, struct mtk_mac, device_notifier); + struct mtk_eth *eth = mac->hw; + struct net_device *dev = netdev_notifier_info_to_dev(ptr); + struct ethtool_link_ksettings s; + struct net_device *ldev; + struct list_head *iter; + struct dsa_port *dp; + + if (event != NETDEV_CHANGE) + return NOTIFY_DONE; + + netdev_for_each_lower_dev(dev, ldev, iter) { + if (netdev_priv(ldev) == mac) + goto found; + } + + return NOTIFY_DONE; + +found: + if (!dsa_slave_dev_check(dev)) + return NOTIFY_DONE; + + if (__ethtool_get_link_ksettings(dev, &s)) + return NOTIFY_DONE; + + if (s.base.speed == 0 || s.base.speed == ((__u32)-1)) + return NOTIFY_DONE; + + dp = dsa_port_from_netdev(dev); + if (dp->index >= MTK_QDMA_NUM_QUEUES) + return NOTIFY_DONE; + + mtk_set_queue_speed(eth, dp->index + 3, s.base.speed); + + return NOTIFY_DONE; +} + static int mtk_open(struct net_device *dev) { struct mtk_mac *mac = netdev_priv(dev); @@ -3078,7 +3233,8 @@ static int mtk_open(struct net_device *dev) refcount_inc(ð->dma_refcnt); phylink_start(mac->phylink); - netif_start_queue(dev); + netif_tx_start_all_queues(dev); + return 0; } @@ -3619,8 +3775,12 @@ static int mtk_unreg_dev(struct mtk_eth *eth) int i; for (i = 0; i < MTK_MAC_COUNT; i++) { + struct mtk_mac *mac; if (!eth->netdev[i]) continue; + mac = netdev_priv(eth->netdev[i]); + if (MTK_HAS_CAPS(eth->soc->caps, MTK_QDMA)) + unregister_netdevice_notifier(&mac->device_notifier); unregister_netdev(eth->netdev[i]); } @@ -3836,6 +3996,23 @@ static int mtk_set_rxnfc(struct net_device *dev, struct ethtool_rxnfc *cmd) return ret; } +static u16 mtk_select_queue(struct net_device *dev, struct sk_buff *skb, + struct net_device *sb_dev) +{ + struct mtk_mac *mac = netdev_priv(dev); + unsigned int queue = 0; + + if (netdev_uses_dsa(dev)) + queue = skb_get_queue_mapping(skb) + 3; + else + queue = mac->id; + + if (queue >= dev->num_tx_queues) + queue = 0; + + return queue; +} + static const struct ethtool_ops mtk_ethtool_ops = { .get_link_ksettings = mtk_get_link_ksettings, .set_link_ksettings = mtk_set_link_ksettings, @@ -3871,6 +4048,7 @@ static const struct net_device_ops mtk_netdev_ops = { .ndo_setup_tc = mtk_eth_setup_tc, .ndo_bpf = mtk_xdp, .ndo_xdp_xmit = mtk_xdp_xmit, + .ndo_select_queue = mtk_select_queue, }; static int mtk_add_mac(struct mtk_eth *eth, struct device_node *np) @@ -3880,6 +4058,7 @@ static int mtk_add_mac(struct mtk_eth *eth, struct device_node *np) struct phylink *phylink; struct mtk_mac *mac; int id, err; + int txqs = 1; if (!_id) { dev_err(eth->dev, "missing mac id\n"); @@ -3897,7 +4076,10 @@ static int mtk_add_mac(struct mtk_eth *eth, struct device_node *np) return -EINVAL; } - eth->netdev[id] = alloc_etherdev(sizeof(*mac)); + if (MTK_HAS_CAPS(eth->soc->caps, MTK_QDMA)) + txqs = MTK_QDMA_NUM_QUEUES; + + eth->netdev[id] = alloc_etherdev_mqs(sizeof(*mac), txqs, 1); if (!eth->netdev[id]) { dev_err(eth->dev, "alloc_etherdev failed\n"); return -ENOMEM; @@ -3994,6 +4176,11 @@ static int mtk_add_mac(struct mtk_eth *eth, struct device_node *np) else eth->netdev[id]->max_mtu = MTK_MAX_RX_LENGTH_2K - MTK_RX_ETH_HLEN; + if (MTK_HAS_CAPS(eth->soc->caps, MTK_QDMA)) { + mac->device_notifier.notifier_call = mtk_device_event; + register_netdevice_notifier(&mac->device_notifier); + } + return 0; free_netdev: diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.h b/drivers/net/ethernet/mediatek/mtk_eth_soc.h index eaaa0c67ef2a..1581eba053ab 100644 --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h @@ -25,6 +25,7 @@ #define MTK_MAX_DSA_PORTS 7 #define MTK_DSA_PORT_MASK GENMASK(2, 0) +#define MTK_QDMA_NUM_QUEUES 16 #define MTK_QDMA_PAGE_SIZE 2048 #define MTK_MAX_RX_LENGTH 1536 #define MTK_MAX_RX_LENGTH_2K 2048 @@ -210,8 +211,26 @@ #define MTK_RING_MAX_AGG_CNT_H ((MTK_HW_LRO_MAX_AGG_CNT >> 6) & 0x3) /* QDMA TX Queue Configuration Registers */ +#define MTK_QTX_OFFSET 0x10 #define QDMA_RES_THRES 4 +/* QDMA Tx Queue Scheduler Configuration Registers */ +#define MTK_QTX_SCH_TX_SEL BIT(31) +#define MTK_QTX_SCH_TX_SEL_V2 GENMASK(31, 30) + +#define MTK_QTX_SCH_LEAKY_BUCKET_EN BIT(30) +#define MTK_QTX_SCH_LEAKY_BUCKET_SIZE GENMASK(29, 28) +#define MTK_QTX_SCH_MIN_RATE_EN BIT(27) +#define MTK_QTX_SCH_MIN_RATE_MAN GENMASK(26, 20) +#define MTK_QTX_SCH_MIN_RATE_EXP GENMASK(19, 16) +#define MTK_QTX_SCH_MAX_RATE_WEIGHT GENMASK(15, 12) +#define MTK_QTX_SCH_MAX_RATE_EN BIT(11) +#define MTK_QTX_SCH_MAX_RATE_MAN GENMASK(10, 4) +#define MTK_QTX_SCH_MAX_RATE_EXP GENMASK(3, 0) + +/* QDMA TX Scheduler Rate Control Register */ +#define MTK_QDMA_TX_SCH_MAX_WFQ BIT(15) + /* QDMA Global Configuration Register */ #define MTK_RX_2B_OFFSET BIT(31) #define MTK_RX_BT_32DWORDS (3 << 11) @@ -230,6 +249,7 @@ #define MTK_WCOMP_EN BIT(24) #define MTK_RESV_BUF (0x40 << 16) #define MTK_MUTLI_CNT (0x4 << 12) +#define MTK_LEAKY_BUCKET_EN BIT(11) /* QDMA Flow Control Register */ #define FC_THRES_DROP_MODE BIT(20) @@ -258,8 +278,6 @@ #define MTK_STAT_OFFSET 0x40 /* QDMA TX NUM */ -#define MTK_QDMA_TX_NUM 16 -#define MTK_QDMA_TX_MASK (MTK_QDMA_TX_NUM - 1) #define QID_BITS_V2(x) (((x) & 0x3f) << 16) #define MTK_QDMA_GMAC2_QID 8 @@ -289,6 +307,7 @@ #define TX_DMA_PLEN0(x) (((x) & eth->soc->txrx.dma_max_len) << eth->soc->txrx.dma_len_offset) #define TX_DMA_PLEN1(x) ((x) & eth->soc->txrx.dma_max_len) #define TX_DMA_SWC BIT(14) +#define TX_DMA_PQID GENMASK(3, 0) /* PDMA on MT7628 */ #define TX_DMA_DONE BIT(31) @@ -947,6 +966,7 @@ struct mtk_reg_map { } pdma; struct { u32 qtx_cfg; /* tx queue configuration */ + u32 qtx_sch; /* tx queue scheduler configuration */ u32 rx_ptr; /* rx base pointer */ u32 rx_cnt_cfg; /* rx max count configuration */ u32 qcrx_ptr; /* rx cpu pointer */ @@ -964,6 +984,7 @@ struct mtk_reg_map { u32 fq_tail; /* fq tail pointer */ u32 fq_count; /* fq free page count */ u32 fq_blen; /* fq free page buffer length */ + u32 tx_sch_rate; /* tx scheduler rate control registers */ } qdma; u32 gdm1_cnt; u32 gdma_to_ppe; @@ -1157,6 +1178,7 @@ struct mtk_mac { __be32 hwlro_ip[MTK_MAX_LRO_IP_CNT]; int hwlro_ip_cnt; unsigned int syscfg0; + struct notifier_block device_notifier; }; /* the struct describing the SoC. these are declared in the soc_xyz.c files */ From d169ecb536e46985e3e2acf5f2b39efae3953c2d Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Wed, 16 Nov 2022 09:07:33 +0100 Subject: [PATCH 1083/2002] net: dsa: tag_mtk: assign per-port queues Keeps traffic sent to the switch within link speed limits Signed-off-by: Felix Fietkau Reviewed-by: Florian Fainelli Link: https://lore.kernel.org/r/20221116080734.44013-6-nbd@nbd.name Signed-off-by: Jakub Kicinski --- net/dsa/tag_mtk.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/net/dsa/tag_mtk.c b/net/dsa/tag_mtk.c index ba37495ab5f4..8948c4f99f8e 100644 --- a/net/dsa/tag_mtk.c +++ b/net/dsa/tag_mtk.c @@ -27,6 +27,8 @@ static struct sk_buff *mtk_tag_xmit(struct sk_buff *skb, u8 xmit_tpid; u8 *mtk_tag; + skb_set_queue_mapping(skb, dp->index); + /* Build the special tag after the MAC Source Address. If VLAN header * is present, it's required that VLAN header and special tag is * being combined. Only in this way we can allow the switch can parse From 8bd8dcc5e47f0f9dc40187c3b8b42d992181eee1 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Wed, 16 Nov 2022 09:07:34 +0100 Subject: [PATCH 1084/2002] net: ethernet: mediatek: ppe: assign per-port queues for offloaded traffic Keeps traffic sent to the switch within link speed limits Signed-off-by: Felix Fietkau Link: https://lore.kernel.org/r/20221116080734.44013-7-nbd@nbd.name Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/mediatek/mtk_ppe.c | 18 ++++++++++++++++++ drivers/net/ethernet/mediatek/mtk_ppe.h | 4 ++++ .../net/ethernet/mediatek/mtk_ppe_offload.c | 12 +++++++++--- 3 files changed, 31 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/mediatek/mtk_ppe.c b/drivers/net/ethernet/mediatek/mtk_ppe.c index 3ee2bf53f9e5..96ad0a9b79b4 100644 --- a/drivers/net/ethernet/mediatek/mtk_ppe.c +++ b/drivers/net/ethernet/mediatek/mtk_ppe.c @@ -399,6 +399,24 @@ int mtk_foe_entry_set_wdma(struct mtk_eth *eth, struct mtk_foe_entry *entry, return 0; } +int mtk_foe_entry_set_queue(struct mtk_eth *eth, struct mtk_foe_entry *entry, + unsigned int queue) +{ + u32 *ib2 = mtk_foe_entry_ib2(eth, entry); + + if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) { + *ib2 &= ~MTK_FOE_IB2_QID_V2; + *ib2 |= FIELD_PREP(MTK_FOE_IB2_QID_V2, queue); + *ib2 |= MTK_FOE_IB2_PSE_QOS_V2; + } else { + *ib2 &= ~MTK_FOE_IB2_QID; + *ib2 |= FIELD_PREP(MTK_FOE_IB2_QID, queue); + *ib2 |= MTK_FOE_IB2_PSE_QOS; + } + + return 0; +} + static bool mtk_flow_entry_match(struct mtk_eth *eth, struct mtk_flow_entry *entry, struct mtk_foe_entry *data) diff --git a/drivers/net/ethernet/mediatek/mtk_ppe.h b/drivers/net/ethernet/mediatek/mtk_ppe.h index 0b7a67a958e4..be635864bb96 100644 --- a/drivers/net/ethernet/mediatek/mtk_ppe.h +++ b/drivers/net/ethernet/mediatek/mtk_ppe.h @@ -68,7 +68,9 @@ enum { #define MTK_FOE_IB2_DSCP GENMASK(31, 24) /* CONFIG_MEDIATEK_NETSYS_V2 */ +#define MTK_FOE_IB2_QID_V2 GENMASK(6, 0) #define MTK_FOE_IB2_PORT_MG_V2 BIT(7) +#define MTK_FOE_IB2_PSE_QOS_V2 BIT(8) #define MTK_FOE_IB2_DEST_PORT_V2 GENMASK(12, 9) #define MTK_FOE_IB2_MULTICAST_V2 BIT(13) #define MTK_FOE_IB2_WDMA_WINFO_V2 BIT(19) @@ -350,6 +352,8 @@ int mtk_foe_entry_set_pppoe(struct mtk_eth *eth, struct mtk_foe_entry *entry, int sid); int mtk_foe_entry_set_wdma(struct mtk_eth *eth, struct mtk_foe_entry *entry, int wdma_idx, int txq, int bss, int wcid); +int mtk_foe_entry_set_queue(struct mtk_eth *eth, struct mtk_foe_entry *entry, + unsigned int queue); int mtk_foe_entry_commit(struct mtk_ppe *ppe, struct mtk_flow_entry *entry); void mtk_foe_entry_clear(struct mtk_ppe *ppe, struct mtk_flow_entry *entry); int mtk_foe_entry_idle_time(struct mtk_ppe *ppe, struct mtk_flow_entry *entry); diff --git a/drivers/net/ethernet/mediatek/mtk_ppe_offload.c b/drivers/net/ethernet/mediatek/mtk_ppe_offload.c index 28bbd1df3e30..81afd5ee3fbf 100644 --- a/drivers/net/ethernet/mediatek/mtk_ppe_offload.c +++ b/drivers/net/ethernet/mediatek/mtk_ppe_offload.c @@ -188,7 +188,7 @@ mtk_flow_set_output_device(struct mtk_eth *eth, struct mtk_foe_entry *foe, int *wed_index) { struct mtk_wdma_info info = {}; - int pse_port, dsa_port; + int pse_port, dsa_port, queue; if (mtk_flow_get_wdma_info(dev, dest_mac, &info) == 0) { mtk_foe_entry_set_wdma(eth, foe, info.wdma_idx, info.queue, @@ -212,8 +212,6 @@ mtk_flow_set_output_device(struct mtk_eth *eth, struct mtk_foe_entry *foe, } dsa_port = mtk_flow_get_dsa_port(&dev); - if (dsa_port >= 0) - mtk_foe_entry_set_dsa(eth, foe, dsa_port); if (dev == eth->netdev[0]) pse_port = 1; @@ -222,6 +220,14 @@ mtk_flow_set_output_device(struct mtk_eth *eth, struct mtk_foe_entry *foe, else return -EOPNOTSUPP; + if (dsa_port >= 0) { + mtk_foe_entry_set_dsa(eth, foe, dsa_port); + queue = 3 + dsa_port; + } else { + queue = pse_port - 1; + } + mtk_foe_entry_set_queue(eth, foe, queue); + out: mtk_foe_entry_set_pse_port(eth, foe, pse_port); From ef66c5475d7fb864c2418d3bdd19dee46324624b Mon Sep 17 00:00:00 2001 From: David Vernet Date: Sat, 19 Nov 2022 23:10:01 -0600 Subject: [PATCH 1085/2002] bpf: Allow multiple modifiers in reg_type_str() prefix reg_type_str() in the verifier currently only allows a single register type modifier to be present in the 'prefix' string which is eventually stored in the env type_str_buf. This currently works fine because there are no overlapping type modifiers, but once PTR_TRUSTED is added, that will no longer be the case. This patch updates reg_type_str() to support having multiple modifiers in the prefix string, and updates the size of type_str_buf to be 128 bytes. Signed-off-by: David Vernet Link: https://lore.kernel.org/r/20221120051004.3605026-2-void@manifault.com Signed-off-by: Alexei Starovoitov --- include/linux/bpf_verifier.h | 2 +- kernel/bpf/verifier.c | 19 ++++++++----------- 2 files changed, 9 insertions(+), 12 deletions(-) diff --git a/include/linux/bpf_verifier.h b/include/linux/bpf_verifier.h index 23f30c685f28..608dde740fef 100644 --- a/include/linux/bpf_verifier.h +++ b/include/linux/bpf_verifier.h @@ -19,7 +19,7 @@ */ #define BPF_MAX_VAR_SIZ (1 << 29) /* size of type_str_buf in bpf_verifier. */ -#define TYPE_STR_BUF_LEN 64 +#define TYPE_STR_BUF_LEN 128 /* Liveness marks, used for registers and spilled-regs (in stack slots). * Read marks propagate upwards until they find a write mark; they record that diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 195d24316750..67a6f11d953c 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -557,7 +557,7 @@ static bool is_cmpxchg_insn(const struct bpf_insn *insn) static const char *reg_type_str(struct bpf_verifier_env *env, enum bpf_reg_type type) { - char postfix[16] = {0}, prefix[32] = {0}; + char postfix[16] = {0}, prefix[64] = {0}; static const char * const str[] = { [NOT_INIT] = "?", [SCALAR_VALUE] = "scalar", @@ -589,16 +589,13 @@ static const char *reg_type_str(struct bpf_verifier_env *env, strncpy(postfix, "_or_null", 16); } - if (type & MEM_RDONLY) - strncpy(prefix, "rdonly_", 32); - if (type & MEM_RINGBUF) - strncpy(prefix, "ringbuf_", 32); - if (type & MEM_USER) - strncpy(prefix, "user_", 32); - if (type & MEM_PERCPU) - strncpy(prefix, "percpu_", 32); - if (type & PTR_UNTRUSTED) - strncpy(prefix, "untrusted_", 32); + snprintf(prefix, sizeof(prefix), "%s%s%s%s%s", + type & MEM_RDONLY ? "rdonly_" : "", + type & MEM_RINGBUF ? "ringbuf_" : "", + type & MEM_USER ? "user_" : "", + type & MEM_PERCPU ? "percpu_" : "", + type & PTR_UNTRUSTED ? "untrusted_" : "" + ); snprintf(env->type_str_buf, TYPE_STR_BUF_LEN, "%s%s%s", prefix, str[base_type(type)], postfix); From 3f00c52393445ed49aadc1a567aa502c6333b1a1 Mon Sep 17 00:00:00 2001 From: David Vernet Date: Sat, 19 Nov 2022 23:10:02 -0600 Subject: [PATCH 1086/2002] bpf: Allow trusted pointers to be passed to KF_TRUSTED_ARGS kfuncs Kfuncs currently support specifying the KF_TRUSTED_ARGS flag to signal to the verifier that it should enforce that a BPF program passes it a "safe", trusted pointer. Currently, "safe" means that the pointer is either PTR_TO_CTX, or is refcounted. There may be cases, however, where the kernel passes a BPF program a safe / trusted pointer to an object that the BPF program wishes to use as a kptr, but because the object does not yet have a ref_obj_id from the perspective of the verifier, the program would be unable to pass it to a KF_ACQUIRE | KF_TRUSTED_ARGS kfunc. The solution is to expand the set of pointers that are considered trusted according to KF_TRUSTED_ARGS, so that programs can invoke kfuncs with these pointers without getting rejected by the verifier. There is already a PTR_UNTRUSTED flag that is set in some scenarios, such as when a BPF program reads a kptr directly from a map without performing a bpf_kptr_xchg() call. These pointers of course can and should be rejected by the verifier. Unfortunately, however, PTR_UNTRUSTED does not cover all the cases for safety that need to be addressed to adequately protect kfuncs. Specifically, pointers obtained by a BPF program "walking" a struct are _not_ considered PTR_UNTRUSTED according to BPF. For example, say that we were to add a kfunc called bpf_task_acquire(), with KF_ACQUIRE | KF_TRUSTED_ARGS, to acquire a struct task_struct *. If we only used PTR_UNTRUSTED to signal that a task was unsafe to pass to a kfunc, the verifier would mistakenly allow the following unsafe BPF program to be loaded: SEC("tp_btf/task_newtask") int BPF_PROG(unsafe_acquire_task, struct task_struct *task, u64 clone_flags) { struct task_struct *acquired, *nested; nested = task->last_wakee; /* Would not be rejected by the verifier. */ acquired = bpf_task_acquire(nested); if (!acquired) return 0; bpf_task_release(acquired); return 0; } To address this, this patch defines a new type flag called PTR_TRUSTED which tracks whether a PTR_TO_BTF_ID pointer is safe to pass to a KF_TRUSTED_ARGS kfunc or a BPF helper function. PTR_TRUSTED pointers are passed directly from the kernel as a tracepoint or struct_ops callback argument. Any nested pointer that is obtained from walking a PTR_TRUSTED pointer is no longer PTR_TRUSTED. From the example above, the struct task_struct *task argument is PTR_TRUSTED, but the 'nested' pointer obtained from 'task->last_wakee' is not PTR_TRUSTED. A subsequent patch will add kfuncs for storing a task kfunc as a kptr, and then another patch will add selftests to validate. Signed-off-by: David Vernet Link: https://lore.kernel.org/r/20221120051004.3605026-3-void@manifault.com Signed-off-by: Alexei Starovoitov --- Documentation/bpf/kfuncs.rst | 24 +++---- include/linux/bpf.h | 30 ++++++++ include/linux/bpf_verifier.h | 7 ++ include/linux/btf.h | 65 ++++++++++------- kernel/bpf/btf.c | 8 +++ kernel/bpf/verifier.c | 69 +++++++++++++++---- kernel/trace/bpf_trace.c | 2 +- net/ipv4/bpf_tcp_ca.c | 4 +- tools/testing/selftests/bpf/verifier/calls.c | 2 +- .../selftests/bpf/verifier/ref_tracking.c | 4 +- 10 files changed, 161 insertions(+), 54 deletions(-) diff --git a/Documentation/bpf/kfuncs.rst b/Documentation/bpf/kfuncs.rst index 3b1501c3b6cd..90774479ab7a 100644 --- a/Documentation/bpf/kfuncs.rst +++ b/Documentation/bpf/kfuncs.rst @@ -161,22 +161,20 @@ KF_ACQUIRE and KF_RET_NULL flags. -------------------------- The KF_TRUSTED_ARGS flag is used for kfuncs taking pointer arguments. It -indicates that the all pointer arguments will always have a guaranteed lifetime, -and pointers to kernel objects are always passed to helpers in their unmodified -form (as obtained from acquire kfuncs). +indicates that the all pointer arguments are valid, and that all pointers to +BTF objects have been passed in their unmodified form (that is, at a zero +offset, and without having been obtained from walking another pointer). -It can be used to enforce that a pointer to a refcounted object acquired from a -kfunc or BPF helper is passed as an argument to this kfunc without any -modifications (e.g. pointer arithmetic) such that it is trusted and points to -the original object. +There are two types of pointers to kernel objects which are considered "valid": -Meanwhile, it is also allowed pass pointers to normal memory to such kfuncs, -but those can have a non-zero offset. +1. Pointers which are passed as tracepoint or struct_ops callback arguments. +2. Pointers which were returned from a KF_ACQUIRE or KF_KPTR_GET kfunc. -This flag is often used for kfuncs that operate (change some property, perform -some operation) on an object that was obtained using an acquire kfunc. Such -kfuncs need an unchanged pointer to ensure the integrity of the operation being -performed on the expected object. +Pointers to non-BTF objects (e.g. scalar pointers) may also be passed to +KF_TRUSTED_ARGS kfuncs, and may have a non-zero offset. + +The definition of "valid" pointers is subject to change at any time, and has +absolutely no ABI stability guarantees. 2.4.6 KF_SLEEPABLE flag ----------------------- diff --git a/include/linux/bpf.h b/include/linux/bpf.h index 8b32376ce746..c9eafa67f2a2 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -543,6 +543,35 @@ enum bpf_type_flag { */ MEM_ALLOC = BIT(11 + BPF_BASE_TYPE_BITS), + /* PTR was passed from the kernel in a trusted context, and may be + * passed to KF_TRUSTED_ARGS kfuncs or BPF helper functions. + * Confusingly, this is _not_ the opposite of PTR_UNTRUSTED above. + * PTR_UNTRUSTED refers to a kptr that was read directly from a map + * without invoking bpf_kptr_xchg(). What we really need to know is + * whether a pointer is safe to pass to a kfunc or BPF helper function. + * While PTR_UNTRUSTED pointers are unsafe to pass to kfuncs and BPF + * helpers, they do not cover all possible instances of unsafe + * pointers. For example, a pointer that was obtained from walking a + * struct will _not_ get the PTR_UNTRUSTED type modifier, despite the + * fact that it may be NULL, invalid, etc. This is due to backwards + * compatibility requirements, as this was the behavior that was first + * introduced when kptrs were added. The behavior is now considered + * deprecated, and PTR_UNTRUSTED will eventually be removed. + * + * PTR_TRUSTED, on the other hand, is a pointer that the kernel + * guarantees to be valid and safe to pass to kfuncs and BPF helpers. + * For example, pointers passed to tracepoint arguments are considered + * PTR_TRUSTED, as are pointers that are passed to struct_ops + * callbacks. As alluded to above, pointers that are obtained from + * walking PTR_TRUSTED pointers are _not_ trusted. For example, if a + * struct task_struct *task is PTR_TRUSTED, then accessing + * task->last_wakee will lose the PTR_TRUSTED modifier when it's stored + * in a BPF register. Similarly, pointers passed to certain programs + * types such as kretprobes are not guaranteed to be valid, as they may + * for example contain an object that was recently freed. + */ + PTR_TRUSTED = BIT(12 + BPF_BASE_TYPE_BITS), + __BPF_TYPE_FLAG_MAX, __BPF_TYPE_LAST_FLAG = __BPF_TYPE_FLAG_MAX - 1, }; @@ -636,6 +665,7 @@ enum bpf_return_type { RET_PTR_TO_RINGBUF_MEM_OR_NULL = PTR_MAYBE_NULL | MEM_RINGBUF | RET_PTR_TO_MEM, RET_PTR_TO_DYNPTR_MEM_OR_NULL = PTR_MAYBE_NULL | RET_PTR_TO_MEM, RET_PTR_TO_BTF_ID_OR_NULL = PTR_MAYBE_NULL | RET_PTR_TO_BTF_ID, + RET_PTR_TO_BTF_ID_TRUSTED = PTR_TRUSTED | RET_PTR_TO_BTF_ID, /* This must be the last entry. Its purpose is to ensure the enum is * wide enough to hold the higher bits reserved for bpf_type_flag. diff --git a/include/linux/bpf_verifier.h b/include/linux/bpf_verifier.h index 608dde740fef..545152ac136c 100644 --- a/include/linux/bpf_verifier.h +++ b/include/linux/bpf_verifier.h @@ -680,4 +680,11 @@ static inline bool bpf_prog_check_recur(const struct bpf_prog *prog) } } +#define BPF_REG_TRUSTED_MODIFIERS (MEM_ALLOC | PTR_TRUSTED) + +static inline bool bpf_type_has_unsafe_modifiers(u32 type) +{ + return type_flag(type) & ~BPF_REG_TRUSTED_MODIFIERS; +} + #endif /* _LINUX_BPF_VERIFIER_H */ diff --git a/include/linux/btf.h b/include/linux/btf.h index d5b26380a60f..d38aa4251c28 100644 --- a/include/linux/btf.h +++ b/include/linux/btf.h @@ -19,36 +19,53 @@ #define KF_RELEASE (1 << 1) /* kfunc is a release function */ #define KF_RET_NULL (1 << 2) /* kfunc returns a pointer that may be NULL */ #define KF_KPTR_GET (1 << 3) /* kfunc returns reference to a kptr */ -/* Trusted arguments are those which are meant to be referenced arguments with - * unchanged offset. It is used to enforce that pointers obtained from acquire - * kfuncs remain unmodified when being passed to helpers taking trusted args. +/* Trusted arguments are those which are guaranteed to be valid when passed to + * the kfunc. It is used to enforce that pointers obtained from either acquire + * kfuncs, or from the main kernel on a tracepoint or struct_ops callback + * invocation, remain unmodified when being passed to helpers taking trusted + * args. * - * Consider - * struct foo { - * int data; - * struct foo *next; - * }; + * Consider, for example, the following new task tracepoint: * - * struct bar { - * int data; - * struct foo f; - * }; + * SEC("tp_btf/task_newtask") + * int BPF_PROG(new_task_tp, struct task_struct *task, u64 clone_flags) + * { + * ... + * } * - * struct foo *f = alloc_foo(); // Acquire kfunc - * struct bar *b = alloc_bar(); // Acquire kfunc + * And the following kfunc: * - * If a kfunc set_foo_data() wants to operate only on the allocated object, it - * will set the KF_TRUSTED_ARGS flag, which will prevent unsafe usage like: + * BTF_ID_FLAGS(func, bpf_task_acquire, KF_ACQUIRE | KF_TRUSTED_ARGS) * - * set_foo_data(f, 42); // Allowed - * set_foo_data(f->next, 42); // Rejected, non-referenced pointer - * set_foo_data(&f->next, 42);// Rejected, referenced, but wrong type - * set_foo_data(&b->f, 42); // Rejected, referenced, but bad offset + * All invocations to the kfunc must pass the unmodified, unwalked task: * - * In the final case, usually for the purposes of type matching, it is deduced - * by looking at the type of the member at the offset, but due to the - * requirement of trusted argument, this deduction will be strict and not done - * for this case. + * bpf_task_acquire(task); // Allowed + * bpf_task_acquire(task->last_wakee); // Rejected, walked task + * + * Programs may also pass referenced tasks directly to the kfunc: + * + * struct task_struct *acquired; + * + * acquired = bpf_task_acquire(task); // Allowed, same as above + * bpf_task_acquire(acquired); // Allowed + * bpf_task_acquire(task); // Allowed + * bpf_task_acquire(acquired->last_wakee); // Rejected, walked task + * + * Programs may _not_, however, pass a task from an arbitrary fentry/fexit, or + * kprobe/kretprobe to the kfunc, as BPF cannot guarantee that all of these + * pointers are guaranteed to be safe. For example, the following BPF program + * would be rejected: + * + * SEC("kretprobe/free_task") + * int BPF_PROG(free_task_probe, struct task_struct *tsk) + * { + * struct task_struct *acquired; + * + * acquired = bpf_task_acquire(acquired); // Rejected, not a trusted pointer + * bpf_task_release(acquired); + * + * return 0; + * } */ #define KF_TRUSTED_ARGS (1 << 4) /* kfunc only takes trusted pointer arguments */ #define KF_SLEEPABLE (1 << 5) /* kfunc may sleep */ diff --git a/kernel/bpf/btf.c b/kernel/bpf/btf.c index f7d5fab61535..d52054ec69c9 100644 --- a/kernel/bpf/btf.c +++ b/kernel/bpf/btf.c @@ -5799,6 +5799,11 @@ static u32 get_ctx_arg_idx(struct btf *btf, const struct btf_type *func_proto, return nr_args + 1; } +static bool prog_type_args_trusted(enum bpf_prog_type prog_type) +{ + return prog_type == BPF_PROG_TYPE_TRACING || prog_type == BPF_PROG_TYPE_STRUCT_OPS; +} + bool btf_ctx_access(int off, int size, enum bpf_access_type type, const struct bpf_prog *prog, struct bpf_insn_access_aux *info) @@ -5942,6 +5947,9 @@ bool btf_ctx_access(int off, int size, enum bpf_access_type type, } info->reg_type = PTR_TO_BTF_ID; + if (prog_type_args_trusted(prog->type)) + info->reg_type |= PTR_TRUSTED; + if (tgt_prog) { enum bpf_prog_type tgt_type; diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 67a6f11d953c..5bc9d84d7924 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -589,12 +589,13 @@ static const char *reg_type_str(struct bpf_verifier_env *env, strncpy(postfix, "_or_null", 16); } - snprintf(prefix, sizeof(prefix), "%s%s%s%s%s", + snprintf(prefix, sizeof(prefix), "%s%s%s%s%s%s", type & MEM_RDONLY ? "rdonly_" : "", type & MEM_RINGBUF ? "ringbuf_" : "", type & MEM_USER ? "user_" : "", type & MEM_PERCPU ? "percpu_" : "", - type & PTR_UNTRUSTED ? "untrusted_" : "" + type & PTR_UNTRUSTED ? "untrusted_" : "", + type & PTR_TRUSTED ? "trusted_" : "" ); snprintf(env->type_str_buf, TYPE_STR_BUF_LEN, "%s%s%s", @@ -3856,7 +3857,7 @@ static int map_kptr_match_type(struct bpf_verifier_env *env, struct bpf_reg_state *reg, u32 regno) { const char *targ_name = kernel_type_name(kptr_field->kptr.btf, kptr_field->kptr.btf_id); - int perm_flags = PTR_MAYBE_NULL; + int perm_flags = PTR_MAYBE_NULL | PTR_TRUSTED; const char *reg_name = ""; /* Only unreferenced case accepts untrusted pointers */ @@ -4732,6 +4733,9 @@ static int check_ptr_to_btf_access(struct bpf_verifier_env *env, if (type_flag(reg->type) & PTR_UNTRUSTED) flag |= PTR_UNTRUSTED; + /* Any pointer obtained from walking a trusted pointer is no longer trusted. */ + flag &= ~PTR_TRUSTED; + if (atype == BPF_READ && value_regno >= 0) mark_btf_ld_reg(env, regs, value_regno, ret, reg->btf, btf_id, flag); @@ -5844,6 +5848,7 @@ static const struct bpf_reg_types btf_id_sock_common_types = { PTR_TO_TCP_SOCK, PTR_TO_XDP_SOCK, PTR_TO_BTF_ID, + PTR_TO_BTF_ID | PTR_TRUSTED, }, .btf_id = &btf_sock_ids[BTF_SOCK_TYPE_SOCK_COMMON], }; @@ -5884,8 +5889,18 @@ static const struct bpf_reg_types scalar_types = { .types = { SCALAR_VALUE } }; static const struct bpf_reg_types context_types = { .types = { PTR_TO_CTX } }; static const struct bpf_reg_types ringbuf_mem_types = { .types = { PTR_TO_MEM | MEM_RINGBUF } }; static const struct bpf_reg_types const_map_ptr_types = { .types = { CONST_PTR_TO_MAP } }; -static const struct bpf_reg_types btf_ptr_types = { .types = { PTR_TO_BTF_ID } }; -static const struct bpf_reg_types percpu_btf_ptr_types = { .types = { PTR_TO_BTF_ID | MEM_PERCPU } }; +static const struct bpf_reg_types btf_ptr_types = { + .types = { + PTR_TO_BTF_ID, + PTR_TO_BTF_ID | PTR_TRUSTED, + }, +}; +static const struct bpf_reg_types percpu_btf_ptr_types = { + .types = { + PTR_TO_BTF_ID | MEM_PERCPU, + PTR_TO_BTF_ID | MEM_PERCPU | PTR_TRUSTED, + } +}; static const struct bpf_reg_types func_ptr_types = { .types = { PTR_TO_FUNC } }; static const struct bpf_reg_types stack_ptr_types = { .types = { PTR_TO_STACK } }; static const struct bpf_reg_types const_str_ptr_types = { .types = { PTR_TO_MAP_VALUE } }; @@ -5973,7 +5988,7 @@ static int check_reg_type(struct bpf_verifier_env *env, u32 regno, return -EACCES; found: - if (reg->type == PTR_TO_BTF_ID) { + if (reg->type == PTR_TO_BTF_ID || reg->type & PTR_TRUSTED) { /* For bpf_sk_release, it needs to match against first member * 'struct sock_common', hence make an exception for it. This * allows bpf_sk_release to work for multiple socket types. @@ -6055,6 +6070,8 @@ int check_func_arg_reg_off(struct bpf_verifier_env *env, */ case PTR_TO_BTF_ID: case PTR_TO_BTF_ID | MEM_ALLOC: + case PTR_TO_BTF_ID | PTR_TRUSTED: + case PTR_TO_BTF_ID | MEM_ALLOC | PTR_TRUSTED: /* When referenced PTR_TO_BTF_ID is passed to release function, * it's fixed offset must be 0. In the other cases, fixed offset * can be non-zero. @@ -7939,6 +7956,25 @@ static bool is_kfunc_arg_kptr_get(struct bpf_kfunc_call_arg_meta *meta, int arg) return arg == 0 && (meta->kfunc_flags & KF_KPTR_GET); } +static bool is_trusted_reg(const struct bpf_reg_state *reg) +{ + /* A referenced register is always trusted. */ + if (reg->ref_obj_id) + return true; + + /* If a register is not referenced, it is trusted if it has either the + * MEM_ALLOC or PTR_TRUSTED type modifiers, and no others. Some of the + * other type modifiers may be safe, but we elect to take an opt-in + * approach here as some (e.g. PTR_UNTRUSTED and PTR_MAYBE_NULL) are + * not. + * + * Eventually, we should make PTR_TRUSTED the single source of truth + * for whether a register is trusted. + */ + return type_flag(reg->type) & BPF_REG_TRUSTED_MODIFIERS && + !bpf_type_has_unsafe_modifiers(reg->type); +} + static bool __kfunc_param_match_suffix(const struct btf *btf, const struct btf_param *arg, const char *suffix) @@ -8220,7 +8256,7 @@ static int process_kf_arg_ptr_to_btf_id(struct bpf_verifier_env *env, const char *reg_ref_tname; u32 reg_ref_id; - if (reg->type == PTR_TO_BTF_ID) { + if (base_type(reg->type) == PTR_TO_BTF_ID) { reg_btf = reg->btf; reg_ref_id = reg->btf_id; } else { @@ -8366,6 +8402,7 @@ static int check_reg_allocation_locked(struct bpf_verifier_env *env, struct bpf_ ptr = reg->map_ptr; break; case PTR_TO_BTF_ID | MEM_ALLOC: + case PTR_TO_BTF_ID | MEM_ALLOC | PTR_TRUSTED: ptr = reg->btf; break; default: @@ -8596,8 +8633,9 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_kfunc_call_ case KF_ARG_PTR_TO_BTF_ID: if (!is_kfunc_trusted_args(meta)) break; - if (!reg->ref_obj_id) { - verbose(env, "R%d must be referenced\n", regno); + + if (!is_trusted_reg(reg)) { + verbose(env, "R%d must be referenced or trusted\n", regno); return -EINVAL; } fallthrough; @@ -8702,9 +8740,13 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_kfunc_call_ break; case KF_ARG_PTR_TO_BTF_ID: /* Only base_type is checked, further checks are done here */ - if (reg->type != PTR_TO_BTF_ID && - (!reg2btf_ids[base_type(reg->type)] || type_flag(reg->type))) { - verbose(env, "arg#%d expected pointer to btf or socket\n", i); + if ((base_type(reg->type) != PTR_TO_BTF_ID || + bpf_type_has_unsafe_modifiers(reg->type)) && + !reg2btf_ids[base_type(reg->type)]) { + verbose(env, "arg#%d is %s ", i, reg_type_str(env, reg->type)); + verbose(env, "expected %s or socket\n", + reg_type_str(env, base_type(reg->type) | + (type_flag(reg->type) & BPF_REG_TRUSTED_MODIFIERS))); return -EINVAL; } ret = process_kf_arg_ptr_to_btf_id(env, reg, ref_t, ref_tname, ref_id, meta, i); @@ -14713,6 +14755,7 @@ static int convert_ctx_accesses(struct bpf_verifier_env *env) break; case PTR_TO_BTF_ID: case PTR_TO_BTF_ID | PTR_UNTRUSTED: + case PTR_TO_BTF_ID | PTR_TRUSTED: /* PTR_TO_BTF_ID | MEM_ALLOC always has a valid lifetime, unlike * PTR_TO_BTF_ID, and an active ref_obj_id, but the same cannot * be said once it is marked PTR_UNTRUSTED, hence we must handle @@ -14720,6 +14763,8 @@ static int convert_ctx_accesses(struct bpf_verifier_env *env) * for this case. */ case PTR_TO_BTF_ID | MEM_ALLOC | PTR_UNTRUSTED: + case PTR_TO_BTF_ID | PTR_UNTRUSTED | PTR_TRUSTED: + case PTR_TO_BTF_ID | PTR_UNTRUSTED | MEM_ALLOC | PTR_TRUSTED: if (type == BPF_READ) { insn->code = BPF_LDX | BPF_PROBE_MEM | BPF_SIZE((insn)->code); diff --git a/kernel/trace/bpf_trace.c b/kernel/trace/bpf_trace.c index f2d8d070d024..5b9008bc597b 100644 --- a/kernel/trace/bpf_trace.c +++ b/kernel/trace/bpf_trace.c @@ -774,7 +774,7 @@ BPF_CALL_0(bpf_get_current_task_btf) const struct bpf_func_proto bpf_get_current_task_btf_proto = { .func = bpf_get_current_task_btf, .gpl_only = true, - .ret_type = RET_PTR_TO_BTF_ID, + .ret_type = RET_PTR_TO_BTF_ID_TRUSTED, .ret_btf_id = &btf_tracing_ids[BTF_TRACING_TYPE_TASK], }; diff --git a/net/ipv4/bpf_tcp_ca.c b/net/ipv4/bpf_tcp_ca.c index d15c91de995f..4517d2bd186a 100644 --- a/net/ipv4/bpf_tcp_ca.c +++ b/net/ipv4/bpf_tcp_ca.c @@ -61,7 +61,9 @@ static bool bpf_tcp_ca_is_valid_access(int off, int size, if (!bpf_tracing_btf_ctx_access(off, size, type, prog, info)) return false; - if (info->reg_type == PTR_TO_BTF_ID && info->btf_id == sock_id) + if (base_type(info->reg_type) == PTR_TO_BTF_ID && + !bpf_type_has_unsafe_modifiers(info->reg_type) && + info->btf_id == sock_id) /* promote it to tcp_sock */ info->btf_id = tcp_sock_id; diff --git a/tools/testing/selftests/bpf/verifier/calls.c b/tools/testing/selftests/bpf/verifier/calls.c index 86d6fef2e3b4..3193915c5ee6 100644 --- a/tools/testing/selftests/bpf/verifier/calls.c +++ b/tools/testing/selftests/bpf/verifier/calls.c @@ -109,7 +109,7 @@ }, .prog_type = BPF_PROG_TYPE_SCHED_CLS, .result = REJECT, - .errstr = "arg#0 expected pointer to btf or socket", + .errstr = "arg#0 is ptr_or_null_ expected ptr_ or socket", .fixup_kfunc_btf_id = { { "bpf_kfunc_call_test_acquire", 3 }, { "bpf_kfunc_call_test_release", 5 }, diff --git a/tools/testing/selftests/bpf/verifier/ref_tracking.c b/tools/testing/selftests/bpf/verifier/ref_tracking.c index 55cba01c99d5..9540164712b7 100644 --- a/tools/testing/selftests/bpf/verifier/ref_tracking.c +++ b/tools/testing/selftests/bpf/verifier/ref_tracking.c @@ -142,7 +142,7 @@ .kfunc = "bpf", .expected_attach_type = BPF_LSM_MAC, .flags = BPF_F_SLEEPABLE, - .errstr = "arg#0 expected pointer to btf or socket", + .errstr = "arg#0 is ptr_or_null_ expected ptr_ or socket", .fixup_kfunc_btf_id = { { "bpf_lookup_user_key", 2 }, { "bpf_key_put", 4 }, @@ -163,7 +163,7 @@ .kfunc = "bpf", .expected_attach_type = BPF_LSM_MAC, .flags = BPF_F_SLEEPABLE, - .errstr = "arg#0 expected pointer to btf or socket", + .errstr = "arg#0 is ptr_or_null_ expected ptr_ or socket", .fixup_kfunc_btf_id = { { "bpf_lookup_system_key", 1 }, { "bpf_key_put", 3 }, From 90660309b0c76c564a31a21f3a81d6641a9acaa0 Mon Sep 17 00:00:00 2001 From: David Vernet Date: Sat, 19 Nov 2022 23:10:03 -0600 Subject: [PATCH 1087/2002] bpf: Add kfuncs for storing struct task_struct * as a kptr Now that BPF supports adding new kernel functions with kfuncs, and storing kernel objects in maps with kptrs, we can add a set of kfuncs which allow struct task_struct objects to be stored in maps as referenced kptrs. The possible use cases for doing this are plentiful. During tracing, for example, it would be useful to be able to collect some tasks that performed a certain operation, and then periodically summarize who they are, which cgroup they're in, how much CPU time they've utilized, etc. In order to enable this, this patch adds three new kfuncs: struct task_struct *bpf_task_acquire(struct task_struct *p); struct task_struct *bpf_task_kptr_get(struct task_struct **pp); void bpf_task_release(struct task_struct *p); A follow-on patch will add selftests validating these kfuncs. Signed-off-by: David Vernet Link: https://lore.kernel.org/r/20221120051004.3605026-4-void@manifault.com Signed-off-by: Alexei Starovoitov --- kernel/bpf/helpers.c | 78 ++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 75 insertions(+), 3 deletions(-) diff --git a/kernel/bpf/helpers.c b/kernel/bpf/helpers.c index 212e791d7452..89a95f3d854c 100644 --- a/kernel/bpf/helpers.c +++ b/kernel/bpf/helpers.c @@ -1824,6 +1824,63 @@ struct bpf_list_node *bpf_list_pop_back(struct bpf_list_head *head) return __bpf_list_del(head, true); } +/** + * bpf_task_acquire - Acquire a reference to a task. A task acquired by this + * kfunc which is not stored in a map as a kptr, must be released by calling + * bpf_task_release(). + * @p: The task on which a reference is being acquired. + */ +struct task_struct *bpf_task_acquire(struct task_struct *p) +{ + refcount_inc(&p->rcu_users); + return p; +} + +/** + * bpf_task_kptr_get - Acquire a reference on a struct task_struct kptr. A task + * kptr acquired by this kfunc which is not subsequently stored in a map, must + * be released by calling bpf_task_release(). + * @pp: A pointer to a task kptr on which a reference is being acquired. + */ +struct task_struct *bpf_task_kptr_get(struct task_struct **pp) +{ + struct task_struct *p; + + rcu_read_lock(); + p = READ_ONCE(*pp); + + /* Another context could remove the task from the map and release it at + * any time, including after we've done the lookup above. This is safe + * because we're in an RCU read region, so the task is guaranteed to + * remain valid until at least the rcu_read_unlock() below. + */ + if (p && !refcount_inc_not_zero(&p->rcu_users)) + /* If the task had been removed from the map and freed as + * described above, refcount_inc_not_zero() will return false. + * The task will be freed at some point after the current RCU + * gp has ended, so just return NULL to the user. + */ + p = NULL; + rcu_read_unlock(); + + return p; +} + +/** + * bpf_task_release - Release the reference acquired on a struct task_struct *. + * If this kfunc is invoked in an RCU read region, the task_struct is + * guaranteed to not be freed until the current grace period has ended, even if + * its refcount drops to 0. + * @p: The task on which a reference is being released. + */ +void bpf_task_release(struct task_struct *p) +{ + if (!p) + return; + + put_task_struct_rcu_user(p); +} + __diag_pop(); BTF_SET8_START(generic_btf_ids) @@ -1836,6 +1893,9 @@ BTF_ID_FLAGS(func, bpf_list_push_front) BTF_ID_FLAGS(func, bpf_list_push_back) BTF_ID_FLAGS(func, bpf_list_pop_front, KF_ACQUIRE | KF_RET_NULL) BTF_ID_FLAGS(func, bpf_list_pop_back, KF_ACQUIRE | KF_RET_NULL) +BTF_ID_FLAGS(func, bpf_task_acquire, KF_ACQUIRE | KF_TRUSTED_ARGS) +BTF_ID_FLAGS(func, bpf_task_kptr_get, KF_ACQUIRE | KF_KPTR_GET | KF_RET_NULL) +BTF_ID_FLAGS(func, bpf_task_release, KF_RELEASE) BTF_SET8_END(generic_btf_ids) static const struct btf_kfunc_id_set generic_kfunc_set = { @@ -1843,14 +1903,26 @@ static const struct btf_kfunc_id_set generic_kfunc_set = { .set = &generic_btf_ids, }; +BTF_ID_LIST(generic_dtor_ids) +BTF_ID(struct, task_struct) +BTF_ID(func, bpf_task_release) + static int __init kfunc_init(void) { int ret; + const struct btf_id_dtor_kfunc generic_dtors[] = { + { + .btf_id = generic_dtor_ids[0], + .kfunc_btf_id = generic_dtor_ids[1] + }, + }; ret = register_btf_kfunc_id_set(BPF_PROG_TYPE_TRACING, &generic_kfunc_set); - if (ret) - return ret; - return register_btf_kfunc_id_set(BPF_PROG_TYPE_SCHED_CLS, &generic_kfunc_set); + ret = ret ?: register_btf_kfunc_id_set(BPF_PROG_TYPE_SCHED_CLS, &generic_kfunc_set); + ret = ret ?: register_btf_kfunc_id_set(BPF_PROG_TYPE_STRUCT_OPS, &generic_kfunc_set); + return ret ?: register_btf_id_dtor_kfuncs(generic_dtors, + ARRAY_SIZE(generic_dtors), + THIS_MODULE); } late_initcall(kfunc_init); From fe147956fca4604b920e6be652abc9bea8ce8952 Mon Sep 17 00:00:00 2001 From: David Vernet Date: Sat, 19 Nov 2022 23:10:04 -0600 Subject: [PATCH 1088/2002] bpf/selftests: Add selftests for new task kfuncs A previous change added a series of kfuncs for storing struct task_struct objects as referenced kptrs. This patch adds a new task_kfunc test suite for validating their expected behavior. Signed-off-by: David Vernet Link: https://lore.kernel.org/r/20221120051004.3605026-5-void@manifault.com Signed-off-by: Alexei Starovoitov --- tools/testing/selftests/bpf/DENYLIST.s390x | 1 + .../selftests/bpf/prog_tests/task_kfunc.c | 159 +++++++++++ .../selftests/bpf/progs/task_kfunc_common.h | 71 +++++ .../selftests/bpf/progs/task_kfunc_failure.c | 260 ++++++++++++++++++ .../selftests/bpf/progs/task_kfunc_success.c | 149 ++++++++++ 5 files changed, 640 insertions(+) create mode 100644 tools/testing/selftests/bpf/prog_tests/task_kfunc.c create mode 100644 tools/testing/selftests/bpf/progs/task_kfunc_common.h create mode 100644 tools/testing/selftests/bpf/progs/task_kfunc_failure.c create mode 100644 tools/testing/selftests/bpf/progs/task_kfunc_success.c diff --git a/tools/testing/selftests/bpf/DENYLIST.s390x b/tools/testing/selftests/bpf/DENYLIST.s390x index 072243af93b0..f70a677b38e5 100644 --- a/tools/testing/selftests/bpf/DENYLIST.s390x +++ b/tools/testing/selftests/bpf/DENYLIST.s390x @@ -54,6 +54,7 @@ skc_to_unix_sock # could not attach BPF object unexpecte socket_cookie # prog_attach unexpected error: -524 (trampoline) stacktrace_build_id # compare_map_keys stackid_hmap vs. stackmap err -2 errno 2 (?) tailcalls # tail_calls are not allowed in non-JITed programs with bpf-to-bpf calls (?) +task_kfunc # JIT does not support calling kernel function task_local_storage # failed to auto-attach program 'trace_exit_creds': -524 (trampoline) test_bpffs # bpffs test failed 255 (iterator) test_bprm_opts # failed to auto-attach program 'secure_exec': -524 (trampoline) diff --git a/tools/testing/selftests/bpf/prog_tests/task_kfunc.c b/tools/testing/selftests/bpf/prog_tests/task_kfunc.c new file mode 100644 index 000000000000..4994fe6092cc --- /dev/null +++ b/tools/testing/selftests/bpf/prog_tests/task_kfunc.c @@ -0,0 +1,159 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2022 Meta Platforms, Inc. and affiliates. */ + +#define _GNU_SOURCE +#include +#include +#include + +#include "task_kfunc_failure.skel.h" +#include "task_kfunc_success.skel.h" + +static size_t log_buf_sz = 1 << 20; /* 1 MB */ +static char obj_log_buf[1048576]; + +static struct task_kfunc_success *open_load_task_kfunc_skel(void) +{ + struct task_kfunc_success *skel; + int err; + + skel = task_kfunc_success__open(); + if (!ASSERT_OK_PTR(skel, "skel_open")) + return NULL; + + skel->bss->pid = getpid(); + + err = task_kfunc_success__load(skel); + if (!ASSERT_OK(err, "skel_load")) + goto cleanup; + + return skel; + +cleanup: + task_kfunc_success__destroy(skel); + return NULL; +} + +static void run_success_test(const char *prog_name) +{ + struct task_kfunc_success *skel; + int status; + pid_t child_pid; + struct bpf_program *prog; + struct bpf_link *link = NULL; + + skel = open_load_task_kfunc_skel(); + if (!ASSERT_OK_PTR(skel, "open_load_skel")) + return; + + if (!ASSERT_OK(skel->bss->err, "pre_spawn_err")) + goto cleanup; + + prog = bpf_object__find_program_by_name(skel->obj, prog_name); + if (!ASSERT_OK_PTR(prog, "bpf_object__find_program_by_name")) + goto cleanup; + + link = bpf_program__attach(prog); + if (!ASSERT_OK_PTR(link, "attached_link")) + goto cleanup; + + child_pid = fork(); + if (!ASSERT_GT(child_pid, -1, "child_pid")) + goto cleanup; + if (child_pid == 0) + _exit(0); + waitpid(child_pid, &status, 0); + + ASSERT_OK(skel->bss->err, "post_wait_err"); + +cleanup: + bpf_link__destroy(link); + task_kfunc_success__destroy(skel); +} + +static const char * const success_tests[] = { + "test_task_acquire_release_argument", + "test_task_acquire_release_current", + "test_task_acquire_leave_in_map", + "test_task_xchg_release", + "test_task_get_release", + "test_task_current_acquire_release", +}; + +static struct { + const char *prog_name; + const char *expected_err_msg; +} failure_tests[] = { + {"task_kfunc_acquire_untrusted", "R1 must be referenced or trusted"}, + {"task_kfunc_acquire_fp", "arg#0 pointer type STRUCT task_struct must point"}, + {"task_kfunc_acquire_unsafe_kretprobe", "reg type unsupported for arg#0 function"}, + {"task_kfunc_acquire_trusted_walked", "R1 must be referenced or trusted"}, + {"task_kfunc_acquire_null", "arg#0 pointer type STRUCT task_struct must point"}, + {"task_kfunc_acquire_unreleased", "Unreleased reference"}, + {"task_kfunc_get_non_kptr_param", "arg#0 expected pointer to map value"}, + {"task_kfunc_get_non_kptr_acquired", "arg#0 expected pointer to map value"}, + {"task_kfunc_get_null", "arg#0 expected pointer to map value"}, + {"task_kfunc_xchg_unreleased", "Unreleased reference"}, + {"task_kfunc_get_unreleased", "Unreleased reference"}, + {"task_kfunc_release_untrusted", "arg#0 is untrusted_ptr_or_null_ expected ptr_ or socket"}, + {"task_kfunc_release_fp", "arg#0 pointer type STRUCT task_struct must point"}, + {"task_kfunc_release_null", "arg#0 is ptr_or_null_ expected ptr_ or socket"}, + {"task_kfunc_release_unacquired", "release kernel function bpf_task_release expects"}, +}; + +static void verify_fail(const char *prog_name, const char *expected_err_msg) +{ + LIBBPF_OPTS(bpf_object_open_opts, opts); + struct task_kfunc_failure *skel; + int err, i; + + opts.kernel_log_buf = obj_log_buf; + opts.kernel_log_size = log_buf_sz; + opts.kernel_log_level = 1; + + skel = task_kfunc_failure__open_opts(&opts); + if (!ASSERT_OK_PTR(skel, "task_kfunc_failure__open_opts")) + goto cleanup; + + for (i = 0; i < ARRAY_SIZE(failure_tests); i++) { + struct bpf_program *prog; + const char *curr_name = failure_tests[i].prog_name; + + prog = bpf_object__find_program_by_name(skel->obj, curr_name); + if (!ASSERT_OK_PTR(prog, "bpf_object__find_program_by_name")) + goto cleanup; + + bpf_program__set_autoload(prog, !strcmp(curr_name, prog_name)); + } + + err = task_kfunc_failure__load(skel); + if (!ASSERT_ERR(err, "unexpected load success")) + goto cleanup; + + if (!ASSERT_OK_PTR(strstr(obj_log_buf, expected_err_msg), "expected_err_msg")) { + fprintf(stderr, "Expected err_msg: %s\n", expected_err_msg); + fprintf(stderr, "Verifier output: %s\n", obj_log_buf); + } + +cleanup: + task_kfunc_failure__destroy(skel); +} + +void test_task_kfunc(void) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(success_tests); i++) { + if (!test__start_subtest(success_tests[i])) + continue; + + run_success_test(success_tests[i]); + } + + for (i = 0; i < ARRAY_SIZE(failure_tests); i++) { + if (!test__start_subtest(failure_tests[i].prog_name)) + continue; + + verify_fail(failure_tests[i].prog_name, failure_tests[i].expected_err_msg); + } +} diff --git a/tools/testing/selftests/bpf/progs/task_kfunc_common.h b/tools/testing/selftests/bpf/progs/task_kfunc_common.h new file mode 100644 index 000000000000..160d6dde00be --- /dev/null +++ b/tools/testing/selftests/bpf/progs/task_kfunc_common.h @@ -0,0 +1,71 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (c) 2022 Meta Platforms, Inc. and affiliates. */ + +#ifndef _TASK_KFUNC_COMMON_H +#define _TASK_KFUNC_COMMON_H + +#include +#include +#include +#include + +struct __tasks_kfunc_map_value { + struct task_struct __kptr_ref * task; +}; + +struct hash_map { + __uint(type, BPF_MAP_TYPE_HASH); + __type(key, int); + __type(value, struct __tasks_kfunc_map_value); + __uint(max_entries, 1); +} __tasks_kfunc_map SEC(".maps"); + +struct task_struct *bpf_task_acquire(struct task_struct *p) __ksym; +struct task_struct *bpf_task_kptr_get(struct task_struct **pp) __ksym; +void bpf_task_release(struct task_struct *p) __ksym; + +static inline struct __tasks_kfunc_map_value *tasks_kfunc_map_value_lookup(struct task_struct *p) +{ + s32 pid; + long status; + + status = bpf_probe_read_kernel(&pid, sizeof(pid), &p->pid); + if (status) + return NULL; + + return bpf_map_lookup_elem(&__tasks_kfunc_map, &pid); +} + +static inline int tasks_kfunc_map_insert(struct task_struct *p) +{ + struct __tasks_kfunc_map_value local, *v; + long status; + struct task_struct *acquired, *old; + s32 pid; + + status = bpf_probe_read_kernel(&pid, sizeof(pid), &p->pid); + if (status) + return status; + + local.task = NULL; + status = bpf_map_update_elem(&__tasks_kfunc_map, &pid, &local, BPF_NOEXIST); + if (status) + return status; + + v = bpf_map_lookup_elem(&__tasks_kfunc_map, &pid); + if (!v) { + bpf_map_delete_elem(&__tasks_kfunc_map, &pid); + return -ENOENT; + } + + acquired = bpf_task_acquire(p); + old = bpf_kptr_xchg(&v->task, acquired); + if (old) { + bpf_task_release(old); + return -EEXIST; + } + + return 0; +} + +#endif /* _TASK_KFUNC_COMMON_H */ diff --git a/tools/testing/selftests/bpf/progs/task_kfunc_failure.c b/tools/testing/selftests/bpf/progs/task_kfunc_failure.c new file mode 100644 index 000000000000..93e934ddfcb6 --- /dev/null +++ b/tools/testing/selftests/bpf/progs/task_kfunc_failure.c @@ -0,0 +1,260 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2022 Meta Platforms, Inc. and affiliates. */ + +#include +#include +#include + +#include "task_kfunc_common.h" + +char _license[] SEC("license") = "GPL"; + +/* Prototype for all of the program trace events below: + * + * TRACE_EVENT(task_newtask, + * TP_PROTO(struct task_struct *p, u64 clone_flags) + */ + +static struct __tasks_kfunc_map_value *insert_lookup_task(struct task_struct *task) +{ + int status; + + status = tasks_kfunc_map_insert(task); + if (status) + return NULL; + + return tasks_kfunc_map_value_lookup(task); +} + +SEC("tp_btf/task_newtask") +int BPF_PROG(task_kfunc_acquire_untrusted, struct task_struct *task, u64 clone_flags) +{ + struct task_struct *acquired; + struct __tasks_kfunc_map_value *v; + + v = insert_lookup_task(task); + if (!v) + return 0; + + /* Can't invoke bpf_task_acquire() on an untrusted pointer. */ + acquired = bpf_task_acquire(v->task); + bpf_task_release(acquired); + + return 0; +} + +SEC("tp_btf/task_newtask") +int BPF_PROG(task_kfunc_acquire_fp, struct task_struct *task, u64 clone_flags) +{ + struct task_struct *acquired, *stack_task = (struct task_struct *)&clone_flags; + + /* Can't invoke bpf_task_acquire() on a random frame pointer. */ + acquired = bpf_task_acquire((struct task_struct *)&stack_task); + bpf_task_release(acquired); + + return 0; +} + +SEC("kretprobe/free_task") +int BPF_PROG(task_kfunc_acquire_unsafe_kretprobe, struct task_struct *task, u64 clone_flags) +{ + struct task_struct *acquired; + + acquired = bpf_task_acquire(task); + /* Can't release a bpf_task_acquire()'d task without a NULL check. */ + bpf_task_release(acquired); + + return 0; +} + +SEC("tp_btf/task_newtask") +int BPF_PROG(task_kfunc_acquire_trusted_walked, struct task_struct *task, u64 clone_flags) +{ + struct task_struct *acquired; + + /* Can't invoke bpf_task_acquire() on a trusted pointer obtained from walking a struct. */ + acquired = bpf_task_acquire(task->last_wakee); + bpf_task_release(acquired); + + return 0; +} + + +SEC("tp_btf/task_newtask") +int BPF_PROG(task_kfunc_acquire_null, struct task_struct *task, u64 clone_flags) +{ + struct task_struct *acquired; + + /* Can't invoke bpf_task_acquire() on a NULL pointer. */ + acquired = bpf_task_acquire(NULL); + if (!acquired) + return 0; + bpf_task_release(acquired); + + return 0; +} + +SEC("tp_btf/task_newtask") +int BPF_PROG(task_kfunc_acquire_unreleased, struct task_struct *task, u64 clone_flags) +{ + struct task_struct *acquired; + + acquired = bpf_task_acquire(task); + + /* Acquired task is never released. */ + + return 0; +} + +SEC("tp_btf/task_newtask") +int BPF_PROG(task_kfunc_get_non_kptr_param, struct task_struct *task, u64 clone_flags) +{ + struct task_struct *kptr; + + /* Cannot use bpf_task_kptr_get() on a non-kptr, even on a valid task. */ + kptr = bpf_task_kptr_get(&task); + if (!kptr) + return 0; + + bpf_task_release(kptr); + + return 0; +} + +SEC("tp_btf/task_newtask") +int BPF_PROG(task_kfunc_get_non_kptr_acquired, struct task_struct *task, u64 clone_flags) +{ + struct task_struct *kptr, *acquired; + + acquired = bpf_task_acquire(task); + + /* Cannot use bpf_task_kptr_get() on a non-kptr, even if it was acquired. */ + kptr = bpf_task_kptr_get(&acquired); + bpf_task_release(acquired); + if (!kptr) + return 0; + + bpf_task_release(kptr); + + return 0; +} + +SEC("tp_btf/task_newtask") +int BPF_PROG(task_kfunc_get_null, struct task_struct *task, u64 clone_flags) +{ + struct task_struct *kptr; + + /* Cannot use bpf_task_kptr_get() on a NULL pointer. */ + kptr = bpf_task_kptr_get(NULL); + if (!kptr) + return 0; + + bpf_task_release(kptr); + + return 0; +} + +SEC("tp_btf/task_newtask") +int BPF_PROG(task_kfunc_xchg_unreleased, struct task_struct *task, u64 clone_flags) +{ + struct task_struct *kptr; + struct __tasks_kfunc_map_value *v; + + v = insert_lookup_task(task); + if (!v) + return 0; + + kptr = bpf_kptr_xchg(&v->task, NULL); + if (!kptr) + return 0; + + /* Kptr retrieved from map is never released. */ + + return 0; +} + +SEC("tp_btf/task_newtask") +int BPF_PROG(task_kfunc_get_unreleased, struct task_struct *task, u64 clone_flags) +{ + struct task_struct *kptr; + struct __tasks_kfunc_map_value *v; + + v = insert_lookup_task(task); + if (!v) + return 0; + + kptr = bpf_task_kptr_get(&v->task); + if (!kptr) + return 0; + + /* Kptr acquired above is never released. */ + + return 0; +} + +SEC("tp_btf/task_newtask") +int BPF_PROG(task_kfunc_release_untrusted, struct task_struct *task, u64 clone_flags) +{ + struct __tasks_kfunc_map_value *v; + + v = insert_lookup_task(task); + if (!v) + return 0; + + /* Can't invoke bpf_task_release() on an untrusted pointer. */ + bpf_task_release(v->task); + + return 0; +} + +SEC("tp_btf/task_newtask") +int BPF_PROG(task_kfunc_release_fp, struct task_struct *task, u64 clone_flags) +{ + struct task_struct *acquired = (struct task_struct *)&clone_flags; + + /* Cannot release random frame pointer. */ + bpf_task_release(acquired); + + return 0; +} + +SEC("tp_btf/task_newtask") +int BPF_PROG(task_kfunc_release_null, struct task_struct *task, u64 clone_flags) +{ + struct __tasks_kfunc_map_value local, *v; + long status; + struct task_struct *acquired, *old; + s32 pid; + + status = bpf_probe_read_kernel(&pid, sizeof(pid), &task->pid); + if (status) + return 0; + + local.task = NULL; + status = bpf_map_update_elem(&__tasks_kfunc_map, &pid, &local, BPF_NOEXIST); + if (status) + return status; + + v = bpf_map_lookup_elem(&__tasks_kfunc_map, &pid); + if (!v) + return -ENOENT; + + acquired = bpf_task_acquire(task); + + old = bpf_kptr_xchg(&v->task, acquired); + + /* old cannot be passed to bpf_task_release() without a NULL check. */ + bpf_task_release(old); + bpf_task_release(old); + + return 0; +} + +SEC("tp_btf/task_newtask") +int BPF_PROG(task_kfunc_release_unacquired, struct task_struct *task, u64 clone_flags) +{ + /* Cannot release trusted task pointer which was not acquired. */ + bpf_task_release(task); + + return 0; +} diff --git a/tools/testing/selftests/bpf/progs/task_kfunc_success.c b/tools/testing/selftests/bpf/progs/task_kfunc_success.c new file mode 100644 index 000000000000..be4534b5ba2e --- /dev/null +++ b/tools/testing/selftests/bpf/progs/task_kfunc_success.c @@ -0,0 +1,149 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2022 Meta Platforms, Inc. and affiliates. */ + +#include +#include +#include + +#include "task_kfunc_common.h" + +char _license[] SEC("license") = "GPL"; + +int err, pid; + +/* Prototype for all of the program trace events below: + * + * TRACE_EVENT(task_newtask, + * TP_PROTO(struct task_struct *p, u64 clone_flags) + */ + +static bool is_test_kfunc_task(void) +{ + int cur_pid = bpf_get_current_pid_tgid() >> 32; + + return pid == cur_pid; +} + +static int test_acquire_release(struct task_struct *task) +{ + struct task_struct *acquired; + + acquired = bpf_task_acquire(task); + bpf_task_release(acquired); + + return 0; +} + +SEC("tp_btf/task_newtask") +int BPF_PROG(test_task_acquire_release_argument, struct task_struct *task, u64 clone_flags) +{ + if (!is_test_kfunc_task()) + return 0; + + return test_acquire_release(task); +} + +SEC("tp_btf/task_newtask") +int BPF_PROG(test_task_acquire_release_current, struct task_struct *task, u64 clone_flags) +{ + if (!is_test_kfunc_task()) + return 0; + + return test_acquire_release(bpf_get_current_task_btf()); +} + +SEC("tp_btf/task_newtask") +int BPF_PROG(test_task_acquire_leave_in_map, struct task_struct *task, u64 clone_flags) +{ + long status; + + if (!is_test_kfunc_task()) + return 0; + + status = tasks_kfunc_map_insert(task); + if (status) + err = 1; + + return 0; +} + +SEC("tp_btf/task_newtask") +int BPF_PROG(test_task_xchg_release, struct task_struct *task, u64 clone_flags) +{ + struct task_struct *kptr; + struct __tasks_kfunc_map_value *v; + long status; + + if (!is_test_kfunc_task()) + return 0; + + status = tasks_kfunc_map_insert(task); + if (status) { + err = 1; + return 0; + } + + v = tasks_kfunc_map_value_lookup(task); + if (!v) { + err = 2; + return 0; + } + + kptr = bpf_kptr_xchg(&v->task, NULL); + if (!kptr) { + err = 3; + return 0; + } + + bpf_task_release(kptr); + + return 0; +} + +SEC("tp_btf/task_newtask") +int BPF_PROG(test_task_get_release, struct task_struct *task, u64 clone_flags) +{ + struct task_struct *kptr; + struct __tasks_kfunc_map_value *v; + long status; + + if (!is_test_kfunc_task()) + return 0; + + status = tasks_kfunc_map_insert(task); + if (status) { + err = 1; + return 0; + } + + v = tasks_kfunc_map_value_lookup(task); + if (!v) { + err = 2; + return 0; + } + + kptr = bpf_task_kptr_get(&v->task); + if (!kptr) { + err = 3; + return 0; + } + + bpf_task_release(kptr); + + return 0; +} + +SEC("tp_btf/task_newtask") +int BPF_PROG(test_task_current_acquire_release, struct task_struct *task, u64 clone_flags) +{ + struct task_struct *current, *acquired; + + if (!is_test_kfunc_task()) + return 0; + + current = bpf_get_current_task_btf(); + acquired = bpf_task_acquire(current); + bpf_task_release(acquired); + + return 0; +} From e181d3f143f7957a73c8365829249d8084602606 Mon Sep 17 00:00:00 2001 From: Kumar Kartikeya Dwivedi Date: Mon, 21 Nov 2022 02:56:10 +0530 Subject: [PATCH 1089/2002] bpf: Disallow bpf_obj_new_impl call when bpf_mem_alloc_init fails In the unlikely event that bpf_global_ma is not correctly initialized, instead of checking the boolean everytime bpf_obj_new_impl is called, simply check it while loading the program and return an error if bpf_global_ma_set is false. Suggested-by: Alexei Starovoitov Signed-off-by: Kumar Kartikeya Dwivedi Link: https://lore.kernel.org/r/20221120212610.2361700-1-memxor@gmail.com Signed-off-by: Alexei Starovoitov --- kernel/bpf/helpers.c | 2 -- kernel/bpf/verifier.c | 3 +++ 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/kernel/bpf/helpers.c b/kernel/bpf/helpers.c index 89a95f3d854c..3d4edd314450 100644 --- a/kernel/bpf/helpers.c +++ b/kernel/bpf/helpers.c @@ -1760,8 +1760,6 @@ void *bpf_obj_new_impl(u64 local_type_id__k, void *meta__ign) u64 size = local_type_id__k; void *p; - if (unlikely(!bpf_global_ma_set)) - return NULL; p = bpf_mem_alloc(&bpf_global_ma, size); if (!p) return NULL; diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 5bc9d84d7924..eb090af35477 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -8878,6 +8878,9 @@ static int check_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn, struct btf *ret_btf; u32 ret_btf_id; + if (unlikely(!bpf_global_ma_set)) + return -ENOMEM; + if (((u64)(u32)meta.arg_constant.value) != meta.arg_constant.value) { verbose(env, "local type ID argument must be in range [0, U32_MAX]\n"); return -EINVAL; From cfe1456440c8feaf6558577a400745d774418379 Mon Sep 17 00:00:00 2001 From: Yonghong Song Date: Sun, 20 Nov 2022 11:54:26 -0800 Subject: [PATCH 1090/2002] bpf: Add support for kfunc set with common btf_ids Later on, we will introduce kfuncs bpf_cast_to_kern_ctx() and bpf_rdonly_cast() which apply to all program types. Currently kfunc set only supports individual prog types. This patch added support for kfunc applying to all program types. Signed-off-by: Yonghong Song Link: https://lore.kernel.org/r/20221120195426.3113828-1-yhs@fb.com Signed-off-by: Alexei Starovoitov --- kernel/bpf/btf.c | 8 ++++++++ kernel/bpf/helpers.c | 12 +++++++++++- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/kernel/bpf/btf.c b/kernel/bpf/btf.c index d52054ec69c9..1c78d4df9e18 100644 --- a/kernel/bpf/btf.c +++ b/kernel/bpf/btf.c @@ -199,6 +199,7 @@ DEFINE_IDR(btf_idr); DEFINE_SPINLOCK(btf_idr_lock); enum btf_kfunc_hook { + BTF_KFUNC_HOOK_COMMON, BTF_KFUNC_HOOK_XDP, BTF_KFUNC_HOOK_TC, BTF_KFUNC_HOOK_STRUCT_OPS, @@ -7531,6 +7532,8 @@ static u32 *__btf_kfunc_id_set_contains(const struct btf *btf, static int bpf_prog_type_to_kfunc_hook(enum bpf_prog_type prog_type) { switch (prog_type) { + case BPF_PROG_TYPE_UNSPEC: + return BTF_KFUNC_HOOK_COMMON; case BPF_PROG_TYPE_XDP: return BTF_KFUNC_HOOK_XDP; case BPF_PROG_TYPE_SCHED_CLS: @@ -7559,6 +7562,11 @@ u32 *btf_kfunc_id_set_contains(const struct btf *btf, u32 kfunc_btf_id) { enum btf_kfunc_hook hook; + u32 *kfunc_flags; + + kfunc_flags = __btf_kfunc_id_set_contains(btf, BTF_KFUNC_HOOK_COMMON, kfunc_btf_id); + if (kfunc_flags) + return kfunc_flags; hook = bpf_prog_type_to_kfunc_hook(prog_type); return __btf_kfunc_id_set_contains(btf, hook, kfunc_btf_id); diff --git a/kernel/bpf/helpers.c b/kernel/bpf/helpers.c index 3d4edd314450..8e0d80b6bf09 100644 --- a/kernel/bpf/helpers.c +++ b/kernel/bpf/helpers.c @@ -1901,10 +1901,19 @@ static const struct btf_kfunc_id_set generic_kfunc_set = { .set = &generic_btf_ids, }; + BTF_ID_LIST(generic_dtor_ids) BTF_ID(struct, task_struct) BTF_ID(func, bpf_task_release) +BTF_SET8_START(common_btf_ids) +BTF_SET8_END(common_btf_ids) + +static const struct btf_kfunc_id_set common_kfunc_set = { + .owner = THIS_MODULE, + .set = &common_btf_ids, +}; + static int __init kfunc_init(void) { int ret; @@ -1918,9 +1927,10 @@ static int __init kfunc_init(void) ret = register_btf_kfunc_id_set(BPF_PROG_TYPE_TRACING, &generic_kfunc_set); ret = ret ?: register_btf_kfunc_id_set(BPF_PROG_TYPE_SCHED_CLS, &generic_kfunc_set); ret = ret ?: register_btf_kfunc_id_set(BPF_PROG_TYPE_STRUCT_OPS, &generic_kfunc_set); - return ret ?: register_btf_id_dtor_kfuncs(generic_dtors, + ret = ret ?: register_btf_id_dtor_kfuncs(generic_dtors, ARRAY_SIZE(generic_dtors), THIS_MODULE); + return ret ?: register_btf_kfunc_id_set(BPF_PROG_TYPE_UNSPEC, &common_kfunc_set); } late_initcall(kfunc_init); From fd264ca020948a743e4c36731dfdecc4a812153c Mon Sep 17 00:00:00 2001 From: Yonghong Song Date: Sun, 20 Nov 2022 11:54:32 -0800 Subject: [PATCH 1091/2002] bpf: Add a kfunc to type cast from bpf uapi ctx to kernel ctx Implement bpf_cast_to_kern_ctx() kfunc which does a type cast of a uapi ctx object to the corresponding kernel ctx. Previously if users want to access some data available in kctx but not in uapi ctx, bpf_probe_read_kernel() helper is needed. The introduction of bpf_cast_to_kern_ctx() allows direct memory access which makes code simpler and easier to understand. Signed-off-by: Yonghong Song Link: https://lore.kernel.org/r/20221120195432.3113982-1-yhs@fb.com Signed-off-by: Alexei Starovoitov --- include/linux/btf.h | 5 +++++ kernel/bpf/btf.c | 20 ++++++++++++++++++++ kernel/bpf/helpers.c | 6 ++++++ kernel/bpf/verifier.c | 22 ++++++++++++++++++++++ 4 files changed, 53 insertions(+) diff --git a/include/linux/btf.h b/include/linux/btf.h index d38aa4251c28..9ed00077db6e 100644 --- a/include/linux/btf.h +++ b/include/linux/btf.h @@ -487,6 +487,7 @@ const struct btf_member * btf_get_prog_ctx_type(struct bpf_verifier_log *log, const struct btf *btf, const struct btf_type *t, enum bpf_prog_type prog_type, int arg); +int get_kern_ctx_btf_id(struct bpf_verifier_log *log, enum bpf_prog_type prog_type); bool btf_types_are_same(const struct btf *btf1, u32 id1, const struct btf *btf2, u32 id2); #else @@ -531,6 +532,10 @@ btf_get_prog_ctx_type(struct bpf_verifier_log *log, const struct btf *btf, { return NULL; } +static inline int get_kern_ctx_btf_id(struct bpf_verifier_log *log, + enum bpf_prog_type prog_type) { + return -EINVAL; +} static inline bool btf_types_are_same(const struct btf *btf1, u32 id1, const struct btf *btf2, u32 id2) { diff --git a/kernel/bpf/btf.c b/kernel/bpf/btf.c index 1c78d4df9e18..1a59cc7ad730 100644 --- a/kernel/bpf/btf.c +++ b/kernel/bpf/btf.c @@ -5603,6 +5603,26 @@ static int btf_translate_to_vmlinux(struct bpf_verifier_log *log, return kern_ctx_type->type; } +int get_kern_ctx_btf_id(struct bpf_verifier_log *log, enum bpf_prog_type prog_type) +{ + const struct btf_member *kctx_member; + const struct btf_type *conv_struct; + const struct btf_type *kctx_type; + u32 kctx_type_id; + + conv_struct = bpf_ctx_convert.t; + /* get member for kernel ctx type */ + kctx_member = btf_type_member(conv_struct) + bpf_ctx_convert_map[prog_type] * 2 + 1; + kctx_type_id = kctx_member->type; + kctx_type = btf_type_by_id(btf_vmlinux, kctx_type_id); + if (!btf_type_is_struct(kctx_type)) { + bpf_log(log, "kern ctx type id %u is not a struct\n", kctx_type_id); + return -EINVAL; + } + + return kctx_type_id; +} + BTF_ID_LIST(bpf_ctx_convert_btf_id) BTF_ID(struct, bpf_ctx_convert) diff --git a/kernel/bpf/helpers.c b/kernel/bpf/helpers.c index 8e0d80b6bf09..e45e72676f5a 100644 --- a/kernel/bpf/helpers.c +++ b/kernel/bpf/helpers.c @@ -1879,6 +1879,11 @@ void bpf_task_release(struct task_struct *p) put_task_struct_rcu_user(p); } +void *bpf_cast_to_kern_ctx(void *obj) +{ + return obj; +} + __diag_pop(); BTF_SET8_START(generic_btf_ids) @@ -1907,6 +1912,7 @@ BTF_ID(struct, task_struct) BTF_ID(func, bpf_task_release) BTF_SET8_START(common_btf_ids) +BTF_ID_FLAGS(func, bpf_cast_to_kern_ctx) BTF_SET8_END(common_btf_ids) static const struct btf_kfunc_id_set common_kfunc_set = { diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index eb090af35477..d63cc0fb0a9c 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -7907,6 +7907,7 @@ struct bpf_kfunc_call_arg_meta { u32 ref_obj_id; u8 release_regno; bool r0_rdonly; + u32 ret_btf_id; u64 r0_size; struct { u64 value; @@ -8151,6 +8152,7 @@ enum special_kfunc_type { KF_bpf_list_push_back, KF_bpf_list_pop_front, KF_bpf_list_pop_back, + KF_bpf_cast_to_kern_ctx, }; BTF_SET_START(special_kfunc_set) @@ -8160,6 +8162,7 @@ BTF_ID(func, bpf_list_push_front) BTF_ID(func, bpf_list_push_back) BTF_ID(func, bpf_list_pop_front) BTF_ID(func, bpf_list_pop_back) +BTF_ID(func, bpf_cast_to_kern_ctx) BTF_SET_END(special_kfunc_set) BTF_ID_LIST(special_kfunc_list) @@ -8169,6 +8172,7 @@ BTF_ID(func, bpf_list_push_front) BTF_ID(func, bpf_list_push_back) BTF_ID(func, bpf_list_pop_front) BTF_ID(func, bpf_list_pop_back) +BTF_ID(func, bpf_cast_to_kern_ctx) static enum kfunc_ptr_arg_type get_kfunc_ptr_arg_type(struct bpf_verifier_env *env, @@ -8182,6 +8186,9 @@ get_kfunc_ptr_arg_type(struct bpf_verifier_env *env, struct bpf_reg_state *reg = ®s[regno]; bool arg_mem_size = false; + if (meta->func_id == special_kfunc_list[KF_bpf_cast_to_kern_ctx]) + return KF_ARG_PTR_TO_CTX; + /* In this function, we verify the kfunc's BTF as per the argument type, * leaving the rest of the verification with respect to the register * type to our caller. When a set of conditions hold in the BTF type of @@ -8668,6 +8675,13 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_kfunc_call_ verbose(env, "arg#%d expected pointer to ctx, but got %s\n", i, btf_type_str(t)); return -EINVAL; } + + if (meta->func_id == special_kfunc_list[KF_bpf_cast_to_kern_ctx]) { + ret = get_kern_ctx_btf_id(&env->log, resolve_prog_type(env->prog)); + if (ret < 0) + return -EINVAL; + meta->ret_btf_id = ret; + } break; case KF_ARG_PTR_TO_ALLOC_BTF_ID: if (reg->type != (PTR_TO_BTF_ID | MEM_ALLOC)) { @@ -8922,6 +8936,11 @@ static int check_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn, regs[BPF_REG_0].btf = field->list_head.btf; regs[BPF_REG_0].btf_id = field->list_head.value_btf_id; regs[BPF_REG_0].off = field->list_head.node_offset; + } else if (meta.func_id == special_kfunc_list[KF_bpf_cast_to_kern_ctx]) { + mark_reg_known_zero(env, regs, BPF_REG_0); + regs[BPF_REG_0].type = PTR_TO_BTF_ID | PTR_TRUSTED; + regs[BPF_REG_0].btf = desc_btf; + regs[BPF_REG_0].btf_id = meta.ret_btf_id; } else { verbose(env, "kernel function %s unhandled dynamic return type\n", meta.func_name); @@ -15175,6 +15194,9 @@ static int fixup_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn, insn_buf[1] = addr[1]; insn_buf[2] = *insn; *cnt = 3; + } else if (desc->func_id == special_kfunc_list[KF_bpf_cast_to_kern_ctx]) { + insn_buf[0] = BPF_MOV64_REG(BPF_REG_0, BPF_REG_1); + *cnt = 1; } return 0; } From a35b9af4ec2c7f69286ef861fd2074a577e354cb Mon Sep 17 00:00:00 2001 From: Yonghong Song Date: Sun, 20 Nov 2022 11:54:37 -0800 Subject: [PATCH 1092/2002] bpf: Add a kfunc for generic type cast Implement bpf_rdonly_cast() which tries to cast the object to a specified type. This tries to support use case like below: #define skb_shinfo(SKB) ((struct skb_shared_info *)(skb_end_pointer(SKB))) where skb_end_pointer(SKB) is a 'unsigned char *' and needs to be casted to 'struct skb_shared_info *'. The signature of bpf_rdonly_cast() looks like void *bpf_rdonly_cast(void *obj, __u32 btf_id) The function returns the same 'obj' but with PTR_TO_BTF_ID with btf_id. The verifier will ensure btf_id being a struct type. Since the supported type cast may not reflect what the 'obj' represents, the returned btf_id is marked as PTR_UNTRUSTED, so the return value and subsequent pointer chasing cannot be used as helper/kfunc arguments. Signed-off-by: Yonghong Song Link: https://lore.kernel.org/r/20221120195437.3114585-1-yhs@fb.com Signed-off-by: Alexei Starovoitov --- kernel/bpf/helpers.c | 6 ++++++ kernel/bpf/verifier.c | 20 ++++++++++++++++++-- 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/kernel/bpf/helpers.c b/kernel/bpf/helpers.c index e45e72676f5a..9e8a5557ea8d 100644 --- a/kernel/bpf/helpers.c +++ b/kernel/bpf/helpers.c @@ -1884,6 +1884,11 @@ void *bpf_cast_to_kern_ctx(void *obj) return obj; } +void *bpf_rdonly_cast(void *obj__ign, u32 btf_id__k) +{ + return obj__ign; +} + __diag_pop(); BTF_SET8_START(generic_btf_ids) @@ -1913,6 +1918,7 @@ BTF_ID(func, bpf_task_release) BTF_SET8_START(common_btf_ids) BTF_ID_FLAGS(func, bpf_cast_to_kern_ctx) +BTF_ID_FLAGS(func, bpf_rdonly_cast) BTF_SET8_END(common_btf_ids) static const struct btf_kfunc_id_set common_kfunc_set = { diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index d63cc0fb0a9c..f07bec227fef 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -8153,6 +8153,7 @@ enum special_kfunc_type { KF_bpf_list_pop_front, KF_bpf_list_pop_back, KF_bpf_cast_to_kern_ctx, + KF_bpf_rdonly_cast, }; BTF_SET_START(special_kfunc_set) @@ -8163,6 +8164,7 @@ BTF_ID(func, bpf_list_push_back) BTF_ID(func, bpf_list_pop_front) BTF_ID(func, bpf_list_pop_back) BTF_ID(func, bpf_cast_to_kern_ctx) +BTF_ID(func, bpf_rdonly_cast) BTF_SET_END(special_kfunc_set) BTF_ID_LIST(special_kfunc_list) @@ -8173,6 +8175,7 @@ BTF_ID(func, bpf_list_push_back) BTF_ID(func, bpf_list_pop_front) BTF_ID(func, bpf_list_pop_back) BTF_ID(func, bpf_cast_to_kern_ctx) +BTF_ID(func, bpf_rdonly_cast) static enum kfunc_ptr_arg_type get_kfunc_ptr_arg_type(struct bpf_verifier_env *env, @@ -8809,6 +8812,7 @@ static int check_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn, u32 i, nargs, func_id, ptr_type_id; int err, insn_idx = *insn_idx_p; const struct btf_param *args; + const struct btf_type *ret_t; struct btf *desc_btf; u32 *kfunc_flags; @@ -8888,7 +8892,6 @@ static int check_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn, if (meta.btf == btf_vmlinux && btf_id_set_contains(&special_kfunc_set, meta.func_id)) { if (meta.func_id == special_kfunc_list[KF_bpf_obj_new_impl]) { - const struct btf_type *ret_t; struct btf *ret_btf; u32 ret_btf_id; @@ -8941,6 +8944,18 @@ static int check_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn, regs[BPF_REG_0].type = PTR_TO_BTF_ID | PTR_TRUSTED; regs[BPF_REG_0].btf = desc_btf; regs[BPF_REG_0].btf_id = meta.ret_btf_id; + } else if (meta.func_id == special_kfunc_list[KF_bpf_rdonly_cast]) { + ret_t = btf_type_by_id(desc_btf, meta.arg_constant.value); + if (!ret_t || !btf_type_is_struct(ret_t)) { + verbose(env, + "kfunc bpf_rdonly_cast type ID argument must be of a struct\n"); + return -EINVAL; + } + + mark_reg_known_zero(env, regs, BPF_REG_0); + regs[BPF_REG_0].type = PTR_TO_BTF_ID | PTR_UNTRUSTED; + regs[BPF_REG_0].btf = desc_btf; + regs[BPF_REG_0].btf_id = meta.arg_constant.value; } else { verbose(env, "kernel function %s unhandled dynamic return type\n", meta.func_name); @@ -15194,7 +15209,8 @@ static int fixup_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn, insn_buf[1] = addr[1]; insn_buf[2] = *insn; *cnt = 3; - } else if (desc->func_id == special_kfunc_list[KF_bpf_cast_to_kern_ctx]) { + } else if (desc->func_id == special_kfunc_list[KF_bpf_cast_to_kern_ctx] || + desc->func_id == special_kfunc_list[KF_bpf_rdonly_cast]) { insn_buf[0] = BPF_MOV64_REG(BPF_REG_0, BPF_REG_1); *cnt = 1; } From 58d84bee58465cc9f6a63ca3931240419497f917 Mon Sep 17 00:00:00 2001 From: Yonghong Song Date: Sun, 20 Nov 2022 11:54:42 -0800 Subject: [PATCH 1093/2002] bpf: Add type cast unit tests Three tests are added. One is from John Fastabend ({1]) which tests tracing style access for xdp program from the kernel ctx. Another is a tc test to test both kernel ctx tracing style access and explicit non-ctx type cast. The third one is for negative tests including two tests, a tp_bpf test where the bpf_rdonly_cast() returns a untrusted ptr which cannot be used as helper argument, and a tracepoint test where the kernel ctx is a u64. Also added the test to DENYLIST.s390x since s390 does not currently support calling kernel functions in JIT mode. [1] https://lore.kernel.org/bpf/20221109215242.1279993-1-john.fastabend@gmail.com/ Signed-off-by: Yonghong Song Link: https://lore.kernel.org/r/20221120195442.3114844-1-yhs@fb.com Signed-off-by: Alexei Starovoitov --- tools/testing/selftests/bpf/DENYLIST.s390x | 1 + .../selftests/bpf/prog_tests/type_cast.c | 114 ++++++++++++++++++ tools/testing/selftests/bpf/progs/type_cast.c | 83 +++++++++++++ 3 files changed, 198 insertions(+) create mode 100644 tools/testing/selftests/bpf/prog_tests/type_cast.c create mode 100644 tools/testing/selftests/bpf/progs/type_cast.c diff --git a/tools/testing/selftests/bpf/DENYLIST.s390x b/tools/testing/selftests/bpf/DENYLIST.s390x index f70a677b38e5..12cf2159975e 100644 --- a/tools/testing/selftests/bpf/DENYLIST.s390x +++ b/tools/testing/selftests/bpf/DENYLIST.s390x @@ -71,6 +71,7 @@ trace_printk # trace_printk__load unexpected error: trace_vprintk # trace_vprintk__open_and_load unexpected error: -9 (?) tracing_struct # failed to auto-attach: -524 (trampoline) trampoline_count # prog 'prog1': failed to attach: ERROR: strerror_r(-524)=22 (trampoline) +type_cast # JIT does not support calling kernel function unpriv_bpf_disabled # fentry user_ringbuf # failed to find kernel BTF type ID of '__s390x_sys_prctl': -3 (?) verif_stats # trace_vprintk__open_and_load unexpected error: -9 (?) diff --git a/tools/testing/selftests/bpf/prog_tests/type_cast.c b/tools/testing/selftests/bpf/prog_tests/type_cast.c new file mode 100644 index 000000000000..9317d5fa2635 --- /dev/null +++ b/tools/testing/selftests/bpf/prog_tests/type_cast.c @@ -0,0 +1,114 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2022 Meta Platforms, Inc. and affiliates. */ +#include +#include +#include "type_cast.skel.h" + +static void test_xdp(void) +{ + struct type_cast *skel; + int err, prog_fd; + char buf[128]; + + LIBBPF_OPTS(bpf_test_run_opts, topts, + .data_in = &pkt_v4, + .data_size_in = sizeof(pkt_v4), + .data_out = buf, + .data_size_out = sizeof(buf), + .repeat = 1, + ); + + skel = type_cast__open(); + if (!ASSERT_OK_PTR(skel, "skel_open")) + return; + + bpf_program__set_autoload(skel->progs.md_xdp, true); + err = type_cast__load(skel); + if (!ASSERT_OK(err, "skel_load")) + goto out; + + prog_fd = bpf_program__fd(skel->progs.md_xdp); + err = bpf_prog_test_run_opts(prog_fd, &topts); + ASSERT_OK(err, "test_run"); + ASSERT_EQ(topts.retval, XDP_PASS, "xdp test_run retval"); + + ASSERT_EQ(skel->bss->ifindex, 1, "xdp_md ifindex"); + ASSERT_EQ(skel->bss->ifindex, skel->bss->ingress_ifindex, "xdp_md ingress_ifindex"); + ASSERT_STREQ(skel->bss->name, "lo", "xdp_md name"); + ASSERT_NEQ(skel->bss->inum, 0, "xdp_md inum"); + +out: + type_cast__destroy(skel); +} + +static void test_tc(void) +{ + struct type_cast *skel; + int err, prog_fd; + + LIBBPF_OPTS(bpf_test_run_opts, topts, + .data_in = &pkt_v4, + .data_size_in = sizeof(pkt_v4), + .repeat = 1, + ); + + skel = type_cast__open(); + if (!ASSERT_OK_PTR(skel, "skel_open")) + return; + + bpf_program__set_autoload(skel->progs.md_skb, true); + err = type_cast__load(skel); + if (!ASSERT_OK(err, "skel_load")) + goto out; + + prog_fd = bpf_program__fd(skel->progs.md_skb); + err = bpf_prog_test_run_opts(prog_fd, &topts); + ASSERT_OK(err, "test_run"); + ASSERT_EQ(topts.retval, 0, "tc test_run retval"); + + ASSERT_EQ(skel->bss->meta_len, 0, "skb meta_len"); + ASSERT_EQ(skel->bss->frag0_len, 0, "skb frag0_len"); + ASSERT_NEQ(skel->bss->kskb_len, 0, "skb len"); + ASSERT_NEQ(skel->bss->kskb2_len, 0, "skb2 len"); + ASSERT_EQ(skel->bss->kskb_len, skel->bss->kskb2_len, "skb len compare"); + +out: + type_cast__destroy(skel); +} + +static const char * const negative_tests[] = { + "untrusted_ptr", + "kctx_u64", +}; + +static void test_negative(void) +{ + struct bpf_program *prog; + struct type_cast *skel; + int i, err; + + for (i = 0; i < ARRAY_SIZE(negative_tests); i++) { + skel = type_cast__open(); + if (!ASSERT_OK_PTR(skel, "skel_open")) + return; + + prog = bpf_object__find_program_by_name(skel->obj, negative_tests[i]); + if (!ASSERT_OK_PTR(prog, "bpf_object__find_program_by_name")) + goto out; + bpf_program__set_autoload(prog, true); + err = type_cast__load(skel); + ASSERT_ERR(err, "skel_load"); +out: + type_cast__destroy(skel); + } +} + +void test_type_cast(void) +{ + if (test__start_subtest("xdp")) + test_xdp(); + if (test__start_subtest("tc")) + test_tc(); + if (test__start_subtest("negative")) + test_negative(); +} diff --git a/tools/testing/selftests/bpf/progs/type_cast.c b/tools/testing/selftests/bpf/progs/type_cast.c new file mode 100644 index 000000000000..eb78e6f03129 --- /dev/null +++ b/tools/testing/selftests/bpf/progs/type_cast.c @@ -0,0 +1,83 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2022 Meta Platforms, Inc. and affiliates. */ +#include "vmlinux.h" +#include +#include +#include + +struct { + __uint(type, BPF_MAP_TYPE_TASK_STORAGE); + __uint(map_flags, BPF_F_NO_PREALLOC); + __type(key, int); + __type(value, long); +} enter_id SEC(".maps"); + +#define IFNAMSIZ 16 + +int ifindex, ingress_ifindex; +char name[IFNAMSIZ]; +unsigned int inum; +unsigned int meta_len, frag0_len, kskb_len, kskb2_len; + +void *bpf_cast_to_kern_ctx(void *) __ksym; +void *bpf_rdonly_cast(void *, __u32) __ksym; + +SEC("?xdp") +int md_xdp(struct xdp_md *ctx) +{ + struct xdp_buff *kctx = bpf_cast_to_kern_ctx(ctx); + struct net_device *dev; + + dev = kctx->rxq->dev; + ifindex = dev->ifindex; + inum = dev->nd_net.net->ns.inum; + __builtin_memcpy(name, dev->name, IFNAMSIZ); + ingress_ifindex = ctx->ingress_ifindex; + return XDP_PASS; +} + +SEC("?tc") +int md_skb(struct __sk_buff *skb) +{ + struct sk_buff *kskb = bpf_cast_to_kern_ctx(skb); + struct skb_shared_info *shared_info; + struct sk_buff *kskb2; + + kskb_len = kskb->len; + + /* Simulate the following kernel macro: + * #define skb_shinfo(SKB) ((struct skb_shared_info *)(skb_end_pointer(SKB))) + */ + shared_info = bpf_rdonly_cast(kskb->head + kskb->end, + bpf_core_type_id_kernel(struct skb_shared_info)); + meta_len = shared_info->meta_len; + frag0_len = shared_info->frag_list->len; + + /* kskb2 should be equal to kskb */ + kskb2 = bpf_rdonly_cast(kskb, bpf_core_type_id_kernel(struct sk_buff)); + kskb2_len = kskb2->len; + return 0; +} + +SEC("?tp_btf/sys_enter") +int BPF_PROG(untrusted_ptr, struct pt_regs *regs, long id) +{ + struct task_struct *task, *task_dup; + long *ptr; + + task = bpf_get_current_task_btf(); + task_dup = bpf_rdonly_cast(task, bpf_core_type_id_kernel(struct task_struct)); + (void)bpf_task_storage_get(&enter_id, task_dup, 0, 0); + return 0; +} + +SEC("?tracepoint/syscalls/sys_enter_nanosleep") +int kctx_u64(void *ctx) +{ + u64 *kctx = bpf_rdonly_cast(ctx, bpf_core_type_id_kernel(u64)); + + (void)kctx; + return 0; +} + +char _license[] SEC("license") = "GPL"; From 9b8107553424fd87955fed257a807672c2097297 Mon Sep 17 00:00:00 2001 From: Sahid Orentino Ferdjaoui Date: Sun, 20 Nov 2022 11:25:55 +0000 Subject: [PATCH 1094/2002] bpftool: remove support of --legacy option for bpftool Following: commit bd054102a8c7 ("libbpf: enforce strict libbpf 1.0 behaviors") commit 93b8952d223a ("libbpf: deprecate legacy BPF map definitions") The --legacy option is no longer relevant as libbpf no longer supports it. libbpf_set_strict_mode() is a no-op operation. Signed-off-by: Sahid Orentino Ferdjaoui Acked-by: Yonghong Song Reviewed-by: Quentin Monnet Link: https://lore.kernel.org/r/20221120112515.38165-2-sahid.ferdjaoui@industrialdiscipline.com Signed-off-by: Alexei Starovoitov --- .../bpf/bpftool/Documentation/common_options.rst | 9 --------- tools/bpf/bpftool/Documentation/substitutions.rst | 2 +- tools/bpf/bpftool/bash-completion/bpftool | 2 +- tools/bpf/bpftool/main.c | 15 --------------- tools/bpf/bpftool/main.h | 3 +-- tools/bpf/bpftool/prog.c | 5 ----- .../selftests/bpf/test_bpftool_synctypes.py | 6 +++--- 7 files changed, 6 insertions(+), 36 deletions(-) diff --git a/tools/bpf/bpftool/Documentation/common_options.rst b/tools/bpf/bpftool/Documentation/common_options.rst index 05350a1aadf9..30df7a707f02 100644 --- a/tools/bpf/bpftool/Documentation/common_options.rst +++ b/tools/bpf/bpftool/Documentation/common_options.rst @@ -23,12 +23,3 @@ Print all logs available, even debug-level information. This includes logs from libbpf as well as from the verifier, when attempting to load programs. - --l, --legacy - Use legacy libbpf mode which has more relaxed BPF program - requirements. By default, bpftool has more strict requirements - about section names, changes pinning logic and doesn't support - some of the older non-BTF map declarations. - - See https://github.com/libbpf/libbpf/wiki/Libbpf:-the-road-to-v1.0 - for details. diff --git a/tools/bpf/bpftool/Documentation/substitutions.rst b/tools/bpf/bpftool/Documentation/substitutions.rst index ccf1ffa0686c..827e3ffb1766 100644 --- a/tools/bpf/bpftool/Documentation/substitutions.rst +++ b/tools/bpf/bpftool/Documentation/substitutions.rst @@ -1,3 +1,3 @@ .. SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) -.. |COMMON_OPTIONS| replace:: { **-j** | **--json** } [{ **-p** | **--pretty** }] | { **-d** | **--debug** } | { **-l** | **--legacy** } +.. |COMMON_OPTIONS| replace:: { **-j** | **--json** } [{ **-p** | **--pretty** }] | { **-d** | **--debug** } diff --git a/tools/bpf/bpftool/bash-completion/bpftool b/tools/bpf/bpftool/bash-completion/bpftool index 2957b42cab67..35f26f7c1124 100644 --- a/tools/bpf/bpftool/bash-completion/bpftool +++ b/tools/bpf/bpftool/bash-completion/bpftool @@ -261,7 +261,7 @@ _bpftool() # Deal with options if [[ ${words[cword]} == -* ]]; then local c='--version --json --pretty --bpffs --mapcompat --debug \ - --use-loader --base-btf --legacy' + --use-loader --base-btf' COMPREPLY=( $( compgen -W "$c" -- "$cur" ) ) return 0 fi diff --git a/tools/bpf/bpftool/main.c b/tools/bpf/bpftool/main.c index 337ab7977ea4..c53c553adcac 100644 --- a/tools/bpf/bpftool/main.c +++ b/tools/bpf/bpftool/main.c @@ -31,7 +31,6 @@ bool block_mount; bool verifier_logs; bool relaxed_maps; bool use_loader; -bool legacy_libbpf; struct btf *base_btf; struct hashmap *refs_table; @@ -160,7 +159,6 @@ static int do_version(int argc, char **argv) jsonw_start_object(json_wtr); /* features */ jsonw_bool_field(json_wtr, "libbfd", has_libbfd); jsonw_bool_field(json_wtr, "llvm", has_llvm); - jsonw_bool_field(json_wtr, "libbpf_strict", !legacy_libbpf); jsonw_bool_field(json_wtr, "skeletons", has_skeletons); jsonw_bool_field(json_wtr, "bootstrap", bootstrap); jsonw_end_object(json_wtr); /* features */ @@ -179,7 +177,6 @@ static int do_version(int argc, char **argv) printf("features:"); print_feature("libbfd", has_libbfd, &nb_features); print_feature("llvm", has_llvm, &nb_features); - print_feature("libbpf_strict", !legacy_libbpf, &nb_features); print_feature("skeletons", has_skeletons, &nb_features); print_feature("bootstrap", bootstrap, &nb_features); printf("\n"); @@ -451,7 +448,6 @@ int main(int argc, char **argv) { "debug", no_argument, NULL, 'd' }, { "use-loader", no_argument, NULL, 'L' }, { "base-btf", required_argument, NULL, 'B' }, - { "legacy", no_argument, NULL, 'l' }, { 0 } }; bool version_requested = false; @@ -524,9 +520,6 @@ int main(int argc, char **argv) case 'L': use_loader = true; break; - case 'l': - legacy_libbpf = true; - break; default: p_err("unrecognized option '%s'", argv[optind - 1]); if (json_output) @@ -536,14 +529,6 @@ int main(int argc, char **argv) } } - if (!legacy_libbpf) { - /* Allow legacy map definitions for skeleton generation. - * It will still be rejected if users use LIBBPF_STRICT_ALL - * mode for loading generated skeleton. - */ - libbpf_set_strict_mode(LIBBPF_STRICT_ALL & ~LIBBPF_STRICT_MAP_DEFINITIONS); - } - argc -= optind; argv += optind; if (argc < 0) diff --git a/tools/bpf/bpftool/main.h b/tools/bpf/bpftool/main.h index d4e8a1aef787..a84224b6a604 100644 --- a/tools/bpf/bpftool/main.h +++ b/tools/bpf/bpftool/main.h @@ -57,7 +57,7 @@ static inline void *u64_to_ptr(__u64 ptr) #define HELP_SPEC_PROGRAM \ "PROG := { id PROG_ID | pinned FILE | tag PROG_TAG | name PROG_NAME }" #define HELP_SPEC_OPTIONS \ - "OPTIONS := { {-j|--json} [{-p|--pretty}] | {-d|--debug} | {-l|--legacy}" + "OPTIONS := { {-j|--json} [{-p|--pretty}] | {-d|--debug}" #define HELP_SPEC_MAP \ "MAP := { id MAP_ID | pinned FILE | name MAP_NAME }" #define HELP_SPEC_LINK \ @@ -82,7 +82,6 @@ extern bool block_mount; extern bool verifier_logs; extern bool relaxed_maps; extern bool use_loader; -extern bool legacy_libbpf; extern struct btf *base_btf; extern struct hashmap *refs_table; diff --git a/tools/bpf/bpftool/prog.c b/tools/bpf/bpftool/prog.c index 9d32ffb9f22e..2266958f203f 100644 --- a/tools/bpf/bpftool/prog.c +++ b/tools/bpf/bpftool/prog.c @@ -1802,11 +1802,6 @@ err_unpin: else bpf_object__unpin_programs(obj, pinfile); err_close_obj: - if (!legacy_libbpf) { - p_info("Warning: bpftool is now running in libbpf strict mode and has more stringent requirements about BPF programs.\n" - "If it used to work for this object file but now doesn't, see --legacy option for more details.\n"); - } - bpf_object__close(obj); err_free_reuse_maps: for (i = 0; i < old_map_fds; i++) diff --git a/tools/testing/selftests/bpf/test_bpftool_synctypes.py b/tools/testing/selftests/bpf/test_bpftool_synctypes.py index 9fe4c9336c6f..0cfece7ff4f8 100755 --- a/tools/testing/selftests/bpf/test_bpftool_synctypes.py +++ b/tools/testing/selftests/bpf/test_bpftool_synctypes.py @@ -309,11 +309,11 @@ class MainHeaderFileExtractor(SourceFileExtractor): commands), which looks to the lists of options in other source files but has different start and end markers: - "OPTIONS := { {-j|--json} [{-p|--pretty}] | {-d|--debug} | {-l|--legacy}" + "OPTIONS := { {-j|--json} [{-p|--pretty}] | {-d|--debug}" Return a set containing all options, such as: - {'-p', '-d', '--legacy', '--pretty', '--debug', '--json', '-l', '-j'} + {'-p', '-d', '--pretty', '--debug', '--json', '-j'} """ start_marker = re.compile(f'"OPTIONS :=') pattern = re.compile('([\w-]+) ?(?:\||}[ }\]"])') @@ -336,7 +336,7 @@ class ManSubstitutionsExtractor(SourceFileExtractor): Return a set containing all options, such as: - {'-p', '-d', '--legacy', '--pretty', '--debug', '--json', '-l', '-j'} + {'-p', '-d', '--pretty', '--debug', '--json', '-j'} """ start_marker = re.compile('\|COMMON_OPTIONS\| replace:: {') pattern = re.compile('\*\*([\w/-]+)\*\*') From 989f285159b84831c54d277112dcf803da51e722 Mon Sep 17 00:00:00 2001 From: Sahid Orentino Ferdjaoui Date: Sun, 20 Nov 2022 11:26:07 +0000 Subject: [PATCH 1095/2002] bpftool: replace return value PTR_ERR(NULL) with 0 There is no reasons to keep PTR_ERR() when kern_btf=NULL, let's just return 0. This also cleans this part of code from using libbpf_get_error(). Signed-off-by: Sahid Orentino Ferdjaoui Acked-by: Yonghong Song Suggested-by: Quentin Monnet Link: https://lore.kernel.org/r/20221120112515.38165-3-sahid.ferdjaoui@industrialdiscipline.com Signed-off-by: Alexei Starovoitov --- tools/bpf/bpftool/struct_ops.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/tools/bpf/bpftool/struct_ops.c b/tools/bpf/bpftool/struct_ops.c index e08a6ff2866c..d3cfdfef9b58 100644 --- a/tools/bpf/bpftool/struct_ops.c +++ b/tools/bpf/bpftool/struct_ops.c @@ -63,10 +63,8 @@ static __s32 get_map_info_type_id(void) return map_info_type_id; kern_btf = get_btf_vmlinux(); - if (libbpf_get_error(kern_btf)) { - map_info_type_id = PTR_ERR(kern_btf); - return map_info_type_id; - } + if (!kern_btf) + return 0; map_info_type_id = btf__find_by_name_kind(kern_btf, "bpf_map_info", BTF_KIND_STRUCT); From d2973ffd25c2295055349212ca26c18929e5e9f5 Mon Sep 17 00:00:00 2001 From: Sahid Orentino Ferdjaoui Date: Sun, 20 Nov 2022 11:26:18 +0000 Subject: [PATCH 1096/2002] bpftool: fix error message when function can't register struct_ops It is expected that errno be passed to strerror(). This also cleans this part of code from using libbpf_get_error(). Signed-off-by: Sahid Orentino Ferdjaoui Acked-by: Yonghong Song Suggested-by: Quentin Monnet Reviewed-by: Quentin Monnet Link: https://lore.kernel.org/r/20221120112515.38165-4-sahid.ferdjaoui@industrialdiscipline.com Signed-off-by: Alexei Starovoitov --- tools/bpf/bpftool/struct_ops.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tools/bpf/bpftool/struct_ops.c b/tools/bpf/bpftool/struct_ops.c index d3cfdfef9b58..ae9ad85f5cda 100644 --- a/tools/bpf/bpftool/struct_ops.c +++ b/tools/bpf/bpftool/struct_ops.c @@ -511,10 +511,9 @@ static int do_register(int argc, char **argv) continue; link = bpf_map__attach_struct_ops(map); - if (libbpf_get_error(link)) { + if (!link) { p_err("can't register struct_ops %s: %s", - bpf_map__name(map), - strerror(-PTR_ERR(link))); + bpf_map__name(map), strerror(errno)); nr_errs++; continue; } From d1313e01271d2d8f33d6c82f1afb77e820a3540d Mon Sep 17 00:00:00 2001 From: Sahid Orentino Ferdjaoui Date: Sun, 20 Nov 2022 11:26:32 +0000 Subject: [PATCH 1097/2002] bpftool: clean-up usage of libbpf_get_error() bpftool is now totally compliant with libbpf 1.0 mode and is not expected to be compiled with pre-1.0, let's clean-up the usage of libbpf_get_error(). The changes stay aligned with returned errors always negative. - In tools/bpf/bpftool/btf.c This fixes an uninitialized local variable `err` in function do_dump() because it may now be returned without having been set. - This also removes the checks on NULL pointers before calling btf__free() because that function already does the check. Signed-off-by: Sahid Orentino Ferdjaoui Link: https://lore.kernel.org/r/20221120112515.38165-5-sahid.ferdjaoui@industrialdiscipline.com Signed-off-by: Alexei Starovoitov --- tools/bpf/bpftool/btf.c | 19 ++++++++----------- tools/bpf/bpftool/btf_dumper.c | 2 +- tools/bpf/bpftool/gen.c | 10 ++++------ tools/bpf/bpftool/iter.c | 10 ++++++---- tools/bpf/bpftool/main.c | 7 +++---- tools/bpf/bpftool/map.c | 15 +++++++-------- tools/bpf/bpftool/prog.c | 10 +++++----- tools/bpf/bpftool/struct_ops.c | 11 +++++------ 8 files changed, 39 insertions(+), 45 deletions(-) diff --git a/tools/bpf/bpftool/btf.c b/tools/bpf/bpftool/btf.c index b87e4a7fd689..352290ba7b29 100644 --- a/tools/bpf/bpftool/btf.c +++ b/tools/bpf/bpftool/btf.c @@ -467,9 +467,8 @@ static int dump_btf_c(const struct btf *btf, int err = 0, i; d = btf_dump__new(btf, btf_dump_printf, NULL, NULL); - err = libbpf_get_error(d); - if (err) - return err; + if (!d) + return -errno; printf("#ifndef __VMLINUX_H__\n"); printf("#define __VMLINUX_H__\n"); @@ -512,11 +511,9 @@ static struct btf *get_vmlinux_btf_from_sysfs(void) struct btf *base; base = btf__parse(sysfs_vmlinux, NULL); - if (libbpf_get_error(base)) { - p_err("failed to parse vmlinux BTF at '%s': %ld\n", - sysfs_vmlinux, libbpf_get_error(base)); - base = NULL; - } + if (!base) + p_err("failed to parse vmlinux BTF at '%s': %d\n", + sysfs_vmlinux, -errno); return base; } @@ -559,7 +556,7 @@ static int do_dump(int argc, char **argv) __u32 btf_id = -1; const char *src; int fd = -1; - int err; + int err = 0; if (!REQ_ARGS(2)) { usage(); @@ -634,8 +631,8 @@ static int do_dump(int argc, char **argv) base = get_vmlinux_btf_from_sysfs(); btf = btf__parse_split(*argv, base ?: base_btf); - err = libbpf_get_error(btf); if (!btf) { + err = -errno; p_err("failed to load BTF from %s: %s", *argv, strerror(errno)); goto done; @@ -681,8 +678,8 @@ static int do_dump(int argc, char **argv) } btf = btf__load_from_kernel_by_id_split(btf_id, base_btf); - err = libbpf_get_error(btf); if (!btf) { + err = -errno; p_err("get btf by id (%u): %s", btf_id, strerror(errno)); goto done; } diff --git a/tools/bpf/bpftool/btf_dumper.c b/tools/bpf/bpftool/btf_dumper.c index 19924b6ce796..eda71fdfe95a 100644 --- a/tools/bpf/bpftool/btf_dumper.c +++ b/tools/bpf/bpftool/btf_dumper.c @@ -75,7 +75,7 @@ static int dump_prog_id_as_func_ptr(const struct btf_dumper *d, goto print; prog_btf = btf__load_from_kernel_by_id(info.btf_id); - if (libbpf_get_error(prog_btf)) + if (!prog_btf) goto print; func_type = btf__type_by_id(prog_btf, finfo.type_id); if (!func_type || !btf_is_func(func_type)) diff --git a/tools/bpf/bpftool/gen.c b/tools/bpf/bpftool/gen.c index 01bb8d8f5568..2883660d6b67 100644 --- a/tools/bpf/bpftool/gen.c +++ b/tools/bpf/bpftool/gen.c @@ -252,9 +252,8 @@ static int codegen_datasecs(struct bpf_object *obj, const char *obj_name) int err = 0; d = btf_dump__new(btf, codegen_btf_dump_printf, NULL, NULL); - err = libbpf_get_error(d); - if (err) - return err; + if (!d) + return -errno; bpf_object__for_each_map(map, obj) { /* only generate definitions for memory-mapped internal maps */ @@ -976,13 +975,12 @@ static int do_skeleton(int argc, char **argv) /* log_level1 + log_level2 + stats, but not stable UAPI */ opts.kernel_log_level = 1 + 2 + 4; obj = bpf_object__open_mem(obj_data, file_sz, &opts); - err = libbpf_get_error(obj); - if (err) { + if (!obj) { char err_buf[256]; + err = -errno; libbpf_strerror(err, err_buf, sizeof(err_buf)); p_err("failed to open BPF object file: %s", err_buf); - obj = NULL; goto out; } diff --git a/tools/bpf/bpftool/iter.c b/tools/bpf/bpftool/iter.c index a3e6b167153d..9a1d2365a297 100644 --- a/tools/bpf/bpftool/iter.c +++ b/tools/bpf/bpftool/iter.c @@ -4,6 +4,7 @@ #ifndef _GNU_SOURCE #define _GNU_SOURCE #endif +#include #include #include #include @@ -48,8 +49,8 @@ static int do_pin(int argc, char **argv) } obj = bpf_object__open(objfile); - err = libbpf_get_error(obj); - if (err) { + if (!obj) { + err = -errno; p_err("can't open objfile %s", objfile); goto close_map_fd; } @@ -62,13 +63,14 @@ static int do_pin(int argc, char **argv) prog = bpf_object__next_program(obj, NULL); if (!prog) { + err = -errno; p_err("can't find bpf program in objfile %s", objfile); goto close_obj; } link = bpf_program__attach_iter(prog, &iter_opts); - err = libbpf_get_error(link); - if (err) { + if (!link) { + err = -errno; p_err("attach_iter failed for program %s", bpf_program__name(prog)); goto close_obj; diff --git a/tools/bpf/bpftool/main.c b/tools/bpf/bpftool/main.c index c53c553adcac..08d0ac543c67 100644 --- a/tools/bpf/bpftool/main.c +++ b/tools/bpf/bpftool/main.c @@ -510,10 +510,9 @@ int main(int argc, char **argv) break; case 'B': base_btf = btf__parse(optarg, NULL); - if (libbpf_get_error(base_btf)) { - p_err("failed to parse base BTF at '%s': %ld\n", - optarg, libbpf_get_error(base_btf)); - base_btf = NULL; + if (!base_btf) { + p_err("failed to parse base BTF at '%s': %d\n", + optarg, -errno); return -1; } break; diff --git a/tools/bpf/bpftool/map.c b/tools/bpf/bpftool/map.c index d884070a2314..eb362bd3d2c9 100644 --- a/tools/bpf/bpftool/map.c +++ b/tools/bpf/bpftool/map.c @@ -786,18 +786,18 @@ static int get_map_kv_btf(const struct bpf_map_info *info, struct btf **btf) if (info->btf_vmlinux_value_type_id) { if (!btf_vmlinux) { btf_vmlinux = libbpf_find_kernel_btf(); - err = libbpf_get_error(btf_vmlinux); - if (err) { + if (!btf_vmlinux) { p_err("failed to get kernel btf"); - return err; + return -errno; } } *btf = btf_vmlinux; } else if (info->btf_value_type_id) { *btf = btf__load_from_kernel_by_id(info->btf_id); - err = libbpf_get_error(*btf); - if (err) + if (!*btf) { + err = -errno; p_err("failed to get btf"); + } } else { *btf = NULL; } @@ -807,14 +807,13 @@ static int get_map_kv_btf(const struct bpf_map_info *info, struct btf **btf) static void free_map_kv_btf(struct btf *btf) { - if (!libbpf_get_error(btf) && btf != btf_vmlinux) + if (btf != btf_vmlinux) btf__free(btf); } static void free_btf_vmlinux(void) { - if (!libbpf_get_error(btf_vmlinux)) - btf__free(btf_vmlinux); + btf__free(btf_vmlinux); } static int diff --git a/tools/bpf/bpftool/prog.c b/tools/bpf/bpftool/prog.c index 2266958f203f..cfc9fdc1e863 100644 --- a/tools/bpf/bpftool/prog.c +++ b/tools/bpf/bpftool/prog.c @@ -322,7 +322,7 @@ static void show_prog_metadata(int fd, __u32 num_maps) return; btf = btf__load_from_kernel_by_id(map_info.btf_id); - if (libbpf_get_error(btf)) + if (!btf) goto out_free; t_datasec = btf__type_by_id(btf, map_info.btf_value_type_id); @@ -726,7 +726,7 @@ prog_dump(struct bpf_prog_info *info, enum dump_mode mode, if (info->btf_id) { btf = btf__load_from_kernel_by_id(info->btf_id); - if (libbpf_get_error(btf)) { + if (!btf) { p_err("failed to get btf"); return -1; } @@ -1663,7 +1663,7 @@ static int load_with_options(int argc, char **argv, bool first_prog_only) open_opts.kernel_log_level = 1 + 2 + 4; obj = bpf_object__open_file(file, &open_opts); - if (libbpf_get_error(obj)) { + if (!obj) { p_err("failed to open object file"); goto err_free_reuse_maps; } @@ -1882,7 +1882,7 @@ static int do_loader(int argc, char **argv) open_opts.kernel_log_level = 1 + 2 + 4; obj = bpf_object__open_file(file, &open_opts); - if (libbpf_get_error(obj)) { + if (!obj) { p_err("failed to open object file"); goto err_close_obj; } @@ -2199,7 +2199,7 @@ static char *profile_target_name(int tgt_fd) } btf = btf__load_from_kernel_by_id(info.btf_id); - if (libbpf_get_error(btf)) { + if (!btf) { p_err("failed to load btf for prog FD %d", tgt_fd); goto out; } diff --git a/tools/bpf/bpftool/struct_ops.c b/tools/bpf/bpftool/struct_ops.c index ae9ad85f5cda..903b80ff4e9a 100644 --- a/tools/bpf/bpftool/struct_ops.c +++ b/tools/bpf/bpftool/struct_ops.c @@ -32,7 +32,7 @@ static const struct btf *get_btf_vmlinux(void) return btf_vmlinux; btf_vmlinux = libbpf_find_kernel_btf(); - if (libbpf_get_error(btf_vmlinux)) + if (!btf_vmlinux) p_err("struct_ops requires kernel CONFIG_DEBUG_INFO_BTF=y"); return btf_vmlinux; @@ -45,7 +45,7 @@ static const char *get_kern_struct_ops_name(const struct bpf_map_info *info) const char *st_ops_name; kern_btf = get_btf_vmlinux(); - if (libbpf_get_error(kern_btf)) + if (!kern_btf) return ""; t = btf__type_by_id(kern_btf, info->btf_vmlinux_value_type_id); @@ -413,7 +413,7 @@ static int do_dump(int argc, char **argv) } kern_btf = get_btf_vmlinux(); - if (libbpf_get_error(kern_btf)) + if (!kern_btf) return -1; if (!json_output) { @@ -496,7 +496,7 @@ static int do_register(int argc, char **argv) open_opts.kernel_log_level = 1 + 2 + 4; obj = bpf_object__open_file(file, &open_opts); - if (libbpf_get_error(obj)) + if (!obj) return -1; set_max_rlimit(); @@ -590,8 +590,7 @@ int do_struct_ops(int argc, char **argv) err = cmd_select(cmds, argc, argv, do_help); - if (!libbpf_get_error(btf_vmlinux)) - btf__free(btf_vmlinux); + btf__free(btf_vmlinux); return err; } From 52df1a8aabadeba1e4c2fe157784637ddec76301 Mon Sep 17 00:00:00 2001 From: Sahid Orentino Ferdjaoui Date: Sun, 20 Nov 2022 11:26:43 +0000 Subject: [PATCH 1098/2002] bpftool: remove function free_btf_vmlinux() The function contains a single btf__free() call which can be inlined. Credits to Yonghong Song. Signed-off-by: Sahid Orentino Ferdjaoui Acked-by: Yonghong Song Suggested-by: Yonghong Song Reviewed-by: Quentin Monnet Link: https://lore.kernel.org/r/20221120112515.38165-6-sahid.ferdjaoui@industrialdiscipline.com Signed-off-by: Alexei Starovoitov --- tools/bpf/bpftool/map.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/tools/bpf/bpftool/map.c b/tools/bpf/bpftool/map.c index eb362bd3d2c9..88911d3aa2d9 100644 --- a/tools/bpf/bpftool/map.c +++ b/tools/bpf/bpftool/map.c @@ -811,11 +811,6 @@ static void free_map_kv_btf(struct btf *btf) btf__free(btf); } -static void free_btf_vmlinux(void) -{ - btf__free(btf_vmlinux); -} - static int map_dump(int fd, struct bpf_map_info *info, json_writer_t *wtr, bool show_header) @@ -952,7 +947,7 @@ exit_close: close(fds[i]); exit_free: free(fds); - free_btf_vmlinux(); + btf__free(btf_vmlinux); return err; } From 8719a1c30d3a1b4f347afca8e78be201d66d5c49 Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Wed, 16 Nov 2022 10:59:44 -0600 Subject: [PATCH 1099/2002] bna: Avoid clashing function prototypes When built with Control Flow Integrity, function prototypes between caller and function declaration must match. These mismatches are visible at compile time with the new -Wcast-function-type-strict in Clang[1]. Fix a total of 227 warnings like these: drivers/net/ethernet/brocade/bna/bna_enet.c:519:3: warning: cast from 'void (*)(struct bna_ethport *, enum bna_ethport_event)' to 'bfa_fsm_t' (aka 'void (*)(void *, int)') converts to incompatible function type [-Wcast-function-type-strict] bfa_fsm_set_state(ethport, bna_ethport_sm_down); ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The bna state machine code heavily overloads its state machine functions, so these have been separated into their own sets of structs, enums, typedefs, and helper functions. There are almost zero binary code changes, all seem to be related to header file line numbers changing, or the addition of the new stats helper. Important to mention is that while I was manually implementing this changes I was staring at this[2] patch from Kees Cook. Thanks, Kees. :) Link: https://github.com/KSPP/linux/issues/240 [1] https://reviews.llvm.org/D134831 [2] https://lore.kernel.org/linux-hardening/20220929230334.2109344-1-keescook@chromium.org/ Reviewed-by: Kees Cook Signed-off-by: Gustavo A. R. Silva Signed-off-by: David S. Miller --- drivers/net/ethernet/brocade/bna/bfa_cs.h | 60 +++++++++++++------- drivers/net/ethernet/brocade/bna/bfa_ioc.c | 10 ++-- drivers/net/ethernet/brocade/bna/bfa_ioc.h | 8 ++- drivers/net/ethernet/brocade/bna/bfa_msgq.h | 8 ++- drivers/net/ethernet/brocade/bna/bna_enet.c | 6 +- drivers/net/ethernet/brocade/bna/bna_tx_rx.c | 6 +- drivers/net/ethernet/brocade/bna/bna_types.h | 27 +++++++-- 7 files changed, 82 insertions(+), 43 deletions(-) diff --git a/drivers/net/ethernet/brocade/bna/bfa_cs.h b/drivers/net/ethernet/brocade/bna/bfa_cs.h index 8f0ac7b99973..858c92129451 100644 --- a/drivers/net/ethernet/brocade/bna/bfa_cs.h +++ b/drivers/net/ethernet/brocade/bna/bfa_cs.h @@ -18,15 +18,43 @@ /* BFA state machine interfaces */ -typedef void (*bfa_sm_t)(void *sm, int event); - /* For converting from state machine function to state encoding. */ -struct bfa_sm_table { - bfa_sm_t sm; /*!< state machine function */ - int state; /*!< state machine encoding */ - char *name; /*!< state name for display */ -}; -#define BFA_SM(_sm) ((bfa_sm_t)(_sm)) +#define BFA_SM_TABLE(n, s, e, t) \ +struct s; \ +enum e; \ +typedef void (*t)(struct s *, enum e); \ + \ +struct n ## _sm_table_s { \ + t sm; /* state machine function */ \ + int state; /* state machine encoding */ \ + char *name; /* state name for display */ \ +}; \ + \ +static inline int \ +n ## _sm_to_state(struct n ## _sm_table_s *smt, t sm) \ +{ \ + int i = 0; \ + \ + while (smt[i].sm && smt[i].sm != sm) \ + i++; \ + return smt[i].state; \ +} + +BFA_SM_TABLE(iocpf, bfa_iocpf, iocpf_event, bfa_fsm_iocpf_t) +BFA_SM_TABLE(ioc, bfa_ioc, ioc_event, bfa_fsm_ioc_t) +BFA_SM_TABLE(cmdq, bfa_msgq_cmdq, cmdq_event, bfa_fsm_msgq_cmdq_t) +BFA_SM_TABLE(rspq, bfa_msgq_rspq, rspq_event, bfa_fsm_msgq_rspq_t) + +BFA_SM_TABLE(ioceth, bna_ioceth, bna_ioceth_event, bna_fsm_ioceth_t) +BFA_SM_TABLE(enet, bna_enet, bna_enet_event, bna_fsm_enet_t) +BFA_SM_TABLE(ethport, bna_ethport, bna_ethport_event, bna_fsm_ethport_t) +BFA_SM_TABLE(tx, bna_tx, bna_tx_event, bna_fsm_tx_t) +BFA_SM_TABLE(rxf, bna_rxf, bna_rxf_event, bna_fsm_rxf_t) +BFA_SM_TABLE(rx, bna_rx, bna_rx_event, bna_fsm_rx_t) + +#undef BFA_SM_TABLE + +#define BFA_SM(_sm) (_sm) /* State machine with entry actions. */ typedef void (*bfa_fsm_t)(void *fsm, int event); @@ -41,24 +69,12 @@ typedef void (*bfa_fsm_t)(void *fsm, int event); static void oc ## _sm_ ## st ## _entry(otype * fsm) #define bfa_fsm_set_state(_fsm, _state) do { \ - (_fsm)->fsm = (bfa_fsm_t)(_state); \ + (_fsm)->fsm = (_state); \ _state ## _entry(_fsm); \ } while (0) #define bfa_fsm_send_event(_fsm, _event) ((_fsm)->fsm((_fsm), (_event))) -#define bfa_fsm_cmp_state(_fsm, _state) \ - ((_fsm)->fsm == (bfa_fsm_t)(_state)) - -static inline int -bfa_sm_to_state(const struct bfa_sm_table *smt, bfa_sm_t sm) -{ - int i = 0; - - while (smt[i].sm && smt[i].sm != sm) - i++; - return smt[i].state; -} - +#define bfa_fsm_cmp_state(_fsm, _state) ((_fsm)->fsm == (_state)) /* Generic wait counter. */ typedef void (*bfa_wc_resume_t) (void *cbarg); diff --git a/drivers/net/ethernet/brocade/bna/bfa_ioc.c b/drivers/net/ethernet/brocade/bna/bfa_ioc.c index cd933817a0b8..b07522ac3e74 100644 --- a/drivers/net/ethernet/brocade/bna/bfa_ioc.c +++ b/drivers/net/ethernet/brocade/bna/bfa_ioc.c @@ -114,7 +114,7 @@ bfa_fsm_state_decl(bfa_ioc, disabling, struct bfa_ioc, enum ioc_event); bfa_fsm_state_decl(bfa_ioc, disabled, struct bfa_ioc, enum ioc_event); bfa_fsm_state_decl(bfa_ioc, hwfail, struct bfa_ioc, enum ioc_event); -static struct bfa_sm_table ioc_sm_table[] = { +static struct ioc_sm_table_s ioc_sm_table[] = { {BFA_SM(bfa_ioc_sm_uninit), BFA_IOC_UNINIT}, {BFA_SM(bfa_ioc_sm_reset), BFA_IOC_RESET}, {BFA_SM(bfa_ioc_sm_enabling), BFA_IOC_ENABLING}, @@ -183,7 +183,7 @@ bfa_fsm_state_decl(bfa_iocpf, disabling_sync, struct bfa_iocpf, enum iocpf_event); bfa_fsm_state_decl(bfa_iocpf, disabled, struct bfa_iocpf, enum iocpf_event); -static struct bfa_sm_table iocpf_sm_table[] = { +static struct iocpf_sm_table_s iocpf_sm_table[] = { {BFA_SM(bfa_iocpf_sm_reset), BFA_IOCPF_RESET}, {BFA_SM(bfa_iocpf_sm_fwcheck), BFA_IOCPF_FWMISMATCH}, {BFA_SM(bfa_iocpf_sm_mismatch), BFA_IOCPF_FWMISMATCH}, @@ -2860,12 +2860,12 @@ static enum bfa_ioc_state bfa_ioc_get_state(struct bfa_ioc *ioc) { enum bfa_iocpf_state iocpf_st; - enum bfa_ioc_state ioc_st = bfa_sm_to_state(ioc_sm_table, ioc->fsm); + enum bfa_ioc_state ioc_st = ioc_sm_to_state(ioc_sm_table, ioc->fsm); if (ioc_st == BFA_IOC_ENABLING || ioc_st == BFA_IOC_FAIL || ioc_st == BFA_IOC_INITFAIL) { - iocpf_st = bfa_sm_to_state(iocpf_sm_table, ioc->iocpf.fsm); + iocpf_st = iocpf_sm_to_state(iocpf_sm_table, ioc->iocpf.fsm); switch (iocpf_st) { case BFA_IOCPF_SEMWAIT: @@ -2983,7 +2983,7 @@ bfa_nw_iocpf_timeout(struct bfa_ioc *ioc) { enum bfa_iocpf_state iocpf_st; - iocpf_st = bfa_sm_to_state(iocpf_sm_table, ioc->iocpf.fsm); + iocpf_st = iocpf_sm_to_state(iocpf_sm_table, ioc->iocpf.fsm); if (iocpf_st == BFA_IOCPF_HWINIT) bfa_ioc_poll_fwinit(ioc); diff --git a/drivers/net/ethernet/brocade/bna/bfa_ioc.h b/drivers/net/ethernet/brocade/bna/bfa_ioc.h index edd0ed5b5332..f30d06ec4ffe 100644 --- a/drivers/net/ethernet/brocade/bna/bfa_ioc.h +++ b/drivers/net/ethernet/brocade/bna/bfa_ioc.h @@ -147,16 +147,20 @@ struct bfa_ioc_notify { (__notify)->cbarg = (__cbarg); \ } while (0) +enum iocpf_event; + struct bfa_iocpf { - bfa_fsm_t fsm; + void (*fsm)(struct bfa_iocpf *s, enum iocpf_event e); struct bfa_ioc *ioc; bool fw_mismatch_notified; bool auto_recover; u32 poll_time; }; +enum ioc_event; + struct bfa_ioc { - bfa_fsm_t fsm; + void (*fsm)(struct bfa_ioc *s, enum ioc_event e); struct bfa *bfa; struct bfa_pcidev pcidev; struct timer_list ioc_timer; diff --git a/drivers/net/ethernet/brocade/bna/bfa_msgq.h b/drivers/net/ethernet/brocade/bna/bfa_msgq.h index 75343b535798..170a4b4bed96 100644 --- a/drivers/net/ethernet/brocade/bna/bfa_msgq.h +++ b/drivers/net/ethernet/brocade/bna/bfa_msgq.h @@ -55,8 +55,10 @@ enum bfa_msgq_cmdq_flags { BFA_MSGQ_CMDQ_F_DB_UPDATE = 1, }; +enum cmdq_event; + struct bfa_msgq_cmdq { - bfa_fsm_t fsm; + void (*fsm)(struct bfa_msgq_cmdq *s, enum cmdq_event e); enum bfa_msgq_cmdq_flags flags; u16 producer_index; @@ -81,8 +83,10 @@ enum bfa_msgq_rspq_flags { typedef void (*bfa_msgq_mcfunc_t)(void *cbarg, struct bfi_msgq_mhdr *mhdr); +enum rspq_event; + struct bfa_msgq_rspq { - bfa_fsm_t fsm; + void (*fsm)(struct bfa_msgq_rspq *s, enum rspq_event e); enum bfa_msgq_rspq_flags flags; u16 producer_index; diff --git a/drivers/net/ethernet/brocade/bna/bna_enet.c b/drivers/net/ethernet/brocade/bna/bna_enet.c index a2c983f56b00..883de0ac8de4 100644 --- a/drivers/net/ethernet/brocade/bna/bna_enet.c +++ b/drivers/net/ethernet/brocade/bna/bna_enet.c @@ -1257,7 +1257,7 @@ bna_enet_mtu_get(struct bna_enet *enet) void bna_enet_enable(struct bna_enet *enet) { - if (enet->fsm != (bfa_sm_t)bna_enet_sm_stopped) + if (enet->fsm != bna_enet_sm_stopped) return; enet->flags |= BNA_ENET_F_ENABLED; @@ -1751,12 +1751,12 @@ bna_ioceth_uninit(struct bna_ioceth *ioceth) void bna_ioceth_enable(struct bna_ioceth *ioceth) { - if (ioceth->fsm == (bfa_fsm_t)bna_ioceth_sm_ready) { + if (ioceth->fsm == bna_ioceth_sm_ready) { bnad_cb_ioceth_ready(ioceth->bna->bnad); return; } - if (ioceth->fsm == (bfa_fsm_t)bna_ioceth_sm_stopped) + if (ioceth->fsm == bna_ioceth_sm_stopped) bfa_fsm_send_event(ioceth, IOCETH_E_ENABLE); } diff --git a/drivers/net/ethernet/brocade/bna/bna_tx_rx.c b/drivers/net/ethernet/brocade/bna/bna_tx_rx.c index 2623a0da4682..c05dc7a1c4a1 100644 --- a/drivers/net/ethernet/brocade/bna/bna_tx_rx.c +++ b/drivers/net/ethernet/brocade/bna/bna_tx_rx.c @@ -1956,7 +1956,7 @@ static void bna_rx_stop(struct bna_rx *rx) { rx->rx_flags &= ~BNA_RX_F_ENET_STARTED; - if (rx->fsm == (bfa_fsm_t) bna_rx_sm_stopped) + if (rx->fsm == bna_rx_sm_stopped) bna_rx_mod_cb_rx_stopped(&rx->bna->rx_mod, rx); else { rx->stop_cbfn = bna_rx_mod_cb_rx_stopped; @@ -2535,7 +2535,7 @@ bna_rx_destroy(struct bna_rx *rx) void bna_rx_enable(struct bna_rx *rx) { - if (rx->fsm != (bfa_sm_t)bna_rx_sm_stopped) + if (rx->fsm != bna_rx_sm_stopped) return; rx->rx_flags |= BNA_RX_F_ENABLED; @@ -3523,7 +3523,7 @@ bna_tx_destroy(struct bna_tx *tx) void bna_tx_enable(struct bna_tx *tx) { - if (tx->fsm != (bfa_sm_t)bna_tx_sm_stopped) + if (tx->fsm != bna_tx_sm_stopped) return; tx->flags |= BNA_TX_F_ENABLED; diff --git a/drivers/net/ethernet/brocade/bna/bna_types.h b/drivers/net/ethernet/brocade/bna/bna_types.h index 666b6922e24d..a5ebd7110e07 100644 --- a/drivers/net/ethernet/brocade/bna/bna_types.h +++ b/drivers/net/ethernet/brocade/bna/bna_types.h @@ -312,8 +312,10 @@ struct bna_attr { /* IOCEth */ +enum bna_ioceth_event; + struct bna_ioceth { - bfa_fsm_t fsm; + void (*fsm)(struct bna_ioceth *s, enum bna_ioceth_event e); struct bfa_ioc ioc; struct bna_attr attr; @@ -334,8 +336,10 @@ struct bna_pause_config { enum bna_status rx_pause; }; +enum bna_enet_event; + struct bna_enet { - bfa_fsm_t fsm; + void (*fsm)(struct bna_enet *s, enum bna_enet_event e); enum bna_enet_flags flags; enum bna_enet_type type; @@ -360,8 +364,10 @@ struct bna_enet { /* Ethport */ +enum bna_ethport_event; + struct bna_ethport { - bfa_fsm_t fsm; + void (*fsm)(struct bna_ethport *s, enum bna_ethport_event e); enum bna_ethport_flags flags; enum bna_link_status link_status; @@ -454,13 +460,16 @@ struct bna_txq { }; /* Tx object */ + +enum bna_tx_event; + struct bna_tx { /* This should be the first one */ struct list_head qe; int rid; int hw_id; - bfa_fsm_t fsm; + void (*fsm)(struct bna_tx *s, enum bna_tx_event e); enum bna_tx_flags flags; enum bna_tx_type type; @@ -698,8 +707,11 @@ struct bna_rxp { }; /* RxF structure (hardware Rx Function) */ + +enum bna_rxf_event; + struct bna_rxf { - bfa_fsm_t fsm; + void (*fsm)(struct bna_rxf *s, enum bna_rxf_event e); struct bfa_msgq_cmd_entry msgq_cmd; union { @@ -769,13 +781,16 @@ struct bna_rxf { }; /* Rx object */ + +enum bna_rx_event; + struct bna_rx { /* This should be the first one */ struct list_head qe; int rid; int hw_id; - bfa_fsm_t fsm; + void (*fsm)(struct bna_rx *s, enum bna_rx_event e); enum bna_rx_type type; From 484963ce9f1eb89720055de8cad91e8c9fc7f3e6 Mon Sep 17 00:00:00 2001 From: Yinjun Zhang Date: Thu, 17 Nov 2022 14:21:00 +0100 Subject: [PATCH 1100/2002] nfp: extend capability and control words Currently the 32-bit capability word is almost exhausted, now allocate some more words to support new features, and control word is also extended accordingly. Packet-type offloading is implemented in NIC application firmware, but it's not used in kernel driver, so reserve this bit here in case it's redefined for other use. Signed-off-by: Yinjun Zhang Reviewed-by: Louis Peens Signed-off-by: Simon Horman Reviewed-by: Leon Romanovsky Signed-off-by: David S. Miller --- drivers/net/ethernet/netronome/nfp/nfp_net.h | 2 ++ .../net/ethernet/netronome/nfp/nfp_net_common.c | 1 + drivers/net/ethernet/netronome/nfp/nfp_net_ctrl.h | 14 +++++++++++--- 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net.h b/drivers/net/ethernet/netronome/nfp/nfp_net.h index a101ff30a1ae..0c3e7e2f856d 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net.h +++ b/drivers/net/ethernet/netronome/nfp/nfp_net.h @@ -541,6 +541,7 @@ struct nfp_net_dp { * @id: vNIC id within the PF (0 for VFs) * @fw_ver: Firmware version * @cap: Capabilities advertised by the Firmware + * @cap_w1: Extended capabilities word advertised by the Firmware * @max_mtu: Maximum support MTU advertised by the Firmware * @rss_hfunc: RSS selected hash function * @rss_cfg: RSS configuration @@ -617,6 +618,7 @@ struct nfp_net { u32 id; u32 cap; + u32 cap_w1; u32 max_mtu; u8 rss_hfunc; diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c index 5620faa63c7e..3b3cad449f7a 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c @@ -2452,6 +2452,7 @@ static int nfp_net_read_caps(struct nfp_net *nn) { /* Get some of the read-only fields from the BAR */ nn->cap = nn_readl(nn, NFP_NET_CFG_CAP); + nn->cap_w1 = nn_readq(nn, NFP_NET_CFG_CAP_WORD1); nn->max_mtu = nn_readl(nn, NFP_NET_CFG_MAX_MTU); /* ABI 4.x and ctrl vNIC always use chained metadata, in other cases diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_ctrl.h b/drivers/net/ethernet/netronome/nfp/nfp_net_ctrl.h index 6714d5e8fdab..bc94d2cf1042 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_ctrl.h +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_ctrl.h @@ -257,10 +257,18 @@ #define NFP_NET_CFG_BPF_CFG_MASK 7ULL #define NFP_NET_CFG_BPF_ADDR_MASK (~NFP_NET_CFG_BPF_CFG_MASK) -/* 40B reserved for future use (0x0098 - 0x00c0) +/* 3 words reserved for extended ctrl words (0x0098 - 0x00a4) + * 3 words reserved for extended cap words (0x00a4 - 0x00b0) + * Currently only one word is used, can be extended in future. */ -#define NFP_NET_CFG_RESERVED 0x0098 -#define NFP_NET_CFG_RESERVED_SZ 0x0028 +#define NFP_NET_CFG_CTRL_WORD1 0x0098 +#define NFP_NET_CFG_CTRL_PKT_TYPE (0x1 << 0) /* Pkttype offload */ + +#define NFP_NET_CFG_CAP_WORD1 0x00a4 + +/* 16B reserved for future use (0x00b0 - 0x00c0) */ +#define NFP_NET_CFG_RESERVED 0x00b0 +#define NFP_NET_CFG_RESERVED_SZ 0x0010 /* RSS configuration (0x0100 - 0x01ac): * Used only when NFP_NET_CFG_CTRL_RSS is enabled From 57f273adbcd44172cbe0bd10b8b7408dd255699f Mon Sep 17 00:00:00 2001 From: Huanhuan Wang Date: Thu, 17 Nov 2022 14:21:01 +0100 Subject: [PATCH 1101/2002] nfp: add framework to support ipsec offloading A new metadata type and config structure are introduced to interact with firmware to support ipsec offloading. This feature relies on specific firmware that supports ipsec encrypt/decrypt by advertising related capability bit. The xfrm callbacks which interact with upper layer are implemented in the following patch. Based on initial work of Norm Bagley . Signed-off-by: Huanhuan Wang Reviewed-by: Louis Peens Signed-off-by: Simon Horman Reviewed-by: Leon Romanovsky Signed-off-by: David S. Miller --- drivers/net/ethernet/netronome/Kconfig | 11 ++ drivers/net/ethernet/netronome/nfp/Makefile | 2 + .../ethernet/netronome/nfp/crypto/crypto.h | 23 ++++ .../net/ethernet/netronome/nfp/crypto/ipsec.c | 107 ++++++++++++++++++ drivers/net/ethernet/netronome/nfp/nfd3/dp.c | 58 ++++++++-- .../net/ethernet/netronome/nfp/nfd3/ipsec.c | 18 +++ .../net/ethernet/netronome/nfp/nfd3/nfd3.h | 8 ++ drivers/net/ethernet/netronome/nfp/nfp_net.h | 9 ++ .../ethernet/netronome/nfp/nfp_net_common.c | 3 + .../net/ethernet/netronome/nfp/nfp_net_ctrl.h | 4 + 10 files changed, 233 insertions(+), 10 deletions(-) create mode 100644 drivers/net/ethernet/netronome/nfp/crypto/ipsec.c create mode 100644 drivers/net/ethernet/netronome/nfp/nfd3/ipsec.c diff --git a/drivers/net/ethernet/netronome/Kconfig b/drivers/net/ethernet/netronome/Kconfig index 8844d1ac053a..e785c00b5845 100644 --- a/drivers/net/ethernet/netronome/Kconfig +++ b/drivers/net/ethernet/netronome/Kconfig @@ -54,6 +54,17 @@ config NFP_APP_ABM_NIC functionality. Code will be built into the nfp.ko driver. +config NFP_NET_IPSEC + bool "NFP IPsec crypto offload support" + depends on NFP + depends on XFRM_OFFLOAD + default y + help + Enable driver support IPsec crypto offload on NFP NIC. + Say Y, if you are planning to make use of IPsec crypto + offload. NOTE that IPsec crypto offload on NFP NIC + requires specific FW to work. + config NFP_DEBUG bool "Debug support for Netronome(R) NFP4000/NFP6000 NIC drivers" depends on NFP diff --git a/drivers/net/ethernet/netronome/nfp/Makefile b/drivers/net/ethernet/netronome/nfp/Makefile index 9c0861d03634..8a250214e289 100644 --- a/drivers/net/ethernet/netronome/nfp/Makefile +++ b/drivers/net/ethernet/netronome/nfp/Makefile @@ -80,4 +80,6 @@ nfp-objs += \ abm/main.o endif +nfp-$(CONFIG_NFP_NET_IPSEC) += crypto/ipsec.o nfd3/ipsec.o + nfp-$(CONFIG_NFP_DEBUG) += nfp_net_debugfs.o diff --git a/drivers/net/ethernet/netronome/nfp/crypto/crypto.h b/drivers/net/ethernet/netronome/nfp/crypto/crypto.h index bffe58bb2f27..1df73d658938 100644 --- a/drivers/net/ethernet/netronome/nfp/crypto/crypto.h +++ b/drivers/net/ethernet/netronome/nfp/crypto/crypto.h @@ -39,4 +39,27 @@ nfp_net_tls_rx_resync_req(struct net_device *netdev, } #endif +/* IPsec related structures and functions */ +struct nfp_ipsec_offload { + u32 seq_hi; + u32 seq_low; + u32 handle; +}; + +#ifndef CONFIG_NFP_NET_IPSEC +static inline void nfp_net_ipsec_init(struct nfp_net *nn) +{ +} + +static inline void nfp_net_ipsec_clean(struct nfp_net *nn) +{ +} +#else +void nfp_net_ipsec_init(struct nfp_net *nn); +void nfp_net_ipsec_clean(struct nfp_net *nn); +bool nfp_net_ipsec_tx_prep(struct nfp_net_dp *dp, struct sk_buff *skb, + struct nfp_ipsec_offload *offload_info); +int nfp_net_ipsec_rx(struct nfp_meta_parsed *meta, struct sk_buff *skb); +#endif + #endif diff --git a/drivers/net/ethernet/netronome/nfp/crypto/ipsec.c b/drivers/net/ethernet/netronome/nfp/crypto/ipsec.c new file mode 100644 index 000000000000..154ef841e847 --- /dev/null +++ b/drivers/net/ethernet/netronome/nfp/crypto/ipsec.c @@ -0,0 +1,107 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +/* Copyright (C) 2018 Netronome Systems, Inc */ +/* Copyright (C) 2021 Corigine, Inc */ + +#include +#include +#include +#include +#include +#include +#include + +#include "../nfp_net_ctrl.h" +#include "../nfp_net.h" +#include "crypto.h" + +#define NFP_NET_IPSEC_MAX_SA_CNT (16 * 1024) /* Firmware support a maximum of 16K SA offload */ + +static int nfp_net_xfrm_add_state(struct xfrm_state *x) +{ + return -EOPNOTSUPP; +} + +static void nfp_net_xfrm_del_state(struct xfrm_state *x) +{ +} + +static bool nfp_net_ipsec_offload_ok(struct sk_buff *skb, struct xfrm_state *x) +{ + return false; +} + +static const struct xfrmdev_ops nfp_net_ipsec_xfrmdev_ops = { + .xdo_dev_state_add = nfp_net_xfrm_add_state, + .xdo_dev_state_delete = nfp_net_xfrm_del_state, + .xdo_dev_offload_ok = nfp_net_ipsec_offload_ok, +}; + +void nfp_net_ipsec_init(struct nfp_net *nn) +{ + if (!(nn->cap_w1 & NFP_NET_CFG_CTRL_IPSEC)) + return; + + xa_init_flags(&nn->xa_ipsec, XA_FLAGS_ALLOC); + nn->dp.netdev->xfrmdev_ops = &nfp_net_ipsec_xfrmdev_ops; +} + +void nfp_net_ipsec_clean(struct nfp_net *nn) +{ + if (!(nn->cap_w1 & NFP_NET_CFG_CTRL_IPSEC)) + return; + + WARN_ON(!xa_empty(&nn->xa_ipsec)); + xa_destroy(&nn->xa_ipsec); +} + +bool nfp_net_ipsec_tx_prep(struct nfp_net_dp *dp, struct sk_buff *skb, + struct nfp_ipsec_offload *offload_info) +{ + struct xfrm_offload *xo = xfrm_offload(skb); + struct xfrm_state *x; + + x = xfrm_input_state(skb); + if (!x) + return false; + + offload_info->seq_hi = xo->seq.hi; + offload_info->seq_low = xo->seq.low; + offload_info->handle = x->xso.offload_handle; + + return true; +} + +int nfp_net_ipsec_rx(struct nfp_meta_parsed *meta, struct sk_buff *skb) +{ + struct net_device *netdev = skb->dev; + struct xfrm_offload *xo; + struct xfrm_state *x; + struct sec_path *sp; + struct nfp_net *nn; + u32 saidx; + + nn = netdev_priv(netdev); + + saidx = meta->ipsec_saidx - 1; + if (saidx >= NFP_NET_IPSEC_MAX_SA_CNT) + return -EINVAL; + + sp = secpath_set(skb); + if (unlikely(!sp)) + return -ENOMEM; + + xa_lock(&nn->xa_ipsec); + x = xa_load(&nn->xa_ipsec, saidx); + xa_unlock(&nn->xa_ipsec); + if (!x) + return -EINVAL; + + xfrm_state_hold(x); + sp->xvec[sp->len++] = x; + sp->olen++; + xo = xfrm_offload(skb); + xo->flags = CRYPTO_DONE; + xo->status = CRYPTO_SUCCESS; + + return 0; +} diff --git a/drivers/net/ethernet/netronome/nfp/nfd3/dp.c b/drivers/net/ethernet/netronome/nfp/nfd3/dp.c index 448c1c1afaee..861082c5dbff 100644 --- a/drivers/net/ethernet/netronome/nfp/nfd3/dp.c +++ b/drivers/net/ethernet/netronome/nfp/nfd3/dp.c @@ -4,6 +4,7 @@ #include #include #include +#include #include "../nfp_app.h" #include "../nfp_net.h" @@ -167,28 +168,34 @@ nfp_nfd3_tx_csum(struct nfp_net_dp *dp, struct nfp_net_r_vector *r_vec, u64_stats_update_end(&r_vec->tx_sync); } -static int nfp_nfd3_prep_tx_meta(struct nfp_net_dp *dp, struct sk_buff *skb, u64 tls_handle) +static int nfp_nfd3_prep_tx_meta(struct nfp_net_dp *dp, struct sk_buff *skb, + u64 tls_handle, bool *ipsec) { struct metadata_dst *md_dst = skb_metadata_dst(skb); + struct nfp_ipsec_offload offload_info; unsigned char *data; bool vlan_insert; u32 meta_id = 0; int md_bytes; - if (unlikely(md_dst || tls_handle)) { - if (unlikely(md_dst && md_dst->type != METADATA_HW_PORT_MUX)) - md_dst = NULL; - } +#ifdef CONFIG_NFP_NET_IPSEC + if (xfrm_offload(skb)) + *ipsec = nfp_net_ipsec_tx_prep(dp, skb, &offload_info); +#endif + + if (unlikely(md_dst && md_dst->type != METADATA_HW_PORT_MUX)) + md_dst = NULL; vlan_insert = skb_vlan_tag_present(skb) && (dp->ctrl & NFP_NET_CFG_CTRL_TXVLAN_V2); - if (!(md_dst || tls_handle || vlan_insert)) + if (!(md_dst || tls_handle || vlan_insert || *ipsec)) return 0; md_bytes = sizeof(meta_id) + !!md_dst * NFP_NET_META_PORTID_SIZE + !!tls_handle * NFP_NET_META_CONN_HANDLE_SIZE + - vlan_insert * NFP_NET_META_VLAN_SIZE; + vlan_insert * NFP_NET_META_VLAN_SIZE + + *ipsec * NFP_NET_META_IPSEC_FIELD_SIZE; /* IPsec has 12 bytes of metadata */ if (unlikely(skb_cow_head(skb, md_bytes))) return -ENOMEM; @@ -218,6 +225,19 @@ static int nfp_nfd3_prep_tx_meta(struct nfp_net_dp *dp, struct sk_buff *skb, u64 meta_id <<= NFP_NET_META_FIELD_SIZE; meta_id |= NFP_NET_META_VLAN; } + if (*ipsec) { + /* IPsec has three consecutive 4-bit IPsec metadata types, + * so in total IPsec has three 4 bytes of metadata. + */ + data -= NFP_NET_META_IPSEC_SIZE; + put_unaligned_be32(offload_info.seq_hi, data); + data -= NFP_NET_META_IPSEC_SIZE; + put_unaligned_be32(offload_info.seq_low, data); + data -= NFP_NET_META_IPSEC_SIZE; + put_unaligned_be32(offload_info.handle - 1, data); + meta_id <<= NFP_NET_META_IPSEC_FIELD_SIZE; + meta_id |= NFP_NET_META_IPSEC << 8 | NFP_NET_META_IPSEC << 4 | NFP_NET_META_IPSEC; + } data -= sizeof(meta_id); put_unaligned_be32(meta_id, data); @@ -246,6 +266,7 @@ netdev_tx_t nfp_nfd3_tx(struct sk_buff *skb, struct net_device *netdev) dma_addr_t dma_addr; unsigned int fsize; u64 tls_handle = 0; + bool ipsec = false; u16 qidx; dp = &nn->dp; @@ -273,7 +294,7 @@ netdev_tx_t nfp_nfd3_tx(struct sk_buff *skb, struct net_device *netdev) return NETDEV_TX_OK; } - md_bytes = nfp_nfd3_prep_tx_meta(dp, skb, tls_handle); + md_bytes = nfp_nfd3_prep_tx_meta(dp, skb, tls_handle, &ipsec); if (unlikely(md_bytes < 0)) goto err_flush; @@ -312,6 +333,8 @@ netdev_tx_t nfp_nfd3_tx(struct sk_buff *skb, struct net_device *netdev) txd->vlan = cpu_to_le16(skb_vlan_tag_get(skb)); } + if (ipsec) + nfp_nfd3_ipsec_tx(txd, skb); /* Gather DMA */ if (nr_frags > 0) { __le64 second_half; @@ -764,6 +787,15 @@ nfp_nfd3_parse_meta(struct net_device *netdev, struct nfp_meta_parsed *meta, return false; data += sizeof(struct nfp_net_tls_resync_req); break; +#ifdef CONFIG_NFP_NET_IPSEC + case NFP_NET_META_IPSEC: + /* Note: IPsec packet will have zero saidx, so need add 1 + * to indicate packet is IPsec packet within driver. + */ + meta->ipsec_saidx = get_unaligned_be32(data) + 1; + data += 4; + break; +#endif default: return true; } @@ -876,12 +908,11 @@ static int nfp_nfd3_rx(struct nfp_net_rx_ring *rx_ring, int budget) struct nfp_net_dp *dp = &r_vec->nfp_net->dp; struct nfp_net_tx_ring *tx_ring; struct bpf_prog *xdp_prog; + int idx, pkts_polled = 0; bool xdp_tx_cmpl = false; unsigned int true_bufsz; struct sk_buff *skb; - int pkts_polled = 0; struct xdp_buff xdp; - int idx; xdp_prog = READ_ONCE(dp->xdp_prog); true_bufsz = xdp_prog ? PAGE_SIZE : dp->fl_bufsz; @@ -1081,6 +1112,13 @@ static int nfp_nfd3_rx(struct nfp_net_rx_ring *rx_ring, int budget) continue; } +#ifdef CONFIG_NFP_NET_IPSEC + if (meta.ipsec_saidx != 0 && unlikely(nfp_net_ipsec_rx(&meta, skb))) { + nfp_nfd3_rx_drop(dp, r_vec, rx_ring, NULL, skb); + continue; + } +#endif + if (meta_len_xdp) skb_metadata_set(skb, meta_len_xdp); diff --git a/drivers/net/ethernet/netronome/nfp/nfd3/ipsec.c b/drivers/net/ethernet/netronome/nfp/nfd3/ipsec.c new file mode 100644 index 000000000000..e90f8c975903 --- /dev/null +++ b/drivers/net/ethernet/netronome/nfp/nfd3/ipsec.c @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +/* Copyright (C) 2018 Netronome Systems, Inc */ +/* Copyright (C) 2021 Corigine, Inc */ + +#include + +#include "../nfp_net.h" +#include "nfd3.h" + +void nfp_nfd3_ipsec_tx(struct nfp_nfd3_tx_desc *txd, struct sk_buff *skb) +{ + struct xfrm_state *x = xfrm_input_state(skb); + + if (x->xso.dev && (x->xso.dev->features & NETIF_F_HW_ESP_TX_CSUM)) { + txd->flags |= NFD3_DESC_TX_CSUM | NFD3_DESC_TX_IP4_CSUM | + NFD3_DESC_TX_TCP_CSUM | NFD3_DESC_TX_UDP_CSUM; + } +} diff --git a/drivers/net/ethernet/netronome/nfp/nfd3/nfd3.h b/drivers/net/ethernet/netronome/nfp/nfd3/nfd3.h index 7a0df9e6c3c4..9c1c10dcbaee 100644 --- a/drivers/net/ethernet/netronome/nfp/nfd3/nfd3.h +++ b/drivers/net/ethernet/netronome/nfp/nfd3/nfd3.h @@ -103,4 +103,12 @@ void nfp_nfd3_rx_ring_fill_freelist(struct nfp_net_dp *dp, void nfp_nfd3_xsk_tx_free(struct nfp_nfd3_tx_buf *txbuf); int nfp_nfd3_xsk_poll(struct napi_struct *napi, int budget); +#ifndef CONFIG_NFP_NET_IPSEC +static inline void nfp_nfd3_ipsec_tx(struct nfp_nfd3_tx_desc *txd, struct sk_buff *skb) +{ +} +#else +void nfp_nfd3_ipsec_tx(struct nfp_nfd3_tx_desc *txd, struct sk_buff *skb); +#endif + #endif diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net.h b/drivers/net/ethernet/netronome/nfp/nfp_net.h index 0c3e7e2f856d..6c83e47d8b3d 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net.h +++ b/drivers/net/ethernet/netronome/nfp/nfp_net.h @@ -263,6 +263,10 @@ struct nfp_meta_parsed { u8 tpid; u16 tci; } vlan; + +#ifdef CONFIG_NFP_NET_IPSEC + u32 ipsec_saidx; +#endif }; struct nfp_net_rx_hash { @@ -584,6 +588,7 @@ struct nfp_net_dp { * @qcp_cfg: Pointer to QCP queue used for configuration notification * @tx_bar: Pointer to mapped TX queues * @rx_bar: Pointer to mapped FL/RX queues + * @xa_ipsec: IPsec xarray SA data * @tlv_caps: Parsed TLV capabilities * @ktls_tx_conn_cnt: Number of offloaded kTLS TX connections * @ktls_rx_conn_cnt: Number of offloaded kTLS RX connections @@ -672,6 +677,10 @@ struct nfp_net { u8 __iomem *tx_bar; u8 __iomem *rx_bar; +#ifdef CONFIG_NFP_NET_IPSEC + struct xarray xa_ipsec; +#endif + struct nfp_net_tlv_caps tlv_caps; unsigned int ktls_tx_conn_cnt; diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c index 3b3cad449f7a..593df8f8ac8f 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c @@ -2564,6 +2564,8 @@ int nfp_net_init(struct nfp_net *nn) err = nfp_net_tls_init(nn); if (err) goto err_clean_mbox; + + nfp_net_ipsec_init(nn); } nfp_net_vecs_init(nn); @@ -2587,6 +2589,7 @@ void nfp_net_clean(struct nfp_net *nn) return; unregister_netdev(nn->dp.netdev); + nfp_net_ipsec_clean(nn); nfp_ccm_mbox_clean(nn); nfp_net_reconfig_wait_posted(nn); } diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_ctrl.h b/drivers/net/ethernet/netronome/nfp/nfp_net_ctrl.h index bc94d2cf1042..8f75efd9e463 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_ctrl.h +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_ctrl.h @@ -48,6 +48,7 @@ #define NFP_NET_META_CSUM 6 /* checksum complete type */ #define NFP_NET_META_CONN_HANDLE 7 #define NFP_NET_META_RESYNC_INFO 8 /* RX resync info request */ +#define NFP_NET_META_IPSEC 9 /* IPsec SA index for tx and rx */ #define NFP_META_PORT_ID_CTRL ~0U @@ -55,6 +56,8 @@ #define NFP_NET_META_VLAN_SIZE 4 #define NFP_NET_META_PORTID_SIZE 4 #define NFP_NET_META_CONN_HANDLE_SIZE 8 +#define NFP_NET_META_IPSEC_SIZE 4 +#define NFP_NET_META_IPSEC_FIELD_SIZE 12 /* Hash type pre-pended when a RSS hash was computed */ #define NFP_NET_RSS_NONE 0 #define NFP_NET_RSS_IPV4 1 @@ -263,6 +266,7 @@ */ #define NFP_NET_CFG_CTRL_WORD1 0x0098 #define NFP_NET_CFG_CTRL_PKT_TYPE (0x1 << 0) /* Pkttype offload */ +#define NFP_NET_CFG_CTRL_IPSEC (0x1 << 1) /* IPsec offload */ #define NFP_NET_CFG_CAP_WORD1 0x00a4 From 859a497fe80ce1b4727d14fd43067c7b6fa2a85b Mon Sep 17 00:00:00 2001 From: Huanhuan Wang Date: Thu, 17 Nov 2022 14:21:02 +0100 Subject: [PATCH 1102/2002] nfp: implement xfrm callbacks and expose ipsec offload feature to upper layer Xfrm callbacks are implemented to offload SA info into firmware by mailbox. It supports 16K SA info in total. Expose ipsec offload feature to upper layer, this feature will signal the availability of the offload. Based on initial work of Norm Bagley . Signed-off-by: Huanhuan Wang Reviewed-by: Louis Peens Signed-off-by: Simon Horman Acked-by: Steffen Klassert Signed-off-by: David S. Miller --- .../net/ethernet/netronome/nfp/crypto/ipsec.c | 484 +++++++++++++++++- .../ethernet/netronome/nfp/nfp_net_common.c | 6 + .../net/ethernet/netronome/nfp/nfp_net_ctrl.h | 4 +- 3 files changed, 490 insertions(+), 4 deletions(-) diff --git a/drivers/net/ethernet/netronome/nfp/crypto/ipsec.c b/drivers/net/ethernet/netronome/nfp/crypto/ipsec.c index 154ef841e847..3728870d8e9c 100644 --- a/drivers/net/ethernet/netronome/nfp/crypto/ipsec.c +++ b/drivers/net/ethernet/netronome/nfp/crypto/ipsec.c @@ -16,18 +16,498 @@ #define NFP_NET_IPSEC_MAX_SA_CNT (16 * 1024) /* Firmware support a maximum of 16K SA offload */ +/* IPsec config message cmd codes */ +enum nfp_ipsec_cfg_mssg_cmd_codes { + NFP_IPSEC_CFG_MSSG_ADD_SA, /* Add a new SA */ + NFP_IPSEC_CFG_MSSG_INV_SA /* Invalidate an existing SA */ +}; + +/* IPsec config message response codes */ +enum nfp_ipsec_cfg_mssg_rsp_codes { + NFP_IPSEC_CFG_MSSG_OK, + NFP_IPSEC_CFG_MSSG_FAILED, + NFP_IPSEC_CFG_MSSG_SA_VALID, + NFP_IPSEC_CFG_MSSG_SA_HASH_ADD_FAILED, + NFP_IPSEC_CFG_MSSG_SA_HASH_DEL_FAILED, + NFP_IPSEC_CFG_MSSG_SA_INVALID_CMD +}; + +/* Protocol */ +enum nfp_ipsec_sa_prot { + NFP_IPSEC_PROTOCOL_AH = 0, + NFP_IPSEC_PROTOCOL_ESP = 1 +}; + +/* Mode */ +enum nfp_ipsec_sa_mode { + NFP_IPSEC_PROTMODE_TRANSPORT = 0, + NFP_IPSEC_PROTMODE_TUNNEL = 1 +}; + +/* Cipher types */ +enum nfp_ipsec_sa_cipher { + NFP_IPSEC_CIPHER_NULL, + NFP_IPSEC_CIPHER_3DES, + NFP_IPSEC_CIPHER_AES128, + NFP_IPSEC_CIPHER_AES192, + NFP_IPSEC_CIPHER_AES256, + NFP_IPSEC_CIPHER_AES128_NULL, + NFP_IPSEC_CIPHER_AES192_NULL, + NFP_IPSEC_CIPHER_AES256_NULL, + NFP_IPSEC_CIPHER_CHACHA20 +}; + +/* Cipher modes */ +enum nfp_ipsec_sa_cipher_mode { + NFP_IPSEC_CIMODE_ECB, + NFP_IPSEC_CIMODE_CBC, + NFP_IPSEC_CIMODE_CFB, + NFP_IPSEC_CIMODE_OFB, + NFP_IPSEC_CIMODE_CTR +}; + +/* Hash types */ +enum nfp_ipsec_sa_hash_type { + NFP_IPSEC_HASH_NONE, + NFP_IPSEC_HASH_MD5_96, + NFP_IPSEC_HASH_SHA1_96, + NFP_IPSEC_HASH_SHA256_96, + NFP_IPSEC_HASH_SHA384_96, + NFP_IPSEC_HASH_SHA512_96, + NFP_IPSEC_HASH_MD5_128, + NFP_IPSEC_HASH_SHA1_80, + NFP_IPSEC_HASH_SHA256_128, + NFP_IPSEC_HASH_SHA384_192, + NFP_IPSEC_HASH_SHA512_256, + NFP_IPSEC_HASH_GF128_128, + NFP_IPSEC_HASH_POLY1305_128 +}; + +/* IPSEC_CFG_MSSG_ADD_SA */ +struct nfp_ipsec_cfg_add_sa { + u32 ciph_key[8]; /* Cipher Key */ + union { + u32 auth_key[16]; /* Authentication Key */ + struct nfp_ipsec_aesgcm { /* AES-GCM-ESP fields */ + u32 salt; /* Initialized with SA */ + u32 resv[15]; + } aesgcm_fields; + }; + struct sa_ctrl_word { + uint32_t hash :4; /* From nfp_ipsec_sa_hash_type */ + uint32_t cimode :4; /* From nfp_ipsec_sa_cipher_mode */ + uint32_t cipher :4; /* From nfp_ipsec_sa_cipher */ + uint32_t mode :2; /* From nfp_ipsec_sa_mode */ + uint32_t proto :2; /* From nfp_ipsec_sa_prot */ + uint32_t dir :1; /* SA direction */ + uint32_t resv0 :12; + uint32_t encap_dsbl:1; /* Encap/Decap disable */ + uint32_t resv1 :2; /* Must be set to 0 */ + } ctrl_word; + u32 spi; /* SPI Value */ + uint32_t pmtu_limit :16; /* PMTU Limit */ + uint32_t resv0 :5; + uint32_t ipv6 :1; /* Outbound IPv6 addr format */ + uint32_t resv1 :10; + u32 resv2[2]; + u32 src_ip[4]; /* Src IP addr */ + u32 dst_ip[4]; /* Dst IP addr */ + u32 resv3[6]; +}; + +/* IPSEC_CFG_MSSG */ +struct nfp_ipsec_cfg_mssg { + union { + struct{ + uint32_t cmd:16; /* One of nfp_ipsec_cfg_mssg_cmd_codes */ + uint32_t rsp:16; /* One of nfp_ipsec_cfg_mssg_rsp_codes */ + uint32_t sa_idx:16; /* SA table index */ + uint32_t spare0:16; + struct nfp_ipsec_cfg_add_sa cfg_add_sa; + }; + u32 raw[64]; + }; +}; + +static int nfp_ipsec_cfg_cmd_issue(struct nfp_net *nn, int type, int saidx, + struct nfp_ipsec_cfg_mssg *msg) +{ + int i, msg_size, ret; + + msg->cmd = type; + msg->sa_idx = saidx; + msg->rsp = 0; + msg_size = ARRAY_SIZE(msg->raw); + + for (i = 0; i < msg_size; i++) + nn_writel(nn, NFP_NET_CFG_MBOX_VAL + 4 * i, msg->raw[i]); + + ret = nfp_net_mbox_reconfig(nn, NFP_NET_CFG_MBOX_CMD_IPSEC); + if (ret < 0) + return ret; + + /* For now we always read the whole message response back */ + for (i = 0; i < msg_size; i++) + msg->raw[i] = nn_readl(nn, NFP_NET_CFG_MBOX_VAL + 4 * i); + + switch (msg->rsp) { + case NFP_IPSEC_CFG_MSSG_OK: + return 0; + case NFP_IPSEC_CFG_MSSG_SA_INVALID_CMD: + return -EINVAL; + case NFP_IPSEC_CFG_MSSG_SA_VALID: + return -EEXIST; + case NFP_IPSEC_CFG_MSSG_FAILED: + case NFP_IPSEC_CFG_MSSG_SA_HASH_ADD_FAILED: + case NFP_IPSEC_CFG_MSSG_SA_HASH_DEL_FAILED: + return -EIO; + default: + return -EINVAL; + } +} + +static int set_aes_keylen(struct nfp_ipsec_cfg_add_sa *cfg, int alg, int keylen) +{ + bool aes_gmac = (alg == SADB_X_EALG_NULL_AES_GMAC); + + switch (keylen) { + case 128: + cfg->ctrl_word.cipher = aes_gmac ? NFP_IPSEC_CIPHER_AES128_NULL : + NFP_IPSEC_CIPHER_AES128; + break; + case 192: + cfg->ctrl_word.cipher = aes_gmac ? NFP_IPSEC_CIPHER_AES192_NULL : + NFP_IPSEC_CIPHER_AES192; + break; + case 256: + cfg->ctrl_word.cipher = aes_gmac ? NFP_IPSEC_CIPHER_AES256_NULL : + NFP_IPSEC_CIPHER_AES256; + break; + default: + return -EINVAL; + } + + return 0; +} + +static void set_md5hmac(struct nfp_ipsec_cfg_add_sa *cfg, int *trunc_len) +{ + switch (*trunc_len) { + case 96: + cfg->ctrl_word.hash = NFP_IPSEC_HASH_MD5_96; + break; + case 128: + cfg->ctrl_word.hash = NFP_IPSEC_HASH_MD5_128; + break; + default: + *trunc_len = 0; + } +} + +static void set_sha1hmac(struct nfp_ipsec_cfg_add_sa *cfg, int *trunc_len) +{ + switch (*trunc_len) { + case 96: + cfg->ctrl_word.hash = NFP_IPSEC_HASH_SHA1_96; + break; + case 80: + cfg->ctrl_word.hash = NFP_IPSEC_HASH_SHA1_80; + break; + default: + *trunc_len = 0; + } +} + +static void set_sha2_256hmac(struct nfp_ipsec_cfg_add_sa *cfg, int *trunc_len) +{ + switch (*trunc_len) { + case 96: + cfg->ctrl_word.hash = NFP_IPSEC_HASH_SHA256_96; + break; + case 128: + cfg->ctrl_word.hash = NFP_IPSEC_HASH_SHA256_128; + break; + default: + *trunc_len = 0; + } +} + +static void set_sha2_384hmac(struct nfp_ipsec_cfg_add_sa *cfg, int *trunc_len) +{ + switch (*trunc_len) { + case 96: + cfg->ctrl_word.hash = NFP_IPSEC_HASH_SHA384_96; + break; + case 192: + cfg->ctrl_word.hash = NFP_IPSEC_HASH_SHA384_192; + break; + default: + *trunc_len = 0; + } +} + +static void set_sha2_512hmac(struct nfp_ipsec_cfg_add_sa *cfg, int *trunc_len) +{ + switch (*trunc_len) { + case 96: + cfg->ctrl_word.hash = NFP_IPSEC_HASH_SHA512_96; + break; + case 256: + cfg->ctrl_word.hash = NFP_IPSEC_HASH_SHA512_256; + break; + default: + *trunc_len = 0; + } +} + static int nfp_net_xfrm_add_state(struct xfrm_state *x) { - return -EOPNOTSUPP; + struct net_device *netdev = x->xso.dev; + struct nfp_ipsec_cfg_mssg msg = {}; + int i, key_len, trunc_len, err = 0; + struct nfp_ipsec_cfg_add_sa *cfg; + struct nfp_net *nn; + unsigned int saidx; + + nn = netdev_priv(netdev); + cfg = &msg.cfg_add_sa; + + /* General */ + switch (x->props.mode) { + case XFRM_MODE_TUNNEL: + cfg->ctrl_word.mode = NFP_IPSEC_PROTMODE_TUNNEL; + break; + case XFRM_MODE_TRANSPORT: + cfg->ctrl_word.mode = NFP_IPSEC_PROTMODE_TRANSPORT; + break; + default: + nn_err(nn, "Unsupported mode for xfrm offload\n"); + return -EINVAL; + } + + switch (x->id.proto) { + case IPPROTO_ESP: + cfg->ctrl_word.proto = NFP_IPSEC_PROTOCOL_ESP; + break; + case IPPROTO_AH: + cfg->ctrl_word.proto = NFP_IPSEC_PROTOCOL_AH; + break; + default: + nn_err(nn, "Unsupported protocol for xfrm offload\n"); + return -EINVAL; + } + + if (x->props.flags & XFRM_STATE_ESN) { + nn_err(nn, "Unsupported XFRM_REPLAY_MODE_ESN for xfrm offload\n"); + return -EINVAL; + } + + cfg->spi = ntohl(x->id.spi); + + /* Hash/Authentication */ + if (x->aalg) + trunc_len = x->aalg->alg_trunc_len; + else + trunc_len = 0; + + switch (x->props.aalgo) { + case SADB_AALG_NONE: + if (x->aead) { + trunc_len = -1; + } else { + nn_err(nn, "Unsupported authentication algorithm\n"); + return -EINVAL; + } + break; + case SADB_X_AALG_NULL: + cfg->ctrl_word.hash = NFP_IPSEC_HASH_NONE; + trunc_len = -1; + break; + case SADB_AALG_MD5HMAC: + set_md5hmac(cfg, &trunc_len); + break; + case SADB_AALG_SHA1HMAC: + set_sha1hmac(cfg, &trunc_len); + break; + case SADB_X_AALG_SHA2_256HMAC: + set_sha2_256hmac(cfg, &trunc_len); + break; + case SADB_X_AALG_SHA2_384HMAC: + set_sha2_384hmac(cfg, &trunc_len); + break; + case SADB_X_AALG_SHA2_512HMAC: + set_sha2_512hmac(cfg, &trunc_len); + break; + default: + nn_err(nn, "Unsupported authentication algorithm\n"); + return -EINVAL; + } + + if (!trunc_len) { + nn_err(nn, "Unsupported authentication algorithm trunc length\n"); + return -EINVAL; + } + + if (x->aalg) { + key_len = DIV_ROUND_UP(x->aalg->alg_key_len, BITS_PER_BYTE); + if (key_len > sizeof(cfg->auth_key)) { + nn_err(nn, "Insufficient space for offloaded auth key\n"); + return -EINVAL; + } + for (i = 0; i < key_len / sizeof(cfg->auth_key[0]) ; i++) + cfg->auth_key[i] = get_unaligned_be32(x->aalg->alg_key + + sizeof(cfg->auth_key[0]) * i); + } + + /* Encryption */ + switch (x->props.ealgo) { + case SADB_EALG_NONE: + case SADB_EALG_NULL: + cfg->ctrl_word.cimode = NFP_IPSEC_CIMODE_CBC; + cfg->ctrl_word.cipher = NFP_IPSEC_CIPHER_NULL; + break; + case SADB_EALG_3DESCBC: + cfg->ctrl_word.cimode = NFP_IPSEC_CIMODE_CBC; + cfg->ctrl_word.cipher = NFP_IPSEC_CIPHER_3DES; + break; + case SADB_X_EALG_AES_GCM_ICV16: + case SADB_X_EALG_NULL_AES_GMAC: + if (!x->aead) { + nn_err(nn, "Invalid AES key data\n"); + return -EINVAL; + } + + if (x->aead->alg_icv_len != 128) { + nn_err(nn, "ICV must be 128bit with SADB_X_EALG_AES_GCM_ICV16\n"); + return -EINVAL; + } + cfg->ctrl_word.cimode = NFP_IPSEC_CIMODE_CTR; + cfg->ctrl_word.hash = NFP_IPSEC_HASH_GF128_128; + + /* Aead->alg_key_len includes 32-bit salt */ + if (set_aes_keylen(cfg, x->props.ealgo, x->aead->alg_key_len - 32)) { + nn_err(nn, "Unsupported AES key length %d\n", x->aead->alg_key_len); + return -EINVAL; + } + break; + case SADB_X_EALG_AESCBC: + cfg->ctrl_word.cimode = NFP_IPSEC_CIMODE_CBC; + if (!x->ealg) { + nn_err(nn, "Invalid AES key data\n"); + return -EINVAL; + } + if (set_aes_keylen(cfg, x->props.ealgo, x->ealg->alg_key_len) < 0) { + nn_err(nn, "Unsupported AES key length %d\n", x->ealg->alg_key_len); + return -EINVAL; + } + break; + default: + nn_err(nn, "Unsupported encryption algorithm for offload\n"); + return -EINVAL; + } + + if (x->aead) { + int salt_len = 4; + + key_len = DIV_ROUND_UP(x->aead->alg_key_len, BITS_PER_BYTE); + key_len -= salt_len; + + if (key_len > sizeof(cfg->ciph_key)) { + nn_err(nn, "aead: Insufficient space for offloaded key\n"); + return -EINVAL; + } + + for (i = 0; i < key_len / sizeof(cfg->ciph_key[0]) ; i++) + cfg->ciph_key[i] = get_unaligned_be32(x->aead->alg_key + + sizeof(cfg->ciph_key[0]) * i); + + /* Load up the salt */ + cfg->aesgcm_fields.salt = get_unaligned_be32(x->aead->alg_key + key_len); + } + + if (x->ealg) { + key_len = DIV_ROUND_UP(x->ealg->alg_key_len, BITS_PER_BYTE); + + if (key_len > sizeof(cfg->ciph_key)) { + nn_err(nn, "ealg: Insufficient space for offloaded key\n"); + return -EINVAL; + } + for (i = 0; i < key_len / sizeof(cfg->ciph_key[0]) ; i++) + cfg->ciph_key[i] = get_unaligned_be32(x->ealg->alg_key + + sizeof(cfg->ciph_key[0]) * i); + } + + /* IP related info */ + switch (x->props.family) { + case AF_INET: + cfg->ipv6 = 0; + cfg->src_ip[0] = ntohl(x->props.saddr.a4); + cfg->dst_ip[0] = ntohl(x->id.daddr.a4); + break; + case AF_INET6: + cfg->ipv6 = 1; + for (i = 0; i < 4; i++) { + cfg->src_ip[i] = ntohl(x->props.saddr.a6[i]); + cfg->dst_ip[i] = ntohl(x->id.daddr.a6[i]); + } + break; + default: + nn_err(nn, "Unsupported address family\n"); + return -EINVAL; + } + + /* Maximum nic IPsec code could handle. Other limits may apply. */ + cfg->pmtu_limit = 0xffff; + cfg->ctrl_word.encap_dsbl = 1; + + /* SA direction */ + cfg->ctrl_word.dir = x->xso.dir; + + /* Find unused SA data*/ + err = xa_alloc(&nn->xa_ipsec, &saidx, x, + XA_LIMIT(0, NFP_NET_IPSEC_MAX_SA_CNT - 1), GFP_KERNEL); + if (err < 0) { + nn_err(nn, "Unable to get sa_data number for IPsec\n"); + return err; + } + + /* Allocate saidx and commit the SA */ + err = nfp_ipsec_cfg_cmd_issue(nn, NFP_IPSEC_CFG_MSSG_ADD_SA, saidx, &msg); + if (err) { + xa_erase(&nn->xa_ipsec, saidx); + nn_err(nn, "Failed to issue IPsec command err ret=%d\n", err); + return err; + } + + /* 0 is invalid offload_handle for kernel */ + x->xso.offload_handle = saidx + 1; + return 0; } static void nfp_net_xfrm_del_state(struct xfrm_state *x) { + struct net_device *netdev = x->xso.dev; + struct nfp_ipsec_cfg_mssg msg; + struct nfp_net *nn; + int err; + + nn = netdev_priv(netdev); + err = nfp_ipsec_cfg_cmd_issue(nn, NFP_IPSEC_CFG_MSSG_INV_SA, + x->xso.offload_handle - 1, &msg); + if (err) + nn_warn(nn, "Failed to invalidate SA in hardware\n"); + + xa_erase(&nn->xa_ipsec, x->xso.offload_handle - 1); } static bool nfp_net_ipsec_offload_ok(struct sk_buff *skb, struct xfrm_state *x) { - return false; + if (x->props.family == AF_INET) + /* Offload with IPv4 options is not supported yet */ + return ip_hdr(skb)->ihl == 5; + + /* Offload with IPv6 extension headers is not support yet */ + return !(ipv6_ext_hdr(ipv6_hdr(skb)->nexthdr)); } static const struct xfrmdev_ops nfp_net_ipsec_xfrmdev_ops = { diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c index 593df8f8ac8f..682a9198fb54 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c @@ -2371,6 +2371,12 @@ static void nfp_net_netdev_init(struct nfp_net *nn) } if (nn->cap & NFP_NET_CFG_CTRL_RSS_ANY) netdev->hw_features |= NETIF_F_RXHASH; + +#ifdef CONFIG_NFP_NET_IPSEC + if (nn->cap_w1 & NFP_NET_CFG_CTRL_IPSEC) + netdev->hw_features |= NETIF_F_HW_ESP | NETIF_F_HW_ESP_TX_CSUM; +#endif + if (nn->cap & NFP_NET_CFG_CTRL_VXLAN) { if (nn->cap & NFP_NET_CFG_CTRL_LSO) { netdev->hw_features |= NETIF_F_GSO_UDP_TUNNEL | diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_ctrl.h b/drivers/net/ethernet/netronome/nfp/nfp_net_ctrl.h index 8f75efd9e463..cc11b3dc1252 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_ctrl.h +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_ctrl.h @@ -402,14 +402,14 @@ */ #define NFP_NET_CFG_MBOX_BASE 0x1800 #define NFP_NET_CFG_MBOX_VAL_MAX_SZ 0x1F8 - +#define NFP_NET_CFG_MBOX_VAL 0x1808 #define NFP_NET_CFG_MBOX_SIMPLE_CMD 0x0 #define NFP_NET_CFG_MBOX_SIMPLE_RET 0x4 #define NFP_NET_CFG_MBOX_SIMPLE_VAL 0x8 #define NFP_NET_CFG_MBOX_CMD_CTAG_FILTER_ADD 1 #define NFP_NET_CFG_MBOX_CMD_CTAG_FILTER_KILL 2 - +#define NFP_NET_CFG_MBOX_CMD_IPSEC 3 #define NFP_NET_CFG_MBOX_CMD_PCI_DSCP_PRIOMAP_SET 5 #define NFP_NET_CFG_MBOX_CMD_TLV_CMSG 6 From aa5ac4be8da1b0ea7c588b55aa5214efb94644bc Mon Sep 17 00:00:00 2001 From: Horatiu Vultur Date: Thu, 17 Nov 2022 14:28:12 +0100 Subject: [PATCH 1103/2002] net: microchip: sparx5: kunit test: Fix compile warnings. When VCAP_KUNIT_TEST is enabled the following warnings are generated: drivers/net/ethernet/microchip/vcap/vcap_api_kunit.c:257:34: warning: Using plain integer as NULL pointer drivers/net/ethernet/microchip/vcap/vcap_api_kunit.c:258:41: warning: Using plain integer as NULL pointer drivers/net/ethernet/microchip/vcap/vcap_api_kunit.c:342:23: warning: Using plain integer as NULL pointer drivers/net/ethernet/microchip/vcap/vcap_api_kunit.c:359:23: warning: Using plain integer as NULL pointer drivers/net/ethernet/microchip/vcap/vcap_api_kunit.c:1327:34: warning: Using plain integer as NULL pointer drivers/net/ethernet/microchip/vcap/vcap_api_kunit.c:1328:41: warning: Using plain integer as NULL pointer Therefore fix this. Fixes: dccc30cc4906 ("net: microchip: sparx5: Add KUNIT test of counters and sorted rules") Fixes: c956b9b318d9 ("net: microchip: sparx5: Adding KUNIT tests of key/action values in VCAP API") Fixes: 67d637516fa9 ("net: microchip: sparx5: Adding KUNIT test for the VCAP API") Signed-off-by: Horatiu Vultur Signed-off-by: David S. Miller --- drivers/net/ethernet/microchip/vcap/vcap_api_kunit.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/net/ethernet/microchip/vcap/vcap_api_kunit.c b/drivers/net/ethernet/microchip/vcap/vcap_api_kunit.c index 6858e44ce4a5..194734cadf8b 100644 --- a/drivers/net/ethernet/microchip/vcap/vcap_api_kunit.c +++ b/drivers/net/ethernet/microchip/vcap/vcap_api_kunit.c @@ -254,8 +254,8 @@ test_vcap_xn_rule_creator(struct kunit *test, int cid, enum vcap_user user, u16 priority, int id, int size, int expected_addr) { - struct vcap_rule *rule = 0; - struct vcap_rule_internal *ri = 0; + struct vcap_rule *rule; + struct vcap_rule_internal *ri; enum vcap_keyfield_set keyset = VCAP_KFS_NO_VALUE; enum vcap_actionfield_set actionset = VCAP_AFS_NO_VALUE; int ret; @@ -339,7 +339,7 @@ static void vcap_api_set_bit_1_test(struct kunit *test) .sw_width = 52, .reg_idx = 1, .reg_bitpos = 20, - .tg = 0 + .tg = NULL, }; u32 stream[2] = {0}; @@ -356,7 +356,7 @@ static void vcap_api_set_bit_0_test(struct kunit *test) .sw_width = 52, .reg_idx = 2, .reg_bitpos = 11, - .tg = 0 + .tg = NULL, }; u32 stream[3] = {~0, ~0, ~0}; @@ -1324,8 +1324,8 @@ static void vcap_api_encode_rule_test(struct kunit *test) .actionstream = actdata, }, }; - struct vcap_rule *rule = 0; - struct vcap_rule_internal *ri = 0; + struct vcap_rule *rule; + struct vcap_rule_internal *ri; int vcap_chain_id = 10005; enum vcap_user user = VCAP_USER_VCAP_UTIL; u16 priority = 10; From ef8c373bd91df3cf70596497da0955d218961ead Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Thu, 17 Nov 2022 15:29:53 +0100 Subject: [PATCH 1104/2002] net: ethernet: mtk_eth_soc: fix RSTCTRL_PPE{0,1} definitions Fix RSTCTRL_PPE0 and RSTCTRL_PPE1 register mask definitions for MTK_NETSYS_V2. Remove duplicated definitions. Fixes: 160d3a9b1929 ("net: ethernet: mtk_eth_soc: introduce MTK_NETSYS_V2 support") Signed-off-by: Lorenzo Bianconi Signed-off-by: David S. Miller --- drivers/net/ethernet/mediatek/mtk_eth_soc.c | 13 +++++++------ drivers/net/ethernet/mediatek/mtk_eth_soc.h | 10 +++------- 2 files changed, 10 insertions(+), 13 deletions(-) diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c index 41eca4887f18..4c9972a94451 100644 --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c @@ -3506,16 +3506,17 @@ static int mtk_hw_init(struct mtk_eth *eth) return 0; } - val = RSTCTRL_FE | RSTCTRL_PPE; if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) { regmap_write(eth->ethsys, ETHSYS_FE_RST_CHK_IDLE_EN, 0); - - val |= RSTCTRL_ETH; - if (MTK_HAS_CAPS(eth->soc->caps, MTK_RSTCTRL_PPE1)) - val |= RSTCTRL_PPE1; + val = RSTCTRL_PPE0_V2; + } else { + val = RSTCTRL_PPE0; } - ethsys_reset(eth, val); + if (MTK_HAS_CAPS(eth->soc->caps, MTK_RSTCTRL_PPE1)) + val |= RSTCTRL_PPE1; + + ethsys_reset(eth, RSTCTRL_ETH | RSTCTRL_FE | val); if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) { regmap_write(eth->ethsys, ETHSYS_FE_RST_CHK_IDLE_EN, diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.h b/drivers/net/ethernet/mediatek/mtk_eth_soc.h index 1581eba053ab..18a50529ce7b 100644 --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h @@ -474,18 +474,14 @@ /* ethernet reset control register */ #define ETHSYS_RSTCTRL 0x34 #define RSTCTRL_FE BIT(6) -#define RSTCTRL_PPE BIT(31) -#define RSTCTRL_PPE1 BIT(30) +#define RSTCTRL_PPE0 BIT(31) +#define RSTCTRL_PPE0_V2 BIT(30) +#define RSTCTRL_PPE1 BIT(31) #define RSTCTRL_ETH BIT(23) /* ethernet reset check idle register */ #define ETHSYS_FE_RST_CHK_IDLE_EN 0x28 -/* ethernet reset control register */ -#define ETHSYS_RSTCTRL 0x34 -#define RSTCTRL_FE BIT(6) -#define RSTCTRL_PPE BIT(31) - /* ethernet dma channel agent map */ #define ETHSYS_DMA_AG_MAP 0x408 #define ETHSYS_DMA_AG_MAP_PDMA BIT(0) From 62a45b384a2833fbdff4658a56dab38e9d6f4580 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Thu, 17 Nov 2022 18:29:05 +0300 Subject: [PATCH 1105/2002] net: microchip: sparx5: prevent uninitialized variable Smatch complains that: drivers/net/ethernet/microchip/sparx5/sparx5_dcb.c:112 sparx5_dcb_apptrust_validate() error: uninitialized symbol 'match'. This would only happen if the: if (sparx5_dcb_apptrust_policies[i].nselectors != nselectors) condition is always true (they are not equal). The "nselectors" variable comes from dcbnl_ieee_set() and it is a number between 0-256. This seems like a probably a real bug. Fixes: 23f8382cd95d ("net: microchip: sparx5: add support for apptrust") Signed-off-by: Dan Carpenter Reviewed-by: Daniel Machon Signed-off-by: David S. Miller --- drivers/net/ethernet/microchip/sparx5/sparx5_dcb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_dcb.c b/drivers/net/ethernet/microchip/sparx5/sparx5_dcb.c index 8108f3767767..74abb946b2a3 100644 --- a/drivers/net/ethernet/microchip/sparx5/sparx5_dcb.c +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_dcb.c @@ -90,7 +90,7 @@ static int sparx5_dcb_app_validate(struct net_device *dev, static int sparx5_dcb_apptrust_validate(struct net_device *dev, u8 *selectors, int nselectors, int *err) { - bool match; + bool match = false; int i, ii; for (i = 0; i < ARRAY_SIZE(sparx5_dcb_apptrust_policies); i++) { From 29f8eefba3ba3c9a3416ee09dc868b288a09fc25 Mon Sep 17 00:00:00 2001 From: Andy Chiu Date: Thu, 17 Nov 2022 23:40:12 +0800 Subject: [PATCH 1106/2002] net: axienet: Unexport and remove unused mdio functions Both axienet_mdio_{enable/disable} functions are no longer used in xilinx_axienet_main.c due to 253761a0e61b7. And axienet_mdio_disable is not even used in the mdio.c. So unexport and remove them. Signed-off-by: Andy Chiu Reviewed-by: Greentime Hu Reviewed-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/ethernet/xilinx/xilinx_axienet.h | 2 -- drivers/net/ethernet/xilinx/xilinx_axienet_mdio.c | 13 +------------ 2 files changed, 1 insertion(+), 14 deletions(-) diff --git a/drivers/net/ethernet/xilinx/xilinx_axienet.h b/drivers/net/ethernet/xilinx/xilinx_axienet.h index 6370c447ac5c..575ff9de8985 100644 --- a/drivers/net/ethernet/xilinx/xilinx_axienet.h +++ b/drivers/net/ethernet/xilinx/xilinx_axienet.h @@ -611,8 +611,6 @@ static inline void axienet_dma_out_addr(struct axienet_local *lp, off_t reg, #endif /* CONFIG_64BIT */ /* Function prototypes visible in xilinx_axienet_mdio.c for other files */ -int axienet_mdio_enable(struct axienet_local *lp); -void axienet_mdio_disable(struct axienet_local *lp); int axienet_mdio_setup(struct axienet_local *lp); void axienet_mdio_teardown(struct axienet_local *lp); diff --git a/drivers/net/ethernet/xilinx/xilinx_axienet_mdio.c b/drivers/net/ethernet/xilinx/xilinx_axienet_mdio.c index 0b3b6935c558..e1f51a071888 100644 --- a/drivers/net/ethernet/xilinx/xilinx_axienet_mdio.c +++ b/drivers/net/ethernet/xilinx/xilinx_axienet_mdio.c @@ -153,7 +153,7 @@ static int axienet_mdio_write(struct mii_bus *bus, int phy_id, int reg, * Sets up the MDIO interface by initializing the MDIO clock and enabling the * MDIO interface in hardware. **/ -int axienet_mdio_enable(struct axienet_local *lp) +static int axienet_mdio_enable(struct axienet_local *lp) { u32 host_clock; @@ -226,17 +226,6 @@ int axienet_mdio_enable(struct axienet_local *lp) return axienet_mdio_wait_until_ready(lp); } -/** - * axienet_mdio_disable - MDIO hardware disable function - * @lp: Pointer to axienet local data structure. - * - * Disable the MDIO interface in hardware. - **/ -void axienet_mdio_disable(struct axienet_local *lp) -{ - axienet_iow(lp, XAE_MDIO_MC_OFFSET, 0); -} - /** * axienet_mdio_setup - MDIO setup function * @lp: Pointer to axienet local data structure. From 6830604ec0c73ff8ecafb48046db7332210e58fd Mon Sep 17 00:00:00 2001 From: Andy Chiu Date: Thu, 17 Nov 2022 23:40:13 +0800 Subject: [PATCH 1107/2002] dt-bindings: describe the support of "clock-frequency" in mdio mdio bus frequency is going to be configurable at boottime by a property in DT now, so add a description to it. Signed-off-by: Andy Chiu Reviewed-by: Greentime Hu Reviewed-by: Andrew Lunn Acked-by: Rob Herring Signed-off-by: David S. Miller --- Documentation/devicetree/bindings/net/xilinx_axienet.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Documentation/devicetree/bindings/net/xilinx_axienet.txt b/Documentation/devicetree/bindings/net/xilinx_axienet.txt index 1aa4c6006cd0..80e505a2fda1 100644 --- a/Documentation/devicetree/bindings/net/xilinx_axienet.txt +++ b/Documentation/devicetree/bindings/net/xilinx_axienet.txt @@ -68,6 +68,8 @@ Optional properties: - mdio : Child node for MDIO bus. Must be defined if PHY access is required through the core's MDIO interface (i.e. always, unless the PHY is accessed through a different bus). + Non-standard MDIO bus frequency is supported via + "clock-frequency", see mdio.yaml. - pcs-handle: Phandle to the internal PCS/PMA PHY in SGMII or 1000Base-X modes, where "pcs-handle" should be used to point From 2e1f2c1066c14bfdd915b556f50ff412d7c9f622 Mon Sep 17 00:00:00 2001 From: Andy Chiu Date: Thu, 17 Nov 2022 23:40:14 +0800 Subject: [PATCH 1108/2002] net: axienet: set mdio clock according to bus-frequency Some FPGA platforms have 80KHz MDIO bus frequency constraint when connecting Ethernet to its on-board external Marvell PHY. Thus, we may have to set MDIO clock according to the DT. Otherwise, use the default 2.5 MHz, as specified by 802.3, if the entry is not present. Also, change MAX_MDIO_FREQ to DEFAULT_MDIO_FREQ because we may actually set MDIO bus frequency higher than 2.5MHz if undelying devices support it. And properly disable the mdio bus clock in error path. Signed-off-by: Andy Chiu Reviewed-by: Radhey Shyam Pandey Reviewed-by: Andrew Lunn Signed-off-by: David S. Miller --- .../net/ethernet/xilinx/xilinx_axienet_mdio.c | 70 +++++++++++++------ 1 file changed, 49 insertions(+), 21 deletions(-) diff --git a/drivers/net/ethernet/xilinx/xilinx_axienet_mdio.c b/drivers/net/ethernet/xilinx/xilinx_axienet_mdio.c index e1f51a071888..2f07fde361aa 100644 --- a/drivers/net/ethernet/xilinx/xilinx_axienet_mdio.c +++ b/drivers/net/ethernet/xilinx/xilinx_axienet_mdio.c @@ -17,7 +17,7 @@ #include "xilinx_axienet.h" -#define MAX_MDIO_FREQ 2500000 /* 2.5 MHz */ +#define DEFAULT_MDIO_FREQ 2500000 /* 2.5 MHz */ #define DEFAULT_HOST_CLOCK 150000000 /* 150 MHz */ /* Wait till MDIO interface is ready to accept a new transaction.*/ @@ -147,15 +147,20 @@ static int axienet_mdio_write(struct mii_bus *bus, int phy_id, int reg, /** * axienet_mdio_enable - MDIO hardware setup function * @lp: Pointer to axienet local data structure. + * @np: Pointer to mdio device tree node. * - * Return: 0 on success, -ETIMEDOUT on a timeout. + * Return: 0 on success, -ETIMEDOUT on a timeout, -EOVERFLOW on a clock + * divisor overflow. * * Sets up the MDIO interface by initializing the MDIO clock and enabling the * MDIO interface in hardware. **/ -static int axienet_mdio_enable(struct axienet_local *lp) +static int axienet_mdio_enable(struct axienet_local *lp, struct device_node *np) { + u32 mdio_freq = DEFAULT_MDIO_FREQ; u32 host_clock; + u32 clk_div; + int ret; lp->mii_clk_div = 0; @@ -184,6 +189,12 @@ static int axienet_mdio_enable(struct axienet_local *lp) host_clock); } + if (np) + of_property_read_u32(np, "clock-frequency", &mdio_freq); + if (mdio_freq != DEFAULT_MDIO_FREQ) + netdev_info(lp->ndev, "Setting non-standard mdio bus frequency to %u Hz\n", + mdio_freq); + /* clk_div can be calculated by deriving it from the equation: * fMDIO = fHOST / ((1 + clk_div) * 2) * @@ -209,29 +220,42 @@ static int axienet_mdio_enable(struct axienet_local *lp) * "clock-frequency" from the CPU */ - lp->mii_clk_div = (host_clock / (MAX_MDIO_FREQ * 2)) - 1; + clk_div = (host_clock / (mdio_freq * 2)) - 1; /* If there is any remainder from the division of - * fHOST / (MAX_MDIO_FREQ * 2), then we need to add - * 1 to the clock divisor or we will surely be above 2.5 MHz + * fHOST / (mdio_freq * 2), then we need to add + * 1 to the clock divisor or we will surely be + * above the requested frequency */ - if (host_clock % (MAX_MDIO_FREQ * 2)) - lp->mii_clk_div++; + if (host_clock % (mdio_freq * 2)) + clk_div++; + + /* Check for overflow of mii_clk_div */ + if (clk_div & ~XAE_MDIO_MC_CLOCK_DIVIDE_MAX) { + netdev_warn(lp->ndev, "MDIO clock divisor overflow\n"); + return -EOVERFLOW; + } + lp->mii_clk_div = (u8)clk_div; netdev_dbg(lp->ndev, "Setting MDIO clock divisor to %u/%u Hz host clock.\n", lp->mii_clk_div, host_clock); - axienet_iow(lp, XAE_MDIO_MC_OFFSET, lp->mii_clk_div | XAE_MDIO_MC_MDIOEN_MASK); + axienet_mdio_mdc_enable(lp); - return axienet_mdio_wait_until_ready(lp); + ret = axienet_mdio_wait_until_ready(lp); + if (ret) + axienet_mdio_mdc_disable(lp); + + return ret; } /** * axienet_mdio_setup - MDIO setup function * @lp: Pointer to axienet local data structure. * - * Return: 0 on success, -ETIMEDOUT on a timeout, -ENOMEM when - * mdiobus_alloc (to allocate memory for mii bus structure) fails. + * Return: 0 on success, -ETIMEDOUT on a timeout, -EOVERFLOW on a clock + * divisor overflow, -ENOMEM when mdiobus_alloc (to allocate + * memory for mii bus structure) fails. * * Sets up the MDIO interface by initializing the MDIO clock. * Register the MDIO interface. @@ -242,10 +266,6 @@ int axienet_mdio_setup(struct axienet_local *lp) struct mii_bus *bus; int ret; - ret = axienet_mdio_enable(lp); - if (ret < 0) - return ret; - bus = mdiobus_alloc(); if (!bus) return -ENOMEM; @@ -261,15 +281,23 @@ int axienet_mdio_setup(struct axienet_local *lp) lp->mii_bus = bus; mdio_node = of_get_child_by_name(lp->dev->of_node, "mdio"); + ret = axienet_mdio_enable(lp, mdio_node); + if (ret < 0) + goto unregister; ret = of_mdiobus_register(bus, mdio_node); + if (ret) + goto unregister_mdio_enabled; of_node_put(mdio_node); - if (ret) { - mdiobus_free(bus); - lp->mii_bus = NULL; - return ret; - } axienet_mdio_mdc_disable(lp); return 0; + +unregister_mdio_enabled: + axienet_mdio_mdc_disable(lp); +unregister: + of_node_put(mdio_node); + mdiobus_free(bus); + lp->mii_bus = NULL; + return ret; } /** From d9e8da558580513318a36f4ecb1b6e28e41b4de0 Mon Sep 17 00:00:00 2001 From: Dmitry Vyukov Date: Thu, 17 Nov 2022 17:21:01 +0100 Subject: [PATCH 1109/2002] NFC: nci: Extend virtual NCI deinit test Extend the test to check the scenario when NCI core tries to send data to already closed device to ensure that nothing bad happens. Signed-off-by: Dmitry Vyukov Cc: Bongsu Jeon Cc: Krzysztof Kozlowski Cc: Jakub Kicinski Cc: netdev@vger.kernel.org Signed-off-by: David S. Miller --- tools/testing/selftests/nci/nci_dev.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/tools/testing/selftests/nci/nci_dev.c b/tools/testing/selftests/nci/nci_dev.c index 162c41e9bcae..1562aa7d60b0 100644 --- a/tools/testing/selftests/nci/nci_dev.c +++ b/tools/testing/selftests/nci/nci_dev.c @@ -888,6 +888,17 @@ TEST_F(NCI, deinit) &msg); ASSERT_EQ(rc, 0); EXPECT_EQ(get_dev_enable_state(&msg), 0); + + /* Test that operations that normally send packets to the driver + * don't cause issues when the device is already closed. + * Note: the send of NFC_CMD_DEV_UP itself still succeeds it's just + * that the device won't actually be up. + */ + close(self->virtual_nci_fd); + self->virtual_nci_fd = -1; + rc = send_cmd_with_idx(self->sd, self->fid, self->pid, + NFC_CMD_DEV_UP, self->dev_idex); + EXPECT_EQ(rc, 0); } TEST_HARNESS_MAIN From c2a0c3ed5b64750a41cec052e40cb377b5c4b9bc Mon Sep 17 00:00:00 2001 From: Jeroen de Borst Date: Thu, 17 Nov 2022 08:27:00 -0800 Subject: [PATCH 1110/2002] gve: Adding a new AdminQ command to verify driver Check whether the driver is compatible with the device presented. Signed-off-by: Jeroen de Borst Reviewed-by: Jesse Brandeburg Signed-off-by: David S. Miller --- drivers/net/ethernet/google/gve/gve.h | 1 + drivers/net/ethernet/google/gve/gve_adminq.c | 21 +++++++- drivers/net/ethernet/google/gve/gve_adminq.h | 49 ++++++++++++++++++ drivers/net/ethernet/google/gve/gve_main.c | 52 ++++++++++++++++++++ 4 files changed, 122 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/google/gve/gve.h b/drivers/net/ethernet/google/gve/gve.h index 5655da9cd236..64eb0442c82f 100644 --- a/drivers/net/ethernet/google/gve/gve.h +++ b/drivers/net/ethernet/google/gve/gve.h @@ -563,6 +563,7 @@ struct gve_priv { u32 adminq_report_stats_cnt; u32 adminq_report_link_speed_cnt; u32 adminq_get_ptype_map_cnt; + u32 adminq_verify_driver_compatibility_cnt; /* Global stats */ u32 interface_up_cnt; /* count of times interface turned up since last reset */ diff --git a/drivers/net/ethernet/google/gve/gve_adminq.c b/drivers/net/ethernet/google/gve/gve_adminq.c index f7621ab672b9..60061288ad9d 100644 --- a/drivers/net/ethernet/google/gve/gve_adminq.c +++ b/drivers/net/ethernet/google/gve/gve_adminq.c @@ -289,7 +289,7 @@ static int gve_adminq_parse_err(struct gve_priv *priv, u32 status) case GVE_ADMINQ_COMMAND_ERROR_RESOURCE_EXHAUSTED: return -ENOMEM; case GVE_ADMINQ_COMMAND_ERROR_UNIMPLEMENTED: - return -ENOTSUPP; + return -EOPNOTSUPP; default: dev_err(&priv->pdev->dev, "parse_aq_err: unknown status code %d\n", status); return -EINVAL; @@ -407,6 +407,9 @@ static int gve_adminq_issue_cmd(struct gve_priv *priv, case GVE_ADMINQ_GET_PTYPE_MAP: priv->adminq_get_ptype_map_cnt++; break; + case GVE_ADMINQ_VERIFY_DRIVER_COMPATIBILITY: + priv->adminq_verify_driver_compatibility_cnt++; + break; default: dev_err(&priv->pdev->dev, "unknown AQ command opcode %d\n", opcode); } @@ -878,6 +881,22 @@ int gve_adminq_report_stats(struct gve_priv *priv, u64 stats_report_len, return gve_adminq_execute_cmd(priv, &cmd); } +int gve_adminq_verify_driver_compatibility(struct gve_priv *priv, + u64 driver_info_len, + dma_addr_t driver_info_addr) +{ + union gve_adminq_command cmd; + + memset(&cmd, 0, sizeof(cmd)); + cmd.opcode = cpu_to_be32(GVE_ADMINQ_VERIFY_DRIVER_COMPATIBILITY); + cmd.verify_driver_compatibility = (struct gve_adminq_verify_driver_compatibility) { + .driver_info_len = cpu_to_be64(driver_info_len), + .driver_info_addr = cpu_to_be64(driver_info_addr), + }; + + return gve_adminq_execute_cmd(priv, &cmd); +} + int gve_adminq_report_link_speed(struct gve_priv *priv) { union gve_adminq_command gvnic_cmd; diff --git a/drivers/net/ethernet/google/gve/gve_adminq.h b/drivers/net/ethernet/google/gve/gve_adminq.h index 83c0b40cd2d9..b9ee8be73f96 100644 --- a/drivers/net/ethernet/google/gve/gve_adminq.h +++ b/drivers/net/ethernet/google/gve/gve_adminq.h @@ -24,6 +24,7 @@ enum gve_adminq_opcodes { GVE_ADMINQ_REPORT_STATS = 0xC, GVE_ADMINQ_REPORT_LINK_SPEED = 0xD, GVE_ADMINQ_GET_PTYPE_MAP = 0xE, + GVE_ADMINQ_VERIFY_DRIVER_COMPATIBILITY = 0xF, }; /* Admin queue status codes */ @@ -146,6 +147,49 @@ enum gve_sup_feature_mask { #define GVE_DEV_OPT_LEN_GQI_RAW_ADDRESSING 0x0 +#define GVE_VERSION_STR_LEN 128 + +enum gve_driver_capbility { + gve_driver_capability_gqi_qpl = 0, + gve_driver_capability_gqi_rda = 1, + gve_driver_capability_dqo_qpl = 2, /* reserved for future use */ + gve_driver_capability_dqo_rda = 3, +}; + +#define GVE_CAP1(a) BIT((int)a) +#define GVE_CAP2(a) BIT(((int)a) - 64) +#define GVE_CAP3(a) BIT(((int)a) - 128) +#define GVE_CAP4(a) BIT(((int)a) - 192) + +#define GVE_DRIVER_CAPABILITY_FLAGS1 \ + (GVE_CAP1(gve_driver_capability_gqi_qpl) | \ + GVE_CAP1(gve_driver_capability_gqi_rda) | \ + GVE_CAP1(gve_driver_capability_dqo_rda)) + +#define GVE_DRIVER_CAPABILITY_FLAGS2 0x0 +#define GVE_DRIVER_CAPABILITY_FLAGS3 0x0 +#define GVE_DRIVER_CAPABILITY_FLAGS4 0x0 + +struct gve_driver_info { + u8 os_type; /* 0x01 = Linux */ + u8 driver_major; + u8 driver_minor; + u8 driver_sub; + __be32 os_version_major; + __be32 os_version_minor; + __be32 os_version_sub; + __be64 driver_capability_flags[4]; + u8 os_version_str1[GVE_VERSION_STR_LEN]; + u8 os_version_str2[GVE_VERSION_STR_LEN]; +}; + +struct gve_adminq_verify_driver_compatibility { + __be64 driver_info_len; + __be64 driver_info_addr; +}; + +static_assert(sizeof(struct gve_adminq_verify_driver_compatibility) == 16); + struct gve_adminq_configure_device_resources { __be64 counter_array; __be64 irq_db_addr; @@ -345,6 +389,8 @@ union gve_adminq_command { struct gve_adminq_report_stats report_stats; struct gve_adminq_report_link_speed report_link_speed; struct gve_adminq_get_ptype_map get_ptype_map; + struct gve_adminq_verify_driver_compatibility + verify_driver_compatibility; }; }; u8 reserved[64]; @@ -372,6 +418,9 @@ int gve_adminq_unregister_page_list(struct gve_priv *priv, u32 page_list_id); int gve_adminq_set_mtu(struct gve_priv *priv, u64 mtu); int gve_adminq_report_stats(struct gve_priv *priv, u64 stats_report_len, dma_addr_t stats_report_addr, u64 interval); +int gve_adminq_verify_driver_compatibility(struct gve_priv *priv, + u64 driver_info_len, + dma_addr_t driver_info_addr); int gve_adminq_report_link_speed(struct gve_priv *priv); struct gve_ptype_lut; diff --git a/drivers/net/ethernet/google/gve/gve_main.c b/drivers/net/ethernet/google/gve/gve_main.c index 5a229a01f49d..5b40f9c53196 100644 --- a/drivers/net/ethernet/google/gve/gve_main.c +++ b/drivers/net/ethernet/google/gve/gve_main.c @@ -12,6 +12,8 @@ #include #include #include +#include +#include #include #include "gve.h" #include "gve_dqo.h" @@ -30,6 +32,49 @@ const char gve_version_str[] = GVE_VERSION; static const char gve_version_prefix[] = GVE_VERSION_PREFIX; +static int gve_verify_driver_compatibility(struct gve_priv *priv) +{ + int err; + struct gve_driver_info *driver_info; + dma_addr_t driver_info_bus; + + driver_info = dma_alloc_coherent(&priv->pdev->dev, + sizeof(struct gve_driver_info), + &driver_info_bus, GFP_KERNEL); + if (!driver_info) + return -ENOMEM; + + *driver_info = (struct gve_driver_info) { + .os_type = 1, /* Linux */ + .os_version_major = cpu_to_be32(LINUX_VERSION_MAJOR), + .os_version_minor = cpu_to_be32(LINUX_VERSION_SUBLEVEL), + .os_version_sub = cpu_to_be32(LINUX_VERSION_PATCHLEVEL), + .driver_capability_flags = { + cpu_to_be64(GVE_DRIVER_CAPABILITY_FLAGS1), + cpu_to_be64(GVE_DRIVER_CAPABILITY_FLAGS2), + cpu_to_be64(GVE_DRIVER_CAPABILITY_FLAGS3), + cpu_to_be64(GVE_DRIVER_CAPABILITY_FLAGS4), + }, + }; + strscpy(driver_info->os_version_str1, utsname()->release, + sizeof(driver_info->os_version_str1)); + strscpy(driver_info->os_version_str2, utsname()->version, + sizeof(driver_info->os_version_str2)); + + err = gve_adminq_verify_driver_compatibility(priv, + sizeof(struct gve_driver_info), + driver_info_bus); + + /* It's ok if the device doesn't support this */ + if (err == -EOPNOTSUPP) + err = 0; + + dma_free_coherent(&priv->pdev->dev, + sizeof(struct gve_driver_info), + driver_info, driver_info_bus); + return err; +} + static netdev_tx_t gve_start_xmit(struct sk_buff *skb, struct net_device *dev) { struct gve_priv *priv = netdev_priv(dev); @@ -1368,6 +1413,13 @@ static int gve_init_priv(struct gve_priv *priv, bool skip_describe_device) return err; } + err = gve_verify_driver_compatibility(priv); + if (err) { + dev_err(&priv->pdev->dev, + "Could not verify driver compatibility: err=%d\n", err); + goto err; + } + if (skip_describe_device) goto setup_device; From a5affbd8a73eb0a62378767f9d44527738213c07 Mon Sep 17 00:00:00 2001 From: Jeroen de Borst Date: Thu, 17 Nov 2022 08:27:01 -0800 Subject: [PATCH 1111/2002] gve: Handle alternate miss completions The virtual NIC has 2 ways of indicating a miss-path completion. This handles the alternate. Signed-off-by: Jeroen de Borst Reviewed-by: Jesse Brandeburg Signed-off-by: David S. Miller --- drivers/net/ethernet/google/gve/gve_adminq.h | 4 +++- .../net/ethernet/google/gve/gve_desc_dqo.h | 5 +++++ drivers/net/ethernet/google/gve/gve_tx_dqo.c | 20 ++++++++++++------- 3 files changed, 21 insertions(+), 8 deletions(-) diff --git a/drivers/net/ethernet/google/gve/gve_adminq.h b/drivers/net/ethernet/google/gve/gve_adminq.h index b9ee8be73f96..cf29662e6ad1 100644 --- a/drivers/net/ethernet/google/gve/gve_adminq.h +++ b/drivers/net/ethernet/google/gve/gve_adminq.h @@ -154,6 +154,7 @@ enum gve_driver_capbility { gve_driver_capability_gqi_rda = 1, gve_driver_capability_dqo_qpl = 2, /* reserved for future use */ gve_driver_capability_dqo_rda = 3, + gve_driver_capability_alt_miss_compl = 4, }; #define GVE_CAP1(a) BIT((int)a) @@ -164,7 +165,8 @@ enum gve_driver_capbility { #define GVE_DRIVER_CAPABILITY_FLAGS1 \ (GVE_CAP1(gve_driver_capability_gqi_qpl) | \ GVE_CAP1(gve_driver_capability_gqi_rda) | \ - GVE_CAP1(gve_driver_capability_dqo_rda)) + GVE_CAP1(gve_driver_capability_dqo_rda) | \ + GVE_CAP1(gve_driver_capability_alt_miss_compl)) #define GVE_DRIVER_CAPABILITY_FLAGS2 0x0 #define GVE_DRIVER_CAPABILITY_FLAGS3 0x0 diff --git a/drivers/net/ethernet/google/gve/gve_desc_dqo.h b/drivers/net/ethernet/google/gve/gve_desc_dqo.h index e8fe9adef7f2..f79cd0591110 100644 --- a/drivers/net/ethernet/google/gve/gve_desc_dqo.h +++ b/drivers/net/ethernet/google/gve/gve_desc_dqo.h @@ -176,6 +176,11 @@ static_assert(sizeof(struct gve_tx_compl_desc) == 8); #define GVE_COMPL_TYPE_DQO_MISS 0x1 /* Miss path completion */ #define GVE_COMPL_TYPE_DQO_REINJECTION 0x3 /* Re-injection completion */ +/* The most significant bit in the completion tag can change the completion + * type from packet completion to miss path completion. + */ +#define GVE_ALT_MISS_COMPL_BIT BIT(15) + /* Descriptor to post buffers to HW on buffer queue. */ struct gve_rx_desc_dqo { __le16 buf_id; /* ID returned in Rx completion descriptor */ diff --git a/drivers/net/ethernet/google/gve/gve_tx_dqo.c b/drivers/net/ethernet/google/gve/gve_tx_dqo.c index 588d64819ed5..b76143bfd594 100644 --- a/drivers/net/ethernet/google/gve/gve_tx_dqo.c +++ b/drivers/net/ethernet/google/gve/gve_tx_dqo.c @@ -953,12 +953,18 @@ int gve_clean_tx_done_dqo(struct gve_priv *priv, struct gve_tx_ring *tx, atomic_set_release(&tx->dqo_compl.hw_tx_head, tx_head); } else if (type == GVE_COMPL_TYPE_DQO_PKT) { u16 compl_tag = le16_to_cpu(compl_desc->completion_tag); - - gve_handle_packet_completion(priv, tx, !!napi, - compl_tag, - &pkt_compl_bytes, - &pkt_compl_pkts, - /*is_reinjection=*/false); + if (compl_tag & GVE_ALT_MISS_COMPL_BIT) { + compl_tag &= ~GVE_ALT_MISS_COMPL_BIT; + gve_handle_miss_completion(priv, tx, compl_tag, + &miss_compl_bytes, + &miss_compl_pkts); + } else { + gve_handle_packet_completion(priv, tx, !!napi, + compl_tag, + &pkt_compl_bytes, + &pkt_compl_pkts, + false); + } } else if (type == GVE_COMPL_TYPE_DQO_MISS) { u16 compl_tag = le16_to_cpu(compl_desc->completion_tag); @@ -972,7 +978,7 @@ int gve_clean_tx_done_dqo(struct gve_priv *priv, struct gve_tx_ring *tx, compl_tag, &reinject_compl_bytes, &reinject_compl_pkts, - /*is_reinjection=*/true); + true); } tx->dqo_compl.head = From bcddc196d48182fbbe4b0add157baf50ae0e3d52 Mon Sep 17 00:00:00 2001 From: Steen Hegelund Date: Thu, 17 Nov 2022 22:31:07 +0100 Subject: [PATCH 1112/2002] net: microchip: sparx5: Ensure L3 protocol has a default value This ensures that the l3_proto always have a valid value and that any dissector parsing errors causes the flower rule to be discarded. Signed-off-by: Steen Hegelund Signed-off-by: David S. Miller --- drivers/net/ethernet/microchip/sparx5/sparx5_tc_flower.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_tc_flower.c b/drivers/net/ethernet/microchip/sparx5/sparx5_tc_flower.c index a48baeacc1d2..04fc2f3b1979 100644 --- a/drivers/net/ethernet/microchip/sparx5/sparx5_tc_flower.c +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_tc_flower.c @@ -648,7 +648,11 @@ static int sparx5_tc_flower_replace(struct net_device *ndev, return PTR_ERR(vrule); vrule->cookie = fco->cookie; - sparx5_tc_use_dissectors(fco, admin, vrule, &l3_proto); + + l3_proto = ETH_P_ALL; + err = sparx5_tc_use_dissectors(fco, admin, vrule, &l3_proto); + if (err) + goto out; err = sparx5_tc_add_rule_counter(admin, vrule); if (err) From 277e9179efe5cf3719c7cf181404d26a348c37cf Mon Sep 17 00:00:00 2001 From: Steen Hegelund Date: Thu, 17 Nov 2022 22:31:08 +0100 Subject: [PATCH 1113/2002] net: microchip: sparx5: Ensure VCAP last_used_addr is set back to default This ensures that the last_used_addr in a VCAP instance is returned to the default value when all rules have been deleted. Signed-off-by: Steen Hegelund Signed-off-by: David S. Miller --- drivers/net/ethernet/microchip/vcap/vcap_api.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/microchip/vcap/vcap_api.c b/drivers/net/ethernet/microchip/vcap/vcap_api.c index d12c8ec40fe2..24f4ea1eacb3 100644 --- a/drivers/net/ethernet/microchip/vcap/vcap_api.c +++ b/drivers/net/ethernet/microchip/vcap/vcap_api.c @@ -1249,9 +1249,9 @@ int vcap_del_rule(struct vcap_control *vctrl, struct net_device *ndev, u32 id) vctrl->ops->init(ndev, admin, admin->last_used_addr, ri->size + gap); kfree(ri); - /* Update the last used address */ + /* Update the last used address, set to default when no rules */ if (list_empty(&admin->rules)) { - admin->last_used_addr = admin->last_valid_addr; + admin->last_used_addr = admin->last_valid_addr + 1; } else { elem = list_last_entry(&admin->rules, struct vcap_rule_internal, list); From e0305cc1d125d76e6614a675b1126f00152d6789 Mon Sep 17 00:00:00 2001 From: Steen Hegelund Date: Thu, 17 Nov 2022 22:31:09 +0100 Subject: [PATCH 1114/2002] net: microchip: sparx5: Add VCAP debugFS support Add a debugFS root folder for Sparx5 and add a vcap folder underneath with the VCAP instances and the ports Signed-off-by: Steen Hegelund Signed-off-by: David S. Miller --- .../net/ethernet/microchip/sparx5/Makefile | 1 + .../ethernet/microchip/sparx5/sparx5_main.c | 3 + .../ethernet/microchip/sparx5/sparx5_main.h | 3 + .../microchip/sparx5/sparx5_vcap_debugfs.c | 23 +++++ .../microchip/sparx5/sparx5_vcap_debugfs.h | 33 +++++++ .../microchip/sparx5/sparx5_vcap_impl.c | 65 ++----------- .../microchip/sparx5/sparx5_vcap_impl.h | 48 ++++++++++ drivers/net/ethernet/microchip/vcap/Makefile | 1 + .../net/ethernet/microchip/vcap/vcap_api.h | 13 ++- .../microchip/vcap/vcap_api_debugfs.c | 93 +++++++++++++++++++ .../microchip/vcap/vcap_api_debugfs.h | 41 ++++++++ .../ethernet/microchip/vcap/vcap_api_kunit.c | 6 +- 12 files changed, 266 insertions(+), 64 deletions(-) create mode 100644 drivers/net/ethernet/microchip/sparx5/sparx5_vcap_debugfs.c create mode 100644 drivers/net/ethernet/microchip/sparx5/sparx5_vcap_debugfs.h create mode 100644 drivers/net/ethernet/microchip/vcap/vcap_api_debugfs.c create mode 100644 drivers/net/ethernet/microchip/vcap/vcap_api_debugfs.h diff --git a/drivers/net/ethernet/microchip/sparx5/Makefile b/drivers/net/ethernet/microchip/sparx5/Makefile index cff07b8841bd..d0ed7090aa54 100644 --- a/drivers/net/ethernet/microchip/sparx5/Makefile +++ b/drivers/net/ethernet/microchip/sparx5/Makefile @@ -12,6 +12,7 @@ sparx5-switch-y := sparx5_main.o sparx5_packet.o \ sparx5_vcap_impl.o sparx5_vcap_ag_api.o sparx5_tc_flower.o sparx5_tc_matchall.o sparx5-switch-$(CONFIG_SPARX5_DCB) += sparx5_dcb.o +sparx5-switch-$(CONFIG_DEBUG_FS) += sparx5_vcap_debugfs.o # Provide include files ccflags-y += -I$(srctree)/drivers/net/ethernet/microchip/vcap diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_main.c b/drivers/net/ethernet/microchip/sparx5/sparx5_main.c index 33e17d4f5406..f8382d3124e0 100644 --- a/drivers/net/ethernet/microchip/sparx5/sparx5_main.c +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_main.c @@ -763,6 +763,8 @@ static int mchp_sparx5_probe(struct platform_device *pdev) /* Default values, some from DT */ sparx5->coreclock = SPX5_CORE_CLOCK_DEFAULT; + sparx5->debugfs_root = debugfs_create_dir("sparx5", NULL); + ports = of_get_child_by_name(np, "ethernet-ports"); if (!ports) { dev_err(sparx5->dev, "no ethernet-ports child node found\n"); @@ -906,6 +908,7 @@ static int mchp_sparx5_remove(struct platform_device *pdev) { struct sparx5 *sparx5 = platform_get_drvdata(pdev); + debugfs_remove_recursive(sparx5->debugfs_root); if (sparx5->xtr_irq) { disable_irq(sparx5->xtr_irq); sparx5->xtr_irq = -ENXIO; diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_main.h b/drivers/net/ethernet/microchip/sparx5/sparx5_main.h index 5985f2087d7f..4a574cdcb584 100644 --- a/drivers/net/ethernet/microchip/sparx5/sparx5_main.h +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_main.h @@ -17,6 +17,7 @@ #include #include #include +#include #include "sparx5_main_regs.h" @@ -292,6 +293,8 @@ struct sparx5 { struct vcap_control *vcap_ctrl; /* PGID allocation map */ u8 pgid_map[PGID_TABLE_SIZE]; + /* Common root for debugfs */ + struct dentry *debugfs_root; }; /* sparx5_switchdev.c */ diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_vcap_debugfs.c b/drivers/net/ethernet/microchip/sparx5/sparx5_vcap_debugfs.c new file mode 100644 index 000000000000..2cb061e891c5 --- /dev/null +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_vcap_debugfs.c @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* Microchip Sparx5 Switch driver VCAP debugFS implementation + * + * Copyright (c) 2022 Microchip Technology Inc. and its subsidiaries. + */ + +#include +#include + +#include "sparx5_vcap_debugfs.h" +#include "sparx5_main_regs.h" +#include "sparx5_main.h" +#include "sparx5_vcap_impl.h" +#include "sparx5_vcap_ag_api.h" + +/* Provide port information via a callback interface */ +int sparx5_port_info(struct net_device *ndev, + struct vcap_admin *admin, + struct vcap_output_print *out) +{ + /* this will be added later */ + return 0; +} diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_vcap_debugfs.h b/drivers/net/ethernet/microchip/sparx5/sparx5_vcap_debugfs.h new file mode 100644 index 000000000000..f9ede03441f2 --- /dev/null +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_vcap_debugfs.h @@ -0,0 +1,33 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* Microchip Sparx5 Switch driver VCAP implementation + * + * Copyright (c) 2022 Microchip Technology Inc. and its subsidiaries. + */ + +#ifndef __SPARX5_VCAP_DEBUGFS_H__ +#define __SPARX5_VCAP_DEBUGFS_H__ + +#include + +#include +#include + +#if defined(CONFIG_DEBUG_FS) + +/* Provide port information via a callback interface */ +int sparx5_port_info(struct net_device *ndev, + struct vcap_admin *admin, + struct vcap_output_print *out); + +#else + +static inline int sparx5_port_info(struct net_device *ndev, + struct vcap_admin *admin, + struct vcap_output_print *out) +{ + return 0; +} + +#endif + +#endif /* __SPARX5_VCAP_DEBUGFS_H__ */ diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_vcap_impl.c b/drivers/net/ethernet/microchip/sparx5/sparx5_vcap_impl.c index e8f3d030eba2..e70ff1aa6d57 100644 --- a/drivers/net/ethernet/microchip/sparx5/sparx5_vcap_impl.c +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_vcap_impl.c @@ -12,10 +12,12 @@ #include "vcap_api.h" #include "vcap_api_client.h" +#include "vcap_api_debugfs.h" #include "sparx5_main_regs.h" #include "sparx5_main.h" #include "sparx5_vcap_impl.h" #include "sparx5_vcap_ag_api.h" +#include "sparx5_vcap_debugfs.h" #define SUPER_VCAP_BLK_SIZE 3072 /* addresses per Super VCAP block */ #define STREAMSIZE (64 * 4) /* bytes in the VCAP cache area */ @@ -30,54 +32,6 @@ ANA_ACL_VCAP_S2_KEY_SEL_IP6_UC_KEY_SEL_SET(_v6_uc) | \ ANA_ACL_VCAP_S2_KEY_SEL_ARP_KEY_SEL_SET(_arp)) -/* IS2 port keyset selection control */ - -/* IS2 non-ethernet traffic type keyset generation */ -enum vcap_is2_port_sel_noneth { - VCAP_IS2_PS_NONETH_MAC_ETYPE, - VCAP_IS2_PS_NONETH_CUSTOM_1, - VCAP_IS2_PS_NONETH_CUSTOM_2, - VCAP_IS2_PS_NONETH_NO_LOOKUP -}; - -/* IS2 IPv4 unicast traffic type keyset generation */ -enum vcap_is2_port_sel_ipv4_uc { - VCAP_IS2_PS_IPV4_UC_MAC_ETYPE, - VCAP_IS2_PS_IPV4_UC_IP4_TCP_UDP_OTHER, - VCAP_IS2_PS_IPV4_UC_IP_7TUPLE, -}; - -/* IS2 IPv4 multicast traffic type keyset generation */ -enum vcap_is2_port_sel_ipv4_mc { - VCAP_IS2_PS_IPV4_MC_MAC_ETYPE, - VCAP_IS2_PS_IPV4_MC_IP4_TCP_UDP_OTHER, - VCAP_IS2_PS_IPV4_MC_IP_7TUPLE, - VCAP_IS2_PS_IPV4_MC_IP4_VID, -}; - -/* IS2 IPv6 unicast traffic type keyset generation */ -enum vcap_is2_port_sel_ipv6_uc { - VCAP_IS2_PS_IPV6_UC_MAC_ETYPE, - VCAP_IS2_PS_IPV6_UC_IP_7TUPLE, - VCAP_IS2_PS_IPV6_UC_IP6_STD, - VCAP_IS2_PS_IPV6_UC_IP4_TCP_UDP_OTHER, -}; - -/* IS2 IPv6 multicast traffic type keyset generation */ -enum vcap_is2_port_sel_ipv6_mc { - VCAP_IS2_PS_IPV6_MC_MAC_ETYPE, - VCAP_IS2_PS_IPV6_MC_IP_7TUPLE, - VCAP_IS2_PS_IPV6_MC_IP6_VID, - VCAP_IS2_PS_IPV6_MC_IP6_STD, - VCAP_IS2_PS_IPV6_MC_IP4_TCP_UDP_OTHER, -}; - -/* IS2 ARP traffic type keyset generation */ -enum vcap_is2_port_sel_arp { - VCAP_IS2_PS_ARP_MAC_ETYPE, - VCAP_IS2_PS_ARP_ARP, -}; - static struct sparx5_vcap_inst { enum vcap_type vtype; /* type of vcap */ int vinst; /* instance number within the same type */ @@ -548,15 +502,6 @@ static void sparx5_vcap_move(struct net_device *ndev, struct vcap_admin *admin, sparx5_vcap_wait_super_update(sparx5); } -/* Provide port information via a callback interface */ -static int sparx5_port_info(struct net_device *ndev, enum vcap_type vtype, - int (*pf)(void *out, int arg, const char *fmt, ...), - void *out, int arg) -{ - /* this will be added later */ - return 0; -} - /* Enable all lookups in the VCAP instance */ static int sparx5_vcap_enable(struct net_device *ndev, struct vcap_admin *admin, @@ -702,6 +647,7 @@ int sparx5_vcap_init(struct sparx5 *sparx5) const struct sparx5_vcap_inst *cfg; struct vcap_control *ctrl; struct vcap_admin *admin; + struct dentry *dir; int err = 0, idx; /* Create a VCAP control instance that owns the platform specific VCAP @@ -740,6 +686,11 @@ int sparx5_vcap_init(struct sparx5 *sparx5) sparx5_vcap_port_key_selection(sparx5, admin); list_add_tail(&admin->list, &ctrl->list); } + dir = vcap_debugfs(sparx5->dev, sparx5->debugfs_root, ctrl); + for (idx = 0; idx < SPX5_PORTS; ++idx) + if (sparx5->ports[idx]) + vcap_port_debugfs(sparx5->dev, dir, ctrl, + sparx5->ports[idx]->ndev); return err; } diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_vcap_impl.h b/drivers/net/ethernet/microchip/sparx5/sparx5_vcap_impl.h index 8e44ebd76b41..8a6b7e3d2618 100644 --- a/drivers/net/ethernet/microchip/sparx5/sparx5_vcap_impl.h +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_vcap_impl.h @@ -17,4 +17,52 @@ #define SPARX5_VCAP_CID_IS2_MAX \ (VCAP_CID_INGRESS_STAGE2_L3 + VCAP_CID_LOOKUP_SIZE - 1) /* IS2 Max */ +/* IS2 port keyset selection control */ + +/* IS2 non-ethernet traffic type keyset generation */ +enum vcap_is2_port_sel_noneth { + VCAP_IS2_PS_NONETH_MAC_ETYPE, + VCAP_IS2_PS_NONETH_CUSTOM_1, + VCAP_IS2_PS_NONETH_CUSTOM_2, + VCAP_IS2_PS_NONETH_NO_LOOKUP +}; + +/* IS2 IPv4 unicast traffic type keyset generation */ +enum vcap_is2_port_sel_ipv4_uc { + VCAP_IS2_PS_IPV4_UC_MAC_ETYPE, + VCAP_IS2_PS_IPV4_UC_IP4_TCP_UDP_OTHER, + VCAP_IS2_PS_IPV4_UC_IP_7TUPLE, +}; + +/* IS2 IPv4 multicast traffic type keyset generation */ +enum vcap_is2_port_sel_ipv4_mc { + VCAP_IS2_PS_IPV4_MC_MAC_ETYPE, + VCAP_IS2_PS_IPV4_MC_IP4_TCP_UDP_OTHER, + VCAP_IS2_PS_IPV4_MC_IP_7TUPLE, + VCAP_IS2_PS_IPV4_MC_IP4_VID, +}; + +/* IS2 IPv6 unicast traffic type keyset generation */ +enum vcap_is2_port_sel_ipv6_uc { + VCAP_IS2_PS_IPV6_UC_MAC_ETYPE, + VCAP_IS2_PS_IPV6_UC_IP_7TUPLE, + VCAP_IS2_PS_IPV6_UC_IP6_STD, + VCAP_IS2_PS_IPV6_UC_IP4_TCP_UDP_OTHER, +}; + +/* IS2 IPv6 multicast traffic type keyset generation */ +enum vcap_is2_port_sel_ipv6_mc { + VCAP_IS2_PS_IPV6_MC_MAC_ETYPE, + VCAP_IS2_PS_IPV6_MC_IP_7TUPLE, + VCAP_IS2_PS_IPV6_MC_IP6_VID, + VCAP_IS2_PS_IPV6_MC_IP6_STD, + VCAP_IS2_PS_IPV6_MC_IP4_TCP_UDP_OTHER, +}; + +/* IS2 ARP traffic type keyset generation */ +enum vcap_is2_port_sel_arp { + VCAP_IS2_PS_ARP_MAC_ETYPE, + VCAP_IS2_PS_ARP_ARP, +}; + #endif /* __SPARX5_VCAP_IMPL_H__ */ diff --git a/drivers/net/ethernet/microchip/vcap/Makefile b/drivers/net/ethernet/microchip/vcap/Makefile index b377569f92d8..0adb8f5a8735 100644 --- a/drivers/net/ethernet/microchip/vcap/Makefile +++ b/drivers/net/ethernet/microchip/vcap/Makefile @@ -5,5 +5,6 @@ obj-$(CONFIG_VCAP) += vcap.o obj-$(CONFIG_VCAP_KUNIT_TEST) += vcap_model_kunit.o +vcap-$(CONFIG_DEBUG_FS) += vcap_api_debugfs.o vcap-y += vcap_api.o diff --git a/drivers/net/ethernet/microchip/vcap/vcap_api.h b/drivers/net/ethernet/microchip/vcap/vcap_api.h index bfb8ad535074..e71e7d3d79c2 100644 --- a/drivers/net/ethernet/microchip/vcap/vcap_api.h +++ b/drivers/net/ethernet/microchip/vcap/vcap_api.h @@ -203,6 +203,13 @@ struct vcap_keyset_list { enum vcap_keyfield_set *keysets; /* the list of keysets */ }; +/* Client output printf-like function with destination */ +struct vcap_output_print { + __printf(2, 3) + void (*prf)(void *out, const char *fmt, ...); + void *dst; +}; + /* Client supplied VCAP callback operations */ struct vcap_operations { /* validate port keyset operation */ @@ -252,10 +259,8 @@ struct vcap_operations { /* informational */ int (*port_info) (struct net_device *ndev, - enum vcap_type vtype, - int (*pf)(void *out, int arg, const char *fmt, ...), - void *out, - int arg); + struct vcap_admin *admin, + struct vcap_output_print *out); /* enable/disable the lookups in a vcap instance */ int (*enable) (struct net_device *ndev, diff --git a/drivers/net/ethernet/microchip/vcap/vcap_api_debugfs.c b/drivers/net/ethernet/microchip/vcap/vcap_api_debugfs.c new file mode 100644 index 000000000000..0c7557b1ed81 --- /dev/null +++ b/drivers/net/ethernet/microchip/vcap/vcap_api_debugfs.c @@ -0,0 +1,93 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* Microchip VCAP API debug file system support + * + * Copyright (c) 2022 Microchip Technology Inc. and its subsidiaries. + * + */ + +#include +#include +#include +#include + +#include "vcap_api_debugfs.h" + +struct vcap_admin_debugfs_info { + struct vcap_control *vctrl; + struct vcap_admin *admin; +}; + +struct vcap_port_debugfs_info { + struct vcap_control *vctrl; + struct net_device *ndev; +}; + +/* Show the port configuration and status */ +static int vcap_port_debugfs_show(struct seq_file *m, void *unused) +{ + struct vcap_port_debugfs_info *info = m->private; + struct vcap_admin *admin; + struct vcap_output_print out = { + .prf = (void *)seq_printf, + .dst = m, + }; + + list_for_each_entry(admin, &info->vctrl->list, list) { + if (admin->vinst) + continue; + info->vctrl->ops->port_info(info->ndev, admin, &out); + } + return 0; +} +DEFINE_SHOW_ATTRIBUTE(vcap_port_debugfs); + +void vcap_port_debugfs(struct device *dev, struct dentry *parent, + struct vcap_control *vctrl, + struct net_device *ndev) +{ + struct vcap_port_debugfs_info *info; + + info = devm_kzalloc(dev, sizeof(*info), GFP_KERNEL); + if (!info) + return; + + info->vctrl = vctrl; + info->ndev = ndev; + debugfs_create_file(netdev_name(ndev), 0444, parent, info, + &vcap_port_debugfs_fops); +} +EXPORT_SYMBOL_GPL(vcap_port_debugfs); + +/* Show the raw VCAP instance data (rules with address info) */ +static int vcap_raw_debugfs_show(struct seq_file *m, void *unused) +{ + /* The output will be added later */ + return 0; +} +DEFINE_SHOW_ATTRIBUTE(vcap_raw_debugfs); + +struct dentry *vcap_debugfs(struct device *dev, struct dentry *parent, + struct vcap_control *vctrl) +{ + struct vcap_admin_debugfs_info *info; + struct vcap_admin *admin; + struct dentry *dir; + char name[50]; + + dir = debugfs_create_dir("vcaps", parent); + if (PTR_ERR_OR_ZERO(dir)) + return NULL; + list_for_each_entry(admin, &vctrl->list, list) { + sprintf(name, "raw_%s_%d", vctrl->vcaps[admin->vtype].name, + admin->vinst); + info = devm_kzalloc(dev, sizeof(*info), GFP_KERNEL); + if (!info) + return NULL; + info->vctrl = vctrl; + info->admin = admin; + debugfs_create_file(name, 0444, dir, info, + &vcap_raw_debugfs_fops); + } + return dir; +} +EXPORT_SYMBOL_GPL(vcap_debugfs); diff --git a/drivers/net/ethernet/microchip/vcap/vcap_api_debugfs.h b/drivers/net/ethernet/microchip/vcap/vcap_api_debugfs.h new file mode 100644 index 000000000000..9f2c59b5f6f5 --- /dev/null +++ b/drivers/net/ethernet/microchip/vcap/vcap_api_debugfs.h @@ -0,0 +1,41 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* Copyright (C) 2022 Microchip Technology Inc. and its subsidiaries. + * Microchip VCAP API + */ + +#ifndef __VCAP_API_DEBUGFS__ +#define __VCAP_API_DEBUGFS__ + +#include +#include +#include + +#include "vcap_api.h" + +#if defined(CONFIG_DEBUG_FS) + +void vcap_port_debugfs(struct device *dev, struct dentry *parent, + struct vcap_control *vctrl, + struct net_device *ndev); + +/* Create a debugFS entry for a vcap instance */ +struct dentry *vcap_debugfs(struct device *dev, struct dentry *parent, + struct vcap_control *vctrl); + +#else + +static inline void vcap_port_debugfs(struct device *dev, struct dentry *parent, + struct vcap_control *vctrl, + struct net_device *ndev) +{ +} + +static inline struct dentry *vcap_debugfs(struct device *dev, + struct dentry *parent, + struct vcap_control *vctrl) +{ + return NULL; +} + +#endif +#endif /* __VCAP_API_DEBUGFS__ */ diff --git a/drivers/net/ethernet/microchip/vcap/vcap_api_kunit.c b/drivers/net/ethernet/microchip/vcap/vcap_api_kunit.c index 194734cadf8b..3c3de76b78b8 100644 --- a/drivers/net/ethernet/microchip/vcap/vcap_api_kunit.c +++ b/drivers/net/ethernet/microchip/vcap/vcap_api_kunit.c @@ -204,9 +204,9 @@ static void test_cache_move(struct net_device *ndev, struct vcap_admin *admin, } /* Provide port information via a callback interface */ -static int vcap_test_port_info(struct net_device *ndev, enum vcap_type vtype, - int (*pf)(void *out, int arg, const char *fmt, ...), - void *out, int arg) +static int vcap_test_port_info(struct net_device *ndev, + struct vcap_admin *admin, + struct vcap_output_print *out) { return 0; } From d4134d41e3cb589b55996429a0369dfff37a7180 Mon Sep 17 00:00:00 2001 From: Steen Hegelund Date: Thu, 17 Nov 2022 22:31:10 +0100 Subject: [PATCH 1115/2002] net: microchip: sparx5: Add raw VCAP debugFS support for the VCAP API This adds support for decoding VCAP rules with a minimum number of attributes: address, rule size and keyset. This allows for a quick inspection of a VCAP instance to determine if the rule are present and in the correct order. Signed-off-by: Steen Hegelund Signed-off-by: David S. Miller --- .../microchip/sparx5/sparx5_vcap_debugfs.c | 179 +++++++++++- .../net/ethernet/microchip/vcap/vcap_api.c | 65 ++--- .../microchip/vcap/vcap_api_debugfs.c | 274 +++++++++++++++++- .../microchip/vcap/vcap_api_private.h | 73 +++++ 4 files changed, 535 insertions(+), 56 deletions(-) create mode 100644 drivers/net/ethernet/microchip/vcap/vcap_api_private.h diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_vcap_debugfs.c b/drivers/net/ethernet/microchip/sparx5/sparx5_vcap_debugfs.c index 2cb061e891c5..b91e05ffe2f4 100644 --- a/drivers/net/ethernet/microchip/sparx5/sparx5_vcap_debugfs.c +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_vcap_debugfs.c @@ -13,11 +13,188 @@ #include "sparx5_vcap_impl.h" #include "sparx5_vcap_ag_api.h" +static void sparx5_vcap_port_keys(struct sparx5 *sparx5, + struct vcap_admin *admin, + struct sparx5_port *port, + struct vcap_output_print *out) +{ + int lookup; + u32 value; + + out->prf(out->dst, " port[%02d] (%s): ", port->portno, + netdev_name(port->ndev)); + for (lookup = 0; lookup < admin->lookups; ++lookup) { + out->prf(out->dst, "\n Lookup %d: ", lookup); + + /* Get lookup state */ + value = spx5_rd(sparx5, ANA_ACL_VCAP_S2_CFG(port->portno)); + out->prf(out->dst, "\n state: "); + if (ANA_ACL_VCAP_S2_CFG_SEC_ENA_GET(value)) + out->prf(out->dst, "on"); + else + out->prf(out->dst, "off"); + + /* Get key selection state */ + value = spx5_rd(sparx5, + ANA_ACL_VCAP_S2_KEY_SEL(port->portno, lookup)); + + out->prf(out->dst, "\n noneth: "); + switch (ANA_ACL_VCAP_S2_KEY_SEL_NON_ETH_KEY_SEL_GET(value)) { + case VCAP_IS2_PS_NONETH_MAC_ETYPE: + out->prf(out->dst, "mac_etype"); + break; + case VCAP_IS2_PS_NONETH_CUSTOM_1: + out->prf(out->dst, "custom1"); + break; + case VCAP_IS2_PS_NONETH_CUSTOM_2: + out->prf(out->dst, "custom2"); + break; + case VCAP_IS2_PS_NONETH_NO_LOOKUP: + out->prf(out->dst, "none"); + break; + } + out->prf(out->dst, "\n ipv4_mc: "); + switch (ANA_ACL_VCAP_S2_KEY_SEL_IP4_MC_KEY_SEL_GET(value)) { + case VCAP_IS2_PS_IPV4_MC_MAC_ETYPE: + out->prf(out->dst, "mac_etype"); + break; + case VCAP_IS2_PS_IPV4_MC_IP4_TCP_UDP_OTHER: + out->prf(out->dst, "ip4_tcp_udp ip4_other"); + break; + case VCAP_IS2_PS_IPV4_MC_IP_7TUPLE: + out->prf(out->dst, "ip_7tuple"); + break; + case VCAP_IS2_PS_IPV4_MC_IP4_VID: + out->prf(out->dst, "ip4_vid"); + break; + } + out->prf(out->dst, "\n ipv4_uc: "); + switch (ANA_ACL_VCAP_S2_KEY_SEL_IP4_UC_KEY_SEL_GET(value)) { + case VCAP_IS2_PS_IPV4_UC_MAC_ETYPE: + out->prf(out->dst, "mac_etype"); + break; + case VCAP_IS2_PS_IPV4_UC_IP4_TCP_UDP_OTHER: + out->prf(out->dst, "ip4_tcp_udp ip4_other"); + break; + case VCAP_IS2_PS_IPV4_UC_IP_7TUPLE: + out->prf(out->dst, "ip_7tuple"); + break; + } + out->prf(out->dst, "\n ipv6_mc: "); + switch (ANA_ACL_VCAP_S2_KEY_SEL_IP6_MC_KEY_SEL_GET(value)) { + case VCAP_IS2_PS_IPV6_MC_MAC_ETYPE: + out->prf(out->dst, "mac_etype"); + break; + case VCAP_IS2_PS_IPV6_MC_IP_7TUPLE: + out->prf(out->dst, "ip_7tuple"); + break; + case VCAP_IS2_PS_IPV6_MC_IP6_VID: + out->prf(out->dst, "ip6_vid"); + break; + case VCAP_IS2_PS_IPV6_MC_IP6_STD: + out->prf(out->dst, "ip6_std"); + break; + case VCAP_IS2_PS_IPV6_MC_IP4_TCP_UDP_OTHER: + out->prf(out->dst, "ip4_tcp_udp ipv4_other"); + break; + } + out->prf(out->dst, "\n ipv6_uc: "); + switch (ANA_ACL_VCAP_S2_KEY_SEL_IP6_UC_KEY_SEL_GET(value)) { + case VCAP_IS2_PS_IPV6_UC_MAC_ETYPE: + out->prf(out->dst, "mac_etype"); + break; + case VCAP_IS2_PS_IPV6_UC_IP_7TUPLE: + out->prf(out->dst, "ip_7tuple"); + break; + case VCAP_IS2_PS_IPV6_UC_IP6_STD: + out->prf(out->dst, "ip6_std"); + break; + case VCAP_IS2_PS_IPV6_UC_IP4_TCP_UDP_OTHER: + out->prf(out->dst, "ip4_tcp_udp ip4_other"); + break; + } + out->prf(out->dst, "\n arp: "); + switch (ANA_ACL_VCAP_S2_KEY_SEL_ARP_KEY_SEL_GET(value)) { + case VCAP_IS2_PS_ARP_MAC_ETYPE: + out->prf(out->dst, "mac_etype"); + break; + case VCAP_IS2_PS_ARP_ARP: + out->prf(out->dst, "arp"); + break; + } + } + out->prf(out->dst, "\n"); +} + +static void sparx5_vcap_port_stickies(struct sparx5 *sparx5, + struct vcap_admin *admin, + struct vcap_output_print *out) +{ + int lookup; + u32 value; + + out->prf(out->dst, " Sticky bits: "); + for (lookup = 0; lookup < admin->lookups; ++lookup) { + out->prf(out->dst, "\n Lookup %d: ", lookup); + /* Get lookup sticky bits */ + value = spx5_rd(sparx5, ANA_ACL_SEC_LOOKUP_STICKY(lookup)); + + if (ANA_ACL_SEC_LOOKUP_STICKY_KEY_SEL_CLM_STICKY_GET(value)) + out->prf(out->dst, " sel_clm"); + if (ANA_ACL_SEC_LOOKUP_STICKY_KEY_SEL_IRLEG_STICKY_GET(value)) + out->prf(out->dst, " sel_irleg"); + if (ANA_ACL_SEC_LOOKUP_STICKY_KEY_SEL_ERLEG_STICKY_GET(value)) + out->prf(out->dst, " sel_erleg"); + if (ANA_ACL_SEC_LOOKUP_STICKY_KEY_SEL_PORT_STICKY_GET(value)) + out->prf(out->dst, " sel_port"); + if (ANA_ACL_SEC_LOOKUP_STICKY_SEC_TYPE_CUSTOM2_STICKY_GET(value)) + out->prf(out->dst, " custom2"); + if (ANA_ACL_SEC_LOOKUP_STICKY_SEC_TYPE_CUSTOM1_STICKY_GET(value)) + out->prf(out->dst, " custom1"); + if (ANA_ACL_SEC_LOOKUP_STICKY_SEC_TYPE_OAM_STICKY_GET(value)) + out->prf(out->dst, " oam"); + if (ANA_ACL_SEC_LOOKUP_STICKY_SEC_TYPE_IP6_VID_STICKY_GET(value)) + out->prf(out->dst, " ip6_vid"); + if (ANA_ACL_SEC_LOOKUP_STICKY_SEC_TYPE_IP6_STD_STICKY_GET(value)) + out->prf(out->dst, " ip6_std"); + if (ANA_ACL_SEC_LOOKUP_STICKY_SEC_TYPE_IP6_TCPUDP_STICKY_GET(value)) + out->prf(out->dst, " ip6_tcpudp"); + if (ANA_ACL_SEC_LOOKUP_STICKY_SEC_TYPE_IP_7TUPLE_STICKY_GET(value)) + out->prf(out->dst, " ip_7tuple"); + if (ANA_ACL_SEC_LOOKUP_STICKY_SEC_TYPE_IP4_VID_STICKY_GET(value)) + out->prf(out->dst, " ip4_vid"); + if (ANA_ACL_SEC_LOOKUP_STICKY_SEC_TYPE_IP4_TCPUDP_STICKY_GET(value)) + out->prf(out->dst, " ip4_tcpudp"); + if (ANA_ACL_SEC_LOOKUP_STICKY_SEC_TYPE_IP4_OTHER_STICKY_GET(value)) + out->prf(out->dst, " ip4_other"); + if (ANA_ACL_SEC_LOOKUP_STICKY_SEC_TYPE_ARP_STICKY_GET(value)) + out->prf(out->dst, " arp"); + if (ANA_ACL_SEC_LOOKUP_STICKY_SEC_TYPE_MAC_SNAP_STICKY_GET(value)) + out->prf(out->dst, " mac_snap"); + if (ANA_ACL_SEC_LOOKUP_STICKY_SEC_TYPE_MAC_LLC_STICKY_GET(value)) + out->prf(out->dst, " mac_llc"); + if (ANA_ACL_SEC_LOOKUP_STICKY_SEC_TYPE_MAC_ETYPE_STICKY_GET(value)) + out->prf(out->dst, " mac_etype"); + /* Clear stickies */ + spx5_wr(value, sparx5, ANA_ACL_SEC_LOOKUP_STICKY(lookup)); + } + out->prf(out->dst, "\n"); +} + /* Provide port information via a callback interface */ int sparx5_port_info(struct net_device *ndev, struct vcap_admin *admin, struct vcap_output_print *out) { - /* this will be added later */ + struct sparx5_port *port = netdev_priv(ndev); + struct sparx5 *sparx5 = port->sparx5; + const struct vcap_info *vcap; + struct vcap_control *vctrl; + + vctrl = sparx5->vcap_ctrl; + vcap = &vctrl->vcaps[admin->vtype]; + out->prf(out->dst, "%s:\n", vcap->name); + sparx5_vcap_port_keys(sparx5, admin, port, out); + sparx5_vcap_port_stickies(sparx5, admin, out); return 0; } diff --git a/drivers/net/ethernet/microchip/vcap/vcap_api.c b/drivers/net/ethernet/microchip/vcap/vcap_api.c index 24f4ea1eacb3..153e28e124bc 100644 --- a/drivers/net/ethernet/microchip/vcap/vcap_api.c +++ b/drivers/net/ethernet/microchip/vcap/vcap_api.c @@ -6,28 +6,7 @@ #include -#include "vcap_api.h" -#include "vcap_api_client.h" - -#define to_intrule(rule) container_of((rule), struct vcap_rule_internal, data) - -/* Private VCAP API rule data */ -struct vcap_rule_internal { - struct vcap_rule data; /* provided by the client */ - struct list_head list; /* for insertion in the vcap admin list of rules */ - struct vcap_admin *admin; /* vcap hw instance */ - struct net_device *ndev; /* the interface that the rule applies to */ - struct vcap_control *vctrl; /* the client control */ - u32 sort_key; /* defines the position in the VCAP */ - int keyset_sw; /* subwords in a keyset */ - int actionset_sw; /* subwords in an actionset */ - int keyset_sw_regs; /* registers in a subword in an keyset */ - int actionset_sw_regs; /* registers in a subword in an actionset */ - int size; /* the size of the rule: max(entry, action) */ - u32 addr; /* address in the VCAP at insertion */ - u32 counter_id; /* counter id (if a dedicated counter is available) */ - struct vcap_counter counter; /* last read counter value */ -}; +#include "vcap_api_private.h" /* Moving a rule in the VCAP address space */ struct vcap_rule_move { @@ -36,16 +15,6 @@ struct vcap_rule_move { int count; /* blocksize of addresses to move */ }; -/* Bit iterator for the VCAP cache streams */ -struct vcap_stream_iter { - u32 offset; /* bit offset from the stream start */ - u32 sw_width; /* subword width in bits */ - u32 regs_per_sw; /* registers per subword */ - u32 reg_idx; /* current register index */ - u32 reg_bitpos; /* bit offset in current register */ - const struct vcap_typegroup *tg; /* current typegroup */ -}; - /* Stores the filter cookie that enabled the port */ struct vcap_enabled_port { struct list_head list; /* for insertion in enabled ports list */ @@ -53,8 +22,8 @@ struct vcap_enabled_port { unsigned long cookie; /* filter that enabled the port */ }; -static void vcap_iter_set(struct vcap_stream_iter *itr, int sw_width, - const struct vcap_typegroup *tg, u32 offset) +void vcap_iter_set(struct vcap_stream_iter *itr, int sw_width, + const struct vcap_typegroup *tg, u32 offset) { memset(itr, 0, sizeof(*itr)); itr->offset = offset; @@ -74,7 +43,7 @@ static void vcap_iter_skip_tg(struct vcap_stream_iter *itr) } } -static void vcap_iter_update(struct vcap_stream_iter *itr) +void vcap_iter_update(struct vcap_stream_iter *itr) { int sw_idx, sw_bitpos; @@ -86,15 +55,15 @@ static void vcap_iter_update(struct vcap_stream_iter *itr) itr->reg_bitpos = sw_bitpos % 32; } -static void vcap_iter_init(struct vcap_stream_iter *itr, int sw_width, - const struct vcap_typegroup *tg, u32 offset) +void vcap_iter_init(struct vcap_stream_iter *itr, int sw_width, + const struct vcap_typegroup *tg, u32 offset) { vcap_iter_set(itr, sw_width, tg, offset); vcap_iter_skip_tg(itr); vcap_iter_update(itr); } -static void vcap_iter_next(struct vcap_stream_iter *itr) +void vcap_iter_next(struct vcap_stream_iter *itr) { itr->offset++; vcap_iter_skip_tg(itr); @@ -179,9 +148,9 @@ static void vcap_encode_typegroups(u32 *stream, int sw_width, } /* Return the list of keyfields for the keyset */ -static const struct vcap_field *vcap_keyfields(struct vcap_control *vctrl, - enum vcap_type vt, - enum vcap_keyfield_set keyset) +const struct vcap_field *vcap_keyfields(struct vcap_control *vctrl, + enum vcap_type vt, + enum vcap_keyfield_set keyset) { /* Check that the keyset exists in the vcap keyset list */ if (keyset >= vctrl->vcaps[vt].keyfield_set_size) @@ -190,9 +159,9 @@ static const struct vcap_field *vcap_keyfields(struct vcap_control *vctrl, } /* Return the keyset information for the keyset */ -static const struct vcap_set *vcap_keyfieldset(struct vcap_control *vctrl, - enum vcap_type vt, - enum vcap_keyfield_set keyset) +const struct vcap_set *vcap_keyfieldset(struct vcap_control *vctrl, + enum vcap_type vt, + enum vcap_keyfield_set keyset) { const struct vcap_set *kset; @@ -206,7 +175,7 @@ static const struct vcap_set *vcap_keyfieldset(struct vcap_control *vctrl, } /* Return the typegroup table for the matching keyset (using subword size) */ -static const struct vcap_typegroup * +const struct vcap_typegroup * vcap_keyfield_typegroup(struct vcap_control *vctrl, enum vcap_type vt, enum vcap_keyfield_set keyset) { @@ -219,8 +188,8 @@ vcap_keyfield_typegroup(struct vcap_control *vctrl, } /* Return the number of keyfields in the keyset */ -static int vcap_keyfield_count(struct vcap_control *vctrl, - enum vcap_type vt, enum vcap_keyfield_set keyset) +int vcap_keyfield_count(struct vcap_control *vctrl, + enum vcap_type vt, enum vcap_keyfield_set keyset) { /* Check that the keyset exists in the vcap keyset list */ if (keyset >= vctrl->vcaps[vt].keyfield_set_size) @@ -515,7 +484,7 @@ static int vcap_encode_rule(struct vcap_rule_internal *ri) return 0; } -static int vcap_api_check(struct vcap_control *ctrl) +int vcap_api_check(struct vcap_control *ctrl) { if (!ctrl) { pr_err("%s:%d: vcap control is missing\n", __func__, __LINE__); diff --git a/drivers/net/ethernet/microchip/vcap/vcap_api_debugfs.c b/drivers/net/ethernet/microchip/vcap/vcap_api_debugfs.c index 0c7557b1ed81..4a1ca26be901 100644 --- a/drivers/net/ethernet/microchip/vcap/vcap_api_debugfs.c +++ b/drivers/net/ethernet/microchip/vcap/vcap_api_debugfs.c @@ -5,11 +5,7 @@ * */ -#include -#include -#include -#include - +#include "vcap_api_private.h" #include "vcap_api_debugfs.h" struct vcap_admin_debugfs_info { @@ -22,6 +18,265 @@ struct vcap_port_debugfs_info { struct net_device *ndev; }; +static bool vcap_bitarray_zero(int width, u8 *value) +{ + int bytes = DIV_ROUND_UP(width, BITS_PER_BYTE); + u8 total = 0, bmask = 0xff; + int rwidth = width; + int idx; + + for (idx = 0; idx < bytes; ++idx, rwidth -= BITS_PER_BYTE) { + if (rwidth && rwidth < BITS_PER_BYTE) + bmask = (1 << rwidth) - 1; + total += value[idx] & bmask; + } + return total == 0; +} + +static bool vcap_get_bit(u32 *stream, struct vcap_stream_iter *itr) +{ + u32 mask = BIT(itr->reg_bitpos); + u32 *p = &stream[itr->reg_idx]; + + return !!(*p & mask); +} + +static void vcap_decode_field(u32 *stream, struct vcap_stream_iter *itr, + int width, u8 *value) +{ + int idx; + + /* Loop over the field value bits and get the field bits and + * set them in the output value byte array + */ + for (idx = 0; idx < width; idx++) { + u8 bidx = idx & 0x7; + + /* Decode one field value bit */ + if (vcap_get_bit(stream, itr)) + *value |= 1 << bidx; + vcap_iter_next(itr); + if (bidx == 7) + value++; + } +} + +/* Verify that the typegroup bits have the correct values */ +static int vcap_verify_typegroups(u32 *stream, int sw_width, + const struct vcap_typegroup *tgt, bool mask, + int sw_max) +{ + struct vcap_stream_iter iter; + int sw_cnt, idx; + + vcap_iter_set(&iter, sw_width, tgt, 0); + sw_cnt = 0; + while (iter.tg->width) { + u32 value = 0; + u32 tg_value = iter.tg->value; + + if (mask) + tg_value = (1 << iter.tg->width) - 1; + /* Set position to current typegroup bit */ + iter.offset = iter.tg->offset; + vcap_iter_update(&iter); + for (idx = 0; idx < iter.tg->width; idx++) { + /* Decode one typegroup bit */ + if (vcap_get_bit(stream, &iter)) + value |= 1 << idx; + iter.offset++; + vcap_iter_update(&iter); + } + if (value != tg_value) + return -EINVAL; + iter.tg++; /* next typegroup */ + sw_cnt++; + /* Stop checking more typegroups */ + if (sw_max && sw_cnt >= sw_max) + break; + } + return 0; +} + +/* Find the subword width of the key typegroup that matches the stream data */ +static int vcap_find_keystream_typegroup_sw(struct vcap_control *vctrl, + enum vcap_type vt, u32 *stream, + bool mask, int sw_max) +{ + const struct vcap_typegroup **tgt; + int sw_idx, res; + + tgt = vctrl->vcaps[vt].keyfield_set_typegroups; + /* Try the longest subword match first */ + for (sw_idx = vctrl->vcaps[vt].sw_count; sw_idx >= 0; sw_idx--) { + if (!tgt[sw_idx]) + continue; + + res = vcap_verify_typegroups(stream, vctrl->vcaps[vt].sw_width, + tgt[sw_idx], mask, sw_max); + if (res == 0) + return sw_idx; + } + return -EINVAL; +} + +/* Verify that the type id in the stream matches the type id of the keyset */ +static bool vcap_verify_keystream_keyset(struct vcap_control *vctrl, + enum vcap_type vt, + u32 *keystream, + u32 *mskstream, + enum vcap_keyfield_set keyset) +{ + const struct vcap_info *vcap = &vctrl->vcaps[vt]; + const struct vcap_field *typefld; + const struct vcap_typegroup *tgt; + const struct vcap_field *fields; + struct vcap_stream_iter iter; + const struct vcap_set *info; + u32 value = 0; + u32 mask = 0; + + if (vcap_keyfield_count(vctrl, vt, keyset) == 0) + return false; + + info = vcap_keyfieldset(vctrl, vt, keyset); + /* Check that the keyset is valid */ + if (!info) + return false; + + /* a type_id of value -1 means that there is no type field */ + if (info->type_id == (u8)-1) + return true; + + /* Get a valid typegroup for the specific keyset */ + tgt = vcap_keyfield_typegroup(vctrl, vt, keyset); + if (!tgt) + return false; + + fields = vcap_keyfields(vctrl, vt, keyset); + if (!fields) + return false; + + typefld = &fields[VCAP_KF_TYPE]; + vcap_iter_init(&iter, vcap->sw_width, tgt, typefld->offset); + vcap_decode_field(mskstream, &iter, typefld->width, (u8 *)&mask); + /* no type info if there are no mask bits */ + if (vcap_bitarray_zero(typefld->width, (u8 *)&mask)) + return false; + + /* Get the value of the type field in the stream and compare to the + * one define in the vcap keyset + */ + vcap_iter_init(&iter, vcap->sw_width, tgt, typefld->offset); + vcap_decode_field(keystream, &iter, typefld->width, (u8 *)&value); + + return (value == info->type_id); +} + +/* Verify that the typegroup information, subword count, keyset and type id + * are in sync and correct, return the keyset + */ +static enum +vcap_keyfield_set vcap_find_keystream_keyset(struct vcap_control *vctrl, + enum vcap_type vt, + u32 *keystream, + u32 *mskstream, + bool mask, int sw_max) +{ + const struct vcap_set *keyfield_set; + int sw_count, idx; + bool res; + + sw_count = vcap_find_keystream_typegroup_sw(vctrl, vt, keystream, mask, + sw_max); + if (sw_count < 0) + return sw_count; + + keyfield_set = vctrl->vcaps[vt].keyfield_set; + for (idx = 0; idx < vctrl->vcaps[vt].keyfield_set_size; ++idx) { + if (keyfield_set[idx].sw_per_item != sw_count) + continue; + + res = vcap_verify_keystream_keyset(vctrl, vt, keystream, + mskstream, idx); + if (res) + return idx; + } + return -EINVAL; +} + +/* Read key data from a VCAP address and discover if there is a rule keyset + * here + */ +static int vcap_addr_keyset(struct vcap_control *vctrl, + struct net_device *ndev, + struct vcap_admin *admin, + int addr) +{ + enum vcap_type vt = admin->vtype; + int keyset_sw_regs, idx; + u32 key = 0, mask = 0; + + /* Read the cache at the specified address */ + keyset_sw_regs = DIV_ROUND_UP(vctrl->vcaps[vt].sw_width, 32); + vctrl->ops->update(ndev, admin, VCAP_CMD_READ, VCAP_SEL_ALL, addr); + vctrl->ops->cache_read(ndev, admin, VCAP_SEL_ENTRY, 0, + keyset_sw_regs); + /* Skip uninitialized key/mask entries */ + for (idx = 0; idx < keyset_sw_regs; ++idx) { + key |= ~admin->cache.keystream[idx]; + mask |= admin->cache.maskstream[idx]; + } + if (key == 0 && mask == 0) + return -EINVAL; + /* Decode and locate the keyset */ + return vcap_find_keystream_keyset(vctrl, vt, admin->cache.keystream, + admin->cache.maskstream, false, 0); +} + +static int vcap_show_admin_raw(struct vcap_control *vctrl, + struct vcap_admin *admin, + struct vcap_output_print *out) +{ + enum vcap_type vt = admin->vtype; + struct vcap_rule_internal *ri; + const struct vcap_set *info; + int keyset; + int addr; + int ret; + + if (list_empty(&admin->rules)) + return 0; + + ret = vcap_api_check(vctrl); + if (ret) + return ret; + + ri = list_first_entry(&admin->rules, struct vcap_rule_internal, list); + + /* Go from higher to lower addresses searching for a keyset */ + for (addr = admin->last_valid_addr; addr >= admin->first_valid_addr; + --addr) { + keyset = vcap_addr_keyset(vctrl, ri->ndev, admin, addr); + if (keyset < 0) + continue; + info = vcap_keyfieldset(vctrl, vt, keyset); + if (!info) + continue; + if (addr % info->sw_per_item) + pr_info("addr: %d X%d error rule, keyset: %s\n", + addr, + info->sw_per_item, + vcap_keyset_name(vctrl, keyset)); + else + out->prf(out->dst, " addr: %d, X%d rule, keyset: %s\n", + addr, + info->sw_per_item, + vcap_keyset_name(vctrl, keyset)); + } + return 0; +} + /* Show the port configuration and status */ static int vcap_port_debugfs_show(struct seq_file *m, void *unused) { @@ -61,8 +316,13 @@ EXPORT_SYMBOL_GPL(vcap_port_debugfs); /* Show the raw VCAP instance data (rules with address info) */ static int vcap_raw_debugfs_show(struct seq_file *m, void *unused) { - /* The output will be added later */ - return 0; + struct vcap_admin_debugfs_info *info = m->private; + struct vcap_output_print out = { + .prf = (void *)seq_printf, + .dst = m, + }; + + return vcap_show_admin_raw(info->vctrl, info->admin, &out); } DEFINE_SHOW_ATTRIBUTE(vcap_raw_debugfs); diff --git a/drivers/net/ethernet/microchip/vcap/vcap_api_private.h b/drivers/net/ethernet/microchip/vcap/vcap_api_private.h new file mode 100644 index 000000000000..1ea25c5d0ca7 --- /dev/null +++ b/drivers/net/ethernet/microchip/vcap/vcap_api_private.h @@ -0,0 +1,73 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* Copyright (C) 2022 Microchip Technology Inc. and its subsidiaries. + * Microchip VCAP API + */ + +#ifndef __VCAP_API_PRIVATE__ +#define __VCAP_API_PRIVATE__ + +#include + +#include "vcap_api.h" +#include "vcap_api_client.h" + +#define to_intrule(rule) container_of((rule), struct vcap_rule_internal, data) + +/* Private VCAP API rule data */ +struct vcap_rule_internal { + struct vcap_rule data; /* provided by the client */ + struct list_head list; /* the vcap admin list of rules */ + struct vcap_admin *admin; /* vcap hw instance */ + struct net_device *ndev; /* the interface that the rule applies to */ + struct vcap_control *vctrl; /* the client control */ + u32 sort_key; /* defines the position in the VCAP */ + int keyset_sw; /* subwords in a keyset */ + int actionset_sw; /* subwords in an actionset */ + int keyset_sw_regs; /* registers in a subword in an keyset */ + int actionset_sw_regs; /* registers in a subword in an actionset */ + int size; /* the size of the rule: max(entry, action) */ + u32 addr; /* address in the VCAP at insertion */ + u32 counter_id; /* counter id (if a dedicated counter is available) */ + struct vcap_counter counter; /* last read counter value */ +}; + +/* Bit iterator for the VCAP cache streams */ +struct vcap_stream_iter { + u32 offset; /* bit offset from the stream start */ + u32 sw_width; /* subword width in bits */ + u32 regs_per_sw; /* registers per subword */ + u32 reg_idx; /* current register index */ + u32 reg_bitpos; /* bit offset in current register */ + const struct vcap_typegroup *tg; /* current typegroup */ +}; + +/* Check that the control has a valid set of callbacks */ +int vcap_api_check(struct vcap_control *ctrl); + +/* Iterator functionality */ + +void vcap_iter_init(struct vcap_stream_iter *itr, int sw_width, + const struct vcap_typegroup *tg, u32 offset); +void vcap_iter_next(struct vcap_stream_iter *itr); +void vcap_iter_set(struct vcap_stream_iter *itr, int sw_width, + const struct vcap_typegroup *tg, u32 offset); +void vcap_iter_update(struct vcap_stream_iter *itr); + +/* Keyset and keyfield functionality */ + +/* Return the keyset information for the keyset */ +const struct vcap_set *vcap_keyfieldset(struct vcap_control *vctrl, + enum vcap_type vt, + enum vcap_keyfield_set keyset); +/* Return the number of keyfields in the keyset */ +int vcap_keyfield_count(struct vcap_control *vctrl, + enum vcap_type vt, enum vcap_keyfield_set keyset); +/* Return the typegroup table for the matching keyset (using subword size) */ +const struct vcap_typegroup * +vcap_keyfield_typegroup(struct vcap_control *vctrl, + enum vcap_type vt, enum vcap_keyfield_set keyset); +/* Return the list of keyfields for the keyset */ +const struct vcap_field *vcap_keyfields(struct vcap_control *vctrl, + enum vcap_type vt, + enum vcap_keyfield_set keyset); +#endif /* __VCAP_API_PRIVATE__ */ From 3a7921560d2fd38433fdb2e9b8728ab832880680 Mon Sep 17 00:00:00 2001 From: Steen Hegelund Date: Thu, 17 Nov 2022 22:31:11 +0100 Subject: [PATCH 1116/2002] net: microchip: sparx5: Add VCAP rule debugFS support for the VCAP API This add support to show all rules in a VCAP instance. The information shown is: - rule id - address range - size - chain id - keyset name, subword size, register span - actionset name, subword size, register span - counter value - sticky bit (one bit width counter) Signed-off-by: Steen Hegelund Signed-off-by: David S. Miller --- .../net/ethernet/microchip/vcap/vcap_api.c | 15 ++- .../microchip/vcap/vcap_api_debugfs.c | 116 ++++++++++++++++++ .../microchip/vcap/vcap_api_private.h | 14 +++ 3 files changed, 141 insertions(+), 4 deletions(-) diff --git a/drivers/net/ethernet/microchip/vcap/vcap_api.c b/drivers/net/ethernet/microchip/vcap/vcap_api.c index 153e28e124bc..3da714e9639c 100644 --- a/drivers/net/ethernet/microchip/vcap/vcap_api.c +++ b/drivers/net/ethernet/microchip/vcap/vcap_api.c @@ -502,7 +502,7 @@ int vcap_api_check(struct vcap_control *ctrl) return 0; } -static void vcap_erase_cache(struct vcap_rule_internal *ri) +void vcap_erase_cache(struct vcap_rule_internal *ri) { ri->vctrl->ops->cache_erase(ri->admin); } @@ -578,7 +578,7 @@ int vcap_lookup_rule_by_cookie(struct vcap_control *vctrl, u64 cookie) EXPORT_SYMBOL_GPL(vcap_lookup_rule_by_cookie); /* Make a shallow copy of the rule without the fields */ -static struct vcap_rule_internal *vcap_dup_rule(struct vcap_rule_internal *ri) +struct vcap_rule_internal *vcap_dup_rule(struct vcap_rule_internal *ri) { struct vcap_rule_internal *duprule; @@ -782,9 +782,16 @@ const char *vcap_keyfield_name(struct vcap_control *vctrl, } EXPORT_SYMBOL_GPL(vcap_keyfield_name); +/* map actionset id to a string with the actionset name */ +const char *vcap_actionset_name(struct vcap_control *vctrl, + enum vcap_actionfield_set actionset) +{ + return vctrl->stats->actionfield_set_names[actionset]; +} + /* map action field id to a string with the action name */ -static const char *vcap_actionfield_name(struct vcap_control *vctrl, - enum vcap_action_field action) +const char *vcap_actionfield_name(struct vcap_control *vctrl, + enum vcap_action_field action) { return vctrl->stats->actionfield_names[action]; } diff --git a/drivers/net/ethernet/microchip/vcap/vcap_api_debugfs.c b/drivers/net/ethernet/microchip/vcap/vcap_api_debugfs.c index 4a1ca26be901..b4bc32a08f2c 100644 --- a/drivers/net/ethernet/microchip/vcap/vcap_api_debugfs.c +++ b/drivers/net/ethernet/microchip/vcap/vcap_api_debugfs.c @@ -234,6 +234,106 @@ static int vcap_addr_keyset(struct vcap_control *vctrl, admin->cache.maskstream, false, 0); } +static int vcap_read_rule(struct vcap_rule_internal *ri) +{ + struct vcap_admin *admin = ri->admin; + int sw_idx, ent_idx = 0, act_idx = 0; + u32 addr = ri->addr; + + if (!ri->size || !ri->keyset_sw_regs || !ri->actionset_sw_regs) { + pr_err("%s:%d: rule is empty\n", __func__, __LINE__); + return -EINVAL; + } + vcap_erase_cache(ri); + /* Use the values in the streams to read the VCAP cache */ + for (sw_idx = 0; sw_idx < ri->size; sw_idx++, addr++) { + ri->vctrl->ops->update(ri->ndev, admin, VCAP_CMD_READ, + VCAP_SEL_ALL, addr); + ri->vctrl->ops->cache_read(ri->ndev, admin, + VCAP_SEL_ENTRY, ent_idx, + ri->keyset_sw_regs); + ri->vctrl->ops->cache_read(ri->ndev, admin, + VCAP_SEL_ACTION, act_idx, + ri->actionset_sw_regs); + if (sw_idx == 0) + ri->vctrl->ops->cache_read(ri->ndev, admin, + VCAP_SEL_COUNTER, + ri->counter_id, 0); + ent_idx += ri->keyset_sw_regs; + act_idx += ri->actionset_sw_regs; + } + return 0; +} + +static void vcap_show_admin_rule(struct vcap_control *vctrl, + struct vcap_admin *admin, + struct vcap_output_print *out, + struct vcap_rule_internal *ri) +{ + ri->counter.value = admin->cache.counter; + ri->counter.sticky = admin->cache.sticky; + out->prf(out->dst, + "rule: %u, addr: [%d,%d], X%d, ctr[%d]: %d, hit: %d\n", + ri->data.id, ri->addr, ri->addr + ri->size - 1, ri->size, + ri->counter_id, ri->counter.value, ri->counter.sticky); + out->prf(out->dst, " chain_id: %d\n", ri->data.vcap_chain_id); + out->prf(out->dst, " user: %d\n", ri->data.user); + out->prf(out->dst, " priority: %d\n", ri->data.priority); + out->prf(out->dst, " keyset: %s\n", + vcap_keyset_name(vctrl, ri->data.keyset)); + out->prf(out->dst, " actionset: %s\n", + vcap_actionset_name(vctrl, ri->data.actionset)); +} + +static void vcap_show_admin_info(struct vcap_control *vctrl, + struct vcap_admin *admin, + struct vcap_output_print *out) +{ + const struct vcap_info *vcap = &vctrl->vcaps[admin->vtype]; + + out->prf(out->dst, "name: %s\n", vcap->name); + out->prf(out->dst, "rows: %d\n", vcap->rows); + out->prf(out->dst, "sw_count: %d\n", vcap->sw_count); + out->prf(out->dst, "sw_width: %d\n", vcap->sw_width); + out->prf(out->dst, "sticky_width: %d\n", vcap->sticky_width); + out->prf(out->dst, "act_width: %d\n", vcap->act_width); + out->prf(out->dst, "default_cnt: %d\n", vcap->default_cnt); + out->prf(out->dst, "require_cnt_dis: %d\n", vcap->require_cnt_dis); + out->prf(out->dst, "version: %d\n", vcap->version); + out->prf(out->dst, "vtype: %d\n", admin->vtype); + out->prf(out->dst, "vinst: %d\n", admin->vinst); + out->prf(out->dst, "first_cid: %d\n", admin->first_cid); + out->prf(out->dst, "last_cid: %d\n", admin->last_cid); + out->prf(out->dst, "lookups: %d\n", admin->lookups); + out->prf(out->dst, "first_valid_addr: %d\n", admin->first_valid_addr); + out->prf(out->dst, "last_valid_addr: %d\n", admin->last_valid_addr); + out->prf(out->dst, "last_used_addr: %d\n", admin->last_used_addr); +} + +static int vcap_show_admin(struct vcap_control *vctrl, + struct vcap_admin *admin, + struct vcap_output_print *out) +{ + struct vcap_rule_internal *elem, *ri; + int ret = 0; + + vcap_show_admin_info(vctrl, admin, out); + list_for_each_entry(elem, &admin->rules, list) { + ri = vcap_dup_rule(elem); + if (IS_ERR(ri)) + goto free_rule; + /* Read data from VCAP */ + ret = vcap_read_rule(ri); + if (ret) + goto free_rule; + out->prf(out->dst, "\n"); + vcap_show_admin_rule(vctrl, admin, out, ri); +free_rule: + vcap_free_rule((struct vcap_rule *)ri); + } + return ret; +} + static int vcap_show_admin_raw(struct vcap_control *vctrl, struct vcap_admin *admin, struct vcap_output_print *out) @@ -313,6 +413,19 @@ void vcap_port_debugfs(struct device *dev, struct dentry *parent, } EXPORT_SYMBOL_GPL(vcap_port_debugfs); +/* Show the full VCAP instance data (rules with all fields) */ +static int vcap_debugfs_show(struct seq_file *m, void *unused) +{ + struct vcap_admin_debugfs_info *info = m->private; + struct vcap_output_print out = { + .prf = (void *)seq_printf, + .dst = m, + }; + + return vcap_show_admin(info->vctrl, info->admin, &out); +} +DEFINE_SHOW_ATTRIBUTE(vcap_debugfs); + /* Show the raw VCAP instance data (rules with address info) */ static int vcap_raw_debugfs_show(struct seq_file *m, void *unused) { @@ -347,6 +460,9 @@ struct dentry *vcap_debugfs(struct device *dev, struct dentry *parent, info->admin = admin; debugfs_create_file(name, 0444, dir, info, &vcap_raw_debugfs_fops); + sprintf(name, "%s_%d", vctrl->vcaps[admin->vtype].name, + admin->vinst); + debugfs_create_file(name, 0444, dir, info, &vcap_debugfs_fops); } return dir; } diff --git a/drivers/net/ethernet/microchip/vcap/vcap_api_private.h b/drivers/net/ethernet/microchip/vcap/vcap_api_private.h index 1ea25c5d0ca7..57309de463d7 100644 --- a/drivers/net/ethernet/microchip/vcap/vcap_api_private.h +++ b/drivers/net/ethernet/microchip/vcap/vcap_api_private.h @@ -43,6 +43,10 @@ struct vcap_stream_iter { /* Check that the control has a valid set of callbacks */ int vcap_api_check(struct vcap_control *ctrl); +/* Make a shallow copy of the rule without the fields */ +struct vcap_rule_internal *vcap_dup_rule(struct vcap_rule_internal *ri); +/* Erase the VCAP cache area used or encoding and decoding */ +void vcap_erase_cache(struct vcap_rule_internal *ri); /* Iterator functionality */ @@ -70,4 +74,14 @@ vcap_keyfield_typegroup(struct vcap_control *vctrl, const struct vcap_field *vcap_keyfields(struct vcap_control *vctrl, enum vcap_type vt, enum vcap_keyfield_set keyset); + +/* Actionset and actionfield functionality */ + +/* Map actionset id to a string with the actionset name */ +const char *vcap_actionset_name(struct vcap_control *vctrl, + enum vcap_actionfield_set actionset); +/* Map key field id to a string with the key name */ +const char *vcap_actionfield_name(struct vcap_control *vctrl, + enum vcap_action_field action); + #endif /* __VCAP_API_PRIVATE__ */ From 72d84dd609bea135f823e8d1a60380b4aa443ee5 Mon Sep 17 00:00:00 2001 From: Steen Hegelund Date: Thu, 17 Nov 2022 22:31:12 +0100 Subject: [PATCH 1117/2002] net: microchip: sparx5: Add VCAP debugFS key/action support for the VCAP API This add support for displaying the keys and actions in a rule. The keys and action display format will be determined by the size and the type of the key or action. The longer keys will typically be displayed as a hexadecimal byte array. The actionset is not decoded in full as the Sparx5 IS2 only has one supported action, so this will be added later with other VCAP types. Signed-off-by: Steen Hegelund Signed-off-by: David S. Miller --- .../net/ethernet/microchip/vcap/vcap_api.c | 12 +- .../microchip/vcap/vcap_api_debugfs.c | 315 +++++++++++++++++- .../microchip/vcap/vcap_api_private.h | 16 + 3 files changed, 333 insertions(+), 10 deletions(-) diff --git a/drivers/net/ethernet/microchip/vcap/vcap_api.c b/drivers/net/ethernet/microchip/vcap/vcap_api.c index 3da714e9639c..3415605350c9 100644 --- a/drivers/net/ethernet/microchip/vcap/vcap_api.c +++ b/drivers/net/ethernet/microchip/vcap/vcap_api.c @@ -316,7 +316,7 @@ static int vcap_encode_rule_keyset(struct vcap_rule_internal *ri) } /* Return the list of actionfields for the actionset */ -static const struct vcap_field * +const struct vcap_field * vcap_actionfields(struct vcap_control *vctrl, enum vcap_type vt, enum vcap_actionfield_set actionset) { @@ -326,7 +326,7 @@ vcap_actionfields(struct vcap_control *vctrl, return vctrl->vcaps[vt].actionfield_set_map[actionset]; } -static const struct vcap_set * +const struct vcap_set * vcap_actionfieldset(struct vcap_control *vctrl, enum vcap_type vt, enum vcap_actionfield_set actionset) { @@ -342,7 +342,7 @@ vcap_actionfieldset(struct vcap_control *vctrl, } /* Return the typegroup table for the matching actionset (using subword size) */ -static const struct vcap_typegroup * +const struct vcap_typegroup * vcap_actionfield_typegroup(struct vcap_control *vctrl, enum vcap_type vt, enum vcap_actionfield_set actionset) { @@ -355,9 +355,9 @@ vcap_actionfield_typegroup(struct vcap_control *vctrl, } /* Return the number of actionfields in the actionset */ -static int vcap_actionfield_count(struct vcap_control *vctrl, - enum vcap_type vt, - enum vcap_actionfield_set actionset) +int vcap_actionfield_count(struct vcap_control *vctrl, + enum vcap_type vt, + enum vcap_actionfield_set actionset) { /* Check that the actionset exists in the vcap actionset list */ if (actionset >= vctrl->vcaps[vt].actionfield_set_size) diff --git a/drivers/net/ethernet/microchip/vcap/vcap_api_debugfs.c b/drivers/net/ethernet/microchip/vcap/vcap_api_debugfs.c index b4bc32a08f2c..981c4ed6ad7d 100644 --- a/drivers/net/ethernet/microchip/vcap/vcap_api_debugfs.c +++ b/drivers/net/ethernet/microchip/vcap/vcap_api_debugfs.c @@ -120,6 +120,28 @@ static int vcap_find_keystream_typegroup_sw(struct vcap_control *vctrl, return -EINVAL; } +/* Find the subword width of the action typegroup that matches the stream data + */ +static int vcap_find_actionstream_typegroup_sw(struct vcap_control *vctrl, + enum vcap_type vt, u32 *stream, + int sw_max) +{ + const struct vcap_typegroup **tgt; + int sw_idx, res; + + tgt = vctrl->vcaps[vt].actionfield_set_typegroups; + /* Try the longest subword match first */ + for (sw_idx = vctrl->vcaps[vt].sw_count; sw_idx >= 0; sw_idx--) { + if (!tgt[sw_idx]) + continue; + res = vcap_verify_typegroups(stream, vctrl->vcaps[vt].act_width, + tgt[sw_idx], false, sw_max); + if (res == 0) + return sw_idx; + } + return -EINVAL; +} + /* Verify that the type id in the stream matches the type id of the keyset */ static bool vcap_verify_keystream_keyset(struct vcap_control *vctrl, enum vcap_type vt, @@ -205,6 +227,75 @@ vcap_keyfield_set vcap_find_keystream_keyset(struct vcap_control *vctrl, return -EINVAL; } +/* Read key data from a VCAP address and discover if there is a rule keyset + * here + */ +static bool +vcap_verify_actionstream_actionset(struct vcap_control *vctrl, + enum vcap_type vt, + u32 *actionstream, + enum vcap_actionfield_set actionset) +{ + const struct vcap_typegroup *tgt; + const struct vcap_field *fields; + const struct vcap_set *info; + + if (vcap_actionfield_count(vctrl, vt, actionset) == 0) + return false; + + info = vcap_actionfieldset(vctrl, vt, actionset); + /* Check that the actionset is valid */ + if (!info) + return false; + + /* a type_id of value -1 means that there is no type field */ + if (info->type_id == (u8)-1) + return true; + + /* Get a valid typegroup for the specific actionset */ + tgt = vcap_actionfield_typegroup(vctrl, vt, actionset); + if (!tgt) + return false; + + fields = vcap_actionfields(vctrl, vt, actionset); + if (!fields) + return false; + + /* Later this will be expanded with a check of the type id */ + return true; +} + +/* Verify that the typegroup information, subword count, actionset and type id + * are in sync and correct, return the actionset + */ +static enum vcap_actionfield_set +vcap_find_actionstream_actionset(struct vcap_control *vctrl, + enum vcap_type vt, + u32 *stream, + int sw_max) +{ + const struct vcap_set *actionfield_set; + int sw_count, idx; + bool res; + + sw_count = vcap_find_actionstream_typegroup_sw(vctrl, vt, stream, + sw_max); + if (sw_count < 0) + return sw_count; + + actionfield_set = vctrl->vcaps[vt].actionfield_set; + for (idx = 0; idx < vctrl->vcaps[vt].actionfield_set_size; ++idx) { + if (actionfield_set[idx].sw_per_item != sw_count) + continue; + + res = vcap_verify_actionstream_actionset(vctrl, vt, + stream, idx); + if (res) + return idx; + } + return -EINVAL; +} + /* Read key data from a VCAP address and discover if there is a rule keyset * here */ @@ -265,6 +356,224 @@ static int vcap_read_rule(struct vcap_rule_internal *ri) return 0; } +/* Dump the keyfields value and mask values */ +static void vcap_debugfs_show_rule_keyfield(struct vcap_control *vctrl, + struct vcap_output_print *out, + enum vcap_key_field key, + const struct vcap_field *keyfield, + u8 *value, u8 *mask) +{ + bool hex = false; + int idx, bytes; + + out->prf(out->dst, " %s: W%d: ", vcap_keyfield_name(vctrl, key), + keyfield[key].width); + + switch (keyfield[key].type) { + case VCAP_FIELD_BIT: + out->prf(out->dst, "%d/%d", value[0], mask[0]); + break; + case VCAP_FIELD_U32: + if (key == VCAP_KF_L3_IP4_SIP || key == VCAP_KF_L3_IP4_DIP) { + out->prf(out->dst, "%pI4h/%pI4h", value, mask); + } else if (key == VCAP_KF_ETYPE || + key == VCAP_KF_IF_IGR_PORT_MASK) { + hex = true; + } else { + u32 fmsk = (1 << keyfield[key].width) - 1; + u32 val = *(u32 *)value; + u32 msk = *(u32 *)mask; + + out->prf(out->dst, "%u/%u", val & fmsk, msk & fmsk); + } + break; + case VCAP_FIELD_U48: + if (key == VCAP_KF_L2_SMAC || key == VCAP_KF_L2_DMAC) + out->prf(out->dst, "%pMR/%pMR", value, mask); + else + hex = true; + break; + case VCAP_FIELD_U56: + case VCAP_FIELD_U64: + case VCAP_FIELD_U72: + case VCAP_FIELD_U112: + hex = true; + break; + case VCAP_FIELD_U128: + if (key == VCAP_KF_L3_IP6_SIP || key == VCAP_KF_L3_IP6_DIP) { + u8 nvalue[16], nmask[16]; + + vcap_netbytes_copy(nvalue, value, sizeof(nvalue)); + vcap_netbytes_copy(nmask, mask, sizeof(nmask)); + out->prf(out->dst, "%pI6/%pI6", nvalue, nmask); + } else { + hex = true; + } + break; + } + if (hex) { + bytes = DIV_ROUND_UP(keyfield[key].width, BITS_PER_BYTE); + out->prf(out->dst, "0x"); + for (idx = 0; idx < bytes; ++idx) + out->prf(out->dst, "%02x", value[bytes - idx - 1]); + out->prf(out->dst, "/0x"); + for (idx = 0; idx < bytes; ++idx) + out->prf(out->dst, "%02x", mask[bytes - idx - 1]); + } + out->prf(out->dst, "\n"); +} + +static void +vcap_debugfs_show_rule_actionfield(struct vcap_control *vctrl, + struct vcap_output_print *out, + enum vcap_action_field action, + const struct vcap_field *actionfield, + u8 *value) +{ + bool hex = false; + int idx, bytes; + u32 fmsk, val; + + out->prf(out->dst, " %s: W%d: ", + vcap_actionfield_name(vctrl, action), + actionfield[action].width); + + switch (actionfield[action].type) { + case VCAP_FIELD_BIT: + out->prf(out->dst, "%d", value[0]); + break; + case VCAP_FIELD_U32: + fmsk = (1 << actionfield[action].width) - 1; + val = *(u32 *)value; + out->prf(out->dst, "%u", val & fmsk); + break; + case VCAP_FIELD_U48: + case VCAP_FIELD_U56: + case VCAP_FIELD_U64: + case VCAP_FIELD_U72: + case VCAP_FIELD_U112: + case VCAP_FIELD_U128: + hex = true; + break; + } + if (hex) { + bytes = DIV_ROUND_UP(actionfield[action].width, BITS_PER_BYTE); + out->prf(out->dst, "0x"); + for (idx = 0; idx < bytes; ++idx) + out->prf(out->dst, "%02x", value[bytes - idx - 1]); + } + out->prf(out->dst, "\n"); +} + +static int vcap_debugfs_show_rule_keyset(struct vcap_rule_internal *ri, + struct vcap_output_print *out) +{ + struct vcap_control *vctrl = ri->vctrl; + struct vcap_stream_iter kiter, miter; + struct vcap_admin *admin = ri->admin; + const struct vcap_field *keyfield; + enum vcap_type vt = admin->vtype; + const struct vcap_typegroup *tgt; + enum vcap_keyfield_set keyset; + int idx, res, keyfield_count; + u32 *maskstream; + u32 *keystream; + u8 value[16]; + u8 mask[16]; + + keystream = admin->cache.keystream; + maskstream = admin->cache.maskstream; + res = vcap_find_keystream_keyset(vctrl, vt, keystream, maskstream, + false, 0); + if (res < 0) { + pr_err("%s:%d: could not find valid keyset: %d\n", + __func__, __LINE__, res); + return -EINVAL; + } + keyset = res; + out->prf(out->dst, " keyset: %s\n", + vcap_keyset_name(vctrl, ri->data.keyset)); + out->prf(out->dst, " keyset_sw: %d\n", ri->keyset_sw); + out->prf(out->dst, " keyset_sw_regs: %d\n", ri->keyset_sw_regs); + keyfield_count = vcap_keyfield_count(vctrl, vt, keyset); + keyfield = vcap_keyfields(vctrl, vt, keyset); + tgt = vcap_keyfield_typegroup(vctrl, vt, keyset); + /* Start decoding the streams */ + for (idx = 0; idx < keyfield_count; ++idx) { + if (keyfield[idx].width <= 0) + continue; + /* First get the mask */ + memset(mask, 0, DIV_ROUND_UP(keyfield[idx].width, 8)); + vcap_iter_init(&miter, vctrl->vcaps[vt].sw_width, tgt, + keyfield[idx].offset); + vcap_decode_field(maskstream, &miter, keyfield[idx].width, + mask); + /* Skip if no mask bits are set */ + if (vcap_bitarray_zero(keyfield[idx].width, mask)) + continue; + /* Get the key */ + memset(value, 0, DIV_ROUND_UP(keyfield[idx].width, 8)); + vcap_iter_init(&kiter, vctrl->vcaps[vt].sw_width, tgt, + keyfield[idx].offset); + vcap_decode_field(keystream, &kiter, keyfield[idx].width, + value); + vcap_debugfs_show_rule_keyfield(vctrl, out, idx, keyfield, + value, mask); + } + return 0; +} + +static int vcap_debugfs_show_rule_actionset(struct vcap_rule_internal *ri, + struct vcap_output_print *out) +{ + struct vcap_control *vctrl = ri->vctrl; + struct vcap_admin *admin = ri->admin; + const struct vcap_field *actionfield; + enum vcap_actionfield_set actionset; + enum vcap_type vt = admin->vtype; + const struct vcap_typegroup *tgt; + struct vcap_stream_iter iter; + int idx, res, actfield_count; + u32 *actstream; + u8 value[16]; + bool no_bits; + + actstream = admin->cache.actionstream; + res = vcap_find_actionstream_actionset(vctrl, vt, actstream, 0); + if (res < 0) { + pr_err("%s:%d: could not find valid actionset: %d\n", + __func__, __LINE__, res); + return -EINVAL; + } + actionset = res; + out->prf(out->dst, " actionset: %s\n", + vcap_actionset_name(vctrl, ri->data.actionset)); + out->prf(out->dst, " actionset_sw: %d\n", ri->actionset_sw); + out->prf(out->dst, " actionset_sw_regs: %d\n", ri->actionset_sw_regs); + actfield_count = vcap_actionfield_count(vctrl, vt, actionset); + actionfield = vcap_actionfields(vctrl, vt, actionset); + tgt = vcap_actionfield_typegroup(vctrl, vt, actionset); + /* Start decoding the stream */ + for (idx = 0; idx < actfield_count; ++idx) { + if (actionfield[idx].width <= 0) + continue; + /* Get the action */ + memset(value, 0, DIV_ROUND_UP(actionfield[idx].width, 8)); + vcap_iter_init(&iter, vctrl->vcaps[vt].act_width, tgt, + actionfield[idx].offset); + vcap_decode_field(actstream, &iter, actionfield[idx].width, + value); + /* Skip if no bits are set */ + no_bits = vcap_bitarray_zero(actionfield[idx].width, value); + if (no_bits) + continue; + /* Later the action id will also be checked */ + vcap_debugfs_show_rule_actionfield(vctrl, out, idx, actionfield, + value); + } + return 0; +} + static void vcap_show_admin_rule(struct vcap_control *vctrl, struct vcap_admin *admin, struct vcap_output_print *out, @@ -279,10 +588,8 @@ static void vcap_show_admin_rule(struct vcap_control *vctrl, out->prf(out->dst, " chain_id: %d\n", ri->data.vcap_chain_id); out->prf(out->dst, " user: %d\n", ri->data.user); out->prf(out->dst, " priority: %d\n", ri->data.priority); - out->prf(out->dst, " keyset: %s\n", - vcap_keyset_name(vctrl, ri->data.keyset)); - out->prf(out->dst, " actionset: %s\n", - vcap_actionset_name(vctrl, ri->data.actionset)); + vcap_debugfs_show_rule_keyset(ri, out); + vcap_debugfs_show_rule_actionset(ri, out); } static void vcap_show_admin_info(struct vcap_control *vctrl, diff --git a/drivers/net/ethernet/microchip/vcap/vcap_api_private.h b/drivers/net/ethernet/microchip/vcap/vcap_api_private.h index 57309de463d7..18a9a0cd9606 100644 --- a/drivers/net/ethernet/microchip/vcap/vcap_api_private.h +++ b/drivers/net/ethernet/microchip/vcap/vcap_api_private.h @@ -77,6 +77,22 @@ const struct vcap_field *vcap_keyfields(struct vcap_control *vctrl, /* Actionset and actionfield functionality */ +/* Return the actionset information for the actionset */ +const struct vcap_set * +vcap_actionfieldset(struct vcap_control *vctrl, + enum vcap_type vt, enum vcap_actionfield_set actionset); +/* Return the number of actionfields in the actionset */ +int vcap_actionfield_count(struct vcap_control *vctrl, + enum vcap_type vt, + enum vcap_actionfield_set actionset); +/* Return the typegroup table for the matching actionset (using subword size) */ +const struct vcap_typegroup * +vcap_actionfield_typegroup(struct vcap_control *vctrl, enum vcap_type vt, + enum vcap_actionfield_set actionset); +/* Return the list of actionfields for the actionset */ +const struct vcap_field * +vcap_actionfields(struct vcap_control *vctrl, + enum vcap_type vt, enum vcap_actionfield_set actionset); /* Map actionset id to a string with the actionset name */ const char *vcap_actionset_name(struct vcap_control *vctrl, enum vcap_actionfield_set actionset); From 71c9de995260222e739020104d477af4775a6d26 Mon Sep 17 00:00:00 2001 From: Steen Hegelund Date: Thu, 17 Nov 2022 22:31:13 +0100 Subject: [PATCH 1118/2002] net: microchip: sparx5: Add VCAP locking to protect rules This ensures that the VCAP cache and the lists maintained in the VCAP instance is protected when accessed by different clients. Signed-off-by: Steen Hegelund Signed-off-by: David S. Miller --- .../net/ethernet/microchip/sparx5/sparx5_vcap_impl.c | 2 ++ drivers/net/ethernet/microchip/vcap/vcap_api.c | 10 ++++++++++ drivers/net/ethernet/microchip/vcap/vcap_api.h | 1 + drivers/net/ethernet/microchip/vcap/vcap_api_debugfs.c | 2 ++ 4 files changed, 15 insertions(+) diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_vcap_impl.c b/drivers/net/ethernet/microchip/sparx5/sparx5_vcap_impl.c index e70ff1aa6d57..0c4d4e6d51e6 100644 --- a/drivers/net/ethernet/microchip/sparx5/sparx5_vcap_impl.c +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_vcap_impl.c @@ -579,6 +579,7 @@ static void sparx5_vcap_admin_free(struct vcap_admin *admin) { if (!admin) return; + mutex_destroy(&admin->lock); kfree(admin->cache.keystream); kfree(admin->cache.maskstream); kfree(admin->cache.actionstream); @@ -598,6 +599,7 @@ sparx5_vcap_admin_alloc(struct sparx5 *sparx5, struct vcap_control *ctrl, INIT_LIST_HEAD(&admin->list); INIT_LIST_HEAD(&admin->rules); INIT_LIST_HEAD(&admin->enabled); + mutex_init(&admin->lock); admin->vtype = cfg->vtype; admin->vinst = cfg->vinst; admin->lookups = cfg->lookups; diff --git a/drivers/net/ethernet/microchip/vcap/vcap_api.c b/drivers/net/ethernet/microchip/vcap/vcap_api.c index 3415605350c9..ac7a32ff755e 100644 --- a/drivers/net/ethernet/microchip/vcap/vcap_api.c +++ b/drivers/net/ethernet/microchip/vcap/vcap_api.c @@ -1054,6 +1054,7 @@ int vcap_add_rule(struct vcap_rule *rule) if (ret) return ret; /* Insert the new rule in the list of vcap rules */ + mutex_lock(&ri->admin->lock); ret = vcap_insert_rule(ri, &move); if (ret < 0) { pr_err("%s:%d: could not insert rule in vcap list: %d\n", @@ -1072,6 +1073,7 @@ int vcap_add_rule(struct vcap_rule *rule) if (ret) pr_err("%s:%d: rule write error: %d\n", __func__, __LINE__, ret); out: + mutex_unlock(&ri->admin->lock); return ret; } EXPORT_SYMBOL_GPL(vcap_add_rule); @@ -1221,9 +1223,11 @@ int vcap_del_rule(struct vcap_control *vctrl, struct net_device *ndev, u32 id) gap = vcap_fill_rule_gap(ri); /* Delete the rule from the list of rules and the cache */ + mutex_lock(&admin->lock); list_del(&ri->list); vctrl->ops->init(ndev, admin, admin->last_used_addr, ri->size + gap); kfree(ri); + mutex_unlock(&admin->lock); /* Update the last used address, set to default when no rules */ if (list_empty(&admin->rules)) { @@ -1246,6 +1250,8 @@ int vcap_del_rules(struct vcap_control *vctrl, struct vcap_admin *admin) if (ret) return ret; + + mutex_lock(&admin->lock); list_for_each_entry_safe(ri, next_ri, &admin->rules, list) { vctrl->ops->init(ri->ndev, admin, ri->addr, ri->size); list_del(&ri->list); @@ -1258,6 +1264,7 @@ int vcap_del_rules(struct vcap_control *vctrl, struct vcap_admin *admin) list_del(&eport->list); kfree(eport); } + mutex_unlock(&admin->lock); return 0; } @@ -1687,10 +1694,13 @@ int vcap_enable_lookups(struct vcap_control *vctrl, struct net_device *ndev, if (chain_id) { if (vcap_is_enabled(admin, ndev, cookie)) return -EADDRINUSE; + mutex_lock(&admin->lock); vcap_enable(admin, ndev, cookie); } else { + mutex_lock(&admin->lock); vcap_disable(admin, ndev, cookie); } + mutex_unlock(&admin->lock); return 0; } diff --git a/drivers/net/ethernet/microchip/vcap/vcap_api.h b/drivers/net/ethernet/microchip/vcap/vcap_api.h index e71e7d3d79c2..f4a5ba5ffa87 100644 --- a/drivers/net/ethernet/microchip/vcap/vcap_api.h +++ b/drivers/net/ethernet/microchip/vcap/vcap_api.h @@ -167,6 +167,7 @@ struct vcap_admin { struct list_head list; /* for insertion in vcap_control */ struct list_head rules; /* list of rules */ struct list_head enabled; /* list of enabled ports */ + struct mutex lock; /* control access to rules */ enum vcap_type vtype; /* type of vcap */ int vinst; /* instance number within the same type */ int first_cid; /* first chain id in this vcap */ diff --git a/drivers/net/ethernet/microchip/vcap/vcap_api_debugfs.c b/drivers/net/ethernet/microchip/vcap/vcap_api_debugfs.c index 981c4ed6ad7d..891034e349de 100644 --- a/drivers/net/ethernet/microchip/vcap/vcap_api_debugfs.c +++ b/drivers/net/ethernet/microchip/vcap/vcap_api_debugfs.c @@ -625,6 +625,7 @@ static int vcap_show_admin(struct vcap_control *vctrl, int ret = 0; vcap_show_admin_info(vctrl, admin, out); + mutex_lock(&admin->lock); list_for_each_entry(elem, &admin->rules, list) { ri = vcap_dup_rule(elem); if (IS_ERR(ri)) @@ -638,6 +639,7 @@ static int vcap_show_admin(struct vcap_control *vctrl, free_rule: vcap_free_rule((struct vcap_rule *)ri); } + mutex_unlock(&admin->lock); return ret; } From 552b7d131aa01bcb046fb635d66e256c3e7e13ee Mon Sep 17 00:00:00 2001 From: Steen Hegelund Date: Thu, 17 Nov 2022 22:31:14 +0100 Subject: [PATCH 1119/2002] net: microchip: sparx5: Add VCAP debugfs KUNIT test This tests the functionality of the debugFS support: - finding valid keyset on an address - raw VCAP output - full rule VCAP output Signed-off-by: Steen Hegelund Signed-off-by: David S. Miller --- drivers/net/ethernet/microchip/vcap/Kconfig | 1 + .../microchip/vcap/vcap_api_debugfs.c | 4 + .../microchip/vcap/vcap_api_debugfs_kunit.c | 545 ++++++++++++++++++ .../ethernet/microchip/vcap/vcap_api_kunit.c | 6 +- 4 files changed, 553 insertions(+), 3 deletions(-) create mode 100644 drivers/net/ethernet/microchip/vcap/vcap_api_debugfs_kunit.c diff --git a/drivers/net/ethernet/microchip/vcap/Kconfig b/drivers/net/ethernet/microchip/vcap/Kconfig index 1af30a358a15..97f43fd4473f 100644 --- a/drivers/net/ethernet/microchip/vcap/Kconfig +++ b/drivers/net/ethernet/microchip/vcap/Kconfig @@ -40,6 +40,7 @@ config VCAP_KUNIT_TEST bool "KUnit test for VCAP library" if !KUNIT_ALL_TESTS depends on KUNIT depends on KUNIT=y && VCAP=y && y + select DEBUG_FS default KUNIT_ALL_TESTS help This builds unit tests for the VCAP library. diff --git a/drivers/net/ethernet/microchip/vcap/vcap_api_debugfs.c b/drivers/net/ethernet/microchip/vcap/vcap_api_debugfs.c index 891034e349de..d9c7ca988b76 100644 --- a/drivers/net/ethernet/microchip/vcap/vcap_api_debugfs.c +++ b/drivers/net/ethernet/microchip/vcap/vcap_api_debugfs.c @@ -776,3 +776,7 @@ struct dentry *vcap_debugfs(struct device *dev, struct dentry *parent, return dir; } EXPORT_SYMBOL_GPL(vcap_debugfs); + +#ifdef CONFIG_VCAP_KUNIT_TEST +#include "vcap_api_debugfs_kunit.c" +#endif diff --git a/drivers/net/ethernet/microchip/vcap/vcap_api_debugfs_kunit.c b/drivers/net/ethernet/microchip/vcap/vcap_api_debugfs_kunit.c new file mode 100644 index 000000000000..ed455dad3a14 --- /dev/null +++ b/drivers/net/ethernet/microchip/vcap/vcap_api_debugfs_kunit.c @@ -0,0 +1,545 @@ +// SPDX-License-Identifier: BSD-3-Clause +/* Copyright (C) 2022 Microchip Technology Inc. and its subsidiaries. + * Microchip VCAP API kunit test suite + */ + +#include +#include "vcap_api.h" +#include "vcap_api_client.h" +#include "vcap_api_debugfs.h" +#include "vcap_model_kunit.h" + +/* First we have the test infrastructure that emulates the platform + * implementation + */ +#define TEST_BUF_CNT 100 +#define TEST_BUF_SZ 350 +#define STREAMWSIZE 64 + +static u32 test_updateaddr[STREAMWSIZE] = {}; +static int test_updateaddridx; +static int test_cache_erase_count; +static u32 test_init_start; +static u32 test_init_count; +static u32 test_hw_counter_id; +static struct vcap_cache_data test_hw_cache; +static struct net_device test_netdev = {}; +static int test_move_addr; +static int test_move_offset; +static int test_move_count; +static char test_pr_buffer[TEST_BUF_CNT][TEST_BUF_SZ]; +static int test_pr_bufferidx; +static int test_pr_idx; + +/* Callback used by the VCAP API */ +static enum vcap_keyfield_set test_val_keyset(struct net_device *ndev, + struct vcap_admin *admin, + struct vcap_rule *rule, + struct vcap_keyset_list *kslist, + u16 l3_proto) +{ + int idx; + + if (kslist->cnt > 0) { + switch (admin->vtype) { + case VCAP_TYPE_IS0: + for (idx = 0; idx < kslist->cnt; idx++) { + if (kslist->keysets[idx] == VCAP_KFS_ETAG) + return kslist->keysets[idx]; + if (kslist->keysets[idx] == + VCAP_KFS_PURE_5TUPLE_IP4) + return kslist->keysets[idx]; + if (kslist->keysets[idx] == + VCAP_KFS_NORMAL_5TUPLE_IP4) + return kslist->keysets[idx]; + if (kslist->keysets[idx] == + VCAP_KFS_NORMAL_7TUPLE) + return kslist->keysets[idx]; + } + break; + case VCAP_TYPE_IS2: + for (idx = 0; idx < kslist->cnt; idx++) { + if (kslist->keysets[idx] == VCAP_KFS_MAC_ETYPE) + return kslist->keysets[idx]; + if (kslist->keysets[idx] == VCAP_KFS_ARP) + return kslist->keysets[idx]; + if (kslist->keysets[idx] == VCAP_KFS_IP_7TUPLE) + return kslist->keysets[idx]; + } + break; + default: + pr_info("%s:%d: no validation for VCAP %d\n", + __func__, __LINE__, admin->vtype); + break; + } + } + return -EINVAL; +} + +/* Callback used by the VCAP API */ +static void test_add_def_fields(struct net_device *ndev, + struct vcap_admin *admin, + struct vcap_rule *rule) +{ + if (admin->vinst == 0 || admin->vinst == 2) + vcap_rule_add_key_bit(rule, VCAP_KF_LOOKUP_FIRST_IS, + VCAP_BIT_1); + else + vcap_rule_add_key_bit(rule, VCAP_KF_LOOKUP_FIRST_IS, + VCAP_BIT_0); +} + +/* Callback used by the VCAP API */ +static void test_cache_erase(struct vcap_admin *admin) +{ + if (test_cache_erase_count) { + memset(admin->cache.keystream, 0, test_cache_erase_count); + memset(admin->cache.maskstream, 0, test_cache_erase_count); + memset(admin->cache.actionstream, 0, test_cache_erase_count); + test_cache_erase_count = 0; + } +} + +/* Callback used by the VCAP API */ +static void test_cache_init(struct net_device *ndev, struct vcap_admin *admin, + u32 start, u32 count) +{ + test_init_start = start; + test_init_count = count; +} + +/* Callback used by the VCAP API */ +static void test_cache_read(struct net_device *ndev, struct vcap_admin *admin, + enum vcap_selection sel, u32 start, u32 count) +{ + u32 *keystr, *mskstr, *actstr; + int idx; + + pr_debug("%s:%d: %d %d\n", __func__, __LINE__, start, count); + switch (sel) { + case VCAP_SEL_ENTRY: + keystr = &admin->cache.keystream[start]; + mskstr = &admin->cache.maskstream[start]; + for (idx = 0; idx < count; ++idx) { + pr_debug("%s:%d: keydata[%02d]: 0x%08x\n", __func__, + __LINE__, start + idx, keystr[idx]); + } + for (idx = 0; idx < count; ++idx) { + /* Invert the mask before decoding starts */ + mskstr[idx] = ~mskstr[idx]; + pr_debug("%s:%d: mskdata[%02d]: 0x%08x\n", __func__, + __LINE__, start + idx, mskstr[idx]); + } + break; + case VCAP_SEL_ACTION: + actstr = &admin->cache.actionstream[start]; + for (idx = 0; idx < count; ++idx) { + pr_debug("%s:%d: actdata[%02d]: 0x%08x\n", __func__, + __LINE__, start + idx, actstr[idx]); + } + break; + case VCAP_SEL_COUNTER: + pr_debug("%s:%d\n", __func__, __LINE__); + test_hw_counter_id = start; + admin->cache.counter = test_hw_cache.counter; + admin->cache.sticky = test_hw_cache.sticky; + break; + case VCAP_SEL_ALL: + pr_debug("%s:%d\n", __func__, __LINE__); + break; + } +} + +/* Callback used by the VCAP API */ +static void test_cache_write(struct net_device *ndev, struct vcap_admin *admin, + enum vcap_selection sel, u32 start, u32 count) +{ + u32 *keystr, *mskstr, *actstr; + int idx; + + switch (sel) { + case VCAP_SEL_ENTRY: + keystr = &admin->cache.keystream[start]; + mskstr = &admin->cache.maskstream[start]; + for (idx = 0; idx < count; ++idx) { + pr_debug("%s:%d: keydata[%02d]: 0x%08x\n", __func__, + __LINE__, start + idx, keystr[idx]); + } + for (idx = 0; idx < count; ++idx) { + /* Invert the mask before encoding starts */ + mskstr[idx] = ~mskstr[idx]; + pr_debug("%s:%d: mskdata[%02d]: 0x%08x\n", __func__, + __LINE__, start + idx, mskstr[idx]); + } + break; + case VCAP_SEL_ACTION: + actstr = &admin->cache.actionstream[start]; + for (idx = 0; idx < count; ++idx) { + pr_debug("%s:%d: actdata[%02d]: 0x%08x\n", __func__, + __LINE__, start + idx, actstr[idx]); + } + break; + case VCAP_SEL_COUNTER: + pr_debug("%s:%d\n", __func__, __LINE__); + test_hw_counter_id = start; + test_hw_cache.counter = admin->cache.counter; + test_hw_cache.sticky = admin->cache.sticky; + break; + case VCAP_SEL_ALL: + pr_err("%s:%d: cannot write all streams at once\n", + __func__, __LINE__); + break; + } +} + +/* Callback used by the VCAP API */ +static void test_cache_update(struct net_device *ndev, struct vcap_admin *admin, + enum vcap_command cmd, + enum vcap_selection sel, u32 addr) +{ + if (test_updateaddridx < ARRAY_SIZE(test_updateaddr)) + test_updateaddr[test_updateaddridx] = addr; + else + pr_err("%s:%d: overflow: %d\n", __func__, __LINE__, + test_updateaddridx); + test_updateaddridx++; +} + +static void test_cache_move(struct net_device *ndev, struct vcap_admin *admin, + u32 addr, int offset, int count) +{ + test_move_addr = addr; + test_move_offset = offset; + test_move_count = count; +} + +/* Provide port information via a callback interface */ +static int vcap_test_port_info(struct net_device *ndev, + struct vcap_admin *admin, + struct vcap_output_print *out) +{ + return 0; +} + +static int vcap_test_enable(struct net_device *ndev, + struct vcap_admin *admin, + bool enable) +{ + return 0; +} + +static struct vcap_operations test_callbacks = { + .validate_keyset = test_val_keyset, + .add_default_fields = test_add_def_fields, + .cache_erase = test_cache_erase, + .cache_write = test_cache_write, + .cache_read = test_cache_read, + .init = test_cache_init, + .update = test_cache_update, + .move = test_cache_move, + .port_info = vcap_test_port_info, + .enable = vcap_test_enable, +}; + +static struct vcap_control test_vctrl = { + .vcaps = kunit_test_vcaps, + .stats = &kunit_test_vcap_stats, + .ops = &test_callbacks, +}; + +static void vcap_test_api_init(struct vcap_admin *admin) +{ + /* Initialize the shared objects */ + INIT_LIST_HEAD(&test_vctrl.list); + INIT_LIST_HEAD(&admin->list); + INIT_LIST_HEAD(&admin->rules); + list_add_tail(&admin->list, &test_vctrl.list); + memset(test_updateaddr, 0, sizeof(test_updateaddr)); + test_updateaddridx = 0; + test_pr_bufferidx = 0; + test_pr_idx = 0; +} + +/* callback used by the show_admin function */ +static __printf(2, 3) +int test_prf(void *out, const char *fmt, ...) +{ + static char test_buffer[TEST_BUF_SZ]; + va_list args; + int idx, cnt; + + if (test_pr_bufferidx >= TEST_BUF_CNT) { + pr_err("%s:%d: overflow: %d\n", __func__, __LINE__, + test_pr_bufferidx); + return 0; + } + + va_start(args, fmt); + cnt = vscnprintf(test_buffer, TEST_BUF_SZ, fmt, args); + va_end(args); + + for (idx = 0; idx < cnt; ++idx) { + test_pr_buffer[test_pr_bufferidx][test_pr_idx] = + test_buffer[idx]; + if (test_buffer[idx] == '\n') { + test_pr_buffer[test_pr_bufferidx][++test_pr_idx] = 0; + test_pr_idx = 0; + test_pr_bufferidx++; + } else { + ++test_pr_idx; + } + } + + return cnt; +} + +/* Define the test cases. */ + +static void vcap_api_addr_keyset_test(struct kunit *test) +{ + u32 keydata[12] = { + 0x40450042, 0x000feaf3, 0x00000003, 0x00050600, + 0x10203040, 0x00075880, 0x633c6864, 0x00040003, + 0x00000020, 0x00000008, 0x00000240, 0x00000000, + }; + u32 mskdata[12] = { + 0x0030ff80, 0xfff00000, 0xfffffffc, 0xfff000ff, + 0x00000000, 0xfff00000, 0x00000000, 0xfff3fffc, + 0xffffffc0, 0xffffffff, 0xfffffc03, 0xffffffff, + }; + u32 actdata[12] = {}; + struct vcap_admin admin = { + .vtype = VCAP_TYPE_IS2, + .cache = { + .keystream = keydata, + .maskstream = mskdata, + .actionstream = actdata, + }, + }; + int ret, idx, addr; + + vcap_test_api_init(&admin); + + /* Go from higher to lower addresses searching for a keyset */ + for (idx = ARRAY_SIZE(keydata) - 1, addr = 799; idx > 0; + --idx, --addr) { + admin.cache.keystream = &keydata[idx]; + admin.cache.maskstream = &mskdata[idx]; + ret = vcap_addr_keyset(&test_vctrl, &test_netdev, &admin, addr); + KUNIT_EXPECT_EQ(test, -EINVAL, ret); + } + + /* Finally we hit the start of the rule */ + admin.cache.keystream = &keydata[idx]; + admin.cache.maskstream = &mskdata[idx]; + ret = vcap_addr_keyset(&test_vctrl, &test_netdev, &admin, addr); + KUNIT_EXPECT_EQ(test, VCAP_KFS_MAC_ETYPE, ret); +} + +static void vcap_api_show_admin_raw_test(struct kunit *test) +{ + u32 keydata[4] = { + 0x40450042, 0x000feaf3, 0x00000003, 0x00050600, + }; + u32 mskdata[4] = { + 0x0030ff80, 0xfff00000, 0xfffffffc, 0xfff000ff, + }; + u32 actdata[12] = {}; + struct vcap_admin admin = { + .vtype = VCAP_TYPE_IS2, + .cache = { + .keystream = keydata, + .maskstream = mskdata, + .actionstream = actdata, + }, + .first_valid_addr = 786, + .last_valid_addr = 788, + }; + struct vcap_rule_internal ri = { + .ndev = &test_netdev, + }; + struct vcap_output_print out = { + .prf = (void *)test_prf, + }; + const char *test_expected = + " addr: 786, X6 rule, keyset: VCAP_KFS_MAC_ETYPE\n"; + int ret; + + vcap_test_api_init(&admin); + list_add_tail(&ri.list, &admin.rules); + + ret = vcap_show_admin_raw(&test_vctrl, &admin, &out); + KUNIT_EXPECT_EQ(test, 0, ret); + KUNIT_EXPECT_STREQ(test, test_expected, test_pr_buffer[0]); +} + +static const char * const test_admin_info_expect[] = { + "name: is2\n", + "rows: 256\n", + "sw_count: 12\n", + "sw_width: 52\n", + "sticky_width: 1\n", + "act_width: 110\n", + "default_cnt: 73\n", + "require_cnt_dis: 0\n", + "version: 1\n", + "vtype: 2\n", + "vinst: 0\n", + "first_cid: 10000\n", + "last_cid: 19999\n", + "lookups: 4\n", + "first_valid_addr: 0\n", + "last_valid_addr: 3071\n", + "last_used_addr: 794\n", +}; + +static void vcap_api_show_admin_test(struct kunit *test) +{ + struct vcap_admin admin = { + .vtype = VCAP_TYPE_IS2, + .first_cid = 10000, + .last_cid = 19999, + .lookups = 4, + .last_valid_addr = 3071, + .first_valid_addr = 0, + .last_used_addr = 794, + }; + struct vcap_output_print out = { + .prf = (void *)test_prf, + }; + int idx; + + vcap_test_api_init(&admin); + + vcap_show_admin_info(&test_vctrl, &admin, &out); + for (idx = 0; idx < test_pr_bufferidx; ++idx) { + /* pr_info("log[%02d]: %s", idx, test_pr_buffer[idx]); */ + KUNIT_EXPECT_STREQ(test, test_admin_info_expect[idx], + test_pr_buffer[idx]); + } +} + +static const char * const test_admin_expect[] = { + "name: is2\n", + "rows: 256\n", + "sw_count: 12\n", + "sw_width: 52\n", + "sticky_width: 1\n", + "act_width: 110\n", + "default_cnt: 73\n", + "require_cnt_dis: 0\n", + "version: 1\n", + "vtype: 2\n", + "vinst: 0\n", + "first_cid: 8000000\n", + "last_cid: 8199999\n", + "lookups: 4\n", + "first_valid_addr: 0\n", + "last_valid_addr: 3071\n", + "last_used_addr: 794\n", + "\n", + "rule: 100, addr: [794,799], X6, ctr[0]: 0, hit: 0\n", + " chain_id: 0\n", + " user: 0\n", + " priority: 0\n", + " keyset: VCAP_KFS_MAC_ETYPE\n", + " keyset_sw: 6\n", + " keyset_sw_regs: 2\n", + " ETYPE_LEN_IS: W1: 1/1\n", + " IF_IGR_PORT_MASK: W32: 0xffabcd01/0xffffffff\n", + " IF_IGR_PORT_MASK_RNG: W4: 5/15\n", + " L2_DMAC: W48: 01:02:03:04:05:06/ff:ff:ff:ff:ff:ff\n", + " L2_PAYLOAD_ETYPE: W64: 0x9000002000000081/0xff000000000000ff\n", + " L2_SMAC: W48: b1:9e:34:32:75:88/ff:ff:ff:ff:ff:ff\n", + " LOOKUP_FIRST_IS: W1: 1/1\n", + " TYPE: W4: 0/15\n", + " actionset: VCAP_AFS_BASE_TYPE\n", + " actionset_sw: 3\n", + " actionset_sw_regs: 4\n", + " CNT_ID: W12: 100\n", + " MATCH_ID: W16: 1\n", + " MATCH_ID_MASK: W16: 1\n", + " POLICE_ENA: W1: 1\n", + " PORT_MASK: W68: 0x0514670115f3324589\n", +}; + +static void vcap_api_show_admin_rule_test(struct kunit *test) +{ + u32 keydata[] = { + 0x40450042, 0x000feaf3, 0x00000003, 0x00050600, + 0x10203040, 0x00075880, 0x633c6864, 0x00040003, + 0x00000020, 0x00000008, 0x00000240, 0x00000000, + }; + u32 mskdata[] = { + 0x0030ff80, 0xfff00000, 0xfffffffc, 0xfff000ff, + 0x00000000, 0xfff00000, 0x00000000, 0xfff3fffc, + 0xffffffc0, 0xffffffff, 0xfffffc03, 0xffffffff, + }; + u32 actdata[] = { + 0x00040002, 0xf3324589, 0x14670115, 0x00000005, + 0x00000000, 0x00100000, 0x06400010, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + }; + struct vcap_admin admin = { + .vtype = VCAP_TYPE_IS2, + .first_cid = 8000000, + .last_cid = 8199999, + .lookups = 4, + .last_valid_addr = 3071, + .first_valid_addr = 0, + .last_used_addr = 794, + .cache = { + .keystream = keydata, + .maskstream = mskdata, + .actionstream = actdata, + }, + }; + struct vcap_rule_internal ri = { + .admin = &admin, + .data = { + .id = 100, + .keyset = VCAP_KFS_MAC_ETYPE, + .actionset = VCAP_AFS_BASE_TYPE, + }, + .size = 6, + .keyset_sw = 6, + .keyset_sw_regs = 2, + .actionset_sw = 3, + .actionset_sw_regs = 4, + .addr = 794, + .vctrl = &test_vctrl, + }; + struct vcap_output_print out = { + .prf = (void *)test_prf, + }; + int ret, idx; + + vcap_test_api_init(&admin); + list_add_tail(&ri.list, &admin.rules); + + ret = vcap_show_admin(&test_vctrl, &admin, &out); + KUNIT_EXPECT_EQ(test, 0, ret); + for (idx = 0; idx < test_pr_bufferidx; ++idx) { + /* pr_info("log[%02d]: %s", idx, test_pr_buffer[idx]); */ + KUNIT_EXPECT_STREQ(test, test_admin_expect[idx], + test_pr_buffer[idx]); + } +} + +static struct kunit_case vcap_api_debugfs_test_cases[] = { + KUNIT_CASE(vcap_api_addr_keyset_test), + KUNIT_CASE(vcap_api_show_admin_raw_test), + KUNIT_CASE(vcap_api_show_admin_test), + KUNIT_CASE(vcap_api_show_admin_rule_test), + {} +}; + +static struct kunit_suite vcap_api_debugfs_test_suite = { + .name = "VCAP_API_DebugFS_Testsuite", + .test_cases = vcap_api_debugfs_test_cases, +}; + +kunit_test_suite(vcap_api_debugfs_test_suite); diff --git a/drivers/net/ethernet/microchip/vcap/vcap_api_kunit.c b/drivers/net/ethernet/microchip/vcap/vcap_api_kunit.c index 3c3de76b78b8..f48d93f374af 100644 --- a/drivers/net/ethernet/microchip/vcap/vcap_api_kunit.c +++ b/drivers/net/ethernet/microchip/vcap/vcap_api_kunit.c @@ -1691,7 +1691,7 @@ static void vcap_api_rule_remove_at_end_test(struct kunit *test) KUNIT_EXPECT_EQ(test, 0, test_move_count); KUNIT_EXPECT_EQ(test, 780, test_init_start); KUNIT_EXPECT_EQ(test, 12, test_init_count); - KUNIT_EXPECT_EQ(test, 3071, admin.last_used_addr); + KUNIT_EXPECT_EQ(test, 3072, admin.last_used_addr); } static void vcap_api_rule_remove_in_middle_test(struct kunit *test) @@ -1766,7 +1766,7 @@ static void vcap_api_rule_remove_in_middle_test(struct kunit *test) KUNIT_EXPECT_EQ(test, 0, test_move_count); KUNIT_EXPECT_EQ(test, 798, test_init_start); KUNIT_EXPECT_EQ(test, 2, test_init_count); - KUNIT_EXPECT_EQ(test, 799, admin.last_used_addr); + KUNIT_EXPECT_EQ(test, 800, admin.last_used_addr); } static void vcap_api_rule_remove_in_front_test(struct kunit *test) @@ -1805,7 +1805,7 @@ static void vcap_api_rule_remove_in_front_test(struct kunit *test) KUNIT_EXPECT_EQ(test, 0, test_move_count); KUNIT_EXPECT_EQ(test, 780, test_init_start); KUNIT_EXPECT_EQ(test, 12, test_init_count); - KUNIT_EXPECT_EQ(test, 799, admin.last_used_addr); + KUNIT_EXPECT_EQ(test, 800, admin.last_used_addr); test_vcap_xn_rule_creator(test, 10000, VCAP_USER_QOS, 20, 400, 6, 792); test_vcap_xn_rule_creator(test, 10000, VCAP_USER_QOS, 30, 300, 3, 789); From 1cb50726329070a6e3235b6bac38dfb8d5fd18c2 Mon Sep 17 00:00:00 2001 From: Yoshihiro Shimoda Date: Fri, 18 Nov 2022 09:27:24 +0900 Subject: [PATCH 1120/2002] net: ethernet: renesas: rswitch: Fix MAC address info Smatch detected the following warning. drivers/net/ethernet/renesas/rswitch.c:1717 rswitch_init() warn: '%pM' cannot be followed by 'n' The 'n' should be '\n'. Reported-by: Dan Carpenter Suggested-by: Geert Uytterhoeven Fixes: 3590918b5d07 ("net: ethernet: renesas: Add support for "Ethernet Switch"") Signed-off-by: Yoshihiro Shimoda Reviewed-by: Saeed Mahameed Signed-off-by: David S. Miller --- drivers/net/ethernet/renesas/rswitch.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/renesas/rswitch.c b/drivers/net/ethernet/renesas/rswitch.c index c098b27093ea..e42ceaa0099f 100644 --- a/drivers/net/ethernet/renesas/rswitch.c +++ b/drivers/net/ethernet/renesas/rswitch.c @@ -1714,7 +1714,7 @@ static int rswitch_init(struct rswitch_private *priv) } for (i = 0; i < RSWITCH_NUM_PORTS; i++) - netdev_info(priv->rdev[i]->ndev, "MAC address %pMn", + netdev_info(priv->rdev[i]->ndev, "MAC address %pM\n", priv->rdev[i]->ndev->dev_addr); return 0; From 7a7160edf1bfde25422262fb26851cef65f695d3 Mon Sep 17 00:00:00 2001 From: Kuniyuki Iwashima Date: Fri, 18 Nov 2022 10:25:06 -0800 Subject: [PATCH 1121/2002] net: Return errno in sk->sk_prot->get_port(). We assume the correct errno is -EADDRINUSE when sk->sk_prot->get_port() fails, so some ->get_port() functions return just 1 on failure and the callers return -EADDRINUSE instead. However, mptcp_get_port() can return -EINVAL. Let's not ignore the error. Note the only exception is inet_autobind(), all of whose callers return -EAGAIN instead. Fixes: cec37a6e41aa ("mptcp: Handle MP_CAPABLE options for outgoing connections") Signed-off-by: Kuniyuki Iwashima Signed-off-by: David S. Miller --- net/ipv4/af_inet.c | 4 ++-- net/ipv4/inet_connection_sock.c | 7 ++++--- net/ipv4/ping.c | 2 +- net/ipv4/udp.c | 2 +- net/ipv6/af_inet6.c | 4 ++-- 5 files changed, 10 insertions(+), 9 deletions(-) diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c index 378bcd777514..5b4d86701822 100644 --- a/net/ipv4/af_inet.c +++ b/net/ipv4/af_inet.c @@ -522,9 +522,9 @@ int __inet_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len, /* Make sure we are allowed to bind here. */ if (snum || !(inet->bind_address_no_port || (flags & BIND_FORCE_ADDRESS_NO_PORT))) { - if (sk->sk_prot->get_port(sk, snum)) { + err = sk->sk_prot->get_port(sk, snum); + if (err) { inet->inet_saddr = inet->inet_rcv_saddr = 0; - err = -EADDRINUSE; goto out_release_sock; } if (!(flags & BIND_FROM_BPF)) { diff --git a/net/ipv4/inet_connection_sock.c b/net/ipv4/inet_connection_sock.c index 4e84ed21d16f..4a34bc7cb15e 100644 --- a/net/ipv4/inet_connection_sock.c +++ b/net/ipv4/inet_connection_sock.c @@ -471,11 +471,11 @@ int inet_csk_get_port(struct sock *sk, unsigned short snum) bool reuse = sk->sk_reuse && sk->sk_state != TCP_LISTEN; bool found_port = false, check_bind_conflict = true; bool bhash_created = false, bhash2_created = false; + int ret = -EADDRINUSE, port = snum, l3mdev; struct inet_bind_hashbucket *head, *head2; struct inet_bind2_bucket *tb2 = NULL; struct inet_bind_bucket *tb = NULL; bool head2_lock_acquired = false; - int ret = 1, port = snum, l3mdev; struct net *net = sock_net(sk); l3mdev = inet_sk_bound_l3mdev(sk); @@ -1186,7 +1186,7 @@ int inet_csk_listen_start(struct sock *sk) { struct inet_connection_sock *icsk = inet_csk(sk); struct inet_sock *inet = inet_sk(sk); - int err = -EADDRINUSE; + int err; reqsk_queue_alloc(&icsk->icsk_accept_queue); @@ -1202,7 +1202,8 @@ int inet_csk_listen_start(struct sock *sk) * after validation is complete. */ inet_sk_state_store(sk, TCP_LISTEN); - if (!sk->sk_prot->get_port(sk, inet->inet_num)) { + err = sk->sk_prot->get_port(sk, inet->inet_num); + if (!err) { inet->inet_sport = htons(inet->inet_num); sk_dst_reset(sk); diff --git a/net/ipv4/ping.c b/net/ipv4/ping.c index bde333b24837..bb9854c2b7a1 100644 --- a/net/ipv4/ping.c +++ b/net/ipv4/ping.c @@ -138,7 +138,7 @@ next_port: fail: spin_unlock(&ping_table.lock); - return 1; + return -EADDRINUSE; } EXPORT_SYMBOL_GPL(ping_get_port); diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index 1fb7d1ed1cb1..9592fe3e444a 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c @@ -240,7 +240,7 @@ int udp_lib_get_port(struct sock *sk, unsigned short snum, struct udp_table *udptable = udp_get_table_prot(sk); struct udp_hslot *hslot, *hslot2; struct net *net = sock_net(sk); - int error = 1; + int error = -EADDRINUSE; if (!snum) { DECLARE_BITMAP(bitmap, PORTS_PER_CHAIN); diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c index 68075295d587..fee9163382c2 100644 --- a/net/ipv6/af_inet6.c +++ b/net/ipv6/af_inet6.c @@ -410,10 +410,10 @@ static int __inet6_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len, /* Make sure we are allowed to bind here. */ if (snum || !(inet->bind_address_no_port || (flags & BIND_FORCE_ADDRESS_NO_PORT))) { - if (sk->sk_prot->get_port(sk, snum)) { + err = sk->sk_prot->get_port(sk, snum); + if (err) { sk->sk_ipv6only = saved_ipv6only; inet_reset_saddr(sk); - err = -EADDRINUSE; goto out; } if (!(flags & BIND_FROM_BPF)) { From 976d302fb6165ad620778d7ba834cde6e3fe9f9f Mon Sep 17 00:00:00 2001 From: Paolo Abeni Date: Fri, 18 Nov 2022 10:46:07 -0800 Subject: [PATCH 1122/2002] mptcp: deduplicate error paths on endpoint creation When endpoint creation fails, we need to free the newly allocated entry and eventually destroy the paired mptcp listener socket. Consolidate such action in a single point let all the errors path reach it. Reviewed-by: Mat Martineau Signed-off-by: Paolo Abeni Signed-off-by: Mat Martineau Signed-off-by: David S. Miller --- net/mptcp/pm_netlink.c | 35 +++++++++++++---------------------- 1 file changed, 13 insertions(+), 22 deletions(-) diff --git a/net/mptcp/pm_netlink.c b/net/mptcp/pm_netlink.c index 9813ed0fde9b..fdf2ee29f762 100644 --- a/net/mptcp/pm_netlink.c +++ b/net/mptcp/pm_netlink.c @@ -1003,16 +1003,12 @@ static int mptcp_pm_nl_create_listen_socket(struct sock *sk, return err; msk = mptcp_sk(entry->lsk->sk); - if (!msk) { - err = -EINVAL; - goto out; - } + if (!msk) + return -EINVAL; ssock = __mptcp_nmpc_socket(msk); - if (!ssock) { - err = -EINVAL; - goto out; - } + if (!ssock) + return -EINVAL; mptcp_info2sockaddr(&entry->addr, &addr, entry->addr.family); #if IS_ENABLED(CONFIG_MPTCP_IPV6) @@ -1022,20 +1018,16 @@ static int mptcp_pm_nl_create_listen_socket(struct sock *sk, err = kernel_bind(ssock, (struct sockaddr *)&addr, addrlen); if (err) { pr_warn("kernel_bind error, err=%d", err); - goto out; + return err; } err = kernel_listen(ssock, backlog); if (err) { pr_warn("kernel_listen error, err=%d", err); - goto out; + return err; } return 0; - -out: - sock_release(entry->lsk); - return err; } int mptcp_pm_nl_get_local_id(struct mptcp_sock *msk, struct sock_common *skc) @@ -1327,7 +1319,7 @@ static int mptcp_nl_cmd_add_addr(struct sk_buff *skb, struct genl_info *info) return -EINVAL; } - entry = kmalloc(sizeof(*entry), GFP_KERNEL_ACCOUNT); + entry = kzalloc(sizeof(*entry), GFP_KERNEL_ACCOUNT); if (!entry) { GENL_SET_ERR_MSG(info, "can't allocate addr"); return -ENOMEM; @@ -1338,22 +1330,21 @@ static int mptcp_nl_cmd_add_addr(struct sk_buff *skb, struct genl_info *info) ret = mptcp_pm_nl_create_listen_socket(skb->sk, entry); if (ret) { GENL_SET_ERR_MSG(info, "create listen socket error"); - kfree(entry); - return ret; + goto out_free; } } ret = mptcp_pm_nl_append_new_local_addr(pernet, entry); if (ret < 0) { GENL_SET_ERR_MSG(info, "too many addresses or duplicate one"); - if (entry->lsk) - sock_release(entry->lsk); - kfree(entry); - return ret; + goto out_free; } mptcp_nl_add_subflow_or_signal_addr(sock_net(skb->sk)); - return 0; + +out_free: + __mptcp_pm_release_addr_entry(entry); + return ret; } int mptcp_pm_get_flags_and_ifindex_by_id(struct mptcp_sock *msk, unsigned int id, From a3400e8746b626531099e4d9fd8eac41be066683 Mon Sep 17 00:00:00 2001 From: Paolo Abeni Date: Fri, 18 Nov 2022 10:46:08 -0800 Subject: [PATCH 1123/2002] mptcp: more detailed error reporting on endpoint creation Endpoint creation can fail for a number of reasons; in case of failure append the error number to the extended ack message, using a newly introduced generic helper. Additionally let mptcp_pm_nl_append_new_local_addr() report different error reasons. Reviewed-by: Mat Martineau Signed-off-by: Paolo Abeni Signed-off-by: Mat Martineau Signed-off-by: David S. Miller --- include/net/genetlink.h | 3 +++ net/mptcp/pm_netlink.c | 24 +++++++++++++----------- 2 files changed, 16 insertions(+), 11 deletions(-) diff --git a/include/net/genetlink.h b/include/net/genetlink.h index d21210709f84..ed4622dd4828 100644 --- a/include/net/genetlink.h +++ b/include/net/genetlink.h @@ -125,6 +125,9 @@ static inline void genl_info_net_set(struct genl_info *info, struct net *net) #define GENL_SET_ERR_MSG(info, msg) NL_SET_ERR_MSG((info)->extack, msg) +#define GENL_SET_ERR_MSG_FMT(info, msg, args...) \ + NL_SET_ERR_MSG_FMT((info)->extack, msg, ##args) + /* Report that a root attribute is missing */ #define GENL_REQ_ATTR_CHECK(info, attr) ({ \ struct genl_info *__info = (info); \ diff --git a/net/mptcp/pm_netlink.c b/net/mptcp/pm_netlink.c index fdf2ee29f762..d66fbd558263 100644 --- a/net/mptcp/pm_netlink.c +++ b/net/mptcp/pm_netlink.c @@ -912,10 +912,14 @@ static int mptcp_pm_nl_append_new_local_addr(struct pm_nl_pernet *pernet, */ if (pernet->next_id == MPTCP_PM_MAX_ADDR_ID) pernet->next_id = 1; - if (pernet->addrs >= MPTCP_PM_ADDR_MAX) + if (pernet->addrs >= MPTCP_PM_ADDR_MAX) { + ret = -ERANGE; goto out; - if (test_bit(entry->addr.id, pernet->id_bitmap)) + } + if (test_bit(entry->addr.id, pernet->id_bitmap)) { + ret = -EBUSY; goto out; + } /* do not insert duplicate address, differentiate on port only * singled addresses @@ -929,8 +933,10 @@ static int mptcp_pm_nl_append_new_local_addr(struct pm_nl_pernet *pernet, * endpoint is an implicit one and the user-space * did not provide an endpoint id */ - if (!(cur->flags & MPTCP_PM_ADDR_FLAG_IMPLICIT)) + if (!(cur->flags & MPTCP_PM_ADDR_FLAG_IMPLICIT)) { + ret = -EEXIST; goto out; + } if (entry->addr.id) goto out; @@ -1016,16 +1022,12 @@ static int mptcp_pm_nl_create_listen_socket(struct sock *sk, addrlen = sizeof(struct sockaddr_in6); #endif err = kernel_bind(ssock, (struct sockaddr *)&addr, addrlen); - if (err) { - pr_warn("kernel_bind error, err=%d", err); + if (err) return err; - } err = kernel_listen(ssock, backlog); - if (err) { - pr_warn("kernel_listen error, err=%d", err); + if (err) return err; - } return 0; } @@ -1329,13 +1331,13 @@ static int mptcp_nl_cmd_add_addr(struct sk_buff *skb, struct genl_info *info) if (entry->addr.port) { ret = mptcp_pm_nl_create_listen_socket(skb->sk, entry); if (ret) { - GENL_SET_ERR_MSG(info, "create listen socket error"); + GENL_SET_ERR_MSG_FMT(info, "create listen socket error: %d", ret); goto out_free; } } ret = mptcp_pm_nl_append_new_local_addr(pernet, entry); if (ret < 0) { - GENL_SET_ERR_MSG(info, "too many addresses or duplicate one"); + GENL_SET_ERR_MSG_FMT(info, "too many addresses or duplicate one: %d", ret); goto out_free; } From ceb35b666d42c2e91b1f94aeca95bb5eb0943268 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Fri, 18 Nov 2022 10:34:14 -0800 Subject: [PATCH 1124/2002] bpf/verifier: Use kmalloc_size_roundup() to match ksize() usage Most allocation sites in the kernel want an explicitly sized allocation (and not "more"), and that dynamic runtime analysis tools (e.g. KASAN, UBSAN_BOUNDS, FORTIFY_SOURCE, etc) are looking for precise bounds checking (i.e. not something that is rounded up). A tiny handful of allocations were doing an implicit alloc/realloc loop that actually depended on ksize(), and didn't actually always call realloc. This has created a long series of bugs and problems over many years related to the runtime bounds checking, so these callers are finally being adjusted to _not_ depend on the ksize() side-effect, by doing one of several things: - tracking the allocation size precisely and just never calling ksize() at all [1]. - always calling realloc and not using ksize() at all. (This solution ends up actually be a subset of the next solution.) - using kmalloc_size_roundup() to explicitly round up the desired allocation size immediately [2]. The bpf/verifier case is this another of this latter case, and is the last outstanding case to be fixed in the kernel. Because some of the dynamic bounds checking depends on the size being an _argument_ to an allocator function (i.e. see the __alloc_size attribute), the ksize() users are rare, and it could waste local variables, it was been deemed better to explicitly separate the rounding up from the allocation itself [3]. Round up allocations with kmalloc_size_roundup() so that the verifier's use of ksize() is always accurate. [1] e.g.: https://git.kernel.org/linus/712f210a457d https://git.kernel.org/linus/72c08d9f4c72 [2] e.g.: https://git.kernel.org/netdev/net-next/c/12d6c1d3a2ad https://git.kernel.org/netdev/net-next/c/ab3f7828c979 https://git.kernel.org/netdev/net-next/c/d6dd508080a3 [3] https://lore.kernel.org/lkml/0ea1fc165a6c6117f982f4f135093e69cb884930.camel@redhat.com/ Signed-off-by: Kees Cook Signed-off-by: Daniel Borkmann Acked-by: Stanislav Fomichev Link: https://lore.kernel.org/bpf/20221118183409.give.387-kees@kernel.org --- kernel/bpf/verifier.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index f07bec227fef..9528a066cfa5 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -1022,9 +1022,9 @@ static void *copy_array(void *dst, const void *src, size_t n, size_t size, gfp_t if (unlikely(check_mul_overflow(n, size, &bytes))) return NULL; - if (ksize(dst) < bytes) { + if (ksize(dst) < ksize(src)) { kfree(dst); - dst = kmalloc_track_caller(bytes, flags); + dst = kmalloc_track_caller(kmalloc_size_roundup(bytes), flags); if (!dst) return NULL; } @@ -1041,12 +1041,14 @@ out: */ static void *realloc_array(void *arr, size_t old_n, size_t new_n, size_t size) { + size_t alloc_size; void *new_arr; if (!new_n || old_n == new_n) goto out; - new_arr = krealloc_array(arr, new_n, size, GFP_KERNEL); + alloc_size = kmalloc_size_roundup(size_mul(new_n, size)); + new_arr = krealloc(arr, alloc_size, GFP_KERNEL); if (!new_arr) { kfree(arr); return NULL; @@ -2519,9 +2521,11 @@ static int push_jmp_history(struct bpf_verifier_env *env, { u32 cnt = cur->jmp_history_cnt; struct bpf_idx_pair *p; + size_t alloc_size; cnt++; - p = krealloc(cur->jmp_history, cnt * sizeof(*p), GFP_USER); + alloc_size = kmalloc_size_roundup(size_mul(cnt, sizeof(*p))); + p = krealloc(cur->jmp_history, alloc_size, GFP_USER); if (!p) return -ENOMEM; p[cnt - 1].idx = env->insn_idx; From 1a5160d4d8fe63ba4964cfff4a85831b6af75f2d Mon Sep 17 00:00:00 2001 From: Hou Tao Date: Mon, 21 Nov 2022 15:34:38 +0800 Subject: [PATCH 1125/2002] bpf: Pin the start cgroup in cgroup_iter_seq_init() bpf_iter_attach_cgroup() has already acquired an extra reference for the start cgroup, but the reference may be released if the iterator link fd is closed after the creation of iterator fd, and it may lead to user-after-free problem when reading the iterator fd. An alternative fix is pinning iterator link when opening iterator, but it will make iterator link being still visible after the close of iterator link fd and the behavior is different with other link types, so just fixing it by acquiring another reference for the start cgroup. Fixes: d4ccaf58a847 ("bpf: Introduce cgroup iter") Signed-off-by: Hou Tao Signed-off-by: Daniel Borkmann Acked-by: Yonghong Song Link: https://lore.kernel.org/bpf/20221121073440.1828292-2-houtao@huaweicloud.com --- kernel/bpf/cgroup_iter.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/kernel/bpf/cgroup_iter.c b/kernel/bpf/cgroup_iter.c index fbc6167c3599..06989d278846 100644 --- a/kernel/bpf/cgroup_iter.c +++ b/kernel/bpf/cgroup_iter.c @@ -164,16 +164,30 @@ static int cgroup_iter_seq_init(void *priv, struct bpf_iter_aux_info *aux) struct cgroup_iter_priv *p = (struct cgroup_iter_priv *)priv; struct cgroup *cgrp = aux->cgroup.start; + /* bpf_iter_attach_cgroup() has already acquired an extra reference + * for the start cgroup, but the reference may be released after + * cgroup_iter_seq_init(), so acquire another reference for the + * start cgroup. + */ p->start_css = &cgrp->self; + css_get(p->start_css); p->terminate = false; p->visited_all = false; p->order = aux->cgroup.order; return 0; } +static void cgroup_iter_seq_fini(void *priv) +{ + struct cgroup_iter_priv *p = (struct cgroup_iter_priv *)priv; + + css_put(p->start_css); +} + static const struct bpf_iter_seq_info cgroup_iter_seq_info = { .seq_ops = &cgroup_iter_seq_ops, .init_seq_private = cgroup_iter_seq_init, + .fini_seq_private = cgroup_iter_seq_fini, .seq_priv_size = sizeof(struct cgroup_iter_priv), }; From 2a42461a88314bbeaa3dcad3d19a4bb3d9aa546f Mon Sep 17 00:00:00 2001 From: Hou Tao Date: Mon, 21 Nov 2022 15:34:39 +0800 Subject: [PATCH 1126/2002] selftests/bpf: Add cgroup helper remove_cgroup() Add remove_cgroup() to remove a cgroup which doesn't have any children or live processes. It will be used by the following patch to test cgroup iterator on a dead cgroup. Signed-off-by: Hou Tao Signed-off-by: Daniel Borkmann Acked-by: Yonghong Song Link: https://lore.kernel.org/bpf/20221121073440.1828292-3-houtao@huaweicloud.com --- tools/testing/selftests/bpf/cgroup_helpers.c | 19 +++++++++++++++++++ tools/testing/selftests/bpf/cgroup_helpers.h | 1 + 2 files changed, 20 insertions(+) diff --git a/tools/testing/selftests/bpf/cgroup_helpers.c b/tools/testing/selftests/bpf/cgroup_helpers.c index dd1aa5afcf5a..9e95b37a7dff 100644 --- a/tools/testing/selftests/bpf/cgroup_helpers.c +++ b/tools/testing/selftests/bpf/cgroup_helpers.c @@ -333,6 +333,25 @@ int get_root_cgroup(void) return fd; } +/* + * remove_cgroup() - Remove a cgroup + * @relative_path: The cgroup path, relative to the workdir, to remove + * + * This function expects a cgroup to already be created, relative to the cgroup + * work dir. It also expects the cgroup doesn't have any children or live + * processes and it removes the cgroup. + * + * On failure, it will print an error to stderr. + */ +void remove_cgroup(const char *relative_path) +{ + char cgroup_path[PATH_MAX + 1]; + + format_cgroup_path(cgroup_path, relative_path); + if (rmdir(cgroup_path)) + log_err("rmdiring cgroup %s .. %s", relative_path, cgroup_path); +} + /** * create_and_get_cgroup() - Create a cgroup, relative to workdir, and get the FD * @relative_path: The cgroup path, relative to the workdir, to join diff --git a/tools/testing/selftests/bpf/cgroup_helpers.h b/tools/testing/selftests/bpf/cgroup_helpers.h index 3358734356ab..f099a166c94d 100644 --- a/tools/testing/selftests/bpf/cgroup_helpers.h +++ b/tools/testing/selftests/bpf/cgroup_helpers.h @@ -18,6 +18,7 @@ int write_cgroup_file_parent(const char *relative_path, const char *file, int cgroup_setup_and_join(const char *relative_path); int get_root_cgroup(void); int create_and_get_cgroup(const char *relative_path); +void remove_cgroup(const char *relative_path); unsigned long long get_cgroup_id(const char *relative_path); int join_cgroup(const char *relative_path); From 8589e92675aa4727bede3f9230709624619844f3 Mon Sep 17 00:00:00 2001 From: Hou Tao Date: Mon, 21 Nov 2022 15:34:40 +0800 Subject: [PATCH 1127/2002] selftests/bpf: Add test for cgroup iterator on a dead cgroup The test closes both iterator link fd and cgroup fd, and removes the cgroup file to make a dead cgroup before reading from cgroup iterator. It also uses kern_sync_rcu() and usleep() to wait for the release of start cgroup. If the start cgroup is not pinned by cgroup iterator, reading from iterator fd will trigger use-after-free. Signed-off-by: Hou Tao Signed-off-by: Daniel Borkmann Acked-by: Yonghong Song Acked-by: Hao Luo Link: https://lore.kernel.org/bpf/20221121073440.1828292-4-houtao@huaweicloud.com --- .../selftests/bpf/prog_tests/cgroup_iter.c | 76 +++++++++++++++++++ 1 file changed, 76 insertions(+) diff --git a/tools/testing/selftests/bpf/prog_tests/cgroup_iter.c b/tools/testing/selftests/bpf/prog_tests/cgroup_iter.c index c4a2adb38da1..e02feb5fae97 100644 --- a/tools/testing/selftests/bpf/prog_tests/cgroup_iter.c +++ b/tools/testing/selftests/bpf/prog_tests/cgroup_iter.c @@ -189,6 +189,80 @@ static void test_walk_self_only(struct cgroup_iter *skel) BPF_CGROUP_ITER_SELF_ONLY, "self_only"); } +static void test_walk_dead_self_only(struct cgroup_iter *skel) +{ + DECLARE_LIBBPF_OPTS(bpf_iter_attach_opts, opts); + char expected_output[128], buf[128]; + const char *cgrp_name = "/dead"; + union bpf_iter_link_info linfo; + int len, cgrp_fd, iter_fd; + struct bpf_link *link; + size_t left; + char *p; + + cgrp_fd = create_and_get_cgroup(cgrp_name); + if (!ASSERT_GE(cgrp_fd, 0, "create cgrp")) + return; + + /* The cgroup will be dead during read() iteration, so it only has + * epilogue in the output + */ + snprintf(expected_output, sizeof(expected_output), EPILOGUE); + + memset(&linfo, 0, sizeof(linfo)); + linfo.cgroup.cgroup_fd = cgrp_fd; + linfo.cgroup.order = BPF_CGROUP_ITER_SELF_ONLY; + opts.link_info = &linfo; + opts.link_info_len = sizeof(linfo); + + link = bpf_program__attach_iter(skel->progs.cgroup_id_printer, &opts); + if (!ASSERT_OK_PTR(link, "attach_iter")) + goto close_cgrp; + + iter_fd = bpf_iter_create(bpf_link__fd(link)); + if (!ASSERT_GE(iter_fd, 0, "iter_create")) + goto free_link; + + /* Close link fd and cgroup fd */ + bpf_link__destroy(link); + close(cgrp_fd); + + /* Remove cgroup to mark it as dead */ + remove_cgroup(cgrp_name); + + /* Two kern_sync_rcu() and usleep() pairs are used to wait for the + * releases of cgroup css, and the last kern_sync_rcu() and usleep() + * pair is used to wait for the free of cgroup itself. + */ + kern_sync_rcu(); + usleep(8000); + kern_sync_rcu(); + usleep(8000); + kern_sync_rcu(); + usleep(1000); + + memset(buf, 0, sizeof(buf)); + left = ARRAY_SIZE(buf); + p = buf; + while ((len = read(iter_fd, p, left)) > 0) { + p += len; + left -= len; + } + + ASSERT_STREQ(buf, expected_output, "dead cgroup output"); + + /* read() after iter finishes should be ok. */ + if (len == 0) + ASSERT_OK(read(iter_fd, buf, sizeof(buf)), "second_read"); + + close(iter_fd); + return; +free_link: + bpf_link__destroy(link); +close_cgrp: + close(cgrp_fd); +} + void test_cgroup_iter(void) { struct cgroup_iter *skel = NULL; @@ -217,6 +291,8 @@ void test_cgroup_iter(void) test_early_termination(skel); if (test__start_subtest("cgroup_iter__self_only")) test_walk_self_only(skel); + if (test__start_subtest("cgroup_iter__dead_self_only")) + test_walk_dead_self_only(skel); out: cgroup_iter__destroy(skel); cleanup_cgroups(); From 114039b342014680911c35bd6b72624180fd669a Mon Sep 17 00:00:00 2001 From: Stanislav Fomichev Date: Mon, 21 Nov 2022 10:03:39 -0800 Subject: [PATCH 1128/2002] bpf: Move skb->len == 0 checks into __bpf_redirect To avoid potentially breaking existing users. Both mac/no-mac cases have to be amended; mac_header >= network_header is not enough (verified with a new test, see next patch). Fixes: fd1894224407 ("bpf: Don't redirect packets with invalid pkt_len") Signed-off-by: Stanislav Fomichev Link: https://lore.kernel.org/r/20221121180340.1983627-1-sdf@google.com Signed-off-by: Martin KaFai Lau --- net/bpf/test_run.c | 3 --- net/core/filter.c | 11 ++++++----- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/net/bpf/test_run.c b/net/bpf/test_run.c index 13d578ce2a09..6fba440efc40 100644 --- a/net/bpf/test_run.c +++ b/net/bpf/test_run.c @@ -979,9 +979,6 @@ static int convert___skb_to_skb(struct sk_buff *skb, struct __sk_buff *__skb) { struct qdisc_skb_cb *cb = (struct qdisc_skb_cb *)skb->cb; - if (!skb->len) - return -EINVAL; - if (!__skb) return 0; diff --git a/net/core/filter.c b/net/core/filter.c index 2a105fb1ceb2..b6e1b81cdfae 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -2124,12 +2124,13 @@ static int __bpf_redirect_no_mac(struct sk_buff *skb, struct net_device *dev, { unsigned int mlen = skb_network_offset(skb); + if (unlikely(skb->len <= mlen)) { + kfree_skb(skb); + return -ERANGE; + } + if (mlen) { __skb_pull(skb, mlen); - if (unlikely(!skb->len)) { - kfree_skb(skb); - return -ERANGE; - } /* At ingress, the mac header has already been pulled once. * At egress, skb_pospull_rcsum has to be done in case that @@ -2149,7 +2150,7 @@ static int __bpf_redirect_common(struct sk_buff *skb, struct net_device *dev, u32 flags) { /* Verify that a link layer header is carried */ - if (unlikely(skb->mac_header >= skb->network_header)) { + if (unlikely(skb->mac_header >= skb->network_header || skb->len == 0)) { kfree_skb(skb); return -ERANGE; } From 68f8e3d4b916531ea3bb8b83e35138cf78f2fce5 Mon Sep 17 00:00:00 2001 From: Stanislav Fomichev Date: Mon, 21 Nov 2022 10:03:40 -0800 Subject: [PATCH 1129/2002] selftests/bpf: Make sure zero-len skbs aren't redirectable LWT_XMIT to test L3 case, TC to test L2 case. v2: - s/veth_ifindex/ipip_ifindex/ in two places (Martin) - add comment about which condition triggers the rejection (Martin) Signed-off-by: Stanislav Fomichev Link: https://lore.kernel.org/r/20221121180340.1983627-2-sdf@google.com Signed-off-by: Martin KaFai Lau --- .../selftests/bpf/prog_tests/empty_skb.c | 146 ++++++++++++++++++ tools/testing/selftests/bpf/progs/empty_skb.c | 37 +++++ 2 files changed, 183 insertions(+) create mode 100644 tools/testing/selftests/bpf/prog_tests/empty_skb.c create mode 100644 tools/testing/selftests/bpf/progs/empty_skb.c diff --git a/tools/testing/selftests/bpf/prog_tests/empty_skb.c b/tools/testing/selftests/bpf/prog_tests/empty_skb.c new file mode 100644 index 000000000000..32dd731e9070 --- /dev/null +++ b/tools/testing/selftests/bpf/prog_tests/empty_skb.c @@ -0,0 +1,146 @@ +// SPDX-License-Identifier: GPL-2.0 +#include +#include +#include +#include "empty_skb.skel.h" + +#define SYS(cmd) ({ \ + if (!ASSERT_OK(system(cmd), (cmd))) \ + goto out; \ +}) + +void test_empty_skb(void) +{ + LIBBPF_OPTS(bpf_test_run_opts, tattr); + struct empty_skb *bpf_obj = NULL; + struct nstoken *tok = NULL; + struct bpf_program *prog; + char eth_hlen_pp[15]; + char eth_hlen[14]; + int veth_ifindex; + int ipip_ifindex; + int err; + int i; + + struct { + const char *msg; + const void *data_in; + __u32 data_size_in; + int *ifindex; + int err; + int ret; + bool success_on_tc; + } tests[] = { + /* Empty packets are always rejected. */ + + { + /* BPF_PROG_RUN ETH_HLEN size check */ + .msg = "veth empty ingress packet", + .data_in = NULL, + .data_size_in = 0, + .ifindex = &veth_ifindex, + .err = -EINVAL, + }, + { + /* BPF_PROG_RUN ETH_HLEN size check */ + .msg = "ipip empty ingress packet", + .data_in = NULL, + .data_size_in = 0, + .ifindex = &ipip_ifindex, + .err = -EINVAL, + }, + + /* ETH_HLEN-sized packets: + * - can not be redirected at LWT_XMIT + * - can be redirected at TC to non-tunneling dest + */ + + { + /* __bpf_redirect_common */ + .msg = "veth ETH_HLEN packet ingress", + .data_in = eth_hlen, + .data_size_in = sizeof(eth_hlen), + .ifindex = &veth_ifindex, + .ret = -ERANGE, + .success_on_tc = true, + }, + { + /* __bpf_redirect_no_mac + * + * lwt: skb->len=0 <= skb_network_offset=0 + * tc: skb->len=14 <= skb_network_offset=14 + */ + .msg = "ipip ETH_HLEN packet ingress", + .data_in = eth_hlen, + .data_size_in = sizeof(eth_hlen), + .ifindex = &ipip_ifindex, + .ret = -ERANGE, + }, + + /* ETH_HLEN+1-sized packet should be redirected. */ + + { + .msg = "veth ETH_HLEN+1 packet ingress", + .data_in = eth_hlen_pp, + .data_size_in = sizeof(eth_hlen_pp), + .ifindex = &veth_ifindex, + }, + { + .msg = "ipip ETH_HLEN+1 packet ingress", + .data_in = eth_hlen_pp, + .data_size_in = sizeof(eth_hlen_pp), + .ifindex = &ipip_ifindex, + }, + }; + + SYS("ip netns add empty_skb"); + tok = open_netns("empty_skb"); + SYS("ip link add veth0 type veth peer veth1"); + SYS("ip link set dev veth0 up"); + SYS("ip link set dev veth1 up"); + SYS("ip addr add 10.0.0.1/8 dev veth0"); + SYS("ip addr add 10.0.0.2/8 dev veth1"); + veth_ifindex = if_nametoindex("veth0"); + + SYS("ip link add ipip0 type ipip local 10.0.0.1 remote 10.0.0.2"); + SYS("ip link set ipip0 up"); + SYS("ip addr add 192.168.1.1/16 dev ipip0"); + ipip_ifindex = if_nametoindex("ipip0"); + + bpf_obj = empty_skb__open_and_load(); + if (!ASSERT_OK_PTR(bpf_obj, "open skeleton")) + goto out; + + for (i = 0; i < ARRAY_SIZE(tests); i++) { + bpf_object__for_each_program(prog, bpf_obj->obj) { + char buf[128]; + bool at_tc = !strncmp(bpf_program__section_name(prog), "tc", 2); + + tattr.data_in = tests[i].data_in; + tattr.data_size_in = tests[i].data_size_in; + + tattr.data_size_out = 0; + bpf_obj->bss->ifindex = *tests[i].ifindex; + bpf_obj->bss->ret = 0; + err = bpf_prog_test_run_opts(bpf_program__fd(prog), &tattr); + sprintf(buf, "err: %s [%s]", tests[i].msg, bpf_program__name(prog)); + + if (at_tc && tests[i].success_on_tc) + ASSERT_GE(err, 0, buf); + else + ASSERT_EQ(err, tests[i].err, buf); + sprintf(buf, "ret: %s [%s]", tests[i].msg, bpf_program__name(prog)); + if (at_tc && tests[i].success_on_tc) + ASSERT_GE(bpf_obj->bss->ret, 0, buf); + else + ASSERT_EQ(bpf_obj->bss->ret, tests[i].ret, buf); + } + } + +out: + if (bpf_obj) + empty_skb__destroy(bpf_obj); + if (tok) + close_netns(tok); + system("ip netns del empty_skb"); +} diff --git a/tools/testing/selftests/bpf/progs/empty_skb.c b/tools/testing/selftests/bpf/progs/empty_skb.c new file mode 100644 index 000000000000..4b0cd6753251 --- /dev/null +++ b/tools/testing/selftests/bpf/progs/empty_skb.c @@ -0,0 +1,37 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +#include +#include +#include + +char _license[] SEC("license") = "GPL"; + +int ifindex; +int ret; + +SEC("lwt_xmit") +int redirect_ingress(struct __sk_buff *skb) +{ + ret = bpf_clone_redirect(skb, ifindex, BPF_F_INGRESS); + return 0; +} + +SEC("lwt_xmit") +int redirect_egress(struct __sk_buff *skb) +{ + ret = bpf_clone_redirect(skb, ifindex, 0); + return 0; +} + +SEC("tc") +int tc_redirect_ingress(struct __sk_buff *skb) +{ + ret = bpf_clone_redirect(skb, ifindex, BPF_F_INGRESS); + return 0; +} + +SEC("tc") +int tc_redirect_egress(struct __sk_buff *skb) +{ + ret = bpf_clone_redirect(skb, ifindex, 0); + return 0; +} From 32634819ad37290b5d5a84bf8b71ef5e972c4a20 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Fri, 18 Nov 2022 04:38:43 +0000 Subject: [PATCH 1130/2002] net: fix __sock_gen_cookie() I was mistaken how atomic64_try_cmpxchg(&sk_cookie, &res, new) is working. I was assuming @res would contain the final sk_cookie value, regardless of the success of our cmpxchg() We could do something like: if (atomic64_try_cmpxchg(&sk_cookie, &res, new) res = new; But we can avoid a conditional and read sk_cookie again. atomic64_cmpxchg(&sk_cookie, res, new); res = atomic64_read(&sk_cookie); Reported-by: coverity-bot Addresses-Coverity-ID: 1527347 ("Error handling issues") Fixes: 4ebf802cf1c6 ("net: __sock_gen_cookie() cleanup") Signed-off-by: Eric Dumazet Link: https://lore.kernel.org/r/20221118043843.3703186-1-edumazet@google.com Signed-off-by: Jakub Kicinski --- net/core/sock_diag.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/net/core/sock_diag.c b/net/core/sock_diag.c index b11593cae5a0..b1e29e18d1d6 100644 --- a/net/core/sock_diag.c +++ b/net/core/sock_diag.c @@ -30,7 +30,10 @@ u64 __sock_gen_cookie(struct sock *sk) if (!res) { u64 new = gen_cookie_next(&sock_cookie); - atomic64_try_cmpxchg(&sk->sk_cookie, &res, new); + atomic64_cmpxchg(&sk->sk_cookie, res, new); + + /* Another thread might have changed sk_cookie before us. */ + res = atomic64_read(&sk->sk_cookie); } return res; } From 4e9a61394dc47d651690f4f87398ac5944e02678 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Fri, 18 Nov 2022 18:12:52 +0300 Subject: [PATCH 1131/2002] net: microchip: sparx5: fix uninitialized variables Smatch complains that "err" can be uninitialized on these paths. Also it's just nicer to "return 0;" instead of "return err;" Fixes: 3a344f99bb55 ("net: microchip: sparx5: Add support for TC flower ARP dissector") Signed-off-by: Dan Carpenter Link: https://lore.kernel.org/r/Y3eg9Ml/LmLR3L3C@kili Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/microchip/sparx5/sparx5_tc_flower.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_tc_flower.c b/drivers/net/ethernet/microchip/sparx5/sparx5_tc_flower.c index 04fc2f3b1979..bd6bd380ba34 100644 --- a/drivers/net/ethernet/microchip/sparx5/sparx5_tc_flower.c +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_tc_flower.c @@ -341,7 +341,7 @@ sparx5_tc_flower_handler_vlan_usage(struct sparx5_tc_flower_parse_usage *st) st->used_keys |= BIT(FLOW_DISSECTOR_KEY_VLAN); - return err; + return 0; out: NL_SET_ERR_MSG_MOD(st->fco->common.extack, "vlan parse error"); return err; @@ -452,8 +452,10 @@ sparx5_tc_flower_handler_arp_usage(struct sparx5_tc_flower_parse_usage *st) /* The IS2 ARP keyset does not support ARP hardware addresses */ if (!is_zero_ether_addr(mt.mask->sha) || - !is_zero_ether_addr(mt.mask->tha)) + !is_zero_ether_addr(mt.mask->tha)) { + err = -EINVAL; goto out; + } if (mt.mask->sip) { ipval = be32_to_cpu((__force __be32)mt.key->sip); @@ -477,7 +479,7 @@ sparx5_tc_flower_handler_arp_usage(struct sparx5_tc_flower_parse_usage *st) st->used_keys |= BIT(FLOW_DISSECTOR_KEY_ARP); - return err; + return 0; out: NL_SET_ERR_MSG_MOD(st->fco->common.extack, "arp parse error"); From 696450c05181559a35d4d5bee55c465b1ac6fe2e Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Fri, 18 Nov 2022 13:37:54 -0800 Subject: [PATCH 1132/2002] net: bcmgenet: Clear RGMII_LINK upon link down Clear the RGMII_LINK bit upon detecting link down to be consistent with setting the bit upon link up. We also move the clearing of the out-of-band disable to the runtime initialization rather than for each link up/down transition. Signed-off-by: Florian Fainelli Link: https://lore.kernel.org/r/20221118213754.1383364-1-f.fainelli@gmail.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/broadcom/genet/bcmmii.c | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/drivers/net/ethernet/broadcom/genet/bcmmii.c b/drivers/net/ethernet/broadcom/genet/bcmmii.c index 7ded559842e8..b615176338b2 100644 --- a/drivers/net/ethernet/broadcom/genet/bcmmii.c +++ b/drivers/net/ethernet/broadcom/genet/bcmmii.c @@ -72,7 +72,6 @@ static void bcmgenet_mac_config(struct net_device *dev) * Receive clock is provided by the PHY. */ reg = bcmgenet_ext_readl(priv, EXT_RGMII_OOB_CTRL); - reg &= ~OOB_DISABLE; reg |= RGMII_LINK; bcmgenet_ext_writel(priv, reg, EXT_RGMII_OOB_CTRL); @@ -95,10 +94,18 @@ static void bcmgenet_mac_config(struct net_device *dev) */ void bcmgenet_mii_setup(struct net_device *dev) { + struct bcmgenet_priv *priv = netdev_priv(dev); struct phy_device *phydev = dev->phydev; + u32 reg; - if (phydev->link) + if (phydev->link) { bcmgenet_mac_config(dev); + } else { + reg = bcmgenet_ext_readl(priv, EXT_RGMII_OOB_CTRL); + reg &= ~RGMII_LINK; + bcmgenet_ext_writel(priv, reg, EXT_RGMII_OOB_CTRL); + } + phy_print_status(phydev); } @@ -266,18 +273,20 @@ int bcmgenet_mii_config(struct net_device *dev, bool init) (priv->phy_interface != PHY_INTERFACE_MODE_MOCA); /* This is an external PHY (xMII), so we need to enable the RGMII - * block for the interface to work + * block for the interface to work, unconditionally clear the + * Out-of-band disable since we do not need it. */ + reg = bcmgenet_ext_readl(priv, EXT_RGMII_OOB_CTRL); + reg &= ~OOB_DISABLE; if (priv->ext_phy) { - reg = bcmgenet_ext_readl(priv, EXT_RGMII_OOB_CTRL); reg &= ~ID_MODE_DIS; reg |= id_mode_dis; if (GENET_IS_V1(priv) || GENET_IS_V2(priv) || GENET_IS_V3(priv)) reg |= RGMII_MODE_EN_V123; else reg |= RGMII_MODE_EN; - bcmgenet_ext_writel(priv, reg, EXT_RGMII_OOB_CTRL); } + bcmgenet_ext_writel(priv, reg, EXT_RGMII_OOB_CTRL); if (init) dev_info(kdev, "configuring instance for %s\n", phy_name); From 674b3e164238a31f236ac63f82d5d160f7d4c201 Mon Sep 17 00:00:00 2001 From: Suman Ghosh Date: Fri, 18 Nov 2022 11:03:29 +0530 Subject: [PATCH 1133/2002] octeontx2-pf: Add additional checks while configuring ucast/bcast/mcast rules 1. If a profile does not support DMAC extraction then avoid installing NPC flow rules for unicast. Similarly, if LXMB(L2 and L3) extraction is not supported by the profile then avoid installing broadcast and multicast rules. 2. Allow MCAM entry insertion for promiscuous mode. 3. For the profiles where DMAC is not extracted in MKEX key default unicast entry installed by AF is not valid. Hence do not use action from the AF installed default unicast entry for such cases. 4. Adjacent packet header fields in a packet like IP header source and destination addresses or UDP/TCP header source port and destination can be extracted together in MKEX profile. Therefore MKEX profile can be configured to in two ways: a. Total of 4 bytes from start of UDP header(src port + destination port) or b. Two bytes from start and two bytes from offset 2 Signed-off-by: Suman Ghosh Signed-off-by: Subbaraya Sundeep Link: https://lore.kernel.org/r/20221118053329.2288486-1-sumang@marvell.com Signed-off-by: Paolo Abeni --- .../net/ethernet/marvell/octeontx2/af/mbox.h | 14 ++++ .../net/ethernet/marvell/octeontx2/af/rvu.h | 1 + .../ethernet/marvell/octeontx2/af/rvu_npc.c | 22 ++++++ .../marvell/octeontx2/af/rvu_npc_fs.c | 76 +++++++++++++++---- .../marvell/octeontx2/nic/otx2_flows.c | 27 ++++++- 5 files changed, 124 insertions(+), 16 deletions(-) diff --git a/drivers/net/ethernet/marvell/octeontx2/af/mbox.h b/drivers/net/ethernet/marvell/octeontx2/af/mbox.h index 8d5d5a0f68c4..c7c92c7510fa 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/mbox.h +++ b/drivers/net/ethernet/marvell/octeontx2/af/mbox.h @@ -245,6 +245,9 @@ M(NPC_MCAM_GET_STATS, 0x6012, npc_mcam_entry_stats, \ M(NPC_GET_SECRET_KEY, 0x6013, npc_get_secret_key, \ npc_get_secret_key_req, \ npc_get_secret_key_rsp) \ +M(NPC_GET_FIELD_STATUS, 0x6014, npc_get_field_status, \ + npc_get_field_status_req, \ + npc_get_field_status_rsp) \ /* NIX mbox IDs (range 0x8000 - 0xFFFF) */ \ M(NIX_LF_ALLOC, 0x8000, nix_lf_alloc, \ nix_lf_alloc_req, nix_lf_alloc_rsp) \ @@ -1541,6 +1544,17 @@ struct ptp_rsp { u64 clk; }; +struct npc_get_field_status_req { + struct mbox_msghdr hdr; + u8 intf; + u8 field; +}; + +struct npc_get_field_status_rsp { + struct mbox_msghdr hdr; + u8 enable; +}; + struct set_vf_perm { struct mbox_msghdr hdr; u16 vf; diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu.h b/drivers/net/ethernet/marvell/octeontx2/af/rvu.h index 76474385a602..f718cbd32a94 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu.h +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu.h @@ -851,6 +851,7 @@ int npc_install_mcam_drop_rule(struct rvu *rvu, int mcam_idx, u16 *counter_idx, u64 chan_val, u64 chan_mask, u64 exact_val, u64 exact_mask, u64 bcast_mcast_val, u64 bcast_mcast_mask); void npc_mcam_rsrcs_reserve(struct rvu *rvu, int blkaddr, int entry_idx); +bool npc_is_feature_supported(struct rvu *rvu, u64 features, u8 intf); /* CPT APIs */ int rvu_cpt_register_interrupts(struct rvu *rvu); diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c index 1e348fd0d930..16cfc802e348 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c @@ -617,6 +617,12 @@ void rvu_npc_install_ucast_entry(struct rvu *rvu, u16 pcifunc, if (blkaddr < 0) return; + /* Ucast rule should not be installed if DMAC + * extraction is not supported by the profile. + */ + if (!npc_is_feature_supported(rvu, BIT_ULL(NPC_DMAC), pfvf->nix_rx_intf)) + return; + index = npc_get_nixlf_mcam_index(mcam, pcifunc, nixlf, NIXLF_UCAST_ENTRY); @@ -778,6 +784,14 @@ void rvu_npc_install_bcast_match_entry(struct rvu *rvu, u16 pcifunc, /* Get 'pcifunc' of PF device */ pcifunc = pcifunc & ~RVU_PFVF_FUNC_MASK; pfvf = rvu_get_pfvf(rvu, pcifunc); + + /* Bcast rule should not be installed if both DMAC + * and LXMB extraction is not supported by the profile. + */ + if (!npc_is_feature_supported(rvu, BIT_ULL(NPC_DMAC), pfvf->nix_rx_intf) && + !npc_is_feature_supported(rvu, BIT_ULL(NPC_LXMB), pfvf->nix_rx_intf)) + return; + index = npc_get_nixlf_mcam_index(mcam, pcifunc, nixlf, NIXLF_BCAST_ENTRY); @@ -848,6 +862,14 @@ void rvu_npc_install_allmulti_entry(struct rvu *rvu, u16 pcifunc, int nixlf, vf_func = pcifunc & RVU_PFVF_FUNC_MASK; pcifunc = pcifunc & ~RVU_PFVF_FUNC_MASK; pfvf = rvu_get_pfvf(rvu, pcifunc); + + /* Mcast rule should not be installed if both DMAC + * and LXMB extraction is not supported by the profile. + */ + if (!npc_is_feature_supported(rvu, BIT_ULL(NPC_DMAC), pfvf->nix_rx_intf) && + !npc_is_feature_supported(rvu, BIT_ULL(NPC_LXMB), pfvf->nix_rx_intf)) + return; + index = npc_get_nixlf_mcam_index(mcam, pcifunc, nixlf, NIXLF_ALLMULTI_ENTRY); diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_fs.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_fs.c index 50d3994efa97..f3fecd2a4015 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_fs.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_fs.c @@ -47,6 +47,19 @@ static const char * const npc_flow_names[] = { [NPC_UNKNOWN] = "unknown", }; +bool npc_is_feature_supported(struct rvu *rvu, u64 features, u8 intf) +{ + struct npc_mcam *mcam = &rvu->hw->mcam; + u64 mcam_features; + u64 unsupported; + + mcam_features = is_npc_intf_tx(intf) ? mcam->tx_features : mcam->rx_features; + unsupported = (mcam_features ^ features) & ~mcam_features; + + /* Return false if at least one of the input flows is not extracted */ + return !unsupported; +} + const char *npc_get_field_name(u8 hdr) { if (hdr >= ARRAY_SIZE(npc_flow_names)) @@ -436,8 +449,6 @@ static void npc_scan_ldata(struct rvu *rvu, int blkaddr, u8 lid, nr_bytes = FIELD_GET(NPC_BYTESM, cfg) + 1; hdr = FIELD_GET(NPC_HDR_OFFSET, cfg); key = FIELD_GET(NPC_KEY_OFFSET, cfg); - start_kwi = key / 8; - offset = (key * 8) % 64; /* For Tx, Layer A has NIX_INST_HDR_S(64 bytes) preceding * ethernet header. @@ -452,13 +463,18 @@ static void npc_scan_ldata(struct rvu *rvu, int blkaddr, u8 lid, #define NPC_SCAN_HDR(name, hlid, hlt, hstart, hlen) \ do { \ + start_kwi = key / 8; \ + offset = (key * 8) % 64; \ if (lid == (hlid) && lt == (hlt)) { \ if ((hstart) >= hdr && \ ((hstart) + (hlen)) <= (hdr + nr_bytes)) { \ bit_offset = (hdr + nr_bytes - (hstart) - (hlen)) * 8; \ npc_set_layer_mdata(mcam, (name), cfg, lid, lt, intf); \ + offset += bit_offset; \ + start_kwi += offset / 64; \ + offset %= 64; \ npc_set_kw_masks(mcam, (name), (hlen) * 8, \ - start_kwi, offset + bit_offset, intf);\ + start_kwi, offset, intf); \ } \ } \ } while (0) @@ -650,9 +666,9 @@ static int npc_check_unsupported_flows(struct rvu *rvu, u64 features, u8 intf) unsupported = (*mcam_features ^ features) & ~(*mcam_features); if (unsupported) { - dev_info(rvu->dev, "Unsupported flow(s):\n"); + dev_warn(rvu->dev, "Unsupported flow(s):\n"); for_each_set_bit(bit, (unsigned long *)&unsupported, 64) - dev_info(rvu->dev, "%s ", npc_get_field_name(bit)); + dev_warn(rvu->dev, "%s ", npc_get_field_name(bit)); return -EOPNOTSUPP; } @@ -1007,8 +1023,20 @@ static void npc_update_rx_entry(struct rvu *rvu, struct rvu_pfvf *pfvf, action.match_id = req->match_id; action.flow_key_alg = req->flow_key_alg; - if (req->op == NIX_RX_ACTION_DEFAULT && pfvf->def_ucast_rule) - action = pfvf->def_ucast_rule->rx_action; + if (req->op == NIX_RX_ACTION_DEFAULT) { + if (pfvf->def_ucast_rule) { + action = pfvf->def_ucast_rule->rx_action; + } else { + /* For profiles which do not extract DMAC, the default + * unicast entry is unused. Hence modify action for the + * requests which use same action as default unicast + * entry + */ + *(u64 *)&action = 0; + action.pf_func = target; + action.op = NIX_RX_ACTIONOP_UCAST; + } + } entry->action = *(u64 *)&action; @@ -1239,18 +1267,19 @@ int rvu_mbox_handler_npc_install_flow(struct rvu *rvu, if (npc_check_field(rvu, blkaddr, NPC_DMAC, req->intf)) goto process_flow; - if (is_pffunc_af(req->hdr.pcifunc)) { + if (is_pffunc_af(req->hdr.pcifunc) && + req->features & BIT_ULL(NPC_DMAC)) { if (is_unicast_ether_addr(req->packet.dmac)) { - dev_err(rvu->dev, - "%s: mkex profile does not support ucast flow\n", - __func__); + dev_warn(rvu->dev, + "%s: mkex profile does not support ucast flow\n", + __func__); return NPC_FLOW_NOT_SUPPORTED; } if (!npc_is_field_present(rvu, NPC_LXMB, req->intf)) { - dev_err(rvu->dev, - "%s: mkex profile does not support bcast/mcast flow", - __func__); + dev_warn(rvu->dev, + "%s: mkex profile does not support bcast/mcast flow", + __func__); return NPC_FLOW_NOT_SUPPORTED; } @@ -1603,3 +1632,22 @@ int npc_install_mcam_drop_rule(struct rvu *rvu, int mcam_idx, u16 *counter_idx, return 0; } + +int rvu_mbox_handler_npc_get_field_status(struct rvu *rvu, + struct npc_get_field_status_req *req, + struct npc_get_field_status_rsp *rsp) +{ + int blkaddr; + + blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NPC, 0); + if (blkaddr < 0) + return NPC_MCAM_INVALID_REQ; + + if (!is_npc_interface_valid(rvu, req->intf)) + return NPC_FLOW_INTF_INVALID; + + if (npc_check_field(rvu, blkaddr, req->field, req->intf)) + rsp->enable = 1; + + return 0; +} diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_flows.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_flows.c index 709fc0114fbd..13aa79efee03 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_flows.c +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_flows.c @@ -164,6 +164,8 @@ EXPORT_SYMBOL(otx2_alloc_mcam_entries); static int otx2_mcam_entry_init(struct otx2_nic *pfvf) { struct otx2_flow_config *flow_cfg = pfvf->flow_cfg; + struct npc_get_field_status_req *freq; + struct npc_get_field_status_rsp *frsp; struct npc_mcam_alloc_entry_req *req; struct npc_mcam_alloc_entry_rsp *rsp; int vf_vlan_max_flows; @@ -214,8 +216,29 @@ static int otx2_mcam_entry_init(struct otx2_nic *pfvf) flow_cfg->rx_vlan_offset = flow_cfg->unicast_offset + OTX2_MAX_UNICAST_FLOWS; pfvf->flags |= OTX2_FLAG_UCAST_FLTR_SUPPORT; - pfvf->flags |= OTX2_FLAG_RX_VLAN_SUPPORT; - pfvf->flags |= OTX2_FLAG_VF_VLAN_SUPPORT; + + /* Check if NPC_DMAC field is supported + * by the mkex profile before setting VLAN support flag. + */ + freq = otx2_mbox_alloc_msg_npc_get_field_status(&pfvf->mbox); + if (!freq) { + mutex_unlock(&pfvf->mbox.lock); + return -ENOMEM; + } + + freq->field = NPC_DMAC; + if (otx2_sync_mbox_msg(&pfvf->mbox)) { + mutex_unlock(&pfvf->mbox.lock); + return -EINVAL; + } + + frsp = (struct npc_get_field_status_rsp *)otx2_mbox_get_rsp + (&pfvf->mbox.mbox, 0, &freq->hdr); + + if (frsp->enable) { + pfvf->flags |= OTX2_FLAG_RX_VLAN_SUPPORT; + pfvf->flags |= OTX2_FLAG_VF_VLAN_SUPPORT; + } pfvf->flags |= OTX2_FLAG_MCAM_ENTRIES_ALLOC; mutex_unlock(&pfvf->mbox.lock); From 61b0853d0314a474f9135537ebf96ef85a7216df Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Mon, 14 Nov 2022 17:01:11 -0600 Subject: [PATCH 1134/2002] wifi: brcmfmac: Replace one-element array with flexible-array member One-element arrays are deprecated, and we are replacing them with flexible array members instead. So, replace one-element array with flexible-array member in struct brcmf_gscan_config. Important to mention is that doing a build before/after this patch results in no binary output differences. This helps with the ongoing efforts to tighten the FORTIFY_SOURCE routines on memcpy() and help us make progress towards globally enabling -fstrict-flex-arrays=3 [1]. Link: https://github.com/KSPP/linux/issues/79 Link: https://github.com/KSPP/linux/issues/241 Link: https://gcc.gnu.org/pipermail/gcc-patches/2022-October/602902.html [1] Signed-off-by: Gustavo A. R. Silva Reviewed-by: Kees Cook Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/7694550aa9a2753a73a687f61af9441c8cf52fd7.1668466470.git.gustavoars@kernel.org --- drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h | 2 +- drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h index f518e025d6e4..62f65dc6a3b9 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h @@ -1049,7 +1049,7 @@ struct brcmf_gscan_config { u8 count_of_channel_buckets; u8 retry_threshold; __le16 lost_ap_window; - struct brcmf_gscan_bucket_config bucket[1]; + struct brcmf_gscan_bucket_config bucket[]; }; /** diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.c index 170c61c8136c..7c5da506637f 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.c @@ -405,7 +405,7 @@ static int brcmf_pno_config_sched_scans(struct brcmf_if *ifp) if (n_buckets < 0) return n_buckets; - gsz = sizeof(*gscan_cfg) + (n_buckets - 1) * sizeof(*buckets); + gsz = sizeof(*gscan_cfg) + n_buckets * sizeof(*buckets); gscan_cfg = kzalloc(gsz, GFP_KERNEL); if (!gscan_cfg) { err = -ENOMEM; From f0e0897b4c7eb590581463125c553bd6bb029808 Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Mon, 14 Nov 2022 17:02:06 -0600 Subject: [PATCH 1135/2002] wifi: brcmfmac: Use struct_size() and array_size() in code ralated to struct brcmf_gscan_config Prefer struct_size() over open-coded versions of idiom: sizeof(struct-with-flex-array) + sizeof(typeof-flex-array-elements) * count where count is the max number of items the flexible array is supposed to contain. Also, use array_size() in call to memcpy(). Link: https://github.com/KSPP/linux/issues/160 Signed-off-by: Gustavo A. R. Silva Reviewed-by: Kees Cook Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/de0226a549c8d000d8974e207ede786220a3df1a.1668466470.git.gustavoars@kernel.org --- drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.c index 7c5da506637f..05f66ab13bed 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.c @@ -405,7 +405,7 @@ static int brcmf_pno_config_sched_scans(struct brcmf_if *ifp) if (n_buckets < 0) return n_buckets; - gsz = sizeof(*gscan_cfg) + n_buckets * sizeof(*buckets); + gsz = struct_size(gscan_cfg, bucket, n_buckets); gscan_cfg = kzalloc(gsz, GFP_KERNEL); if (!gscan_cfg) { err = -ENOMEM; @@ -434,8 +434,8 @@ static int brcmf_pno_config_sched_scans(struct brcmf_if *ifp) gscan_cfg->flags = BRCMF_GSCAN_CFG_ALL_BUCKETS_IN_1ST_SCAN; gscan_cfg->count_of_channel_buckets = n_buckets; - memcpy(&gscan_cfg->bucket[0], buckets, - n_buckets * sizeof(*buckets)); + memcpy(gscan_cfg->bucket, buckets, + array_size(n_buckets, sizeof(*buckets))); err = brcmf_fil_iovar_data_set(ifp, "pfn_gscan_cfg", gscan_cfg, gsz); From 0001650b3d89683bbea0cc3262c2a509dac9e78e Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Tue, 15 Nov 2022 15:53:27 -0600 Subject: [PATCH 1136/2002] wifi: brcmfmac: replace one-element array with flexible-array member in struct brcmf_dload_data_le One-element arrays are deprecated, and we are replacing them with flexible array members instead. So, replace one-element array with flexible-array member in struct brcmf_dload_data_le. Important to mention is that doing a build before/after this patch results in no binary output differences. This helps with the ongoing efforts to tighten the FORTIFY_SOURCE routines on memcpy() and help us make progress towards globally enabling -fstrict-flex-arrays=3 [1]. Link: https://github.com/KSPP/linux/issues/230 Link: https://github.com/KSPP/linux/issues/79 Link: https://gcc.gnu.org/pipermail/gcc-patches/2022-October/602902.html [1] Signed-off-by: Gustavo A. R. Silva Reviewed-by: Kees Cook Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/905f5b68cf93c812360d081caae5b15221db09b6.1668548907.git.gustavoars@kernel.org --- drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c | 4 ++-- drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c index 22344e68fd59..2e836566e218 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c @@ -110,7 +110,7 @@ static int brcmf_c_download(struct brcmf_if *ifp, u16 flag, dload_buf->dload_type = cpu_to_le16(DL_TYPE_CLM); dload_buf->len = cpu_to_le32(len); dload_buf->crc = cpu_to_le32(0); - len = sizeof(*dload_buf) + len - 1; + len = sizeof(*dload_buf) + len; err = brcmf_fil_iovar_data_set(ifp, "clmload", dload_buf, len); @@ -139,7 +139,7 @@ static int brcmf_c_process_clm_blob(struct brcmf_if *ifp) return 0; } - chunk_buf = kzalloc(sizeof(*chunk_buf) + MAX_CHUNK_LEN - 1, GFP_KERNEL); + chunk_buf = kzalloc(sizeof(*chunk_buf) + MAX_CHUNK_LEN, GFP_KERNEL); if (!chunk_buf) { err = -ENOMEM; goto done; diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h index 62f65dc6a3b9..04e1beedfd81 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h @@ -943,7 +943,7 @@ struct brcmf_dload_data_le { __le16 dload_type; __le32 len; __le32 crc; - u8 data[1]; + u8 data[]; }; /** From 633a9b6f514c12b3ee42b3a4e647f137aca1e198 Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Tue, 15 Nov 2022 15:55:34 -0600 Subject: [PATCH 1137/2002] wifi: brcmfmac: Use struct_size() in code ralated to struct brcmf_dload_data_le Prefer struct_size() over open-coded versions of idiom: sizeof(struct-with-flex-array) + sizeof(typeof-flex-array-elements) * count where count is the max number of items the flexible array is supposed to contain. In this particular case, in the open-coded version sizeof(typeof-flex-array-elements) is implicit in _count_ because the type of the flex array data is u8: drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h:941: 941 struct brcmf_dload_data_le { 942 __le16 flag; 943 __le16 dload_type; 944 __le32 len; 945 __le32 crc; 946 u8 data[]; 947 }; Link: https://github.com/KSPP/linux/issues/160 Signed-off-by: Gustavo A. R. Silva Reviewed-by: Kees Cook Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/41845ad3660ed4375f0c03fd36a67b2e12fafed5.1668548907.git.gustavoars@kernel.org --- drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c index 2e836566e218..4a309e5a5707 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c @@ -110,9 +110,9 @@ static int brcmf_c_download(struct brcmf_if *ifp, u16 flag, dload_buf->dload_type = cpu_to_le16(DL_TYPE_CLM); dload_buf->len = cpu_to_le32(len); dload_buf->crc = cpu_to_le32(0); - len = sizeof(*dload_buf) + len; - err = brcmf_fil_iovar_data_set(ifp, "clmload", dload_buf, len); + err = brcmf_fil_iovar_data_set(ifp, "clmload", dload_buf, + struct_size(dload_buf, data, len)); return err; } @@ -139,7 +139,8 @@ static int brcmf_c_process_clm_blob(struct brcmf_if *ifp) return 0; } - chunk_buf = kzalloc(sizeof(*chunk_buf) + MAX_CHUNK_LEN, GFP_KERNEL); + chunk_buf = kzalloc(struct_size(chunk_buf, data, MAX_CHUNK_LEN), + GFP_KERNEL); if (!chunk_buf) { err = -ENOMEM; goto done; From 79ca91a3c1f1e5d871f393791e7538f9386a7711 Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Thu, 17 Nov 2022 14:18:32 +0800 Subject: [PATCH 1138/2002] wifi: rtw89: 8852b: correct TX power controlled by BT-coexistence When coexistence mechanism is under free-run mode, it could adjust WiFi and BT TX power to avoid interference with each other. For other cases, it should keep original TX power from regular predefined tables, so set correct values to 255 for these cases. Signed-off-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20221117061832.42057-1-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/rtw8852b.c | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852b.c b/drivers/net/wireless/realtek/rtw89/rtw8852b.c index 0df044b1c392..85dfc1ebb0d9 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852b.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852b.c @@ -318,27 +318,27 @@ static const struct rtw89_dig_regs rtw8852b_dig_regs = { }; static const struct rtw89_btc_rf_trx_para rtw89_btc_8852b_rf_ul[] = { - {15, 0, 0, 7}, /* 0 -> original */ - {15, 2, 0, 7}, /* 1 -> for BT-connected ACI issue && BTG co-rx */ - {15, 0, 0, 7}, /* 2 ->reserved for shared-antenna */ - {15, 0, 0, 7}, /* 3- >reserved for shared-antenna */ - {15, 0, 0, 7}, /* 4 ->reserved for shared-antenna */ - {15, 0, 0, 7}, /* the below id is for non-shared-antenna free-run */ + {255, 0, 0, 7}, /* 0 -> original */ + {255, 2, 0, 7}, /* 1 -> for BT-connected ACI issue && BTG co-rx */ + {255, 0, 0, 7}, /* 2 ->reserved for shared-antenna */ + {255, 0, 0, 7}, /* 3- >reserved for shared-antenna */ + {255, 0, 0, 7}, /* 4 ->reserved for shared-antenna */ + {255, 0, 0, 7}, /* the below id is for non-shared-antenna free-run */ {6, 1, 0, 7}, {13, 1, 0, 7}, {13, 1, 0, 7} }; static const struct rtw89_btc_rf_trx_para rtw89_btc_8852b_rf_dl[] = { - {15, 0, 0, 7}, /* 0 -> original */ - {15, 2, 0, 7}, /* 1 -> reserved for shared-antenna */ - {15, 0, 0, 7}, /* 2 ->reserved for shared-antenna */ - {15, 0, 0, 7}, /* 3- >reserved for shared-antenna */ - {15, 0, 0, 7}, /* 4 ->reserved for shared-antenna */ - {15, 0, 0, 7}, /* the below id is for non-shared-antenna free-run */ - {15, 1, 0, 7}, - {15, 1, 0, 7}, - {15, 1, 0, 7} + {255, 0, 0, 7}, /* 0 -> original */ + {255, 2, 0, 7}, /* 1 -> reserved for shared-antenna */ + {255, 0, 0, 7}, /* 2 ->reserved for shared-antenna */ + {255, 0, 0, 7}, /* 3- >reserved for shared-antenna */ + {255, 0, 0, 7}, /* 4 ->reserved for shared-antenna */ + {255, 0, 0, 7}, /* the below id is for non-shared-antenna free-run */ + {255, 1, 0, 7}, + {255, 1, 0, 7}, + {255, 1, 0, 7} }; static const struct rtw89_btc_fbtc_mreg rtw89_btc_8852b_mon_reg[] = { From 10cd4092f67eaf0cade4f50c68ecb055d4ee3a93 Mon Sep 17 00:00:00 2001 From: Eric Huang Date: Thu, 17 Nov 2022 14:30:00 +0800 Subject: [PATCH 1139/2002] wifi: rtw89: read CFO from FD or preamble CFO field of phy status ie_type 1 accordingly Add macro to get FD(frequency domain) CFO field from ie_type 1, and correct the naming for preamble CFO field. Each IC could assign the CFO source to either FD CFO or preamble CFO in chip_info. Based on the suggestion from HW designer, rtw8852b and its derived versions will have better CFO tracking performance with FD CFO. Signed-off-by: Eric Huang Signed-off-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20221117063001.42967-2-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/core.c | 6 +++++- drivers/net/wireless/realtek/rtw89/core.h | 1 + drivers/net/wireless/realtek/rtw89/rtw8852a.c | 1 + drivers/net/wireless/realtek/rtw89/rtw8852b.c | 1 + drivers/net/wireless/realtek/rtw89/rtw8852c.c | 1 + drivers/net/wireless/realtek/rtw89/txrx.h | 4 +++- 6 files changed, 12 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c index f30aadc41f2b..c0a4667fd7e2 100644 --- a/drivers/net/wireless/realtek/rtw89/core.c +++ b/drivers/net/wireless/realtek/rtw89/core.c @@ -1196,7 +1196,11 @@ static void rtw89_core_parse_phy_status_ie01(struct rtw89_dev *rtwdev, u8 *addr, if (phy_ppdu->rate < RTW89_HW_RATE_OFDM6) return; /* sign conversion for S(12,2) */ - cfo = sign_extend32(RTW89_GET_PHY_STS_IE01_CFO(addr), 11); + if (rtwdev->chip->cfo_src_fd) + cfo = sign_extend32(RTW89_GET_PHY_STS_IE01_FD_CFO(addr), 11); + else + cfo = sign_extend32(RTW89_GET_PHY_STS_IE01_PREMB_CFO(addr), 11); + rtw89_phy_cfo_parse(rtwdev, cfo, phy_ppdu); } diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index b60de6662548..ba4ccbde5ecf 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -2755,6 +2755,7 @@ struct rtw89_chip_info { u32 c2h_ctrl_reg; const u32 *c2h_regs; const struct rtw89_page_regs *page_regs; + bool cfo_src_fd; const struct rtw89_reg_def *dcfo_comp; u8 dcfo_comp_sft; const struct rtw89_imr_info *imr_info; diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852a.c b/drivers/net/wireless/realtek/rtw89/rtw8852a.c index 4cea5fb4327d..f38a330698e9 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852a.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852a.c @@ -2143,6 +2143,7 @@ const struct rtw89_chip_info rtw8852a_chip_info = { .c2h_ctrl_reg = R_AX_C2HREG_CTRL, .c2h_regs = rtw8852a_c2h_regs, .page_regs = &rtw8852a_page_regs, + .cfo_src_fd = false, .dcfo_comp = &rtw8852a_dcfo_comp, .dcfo_comp_sft = 3, .imr_info = &rtw8852a_imr_info, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852b.c b/drivers/net/wireless/realtek/rtw89/rtw8852b.c index 85dfc1ebb0d9..22b3c86ee7d8 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852b.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852b.c @@ -2512,6 +2512,7 @@ const struct rtw89_chip_info rtw8852b_chip_info = { .c2h_ctrl_reg = R_AX_C2HREG_CTRL, .c2h_regs = rtw8852b_c2h_regs, .page_regs = &rtw8852b_page_regs, + .cfo_src_fd = true, .dcfo_comp = &rtw8852b_dcfo_comp, .dcfo_comp_sft = 3, .imr_info = &rtw8852b_imr_info, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852c.c b/drivers/net/wireless/realtek/rtw89/rtw8852c.c index 6619ba730719..01e6358fa1a2 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852c.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852c.c @@ -2953,6 +2953,7 @@ const struct rtw89_chip_info rtw8852c_chip_info = { .c2h_ctrl_reg = R_AX_C2HREG_CTRL_V1, .c2h_regs = rtw8852c_c2h_regs, .page_regs = &rtw8852c_page_regs, + .cfo_src_fd = false, .dcfo_comp = &rtw8852c_dcfo_comp, .dcfo_comp_sft = 5, .imr_info = &rtw8852c_imr_info, diff --git a/drivers/net/wireless/realtek/rtw89/txrx.h b/drivers/net/wireless/realtek/rtw89/txrx.h index b889e7bf34c0..9d4c6b6fa125 100644 --- a/drivers/net/wireless/realtek/rtw89/txrx.h +++ b/drivers/net/wireless/realtek/rtw89/txrx.h @@ -298,7 +298,9 @@ le32_get_bits(*((const __le32 *)ie), GENMASK(11, 5)) #define RTW89_GET_PHY_STS_IE01_CH_IDX(ie) \ le32_get_bits(*((const __le32 *)ie), GENMASK(23, 16)) -#define RTW89_GET_PHY_STS_IE01_CFO(ie) \ +#define RTW89_GET_PHY_STS_IE01_FD_CFO(ie) \ + le32_get_bits(*((const __le32 *)(ie) + 1), GENMASK(19, 8)) +#define RTW89_GET_PHY_STS_IE01_PREMB_CFO(ie) \ le32_get_bits(*((const __le32 *)(ie) + 1), GENMASK(31, 20)) enum rtw89_tx_channel { From 29136c95fdc5d9bbfb56131408388fefdba4ed95 Mon Sep 17 00:00:00 2001 From: Eric Huang Date: Thu, 17 Nov 2022 14:30:01 +0800 Subject: [PATCH 1140/2002] wifi: rtw89: switch BANDEDGE and TX_SHAPE based on OFDMA trigger frame There are some registers for transmit waveform control, two of them used in this change are for BANDEDGE and TX_SHAPE control. BANDEDGE controls whether to apply band edge filter to transmit waveform. TX_SHAPE controls whether to apply triangular mask to transmit waveform. It is found for some chip, these two should be turned off during OFDMA UL traffic for better performance. Signed-off-by: Eric Huang Signed-off-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20221117063001.42967-3-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/core.c | 2 + drivers/net/wireless/realtek/rtw89/core.h | 9 ++ drivers/net/wireless/realtek/rtw89/debug.h | 1 + drivers/net/wireless/realtek/rtw89/phy.c | 127 +++++++++++++++++- drivers/net/wireless/realtek/rtw89/phy.h | 5 + drivers/net/wireless/realtek/rtw89/rtw8852a.c | 1 + drivers/net/wireless/realtek/rtw89/rtw8852b.c | 1 + drivers/net/wireless/realtek/rtw89/rtw8852c.c | 1 + 8 files changed, 146 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c index c0a4667fd7e2..a5fe45d79f18 100644 --- a/drivers/net/wireless/realtek/rtw89/core.c +++ b/drivers/net/wireless/realtek/rtw89/core.c @@ -2237,6 +2237,7 @@ static void rtw89_track_work(struct work_struct *work) rtw89_phy_ra_update(rtwdev); rtw89_phy_cfo_track(rtwdev); rtw89_phy_tx_path_div_track(rtwdev); + rtw89_phy_ul_tb_ctrl_track(rtwdev); if (rtwdev->lps_enabled && !rtwdev->btc.lps) rtw89_enter_lps_track(rtwdev); @@ -2560,6 +2561,7 @@ int rtw89_core_sta_assoc(struct rtw89_dev *rtwdev, rtw89_btc_ntfy_role_info(rtwdev, rtwvif, rtwsta, BTC_ROLE_MSTS_STA_CONN_END); rtw89_core_get_no_ul_ofdma_htc(rtwdev, &rtwsta->htc_template); + rtw89_phy_ul_tb_assoc(rtwdev, rtwvif); } return ret; diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index ba4ccbde5ecf..62d834dbff67 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -2261,6 +2261,8 @@ struct rtw89_vif { bool wowlan_magic; bool is_hesta; bool last_a_ctrl; + bool dyn_tb_bedge_en; + u8 def_tri_idx; struct work_struct update_beacon_work; struct rtw89_addr_cam_entry addr_cam; struct rtw89_bssid_cam_entry bssid_cam; @@ -2646,6 +2648,11 @@ struct rtw89_dig_regs { struct rtw89_reg_def p1_s20_pagcugc_en; }; +struct rtw89_phy_ul_tb_info { + bool dyn_tb_tri_en; + u8 def_if_bandedge; +}; + struct rtw89_chip_info { enum rtw89_core_chip_id chip_id; const struct rtw89_chip_ops *ops; @@ -2663,6 +2670,7 @@ struct rtw89_chip_info { u8 support_chanctx_num; u8 support_bands; bool support_bw160; + bool support_ul_tb_ctrl; bool hw_sec_hdr; u8 rf_path_num; u8 tx_nss; @@ -3585,6 +3593,7 @@ struct rtw89_dev { struct rtw89_phy_ch_info ch_info; struct rtw89_phy_bb_gain_info bb_gain; struct rtw89_phy_efuse_gain efuse_gain; + struct rtw89_phy_ul_tb_info ul_tb_info; struct delayed_work track_work; struct delayed_work coex_act1_work; diff --git a/drivers/net/wireless/realtek/rtw89/debug.h b/drivers/net/wireless/realtek/rtw89/debug.h index e7971583acbc..d1de5e600836 100644 --- a/drivers/net/wireless/realtek/rtw89/debug.h +++ b/drivers/net/wireless/realtek/rtw89/debug.h @@ -27,6 +27,7 @@ enum rtw89_debug_mask { RTW89_DBG_SAR = BIT(16), RTW89_DBG_STATE = BIT(17), RTW89_DBG_WOW = BIT(18), + RTW89_DBG_UL_TB = BIT(19), RTW89_DBG_UNEXP = BIT(31), }; diff --git a/drivers/net/wireless/realtek/rtw89/phy.c b/drivers/net/wireless/realtek/rtw89/phy.c index dd4685698983..baa48b1058c5 100644 --- a/drivers/net/wireless/realtek/rtw89/phy.c +++ b/drivers/net/wireless/realtek/rtw89/phy.c @@ -2,6 +2,7 @@ /* Copyright(c) 2019-2020 Realtek Corporation */ +#include "coex.h" #include "debug.h" #include "fw.h" #include "mac.h" @@ -9,7 +10,7 @@ #include "ps.h" #include "reg.h" #include "sar.h" -#include "coex.h" +#include "util.h" static u16 get_max_amsdu_len(struct rtw89_dev *rtwdev, const struct rtw89_ra_report *report) @@ -2794,6 +2795,129 @@ void rtw89_phy_cfo_parse(struct rtw89_dev *rtwdev, s16 cfo_val, cfo->packet_count++; } +void rtw89_phy_ul_tb_assoc(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif) +{ + const struct rtw89_chip_info *chip = rtwdev->chip; + const struct rtw89_chan *chan = rtw89_chan_get(rtwdev, RTW89_SUB_ENTITY_0); + struct rtw89_phy_ul_tb_info *ul_tb_info = &rtwdev->ul_tb_info; + + if (!chip->support_ul_tb_ctrl) + return; + + rtwvif->def_tri_idx = + rtw89_phy_read32_mask(rtwdev, R_DCFO_OPT, B_TXSHAPE_TRIANGULAR_CFG); + + if (chip->chip_id == RTL8852B && rtwdev->hal.cv > CHIP_CBV) + rtwvif->dyn_tb_bedge_en = false; + else if (chan->band_type >= RTW89_BAND_5G && + chan->band_width >= RTW89_CHANNEL_WIDTH_40) + rtwvif->dyn_tb_bedge_en = true; + else + rtwvif->dyn_tb_bedge_en = false; + + rtw89_debug(rtwdev, RTW89_DBG_UL_TB, + "[ULTB] def_if_bandedge=%d, def_tri_idx=%d\n", + ul_tb_info->def_if_bandedge, rtwvif->def_tri_idx); + rtw89_debug(rtwdev, RTW89_DBG_UL_TB, + "[ULTB] dyn_tb_begde_en=%d, dyn_tb_tri_en=%d\n", + rtwvif->dyn_tb_bedge_en, ul_tb_info->dyn_tb_tri_en); +} + +struct rtw89_phy_ul_tb_check_data { + bool valid; + bool high_tf_client; + bool low_tf_client; + bool dyn_tb_bedge_en; + u8 def_tri_idx; +}; + +static +void rtw89_phy_ul_tb_ctrl_check(struct rtw89_dev *rtwdev, + struct rtw89_vif *rtwvif, + struct rtw89_phy_ul_tb_check_data *ul_tb_data) +{ + struct rtw89_traffic_stats *stats = &rtwdev->stats; + struct ieee80211_vif *vif = rtwvif_to_vif(rtwvif); + + if (rtwvif->wifi_role != RTW89_WIFI_ROLE_STATION) + return; + + if (!vif->cfg.assoc) + return; + + if (stats->rx_tf_periodic > UL_TB_TF_CNT_L2H_TH) + ul_tb_data->high_tf_client = true; + else if (stats->rx_tf_periodic < UL_TB_TF_CNT_H2L_TH) + ul_tb_data->low_tf_client = true; + + ul_tb_data->valid = true; + ul_tb_data->def_tri_idx = rtwvif->def_tri_idx; + ul_tb_data->dyn_tb_bedge_en = rtwvif->dyn_tb_bedge_en; +} + +void rtw89_phy_ul_tb_ctrl_track(struct rtw89_dev *rtwdev) +{ + const struct rtw89_chip_info *chip = rtwdev->chip; + struct rtw89_phy_ul_tb_info *ul_tb_info = &rtwdev->ul_tb_info; + struct rtw89_phy_ul_tb_check_data ul_tb_data = {}; + struct rtw89_vif *rtwvif; + + if (!chip->support_ul_tb_ctrl) + return; + + if (rtwdev->total_sta_assoc != 1) + return; + + rtw89_for_each_rtwvif(rtwdev, rtwvif) + rtw89_phy_ul_tb_ctrl_check(rtwdev, rtwvif, &ul_tb_data); + + if (!ul_tb_data.valid) + return; + + if (ul_tb_data.dyn_tb_bedge_en) { + if (ul_tb_data.high_tf_client) { + rtw89_phy_write32_mask(rtwdev, R_BANDEDGE, B_BANDEDGE_EN, 0); + rtw89_debug(rtwdev, RTW89_DBG_UL_TB, + "[ULTB] Turn off if_bandedge\n"); + } else if (ul_tb_data.low_tf_client) { + rtw89_phy_write32_mask(rtwdev, R_BANDEDGE, B_BANDEDGE_EN, + ul_tb_info->def_if_bandedge); + rtw89_debug(rtwdev, RTW89_DBG_UL_TB, + "[ULTB] Set to default if_bandedge = %d\n", + ul_tb_info->def_if_bandedge); + } + } + + if (ul_tb_info->dyn_tb_tri_en) { + if (ul_tb_data.high_tf_client) { + rtw89_phy_write32_mask(rtwdev, R_DCFO_OPT, + B_TXSHAPE_TRIANGULAR_CFG, 0); + rtw89_debug(rtwdev, RTW89_DBG_UL_TB, + "[ULTB] Turn off Tx triangle\n"); + } else if (ul_tb_data.low_tf_client) { + rtw89_phy_write32_mask(rtwdev, R_DCFO_OPT, + B_TXSHAPE_TRIANGULAR_CFG, + ul_tb_data.def_tri_idx); + rtw89_debug(rtwdev, RTW89_DBG_UL_TB, + "[ULTB] Set to default tx_shap_idx = %d\n", + ul_tb_data.def_tri_idx); + } + } +} + +static void rtw89_phy_ul_tb_info_init(struct rtw89_dev *rtwdev) +{ + const struct rtw89_chip_info *chip = rtwdev->chip; + struct rtw89_phy_ul_tb_info *ul_tb_info = &rtwdev->ul_tb_info; + + if (!chip->support_ul_tb_ctrl) + return; + + ul_tb_info->dyn_tb_tri_en = true; + ul_tb_info->def_if_bandedge = + rtw89_phy_read32_mask(rtwdev, R_BANDEDGE, B_BANDEDGE_EN); +} + static void rtw89_phy_stat_thermal_update(struct rtw89_dev *rtwdev) { struct rtw89_phy_stat *phystat = &rtwdev->phystat; @@ -3980,6 +4104,7 @@ void rtw89_phy_dm_init(struct rtw89_dev *rtwdev) rtw89_physts_parsing_init(rtwdev); rtw89_phy_dig_init(rtwdev); rtw89_phy_cfo_init(rtwdev); + rtw89_phy_ul_tb_info_init(rtwdev); rtw89_phy_init_rf_nctl(rtwdev); rtw89_chip_rfk_init(rtwdev); diff --git a/drivers/net/wireless/realtek/rtw89/phy.h b/drivers/net/wireless/realtek/rtw89/phy.h index dac69a02e868..21233f094644 100644 --- a/drivers/net/wireless/realtek/rtw89/phy.h +++ b/drivers/net/wireless/realtek/rtw89/phy.h @@ -64,6 +64,9 @@ #define MAX_CFO_TOLERANCE 30 #define CFO_TF_CNT_TH 300 +#define UL_TB_TF_CNT_L2H_TH 100 +#define UL_TB_TF_CNT_H2L_TH 70 + #define CCX_MAX_PERIOD 2097 #define CCX_MAX_PERIOD_UNIT 32 #define MS_TO_4US_RATIO 250 @@ -550,5 +553,7 @@ void rtw89_phy_set_bss_color(struct rtw89_dev *rtwdev, struct ieee80211_vif *vif void rtw89_phy_tssi_ctrl_set_bandedge_cfg(struct rtw89_dev *rtwdev, enum rtw89_mac_idx mac_idx, enum rtw89_tssi_bandedge_cfg bandedge_cfg); +void rtw89_phy_ul_tb_assoc(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif); +void rtw89_phy_ul_tb_ctrl_track(struct rtw89_dev *rtwdev); #endif diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852a.c b/drivers/net/wireless/realtek/rtw89/rtw8852a.c index f38a330698e9..eff6519cf019 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852a.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852a.c @@ -2082,6 +2082,7 @@ const struct rtw89_chip_info rtw8852a_chip_info = { .support_bands = BIT(NL80211_BAND_2GHZ) | BIT(NL80211_BAND_5GHZ), .support_bw160 = false, + .support_ul_tb_ctrl = false, .hw_sec_hdr = false, .rf_path_num = 2, .tx_nss = 2, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852b.c b/drivers/net/wireless/realtek/rtw89/rtw8852b.c index 22b3c86ee7d8..2d4d572dc601 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852b.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852b.c @@ -2452,6 +2452,7 @@ const struct rtw89_chip_info rtw8852b_chip_info = { .support_bands = BIT(NL80211_BAND_2GHZ) | BIT(NL80211_BAND_5GHZ), .support_bw160 = false, + .support_ul_tb_ctrl = true, .hw_sec_hdr = false, .rf_path_num = 2, .tx_nss = 2, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852c.c b/drivers/net/wireless/realtek/rtw89/rtw8852c.c index 01e6358fa1a2..9bc98fd5d4ac 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852c.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852c.c @@ -2891,6 +2891,7 @@ const struct rtw89_chip_info rtw8852c_chip_info = { BIT(NL80211_BAND_5GHZ) | BIT(NL80211_BAND_6GHZ), .support_bw160 = true, + .support_ul_tb_ctrl = false, .hw_sec_hdr = true, .rf_path_num = 2, .tx_nss = 2, From ac3a9f1838d8f5e5f9c8b6e2582b65c48a1e7bc1 Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Thu, 17 Nov 2022 16:52:35 +0800 Subject: [PATCH 1141/2002] wifi: rtw89: avoid inaccessible IO operations during doing change_interface() During doing change_interface(), hardware is power-off, so some components are inaccessible and return error. This causes things unexpected, and we don't have a warning message for that. So, ignore some IO operations in this situation, and add a warning message to indicate something wrong. Signed-off-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20221117085235.53777-1-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/core.h | 1 + drivers/net/wireless/realtek/rtw89/mac.c | 7 +++++++ drivers/net/wireless/realtek/rtw89/mac80211.c | 11 ++++++++++- 3 files changed, 18 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index 62d834dbff67..1b0acb1c5450 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -2968,6 +2968,7 @@ enum rtw89_flags { RTW89_FLAG_CRASH_SIMULATING, RTW89_FLAG_WOWLAN, RTW89_FLAG_FORBIDDEN_TRACK_WROK, + RTW89_FLAG_CHANGING_INTERFACE, NUM_OF_RTW89_FLAGS, }; diff --git a/drivers/net/wireless/realtek/rtw89/mac.c b/drivers/net/wireless/realtek/rtw89/mac.c index ecd603a88134..6587cdf6ba62 100644 --- a/drivers/net/wireless/realtek/rtw89/mac.c +++ b/drivers/net/wireless/realtek/rtw89/mac.c @@ -3600,6 +3600,13 @@ int rtw89_mac_set_macid_pause(struct rtw89_dev *rtwdev, u8 macid, bool pause) u8 grp = macid >> 5; int ret; + /* If this is called by change_interface() in the case of P2P, it could + * be power-off, so ignore this operation. + */ + if (test_bit(RTW89_FLAG_CHANGING_INTERFACE, rtwdev->flags) && + !test_bit(RTW89_FLAG_POWERON, rtwdev->flags)) + return 0; + ret = rtw89_mac_check_mac_en(rtwdev, RTW89_MAC_0, RTW89_CMAC_SEL); if (ret) return ret; diff --git a/drivers/net/wireless/realtek/rtw89/mac80211.c b/drivers/net/wireless/realtek/rtw89/mac80211.c index 6e79bf899901..ce980d2f22c4 100644 --- a/drivers/net/wireless/realtek/rtw89/mac80211.c +++ b/drivers/net/wireless/realtek/rtw89/mac80211.c @@ -174,6 +174,9 @@ static int rtw89_ops_change_interface(struct ieee80211_hw *hw, enum nl80211_iftype type, bool p2p) { struct rtw89_dev *rtwdev = hw->priv; + int ret; + + set_bit(RTW89_FLAG_CHANGING_INTERFACE, rtwdev->flags); rtw89_debug(rtwdev, RTW89_DBG_STATE, "change vif %pM (%d)->(%d), p2p (%d)->(%d)\n", vif->addr, vif->type, type, vif->p2p, p2p); @@ -183,7 +186,13 @@ static int rtw89_ops_change_interface(struct ieee80211_hw *hw, vif->type = type; vif->p2p = p2p; - return rtw89_ops_add_interface(hw, vif); + ret = rtw89_ops_add_interface(hw, vif); + if (ret) + rtw89_warn(rtwdev, "failed to change interface %d\n", ret); + + clear_bit(RTW89_FLAG_CHANGING_INTERFACE, rtwdev->flags); + + return ret; } static void rtw89_ops_configure_filter(struct ieee80211_hw *hw, From 9e2f177de1bfb7d891bf38140bda54831ecef30d Mon Sep 17 00:00:00 2001 From: Zong-Zhe Yang Date: Fri, 18 Nov 2022 12:23:22 +0800 Subject: [PATCH 1142/2002] wifi: rtw89: fix physts IE page check The index RTW89_PHYSTS_BITMAP_NUM is not a valid physts IE page. So, fix the check condition. Fixes: eb4e52b3f38d ("rtw89: fix incorrect channel info during scan") Signed-off-by: Zong-Zhe Yang Signed-off-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20221118042322.26794-1-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/phy.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/realtek/rtw89/phy.c b/drivers/net/wireless/realtek/rtw89/phy.c index baa48b1058c5..017710c580c7 100644 --- a/drivers/net/wireless/realtek/rtw89/phy.c +++ b/drivers/net/wireless/realtek/rtw89/phy.c @@ -3464,7 +3464,7 @@ void rtw89_phy_env_monitor_track(struct rtw89_dev *rtwdev) static bool rtw89_physts_ie_page_valid(enum rtw89_phy_status_bitmap *ie_page) { - if (*ie_page > RTW89_PHYSTS_BITMAP_NUM || + if (*ie_page >= RTW89_PHYSTS_BITMAP_NUM || *ie_page == RTW89_RSVD_9) return false; else if (*ie_page > RTW89_RSVD_9) From 3b79d4bad3a0e73d9becb409a5f6112bc61e2c93 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Fri, 18 Nov 2022 15:42:44 -0800 Subject: [PATCH 1143/2002] wifi: p54: Replace zero-length array of trailing structs with flex-array Zero-length arrays are deprecated[1] and are being replaced with flexible array members in support of the ongoing efforts to tighten the FORTIFY_SOURCE routines on memcpy(), correctly instrument array indexing with UBSAN_BOUNDS, and to globally enable -fstrict-flex-arrays=3. Replace zero-length array with flexible-array member. This results in no differences in binary output (most especially because struct pda_antenna_gain is unused). The struct is kept for future reference. [1] https://github.com/KSPP/linux/issues/78 Cc: Christian Lamparter Cc: Kalle Valo Cc: "David S. Miller" Cc: Eric Dumazet Cc: Jakub Kicinski Cc: Paolo Abeni Cc: "Gustavo A. R. Silva" Cc: linux-wireless@vger.kernel.org Cc: netdev@vger.kernel.org Signed-off-by: Kees Cook Acked-by: Christian Lamparter Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20221118234240.gonna.369-kees@kernel.org --- drivers/net/wireless/intersil/p54/eeprom.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/intersil/p54/eeprom.h b/drivers/net/wireless/intersil/p54/eeprom.h index 1d0aaf54389a..641c4e79879e 100644 --- a/drivers/net/wireless/intersil/p54/eeprom.h +++ b/drivers/net/wireless/intersil/p54/eeprom.h @@ -108,10 +108,10 @@ struct pda_country { } __packed; struct pda_antenna_gain { - struct { + DECLARE_FLEX_ARRAY(struct { u8 gain_5GHz; /* 0.25 dBi units */ u8 gain_2GHz; /* 0.25 dBi units */ - } __packed antenna[0]; + } __packed, antenna); } __packed; struct pda_custom_wrapper { From 6dcd6d0152200794a26b52ff762a110268551ba6 Mon Sep 17 00:00:00 2001 From: Tiezhu Yang Date: Sat, 19 Nov 2022 10:55:04 +0800 Subject: [PATCH 1144/2002] samples: pktgen: Use "grep -E" instead of "egrep" The latest version of grep claims the egrep is now obsolete so the build now contains warnings that look like: egrep: warning: egrep is obsolescent; using grep -E fix this up by moving the related file to use "grep -E" instead. sed -i "s/egrep/grep -E/g" `grep egrep -rwl samples/pktgen` Here are the steps to install the latest grep: wget http://ftp.gnu.org/gnu/grep/grep-3.8.tar.gz tar xf grep-3.8.tar.gz cd grep-3.8 && ./configure && make sudo make install export PATH=/usr/local/bin:$PATH Signed-off-by: Tiezhu Yang Link: https://lore.kernel.org/r/1668826504-32162-1-git-send-email-yangtiezhu@loongson.cn Signed-off-by: Paolo Abeni --- samples/pktgen/functions.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/pktgen/functions.sh b/samples/pktgen/functions.sh index 933194257a24..dd4e53ae9b73 100644 --- a/samples/pktgen/functions.sh +++ b/samples/pktgen/functions.sh @@ -191,7 +191,7 @@ function extend_addr6() fi # if shrink '::' occurs multiple, it's malformed. - shrink=( $(egrep -o "$sep{2,}" <<< $addr) ) + shrink=( $(grep -E -o "$sep{2,}" <<< $addr) ) if [[ ${#shrink[@]} -ne 0 ]]; then if [[ ${#shrink[@]} -gt 1 || ( ${shrink[0]} != $sep2 ) ]]; then err 5 "Invalid IP6 address: $1" From 837a3d66d698516ad2330e122eba9752ec3a48ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20T=C3=B6pel?= Date: Sat, 19 Nov 2022 18:18:41 +0100 Subject: [PATCH 1145/2002] selftests: net: Add cross-compilation support for BPF programs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The selftests/net does not have proper cross-compilation support, and does not properly state libbpf as a dependency. Mimic/copy the BPF build from selftests/bpf, which has the nice side-effect that libbpf is built as well. Signed-off-by: Björn Töpel Reviewed-by: Anders Roxell Link: https://lore.kernel.org/r/20221119171841.2014936-1-bjorn@kernel.org Signed-off-by: Paolo Abeni --- tools/testing/selftests/net/bpf/Makefile | 45 +++++++++++++++++++++--- 1 file changed, 41 insertions(+), 4 deletions(-) diff --git a/tools/testing/selftests/net/bpf/Makefile b/tools/testing/selftests/net/bpf/Makefile index 8ccaf8732eb2..a26cb94354f6 100644 --- a/tools/testing/selftests/net/bpf/Makefile +++ b/tools/testing/selftests/net/bpf/Makefile @@ -1,14 +1,51 @@ # SPDX-License-Identifier: GPL-2.0 CLANG ?= clang +SCRATCH_DIR := $(OUTPUT)/tools +BUILD_DIR := $(SCRATCH_DIR)/build +BPFDIR := $(abspath ../../../lib/bpf) +APIDIR := $(abspath ../../../include/uapi) + CCINCLUDE += -I../../bpf -CCINCLUDE += -I../../../../lib CCINCLUDE += -I../../../../../usr/include/ +CCINCLUDE += -I$(SCRATCH_DIR)/include + +BPFOBJ := $(BUILD_DIR)/libbpf/libbpf.a + +MAKE_DIRS := $(BUILD_DIR)/libbpf +$(MAKE_DIRS): + mkdir -p $@ TEST_CUSTOM_PROGS = $(OUTPUT)/bpf/nat6to4.o all: $(TEST_CUSTOM_PROGS) -$(OUTPUT)/%.o: %.c - $(CLANG) -O2 -target bpf -c $< $(CCINCLUDE) -o $@ +# Get Clang's default includes on this system, as opposed to those seen by +# '-target bpf'. This fixes "missing" files on some architectures/distros, +# such as asm/byteorder.h, asm/socket.h, asm/sockios.h, sys/cdefs.h etc. +# +# Use '-idirafter': Don't interfere with include mechanics except where the +# build would have failed anyways. +define get_sys_includes +$(shell $(1) $(2) -v -E - &1 \ + | sed -n '/<...> search starts here:/,/End of search list./{ s| \(/.*\)|-idirafter \1|p }') \ +$(shell $(1) $(2) -dM -E - Date: Sat, 19 Nov 2022 15:14:04 -0800 Subject: [PATCH 1146/2002] net: mscc: ocelot: remove redundant stats_layout pointers Ever since commit 4d1d157fb6a4 ("net: mscc: ocelot: share the common stat definitions between all drivers") the stats_layout entry in ocelot and felix drivers have become redundant. Remove the unnecessary code. Suggested-by: Vladimir Oltean Signed-off-by: Colin Foster Reviewed-by: Vladimir Oltean Signed-off-by: Paolo Abeni --- drivers/net/dsa/ocelot/felix.c | 1 - drivers/net/dsa/ocelot/felix.h | 1 - drivers/net/dsa/ocelot/felix_vsc9959.c | 5 ----- drivers/net/dsa/ocelot/seville_vsc9953.c | 5 ----- drivers/net/ethernet/mscc/ocelot_stats.c | 20 ++++++++++++-------- drivers/net/ethernet/mscc/ocelot_vsc7514.c | 5 ----- include/soc/mscc/ocelot.h | 1 - 7 files changed, 12 insertions(+), 26 deletions(-) diff --git a/drivers/net/dsa/ocelot/felix.c b/drivers/net/dsa/ocelot/felix.c index 44e160f32067..3b738cb2ae6e 100644 --- a/drivers/net/dsa/ocelot/felix.c +++ b/drivers/net/dsa/ocelot/felix.c @@ -1363,7 +1363,6 @@ static int felix_init_structs(struct felix *felix, int num_phys_ports) return -ENOMEM; ocelot->map = felix->info->map; - ocelot->stats_layout = felix->info->stats_layout; ocelot->num_mact_rows = felix->info->num_mact_rows; ocelot->vcap = felix->info->vcap; ocelot->vcap_pol.base = felix->info->vcap_pol_base; diff --git a/drivers/net/dsa/ocelot/felix.h b/drivers/net/dsa/ocelot/felix.h index 42338116eed0..be22d6ccd7c8 100644 --- a/drivers/net/dsa/ocelot/felix.h +++ b/drivers/net/dsa/ocelot/felix.h @@ -28,7 +28,6 @@ struct felix_info { const struct ocelot_ops *ops; const u32 *port_modes; int num_mact_rows; - const struct ocelot_stat_layout *stats_layout; int num_ports; int num_tx_queues; struct vcap_props *vcap; diff --git a/drivers/net/dsa/ocelot/felix_vsc9959.c b/drivers/net/dsa/ocelot/felix_vsc9959.c index b0ae8d6156f6..01ac70fd7ddf 100644 --- a/drivers/net/dsa/ocelot/felix_vsc9959.c +++ b/drivers/net/dsa/ocelot/felix_vsc9959.c @@ -565,10 +565,6 @@ static const struct reg_field vsc9959_regfields[REGFIELD_MAX] = { [SYS_PAUSE_CFG_PAUSE_ENA] = REG_FIELD_ID(SYS_PAUSE_CFG, 0, 1, 7, 4), }; -static const struct ocelot_stat_layout vsc9959_stats_layout[OCELOT_NUM_STATS] = { - OCELOT_COMMON_STATS, -}; - static const struct vcap_field vsc9959_vcap_es0_keys[] = { [VCAP_ES0_EGR_PORT] = { 0, 3}, [VCAP_ES0_IGR_PORT] = { 3, 3}, @@ -2546,7 +2542,6 @@ static const struct felix_info felix_info_vsc9959 = { .regfields = vsc9959_regfields, .map = vsc9959_regmap, .ops = &vsc9959_ops, - .stats_layout = vsc9959_stats_layout, .vcap = vsc9959_vcap_props, .vcap_pol_base = VSC9959_VCAP_POLICER_BASE, .vcap_pol_max = VSC9959_VCAP_POLICER_MAX, diff --git a/drivers/net/dsa/ocelot/seville_vsc9953.c b/drivers/net/dsa/ocelot/seville_vsc9953.c index 6500c1697dd6..88ed3a2e487a 100644 --- a/drivers/net/dsa/ocelot/seville_vsc9953.c +++ b/drivers/net/dsa/ocelot/seville_vsc9953.c @@ -543,10 +543,6 @@ static const struct reg_field vsc9953_regfields[REGFIELD_MAX] = { [SYS_PAUSE_CFG_PAUSE_ENA] = REG_FIELD_ID(SYS_PAUSE_CFG, 0, 1, 11, 4), }; -static const struct ocelot_stat_layout vsc9953_stats_layout[OCELOT_NUM_STATS] = { - OCELOT_COMMON_STATS, -}; - static const struct vcap_field vsc9953_vcap_es0_keys[] = { [VCAP_ES0_EGR_PORT] = { 0, 4}, [VCAP_ES0_IGR_PORT] = { 4, 4}, @@ -970,7 +966,6 @@ static const struct felix_info seville_info_vsc9953 = { .regfields = vsc9953_regfields, .map = vsc9953_regmap, .ops = &vsc9953_ops, - .stats_layout = vsc9953_stats_layout, .vcap = vsc9953_vcap_props, .vcap_pol_base = VSC9953_VCAP_POLICER_BASE, .vcap_pol_max = VSC9953_VCAP_POLICER_MAX, diff --git a/drivers/net/ethernet/mscc/ocelot_stats.c b/drivers/net/ethernet/mscc/ocelot_stats.c index dbd20b125cea..5dc132f61d6a 100644 --- a/drivers/net/ethernet/mscc/ocelot_stats.c +++ b/drivers/net/ethernet/mscc/ocelot_stats.c @@ -9,6 +9,10 @@ #include #include "ocelot.h" +static const struct ocelot_stat_layout ocelot_stats_layout[OCELOT_NUM_STATS] = { + OCELOT_COMMON_STATS, +}; + /* Read the counters from hardware and keep them in region->buf. * Caller must hold &ocelot->stat_view_lock. */ @@ -93,10 +97,10 @@ void ocelot_get_strings(struct ocelot *ocelot, int port, u32 sset, u8 *data) return; for (i = 0; i < OCELOT_NUM_STATS; i++) { - if (ocelot->stats_layout[i].name[0] == '\0') + if (ocelot_stats_layout[i].name[0] == '\0') continue; - memcpy(data + i * ETH_GSTRING_LEN, ocelot->stats_layout[i].name, + memcpy(data + i * ETH_GSTRING_LEN, ocelot_stats_layout[i].name, ETH_GSTRING_LEN); } } @@ -137,7 +141,7 @@ int ocelot_get_sset_count(struct ocelot *ocelot, int port, int sset) return -EOPNOTSUPP; for (i = 0; i < OCELOT_NUM_STATS; i++) - if (ocelot->stats_layout[i].name[0] != '\0') + if (ocelot_stats_layout[i].name[0] != '\0') num_stats++; return num_stats; @@ -154,7 +158,7 @@ static void ocelot_port_ethtool_stats_cb(struct ocelot *ocelot, int port, for (i = 0; i < OCELOT_NUM_STATS; i++) { int index = port * OCELOT_NUM_STATS + i; - if (ocelot->stats_layout[i].name[0] == '\0') + if (ocelot_stats_layout[i].name[0] == '\0') continue; *data++ = ocelot->stats[index]; @@ -389,10 +393,10 @@ static int ocelot_prepare_stats_regions(struct ocelot *ocelot) INIT_LIST_HEAD(&ocelot->stats_regions); for (i = 0; i < OCELOT_NUM_STATS; i++) { - if (!ocelot->stats_layout[i].reg) + if (!ocelot_stats_layout[i].reg) continue; - if (region && ocelot->stats_layout[i].reg == last + 4) { + if (region && ocelot_stats_layout[i].reg == last + 4) { region->count++; } else { region = devm_kzalloc(ocelot->dev, sizeof(*region), @@ -400,12 +404,12 @@ static int ocelot_prepare_stats_regions(struct ocelot *ocelot) if (!region) return -ENOMEM; - region->base = ocelot->stats_layout[i].reg; + region->base = ocelot_stats_layout[i].reg; region->count = 1; list_add_tail(®ion->node, &ocelot->stats_regions); } - last = ocelot->stats_layout[i].reg; + last = ocelot_stats_layout[i].reg; } list_for_each_entry(region, &ocelot->stats_regions, node) { diff --git a/drivers/net/ethernet/mscc/ocelot_vsc7514.c b/drivers/net/ethernet/mscc/ocelot_vsc7514.c index 93431d2ff4f1..b097fd4a4061 100644 --- a/drivers/net/ethernet/mscc/ocelot_vsc7514.c +++ b/drivers/net/ethernet/mscc/ocelot_vsc7514.c @@ -100,10 +100,6 @@ static const struct reg_field ocelot_regfields[REGFIELD_MAX] = { [SYS_PAUSE_CFG_PAUSE_ENA] = REG_FIELD_ID(SYS_PAUSE_CFG, 0, 1, 12, 4), }; -static const struct ocelot_stat_layout ocelot_stats_layout[OCELOT_NUM_STATS] = { - OCELOT_COMMON_STATS, -}; - static void ocelot_pll5_init(struct ocelot *ocelot) { /* Configure PLL5. This will need a proper CCF driver @@ -138,7 +134,6 @@ static int ocelot_chip_init(struct ocelot *ocelot, const struct ocelot_ops *ops) int ret; ocelot->map = ocelot_regmap; - ocelot->stats_layout = ocelot_stats_layout; ocelot->num_mact_rows = 1024; ocelot->ops = ops; diff --git a/include/soc/mscc/ocelot.h b/include/soc/mscc/ocelot.h index 967ba30ea636..995b5950afe6 100644 --- a/include/soc/mscc/ocelot.h +++ b/include/soc/mscc/ocelot.h @@ -967,7 +967,6 @@ struct ocelot { struct regmap *targets[TARGET_MAX]; struct regmap_field *regfields[REGFIELD_MAX]; const u32 *const *map; - const struct ocelot_stat_layout *stats_layout; struct list_head stats_regions; u32 pool_size[OCELOT_SB_NUM][OCELOT_SB_POOL_NUM]; From a3bb8f521fd8703cdb429d3e52f3e6802706c87d Mon Sep 17 00:00:00 2001 From: Colin Foster Date: Sat, 19 Nov 2022 15:14:05 -0800 Subject: [PATCH 1147/2002] net: mscc: ocelot: remove unnecessary exposure of stats structures Since commit 4d1d157fb6a4 ("net: mscc: ocelot: share the common stat definitions between all drivers") there is no longer a need to share the stats structures to the world. Relocate these definitions to inside ocelot_stats.c instead of a global include header. Signed-off-by: Colin Foster Reviewed-by: Vladimir Oltean Signed-off-by: Paolo Abeni --- drivers/net/ethernet/mscc/ocelot_stats.c | 216 +++++++++++++++++++++++ include/soc/mscc/ocelot.h | 215 ---------------------- 2 files changed, 216 insertions(+), 215 deletions(-) diff --git a/drivers/net/ethernet/mscc/ocelot_stats.c b/drivers/net/ethernet/mscc/ocelot_stats.c index 5dc132f61d6a..68e9f450c468 100644 --- a/drivers/net/ethernet/mscc/ocelot_stats.c +++ b/drivers/net/ethernet/mscc/ocelot_stats.c @@ -9,6 +9,221 @@ #include #include "ocelot.h" +enum ocelot_stat { + OCELOT_STAT_RX_OCTETS, + OCELOT_STAT_RX_UNICAST, + OCELOT_STAT_RX_MULTICAST, + OCELOT_STAT_RX_BROADCAST, + OCELOT_STAT_RX_SHORTS, + OCELOT_STAT_RX_FRAGMENTS, + OCELOT_STAT_RX_JABBERS, + OCELOT_STAT_RX_CRC_ALIGN_ERRS, + OCELOT_STAT_RX_SYM_ERRS, + OCELOT_STAT_RX_64, + OCELOT_STAT_RX_65_127, + OCELOT_STAT_RX_128_255, + OCELOT_STAT_RX_256_511, + OCELOT_STAT_RX_512_1023, + OCELOT_STAT_RX_1024_1526, + OCELOT_STAT_RX_1527_MAX, + OCELOT_STAT_RX_PAUSE, + OCELOT_STAT_RX_CONTROL, + OCELOT_STAT_RX_LONGS, + OCELOT_STAT_RX_CLASSIFIED_DROPS, + OCELOT_STAT_RX_RED_PRIO_0, + OCELOT_STAT_RX_RED_PRIO_1, + OCELOT_STAT_RX_RED_PRIO_2, + OCELOT_STAT_RX_RED_PRIO_3, + OCELOT_STAT_RX_RED_PRIO_4, + OCELOT_STAT_RX_RED_PRIO_5, + OCELOT_STAT_RX_RED_PRIO_6, + OCELOT_STAT_RX_RED_PRIO_7, + OCELOT_STAT_RX_YELLOW_PRIO_0, + OCELOT_STAT_RX_YELLOW_PRIO_1, + OCELOT_STAT_RX_YELLOW_PRIO_2, + OCELOT_STAT_RX_YELLOW_PRIO_3, + OCELOT_STAT_RX_YELLOW_PRIO_4, + OCELOT_STAT_RX_YELLOW_PRIO_5, + OCELOT_STAT_RX_YELLOW_PRIO_6, + OCELOT_STAT_RX_YELLOW_PRIO_7, + OCELOT_STAT_RX_GREEN_PRIO_0, + OCELOT_STAT_RX_GREEN_PRIO_1, + OCELOT_STAT_RX_GREEN_PRIO_2, + OCELOT_STAT_RX_GREEN_PRIO_3, + OCELOT_STAT_RX_GREEN_PRIO_4, + OCELOT_STAT_RX_GREEN_PRIO_5, + OCELOT_STAT_RX_GREEN_PRIO_6, + OCELOT_STAT_RX_GREEN_PRIO_7, + OCELOT_STAT_TX_OCTETS, + OCELOT_STAT_TX_UNICAST, + OCELOT_STAT_TX_MULTICAST, + OCELOT_STAT_TX_BROADCAST, + OCELOT_STAT_TX_COLLISION, + OCELOT_STAT_TX_DROPS, + OCELOT_STAT_TX_PAUSE, + OCELOT_STAT_TX_64, + OCELOT_STAT_TX_65_127, + OCELOT_STAT_TX_128_255, + OCELOT_STAT_TX_256_511, + OCELOT_STAT_TX_512_1023, + OCELOT_STAT_TX_1024_1526, + OCELOT_STAT_TX_1527_MAX, + OCELOT_STAT_TX_YELLOW_PRIO_0, + OCELOT_STAT_TX_YELLOW_PRIO_1, + OCELOT_STAT_TX_YELLOW_PRIO_2, + OCELOT_STAT_TX_YELLOW_PRIO_3, + OCELOT_STAT_TX_YELLOW_PRIO_4, + OCELOT_STAT_TX_YELLOW_PRIO_5, + OCELOT_STAT_TX_YELLOW_PRIO_6, + OCELOT_STAT_TX_YELLOW_PRIO_7, + OCELOT_STAT_TX_GREEN_PRIO_0, + OCELOT_STAT_TX_GREEN_PRIO_1, + OCELOT_STAT_TX_GREEN_PRIO_2, + OCELOT_STAT_TX_GREEN_PRIO_3, + OCELOT_STAT_TX_GREEN_PRIO_4, + OCELOT_STAT_TX_GREEN_PRIO_5, + OCELOT_STAT_TX_GREEN_PRIO_6, + OCELOT_STAT_TX_GREEN_PRIO_7, + OCELOT_STAT_TX_AGED, + OCELOT_STAT_DROP_LOCAL, + OCELOT_STAT_DROP_TAIL, + OCELOT_STAT_DROP_YELLOW_PRIO_0, + OCELOT_STAT_DROP_YELLOW_PRIO_1, + OCELOT_STAT_DROP_YELLOW_PRIO_2, + OCELOT_STAT_DROP_YELLOW_PRIO_3, + OCELOT_STAT_DROP_YELLOW_PRIO_4, + OCELOT_STAT_DROP_YELLOW_PRIO_5, + OCELOT_STAT_DROP_YELLOW_PRIO_6, + OCELOT_STAT_DROP_YELLOW_PRIO_7, + OCELOT_STAT_DROP_GREEN_PRIO_0, + OCELOT_STAT_DROP_GREEN_PRIO_1, + OCELOT_STAT_DROP_GREEN_PRIO_2, + OCELOT_STAT_DROP_GREEN_PRIO_3, + OCELOT_STAT_DROP_GREEN_PRIO_4, + OCELOT_STAT_DROP_GREEN_PRIO_5, + OCELOT_STAT_DROP_GREEN_PRIO_6, + OCELOT_STAT_DROP_GREEN_PRIO_7, + OCELOT_NUM_STATS, +}; + +struct ocelot_stat_layout { + u32 reg; + char name[ETH_GSTRING_LEN]; +}; + +/* 32-bit counter checked for wraparound by ocelot_port_update_stats() + * and copied to ocelot->stats. + */ +#define OCELOT_STAT(kind) \ + [OCELOT_STAT_ ## kind] = { .reg = SYS_COUNT_ ## kind } +/* Same as above, except also exported to ethtool -S. Standard counters should + * only be exposed to more specific interfaces rather than by their string name. + */ +#define OCELOT_STAT_ETHTOOL(kind, ethtool_name) \ + [OCELOT_STAT_ ## kind] = { .reg = SYS_COUNT_ ## kind, .name = ethtool_name } + +#define OCELOT_COMMON_STATS \ + OCELOT_STAT_ETHTOOL(RX_OCTETS, "rx_octets"), \ + OCELOT_STAT_ETHTOOL(RX_UNICAST, "rx_unicast"), \ + OCELOT_STAT_ETHTOOL(RX_MULTICAST, "rx_multicast"), \ + OCELOT_STAT_ETHTOOL(RX_BROADCAST, "rx_broadcast"), \ + OCELOT_STAT_ETHTOOL(RX_SHORTS, "rx_shorts"), \ + OCELOT_STAT_ETHTOOL(RX_FRAGMENTS, "rx_fragments"), \ + OCELOT_STAT_ETHTOOL(RX_JABBERS, "rx_jabbers"), \ + OCELOT_STAT_ETHTOOL(RX_CRC_ALIGN_ERRS, "rx_crc_align_errs"), \ + OCELOT_STAT_ETHTOOL(RX_SYM_ERRS, "rx_sym_errs"), \ + OCELOT_STAT_ETHTOOL(RX_64, "rx_frames_below_65_octets"), \ + OCELOT_STAT_ETHTOOL(RX_65_127, "rx_frames_65_to_127_octets"), \ + OCELOT_STAT_ETHTOOL(RX_128_255, "rx_frames_128_to_255_octets"), \ + OCELOT_STAT_ETHTOOL(RX_256_511, "rx_frames_256_to_511_octets"), \ + OCELOT_STAT_ETHTOOL(RX_512_1023, "rx_frames_512_to_1023_octets"), \ + OCELOT_STAT_ETHTOOL(RX_1024_1526, "rx_frames_1024_to_1526_octets"), \ + OCELOT_STAT_ETHTOOL(RX_1527_MAX, "rx_frames_over_1526_octets"), \ + OCELOT_STAT_ETHTOOL(RX_PAUSE, "rx_pause"), \ + OCELOT_STAT_ETHTOOL(RX_CONTROL, "rx_control"), \ + OCELOT_STAT_ETHTOOL(RX_LONGS, "rx_longs"), \ + OCELOT_STAT_ETHTOOL(RX_CLASSIFIED_DROPS, "rx_classified_drops"), \ + OCELOT_STAT_ETHTOOL(RX_RED_PRIO_0, "rx_red_prio_0"), \ + OCELOT_STAT_ETHTOOL(RX_RED_PRIO_1, "rx_red_prio_1"), \ + OCELOT_STAT_ETHTOOL(RX_RED_PRIO_2, "rx_red_prio_2"), \ + OCELOT_STAT_ETHTOOL(RX_RED_PRIO_3, "rx_red_prio_3"), \ + OCELOT_STAT_ETHTOOL(RX_RED_PRIO_4, "rx_red_prio_4"), \ + OCELOT_STAT_ETHTOOL(RX_RED_PRIO_5, "rx_red_prio_5"), \ + OCELOT_STAT_ETHTOOL(RX_RED_PRIO_6, "rx_red_prio_6"), \ + OCELOT_STAT_ETHTOOL(RX_RED_PRIO_7, "rx_red_prio_7"), \ + OCELOT_STAT_ETHTOOL(RX_YELLOW_PRIO_0, "rx_yellow_prio_0"), \ + OCELOT_STAT_ETHTOOL(RX_YELLOW_PRIO_1, "rx_yellow_prio_1"), \ + OCELOT_STAT_ETHTOOL(RX_YELLOW_PRIO_2, "rx_yellow_prio_2"), \ + OCELOT_STAT_ETHTOOL(RX_YELLOW_PRIO_3, "rx_yellow_prio_3"), \ + OCELOT_STAT_ETHTOOL(RX_YELLOW_PRIO_4, "rx_yellow_prio_4"), \ + OCELOT_STAT_ETHTOOL(RX_YELLOW_PRIO_5, "rx_yellow_prio_5"), \ + OCELOT_STAT_ETHTOOL(RX_YELLOW_PRIO_6, "rx_yellow_prio_6"), \ + OCELOT_STAT_ETHTOOL(RX_YELLOW_PRIO_7, "rx_yellow_prio_7"), \ + OCELOT_STAT_ETHTOOL(RX_GREEN_PRIO_0, "rx_green_prio_0"), \ + OCELOT_STAT_ETHTOOL(RX_GREEN_PRIO_1, "rx_green_prio_1"), \ + OCELOT_STAT_ETHTOOL(RX_GREEN_PRIO_2, "rx_green_prio_2"), \ + OCELOT_STAT_ETHTOOL(RX_GREEN_PRIO_3, "rx_green_prio_3"), \ + OCELOT_STAT_ETHTOOL(RX_GREEN_PRIO_4, "rx_green_prio_4"), \ + OCELOT_STAT_ETHTOOL(RX_GREEN_PRIO_5, "rx_green_prio_5"), \ + OCELOT_STAT_ETHTOOL(RX_GREEN_PRIO_6, "rx_green_prio_6"), \ + OCELOT_STAT_ETHTOOL(RX_GREEN_PRIO_7, "rx_green_prio_7"), \ + OCELOT_STAT_ETHTOOL(TX_OCTETS, "tx_octets"), \ + OCELOT_STAT_ETHTOOL(TX_UNICAST, "tx_unicast"), \ + OCELOT_STAT_ETHTOOL(TX_MULTICAST, "tx_multicast"), \ + OCELOT_STAT_ETHTOOL(TX_BROADCAST, "tx_broadcast"), \ + OCELOT_STAT_ETHTOOL(TX_COLLISION, "tx_collision"), \ + OCELOT_STAT_ETHTOOL(TX_DROPS, "tx_drops"), \ + OCELOT_STAT_ETHTOOL(TX_PAUSE, "tx_pause"), \ + OCELOT_STAT_ETHTOOL(TX_64, "tx_frames_below_65_octets"), \ + OCELOT_STAT_ETHTOOL(TX_65_127, "tx_frames_65_to_127_octets"), \ + OCELOT_STAT_ETHTOOL(TX_128_255, "tx_frames_128_255_octets"), \ + OCELOT_STAT_ETHTOOL(TX_256_511, "tx_frames_256_511_octets"), \ + OCELOT_STAT_ETHTOOL(TX_512_1023, "tx_frames_512_1023_octets"), \ + OCELOT_STAT_ETHTOOL(TX_1024_1526, "tx_frames_1024_1526_octets"), \ + OCELOT_STAT_ETHTOOL(TX_1527_MAX, "tx_frames_over_1526_octets"), \ + OCELOT_STAT_ETHTOOL(TX_YELLOW_PRIO_0, "tx_yellow_prio_0"), \ + OCELOT_STAT_ETHTOOL(TX_YELLOW_PRIO_1, "tx_yellow_prio_1"), \ + OCELOT_STAT_ETHTOOL(TX_YELLOW_PRIO_2, "tx_yellow_prio_2"), \ + OCELOT_STAT_ETHTOOL(TX_YELLOW_PRIO_3, "tx_yellow_prio_3"), \ + OCELOT_STAT_ETHTOOL(TX_YELLOW_PRIO_4, "tx_yellow_prio_4"), \ + OCELOT_STAT_ETHTOOL(TX_YELLOW_PRIO_5, "tx_yellow_prio_5"), \ + OCELOT_STAT_ETHTOOL(TX_YELLOW_PRIO_6, "tx_yellow_prio_6"), \ + OCELOT_STAT_ETHTOOL(TX_YELLOW_PRIO_7, "tx_yellow_prio_7"), \ + OCELOT_STAT_ETHTOOL(TX_GREEN_PRIO_0, "tx_green_prio_0"), \ + OCELOT_STAT_ETHTOOL(TX_GREEN_PRIO_1, "tx_green_prio_1"), \ + OCELOT_STAT_ETHTOOL(TX_GREEN_PRIO_2, "tx_green_prio_2"), \ + OCELOT_STAT_ETHTOOL(TX_GREEN_PRIO_3, "tx_green_prio_3"), \ + OCELOT_STAT_ETHTOOL(TX_GREEN_PRIO_4, "tx_green_prio_4"), \ + OCELOT_STAT_ETHTOOL(TX_GREEN_PRIO_5, "tx_green_prio_5"), \ + OCELOT_STAT_ETHTOOL(TX_GREEN_PRIO_6, "tx_green_prio_6"), \ + OCELOT_STAT_ETHTOOL(TX_GREEN_PRIO_7, "tx_green_prio_7"), \ + OCELOT_STAT_ETHTOOL(TX_AGED, "tx_aged"), \ + OCELOT_STAT_ETHTOOL(DROP_LOCAL, "drop_local"), \ + OCELOT_STAT_ETHTOOL(DROP_TAIL, "drop_tail"), \ + OCELOT_STAT_ETHTOOL(DROP_YELLOW_PRIO_0, "drop_yellow_prio_0"), \ + OCELOT_STAT_ETHTOOL(DROP_YELLOW_PRIO_1, "drop_yellow_prio_1"), \ + OCELOT_STAT_ETHTOOL(DROP_YELLOW_PRIO_2, "drop_yellow_prio_2"), \ + OCELOT_STAT_ETHTOOL(DROP_YELLOW_PRIO_3, "drop_yellow_prio_3"), \ + OCELOT_STAT_ETHTOOL(DROP_YELLOW_PRIO_4, "drop_yellow_prio_4"), \ + OCELOT_STAT_ETHTOOL(DROP_YELLOW_PRIO_5, "drop_yellow_prio_5"), \ + OCELOT_STAT_ETHTOOL(DROP_YELLOW_PRIO_6, "drop_yellow_prio_6"), \ + OCELOT_STAT_ETHTOOL(DROP_YELLOW_PRIO_7, "drop_yellow_prio_7"), \ + OCELOT_STAT_ETHTOOL(DROP_GREEN_PRIO_0, "drop_green_prio_0"), \ + OCELOT_STAT_ETHTOOL(DROP_GREEN_PRIO_1, "drop_green_prio_1"), \ + OCELOT_STAT_ETHTOOL(DROP_GREEN_PRIO_2, "drop_green_prio_2"), \ + OCELOT_STAT_ETHTOOL(DROP_GREEN_PRIO_3, "drop_green_prio_3"), \ + OCELOT_STAT_ETHTOOL(DROP_GREEN_PRIO_4, "drop_green_prio_4"), \ + OCELOT_STAT_ETHTOOL(DROP_GREEN_PRIO_5, "drop_green_prio_5"), \ + OCELOT_STAT_ETHTOOL(DROP_GREEN_PRIO_6, "drop_green_prio_6"), \ + OCELOT_STAT_ETHTOOL(DROP_GREEN_PRIO_7, "drop_green_prio_7") + +struct ocelot_stats_region { + struct list_head node; + u32 base; + int count; + u32 *buf; +}; + static const struct ocelot_stat_layout ocelot_stats_layout[OCELOT_NUM_STATS] = { OCELOT_COMMON_STATS, }; @@ -460,3 +675,4 @@ void ocelot_stats_deinit(struct ocelot *ocelot) cancel_delayed_work(&ocelot->stats_work); destroy_workqueue(ocelot->stats_queue); } + diff --git a/include/soc/mscc/ocelot.h b/include/soc/mscc/ocelot.h index 995b5950afe6..df62be80a193 100644 --- a/include/soc/mscc/ocelot.h +++ b/include/soc/mscc/ocelot.h @@ -596,221 +596,6 @@ enum ocelot_ptp_pins { TOD_ACC_PIN }; -enum ocelot_stat { - OCELOT_STAT_RX_OCTETS, - OCELOT_STAT_RX_UNICAST, - OCELOT_STAT_RX_MULTICAST, - OCELOT_STAT_RX_BROADCAST, - OCELOT_STAT_RX_SHORTS, - OCELOT_STAT_RX_FRAGMENTS, - OCELOT_STAT_RX_JABBERS, - OCELOT_STAT_RX_CRC_ALIGN_ERRS, - OCELOT_STAT_RX_SYM_ERRS, - OCELOT_STAT_RX_64, - OCELOT_STAT_RX_65_127, - OCELOT_STAT_RX_128_255, - OCELOT_STAT_RX_256_511, - OCELOT_STAT_RX_512_1023, - OCELOT_STAT_RX_1024_1526, - OCELOT_STAT_RX_1527_MAX, - OCELOT_STAT_RX_PAUSE, - OCELOT_STAT_RX_CONTROL, - OCELOT_STAT_RX_LONGS, - OCELOT_STAT_RX_CLASSIFIED_DROPS, - OCELOT_STAT_RX_RED_PRIO_0, - OCELOT_STAT_RX_RED_PRIO_1, - OCELOT_STAT_RX_RED_PRIO_2, - OCELOT_STAT_RX_RED_PRIO_3, - OCELOT_STAT_RX_RED_PRIO_4, - OCELOT_STAT_RX_RED_PRIO_5, - OCELOT_STAT_RX_RED_PRIO_6, - OCELOT_STAT_RX_RED_PRIO_7, - OCELOT_STAT_RX_YELLOW_PRIO_0, - OCELOT_STAT_RX_YELLOW_PRIO_1, - OCELOT_STAT_RX_YELLOW_PRIO_2, - OCELOT_STAT_RX_YELLOW_PRIO_3, - OCELOT_STAT_RX_YELLOW_PRIO_4, - OCELOT_STAT_RX_YELLOW_PRIO_5, - OCELOT_STAT_RX_YELLOW_PRIO_6, - OCELOT_STAT_RX_YELLOW_PRIO_7, - OCELOT_STAT_RX_GREEN_PRIO_0, - OCELOT_STAT_RX_GREEN_PRIO_1, - OCELOT_STAT_RX_GREEN_PRIO_2, - OCELOT_STAT_RX_GREEN_PRIO_3, - OCELOT_STAT_RX_GREEN_PRIO_4, - OCELOT_STAT_RX_GREEN_PRIO_5, - OCELOT_STAT_RX_GREEN_PRIO_6, - OCELOT_STAT_RX_GREEN_PRIO_7, - OCELOT_STAT_TX_OCTETS, - OCELOT_STAT_TX_UNICAST, - OCELOT_STAT_TX_MULTICAST, - OCELOT_STAT_TX_BROADCAST, - OCELOT_STAT_TX_COLLISION, - OCELOT_STAT_TX_DROPS, - OCELOT_STAT_TX_PAUSE, - OCELOT_STAT_TX_64, - OCELOT_STAT_TX_65_127, - OCELOT_STAT_TX_128_255, - OCELOT_STAT_TX_256_511, - OCELOT_STAT_TX_512_1023, - OCELOT_STAT_TX_1024_1526, - OCELOT_STAT_TX_1527_MAX, - OCELOT_STAT_TX_YELLOW_PRIO_0, - OCELOT_STAT_TX_YELLOW_PRIO_1, - OCELOT_STAT_TX_YELLOW_PRIO_2, - OCELOT_STAT_TX_YELLOW_PRIO_3, - OCELOT_STAT_TX_YELLOW_PRIO_4, - OCELOT_STAT_TX_YELLOW_PRIO_5, - OCELOT_STAT_TX_YELLOW_PRIO_6, - OCELOT_STAT_TX_YELLOW_PRIO_7, - OCELOT_STAT_TX_GREEN_PRIO_0, - OCELOT_STAT_TX_GREEN_PRIO_1, - OCELOT_STAT_TX_GREEN_PRIO_2, - OCELOT_STAT_TX_GREEN_PRIO_3, - OCELOT_STAT_TX_GREEN_PRIO_4, - OCELOT_STAT_TX_GREEN_PRIO_5, - OCELOT_STAT_TX_GREEN_PRIO_6, - OCELOT_STAT_TX_GREEN_PRIO_7, - OCELOT_STAT_TX_AGED, - OCELOT_STAT_DROP_LOCAL, - OCELOT_STAT_DROP_TAIL, - OCELOT_STAT_DROP_YELLOW_PRIO_0, - OCELOT_STAT_DROP_YELLOW_PRIO_1, - OCELOT_STAT_DROP_YELLOW_PRIO_2, - OCELOT_STAT_DROP_YELLOW_PRIO_3, - OCELOT_STAT_DROP_YELLOW_PRIO_4, - OCELOT_STAT_DROP_YELLOW_PRIO_5, - OCELOT_STAT_DROP_YELLOW_PRIO_6, - OCELOT_STAT_DROP_YELLOW_PRIO_7, - OCELOT_STAT_DROP_GREEN_PRIO_0, - OCELOT_STAT_DROP_GREEN_PRIO_1, - OCELOT_STAT_DROP_GREEN_PRIO_2, - OCELOT_STAT_DROP_GREEN_PRIO_3, - OCELOT_STAT_DROP_GREEN_PRIO_4, - OCELOT_STAT_DROP_GREEN_PRIO_5, - OCELOT_STAT_DROP_GREEN_PRIO_6, - OCELOT_STAT_DROP_GREEN_PRIO_7, - OCELOT_NUM_STATS, -}; - -struct ocelot_stat_layout { - u32 reg; - char name[ETH_GSTRING_LEN]; -}; - -/* 32-bit counter checked for wraparound by ocelot_port_update_stats() - * and copied to ocelot->stats. - */ -#define OCELOT_STAT(kind) \ - [OCELOT_STAT_ ## kind] = { .reg = SYS_COUNT_ ## kind } -/* Same as above, except also exported to ethtool -S. Standard counters should - * only be exposed to more specific interfaces rather than by their string name. - */ -#define OCELOT_STAT_ETHTOOL(kind, ethtool_name) \ - [OCELOT_STAT_ ## kind] = { .reg = SYS_COUNT_ ## kind, .name = ethtool_name } - -#define OCELOT_COMMON_STATS \ - OCELOT_STAT_ETHTOOL(RX_OCTETS, "rx_octets"), \ - OCELOT_STAT_ETHTOOL(RX_UNICAST, "rx_unicast"), \ - OCELOT_STAT_ETHTOOL(RX_MULTICAST, "rx_multicast"), \ - OCELOT_STAT_ETHTOOL(RX_BROADCAST, "rx_broadcast"), \ - OCELOT_STAT_ETHTOOL(RX_SHORTS, "rx_shorts"), \ - OCELOT_STAT_ETHTOOL(RX_FRAGMENTS, "rx_fragments"), \ - OCELOT_STAT_ETHTOOL(RX_JABBERS, "rx_jabbers"), \ - OCELOT_STAT_ETHTOOL(RX_CRC_ALIGN_ERRS, "rx_crc_align_errs"), \ - OCELOT_STAT_ETHTOOL(RX_SYM_ERRS, "rx_sym_errs"), \ - OCELOT_STAT_ETHTOOL(RX_64, "rx_frames_below_65_octets"), \ - OCELOT_STAT_ETHTOOL(RX_65_127, "rx_frames_65_to_127_octets"), \ - OCELOT_STAT_ETHTOOL(RX_128_255, "rx_frames_128_to_255_octets"), \ - OCELOT_STAT_ETHTOOL(RX_256_511, "rx_frames_256_to_511_octets"), \ - OCELOT_STAT_ETHTOOL(RX_512_1023, "rx_frames_512_to_1023_octets"), \ - OCELOT_STAT_ETHTOOL(RX_1024_1526, "rx_frames_1024_to_1526_octets"), \ - OCELOT_STAT_ETHTOOL(RX_1527_MAX, "rx_frames_over_1526_octets"), \ - OCELOT_STAT_ETHTOOL(RX_PAUSE, "rx_pause"), \ - OCELOT_STAT_ETHTOOL(RX_CONTROL, "rx_control"), \ - OCELOT_STAT_ETHTOOL(RX_LONGS, "rx_longs"), \ - OCELOT_STAT_ETHTOOL(RX_CLASSIFIED_DROPS, "rx_classified_drops"), \ - OCELOT_STAT_ETHTOOL(RX_RED_PRIO_0, "rx_red_prio_0"), \ - OCELOT_STAT_ETHTOOL(RX_RED_PRIO_1, "rx_red_prio_1"), \ - OCELOT_STAT_ETHTOOL(RX_RED_PRIO_2, "rx_red_prio_2"), \ - OCELOT_STAT_ETHTOOL(RX_RED_PRIO_3, "rx_red_prio_3"), \ - OCELOT_STAT_ETHTOOL(RX_RED_PRIO_4, "rx_red_prio_4"), \ - OCELOT_STAT_ETHTOOL(RX_RED_PRIO_5, "rx_red_prio_5"), \ - OCELOT_STAT_ETHTOOL(RX_RED_PRIO_6, "rx_red_prio_6"), \ - OCELOT_STAT_ETHTOOL(RX_RED_PRIO_7, "rx_red_prio_7"), \ - OCELOT_STAT_ETHTOOL(RX_YELLOW_PRIO_0, "rx_yellow_prio_0"), \ - OCELOT_STAT_ETHTOOL(RX_YELLOW_PRIO_1, "rx_yellow_prio_1"), \ - OCELOT_STAT_ETHTOOL(RX_YELLOW_PRIO_2, "rx_yellow_prio_2"), \ - OCELOT_STAT_ETHTOOL(RX_YELLOW_PRIO_3, "rx_yellow_prio_3"), \ - OCELOT_STAT_ETHTOOL(RX_YELLOW_PRIO_4, "rx_yellow_prio_4"), \ - OCELOT_STAT_ETHTOOL(RX_YELLOW_PRIO_5, "rx_yellow_prio_5"), \ - OCELOT_STAT_ETHTOOL(RX_YELLOW_PRIO_6, "rx_yellow_prio_6"), \ - OCELOT_STAT_ETHTOOL(RX_YELLOW_PRIO_7, "rx_yellow_prio_7"), \ - OCELOT_STAT_ETHTOOL(RX_GREEN_PRIO_0, "rx_green_prio_0"), \ - OCELOT_STAT_ETHTOOL(RX_GREEN_PRIO_1, "rx_green_prio_1"), \ - OCELOT_STAT_ETHTOOL(RX_GREEN_PRIO_2, "rx_green_prio_2"), \ - OCELOT_STAT_ETHTOOL(RX_GREEN_PRIO_3, "rx_green_prio_3"), \ - OCELOT_STAT_ETHTOOL(RX_GREEN_PRIO_4, "rx_green_prio_4"), \ - OCELOT_STAT_ETHTOOL(RX_GREEN_PRIO_5, "rx_green_prio_5"), \ - OCELOT_STAT_ETHTOOL(RX_GREEN_PRIO_6, "rx_green_prio_6"), \ - OCELOT_STAT_ETHTOOL(RX_GREEN_PRIO_7, "rx_green_prio_7"), \ - OCELOT_STAT_ETHTOOL(TX_OCTETS, "tx_octets"), \ - OCELOT_STAT_ETHTOOL(TX_UNICAST, "tx_unicast"), \ - OCELOT_STAT_ETHTOOL(TX_MULTICAST, "tx_multicast"), \ - OCELOT_STAT_ETHTOOL(TX_BROADCAST, "tx_broadcast"), \ - OCELOT_STAT_ETHTOOL(TX_COLLISION, "tx_collision"), \ - OCELOT_STAT_ETHTOOL(TX_DROPS, "tx_drops"), \ - OCELOT_STAT_ETHTOOL(TX_PAUSE, "tx_pause"), \ - OCELOT_STAT_ETHTOOL(TX_64, "tx_frames_below_65_octets"), \ - OCELOT_STAT_ETHTOOL(TX_65_127, "tx_frames_65_to_127_octets"), \ - OCELOT_STAT_ETHTOOL(TX_128_255, "tx_frames_128_255_octets"), \ - OCELOT_STAT_ETHTOOL(TX_256_511, "tx_frames_256_511_octets"), \ - OCELOT_STAT_ETHTOOL(TX_512_1023, "tx_frames_512_1023_octets"), \ - OCELOT_STAT_ETHTOOL(TX_1024_1526, "tx_frames_1024_1526_octets"), \ - OCELOT_STAT_ETHTOOL(TX_1527_MAX, "tx_frames_over_1526_octets"), \ - OCELOT_STAT_ETHTOOL(TX_YELLOW_PRIO_0, "tx_yellow_prio_0"), \ - OCELOT_STAT_ETHTOOL(TX_YELLOW_PRIO_1, "tx_yellow_prio_1"), \ - OCELOT_STAT_ETHTOOL(TX_YELLOW_PRIO_2, "tx_yellow_prio_2"), \ - OCELOT_STAT_ETHTOOL(TX_YELLOW_PRIO_3, "tx_yellow_prio_3"), \ - OCELOT_STAT_ETHTOOL(TX_YELLOW_PRIO_4, "tx_yellow_prio_4"), \ - OCELOT_STAT_ETHTOOL(TX_YELLOW_PRIO_5, "tx_yellow_prio_5"), \ - OCELOT_STAT_ETHTOOL(TX_YELLOW_PRIO_6, "tx_yellow_prio_6"), \ - OCELOT_STAT_ETHTOOL(TX_YELLOW_PRIO_7, "tx_yellow_prio_7"), \ - OCELOT_STAT_ETHTOOL(TX_GREEN_PRIO_0, "tx_green_prio_0"), \ - OCELOT_STAT_ETHTOOL(TX_GREEN_PRIO_1, "tx_green_prio_1"), \ - OCELOT_STAT_ETHTOOL(TX_GREEN_PRIO_2, "tx_green_prio_2"), \ - OCELOT_STAT_ETHTOOL(TX_GREEN_PRIO_3, "tx_green_prio_3"), \ - OCELOT_STAT_ETHTOOL(TX_GREEN_PRIO_4, "tx_green_prio_4"), \ - OCELOT_STAT_ETHTOOL(TX_GREEN_PRIO_5, "tx_green_prio_5"), \ - OCELOT_STAT_ETHTOOL(TX_GREEN_PRIO_6, "tx_green_prio_6"), \ - OCELOT_STAT_ETHTOOL(TX_GREEN_PRIO_7, "tx_green_prio_7"), \ - OCELOT_STAT_ETHTOOL(TX_AGED, "tx_aged"), \ - OCELOT_STAT_ETHTOOL(DROP_LOCAL, "drop_local"), \ - OCELOT_STAT_ETHTOOL(DROP_TAIL, "drop_tail"), \ - OCELOT_STAT_ETHTOOL(DROP_YELLOW_PRIO_0, "drop_yellow_prio_0"), \ - OCELOT_STAT_ETHTOOL(DROP_YELLOW_PRIO_1, "drop_yellow_prio_1"), \ - OCELOT_STAT_ETHTOOL(DROP_YELLOW_PRIO_2, "drop_yellow_prio_2"), \ - OCELOT_STAT_ETHTOOL(DROP_YELLOW_PRIO_3, "drop_yellow_prio_3"), \ - OCELOT_STAT_ETHTOOL(DROP_YELLOW_PRIO_4, "drop_yellow_prio_4"), \ - OCELOT_STAT_ETHTOOL(DROP_YELLOW_PRIO_5, "drop_yellow_prio_5"), \ - OCELOT_STAT_ETHTOOL(DROP_YELLOW_PRIO_6, "drop_yellow_prio_6"), \ - OCELOT_STAT_ETHTOOL(DROP_YELLOW_PRIO_7, "drop_yellow_prio_7"), \ - OCELOT_STAT_ETHTOOL(DROP_GREEN_PRIO_0, "drop_green_prio_0"), \ - OCELOT_STAT_ETHTOOL(DROP_GREEN_PRIO_1, "drop_green_prio_1"), \ - OCELOT_STAT_ETHTOOL(DROP_GREEN_PRIO_2, "drop_green_prio_2"), \ - OCELOT_STAT_ETHTOOL(DROP_GREEN_PRIO_3, "drop_green_prio_3"), \ - OCELOT_STAT_ETHTOOL(DROP_GREEN_PRIO_4, "drop_green_prio_4"), \ - OCELOT_STAT_ETHTOOL(DROP_GREEN_PRIO_5, "drop_green_prio_5"), \ - OCELOT_STAT_ETHTOOL(DROP_GREEN_PRIO_6, "drop_green_prio_6"), \ - OCELOT_STAT_ETHTOOL(DROP_GREEN_PRIO_7, "drop_green_prio_7") - -struct ocelot_stats_region { - struct list_head node; - u32 base; - int count; - u32 *buf; -}; - enum ocelot_tag_prefix { OCELOT_TAG_PREFIX_DISABLED = 0, OCELOT_TAG_PREFIX_NONE, From 877e7b7c3b12d78f0488b767ed10ec21166ee422 Mon Sep 17 00:00:00 2001 From: Colin Foster Date: Sat, 19 Nov 2022 15:14:06 -0800 Subject: [PATCH 1148/2002] net: mscc: ocelot: issue a warning if stats are incorrectly ordered Ocelot uses regmap_bulk_read() operations to efficiently read stats registers. Currently the implementation relies on the stats layout to be ordered to be most efficient. Issue a warning if any future implementations happen to break this pattern. Signed-off-by: Colin Foster Co-developed-by: Vladimir Oltean Reviewed-by: Vladimir Oltean Signed-off-by: Paolo Abeni --- drivers/net/ethernet/mscc/ocelot_stats.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/mscc/ocelot_stats.c b/drivers/net/ethernet/mscc/ocelot_stats.c index 68e9f450c468..1478c3b21af1 100644 --- a/drivers/net/ethernet/mscc/ocelot_stats.c +++ b/drivers/net/ethernet/mscc/ocelot_stats.c @@ -602,7 +602,7 @@ EXPORT_SYMBOL(ocelot_port_get_stats64); static int ocelot_prepare_stats_regions(struct ocelot *ocelot) { struct ocelot_stats_region *region = NULL; - unsigned int last; + unsigned int last = 0; int i; INIT_LIST_HEAD(&ocelot->stats_regions); @@ -619,6 +619,12 @@ static int ocelot_prepare_stats_regions(struct ocelot *ocelot) if (!region) return -ENOMEM; + /* enum ocelot_stat must be kept sorted in the same + * order as ocelot_stats_layout[i].reg in order to have + * efficient bulking + */ + WARN_ON(last >= ocelot_stats_layout[i].reg); + region->base = ocelot_stats_layout[i].reg; region->count = 1; list_add_tail(®ion->node, &ocelot->stats_regions); From 98cbc40e4f7d15bc21a314a151071566e14ca39c Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Tue, 15 Nov 2022 16:26:07 +0300 Subject: [PATCH 1149/2002] netfilter: nft_inner: fix IS_ERR() vs NULL check The __nft_expr_type_get() function returns NULL on error. It never returns error pointers. Fixes: 3a07327d10a0 ("netfilter: nft_inner: support for inner tunnel header matching") Signed-off-by: Dan Carpenter Signed-off-by: Pablo Neira Ayuso --- net/netfilter/nf_tables_api.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c index 2fa52b8d5ce1..833850a4780f 100644 --- a/net/netfilter/nf_tables_api.c +++ b/net/netfilter/nf_tables_api.c @@ -2873,8 +2873,8 @@ int nft_expr_inner_parse(const struct nft_ctx *ctx, const struct nlattr *nla, return -EINVAL; type = __nft_expr_type_get(ctx->family, tb[NFTA_EXPR_NAME]); - if (IS_ERR(type)) - return PTR_ERR(type); + if (!type) + return -ENOENT; if (!type->inner_ops) return -EOPNOTSUPP; From 0b2971a2703c015b5737d66688c2c7c81a5e391b Mon Sep 17 00:00:00 2001 From: Alexei Starovoitov Date: Tue, 22 Nov 2022 08:50:04 -0800 Subject: [PATCH 1150/2002] Revert "selftests/bpf: Temporarily disable linked list tests" This reverts commit 0a2f85a1be4328d29aefa54684d10c23a3298fef. Signed-off-by: Alexei Starovoitov --- .../selftests/bpf/prog_tests/linked_list.c | 21 +++++++------------ .../testing/selftests/bpf/progs/linked_list.c | 11 +--------- .../testing/selftests/bpf/progs/linked_list.h | 2 -- .../selftests/bpf/progs/linked_list_fail.c | 16 +++++++------- 4 files changed, 16 insertions(+), 34 deletions(-) diff --git a/tools/testing/selftests/bpf/prog_tests/linked_list.c b/tools/testing/selftests/bpf/prog_tests/linked_list.c index 6170d36fe5fc..dd73d0a62c6e 100644 --- a/tools/testing/selftests/bpf/prog_tests/linked_list.c +++ b/tools/testing/selftests/bpf/prog_tests/linked_list.c @@ -24,9 +24,7 @@ static struct { { #test "_missing_lock_pop_back", \ "bpf_spin_lock at off=" #off " must be held for bpf_list_head" }, TEST(kptr, 32) -/* FIXME TEST(global, 16) -*/ TEST(map, 0) TEST(inner_map, 0) #undef TEST @@ -34,6 +32,9 @@ static struct { { #test "_kptr_incorrect_lock_" #op, \ "held lock and object are not in the same allocation\n" \ "bpf_spin_lock at off=32 must be held for bpf_list_head" }, \ + { #test "_global_incorrect_lock_" #op, \ + "held lock and object are not in the same allocation\n" \ + "bpf_spin_lock at off=16 must be held for bpf_list_head" }, \ { #test "_map_incorrect_lock_" #op, \ "held lock and object are not in the same allocation\n" \ "bpf_spin_lock at off=0 must be held for bpf_list_head" }, \ @@ -44,6 +45,10 @@ static struct { TEST(kptr, push_back) TEST(kptr, pop_front) TEST(kptr, pop_back) + TEST(global, push_front) + TEST(global, push_back) + TEST(global, pop_front) + TEST(global, pop_back) TEST(map, push_front) TEST(map, push_back) TEST(map, pop_front) @@ -53,14 +58,12 @@ static struct { TEST(inner_map, pop_front) TEST(inner_map, pop_back) #undef TEST -/* FIXME { "map_compat_kprobe", "tracing progs cannot use bpf_list_head yet" }, { "map_compat_kretprobe", "tracing progs cannot use bpf_list_head yet" }, { "map_compat_tp", "tracing progs cannot use bpf_list_head yet" }, { "map_compat_perf", "tracing progs cannot use bpf_list_head yet" }, { "map_compat_raw_tp", "tracing progs cannot use bpf_list_head yet" }, { "map_compat_raw_tp_w", "tracing progs cannot use bpf_list_head yet" }, -*/ { "obj_type_id_oor", "local type ID argument must be in range [0, U32_MAX]" }, { "obj_new_no_composite", "bpf_obj_new type ID argument must be of a struct" }, { "obj_new_no_struct", "bpf_obj_new type ID argument must be of a struct" }, @@ -75,7 +78,6 @@ static struct { { "direct_write_head", "direct access to bpf_list_head is disallowed" }, { "direct_read_node", "direct access to bpf_list_node is disallowed" }, { "direct_write_node", "direct access to bpf_list_node is disallowed" }, -/* FIXME { "write_after_push_front", "only read is supported" }, { "write_after_push_back", "only read is supported" }, { "use_after_unlock_push_front", "invalid mem access 'scalar'" }, @@ -92,11 +94,8 @@ static struct { { "no_head_type", "bpf_list_head not found at offset=0" }, { "incorrect_head_var_off1", "R1 doesn't have constant offset" }, { "incorrect_head_var_off2", "variable ptr_ access var_off=(0x0; 0xffffffff) disallowed" }, -*/ { "incorrect_head_off1", "bpf_list_head not found at offset=17" }, -/* FIXME { "incorrect_head_off2", "bpf_list_head not found at offset=1" }, -*/ { "pop_front_off", "15: (bf) r1 = r6 ; R1_w=ptr_or_null_foo(id=4,ref_obj_id=4,off=40,imm=0) " "R6_w=ptr_or_null_foo(id=4,ref_obj_id=4,off=40,imm=0) refs=2,4\n" @@ -189,10 +188,8 @@ static void test_linked_list_success(int mode, bool leave_in_map) ret = bpf_prog_test_run_opts(bpf_program__fd(skel->progs.global_list_push_pop), &opts); ASSERT_OK(ret, "global_list_push_pop"); ASSERT_OK(opts.retval, "global_list_push_pop retval"); - /* FIXME: if (!leave_in_map) clear_fields(skel->maps.data_A); - */ if (mode == PUSH_POP) goto end; @@ -213,10 +210,8 @@ ppm: ret = bpf_prog_test_run_opts(bpf_program__fd(skel->progs.global_list_push_pop_multiple), &opts); ASSERT_OK(ret, "global_list_push_pop_multiple"); ASSERT_OK(opts.retval, "global_list_push_pop_multiple retval"); - /* FIXME: if (!leave_in_map) clear_fields(skel->maps.data_A); - */ if (mode == PUSH_POP_MULT) goto end; @@ -237,10 +232,8 @@ lil: ret = bpf_prog_test_run_opts(bpf_program__fd(skel->progs.global_list_in_list), &opts); ASSERT_OK(ret, "global_list_in_list"); ASSERT_OK(opts.retval, "global_list_in_list retval"); - /* FIXME: if (!leave_in_map) clear_fields(skel->maps.data_A); - */ end: linked_list__destroy(skel); } diff --git a/tools/testing/selftests/bpf/progs/linked_list.c b/tools/testing/selftests/bpf/progs/linked_list.c index a99103c86e48..2c7b615c6d41 100644 --- a/tools/testing/selftests/bpf/progs/linked_list.c +++ b/tools/testing/selftests/bpf/progs/linked_list.c @@ -291,10 +291,7 @@ int inner_map_list_push_pop(void *ctx) SEC("tc") int global_list_push_pop(void *ctx) { - /* FIXME: - * return test_list_push_pop(&glock, &ghead); - */ - return 0; + return test_list_push_pop(&glock, &ghead); } SEC("tc") @@ -330,13 +327,10 @@ int global_list_push_pop_multiple(void *ctx) { int ret; - /* FIXME: ret = list_push_pop_multiple(&glock, &ghead, false); if (ret) return ret; return list_push_pop_multiple(&glock, &ghead, true); - */ - return 0; } SEC("tc") @@ -370,10 +364,7 @@ int inner_map_list_in_list(void *ctx) SEC("tc") int global_list_in_list(void *ctx) { - /* FIXME return test_list_in_list(&glock, &ghead); - */ - return 0; } char _license[] SEC("license") = "GPL"; diff --git a/tools/testing/selftests/bpf/progs/linked_list.h b/tools/testing/selftests/bpf/progs/linked_list.h index 93157efc2d04..8db80ed64db1 100644 --- a/tools/testing/selftests/bpf/progs/linked_list.h +++ b/tools/testing/selftests/bpf/progs/linked_list.h @@ -47,12 +47,10 @@ struct { }, }; -/* FIXME #define private(name) SEC(".data." #name) __hidden __attribute__((aligned(8))) private(A) struct bpf_spin_lock glock; private(A) struct bpf_list_head ghead __contains(foo, node); private(B) struct bpf_spin_lock glock2; -*/ #endif diff --git a/tools/testing/selftests/bpf/progs/linked_list_fail.c b/tools/testing/selftests/bpf/progs/linked_list_fail.c index 1b7ed1d3a9bb..1d9017240e19 100644 --- a/tools/testing/selftests/bpf/progs/linked_list_fail.c +++ b/tools/testing/selftests/bpf/progs/linked_list_fail.c @@ -59,12 +59,10 @@ CHECK(kptr, push_back, &f->head); CHECK(kptr, pop_front, &f->head); CHECK(kptr, pop_back, &f->head); -/* FIXME CHECK(global, push_front, &ghead); CHECK(global, push_back, &ghead); CHECK(global, pop_front, &ghead); CHECK(global, pop_back, &ghead); -*/ CHECK(map, push_front, &v->head); CHECK(map, push_back, &v->head); @@ -91,15 +89,23 @@ CHECK(inner_map, pop_back, &iv->head); #define CHECK_OP(op) \ CHECK(kptr_kptr, op, &f1->lock, &f2->head); \ + CHECK(kptr_global, op, &f1->lock, &ghead); \ CHECK(kptr_map, op, &f1->lock, &v->head); \ CHECK(kptr_inner_map, op, &f1->lock, &iv->head); \ \ + CHECK(global_global, op, &glock2, &ghead); \ + CHECK(global_kptr, op, &glock, &f1->head); \ + CHECK(global_map, op, &glock, &v->head); \ + CHECK(global_inner_map, op, &glock, &iv->head); \ + \ CHECK(map_map, op, &v->lock, &v2->head); \ CHECK(map_kptr, op, &v->lock, &f2->head); \ + CHECK(map_global, op, &v->lock, &ghead); \ CHECK(map_inner_map, op, &v->lock, &iv->head); \ \ CHECK(inner_map_inner_map, op, &iv->lock, &iv2->head); \ CHECK(inner_map_kptr, op, &iv->lock, &f2->head); \ + CHECK(inner_map_global, op, &iv->lock, &ghead); \ CHECK(inner_map_map, op, &iv->lock, &v->head); CHECK_OP(push_front); @@ -111,7 +117,6 @@ CHECK_OP(pop_back); #undef CHECK_OP #undef INIT -/* FIXME SEC("?kprobe/xyz") int map_compat_kprobe(void *ctx) { @@ -153,7 +158,6 @@ int map_compat_raw_tp_w(void *ctx) bpf_list_push_front(&ghead, NULL); return 0; } -*/ SEC("?tc") int obj_type_id_oor(void *ctx) @@ -299,7 +303,6 @@ int direct_write_node(void *ctx) return 0; } -/* FIXME static __always_inline int write_after_op(void (*push_op)(void *head, void *node)) { @@ -503,7 +506,6 @@ int incorrect_head_var_off2(struct __sk_buff *ctx) return 0; } -*/ SEC("?tc") int incorrect_head_off1(void *ctx) @@ -527,7 +529,6 @@ int incorrect_head_off1(void *ctx) return 0; } -/* FIXME SEC("?tc") int incorrect_head_off2(void *ctx) { @@ -544,7 +545,6 @@ int incorrect_head_off2(void *ctx) return 0; } -*/ static __always_inline int pop_ptr_off(void *(*op)(void *head)) From dc79f035b2062e4ff4f6432eda18f461f82b1333 Mon Sep 17 00:00:00 2001 From: Alexei Starovoitov Date: Tue, 22 Nov 2022 09:15:29 -0800 Subject: [PATCH 1151/2002] selftests/bpf: Workaround for llvm nop-4 bug Currently LLVM fails to recognize .data.* as data section and defaults to .text section. Later BPF backend tries to emit 4-byte NOP instruction which doesn't exist in BPF ISA and aborts. The fix for LLVM is pending: https://reviews.llvm.org/D138477 While waiting for the fix lets workaround the linked_list test case by using .bss.* prefix which is properly recognized by LLVM as BSS section. Fix libbpf to support .bss. prefix and adjust tests. Signed-off-by: Alexei Starovoitov --- tools/lib/bpf/libbpf.c | 3 ++- tools/testing/selftests/bpf/prog_tests/linked_list.c | 6 +++--- tools/testing/selftests/bpf/progs/linked_list.h | 2 +- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c index b5df6aca06ea..93ccea238391 100644 --- a/tools/lib/bpf/libbpf.c +++ b/tools/lib/bpf/libbpf.c @@ -3511,7 +3511,8 @@ static int bpf_object__elf_collect(struct bpf_object *obj) sec_desc->sec_type = SEC_RELO; sec_desc->shdr = sh; sec_desc->data = data; - } else if (sh->sh_type == SHT_NOBITS && strcmp(name, BSS_SEC) == 0) { + } else if (sh->sh_type == SHT_NOBITS && (strcmp(name, BSS_SEC) == 0 || + str_has_pfx(name, BSS_SEC "."))) { sec_desc->sec_type = SEC_BSS; sec_desc->shdr = sh; sec_desc->data = data; diff --git a/tools/testing/selftests/bpf/prog_tests/linked_list.c b/tools/testing/selftests/bpf/prog_tests/linked_list.c index dd73d0a62c6e..9a7d4c47af63 100644 --- a/tools/testing/selftests/bpf/prog_tests/linked_list.c +++ b/tools/testing/selftests/bpf/prog_tests/linked_list.c @@ -189,7 +189,7 @@ static void test_linked_list_success(int mode, bool leave_in_map) ASSERT_OK(ret, "global_list_push_pop"); ASSERT_OK(opts.retval, "global_list_push_pop retval"); if (!leave_in_map) - clear_fields(skel->maps.data_A); + clear_fields(skel->maps.bss_A); if (mode == PUSH_POP) goto end; @@ -211,7 +211,7 @@ ppm: ASSERT_OK(ret, "global_list_push_pop_multiple"); ASSERT_OK(opts.retval, "global_list_push_pop_multiple retval"); if (!leave_in_map) - clear_fields(skel->maps.data_A); + clear_fields(skel->maps.bss_A); if (mode == PUSH_POP_MULT) goto end; @@ -233,7 +233,7 @@ lil: ASSERT_OK(ret, "global_list_in_list"); ASSERT_OK(opts.retval, "global_list_in_list retval"); if (!leave_in_map) - clear_fields(skel->maps.data_A); + clear_fields(skel->maps.bss_A); end: linked_list__destroy(skel); } diff --git a/tools/testing/selftests/bpf/progs/linked_list.h b/tools/testing/selftests/bpf/progs/linked_list.h index 8db80ed64db1..3fb2412552fc 100644 --- a/tools/testing/selftests/bpf/progs/linked_list.h +++ b/tools/testing/selftests/bpf/progs/linked_list.h @@ -47,7 +47,7 @@ struct { }, }; -#define private(name) SEC(".data." #name) __hidden __attribute__((aligned(8))) +#define private(name) SEC(".bss." #name) __hidden __attribute__((aligned(8))) private(A) struct bpf_spin_lock glock; private(A) struct bpf_list_head ghead __contains(foo, node); From fda01efc61605af7c6fa03c4109f14d59c9228b7 Mon Sep 17 00:00:00 2001 From: David Vernet Date: Mon, 21 Nov 2022 23:54:55 -0600 Subject: [PATCH 1152/2002] bpf: Enable cgroups to be used as kptrs Now that tasks can be used as kfuncs, and the PTR_TRUSTED flag is available for us to easily add basic acquire / get / release kfuncs, we can do the same for cgroups. This patch set adds the following kfuncs which enable using cgroups as kptrs: struct cgroup *bpf_cgroup_acquire(struct cgroup *cgrp); struct cgroup *bpf_cgroup_kptr_get(struct cgroup **cgrpp); void bpf_cgroup_release(struct cgroup *cgrp); A follow-on patch will add a selftest suite which validates these kfuncs. Signed-off-by: David Vernet Link: https://lore.kernel.org/r/20221122055458.173143-2-void@manifault.com Signed-off-by: Alexei Starovoitov --- kernel/bpf/helpers.c | 83 ++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 80 insertions(+), 3 deletions(-) diff --git a/kernel/bpf/helpers.c b/kernel/bpf/helpers.c index 9e8a5557ea8d..420a4251cccc 100644 --- a/kernel/bpf/helpers.c +++ b/kernel/bpf/helpers.c @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include @@ -1879,6 +1880,66 @@ void bpf_task_release(struct task_struct *p) put_task_struct_rcu_user(p); } +#ifdef CONFIG_CGROUPS +/** + * bpf_cgroup_acquire - Acquire a reference to a cgroup. A cgroup acquired by + * this kfunc which is not stored in a map as a kptr, must be released by + * calling bpf_cgroup_release(). + * @cgrp: The cgroup on which a reference is being acquired. + */ +struct cgroup *bpf_cgroup_acquire(struct cgroup *cgrp) +{ + cgroup_get(cgrp); + return cgrp; +} + +/** + * bpf_cgroup_kptr_get - Acquire a reference on a struct cgroup kptr. A cgroup + * kptr acquired by this kfunc which is not subsequently stored in a map, must + * be released by calling bpf_cgroup_release(). + * @cgrpp: A pointer to a cgroup kptr on which a reference is being acquired. + */ +struct cgroup *bpf_cgroup_kptr_get(struct cgroup **cgrpp) +{ + struct cgroup *cgrp; + + rcu_read_lock(); + /* Another context could remove the cgroup from the map and release it + * at any time, including after we've done the lookup above. This is + * safe because we're in an RCU read region, so the cgroup is + * guaranteed to remain valid until at least the rcu_read_unlock() + * below. + */ + cgrp = READ_ONCE(*cgrpp); + + if (cgrp && !cgroup_tryget(cgrp)) + /* If the cgroup had been removed from the map and freed as + * described above, cgroup_tryget() will return false. The + * cgroup will be freed at some point after the current RCU gp + * has ended, so just return NULL to the user. + */ + cgrp = NULL; + rcu_read_unlock(); + + return cgrp; +} + +/** + * bpf_cgroup_release - Release the reference acquired on a struct cgroup *. + * If this kfunc is invoked in an RCU read region, the cgroup is guaranteed to + * not be freed until the current grace period has ended, even if its refcount + * drops to 0. + * @cgrp: The cgroup on which a reference is being released. + */ +void bpf_cgroup_release(struct cgroup *cgrp) +{ + if (!cgrp) + return; + + cgroup_put(cgrp); +} +#endif /* CONFIG_CGROUPS */ + void *bpf_cast_to_kern_ctx(void *obj) { return obj; @@ -1904,6 +1965,12 @@ BTF_ID_FLAGS(func, bpf_list_pop_back, KF_ACQUIRE | KF_RET_NULL) BTF_ID_FLAGS(func, bpf_task_acquire, KF_ACQUIRE | KF_TRUSTED_ARGS) BTF_ID_FLAGS(func, bpf_task_kptr_get, KF_ACQUIRE | KF_KPTR_GET | KF_RET_NULL) BTF_ID_FLAGS(func, bpf_task_release, KF_RELEASE) + +#ifdef CONFIG_CGROUPS +BTF_ID_FLAGS(func, bpf_cgroup_acquire, KF_ACQUIRE | KF_TRUSTED_ARGS) +BTF_ID_FLAGS(func, bpf_cgroup_kptr_get, KF_ACQUIRE | KF_KPTR_GET | KF_RET_NULL) +BTF_ID_FLAGS(func, bpf_cgroup_release, KF_RELEASE) +#endif BTF_SET8_END(generic_btf_ids) static const struct btf_kfunc_id_set generic_kfunc_set = { @@ -1915,6 +1982,10 @@ static const struct btf_kfunc_id_set generic_kfunc_set = { BTF_ID_LIST(generic_dtor_ids) BTF_ID(struct, task_struct) BTF_ID(func, bpf_task_release) +#ifdef CONFIG_CGROUPS +BTF_ID(struct, cgroup) +BTF_ID(func, bpf_cgroup_release) +#endif BTF_SET8_START(common_btf_ids) BTF_ID_FLAGS(func, bpf_cast_to_kern_ctx) @@ -1928,12 +1999,18 @@ static const struct btf_kfunc_id_set common_kfunc_set = { static int __init kfunc_init(void) { - int ret; + int ret, idx = 0; const struct btf_id_dtor_kfunc generic_dtors[] = { { - .btf_id = generic_dtor_ids[0], - .kfunc_btf_id = generic_dtor_ids[1] + .btf_id = generic_dtor_ids[idx++], + .kfunc_btf_id = generic_dtor_ids[idx++] }, +#ifdef CONFIG_CGROUPS + { + .btf_id = generic_dtor_ids[idx++], + .kfunc_btf_id = generic_dtor_ids[idx++] + }, +#endif }; ret = register_btf_kfunc_id_set(BPF_PROG_TYPE_TRACING, &generic_kfunc_set); From f583ddf15e57746e60f3b68d529afc9faa2e2cb3 Mon Sep 17 00:00:00 2001 From: David Vernet Date: Mon, 21 Nov 2022 23:54:56 -0600 Subject: [PATCH 1153/2002] selftests/bpf: Add cgroup kfunc / kptr selftests This patch adds a selftest suite to validate the cgroup kfuncs that were added in the prior patch. Signed-off-by: David Vernet Link: https://lore.kernel.org/r/20221122055458.173143-3-void@manifault.com Signed-off-by: Alexei Starovoitov --- tools/testing/selftests/bpf/DENYLIST.s390x | 1 + .../selftests/bpf/prog_tests/cgrp_kfunc.c | 174 ++++++++++++ .../selftests/bpf/progs/cgrp_kfunc_common.h | 71 +++++ .../selftests/bpf/progs/cgrp_kfunc_failure.c | 260 ++++++++++++++++++ .../selftests/bpf/progs/cgrp_kfunc_success.c | 125 +++++++++ 5 files changed, 631 insertions(+) create mode 100644 tools/testing/selftests/bpf/prog_tests/cgrp_kfunc.c create mode 100644 tools/testing/selftests/bpf/progs/cgrp_kfunc_common.h create mode 100644 tools/testing/selftests/bpf/progs/cgrp_kfunc_failure.c create mode 100644 tools/testing/selftests/bpf/progs/cgrp_kfunc_success.c diff --git a/tools/testing/selftests/bpf/DENYLIST.s390x b/tools/testing/selftests/bpf/DENYLIST.s390x index 12cf2159975e..b9a3d80204c6 100644 --- a/tools/testing/selftests/bpf/DENYLIST.s390x +++ b/tools/testing/selftests/bpf/DENYLIST.s390x @@ -10,6 +10,7 @@ bpf_nf # JIT does not support calling kernel f bpf_tcp_ca # JIT does not support calling kernel function (kfunc) cb_refs # expected error message unexpected error: -524 (trampoline) cgroup_hierarchical_stats # JIT does not support calling kernel function (kfunc) +cgrp_kfunc # JIT does not support calling kernel function cgrp_local_storage # prog_attach unexpected error: -524 (trampoline) core_read_macros # unknown func bpf_probe_read#4 (overlapping) d_path # failed to auto-attach program 'prog_stat': -524 (trampoline) diff --git a/tools/testing/selftests/bpf/prog_tests/cgrp_kfunc.c b/tools/testing/selftests/bpf/prog_tests/cgrp_kfunc.c new file mode 100644 index 000000000000..a59b166bbcc4 --- /dev/null +++ b/tools/testing/selftests/bpf/prog_tests/cgrp_kfunc.c @@ -0,0 +1,174 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2022 Meta Platforms, Inc. and affiliates. */ + +#define _GNU_SOURCE +#include +#include + +#include "cgrp_kfunc_failure.skel.h" +#include "cgrp_kfunc_success.skel.h" + +static size_t log_buf_sz = 1 << 20; /* 1 MB */ +static char obj_log_buf[1048576]; + +static struct cgrp_kfunc_success *open_load_cgrp_kfunc_skel(void) +{ + struct cgrp_kfunc_success *skel; + int err; + + skel = cgrp_kfunc_success__open(); + if (!ASSERT_OK_PTR(skel, "skel_open")) + return NULL; + + skel->bss->pid = getpid(); + + err = cgrp_kfunc_success__load(skel); + if (!ASSERT_OK(err, "skel_load")) + goto cleanup; + + return skel; + +cleanup: + cgrp_kfunc_success__destroy(skel); + return NULL; +} + +static int mkdir_rm_test_dir(void) +{ + int fd; + const char *cgrp_path = "cgrp_kfunc"; + + fd = create_and_get_cgroup(cgrp_path); + if (!ASSERT_GT(fd, 0, "mkdir_cgrp_fd")) + return -1; + + close(fd); + remove_cgroup(cgrp_path); + + return 0; +} + +static void run_success_test(const char *prog_name) +{ + struct cgrp_kfunc_success *skel; + struct bpf_program *prog; + struct bpf_link *link = NULL; + + skel = open_load_cgrp_kfunc_skel(); + if (!ASSERT_OK_PTR(skel, "open_load_skel")) + return; + + if (!ASSERT_OK(skel->bss->err, "pre_mkdir_err")) + goto cleanup; + + prog = bpf_object__find_program_by_name(skel->obj, prog_name); + if (!ASSERT_OK_PTR(prog, "bpf_object__find_program_by_name")) + goto cleanup; + + link = bpf_program__attach(prog); + if (!ASSERT_OK_PTR(link, "attached_link")) + goto cleanup; + + ASSERT_EQ(skel->bss->invocations, 0, "pre_rmdir_count"); + if (!ASSERT_OK(mkdir_rm_test_dir(), "cgrp_mkdir")) + goto cleanup; + + ASSERT_EQ(skel->bss->invocations, 1, "post_rmdir_count"); + ASSERT_OK(skel->bss->err, "post_rmdir_err"); + +cleanup: + bpf_link__destroy(link); + cgrp_kfunc_success__destroy(skel); +} + +static const char * const success_tests[] = { + "test_cgrp_acquire_release_argument", + "test_cgrp_acquire_leave_in_map", + "test_cgrp_xchg_release", + "test_cgrp_get_release", +}; + +static struct { + const char *prog_name; + const char *expected_err_msg; +} failure_tests[] = { + {"cgrp_kfunc_acquire_untrusted", "R1 must be referenced or trusted"}, + {"cgrp_kfunc_acquire_fp", "arg#0 pointer type STRUCT cgroup must point"}, + {"cgrp_kfunc_acquire_unsafe_kretprobe", "reg type unsupported for arg#0 function"}, + {"cgrp_kfunc_acquire_trusted_walked", "R1 must be referenced or trusted"}, + {"cgrp_kfunc_acquire_null", "arg#0 pointer type STRUCT cgroup must point"}, + {"cgrp_kfunc_acquire_unreleased", "Unreleased reference"}, + {"cgrp_kfunc_get_non_kptr_param", "arg#0 expected pointer to map value"}, + {"cgrp_kfunc_get_non_kptr_acquired", "arg#0 expected pointer to map value"}, + {"cgrp_kfunc_get_null", "arg#0 expected pointer to map value"}, + {"cgrp_kfunc_xchg_unreleased", "Unreleased reference"}, + {"cgrp_kfunc_get_unreleased", "Unreleased reference"}, + {"cgrp_kfunc_release_untrusted", "arg#0 is untrusted_ptr_or_null_ expected ptr_ or socket"}, + {"cgrp_kfunc_release_fp", "arg#0 pointer type STRUCT cgroup must point"}, + {"cgrp_kfunc_release_null", "arg#0 is ptr_or_null_ expected ptr_ or socket"}, + {"cgrp_kfunc_release_unacquired", "release kernel function bpf_cgroup_release expects"}, +}; + +static void verify_fail(const char *prog_name, const char *expected_err_msg) +{ + LIBBPF_OPTS(bpf_object_open_opts, opts); + struct cgrp_kfunc_failure *skel; + int err, i; + + opts.kernel_log_buf = obj_log_buf; + opts.kernel_log_size = log_buf_sz; + opts.kernel_log_level = 1; + + skel = cgrp_kfunc_failure__open_opts(&opts); + if (!ASSERT_OK_PTR(skel, "cgrp_kfunc_failure__open_opts")) + goto cleanup; + + for (i = 0; i < ARRAY_SIZE(failure_tests); i++) { + struct bpf_program *prog; + const char *curr_name = failure_tests[i].prog_name; + + prog = bpf_object__find_program_by_name(skel->obj, curr_name); + if (!ASSERT_OK_PTR(prog, "bpf_object__find_program_by_name")) + goto cleanup; + + bpf_program__set_autoload(prog, !strcmp(curr_name, prog_name)); + } + + err = cgrp_kfunc_failure__load(skel); + if (!ASSERT_ERR(err, "unexpected load success")) + goto cleanup; + + if (!ASSERT_OK_PTR(strstr(obj_log_buf, expected_err_msg), "expected_err_msg")) { + fprintf(stderr, "Expected err_msg: %s\n", expected_err_msg); + fprintf(stderr, "Verifier output: %s\n", obj_log_buf); + } + +cleanup: + cgrp_kfunc_failure__destroy(skel); +} + +void test_cgrp_kfunc(void) +{ + int i, err; + + err = setup_cgroup_environment(); + if (!ASSERT_OK(err, "cgrp_env_setup")) + goto cleanup; + + for (i = 0; i < ARRAY_SIZE(success_tests); i++) { + if (!test__start_subtest(success_tests[i])) + continue; + + run_success_test(success_tests[i]); + } + + for (i = 0; i < ARRAY_SIZE(failure_tests); i++) { + if (!test__start_subtest(failure_tests[i].prog_name)) + continue; + + verify_fail(failure_tests[i].prog_name, failure_tests[i].expected_err_msg); + } + +cleanup: + cleanup_cgroup_environment(); +} diff --git a/tools/testing/selftests/bpf/progs/cgrp_kfunc_common.h b/tools/testing/selftests/bpf/progs/cgrp_kfunc_common.h new file mode 100644 index 000000000000..3f18def0e45c --- /dev/null +++ b/tools/testing/selftests/bpf/progs/cgrp_kfunc_common.h @@ -0,0 +1,71 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (c) 2022 Meta Platforms, Inc. and affiliates. */ + +#ifndef _CGRP_KFUNC_COMMON_H +#define _CGRP_KFUNC_COMMON_H + +#include +#include +#include +#include + +struct __cgrps_kfunc_map_value { + struct cgroup __kptr_ref * cgrp; +}; + +struct hash_map { + __uint(type, BPF_MAP_TYPE_HASH); + __type(key, int); + __type(value, struct __cgrps_kfunc_map_value); + __uint(max_entries, 1); +} __cgrps_kfunc_map SEC(".maps"); + +struct cgroup *bpf_cgroup_acquire(struct cgroup *p) __ksym; +struct cgroup *bpf_cgroup_kptr_get(struct cgroup **pp) __ksym; +void bpf_cgroup_release(struct cgroup *p) __ksym; + +static inline struct __cgrps_kfunc_map_value *cgrps_kfunc_map_value_lookup(struct cgroup *cgrp) +{ + s32 id; + long status; + + status = bpf_probe_read_kernel(&id, sizeof(id), &cgrp->self.id); + if (status) + return NULL; + + return bpf_map_lookup_elem(&__cgrps_kfunc_map, &id); +} + +static inline int cgrps_kfunc_map_insert(struct cgroup *cgrp) +{ + struct __cgrps_kfunc_map_value local, *v; + long status; + struct cgroup *acquired, *old; + s32 id; + + status = bpf_probe_read_kernel(&id, sizeof(id), &cgrp->self.id); + if (status) + return status; + + local.cgrp = NULL; + status = bpf_map_update_elem(&__cgrps_kfunc_map, &id, &local, BPF_NOEXIST); + if (status) + return status; + + v = bpf_map_lookup_elem(&__cgrps_kfunc_map, &id); + if (!v) { + bpf_map_delete_elem(&__cgrps_kfunc_map, &id); + return -ENOENT; + } + + acquired = bpf_cgroup_acquire(cgrp); + old = bpf_kptr_xchg(&v->cgrp, acquired); + if (old) { + bpf_cgroup_release(old); + return -EEXIST; + } + + return 0; +} + +#endif /* _CGRP_KFUNC_COMMON_H */ diff --git a/tools/testing/selftests/bpf/progs/cgrp_kfunc_failure.c b/tools/testing/selftests/bpf/progs/cgrp_kfunc_failure.c new file mode 100644 index 000000000000..a1369b5ebcf8 --- /dev/null +++ b/tools/testing/selftests/bpf/progs/cgrp_kfunc_failure.c @@ -0,0 +1,260 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2022 Meta Platforms, Inc. and affiliates. */ + +#include +#include +#include + +#include "cgrp_kfunc_common.h" + +char _license[] SEC("license") = "GPL"; + +/* Prototype for all of the program trace events below: + * + * TRACE_EVENT(cgroup_mkdir, + * TP_PROTO(struct cgroup *cgrp, const char *path), + * TP_ARGS(cgrp, path) + */ + +static struct __cgrps_kfunc_map_value *insert_lookup_cgrp(struct cgroup *cgrp) +{ + int status; + + status = cgrps_kfunc_map_insert(cgrp); + if (status) + return NULL; + + return cgrps_kfunc_map_value_lookup(cgrp); +} + +SEC("tp_btf/cgroup_mkdir") +int BPF_PROG(cgrp_kfunc_acquire_untrusted, struct cgroup *cgrp, const char *path) +{ + struct cgroup *acquired; + struct __cgrps_kfunc_map_value *v; + + v = insert_lookup_cgrp(cgrp); + if (!v) + return 0; + + /* Can't invoke bpf_cgroup_acquire() on an untrusted pointer. */ + acquired = bpf_cgroup_acquire(v->cgrp); + bpf_cgroup_release(acquired); + + return 0; +} + +SEC("tp_btf/cgroup_mkdir") +int BPF_PROG(cgrp_kfunc_acquire_fp, struct cgroup *cgrp, const char *path) +{ + struct cgroup *acquired, *stack_cgrp = (struct cgroup *)&path; + + /* Can't invoke bpf_cgroup_acquire() on a random frame pointer. */ + acquired = bpf_cgroup_acquire((struct cgroup *)&stack_cgrp); + bpf_cgroup_release(acquired); + + return 0; +} + +SEC("kretprobe/cgroup_destroy_locked") +int BPF_PROG(cgrp_kfunc_acquire_unsafe_kretprobe, struct cgroup *cgrp) +{ + struct cgroup *acquired; + + /* Can't acquire an untrusted struct cgroup * pointer. */ + acquired = bpf_cgroup_acquire(cgrp); + bpf_cgroup_release(acquired); + + return 0; +} + +SEC("tp_btf/cgroup_mkdir") +int BPF_PROG(cgrp_kfunc_acquire_trusted_walked, struct cgroup *cgrp, const char *path) +{ + struct cgroup *acquired; + + /* Can't invoke bpf_cgroup_acquire() on a pointer obtained from walking a trusted cgroup. */ + acquired = bpf_cgroup_acquire(cgrp->old_dom_cgrp); + bpf_cgroup_release(acquired); + + return 0; +} + + +SEC("tp_btf/cgroup_mkdir") +int BPF_PROG(cgrp_kfunc_acquire_null, struct cgroup *cgrp, const char *path) +{ + struct cgroup *acquired; + + /* Can't invoke bpf_cgroup_acquire() on a NULL pointer. */ + acquired = bpf_cgroup_acquire(NULL); + if (!acquired) + return 0; + bpf_cgroup_release(acquired); + + return 0; +} + +SEC("tp_btf/cgroup_mkdir") +int BPF_PROG(cgrp_kfunc_acquire_unreleased, struct cgroup *cgrp, const char *path) +{ + struct cgroup *acquired; + + acquired = bpf_cgroup_acquire(cgrp); + + /* Acquired cgroup is never released. */ + + return 0; +} + +SEC("tp_btf/cgroup_mkdir") +int BPF_PROG(cgrp_kfunc_get_non_kptr_param, struct cgroup *cgrp, const char *path) +{ + struct cgroup *kptr; + + /* Cannot use bpf_cgroup_kptr_get() on a non-kptr, even on a valid cgroup. */ + kptr = bpf_cgroup_kptr_get(&cgrp); + if (!kptr) + return 0; + + bpf_cgroup_release(kptr); + + return 0; +} + +SEC("tp_btf/cgroup_mkdir") +int BPF_PROG(cgrp_kfunc_get_non_kptr_acquired, struct cgroup *cgrp, const char *path) +{ + struct cgroup *kptr, *acquired; + + acquired = bpf_cgroup_acquire(cgrp); + + /* Cannot use bpf_cgroup_kptr_get() on a non-map-value, even if the kptr was acquired. */ + kptr = bpf_cgroup_kptr_get(&acquired); + bpf_cgroup_release(acquired); + if (!kptr) + return 0; + + bpf_cgroup_release(kptr); + + return 0; +} + +SEC("tp_btf/cgroup_mkdir") +int BPF_PROG(cgrp_kfunc_get_null, struct cgroup *cgrp, const char *path) +{ + struct cgroup *kptr; + + /* Cannot use bpf_cgroup_kptr_get() on a NULL pointer. */ + kptr = bpf_cgroup_kptr_get(NULL); + if (!kptr) + return 0; + + bpf_cgroup_release(kptr); + + return 0; +} + +SEC("tp_btf/cgroup_mkdir") +int BPF_PROG(cgrp_kfunc_xchg_unreleased, struct cgroup *cgrp, const char *path) +{ + struct cgroup *kptr; + struct __cgrps_kfunc_map_value *v; + + v = insert_lookup_cgrp(cgrp); + if (!v) + return 0; + + kptr = bpf_kptr_xchg(&v->cgrp, NULL); + if (!kptr) + return 0; + + /* Kptr retrieved from map is never released. */ + + return 0; +} + +SEC("tp_btf/cgroup_mkdir") +int BPF_PROG(cgrp_kfunc_get_unreleased, struct cgroup *cgrp, const char *path) +{ + struct cgroup *kptr; + struct __cgrps_kfunc_map_value *v; + + v = insert_lookup_cgrp(cgrp); + if (!v) + return 0; + + kptr = bpf_cgroup_kptr_get(&v->cgrp); + if (!kptr) + return 0; + + /* Kptr acquired above is never released. */ + + return 0; +} + +SEC("tp_btf/cgroup_mkdir") +int BPF_PROG(cgrp_kfunc_release_untrusted, struct cgroup *cgrp, const char *path) +{ + struct __cgrps_kfunc_map_value *v; + + v = insert_lookup_cgrp(cgrp); + if (!v) + return 0; + + /* Can't invoke bpf_cgroup_release() on an untrusted pointer. */ + bpf_cgroup_release(v->cgrp); + + return 0; +} + +SEC("tp_btf/cgroup_mkdir") +int BPF_PROG(cgrp_kfunc_release_fp, struct cgroup *cgrp, const char *path) +{ + struct cgroup *acquired = (struct cgroup *)&path; + + /* Cannot release random frame pointer. */ + bpf_cgroup_release(acquired); + + return 0; +} + +SEC("tp_btf/cgroup_mkdir") +int BPF_PROG(cgrp_kfunc_release_null, struct cgroup *cgrp, const char *path) +{ + struct __cgrps_kfunc_map_value local, *v; + long status; + struct cgroup *acquired, *old; + s32 id; + + status = bpf_probe_read_kernel(&id, sizeof(id), &cgrp->self.id); + if (status) + return 0; + + local.cgrp = NULL; + status = bpf_map_update_elem(&__cgrps_kfunc_map, &id, &local, BPF_NOEXIST); + if (status) + return status; + + v = bpf_map_lookup_elem(&__cgrps_kfunc_map, &id); + if (!v) + return -ENOENT; + + acquired = bpf_cgroup_acquire(cgrp); + + old = bpf_kptr_xchg(&v->cgrp, acquired); + + /* old cannot be passed to bpf_cgroup_release() without a NULL check. */ + bpf_cgroup_release(old); + + return 0; +} + +SEC("tp_btf/cgroup_mkdir") +int BPF_PROG(cgrp_kfunc_release_unacquired, struct cgroup *cgrp, const char *path) +{ + /* Cannot release trusted cgroup pointer which was not acquired. */ + bpf_cgroup_release(cgrp); + + return 0; +} diff --git a/tools/testing/selftests/bpf/progs/cgrp_kfunc_success.c b/tools/testing/selftests/bpf/progs/cgrp_kfunc_success.c new file mode 100644 index 000000000000..9f4569f7598b --- /dev/null +++ b/tools/testing/selftests/bpf/progs/cgrp_kfunc_success.c @@ -0,0 +1,125 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2022 Meta Platforms, Inc. and affiliates. */ + +#include +#include +#include + +#include "cgrp_kfunc_common.h" + +char _license[] SEC("license") = "GPL"; + +int err, pid, invocations; + +/* Prototype for all of the program trace events below: + * + * TRACE_EVENT(cgroup_mkdir, + * TP_PROTO(struct cgroup *cgrp, const char *path), + * TP_ARGS(cgrp, path) + */ + +static bool is_test_kfunc_task(void) +{ + int cur_pid = bpf_get_current_pid_tgid() >> 32; + bool same = pid == cur_pid; + + if (same) + __sync_fetch_and_add(&invocations, 1); + + return same; +} + +SEC("tp_btf/cgroup_mkdir") +int BPF_PROG(test_cgrp_acquire_release_argument, struct cgroup *cgrp, const char *path) +{ + struct cgroup *acquired; + + if (!is_test_kfunc_task()) + return 0; + + acquired = bpf_cgroup_acquire(cgrp); + bpf_cgroup_release(acquired); + + return 0; +} + +SEC("tp_btf/cgroup_mkdir") +int BPF_PROG(test_cgrp_acquire_leave_in_map, struct cgroup *cgrp, const char *path) +{ + long status; + + if (!is_test_kfunc_task()) + return 0; + + status = cgrps_kfunc_map_insert(cgrp); + if (status) + err = 1; + + return 0; +} + +SEC("tp_btf/cgroup_mkdir") +int BPF_PROG(test_cgrp_xchg_release, struct cgroup *cgrp, const char *path) +{ + struct cgroup *kptr; + struct __cgrps_kfunc_map_value *v; + long status; + + if (!is_test_kfunc_task()) + return 0; + + status = cgrps_kfunc_map_insert(cgrp); + if (status) { + err = 1; + return 0; + } + + v = cgrps_kfunc_map_value_lookup(cgrp); + if (!v) { + err = 2; + return 0; + } + + kptr = bpf_kptr_xchg(&v->cgrp, NULL); + if (!kptr) { + err = 3; + return 0; + } + + bpf_cgroup_release(kptr); + + return 0; +} + +SEC("tp_btf/cgroup_mkdir") +int BPF_PROG(test_cgrp_get_release, struct cgroup *cgrp, const char *path) +{ + struct cgroup *kptr; + struct __cgrps_kfunc_map_value *v; + long status; + + if (!is_test_kfunc_task()) + return 0; + + status = cgrps_kfunc_map_insert(cgrp); + if (status) { + err = 1; + return 0; + } + + v = cgrps_kfunc_map_value_lookup(cgrp); + if (!v) { + err = 2; + return 0; + } + + kptr = bpf_cgroup_kptr_get(&v->cgrp); + if (!kptr) { + err = 3; + return 0; + } + + bpf_cgroup_release(kptr); + + return 0; +} From 5ca7867078296cfa9c100f9a3b2d24be1e139825 Mon Sep 17 00:00:00 2001 From: David Vernet Date: Mon, 21 Nov 2022 23:54:57 -0600 Subject: [PATCH 1154/2002] bpf: Add bpf_cgroup_ancestor() kfunc struct cgroup * objects have a variably sized struct cgroup *ancestors[] field which stores pointers to their ancestor cgroups. If using a cgroup as a kptr, it can be useful to access these ancestors, but doing so requires variable offset accesses for PTR_TO_BTF_ID, which is currently unsupported. This is a very useful field to access for cgroup kptrs, as programs may wish to walk their ancestor cgroups when determining e.g. their proportional cpu.weight. So as to enable this functionality with cgroup kptrs before var_off is supported for PTR_TO_BTF_ID, this patch adds a bpf_cgroup_ancestor() kfunc which accesses the cgroup node on behalf of the caller, and acquires a reference on it. Once var_off is supported for PTR_TO_BTF_ID, and fields inside a struct can be marked as trusted so they retain the PTR_TRUSTED modifier when walked, this can be removed. Signed-off-by: David Vernet Link: https://lore.kernel.org/r/20221122055458.173143-4-void@manifault.com Signed-off-by: Alexei Starovoitov --- kernel/bpf/helpers.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/kernel/bpf/helpers.c b/kernel/bpf/helpers.c index 420a4251cccc..e4e9db301db5 100644 --- a/kernel/bpf/helpers.c +++ b/kernel/bpf/helpers.c @@ -1938,6 +1938,25 @@ void bpf_cgroup_release(struct cgroup *cgrp) cgroup_put(cgrp); } + +/** + * bpf_cgroup_ancestor - Perform a lookup on an entry in a cgroup's ancestor + * array. A cgroup returned by this kfunc which is not subsequently stored in a + * map, must be released by calling bpf_cgroup_release(). + * @cgrp: The cgroup for which we're performing a lookup. + * @level: The level of ancestor to look up. + */ +struct cgroup *bpf_cgroup_ancestor(struct cgroup *cgrp, int level) +{ + struct cgroup *ancestor; + + if (level > cgrp->level || level < 0) + return NULL; + + ancestor = cgrp->ancestors[level]; + cgroup_get(ancestor); + return ancestor; +} #endif /* CONFIG_CGROUPS */ void *bpf_cast_to_kern_ctx(void *obj) @@ -1970,6 +1989,7 @@ BTF_ID_FLAGS(func, bpf_task_release, KF_RELEASE) BTF_ID_FLAGS(func, bpf_cgroup_acquire, KF_ACQUIRE | KF_TRUSTED_ARGS) BTF_ID_FLAGS(func, bpf_cgroup_kptr_get, KF_ACQUIRE | KF_KPTR_GET | KF_RET_NULL) BTF_ID_FLAGS(func, bpf_cgroup_release, KF_RELEASE) +BTF_ID_FLAGS(func, bpf_cgroup_ancestor, KF_ACQUIRE | KF_TRUSTED_ARGS | KF_RET_NULL) #endif BTF_SET8_END(generic_btf_ids) From 227a89cf504188759fd3d8933a3a06fc60cca7f2 Mon Sep 17 00:00:00 2001 From: David Vernet Date: Mon, 21 Nov 2022 23:54:58 -0600 Subject: [PATCH 1155/2002] selftests/bpf: Add selftests for bpf_cgroup_ancestor() kfunc bpf_cgroup_ancestor() allows BPF programs to access the ancestor of a struct cgroup *. This patch adds selftests that validate its expected behavior. Signed-off-by: David Vernet Link: https://lore.kernel.org/r/20221122055458.173143-5-void@manifault.com Signed-off-by: Alexei Starovoitov --- .../selftests/bpf/prog_tests/cgrp_kfunc.c | 1 + .../selftests/bpf/progs/cgrp_kfunc_common.h | 1 + .../selftests/bpf/progs/cgrp_kfunc_success.c | 45 +++++++++++++++++++ 3 files changed, 47 insertions(+) diff --git a/tools/testing/selftests/bpf/prog_tests/cgrp_kfunc.c b/tools/testing/selftests/bpf/prog_tests/cgrp_kfunc.c index a59b166bbcc4..973f0c5af965 100644 --- a/tools/testing/selftests/bpf/prog_tests/cgrp_kfunc.c +++ b/tools/testing/selftests/bpf/prog_tests/cgrp_kfunc.c @@ -86,6 +86,7 @@ static const char * const success_tests[] = { "test_cgrp_acquire_leave_in_map", "test_cgrp_xchg_release", "test_cgrp_get_release", + "test_cgrp_get_ancestors", }; static struct { diff --git a/tools/testing/selftests/bpf/progs/cgrp_kfunc_common.h b/tools/testing/selftests/bpf/progs/cgrp_kfunc_common.h index 3f18def0e45c..7d30855bfe78 100644 --- a/tools/testing/selftests/bpf/progs/cgrp_kfunc_common.h +++ b/tools/testing/selftests/bpf/progs/cgrp_kfunc_common.h @@ -23,6 +23,7 @@ struct hash_map { struct cgroup *bpf_cgroup_acquire(struct cgroup *p) __ksym; struct cgroup *bpf_cgroup_kptr_get(struct cgroup **pp) __ksym; void bpf_cgroup_release(struct cgroup *p) __ksym; +struct cgroup *bpf_cgroup_ancestor(struct cgroup *cgrp, int level) __ksym; static inline struct __cgrps_kfunc_map_value *cgrps_kfunc_map_value_lookup(struct cgroup *cgrp) { diff --git a/tools/testing/selftests/bpf/progs/cgrp_kfunc_success.c b/tools/testing/selftests/bpf/progs/cgrp_kfunc_success.c index 9f4569f7598b..0c23ea32df9f 100644 --- a/tools/testing/selftests/bpf/progs/cgrp_kfunc_success.c +++ b/tools/testing/selftests/bpf/progs/cgrp_kfunc_success.c @@ -123,3 +123,48 @@ int BPF_PROG(test_cgrp_get_release, struct cgroup *cgrp, const char *path) return 0; } + +SEC("tp_btf/cgroup_mkdir") +int BPF_PROG(test_cgrp_get_ancestors, struct cgroup *cgrp, const char *path) +{ + struct cgroup *self, *ancestor1, *invalid; + + if (!is_test_kfunc_task()) + return 0; + + self = bpf_cgroup_ancestor(cgrp, cgrp->level); + if (!self) { + err = 1; + return 0; + } + + if (self->self.id != cgrp->self.id) { + bpf_cgroup_release(self); + err = 2; + return 0; + } + bpf_cgroup_release(self); + + ancestor1 = bpf_cgroup_ancestor(cgrp, cgrp->level - 1); + if (!ancestor1) { + err = 3; + return 0; + } + bpf_cgroup_release(ancestor1); + + invalid = bpf_cgroup_ancestor(cgrp, 10000); + if (invalid) { + bpf_cgroup_release(invalid); + err = 4; + return 0; + } + + invalid = bpf_cgroup_ancestor(cgrp, -1); + if (invalid) { + bpf_cgroup_release(invalid); + err = 5; + return 0; + } + + return 0; +} From c90b6b1005ec7423c7d0063eb27a9728498f6ec8 Mon Sep 17 00:00:00 2001 From: Saeed Mahameed Date: Tue, 22 Nov 2022 10:41:58 -0800 Subject: [PATCH 1156/2002] tcp: Fix build break when CONFIG_IPV6=n MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The cited commit caused the following build break when CONFIG_IPV6 was disabled net/ipv4/tcp_input.c: In function ‘tcp_syn_flood_action’: include/net/sock.h:387:37: error: ‘const struct sock_common’ has no member named ‘skc_v6_rcv_saddr’; did you mean ‘skc_rcv_saddr’? Fix by using inet6_rcv_saddr() macro which handles this situation nicely. Fixes: d9282e48c608 ("tcp: Add listening address to SYN flood message") Signed-off-by: Saeed Mahameed Reported-by: Geert Uytterhoeven CC: Matthieu Baerts CC: Jamie Bainbridge Reviewed-by: Eric Dumazet Link: https://lore.kernel.org/r/20221122184158.170798-1-saeed@kernel.org Signed-off-by: Jakub Kicinski --- net/ipv4/tcp_input.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index 0ae291e53eab..1efacbe948da 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -6845,7 +6845,7 @@ static bool tcp_syn_flood_action(const struct sock *sk, const char *proto) xchg(&queue->synflood_warned, 1) == 0) { if (IS_ENABLED(CONFIG_IPV6) && sk->sk_family == AF_INET6) { net_info_ratelimited("%s: Possible SYN flooding on port [%pI6c]:%u. %s.\n", - proto, &sk->sk_v6_rcv_saddr, + proto, inet6_rcv_saddr(sk), sk->sk_num, msg); } else { net_info_ratelimited("%s: Possible SYN flooding on port %pI4:%u. %s.\n", From 815bc3ac75e9e34727f8ca78380266f34a3b6c66 Mon Sep 17 00:00:00 2001 From: Moshe Shemesh Date: Sun, 20 Nov 2022 10:36:52 +0200 Subject: [PATCH 1157/2002] devlink: remove redundant health state set to error Reporter health_state is set twice to error in devlink_health_report(). Remove second time as it is redundant. Signed-off-by: Moshe Shemesh Reviewed-by: Eran Ben Elisha Reviewed-by: Leon Romanovsky Link: https://lore.kernel.org/r/1668933412-5498-1-git-send-email-moshe@nvidia.com Signed-off-by: Jakub Kicinski --- net/core/devlink.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/net/core/devlink.c b/net/core/devlink.c index d93bc95cd7cb..cea154ddce7a 100644 --- a/net/core/devlink.c +++ b/net/core/devlink.c @@ -7846,8 +7846,6 @@ int devlink_health_report(struct devlink_health_reporter *reporter, return -ECANCELED; } - reporter->health_state = DEVLINK_HEALTH_REPORTER_STATE_ERROR; - if (reporter->auto_dump) { mutex_lock(&reporter->dump_lock); /* store current dump of current error, for later analysis */ From c5fb8ead3283955dc68671f853017b181f96fdc1 Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Mon, 21 Nov 2022 15:55:39 +0200 Subject: [PATCH 1158/2002] net: dsa: unexport dsa_dev_to_net_device() dsa.o and dsa2.o are linked into the same dsa_core.o, there is no reason to export this symbol when its only caller is local. Signed-off-by: Vladimir Oltean Reviewed-by: Florian Fainelli Signed-off-by: Jakub Kicinski --- include/net/dsa.h | 2 -- net/dsa/dsa.c | 1 - net/dsa/dsa_priv.h | 2 ++ 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/include/net/dsa.h b/include/net/dsa.h index 82da44561f4c..d5bfcb63d4c2 100644 --- a/include/net/dsa.h +++ b/include/net/dsa.h @@ -1285,8 +1285,6 @@ struct dsa_switch_driver { const struct dsa_switch_ops *ops; }; -struct net_device *dsa_dev_to_net_device(struct device *dev); - bool dsa_fdb_present_in_other_db(struct dsa_switch *ds, int port, const unsigned char *addr, u16 vid, struct dsa_db db); diff --git a/net/dsa/dsa.c b/net/dsa/dsa.c index 4afd3edbd64d..07158c7560b5 100644 --- a/net/dsa/dsa.c +++ b/net/dsa/dsa.c @@ -182,7 +182,6 @@ struct net_device *dsa_dev_to_net_device(struct device *dev) return NULL; } -EXPORT_SYMBOL_GPL(dsa_dev_to_net_device); /* Determine if we should defer delivery of skb until we have a rx timestamp. * diff --git a/net/dsa/dsa_priv.h b/net/dsa/dsa_priv.h index 24e0ea218a35..b60987e8d931 100644 --- a/net/dsa/dsa_priv.h +++ b/net/dsa/dsa_priv.h @@ -247,6 +247,8 @@ const struct dsa_device_ops *dsa_tag_driver_get_by_id(int tag_protocol); const struct dsa_device_ops *dsa_tag_driver_get_by_name(const char *name); void dsa_tag_driver_put(const struct dsa_device_ops *ops); +struct net_device *dsa_dev_to_net_device(struct device *dev); + bool dsa_db_equal(const struct dsa_db *a, const struct dsa_db *b); bool dsa_schedule_work(struct work_struct *work); From d2be320495b93ffb469e53100ba1668d0cd7eedc Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Mon, 21 Nov 2022 15:55:40 +0200 Subject: [PATCH 1159/2002] net: dsa: modularize DSA_TAG_PROTO_NONE There is no reason that I can see why the no-op tagging protocol should be registered manually, so make it a module and make all drivers which have any sort of reference to DSA_TAG_PROTO_NONE select it. Note that I don't know if ksz_get_tag_protocol() really needs this, or if it's just the logic which is poorly written. All switches seem to have their own tagging protocol, and DSA_TAG_PROTO_NONE is just a fallback that never gets used. Signed-off-by: Vladimir Oltean Reviewed-by: Florian Fainelli Signed-off-by: Jakub Kicinski --- drivers/net/dsa/Kconfig | 2 ++ drivers/net/dsa/b53/Kconfig | 1 + drivers/net/dsa/microchip/Kconfig | 1 + net/dsa/Kconfig | 6 ++++++ net/dsa/Makefile | 1 + net/dsa/dsa.c | 21 --------------------- net/dsa/dsa_priv.h | 1 - net/dsa/tag_none.c | 30 ++++++++++++++++++++++++++++++ 8 files changed, 41 insertions(+), 22 deletions(-) create mode 100644 net/dsa/tag_none.c diff --git a/drivers/net/dsa/Kconfig b/drivers/net/dsa/Kconfig index 07507b4820d7..c26755f662c1 100644 --- a/drivers/net/dsa/Kconfig +++ b/drivers/net/dsa/Kconfig @@ -18,6 +18,7 @@ config NET_DSA_BCM_SF2 config NET_DSA_LOOP tristate "DSA mock-up Ethernet switch chip support" + select NET_DSA_TAG_NONE select FIXED_PHY help This enables support for a fake mock-up switch chip which @@ -99,6 +100,7 @@ config NET_DSA_SMSC_LAN9303_MDIO config NET_DSA_VITESSE_VSC73XX tristate + select NET_DSA_TAG_NONE select FIXED_PHY select VITESSE_PHY select GPIOLIB diff --git a/drivers/net/dsa/b53/Kconfig b/drivers/net/dsa/b53/Kconfig index 90b525160b71..ebaa4a80d544 100644 --- a/drivers/net/dsa/b53/Kconfig +++ b/drivers/net/dsa/b53/Kconfig @@ -2,6 +2,7 @@ menuconfig B53 tristate "Broadcom BCM53xx managed switch support" depends on NET_DSA + select NET_DSA_TAG_NONE select NET_DSA_TAG_BRCM select NET_DSA_TAG_BRCM_LEGACY select NET_DSA_TAG_BRCM_PREPEND diff --git a/drivers/net/dsa/microchip/Kconfig b/drivers/net/dsa/microchip/Kconfig index 06b1efdb5e7d..913f83ef013c 100644 --- a/drivers/net/dsa/microchip/Kconfig +++ b/drivers/net/dsa/microchip/Kconfig @@ -3,6 +3,7 @@ menuconfig NET_DSA_MICROCHIP_KSZ_COMMON tristate "Microchip KSZ8795/KSZ9477/LAN937x series switch support" depends on NET_DSA select NET_DSA_TAG_KSZ + select NET_DSA_TAG_NONE help This driver adds support for Microchip KSZ9477 series switch and KSZ8795/KSZ88x3 switch chips. diff --git a/net/dsa/Kconfig b/net/dsa/Kconfig index 3eef72ce99a4..8e698bea99a3 100644 --- a/net/dsa/Kconfig +++ b/net/dsa/Kconfig @@ -18,6 +18,12 @@ if NET_DSA # Drivers must select the appropriate tagging format(s) +config NET_DSA_TAG_NONE + tristate "No-op tag driver" + help + Say Y or M if you want to enable support for switches which don't tag + frames over the CPU port. + config NET_DSA_TAG_AR9331 tristate "Tag driver for Atheros AR9331 SoC with built-in switch" help diff --git a/net/dsa/Makefile b/net/dsa/Makefile index bf57ef3bce2a..14e05ab64135 100644 --- a/net/dsa/Makefile +++ b/net/dsa/Makefile @@ -20,6 +20,7 @@ obj-$(CONFIG_NET_DSA_TAG_HELLCREEK) += tag_hellcreek.o obj-$(CONFIG_NET_DSA_TAG_KSZ) += tag_ksz.o obj-$(CONFIG_NET_DSA_TAG_LAN9303) += tag_lan9303.o obj-$(CONFIG_NET_DSA_TAG_MTK) += tag_mtk.o +obj-$(CONFIG_NET_DSA_TAG_NONE) += tag_none.o obj-$(CONFIG_NET_DSA_TAG_OCELOT) += tag_ocelot.o obj-$(CONFIG_NET_DSA_TAG_OCELOT_8021Q) += tag_ocelot_8021q.o obj-$(CONFIG_NET_DSA_TAG_QCA) += tag_qca.o diff --git a/net/dsa/dsa.c b/net/dsa/dsa.c index 07158c7560b5..e609d64a2216 100644 --- a/net/dsa/dsa.c +++ b/net/dsa/dsa.c @@ -18,22 +18,6 @@ static LIST_HEAD(dsa_tag_drivers_list); static DEFINE_MUTEX(dsa_tag_drivers_lock); -static struct sk_buff *dsa_slave_notag_xmit(struct sk_buff *skb, - struct net_device *dev) -{ - /* Just return the original SKB */ - return skb; -} - -static const struct dsa_device_ops none_ops = { - .name = "none", - .proto = DSA_TAG_PROTO_NONE, - .xmit = dsa_slave_notag_xmit, - .rcv = NULL, -}; - -DSA_TAG_DRIVER(none_ops); - static void dsa_tag_driver_register(struct dsa_tag_driver *dsa_tag_driver, struct module *owner) { @@ -551,9 +535,6 @@ static int __init dsa_init_module(void) dev_add_pack(&dsa_pack_type); - dsa_tag_driver_register(&DSA_TAG_DRIVER_NAME(none_ops), - THIS_MODULE); - rc = rtnl_link_register(&dsa_link_ops); if (rc) goto netlink_register_fail; @@ -561,7 +542,6 @@ static int __init dsa_init_module(void) return 0; netlink_register_fail: - dsa_tag_driver_unregister(&DSA_TAG_DRIVER_NAME(none_ops)); dsa_slave_unregister_notifier(); dev_remove_pack(&dsa_pack_type); register_notifier_fail: @@ -574,7 +554,6 @@ module_init(dsa_init_module); static void __exit dsa_cleanup_module(void) { rtnl_link_unregister(&dsa_link_ops); - dsa_tag_driver_unregister(&DSA_TAG_DRIVER_NAME(none_ops)); dsa_slave_unregister_notifier(); dev_remove_pack(&dsa_pack_type); diff --git a/net/dsa/dsa_priv.h b/net/dsa/dsa_priv.h index b60987e8d931..c4ea5fda8f14 100644 --- a/net/dsa/dsa_priv.h +++ b/net/dsa/dsa_priv.h @@ -384,7 +384,6 @@ int dsa_port_change_master(struct dsa_port *dp, struct net_device *master, struct netlink_ext_ack *extack); /* slave.c */ -extern const struct dsa_device_ops notag_netdev_ops; extern struct notifier_block dsa_slave_switchdev_notifier; extern struct notifier_block dsa_slave_switchdev_blocking_notifier; diff --git a/net/dsa/tag_none.c b/net/dsa/tag_none.c new file mode 100644 index 000000000000..34a13c50d245 --- /dev/null +++ b/net/dsa/tag_none.c @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * net/dsa/tag_none.c - Traffic handling for switches with no tag + * Copyright (c) 2008-2009 Marvell Semiconductor + * Copyright (c) 2013 Florian Fainelli + * + * WARNING: do not use this for new switches. In case of no hardware + * tagging support, look at tag_8021q.c instead. + */ + +#include "dsa_priv.h" + +#define NONE_NAME "none" + +static struct sk_buff *dsa_slave_notag_xmit(struct sk_buff *skb, + struct net_device *dev) +{ + /* Just return the original SKB */ + return skb; +} + +static const struct dsa_device_ops none_ops = { + .name = NONE_NAME, + .proto = DSA_TAG_PROTO_NONE, + .xmit = dsa_slave_notag_xmit, +}; + +module_dsa_tag_driver(none_ops); +MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_NONE, NONE_NAME); +MODULE_LICENSE("GPL"); From 5cf2c75b5b91bcf81d61b2d2ea1c71363bcacf89 Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Mon, 21 Nov 2022 15:55:41 +0200 Subject: [PATCH 1160/2002] net: dsa: move bulk of devlink code to devlink.{c,h} dsa.c and dsa2.c are bloated with too much off-topic code. Identify all code related to devlink and move it to a new devlink.c file. Steer clear of the dsa_priv.h dumping ground antipattern and create a dedicated devlink.h for it, which will be included only by the C files which need it. Usage of dsa_priv.h will be minimized in later patches. Signed-off-by: Vladimir Oltean Reviewed-by: Florian Fainelli Signed-off-by: Jakub Kicinski --- net/dsa/Makefile | 1 + net/dsa/devlink.c | 355 ++++++++++++++++++++++++++++++++++++++++++++++ net/dsa/devlink.h | 13 ++ net/dsa/dsa.c | 107 -------------- net/dsa/dsa2.c | 240 +------------------------------ 5 files changed, 370 insertions(+), 346 deletions(-) create mode 100644 net/dsa/devlink.c create mode 100644 net/dsa/devlink.h diff --git a/net/dsa/Makefile b/net/dsa/Makefile index 14e05ab64135..bc872c0d7011 100644 --- a/net/dsa/Makefile +++ b/net/dsa/Makefile @@ -2,6 +2,7 @@ # the core obj-$(CONFIG_NET_DSA) += dsa_core.o dsa_core-y += \ + devlink.o \ dsa.o \ dsa2.o \ master.o \ diff --git a/net/dsa/devlink.c b/net/dsa/devlink.c new file mode 100644 index 000000000000..eff440b2b3c5 --- /dev/null +++ b/net/dsa/devlink.c @@ -0,0 +1,355 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * DSA devlink handling + */ + +#include +#include + +#include "devlink.h" + +static int dsa_devlink_info_get(struct devlink *dl, + struct devlink_info_req *req, + struct netlink_ext_ack *extack) +{ + struct dsa_switch *ds = dsa_devlink_to_ds(dl); + + if (ds->ops->devlink_info_get) + return ds->ops->devlink_info_get(ds, req, extack); + + return -EOPNOTSUPP; +} + +static int dsa_devlink_sb_pool_get(struct devlink *dl, + unsigned int sb_index, u16 pool_index, + struct devlink_sb_pool_info *pool_info) +{ + struct dsa_switch *ds = dsa_devlink_to_ds(dl); + + if (!ds->ops->devlink_sb_pool_get) + return -EOPNOTSUPP; + + return ds->ops->devlink_sb_pool_get(ds, sb_index, pool_index, + pool_info); +} + +static int dsa_devlink_sb_pool_set(struct devlink *dl, unsigned int sb_index, + u16 pool_index, u32 size, + enum devlink_sb_threshold_type threshold_type, + struct netlink_ext_ack *extack) +{ + struct dsa_switch *ds = dsa_devlink_to_ds(dl); + + if (!ds->ops->devlink_sb_pool_set) + return -EOPNOTSUPP; + + return ds->ops->devlink_sb_pool_set(ds, sb_index, pool_index, size, + threshold_type, extack); +} + +static int dsa_devlink_sb_port_pool_get(struct devlink_port *dlp, + unsigned int sb_index, u16 pool_index, + u32 *p_threshold) +{ + struct dsa_switch *ds = dsa_devlink_port_to_ds(dlp); + int port = dsa_devlink_port_to_port(dlp); + + if (!ds->ops->devlink_sb_port_pool_get) + return -EOPNOTSUPP; + + return ds->ops->devlink_sb_port_pool_get(ds, port, sb_index, + pool_index, p_threshold); +} + +static int dsa_devlink_sb_port_pool_set(struct devlink_port *dlp, + unsigned int sb_index, u16 pool_index, + u32 threshold, + struct netlink_ext_ack *extack) +{ + struct dsa_switch *ds = dsa_devlink_port_to_ds(dlp); + int port = dsa_devlink_port_to_port(dlp); + + if (!ds->ops->devlink_sb_port_pool_set) + return -EOPNOTSUPP; + + return ds->ops->devlink_sb_port_pool_set(ds, port, sb_index, + pool_index, threshold, extack); +} + +static int +dsa_devlink_sb_tc_pool_bind_get(struct devlink_port *dlp, + unsigned int sb_index, u16 tc_index, + enum devlink_sb_pool_type pool_type, + u16 *p_pool_index, u32 *p_threshold) +{ + struct dsa_switch *ds = dsa_devlink_port_to_ds(dlp); + int port = dsa_devlink_port_to_port(dlp); + + if (!ds->ops->devlink_sb_tc_pool_bind_get) + return -EOPNOTSUPP; + + return ds->ops->devlink_sb_tc_pool_bind_get(ds, port, sb_index, + tc_index, pool_type, + p_pool_index, p_threshold); +} + +static int +dsa_devlink_sb_tc_pool_bind_set(struct devlink_port *dlp, + unsigned int sb_index, u16 tc_index, + enum devlink_sb_pool_type pool_type, + u16 pool_index, u32 threshold, + struct netlink_ext_ack *extack) +{ + struct dsa_switch *ds = dsa_devlink_port_to_ds(dlp); + int port = dsa_devlink_port_to_port(dlp); + + if (!ds->ops->devlink_sb_tc_pool_bind_set) + return -EOPNOTSUPP; + + return ds->ops->devlink_sb_tc_pool_bind_set(ds, port, sb_index, + tc_index, pool_type, + pool_index, threshold, + extack); +} + +static int dsa_devlink_sb_occ_snapshot(struct devlink *dl, + unsigned int sb_index) +{ + struct dsa_switch *ds = dsa_devlink_to_ds(dl); + + if (!ds->ops->devlink_sb_occ_snapshot) + return -EOPNOTSUPP; + + return ds->ops->devlink_sb_occ_snapshot(ds, sb_index); +} + +static int dsa_devlink_sb_occ_max_clear(struct devlink *dl, + unsigned int sb_index) +{ + struct dsa_switch *ds = dsa_devlink_to_ds(dl); + + if (!ds->ops->devlink_sb_occ_max_clear) + return -EOPNOTSUPP; + + return ds->ops->devlink_sb_occ_max_clear(ds, sb_index); +} + +static int dsa_devlink_sb_occ_port_pool_get(struct devlink_port *dlp, + unsigned int sb_index, + u16 pool_index, u32 *p_cur, + u32 *p_max) +{ + struct dsa_switch *ds = dsa_devlink_port_to_ds(dlp); + int port = dsa_devlink_port_to_port(dlp); + + if (!ds->ops->devlink_sb_occ_port_pool_get) + return -EOPNOTSUPP; + + return ds->ops->devlink_sb_occ_port_pool_get(ds, port, sb_index, + pool_index, p_cur, p_max); +} + +static int +dsa_devlink_sb_occ_tc_port_bind_get(struct devlink_port *dlp, + unsigned int sb_index, u16 tc_index, + enum devlink_sb_pool_type pool_type, + u32 *p_cur, u32 *p_max) +{ + struct dsa_switch *ds = dsa_devlink_port_to_ds(dlp); + int port = dsa_devlink_port_to_port(dlp); + + if (!ds->ops->devlink_sb_occ_tc_port_bind_get) + return -EOPNOTSUPP; + + return ds->ops->devlink_sb_occ_tc_port_bind_get(ds, port, + sb_index, tc_index, + pool_type, p_cur, + p_max); +} + +const struct devlink_ops dsa_devlink_ops = { + .info_get = dsa_devlink_info_get, + .sb_pool_get = dsa_devlink_sb_pool_get, + .sb_pool_set = dsa_devlink_sb_pool_set, + .sb_port_pool_get = dsa_devlink_sb_port_pool_get, + .sb_port_pool_set = dsa_devlink_sb_port_pool_set, + .sb_tc_pool_bind_get = dsa_devlink_sb_tc_pool_bind_get, + .sb_tc_pool_bind_set = dsa_devlink_sb_tc_pool_bind_set, + .sb_occ_snapshot = dsa_devlink_sb_occ_snapshot, + .sb_occ_max_clear = dsa_devlink_sb_occ_max_clear, + .sb_occ_port_pool_get = dsa_devlink_sb_occ_port_pool_get, + .sb_occ_tc_port_bind_get = dsa_devlink_sb_occ_tc_port_bind_get, +}; + +int dsa_devlink_param_get(struct devlink *dl, u32 id, + struct devlink_param_gset_ctx *ctx) +{ + struct dsa_switch *ds = dsa_devlink_to_ds(dl); + + if (!ds->ops->devlink_param_get) + return -EOPNOTSUPP; + + return ds->ops->devlink_param_get(ds, id, ctx); +} +EXPORT_SYMBOL_GPL(dsa_devlink_param_get); + +int dsa_devlink_param_set(struct devlink *dl, u32 id, + struct devlink_param_gset_ctx *ctx) +{ + struct dsa_switch *ds = dsa_devlink_to_ds(dl); + + if (!ds->ops->devlink_param_set) + return -EOPNOTSUPP; + + return ds->ops->devlink_param_set(ds, id, ctx); +} +EXPORT_SYMBOL_GPL(dsa_devlink_param_set); + +int dsa_devlink_params_register(struct dsa_switch *ds, + const struct devlink_param *params, + size_t params_count) +{ + return devlink_params_register(ds->devlink, params, params_count); +} +EXPORT_SYMBOL_GPL(dsa_devlink_params_register); + +void dsa_devlink_params_unregister(struct dsa_switch *ds, + const struct devlink_param *params, + size_t params_count) +{ + devlink_params_unregister(ds->devlink, params, params_count); +} +EXPORT_SYMBOL_GPL(dsa_devlink_params_unregister); + +int dsa_devlink_resource_register(struct dsa_switch *ds, + const char *resource_name, + u64 resource_size, + u64 resource_id, + u64 parent_resource_id, + const struct devlink_resource_size_params *size_params) +{ + return devlink_resource_register(ds->devlink, resource_name, + resource_size, resource_id, + parent_resource_id, + size_params); +} +EXPORT_SYMBOL_GPL(dsa_devlink_resource_register); + +void dsa_devlink_resources_unregister(struct dsa_switch *ds) +{ + devlink_resources_unregister(ds->devlink); +} +EXPORT_SYMBOL_GPL(dsa_devlink_resources_unregister); + +void dsa_devlink_resource_occ_get_register(struct dsa_switch *ds, + u64 resource_id, + devlink_resource_occ_get_t *occ_get, + void *occ_get_priv) +{ + return devlink_resource_occ_get_register(ds->devlink, resource_id, + occ_get, occ_get_priv); +} +EXPORT_SYMBOL_GPL(dsa_devlink_resource_occ_get_register); + +void dsa_devlink_resource_occ_get_unregister(struct dsa_switch *ds, + u64 resource_id) +{ + devlink_resource_occ_get_unregister(ds->devlink, resource_id); +} +EXPORT_SYMBOL_GPL(dsa_devlink_resource_occ_get_unregister); + +struct devlink_region * +dsa_devlink_region_create(struct dsa_switch *ds, + const struct devlink_region_ops *ops, + u32 region_max_snapshots, u64 region_size) +{ + return devlink_region_create(ds->devlink, ops, region_max_snapshots, + region_size); +} +EXPORT_SYMBOL_GPL(dsa_devlink_region_create); + +struct devlink_region * +dsa_devlink_port_region_create(struct dsa_switch *ds, + int port, + const struct devlink_port_region_ops *ops, + u32 region_max_snapshots, u64 region_size) +{ + struct dsa_port *dp = dsa_to_port(ds, port); + + return devlink_port_region_create(&dp->devlink_port, ops, + region_max_snapshots, + region_size); +} +EXPORT_SYMBOL_GPL(dsa_devlink_port_region_create); + +void dsa_devlink_region_destroy(struct devlink_region *region) +{ + devlink_region_destroy(region); +} +EXPORT_SYMBOL_GPL(dsa_devlink_region_destroy); + +int dsa_port_devlink_setup(struct dsa_port *dp) +{ + struct devlink_port *dlp = &dp->devlink_port; + struct dsa_switch_tree *dst = dp->ds->dst; + struct devlink_port_attrs attrs = {}; + struct devlink *dl = dp->ds->devlink; + struct dsa_switch *ds = dp->ds; + const unsigned char *id; + unsigned char len; + int err; + + memset(dlp, 0, sizeof(*dlp)); + devlink_port_init(dl, dlp); + + if (ds->ops->port_setup) { + err = ds->ops->port_setup(ds, dp->index); + if (err) + return err; + } + + id = (const unsigned char *)&dst->index; + len = sizeof(dst->index); + + attrs.phys.port_number = dp->index; + memcpy(attrs.switch_id.id, id, len); + attrs.switch_id.id_len = len; + + switch (dp->type) { + case DSA_PORT_TYPE_UNUSED: + attrs.flavour = DEVLINK_PORT_FLAVOUR_UNUSED; + break; + case DSA_PORT_TYPE_CPU: + attrs.flavour = DEVLINK_PORT_FLAVOUR_CPU; + break; + case DSA_PORT_TYPE_DSA: + attrs.flavour = DEVLINK_PORT_FLAVOUR_DSA; + break; + case DSA_PORT_TYPE_USER: + attrs.flavour = DEVLINK_PORT_FLAVOUR_PHYSICAL; + break; + } + + devlink_port_attrs_set(dlp, &attrs); + err = devlink_port_register(dl, dlp, dp->index); + if (err) { + if (ds->ops->port_teardown) + ds->ops->port_teardown(ds, dp->index); + return err; + } + + return 0; +} + +void dsa_port_devlink_teardown(struct dsa_port *dp) +{ + struct devlink_port *dlp = &dp->devlink_port; + struct dsa_switch *ds = dp->ds; + + devlink_port_unregister(dlp); + + if (ds->ops->port_teardown) + ds->ops->port_teardown(ds, dp->index); + + devlink_port_fini(dlp); +} diff --git a/net/dsa/devlink.h b/net/dsa/devlink.h new file mode 100644 index 000000000000..d077c7f336da --- /dev/null +++ b/net/dsa/devlink.h @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#ifndef __DSA_DEVLINK_H +#define __DSA_DEVLINK_H + +struct dsa_port; + +extern const struct devlink_ops dsa_devlink_ops; + +int dsa_port_devlink_setup(struct dsa_port *dp); +void dsa_port_devlink_teardown(struct dsa_port *dp); + +#endif diff --git a/net/dsa/dsa.c b/net/dsa/dsa.c index e609d64a2216..842a1f2488b2 100644 --- a/net/dsa/dsa.c +++ b/net/dsa/dsa.c @@ -344,113 +344,6 @@ void dsa_flush_workqueue(void) } EXPORT_SYMBOL_GPL(dsa_flush_workqueue); -int dsa_devlink_param_get(struct devlink *dl, u32 id, - struct devlink_param_gset_ctx *ctx) -{ - struct dsa_switch *ds = dsa_devlink_to_ds(dl); - - if (!ds->ops->devlink_param_get) - return -EOPNOTSUPP; - - return ds->ops->devlink_param_get(ds, id, ctx); -} -EXPORT_SYMBOL_GPL(dsa_devlink_param_get); - -int dsa_devlink_param_set(struct devlink *dl, u32 id, - struct devlink_param_gset_ctx *ctx) -{ - struct dsa_switch *ds = dsa_devlink_to_ds(dl); - - if (!ds->ops->devlink_param_set) - return -EOPNOTSUPP; - - return ds->ops->devlink_param_set(ds, id, ctx); -} -EXPORT_SYMBOL_GPL(dsa_devlink_param_set); - -int dsa_devlink_params_register(struct dsa_switch *ds, - const struct devlink_param *params, - size_t params_count) -{ - return devlink_params_register(ds->devlink, params, params_count); -} -EXPORT_SYMBOL_GPL(dsa_devlink_params_register); - -void dsa_devlink_params_unregister(struct dsa_switch *ds, - const struct devlink_param *params, - size_t params_count) -{ - devlink_params_unregister(ds->devlink, params, params_count); -} -EXPORT_SYMBOL_GPL(dsa_devlink_params_unregister); - -int dsa_devlink_resource_register(struct dsa_switch *ds, - const char *resource_name, - u64 resource_size, - u64 resource_id, - u64 parent_resource_id, - const struct devlink_resource_size_params *size_params) -{ - return devlink_resource_register(ds->devlink, resource_name, - resource_size, resource_id, - parent_resource_id, - size_params); -} -EXPORT_SYMBOL_GPL(dsa_devlink_resource_register); - -void dsa_devlink_resources_unregister(struct dsa_switch *ds) -{ - devlink_resources_unregister(ds->devlink); -} -EXPORT_SYMBOL_GPL(dsa_devlink_resources_unregister); - -void dsa_devlink_resource_occ_get_register(struct dsa_switch *ds, - u64 resource_id, - devlink_resource_occ_get_t *occ_get, - void *occ_get_priv) -{ - return devlink_resource_occ_get_register(ds->devlink, resource_id, - occ_get, occ_get_priv); -} -EXPORT_SYMBOL_GPL(dsa_devlink_resource_occ_get_register); - -void dsa_devlink_resource_occ_get_unregister(struct dsa_switch *ds, - u64 resource_id) -{ - devlink_resource_occ_get_unregister(ds->devlink, resource_id); -} -EXPORT_SYMBOL_GPL(dsa_devlink_resource_occ_get_unregister); - -struct devlink_region * -dsa_devlink_region_create(struct dsa_switch *ds, - const struct devlink_region_ops *ops, - u32 region_max_snapshots, u64 region_size) -{ - return devlink_region_create(ds->devlink, ops, region_max_snapshots, - region_size); -} -EXPORT_SYMBOL_GPL(dsa_devlink_region_create); - -struct devlink_region * -dsa_devlink_port_region_create(struct dsa_switch *ds, - int port, - const struct devlink_port_region_ops *ops, - u32 region_max_snapshots, u64 region_size) -{ - struct dsa_port *dp = dsa_to_port(ds, port); - - return devlink_port_region_create(&dp->devlink_port, ops, - region_max_snapshots, - region_size); -} -EXPORT_SYMBOL_GPL(dsa_devlink_port_region_create); - -void dsa_devlink_region_destroy(struct devlink_region *region) -{ - devlink_region_destroy(region); -} -EXPORT_SYMBOL_GPL(dsa_devlink_region_destroy); - struct dsa_port *dsa_port_from_netdev(struct net_device *netdev) { if (!netdev || !dsa_slave_dev_check(netdev)) diff --git a/net/dsa/dsa2.c b/net/dsa/dsa2.c index f8df55e2e23a..05e682c25590 100644 --- a/net/dsa/dsa2.c +++ b/net/dsa/dsa2.c @@ -18,6 +18,7 @@ #include #include +#include "devlink.h" #include "dsa_priv.h" static DEFINE_MUTEX(dsa2_mutex); @@ -461,72 +462,6 @@ static void dsa_tree_teardown_cpu_ports(struct dsa_switch_tree *dst) dp->cpu_dp = NULL; } -static int dsa_port_devlink_setup(struct dsa_port *dp) -{ - struct devlink_port *dlp = &dp->devlink_port; - struct dsa_switch_tree *dst = dp->ds->dst; - struct devlink_port_attrs attrs = {}; - struct devlink *dl = dp->ds->devlink; - struct dsa_switch *ds = dp->ds; - const unsigned char *id; - unsigned char len; - int err; - - memset(dlp, 0, sizeof(*dlp)); - devlink_port_init(dl, dlp); - - if (ds->ops->port_setup) { - err = ds->ops->port_setup(ds, dp->index); - if (err) - return err; - } - - id = (const unsigned char *)&dst->index; - len = sizeof(dst->index); - - attrs.phys.port_number = dp->index; - memcpy(attrs.switch_id.id, id, len); - attrs.switch_id.id_len = len; - - switch (dp->type) { - case DSA_PORT_TYPE_UNUSED: - attrs.flavour = DEVLINK_PORT_FLAVOUR_UNUSED; - break; - case DSA_PORT_TYPE_CPU: - attrs.flavour = DEVLINK_PORT_FLAVOUR_CPU; - break; - case DSA_PORT_TYPE_DSA: - attrs.flavour = DEVLINK_PORT_FLAVOUR_DSA; - break; - case DSA_PORT_TYPE_USER: - attrs.flavour = DEVLINK_PORT_FLAVOUR_PHYSICAL; - break; - } - - devlink_port_attrs_set(dlp, &attrs); - err = devlink_port_register(dl, dlp, dp->index); - if (err) { - if (ds->ops->port_teardown) - ds->ops->port_teardown(ds, dp->index); - return err; - } - - return 0; -} - -static void dsa_port_devlink_teardown(struct dsa_port *dp) -{ - struct devlink_port *dlp = &dp->devlink_port; - struct dsa_switch *ds = dp->ds; - - devlink_port_unregister(dlp); - - if (ds->ops->port_teardown) - ds->ops->port_teardown(ds, dp->index); - - devlink_port_fini(dlp); -} - static int dsa_port_setup(struct dsa_port *dp) { bool dsa_port_link_registered = false; @@ -638,179 +573,6 @@ static int dsa_port_setup_as_unused(struct dsa_port *dp) return dsa_port_setup(dp); } -static int dsa_devlink_info_get(struct devlink *dl, - struct devlink_info_req *req, - struct netlink_ext_ack *extack) -{ - struct dsa_switch *ds = dsa_devlink_to_ds(dl); - - if (ds->ops->devlink_info_get) - return ds->ops->devlink_info_get(ds, req, extack); - - return -EOPNOTSUPP; -} - -static int dsa_devlink_sb_pool_get(struct devlink *dl, - unsigned int sb_index, u16 pool_index, - struct devlink_sb_pool_info *pool_info) -{ - struct dsa_switch *ds = dsa_devlink_to_ds(dl); - - if (!ds->ops->devlink_sb_pool_get) - return -EOPNOTSUPP; - - return ds->ops->devlink_sb_pool_get(ds, sb_index, pool_index, - pool_info); -} - -static int dsa_devlink_sb_pool_set(struct devlink *dl, unsigned int sb_index, - u16 pool_index, u32 size, - enum devlink_sb_threshold_type threshold_type, - struct netlink_ext_ack *extack) -{ - struct dsa_switch *ds = dsa_devlink_to_ds(dl); - - if (!ds->ops->devlink_sb_pool_set) - return -EOPNOTSUPP; - - return ds->ops->devlink_sb_pool_set(ds, sb_index, pool_index, size, - threshold_type, extack); -} - -static int dsa_devlink_sb_port_pool_get(struct devlink_port *dlp, - unsigned int sb_index, u16 pool_index, - u32 *p_threshold) -{ - struct dsa_switch *ds = dsa_devlink_port_to_ds(dlp); - int port = dsa_devlink_port_to_port(dlp); - - if (!ds->ops->devlink_sb_port_pool_get) - return -EOPNOTSUPP; - - return ds->ops->devlink_sb_port_pool_get(ds, port, sb_index, - pool_index, p_threshold); -} - -static int dsa_devlink_sb_port_pool_set(struct devlink_port *dlp, - unsigned int sb_index, u16 pool_index, - u32 threshold, - struct netlink_ext_ack *extack) -{ - struct dsa_switch *ds = dsa_devlink_port_to_ds(dlp); - int port = dsa_devlink_port_to_port(dlp); - - if (!ds->ops->devlink_sb_port_pool_set) - return -EOPNOTSUPP; - - return ds->ops->devlink_sb_port_pool_set(ds, port, sb_index, - pool_index, threshold, extack); -} - -static int -dsa_devlink_sb_tc_pool_bind_get(struct devlink_port *dlp, - unsigned int sb_index, u16 tc_index, - enum devlink_sb_pool_type pool_type, - u16 *p_pool_index, u32 *p_threshold) -{ - struct dsa_switch *ds = dsa_devlink_port_to_ds(dlp); - int port = dsa_devlink_port_to_port(dlp); - - if (!ds->ops->devlink_sb_tc_pool_bind_get) - return -EOPNOTSUPP; - - return ds->ops->devlink_sb_tc_pool_bind_get(ds, port, sb_index, - tc_index, pool_type, - p_pool_index, p_threshold); -} - -static int -dsa_devlink_sb_tc_pool_bind_set(struct devlink_port *dlp, - unsigned int sb_index, u16 tc_index, - enum devlink_sb_pool_type pool_type, - u16 pool_index, u32 threshold, - struct netlink_ext_ack *extack) -{ - struct dsa_switch *ds = dsa_devlink_port_to_ds(dlp); - int port = dsa_devlink_port_to_port(dlp); - - if (!ds->ops->devlink_sb_tc_pool_bind_set) - return -EOPNOTSUPP; - - return ds->ops->devlink_sb_tc_pool_bind_set(ds, port, sb_index, - tc_index, pool_type, - pool_index, threshold, - extack); -} - -static int dsa_devlink_sb_occ_snapshot(struct devlink *dl, - unsigned int sb_index) -{ - struct dsa_switch *ds = dsa_devlink_to_ds(dl); - - if (!ds->ops->devlink_sb_occ_snapshot) - return -EOPNOTSUPP; - - return ds->ops->devlink_sb_occ_snapshot(ds, sb_index); -} - -static int dsa_devlink_sb_occ_max_clear(struct devlink *dl, - unsigned int sb_index) -{ - struct dsa_switch *ds = dsa_devlink_to_ds(dl); - - if (!ds->ops->devlink_sb_occ_max_clear) - return -EOPNOTSUPP; - - return ds->ops->devlink_sb_occ_max_clear(ds, sb_index); -} - -static int dsa_devlink_sb_occ_port_pool_get(struct devlink_port *dlp, - unsigned int sb_index, - u16 pool_index, u32 *p_cur, - u32 *p_max) -{ - struct dsa_switch *ds = dsa_devlink_port_to_ds(dlp); - int port = dsa_devlink_port_to_port(dlp); - - if (!ds->ops->devlink_sb_occ_port_pool_get) - return -EOPNOTSUPP; - - return ds->ops->devlink_sb_occ_port_pool_get(ds, port, sb_index, - pool_index, p_cur, p_max); -} - -static int -dsa_devlink_sb_occ_tc_port_bind_get(struct devlink_port *dlp, - unsigned int sb_index, u16 tc_index, - enum devlink_sb_pool_type pool_type, - u32 *p_cur, u32 *p_max) -{ - struct dsa_switch *ds = dsa_devlink_port_to_ds(dlp); - int port = dsa_devlink_port_to_port(dlp); - - if (!ds->ops->devlink_sb_occ_tc_port_bind_get) - return -EOPNOTSUPP; - - return ds->ops->devlink_sb_occ_tc_port_bind_get(ds, port, - sb_index, tc_index, - pool_type, p_cur, - p_max); -} - -static const struct devlink_ops dsa_devlink_ops = { - .info_get = dsa_devlink_info_get, - .sb_pool_get = dsa_devlink_sb_pool_get, - .sb_pool_set = dsa_devlink_sb_pool_set, - .sb_port_pool_get = dsa_devlink_sb_port_pool_get, - .sb_port_pool_set = dsa_devlink_sb_port_pool_set, - .sb_tc_pool_bind_get = dsa_devlink_sb_tc_pool_bind_get, - .sb_tc_pool_bind_set = dsa_devlink_sb_tc_pool_bind_set, - .sb_occ_snapshot = dsa_devlink_sb_occ_snapshot, - .sb_occ_max_clear = dsa_devlink_sb_occ_max_clear, - .sb_occ_port_pool_get = dsa_devlink_sb_occ_port_pool_get, - .sb_occ_tc_port_bind_get = dsa_devlink_sb_occ_tc_port_bind_get, -}; - static int dsa_switch_setup_tag_protocol(struct dsa_switch *ds) { const struct dsa_device_ops *tag_ops = ds->dst->tag_ops; From d95fa75061fbd09a7ae78a76bc8902a84b8538a5 Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Mon, 21 Nov 2022 15:55:42 +0200 Subject: [PATCH 1161/2002] net: dsa: if ds->setup is true, ds->devlink is always non-NULL Simplify dsa_switch_teardown() to remove the NULL checking for ds->devlink. Signed-off-by: Vladimir Oltean Reviewed-by: Florian Fainelli Signed-off-by: Jakub Kicinski --- net/dsa/dsa2.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/net/dsa/dsa2.c b/net/dsa/dsa2.c index 05e682c25590..f890dfcbf412 100644 --- a/net/dsa/dsa2.c +++ b/net/dsa/dsa2.c @@ -682,8 +682,9 @@ static int dsa_switch_setup(struct dsa_switch *ds) goto free_slave_mii_bus; } - ds->setup = true; devlink_register(ds->devlink); + + ds->setup = true; return 0; free_slave_mii_bus: @@ -705,8 +706,7 @@ static void dsa_switch_teardown(struct dsa_switch *ds) if (!ds->setup) return; - if (ds->devlink) - devlink_unregister(ds->devlink); + devlink_unregister(ds->devlink); if (ds->slave_mii_bus && ds->ops->phy_read) { mdiobus_unregister(ds->slave_mii_bus); @@ -721,10 +721,8 @@ static void dsa_switch_teardown(struct dsa_switch *ds) dsa_switch_unregister_notifier(ds); - if (ds->devlink) { - devlink_free(ds->devlink); - ds->devlink = NULL; - } + devlink_free(ds->devlink); + ds->devlink = NULL; ds->setup = false; } From 7aea535d40eaba97cb116ba2b30ecd897eee0f43 Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Mon, 21 Nov 2022 15:55:43 +0200 Subject: [PATCH 1162/2002] net: dsa: move rest of devlink setup/teardown to devlink.c The code that needed further refactoring into dedicated functions in dsa2.c was left aside. Move it now to devlink.c, and make dsa2.c stop including net/devlink.h. Signed-off-by: Vladimir Oltean Reviewed-by: Florian Fainelli Signed-off-by: Jakub Kicinski --- net/dsa/devlink.c | 38 +++++++++++++++++++++++++++++++++++++- net/dsa/devlink.h | 7 +++++-- net/dsa/dsa2.c | 24 +++++++----------------- 3 files changed, 49 insertions(+), 20 deletions(-) diff --git a/net/dsa/devlink.c b/net/dsa/devlink.c index eff440b2b3c5..431bf52290a1 100644 --- a/net/dsa/devlink.c +++ b/net/dsa/devlink.c @@ -167,7 +167,7 @@ dsa_devlink_sb_occ_tc_port_bind_get(struct devlink_port *dlp, p_max); } -const struct devlink_ops dsa_devlink_ops = { +static const struct devlink_ops dsa_devlink_ops = { .info_get = dsa_devlink_info_get, .sb_pool_get = dsa_devlink_sb_pool_get, .sb_pool_set = dsa_devlink_sb_pool_set, @@ -353,3 +353,39 @@ void dsa_port_devlink_teardown(struct dsa_port *dp) devlink_port_fini(dlp); } + +void dsa_switch_devlink_register(struct dsa_switch *ds) +{ + devlink_register(ds->devlink); +} + +void dsa_switch_devlink_unregister(struct dsa_switch *ds) +{ + devlink_unregister(ds->devlink); +} + +int dsa_switch_devlink_alloc(struct dsa_switch *ds) +{ + struct dsa_devlink_priv *dl_priv; + struct devlink *dl; + + /* Add the switch to devlink before calling setup, so that setup can + * add dpipe tables + */ + dl = devlink_alloc(&dsa_devlink_ops, sizeof(*dl_priv), ds->dev); + if (!dl) + return -ENOMEM; + + ds->devlink = dl; + + dl_priv = devlink_priv(ds->devlink); + dl_priv->ds = ds; + + return 0; +} + +void dsa_switch_devlink_free(struct dsa_switch *ds) +{ + devlink_free(ds->devlink); + ds->devlink = NULL; +} diff --git a/net/dsa/devlink.h b/net/dsa/devlink.h index d077c7f336da..4d9f4f23705b 100644 --- a/net/dsa/devlink.h +++ b/net/dsa/devlink.h @@ -4,10 +4,13 @@ #define __DSA_DEVLINK_H struct dsa_port; - -extern const struct devlink_ops dsa_devlink_ops; +struct dsa_switch; int dsa_port_devlink_setup(struct dsa_port *dp); void dsa_port_devlink_teardown(struct dsa_port *dp); +void dsa_switch_devlink_register(struct dsa_switch *ds); +void dsa_switch_devlink_unregister(struct dsa_switch *ds); +int dsa_switch_devlink_alloc(struct dsa_switch *ds); +void dsa_switch_devlink_free(struct dsa_switch *ds); #endif diff --git a/net/dsa/dsa2.c b/net/dsa/dsa2.c index f890dfcbf412..c0ef49d86381 100644 --- a/net/dsa/dsa2.c +++ b/net/dsa/dsa2.c @@ -15,7 +15,6 @@ #include #include #include -#include #include #include "devlink.h" @@ -627,7 +626,6 @@ static void dsa_switch_teardown_tag_protocol(struct dsa_switch *ds) static int dsa_switch_setup(struct dsa_switch *ds) { - struct dsa_devlink_priv *dl_priv; struct device_node *dn; int err; @@ -641,15 +639,9 @@ static int dsa_switch_setup(struct dsa_switch *ds) */ ds->phys_mii_mask |= dsa_user_ports(ds); - /* Add the switch to devlink before calling setup, so that setup can - * add dpipe tables - */ - ds->devlink = - devlink_alloc(&dsa_devlink_ops, sizeof(*dl_priv), ds->dev); - if (!ds->devlink) - return -ENOMEM; - dl_priv = devlink_priv(ds->devlink); - dl_priv->ds = ds; + err = dsa_switch_devlink_alloc(ds); + if (err) + return err; err = dsa_switch_register_notifier(ds); if (err) @@ -682,7 +674,7 @@ static int dsa_switch_setup(struct dsa_switch *ds) goto free_slave_mii_bus; } - devlink_register(ds->devlink); + dsa_switch_devlink_register(ds); ds->setup = true; return 0; @@ -696,8 +688,7 @@ teardown: unregister_notifier: dsa_switch_unregister_notifier(ds); devlink_free: - devlink_free(ds->devlink); - ds->devlink = NULL; + dsa_switch_devlink_free(ds); return err; } @@ -706,7 +697,7 @@ static void dsa_switch_teardown(struct dsa_switch *ds) if (!ds->setup) return; - devlink_unregister(ds->devlink); + dsa_switch_devlink_unregister(ds); if (ds->slave_mii_bus && ds->ops->phy_read) { mdiobus_unregister(ds->slave_mii_bus); @@ -721,8 +712,7 @@ static void dsa_switch_teardown(struct dsa_switch *ds) dsa_switch_unregister_notifier(ds); - devlink_free(ds->devlink); - ds->devlink = NULL; + dsa_switch_devlink_free(ds); ds->setup = false; } From 022bba63c3ca02fc074c68b4e7b949bddcf320d6 Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Mon, 21 Nov 2022 15:55:44 +0200 Subject: [PATCH 1163/2002] net: dsa: move headers exported by port.c to port.h Minimize the use of the bloated dsa_priv.h by moving the prototypes exported by port.c to their own header file. Signed-off-by: Vladimir Oltean Reviewed-by: Florian Fainelli Signed-off-by: Jakub Kicinski --- net/dsa/dsa2.c | 1 + net/dsa/dsa_priv.h | 97 ------------------------------------- net/dsa/master.c | 1 + net/dsa/port.c | 1 + net/dsa/port.h | 114 ++++++++++++++++++++++++++++++++++++++++++++ net/dsa/slave.c | 1 + net/dsa/switch.c | 1 + net/dsa/tag_8021q.c | 1 + 8 files changed, 120 insertions(+), 97 deletions(-) create mode 100644 net/dsa/port.h diff --git a/net/dsa/dsa2.c b/net/dsa/dsa2.c index c0ef49d86381..5a9cf74a0166 100644 --- a/net/dsa/dsa2.c +++ b/net/dsa/dsa2.c @@ -19,6 +19,7 @@ #include "devlink.h" #include "dsa_priv.h" +#include "port.h" static DEFINE_MUTEX(dsa2_mutex); LIST_HEAD(dsa_tree_list); diff --git a/net/dsa/dsa_priv.h b/net/dsa/dsa_priv.h index c4ea5fda8f14..81ddc52feb94 100644 --- a/net/dsa/dsa_priv.h +++ b/net/dsa/dsa_priv.h @@ -286,103 +286,6 @@ static inline struct net_device *dsa_master_find_slave(struct net_device *dev, /* netlink.c */ extern struct rtnl_link_ops dsa_link_ops __read_mostly; -/* port.c */ -bool dsa_port_supports_hwtstamp(struct dsa_port *dp, struct ifreq *ifr); -void dsa_port_set_tag_protocol(struct dsa_port *cpu_dp, - const struct dsa_device_ops *tag_ops); -int dsa_port_set_state(struct dsa_port *dp, u8 state, bool do_fast_age); -int dsa_port_set_mst_state(struct dsa_port *dp, - const struct switchdev_mst_state *state, - struct netlink_ext_ack *extack); -int dsa_port_enable_rt(struct dsa_port *dp, struct phy_device *phy); -int dsa_port_enable(struct dsa_port *dp, struct phy_device *phy); -void dsa_port_disable_rt(struct dsa_port *dp); -void dsa_port_disable(struct dsa_port *dp); -int dsa_port_bridge_join(struct dsa_port *dp, struct net_device *br, - struct netlink_ext_ack *extack); -void dsa_port_pre_bridge_leave(struct dsa_port *dp, struct net_device *br); -void dsa_port_bridge_leave(struct dsa_port *dp, struct net_device *br); -int dsa_port_lag_change(struct dsa_port *dp, - struct netdev_lag_lower_state_info *linfo); -int dsa_port_lag_join(struct dsa_port *dp, struct net_device *lag_dev, - struct netdev_lag_upper_info *uinfo, - struct netlink_ext_ack *extack); -void dsa_port_pre_lag_leave(struct dsa_port *dp, struct net_device *lag_dev); -void dsa_port_lag_leave(struct dsa_port *dp, struct net_device *lag_dev); -int dsa_port_vlan_filtering(struct dsa_port *dp, bool vlan_filtering, - struct netlink_ext_ack *extack); -bool dsa_port_skip_vlan_configuration(struct dsa_port *dp); -int dsa_port_ageing_time(struct dsa_port *dp, clock_t ageing_clock); -int dsa_port_mst_enable(struct dsa_port *dp, bool on, - struct netlink_ext_ack *extack); -int dsa_port_vlan_msti(struct dsa_port *dp, - const struct switchdev_vlan_msti *msti); -int dsa_port_mtu_change(struct dsa_port *dp, int new_mtu); -int dsa_port_fdb_add(struct dsa_port *dp, const unsigned char *addr, - u16 vid); -int dsa_port_fdb_del(struct dsa_port *dp, const unsigned char *addr, - u16 vid); -int dsa_port_standalone_host_fdb_add(struct dsa_port *dp, - const unsigned char *addr, u16 vid); -int dsa_port_standalone_host_fdb_del(struct dsa_port *dp, - const unsigned char *addr, u16 vid); -int dsa_port_bridge_host_fdb_add(struct dsa_port *dp, const unsigned char *addr, - u16 vid); -int dsa_port_bridge_host_fdb_del(struct dsa_port *dp, const unsigned char *addr, - u16 vid); -int dsa_port_lag_fdb_add(struct dsa_port *dp, const unsigned char *addr, - u16 vid); -int dsa_port_lag_fdb_del(struct dsa_port *dp, const unsigned char *addr, - u16 vid); -int dsa_port_fdb_dump(struct dsa_port *dp, dsa_fdb_dump_cb_t *cb, void *data); -int dsa_port_mdb_add(const struct dsa_port *dp, - const struct switchdev_obj_port_mdb *mdb); -int dsa_port_mdb_del(const struct dsa_port *dp, - const struct switchdev_obj_port_mdb *mdb); -int dsa_port_standalone_host_mdb_add(const struct dsa_port *dp, - const struct switchdev_obj_port_mdb *mdb); -int dsa_port_standalone_host_mdb_del(const struct dsa_port *dp, - const struct switchdev_obj_port_mdb *mdb); -int dsa_port_bridge_host_mdb_add(const struct dsa_port *dp, - const struct switchdev_obj_port_mdb *mdb); -int dsa_port_bridge_host_mdb_del(const struct dsa_port *dp, - const struct switchdev_obj_port_mdb *mdb); -int dsa_port_pre_bridge_flags(const struct dsa_port *dp, - struct switchdev_brport_flags flags, - struct netlink_ext_ack *extack); -int dsa_port_bridge_flags(struct dsa_port *dp, - struct switchdev_brport_flags flags, - struct netlink_ext_ack *extack); -int dsa_port_vlan_add(struct dsa_port *dp, - const struct switchdev_obj_port_vlan *vlan, - struct netlink_ext_ack *extack); -int dsa_port_vlan_del(struct dsa_port *dp, - const struct switchdev_obj_port_vlan *vlan); -int dsa_port_host_vlan_add(struct dsa_port *dp, - const struct switchdev_obj_port_vlan *vlan, - struct netlink_ext_ack *extack); -int dsa_port_host_vlan_del(struct dsa_port *dp, - const struct switchdev_obj_port_vlan *vlan); -int dsa_port_mrp_add(const struct dsa_port *dp, - const struct switchdev_obj_mrp *mrp); -int dsa_port_mrp_del(const struct dsa_port *dp, - const struct switchdev_obj_mrp *mrp); -int dsa_port_mrp_add_ring_role(const struct dsa_port *dp, - const struct switchdev_obj_ring_role_mrp *mrp); -int dsa_port_mrp_del_ring_role(const struct dsa_port *dp, - const struct switchdev_obj_ring_role_mrp *mrp); -int dsa_port_phylink_create(struct dsa_port *dp); -void dsa_port_phylink_destroy(struct dsa_port *dp); -int dsa_shared_port_link_register_of(struct dsa_port *dp); -void dsa_shared_port_link_unregister_of(struct dsa_port *dp); -int dsa_port_hsr_join(struct dsa_port *dp, struct net_device *hsr); -void dsa_port_hsr_leave(struct dsa_port *dp, struct net_device *hsr); -int dsa_port_tag_8021q_vlan_add(struct dsa_port *dp, u16 vid, bool broadcast); -void dsa_port_tag_8021q_vlan_del(struct dsa_port *dp, u16 vid, bool broadcast); -void dsa_port_set_host_flood(struct dsa_port *dp, bool uc, bool mc); -int dsa_port_change_master(struct dsa_port *dp, struct net_device *master, - struct netlink_ext_ack *extack); - /* slave.c */ extern struct notifier_block dsa_slave_switchdev_notifier; extern struct notifier_block dsa_slave_switchdev_blocking_notifier; diff --git a/net/dsa/master.c b/net/dsa/master.c index e24f02743c21..0d3ef591b3b4 100644 --- a/net/dsa/master.c +++ b/net/dsa/master.c @@ -7,6 +7,7 @@ */ #include "dsa_priv.h" +#include "port.h" static int dsa_master_get_regs_len(struct net_device *dev) { diff --git a/net/dsa/port.c b/net/dsa/port.c index 707bd854cea2..0708fe8d4736 100644 --- a/net/dsa/port.c +++ b/net/dsa/port.c @@ -13,6 +13,7 @@ #include #include "dsa_priv.h" +#include "port.h" /** * dsa_port_notify - Notify the switching fabric of changes to a port diff --git a/net/dsa/port.h b/net/dsa/port.h new file mode 100644 index 000000000000..9c218660d223 --- /dev/null +++ b/net/dsa/port.h @@ -0,0 +1,114 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#ifndef __DSA_PORT_H +#define __DSA_PORT_H + +#include +#include + +struct ifreq; +struct netdev_lag_lower_state_info; +struct netdev_lag_upper_info; +struct netlink_ext_ack; +struct switchdev_mst_state; +struct switchdev_obj_port_mdb; +struct switchdev_vlan_msti; +struct phy_device; + +bool dsa_port_supports_hwtstamp(struct dsa_port *dp, struct ifreq *ifr); +void dsa_port_set_tag_protocol(struct dsa_port *cpu_dp, + const struct dsa_device_ops *tag_ops); +int dsa_port_set_state(struct dsa_port *dp, u8 state, bool do_fast_age); +int dsa_port_set_mst_state(struct dsa_port *dp, + const struct switchdev_mst_state *state, + struct netlink_ext_ack *extack); +int dsa_port_enable_rt(struct dsa_port *dp, struct phy_device *phy); +int dsa_port_enable(struct dsa_port *dp, struct phy_device *phy); +void dsa_port_disable_rt(struct dsa_port *dp); +void dsa_port_disable(struct dsa_port *dp); +int dsa_port_bridge_join(struct dsa_port *dp, struct net_device *br, + struct netlink_ext_ack *extack); +void dsa_port_pre_bridge_leave(struct dsa_port *dp, struct net_device *br); +void dsa_port_bridge_leave(struct dsa_port *dp, struct net_device *br); +int dsa_port_lag_change(struct dsa_port *dp, + struct netdev_lag_lower_state_info *linfo); +int dsa_port_lag_join(struct dsa_port *dp, struct net_device *lag_dev, + struct netdev_lag_upper_info *uinfo, + struct netlink_ext_ack *extack); +void dsa_port_pre_lag_leave(struct dsa_port *dp, struct net_device *lag_dev); +void dsa_port_lag_leave(struct dsa_port *dp, struct net_device *lag_dev); +int dsa_port_vlan_filtering(struct dsa_port *dp, bool vlan_filtering, + struct netlink_ext_ack *extack); +bool dsa_port_skip_vlan_configuration(struct dsa_port *dp); +int dsa_port_ageing_time(struct dsa_port *dp, clock_t ageing_clock); +int dsa_port_mst_enable(struct dsa_port *dp, bool on, + struct netlink_ext_ack *extack); +int dsa_port_vlan_msti(struct dsa_port *dp, + const struct switchdev_vlan_msti *msti); +int dsa_port_mtu_change(struct dsa_port *dp, int new_mtu); +int dsa_port_fdb_add(struct dsa_port *dp, const unsigned char *addr, + u16 vid); +int dsa_port_fdb_del(struct dsa_port *dp, const unsigned char *addr, + u16 vid); +int dsa_port_standalone_host_fdb_add(struct dsa_port *dp, + const unsigned char *addr, u16 vid); +int dsa_port_standalone_host_fdb_del(struct dsa_port *dp, + const unsigned char *addr, u16 vid); +int dsa_port_bridge_host_fdb_add(struct dsa_port *dp, const unsigned char *addr, + u16 vid); +int dsa_port_bridge_host_fdb_del(struct dsa_port *dp, const unsigned char *addr, + u16 vid); +int dsa_port_lag_fdb_add(struct dsa_port *dp, const unsigned char *addr, + u16 vid); +int dsa_port_lag_fdb_del(struct dsa_port *dp, const unsigned char *addr, + u16 vid); +int dsa_port_fdb_dump(struct dsa_port *dp, dsa_fdb_dump_cb_t *cb, void *data); +int dsa_port_mdb_add(const struct dsa_port *dp, + const struct switchdev_obj_port_mdb *mdb); +int dsa_port_mdb_del(const struct dsa_port *dp, + const struct switchdev_obj_port_mdb *mdb); +int dsa_port_standalone_host_mdb_add(const struct dsa_port *dp, + const struct switchdev_obj_port_mdb *mdb); +int dsa_port_standalone_host_mdb_del(const struct dsa_port *dp, + const struct switchdev_obj_port_mdb *mdb); +int dsa_port_bridge_host_mdb_add(const struct dsa_port *dp, + const struct switchdev_obj_port_mdb *mdb); +int dsa_port_bridge_host_mdb_del(const struct dsa_port *dp, + const struct switchdev_obj_port_mdb *mdb); +int dsa_port_pre_bridge_flags(const struct dsa_port *dp, + struct switchdev_brport_flags flags, + struct netlink_ext_ack *extack); +int dsa_port_bridge_flags(struct dsa_port *dp, + struct switchdev_brport_flags flags, + struct netlink_ext_ack *extack); +int dsa_port_vlan_add(struct dsa_port *dp, + const struct switchdev_obj_port_vlan *vlan, + struct netlink_ext_ack *extack); +int dsa_port_vlan_del(struct dsa_port *dp, + const struct switchdev_obj_port_vlan *vlan); +int dsa_port_host_vlan_add(struct dsa_port *dp, + const struct switchdev_obj_port_vlan *vlan, + struct netlink_ext_ack *extack); +int dsa_port_host_vlan_del(struct dsa_port *dp, + const struct switchdev_obj_port_vlan *vlan); +int dsa_port_mrp_add(const struct dsa_port *dp, + const struct switchdev_obj_mrp *mrp); +int dsa_port_mrp_del(const struct dsa_port *dp, + const struct switchdev_obj_mrp *mrp); +int dsa_port_mrp_add_ring_role(const struct dsa_port *dp, + const struct switchdev_obj_ring_role_mrp *mrp); +int dsa_port_mrp_del_ring_role(const struct dsa_port *dp, + const struct switchdev_obj_ring_role_mrp *mrp); +int dsa_port_phylink_create(struct dsa_port *dp); +void dsa_port_phylink_destroy(struct dsa_port *dp); +int dsa_shared_port_link_register_of(struct dsa_port *dp); +void dsa_shared_port_link_unregister_of(struct dsa_port *dp); +int dsa_port_hsr_join(struct dsa_port *dp, struct net_device *hsr); +void dsa_port_hsr_leave(struct dsa_port *dp, struct net_device *hsr); +int dsa_port_tag_8021q_vlan_add(struct dsa_port *dp, u16 vid, bool broadcast); +void dsa_port_tag_8021q_vlan_del(struct dsa_port *dp, u16 vid, bool broadcast); +void dsa_port_set_host_flood(struct dsa_port *dp, bool uc, bool mc); +int dsa_port_change_master(struct dsa_port *dp, struct net_device *master, + struct netlink_ext_ack *extack); + +#endif diff --git a/net/dsa/slave.c b/net/dsa/slave.c index 24d8ad36fc8b..b782a1788f5a 100644 --- a/net/dsa/slave.c +++ b/net/dsa/slave.c @@ -23,6 +23,7 @@ #include #include "dsa_priv.h" +#include "port.h" static void dsa_slave_standalone_event_work(struct work_struct *work) { diff --git a/net/dsa/switch.c b/net/dsa/switch.c index ce56acdba203..5ece5c5c2acf 100644 --- a/net/dsa/switch.c +++ b/net/dsa/switch.c @@ -13,6 +13,7 @@ #include #include "dsa_priv.h" +#include "port.h" static unsigned int dsa_switch_fastest_ageing_time(struct dsa_switch *ds, unsigned int ageing_time) diff --git a/net/dsa/tag_8021q.c b/net/dsa/tag_8021q.c index 34e5ec5d3e23..a6617d7b692a 100644 --- a/net/dsa/tag_8021q.c +++ b/net/dsa/tag_8021q.c @@ -8,6 +8,7 @@ #include #include "dsa_priv.h" +#include "port.h" /* Binary structure of the fake 12-bit VID field (when the TPID is * ETH_P_DSA_8021Q): From 94ef6fad3bf317b43cdc59ba171dff2486e59975 Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Mon, 21 Nov 2022 15:55:45 +0200 Subject: [PATCH 1164/2002] net: dsa: move headers exported by master.c to master.h Minimize the use of the bloated dsa_priv.h by moving the prototypes exported by master.c to their own header file. Signed-off-by: Vladimir Oltean Reviewed-by: Florian Fainelli Signed-off-by: Jakub Kicinski --- net/dsa/dsa2.c | 1 + net/dsa/dsa_priv.h | 9 --------- net/dsa/master.c | 6 ++++++ net/dsa/master.h | 19 +++++++++++++++++++ net/dsa/slave.c | 1 + 5 files changed, 27 insertions(+), 9 deletions(-) create mode 100644 net/dsa/master.h diff --git a/net/dsa/dsa2.c b/net/dsa/dsa2.c index 5a9cf74a0166..10cd4ea9afe1 100644 --- a/net/dsa/dsa2.c +++ b/net/dsa/dsa2.c @@ -19,6 +19,7 @@ #include "devlink.h" #include "dsa_priv.h" +#include "master.h" #include "port.h" static DEFINE_MUTEX(dsa2_mutex); diff --git a/net/dsa/dsa_priv.h b/net/dsa/dsa_priv.h index 81ddc52feb94..94e385ec6da5 100644 --- a/net/dsa/dsa_priv.h +++ b/net/dsa/dsa_priv.h @@ -259,15 +259,6 @@ static inline int dsa_tag_protocol_overhead(const struct dsa_device_ops *ops) return ops->needed_headroom + ops->needed_tailroom; } -/* master.c */ -int dsa_master_setup(struct net_device *dev, struct dsa_port *cpu_dp); -void dsa_master_teardown(struct net_device *dev); -int dsa_master_lag_setup(struct net_device *lag_dev, struct dsa_port *cpu_dp, - struct netdev_lag_upper_info *uinfo, - struct netlink_ext_ack *extack); -void dsa_master_lag_teardown(struct net_device *lag_dev, - struct dsa_port *cpu_dp); - static inline struct net_device *dsa_master_find_slave(struct net_device *dev, int device, int port) { diff --git a/net/dsa/master.c b/net/dsa/master.c index 0d3ef591b3b4..6105821834a2 100644 --- a/net/dsa/master.c +++ b/net/dsa/master.c @@ -6,7 +6,13 @@ * Vivien Didelot */ +#include +#include +#include +#include + #include "dsa_priv.h" +#include "master.h" #include "port.h" static int dsa_master_get_regs_len(struct net_device *dev) diff --git a/net/dsa/master.h b/net/dsa/master.h new file mode 100644 index 000000000000..3fc0e610b5b5 --- /dev/null +++ b/net/dsa/master.h @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#ifndef __DSA_MASTER_H +#define __DSA_MASTER_H + +struct dsa_port; +struct net_device; +struct netdev_lag_upper_info; +struct netlink_ext_ack; + +int dsa_master_setup(struct net_device *dev, struct dsa_port *cpu_dp); +void dsa_master_teardown(struct net_device *dev); +int dsa_master_lag_setup(struct net_device *lag_dev, struct dsa_port *cpu_dp, + struct netdev_lag_upper_info *uinfo, + struct netlink_ext_ack *extack); +void dsa_master_lag_teardown(struct net_device *lag_dev, + struct dsa_port *cpu_dp); + +#endif diff --git a/net/dsa/slave.c b/net/dsa/slave.c index b782a1788f5a..523f9ebeb45b 100644 --- a/net/dsa/slave.c +++ b/net/dsa/slave.c @@ -24,6 +24,7 @@ #include "dsa_priv.h" #include "port.h" +#include "master.h" static void dsa_slave_standalone_event_work(struct work_struct *work) { From 09f92341681a23346c456938bcb2670de2cd99d4 Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Mon, 21 Nov 2022 15:55:46 +0200 Subject: [PATCH 1165/2002] net: dsa: move headers exported by slave.c to slave.h Minimize the use of the bloated dsa_priv.h by moving the prototypes exported by slave.c to their own header file. This is just approximate to get the code structure right. There are some interdependencies with static inline code left in dsa_priv.h, so leave slave.h included from there for now. Signed-off-by: Vladimir Oltean Reviewed-by: Florian Fainelli Signed-off-by: Jakub Kicinski --- net/dsa/dsa.c | 1 + net/dsa/dsa2.c | 1 + net/dsa/dsa_priv.h | 59 ++------------------------------------- net/dsa/netlink.c | 1 + net/dsa/port.c | 1 + net/dsa/slave.c | 1 + net/dsa/slave.h | 69 ++++++++++++++++++++++++++++++++++++++++++++++ net/dsa/switch.c | 1 + 8 files changed, 77 insertions(+), 57 deletions(-) create mode 100644 net/dsa/slave.h diff --git a/net/dsa/dsa.c b/net/dsa/dsa.c index 842a1f2488b2..422f8853d1c4 100644 --- a/net/dsa/dsa.c +++ b/net/dsa/dsa.c @@ -14,6 +14,7 @@ #include #include "dsa_priv.h" +#include "slave.h" static LIST_HEAD(dsa_tag_drivers_list); static DEFINE_MUTEX(dsa_tag_drivers_lock); diff --git a/net/dsa/dsa2.c b/net/dsa/dsa2.c index 10cd4ea9afe1..f917e695d38c 100644 --- a/net/dsa/dsa2.c +++ b/net/dsa/dsa2.c @@ -21,6 +21,7 @@ #include "dsa_priv.h" #include "master.h" #include "port.h" +#include "slave.h" static DEFINE_MUTEX(dsa2_mutex); LIST_HEAD(dsa_tree_list); diff --git a/net/dsa/dsa_priv.h b/net/dsa/dsa_priv.h index 94e385ec6da5..fcff35b15dd4 100644 --- a/net/dsa/dsa_priv.h +++ b/net/dsa/dsa_priv.h @@ -7,13 +7,11 @@ #ifndef __DSA_PRIV_H #define __DSA_PRIV_H -#include -#include #include #include -#include #include -#include + +#include "slave.h" #define DSA_MAX_NUM_OFFLOADING_BRIDGES BITS_PER_LONG @@ -224,24 +222,6 @@ struct dsa_standalone_event_work { u16 vid; }; -struct dsa_slave_priv { - /* Copy of CPU port xmit for faster access in slave transmit hot path */ - struct sk_buff * (*xmit)(struct sk_buff *skb, - struct net_device *dev); - - struct gro_cells gcells; - - /* DSA port data, such as switch, port index, etc. */ - struct dsa_port *dp; - -#ifdef CONFIG_NET_POLL_CONTROLLER - struct netpoll *netpoll; -#endif - - /* TC context */ - struct list_head mall_tc_list; -}; - /* dsa.c */ const struct dsa_device_ops *dsa_tag_driver_get_by_id(int tag_protocol); const struct dsa_device_ops *dsa_tag_driver_get_by_name(const char *name); @@ -277,41 +257,6 @@ static inline struct net_device *dsa_master_find_slave(struct net_device *dev, /* netlink.c */ extern struct rtnl_link_ops dsa_link_ops __read_mostly; -/* slave.c */ -extern struct notifier_block dsa_slave_switchdev_notifier; -extern struct notifier_block dsa_slave_switchdev_blocking_notifier; - -void dsa_slave_mii_bus_init(struct dsa_switch *ds); -int dsa_slave_create(struct dsa_port *dp); -void dsa_slave_destroy(struct net_device *slave_dev); -int dsa_slave_suspend(struct net_device *slave_dev); -int dsa_slave_resume(struct net_device *slave_dev); -int dsa_slave_register_notifier(void); -void dsa_slave_unregister_notifier(void); -void dsa_slave_sync_ha(struct net_device *dev); -void dsa_slave_unsync_ha(struct net_device *dev); -void dsa_slave_setup_tagger(struct net_device *slave); -int dsa_slave_change_mtu(struct net_device *dev, int new_mtu); -int dsa_slave_change_master(struct net_device *dev, struct net_device *master, - struct netlink_ext_ack *extack); -int dsa_slave_manage_vlan_filtering(struct net_device *dev, - bool vlan_filtering); - -static inline struct dsa_port *dsa_slave_to_port(const struct net_device *dev) -{ - struct dsa_slave_priv *p = netdev_priv(dev); - - return p->dp; -} - -static inline struct net_device * -dsa_slave_to_master(const struct net_device *dev) -{ - struct dsa_port *dp = dsa_slave_to_port(dev); - - return dsa_port_to_master(dp); -} - /* If under a bridge with vlan_filtering=0, make sure to send pvid-tagged * frames as untagged, since the bridge will not untag them. */ diff --git a/net/dsa/netlink.c b/net/dsa/netlink.c index ecf9ed1de185..824b09d904cc 100644 --- a/net/dsa/netlink.c +++ b/net/dsa/netlink.c @@ -5,6 +5,7 @@ #include #include "dsa_priv.h" +#include "slave.h" static const struct nla_policy dsa_policy[IFLA_DSA_MAX + 1] = { [IFLA_DSA_MASTER] = { .type = NLA_U32 }, diff --git a/net/dsa/port.c b/net/dsa/port.c index 0708fe8d4736..56728242f079 100644 --- a/net/dsa/port.c +++ b/net/dsa/port.c @@ -14,6 +14,7 @@ #include "dsa_priv.h" #include "port.h" +#include "slave.h" /** * dsa_port_notify - Notify the switching fabric of changes to a port diff --git a/net/dsa/slave.c b/net/dsa/slave.c index 523f9ebeb45b..2cf83892072f 100644 --- a/net/dsa/slave.c +++ b/net/dsa/slave.c @@ -25,6 +25,7 @@ #include "dsa_priv.h" #include "port.h" #include "master.h" +#include "slave.h" static void dsa_slave_standalone_event_work(struct work_struct *work) { diff --git a/net/dsa/slave.h b/net/dsa/slave.h new file mode 100644 index 000000000000..d0abe609e00d --- /dev/null +++ b/net/dsa/slave.h @@ -0,0 +1,69 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#ifndef __DSA_SLAVE_H +#define __DSA_SLAVE_H + +#include +#include +#include +#include +#include +#include +#include + +struct net_device; +struct netlink_ext_ack; + +extern struct notifier_block dsa_slave_switchdev_notifier; +extern struct notifier_block dsa_slave_switchdev_blocking_notifier; + +struct dsa_slave_priv { + /* Copy of CPU port xmit for faster access in slave transmit hot path */ + struct sk_buff * (*xmit)(struct sk_buff *skb, + struct net_device *dev); + + struct gro_cells gcells; + + /* DSA port data, such as switch, port index, etc. */ + struct dsa_port *dp; + +#ifdef CONFIG_NET_POLL_CONTROLLER + struct netpoll *netpoll; +#endif + + /* TC context */ + struct list_head mall_tc_list; +}; + +void dsa_slave_mii_bus_init(struct dsa_switch *ds); +int dsa_slave_create(struct dsa_port *dp); +void dsa_slave_destroy(struct net_device *slave_dev); +int dsa_slave_suspend(struct net_device *slave_dev); +int dsa_slave_resume(struct net_device *slave_dev); +int dsa_slave_register_notifier(void); +void dsa_slave_unregister_notifier(void); +void dsa_slave_sync_ha(struct net_device *dev); +void dsa_slave_unsync_ha(struct net_device *dev); +void dsa_slave_setup_tagger(struct net_device *slave); +int dsa_slave_change_mtu(struct net_device *dev, int new_mtu); +int dsa_slave_change_master(struct net_device *dev, struct net_device *master, + struct netlink_ext_ack *extack); +int dsa_slave_manage_vlan_filtering(struct net_device *dev, + bool vlan_filtering); + +static inline struct dsa_port *dsa_slave_to_port(const struct net_device *dev) +{ + struct dsa_slave_priv *p = netdev_priv(dev); + + return p->dp; +} + +static inline struct net_device * +dsa_slave_to_master(const struct net_device *dev) +{ + struct dsa_port *dp = dsa_slave_to_port(dev); + + return dsa_port_to_master(dp); +} + +#endif diff --git a/net/dsa/switch.c b/net/dsa/switch.c index 5ece5c5c2acf..d0d5a1c7e6f6 100644 --- a/net/dsa/switch.c +++ b/net/dsa/switch.c @@ -14,6 +14,7 @@ #include "dsa_priv.h" #include "port.h" +#include "slave.h" static unsigned int dsa_switch_fastest_ageing_time(struct dsa_switch *ds, unsigned int ageing_time) From bd954b826032e7bd6be8a53e30eb81c1b348aef6 Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Mon, 21 Nov 2022 15:55:47 +0200 Subject: [PATCH 1166/2002] net: dsa: move tagging protocol code to tag.{c,h} It would be nice if tagging protocol drivers could include just the header they need, since they are (mostly) data path and isolated from most of the other DSA core code does. Create a tag.c and a tag.h file which are meant to support tagging protocol drivers. Signed-off-by: Vladimir Oltean Reviewed-by: Florian Fainelli Signed-off-by: Jakub Kicinski --- net/dsa/Makefile | 1 + net/dsa/dsa.c | 229 +-------------------------- net/dsa/dsa2.c | 1 + net/dsa/dsa_priv.h | 296 ----------------------------------- net/dsa/master.c | 1 + net/dsa/slave.c | 1 + net/dsa/tag.c | 243 +++++++++++++++++++++++++++++ net/dsa/tag.h | 310 +++++++++++++++++++++++++++++++++++++ net/dsa/tag_8021q.c | 1 + net/dsa/tag_ar9331.c | 2 +- net/dsa/tag_brcm.c | 2 +- net/dsa/tag_dsa.c | 2 +- net/dsa/tag_gswip.c | 2 +- net/dsa/tag_hellcreek.c | 1 + net/dsa/tag_ksz.c | 3 +- net/dsa/tag_lan9303.c | 2 +- net/dsa/tag_mtk.c | 2 +- net/dsa/tag_none.c | 2 +- net/dsa/tag_ocelot.c | 3 +- net/dsa/tag_ocelot_8021q.c | 3 +- net/dsa/tag_qca.c | 2 +- net/dsa/tag_rtl4_a.c | 2 +- net/dsa/tag_rtl8_4.c | 2 +- net/dsa/tag_rzn1_a5psw.c | 2 +- net/dsa/tag_sja1105.c | 3 +- net/dsa/tag_trailer.c | 2 +- net/dsa/tag_xrs700x.c | 2 +- 27 files changed, 581 insertions(+), 541 deletions(-) create mode 100644 net/dsa/tag.c create mode 100644 net/dsa/tag.h diff --git a/net/dsa/Makefile b/net/dsa/Makefile index bc872c0d7011..93f5d5f1e495 100644 --- a/net/dsa/Makefile +++ b/net/dsa/Makefile @@ -10,6 +10,7 @@ dsa_core-y += \ port.o \ slave.o \ switch.o \ + tag.o \ tag_8021q.o # tagging formats diff --git a/net/dsa/dsa.c b/net/dsa/dsa.c index 422f8853d1c4..6f87dd1ee6bf 100644 --- a/net/dsa/dsa.c +++ b/net/dsa/dsa.c @@ -10,127 +10,10 @@ #include #include #include -#include -#include #include "dsa_priv.h" #include "slave.h" - -static LIST_HEAD(dsa_tag_drivers_list); -static DEFINE_MUTEX(dsa_tag_drivers_lock); - -static void dsa_tag_driver_register(struct dsa_tag_driver *dsa_tag_driver, - struct module *owner) -{ - dsa_tag_driver->owner = owner; - - mutex_lock(&dsa_tag_drivers_lock); - list_add_tail(&dsa_tag_driver->list, &dsa_tag_drivers_list); - mutex_unlock(&dsa_tag_drivers_lock); -} - -void dsa_tag_drivers_register(struct dsa_tag_driver *dsa_tag_driver_array[], - unsigned int count, struct module *owner) -{ - unsigned int i; - - for (i = 0; i < count; i++) - dsa_tag_driver_register(dsa_tag_driver_array[i], owner); -} - -static void dsa_tag_driver_unregister(struct dsa_tag_driver *dsa_tag_driver) -{ - mutex_lock(&dsa_tag_drivers_lock); - list_del(&dsa_tag_driver->list); - mutex_unlock(&dsa_tag_drivers_lock); -} -EXPORT_SYMBOL_GPL(dsa_tag_drivers_register); - -void dsa_tag_drivers_unregister(struct dsa_tag_driver *dsa_tag_driver_array[], - unsigned int count) -{ - unsigned int i; - - for (i = 0; i < count; i++) - dsa_tag_driver_unregister(dsa_tag_driver_array[i]); -} -EXPORT_SYMBOL_GPL(dsa_tag_drivers_unregister); - -const char *dsa_tag_protocol_to_str(const struct dsa_device_ops *ops) -{ - return ops->name; -}; - -/* Function takes a reference on the module owning the tagger, - * so dsa_tag_driver_put must be called afterwards. - */ -const struct dsa_device_ops *dsa_tag_driver_get_by_name(const char *name) -{ - const struct dsa_device_ops *ops = ERR_PTR(-ENOPROTOOPT); - struct dsa_tag_driver *dsa_tag_driver; - - request_module("%s%s", DSA_TAG_DRIVER_ALIAS, name); - - mutex_lock(&dsa_tag_drivers_lock); - list_for_each_entry(dsa_tag_driver, &dsa_tag_drivers_list, list) { - const struct dsa_device_ops *tmp = dsa_tag_driver->ops; - - if (strcmp(name, tmp->name)) - continue; - - if (!try_module_get(dsa_tag_driver->owner)) - break; - - ops = tmp; - break; - } - mutex_unlock(&dsa_tag_drivers_lock); - - return ops; -} - -const struct dsa_device_ops *dsa_tag_driver_get_by_id(int tag_protocol) -{ - struct dsa_tag_driver *dsa_tag_driver; - const struct dsa_device_ops *ops; - bool found = false; - - request_module("%sid-%d", DSA_TAG_DRIVER_ALIAS, tag_protocol); - - mutex_lock(&dsa_tag_drivers_lock); - list_for_each_entry(dsa_tag_driver, &dsa_tag_drivers_list, list) { - ops = dsa_tag_driver->ops; - if (ops->proto == tag_protocol) { - found = true; - break; - } - } - - if (found) { - if (!try_module_get(dsa_tag_driver->owner)) - ops = ERR_PTR(-ENOPROTOOPT); - } else { - ops = ERR_PTR(-ENOPROTOOPT); - } - - mutex_unlock(&dsa_tag_drivers_lock); - - return ops; -} - -void dsa_tag_driver_put(const struct dsa_device_ops *ops) -{ - struct dsa_tag_driver *dsa_tag_driver; - - mutex_lock(&dsa_tag_drivers_lock); - list_for_each_entry(dsa_tag_driver, &dsa_tag_drivers_list, list) { - if (dsa_tag_driver->ops == ops) { - module_put(dsa_tag_driver->owner); - break; - } - } - mutex_unlock(&dsa_tag_drivers_lock); -} +#include "tag.h" static int dev_is_class(struct device *dev, void *class) { @@ -168,111 +51,6 @@ struct net_device *dsa_dev_to_net_device(struct device *dev) return NULL; } -/* Determine if we should defer delivery of skb until we have a rx timestamp. - * - * Called from dsa_switch_rcv. For now, this will only work if tagging is - * enabled on the switch. Normally the MAC driver would retrieve the hardware - * timestamp when it reads the packet out of the hardware. However in a DSA - * switch, the DSA driver owning the interface to which the packet is - * delivered is never notified unless we do so here. - */ -static bool dsa_skb_defer_rx_timestamp(struct dsa_slave_priv *p, - struct sk_buff *skb) -{ - struct dsa_switch *ds = p->dp->ds; - unsigned int type; - - if (skb_headroom(skb) < ETH_HLEN) - return false; - - __skb_push(skb, ETH_HLEN); - - type = ptp_classify_raw(skb); - - __skb_pull(skb, ETH_HLEN); - - if (type == PTP_CLASS_NONE) - return false; - - if (likely(ds->ops->port_rxtstamp)) - return ds->ops->port_rxtstamp(ds, p->dp->index, skb, type); - - return false; -} - -static int dsa_switch_rcv(struct sk_buff *skb, struct net_device *dev, - struct packet_type *pt, struct net_device *unused) -{ - struct metadata_dst *md_dst = skb_metadata_dst(skb); - struct dsa_port *cpu_dp = dev->dsa_ptr; - struct sk_buff *nskb = NULL; - struct dsa_slave_priv *p; - - if (unlikely(!cpu_dp)) { - kfree_skb(skb); - return 0; - } - - skb = skb_unshare(skb, GFP_ATOMIC); - if (!skb) - return 0; - - if (md_dst && md_dst->type == METADATA_HW_PORT_MUX) { - unsigned int port = md_dst->u.port_info.port_id; - - skb_dst_drop(skb); - if (!skb_has_extensions(skb)) - skb->slow_gro = 0; - - skb->dev = dsa_master_find_slave(dev, 0, port); - if (likely(skb->dev)) { - dsa_default_offload_fwd_mark(skb); - nskb = skb; - } - } else { - nskb = cpu_dp->rcv(skb, dev); - } - - if (!nskb) { - kfree_skb(skb); - return 0; - } - - skb = nskb; - skb_push(skb, ETH_HLEN); - skb->pkt_type = PACKET_HOST; - skb->protocol = eth_type_trans(skb, skb->dev); - - if (unlikely(!dsa_slave_dev_check(skb->dev))) { - /* Packet is to be injected directly on an upper - * device, e.g. a team/bond, so skip all DSA-port - * specific actions. - */ - netif_rx(skb); - return 0; - } - - p = netdev_priv(skb->dev); - - if (unlikely(cpu_dp->ds->untag_bridge_pvid)) { - nskb = dsa_untag_bridge_pvid(skb); - if (!nskb) { - kfree_skb(skb); - return 0; - } - skb = nskb; - } - - dev_sw_netstats_rx_add(skb->dev, skb->len); - - if (dsa_skb_defer_rx_timestamp(p, skb)) - return 0; - - gro_cells_receive(&p->gcells, skb); - - return 0; -} - #ifdef CONFIG_PM_SLEEP static bool dsa_port_is_initialized(const struct dsa_port *dp) { @@ -327,11 +105,6 @@ int dsa_switch_resume(struct dsa_switch *ds) EXPORT_SYMBOL_GPL(dsa_switch_resume); #endif -static struct packet_type dsa_pack_type __read_mostly = { - .type = cpu_to_be16(ETH_P_XDSA), - .func = dsa_switch_rcv, -}; - static struct workqueue_struct *dsa_owq; bool dsa_schedule_work(struct work_struct *work) diff --git a/net/dsa/dsa2.c b/net/dsa/dsa2.c index f917e695d38c..5373edf45a62 100644 --- a/net/dsa/dsa2.c +++ b/net/dsa/dsa2.c @@ -22,6 +22,7 @@ #include "master.h" #include "port.h" #include "slave.h" +#include "tag.h" static DEFINE_MUTEX(dsa2_mutex); LIST_HEAD(dsa_tree_list); diff --git a/net/dsa/dsa_priv.h b/net/dsa/dsa_priv.h index fcff35b15dd4..eee2c9729e32 100644 --- a/net/dsa/dsa_priv.h +++ b/net/dsa/dsa_priv.h @@ -11,85 +11,8 @@ #include #include -#include "slave.h" - #define DSA_MAX_NUM_OFFLOADING_BRIDGES BITS_PER_LONG -/* Create 2 modaliases per tagging protocol, one to auto-load the module - * given the ID reported by get_tag_protocol(), and the other by name. - */ -#define DSA_TAG_DRIVER_ALIAS "dsa_tag:" -#define MODULE_ALIAS_DSA_TAG_DRIVER(__proto, __name) \ - MODULE_ALIAS(DSA_TAG_DRIVER_ALIAS __name); \ - MODULE_ALIAS(DSA_TAG_DRIVER_ALIAS "id-" \ - __stringify(__proto##_VALUE)) - -struct dsa_tag_driver { - const struct dsa_device_ops *ops; - struct list_head list; - struct module *owner; -}; - -void dsa_tag_drivers_register(struct dsa_tag_driver *dsa_tag_driver_array[], - unsigned int count, - struct module *owner); -void dsa_tag_drivers_unregister(struct dsa_tag_driver *dsa_tag_driver_array[], - unsigned int count); - -#define dsa_tag_driver_module_drivers(__dsa_tag_drivers_array, __count) \ -static int __init dsa_tag_driver_module_init(void) \ -{ \ - dsa_tag_drivers_register(__dsa_tag_drivers_array, __count, \ - THIS_MODULE); \ - return 0; \ -} \ -module_init(dsa_tag_driver_module_init); \ - \ -static void __exit dsa_tag_driver_module_exit(void) \ -{ \ - dsa_tag_drivers_unregister(__dsa_tag_drivers_array, __count); \ -} \ -module_exit(dsa_tag_driver_module_exit) - -/** - * module_dsa_tag_drivers() - Helper macro for registering DSA tag - * drivers - * @__ops_array: Array of tag driver structures - * - * Helper macro for DSA tag drivers which do not do anything special - * in module init/exit. Each module may only use this macro once, and - * calling it replaces module_init() and module_exit(). - */ -#define module_dsa_tag_drivers(__ops_array) \ -dsa_tag_driver_module_drivers(__ops_array, ARRAY_SIZE(__ops_array)) - -#define DSA_TAG_DRIVER_NAME(__ops) dsa_tag_driver ## _ ## __ops - -/* Create a static structure we can build a linked list of dsa_tag - * drivers - */ -#define DSA_TAG_DRIVER(__ops) \ -static struct dsa_tag_driver DSA_TAG_DRIVER_NAME(__ops) = { \ - .ops = &__ops, \ -} - -/** - * module_dsa_tag_driver() - Helper macro for registering a single DSA tag - * driver - * @__ops: Single tag driver structures - * - * Helper macro for DSA tag drivers which do not do anything special - * in module init/exit. Each module may only use this macro once, and - * calling it replaces module_init() and module_exit(). - */ -#define module_dsa_tag_driver(__ops) \ -DSA_TAG_DRIVER(__ops); \ - \ -static struct dsa_tag_driver *dsa_tag_driver_array[] = { \ - &DSA_TAG_DRIVER_NAME(__ops) \ -}; \ -module_dsa_tag_drivers(dsa_tag_driver_array) - enum { DSA_NOTIFIER_AGEING_TIME, DSA_NOTIFIER_BRIDGE_JOIN, @@ -223,234 +146,15 @@ struct dsa_standalone_event_work { }; /* dsa.c */ -const struct dsa_device_ops *dsa_tag_driver_get_by_id(int tag_protocol); -const struct dsa_device_ops *dsa_tag_driver_get_by_name(const char *name); -void dsa_tag_driver_put(const struct dsa_device_ops *ops); - struct net_device *dsa_dev_to_net_device(struct device *dev); bool dsa_db_equal(const struct dsa_db *a, const struct dsa_db *b); bool dsa_schedule_work(struct work_struct *work); -const char *dsa_tag_protocol_to_str(const struct dsa_device_ops *ops); - -static inline int dsa_tag_protocol_overhead(const struct dsa_device_ops *ops) -{ - return ops->needed_headroom + ops->needed_tailroom; -} - -static inline struct net_device *dsa_master_find_slave(struct net_device *dev, - int device, int port) -{ - struct dsa_port *cpu_dp = dev->dsa_ptr; - struct dsa_switch_tree *dst = cpu_dp->dst; - struct dsa_port *dp; - - list_for_each_entry(dp, &dst->ports, list) - if (dp->ds->index == device && dp->index == port && - dp->type == DSA_PORT_TYPE_USER) - return dp->slave; - - return NULL; -} /* netlink.c */ extern struct rtnl_link_ops dsa_link_ops __read_mostly; -/* If under a bridge with vlan_filtering=0, make sure to send pvid-tagged - * frames as untagged, since the bridge will not untag them. - */ -static inline struct sk_buff *dsa_untag_bridge_pvid(struct sk_buff *skb) -{ - struct dsa_port *dp = dsa_slave_to_port(skb->dev); - struct net_device *br = dsa_port_bridge_dev_get(dp); - struct net_device *dev = skb->dev; - struct net_device *upper_dev; - u16 vid, pvid, proto; - int err; - - if (!br || br_vlan_enabled(br)) - return skb; - - err = br_vlan_get_proto(br, &proto); - if (err) - return skb; - - /* Move VLAN tag from data to hwaccel */ - if (!skb_vlan_tag_present(skb) && skb->protocol == htons(proto)) { - skb = skb_vlan_untag(skb); - if (!skb) - return NULL; - } - - if (!skb_vlan_tag_present(skb)) - return skb; - - vid = skb_vlan_tag_get_id(skb); - - /* We already run under an RCU read-side critical section since - * we are called from netif_receive_skb_list_internal(). - */ - err = br_vlan_get_pvid_rcu(dev, &pvid); - if (err) - return skb; - - if (vid != pvid) - return skb; - - /* The sad part about attempting to untag from DSA is that we - * don't know, unless we check, if the skb will end up in - * the bridge's data path - br_allowed_ingress() - or not. - * For example, there might be an 8021q upper for the - * default_pvid of the bridge, which will steal VLAN-tagged traffic - * from the bridge's data path. This is a configuration that DSA - * supports because vlan_filtering is 0. In that case, we should - * definitely keep the tag, to make sure it keeps working. - */ - upper_dev = __vlan_find_dev_deep_rcu(br, htons(proto), vid); - if (upper_dev) - return skb; - - __vlan_hwaccel_clear_tag(skb); - - return skb; -} - -/* For switches without hardware support for DSA tagging to be able - * to support termination through the bridge. - */ -static inline struct net_device * -dsa_find_designated_bridge_port_by_vid(struct net_device *master, u16 vid) -{ - struct dsa_port *cpu_dp = master->dsa_ptr; - struct dsa_switch_tree *dst = cpu_dp->dst; - struct bridge_vlan_info vinfo; - struct net_device *slave; - struct dsa_port *dp; - int err; - - list_for_each_entry(dp, &dst->ports, list) { - if (dp->type != DSA_PORT_TYPE_USER) - continue; - - if (!dp->bridge) - continue; - - if (dp->stp_state != BR_STATE_LEARNING && - dp->stp_state != BR_STATE_FORWARDING) - continue; - - /* Since the bridge might learn this packet, keep the CPU port - * affinity with the port that will be used for the reply on - * xmit. - */ - if (dp->cpu_dp != cpu_dp) - continue; - - slave = dp->slave; - - err = br_vlan_get_info_rcu(slave, vid, &vinfo); - if (err) - continue; - - return slave; - } - - return NULL; -} - -/* If the ingress port offloads the bridge, we mark the frame as autonomously - * forwarded by hardware, so the software bridge doesn't forward in twice, back - * to us, because we already did. However, if we're in fallback mode and we do - * software bridging, we are not offloading it, therefore the dp->bridge - * pointer is not populated, and flooding needs to be done by software (we are - * effectively operating in standalone ports mode). - */ -static inline void dsa_default_offload_fwd_mark(struct sk_buff *skb) -{ - struct dsa_port *dp = dsa_slave_to_port(skb->dev); - - skb->offload_fwd_mark = !!(dp->bridge); -} - -/* Helper for removing DSA header tags from packets in the RX path. - * Must not be called before skb_pull(len). - * skb->data - * | - * v - * | | | | | | | | | | | | | | | | | | | - * +-----------------------+-----------------------+---------------+-------+ - * | Destination MAC | Source MAC | DSA header | EType | - * +-----------------------+-----------------------+---------------+-------+ - * | | - * <----- len -----> <----- len -----> - * | - * >>>>>>> v - * >>>>>>> | | | | | | | | | | | | | | | - * >>>>>>> +-----------------------+-----------------------+-------+ - * >>>>>>> | Destination MAC | Source MAC | EType | - * +-----------------------+-----------------------+-------+ - * ^ - * | - * skb->data - */ -static inline void dsa_strip_etype_header(struct sk_buff *skb, int len) -{ - memmove(skb->data - ETH_HLEN, skb->data - ETH_HLEN - len, 2 * ETH_ALEN); -} - -/* Helper for creating space for DSA header tags in TX path packets. - * Must not be called before skb_push(len). - * - * Before: - * - * <<<<<<< | | | | | | | | | | | | | | | - * ^ <<<<<<< +-----------------------+-----------------------+-------+ - * | <<<<<<< | Destination MAC | Source MAC | EType | - * | +-----------------------+-----------------------+-------+ - * <----- len -----> - * | - * | - * skb->data - * - * After: - * - * | | | | | | | | | | | | | | | | | | | - * +-----------------------+-----------------------+---------------+-------+ - * | Destination MAC | Source MAC | DSA header | EType | - * +-----------------------+-----------------------+---------------+-------+ - * ^ | | - * | <----- len -----> - * skb->data - */ -static inline void dsa_alloc_etype_header(struct sk_buff *skb, int len) -{ - memmove(skb->data, skb->data + len, 2 * ETH_ALEN); -} - -/* On RX, eth_type_trans() on the DSA master pulls ETH_HLEN bytes starting from - * skb_mac_header(skb), which leaves skb->data pointing at the first byte after - * what the DSA master perceives as the EtherType (the beginning of the L3 - * protocol). Since DSA EtherType header taggers treat the EtherType as part of - * the DSA tag itself, and the EtherType is 2 bytes in length, the DSA header - * is located 2 bytes behind skb->data. Note that EtherType in this context - * means the first 2 bytes of the DSA header, not the encapsulated EtherType - * that will become visible after the DSA header is stripped. - */ -static inline void *dsa_etype_header_pos_rx(struct sk_buff *skb) -{ - return skb->data - 2; -} - -/* On TX, skb->data points to skb_mac_header(skb), which means that EtherType - * header taggers start exactly where the EtherType is (the EtherType is - * treated as part of the DSA header). - */ -static inline void *dsa_etype_header_pos_tx(struct sk_buff *skb) -{ - return skb->data + 2 * ETH_ALEN; -} - /* switch.c */ int dsa_switch_register_notifier(struct dsa_switch *ds); void dsa_switch_unregister_notifier(struct dsa_switch *ds); diff --git a/net/dsa/master.c b/net/dsa/master.c index 6105821834a2..e38b349ca7f8 100644 --- a/net/dsa/master.c +++ b/net/dsa/master.c @@ -14,6 +14,7 @@ #include "dsa_priv.h" #include "master.h" #include "port.h" +#include "tag.h" static int dsa_master_get_regs_len(struct net_device *dev) { diff --git a/net/dsa/slave.c b/net/dsa/slave.c index 2cf83892072f..a928aaf68804 100644 --- a/net/dsa/slave.c +++ b/net/dsa/slave.c @@ -26,6 +26,7 @@ #include "port.h" #include "master.h" #include "slave.h" +#include "tag.h" static void dsa_slave_standalone_event_work(struct work_struct *work) { diff --git a/net/dsa/tag.c b/net/dsa/tag.c new file mode 100644 index 000000000000..383721e167d6 --- /dev/null +++ b/net/dsa/tag.c @@ -0,0 +1,243 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * DSA tagging protocol handling + * + * Copyright (c) 2008-2009 Marvell Semiconductor + * Copyright (c) 2013 Florian Fainelli + * Copyright (c) 2016 Andrew Lunn + */ + +#include +#include +#include +#include +#include + +#include "slave.h" +#include "tag.h" + +static LIST_HEAD(dsa_tag_drivers_list); +static DEFINE_MUTEX(dsa_tag_drivers_lock); + +/* Determine if we should defer delivery of skb until we have a rx timestamp. + * + * Called from dsa_switch_rcv. For now, this will only work if tagging is + * enabled on the switch. Normally the MAC driver would retrieve the hardware + * timestamp when it reads the packet out of the hardware. However in a DSA + * switch, the DSA driver owning the interface to which the packet is + * delivered is never notified unless we do so here. + */ +static bool dsa_skb_defer_rx_timestamp(struct dsa_slave_priv *p, + struct sk_buff *skb) +{ + struct dsa_switch *ds = p->dp->ds; + unsigned int type; + + if (skb_headroom(skb) < ETH_HLEN) + return false; + + __skb_push(skb, ETH_HLEN); + + type = ptp_classify_raw(skb); + + __skb_pull(skb, ETH_HLEN); + + if (type == PTP_CLASS_NONE) + return false; + + if (likely(ds->ops->port_rxtstamp)) + return ds->ops->port_rxtstamp(ds, p->dp->index, skb, type); + + return false; +} + +static int dsa_switch_rcv(struct sk_buff *skb, struct net_device *dev, + struct packet_type *pt, struct net_device *unused) +{ + struct metadata_dst *md_dst = skb_metadata_dst(skb); + struct dsa_port *cpu_dp = dev->dsa_ptr; + struct sk_buff *nskb = NULL; + struct dsa_slave_priv *p; + + if (unlikely(!cpu_dp)) { + kfree_skb(skb); + return 0; + } + + skb = skb_unshare(skb, GFP_ATOMIC); + if (!skb) + return 0; + + if (md_dst && md_dst->type == METADATA_HW_PORT_MUX) { + unsigned int port = md_dst->u.port_info.port_id; + + skb_dst_drop(skb); + if (!skb_has_extensions(skb)) + skb->slow_gro = 0; + + skb->dev = dsa_master_find_slave(dev, 0, port); + if (likely(skb->dev)) { + dsa_default_offload_fwd_mark(skb); + nskb = skb; + } + } else { + nskb = cpu_dp->rcv(skb, dev); + } + + if (!nskb) { + kfree_skb(skb); + return 0; + } + + skb = nskb; + skb_push(skb, ETH_HLEN); + skb->pkt_type = PACKET_HOST; + skb->protocol = eth_type_trans(skb, skb->dev); + + if (unlikely(!dsa_slave_dev_check(skb->dev))) { + /* Packet is to be injected directly on an upper + * device, e.g. a team/bond, so skip all DSA-port + * specific actions. + */ + netif_rx(skb); + return 0; + } + + p = netdev_priv(skb->dev); + + if (unlikely(cpu_dp->ds->untag_bridge_pvid)) { + nskb = dsa_untag_bridge_pvid(skb); + if (!nskb) { + kfree_skb(skb); + return 0; + } + skb = nskb; + } + + dev_sw_netstats_rx_add(skb->dev, skb->len); + + if (dsa_skb_defer_rx_timestamp(p, skb)) + return 0; + + gro_cells_receive(&p->gcells, skb); + + return 0; +} + +struct packet_type dsa_pack_type __read_mostly = { + .type = cpu_to_be16(ETH_P_XDSA), + .func = dsa_switch_rcv, +}; + +static void dsa_tag_driver_register(struct dsa_tag_driver *dsa_tag_driver, + struct module *owner) +{ + dsa_tag_driver->owner = owner; + + mutex_lock(&dsa_tag_drivers_lock); + list_add_tail(&dsa_tag_driver->list, &dsa_tag_drivers_list); + mutex_unlock(&dsa_tag_drivers_lock); +} + +void dsa_tag_drivers_register(struct dsa_tag_driver *dsa_tag_driver_array[], + unsigned int count, struct module *owner) +{ + unsigned int i; + + for (i = 0; i < count; i++) + dsa_tag_driver_register(dsa_tag_driver_array[i], owner); +} + +static void dsa_tag_driver_unregister(struct dsa_tag_driver *dsa_tag_driver) +{ + mutex_lock(&dsa_tag_drivers_lock); + list_del(&dsa_tag_driver->list); + mutex_unlock(&dsa_tag_drivers_lock); +} +EXPORT_SYMBOL_GPL(dsa_tag_drivers_register); + +void dsa_tag_drivers_unregister(struct dsa_tag_driver *dsa_tag_driver_array[], + unsigned int count) +{ + unsigned int i; + + for (i = 0; i < count; i++) + dsa_tag_driver_unregister(dsa_tag_driver_array[i]); +} +EXPORT_SYMBOL_GPL(dsa_tag_drivers_unregister); + +const char *dsa_tag_protocol_to_str(const struct dsa_device_ops *ops) +{ + return ops->name; +}; + +/* Function takes a reference on the module owning the tagger, + * so dsa_tag_driver_put must be called afterwards. + */ +const struct dsa_device_ops *dsa_tag_driver_get_by_name(const char *name) +{ + const struct dsa_device_ops *ops = ERR_PTR(-ENOPROTOOPT); + struct dsa_tag_driver *dsa_tag_driver; + + request_module("%s%s", DSA_TAG_DRIVER_ALIAS, name); + + mutex_lock(&dsa_tag_drivers_lock); + list_for_each_entry(dsa_tag_driver, &dsa_tag_drivers_list, list) { + const struct dsa_device_ops *tmp = dsa_tag_driver->ops; + + if (strcmp(name, tmp->name)) + continue; + + if (!try_module_get(dsa_tag_driver->owner)) + break; + + ops = tmp; + break; + } + mutex_unlock(&dsa_tag_drivers_lock); + + return ops; +} + +const struct dsa_device_ops *dsa_tag_driver_get_by_id(int tag_protocol) +{ + struct dsa_tag_driver *dsa_tag_driver; + const struct dsa_device_ops *ops; + bool found = false; + + request_module("%sid-%d", DSA_TAG_DRIVER_ALIAS, tag_protocol); + + mutex_lock(&dsa_tag_drivers_lock); + list_for_each_entry(dsa_tag_driver, &dsa_tag_drivers_list, list) { + ops = dsa_tag_driver->ops; + if (ops->proto == tag_protocol) { + found = true; + break; + } + } + + if (found) { + if (!try_module_get(dsa_tag_driver->owner)) + ops = ERR_PTR(-ENOPROTOOPT); + } else { + ops = ERR_PTR(-ENOPROTOOPT); + } + + mutex_unlock(&dsa_tag_drivers_lock); + + return ops; +} + +void dsa_tag_driver_put(const struct dsa_device_ops *ops) +{ + struct dsa_tag_driver *dsa_tag_driver; + + mutex_lock(&dsa_tag_drivers_lock); + list_for_each_entry(dsa_tag_driver, &dsa_tag_drivers_list, list) { + if (dsa_tag_driver->ops == ops) { + module_put(dsa_tag_driver->owner); + break; + } + } + mutex_unlock(&dsa_tag_drivers_lock); +} diff --git a/net/dsa/tag.h b/net/dsa/tag.h new file mode 100644 index 000000000000..7cfbca824f1c --- /dev/null +++ b/net/dsa/tag.h @@ -0,0 +1,310 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#ifndef __DSA_TAG_H +#define __DSA_TAG_H + +#include +#include +#include +#include + +#include "port.h" +#include "slave.h" + +struct dsa_tag_driver { + const struct dsa_device_ops *ops; + struct list_head list; + struct module *owner; +}; + +extern struct packet_type dsa_pack_type; + +const struct dsa_device_ops *dsa_tag_driver_get_by_id(int tag_protocol); +const struct dsa_device_ops *dsa_tag_driver_get_by_name(const char *name); +void dsa_tag_driver_put(const struct dsa_device_ops *ops); +const char *dsa_tag_protocol_to_str(const struct dsa_device_ops *ops); + +static inline int dsa_tag_protocol_overhead(const struct dsa_device_ops *ops) +{ + return ops->needed_headroom + ops->needed_tailroom; +} + +static inline struct net_device *dsa_master_find_slave(struct net_device *dev, + int device, int port) +{ + struct dsa_port *cpu_dp = dev->dsa_ptr; + struct dsa_switch_tree *dst = cpu_dp->dst; + struct dsa_port *dp; + + list_for_each_entry(dp, &dst->ports, list) + if (dp->ds->index == device && dp->index == port && + dp->type == DSA_PORT_TYPE_USER) + return dp->slave; + + return NULL; +} + +/* If under a bridge with vlan_filtering=0, make sure to send pvid-tagged + * frames as untagged, since the bridge will not untag them. + */ +static inline struct sk_buff *dsa_untag_bridge_pvid(struct sk_buff *skb) +{ + struct dsa_port *dp = dsa_slave_to_port(skb->dev); + struct net_device *br = dsa_port_bridge_dev_get(dp); + struct net_device *dev = skb->dev; + struct net_device *upper_dev; + u16 vid, pvid, proto; + int err; + + if (!br || br_vlan_enabled(br)) + return skb; + + err = br_vlan_get_proto(br, &proto); + if (err) + return skb; + + /* Move VLAN tag from data to hwaccel */ + if (!skb_vlan_tag_present(skb) && skb->protocol == htons(proto)) { + skb = skb_vlan_untag(skb); + if (!skb) + return NULL; + } + + if (!skb_vlan_tag_present(skb)) + return skb; + + vid = skb_vlan_tag_get_id(skb); + + /* We already run under an RCU read-side critical section since + * we are called from netif_receive_skb_list_internal(). + */ + err = br_vlan_get_pvid_rcu(dev, &pvid); + if (err) + return skb; + + if (vid != pvid) + return skb; + + /* The sad part about attempting to untag from DSA is that we + * don't know, unless we check, if the skb will end up in + * the bridge's data path - br_allowed_ingress() - or not. + * For example, there might be an 8021q upper for the + * default_pvid of the bridge, which will steal VLAN-tagged traffic + * from the bridge's data path. This is a configuration that DSA + * supports because vlan_filtering is 0. In that case, we should + * definitely keep the tag, to make sure it keeps working. + */ + upper_dev = __vlan_find_dev_deep_rcu(br, htons(proto), vid); + if (upper_dev) + return skb; + + __vlan_hwaccel_clear_tag(skb); + + return skb; +} + +/* For switches without hardware support for DSA tagging to be able + * to support termination through the bridge. + */ +static inline struct net_device * +dsa_find_designated_bridge_port_by_vid(struct net_device *master, u16 vid) +{ + struct dsa_port *cpu_dp = master->dsa_ptr; + struct dsa_switch_tree *dst = cpu_dp->dst; + struct bridge_vlan_info vinfo; + struct net_device *slave; + struct dsa_port *dp; + int err; + + list_for_each_entry(dp, &dst->ports, list) { + if (dp->type != DSA_PORT_TYPE_USER) + continue; + + if (!dp->bridge) + continue; + + if (dp->stp_state != BR_STATE_LEARNING && + dp->stp_state != BR_STATE_FORWARDING) + continue; + + /* Since the bridge might learn this packet, keep the CPU port + * affinity with the port that will be used for the reply on + * xmit. + */ + if (dp->cpu_dp != cpu_dp) + continue; + + slave = dp->slave; + + err = br_vlan_get_info_rcu(slave, vid, &vinfo); + if (err) + continue; + + return slave; + } + + return NULL; +} + +/* If the ingress port offloads the bridge, we mark the frame as autonomously + * forwarded by hardware, so the software bridge doesn't forward in twice, back + * to us, because we already did. However, if we're in fallback mode and we do + * software bridging, we are not offloading it, therefore the dp->bridge + * pointer is not populated, and flooding needs to be done by software (we are + * effectively operating in standalone ports mode). + */ +static inline void dsa_default_offload_fwd_mark(struct sk_buff *skb) +{ + struct dsa_port *dp = dsa_slave_to_port(skb->dev); + + skb->offload_fwd_mark = !!(dp->bridge); +} + +/* Helper for removing DSA header tags from packets in the RX path. + * Must not be called before skb_pull(len). + * skb->data + * | + * v + * | | | | | | | | | | | | | | | | | | | + * +-----------------------+-----------------------+---------------+-------+ + * | Destination MAC | Source MAC | DSA header | EType | + * +-----------------------+-----------------------+---------------+-------+ + * | | + * <----- len -----> <----- len -----> + * | + * >>>>>>> v + * >>>>>>> | | | | | | | | | | | | | | | + * >>>>>>> +-----------------------+-----------------------+-------+ + * >>>>>>> | Destination MAC | Source MAC | EType | + * +-----------------------+-----------------------+-------+ + * ^ + * | + * skb->data + */ +static inline void dsa_strip_etype_header(struct sk_buff *skb, int len) +{ + memmove(skb->data - ETH_HLEN, skb->data - ETH_HLEN - len, 2 * ETH_ALEN); +} + +/* Helper for creating space for DSA header tags in TX path packets. + * Must not be called before skb_push(len). + * + * Before: + * + * <<<<<<< | | | | | | | | | | | | | | | + * ^ <<<<<<< +-----------------------+-----------------------+-------+ + * | <<<<<<< | Destination MAC | Source MAC | EType | + * | +-----------------------+-----------------------+-------+ + * <----- len -----> + * | + * | + * skb->data + * + * After: + * + * | | | | | | | | | | | | | | | | | | | + * +-----------------------+-----------------------+---------------+-------+ + * | Destination MAC | Source MAC | DSA header | EType | + * +-----------------------+-----------------------+---------------+-------+ + * ^ | | + * | <----- len -----> + * skb->data + */ +static inline void dsa_alloc_etype_header(struct sk_buff *skb, int len) +{ + memmove(skb->data, skb->data + len, 2 * ETH_ALEN); +} + +/* On RX, eth_type_trans() on the DSA master pulls ETH_HLEN bytes starting from + * skb_mac_header(skb), which leaves skb->data pointing at the first byte after + * what the DSA master perceives as the EtherType (the beginning of the L3 + * protocol). Since DSA EtherType header taggers treat the EtherType as part of + * the DSA tag itself, and the EtherType is 2 bytes in length, the DSA header + * is located 2 bytes behind skb->data. Note that EtherType in this context + * means the first 2 bytes of the DSA header, not the encapsulated EtherType + * that will become visible after the DSA header is stripped. + */ +static inline void *dsa_etype_header_pos_rx(struct sk_buff *skb) +{ + return skb->data - 2; +} + +/* On TX, skb->data points to skb_mac_header(skb), which means that EtherType + * header taggers start exactly where the EtherType is (the EtherType is + * treated as part of the DSA header). + */ +static inline void *dsa_etype_header_pos_tx(struct sk_buff *skb) +{ + return skb->data + 2 * ETH_ALEN; +} + +/* Create 2 modaliases per tagging protocol, one to auto-load the module + * given the ID reported by get_tag_protocol(), and the other by name. + */ +#define DSA_TAG_DRIVER_ALIAS "dsa_tag:" +#define MODULE_ALIAS_DSA_TAG_DRIVER(__proto, __name) \ + MODULE_ALIAS(DSA_TAG_DRIVER_ALIAS __name); \ + MODULE_ALIAS(DSA_TAG_DRIVER_ALIAS "id-" \ + __stringify(__proto##_VALUE)) + +void dsa_tag_drivers_register(struct dsa_tag_driver *dsa_tag_driver_array[], + unsigned int count, + struct module *owner); +void dsa_tag_drivers_unregister(struct dsa_tag_driver *dsa_tag_driver_array[], + unsigned int count); + +#define dsa_tag_driver_module_drivers(__dsa_tag_drivers_array, __count) \ +static int __init dsa_tag_driver_module_init(void) \ +{ \ + dsa_tag_drivers_register(__dsa_tag_drivers_array, __count, \ + THIS_MODULE); \ + return 0; \ +} \ +module_init(dsa_tag_driver_module_init); \ + \ +static void __exit dsa_tag_driver_module_exit(void) \ +{ \ + dsa_tag_drivers_unregister(__dsa_tag_drivers_array, __count); \ +} \ +module_exit(dsa_tag_driver_module_exit) + +/** + * module_dsa_tag_drivers() - Helper macro for registering DSA tag + * drivers + * @__ops_array: Array of tag driver structures + * + * Helper macro for DSA tag drivers which do not do anything special + * in module init/exit. Each module may only use this macro once, and + * calling it replaces module_init() and module_exit(). + */ +#define module_dsa_tag_drivers(__ops_array) \ +dsa_tag_driver_module_drivers(__ops_array, ARRAY_SIZE(__ops_array)) + +#define DSA_TAG_DRIVER_NAME(__ops) dsa_tag_driver ## _ ## __ops + +/* Create a static structure we can build a linked list of dsa_tag + * drivers + */ +#define DSA_TAG_DRIVER(__ops) \ +static struct dsa_tag_driver DSA_TAG_DRIVER_NAME(__ops) = { \ + .ops = &__ops, \ +} + +/** + * module_dsa_tag_driver() - Helper macro for registering a single DSA tag + * driver + * @__ops: Single tag driver structures + * + * Helper macro for DSA tag drivers which do not do anything special + * in module init/exit. Each module may only use this macro once, and + * calling it replaces module_init() and module_exit(). + */ +#define module_dsa_tag_driver(__ops) \ +DSA_TAG_DRIVER(__ops); \ + \ +static struct dsa_tag_driver *dsa_tag_driver_array[] = { \ + &DSA_TAG_DRIVER_NAME(__ops) \ +}; \ +module_dsa_tag_drivers(dsa_tag_driver_array) + +#endif diff --git a/net/dsa/tag_8021q.c b/net/dsa/tag_8021q.c index a6617d7b692a..ee5dd1a54b51 100644 --- a/net/dsa/tag_8021q.c +++ b/net/dsa/tag_8021q.c @@ -9,6 +9,7 @@ #include "dsa_priv.h" #include "port.h" +#include "tag.h" /* Binary structure of the fake 12-bit VID field (when the TPID is * ETH_P_DSA_8021Q): diff --git a/net/dsa/tag_ar9331.c b/net/dsa/tag_ar9331.c index bfa161a4f502..7f3b7d730b85 100644 --- a/net/dsa/tag_ar9331.c +++ b/net/dsa/tag_ar9331.c @@ -7,7 +7,7 @@ #include #include -#include "dsa_priv.h" +#include "tag.h" #define AR9331_NAME "ar9331" diff --git a/net/dsa/tag_brcm.c b/net/dsa/tag_brcm.c index 9e7477ed70f1..10239daa5745 100644 --- a/net/dsa/tag_brcm.c +++ b/net/dsa/tag_brcm.c @@ -10,7 +10,7 @@ #include #include -#include "dsa_priv.h" +#include "tag.h" #define BRCM_NAME "brcm" #define BRCM_LEGACY_NAME "brcm-legacy" diff --git a/net/dsa/tag_dsa.c b/net/dsa/tag_dsa.c index 9fe77f5cc759..1fd7fa26db64 100644 --- a/net/dsa/tag_dsa.c +++ b/net/dsa/tag_dsa.c @@ -50,7 +50,7 @@ #include #include -#include "dsa_priv.h" +#include "tag.h" #define DSA_NAME "dsa" #define EDSA_NAME "edsa" diff --git a/net/dsa/tag_gswip.c b/net/dsa/tag_gswip.c index 020050dff3e4..e279cd9057b0 100644 --- a/net/dsa/tag_gswip.c +++ b/net/dsa/tag_gswip.c @@ -10,7 +10,7 @@ #include #include -#include "dsa_priv.h" +#include "tag.h" #define GSWIP_NAME "gswip" diff --git a/net/dsa/tag_hellcreek.c b/net/dsa/tag_hellcreek.c index 03fd5f2877c8..a047041e7686 100644 --- a/net/dsa/tag_hellcreek.c +++ b/net/dsa/tag_hellcreek.c @@ -12,6 +12,7 @@ #include #include "dsa_priv.h" +#include "tag.h" #define HELLCREEK_NAME "hellcreek" diff --git a/net/dsa/tag_ksz.c b/net/dsa/tag_ksz.c index 37db5156f9a3..0f6ae143afc9 100644 --- a/net/dsa/tag_ksz.c +++ b/net/dsa/tag_ksz.c @@ -7,7 +7,8 @@ #include #include #include -#include "dsa_priv.h" + +#include "tag.h" #define KSZ8795_NAME "ksz8795" #define KSZ9477_NAME "ksz9477" diff --git a/net/dsa/tag_lan9303.c b/net/dsa/tag_lan9303.c index 4118292ed218..c25f5536706b 100644 --- a/net/dsa/tag_lan9303.c +++ b/net/dsa/tag_lan9303.c @@ -7,7 +7,7 @@ #include #include -#include "dsa_priv.h" +#include "tag.h" /* To define the outgoing port and to discover the incoming port a regular * VLAN tag is used by the LAN9303. But its VID meaning is 'special': diff --git a/net/dsa/tag_mtk.c b/net/dsa/tag_mtk.c index 8948c4f99f8e..40af80452747 100644 --- a/net/dsa/tag_mtk.c +++ b/net/dsa/tag_mtk.c @@ -8,7 +8,7 @@ #include #include -#include "dsa_priv.h" +#include "tag.h" #define MTK_NAME "mtk" diff --git a/net/dsa/tag_none.c b/net/dsa/tag_none.c index 34a13c50d245..d2fd179c4227 100644 --- a/net/dsa/tag_none.c +++ b/net/dsa/tag_none.c @@ -8,7 +8,7 @@ * tagging support, look at tag_8021q.c instead. */ -#include "dsa_priv.h" +#include "tag.h" #define NONE_NAME "none" diff --git a/net/dsa/tag_ocelot.c b/net/dsa/tag_ocelot.c index 8cc31ab47e28..28ebecafdd24 100644 --- a/net/dsa/tag_ocelot.c +++ b/net/dsa/tag_ocelot.c @@ -2,7 +2,8 @@ /* Copyright 2019 NXP */ #include -#include "dsa_priv.h" + +#include "tag.h" #define OCELOT_NAME "ocelot" #define SEVILLE_NAME "seville" diff --git a/net/dsa/tag_ocelot_8021q.c b/net/dsa/tag_ocelot_8021q.c index d1ec68001487..7f0c2d71e89b 100644 --- a/net/dsa/tag_ocelot_8021q.c +++ b/net/dsa/tag_ocelot_8021q.c @@ -10,7 +10,8 @@ */ #include #include -#include "dsa_priv.h" + +#include "tag.h" #define OCELOT_8021Q_NAME "ocelot-8021q" diff --git a/net/dsa/tag_qca.c b/net/dsa/tag_qca.c index 73d6e111228d..e757c8de06f1 100644 --- a/net/dsa/tag_qca.c +++ b/net/dsa/tag_qca.c @@ -8,7 +8,7 @@ #include #include -#include "dsa_priv.h" +#include "tag.h" #define QCA_NAME "qca" diff --git a/net/dsa/tag_rtl4_a.c b/net/dsa/tag_rtl4_a.c index 18b52d77d200..c327314b95e3 100644 --- a/net/dsa/tag_rtl4_a.c +++ b/net/dsa/tag_rtl4_a.c @@ -18,7 +18,7 @@ #include #include -#include "dsa_priv.h" +#include "tag.h" #define RTL4_A_NAME "rtl4a" diff --git a/net/dsa/tag_rtl8_4.c b/net/dsa/tag_rtl8_4.c index 030a8cf0ad48..4f67834fd121 100644 --- a/net/dsa/tag_rtl8_4.c +++ b/net/dsa/tag_rtl8_4.c @@ -77,7 +77,7 @@ #include #include -#include "dsa_priv.h" +#include "tag.h" /* Protocols supported: * diff --git a/net/dsa/tag_rzn1_a5psw.c b/net/dsa/tag_rzn1_a5psw.c index b9135069f9fc..437a6820ac42 100644 --- a/net/dsa/tag_rzn1_a5psw.c +++ b/net/dsa/tag_rzn1_a5psw.c @@ -10,7 +10,7 @@ #include #include -#include "dsa_priv.h" +#include "tag.h" /* To define the outgoing port and to discover the incoming port a TAG is * inserted after Src MAC : diff --git a/net/dsa/tag_sja1105.c b/net/dsa/tag_sja1105.c index 3b6e642a90e9..8f581617e15c 100644 --- a/net/dsa/tag_sja1105.c +++ b/net/dsa/tag_sja1105.c @@ -5,7 +5,8 @@ #include #include #include -#include "dsa_priv.h" + +#include "tag.h" #define SJA1105_NAME "sja1105" #define SJA1110_NAME "sja1110" diff --git a/net/dsa/tag_trailer.c b/net/dsa/tag_trailer.c index 8754dfe680f6..7361b9106382 100644 --- a/net/dsa/tag_trailer.c +++ b/net/dsa/tag_trailer.c @@ -8,7 +8,7 @@ #include #include -#include "dsa_priv.h" +#include "tag.h" #define TRAILER_NAME "trailer" diff --git a/net/dsa/tag_xrs700x.c b/net/dsa/tag_xrs700x.c index dc935dd90f98..af19969f9bc4 100644 --- a/net/dsa/tag_xrs700x.c +++ b/net/dsa/tag_xrs700x.c @@ -7,7 +7,7 @@ #include -#include "dsa_priv.h" +#include "tag.h" #define XRS700X_NAME "xrs700x" From 0c603136e1e0868fb5325c3b2addc669a10ed384 Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Mon, 21 Nov 2022 15:55:48 +0200 Subject: [PATCH 1167/2002] net: dsa: move headers exported by switch.c to switch.h Reduce code bloat in dsa_priv.h by moving the prototypes exported by switch.h into their own header file. Signed-off-by: Vladimir Oltean Reviewed-by: Florian Fainelli Signed-off-by: Jakub Kicinski --- net/dsa/dsa2.c | 1 + net/dsa/dsa_priv.h | 4 ---- net/dsa/port.c | 1 + net/dsa/switch.c | 1 + net/dsa/switch.h | 11 +++++++++++ net/dsa/tag_8021q.c | 1 + 6 files changed, 15 insertions(+), 4 deletions(-) create mode 100644 net/dsa/switch.h diff --git a/net/dsa/dsa2.c b/net/dsa/dsa2.c index 5373edf45a62..7c6b689267d0 100644 --- a/net/dsa/dsa2.c +++ b/net/dsa/dsa2.c @@ -22,6 +22,7 @@ #include "master.h" #include "port.h" #include "slave.h" +#include "switch.h" #include "tag.h" static DEFINE_MUTEX(dsa2_mutex); diff --git a/net/dsa/dsa_priv.h b/net/dsa/dsa_priv.h index eee2c9729e32..4f21228c6f52 100644 --- a/net/dsa/dsa_priv.h +++ b/net/dsa/dsa_priv.h @@ -155,10 +155,6 @@ bool dsa_schedule_work(struct work_struct *work); /* netlink.c */ extern struct rtnl_link_ops dsa_link_ops __read_mostly; -/* switch.c */ -int dsa_switch_register_notifier(struct dsa_switch *ds); -void dsa_switch_unregister_notifier(struct dsa_switch *ds); - static inline bool dsa_switch_supports_uc_filtering(struct dsa_switch *ds) { return ds->ops->port_fdb_add && ds->ops->port_fdb_del && diff --git a/net/dsa/port.c b/net/dsa/port.c index 56728242f079..bf2c98215021 100644 --- a/net/dsa/port.c +++ b/net/dsa/port.c @@ -15,6 +15,7 @@ #include "dsa_priv.h" #include "port.h" #include "slave.h" +#include "switch.h" /** * dsa_port_notify - Notify the switching fabric of changes to a port diff --git a/net/dsa/switch.c b/net/dsa/switch.c index d0d5a1c7e6f6..6a1c84c5ec8b 100644 --- a/net/dsa/switch.c +++ b/net/dsa/switch.c @@ -15,6 +15,7 @@ #include "dsa_priv.h" #include "port.h" #include "slave.h" +#include "switch.h" static unsigned int dsa_switch_fastest_ageing_time(struct dsa_switch *ds, unsigned int ageing_time) diff --git a/net/dsa/switch.h b/net/dsa/switch.h new file mode 100644 index 000000000000..b831b6fb45e9 --- /dev/null +++ b/net/dsa/switch.h @@ -0,0 +1,11 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#ifndef __DSA_SWITCH_H +#define __DSA_SWITCH_H + +struct dsa_switch; + +int dsa_switch_register_notifier(struct dsa_switch *ds); +void dsa_switch_unregister_notifier(struct dsa_switch *ds); + +#endif diff --git a/net/dsa/tag_8021q.c b/net/dsa/tag_8021q.c index ee5dd1a54b51..abd994dc76d5 100644 --- a/net/dsa/tag_8021q.c +++ b/net/dsa/tag_8021q.c @@ -9,6 +9,7 @@ #include "dsa_priv.h" #include "port.h" +#include "switch.h" #include "tag.h" /* Binary structure of the fake 12-bit VID field (when the TPID is From 6dbdfce7735786f9f2dd3af615c8a03ffa1246f5 Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Mon, 21 Nov 2022 15:55:49 +0200 Subject: [PATCH 1168/2002] net: dsa: move dsa_tree_notify() and dsa_broadcast() to switch.c There isn't an intuitive place for these 2 cross-chip notifier functions according to the function-to-file classification based on names (dsa_switch_*() goes to switch.c), but I consider these to be part of the cross-chip notifier handling, therefore part of switch.c. Move them there to reduce bloat in dsa2.c (the place where all code with no better place to go goes). Signed-off-by: Vladimir Oltean Reviewed-by: Florian Fainelli Signed-off-by: Jakub Kicinski --- net/dsa/dsa2.c | 46 ---------------------------------------------- net/dsa/dsa_priv.h | 2 -- net/dsa/switch.c | 46 ++++++++++++++++++++++++++++++++++++++++++++++ net/dsa/switch.h | 4 ++++ 4 files changed, 50 insertions(+), 48 deletions(-) diff --git a/net/dsa/dsa2.c b/net/dsa/dsa2.c index 7c6b689267d0..7a314c8b3aaa 100644 --- a/net/dsa/dsa2.c +++ b/net/dsa/dsa2.c @@ -31,52 +31,6 @@ LIST_HEAD(dsa_tree_list); /* Track the bridges with forwarding offload enabled */ static unsigned long dsa_fwd_offloading_bridges; -/** - * dsa_tree_notify - Execute code for all switches in a DSA switch tree. - * @dst: collection of struct dsa_switch devices to notify. - * @e: event, must be of type DSA_NOTIFIER_* - * @v: event-specific value. - * - * Given a struct dsa_switch_tree, this can be used to run a function once for - * each member DSA switch. The other alternative of traversing the tree is only - * through its ports list, which does not uniquely list the switches. - */ -int dsa_tree_notify(struct dsa_switch_tree *dst, unsigned long e, void *v) -{ - struct raw_notifier_head *nh = &dst->nh; - int err; - - err = raw_notifier_call_chain(nh, e, v); - - return notifier_to_errno(err); -} - -/** - * dsa_broadcast - Notify all DSA trees in the system. - * @e: event, must be of type DSA_NOTIFIER_* - * @v: event-specific value. - * - * Can be used to notify the switching fabric of events such as cross-chip - * bridging between disjoint trees (such as islands of tagger-compatible - * switches bridged by an incompatible middle switch). - * - * WARNING: this function is not reliable during probe time, because probing - * between trees is asynchronous and not all DSA trees might have probed. - */ -int dsa_broadcast(unsigned long e, void *v) -{ - struct dsa_switch_tree *dst; - int err = 0; - - list_for_each_entry(dst, &dsa_tree_list, list) { - err = dsa_tree_notify(dst, e, v); - if (err) - break; - } - - return err; -} - /** * dsa_lag_map() - Map LAG structure to a linear LAG array * @dst: Tree in which to record the mapping. diff --git a/net/dsa/dsa_priv.h b/net/dsa/dsa_priv.h index 4f21228c6f52..8cf8608344f5 100644 --- a/net/dsa/dsa_priv.h +++ b/net/dsa/dsa_priv.h @@ -175,8 +175,6 @@ void dsa_lag_unmap(struct dsa_switch_tree *dst, struct dsa_lag *lag); struct dsa_lag *dsa_tree_lag_find(struct dsa_switch_tree *dst, const struct net_device *lag_dev); struct net_device *dsa_tree_find_first_master(struct dsa_switch_tree *dst); -int dsa_tree_notify(struct dsa_switch_tree *dst, unsigned long e, void *v); -int dsa_broadcast(unsigned long e, void *v); int dsa_tree_change_tag_proto(struct dsa_switch_tree *dst, const struct dsa_device_ops *tag_ops, const struct dsa_device_ops *old_tag_ops); diff --git a/net/dsa/switch.c b/net/dsa/switch.c index 6a1c84c5ec8b..b534116dc519 100644 --- a/net/dsa/switch.c +++ b/net/dsa/switch.c @@ -1016,6 +1016,52 @@ static int dsa_switch_event(struct notifier_block *nb, return notifier_from_errno(err); } +/** + * dsa_tree_notify - Execute code for all switches in a DSA switch tree. + * @dst: collection of struct dsa_switch devices to notify. + * @e: event, must be of type DSA_NOTIFIER_* + * @v: event-specific value. + * + * Given a struct dsa_switch_tree, this can be used to run a function once for + * each member DSA switch. The other alternative of traversing the tree is only + * through its ports list, which does not uniquely list the switches. + */ +int dsa_tree_notify(struct dsa_switch_tree *dst, unsigned long e, void *v) +{ + struct raw_notifier_head *nh = &dst->nh; + int err; + + err = raw_notifier_call_chain(nh, e, v); + + return notifier_to_errno(err); +} + +/** + * dsa_broadcast - Notify all DSA trees in the system. + * @e: event, must be of type DSA_NOTIFIER_* + * @v: event-specific value. + * + * Can be used to notify the switching fabric of events such as cross-chip + * bridging between disjoint trees (such as islands of tagger-compatible + * switches bridged by an incompatible middle switch). + * + * WARNING: this function is not reliable during probe time, because probing + * between trees is asynchronous and not all DSA trees might have probed. + */ +int dsa_broadcast(unsigned long e, void *v) +{ + struct dsa_switch_tree *dst; + int err = 0; + + list_for_each_entry(dst, &dsa_tree_list, list) { + err = dsa_tree_notify(dst, e, v); + if (err) + break; + } + + return err; +} + int dsa_switch_register_notifier(struct dsa_switch *ds) { ds->nb.notifier_call = dsa_switch_event; diff --git a/net/dsa/switch.h b/net/dsa/switch.h index b831b6fb45e9..b2fd496bc56f 100644 --- a/net/dsa/switch.h +++ b/net/dsa/switch.h @@ -3,8 +3,12 @@ #ifndef __DSA_SWITCH_H #define __DSA_SWITCH_H +struct dsa_switch_tree; struct dsa_switch; +int dsa_tree_notify(struct dsa_switch_tree *dst, unsigned long e, void *v); +int dsa_broadcast(unsigned long e, void *v); + int dsa_switch_register_notifier(struct dsa_switch *ds); void dsa_switch_unregister_notifier(struct dsa_switch *ds); From 495550a4844bb4b4d10e26a7d22e9e231adb1b84 Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Mon, 21 Nov 2022 15:55:50 +0200 Subject: [PATCH 1169/2002] net: dsa: move notifier definitions to switch.h Reduce bloat in dsa_priv.h by moving the cross-chip notifier data structures to switch.h. Signed-off-by: Vladimir Oltean Reviewed-by: Florian Fainelli Signed-off-by: Jakub Kicinski --- net/dsa/dsa_priv.h | 104 +----------------------------------------- net/dsa/switch.h | 109 ++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 108 insertions(+), 105 deletions(-) diff --git a/net/dsa/dsa_priv.h b/net/dsa/dsa_priv.h index 8cf8608344f5..e5d421cdaa8f 100644 --- a/net/dsa/dsa_priv.h +++ b/net/dsa/dsa_priv.h @@ -13,109 +13,7 @@ #define DSA_MAX_NUM_OFFLOADING_BRIDGES BITS_PER_LONG -enum { - DSA_NOTIFIER_AGEING_TIME, - DSA_NOTIFIER_BRIDGE_JOIN, - DSA_NOTIFIER_BRIDGE_LEAVE, - DSA_NOTIFIER_FDB_ADD, - DSA_NOTIFIER_FDB_DEL, - DSA_NOTIFIER_HOST_FDB_ADD, - DSA_NOTIFIER_HOST_FDB_DEL, - DSA_NOTIFIER_LAG_FDB_ADD, - DSA_NOTIFIER_LAG_FDB_DEL, - DSA_NOTIFIER_LAG_CHANGE, - DSA_NOTIFIER_LAG_JOIN, - DSA_NOTIFIER_LAG_LEAVE, - DSA_NOTIFIER_MDB_ADD, - DSA_NOTIFIER_MDB_DEL, - DSA_NOTIFIER_HOST_MDB_ADD, - DSA_NOTIFIER_HOST_MDB_DEL, - DSA_NOTIFIER_VLAN_ADD, - DSA_NOTIFIER_VLAN_DEL, - DSA_NOTIFIER_HOST_VLAN_ADD, - DSA_NOTIFIER_HOST_VLAN_DEL, - DSA_NOTIFIER_MTU, - DSA_NOTIFIER_TAG_PROTO, - DSA_NOTIFIER_TAG_PROTO_CONNECT, - DSA_NOTIFIER_TAG_PROTO_DISCONNECT, - DSA_NOTIFIER_TAG_8021Q_VLAN_ADD, - DSA_NOTIFIER_TAG_8021Q_VLAN_DEL, - DSA_NOTIFIER_MASTER_STATE_CHANGE, -}; - -/* DSA_NOTIFIER_AGEING_TIME */ -struct dsa_notifier_ageing_time_info { - unsigned int ageing_time; -}; - -/* DSA_NOTIFIER_BRIDGE_* */ -struct dsa_notifier_bridge_info { - const struct dsa_port *dp; - struct dsa_bridge bridge; - bool tx_fwd_offload; - struct netlink_ext_ack *extack; -}; - -/* DSA_NOTIFIER_FDB_* */ -struct dsa_notifier_fdb_info { - const struct dsa_port *dp; - const unsigned char *addr; - u16 vid; - struct dsa_db db; -}; - -/* DSA_NOTIFIER_LAG_FDB_* */ -struct dsa_notifier_lag_fdb_info { - struct dsa_lag *lag; - const unsigned char *addr; - u16 vid; - struct dsa_db db; -}; - -/* DSA_NOTIFIER_MDB_* */ -struct dsa_notifier_mdb_info { - const struct dsa_port *dp; - const struct switchdev_obj_port_mdb *mdb; - struct dsa_db db; -}; - -/* DSA_NOTIFIER_LAG_* */ -struct dsa_notifier_lag_info { - const struct dsa_port *dp; - struct dsa_lag lag; - struct netdev_lag_upper_info *info; - struct netlink_ext_ack *extack; -}; - -/* DSA_NOTIFIER_VLAN_* */ -struct dsa_notifier_vlan_info { - const struct dsa_port *dp; - const struct switchdev_obj_port_vlan *vlan; - struct netlink_ext_ack *extack; -}; - -/* DSA_NOTIFIER_MTU */ -struct dsa_notifier_mtu_info { - const struct dsa_port *dp; - int mtu; -}; - -/* DSA_NOTIFIER_TAG_PROTO_* */ -struct dsa_notifier_tag_proto_info { - const struct dsa_device_ops *tag_ops; -}; - -/* DSA_NOTIFIER_TAG_8021Q_VLAN_* */ -struct dsa_notifier_tag_8021q_vlan_info { - const struct dsa_port *dp; - u16 vid; -}; - -/* DSA_NOTIFIER_MASTER_STATE_CHANGE */ -struct dsa_notifier_master_state_info { - const struct net_device *master; - bool operational; -}; +struct dsa_notifier_tag_8021q_vlan_info; struct dsa_switchdev_event_work { struct net_device *dev; diff --git a/net/dsa/switch.h b/net/dsa/switch.h index b2fd496bc56f..15e67b95eb6e 100644 --- a/net/dsa/switch.h +++ b/net/dsa/switch.h @@ -3,8 +3,113 @@ #ifndef __DSA_SWITCH_H #define __DSA_SWITCH_H -struct dsa_switch_tree; -struct dsa_switch; +#include + +struct netlink_ext_ack; + +enum { + DSA_NOTIFIER_AGEING_TIME, + DSA_NOTIFIER_BRIDGE_JOIN, + DSA_NOTIFIER_BRIDGE_LEAVE, + DSA_NOTIFIER_FDB_ADD, + DSA_NOTIFIER_FDB_DEL, + DSA_NOTIFIER_HOST_FDB_ADD, + DSA_NOTIFIER_HOST_FDB_DEL, + DSA_NOTIFIER_LAG_FDB_ADD, + DSA_NOTIFIER_LAG_FDB_DEL, + DSA_NOTIFIER_LAG_CHANGE, + DSA_NOTIFIER_LAG_JOIN, + DSA_NOTIFIER_LAG_LEAVE, + DSA_NOTIFIER_MDB_ADD, + DSA_NOTIFIER_MDB_DEL, + DSA_NOTIFIER_HOST_MDB_ADD, + DSA_NOTIFIER_HOST_MDB_DEL, + DSA_NOTIFIER_VLAN_ADD, + DSA_NOTIFIER_VLAN_DEL, + DSA_NOTIFIER_HOST_VLAN_ADD, + DSA_NOTIFIER_HOST_VLAN_DEL, + DSA_NOTIFIER_MTU, + DSA_NOTIFIER_TAG_PROTO, + DSA_NOTIFIER_TAG_PROTO_CONNECT, + DSA_NOTIFIER_TAG_PROTO_DISCONNECT, + DSA_NOTIFIER_TAG_8021Q_VLAN_ADD, + DSA_NOTIFIER_TAG_8021Q_VLAN_DEL, + DSA_NOTIFIER_MASTER_STATE_CHANGE, +}; + +/* DSA_NOTIFIER_AGEING_TIME */ +struct dsa_notifier_ageing_time_info { + unsigned int ageing_time; +}; + +/* DSA_NOTIFIER_BRIDGE_* */ +struct dsa_notifier_bridge_info { + const struct dsa_port *dp; + struct dsa_bridge bridge; + bool tx_fwd_offload; + struct netlink_ext_ack *extack; +}; + +/* DSA_NOTIFIER_FDB_* */ +struct dsa_notifier_fdb_info { + const struct dsa_port *dp; + const unsigned char *addr; + u16 vid; + struct dsa_db db; +}; + +/* DSA_NOTIFIER_LAG_FDB_* */ +struct dsa_notifier_lag_fdb_info { + struct dsa_lag *lag; + const unsigned char *addr; + u16 vid; + struct dsa_db db; +}; + +/* DSA_NOTIFIER_MDB_* */ +struct dsa_notifier_mdb_info { + const struct dsa_port *dp; + const struct switchdev_obj_port_mdb *mdb; + struct dsa_db db; +}; + +/* DSA_NOTIFIER_LAG_* */ +struct dsa_notifier_lag_info { + const struct dsa_port *dp; + struct dsa_lag lag; + struct netdev_lag_upper_info *info; + struct netlink_ext_ack *extack; +}; + +/* DSA_NOTIFIER_VLAN_* */ +struct dsa_notifier_vlan_info { + const struct dsa_port *dp; + const struct switchdev_obj_port_vlan *vlan; + struct netlink_ext_ack *extack; +}; + +/* DSA_NOTIFIER_MTU */ +struct dsa_notifier_mtu_info { + const struct dsa_port *dp; + int mtu; +}; + +/* DSA_NOTIFIER_TAG_PROTO_* */ +struct dsa_notifier_tag_proto_info { + const struct dsa_device_ops *tag_ops; +}; + +/* DSA_NOTIFIER_TAG_8021Q_VLAN_* */ +struct dsa_notifier_tag_8021q_vlan_info { + const struct dsa_port *dp; + u16 vid; +}; + +/* DSA_NOTIFIER_MASTER_STATE_CHANGE */ +struct dsa_notifier_master_state_info { + const struct net_device *master; + bool operational; +}; int dsa_tree_notify(struct dsa_switch_tree *dst, unsigned long e, void *v); int dsa_broadcast(unsigned long e, void *v); From 165c2fb93bed2e73c63d064b315a9da15a3e4694 Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Mon, 21 Nov 2022 15:55:51 +0200 Subject: [PATCH 1170/2002] net: dsa: merge dsa.c into dsa2.c There is no longer a meaningful distinction between what goes into dsa2.c and what goes into dsa.c. Merge the 2 into a single file. Signed-off-by: Vladimir Oltean Reviewed-by: Florian Fainelli Signed-off-by: Jakub Kicinski --- net/dsa/Makefile | 1 - net/dsa/dsa.c | 234 --------------------------------------------- net/dsa/dsa2.c | 222 +++++++++++++++++++++++++++++++++++++++++- net/dsa/dsa_priv.h | 2 - 4 files changed, 221 insertions(+), 238 deletions(-) delete mode 100644 net/dsa/dsa.c diff --git a/net/dsa/Makefile b/net/dsa/Makefile index 93f5d5f1e495..f38d0f4bf76c 100644 --- a/net/dsa/Makefile +++ b/net/dsa/Makefile @@ -3,7 +3,6 @@ obj-$(CONFIG_NET_DSA) += dsa_core.o dsa_core-y += \ devlink.o \ - dsa.o \ dsa2.o \ master.o \ netlink.o \ diff --git a/net/dsa/dsa.c b/net/dsa/dsa.c deleted file mode 100644 index 6f87dd1ee6bf..000000000000 --- a/net/dsa/dsa.c +++ /dev/null @@ -1,234 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * net/dsa/dsa.c - Hardware switch handling - * Copyright (c) 2008-2009 Marvell Semiconductor - * Copyright (c) 2013 Florian Fainelli - */ - -#include -#include -#include -#include -#include - -#include "dsa_priv.h" -#include "slave.h" -#include "tag.h" - -static int dev_is_class(struct device *dev, void *class) -{ - if (dev->class != NULL && !strcmp(dev->class->name, class)) - return 1; - - return 0; -} - -static struct device *dev_find_class(struct device *parent, char *class) -{ - if (dev_is_class(parent, class)) { - get_device(parent); - return parent; - } - - return device_find_child(parent, class, dev_is_class); -} - -struct net_device *dsa_dev_to_net_device(struct device *dev) -{ - struct device *d; - - d = dev_find_class(dev, "net"); - if (d != NULL) { - struct net_device *nd; - - nd = to_net_dev(d); - dev_hold(nd); - put_device(d); - - return nd; - } - - return NULL; -} - -#ifdef CONFIG_PM_SLEEP -static bool dsa_port_is_initialized(const struct dsa_port *dp) -{ - return dp->type == DSA_PORT_TYPE_USER && dp->slave; -} - -int dsa_switch_suspend(struct dsa_switch *ds) -{ - struct dsa_port *dp; - int ret = 0; - - /* Suspend slave network devices */ - dsa_switch_for_each_port(dp, ds) { - if (!dsa_port_is_initialized(dp)) - continue; - - ret = dsa_slave_suspend(dp->slave); - if (ret) - return ret; - } - - if (ds->ops->suspend) - ret = ds->ops->suspend(ds); - - return ret; -} -EXPORT_SYMBOL_GPL(dsa_switch_suspend); - -int dsa_switch_resume(struct dsa_switch *ds) -{ - struct dsa_port *dp; - int ret = 0; - - if (ds->ops->resume) - ret = ds->ops->resume(ds); - - if (ret) - return ret; - - /* Resume slave network devices */ - dsa_switch_for_each_port(dp, ds) { - if (!dsa_port_is_initialized(dp)) - continue; - - ret = dsa_slave_resume(dp->slave); - if (ret) - return ret; - } - - return 0; -} -EXPORT_SYMBOL_GPL(dsa_switch_resume); -#endif - -static struct workqueue_struct *dsa_owq; - -bool dsa_schedule_work(struct work_struct *work) -{ - return queue_work(dsa_owq, work); -} - -void dsa_flush_workqueue(void) -{ - flush_workqueue(dsa_owq); -} -EXPORT_SYMBOL_GPL(dsa_flush_workqueue); - -struct dsa_port *dsa_port_from_netdev(struct net_device *netdev) -{ - if (!netdev || !dsa_slave_dev_check(netdev)) - return ERR_PTR(-ENODEV); - - return dsa_slave_to_port(netdev); -} -EXPORT_SYMBOL_GPL(dsa_port_from_netdev); - -bool dsa_db_equal(const struct dsa_db *a, const struct dsa_db *b) -{ - if (a->type != b->type) - return false; - - switch (a->type) { - case DSA_DB_PORT: - return a->dp == b->dp; - case DSA_DB_LAG: - return a->lag.dev == b->lag.dev; - case DSA_DB_BRIDGE: - return a->bridge.num == b->bridge.num; - default: - WARN_ON(1); - return false; - } -} - -bool dsa_fdb_present_in_other_db(struct dsa_switch *ds, int port, - const unsigned char *addr, u16 vid, - struct dsa_db db) -{ - struct dsa_port *dp = dsa_to_port(ds, port); - struct dsa_mac_addr *a; - - lockdep_assert_held(&dp->addr_lists_lock); - - list_for_each_entry(a, &dp->fdbs, list) { - if (!ether_addr_equal(a->addr, addr) || a->vid != vid) - continue; - - if (a->db.type == db.type && !dsa_db_equal(&a->db, &db)) - return true; - } - - return false; -} -EXPORT_SYMBOL_GPL(dsa_fdb_present_in_other_db); - -bool dsa_mdb_present_in_other_db(struct dsa_switch *ds, int port, - const struct switchdev_obj_port_mdb *mdb, - struct dsa_db db) -{ - struct dsa_port *dp = dsa_to_port(ds, port); - struct dsa_mac_addr *a; - - lockdep_assert_held(&dp->addr_lists_lock); - - list_for_each_entry(a, &dp->mdbs, list) { - if (!ether_addr_equal(a->addr, mdb->addr) || a->vid != mdb->vid) - continue; - - if (a->db.type == db.type && !dsa_db_equal(&a->db, &db)) - return true; - } - - return false; -} -EXPORT_SYMBOL_GPL(dsa_mdb_present_in_other_db); - -static int __init dsa_init_module(void) -{ - int rc; - - dsa_owq = alloc_ordered_workqueue("dsa_ordered", - WQ_MEM_RECLAIM); - if (!dsa_owq) - return -ENOMEM; - - rc = dsa_slave_register_notifier(); - if (rc) - goto register_notifier_fail; - - dev_add_pack(&dsa_pack_type); - - rc = rtnl_link_register(&dsa_link_ops); - if (rc) - goto netlink_register_fail; - - return 0; - -netlink_register_fail: - dsa_slave_unregister_notifier(); - dev_remove_pack(&dsa_pack_type); -register_notifier_fail: - destroy_workqueue(dsa_owq); - - return rc; -} -module_init(dsa_init_module); - -static void __exit dsa_cleanup_module(void) -{ - rtnl_link_unregister(&dsa_link_ops); - - dsa_slave_unregister_notifier(); - dev_remove_pack(&dsa_pack_type); - destroy_workqueue(dsa_owq); -} -module_exit(dsa_cleanup_module); - -MODULE_AUTHOR("Lennert Buytenhek "); -MODULE_DESCRIPTION("Driver for Distributed Switch Architecture switch chips"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:dsa"); diff --git a/net/dsa/dsa2.c b/net/dsa/dsa2.c index 7a314c8b3aaa..7a75b0767dd1 100644 --- a/net/dsa/dsa2.c +++ b/net/dsa/dsa2.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * net/dsa/dsa2.c - Hardware switch handling, binding version 2 + * DSA topology and switch handling + * * Copyright (c) 2008-2009 Marvell Semiconductor * Copyright (c) 2013 Florian Fainelli * Copyright (c) 2016 Andrew Lunn @@ -9,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -28,9 +30,22 @@ static DEFINE_MUTEX(dsa2_mutex); LIST_HEAD(dsa_tree_list); +static struct workqueue_struct *dsa_owq; + /* Track the bridges with forwarding offload enabled */ static unsigned long dsa_fwd_offloading_bridges; +bool dsa_schedule_work(struct work_struct *work) +{ + return queue_work(dsa_owq, work); +} + +void dsa_flush_workqueue(void) +{ + flush_workqueue(dsa_owq); +} +EXPORT_SYMBOL_GPL(dsa_flush_workqueue); + /** * dsa_lag_map() - Map LAG structure to a linear LAG array * @dst: Tree in which to record the mapping. @@ -1331,6 +1346,42 @@ static int dsa_switch_parse_of(struct dsa_switch *ds, struct device_node *dn) return dsa_switch_parse_ports_of(ds, dn); } +static int dev_is_class(struct device *dev, void *class) +{ + if (dev->class != NULL && !strcmp(dev->class->name, class)) + return 1; + + return 0; +} + +static struct device *dev_find_class(struct device *parent, char *class) +{ + if (dev_is_class(parent, class)) { + get_device(parent); + return parent; + } + + return device_find_child(parent, class, dev_is_class); +} + +static struct net_device *dsa_dev_to_net_device(struct device *dev) +{ + struct device *d; + + d = dev_find_class(dev, "net"); + if (d != NULL) { + struct net_device *nd; + + nd = to_net_dev(d); + dev_hold(nd); + put_device(d); + + return nd; + } + + return NULL; +} + static int dsa_port_parse(struct dsa_port *dp, const char *name, struct device *dev) { @@ -1524,3 +1575,172 @@ out: mutex_unlock(&dsa2_mutex); } EXPORT_SYMBOL_GPL(dsa_switch_shutdown); + +#ifdef CONFIG_PM_SLEEP +static bool dsa_port_is_initialized(const struct dsa_port *dp) +{ + return dp->type == DSA_PORT_TYPE_USER && dp->slave; +} + +int dsa_switch_suspend(struct dsa_switch *ds) +{ + struct dsa_port *dp; + int ret = 0; + + /* Suspend slave network devices */ + dsa_switch_for_each_port(dp, ds) { + if (!dsa_port_is_initialized(dp)) + continue; + + ret = dsa_slave_suspend(dp->slave); + if (ret) + return ret; + } + + if (ds->ops->suspend) + ret = ds->ops->suspend(ds); + + return ret; +} +EXPORT_SYMBOL_GPL(dsa_switch_suspend); + +int dsa_switch_resume(struct dsa_switch *ds) +{ + struct dsa_port *dp; + int ret = 0; + + if (ds->ops->resume) + ret = ds->ops->resume(ds); + + if (ret) + return ret; + + /* Resume slave network devices */ + dsa_switch_for_each_port(dp, ds) { + if (!dsa_port_is_initialized(dp)) + continue; + + ret = dsa_slave_resume(dp->slave); + if (ret) + return ret; + } + + return 0; +} +EXPORT_SYMBOL_GPL(dsa_switch_resume); +#endif + +struct dsa_port *dsa_port_from_netdev(struct net_device *netdev) +{ + if (!netdev || !dsa_slave_dev_check(netdev)) + return ERR_PTR(-ENODEV); + + return dsa_slave_to_port(netdev); +} +EXPORT_SYMBOL_GPL(dsa_port_from_netdev); + +bool dsa_db_equal(const struct dsa_db *a, const struct dsa_db *b) +{ + if (a->type != b->type) + return false; + + switch (a->type) { + case DSA_DB_PORT: + return a->dp == b->dp; + case DSA_DB_LAG: + return a->lag.dev == b->lag.dev; + case DSA_DB_BRIDGE: + return a->bridge.num == b->bridge.num; + default: + WARN_ON(1); + return false; + } +} + +bool dsa_fdb_present_in_other_db(struct dsa_switch *ds, int port, + const unsigned char *addr, u16 vid, + struct dsa_db db) +{ + struct dsa_port *dp = dsa_to_port(ds, port); + struct dsa_mac_addr *a; + + lockdep_assert_held(&dp->addr_lists_lock); + + list_for_each_entry(a, &dp->fdbs, list) { + if (!ether_addr_equal(a->addr, addr) || a->vid != vid) + continue; + + if (a->db.type == db.type && !dsa_db_equal(&a->db, &db)) + return true; + } + + return false; +} +EXPORT_SYMBOL_GPL(dsa_fdb_present_in_other_db); + +bool dsa_mdb_present_in_other_db(struct dsa_switch *ds, int port, + const struct switchdev_obj_port_mdb *mdb, + struct dsa_db db) +{ + struct dsa_port *dp = dsa_to_port(ds, port); + struct dsa_mac_addr *a; + + lockdep_assert_held(&dp->addr_lists_lock); + + list_for_each_entry(a, &dp->mdbs, list) { + if (!ether_addr_equal(a->addr, mdb->addr) || a->vid != mdb->vid) + continue; + + if (a->db.type == db.type && !dsa_db_equal(&a->db, &db)) + return true; + } + + return false; +} +EXPORT_SYMBOL_GPL(dsa_mdb_present_in_other_db); + +static int __init dsa_init_module(void) +{ + int rc; + + dsa_owq = alloc_ordered_workqueue("dsa_ordered", + WQ_MEM_RECLAIM); + if (!dsa_owq) + return -ENOMEM; + + rc = dsa_slave_register_notifier(); + if (rc) + goto register_notifier_fail; + + dev_add_pack(&dsa_pack_type); + + rc = rtnl_link_register(&dsa_link_ops); + if (rc) + goto netlink_register_fail; + + return 0; + +netlink_register_fail: + dsa_slave_unregister_notifier(); + dev_remove_pack(&dsa_pack_type); +register_notifier_fail: + destroy_workqueue(dsa_owq); + + return rc; +} +module_init(dsa_init_module); + +static void __exit dsa_cleanup_module(void) +{ + rtnl_link_unregister(&dsa_link_ops); + + dsa_slave_unregister_notifier(); + dev_remove_pack(&dsa_pack_type); + destroy_workqueue(dsa_owq); +} +module_exit(dsa_cleanup_module); + +MODULE_AUTHOR("Lennert Buytenhek "); +MODULE_DESCRIPTION("Driver for Distributed Switch Architecture switch chips"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:dsa"); diff --git a/net/dsa/dsa_priv.h b/net/dsa/dsa_priv.h index e5d421cdaa8f..3f6f84150592 100644 --- a/net/dsa/dsa_priv.h +++ b/net/dsa/dsa_priv.h @@ -44,8 +44,6 @@ struct dsa_standalone_event_work { }; /* dsa.c */ -struct net_device *dsa_dev_to_net_device(struct device *dev); - bool dsa_db_equal(const struct dsa_db *a, const struct dsa_db *b); bool dsa_schedule_work(struct work_struct *work); From 47d2ce03dcfb6b7f0373aac6c667715d94caba78 Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Mon, 21 Nov 2022 15:55:52 +0200 Subject: [PATCH 1171/2002] net: dsa: rename dsa2.c back into dsa.c and create its header The previous change moved the code into the larger file (dsa2.c) to minimize the delta. Rename that now to dsa.c, and create dsa.h, where all related definitions from dsa_priv.h go. Signed-off-by: Vladimir Oltean Reviewed-by: Florian Fainelli Signed-off-by: Jakub Kicinski --- net/dsa/Makefile | 2 +- net/dsa/{dsa2.c => dsa.c} | 1 + net/dsa/dsa.h | 40 +++++++++++++++++++++++++++++++++++++++ net/dsa/dsa_priv.h | 28 --------------------------- net/dsa/master.c | 2 +- net/dsa/port.c | 2 +- net/dsa/slave.c | 1 + net/dsa/switch.c | 1 + 8 files changed, 46 insertions(+), 31 deletions(-) rename net/dsa/{dsa2.c => dsa.c} (99%) create mode 100644 net/dsa/dsa.h diff --git a/net/dsa/Makefile b/net/dsa/Makefile index f38d0f4bf76c..cc7e93a562fe 100644 --- a/net/dsa/Makefile +++ b/net/dsa/Makefile @@ -3,7 +3,7 @@ obj-$(CONFIG_NET_DSA) += dsa_core.o dsa_core-y += \ devlink.o \ - dsa2.o \ + dsa.o \ master.o \ netlink.o \ port.o \ diff --git a/net/dsa/dsa2.c b/net/dsa/dsa.c similarity index 99% rename from net/dsa/dsa2.c rename to net/dsa/dsa.c index 7a75b0767dd1..fee4d28b7304 100644 --- a/net/dsa/dsa2.c +++ b/net/dsa/dsa.c @@ -20,6 +20,7 @@ #include #include "devlink.h" +#include "dsa.h" #include "dsa_priv.h" #include "master.h" #include "port.h" diff --git a/net/dsa/dsa.h b/net/dsa/dsa.h new file mode 100644 index 000000000000..b7e17ae1094d --- /dev/null +++ b/net/dsa/dsa.h @@ -0,0 +1,40 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#ifndef __DSA_H +#define __DSA_H + +#include +#include + +struct dsa_db; +struct dsa_device_ops; +struct dsa_lag; +struct dsa_switch_tree; +struct net_device; +struct work_struct; + +extern struct list_head dsa_tree_list; + +bool dsa_db_equal(const struct dsa_db *a, const struct dsa_db *b); +bool dsa_schedule_work(struct work_struct *work); +void dsa_lag_map(struct dsa_switch_tree *dst, struct dsa_lag *lag); +void dsa_lag_unmap(struct dsa_switch_tree *dst, struct dsa_lag *lag); +struct dsa_lag *dsa_tree_lag_find(struct dsa_switch_tree *dst, + const struct net_device *lag_dev); +struct net_device *dsa_tree_find_first_master(struct dsa_switch_tree *dst); +int dsa_tree_change_tag_proto(struct dsa_switch_tree *dst, + const struct dsa_device_ops *tag_ops, + const struct dsa_device_ops *old_tag_ops); +void dsa_tree_master_admin_state_change(struct dsa_switch_tree *dst, + struct net_device *master, + bool up); +void dsa_tree_master_oper_state_change(struct dsa_switch_tree *dst, + struct net_device *master, + bool up); +unsigned int dsa_bridge_num_get(const struct net_device *bridge_dev, int max); +void dsa_bridge_num_put(const struct net_device *bridge_dev, + unsigned int bridge_num); +struct dsa_bridge *dsa_tree_bridge_find(struct dsa_switch_tree *dst, + const struct net_device *br); + +#endif diff --git a/net/dsa/dsa_priv.h b/net/dsa/dsa_priv.h index 3f6f84150592..b7ec6efe8b74 100644 --- a/net/dsa/dsa_priv.h +++ b/net/dsa/dsa_priv.h @@ -43,11 +43,6 @@ struct dsa_standalone_event_work { u16 vid; }; -/* dsa.c */ -bool dsa_db_equal(const struct dsa_db *a, const struct dsa_db *b); - -bool dsa_schedule_work(struct work_struct *work); - /* netlink.c */ extern struct rtnl_link_ops dsa_link_ops __read_mostly; @@ -65,33 +60,10 @@ static inline bool dsa_switch_supports_mc_filtering(struct dsa_switch *ds) !ds->needs_standalone_vlan_filtering; } -/* dsa2.c */ -void dsa_lag_map(struct dsa_switch_tree *dst, struct dsa_lag *lag); -void dsa_lag_unmap(struct dsa_switch_tree *dst, struct dsa_lag *lag); -struct dsa_lag *dsa_tree_lag_find(struct dsa_switch_tree *dst, - const struct net_device *lag_dev); -struct net_device *dsa_tree_find_first_master(struct dsa_switch_tree *dst); -int dsa_tree_change_tag_proto(struct dsa_switch_tree *dst, - const struct dsa_device_ops *tag_ops, - const struct dsa_device_ops *old_tag_ops); -void dsa_tree_master_admin_state_change(struct dsa_switch_tree *dst, - struct net_device *master, - bool up); -void dsa_tree_master_oper_state_change(struct dsa_switch_tree *dst, - struct net_device *master, - bool up); -unsigned int dsa_bridge_num_get(const struct net_device *bridge_dev, int max); -void dsa_bridge_num_put(const struct net_device *bridge_dev, - unsigned int bridge_num); -struct dsa_bridge *dsa_tree_bridge_find(struct dsa_switch_tree *dst, - const struct net_device *br); - /* tag_8021q.c */ int dsa_switch_tag_8021q_vlan_add(struct dsa_switch *ds, struct dsa_notifier_tag_8021q_vlan_info *info); int dsa_switch_tag_8021q_vlan_del(struct dsa_switch *ds, struct dsa_notifier_tag_8021q_vlan_info *info); -extern struct list_head dsa_tree_list; - #endif diff --git a/net/dsa/master.c b/net/dsa/master.c index e38b349ca7f8..26d90140d271 100644 --- a/net/dsa/master.c +++ b/net/dsa/master.c @@ -11,7 +11,7 @@ #include #include -#include "dsa_priv.h" +#include "dsa.h" #include "master.h" #include "port.h" #include "tag.h" diff --git a/net/dsa/port.c b/net/dsa/port.c index bf2c98215021..e6d5c05b41b4 100644 --- a/net/dsa/port.c +++ b/net/dsa/port.c @@ -12,7 +12,7 @@ #include #include -#include "dsa_priv.h" +#include "dsa.h" #include "port.h" #include "slave.h" #include "switch.h" diff --git a/net/dsa/slave.c b/net/dsa/slave.c index a928aaf68804..d4c436930a04 100644 --- a/net/dsa/slave.c +++ b/net/dsa/slave.c @@ -22,6 +22,7 @@ #include #include +#include "dsa.h" #include "dsa_priv.h" #include "port.h" #include "master.h" diff --git a/net/dsa/switch.c b/net/dsa/switch.c index b534116dc519..4420af0081af 100644 --- a/net/dsa/switch.c +++ b/net/dsa/switch.c @@ -12,6 +12,7 @@ #include #include +#include "dsa.h" #include "dsa_priv.h" #include "port.h" #include "slave.h" From 8e396fec21469610e6f1efb6ae8324e72ae8e135 Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Mon, 21 Nov 2022 15:55:53 +0200 Subject: [PATCH 1172/2002] net: dsa: move definitions from dsa_priv.h to slave.c There are some definitions in dsa_priv.h which are only used from slave.c. So move them to slave.c. Signed-off-by: Vladimir Oltean Reviewed-by: Florian Fainelli Signed-off-by: Jakub Kicinski --- net/dsa/dsa_priv.h | 42 ------------------------------------------ net/dsa/slave.c | 42 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+), 42 deletions(-) diff --git a/net/dsa/dsa_priv.h b/net/dsa/dsa_priv.h index b7ec6efe8b74..aa685d2309e0 100644 --- a/net/dsa/dsa_priv.h +++ b/net/dsa/dsa_priv.h @@ -15,51 +15,9 @@ struct dsa_notifier_tag_8021q_vlan_info; -struct dsa_switchdev_event_work { - struct net_device *dev; - struct net_device *orig_dev; - struct work_struct work; - unsigned long event; - /* Specific for SWITCHDEV_FDB_ADD_TO_DEVICE and - * SWITCHDEV_FDB_DEL_TO_DEVICE - */ - unsigned char addr[ETH_ALEN]; - u16 vid; - bool host_addr; -}; - -enum dsa_standalone_event { - DSA_UC_ADD, - DSA_UC_DEL, - DSA_MC_ADD, - DSA_MC_DEL, -}; - -struct dsa_standalone_event_work { - struct work_struct work; - struct net_device *dev; - enum dsa_standalone_event event; - unsigned char addr[ETH_ALEN]; - u16 vid; -}; - /* netlink.c */ extern struct rtnl_link_ops dsa_link_ops __read_mostly; -static inline bool dsa_switch_supports_uc_filtering(struct dsa_switch *ds) -{ - return ds->ops->port_fdb_add && ds->ops->port_fdb_del && - ds->fdb_isolation && !ds->vlan_filtering_is_global && - !ds->needs_standalone_vlan_filtering; -} - -static inline bool dsa_switch_supports_mc_filtering(struct dsa_switch *ds) -{ - return ds->ops->port_mdb_add && ds->ops->port_mdb_del && - ds->fdb_isolation && !ds->vlan_filtering_is_global && - !ds->needs_standalone_vlan_filtering; -} - /* tag_8021q.c */ int dsa_switch_tag_8021q_vlan_add(struct dsa_switch *ds, struct dsa_notifier_tag_8021q_vlan_info *info); diff --git a/net/dsa/slave.c b/net/dsa/slave.c index d4c436930a04..337cbd80633a 100644 --- a/net/dsa/slave.c +++ b/net/dsa/slave.c @@ -29,6 +29,48 @@ #include "slave.h" #include "tag.h" +struct dsa_switchdev_event_work { + struct net_device *dev; + struct net_device *orig_dev; + struct work_struct work; + unsigned long event; + /* Specific for SWITCHDEV_FDB_ADD_TO_DEVICE and + * SWITCHDEV_FDB_DEL_TO_DEVICE + */ + unsigned char addr[ETH_ALEN]; + u16 vid; + bool host_addr; +}; + +enum dsa_standalone_event { + DSA_UC_ADD, + DSA_UC_DEL, + DSA_MC_ADD, + DSA_MC_DEL, +}; + +struct dsa_standalone_event_work { + struct work_struct work; + struct net_device *dev; + enum dsa_standalone_event event; + unsigned char addr[ETH_ALEN]; + u16 vid; +}; + +static bool dsa_switch_supports_uc_filtering(struct dsa_switch *ds) +{ + return ds->ops->port_fdb_add && ds->ops->port_fdb_del && + ds->fdb_isolation && !ds->vlan_filtering_is_global && + !ds->needs_standalone_vlan_filtering; +} + +static bool dsa_switch_supports_mc_filtering(struct dsa_switch *ds) +{ + return ds->ops->port_mdb_add && ds->ops->port_mdb_del && + ds->fdb_isolation && !ds->vlan_filtering_is_global && + !ds->needs_standalone_vlan_filtering; +} + static void dsa_slave_standalone_event_work(struct work_struct *work) { struct dsa_standalone_event_work *standalone_work = From 19d05ea712ecbbb67d302664da5ec58b37b9aece Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Mon, 21 Nov 2022 15:55:54 +0200 Subject: [PATCH 1173/2002] net: dsa: move tag_8021q headers to their proper place tag_8021q definitions are all over the place. Some are exported to linux/dsa/8021q.h (visible by DSA core, taggers, switch drivers and everyone else), and some are in dsa_priv.h. Move the structures that don't need external visibility into tag_8021q.c, and the ones which don't need the world or switch drivers to see them into tag_8021q.h. We also have the tag_8021q.h inclusion from switch.c, which is basically the entire reason why tag_8021q.c was built into DSA in commit 8b6e638b4be2 ("net: dsa: build tag_8021q.c as part of DSA core"). I still don't know how to better deal with that, so leave it alone. Signed-off-by: Vladimir Oltean Reviewed-by: Florian Fainelli Signed-off-by: Jakub Kicinski --- include/linux/dsa/8021q.h | 31 +------------------------------ include/net/dsa.h | 1 + net/dsa/dsa_priv.h | 8 -------- net/dsa/port.c | 1 + net/dsa/switch.c | 1 + net/dsa/tag_8021q.c | 15 +++++++++++++++ net/dsa/tag_8021q.h | 27 +++++++++++++++++++++++++++ net/dsa/tag_ocelot_8021q.c | 1 + net/dsa/tag_sja1105.c | 1 + 9 files changed, 48 insertions(+), 38 deletions(-) create mode 100644 net/dsa/tag_8021q.h diff --git a/include/linux/dsa/8021q.h b/include/linux/dsa/8021q.h index 3ed117e299ec..f3664ee12170 100644 --- a/include/linux/dsa/8021q.h +++ b/include/linux/dsa/8021q.h @@ -5,28 +5,8 @@ #ifndef _NET_DSA_8021Q_H #define _NET_DSA_8021Q_H -#include -#include #include - -struct dsa_switch; -struct dsa_port; -struct sk_buff; -struct net_device; - -struct dsa_tag_8021q_vlan { - struct list_head list; - int port; - u16 vid; - refcount_t refcount; -}; - -struct dsa_8021q_context { - struct dsa_switch *ds; - struct list_head vlans; - /* EtherType of RX VID, used for filtering on master interface */ - __be16 proto; -}; +#include int dsa_tag_8021q_register(struct dsa_switch *ds, __be16 proto); @@ -38,15 +18,6 @@ int dsa_tag_8021q_bridge_join(struct dsa_switch *ds, int port, void dsa_tag_8021q_bridge_leave(struct dsa_switch *ds, int port, struct dsa_bridge bridge); -struct sk_buff *dsa_8021q_xmit(struct sk_buff *skb, struct net_device *netdev, - u16 tpid, u16 tci); - -void dsa_8021q_rcv(struct sk_buff *skb, int *source_port, int *switch_id, - int *vbid); - -struct net_device *dsa_tag_8021q_find_port_by_vbid(struct net_device *master, - int vbid); - u16 dsa_tag_8021q_bridge_vid(unsigned int bridge_num); u16 dsa_tag_8021q_standalone_vid(const struct dsa_port *dp); diff --git a/include/net/dsa.h b/include/net/dsa.h index d5bfcb63d4c2..96086289aa9b 100644 --- a/include/net/dsa.h +++ b/include/net/dsa.h @@ -22,6 +22,7 @@ #include #include +struct dsa_8021q_context; struct tc_action; struct phy_device; struct fixed_phy_status; diff --git a/net/dsa/dsa_priv.h b/net/dsa/dsa_priv.h index aa685d2309e0..265659954ffd 100644 --- a/net/dsa/dsa_priv.h +++ b/net/dsa/dsa_priv.h @@ -13,15 +13,7 @@ #define DSA_MAX_NUM_OFFLOADING_BRIDGES BITS_PER_LONG -struct dsa_notifier_tag_8021q_vlan_info; - /* netlink.c */ extern struct rtnl_link_ops dsa_link_ops __read_mostly; -/* tag_8021q.c */ -int dsa_switch_tag_8021q_vlan_add(struct dsa_switch *ds, - struct dsa_notifier_tag_8021q_vlan_info *info); -int dsa_switch_tag_8021q_vlan_del(struct dsa_switch *ds, - struct dsa_notifier_tag_8021q_vlan_info *info); - #endif diff --git a/net/dsa/port.c b/net/dsa/port.c index e6d5c05b41b4..67ad1adec2a2 100644 --- a/net/dsa/port.c +++ b/net/dsa/port.c @@ -16,6 +16,7 @@ #include "port.h" #include "slave.h" #include "switch.h" +#include "tag_8021q.h" /** * dsa_port_notify - Notify the switching fabric of changes to a port diff --git a/net/dsa/switch.c b/net/dsa/switch.c index 4420af0081af..e53cc0c3c933 100644 --- a/net/dsa/switch.c +++ b/net/dsa/switch.c @@ -17,6 +17,7 @@ #include "port.h" #include "slave.h" #include "switch.h" +#include "tag_8021q.h" static unsigned int dsa_switch_fastest_ageing_time(struct dsa_switch *ds, unsigned int ageing_time) diff --git a/net/dsa/tag_8021q.c b/net/dsa/tag_8021q.c index abd994dc76d5..ac2eb933106e 100644 --- a/net/dsa/tag_8021q.c +++ b/net/dsa/tag_8021q.c @@ -11,6 +11,7 @@ #include "port.h" #include "switch.h" #include "tag.h" +#include "tag_8021q.h" /* Binary structure of the fake 12-bit VID field (when the TPID is * ETH_P_DSA_8021Q): @@ -63,6 +64,20 @@ #define DSA_8021Q_PORT(x) (((x) << DSA_8021Q_PORT_SHIFT) & \ DSA_8021Q_PORT_MASK) +struct dsa_tag_8021q_vlan { + struct list_head list; + int port; + u16 vid; + refcount_t refcount; +}; + +struct dsa_8021q_context { + struct dsa_switch *ds; + struct list_head vlans; + /* EtherType of RX VID, used for filtering on master interface */ + __be16 proto; +}; + u16 dsa_tag_8021q_bridge_vid(unsigned int bridge_num) { /* The VBID value of 0 is reserved for precise TX, but it is also diff --git a/net/dsa/tag_8021q.h b/net/dsa/tag_8021q.h new file mode 100644 index 000000000000..b75cbaa028ef --- /dev/null +++ b/net/dsa/tag_8021q.h @@ -0,0 +1,27 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#ifndef __DSA_TAG_8021Q_H +#define __DSA_TAG_8021Q_H + +#include + +#include "switch.h" + +struct sk_buff; +struct net_device; + +struct sk_buff *dsa_8021q_xmit(struct sk_buff *skb, struct net_device *netdev, + u16 tpid, u16 tci); + +void dsa_8021q_rcv(struct sk_buff *skb, int *source_port, int *switch_id, + int *vbid); + +struct net_device *dsa_tag_8021q_find_port_by_vbid(struct net_device *master, + int vbid); + +int dsa_switch_tag_8021q_vlan_add(struct dsa_switch *ds, + struct dsa_notifier_tag_8021q_vlan_info *info); +int dsa_switch_tag_8021q_vlan_del(struct dsa_switch *ds, + struct dsa_notifier_tag_8021q_vlan_info *info); + +#endif diff --git a/net/dsa/tag_ocelot_8021q.c b/net/dsa/tag_ocelot_8021q.c index 7f0c2d71e89b..1f0b8c20eba5 100644 --- a/net/dsa/tag_ocelot_8021q.c +++ b/net/dsa/tag_ocelot_8021q.c @@ -12,6 +12,7 @@ #include #include "tag.h" +#include "tag_8021q.h" #define OCELOT_8021Q_NAME "ocelot-8021q" diff --git a/net/dsa/tag_sja1105.c b/net/dsa/tag_sja1105.c index 8f581617e15c..f14f51b41491 100644 --- a/net/dsa/tag_sja1105.c +++ b/net/dsa/tag_sja1105.c @@ -7,6 +7,7 @@ #include #include "tag.h" +#include "tag_8021q.h" #define SJA1105_NAME "sja1105" #define SJA1110_NAME "sja1110" From 5917bfe688672a6afc816ad472a274eb16c9bb7a Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Mon, 21 Nov 2022 15:55:55 +0200 Subject: [PATCH 1174/2002] net: dsa: kill off dsa_priv.h The last remnants in dsa_priv.h are a netlink-related definition for which we create a new header, and DSA_MAX_NUM_OFFLOADING_BRIDGES which is only used from dsa.c, so move it there. Some inclusions need to be adjusted now that we no longer have headers included transitively from dsa_priv.h. Signed-off-by: Vladimir Oltean Reviewed-by: Florian Fainelli Signed-off-by: Jakub Kicinski --- net/dsa/dsa.c | 4 +++- net/dsa/dsa_priv.h | 19 ------------------- net/dsa/netlink.c | 2 +- net/dsa/netlink.h | 8 ++++++++ net/dsa/slave.c | 2 +- net/dsa/switch.c | 2 +- net/dsa/tag_8021q.c | 1 - net/dsa/tag_hellcreek.c | 1 - 8 files changed, 14 insertions(+), 25 deletions(-) delete mode 100644 net/dsa/dsa_priv.h create mode 100644 net/dsa/netlink.h diff --git a/net/dsa/dsa.c b/net/dsa/dsa.c index fee4d28b7304..e5f156940c67 100644 --- a/net/dsa/dsa.c +++ b/net/dsa/dsa.c @@ -21,13 +21,15 @@ #include "devlink.h" #include "dsa.h" -#include "dsa_priv.h" #include "master.h" +#include "netlink.h" #include "port.h" #include "slave.h" #include "switch.h" #include "tag.h" +#define DSA_MAX_NUM_OFFLOADING_BRIDGES BITS_PER_LONG + static DEFINE_MUTEX(dsa2_mutex); LIST_HEAD(dsa_tree_list); diff --git a/net/dsa/dsa_priv.h b/net/dsa/dsa_priv.h deleted file mode 100644 index 265659954ffd..000000000000 --- a/net/dsa/dsa_priv.h +++ /dev/null @@ -1,19 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * net/dsa/dsa_priv.h - Hardware switch handling - * Copyright (c) 2008-2009 Marvell Semiconductor - */ - -#ifndef __DSA_PRIV_H -#define __DSA_PRIV_H - -#include -#include -#include - -#define DSA_MAX_NUM_OFFLOADING_BRIDGES BITS_PER_LONG - -/* netlink.c */ -extern struct rtnl_link_ops dsa_link_ops __read_mostly; - -#endif diff --git a/net/dsa/netlink.c b/net/dsa/netlink.c index 824b09d904cc..bd4bbaf851de 100644 --- a/net/dsa/netlink.c +++ b/net/dsa/netlink.c @@ -4,7 +4,7 @@ #include #include -#include "dsa_priv.h" +#include "netlink.h" #include "slave.h" static const struct nla_policy dsa_policy[IFLA_DSA_MAX + 1] = { diff --git a/net/dsa/netlink.h b/net/dsa/netlink.h new file mode 100644 index 000000000000..7eda2fa15722 --- /dev/null +++ b/net/dsa/netlink.h @@ -0,0 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#ifndef __DSA_NETLINK_H +#define __DSA_NETLINK_H + +extern struct rtnl_link_ops dsa_link_ops __read_mostly; + +#endif diff --git a/net/dsa/slave.c b/net/dsa/slave.c index 337cbd80633a..aab79c355224 100644 --- a/net/dsa/slave.c +++ b/net/dsa/slave.c @@ -23,9 +23,9 @@ #include #include "dsa.h" -#include "dsa_priv.h" #include "port.h" #include "master.h" +#include "netlink.h" #include "slave.h" #include "tag.h" diff --git a/net/dsa/switch.c b/net/dsa/switch.c index e53cc0c3c933..d5bc4bb7310d 100644 --- a/net/dsa/switch.c +++ b/net/dsa/switch.c @@ -13,7 +13,7 @@ #include #include "dsa.h" -#include "dsa_priv.h" +#include "netlink.h" #include "port.h" #include "slave.h" #include "switch.h" diff --git a/net/dsa/tag_8021q.c b/net/dsa/tag_8021q.c index ac2eb933106e..b1263917fcb2 100644 --- a/net/dsa/tag_8021q.c +++ b/net/dsa/tag_8021q.c @@ -7,7 +7,6 @@ #include #include -#include "dsa_priv.h" #include "port.h" #include "switch.h" #include "tag.h" diff --git a/net/dsa/tag_hellcreek.c b/net/dsa/tag_hellcreek.c index a047041e7686..71884296fc70 100644 --- a/net/dsa/tag_hellcreek.c +++ b/net/dsa/tag_hellcreek.c @@ -11,7 +11,6 @@ #include #include -#include "dsa_priv.h" #include "tag.h" #define HELLCREEK_NAME "hellcreek" From b535d681adda8e3bf1cb399dd1c448c8e8fd98e6 Mon Sep 17 00:00:00 2001 From: Heng Qi Date: Tue, 22 Nov 2022 11:50:14 +0800 Subject: [PATCH 1175/2002] Revert "bpf: veth driver panics when xdp prog attached before veth_open" This reverts commit 5e5dc33d5dacb34b0165061bc5a10efd2fd3b66f. This patch fixes the panic maked by 2e0de6366ac16. Now Paolo and Toke suggest reverting the patch 2e0de6366ac16 and making it stronger, so do this first. Signed-off-by: Heng Qi Reviewed-by: Xuan Zhuo Signed-off-by: Jakub Kicinski --- drivers/net/veth.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/veth.c b/drivers/net/veth.c index 2a4592780141..b1ed5a93b6c5 100644 --- a/drivers/net/veth.c +++ b/drivers/net/veth.c @@ -1125,7 +1125,7 @@ static int veth_enable_xdp(struct net_device *dev) int err, i; rq = &priv->rq[0]; - napi_already_on = rcu_access_pointer(rq->napi); + napi_already_on = (dev->flags & IFF_UP) && rcu_access_pointer(rq->napi); if (!xdp_rxq_info_is_reg(&priv->rq[0].xdp_rxq)) { err = veth_enable_xdp_range(dev, 0, dev->real_num_rx_queues, napi_already_on); From 5e8d3dc73e800027f116ec964885d761d3c00777 Mon Sep 17 00:00:00 2001 From: Heng Qi Date: Tue, 22 Nov 2022 11:50:15 +0800 Subject: [PATCH 1176/2002] Revert "veth: Avoid drop packets when xdp_redirect performs" This reverts commit 2e0de6366ac16ab4d0abb2aaddbc8a1eba216d11. Based on the issues reported by John and Paolo and their comments, this patch and the corresponding fix 5e5dc33d5da are reverted, and we'll remake it. Signed-off-by: Heng Qi Reviewed-by: Xuan Zhuo Signed-off-by: Jakub Kicinski --- drivers/net/veth.c | 90 +++++++--------------------------------------- 1 file changed, 13 insertions(+), 77 deletions(-) diff --git a/drivers/net/veth.c b/drivers/net/veth.c index b1ed5a93b6c5..ac7c0653695f 100644 --- a/drivers/net/veth.c +++ b/drivers/net/veth.c @@ -1119,14 +1119,10 @@ static void veth_disable_xdp_range(struct net_device *dev, int start, int end, static int veth_enable_xdp(struct net_device *dev) { + bool napi_already_on = veth_gro_requested(dev) && (dev->flags & IFF_UP); struct veth_priv *priv = netdev_priv(dev); - bool napi_already_on; - struct veth_rq *rq; int err, i; - rq = &priv->rq[0]; - napi_already_on = (dev->flags & IFF_UP) && rcu_access_pointer(rq->napi); - if (!xdp_rxq_info_is_reg(&priv->rq[0].xdp_rxq)) { err = veth_enable_xdp_range(dev, 0, dev->real_num_rx_queues, napi_already_on); if (err) @@ -1327,28 +1323,18 @@ revert: static int veth_open(struct net_device *dev) { - struct veth_priv *peer_priv, *priv = netdev_priv(dev); + struct veth_priv *priv = netdev_priv(dev); struct net_device *peer = rtnl_dereference(priv->peer); - struct veth_rq *peer_rq; int err; if (!peer) return -ENOTCONN; - peer_priv = netdev_priv(peer); - peer_rq = &peer_priv->rq[0]; - if (priv->_xdp_prog) { err = veth_enable_xdp(dev); if (err) return err; - /* refer to the logic in veth_xdp_set() */ - if (!rtnl_dereference(peer_rq->napi)) { - err = veth_napi_enable(peer); - if (err) - return err; - } - } else if (veth_gro_requested(dev) || peer_priv->_xdp_prog) { + } else if (veth_gro_requested(dev)) { err = veth_napi_enable(dev); if (err) return err; @@ -1364,30 +1350,18 @@ static int veth_open(struct net_device *dev) static int veth_close(struct net_device *dev) { - struct veth_priv *peer_priv, *priv = netdev_priv(dev); + struct veth_priv *priv = netdev_priv(dev); struct net_device *peer = rtnl_dereference(priv->peer); - struct veth_rq *peer_rq; netif_carrier_off(dev); - if (peer) { - peer_priv = netdev_priv(peer); - peer_rq = &peer_priv->rq[0]; - } - - if (priv->_xdp_prog) { - veth_disable_xdp(dev); - /* refer to the logic in veth_xdp_set */ - if (peer && rtnl_dereference(peer_rq->napi)) { - if (!veth_gro_requested(peer) && !peer_priv->_xdp_prog) - veth_napi_del(peer); - } - } else if (veth_gro_requested(dev) || (peer && peer_priv->_xdp_prog)) { - veth_napi_del(dev); - } - if (peer) netif_carrier_off(peer); + if (priv->_xdp_prog) + veth_disable_xdp(dev); + else if (veth_gro_requested(dev)) + veth_napi_del(dev); + return 0; } @@ -1496,21 +1470,17 @@ static int veth_set_features(struct net_device *dev, { netdev_features_t changed = features ^ dev->features; struct veth_priv *priv = netdev_priv(dev); - struct veth_rq *rq = &priv->rq[0]; int err; if (!(changed & NETIF_F_GRO) || !(dev->flags & IFF_UP) || priv->_xdp_prog) return 0; if (features & NETIF_F_GRO) { - if (!rtnl_dereference(rq->napi)) { - err = veth_napi_enable(dev); - if (err) - return err; - } + err = veth_napi_enable(dev); + if (err) + return err; } else { - if (rtnl_dereference(rq->napi)) - veth_napi_del(dev); + veth_napi_del(dev); } return 0; } @@ -1542,19 +1512,14 @@ static int veth_xdp_set(struct net_device *dev, struct bpf_prog *prog, struct netlink_ext_ack *extack) { struct veth_priv *priv = netdev_priv(dev); - struct veth_priv *peer_priv; struct bpf_prog *old_prog; - struct veth_rq *peer_rq; struct net_device *peer; - bool napi_already_off; unsigned int max_mtu; - bool noreq_napi; int err; old_prog = priv->_xdp_prog; priv->_xdp_prog = prog; peer = rtnl_dereference(priv->peer); - peer_priv = netdev_priv(peer); if (prog) { if (!peer) { @@ -1591,24 +1556,6 @@ static int veth_xdp_set(struct net_device *dev, struct bpf_prog *prog, } } - if (peer && (peer->flags & IFF_UP)) { - peer_rq = &peer_priv->rq[0]; - - /* If the peer hasn't enabled GRO and loaded xdp, - * then we enable napi automatically if its napi - * is not ready. - */ - napi_already_off = !rtnl_dereference(peer_rq->napi); - if (napi_already_off) { - err = veth_napi_enable(peer); - if (err) { - NL_SET_ERR_MSG_MOD(extack, - "Failed to automatically enable napi of peer"); - goto err; - } - } - } - if (!old_prog) { peer->hw_features &= ~NETIF_F_GSO_SOFTWARE; peer->max_mtu = max_mtu; @@ -1623,17 +1570,6 @@ static int veth_xdp_set(struct net_device *dev, struct bpf_prog *prog, if (peer) { peer->hw_features |= NETIF_F_GSO_SOFTWARE; peer->max_mtu = ETH_MAX_MTU; - peer_rq = &peer_priv->rq[0]; - - /* If the peer doesn't has its xdp and enabled - * GRO, then we disable napi if its napi is ready; - */ - if (rtnl_dereference(peer_rq->napi)) { - noreq_napi = !veth_gro_requested(peer) && - !peer_priv->_xdp_prog; - if (noreq_napi && (peer->flags & IFF_UP)) - veth_napi_del(peer); - } } } bpf_prog_put(old_prog); From c84f6f6c2bb5cd907cd1e770c5b29adb7bf2ae0e Mon Sep 17 00:00:00 2001 From: Bagas Sanjaya Date: Mon, 21 Nov 2022 10:58:55 +0700 Subject: [PATCH 1177/2002] Documentation: devlink: Add blank line padding on numbered lists in Devlink Port documentation kernel test robot reported indentation warnings: Documentation/networking/devlink/devlink-port.rst:220: WARNING: Unexpected indentation. Documentation/networking/devlink/devlink-port.rst:222: WARNING: Block quote ends without a blank line; unexpected unindent. These warnings cause lists (arbitration flow for which the warnings blame to and 3-step subfunction setup) to be rendered inline instead. Also, for the former list, automatic list numbering is messed up. Fix these warnings by adding missing blank line padding. Link: https://lore.kernel.org/linux-doc/202211200926.kfOPiVti-lkp@intel.com/ Fixes: 242dd64375b80a ("Documentation: Add documentation for new devlink-rate attributes") Reported-by: kernel test robot Signed-off-by: Bagas Sanjaya Signed-off-by: David S. Miller --- Documentation/networking/devlink/devlink-port.rst | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/Documentation/networking/devlink/devlink-port.rst b/Documentation/networking/devlink/devlink-port.rst index 643f5903d1d8..98557c2ab1c1 100644 --- a/Documentation/networking/devlink/devlink-port.rst +++ b/Documentation/networking/devlink/devlink-port.rst @@ -130,10 +130,11 @@ it is deployed. Subfunction is created and deployed in unit of 1. Unlike SRIOV VFs, a subfunction doesn't require its own PCI virtual function. A subfunction communicates with the hardware through the parent PCI function. -To use a subfunction, 3 steps setup sequence is followed. -(1) create - create a subfunction; -(2) configure - configure subfunction attributes; -(3) deploy - deploy the subfunction; +To use a subfunction, 3 steps setup sequence is followed: + +1) create - create a subfunction; +2) configure - configure subfunction attributes; +3) deploy - deploy the subfunction; Subfunction management is done using devlink port user interface. User performs setup on the subfunction management device. @@ -216,13 +217,17 @@ nodes with the same priority form a WFQ subgroup in the sibling group and arbitration among them is based on assigned weights. Arbitration flow from the high level: + #. Choose a node, or group of nodes with the highest priority that stays within the BW limit and are not blocked. Use ``tx_priority`` as a parameter for this arbitration. + #. If group of nodes have the same priority perform WFQ arbitration on that subgroup. Use ``tx_weight`` as a parameter for this arbitration. + #. Select the winner node, and continue arbitration flow among it's children, until leaf node is reached, and the winner is established. + #. If all the nodes from the highest priority sub-group are satisfied, or overused their assigned BW, move to the lower priority nodes. From 9f225444467b98579cf28d94f4ad053460dfdb84 Mon Sep 17 00:00:00 2001 From: Stanislav Fomichev Date: Mon, 21 Nov 2022 10:29:13 -0800 Subject: [PATCH 1178/2002] ppp: associate skb with a device at tx Syzkaller triggered flow dissector warning with the following: r0 = openat$ppp(0xffffffffffffff9c, &(0x7f0000000000), 0xc0802, 0x0) ioctl$PPPIOCNEWUNIT(r0, 0xc004743e, &(0x7f00000000c0)) ioctl$PPPIOCSACTIVE(r0, 0x40107446, &(0x7f0000000240)={0x2, &(0x7f0000000180)=[{0x20, 0x0, 0x0, 0xfffff034}, {0x6}]}) pwritev(r0, &(0x7f0000000040)=[{&(0x7f0000000140)='\x00!', 0x2}], 0x1, 0x0, 0x0) [ 9.485814] WARNING: CPU: 3 PID: 329 at net/core/flow_dissector.c:1016 __skb_flow_dissect+0x1ee0/0x1fa0 [ 9.485929] skb_get_poff+0x53/0xa0 [ 9.485937] bpf_skb_get_pay_offset+0xe/0x20 [ 9.485944] ? ppp_send_frame+0xc2/0x5b0 [ 9.485949] ? _raw_spin_unlock_irqrestore+0x40/0x60 [ 9.485958] ? __ppp_xmit_process+0x7a/0xe0 [ 9.485968] ? ppp_xmit_process+0x5b/0xb0 [ 9.485974] ? ppp_write+0x12a/0x190 [ 9.485981] ? do_iter_write+0x18e/0x2d0 [ 9.485987] ? __import_iovec+0x30/0x130 [ 9.485997] ? do_pwritev+0x1b6/0x240 [ 9.486016] ? trace_hardirqs_on+0x47/0x50 [ 9.486023] ? __x64_sys_pwritev+0x24/0x30 [ 9.486026] ? do_syscall_64+0x3d/0x80 [ 9.486031] ? entry_SYSCALL_64_after_hwframe+0x63/0xcd Flow dissector tries to find skb net namespace either via device or via socket. Neigher is set in ppp_send_frame, so let's manually use ppp->dev. Cc: Paul Mackerras Cc: linux-ppp@vger.kernel.org Reported-by: syzbot+41cab52ab62ee99ed24a@syzkaller.appspotmail.com Signed-off-by: Stanislav Fomichev Signed-off-by: David S. Miller --- drivers/net/ppp/ppp_generic.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/ppp/ppp_generic.c b/drivers/net/ppp/ppp_generic.c index 9206c660a72e..d4c821c8cf57 100644 --- a/drivers/net/ppp/ppp_generic.c +++ b/drivers/net/ppp/ppp_generic.c @@ -1743,6 +1743,8 @@ ppp_send_frame(struct ppp *ppp, struct sk_buff *skb) int len; unsigned char *cp; + skb->dev = ppp->dev; + if (proto < 0x8000) { #ifdef CONFIG_PPP_FILTER /* check if we should pass this packet */ From e80bd08fd75a644e2337fb535c1afdb6417357ff Mon Sep 17 00:00:00 2001 From: Edward Cree Date: Mon, 21 Nov 2022 21:37:08 +0000 Subject: [PATCH 1179/2002] sfc: ensure type is valid before updating seen_gen In the case of invalid or corrupted v2 counter update packets, efx_tc_rx_version_2() returns EFX_TC_COUNTER_TYPE_MAX. In this case we should not attempt to update generation counts as this will write beyond the end of the seen_gen array. Reported-by: coverity-bot Addresses-Coverity-ID: 1527356 ("Memory - illegal accesses") Fixes: 25730d8be5d8 ("sfc: add extra RX channel to receive MAE counter updates on ef100") Signed-off-by: Edward Cree Reviewed-by: Kees Cook Signed-off-by: David S. Miller --- drivers/net/ethernet/sfc/tc_counters.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/drivers/net/ethernet/sfc/tc_counters.c b/drivers/net/ethernet/sfc/tc_counters.c index 2bba5d3a2fdb..d1a91d54c6bb 100644 --- a/drivers/net/ethernet/sfc/tc_counters.c +++ b/drivers/net/ethernet/sfc/tc_counters.c @@ -476,13 +476,15 @@ static bool efx_tc_rx(struct efx_rx_queue *rx_queue, u32 mark) goto out; } - /* Update seen_gen unconditionally, to avoid a missed wakeup if - * we race with efx_mae_stop_counters(). - */ - efx->tc->seen_gen[type] = mark; - if (efx->tc->flush_counters && - (s32)(efx->tc->flush_gen[type] - mark) <= 0) - wake_up(&efx->tc->flush_wq); + if (type < EFX_TC_COUNTER_TYPE_MAX) { + /* Update seen_gen unconditionally, to avoid a missed wakeup if + * we race with efx_mae_stop_counters(). + */ + efx->tc->seen_gen[type] = mark; + if (efx->tc->flush_counters && + (s32)(efx->tc->flush_gen[type] - mark) <= 0) + wake_up(&efx->tc->flush_wq); + } out: efx_free_rx_buffers(rx_queue, rx_buf, 1); channel->rx_pkt_n_frags = 0; From a711a3288cc69af93148db343d3bfaf5a177a6b0 Mon Sep 17 00:00:00 2001 From: Karol Kolacinski Date: Mon, 3 Oct 2022 11:55:18 +0200 Subject: [PATCH 1180/2002] ice: Check for PTP HW lock more frequently It was observed that PTP HW semaphore can be held for ~50 ms in worst case. SW should wait longer and check more frequently if the HW lock is held. Signed-off-by: Karol Kolacinski Tested-by: Gurucharan G (A Contingent worker at Intel) Signed-off-by: Tony Nguyen --- drivers/net/ethernet/intel/ice/ice_ptp_hw.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/drivers/net/ethernet/intel/ice/ice_ptp_hw.c b/drivers/net/ethernet/intel/ice/ice_ptp_hw.c index 772b1f566d6e..1f8dd50db524 100644 --- a/drivers/net/ethernet/intel/ice/ice_ptp_hw.c +++ b/drivers/net/ethernet/intel/ice/ice_ptp_hw.c @@ -2963,16 +2963,18 @@ bool ice_ptp_lock(struct ice_hw *hw) u32 hw_lock; int i; -#define MAX_TRIES 5 +#define MAX_TRIES 15 for (i = 0; i < MAX_TRIES; i++) { hw_lock = rd32(hw, PFTSYN_SEM + (PFTSYN_SEM_BYTES * hw->pf_id)); hw_lock = hw_lock & PFTSYN_SEM_BUSY_M; - if (!hw_lock) - break; + if (hw_lock) { + /* Somebody is holding the lock */ + usleep_range(5000, 6000); + continue; + } - /* Somebody is holding the lock */ - usleep_range(10000, 20000); + break; } return !hw_lock; From 1d0e28a9be1fa02309b16dd68db31b4c8e716912 Mon Sep 17 00:00:00 2001 From: Brett Creeley Date: Mon, 31 Oct 2022 10:09:12 -0700 Subject: [PATCH 1181/2002] ice: Remove and replace ice speed defines with ethtool.h versions The driver is currently using ICE_LINK_SPEED_* defines that mirror what ethtool.h defines, with one exception ICE_LINK_SPEED_UNKNOWN. This issue is fixed by the following changes: 1. replace ICE_LINK_SPEED_UNKNOWN with 0 because SPEED_UNKNOWN in ethtool.h is "-1" and that doesn't match the driver's expected behavior 2. transform ICE_LINK_SPEED_*MBPS to SPEED_* using static tables and fls()-1 to convert from BIT() to an index in a table. Suggested-by: Alexander Lobakin Signed-off-by: Brett Creeley Co-developed-by: Jesse Brandeburg Signed-off-by: Jesse Brandeburg Tested-by: Gurucharan G (A Contingent worker at Intel) Signed-off-by: Tony Nguyen --- drivers/net/ethernet/intel/ice/ice_common.c | 41 ++++++++- drivers/net/ethernet/intel/ice/ice_common.h | 1 + .../net/ethernet/intel/ice/ice_lan_tx_rx.h | 12 --- drivers/net/ethernet/intel/ice/ice_lib.c | 32 +------ drivers/net/ethernet/intel/ice/ice_vf_mbx.c | 92 +++++-------------- 5 files changed, 69 insertions(+), 109 deletions(-) diff --git a/drivers/net/ethernet/intel/ice/ice_common.c b/drivers/net/ethernet/intel/ice/ice_common.c index 216370ec60d4..d02b55b6aa9c 100644 --- a/drivers/net/ethernet/intel/ice/ice_common.c +++ b/drivers/net/ethernet/intel/ice/ice_common.c @@ -2948,8 +2948,8 @@ bool ice_is_100m_speed_supported(struct ice_hw *hw) * Note: In the structure of [phy_type_low, phy_type_high], there should * be one bit set, as this function will convert one PHY type to its * speed. - * If no bit gets set, ICE_LINK_SPEED_UNKNOWN will be returned - * If more than one bit gets set, ICE_LINK_SPEED_UNKNOWN will be returned + * If no bit gets set, ICE_AQ_LINK_SPEED_UNKNOWN will be returned + * If more than one bit gets set, ICE_AQ_LINK_SPEED_UNKNOWN will be returned */ static u16 ice_get_link_speed_based_on_phy_type(u64 phy_type_low, u64 phy_type_high) @@ -5515,3 +5515,40 @@ bool ice_fw_supports_report_dflt_cfg(struct ice_hw *hw) ICE_FW_API_REPORT_DFLT_CFG_MIN, ICE_FW_API_REPORT_DFLT_CFG_PATCH); } + +/* each of the indexes into the following array match the speed of a return + * value from the list of AQ returned speeds like the range: + * ICE_AQ_LINK_SPEED_10MB .. ICE_AQ_LINK_SPEED_100GB excluding + * ICE_AQ_LINK_SPEED_UNKNOWN which is BIT(15) and maps to BIT(14) in this + * array. The array is defined as 15 elements long because the link_speed + * returned by the firmware is a 16 bit * value, but is indexed + * by [fls(speed) - 1] + */ +static const u32 ice_aq_to_link_speed[15] = { + SPEED_10, /* BIT(0) */ + SPEED_100, + SPEED_1000, + SPEED_2500, + SPEED_5000, + SPEED_10000, + SPEED_20000, + SPEED_25000, + SPEED_40000, + SPEED_50000, + SPEED_100000, /* BIT(10) */ + 0, + 0, + 0, + 0 /* BIT(14) */ +}; + +/** + * ice_get_link_speed - get integer speed from table + * @index: array index from fls(aq speed) - 1 + * + * Returns: u32 value containing integer speed + */ +u32 ice_get_link_speed(u16 index) +{ + return ice_aq_to_link_speed[index]; +} diff --git a/drivers/net/ethernet/intel/ice/ice_common.h b/drivers/net/ethernet/intel/ice/ice_common.h index 8b6712b92e84..4c6a0b5c9304 100644 --- a/drivers/net/ethernet/intel/ice/ice_common.h +++ b/drivers/net/ethernet/intel/ice/ice_common.h @@ -163,6 +163,7 @@ int ice_aq_sff_eeprom(struct ice_hw *hw, u16 lport, u8 bus_addr, u16 mem_addr, u8 page, u8 set_page, u8 *data, u8 length, bool write, struct ice_sq_cd *cd); +u32 ice_get_link_speed(u16 index); int ice_cfg_vsi_rdma(struct ice_port_info *pi, u16 vsi_handle, u16 tc_bitmap, diff --git a/drivers/net/ethernet/intel/ice/ice_lan_tx_rx.h b/drivers/net/ethernet/intel/ice/ice_lan_tx_rx.h index b3baf7c3f910..89f986a75cc8 100644 --- a/drivers/net/ethernet/intel/ice/ice_lan_tx_rx.h +++ b/drivers/net/ethernet/intel/ice/ice_lan_tx_rx.h @@ -908,17 +908,5 @@ static inline struct ice_rx_ptype_decoded ice_decode_rx_desc_ptype(u16 ptype) return ice_ptype_lkup[ptype]; } -#define ICE_LINK_SPEED_UNKNOWN 0 -#define ICE_LINK_SPEED_10MBPS 10 -#define ICE_LINK_SPEED_100MBPS 100 -#define ICE_LINK_SPEED_1000MBPS 1000 -#define ICE_LINK_SPEED_2500MBPS 2500 -#define ICE_LINK_SPEED_5000MBPS 5000 -#define ICE_LINK_SPEED_10000MBPS 10000 -#define ICE_LINK_SPEED_20000MBPS 20000 -#define ICE_LINK_SPEED_25000MBPS 25000 -#define ICE_LINK_SPEED_40000MBPS 40000 -#define ICE_LINK_SPEED_50000MBPS 50000 -#define ICE_LINK_SPEED_100000MBPS 100000 #endif /* _ICE_LAN_TX_RX_H_ */ diff --git a/drivers/net/ethernet/intel/ice/ice_lib.c b/drivers/net/ethernet/intel/ice/ice_lib.c index 7276badfa19e..a2cfa0c614ce 100644 --- a/drivers/net/ethernet/intel/ice/ice_lib.c +++ b/drivers/net/ethernet/intel/ice/ice_lib.c @@ -3850,33 +3850,11 @@ int ice_clear_dflt_vsi(struct ice_vsi *vsi) */ int ice_get_link_speed_mbps(struct ice_vsi *vsi) { - switch (vsi->port_info->phy.link_info.link_speed) { - case ICE_AQ_LINK_SPEED_100GB: - return SPEED_100000; - case ICE_AQ_LINK_SPEED_50GB: - return SPEED_50000; - case ICE_AQ_LINK_SPEED_40GB: - return SPEED_40000; - case ICE_AQ_LINK_SPEED_25GB: - return SPEED_25000; - case ICE_AQ_LINK_SPEED_20GB: - return SPEED_20000; - case ICE_AQ_LINK_SPEED_10GB: - return SPEED_10000; - case ICE_AQ_LINK_SPEED_5GB: - return SPEED_5000; - case ICE_AQ_LINK_SPEED_2500MB: - return SPEED_2500; - case ICE_AQ_LINK_SPEED_1000MB: - return SPEED_1000; - case ICE_AQ_LINK_SPEED_100MB: - return SPEED_100; - case ICE_AQ_LINK_SPEED_10MB: - return SPEED_10; - case ICE_AQ_LINK_SPEED_UNKNOWN: - default: - return 0; - } + unsigned int link_speed; + + link_speed = vsi->port_info->phy.link_info.link_speed; + + return (int)ice_get_link_speed(fls(link_speed) - 1); } /** diff --git a/drivers/net/ethernet/intel/ice/ice_vf_mbx.c b/drivers/net/ethernet/intel/ice/ice_vf_mbx.c index fc8c93fa4455..d4a4001b6e5d 100644 --- a/drivers/net/ethernet/intel/ice/ice_vf_mbx.c +++ b/drivers/net/ethernet/intel/ice/ice_vf_mbx.c @@ -39,6 +39,24 @@ ice_aq_send_msg_to_vf(struct ice_hw *hw, u16 vfid, u32 v_opcode, u32 v_retval, return ice_sq_send_cmd(hw, &hw->mailboxq, &desc, msg, msglen, cd); } +static const u32 ice_legacy_aq_to_vc_speed[15] = { + VIRTCHNL_LINK_SPEED_100MB, /* BIT(0) */ + VIRTCHNL_LINK_SPEED_100MB, + VIRTCHNL_LINK_SPEED_1GB, + VIRTCHNL_LINK_SPEED_1GB, + VIRTCHNL_LINK_SPEED_1GB, + VIRTCHNL_LINK_SPEED_10GB, + VIRTCHNL_LINK_SPEED_20GB, + VIRTCHNL_LINK_SPEED_25GB, + VIRTCHNL_LINK_SPEED_40GB, + VIRTCHNL_LINK_SPEED_40GB, + VIRTCHNL_LINK_SPEED_40GB, + VIRTCHNL_LINK_SPEED_UNKNOWN, + VIRTCHNL_LINK_SPEED_UNKNOWN, + VIRTCHNL_LINK_SPEED_UNKNOWN, + VIRTCHNL_LINK_SPEED_UNKNOWN /* BIT(14) */ +}; + /** * ice_conv_link_speed_to_virtchnl * @adv_link_support: determines the format of the returned link speed @@ -55,79 +73,17 @@ u32 ice_conv_link_speed_to_virtchnl(bool adv_link_support, u16 link_speed) { u32 speed; - if (adv_link_support) - switch (link_speed) { - case ICE_AQ_LINK_SPEED_10MB: - speed = ICE_LINK_SPEED_10MBPS; - break; - case ICE_AQ_LINK_SPEED_100MB: - speed = ICE_LINK_SPEED_100MBPS; - break; - case ICE_AQ_LINK_SPEED_1000MB: - speed = ICE_LINK_SPEED_1000MBPS; - break; - case ICE_AQ_LINK_SPEED_2500MB: - speed = ICE_LINK_SPEED_2500MBPS; - break; - case ICE_AQ_LINK_SPEED_5GB: - speed = ICE_LINK_SPEED_5000MBPS; - break; - case ICE_AQ_LINK_SPEED_10GB: - speed = ICE_LINK_SPEED_10000MBPS; - break; - case ICE_AQ_LINK_SPEED_20GB: - speed = ICE_LINK_SPEED_20000MBPS; - break; - case ICE_AQ_LINK_SPEED_25GB: - speed = ICE_LINK_SPEED_25000MBPS; - break; - case ICE_AQ_LINK_SPEED_40GB: - speed = ICE_LINK_SPEED_40000MBPS; - break; - case ICE_AQ_LINK_SPEED_50GB: - speed = ICE_LINK_SPEED_50000MBPS; - break; - case ICE_AQ_LINK_SPEED_100GB: - speed = ICE_LINK_SPEED_100000MBPS; - break; - default: - speed = ICE_LINK_SPEED_UNKNOWN; - break; - } - else + if (adv_link_support) { + /* convert a BIT() value into an array index */ + speed = ice_get_link_speed(fls(link_speed) - 1); + } else { /* Virtchnl speeds are not defined for every speed supported in * the hardware. To maintain compatibility with older AVF * drivers, while reporting the speed the new speed values are * resolved to the closest known virtchnl speeds */ - switch (link_speed) { - case ICE_AQ_LINK_SPEED_10MB: - case ICE_AQ_LINK_SPEED_100MB: - speed = (u32)VIRTCHNL_LINK_SPEED_100MB; - break; - case ICE_AQ_LINK_SPEED_1000MB: - case ICE_AQ_LINK_SPEED_2500MB: - case ICE_AQ_LINK_SPEED_5GB: - speed = (u32)VIRTCHNL_LINK_SPEED_1GB; - break; - case ICE_AQ_LINK_SPEED_10GB: - speed = (u32)VIRTCHNL_LINK_SPEED_10GB; - break; - case ICE_AQ_LINK_SPEED_20GB: - speed = (u32)VIRTCHNL_LINK_SPEED_20GB; - break; - case ICE_AQ_LINK_SPEED_25GB: - speed = (u32)VIRTCHNL_LINK_SPEED_25GB; - break; - case ICE_AQ_LINK_SPEED_40GB: - case ICE_AQ_LINK_SPEED_50GB: - case ICE_AQ_LINK_SPEED_100GB: - speed = (u32)VIRTCHNL_LINK_SPEED_40GB; - break; - default: - speed = (u32)VIRTCHNL_LINK_SPEED_UNKNOWN; - break; - } + speed = ice_legacy_aq_to_vc_speed[fls(link_speed) - 1]; + } return speed; } From 2fd5e433cd2685d4af471209e48be1a951687193 Mon Sep 17 00:00:00 2001 From: Benjamin Mikailenko Date: Fri, 18 Nov 2022 16:20:01 -0500 Subject: [PATCH 1182/2002] ice: Accumulate HW and Netdev statistics over reset Resets happen with or without user interaction. For example, incidents such as TX hang or a reconfiguration of parameters will result in a reset. During reset, hardware and software statistics were set to zero. This created an issue for the user where a reset happens in the background, statistics set to zero, and the user checks statistics expecting them to be populated. To ensure this doesn't happen, keep accumulating stats over reset. 1. Remove function calls which reset hardware and netdev statistics. 2. Do not rollover statistics in ice_stat_update40 during reset. Signed-off-by: Benjamin Mikailenko Tested-by: Gurucharan G (A Contingent worker at Intel) Signed-off-by: Tony Nguyen --- drivers/net/ethernet/intel/ice/ice.h | 1 + drivers/net/ethernet/intel/ice/ice_dcb_lib.c | 3 ++ drivers/net/ethernet/intel/ice/ice_lib.c | 7 +++++ drivers/net/ethernet/intel/ice/ice_main.c | 30 +++++++++++++++++--- 4 files changed, 37 insertions(+), 4 deletions(-) diff --git a/drivers/net/ethernet/intel/ice/ice.h b/drivers/net/ethernet/intel/ice/ice.h index f88ee051e71c..74d40a766664 100644 --- a/drivers/net/ethernet/intel/ice/ice.h +++ b/drivers/net/ethernet/intel/ice/ice.h @@ -373,6 +373,7 @@ struct ice_vsi { /* VSI stats */ struct rtnl_link_stats64 net_stats; + struct rtnl_link_stats64 net_stats_prev; struct ice_eth_stats eth_stats; struct ice_eth_stats eth_stats_prev; diff --git a/drivers/net/ethernet/intel/ice/ice_dcb_lib.c b/drivers/net/ethernet/intel/ice/ice_dcb_lib.c index 9defb9d0fe88..4f24d441c35e 100644 --- a/drivers/net/ethernet/intel/ice/ice_dcb_lib.c +++ b/drivers/net/ethernet/intel/ice/ice_dcb_lib.c @@ -881,6 +881,9 @@ void ice_update_dcb_stats(struct ice_pf *pf) prev_ps = &pf->stats_prev; cur_ps = &pf->stats; + if (ice_is_reset_in_progress(pf->state)) + pf->stat_prev_loaded = false; + for (i = 0; i < 8; i++) { ice_stat_update32(hw, GLPRT_PXOFFRXC(port, i), pf->stat_prev_loaded, diff --git a/drivers/net/ethernet/intel/ice/ice_lib.c b/drivers/net/ethernet/intel/ice/ice_lib.c index a2cfa0c614ce..ffb455e1f3dc 100644 --- a/drivers/net/ethernet/intel/ice/ice_lib.c +++ b/drivers/net/ethernet/intel/ice/ice_lib.c @@ -1795,11 +1795,15 @@ void ice_update_eth_stats(struct ice_vsi *vsi) { struct ice_eth_stats *prev_es, *cur_es; struct ice_hw *hw = &vsi->back->hw; + struct ice_pf *pf = vsi->back; u16 vsi_num = vsi->vsi_num; /* HW absolute index of a VSI */ prev_es = &vsi->eth_stats_prev; cur_es = &vsi->eth_stats; + if (ice_is_reset_in_progress(pf->state)) + vsi->stat_offsets_loaded = false; + ice_stat_update40(hw, GLV_GORCL(vsi_num), vsi->stat_offsets_loaded, &prev_es->rx_bytes, &cur_es->rx_bytes); @@ -3304,6 +3308,8 @@ int ice_vsi_rebuild(struct ice_vsi *vsi, bool init_vsi) goto err_vectors; ice_vsi_map_rings_to_vectors(vsi); + + vsi->stat_offsets_loaded = false; if (ice_is_xdp_ena_vsi(vsi)) { ret = ice_vsi_determine_xdp_res(vsi); if (ret) @@ -3340,6 +3346,7 @@ int ice_vsi_rebuild(struct ice_vsi *vsi, bool init_vsi) if (ret) goto err_vectors; + vsi->stat_offsets_loaded = false; break; case ICE_VSI_CHNL: if (test_bit(ICE_FLAG_RSS_ENA, pf->flags)) { diff --git a/drivers/net/ethernet/intel/ice/ice_main.c b/drivers/net/ethernet/intel/ice/ice_main.c index d6f460ff1b72..aa80e2b896b1 100644 --- a/drivers/net/ethernet/intel/ice/ice_main.c +++ b/drivers/net/ethernet/intel/ice/ice_main.c @@ -6397,6 +6397,7 @@ ice_update_vsi_tx_ring_stats(struct ice_vsi *vsi, */ static void ice_update_vsi_ring_stats(struct ice_vsi *vsi) { + struct rtnl_link_stats64 *net_stats, *stats_prev; struct rtnl_link_stats64 *vsi_stats; u64 pkts, bytes; int i; @@ -6436,10 +6437,28 @@ static void ice_update_vsi_ring_stats(struct ice_vsi *vsi) rcu_read_unlock(); - vsi->net_stats.tx_packets = vsi_stats->tx_packets; - vsi->net_stats.tx_bytes = vsi_stats->tx_bytes; - vsi->net_stats.rx_packets = vsi_stats->rx_packets; - vsi->net_stats.rx_bytes = vsi_stats->rx_bytes; + net_stats = &vsi->net_stats; + stats_prev = &vsi->net_stats_prev; + + /* clear prev counters after reset */ + if (vsi_stats->tx_packets < stats_prev->tx_packets || + vsi_stats->rx_packets < stats_prev->rx_packets) { + stats_prev->tx_packets = 0; + stats_prev->tx_bytes = 0; + stats_prev->rx_packets = 0; + stats_prev->rx_bytes = 0; + } + + /* update netdev counters */ + net_stats->tx_packets += vsi_stats->tx_packets - stats_prev->tx_packets; + net_stats->tx_bytes += vsi_stats->tx_bytes - stats_prev->tx_bytes; + net_stats->rx_packets += vsi_stats->rx_packets - stats_prev->rx_packets; + net_stats->rx_bytes += vsi_stats->rx_bytes - stats_prev->rx_bytes; + + stats_prev->tx_packets = vsi_stats->tx_packets; + stats_prev->tx_bytes = vsi_stats->tx_bytes; + stats_prev->rx_packets = vsi_stats->rx_packets; + stats_prev->rx_bytes = vsi_stats->rx_bytes; kfree(vsi_stats); } @@ -6501,6 +6520,9 @@ void ice_update_pf_stats(struct ice_pf *pf) prev_ps = &pf->stats_prev; cur_ps = &pf->stats; + if (ice_is_reset_in_progress(pf->state)) + pf->stat_prev_loaded = false; + ice_stat_update40(hw, GLPRT_GORCL(port), pf->stat_prev_loaded, &prev_ps->eth.rx_bytes, &cur_ps->eth.rx_bytes); From 288ecf491b1654845ae99c79b7fefad2d3ea47bd Mon Sep 17 00:00:00 2001 From: Benjamin Mikailenko Date: Fri, 18 Nov 2022 16:20:02 -0500 Subject: [PATCH 1183/2002] ice: Accumulate ring statistics over reset Resets may occur with or without user interaction. For example, a TX hang or reconfiguration of parameters will result in a reset. During reset, the VSI is freed, freeing any statistics structures inside as well. This would create an issue for the user where a reset happens in the background, statistics set to zero, and the user checks ring statistics expecting them to be populated. To ensure this doesn't happen, accumulate ring statistics over reset. Define a new ring statistics structure, ice_ring_stats. The new structure lives in the VSI's parent, preserving ring statistics when VSI is freed. 1. Define a new structure vsi_ring_stats in the PF scope 2. Allocate/free stats only during probe, unload, or change in ring size 3. Replace previous ring statistics functionality with new structure Signed-off-by: Benjamin Mikailenko Tested-by: Gurucharan G (A Contingent worker at Intel) Signed-off-by: Tony Nguyen --- drivers/net/ethernet/intel/ice/ice.h | 6 + drivers/net/ethernet/intel/ice/ice_ethtool.c | 12 +- drivers/net/ethernet/intel/ice/ice_lib.c | 233 +++++++++++++++++- drivers/net/ethernet/intel/ice/ice_main.c | 66 +++-- drivers/net/ethernet/intel/ice/ice_repr.c | 10 +- drivers/net/ethernet/intel/ice/ice_txrx.c | 40 +-- drivers/net/ethernet/intel/ice/ice_txrx.h | 18 +- drivers/net/ethernet/intel/ice/ice_txrx_lib.c | 2 +- drivers/net/ethernet/intel/ice/ice_xsk.c | 25 +- 9 files changed, 351 insertions(+), 61 deletions(-) diff --git a/drivers/net/ethernet/intel/ice/ice.h b/drivers/net/ethernet/intel/ice/ice.h index 74d40a766664..2f0b604abc5e 100644 --- a/drivers/net/ethernet/intel/ice/ice.h +++ b/drivers/net/ethernet/intel/ice/ice.h @@ -320,6 +320,11 @@ enum ice_vsi_state { ICE_VSI_STATE_NBITS /* must be last */ }; +struct ice_vsi_stats { + struct ice_ring_stats **tx_ring_stats; /* Tx ring stats array */ + struct ice_ring_stats **rx_ring_stats; /* Rx ring stats array */ +}; + /* struct that defines a VSI, associated with a dev */ struct ice_vsi { struct net_device *netdev; @@ -541,6 +546,7 @@ struct ice_pf { u16 ctrl_vsi_idx; /* control VSI index in pf->vsi array */ struct ice_vsi **vsi; /* VSIs created by the driver */ + struct ice_vsi_stats **vsi_stats; struct ice_sw *first_sw; /* first switch created by firmware */ u16 eswitch_mode; /* current mode of eswitch */ struct ice_vfs vfs; diff --git a/drivers/net/ethernet/intel/ice/ice_ethtool.c b/drivers/net/ethernet/intel/ice/ice_ethtool.c index f71a7521c7bd..4191994d8f3a 100644 --- a/drivers/net/ethernet/intel/ice/ice_ethtool.c +++ b/drivers/net/ethernet/intel/ice/ice_ethtool.c @@ -1544,9 +1544,9 @@ __ice_get_ethtool_stats(struct net_device *netdev, ice_for_each_alloc_txq(vsi, j) { tx_ring = READ_ONCE(vsi->tx_rings[j]); - if (tx_ring) { - data[i++] = tx_ring->stats.pkts; - data[i++] = tx_ring->stats.bytes; + if (tx_ring && tx_ring->ring_stats) { + data[i++] = tx_ring->ring_stats->stats.pkts; + data[i++] = tx_ring->ring_stats->stats.bytes; } else { data[i++] = 0; data[i++] = 0; @@ -1555,9 +1555,9 @@ __ice_get_ethtool_stats(struct net_device *netdev, ice_for_each_alloc_rxq(vsi, j) { rx_ring = READ_ONCE(vsi->rx_rings[j]); - if (rx_ring) { - data[i++] = rx_ring->stats.pkts; - data[i++] = rx_ring->stats.bytes; + if (rx_ring && rx_ring->ring_stats) { + data[i++] = rx_ring->ring_stats->stats.pkts; + data[i++] = rx_ring->ring_stats->stats.bytes; } else { data[i++] = 0; data[i++] = 0; diff --git a/drivers/net/ethernet/intel/ice/ice_lib.c b/drivers/net/ethernet/intel/ice/ice_lib.c index ffb455e1f3dc..94aa834cd9a6 100644 --- a/drivers/net/ethernet/intel/ice/ice_lib.c +++ b/drivers/net/ethernet/intel/ice/ice_lib.c @@ -447,6 +447,49 @@ static irqreturn_t ice_eswitch_msix_clean_rings(int __always_unused irq, void *d return IRQ_HANDLED; } +/** + * ice_vsi_alloc_stat_arrays - Allocate statistics arrays + * @vsi: VSI pointer + */ +static int ice_vsi_alloc_stat_arrays(struct ice_vsi *vsi) +{ + struct ice_vsi_stats *vsi_stat; + struct ice_pf *pf = vsi->back; + + if (vsi->type == ICE_VSI_CHNL) + return 0; + if (!pf->vsi_stats) + return -ENOENT; + + vsi_stat = kzalloc(sizeof(*vsi_stat), GFP_KERNEL); + if (!vsi_stat) + return -ENOMEM; + + vsi_stat->tx_ring_stats = + kcalloc(vsi->alloc_txq, sizeof(*vsi_stat->tx_ring_stats), + GFP_KERNEL); + if (!vsi_stat->tx_ring_stats) + goto err_alloc_tx; + + vsi_stat->rx_ring_stats = + kcalloc(vsi->alloc_rxq, sizeof(*vsi_stat->rx_ring_stats), + GFP_KERNEL); + if (!vsi_stat->rx_ring_stats) + goto err_alloc_rx; + + pf->vsi_stats[vsi->idx] = vsi_stat; + + return 0; + +err_alloc_rx: + kfree(vsi_stat->rx_ring_stats); +err_alloc_tx: + kfree(vsi_stat->tx_ring_stats); + kfree(vsi_stat); + pf->vsi_stats[vsi->idx] = NULL; + return -ENOMEM; +} + /** * ice_vsi_alloc - Allocates the next available struct VSI in the PF * @pf: board private structure @@ -560,6 +603,11 @@ ice_vsi_alloc(struct ice_pf *pf, enum ice_vsi_type vsi_type, if (vsi->type == ICE_VSI_CTRL && vf) vf->ctrl_vsi_idx = vsi->idx; + + /* allocate memory for Tx/Rx ring stat pointers */ + if (ice_vsi_alloc_stat_arrays(vsi)) + goto err_rings; + goto unlock_pf; err_rings: @@ -1535,6 +1583,106 @@ err_out: return -ENOMEM; } +/** + * ice_vsi_free_stats - Free the ring statistics structures + * @vsi: VSI pointer + */ +static void ice_vsi_free_stats(struct ice_vsi *vsi) +{ + struct ice_vsi_stats *vsi_stat; + struct ice_pf *pf = vsi->back; + int i; + + if (vsi->type == ICE_VSI_CHNL) + return; + if (!pf->vsi_stats) + return; + + vsi_stat = pf->vsi_stats[vsi->idx]; + if (!vsi_stat) + return; + + ice_for_each_alloc_txq(vsi, i) { + if (vsi_stat->tx_ring_stats[i]) { + kfree_rcu(vsi_stat->tx_ring_stats[i], rcu); + WRITE_ONCE(vsi_stat->tx_ring_stats[i], NULL); + } + } + + ice_for_each_alloc_rxq(vsi, i) { + if (vsi_stat->rx_ring_stats[i]) { + kfree_rcu(vsi_stat->rx_ring_stats[i], rcu); + WRITE_ONCE(vsi_stat->rx_ring_stats[i], NULL); + } + } + + kfree(vsi_stat->tx_ring_stats); + kfree(vsi_stat->rx_ring_stats); + kfree(vsi_stat); + pf->vsi_stats[vsi->idx] = NULL; +} + +/** + * ice_vsi_alloc_ring_stats - Allocates Tx and Rx ring stats for the VSI + * @vsi: VSI which is having stats allocated + */ +static int ice_vsi_alloc_ring_stats(struct ice_vsi *vsi) +{ + struct ice_ring_stats **tx_ring_stats; + struct ice_ring_stats **rx_ring_stats; + struct ice_vsi_stats *vsi_stats; + struct ice_pf *pf = vsi->back; + u16 i; + + vsi_stats = pf->vsi_stats[vsi->idx]; + tx_ring_stats = vsi_stats->tx_ring_stats; + rx_ring_stats = vsi_stats->rx_ring_stats; + + /* Allocate Tx ring stats */ + ice_for_each_alloc_txq(vsi, i) { + struct ice_ring_stats *ring_stats; + struct ice_tx_ring *ring; + + ring = vsi->tx_rings[i]; + ring_stats = tx_ring_stats[i]; + + if (!ring_stats) { + ring_stats = kzalloc(sizeof(*ring_stats), GFP_KERNEL); + if (!ring_stats) + goto err_out; + + WRITE_ONCE(tx_ring_stats[i], ring_stats); + } + + ring->ring_stats = ring_stats; + } + + /* Allocate Rx ring stats */ + ice_for_each_alloc_rxq(vsi, i) { + struct ice_ring_stats *ring_stats; + struct ice_rx_ring *ring; + + ring = vsi->rx_rings[i]; + ring_stats = rx_ring_stats[i]; + + if (!ring_stats) { + ring_stats = kzalloc(sizeof(*ring_stats), GFP_KERNEL); + if (!ring_stats) + goto err_out; + + WRITE_ONCE(rx_ring_stats[i], ring_stats); + } + + ring->ring_stats = ring_stats; + } + + return 0; + +err_out: + ice_vsi_free_stats(vsi); + return -ENOMEM; +} + /** * ice_vsi_manage_rss_lut - disable/enable RSS * @vsi: the VSI being changed @@ -2580,6 +2728,10 @@ ice_vsi_setup(struct ice_pf *pf, struct ice_port_info *pi, if (ret) goto unroll_vector_base; + ret = ice_vsi_alloc_ring_stats(vsi); + if (ret) + goto unroll_vector_base; + ice_vsi_map_rings_to_vectors(vsi); /* ICE_VSI_CTRL does not need RSS so skip RSS processing */ @@ -2618,6 +2770,9 @@ ice_vsi_setup(struct ice_pf *pf, struct ice_port_info *pi, if (ret) goto unroll_vector_base; + ret = ice_vsi_alloc_ring_stats(vsi); + if (ret) + goto unroll_vector_base; /* Do not exit if configuring RSS had an issue, at least * receive traffic on first queue. Hence no need to capture * return value @@ -2631,6 +2786,11 @@ ice_vsi_setup(struct ice_pf *pf, struct ice_port_info *pi, ret = ice_vsi_alloc_rings(vsi); if (ret) goto unroll_vsi_init; + + ret = ice_vsi_alloc_ring_stats(vsi); + if (ret) + goto unroll_vector_base; + break; default: /* clean up the resources and exit */ @@ -2690,6 +2850,7 @@ unroll_vector_base: unroll_alloc_q_vector: ice_vsi_free_q_vectors(vsi); unroll_vsi_init: + ice_vsi_free_stats(vsi); ice_vsi_delete(vsi); unroll_get_qs: ice_vsi_put_qs(vsi); @@ -3081,7 +3242,7 @@ int ice_vsi_release(struct ice_vsi *vsi) vsi->agg_node && vsi->agg_node->valid) vsi->agg_node->num_vsis--; ice_vsi_clear_rings(vsi); - + ice_vsi_free_stats(vsi); ice_vsi_put_qs(vsi); /* retain SW VSI data structure since it is needed to unregister and @@ -3208,6 +3369,47 @@ ice_vsi_rebuild_set_coalesce(struct ice_vsi *vsi, } } +/** + * ice_vsi_realloc_stat_arrays - Frees unused stat structures + * @vsi: VSI pointer + * @prev_txq: Number of Tx rings before ring reallocation + * @prev_rxq: Number of Rx rings before ring reallocation + */ +static int +ice_vsi_realloc_stat_arrays(struct ice_vsi *vsi, int prev_txq, int prev_rxq) +{ + struct ice_vsi_stats *vsi_stat; + struct ice_pf *pf = vsi->back; + int i; + + if (!prev_txq || !prev_rxq) + return 0; + if (vsi->type == ICE_VSI_CHNL) + return 0; + + vsi_stat = pf->vsi_stats[vsi->idx]; + + if (vsi->num_txq < prev_txq) { + for (i = vsi->num_txq; i < prev_txq; i++) { + if (vsi_stat->tx_ring_stats[i]) { + kfree_rcu(vsi_stat->tx_ring_stats[i], rcu); + WRITE_ONCE(vsi_stat->tx_ring_stats[i], NULL); + } + } + } + + if (vsi->num_rxq < prev_rxq) { + for (i = vsi->num_rxq; i < prev_rxq; i++) { + if (vsi_stat->rx_ring_stats[i]) { + kfree_rcu(vsi_stat->rx_ring_stats[i], rcu); + WRITE_ONCE(vsi_stat->rx_ring_stats[i], NULL); + } + } + } + + return 0; +} + /** * ice_vsi_rebuild - Rebuild VSI after reset * @vsi: VSI to be rebuild @@ -3219,10 +3421,10 @@ int ice_vsi_rebuild(struct ice_vsi *vsi, bool init_vsi) { u16 max_txqs[ICE_MAX_TRAFFIC_CLASS] = { 0 }; struct ice_coalesce_stored *coalesce; + int ret, i, prev_txq, prev_rxq; int prev_num_q_vectors = 0; enum ice_vsi_type vtype; struct ice_pf *pf; - int ret, i; if (!vsi) return -EINVAL; @@ -3241,6 +3443,9 @@ int ice_vsi_rebuild(struct ice_vsi *vsi, bool init_vsi) prev_num_q_vectors = ice_vsi_rebuild_get_coalesce(vsi, coalesce); + prev_txq = vsi->num_txq; + prev_rxq = vsi->num_rxq; + ice_rm_vsi_lan_cfg(vsi->port_info, vsi->idx); ret = ice_rm_vsi_rdma_cfg(vsi->port_info, vsi->idx); if (ret) @@ -3307,6 +3512,10 @@ int ice_vsi_rebuild(struct ice_vsi *vsi, bool init_vsi) if (ret) goto err_vectors; + ret = ice_vsi_alloc_ring_stats(vsi); + if (ret) + goto err_vectors; + ice_vsi_map_rings_to_vectors(vsi); vsi->stat_offsets_loaded = false; @@ -3346,6 +3555,10 @@ int ice_vsi_rebuild(struct ice_vsi *vsi, bool init_vsi) if (ret) goto err_vectors; + ret = ice_vsi_alloc_ring_stats(vsi); + if (ret) + goto err_vectors; + vsi->stat_offsets_loaded = false; break; case ICE_VSI_CHNL: @@ -3394,6 +3607,10 @@ int ice_vsi_rebuild(struct ice_vsi *vsi, bool init_vsi) return ice_schedule_reset(pf, ICE_RESET_PFR); } } + + if (ice_vsi_realloc_stat_arrays(vsi, prev_txq, prev_rxq)) + goto err_vectors; + ice_vsi_rebuild_set_coalesce(vsi, coalesce, prev_num_q_vectors); kfree(coalesce); @@ -3735,9 +3952,9 @@ static void ice_update_ring_stats(struct ice_q_stats *stats, u64 pkts, u64 bytes */ void ice_update_tx_ring_stats(struct ice_tx_ring *tx_ring, u64 pkts, u64 bytes) { - u64_stats_update_begin(&tx_ring->syncp); - ice_update_ring_stats(&tx_ring->stats, pkts, bytes); - u64_stats_update_end(&tx_ring->syncp); + u64_stats_update_begin(&tx_ring->ring_stats->syncp); + ice_update_ring_stats(&tx_ring->ring_stats->stats, pkts, bytes); + u64_stats_update_end(&tx_ring->ring_stats->syncp); } /** @@ -3748,9 +3965,9 @@ void ice_update_tx_ring_stats(struct ice_tx_ring *tx_ring, u64 pkts, u64 bytes) */ void ice_update_rx_ring_stats(struct ice_rx_ring *rx_ring, u64 pkts, u64 bytes) { - u64_stats_update_begin(&rx_ring->syncp); - ice_update_ring_stats(&rx_ring->stats, pkts, bytes); - u64_stats_update_end(&rx_ring->syncp); + u64_stats_update_begin(&rx_ring->ring_stats->syncp); + ice_update_ring_stats(&rx_ring->ring_stats->stats, pkts, bytes); + u64_stats_update_end(&rx_ring->ring_stats->syncp); } /** diff --git a/drivers/net/ethernet/intel/ice/ice_main.c b/drivers/net/ethernet/intel/ice/ice_main.c index aa80e2b896b1..a21085c0cfee 100644 --- a/drivers/net/ethernet/intel/ice/ice_main.c +++ b/drivers/net/ethernet/intel/ice/ice_main.c @@ -130,12 +130,17 @@ static void ice_check_for_hang_subtask(struct ice_pf *pf) ice_for_each_txq(vsi, i) { struct ice_tx_ring *tx_ring = vsi->tx_rings[i]; + struct ice_ring_stats *ring_stats; if (!tx_ring) continue; if (ice_ring_ch_enabled(tx_ring)) continue; + ring_stats = tx_ring->ring_stats; + if (!ring_stats) + continue; + if (tx_ring->desc) { /* If packet counter has not changed the queue is * likely stalled, so force an interrupt for this @@ -144,8 +149,8 @@ static void ice_check_for_hang_subtask(struct ice_pf *pf) * prev_pkt would be negative if there was no * pending work. */ - packets = tx_ring->stats.pkts & INT_MAX; - if (tx_ring->tx_stats.prev_pkt == packets) { + packets = ring_stats->stats.pkts & INT_MAX; + if (ring_stats->tx_stats.prev_pkt == packets) { /* Trigger sw interrupt to revive the queue */ ice_trigger_sw_intr(hw, tx_ring->q_vector); continue; @@ -155,7 +160,7 @@ static void ice_check_for_hang_subtask(struct ice_pf *pf) * to ice_get_tx_pending() */ smp_rmb(); - tx_ring->tx_stats.prev_pkt = + ring_stats->tx_stats.prev_pkt = ice_get_tx_pending(tx_ring) ? packets : -1; } } @@ -2546,13 +2551,20 @@ static int ice_xdp_alloc_setup_rings(struct ice_vsi *vsi) ice_for_each_xdp_txq(vsi, i) { u16 xdp_q_idx = vsi->alloc_txq + i; + struct ice_ring_stats *ring_stats; struct ice_tx_ring *xdp_ring; xdp_ring = kzalloc(sizeof(*xdp_ring), GFP_KERNEL); - if (!xdp_ring) goto free_xdp_rings; + ring_stats = kzalloc(sizeof(*ring_stats), GFP_KERNEL); + if (!ring_stats) { + ice_free_tx_ring(xdp_ring); + goto free_xdp_rings; + } + + xdp_ring->ring_stats = ring_stats; xdp_ring->q_index = xdp_q_idx; xdp_ring->reg_idx = vsi->txq_map[xdp_q_idx]; xdp_ring->vsi = vsi; @@ -2575,9 +2587,13 @@ static int ice_xdp_alloc_setup_rings(struct ice_vsi *vsi) return 0; free_xdp_rings: - for (; i >= 0; i--) - if (vsi->xdp_rings[i] && vsi->xdp_rings[i]->desc) + for (; i >= 0; i--) { + if (vsi->xdp_rings[i] && vsi->xdp_rings[i]->desc) { + kfree_rcu(vsi->xdp_rings[i]->ring_stats, rcu); + vsi->xdp_rings[i]->ring_stats = NULL; ice_free_tx_ring(vsi->xdp_rings[i]); + } + } return -ENOMEM; } @@ -2778,6 +2794,8 @@ free_qmap: synchronize_rcu(); ice_free_tx_ring(vsi->xdp_rings[i]); } + kfree_rcu(vsi->xdp_rings[i]->ring_stats, rcu); + vsi->xdp_rings[i]->ring_stats = NULL; kfree_rcu(vsi->xdp_rings[i], rcu); vsi->xdp_rings[i] = NULL; } @@ -4756,11 +4774,18 @@ ice_probe(struct pci_dev *pdev, const struct pci_device_id __always_unused *ent) goto err_init_pf_unroll; } + pf->vsi_stats = devm_kcalloc(dev, pf->num_alloc_vsi, + sizeof(*pf->vsi_stats), GFP_KERNEL); + if (!pf->vsi_stats) { + err = -ENOMEM; + goto err_init_vsi_unroll; + } + err = ice_init_interrupt_scheme(pf); if (err) { dev_err(dev, "ice_init_interrupt_scheme failed: %d\n", err); err = -EIO; - goto err_init_vsi_unroll; + goto err_init_vsi_stats_unroll; } /* In case of MSIX we are going to setup the misc vector right here @@ -4941,6 +4966,9 @@ err_msix_misc_unroll: ice_free_irq_msix_misc(pf); err_init_interrupt_unroll: ice_clear_interrupt_scheme(pf); +err_init_vsi_stats_unroll: + devm_kfree(dev, pf->vsi_stats); + pf->vsi_stats = NULL; err_init_vsi_unroll: devm_kfree(dev, pf->vsi); err_init_pf_unroll: @@ -5063,6 +5091,8 @@ static void ice_remove(struct pci_dev *pdev) continue; ice_vsi_free_q_vectors(pf->vsi[i]); } + devm_kfree(&pdev->dev, pf->vsi_stats); + pf->vsi_stats = NULL; ice_deinit_pf(pf); ice_devlink_destroy_regions(pf); ice_deinit_hw(&pf->hw); @@ -6380,14 +6410,16 @@ ice_update_vsi_tx_ring_stats(struct ice_vsi *vsi, u64 pkts = 0, bytes = 0; ring = READ_ONCE(rings[i]); - if (!ring) + if (!ring || !ring->ring_stats) continue; - ice_fetch_u64_stats_per_ring(&ring->syncp, ring->stats, &pkts, &bytes); + ice_fetch_u64_stats_per_ring(&ring->ring_stats->syncp, + ring->ring_stats->stats, &pkts, + &bytes); vsi_stats->tx_packets += pkts; vsi_stats->tx_bytes += bytes; - vsi->tx_restart += ring->tx_stats.restart_q; - vsi->tx_busy += ring->tx_stats.tx_busy; - vsi->tx_linearize += ring->tx_stats.tx_linearize; + vsi->tx_restart += ring->ring_stats->tx_stats.restart_q; + vsi->tx_busy += ring->ring_stats->tx_stats.tx_busy; + vsi->tx_linearize += ring->ring_stats->tx_stats.tx_linearize; } } @@ -6422,12 +6454,16 @@ static void ice_update_vsi_ring_stats(struct ice_vsi *vsi) /* update Rx rings counters */ ice_for_each_rxq(vsi, i) { struct ice_rx_ring *ring = READ_ONCE(vsi->rx_rings[i]); + struct ice_ring_stats *ring_stats; - ice_fetch_u64_stats_per_ring(&ring->syncp, ring->stats, &pkts, &bytes); + ring_stats = ring->ring_stats; + ice_fetch_u64_stats_per_ring(&ring_stats->syncp, + ring_stats->stats, &pkts, + &bytes); vsi_stats->rx_packets += pkts; vsi_stats->rx_bytes += bytes; - vsi->rx_buf_failed += ring->rx_stats.alloc_buf_failed; - vsi->rx_page_failed += ring->rx_stats.alloc_page_failed; + vsi->rx_buf_failed += ring_stats->rx_stats.alloc_buf_failed; + vsi->rx_page_failed += ring_stats->rx_stats.alloc_page_failed; } /* update XDP Tx rings counters */ diff --git a/drivers/net/ethernet/intel/ice/ice_repr.c b/drivers/net/ethernet/intel/ice/ice_repr.c index 109761c8c858..fd1f8b0ad0ab 100644 --- a/drivers/net/ethernet/intel/ice/ice_repr.c +++ b/drivers/net/ethernet/intel/ice/ice_repr.c @@ -156,18 +156,20 @@ ice_repr_sp_stats64(const struct net_device *dev, u64 pkts, bytes; tx_ring = np->vsi->tx_rings[vf_id]; - ice_fetch_u64_stats_per_ring(&tx_ring->syncp, tx_ring->stats, + ice_fetch_u64_stats_per_ring(&tx_ring->ring_stats->syncp, + tx_ring->ring_stats->stats, &pkts, &bytes); stats->rx_packets = pkts; stats->rx_bytes = bytes; rx_ring = np->vsi->rx_rings[vf_id]; - ice_fetch_u64_stats_per_ring(&rx_ring->syncp, rx_ring->stats, + ice_fetch_u64_stats_per_ring(&rx_ring->ring_stats->syncp, + rx_ring->ring_stats->stats, &pkts, &bytes); stats->tx_packets = pkts; stats->tx_bytes = bytes; - stats->tx_dropped = rx_ring->rx_stats.alloc_page_failed + - rx_ring->rx_stats.alloc_buf_failed; + stats->tx_dropped = rx_ring->ring_stats->rx_stats.alloc_page_failed + + rx_ring->ring_stats->rx_stats.alloc_buf_failed; return 0; } diff --git a/drivers/net/ethernet/intel/ice/ice_txrx.c b/drivers/net/ethernet/intel/ice/ice_txrx.c index dbe80e5053a8..086f0b3ab68d 100644 --- a/drivers/net/ethernet/intel/ice/ice_txrx.c +++ b/drivers/net/ethernet/intel/ice/ice_txrx.c @@ -325,7 +325,7 @@ static bool ice_clean_tx_irq(struct ice_tx_ring *tx_ring, int napi_budget) if (netif_tx_queue_stopped(txring_txq(tx_ring)) && !test_bit(ICE_VSI_DOWN, vsi->state)) { netif_tx_wake_queue(txring_txq(tx_ring)); - ++tx_ring->tx_stats.restart_q; + ++tx_ring->ring_stats->tx_stats.restart_q; } } @@ -367,7 +367,7 @@ int ice_setup_tx_ring(struct ice_tx_ring *tx_ring) tx_ring->next_to_use = 0; tx_ring->next_to_clean = 0; - tx_ring->tx_stats.prev_pkt = -1; + tx_ring->ring_stats->tx_stats.prev_pkt = -1; return 0; err: @@ -667,7 +667,7 @@ ice_alloc_mapped_page(struct ice_rx_ring *rx_ring, struct ice_rx_buf *bi) /* alloc new page for storage */ page = dev_alloc_pages(ice_rx_pg_order(rx_ring)); if (unlikely(!page)) { - rx_ring->rx_stats.alloc_page_failed++; + rx_ring->ring_stats->rx_stats.alloc_page_failed++; return false; } @@ -680,7 +680,7 @@ ice_alloc_mapped_page(struct ice_rx_ring *rx_ring, struct ice_rx_buf *bi) */ if (dma_mapping_error(rx_ring->dev, dma)) { __free_pages(page, ice_rx_pg_order(rx_ring)); - rx_ring->rx_stats.alloc_page_failed++; + rx_ring->ring_stats->rx_stats.alloc_page_failed++; return false; } @@ -1091,7 +1091,7 @@ ice_is_non_eop(struct ice_rx_ring *rx_ring, union ice_32b_rx_flex_desc *rx_desc) if (likely(ice_test_staterr(rx_desc->wb.status_error0, ICE_RXD_EOF))) return false; - rx_ring->rx_stats.non_eop_descs++; + rx_ring->ring_stats->rx_stats.non_eop_descs++; return true; } @@ -1222,7 +1222,7 @@ construct_skb: } /* exit if we failed to retrieve a buffer */ if (!skb) { - rx_ring->rx_stats.alloc_buf_failed++; + rx_ring->ring_stats->rx_stats.alloc_buf_failed++; if (rx_buf) rx_buf->pagecnt_bias++; break; @@ -1275,7 +1275,9 @@ construct_skb: ice_finalize_xdp_rx(xdp_ring, xdp_xmit); rx_ring->skb = skb; - ice_update_rx_ring_stats(rx_ring, total_rx_pkts, total_rx_bytes); + if (rx_ring->ring_stats) + ice_update_rx_ring_stats(rx_ring, total_rx_pkts, + total_rx_bytes); /* guarantee a trip back through this routine if there was a failure */ return failure ? budget : (int)total_rx_pkts; @@ -1292,15 +1294,25 @@ static void __ice_update_sample(struct ice_q_vector *q_vector, struct ice_tx_ring *tx_ring; ice_for_each_tx_ring(tx_ring, *rc) { - packets += tx_ring->stats.pkts; - bytes += tx_ring->stats.bytes; + struct ice_ring_stats *ring_stats; + + ring_stats = tx_ring->ring_stats; + if (!ring_stats) + continue; + packets += ring_stats->stats.pkts; + bytes += ring_stats->stats.bytes; } } else { struct ice_rx_ring *rx_ring; ice_for_each_rx_ring(rx_ring, *rc) { - packets += rx_ring->stats.pkts; - bytes += rx_ring->stats.bytes; + struct ice_ring_stats *ring_stats; + + ring_stats = rx_ring->ring_stats; + if (!ring_stats) + continue; + packets += ring_stats->stats.pkts; + bytes += ring_stats->stats.bytes; } } @@ -1549,7 +1561,7 @@ static int __ice_maybe_stop_tx(struct ice_tx_ring *tx_ring, unsigned int size) /* A reprieve! - use start_queue because it doesn't call schedule */ netif_tx_start_queue(txring_txq(tx_ring)); - ++tx_ring->tx_stats.restart_q; + ++tx_ring->ring_stats->tx_stats.restart_q; return 0; } @@ -2293,7 +2305,7 @@ ice_xmit_frame_ring(struct sk_buff *skb, struct ice_tx_ring *tx_ring) if (__skb_linearize(skb)) goto out_drop; count = ice_txd_use_count(skb->len); - tx_ring->tx_stats.tx_linearize++; + tx_ring->ring_stats->tx_stats.tx_linearize++; } /* need: 1 descriptor per page * PAGE_SIZE/ICE_MAX_DATA_PER_TXD, @@ -2304,7 +2316,7 @@ ice_xmit_frame_ring(struct sk_buff *skb, struct ice_tx_ring *tx_ring) */ if (ice_maybe_stop_tx(tx_ring, count + ICE_DESCS_PER_CACHE_LINE + ICE_DESCS_FOR_CTX_DESC)) { - tx_ring->tx_stats.tx_busy++; + tx_ring->ring_stats->tx_stats.tx_busy++; return NETDEV_TX_BUSY; } diff --git a/drivers/net/ethernet/intel/ice/ice_txrx.h b/drivers/net/ethernet/intel/ice/ice_txrx.h index 932b5661ec4d..4fd0e5d0a313 100644 --- a/drivers/net/ethernet/intel/ice/ice_txrx.h +++ b/drivers/net/ethernet/intel/ice/ice_txrx.h @@ -191,6 +191,16 @@ struct ice_rxq_stats { u64 alloc_buf_failed; }; +struct ice_ring_stats { + struct rcu_head rcu; /* to avoid race on free */ + struct ice_q_stats stats; + struct u64_stats_sync syncp; + union { + struct ice_txq_stats tx_stats; + struct ice_rxq_stats rx_stats; + }; +}; + enum ice_ring_state_t { ICE_TX_XPS_INIT_DONE, ICE_TX_NBITS, @@ -283,9 +293,7 @@ struct ice_rx_ring { u16 rx_buf_len; /* stats structs */ - struct ice_rxq_stats rx_stats; - struct ice_q_stats stats; - struct u64_stats_sync syncp; + struct ice_ring_stats *ring_stats; struct rcu_head rcu; /* to avoid race on free */ /* CL4 - 3rd cacheline starts here */ @@ -325,10 +333,8 @@ struct ice_tx_ring { u16 count; /* Number of descriptors */ u16 q_index; /* Queue number of ring */ /* stats structs */ - struct ice_txq_stats tx_stats; + struct ice_ring_stats *ring_stats; /* CL3 - 3rd cacheline starts here */ - struct ice_q_stats stats; - struct u64_stats_sync syncp; struct rcu_head rcu; /* to avoid race on free */ DECLARE_BITMAP(xps_state, ICE_TX_NBITS); /* XPS Config State */ struct ice_channel *ch; diff --git a/drivers/net/ethernet/intel/ice/ice_txrx_lib.c b/drivers/net/ethernet/intel/ice/ice_txrx_lib.c index 7ee38d02d1e5..25f04266c668 100644 --- a/drivers/net/ethernet/intel/ice/ice_txrx_lib.c +++ b/drivers/net/ethernet/intel/ice/ice_txrx_lib.c @@ -285,7 +285,7 @@ int ice_xmit_xdp_ring(void *data, u16 size, struct ice_tx_ring *xdp_ring) ice_clean_xdp_irq(xdp_ring); if (!unlikely(ICE_DESC_UNUSED(xdp_ring))) { - xdp_ring->tx_stats.tx_busy++; + xdp_ring->ring_stats->tx_stats.tx_busy++; return ICE_XDP_CONSUMED; } diff --git a/drivers/net/ethernet/intel/ice/ice_xsk.c b/drivers/net/ethernet/intel/ice/ice_xsk.c index 056c904b83cc..907055b77af0 100644 --- a/drivers/net/ethernet/intel/ice/ice_xsk.c +++ b/drivers/net/ethernet/intel/ice/ice_xsk.c @@ -24,13 +24,24 @@ static struct xdp_buff **ice_xdp_buf(struct ice_rx_ring *rx_ring, u32 idx) */ static void ice_qp_reset_stats(struct ice_vsi *vsi, u16 q_idx) { - memset(&vsi->rx_rings[q_idx]->rx_stats, 0, - sizeof(vsi->rx_rings[q_idx]->rx_stats)); - memset(&vsi->tx_rings[q_idx]->stats, 0, - sizeof(vsi->tx_rings[q_idx]->stats)); + struct ice_vsi_stats *vsi_stat; + struct ice_pf *pf; + + pf = vsi->back; + if (!pf->vsi_stats) + return; + + vsi_stat = pf->vsi_stats[vsi->idx]; + if (!vsi_stat) + return; + + memset(&vsi_stat->rx_ring_stats[q_idx]->rx_stats, 0, + sizeof(vsi_stat->rx_ring_stats[q_idx]->rx_stats)); + memset(&vsi_stat->tx_ring_stats[q_idx]->stats, 0, + sizeof(vsi_stat->tx_ring_stats[q_idx]->stats)); if (ice_is_xdp_ena_vsi(vsi)) - memset(&vsi->xdp_rings[q_idx]->stats, 0, - sizeof(vsi->xdp_rings[q_idx]->stats)); + memset(&vsi->xdp_rings[q_idx]->ring_stats->stats, 0, + sizeof(vsi->xdp_rings[q_idx]->ring_stats->stats)); } /** @@ -722,7 +733,7 @@ construct_skb: /* XDP_PASS path */ skb = ice_construct_skb_zc(rx_ring, xdp); if (!skb) { - rx_ring->rx_stats.alloc_buf_failed++; + rx_ring->ring_stats->rx_stats.alloc_buf_failed++; break; } From c7cb9dfc57a2aff7a7f270c883cbe0f5cd3f68e1 Mon Sep 17 00:00:00 2001 From: Marcin Szycik Date: Mon, 7 Nov 2022 17:10:38 +0100 Subject: [PATCH 1184/2002] ice: Fix configuring VIRTCHNL_OP_CONFIG_VSI_QUEUES with unbalanced queues Currently the VIRTCHNL_OP_CONFIG_VSI_QUEUES command may fail if there are less RX queues than TX queues requested. To fix it, only configure RXDID if RX queue exists. Fixes: e753df8fbca5 ("ice: Add support Flex RXD") Signed-off-by: Marcin Szycik Tested-by: Konrad Jankowski Signed-off-by: Tony Nguyen --- drivers/net/ethernet/intel/ice/ice_virtchnl.c | 41 +++++++++---------- 1 file changed, 19 insertions(+), 22 deletions(-) diff --git a/drivers/net/ethernet/intel/ice/ice_virtchnl.c b/drivers/net/ethernet/intel/ice/ice_virtchnl.c index ec90e594986e..dab3cd5d300e 100644 --- a/drivers/net/ethernet/intel/ice/ice_virtchnl.c +++ b/drivers/net/ethernet/intel/ice/ice_virtchnl.c @@ -1621,9 +1621,6 @@ static int ice_vc_cfg_qs_msg(struct ice_vf *vf, u8 *msg) } for (i = 0; i < qci->num_queue_pairs; i++) { - struct ice_hw *hw; - u32 rxdid; - u16 pf_q; qpi = &qci->qpair[i]; if (qpi->txq.vsi_id != qci->vsi_id || qpi->rxq.vsi_id != qci->vsi_id || @@ -1664,6 +1661,7 @@ static int ice_vc_cfg_qs_msg(struct ice_vf *vf, u8 *msg) /* copy Rx queue info from VF into VSI */ if (qpi->rxq.ring_len > 0) { u16 max_frame_size = ice_vc_get_max_frame_size(vf); + u32 rxdid; vsi->rx_rings[i]->dma = qpi->rxq.dma_ring_addr; vsi->rx_rings[i]->count = qpi->rxq.ring_len; @@ -1691,26 +1689,25 @@ static int ice_vc_cfg_qs_msg(struct ice_vf *vf, u8 *msg) vf->vf_id, i); goto error_param; } + + /* If Rx flex desc is supported, select RXDID for Rx + * queues. Otherwise, use legacy 32byte descriptor + * format. Legacy 16byte descriptor is not supported. + * If this RXDID is selected, return error. + */ + if (vf->driver_caps & + VIRTCHNL_VF_OFFLOAD_RX_FLEX_DESC) { + rxdid = qpi->rxq.rxdid; + if (!(BIT(rxdid) & pf->supported_rxdids)) + goto error_param; + } else { + rxdid = ICE_RXDID_LEGACY_1; + } + + ice_write_qrxflxp_cntxt(&vsi->back->hw, + vsi->rxq_map[q_idx], + rxdid, 0x03, false); } - - /* VF Rx queue RXDID configuration */ - pf_q = vsi->rxq_map[qpi->rxq.queue_id]; - rxdid = qpi->rxq.rxdid; - hw = &vsi->back->hw; - - /* If Rx flex desc is supported, select RXDID for Rx queues. - * Otherwise, use legacy 32byte descriptor format. - * Legacy 16byte descriptor is not supported. If this RXDID - * is selected, return error. - */ - if (vf->driver_caps & VIRTCHNL_VF_OFFLOAD_RX_FLEX_DESC) { - if (!(BIT(rxdid) & pf->supported_rxdids)) - goto error_param; - } else { - rxdid = ICE_RXDID_LEGACY_1; - } - - ice_write_qrxflxp_cntxt(hw, pf_q, rxdid, 0x03, false); } /* send the response to the VF */ From 60aeca6dc47493a681199f8f53fabd5b96adfa44 Mon Sep 17 00:00:00 2001 From: Anatolii Gerasymenko Date: Thu, 3 Nov 2022 15:30:05 +0100 Subject: [PATCH 1185/2002] ice: Use ICE_RLAN_BASE_S instead of magic number Commit 72adf2421d9b ("ice: Move common functions out of ice_main.c part 2/7") moved an older version of ice_setup_rx_ctx() function with usage of magic number 7. Reimplement the commit 5ab522443bd1 ("ice: Cleanup magic number") to use ICE_RLAN_BASE_S instead of magic number. Signed-off-by: Anatolii Gerasymenko Tested-by: Gurucharan G (A Contingent worker at Intel) Signed-off-by: Tony Nguyen --- drivers/net/ethernet/intel/ice/ice_base.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/intel/ice/ice_base.c b/drivers/net/ethernet/intel/ice/ice_base.c index e864634d66bc..554095b25f44 100644 --- a/drivers/net/ethernet/intel/ice/ice_base.c +++ b/drivers/net/ethernet/intel/ice/ice_base.c @@ -389,7 +389,7 @@ static int ice_setup_rx_ctx(struct ice_rx_ring *ring) * Indicates the starting address of the descriptor queue defined in * 128 Byte units. */ - rlan_ctx.base = ring->dma >> 7; + rlan_ctx.base = ring->dma >> ICE_RLAN_BASE_S; rlan_ctx.qlen = ring->count; From 2fcc6081a7bf8f7f531cffdc58b630b822e700a1 Mon Sep 17 00:00:00 2001 From: David Vernet Date: Wed, 23 Nov 2022 07:52:53 -0600 Subject: [PATCH 1186/2002] bpf: Don't use idx variable when registering kfunc dtors In commit fda01efc6160 ("bpf: Enable cgroups to be used as kptrs"), I added an 'int idx' variable to kfunc_init() which was meant to dynamically set the index of the btf id entries of the 'generic_dtor_ids' array. This was done to make the code slightly less brittle as the struct cgroup * kptr kfuncs such as bpf_cgroup_aquire() are compiled out if CONFIG_CGROUPS is not defined. This, however, causes an lkp build warning: >> kernel/bpf/helpers.c:2005:40: warning: multiple unsequenced modifications to 'idx' [-Wunsequenced] .btf_id = generic_dtor_ids[idx++], Fix the warning by just hard-coding the indices. Fixes: fda01efc6160 ("bpf: Enable cgroups to be used as kptrs") Reported-by: kernel test robot Signed-off-by: David Vernet Acked-by: Yonghong Song Link: https://lore.kernel.org/r/20221123135253.637525-1-void@manifault.com Signed-off-by: Alexei Starovoitov --- kernel/bpf/helpers.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/kernel/bpf/helpers.c b/kernel/bpf/helpers.c index e4e9db301db5..da2681ebb7c3 100644 --- a/kernel/bpf/helpers.c +++ b/kernel/bpf/helpers.c @@ -2019,16 +2019,16 @@ static const struct btf_kfunc_id_set common_kfunc_set = { static int __init kfunc_init(void) { - int ret, idx = 0; + int ret; const struct btf_id_dtor_kfunc generic_dtors[] = { { - .btf_id = generic_dtor_ids[idx++], - .kfunc_btf_id = generic_dtor_ids[idx++] + .btf_id = generic_dtor_ids[0], + .kfunc_btf_id = generic_dtor_ids[1] }, #ifdef CONFIG_CGROUPS { - .btf_id = generic_dtor_ids[idx++], - .kfunc_btf_id = generic_dtor_ids[idx++] + .btf_id = generic_dtor_ids[2], + .kfunc_btf_id = generic_dtor_ids[3] }, #endif }; From 8ac88eece8009428e2577c345080a458e4507e2f Mon Sep 17 00:00:00 2001 From: Stanislav Fomichev Date: Wed, 23 Nov 2022 12:08:29 -0800 Subject: [PATCH 1187/2002] selftests/bpf: Mount debugfs in setns_by_fd Jiri reports broken test_progs after recent commit 68f8e3d4b916 ("selftests/bpf: Make sure zero-len skbs aren't redirectable"). Apparently we don't remount debugfs when we switch back networking namespace. Let's explicitly mount /sys/kernel/debug. 0: https://lore.kernel.org/bpf/63b85917-a2ea-8e35-620c-808560910819@meta.com/T/#ma66ca9c92e99eee0a25e40f422489b26ee0171c1 Fixes: a30338840fa5 ("selftests/bpf: Move open_netns() and close_netns() into network_helpers.c") Reported-by: Jiri Olsa Signed-off-by: Stanislav Fomichev Link: https://lore.kernel.org/r/20221123200829.2226254-1-sdf@google.com Signed-off-by: Alexei Starovoitov --- tools/testing/selftests/bpf/network_helpers.c | 4 ++++ tools/testing/selftests/bpf/prog_tests/empty_skb.c | 2 +- tools/testing/selftests/bpf/prog_tests/xdp_do_redirect.c | 2 +- tools/testing/selftests/bpf/prog_tests/xdp_synproxy.c | 2 +- 4 files changed, 7 insertions(+), 3 deletions(-) diff --git a/tools/testing/selftests/bpf/network_helpers.c b/tools/testing/selftests/bpf/network_helpers.c index bec15558fd93..1f37adff7632 100644 --- a/tools/testing/selftests/bpf/network_helpers.c +++ b/tools/testing/selftests/bpf/network_helpers.c @@ -426,6 +426,10 @@ static int setns_by_fd(int nsfd) if (!ASSERT_OK(err, "mount /sys/fs/bpf")) return err; + err = mount("debugfs", "/sys/kernel/debug", "debugfs", 0, NULL); + if (!ASSERT_OK(err, "mount /sys/kernel/debug")) + return err; + return 0; } diff --git a/tools/testing/selftests/bpf/prog_tests/empty_skb.c b/tools/testing/selftests/bpf/prog_tests/empty_skb.c index 32dd731e9070..0613f3bb8b5e 100644 --- a/tools/testing/selftests/bpf/prog_tests/empty_skb.c +++ b/tools/testing/selftests/bpf/prog_tests/empty_skb.c @@ -9,7 +9,7 @@ goto out; \ }) -void test_empty_skb(void) +void serial_test_empty_skb(void) { LIBBPF_OPTS(bpf_test_run_opts, tattr); struct empty_skb *bpf_obj = NULL; diff --git a/tools/testing/selftests/bpf/prog_tests/xdp_do_redirect.c b/tools/testing/selftests/bpf/prog_tests/xdp_do_redirect.c index a50971c6cf4a..9ac6f6a268db 100644 --- a/tools/testing/selftests/bpf/prog_tests/xdp_do_redirect.c +++ b/tools/testing/selftests/bpf/prog_tests/xdp_do_redirect.c @@ -85,7 +85,7 @@ static void test_max_pkt_size(int fd) } #define NUM_PKTS 10000 -void test_xdp_do_redirect(void) +void serial_test_xdp_do_redirect(void) { int err, xdp_prog_fd, tc_prog_fd, ifindex_src, ifindex_dst; char data[sizeof(pkt_udp) + sizeof(__u32)]; diff --git a/tools/testing/selftests/bpf/prog_tests/xdp_synproxy.c b/tools/testing/selftests/bpf/prog_tests/xdp_synproxy.c index c72083885b6d..13daa3746064 100644 --- a/tools/testing/selftests/bpf/prog_tests/xdp_synproxy.c +++ b/tools/testing/selftests/bpf/prog_tests/xdp_synproxy.c @@ -174,7 +174,7 @@ out: system("ip netns del synproxy"); } -void test_xdp_synproxy(void) +void serial_test_xdp_synproxy(void) { if (test__start_subtest("xdp")) test_synproxy(true); From c479babce5b1f7b6e6ec172bdca075534343be55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Tue, 22 Nov 2022 20:54:56 -0800 Subject: [PATCH 1188/2002] net: dsa: lan9303: Convert to i2c's .probe_new() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The probe function doesn't make use of the i2c_device_id * parameter so it can be trivially converted. Signed-off-by: Uwe Kleine-König Signed-off-by: Jakub Kicinski --- drivers/net/dsa/lan9303_i2c.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/net/dsa/lan9303_i2c.c b/drivers/net/dsa/lan9303_i2c.c index 7d746cd9ca1b..1cb41c36bd47 100644 --- a/drivers/net/dsa/lan9303_i2c.c +++ b/drivers/net/dsa/lan9303_i2c.c @@ -29,8 +29,7 @@ static const struct regmap_config lan9303_i2c_regmap_config = { .cache_type = REGCACHE_NONE, }; -static int lan9303_i2c_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int lan9303_i2c_probe(struct i2c_client *client) { struct lan9303_i2c *sw_dev; int ret; @@ -106,7 +105,7 @@ static struct i2c_driver lan9303_i2c_driver = { .name = "LAN9303_I2C", .of_match_table = of_match_ptr(lan9303_i2c_of_match), }, - .probe = lan9303_i2c_probe, + .probe_new = lan9303_i2c_probe, .remove = lan9303_i2c_remove, .shutdown = lan9303_i2c_shutdown, .id_table = lan9303_i2c_id, From f925e2154de9ddfa57091bd8b6ba1b2509c55a85 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Tue, 22 Nov 2022 20:54:57 -0800 Subject: [PATCH 1189/2002] net: dsa: microchip: ksz9477: Convert to i2c's .probe_new() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The probe function doesn't make use of the i2c_device_id * parameter so it can be trivially converted. Signed-off-by: Uwe Kleine-König Signed-off-by: Jakub Kicinski --- drivers/net/dsa/microchip/ksz9477_i2c.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/net/dsa/microchip/ksz9477_i2c.c b/drivers/net/dsa/microchip/ksz9477_i2c.c index db4aec0a51dc..c1a633ca1e6d 100644 --- a/drivers/net/dsa/microchip/ksz9477_i2c.c +++ b/drivers/net/dsa/microchip/ksz9477_i2c.c @@ -14,8 +14,7 @@ KSZ_REGMAP_TABLE(ksz9477, not_used, 16, 0, 0); -static int ksz9477_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *i2c_id) +static int ksz9477_i2c_probe(struct i2c_client *i2c) { struct regmap_config rc; struct ksz_device *dev; @@ -120,7 +119,7 @@ static struct i2c_driver ksz9477_i2c_driver = { .name = "ksz9477-switch", .of_match_table = of_match_ptr(ksz9477_dt_ids), }, - .probe = ksz9477_i2c_probe, + .probe_new = ksz9477_i2c_probe, .remove = ksz9477_i2c_remove, .shutdown = ksz9477_i2c_shutdown, .id_table = ksz9477_i2c_id, From dfd5e53dd72113f37663f59a6337fe9a0dfbf0f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Tue, 22 Nov 2022 20:54:58 -0800 Subject: [PATCH 1190/2002] net: dsa: xrs700x: Convert to i2c's .probe_new() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The probe function doesn't make use of the i2c_device_id * parameter so it can be trivially converted. Signed-off-by: Uwe Kleine-König Signed-off-by: Jakub Kicinski --- drivers/net/dsa/xrs700x/xrs700x_i2c.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/net/dsa/xrs700x/xrs700x_i2c.c b/drivers/net/dsa/xrs700x/xrs700x_i2c.c index 54065cdedd35..14ff6887a225 100644 --- a/drivers/net/dsa/xrs700x/xrs700x_i2c.c +++ b/drivers/net/dsa/xrs700x/xrs700x_i2c.c @@ -76,8 +76,7 @@ static const struct regmap_config xrs700x_i2c_regmap_config = { .val_format_endian = REGMAP_ENDIAN_BIG }; -static int xrs700x_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *i2c_id) +static int xrs700x_i2c_probe(struct i2c_client *i2c) { struct xrs700x *priv; int ret; @@ -148,7 +147,7 @@ static struct i2c_driver xrs700x_i2c_driver = { .name = "xrs700x-i2c", .of_match_table = of_match_ptr(xrs700x_i2c_dt_ids), }, - .probe = xrs700x_i2c_probe, + .probe_new = xrs700x_i2c_probe, .remove = xrs700x_i2c_remove, .shutdown = xrs700x_i2c_shutdown, .id_table = xrs700x_i2c_id, From cb405c2a40400c7329b08bef386b79dd3ec4e78f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Tue, 22 Nov 2022 20:54:59 -0800 Subject: [PATCH 1191/2002] net/mlxsw: Convert to i2c's .probe_new() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit .probe_new() doesn't get the i2c_device_id * parameter, so determine that explicitly in the probe function. Signed-off-by: Uwe Kleine-König Reviewed-by: Ido Schimmel Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/mellanox/mlxsw/i2c.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlxsw/i2c.c b/drivers/net/ethernet/mellanox/mlxsw/i2c.c index f5f5f8dc3d19..2c586c2308ae 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/i2c.c +++ b/drivers/net/ethernet/mellanox/mlxsw/i2c.c @@ -632,9 +632,9 @@ static const struct mlxsw_bus mlxsw_i2c_bus = { .cmd_exec = mlxsw_i2c_cmd_exec, }; -static int mlxsw_i2c_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int mlxsw_i2c_probe(struct i2c_client *client) { + const struct i2c_device_id *id = i2c_client_get_device_id(client); const struct i2c_adapter_quirks *quirks = client->adapter->quirks; struct mlxsw_i2c *mlxsw_i2c; u8 status; @@ -751,7 +751,7 @@ static void mlxsw_i2c_remove(struct i2c_client *client) int mlxsw_i2c_driver_register(struct i2c_driver *i2c_driver) { - i2c_driver->probe = mlxsw_i2c_probe; + i2c_driver->probe_new = mlxsw_i2c_probe; i2c_driver->remove = mlxsw_i2c_remove; return i2c_add_driver(i2c_driver); } From f72eed59eab42414f3844f4a8e3ce0c4e6280733 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Tue, 22 Nov 2022 20:55:00 -0800 Subject: [PATCH 1192/2002] nfc: microread: Convert to i2c's .probe_new() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The probe function doesn't make use of the i2c_device_id * parameter so it can be trivially converted. Signed-off-by: Uwe Kleine-König Signed-off-by: Jakub Kicinski --- drivers/nfc/microread/i2c.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/nfc/microread/i2c.c b/drivers/nfc/microread/i2c.c index 5eaa18f81355..e72b358a2a12 100644 --- a/drivers/nfc/microread/i2c.c +++ b/drivers/nfc/microread/i2c.c @@ -231,8 +231,7 @@ static const struct nfc_phy_ops i2c_phy_ops = { .disable = microread_i2c_disable, }; -static int microread_i2c_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int microread_i2c_probe(struct i2c_client *client) { struct microread_i2c_phy *phy; int r; @@ -287,7 +286,7 @@ static struct i2c_driver microread_i2c_driver = { .driver = { .name = MICROREAD_I2C_DRIVER_NAME, }, - .probe = microread_i2c_probe, + .probe_new = microread_i2c_probe, .remove = microread_i2c_remove, .id_table = microread_i2c_id, }; From a9f656c88a903ef704b70efe12c2e8feb08a85eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Tue, 22 Nov 2022 20:55:01 -0800 Subject: [PATCH 1193/2002] nfc: mrvl: Convert to i2c's .probe_new() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The probe function doesn't make use of the i2c_device_id * parameter so it can be trivially converted. Signed-off-by: Uwe Kleine-König Signed-off-by: Jakub Kicinski --- drivers/nfc/nfcmrvl/i2c.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/nfc/nfcmrvl/i2c.c b/drivers/nfc/nfcmrvl/i2c.c index 24436c9e54c9..c3ab49df1e35 100644 --- a/drivers/nfc/nfcmrvl/i2c.c +++ b/drivers/nfc/nfcmrvl/i2c.c @@ -181,8 +181,7 @@ static int nfcmrvl_i2c_parse_dt(struct device_node *node, return 0; } -static int nfcmrvl_i2c_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int nfcmrvl_i2c_probe(struct i2c_client *client) { const struct nfcmrvl_platform_data *pdata; struct nfcmrvl_i2c_drv_data *drv_data; @@ -257,7 +256,7 @@ static const struct i2c_device_id nfcmrvl_i2c_id_table[] = { MODULE_DEVICE_TABLE(i2c, nfcmrvl_i2c_id_table); static struct i2c_driver nfcmrvl_i2c_driver = { - .probe = nfcmrvl_i2c_probe, + .probe_new = nfcmrvl_i2c_probe, .id_table = nfcmrvl_i2c_id_table, .remove = nfcmrvl_i2c_remove, .driver = { From bf1f6f2975288a373c4879c4e23e7fc2dab2634f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Tue, 22 Nov 2022 20:55:02 -0800 Subject: [PATCH 1194/2002] NFC: nxp-nci: Convert to i2c's .probe_new() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The probe function doesn't make use of the i2c_device_id * parameter so it can be trivially converted. Signed-off-by: Uwe Kleine-König Signed-off-by: Jakub Kicinski --- drivers/nfc/nxp-nci/i2c.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/nfc/nxp-nci/i2c.c b/drivers/nfc/nxp-nci/i2c.c index ec6446511984..d4c299be7949 100644 --- a/drivers/nfc/nxp-nci/i2c.c +++ b/drivers/nfc/nxp-nci/i2c.c @@ -263,8 +263,7 @@ static const struct acpi_gpio_mapping acpi_nxp_nci_gpios[] = { { } }; -static int nxp_nci_i2c_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int nxp_nci_i2c_probe(struct i2c_client *client) { struct device *dev = &client->dev; struct nxp_nci_i2c_phy *phy; @@ -349,7 +348,7 @@ static struct i2c_driver nxp_nci_i2c_driver = { .acpi_match_table = ACPI_PTR(acpi_id), .of_match_table = of_nxp_nci_i2c_match, }, - .probe = nxp_nci_i2c_probe, + .probe_new = nxp_nci_i2c_probe, .id_table = nxp_nci_i2c_id_table, .remove = nxp_nci_i2c_remove, }; From d72c9828a3cbf3110b0c5ce26a81fabc68312254 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Tue, 22 Nov 2022 20:55:03 -0800 Subject: [PATCH 1195/2002] nfc: pn533: Convert to i2c's .probe_new() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The probe function doesn't make use of the i2c_device_id * parameter so it can be trivially converted. Signed-off-by: Uwe Kleine-König Signed-off-by: Jakub Kicinski --- drivers/nfc/pn533/i2c.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/nfc/pn533/i2c.c b/drivers/nfc/pn533/i2c.c index ddf3db286bad..1503a98f0405 100644 --- a/drivers/nfc/pn533/i2c.c +++ b/drivers/nfc/pn533/i2c.c @@ -163,8 +163,7 @@ static const struct pn533_phy_ops i2c_phy_ops = { }; -static int pn533_i2c_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int pn533_i2c_probe(struct i2c_client *client) { struct pn533_i2c_phy *phy; struct pn533 *priv; @@ -260,7 +259,7 @@ static struct i2c_driver pn533_i2c_driver = { .name = PN533_I2C_DRIVER_NAME, .of_match_table = of_match_ptr(of_pn533_i2c_match), }, - .probe = pn533_i2c_probe, + .probe_new = pn533_i2c_probe, .id_table = pn533_i2c_id_table, .remove = pn533_i2c_remove, }; From 2338adb2e0914904f3a23ffdbb8c5d13ff38638e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Tue, 22 Nov 2022 20:55:04 -0800 Subject: [PATCH 1196/2002] nfc: pn544: Convert to i2c's .probe_new() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The probe function doesn't make use of the i2c_device_id * parameter so it can be trivially converted. Signed-off-by: Uwe Kleine-König Signed-off-by: Jakub Kicinski --- drivers/nfc/pn544/i2c.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/nfc/pn544/i2c.c b/drivers/nfc/pn544/i2c.c index 9e754abcfa2a..8b0d910bee06 100644 --- a/drivers/nfc/pn544/i2c.c +++ b/drivers/nfc/pn544/i2c.c @@ -866,8 +866,7 @@ static const struct acpi_gpio_mapping acpi_pn544_gpios[] = { { }, }; -static int pn544_hci_i2c_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int pn544_hci_i2c_probe(struct i2c_client *client) { struct device *dev = &client->dev; struct pn544_i2c_phy *phy; @@ -954,7 +953,7 @@ static struct i2c_driver pn544_hci_i2c_driver = { .of_match_table = of_match_ptr(of_pn544_i2c_match), .acpi_match_table = ACPI_PTR(pn544_hci_i2c_acpi_match), }, - .probe = pn544_hci_i2c_probe, + .probe_new = pn544_hci_i2c_probe, .id_table = pn544_hci_i2c_id_table, .remove = pn544_hci_i2c_remove, }; From 0fc00c085d71b2777b91b6db22afd35ddab4c0c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Tue, 22 Nov 2022 20:55:05 -0800 Subject: [PATCH 1197/2002] nfc: s3fwrn5: Convert to i2c's .probe_new() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The probe function doesn't make use of the i2c_device_id * parameter so it can be trivially converted. Signed-off-by: Uwe Kleine-König Signed-off-by: Jakub Kicinski --- drivers/nfc/s3fwrn5/i2c.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/nfc/s3fwrn5/i2c.c b/drivers/nfc/s3fwrn5/i2c.c index ecdee838d25d..2517ae71f9a4 100644 --- a/drivers/nfc/s3fwrn5/i2c.c +++ b/drivers/nfc/s3fwrn5/i2c.c @@ -177,8 +177,7 @@ static int s3fwrn5_i2c_parse_dt(struct i2c_client *client) return 0; } -static int s3fwrn5_i2c_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int s3fwrn5_i2c_probe(struct i2c_client *client) { struct s3fwrn5_i2c_phy *phy; int ret; @@ -262,7 +261,7 @@ static struct i2c_driver s3fwrn5_i2c_driver = { .name = S3FWRN5_I2C_DRIVER_NAME, .of_match_table = of_match_ptr(of_s3fwrn5_i2c_match), }, - .probe = s3fwrn5_i2c_probe, + .probe_new = s3fwrn5_i2c_probe, .remove = s3fwrn5_i2c_remove, .id_table = s3fwrn5_i2c_id_table, }; From 75cc560ff661ee05ab261c92dc158471e48efdd3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Tue, 22 Nov 2022 20:55:06 -0800 Subject: [PATCH 1198/2002] nfc: st-nci: Convert to i2c's .probe_new() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The probe function doesn't make use of the i2c_device_id * parameter so it can be trivially converted. Signed-off-by: Uwe Kleine-König Signed-off-by: Jakub Kicinski --- drivers/nfc/st-nci/i2c.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/nfc/st-nci/i2c.c b/drivers/nfc/st-nci/i2c.c index 89fa24d71bef..6b5eed8a1fbe 100644 --- a/drivers/nfc/st-nci/i2c.c +++ b/drivers/nfc/st-nci/i2c.c @@ -195,8 +195,7 @@ static const struct acpi_gpio_mapping acpi_st_nci_gpios[] = { {}, }; -static int st_nci_i2c_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int st_nci_i2c_probe(struct i2c_client *client) { struct device *dev = &client->dev; struct st_nci_i2c_phy *phy; @@ -284,7 +283,7 @@ static struct i2c_driver st_nci_i2c_driver = { .of_match_table = of_match_ptr(of_st_nci_i2c_match), .acpi_match_table = ACPI_PTR(st_nci_i2c_acpi_match), }, - .probe = st_nci_i2c_probe, + .probe_new = st_nci_i2c_probe, .id_table = st_nci_i2c_id_table, .remove = st_nci_i2c_remove, }; From 1fa082734076fe2c34d3e4a64334f79bee4a5bf9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Tue, 22 Nov 2022 20:55:07 -0800 Subject: [PATCH 1199/2002] nfc: st21nfca: i2c: Convert to i2c's .probe_new() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The probe function doesn't make use of the i2c_device_id * parameter so it can be trivially converted. Signed-off-by: Uwe Kleine-König Signed-off-by: Jakub Kicinski --- drivers/nfc/st21nfca/i2c.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/nfc/st21nfca/i2c.c b/drivers/nfc/st21nfca/i2c.c index 76b55986bcf8..55f7a2391bb1 100644 --- a/drivers/nfc/st21nfca/i2c.c +++ b/drivers/nfc/st21nfca/i2c.c @@ -487,8 +487,7 @@ static const struct acpi_gpio_mapping acpi_st21nfca_gpios[] = { {}, }; -static int st21nfca_hci_i2c_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int st21nfca_hci_i2c_probe(struct i2c_client *client) { struct device *dev = &client->dev; struct st21nfca_i2c_phy *phy; @@ -598,7 +597,7 @@ static struct i2c_driver st21nfca_hci_i2c_driver = { .of_match_table = of_match_ptr(of_st21nfca_i2c_match), .acpi_match_table = ACPI_PTR(st21nfca_hci_i2c_acpi_match), }, - .probe = st21nfca_hci_i2c_probe, + .probe_new = st21nfca_hci_i2c_probe, .id_table = st21nfca_hci_i2c_id_table, .remove = st21nfca_hci_i2c_remove, }; From beb3d47d1d3d7185bb401af628ad32ee204a9526 Mon Sep 17 00:00:00 2001 From: Yonghong Song Date: Wed, 23 Nov 2022 07:57:59 -0800 Subject: [PATCH 1200/2002] bpf: Fix a BTF_ID_LIST bug with CONFIG_DEBUG_INFO_BTF not set With CONFIG_DEBUG_INFO_BTF not set, we hit the following compilation error, /.../kernel/bpf/verifier.c:8196:23: error: array index 6 is past the end of the array (that has type 'u32[5]' (aka 'unsigned int[5]')) [-Werror,-Warray-bounds] if (meta->func_id == special_kfunc_list[KF_bpf_cast_to_kern_ctx]) ^ ~~~~~~~~~~~~~~~~~~~~~~~ /.../kernel/bpf/verifier.c:8174:1: note: array 'special_kfunc_list' declared here BTF_ID_LIST(special_kfunc_list) ^ /.../include/linux/btf_ids.h:207:27: note: expanded from macro 'BTF_ID_LIST' #define BTF_ID_LIST(name) static u32 __maybe_unused name[5]; ^ /.../kernel/bpf/verifier.c:8443:19: error: array index 5 is past the end of the array (that has type 'u32[5]' (aka 'unsigned int[5]')) [-Werror,-Warray-bounds] btf_id == special_kfunc_list[KF_bpf_list_pop_back]; ^ ~~~~~~~~~~~~~~~~~~~~ /.../kernel/bpf/verifier.c:8174:1: note: array 'special_kfunc_list' declared here BTF_ID_LIST(special_kfunc_list) ^ /.../include/linux/btf_ids.h:207:27: note: expanded from macro 'BTF_ID_LIST' #define BTF_ID_LIST(name) static u32 __maybe_unused name[5]; ... Fix the problem by increase the size of BTF_ID_LIST to 16 to avoid compilation error and also prevent potentially unintended issue due to out-of-bound access. Reported-by: kernel test robot Reported-by: Dan Carpenter Reported-by: Nathan Chancellor Signed-off-by: Yonghong Song Link: https://lore.kernel.org/r/20221123155759.2669749-1-yhs@fb.com Signed-off-by: Alexei Starovoitov --- include/linux/btf_ids.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/linux/btf_ids.h b/include/linux/btf_ids.h index c9744efd202f..93397711a68c 100644 --- a/include/linux/btf_ids.h +++ b/include/linux/btf_ids.h @@ -204,7 +204,7 @@ extern struct btf_id_set8 name; #else -#define BTF_ID_LIST(name) static u32 __maybe_unused name[5]; +#define BTF_ID_LIST(name) static u32 __maybe_unused name[16]; #define BTF_ID(prefix, name) #define BTF_ID_FLAGS(prefix, name, ...) #define BTF_ID_UNUSED From c742cb7c3ebdeb358794b9c76d93d8be7b631b9a Mon Sep 17 00:00:00 2001 From: Donald Hunter Date: Mon, 21 Nov 2022 12:17:34 +0000 Subject: [PATCH 1201/2002] docs/bpf: Add table of BPF program types to libbpf docs Extend the libbpf documentation with a table of program types, attach points and ELF section names. Signed-off-by: Donald Hunter Signed-off-by: Andrii Nakryiko Reviewed-by: Bagas Sanjaya Acked-by: David Vernet Link: https://lore.kernel.org/bpf/20221121121734.98329-1-donald.hunter@gmail.com --- Documentation/bpf/libbpf/index.rst | 3 + Documentation/bpf/libbpf/program_types.rst | 203 +++++++++++++++++++++ Documentation/bpf/programs.rst | 3 + 3 files changed, 209 insertions(+) create mode 100644 Documentation/bpf/libbpf/program_types.rst diff --git a/Documentation/bpf/libbpf/index.rst b/Documentation/bpf/libbpf/index.rst index 3722537d1384..f9b3b252e28f 100644 --- a/Documentation/bpf/libbpf/index.rst +++ b/Documentation/bpf/libbpf/index.rst @@ -1,5 +1,7 @@ .. SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) +.. _libbpf: + libbpf ====== @@ -7,6 +9,7 @@ libbpf :maxdepth: 1 API Documentation + program_types libbpf_naming_convention libbpf_build diff --git a/Documentation/bpf/libbpf/program_types.rst b/Documentation/bpf/libbpf/program_types.rst new file mode 100644 index 000000000000..ad4d4d5eecb0 --- /dev/null +++ b/Documentation/bpf/libbpf/program_types.rst @@ -0,0 +1,203 @@ +.. SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) + +.. _program_types_and_elf: + +Program Types and ELF Sections +============================== + +The table below lists the program types, their attach types where relevant and the ELF section +names supported by libbpf for them. The ELF section names follow these rules: + +- ``type`` is an exact match, e.g. ``SEC("socket")`` +- ``type+`` means it can be either exact ``SEC("type")`` or well-formed ``SEC("type/extras")`` + with a '``/``' separator between ``type`` and ``extras``. + +When ``extras`` are specified, they provide details of how to auto-attach the BPF program. The +format of ``extras`` depends on the program type, e.g. ``SEC("tracepoint//")`` +for tracepoints or ``SEC("usdt/::")`` for USDT probes. The extras are +described in more detail in the footnotes. + + ++-------------------------------------------+----------------------------------------+----------------------------------+-----------+ +| Program Type | Attach Type | ELF Section Name | Sleepable | ++===========================================+========================================+==================================+===========+ +| ``BPF_PROG_TYPE_CGROUP_DEVICE`` | ``BPF_CGROUP_DEVICE`` | ``cgroup/dev`` | | ++-------------------------------------------+----------------------------------------+----------------------------------+-----------+ +| ``BPF_PROG_TYPE_CGROUP_SKB`` | | ``cgroup/skb`` | | ++ +----------------------------------------+----------------------------------+-----------+ +| | ``BPF_CGROUP_INET_EGRESS`` | ``cgroup_skb/egress`` | | ++ +----------------------------------------+----------------------------------+-----------+ +| | ``BPF_CGROUP_INET_INGRESS`` | ``cgroup_skb/ingress`` | | ++-------------------------------------------+----------------------------------------+----------------------------------+-----------+ +| ``BPF_PROG_TYPE_CGROUP_SOCKOPT`` | ``BPF_CGROUP_GETSOCKOPT`` | ``cgroup/getsockopt`` | | ++ +----------------------------------------+----------------------------------+-----------+ +| | ``BPF_CGROUP_SETSOCKOPT`` | ``cgroup/setsockopt`` | | ++-------------------------------------------+----------------------------------------+----------------------------------+-----------+ +| ``BPF_PROG_TYPE_CGROUP_SOCK_ADDR`` | ``BPF_CGROUP_INET4_BIND`` | ``cgroup/bind4`` | | ++ +----------------------------------------+----------------------------------+-----------+ +| | ``BPF_CGROUP_INET4_CONNECT`` | ``cgroup/connect4`` | | ++ +----------------------------------------+----------------------------------+-----------+ +| | ``BPF_CGROUP_INET4_GETPEERNAME`` | ``cgroup/getpeername4`` | | ++ +----------------------------------------+----------------------------------+-----------+ +| | ``BPF_CGROUP_INET4_GETSOCKNAME`` | ``cgroup/getsockname4`` | | ++ +----------------------------------------+----------------------------------+-----------+ +| | ``BPF_CGROUP_INET6_BIND`` | ``cgroup/bind6`` | | ++ +----------------------------------------+----------------------------------+-----------+ +| | ``BPF_CGROUP_INET6_CONNECT`` | ``cgroup/connect6`` | | ++ +----------------------------------------+----------------------------------+-----------+ +| | ``BPF_CGROUP_INET6_GETPEERNAME`` | ``cgroup/getpeername6`` | | ++ +----------------------------------------+----------------------------------+-----------+ +| | ``BPF_CGROUP_INET6_GETSOCKNAME`` | ``cgroup/getsockname6`` | | ++ +----------------------------------------+----------------------------------+-----------+ +| | ``BPF_CGROUP_UDP4_RECVMSG`` | ``cgroup/recvmsg4`` | | ++ +----------------------------------------+----------------------------------+-----------+ +| | ``BPF_CGROUP_UDP4_SENDMSG`` | ``cgroup/sendmsg4`` | | ++ +----------------------------------------+----------------------------------+-----------+ +| | ``BPF_CGROUP_UDP6_RECVMSG`` | ``cgroup/recvmsg6`` | | ++ +----------------------------------------+----------------------------------+-----------+ +| | ``BPF_CGROUP_UDP6_SENDMSG`` | ``cgroup/sendmsg6`` | | ++-------------------------------------------+----------------------------------------+----------------------------------+-----------+ +| ``BPF_PROG_TYPE_CGROUP_SOCK`` | ``BPF_CGROUP_INET4_POST_BIND`` | ``cgroup/post_bind4`` | | ++ +----------------------------------------+----------------------------------+-----------+ +| | ``BPF_CGROUP_INET6_POST_BIND`` | ``cgroup/post_bind6`` | | ++ +----------------------------------------+----------------------------------+-----------+ +| | ``BPF_CGROUP_INET_SOCK_CREATE`` | ``cgroup/sock_create`` | | ++ + +----------------------------------+-----------+ +| | | ``cgroup/sock`` | | ++ +----------------------------------------+----------------------------------+-----------+ +| | ``BPF_CGROUP_INET_SOCK_RELEASE`` | ``cgroup/sock_release`` | | ++-------------------------------------------+----------------------------------------+----------------------------------+-----------+ +| ``BPF_PROG_TYPE_CGROUP_SYSCTL`` | ``BPF_CGROUP_SYSCTL`` | ``cgroup/sysctl`` | | ++-------------------------------------------+----------------------------------------+----------------------------------+-----------+ +| ``BPF_PROG_TYPE_EXT`` | | ``freplace+`` [#fentry]_ | | ++-------------------------------------------+----------------------------------------+----------------------------------+-----------+ +| ``BPF_PROG_TYPE_FLOW_DISSECTOR`` | ``BPF_FLOW_DISSECTOR`` | ``flow_dissector`` | | ++-------------------------------------------+----------------------------------------+----------------------------------+-----------+ +| ``BPF_PROG_TYPE_KPROBE`` | | ``kprobe+`` [#kprobe]_ | | ++ + +----------------------------------+-----------+ +| | | ``kretprobe+`` [#kprobe]_ | | ++ + +----------------------------------+-----------+ +| | | ``ksyscall+`` [#ksyscall]_ | | ++ + +----------------------------------+-----------+ +| | | ``kretsyscall+`` [#ksyscall]_ | | ++ + +----------------------------------+-----------+ +| | | ``uprobe+`` [#uprobe]_ | | ++ + +----------------------------------+-----------+ +| | | ``uprobe.s+`` [#uprobe]_ | Yes | ++ + +----------------------------------+-----------+ +| | | ``uretprobe+`` [#uprobe]_ | | ++ + +----------------------------------+-----------+ +| | | ``uretprobe.s+`` [#uprobe]_ | Yes | ++ + +----------------------------------+-----------+ +| | | ``usdt+`` [#usdt]_ | | ++ +----------------------------------------+----------------------------------+-----------+ +| | ``BPF_TRACE_KPROBE_MULTI`` | ``kprobe.multi+`` [#kpmulti]_ | | ++ + +----------------------------------+-----------+ +| | | ``kretprobe.multi+`` [#kpmulti]_ | | ++-------------------------------------------+----------------------------------------+----------------------------------+-----------+ +| ``BPF_PROG_TYPE_LIRC_MODE2`` | ``BPF_LIRC_MODE2`` | ``lirc_mode2`` | | ++-------------------------------------------+----------------------------------------+----------------------------------+-----------+ +| ``BPF_PROG_TYPE_LSM`` | ``BPF_LSM_CGROUP`` | ``lsm_cgroup+`` | | ++ +----------------------------------------+----------------------------------+-----------+ +| | ``BPF_LSM_MAC`` | ``lsm+`` [#lsm]_ | | ++ + +----------------------------------+-----------+ +| | | ``lsm.s+`` [#lsm]_ | Yes | ++-------------------------------------------+----------------------------------------+----------------------------------+-----------+ +| ``BPF_PROG_TYPE_LWT_IN`` | | ``lwt_in`` | | ++-------------------------------------------+----------------------------------------+----------------------------------+-----------+ +| ``BPF_PROG_TYPE_LWT_OUT`` | | ``lwt_out`` | | ++-------------------------------------------+----------------------------------------+----------------------------------+-----------+ +| ``BPF_PROG_TYPE_LWT_SEG6LOCAL`` | | ``lwt_seg6local`` | | ++-------------------------------------------+----------------------------------------+----------------------------------+-----------+ +| ``BPF_PROG_TYPE_LWT_XMIT`` | | ``lwt_xmit`` | | ++-------------------------------------------+----------------------------------------+----------------------------------+-----------+ +| ``BPF_PROG_TYPE_PERF_EVENT`` | | ``perf_event`` | | ++-------------------------------------------+----------------------------------------+----------------------------------+-----------+ +| ``BPF_PROG_TYPE_RAW_TRACEPOINT_WRITABLE`` | | ``raw_tp.w+`` [#rawtp]_ | | ++ + +----------------------------------+-----------+ +| | | ``raw_tracepoint.w+`` | | ++-------------------------------------------+----------------------------------------+----------------------------------+-----------+ +| ``BPF_PROG_TYPE_RAW_TRACEPOINT`` | | ``raw_tp+`` [#rawtp]_ | | ++ + +----------------------------------+-----------+ +| | | ``raw_tracepoint+`` | | ++-------------------------------------------+----------------------------------------+----------------------------------+-----------+ +| ``BPF_PROG_TYPE_SCHED_ACT`` | | ``action`` | | ++-------------------------------------------+----------------------------------------+----------------------------------+-----------+ +| ``BPF_PROG_TYPE_SCHED_CLS`` | | ``classifier`` | | ++ + +----------------------------------+-----------+ +| | | ``tc`` | | ++-------------------------------------------+----------------------------------------+----------------------------------+-----------+ +| ``BPF_PROG_TYPE_SK_LOOKUP`` | ``BPF_SK_LOOKUP`` | ``sk_lookup`` | | ++-------------------------------------------+----------------------------------------+----------------------------------+-----------+ +| ``BPF_PROG_TYPE_SK_MSG`` | ``BPF_SK_MSG_VERDICT`` | ``sk_msg`` | | ++-------------------------------------------+----------------------------------------+----------------------------------+-----------+ +| ``BPF_PROG_TYPE_SK_REUSEPORT`` | ``BPF_SK_REUSEPORT_SELECT_OR_MIGRATE`` | ``sk_reuseport/migrate`` | | ++ +----------------------------------------+----------------------------------+-----------+ +| | ``BPF_SK_REUSEPORT_SELECT`` | ``sk_reuseport`` | | ++-------------------------------------------+----------------------------------------+----------------------------------+-----------+ +| ``BPF_PROG_TYPE_SK_SKB`` | | ``sk_skb`` | | ++ +----------------------------------------+----------------------------------+-----------+ +| | ``BPF_SK_SKB_STREAM_PARSER`` | ``sk_skb/stream_parser`` | | ++ +----------------------------------------+----------------------------------+-----------+ +| | ``BPF_SK_SKB_STREAM_VERDICT`` | ``sk_skb/stream_verdict`` | | ++-------------------------------------------+----------------------------------------+----------------------------------+-----------+ +| ``BPF_PROG_TYPE_SOCKET_FILTER`` | | ``socket`` | | ++-------------------------------------------+----------------------------------------+----------------------------------+-----------+ +| ``BPF_PROG_TYPE_SOCK_OPS`` | ``BPF_CGROUP_SOCK_OPS`` | ``sockops`` | | ++-------------------------------------------+----------------------------------------+----------------------------------+-----------+ +| ``BPF_PROG_TYPE_STRUCT_OPS`` | | ``struct_ops+`` | | ++-------------------------------------------+----------------------------------------+----------------------------------+-----------+ +| ``BPF_PROG_TYPE_SYSCALL`` | | ``syscall`` | Yes | ++-------------------------------------------+----------------------------------------+----------------------------------+-----------+ +| ``BPF_PROG_TYPE_TRACEPOINT`` | | ``tp+`` [#tp]_ | | ++ + +----------------------------------+-----------+ +| | | ``tracepoint+`` [#tp]_ | | ++-------------------------------------------+----------------------------------------+----------------------------------+-----------+ +| ``BPF_PROG_TYPE_TRACING`` | ``BPF_MODIFY_RETURN`` | ``fmod_ret+`` [#fentry]_ | | ++ + +----------------------------------+-----------+ +| | | ``fmod_ret.s+`` [#fentry]_ | Yes | ++ +----------------------------------------+----------------------------------+-----------+ +| | ``BPF_TRACE_FENTRY`` | ``fentry+`` [#fentry]_ | | ++ + +----------------------------------+-----------+ +| | | ``fentry.s+`` [#fentry]_ | Yes | ++ +----------------------------------------+----------------------------------+-----------+ +| | ``BPF_TRACE_FEXIT`` | ``fexit+`` [#fentry]_ | | ++ + +----------------------------------+-----------+ +| | | ``fexit.s+`` [#fentry]_ | Yes | ++ +----------------------------------------+----------------------------------+-----------+ +| | ``BPF_TRACE_ITER`` | ``iter+`` [#iter]_ | | ++ + +----------------------------------+-----------+ +| | | ``iter.s+`` [#iter]_ | Yes | ++ +----------------------------------------+----------------------------------+-----------+ +| | ``BPF_TRACE_RAW_TP`` | ``tp_btf+`` [#fentry]_ | | ++-------------------------------------------+----------------------------------------+----------------------------------+-----------+ +| ``BPF_PROG_TYPE_XDP`` | ``BPF_XDP_CPUMAP`` | ``xdp.frags/cpumap`` | | ++ + +----------------------------------+-----------+ +| | | ``xdp/cpumap`` | | ++ +----------------------------------------+----------------------------------+-----------+ +| | ``BPF_XDP_DEVMAP`` | ``xdp.frags/devmap`` | | ++ + +----------------------------------+-----------+ +| | | ``xdp/devmap`` | | ++ +----------------------------------------+----------------------------------+-----------+ +| | ``BPF_XDP`` | ``xdp.frags`` | | ++ + +----------------------------------+-----------+ +| | | ``xdp`` | | ++-------------------------------------------+----------------------------------------+----------------------------------+-----------+ + + +.. rubric:: Footnotes + +.. [#fentry] The ``fentry`` attach format is ``fentry[.s]/``. +.. [#kprobe] The ``kprobe`` attach format is ``kprobe/[+]``. Valid + characters for ``function`` are ``a-zA-Z0-9_.`` and ``offset`` must be a valid + non-negative integer. +.. [#ksyscall] The ``ksyscall`` attach format is ``ksyscall/``. +.. [#uprobe] The ``uprobe`` attach format is ``uprobe[.s]/:[+]``. +.. [#usdt] The ``usdt`` attach format is ``usdt/::``. +.. [#kpmulti] The ``kprobe.multi`` attach format is ``kprobe.multi/`` where ``pattern`` + supports ``*`` and ``?`` wildcards. Valid characters for pattern are + ``a-zA-Z0-9_.*?``. +.. [#lsm] The ``lsm`` attachment format is ``lsm[.s]/``. +.. [#rawtp] The ``raw_tp`` attach format is ``raw_tracepoint[.w]/``. +.. [#tp] The ``tracepoint`` attach format is ``tracepoint//``. +.. [#iter] The ``iter`` attach format is ``iter[.s]/``. diff --git a/Documentation/bpf/programs.rst b/Documentation/bpf/programs.rst index 620eb667ac7a..c99000ab6d9b 100644 --- a/Documentation/bpf/programs.rst +++ b/Documentation/bpf/programs.rst @@ -7,3 +7,6 @@ Program Types :glob: prog_* + +For a list of all program types, see :ref:`program_types_and_elf` in +the :ref:`libbpf` documentation. From 3685b0dc0d02ad89a36893d9d6be1675dd40a5fd Mon Sep 17 00:00:00 2001 From: Maryam Tahhan Date: Wed, 23 Nov 2022 09:23:20 +0000 Subject: [PATCH 1202/2002] docs/bpf: Fix sphinx warnings for cpumap Sphinx version >=3.1 warns about duplicate function declarations in the CPUMAP documentation. This is because the function name is the same for kernel and user space BPF progs but the parameters and return types they take is what differs. This patch moves from using the ``c:function::`` directive to using the ``code-block:: c`` directive. The patches also fix the indentation for the text associated with the "new" code block delcarations. The missing support of c:namespace-push:: and c:namespace-pop:: directives by helper scripts for kernel documentation prevents using the ``c:function::`` directive with proper namespacing. Signed-off-by: Maryam Tahhan Signed-off-by: Daniel Borkmann Link: https://lore.kernel.org/bpf/20221123092321.88558-2-mtahhan@redhat.com --- Documentation/bpf/map_cpumap.rst | 56 +++++++++++++++++++------------- 1 file changed, 34 insertions(+), 22 deletions(-) diff --git a/Documentation/bpf/map_cpumap.rst b/Documentation/bpf/map_cpumap.rst index 61a797a86342..923cfc8ab51f 100644 --- a/Documentation/bpf/map_cpumap.rst +++ b/Documentation/bpf/map_cpumap.rst @@ -30,29 +30,35 @@ Usage Kernel BPF ---------- -.. c:function:: +bpf_redirect_map() +^^^^^^^^^^^^^^^^^^ +.. code-block:: c + long bpf_redirect_map(struct bpf_map *map, u32 key, u64 flags) - Redirect the packet to the endpoint referenced by ``map`` at index ``key``. - For ``BPF_MAP_TYPE_CPUMAP`` this map contains references to CPUs. +Redirect the packet to the endpoint referenced by ``map`` at index ``key``. +For ``BPF_MAP_TYPE_CPUMAP`` this map contains references to CPUs. - The lower two bits of ``flags`` are used as the return code if the map lookup - fails. This is so that the return value can be one of the XDP program return - codes up to ``XDP_TX``, as chosen by the caller. +The lower two bits of ``flags`` are used as the return code if the map lookup +fails. This is so that the return value can be one of the XDP program return +codes up to ``XDP_TX``, as chosen by the caller. -Userspace ---------- +User space +---------- .. note:: CPUMAP entries can only be updated/looked up/deleted from user space and not from an eBPF program. Trying to call these functions from a kernel eBPF program will result in the program failing to load and a verifier warning. -.. c:function:: +bpf_map_update_elem() +^^^^^^^^^^^^^^^^^^^^^ +.. code-block:: c + int bpf_map_update_elem(int fd, const void *key, const void *value, __u64 flags); - CPU entries can be added or updated using the ``bpf_map_update_elem()`` - helper. This helper replaces existing elements atomically. The ``value`` parameter - can be ``struct bpf_cpumap_val``. +CPU entries can be added or updated using the ``bpf_map_update_elem()`` +helper. This helper replaces existing elements atomically. The ``value`` parameter +can be ``struct bpf_cpumap_val``. .. code-block:: c @@ -64,23 +70,29 @@ Userspace } bpf_prog; }; - The flags argument can be one of the following: +The flags argument can be one of the following: - BPF_ANY: Create a new element or update an existing element. - BPF_NOEXIST: Create a new element only if it did not exist. - BPF_EXIST: Update an existing element. -.. c:function:: +bpf_map_lookup_elem() +^^^^^^^^^^^^^^^^^^^^^ +.. code-block:: c + int bpf_map_lookup_elem(int fd, const void *key, void *value); - CPU entries can be retrieved using the ``bpf_map_lookup_elem()`` - helper. +CPU entries can be retrieved using the ``bpf_map_lookup_elem()`` +helper. + +bpf_map_delete_elem() +^^^^^^^^^^^^^^^^^^^^^ +.. code-block:: c -.. c:function:: int bpf_map_delete_elem(int fd, const void *key); - CPU entries can be deleted using the ``bpf_map_delete_elem()`` - helper. This helper will return 0 on success, or negative error in case of - failure. +CPU entries can be deleted using the ``bpf_map_delete_elem()`` +helper. This helper will return 0 on success, or negative error in case of +failure. Examples ======== @@ -141,8 +153,8 @@ The following code snippet shows how to declare a ``BPF_MAP_TYPE_CPUMAP`` called return bpf_redirect_map(&cpu_map, cpu_dest, 0); } -Userspace ---------- +User space +---------- The following code snippet shows how to dynamically set the max_entries for a CPUMAP to the max number of cpus available on the system. From c645eee4d35b58bffc90ee086f7a087fa942fd52 Mon Sep 17 00:00:00 2001 From: Maryam Tahhan Date: Wed, 23 Nov 2022 09:23:21 +0000 Subject: [PATCH 1203/2002] docs/bpf: Fix sphinx warnings for devmap Sphinx version >=3.1 warns about duplicate function declarations in the DEVMAP documentation. This is because the function name is the same for kernel and user space BPF progs but the parameters and return types they take is what differs. This patch moves from using the ``c:function::`` directive to using the ``code-block:: c`` directive. The patches also fix the indentation for the text associated with the "new" code block delcarations. The missing support of c:namespace-push:: and c:namespace-pop:: directives by helper scripts for kernel documentation prevents using the ``c:function::`` directive with proper namespacing. Signed-off-by: Maryam Tahhan Signed-off-by: Daniel Borkmann Link: https://lore.kernel.org/bpf/20221123092321.88558-3-mtahhan@redhat.com --- Documentation/bpf/map_devmap.rst | 68 ++++++++++++++++++++------------ 1 file changed, 42 insertions(+), 26 deletions(-) diff --git a/Documentation/bpf/map_devmap.rst b/Documentation/bpf/map_devmap.rst index f64da348dbfe..927312c7b8c8 100644 --- a/Documentation/bpf/map_devmap.rst +++ b/Documentation/bpf/map_devmap.rst @@ -29,8 +29,11 @@ Usage ===== Kernel BPF ---------- -.. c:function:: - long bpf_redirect_map(struct bpf_map *map, u32 key, u64 flags) +bpf_redirect_map() +^^^^^^^^^^^^^^^^^^ +.. code-block:: c + + long bpf_redirect_map(struct bpf_map *map, u32 key, u64 flags) Redirect the packet to the endpoint referenced by ``map`` at index ``key``. For ``BPF_MAP_TYPE_DEVMAP`` and ``BPF_MAP_TYPE_DEVMAP_HASH`` this map contains @@ -56,26 +59,32 @@ lower bits of the ``flags`` argument if the map lookup fails. More information about redirection can be found :doc:`redirect` -.. c:function:: +bpf_map_lookup_elem() +^^^^^^^^^^^^^^^^^^^^^ +.. code-block:: c + void *bpf_map_lookup_elem(struct bpf_map *map, const void *key) Net device entries can be retrieved using the ``bpf_map_lookup_elem()`` helper. -Userspace ---------- +User space +---------- .. note:: DEVMAP entries can only be updated/deleted from user space and not from an eBPF program. Trying to call these functions from a kernel eBPF program will result in the program failing to load and a verifier warning. -.. c:function:: +bpf_map_update_elem() +^^^^^^^^^^^^^^^^^^^^^ +.. code-block:: c + int bpf_map_update_elem(int fd, const void *key, const void *value, __u64 flags); - Net device entries can be added or updated using the ``bpf_map_update_elem()`` - helper. This helper replaces existing elements atomically. The ``value`` parameter - can be ``struct bpf_devmap_val`` or a simple ``int ifindex`` for backwards - compatibility. +Net device entries can be added or updated using the ``bpf_map_update_elem()`` +helper. This helper replaces existing elements atomically. The ``value`` parameter +can be ``struct bpf_devmap_val`` or a simple ``int ifindex`` for backwards +compatibility. .. code-block:: c @@ -87,35 +96,42 @@ Userspace } bpf_prog; }; - The ``flags`` argument can be one of the following: - +The ``flags`` argument can be one of the following: - ``BPF_ANY``: Create a new element or update an existing element. - ``BPF_NOEXIST``: Create a new element only if it did not exist. - ``BPF_EXIST``: Update an existing element. - DEVMAPs can associate a program with a device entry by adding a ``bpf_prog.fd`` - to ``struct bpf_devmap_val``. Programs are run after ``XDP_REDIRECT`` and have - access to both Rx device and Tx device. The program associated with the ``fd`` - must have type XDP with expected attach type ``xdp_devmap``. - When a program is associated with a device index, the program is run on an - ``XDP_REDIRECT`` and before the buffer is added to the per-cpu queue. Examples - of how to attach/use xdp_devmap progs can be found in the kernel selftests: +DEVMAPs can associate a program with a device entry by adding a ``bpf_prog.fd`` +to ``struct bpf_devmap_val``. Programs are run after ``XDP_REDIRECT`` and have +access to both Rx device and Tx device. The program associated with the ``fd`` +must have type XDP with expected attach type ``xdp_devmap``. +When a program is associated with a device index, the program is run on an +``XDP_REDIRECT`` and before the buffer is added to the per-cpu queue. Examples +of how to attach/use xdp_devmap progs can be found in the kernel selftests: - - ``tools/testing/selftests/bpf/prog_tests/xdp_devmap_attach.c`` - - ``tools/testing/selftests/bpf/progs/test_xdp_with_devmap_helpers.c`` +- ``tools/testing/selftests/bpf/prog_tests/xdp_devmap_attach.c`` +- ``tools/testing/selftests/bpf/progs/test_xdp_with_devmap_helpers.c`` + +bpf_map_lookup_elem() +^^^^^^^^^^^^^^^^^^^^^ +.. code-block:: c .. c:function:: int bpf_map_lookup_elem(int fd, const void *key, void *value); - Net device entries can be retrieved using the ``bpf_map_lookup_elem()`` - helper. +Net device entries can be retrieved using the ``bpf_map_lookup_elem()`` +helper. + +bpf_map_delete_elem() +^^^^^^^^^^^^^^^^^^^^^ +.. code-block:: c .. c:function:: int bpf_map_delete_elem(int fd, const void *key); - Net device entries can be deleted using the ``bpf_map_delete_elem()`` - helper. This helper will return 0 on success, or negative error in case of - failure. +Net device entries can be deleted using the ``bpf_map_delete_elem()`` +helper. This helper will return 0 on success, or negative error in case of +failure. Examples ======== From 264c21867a0e1b9a820732ba7c0cf19c6784e5fe Mon Sep 17 00:00:00 2001 From: Donald Hunter Date: Wed, 23 Nov 2022 14:11:51 +0000 Subject: [PATCH 1204/2002] docs/bpf: Document BPF_MAP_TYPE_BLOOM_FILTER Add documentation for BPF_MAP_TYPE_BLOOM_FILTER including kernel BPF helper usage, userspace usage and examples. Signed-off-by: Donald Hunter Signed-off-by: Daniel Borkmann Acked-by: Joanne Koong Link: https://lore.kernel.org/bpf/20221123141151.54556-1-donald.hunter@gmail.com --- Documentation/bpf/map_bloom_filter.rst | 174 +++++++++++++++++++++++++ 1 file changed, 174 insertions(+) create mode 100644 Documentation/bpf/map_bloom_filter.rst diff --git a/Documentation/bpf/map_bloom_filter.rst b/Documentation/bpf/map_bloom_filter.rst new file mode 100644 index 000000000000..c82487f2fe0d --- /dev/null +++ b/Documentation/bpf/map_bloom_filter.rst @@ -0,0 +1,174 @@ +.. SPDX-License-Identifier: GPL-2.0-only +.. Copyright (C) 2022 Red Hat, Inc. + +========================= +BPF_MAP_TYPE_BLOOM_FILTER +========================= + +.. note:: + - ``BPF_MAP_TYPE_BLOOM_FILTER`` was introduced in kernel version 5.16 + +``BPF_MAP_TYPE_BLOOM_FILTER`` provides a BPF bloom filter map. Bloom +filters are a space-efficient probabilistic data structure used to +quickly test whether an element exists in a set. In a bloom filter, +false positives are possible whereas false negatives are not. + +The bloom filter map does not have keys, only values. When the bloom +filter map is created, it must be created with a ``key_size`` of 0. The +bloom filter map supports two operations: + +- push: adding an element to the map +- peek: determining whether an element is present in the map + +BPF programs must use ``bpf_map_push_elem`` to add an element to the +bloom filter map and ``bpf_map_peek_elem`` to query the map. These +operations are exposed to userspace applications using the existing +``bpf`` syscall in the following way: + +- ``BPF_MAP_UPDATE_ELEM`` -> push +- ``BPF_MAP_LOOKUP_ELEM`` -> peek + +The ``max_entries`` size that is specified at map creation time is used +to approximate a reasonable bitmap size for the bloom filter, and is not +otherwise strictly enforced. If the user wishes to insert more entries +into the bloom filter than ``max_entries``, this may lead to a higher +false positive rate. + +The number of hashes to use for the bloom filter is configurable using +the lower 4 bits of ``map_extra`` in ``union bpf_attr`` at map creation +time. If no number is specified, the default used will be 5 hash +functions. In general, using more hashes decreases both the false +positive rate and the speed of a lookup. + +It is not possible to delete elements from a bloom filter map. A bloom +filter map may be used as an inner map. The user is responsible for +synchronising concurrent updates and lookups to ensure no false negative +lookups occur. + +Usage +===== + +Kernel BPF +---------- + +bpf_map_push_elem() +~~~~~~~~~~~~~~~~~~~ + +.. code-block:: c + + long bpf_map_push_elem(struct bpf_map *map, const void *value, u64 flags) + +A ``value`` can be added to a bloom filter using the +``bpf_map_push_elem()`` helper. The ``flags`` parameter must be set to +``BPF_ANY`` when adding an entry to the bloom filter. This helper +returns ``0`` on success, or negative error in case of failure. + +bpf_map_peek_elem() +~~~~~~~~~~~~~~~~~~~ + +.. code-block:: c + + long bpf_map_peek_elem(struct bpf_map *map, void *value) + +The ``bpf_map_peek_elem()`` helper is used to determine whether +``value`` is present in the bloom filter map. This helper returns ``0`` +if ``value`` is probably present in the map, or ``-ENOENT`` if ``value`` +is definitely not present in the map. + +Userspace +--------- + +bpf_map_update_elem() +~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: c + + int bpf_map_update_elem (int fd, const void *key, const void *value, __u64 flags) + +A userspace program can add a ``value`` to a bloom filter using libbpf's +``bpf_map_update_elem`` function. The ``key`` parameter must be set to +``NULL`` and ``flags`` must be set to ``BPF_ANY``. Returns ``0`` on +success, or negative error in case of failure. + +bpf_map_lookup_elem() +~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: c + + int bpf_map_lookup_elem (int fd, const void *key, void *value) + +A userspace program can determine the presence of ``value`` in a bloom +filter using libbpf's ``bpf_map_lookup_elem`` function. The ``key`` +parameter must be set to ``NULL``. Returns ``0`` if ``value`` is +probably present in the map, or ``-ENOENT`` if ``value`` is definitely +not present in the map. + +Examples +======== + +Kernel BPF +---------- + +This snippet shows how to declare a bloom filter in a BPF program: + +.. code-block:: c + + struct { + __uint(type, BPF_MAP_TYPE_BLOOM_FILTER); + __type(value, __u32); + __uint(max_entries, 1000); + __uint(map_extra, 3); + } bloom_filter SEC(".maps"); + +This snippet shows how to determine presence of a value in a bloom +filter in a BPF program: + +.. code-block:: c + + void *lookup(__u32 key) + { + if (bpf_map_peek_elem(&bloom_filter, &key) == 0) { + /* Verify not a false positive and fetch an associated + * value using a secondary lookup, e.g. in a hash table + */ + return bpf_map_lookup_elem(&hash_table, &key); + } + return 0; + } + +Userspace +--------- + +This snippet shows how to use libbpf to create a bloom filter map from +userspace: + +.. code-block:: c + + int create_bloom() + { + LIBBPF_OPTS(bpf_map_create_opts, opts, + .map_extra = 3); /* number of hashes */ + + return bpf_map_create(BPF_MAP_TYPE_BLOOM_FILTER, + "ipv6_bloom", /* name */ + 0, /* key size, must be zero */ + sizeof(ipv6_addr), /* value size */ + 10000, /* max entries */ + &opts); /* create options */ + } + +This snippet shows how to add an element to a bloom filter from +userspace: + +.. code-block:: c + + int add_element(struct bpf_map *bloom_map, __u32 value) + { + int bloom_fd = bpf_map__fd(bloom_map); + return bpf_map_update_elem(bloom_fd, NULL, &value, BPF_ANY); + } + +References +========== + +https://lwn.net/ml/bpf/20210831225005.2762202-1-joannekoong@fb.com/ From f17472d4599697d701aa239b4c475a506bccfd19 Mon Sep 17 00:00:00 2001 From: Stanislav Fomichev Date: Tue, 22 Nov 2022 19:54:22 -0800 Subject: [PATCH 1205/2002] bpf: Prevent decl_tag from being referenced in func_proto arg Syzkaller managed to hit another decl_tag issue: btf_func_proto_check kernel/bpf/btf.c:4506 [inline] btf_check_all_types kernel/bpf/btf.c:4734 [inline] btf_parse_type_sec+0x1175/0x1980 kernel/bpf/btf.c:4763 btf_parse kernel/bpf/btf.c:5042 [inline] btf_new_fd+0x65a/0xb00 kernel/bpf/btf.c:6709 bpf_btf_load+0x6f/0x90 kernel/bpf/syscall.c:4342 __sys_bpf+0x50a/0x6c0 kernel/bpf/syscall.c:5034 __do_sys_bpf kernel/bpf/syscall.c:5093 [inline] __se_sys_bpf kernel/bpf/syscall.c:5091 [inline] __x64_sys_bpf+0x7c/0x90 kernel/bpf/syscall.c:5091 do_syscall_64+0x54/0x70 arch/x86/entry/common.c:48 This seems similar to commit ea68376c8bed ("bpf: prevent decl_tag from being referenced in func_proto") but for the argument. Reported-by: syzbot+8dd0551dda6020944c5d@syzkaller.appspotmail.com Signed-off-by: Stanislav Fomichev Signed-off-by: Daniel Borkmann Acked-by: Yonghong Song Link: https://lore.kernel.org/bpf/20221123035422.872531-2-sdf@google.com --- kernel/bpf/btf.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/kernel/bpf/btf.c b/kernel/bpf/btf.c index 1a59cc7ad730..cb43cb842e16 100644 --- a/kernel/bpf/btf.c +++ b/kernel/bpf/btf.c @@ -4792,6 +4792,11 @@ static int btf_func_proto_check(struct btf_verifier_env *env, break; } + if (btf_type_is_resolve_source_only(arg_type)) { + btf_verifier_log_type(env, t, "Invalid arg#%u", i + 1); + return -EINVAL; + } + if (args[i].name_off && (!btf_name_offset_valid(btf, args[i].name_off) || !btf_name_valid_identifier(btf, args[i].name_off))) { From 8e898aaa733eca61393fc036c8a4b5834fee5dd3 Mon Sep 17 00:00:00 2001 From: Stanislav Fomichev Date: Tue, 22 Nov 2022 19:54:21 -0800 Subject: [PATCH 1206/2002] selftests/bpf: Add reproducer for decl_tag in func_proto argument It should trigger a WARN_ON_ONCE in btf_type_id_size: RIP: 0010:btf_type_id_size+0x8bd/0x940 kernel/bpf/btf.c:1952 btf_func_proto_check kernel/bpf/btf.c:4506 [inline] btf_check_all_types kernel/bpf/btf.c:4734 [inline] btf_parse_type_sec+0x1175/0x1980 kernel/bpf/btf.c:4763 btf_parse kernel/bpf/btf.c:5042 [inline] btf_new_fd+0x65a/0xb00 kernel/bpf/btf.c:6709 bpf_btf_load+0x6f/0x90 kernel/bpf/syscall.c:4342 __sys_bpf+0x50a/0x6c0 kernel/bpf/syscall.c:5034 __do_sys_bpf kernel/bpf/syscall.c:5093 [inline] __se_sys_bpf kernel/bpf/syscall.c:5091 [inline] __x64_sys_bpf+0x7c/0x90 kernel/bpf/syscall.c:5091 do_syscall_64+0x54/0x70 arch/x86/entry/common.c:48 Signed-off-by: Stanislav Fomichev Signed-off-by: Daniel Borkmann Acked-by: Yonghong Song Link: https://lore.kernel.org/bpf/20221123035422.872531-1-sdf@google.com --- tools/testing/selftests/bpf/prog_tests/btf.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/tools/testing/selftests/bpf/prog_tests/btf.c b/tools/testing/selftests/bpf/prog_tests/btf.c index 95a2b80f0d17..de1b5b9eb93a 100644 --- a/tools/testing/selftests/bpf/prog_tests/btf.c +++ b/tools/testing/selftests/bpf/prog_tests/btf.c @@ -3948,6 +3948,20 @@ static struct btf_raw_test raw_tests[] = { .btf_load_err = true, .err_str = "Invalid return type", }, +{ + .descr = "decl_tag test #17, func proto, argument", + .raw_types = { + BTF_TYPE_ENC(NAME_TBD, BTF_INFO_ENC(BTF_KIND_DECL_TAG, 0, 0), 4), (-1), /* [1] */ + BTF_TYPE_ENC(0, BTF_INFO_ENC(BTF_KIND_PTR, 0, 0), 0), /* [2] */ + BTF_FUNC_PROTO_ENC(0, 1), /* [3] */ + BTF_FUNC_PROTO_ARG_ENC(NAME_TBD, 1), + BTF_VAR_ENC(NAME_TBD, 2, 0), /* [4] */ + BTF_END_RAW, + }, + BTF_STR_SEC("\0local\0tag1\0var"), + .btf_load_err = true, + .err_str = "Invalid arg#1", +}, { .descr = "type_tag test #1", .raw_types = { From 539886a32a6a4601241d928dc8b776b052c04ed0 Mon Sep 17 00:00:00 2001 From: Donald Hunter Date: Tue, 22 Nov 2022 14:39:33 +0000 Subject: [PATCH 1207/2002] docs/bpf: Fix sphinx warnings in BPF map docs Fix duplicate C declaration warnings when using sphinx >= 3.1. Reported-by: Akira Yokosawa Signed-off-by: Donald Hunter Signed-off-by: Daniel Borkmann Reviewed-by: Akira Yokosawa Link: https://lore.kernel.org/bpf/ed4dac84-1b12-5c58-e4de-93ab9ac67c09@gmail.com Link: https://lore.kernel.org/bpf/20221122143933.91321-1-donald.hunter@gmail.com --- Documentation/bpf/map_array.rst | 20 ++++++++++++--- Documentation/bpf/map_hash.rst | 33 ++++++++++++++++++++---- Documentation/bpf/map_lpm_trie.rst | 24 +++++++++++++++--- Documentation/bpf/map_of_maps.rst | 6 ++++- Documentation/bpf/map_queue_stack.rst | 36 ++++++++++++++++++++++----- 5 files changed, 99 insertions(+), 20 deletions(-) diff --git a/Documentation/bpf/map_array.rst b/Documentation/bpf/map_array.rst index 97bb80333254..f2f51a53e8ae 100644 --- a/Documentation/bpf/map_array.rst +++ b/Documentation/bpf/map_array.rst @@ -32,7 +32,11 @@ Usage Kernel BPF ---------- -.. c:function:: +bpf_map_lookup_elem() +~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: c + void *bpf_map_lookup_elem(struct bpf_map *map, const void *key) Array elements can be retrieved using the ``bpf_map_lookup_elem()`` helper. @@ -40,7 +44,11 @@ This helper returns a pointer into the array element, so to avoid data races with userspace reading the value, the user must use primitives like ``__sync_fetch_and_add()`` when updating the value in-place. -.. c:function:: +bpf_map_update_elem() +~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: c + long bpf_map_update_elem(struct bpf_map *map, const void *key, const void *value, u64 flags) Array elements can be updated using the ``bpf_map_update_elem()`` helper. @@ -53,7 +61,7 @@ To clear an array element, you may use ``bpf_map_update_elem()`` to insert a zero value to that index. Per CPU Array -~~~~~~~~~~~~~ +------------- Values stored in ``BPF_MAP_TYPE_ARRAY`` can be accessed by multiple programs across different CPUs. To restrict storage to a single CPU, you may use a @@ -63,7 +71,11 @@ When using a ``BPF_MAP_TYPE_PERCPU_ARRAY`` the ``bpf_map_update_elem()`` and ``bpf_map_lookup_elem()`` helpers automatically access the slot for the current CPU. -.. c:function:: +bpf_map_lookup_percpu_elem() +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: c + void *bpf_map_lookup_percpu_elem(struct bpf_map *map, const void *key, u32 cpu) The ``bpf_map_lookup_percpu_elem()`` helper can be used to lookup the array diff --git a/Documentation/bpf/map_hash.rst b/Documentation/bpf/map_hash.rst index e85120878b27..8669426264c6 100644 --- a/Documentation/bpf/map_hash.rst +++ b/Documentation/bpf/map_hash.rst @@ -34,7 +34,14 @@ the ``BPF_F_NO_COMMON_LRU`` flag when calling ``bpf_map_create``. Usage ===== -.. c:function:: +Kernel BPF +---------- + +bpf_map_update_elem() +~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: c + long bpf_map_update_elem(struct bpf_map *map, const void *key, const void *value, u64 flags) Hash entries can be added or updated using the ``bpf_map_update_elem()`` @@ -49,14 +56,22 @@ parameter can be used to control the update behaviour: ``bpf_map_update_elem()`` returns 0 on success, or negative error in case of failure. -.. c:function:: +bpf_map_lookup_elem() +~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: c + void *bpf_map_lookup_elem(struct bpf_map *map, const void *key) Hash entries can be retrieved using the ``bpf_map_lookup_elem()`` helper. This helper returns a pointer to the value associated with ``key``, or ``NULL`` if no entry was found. -.. c:function:: +bpf_map_delete_elem() +~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: c + long bpf_map_delete_elem(struct bpf_map *map, const void *key) Hash entries can be deleted using the ``bpf_map_delete_elem()`` @@ -70,7 +85,11 @@ For ``BPF_MAP_TYPE_PERCPU_HASH`` and ``BPF_MAP_TYPE_LRU_PERCPU_HASH`` the ``bpf_map_update_elem()`` and ``bpf_map_lookup_elem()`` helpers automatically access the hash slot for the current CPU. -.. c:function:: +bpf_map_lookup_percpu_elem() +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: c + void *bpf_map_lookup_percpu_elem(struct bpf_map *map, const void *key, u32 cpu) The ``bpf_map_lookup_percpu_elem()`` helper can be used to lookup the @@ -89,7 +108,11 @@ See ``tools/testing/selftests/bpf/progs/test_spin_lock.c``. Userspace --------- -.. c:function:: +bpf_map_get_next_key() +~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: c + int bpf_map_get_next_key(int fd, const void *cur_key, void *next_key) In userspace, it is possible to iterate through the keys of a hash using diff --git a/Documentation/bpf/map_lpm_trie.rst b/Documentation/bpf/map_lpm_trie.rst index 31be1aa7ba2c..74d64a30f500 100644 --- a/Documentation/bpf/map_lpm_trie.rst +++ b/Documentation/bpf/map_lpm_trie.rst @@ -35,7 +35,11 @@ Usage Kernel BPF ---------- -.. c:function:: +bpf_map_lookup_elem() +~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: c + void *bpf_map_lookup_elem(struct bpf_map *map, const void *key) The longest prefix entry for a given data value can be found using the @@ -48,7 +52,11 @@ performing longest prefix lookups. For example, when searching for the longest prefix match for an IPv4 address, ``prefixlen`` should be set to ``32``. -.. c:function:: +bpf_map_update_elem() +~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: c + long bpf_map_update_elem(struct bpf_map *map, const void *key, const void *value, u64 flags) Prefix entries can be added or updated using the ``bpf_map_update_elem()`` @@ -61,7 +69,11 @@ case of failure. The flags parameter must be one of BPF_ANY, BPF_NOEXIST or BPF_EXIST, but the value is ignored, giving BPF_ANY semantics. -.. c:function:: +bpf_map_delete_elem() +~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: c + long bpf_map_delete_elem(struct bpf_map *map, const void *key) Prefix entries can be deleted using the ``bpf_map_delete_elem()`` @@ -74,7 +86,11 @@ Userspace Access from userspace uses libbpf APIs with the same names as above, with the map identified by ``fd``. -.. c:function:: +bpf_map_get_next_key() +~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: c + int bpf_map_get_next_key (int fd, const void *cur_key, void *next_key) A userspace program can iterate through the entries in an LPM trie using diff --git a/Documentation/bpf/map_of_maps.rst b/Documentation/bpf/map_of_maps.rst index 07212b9227a9..7b5617c2d017 100644 --- a/Documentation/bpf/map_of_maps.rst +++ b/Documentation/bpf/map_of_maps.rst @@ -45,7 +45,11 @@ Usage Kernel BPF Helper ----------------- -.. c:function:: +bpf_map_lookup_elem() +~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: c + void *bpf_map_lookup_elem(struct bpf_map *map, const void *key) Inner maps can be retrieved using the ``bpf_map_lookup_elem()`` helper. This diff --git a/Documentation/bpf/map_queue_stack.rst b/Documentation/bpf/map_queue_stack.rst index f20e31a647b9..8d14ed49d6e1 100644 --- a/Documentation/bpf/map_queue_stack.rst +++ b/Documentation/bpf/map_queue_stack.rst @@ -28,7 +28,11 @@ Usage Kernel BPF ---------- -.. c:function:: +bpf_map_push_elem() +~~~~~~~~~~~~~~~~~~~ + +.. code-block:: c + long bpf_map_push_elem(struct bpf_map *map, const void *value, u64 flags) An element ``value`` can be added to a queue or stack using the @@ -38,14 +42,22 @@ when the queue or stack is full, the oldest element will be removed to make room for ``value`` to be added. Returns ``0`` on success, or negative error in case of failure. -.. c:function:: +bpf_map_peek_elem() +~~~~~~~~~~~~~~~~~~~ + +.. code-block:: c + long bpf_map_peek_elem(struct bpf_map *map, void *value) This helper fetches an element ``value`` from a queue or stack without removing it. Returns ``0`` on success, or negative error in case of failure. -.. c:function:: +bpf_map_pop_elem() +~~~~~~~~~~~~~~~~~~ + +.. code-block:: c + long bpf_map_pop_elem(struct bpf_map *map, void *value) This helper removes an element into ``value`` from a queue or @@ -55,7 +67,11 @@ stack. Returns ``0`` on success, or negative error in case of failure. Userspace --------- -.. c:function:: +bpf_map_update_elem() +~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: c + int bpf_map_update_elem (int fd, const void *key, const void *value, __u64 flags) A userspace program can push ``value`` onto a queue or stack using libbpf's @@ -64,7 +80,11 @@ A userspace program can push ``value`` onto a queue or stack using libbpf's same semantics as the ``bpf_map_push_elem`` kernel helper. Returns ``0`` on success, or negative error in case of failure. -.. c:function:: +bpf_map_lookup_elem() +~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: c + int bpf_map_lookup_elem (int fd, const void *key, void *value) A userspace program can peek at the ``value`` at the head of a queue or stack @@ -72,7 +92,11 @@ using the libbpf ``bpf_map_lookup_elem`` function. The ``key`` parameter must be set to ``NULL``. Returns ``0`` on success, or negative error in case of failure. -.. c:function:: +bpf_map_lookup_and_delete_elem() +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: c + int bpf_map_lookup_and_delete_elem (int fd, const void *key, void *value) A userspace program can pop a ``value`` from the head of a queue or stack using From 72b43bde38de4aa05e6a7fa12d7965f48180deb6 Mon Sep 17 00:00:00 2001 From: Ji Rongfeng Date: Fri, 18 Nov 2022 16:18:18 +0800 Subject: [PATCH 1208/2002] bpf: Update bpf_{g,s}etsockopt() documentation * append missing optnames to the end * simplify bpf_getsockopt()'s doc Signed-off-by: Ji Rongfeng Link: https://lore.kernel.org/r/DU0P192MB15479B86200B1216EC90E162D6099@DU0P192MB1547.EURP192.PROD.OUTLOOK.COM Signed-off-by: Martin KaFai Lau --- include/uapi/linux/bpf.h | 21 ++++++++++++--------- tools/include/uapi/linux/bpf.h | 23 +++++++++++++---------- 2 files changed, 25 insertions(+), 19 deletions(-) diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h index ab86145df760..f89de51a45db 100644 --- a/include/uapi/linux/bpf.h +++ b/include/uapi/linux/bpf.h @@ -2584,14 +2584,19 @@ union bpf_attr { * * **SOL_SOCKET**, which supports the following *optname*\ s: * **SO_RCVBUF**, **SO_SNDBUF**, **SO_MAX_PACING_RATE**, * **SO_PRIORITY**, **SO_RCVLOWAT**, **SO_MARK**, - * **SO_BINDTODEVICE**, **SO_KEEPALIVE**. + * **SO_BINDTODEVICE**, **SO_KEEPALIVE**, **SO_REUSEADDR**, + * **SO_REUSEPORT**, **SO_BINDTOIFINDEX**, **SO_TXREHASH**. * * **IPPROTO_TCP**, which supports the following *optname*\ s: * **TCP_CONGESTION**, **TCP_BPF_IW**, * **TCP_BPF_SNDCWND_CLAMP**, **TCP_SAVE_SYN**, * **TCP_KEEPIDLE**, **TCP_KEEPINTVL**, **TCP_KEEPCNT**, - * **TCP_SYNCNT**, **TCP_USER_TIMEOUT**, **TCP_NOTSENT_LOWAT**. + * **TCP_SYNCNT**, **TCP_USER_TIMEOUT**, **TCP_NOTSENT_LOWAT**, + * **TCP_NODELAY**, **TCP_MAXSEG**, **TCP_WINDOW_CLAMP**, + * **TCP_THIN_LINEAR_TIMEOUTS**, **TCP_BPF_DELACK_MAX**, + * **TCP_BPF_RTO_MIN**. * * **IPPROTO_IP**, which supports *optname* **IP_TOS**. - * * **IPPROTO_IPV6**, which supports *optname* **IPV6_TCLASS**. + * * **IPPROTO_IPV6**, which supports the following *optname*\ s: + * **IPV6_TCLASS**, **IPV6_AUTOFLOWLABEL**. * Return * 0 on success, or a negative error in case of failure. * @@ -2808,12 +2813,10 @@ union bpf_attr { * and **BPF_CGROUP_INET6_CONNECT**. * * This helper actually implements a subset of **getsockopt()**. - * It supports the following *level*\ s: - * - * * **IPPROTO_TCP**, which supports *optname* - * **TCP_CONGESTION**. - * * **IPPROTO_IP**, which supports *optname* **IP_TOS**. - * * **IPPROTO_IPV6**, which supports *optname* **IPV6_TCLASS**. + * It supports the same set of *optname*\ s that is supported by + * the **bpf_setsockopt**\ () helper. The exceptions are + * **TCP_BPF_*** is **bpf_setsockopt**\ () only and + * **TCP_SAVED_SYN** is **bpf_getsockopt**\ () only. * Return * 0 on success, or a negative error in case of failure. * diff --git a/tools/include/uapi/linux/bpf.h b/tools/include/uapi/linux/bpf.h index 6580448e9f77..f89de51a45db 100644 --- a/tools/include/uapi/linux/bpf.h +++ b/tools/include/uapi/linux/bpf.h @@ -2584,14 +2584,19 @@ union bpf_attr { * * **SOL_SOCKET**, which supports the following *optname*\ s: * **SO_RCVBUF**, **SO_SNDBUF**, **SO_MAX_PACING_RATE**, * **SO_PRIORITY**, **SO_RCVLOWAT**, **SO_MARK**, - * **SO_BINDTODEVICE**, **SO_KEEPALIVE**. + * **SO_BINDTODEVICE**, **SO_KEEPALIVE**, **SO_REUSEADDR**, + * **SO_REUSEPORT**, **SO_BINDTOIFINDEX**, **SO_TXREHASH**. * * **IPPROTO_TCP**, which supports the following *optname*\ s: * **TCP_CONGESTION**, **TCP_BPF_IW**, * **TCP_BPF_SNDCWND_CLAMP**, **TCP_SAVE_SYN**, * **TCP_KEEPIDLE**, **TCP_KEEPINTVL**, **TCP_KEEPCNT**, - * **TCP_SYNCNT**, **TCP_USER_TIMEOUT**, **TCP_NOTSENT_LOWAT**. + * **TCP_SYNCNT**, **TCP_USER_TIMEOUT**, **TCP_NOTSENT_LOWAT**, + * **TCP_NODELAY**, **TCP_MAXSEG**, **TCP_WINDOW_CLAMP**, + * **TCP_THIN_LINEAR_TIMEOUTS**, **TCP_BPF_DELACK_MAX**, + * **TCP_BPF_RTO_MIN**. * * **IPPROTO_IP**, which supports *optname* **IP_TOS**. - * * **IPPROTO_IPV6**, which supports *optname* **IPV6_TCLASS**. + * * **IPPROTO_IPV6**, which supports the following *optname*\ s: + * **IPV6_TCLASS**, **IPV6_AUTOFLOWLABEL**. * Return * 0 on success, or a negative error in case of failure. * @@ -2647,7 +2652,7 @@ union bpf_attr { * Return * 0 on success, or a negative error in case of failure. * - * long bpf_redirect_map(struct bpf_map *map, u32 key, u64 flags) + * long bpf_redirect_map(struct bpf_map *map, u64 key, u64 flags) * Description * Redirect the packet to the endpoint referenced by *map* at * index *key*. Depending on its type, this *map* can contain @@ -2808,12 +2813,10 @@ union bpf_attr { * and **BPF_CGROUP_INET6_CONNECT**. * * This helper actually implements a subset of **getsockopt()**. - * It supports the following *level*\ s: - * - * * **IPPROTO_TCP**, which supports *optname* - * **TCP_CONGESTION**. - * * **IPPROTO_IP**, which supports *optname* **IP_TOS**. - * * **IPPROTO_IPV6**, which supports *optname* **IPV6_TCLASS**. + * It supports the same set of *optname*\ s that is supported by + * the **bpf_setsockopt**\ () helper. The exceptions are + * **TCP_BPF_*** is **bpf_setsockopt**\ () only and + * **TCP_SAVED_SYN** is **bpf_getsockopt**\ () only. * Return * 0 on success, or a negative error in case of failure. * From 5bad3587b7a292148cea10185cd8770baaeb7445 Mon Sep 17 00:00:00 2001 From: Stanislav Fomichev Date: Wed, 23 Nov 2022 16:28:38 -0800 Subject: [PATCH 1209/2002] bpf: Unify and simplify btf_func_proto_check error handling Replace 'err = x; break;' with 'return x;'. Suggested-by: Daniel Borkmann Signed-off-by: Stanislav Fomichev Signed-off-by: Daniel Borkmann Link: https://lore.kernel.org/bpf/20221124002838.2700179-1-sdf@google.com --- kernel/bpf/btf.c | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/kernel/bpf/btf.c b/kernel/bpf/btf.c index cb43cb842e16..9dbfda2b5c6c 100644 --- a/kernel/bpf/btf.c +++ b/kernel/bpf/btf.c @@ -4779,7 +4779,6 @@ static int btf_func_proto_check(struct btf_verifier_env *env, nr_args--; } - err = 0; for (i = 0; i < nr_args; i++) { const struct btf_type *arg_type; u32 arg_type_id; @@ -4788,8 +4787,7 @@ static int btf_func_proto_check(struct btf_verifier_env *env, arg_type = btf_type_by_id(btf, arg_type_id); if (!arg_type) { btf_verifier_log_type(env, t, "Invalid arg#%u", i + 1); - err = -EINVAL; - break; + return -EINVAL; } if (btf_type_is_resolve_source_only(arg_type)) { @@ -4802,25 +4800,23 @@ static int btf_func_proto_check(struct btf_verifier_env *env, !btf_name_valid_identifier(btf, args[i].name_off))) { btf_verifier_log_type(env, t, "Invalid arg#%u", i + 1); - err = -EINVAL; - break; + return -EINVAL; } if (btf_type_needs_resolve(arg_type) && !env_type_is_resolved(env, arg_type_id)) { err = btf_resolve(env, arg_type, arg_type_id); if (err) - break; + return err; } if (!btf_type_id_size(btf, &arg_type_id, NULL)) { btf_verifier_log_type(env, t, "Invalid arg#%u", i + 1); - err = -EINVAL; - break; + return -EINVAL; } } - return err; + return 0; } static int btf_func_check(struct btf_verifier_env *env, From 3f0e6f2b41d35d4446160c745e8f09037447dd8f Mon Sep 17 00:00:00 2001 From: David Vernet Date: Tue, 22 Nov 2022 08:52:59 -0600 Subject: [PATCH 1210/2002] bpf: Add bpf_task_from_pid() kfunc Callers can currently store tasks as kptrs using bpf_task_acquire(), bpf_task_kptr_get(), and bpf_task_release(). These are useful if a caller already has a struct task_struct *, but there may be some callers who only have a pid, and want to look up the associated struct task_struct * from that to e.g. find task->comm. This patch therefore adds a new bpf_task_from_pid() kfunc which allows BPF programs to get a struct task_struct * kptr from a pid. Signed-off-by: David Vernet Link: https://lore.kernel.org/r/20221122145300.251210-2-void@manifault.com Signed-off-by: Alexei Starovoitov --- kernel/bpf/helpers.c | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/kernel/bpf/helpers.c b/kernel/bpf/helpers.c index da2681ebb7c3..2299bb0d89e5 100644 --- a/kernel/bpf/helpers.c +++ b/kernel/bpf/helpers.c @@ -1959,6 +1959,25 @@ struct cgroup *bpf_cgroup_ancestor(struct cgroup *cgrp, int level) } #endif /* CONFIG_CGROUPS */ +/** + * bpf_task_from_pid - Find a struct task_struct from its pid by looking it up + * in the root pid namespace idr. If a task is returned, it must either be + * stored in a map, or released with bpf_task_release(). + * @pid: The pid of the task being looked up. + */ +struct task_struct *bpf_task_from_pid(s32 pid) +{ + struct task_struct *p; + + rcu_read_lock(); + p = find_task_by_pid_ns(pid, &init_pid_ns); + if (p) + bpf_task_acquire(p); + rcu_read_unlock(); + + return p; +} + void *bpf_cast_to_kern_ctx(void *obj) { return obj; @@ -1984,13 +2003,13 @@ BTF_ID_FLAGS(func, bpf_list_pop_back, KF_ACQUIRE | KF_RET_NULL) BTF_ID_FLAGS(func, bpf_task_acquire, KF_ACQUIRE | KF_TRUSTED_ARGS) BTF_ID_FLAGS(func, bpf_task_kptr_get, KF_ACQUIRE | KF_KPTR_GET | KF_RET_NULL) BTF_ID_FLAGS(func, bpf_task_release, KF_RELEASE) - #ifdef CONFIG_CGROUPS BTF_ID_FLAGS(func, bpf_cgroup_acquire, KF_ACQUIRE | KF_TRUSTED_ARGS) BTF_ID_FLAGS(func, bpf_cgroup_kptr_get, KF_ACQUIRE | KF_KPTR_GET | KF_RET_NULL) BTF_ID_FLAGS(func, bpf_cgroup_release, KF_RELEASE) BTF_ID_FLAGS(func, bpf_cgroup_ancestor, KF_ACQUIRE | KF_TRUSTED_ARGS | KF_RET_NULL) #endif +BTF_ID_FLAGS(func, bpf_task_from_pid, KF_ACQUIRE | KF_RET_NULL) BTF_SET8_END(generic_btf_ids) static const struct btf_kfunc_id_set generic_kfunc_set = { From f471748b7fe5ab7ec6de4cbadffabfa7bb5b6240 Mon Sep 17 00:00:00 2001 From: David Vernet Date: Tue, 22 Nov 2022 08:53:00 -0600 Subject: [PATCH 1211/2002] selftests/bpf: Add selftests for bpf_task_from_pid() Add some selftest testcases that validate the expected behavior of the bpf_task_from_pid() kfunc that was added in the prior patch. Signed-off-by: David Vernet Link: https://lore.kernel.org/r/20221122145300.251210-3-void@manifault.com Signed-off-by: Alexei Starovoitov --- .../selftests/bpf/prog_tests/task_kfunc.c | 4 + .../selftests/bpf/progs/task_kfunc_common.h | 1 + .../selftests/bpf/progs/task_kfunc_failure.c | 13 ++++ .../selftests/bpf/progs/task_kfunc_success.c | 73 +++++++++++++++++++ 4 files changed, 91 insertions(+) diff --git a/tools/testing/selftests/bpf/prog_tests/task_kfunc.c b/tools/testing/selftests/bpf/prog_tests/task_kfunc.c index 4994fe6092cc..ffd8ef4303c8 100644 --- a/tools/testing/selftests/bpf/prog_tests/task_kfunc.c +++ b/tools/testing/selftests/bpf/prog_tests/task_kfunc.c @@ -78,6 +78,9 @@ static const char * const success_tests[] = { "test_task_xchg_release", "test_task_get_release", "test_task_current_acquire_release", + "test_task_from_pid_arg", + "test_task_from_pid_current", + "test_task_from_pid_invalid", }; static struct { @@ -99,6 +102,7 @@ static struct { {"task_kfunc_release_fp", "arg#0 pointer type STRUCT task_struct must point"}, {"task_kfunc_release_null", "arg#0 is ptr_or_null_ expected ptr_ or socket"}, {"task_kfunc_release_unacquired", "release kernel function bpf_task_release expects"}, + {"task_kfunc_from_pid_no_null_check", "arg#0 is ptr_or_null_ expected ptr_ or socket"}, }; static void verify_fail(const char *prog_name, const char *expected_err_msg) diff --git a/tools/testing/selftests/bpf/progs/task_kfunc_common.h b/tools/testing/selftests/bpf/progs/task_kfunc_common.h index 160d6dde00be..c0ffd171743e 100644 --- a/tools/testing/selftests/bpf/progs/task_kfunc_common.h +++ b/tools/testing/selftests/bpf/progs/task_kfunc_common.h @@ -23,6 +23,7 @@ struct hash_map { struct task_struct *bpf_task_acquire(struct task_struct *p) __ksym; struct task_struct *bpf_task_kptr_get(struct task_struct **pp) __ksym; void bpf_task_release(struct task_struct *p) __ksym; +struct task_struct *bpf_task_from_pid(s32 pid) __ksym; static inline struct __tasks_kfunc_map_value *tasks_kfunc_map_value_lookup(struct task_struct *p) { diff --git a/tools/testing/selftests/bpf/progs/task_kfunc_failure.c b/tools/testing/selftests/bpf/progs/task_kfunc_failure.c index 93e934ddfcb6..e310473190d5 100644 --- a/tools/testing/selftests/bpf/progs/task_kfunc_failure.c +++ b/tools/testing/selftests/bpf/progs/task_kfunc_failure.c @@ -258,3 +258,16 @@ int BPF_PROG(task_kfunc_release_unacquired, struct task_struct *task, u64 clone_ return 0; } + +SEC("tp_btf/task_newtask") +int BPF_PROG(task_kfunc_from_pid_no_null_check, struct task_struct *task, u64 clone_flags) +{ + struct task_struct *acquired; + + acquired = bpf_task_from_pid(task->pid); + + /* Releasing bpf_task_from_pid() lookup without a NULL check. */ + bpf_task_release(acquired); + + return 0; +} diff --git a/tools/testing/selftests/bpf/progs/task_kfunc_success.c b/tools/testing/selftests/bpf/progs/task_kfunc_success.c index be4534b5ba2e..60c7ead41cfc 100644 --- a/tools/testing/selftests/bpf/progs/task_kfunc_success.c +++ b/tools/testing/selftests/bpf/progs/task_kfunc_success.c @@ -147,3 +147,76 @@ int BPF_PROG(test_task_current_acquire_release, struct task_struct *task, u64 cl return 0; } + +static void lookup_compare_pid(const struct task_struct *p) +{ + struct task_struct *acquired; + + acquired = bpf_task_from_pid(p->pid); + if (!acquired) { + err = 1; + return; + } + + if (acquired->pid != p->pid) + err = 2; + bpf_task_release(acquired); +} + +SEC("tp_btf/task_newtask") +int BPF_PROG(test_task_from_pid_arg, struct task_struct *task, u64 clone_flags) +{ + struct task_struct *acquired; + + if (!is_test_kfunc_task()) + return 0; + + lookup_compare_pid(task); + return 0; +} + +SEC("tp_btf/task_newtask") +int BPF_PROG(test_task_from_pid_current, struct task_struct *task, u64 clone_flags) +{ + struct task_struct *current, *acquired; + + if (!is_test_kfunc_task()) + return 0; + + lookup_compare_pid(bpf_get_current_task_btf()); + return 0; +} + +static int is_pid_lookup_valid(s32 pid) +{ + struct task_struct *acquired; + + acquired = bpf_task_from_pid(pid); + if (acquired) { + bpf_task_release(acquired); + return 1; + } + + return 0; +} + +SEC("tp_btf/task_newtask") +int BPF_PROG(test_task_from_pid_invalid, struct task_struct *task, u64 clone_flags) +{ + struct task_struct *acquired; + + if (!is_test_kfunc_task()) + return 0; + + if (is_pid_lookup_valid(-1)) { + err = 1; + return 0; + } + + if (is_pid_lookup_valid(0xcafef00d)) { + err = 2; + return 0; + } + + return 0; +} From 64a8f8f7127da228d59a39e2c5e75f86590f90b4 Mon Sep 17 00:00:00 2001 From: Maxim Korotkov Date: Tue, 22 Nov 2022 15:29:01 +0300 Subject: [PATCH 1212/2002] ethtool: avoiding integer overflow in ethtool_phys_id() The value of an arithmetic expression "n * id.data" is subject to possible overflow due to a failure to cast operands to a larger data type before performing arithmetic. Used macro for multiplication instead operator for avoiding overflow. Found by Linux Verification Center (linuxtesting.org) with SVACE. Signed-off-by: Maxim Korotkov Reviewed-by: Alexander Lobakin Reviewed-by: Andrew Lunn Link: https://lore.kernel.org/r/20221122122901.22294-1-korotkov.maxim.s@gmail.com Signed-off-by: Jakub Kicinski --- net/ethtool/ioctl.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/net/ethtool/ioctl.c b/net/ethtool/ioctl.c index 99272a67525c..c2f1a542e6fa 100644 --- a/net/ethtool/ioctl.c +++ b/net/ethtool/ioctl.c @@ -2013,7 +2013,8 @@ static int ethtool_phys_id(struct net_device *dev, void __user *useraddr) } else { /* Driver expects to be called at twice the frequency in rc */ int n = rc * 2, interval = HZ / n; - u64 count = n * id.data, i = 0; + u64 count = mul_u32_u32(n, id.data); + u64 i = 0; do { rtnl_lock(); From d43eff0b85ae28dd2b95dab18a3e7dcb813d7bc1 Mon Sep 17 00:00:00 2001 From: Jonathan Toppins Date: Tue, 22 Nov 2022 15:25:04 -0500 Subject: [PATCH 1213/2002] selftests: bonding: up/down delay w/ slave link flapping Verify when a bond is configured with {up,down}delay and the link state of slave members flaps if there are no remaining members up the bond should immediately select a member to bring up. (from bonding.txt section 13.1 paragraph 4) Suggested-by: Liang Li Signed-off-by: Jonathan Toppins Signed-off-by: Jakub Kicinski --- .../selftests/drivers/net/bonding/Makefile | 4 +- .../selftests/drivers/net/bonding/lag_lib.sh | 106 ++++++++++++++++++ .../net/bonding/mode-1-recovery-updelay.sh | 45 ++++++++ .../net/bonding/mode-2-recovery-updelay.sh | 45 ++++++++ .../selftests/drivers/net/bonding/settings | 2 +- 5 files changed, 200 insertions(+), 2 deletions(-) create mode 100755 tools/testing/selftests/drivers/net/bonding/mode-1-recovery-updelay.sh create mode 100755 tools/testing/selftests/drivers/net/bonding/mode-2-recovery-updelay.sh diff --git a/tools/testing/selftests/drivers/net/bonding/Makefile b/tools/testing/selftests/drivers/net/bonding/Makefile index 6b8d2e2f23c2..0f3921908b07 100644 --- a/tools/testing/selftests/drivers/net/bonding/Makefile +++ b/tools/testing/selftests/drivers/net/bonding/Makefile @@ -5,7 +5,9 @@ TEST_PROGS := \ bond-arp-interval-causes-panic.sh \ bond-break-lacpdu-tx.sh \ bond-lladdr-target.sh \ - dev_addr_lists.sh + dev_addr_lists.sh \ + mode-1-recovery-updelay.sh \ + mode-2-recovery-updelay.sh TEST_FILES := \ lag_lib.sh \ diff --git a/tools/testing/selftests/drivers/net/bonding/lag_lib.sh b/tools/testing/selftests/drivers/net/bonding/lag_lib.sh index 16c7fb858ac1..2a268b17b61f 100644 --- a/tools/testing/selftests/drivers/net/bonding/lag_lib.sh +++ b/tools/testing/selftests/drivers/net/bonding/lag_lib.sh @@ -1,6 +1,8 @@ #!/bin/bash # SPDX-License-Identifier: GPL-2.0 +NAMESPACES="" + # Test that a link aggregation device (bonding, team) removes the hardware # addresses that it adds on its underlying devices. test_LAG_cleanup() @@ -59,3 +61,107 @@ test_LAG_cleanup() log_test "$driver cleanup mode $mode" } + +# Build a generic 2 node net namespace with 2 connections +# between the namespaces +# +# +-----------+ +-----------+ +# | node1 | | node2 | +# | | | | +# | | | | +# | eth0 +-------+ eth0 | +# | | | | +# | eth1 +-------+ eth1 | +# | | | | +# +-----------+ +-----------+ +lag_setup2x2() +{ + local state=${1:-down} + local namespaces="lag_node1 lag_node2" + + # create namespaces + for n in ${namespaces}; do + ip netns add ${n} + done + + # wire up namespaces + ip link add name lag1 type veth peer name lag1-end + ip link set dev lag1 netns lag_node1 $state name eth0 + ip link set dev lag1-end netns lag_node2 $state name eth0 + + ip link add name lag1 type veth peer name lag1-end + ip link set dev lag1 netns lag_node1 $state name eth1 + ip link set dev lag1-end netns lag_node2 $state name eth1 + + NAMESPACES="${namespaces}" +} + +# cleanup all lag related namespaces and remove the bonding module +lag_cleanup() +{ + for n in ${NAMESPACES}; do + ip netns delete ${n} >/dev/null 2>&1 || true + done + modprobe -r bonding +} + +SWITCH="lag_node1" +CLIENT="lag_node2" +CLIENTIP="172.20.2.1" +SWITCHIP="172.20.2.2" + +lag_setup_network() +{ + lag_setup2x2 "down" + + # create switch + ip netns exec ${SWITCH} ip link add br0 up type bridge + ip netns exec ${SWITCH} ip link set eth0 master br0 up + ip netns exec ${SWITCH} ip link set eth1 master br0 up + ip netns exec ${SWITCH} ip addr add ${SWITCHIP}/24 dev br0 +} + +lag_reset_network() +{ + ip netns exec ${CLIENT} ip link del bond0 + ip netns exec ${SWITCH} ip link set eth0 up + ip netns exec ${SWITCH} ip link set eth1 up +} + +create_bond() +{ + # create client + ip netns exec ${CLIENT} ip link set eth0 down + ip netns exec ${CLIENT} ip link set eth1 down + + ip netns exec ${CLIENT} ip link add bond0 type bond $@ + ip netns exec ${CLIENT} ip link set eth0 master bond0 + ip netns exec ${CLIENT} ip link set eth1 master bond0 + ip netns exec ${CLIENT} ip link set bond0 up + ip netns exec ${CLIENT} ip addr add ${CLIENTIP}/24 dev bond0 +} + +test_bond_recovery() +{ + RET=0 + + create_bond $@ + + # verify connectivity + ip netns exec ${CLIENT} ping ${SWITCHIP} -c 2 >/dev/null 2>&1 + check_err $? "No connectivity" + + # force the links of the bond down + ip netns exec ${SWITCH} ip link set eth0 down + sleep 2 + ip netns exec ${SWITCH} ip link set eth0 up + ip netns exec ${SWITCH} ip link set eth1 down + + # re-verify connectivity + ip netns exec ${CLIENT} ping ${SWITCHIP} -c 2 >/dev/null 2>&1 + + local rc=$? + check_err $rc "Bond failed to recover" + log_test "$1 ($2) bond recovery" + lag_reset_network +} diff --git a/tools/testing/selftests/drivers/net/bonding/mode-1-recovery-updelay.sh b/tools/testing/selftests/drivers/net/bonding/mode-1-recovery-updelay.sh new file mode 100755 index 000000000000..ad4c845a4ac7 --- /dev/null +++ b/tools/testing/selftests/drivers/net/bonding/mode-1-recovery-updelay.sh @@ -0,0 +1,45 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0 + +# Regression Test: +# When the bond is configured with down/updelay and the link state of +# slave members flaps if there are no remaining members up the bond +# should immediately select a member to bring up. (from bonding.txt +# section 13.1 paragraph 4) +# +# +-------------+ +-----------+ +# | client | | switch | +# | | | | +# | +--------| link1 |-----+ | +# | | +-------+ | | +# | | | | | | +# | | +-------+ | | +# | | bond | link2 | Br0 | | +# +-------------+ +-----------+ +# 172.20.2.1 172.20.2.2 + + +REQUIRE_MZ=no +REQUIRE_JQ=no +NUM_NETIFS=0 +lib_dir=$(dirname "$0") +source "$lib_dir"/net_forwarding_lib.sh +source "$lib_dir"/lag_lib.sh + +cleanup() +{ + lag_cleanup +} + +trap cleanup 0 1 2 + +lag_setup_network +test_bond_recovery mode 1 miimon 100 updelay 0 +test_bond_recovery mode 1 miimon 100 updelay 200 +test_bond_recovery mode 1 miimon 100 updelay 500 +test_bond_recovery mode 1 miimon 100 updelay 1000 +test_bond_recovery mode 1 miimon 100 updelay 2000 +test_bond_recovery mode 1 miimon 100 updelay 5000 +test_bond_recovery mode 1 miimon 100 updelay 10000 + +exit "$EXIT_STATUS" diff --git a/tools/testing/selftests/drivers/net/bonding/mode-2-recovery-updelay.sh b/tools/testing/selftests/drivers/net/bonding/mode-2-recovery-updelay.sh new file mode 100755 index 000000000000..2330d37453f9 --- /dev/null +++ b/tools/testing/selftests/drivers/net/bonding/mode-2-recovery-updelay.sh @@ -0,0 +1,45 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0 + +# Regression Test: +# When the bond is configured with down/updelay and the link state of +# slave members flaps if there are no remaining members up the bond +# should immediately select a member to bring up. (from bonding.txt +# section 13.1 paragraph 4) +# +# +-------------+ +-----------+ +# | client | | switch | +# | | | | +# | +--------| link1 |-----+ | +# | | +-------+ | | +# | | | | | | +# | | +-------+ | | +# | | bond | link2 | Br0 | | +# +-------------+ +-----------+ +# 172.20.2.1 172.20.2.2 + + +REQUIRE_MZ=no +REQUIRE_JQ=no +NUM_NETIFS=0 +lib_dir=$(dirname "$0") +source "$lib_dir"/net_forwarding_lib.sh +source "$lib_dir"/lag_lib.sh + +cleanup() +{ + lag_cleanup +} + +trap cleanup 0 1 2 + +lag_setup_network +test_bond_recovery mode 2 miimon 100 updelay 0 +test_bond_recovery mode 2 miimon 100 updelay 200 +test_bond_recovery mode 2 miimon 100 updelay 500 +test_bond_recovery mode 2 miimon 100 updelay 1000 +test_bond_recovery mode 2 miimon 100 updelay 2000 +test_bond_recovery mode 2 miimon 100 updelay 5000 +test_bond_recovery mode 2 miimon 100 updelay 10000 + +exit "$EXIT_STATUS" diff --git a/tools/testing/selftests/drivers/net/bonding/settings b/tools/testing/selftests/drivers/net/bonding/settings index 867e118223cd..6091b45d226b 100644 --- a/tools/testing/selftests/drivers/net/bonding/settings +++ b/tools/testing/selftests/drivers/net/bonding/settings @@ -1 +1 @@ -timeout=60 +timeout=120 From f8a65ab2f3ff7410921ebbf0dc55453102c33c56 Mon Sep 17 00:00:00 2001 From: Jonathan Toppins Date: Tue, 22 Nov 2022 16:24:29 -0500 Subject: [PATCH 1214/2002] bonding: fix link recovery in mode 2 when updelay is nonzero Before this change when a bond in mode 2 lost link, all of its slaves lost link, the bonding device would never recover even after the expiration of updelay. This change removes the updelay when the bond currently has no usable links. Conforming to bonding.txt section 13.1 paragraph 4. Fixes: 41f891004063 ("bonding: ignore updelay param when there is no active slave") Signed-off-by: Jonathan Toppins Acked-by: Jay Vosburgh Signed-off-by: Jakub Kicinski --- drivers/net/bonding/bond_main.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index 1cd4e71916f8..6c4348245d1f 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -2529,7 +2529,16 @@ static int bond_miimon_inspect(struct bonding *bond) struct slave *slave; bool ignore_updelay; - ignore_updelay = !rcu_dereference(bond->curr_active_slave); + if (BOND_MODE(bond) == BOND_MODE_ACTIVEBACKUP) { + ignore_updelay = !rcu_dereference(bond->curr_active_slave); + } else { + struct bond_up_slave *usable_slaves; + + usable_slaves = rcu_dereference(bond->usable_slaves); + + if (usable_slaves && usable_slaves->count == 0) + ignore_updelay = true; + } bond_for_each_slave_rcu(bond, slave, iter) { bond_propose_link_state(slave, BOND_LINK_NOCHANGE); From 14e5f71e31ff3925cc970fa7907393ee7f4b748d Mon Sep 17 00:00:00 2001 From: Stanislav Fomichev Date: Tue, 22 Nov 2022 20:09:47 -0800 Subject: [PATCH 1215/2002] net: use %pS for kfree_skb tracing event location For the cases where 'reason' doesn't give any clue, it's still nice to be able to track the kfree_skb caller location. %p doesn't help much so let's use %pS which prints the symbol+offset. Signed-off-by: Stanislav Fomichev Link: https://lore.kernel.org/r/20221123040947.1015721-1-sdf@google.com Signed-off-by: Paolo Abeni --- include/trace/events/skb.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/trace/events/skb.h b/include/trace/events/skb.h index 50a974f7dfb4..25ab1ff9423d 100644 --- a/include/trace/events/skb.h +++ b/include/trace/events/skb.h @@ -42,7 +42,7 @@ TRACE_EVENT(kfree_skb, __entry->reason = reason; ), - TP_printk("skbaddr=%p protocol=%u location=%p reason: %s", + TP_printk("skbaddr=%p protocol=%u location=%pS reason: %s", __entry->skbaddr, __entry->protocol, __entry->location, __print_symbolic(__entry->reason, DEFINE_DROP_REASON(FN, FNe))) From 5a0f663f0189cf9e031e444f97c029717a99548d Mon Sep 17 00:00:00 2001 From: Yonghong Song Date: Wed, 23 Nov 2022 21:32:06 -0800 Subject: [PATCH 1216/2002] compiler_types: Define __rcu as __attribute__((btf_type_tag("rcu"))) Currently, without rcu attribute info in BTF, the verifier treats rcu tagged pointer as a normal pointer. This might be a problem for sleepable program where rcu_read_lock()/unlock() is not available. For example, for a sleepable fentry program, if rcu protected memory access is interleaved with a sleepable helper/kfunc, it is possible the memory access after the sleepable helper/kfunc might be invalid since the object might have been freed then. To prevent such cases, introducing rcu tagging for memory accesses in verifier can help to reject such programs. To enable rcu tagging in BTF, during kernel compilation, define __rcu as attribute btf_type_tag("rcu") so __rcu information can be preserved in dwarf and btf, and later can be used for bpf prog verification. Acked-by: KP Singh Acked-by: Martin KaFai Lau Signed-off-by: Yonghong Song Link: https://lore.kernel.org/r/20221124053206.2373141-1-yhs@fb.com Signed-off-by: Alexei Starovoitov --- include/linux/compiler_types.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/include/linux/compiler_types.h b/include/linux/compiler_types.h index eb0466236661..7c1afe0f4129 100644 --- a/include/linux/compiler_types.h +++ b/include/linux/compiler_types.h @@ -49,7 +49,8 @@ static inline void __chk_io_ptr(const volatile void __iomem *ptr) { } # endif # define __iomem # define __percpu BTF_TYPE_TAG(percpu) -# define __rcu +# define __rcu BTF_TYPE_TAG(rcu) + # define __chk_user_ptr(x) (void)0 # define __chk_io_ptr(x) (void)0 /* context/locking */ From 01685c5bddaa6df3d662c8afed5e5289fcc68e5a Mon Sep 17 00:00:00 2001 From: Yonghong Song Date: Wed, 23 Nov 2022 21:32:11 -0800 Subject: [PATCH 1217/2002] bpf: Introduce might_sleep field in bpf_func_proto Introduce bpf_func_proto->might_sleep to indicate a particular helper might sleep. This will make later check whether a helper might be sleepable or not easier. Acked-by: Martin KaFai Lau Signed-off-by: Yonghong Song Link: https://lore.kernel.org/r/20221124053211.2373553-1-yhs@fb.com Signed-off-by: Alexei Starovoitov --- include/linux/bpf.h | 1 + kernel/bpf/bpf_lsm.c | 6 ++++-- kernel/bpf/helpers.c | 2 ++ kernel/bpf/verifier.c | 5 +++++ kernel/trace/bpf_trace.c | 4 ++-- 5 files changed, 14 insertions(+), 4 deletions(-) diff --git a/include/linux/bpf.h b/include/linux/bpf.h index c9eafa67f2a2..43fd7eeeeabb 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -682,6 +682,7 @@ struct bpf_func_proto { u64 (*func)(u64 r1, u64 r2, u64 r3, u64 r4, u64 r5); bool gpl_only; bool pkt_access; + bool might_sleep; enum bpf_return_type ret_type; union { struct { diff --git a/kernel/bpf/bpf_lsm.c b/kernel/bpf/bpf_lsm.c index d6c9b3705f24..ae0267f150b5 100644 --- a/kernel/bpf/bpf_lsm.c +++ b/kernel/bpf/bpf_lsm.c @@ -151,6 +151,7 @@ BTF_ID_LIST_SINGLE(bpf_ima_inode_hash_btf_ids, struct, inode) static const struct bpf_func_proto bpf_ima_inode_hash_proto = { .func = bpf_ima_inode_hash, .gpl_only = false, + .might_sleep = true, .ret_type = RET_INTEGER, .arg1_type = ARG_PTR_TO_BTF_ID, .arg1_btf_id = &bpf_ima_inode_hash_btf_ids[0], @@ -169,6 +170,7 @@ BTF_ID_LIST_SINGLE(bpf_ima_file_hash_btf_ids, struct, file) static const struct bpf_func_proto bpf_ima_file_hash_proto = { .func = bpf_ima_file_hash, .gpl_only = false, + .might_sleep = true, .ret_type = RET_INTEGER, .arg1_type = ARG_PTR_TO_BTF_ID, .arg1_btf_id = &bpf_ima_file_hash_btf_ids[0], @@ -221,9 +223,9 @@ bpf_lsm_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog) case BPF_FUNC_bprm_opts_set: return &bpf_bprm_opts_set_proto; case BPF_FUNC_ima_inode_hash: - return prog->aux->sleepable ? &bpf_ima_inode_hash_proto : NULL; + return &bpf_ima_inode_hash_proto; case BPF_FUNC_ima_file_hash: - return prog->aux->sleepable ? &bpf_ima_file_hash_proto : NULL; + return &bpf_ima_file_hash_proto; case BPF_FUNC_get_attach_cookie: return bpf_prog_has_trampoline(prog) ? &bpf_get_attach_cookie_proto : NULL; #ifdef CONFIG_NET diff --git a/kernel/bpf/helpers.c b/kernel/bpf/helpers.c index 2299bb0d89e5..9296b654dbd7 100644 --- a/kernel/bpf/helpers.c +++ b/kernel/bpf/helpers.c @@ -661,6 +661,7 @@ BPF_CALL_3(bpf_copy_from_user, void *, dst, u32, size, const struct bpf_func_proto bpf_copy_from_user_proto = { .func = bpf_copy_from_user, .gpl_only = false, + .might_sleep = true, .ret_type = RET_INTEGER, .arg1_type = ARG_PTR_TO_UNINIT_MEM, .arg2_type = ARG_CONST_SIZE_OR_ZERO, @@ -691,6 +692,7 @@ BPF_CALL_5(bpf_copy_from_user_task, void *, dst, u32, size, const struct bpf_func_proto bpf_copy_from_user_task_proto = { .func = bpf_copy_from_user_task, .gpl_only = true, + .might_sleep = true, .ret_type = RET_INTEGER, .arg1_type = ARG_PTR_TO_UNINIT_MEM, .arg2_type = ARG_CONST_SIZE_OR_ZERO, diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 9528a066cfa5..068cc885903c 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -7516,6 +7516,11 @@ static int check_helper_call(struct bpf_verifier_env *env, struct bpf_insn *insn return -EINVAL; } + if (!env->prog->aux->sleepable && fn->might_sleep) { + verbose(env, "helper call might sleep in a non-sleepable prog\n"); + return -EINVAL; + } + /* With LD_ABS/IND some JITs save/restore skb from r1. */ changes_data = bpf_helper_changes_pkt_data(fn->func); if (changes_data && fn->arg1_type != ARG_PTR_TO_CTX) { diff --git a/kernel/trace/bpf_trace.c b/kernel/trace/bpf_trace.c index 5b9008bc597b..3bbd3f0c810c 100644 --- a/kernel/trace/bpf_trace.c +++ b/kernel/trace/bpf_trace.c @@ -1485,9 +1485,9 @@ bpf_tracing_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog) case BPF_FUNC_get_task_stack: return &bpf_get_task_stack_proto; case BPF_FUNC_copy_from_user: - return prog->aux->sleepable ? &bpf_copy_from_user_proto : NULL; + return &bpf_copy_from_user_proto; case BPF_FUNC_copy_from_user_task: - return prog->aux->sleepable ? &bpf_copy_from_user_task_proto : NULL; + return &bpf_copy_from_user_task_proto; case BPF_FUNC_snprintf_btf: return &bpf_snprintf_btf_proto; case BPF_FUNC_per_cpu_ptr: From 9bb00b2895cbfe0ad410457b605d0a72524168c1 Mon Sep 17 00:00:00 2001 From: Yonghong Song Date: Wed, 23 Nov 2022 21:32:17 -0800 Subject: [PATCH 1218/2002] bpf: Add kfunc bpf_rcu_read_lock/unlock() Add two kfunc's bpf_rcu_read_lock() and bpf_rcu_read_unlock(). These two kfunc's can be used for all program types. The following is an example about how rcu pointer are used w.r.t. bpf_rcu_read_lock()/bpf_rcu_read_unlock(). struct task_struct { ... struct task_struct *last_wakee; struct task_struct __rcu *real_parent; ... }; Let us say prog does 'task = bpf_get_current_task_btf()' to get a 'task' pointer. The basic rules are: - 'real_parent = task->real_parent' should be inside bpf_rcu_read_lock region. This is to simulate rcu_dereference() operation. The 'real_parent' is marked as MEM_RCU only if (1). task->real_parent is inside bpf_rcu_read_lock region, and (2). task is a trusted ptr. So MEM_RCU marked ptr can be 'trusted' inside the bpf_rcu_read_lock region. - 'last_wakee = real_parent->last_wakee' should be inside bpf_rcu_read_lock region since it tries to access rcu protected memory. - the ptr 'last_wakee' will be marked as PTR_UNTRUSTED since in general it is not clear whether the object pointed by 'last_wakee' is valid or not even inside bpf_rcu_read_lock region. The verifier will reset all rcu pointer register states to untrusted at bpf_rcu_read_unlock() kfunc call site, so any such rcu pointer won't be trusted any more outside the bpf_rcu_read_lock() region. The current implementation does not support nested rcu read lock region in the prog. Acked-by: Martin KaFai Lau Signed-off-by: Yonghong Song Link: https://lore.kernel.org/r/20221124053217.2373910-1-yhs@fb.com Signed-off-by: Alexei Starovoitov --- include/linux/bpf.h | 3 + include/linux/bpf_verifier.h | 5 +- kernel/bpf/btf.c | 3 + kernel/bpf/helpers.c | 12 +++ kernel/bpf/verifier.c | 162 ++++++++++++++++++++++++++++------- 5 files changed, 155 insertions(+), 30 deletions(-) diff --git a/include/linux/bpf.h b/include/linux/bpf.h index 43fd7eeeeabb..c6aa6912ea16 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -572,6 +572,9 @@ enum bpf_type_flag { */ PTR_TRUSTED = BIT(12 + BPF_BASE_TYPE_BITS), + /* MEM is tagged with rcu and memory access needs rcu_read_lock protection. */ + MEM_RCU = BIT(13 + BPF_BASE_TYPE_BITS), + __BPF_TYPE_FLAG_MAX, __BPF_TYPE_LAST_FLAG = __BPF_TYPE_FLAG_MAX - 1, }; diff --git a/include/linux/bpf_verifier.h b/include/linux/bpf_verifier.h index 545152ac136c..c05aa6e1f6f5 100644 --- a/include/linux/bpf_verifier.h +++ b/include/linux/bpf_verifier.h @@ -344,6 +344,7 @@ struct bpf_verifier_state { u32 id; } active_lock; bool speculative; + bool active_rcu_lock; /* first and last insn idx of this verifier state */ u32 first_insn_idx; @@ -445,6 +446,7 @@ struct bpf_insn_aux_data { u32 seen; /* this insn was processed by the verifier at env->pass_cnt */ bool sanitize_stack_spill; /* subject to Spectre v4 sanitation */ bool zext_dst; /* this insn zero extends dst reg */ + bool storage_get_func_atomic; /* bpf_*_storage_get() with atomic memory alloc */ u8 alu_state; /* used in combination with alu_limit */ /* below fields are initialized once */ @@ -534,6 +536,7 @@ struct bpf_verifier_env { bool bypass_spec_v1; bool bypass_spec_v4; bool seen_direct_write; + bool rcu_tag_supported; struct bpf_insn_aux_data *insn_aux_data; /* array of per-insn state */ const struct bpf_line_info *prev_linfo; struct bpf_verifier_log log; @@ -680,7 +683,7 @@ static inline bool bpf_prog_check_recur(const struct bpf_prog *prog) } } -#define BPF_REG_TRUSTED_MODIFIERS (MEM_ALLOC | PTR_TRUSTED) +#define BPF_REG_TRUSTED_MODIFIERS (MEM_ALLOC | MEM_RCU | PTR_TRUSTED) static inline bool bpf_type_has_unsafe_modifiers(u32 type) { diff --git a/kernel/bpf/btf.c b/kernel/bpf/btf.c index 9dbfda2b5c6c..bd3369100239 100644 --- a/kernel/bpf/btf.c +++ b/kernel/bpf/btf.c @@ -6238,6 +6238,9 @@ error: /* check __percpu tag */ if (strcmp(tag_value, "percpu") == 0) tmp_flag = MEM_PERCPU; + /* check __rcu tag */ + if (strcmp(tag_value, "rcu") == 0) + tmp_flag = MEM_RCU; } stype = btf_type_skip_modifiers(btf, mtype->type, &id); diff --git a/kernel/bpf/helpers.c b/kernel/bpf/helpers.c index 9296b654dbd7..a5a511430f2a 100644 --- a/kernel/bpf/helpers.c +++ b/kernel/bpf/helpers.c @@ -1990,6 +1990,16 @@ void *bpf_rdonly_cast(void *obj__ign, u32 btf_id__k) return obj__ign; } +void bpf_rcu_read_lock(void) +{ + rcu_read_lock(); +} + +void bpf_rcu_read_unlock(void) +{ + rcu_read_unlock(); +} + __diag_pop(); BTF_SET8_START(generic_btf_ids) @@ -2031,6 +2041,8 @@ BTF_ID(func, bpf_cgroup_release) BTF_SET8_START(common_btf_ids) BTF_ID_FLAGS(func, bpf_cast_to_kern_ctx) BTF_ID_FLAGS(func, bpf_rdonly_cast) +BTF_ID_FLAGS(func, bpf_rcu_read_lock) +BTF_ID_FLAGS(func, bpf_rcu_read_unlock) BTF_SET8_END(common_btf_ids) static const struct btf_kfunc_id_set common_kfunc_set = { diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 068cc885903c..f4500479f1c2 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -527,6 +527,14 @@ static bool is_callback_calling_function(enum bpf_func_id func_id) func_id == BPF_FUNC_user_ringbuf_drain; } +static bool is_storage_get_function(enum bpf_func_id func_id) +{ + return func_id == BPF_FUNC_sk_storage_get || + func_id == BPF_FUNC_inode_storage_get || + func_id == BPF_FUNC_task_storage_get || + func_id == BPF_FUNC_cgrp_storage_get; +} + static bool helper_multiple_ref_obj_use(enum bpf_func_id func_id, const struct bpf_map *map) { @@ -589,11 +597,12 @@ static const char *reg_type_str(struct bpf_verifier_env *env, strncpy(postfix, "_or_null", 16); } - snprintf(prefix, sizeof(prefix), "%s%s%s%s%s%s", + snprintf(prefix, sizeof(prefix), "%s%s%s%s%s%s%s", type & MEM_RDONLY ? "rdonly_" : "", type & MEM_RINGBUF ? "ringbuf_" : "", type & MEM_USER ? "user_" : "", type & MEM_PERCPU ? "percpu_" : "", + type & MEM_RCU ? "rcu_" : "", type & PTR_UNTRUSTED ? "untrusted_" : "", type & PTR_TRUSTED ? "trusted_" : "" ); @@ -1220,6 +1229,7 @@ static int copy_verifier_state(struct bpf_verifier_state *dst_state, dst_state->frame[i] = NULL; } dst_state->speculative = src->speculative; + dst_state->active_rcu_lock = src->active_rcu_lock; dst_state->curframe = src->curframe; dst_state->active_lock.ptr = src->active_lock.ptr; dst_state->active_lock.id = src->active_lock.id; @@ -4258,6 +4268,25 @@ static bool is_flow_key_reg(struct bpf_verifier_env *env, int regno) return reg->type == PTR_TO_FLOW_KEYS; } +static bool is_trusted_reg(const struct bpf_reg_state *reg) +{ + /* A referenced register is always trusted. */ + if (reg->ref_obj_id) + return true; + + /* If a register is not referenced, it is trusted if it has the + * MEM_ALLOC, MEM_RCU or PTR_TRUSTED type modifiers, and no others. Some of the + * other type modifiers may be safe, but we elect to take an opt-in + * approach here as some (e.g. PTR_UNTRUSTED and PTR_MAYBE_NULL) are + * not. + * + * Eventually, we should make PTR_TRUSTED the single source of truth + * for whether a register is trusted. + */ + return type_flag(reg->type) & BPF_REG_TRUSTED_MODIFIERS && + !bpf_type_has_unsafe_modifiers(reg->type); +} + static int check_pkt_ptr_alignment(struct bpf_verifier_env *env, const struct bpf_reg_state *reg, int off, int size, bool strict) @@ -4737,9 +4766,28 @@ static int check_ptr_to_btf_access(struct bpf_verifier_env *env, if (type_flag(reg->type) & PTR_UNTRUSTED) flag |= PTR_UNTRUSTED; - /* Any pointer obtained from walking a trusted pointer is no longer trusted. */ + /* By default any pointer obtained from walking a trusted pointer is + * no longer trusted except the rcu case below. + */ flag &= ~PTR_TRUSTED; + if (flag & MEM_RCU) { + /* Mark value register as MEM_RCU only if it is protected by + * bpf_rcu_read_lock() and the ptr reg is trusted. MEM_RCU + * itself can already indicate trustedness inside the rcu + * read lock region. Also mark it as PTR_TRUSTED. + */ + if (!env->cur_state->active_rcu_lock || !is_trusted_reg(reg)) + flag &= ~MEM_RCU; + else + flag |= PTR_TRUSTED; + } else if (reg->type & MEM_RCU) { + /* ptr (reg) is marked as MEM_RCU, but the struct field is not tagged + * with __rcu. Mark the flag as PTR_UNTRUSTED conservatively. + */ + flag |= PTR_UNTRUSTED; + } + if (atype == BPF_READ && value_regno >= 0) mark_btf_ld_reg(env, regs, value_regno, ret, reg->btf, btf_id, flag); @@ -5897,6 +5945,7 @@ static const struct bpf_reg_types btf_ptr_types = { .types = { PTR_TO_BTF_ID, PTR_TO_BTF_ID | PTR_TRUSTED, + PTR_TO_BTF_ID | MEM_RCU | PTR_TRUSTED, }, }; static const struct bpf_reg_types percpu_btf_ptr_types = { @@ -6075,6 +6124,7 @@ int check_func_arg_reg_off(struct bpf_verifier_env *env, case PTR_TO_BTF_ID: case PTR_TO_BTF_ID | MEM_ALLOC: case PTR_TO_BTF_ID | PTR_TRUSTED: + case PTR_TO_BTF_ID | MEM_RCU | PTR_TRUSTED: case PTR_TO_BTF_ID | MEM_ALLOC | PTR_TRUSTED: /* When referenced PTR_TO_BTF_ID is passed to release function, * it's fixed offset must be 0. In the other cases, fixed offset @@ -7539,6 +7589,17 @@ static int check_helper_call(struct bpf_verifier_env *env, struct bpf_insn *insn return err; } + if (env->cur_state->active_rcu_lock) { + if (fn->might_sleep) { + verbose(env, "sleepable helper %s#%d in rcu_read_lock region\n", + func_id_name(func_id), func_id); + return -EINVAL; + } + + if (env->prog->aux->sleepable && is_storage_get_function(func_id)) + env->insn_aux_data[insn_idx].storage_get_func_atomic = true; + } + meta.func_id = func_id; /* check args */ for (i = 0; i < MAX_BPF_FUNC_REG_ARGS; i++) { @@ -7966,25 +8027,6 @@ static bool is_kfunc_arg_kptr_get(struct bpf_kfunc_call_arg_meta *meta, int arg) return arg == 0 && (meta->kfunc_flags & KF_KPTR_GET); } -static bool is_trusted_reg(const struct bpf_reg_state *reg) -{ - /* A referenced register is always trusted. */ - if (reg->ref_obj_id) - return true; - - /* If a register is not referenced, it is trusted if it has either the - * MEM_ALLOC or PTR_TRUSTED type modifiers, and no others. Some of the - * other type modifiers may be safe, but we elect to take an opt-in - * approach here as some (e.g. PTR_UNTRUSTED and PTR_MAYBE_NULL) are - * not. - * - * Eventually, we should make PTR_TRUSTED the single source of truth - * for whether a register is trusted. - */ - return type_flag(reg->type) & BPF_REG_TRUSTED_MODIFIERS && - !bpf_type_has_unsafe_modifiers(reg->type); -} - static bool __kfunc_param_match_suffix(const struct btf *btf, const struct btf_param *arg, const char *suffix) @@ -8163,6 +8205,8 @@ enum special_kfunc_type { KF_bpf_list_pop_back, KF_bpf_cast_to_kern_ctx, KF_bpf_rdonly_cast, + KF_bpf_rcu_read_lock, + KF_bpf_rcu_read_unlock, }; BTF_SET_START(special_kfunc_set) @@ -8185,6 +8229,18 @@ BTF_ID(func, bpf_list_pop_front) BTF_ID(func, bpf_list_pop_back) BTF_ID(func, bpf_cast_to_kern_ctx) BTF_ID(func, bpf_rdonly_cast) +BTF_ID(func, bpf_rcu_read_lock) +BTF_ID(func, bpf_rcu_read_unlock) + +static bool is_kfunc_bpf_rcu_read_lock(struct bpf_kfunc_call_arg_meta *meta) +{ + return meta->func_id == special_kfunc_list[KF_bpf_rcu_read_lock]; +} + +static bool is_kfunc_bpf_rcu_read_unlock(struct bpf_kfunc_call_arg_meta *meta) +{ + return meta->func_id == special_kfunc_list[KF_bpf_rcu_read_unlock]; +} static enum kfunc_ptr_arg_type get_kfunc_ptr_arg_type(struct bpf_verifier_env *env, @@ -8817,6 +8873,7 @@ static int check_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn, const struct btf_type *t, *func, *func_proto, *ptr_type; struct bpf_reg_state *regs = cur_regs(env); const char *func_name, *ptr_type_name; + bool sleepable, rcu_lock, rcu_unlock; struct bpf_kfunc_call_arg_meta meta; u32 i, nargs, func_id, ptr_type_id; int err, insn_idx = *insn_idx_p; @@ -8858,11 +8915,45 @@ static int check_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn, return -EACCES; } - if (is_kfunc_sleepable(&meta) && !env->prog->aux->sleepable) { + sleepable = is_kfunc_sleepable(&meta); + if (sleepable && !env->prog->aux->sleepable) { verbose(env, "program must be sleepable to call sleepable kfunc %s\n", func_name); return -EACCES; } + rcu_lock = is_kfunc_bpf_rcu_read_lock(&meta); + rcu_unlock = is_kfunc_bpf_rcu_read_unlock(&meta); + if ((rcu_lock || rcu_unlock) && !env->rcu_tag_supported) { + verbose(env, "no vmlinux btf rcu tag support for kfunc %s\n", func_name); + return -EACCES; + } + + if (env->cur_state->active_rcu_lock) { + struct bpf_func_state *state; + struct bpf_reg_state *reg; + + if (rcu_lock) { + verbose(env, "nested rcu read lock (kernel function %s)\n", func_name); + return -EINVAL; + } else if (rcu_unlock) { + bpf_for_each_reg_in_vstate(env->cur_state, state, reg, ({ + if (reg->type & MEM_RCU) { + reg->type &= ~(MEM_RCU | PTR_TRUSTED); + reg->type |= PTR_UNTRUSTED; + } + })); + env->cur_state->active_rcu_lock = false; + } else if (sleepable) { + verbose(env, "kernel func %s is sleepable within rcu_read_lock region\n", func_name); + return -EACCES; + } + } else if (rcu_lock) { + env->cur_state->active_rcu_lock = true; + } else if (rcu_unlock) { + verbose(env, "unmatched rcu read unlock (kernel function %s)\n", func_name); + return -EINVAL; + } + /* Check the arguments */ err = check_kfunc_args(env, &meta); if (err < 0) @@ -11754,6 +11845,11 @@ static int check_ld_abs(struct bpf_verifier_env *env, struct bpf_insn *insn) return -EINVAL; } + if (env->cur_state->active_rcu_lock) { + verbose(env, "BPF_LD_[ABS|IND] cannot be used inside bpf_rcu_read_lock-ed region\n"); + return -EINVAL; + } + if (regs[ctx_reg].type != PTR_TO_CTX) { verbose(env, "at the time of BPF_LD_ABS|IND R6 != pointer to skb\n"); @@ -13019,6 +13115,9 @@ static bool states_equal(struct bpf_verifier_env *env, old->active_lock.id != cur->active_lock.id) return false; + if (old->active_rcu_lock != cur->active_rcu_lock) + return false; + /* for states to be equal callsites have to be the same * and all frame states need to be equivalent */ @@ -13706,6 +13805,11 @@ static int do_check(struct bpf_verifier_env *env) return -EINVAL; } + if (env->cur_state->active_rcu_lock) { + verbose(env, "bpf_rcu_read_unlock is missing\n"); + return -EINVAL; + } + /* We must do check_reference_leak here before * prepare_func_exit to handle the case when * state->curframe > 0, it may be a callback @@ -15494,14 +15598,12 @@ static int do_misc_fixups(struct bpf_verifier_env *env) goto patch_call_imm; } - if (insn->imm == BPF_FUNC_task_storage_get || - insn->imm == BPF_FUNC_sk_storage_get || - insn->imm == BPF_FUNC_inode_storage_get || - insn->imm == BPF_FUNC_cgrp_storage_get) { - if (env->prog->aux->sleepable) - insn_buf[0] = BPF_MOV64_IMM(BPF_REG_5, (__force __s32)GFP_KERNEL); - else + if (is_storage_get_function(insn->imm)) { + if (!env->prog->aux->sleepable || + env->insn_aux_data[i + delta].storage_get_func_atomic) insn_buf[0] = BPF_MOV64_IMM(BPF_REG_5, (__force __s32)GFP_ATOMIC); + else + insn_buf[0] = BPF_MOV64_IMM(BPF_REG_5, (__force __s32)GFP_KERNEL); insn_buf[1] = *insn; cnt = 2; @@ -16580,6 +16682,8 @@ int bpf_check(struct bpf_prog **prog, union bpf_attr *attr, bpfptr_t uattr) env->bypass_spec_v1 = bpf_bypass_spec_v1(); env->bypass_spec_v4 = bpf_bypass_spec_v4(); env->bpf_capable = bpf_capable(); + env->rcu_tag_supported = btf_vmlinux && + btf_find_by_name_kind(btf_vmlinux, "rcu", BTF_KIND_TYPE_TAG) > 0; if (is_priv) env->test_state_freq = attr->prog_flags & BPF_F_TEST_STATE_FREQ; From 48671232fcb81b76be13c11b0df7089b16baea57 Mon Sep 17 00:00:00 2001 From: Yonghong Song Date: Wed, 23 Nov 2022 21:32:22 -0800 Subject: [PATCH 1219/2002] selftests/bpf: Add tests for bpf_rcu_read_lock() Add a few positive/negative tests to test bpf_rcu_read_lock() and its corresponding verifier support. The new test will fail on s390x and aarch64, so an entry is added to each of their respective deny lists. Acked-by: Martin KaFai Lau Signed-off-by: Yonghong Song Link: https://lore.kernel.org/r/20221124053222.2374650-1-yhs@fb.com Signed-off-by: Alexei Starovoitov --- tools/testing/selftests/bpf/DENYLIST.aarch64 | 1 + tools/testing/selftests/bpf/DENYLIST.s390x | 1 + .../selftests/bpf/prog_tests/rcu_read_lock.c | 158 ++++++++++ .../selftests/bpf/progs/rcu_read_lock.c | 290 ++++++++++++++++++ 4 files changed, 450 insertions(+) create mode 100644 tools/testing/selftests/bpf/prog_tests/rcu_read_lock.c create mode 100644 tools/testing/selftests/bpf/progs/rcu_read_lock.c diff --git a/tools/testing/selftests/bpf/DENYLIST.aarch64 b/tools/testing/selftests/bpf/DENYLIST.aarch64 index affc5aebbf0f..8e77515d56f6 100644 --- a/tools/testing/selftests/bpf/DENYLIST.aarch64 +++ b/tools/testing/selftests/bpf/DENYLIST.aarch64 @@ -45,6 +45,7 @@ modify_return # modify_return__attach failed module_attach # skel_attach skeleton attach failed: -524 mptcp/base # run_test mptcp unexpected error: -524 (errno 524) netcnt # packets unexpected packets: actual 10001 != expected 10000 +rcu_read_lock # failed to attach: ERROR: strerror_r(-524)=22 recursion # skel_attach unexpected error: -524 (errno 524) ringbuf # skel_attach skeleton attachment failed: -1 setget_sockopt # attach_cgroup unexpected error: -524 diff --git a/tools/testing/selftests/bpf/DENYLIST.s390x b/tools/testing/selftests/bpf/DENYLIST.s390x index b9a3d80204c6..648a8a1b6b78 100644 --- a/tools/testing/selftests/bpf/DENYLIST.s390x +++ b/tools/testing/selftests/bpf/DENYLIST.s390x @@ -43,6 +43,7 @@ module_attach # skel_attach skeleton attach failed: - mptcp netcnt # failed to load BPF skeleton 'netcnt_prog': -7 (?) probe_user # check_kprobe_res wrong kprobe res from probe read (?) +rcu_read_lock # failed to find kernel BTF type ID of '__x64_sys_getpgid': -3 (?) recursion # skel_attach unexpected error: -524 (trampoline) ringbuf # skel_load skeleton load failed (?) select_reuseport # intermittently fails on new s390x setup diff --git a/tools/testing/selftests/bpf/prog_tests/rcu_read_lock.c b/tools/testing/selftests/bpf/prog_tests/rcu_read_lock.c new file mode 100644 index 000000000000..447d8560ecb6 --- /dev/null +++ b/tools/testing/selftests/bpf/prog_tests/rcu_read_lock.c @@ -0,0 +1,158 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2022 Meta Platforms, Inc. and affiliates.*/ + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include "rcu_read_lock.skel.h" +#include "cgroup_helpers.h" + +static unsigned long long cgroup_id; + +static void test_success(void) +{ + struct rcu_read_lock *skel; + int err; + + skel = rcu_read_lock__open(); + if (!ASSERT_OK_PTR(skel, "skel_open")) + return; + + skel->bss->target_pid = syscall(SYS_gettid); + + bpf_program__set_autoload(skel->progs.get_cgroup_id, true); + bpf_program__set_autoload(skel->progs.task_succ, true); + bpf_program__set_autoload(skel->progs.no_lock, true); + bpf_program__set_autoload(skel->progs.two_regions, true); + bpf_program__set_autoload(skel->progs.non_sleepable_1, true); + bpf_program__set_autoload(skel->progs.non_sleepable_2, true); + err = rcu_read_lock__load(skel); + if (!ASSERT_OK(err, "skel_load")) + goto out; + + err = rcu_read_lock__attach(skel); + if (!ASSERT_OK(err, "skel_attach")) + goto out; + + syscall(SYS_getpgid); + + ASSERT_EQ(skel->bss->task_storage_val, 2, "task_storage_val"); + ASSERT_EQ(skel->bss->cgroup_id, cgroup_id, "cgroup_id"); +out: + rcu_read_lock__destroy(skel); +} + +static void test_rcuptr_acquire(void) +{ + struct rcu_read_lock *skel; + int err; + + skel = rcu_read_lock__open(); + if (!ASSERT_OK_PTR(skel, "skel_open")) + return; + + skel->bss->target_pid = syscall(SYS_gettid); + + bpf_program__set_autoload(skel->progs.task_acquire, true); + err = rcu_read_lock__load(skel); + if (!ASSERT_OK(err, "skel_load")) + goto out; + + err = rcu_read_lock__attach(skel); + ASSERT_OK(err, "skel_attach"); +out: + rcu_read_lock__destroy(skel); +} + +static const char * const inproper_region_tests[] = { + "miss_lock", + "miss_unlock", + "non_sleepable_rcu_mismatch", + "inproper_sleepable_helper", + "inproper_sleepable_kfunc", + "nested_rcu_region", +}; + +static void test_inproper_region(void) +{ + struct rcu_read_lock *skel; + struct bpf_program *prog; + int i, err; + + for (i = 0; i < ARRAY_SIZE(inproper_region_tests); i++) { + skel = rcu_read_lock__open(); + if (!ASSERT_OK_PTR(skel, "skel_open")) + return; + + prog = bpf_object__find_program_by_name(skel->obj, inproper_region_tests[i]); + if (!ASSERT_OK_PTR(prog, "bpf_object__find_program_by_name")) + goto out; + bpf_program__set_autoload(prog, true); + err = rcu_read_lock__load(skel); + ASSERT_ERR(err, "skel_load"); +out: + rcu_read_lock__destroy(skel); + } +} + +static const char * const rcuptr_misuse_tests[] = { + "task_untrusted_non_rcuptr", + "task_untrusted_rcuptr", + "cross_rcu_region", +}; + +static void test_rcuptr_misuse(void) +{ + struct rcu_read_lock *skel; + struct bpf_program *prog; + int i, err; + + for (i = 0; i < ARRAY_SIZE(rcuptr_misuse_tests); i++) { + skel = rcu_read_lock__open(); + if (!ASSERT_OK_PTR(skel, "skel_open")) + return; + + prog = bpf_object__find_program_by_name(skel->obj, rcuptr_misuse_tests[i]); + if (!ASSERT_OK_PTR(prog, "bpf_object__find_program_by_name")) + goto out; + bpf_program__set_autoload(prog, true); + err = rcu_read_lock__load(skel); + ASSERT_ERR(err, "skel_load"); +out: + rcu_read_lock__destroy(skel); + } +} + +void test_rcu_read_lock(void) +{ + struct btf *vmlinux_btf; + int cgroup_fd; + + vmlinux_btf = btf__load_vmlinux_btf(); + if (!ASSERT_OK_PTR(vmlinux_btf, "could not load vmlinux BTF")) + return; + if (btf__find_by_name_kind(vmlinux_btf, "rcu", BTF_KIND_TYPE_TAG) < 0) { + test__skip(); + goto out; + } + + cgroup_fd = test__join_cgroup("/rcu_read_lock"); + if (!ASSERT_GE(cgroup_fd, 0, "join_cgroup /rcu_read_lock")) + goto out; + + cgroup_id = get_cgroup_id("/rcu_read_lock"); + if (test__start_subtest("success")) + test_success(); + if (test__start_subtest("rcuptr_acquire")) + test_rcuptr_acquire(); + if (test__start_subtest("negative_tests_inproper_region")) + test_inproper_region(); + if (test__start_subtest("negative_tests_rcuptr_misuse")) + test_rcuptr_misuse(); + close(cgroup_fd); +out: + btf__free(vmlinux_btf); +} diff --git a/tools/testing/selftests/bpf/progs/rcu_read_lock.c b/tools/testing/selftests/bpf/progs/rcu_read_lock.c new file mode 100644 index 000000000000..94a970076b98 --- /dev/null +++ b/tools/testing/selftests/bpf/progs/rcu_read_lock.c @@ -0,0 +1,290 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2022 Meta Platforms, Inc. and affiliates. */ + +#include "vmlinux.h" +#include +#include +#include "bpf_tracing_net.h" +#include "bpf_misc.h" + +char _license[] SEC("license") = "GPL"; + +struct { + __uint(type, BPF_MAP_TYPE_TASK_STORAGE); + __uint(map_flags, BPF_F_NO_PREALLOC); + __type(key, int); + __type(value, long); +} map_a SEC(".maps"); + +__u32 user_data, key_serial, target_pid; +__u64 flags, task_storage_val, cgroup_id; + +struct bpf_key *bpf_lookup_user_key(__u32 serial, __u64 flags) __ksym; +void bpf_key_put(struct bpf_key *key) __ksym; +void bpf_rcu_read_lock(void) __ksym; +void bpf_rcu_read_unlock(void) __ksym; +struct task_struct *bpf_task_acquire(struct task_struct *p) __ksym; +void bpf_task_release(struct task_struct *p) __ksym; + +SEC("?fentry.s/" SYS_PREFIX "sys_getpgid") +int get_cgroup_id(void *ctx) +{ + struct task_struct *task; + + task = bpf_get_current_task_btf(); + if (task->pid != target_pid) + return 0; + + /* simulate bpf_get_current_cgroup_id() helper */ + bpf_rcu_read_lock(); + cgroup_id = task->cgroups->dfl_cgrp->kn->id; + bpf_rcu_read_unlock(); + return 0; +} + +SEC("?fentry.s/" SYS_PREFIX "sys_getpgid") +int task_succ(void *ctx) +{ + struct task_struct *task, *real_parent; + long init_val = 2; + long *ptr; + + task = bpf_get_current_task_btf(); + if (task->pid != target_pid) + return 0; + + bpf_rcu_read_lock(); + /* region including helper using rcu ptr real_parent */ + real_parent = task->real_parent; + ptr = bpf_task_storage_get(&map_a, real_parent, &init_val, + BPF_LOCAL_STORAGE_GET_F_CREATE); + if (!ptr) + goto out; + ptr = bpf_task_storage_get(&map_a, real_parent, 0, 0); + if (!ptr) + goto out; + task_storage_val = *ptr; +out: + bpf_rcu_read_unlock(); + return 0; +} + +SEC("?fentry.s/" SYS_PREFIX "sys_nanosleep") +int no_lock(void *ctx) +{ + struct task_struct *task, *real_parent; + + /* no bpf_rcu_read_lock(), old code still works */ + task = bpf_get_current_task_btf(); + real_parent = task->real_parent; + (void)bpf_task_storage_get(&map_a, real_parent, 0, 0); + return 0; +} + +SEC("?fentry.s/" SYS_PREFIX "sys_nanosleep") +int two_regions(void *ctx) +{ + struct task_struct *task, *real_parent; + + /* two regions */ + task = bpf_get_current_task_btf(); + bpf_rcu_read_lock(); + bpf_rcu_read_unlock(); + bpf_rcu_read_lock(); + real_parent = task->real_parent; + (void)bpf_task_storage_get(&map_a, real_parent, 0, 0); + bpf_rcu_read_unlock(); + return 0; +} + +SEC("?fentry/" SYS_PREFIX "sys_getpgid") +int non_sleepable_1(void *ctx) +{ + struct task_struct *task, *real_parent; + + task = bpf_get_current_task_btf(); + bpf_rcu_read_lock(); + real_parent = task->real_parent; + (void)bpf_task_storage_get(&map_a, real_parent, 0, 0); + bpf_rcu_read_unlock(); + return 0; +} + +SEC("?fentry/" SYS_PREFIX "sys_getpgid") +int non_sleepable_2(void *ctx) +{ + struct task_struct *task, *real_parent; + + bpf_rcu_read_lock(); + task = bpf_get_current_task_btf(); + bpf_rcu_read_unlock(); + + bpf_rcu_read_lock(); + real_parent = task->real_parent; + (void)bpf_task_storage_get(&map_a, real_parent, 0, 0); + bpf_rcu_read_unlock(); + return 0; +} + +SEC("?fentry.s/" SYS_PREFIX "sys_nanosleep") +int task_acquire(void *ctx) +{ + struct task_struct *task, *real_parent; + + task = bpf_get_current_task_btf(); + bpf_rcu_read_lock(); + real_parent = task->real_parent; + /* acquire a reference which can be used outside rcu read lock region */ + real_parent = bpf_task_acquire(real_parent); + bpf_rcu_read_unlock(); + (void)bpf_task_storage_get(&map_a, real_parent, 0, 0); + bpf_task_release(real_parent); + return 0; +} + +SEC("?fentry.s/" SYS_PREFIX "sys_getpgid") +int miss_lock(void *ctx) +{ + struct task_struct *task; + struct css_set *cgroups; + struct cgroup *dfl_cgrp; + + /* missing bpf_rcu_read_lock() */ + task = bpf_get_current_task_btf(); + bpf_rcu_read_lock(); + (void)bpf_task_storage_get(&map_a, task, 0, 0); + bpf_rcu_read_unlock(); + bpf_rcu_read_unlock(); + return 0; +} + +SEC("?fentry.s/" SYS_PREFIX "sys_getpgid") +int miss_unlock(void *ctx) +{ + struct task_struct *task; + struct css_set *cgroups; + struct cgroup *dfl_cgrp; + + /* missing bpf_rcu_read_unlock() */ + task = bpf_get_current_task_btf(); + bpf_rcu_read_lock(); + (void)bpf_task_storage_get(&map_a, task, 0, 0); + return 0; +} + +SEC("?fentry/" SYS_PREFIX "sys_getpgid") +int non_sleepable_rcu_mismatch(void *ctx) +{ + struct task_struct *task, *real_parent; + + task = bpf_get_current_task_btf(); + /* non-sleepable: missing bpf_rcu_read_unlock() in one path */ + bpf_rcu_read_lock(); + real_parent = task->real_parent; + (void)bpf_task_storage_get(&map_a, real_parent, 0, 0); + if (real_parent) + bpf_rcu_read_unlock(); + return 0; +} + +SEC("?fentry.s/" SYS_PREFIX "sys_getpgid") +int inproper_sleepable_helper(void *ctx) +{ + struct task_struct *task, *real_parent; + struct pt_regs *regs; + __u32 value = 0; + void *ptr; + + task = bpf_get_current_task_btf(); + /* sleepable helper in rcu read lock region */ + bpf_rcu_read_lock(); + real_parent = task->real_parent; + regs = (struct pt_regs *)bpf_task_pt_regs(real_parent); + if (!regs) { + bpf_rcu_read_unlock(); + return 0; + } + + ptr = (void *)PT_REGS_IP(regs); + (void)bpf_copy_from_user_task(&value, sizeof(uint32_t), ptr, task, 0); + user_data = value; + (void)bpf_task_storage_get(&map_a, real_parent, 0, 0); + bpf_rcu_read_unlock(); + return 0; +} + +SEC("?lsm.s/bpf") +int BPF_PROG(inproper_sleepable_kfunc, int cmd, union bpf_attr *attr, unsigned int size) +{ + struct bpf_key *bkey; + + /* sleepable kfunc in rcu read lock region */ + bpf_rcu_read_lock(); + bkey = bpf_lookup_user_key(key_serial, flags); + bpf_rcu_read_unlock(); + if (!bkey) + return -1; + bpf_key_put(bkey); + + return 0; +} + +SEC("?fentry.s/" SYS_PREFIX "sys_nanosleep") +int nested_rcu_region(void *ctx) +{ + struct task_struct *task, *real_parent; + + /* nested rcu read lock regions */ + task = bpf_get_current_task_btf(); + bpf_rcu_read_lock(); + bpf_rcu_read_lock(); + real_parent = task->real_parent; + (void)bpf_task_storage_get(&map_a, real_parent, 0, 0); + bpf_rcu_read_unlock(); + bpf_rcu_read_unlock(); + return 0; +} + +SEC("?fentry.s/" SYS_PREFIX "sys_getpgid") +int task_untrusted_non_rcuptr(void *ctx) +{ + struct task_struct *task, *last_wakee; + + task = bpf_get_current_task_btf(); + bpf_rcu_read_lock(); + /* the pointer last_wakee marked as untrusted */ + last_wakee = task->real_parent->last_wakee; + (void)bpf_task_storage_get(&map_a, last_wakee, 0, 0); + bpf_rcu_read_unlock(); + return 0; +} + +SEC("?fentry.s/" SYS_PREFIX "sys_getpgid") +int task_untrusted_rcuptr(void *ctx) +{ + struct task_struct *task, *real_parent; + + task = bpf_get_current_task_btf(); + bpf_rcu_read_lock(); + real_parent = task->real_parent; + bpf_rcu_read_unlock(); + /* helper use of rcu ptr outside the rcu read lock region */ + (void)bpf_task_storage_get(&map_a, real_parent, 0, 0); + return 0; +} + +SEC("?fentry.s/" SYS_PREFIX "sys_nanosleep") +int cross_rcu_region(void *ctx) +{ + struct task_struct *task, *real_parent; + + /* rcu ptr define/use in different regions */ + task = bpf_get_current_task_btf(); + bpf_rcu_read_lock(); + real_parent = task->real_parent; + bpf_rcu_read_unlock(); + bpf_rcu_read_lock(); + (void)bpf_task_storage_get(&map_a, real_parent, 0, 0); + bpf_rcu_read_unlock(); + return 0; +} From c6b0337f01205decb31ed5e90e5aa760ac2d5b41 Mon Sep 17 00:00:00 2001 From: Alexei Starovoitov Date: Thu, 24 Nov 2022 13:53:14 -0800 Subject: [PATCH 1220/2002] bpf: Don't mark arguments to fentry/fexit programs as trusted. The PTR_TRUSTED flag should only be applied to pointers where the verifier can guarantee that such pointers are valid. The fentry/fexit/fmod_ret programs are not in this category. Only arguments of SEC("tp_btf") and SEC("iter") programs are trusted (which have BPF_TRACE_RAW_TP and BPF_TRACE_ITER attach_type correspondingly) This bug was masked because convert_ctx_accesses() was converting trusted loads into BPF_PROBE_MEM loads. Fix it as well. The loads from trusted pointers don't need exception handling. Fixes: 3f00c5239344 ("bpf: Allow trusted pointers to be passed to KF_TRUSTED_ARGS kfuncs") Signed-off-by: Alexei Starovoitov Signed-off-by: Daniel Borkmann Link: https://lore.kernel.org/bpf/20221124215314.55890-1-alexei.starovoitov@gmail.com --- kernel/bpf/btf.c | 16 +++++++++++++--- kernel/bpf/verifier.c | 3 --- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/kernel/bpf/btf.c b/kernel/bpf/btf.c index bd3369100239..d11cbf8cece7 100644 --- a/kernel/bpf/btf.c +++ b/kernel/bpf/btf.c @@ -5821,9 +5821,19 @@ static u32 get_ctx_arg_idx(struct btf *btf, const struct btf_type *func_proto, return nr_args + 1; } -static bool prog_type_args_trusted(enum bpf_prog_type prog_type) +static bool prog_args_trusted(const struct bpf_prog *prog) { - return prog_type == BPF_PROG_TYPE_TRACING || prog_type == BPF_PROG_TYPE_STRUCT_OPS; + enum bpf_attach_type atype = prog->expected_attach_type; + + switch (prog->type) { + case BPF_PROG_TYPE_TRACING: + return atype == BPF_TRACE_RAW_TP || atype == BPF_TRACE_ITER; + case BPF_PROG_TYPE_LSM: + case BPF_PROG_TYPE_STRUCT_OPS: + return true; + default: + return false; + } } bool btf_ctx_access(int off, int size, enum bpf_access_type type, @@ -5969,7 +5979,7 @@ bool btf_ctx_access(int off, int size, enum bpf_access_type type, } info->reg_type = PTR_TO_BTF_ID; - if (prog_type_args_trusted(prog->type)) + if (prog_args_trusted(prog)) info->reg_type |= PTR_TRUSTED; if (tgt_prog) { diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index f4500479f1c2..6599d25dae38 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -14905,7 +14905,6 @@ static int convert_ctx_accesses(struct bpf_verifier_env *env) break; case PTR_TO_BTF_ID: case PTR_TO_BTF_ID | PTR_UNTRUSTED: - case PTR_TO_BTF_ID | PTR_TRUSTED: /* PTR_TO_BTF_ID | MEM_ALLOC always has a valid lifetime, unlike * PTR_TO_BTF_ID, and an active ref_obj_id, but the same cannot * be said once it is marked PTR_UNTRUSTED, hence we must handle @@ -14913,8 +14912,6 @@ static int convert_ctx_accesses(struct bpf_verifier_env *env) * for this case. */ case PTR_TO_BTF_ID | MEM_ALLOC | PTR_UNTRUSTED: - case PTR_TO_BTF_ID | PTR_UNTRUSTED | PTR_TRUSTED: - case PTR_TO_BTF_ID | PTR_UNTRUSTED | MEM_ALLOC | PTR_TRUSTED: if (type == BPF_READ) { insn->code = BPF_LDX | BPF_PROBE_MEM | BPF_SIZE((insn)->code); From b74344cbede22fdfc1366348b3d576eac0131380 Mon Sep 17 00:00:00 2001 From: Rong Tao Date: Tue, 22 Nov 2022 08:50:42 +0800 Subject: [PATCH 1221/2002] docs/bpf: Update btf selftests program and add link Commit c64779e24e88("selftests/bpf: Merge most of test_btf into test_progs") renamed the BTF selftest from 'test_btf.c' to 'prog_tests/btf.c'. Signed-off-by: Rong Tao Signed-off-by: Daniel Borkmann Acked-by: David Vernet Link: https://lore.kernel.org/bpf/tencent_1FA6904156E8E599CAE4ABDBE80F22830106@qq.com --- Documentation/bpf/btf.rst | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Documentation/bpf/btf.rst b/Documentation/bpf/btf.rst index cf8722f96090..7cd7c5415a99 100644 --- a/Documentation/bpf/btf.rst +++ b/Documentation/bpf/btf.rst @@ -1062,4 +1062,9 @@ format.:: 7. Testing ========== -Kernel bpf selftest `test_btf.c` provides extensive set of BTF-related tests. +The kernel BPF selftest `tools/testing/selftests/bpf/prog_tests/btf.c`_ +provides an extensive set of BTF-related tests. + +.. Links +.. _tools/testing/selftests/bpf/prog_tests/btf.c: + https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/tree/tools/testing/selftests/bpf/prog_tests/btf.c From 19a2bdbaaddc71405494bd35fa034d9cf582b05e Mon Sep 17 00:00:00 2001 From: Rong Tao Date: Tue, 22 Nov 2022 10:32:56 +0800 Subject: [PATCH 1222/2002] samples/bpf: Fix wrong allocation size in xdp_router_ipv4_user prefix_key->data allocates three bytes using alloca(), but four bytes are actually accessed in the program. Signed-off-by: Rong Tao Signed-off-by: Daniel Borkmann Link: https://lore.kernel.org/bpf/tencent_F9E2E81922B0C181D05B96DAE5AB0ACE6B06@qq.com --- samples/bpf/xdp_router_ipv4_user.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/bpf/xdp_router_ipv4_user.c b/samples/bpf/xdp_router_ipv4_user.c index 683913bbf279..9d41db09c480 100644 --- a/samples/bpf/xdp_router_ipv4_user.c +++ b/samples/bpf/xdp_router_ipv4_user.c @@ -162,7 +162,7 @@ static void read_route(struct nlmsghdr *nh, int nll) __be32 gw; } *prefix_value; - prefix_key = alloca(sizeof(*prefix_key) + 3); + prefix_key = alloca(sizeof(*prefix_key) + 4); prefix_value = alloca(sizeof(*prefix_value)); prefix_key->prefixlen = 32; From 2b3e8f6f5b939ceeb2e097339bf78ebaaf11dfe9 Mon Sep 17 00:00:00 2001 From: Maryam Tahhan Date: Wed, 23 Nov 2022 09:00:43 +0000 Subject: [PATCH 1223/2002] docs/bpf: Add BPF_MAP_TYPE_XSKMAP documentation Add documentation for BPF_MAP_TYPE_XSKMAP including kernel version introduced, usage and examples. Signed-off-by: Maryam Tahhan Signed-off-by: Daniel Borkmann Link: https://lore.kernel.org/bpf/20221123090043.83945-1-mtahhan@redhat.com --- Documentation/bpf/map_xskmap.rst | 192 +++++++++++++++++++++++++++++++ 1 file changed, 192 insertions(+) create mode 100644 Documentation/bpf/map_xskmap.rst diff --git a/Documentation/bpf/map_xskmap.rst b/Documentation/bpf/map_xskmap.rst new file mode 100644 index 000000000000..7093b8208451 --- /dev/null +++ b/Documentation/bpf/map_xskmap.rst @@ -0,0 +1,192 @@ +.. SPDX-License-Identifier: GPL-2.0-only +.. Copyright (C) 2022 Red Hat, Inc. + +=================== +BPF_MAP_TYPE_XSKMAP +=================== + +.. note:: + - ``BPF_MAP_TYPE_XSKMAP`` was introduced in kernel version 4.18 + +The ``BPF_MAP_TYPE_XSKMAP`` is used as a backend map for XDP BPF helper +call ``bpf_redirect_map()`` and ``XDP_REDIRECT`` action, like 'devmap' and 'cpumap'. +This map type redirects raw XDP frames to `AF_XDP`_ sockets (XSKs), a new type of +address family in the kernel that allows redirection of frames from a driver to +user space without having to traverse the full network stack. An AF_XDP socket +binds to a single netdev queue. A mapping of XSKs to queues is shown below: + +.. code-block:: none + + +---------------------------------------------------+ + | xsk A | xsk B | xsk C |<---+ User space + =========================================================|========== + | Queue 0 | Queue 1 | Queue 2 | | Kernel + +---------------------------------------------------+ | + | Netdev eth0 | | + +---------------------------------------------------+ | + | +=============+ | | + | | key | xsk | | | + | +---------+ +=============+ | | + | | | | 0 | xsk A | | | + | | | +-------------+ | | + | | | | 1 | xsk B | | | + | | BPF |-- redirect -->+-------------+-------------+ + | | prog | | 2 | xsk C | | + | | | +-------------+ | + | | | | + | | | | + | +---------+ | + | | + +---------------------------------------------------+ + +.. note:: + An AF_XDP socket that is bound to a certain will *only* + accept XDP frames from that . If an XDP program tries to redirect + from a other than what the socket is bound to, the frame will + not be received on the socket. + +Typically an XSKMAP is created per netdev. This map contains an array of XSK File +Descriptors (FDs). The number of array elements is typically set or adjusted using +the ``max_entries`` map parameter. For AF_XDP ``max_entries`` is equal to the number +of queues supported by the netdev. + +.. note:: + Both the map key and map value size must be 4 bytes. + +Usage +===== + +Kernel BPF +---------- +bpf_redirect_map() +^^^^^^^^^^^^^^^^^^ +.. code-block:: c + + long bpf_redirect_map(struct bpf_map *map, u32 key, u64 flags) + +Redirect the packet to the endpoint referenced by ``map`` at index ``key``. +For ``BPF_MAP_TYPE_XSKMAP`` this map contains references to XSK FDs +for sockets attached to a netdev's queues. + +.. note:: + If the map is empty at an index, the packet is dropped. This means that it is + necessary to have an XDP program loaded with at least one XSK in the + XSKMAP to be able to get any traffic to user space through the socket. + +bpf_map_lookup_elem() +^^^^^^^^^^^^^^^^^^^^^ +.. code-block:: c + + void *bpf_map_lookup_elem(struct bpf_map *map, const void *key) + +XSK entry references of type ``struct xdp_sock *`` can be retrieved using the +``bpf_map_lookup_elem()`` helper. + +User space +---------- +.. note:: + XSK entries can only be updated/deleted from user space and not from + a BPF program. Trying to call these functions from a kernel BPF program will + result in the program failing to load and a verifier warning. + +bpf_map_update_elem() +^^^^^^^^^^^^^^^^^^^^^ +.. code-block:: c + + int bpf_map_update_elem(int fd, const void *key, const void *value, __u64 flags) + +XSK entries can be added or updated using the ``bpf_map_update_elem()`` +helper. The ``key`` parameter is equal to the queue_id of the queue the XSK +is attaching to. And the ``value`` parameter is the FD value of that socket. + +Under the hood, the XSKMAP update function uses the XSK FD value to retrieve the +associated ``struct xdp_sock`` instance. + +The flags argument can be one of the following: + +- BPF_ANY: Create a new element or update an existing element. +- BPF_NOEXIST: Create a new element only if it did not exist. +- BPF_EXIST: Update an existing element. + +bpf_map_lookup_elem() +^^^^^^^^^^^^^^^^^^^^^ +.. code-block:: c + + int bpf_map_lookup_elem(int fd, const void *key, void *value) + +Returns ``struct xdp_sock *`` or negative error in case of failure. + +bpf_map_delete_elem() +^^^^^^^^^^^^^^^^^^^^^ +.. code-block:: c + + int bpf_map_delete_elem(int fd, const void *key) + +XSK entries can be deleted using the ``bpf_map_delete_elem()`` +helper. This helper will return 0 on success, or negative error in case of +failure. + +.. note:: + When `libxdp`_ deletes an XSK it also removes the associated socket + entry from the XSKMAP. + +Examples +======== +Kernel +------ + +The following code snippet shows how to declare a ``BPF_MAP_TYPE_XSKMAP`` called +``xsks_map`` and how to redirect packets to an XSK. + +.. code-block:: c + + struct { + __uint(type, BPF_MAP_TYPE_XSKMAP); + __type(key, __u32); + __type(value, __u32); + __uint(max_entries, 64); + } xsks_map SEC(".maps"); + + + SEC("xdp") + int xsk_redir_prog(struct xdp_md *ctx) + { + __u32 index = ctx->rx_queue_index; + + if (bpf_map_lookup_elem(&xsks_map, &index)) + return bpf_redirect_map(&xsks_map, index, 0); + return XDP_PASS; + } + +User space +---------- + +The following code snippet shows how to update an XSKMAP with an XSK entry. + +.. code-block:: c + + int update_xsks_map(struct bpf_map *xsks_map, int queue_id, int xsk_fd) + { + int ret; + + ret = bpf_map_update_elem(bpf_map__fd(xsks_map), &queue_id, &xsk_fd, 0); + if (ret < 0) + fprintf(stderr, "Failed to update xsks_map: %s\n", strerror(errno)); + + return ret; + } + +For an example on how create AF_XDP sockets, please see the AF_XDP-example and +AF_XDP-forwarding programs in the `bpf-examples`_ directory in the `libxdp`_ repository. +For a detailed explaination of the AF_XDP interface please see: + +- `libxdp-readme`_. +- `AF_XDP`_ kernel documentation. + +.. note:: + The most comprehensive resource for using XSKMAPs and AF_XDP is `libxdp`_. + +.. _libxdp: https://github.com/xdp-project/xdp-tools/tree/master/lib/libxdp +.. _AF_XDP: https://www.kernel.org/doc/html/latest/networking/af_xdp.html +.. _bpf-examples: https://github.com/xdp-project/bpf-examples +.. _libxdp-readme: https://github.com/xdp-project/xdp-tools/tree/master/lib/libxdp#using-af_xdp-sockets From 813abcd98fb1b2cccf850cdfa092a4bfc50b2363 Mon Sep 17 00:00:00 2001 From: Frank Date: Tue, 22 Nov 2022 16:42:32 +0800 Subject: [PATCH 1224/2002] net: phy: add Motorcomm YT8531S phy id. We added patch for motorcomm.c to support YT8531S. This patch has been tested on AM335x platform which has one YT8531S interface card and passed all test cases. The tested cases indluding: YT8531S UTP function with support of 10M/100M/1000M; YT8531S Fiber function with support of 100M/1000M; and YT8531S Combo function that supports auto detection of media type. Since most functions of YT8531S are similar to YT8521 and we reuse some codes for YT8521 in the patch file. Signed-off-by: Frank Signed-off-by: David S. Miller --- drivers/net/phy/Kconfig | 2 +- drivers/net/phy/motorcomm.c | 52 +++++++++++++++++++++++++++++++++---- 2 files changed, 48 insertions(+), 6 deletions(-) diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig index 040c8bf6d05b..af00cf44cd97 100644 --- a/drivers/net/phy/Kconfig +++ b/drivers/net/phy/Kconfig @@ -260,7 +260,7 @@ config MOTORCOMM_PHY tristate "Motorcomm PHYs" help Enables support for Motorcomm network PHYs. - Currently supports the YT8511, YT8521 Gigabit Ethernet PHYs. + Currently supports the YT8511, YT8521, YT8531S Gigabit Ethernet PHYs. config NATIONAL_PHY tristate "National Semiconductor PHYs" diff --git a/drivers/net/phy/motorcomm.c b/drivers/net/phy/motorcomm.c index bd1ab5d0631f..685190db72de 100644 --- a/drivers/net/phy/motorcomm.c +++ b/drivers/net/phy/motorcomm.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0+ /* - * Motorcomm 8511/8521 PHY driver. + * Motorcomm 8511/8521/8531S PHY driver. * * Author: Peter Geis * Author: Frank @@ -12,9 +12,10 @@ #include #define PHY_ID_YT8511 0x0000010a -#define PHY_ID_YT8521 0x0000011A +#define PHY_ID_YT8521 0x0000011A +#define PHY_ID_YT8531S 0x4F51E91A -/* YT8521 Register Overview +/* YT8521/YT8531S Register Overview * UTP Register space | FIBER Register space * ------------------------------------------------------------ * | UTP MII | FIBER MII | @@ -147,7 +148,7 @@ #define YT8521_LINK_TIMER_CFG2_REG 0xA5 #define YT8521_LTCR_EN_AUTOSEN BIT(15) -/* 0xA000, 0xA001, 0xA003 ,and 0xA006 ~ 0xA00A are common ext registers +/* 0xA000, 0xA001, 0xA003, 0xA006 ~ 0xA00A and 0xA012 are common ext registers * of yt8521 phy. There is no need to switch reg space when operating these * registers. */ @@ -221,6 +222,9 @@ */ #define YTPHY_WCR_TYPE_PULSE BIT(0) +#define YT8531S_SYNCE_CFG_REG 0xA012 +#define YT8531S_SCR_SYNCE_ENABLE BIT(6) + /* Extended Register end */ struct yt8521_priv { @@ -647,6 +651,26 @@ static int yt8521_probe(struct phy_device *phydev) return 0; } +/** + * yt8531s_probe() - read chip config then set suitable polling_mode + * @phydev: a pointer to a &struct phy_device + * + * returns 0 or negative errno code + */ +static int yt8531s_probe(struct phy_device *phydev) +{ + int ret; + + /* Disable SyncE clock output by default */ + ret = ytphy_modify_ext_with_lock(phydev, YT8531S_SYNCE_CFG_REG, + YT8531S_SCR_SYNCE_ENABLE, 0); + if (ret < 0) + return ret; + + /* same as yt8521_probe */ + return yt8521_probe(phydev); +} + /** * ytphy_utp_read_lpa() - read LPA then setup lp_advertising for utp * @phydev: a pointer to a &struct phy_device @@ -1750,11 +1774,28 @@ static struct phy_driver motorcomm_phy_drvs[] = { .suspend = yt8521_suspend, .resume = yt8521_resume, }, + { + PHY_ID_MATCH_EXACT(PHY_ID_YT8531S), + .name = "YT8531S Gigabit Ethernet", + .get_features = yt8521_get_features, + .probe = yt8531s_probe, + .read_page = yt8521_read_page, + .write_page = yt8521_write_page, + .get_wol = ytphy_get_wol, + .set_wol = ytphy_set_wol, + .config_aneg = yt8521_config_aneg, + .aneg_done = yt8521_aneg_done, + .config_init = yt8521_config_init, + .read_status = yt8521_read_status, + .soft_reset = yt8521_soft_reset, + .suspend = yt8521_suspend, + .resume = yt8521_resume, + }, }; module_phy_driver(motorcomm_phy_drvs); -MODULE_DESCRIPTION("Motorcomm 8511/8521 PHY driver"); +MODULE_DESCRIPTION("Motorcomm 8511/8521/8531S PHY driver"); MODULE_AUTHOR("Peter Geis"); MODULE_AUTHOR("Frank"); MODULE_LICENSE("GPL"); @@ -1762,6 +1803,7 @@ MODULE_LICENSE("GPL"); static const struct mdio_device_id __maybe_unused motorcomm_tbl[] = { { PHY_ID_MATCH_EXACT(PHY_ID_YT8511) }, { PHY_ID_MATCH_EXACT(PHY_ID_YT8521) }, + { PHY_ID_MATCH_EXACT(PHY_ID_YT8531S) }, { /* sentinal */ } }; From f72cd76b05ea1ce9258484e8127932d0ea928f22 Mon Sep 17 00:00:00 2001 From: Xu Panda Date: Tue, 22 Nov 2022 20:09:23 +0800 Subject: [PATCH 1225/2002] net: stmmac: use sysfs_streq() instead of strncmp() Replace the open-code with sysfs_streq(). Signed-off-by: Xu Panda Signed-off-by: Yang Yang Signed-off-by: David S. Miller --- .../net/ethernet/stmicro/stmmac/stmmac_main.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index 0a9d13d7976f..4167e768a86a 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -7562,31 +7562,31 @@ static int __init stmmac_cmdline_opt(char *str) if (!str || !*str) return 1; while ((opt = strsep(&str, ",")) != NULL) { - if (!strncmp(opt, "debug:", 6)) { + if (sysfs_streq(opt, "debug:")) { if (kstrtoint(opt + 6, 0, &debug)) goto err; - } else if (!strncmp(opt, "phyaddr:", 8)) { + } else if (sysfs_streq(opt, "phyaddr:")) { if (kstrtoint(opt + 8, 0, &phyaddr)) goto err; - } else if (!strncmp(opt, "buf_sz:", 7)) { + } else if (sysfs_streq(opt, "buf_sz:")) { if (kstrtoint(opt + 7, 0, &buf_sz)) goto err; - } else if (!strncmp(opt, "tc:", 3)) { + } else if (sysfs_streq(opt, "tc:")) { if (kstrtoint(opt + 3, 0, &tc)) goto err; - } else if (!strncmp(opt, "watchdog:", 9)) { + } else if (sysfs_streq(opt, "watchdog:")) { if (kstrtoint(opt + 9, 0, &watchdog)) goto err; - } else if (!strncmp(opt, "flow_ctrl:", 10)) { + } else if (sysfs_streq(opt, "flow_ctrl:")) { if (kstrtoint(opt + 10, 0, &flow_ctrl)) goto err; - } else if (!strncmp(opt, "pause:", 6)) { + } else if (sysfs_streq(opt, "pause:", 6)) { if (kstrtoint(opt + 6, 0, &pause)) goto err; - } else if (!strncmp(opt, "eee_timer:", 10)) { + } else if (sysfs_streq(opt, "eee_timer:")) { if (kstrtoint(opt + 10, 0, &eee_timer)) goto err; - } else if (!strncmp(opt, "chain_mode:", 11)) { + } else if (sysfs_streq(opt, "chain_mode:")) { if (kstrtoint(opt + 11, 0, &chain_mode)) goto err; } From b084f6cc3563faf4f4d16c98852c0c734fe18914 Mon Sep 17 00:00:00 2001 From: Jiapeng Chong Date: Wed, 23 Nov 2022 17:37:02 +0800 Subject: [PATCH 1226/2002] lib/test_rhashtable: Remove set but unused variable 'insert_retries' Variable 'insert_retries' is not effectively used in the function, so delete it. lib/test_rhashtable.c:437:18: warning: variable 'insert_retries' set but not used. Link: https://bugzilla.openanolis.cn/show_bug.cgi?id=3242 Reported-by: Abaci Robot Signed-off-by: Jiapeng Chong Acked-by: Herbert Xu Signed-off-by: David S. Miller --- lib/test_rhashtable.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/lib/test_rhashtable.c b/lib/test_rhashtable.c index f2ba5787055a..3ae3399f3651 100644 --- a/lib/test_rhashtable.c +++ b/lib/test_rhashtable.c @@ -434,7 +434,7 @@ out_free: static int __init test_rhashtable_max(struct test_obj *array, unsigned int entries) { - unsigned int i, insert_retries = 0; + unsigned int i; int err; test_rht_params.max_size = roundup_pow_of_two(entries / 8); @@ -447,9 +447,7 @@ static int __init test_rhashtable_max(struct test_obj *array, obj->value.id = i * 2; err = insert_retry(&ht, obj, test_rht_params); - if (err > 0) - insert_retries += err; - else if (err) + if (err < 0) return err; } From f157c416c51ab8b7f5e833dfde8ace3a6325c19a Mon Sep 17 00:00:00 2001 From: Sabrina Dubroca Date: Thu, 24 Nov 2022 15:43:38 +0100 Subject: [PATCH 1227/2002] xfrm: a few coding style clean ups Signed-off-by: Sabrina Dubroca Signed-off-by: Steffen Klassert --- net/xfrm/xfrm_policy.c | 9 ++++++--- net/xfrm/xfrm_user.c | 6 +++--- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c index d80519c4e389..a049f91d4446 100644 --- a/net/xfrm/xfrm_policy.c +++ b/net/xfrm/xfrm_policy.c @@ -4414,7 +4414,8 @@ int xfrm_migrate(const struct xfrm_selector *sel, u8 dir, u8 type, struct xfrm_migrate *mp; /* Stage 0 - sanity checks */ - if ((err = xfrm_migrate_check(m, num_migrate)) < 0) + err = xfrm_migrate_check(m, num_migrate); + if (err < 0) goto out; if (dir >= XFRM_POLICY_MAX) { @@ -4423,7 +4424,8 @@ int xfrm_migrate(const struct xfrm_selector *sel, u8 dir, u8 type, } /* Stage 1 - find policy */ - if ((pol = xfrm_migrate_policy_find(sel, dir, type, net, if_id)) == NULL) { + pol = xfrm_migrate_policy_find(sel, dir, type, net, if_id); + if (!pol) { err = -ENOENT; goto out; } @@ -4445,7 +4447,8 @@ int xfrm_migrate(const struct xfrm_selector *sel, u8 dir, u8 type, } /* Stage 3 - update policy */ - if ((err = xfrm_policy_migrate(pol, m, num_migrate)) < 0) + err = xfrm_policy_migrate(pol, m, num_migrate); + if (err < 0) goto restore_state; /* Stage 4 - delete old state(s) */ diff --git a/net/xfrm/xfrm_user.c b/net/xfrm/xfrm_user.c index e73f9efc54c1..25de6e8faf8d 100644 --- a/net/xfrm/xfrm_user.c +++ b/net/xfrm/xfrm_user.c @@ -1538,7 +1538,7 @@ static int xfrm_alloc_userspi(struct sk_buff *skb, struct nlmsghdr *nlh, &p->info.saddr, 1, family); err = -ENOENT; - if (x == NULL) + if (!x) goto out_noput; err = xfrm_alloc_spi(x, p->min, p->max); @@ -2718,7 +2718,7 @@ static int xfrm_do_migrate(struct sk_buff *skb, struct nlmsghdr *nlh, struct xfrm_encap_tmpl *encap = NULL; u32 if_id = 0; - if (attrs[XFRMA_MIGRATE] == NULL) + if (!attrs[XFRMA_MIGRATE]) return -EINVAL; kmp = attrs[XFRMA_KMADDRESS] ? &km : NULL; @@ -2727,7 +2727,7 @@ static int xfrm_do_migrate(struct sk_buff *skb, struct nlmsghdr *nlh, if (err) return err; - err = copy_from_user_migrate((struct xfrm_migrate *)m, kmp, attrs, &n); + err = copy_from_user_migrate(m, kmp, attrs, &n); if (err) return err; From a25b19f36f921b90bcb826c80b568266b8ad40a4 Mon Sep 17 00:00:00 2001 From: Sabrina Dubroca Date: Thu, 24 Nov 2022 15:43:39 +0100 Subject: [PATCH 1228/2002] xfrm: add extack to xfrm_add_sa_expire Signed-off-by: Sabrina Dubroca Signed-off-by: Steffen Klassert --- net/xfrm/xfrm_user.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/net/xfrm/xfrm_user.c b/net/xfrm/xfrm_user.c index 25de6e8faf8d..1664baefae80 100644 --- a/net/xfrm/xfrm_user.c +++ b/net/xfrm/xfrm_user.c @@ -2584,8 +2584,11 @@ static int xfrm_add_sa_expire(struct sk_buff *skb, struct nlmsghdr *nlh, spin_lock_bh(&x->lock); err = -EINVAL; - if (x->km.state != XFRM_STATE_VALID) + if (x->km.state != XFRM_STATE_VALID) { + NL_SET_ERR_MSG(extack, "SA must be in VALID state"); goto out; + } + km_state_expired(x, ue->hard, nlh->nlmsg_pid); if (ue->hard) { From 880e475d2b0b1131a6e91464b2145820893e4ddf Mon Sep 17 00:00:00 2001 From: Sabrina Dubroca Date: Thu, 24 Nov 2022 15:43:40 +0100 Subject: [PATCH 1229/2002] xfrm: add extack to xfrm_del_sa Signed-off-by: Sabrina Dubroca Signed-off-by: Steffen Klassert --- net/xfrm/xfrm_user.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/xfrm/xfrm_user.c b/net/xfrm/xfrm_user.c index 1664baefae80..06a379d35ebb 100644 --- a/net/xfrm/xfrm_user.c +++ b/net/xfrm/xfrm_user.c @@ -862,12 +862,12 @@ static int xfrm_del_sa(struct sk_buff *skb, struct nlmsghdr *nlh, goto out; if (xfrm_state_kern(x)) { + NL_SET_ERR_MSG(extack, "SA is in use by tunnels"); err = -EPERM; goto out; } err = xfrm_state_delete(x); - if (err < 0) goto out; From 643bc1a2ee30efc9ab832401e89c9400cd9f52ac Mon Sep 17 00:00:00 2001 From: Sabrina Dubroca Date: Thu, 24 Nov 2022 15:43:41 +0100 Subject: [PATCH 1230/2002] xfrm: add extack to xfrm_new_ae and xfrm_replay_verify_len Signed-off-by: Sabrina Dubroca Signed-off-by: Steffen Klassert --- net/xfrm/xfrm_user.c | 37 ++++++++++++++++++++++++++++--------- 1 file changed, 28 insertions(+), 9 deletions(-) diff --git a/net/xfrm/xfrm_user.c b/net/xfrm/xfrm_user.c index 06a379d35ebb..13607df4f30d 100644 --- a/net/xfrm/xfrm_user.c +++ b/net/xfrm/xfrm_user.c @@ -515,7 +515,8 @@ static int attach_aead(struct xfrm_state *x, struct nlattr *rta, } static inline int xfrm_replay_verify_len(struct xfrm_replay_state_esn *replay_esn, - struct nlattr *rp) + struct nlattr *rp, + struct netlink_ext_ack *extack) { struct xfrm_replay_state_esn *up; unsigned int ulen; @@ -528,13 +529,25 @@ static inline int xfrm_replay_verify_len(struct xfrm_replay_state_esn *replay_es /* Check the overall length and the internal bitmap length to avoid * potential overflow. */ - if (nla_len(rp) < (int)ulen || - xfrm_replay_state_esn_len(replay_esn) != ulen || - replay_esn->bmp_len != up->bmp_len) + if (nla_len(rp) < (int)ulen) { + NL_SET_ERR_MSG(extack, "ESN attribute is too short"); return -EINVAL; + } - if (up->replay_window > up->bmp_len * sizeof(__u32) * 8) + if (xfrm_replay_state_esn_len(replay_esn) != ulen) { + NL_SET_ERR_MSG(extack, "New ESN size doesn't match the existing SA's ESN size"); return -EINVAL; + } + + if (replay_esn->bmp_len != up->bmp_len) { + NL_SET_ERR_MSG(extack, "New ESN bitmap size doesn't match the existing SA's ESN bitmap"); + return -EINVAL; + } + + if (up->replay_window > up->bmp_len * sizeof(__u32) * 8) { + NL_SET_ERR_MSG(extack, "ESN replay window is longer than the bitmap"); + return -EINVAL; + } return 0; } @@ -2433,12 +2446,16 @@ static int xfrm_new_ae(struct sk_buff *skb, struct nlmsghdr *nlh, struct nlattr *et = attrs[XFRMA_ETIMER_THRESH]; struct nlattr *rt = attrs[XFRMA_REPLAY_THRESH]; - if (!lt && !rp && !re && !et && !rt) + if (!lt && !rp && !re && !et && !rt) { + NL_SET_ERR_MSG(extack, "Missing required attribute for AE"); return err; + } /* pedantic mode - thou shalt sayeth replaceth */ - if (!(nlh->nlmsg_flags&NLM_F_REPLACE)) + if (!(nlh->nlmsg_flags & NLM_F_REPLACE)) { + NL_SET_ERR_MSG(extack, "NLM_F_REPLACE flag is required"); return err; + } mark = xfrm_mark_get(attrs, &m); @@ -2446,10 +2463,12 @@ static int xfrm_new_ae(struct sk_buff *skb, struct nlmsghdr *nlh, if (x == NULL) return -ESRCH; - if (x->km.state != XFRM_STATE_VALID) + if (x->km.state != XFRM_STATE_VALID) { + NL_SET_ERR_MSG(extack, "SA must be in VALID state"); goto out; + } - err = xfrm_replay_verify_len(x->replay_esn, re); + err = xfrm_replay_verify_len(x->replay_esn, re, extack); if (err) goto out; From bd12240337f43522b99c43f8976af34c712b5f57 Mon Sep 17 00:00:00 2001 From: Sabrina Dubroca Date: Thu, 24 Nov 2022 15:43:42 +0100 Subject: [PATCH 1231/2002] xfrm: add extack to xfrm_do_migrate Signed-off-by: Sabrina Dubroca Signed-off-by: Steffen Klassert --- include/net/xfrm.h | 3 ++- net/key/af_key.c | 2 +- net/xfrm/xfrm_policy.c | 28 ++++++++++++++++++++-------- net/xfrm/xfrm_user.c | 16 +++++++++++----- 4 files changed, 34 insertions(+), 15 deletions(-) diff --git a/include/net/xfrm.h b/include/net/xfrm.h index dbc81f5eb553..576566bd0be9 100644 --- a/include/net/xfrm.h +++ b/include/net/xfrm.h @@ -1703,7 +1703,8 @@ struct xfrm_state *xfrm_state_migrate(struct xfrm_state *x, int xfrm_migrate(const struct xfrm_selector *sel, u8 dir, u8 type, struct xfrm_migrate *m, int num_bundles, struct xfrm_kmaddress *k, struct net *net, - struct xfrm_encap_tmpl *encap, u32 if_id); + struct xfrm_encap_tmpl *encap, u32 if_id, + struct netlink_ext_ack *extack); #endif int km_new_mapping(struct xfrm_state *x, xfrm_address_t *ipaddr, __be16 sport); diff --git a/net/key/af_key.c b/net/key/af_key.c index c85df5b958d2..7f4ff5fe2257 100644 --- a/net/key/af_key.c +++ b/net/key/af_key.c @@ -2626,7 +2626,7 @@ static int pfkey_migrate(struct sock *sk, struct sk_buff *skb, } return xfrm_migrate(&sel, dir, XFRM_POLICY_TYPE_MAIN, m, i, - kma ? &k : NULL, net, NULL, 0); + kma ? &k : NULL, net, NULL, 0, NULL); out: return err; diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c index a049f91d4446..9b9e2765363d 100644 --- a/net/xfrm/xfrm_policy.c +++ b/net/xfrm/xfrm_policy.c @@ -4333,7 +4333,8 @@ static int migrate_tmpl_match(const struct xfrm_migrate *m, const struct xfrm_tm /* update endpoint address(es) of template(s) */ static int xfrm_policy_migrate(struct xfrm_policy *pol, - struct xfrm_migrate *m, int num_migrate) + struct xfrm_migrate *m, int num_migrate, + struct netlink_ext_ack *extack) { struct xfrm_migrate *mp; int i, j, n = 0; @@ -4341,6 +4342,7 @@ static int xfrm_policy_migrate(struct xfrm_policy *pol, write_lock_bh(&pol->lock); if (unlikely(pol->walk.dead)) { /* target policy has been deleted */ + NL_SET_ERR_MSG(extack, "Target policy not found"); write_unlock_bh(&pol->lock); return -ENOENT; } @@ -4372,17 +4374,22 @@ static int xfrm_policy_migrate(struct xfrm_policy *pol, return 0; } -static int xfrm_migrate_check(const struct xfrm_migrate *m, int num_migrate) +static int xfrm_migrate_check(const struct xfrm_migrate *m, int num_migrate, + struct netlink_ext_ack *extack) { int i, j; - if (num_migrate < 1 || num_migrate > XFRM_MAX_DEPTH) + if (num_migrate < 1 || num_migrate > XFRM_MAX_DEPTH) { + NL_SET_ERR_MSG(extack, "Invalid number of SAs to migrate, must be 0 < num <= XFRM_MAX_DEPTH (6)"); return -EINVAL; + } for (i = 0; i < num_migrate; i++) { if (xfrm_addr_any(&m[i].new_daddr, m[i].new_family) || - xfrm_addr_any(&m[i].new_saddr, m[i].new_family)) + xfrm_addr_any(&m[i].new_saddr, m[i].new_family)) { + NL_SET_ERR_MSG(extack, "Addresses in the MIGRATE attribute's list cannot be null"); return -EINVAL; + } /* check if there is any duplicated entry */ for (j = i + 1; j < num_migrate; j++) { @@ -4393,8 +4400,10 @@ static int xfrm_migrate_check(const struct xfrm_migrate *m, int num_migrate) m[i].proto == m[j].proto && m[i].mode == m[j].mode && m[i].reqid == m[j].reqid && - m[i].old_family == m[j].old_family) + m[i].old_family == m[j].old_family) { + NL_SET_ERR_MSG(extack, "Entries in the MIGRATE attribute's list must be unique"); return -EINVAL; + } } } @@ -4404,7 +4413,8 @@ static int xfrm_migrate_check(const struct xfrm_migrate *m, int num_migrate) int xfrm_migrate(const struct xfrm_selector *sel, u8 dir, u8 type, struct xfrm_migrate *m, int num_migrate, struct xfrm_kmaddress *k, struct net *net, - struct xfrm_encap_tmpl *encap, u32 if_id) + struct xfrm_encap_tmpl *encap, u32 if_id, + struct netlink_ext_ack *extack) { int i, err, nx_cur = 0, nx_new = 0; struct xfrm_policy *pol = NULL; @@ -4414,11 +4424,12 @@ int xfrm_migrate(const struct xfrm_selector *sel, u8 dir, u8 type, struct xfrm_migrate *mp; /* Stage 0 - sanity checks */ - err = xfrm_migrate_check(m, num_migrate); + err = xfrm_migrate_check(m, num_migrate, extack); if (err < 0) goto out; if (dir >= XFRM_POLICY_MAX) { + NL_SET_ERR_MSG(extack, "Invalid policy direction"); err = -EINVAL; goto out; } @@ -4426,6 +4437,7 @@ int xfrm_migrate(const struct xfrm_selector *sel, u8 dir, u8 type, /* Stage 1 - find policy */ pol = xfrm_migrate_policy_find(sel, dir, type, net, if_id); if (!pol) { + NL_SET_ERR_MSG(extack, "Target policy not found"); err = -ENOENT; goto out; } @@ -4447,7 +4459,7 @@ int xfrm_migrate(const struct xfrm_selector *sel, u8 dir, u8 type, } /* Stage 3 - update policy */ - err = xfrm_policy_migrate(pol, m, num_migrate); + err = xfrm_policy_migrate(pol, m, num_migrate, extack); if (err < 0) goto restore_state; diff --git a/net/xfrm/xfrm_user.c b/net/xfrm/xfrm_user.c index 13607df4f30d..c5d6a92d73cb 100644 --- a/net/xfrm/xfrm_user.c +++ b/net/xfrm/xfrm_user.c @@ -2687,7 +2687,8 @@ nomem: #ifdef CONFIG_XFRM_MIGRATE static int copy_from_user_migrate(struct xfrm_migrate *ma, struct xfrm_kmaddress *k, - struct nlattr **attrs, int *num) + struct nlattr **attrs, int *num, + struct netlink_ext_ack *extack) { struct nlattr *rt = attrs[XFRMA_MIGRATE]; struct xfrm_user_migrate *um; @@ -2706,8 +2707,10 @@ static int copy_from_user_migrate(struct xfrm_migrate *ma, um = nla_data(rt); num_migrate = nla_len(rt) / sizeof(*um); - if (num_migrate <= 0 || num_migrate > XFRM_MAX_DEPTH) + if (num_migrate <= 0 || num_migrate > XFRM_MAX_DEPTH) { + NL_SET_ERR_MSG(extack, "Invalid number of SAs to migrate, must be 0 < num <= XFRM_MAX_DEPTH (6)"); return -EINVAL; + } for (i = 0; i < num_migrate; i++, um++, ma++) { memcpy(&ma->old_daddr, &um->old_daddr, sizeof(ma->old_daddr)); @@ -2740,8 +2743,10 @@ static int xfrm_do_migrate(struct sk_buff *skb, struct nlmsghdr *nlh, struct xfrm_encap_tmpl *encap = NULL; u32 if_id = 0; - if (!attrs[XFRMA_MIGRATE]) + if (!attrs[XFRMA_MIGRATE]) { + NL_SET_ERR_MSG(extack, "Missing required MIGRATE attribute"); return -EINVAL; + } kmp = attrs[XFRMA_KMADDRESS] ? &km : NULL; @@ -2749,7 +2754,7 @@ static int xfrm_do_migrate(struct sk_buff *skb, struct nlmsghdr *nlh, if (err) return err; - err = copy_from_user_migrate(m, kmp, attrs, &n); + err = copy_from_user_migrate(m, kmp, attrs, &n, extack); if (err) return err; @@ -2766,7 +2771,8 @@ static int xfrm_do_migrate(struct sk_buff *skb, struct nlmsghdr *nlh, if (attrs[XFRMA_IF_ID]) if_id = nla_get_u32(attrs[XFRMA_IF_ID]); - err = xfrm_migrate(&pi->sel, pi->dir, type, m, n, kmp, net, encap, if_id); + err = xfrm_migrate(&pi->sel, pi->dir, type, m, n, kmp, net, encap, + if_id, extack); kfree(encap); From c2dad11e0466a27d40041845cf63cdfb4fbd991f Mon Sep 17 00:00:00 2001 From: Sabrina Dubroca Date: Thu, 24 Nov 2022 15:43:43 +0100 Subject: [PATCH 1232/2002] xfrm: add extack to xfrm_alloc_userspi Signed-off-by: Sabrina Dubroca Signed-off-by: Steffen Klassert --- include/net/xfrm.h | 5 +++-- net/key/af_key.c | 4 ++-- net/xfrm/xfrm_state.c | 21 ++++++++++++++++----- net/xfrm/xfrm_user.c | 8 +++++--- 4 files changed, 26 insertions(+), 12 deletions(-) diff --git a/include/net/xfrm.h b/include/net/xfrm.h index 576566bd0be9..e0cc6791c001 100644 --- a/include/net/xfrm.h +++ b/include/net/xfrm.h @@ -1681,8 +1681,9 @@ struct xfrm_policy *xfrm_policy_byid(struct net *net, int xfrm_policy_flush(struct net *net, u8 type, bool task_valid); void xfrm_policy_hash_rebuild(struct net *net); u32 xfrm_get_acqseq(void); -int verify_spi_info(u8 proto, u32 min, u32 max); -int xfrm_alloc_spi(struct xfrm_state *x, u32 minspi, u32 maxspi); +int verify_spi_info(u8 proto, u32 min, u32 max, struct netlink_ext_ack *extack); +int xfrm_alloc_spi(struct xfrm_state *x, u32 minspi, u32 maxspi, + struct netlink_ext_ack *extack); struct xfrm_state *xfrm_find_acq(struct net *net, const struct xfrm_mark *mark, u8 mode, u32 reqid, u32 if_id, u8 proto, const xfrm_address_t *daddr, diff --git a/net/key/af_key.c b/net/key/af_key.c index 7f4ff5fe2257..e1d2155605aa 100644 --- a/net/key/af_key.c +++ b/net/key/af_key.c @@ -1377,13 +1377,13 @@ static int pfkey_getspi(struct sock *sk, struct sk_buff *skb, const struct sadb_ max_spi = range->sadb_spirange_max; } - err = verify_spi_info(x->id.proto, min_spi, max_spi); + err = verify_spi_info(x->id.proto, min_spi, max_spi, NULL); if (err) { xfrm_state_put(x); return err; } - err = xfrm_alloc_spi(x, min_spi, max_spi); + err = xfrm_alloc_spi(x, min_spi, max_spi, NULL); resp_skb = err ? ERR_PTR(err) : pfkey_xfrm_state2msg(x); if (IS_ERR(resp_skb)) { diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c index 81df34b3da6e..d0ae17e3bb38 100644 --- a/net/xfrm/xfrm_state.c +++ b/net/xfrm/xfrm_state.c @@ -2017,7 +2017,7 @@ u32 xfrm_get_acqseq(void) } EXPORT_SYMBOL(xfrm_get_acqseq); -int verify_spi_info(u8 proto, u32 min, u32 max) +int verify_spi_info(u8 proto, u32 min, u32 max, struct netlink_ext_ack *extack) { switch (proto) { case IPPROTO_AH: @@ -2026,22 +2026,28 @@ int verify_spi_info(u8 proto, u32 min, u32 max) case IPPROTO_COMP: /* IPCOMP spi is 16-bits. */ - if (max >= 0x10000) + if (max >= 0x10000) { + NL_SET_ERR_MSG(extack, "IPCOMP SPI must be <= 65535"); return -EINVAL; + } break; default: + NL_SET_ERR_MSG(extack, "Invalid protocol, must be one of AH, ESP, IPCOMP"); return -EINVAL; } - if (min > max) + if (min > max) { + NL_SET_ERR_MSG(extack, "Invalid SPI range: min > max"); return -EINVAL; + } return 0; } EXPORT_SYMBOL(verify_spi_info); -int xfrm_alloc_spi(struct xfrm_state *x, u32 low, u32 high) +int xfrm_alloc_spi(struct xfrm_state *x, u32 low, u32 high, + struct netlink_ext_ack *extack) { struct net *net = xs_net(x); unsigned int h; @@ -2053,8 +2059,10 @@ int xfrm_alloc_spi(struct xfrm_state *x, u32 low, u32 high) u32 mark = x->mark.v & x->mark.m; spin_lock_bh(&x->lock); - if (x->km.state == XFRM_STATE_DEAD) + if (x->km.state == XFRM_STATE_DEAD) { + NL_SET_ERR_MSG(extack, "Target ACQUIRE is in DEAD state"); goto unlock; + } err = 0; if (x->id.spi) @@ -2065,6 +2073,7 @@ int xfrm_alloc_spi(struct xfrm_state *x, u32 low, u32 high) if (minspi == maxspi) { x0 = xfrm_state_lookup(net, mark, &x->id.daddr, minspi, x->id.proto, x->props.family); if (x0) { + NL_SET_ERR_MSG(extack, "Requested SPI is already in use"); xfrm_state_put(x0); goto unlock; } @@ -2089,6 +2098,8 @@ int xfrm_alloc_spi(struct xfrm_state *x, u32 low, u32 high) spin_unlock_bh(&net->xfrm.xfrm_state_lock); err = 0; + } else { + NL_SET_ERR_MSG(extack, "No SPI available in the requested range"); } unlock: diff --git a/net/xfrm/xfrm_user.c b/net/xfrm/xfrm_user.c index c5d6a92d73cb..5c280e04e02c 100644 --- a/net/xfrm/xfrm_user.c +++ b/net/xfrm/xfrm_user.c @@ -1523,7 +1523,7 @@ static int xfrm_alloc_userspi(struct sk_buff *skb, struct nlmsghdr *nlh, u32 if_id = 0; p = nlmsg_data(nlh); - err = verify_spi_info(p->info.id.proto, p->min, p->max); + err = verify_spi_info(p->info.id.proto, p->min, p->max, extack); if (err) goto out_noput; @@ -1551,10 +1551,12 @@ static int xfrm_alloc_userspi(struct sk_buff *skb, struct nlmsghdr *nlh, &p->info.saddr, 1, family); err = -ENOENT; - if (!x) + if (!x) { + NL_SET_ERR_MSG(extack, "Target ACQUIRE not found"); goto out_noput; + } - err = xfrm_alloc_spi(x, p->min, p->max); + err = xfrm_alloc_spi(x, p->min, p->max, extack); if (err) goto out; From a741721680092b64ff71fc1f1c790123c6d40a02 Mon Sep 17 00:00:00 2001 From: Sabrina Dubroca Date: Thu, 24 Nov 2022 15:43:44 +0100 Subject: [PATCH 1233/2002] xfrm: add extack to xfrm_set_spdinfo Signed-off-by: Sabrina Dubroca Signed-off-by: Steffen Klassert --- net/xfrm/xfrm_user.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/net/xfrm/xfrm_user.c b/net/xfrm/xfrm_user.c index 5c280e04e02c..0eb4696661c8 100644 --- a/net/xfrm/xfrm_user.c +++ b/net/xfrm/xfrm_user.c @@ -1367,20 +1367,28 @@ static int xfrm_set_spdinfo(struct sk_buff *skb, struct nlmsghdr *nlh, if (attrs[XFRMA_SPD_IPV4_HTHRESH]) { struct nlattr *rta = attrs[XFRMA_SPD_IPV4_HTHRESH]; - if (nla_len(rta) < sizeof(*thresh4)) + if (nla_len(rta) < sizeof(*thresh4)) { + NL_SET_ERR_MSG(extack, "Invalid SPD_IPV4_HTHRESH attribute length"); return -EINVAL; + } thresh4 = nla_data(rta); - if (thresh4->lbits > 32 || thresh4->rbits > 32) + if (thresh4->lbits > 32 || thresh4->rbits > 32) { + NL_SET_ERR_MSG(extack, "Invalid hash threshold (must be <= 32 for IPv4)"); return -EINVAL; + } } if (attrs[XFRMA_SPD_IPV6_HTHRESH]) { struct nlattr *rta = attrs[XFRMA_SPD_IPV6_HTHRESH]; - if (nla_len(rta) < sizeof(*thresh6)) + if (nla_len(rta) < sizeof(*thresh6)) { + NL_SET_ERR_MSG(extack, "Invalid SPD_IPV6_HTHRESH attribute length"); return -EINVAL; + } thresh6 = nla_data(rta); - if (thresh6->lbits > 128 || thresh6->rbits > 128) + if (thresh6->lbits > 128 || thresh6->rbits > 128) { + NL_SET_ERR_MSG(extack, "Invalid hash threshold (must be <= 128 for IPv6)"); return -EINVAL; + } } if (thresh4 || thresh6) { From 465a38a269e93855393a217fb6a74de1e59767b8 Mon Sep 17 00:00:00 2001 From: Steen Hegelund Date: Wed, 23 Nov 2022 16:25:42 +0100 Subject: [PATCH 1234/2002] net: microchip: sparx5: Support for copying and modifying rules in the API This adds support for making a copy of a rule and modify keys and actions to differentiate the copy. Signed-off-by: Steen Hegelund Signed-off-by: David S. Miller --- .../net/ethernet/microchip/vcap/vcap_api.c | 185 +++++++++++++++++- .../ethernet/microchip/vcap/vcap_api_client.h | 22 ++- .../ethernet/microchip/vcap/vcap_api_kunit.c | 6 +- .../microchip/vcap/vcap_api_private.h | 4 - 4 files changed, 206 insertions(+), 11 deletions(-) diff --git a/drivers/net/ethernet/microchip/vcap/vcap_api.c b/drivers/net/ethernet/microchip/vcap/vcap_api.c index ac7a32ff755e..2f171d0b9187 100644 --- a/drivers/net/ethernet/microchip/vcap/vcap_api.c +++ b/drivers/net/ethernet/microchip/vcap/vcap_api.c @@ -173,6 +173,7 @@ const struct vcap_set *vcap_keyfieldset(struct vcap_control *vctrl, return NULL; return kset; } +EXPORT_SYMBOL_GPL(vcap_keyfieldset); /* Return the typegroup table for the matching keyset (using subword size) */ const struct vcap_typegroup * @@ -824,8 +825,8 @@ vcap_find_keyset_keyfield(struct vcap_control *vctrl, } /* Match a list of keys against the keysets available in a vcap type */ -static bool vcap_rule_find_keysets(struct vcap_rule_internal *ri, - struct vcap_keyset_list *matches) +static bool _vcap_rule_find_keysets(struct vcap_rule_internal *ri, + struct vcap_keyset_list *matches) { const struct vcap_client_keyfield *ckf; int keyset, found, keycount, map_size; @@ -864,6 +865,16 @@ static bool vcap_rule_find_keysets(struct vcap_rule_internal *ri, return matches->cnt > 0; } +/* Match a list of keys against the keysets available in a vcap type */ +bool vcap_rule_find_keysets(struct vcap_rule *rule, + struct vcap_keyset_list *matches) +{ + struct vcap_rule_internal *ri = to_intrule(rule); + + return _vcap_rule_find_keysets(ri, matches); +} +EXPORT_SYMBOL_GPL(vcap_rule_find_keysets); + /* Validate a rule with respect to available port keys */ int vcap_val_rule(struct vcap_rule *rule, u16 l3_proto) { @@ -888,7 +899,7 @@ int vcap_val_rule(struct vcap_rule *rule, u16 l3_proto) matches.max = ARRAY_SIZE(keysets); if (ri->data.keyset == VCAP_KFS_NO_VALUE) { /* Iterate over rule keyfields and select keysets that fits */ - if (!vcap_rule_find_keysets(ri, &matches)) { + if (!_vcap_rule_find_keysets(ri, &matches)) { ri->data.exterr = VCAP_ERR_NO_KEYSET_MATCH; return -EINVAL; } @@ -1270,6 +1281,19 @@ int vcap_del_rules(struct vcap_control *vctrl, struct vcap_admin *admin) } EXPORT_SYMBOL_GPL(vcap_del_rules); +/* Find a client key field in a rule */ +static struct vcap_client_keyfield * +vcap_find_keyfield(struct vcap_rule *rule, enum vcap_key_field key) +{ + struct vcap_rule_internal *ri = to_intrule(rule); + struct vcap_client_keyfield *ckf; + + list_for_each_entry(ckf, &ri->data.keyfields, ctrl.list) + if (ckf->ctrl.key == key) + return ckf; + return NULL; +} + /* Find information on a key field in a rule */ const struct vcap_field *vcap_lookup_keyfield(struct vcap_rule *rule, enum vcap_key_field key) @@ -1434,6 +1458,19 @@ int vcap_rule_add_key_u128(struct vcap_rule *rule, enum vcap_key_field key, } EXPORT_SYMBOL_GPL(vcap_rule_add_key_u128); +/* Find a client action field in a rule */ +static struct vcap_client_actionfield * +vcap_find_actionfield(struct vcap_rule *rule, enum vcap_action_field act) +{ + struct vcap_rule_internal *ri = (struct vcap_rule_internal *)rule; + struct vcap_client_actionfield *caf; + + list_for_each_entry(caf, &ri->data.actionfields, ctrl.list) + if (caf->ctrl.action == act) + return caf; + return NULL; +} + static void vcap_copy_from_client_actionfield(struct vcap_rule *rule, struct vcap_client_actionfield *field, struct vcap_client_actionfield_data *data) @@ -1772,6 +1809,148 @@ int vcap_rule_get_counter(struct vcap_rule *rule, struct vcap_counter *ctr) } EXPORT_SYMBOL_GPL(vcap_rule_get_counter); +static int vcap_rule_mod_key(struct vcap_rule *rule, + enum vcap_key_field key, + enum vcap_field_type ftype, + struct vcap_client_keyfield_data *data) +{ + struct vcap_client_keyfield *field; + + field = vcap_find_keyfield(rule, key); + if (!field) + return vcap_rule_add_key(rule, key, ftype, data); + vcap_copy_from_client_keyfield(rule, field, data); + return 0; +} + +/* Modify a 32 bit key field with value and mask in the rule */ +int vcap_rule_mod_key_u32(struct vcap_rule *rule, enum vcap_key_field key, + u32 value, u32 mask) +{ + struct vcap_client_keyfield_data data; + + data.u32.value = value; + data.u32.mask = mask; + return vcap_rule_mod_key(rule, key, VCAP_FIELD_U32, &data); +} +EXPORT_SYMBOL_GPL(vcap_rule_mod_key_u32); + +static int vcap_rule_mod_action(struct vcap_rule *rule, + enum vcap_action_field action, + enum vcap_field_type ftype, + struct vcap_client_actionfield_data *data) +{ + struct vcap_client_actionfield *field; + + field = vcap_find_actionfield(rule, action); + if (!field) + return vcap_rule_add_action(rule, action, ftype, data); + vcap_copy_from_client_actionfield(rule, field, data); + return 0; +} + +/* Modify a 32 bit action field with value in the rule */ +int vcap_rule_mod_action_u32(struct vcap_rule *rule, + enum vcap_action_field action, + u32 value) +{ + struct vcap_client_actionfield_data data; + + data.u32.value = value; + return vcap_rule_mod_action(rule, action, VCAP_FIELD_U32, &data); +} +EXPORT_SYMBOL_GPL(vcap_rule_mod_action_u32); + +/* Drop keys in a keylist and any keys that are not supported by the keyset */ +int vcap_filter_rule_keys(struct vcap_rule *rule, + enum vcap_key_field keylist[], int length, + bool drop_unsupported) +{ + struct vcap_rule_internal *ri = to_intrule(rule); + struct vcap_client_keyfield *ckf, *next_ckf; + const struct vcap_field *fields; + enum vcap_key_field key; + int err = 0; + int idx; + + if (length > 0) { + err = -EEXIST; + list_for_each_entry_safe(ckf, next_ckf, + &ri->data.keyfields, ctrl.list) { + key = ckf->ctrl.key; + for (idx = 0; idx < length; ++idx) + if (key == keylist[idx]) { + list_del(&ckf->ctrl.list); + kfree(ckf); + idx++; + err = 0; + } + } + } + if (drop_unsupported) { + err = -EEXIST; + fields = vcap_keyfields(ri->vctrl, ri->admin->vtype, + rule->keyset); + if (!fields) + return err; + list_for_each_entry_safe(ckf, next_ckf, + &ri->data.keyfields, ctrl.list) { + key = ckf->ctrl.key; + if (fields[key].width == 0) { + list_del(&ckf->ctrl.list); + kfree(ckf); + err = 0; + } + } + } + return err; +} +EXPORT_SYMBOL_GPL(vcap_filter_rule_keys); + +/* Make a full copy of an existing rule with a new rule id */ +struct vcap_rule *vcap_copy_rule(struct vcap_rule *erule) +{ + struct vcap_rule_internal *ri = to_intrule(erule); + struct vcap_client_actionfield *caf; + struct vcap_client_keyfield *ckf; + struct vcap_rule *rule; + int err; + + err = vcap_api_check(ri->vctrl); + if (err) + return ERR_PTR(err); + + rule = vcap_alloc_rule(ri->vctrl, ri->ndev, ri->data.vcap_chain_id, + ri->data.user, ri->data.priority, 0); + if (IS_ERR(rule)) + return rule; + + list_for_each_entry(ckf, &ri->data.keyfields, ctrl.list) { + /* Add a key duplicate in the new rule */ + err = vcap_rule_add_key(rule, + ckf->ctrl.key, + ckf->ctrl.type, + &ckf->data); + if (err) + goto err; + } + + list_for_each_entry(caf, &ri->data.actionfields, ctrl.list) { + /* Add a action duplicate in the new rule */ + err = vcap_rule_add_action(rule, + caf->ctrl.action, + caf->ctrl.type, + &caf->data); + if (err) + goto err; + } + return rule; +err: + vcap_free_rule(rule); + return ERR_PTR(err); +} +EXPORT_SYMBOL_GPL(vcap_copy_rule); + #ifdef CONFIG_VCAP_KUNIT_TEST #include "vcap_api_kunit.c" #endif diff --git a/drivers/net/ethernet/microchip/vcap/vcap_api_client.h b/drivers/net/ethernet/microchip/vcap/vcap_api_client.h index 654ef8fa6d62..93a0fcb12a81 100644 --- a/drivers/net/ethernet/microchip/vcap/vcap_api_client.h +++ b/drivers/net/ethernet/microchip/vcap/vcap_api_client.h @@ -168,6 +168,8 @@ int vcap_val_rule(struct vcap_rule *rule, u16 l3_proto); int vcap_add_rule(struct vcap_rule *rule); /* Delete rule in a VCAP instance */ int vcap_del_rule(struct vcap_control *vctrl, struct net_device *ndev, u32 id); +/* Make a full copy of an existing rule with a new rule id */ +struct vcap_rule *vcap_copy_rule(struct vcap_rule *rule); /* Update the keyset for the rule */ int vcap_set_rule_set_keyset(struct vcap_rule *rule, @@ -213,7 +215,13 @@ bool vcap_is_next_lookup(struct vcap_control *vctrl, int cur_cid, int next_cid); /* Provide all rules via a callback interface */ int vcap_rule_iter(struct vcap_control *vctrl, int (*callback)(void *, struct vcap_rule *), void *arg); - +/* Match a list of keys against the keysets available in a vcap type */ +bool vcap_rule_find_keysets(struct vcap_rule *rule, + struct vcap_keyset_list *matches); +/* Return the keyset information for the keyset */ +const struct vcap_set *vcap_keyfieldset(struct vcap_control *vctrl, + enum vcap_type vt, + enum vcap_keyfield_set keyset); /* Copy to host byte order */ void vcap_netbytes_copy(u8 *dst, u8 *src, int count); @@ -226,6 +234,10 @@ int vcap_del_rules(struct vcap_control *vctrl, struct vcap_admin *admin); /* Add a keyset to a keyset list */ bool vcap_keyset_list_add(struct vcap_keyset_list *keysetlist, enum vcap_keyfield_set keyset); +/* Drop keys in a keylist and any keys that are not supported by the keyset */ +int vcap_filter_rule_keys(struct vcap_rule *rule, + enum vcap_key_field keylist[], int length, + bool drop_unsupported); /* map keyset id to a string with the keyset name */ const char *vcap_keyset_name(struct vcap_control *vctrl, @@ -234,4 +246,12 @@ const char *vcap_keyset_name(struct vcap_control *vctrl, const char *vcap_keyfield_name(struct vcap_control *vctrl, enum vcap_key_field key); +/* Modify a 32 bit key field with value and mask in the rule */ +int vcap_rule_mod_key_u32(struct vcap_rule *rule, enum vcap_key_field key, + u32 value, u32 mask); +/* Modify a 32 bit action field with value in the rule */ +int vcap_rule_mod_action_u32(struct vcap_rule *rule, + enum vcap_action_field action, + u32 value); + #endif /* __VCAP_API_CLIENT__ */ diff --git a/drivers/net/ethernet/microchip/vcap/vcap_api_kunit.c b/drivers/net/ethernet/microchip/vcap/vcap_api_kunit.c index f48d93f374af..875068e484c9 100644 --- a/drivers/net/ethernet/microchip/vcap/vcap_api_kunit.c +++ b/drivers/net/ethernet/microchip/vcap/vcap_api_kunit.c @@ -1197,7 +1197,7 @@ static void vcap_api_rule_find_keyset_basic_test(struct kunit *test) for (idx = 0; idx < ARRAY_SIZE(ckf); idx++) list_add_tail(&ckf[idx].ctrl.list, &ri.data.keyfields); - ret = vcap_rule_find_keysets(&ri, &matches); + ret = vcap_rule_find_keysets(&ri.data, &matches); KUNIT_EXPECT_EQ(test, true, ret); KUNIT_EXPECT_EQ(test, 1, matches.cnt); @@ -1244,7 +1244,7 @@ static void vcap_api_rule_find_keyset_failed_test(struct kunit *test) for (idx = 0; idx < ARRAY_SIZE(ckf); idx++) list_add_tail(&ckf[idx].ctrl.list, &ri.data.keyfields); - ret = vcap_rule_find_keysets(&ri, &matches); + ret = vcap_rule_find_keysets(&ri.data, &matches); KUNIT_EXPECT_EQ(test, false, ret); KUNIT_EXPECT_EQ(test, 0, matches.cnt); @@ -1291,7 +1291,7 @@ static void vcap_api_rule_find_keyset_many_test(struct kunit *test) for (idx = 0; idx < ARRAY_SIZE(ckf); idx++) list_add_tail(&ckf[idx].ctrl.list, &ri.data.keyfields); - ret = vcap_rule_find_keysets(&ri, &matches); + ret = vcap_rule_find_keysets(&ri.data, &matches); KUNIT_EXPECT_EQ(test, true, ret); KUNIT_EXPECT_EQ(test, 6, matches.cnt); diff --git a/drivers/net/ethernet/microchip/vcap/vcap_api_private.h b/drivers/net/ethernet/microchip/vcap/vcap_api_private.h index 18a9a0cd9606..9ac1b1d55f22 100644 --- a/drivers/net/ethernet/microchip/vcap/vcap_api_private.h +++ b/drivers/net/ethernet/microchip/vcap/vcap_api_private.h @@ -59,10 +59,6 @@ void vcap_iter_update(struct vcap_stream_iter *itr); /* Keyset and keyfield functionality */ -/* Return the keyset information for the keyset */ -const struct vcap_set *vcap_keyfieldset(struct vcap_control *vctrl, - enum vcap_type vt, - enum vcap_keyfield_set keyset); /* Return the number of keyfields in the keyset */ int vcap_keyfield_count(struct vcap_control *vctrl, enum vcap_type vt, enum vcap_keyfield_set keyset); From 0ca60948487757b1163fd3a13884d2f868eb8ddf Mon Sep 17 00:00:00 2001 From: Steen Hegelund Date: Wed, 23 Nov 2022 16:25:43 +0100 Subject: [PATCH 1235/2002] net: microchip: sparx5: Support for TC protocol all This allows support of TC protocol all for the Sparx5 IS2 VCAP. This is done by creating multiple rules that covers the rule size and traffic types in the IS2. Each rule size (e.g X16 and X6) may have multiple keysets and if there are more than one the type field in the VCAP rule will be wildcarded to support these keysets. Signed-off-by: Steen Hegelund Signed-off-by: David S. Miller --- .../microchip/sparx5/sparx5_tc_flower.c | 209 +++++++++++++++++- .../microchip/sparx5/sparx5_vcap_impl.c | 18 +- .../microchip/sparx5/sparx5_vcap_impl.h | 13 ++ 3 files changed, 234 insertions(+), 6 deletions(-) diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_tc_flower.c b/drivers/net/ethernet/microchip/sparx5/sparx5_tc_flower.c index bd6bd380ba34..1ed304a816cc 100644 --- a/drivers/net/ethernet/microchip/sparx5/sparx5_tc_flower.c +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_tc_flower.c @@ -12,6 +12,20 @@ #include "sparx5_main.h" #include "sparx5_vcap_impl.h" +#define SPX5_MAX_RULE_SIZE 13 /* allows X1, X2, X4, X6 and X12 rules */ + +/* Collect keysets and type ids for multiple rules per size */ +struct sparx5_wildcard_rule { + bool selected; + u8 value; + u8 mask; + enum vcap_keyfield_set keyset; +}; + +struct sparx5_multiple_rules { + struct sparx5_wildcard_rule rule[SPX5_MAX_RULE_SIZE]; +}; + struct sparx5_tc_flower_parse_usage { struct flow_cls_offload *fco; struct flow_rule *frule; @@ -618,7 +632,7 @@ static int sparx5_tc_add_rule_counter(struct vcap_admin *admin, { int err; - err = vcap_rule_add_action_u32(vrule, VCAP_AF_CNT_ID, vrule->id); + err = vcap_rule_mod_action_u32(vrule, VCAP_AF_CNT_ID, vrule->id); if (err) return err; @@ -626,11 +640,190 @@ static int sparx5_tc_add_rule_counter(struct vcap_admin *admin, return err; } +/* Collect all port keysets and apply the first of them, possibly wildcarded */ +static int sparx5_tc_select_protocol_keyset(struct net_device *ndev, + struct vcap_rule *vrule, + struct vcap_admin *admin, + u16 l3_proto, + struct sparx5_multiple_rules *multi) +{ + struct sparx5_port *port = netdev_priv(ndev); + struct vcap_keyset_list portkeysetlist = {}; + enum vcap_keyfield_set portkeysets[10] = {}; + struct vcap_keyset_list matches = {}; + enum vcap_keyfield_set keysets[10]; + int idx, jdx, err = 0, count = 0; + struct sparx5_wildcard_rule *mru; + const struct vcap_set *kinfo; + struct vcap_control *vctrl; + + vctrl = port->sparx5->vcap_ctrl; + + /* Find the keysets that the rule can use */ + matches.keysets = keysets; + matches.max = ARRAY_SIZE(keysets); + if (vcap_rule_find_keysets(vrule, &matches) == 0) + return -EINVAL; + + /* Find the keysets that the port configuration supports */ + portkeysetlist.max = ARRAY_SIZE(portkeysets); + portkeysetlist.keysets = portkeysets; + err = sparx5_vcap_get_port_keyset(ndev, + admin, vrule->vcap_chain_id, + l3_proto, + &portkeysetlist); + if (err) + return err; + + /* Find the intersection of the two sets of keyset */ + for (idx = 0; idx < portkeysetlist.cnt; ++idx) { + kinfo = vcap_keyfieldset(vctrl, admin->vtype, + portkeysetlist.keysets[idx]); + if (!kinfo) + continue; + + /* Find a port keyset that matches the required keys + * If there are multiple keysets then compose a type id mask + */ + for (jdx = 0; jdx < matches.cnt; ++jdx) { + if (portkeysetlist.keysets[idx] != matches.keysets[jdx]) + continue; + + mru = &multi->rule[kinfo->sw_per_item]; + if (!mru->selected) { + mru->selected = true; + mru->keyset = portkeysetlist.keysets[idx]; + mru->value = kinfo->type_id; + } + mru->value &= kinfo->type_id; + mru->mask |= kinfo->type_id; + ++count; + } + } + if (count == 0) + return -EPROTO; + + if (l3_proto == ETH_P_ALL && count < portkeysetlist.cnt) + return -ENOENT; + + for (idx = 0; idx < SPX5_MAX_RULE_SIZE; ++idx) { + mru = &multi->rule[idx]; + if (!mru->selected) + continue; + + /* Align the mask to the combined value */ + mru->mask ^= mru->value; + } + + /* Set the chosen keyset on the rule and set a wildcarded type if there + * are more than one keyset + */ + for (idx = 0; idx < SPX5_MAX_RULE_SIZE; ++idx) { + mru = &multi->rule[idx]; + if (!mru->selected) + continue; + + vcap_set_rule_set_keyset(vrule, mru->keyset); + if (count > 1) + /* Some keysets do not have a type field */ + vcap_rule_mod_key_u32(vrule, VCAP_KF_TYPE, + mru->value, + ~mru->mask); + mru->selected = false; /* mark as done */ + break; /* Stop here and add more rules later */ + } + return err; +} + +static int sparx5_tc_add_rule_copy(struct vcap_control *vctrl, + struct flow_cls_offload *fco, + struct vcap_rule *erule, + struct vcap_admin *admin, + struct sparx5_wildcard_rule *rule) +{ + enum vcap_key_field keylist[] = { + VCAP_KF_IF_IGR_PORT_MASK, + VCAP_KF_IF_IGR_PORT_MASK_SEL, + VCAP_KF_IF_IGR_PORT_MASK_RNG, + VCAP_KF_LOOKUP_FIRST_IS, + VCAP_KF_TYPE, + }; + struct vcap_rule *vrule; + int err; + + /* Add an extra rule with a special user and the new keyset */ + erule->user = VCAP_USER_TC_EXTRA; + vrule = vcap_copy_rule(erule); + if (IS_ERR(vrule)) + return PTR_ERR(vrule); + + /* Link the new rule to the existing rule with the cookie */ + vrule->cookie = erule->cookie; + vcap_filter_rule_keys(vrule, keylist, ARRAY_SIZE(keylist), true); + err = vcap_set_rule_set_keyset(vrule, rule->keyset); + if (err) { + pr_err("%s:%d: could not set keyset %s in rule: %u\n", + __func__, __LINE__, + vcap_keyset_name(vctrl, rule->keyset), + vrule->id); + goto out; + } + + /* Some keysets do not have a type field, so ignore return value */ + vcap_rule_mod_key_u32(vrule, VCAP_KF_TYPE, rule->value, ~rule->mask); + + err = vcap_set_rule_set_actionset(vrule, erule->actionset); + if (err) + goto out; + + err = sparx5_tc_add_rule_counter(admin, vrule); + if (err) + goto out; + + err = vcap_val_rule(vrule, ETH_P_ALL); + if (err) { + pr_err("%s:%d: could not validate rule: %u\n", + __func__, __LINE__, vrule->id); + vcap_set_tc_exterr(fco, vrule); + goto out; + } + err = vcap_add_rule(vrule); + if (err) { + pr_err("%s:%d: could not add rule: %u\n", + __func__, __LINE__, vrule->id); + goto out; + } +out: + vcap_free_rule(vrule); + return err; +} + +static int sparx5_tc_add_remaining_rules(struct vcap_control *vctrl, + struct flow_cls_offload *fco, + struct vcap_rule *erule, + struct vcap_admin *admin, + struct sparx5_multiple_rules *multi) +{ + int idx, err = 0; + + for (idx = 0; idx < SPX5_MAX_RULE_SIZE; ++idx) { + if (!multi->rule[idx].selected) + continue; + + err = sparx5_tc_add_rule_copy(vctrl, fco, erule, admin, + &multi->rule[idx]); + if (err) + break; + } + return err; +} + static int sparx5_tc_flower_replace(struct net_device *ndev, struct flow_cls_offload *fco, struct vcap_admin *admin) { struct sparx5_port *port = netdev_priv(ndev); + struct sparx5_multiple_rules multi = {}; struct flow_action_entry *act; struct vcap_control *vctrl; struct flow_rule *frule; @@ -700,6 +893,15 @@ static int sparx5_tc_flower_replace(struct net_device *ndev, goto out; } } + + err = sparx5_tc_select_protocol_keyset(ndev, vrule, admin, l3_proto, + &multi); + if (err) { + NL_SET_ERR_MSG_MOD(fco->common.extack, + "No matching port keyset for filter protocol and keys"); + goto out; + } + /* provide the l3 protocol to guide the keyset selection */ err = vcap_val_rule(vrule, l3_proto); if (err) { @@ -710,6 +912,11 @@ static int sparx5_tc_flower_replace(struct net_device *ndev, if (err) NL_SET_ERR_MSG_MOD(fco->common.extack, "Could not add the filter"); + + if (l3_proto == ETH_P_ALL) + err = sparx5_tc_add_remaining_rules(vctrl, fco, vrule, admin, + &multi); + out: vcap_free_rule(vrule); return err; diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_vcap_impl.c b/drivers/net/ethernet/microchip/sparx5/sparx5_vcap_impl.c index 0c4d4e6d51e6..a0c126ba9a87 100644 --- a/drivers/net/ethernet/microchip/sparx5/sparx5_vcap_impl.c +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_vcap_impl.c @@ -7,11 +7,6 @@ * https://github.com/microchip-ung/sparx-5_reginfo */ -#include -#include - -#include "vcap_api.h" -#include "vcap_api_client.h" #include "vcap_api_debugfs.h" #include "sparx5_main_regs.h" #include "sparx5_main.h" @@ -279,6 +274,19 @@ static int sparx5_vcap_is2_get_port_keysets(struct net_device *ndev, return 0; } +/* Get the port keyset for the vcap lookup */ +int sparx5_vcap_get_port_keyset(struct net_device *ndev, + struct vcap_admin *admin, + int cid, + u16 l3_proto, + struct vcap_keyset_list *kslist) +{ + int lookup; + + lookup = sparx5_vcap_cid_to_lookup(cid); + return sparx5_vcap_is2_get_port_keysets(ndev, lookup, kslist, l3_proto); +} + /* API callback used for validating a field keyset (check the port keysets) */ static enum vcap_keyfield_set sparx5_vcap_validate_keyset(struct net_device *ndev, diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_vcap_impl.h b/drivers/net/ethernet/microchip/sparx5/sparx5_vcap_impl.h index 8a6b7e3d2618..0a0f2412c980 100644 --- a/drivers/net/ethernet/microchip/sparx5/sparx5_vcap_impl.h +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_vcap_impl.h @@ -10,6 +10,12 @@ #ifndef __SPARX5_VCAP_IMPL_H__ #define __SPARX5_VCAP_IMPL_H__ +#include +#include + +#include "vcap_api.h" +#include "vcap_api_client.h" + #define SPARX5_VCAP_CID_IS2_L0 VCAP_CID_INGRESS_STAGE2_L0 /* IS2 lookup 0 */ #define SPARX5_VCAP_CID_IS2_L1 VCAP_CID_INGRESS_STAGE2_L1 /* IS2 lookup 1 */ #define SPARX5_VCAP_CID_IS2_L2 VCAP_CID_INGRESS_STAGE2_L2 /* IS2 lookup 2 */ @@ -65,4 +71,11 @@ enum vcap_is2_port_sel_arp { VCAP_IS2_PS_ARP_ARP, }; +/* Get the port keyset for the vcap lookup */ +int sparx5_vcap_get_port_keyset(struct net_device *ndev, + struct vcap_admin *admin, + int cid, + u16 l3_proto, + struct vcap_keyset_list *kslist); + #endif /* __SPARX5_VCAP_IMPL_H__ */ From 14b639caa6e4016a10112e0a1f6fcb5fdad4374c Mon Sep 17 00:00:00 2001 From: Steen Hegelund Date: Wed, 23 Nov 2022 16:25:44 +0100 Subject: [PATCH 1236/2002] net: microchip: sparx5: Support for displaying a list of keysets This will display a list of keyset in case the type_id field in the VCAP rule has been wildcarded. Signed-off-by: Steen Hegelund Signed-off-by: David S. Miller --- .../microchip/vcap/vcap_api_debugfs.c | 98 +++++++++++-------- .../microchip/vcap/vcap_api_debugfs_kunit.c | 20 +++- 2 files changed, 74 insertions(+), 44 deletions(-) diff --git a/drivers/net/ethernet/microchip/vcap/vcap_api_debugfs.c b/drivers/net/ethernet/microchip/vcap/vcap_api_debugfs.c index d9c7ca988b76..5df00e940333 100644 --- a/drivers/net/ethernet/microchip/vcap/vcap_api_debugfs.c +++ b/drivers/net/ethernet/microchip/vcap/vcap_api_debugfs.c @@ -192,22 +192,22 @@ static bool vcap_verify_keystream_keyset(struct vcap_control *vctrl, vcap_iter_init(&iter, vcap->sw_width, tgt, typefld->offset); vcap_decode_field(keystream, &iter, typefld->width, (u8 *)&value); - return (value == info->type_id); + return (value & mask) == (info->type_id & mask); } /* Verify that the typegroup information, subword count, keyset and type id - * are in sync and correct, return the keyset + * are in sync and correct, return the list of matching keysets */ -static enum -vcap_keyfield_set vcap_find_keystream_keyset(struct vcap_control *vctrl, - enum vcap_type vt, - u32 *keystream, - u32 *mskstream, - bool mask, int sw_max) +static int +vcap_find_keystream_keysets(struct vcap_control *vctrl, + enum vcap_type vt, + u32 *keystream, + u32 *mskstream, + bool mask, int sw_max, + struct vcap_keyset_list *kslist) { const struct vcap_set *keyfield_set; int sw_count, idx; - bool res; sw_count = vcap_find_keystream_typegroup_sw(vctrl, vt, keystream, mask, sw_max); @@ -219,11 +219,12 @@ vcap_keyfield_set vcap_find_keystream_keyset(struct vcap_control *vctrl, if (keyfield_set[idx].sw_per_item != sw_count) continue; - res = vcap_verify_keystream_keyset(vctrl, vt, keystream, - mskstream, idx); - if (res) - return idx; + if (vcap_verify_keystream_keyset(vctrl, vt, keystream, + mskstream, idx)) + vcap_keyset_list_add(kslist, idx); } + if (kslist->cnt > 0) + return 0; return -EINVAL; } @@ -296,13 +297,14 @@ vcap_find_actionstream_actionset(struct vcap_control *vctrl, return -EINVAL; } -/* Read key data from a VCAP address and discover if there is a rule keyset +/* Read key data from a VCAP address and discover if there are any rule keysets * here */ -static int vcap_addr_keyset(struct vcap_control *vctrl, - struct net_device *ndev, - struct vcap_admin *admin, - int addr) +static int vcap_addr_keysets(struct vcap_control *vctrl, + struct net_device *ndev, + struct vcap_admin *admin, + int addr, + struct vcap_keyset_list *kslist) { enum vcap_type vt = admin->vtype; int keyset_sw_regs, idx; @@ -320,9 +322,10 @@ static int vcap_addr_keyset(struct vcap_control *vctrl, } if (key == 0 && mask == 0) return -EINVAL; - /* Decode and locate the keyset */ - return vcap_find_keystream_keyset(vctrl, vt, admin->cache.keystream, - admin->cache.maskstream, false, 0); + /* Decode and locate the keysets */ + return vcap_find_keystream_keysets(vctrl, vt, admin->cache.keystream, + admin->cache.maskstream, false, 0, + kslist); } static int vcap_read_rule(struct vcap_rule_internal *ri) @@ -471,9 +474,11 @@ static int vcap_debugfs_show_rule_keyset(struct vcap_rule_internal *ri, struct vcap_control *vctrl = ri->vctrl; struct vcap_stream_iter kiter, miter; struct vcap_admin *admin = ri->admin; + enum vcap_keyfield_set keysets[10]; const struct vcap_field *keyfield; enum vcap_type vt = admin->vtype; const struct vcap_typegroup *tgt; + struct vcap_keyset_list matches; enum vcap_keyfield_set keyset; int idx, res, keyfield_count; u32 *maskstream; @@ -483,16 +488,22 @@ static int vcap_debugfs_show_rule_keyset(struct vcap_rule_internal *ri, keystream = admin->cache.keystream; maskstream = admin->cache.maskstream; - res = vcap_find_keystream_keyset(vctrl, vt, keystream, maskstream, - false, 0); + matches.keysets = keysets; + matches.cnt = 0; + matches.max = ARRAY_SIZE(keysets); + res = vcap_find_keystream_keysets(vctrl, vt, keystream, maskstream, + false, 0, &matches); if (res < 0) { - pr_err("%s:%d: could not find valid keyset: %d\n", + pr_err("%s:%d: could not find valid keysets: %d\n", __func__, __LINE__, res); return -EINVAL; } - keyset = res; - out->prf(out->dst, " keyset: %s\n", - vcap_keyset_name(vctrl, ri->data.keyset)); + keyset = matches.keysets[0]; + out->prf(out->dst, " keysets:"); + for (idx = 0; idx < matches.cnt; ++idx) + out->prf(out->dst, " %s", + vcap_keyset_name(vctrl, matches.keysets[idx])); + out->prf(out->dst, "\n"); out->prf(out->dst, " keyset_sw: %d\n", ri->keyset_sw); out->prf(out->dst, " keyset_sw_regs: %d\n", ri->keyset_sw_regs); keyfield_count = vcap_keyfield_count(vctrl, vt, keyset); @@ -647,11 +658,12 @@ static int vcap_show_admin_raw(struct vcap_control *vctrl, struct vcap_admin *admin, struct vcap_output_print *out) { + enum vcap_keyfield_set keysets[10]; enum vcap_type vt = admin->vtype; + struct vcap_keyset_list kslist; struct vcap_rule_internal *ri; const struct vcap_set *info; - int keyset; - int addr; + int addr, idx; int ret; if (list_empty(&admin->rules)) @@ -664,24 +676,32 @@ static int vcap_show_admin_raw(struct vcap_control *vctrl, ri = list_first_entry(&admin->rules, struct vcap_rule_internal, list); /* Go from higher to lower addresses searching for a keyset */ + kslist.keysets = keysets; + kslist.max = ARRAY_SIZE(keysets); for (addr = admin->last_valid_addr; addr >= admin->first_valid_addr; --addr) { - keyset = vcap_addr_keyset(vctrl, ri->ndev, admin, addr); - if (keyset < 0) + kslist.cnt = 0; + ret = vcap_addr_keysets(vctrl, ri->ndev, admin, addr, &kslist); + if (ret < 0) continue; - info = vcap_keyfieldset(vctrl, vt, keyset); + info = vcap_keyfieldset(vctrl, vt, kslist.keysets[0]); if (!info) continue; - if (addr % info->sw_per_item) + if (addr % info->sw_per_item) { pr_info("addr: %d X%d error rule, keyset: %s\n", addr, info->sw_per_item, - vcap_keyset_name(vctrl, keyset)); - else - out->prf(out->dst, " addr: %d, X%d rule, keyset: %s\n", - addr, - info->sw_per_item, - vcap_keyset_name(vctrl, keyset)); + vcap_keyset_name(vctrl, kslist.keysets[0])); + } else { + out->prf(out->dst, " addr: %d, X%d rule, keysets:", + addr, + info->sw_per_item); + for (idx = 0; idx < kslist.cnt; ++idx) + out->prf(out->dst, " %s", + vcap_keyset_name(vctrl, + kslist.keysets[idx])); + out->prf(out->dst, "\n"); + } } return 0; } diff --git a/drivers/net/ethernet/microchip/vcap/vcap_api_debugfs_kunit.c b/drivers/net/ethernet/microchip/vcap/vcap_api_debugfs_kunit.c index ed455dad3a14..cf594668d5d9 100644 --- a/drivers/net/ethernet/microchip/vcap/vcap_api_debugfs_kunit.c +++ b/drivers/net/ethernet/microchip/vcap/vcap_api_debugfs_kunit.c @@ -316,24 +316,34 @@ static void vcap_api_addr_keyset_test(struct kunit *test) .actionstream = actdata, }, }; + enum vcap_keyfield_set keysets[10]; + struct vcap_keyset_list matches; int ret, idx, addr; vcap_test_api_init(&admin); /* Go from higher to lower addresses searching for a keyset */ + matches.keysets = keysets; + matches.cnt = 0; + matches.max = ARRAY_SIZE(keysets); for (idx = ARRAY_SIZE(keydata) - 1, addr = 799; idx > 0; --idx, --addr) { admin.cache.keystream = &keydata[idx]; admin.cache.maskstream = &mskdata[idx]; - ret = vcap_addr_keyset(&test_vctrl, &test_netdev, &admin, addr); + ret = vcap_addr_keysets(&test_vctrl, &test_netdev, &admin, + addr, &matches); KUNIT_EXPECT_EQ(test, -EINVAL, ret); } /* Finally we hit the start of the rule */ admin.cache.keystream = &keydata[idx]; admin.cache.maskstream = &mskdata[idx]; - ret = vcap_addr_keyset(&test_vctrl, &test_netdev, &admin, addr); - KUNIT_EXPECT_EQ(test, VCAP_KFS_MAC_ETYPE, ret); + matches.cnt = 0; + ret = vcap_addr_keysets(&test_vctrl, &test_netdev, &admin, + addr, &matches); + KUNIT_EXPECT_EQ(test, 0, ret); + KUNIT_EXPECT_EQ(test, matches.cnt, 1); + KUNIT_EXPECT_EQ(test, matches.keysets[0], VCAP_KFS_MAC_ETYPE); } static void vcap_api_show_admin_raw_test(struct kunit *test) @@ -362,7 +372,7 @@ static void vcap_api_show_admin_raw_test(struct kunit *test) .prf = (void *)test_prf, }; const char *test_expected = - " addr: 786, X6 rule, keyset: VCAP_KFS_MAC_ETYPE\n"; + " addr: 786, X6 rule, keysets: VCAP_KFS_MAC_ETYPE\n"; int ret; vcap_test_api_init(&admin); @@ -442,7 +452,7 @@ static const char * const test_admin_expect[] = { " chain_id: 0\n", " user: 0\n", " priority: 0\n", - " keyset: VCAP_KFS_MAC_ETYPE\n", + " keysets: VCAP_KFS_MAC_ETYPE\n", " keyset_sw: 6\n", " keyset_sw_regs: 2\n", " ETYPE_LEN_IS: W1: 1/1\n", From 22f3c32572884ece55ed0acf292bef11bc2e6b63 Mon Sep 17 00:00:00 2001 From: Steen Hegelund Date: Wed, 23 Nov 2022 16:25:45 +0100 Subject: [PATCH 1237/2002] net: microchip: sparx5: Add VCAP filter keys KUNIT test This tests the filtering of keys, either dropping unsupported keys or dropping keys specified in a list. Signed-off-by: Steen Hegelund Signed-off-by: David S. Miller --- .../ethernet/microchip/vcap/vcap_api_kunit.c | 194 ++++++++++++++++++ 1 file changed, 194 insertions(+) diff --git a/drivers/net/ethernet/microchip/vcap/vcap_api_kunit.c b/drivers/net/ethernet/microchip/vcap/vcap_api_kunit.c index 875068e484c9..76a31215ebfb 100644 --- a/drivers/net/ethernet/microchip/vcap/vcap_api_kunit.c +++ b/drivers/net/ethernet/microchip/vcap/vcap_api_kunit.c @@ -1954,6 +1954,198 @@ static void vcap_api_next_lookup_advanced_test(struct kunit *test) KUNIT_EXPECT_EQ(test, true, ret); } +static void vcap_api_filter_unsupported_keys_test(struct kunit *test) +{ + struct vcap_admin admin = { + .vtype = VCAP_TYPE_IS2, + }; + struct vcap_rule_internal ri = { + .admin = &admin, + .vctrl = &test_vctrl, + .data.keyset = VCAP_KFS_MAC_ETYPE, + }; + enum vcap_key_field keylist[] = { + VCAP_KF_TYPE, + VCAP_KF_LOOKUP_FIRST_IS, + VCAP_KF_ARP_ADDR_SPACE_OK_IS, /* arp keys are not in keyset */ + VCAP_KF_ARP_PROTO_SPACE_OK_IS, + VCAP_KF_ARP_LEN_OK_IS, + VCAP_KF_ARP_TGT_MATCH_IS, + VCAP_KF_ARP_SENDER_MATCH_IS, + VCAP_KF_ARP_OPCODE_UNKNOWN_IS, + VCAP_KF_ARP_OPCODE, + VCAP_KF_8021Q_DEI_CLS, + VCAP_KF_8021Q_PCP_CLS, + VCAP_KF_8021Q_VID_CLS, + VCAP_KF_L2_MC_IS, + VCAP_KF_L2_BC_IS, + }; + enum vcap_key_field expected[] = { + VCAP_KF_TYPE, + VCAP_KF_LOOKUP_FIRST_IS, + VCAP_KF_8021Q_DEI_CLS, + VCAP_KF_8021Q_PCP_CLS, + VCAP_KF_8021Q_VID_CLS, + VCAP_KF_L2_MC_IS, + VCAP_KF_L2_BC_IS, + }; + struct vcap_client_keyfield *ckf, *next; + bool ret; + int idx; + + /* Add all keys to the rule */ + INIT_LIST_HEAD(&ri.data.keyfields); + for (idx = 0; idx < ARRAY_SIZE(keylist); idx++) { + ckf = kzalloc(sizeof(*ckf), GFP_KERNEL); + if (ckf) { + ckf->ctrl.key = keylist[idx]; + list_add_tail(&ckf->ctrl.list, &ri.data.keyfields); + } + } + + KUNIT_EXPECT_EQ(test, 14, ARRAY_SIZE(keylist)); + + /* Drop unsupported keys from the rule */ + ret = vcap_filter_rule_keys(&ri.data, NULL, 0, true); + + KUNIT_EXPECT_EQ(test, 0, ret); + + /* Check remaining keys in the rule */ + idx = 0; + list_for_each_entry_safe(ckf, next, &ri.data.keyfields, ctrl.list) { + KUNIT_EXPECT_EQ(test, expected[idx], ckf->ctrl.key); + list_del(&ckf->ctrl.list); + kfree(ckf); + ++idx; + } + KUNIT_EXPECT_EQ(test, 7, idx); +} + +static void vcap_api_filter_keylist_test(struct kunit *test) +{ + struct vcap_admin admin = { + .vtype = VCAP_TYPE_IS0, + }; + struct vcap_rule_internal ri = { + .admin = &admin, + .vctrl = &test_vctrl, + .data.keyset = VCAP_KFS_NORMAL_7TUPLE, + }; + enum vcap_key_field keylist[] = { + VCAP_KF_TYPE, + VCAP_KF_LOOKUP_FIRST_IS, + VCAP_KF_LOOKUP_GEN_IDX_SEL, + VCAP_KF_LOOKUP_GEN_IDX, + VCAP_KF_IF_IGR_PORT_MASK_SEL, + VCAP_KF_IF_IGR_PORT_MASK, + VCAP_KF_L2_MC_IS, + VCAP_KF_L2_BC_IS, + VCAP_KF_8021Q_VLAN_TAGS, + VCAP_KF_8021Q_TPID0, + VCAP_KF_8021Q_PCP0, + VCAP_KF_8021Q_DEI0, + VCAP_KF_8021Q_VID0, + VCAP_KF_8021Q_TPID1, + VCAP_KF_8021Q_PCP1, + VCAP_KF_8021Q_DEI1, + VCAP_KF_8021Q_VID1, + VCAP_KF_8021Q_TPID2, + VCAP_KF_8021Q_PCP2, + VCAP_KF_8021Q_DEI2, + VCAP_KF_8021Q_VID2, + VCAP_KF_L2_DMAC, + VCAP_KF_L2_SMAC, + VCAP_KF_IP_MC_IS, + VCAP_KF_ETYPE_LEN_IS, + VCAP_KF_ETYPE, + VCAP_KF_IP_SNAP_IS, + VCAP_KF_IP4_IS, + VCAP_KF_L3_FRAGMENT_TYPE, + VCAP_KF_L3_FRAG_INVLD_L4_LEN, + VCAP_KF_L3_OPTIONS_IS, + VCAP_KF_L3_DSCP, + VCAP_KF_L3_IP6_DIP, + VCAP_KF_L3_IP6_SIP, + VCAP_KF_TCP_UDP_IS, + VCAP_KF_TCP_IS, + VCAP_KF_L4_SPORT, + VCAP_KF_L4_RNG, + }; + enum vcap_key_field droplist[] = { + VCAP_KF_8021Q_TPID1, + VCAP_KF_8021Q_PCP1, + VCAP_KF_8021Q_DEI1, + VCAP_KF_8021Q_VID1, + VCAP_KF_8021Q_TPID2, + VCAP_KF_8021Q_PCP2, + VCAP_KF_8021Q_DEI2, + VCAP_KF_8021Q_VID2, + VCAP_KF_L3_IP6_DIP, + VCAP_KF_L3_IP6_SIP, + VCAP_KF_L4_SPORT, + VCAP_KF_L4_RNG, + }; + enum vcap_key_field expected[] = { + VCAP_KF_TYPE, + VCAP_KF_LOOKUP_FIRST_IS, + VCAP_KF_LOOKUP_GEN_IDX_SEL, + VCAP_KF_LOOKUP_GEN_IDX, + VCAP_KF_IF_IGR_PORT_MASK_SEL, + VCAP_KF_IF_IGR_PORT_MASK, + VCAP_KF_L2_MC_IS, + VCAP_KF_L2_BC_IS, + VCAP_KF_8021Q_VLAN_TAGS, + VCAP_KF_8021Q_TPID0, + VCAP_KF_8021Q_PCP0, + VCAP_KF_8021Q_DEI0, + VCAP_KF_8021Q_VID0, + VCAP_KF_L2_DMAC, + VCAP_KF_L2_SMAC, + VCAP_KF_IP_MC_IS, + VCAP_KF_ETYPE_LEN_IS, + VCAP_KF_ETYPE, + VCAP_KF_IP_SNAP_IS, + VCAP_KF_IP4_IS, + VCAP_KF_L3_FRAGMENT_TYPE, + VCAP_KF_L3_FRAG_INVLD_L4_LEN, + VCAP_KF_L3_OPTIONS_IS, + VCAP_KF_L3_DSCP, + VCAP_KF_TCP_UDP_IS, + VCAP_KF_TCP_IS, + }; + struct vcap_client_keyfield *ckf, *next; + bool ret; + int idx; + + /* Add all keys to the rule */ + INIT_LIST_HEAD(&ri.data.keyfields); + for (idx = 0; idx < ARRAY_SIZE(keylist); idx++) { + ckf = kzalloc(sizeof(*ckf), GFP_KERNEL); + if (ckf) { + ckf->ctrl.key = keylist[idx]; + list_add_tail(&ckf->ctrl.list, &ri.data.keyfields); + } + } + + KUNIT_EXPECT_EQ(test, 38, ARRAY_SIZE(keylist)); + + /* Drop listed keys from the rule */ + ret = vcap_filter_rule_keys(&ri.data, droplist, ARRAY_SIZE(droplist), + false); + + KUNIT_EXPECT_EQ(test, 0, ret); + + /* Check remaining keys in the rule */ + idx = 0; + list_for_each_entry_safe(ckf, next, &ri.data.keyfields, ctrl.list) { + KUNIT_EXPECT_EQ(test, expected[idx], ckf->ctrl.key); + list_del(&ckf->ctrl.list); + kfree(ckf); + ++idx; + } + KUNIT_EXPECT_EQ(test, 26, idx); +} + static struct kunit_suite vcap_api_rule_remove_test_suite = { .name = "VCAP_API_Rule_Remove_Testsuite", .test_cases = vcap_api_rule_remove_test_cases, @@ -1984,6 +2176,8 @@ static struct kunit_suite vcap_api_rule_counter_test_suite = { static struct kunit_case vcap_api_support_test_cases[] = { KUNIT_CASE(vcap_api_next_lookup_basic_test), KUNIT_CASE(vcap_api_next_lookup_advanced_test), + KUNIT_CASE(vcap_api_filter_unsupported_keys_test), + KUNIT_CASE(vcap_api_filter_keylist_test), {} }; From ad3cc7760dc45f30a772b312a01f965d6e74d36b Mon Sep 17 00:00:00 2001 From: Min Li Date: Wed, 23 Nov 2022 14:52:06 -0500 Subject: [PATCH 1238/2002] ptp: idt82p33: Add PTP_CLK_REQ_EXTTS support 82P33 family of chips can trigger TOD read/write by external signal from one of the IN12/13/14 pins, which are set user space programs by calling PTP_PIN_SETFUNC through ptp_ioctl Signed-off-by: Min Li Signed-off-by: David S. Miller --- drivers/ptp/ptp_idt82p33.c | 685 +++++++++++++++++++++++++++++++++---- drivers/ptp/ptp_idt82p33.h | 20 +- 2 files changed, 641 insertions(+), 64 deletions(-) diff --git a/drivers/ptp/ptp_idt82p33.c b/drivers/ptp/ptp_idt82p33.c index 97c1be44e323..aece499c26d4 100644 --- a/drivers/ptp/ptp_idt82p33.c +++ b/drivers/ptp/ptp_idt82p33.c @@ -27,6 +27,8 @@ MODULE_VERSION("1.0"); MODULE_LICENSE("GPL"); MODULE_FIRMWARE(FW_FILENAME); +#define EXTTS_PERIOD_MS (95) + /* Module Parameters */ static u32 phase_snap_threshold = SNAP_THRESHOLD_NS; module_param(phase_snap_threshold, uint, 0); @@ -36,6 +38,8 @@ MODULE_PARM_DESC(phase_snap_threshold, static char *firmware; module_param(firmware, charp, 0); +static struct ptp_pin_desc pin_config[MAX_PHC_PLL][MAX_TRIG_CLK]; + static inline int idt82p33_read(struct idt82p33 *idt82p33, u16 regaddr, u8 *buf, u16 count) { @@ -121,24 +125,270 @@ static int idt82p33_dpll_set_mode(struct idt82p33_channel *channel, return 0; } +static int idt82p33_set_tod_trigger(struct idt82p33_channel *channel, + u8 trigger, bool write) +{ + struct idt82p33 *idt82p33 = channel->idt82p33; + int err; + u8 cfg; + + if (trigger > WR_TRIG_SEL_MAX) + return -EINVAL; + + err = idt82p33_read(idt82p33, channel->dpll_tod_trigger, + &cfg, sizeof(cfg)); + + if (err) + return err; + + if (write == true) + trigger = (trigger << WRITE_TRIGGER_SHIFT) | + (cfg & READ_TRIGGER_MASK); + else + trigger = (trigger << READ_TRIGGER_SHIFT) | + (cfg & WRITE_TRIGGER_MASK); + + return idt82p33_write(idt82p33, channel->dpll_tod_trigger, + &trigger, sizeof(trigger)); +} + +static int idt82p33_get_extts(struct idt82p33_channel *channel, + struct timespec64 *ts) +{ + struct idt82p33 *idt82p33 = channel->idt82p33; + u8 buf[TOD_BYTE_COUNT]; + int err; + + err = idt82p33_read(idt82p33, channel->dpll_tod_sts, buf, sizeof(buf)); + + if (err) + return err; + + /* Since trigger is not self clearing itself, we have to poll tod_sts */ + if (memcmp(buf, channel->extts_tod_sts, TOD_BYTE_COUNT) == 0) + return -EAGAIN; + + memcpy(channel->extts_tod_sts, buf, TOD_BYTE_COUNT); + + idt82p33_byte_array_to_timespec(ts, buf); + + if (channel->discard_next_extts) { + channel->discard_next_extts = false; + return -EAGAIN; + } + + return 0; +} + +static int map_ref_to_tod_trig_sel(int ref, u8 *trigger) +{ + int err = 0; + + switch (ref) { + case 0: + *trigger = HW_TOD_TRIG_SEL_IN12; + break; + case 1: + *trigger = HW_TOD_TRIG_SEL_IN13; + break; + case 2: + *trigger = HW_TOD_TRIG_SEL_IN14; + break; + default: + err = -EINVAL; + } + + return err; +} + +static bool is_one_shot(u8 mask) +{ + /* Treat single bit PLL masks as continuous trigger */ + if ((mask == 1) || (mask == 2)) + return false; + else + return true; +} + +static int arm_tod_read_with_trigger(struct idt82p33_channel *channel, u8 trigger) +{ + struct idt82p33 *idt82p33 = channel->idt82p33; + u8 buf[TOD_BYTE_COUNT]; + int err; + + /* Remember the current tod_sts before setting the trigger */ + err = idt82p33_read(idt82p33, channel->dpll_tod_sts, buf, sizeof(buf)); + + if (err) + return err; + + memcpy(channel->extts_tod_sts, buf, TOD_BYTE_COUNT); + + err = idt82p33_set_tod_trigger(channel, trigger, false); + + if (err) + dev_err(idt82p33->dev, "%s: err = %d", __func__, err); + + return err; +} + +static int idt82p33_extts_enable(struct idt82p33_channel *channel, + struct ptp_clock_request *rq, int on) +{ + u8 index = rq->extts.index; + struct idt82p33 *idt82p33; + u8 mask = 1 << index; + int err = 0; + u8 old_mask; + u8 trigger; + int ref; + + idt82p33 = channel->idt82p33; + old_mask = idt82p33->extts_mask; + + /* Reject requests with unsupported flags */ + if (rq->extts.flags & ~(PTP_ENABLE_FEATURE | + PTP_RISING_EDGE | + PTP_FALLING_EDGE | + PTP_STRICT_FLAGS)) + return -EOPNOTSUPP; + + /* Reject requests to enable time stamping on falling edge */ + if ((rq->extts.flags & PTP_ENABLE_FEATURE) && + (rq->extts.flags & PTP_FALLING_EDGE)) + return -EOPNOTSUPP; + + if (index >= MAX_PHC_PLL) + return -EINVAL; + + if (on) { + /* Return if it was already enabled */ + if (idt82p33->extts_mask & mask) + return 0; + + /* Use the pin configured for the channel */ + ref = ptp_find_pin(channel->ptp_clock, PTP_PF_EXTTS, channel->plln); + + if (ref < 0) { + dev_err(idt82p33->dev, "%s: No valid pin found for Pll%d!\n", + __func__, channel->plln); + return -EBUSY; + } + + err = map_ref_to_tod_trig_sel(ref, &trigger); + + if (err) { + dev_err(idt82p33->dev, + "%s: Unsupported ref %d!\n", __func__, ref); + return err; + } + + err = arm_tod_read_with_trigger(&idt82p33->channel[index], trigger); + + if (err == 0) { + idt82p33->extts_mask |= mask; + idt82p33->channel[index].tod_trigger = trigger; + idt82p33->event_channel[index] = channel; + idt82p33->extts_single_shot = is_one_shot(idt82p33->extts_mask); + + if (old_mask) + return 0; + + schedule_delayed_work(&idt82p33->extts_work, + msecs_to_jiffies(EXTTS_PERIOD_MS)); + } + } else { + idt82p33->extts_mask &= ~mask; + idt82p33->extts_single_shot = is_one_shot(idt82p33->extts_mask); + + if (idt82p33->extts_mask == 0) + cancel_delayed_work(&idt82p33->extts_work); + } + + return err; +} + +static int idt82p33_extts_check_channel(struct idt82p33 *idt82p33, u8 todn) +{ + struct idt82p33_channel *event_channel; + struct ptp_clock_event event; + struct timespec64 ts; + int err; + + err = idt82p33_get_extts(&idt82p33->channel[todn], &ts); + if (err == 0) { + event_channel = idt82p33->event_channel[todn]; + event.type = PTP_CLOCK_EXTTS; + event.index = todn; + event.timestamp = timespec64_to_ns(&ts); + ptp_clock_event(event_channel->ptp_clock, + &event); + } + return err; +} + +static u8 idt82p33_extts_enable_mask(struct idt82p33_channel *channel, + u8 extts_mask, bool enable) +{ + struct idt82p33 *idt82p33 = channel->idt82p33; + u8 trigger = channel->tod_trigger; + u8 mask; + int err; + int i; + + if (extts_mask == 0) + return 0; + + if (enable == false) + cancel_delayed_work_sync(&idt82p33->extts_work); + + for (i = 0; i < MAX_PHC_PLL; i++) { + mask = 1 << i; + + if ((extts_mask & mask) == 0) + continue; + + if (enable) { + err = arm_tod_read_with_trigger(&idt82p33->channel[i], trigger); + if (err) + dev_err(idt82p33->dev, + "%s: Arm ToD read trigger failed, err = %d", + __func__, err); + } else { + err = idt82p33_extts_check_channel(idt82p33, i); + if (err == 0 && idt82p33->extts_single_shot) + /* trigger happened so we won't re-enable it */ + extts_mask &= ~mask; + } + } + + if (enable) + schedule_delayed_work(&idt82p33->extts_work, + msecs_to_jiffies(EXTTS_PERIOD_MS)); + + return extts_mask; +} + static int _idt82p33_gettime(struct idt82p33_channel *channel, struct timespec64 *ts) { struct idt82p33 *idt82p33 = channel->idt82p33; + u8 old_mask = idt82p33->extts_mask; u8 buf[TOD_BYTE_COUNT]; - u8 trigger; + u8 new_mask = 0; int err; - trigger = TOD_TRIGGER(HW_TOD_WR_TRIG_SEL_MSB_TOD_CNFG, - HW_TOD_RD_TRIG_SEL_LSB_TOD_STS); - - - err = idt82p33_write(idt82p33, channel->dpll_tod_trigger, - &trigger, sizeof(trigger)); + /* Disable extts */ + if (old_mask) + new_mask = idt82p33_extts_enable_mask(channel, old_mask, false); + err = idt82p33_set_tod_trigger(channel, HW_TOD_RD_TRIG_SEL_LSB_TOD_STS, + false); if (err) return err; + channel->discard_next_extts = true; + if (idt82p33->calculate_overhead_flag) idt82p33->start_time = ktime_get_raw(); @@ -147,6 +397,10 @@ static int _idt82p33_gettime(struct idt82p33_channel *channel, if (err) return err; + /* Re-enable extts */ + if (new_mask) + idt82p33_extts_enable_mask(channel, new_mask, true); + idt82p33_byte_array_to_timespec(ts, buf); return 0; @@ -165,19 +419,16 @@ static int _idt82p33_settime(struct idt82p33_channel *channel, struct timespec64 local_ts = *ts; char buf[TOD_BYTE_COUNT]; s64 dynamic_overhead_ns; - unsigned char trigger; int err; u8 i; - trigger = TOD_TRIGGER(HW_TOD_WR_TRIG_SEL_MSB_TOD_CNFG, - HW_TOD_RD_TRIG_SEL_LSB_TOD_STS); - - err = idt82p33_write(idt82p33, channel->dpll_tod_trigger, - &trigger, sizeof(trigger)); - + err = idt82p33_set_tod_trigger(channel, HW_TOD_WR_TRIG_SEL_MSB_TOD_CNFG, + true); if (err) return err; + channel->discard_next_extts = true; + if (idt82p33->calculate_overhead_flag) { dynamic_overhead_ns = ktime_to_ns(ktime_get_raw()) - ktime_to_ns(idt82p33->start_time); @@ -202,7 +453,8 @@ static int _idt82p33_settime(struct idt82p33_channel *channel, return err; } -static int _idt82p33_adjtime(struct idt82p33_channel *channel, s64 delta_ns) +static int _idt82p33_adjtime_immediate(struct idt82p33_channel *channel, + s64 delta_ns) { struct idt82p33 *idt82p33 = channel->idt82p33; struct timespec64 ts; @@ -226,6 +478,60 @@ static int _idt82p33_adjtime(struct idt82p33_channel *channel, s64 delta_ns) return err; } +static int _idt82p33_adjtime_internal_triggered(struct idt82p33_channel *channel, + s64 delta_ns) +{ + struct idt82p33 *idt82p33 = channel->idt82p33; + char buf[TOD_BYTE_COUNT]; + struct timespec64 ts; + const u8 delay_ns = 32; + s32 remainder; + s64 ns; + int err; + + err = _idt82p33_gettime(channel, &ts); + + if (err) + return err; + + if (ts.tv_nsec > (NSEC_PER_SEC - 5 * NSEC_PER_MSEC)) { + /* Too close to miss next trigger, so skip it */ + mdelay(6); + ns = (ts.tv_sec + 2) * NSEC_PER_SEC + delta_ns + delay_ns; + } else + ns = (ts.tv_sec + 1) * NSEC_PER_SEC + delta_ns + delay_ns; + + ts = ns_to_timespec64(ns); + idt82p33_timespec_to_byte_array(&ts, buf); + + /* + * Store the new time value. + */ + err = idt82p33_write(idt82p33, channel->dpll_tod_cnfg, buf, sizeof(buf)); + if (err) + return err; + + /* Schedule to implement the workaround in one second */ + (void)div_s64_rem(delta_ns, NSEC_PER_SEC, &remainder); + if (remainder != 0) + schedule_delayed_work(&channel->adjtime_work, HZ); + + return idt82p33_set_tod_trigger(channel, HW_TOD_TRIG_SEL_TOD_PPS, true); +} + +static void idt82p33_adjtime_workaround(struct work_struct *work) +{ + struct idt82p33_channel *channel = container_of(work, + struct idt82p33_channel, + adjtime_work.work); + struct idt82p33 *idt82p33 = channel->idt82p33; + + mutex_lock(idt82p33->lock); + /* Workaround for TOD-to-output alignment issue */ + _idt82p33_adjtime_internal_triggered(channel, 0); + mutex_unlock(idt82p33->lock); +} + static int _idt82p33_adjfine(struct idt82p33_channel *channel, long scaled_ppm) { struct idt82p33 *idt82p33 = channel->idt82p33; @@ -233,25 +539,22 @@ static int _idt82p33_adjfine(struct idt82p33_channel *channel, long scaled_ppm) int err, i; s64 fcw; - if (scaled_ppm == channel->current_freq_ppb) - return 0; - /* - * Frequency Control Word unit is: 1.68 * 10^-10 ppm + * Frequency Control Word unit is: 1.6861512 * 10^-10 ppm * * adjfreq: - * ppb * 10^9 - * FCW = ---------- - * 168 + * ppb * 10^14 + * FCW = ----------- + * 16861512 * * adjfine: - * scaled_ppm * 5^12 - * FCW = ------------- - * 168 * 2^4 + * scaled_ppm * 5^12 * 10^5 + * FCW = ------------------------ + * 16861512 * 2^4 */ - fcw = scaled_ppm * 244140625ULL; - fcw = div_s64(fcw, 2688); + fcw = scaled_ppm * 762939453125ULL; + fcw = div_s64(fcw, 8430756LL); for (i = 0; i < 5; i++) { buf[i] = fcw & 0xff; @@ -266,26 +569,84 @@ static int _idt82p33_adjfine(struct idt82p33_channel *channel, long scaled_ppm) err = idt82p33_write(idt82p33, channel->dpll_freq_cnfg, buf, sizeof(buf)); - if (err == 0) - channel->current_freq_ppb = scaled_ppm; - return err; } +/* ppb = scaled_ppm * 125 / 2^13 */ +static s32 idt82p33_ddco_scaled_ppm(long current_ppm, s32 ddco_ppb) +{ + s64 scaled_ppm = div_s64(((s64)ddco_ppb << 13), 125); + s64 max_scaled_ppm = div_s64(((s64)DCO_MAX_PPB << 13), 125); + + current_ppm += scaled_ppm; + + if (current_ppm > max_scaled_ppm) + current_ppm = max_scaled_ppm; + else if (current_ppm < -max_scaled_ppm) + current_ppm = -max_scaled_ppm; + + return (s32)current_ppm; +} + +static int idt82p33_stop_ddco(struct idt82p33_channel *channel) +{ + int err; + + err = _idt82p33_adjfine(channel, channel->current_freq); + if (err) + return err; + + channel->ddco = false; + + return 0; +} + +static int idt82p33_start_ddco(struct idt82p33_channel *channel, s32 delta_ns) +{ + s32 current_ppm = channel->current_freq; + u32 duration_ms = MSEC_PER_SEC; + s32 ppb; + int err; + + /* If the ToD correction is less than 5 nanoseconds, then skip it. + * The error introduced by the ToD adjustment procedure would be bigger + * than the required ToD correction + */ + if (abs(delta_ns) < DDCO_THRESHOLD_NS) + return 0; + + /* For most cases, keep ddco duration 1 second */ + ppb = delta_ns; + while (abs(ppb) > DCO_MAX_PPB) { + duration_ms *= 2; + ppb /= 2; + } + + err = _idt82p33_adjfine(channel, + idt82p33_ddco_scaled_ppm(current_ppm, ppb)); + if (err) + return err; + + /* schedule the worker to cancel ddco */ + ptp_schedule_worker(channel->ptp_clock, + msecs_to_jiffies(duration_ms) - 1); + channel->ddco = true; + + return 0; +} + static int idt82p33_measure_one_byte_write_overhead( struct idt82p33_channel *channel, s64 *overhead_ns) { struct idt82p33 *idt82p33 = channel->idt82p33; ktime_t start, stop; + u8 trigger = 0; s64 total_ns; - u8 trigger; int err; u8 i; total_ns = 0; *overhead_ns = 0; - trigger = TOD_TRIGGER(HW_TOD_WR_TRIG_SEL_MSB_TOD_CNFG, - HW_TOD_RD_TRIG_SEL_LSB_TOD_STS); for (i = 0; i < MAX_MEASURMENT_COUNT; i++) { @@ -307,8 +668,41 @@ static int idt82p33_measure_one_byte_write_overhead( return err; } +static int idt82p33_measure_one_byte_read_overhead( + struct idt82p33_channel *channel, s64 *overhead_ns) +{ + struct idt82p33 *idt82p33 = channel->idt82p33; + ktime_t start, stop; + u8 trigger = 0; + s64 total_ns; + int err; + u8 i; + + total_ns = 0; + *overhead_ns = 0; + + for (i = 0; i < MAX_MEASURMENT_COUNT; i++) { + + start = ktime_get_raw(); + + err = idt82p33_read(idt82p33, channel->dpll_tod_trigger, + &trigger, sizeof(trigger)); + + stop = ktime_get_raw(); + + if (err) + return err; + + total_ns += ktime_to_ns(stop) - ktime_to_ns(start); + } + + *overhead_ns = div_s64(total_ns, MAX_MEASURMENT_COUNT); + + return err; +} + static int idt82p33_measure_tod_write_9_byte_overhead( - struct idt82p33_channel *channel) + struct idt82p33_channel *channel) { struct idt82p33 *idt82p33 = channel->idt82p33; u8 buf[TOD_BYTE_COUNT]; @@ -368,7 +762,7 @@ static int idt82p33_measure_settime_gettime_gap_overhead( static int idt82p33_measure_tod_write_overhead(struct idt82p33_channel *channel) { - s64 trailing_overhead_ns, one_byte_write_ns, gap_ns; + s64 trailing_overhead_ns, one_byte_write_ns, gap_ns, one_byte_read_ns; struct idt82p33 *idt82p33 = channel->idt82p33; int err; @@ -388,12 +782,19 @@ static int idt82p33_measure_tod_write_overhead(struct idt82p33_channel *channel) if (err) return err; + err = idt82p33_measure_one_byte_read_overhead(channel, + &one_byte_read_ns); + + if (err) + return err; + err = idt82p33_measure_tod_write_9_byte_overhead(channel); if (err) return err; - trailing_overhead_ns = gap_ns - (2 * one_byte_write_ns); + trailing_overhead_ns = gap_ns - 2 * one_byte_write_ns + - one_byte_read_ns; idt82p33->tod_write_overhead_ns -= trailing_overhead_ns; @@ -462,6 +863,20 @@ static int idt82p33_sync_tod(struct idt82p33_channel *channel, bool enable) &sync_cnfg, sizeof(sync_cnfg)); } +static long idt82p33_work_handler(struct ptp_clock_info *ptp) +{ + struct idt82p33_channel *channel = + container_of(ptp, struct idt82p33_channel, caps); + struct idt82p33 *idt82p33 = channel->idt82p33; + + mutex_lock(idt82p33->lock); + (void)idt82p33_stop_ddco(channel); + mutex_unlock(idt82p33->lock); + + /* Return a negative value here to not reschedule */ + return -1; +} + static int idt82p33_output_enable(struct idt82p33_channel *channel, bool enable, unsigned int outn) { @@ -524,6 +939,10 @@ static int idt82p33_enable_tod(struct idt82p33_channel *channel) struct timespec64 ts = {0, 0}; int err; + /* STEELAI-366 - Temporary workaround for ts2phc compatibility */ + if (0) + err = idt82p33_output_mask_enable(channel, false); + err = idt82p33_measure_tod_write_overhead(channel); if (err) { @@ -546,14 +965,15 @@ static void idt82p33_ptp_clock_unregister_all(struct idt82p33 *idt82p33) u8 i; for (i = 0; i < MAX_PHC_PLL; i++) { - channel = &idt82p33->channel[i]; - + cancel_delayed_work_sync(&channel->adjtime_work); if (channel->ptp_clock) ptp_clock_unregister(channel->ptp_clock); } } + + static int idt82p33_enable(struct ptp_clock_info *ptp, struct ptp_clock_request *rq, int on) { @@ -564,7 +984,8 @@ static int idt82p33_enable(struct ptp_clock_info *ptp, mutex_lock(idt82p33->lock); - if (rq->type == PTP_CLK_REQ_PEROUT) { + switch (rq->type) { + case PTP_CLK_REQ_PEROUT: if (!on) err = idt82p33_perout_enable(channel, false, &rq->perout); @@ -575,6 +996,12 @@ static int idt82p33_enable(struct ptp_clock_info *ptp, else err = idt82p33_perout_enable(channel, true, &rq->perout); + break; + case PTP_CLK_REQ_EXTTS: + err = idt82p33_extts_enable(channel, rq, on); + break; + default: + break; } mutex_unlock(idt82p33->lock); @@ -634,13 +1061,22 @@ static int idt82p33_adjfine(struct ptp_clock_info *ptp, long scaled_ppm) struct idt82p33 *idt82p33 = channel->idt82p33; int err; + if (channel->ddco == true) + return 0; + + if (scaled_ppm == channel->current_freq) + return 0; + mutex_lock(idt82p33->lock); err = _idt82p33_adjfine(channel, scaled_ppm); + + if (err == 0) + channel->current_freq = scaled_ppm; mutex_unlock(idt82p33->lock); + if (err) dev_err(idt82p33->dev, "Failed in %s with err %d!\n", __func__, err); - return err; } @@ -651,14 +1087,21 @@ static int idt82p33_adjtime(struct ptp_clock_info *ptp, s64 delta_ns) struct idt82p33 *idt82p33 = channel->idt82p33; int err; + if (channel->ddco == true) + return -EBUSY; + mutex_lock(idt82p33->lock); if (abs(delta_ns) < phase_snap_threshold) { + err = idt82p33_start_ddco(channel, delta_ns); mutex_unlock(idt82p33->lock); - return 0; + return err; } - err = _idt82p33_adjtime(channel, delta_ns); + /* Use more accurate internal 1pps triggered write first */ + err = _idt82p33_adjtime_internal_triggered(channel, delta_ns); + if (err && delta_ns > IMMEDIATE_SNAP_THRESHOLD_NS) + err = _idt82p33_adjtime_immediate(channel, delta_ns); mutex_unlock(idt82p33->lock); @@ -703,8 +1146,10 @@ static int idt82p33_settime(struct ptp_clock_info *ptp, return err; } -static int idt82p33_channel_init(struct idt82p33_channel *channel, int index) +static int idt82p33_channel_init(struct idt82p33 *idt82p33, u32 index) { + struct idt82p33_channel *channel = &idt82p33->channel[index]; + switch (index) { case 0: channel->dpll_tod_cnfg = DPLL1_TOD_CNFG; @@ -730,22 +1175,60 @@ static int idt82p33_channel_init(struct idt82p33_channel *channel, int index) return -EINVAL; } - channel->current_freq_ppb = 0; + channel->plln = index; + channel->current_freq = 0; + channel->idt82p33 = idt82p33; + INIT_DELAYED_WORK(&channel->adjtime_work, idt82p33_adjtime_workaround); return 0; } -static void idt82p33_caps_init(struct ptp_clock_info *caps) +static int idt82p33_verify_pin(struct ptp_clock_info *ptp, unsigned int pin, + enum ptp_pin_function func, unsigned int chan) { + switch (func) { + case PTP_PF_NONE: + case PTP_PF_EXTTS: + break; + case PTP_PF_PEROUT: + case PTP_PF_PHYSYNC: + return -1; + } + return 0; +} + +static void idt82p33_caps_init(u32 index, struct ptp_clock_info *caps, + struct ptp_pin_desc *pin_cfg, u8 max_pins) +{ + struct ptp_pin_desc *ppd; + int i; + caps->owner = THIS_MODULE; caps->max_adj = DCO_MAX_PPB; - caps->n_per_out = 11; - caps->adjphase = idt82p33_adjwritephase; + caps->n_per_out = MAX_PER_OUT; + caps->n_ext_ts = MAX_PHC_PLL, + caps->n_pins = max_pins, + caps->adjphase = idt82p33_adjwritephase, caps->adjfine = idt82p33_adjfine; caps->adjtime = idt82p33_adjtime; caps->gettime64 = idt82p33_gettime; caps->settime64 = idt82p33_settime; caps->enable = idt82p33_enable; + caps->verify = idt82p33_verify_pin; + caps->do_aux_work = idt82p33_work_handler; + + snprintf(caps->name, sizeof(caps->name), "IDT 82P33 PLL%u", index); + + caps->pin_config = pin_cfg; + + for (i = 0; i < max_pins; ++i) { + ppd = &pin_cfg[i]; + + ppd->index = i; + ppd->func = PTP_PF_NONE; + ppd->chan = index; + snprintf(ppd->name, sizeof(ppd->name), "in%d", 12 + i); + } } static int idt82p33_enable_channel(struct idt82p33 *idt82p33, u32 index) @@ -758,7 +1241,7 @@ static int idt82p33_enable_channel(struct idt82p33 *idt82p33, u32 index) channel = &idt82p33->channel[index]; - err = idt82p33_channel_init(channel, index); + err = idt82p33_channel_init(idt82p33, index); if (err) { dev_err(idt82p33->dev, "Channel_init failed in %s with err %d!\n", @@ -766,11 +1249,8 @@ static int idt82p33_enable_channel(struct idt82p33 *idt82p33, u32 index) return err; } - channel->idt82p33 = idt82p33; - - idt82p33_caps_init(&channel->caps); - snprintf(channel->caps.name, sizeof(channel->caps.name), - "IDT 82P33 PLL%u", index); + idt82p33_caps_init(index, &channel->caps, + pin_config[index], MAX_TRIG_CLK); channel->ptp_clock = ptp_clock_register(&channel->caps, NULL); @@ -805,17 +1285,46 @@ static int idt82p33_enable_channel(struct idt82p33 *idt82p33, u32 index) return 0; } +static int idt82p33_reset(struct idt82p33 *idt82p33, bool cold) +{ + int err; + u8 cfg = SOFT_RESET_EN; + + if (cold == true) + goto cold_reset; + + err = idt82p33_read(idt82p33, REG_SOFT_RESET, &cfg, sizeof(cfg)); + if (err) { + dev_err(idt82p33->dev, + "Soft reset failed with err %d!\n", err); + return err; + } + + cfg |= SOFT_RESET_EN; + +cold_reset: + err = idt82p33_write(idt82p33, REG_SOFT_RESET, &cfg, sizeof(cfg)); + if (err) + dev_err(idt82p33->dev, + "Cold reset failed with err %d!\n", err); + return err; +} + static int idt82p33_load_firmware(struct idt82p33 *idt82p33) { + char fname[128] = FW_FILENAME; const struct firmware *fw; struct idt82p33_fwrc *rec; u8 loaddr, page, val; int err; s32 len; - dev_dbg(idt82p33->dev, "requesting firmware '%s'\n", FW_FILENAME); + if (firmware) /* module parameter */ + snprintf(fname, sizeof(fname), "%s", firmware); - err = request_firmware(&fw, FW_FILENAME, idt82p33->dev); + dev_info(idt82p33->dev, "requesting firmware '%s'\n", fname); + + err = request_firmware(&fw, fname, idt82p33->dev); if (err) { dev_err(idt82p33->dev, @@ -863,6 +1372,46 @@ out: return err; } +static void idt82p33_extts_check(struct work_struct *work) +{ + struct idt82p33 *idt82p33 = container_of(work, struct idt82p33, + extts_work.work); + struct idt82p33_channel *channel; + int err; + u8 mask; + int i; + + if (idt82p33->extts_mask == 0) + return; + + mutex_lock(idt82p33->lock); + + for (i = 0; i < MAX_PHC_PLL; i++) { + mask = 1 << i; + + if ((idt82p33->extts_mask & mask) == 0) + continue; + + err = idt82p33_extts_check_channel(idt82p33, i); + + if (err == 0) { + /* trigger clears itself, so clear the mask */ + if (idt82p33->extts_single_shot) { + idt82p33->extts_mask &= ~mask; + } else { + /* Re-arm */ + channel = &idt82p33->channel[i]; + arm_tod_read_with_trigger(channel, channel->tod_trigger); + } + } + } + + if (idt82p33->extts_mask) + schedule_delayed_work(&idt82p33->extts_work, + msecs_to_jiffies(EXTTS_PERIOD_MS)); + + mutex_unlock(idt82p33->lock); +} static int idt82p33_probe(struct platform_device *pdev) { @@ -885,25 +1434,33 @@ static int idt82p33_probe(struct platform_device *pdev) idt82p33->pll_mask = DEFAULT_PLL_MASK; idt82p33->channel[0].output_mask = DEFAULT_OUTPUT_MASK_PLL0; idt82p33->channel[1].output_mask = DEFAULT_OUTPUT_MASK_PLL1; + idt82p33->extts_mask = 0; + INIT_DELAYED_WORK(&idt82p33->extts_work, idt82p33_extts_check); mutex_lock(idt82p33->lock); - err = idt82p33_load_firmware(idt82p33); + /* cold reset before loading firmware */ + idt82p33_reset(idt82p33, true); + err = idt82p33_load_firmware(idt82p33); if (err) dev_warn(idt82p33->dev, "loading firmware failed with %d\n", err); + /* soft reset after loading firmware */ + idt82p33_reset(idt82p33, false); + if (idt82p33->pll_mask) { for (i = 0; i < MAX_PHC_PLL; i++) { - if (idt82p33->pll_mask & (1 << i)) { + if (idt82p33->pll_mask & (1 << i)) err = idt82p33_enable_channel(idt82p33, i); - if (err) { - dev_err(idt82p33->dev, - "Failed in %s with err %d!\n", - __func__, err); - break; - } + else + err = idt82p33_channel_init(idt82p33, i); + if (err) { + dev_err(idt82p33->dev, + "Failed in %s with err %d!\n", + __func__, err); + break; } } } else { @@ -928,6 +1485,8 @@ static int idt82p33_remove(struct platform_device *pdev) { struct idt82p33 *idt82p33 = platform_get_drvdata(pdev); + cancel_delayed_work_sync(&idt82p33->extts_work); + idt82p33_ptp_clock_unregister_all(idt82p33); return 0; diff --git a/drivers/ptp/ptp_idt82p33.h b/drivers/ptp/ptp_idt82p33.h index 0ea1c35c0f9f..cddebf05a5b9 100644 --- a/drivers/ptp/ptp_idt82p33.h +++ b/drivers/ptp/ptp_idt82p33.h @@ -13,6 +13,8 @@ #define FW_FILENAME "idt82p33xxx.bin" #define MAX_PHC_PLL (2) +#define MAX_TRIG_CLK (3) +#define MAX_PER_OUT (11) #define TOD_BYTE_COUNT (10) #define DCO_MAX_PPB (92000) #define MAX_MEASURMENT_COUNT (5) @@ -60,8 +62,18 @@ struct idt82p33_channel { struct ptp_clock *ptp_clock; struct idt82p33 *idt82p33; enum pll_mode pll_mode; - s32 current_freq_ppb; + /* Workaround for TOD-to-output alignment issue */ + struct delayed_work adjtime_work; + s32 current_freq; + /* double dco mode */ + bool ddco; u8 output_mask; + /* last input trigger for extts */ + u8 tod_trigger; + bool discard_next_extts; + u8 plln; + /* remember last tod_sts for extts */ + u8 extts_tod_sts[TOD_BYTE_COUNT]; u16 dpll_tod_cnfg; u16 dpll_tod_trigger; u16 dpll_tod_sts; @@ -76,6 +88,12 @@ struct idt82p33 { struct idt82p33_channel channel[MAX_PHC_PLL]; struct device *dev; u8 pll_mask; + /* Polls for external time stamps */ + u8 extts_mask; + bool extts_single_shot; + struct delayed_work extts_work; + /* Remember the ptp channel to report extts */ + struct idt82p33_channel *event_channel[MAX_PHC_PLL]; /* Mutex to protect operations from being interrupted */ struct mutex *lock; struct regmap *regmap; From 46da4aa2560f44eb24d55d931533dc1da45b8e89 Mon Sep 17 00:00:00 2001 From: Min Li Date: Wed, 23 Nov 2022 14:52:07 -0500 Subject: [PATCH 1239/2002] ptp: idt82p33: remove PEROUT_ENABLE_OUTPUT_MASK PEROUT_ENABLE_OUTPUT_MASK was there to allow us to enable/disable all the perout pins. But it is not standard procedure, we will have to discard it. Signed-off-by: Min Li Signed-off-by: David S. Miller --- drivers/ptp/ptp_idt82p33.c | 34 ---------------------------------- drivers/ptp/ptp_idt82p33.h | 1 - 2 files changed, 35 deletions(-) diff --git a/drivers/ptp/ptp_idt82p33.c b/drivers/ptp/ptp_idt82p33.c index aece499c26d4..afc76c22271a 100644 --- a/drivers/ptp/ptp_idt82p33.c +++ b/drivers/ptp/ptp_idt82p33.c @@ -895,40 +895,10 @@ static int idt82p33_output_enable(struct idt82p33_channel *channel, return idt82p33_write(idt82p33, OUT_MUX_CNFG(outn), &val, sizeof(val)); } -static int idt82p33_output_mask_enable(struct idt82p33_channel *channel, - bool enable) -{ - u16 mask; - int err; - u8 outn; - - mask = channel->output_mask; - outn = 0; - - while (mask) { - if (mask & 0x1) { - err = idt82p33_output_enable(channel, enable, outn); - if (err) - return err; - } - - mask >>= 0x1; - outn++; - } - - return 0; -} - static int idt82p33_perout_enable(struct idt82p33_channel *channel, bool enable, struct ptp_perout_request *perout) { - unsigned int flags = perout->flags; - - /* Enable/disable output based on output_mask */ - if (flags == PEROUT_ENABLE_OUTPUT_MASK) - return idt82p33_output_mask_enable(channel, enable); - /* Enable/disable individual output instead */ return idt82p33_output_enable(channel, enable, perout->index); } @@ -939,10 +909,6 @@ static int idt82p33_enable_tod(struct idt82p33_channel *channel) struct timespec64 ts = {0, 0}; int err; - /* STEELAI-366 - Temporary workaround for ts2phc compatibility */ - if (0) - err = idt82p33_output_mask_enable(channel, false); - err = idt82p33_measure_tod_write_overhead(channel); if (err) { diff --git a/drivers/ptp/ptp_idt82p33.h b/drivers/ptp/ptp_idt82p33.h index cddebf05a5b9..8fcb0b17d207 100644 --- a/drivers/ptp/ptp_idt82p33.h +++ b/drivers/ptp/ptp_idt82p33.h @@ -22,7 +22,6 @@ #define IMMEDIATE_SNAP_THRESHOLD_NS (50000) #define DDCO_THRESHOLD_NS (5) #define IDT82P33_MAX_WRITE_COUNT (512) -#define PEROUT_ENABLE_OUTPUT_MASK (0xdeadbeef) #define PLLMASK_ADDR_HI 0xFF #define PLLMASK_ADDR_LO 0xA5 From 7292bb064d627f5d97686d4e765508b604a13241 Mon Sep 17 00:00:00 2001 From: Horatiu Vultur Date: Wed, 23 Nov 2022 21:31:33 +0100 Subject: [PATCH 1240/2002] net: lan966x: Add XDP_PACKET_HEADROOM Update the page_pool params to allocate XDP_PACKET_HEADROOM space as headroom for all received frames. This is needed for when the XDP_TX and XDP_REDIRECT are implemented. Signed-off-by: Horatiu Vultur Signed-off-by: David S. Miller --- .../ethernet/microchip/lan966x/lan966x_fdma.c | 16 +++++++++++----- .../net/ethernet/microchip/lan966x/lan966x_xdp.c | 3 ++- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_fdma.c b/drivers/net/ethernet/microchip/lan966x/lan966x_fdma.c index 5fbbd479cfb0..3055124b4dd7 100644 --- a/drivers/net/ethernet/microchip/lan966x/lan966x_fdma.c +++ b/drivers/net/ethernet/microchip/lan966x/lan966x_fdma.c @@ -1,5 +1,7 @@ // SPDX-License-Identifier: GPL-2.0+ +#include + #include "lan966x_main.h" static int lan966x_fdma_channel_active(struct lan966x *lan966x) @@ -16,7 +18,7 @@ static struct page *lan966x_fdma_rx_alloc_page(struct lan966x_rx *rx, if (unlikely(!page)) return NULL; - db->dataptr = page_pool_get_dma_addr(page); + db->dataptr = page_pool_get_dma_addr(page) + XDP_PACKET_HEADROOM; return page; } @@ -72,7 +74,7 @@ static int lan966x_fdma_rx_alloc_page_pool(struct lan966x_rx *rx) .nid = NUMA_NO_NODE, .dev = lan966x->dev, .dma_dir = DMA_FROM_DEVICE, - .offset = 0, + .offset = XDP_PACKET_HEADROOM, .max_len = rx->max_mtu - SKB_DATA_ALIGN(sizeof(struct skb_shared_info)), }; @@ -432,11 +434,13 @@ static int lan966x_fdma_rx_check_frame(struct lan966x_rx *rx, u64 *src_port) if (unlikely(!page)) return FDMA_ERROR; - dma_sync_single_for_cpu(lan966x->dev, (dma_addr_t)db->dataptr, + dma_sync_single_for_cpu(lan966x->dev, + (dma_addr_t)db->dataptr + XDP_PACKET_HEADROOM, FDMA_DCB_STATUS_BLOCKL(db->status), DMA_FROM_DEVICE); - lan966x_ifh_get_src_port(page_address(page), src_port); + lan966x_ifh_get_src_port(page_address(page) + XDP_PACKET_HEADROOM, + src_port); if (WARN_ON(*src_port >= lan966x->num_phys_ports)) return FDMA_ERROR; @@ -466,6 +470,7 @@ static struct sk_buff *lan966x_fdma_rx_get_frame(struct lan966x_rx *rx, skb_mark_for_recycle(skb); + skb_reserve(skb, XDP_PACKET_HEADROOM); skb_put(skb, FDMA_DCB_STATUS_BLOCKL(db->status)); lan966x_ifh_get_timestamp(skb->data, ×tamp); @@ -786,7 +791,8 @@ static int lan966x_fdma_get_max_frame(struct lan966x *lan966x) return lan966x_fdma_get_max_mtu(lan966x) + IFH_LEN_BYTES + SKB_DATA_ALIGN(sizeof(struct skb_shared_info)) + - VLAN_HLEN * 2; + VLAN_HLEN * 2 + + XDP_PACKET_HEADROOM; } int lan966x_fdma_change_mtu(struct lan966x *lan966x) diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_xdp.c b/drivers/net/ethernet/microchip/lan966x/lan966x_xdp.c index e77d9f2aad2b..8ebde1eb6a09 100644 --- a/drivers/net/ethernet/microchip/lan966x/lan966x_xdp.c +++ b/drivers/net/ethernet/microchip/lan966x/lan966x_xdp.c @@ -44,7 +44,8 @@ int lan966x_xdp_run(struct lan966x_port *port, struct page *page, u32 data_len) xdp_init_buff(&xdp, PAGE_SIZE << lan966x->rx.page_order, &port->xdp_rxq); - xdp_prepare_buff(&xdp, page_address(page), IFH_LEN_BYTES, + xdp_prepare_buff(&xdp, page_address(page), + IFH_LEN_BYTES + XDP_PACKET_HEADROOM, data_len - IFH_LEN_BYTES, false); act = bpf_prog_run_xdp(xdp_prog, &xdp); switch (act) { From 3d66bc578655926ab1aff4bb0bb5037af16f914a Mon Sep 17 00:00:00 2001 From: Horatiu Vultur Date: Wed, 23 Nov 2022 21:31:34 +0100 Subject: [PATCH 1241/2002] net: lan966x: Introduce helper functions Introduce lan966x_fdma_tx_setup_dcb and lan966x_fdma_tx_start functions and use of them inside lan966x_fdma_xmit. There is no functional change in here. They are introduced to be used when XDP_TX/REDIRECT actions are introduced. Signed-off-by: Horatiu Vultur Signed-off-by: David S. Miller --- .../ethernet/microchip/lan966x/lan966x_fdma.c | 71 ++++++++++++------- 1 file changed, 44 insertions(+), 27 deletions(-) diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_fdma.c b/drivers/net/ethernet/microchip/lan966x/lan966x_fdma.c index 3055124b4dd7..94c720e59cae 100644 --- a/drivers/net/ethernet/microchip/lan966x/lan966x_fdma.c +++ b/drivers/net/ethernet/microchip/lan966x/lan966x_fdma.c @@ -612,14 +612,53 @@ static int lan966x_fdma_get_next_dcb(struct lan966x_tx *tx) return -1; } +static void lan966x_fdma_tx_setup_dcb(struct lan966x_tx *tx, + int next_to_use, int len, + dma_addr_t dma_addr) +{ + struct lan966x_tx_dcb *next_dcb; + struct lan966x_db *next_db; + + next_dcb = &tx->dcbs[next_to_use]; + next_dcb->nextptr = FDMA_DCB_INVALID_DATA; + + next_db = &next_dcb->db[0]; + next_db->dataptr = dma_addr; + next_db->status = FDMA_DCB_STATUS_SOF | + FDMA_DCB_STATUS_EOF | + FDMA_DCB_STATUS_INTR | + FDMA_DCB_STATUS_BLOCKO(0) | + FDMA_DCB_STATUS_BLOCKL(len); +} + +static void lan966x_fdma_tx_start(struct lan966x_tx *tx, int next_to_use) +{ + struct lan966x *lan966x = tx->lan966x; + struct lan966x_tx_dcb *dcb; + + if (likely(lan966x->tx.activated)) { + /* Connect current dcb to the next db */ + dcb = &tx->dcbs[tx->last_in_use]; + dcb->nextptr = tx->dma + (next_to_use * + sizeof(struct lan966x_tx_dcb)); + + lan966x_fdma_tx_reload(tx); + } else { + /* Because it is first time, then just activate */ + lan966x->tx.activated = true; + lan966x_fdma_tx_activate(tx); + } + + /* Move to next dcb because this last in use */ + tx->last_in_use = next_to_use; +} + int lan966x_fdma_xmit(struct sk_buff *skb, __be32 *ifh, struct net_device *dev) { struct lan966x_port *port = netdev_priv(dev); struct lan966x *lan966x = port->lan966x; struct lan966x_tx_dcb_buf *next_dcb_buf; - struct lan966x_tx_dcb *next_dcb, *dcb; struct lan966x_tx *tx = &lan966x->tx; - struct lan966x_db *next_db; int needed_headroom; int needed_tailroom; dma_addr_t dma_addr; @@ -665,16 +704,7 @@ int lan966x_fdma_xmit(struct sk_buff *skb, __be32 *ifh, struct net_device *dev) } /* Setup next dcb */ - next_dcb = &tx->dcbs[next_to_use]; - next_dcb->nextptr = FDMA_DCB_INVALID_DATA; - - next_db = &next_dcb->db[0]; - next_db->dataptr = dma_addr; - next_db->status = FDMA_DCB_STATUS_SOF | - FDMA_DCB_STATUS_EOF | - FDMA_DCB_STATUS_INTR | - FDMA_DCB_STATUS_BLOCKO(0) | - FDMA_DCB_STATUS_BLOCKL(skb->len); + lan966x_fdma_tx_setup_dcb(tx, next_to_use, skb->len, dma_addr); /* Fill up the buffer */ next_dcb_buf = &tx->dcbs_buf[next_to_use]; @@ -688,21 +718,8 @@ int lan966x_fdma_xmit(struct sk_buff *skb, __be32 *ifh, struct net_device *dev) LAN966X_SKB_CB(skb)->rew_op == IFH_REW_OP_TWO_STEP_PTP) next_dcb_buf->ptp = true; - if (likely(lan966x->tx.activated)) { - /* Connect current dcb to the next db */ - dcb = &tx->dcbs[tx->last_in_use]; - dcb->nextptr = tx->dma + (next_to_use * - sizeof(struct lan966x_tx_dcb)); - - lan966x_fdma_tx_reload(tx); - } else { - /* Because it is first time, then just activate */ - lan966x->tx.activated = true; - lan966x_fdma_tx_activate(tx); - } - - /* Move to next dcb because this last in use */ - tx->last_in_use = next_to_use; + /* Start the transmission */ + lan966x_fdma_tx_start(tx, next_to_use); return NETDEV_TX_OK; From 49f5eea8c4f548ef174b17ef2e86f4d867a64516 Mon Sep 17 00:00:00 2001 From: Horatiu Vultur Date: Wed, 23 Nov 2022 21:31:35 +0100 Subject: [PATCH 1242/2002] net: lan966x: Add len field to lan966x_tx_dcb_buf Currently when a frame was transmitted, it is required to unamp the frame that was transmitted. The length of the frame was taken from the transmitted skb. In the future we might not have an skb, therefore store the length skb directly in the lan966x_tx_dcb_buf and use this one to unamp the frame. While at this, also arrange the members in lan966x_tx_dcb_buf not to have any holes. Signed-off-by: Horatiu Vultur Signed-off-by: David S. Miller --- drivers/net/ethernet/microchip/lan966x/lan966x_fdma.c | 5 +++-- drivers/net/ethernet/microchip/lan966x/lan966x_main.h | 7 ++++--- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_fdma.c b/drivers/net/ethernet/microchip/lan966x/lan966x_fdma.c index 94c720e59cae..384ed34197d5 100644 --- a/drivers/net/ethernet/microchip/lan966x/lan966x_fdma.c +++ b/drivers/net/ethernet/microchip/lan966x/lan966x_fdma.c @@ -391,12 +391,12 @@ static void lan966x_fdma_tx_clear_buf(struct lan966x *lan966x, int weight) continue; dcb_buf->dev->stats.tx_packets++; - dcb_buf->dev->stats.tx_bytes += dcb_buf->skb->len; + dcb_buf->dev->stats.tx_bytes += dcb_buf->len; dcb_buf->used = false; dma_unmap_single(lan966x->dev, dcb_buf->dma_addr, - dcb_buf->skb->len, + dcb_buf->len, DMA_TO_DEVICE); if (!dcb_buf->ptp) dev_kfree_skb_any(dcb_buf->skb); @@ -709,6 +709,7 @@ int lan966x_fdma_xmit(struct sk_buff *skb, __be32 *ifh, struct net_device *dev) /* Fill up the buffer */ next_dcb_buf = &tx->dcbs_buf[next_to_use]; next_dcb_buf->skb = skb; + next_dcb_buf->len = skb->len; next_dcb_buf->dma_addr = dma_addr; next_dcb_buf->used = true; next_dcb_buf->ptp = false; diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_main.h b/drivers/net/ethernet/microchip/lan966x/lan966x_main.h index bc93051aa079..c762e3732f88 100644 --- a/drivers/net/ethernet/microchip/lan966x/lan966x_main.h +++ b/drivers/net/ethernet/microchip/lan966x/lan966x_main.h @@ -173,11 +173,12 @@ struct lan966x_rx { }; struct lan966x_tx_dcb_buf { + dma_addr_t dma_addr; struct net_device *dev; struct sk_buff *skb; - dma_addr_t dma_addr; - bool used; - bool ptp; + u32 len; + u32 used : 1; + u32 ptp : 1; }; struct lan966x_tx { From 77ddda44411c36f6276616b9609a50ca98dc4d2c Mon Sep 17 00:00:00 2001 From: Horatiu Vultur Date: Wed, 23 Nov 2022 21:31:36 +0100 Subject: [PATCH 1243/2002] net: lan966x: Update rxq memory model By default the rxq memory model is MEM_TYPE_PAGE_SHARED but to be able to reuse pages on the TX side, when the XDP action XDP_TX it is required to update the memory model to PAGE_POOL. Signed-off-by: Horatiu Vultur Signed-off-by: David S. Miller --- .../net/ethernet/microchip/lan966x/lan966x_fdma.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_fdma.c b/drivers/net/ethernet/microchip/lan966x/lan966x_fdma.c index 384ed34197d5..3df3b3d29b4f 100644 --- a/drivers/net/ethernet/microchip/lan966x/lan966x_fdma.c +++ b/drivers/net/ethernet/microchip/lan966x/lan966x_fdma.c @@ -80,6 +80,19 @@ static int lan966x_fdma_rx_alloc_page_pool(struct lan966x_rx *rx) }; rx->page_pool = page_pool_create(&pp_params); + + for (int i = 0; i < lan966x->num_phys_ports; ++i) { + struct lan966x_port *port; + + if (!lan966x->ports[i]) + continue; + + port = lan966x->ports[i]; + xdp_rxq_info_unreg_mem_model(&port->xdp_rxq); + xdp_rxq_info_reg_mem_model(&port->xdp_rxq, MEM_TYPE_PAGE_POOL, + rx->page_pool); + } + return PTR_ERR_OR_ZERO(rx->page_pool); } From 560c7223d6e48171d5a34ca07f6c886d364bcbdf Mon Sep 17 00:00:00 2001 From: Horatiu Vultur Date: Wed, 23 Nov 2022 21:31:37 +0100 Subject: [PATCH 1244/2002] net: lan966x: Update dma_dir of page_pool_params To add support for XDP_TX it is required to be able to write to the DMA area therefore it is required that the pages will be mapped using DMA_BIDIRECTIONAL flag. Therefore check if there are any xdp programs on the interfaces and in that case set DMA_BIDRECTIONAL otherwise use DMA_FROM_DEVICE. Therefore when a new XDP program is added it is required to redo the page_pool. Signed-off-by: Horatiu Vultur Signed-off-by: David S. Miller --- .../ethernet/microchip/lan966x/lan966x_fdma.c | 29 +++++++++++++++---- .../ethernet/microchip/lan966x/lan966x_main.h | 2 ++ .../ethernet/microchip/lan966x/lan966x_xdp.c | 28 ++++++++++++++++++ 3 files changed, 53 insertions(+), 6 deletions(-) diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_fdma.c b/drivers/net/ethernet/microchip/lan966x/lan966x_fdma.c index 3df3b3d29b4f..bc30c31c4360 100644 --- a/drivers/net/ethernet/microchip/lan966x/lan966x_fdma.c +++ b/drivers/net/ethernet/microchip/lan966x/lan966x_fdma.c @@ -79,6 +79,9 @@ static int lan966x_fdma_rx_alloc_page_pool(struct lan966x_rx *rx) SKB_DATA_ALIGN(sizeof(struct skb_shared_info)), }; + if (lan966x_xdp_present(lan966x)) + pp_params.dma_dir = DMA_BIDIRECTIONAL; + rx->page_pool = page_pool_create(&pp_params); for (int i = 0; i < lan966x->num_phys_ports; ++i) { @@ -826,16 +829,11 @@ static int lan966x_fdma_get_max_frame(struct lan966x *lan966x) XDP_PACKET_HEADROOM; } -int lan966x_fdma_change_mtu(struct lan966x *lan966x) +static int __lan966x_fdma_reload(struct lan966x *lan966x, int max_mtu) { - int max_mtu; int err; u32 val; - max_mtu = lan966x_fdma_get_max_frame(lan966x); - if (max_mtu == lan966x->rx.max_mtu) - return 0; - /* Disable the CPU port */ lan_rmw(QSYS_SW_PORT_MODE_PORT_ENA_SET(0), QSYS_SW_PORT_MODE_PORT_ENA, @@ -861,6 +859,25 @@ int lan966x_fdma_change_mtu(struct lan966x *lan966x) return err; } +int lan966x_fdma_change_mtu(struct lan966x *lan966x) +{ + int max_mtu; + + max_mtu = lan966x_fdma_get_max_frame(lan966x); + if (max_mtu == lan966x->rx.max_mtu) + return 0; + + return __lan966x_fdma_reload(lan966x, max_mtu); +} + +int lan966x_fdma_reload_page_pool(struct lan966x *lan966x) +{ + int max_mtu; + + max_mtu = lan966x_fdma_get_max_frame(lan966x); + return __lan966x_fdma_reload(lan966x, max_mtu); +} + void lan966x_fdma_netdev_init(struct lan966x *lan966x, struct net_device *dev) { if (lan966x->fdma_ndev) diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_main.h b/drivers/net/ethernet/microchip/lan966x/lan966x_main.h index c762e3732f88..81c0b11097ce 100644 --- a/drivers/net/ethernet/microchip/lan966x/lan966x_main.h +++ b/drivers/net/ethernet/microchip/lan966x/lan966x_main.h @@ -466,6 +466,7 @@ void lan966x_fdma_netdev_deinit(struct lan966x *lan966x, struct net_device *dev) int lan966x_fdma_init(struct lan966x *lan966x); void lan966x_fdma_deinit(struct lan966x *lan966x); irqreturn_t lan966x_fdma_irq_handler(int irq, void *args); +int lan966x_fdma_reload_page_pool(struct lan966x *lan966x); int lan966x_lag_port_join(struct lan966x_port *port, struct net_device *brport_dev, @@ -556,6 +557,7 @@ int lan966x_xdp(struct net_device *dev, struct netdev_bpf *xdp); int lan966x_xdp_run(struct lan966x_port *port, struct page *page, u32 data_len); +bool lan966x_xdp_present(struct lan966x *lan966x); static inline bool lan966x_xdp_port_present(struct lan966x_port *port) { return !!port->xdp_prog; diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_xdp.c b/drivers/net/ethernet/microchip/lan966x/lan966x_xdp.c index 8ebde1eb6a09..ba7aa6df9d0e 100644 --- a/drivers/net/ethernet/microchip/lan966x/lan966x_xdp.c +++ b/drivers/net/ethernet/microchip/lan966x/lan966x_xdp.c @@ -11,6 +11,8 @@ static int lan966x_xdp_setup(struct net_device *dev, struct netdev_bpf *xdp) struct lan966x_port *port = netdev_priv(dev); struct lan966x *lan966x = port->lan966x; struct bpf_prog *old_prog; + bool old_xdp, new_xdp; + int err; if (!lan966x->fdma) { NL_SET_ERR_MSG_MOD(xdp->extack, @@ -18,7 +20,20 @@ static int lan966x_xdp_setup(struct net_device *dev, struct netdev_bpf *xdp) return -EOPNOTSUPP; } + old_xdp = lan966x_xdp_present(lan966x); old_prog = xchg(&port->xdp_prog, xdp->prog); + new_xdp = lan966x_xdp_present(lan966x); + + if (old_xdp == new_xdp) + goto out; + + err = lan966x_fdma_reload_page_pool(lan966x); + if (err) { + xchg(&port->xdp_prog, old_prog); + return err; + } + +out: if (old_prog) bpf_prog_put(old_prog); @@ -62,6 +77,19 @@ int lan966x_xdp_run(struct lan966x_port *port, struct page *page, u32 data_len) } } +bool lan966x_xdp_present(struct lan966x *lan966x) +{ + for (int p = 0; p < lan966x->num_phys_ports; ++p) { + if (!lan966x->ports[p]) + continue; + + if (lan966x_xdp_port_present(lan966x->ports[p])) + return true; + } + + return false; +} + int lan966x_xdp_port_init(struct lan966x_port *port) { struct lan966x *lan966x = port->lan966x; From 19c6f534f63608fbf966897b2c64fefcb916dc08 Mon Sep 17 00:00:00 2001 From: Horatiu Vultur Date: Wed, 23 Nov 2022 21:31:38 +0100 Subject: [PATCH 1245/2002] net: lan966x: Add support for XDP_TX Extend lan966x XDP support with the action XDP_TX. In this case when the received buffer needs to execute XDP_TX, the buffer will be moved to the TX buffers. So a new RX buffer will be allocated. When the TX finish with the frame, it would give back the buffer to the page pool. Signed-off-by: Horatiu Vultur Signed-off-by: David S. Miller --- .../ethernet/microchip/lan966x/lan966x_fdma.c | 79 +++++++++++++++++-- .../ethernet/microchip/lan966x/lan966x_main.c | 4 +- .../ethernet/microchip/lan966x/lan966x_main.h | 13 ++- .../ethernet/microchip/lan966x/lan966x_xdp.c | 8 ++ 4 files changed, 94 insertions(+), 10 deletions(-) diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_fdma.c b/drivers/net/ethernet/microchip/lan966x/lan966x_fdma.c index bc30c31c4360..cd622310385b 100644 --- a/drivers/net/ethernet/microchip/lan966x/lan966x_fdma.c +++ b/drivers/net/ethernet/microchip/lan966x/lan966x_fdma.c @@ -410,12 +410,17 @@ static void lan966x_fdma_tx_clear_buf(struct lan966x *lan966x, int weight) dcb_buf->dev->stats.tx_bytes += dcb_buf->len; dcb_buf->used = false; - dma_unmap_single(lan966x->dev, - dcb_buf->dma_addr, - dcb_buf->len, - DMA_TO_DEVICE); - if (!dcb_buf->ptp) - dev_kfree_skb_any(dcb_buf->skb); + if (dcb_buf->use_skb) { + dma_unmap_single(lan966x->dev, + dcb_buf->dma_addr, + dcb_buf->len, + DMA_TO_DEVICE); + + if (!dcb_buf->ptp) + napi_consume_skb(dcb_buf->data.skb, weight); + } else { + xdp_return_frame_rx_napi(dcb_buf->data.xdpf); + } clear = true; } @@ -548,6 +553,9 @@ static int lan966x_fdma_napi_poll(struct napi_struct *napi, int weight) lan966x_fdma_rx_free_page(rx); lan966x_fdma_rx_advance_dcb(rx); goto allocate_new; + case FDMA_TX: + lan966x_fdma_rx_advance_dcb(rx); + continue; case FDMA_DROP: lan966x_fdma_rx_free_page(rx); lan966x_fdma_rx_advance_dcb(rx); @@ -669,6 +677,62 @@ static void lan966x_fdma_tx_start(struct lan966x_tx *tx, int next_to_use) tx->last_in_use = next_to_use; } +int lan966x_fdma_xmit_xdpf(struct lan966x_port *port, + struct xdp_frame *xdpf, + struct page *page) +{ + struct lan966x *lan966x = port->lan966x; + struct lan966x_tx_dcb_buf *next_dcb_buf; + struct lan966x_tx *tx = &lan966x->tx; + dma_addr_t dma_addr; + int next_to_use; + __be32 *ifh; + int ret = 0; + + spin_lock(&lan966x->tx_lock); + + /* Get next index */ + next_to_use = lan966x_fdma_get_next_dcb(tx); + if (next_to_use < 0) { + netif_stop_queue(port->dev); + ret = NETDEV_TX_BUSY; + goto out; + } + + /* Generate new IFH */ + ifh = page_address(page) + XDP_PACKET_HEADROOM; + memset(ifh, 0x0, sizeof(__be32) * IFH_LEN); + lan966x_ifh_set_bypass(ifh, 1); + lan966x_ifh_set_port(ifh, BIT_ULL(port->chip_port)); + + dma_addr = page_pool_get_dma_addr(page); + dma_sync_single_for_device(lan966x->dev, dma_addr + XDP_PACKET_HEADROOM, + xdpf->len + IFH_LEN_BYTES, + DMA_TO_DEVICE); + + /* Setup next dcb */ + lan966x_fdma_tx_setup_dcb(tx, next_to_use, xdpf->len + IFH_LEN_BYTES, + dma_addr + XDP_PACKET_HEADROOM); + + /* Fill up the buffer */ + next_dcb_buf = &tx->dcbs_buf[next_to_use]; + next_dcb_buf->use_skb = false; + next_dcb_buf->data.xdpf = xdpf; + next_dcb_buf->len = xdpf->len + IFH_LEN_BYTES; + next_dcb_buf->dma_addr = dma_addr; + next_dcb_buf->used = true; + next_dcb_buf->ptp = false; + next_dcb_buf->dev = port->dev; + + /* Start the transmission */ + lan966x_fdma_tx_start(tx, next_to_use); + +out: + spin_unlock(&lan966x->tx_lock); + + return ret; +} + int lan966x_fdma_xmit(struct sk_buff *skb, __be32 *ifh, struct net_device *dev) { struct lan966x_port *port = netdev_priv(dev); @@ -724,7 +788,8 @@ int lan966x_fdma_xmit(struct sk_buff *skb, __be32 *ifh, struct net_device *dev) /* Fill up the buffer */ next_dcb_buf = &tx->dcbs_buf[next_to_use]; - next_dcb_buf->skb = skb; + next_dcb_buf->use_skb = true; + next_dcb_buf->data.skb = skb; next_dcb_buf->len = skb->len; next_dcb_buf->dma_addr = dma_addr; next_dcb_buf->used = true; diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_main.c b/drivers/net/ethernet/microchip/lan966x/lan966x_main.c index 42be5d0f1f01..0b7707306da2 100644 --- a/drivers/net/ethernet/microchip/lan966x/lan966x_main.c +++ b/drivers/net/ethernet/microchip/lan966x/lan966x_main.c @@ -302,13 +302,13 @@ err: return NETDEV_TX_BUSY; } -static void lan966x_ifh_set_bypass(void *ifh, u64 bypass) +void lan966x_ifh_set_bypass(void *ifh, u64 bypass) { packing(ifh, &bypass, IFH_POS_BYPASS + IFH_WID_BYPASS - 1, IFH_POS_BYPASS, IFH_LEN * 4, PACK, 0); } -static void lan966x_ifh_set_port(void *ifh, u64 bypass) +void lan966x_ifh_set_port(void *ifh, u64 bypass) { packing(ifh, &bypass, IFH_POS_DSTS + IFH_WID_DSTS - 1, IFH_POS_DSTS, IFH_LEN * 4, PACK, 0); diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_main.h b/drivers/net/ethernet/microchip/lan966x/lan966x_main.h index 81c0b11097ce..3c18fcec5162 100644 --- a/drivers/net/ethernet/microchip/lan966x/lan966x_main.h +++ b/drivers/net/ethernet/microchip/lan966x/lan966x_main.h @@ -105,11 +105,13 @@ enum macaccess_entry_type { * FDMA_PASS, frame is valid and can be used * FDMA_ERROR, something went wrong, stop getting more frames * FDMA_DROP, frame is dropped, but continue to get more frames + * FDMA_TX, frame is given to TX, but continue to get more frames */ enum lan966x_fdma_action { FDMA_PASS = 0, FDMA_ERROR, FDMA_DROP, + FDMA_TX, }; struct lan966x_port; @@ -175,10 +177,14 @@ struct lan966x_rx { struct lan966x_tx_dcb_buf { dma_addr_t dma_addr; struct net_device *dev; - struct sk_buff *skb; + union { + struct sk_buff *skb; + struct xdp_frame *xdpf; + } data; u32 len; u32 used : 1; u32 ptp : 1; + u32 use_skb : 1; }; struct lan966x_tx { @@ -360,6 +366,8 @@ bool lan966x_hw_offload(struct lan966x *lan966x, u32 port, struct sk_buff *skb); void lan966x_ifh_get_src_port(void *ifh, u64 *src_port); void lan966x_ifh_get_timestamp(void *ifh, u64 *timestamp); +void lan966x_ifh_set_bypass(void *ifh, u64 bypass); +void lan966x_ifh_set_port(void *ifh, u64 bypass); void lan966x_stats_get(struct net_device *dev, struct rtnl_link_stats64 *stats); @@ -460,6 +468,9 @@ u32 lan966x_ptp_get_period_ps(void); int lan966x_ptp_gettime64(struct ptp_clock_info *ptp, struct timespec64 *ts); int lan966x_fdma_xmit(struct sk_buff *skb, __be32 *ifh, struct net_device *dev); +int lan966x_fdma_xmit_xdpf(struct lan966x_port *port, + struct xdp_frame *frame, + struct page *page); int lan966x_fdma_change_mtu(struct lan966x *lan966x); void lan966x_fdma_netdev_init(struct lan966x *lan966x, struct net_device *dev); void lan966x_fdma_netdev_deinit(struct lan966x *lan966x, struct net_device *dev); diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_xdp.c b/drivers/net/ethernet/microchip/lan966x/lan966x_xdp.c index ba7aa6df9d0e..8996110a0846 100644 --- a/drivers/net/ethernet/microchip/lan966x/lan966x_xdp.c +++ b/drivers/net/ethernet/microchip/lan966x/lan966x_xdp.c @@ -54,6 +54,7 @@ int lan966x_xdp_run(struct lan966x_port *port, struct page *page, u32 data_len) { struct bpf_prog *xdp_prog = port->xdp_prog; struct lan966x *lan966x = port->lan966x; + struct xdp_frame *xdpf; struct xdp_buff xdp; u32 act; @@ -66,6 +67,13 @@ int lan966x_xdp_run(struct lan966x_port *port, struct page *page, u32 data_len) switch (act) { case XDP_PASS: return FDMA_PASS; + case XDP_TX: + xdpf = xdp_convert_buff_to_frame(&xdp); + if (!xdpf) + return FDMA_DROP; + + return lan966x_fdma_xmit_xdpf(port, xdpf, page) ? + FDMA_DROP : FDMA_TX; default: bpf_warn_invalid_xdp_action(port->dev, xdp_prog, act); fallthrough; From a825b611c7c186c8b53fa22ae0c027a75c5d4096 Mon Sep 17 00:00:00 2001 From: Horatiu Vultur Date: Wed, 23 Nov 2022 21:31:39 +0100 Subject: [PATCH 1246/2002] net: lan966x: Add support for XDP_REDIRECT Extend lan966x XDP support with the action XDP_REDIRECT. This is similar with the XDP_TX, so a lot of functionality can be reused. Signed-off-by: Horatiu Vultur Signed-off-by: David S. Miller --- .../ethernet/microchip/lan966x/lan966x_fdma.c | 79 ++++++++++++++++--- .../ethernet/microchip/lan966x/lan966x_main.c | 1 + .../ethernet/microchip/lan966x/lan966x_main.h | 10 ++- .../ethernet/microchip/lan966x/lan966x_xdp.c | 29 ++++++- 4 files changed, 104 insertions(+), 15 deletions(-) diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_fdma.c b/drivers/net/ethernet/microchip/lan966x/lan966x_fdma.c index cd622310385b..5314c064ceae 100644 --- a/drivers/net/ethernet/microchip/lan966x/lan966x_fdma.c +++ b/drivers/net/ethernet/microchip/lan966x/lan966x_fdma.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0+ #include +#include #include "lan966x_main.h" @@ -390,11 +391,14 @@ static void lan966x_fdma_tx_clear_buf(struct lan966x *lan966x, int weight) { struct lan966x_tx *tx = &lan966x->tx; struct lan966x_tx_dcb_buf *dcb_buf; + struct xdp_frame_bulk bq; struct lan966x_db *db; unsigned long flags; bool clear = false; int i; + xdp_frame_bulk_init(&bq); + spin_lock_irqsave(&lan966x->tx_lock, flags); for (i = 0; i < FDMA_DCB_MAX; ++i) { dcb_buf = &tx->dcbs_buf[i]; @@ -419,12 +423,23 @@ static void lan966x_fdma_tx_clear_buf(struct lan966x *lan966x, int weight) if (!dcb_buf->ptp) napi_consume_skb(dcb_buf->data.skb, weight); } else { - xdp_return_frame_rx_napi(dcb_buf->data.xdpf); + if (dcb_buf->xdp_ndo) + dma_unmap_single(lan966x->dev, + dcb_buf->dma_addr, + dcb_buf->len, + DMA_TO_DEVICE); + + if (dcb_buf->xdp_ndo) + xdp_return_frame_bulk(dcb_buf->data.xdpf, &bq); + else + xdp_return_frame_rx_napi(dcb_buf->data.xdpf); } clear = true; } + xdp_flush_frame_bulk(&bq); + if (clear) lan966x_fdma_wakeup_netdev(lan966x); @@ -531,6 +546,7 @@ static int lan966x_fdma_napi_poll(struct napi_struct *napi, int weight) int dcb_reload = rx->dcb_index; struct lan966x_rx_dcb *old_dcb; struct lan966x_db *db; + bool redirect = false; struct sk_buff *skb; struct page *page; int counter = 0; @@ -553,6 +569,9 @@ static int lan966x_fdma_napi_poll(struct napi_struct *napi, int weight) lan966x_fdma_rx_free_page(rx); lan966x_fdma_rx_advance_dcb(rx); goto allocate_new; + case FDMA_REDIRECT: + redirect = true; + fallthrough; case FDMA_TX: lan966x_fdma_rx_advance_dcb(rx); continue; @@ -592,6 +611,9 @@ allocate_new: if (counter < weight && napi_complete_done(napi, counter)) lan_wr(0xff, lan966x, FDMA_INTR_DB_ENA); + if (redirect) + xdp_do_flush(); + return counter; } @@ -679,7 +701,8 @@ static void lan966x_fdma_tx_start(struct lan966x_tx *tx, int next_to_use) int lan966x_fdma_xmit_xdpf(struct lan966x_port *port, struct xdp_frame *xdpf, - struct page *page) + struct page *page, + bool dma_map) { struct lan966x *lan966x = port->lan966x; struct lan966x_tx_dcb_buf *next_dcb_buf; @@ -700,24 +723,53 @@ int lan966x_fdma_xmit_xdpf(struct lan966x_port *port, } /* Generate new IFH */ - ifh = page_address(page) + XDP_PACKET_HEADROOM; - memset(ifh, 0x0, sizeof(__be32) * IFH_LEN); - lan966x_ifh_set_bypass(ifh, 1); - lan966x_ifh_set_port(ifh, BIT_ULL(port->chip_port)); + if (dma_map) { + if (xdpf->headroom < IFH_LEN_BYTES) { + ret = NETDEV_TX_OK; + goto out; + } - dma_addr = page_pool_get_dma_addr(page); - dma_sync_single_for_device(lan966x->dev, dma_addr + XDP_PACKET_HEADROOM, - xdpf->len + IFH_LEN_BYTES, - DMA_TO_DEVICE); + ifh = xdpf->data - IFH_LEN_BYTES; + memset(ifh, 0x0, sizeof(__be32) * IFH_LEN); + lan966x_ifh_set_bypass(ifh, 1); + lan966x_ifh_set_port(ifh, BIT_ULL(port->chip_port)); - /* Setup next dcb */ - lan966x_fdma_tx_setup_dcb(tx, next_to_use, xdpf->len + IFH_LEN_BYTES, - dma_addr + XDP_PACKET_HEADROOM); + dma_addr = dma_map_single(lan966x->dev, + xdpf->data - IFH_LEN_BYTES, + xdpf->len + IFH_LEN_BYTES, + DMA_TO_DEVICE); + if (dma_mapping_error(lan966x->dev, dma_addr)) { + ret = NETDEV_TX_OK; + goto out; + } + + /* Setup next dcb */ + lan966x_fdma_tx_setup_dcb(tx, next_to_use, + xdpf->len + IFH_LEN_BYTES, + dma_addr); + } else { + ifh = page_address(page) + XDP_PACKET_HEADROOM; + memset(ifh, 0x0, sizeof(__be32) * IFH_LEN); + lan966x_ifh_set_bypass(ifh, 1); + lan966x_ifh_set_port(ifh, BIT_ULL(port->chip_port)); + + dma_addr = page_pool_get_dma_addr(page); + dma_sync_single_for_device(lan966x->dev, + dma_addr + XDP_PACKET_HEADROOM, + xdpf->len + IFH_LEN_BYTES, + DMA_TO_DEVICE); + + /* Setup next dcb */ + lan966x_fdma_tx_setup_dcb(tx, next_to_use, + xdpf->len + IFH_LEN_BYTES, + dma_addr + XDP_PACKET_HEADROOM); + } /* Fill up the buffer */ next_dcb_buf = &tx->dcbs_buf[next_to_use]; next_dcb_buf->use_skb = false; next_dcb_buf->data.xdpf = xdpf; + next_dcb_buf->xdp_ndo = dma_map; next_dcb_buf->len = xdpf->len + IFH_LEN_BYTES; next_dcb_buf->dma_addr = dma_addr; next_dcb_buf->used = true; @@ -790,6 +842,7 @@ int lan966x_fdma_xmit(struct sk_buff *skb, __be32 *ifh, struct net_device *dev) next_dcb_buf = &tx->dcbs_buf[next_to_use]; next_dcb_buf->use_skb = true; next_dcb_buf->data.skb = skb; + next_dcb_buf->xdp_ndo = false; next_dcb_buf->len = skb->len; next_dcb_buf->dma_addr = dma_addr; next_dcb_buf->used = true; diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_main.c b/drivers/net/ethernet/microchip/lan966x/lan966x_main.c index 0b7707306da2..0aed244826d3 100644 --- a/drivers/net/ethernet/microchip/lan966x/lan966x_main.c +++ b/drivers/net/ethernet/microchip/lan966x/lan966x_main.c @@ -469,6 +469,7 @@ static const struct net_device_ops lan966x_port_netdev_ops = { .ndo_eth_ioctl = lan966x_port_ioctl, .ndo_setup_tc = lan966x_tc_setup, .ndo_bpf = lan966x_xdp, + .ndo_xdp_xmit = lan966x_xdp_xmit, }; bool lan966x_netdevice_check(const struct net_device *dev) diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_main.h b/drivers/net/ethernet/microchip/lan966x/lan966x_main.h index 3c18fcec5162..a0170a3fb976 100644 --- a/drivers/net/ethernet/microchip/lan966x/lan966x_main.h +++ b/drivers/net/ethernet/microchip/lan966x/lan966x_main.h @@ -106,12 +106,14 @@ enum macaccess_entry_type { * FDMA_ERROR, something went wrong, stop getting more frames * FDMA_DROP, frame is dropped, but continue to get more frames * FDMA_TX, frame is given to TX, but continue to get more frames + * FDMA_REDIRECT, frame is given to TX, but continue to get more frames */ enum lan966x_fdma_action { FDMA_PASS = 0, FDMA_ERROR, FDMA_DROP, FDMA_TX, + FDMA_REDIRECT, }; struct lan966x_port; @@ -185,6 +187,7 @@ struct lan966x_tx_dcb_buf { u32 used : 1; u32 ptp : 1; u32 use_skb : 1; + u32 xdp_ndo : 1; }; struct lan966x_tx { @@ -470,7 +473,8 @@ int lan966x_ptp_gettime64(struct ptp_clock_info *ptp, struct timespec64 *ts); int lan966x_fdma_xmit(struct sk_buff *skb, __be32 *ifh, struct net_device *dev); int lan966x_fdma_xmit_xdpf(struct lan966x_port *port, struct xdp_frame *frame, - struct page *page); + struct page *page, + bool dma_map); int lan966x_fdma_change_mtu(struct lan966x *lan966x); void lan966x_fdma_netdev_init(struct lan966x *lan966x, struct net_device *dev); void lan966x_fdma_netdev_deinit(struct lan966x *lan966x, struct net_device *dev); @@ -568,6 +572,10 @@ int lan966x_xdp(struct net_device *dev, struct netdev_bpf *xdp); int lan966x_xdp_run(struct lan966x_port *port, struct page *page, u32 data_len); +int lan966x_xdp_xmit(struct net_device *dev, + int n, + struct xdp_frame **frames, + u32 flags); bool lan966x_xdp_present(struct lan966x *lan966x); static inline bool lan966x_xdp_port_present(struct lan966x_port *port) { diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_xdp.c b/drivers/net/ethernet/microchip/lan966x/lan966x_xdp.c index 8996110a0846..2e6f486ec67d 100644 --- a/drivers/net/ethernet/microchip/lan966x/lan966x_xdp.c +++ b/drivers/net/ethernet/microchip/lan966x/lan966x_xdp.c @@ -50,6 +50,28 @@ int lan966x_xdp(struct net_device *dev, struct netdev_bpf *xdp) } } +int lan966x_xdp_xmit(struct net_device *dev, + int n, + struct xdp_frame **frames, + u32 flags) +{ + struct lan966x_port *port = netdev_priv(dev); + int nxmit = 0; + + for (int i = 0; i < n; ++i) { + struct xdp_frame *xdpf = frames[i]; + int err; + + err = lan966x_fdma_xmit_xdpf(port, xdpf, NULL, true); + if (err) + break; + + nxmit++; + } + + return nxmit; +} + int lan966x_xdp_run(struct lan966x_port *port, struct page *page, u32 data_len) { struct bpf_prog *xdp_prog = port->xdp_prog; @@ -72,8 +94,13 @@ int lan966x_xdp_run(struct lan966x_port *port, struct page *page, u32 data_len) if (!xdpf) return FDMA_DROP; - return lan966x_fdma_xmit_xdpf(port, xdpf, page) ? + return lan966x_fdma_xmit_xdpf(port, xdpf, page, false) ? FDMA_DROP : FDMA_TX; + case XDP_REDIRECT: + if (xdp_do_redirect(port->dev, &xdp, xdp_prog)) + return FDMA_DROP; + + return FDMA_REDIRECT; default: bpf_warn_invalid_xdp_action(port->dev, xdp_prog, act); fallthrough; From 51337ef07a40652efec48bb4103880c39ad0f119 Mon Sep 17 00:00:00 2001 From: Anirudh Venkataramanan Date: Wed, 23 Nov 2022 12:52:14 -0800 Subject: [PATCH 1247/2002] ch_ktls: Use memcpy_from_page() instead of k[un]map_atomic() kmap_atomic() is being deprecated in favor of kmap_local_page(). Replace the map-memcpy-unmap usage pattern (done using k[un]map_atomic()) with memcpy_from_page(), which internally uses kmap_local_page() and kunmap_local(). This renders the variables 'data' and 'vaddr' unnecessary, and so remove these too. Note that kmap_atomic() disables preemption and page-fault processing, but kmap_local_page() doesn't. When converting uses of kmap_atomic(), one has to check if the code being executed between the map/unmap implicitly depends on page-faults and/or preemption being disabled. If yes, then code to disable page-faults and/or preemption should also be added for functional correctness. That however doesn't appear to be the case here, so just memcpy_from_page() is used. Also note that the page being mapped is not allocated by the driver, and so the driver doesn't know if the page is in normal memory. This is the reason kmap_local_page() is used (via memcpy_from_page()) as opposed to page_address(). I don't have hardware, so this change has only been compile tested. Cc: Ayush Sawal Cc: Ira Weiny Cc: Fabio M. De Francesco Suggested-by: Fabio M. De Francesco Signed-off-by: Anirudh Venkataramanan Acked-by: Ayush Sawal Signed-off-by: David S. Miller --- .../chelsio/inline_crypto/ch_ktls/chcr_ktls.c | 26 +++++++++---------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/drivers/net/ethernet/chelsio/inline_crypto/ch_ktls/chcr_ktls.c b/drivers/net/ethernet/chelsio/inline_crypto/ch_ktls/chcr_ktls.c index da9973b711f4..1a5fdd755e9e 100644 --- a/drivers/net/ethernet/chelsio/inline_crypto/ch_ktls/chcr_ktls.c +++ b/drivers/net/ethernet/chelsio/inline_crypto/ch_ktls/chcr_ktls.c @@ -1839,9 +1839,7 @@ static int chcr_short_record_handler(struct chcr_ktls_info *tx_info, */ if (prior_data_len) { int i = 0; - u8 *data = NULL; skb_frag_t *f; - u8 *vaddr; int frag_size = 0, frag_delta = 0; while (remaining > 0) { @@ -1853,24 +1851,24 @@ static int chcr_short_record_handler(struct chcr_ktls_info *tx_info, i++; } f = &record->frags[i]; - vaddr = kmap_atomic(skb_frag_page(f)); - - data = vaddr + skb_frag_off(f) + remaining; frag_delta = skb_frag_size(f) - remaining; if (frag_delta >= prior_data_len) { - memcpy(prior_data, data, prior_data_len); - kunmap_atomic(vaddr); + memcpy_from_page(prior_data, skb_frag_page(f), + skb_frag_off(f) + remaining, + prior_data_len); } else { - memcpy(prior_data, data, frag_delta); - kunmap_atomic(vaddr); + memcpy_from_page(prior_data, skb_frag_page(f), + skb_frag_off(f) + remaining, + frag_delta); + /* get the next page */ f = &record->frags[i + 1]; - vaddr = kmap_atomic(skb_frag_page(f)); - data = vaddr + skb_frag_off(f); - memcpy(prior_data + frag_delta, - data, (prior_data_len - frag_delta)); - kunmap_atomic(vaddr); + + memcpy_from_page(prior_data + frag_delta, + skb_frag_page(f), + skb_frag_off(f), + prior_data_len - frag_delta); } /* reset tcp_seq as per the prior_data_required len */ tcp_seq -= prior_data_len; From f61e6d3ca4dac83d7093e7be7ceabce95e738e68 Mon Sep 17 00:00:00 2001 From: Anirudh Venkataramanan Date: Wed, 23 Nov 2022 12:52:15 -0800 Subject: [PATCH 1248/2002] sfc: Use kmap_local_page() instead of kmap_atomic() kmap_atomic() is being deprecated in favor of kmap_local_page(). Replace kmap_atomic() and kunmap_atomic() with kmap_local_page() and kunmap_local() respectively. Note that kmap_atomic() disables preemption and page-fault processing, but kmap_local_page() doesn't. When converting uses of kmap_atomic(), one has to check if the code being executed between the map/unmap implicitly depends on page-faults and/or preemption being disabled. If yes, then code to disable page-faults and/or preemption should also be added for functional correctness. That however doesn't appear to be the case here, so just kmap_local_page() is used. Also note that the page being mapped is not allocated by the driver, and so the driver doesn't know if the page is in normal memory. This is the reason kmap_local_page() is used as opposed to page_address(). I don't have hardware, so this change has only been compile tested. Cc: Ira Weiny Cc: Fabio M. De Francesco Cc: Edward Cree Cc: Martin Habets Signed-off-by: Anirudh Venkataramanan Acked-by: Martin Habets Signed-off-by: David S. Miller --- drivers/net/ethernet/sfc/tx.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/sfc/tx.c b/drivers/net/ethernet/sfc/tx.c index c5f88f7a7a04..4ed4082836a9 100644 --- a/drivers/net/ethernet/sfc/tx.c +++ b/drivers/net/ethernet/sfc/tx.c @@ -207,11 +207,11 @@ static void efx_skb_copy_bits_to_pio(struct efx_nic *efx, struct sk_buff *skb, skb_frag_t *f = &skb_shinfo(skb)->frags[i]; u8 *vaddr; - vaddr = kmap_atomic(skb_frag_page(f)); + vaddr = kmap_local_page(skb_frag_page(f)); efx_memcpy_toio_aligned_cb(efx, piobuf, vaddr + skb_frag_off(f), skb_frag_size(f), copy_buf); - kunmap_atomic(vaddr); + kunmap_local(vaddr); } EFX_WARN_ON_ONCE_PARANOID(skb_shinfo(skb)->frag_list); From c191445874bba16a636a88fc4afc7c3ab09228fd Mon Sep 17 00:00:00 2001 From: Anirudh Venkataramanan Date: Wed, 23 Nov 2022 12:52:16 -0800 Subject: [PATCH 1249/2002] cassini: Use page_address() instead of kmap_atomic() Pages for Rx buffers are allocated in cas_page_alloc() using either GFP_ATOMIC or GFP_KERNEL. Memory allocated with GFP_KERNEL/GFP_ATOMIC can't come from highmem and so there's no need to kmap() them. Just use page_address() instead. This makes the variable 'addr' unnecessary, so remove it too. Note that kmap_atomic() disables preemption and page-fault processing, but page_address() doesn't. When removing uses of kmap_atomic(), one has to check if the code being executed between the map/unmap implicitly depends on page-faults and/or preemption being disabled. If yes, then code to disable page-faults and/or preemption should also be added for functional correctness. That however doesn't appear to be the case here, so just page_address() is used. I don't have hardware, so this change has only been compile tested. Cc: Ira Weiny Cc: Fabio M. De Francesco Signed-off-by: Anirudh Venkataramanan Signed-off-by: David S. Miller --- drivers/net/ethernet/sun/cassini.c | 34 ++++++++++-------------------- 1 file changed, 11 insertions(+), 23 deletions(-) diff --git a/drivers/net/ethernet/sun/cassini.c b/drivers/net/ethernet/sun/cassini.c index 0aca193d9550..2f66cfc5e796 100644 --- a/drivers/net/ethernet/sun/cassini.c +++ b/drivers/net/ethernet/sun/cassini.c @@ -1915,7 +1915,7 @@ static int cas_rx_process_pkt(struct cas *cp, struct cas_rx_comp *rxc, int off, swivel = RX_SWIVEL_OFF_VAL; struct cas_page *page; struct sk_buff *skb; - void *addr, *crcaddr; + void *crcaddr; __sum16 csum; char *p; @@ -1936,7 +1936,7 @@ static int cas_rx_process_pkt(struct cas *cp, struct cas_rx_comp *rxc, skb_reserve(skb, swivel); p = skb->data; - addr = crcaddr = NULL; + crcaddr = NULL; if (hlen) { /* always copy header pages */ i = CAS_VAL(RX_COMP2_HDR_INDEX, words[1]); page = cp->rx_pages[CAS_VAL(RX_INDEX_RING, i)][CAS_VAL(RX_INDEX_NUM, i)]; @@ -1948,12 +1948,10 @@ static int cas_rx_process_pkt(struct cas *cp, struct cas_rx_comp *rxc, i += cp->crc_size; dma_sync_single_for_cpu(&cp->pdev->dev, page->dma_addr + off, i, DMA_FROM_DEVICE); - addr = cas_page_map(page->buffer); - memcpy(p, addr + off, i); + memcpy(p, page_address(page->buffer) + off, i); dma_sync_single_for_device(&cp->pdev->dev, page->dma_addr + off, i, DMA_FROM_DEVICE); - cas_page_unmap(addr); RX_USED_ADD(page, 0x100); p += hlen; swivel = 0; @@ -1984,12 +1982,11 @@ static int cas_rx_process_pkt(struct cas *cp, struct cas_rx_comp *rxc, /* make sure we always copy a header */ swivel = 0; if (p == (char *) skb->data) { /* not split */ - addr = cas_page_map(page->buffer); - memcpy(p, addr + off, RX_COPY_MIN); + memcpy(p, page_address(page->buffer) + off, + RX_COPY_MIN); dma_sync_single_for_device(&cp->pdev->dev, page->dma_addr + off, i, DMA_FROM_DEVICE); - cas_page_unmap(addr); off += RX_COPY_MIN; swivel = RX_COPY_MIN; RX_USED_ADD(page, cp->mtu_stride); @@ -2036,10 +2033,8 @@ static int cas_rx_process_pkt(struct cas *cp, struct cas_rx_comp *rxc, RX_USED_ADD(page, hlen + cp->crc_size); } - if (cp->crc_size) { - addr = cas_page_map(page->buffer); - crcaddr = addr + off + hlen; - } + if (cp->crc_size) + crcaddr = page_address(page->buffer) + off + hlen; } else { /* copying packet */ @@ -2061,12 +2056,10 @@ static int cas_rx_process_pkt(struct cas *cp, struct cas_rx_comp *rxc, i += cp->crc_size; dma_sync_single_for_cpu(&cp->pdev->dev, page->dma_addr + off, i, DMA_FROM_DEVICE); - addr = cas_page_map(page->buffer); - memcpy(p, addr + off, i); + memcpy(p, page_address(page->buffer) + off, i); dma_sync_single_for_device(&cp->pdev->dev, page->dma_addr + off, i, DMA_FROM_DEVICE); - cas_page_unmap(addr); if (p == (char *) skb->data) /* not split */ RX_USED_ADD(page, cp->mtu_stride); else @@ -2081,20 +2074,17 @@ static int cas_rx_process_pkt(struct cas *cp, struct cas_rx_comp *rxc, page->dma_addr, dlen + cp->crc_size, DMA_FROM_DEVICE); - addr = cas_page_map(page->buffer); - memcpy(p, addr, dlen + cp->crc_size); + memcpy(p, page_address(page->buffer), dlen + cp->crc_size); dma_sync_single_for_device(&cp->pdev->dev, page->dma_addr, dlen + cp->crc_size, DMA_FROM_DEVICE); - cas_page_unmap(addr); RX_USED_ADD(page, dlen + cp->crc_size); } end_copy_pkt: - if (cp->crc_size) { - addr = NULL; + if (cp->crc_size) crcaddr = skb->data + alloclen; - } + skb_put(skb, alloclen); } @@ -2103,8 +2093,6 @@ end_copy_pkt: /* checksum includes FCS. strip it out. */ csum = csum_fold(csum_partial(crcaddr, cp->crc_size, csum_unfold(csum))); - if (addr) - cas_page_unmap(addr); } skb->protocol = eth_type_trans(skb, cp->dev); if (skb->protocol == htons(ETH_P_IP)) { From e3128591b55a009558cfb5e1df99b719eb60ce62 Mon Sep 17 00:00:00 2001 From: Anirudh Venkataramanan Date: Wed, 23 Nov 2022 12:52:17 -0800 Subject: [PATCH 1250/2002] cassini: Use memcpy_from_page() instead of k[un]map_atomic() kmap_atomic() is being deprecated in favor of kmap_local_page(). Replace the map-memcpy-unmap usage pattern (done using k[un]map_atomic()) with memcpy_from_page(), which internally uses kmap_local_page() and kunmap_local(). This renders the variable 'vaddr' unnecessary, and so remove this too. Note that kmap_atomic() disables preemption and page-fault processing, but kmap_local_page() doesn't. When converting uses of kmap_atomic(), one has to check if the code being executed between the map/unmap implicitly depends on page-faults and/or preemption being disabled. If yes, then code to disable page-faults and/or preemption should also be added for functional correctness. That however doesn't appear to be the case here, so just memcpy_from_page() is used. Also note that the page being mapped is not allocated by the driver, and so the driver doesn't know if the page is in normal memory. This is the reason kmap_local_page() is used (via memcpy_from_page()) as opposed to page_address(). I don't have hardware, so this change has only been compile tested. Cc: Ira Weiny Cc: Fabio M. De Francesco Suggested-by: Fabio M. De Francesco Signed-off-by: Anirudh Venkataramanan Signed-off-by: David S. Miller --- drivers/net/ethernet/sun/cassini.c | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/drivers/net/ethernet/sun/cassini.c b/drivers/net/ethernet/sun/cassini.c index 2f66cfc5e796..4ef05bad4613 100644 --- a/drivers/net/ethernet/sun/cassini.c +++ b/drivers/net/ethernet/sun/cassini.c @@ -90,8 +90,6 @@ #include #include -#define cas_page_map(x) kmap_atomic((x)) -#define cas_page_unmap(x) kunmap_atomic((x)) #define CAS_NCPUS num_online_cpus() #define cas_skb_release(x) netif_rx(x) @@ -2781,18 +2779,14 @@ static inline int cas_xmit_tx_ringN(struct cas *cp, int ring, tabort = cas_calc_tabort(cp, skb_frag_off(fragp), len); if (unlikely(tabort)) { - void *addr; - /* NOTE: len is always > tabort */ cas_write_txd(cp, ring, entry, mapping, len - tabort, ctrl, 0); entry = TX_DESC_NEXT(ring, entry); - - addr = cas_page_map(skb_frag_page(fragp)); - memcpy(tx_tiny_buf(cp, ring, entry), - addr + skb_frag_off(fragp) + len - tabort, - tabort); - cas_page_unmap(addr); + memcpy_from_page(tx_tiny_buf(cp, ring, entry), + skb_frag_page(fragp), + skb_frag_off(fragp) + len - tabort, + tabort); mapping = tx_tiny_map(cp, ring, entry, tentry); len = tabort; } From 350d351389e9f3885697de477986f1f891f95fda Mon Sep 17 00:00:00 2001 From: Anirudh Venkataramanan Date: Wed, 23 Nov 2022 12:52:18 -0800 Subject: [PATCH 1251/2002] sunvnet: Use kmap_local_page() instead of kmap_atomic() kmap_atomic() is being deprecated in favor of kmap_local_page(). Replace kmap_atomic() and kunmap_atomic() with kmap_local_page() and kunmap_local() respectively. Note that kmap_atomic() disables preemption and page-fault processing, but kmap_local_page() doesn't. When converting uses of kmap_atomic(), one has to check if the code being executed between the map/unmap implicitly depends on page-faults and/or preemption being disabled. If yes, then code to disable page-faults and/or preemption should also be added for functional correctness. That however doesn't appear to be the case here, so just kmap_local_page() is used. Also note that the page being mapped is not allocated by the driver, and so the driver doesn't know if the page is in normal memory. This is the reason kmap_local_page() is used as opposed to page_address(). I don't have hardware, so this change has only been compile tested. Cc: Ira Weiny Cc: Fabio M. De Francesco Signed-off-by: Anirudh Venkataramanan Signed-off-by: David S. Miller --- drivers/net/ethernet/sun/sunvnet_common.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/sun/sunvnet_common.c b/drivers/net/ethernet/sun/sunvnet_common.c index 80fde5f06fce..a6211b95ed17 100644 --- a/drivers/net/ethernet/sun/sunvnet_common.c +++ b/drivers/net/ethernet/sun/sunvnet_common.c @@ -1085,13 +1085,13 @@ static inline int vnet_skb_map(struct ldc_channel *lp, struct sk_buff *skb, u8 *vaddr; if (nc < ncookies) { - vaddr = kmap_atomic(skb_frag_page(f)); + vaddr = kmap_local_page(skb_frag_page(f)); blen = skb_frag_size(f); blen += 8 - (blen & 7); err = ldc_map_single(lp, vaddr + skb_frag_off(f), blen, cookies + nc, ncookies - nc, map_perm); - kunmap_atomic(vaddr); + kunmap_local(vaddr); } else { err = -EMSGSIZE; } From c3a8d375f3b996e6e9a32e2b8249b6d704b358e3 Mon Sep 17 00:00:00 2001 From: Anirudh Venkataramanan Date: Wed, 23 Nov 2022 12:52:19 -0800 Subject: [PATCH 1252/2002] net: thunderbolt: Use kmap_local_page() instead of kmap_atomic() kmap_atomic() is being deprecated in favor of kmap_local_page(). Replace kmap_atomic() and kunmap_atomic() with kmap_local_page() and kunmap_local() respectively. Note that kmap_atomic() disables preemption and page-fault processing, but kmap_local_page() doesn't. When converting uses of kmap_atomic(), one has to check if the code being executed between the map/unmap implicitly depends on page-faults and/or preemption being disabled. If yes, then code to disable page-faults and/or preemption should also be added for functional correctness. That however doesn't appear to be the case here, so just kmap_local_page() is used. Also note that the page being mapped is not allocated by the driver, and so the driver doesn't know if the page is in normal memory. This is the reason kmap_local_page() is used as opposed to page_address(). I don't have hardware, so this change has only been compile tested. Cc: Michael Jamet Cc: Mika Westerberg Cc: Yehezkel Bernat Cc: Ira Weiny Cc: Fabio M. De Francesco Signed-off-by: Anirudh Venkataramanan Acked-by: Mika Westerberg Signed-off-by: David S. Miller --- drivers/net/thunderbolt.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/net/thunderbolt.c b/drivers/net/thunderbolt.c index a52ee2bf5575..b20cd370b7f2 100644 --- a/drivers/net/thunderbolt.c +++ b/drivers/net/thunderbolt.c @@ -1051,7 +1051,7 @@ static void *tbnet_kmap_frag(struct sk_buff *skb, unsigned int frag_num, const skb_frag_t *frag = &skb_shinfo(skb)->frags[frag_num]; *len = skb_frag_size(frag); - return kmap_atomic(skb_frag_page(frag)) + skb_frag_off(frag); + return kmap_local_page(skb_frag_page(frag)) + skb_frag_off(frag); } static netdev_tx_t tbnet_start_xmit(struct sk_buff *skb, @@ -1109,7 +1109,7 @@ static netdev_tx_t tbnet_start_xmit(struct sk_buff *skb, dest += len; if (unmap) { - kunmap_atomic(src); + kunmap_local(src); unmap = false; } @@ -1147,7 +1147,7 @@ static netdev_tx_t tbnet_start_xmit(struct sk_buff *skb, dest += len; if (unmap) { - kunmap_atomic(src); + kunmap_local(src); unmap = false; } @@ -1162,7 +1162,7 @@ static netdev_tx_t tbnet_start_xmit(struct sk_buff *skb, memcpy(dest, src, data_len); if (unmap) - kunmap_atomic(src); + kunmap_local(src); if (!tbnet_xmit_csum_and_map(net, skb, frames, frame_index + 1)) goto err_drop; From 4d79f6f34bbb01c6715b31ef457d5ab0390501a1 Mon Sep 17 00:00:00 2001 From: Youghandhar Chintala Date: Thu, 17 Nov 2022 23:35:34 +0530 Subject: [PATCH 1253/2002] wifi: ath10k: Store WLAN firmware version in SMEM image table In a SoC based solution, it would be useful to know the versions of the various binary firmware blobs the system is running on. On a QCOM based SoC, this info can be obtained from socinfo debugfs infrastructure. For this to work, respective subsystem drivers have to export the firmware version information to an SMEM based version information table. Having firmware version information at one place will help quickly figure out the firmware versions of various subsystems on the device instead of going through builds/logs in an event of a system crash. Fill WLAN firmware version information in SMEM version table to be printed as part of socinfo debugfs infrastructure on a Qualcomm based SoC. This change is applicable only for SNOC/QMI based targets. Example: cat /sys/kernel/debug/qcom_socinfo/cnss/name QC_IMAGE_VERSION_STRING=WLAN.HL.3.2.2.c10-00754-QCAHLSWMTPL-1 Tested-on: WCN3990 hw1.0 SNOC WLAN.HL.3.2.2.c10-00754-QCAHLSWMTPL-1 Signed-off-by: Youghandhar Chintala Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20221117180534.2267-1-quic_youghand@quicinc.com --- drivers/net/wireless/ath/ath10k/Kconfig | 1 + drivers/net/wireless/ath/ath10k/qmi.c | 35 +++++++++++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/drivers/net/wireless/ath/ath10k/Kconfig b/drivers/net/wireless/ath/ath10k/Kconfig index ca007b800f75..e0a51dad8e42 100644 --- a/drivers/net/wireless/ath/ath10k/Kconfig +++ b/drivers/net/wireless/ath/ath10k/Kconfig @@ -44,6 +44,7 @@ config ATH10K_SNOC tristate "Qualcomm ath10k SNOC support" depends on ATH10K depends on ARCH_QCOM || COMPILE_TEST + select QCOM_SMEM select QCOM_SCM select QCOM_QMI_HELPERS help diff --git a/drivers/net/wireless/ath/ath10k/qmi.c b/drivers/net/wireless/ath/ath10k/qmi.c index 8930a0f4c42f..3f94fbf83702 100644 --- a/drivers/net/wireless/ath/ath10k/qmi.c +++ b/drivers/net/wireless/ath/ath10k/qmi.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include @@ -22,6 +23,10 @@ #define ATH10K_QMI_CLIENT_ID 0x4b4e454c #define ATH10K_QMI_TIMEOUT 30 +#define SMEM_IMAGE_VERSION_TABLE 469 +#define SMEM_IMAGE_TABLE_CNSS_INDEX 13 +#define SMEM_IMAGE_VERSION_ENTRY_SIZE 128 +#define SMEM_IMAGE_VERSION_NAME_SIZE 75 static int ath10k_qmi_map_msa_permission(struct ath10k_qmi *qmi, struct ath10k_msa_mem_info *mem_info) @@ -536,6 +541,33 @@ int ath10k_qmi_wlan_disable(struct ath10k *ar) return ath10k_qmi_mode_send_sync_msg(ar, QMI_WLFW_OFF_V01); } +static void ath10k_qmi_add_wlan_ver_smem(struct ath10k *ar, const char *fw_build_id) +{ + u8 *table_ptr; + size_t smem_item_size; + const u32 smem_img_idx_wlan = SMEM_IMAGE_TABLE_CNSS_INDEX * + SMEM_IMAGE_VERSION_ENTRY_SIZE; + + table_ptr = qcom_smem_get(QCOM_SMEM_HOST_ANY, + SMEM_IMAGE_VERSION_TABLE, + &smem_item_size); + + if (IS_ERR(table_ptr)) { + ath10k_err(ar, "smem image version table not found\n"); + return; + } + + if (smem_img_idx_wlan + SMEM_IMAGE_VERSION_ENTRY_SIZE > + smem_item_size) { + ath10k_err(ar, "smem block size too small: %zu\n", + smem_item_size); + return; + } + + strscpy(table_ptr + smem_img_idx_wlan, fw_build_id, + SMEM_IMAGE_VERSION_NAME_SIZE); +} + static int ath10k_qmi_cap_send_sync_msg(struct ath10k_qmi *qmi) { struct wlfw_cap_resp_msg_v01 *resp; @@ -606,6 +638,9 @@ static int ath10k_qmi_cap_send_sync_msg(struct ath10k_qmi *qmi) qmi->fw_version, qmi->fw_build_timestamp, qmi->fw_build_id); } + if (resp->fw_build_id_valid) + ath10k_qmi_add_wlan_ver_smem(ar, qmi->fw_build_id); + kfree(resp); return 0; From 7256f28767fa40249254116a4bcb45efff35f31b Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Fri, 18 Nov 2022 13:11:47 -0800 Subject: [PATCH 1254/2002] wifi: carl9170: Replace zero-length array of trailing structs with flex-array Zero-length arrays are deprecated[1] and are being replaced with flexible array members in support of the ongoing efforts to tighten the FORTIFY_SOURCE routines on memcpy(), correctly instrument array indexing with UBSAN_BOUNDS, and to globally enable -fstrict-flex-arrays=3. Replace zero-length array with flexible-array member. This results in no differences in binary output. [1] https://github.com/KSPP/linux/issues/78 Cc: Christian Lamparter Cc: Kalle Valo Cc: "David S. Miller" Cc: Eric Dumazet Cc: Jakub Kicinski Cc: Paolo Abeni Cc: "Gustavo A. R. Silva" Cc: linux-wireless@vger.kernel.org Cc: netdev@vger.kernel.org Signed-off-by: Kees Cook Reviewed-by: Gustavo A. R. Silva Acked-by: Christian Lamparter Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20221118211146.never.395-kees@kernel.org --- drivers/net/wireless/ath/carl9170/fwcmd.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/ath/carl9170/fwcmd.h b/drivers/net/wireless/ath/carl9170/fwcmd.h index 4a500095555c..ff4b3b50250c 100644 --- a/drivers/net/wireless/ath/carl9170/fwcmd.h +++ b/drivers/net/wireless/ath/carl9170/fwcmd.h @@ -118,10 +118,10 @@ struct carl9170_reg_list { } __packed; struct carl9170_write_reg { - struct { + DECLARE_FLEX_ARRAY(struct { __le32 addr; __le32 val; - } regs[0] __packed; + } __packed, regs); } __packed; struct carl9170_write_reg_byte { From a66d79ee0bd5140a64b72cde588f8c83a55a1eb9 Mon Sep 17 00:00:00 2001 From: Sujuan Chen Date: Thu, 24 Nov 2022 11:18:14 +0800 Subject: [PATCH 1255/2002] net: ethernet: mtk_wed: add wcid overwritten support for wed v1 All wed versions should enable the wcid overwritten feature, since the wcid size is controlled by the wlan driver. Tested-by: Sujuan Chen Co-developed-by: Bo Jiao Signed-off-by: Bo Jiao Signed-off-by: Sujuan Chen Signed-off-by: David S. Miller --- drivers/net/ethernet/mediatek/mtk_wed.c | 13 ++++++++----- drivers/net/ethernet/mediatek/mtk_wed_regs.h | 2 ++ include/linux/soc/mediatek/mtk_wed.h | 3 +++ 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/drivers/net/ethernet/mediatek/mtk_wed.c b/drivers/net/ethernet/mediatek/mtk_wed.c index 7d8842378c2b..16d7af88bbac 100644 --- a/drivers/net/ethernet/mediatek/mtk_wed.c +++ b/drivers/net/ethernet/mediatek/mtk_wed.c @@ -526,9 +526,9 @@ mtk_wed_dma_disable(struct mtk_wed_device *dev) MTK_WED_WPDMA_RX_D_RX_DRV_EN); wed_clr(dev, MTK_WED_WDMA_GLO_CFG, MTK_WED_WDMA_GLO_CFG_TX_DDONE_CHK); - - mtk_wed_set_512_support(dev, false); } + + mtk_wed_set_512_support(dev, false); } static void @@ -1290,9 +1290,10 @@ mtk_wed_start(struct mtk_wed_device *dev, u32 irq_mask) if (mtk_wed_rro_cfg(dev)) return; - mtk_wed_set_512_support(dev, dev->wlan.wcid_512); } + mtk_wed_set_512_support(dev, dev->wlan.wcid_512); + mtk_wed_dma_enable(dev); dev->running = true; } @@ -1358,11 +1359,13 @@ mtk_wed_attach(struct mtk_wed_device *dev) } mtk_wed_hw_init_early(dev); - if (hw->version == 1) + if (hw->version == 1) { regmap_update_bits(hw->hifsys, HIFSYS_DMA_AG_MAP, BIT(hw->index), 0); - else + } else { + dev->rev_id = wed_r32(dev, MTK_WED_REV_ID); ret = mtk_wed_wo_init(hw); + } out: if (ret) mtk_wed_detach(dev); diff --git a/drivers/net/ethernet/mediatek/mtk_wed_regs.h b/drivers/net/ethernet/mediatek/mtk_wed_regs.h index 9e39dace95eb..873d50b9a6e6 100644 --- a/drivers/net/ethernet/mediatek/mtk_wed_regs.h +++ b/drivers/net/ethernet/mediatek/mtk_wed_regs.h @@ -20,6 +20,8 @@ struct mtk_wdma_desc { __le32 info; } __packed __aligned(4); +#define MTK_WED_REV_ID 0x004 + #define MTK_WED_RESET 0x008 #define MTK_WED_RESET_TX_BM BIT(0) #define MTK_WED_RESET_TX_FREE_AGENT BIT(4) diff --git a/include/linux/soc/mediatek/mtk_wed.h b/include/linux/soc/mediatek/mtk_wed.h index 8294978f4bca..1b1ef57609f7 100644 --- a/include/linux/soc/mediatek/mtk_wed.h +++ b/include/linux/soc/mediatek/mtk_wed.h @@ -85,6 +85,9 @@ struct mtk_wed_device { int irq; u8 version; + /* used by wlan driver */ + u32 rev_id; + struct mtk_wed_ring tx_ring[MTK_WED_TX_QUEUES]; struct mtk_wed_ring rx_ring[MTK_WED_RX_QUEUES]; struct mtk_wed_ring txfree_ring; From c672e37279896f570cfa44926d57497e8d16033b Mon Sep 17 00:00:00 2001 From: Suman Ghosh Date: Thu, 24 Nov 2022 12:05:48 +0530 Subject: [PATCH 1256/2002] octeontx2-pf: Add support to filter packet based on IP fragment 1. Added support to filter packets based on IP fragment. For IPv4 packets check for ip_flag == 0x20 (more fragment bit set). For IPv6 packets check for next_header == 0x2c (next_header set to 'fragment header for IPv6') 2. Added configuration support from both "ethtool ntuple" and "tc flower". Signed-off-by: Suman Ghosh Signed-off-by: David S. Miller --- .../net/ethernet/marvell/octeontx2/af/mbox.h | 4 +++ .../net/ethernet/marvell/octeontx2/af/npc.h | 2 ++ .../marvell/octeontx2/af/rvu_debugfs.c | 8 ++++++ .../marvell/octeontx2/af/rvu_npc_fs.c | 8 ++++++ .../marvell/octeontx2/nic/otx2_common.h | 3 +++ .../marvell/octeontx2/nic/otx2_flows.c | 25 ++++++++++++++++--- .../ethernet/marvell/octeontx2/nic/otx2_tc.c | 25 +++++++++++++++++++ 7 files changed, 71 insertions(+), 4 deletions(-) diff --git a/drivers/net/ethernet/marvell/octeontx2/af/mbox.h b/drivers/net/ethernet/marvell/octeontx2/af/mbox.h index c7c92c7510fa..d2584ebb7a70 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/mbox.h +++ b/drivers/net/ethernet/marvell/octeontx2/af/mbox.h @@ -1440,6 +1440,10 @@ struct flow_msg { u8 tc; __be16 sport; __be16 dport; + union { + u8 ip_flag; + u8 next_header; + }; }; struct npc_install_flow_req { diff --git a/drivers/net/ethernet/marvell/octeontx2/af/npc.h b/drivers/net/ethernet/marvell/octeontx2/af/npc.h index d027c23b8ef8..9beeead56d7b 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/npc.h +++ b/drivers/net/ethernet/marvell/octeontx2/af/npc.h @@ -185,8 +185,10 @@ enum key_fields { NPC_VLAN_ETYPE_STAG, /* 0x88A8 */ NPC_OUTER_VID, NPC_TOS, + NPC_IPFRAG_IPV4, NPC_SIP_IPV4, NPC_DIP_IPV4, + NPC_IPFRAG_IPV6, NPC_SIP_IPV6, NPC_DIP_IPV6, NPC_IPPROTO_TCP, diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_debugfs.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_debugfs.c index 642e58a04da0..cdb2e6d8ffb8 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_debugfs.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_debugfs.c @@ -2799,6 +2799,14 @@ static void rvu_dbg_npc_mcam_show_flows(struct seq_file *s, seq_printf(s, "%pI6 ", rule->packet.ip6dst); seq_printf(s, "mask %pI6\n", rule->mask.ip6dst); break; + case NPC_IPFRAG_IPV6: + seq_printf(s, "0x%x ", rule->packet.next_header); + seq_printf(s, "mask 0x%x\n", rule->mask.next_header); + break; + case NPC_IPFRAG_IPV4: + seq_printf(s, "0x%x ", rule->packet.ip_flag); + seq_printf(s, "mask 0x%x\n", rule->mask.ip_flag); + break; case NPC_SPORT_TCP: case NPC_SPORT_UDP: case NPC_SPORT_SCTP: diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_fs.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_fs.c index f3fecd2a4015..006beb5cf98d 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_fs.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_fs.c @@ -26,8 +26,10 @@ static const char * const npc_flow_names[] = { [NPC_VLAN_ETYPE_STAG] = "vlan ether type stag", [NPC_OUTER_VID] = "outer vlan id", [NPC_TOS] = "tos", + [NPC_IPFRAG_IPV4] = "fragmented IPv4 header ", [NPC_SIP_IPV4] = "ipv4 source ip", [NPC_DIP_IPV4] = "ipv4 destination ip", + [NPC_IPFRAG_IPV6] = "fragmented IPv6 header ", [NPC_SIP_IPV6] = "ipv6 source ip", [NPC_DIP_IPV6] = "ipv6 destination ip", [NPC_IPPROTO_TCP] = "ip proto tcp", @@ -484,8 +486,10 @@ do { \ * Example: Source IP is 4 bytes and starts at 12th byte of IP header */ NPC_SCAN_HDR(NPC_TOS, NPC_LID_LC, NPC_LT_LC_IP, 1, 1); + NPC_SCAN_HDR(NPC_IPFRAG_IPV4, NPC_LID_LC, NPC_LT_LC_IP, 6, 1); NPC_SCAN_HDR(NPC_SIP_IPV4, NPC_LID_LC, NPC_LT_LC_IP, 12, 4); NPC_SCAN_HDR(NPC_DIP_IPV4, NPC_LID_LC, NPC_LT_LC_IP, 16, 4); + NPC_SCAN_HDR(NPC_IPFRAG_IPV6, NPC_LID_LC, NPC_LT_LC_IP6_EXT, 6, 1); NPC_SCAN_HDR(NPC_SIP_IPV6, NPC_LID_LC, NPC_LT_LC_IP6, 8, 16); NPC_SCAN_HDR(NPC_DIP_IPV6, NPC_LID_LC, NPC_LT_LC_IP6, 24, 16); NPC_SCAN_HDR(NPC_SPORT_UDP, NPC_LID_LD, NPC_LT_LD_UDP, 0, 2); @@ -899,6 +903,8 @@ do { \ NPC_WRITE_FLOW(NPC_ETYPE, etype, ntohs(pkt->etype), 0, ntohs(mask->etype), 0); NPC_WRITE_FLOW(NPC_TOS, tos, pkt->tos, 0, mask->tos, 0); + NPC_WRITE_FLOW(NPC_IPFRAG_IPV4, ip_flag, pkt->ip_flag, 0, + mask->ip_flag, 0); NPC_WRITE_FLOW(NPC_SIP_IPV4, ip4src, ntohl(pkt->ip4src), 0, ntohl(mask->ip4src), 0); NPC_WRITE_FLOW(NPC_DIP_IPV4, ip4dst, ntohl(pkt->ip4dst), 0, @@ -919,6 +925,8 @@ do { \ NPC_WRITE_FLOW(NPC_OUTER_VID, vlan_tci, ntohs(pkt->vlan_tci), 0, ntohs(mask->vlan_tci), 0); + NPC_WRITE_FLOW(NPC_IPFRAG_IPV6, next_header, pkt->next_header, 0, + mask->next_header, 0); npc_update_ipv6_flow(rvu, entry, features, pkt, mask, output, intf); npc_update_vlan_features(rvu, entry, features, intf); diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h index 282db6fe3b08..99d0da7ba750 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h @@ -28,6 +28,9 @@ #include "otx2_devlink.h" #include +/* IPv4 flag more fragment bit */ +#define IPV4_FLAG_MORE 0x20 + /* PCI device IDs */ #define PCI_DEVID_OCTEONTX2_RVU_PF 0xA063 #define PCI_DEVID_OCTEONTX2_RVU_VF 0xA064 diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_flows.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_flows.c index 13aa79efee03..684cb8ec9f21 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_flows.c +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_flows.c @@ -711,6 +711,11 @@ static int otx2_prepare_ipv6_flow(struct ethtool_rx_flow_spec *fsp, sizeof(pmask->ip6dst)); req->features |= BIT_ULL(NPC_DIP_IPV6); } + if (ipv6_usr_hdr->l4_proto == IPPROTO_FRAGMENT) { + pkt->next_header = ipv6_usr_hdr->l4_proto; + pmask->next_header = ipv6_usr_mask->l4_proto; + req->features |= BIT_ULL(NPC_IPFRAG_IPV6); + } pkt->etype = cpu_to_be16(ETH_P_IPV6); pmask->etype = cpu_to_be16(0xFFFF); req->features |= BIT_ULL(NPC_ETYPE); @@ -891,10 +896,22 @@ static int otx2_prepare_flow_request(struct ethtool_rx_flow_spec *fsp, req->features |= BIT_ULL(NPC_OUTER_VID); } - /* Not Drop/Direct to queue but use action in default entry */ - if (fsp->m_ext.data[1] && - fsp->h_ext.data[1] == cpu_to_be32(OTX2_DEFAULT_ACTION)) - req->op = NIX_RX_ACTION_DEFAULT; + if (fsp->m_ext.data[1]) { + if (flow_type == IP_USER_FLOW) { + if (be32_to_cpu(fsp->h_ext.data[1]) != IPV4_FLAG_MORE) + return -EINVAL; + + pkt->ip_flag = be32_to_cpu(fsp->h_ext.data[1]); + pmask->ip_flag = be32_to_cpu(fsp->m_ext.data[1]); + req->features |= BIT_ULL(NPC_IPFRAG_IPV4); + } else if (fsp->h_ext.data[1] == + cpu_to_be32(OTX2_DEFAULT_ACTION)) { + /* Not Drop/Direct to queue but use action + * in default entry + */ + req->op = NIX_RX_ACTION_DEFAULT; + } + } } if (fsp->flow_type & FLOW_MAC_EXT && diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_tc.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_tc.c index e64318c110fd..e421714524c2 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_tc.c +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_tc.c @@ -532,6 +532,31 @@ static int otx2_tc_prepare_flow(struct otx2_nic *nic, struct otx2_tc_flow *node, req->features |= BIT_ULL(NPC_IPPROTO_ICMP6); } + if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_CONTROL)) { + struct flow_match_control match; + + flow_rule_match_control(rule, &match); + if (match.mask->flags & FLOW_DIS_FIRST_FRAG) { + NL_SET_ERR_MSG_MOD(extack, "HW doesn't support frag first/later"); + return -EOPNOTSUPP; + } + + if (match.mask->flags & FLOW_DIS_IS_FRAGMENT) { + if (ntohs(flow_spec->etype) == ETH_P_IP) { + flow_spec->ip_flag = IPV4_FLAG_MORE; + flow_mask->ip_flag = 0xff; + req->features |= BIT_ULL(NPC_IPFRAG_IPV4); + } else if (ntohs(flow_spec->etype) == ETH_P_IPV6) { + flow_spec->next_header = IPPROTO_FRAGMENT; + flow_mask->next_header = 0xff; + req->features |= BIT_ULL(NPC_IPFRAG_IPV6); + } else { + NL_SET_ERR_MSG_MOD(extack, "flow-type should be either IPv4 and IPv6"); + return -EOPNOTSUPP; + } + } + } + if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ETH_ADDRS)) { struct flow_match_eth_addrs match; From 895b3b06efc285c1245242e9638b9ae251dc13ec Mon Sep 17 00:00:00 2001 From: Ziyang Xuan Date: Sat, 19 Nov 2022 13:19:00 +0800 Subject: [PATCH 1257/2002] wifi: plfxlc: fix potential memory leak in __lf_x_usb_enable_rx() urbs does not be freed in exception paths in __lf_x_usb_enable_rx(). That will trigger memory leak. To fix it, add kfree() for urbs within "error" label. Compile tested only. Fixes: 68d57a07bfe5 ("wireless: add plfxlc driver for pureLiFi X, XL, XC devices") Signed-off-by: Ziyang Xuan Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20221119051900.1192401-1-william.xuanziyang@huawei.com --- drivers/net/wireless/purelifi/plfxlc/usb.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/wireless/purelifi/plfxlc/usb.c b/drivers/net/wireless/purelifi/plfxlc/usb.c index 39e54b3787d6..76d0a778636a 100644 --- a/drivers/net/wireless/purelifi/plfxlc/usb.c +++ b/drivers/net/wireless/purelifi/plfxlc/usb.c @@ -247,6 +247,7 @@ error: for (i = 0; i < RX_URBS_COUNT; i++) free_rx_urb(urbs[i]); } + kfree(urbs); return r; } From 7927afb5e27baac694f585b59c436ba323528dc2 Mon Sep 17 00:00:00 2001 From: Bitterblue Smith Date: Mon, 21 Nov 2022 22:56:58 +0200 Subject: [PATCH 1258/2002] wifi: rtl8xxxu: Fix use after rcu_read_unlock in rtl8xxxu_bss_info_changed Commit a8b5aef2cca1 ("wifi: rtl8xxxu: gen2: Enable 40 MHz channel width") introduced a line where the pointer returned by ieee80211_find_sta() is used after rcu_read_unlock(). Move rcu_read_unlock() a bit lower to fix this. Fixes: a8b5aef2cca1 ("wifi: rtl8xxxu: gen2: Enable 40 MHz channel width") Signed-off-by: Bitterblue Smith Reviewed-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/3c82ad09-7593-3be1-1d2c-e58505fb43cb@gmail.com --- drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c index e8fcd531c437..28f136064297 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c +++ b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c @@ -4645,7 +4645,6 @@ rtl8xxxu_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif, if (sta->deflink.ht_cap.cap & (IEEE80211_HT_CAP_SGI_40 | IEEE80211_HT_CAP_SGI_20)) sgi = 1; - rcu_read_unlock(); highest_rate = fls(ramask) - 1; if (highest_rate < DESC_RATE_MCS0) { @@ -4670,6 +4669,7 @@ rtl8xxxu_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif, else rarpt->txrate.bw = RATE_INFO_BW_20; } + rcu_read_unlock(); bit_rate = cfg80211_calculate_bitrate(&rarpt->txrate); rarpt->bit_rate = bit_rate; rarpt->desc_rate = highest_rate; From 3ca7f0b2523cac5d436cd09758db07e10b1a322a Mon Sep 17 00:00:00 2001 From: Lukas Bulwahn Date: Tue, 22 Nov 2022 14:12:48 +0100 Subject: [PATCH 1259/2002] wifi: b43: remove reference to removed config B43_PCMCIA Commit 399500da18f7 ("ssb: pick PCMCIA host code support from b43 driver") removes the config B43_PCMCIA. Clean up the last reference to this removed config B43_PCMCIA in the b43_print_driverinfo() function. Signed-off-by: Lukas Bulwahn Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20221122131248.23738-1-lukas.bulwahn@gmail.com --- drivers/net/wireless/broadcom/b43/main.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/drivers/net/wireless/broadcom/b43/main.c b/drivers/net/wireless/broadcom/b43/main.c index 298febbdffc0..92ca0b2ca286 100644 --- a/drivers/net/wireless/broadcom/b43/main.c +++ b/drivers/net/wireless/broadcom/b43/main.c @@ -5784,15 +5784,12 @@ void b43_controller_restart(struct b43_wldev *dev, const char *reason) static void b43_print_driverinfo(void) { - const char *feat_pci = "", *feat_pcmcia = "", *feat_nphy = "", + const char *feat_pci = "", *feat_nphy = "", *feat_leds = "", *feat_sdio = ""; #ifdef CONFIG_B43_PCI_AUTOSELECT feat_pci = "P"; #endif -#ifdef CONFIG_B43_PCMCIA - feat_pcmcia = "M"; -#endif #ifdef CONFIG_B43_PHY_N feat_nphy = "N"; #endif @@ -5803,9 +5800,8 @@ static void b43_print_driverinfo(void) feat_sdio = "S"; #endif printk(KERN_INFO "Broadcom 43xx driver loaded " - "[ Features: %s%s%s%s%s ]\n", - feat_pci, feat_pcmcia, feat_nphy, - feat_leds, feat_sdio); + "[ Features: %s%s%s%s ]\n", + feat_pci, feat_nphy, feat_leds, feat_sdio); } static int __init b43_init(void) From 823092a53556ebf8656623d0e857626d30fe1e18 Mon Sep 17 00:00:00 2001 From: Ji-Pin Jou Date: Thu, 24 Nov 2022 14:44:42 +0800 Subject: [PATCH 1260/2002] wifi: rtw88: fix race condition when doing H2C command For SDIO/USB interface, since the tranferring speed is slower than that in PCIE, it may have race condition when the driver sets down H2C command to the FW. In the function rtw_fw_send_h2c_command, before the patch, box_reg is written first, then box_ex_reg is written. FW starts to work and fetch the value of box_ex_reg, when the most significant byte of box_reg(4 bytes) is written. Meanwhile, for SDIO/USB interface, since the transferring speed is slow, the driver is still in writing the new value of box_ex_reg through the bus, and FW may get the wrong value of box_ex_reg at the moment. To prevent the above driver/FW racing situation, box_ex_reg is written first then box_reg. Furthermore, it is written in 4 bytes at a time, instead of written in one byte one by one. It can increase the speed for SDIO/USB interface. Signed-off-by: Ji-Pin Jou Signed-off-by: Ping-Ke Shih Tested-by: Sascha Hauer Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20221124064442.28042-1-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw88/fw.c | 8 +++----- drivers/net/wireless/realtek/rtw88/fw.h | 5 +++++ 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw88/fw.c b/drivers/net/wireless/realtek/rtw88/fw.c index 0b5f903c0f36..b290811d75e1 100644 --- a/drivers/net/wireless/realtek/rtw88/fw.c +++ b/drivers/net/wireless/realtek/rtw88/fw.c @@ -311,10 +311,10 @@ EXPORT_SYMBOL(rtw_fw_c2h_cmd_isr); static void rtw_fw_send_h2c_command(struct rtw_dev *rtwdev, u8 *h2c) { + struct rtw_h2c_cmd *h2c_cmd = (struct rtw_h2c_cmd *)h2c; u8 box; u8 box_state; u32 box_reg, box_ex_reg; - int idx; int ret; rtw_dbg(rtwdev, RTW_DBG_FW, @@ -356,10 +356,8 @@ static void rtw_fw_send_h2c_command(struct rtw_dev *rtwdev, goto out; } - for (idx = 0; idx < 4; idx++) - rtw_write8(rtwdev, box_reg + idx, h2c[idx]); - for (idx = 0; idx < 4; idx++) - rtw_write8(rtwdev, box_ex_reg + idx, h2c[idx + 4]); + rtw_write32(rtwdev, box_ex_reg, le32_to_cpu(h2c_cmd->msg_ext)); + rtw_write32(rtwdev, box_reg, le32_to_cpu(h2c_cmd->msg)); if (++rtwdev->h2c.last_box_num >= 4) rtwdev->h2c.last_box_num = 0; diff --git a/drivers/net/wireless/realtek/rtw88/fw.h b/drivers/net/wireless/realtek/rtw88/fw.h index a5a965803a3c..bca610dc99ab 100644 --- a/drivers/net/wireless/realtek/rtw88/fw.h +++ b/drivers/net/wireless/realtek/rtw88/fw.h @@ -81,6 +81,11 @@ struct rtw_c2h_adaptivity { u8 option; } __packed; +struct rtw_h2c_cmd { + __le32 msg; + __le32 msg_ext; +} __packed; + enum rtw_rsvd_packet_type { RSVD_BEACON, RSVD_DUMMY, From 26d7cc0abe6181148c50982ec2e8e0e46f74b419 Mon Sep 17 00:00:00 2001 From: Ilan Peer Date: Tue, 22 Nov 2022 22:10:32 +0200 Subject: [PATCH 1261/2002] wifi: iwlwifi: mvm: Advertise EHT capabilities Add support for advertising EHT capabilities if supported by device SKU and not disabled by module parameters. Signed-off-by: Ilan Peer Link: https://lore.kernel.org/r/20221122220713.6bc00d851794.I214005645f3da21d8f2458a70355deeca04a19e8@changeid Signed-off-by: Gregory Greenman --- drivers/net/wireless/intel/iwlwifi/iwl-drv.c | 3 + .../wireless/intel/iwlwifi/iwl-eeprom-parse.h | 3 +- .../wireless/intel/iwlwifi/iwl-modparams.h | 1 + .../wireless/intel/iwlwifi/iwl-nvm-parse.c | 178 ++++++++++++++++-- 4 files changed, 167 insertions(+), 18 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-drv.c b/drivers/net/wireless/intel/iwlwifi/iwl-drv.c index a2203f661321..ab7065c93826 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-drv.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-drv.c @@ -1971,3 +1971,6 @@ MODULE_PARM_DESC(remove_when_gone, module_param_named(disable_11ax, iwlwifi_mod_params.disable_11ax, bool, S_IRUGO); MODULE_PARM_DESC(disable_11ax, "Disable HE capabilities (default: false)"); + +module_param_named(disable_11be, iwlwifi_mod_params.disable_11be, bool, 0444); +MODULE_PARM_DESC(disable_11be, "Disable EHT capabilities (default: false)"); diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-eeprom-parse.h b/drivers/net/wireless/intel/iwlwifi/iwl-eeprom-parse.h index 0efffb6eeb1e..baa643386018 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-eeprom-parse.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-eeprom-parse.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* - * Copyright (C) 2005-2014, 2018 Intel Corporation + * Copyright (C) 2005-2014, 2018, 2020-2022 Intel Corporation * Copyright (C) 2015 Intel Mobile Communications GmbH */ #ifndef __iwl_eeprom_parse_h__ @@ -31,6 +31,7 @@ struct iwl_nvm_data { bool sku_cap_amt_enable; bool sku_cap_ipan_enable; bool sku_cap_mimo_disabled; + bool sku_cap_11be_enable; u16 radio_cfg_type; u8 radio_cfg_step; diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-modparams.h b/drivers/net/wireless/intel/iwlwifi/iwl-modparams.h index d0b4d02bdab9..1cf26ab4f488 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-modparams.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-modparams.h @@ -62,6 +62,7 @@ enum iwl_uapsd_disable { * @disable_11ac: disable VHT capabilities, default = false. * @remove_when_gone: remove an inaccessible device from the PCIe bus. * @enable_ini: enable new FW debug infratructure (INI TLVs) + * @disable_11be: disable EHT capabilities, default = false. */ struct iwl_mod_params { int swcrypto; diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c index 9040da3dcce3..2b43fff4d3f9 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c @@ -546,7 +546,7 @@ static const u8 iwl_vendor_caps[] = { 0x00 }; -static const struct ieee80211_sband_iftype_data iwl_he_capa[] = { +static const struct ieee80211_sband_iftype_data iwl_he_eht_capa[] = { { .types_mask = BIT(NL80211_IFTYPE_STATION), .he_cap = { @@ -631,6 +631,78 @@ static const struct ieee80211_sband_iftype_data iwl_he_capa[] = { */ .ppe_thres = {0x61, 0x1c, 0xc7, 0x71}, }, + .eht_cap = { + .has_eht = true, + .eht_cap_elem = { + .mac_cap_info[0] = + IEEE80211_EHT_MAC_CAP0_EPCS_PRIO_ACCESS | + IEEE80211_EHT_MAC_CAP0_OM_CONTROL | + IEEE80211_EHT_MAC_CAP0_TRIG_TXOP_SHARING_MODE1 | + IEEE80211_EHT_MAC_CAP0_TRIG_TXOP_SHARING_MODE2, + .phy_cap_info[0] = + IEEE80211_EHT_PHY_CAP0_242_TONE_RU_GT20MHZ | + IEEE80211_EHT_PHY_CAP0_NDP_4_EHT_LFT_32_GI | + IEEE80211_EHT_PHY_CAP0_PARTIAL_BW_UL_MU_MIMO | + IEEE80211_EHT_PHY_CAP0_SU_BEAMFORMEE | + IEEE80211_EHT_PHY_CAP0_BEAMFORMEE_SS_80MHZ_MASK, + .phy_cap_info[1] = + IEEE80211_EHT_PHY_CAP1_BEAMFORMEE_SS_80MHZ_MASK | + IEEE80211_EHT_PHY_CAP1_BEAMFORMEE_SS_160MHZ_MASK | + IEEE80211_EHT_PHY_CAP1_BEAMFORMEE_SS_320MHZ_MASK, + .phy_cap_info[3] = + IEEE80211_EHT_PHY_CAP3_NG_16_SU_FEEDBACK | + IEEE80211_EHT_PHY_CAP3_NG_16_MU_FEEDBACK | + IEEE80211_EHT_PHY_CAP3_CODEBOOK_4_2_SU_FDBK | + IEEE80211_EHT_PHY_CAP3_CODEBOOK_7_5_MU_FDBK | + IEEE80211_EHT_PHY_CAP3_TRIG_SU_BF_FDBK | + IEEE80211_EHT_PHY_CAP3_TRIG_MU_BF_PART_BW_FDBK | + IEEE80211_EHT_PHY_CAP3_TRIG_CQI_FDBK, + + .phy_cap_info[4] = + IEEE80211_EHT_PHY_CAP4_PART_BW_DL_MU_MIMO | + IEEE80211_EHT_PHY_CAP4_POWER_BOOST_FACT_SUPP | + IEEE80211_EHT_PHY_CAP4_EHT_MU_PPDU_4_EHT_LTF_08_GI, + .phy_cap_info[5] = + IEEE80211_EHT_PHY_CAP5_NON_TRIG_CQI_FEEDBACK | + IEEE80211_EHT_PHY_CAP5_TX_LESS_242_TONE_RU_SUPP | + IEEE80211_EHT_PHY_CAP5_RX_LESS_242_TONE_RU_SUPP | + IEEE80211_EHT_PHY_CAP5_PPE_THRESHOLD_PRESENT, + .phy_cap_info[6] = + IEEE80211_EHT_PHY_CAP6_MCS15_SUPP_MASK | + IEEE80211_EHT_PHY_CAP6_EHT_DUP_6GHZ_SUPP, + .phy_cap_info[8] = + IEEE80211_EHT_PHY_CAP8_RX_1024QAM_WIDER_BW_DL_OFDMA | + IEEE80211_EHT_PHY_CAP8_RX_4096QAM_WIDER_BW_DL_OFDMA, + }, + + /* For all MCS and bandwidth, set 2 NSS for both Tx and + * Rx - note we don't set the only_20mhz, but due to this + * being a union, it gets set correctly anyway. + */ + .eht_mcs_nss_supp = { + .bw._80 = { + .rx_tx_mcs9_max_nss = 0x22, + .rx_tx_mcs11_max_nss = 0x22, + .rx_tx_mcs13_max_nss = 0x22, + }, + .bw._160 = { + .rx_tx_mcs9_max_nss = 0x22, + .rx_tx_mcs11_max_nss = 0x22, + .rx_tx_mcs13_max_nss = 0x22, + }, + .bw._320 = { + .rx_tx_mcs9_max_nss = 0x22, + .rx_tx_mcs11_max_nss = 0x22, + .rx_tx_mcs13_max_nss = 0x22, + }, + }, + + /* + * PPE thresholds for NSS = 2, and RU index bitmap set + * to 0xc. + */ + .eht_ppe_thres = {0xc1, 0x0e, 0xe0 } + }, }, { .types_mask = BIT(NL80211_IFTYPE_AP), @@ -687,6 +759,49 @@ static const struct ieee80211_sband_iftype_data iwl_he_capa[] = { */ .ppe_thres = {0x61, 0x1c, 0xc7, 0x71}, }, + .eht_cap = { + .has_eht = true, + .eht_cap_elem = { + .mac_cap_info[0] = + IEEE80211_EHT_MAC_CAP0_EPCS_PRIO_ACCESS | + IEEE80211_EHT_MAC_CAP0_OM_CONTROL | + IEEE80211_EHT_MAC_CAP0_TRIG_TXOP_SHARING_MODE1 | + IEEE80211_EHT_MAC_CAP0_TRIG_TXOP_SHARING_MODE2, + .phy_cap_info[0] = + IEEE80211_EHT_PHY_CAP0_242_TONE_RU_GT20MHZ | + IEEE80211_EHT_PHY_CAP0_NDP_4_EHT_LFT_32_GI, + .phy_cap_info[5] = + IEEE80211_EHT_PHY_CAP5_PPE_THRESHOLD_PRESENT, + }, + + /* For all MCS and bandwidth, set 2 NSS for both Tx and + * Rx - note we don't set the only_20mhz, but due to this + * being a union, it gets set correctly anyway. + */ + .eht_mcs_nss_supp = { + .bw._80 = { + .rx_tx_mcs9_max_nss = 0x22, + .rx_tx_mcs11_max_nss = 0x22, + .rx_tx_mcs13_max_nss = 0x22, + }, + .bw._160 = { + .rx_tx_mcs9_max_nss = 0x22, + .rx_tx_mcs11_max_nss = 0x22, + .rx_tx_mcs13_max_nss = 0x22, + }, + .bw._320 = { + .rx_tx_mcs9_max_nss = 0x22, + .rx_tx_mcs11_max_nss = 0x22, + .rx_tx_mcs13_max_nss = 0x22, + }, + }, + + /* + * PPE thresholds for NSS = 2, and RU index bitmap set + * to 0xc. + */ + .eht_ppe_thres = {0xc1, 0x0e, 0xe0 } + }, }, }; @@ -738,6 +853,7 @@ static void iwl_init_he_6ghz_capa(struct iwl_trans *trans, static void iwl_nvm_fixup_sband_iftd(struct iwl_trans *trans, + struct iwl_nvm_data *data, struct ieee80211_supported_band *sband, struct ieee80211_sband_iftype_data *iftype_data, u8 tx_chains, u8 rx_chains, @@ -745,6 +861,9 @@ iwl_nvm_fixup_sband_iftd(struct iwl_trans *trans, { bool is_ap = iftype_data->types_mask & BIT(NL80211_IFTYPE_AP); + if (!data->sku_cap_11be_enable || iwlwifi_mod_params.disable_11be) + iftype_data->eht_cap.has_eht = false; + /* Advertise an A-MPDU exponent extension based on * operating band */ @@ -765,19 +884,44 @@ iwl_nvm_fixup_sband_iftd(struct iwl_trans *trans, iftype_data->he_cap.he_cap_elem.phy_cap_info[5] |= IEEE80211_HE_PHY_CAP5_BEAMFORMEE_NUM_SND_DIM_UNDER_80MHZ_2 | IEEE80211_HE_PHY_CAP5_BEAMFORMEE_NUM_SND_DIM_ABOVE_80MHZ_2; - if (!is_ap) + if (!is_ap) { iftype_data->he_cap.he_cap_elem.phy_cap_info[7] |= IEEE80211_HE_PHY_CAP7_MAX_NC_2; - } else if (!is_ap) { - /* If not 2x2, we need to indicate 1x1 in the - * Midamble RX Max NSTS - but not for AP mode - */ - iftype_data->he_cap.he_cap_elem.phy_cap_info[1] &= - ~IEEE80211_HE_PHY_CAP1_MIDAMBLE_RX_TX_MAX_NSTS; - iftype_data->he_cap.he_cap_elem.phy_cap_info[2] &= - ~IEEE80211_HE_PHY_CAP2_MIDAMBLE_RX_TX_MAX_NSTS; - iftype_data->he_cap.he_cap_elem.phy_cap_info[7] |= - IEEE80211_HE_PHY_CAP7_MAX_NC_1; + + if (iftype_data->eht_cap.has_eht) { + /* + * Set the number of sounding dimensions for each + * bandwidth to 1 to indicate the maximal supported + * value of TXVECTOR parameter NUM_STS of 2 + */ + iftype_data->eht_cap.eht_cap_elem.phy_cap_info[2] |= 0x49; + + /* + * Set the MAX NC to 1 to indicate sounding feedback of + * 2 supported by the beamfomee. + */ + iftype_data->eht_cap.eht_cap_elem.phy_cap_info[4] |= 0x10; + } + } + } else { + if (iftype_data->eht_cap.has_eht) { + struct ieee80211_eht_mcs_nss_supp *mcs_nss = + &iftype_data->eht_cap.eht_mcs_nss_supp; + + memset(mcs_nss, 0x11, sizeof(*mcs_nss)); + } + + if (!is_ap) { + /* If not 2x2, we need to indicate 1x1 in the + * Midamble RX Max NSTS - but not for AP mode + */ + iftype_data->he_cap.he_cap_elem.phy_cap_info[1] &= + ~IEEE80211_HE_PHY_CAP1_MIDAMBLE_RX_TX_MAX_NSTS; + iftype_data->he_cap.he_cap_elem.phy_cap_info[2] &= + ~IEEE80211_HE_PHY_CAP2_MIDAMBLE_RX_TX_MAX_NSTS; + iftype_data->he_cap.he_cap_elem.phy_cap_info[7] |= + IEEE80211_HE_PHY_CAP7_MAX_NC_1; + } } switch (CSR_HW_RFID_TYPE(trans->hw_rf_id)) { @@ -816,8 +960,8 @@ static void iwl_init_he_hw_capab(struct iwl_trans *trans, if (WARN_ON(sband->iftype_data)) return; - BUILD_BUG_ON(sizeof(data->iftd.low) != sizeof(iwl_he_capa)); - BUILD_BUG_ON(sizeof(data->iftd.high) != sizeof(iwl_he_capa)); + BUILD_BUG_ON(sizeof(data->iftd.low) != sizeof(iwl_he_eht_capa)); + BUILD_BUG_ON(sizeof(data->iftd.high) != sizeof(iwl_he_eht_capa)); switch (sband->band) { case NL80211_BAND_2GHZ: @@ -832,13 +976,13 @@ static void iwl_init_he_hw_capab(struct iwl_trans *trans, return; } - memcpy(iftype_data, iwl_he_capa, sizeof(iwl_he_capa)); + memcpy(iftype_data, iwl_he_eht_capa, sizeof(iwl_he_eht_capa)); sband->iftype_data = iftype_data; - sband->n_iftype_data = ARRAY_SIZE(iwl_he_capa); + sband->n_iftype_data = ARRAY_SIZE(iwl_he_eht_capa); for (i = 0; i < sband->n_iftype_data; i++) - iwl_nvm_fixup_sband_iftd(trans, sband, &iftype_data[i], + iwl_nvm_fixup_sband_iftd(trans, data, sband, &iftype_data[i], tx_chains, rx_chains, fw); iwl_init_he_6ghz_capa(trans, data, sband, tx_chains, rx_chains); From 7ac875753a11589bfbebe707941ac8a11306ba25 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 22 Nov 2022 22:10:33 +0200 Subject: [PATCH 1262/2002] wifi: iwlwifi: mvm: support 320 MHz PHY configuration Support 320 MHz PHY configuration and while doing so rewrite the code since we'd otherwise double the number of cases in the switch statement. Signed-off-by: Johannes Berg Link: https://lore.kernel.org/r/20221122220713.964db911b733.If56c94a9bf20c050f35d2421b680e400a9f4aeb8@changeid Signed-off-by: Gregory Greenman --- .../wireless/intel/iwlwifi/fw/api/phy-ctxt.h | 33 ++++++------ .../net/wireless/intel/iwlwifi/mvm/mac80211.c | 2 +- .../net/wireless/intel/iwlwifi/mvm/phy-ctxt.c | 54 +++++++++---------- 3 files changed, 44 insertions(+), 45 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/phy-ctxt.h b/drivers/net/wireless/intel/iwlwifi/fw/api/phy-ctxt.h index e66f77924f83..2f7d8558becd 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/phy-ctxt.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/phy-ctxt.h @@ -13,10 +13,12 @@ #define PHY_BAND_6 (2) /* Supported channel width, vary if there is VHT support */ -#define PHY_VHT_CHANNEL_MODE20 (0x0) -#define PHY_VHT_CHANNEL_MODE40 (0x1) -#define PHY_VHT_CHANNEL_MODE80 (0x2) -#define PHY_VHT_CHANNEL_MODE160 (0x3) +#define IWL_PHY_CHANNEL_MODE20 0x0 +#define IWL_PHY_CHANNEL_MODE40 0x1 +#define IWL_PHY_CHANNEL_MODE80 0x2 +#define IWL_PHY_CHANNEL_MODE160 0x3 +/* and 320 MHz for EHT */ +#define IWL_PHY_CHANNEL_MODE320 0x4 /* * Control channel position: @@ -24,20 +26,17 @@ * For VHT - bit-2 marks if the control is lower/upper relative to center-freq * bits-1:0 mark the distance from the center freq. for 20Mhz, offset is 0. * center_freq - * | - * 40Mhz |_______|_______| - * 80Mhz |_______|_______|_______|_______| - * 160Mhz |_______|_______|_______|_______|_______|_______|_______|_______| - * code 011 010 001 000 | 100 101 110 111 + * For EHT - bit-3 is used for extended distance + * | + * 40Mhz |____|____| + * 80Mhz |____|____|____|____| + * 160Mhz |____|____|____|____|____|____|____|____| + * 320MHz |____|____|____|____|____|____|____|____|____|____|____|____|____|____|____|____| + * code 1011 1010 1001 1000 0011 0010 0001 0000 0100 0101 0110 0111 1100 1101 1110 1111 */ -#define PHY_VHT_CTRL_POS_1_BELOW (0x0) -#define PHY_VHT_CTRL_POS_2_BELOW (0x1) -#define PHY_VHT_CTRL_POS_3_BELOW (0x2) -#define PHY_VHT_CTRL_POS_4_BELOW (0x3) -#define PHY_VHT_CTRL_POS_1_ABOVE (0x4) -#define PHY_VHT_CTRL_POS_2_ABOVE (0x5) -#define PHY_VHT_CTRL_POS_3_ABOVE (0x6) -#define PHY_VHT_CTRL_POS_4_ABOVE (0x7) +#define IWL_PHY_CTRL_POS_ABOVE 0x4 +#define IWL_PHY_CTRL_POS_OFFS_EXT 0x8 +#define IWL_PHY_CTRL_POS_OFFS_MSK 0x3 /* * struct iwl_fw_channel_info_v1 - channel information diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c index a81dd488caa8..57aa10466637 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c @@ -3777,7 +3777,7 @@ static int iwl_mvm_send_aux_roc_cmd(struct iwl_mvm *mvm, /* Set the channel info data */ iwl_mvm_set_chan_info(mvm, &aux_roc_req.channel_info, channel->hw_value, iwl_mvm_phy_band_from_nl80211(channel->band), - PHY_VHT_CHANNEL_MODE20, + IWL_PHY_CHANNEL_MODE20, 0); /* Set the time and duration */ diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/phy-ctxt.c b/drivers/net/wireless/intel/iwlwifi/mvm/phy-ctxt.c index a3cefbc43e80..06f4203fb989 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/phy-ctxt.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/phy-ctxt.c @@ -14,16 +14,18 @@ u8 iwl_mvm_get_channel_width(struct cfg80211_chan_def *chandef) switch (chandef->width) { case NL80211_CHAN_WIDTH_20_NOHT: case NL80211_CHAN_WIDTH_20: - return PHY_VHT_CHANNEL_MODE20; + return IWL_PHY_CHANNEL_MODE20; case NL80211_CHAN_WIDTH_40: - return PHY_VHT_CHANNEL_MODE40; + return IWL_PHY_CHANNEL_MODE40; case NL80211_CHAN_WIDTH_80: - return PHY_VHT_CHANNEL_MODE80; + return IWL_PHY_CHANNEL_MODE80; case NL80211_CHAN_WIDTH_160: - return PHY_VHT_CHANNEL_MODE160; + return IWL_PHY_CHANNEL_MODE160; + case NL80211_CHAN_WIDTH_320: + return IWL_PHY_CHANNEL_MODE320; default: WARN(1, "Invalid channel width=%u", chandef->width); - return PHY_VHT_CHANNEL_MODE20; + return IWL_PHY_CHANNEL_MODE20; } } @@ -33,34 +35,32 @@ u8 iwl_mvm_get_channel_width(struct cfg80211_chan_def *chandef) */ u8 iwl_mvm_get_ctrl_pos(struct cfg80211_chan_def *chandef) { - switch (chandef->chan->center_freq - chandef->center_freq1) { - case -70: - return PHY_VHT_CTRL_POS_4_BELOW; - case -50: - return PHY_VHT_CTRL_POS_3_BELOW; - case -30: - return PHY_VHT_CTRL_POS_2_BELOW; - case -10: - return PHY_VHT_CTRL_POS_1_BELOW; - case 10: - return PHY_VHT_CTRL_POS_1_ABOVE; - case 30: - return PHY_VHT_CTRL_POS_2_ABOVE; - case 50: - return PHY_VHT_CTRL_POS_3_ABOVE; - case 70: - return PHY_VHT_CTRL_POS_4_ABOVE; - default: - WARN(1, "Invalid channel definition"); - fallthrough; - case 0: + int offs = chandef->chan->center_freq - chandef->center_freq1; + int abs_offs = abs(offs); + u8 ret; + + if (offs == 0) { /* * The FW is expected to check the control channel position only * when in HT/VHT and the channel width is not 20MHz. Return * this value as the default one. */ - return PHY_VHT_CTRL_POS_1_BELOW; + return 0; } + + /* this results in a value 0-7, i.e. fitting into 0b0111 */ + ret = (abs_offs - 10) / 20; + /* + * But we need the value to be in 0b1011 because 0b0100 is + * IWL_PHY_CTRL_POS_ABOVE, so shift bit 2 up to land in + * IWL_PHY_CTRL_POS_OFFS_EXT (0b1000) + */ + ret = (ret & IWL_PHY_CTRL_POS_OFFS_MSK) | + ((ret & BIT(2)) << 1); + /* and add the above bit */ + ret |= (offs > 0) * IWL_PHY_CTRL_POS_ABOVE; + + return ret; } /* From 701404f1091d8b4cdccfa835ceb05a3982b2c614 Mon Sep 17 00:00:00 2001 From: Mordechay Goodstein Date: Tue, 22 Nov 2022 22:10:34 +0200 Subject: [PATCH 1263/2002] wifi: iwlwifi: rs: add support for parsing max MCS per NSS/BW in 11be Rate scale needs to be configured to what MCS it can use per BW and NSS, this is done by parsing our capabilities of TX and peer's capabilities of RX and setting the minimum for rate scale usage. Also do some cleanup removing redundant enum defines not used by FW/Driver. Signed-off-by: Mordechay Goodstein Link: https://lore.kernel.org/r/20221122220713.f71f3b4c4583.I7b5e8071df91146c4bee3e9bcb7ad62595b275e1@changeid Signed-off-by: Gregory Greenman --- .../net/wireless/intel/iwlwifi/fw/api/rs.h | 10 +- drivers/net/wireless/intel/iwlwifi/fw/rs.c | 2 + .../net/wireless/intel/iwlwifi/mvm/rs-fw.c | 125 +++++++++++++++++- 3 files changed, 129 insertions(+), 8 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/rs.h b/drivers/net/wireless/intel/iwlwifi/fw/api/rs.h index 687f804c46b7..ddacd5b45aea 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/rs.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/rs.h @@ -36,14 +36,14 @@ enum iwl_tlc_mng_cfg_flags { * @IWL_TLC_MNG_CH_WIDTH_40MHZ: 40MHZ channel * @IWL_TLC_MNG_CH_WIDTH_80MHZ: 80MHZ channel * @IWL_TLC_MNG_CH_WIDTH_160MHZ: 160MHZ channel - * @IWL_TLC_MNG_CH_WIDTH_LAST: maximum value + * @IWL_TLC_MNG_CH_WIDTH_320MHZ: 320MHZ channel */ enum iwl_tlc_mng_cfg_cw { IWL_TLC_MNG_CH_WIDTH_20MHZ, IWL_TLC_MNG_CH_WIDTH_40MHZ, IWL_TLC_MNG_CH_WIDTH_80MHZ, IWL_TLC_MNG_CH_WIDTH_160MHZ, - IWL_TLC_MNG_CH_WIDTH_LAST = IWL_TLC_MNG_CH_WIDTH_160MHZ, + IWL_TLC_MNG_CH_WIDTH_320MHZ, }; /** @@ -64,8 +64,7 @@ enum iwl_tlc_mng_cfg_chains { * @IWL_TLC_MNG_MODE_HT: enable HT * @IWL_TLC_MNG_MODE_VHT: enable VHT * @IWL_TLC_MNG_MODE_HE: enable HE - * @IWL_TLC_MNG_MODE_INVALID: invalid value - * @IWL_TLC_MNG_MODE_NUM: a count of possible modes + * @IWL_TLC_MNG_MODE_EHT: enable EHT */ enum iwl_tlc_mng_cfg_mode { IWL_TLC_MNG_MODE_CCK = 0, @@ -74,8 +73,7 @@ enum iwl_tlc_mng_cfg_mode { IWL_TLC_MNG_MODE_HT, IWL_TLC_MNG_MODE_VHT, IWL_TLC_MNG_MODE_HE, - IWL_TLC_MNG_MODE_INVALID, - IWL_TLC_MNG_MODE_NUM = IWL_TLC_MNG_MODE_INVALID, + IWL_TLC_MNG_MODE_EHT, }; /** diff --git a/drivers/net/wireless/intel/iwlwifi/fw/rs.c b/drivers/net/wireless/intel/iwlwifi/fw/rs.c index a835214611ce..e128d2e07f38 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/rs.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/rs.c @@ -218,6 +218,8 @@ int rs_pretty_print_rate(char *buf, int bufsz, const u32 rate) type = "HT"; else if (format == RATE_MCS_HE_MSK) type = "HE"; + else if (format == RATE_MCS_EHT_MSK) + type = "EHT"; else type = "Unknown"; /* shouldn't happen */ diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rs-fw.c b/drivers/net/wireless/intel/iwlwifi/mvm/rs-fw.c index 2e9081cb6627..f30eeab5505b 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/rs-fw.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/rs-fw.c @@ -9,9 +9,11 @@ #include "iwl-op-mode.h" #include "mvm.h" -static u8 rs_fw_bw_from_sta_bw(struct ieee80211_sta *sta) +static u8 rs_fw_bw_from_sta_bw(const struct ieee80211_sta *sta) { switch (sta->deflink.bandwidth) { + case IEEE80211_STA_RX_BW_320: + return IWL_TLC_MNG_CH_WIDTH_320MHZ; case IEEE80211_STA_RX_BW_160: return IWL_TLC_MNG_CH_WIDTH_160MHZ; case IEEE80211_STA_RX_BW_80: @@ -238,6 +240,122 @@ rs_fw_he_set_enabled_rates(const struct ieee80211_sta *sta, } } +static u8 rs_fw_eht_max_nss(u8 rx_nss, u8 tx_nss) +{ + u8 tx = u8_get_bits(tx_nss, IEEE80211_EHT_MCS_NSS_TX); + u8 rx = u8_get_bits(rx_nss, IEEE80211_EHT_MCS_NSS_RX); + /* the max nss that can be used, + * is the min with our tx capa and the peer rx capa. + */ + return min(tx, rx); +} + +#define MAX_NSS_MCS(mcs_num, rx, tx) \ + rs_fw_eht_max_nss((rx)->rx_tx_mcs ##mcs_num## _max_nss, \ + (tx)->rx_tx_mcs ##mcs_num## _max_nss) + +static void rs_fw_set_eht_mcs_nss(__le16 ht_rates[][3], + enum IWL_TLC_MCS_PER_BW bw, + u8 max_nss, u16 mcs_msk) +{ + if (max_nss >= 2) + ht_rates[IWL_TLC_NSS_2][bw] |= cpu_to_le16(mcs_msk); + + if (max_nss >= 1) + ht_rates[IWL_TLC_NSS_1][bw] |= cpu_to_le16(mcs_msk); +} + +static const +struct ieee80211_eht_mcs_nss_supp_bw * +rs_fw_rs_mcs2eht_mcs(enum IWL_TLC_MCS_PER_BW bw, + const struct ieee80211_eht_mcs_nss_supp *eht_mcs) +{ + switch (bw) { + case IWL_TLC_MCS_PER_BW_80: + return &eht_mcs->bw._80; + case IWL_TLC_MCS_PER_BW_160: + return &eht_mcs->bw._160; + case IWL_TLC_MCS_PER_BW_320: + return &eht_mcs->bw._320; + default: + return NULL; + } +} + +static void rs_fw_eht_set_enabled_rates(const struct ieee80211_sta *sta, + struct ieee80211_supported_band *sband, + struct iwl_tlc_config_cmd_v4 *cmd) +{ + /* peer RX mcs capa */ + const struct ieee80211_eht_mcs_nss_supp *eht_rx_mcs = + &sta->deflink.eht_cap.eht_mcs_nss_supp; + /* our TX mcs capa */ + const struct ieee80211_eht_mcs_nss_supp *eht_tx_mcs = + &sband->iftype_data->eht_cap.eht_mcs_nss_supp; + + enum IWL_TLC_MCS_PER_BW bw; + struct ieee80211_eht_mcs_nss_supp_20mhz_only mcs_rx_20; + struct ieee80211_eht_mcs_nss_supp_20mhz_only mcs_tx_20; + + /* peer is 20Mhz only */ + if (!(sta->deflink.he_cap.he_cap_elem.phy_cap_info[0] & + IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_MASK_ALL)) { + mcs_rx_20 = eht_rx_mcs->only_20mhz; + } else { + mcs_rx_20.rx_tx_mcs7_max_nss = eht_rx_mcs->bw._80.rx_tx_mcs9_max_nss; + mcs_rx_20.rx_tx_mcs9_max_nss = eht_rx_mcs->bw._80.rx_tx_mcs9_max_nss; + mcs_rx_20.rx_tx_mcs11_max_nss = eht_rx_mcs->bw._80.rx_tx_mcs11_max_nss; + mcs_rx_20.rx_tx_mcs13_max_nss = eht_rx_mcs->bw._80.rx_tx_mcs13_max_nss; + } + + /* nic is 20Mhz only */ + if (!(sband->iftype_data->he_cap.he_cap_elem.phy_cap_info[0] & + IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_MASK_ALL)) { + mcs_tx_20 = eht_tx_mcs->only_20mhz; + } else { + mcs_tx_20.rx_tx_mcs7_max_nss = eht_tx_mcs->bw._80.rx_tx_mcs9_max_nss; + mcs_tx_20.rx_tx_mcs9_max_nss = eht_tx_mcs->bw._80.rx_tx_mcs9_max_nss; + mcs_tx_20.rx_tx_mcs11_max_nss = eht_tx_mcs->bw._80.rx_tx_mcs11_max_nss; + mcs_tx_20.rx_tx_mcs13_max_nss = eht_tx_mcs->bw._80.rx_tx_mcs13_max_nss; + } + + /* rates for 20/40/80 bw */ + bw = IWL_TLC_MCS_PER_BW_80; + rs_fw_set_eht_mcs_nss(cmd->ht_rates, bw, + MAX_NSS_MCS(7, &mcs_rx_20, &mcs_tx_20), GENMASK(7, 0)); + rs_fw_set_eht_mcs_nss(cmd->ht_rates, bw, + MAX_NSS_MCS(9, &mcs_rx_20, &mcs_tx_20), GENMASK(9, 8)); + rs_fw_set_eht_mcs_nss(cmd->ht_rates, bw, + MAX_NSS_MCS(11, &mcs_rx_20, &mcs_tx_20), GENMASK(11, 10)); + rs_fw_set_eht_mcs_nss(cmd->ht_rates, bw, + MAX_NSS_MCS(13, &mcs_rx_20, &mcs_tx_20), GENMASK(13, 12)); + + /* rate for 160/320 bw */ + for (bw = IWL_TLC_MCS_PER_BW_160; bw <= IWL_TLC_MCS_PER_BW_320; bw++) { + const struct ieee80211_eht_mcs_nss_supp_bw *mcs_rx = + rs_fw_rs_mcs2eht_mcs(bw, eht_rx_mcs); + const struct ieee80211_eht_mcs_nss_supp_bw *mcs_tx = + rs_fw_rs_mcs2eht_mcs(bw, eht_tx_mcs); + + /* got unsuppored index for bw */ + if (!mcs_rx || !mcs_tx) + continue; + + rs_fw_set_eht_mcs_nss(cmd->ht_rates, bw, + MAX_NSS_MCS(9, mcs_rx, mcs_tx), GENMASK(9, 0)); + rs_fw_set_eht_mcs_nss(cmd->ht_rates, bw, + MAX_NSS_MCS(11, mcs_rx, mcs_tx), GENMASK(11, 10)); + rs_fw_set_eht_mcs_nss(cmd->ht_rates, bw, + MAX_NSS_MCS(13, mcs_rx, mcs_tx), GENMASK(13, 12)); + } + + /* the station support only a single receive chain */ + if (sta->deflink.smps_mode == IEEE80211_SMPS_STATIC || + sta->deflink.rx_nss < 2) + memset(cmd->ht_rates[IWL_TLC_NSS_2], 0, + sizeof(cmd->ht_rates[IWL_TLC_NSS_2])); +} + static void rs_fw_set_supp_rates(struct ieee80211_sta *sta, struct ieee80211_supported_band *sband, struct iwl_tlc_config_cmd_v4 *cmd) @@ -258,7 +376,10 @@ static void rs_fw_set_supp_rates(struct ieee80211_sta *sta, cmd->mode = IWL_TLC_MNG_MODE_NON_HT; /* HT/VHT rates */ - if (he_cap->has_he) { + if (sta->deflink.eht_cap.has_eht) { + cmd->mode = IWL_TLC_MNG_MODE_EHT; + rs_fw_eht_set_enabled_rates(sta, sband, cmd); + } else if (he_cap->has_he) { cmd->mode = IWL_TLC_MNG_MODE_HE; rs_fw_he_set_enabled_rates(sta, sband, cmd); } else if (vht_cap->vht_supported) { From 64e7dd3f9be68a53e760f184a6d8049a1121a060 Mon Sep 17 00:00:00 2001 From: Mordechay Goodstein Date: Tue, 22 Nov 2022 22:10:35 +0200 Subject: [PATCH 1264/2002] wifi: iwlwifi: mvm: add support for EHT 1K aggregation size In 11be we can receive 1K aggregation size so update our max HW rx aggregation for mac80211 usage. Signed-off-by: Mordechay Goodstein Link: https://lore.kernel.org/r/20221122220713.ef0431f64975.Ie4d90c376c822694f4523ff0fb5731f20004c24d@changeid Signed-off-by: Gregory Greenman --- drivers/net/wireless/intel/iwlwifi/mvm/ops.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c index 9699433137bc..2a4db13c9dcf 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c @@ -1077,6 +1077,7 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg, static const u8 no_reclaim_cmds[] = { TX_CMD, }; + u32 max_agg; size_t scan_size; u32 min_backoff; struct iwl_mvm_csme_conn_info *csme_conn_info __maybe_unused; @@ -1098,12 +1099,17 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg, if (!hw) return NULL; - hw->max_rx_aggregation_subframes = IEEE80211_MAX_AMPDU_BUF_HE; + if (trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_BZ) + max_agg = IEEE80211_MAX_AMPDU_BUF_EHT; + else + max_agg = IEEE80211_MAX_AMPDU_BUF_HE; + + hw->max_rx_aggregation_subframes = max_agg; if (cfg->max_tx_agg_size) hw->max_tx_aggregation_subframes = cfg->max_tx_agg_size; else - hw->max_tx_aggregation_subframes = IEEE80211_MAX_AMPDU_BUF_HE; + hw->max_tx_aggregation_subframes = max_agg; op_mode = hw->priv; From cb63eb438ee9264b086ec7e0b2f01c56983ebe61 Mon Sep 17 00:00:00 2001 From: Miri Korenblit Date: Tue, 22 Nov 2022 22:10:36 +0200 Subject: [PATCH 1265/2002] wifi: iwlwifi: mvm: support PPE Thresholds for EHT In an EHT connection fill in the HE context command with PPE Thresholds field from EHT Capabilities IE. If PPE Thresholds field presents only in HE Capabilities IE, take the thresholds from there. If it's not present in both - set the Common Nominal Packet Padding field from EHT Capabilities IE. Signed-off-by: Miri Korenblit Link: https://lore.kernel.org/r/20221122220713.062ac86110f2.I0bcd5ee35f4d987a49f15501985f90e0d3ce7590@changeid Signed-off-by: Gregory Greenman --- .../net/wireless/intel/iwlwifi/fw/api/mac.h | 6 +- .../net/wireless/intel/iwlwifi/mvm/mac80211.c | 177 +++++++++++++++--- 2 files changed, 153 insertions(+), 30 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/mac.h b/drivers/net/wireless/intel/iwlwifi/fw/api/mac.h index 9b7caf968346..e3eda251c728 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/mac.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/mac.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* - * Copyright (C) 2012-2014, 2018-2021 Intel Corporation + * Copyright (C) 2012-2014, 2018-2022 Intel Corporation * Copyright (C) 2017 Intel Deutschland GmbH */ #ifndef __iwl_fw_api_mac_h__ @@ -398,7 +398,7 @@ struct iwl_he_backoff_conf { * @IWL_HE_PKT_EXT_64QAM: 64-QAM * @IWL_HE_PKT_EXT_256QAM: 256-QAM * @IWL_HE_PKT_EXT_1024QAM: 1024-QAM - * @IWL_HE_PKT_EXT_RESERVED: reserved value + * @IWL_HE_PKT_EXT_4096QAM: 4096-QAM, for EHT only * @IWL_HE_PKT_EXT_NONE: not defined */ enum iwl_he_pkt_ext_constellations { @@ -408,7 +408,7 @@ enum iwl_he_pkt_ext_constellations { IWL_HE_PKT_EXT_64QAM, IWL_HE_PKT_EXT_256QAM, IWL_HE_PKT_EXT_1024QAM, - IWL_HE_PKT_EXT_RESERVED, + IWL_HE_PKT_EXT_4096QAM, IWL_HE_PKT_EXT_NONE, }; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c index 57aa10466637..3fba69554f83 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c @@ -1825,7 +1825,8 @@ static u8 iwl_mvm_he_get_ppe_val(u8 *ppe, u8 ppe_pos_bit) static void iwl_mvm_parse_ppe(struct iwl_mvm *mvm, struct iwl_he_pkt_ext_v2 *pkt_ext, u8 nss, - u8 ru_index_bitmap, u8 *ppe, u8 ppe_pos_bit) + u8 ru_index_bitmap, u8 *ppe, u8 ppe_pos_bit, + bool inheritance) { int i; @@ -1851,14 +1852,25 @@ static void iwl_mvm_parse_ppe(struct iwl_mvm *mvm, bw++) { ru_index_tmp >>= 1; - if (!(ru_index_tmp & 1)) - continue; + /* + * According to the 11be spec, if for a specific BW the PPE Thresholds + * isn't present - it should inherit the thresholds from the last + * BW for which we had PPE Thresholds. In 11ax though, we don't have + * this inheritance - continue in this case + */ + if (!(ru_index_tmp & 1)) { + if (inheritance) + goto set_thresholds; + else + continue; + } high_th = iwl_mvm_he_get_ppe_val(ppe, ppe_pos_bit); ppe_pos_bit += IEEE80211_PPE_THRES_INFO_PPET_SIZE; low_th = iwl_mvm_he_get_ppe_val(ppe, ppe_pos_bit); ppe_pos_bit += IEEE80211_PPE_THRES_INFO_PPET_SIZE; +set_thresholds: pkt_ext->pkt_ext_qam_th[i][bw][0] = low_th; pkt_ext->pkt_ext_qam_th[i][bw][1] = high_th; } @@ -1867,7 +1879,8 @@ static void iwl_mvm_parse_ppe(struct iwl_mvm *mvm, static void iwl_mvm_set_pkt_ext_from_he_ppe(struct iwl_mvm *mvm, struct ieee80211_sta *sta, - struct iwl_he_pkt_ext_v2 *pkt_ext) + struct iwl_he_pkt_ext_v2 *pkt_ext, + bool inheritance) { u8 nss = (sta->deflink.he_cap.ppe_thres[0] & IEEE80211_PPE_THRES_NSS_MASK) + 1; u8 *ppe = &sta->deflink.he_cap.ppe_thres[0]; @@ -1877,7 +1890,8 @@ static void iwl_mvm_set_pkt_ext_from_he_ppe(struct iwl_mvm *mvm, /* Starting after PPE header */ u8 ppe_pos_bit = IEEE80211_HE_PPE_THRES_INFO_HEADER_SIZE; - iwl_mvm_parse_ppe(mvm, pkt_ext, nss, ru_index_bitmap, ppe, ppe_pos_bit); + iwl_mvm_parse_ppe(mvm, pkt_ext, nss, ru_index_bitmap, ppe, ppe_pos_bit, + inheritance); } static void iwl_mvm_set_pkt_ext_from_nominal_padding(struct iwl_he_pkt_ext_v2 *pkt_ext, @@ -1888,16 +1902,18 @@ static void iwl_mvm_set_pkt_ext_from_nominal_padding(struct iwl_he_pkt_ext_v2 *p int high_th = -1; int i; + /* all the macros are the same for EHT and HE */ switch (nominal_padding) { - case IEEE80211_HE_PHY_CAP9_NOMINAL_PKT_PADDING_0US: + case IEEE80211_EHT_PHY_CAP5_COMMON_NOMINAL_PKT_PAD_0US: low_th = IWL_HE_PKT_EXT_NONE; high_th = IWL_HE_PKT_EXT_NONE; break; - case IEEE80211_HE_PHY_CAP9_NOMINAL_PKT_PADDING_8US: + case IEEE80211_EHT_PHY_CAP5_COMMON_NOMINAL_PKT_PAD_8US: low_th = IWL_HE_PKT_EXT_BPSK; high_th = IWL_HE_PKT_EXT_NONE; break; - case IEEE80211_HE_PHY_CAP9_NOMINAL_PKT_PADDING_16US: + case IEEE80211_EHT_PHY_CAP5_COMMON_NOMINAL_PKT_PAD_16US: + case IEEE80211_EHT_PHY_CAP5_COMMON_NOMINAL_PKT_PAD_20US: low_th = IWL_HE_PKT_EXT_NONE; high_th = IWL_HE_PKT_EXT_BPSK; break; @@ -1920,6 +1936,31 @@ static void iwl_mvm_set_pkt_ext_from_nominal_padding(struct iwl_he_pkt_ext_v2 *p } } +static void iwl_mvm_get_optimal_ppe_info(struct iwl_he_pkt_ext_v2 *pkt_ext, + u8 nominal_padding) +{ + int i; + + for (i = 0; i < MAX_HE_SUPP_NSS; i++) { + u8 bw; + + for (bw = 0; bw < ARRAY_SIZE(pkt_ext->pkt_ext_qam_th[i]); + bw++) { + u8 *qam_th = &pkt_ext->pkt_ext_qam_th[i][bw][0]; + + if (nominal_padding > + IEEE80211_EHT_PHY_CAP5_COMMON_NOMINAL_PKT_PAD_8US && + qam_th[1] == IWL_HE_PKT_EXT_NONE) + qam_th[1] = IWL_HE_PKT_EXT_4096QAM; + else if (nominal_padding == + IEEE80211_EHT_PHY_CAP5_COMMON_NOMINAL_PKT_PAD_8US && + qam_th[0] == IWL_HE_PKT_EXT_NONE && + qam_th[1] == IWL_HE_PKT_EXT_NONE) + qam_th[0] = IWL_HE_PKT_EXT_4096QAM; + } + } +} + static void iwl_mvm_cfg_he_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif, u8 sta_id) { @@ -1943,6 +1984,7 @@ static void iwl_mvm_cfg_he_sta(struct iwl_mvm *mvm, struct ieee80211_chanctx_conf *chanctx_conf; const struct ieee80211_supported_band *sband; void *cmd; + u8 nominal_padding; if (!fw_has_api(&mvm->fw->ucode_capa, IWL_UCODE_TLV_API_MBSSID_HE)) ver = 1; @@ -2032,22 +2074,96 @@ static void iwl_mvm_cfg_he_sta(struct iwl_mvm *mvm, memset(&sta_ctxt_cmd.pkt_ext, IWL_HE_PKT_EXT_NONE, sizeof(sta_ctxt_cmd.pkt_ext)); - /* If PPE Thresholds exist, parse them into a FW-familiar format. */ - if (sta->deflink.he_cap.he_cap_elem.phy_cap_info[6] & - IEEE80211_HE_PHY_CAP6_PPE_THRESHOLD_PRESENT) { - iwl_mvm_set_pkt_ext_from_he_ppe(mvm, sta, - &sta_ctxt_cmd.pkt_ext); - flags |= STA_CTXT_HE_PACKET_EXT; - /* PPE Thresholds doesn't exist - set the API PPE values - * according to Common Nominal Packet Padding fiels. */ - } else { - u8 nominal_padding = - u8_get_bits(sta->deflink.he_cap.he_cap_elem.phy_cap_info[9], - IEEE80211_HE_PHY_CAP9_NOMINAL_PKT_PADDING_MASK); - if (nominal_padding != IEEE80211_HE_PHY_CAP9_NOMINAL_PKT_PADDING_RESERVED) + if (sta->deflink.eht_cap.has_eht) { + nominal_padding = + u8_get_bits(sta->deflink.eht_cap.eht_cap_elem.phy_cap_info[5], + IEEE80211_EHT_PHY_CAP5_COMMON_NOMINAL_PKT_PAD_MASK); + + /* If PPE Thresholds exists, parse them into a FW-familiar format. */ + if (sta->deflink.eht_cap.eht_cap_elem.phy_cap_info[5] & + IEEE80211_EHT_PHY_CAP5_PPE_THRESHOLD_PRESENT) { + u8 nss = (sta->deflink.eht_cap.eht_ppe_thres[0] & + IEEE80211_EHT_PPE_THRES_NSS_MASK) + 1; + u8 *ppe = &sta->deflink.eht_cap.eht_ppe_thres[0]; + u8 ru_index_bitmap = + u16_get_bits(*ppe, + IEEE80211_EHT_PPE_THRES_RU_INDEX_BITMASK_MASK); + /* Starting after PPE header */ + u8 ppe_pos_bit = IEEE80211_EHT_PPE_THRES_INFO_HEADER_SIZE; + + iwl_mvm_parse_ppe(mvm, + &sta_ctxt_cmd.pkt_ext, + nss, ru_index_bitmap, ppe, + ppe_pos_bit, true); + flags |= STA_CTXT_HE_PACKET_EXT; + /* EHT PPE Thresholds doesn't exist - set the API according to HE PPE Tresholds*/ + } else if (sta->deflink.he_cap.he_cap_elem.phy_cap_info[6] & + IEEE80211_HE_PHY_CAP6_PPE_THRESHOLD_PRESENT) { + struct iwl_he_pkt_ext_v2 *pkt_ext = + &sta_ctxt_cmd.pkt_ext; + + /* + * Even though HE Capabilities IE doesn't contain PPE + * Thresholds for BW 320Mhz, thresholds for this BW will + * be filled in with the same values as 160Mhz, due to + * the inheritance, as required. + */ + iwl_mvm_set_pkt_ext_from_he_ppe(mvm, sta, pkt_ext, + true); + + /* + * According to the requirements, for MCSs 12-13 the maximum value between + * HE PPE Threshold and Common Nominal Packet Padding needs to be taken + */ + iwl_mvm_get_optimal_ppe_info(pkt_ext, nominal_padding); + + flags |= STA_CTXT_HE_PACKET_EXT; + + /* + * if PPE Thresholds doesn't present in both EHT IE and HE IE - + * take the Thresholds from Common Nominal Packet Padding field + */ + } else { iwl_mvm_set_pkt_ext_from_nominal_padding(&sta_ctxt_cmd.pkt_ext, nominal_padding, &flags); + } + } else if (sta->deflink.he_cap.has_he) { + /* If PPE Thresholds exist, parse them into a FW-familiar format. */ + if (sta->deflink.he_cap.he_cap_elem.phy_cap_info[6] & + IEEE80211_HE_PHY_CAP6_PPE_THRESHOLD_PRESENT) { + iwl_mvm_set_pkt_ext_from_he_ppe(mvm, sta, + &sta_ctxt_cmd.pkt_ext, + false); + flags |= STA_CTXT_HE_PACKET_EXT; + /* + * PPE Thresholds doesn't exist - set the API PPE values + * according to Common Nominal Packet Padding field. + */ + } else { + nominal_padding = + u8_get_bits(sta->deflink.he_cap.he_cap_elem.phy_cap_info[9], + IEEE80211_HE_PHY_CAP9_NOMINAL_PKT_PADDING_MASK); + if (nominal_padding != IEEE80211_HE_PHY_CAP9_NOMINAL_PKT_PADDING_RESERVED) + iwl_mvm_set_pkt_ext_from_nominal_padding(&sta_ctxt_cmd.pkt_ext, + nominal_padding, + &flags); + } + } + + for (i = 0; i < MAX_HE_SUPP_NSS; i++) { + int bw; + + for (bw = 0; + bw < ARRAY_SIZE(sta_ctxt_cmd.pkt_ext.pkt_ext_qam_th[i]); + bw++) { + u8 *qam_th = + &sta_ctxt_cmd.pkt_ext.pkt_ext_qam_th[i][bw][0]; + + IWL_DEBUG_HT(mvm, + "PPE table: nss[%d] bw[%d] PPET8 = %d, PPET16 = %d\n", + i, bw, qam_th[0], qam_th[1]); + } } if (sta->deflink.he_cap.he_cap_elem.mac_cap_info[2] & @@ -2195,8 +2311,10 @@ static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm, * interface was added. */ if (changes & BSS_CHANGED_ASSOC && vif->cfg.assoc) { - if (vif->bss_conf.he_support && - !iwlwifi_mod_params.disable_11ax) + if ((vif->bss_conf.he_support && + !iwlwifi_mod_params.disable_11ax) || + (vif->bss_conf.eht_support && + !iwlwifi_mod_params.disable_11be)) iwl_mvm_cfg_he_sta(mvm, vif, mvmvif->ap_sta_id); iwl_mvm_mac_ctxt_recalc_tsf_id(mvm, vif); @@ -2204,8 +2322,11 @@ static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm, /* Update MU EDCA params */ if (changes & BSS_CHANGED_QOS && mvmvif->associated && - vif->cfg.assoc && vif->bss_conf.he_support && - !iwlwifi_mod_params.disable_11ax) + vif->cfg.assoc && + ((vif->bss_conf.he_support && + !iwlwifi_mod_params.disable_11ax) || + (vif->bss_conf.eht_support && + !iwlwifi_mod_params.disable_11be))) iwl_mvm_cfg_he_sta(mvm, vif, mvmvif->ap_sta_id); /* @@ -3218,8 +3339,10 @@ static int iwl_mvm_mac_sta_state(struct ieee80211_hw *hw, vif->bss_conf.he_support = sta->deflink.he_cap.has_he; mvmvif->ap_assoc_sta_count++; iwl_mvm_mac_ctxt_changed(mvm, vif, false, NULL); - if (vif->bss_conf.he_support && - !iwlwifi_mod_params.disable_11ax) + if ((vif->bss_conf.he_support && + !iwlwifi_mod_params.disable_11ax) || + (vif->bss_conf.eht_support && + !iwlwifi_mod_params.disable_11be)) iwl_mvm_cfg_he_sta(mvm, vif, mvm_sta->sta_id); } else if (vif->type == NL80211_IFTYPE_STATION) { vif->bss_conf.he_support = sta->deflink.he_cap.has_he; From 3895f1609c2e4e274a9a02b412e318a3b11e8032 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 22 Nov 2022 22:10:37 +0200 Subject: [PATCH 1266/2002] wifi: iwlwifi: mvm: set HE PHY bandwidth according to band The bits are reserved on the opposite bands, so we shouldn't always send them, only the 2G bit on 2.4 GHz and the 5G bits on 5/6GHz. Fix that. Signed-off-by: Johannes Berg Link: https://lore.kernel.org/r/20221122220713.0a075d00c796.Ib4cac0b7f90dfadebceceb1e07c8cdfd7a4138f6@changeid Signed-off-by: Gregory Greenman --- .../wireless/intel/iwlwifi/iwl-nvm-parse.c | 27 ++++++++++++------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c index 2b43fff4d3f9..e83382ded8af 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2005-2014, 2018-2021 Intel Corporation + * Copyright (C) 2005-2014, 2018-2022 Intel Corporation * Copyright (C) 2013-2015 Intel Mobile Communications GmbH * Copyright (C) 2016-2017 Intel Deutschland GmbH */ @@ -571,10 +571,6 @@ static const struct ieee80211_sband_iftype_data iwl_he_eht_capa[] = { IEEE80211_HE_MAC_CAP5_UL_2x996_TONE_RU | IEEE80211_HE_MAC_CAP5_HE_DYNAMIC_SM_PS | IEEE80211_HE_MAC_CAP5_HT_VHT_TRIG_FRAME_RX, - .phy_cap_info[0] = - IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_IN_2G | - IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G | - IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G, .phy_cap_info[1] = IEEE80211_HE_PHY_CAP1_PREAMBLE_PUNC_RX_MASK | IEEE80211_HE_PHY_CAP1_DEVICE_CLASS_A | @@ -716,9 +712,6 @@ static const struct ieee80211_sband_iftype_data iwl_he_eht_capa[] = { IEEE80211_HE_MAC_CAP1_MULTI_TID_AGG_RX_QOS_8, .mac_cap_info[3] = IEEE80211_HE_MAC_CAP3_OMI_CONTROL, - .phy_cap_info[0] = - IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_IN_2G | - IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G, .phy_cap_info[1] = IEEE80211_HE_PHY_CAP1_LDPC_CODING_IN_PAYLOAD, .phy_cap_info[2] = @@ -874,9 +867,23 @@ iwl_nvm_fixup_sband_iftd(struct iwl_trans *trans, iftype_data->he_cap.he_cap_elem.mac_cap_info[3] |= IEEE80211_HE_MAC_CAP3_MAX_AMPDU_LEN_EXP_EXT_3; - if (is_ap && iwlwifi_mod_params.nvm_file) + switch (sband->band) { + case NL80211_BAND_2GHZ: iftype_data->he_cap.he_cap_elem.phy_cap_info[0] |= - IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G; + IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_IN_2G; + break; + case NL80211_BAND_6GHZ: + case NL80211_BAND_5GHZ: + iftype_data->he_cap.he_cap_elem.phy_cap_info[0] |= + IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G; + if (!is_ap || iwlwifi_mod_params.nvm_file) + iftype_data->he_cap.he_cap_elem.phy_cap_info[0] |= + IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G; + break; + default: + WARN_ON(1); + break; + } if ((tx_chains & rx_chains) == ANT_AB) { iftype_data->he_cap.he_cap_elem.phy_cap_info[2] |= From 35ea5f619480e81efb652c8e1cab5fd1a3964ae3 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 22 Nov 2022 22:10:38 +0200 Subject: [PATCH 1267/2002] wifi: iwlwifi: mvm: advertise 320 MHz in 6 GHz only conditionally We can't have it in AP mode unless for debug, since we don't have 160 MHz in HE PHY capabilities, and also set it only in 6 GHz even if (unlike in HE) it's always defined. Signed-off-by: Johannes Berg Link: https://lore.kernel.org/r/20221122220713.293ef3e5a1c4.I867526a6bd3f93ee50076ff359436dfb5ec6101c@changeid Signed-off-by: Gregory Greenman --- drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c index e83382ded8af..b59e61a76b70 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c @@ -873,6 +873,10 @@ iwl_nvm_fixup_sband_iftd(struct iwl_trans *trans, IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_IN_2G; break; case NL80211_BAND_6GHZ: + if (!is_ap || iwlwifi_mod_params.nvm_file) + iftype_data->eht_cap.eht_cap_elem.phy_cap_info[0] |= + IEEE80211_EHT_PHY_CAP0_320MHZ_IN_6GHZ; + fallthrough; case NL80211_BAND_5GHZ: iftype_data->he_cap.he_cap_elem.phy_cap_info[0] |= IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G; From 3f44d44f0685778e8b07e52dc1959dd40f1ba964 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 22 Nov 2022 22:10:39 +0200 Subject: [PATCH 1268/2002] wifi: iwlwifi: nvm-parse: support A-MPDU in EHT 2.4 GHz We support large A-MPDU in EHT in 2.4 GHz, so add the right bits for that in the EHT capabilities. Signed-off-by: Johannes Berg Link: https://lore.kernel.org/r/20221122220713.3e7449760415.Id394d7fd0acaca48d429feccda4e66dbadaab993@changeid Signed-off-by: Gregory Greenman --- drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c index b59e61a76b70..3a646bce7b42 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c @@ -871,6 +871,9 @@ iwl_nvm_fixup_sband_iftd(struct iwl_trans *trans, case NL80211_BAND_2GHZ: iftype_data->he_cap.he_cap_elem.phy_cap_info[0] |= IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_IN_2G; + iftype_data->eht_cap.eht_cap_elem.mac_cap_info[0] |= + u8_encode_bits(IEEE80211_EHT_MAC_CAP0_MAX_MPDU_LEN_11454, + IEEE80211_EHT_MAC_CAP0_MAX_MPDU_LEN_MASK); break; case NL80211_BAND_6GHZ: if (!is_ap || iwlwifi_mod_params.nvm_file) From 0e21ec6edbb535126bef3a471cc5ec10374a81bb Mon Sep 17 00:00:00 2001 From: Abhishek Naik Date: Tue, 22 Nov 2022 22:10:41 +0200 Subject: [PATCH 1269/2002] wifi: iwlwifi: nvm: Update EHT capabilities for GL device Update EHT MAC and PHY capabilities for GL device for station and softap interface. Add relevant code in nvm_fixup_sband_iftd. Signed-off-by: Abhishek Naik Link: https://lore.kernel.org/r/20221122220713.1da816ab7ffc.Ief570e27007c9e2ad3a97bee4b074e2fc1c75bae@changeid Signed-off-by: Gregory Greenman --- .../wireless/intel/iwlwifi/iwl-nvm-parse.c | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c index 3a646bce7b42..476b90f32626 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c @@ -950,6 +950,29 @@ iwl_nvm_fixup_sband_iftd(struct iwl_trans *trans, break; } + if (CSR_HW_REV_TYPE(trans->hw_rev) == IWL_CFG_MAC_TYPE_GL && + iftype_data->eht_cap.has_eht) { + iftype_data->eht_cap.eht_cap_elem.mac_cap_info[0] &= + ~(IEEE80211_EHT_MAC_CAP0_EPCS_PRIO_ACCESS | + IEEE80211_EHT_MAC_CAP0_TRIG_TXOP_SHARING_MODE1 | + IEEE80211_EHT_MAC_CAP0_TRIG_TXOP_SHARING_MODE2); + iftype_data->eht_cap.eht_cap_elem.phy_cap_info[3] &= + ~(IEEE80211_EHT_PHY_CAP0_PARTIAL_BW_UL_MU_MIMO | + IEEE80211_EHT_PHY_CAP3_NG_16_SU_FEEDBACK | + IEEE80211_EHT_PHY_CAP3_NG_16_MU_FEEDBACK | + IEEE80211_EHT_PHY_CAP3_CODEBOOK_4_2_SU_FDBK | + IEEE80211_EHT_PHY_CAP3_CODEBOOK_7_5_MU_FDBK | + IEEE80211_EHT_PHY_CAP3_TRIG_MU_BF_PART_BW_FDBK); + iftype_data->eht_cap.eht_cap_elem.phy_cap_info[4] &= + ~(IEEE80211_EHT_PHY_CAP4_PART_BW_DL_MU_MIMO | + IEEE80211_EHT_PHY_CAP4_POWER_BOOST_FACT_SUPP); + iftype_data->eht_cap.eht_cap_elem.phy_cap_info[5] &= + ~IEEE80211_EHT_PHY_CAP5_NON_TRIG_CQI_FEEDBACK; + iftype_data->eht_cap.eht_cap_elem.phy_cap_info[6] &= + ~(IEEE80211_EHT_PHY_CAP6_MCS15_SUPP_MASK | + IEEE80211_EHT_PHY_CAP6_EHT_DUP_6GHZ_SUPP); + } + if (fw_has_capa(&fw->ucode_capa, IWL_UCODE_TLV_CAPA_BROADCAST_TWT)) iftype_data->he_cap.he_cap_elem.mac_cap_info[2] |= IEEE80211_HE_MAC_CAP2_BCAST_TWT; From 56731878c68255745dc0116300d531b24d950f0d Mon Sep 17 00:00:00 2001 From: Daniel Gabay Date: Wed, 23 Nov 2022 23:02:05 +0200 Subject: [PATCH 1270/2002] wifi: iwlwifi: mvm: print OTP info after alive These are register values which are needed for debug regardless of alive fail/success. Signed-off-by: Daniel Gabay Link: https://lore.kernel.org/r/20221123225313.a5ae3679491b.Ic618934df084a9b269e56d892665523c79e40eee@changeid Signed-off-by: Gregory Greenman --- drivers/net/wireless/intel/iwlwifi/mvm/fw.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c index ef43f6971cd9..b3101d12a0a1 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c @@ -354,6 +354,18 @@ static int iwl_mvm_load_ucode_wait_alive(struct iwl_mvm *mvm, */ ret = iwl_wait_notification(&mvm->notif_wait, &alive_wait, MVM_UCODE_ALIVE_TIMEOUT); + + if (mvm->trans->trans_cfg->device_family == + IWL_DEVICE_FAMILY_AX210) { + /* print these registers regardless of alive fail/success */ + IWL_INFO(mvm, "WFPM_UMAC_PD_NOTIFICATION: 0x%x\n", + iwl_read_umac_prph(mvm->trans, WFPM_ARC1_PD_NOTIFICATION)); + IWL_INFO(mvm, "WFPM_LMAC2_PD_NOTIFICATION: 0x%x\n", + iwl_read_umac_prph(mvm->trans, WFPM_LMAC2_PD_NOTIFICATION)); + IWL_INFO(mvm, "WFPM_AUTH_KEY_0: 0x%x\n", + iwl_read_umac_prph(mvm->trans, SB_MODIFY_CFG_FLAG)); + } + if (ret) { struct iwl_trans *trans = mvm->trans; From 0473cbae2137b963bd0eaa74336131cb1d3bc6c3 Mon Sep 17 00:00:00 2001 From: Ben Greear Date: Wed, 23 Nov 2022 23:02:06 +0200 Subject: [PATCH 1271/2002] wifi: iwlwifi: mvm: fix double free on tx path. We see kernel crashes and lockups and KASAN errors related to ax210 firmware crashes. One of the KASAN dumps pointed at the tx path, and it appears there is indeed a way to double-free an skb. If iwl_mvm_tx_skb_sta returns non-zero, then the 'skb' sent into the method will be freed. But, in case where we build TSO skb buffer, the skb may also be freed in error case. So, return 0 in that particular error case and do cleanup manually. BUG: KASAN: use-after-free in __list_del_entry_valid+0x12/0x90 iwlwifi 0000:06:00.0: 0x00000000 | tsf hi Read of size 8 at addr ffff88813cfa4ba0 by task btserver/9650 CPU: 4 PID: 9650 Comm: btserver Tainted: G W 5.19.8+ #5 iwlwifi 0000:06:00.0: 0x00000000 | time gp1 Hardware name: Default string Default string/SKYBAY, BIOS 5.12 02/19/2019 Call Trace: dump_stack_lvl+0x55/0x6d print_report.cold.12+0xf2/0x684 iwlwifi 0000:06:00.0: 0x1D0915A8 | time gp2 ? __list_del_entry_valid+0x12/0x90 kasan_report+0x8b/0x180 iwlwifi 0000:06:00.0: 0x00000001 | uCode revision type ? __list_del_entry_valid+0x12/0x90 __list_del_entry_valid+0x12/0x90 iwlwifi 0000:06:00.0: 0x00000048 | uCode version major tcp_update_skb_after_send+0x5d/0x170 __tcp_transmit_skb+0xb61/0x15c0 iwlwifi 0000:06:00.0: 0xDAA05125 | uCode version minor ? __tcp_select_window+0x490/0x490 iwlwifi 0000:06:00.0: 0x00000420 | hw version ? trace_kmalloc_node+0x29/0xd0 ? __kmalloc_node_track_caller+0x12a/0x260 ? memset+0x1f/0x40 ? __build_skb_around+0x125/0x150 ? __alloc_skb+0x1d4/0x220 ? skb_zerocopy_clone+0x55/0x230 iwlwifi 0000:06:00.0: 0x00489002 | board version ? kmalloc_reserve+0x80/0x80 ? rcu_read_lock_bh_held+0x60/0xb0 tcp_write_xmit+0x3f1/0x24d0 iwlwifi 0000:06:00.0: 0x034E001C | hcmd ? __check_object_size+0x180/0x350 iwlwifi 0000:06:00.0: 0x24020000 | isr0 tcp_sendmsg_locked+0x8a9/0x1520 iwlwifi 0000:06:00.0: 0x01400000 | isr1 ? tcp_sendpage+0x50/0x50 iwlwifi 0000:06:00.0: 0x48F0000A | isr2 ? lock_release+0xb9/0x400 ? tcp_sendmsg+0x14/0x40 iwlwifi 0000:06:00.0: 0x00C3080C | isr3 ? lock_downgrade+0x390/0x390 ? do_raw_spin_lock+0x114/0x1d0 iwlwifi 0000:06:00.0: 0x00200000 | isr4 ? rwlock_bug.part.2+0x50/0x50 iwlwifi 0000:06:00.0: 0x034A001C | last cmd Id ? rwlock_bug.part.2+0x50/0x50 ? lockdep_hardirqs_on_prepare+0xe/0x200 iwlwifi 0000:06:00.0: 0x0000C2F0 | wait_event ? __local_bh_enable_ip+0x87/0xe0 ? inet_send_prepare+0x220/0x220 iwlwifi 0000:06:00.0: 0x000000C4 | l2p_control tcp_sendmsg+0x22/0x40 sock_sendmsg+0x5f/0x70 iwlwifi 0000:06:00.0: 0x00010034 | l2p_duration __sys_sendto+0x19d/0x250 iwlwifi 0000:06:00.0: 0x00000007 | l2p_mhvalid ? __ia32_sys_getpeername+0x40/0x40 iwlwifi 0000:06:00.0: 0x00000000 | l2p_addr_match ? rcu_read_lock_held_common+0x12/0x50 ? rcu_read_lock_sched_held+0x5a/0xd0 ? rcu_read_lock_bh_held+0xb0/0xb0 ? rcu_read_lock_sched_held+0x5a/0xd0 ? rcu_read_lock_sched_held+0x5a/0xd0 ? lock_release+0xb9/0x400 ? lock_downgrade+0x390/0x390 ? ktime_get+0x64/0x130 ? ktime_get+0x8d/0x130 ? rcu_read_lock_held_common+0x12/0x50 ? rcu_read_lock_sched_held+0x5a/0xd0 ? rcu_read_lock_held_common+0x12/0x50 ? rcu_read_lock_sched_held+0x5a/0xd0 ? rcu_read_lock_bh_held+0xb0/0xb0 ? rcu_read_lock_bh_held+0xb0/0xb0 __x64_sys_sendto+0x6f/0x80 do_syscall_64+0x34/0xb0 entry_SYSCALL_64_after_hwframe+0x46/0xb0 RIP: 0033:0x7f1d126e4531 Code: 00 00 00 00 0f 1f 44 00 00 f3 0f 1e fa 48 8d 05 35 80 0c 00 41 89 ca 8b 00 85 c0 75 1c 45 31 c9 45 31 c0 b8 2c 00 00 00 0f 05 <48> 3d 00 f0 ff ff 77 67 c3 66 0f 1f 44 00 00 55 48 83 ec 20 48 89 RSP: 002b:00007ffe21a679d8 EFLAGS: 00000246 ORIG_RAX: 000000000000002c RAX: ffffffffffffffda RBX: 000000000000ffdc RCX: 00007f1d126e4531 RDX: 0000000000010000 RSI: 000000000374acf0 RDI: 0000000000000014 RBP: 00007ffe21a67ac0 R08: 0000000000000000 R09: 0000000000000000 R10: 0000000000000000 R11: 0000000000000246 R12: 0000000000000010 R13: 0000000000000000 R14: 0000000000000001 R15: 0000000000000000 Allocated by task 9650: kasan_save_stack+0x1c/0x40 __kasan_slab_alloc+0x6d/0x90 kmem_cache_alloc_node+0xf3/0x2b0 __alloc_skb+0x191/0x220 tcp_stream_alloc_skb+0x3f/0x330 tcp_sendmsg_locked+0x67c/0x1520 tcp_sendmsg+0x22/0x40 sock_sendmsg+0x5f/0x70 __sys_sendto+0x19d/0x250 __x64_sys_sendto+0x6f/0x80 do_syscall_64+0x34/0xb0 entry_SYSCALL_64_after_hwframe+0x46/0xb0 Freed by task 9650: kasan_save_stack+0x1c/0x40 kasan_set_track+0x21/0x30 kasan_set_free_info+0x20/0x30 __kasan_slab_free+0x102/0x170 kmem_cache_free+0xc8/0x3e0 iwl_mvm_mac_itxq_xmit+0x124/0x270 [iwlmvm] ieee80211_queue_skb+0x874/0xd10 [mac80211] ieee80211_xmit_fast+0xf80/0x1180 [mac80211] __ieee80211_subif_start_xmit+0x287/0x680 [mac80211] ieee80211_subif_start_xmit+0xcd/0x730 [mac80211] dev_hard_start_xmit+0xf6/0x420 __dev_queue_xmit+0x165b/0x1b50 ip_finish_output2+0x66e/0xfb0 __ip_finish_output+0x487/0x6d0 ip_output+0x11c/0x350 __ip_queue_xmit+0x36b/0x9d0 __tcp_transmit_skb+0xb35/0x15c0 tcp_write_xmit+0x3f1/0x24d0 tcp_sendmsg_locked+0x8a9/0x1520 tcp_sendmsg+0x22/0x40 sock_sendmsg+0x5f/0x70 __sys_sendto+0x19d/0x250 __x64_sys_sendto+0x6f/0x80 do_syscall_64+0x34/0xb0 entry_SYSCALL_64_after_hwframe+0x46/0xb0 The buggy address belongs to the object at ffff88813cfa4b40 which belongs to the cache skbuff_fclone_cache of size 472 The buggy address is located 96 bytes inside of 472-byte region [ffff88813cfa4b40, ffff88813cfa4d18) The buggy address belongs to the physical page: page:ffffea0004f3e900 refcount:1 mapcount:0 mapping:0000000000000000 index:0xffff88813cfa6c40 pfn:0x13cfa4 head:ffffea0004f3e900 order:2 compound_mapcount:0 compound_pincount:0 flags: 0x5fff8000010200(slab|head|node=0|zone=2|lastcpupid=0x3fff) raw: 005fff8000010200 ffffea0004656b08 ffffea0008e8cf08 ffff8881081a5240 raw: ffff88813cfa6c40 0000000000170015 00000001ffffffff 0000000000000000 page dumped because: kasan: bad access detected Memory state around the buggy address: ffff88813cfa4a80: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc ffff88813cfa4b00: fc fc fc fc fc fc fc fc fa fb fb fb fb fb fb fb >ffff88813cfa4b80: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb ^ ffff88813cfa4c00: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb ffff88813cfa4c80: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb ================================================================== Fixes: 08f7d8b69aaf ("iwlwifi: mvm: bring back mvm GSO code") Link: https://lore.kernel.org/linux-wireless/20220928193057.16132-1-greearb@candelatech.com/ Tested-by: Amol Jawale Signed-off-by: Ben Greear Link: https://lore.kernel.org/r/20221123225313.21b1ee31d666.I3b3ba184433dd2a544d91eeeda29b467021824ae@changeid Signed-off-by: Gregory Greenman --- drivers/net/wireless/intel/iwlwifi/mvm/tx.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/tx.c b/drivers/net/wireless/intel/iwlwifi/mvm/tx.c index f460332333a7..fadaa683a416 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/tx.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/tx.c @@ -1215,6 +1215,7 @@ int iwl_mvm_tx_skb_sta(struct iwl_mvm *mvm, struct sk_buff *skb, struct sk_buff_head mpdus_skbs; unsigned int payload_len; int ret; + struct sk_buff *orig_skb = skb; if (WARN_ON_ONCE(!mvmsta)) return -1; @@ -1247,8 +1248,17 @@ int iwl_mvm_tx_skb_sta(struct iwl_mvm *mvm, struct sk_buff *skb, ret = iwl_mvm_tx_mpdu(mvm, skb, &info, sta); if (ret) { + /* Free skbs created as part of TSO logic that have not yet been dequeued */ __skb_queue_purge(&mpdus_skbs); - return ret; + /* skb here is not necessarily same as skb that entered this method, + * so free it explicitly. + */ + if (skb == orig_skb) + ieee80211_free_txskb(mvm->hw, skb); + else + kfree_skb(skb); + /* there was error, but we consumed skb one way or another, so return 0 */ + return 0; } } From b8133439bda70ff24e3bdb89bb55d204c3fea606 Mon Sep 17 00:00:00 2001 From: Avraham Stern Date: Wed, 23 Nov 2022 23:02:07 +0200 Subject: [PATCH 1272/2002] wifi: iwlwifi: mvm: trigger PCI re-enumeration in case of PLDR sync When doing the PLDR flow, the fw goes through a re-read and needs PCI re-enumeration in order to recover. In this case, skip the mac start retry and fw dumps as all the fw and registers are invalid until the PCI re-enumeration. In addition, print the register that shows the re-read counter when loading the fw. Signed-off-by: Avraham Stern Link: https://lore.kernel.org/r/20221123225313.9ae77968961e.Ie06e886cef4b5921b65dacb7724db1276bed38cb@changeid Signed-off-by: Gregory Greenman --- drivers/net/wireless/intel/iwlwifi/iwl-prph.h | 1 + .../net/wireless/intel/iwlwifi/iwl-trans.h | 1 + drivers/net/wireless/intel/iwlwifi/mvm/fw.c | 12 ++- .../net/wireless/intel/iwlwifi/mvm/mac80211.c | 10 +++ drivers/net/wireless/intel/iwlwifi/mvm/mvm.h | 2 + drivers/net/wireless/intel/iwlwifi/mvm/ops.c | 3 + .../net/wireless/intel/iwlwifi/pcie/trans.c | 85 ++++++++++--------- 7 files changed, 73 insertions(+), 41 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-prph.h b/drivers/net/wireless/intel/iwlwifi/iwl-prph.h index 82cf904e0b6d..62ce116d3783 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-prph.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-prph.h @@ -368,6 +368,7 @@ enum { #define CNVR_AUX_MISC_CHIP 0xA2B800 #define CNVR_SCU_SD_REGS_SD_REG_DIG_DCDC_VTRIM 0xA29890 #define CNVR_SCU_SD_REGS_SD_REG_ACTIVE_VDIG_MIRROR 0xA29938 +#define CNVI_SCU_SEQ_DATA_DW9 0xA27488 #define PREG_AUX_BUS_WPROT_0 0xA04CC0 diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-trans.h b/drivers/net/wireless/intel/iwlwifi/iwl-trans.h index d659ccd065f7..32bd7f19f1d5 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-trans.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-trans.h @@ -1542,5 +1542,6 @@ void iwl_trans_free(struct iwl_trans *trans); ******************************************************/ int __must_check iwl_pci_register_driver(void); void iwl_pci_unregister_driver(void); +void iwl_trans_pcie_remove(struct iwl_trans *trans, bool rescan); #endif /* __iwl_trans_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c index b3101d12a0a1..74354d044db9 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c @@ -364,6 +364,8 @@ static int iwl_mvm_load_ucode_wait_alive(struct iwl_mvm *mvm, iwl_read_umac_prph(mvm->trans, WFPM_LMAC2_PD_NOTIFICATION)); IWL_INFO(mvm, "WFPM_AUTH_KEY_0: 0x%x\n", iwl_read_umac_prph(mvm->trans, SB_MODIFY_CFG_FLAG)); + IWL_INFO(mvm, "CNVI_SCU_SEQ_DATA_DW9: 0x%x\n", + iwl_read_prph(mvm->trans, CNVI_SCU_SEQ_DATA_DW9)); } if (ret) { @@ -402,7 +404,7 @@ static int iwl_mvm_load_ucode_wait_alive(struct iwl_mvm *mvm, UREG_LMAC2_CURRENT_PC)); } - if (ret == -ETIMEDOUT) + if (ret == -ETIMEDOUT && !mvm->pldr_sync) iwl_fw_dbg_error_collect(&mvm->fwrt, FW_DBG_TRIGGER_ALIVE_TIMEOUT); @@ -1479,18 +1481,22 @@ int iwl_mvm_up(struct iwl_mvm *mvm) return ret; sb_cfg = iwl_read_umac_prph(mvm->trans, SB_MODIFY_CFG_FLAG); - if (!(sb_cfg & SB_CFG_RESIDES_IN_OTP_MASK) && iwl_mei_pldr_req()) + mvm->pldr_sync = !(sb_cfg & SB_CFG_RESIDES_IN_OTP_MASK); + if (mvm->pldr_sync && iwl_mei_pldr_req()) return ret; ret = iwl_mvm_load_rt_fw(mvm); if (ret) { IWL_ERR(mvm, "Failed to start RT ucode: %d\n", ret); - if (ret != -ERFKILL) + if (ret != -ERFKILL && !mvm->pldr_sync) iwl_fw_dbg_error_collect(&mvm->fwrt, FW_DBG_TRIGGER_DRIVER); goto error; } + /* FW loaded successfully */ + mvm->pldr_sync = false; + iwl_get_shared_mem_conf(&mvm->fwrt); ret = iwl_mvm_sf_update(mvm, NULL, false); diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c index 3fba69554f83..5273ade71117 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c @@ -1068,6 +1068,16 @@ static int iwl_mvm_mac_start(struct ieee80211_hw *hw) if (!ret) break; + /* + * In PLDR sync PCI re-enumeration is needed. no point to retry + * mac start before that. + */ + if (mvm->pldr_sync) { + iwl_mei_alive_notif(false); + iwl_trans_pcie_remove(mvm->trans, true); + break; + } + IWL_ERR(mvm, "mac start retry %d\n", retry); } clear_bit(IWL_MVM_STATUS_STARTING, &mvm->status); diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h index 962e304fc2b1..ce6b701f3f4c 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h @@ -1105,6 +1105,8 @@ struct iwl_mvm { unsigned long last_reset_or_resume_time_jiffies; bool sta_remove_requires_queue_remove; + + bool pldr_sync; }; /* Extract MVM priv from op_mode and _hw */ diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c index 2a4db13c9dcf..e78f5beaa2d0 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c @@ -1888,6 +1888,9 @@ static void iwl_mvm_nic_error(struct iwl_op_mode *op_mode, bool sync) { struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode); + if (mvm->pldr_sync) + return; + if (!test_bit(STATUS_TRANS_DEAD, &mvm->trans->status) && !test_and_clear_bit(IWL_MVM_STATUS_SUPPRESS_ERROR_LOG_ONCE, &mvm->status)) diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c index bd50f52a1aad..0a9af1ad1f20 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c @@ -2052,6 +2052,7 @@ static void iwl_trans_pcie_set_pmi(struct iwl_trans *trans, bool state) struct iwl_trans_pcie_removal { struct pci_dev *pdev; struct work_struct work; + bool rescan; }; static void iwl_trans_pcie_removal_wk(struct work_struct *wk) @@ -2060,18 +2061,61 @@ static void iwl_trans_pcie_removal_wk(struct work_struct *wk) container_of(wk, struct iwl_trans_pcie_removal, work); struct pci_dev *pdev = removal->pdev; static char *prop[] = {"EVENT=INACCESSIBLE", NULL}; + struct pci_bus *bus = pdev->bus; dev_err(&pdev->dev, "Device gone - attempting removal\n"); kobject_uevent_env(&pdev->dev.kobj, KOBJ_CHANGE, prop); pci_lock_rescan_remove(); pci_dev_put(pdev); pci_stop_and_remove_bus_device(pdev); + if (removal->rescan) + pci_rescan_bus(bus->parent); pci_unlock_rescan_remove(); kfree(removal); module_put(THIS_MODULE); } +void iwl_trans_pcie_remove(struct iwl_trans *trans, bool rescan) +{ + struct iwl_trans_pcie_removal *removal; + + if (test_bit(STATUS_TRANS_DEAD, &trans->status)) + return; + + IWL_ERR(trans, "Device gone - scheduling removal!\n"); + + /* + * get a module reference to avoid doing this + * while unloading anyway and to avoid + * scheduling a work with code that's being + * removed. + */ + if (!try_module_get(THIS_MODULE)) { + IWL_ERR(trans, + "Module is being unloaded - abort\n"); + return; + } + + removal = kzalloc(sizeof(*removal), GFP_ATOMIC); + if (!removal) { + module_put(THIS_MODULE); + return; + } + /* + * we don't need to clear this flag, because + * the trans will be freed and reallocated. + */ + set_bit(STATUS_TRANS_DEAD, &trans->status); + + removal->pdev = to_pci_dev(trans->dev); + removal->rescan = rescan; + INIT_WORK(&removal->work, iwl_trans_pcie_removal_wk); + pci_dev_get(removal->pdev); + schedule_work(&removal->work); +} +EXPORT_SYMBOL(iwl_trans_pcie_remove); + /* * This version doesn't disable BHs but rather assumes they're * already disabled. @@ -2131,47 +2175,12 @@ bool __iwl_trans_pcie_grab_nic_access(struct iwl_trans *trans) iwl_trans_pcie_dump_regs(trans); - if (iwlwifi_mod_params.remove_when_gone && cntrl == ~0U) { - struct iwl_trans_pcie_removal *removal; - - if (test_bit(STATUS_TRANS_DEAD, &trans->status)) - goto err; - - IWL_ERR(trans, "Device gone - scheduling removal!\n"); - - /* - * get a module reference to avoid doing this - * while unloading anyway and to avoid - * scheduling a work with code that's being - * removed. - */ - if (!try_module_get(THIS_MODULE)) { - IWL_ERR(trans, - "Module is being unloaded - abort\n"); - goto err; - } - - removal = kzalloc(sizeof(*removal), GFP_ATOMIC); - if (!removal) { - module_put(THIS_MODULE); - goto err; - } - /* - * we don't need to clear this flag, because - * the trans will be freed and reallocated. - */ - set_bit(STATUS_TRANS_DEAD, &trans->status); - - removal->pdev = to_pci_dev(trans->dev); - INIT_WORK(&removal->work, iwl_trans_pcie_removal_wk); - pci_dev_get(removal->pdev); - schedule_work(&removal->work); - } else { + if (iwlwifi_mod_params.remove_when_gone && cntrl == ~0U) + iwl_trans_pcie_remove(trans, false); + else iwl_write32(trans, CSR_RESET, CSR_RESET_REG_FLAG_FORCE_NMI); - } -err: spin_unlock(&trans_pcie->reg_lock); return false; } From 274d9aa97372094317fcf0d27af4fedb6659c7d4 Mon Sep 17 00:00:00 2001 From: Avraham Stern Date: Wed, 23 Nov 2022 23:02:08 +0200 Subject: [PATCH 1273/2002] wifi: iwlwifi: mvm: return error value in case PLDR sync failed In case PLDR sync is required but iwlmei failed to get a response from CSME, the device cannot be brought up, so return an error value. Reported-by: Dan Carpenter Link: https://lore.kernel.org/all/Y3uYUEFnMiBY2ABQ@kili/ Signed-off-by: Avraham Stern Link: https://lore.kernel.org/r/20221123225313.08b345123f83.I13bfacc2f6da7de8ada90127fe7108d1bb73af92@changeid Signed-off-by: Gregory Greenman --- drivers/net/wireless/intel/iwlwifi/mvm/fw.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c index 74354d044db9..2f4c9e653e99 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c @@ -1483,7 +1483,7 @@ int iwl_mvm_up(struct iwl_mvm *mvm) sb_cfg = iwl_read_umac_prph(mvm->trans, SB_MODIFY_CFG_FLAG); mvm->pldr_sync = !(sb_cfg & SB_CFG_RESIDES_IN_OTP_MASK); if (mvm->pldr_sync && iwl_mei_pldr_req()) - return ret; + return -EBUSY; ret = iwl_mvm_load_rt_fw(mvm); if (ret) { From f31f7cd9875c2ab744006bd2ad25641db53f079d Mon Sep 17 00:00:00 2001 From: Gregory Greenman Date: Wed, 23 Nov 2022 23:02:09 +0200 Subject: [PATCH 1274/2002] wifi: iwlwifi: mei: fix parameter passing to iwl_mei_alive_notif() Pass true explicitly to iwl_mei_alive_notif() when we can be sure that Alive notification was received. The issue was discovered by Smatch. Reported-by: Dan Carpenter Link: https://lore.kernel.org/all/Y3uYUEFnMiBY2ABQ@kili/ Link: https://lore.kernel.org/r/20221123225313.83c2251055c1.Ia028357e7ab36dfc2abf85106a5926ee7893a408@changeid Signed-off-by: Gregory Greenman --- drivers/net/wireless/intel/iwlwifi/mvm/fw.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c index 2f4c9e653e99..0a41cd0ab243 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c @@ -418,7 +418,8 @@ static int iwl_mvm_load_ucode_wait_alive(struct iwl_mvm *mvm, return -EIO; } - iwl_mei_alive_notif(!ret); + /* if reached this point, Alive notification was received */ + iwl_mei_alive_notif(true); ret = iwl_pnvm_load(mvm->trans, &mvm->notif_wait); if (ret) { From 2816c98606a912af626fc9e2b62a1063cf6000f0 Mon Sep 17 00:00:00 2001 From: Davide Tronchin Date: Thu, 24 Nov 2022 12:28:11 +0100 Subject: [PATCH 1275/2002] net: usb: cdc_ether: add u-blox 0x1343 composition Add CDC-ECM support for LARA-L6. LARA-L6 module can be configured (by AT interface) in three different USB modes: * Default mode (Vendor ID: 0x1546 Product ID: 0x1341) with 4 serial interfaces * RmNet mode (Vendor ID: 0x1546 Product ID: 0x1342) with 4 serial interfaces and 1 RmNet virtual network interface * CDC-ECM mode (Vendor ID: 0x1546 Product ID: 0x1343) with 4 serial interface and 1 CDC-ECM virtual network interface In CDC-ECM mode LARA-L6 exposes the following interfaces: If 0: Diagnostic If 1: AT parser If 2: AT parser If 3: AT parset/alternative functions If 4: CDC-ECM interface Signed-off-by: Davide Tronchin Link: https://lore.kernel.org/r/20221124112811.3548-1-davide.tronchin.94@gmail.com Signed-off-by: Jakub Kicinski --- drivers/net/usb/cdc_ether.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/net/usb/cdc_ether.c b/drivers/net/usb/cdc_ether.c index e11f70911acc..8911cd2ed534 100644 --- a/drivers/net/usb/cdc_ether.c +++ b/drivers/net/usb/cdc_ether.c @@ -989,6 +989,12 @@ static const struct usb_device_id products[] = { USB_CDC_SUBCLASS_ETHERNET, USB_CDC_PROTO_NONE), .driver_info = (unsigned long)&wwan_info, +}, { + /* U-blox LARA-L6 */ + USB_DEVICE_AND_INTERFACE_INFO(UBLOX_VENDOR_ID, 0x1343, USB_CLASS_COMM, + USB_CDC_SUBCLASS_ETHERNET, + USB_CDC_PROTO_NONE), + .driver_info = (unsigned long)&wwan_info, }, { /* Cinterion PLS8 modem by GEMALTO */ USB_DEVICE_AND_INTERFACE_INFO(0x1e2d, 0x0061, USB_CLASS_COMM, From 469d258d9e112fb7ed51a3b35c0c85ee106331e8 Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Fri, 25 Nov 2022 12:53:04 +0200 Subject: [PATCH 1276/2002] Revert "net: stmmac: use sysfs_streq() instead of strncmp()" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit f72cd76b05ea1ce9258484e8127932d0ea928f22. This patch is so broken, it hurts. Apparently no one reviewed it and it passed the build testing (because the code was compiled out), but it was obviously never compile-tested, since it produces the following build error, due to an incomplete conversion where an extra argument was left, although the function being called was left: stmmac_main.c: In function ‘stmmac_cmdline_opt’: stmmac_main.c:7586:28: error: too many arguments to function ‘sysfs_streq’ 7586 | } else if (sysfs_streq(opt, "pause:", 6)) { | ^~~~~~~~~~~ In file included from ../include/linux/bitmap.h:11, from ../include/linux/cpumask.h:12, from ../include/linux/smp.h:13, from ../include/linux/lockdep.h:14, from ../include/linux/mutex.h:17, from ../include/linux/notifier.h:14, from ../include/linux/clk.h:14, from ../drivers/net/ethernet/stmicro/stmmac/stmmac_main.c:17: ../include/linux/string.h:185:13: note: declared here 185 | extern bool sysfs_streq(const char *s1, const char *s2); | ^~~~~~~~~~~ What's even worse is that the patch is flat out wrong. The stmmac_cmdline_opt() function does not parse sysfs input, but cmdline input such as "stmmaceth=tc:1,pause:1". The pattern of using strsep() followed by strncmp() for such strings is not unique to stmmac, it can also be found mainly in drivers under drivers/video/fbdev/. With strncmp("tc:", 3), the code matches on the "tc:1" token properly. With sysfs_streq("tc:"), it doesn't. Fixes: f72cd76b05ea ("net: stmmac: use sysfs_streq() instead of strncmp()") Signed-off-by: Vladimir Oltean Acked-by: Maciej Fijalkowski Link: https://lore.kernel.org/r/20221125105304.3012153-1-vladimir.oltean@nxp.com Signed-off-by: Jakub Kicinski --- .../net/ethernet/stmicro/stmmac/stmmac_main.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index 4167e768a86a..0a9d13d7976f 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -7562,31 +7562,31 @@ static int __init stmmac_cmdline_opt(char *str) if (!str || !*str) return 1; while ((opt = strsep(&str, ",")) != NULL) { - if (sysfs_streq(opt, "debug:")) { + if (!strncmp(opt, "debug:", 6)) { if (kstrtoint(opt + 6, 0, &debug)) goto err; - } else if (sysfs_streq(opt, "phyaddr:")) { + } else if (!strncmp(opt, "phyaddr:", 8)) { if (kstrtoint(opt + 8, 0, &phyaddr)) goto err; - } else if (sysfs_streq(opt, "buf_sz:")) { + } else if (!strncmp(opt, "buf_sz:", 7)) { if (kstrtoint(opt + 7, 0, &buf_sz)) goto err; - } else if (sysfs_streq(opt, "tc:")) { + } else if (!strncmp(opt, "tc:", 3)) { if (kstrtoint(opt + 3, 0, &tc)) goto err; - } else if (sysfs_streq(opt, "watchdog:")) { + } else if (!strncmp(opt, "watchdog:", 9)) { if (kstrtoint(opt + 9, 0, &watchdog)) goto err; - } else if (sysfs_streq(opt, "flow_ctrl:")) { + } else if (!strncmp(opt, "flow_ctrl:", 10)) { if (kstrtoint(opt + 10, 0, &flow_ctrl)) goto err; - } else if (sysfs_streq(opt, "pause:", 6)) { + } else if (!strncmp(opt, "pause:", 6)) { if (kstrtoint(opt + 6, 0, &pause)) goto err; - } else if (sysfs_streq(opt, "eee_timer:")) { + } else if (!strncmp(opt, "eee_timer:", 10)) { if (kstrtoint(opt + 10, 0, &eee_timer)) goto err; - } else if (sysfs_streq(opt, "chain_mode:")) { + } else if (!strncmp(opt, "chain_mode:", 11)) { if (kstrtoint(opt + 11, 0, &chain_mode)) goto err; } From 98eb05dc99fdbd9c7e3b865b2d2927768832b0ff Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Thu, 24 Nov 2022 12:15:50 +0100 Subject: [PATCH 1277/2002] Revert "dt-bindings: marvell,prestera: Add description for device-tree bindings" This reverts commit 40acc05271abc2852c32622edbebd75698736b9b. marvell,prestera.txt is an old file describing the old Alleycat3 standalone switches. The commit mentioned above actually hacked these bindings to add support for a device tree property for a more modern version of the IP connected over PCI, using only the generic compatible in order to retrieve the device node from the prestera driver to read one static property. The problematic property discussed here is "base-mac-provider". The original intent was to point to a nvmem device which could produce the relevant nvmem-cell. This property has never been acked by DT maintainers and fails all the layering that has been brought with the nvmem bindings by pointing at a nvmem producer, bypassing the existing nvmem bindings, rather than a nvmem cell directly. Furthermore, the property cannot even be used upstream because it expected the ONIE tlv driver to produce a specific cell, driver which used nacked bindings and thus was never merged, replaced by a more integrated concept: the nvmem-layout. So let's forget about this temporary addition, safely avoiding the need for any backward compatibility handling. A new (yaml) binding file will be brought with the prestera bindings, and there we will actually include a description of the modern IP over PCI, including the right way to point to a nvmem cell. Cc: Vadym Kochan Cc: Taras Chornyi Signed-off-by: Miquel Raynal Acked-by: Rob Herring Acked-by: Krzysztof Kozlowski Signed-off-by: Paolo Abeni --- .../bindings/net/marvell,prestera.txt | 34 ------------------- 1 file changed, 34 deletions(-) diff --git a/Documentation/devicetree/bindings/net/marvell,prestera.txt b/Documentation/devicetree/bindings/net/marvell,prestera.txt index e28938ddfdf5..83370ebf5b89 100644 --- a/Documentation/devicetree/bindings/net/marvell,prestera.txt +++ b/Documentation/devicetree/bindings/net/marvell,prestera.txt @@ -45,37 +45,3 @@ dfx-server { ranges = <0 MBUS_ID(0x08, 0x00) 0 0x100000>; reg = ; }; - -Marvell Prestera SwitchDev bindings ------------------------------------ -Optional properties: -- compatible: must be "marvell,prestera" -- base-mac-provider: describes handle to node which provides base mac address, - might be a static base mac address or nvme cell provider. - -Example: - -eeprom_mac_addr: eeprom-mac-addr { - compatible = "eeprom,mac-addr-cell"; - status = "okay"; - - nvmem = <&eeprom_at24>; -}; - -prestera { - compatible = "marvell,prestera"; - status = "okay"; - - base-mac-provider = <&eeprom_mac_addr>; -}; - -The current implementation of Prestera Switchdev PCI interface driver requires -that BAR2 is assigned to 0xf6000000 as base address from the PCI IO range: - -&cp0_pcie0 { - ranges = <0x81000000 0x0 0xfb000000 0x0 0xfb000000 0x0 0xf0000 - 0x82000000 0x0 0xf6000000 0x0 0xf6000000 0x0 0x2000000 - 0x82000000 0x0 0xf9000000 0x0 0xf9000000 0x0 0x100000>; - phys = <&cp0_comphy0 0>; - status = "okay"; -}; From 63b956f9917595f63d3519131317e38ec8c876a7 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Thu, 24 Nov 2022 12:15:51 +0100 Subject: [PATCH 1278/2002] dt-bindings: net: marvell,dfx-server: Convert to yaml Even though this description is not used anywhere upstream (no matching driver), while on this file I decided I would try a conversion to yaml in order to clarify the prestera family description. I cannot keep the nodename dfx-server@xxxx so I switched to dfx-bus@xxxx which matches simple-bus.yaml. Otherwise I took the example context from the only user of this compatible: armada-xp-98dx3236.dtsi, which is a rather old and not perfect DT. Signed-off-by: Miquel Raynal Reviewed-by: Krzysztof Kozlowski Signed-off-by: Paolo Abeni --- .../bindings/net/marvell,dfx-server.yaml | 62 +++++++++++++++++++ .../bindings/net/marvell,prestera.txt | 18 ------ 2 files changed, 62 insertions(+), 18 deletions(-) create mode 100644 Documentation/devicetree/bindings/net/marvell,dfx-server.yaml diff --git a/Documentation/devicetree/bindings/net/marvell,dfx-server.yaml b/Documentation/devicetree/bindings/net/marvell,dfx-server.yaml new file mode 100644 index 000000000000..8a14c919e3f7 --- /dev/null +++ b/Documentation/devicetree/bindings/net/marvell,dfx-server.yaml @@ -0,0 +1,62 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/net/marvell,dfx-server.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Marvell Prestera DFX server + +maintainers: + - Miquel Raynal + +select: + properties: + compatible: + contains: + const: marvell,dfx-server + required: + - compatible + +properties: + compatible: + items: + - const: marvell,dfx-server + - const: simple-bus + + reg: + maxItems: 1 + + ranges: true + + '#address-cells': + const: 1 + + '#size-cells': + const: 1 + +required: + - compatible + - reg + - ranges + +# The DFX server may expose clocks described as subnodes +additionalProperties: + type: object + +examples: + - | + + #define MBUS_ID(target,attributes) (((target) << 24) | ((attributes) << 16)) + bus@0 { + reg = <0 0>; + #address-cells = <2>; + #size-cells = <1>; + + dfx-bus@ac000000 { + compatible = "marvell,dfx-server", "simple-bus"; + #address-cells = <1>; + #size-cells = <1>; + ranges = <0 MBUS_ID(0x08, 0x00) 0 0x100000>; + reg = ; + }; + }; diff --git a/Documentation/devicetree/bindings/net/marvell,prestera.txt b/Documentation/devicetree/bindings/net/marvell,prestera.txt index 83370ebf5b89..8868d774da67 100644 --- a/Documentation/devicetree/bindings/net/marvell,prestera.txt +++ b/Documentation/devicetree/bindings/net/marvell,prestera.txt @@ -27,21 +27,3 @@ switch { dfx = <&dfx>; }; }; - -DFX Server bindings -------------------- - -Required properties: -- compatible: must be "marvell,dfx-server", "simple-bus" -- ranges: describes the address mapping of a memory-mapped bus. -- reg: address and length of the register set for the device. - -Example: - -dfx-server { - compatible = "marvell,dfx-server", "simple-bus"; - #address-cells = <1>; - #size-cells = <1>; - ranges = <0 MBUS_ID(0x08, 0x00) 0 0x100000>; - reg = ; -}; From a429ab01163c8d55281ffd64bd924b32c501da57 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Thu, 24 Nov 2022 12:15:52 +0100 Subject: [PATCH 1279/2002] dt-bindings: net: marvell,prestera: Convert to yaml The currently described switch family is named AlleyCat3, it is a memory mapped switch found on Armada XP boards. Signed-off-by: Miquel Raynal Reviewed-by: Rob Herring Signed-off-by: Paolo Abeni --- .../bindings/net/marvell,prestera.txt | 29 ------------ .../bindings/net/marvell,prestera.yaml | 45 +++++++++++++++++++ 2 files changed, 45 insertions(+), 29 deletions(-) delete mode 100644 Documentation/devicetree/bindings/net/marvell,prestera.txt create mode 100644 Documentation/devicetree/bindings/net/marvell,prestera.yaml diff --git a/Documentation/devicetree/bindings/net/marvell,prestera.txt b/Documentation/devicetree/bindings/net/marvell,prestera.txt deleted file mode 100644 index 8868d774da67..000000000000 --- a/Documentation/devicetree/bindings/net/marvell,prestera.txt +++ /dev/null @@ -1,29 +0,0 @@ -Marvell Prestera Switch Chip bindings -------------------------------------- - -Required properties: -- compatible: must be "marvell,prestera" and one of the following - "marvell,prestera-98dx3236", - "marvell,prestera-98dx3336", - "marvell,prestera-98dx4251", -- reg: address and length of the register set for the device. -- interrupts: interrupt for the device - -Optional properties: -- dfx: phandle reference to the "DFX Server" node - -Example: - -switch { - compatible = "simple-bus"; - #address-cells = <1>; - #size-cells = <1>; - ranges = <0 MBUS_ID(0x03, 0x00) 0 0x100000>; - - packet-processor@0 { - compatible = "marvell,prestera-98dx3236", "marvell,prestera"; - reg = <0 0x4000000>; - interrupts = <33>, <34>, <35>; - dfx = <&dfx>; - }; -}; diff --git a/Documentation/devicetree/bindings/net/marvell,prestera.yaml b/Documentation/devicetree/bindings/net/marvell,prestera.yaml new file mode 100644 index 000000000000..b0a3ecca406e --- /dev/null +++ b/Documentation/devicetree/bindings/net/marvell,prestera.yaml @@ -0,0 +1,45 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/net/marvell,prestera.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Marvell Prestera AlleyCat3 switch + +maintainers: + - Miquel Raynal + +properties: + compatible: + items: + - enum: + - marvell,prestera-98dx3236 + - marvell,prestera-98dx3336 + - marvell,prestera-98dx4251 + - const: marvell,prestera + + reg: + maxItems: 1 + + interrupts: + maxItems: 3 + + dfx: + description: Reference to the DFX Server bus node. + $ref: /schemas/types.yaml#/definitions/phandle + +required: + - compatible + - reg + - interrupts + +additionalProperties: false + +examples: + - | + packet-processor@0 { + compatible = "marvell,prestera-98dx3236", "marvell,prestera"; + reg = <0 0x4000000>; + interrupts = <33>, <34>, <35>; + dfx = <&dfx>; + }; From 39d1038620153d9c94a47235d061aefe681d3e65 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Thu, 24 Nov 2022 12:15:53 +0100 Subject: [PATCH 1280/2002] dt-bindings: net: marvell,prestera: Describe PCI devices of the prestera family Even though the devices have very little in common beside the name and the main "switch" feature, Marvell Prestera switch family is also composed of PCI-only devices which can receive additional static properties, like nvmem cells to point at MAC addresses, for instance. Let's describe them. Signed-off-by: Miquel Raynal Reviewed-by: Rob Herring Signed-off-by: Paolo Abeni --- .../bindings/net/marvell,prestera.yaml | 62 ++++++++++++++++--- 1 file changed, 54 insertions(+), 8 deletions(-) diff --git a/Documentation/devicetree/bindings/net/marvell,prestera.yaml b/Documentation/devicetree/bindings/net/marvell,prestera.yaml index b0a3ecca406e..5ea8b73663a5 100644 --- a/Documentation/devicetree/bindings/net/marvell,prestera.yaml +++ b/Documentation/devicetree/bindings/net/marvell,prestera.yaml @@ -4,19 +4,24 @@ $id: http://devicetree.org/schemas/net/marvell,prestera.yaml# $schema: http://devicetree.org/meta-schemas/core.yaml# -title: Marvell Prestera AlleyCat3 switch +title: Marvell Prestera switch family maintainers: - Miquel Raynal properties: compatible: - items: + oneOf: + - items: + - enum: + - marvell,prestera-98dx3236 + - marvell,prestera-98dx3336 + - marvell,prestera-98dx4251 + - const: marvell,prestera - enum: - - marvell,prestera-98dx3236 - - marvell,prestera-98dx3336 - - marvell,prestera-98dx4251 - - const: marvell,prestera + - pci11ab,c804 + - pci11ab,c80c + - pci11ab,cc1e reg: maxItems: 1 @@ -28,12 +33,37 @@ properties: description: Reference to the DFX Server bus node. $ref: /schemas/types.yaml#/definitions/phandle + nvmem-cells: true + + nvmem-cell-names: true + +if: + properties: + compatible: + contains: + const: marvell,prestera + +# Memory mapped AlleyCat3 family +then: + properties: + nvmem-cells: false + nvmem-cell-names: false + required: + - interrupts + +# PCI Aldrin family +else: + properties: + interrupts: false + dfx: false + required: - compatible - reg - - interrupts -additionalProperties: false +# Ports can also be described +additionalProperties: + type: object examples: - | @@ -43,3 +73,19 @@ examples: interrupts = <33>, <34>, <35>; dfx = <&dfx>; }; + + - | + pcie@0 { + #address-cells = <3>; + #size-cells = <2>; + ranges = <0x0 0x0 0x0 0x0 0x0 0x0>; + reg = <0x0 0x0 0x0 0x0 0x0 0x0>; + device_type = "pci"; + + switch@0,0 { + reg = <0x0 0x0 0x0 0x0 0x0>; + compatible = "pci11ab,c80c"; + nvmem-cells = <&mac_address 0>; + nvmem-cell-names = "mac-address"; + }; + }; From 4c47867bc789bdc722f3bb760355c2c246fbe9af Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Thu, 24 Nov 2022 12:15:54 +0100 Subject: [PATCH 1281/2002] of: net: export of_get_mac_address_nvmem() Export of_get_mac_addr_nvmem() and rename it to of_get_mac_address_nvmem() in order to fit the convention followed by the existing exported helpers of the same kind. This way, OF compatible drivers using eg. fwnode_get_mac_address() can do a direct call to it instead of calling of_get_mac_address() just for the nvmem step, avoiding to repeat an expensive DT lookup which has already been done once. Eventually, fwnode_get_mac_address() should probably be updated to perform the nvmem lookup directly, but as of today, nvmem cells seem not to be supported by ACPI yet which would defeat this kind of extension. Suggested-by: Marcin Wojtas Signed-off-by: Miquel Raynal Signed-off-by: Paolo Abeni --- include/linux/of_net.h | 6 ++++++ net/core/of_net.c | 5 +++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/include/linux/of_net.h b/include/linux/of_net.h index 0484b613ca64..d88715a0b3a5 100644 --- a/include/linux/of_net.h +++ b/include/linux/of_net.h @@ -14,6 +14,7 @@ struct net_device; extern int of_get_phy_mode(struct device_node *np, phy_interface_t *interface); extern int of_get_mac_address(struct device_node *np, u8 *mac); +extern int of_get_mac_address_nvmem(struct device_node *np, u8 *mac); int of_get_ethdev_address(struct device_node *np, struct net_device *dev); extern struct net_device *of_find_net_device_by_node(struct device_node *np); #else @@ -28,6 +29,11 @@ static inline int of_get_mac_address(struct device_node *np, u8 *mac) return -ENODEV; } +static inline int of_get_mac_address_nvmem(struct device_node *np, u8 *mac) +{ + return -ENODEV; +} + static inline int of_get_ethdev_address(struct device_node *np, struct net_device *dev) { return -ENODEV; diff --git a/net/core/of_net.c b/net/core/of_net.c index f1a9bf7578e7..55d3fe229269 100644 --- a/net/core/of_net.c +++ b/net/core/of_net.c @@ -57,7 +57,7 @@ static int of_get_mac_addr(struct device_node *np, const char *name, u8 *addr) return -ENODEV; } -static int of_get_mac_addr_nvmem(struct device_node *np, u8 *addr) +int of_get_mac_address_nvmem(struct device_node *np, u8 *addr) { struct platform_device *pdev = of_find_device_by_node(np); struct nvmem_cell *cell; @@ -94,6 +94,7 @@ static int of_get_mac_addr_nvmem(struct device_node *np, u8 *addr) return 0; } +EXPORT_SYMBOL(of_get_mac_address_nvmem); /** * of_get_mac_address() @@ -140,7 +141,7 @@ int of_get_mac_address(struct device_node *np, u8 *addr) if (!ret) return 0; - return of_get_mac_addr_nvmem(np, addr); + return of_get_mac_address_nvmem(np, addr); } EXPORT_SYMBOL(of_get_mac_address); From a48acad789ff33d90e079311ed0323e5e5fc5cbd Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Thu, 24 Nov 2022 12:15:55 +0100 Subject: [PATCH 1282/2002] net: marvell: prestera: Avoid unnecessary DT lookups This driver fist makes an expensive DT lookup to retrieve its DT node (this is a PCI driver) in order to later search for the base-mac-provider property. This property has no reality upstream and this code should not have been accepted like this in the first place. Instead, there is a proper nvmem interface that should be used. Let's avoid these extra lookups and rely on the nvmem internal logic. Signed-off-by: Miquel Raynal Signed-off-by: Paolo Abeni --- .../net/ethernet/marvell/prestera/prestera_main.c | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/drivers/net/ethernet/marvell/prestera/prestera_main.c b/drivers/net/ethernet/marvell/prestera/prestera_main.c index f8deaee84398..8343f203ff23 100644 --- a/drivers/net/ethernet/marvell/prestera/prestera_main.c +++ b/drivers/net/ethernet/marvell/prestera/prestera_main.c @@ -858,17 +858,10 @@ static void prestera_event_handlers_unregister(struct prestera_switch *sw) static int prestera_switch_set_base_mac_addr(struct prestera_switch *sw) { - struct device_node *base_mac_np; - int ret = 0; - - if (sw->np) { - base_mac_np = of_parse_phandle(sw->np, "base-mac-provider", 0); - if (base_mac_np) { - ret = of_get_mac_address(base_mac_np, sw->base_mac); - of_node_put(base_mac_np); - } - } + int ret; + if (sw->np) + ret = of_get_mac_address(sw->np, sw->base_mac); if (!is_valid_ether_addr(sw->base_mac) || ret) { eth_random_addr(sw->base_mac); dev_info(prestera_dev(sw), "using random base mac address\n"); @@ -1372,7 +1365,7 @@ static int prestera_switch_init(struct prestera_switch *sw) { int err; - sw->np = of_find_compatible_node(NULL, NULL, "marvell,prestera"); + sw->np = sw->dev->dev->of_node; err = prestera_hw_switch_init(sw); if (err) { From 7a74c1265ab4340b0fd5223084151003ef4c394a Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Thu, 24 Nov 2022 12:15:56 +0100 Subject: [PATCH 1283/2002] net: mvpp2: Consider NVMEM cells as possible MAC address source The ONIE standard describes the organization of tlv (type-length-value) arrays commonly stored within NVMEM devices on common networking hardware. Several drivers already make use of NVMEM cells for purposes like retrieving a default MAC address provided by the manufacturer. What made ONIE tables unusable so far was the fact that the information where "dynamically" located within the table depending on the manufacturer wishes, while Linux NVMEM support only allowed statically defined NVMEM cells. Fortunately, this limitation was eventually tackled with the introduction of discoverable cells through the use of NVMEM layouts, making it possible to extract and consistently use the content of tables like ONIE's tlv arrays. Parsing this table at runtime in order to get various information is now possible. So, because many Marvell networking switches already follow this standard, let's consider using NVMEM cells as a new valid source of information when looking for a base MAC address, which is one of the primary uses of these new fields. Indeed, manufacturers following the ONIE standard are encouraged to provide a default MAC address there, so let's eventually use it if no other MAC address has been found using the existing methods. Link: https://opencomputeproject.github.io/onie/design-spec/hw_requirements.html Signed-off-by: Miquel Raynal Signed-off-by: Paolo Abeni --- drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c b/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c index d98f7e9a480e..dd41ead416b2 100644 --- a/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c +++ b/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c @@ -6104,6 +6104,13 @@ static void mvpp2_port_copy_mac_addr(struct net_device *dev, struct mvpp2 *priv, } } + /* Only valid on OF enabled platforms */ + if (!of_get_mac_address_nvmem(to_of_node(fwnode), fw_mac_addr)) { + *mac_from = "nvmem cell"; + eth_hw_addr_set(dev, fw_mac_addr); + return; + } + *mac_from = "random"; eth_hw_addr_random(dev); } From b0488c4598a599157ac9b588541c3c6c5055e80f Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Thu, 24 Nov 2022 16:22:51 +0100 Subject: [PATCH 1284/2002] net: ethernet: mtk_wed: return status value in mtk_wdma_rx_reset Move MTK_WDMA_RESET_IDX configuration in mtk_wdma_rx_reset routine. Increase poll timeout to 10ms in order to be aligned with vendor sdk. This is a preliminary patch to add Wireless Ethernet Dispatcher reset support. Co-developed-by: Sujuan Chen Signed-off-by: Sujuan Chen Signed-off-by: Lorenzo Bianconi Signed-off-by: Paolo Abeni --- drivers/net/ethernet/mediatek/mtk_wed.c | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/drivers/net/ethernet/mediatek/mtk_wed.c b/drivers/net/ethernet/mediatek/mtk_wed.c index 16d7af88bbac..4ab2ec9dc5d3 100644 --- a/drivers/net/ethernet/mediatek/mtk_wed.c +++ b/drivers/net/ethernet/mediatek/mtk_wed.c @@ -101,17 +101,21 @@ mtk_wdma_read_reset(struct mtk_wed_device *dev) return wdma_r32(dev, MTK_WDMA_GLO_CFG); } -static void +static int mtk_wdma_rx_reset(struct mtk_wed_device *dev) { u32 status, mask = MTK_WDMA_GLO_CFG_RX_DMA_BUSY; - int i; + int i, ret; wdma_clr(dev, MTK_WDMA_GLO_CFG, MTK_WDMA_GLO_CFG_RX_DMA_EN); - if (readx_poll_timeout(mtk_wdma_read_reset, dev, status, - !(status & mask), 0, 1000)) + ret = readx_poll_timeout(mtk_wdma_read_reset, dev, status, + !(status & mask), 0, 10000); + if (ret) dev_err(dev->hw->dev, "rx reset failed\n"); + wdma_w32(dev, MTK_WDMA_RESET_IDX, MTK_WDMA_RESET_IDX_RX); + wdma_w32(dev, MTK_WDMA_RESET_IDX, 0); + for (i = 0; i < ARRAY_SIZE(dev->rx_wdma); i++) { if (dev->rx_wdma[i].desc) continue; @@ -119,6 +123,8 @@ mtk_wdma_rx_reset(struct mtk_wed_device *dev) wdma_w32(dev, MTK_WDMA_RING_RX(i) + MTK_WED_RING_OFS_CPU_IDX, 0); } + + return ret; } static void @@ -565,9 +571,7 @@ mtk_wed_detach(struct mtk_wed_device *dev) mtk_wed_stop(dev); - wdma_w32(dev, MTK_WDMA_RESET_IDX, MTK_WDMA_RESET_IDX_RX); - wdma_w32(dev, MTK_WDMA_RESET_IDX, 0); - + mtk_wdma_rx_reset(dev); mtk_wed_reset(dev, MTK_WED_RESET_WED); if (mtk_wed_get_rx_capa(dev)) { wdma_clr(dev, MTK_WDMA_GLO_CFG, MTK_WDMA_GLO_CFG_TX_DMA_EN); @@ -582,7 +586,6 @@ mtk_wed_detach(struct mtk_wed_device *dev) mtk_wed_wo_reset(dev); mtk_wed_free_rx_rings(dev); mtk_wed_wo_deinit(hw); - mtk_wdma_rx_reset(dev); } if (dev->wlan.bus_type == MTK_WED_BUS_PCIE) { @@ -999,11 +1002,7 @@ mtk_wed_reset_dma(struct mtk_wed_device *dev) wed_w32(dev, MTK_WED_RESET_IDX, 0); } - wdma_w32(dev, MTK_WDMA_RESET_IDX, MTK_WDMA_RESET_IDX_RX); - wdma_w32(dev, MTK_WDMA_RESET_IDX, 0); - - if (mtk_wed_get_rx_capa(dev)) - mtk_wdma_rx_reset(dev); + mtk_wdma_rx_reset(dev); if (busy) { mtk_wed_reset(dev, MTK_WED_RESET_WDMA_INT_AGENT); From 92b1169660ebe80e09c546555521a043cf7d26bc Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Thu, 24 Nov 2022 16:22:52 +0100 Subject: [PATCH 1285/2002] net: ethernet: mtk_wed: move MTK_WDMA_RESET_IDX_TX configuration in mtk_wdma_tx_reset Remove duplicated code. Increase poll timeout to 10ms in order to be aligned with vendor sdk. This is a preliminary patch to add Wireless Ethernet Dispatcher reset support. Co-developed-by: Sujuan Chen Signed-off-by: Sujuan Chen Signed-off-by: Lorenzo Bianconi Signed-off-by: Paolo Abeni --- drivers/net/ethernet/mediatek/mtk_wed.c | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/drivers/net/ethernet/mediatek/mtk_wed.c b/drivers/net/ethernet/mediatek/mtk_wed.c index 4ab2ec9dc5d3..dbe10bbdb6f6 100644 --- a/drivers/net/ethernet/mediatek/mtk_wed.c +++ b/drivers/net/ethernet/mediatek/mtk_wed.c @@ -135,16 +135,15 @@ mtk_wdma_tx_reset(struct mtk_wed_device *dev) wdma_clr(dev, MTK_WDMA_GLO_CFG, MTK_WDMA_GLO_CFG_TX_DMA_EN); if (readx_poll_timeout(mtk_wdma_read_reset, dev, status, - !(status & mask), 0, 1000)) + !(status & mask), 0, 10000)) dev_err(dev->hw->dev, "tx reset failed\n"); - for (i = 0; i < ARRAY_SIZE(dev->tx_wdma); i++) { - if (dev->tx_wdma[i].desc) - continue; + wdma_w32(dev, MTK_WDMA_RESET_IDX, MTK_WDMA_RESET_IDX_TX); + wdma_w32(dev, MTK_WDMA_RESET_IDX, 0); + for (i = 0; i < ARRAY_SIZE(dev->tx_wdma); i++) wdma_w32(dev, MTK_WDMA_RING_TX(i) + MTK_WED_RING_OFS_CPU_IDX, 0); - } } static void @@ -573,12 +572,6 @@ mtk_wed_detach(struct mtk_wed_device *dev) mtk_wdma_rx_reset(dev); mtk_wed_reset(dev, MTK_WED_RESET_WED); - if (mtk_wed_get_rx_capa(dev)) { - wdma_clr(dev, MTK_WDMA_GLO_CFG, MTK_WDMA_GLO_CFG_TX_DMA_EN); - wdma_w32(dev, MTK_WDMA_RESET_IDX, MTK_WDMA_RESET_IDX_TX); - wdma_w32(dev, MTK_WDMA_RESET_IDX, 0); - } - mtk_wed_free_tx_buffer(dev); mtk_wed_free_tx_rings(dev); From f78cd9c783e09a0fe454b0fc8b39c22025d7869e Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Thu, 24 Nov 2022 16:22:53 +0100 Subject: [PATCH 1286/2002] net: ethernet: mtk_wed: update mtk_wed_stop Update mtk_wed_stop routine and rename old mtk_wed_stop() to mtk_wed_deinit(). This is a preliminary patch to add Wireless Ethernet Dispatcher reset support. Co-developed-by: Sujuan Chen Signed-off-by: Sujuan Chen Signed-off-by: Lorenzo Bianconi Signed-off-by: Paolo Abeni --- drivers/net/ethernet/mediatek/mtk_wed.c | 32 ++++++++++++++++++------- include/linux/soc/mediatek/mtk_wed.h | 4 ++++ 2 files changed, 27 insertions(+), 9 deletions(-) diff --git a/drivers/net/ethernet/mediatek/mtk_wed.c b/drivers/net/ethernet/mediatek/mtk_wed.c index dbe10bbdb6f6..42b7e0407a9b 100644 --- a/drivers/net/ethernet/mediatek/mtk_wed.c +++ b/drivers/net/ethernet/mediatek/mtk_wed.c @@ -539,14 +539,8 @@ mtk_wed_dma_disable(struct mtk_wed_device *dev) static void mtk_wed_stop(struct mtk_wed_device *dev) { - mtk_wed_dma_disable(dev); mtk_wed_set_ext_int(dev, false); - wed_clr(dev, MTK_WED_CTRL, - MTK_WED_CTRL_WDMA_INT_AGENT_EN | - MTK_WED_CTRL_WPDMA_INT_AGENT_EN | - MTK_WED_CTRL_WED_TX_BM_EN | - MTK_WED_CTRL_WED_TX_FREE_AGENT_EN); wed_w32(dev, MTK_WED_WPDMA_INT_TRIGGER, 0); wed_w32(dev, MTK_WED_WDMA_INT_TRIGGER, 0); wdma_w32(dev, MTK_WDMA_INT_MASK, 0); @@ -558,7 +552,27 @@ mtk_wed_stop(struct mtk_wed_device *dev) wed_w32(dev, MTK_WED_EXT_INT_MASK1, 0); wed_w32(dev, MTK_WED_EXT_INT_MASK2, 0); - wed_clr(dev, MTK_WED_CTRL, MTK_WED_CTRL_WED_RX_BM_EN); +} + +static void +mtk_wed_deinit(struct mtk_wed_device *dev) +{ + mtk_wed_stop(dev); + mtk_wed_dma_disable(dev); + + wed_clr(dev, MTK_WED_CTRL, + MTK_WED_CTRL_WDMA_INT_AGENT_EN | + MTK_WED_CTRL_WPDMA_INT_AGENT_EN | + MTK_WED_CTRL_WED_TX_BM_EN | + MTK_WED_CTRL_WED_TX_FREE_AGENT_EN); + + if (dev->hw->version == 1) + return; + + wed_clr(dev, MTK_WED_CTRL, + MTK_WED_CTRL_RX_ROUTE_QM_EN | + MTK_WED_CTRL_WED_RX_BM_EN | + MTK_WED_CTRL_RX_RRO_QM_EN); } static void @@ -568,7 +582,7 @@ mtk_wed_detach(struct mtk_wed_device *dev) mutex_lock(&hw_lock); - mtk_wed_stop(dev); + mtk_wed_deinit(dev); mtk_wdma_rx_reset(dev); mtk_wed_reset(dev, MTK_WED_RESET_WED); @@ -670,7 +684,7 @@ mtk_wed_hw_init_early(struct mtk_wed_device *dev) { u32 mask, set; - mtk_wed_stop(dev); + mtk_wed_deinit(dev); mtk_wed_reset(dev, MTK_WED_RESET_WED); mtk_wed_set_wpdma(dev); diff --git a/include/linux/soc/mediatek/mtk_wed.h b/include/linux/soc/mediatek/mtk_wed.h index 1b1ef57609f7..c43510e541df 100644 --- a/include/linux/soc/mediatek/mtk_wed.h +++ b/include/linux/soc/mediatek/mtk_wed.h @@ -234,6 +234,8 @@ mtk_wed_get_rx_capa(struct mtk_wed_device *dev) (_dev)->ops->ppe_check(_dev, _skb, _reason, _hash) #define mtk_wed_device_update_msg(_dev, _id, _msg, _len) \ (_dev)->ops->msg_update(_dev, _id, _msg, _len) +#define mtk_wed_device_stop(_dev) (_dev)->ops->stop(_dev) +#define mtk_wed_device_dma_reset(_dev) (_dev)->ops->reset_dma(_dev) #else static inline bool mtk_wed_device_active(struct mtk_wed_device *dev) { @@ -250,6 +252,8 @@ static inline bool mtk_wed_device_active(struct mtk_wed_device *dev) #define mtk_wed_device_rx_ring_setup(_dev, _ring, _regs) -ENODEV #define mtk_wed_device_ppe_check(_dev, _skb, _reason, _hash) do {} while (0) #define mtk_wed_device_update_msg(_dev, _id, _msg, _len) -ENODEV +#define mtk_wed_device_stop(_dev) do {} while (0) +#define mtk_wed_device_dma_reset(_dev) do {} while (0) #endif #endif From b08134c6e10938c8de3e47763674a2aa6f380d92 Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Thu, 24 Nov 2022 16:22:54 +0100 Subject: [PATCH 1287/2002] net: ethernet: mtk_wed: add mtk_wed_rx_reset routine Introduce mtk_wed_rx_reset routine in order to reset rx DMA for Wireless Ethernet Dispatcher available on MT7986 SoC. Co-developed-by: Sujuan Chen Signed-off-by: Sujuan Chen Signed-off-by: Lorenzo Bianconi Signed-off-by: Paolo Abeni --- drivers/net/ethernet/mediatek/mtk_wed.c | 190 +++++++++++++++---- drivers/net/ethernet/mediatek/mtk_wed_regs.h | 9 + 2 files changed, 162 insertions(+), 37 deletions(-) diff --git a/drivers/net/ethernet/mediatek/mtk_wed.c b/drivers/net/ethernet/mediatek/mtk_wed.c index 42b7e0407a9b..9594885d5e50 100644 --- a/drivers/net/ethernet/mediatek/mtk_wed.c +++ b/drivers/net/ethernet/mediatek/mtk_wed.c @@ -944,42 +944,130 @@ mtk_wed_ring_reset(struct mtk_wed_ring *ring, int size, bool tx) } static u32 -mtk_wed_check_busy(struct mtk_wed_device *dev) +mtk_wed_check_busy(struct mtk_wed_device *dev, u32 reg, u32 mask) { - if (wed_r32(dev, MTK_WED_GLO_CFG) & MTK_WED_GLO_CFG_TX_DMA_BUSY) - return true; - - if (wed_r32(dev, MTK_WED_WPDMA_GLO_CFG) & - MTK_WED_WPDMA_GLO_CFG_TX_DRV_BUSY) - return true; - - if (wed_r32(dev, MTK_WED_CTRL) & MTK_WED_CTRL_WDMA_INT_AGENT_BUSY) - return true; - - if (wed_r32(dev, MTK_WED_WDMA_GLO_CFG) & - MTK_WED_WDMA_GLO_CFG_RX_DRV_BUSY) - return true; - - if (wdma_r32(dev, MTK_WDMA_GLO_CFG) & - MTK_WED_WDMA_GLO_CFG_RX_DRV_BUSY) - return true; - - if (wed_r32(dev, MTK_WED_CTRL) & - (MTK_WED_CTRL_WED_TX_BM_BUSY | MTK_WED_CTRL_WED_TX_FREE_AGENT_BUSY)) - return true; - - return false; + return !!(wed_r32(dev, reg) & mask); } static int -mtk_wed_poll_busy(struct mtk_wed_device *dev) +mtk_wed_poll_busy(struct mtk_wed_device *dev, u32 reg, u32 mask) { int sleep = 15000; int timeout = 100 * sleep; u32 val; return read_poll_timeout(mtk_wed_check_busy, val, !val, sleep, - timeout, false, dev); + timeout, false, dev, reg, mask); +} + +static int +mtk_wed_rx_reset(struct mtk_wed_device *dev) +{ + struct mtk_wed_wo *wo = dev->hw->wed_wo; + u8 val = MTK_WED_WO_STATE_SER_RESET; + int i, ret; + + ret = mtk_wed_mcu_send_msg(wo, MTK_WED_MODULE_ID_WO, + MTK_WED_WO_CMD_CHANGE_STATE, &val, + sizeof(val), true); + if (ret) + return ret; + + wed_clr(dev, MTK_WED_WPDMA_RX_D_GLO_CFG, MTK_WED_WPDMA_RX_D_RX_DRV_EN); + ret = mtk_wed_poll_busy(dev, MTK_WED_WPDMA_RX_D_GLO_CFG, + MTK_WED_WPDMA_RX_D_RX_DRV_BUSY); + if (ret) { + mtk_wed_reset(dev, MTK_WED_RESET_WPDMA_INT_AGENT); + mtk_wed_reset(dev, MTK_WED_RESET_WPDMA_RX_D_DRV); + } else { + wed_w32(dev, MTK_WED_WPDMA_RX_D_RST_IDX, + MTK_WED_WPDMA_RX_D_RST_CRX_IDX | + MTK_WED_WPDMA_RX_D_RST_DRV_IDX); + + wed_set(dev, MTK_WED_WPDMA_RX_D_GLO_CFG, + MTK_WED_WPDMA_RX_D_RST_INIT_COMPLETE | + MTK_WED_WPDMA_RX_D_FSM_RETURN_IDLE); + wed_clr(dev, MTK_WED_WPDMA_RX_D_GLO_CFG, + MTK_WED_WPDMA_RX_D_RST_INIT_COMPLETE | + MTK_WED_WPDMA_RX_D_FSM_RETURN_IDLE); + + wed_w32(dev, MTK_WED_WPDMA_RX_D_RST_IDX, 0); + } + + /* reset rro qm */ + wed_clr(dev, MTK_WED_CTRL, MTK_WED_CTRL_RX_RRO_QM_EN); + ret = mtk_wed_poll_busy(dev, MTK_WED_CTRL, + MTK_WED_CTRL_RX_RRO_QM_BUSY); + if (ret) { + mtk_wed_reset(dev, MTK_WED_RESET_RX_RRO_QM); + } else { + wed_set(dev, MTK_WED_RROQM_RST_IDX, + MTK_WED_RROQM_RST_IDX_MIOD | + MTK_WED_RROQM_RST_IDX_FDBK); + wed_w32(dev, MTK_WED_RROQM_RST_IDX, 0); + } + + /* reset route qm */ + wed_clr(dev, MTK_WED_CTRL, MTK_WED_CTRL_RX_ROUTE_QM_EN); + ret = mtk_wed_poll_busy(dev, MTK_WED_CTRL, + MTK_WED_CTRL_RX_ROUTE_QM_BUSY); + if (ret) + mtk_wed_reset(dev, MTK_WED_RESET_RX_ROUTE_QM); + else + wed_set(dev, MTK_WED_RTQM_GLO_CFG, + MTK_WED_RTQM_Q_RST); + + /* reset tx wdma */ + mtk_wdma_tx_reset(dev); + + /* reset tx wdma drv */ + wed_clr(dev, MTK_WED_WDMA_GLO_CFG, MTK_WED_WDMA_GLO_CFG_TX_DRV_EN); + mtk_wed_poll_busy(dev, MTK_WED_CTRL, + MTK_WED_CTRL_WDMA_INT_AGENT_BUSY); + mtk_wed_reset(dev, MTK_WED_RESET_WDMA_TX_DRV); + + /* reset wed rx dma */ + ret = mtk_wed_poll_busy(dev, MTK_WED_GLO_CFG, + MTK_WED_GLO_CFG_RX_DMA_BUSY); + wed_clr(dev, MTK_WED_GLO_CFG, MTK_WED_GLO_CFG_RX_DMA_EN); + if (ret) { + mtk_wed_reset(dev, MTK_WED_RESET_WED_RX_DMA); + } else { + struct mtk_eth *eth = dev->hw->eth; + + if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) + wed_set(dev, MTK_WED_RESET_IDX, + MTK_WED_RESET_IDX_RX_V2); + else + wed_set(dev, MTK_WED_RESET_IDX, MTK_WED_RESET_IDX_RX); + wed_w32(dev, MTK_WED_RESET_IDX, 0); + } + + /* reset rx bm */ + wed_clr(dev, MTK_WED_CTRL, MTK_WED_CTRL_WED_RX_BM_EN); + mtk_wed_poll_busy(dev, MTK_WED_CTRL, + MTK_WED_CTRL_WED_RX_BM_BUSY); + mtk_wed_reset(dev, MTK_WED_RESET_RX_BM); + + /* wo change to enable state */ + val = MTK_WED_WO_STATE_ENABLE; + ret = mtk_wed_mcu_send_msg(wo, MTK_WED_MODULE_ID_WO, + MTK_WED_WO_CMD_CHANGE_STATE, &val, + sizeof(val), true); + if (ret) + return ret; + + /* wed_rx_ring_reset */ + for (i = 0; i < ARRAY_SIZE(dev->rx_ring); i++) { + if (!dev->rx_ring[i].desc) + continue; + + mtk_wed_ring_reset(&dev->rx_ring[i], MTK_WED_RX_RING_SIZE, + false); + } + mtk_wed_free_rx_buffer(dev); + + return 0; } static void @@ -997,19 +1085,23 @@ mtk_wed_reset_dma(struct mtk_wed_device *dev) true); } - if (mtk_wed_poll_busy(dev)) - busy = mtk_wed_check_busy(dev); - + /* 1. reset WED tx DMA */ + wed_clr(dev, MTK_WED_GLO_CFG, MTK_WED_GLO_CFG_TX_DMA_EN); + busy = mtk_wed_poll_busy(dev, MTK_WED_GLO_CFG, + MTK_WED_GLO_CFG_TX_DMA_BUSY); if (busy) { mtk_wed_reset(dev, MTK_WED_RESET_WED_TX_DMA); } else { - wed_w32(dev, MTK_WED_RESET_IDX, - MTK_WED_RESET_IDX_TX | - MTK_WED_RESET_IDX_RX); + wed_w32(dev, MTK_WED_RESET_IDX, MTK_WED_RESET_IDX_TX); wed_w32(dev, MTK_WED_RESET_IDX, 0); } - mtk_wdma_rx_reset(dev); + /* 2. reset WDMA rx DMA */ + busy = !!mtk_wdma_rx_reset(dev); + wed_clr(dev, MTK_WED_WDMA_GLO_CFG, MTK_WED_WDMA_GLO_CFG_RX_DRV_EN); + if (!busy) + busy = mtk_wed_poll_busy(dev, MTK_WED_WDMA_GLO_CFG, + MTK_WED_WDMA_GLO_CFG_RX_DRV_BUSY); if (busy) { mtk_wed_reset(dev, MTK_WED_RESET_WDMA_INT_AGENT); @@ -1026,6 +1118,9 @@ mtk_wed_reset_dma(struct mtk_wed_device *dev) MTK_WED_WDMA_GLO_CFG_RST_INIT_COMPLETE); } + /* 3. reset WED WPDMA tx */ + wed_clr(dev, MTK_WED_CTRL, MTK_WED_CTRL_WED_TX_FREE_AGENT_EN); + for (i = 0; i < 100; i++) { val = wed_r32(dev, MTK_WED_TX_BM_INTF); if (FIELD_GET(MTK_WED_TX_BM_INTF_TKFIFO_FDEP, val) == 0x40) @@ -1033,8 +1128,19 @@ mtk_wed_reset_dma(struct mtk_wed_device *dev) } mtk_wed_reset(dev, MTK_WED_RESET_TX_FREE_AGENT); + wed_clr(dev, MTK_WED_CTRL, MTK_WED_CTRL_WED_TX_BM_EN); mtk_wed_reset(dev, MTK_WED_RESET_TX_BM); + /* 4. reset WED WPDMA tx */ + busy = mtk_wed_poll_busy(dev, MTK_WED_WPDMA_GLO_CFG, + MTK_WED_WPDMA_GLO_CFG_TX_DRV_BUSY); + wed_clr(dev, MTK_WED_WPDMA_GLO_CFG, + MTK_WED_WPDMA_GLO_CFG_TX_DRV_EN | + MTK_WED_WPDMA_GLO_CFG_RX_DRV_EN); + if (!busy) + busy = mtk_wed_poll_busy(dev, MTK_WED_WPDMA_GLO_CFG, + MTK_WED_WPDMA_GLO_CFG_RX_DRV_BUSY); + if (busy) { mtk_wed_reset(dev, MTK_WED_RESET_WPDMA_INT_AGENT); mtk_wed_reset(dev, MTK_WED_RESET_WPDMA_TX_DRV); @@ -1045,6 +1151,17 @@ mtk_wed_reset_dma(struct mtk_wed_device *dev) MTK_WED_WPDMA_RESET_IDX_RX); wed_w32(dev, MTK_WED_WPDMA_RESET_IDX, 0); } + + dev->init_done = false; + if (dev->hw->version == 1) + return; + + if (!busy) { + wed_w32(dev, MTK_WED_RESET_IDX, MTK_WED_RESET_WPDMA_IDX_RX); + wed_w32(dev, MTK_WED_RESET_IDX, 0); + } + + mtk_wed_rx_reset(dev); } static int @@ -1267,6 +1384,9 @@ mtk_wed_start(struct mtk_wed_device *dev, u32 irq_mask) { int i; + if (mtk_wed_get_rx_capa(dev) && mtk_wed_rx_buffer_alloc(dev)) + return; + for (i = 0; i < ARRAY_SIZE(dev->rx_wdma); i++) if (!dev->rx_wdma[i].desc) mtk_wed_wdma_rx_ring_setup(dev, i, 16); @@ -1355,10 +1475,6 @@ mtk_wed_attach(struct mtk_wed_device *dev) goto out; if (mtk_wed_get_rx_capa(dev)) { - ret = mtk_wed_rx_buffer_alloc(dev); - if (ret) - goto out; - ret = mtk_wed_rro_alloc(dev); if (ret) goto out; diff --git a/drivers/net/ethernet/mediatek/mtk_wed_regs.h b/drivers/net/ethernet/mediatek/mtk_wed_regs.h index 873d50b9a6e6..0a50bb98c5ea 100644 --- a/drivers/net/ethernet/mediatek/mtk_wed_regs.h +++ b/drivers/net/ethernet/mediatek/mtk_wed_regs.h @@ -24,11 +24,15 @@ struct mtk_wdma_desc { #define MTK_WED_RESET 0x008 #define MTK_WED_RESET_TX_BM BIT(0) +#define MTK_WED_RESET_RX_BM BIT(1) #define MTK_WED_RESET_TX_FREE_AGENT BIT(4) #define MTK_WED_RESET_WPDMA_TX_DRV BIT(8) #define MTK_WED_RESET_WPDMA_RX_DRV BIT(9) +#define MTK_WED_RESET_WPDMA_RX_D_DRV BIT(10) #define MTK_WED_RESET_WPDMA_INT_AGENT BIT(11) #define MTK_WED_RESET_WED_TX_DMA BIT(12) +#define MTK_WED_RESET_WED_RX_DMA BIT(13) +#define MTK_WED_RESET_WDMA_TX_DRV BIT(16) #define MTK_WED_RESET_WDMA_RX_DRV BIT(17) #define MTK_WED_RESET_WDMA_INT_AGENT BIT(19) #define MTK_WED_RESET_RX_RRO_QM BIT(20) @@ -158,6 +162,8 @@ struct mtk_wdma_desc { #define MTK_WED_RESET_IDX 0x20c #define MTK_WED_RESET_IDX_TX GENMASK(3, 0) #define MTK_WED_RESET_IDX_RX GENMASK(17, 16) +#define MTK_WED_RESET_IDX_RX_V2 GENMASK(7, 6) +#define MTK_WED_RESET_WPDMA_IDX_RX GENMASK(31, 30) #define MTK_WED_TX_MIB(_n) (0x2a0 + (_n) * 4) #define MTK_WED_RX_MIB(_n) (0x2e0 + (_n) * 4) @@ -267,6 +273,9 @@ struct mtk_wdma_desc { #define MTK_WED_WPDMA_RX_D_GLO_CFG 0x75c #define MTK_WED_WPDMA_RX_D_RX_DRV_EN BIT(0) +#define MTK_WED_WPDMA_RX_D_RX_DRV_BUSY BIT(1) +#define MTK_WED_WPDMA_RX_D_FSM_RETURN_IDLE BIT(3) +#define MTK_WED_WPDMA_RX_D_RST_INIT_COMPLETE BIT(4) #define MTK_WED_WPDMA_RX_D_INIT_PHASE_RXEN_SEL GENMASK(11, 7) #define MTK_WED_WPDMA_RX_D_RXD_READ_LEN GENMASK(31, 24) From 23dca7a90017ff2512c501f7da4c7ca7a95c2d6e Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Thu, 24 Nov 2022 16:22:55 +0100 Subject: [PATCH 1288/2002] net: ethernet: mtk_wed: add reset to tx_ring_setup callback Introduce reset parameter to mtk_wed_tx_ring_setup signature. This is a preliminary patch to add Wireless Ethernet Dispatcher reset support. Co-developed-by: Sujuan Chen Signed-off-by: Sujuan Chen Signed-off-by: Lorenzo Bianconi Signed-off-by: Paolo Abeni --- drivers/net/ethernet/mediatek/mtk_wed.c | 19 +++++++++++-------- drivers/net/wireless/mediatek/mt76/dma.c | 2 +- include/linux/soc/mediatek/mtk_wed.h | 8 ++++---- 3 files changed, 16 insertions(+), 13 deletions(-) diff --git a/drivers/net/ethernet/mediatek/mtk_wed.c b/drivers/net/ethernet/mediatek/mtk_wed.c index 9594885d5e50..d041615b2bac 100644 --- a/drivers/net/ethernet/mediatek/mtk_wed.c +++ b/drivers/net/ethernet/mediatek/mtk_wed.c @@ -1181,7 +1181,8 @@ mtk_wed_ring_alloc(struct mtk_wed_device *dev, struct mtk_wed_ring *ring, } static int -mtk_wed_wdma_rx_ring_setup(struct mtk_wed_device *dev, int idx, int size) +mtk_wed_wdma_rx_ring_setup(struct mtk_wed_device *dev, int idx, int size, + bool reset) { u32 desc_size = sizeof(struct mtk_wdma_desc) * dev->hw->version; struct mtk_wed_ring *wdma; @@ -1190,8 +1191,8 @@ mtk_wed_wdma_rx_ring_setup(struct mtk_wed_device *dev, int idx, int size) return -EINVAL; wdma = &dev->rx_wdma[idx]; - if (mtk_wed_ring_alloc(dev, wdma, MTK_WED_WDMA_RING_SIZE, desc_size, - true)) + if (!reset && mtk_wed_ring_alloc(dev, wdma, MTK_WED_WDMA_RING_SIZE, + desc_size, true)) return -ENOMEM; wdma_w32(dev, MTK_WDMA_RING_RX(idx) + MTK_WED_RING_OFS_BASE, @@ -1389,7 +1390,7 @@ mtk_wed_start(struct mtk_wed_device *dev, u32 irq_mask) for (i = 0; i < ARRAY_SIZE(dev->rx_wdma); i++) if (!dev->rx_wdma[i].desc) - mtk_wed_wdma_rx_ring_setup(dev, i, 16); + mtk_wed_wdma_rx_ring_setup(dev, i, 16, false); mtk_wed_hw_init(dev); mtk_wed_configure_irq(dev, irq_mask); @@ -1498,7 +1499,8 @@ unlock: } static int -mtk_wed_tx_ring_setup(struct mtk_wed_device *dev, int idx, void __iomem *regs) +mtk_wed_tx_ring_setup(struct mtk_wed_device *dev, int idx, void __iomem *regs, + bool reset) { struct mtk_wed_ring *ring = &dev->tx_ring[idx]; @@ -1517,11 +1519,12 @@ mtk_wed_tx_ring_setup(struct mtk_wed_device *dev, int idx, void __iomem *regs) if (WARN_ON(idx >= ARRAY_SIZE(dev->tx_ring))) return -EINVAL; - if (mtk_wed_ring_alloc(dev, ring, MTK_WED_TX_RING_SIZE, - sizeof(*ring->desc), true)) + if (!reset && mtk_wed_ring_alloc(dev, ring, MTK_WED_TX_RING_SIZE, + sizeof(*ring->desc), true)) return -ENOMEM; - if (mtk_wed_wdma_rx_ring_setup(dev, idx, MTK_WED_WDMA_RING_SIZE)) + if (mtk_wed_wdma_rx_ring_setup(dev, idx, MTK_WED_WDMA_RING_SIZE, + reset)) return -ENOMEM; ring->reg_base = MTK_WED_RING_TX(idx); diff --git a/drivers/net/wireless/mediatek/mt76/dma.c b/drivers/net/wireless/mediatek/mt76/dma.c index 7378c4d1e156..28cbf07046c3 100644 --- a/drivers/net/wireless/mediatek/mt76/dma.c +++ b/drivers/net/wireless/mediatek/mt76/dma.c @@ -502,7 +502,7 @@ mt76_dma_wed_setup(struct mt76_dev *dev, struct mt76_queue *q) switch (type) { case MT76_WED_Q_TX: - ret = mtk_wed_device_tx_ring_setup(wed, ring, q->regs); + ret = mtk_wed_device_tx_ring_setup(wed, ring, q->regs, false); if (!ret) q->wed_regs = wed->tx_ring[ring].reg_base; break; diff --git a/include/linux/soc/mediatek/mtk_wed.h b/include/linux/soc/mediatek/mtk_wed.h index c43510e541df..beb190449704 100644 --- a/include/linux/soc/mediatek/mtk_wed.h +++ b/include/linux/soc/mediatek/mtk_wed.h @@ -158,7 +158,7 @@ struct mtk_wed_device { struct mtk_wed_ops { int (*attach)(struct mtk_wed_device *dev); int (*tx_ring_setup)(struct mtk_wed_device *dev, int ring, - void __iomem *regs); + void __iomem *regs, bool reset); int (*rx_ring_setup)(struct mtk_wed_device *dev, int ring, void __iomem *regs); int (*txfree_ring_setup)(struct mtk_wed_device *dev, @@ -216,8 +216,8 @@ mtk_wed_get_rx_capa(struct mtk_wed_device *dev) #define mtk_wed_device_active(_dev) !!(_dev)->ops #define mtk_wed_device_detach(_dev) (_dev)->ops->detach(_dev) #define mtk_wed_device_start(_dev, _mask) (_dev)->ops->start(_dev, _mask) -#define mtk_wed_device_tx_ring_setup(_dev, _ring, _regs) \ - (_dev)->ops->tx_ring_setup(_dev, _ring, _regs) +#define mtk_wed_device_tx_ring_setup(_dev, _ring, _regs, _reset) \ + (_dev)->ops->tx_ring_setup(_dev, _ring, _regs, _reset) #define mtk_wed_device_txfree_ring_setup(_dev, _regs) \ (_dev)->ops->txfree_ring_setup(_dev, _regs) #define mtk_wed_device_reg_read(_dev, _reg) \ @@ -243,7 +243,7 @@ static inline bool mtk_wed_device_active(struct mtk_wed_device *dev) } #define mtk_wed_device_detach(_dev) do {} while (0) #define mtk_wed_device_start(_dev, _mask) do {} while (0) -#define mtk_wed_device_tx_ring_setup(_dev, _ring, _regs) -ENODEV +#define mtk_wed_device_tx_ring_setup(_dev, _ring, _regs, _reset) -ENODEV #define mtk_wed_device_txfree_ring_setup(_dev, _ring, _regs) -ENODEV #define mtk_wed_device_reg_read(_dev, _reg) 0 #define mtk_wed_device_reg_write(_dev, _reg, _val) do {} while (0) From 0a335db8c745204a69ad0a18ab8b92a46a6098ea Mon Sep 17 00:00:00 2001 From: Horatiu Vultur Date: Fri, 25 Nov 2022 10:50:02 +0100 Subject: [PATCH 1289/2002] net: microchip: vcap: Merge the vcap_ag_api_kunit.h into vcap_ag_api.h Currently there are 2 files that contain the keyfields, keys, actionfields and actions. First file is used by the kunit while the second one is used by VCAP api. The header file that is used by kunit is just a super set of the of the header file used by VCAP api. Therefore not to have duplicate information in different files which is also harder to maintain, create a single file that is used both by API and by kunit. Signed-off-by: Horatiu Vultur Signed-off-by: Paolo Abeni --- .../net/ethernet/microchip/vcap/vcap_ag_api.h | 467 +++++++++++-- .../microchip/vcap/vcap_ag_api_kunit.h | 643 ------------------ .../net/ethernet/microchip/vcap/vcap_api.h | 3 - 3 files changed, 393 insertions(+), 720 deletions(-) delete mode 100644 drivers/net/ethernet/microchip/vcap/vcap_ag_api_kunit.h diff --git a/drivers/net/ethernet/microchip/vcap/vcap_ag_api.h b/drivers/net/ethernet/microchip/vcap/vcap_ag_api.h index 804d57b9b60a..d6d70af89616 100644 --- a/drivers/net/ethernet/microchip/vcap/vcap_ag_api.h +++ b/drivers/net/ethernet/microchip/vcap/vcap_ag_api.h @@ -11,6 +11,8 @@ #define __VCAP_AG_API__ enum vcap_type { + VCAP_TYPE_ES2, + VCAP_TYPE_IS0, VCAP_TYPE_IS2, VCAP_TYPE_MAX }; @@ -18,12 +20,22 @@ enum vcap_type { /* Keyfieldset names with origin information */ enum vcap_keyfield_set { VCAP_KFS_NO_VALUE, /* initial value */ - VCAP_KFS_ARP, /* sparx5 is2 X6 */ - VCAP_KFS_IP4_OTHER, /* sparx5 is2 X6 */ - VCAP_KFS_IP4_TCP_UDP, /* sparx5 is2 X6 */ + VCAP_KFS_ARP, /* sparx5 is2 X6, sparx5 es2 X6 */ + VCAP_KFS_ETAG, /* sparx5 is0 X2 */ + VCAP_KFS_IP4_OTHER, /* sparx5 is2 X6, sparx5 es2 X6 */ + VCAP_KFS_IP4_TCP_UDP, /* sparx5 is2 X6, sparx5 es2 X6 */ + VCAP_KFS_IP4_VID, /* sparx5 es2 X3 */ VCAP_KFS_IP6_STD, /* sparx5 is2 X6 */ - VCAP_KFS_IP_7TUPLE, /* sparx5 is2 X12 */ - VCAP_KFS_MAC_ETYPE, /* sparx5 is2 X6 */ + VCAP_KFS_IP6_VID, /* sparx5 is2 X6, sparx5 es2 X6 */ + VCAP_KFS_IP_7TUPLE, /* sparx5 is2 X12, sparx5 es2 X12 */ + VCAP_KFS_LL_FULL, /* sparx5 is0 X6 */ + VCAP_KFS_MAC_ETYPE, /* sparx5 is2 X6, sparx5 es2 X6 */ + VCAP_KFS_MLL, /* sparx5 is0 X3 */ + VCAP_KFS_NORMAL, /* sparx5 is0 X6 */ + VCAP_KFS_NORMAL_5TUPLE_IP4, /* sparx5 is0 X6 */ + VCAP_KFS_NORMAL_7TUPLE, /* sparx5 is0 X12 */ + VCAP_KFS_PURE_5TUPLE_IP4, /* sparx5 is0 X3 */ + VCAP_KFS_TRI_VID, /* sparx5 is0 X2 */ }; /* List of keyfields with description @@ -31,34 +43,92 @@ enum vcap_keyfield_set { * Keys ending in _IS are booleans derived from frame data * Keys ending in _CLS are classified frame data * - * VCAP_KF_8021Q_DEI_CLS: W1, sparx5: is2 + * VCAP_KF_8021BR_ECID_BASE: W12, sparx5: is0 + * Used by 802.1BR Bridge Port Extension in an E-Tag + * VCAP_KF_8021BR_ECID_EXT: W8, sparx5: is0 + * Used by 802.1BR Bridge Port Extension in an E-Tag + * VCAP_KF_8021BR_E_TAGGED: W1, sparx5: is0 + * Set for frames containing an E-TAG (802.1BR Ethertype 893f) + * VCAP_KF_8021BR_GRP: W2, sparx5: is0 + * E-Tag group bits in 802.1BR Bridge Port Extension + * VCAP_KF_8021BR_IGR_ECID_BASE: W12, sparx5: is0 + * Used by 802.1BR Bridge Port Extension in an E-Tag + * VCAP_KF_8021BR_IGR_ECID_EXT: W8, sparx5: is0 + * Used by 802.1BR Bridge Port Extension in an E-Tag + * VCAP_KF_8021Q_DEI0: W1, sparx5: is0 + * First DEI in multiple vlan tags (outer tag or default port tag) + * VCAP_KF_8021Q_DEI1: W1, sparx5: is0 + * Second DEI in multiple vlan tags (inner tag) + * VCAP_KF_8021Q_DEI2: W1, sparx5: is0 + * Third DEI in multiple vlan tags (not always available) + * VCAP_KF_8021Q_DEI_CLS: W1, sparx5: is2/es2 * Classified DEI - * VCAP_KF_8021Q_PCP_CLS: W3, sparx5: is2 + * VCAP_KF_8021Q_PCP0: W3, sparx5: is0 + * First PCP in multiple vlan tags (outer tag or default port tag) + * VCAP_KF_8021Q_PCP1: W3, sparx5: is0 + * Second PCP in multiple vlan tags (inner tag) + * VCAP_KF_8021Q_PCP2: W3, sparx5: is0 + * Third PCP in multiple vlan tags (not always available) + * VCAP_KF_8021Q_PCP_CLS: W3, sparx5: is2/es2 * Classified PCP - * VCAP_KF_8021Q_VID_CLS: W13, sparx5: is2 + * VCAP_KF_8021Q_TPID0: W3, sparx5: is0 + * First TPIC in multiple vlan tags (outer tag or default port tag) + * VCAP_KF_8021Q_TPID1: W3, sparx5: is0 + * Second TPID in multiple vlan tags (inner tag) + * VCAP_KF_8021Q_TPID2: W3, sparx5: is0 + * Third TPID in multiple vlan tags (not always available) + * VCAP_KF_8021Q_VID0: W12, sparx5: is0 + * First VID in multiple vlan tags (outer tag or default port tag) + * VCAP_KF_8021Q_VID1: W12, sparx5: is0 + * Second VID in multiple vlan tags (inner tag) + * VCAP_KF_8021Q_VID2: W12, sparx5: is0 + * Third VID in multiple vlan tags (not always available) + * VCAP_KF_8021Q_VID_CLS: W13, sparx5: is2/es2 * Classified VID - * VCAP_KF_8021Q_VLAN_TAGGED_IS: W1, sparx5: is2 + * VCAP_KF_8021Q_VLAN_TAGGED_IS: W1, sparx5: is2/es2 * Sparx5: Set if frame was received with a VLAN tag, LAN966x: Set if frame has * one or more Q-tags. Independent of port VLAN awareness - * VCAP_KF_ARP_ADDR_SPACE_OK_IS: W1, sparx5: is2 + * VCAP_KF_8021Q_VLAN_TAGS: W3, sparx5: is0 + * Number of VLAN tags in frame: 0: Untagged, 1: Single tagged, 3: Double + * tagged, 7: Triple tagged + * VCAP_KF_ACL_GRP_ID: W8, sparx5: es2 + * Used in interface map table + * VCAP_KF_ARP_ADDR_SPACE_OK_IS: W1, sparx5: is2/es2 * Set if hardware address is Ethernet - * VCAP_KF_ARP_LEN_OK_IS: W1, sparx5: is2 + * VCAP_KF_ARP_LEN_OK_IS: W1, sparx5: is2/es2 * Set if hardware address length = 6 (Ethernet) and IP address length = 4 (IP). - * VCAP_KF_ARP_OPCODE: W2, sparx5: is2 + * VCAP_KF_ARP_OPCODE: W2, sparx5: is2/es2 * ARP opcode - * VCAP_KF_ARP_OPCODE_UNKNOWN_IS: W1, sparx5: is2 + * VCAP_KF_ARP_OPCODE_UNKNOWN_IS: W1, sparx5: is2/es2 * Set if not one of the codes defined in VCAP_KF_ARP_OPCODE - * VCAP_KF_ARP_PROTO_SPACE_OK_IS: W1, sparx5: is2 + * VCAP_KF_ARP_PROTO_SPACE_OK_IS: W1, sparx5: is2/es2 * Set if protocol address space is 0x0800 - * VCAP_KF_ARP_SENDER_MATCH_IS: W1, sparx5: is2 + * VCAP_KF_ARP_SENDER_MATCH_IS: W1, sparx5: is2/es2 * Sender Hardware Address = SMAC (ARP) - * VCAP_KF_ARP_TGT_MATCH_IS: W1, sparx5: is2 + * VCAP_KF_ARP_TGT_MATCH_IS: W1, sparx5: is2/es2 * Target Hardware Address = SMAC (RARP) - * VCAP_KF_ETYPE: W16, sparx5: is2 + * VCAP_KF_COSID_CLS: W3, sparx5: es2 + * Class of service + * VCAP_KF_DST_ENTRY: W1, sparx5: is0 + * Selects whether the frame’s destination or source information is used for + * fields L2_SMAC and L3_IP4_SIP + * VCAP_KF_ES0_ISDX_KEY_ENA: W1, sparx5: es2 + * The value taken from the IFH .FWD.ES0_ISDX_KEY_ENA + * VCAP_KF_ETYPE: W16, sparx5: is0/is2/es2 * Ethernet type - * VCAP_KF_ETYPE_LEN_IS: W1, sparx5: is2 + * VCAP_KF_ETYPE_LEN_IS: W1, sparx5: is0/is2/es2 * Set if frame has EtherType >= 0x600 - * VCAP_KF_IF_IGR_PORT_MASK: sparx5 is2 W32, sparx5 is2 W65 + * VCAP_KF_ETYPE_MPLS: W2, sparx5: is0 + * Type of MPLS Ethertype (or not) + * VCAP_KF_IF_EGR_PORT_MASK: W32, sparx5: es2 + * Egress port mask, one bit per port + * VCAP_KF_IF_EGR_PORT_MASK_RNG: W3, sparx5: es2 + * Select which 32 port group is available in IF_EGR_PORT (or virtual ports or + * CPU queue) + * VCAP_KF_IF_IGR_PORT: sparx5 is0 W7, sparx5 es2 W9 + * Sparx5: Logical ingress port number retrieved from + * ANA_CL::PORT_ID_CFG.LPORT_NUM or ERLEG, LAN966x: ingress port nunmber + * VCAP_KF_IF_IGR_PORT_MASK: sparx5 is0 W65, sparx5 is2 W32, sparx5 is2 W65 * Ingress port mask, one bit per port/erleg * VCAP_KF_IF_IGR_PORT_MASK_L3: W1, sparx5: is2 * If set, IF_IGR_PORT_MASK, IF_IGR_PORT_MASK_RNG, and IF_IGR_PORT_MASK_SEL are @@ -66,123 +136,170 @@ enum vcap_keyfield_set { * VCAP_KF_IF_IGR_PORT_MASK_RNG: W4, sparx5: is2 * Range selector for IF_IGR_PORT_MASK. Specifies which group of 32 ports are * available in IF_IGR_PORT_MASK - * VCAP_KF_IF_IGR_PORT_MASK_SEL: W2, sparx5: is2 + * VCAP_KF_IF_IGR_PORT_MASK_SEL: W2, sparx5: is0/is2 * Mode selector for IF_IGR_PORT_MASK, applicable when IF_IGR_PORT_MASK_L3 == 0. * Mapping: 0: DEFAULT 1: LOOPBACK 2: MASQUERADE 3: CPU_VD - * VCAP_KF_IP4_IS: W1, sparx5: is2 + * VCAP_KF_IF_IGR_PORT_SEL: W1, sparx5: es2 + * Selector for IF_IGR_PORT: physical port number or ERLEG + * VCAP_KF_IP4_IS: W1, sparx5: is0/is2/es2 * Set if frame has EtherType = 0x800 and IP version = 4 - * VCAP_KF_ISDX_CLS: W12, sparx5: is2 + * VCAP_KF_IP_MC_IS: W1, sparx5: is0 + * Set if frame is IPv4 frame and frame’s destination MAC address is an IPv4 + * multicast address (0x01005E0 /25). Set if frame is IPv6 frame and frame’s + * destination MAC address is an IPv6 multicast address (0x3333/16). + * VCAP_KF_IP_PAYLOAD_5TUPLE: W32, sparx5: is0 + * Payload bytes after IP header + * VCAP_KF_IP_SNAP_IS: W1, sparx5: is0 + * Set if frame is IPv4, IPv6, or SNAP frame + * VCAP_KF_ISDX_CLS: W12, sparx5: is2/es2 * Classified ISDX - * VCAP_KF_ISDX_GT0_IS: W1, sparx5: is2 + * VCAP_KF_ISDX_GT0_IS: W1, sparx5: is2/es2 * Set if classified ISDX > 0 - * VCAP_KF_L2_BC_IS: W1, sparx5: is2 + * VCAP_KF_L2_BC_IS: W1, sparx5: is0/is2/es2 * Set if frame’s destination MAC address is the broadcast address * (FF-FF-FF-FF-FF-FF). - * VCAP_KF_L2_DMAC: W48, sparx5: is2 + * VCAP_KF_L2_DMAC: W48, sparx5: is0/is2/es2 * Destination MAC address * VCAP_KF_L2_FWD_IS: W1, sparx5: is2 * Set if the frame is allowed to be forwarded to front ports - * VCAP_KF_L2_MC_IS: W1, sparx5: is2 + * VCAP_KF_L2_MC_IS: W1, sparx5: is0/is2/es2 * Set if frame’s destination MAC address is a multicast address (bit 40 = 1). - * VCAP_KF_L2_PAYLOAD_ETYPE: W64, sparx5: is2 + * VCAP_KF_L2_PAYLOAD_ETYPE: W64, sparx5: is2/es2 * Byte 0-7 of L2 payload after Type/Len field and overloading for OAM - * VCAP_KF_L2_SMAC: W48, sparx5: is2 + * VCAP_KF_L2_SMAC: W48, sparx5: is0/is2/es2 * Source MAC address - * VCAP_KF_L3_DIP_EQ_SIP_IS: W1, sparx5: is2 + * VCAP_KF_L3_DIP_EQ_SIP_IS: W1, sparx5: is2/es2 * Set if Src IP matches Dst IP address + * VCAP_KF_L3_DMAC_DIP_MATCH: W1, sparx5: is2 + * Match found in DIP security lookup in ANA_L3 + * VCAP_KF_L3_DPL_CLS: W1, sparx5: es2 + * The frames drop precedence level + * VCAP_KF_L3_DSCP: W6, sparx5: is0 + * Frame’s DSCP value * VCAP_KF_L3_DST_IS: W1, sparx5: is2 * Set if lookup is done for egress router leg - * VCAP_KF_L3_FRAGMENT_TYPE: W2, sparx5: is2 + * VCAP_KF_L3_FRAGMENT_TYPE: W2, sparx5: is0/is2/es2 * L3 Fragmentation type (none, initial, suspicious, valid follow up) - * VCAP_KF_L3_FRAG_INVLD_L4_LEN: W1, sparx5: is2 + * VCAP_KF_L3_FRAG_INVLD_L4_LEN: W1, sparx5: is0/is2 * Set if frame's L4 length is less than ANA_CL:COMMON:CLM_FRAGMENT_CFG.L4_MIN_L * EN - * VCAP_KF_L3_IP4_DIP: W32, sparx5: is2 + * VCAP_KF_L3_IP4_DIP: W32, sparx5: is0/is2/es2 * Destination IPv4 Address - * VCAP_KF_L3_IP4_SIP: W32, sparx5: is2 + * VCAP_KF_L3_IP4_SIP: W32, sparx5: is0/is2/es2 * Source IPv4 Address - * VCAP_KF_L3_IP6_DIP: W128, sparx5: is2 + * VCAP_KF_L3_IP6_DIP: W128, sparx5: is0/is2/es2 * Sparx5: Full IPv6 DIP, LAN966x: Either Full IPv6 DIP or a subset depending on * frame type - * VCAP_KF_L3_IP6_SIP: W128, sparx5: is2 + * VCAP_KF_L3_IP6_SIP: W128, sparx5: is0/is2/es2 * Sparx5: Full IPv6 SIP, LAN966x: Either Full IPv6 SIP or a subset depending on * frame type - * VCAP_KF_L3_IP_PROTO: W8, sparx5: is2 + * VCAP_KF_L3_IP_PROTO: W8, sparx5: is0/is2/es2 * IPv4 frames: IP protocol. IPv6 frames: Next header, same as for IPV4 - * VCAP_KF_L3_OPTIONS_IS: W1, sparx5: is2 + * VCAP_KF_L3_OPTIONS_IS: W1, sparx5: is0/is2/es2 * Set if IPv4 frame contains options (IP len > 5) - * VCAP_KF_L3_PAYLOAD: sparx5 is2 W96, sparx5 is2 W40 + * VCAP_KF_L3_PAYLOAD: sparx5 is2 W96, sparx5 is2 W40, sparx5 es2 W96 * Sparx5: Payload bytes after IP header. IPv4: IPv4 options are not parsed so * payload is always taken 20 bytes after the start of the IPv4 header, LAN966x: * Bytes 0-6 after IP header - * VCAP_KF_L3_RT_IS: W1, sparx5: is2 + * VCAP_KF_L3_RT_IS: W1, sparx5: is2/es2 * Set if frame has hit a router leg - * VCAP_KF_L3_TOS: W8, sparx5: is2 + * VCAP_KF_L3_SMAC_SIP_MATCH: W1, sparx5: is2 + * Match found in SIP security lookup in ANA_L3 + * VCAP_KF_L3_TOS: W8, sparx5: is2/es2 * Sparx5: Frame's IPv4/IPv6 DSCP and ECN fields, LAN966x: IP TOS field - * VCAP_KF_L3_TTL_GT0: W1, sparx5: is2 + * VCAP_KF_L3_TTL_GT0: W1, sparx5: is2/es2 * Set if IPv4 TTL / IPv6 hop limit is greater than 0 - * VCAP_KF_L4_ACK: W1, sparx5: is2 + * VCAP_KF_L4_ACK: W1, sparx5: is2/es2 * Sparx5 and LAN966x: TCP flag ACK, LAN966x only: PTP over UDP: flagField bit 2 * (unicastFlag) - * VCAP_KF_L4_DPORT: W16, sparx5: is2 + * VCAP_KF_L4_DPORT: W16, sparx5: is2/es2 * Sparx5: TCP/UDP destination port. Overloading for IP_7TUPLE: Non-TCP/UDP IP * frames: L4_DPORT = L3_IP_PROTO, LAN966x: TCP/UDP destination port - * VCAP_KF_L4_FIN: W1, sparx5: is2 + * VCAP_KF_L4_FIN: W1, sparx5: is2/es2 * TCP flag FIN, LAN966x: TCP flag FIN, and for PTP over UDP: messageType bit 1 - * VCAP_KF_L4_PAYLOAD: W64, sparx5: is2 + * VCAP_KF_L4_PAYLOAD: W64, sparx5: is2/es2 * Payload bytes after TCP/UDP header Overloading for IP_7TUPLE: Non TCP/UDP * frames: Payload bytes 0–7 after IP header. IPv4 options are not parsed so * payload is always taken 20 bytes after the start of the IPv4 header for non * TCP/UDP IPv4 frames - * VCAP_KF_L4_PSH: W1, sparx5: is2 + * VCAP_KF_L4_PSH: W1, sparx5: is2/es2 * Sparx5: TCP flag PSH, LAN966x: TCP: TCP flag PSH. PTP over UDP: flagField bit * 1 (twoStepFlag) - * VCAP_KF_L4_RNG: W16, sparx5: is2 + * VCAP_KF_L4_RNG: sparx5 is0 W8, sparx5 is2 W16, sparx5 es2 W16 * Range checker bitmask (one for each range checker). Input into range checkers * is taken from classified results (VID, DSCP) and frame (SPORT, DPORT, ETYPE, * outer VID, inner VID) - * VCAP_KF_L4_RST: W1, sparx5: is2 + * VCAP_KF_L4_RST: W1, sparx5: is2/es2 * Sparx5: TCP flag RST , LAN966x: TCP: TCP flag RST. PTP over UDP: messageType * bit 3 - * VCAP_KF_L4_SEQUENCE_EQ0_IS: W1, sparx5: is2 + * VCAP_KF_L4_SEQUENCE_EQ0_IS: W1, sparx5: is2/es2 * Set if TCP sequence number is 0, LAN966x: Overlayed with PTP over UDP: * messageType bit 0 - * VCAP_KF_L4_SPORT: W16, sparx5: is2 + * VCAP_KF_L4_SPORT: W16, sparx5: is0/is2/es2 * TCP/UDP source port - * VCAP_KF_L4_SPORT_EQ_DPORT_IS: W1, sparx5: is2 + * VCAP_KF_L4_SPORT_EQ_DPORT_IS: W1, sparx5: is2/es2 * Set if UDP or TCP source port equals UDP or TCP destination port - * VCAP_KF_L4_SYN: W1, sparx5: is2 + * VCAP_KF_L4_SYN: W1, sparx5: is2/es2 * Sparx5: TCP flag SYN, LAN966x: TCP: TCP flag SYN. PTP over UDP: messageType * bit 2 - * VCAP_KF_L4_URG: W1, sparx5: is2 + * VCAP_KF_L4_URG: W1, sparx5: is2/es2 * Sparx5: TCP flag URG, LAN966x: TCP: TCP flag URG. PTP over UDP: flagField bit * 7 (reserved) - * VCAP_KF_LOOKUP_FIRST_IS: W1, sparx5: is2 + * VCAP_KF_LOOKUP_FIRST_IS: W1, sparx5: is0/is2/es2 * Selects between entries relevant for first and second lookup. Set for first * lookup, cleared for second lookup. + * VCAP_KF_LOOKUP_GEN_IDX: W12, sparx5: is0 + * Generic index - for chaining CLM instances + * VCAP_KF_LOOKUP_GEN_IDX_SEL: W2, sparx5: is0 + * Select the mode of the Generic Index * VCAP_KF_LOOKUP_PAG: W8, sparx5: is2 * Classified Policy Association Group: chains rules from IS1/CLM to IS2 - * VCAP_KF_OAM_CCM_CNTS_EQ0: W1, sparx5: is2 + * VCAP_KF_OAM_CCM_CNTS_EQ0: W1, sparx5: is2/es2 * Dual-ended loss measurement counters in CCM frames are all zero - * VCAP_KF_OAM_Y1731_IS: W1, sparx5: is2 + * VCAP_KF_OAM_MEL_FLAGS: W7, sparx5: is0 + * Encoding of MD level/MEG level (MEL) + * VCAP_KF_OAM_Y1731_IS: W1, sparx5: is0/is2/es2 * Set if frame’s EtherType = 0x8902 - * VCAP_KF_TCP_IS: W1, sparx5: is2 + * VCAP_KF_PROT_ACTIVE: W1, sparx5: es2 + * Protection is active + * VCAP_KF_TCP_IS: W1, sparx5: is0/is2/es2 * Set if frame is IPv4 TCP frame (IP protocol = 6) or IPv6 TCP frames (Next * header = 6) - * VCAP_KF_TCP_UDP_IS: W1, sparx5: is2 + * VCAP_KF_TCP_UDP_IS: W1, sparx5: is0/is2/es2 * Set if frame is IPv4/IPv6 TCP or UDP frame (IP protocol/next header equals 6 * or 17) - * VCAP_KF_TYPE: sparx5 is2 W4, sparx5 is2 W2 + * VCAP_KF_TYPE: sparx5 is0 W2, sparx5 is0 W1, sparx5 is2 W4, sparx5 is2 W2, + * sparx5 es2 W3 * Keyset type id - set by the API */ /* Keyfield names */ enum vcap_key_field { VCAP_KF_NO_VALUE, /* initial value */ + VCAP_KF_8021BR_ECID_BASE, + VCAP_KF_8021BR_ECID_EXT, + VCAP_KF_8021BR_E_TAGGED, + VCAP_KF_8021BR_GRP, + VCAP_KF_8021BR_IGR_ECID_BASE, + VCAP_KF_8021BR_IGR_ECID_EXT, + VCAP_KF_8021Q_DEI0, + VCAP_KF_8021Q_DEI1, + VCAP_KF_8021Q_DEI2, VCAP_KF_8021Q_DEI_CLS, + VCAP_KF_8021Q_PCP0, + VCAP_KF_8021Q_PCP1, + VCAP_KF_8021Q_PCP2, VCAP_KF_8021Q_PCP_CLS, + VCAP_KF_8021Q_TPID0, + VCAP_KF_8021Q_TPID1, + VCAP_KF_8021Q_TPID2, + VCAP_KF_8021Q_VID0, + VCAP_KF_8021Q_VID1, + VCAP_KF_8021Q_VID2, VCAP_KF_8021Q_VID_CLS, VCAP_KF_8021Q_VLAN_TAGGED_IS, + VCAP_KF_8021Q_VLAN_TAGS, + VCAP_KF_ACL_GRP_ID, VCAP_KF_ARP_ADDR_SPACE_OK_IS, VCAP_KF_ARP_LEN_OK_IS, VCAP_KF_ARP_OPCODE, @@ -190,13 +307,24 @@ enum vcap_key_field { VCAP_KF_ARP_PROTO_SPACE_OK_IS, VCAP_KF_ARP_SENDER_MATCH_IS, VCAP_KF_ARP_TGT_MATCH_IS, + VCAP_KF_COSID_CLS, + VCAP_KF_DST_ENTRY, + VCAP_KF_ES0_ISDX_KEY_ENA, VCAP_KF_ETYPE, VCAP_KF_ETYPE_LEN_IS, + VCAP_KF_ETYPE_MPLS, + VCAP_KF_IF_EGR_PORT_MASK, + VCAP_KF_IF_EGR_PORT_MASK_RNG, + VCAP_KF_IF_IGR_PORT, VCAP_KF_IF_IGR_PORT_MASK, VCAP_KF_IF_IGR_PORT_MASK_L3, VCAP_KF_IF_IGR_PORT_MASK_RNG, VCAP_KF_IF_IGR_PORT_MASK_SEL, + VCAP_KF_IF_IGR_PORT_SEL, VCAP_KF_IP4_IS, + VCAP_KF_IP_MC_IS, + VCAP_KF_IP_PAYLOAD_5TUPLE, + VCAP_KF_IP_SNAP_IS, VCAP_KF_ISDX_CLS, VCAP_KF_ISDX_GT0_IS, VCAP_KF_L2_BC_IS, @@ -206,6 +334,9 @@ enum vcap_key_field { VCAP_KF_L2_PAYLOAD_ETYPE, VCAP_KF_L2_SMAC, VCAP_KF_L3_DIP_EQ_SIP_IS, + VCAP_KF_L3_DMAC_DIP_MATCH, + VCAP_KF_L3_DPL_CLS, + VCAP_KF_L3_DSCP, VCAP_KF_L3_DST_IS, VCAP_KF_L3_FRAGMENT_TYPE, VCAP_KF_L3_FRAG_INVLD_L4_LEN, @@ -217,6 +348,7 @@ enum vcap_key_field { VCAP_KF_L3_OPTIONS_IS, VCAP_KF_L3_PAYLOAD, VCAP_KF_L3_RT_IS, + VCAP_KF_L3_SMAC_SIP_MATCH, VCAP_KF_L3_TOS, VCAP_KF_L3_TTL_GT0, VCAP_KF_L4_ACK, @@ -232,9 +364,14 @@ enum vcap_key_field { VCAP_KF_L4_SYN, VCAP_KF_L4_URG, VCAP_KF_LOOKUP_FIRST_IS, + VCAP_KF_LOOKUP_GEN_IDX, + VCAP_KF_LOOKUP_GEN_IDX_SEL, VCAP_KF_LOOKUP_PAG, + VCAP_KF_MIRROR_ENA, VCAP_KF_OAM_CCM_CNTS_EQ0, + VCAP_KF_OAM_MEL_FLAGS, VCAP_KF_OAM_Y1731_IS, + VCAP_KF_PROT_ACTIVE, VCAP_KF_TCP_IS, VCAP_KF_TCP_UDP_IS, VCAP_KF_TYPE, @@ -243,84 +380,266 @@ enum vcap_key_field { /* Actionset names with origin information */ enum vcap_actionfield_set { VCAP_AFS_NO_VALUE, /* initial value */ - VCAP_AFS_BASE_TYPE, /* sparx5 is2 X3 */ + VCAP_AFS_BASE_TYPE, /* sparx5 is2 X3, sparx5 es2 X3 */ + VCAP_AFS_CLASSIFICATION, /* sparx5 is0 X2 */ + VCAP_AFS_CLASS_REDUCED, /* sparx5 is0 X1 */ + VCAP_AFS_FULL, /* sparx5 is0 X3 */ + VCAP_AFS_MLBS, /* sparx5 is0 X2 */ + VCAP_AFS_MLBS_REDUCED, /* sparx5 is0 X1 */ }; /* List of actionfields with description * - * VCAP_AF_CNT_ID: W12, sparx5: is2 + * VCAP_AF_CLS_VID_SEL: W3, sparx5: is0 + * Controls the classified VID: 0: VID_NONE: No action. 1: VID_ADD: New VID = + * old VID + VID_VAL. 2: VID_REPLACE: New VID = VID_VAL. 3: VID_FIRST_TAG: New + * VID = VID from frame's first tag (outer tag) if available, otherwise VID_VAL. + * 4: VID_SECOND_TAG: New VID = VID from frame's second tag (middle tag) if + * available, otherwise VID_VAL. 5: VID_THIRD_TAG: New VID = VID from frame's + * third tag (inner tag) if available, otherwise VID_VAL. + * VCAP_AF_CNT_ID: sparx5 is2 W12, sparx5 es2 W11 * Counter ID, used per lookup to index the 4K frame counters (ANA_ACL:CNT_TBL). * Multiple VCAP IS2 entries can use the same counter. - * VCAP_AF_CPU_COPY_ENA: W1, sparx5: is2 + * VCAP_AF_COPY_PORT_NUM: W7, sparx5: es2 + * QSYS port number when FWD_MODE is redirect or copy + * VCAP_AF_COPY_QUEUE_NUM: W16, sparx5: es2 + * QSYS queue number when FWD_MODE is redirect or copy + * VCAP_AF_CPU_COPY_ENA: W1, sparx5: is2/es2 * Setting this bit to 1 causes all frames that hit this action to be copied to * the CPU extraction queue specified in CPU_QUEUE_NUM. - * VCAP_AF_CPU_QUEUE_NUM: W3, sparx5: is2 + * VCAP_AF_CPU_QUEUE_NUM: W3, sparx5: is2/es2 * CPU queue number. Used when CPU_COPY_ENA is set. - * VCAP_AF_HIT_ME_ONCE: W1, sparx5: is2 + * VCAP_AF_DEI_ENA: W1, sparx5: is0 + * If set, use DEI_VAL as classified DEI value. Otherwise, DEI from basic + * classification is used + * VCAP_AF_DEI_VAL: W1, sparx5: is0 + * See DEI_ENA + * VCAP_AF_DP_ENA: W1, sparx5: is0 + * If set, use DP_VAL as classified drop precedence level. Otherwise, drop + * precedence level from basic classification is used. + * VCAP_AF_DP_VAL: W2, sparx5: is0 + * See DP_ENA. + * VCAP_AF_DSCP_ENA: W1, sparx5: is0 + * If set, use DSCP_VAL as classified DSCP value. Otherwise, DSCP value from + * basic classification is used. + * VCAP_AF_DSCP_VAL: W6, sparx5: is0 + * See DSCP_ENA. + * VCAP_AF_ES2_REW_CMD: W3, sparx5: es2 + * Command forwarded to REW: 0: No action. 1: SWAP MAC addresses. 2: Do L2CP + * DMAC translation when entering or leaving a tunnel. + * VCAP_AF_FWD_MODE: W2, sparx5: es2 + * Forward selector: 0: Forward. 1: Discard. 2: Redirect. 3: Copy. + * VCAP_AF_HIT_ME_ONCE: W1, sparx5: is2/es2 * Setting this bit to 1 causes the first frame that hits this action where the * HIT_CNT counter is zero to be copied to the CPU extraction queue specified in * CPU_QUEUE_NUM. The HIT_CNT counter is then incremented and any frames that * hit this action later are not copied to the CPU. To re-enable the HIT_ME_ONCE * functionality, the HIT_CNT counter must be cleared. - * VCAP_AF_IGNORE_PIPELINE_CTRL: W1, sparx5: is2 + * VCAP_AF_IGNORE_PIPELINE_CTRL: W1, sparx5: is2/es2 * Ignore ingress pipeline control. This enforces the use of the VCAP IS2 action * even when the pipeline control has terminated the frame before VCAP IS2. - * VCAP_AF_INTR_ENA: W1, sparx5: is2 + * VCAP_AF_INTR_ENA: W1, sparx5: is2/es2 * If set, an interrupt is triggered when this rule is hit + * VCAP_AF_ISDX_ADD_REPLACE_SEL: W1, sparx5: is0 + * Controls the classified ISDX. 0: New ISDX = old ISDX + ISDX_VAL. 1: New ISDX + * = ISDX_VAL. + * VCAP_AF_ISDX_VAL: W12, sparx5: is0 + * See isdx_add_replace_sel * VCAP_AF_LRN_DIS: W1, sparx5: is2 * Setting this bit to 1 disables learning of frames hitting this action. - * VCAP_AF_MASK_MODE: W3, sparx5: is2 + * VCAP_AF_MAP_IDX: W9, sparx5: is0 + * Index for QoS mapping table lookup + * VCAP_AF_MAP_KEY: W3, sparx5: is0 + * Key type for QoS mapping table lookup. 0: DEI0, PCP0 (outer tag). 1: DEI1, + * PCP1 (middle tag). 2: DEI2, PCP2 (inner tag). 3: MPLS TC. 4: PCP0 (outer + * tag). 5: E-DEI, E-PCP (E-TAG). 6: DSCP if available, otherwise none. 7: DSCP + * if available, otherwise DEI0, PCP0 (outer tag) if available using MAP_IDX+8, + * otherwise none + * VCAP_AF_MAP_LOOKUP_SEL: W2, sparx5: is0 + * Selects which of the two QoS Mapping Table lookups that MAP_KEY and MAP_IDX + * are applied to. 0: No changes to the QoS Mapping Table lookup. 1: Update key + * type and index for QoS Mapping Table lookup #0. 2: Update key type and index + * for QoS Mapping Table lookup #1. 3: Reserved. + * VCAP_AF_MASK_MODE: W3, sparx5: is0/is2 * Controls the PORT_MASK use. Sparx5: 0: OR_DSTMASK, 1: AND_VLANMASK, 2: * REPLACE_PGID, 3: REPLACE_ALL, 4: REDIR_PGID, 5: OR_PGID_MASK, 6: VSTAX, 7: * Not applicable. LAN966X: 0: No action, 1: Permit/deny (AND), 2: Policy * forwarding (DMAC lookup), 3: Redirect. The CPU port is untouched by * MASK_MODE. - * VCAP_AF_MATCH_ID: W16, sparx5: is2 + * VCAP_AF_MATCH_ID: W16, sparx5: is0/is2 * Logical ID for the entry. The MATCH_ID is extracted together with the frame * if the frame is forwarded to the CPU (CPU_COPY_ENA). The result is placed in * IFH.CL_RSLT. - * VCAP_AF_MATCH_ID_MASK: W16, sparx5: is2 + * VCAP_AF_MATCH_ID_MASK: W16, sparx5: is0/is2 * Mask used by MATCH_ID. * VCAP_AF_MIRROR_PROBE: W2, sparx5: is2 * Mirroring performed according to configuration of a mirror probe. 0: No * mirroring. 1: Mirror probe 0. 2: Mirror probe 1. 3: Mirror probe 2 - * VCAP_AF_PIPELINE_FORCE_ENA: W1, sparx5: is2 + * VCAP_AF_MIRROR_PROBE_ID: W2, sparx5: es2 + * Signals a mirror probe to be placed in the IFH. Only possible when FWD_MODE + * is copy. 0: No mirroring. 1–3: Use mirror probe 0-2. + * VCAP_AF_NXT_IDX: W12, sparx5: is0 + * Index used as part of key (field G_IDX) in the next lookup. + * VCAP_AF_NXT_IDX_CTRL: W3, sparx5: is0 + * Controls the generation of the G_IDX used in the VCAP CLM next lookup + * VCAP_AF_PAG_OVERRIDE_MASK: W8, sparx5: is0 + * Bits set in this mask will override PAG_VAL from port profile.  New PAG = + * (PAG (input) AND ~PAG_OVERRIDE_MASK) OR (PAG_VAL AND PAG_OVERRIDE_MASK) + * VCAP_AF_PAG_VAL: W8, sparx5: is0 + * See PAG_OVERRIDE_MASK. + * VCAP_AF_PCP_ENA: W1, sparx5: is0 + * If set, use PCP_VAL as classified PCP value. Otherwise, PCP from basic + * classification is used. + * VCAP_AF_PCP_VAL: W3, sparx5: is0 + * See PCP_ENA. + * VCAP_AF_PIPELINE_FORCE_ENA: sparx5 is0 W2, sparx5 is2 W1 * If set, use PIPELINE_PT unconditionally and set PIPELINE_ACT = NONE if * PIPELINE_PT == NONE. Overrules previous settings of pipeline point. - * VCAP_AF_PIPELINE_PT: W5, sparx5: is2 + * VCAP_AF_PIPELINE_PT: W5, sparx5: is0/is2 * Pipeline point used if PIPELINE_FORCE_ENA is set - * VCAP_AF_POLICE_ENA: W1, sparx5: is2 + * VCAP_AF_POLICE_ENA: W1, sparx5: is2/es2 * Setting this bit to 1 causes frames that hit this action to be policed by the * ACL policer specified in POLICE_IDX. Only applies to the first lookup. - * VCAP_AF_POLICE_IDX: W6, sparx5: is2 + * VCAP_AF_POLICE_IDX: W6, sparx5: is2/es2 * Selects VCAP policer used when policing frames (POLICE_ENA) - * VCAP_AF_PORT_MASK: W68, sparx5: is2 + * VCAP_AF_POLICE_REMARK: W1, sparx5: es2 + * If set, frames exceeding policer rates are marked as yellow but not + * discarded. + * VCAP_AF_PORT_MASK: sparx5 is0 W65, sparx5 is2 W68 * Port mask applied to the forwarding decision based on MASK_MODE. + * VCAP_AF_QOS_ENA: W1, sparx5: is0 + * If set, use QOS_VAL as classified QoS class. Otherwise, QoS class from basic + * classification is used. + * VCAP_AF_QOS_VAL: W3, sparx5: is0 + * See QOS_ENA. * VCAP_AF_RT_DIS: W1, sparx5: is2 * If set, routing is disallowed. Only applies when IS_INNER_ACL is 0. See also * IGR_ACL_ENA, EGR_ACL_ENA, and RLEG_STAT_IDX. + * VCAP_AF_TYPE: W1, sparx5: is0 + * Actionset type id - Set by the API + * VCAP_AF_VID_VAL: W13, sparx5: is0 + * New VID Value */ /* Actionfield names */ enum vcap_action_field { VCAP_AF_NO_VALUE, /* initial value */ + VCAP_AF_ACL_MAC, + VCAP_AF_ACL_RT_MODE, + VCAP_AF_CLS_VID_SEL, VCAP_AF_CNT_ID, + VCAP_AF_COPY_PORT_NUM, + VCAP_AF_COPY_QUEUE_NUM, + VCAP_AF_COSID_ENA, + VCAP_AF_COSID_VAL, VCAP_AF_CPU_COPY_ENA, + VCAP_AF_CPU_DIS, + VCAP_AF_CPU_ENA, + VCAP_AF_CPU_Q, VCAP_AF_CPU_QUEUE_NUM, + VCAP_AF_CUSTOM_ACE_ENA, + VCAP_AF_CUSTOM_ACE_OFFSET, + VCAP_AF_DEI_ENA, + VCAP_AF_DEI_VAL, + VCAP_AF_DLB_OFFSET, + VCAP_AF_DMAC_OFFSET_ENA, + VCAP_AF_DP_ENA, + VCAP_AF_DP_VAL, + VCAP_AF_DSCP_ENA, + VCAP_AF_DSCP_VAL, + VCAP_AF_EGR_ACL_ENA, + VCAP_AF_ES2_REW_CMD, + VCAP_AF_FWD_DIS, + VCAP_AF_FWD_MODE, + VCAP_AF_FWD_TYPE, + VCAP_AF_GVID_ADD_REPLACE_SEL, VCAP_AF_HIT_ME_ONCE, VCAP_AF_IGNORE_PIPELINE_CTRL, + VCAP_AF_IGR_ACL_ENA, + VCAP_AF_INJ_MASQ_ENA, + VCAP_AF_INJ_MASQ_LPORT, + VCAP_AF_INJ_MASQ_PORT, VCAP_AF_INTR_ENA, + VCAP_AF_ISDX_ADD_REPLACE_SEL, + VCAP_AF_ISDX_VAL, + VCAP_AF_IS_INNER_ACL, + VCAP_AF_L3_MAC_UPDATE_DIS, + VCAP_AF_LOG_MSG_INTERVAL, + VCAP_AF_LPM_AFFIX_ENA, + VCAP_AF_LPM_AFFIX_VAL, + VCAP_AF_LPORT_ENA, VCAP_AF_LRN_DIS, + VCAP_AF_MAP_IDX, + VCAP_AF_MAP_KEY, + VCAP_AF_MAP_LOOKUP_SEL, VCAP_AF_MASK_MODE, VCAP_AF_MATCH_ID, VCAP_AF_MATCH_ID_MASK, + VCAP_AF_MIP_SEL, VCAP_AF_MIRROR_PROBE, + VCAP_AF_MIRROR_PROBE_ID, + VCAP_AF_MPLS_IP_CTRL_ENA, + VCAP_AF_MPLS_MEP_ENA, + VCAP_AF_MPLS_MIP_ENA, + VCAP_AF_MPLS_OAM_FLAVOR, + VCAP_AF_MPLS_OAM_TYPE, + VCAP_AF_NUM_VLD_LABELS, + VCAP_AF_NXT_IDX, + VCAP_AF_NXT_IDX_CTRL, + VCAP_AF_NXT_KEY_TYPE, + VCAP_AF_NXT_NORMALIZE, + VCAP_AF_NXT_NORM_W16_OFFSET, + VCAP_AF_NXT_NORM_W32_OFFSET, + VCAP_AF_NXT_OFFSET_FROM_TYPE, + VCAP_AF_NXT_TYPE_AFTER_OFFSET, + VCAP_AF_OAM_IP_BFD_ENA, + VCAP_AF_OAM_TWAMP_ENA, + VCAP_AF_OAM_Y1731_SEL, + VCAP_AF_PAG_OVERRIDE_MASK, + VCAP_AF_PAG_VAL, + VCAP_AF_PCP_ENA, + VCAP_AF_PCP_VAL, + VCAP_AF_PIPELINE_ACT_SEL, VCAP_AF_PIPELINE_FORCE_ENA, VCAP_AF_PIPELINE_PT, + VCAP_AF_PIPELINE_PT_REDUCED, VCAP_AF_POLICE_ENA, VCAP_AF_POLICE_IDX, + VCAP_AF_POLICE_REMARK, VCAP_AF_PORT_MASK, + VCAP_AF_PTP_MASTER_SEL, + VCAP_AF_QOS_ENA, + VCAP_AF_QOS_VAL, + VCAP_AF_REW_CMD, + VCAP_AF_RLEG_DMAC_CHK_DIS, + VCAP_AF_RLEG_STAT_IDX, + VCAP_AF_RSDX_ENA, + VCAP_AF_RSDX_VAL, + VCAP_AF_RSVD_LBL_VAL, VCAP_AF_RT_DIS, + VCAP_AF_RT_SEL, + VCAP_AF_S2_KEY_SEL_ENA, + VCAP_AF_S2_KEY_SEL_IDX, + VCAP_AF_SAM_SEQ_ENA, + VCAP_AF_SIP_IDX, + VCAP_AF_SWAP_MAC_ENA, + VCAP_AF_TCP_UDP_DPORT, + VCAP_AF_TCP_UDP_ENA, + VCAP_AF_TCP_UDP_SPORT, + VCAP_AF_TC_ENA, + VCAP_AF_TC_LABEL, + VCAP_AF_TPID_SEL, + VCAP_AF_TTL_DECR_DIS, + VCAP_AF_TTL_ENA, + VCAP_AF_TTL_LABEL, + VCAP_AF_TTL_UPDATE_ENA, + VCAP_AF_TYPE, + VCAP_AF_VID_VAL, + VCAP_AF_VLAN_POP_CNT, + VCAP_AF_VLAN_POP_CNT_ENA, + VCAP_AF_VLAN_PUSH_CNT, + VCAP_AF_VLAN_PUSH_CNT_ENA, + VCAP_AF_VLAN_WAS_TAGGED, }; #endif /* __VCAP_AG_API__ */ diff --git a/drivers/net/ethernet/microchip/vcap/vcap_ag_api_kunit.h b/drivers/net/ethernet/microchip/vcap/vcap_ag_api_kunit.h deleted file mode 100644 index e538ca725687..000000000000 --- a/drivers/net/ethernet/microchip/vcap/vcap_ag_api_kunit.h +++ /dev/null @@ -1,643 +0,0 @@ -/* SPDX-License-Identifier: BSD-3-Clause */ -/* Copyright (C) 2022 Microchip Technology Inc. and its subsidiaries. - * Microchip VCAP API interface for kunit testing - * This is a different interface, to be able to include different VCAPs - */ - -/* Use same include guard as the official API to be able to override it */ -#ifndef __VCAP_AG_API__ -#define __VCAP_AG_API__ - -enum vcap_type { - VCAP_TYPE_ES2, - VCAP_TYPE_IS0, - VCAP_TYPE_IS2, - VCAP_TYPE_MAX -}; - -/* Keyfieldset names with origin information */ -enum vcap_keyfield_set { - VCAP_KFS_NO_VALUE, /* initial value */ - VCAP_KFS_ARP, /* sparx5 is2 X6, sparx5 es2 X6 */ - VCAP_KFS_ETAG, /* sparx5 is0 X2 */ - VCAP_KFS_IP4_OTHER, /* sparx5 is2 X6, sparx5 es2 X6 */ - VCAP_KFS_IP4_TCP_UDP, /* sparx5 is2 X6, sparx5 es2 X6 */ - VCAP_KFS_IP4_VID, /* sparx5 es2 X3 */ - VCAP_KFS_IP6_STD, /* sparx5 is2 X6 */ - VCAP_KFS_IP6_VID, /* sparx5 is2 X6, sparx5 es2 X6 */ - VCAP_KFS_IP_7TUPLE, /* sparx5 is2 X12, sparx5 es2 X12 */ - VCAP_KFS_LL_FULL, /* sparx5 is0 X6 */ - VCAP_KFS_MAC_ETYPE, /* sparx5 is2 X6, sparx5 es2 X6 */ - VCAP_KFS_MLL, /* sparx5 is0 X3 */ - VCAP_KFS_NORMAL, /* sparx5 is0 X6 */ - VCAP_KFS_NORMAL_5TUPLE_IP4, /* sparx5 is0 X6 */ - VCAP_KFS_NORMAL_7TUPLE, /* sparx5 is0 X12 */ - VCAP_KFS_PURE_5TUPLE_IP4, /* sparx5 is0 X3 */ - VCAP_KFS_TRI_VID, /* sparx5 is0 X2 */ -}; - -/* List of keyfields with description - * - * Keys ending in _IS are booleans derived from frame data - * Keys ending in _CLS are classified frame data - * - * VCAP_KF_8021BR_ECID_BASE: W12, sparx5: is0 - * Used by 802.1BR Bridge Port Extension in an E-Tag - * VCAP_KF_8021BR_ECID_EXT: W8, sparx5: is0 - * Used by 802.1BR Bridge Port Extension in an E-Tag - * VCAP_KF_8021BR_E_TAGGED: W1, sparx5: is0 - * Set for frames containing an E-TAG (802.1BR Ethertype 893f) - * VCAP_KF_8021BR_GRP: W2, sparx5: is0 - * E-Tag group bits in 802.1BR Bridge Port Extension - * VCAP_KF_8021BR_IGR_ECID_BASE: W12, sparx5: is0 - * Used by 802.1BR Bridge Port Extension in an E-Tag - * VCAP_KF_8021BR_IGR_ECID_EXT: W8, sparx5: is0 - * Used by 802.1BR Bridge Port Extension in an E-Tag - * VCAP_KF_8021Q_DEI0: W1, sparx5: is0 - * First DEI in multiple vlan tags (outer tag or default port tag) - * VCAP_KF_8021Q_DEI1: W1, sparx5: is0 - * Second DEI in multiple vlan tags (inner tag) - * VCAP_KF_8021Q_DEI2: W1, sparx5: is0 - * Third DEI in multiple vlan tags (not always available) - * VCAP_KF_8021Q_DEI_CLS: W1, sparx5: is2/es2 - * Classified DEI - * VCAP_KF_8021Q_PCP0: W3, sparx5: is0 - * First PCP in multiple vlan tags (outer tag or default port tag) - * VCAP_KF_8021Q_PCP1: W3, sparx5: is0 - * Second PCP in multiple vlan tags (inner tag) - * VCAP_KF_8021Q_PCP2: W3, sparx5: is0 - * Third PCP in multiple vlan tags (not always available) - * VCAP_KF_8021Q_PCP_CLS: W3, sparx5: is2/es2 - * Classified PCP - * VCAP_KF_8021Q_TPID0: W3, sparx5: is0 - * First TPIC in multiple vlan tags (outer tag or default port tag) - * VCAP_KF_8021Q_TPID1: W3, sparx5: is0 - * Second TPID in multiple vlan tags (inner tag) - * VCAP_KF_8021Q_TPID2: W3, sparx5: is0 - * Third TPID in multiple vlan tags (not always available) - * VCAP_KF_8021Q_VID0: W12, sparx5: is0 - * First VID in multiple vlan tags (outer tag or default port tag) - * VCAP_KF_8021Q_VID1: W12, sparx5: is0 - * Second VID in multiple vlan tags (inner tag) - * VCAP_KF_8021Q_VID2: W12, sparx5: is0 - * Third VID in multiple vlan tags (not always available) - * VCAP_KF_8021Q_VID_CLS: W13, sparx5: is2/es2 - * Classified VID - * VCAP_KF_8021Q_VLAN_TAGGED_IS: W1, sparx5: is2/es2 - * Sparx5: Set if frame was received with a VLAN tag, LAN966x: Set if frame has - * one or more Q-tags. Independent of port VLAN awareness - * VCAP_KF_8021Q_VLAN_TAGS: W3, sparx5: is0 - * Number of VLAN tags in frame: 0: Untagged, 1: Single tagged, 3: Double - * tagged, 7: Triple tagged - * VCAP_KF_ACL_GRP_ID: W8, sparx5: es2 - * Used in interface map table - * VCAP_KF_ARP_ADDR_SPACE_OK_IS: W1, sparx5: is2/es2 - * Set if hardware address is Ethernet - * VCAP_KF_ARP_LEN_OK_IS: W1, sparx5: is2/es2 - * Set if hardware address length = 6 (Ethernet) and IP address length = 4 (IP). - * VCAP_KF_ARP_OPCODE: W2, sparx5: is2/es2 - * ARP opcode - * VCAP_KF_ARP_OPCODE_UNKNOWN_IS: W1, sparx5: is2/es2 - * Set if not one of the codes defined in VCAP_KF_ARP_OPCODE - * VCAP_KF_ARP_PROTO_SPACE_OK_IS: W1, sparx5: is2/es2 - * Set if protocol address space is 0x0800 - * VCAP_KF_ARP_SENDER_MATCH_IS: W1, sparx5: is2/es2 - * Sender Hardware Address = SMAC (ARP) - * VCAP_KF_ARP_TGT_MATCH_IS: W1, sparx5: is2/es2 - * Target Hardware Address = SMAC (RARP) - * VCAP_KF_COSID_CLS: W3, sparx5: es2 - * Class of service - * VCAP_KF_DST_ENTRY: W1, sparx5: is0 - * Selects whether the frame’s destination or source information is used for - * fields L2_SMAC and L3_IP4_SIP - * VCAP_KF_ES0_ISDX_KEY_ENA: W1, sparx5: es2 - * The value taken from the IFH .FWD.ES0_ISDX_KEY_ENA - * VCAP_KF_ETYPE: W16, sparx5: is0/is2/es2 - * Ethernet type - * VCAP_KF_ETYPE_LEN_IS: W1, sparx5: is0/is2/es2 - * Set if frame has EtherType >= 0x600 - * VCAP_KF_ETYPE_MPLS: W2, sparx5: is0 - * Type of MPLS Ethertype (or not) - * VCAP_KF_IF_EGR_PORT_MASK: W32, sparx5: es2 - * Egress port mask, one bit per port - * VCAP_KF_IF_EGR_PORT_MASK_RNG: W3, sparx5: es2 - * Select which 32 port group is available in IF_EGR_PORT (or virtual ports or - * CPU queue) - * VCAP_KF_IF_IGR_PORT: sparx5 is0 W7, sparx5 es2 W9 - * Sparx5: Logical ingress port number retrieved from - * ANA_CL::PORT_ID_CFG.LPORT_NUM or ERLEG, LAN966x: ingress port nunmber - * VCAP_KF_IF_IGR_PORT_MASK: sparx5 is0 W65, sparx5 is2 W32, sparx5 is2 W65 - * Ingress port mask, one bit per port/erleg - * VCAP_KF_IF_IGR_PORT_MASK_L3: W1, sparx5: is2 - * If set, IF_IGR_PORT_MASK, IF_IGR_PORT_MASK_RNG, and IF_IGR_PORT_MASK_SEL are - * used to specify L3 interfaces - * VCAP_KF_IF_IGR_PORT_MASK_RNG: W4, sparx5: is2 - * Range selector for IF_IGR_PORT_MASK. Specifies which group of 32 ports are - * available in IF_IGR_PORT_MASK - * VCAP_KF_IF_IGR_PORT_MASK_SEL: W2, sparx5: is0/is2 - * Mode selector for IF_IGR_PORT_MASK, applicable when IF_IGR_PORT_MASK_L3 == 0. - * Mapping: 0: DEFAULT 1: LOOPBACK 2: MASQUERADE 3: CPU_VD - * VCAP_KF_IF_IGR_PORT_SEL: W1, sparx5: es2 - * Selector for IF_IGR_PORT: physical port number or ERLEG - * VCAP_KF_IP4_IS: W1, sparx5: is0/is2/es2 - * Set if frame has EtherType = 0x800 and IP version = 4 - * VCAP_KF_IP_MC_IS: W1, sparx5: is0 - * Set if frame is IPv4 frame and frame’s destination MAC address is an IPv4 - * multicast address (0x01005E0 /25). Set if frame is IPv6 frame and frame’s - * destination MAC address is an IPv6 multicast address (0x3333/16). - * VCAP_KF_IP_PAYLOAD_5TUPLE: W32, sparx5: is0 - * Payload bytes after IP header - * VCAP_KF_IP_SNAP_IS: W1, sparx5: is0 - * Set if frame is IPv4, IPv6, or SNAP frame - * VCAP_KF_ISDX_CLS: W12, sparx5: is2/es2 - * Classified ISDX - * VCAP_KF_ISDX_GT0_IS: W1, sparx5: is2/es2 - * Set if classified ISDX > 0 - * VCAP_KF_L2_BC_IS: W1, sparx5: is0/is2/es2 - * Set if frame’s destination MAC address is the broadcast address - * (FF-FF-FF-FF-FF-FF). - * VCAP_KF_L2_DMAC: W48, sparx5: is0/is2/es2 - * Destination MAC address - * VCAP_KF_L2_FWD_IS: W1, sparx5: is2 - * Set if the frame is allowed to be forwarded to front ports - * VCAP_KF_L2_MC_IS: W1, sparx5: is0/is2/es2 - * Set if frame’s destination MAC address is a multicast address (bit 40 = 1). - * VCAP_KF_L2_PAYLOAD_ETYPE: W64, sparx5: is2/es2 - * Byte 0-7 of L2 payload after Type/Len field and overloading for OAM - * VCAP_KF_L2_SMAC: W48, sparx5: is0/is2/es2 - * Source MAC address - * VCAP_KF_L3_DIP_EQ_SIP_IS: W1, sparx5: is2/es2 - * Set if Src IP matches Dst IP address - * VCAP_KF_L3_DMAC_DIP_MATCH: W1, sparx5: is2 - * Match found in DIP security lookup in ANA_L3 - * VCAP_KF_L3_DPL_CLS: W1, sparx5: es2 - * The frames drop precedence level - * VCAP_KF_L3_DSCP: W6, sparx5: is0 - * Frame’s DSCP value - * VCAP_KF_L3_DST_IS: W1, sparx5: is2 - * Set if lookup is done for egress router leg - * VCAP_KF_L3_FRAGMENT_TYPE: W2, sparx5: is0/is2/es2 - * L3 Fragmentation type (none, initial, suspicious, valid follow up) - * VCAP_KF_L3_FRAG_INVLD_L4_LEN: W1, sparx5: is0/is2 - * Set if frame's L4 length is less than ANA_CL:COMMON:CLM_FRAGMENT_CFG.L4_MIN_L - * EN - * VCAP_KF_L3_IP4_DIP: W32, sparx5: is0/is2/es2 - * Destination IPv4 Address - * VCAP_KF_L3_IP4_SIP: W32, sparx5: is0/is2/es2 - * Source IPv4 Address - * VCAP_KF_L3_IP6_DIP: W128, sparx5: is0/is2/es2 - * Sparx5: Full IPv6 DIP, LAN966x: Either Full IPv6 DIP or a subset depending on - * frame type - * VCAP_KF_L3_IP6_SIP: W128, sparx5: is0/is2/es2 - * Sparx5: Full IPv6 SIP, LAN966x: Either Full IPv6 SIP or a subset depending on - * frame type - * VCAP_KF_L3_IP_PROTO: W8, sparx5: is0/is2/es2 - * IPv4 frames: IP protocol. IPv6 frames: Next header, same as for IPV4 - * VCAP_KF_L3_OPTIONS_IS: W1, sparx5: is0/is2/es2 - * Set if IPv4 frame contains options (IP len > 5) - * VCAP_KF_L3_PAYLOAD: sparx5 is2 W96, sparx5 is2 W40, sparx5 es2 W96 - * Sparx5: Payload bytes after IP header. IPv4: IPv4 options are not parsed so - * payload is always taken 20 bytes after the start of the IPv4 header, LAN966x: - * Bytes 0-6 after IP header - * VCAP_KF_L3_RT_IS: W1, sparx5: is2/es2 - * Set if frame has hit a router leg - * VCAP_KF_L3_SMAC_SIP_MATCH: W1, sparx5: is2 - * Match found in SIP security lookup in ANA_L3 - * VCAP_KF_L3_TOS: W8, sparx5: is2/es2 - * Sparx5: Frame's IPv4/IPv6 DSCP and ECN fields, LAN966x: IP TOS field - * VCAP_KF_L3_TTL_GT0: W1, sparx5: is2/es2 - * Set if IPv4 TTL / IPv6 hop limit is greater than 0 - * VCAP_KF_L4_ACK: W1, sparx5: is2/es2 - * Sparx5 and LAN966x: TCP flag ACK, LAN966x only: PTP over UDP: flagField bit 2 - * (unicastFlag) - * VCAP_KF_L4_DPORT: W16, sparx5: is2/es2 - * Sparx5: TCP/UDP destination port. Overloading for IP_7TUPLE: Non-TCP/UDP IP - * frames: L4_DPORT = L3_IP_PROTO, LAN966x: TCP/UDP destination port - * VCAP_KF_L4_FIN: W1, sparx5: is2/es2 - * TCP flag FIN, LAN966x: TCP flag FIN, and for PTP over UDP: messageType bit 1 - * VCAP_KF_L4_PAYLOAD: W64, sparx5: is2/es2 - * Payload bytes after TCP/UDP header Overloading for IP_7TUPLE: Non TCP/UDP - * frames: Payload bytes 0–7 after IP header. IPv4 options are not parsed so - * payload is always taken 20 bytes after the start of the IPv4 header for non - * TCP/UDP IPv4 frames - * VCAP_KF_L4_PSH: W1, sparx5: is2/es2 - * Sparx5: TCP flag PSH, LAN966x: TCP: TCP flag PSH. PTP over UDP: flagField bit - * 1 (twoStepFlag) - * VCAP_KF_L4_RNG: sparx5 is0 W8, sparx5 is2 W16, sparx5 es2 W16 - * Range checker bitmask (one for each range checker). Input into range checkers - * is taken from classified results (VID, DSCP) and frame (SPORT, DPORT, ETYPE, - * outer VID, inner VID) - * VCAP_KF_L4_RST: W1, sparx5: is2/es2 - * Sparx5: TCP flag RST , LAN966x: TCP: TCP flag RST. PTP over UDP: messageType - * bit 3 - * VCAP_KF_L4_SEQUENCE_EQ0_IS: W1, sparx5: is2/es2 - * Set if TCP sequence number is 0, LAN966x: Overlayed with PTP over UDP: - * messageType bit 0 - * VCAP_KF_L4_SPORT: W16, sparx5: is0/is2/es2 - * TCP/UDP source port - * VCAP_KF_L4_SPORT_EQ_DPORT_IS: W1, sparx5: is2/es2 - * Set if UDP or TCP source port equals UDP or TCP destination port - * VCAP_KF_L4_SYN: W1, sparx5: is2/es2 - * Sparx5: TCP flag SYN, LAN966x: TCP: TCP flag SYN. PTP over UDP: messageType - * bit 2 - * VCAP_KF_L4_URG: W1, sparx5: is2/es2 - * Sparx5: TCP flag URG, LAN966x: TCP: TCP flag URG. PTP over UDP: flagField bit - * 7 (reserved) - * VCAP_KF_LOOKUP_FIRST_IS: W1, sparx5: is0/is2/es2 - * Selects between entries relevant for first and second lookup. Set for first - * lookup, cleared for second lookup. - * VCAP_KF_LOOKUP_GEN_IDX: W12, sparx5: is0 - * Generic index - for chaining CLM instances - * VCAP_KF_LOOKUP_GEN_IDX_SEL: W2, sparx5: is0 - * Select the mode of the Generic Index - * VCAP_KF_LOOKUP_PAG: W8, sparx5: is2 - * Classified Policy Association Group: chains rules from IS1/CLM to IS2 - * VCAP_KF_OAM_CCM_CNTS_EQ0: W1, sparx5: is2/es2 - * Dual-ended loss measurement counters in CCM frames are all zero - * VCAP_KF_OAM_MEL_FLAGS: W7, sparx5: is0 - * Encoding of MD level/MEG level (MEL) - * VCAP_KF_OAM_Y1731_IS: W1, sparx5: is0/is2/es2 - * Set if frame’s EtherType = 0x8902 - * VCAP_KF_PROT_ACTIVE: W1, sparx5: es2 - * Protection is active - * VCAP_KF_TCP_IS: W1, sparx5: is0/is2/es2 - * Set if frame is IPv4 TCP frame (IP protocol = 6) or IPv6 TCP frames (Next - * header = 6) - * VCAP_KF_TCP_UDP_IS: W1, sparx5: is0/is2/es2 - * Set if frame is IPv4/IPv6 TCP or UDP frame (IP protocol/next header equals 6 - * or 17) - * VCAP_KF_TYPE: sparx5 is0 W2, sparx5 is0 W1, sparx5 is2 W4, sparx5 is2 W2, - * sparx5 es2 W3 - * Keyset type id - set by the API - */ - -/* Keyfield names */ -enum vcap_key_field { - VCAP_KF_NO_VALUE, /* initial value */ - VCAP_KF_8021BR_ECID_BASE, - VCAP_KF_8021BR_ECID_EXT, - VCAP_KF_8021BR_E_TAGGED, - VCAP_KF_8021BR_GRP, - VCAP_KF_8021BR_IGR_ECID_BASE, - VCAP_KF_8021BR_IGR_ECID_EXT, - VCAP_KF_8021Q_DEI0, - VCAP_KF_8021Q_DEI1, - VCAP_KF_8021Q_DEI2, - VCAP_KF_8021Q_DEI_CLS, - VCAP_KF_8021Q_PCP0, - VCAP_KF_8021Q_PCP1, - VCAP_KF_8021Q_PCP2, - VCAP_KF_8021Q_PCP_CLS, - VCAP_KF_8021Q_TPID0, - VCAP_KF_8021Q_TPID1, - VCAP_KF_8021Q_TPID2, - VCAP_KF_8021Q_VID0, - VCAP_KF_8021Q_VID1, - VCAP_KF_8021Q_VID2, - VCAP_KF_8021Q_VID_CLS, - VCAP_KF_8021Q_VLAN_TAGGED_IS, - VCAP_KF_8021Q_VLAN_TAGS, - VCAP_KF_ACL_GRP_ID, - VCAP_KF_ARP_ADDR_SPACE_OK_IS, - VCAP_KF_ARP_LEN_OK_IS, - VCAP_KF_ARP_OPCODE, - VCAP_KF_ARP_OPCODE_UNKNOWN_IS, - VCAP_KF_ARP_PROTO_SPACE_OK_IS, - VCAP_KF_ARP_SENDER_MATCH_IS, - VCAP_KF_ARP_TGT_MATCH_IS, - VCAP_KF_COSID_CLS, - VCAP_KF_DST_ENTRY, - VCAP_KF_ES0_ISDX_KEY_ENA, - VCAP_KF_ETYPE, - VCAP_KF_ETYPE_LEN_IS, - VCAP_KF_ETYPE_MPLS, - VCAP_KF_IF_EGR_PORT_MASK, - VCAP_KF_IF_EGR_PORT_MASK_RNG, - VCAP_KF_IF_IGR_PORT, - VCAP_KF_IF_IGR_PORT_MASK, - VCAP_KF_IF_IGR_PORT_MASK_L3, - VCAP_KF_IF_IGR_PORT_MASK_RNG, - VCAP_KF_IF_IGR_PORT_MASK_SEL, - VCAP_KF_IF_IGR_PORT_SEL, - VCAP_KF_IP4_IS, - VCAP_KF_IP_MC_IS, - VCAP_KF_IP_PAYLOAD_5TUPLE, - VCAP_KF_IP_SNAP_IS, - VCAP_KF_ISDX_CLS, - VCAP_KF_ISDX_GT0_IS, - VCAP_KF_L2_BC_IS, - VCAP_KF_L2_DMAC, - VCAP_KF_L2_FWD_IS, - VCAP_KF_L2_MC_IS, - VCAP_KF_L2_PAYLOAD_ETYPE, - VCAP_KF_L2_SMAC, - VCAP_KF_L3_DIP_EQ_SIP_IS, - VCAP_KF_L3_DMAC_DIP_MATCH, - VCAP_KF_L3_DPL_CLS, - VCAP_KF_L3_DSCP, - VCAP_KF_L3_DST_IS, - VCAP_KF_L3_FRAGMENT_TYPE, - VCAP_KF_L3_FRAG_INVLD_L4_LEN, - VCAP_KF_L3_IP4_DIP, - VCAP_KF_L3_IP4_SIP, - VCAP_KF_L3_IP6_DIP, - VCAP_KF_L3_IP6_SIP, - VCAP_KF_L3_IP_PROTO, - VCAP_KF_L3_OPTIONS_IS, - VCAP_KF_L3_PAYLOAD, - VCAP_KF_L3_RT_IS, - VCAP_KF_L3_SMAC_SIP_MATCH, - VCAP_KF_L3_TOS, - VCAP_KF_L3_TTL_GT0, - VCAP_KF_L4_ACK, - VCAP_KF_L4_DPORT, - VCAP_KF_L4_FIN, - VCAP_KF_L4_PAYLOAD, - VCAP_KF_L4_PSH, - VCAP_KF_L4_RNG, - VCAP_KF_L4_RST, - VCAP_KF_L4_SEQUENCE_EQ0_IS, - VCAP_KF_L4_SPORT, - VCAP_KF_L4_SPORT_EQ_DPORT_IS, - VCAP_KF_L4_SYN, - VCAP_KF_L4_URG, - VCAP_KF_LOOKUP_FIRST_IS, - VCAP_KF_LOOKUP_GEN_IDX, - VCAP_KF_LOOKUP_GEN_IDX_SEL, - VCAP_KF_LOOKUP_PAG, - VCAP_KF_MIRROR_ENA, - VCAP_KF_OAM_CCM_CNTS_EQ0, - VCAP_KF_OAM_MEL_FLAGS, - VCAP_KF_OAM_Y1731_IS, - VCAP_KF_PROT_ACTIVE, - VCAP_KF_TCP_IS, - VCAP_KF_TCP_UDP_IS, - VCAP_KF_TYPE, -}; - -/* Actionset names with origin information */ -enum vcap_actionfield_set { - VCAP_AFS_NO_VALUE, /* initial value */ - VCAP_AFS_BASE_TYPE, /* sparx5 is2 X3, sparx5 es2 X3 */ - VCAP_AFS_CLASSIFICATION, /* sparx5 is0 X2 */ - VCAP_AFS_CLASS_REDUCED, /* sparx5 is0 X1 */ - VCAP_AFS_FULL, /* sparx5 is0 X3 */ - VCAP_AFS_MLBS, /* sparx5 is0 X2 */ - VCAP_AFS_MLBS_REDUCED, /* sparx5 is0 X1 */ -}; - -/* List of actionfields with description - * - * VCAP_AF_CLS_VID_SEL: W3, sparx5: is0 - * Controls the classified VID: 0: VID_NONE: No action. 1: VID_ADD: New VID = - * old VID + VID_VAL. 2: VID_REPLACE: New VID = VID_VAL. 3: VID_FIRST_TAG: New - * VID = VID from frame's first tag (outer tag) if available, otherwise VID_VAL. - * 4: VID_SECOND_TAG: New VID = VID from frame's second tag (middle tag) if - * available, otherwise VID_VAL. 5: VID_THIRD_TAG: New VID = VID from frame's - * third tag (inner tag) if available, otherwise VID_VAL. - * VCAP_AF_CNT_ID: sparx5 is2 W12, sparx5 es2 W11 - * Counter ID, used per lookup to index the 4K frame counters (ANA_ACL:CNT_TBL). - * Multiple VCAP IS2 entries can use the same counter. - * VCAP_AF_COPY_PORT_NUM: W7, sparx5: es2 - * QSYS port number when FWD_MODE is redirect or copy - * VCAP_AF_COPY_QUEUE_NUM: W16, sparx5: es2 - * QSYS queue number when FWD_MODE is redirect or copy - * VCAP_AF_CPU_COPY_ENA: W1, sparx5: is2/es2 - * Setting this bit to 1 causes all frames that hit this action to be copied to - * the CPU extraction queue specified in CPU_QUEUE_NUM. - * VCAP_AF_CPU_QUEUE_NUM: W3, sparx5: is2/es2 - * CPU queue number. Used when CPU_COPY_ENA is set. - * VCAP_AF_DEI_ENA: W1, sparx5: is0 - * If set, use DEI_VAL as classified DEI value. Otherwise, DEI from basic - * classification is used - * VCAP_AF_DEI_VAL: W1, sparx5: is0 - * See DEI_ENA - * VCAP_AF_DP_ENA: W1, sparx5: is0 - * If set, use DP_VAL as classified drop precedence level. Otherwise, drop - * precedence level from basic classification is used. - * VCAP_AF_DP_VAL: W2, sparx5: is0 - * See DP_ENA. - * VCAP_AF_DSCP_ENA: W1, sparx5: is0 - * If set, use DSCP_VAL as classified DSCP value. Otherwise, DSCP value from - * basic classification is used. - * VCAP_AF_DSCP_VAL: W6, sparx5: is0 - * See DSCP_ENA. - * VCAP_AF_ES2_REW_CMD: W3, sparx5: es2 - * Command forwarded to REW: 0: No action. 1: SWAP MAC addresses. 2: Do L2CP - * DMAC translation when entering or leaving a tunnel. - * VCAP_AF_FWD_MODE: W2, sparx5: es2 - * Forward selector: 0: Forward. 1: Discard. 2: Redirect. 3: Copy. - * VCAP_AF_HIT_ME_ONCE: W1, sparx5: is2/es2 - * Setting this bit to 1 causes the first frame that hits this action where the - * HIT_CNT counter is zero to be copied to the CPU extraction queue specified in - * CPU_QUEUE_NUM. The HIT_CNT counter is then incremented and any frames that - * hit this action later are not copied to the CPU. To re-enable the HIT_ME_ONCE - * functionality, the HIT_CNT counter must be cleared. - * VCAP_AF_IGNORE_PIPELINE_CTRL: W1, sparx5: is2/es2 - * Ignore ingress pipeline control. This enforces the use of the VCAP IS2 action - * even when the pipeline control has terminated the frame before VCAP IS2. - * VCAP_AF_INTR_ENA: W1, sparx5: is2/es2 - * If set, an interrupt is triggered when this rule is hit - * VCAP_AF_ISDX_ADD_REPLACE_SEL: W1, sparx5: is0 - * Controls the classified ISDX. 0: New ISDX = old ISDX + ISDX_VAL. 1: New ISDX - * = ISDX_VAL. - * VCAP_AF_ISDX_VAL: W12, sparx5: is0 - * See isdx_add_replace_sel - * VCAP_AF_LRN_DIS: W1, sparx5: is2 - * Setting this bit to 1 disables learning of frames hitting this action. - * VCAP_AF_MAP_IDX: W9, sparx5: is0 - * Index for QoS mapping table lookup - * VCAP_AF_MAP_KEY: W3, sparx5: is0 - * Key type for QoS mapping table lookup. 0: DEI0, PCP0 (outer tag). 1: DEI1, - * PCP1 (middle tag). 2: DEI2, PCP2 (inner tag). 3: MPLS TC. 4: PCP0 (outer - * tag). 5: E-DEI, E-PCP (E-TAG). 6: DSCP if available, otherwise none. 7: DSCP - * if available, otherwise DEI0, PCP0 (outer tag) if available using MAP_IDX+8, - * otherwise none - * VCAP_AF_MAP_LOOKUP_SEL: W2, sparx5: is0 - * Selects which of the two QoS Mapping Table lookups that MAP_KEY and MAP_IDX - * are applied to. 0: No changes to the QoS Mapping Table lookup. 1: Update key - * type and index for QoS Mapping Table lookup #0. 2: Update key type and index - * for QoS Mapping Table lookup #1. 3: Reserved. - * VCAP_AF_MASK_MODE: W3, sparx5: is0/is2 - * Controls the PORT_MASK use. Sparx5: 0: OR_DSTMASK, 1: AND_VLANMASK, 2: - * REPLACE_PGID, 3: REPLACE_ALL, 4: REDIR_PGID, 5: OR_PGID_MASK, 6: VSTAX, 7: - * Not applicable. LAN966X: 0: No action, 1: Permit/deny (AND), 2: Policy - * forwarding (DMAC lookup), 3: Redirect. The CPU port is untouched by - * MASK_MODE. - * VCAP_AF_MATCH_ID: W16, sparx5: is0/is2 - * Logical ID for the entry. The MATCH_ID is extracted together with the frame - * if the frame is forwarded to the CPU (CPU_COPY_ENA). The result is placed in - * IFH.CL_RSLT. - * VCAP_AF_MATCH_ID_MASK: W16, sparx5: is0/is2 - * Mask used by MATCH_ID. - * VCAP_AF_MIRROR_PROBE: W2, sparx5: is2 - * Mirroring performed according to configuration of a mirror probe. 0: No - * mirroring. 1: Mirror probe 0. 2: Mirror probe 1. 3: Mirror probe 2 - * VCAP_AF_MIRROR_PROBE_ID: W2, sparx5: es2 - * Signals a mirror probe to be placed in the IFH. Only possible when FWD_MODE - * is copy. 0: No mirroring. 1–3: Use mirror probe 0-2. - * VCAP_AF_NXT_IDX: W12, sparx5: is0 - * Index used as part of key (field G_IDX) in the next lookup. - * VCAP_AF_NXT_IDX_CTRL: W3, sparx5: is0 - * Controls the generation of the G_IDX used in the VCAP CLM next lookup - * VCAP_AF_PAG_OVERRIDE_MASK: W8, sparx5: is0 - * Bits set in this mask will override PAG_VAL from port profile.  New PAG = - * (PAG (input) AND ~PAG_OVERRIDE_MASK) OR (PAG_VAL AND PAG_OVERRIDE_MASK) - * VCAP_AF_PAG_VAL: W8, sparx5: is0 - * See PAG_OVERRIDE_MASK. - * VCAP_AF_PCP_ENA: W1, sparx5: is0 - * If set, use PCP_VAL as classified PCP value. Otherwise, PCP from basic - * classification is used. - * VCAP_AF_PCP_VAL: W3, sparx5: is0 - * See PCP_ENA. - * VCAP_AF_PIPELINE_FORCE_ENA: sparx5 is0 W2, sparx5 is2 W1 - * If set, use PIPELINE_PT unconditionally and set PIPELINE_ACT = NONE if - * PIPELINE_PT == NONE. Overrules previous settings of pipeline point. - * VCAP_AF_PIPELINE_PT: W5, sparx5: is0/is2 - * Pipeline point used if PIPELINE_FORCE_ENA is set - * VCAP_AF_POLICE_ENA: W1, sparx5: is2/es2 - * Setting this bit to 1 causes frames that hit this action to be policed by the - * ACL policer specified in POLICE_IDX. Only applies to the first lookup. - * VCAP_AF_POLICE_IDX: W6, sparx5: is2/es2 - * Selects VCAP policer used when policing frames (POLICE_ENA) - * VCAP_AF_POLICE_REMARK: W1, sparx5: es2 - * If set, frames exceeding policer rates are marked as yellow but not - * discarded. - * VCAP_AF_PORT_MASK: sparx5 is0 W65, sparx5 is2 W68 - * Port mask applied to the forwarding decision based on MASK_MODE. - * VCAP_AF_QOS_ENA: W1, sparx5: is0 - * If set, use QOS_VAL as classified QoS class. Otherwise, QoS class from basic - * classification is used. - * VCAP_AF_QOS_VAL: W3, sparx5: is0 - * See QOS_ENA. - * VCAP_AF_RT_DIS: W1, sparx5: is2 - * If set, routing is disallowed. Only applies when IS_INNER_ACL is 0. See also - * IGR_ACL_ENA, EGR_ACL_ENA, and RLEG_STAT_IDX. - * VCAP_AF_TYPE: W1, sparx5: is0 - * Actionset type id - Set by the API - * VCAP_AF_VID_VAL: W13, sparx5: is0 - * New VID Value - */ - -/* Actionfield names */ -enum vcap_action_field { - VCAP_AF_NO_VALUE, /* initial value */ - VCAP_AF_ACL_MAC, - VCAP_AF_ACL_RT_MODE, - VCAP_AF_CLS_VID_SEL, - VCAP_AF_CNT_ID, - VCAP_AF_COPY_PORT_NUM, - VCAP_AF_COPY_QUEUE_NUM, - VCAP_AF_COSID_ENA, - VCAP_AF_COSID_VAL, - VCAP_AF_CPU_COPY_ENA, - VCAP_AF_CPU_DIS, - VCAP_AF_CPU_ENA, - VCAP_AF_CPU_Q, - VCAP_AF_CPU_QUEUE_NUM, - VCAP_AF_CUSTOM_ACE_ENA, - VCAP_AF_CUSTOM_ACE_OFFSET, - VCAP_AF_DEI_ENA, - VCAP_AF_DEI_VAL, - VCAP_AF_DLB_OFFSET, - VCAP_AF_DMAC_OFFSET_ENA, - VCAP_AF_DP_ENA, - VCAP_AF_DP_VAL, - VCAP_AF_DSCP_ENA, - VCAP_AF_DSCP_VAL, - VCAP_AF_EGR_ACL_ENA, - VCAP_AF_ES2_REW_CMD, - VCAP_AF_FWD_DIS, - VCAP_AF_FWD_MODE, - VCAP_AF_FWD_TYPE, - VCAP_AF_GVID_ADD_REPLACE_SEL, - VCAP_AF_HIT_ME_ONCE, - VCAP_AF_IGNORE_PIPELINE_CTRL, - VCAP_AF_IGR_ACL_ENA, - VCAP_AF_INJ_MASQ_ENA, - VCAP_AF_INJ_MASQ_LPORT, - VCAP_AF_INJ_MASQ_PORT, - VCAP_AF_INTR_ENA, - VCAP_AF_ISDX_ADD_REPLACE_SEL, - VCAP_AF_ISDX_VAL, - VCAP_AF_IS_INNER_ACL, - VCAP_AF_L3_MAC_UPDATE_DIS, - VCAP_AF_LOG_MSG_INTERVAL, - VCAP_AF_LPM_AFFIX_ENA, - VCAP_AF_LPM_AFFIX_VAL, - VCAP_AF_LPORT_ENA, - VCAP_AF_LRN_DIS, - VCAP_AF_MAP_IDX, - VCAP_AF_MAP_KEY, - VCAP_AF_MAP_LOOKUP_SEL, - VCAP_AF_MASK_MODE, - VCAP_AF_MATCH_ID, - VCAP_AF_MATCH_ID_MASK, - VCAP_AF_MIP_SEL, - VCAP_AF_MIRROR_PROBE, - VCAP_AF_MIRROR_PROBE_ID, - VCAP_AF_MPLS_IP_CTRL_ENA, - VCAP_AF_MPLS_MEP_ENA, - VCAP_AF_MPLS_MIP_ENA, - VCAP_AF_MPLS_OAM_FLAVOR, - VCAP_AF_MPLS_OAM_TYPE, - VCAP_AF_NUM_VLD_LABELS, - VCAP_AF_NXT_IDX, - VCAP_AF_NXT_IDX_CTRL, - VCAP_AF_NXT_KEY_TYPE, - VCAP_AF_NXT_NORMALIZE, - VCAP_AF_NXT_NORM_W16_OFFSET, - VCAP_AF_NXT_NORM_W32_OFFSET, - VCAP_AF_NXT_OFFSET_FROM_TYPE, - VCAP_AF_NXT_TYPE_AFTER_OFFSET, - VCAP_AF_OAM_IP_BFD_ENA, - VCAP_AF_OAM_TWAMP_ENA, - VCAP_AF_OAM_Y1731_SEL, - VCAP_AF_PAG_OVERRIDE_MASK, - VCAP_AF_PAG_VAL, - VCAP_AF_PCP_ENA, - VCAP_AF_PCP_VAL, - VCAP_AF_PIPELINE_ACT_SEL, - VCAP_AF_PIPELINE_FORCE_ENA, - VCAP_AF_PIPELINE_PT, - VCAP_AF_PIPELINE_PT_REDUCED, - VCAP_AF_POLICE_ENA, - VCAP_AF_POLICE_IDX, - VCAP_AF_POLICE_REMARK, - VCAP_AF_PORT_MASK, - VCAP_AF_PTP_MASTER_SEL, - VCAP_AF_QOS_ENA, - VCAP_AF_QOS_VAL, - VCAP_AF_REW_CMD, - VCAP_AF_RLEG_DMAC_CHK_DIS, - VCAP_AF_RLEG_STAT_IDX, - VCAP_AF_RSDX_ENA, - VCAP_AF_RSDX_VAL, - VCAP_AF_RSVD_LBL_VAL, - VCAP_AF_RT_DIS, - VCAP_AF_RT_SEL, - VCAP_AF_S2_KEY_SEL_ENA, - VCAP_AF_S2_KEY_SEL_IDX, - VCAP_AF_SAM_SEQ_ENA, - VCAP_AF_SIP_IDX, - VCAP_AF_SWAP_MAC_ENA, - VCAP_AF_TCP_UDP_DPORT, - VCAP_AF_TCP_UDP_ENA, - VCAP_AF_TCP_UDP_SPORT, - VCAP_AF_TC_ENA, - VCAP_AF_TC_LABEL, - VCAP_AF_TPID_SEL, - VCAP_AF_TTL_DECR_DIS, - VCAP_AF_TTL_ENA, - VCAP_AF_TTL_LABEL, - VCAP_AF_TTL_UPDATE_ENA, - VCAP_AF_TYPE, - VCAP_AF_VID_VAL, - VCAP_AF_VLAN_POP_CNT, - VCAP_AF_VLAN_POP_CNT_ENA, - VCAP_AF_VLAN_PUSH_CNT, - VCAP_AF_VLAN_PUSH_CNT_ENA, - VCAP_AF_VLAN_WAS_TAGGED, -}; - -#endif /* __VCAP_AG_API__ */ diff --git a/drivers/net/ethernet/microchip/vcap/vcap_api.h b/drivers/net/ethernet/microchip/vcap/vcap_api.h index f4a5ba5ffa87..ca4499838306 100644 --- a/drivers/net/ethernet/microchip/vcap/vcap_api.h +++ b/drivers/net/ethernet/microchip/vcap/vcap_api.h @@ -11,9 +11,6 @@ #include /* Use the generated API model */ -#ifdef CONFIG_VCAP_KUNIT_TEST -#include "vcap_ag_api_kunit.h" -#endif #include "vcap_ag_api.h" #define VCAP_CID_LOOKUP_SIZE 100000 /* Chains in a lookup */ From ee72d90b042efcc2aec7c3bd54cb1f03f3d1b584 Mon Sep 17 00:00:00 2001 From: Horatiu Vultur Date: Fri, 25 Nov 2022 10:50:03 +0100 Subject: [PATCH 1290/2002] net: microchip: vcap: Extend vcap with lan966x Add the keysets, keys, actionsets and actions used by lan966x in IS2. Signed-off-by: Horatiu Vultur Signed-off-by: Paolo Abeni --- .../net/ethernet/microchip/vcap/vcap_ag_api.h | 202 +++++++++++++----- 1 file changed, 146 insertions(+), 56 deletions(-) diff --git a/drivers/net/ethernet/microchip/vcap/vcap_ag_api.h b/drivers/net/ethernet/microchip/vcap/vcap_ag_api.h index d6d70af89616..84de2aee4169 100644 --- a/drivers/net/ethernet/microchip/vcap/vcap_ag_api.h +++ b/drivers/net/ethernet/microchip/vcap/vcap_ag_api.h @@ -36,6 +36,13 @@ enum vcap_keyfield_set { VCAP_KFS_NORMAL_7TUPLE, /* sparx5 is0 X12 */ VCAP_KFS_PURE_5TUPLE_IP4, /* sparx5 is0 X3 */ VCAP_KFS_TRI_VID, /* sparx5 is0 X2 */ + VCAP_KFS_MAC_LLC, /* lan966x is2 X2 */ + VCAP_KFS_MAC_SNAP, /* lan966x is2 X2 */ + VCAP_KFS_OAM, /* lan966x is2 X2 */ + VCAP_KFS_IP6_TCP_UDP, /* lan966x is2 X4 */ + VCAP_KFS_IP6_OTHER, /* lan966x is2 X4 */ + VCAP_KFS_SMAC_SIP4, /* lan966x is2 X1 */ + VCAP_KFS_SMAC_SIP6, /* lan966x is2 X2 */ }; /* List of keyfields with description @@ -61,7 +68,7 @@ enum vcap_keyfield_set { * Second DEI in multiple vlan tags (inner tag) * VCAP_KF_8021Q_DEI2: W1, sparx5: is0 * Third DEI in multiple vlan tags (not always available) - * VCAP_KF_8021Q_DEI_CLS: W1, sparx5: is2/es2 + * VCAP_KF_8021Q_DEI_CLS: W1, sparx5: is2/es2, lan966x: is2 * Classified DEI * VCAP_KF_8021Q_PCP0: W3, sparx5: is0 * First PCP in multiple vlan tags (outer tag or default port tag) @@ -69,7 +76,7 @@ enum vcap_keyfield_set { * Second PCP in multiple vlan tags (inner tag) * VCAP_KF_8021Q_PCP2: W3, sparx5: is0 * Third PCP in multiple vlan tags (not always available) - * VCAP_KF_8021Q_PCP_CLS: W3, sparx5: is2/es2 + * VCAP_KF_8021Q_PCP_CLS: W3, sparx5: is2/es2, lan966x: is2 * Classified PCP * VCAP_KF_8021Q_TPID0: W3, sparx5: is0 * First TPIC in multiple vlan tags (outer tag or default port tag) @@ -83,9 +90,9 @@ enum vcap_keyfield_set { * Second VID in multiple vlan tags (inner tag) * VCAP_KF_8021Q_VID2: W12, sparx5: is0 * Third VID in multiple vlan tags (not always available) - * VCAP_KF_8021Q_VID_CLS: W13, sparx5: is2/es2 + * VCAP_KF_8021Q_VID_CLS: W13, sparx5: is2/es2, lan966x is2 W12 * Classified VID - * VCAP_KF_8021Q_VLAN_TAGGED_IS: W1, sparx5: is2/es2 + * VCAP_KF_8021Q_VLAN_TAGGED_IS: W1, sparx5: is2/es2, lan966x: is2 * Sparx5: Set if frame was received with a VLAN tag, LAN966x: Set if frame has * one or more Q-tags. Independent of port VLAN awareness * VCAP_KF_8021Q_VLAN_TAGS: W3, sparx5: is0 @@ -93,19 +100,19 @@ enum vcap_keyfield_set { * tagged, 7: Triple tagged * VCAP_KF_ACL_GRP_ID: W8, sparx5: es2 * Used in interface map table - * VCAP_KF_ARP_ADDR_SPACE_OK_IS: W1, sparx5: is2/es2 + * VCAP_KF_ARP_ADDR_SPACE_OK_IS: W1, sparx5: is2/es2, lan966x: is2 * Set if hardware address is Ethernet - * VCAP_KF_ARP_LEN_OK_IS: W1, sparx5: is2/es2 + * VCAP_KF_ARP_LEN_OK_IS: W1, sparx5: is2/es2, lan966x: is2 * Set if hardware address length = 6 (Ethernet) and IP address length = 4 (IP). - * VCAP_KF_ARP_OPCODE: W2, sparx5: is2/es2 + * VCAP_KF_ARP_OPCODE: W2, sparx5: is2/es2, lan966x: i2 * ARP opcode - * VCAP_KF_ARP_OPCODE_UNKNOWN_IS: W1, sparx5: is2/es2 + * VCAP_KF_ARP_OPCODE_UNKNOWN_IS: W1, sparx5: is2/es2, lan966x: is2 * Set if not one of the codes defined in VCAP_KF_ARP_OPCODE - * VCAP_KF_ARP_PROTO_SPACE_OK_IS: W1, sparx5: is2/es2 + * VCAP_KF_ARP_PROTO_SPACE_OK_IS: W1, sparx5: is2/es2, lan966x: is2 * Set if protocol address space is 0x0800 - * VCAP_KF_ARP_SENDER_MATCH_IS: W1, sparx5: is2/es2 + * VCAP_KF_ARP_SENDER_MATCH_IS: W1, sparx5: is2/es2, lan966x: is2 * Sender Hardware Address = SMAC (ARP) - * VCAP_KF_ARP_TGT_MATCH_IS: W1, sparx5: is2/es2 + * VCAP_KF_ARP_TGT_MATCH_IS: W1, sparx5: is2/es2, lan966x: is2 * Target Hardware Address = SMAC (RARP) * VCAP_KF_COSID_CLS: W3, sparx5: es2 * Class of service @@ -114,7 +121,7 @@ enum vcap_keyfield_set { * fields L2_SMAC and L3_IP4_SIP * VCAP_KF_ES0_ISDX_KEY_ENA: W1, sparx5: es2 * The value taken from the IFH .FWD.ES0_ISDX_KEY_ENA - * VCAP_KF_ETYPE: W16, sparx5: is0/is2/es2 + * VCAP_KF_ETYPE: W16, sparx5: is0/is2/es2, lan966x: is2 * Ethernet type * VCAP_KF_ETYPE_LEN_IS: W1, sparx5: is0/is2/es2 * Set if frame has EtherType >= 0x600 @@ -128,7 +135,8 @@ enum vcap_keyfield_set { * VCAP_KF_IF_IGR_PORT: sparx5 is0 W7, sparx5 es2 W9 * Sparx5: Logical ingress port number retrieved from * ANA_CL::PORT_ID_CFG.LPORT_NUM or ERLEG, LAN966x: ingress port nunmber - * VCAP_KF_IF_IGR_PORT_MASK: sparx5 is0 W65, sparx5 is2 W32, sparx5 is2 W65 + * VCAP_KF_IF_IGR_PORT_MASK: sparx5 is0 W65, sparx5 is2 W32, sparx5 is2 W65, + * lan966x is2 W9 * Ingress port mask, one bit per port/erleg * VCAP_KF_IF_IGR_PORT_MASK_L3: W1, sparx5: is2 * If set, IF_IGR_PORT_MASK, IF_IGR_PORT_MASK_RNG, and IF_IGR_PORT_MASK_SEL are @@ -141,7 +149,7 @@ enum vcap_keyfield_set { * Mapping: 0: DEFAULT 1: LOOPBACK 2: MASQUERADE 3: CPU_VD * VCAP_KF_IF_IGR_PORT_SEL: W1, sparx5: es2 * Selector for IF_IGR_PORT: physical port number or ERLEG - * VCAP_KF_IP4_IS: W1, sparx5: is0/is2/es2 + * VCAP_KF_IP4_IS: W1, sparx5: is0/is2/es2, lan966x: is2 * Set if frame has EtherType = 0x800 and IP version = 4 * VCAP_KF_IP_MC_IS: W1, sparx5: is0 * Set if frame is IPv4 frame and frame’s destination MAC address is an IPv4 @@ -153,22 +161,22 @@ enum vcap_keyfield_set { * Set if frame is IPv4, IPv6, or SNAP frame * VCAP_KF_ISDX_CLS: W12, sparx5: is2/es2 * Classified ISDX - * VCAP_KF_ISDX_GT0_IS: W1, sparx5: is2/es2 + * VCAP_KF_ISDX_GT0_IS: W1, sparx5: is2/es2, lan966x: is2 * Set if classified ISDX > 0 - * VCAP_KF_L2_BC_IS: W1, sparx5: is0/is2/es2 + * VCAP_KF_L2_BC_IS: W1, sparx5: is0/is2/es2, lan966x: is2 * Set if frame’s destination MAC address is the broadcast address * (FF-FF-FF-FF-FF-FF). - * VCAP_KF_L2_DMAC: W48, sparx5: is0/is2/es2 + * VCAP_KF_L2_DMAC: W48, sparx5: is0/is2/es2, lan966x: is2 * Destination MAC address * VCAP_KF_L2_FWD_IS: W1, sparx5: is2 * Set if the frame is allowed to be forwarded to front ports - * VCAP_KF_L2_MC_IS: W1, sparx5: is0/is2/es2 + * VCAP_KF_L2_MC_IS: W1, sparx5: is0/is2/es2, lan9966x is2 * Set if frame’s destination MAC address is a multicast address (bit 40 = 1). * VCAP_KF_L2_PAYLOAD_ETYPE: W64, sparx5: is2/es2 * Byte 0-7 of L2 payload after Type/Len field and overloading for OAM - * VCAP_KF_L2_SMAC: W48, sparx5: is0/is2/es2 + * VCAP_KF_L2_SMAC: W48, sparx5: is0/is2/es2, lan966x is2 * Source MAC address - * VCAP_KF_L3_DIP_EQ_SIP_IS: W1, sparx5: is2/es2 + * VCAP_KF_L3_DIP_EQ_SIP_IS: W1, sparx5: is2/es2, lan966x: is2 * Set if Src IP matches Dst IP address * VCAP_KF_L3_DMAC_DIP_MATCH: W1, sparx5: is2 * Match found in DIP security lookup in ANA_L3 @@ -183,21 +191,22 @@ enum vcap_keyfield_set { * VCAP_KF_L3_FRAG_INVLD_L4_LEN: W1, sparx5: is0/is2 * Set if frame's L4 length is less than ANA_CL:COMMON:CLM_FRAGMENT_CFG.L4_MIN_L * EN - * VCAP_KF_L3_IP4_DIP: W32, sparx5: is0/is2/es2 + * VCAP_KF_L3_IP4_DIP: W32, sparx5: is0/is2/es2, lan966x: is2 * Destination IPv4 Address - * VCAP_KF_L3_IP4_SIP: W32, sparx5: is0/is2/es2 + * VCAP_KF_L3_IP4_SIP: W32, sparx5: is0/is2/es2, lan966x: is2 * Source IPv4 Address - * VCAP_KF_L3_IP6_DIP: W128, sparx5: is0/is2/es2 + * VCAP_KF_L3_IP6_DIP: W128, sparx5: is0/is2/es2, lan966x: is2 * Sparx5: Full IPv6 DIP, LAN966x: Either Full IPv6 DIP or a subset depending on * frame type - * VCAP_KF_L3_IP6_SIP: W128, sparx5: is0/is2/es2 + * VCAP_KF_L3_IP6_SIP: W128, sparx5: is0/is2/es2, lan966x: is2 * Sparx5: Full IPv6 SIP, LAN966x: Either Full IPv6 SIP or a subset depending on * frame type - * VCAP_KF_L3_IP_PROTO: W8, sparx5: is0/is2/es2 + * VCAP_KF_L3_IP_PROTO: W8, sparx5: is0/is2/es2, lan966x: is2 * IPv4 frames: IP protocol. IPv6 frames: Next header, same as for IPV4 - * VCAP_KF_L3_OPTIONS_IS: W1, sparx5: is0/is2/es2 + * VCAP_KF_L3_OPTIONS_IS: W1, sparx5: is0/is2/es2, lan966x: is2 * Set if IPv4 frame contains options (IP len > 5) - * VCAP_KF_L3_PAYLOAD: sparx5 is2 W96, sparx5 is2 W40, sparx5 es2 W96 + * VCAP_KF_L3_PAYLOAD: sparx5 is2 W96, sparx5 is2 W40, sparx5 es2 W96, + * lan966x is2 W56 * Sparx5: Payload bytes after IP header. IPv4: IPv4 options are not parsed so * payload is always taken 20 bytes after the start of the IPv4 header, LAN966x: * Bytes 0-6 after IP header @@ -205,14 +214,14 @@ enum vcap_keyfield_set { * Set if frame has hit a router leg * VCAP_KF_L3_SMAC_SIP_MATCH: W1, sparx5: is2 * Match found in SIP security lookup in ANA_L3 - * VCAP_KF_L3_TOS: W8, sparx5: is2/es2 + * VCAP_KF_L3_TOS: W8, sparx5: is2/es2, lan966x: is2 * Sparx5: Frame's IPv4/IPv6 DSCP and ECN fields, LAN966x: IP TOS field - * VCAP_KF_L3_TTL_GT0: W1, sparx5: is2/es2 + * VCAP_KF_L3_TTL_GT0: W1, sparx5: is2/es2, lan966x: is2 * Set if IPv4 TTL / IPv6 hop limit is greater than 0 - * VCAP_KF_L4_ACK: W1, sparx5: is2/es2 + * VCAP_KF_L4_ACK: W1, sparx5: is2/es2, lan966x: is2 * Sparx5 and LAN966x: TCP flag ACK, LAN966x only: PTP over UDP: flagField bit 2 * (unicastFlag) - * VCAP_KF_L4_DPORT: W16, sparx5: is2/es2 + * VCAP_KF_L4_DPORT: W16, sparx5: is2/es2, lan966x: is2 * Sparx5: TCP/UDP destination port. Overloading for IP_7TUPLE: Non-TCP/UDP IP * frames: L4_DPORT = L3_IP_PROTO, LAN966x: TCP/UDP destination port * VCAP_KF_L4_FIN: W1, sparx5: is2/es2 @@ -222,55 +231,90 @@ enum vcap_keyfield_set { * frames: Payload bytes 0–7 after IP header. IPv4 options are not parsed so * payload is always taken 20 bytes after the start of the IPv4 header for non * TCP/UDP IPv4 frames - * VCAP_KF_L4_PSH: W1, sparx5: is2/es2 + * VCAP_KF_L4_PSH: W1, sparx5: is2/es2, lan966x: is2 * Sparx5: TCP flag PSH, LAN966x: TCP: TCP flag PSH. PTP over UDP: flagField bit * 1 (twoStepFlag) - * VCAP_KF_L4_RNG: sparx5 is0 W8, sparx5 is2 W16, sparx5 es2 W16 + * VCAP_KF_L4_RNG: sparx5 is0 W8, sparx5 is2 W16, sparx5 es2 W16, lan966x: is2 * Range checker bitmask (one for each range checker). Input into range checkers * is taken from classified results (VID, DSCP) and frame (SPORT, DPORT, ETYPE, * outer VID, inner VID) - * VCAP_KF_L4_RST: W1, sparx5: is2/es2 + * VCAP_KF_L4_RST: W1, sparx5: is2/es2, lan966x: is2 * Sparx5: TCP flag RST , LAN966x: TCP: TCP flag RST. PTP over UDP: messageType * bit 3 - * VCAP_KF_L4_SEQUENCE_EQ0_IS: W1, sparx5: is2/es2 + * VCAP_KF_L4_SEQUENCE_EQ0_IS: W1, sparx5: is2/es2, lan966x: is2 * Set if TCP sequence number is 0, LAN966x: Overlayed with PTP over UDP: * messageType bit 0 - * VCAP_KF_L4_SPORT: W16, sparx5: is0/is2/es2 + * VCAP_KF_L4_SPORT: W16, sparx5: is0/is2/es2, lan966x: is2 * TCP/UDP source port - * VCAP_KF_L4_SPORT_EQ_DPORT_IS: W1, sparx5: is2/es2 + * VCAP_KF_L4_SPORT_EQ_DPORT_IS: W1, sparx5: is2/es2, lan966x: is2 * Set if UDP or TCP source port equals UDP or TCP destination port - * VCAP_KF_L4_SYN: W1, sparx5: is2/es2 + * VCAP_KF_L4_SYN: W1, sparx5: is2/es2, lan966x: is2 * Sparx5: TCP flag SYN, LAN966x: TCP: TCP flag SYN. PTP over UDP: messageType * bit 2 - * VCAP_KF_L4_URG: W1, sparx5: is2/es2 + * VCAP_KF_L4_URG: W1, sparx5: is2/es2, lan966x: is2 * Sparx5: TCP flag URG, LAN966x: TCP: TCP flag URG. PTP over UDP: flagField bit * 7 (reserved) - * VCAP_KF_LOOKUP_FIRST_IS: W1, sparx5: is0/is2/es2 + * VCAP_KF_LOOKUP_FIRST_IS: W1, sparx5: is0/is2/es2, lan966x: is2 * Selects between entries relevant for first and second lookup. Set for first * lookup, cleared for second lookup. * VCAP_KF_LOOKUP_GEN_IDX: W12, sparx5: is0 * Generic index - for chaining CLM instances * VCAP_KF_LOOKUP_GEN_IDX_SEL: W2, sparx5: is0 * Select the mode of the Generic Index - * VCAP_KF_LOOKUP_PAG: W8, sparx5: is2 + * VCAP_KF_LOOKUP_PAG: W8, sparx5: is2, lan966x: is2 * Classified Policy Association Group: chains rules from IS1/CLM to IS2 - * VCAP_KF_OAM_CCM_CNTS_EQ0: W1, sparx5: is2/es2 + * VCAP_KF_OAM_CCM_CNTS_EQ0: W1, sparx5: is2/es2, lan966x: is2 * Dual-ended loss measurement counters in CCM frames are all zero - * VCAP_KF_OAM_MEL_FLAGS: W7, sparx5: is0 + * VCAP_KF_OAM_MEL_FLAGS: W7, sparx5: is0, lan966x: is2 * Encoding of MD level/MEG level (MEL) - * VCAP_KF_OAM_Y1731_IS: W1, sparx5: is0/is2/es2 + * VCAP_KF_OAM_Y1731_IS: W1, sparx5: is0/is2/es2, lan966x: is2 * Set if frame’s EtherType = 0x8902 * VCAP_KF_PROT_ACTIVE: W1, sparx5: es2 * Protection is active - * VCAP_KF_TCP_IS: W1, sparx5: is0/is2/es2 + * VCAP_KF_TCP_IS: W1, sparx5: is0/is2/es2, lan966x: is2 * Set if frame is IPv4 TCP frame (IP protocol = 6) or IPv6 TCP frames (Next * header = 6) - * VCAP_KF_TCP_UDP_IS: W1, sparx5: is0/is2/es2 + * VCAP_KF_TCP_UDP_IS: W1, sparx5: is0/is2/es2, lan966x: is2 * Set if frame is IPv4/IPv6 TCP or UDP frame (IP protocol/next header equals 6 * or 17) * VCAP_KF_TYPE: sparx5 is0 W2, sparx5 is0 W1, sparx5 is2 W4, sparx5 is2 W2, - * sparx5 es2 W3 + * sparx5 es2 W3, lan966x: is2 * Keyset type id - set by the API + * VCAP_KF_HOST_MATCH: W1, lan966x: is2 + * The action from the SMAC_SIP4 or SMAC_SIP6 lookups. Used for IP source + * guarding. + * VCAP_KF_L2_FRM_TYPE: W4, lan966x: is2 + * Frame subtype for specific EtherTypes (MRP, DLR) + * VCAP_KF_L2_PAYLOAD0: W16, lan966x: is2 + * Payload bytes 0-1 after the frame’s EtherType + * VCAP_KF_L2_PAYLOAD1: W8, lan966x: is2 + * Payload byte 4 after the frame’s EtherType. This is specifically for PTP + * frames. + * VCAP_KF_L2_PAYLOAD2: W3, lan966x: is2 + * Bits 7, 2, and 1 from payload byte 6 after the frame’s EtherType. This is + * specifically for PTP frames. + * VCAP_KF_L2_LLC: W40, lan966x: is2 + * LLC header and data after up to two VLAN tags and the type/length field + * VCAP_KF_L3_FRAGMENT: W1, lan966x: is2 + * Set if IPv4 frame is fragmented + * VCAP_KF_L3_FRAG_OFS_GT0: W1, lan966x: is2 + * Set if IPv4 frame is fragmented and it is not the first fragment + * VCAP_KF_L2_SNAP: W40, lan966x: is2 + * SNAP header after LLC header (AA-AA-03) + * VCAP_KF_L4_1588_DOM: W8, lan966x: is2 + * PTP over UDP: domainNumber + * VCAP_KF_L4_1588_VER: W4, lan966x: is2 + * PTP over UDP: version + * VCAP_KF_OAM_MEPID: W16, lan966x: is2 + * CCM frame’s OAM MEP ID + * VCAP_KF_OAM_OPCODE: W8, lan966x: is2 + * Frame’s OAM opcode + * VCAP_KF_OAM_VER: W5, lan966x: is2 + * Frame’s OAM version + * VCAP_KF_OAM_FLAGS: W8, lan966x: is2 + * Frame’s OAM flags + * VCAP_KF_OAM_DETECTED: W1, lan966x: is2 + * This is missing in the datasheet, but present in the OAM keyset in XML */ /* Keyfield names */ @@ -375,17 +419,34 @@ enum vcap_key_field { VCAP_KF_TCP_IS, VCAP_KF_TCP_UDP_IS, VCAP_KF_TYPE, + VCAP_KF_HOST_MATCH, + VCAP_KF_L2_FRM_TYPE, + VCAP_KF_L2_PAYLOAD0, + VCAP_KF_L2_PAYLOAD1, + VCAP_KF_L2_PAYLOAD2, + VCAP_KF_L2_LLC, + VCAP_KF_L3_FRAGMENT, + VCAP_KF_L3_FRAG_OFS_GT0, + VCAP_KF_L2_SNAP, + VCAP_KF_L4_1588_DOM, + VCAP_KF_L4_1588_VER, + VCAP_KF_OAM_MEPID, + VCAP_KF_OAM_OPCODE, + VCAP_KF_OAM_VER, + VCAP_KF_OAM_FLAGS, + VCAP_KF_OAM_DETECTED, }; /* Actionset names with origin information */ enum vcap_actionfield_set { VCAP_AFS_NO_VALUE, /* initial value */ - VCAP_AFS_BASE_TYPE, /* sparx5 is2 X3, sparx5 es2 X3 */ + VCAP_AFS_BASE_TYPE, /* sparx5 is2 X3, sparx5 es2 X3, lan966x is2 X2 */ VCAP_AFS_CLASSIFICATION, /* sparx5 is0 X2 */ VCAP_AFS_CLASS_REDUCED, /* sparx5 is0 X1 */ VCAP_AFS_FULL, /* sparx5 is0 X3 */ VCAP_AFS_MLBS, /* sparx5 is0 X2 */ VCAP_AFS_MLBS_REDUCED, /* sparx5 is0 X1 */ + VCAP_AFS_SMAC_SIP, /* lan966x is2 x1 */ }; /* List of actionfields with description @@ -404,10 +465,10 @@ enum vcap_actionfield_set { * QSYS port number when FWD_MODE is redirect or copy * VCAP_AF_COPY_QUEUE_NUM: W16, sparx5: es2 * QSYS queue number when FWD_MODE is redirect or copy - * VCAP_AF_CPU_COPY_ENA: W1, sparx5: is2/es2 + * VCAP_AF_CPU_COPY_ENA: W1, sparx5: is2/es2, lan966x: is2 * Setting this bit to 1 causes all frames that hit this action to be copied to * the CPU extraction queue specified in CPU_QUEUE_NUM. - * VCAP_AF_CPU_QUEUE_NUM: W3, sparx5: is2/es2 + * VCAP_AF_CPU_QUEUE_NUM: W3, sparx5: is2/es2, lan966x: is2 * CPU queue number. Used when CPU_COPY_ENA is set. * VCAP_AF_DEI_ENA: W1, sparx5: is0 * If set, use DEI_VAL as classified DEI value. Otherwise, DEI from basic @@ -429,7 +490,7 @@ enum vcap_actionfield_set { * DMAC translation when entering or leaving a tunnel. * VCAP_AF_FWD_MODE: W2, sparx5: es2 * Forward selector: 0: Forward. 1: Discard. 2: Redirect. 3: Copy. - * VCAP_AF_HIT_ME_ONCE: W1, sparx5: is2/es2 + * VCAP_AF_HIT_ME_ONCE: W1, sparx5: is2/es2, lan966x: is2 * Setting this bit to 1 causes the first frame that hits this action where the * HIT_CNT counter is zero to be copied to the CPU extraction queue specified in * CPU_QUEUE_NUM. The HIT_CNT counter is then incremented and any frames that @@ -445,7 +506,7 @@ enum vcap_actionfield_set { * = ISDX_VAL. * VCAP_AF_ISDX_VAL: W12, sparx5: is0 * See isdx_add_replace_sel - * VCAP_AF_LRN_DIS: W1, sparx5: is2 + * VCAP_AF_LRN_DIS: W1, sparx5: is2, lan966x: is2 * Setting this bit to 1 disables learning of frames hitting this action. * VCAP_AF_MAP_IDX: W9, sparx5: is0 * Index for QoS mapping table lookup @@ -460,7 +521,7 @@ enum vcap_actionfield_set { * are applied to. 0: No changes to the QoS Mapping Table lookup. 1: Update key * type and index for QoS Mapping Table lookup #0. 2: Update key type and index * for QoS Mapping Table lookup #1. 3: Reserved. - * VCAP_AF_MASK_MODE: W3, sparx5: is0/is2 + * VCAP_AF_MASK_MODE: W3, sparx5: is0/is2, lan966x is2 W2 * Controls the PORT_MASK use. Sparx5: 0: OR_DSTMASK, 1: AND_VLANMASK, 2: * REPLACE_PGID, 3: REPLACE_ALL, 4: REDIR_PGID, 5: OR_PGID_MASK, 6: VSTAX, 7: * Not applicable. LAN966X: 0: No action, 1: Permit/deny (AND), 2: Policy @@ -497,15 +558,15 @@ enum vcap_actionfield_set { * PIPELINE_PT == NONE. Overrules previous settings of pipeline point. * VCAP_AF_PIPELINE_PT: W5, sparx5: is0/is2 * Pipeline point used if PIPELINE_FORCE_ENA is set - * VCAP_AF_POLICE_ENA: W1, sparx5: is2/es2 + * VCAP_AF_POLICE_ENA: W1, sparx5: is2/es2, lan966x: is2 * Setting this bit to 1 causes frames that hit this action to be policed by the * ACL policer specified in POLICE_IDX. Only applies to the first lookup. - * VCAP_AF_POLICE_IDX: W6, sparx5: is2/es2 + * VCAP_AF_POLICE_IDX: W6, sparx5: is2/es2, lan966x: is2 W9 * Selects VCAP policer used when policing frames (POLICE_ENA) * VCAP_AF_POLICE_REMARK: W1, sparx5: es2 * If set, frames exceeding policer rates are marked as yellow but not * discarded. - * VCAP_AF_PORT_MASK: sparx5 is0 W65, sparx5 is2 W68 + * VCAP_AF_PORT_MASK: sparx5 is0 W65, sparx5 is2 W68, lan966x is2 W8 * Port mask applied to the forwarding decision based on MASK_MODE. * VCAP_AF_QOS_ENA: W1, sparx5: is0 * If set, use QOS_VAL as classified QoS class. Otherwise, QoS class from basic @@ -519,6 +580,28 @@ enum vcap_actionfield_set { * Actionset type id - Set by the API * VCAP_AF_VID_VAL: W13, sparx5: is0 * New VID Value + * VCAP_AF_MIRROR_ENA: W1, lan966x: is2 + * Setting this bit to 1 causes frames to be mirrored to the mirror target + * port (ANA::MIRRPORPORTS). + * VCAP_AF_POLICE_VCAP_ONLY: W1, lan966x: is2 + * Disable policing from QoS, and port policers. Only the VCAP policer + * selected by POLICE_IDX is active. Only applies to the second lookup. + * VCAP_AF_REW_OP: W16, lan966x: is2 + * Rewriter operation command. + * VCAP_AF_ISDX_ENA: W1, lan966x: is2 + * Setting this bit to 1 causes the classified ISDX to be set to the value of + * POLICE_IDX[8:0]. + * VCAP_AF_ACL_ID: W6, lan966x: is2 + * Logical ID for the entry. This ID is extracted together with the frame in + * the CPU extraction header. Only applicable to actions with CPU_COPY_ENA or + * HIT_ME_ONCE set. + * VCAP_AF_FWD_KILL_ENA: W1, lan966x: is2 + * Setting this bit to 1 denies forwarding of the frame forwarding to any + * front port. The frame can still be copied to the CPU by other actions. + * VCAP_AF_HOST_MATCH: W1, lan966x: is2 + * Used for IP source guarding. If set, it signals that the host is a valid + * (for instance a valid combination of source MAC address and source IP + * address). HOST_MATCH is input to the IS2 keys. */ /* Actionfield names */ @@ -640,6 +723,13 @@ enum vcap_action_field { VCAP_AF_VLAN_PUSH_CNT, VCAP_AF_VLAN_PUSH_CNT_ENA, VCAP_AF_VLAN_WAS_TAGGED, + VCAP_AF_MIRROR_ENA, + VCAP_AF_POLICE_VCAP_ONLY, + VCAP_AF_REW_OP, + VCAP_AF_ISDX_ENA, + VCAP_AF_ACL_ID, + VCAP_AF_FWD_KILL_ENA, + VCAP_AF_HOST_MATCH, }; #endif /* __VCAP_AG_API__ */ From b053122532d7aad88e4424f9e483fc2ad5b4cee0 Mon Sep 17 00:00:00 2001 From: Horatiu Vultur Date: Fri, 25 Nov 2022 10:50:04 +0100 Subject: [PATCH 1291/2002] net: lan966x: Add initial VCAP When lan966x driver is initialized, initialize also the VCAP module for lan966x. Signed-off-by: Horatiu Vultur Signed-off-by: Paolo Abeni --- .../net/ethernet/microchip/lan966x/Kconfig | 1 + .../net/ethernet/microchip/lan966x/Makefile | 5 +++- .../ethernet/microchip/lan966x/lan966x_main.c | 8 ++++++ .../ethernet/microchip/lan966x/lan966x_main.h | 6 ++++ .../microchip/lan966x/lan966x_vcap_impl.c | 28 +++++++++++++++++++ 5 files changed, 47 insertions(+), 1 deletion(-) create mode 100644 drivers/net/ethernet/microchip/lan966x/lan966x_vcap_impl.c diff --git a/drivers/net/ethernet/microchip/lan966x/Kconfig b/drivers/net/ethernet/microchip/lan966x/Kconfig index b7ae5ce7d3f7..8bcd60f17d6d 100644 --- a/drivers/net/ethernet/microchip/lan966x/Kconfig +++ b/drivers/net/ethernet/microchip/lan966x/Kconfig @@ -8,5 +8,6 @@ config LAN966X_SWITCH select PHYLINK select PACKING select PAGE_POOL + select VCAP help This driver supports the Lan966x network switch device. diff --git a/drivers/net/ethernet/microchip/lan966x/Makefile b/drivers/net/ethernet/microchip/lan966x/Makefile index 251a7d561d63..2c2778485901 100644 --- a/drivers/net/ethernet/microchip/lan966x/Makefile +++ b/drivers/net/ethernet/microchip/lan966x/Makefile @@ -12,4 +12,7 @@ lan966x-switch-objs := lan966x_main.o lan966x_phylink.o lan966x_port.o \ lan966x_tc.o lan966x_mqprio.o lan966x_taprio.o \ lan966x_tbf.o lan966x_cbs.o lan966x_ets.o \ lan966x_tc_matchall.o lan966x_police.o lan966x_mirror.o \ - lan966x_xdp.o + lan966x_xdp.o lan966x_vcap_impl.o + +# Provide include files +ccflags-y += -I$(srctree)/drivers/net/ethernet/microchip/vcap diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_main.c b/drivers/net/ethernet/microchip/lan966x/lan966x_main.c index 0aed244826d3..24d7d7dca1df 100644 --- a/drivers/net/ethernet/microchip/lan966x/lan966x_main.c +++ b/drivers/net/ethernet/microchip/lan966x/lan966x_main.c @@ -1157,8 +1157,15 @@ static int lan966x_probe(struct platform_device *pdev) if (err) goto cleanup_ptp; + err = lan966x_vcap_init(lan966x); + if (err) + goto cleanup_fdma; + return 0; +cleanup_fdma: + lan966x_fdma_deinit(lan966x); + cleanup_ptp: lan966x_ptp_deinit(lan966x); @@ -1182,6 +1189,7 @@ static int lan966x_remove(struct platform_device *pdev) struct lan966x *lan966x = platform_get_drvdata(pdev); lan966x_taprio_deinit(lan966x); + lan966x_vcap_deinit(lan966x); lan966x_fdma_deinit(lan966x); lan966x_cleanup_ports(lan966x); diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_main.h b/drivers/net/ethernet/microchip/lan966x/lan966x_main.h index a0170a3fb976..1b3fa2751bcd 100644 --- a/drivers/net/ethernet/microchip/lan966x/lan966x_main.h +++ b/drivers/net/ethernet/microchip/lan966x/lan966x_main.h @@ -300,6 +300,9 @@ struct lan966x { struct lan966x_port *mirror_monitor; u32 mirror_mask[2]; u32 mirror_count; + + /* vcap */ + struct vcap_control *vcap_ctrl; }; struct lan966x_port_config { @@ -582,6 +585,9 @@ static inline bool lan966x_xdp_port_present(struct lan966x_port *port) return !!port->xdp_prog; } +int lan966x_vcap_init(struct lan966x *lan966x); +void lan966x_vcap_deinit(struct lan966x *lan966x); + static inline void __iomem *lan_addr(void __iomem *base[], int id, int tinst, int tcnt, int gbase, int ginst, diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_vcap_impl.c b/drivers/net/ethernet/microchip/lan966x/lan966x_vcap_impl.c new file mode 100644 index 000000000000..8d89cfcb8502 --- /dev/null +++ b/drivers/net/ethernet/microchip/lan966x/lan966x_vcap_impl.c @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: GPL-2.0+ + +#include "lan966x_main.h" +#include "vcap_api.h" + +int lan966x_vcap_init(struct lan966x *lan966x) +{ + struct vcap_control *ctrl; + + ctrl = kzalloc(sizeof(*ctrl), GFP_KERNEL); + if (!ctrl) + return -ENOMEM; + + lan966x->vcap_ctrl = ctrl; + + return 0; +} + +void lan966x_vcap_deinit(struct lan966x *lan966x) +{ + struct vcap_control *ctrl; + + ctrl = lan966x->vcap_ctrl; + if (!ctrl) + return; + + kfree(ctrl); +} From 39bedc169cff3f9320ba58339e82eec85cee140d Mon Sep 17 00:00:00 2001 From: Horatiu Vultur Date: Fri, 25 Nov 2022 10:50:05 +0100 Subject: [PATCH 1292/2002] net: lan966x: Add is2 vcap model to vcap API. This provides the lan966x is2 model and adds it to the vcap control instance that will be provided to the vcap API. Signed-off-by: Horatiu Vultur Signed-off-by: Paolo Abeni --- .../net/ethernet/microchip/lan966x/Makefile | 2 +- .../microchip/lan966x/lan966x_vcap_ag_api.c | 1608 +++++++++++++++++ .../microchip/lan966x/lan966x_vcap_ag_api.h | 11 + .../microchip/lan966x/lan966x_vcap_impl.c | 4 + 4 files changed, 1624 insertions(+), 1 deletion(-) create mode 100644 drivers/net/ethernet/microchip/lan966x/lan966x_vcap_ag_api.c create mode 100644 drivers/net/ethernet/microchip/lan966x/lan966x_vcap_ag_api.h diff --git a/drivers/net/ethernet/microchip/lan966x/Makefile b/drivers/net/ethernet/microchip/lan966x/Makefile index 2c2778485901..053a174f09f3 100644 --- a/drivers/net/ethernet/microchip/lan966x/Makefile +++ b/drivers/net/ethernet/microchip/lan966x/Makefile @@ -12,7 +12,7 @@ lan966x-switch-objs := lan966x_main.o lan966x_phylink.o lan966x_port.o \ lan966x_tc.o lan966x_mqprio.o lan966x_taprio.o \ lan966x_tbf.o lan966x_cbs.o lan966x_ets.o \ lan966x_tc_matchall.o lan966x_police.o lan966x_mirror.o \ - lan966x_xdp.o lan966x_vcap_impl.o + lan966x_xdp.o lan966x_vcap_impl.o lan966x_vcap_ag_api.o # Provide include files ccflags-y += -I$(srctree)/drivers/net/ethernet/microchip/vcap diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_vcap_ag_api.c b/drivers/net/ethernet/microchip/lan966x/lan966x_vcap_ag_api.c new file mode 100644 index 000000000000..928e711960e6 --- /dev/null +++ b/drivers/net/ethernet/microchip/lan966x/lan966x_vcap_ag_api.c @@ -0,0 +1,1608 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MIT) + +#include +#include + +#include "lan966x_vcap_ag_api.h" + +/* keyfields */ +static const struct vcap_field is2_mac_etype_keyfield[] = { + [VCAP_KF_TYPE] = { + .type = VCAP_FIELD_U32, + .offset = 0, + .width = 4, + }, + [VCAP_KF_LOOKUP_FIRST_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 4, + .width = 1, + }, + [VCAP_KF_LOOKUP_PAG] = { + .type = VCAP_FIELD_U32, + .offset = 5, + .width = 8, + }, + [VCAP_KF_IF_IGR_PORT_MASK] = { + .type = VCAP_FIELD_U32, + .offset = 13, + .width = 9, + }, + [VCAP_KF_ISDX_GT0_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 22, + .width = 1, + }, + [VCAP_KF_HOST_MATCH] = { + .type = VCAP_FIELD_BIT, + .offset = 23, + .width = 1, + }, + [VCAP_KF_L2_MC_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 24, + .width = 1, + }, + [VCAP_KF_L2_BC_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 25, + .width = 1, + }, + [VCAP_KF_8021Q_VLAN_TAGGED_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 26, + .width = 1, + }, + [VCAP_KF_8021Q_VID_CLS] = { + .type = VCAP_FIELD_U32, + .offset = 27, + .width = 12, + }, + [VCAP_KF_8021Q_DEI_CLS] = { + .type = VCAP_FIELD_BIT, + .offset = 39, + .width = 1, + }, + [VCAP_KF_8021Q_PCP_CLS] = { + .type = VCAP_FIELD_U32, + .offset = 40, + .width = 3, + }, + [VCAP_KF_L2_DMAC] = { + .type = VCAP_FIELD_U48, + .offset = 43, + .width = 48, + }, + [VCAP_KF_L2_SMAC] = { + .type = VCAP_FIELD_U48, + .offset = 91, + .width = 48, + }, + [VCAP_KF_ETYPE] = { + .type = VCAP_FIELD_U32, + .offset = 139, + .width = 16, + }, + [VCAP_KF_L2_FRM_TYPE] = { + .type = VCAP_FIELD_U32, + .offset = 155, + .width = 4, + }, + [VCAP_KF_L2_PAYLOAD0] = { + .type = VCAP_FIELD_U32, + .offset = 159, + .width = 16, + }, + [VCAP_KF_L2_PAYLOAD1] = { + .type = VCAP_FIELD_U32, + .offset = 175, + .width = 8, + }, + [VCAP_KF_L2_PAYLOAD2] = { + .type = VCAP_FIELD_U32, + .offset = 183, + .width = 3, + }, +}; + +static const struct vcap_field is2_mac_llc_keyfield[] = { + [VCAP_KF_TYPE] = { + .type = VCAP_FIELD_U32, + .offset = 0, + .width = 4, + }, + [VCAP_KF_LOOKUP_FIRST_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 4, + .width = 1, + }, + [VCAP_KF_LOOKUP_PAG] = { + .type = VCAP_FIELD_U32, + .offset = 5, + .width = 8, + }, + [VCAP_KF_IF_IGR_PORT_MASK] = { + .type = VCAP_FIELD_U32, + .offset = 13, + .width = 9, + }, + [VCAP_KF_ISDX_GT0_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 22, + .width = 1, + }, + [VCAP_KF_HOST_MATCH] = { + .type = VCAP_FIELD_BIT, + .offset = 23, + .width = 1, + }, + [VCAP_KF_L2_MC_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 24, + .width = 1, + }, + [VCAP_KF_L2_BC_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 25, + .width = 1, + }, + [VCAP_KF_8021Q_VLAN_TAGGED_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 26, + .width = 1, + }, + [VCAP_KF_8021Q_VID_CLS] = { + .type = VCAP_FIELD_U32, + .offset = 27, + .width = 12, + }, + [VCAP_KF_8021Q_DEI_CLS] = { + .type = VCAP_FIELD_BIT, + .offset = 39, + .width = 1, + }, + [VCAP_KF_8021Q_PCP_CLS] = { + .type = VCAP_FIELD_U32, + .offset = 40, + .width = 3, + }, + [VCAP_KF_L2_DMAC] = { + .type = VCAP_FIELD_U48, + .offset = 43, + .width = 48, + }, + [VCAP_KF_L2_SMAC] = { + .type = VCAP_FIELD_U48, + .offset = 91, + .width = 48, + }, + [VCAP_KF_L2_LLC] = { + .type = VCAP_FIELD_U48, + .offset = 139, + .width = 40, + }, +}; + +static const struct vcap_field is2_mac_snap_keyfield[] = { + [VCAP_KF_TYPE] = { + .type = VCAP_FIELD_U32, + .offset = 0, + .width = 4, + }, + [VCAP_KF_LOOKUP_FIRST_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 4, + .width = 1, + }, + [VCAP_KF_LOOKUP_PAG] = { + .type = VCAP_FIELD_U32, + .offset = 5, + .width = 8, + }, + [VCAP_KF_IF_IGR_PORT_MASK] = { + .type = VCAP_FIELD_U32, + .offset = 13, + .width = 9, + }, + [VCAP_KF_ISDX_GT0_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 22, + .width = 1, + }, + [VCAP_KF_HOST_MATCH] = { + .type = VCAP_FIELD_BIT, + .offset = 23, + .width = 1, + }, + [VCAP_KF_L2_MC_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 24, + .width = 1, + }, + [VCAP_KF_L2_BC_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 25, + .width = 1, + }, + [VCAP_KF_8021Q_VLAN_TAGGED_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 26, + .width = 1, + }, + [VCAP_KF_8021Q_VID_CLS] = { + .type = VCAP_FIELD_U32, + .offset = 27, + .width = 12, + }, + [VCAP_KF_8021Q_DEI_CLS] = { + .type = VCAP_FIELD_BIT, + .offset = 39, + .width = 1, + }, + [VCAP_KF_8021Q_PCP_CLS] = { + .type = VCAP_FIELD_U32, + .offset = 40, + .width = 3, + }, + [VCAP_KF_L2_DMAC] = { + .type = VCAP_FIELD_U48, + .offset = 43, + .width = 48, + }, + [VCAP_KF_L2_SMAC] = { + .type = VCAP_FIELD_U48, + .offset = 91, + .width = 48, + }, + [VCAP_KF_L2_SNAP] = { + .type = VCAP_FIELD_U48, + .offset = 139, + .width = 40, + }, +}; + +static const struct vcap_field is2_arp_keyfield[] = { + [VCAP_KF_TYPE] = { + .type = VCAP_FIELD_U32, + .offset = 0, + .width = 4, + }, + [VCAP_KF_LOOKUP_FIRST_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 4, + .width = 1, + }, + [VCAP_KF_LOOKUP_PAG] = { + .type = VCAP_FIELD_U32, + .offset = 5, + .width = 8, + }, + [VCAP_KF_IF_IGR_PORT_MASK] = { + .type = VCAP_FIELD_U32, + .offset = 13, + .width = 9, + }, + [VCAP_KF_ISDX_GT0_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 22, + .width = 1, + }, + [VCAP_KF_HOST_MATCH] = { + .type = VCAP_FIELD_BIT, + .offset = 23, + .width = 1, + }, + [VCAP_KF_L2_MC_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 24, + .width = 1, + }, + [VCAP_KF_L2_BC_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 25, + .width = 1, + }, + [VCAP_KF_8021Q_VLAN_TAGGED_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 26, + .width = 1, + }, + [VCAP_KF_8021Q_VID_CLS] = { + .type = VCAP_FIELD_U32, + .offset = 27, + .width = 12, + }, + [VCAP_KF_8021Q_DEI_CLS] = { + .type = VCAP_FIELD_BIT, + .offset = 39, + .width = 1, + }, + [VCAP_KF_8021Q_PCP_CLS] = { + .type = VCAP_FIELD_U32, + .offset = 40, + .width = 3, + }, + [VCAP_KF_L2_SMAC] = { + .type = VCAP_FIELD_U48, + .offset = 43, + .width = 48, + }, + [VCAP_KF_ARP_ADDR_SPACE_OK_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 91, + .width = 1, + }, + [VCAP_KF_ARP_PROTO_SPACE_OK_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 92, + .width = 1, + }, + [VCAP_KF_ARP_LEN_OK_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 93, + .width = 1, + }, + [VCAP_KF_ARP_TGT_MATCH_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 94, + .width = 1, + }, + [VCAP_KF_ARP_SENDER_MATCH_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 95, + .width = 1, + }, + [VCAP_KF_ARP_OPCODE_UNKNOWN_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 96, + .width = 1, + }, + [VCAP_KF_ARP_OPCODE] = { + .type = VCAP_FIELD_U32, + .offset = 97, + .width = 2, + }, + [VCAP_KF_L3_IP4_DIP] = { + .type = VCAP_FIELD_U32, + .offset = 99, + .width = 32, + }, + [VCAP_KF_L3_IP4_SIP] = { + .type = VCAP_FIELD_U32, + .offset = 131, + .width = 32, + }, + [VCAP_KF_L3_DIP_EQ_SIP_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 163, + .width = 1, + }, +}; + +static const struct vcap_field is2_ip4_tcp_udp_keyfield[] = { + [VCAP_KF_TYPE] = { + .type = VCAP_FIELD_U32, + .offset = 0, + .width = 4, + }, + [VCAP_KF_LOOKUP_FIRST_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 4, + .width = 1, + }, + [VCAP_KF_LOOKUP_PAG] = { + .type = VCAP_FIELD_U32, + .offset = 5, + .width = 8, + }, + [VCAP_KF_IF_IGR_PORT_MASK] = { + .type = VCAP_FIELD_U32, + .offset = 13, + .width = 9, + }, + [VCAP_KF_ISDX_GT0_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 22, + .width = 1, + }, + [VCAP_KF_HOST_MATCH] = { + .type = VCAP_FIELD_BIT, + .offset = 23, + .width = 1, + }, + [VCAP_KF_L2_MC_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 24, + .width = 1, + }, + [VCAP_KF_L2_BC_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 25, + .width = 1, + }, + [VCAP_KF_8021Q_VLAN_TAGGED_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 26, + .width = 1, + }, + [VCAP_KF_8021Q_VID_CLS] = { + .type = VCAP_FIELD_U32, + .offset = 27, + .width = 12, + }, + [VCAP_KF_8021Q_DEI_CLS] = { + .type = VCAP_FIELD_BIT, + .offset = 39, + .width = 1, + }, + [VCAP_KF_8021Q_PCP_CLS] = { + .type = VCAP_FIELD_U32, + .offset = 40, + .width = 3, + }, + [VCAP_KF_IP4_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 43, + .width = 1, + }, + [VCAP_KF_L3_FRAGMENT] = { + .type = VCAP_FIELD_BIT, + .offset = 44, + .width = 1, + }, + [VCAP_KF_L3_FRAG_OFS_GT0] = { + .type = VCAP_FIELD_BIT, + .offset = 45, + .width = 1, + }, + [VCAP_KF_L3_OPTIONS_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 46, + .width = 1, + }, + [VCAP_KF_L3_TTL_GT0] = { + .type = VCAP_FIELD_BIT, + .offset = 47, + .width = 1, + }, + [VCAP_KF_L3_TOS] = { + .type = VCAP_FIELD_U32, + .offset = 48, + .width = 8, + }, + [VCAP_KF_L3_IP4_DIP] = { + .type = VCAP_FIELD_U32, + .offset = 56, + .width = 32, + }, + [VCAP_KF_L3_IP4_SIP] = { + .type = VCAP_FIELD_U32, + .offset = 88, + .width = 32, + }, + [VCAP_KF_L3_DIP_EQ_SIP_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 120, + .width = 1, + }, + [VCAP_KF_TCP_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 121, + .width = 1, + }, + [VCAP_KF_L4_DPORT] = { + .type = VCAP_FIELD_U32, + .offset = 122, + .width = 16, + }, + [VCAP_KF_L4_SPORT] = { + .type = VCAP_FIELD_U32, + .offset = 138, + .width = 16, + }, + [VCAP_KF_L4_RNG] = { + .type = VCAP_FIELD_U32, + .offset = 154, + .width = 8, + }, + [VCAP_KF_L4_SPORT_EQ_DPORT_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 162, + .width = 1, + }, + [VCAP_KF_L4_SEQUENCE_EQ0_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 163, + .width = 1, + }, + [VCAP_KF_L4_FIN] = { + .type = VCAP_FIELD_BIT, + .offset = 164, + .width = 1, + }, + [VCAP_KF_L4_SYN] = { + .type = VCAP_FIELD_BIT, + .offset = 165, + .width = 1, + }, + [VCAP_KF_L4_RST] = { + .type = VCAP_FIELD_BIT, + .offset = 166, + .width = 1, + }, + [VCAP_KF_L4_PSH] = { + .type = VCAP_FIELD_BIT, + .offset = 167, + .width = 1, + }, + [VCAP_KF_L4_ACK] = { + .type = VCAP_FIELD_BIT, + .offset = 168, + .width = 1, + }, + [VCAP_KF_L4_URG] = { + .type = VCAP_FIELD_BIT, + .offset = 169, + .width = 1, + }, + [VCAP_KF_L4_1588_DOM] = { + .type = VCAP_FIELD_U32, + .offset = 170, + .width = 8, + }, + [VCAP_KF_L4_1588_VER] = { + .type = VCAP_FIELD_U32, + .offset = 178, + .width = 4, + }, +}; + +static const struct vcap_field is2_ip4_other_keyfield[] = { + [VCAP_KF_TYPE] = { + .type = VCAP_FIELD_U32, + .offset = 0, + .width = 4, + }, + [VCAP_KF_LOOKUP_FIRST_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 4, + .width = 1, + }, + [VCAP_KF_LOOKUP_PAG] = { + .type = VCAP_FIELD_U32, + .offset = 5, + .width = 8, + }, + [VCAP_KF_IF_IGR_PORT_MASK] = { + .type = VCAP_FIELD_U32, + .offset = 13, + .width = 9, + }, + [VCAP_KF_ISDX_GT0_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 22, + .width = 1, + }, + [VCAP_KF_HOST_MATCH] = { + .type = VCAP_FIELD_BIT, + .offset = 23, + .width = 1, + }, + [VCAP_KF_L2_MC_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 24, + .width = 1, + }, + [VCAP_KF_L2_BC_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 25, + .width = 1, + }, + [VCAP_KF_8021Q_VLAN_TAGGED_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 26, + .width = 1, + }, + [VCAP_KF_8021Q_VID_CLS] = { + .type = VCAP_FIELD_U32, + .offset = 27, + .width = 12, + }, + [VCAP_KF_8021Q_DEI_CLS] = { + .type = VCAP_FIELD_BIT, + .offset = 39, + .width = 1, + }, + [VCAP_KF_8021Q_PCP_CLS] = { + .type = VCAP_FIELD_U32, + .offset = 40, + .width = 3, + }, + [VCAP_KF_IP4_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 43, + .width = 1, + }, + [VCAP_KF_L3_FRAGMENT] = { + .type = VCAP_FIELD_BIT, + .offset = 44, + .width = 1, + }, + [VCAP_KF_L3_FRAG_OFS_GT0] = { + .type = VCAP_FIELD_BIT, + .offset = 45, + .width = 1, + }, + [VCAP_KF_L3_OPTIONS_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 46, + .width = 1, + }, + [VCAP_KF_L3_TTL_GT0] = { + .type = VCAP_FIELD_BIT, + .offset = 47, + .width = 1, + }, + [VCAP_KF_L3_TOS] = { + .type = VCAP_FIELD_U32, + .offset = 48, + .width = 8, + }, + [VCAP_KF_L3_IP4_DIP] = { + .type = VCAP_FIELD_U32, + .offset = 56, + .width = 32, + }, + [VCAP_KF_L3_IP4_SIP] = { + .type = VCAP_FIELD_U32, + .offset = 88, + .width = 32, + }, + [VCAP_KF_L3_DIP_EQ_SIP_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 120, + .width = 1, + }, + [VCAP_KF_L3_IP_PROTO] = { + .type = VCAP_FIELD_U32, + .offset = 121, + .width = 8, + }, + [VCAP_KF_L3_PAYLOAD] = { + .type = VCAP_FIELD_U56, + .offset = 129, + .width = 56, + }, +}; + +static const struct vcap_field is2_ip6_std_keyfield[] = { + [VCAP_KF_TYPE] = { + .type = VCAP_FIELD_U32, + .offset = 0, + .width = 4, + }, + [VCAP_KF_LOOKUP_FIRST_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 4, + .width = 1, + }, + [VCAP_KF_LOOKUP_PAG] = { + .type = VCAP_FIELD_U32, + .offset = 5, + .width = 8, + }, + [VCAP_KF_IF_IGR_PORT_MASK] = { + .type = VCAP_FIELD_U32, + .offset = 13, + .width = 9, + }, + [VCAP_KF_ISDX_GT0_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 22, + .width = 1, + }, + [VCAP_KF_HOST_MATCH] = { + .type = VCAP_FIELD_BIT, + .offset = 23, + .width = 1, + }, + [VCAP_KF_L2_MC_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 24, + .width = 1, + }, + [VCAP_KF_L2_BC_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 25, + .width = 1, + }, + [VCAP_KF_8021Q_VLAN_TAGGED_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 26, + .width = 1, + }, + [VCAP_KF_8021Q_VID_CLS] = { + .type = VCAP_FIELD_U32, + .offset = 27, + .width = 12, + }, + [VCAP_KF_8021Q_DEI_CLS] = { + .type = VCAP_FIELD_BIT, + .offset = 39, + .width = 1, + }, + [VCAP_KF_8021Q_PCP_CLS] = { + .type = VCAP_FIELD_U32, + .offset = 40, + .width = 3, + }, + [VCAP_KF_L3_TTL_GT0] = { + .type = VCAP_FIELD_BIT, + .offset = 43, + .width = 1, + }, + [VCAP_KF_L3_IP6_SIP] = { + .type = VCAP_FIELD_U128, + .offset = 44, + .width = 128, + }, + [VCAP_KF_L3_IP_PROTO] = { + .type = VCAP_FIELD_U32, + .offset = 172, + .width = 8, + }, +}; + +static const struct vcap_field is2_oam_keyfield[] = { + [VCAP_KF_TYPE] = { + .type = VCAP_FIELD_U32, + .offset = 0, + .width = 4, + }, + [VCAP_KF_LOOKUP_FIRST_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 4, + .width = 1, + }, + [VCAP_KF_LOOKUP_PAG] = { + .type = VCAP_FIELD_U32, + .offset = 5, + .width = 8, + }, + [VCAP_KF_IF_IGR_PORT_MASK] = { + .type = VCAP_FIELD_U32, + .offset = 13, + .width = 9, + }, + [VCAP_KF_ISDX_GT0_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 22, + .width = 1, + }, + [VCAP_KF_HOST_MATCH] = { + .type = VCAP_FIELD_BIT, + .offset = 23, + .width = 1, + }, + [VCAP_KF_L2_MC_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 24, + .width = 1, + }, + [VCAP_KF_L2_BC_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 25, + .width = 1, + }, + [VCAP_KF_8021Q_VLAN_TAGGED_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 26, + .width = 1, + }, + [VCAP_KF_8021Q_VID_CLS] = { + .type = VCAP_FIELD_U32, + .offset = 27, + .width = 12, + }, + [VCAP_KF_8021Q_DEI_CLS] = { + .type = VCAP_FIELD_BIT, + .offset = 39, + .width = 1, + }, + [VCAP_KF_8021Q_PCP_CLS] = { + .type = VCAP_FIELD_U32, + .offset = 40, + .width = 3, + }, + [VCAP_KF_L2_DMAC] = { + .type = VCAP_FIELD_U48, + .offset = 43, + .width = 48, + }, + [VCAP_KF_L2_SMAC] = { + .type = VCAP_FIELD_U48, + .offset = 91, + .width = 48, + }, + [VCAP_KF_OAM_MEL_FLAGS] = { + .type = VCAP_FIELD_U32, + .offset = 139, + .width = 7, + }, + [VCAP_KF_OAM_VER] = { + .type = VCAP_FIELD_U32, + .offset = 146, + .width = 5, + }, + [VCAP_KF_OAM_OPCODE] = { + .type = VCAP_FIELD_U32, + .offset = 151, + .width = 8, + }, + [VCAP_KF_OAM_FLAGS] = { + .type = VCAP_FIELD_U32, + .offset = 159, + .width = 8, + }, + [VCAP_KF_OAM_MEPID] = { + .type = VCAP_FIELD_U32, + .offset = 167, + .width = 16, + }, + [VCAP_KF_OAM_CCM_CNTS_EQ0] = { + .type = VCAP_FIELD_BIT, + .offset = 183, + .width = 1, + }, + [VCAP_KF_OAM_Y1731_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 184, + .width = 1, + }, + [VCAP_KF_OAM_DETECTED] = { + .type = VCAP_FIELD_BIT, + .offset = 185, + .width = 1, + }, +}; + +static const struct vcap_field is2_ip6_tcp_udp_keyfield[] = { + [VCAP_KF_TYPE] = { + .type = VCAP_FIELD_U32, + .offset = 0, + .width = 2, + }, + [VCAP_KF_LOOKUP_FIRST_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 2, + .width = 1, + }, + [VCAP_KF_LOOKUP_PAG] = { + .type = VCAP_FIELD_U32, + .offset = 3, + .width = 8, + }, + [VCAP_KF_IF_IGR_PORT_MASK] = { + .type = VCAP_FIELD_U32, + .offset = 11, + .width = 9, + }, + [VCAP_KF_ISDX_GT0_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 20, + .width = 1, + }, + [VCAP_KF_HOST_MATCH] = { + .type = VCAP_FIELD_BIT, + .offset = 21, + .width = 1, + }, + [VCAP_KF_L2_MC_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 22, + .width = 1, + }, + [VCAP_KF_L2_BC_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 23, + .width = 1, + }, + [VCAP_KF_8021Q_VLAN_TAGGED_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 24, + .width = 1, + }, + [VCAP_KF_8021Q_VID_CLS] = { + .type = VCAP_FIELD_U32, + .offset = 25, + .width = 12, + }, + [VCAP_KF_8021Q_DEI_CLS] = { + .type = VCAP_FIELD_BIT, + .offset = 37, + .width = 1, + }, + [VCAP_KF_8021Q_PCP_CLS] = { + .type = VCAP_FIELD_U32, + .offset = 38, + .width = 3, + }, + [VCAP_KF_L3_TTL_GT0] = { + .type = VCAP_FIELD_BIT, + .offset = 41, + .width = 1, + }, + [VCAP_KF_L3_TOS] = { + .type = VCAP_FIELD_U32, + .offset = 42, + .width = 8, + }, + [VCAP_KF_L3_IP6_DIP] = { + .type = VCAP_FIELD_U128, + .offset = 50, + .width = 128, + }, + [VCAP_KF_L3_IP6_SIP] = { + .type = VCAP_FIELD_U128, + .offset = 178, + .width = 128, + }, + [VCAP_KF_L3_DIP_EQ_SIP_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 306, + .width = 1, + }, + [VCAP_KF_TCP_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 307, + .width = 1, + }, + [VCAP_KF_L4_DPORT] = { + .type = VCAP_FIELD_U32, + .offset = 308, + .width = 16, + }, + [VCAP_KF_L4_SPORT] = { + .type = VCAP_FIELD_U32, + .offset = 324, + .width = 16, + }, + [VCAP_KF_L4_RNG] = { + .type = VCAP_FIELD_U32, + .offset = 340, + .width = 8, + }, + [VCAP_KF_L4_SPORT_EQ_DPORT_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 348, + .width = 1, + }, + [VCAP_KF_L4_SEQUENCE_EQ0_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 349, + .width = 1, + }, + [VCAP_KF_L4_FIN] = { + .type = VCAP_FIELD_BIT, + .offset = 350, + .width = 1, + }, + [VCAP_KF_L4_SYN] = { + .type = VCAP_FIELD_BIT, + .offset = 351, + .width = 1, + }, + [VCAP_KF_L4_RST] = { + .type = VCAP_FIELD_BIT, + .offset = 352, + .width = 1, + }, + [VCAP_KF_L4_PSH] = { + .type = VCAP_FIELD_BIT, + .offset = 353, + .width = 1, + }, + [VCAP_KF_L4_ACK] = { + .type = VCAP_FIELD_BIT, + .offset = 354, + .width = 1, + }, + [VCAP_KF_L4_URG] = { + .type = VCAP_FIELD_BIT, + .offset = 355, + .width = 1, + }, + [VCAP_KF_L4_1588_DOM] = { + .type = VCAP_FIELD_U32, + .offset = 356, + .width = 8, + }, + [VCAP_KF_L4_1588_VER] = { + .type = VCAP_FIELD_U32, + .offset = 364, + .width = 4, + }, +}; + +static const struct vcap_field is2_ip6_other_keyfield[] = { + [VCAP_KF_TYPE] = { + .type = VCAP_FIELD_U32, + .offset = 0, + .width = 2, + }, + [VCAP_KF_LOOKUP_FIRST_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 2, + .width = 1, + }, + [VCAP_KF_LOOKUP_PAG] = { + .type = VCAP_FIELD_U32, + .offset = 3, + .width = 8, + }, + [VCAP_KF_IF_IGR_PORT_MASK] = { + .type = VCAP_FIELD_U32, + .offset = 11, + .width = 9, + }, + [VCAP_KF_ISDX_GT0_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 20, + .width = 1, + }, + [VCAP_KF_HOST_MATCH] = { + .type = VCAP_FIELD_BIT, + .offset = 21, + .width = 1, + }, + [VCAP_KF_L2_MC_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 22, + .width = 1, + }, + [VCAP_KF_L2_BC_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 23, + .width = 1, + }, + [VCAP_KF_8021Q_VLAN_TAGGED_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 24, + .width = 1, + }, + [VCAP_KF_8021Q_VID_CLS] = { + .type = VCAP_FIELD_U32, + .offset = 25, + .width = 12, + }, + [VCAP_KF_8021Q_DEI_CLS] = { + .type = VCAP_FIELD_BIT, + .offset = 37, + .width = 1, + }, + [VCAP_KF_8021Q_PCP_CLS] = { + .type = VCAP_FIELD_U32, + .offset = 38, + .width = 3, + }, + [VCAP_KF_L3_TTL_GT0] = { + .type = VCAP_FIELD_BIT, + .offset = 41, + .width = 1, + }, + [VCAP_KF_L3_TOS] = { + .type = VCAP_FIELD_U32, + .offset = 42, + .width = 8, + }, + [VCAP_KF_L3_IP6_DIP] = { + .type = VCAP_FIELD_U128, + .offset = 50, + .width = 128, + }, + [VCAP_KF_L3_IP6_SIP] = { + .type = VCAP_FIELD_U128, + .offset = 178, + .width = 128, + }, + [VCAP_KF_L3_DIP_EQ_SIP_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 306, + .width = 1, + }, + [VCAP_KF_L3_IP_PROTO] = { + .type = VCAP_FIELD_U32, + .offset = 307, + .width = 8, + }, + [VCAP_KF_L3_PAYLOAD] = { + .type = VCAP_FIELD_U56, + .offset = 315, + .width = 56, + }, +}; + +static const struct vcap_field is2_smac_sip4_keyfield[] = { + [VCAP_KF_IF_IGR_PORT] = { + .type = VCAP_FIELD_U32, + .offset = 0, + .width = 4, + }, + [VCAP_KF_L2_SMAC] = { + .type = VCAP_FIELD_U48, + .offset = 4, + .width = 48, + }, + [VCAP_KF_L3_IP4_SIP] = { + .type = VCAP_FIELD_U32, + .offset = 52, + .width = 32, + }, +}; + +static const struct vcap_field is2_smac_sip6_keyfield[] = { + [VCAP_KF_TYPE] = { + .type = VCAP_FIELD_U32, + .offset = 0, + .width = 4, + }, + [VCAP_KF_IF_IGR_PORT] = { + .type = VCAP_FIELD_U32, + .offset = 4, + .width = 4, + }, + [VCAP_KF_L2_SMAC] = { + .type = VCAP_FIELD_U48, + .offset = 8, + .width = 48, + }, + [VCAP_KF_L3_IP6_SIP] = { + .type = VCAP_FIELD_U128, + .offset = 56, + .width = 128, + }, +}; + +/* keyfield_set */ +static const struct vcap_set is2_keyfield_set[] = { + [VCAP_KFS_MAC_ETYPE] = { + .type_id = 0, + .sw_per_item = 2, + .sw_cnt = 2, + }, + [VCAP_KFS_MAC_LLC] = { + .type_id = 1, + .sw_per_item = 2, + .sw_cnt = 2, + }, + [VCAP_KFS_MAC_SNAP] = { + .type_id = 2, + .sw_per_item = 2, + .sw_cnt = 2, + }, + [VCAP_KFS_ARP] = { + .type_id = 3, + .sw_per_item = 2, + .sw_cnt = 2, + }, + [VCAP_KFS_IP4_TCP_UDP] = { + .type_id = 4, + .sw_per_item = 2, + .sw_cnt = 2, + }, + [VCAP_KFS_IP4_OTHER] = { + .type_id = 5, + .sw_per_item = 2, + .sw_cnt = 2, + }, + [VCAP_KFS_IP6_STD] = { + .type_id = 6, + .sw_per_item = 2, + .sw_cnt = 2, + }, + [VCAP_KFS_OAM] = { + .type_id = 7, + .sw_per_item = 2, + .sw_cnt = 2, + }, + [VCAP_KFS_IP6_TCP_UDP] = { + .type_id = 0, + .sw_per_item = 4, + .sw_cnt = 1, + }, + [VCAP_KFS_IP6_OTHER] = { + .type_id = 1, + .sw_per_item = 4, + .sw_cnt = 1, + }, + [VCAP_KFS_SMAC_SIP4] = { + .type_id = -1, + .sw_per_item = 1, + .sw_cnt = 4, + }, + [VCAP_KFS_SMAC_SIP6] = { + .type_id = 8, + .sw_per_item = 2, + .sw_cnt = 2, + }, +}; + +/* keyfield_set map */ +static const struct vcap_field *is2_keyfield_set_map[] = { + [VCAP_KFS_MAC_ETYPE] = is2_mac_etype_keyfield, + [VCAP_KFS_MAC_LLC] = is2_mac_llc_keyfield, + [VCAP_KFS_MAC_SNAP] = is2_mac_snap_keyfield, + [VCAP_KFS_ARP] = is2_arp_keyfield, + [VCAP_KFS_IP4_TCP_UDP] = is2_ip4_tcp_udp_keyfield, + [VCAP_KFS_IP4_OTHER] = is2_ip4_other_keyfield, + [VCAP_KFS_IP6_STD] = is2_ip6_std_keyfield, + [VCAP_KFS_OAM] = is2_oam_keyfield, + [VCAP_KFS_IP6_TCP_UDP] = is2_ip6_tcp_udp_keyfield, + [VCAP_KFS_IP6_OTHER] = is2_ip6_other_keyfield, + [VCAP_KFS_SMAC_SIP4] = is2_smac_sip4_keyfield, + [VCAP_KFS_SMAC_SIP6] = is2_smac_sip6_keyfield, +}; + +/* keyfield_set map sizes */ +static int is2_keyfield_set_map_size[] = { + [VCAP_KFS_MAC_ETYPE] = ARRAY_SIZE(is2_mac_etype_keyfield), + [VCAP_KFS_MAC_LLC] = ARRAY_SIZE(is2_mac_llc_keyfield), + [VCAP_KFS_MAC_SNAP] = ARRAY_SIZE(is2_mac_snap_keyfield), + [VCAP_KFS_ARP] = ARRAY_SIZE(is2_arp_keyfield), + [VCAP_KFS_IP4_TCP_UDP] = ARRAY_SIZE(is2_ip4_tcp_udp_keyfield), + [VCAP_KFS_IP4_OTHER] = ARRAY_SIZE(is2_ip4_other_keyfield), + [VCAP_KFS_IP6_STD] = ARRAY_SIZE(is2_ip6_std_keyfield), + [VCAP_KFS_OAM] = ARRAY_SIZE(is2_oam_keyfield), + [VCAP_KFS_IP6_TCP_UDP] = ARRAY_SIZE(is2_ip6_tcp_udp_keyfield), + [VCAP_KFS_IP6_OTHER] = ARRAY_SIZE(is2_ip6_other_keyfield), + [VCAP_KFS_SMAC_SIP4] = ARRAY_SIZE(is2_smac_sip4_keyfield), + [VCAP_KFS_SMAC_SIP6] = ARRAY_SIZE(is2_smac_sip6_keyfield), +}; + +/* actionfields */ +static const struct vcap_field is2_base_type_actionfield[] = { + [VCAP_AF_HIT_ME_ONCE] = { + .type = VCAP_FIELD_BIT, + .offset = 0, + .width = 1, + }, + [VCAP_AF_CPU_COPY_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 1, + .width = 1, + }, + [VCAP_AF_CPU_QUEUE_NUM] = { + .type = VCAP_FIELD_U32, + .offset = 2, + .width = 3, + }, + [VCAP_AF_MASK_MODE] = { + .type = VCAP_FIELD_U32, + .offset = 5, + .width = 2, + }, + [VCAP_AF_MIRROR_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 7, + .width = 1, + }, + [VCAP_AF_LRN_DIS] = { + .type = VCAP_FIELD_BIT, + .offset = 8, + .width = 1, + }, + [VCAP_AF_POLICE_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 9, + .width = 1, + }, + [VCAP_AF_POLICE_IDX] = { + .type = VCAP_FIELD_U32, + .offset = 10, + .width = 9, + }, + [VCAP_AF_POLICE_VCAP_ONLY] = { + .type = VCAP_FIELD_BIT, + .offset = 19, + .width = 1, + }, + [VCAP_AF_PORT_MASK] = { + .type = VCAP_FIELD_U32, + .offset = 20, + .width = 8, + }, + [VCAP_AF_REW_OP] = { + .type = VCAP_FIELD_U32, + .offset = 28, + .width = 16, + }, + [VCAP_AF_ISDX_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 44, + .width = 1, + }, + [VCAP_AF_ACL_ID] = { + .type = VCAP_FIELD_U32, + .offset = 45, + .width = 6, + }, +}; + +static const struct vcap_field is2_smac_sip_actionfield[] = { + [VCAP_AF_CPU_COPY_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 0, + .width = 1, + }, + [VCAP_AF_CPU_QUEUE_NUM] = { + .type = VCAP_FIELD_U32, + .offset = 1, + .width = 3, + }, + [VCAP_AF_FWD_KILL_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 4, + .width = 1, + }, + [VCAP_AF_HOST_MATCH] = { + .type = VCAP_FIELD_BIT, + .offset = 5, + .width = 1, + }, +}; + +/* actionfield_set */ +static const struct vcap_set is2_actionfield_set[] = { + [VCAP_AFS_BASE_TYPE] = { + .type_id = -1, + .sw_per_item = 2, + .sw_cnt = 2, + }, + [VCAP_AFS_SMAC_SIP] = { + .type_id = -1, + .sw_per_item = 1, + .sw_cnt = 4, + }, +}; + +/* actionfield_set map */ +static const struct vcap_field *is2_actionfield_set_map[] = { + [VCAP_AFS_BASE_TYPE] = is2_base_type_actionfield, + [VCAP_AFS_SMAC_SIP] = is2_smac_sip_actionfield, +}; + +/* actionfield_set map size */ +static int is2_actionfield_set_map_size[] = { + [VCAP_AFS_BASE_TYPE] = ARRAY_SIZE(is2_base_type_actionfield), + [VCAP_AFS_SMAC_SIP] = ARRAY_SIZE(is2_smac_sip_actionfield), +}; + +/* Type Groups */ +static const struct vcap_typegroup is2_x4_keyfield_set_typegroups[] = { + { + .offset = 0, + .width = 3, + .value = 4, + }, + { + .offset = 96, + .width = 1, + .value = 0, + }, + { + .offset = 192, + .width = 2, + .value = 0, + }, + { + .offset = 288, + .width = 1, + .value = 0, + }, + {} +}; + +static const struct vcap_typegroup is2_x2_keyfield_set_typegroups[] = { + { + .offset = 0, + .width = 2, + .value = 2, + }, + { + .offset = 96, + .width = 1, + .value = 0, + }, + {} +}; + +static const struct vcap_typegroup is2_x1_keyfield_set_typegroups[] = { + { + .offset = 0, + .width = 1, + .value = 1, + }, + {} +}; + +static const struct vcap_typegroup *is2_keyfield_set_typegroups[] = { + [4] = is2_x4_keyfield_set_typegroups, + [2] = is2_x2_keyfield_set_typegroups, + [1] = is2_x1_keyfield_set_typegroups, + [5] = NULL, +}; + +static const struct vcap_typegroup is2_x2_actionfield_set_typegroups[] = { + { + .offset = 0, + .width = 2, + .value = 2, + }, + { + .offset = 31, + .width = 1, + .value = 0, + }, + {} +}; + +static const struct vcap_typegroup is2_x1_actionfield_set_typegroups[] = { + { + .offset = 0, + .width = 1, + .value = 1, + }, + {} +}; + +static const struct vcap_typegroup *is2_actionfield_set_typegroups[] = { + [2] = is2_x2_actionfield_set_typegroups, + [1] = is2_x1_actionfield_set_typegroups, + [5] = NULL, +}; + +/* Keyfieldset names */ +static const char * const vcap_keyfield_set_names[] = { + [VCAP_KFS_NO_VALUE] = "(None)", + [VCAP_KFS_ARP] = "VCAP_KFS_ARP", + [VCAP_KFS_IP4_OTHER] = "VCAP_KFS_IP4_OTHER", + [VCAP_KFS_IP4_TCP_UDP] = "VCAP_KFS_IP4_TCP_UDP", + [VCAP_KFS_IP6_OTHER] = "VCAP_KFS_IP6_OTHER", + [VCAP_KFS_IP6_STD] = "VCAP_KFS_IP6_STD", + [VCAP_KFS_IP6_TCP_UDP] = "VCAP_KFS_IP6_TCP_UDP", + [VCAP_KFS_MAC_ETYPE] = "VCAP_KFS_MAC_ETYPE", + [VCAP_KFS_MAC_LLC] = "VCAP_KFS_MAC_LLC", + [VCAP_KFS_MAC_SNAP] = "VCAP_KFS_MAC_SNAP", + [VCAP_KFS_OAM] = "VCAP_KFS_OAM", + [VCAP_KFS_SMAC_SIP4] = "VCAP_KFS_SMAC_SIP4", + [VCAP_KFS_SMAC_SIP6] = "VCAP_KFS_SMAC_SIP6", +}; + +/* Actionfieldset names */ +static const char * const vcap_actionfield_set_names[] = { + [VCAP_AFS_NO_VALUE] = "(None)", + [VCAP_AFS_BASE_TYPE] = "VCAP_AFS_BASE_TYPE", + [VCAP_AFS_SMAC_SIP] = "VCAP_AFS_SMAC_SIP", +}; + +/* Keyfield names */ +static const char * const vcap_keyfield_names[] = { + [VCAP_KF_NO_VALUE] = "(None)", + [VCAP_KF_8021Q_DEI_CLS] = "8021Q_DEI_CLS", + [VCAP_KF_8021Q_PCP_CLS] = "8021Q_PCP_CLS", + [VCAP_KF_8021Q_VID_CLS] = "8021Q_VID_CLS", + [VCAP_KF_8021Q_VLAN_TAGGED_IS] = "8021Q_VLAN_TAGGED_IS", + [VCAP_KF_ARP_ADDR_SPACE_OK_IS] = "ARP_ADDR_SPACE_OK_IS", + [VCAP_KF_ARP_LEN_OK_IS] = "ARP_LEN_OK_IS", + [VCAP_KF_ARP_OPCODE] = "ARP_OPCODE", + [VCAP_KF_ARP_OPCODE_UNKNOWN_IS] = "ARP_OPCODE_UNKNOWN_IS", + [VCAP_KF_ARP_PROTO_SPACE_OK_IS] = "ARP_PROTO_SPACE_OK_IS", + [VCAP_KF_ARP_SENDER_MATCH_IS] = "ARP_SENDER_MATCH_IS", + [VCAP_KF_ARP_TGT_MATCH_IS] = "ARP_TGT_MATCH_IS", + [VCAP_KF_ETYPE] = "ETYPE", + [VCAP_KF_HOST_MATCH] = "HOST_MATCH", + [VCAP_KF_IF_IGR_PORT] = "IF_IGR_PORT", + [VCAP_KF_IF_IGR_PORT_MASK] = "IF_IGR_PORT_MASK", + [VCAP_KF_IP4_IS] = "IP4_IS", + [VCAP_KF_ISDX_GT0_IS] = "ISDX_GT0_IS", + [VCAP_KF_L2_BC_IS] = "L2_BC_IS", + [VCAP_KF_L2_DMAC] = "L2_DMAC", + [VCAP_KF_L2_FRM_TYPE] = "L2_FRM_TYPE", + [VCAP_KF_L2_LLC] = "L2_LLC", + [VCAP_KF_L2_MC_IS] = "L2_MC_IS", + [VCAP_KF_L2_PAYLOAD0] = "L2_PAYLOAD0", + [VCAP_KF_L2_PAYLOAD1] = "L2_PAYLOAD1", + [VCAP_KF_L2_PAYLOAD2] = "L2_PAYLOAD2", + [VCAP_KF_L2_SMAC] = "L2_SMAC", + [VCAP_KF_L2_SNAP] = "L2_SNAP", + [VCAP_KF_L3_DIP_EQ_SIP_IS] = "L3_DIP_EQ_SIP_IS", + [VCAP_KF_L3_FRAGMENT] = "L3_FRAGMENT", + [VCAP_KF_L3_FRAG_OFS_GT0] = "L3_FRAG_OFS_GT0", + [VCAP_KF_L3_IP4_DIP] = "L3_IP4_DIP", + [VCAP_KF_L3_IP4_SIP] = "L3_IP4_SIP", + [VCAP_KF_L3_IP6_DIP] = "L3_IP6_DIP", + [VCAP_KF_L3_IP6_SIP] = "L3_IP6_SIP", + [VCAP_KF_L3_IP_PROTO] = "L3_IP_PROTO", + [VCAP_KF_L3_OPTIONS_IS] = "L3_OPTIONS_IS", + [VCAP_KF_L3_PAYLOAD] = "L3_PAYLOAD", + [VCAP_KF_L3_TOS] = "L3_TOS", + [VCAP_KF_L3_TTL_GT0] = "L3_TTL_GT0", + [VCAP_KF_L4_1588_DOM] = "L4_1588_DOM", + [VCAP_KF_L4_1588_VER] = "L4_1588_VER", + [VCAP_KF_L4_ACK] = "L4_ACK", + [VCAP_KF_L4_DPORT] = "L4_DPORT", + [VCAP_KF_L4_FIN] = "L4_FIN", + [VCAP_KF_L4_PSH] = "L4_PSH", + [VCAP_KF_L4_RNG] = "L4_RNG", + [VCAP_KF_L4_RST] = "L4_RST", + [VCAP_KF_L4_SEQUENCE_EQ0_IS] = "L4_SEQUENCE_EQ0_IS", + [VCAP_KF_L4_SPORT] = "L4_SPORT", + [VCAP_KF_L4_SPORT_EQ_DPORT_IS] = "L4_SPORT_EQ_DPORT_IS", + [VCAP_KF_L4_SYN] = "L4_SYN", + [VCAP_KF_L4_URG] = "L4_URG", + [VCAP_KF_LOOKUP_FIRST_IS] = "LOOKUP_FIRST_IS", + [VCAP_KF_LOOKUP_PAG] = "LOOKUP_PAG", + [VCAP_KF_OAM_CCM_CNTS_EQ0] = "OAM_CCM_CNTS_EQ0", + [VCAP_KF_OAM_DETECTED] = "OAM_DETECTED", + [VCAP_KF_OAM_FLAGS] = "OAM_FLAGS", + [VCAP_KF_OAM_MEL_FLAGS] = "OAM_MEL_FLAGS", + [VCAP_KF_OAM_MEPID] = "OAM_MEPID", + [VCAP_KF_OAM_OPCODE] = "OAM_OPCODE", + [VCAP_KF_OAM_VER] = "OAM_VER", + [VCAP_KF_OAM_Y1731_IS] = "OAM_Y1731_IS", + [VCAP_KF_TCP_IS] = "TCP_IS", + [VCAP_KF_TYPE] = "TYPE", +}; + +/* Actionfield names */ +static const char * const vcap_actionfield_names[] = { + [VCAP_AF_NO_VALUE] = "(None)", + [VCAP_AF_ACL_ID] = "ACL_ID", + [VCAP_AF_CPU_COPY_ENA] = "CPU_COPY_ENA", + [VCAP_AF_CPU_QUEUE_NUM] = "CPU_QUEUE_NUM", + [VCAP_AF_FWD_KILL_ENA] = "FWD_KILL_ENA", + [VCAP_AF_HIT_ME_ONCE] = "HIT_ME_ONCE", + [VCAP_AF_HOST_MATCH] = "HOST_MATCH", + [VCAP_AF_ISDX_ENA] = "ISDX_ENA", + [VCAP_AF_LRN_DIS] = "LRN_DIS", + [VCAP_AF_MASK_MODE] = "MASK_MODE", + [VCAP_AF_MIRROR_ENA] = "MIRROR_ENA", + [VCAP_AF_POLICE_ENA] = "POLICE_ENA", + [VCAP_AF_POLICE_IDX] = "POLICE_IDX", + [VCAP_AF_POLICE_VCAP_ONLY] = "POLICE_VCAP_ONLY", + [VCAP_AF_PORT_MASK] = "PORT_MASK", + [VCAP_AF_REW_OP] = "REW_OP", +}; + +/* VCAPs */ +const struct vcap_info lan966x_vcaps[] = { + [VCAP_TYPE_IS2] = { + .name = "is2", + .rows = 64, + .sw_count = 4, + .sw_width = 96, + .sticky_width = 32, + .act_width = 31, + .default_cnt = 11, + .require_cnt_dis = 1, + .version = 1, + .keyfield_set = is2_keyfield_set, + .keyfield_set_size = ARRAY_SIZE(is2_keyfield_set), + .actionfield_set = is2_actionfield_set, + .actionfield_set_size = ARRAY_SIZE(is2_actionfield_set), + .keyfield_set_map = is2_keyfield_set_map, + .keyfield_set_map_size = is2_keyfield_set_map_size, + .actionfield_set_map = is2_actionfield_set_map, + .actionfield_set_map_size = is2_actionfield_set_map_size, + .keyfield_set_typegroups = is2_keyfield_set_typegroups, + .actionfield_set_typegroups = is2_actionfield_set_typegroups, + }, +}; + +const struct vcap_statistics lan966x_vcap_stats = { + .name = "lan966x", + .count = 1, + .keyfield_set_names = vcap_keyfield_set_names, + .actionfield_set_names = vcap_actionfield_set_names, + .keyfield_names = vcap_keyfield_names, + .actionfield_names = vcap_actionfield_names, +}; diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_vcap_ag_api.h b/drivers/net/ethernet/microchip/lan966x/lan966x_vcap_ag_api.h new file mode 100644 index 000000000000..0d8bbee73b2a --- /dev/null +++ b/drivers/net/ethernet/microchip/lan966x/lan966x_vcap_ag_api.h @@ -0,0 +1,11 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ + +#ifndef __LAN966X_VCAP_AG_API_H__ +#define __LAN966X_VCAP_AG_API_H__ + +#include "vcap_api.h" + +extern const struct vcap_info lan966x_vcaps[]; +extern const struct vcap_statistics lan966x_vcap_stats; + +#endif /* __LAN966X_VCAP_AG_API_H__ */ diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_vcap_impl.c b/drivers/net/ethernet/microchip/lan966x/lan966x_vcap_impl.c index 8d89cfcb8502..aac821cd611e 100644 --- a/drivers/net/ethernet/microchip/lan966x/lan966x_vcap_impl.c +++ b/drivers/net/ethernet/microchip/lan966x/lan966x_vcap_impl.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0+ #include "lan966x_main.h" +#include "lan966x_vcap_ag_api.h" #include "vcap_api.h" int lan966x_vcap_init(struct lan966x *lan966x) @@ -11,6 +12,9 @@ int lan966x_vcap_init(struct lan966x *lan966x) if (!ctrl) return -ENOMEM; + ctrl->vcaps = lan966x_vcaps; + ctrl->stats = &lan966x_vcap_stats; + lan966x->vcap_ctrl = ctrl; return 0; From f919ccc93dc6185d321c229f7241057406615e1b Mon Sep 17 00:00:00 2001 From: Horatiu Vultur Date: Fri, 25 Nov 2022 10:50:06 +0100 Subject: [PATCH 1293/2002] net: lan966x: add vcap registers Add registers used to access vcap controller. Signed-off-by: Horatiu Vultur Signed-off-by: Paolo Abeni --- .../ethernet/microchip/lan966x/lan966x_main.c | 3 + .../ethernet/microchip/lan966x/lan966x_regs.h | 196 ++++++++++++++++++ 2 files changed, 199 insertions(+) diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_main.c b/drivers/net/ethernet/microchip/lan966x/lan966x_main.c index 24d7d7dca1df..f6092983d028 100644 --- a/drivers/net/ethernet/microchip/lan966x/lan966x_main.c +++ b/drivers/net/ethernet/microchip/lan966x/lan966x_main.c @@ -47,6 +47,9 @@ static const struct lan966x_main_io_resource lan966x_main_iomap[] = { { TARGET_PTP, 0xc000, 1 }, /* 0xe200c000 */ { TARGET_CHIP_TOP, 0x10000, 1 }, /* 0xe2010000 */ { TARGET_REW, 0x14000, 1 }, /* 0xe2014000 */ + { TARGET_VCAP, 0x18000, 1 }, /* 0xe2018000 */ + { TARGET_VCAP + 1, 0x20000, 1 }, /* 0xe2020000 */ + { TARGET_VCAP + 2, 0x24000, 1 }, /* 0xe2024000 */ { TARGET_SYS, 0x28000, 1 }, /* 0xe2028000 */ { TARGET_DEV, 0x34000, 1 }, /* 0xe2034000 */ { TARGET_DEV + 1, 0x38000, 1 }, /* 0xe2038000 */ diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_regs.h b/drivers/net/ethernet/microchip/lan966x/lan966x_regs.h index fb5087fef22e..9767b5a1c958 100644 --- a/drivers/net/ethernet/microchip/lan966x/lan966x_regs.h +++ b/drivers/net/ethernet/microchip/lan966x/lan966x_regs.h @@ -25,6 +25,7 @@ enum lan966x_target { TARGET_QSYS = 46, TARGET_REW = 47, TARGET_SYS = 52, + TARGET_VCAP = 61, NUM_TARGETS = 66 }; @@ -315,6 +316,69 @@ enum lan966x_target { #define ANA_DROP_CFG_DROP_MC_SMAC_ENA_GET(x)\ FIELD_GET(ANA_DROP_CFG_DROP_MC_SMAC_ENA, x) +/* ANA:PORT:VCAP_S2_CFG */ +#define ANA_VCAP_S2_CFG(g) __REG(TARGET_ANA, 0, 1, 28672, g, 9, 128, 28, 0, 1, 4) + +#define ANA_VCAP_S2_CFG_ISDX_ENA GENMASK(20, 19) +#define ANA_VCAP_S2_CFG_ISDX_ENA_SET(x)\ + FIELD_PREP(ANA_VCAP_S2_CFG_ISDX_ENA, x) +#define ANA_VCAP_S2_CFG_ISDX_ENA_GET(x)\ + FIELD_GET(ANA_VCAP_S2_CFG_ISDX_ENA, x) + +#define ANA_VCAP_S2_CFG_UDP_PAYLOAD_ENA GENMASK(18, 17) +#define ANA_VCAP_S2_CFG_UDP_PAYLOAD_ENA_SET(x)\ + FIELD_PREP(ANA_VCAP_S2_CFG_UDP_PAYLOAD_ENA, x) +#define ANA_VCAP_S2_CFG_UDP_PAYLOAD_ENA_GET(x)\ + FIELD_GET(ANA_VCAP_S2_CFG_UDP_PAYLOAD_ENA, x) + +#define ANA_VCAP_S2_CFG_ETYPE_PAYLOAD_ENA GENMASK(16, 15) +#define ANA_VCAP_S2_CFG_ETYPE_PAYLOAD_ENA_SET(x)\ + FIELD_PREP(ANA_VCAP_S2_CFG_ETYPE_PAYLOAD_ENA, x) +#define ANA_VCAP_S2_CFG_ETYPE_PAYLOAD_ENA_GET(x)\ + FIELD_GET(ANA_VCAP_S2_CFG_ETYPE_PAYLOAD_ENA, x) + +#define ANA_VCAP_S2_CFG_ENA BIT(14) +#define ANA_VCAP_S2_CFG_ENA_SET(x)\ + FIELD_PREP(ANA_VCAP_S2_CFG_ENA, x) +#define ANA_VCAP_S2_CFG_ENA_GET(x)\ + FIELD_GET(ANA_VCAP_S2_CFG_ENA, x) + +#define ANA_VCAP_S2_CFG_SNAP_DIS GENMASK(13, 12) +#define ANA_VCAP_S2_CFG_SNAP_DIS_SET(x)\ + FIELD_PREP(ANA_VCAP_S2_CFG_SNAP_DIS, x) +#define ANA_VCAP_S2_CFG_SNAP_DIS_GET(x)\ + FIELD_GET(ANA_VCAP_S2_CFG_SNAP_DIS, x) + +#define ANA_VCAP_S2_CFG_ARP_DIS GENMASK(11, 10) +#define ANA_VCAP_S2_CFG_ARP_DIS_SET(x)\ + FIELD_PREP(ANA_VCAP_S2_CFG_ARP_DIS, x) +#define ANA_VCAP_S2_CFG_ARP_DIS_GET(x)\ + FIELD_GET(ANA_VCAP_S2_CFG_ARP_DIS, x) + +#define ANA_VCAP_S2_CFG_IP_TCPUDP_DIS GENMASK(9, 8) +#define ANA_VCAP_S2_CFG_IP_TCPUDP_DIS_SET(x)\ + FIELD_PREP(ANA_VCAP_S2_CFG_IP_TCPUDP_DIS, x) +#define ANA_VCAP_S2_CFG_IP_TCPUDP_DIS_GET(x)\ + FIELD_GET(ANA_VCAP_S2_CFG_IP_TCPUDP_DIS, x) + +#define ANA_VCAP_S2_CFG_IP_OTHER_DIS GENMASK(7, 6) +#define ANA_VCAP_S2_CFG_IP_OTHER_DIS_SET(x)\ + FIELD_PREP(ANA_VCAP_S2_CFG_IP_OTHER_DIS, x) +#define ANA_VCAP_S2_CFG_IP_OTHER_DIS_GET(x)\ + FIELD_GET(ANA_VCAP_S2_CFG_IP_OTHER_DIS, x) + +#define ANA_VCAP_S2_CFG_IP6_CFG GENMASK(5, 2) +#define ANA_VCAP_S2_CFG_IP6_CFG_SET(x)\ + FIELD_PREP(ANA_VCAP_S2_CFG_IP6_CFG, x) +#define ANA_VCAP_S2_CFG_IP6_CFG_GET(x)\ + FIELD_GET(ANA_VCAP_S2_CFG_IP6_CFG, x) + +#define ANA_VCAP_S2_CFG_OAM_DIS GENMASK(1, 0) +#define ANA_VCAP_S2_CFG_OAM_DIS_SET(x)\ + FIELD_PREP(ANA_VCAP_S2_CFG_OAM_DIS, x) +#define ANA_VCAP_S2_CFG_OAM_DIS_GET(x)\ + FIELD_GET(ANA_VCAP_S2_CFG_OAM_DIS, x) + /* ANA:PORT:CPU_FWD_CFG */ #define ANA_CPU_FWD_CFG(g) __REG(TARGET_ANA, 0, 1, 28672, g, 9, 128, 96, 0, 1, 4) @@ -1506,4 +1570,136 @@ enum lan966x_target { #define SYS_RAM_INIT_RAM_INIT_GET(x)\ FIELD_GET(SYS_RAM_INIT_RAM_INIT, x) +/* VCAP:VCAP_CORE_CFG:VCAP_UPDATE_CTRL */ +#define VCAP_UPDATE_CTRL(t) __REG(TARGET_VCAP, t, 3, 0, 0, 1, 8, 0, 0, 1, 4) + +#define VCAP_UPDATE_CTRL_UPDATE_CMD GENMASK(24, 22) +#define VCAP_UPDATE_CTRL_UPDATE_CMD_SET(x)\ + FIELD_PREP(VCAP_UPDATE_CTRL_UPDATE_CMD, x) +#define VCAP_UPDATE_CTRL_UPDATE_CMD_GET(x)\ + FIELD_GET(VCAP_UPDATE_CTRL_UPDATE_CMD, x) + +#define VCAP_UPDATE_CTRL_UPDATE_ENTRY_DIS BIT(21) +#define VCAP_UPDATE_CTRL_UPDATE_ENTRY_DIS_SET(x)\ + FIELD_PREP(VCAP_UPDATE_CTRL_UPDATE_ENTRY_DIS, x) +#define VCAP_UPDATE_CTRL_UPDATE_ENTRY_DIS_GET(x)\ + FIELD_GET(VCAP_UPDATE_CTRL_UPDATE_ENTRY_DIS, x) + +#define VCAP_UPDATE_CTRL_UPDATE_ACTION_DIS BIT(20) +#define VCAP_UPDATE_CTRL_UPDATE_ACTION_DIS_SET(x)\ + FIELD_PREP(VCAP_UPDATE_CTRL_UPDATE_ACTION_DIS, x) +#define VCAP_UPDATE_CTRL_UPDATE_ACTION_DIS_GET(x)\ + FIELD_GET(VCAP_UPDATE_CTRL_UPDATE_ACTION_DIS, x) + +#define VCAP_UPDATE_CTRL_UPDATE_CNT_DIS BIT(19) +#define VCAP_UPDATE_CTRL_UPDATE_CNT_DIS_SET(x)\ + FIELD_PREP(VCAP_UPDATE_CTRL_UPDATE_CNT_DIS, x) +#define VCAP_UPDATE_CTRL_UPDATE_CNT_DIS_GET(x)\ + FIELD_GET(VCAP_UPDATE_CTRL_UPDATE_CNT_DIS, x) + +#define VCAP_UPDATE_CTRL_UPDATE_ADDR GENMASK(18, 3) +#define VCAP_UPDATE_CTRL_UPDATE_ADDR_SET(x)\ + FIELD_PREP(VCAP_UPDATE_CTRL_UPDATE_ADDR, x) +#define VCAP_UPDATE_CTRL_UPDATE_ADDR_GET(x)\ + FIELD_GET(VCAP_UPDATE_CTRL_UPDATE_ADDR, x) + +#define VCAP_UPDATE_CTRL_UPDATE_SHOT BIT(2) +#define VCAP_UPDATE_CTRL_UPDATE_SHOT_SET(x)\ + FIELD_PREP(VCAP_UPDATE_CTRL_UPDATE_SHOT, x) +#define VCAP_UPDATE_CTRL_UPDATE_SHOT_GET(x)\ + FIELD_GET(VCAP_UPDATE_CTRL_UPDATE_SHOT, x) + +#define VCAP_UPDATE_CTRL_CLEAR_CACHE BIT(1) +#define VCAP_UPDATE_CTRL_CLEAR_CACHE_SET(x)\ + FIELD_PREP(VCAP_UPDATE_CTRL_CLEAR_CACHE, x) +#define VCAP_UPDATE_CTRL_CLEAR_CACHE_GET(x)\ + FIELD_GET(VCAP_UPDATE_CTRL_CLEAR_CACHE, x) + +#define VCAP_UPDATE_CTRL_MV_TRAFFIC_IGN BIT(0) +#define VCAP_UPDATE_CTRL_MV_TRAFFIC_IGN_SET(x)\ + FIELD_PREP(VCAP_UPDATE_CTRL_MV_TRAFFIC_IGN, x) +#define VCAP_UPDATE_CTRL_MV_TRAFFIC_IGN_GET(x)\ + FIELD_GET(VCAP_UPDATE_CTRL_MV_TRAFFIC_IGN, x) + +/* VCAP:VCAP_CORE_CFG:VCAP_MV_CFG */ +#define VCAP_MV_CFG(t) __REG(TARGET_VCAP, t, 3, 0, 0, 1, 8, 4, 0, 1, 4) + +#define VCAP_MV_CFG_MV_NUM_POS GENMASK(31, 16) +#define VCAP_MV_CFG_MV_NUM_POS_SET(x)\ + FIELD_PREP(VCAP_MV_CFG_MV_NUM_POS, x) +#define VCAP_MV_CFG_MV_NUM_POS_GET(x)\ + FIELD_GET(VCAP_MV_CFG_MV_NUM_POS, x) + +#define VCAP_MV_CFG_MV_SIZE GENMASK(15, 0) +#define VCAP_MV_CFG_MV_SIZE_SET(x)\ + FIELD_PREP(VCAP_MV_CFG_MV_SIZE, x) +#define VCAP_MV_CFG_MV_SIZE_GET(x)\ + FIELD_GET(VCAP_MV_CFG_MV_SIZE, x) + +/* VCAP:VCAP_CORE_CACHE:VCAP_ENTRY_DAT */ +#define VCAP_ENTRY_DAT(t, r) __REG(TARGET_VCAP, t, 3, 8, 0, 1, 904, 0, r, 64, 4) + +/* VCAP:VCAP_CORE_CACHE:VCAP_MASK_DAT */ +#define VCAP_MASK_DAT(t, r) __REG(TARGET_VCAP, t, 3, 8, 0, 1, 904, 256, r, 64, 4) + +/* VCAP:VCAP_CORE_CACHE:VCAP_ACTION_DAT */ +#define VCAP_ACTION_DAT(t, r) __REG(TARGET_VCAP, t, 3, 8, 0, 1, 904, 512, r, 64, 4) + +/* VCAP:VCAP_CORE_CACHE:VCAP_CNT_DAT */ +#define VCAP_CNT_DAT(t, r) __REG(TARGET_VCAP, t, 3, 8, 0, 1, 904, 768, r, 32, 4) + +/* VCAP:VCAP_CORE_CACHE:VCAP_CNT_FW_DAT */ +#define VCAP_CNT_FW_DAT(t) __REG(TARGET_VCAP, t, 3, 8, 0, 1, 904, 896, 0, 1, 4) + +/* VCAP:VCAP_CORE_CACHE:VCAP_TG_DAT */ +#define VCAP_TG_DAT(t) __REG(TARGET_VCAP, t, 3, 8, 0, 1, 904, 900, 0, 1, 4) + +/* VCAP:VCAP_CORE_MAP:VCAP_CORE_IDX */ +#define VCAP_CORE_IDX(t) __REG(TARGET_VCAP, t, 3, 912, 0, 1, 8, 0, 0, 1, 4) + +#define VCAP_CORE_IDX_CORE_IDX GENMASK(3, 0) +#define VCAP_CORE_IDX_CORE_IDX_SET(x)\ + FIELD_PREP(VCAP_CORE_IDX_CORE_IDX, x) +#define VCAP_CORE_IDX_CORE_IDX_GET(x)\ + FIELD_GET(VCAP_CORE_IDX_CORE_IDX, x) + +/* VCAP:VCAP_CORE_MAP:VCAP_CORE_MAP */ +#define VCAP_CORE_MAP(t) __REG(TARGET_VCAP, t, 3, 912, 0, 1, 8, 4, 0, 1, 4) + +#define VCAP_CORE_MAP_CORE_MAP GENMASK(2, 0) +#define VCAP_CORE_MAP_CORE_MAP_SET(x)\ + FIELD_PREP(VCAP_CORE_MAP_CORE_MAP, x) +#define VCAP_CORE_MAP_CORE_MAP_GET(x)\ + FIELD_GET(VCAP_CORE_MAP_CORE_MAP, x) + +/* VCAP:VCAP_CONST:VCAP_VER */ +#define VCAP_VER(t) __REG(TARGET_VCAP, t, 3, 924, 0, 1, 40, 0, 0, 1, 4) + +/* VCAP:VCAP_CONST:ENTRY_WIDTH */ +#define VCAP_ENTRY_WIDTH(t) __REG(TARGET_VCAP, t, 3, 924, 0, 1, 40, 4, 0, 1, 4) + +/* VCAP:VCAP_CONST:ENTRY_CNT */ +#define VCAP_ENTRY_CNT(t) __REG(TARGET_VCAP, t, 3, 924, 0, 1, 40, 8, 0, 1, 4) + +/* VCAP:VCAP_CONST:ENTRY_SWCNT */ +#define VCAP_ENTRY_SWCNT(t) __REG(TARGET_VCAP, t, 3, 924, 0, 1, 40, 12, 0, 1, 4) + +/* VCAP:VCAP_CONST:ENTRY_TG_WIDTH */ +#define VCAP_ENTRY_TG_WIDTH(t) __REG(TARGET_VCAP, t, 3, 924, 0, 1, 40, 16, 0, 1, 4) + +/* VCAP:VCAP_CONST:ACTION_DEF_CNT */ +#define VCAP_ACTION_DEF_CNT(t) __REG(TARGET_VCAP, t, 3, 924, 0, 1, 40, 20, 0, 1, 4) + +/* VCAP:VCAP_CONST:ACTION_WIDTH */ +#define VCAP_ACTION_WIDTH(t) __REG(TARGET_VCAP, t, 3, 924, 0, 1, 40, 24, 0, 1, 4) + +/* VCAP:VCAP_CONST:CNT_WIDTH */ +#define VCAP_CNT_WIDTH(t) __REG(TARGET_VCAP, t, 3, 924, 0, 1, 40, 28, 0, 1, 4) + +/* VCAP:VCAP_CONST:CORE_CNT */ +#define VCAP_CORE_CNT(t) __REG(TARGET_VCAP, t, 3, 924, 0, 1, 40, 32, 0, 1, 4) + +/* VCAP:VCAP_CONST:IF_CNT */ +#define VCAP_IF_CNT(t) __REG(TARGET_VCAP, t, 3, 924, 0, 1, 40, 36, 0, 1, 4) + #endif /* _LAN966X_REGS_H_ */ From 3643abd6e6bc1adce8979e974ee7ede387948c1c Mon Sep 17 00:00:00 2001 From: Horatiu Vultur Date: Fri, 25 Nov 2022 10:50:07 +0100 Subject: [PATCH 1294/2002] net: lan966x: add tc flower support for VCAP API Currently the only supported action is ACTION_TRAP and the only dissector is ETH_ADDRS. Others will be added in future patches. Signed-off-by: Horatiu Vultur Signed-off-by: Paolo Abeni --- .../net/ethernet/microchip/lan966x/Makefile | 3 +- .../ethernet/microchip/lan966x/lan966x_main.h | 3 + .../ethernet/microchip/lan966x/lan966x_tc.c | 2 + .../microchip/lan966x/lan966x_tc_flower.c | 262 ++++++++++++++++++ .../microchip/lan966x/lan966x_vcap_impl.c | 152 ++++++++++ 5 files changed, 421 insertions(+), 1 deletion(-) create mode 100644 drivers/net/ethernet/microchip/lan966x/lan966x_tc_flower.c diff --git a/drivers/net/ethernet/microchip/lan966x/Makefile b/drivers/net/ethernet/microchip/lan966x/Makefile index 053a174f09f3..23781149f7a9 100644 --- a/drivers/net/ethernet/microchip/lan966x/Makefile +++ b/drivers/net/ethernet/microchip/lan966x/Makefile @@ -12,7 +12,8 @@ lan966x-switch-objs := lan966x_main.o lan966x_phylink.o lan966x_port.o \ lan966x_tc.o lan966x_mqprio.o lan966x_taprio.o \ lan966x_tbf.o lan966x_cbs.o lan966x_ets.o \ lan966x_tc_matchall.o lan966x_police.o lan966x_mirror.o \ - lan966x_xdp.o lan966x_vcap_impl.o lan966x_vcap_ag_api.o + lan966x_xdp.o lan966x_vcap_impl.o lan966x_vcap_ag_api.o \ + lan966x_tc_flower.o # Provide include files ccflags-y += -I$(srctree)/drivers/net/ethernet/microchip/vcap diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_main.h b/drivers/net/ethernet/microchip/lan966x/lan966x_main.h index 1b3fa2751bcd..118745e7c22a 100644 --- a/drivers/net/ethernet/microchip/lan966x/lan966x_main.h +++ b/drivers/net/ethernet/microchip/lan966x/lan966x_main.h @@ -588,6 +588,9 @@ static inline bool lan966x_xdp_port_present(struct lan966x_port *port) int lan966x_vcap_init(struct lan966x *lan966x); void lan966x_vcap_deinit(struct lan966x *lan966x); +int lan966x_tc_flower(struct lan966x_port *port, + struct flow_cls_offload *f); + static inline void __iomem *lan_addr(void __iomem *base[], int id, int tinst, int tcnt, int gbase, int ginst, diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_tc.c b/drivers/net/ethernet/microchip/lan966x/lan966x_tc.c index 651d5493ae55..01072121c999 100644 --- a/drivers/net/ethernet/microchip/lan966x/lan966x_tc.c +++ b/drivers/net/ethernet/microchip/lan966x/lan966x_tc.c @@ -69,6 +69,8 @@ static int lan966x_tc_block_cb(enum tc_setup_type type, void *type_data, switch (type) { case TC_SETUP_CLSMATCHALL: return lan966x_tc_matchall(port, type_data, ingress); + case TC_SETUP_CLSFLOWER: + return lan966x_tc_flower(port, type_data); default: return -EOPNOTSUPP; } diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_tc_flower.c b/drivers/net/ethernet/microchip/lan966x/lan966x_tc_flower.c new file mode 100644 index 000000000000..04a2afd683cc --- /dev/null +++ b/drivers/net/ethernet/microchip/lan966x/lan966x_tc_flower.c @@ -0,0 +1,262 @@ +// SPDX-License-Identifier: GPL-2.0+ + +#include "lan966x_main.h" +#include "vcap_api.h" +#include "vcap_api_client.h" + +/* Controls how PORT_MASK is applied */ +enum LAN966X_PORT_MASK_MODE { + LAN966X_PMM_NO_ACTION, + LAN966X_PMM_REPLACE, + LAN966X_PMM_FORWARDING, + LAN966X_PMM_REDIRECT, +}; + +struct lan966x_tc_flower_parse_usage { + struct flow_cls_offload *f; + struct flow_rule *frule; + struct vcap_rule *vrule; + unsigned int used_keys; + u16 l3_proto; +}; + +static int lan966x_tc_flower_handler_ethaddr_usage(struct lan966x_tc_flower_parse_usage *st) +{ + enum vcap_key_field smac_key = VCAP_KF_L2_SMAC; + enum vcap_key_field dmac_key = VCAP_KF_L2_DMAC; + struct flow_match_eth_addrs match; + struct vcap_u48_key smac, dmac; + int err = 0; + + flow_rule_match_eth_addrs(st->frule, &match); + + if (!is_zero_ether_addr(match.mask->src)) { + vcap_netbytes_copy(smac.value, match.key->src, ETH_ALEN); + vcap_netbytes_copy(smac.mask, match.mask->src, ETH_ALEN); + err = vcap_rule_add_key_u48(st->vrule, smac_key, &smac); + if (err) + goto out; + } + + if (!is_zero_ether_addr(match.mask->dst)) { + vcap_netbytes_copy(dmac.value, match.key->dst, ETH_ALEN); + vcap_netbytes_copy(dmac.mask, match.mask->dst, ETH_ALEN); + err = vcap_rule_add_key_u48(st->vrule, dmac_key, &dmac); + if (err) + goto out; + } + + st->used_keys |= BIT(FLOW_DISSECTOR_KEY_ETH_ADDRS); + + return err; + +out: + NL_SET_ERR_MSG_MOD(st->f->common.extack, "eth_addr parse error"); + return err; +} + +static int +(*lan966x_tc_flower_handlers_usage[])(struct lan966x_tc_flower_parse_usage *st) = { + [FLOW_DISSECTOR_KEY_ETH_ADDRS] = lan966x_tc_flower_handler_ethaddr_usage, +}; + +static int lan966x_tc_flower_use_dissectors(struct flow_cls_offload *f, + struct vcap_admin *admin, + struct vcap_rule *vrule, + u16 *l3_proto) +{ + struct lan966x_tc_flower_parse_usage state = { + .f = f, + .vrule = vrule, + .l3_proto = ETH_P_ALL, + }; + int err = 0; + + state.frule = flow_cls_offload_flow_rule(f); + for (int i = 0; i < ARRAY_SIZE(lan966x_tc_flower_handlers_usage); ++i) { + if (!flow_rule_match_key(state.frule, i) || + !lan966x_tc_flower_handlers_usage[i]) + continue; + + err = lan966x_tc_flower_handlers_usage[i](&state); + if (err) + return err; + } + + if (l3_proto) + *l3_proto = state.l3_proto; + + return err; +} + +static int lan966x_tc_flower_action_check(struct vcap_control *vctrl, + struct flow_cls_offload *fco, + struct vcap_admin *admin) +{ + struct flow_rule *rule = flow_cls_offload_flow_rule(fco); + struct flow_action_entry *actent, *last_actent = NULL; + struct flow_action *act = &rule->action; + u64 action_mask = 0; + int idx; + + if (!flow_action_has_entries(act)) { + NL_SET_ERR_MSG_MOD(fco->common.extack, "No actions"); + return -EINVAL; + } + + if (!flow_action_basic_hw_stats_check(act, fco->common.extack)) + return -EOPNOTSUPP; + + flow_action_for_each(idx, actent, act) { + if (action_mask & BIT(actent->id)) { + NL_SET_ERR_MSG_MOD(fco->common.extack, + "More actions of the same type"); + return -EINVAL; + } + action_mask |= BIT(actent->id); + last_actent = actent; /* Save last action for later check */ + } + + /* Check that last action is a goto */ + if (last_actent->id != FLOW_ACTION_GOTO) { + NL_SET_ERR_MSG_MOD(fco->common.extack, + "Last action must be 'goto'"); + return -EINVAL; + } + + /* Check if the goto chain is in the next lookup */ + if (!vcap_is_next_lookup(vctrl, fco->common.chain_index, + last_actent->chain_index)) { + NL_SET_ERR_MSG_MOD(fco->common.extack, + "Invalid goto chain"); + return -EINVAL; + } + + /* Catch unsupported combinations of actions */ + if (action_mask & BIT(FLOW_ACTION_TRAP) && + action_mask & BIT(FLOW_ACTION_ACCEPT)) { + NL_SET_ERR_MSG_MOD(fco->common.extack, + "Cannot combine pass and trap action"); + return -EOPNOTSUPP; + } + + return 0; +} + +static int lan966x_tc_flower_add(struct lan966x_port *port, + struct flow_cls_offload *f, + struct vcap_admin *admin) +{ + struct flow_action_entry *act; + u16 l3_proto = ETH_P_ALL; + struct flow_rule *frule; + struct vcap_rule *vrule; + int err, idx; + + err = lan966x_tc_flower_action_check(port->lan966x->vcap_ctrl, f, + admin); + if (err) + return err; + + vrule = vcap_alloc_rule(port->lan966x->vcap_ctrl, port->dev, + f->common.chain_index, VCAP_USER_TC, + f->common.prio, 0); + if (IS_ERR(vrule)) + return PTR_ERR(vrule); + + vrule->cookie = f->cookie; + err = lan966x_tc_flower_use_dissectors(f, admin, vrule, &l3_proto); + if (err) + goto out; + + frule = flow_cls_offload_flow_rule(f); + + flow_action_for_each(idx, act, &frule->action) { + switch (act->id) { + case FLOW_ACTION_TRAP: + err = vcap_rule_add_action_bit(vrule, + VCAP_AF_CPU_COPY_ENA, + VCAP_BIT_1); + err |= vcap_rule_add_action_u32(vrule, + VCAP_AF_CPU_QUEUE_NUM, + 0); + err |= vcap_rule_add_action_u32(vrule, VCAP_AF_MASK_MODE, + LAN966X_PMM_REPLACE); + err |= vcap_set_rule_set_actionset(vrule, + VCAP_AFS_BASE_TYPE); + if (err) + goto out; + + break; + case FLOW_ACTION_GOTO: + break; + default: + NL_SET_ERR_MSG_MOD(f->common.extack, + "Unsupported TC action"); + err = -EOPNOTSUPP; + goto out; + } + } + + err = vcap_val_rule(vrule, l3_proto); + if (err) { + vcap_set_tc_exterr(f, vrule); + goto out; + } + + err = vcap_add_rule(vrule); + if (err) + NL_SET_ERR_MSG_MOD(f->common.extack, + "Could not add the filter"); +out: + vcap_free_rule(vrule); + return err; +} + +static int lan966x_tc_flower_del(struct lan966x_port *port, + struct flow_cls_offload *f, + struct vcap_admin *admin) +{ + struct vcap_control *vctrl; + int err = -ENOENT, rule_id; + + vctrl = port->lan966x->vcap_ctrl; + while (true) { + rule_id = vcap_lookup_rule_by_cookie(vctrl, f->cookie); + if (rule_id <= 0) + break; + + err = vcap_del_rule(vctrl, port->dev, rule_id); + if (err) { + NL_SET_ERR_MSG_MOD(f->common.extack, + "Cannot delete rule"); + break; + } + } + + return err; +} + +int lan966x_tc_flower(struct lan966x_port *port, + struct flow_cls_offload *f) +{ + struct vcap_admin *admin; + + admin = vcap_find_admin(port->lan966x->vcap_ctrl, + f->common.chain_index); + if (!admin) { + NL_SET_ERR_MSG_MOD(f->common.extack, "Invalid chain"); + return -EINVAL; + } + + switch (f->command) { + case FLOW_CLS_REPLACE: + return lan966x_tc_flower_add(port, f, admin); + case FLOW_CLS_DESTROY: + return lan966x_tc_flower_del(port, f, admin); + default: + return -EOPNOTSUPP; + } + + return 0; +} diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_vcap_impl.c b/drivers/net/ethernet/microchip/lan966x/lan966x_vcap_impl.c index aac821cd611e..08253dd4a306 100644 --- a/drivers/net/ethernet/microchip/lan966x/lan966x_vcap_impl.c +++ b/drivers/net/ethernet/microchip/lan966x/lan966x_vcap_impl.c @@ -3,10 +3,143 @@ #include "lan966x_main.h" #include "lan966x_vcap_ag_api.h" #include "vcap_api.h" +#include "vcap_api_client.h" + +#define LAN966X_VCAP_CID_IS2_L0 VCAP_CID_INGRESS_STAGE2_L0 /* IS2 lookup 0 */ +#define LAN966X_VCAP_CID_IS2_L1 VCAP_CID_INGRESS_STAGE2_L1 /* IS2 lookup 1 */ +#define LAN966X_VCAP_CID_IS2_MAX (VCAP_CID_INGRESS_STAGE2_L2 - 1) /* IS2 Max */ + +#define STREAMSIZE (64 * 4) + +#define LAN966X_IS2_LOOKUPS 2 + +static struct lan966x_vcap_inst { + enum vcap_type vtype; /* type of vcap */ + int tgt_inst; /* hardware instance number */ + int lookups; /* number of lookups in this vcap type */ + int first_cid; /* first chain id in this vcap */ + int last_cid; /* last chain id in this vcap */ + int count; /* number of available addresses */ +} lan966x_vcap_inst_cfg[] = { + { + .vtype = VCAP_TYPE_IS2, /* IS2-0 */ + .tgt_inst = 2, + .lookups = LAN966X_IS2_LOOKUPS, + .first_cid = LAN966X_VCAP_CID_IS2_L0, + .last_cid = LAN966X_VCAP_CID_IS2_MAX, + .count = 256, + }, +}; + +struct lan966x_vcap_cmd_cb { + struct lan966x *lan966x; + u32 instance; +}; + +static u32 lan966x_vcap_read_update_ctrl(const struct lan966x_vcap_cmd_cb *cb) +{ + return lan_rd(cb->lan966x, VCAP_UPDATE_CTRL(cb->instance)); +} + +static void lan966x_vcap_wait_update(struct lan966x *lan966x, int instance) +{ + const struct lan966x_vcap_cmd_cb cb = { .lan966x = lan966x, + .instance = instance }; + u32 val; + + readx_poll_timeout(lan966x_vcap_read_update_ctrl, &cb, val, + (val & VCAP_UPDATE_CTRL_UPDATE_SHOT) == 0, 10, + 100000); +} + +static void __lan966x_vcap_range_init(struct lan966x *lan966x, + struct vcap_admin *admin, + u32 addr, + u32 count) +{ + lan_wr(VCAP_MV_CFG_MV_NUM_POS_SET(0) | + VCAP_MV_CFG_MV_SIZE_SET(count - 1), + lan966x, VCAP_MV_CFG(admin->tgt_inst)); + + lan_wr(VCAP_UPDATE_CTRL_UPDATE_CMD_SET(VCAP_CMD_INITIALIZE) | + VCAP_UPDATE_CTRL_UPDATE_ENTRY_DIS_SET(0) | + VCAP_UPDATE_CTRL_UPDATE_ACTION_DIS_SET(0) | + VCAP_UPDATE_CTRL_UPDATE_CNT_DIS_SET(0) | + VCAP_UPDATE_CTRL_UPDATE_ADDR_SET(addr) | + VCAP_UPDATE_CTRL_CLEAR_CACHE_SET(true) | + VCAP_UPDATE_CTRL_UPDATE_SHOT_SET(1), + lan966x, VCAP_UPDATE_CTRL(admin->tgt_inst)); + + lan966x_vcap_wait_update(lan966x, admin->tgt_inst); +} + +static void lan966x_vcap_admin_free(struct vcap_admin *admin) +{ + if (!admin) + return; + + kfree(admin->cache.keystream); + kfree(admin->cache.maskstream); + kfree(admin->cache.actionstream); + mutex_destroy(&admin->lock); + kfree(admin); +} + +static struct vcap_admin * +lan966x_vcap_admin_alloc(struct lan966x *lan966x, struct vcap_control *ctrl, + const struct lan966x_vcap_inst *cfg) +{ + struct vcap_admin *admin; + + admin = kzalloc(sizeof(*admin), GFP_KERNEL); + if (!admin) + return ERR_PTR(-ENOMEM); + + mutex_init(&admin->lock); + INIT_LIST_HEAD(&admin->list); + INIT_LIST_HEAD(&admin->rules); + admin->vtype = cfg->vtype; + admin->vinst = 0; + admin->lookups = cfg->lookups; + admin->lookups_per_instance = cfg->lookups; + admin->first_cid = cfg->first_cid; + admin->last_cid = cfg->last_cid; + admin->cache.keystream = kzalloc(STREAMSIZE, GFP_KERNEL); + admin->cache.maskstream = kzalloc(STREAMSIZE, GFP_KERNEL); + admin->cache.actionstream = kzalloc(STREAMSIZE, GFP_KERNEL); + if (!admin->cache.keystream || + !admin->cache.maskstream || + !admin->cache.actionstream) { + lan966x_vcap_admin_free(admin); + return ERR_PTR(-ENOMEM); + } + + return admin; +} + +static void lan966x_vcap_block_init(struct lan966x *lan966x, + struct vcap_admin *admin, + struct lan966x_vcap_inst *cfg) +{ + admin->first_valid_addr = 0; + admin->last_used_addr = cfg->count; + admin->last_valid_addr = cfg->count - 1; + + lan_wr(VCAP_CORE_IDX_CORE_IDX_SET(0), + lan966x, VCAP_CORE_IDX(admin->tgt_inst)); + lan_wr(VCAP_CORE_MAP_CORE_MAP_SET(1), + lan966x, VCAP_CORE_MAP(admin->tgt_inst)); + + __lan966x_vcap_range_init(lan966x, admin, admin->first_valid_addr, + admin->last_valid_addr - + admin->first_valid_addr); +} int lan966x_vcap_init(struct lan966x *lan966x) { + struct lan966x_vcap_inst *cfg; struct vcap_control *ctrl; + struct vcap_admin *admin; ctrl = kzalloc(sizeof(*ctrl), GFP_KERNEL); if (!ctrl) @@ -15,6 +148,18 @@ int lan966x_vcap_init(struct lan966x *lan966x) ctrl->vcaps = lan966x_vcaps; ctrl->stats = &lan966x_vcap_stats; + INIT_LIST_HEAD(&ctrl->list); + for (int i = 0; i < ARRAY_SIZE(lan966x_vcap_inst_cfg); ++i) { + cfg = &lan966x_vcap_inst_cfg[i]; + + admin = lan966x_vcap_admin_alloc(lan966x, ctrl, cfg); + if (IS_ERR(admin)) + return PTR_ERR(admin); + + lan966x_vcap_block_init(lan966x, admin, cfg); + list_add_tail(&admin->list, &ctrl->list); + } + lan966x->vcap_ctrl = ctrl; return 0; @@ -22,11 +167,18 @@ int lan966x_vcap_init(struct lan966x *lan966x) void lan966x_vcap_deinit(struct lan966x *lan966x) { + struct vcap_admin *admin, *admin_next; struct vcap_control *ctrl; ctrl = lan966x->vcap_ctrl; if (!ctrl) return; + list_for_each_entry_safe(admin, admin_next, &ctrl->list, list) { + vcap_del_rules(ctrl, admin); + list_del(&admin->list); + lan966x_vcap_admin_free(admin); + } + kfree(ctrl); } From 61caac2d1ab51a188f3439d0645b756fde317c1c Mon Sep 17 00:00:00 2001 From: Horatiu Vultur Date: Fri, 25 Nov 2022 10:50:08 +0100 Subject: [PATCH 1295/2002] net: lan966x: add tc matchall goto action Extend matchall with action goto. This is needed to enable the lookup in the VCAP. It is needed to connect chain 0 to a chain that is recognized by the HW. Signed-off-by: Horatiu Vultur Signed-off-by: Paolo Abeni --- .../net/ethernet/microchip/lan966x/Makefile | 2 +- .../ethernet/microchip/lan966x/lan966x_goto.c | 54 +++++++++++++++++++ .../ethernet/microchip/lan966x/lan966x_main.h | 9 ++++ .../microchip/lan966x/lan966x_tc_matchall.c | 6 +++ 4 files changed, 70 insertions(+), 1 deletion(-) create mode 100644 drivers/net/ethernet/microchip/lan966x/lan966x_goto.c diff --git a/drivers/net/ethernet/microchip/lan966x/Makefile b/drivers/net/ethernet/microchip/lan966x/Makefile index 23781149f7a9..56afd694f3c7 100644 --- a/drivers/net/ethernet/microchip/lan966x/Makefile +++ b/drivers/net/ethernet/microchip/lan966x/Makefile @@ -13,7 +13,7 @@ lan966x-switch-objs := lan966x_main.o lan966x_phylink.o lan966x_port.o \ lan966x_tbf.o lan966x_cbs.o lan966x_ets.o \ lan966x_tc_matchall.o lan966x_police.o lan966x_mirror.o \ lan966x_xdp.o lan966x_vcap_impl.o lan966x_vcap_ag_api.o \ - lan966x_tc_flower.o + lan966x_tc_flower.o lan966x_goto.o # Provide include files ccflags-y += -I$(srctree)/drivers/net/ethernet/microchip/vcap diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_goto.c b/drivers/net/ethernet/microchip/lan966x/lan966x_goto.c new file mode 100644 index 000000000000..bf0cfe24a8fc --- /dev/null +++ b/drivers/net/ethernet/microchip/lan966x/lan966x_goto.c @@ -0,0 +1,54 @@ +// SPDX-License-Identifier: GPL-2.0+ + +#include "lan966x_main.h" +#include "vcap_api_client.h" + +int lan966x_goto_port_add(struct lan966x_port *port, + struct flow_action_entry *act, + unsigned long goto_id, + struct netlink_ext_ack *extack) +{ + struct lan966x *lan966x = port->lan966x; + int err; + + err = vcap_enable_lookups(lan966x->vcap_ctrl, port->dev, + act->chain_index, goto_id, + true); + if (err == -EFAULT) { + NL_SET_ERR_MSG_MOD(extack, "Unsupported goto chain"); + return -EOPNOTSUPP; + } + + if (err == -EADDRINUSE) { + NL_SET_ERR_MSG_MOD(extack, "VCAP already enabled"); + return -EOPNOTSUPP; + } + + if (err) { + NL_SET_ERR_MSG_MOD(extack, "Could not enable VCAP lookups"); + return err; + } + + port->tc.goto_id = goto_id; + + return 0; +} + +int lan966x_goto_port_del(struct lan966x_port *port, + unsigned long goto_id, + struct netlink_ext_ack *extack) +{ + struct lan966x *lan966x = port->lan966x; + int err; + + err = vcap_enable_lookups(lan966x->vcap_ctrl, port->dev, 0, + goto_id, false); + if (err) { + NL_SET_ERR_MSG_MOD(extack, "Could not disable VCAP lookups"); + return err; + } + + port->tc.goto_id = 0; + + return 0; +} diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_main.h b/drivers/net/ethernet/microchip/lan966x/lan966x_main.h index 118745e7c22a..f2e45da7ffd4 100644 --- a/drivers/net/ethernet/microchip/lan966x/lan966x_main.h +++ b/drivers/net/ethernet/microchip/lan966x/lan966x_main.h @@ -320,6 +320,7 @@ struct lan966x_port_tc { unsigned long police_id; unsigned long ingress_mirror_id; unsigned long egress_mirror_id; + unsigned long goto_id; struct flow_stats police_stat; struct flow_stats mirror_stat; }; @@ -591,6 +592,14 @@ void lan966x_vcap_deinit(struct lan966x *lan966x); int lan966x_tc_flower(struct lan966x_port *port, struct flow_cls_offload *f); +int lan966x_goto_port_add(struct lan966x_port *port, + struct flow_action_entry *act, + unsigned long goto_id, + struct netlink_ext_ack *extack); +int lan966x_goto_port_del(struct lan966x_port *port, + unsigned long goto_id, + struct netlink_ext_ack *extack); + static inline void __iomem *lan_addr(void __iomem *base[], int id, int tinst, int tcnt, int gbase, int ginst, diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_tc_matchall.c b/drivers/net/ethernet/microchip/lan966x/lan966x_tc_matchall.c index 7368433b9277..a539abaad9b6 100644 --- a/drivers/net/ethernet/microchip/lan966x/lan966x_tc_matchall.c +++ b/drivers/net/ethernet/microchip/lan966x/lan966x_tc_matchall.c @@ -23,6 +23,9 @@ static int lan966x_tc_matchall_add(struct lan966x_port *port, case FLOW_ACTION_MIRRED: return lan966x_mirror_port_add(port, act, f->cookie, ingress, f->common.extack); + case FLOW_ACTION_GOTO: + return lan966x_goto_port_add(port, act, f->cookie, + f->common.extack); default: NL_SET_ERR_MSG_MOD(f->common.extack, "Unsupported action"); @@ -43,6 +46,9 @@ static int lan966x_tc_matchall_del(struct lan966x_port *port, f->cookie == port->tc.egress_mirror_id) { return lan966x_mirror_port_del(port, ingress, f->common.extack); + } else if (f->cookie == port->tc.goto_id) { + return lan966x_goto_port_del(port, f->cookie, + f->common.extack); } else { NL_SET_ERR_MSG_MOD(f->common.extack, "Unsupported action"); From 4426b78c626d6dd0a974263776d658d1ea8f0803 Mon Sep 17 00:00:00 2001 From: Horatiu Vultur Date: Fri, 25 Nov 2022 10:50:09 +0100 Subject: [PATCH 1296/2002] net: lan966x: Add port keyset config and callback interface Implement vcap_operations and enable default port keyset configuration for each port. Now it is possible actually write/read/move entries in the VCAP. Signed-off-by: Horatiu Vultur Signed-off-by: Paolo Abeni --- .../microchip/lan966x/lan966x_vcap_impl.c | 366 ++++++++++++++++++ 1 file changed, 366 insertions(+) diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_vcap_impl.c b/drivers/net/ethernet/microchip/lan966x/lan966x_vcap_impl.c index 08253dd4a306..44f40d914947 100644 --- a/drivers/net/ethernet/microchip/lan966x/lan966x_vcap_impl.c +++ b/drivers/net/ethernet/microchip/lan966x/lan966x_vcap_impl.c @@ -13,6 +13,13 @@ #define LAN966X_IS2_LOOKUPS 2 +enum vcap_is2_port_sel_ipv6 { + VCAP_IS2_PS_IPV6_TCPUDP_OTHER, + VCAP_IS2_PS_IPV6_STD, + VCAP_IS2_PS_IPV6_IP4_TCPUDP_IP4_OTHER, + VCAP_IS2_PS_IPV6_MAC_ETYPE, +}; + static struct lan966x_vcap_inst { enum vcap_type vtype; /* type of vcap */ int tgt_inst; /* hardware instance number */ @@ -73,6 +80,347 @@ static void __lan966x_vcap_range_init(struct lan966x *lan966x, lan966x_vcap_wait_update(lan966x, admin->tgt_inst); } +static int lan966x_vcap_cid_to_lookup(int cid) +{ + if (cid >= LAN966X_VCAP_CID_IS2_L1 && + cid < LAN966X_VCAP_CID_IS2_MAX) + return 1; + + return 0; +} + +static int +lan966x_vcap_is2_get_port_keysets(struct net_device *dev, int lookup, + struct vcap_keyset_list *keysetlist, + u16 l3_proto) +{ + struct lan966x_port *port = netdev_priv(dev); + struct lan966x *lan966x = port->lan966x; + bool found = false; + u32 val; + + /* Check if the port keyset selection is enabled */ + val = lan_rd(lan966x, ANA_VCAP_S2_CFG(port->chip_port)); + if (!ANA_VCAP_S2_CFG_ENA_GET(val)) + return -ENOENT; + + /* Collect all keysets for the port in a list */ + if (l3_proto == ETH_P_ALL) + vcap_keyset_list_add(keysetlist, VCAP_KFS_MAC_ETYPE); + + if (l3_proto == ETH_P_ALL || l3_proto == ETH_P_SNAP) { + if (ANA_VCAP_S2_CFG_SNAP_DIS_GET(val) & (BIT(0) << lookup)) + vcap_keyset_list_add(keysetlist, VCAP_KFS_MAC_LLC); + else + vcap_keyset_list_add(keysetlist, VCAP_KFS_MAC_SNAP); + + found = true; + } + + if (l3_proto == ETH_P_ALL || l3_proto == ETH_P_CFM) { + if (ANA_VCAP_S2_CFG_OAM_DIS_GET(val) & (BIT(0) << lookup)) + vcap_keyset_list_add(keysetlist, VCAP_KFS_MAC_ETYPE); + else + vcap_keyset_list_add(keysetlist, VCAP_KFS_OAM); + + found = true; + } + + if (l3_proto == ETH_P_ALL || l3_proto == ETH_P_ARP) { + if (ANA_VCAP_S2_CFG_ARP_DIS_GET(val) & (BIT(0) << lookup)) + vcap_keyset_list_add(keysetlist, VCAP_KFS_MAC_ETYPE); + else + vcap_keyset_list_add(keysetlist, VCAP_KFS_ARP); + + found = true; + } + + if (l3_proto == ETH_P_ALL || l3_proto == ETH_P_IP) { + if (ANA_VCAP_S2_CFG_IP_OTHER_DIS_GET(val) & (BIT(0) << lookup)) + vcap_keyset_list_add(keysetlist, VCAP_KFS_MAC_ETYPE); + else + vcap_keyset_list_add(keysetlist, VCAP_KFS_IP4_OTHER); + + if (ANA_VCAP_S2_CFG_IP_TCPUDP_DIS_GET(val) & (BIT(0) << lookup)) + vcap_keyset_list_add(keysetlist, VCAP_KFS_MAC_ETYPE); + else + vcap_keyset_list_add(keysetlist, VCAP_KFS_IP4_TCP_UDP); + + found = true; + } + + if (l3_proto == ETH_P_ALL || l3_proto == ETH_P_IPV6) { + switch (ANA_VCAP_S2_CFG_IP6_CFG_GET(val) & (0x3 << lookup)) { + case VCAP_IS2_PS_IPV6_TCPUDP_OTHER: + vcap_keyset_list_add(keysetlist, VCAP_KFS_IP6_OTHER); + vcap_keyset_list_add(keysetlist, VCAP_KFS_IP6_TCP_UDP); + break; + case VCAP_IS2_PS_IPV6_STD: + vcap_keyset_list_add(keysetlist, VCAP_KFS_IP6_STD); + break; + case VCAP_IS2_PS_IPV6_IP4_TCPUDP_IP4_OTHER: + vcap_keyset_list_add(keysetlist, VCAP_KFS_IP4_OTHER); + vcap_keyset_list_add(keysetlist, VCAP_KFS_IP4_TCP_UDP); + break; + case VCAP_IS2_PS_IPV6_MAC_ETYPE: + vcap_keyset_list_add(keysetlist, VCAP_KFS_MAC_ETYPE); + break; + } + + found = true; + } + + if (!found) + vcap_keyset_list_add(keysetlist, VCAP_KFS_MAC_ETYPE); + + return 0; +} + +static enum vcap_keyfield_set +lan966x_vcap_validate_keyset(struct net_device *dev, + struct vcap_admin *admin, + struct vcap_rule *rule, + struct vcap_keyset_list *kslist, + u16 l3_proto) +{ + struct vcap_keyset_list keysetlist = {}; + enum vcap_keyfield_set keysets[10] = {}; + int lookup; + int err; + + if (!kslist || kslist->cnt == 0) + return VCAP_KFS_NO_VALUE; + + lookup = lan966x_vcap_cid_to_lookup(rule->vcap_chain_id); + keysetlist.max = ARRAY_SIZE(keysets); + keysetlist.keysets = keysets; + err = lan966x_vcap_is2_get_port_keysets(dev, lookup, &keysetlist, + l3_proto); + if (err) + return VCAP_KFS_NO_VALUE; + + /* Check if there is a match and return the match */ + for (int i = 0; i < kslist->cnt; ++i) + for (int j = 0; j < keysetlist.cnt; ++j) + if (kslist->keysets[i] == keysets[j]) + return kslist->keysets[i]; + + return VCAP_KFS_NO_VALUE; +} + +static bool lan966x_vcap_is_first_chain(struct vcap_rule *rule) +{ + return (rule->vcap_chain_id >= LAN966X_VCAP_CID_IS2_L0 && + rule->vcap_chain_id < LAN966X_VCAP_CID_IS2_L1); +} + +static void lan966x_vcap_add_default_fields(struct net_device *dev, + struct vcap_admin *admin, + struct vcap_rule *rule) +{ + struct lan966x_port *port = netdev_priv(dev); + + vcap_rule_add_key_u32(rule, VCAP_KF_IF_IGR_PORT_MASK, 0, + ~BIT(port->chip_port)); + + if (lan966x_vcap_is_first_chain(rule)) + vcap_rule_add_key_bit(rule, VCAP_KF_LOOKUP_FIRST_IS, + VCAP_BIT_1); + else + vcap_rule_add_key_bit(rule, VCAP_KF_LOOKUP_FIRST_IS, + VCAP_BIT_0); +} + +static void lan966x_vcap_cache_erase(struct vcap_admin *admin) +{ + memset(admin->cache.keystream, 0, STREAMSIZE); + memset(admin->cache.maskstream, 0, STREAMSIZE); + memset(admin->cache.actionstream, 0, STREAMSIZE); + memset(&admin->cache.counter, 0, sizeof(admin->cache.counter)); +} + +static void lan966x_vcap_cache_write(struct net_device *dev, + struct vcap_admin *admin, + enum vcap_selection sel, + u32 start, + u32 count) +{ + struct lan966x_port *port = netdev_priv(dev); + struct lan966x *lan966x = port->lan966x; + u32 *keystr, *mskstr, *actstr; + + keystr = &admin->cache.keystream[start]; + mskstr = &admin->cache.maskstream[start]; + actstr = &admin->cache.actionstream[start]; + + switch (sel) { + case VCAP_SEL_ENTRY: + for (int i = 0; i < count; ++i) { + lan_wr(keystr[i] & mskstr[i], lan966x, + VCAP_ENTRY_DAT(admin->tgt_inst, i)); + lan_wr(~mskstr[i], lan966x, + VCAP_MASK_DAT(admin->tgt_inst, i)); + } + break; + case VCAP_SEL_ACTION: + for (int i = 0; i < count; ++i) + lan_wr(actstr[i], lan966x, + VCAP_ACTION_DAT(admin->tgt_inst, i)); + break; + case VCAP_SEL_COUNTER: + admin->cache.sticky = admin->cache.counter > 0; + lan_wr(admin->cache.counter, lan966x, + VCAP_CNT_DAT(admin->tgt_inst, 0)); + break; + default: + break; + } +} + +static void lan966x_vcap_cache_read(struct net_device *dev, + struct vcap_admin *admin, + enum vcap_selection sel, + u32 start, + u32 count) +{ + struct lan966x_port *port = netdev_priv(dev); + struct lan966x *lan966x = port->lan966x; + int instance = admin->tgt_inst; + u32 *keystr, *mskstr, *actstr; + + keystr = &admin->cache.keystream[start]; + mskstr = &admin->cache.maskstream[start]; + actstr = &admin->cache.actionstream[start]; + + if (sel & VCAP_SEL_ENTRY) { + for (int i = 0; i < count; ++i) { + keystr[i] = + lan_rd(lan966x, VCAP_ENTRY_DAT(instance, i)); + mskstr[i] = + ~lan_rd(lan966x, VCAP_MASK_DAT(instance, i)); + } + } + + if (sel & VCAP_SEL_ACTION) + for (int i = 0; i < count; ++i) + actstr[i] = + lan_rd(lan966x, VCAP_ACTION_DAT(instance, i)); + + if (sel & VCAP_SEL_COUNTER) { + admin->cache.counter = + lan_rd(lan966x, VCAP_CNT_DAT(instance, 0)); + admin->cache.sticky = admin->cache.counter > 0; + } +} + +static void lan966x_vcap_range_init(struct net_device *dev, + struct vcap_admin *admin, + u32 addr, + u32 count) +{ + struct lan966x_port *port = netdev_priv(dev); + struct lan966x *lan966x = port->lan966x; + + __lan966x_vcap_range_init(lan966x, admin, addr, count); +} + +static void lan966x_vcap_update(struct net_device *dev, + struct vcap_admin *admin, + enum vcap_command cmd, + enum vcap_selection sel, + u32 addr) +{ + struct lan966x_port *port = netdev_priv(dev); + struct lan966x *lan966x = port->lan966x; + bool clear; + + clear = (cmd == VCAP_CMD_INITIALIZE); + + lan_wr(VCAP_MV_CFG_MV_NUM_POS_SET(0) | + VCAP_MV_CFG_MV_SIZE_SET(0), + lan966x, VCAP_MV_CFG(admin->tgt_inst)); + + lan_wr(VCAP_UPDATE_CTRL_UPDATE_CMD_SET(cmd) | + VCAP_UPDATE_CTRL_UPDATE_ENTRY_DIS_SET((VCAP_SEL_ENTRY & sel) == 0) | + VCAP_UPDATE_CTRL_UPDATE_ACTION_DIS_SET((VCAP_SEL_ACTION & sel) == 0) | + VCAP_UPDATE_CTRL_UPDATE_CNT_DIS_SET((VCAP_SEL_COUNTER & sel) == 0) | + VCAP_UPDATE_CTRL_UPDATE_ADDR_SET(addr) | + VCAP_UPDATE_CTRL_CLEAR_CACHE_SET(clear) | + VCAP_UPDATE_CTRL_UPDATE_SHOT, + lan966x, VCAP_UPDATE_CTRL(admin->tgt_inst)); + + lan966x_vcap_wait_update(lan966x, admin->tgt_inst); +} + +static void lan966x_vcap_move(struct net_device *dev, + struct vcap_admin *admin, + u32 addr, int offset, int count) +{ + struct lan966x_port *port = netdev_priv(dev); + struct lan966x *lan966x = port->lan966x; + enum vcap_command cmd; + u16 mv_num_pos; + u16 mv_size; + + mv_size = count - 1; + if (offset > 0) { + mv_num_pos = offset - 1; + cmd = VCAP_CMD_MOVE_DOWN; + } else { + mv_num_pos = -offset - 1; + cmd = VCAP_CMD_MOVE_UP; + } + + lan_wr(VCAP_MV_CFG_MV_NUM_POS_SET(mv_num_pos) | + VCAP_MV_CFG_MV_SIZE_SET(mv_size), + lan966x, VCAP_MV_CFG(admin->tgt_inst)); + + lan_wr(VCAP_UPDATE_CTRL_UPDATE_CMD_SET(cmd) | + VCAP_UPDATE_CTRL_UPDATE_ENTRY_DIS_SET(0) | + VCAP_UPDATE_CTRL_UPDATE_ACTION_DIS_SET(0) | + VCAP_UPDATE_CTRL_UPDATE_CNT_DIS_SET(0) | + VCAP_UPDATE_CTRL_UPDATE_ADDR_SET(addr) | + VCAP_UPDATE_CTRL_CLEAR_CACHE_SET(false) | + VCAP_UPDATE_CTRL_UPDATE_SHOT, + lan966x, VCAP_UPDATE_CTRL(admin->tgt_inst)); + + lan966x_vcap_wait_update(lan966x, admin->tgt_inst); +} + +static int lan966x_vcap_port_info(struct net_device *dev, + struct vcap_admin *admin, + struct vcap_output_print *out) +{ + return 0; +} + +static int lan966x_vcap_enable(struct net_device *dev, + struct vcap_admin *admin, + bool enable) +{ + struct lan966x_port *port = netdev_priv(dev); + struct lan966x *lan966x = port->lan966x; + + lan_rmw(ANA_VCAP_S2_CFG_ENA_SET(enable), + ANA_VCAP_S2_CFG_ENA, + lan966x, ANA_VCAP_S2_CFG(port->chip_port)); + + return 0; +} + +static struct vcap_operations lan966x_vcap_ops = { + .validate_keyset = lan966x_vcap_validate_keyset, + .add_default_fields = lan966x_vcap_add_default_fields, + .cache_erase = lan966x_vcap_cache_erase, + .cache_write = lan966x_vcap_cache_write, + .cache_read = lan966x_vcap_cache_read, + .init = lan966x_vcap_range_init, + .update = lan966x_vcap_update, + .move = lan966x_vcap_move, + .port_info = lan966x_vcap_port_info, + .enable = lan966x_vcap_enable, +}; + static void lan966x_vcap_admin_free(struct vcap_admin *admin) { if (!admin) @@ -98,12 +446,19 @@ lan966x_vcap_admin_alloc(struct lan966x *lan966x, struct vcap_control *ctrl, mutex_init(&admin->lock); INIT_LIST_HEAD(&admin->list); INIT_LIST_HEAD(&admin->rules); + INIT_LIST_HEAD(&admin->enabled); + admin->vtype = cfg->vtype; admin->vinst = 0; + admin->w32be = true; + admin->tgt_inst = cfg->tgt_inst; + admin->lookups = cfg->lookups; admin->lookups_per_instance = cfg->lookups; + admin->first_cid = cfg->first_cid; admin->last_cid = cfg->last_cid; + admin->cache.keystream = kzalloc(STREAMSIZE, GFP_KERNEL); admin->cache.maskstream = kzalloc(STREAMSIZE, GFP_KERNEL); admin->cache.actionstream = kzalloc(STREAMSIZE, GFP_KERNEL); @@ -135,6 +490,13 @@ static void lan966x_vcap_block_init(struct lan966x *lan966x, admin->first_valid_addr); } +static void lan966x_vcap_port_key_deselection(struct lan966x *lan966x, + struct vcap_admin *admin) +{ + for (int p = 0; p < lan966x->num_phys_ports; ++p) + lan_wr(0, lan966x, ANA_VCAP_S2_CFG(p)); +} + int lan966x_vcap_init(struct lan966x *lan966x) { struct lan966x_vcap_inst *cfg; @@ -147,6 +509,7 @@ int lan966x_vcap_init(struct lan966x *lan966x) ctrl->vcaps = lan966x_vcaps; ctrl->stats = &lan966x_vcap_stats; + ctrl->ops = &lan966x_vcap_ops; INIT_LIST_HEAD(&ctrl->list); for (int i = 0; i < ARRAY_SIZE(lan966x_vcap_inst_cfg); ++i) { @@ -157,6 +520,8 @@ int lan966x_vcap_init(struct lan966x *lan966x) return PTR_ERR(admin); lan966x_vcap_block_init(lan966x, admin, cfg); + lan966x_vcap_port_key_deselection(lan966x, admin); + list_add_tail(&admin->list, &ctrl->list); } @@ -175,6 +540,7 @@ void lan966x_vcap_deinit(struct lan966x *lan966x) return; list_for_each_entry_safe(admin, admin_next, &ctrl->list, list) { + lan966x_vcap_port_key_deselection(lan966x, admin); vcap_del_rules(ctrl, admin); list_del(&admin->list); lan966x_vcap_admin_free(admin); From 4f141e3671233c29c973a3245f1f360d62b3c8eb Mon Sep 17 00:00:00 2001 From: Horatiu Vultur Date: Fri, 25 Nov 2022 10:50:10 +0100 Subject: [PATCH 1297/2002] net: microchip: vcap: Implement w32be On lan966x the layout of the vcap memory is different than on sparx5. Signed-off-by: Horatiu Vultur Signed-off-by: Paolo Abeni --- .../net/ethernet/microchip/vcap/vcap_api.c | 116 +++++++++++++++++- 1 file changed, 112 insertions(+), 4 deletions(-) diff --git a/drivers/net/ethernet/microchip/vcap/vcap_api.c b/drivers/net/ethernet/microchip/vcap/vcap_api.c index 2f171d0b9187..93efa8243b02 100644 --- a/drivers/net/ethernet/microchip/vcap/vcap_api.c +++ b/drivers/net/ethernet/microchip/vcap/vcap_api.c @@ -8,6 +8,28 @@ #include "vcap_api_private.h" +static int keyfield_size_table[] = { + [VCAP_FIELD_BIT] = sizeof(struct vcap_u1_key), + [VCAP_FIELD_U32] = sizeof(struct vcap_u32_key), + [VCAP_FIELD_U48] = sizeof(struct vcap_u48_key), + [VCAP_FIELD_U56] = sizeof(struct vcap_u56_key), + [VCAP_FIELD_U64] = sizeof(struct vcap_u64_key), + [VCAP_FIELD_U72] = sizeof(struct vcap_u72_key), + [VCAP_FIELD_U112] = sizeof(struct vcap_u112_key), + [VCAP_FIELD_U128] = sizeof(struct vcap_u128_key), +}; + +static int actionfield_size_table[] = { + [VCAP_FIELD_BIT] = sizeof(struct vcap_u1_action), + [VCAP_FIELD_U32] = sizeof(struct vcap_u32_action), + [VCAP_FIELD_U48] = sizeof(struct vcap_u48_action), + [VCAP_FIELD_U56] = sizeof(struct vcap_u56_action), + [VCAP_FIELD_U64] = sizeof(struct vcap_u64_action), + [VCAP_FIELD_U72] = sizeof(struct vcap_u72_action), + [VCAP_FIELD_U112] = sizeof(struct vcap_u112_action), + [VCAP_FIELD_U128] = sizeof(struct vcap_u128_action), +}; + /* Moving a rule in the VCAP address space */ struct vcap_rule_move { int addr; /* address to move */ @@ -1312,12 +1334,67 @@ const struct vcap_field *vcap_lookup_keyfield(struct vcap_rule *rule, } EXPORT_SYMBOL_GPL(vcap_lookup_keyfield); +/* Copy data from src to dst but reverse the data in chunks of 32bits. + * For example if src is 00:11:22:33:44:55 where 55 is LSB the dst will + * have the value 22:33:44:55:00:11. + */ +static void vcap_copy_to_w32be(u8 *dst, u8 *src, int size) +{ + for (int idx = 0; idx < size; ++idx) { + int first_byte_index = 0; + int nidx; + + first_byte_index = size - (((idx >> 2) + 1) << 2); + if (first_byte_index < 0) + first_byte_index = 0; + nidx = idx + first_byte_index - (idx & ~0x3); + dst[nidx] = src[idx]; + } +} + static void vcap_copy_from_client_keyfield(struct vcap_rule *rule, struct vcap_client_keyfield *field, struct vcap_client_keyfield_data *data) { - /* This will be expanded later to handle different vcap memory layouts */ - memcpy(&field->data, data, sizeof(field->data)); + struct vcap_rule_internal *ri = to_intrule(rule); + int size; + + if (!ri->admin->w32be) { + memcpy(&field->data, data, sizeof(field->data)); + return; + } + + size = keyfield_size_table[field->ctrl.type] / 2; + switch (field->ctrl.type) { + case VCAP_FIELD_BIT: + case VCAP_FIELD_U32: + memcpy(&field->data, data, sizeof(field->data)); + break; + case VCAP_FIELD_U48: + vcap_copy_to_w32be(field->data.u48.value, data->u48.value, size); + vcap_copy_to_w32be(field->data.u48.mask, data->u48.mask, size); + break; + case VCAP_FIELD_U56: + vcap_copy_to_w32be(field->data.u56.value, data->u56.value, size); + vcap_copy_to_w32be(field->data.u56.mask, data->u56.mask, size); + break; + case VCAP_FIELD_U64: + vcap_copy_to_w32be(field->data.u64.value, data->u64.value, size); + vcap_copy_to_w32be(field->data.u64.mask, data->u64.mask, size); + break; + case VCAP_FIELD_U72: + vcap_copy_to_w32be(field->data.u72.value, data->u72.value, size); + vcap_copy_to_w32be(field->data.u72.mask, data->u72.mask, size); + break; + case VCAP_FIELD_U112: + vcap_copy_to_w32be(field->data.u112.value, data->u112.value, size); + vcap_copy_to_w32be(field->data.u112.mask, data->u112.mask, size); + break; + case VCAP_FIELD_U128: + vcap_copy_to_w32be(field->data.u128.value, data->u128.value, size); + vcap_copy_to_w32be(field->data.u128.mask, data->u128.mask, size); + break; + }; } /* Check if the keyfield is already in the rule */ @@ -1475,8 +1552,39 @@ static void vcap_copy_from_client_actionfield(struct vcap_rule *rule, struct vcap_client_actionfield *field, struct vcap_client_actionfield_data *data) { - /* This will be expanded later to handle different vcap memory layouts */ - memcpy(&field->data, data, sizeof(field->data)); + struct vcap_rule_internal *ri = to_intrule(rule); + int size; + + if (!ri->admin->w32be) { + memcpy(&field->data, data, sizeof(field->data)); + return; + } + + size = actionfield_size_table[field->ctrl.type]; + switch (field->ctrl.type) { + case VCAP_FIELD_BIT: + case VCAP_FIELD_U32: + memcpy(&field->data, data, sizeof(field->data)); + break; + case VCAP_FIELD_U48: + vcap_copy_to_w32be(field->data.u48.value, data->u48.value, size); + break; + case VCAP_FIELD_U56: + vcap_copy_to_w32be(field->data.u56.value, data->u56.value, size); + break; + case VCAP_FIELD_U64: + vcap_copy_to_w32be(field->data.u64.value, data->u64.value, size); + break; + case VCAP_FIELD_U72: + vcap_copy_to_w32be(field->data.u72.value, data->u72.value, size); + break; + case VCAP_FIELD_U112: + vcap_copy_to_w32be(field->data.u112.value, data->u112.value, size); + break; + case VCAP_FIELD_U128: + vcap_copy_to_w32be(field->data.u128.value, data->u128.value, size); + break; + }; } /* Check if the actionfield is already in the rule */ From 7666dbec7268458505d44ef3ae22fd9181c09b01 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Fri, 25 Nov 2022 11:02:55 +0100 Subject: [PATCH 1298/2002] net: devlink: add WARN_ON_ONCE to check return value of unregister_netdevice_notifier_net() call As the return value is not 0 only in case there is no such notifier block registered, add a WARN_ON_ONCE() to yell about it. Suggested-by: Ido Schimmel Signed-off-by: Jiri Pirko Reviewed-by: Ido Schimmel Link: https://lore.kernel.org/r/20221125100255.1786741-1-jiri@resnulli.us Signed-off-by: Paolo Abeni --- net/core/devlink.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/core/devlink.c b/net/core/devlink.c index cea154ddce7a..0e10a8a68c5e 100644 --- a/net/core/devlink.c +++ b/net/core/devlink.c @@ -9907,8 +9907,8 @@ void devlink_free(struct devlink *devlink) xa_destroy(&devlink->snapshot_ids); - unregister_netdevice_notifier_net(devlink_net(devlink), - &devlink->netdevice_nb); + WARN_ON_ONCE(unregister_netdevice_notifier_net(devlink_net(devlink), + &devlink->netdevice_nb)); xa_erase(&devlinks, devlink->index); From a61474c41e8c530c54a26db4f5434f050ef7718d Mon Sep 17 00:00:00 2001 From: Yu Xiao Date: Fri, 25 Nov 2022 12:30:30 +0100 Subject: [PATCH 1299/2002] nfp: ethtool: support reporting link modes Add support for reporting link modes, including `Supported link modes` and `Advertised link modes`, via ethtool $DEV. A new command `SPCODE_READ_MEDIA` is added to read info from management firmware. Also, the mapping table `nfp_eth_media_table` associates the link modes between NFP and kernel. Both of them help to support this ability. Signed-off-by: Yu Xiao Reviewed-by: Louis Peens Signed-off-by: Simon Horman Reviewed-by: Alexander Lobakin Link: https://lore.kernel.org/r/20221125113030.141642-1-simon.horman@corigine.com Signed-off-by: Paolo Abeni --- drivers/net/ethernet/netronome/nfp/nfp_main.h | 1 + .../ethernet/netronome/nfp/nfp_net_ethtool.c | 73 +++++++++++++++++++ .../ethernet/netronome/nfp/nfpcore/nfp_nsp.c | 17 +++++ .../ethernet/netronome/nfp/nfpcore/nfp_nsp.h | 56 ++++++++++++++ .../netronome/nfp/nfpcore/nfp_nsp_eth.c | 26 +++++++ 5 files changed, 173 insertions(+) diff --git a/drivers/net/ethernet/netronome/nfp/nfp_main.h b/drivers/net/ethernet/netronome/nfp/nfp_main.h index 5a18af672e6b..14a751bfe1fe 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_main.h +++ b/drivers/net/ethernet/netronome/nfp/nfp_main.h @@ -27,6 +27,7 @@ struct nfp_hwinfo; struct nfp_mip; struct nfp_net; struct nfp_nsp_identify; +struct nfp_eth_media_buf; struct nfp_port; struct nfp_rtsym; struct nfp_rtsym_table; diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c b/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c index 0058ba2b3505..e0cc60d12dda 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c @@ -293,6 +293,76 @@ nfp_net_set_fec_link_mode(struct nfp_eth_table_port *eth_port, } } +static const u16 nfp_eth_media_table[] = { + [NFP_MEDIA_1000BASE_CX] = ETHTOOL_LINK_MODE_1000baseKX_Full_BIT, + [NFP_MEDIA_1000BASE_KX] = ETHTOOL_LINK_MODE_1000baseKX_Full_BIT, + [NFP_MEDIA_10GBASE_KX4] = ETHTOOL_LINK_MODE_10000baseKX4_Full_BIT, + [NFP_MEDIA_10GBASE_KR] = ETHTOOL_LINK_MODE_10000baseKR_Full_BIT, + [NFP_MEDIA_10GBASE_CX4] = ETHTOOL_LINK_MODE_10000baseKX4_Full_BIT, + [NFP_MEDIA_10GBASE_CR] = ETHTOOL_LINK_MODE_10000baseCR_Full_BIT, + [NFP_MEDIA_10GBASE_SR] = ETHTOOL_LINK_MODE_10000baseSR_Full_BIT, + [NFP_MEDIA_10GBASE_ER] = ETHTOOL_LINK_MODE_10000baseER_Full_BIT, + [NFP_MEDIA_25GBASE_KR] = ETHTOOL_LINK_MODE_25000baseKR_Full_BIT, + [NFP_MEDIA_25GBASE_KR_S] = ETHTOOL_LINK_MODE_25000baseKR_Full_BIT, + [NFP_MEDIA_25GBASE_CR] = ETHTOOL_LINK_MODE_25000baseCR_Full_BIT, + [NFP_MEDIA_25GBASE_CR_S] = ETHTOOL_LINK_MODE_25000baseCR_Full_BIT, + [NFP_MEDIA_25GBASE_SR] = ETHTOOL_LINK_MODE_25000baseSR_Full_BIT, + [NFP_MEDIA_40GBASE_CR4] = ETHTOOL_LINK_MODE_40000baseCR4_Full_BIT, + [NFP_MEDIA_40GBASE_KR4] = ETHTOOL_LINK_MODE_40000baseKR4_Full_BIT, + [NFP_MEDIA_40GBASE_SR4] = ETHTOOL_LINK_MODE_40000baseSR4_Full_BIT, + [NFP_MEDIA_40GBASE_LR4] = ETHTOOL_LINK_MODE_40000baseLR4_Full_BIT, + [NFP_MEDIA_50GBASE_KR] = ETHTOOL_LINK_MODE_50000baseKR_Full_BIT, + [NFP_MEDIA_50GBASE_SR] = ETHTOOL_LINK_MODE_50000baseSR_Full_BIT, + [NFP_MEDIA_50GBASE_CR] = ETHTOOL_LINK_MODE_50000baseCR_Full_BIT, + [NFP_MEDIA_50GBASE_LR] = ETHTOOL_LINK_MODE_50000baseLR_ER_FR_Full_BIT, + [NFP_MEDIA_50GBASE_ER] = ETHTOOL_LINK_MODE_50000baseLR_ER_FR_Full_BIT, + [NFP_MEDIA_50GBASE_FR] = ETHTOOL_LINK_MODE_50000baseLR_ER_FR_Full_BIT, + [NFP_MEDIA_100GBASE_KR4] = ETHTOOL_LINK_MODE_100000baseKR4_Full_BIT, + [NFP_MEDIA_100GBASE_SR4] = ETHTOOL_LINK_MODE_100000baseSR4_Full_BIT, + [NFP_MEDIA_100GBASE_CR4] = ETHTOOL_LINK_MODE_100000baseCR4_Full_BIT, + [NFP_MEDIA_100GBASE_KP4] = ETHTOOL_LINK_MODE_100000baseKR4_Full_BIT, + [NFP_MEDIA_100GBASE_CR10] = ETHTOOL_LINK_MODE_100000baseCR4_Full_BIT, +}; + +static void nfp_add_media_link_mode(struct nfp_port *port, + struct nfp_eth_table_port *eth_port, + struct ethtool_link_ksettings *cmd) +{ + u64 supported_modes[2], advertised_modes[2]; + struct nfp_eth_media_buf ethm = { + .eth_index = eth_port->eth_index, + }; + struct nfp_cpp *cpp = port->app->cpp; + + if (nfp_eth_read_media(cpp, ðm)) + return; + + for (u32 i = 0; i < 2; i++) { + supported_modes[i] = le64_to_cpu(ethm.supported_modes[i]); + advertised_modes[i] = le64_to_cpu(ethm.advertised_modes[i]); + } + + for (u32 i = 0; i < NFP_MEDIA_LINK_MODES_NUMBER; i++) { + if (i < 64) { + if (supported_modes[0] & BIT_ULL(i)) + __set_bit(nfp_eth_media_table[i], + cmd->link_modes.supported); + + if (advertised_modes[0] & BIT_ULL(i)) + __set_bit(nfp_eth_media_table[i], + cmd->link_modes.advertising); + } else { + if (supported_modes[1] & BIT_ULL(i - 64)) + __set_bit(nfp_eth_media_table[i], + cmd->link_modes.supported); + + if (advertised_modes[1] & BIT_ULL(i - 64)) + __set_bit(nfp_eth_media_table[i], + cmd->link_modes.advertising); + } + } +} + /** * nfp_net_get_link_ksettings - Get Link Speed settings * @netdev: network interface device structure @@ -311,6 +381,8 @@ nfp_net_get_link_ksettings(struct net_device *netdev, u16 sts; /* Init to unknowns */ + ethtool_link_ksettings_zero_link_mode(cmd, supported); + ethtool_link_ksettings_zero_link_mode(cmd, advertising); ethtool_link_ksettings_add_link_mode(cmd, supported, FIBRE); cmd->base.port = PORT_OTHER; cmd->base.speed = SPEED_UNKNOWN; @@ -321,6 +393,7 @@ nfp_net_get_link_ksettings(struct net_device *netdev, if (eth_port) { ethtool_link_ksettings_add_link_mode(cmd, supported, Pause); ethtool_link_ksettings_add_link_mode(cmd, advertising, Pause); + nfp_add_media_link_mode(port, eth_port, cmd); if (eth_port->supp_aneg) { ethtool_link_ksettings_add_link_mode(cmd, supported, Autoneg); if (eth_port->aneg == NFP_ANEG_AUTO) { diff --git a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp.c b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp.c index 730fea214b8a..7136bc48530b 100644 --- a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp.c +++ b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp.c @@ -100,6 +100,7 @@ enum nfp_nsp_cmd { SPCODE_FW_LOADED = 19, /* Is application firmware loaded */ SPCODE_VERSIONS = 21, /* Report FW versions */ SPCODE_READ_SFF_EEPROM = 22, /* Read module EEPROM */ + SPCODE_READ_MEDIA = 23, /* Get either the supported or advertised media for a port */ }; struct nfp_nsp_dma_buf { @@ -1100,4 +1101,20 @@ int nfp_nsp_read_module_eeprom(struct nfp_nsp *state, int eth_index, kfree(buf); return ret; +}; + +int nfp_nsp_read_media(struct nfp_nsp *state, void *buf, unsigned int size) +{ + struct nfp_nsp_command_buf_arg media = { + { + .code = SPCODE_READ_MEDIA, + .option = size, + }, + .in_buf = buf, + .in_size = size, + .out_buf = buf, + .out_size = size, + }; + + return nfp_nsp_command_buf(state, &media); } diff --git a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp.h b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp.h index 992d72ac98d3..8f5cab0032d0 100644 --- a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp.h +++ b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp.h @@ -65,6 +65,11 @@ static inline bool nfp_nsp_has_read_module_eeprom(struct nfp_nsp *state) return nfp_nsp_get_abi_ver_minor(state) > 28; } +static inline bool nfp_nsp_has_read_media(struct nfp_nsp *state) +{ + return nfp_nsp_get_abi_ver_minor(state) > 33; +} + enum nfp_eth_interface { NFP_INTERFACE_NONE = 0, NFP_INTERFACE_SFP = 1, @@ -97,6 +102,47 @@ enum nfp_eth_fec { NFP_FEC_DISABLED_BIT, }; +/* link modes about RJ45 haven't been used, so there's no mapping to them */ +enum nfp_ethtool_link_mode_list { + NFP_MEDIA_W0_RJ45_10M, + NFP_MEDIA_W0_RJ45_10M_HD, + NFP_MEDIA_W0_RJ45_100M, + NFP_MEDIA_W0_RJ45_100M_HD, + NFP_MEDIA_W0_RJ45_1G, + NFP_MEDIA_W0_RJ45_2P5G, + NFP_MEDIA_W0_RJ45_5G, + NFP_MEDIA_W0_RJ45_10G, + NFP_MEDIA_1000BASE_CX, + NFP_MEDIA_1000BASE_KX, + NFP_MEDIA_10GBASE_KX4, + NFP_MEDIA_10GBASE_KR, + NFP_MEDIA_10GBASE_CX4, + NFP_MEDIA_10GBASE_CR, + NFP_MEDIA_10GBASE_SR, + NFP_MEDIA_10GBASE_ER, + NFP_MEDIA_25GBASE_KR, + NFP_MEDIA_25GBASE_KR_S, + NFP_MEDIA_25GBASE_CR, + NFP_MEDIA_25GBASE_CR_S, + NFP_MEDIA_25GBASE_SR, + NFP_MEDIA_40GBASE_CR4, + NFP_MEDIA_40GBASE_KR4, + NFP_MEDIA_40GBASE_SR4, + NFP_MEDIA_40GBASE_LR4, + NFP_MEDIA_50GBASE_KR, + NFP_MEDIA_50GBASE_SR, + NFP_MEDIA_50GBASE_CR, + NFP_MEDIA_50GBASE_LR, + NFP_MEDIA_50GBASE_ER, + NFP_MEDIA_50GBASE_FR, + NFP_MEDIA_100GBASE_KR4, + NFP_MEDIA_100GBASE_SR4, + NFP_MEDIA_100GBASE_CR4, + NFP_MEDIA_100GBASE_KP4, + NFP_MEDIA_100GBASE_CR10, + NFP_MEDIA_LINK_MODES_NUMBER +}; + #define NFP_FEC_AUTO BIT(NFP_FEC_AUTO_BIT) #define NFP_FEC_BASER BIT(NFP_FEC_BASER_BIT) #define NFP_FEC_REED_SOLOMON BIT(NFP_FEC_REED_SOLOMON_BIT) @@ -256,6 +302,16 @@ enum nfp_nsp_sensor_id { int nfp_hwmon_read_sensor(struct nfp_cpp *cpp, enum nfp_nsp_sensor_id id, long *val); +struct nfp_eth_media_buf { + u8 eth_index; + u8 reserved[7]; + __le64 supported_modes[2]; + __le64 advertised_modes[2]; +}; + +int nfp_nsp_read_media(struct nfp_nsp *state, void *buf, unsigned int size); +int nfp_eth_read_media(struct nfp_cpp *cpp, struct nfp_eth_media_buf *ethm); + #define NFP_NSP_VERSION_BUFSZ 1024 /* reasonable size, not in the ABI */ enum nfp_nsp_versions { diff --git a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp_eth.c b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp_eth.c index bb64efec4c46..570ac1bb2122 100644 --- a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp_eth.c +++ b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp_eth.c @@ -647,3 +647,29 @@ int __nfp_eth_set_split(struct nfp_nsp *nsp, unsigned int lanes) return NFP_ETH_SET_BIT_CONFIG(nsp, NSP_ETH_RAW_PORT, NSP_ETH_PORT_LANES, lanes, NSP_ETH_CTRL_SET_LANES); } + +int nfp_eth_read_media(struct nfp_cpp *cpp, struct nfp_eth_media_buf *ethm) +{ + struct nfp_nsp *nsp; + int ret; + + nsp = nfp_nsp_open(cpp); + if (IS_ERR(nsp)) { + nfp_err(cpp, "Failed to access the NSP: %pe\n", nsp); + return PTR_ERR(nsp); + } + + if (!nfp_nsp_has_read_media(nsp)) { + nfp_warn(cpp, "Reading media link modes not supported. Please update flash\n"); + ret = -EOPNOTSUPP; + goto exit_close_nsp; + } + + ret = nfp_nsp_read_media(nsp, ethm, sizeof(*ethm)); + if (ret) + nfp_err(cpp, "Reading media link modes failed: %pe\n", ERR_PTR(ret)); + +exit_close_nsp: + nfp_nsp_close(nsp); + return ret; +} From 51147284eb7d685a689a5d1b7772faec278a2338 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Tue, 29 Nov 2022 14:55:34 +0100 Subject: [PATCH 1300/2002] ieee802154: Advertize coordinators discovery Let's introduce the basics for advertizing discovered PANs and coordinators, which is: - A new "scan" netlink message group. - A couple of netlink command/attribute. - The main netlink helper to send a netlink message with all the necessary information to forward the main information to the user. Two netlink attributes are proactively added to support future UWB complex channels, but are not actually used yet. Co-developed-by: David Girault Signed-off-by: David Girault Signed-off-by: Miquel Raynal Acked-by: Alexander Aring Link: https://lore.kernel.org/r/20221129135535.532513-2-miquel.raynal@bootlin.com Signed-off-by: Stefan Schmidt --- include/net/cfg802154.h | 18 +++++++ include/net/nl802154.h | 43 ++++++++++++++++ net/ieee802154/nl802154.c | 103 ++++++++++++++++++++++++++++++++++++++ net/ieee802154/nl802154.h | 2 + 4 files changed, 166 insertions(+) diff --git a/include/net/cfg802154.h b/include/net/cfg802154.h index e1481f9cf049..d09c393d229f 100644 --- a/include/net/cfg802154.h +++ b/include/net/cfg802154.h @@ -260,6 +260,24 @@ struct ieee802154_addr { }; }; +/** + * struct ieee802154_coord_desc - Coordinator descriptor + * @addr: PAN ID and coordinator address + * @page: page this coordinator is using + * @channel: channel this coordinator is using + * @superframe_spec: SuperFrame specification as received + * @link_quality: link quality indicator at which the beacon was received + * @gts_permit: the coordinator accepts GTS requests + */ +struct ieee802154_coord_desc { + struct ieee802154_addr addr; + u8 page; + u8 channel; + u16 superframe_spec; + u8 link_quality; + bool gts_permit; +}; + struct ieee802154_llsec_key_id { u8 mode; u8 id; diff --git a/include/net/nl802154.h b/include/net/nl802154.h index f5850b569c52..b79a89d5207c 100644 --- a/include/net/nl802154.h +++ b/include/net/nl802154.h @@ -72,6 +72,8 @@ enum nl802154_commands { NL802154_CMD_NEW_SEC_LEVEL, NL802154_CMD_DEL_SEC_LEVEL, + NL802154_CMD_SCAN_EVENT, + /* add new commands above here */ /* used to define NL802154_CMD_MAX below */ @@ -131,6 +133,8 @@ enum nl802154_attrs { NL802154_ATTR_PID, NL802154_ATTR_NETNS_FD, + NL802154_ATTR_COORDINATOR, + /* add attributes here, update the policy in nl802154.c */ #ifdef CONFIG_IEEE802154_NL802154_EXPERIMENTAL @@ -216,6 +220,45 @@ enum nl802154_wpan_phy_capability_attr { NL802154_CAP_ATTR_MAX = __NL802154_CAP_ATTR_AFTER_LAST - 1 }; +/** + * enum nl802154_coord - Netlink attributes for a coord + * + * @__NL802154_COORD_INVALID: invalid + * @NL802154_COORD_PANID: PANID of the coordinator (2 bytes) + * @NL802154_COORD_ADDR: coordinator address, (8 bytes or 2 bytes) + * @NL802154_COORD_CHANNEL: channel number, related to @NL802154_COORD_PAGE (u8) + * @NL802154_COORD_PAGE: channel page, related to @NL802154_COORD_CHANNEL (u8) + * @NL802154_COORD_PREAMBLE_CODE: Preamble code used when the beacon was received, + * this is PHY dependent and optional (u8) + * @NL802154_COORD_MEAN_PRF: Mean PRF used when the beacon was received, + * this is PHY dependent and optional (u8) + * @NL802154_COORD_SUPERFRAME_SPEC: superframe specification of the PAN (u16) + * @NL802154_COORD_LINK_QUALITY: signal quality of beacon in unspecified units, + * scaled to 0..255 (u8) + * @NL802154_COORD_GTS_PERMIT: set to true if GTS is permitted on this PAN + * @NL802154_COORD_PAYLOAD_DATA: binary data containing the raw data from the + * frame payload, (only if beacon or probe response had data) + * @NL802154_COORD_PAD: attribute used for padding for 64-bit alignment + * @NL802154_COORD_MAX: highest coordinator attribute + */ +enum nl802154_coord { + __NL802154_COORD_INVALID, + NL802154_COORD_PANID, + NL802154_COORD_ADDR, + NL802154_COORD_CHANNEL, + NL802154_COORD_PAGE, + NL802154_COORD_PREAMBLE_CODE, + NL802154_COORD_MEAN_PRF, + NL802154_COORD_SUPERFRAME_SPEC, + NL802154_COORD_LINK_QUALITY, + NL802154_COORD_GTS_PERMIT, + NL802154_COORD_PAYLOAD_DATA, + NL802154_COORD_PAD, + + /* keep last */ + NL802154_COORD_MAX, +}; + /** * enum nl802154_cca_modes - cca modes * diff --git a/net/ieee802154/nl802154.c b/net/ieee802154/nl802154.c index 38c4f3cb010e..80dc73182785 100644 --- a/net/ieee802154/nl802154.c +++ b/net/ieee802154/nl802154.c @@ -26,10 +26,12 @@ static struct genl_family nl802154_fam; /* multicast groups */ enum nl802154_multicast_groups { NL802154_MCGRP_CONFIG, + NL802154_MCGRP_SCAN, }; static const struct genl_multicast_group nl802154_mcgrps[] = { [NL802154_MCGRP_CONFIG] = { .name = "config", }, + [NL802154_MCGRP_SCAN] = { .name = "scan", }, }; /* returns ERR_PTR values */ @@ -216,6 +218,9 @@ static const struct nla_policy nl802154_policy[NL802154_ATTR_MAX+1] = { [NL802154_ATTR_PID] = { .type = NLA_U32 }, [NL802154_ATTR_NETNS_FD] = { .type = NLA_U32 }, + + [NL802154_ATTR_COORDINATOR] = { .type = NLA_NESTED }, + #ifdef CONFIG_IEEE802154_NL802154_EXPERIMENTAL [NL802154_ATTR_SEC_ENABLED] = { .type = NLA_U8, }, [NL802154_ATTR_SEC_OUT_LEVEL] = { .type = NLA_U32, }, @@ -1281,6 +1286,104 @@ static int nl802154_wpan_phy_netns(struct sk_buff *skb, struct genl_info *info) return err; } +static int nl802154_prep_scan_event_msg(struct sk_buff *msg, + struct cfg802154_registered_device *rdev, + struct wpan_dev *wpan_dev, + u32 portid, u32 seq, int flags, u8 cmd, + struct ieee802154_coord_desc *desc) +{ + struct nlattr *nla; + void *hdr; + + hdr = nl802154hdr_put(msg, portid, seq, flags, cmd); + if (!hdr) + return -ENOBUFS; + + if (nla_put_u32(msg, NL802154_ATTR_WPAN_PHY, rdev->wpan_phy_idx)) + goto nla_put_failure; + + if (wpan_dev->netdev && + nla_put_u32(msg, NL802154_ATTR_IFINDEX, wpan_dev->netdev->ifindex)) + goto nla_put_failure; + + if (nla_put_u64_64bit(msg, NL802154_ATTR_WPAN_DEV, + wpan_dev_id(wpan_dev), NL802154_ATTR_PAD)) + goto nla_put_failure; + + nla = nla_nest_start_noflag(msg, NL802154_ATTR_COORDINATOR); + if (!nla) + goto nla_put_failure; + + if (nla_put(msg, NL802154_COORD_PANID, IEEE802154_PAN_ID_LEN, + &desc->addr.pan_id)) + goto nla_put_failure; + + if (desc->addr.mode == IEEE802154_ADDR_SHORT) { + if (nla_put(msg, NL802154_COORD_ADDR, + IEEE802154_SHORT_ADDR_LEN, + &desc->addr.short_addr)) + goto nla_put_failure; + } else { + if (nla_put(msg, NL802154_COORD_ADDR, + IEEE802154_EXTENDED_ADDR_LEN, + &desc->addr.extended_addr)) + goto nla_put_failure; + } + + if (nla_put_u8(msg, NL802154_COORD_CHANNEL, desc->channel)) + goto nla_put_failure; + + if (nla_put_u8(msg, NL802154_COORD_PAGE, desc->page)) + goto nla_put_failure; + + if (nla_put_u16(msg, NL802154_COORD_SUPERFRAME_SPEC, + desc->superframe_spec)) + goto nla_put_failure; + + if (nla_put_u8(msg, NL802154_COORD_LINK_QUALITY, desc->link_quality)) + goto nla_put_failure; + + if (desc->gts_permit && nla_put_flag(msg, NL802154_COORD_GTS_PERMIT)) + goto nla_put_failure; + + /* TODO: NL802154_COORD_PAYLOAD_DATA if any */ + + nla_nest_end(msg, nla); + + genlmsg_end(msg, hdr); + + return 0; + + nla_put_failure: + genlmsg_cancel(msg, hdr); + + return -EMSGSIZE; +} + +int nl802154_scan_event(struct wpan_phy *wpan_phy, struct wpan_dev *wpan_dev, + struct ieee802154_coord_desc *desc) +{ + struct cfg802154_registered_device *rdev = wpan_phy_to_rdev(wpan_phy); + struct sk_buff *msg; + int ret; + + msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC); + if (!msg) + return -ENOMEM; + + ret = nl802154_prep_scan_event_msg(msg, rdev, wpan_dev, 0, 0, 0, + NL802154_CMD_SCAN_EVENT, + desc); + if (ret < 0) { + nlmsg_free(msg); + return ret; + } + + return genlmsg_multicast_netns(&nl802154_fam, wpan_phy_net(wpan_phy), + msg, 0, NL802154_MCGRP_SCAN, GFP_ATOMIC); +} +EXPORT_SYMBOL_GPL(nl802154_scan_event); + #ifdef CONFIG_IEEE802154_NL802154_EXPERIMENTAL static const struct nla_policy nl802154_dev_addr_policy[NL802154_DEV_ADDR_ATTR_MAX + 1] = { [NL802154_DEV_ADDR_ATTR_PAN_ID] = { .type = NLA_U16 }, diff --git a/net/ieee802154/nl802154.h b/net/ieee802154/nl802154.h index 8c4b6d08954c..89b805500032 100644 --- a/net/ieee802154/nl802154.h +++ b/net/ieee802154/nl802154.h @@ -4,5 +4,7 @@ int nl802154_init(void); void nl802154_exit(void); +int nl802154_scan_event(struct wpan_phy *wpan_phy, struct wpan_dev *wpan_dev, + struct ieee802154_coord_desc *desc); #endif /* __IEEE802154_NL802154_H */ From e29e3c7ce6d4b2f164ebd717e4794c626fc1c954 Mon Sep 17 00:00:00 2001 From: David Girault Date: Tue, 29 Nov 2022 14:55:35 +0100 Subject: [PATCH 1301/2002] mac802154: Trace the registration of new PANs Add an internal trace when valid beacons are received. Signed-off-by: David Girault Signed-off-by: Miquel Raynal Acked-by: Alexander Aring Link: https://lore.kernel.org/r/20221129135535.532513-3-miquel.raynal@bootlin.com Signed-off-by: Stefan Schmidt --- net/mac802154/trace.h | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/net/mac802154/trace.h b/net/mac802154/trace.h index df855c33daf2..689396d6c76a 100644 --- a/net/mac802154/trace.h +++ b/net/mac802154/trace.h @@ -264,6 +264,31 @@ TRACE_EVENT(802154_drv_set_promiscuous_mode, BOOL_TO_STR(__entry->on)) ); +TRACE_EVENT(802154_new_scan_event, + TP_PROTO(struct ieee802154_coord_desc *desc), + TP_ARGS(desc), + TP_STRUCT__entry( + __field(__le16, pan_id) + __field(__le64, addr) + __field(u8, channel) + __field(u8, page) + ), + TP_fast_assign( + __entry->page = desc->page; + __entry->channel = desc->channel; + __entry->pan_id = desc->addr.pan_id; + __entry->addr = desc->addr.extended_addr; + ), + TP_printk("panid: %u, coord_addr: 0x%llx, page: %u, channel: %u", + __le16_to_cpu(__entry->pan_id), __le64_to_cpu(__entry->addr), + __entry->page, __entry->channel) +); + +DEFINE_EVENT(802154_new_scan_event, 802154_scan_event, + TP_PROTO(struct ieee802154_coord_desc *desc), + TP_ARGS(desc) +); + #endif /* !__MAC802154_DRIVER_TRACE || TRACE_HEADER_MULTI_READ */ #undef TRACE_INCLUDE_PATH From 7a945ce0c19bbdf821d5f7ce1515e7fb8e444465 Mon Sep 17 00:00:00 2001 From: Yuan Can Date: Tue, 29 Nov 2022 01:39:34 +0000 Subject: [PATCH 1302/2002] udp_tunnel: Add checks for nla_nest_start() in __udp_tunnel_nic_dump_write() As the nla_nest_start() may fail with NULL returned, the return value should be checked. Note that this is not a real bug, nothing will break here. The next nla_put() will fail as well and we'll bail (and nla_nest_cancel() can handle NULL). But we keep getting those "fixes" so whatever. Signed-off-by: Yuan Can Reviewed-by: David Ahern Link: https://lore.kernel.org/r/20221129013934.55184-1-yuancan@huawei.com Signed-off-by: Jakub Kicinski --- net/ipv4/udp_tunnel_nic.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/net/ipv4/udp_tunnel_nic.c b/net/ipv4/udp_tunnel_nic.c index bc3a043a5d5c..029219749785 100644 --- a/net/ipv4/udp_tunnel_nic.c +++ b/net/ipv4/udp_tunnel_nic.c @@ -624,6 +624,8 @@ __udp_tunnel_nic_dump_write(struct net_device *dev, unsigned int table, continue; nest = nla_nest_start(skb, ETHTOOL_A_TUNNEL_UDP_TABLE_ENTRY); + if (!nest) + return -EMSGSIZE; if (nla_put_be16(skb, ETHTOOL_A_TUNNEL_UDP_ENTRY_PORT, utn->entries[table][j].port) || From 1e777f39b4d75e599a3aac8e0f67d739474f198c Mon Sep 17 00:00:00 2001 From: Dmytro Shytyi Date: Fri, 25 Nov 2022 23:29:47 +0100 Subject: [PATCH 1303/2002] mptcp: add MSG_FASTOPEN sendmsg flag support Since commit 54f1944ed6d2 ("mptcp: factor out mptcp_connect()"), all the infrastructure is now in place to support the MSG_FASTOPEN flag, we just need to call into the fastopen path in mptcp_sendmsg(). Co-developed-by: Benjamin Hesmans Signed-off-by: Benjamin Hesmans Acked-by: Paolo Abeni Signed-off-by: Dmytro Shytyi Signed-off-by: Matthieu Baerts Signed-off-by: Jakub Kicinski --- net/mptcp/protocol.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/net/mptcp/protocol.c b/net/mptcp/protocol.c index 3722a8580b61..efc84816a692 100644 --- a/net/mptcp/protocol.c +++ b/net/mptcp/protocol.c @@ -1711,17 +1711,14 @@ static int mptcp_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) int ret = 0; long timeo; - /* we don't support FASTOPEN yet */ - if (msg->msg_flags & MSG_FASTOPEN) - return -EOPNOTSUPP; - /* silently ignore everything else */ - msg->msg_flags &= MSG_MORE | MSG_DONTWAIT | MSG_NOSIGNAL; + msg->msg_flags &= MSG_MORE | MSG_DONTWAIT | MSG_NOSIGNAL | MSG_FASTOPEN; lock_sock(sk); ssock = __mptcp_nmpc_socket(msk); - if (unlikely(ssock && inet_sk(ssock->sk)->defer_connect)) { + if (unlikely(ssock && (inet_sk(ssock->sk)->defer_connect || + msg->msg_flags & MSG_FASTOPEN))) { int copied_syn = 0; ret = mptcp_sendmsg_fastopen(sk, ssock->sk, msg, len, &copied_syn); From fe33d38626779ffcc1c88204b1931774dc204cb5 Mon Sep 17 00:00:00 2001 From: Paolo Abeni Date: Fri, 25 Nov 2022 23:29:48 +0100 Subject: [PATCH 1304/2002] mptcp: track accurately the incoming MPC suboption type Currently in the receive path we don't need to discriminate between MPC SYN, MPC SYN-ACK and MPC ACK, but soon the fastopen code will need that info to properly track the fully established status. Track the exact MPC suboption type into the receive opt bitmap. No functional change intended. Signed-off-by: Paolo Abeni Reviewed-by: Matthieu Baerts Signed-off-by: Matthieu Baerts Signed-off-by: Jakub Kicinski --- net/mptcp/options.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/net/mptcp/options.c b/net/mptcp/options.c index 30d289044e71..784a205e80da 100644 --- a/net/mptcp/options.c +++ b/net/mptcp/options.c @@ -26,6 +26,7 @@ static void mptcp_parse_option(const struct sk_buff *skb, { u8 subtype = *ptr >> 4; int expected_opsize; + u16 subopt; u8 version; u8 flags; u8 i; @@ -38,11 +39,15 @@ static void mptcp_parse_option(const struct sk_buff *skb, expected_opsize = TCPOLEN_MPTCP_MPC_ACK_DATA; else expected_opsize = TCPOLEN_MPTCP_MPC_ACK; + subopt = OPTION_MPTCP_MPC_ACK; } else { - if (TCP_SKB_CB(skb)->tcp_flags & TCPHDR_ACK) + if (TCP_SKB_CB(skb)->tcp_flags & TCPHDR_ACK) { expected_opsize = TCPOLEN_MPTCP_MPC_SYNACK; - else + subopt = OPTION_MPTCP_MPC_SYNACK; + } else { expected_opsize = TCPOLEN_MPTCP_MPC_SYN; + subopt = OPTION_MPTCP_MPC_SYN; + } } /* Cfr RFC 8684 Section 3.3.0: @@ -85,7 +90,7 @@ static void mptcp_parse_option(const struct sk_buff *skb, mp_opt->deny_join_id0 = !!(flags & MPTCP_CAP_DENY_JOIN_ID0); - mp_opt->suboptions |= OPTIONS_MPTCP_MPC; + mp_opt->suboptions |= subopt; if (opsize >= TCPOLEN_MPTCP_MPC_SYNACK) { mp_opt->sndr_key = get_unaligned_be64(ptr); ptr += 8; From b3ea6b272d79a43baaaa9af871ee66f6fda4688f Mon Sep 17 00:00:00 2001 From: Paolo Abeni Date: Fri, 25 Nov 2022 23:29:49 +0100 Subject: [PATCH 1305/2002] mptcp: consolidate initial ack seq generation Currently the initial ack sequence is generated on demand whenever it's requested and the remote key is handy. The relevant code is scattered in different places and can lead to multiple, unneeded, crypto operations. This change consolidates the ack sequence generation code in a single helper, storing the sequence number at the subflow level. The above additionally saves a few conditional in fast-path and will simplify the upcoming fast-open implementation. Signed-off-by: Paolo Abeni Reviewed-by: Matthieu Baerts Signed-off-by: Matthieu Baerts Signed-off-by: Jakub Kicinski --- net/mptcp/options.c | 5 ++-- net/mptcp/protocol.c | 19 +-------------- net/mptcp/protocol.h | 9 ++++--- net/mptcp/subflow.c | 57 +++++++++++++++++++++++++++----------------- 4 files changed, 45 insertions(+), 45 deletions(-) diff --git a/net/mptcp/options.c b/net/mptcp/options.c index 784a205e80da..ae076468fcb9 100644 --- a/net/mptcp/options.c +++ b/net/mptcp/options.c @@ -953,8 +953,9 @@ static bool check_fully_established(struct mptcp_sock *msk, struct sock *ssk, return subflow->mp_capable; } - if (((mp_opt->suboptions & OPTION_MPTCP_DSS) && mp_opt->use_ack) || - ((mp_opt->suboptions & OPTION_MPTCP_ADD_ADDR) && !mp_opt->echo)) { + if (subflow->remote_key_valid && + (((mp_opt->suboptions & OPTION_MPTCP_DSS) && mp_opt->use_ack) || + ((mp_opt->suboptions & OPTION_MPTCP_ADD_ADDR) && !mp_opt->echo))) { /* subflows are fully established as soon as we get any * additional ack, including ADD_ADDR. */ diff --git a/net/mptcp/protocol.c b/net/mptcp/protocol.c index efc84816a692..66a599d2ee19 100644 --- a/net/mptcp/protocol.c +++ b/net/mptcp/protocol.c @@ -3045,7 +3045,6 @@ struct sock *mptcp_sk_clone(const struct sock *sk, struct mptcp_subflow_request_sock *subflow_req = mptcp_subflow_rsk(req); struct sock *nsk = sk_clone_lock(sk, GFP_ATOMIC); struct mptcp_sock *msk; - u64 ack_seq; if (!nsk) return NULL; @@ -3071,15 +3070,6 @@ struct sock *mptcp_sk_clone(const struct sock *sk, msk->wnd_end = msk->snd_nxt + req->rsk_rcv_wnd; msk->setsockopt_seq = mptcp_sk(sk)->setsockopt_seq; - if (mp_opt->suboptions & OPTIONS_MPTCP_MPC) { - msk->can_ack = true; - msk->remote_key = mp_opt->sndr_key; - mptcp_crypto_key_sha(msk->remote_key, NULL, &ack_seq); - ack_seq++; - WRITE_ONCE(msk->ack_seq, ack_seq); - atomic64_set(&msk->rcv_wnd_sent, ack_seq); - } - sock_reset_flag(nsk, SOCK_RCU_FREE); /* will be fully established after successful MPC subflow creation */ inet_sk_state_store(nsk, TCP_SYN_RECV); @@ -3352,7 +3342,6 @@ void mptcp_finish_connect(struct sock *ssk) struct mptcp_subflow_context *subflow; struct mptcp_sock *msk; struct sock *sk; - u64 ack_seq; subflow = mptcp_subflow_ctx(ssk); sk = subflow->conn; @@ -3360,22 +3349,16 @@ void mptcp_finish_connect(struct sock *ssk) pr_debug("msk=%p, token=%u", sk, subflow->token); - mptcp_crypto_key_sha(subflow->remote_key, NULL, &ack_seq); - ack_seq++; - subflow->map_seq = ack_seq; + subflow->map_seq = subflow->iasn; subflow->map_subflow_seq = 1; /* the socket is not connected yet, no msk/subflow ops can access/race * accessing the field below */ - WRITE_ONCE(msk->remote_key, subflow->remote_key); WRITE_ONCE(msk->local_key, subflow->local_key); WRITE_ONCE(msk->write_seq, subflow->idsn + 1); WRITE_ONCE(msk->snd_nxt, msk->write_seq); - WRITE_ONCE(msk->ack_seq, ack_seq); - WRITE_ONCE(msk->can_ack, 1); WRITE_ONCE(msk->snd_una, msk->write_seq); - atomic64_set(&msk->rcv_wnd_sent, ack_seq); mptcp_pm_new_connection(msk, ssk, 0); diff --git a/net/mptcp/protocol.h b/net/mptcp/protocol.h index 6a09ab99a12d..b5abea3d1a9c 100644 --- a/net/mptcp/protocol.h +++ b/net/mptcp/protocol.h @@ -467,7 +467,7 @@ struct mptcp_subflow_context { send_fastclose : 1, send_infinite_map : 1, rx_eof : 1, - can_ack : 1, /* only after processing the remote a key */ + remote_key_valid : 1, /* received the peer key from */ disposable : 1, /* ctx can be free at ulp release time */ stale : 1, /* unable to snd/rcv data, do not use for xmit */ local_id_valid : 1, /* local_id is correctly initialized */ @@ -477,7 +477,10 @@ struct mptcp_subflow_context { u64 thmac; u32 local_nonce; u32 remote_token; - u8 hmac[MPTCPOPT_HMAC_LEN]; + union { + u8 hmac[MPTCPOPT_HMAC_LEN]; /* MPJ subflow only */ + u64 iasn; /* initial ack sequence number, MPC subflows only */ + }; u8 local_id; u8 remote_id; u8 reset_seen:1; @@ -603,7 +606,7 @@ unsigned int mptcp_stale_loss_cnt(const struct net *net); int mptcp_get_pm_type(const struct net *net); void mptcp_copy_inaddrs(struct sock *msk, const struct sock *ssk); void mptcp_subflow_fully_established(struct mptcp_subflow_context *subflow, - struct mptcp_options_received *mp_opt); + const struct mptcp_options_received *mp_opt); bool __mptcp_retransmit_pending_data(struct sock *sk); void mptcp_check_and_set_pending(struct sock *sk); void __mptcp_push_pending(struct sock *sk, unsigned int flags); diff --git a/net/mptcp/subflow.c b/net/mptcp/subflow.c index f3c336872475..eca4de08ca9c 100644 --- a/net/mptcp/subflow.c +++ b/net/mptcp/subflow.c @@ -392,11 +392,33 @@ static void mptcp_set_connected(struct sock *sk) mptcp_data_unlock(sk); } +static void subflow_set_remote_key(struct mptcp_sock *msk, + struct mptcp_subflow_context *subflow, + const struct mptcp_options_received *mp_opt) +{ + /* active MPC subflow will reach here multiple times: + * at subflow_finish_connect() time and at 4th ack time + */ + if (subflow->remote_key_valid) + return; + + subflow->remote_key_valid = 1; + subflow->remote_key = mp_opt->sndr_key; + mptcp_crypto_key_sha(subflow->remote_key, NULL, &subflow->iasn); + subflow->iasn++; + + WRITE_ONCE(msk->remote_key, subflow->remote_key); + WRITE_ONCE(msk->ack_seq, subflow->iasn); + WRITE_ONCE(msk->can_ack, true); + atomic64_set(&msk->rcv_wnd_sent, subflow->iasn); +} + static void subflow_finish_connect(struct sock *sk, const struct sk_buff *skb) { struct mptcp_subflow_context *subflow = mptcp_subflow_ctx(sk); struct mptcp_options_received mp_opt; struct sock *parent = subflow->conn; + struct mptcp_sock *msk; subflow->icsk_af_ops->sk_rx_dst_set(sk, skb); @@ -404,6 +426,7 @@ static void subflow_finish_connect(struct sock *sk, const struct sk_buff *skb) if (subflow->conn_finished) return; + msk = mptcp_sk(parent); mptcp_propagate_sndbuf(parent, sk); subflow->rel_write_seq = 1; subflow->conn_finished = 1; @@ -416,19 +439,16 @@ static void subflow_finish_connect(struct sock *sk, const struct sk_buff *skb) MPTCP_INC_STATS(sock_net(sk), MPTCP_MIB_MPCAPABLEACTIVEFALLBACK); mptcp_do_fallback(sk); - pr_fallback(mptcp_sk(subflow->conn)); + pr_fallback(msk); goto fallback; } if (mp_opt.suboptions & OPTION_MPTCP_CSUMREQD) - WRITE_ONCE(mptcp_sk(parent)->csum_enabled, true); + WRITE_ONCE(msk->csum_enabled, true); if (mp_opt.deny_join_id0) - WRITE_ONCE(mptcp_sk(parent)->pm.remote_deny_join_id0, true); + WRITE_ONCE(msk->pm.remote_deny_join_id0, true); subflow->mp_capable = 1; - subflow->can_ack = 1; - subflow->remote_key = mp_opt.sndr_key; - pr_debug("subflow=%p, remote_key=%llu", subflow, - subflow->remote_key); + subflow_set_remote_key(msk, subflow, &mp_opt); MPTCP_INC_STATS(sock_net(sk), MPTCP_MIB_MPCAPABLEACTIVEACK); mptcp_finish_connect(sk); mptcp_set_connected(parent); @@ -466,7 +486,7 @@ static void subflow_finish_connect(struct sock *sk, const struct sk_buff *skb) subflow->mp_join = 1; MPTCP_INC_STATS(sock_net(sk), MPTCP_MIB_JOINSYNACKRX); - if (subflow_use_different_dport(mptcp_sk(parent), sk)) { + if (subflow_use_different_dport(msk, sk)) { pr_debug("synack inet_dport=%d %d", ntohs(inet_sk(sk)->inet_dport), ntohs(inet_sk(parent)->inet_dport)); @@ -474,7 +494,7 @@ static void subflow_finish_connect(struct sock *sk, const struct sk_buff *skb) } } else if (mptcp_check_fallback(sk)) { fallback: - mptcp_rcv_space_init(mptcp_sk(parent), sk); + mptcp_rcv_space_init(msk, sk); mptcp_set_connected(parent); } return; @@ -637,13 +657,12 @@ static void subflow_drop_ctx(struct sock *ssk) } void mptcp_subflow_fully_established(struct mptcp_subflow_context *subflow, - struct mptcp_options_received *mp_opt) + const struct mptcp_options_received *mp_opt) { struct mptcp_sock *msk = mptcp_sk(subflow->conn); - subflow->remote_key = mp_opt->sndr_key; + subflow_set_remote_key(msk, subflow, mp_opt); subflow->fully_established = 1; - subflow->can_ack = 1; WRITE_ONCE(msk->fully_established, true); } @@ -1198,16 +1217,8 @@ static bool subflow_check_data_avail(struct sock *ssk) if (WARN_ON_ONCE(!skb)) goto no_data; - /* if msk lacks the remote key, this subflow must provide an - * MP_CAPABLE-based mapping - */ - if (unlikely(!READ_ONCE(msk->can_ack))) { - if (!subflow->mpc_map) - goto fallback; - WRITE_ONCE(msk->remote_key, subflow->remote_key); - WRITE_ONCE(msk->ack_seq, subflow->map_seq); - WRITE_ONCE(msk->can_ack, true); - } + if (unlikely(!READ_ONCE(msk->can_ack))) + goto fallback; old_ack = READ_ONCE(msk->ack_seq); ack_seq = mptcp_subflow_get_mapped_dsn(subflow); @@ -1480,6 +1491,7 @@ int __mptcp_subflow_connect(struct sock *sk, const struct mptcp_addr_info *loc, mptcp_pm_get_flags_and_ifindex_by_id(msk, local_id, &flags, &ifindex); + subflow->remote_key_valid = 1; subflow->remote_key = msk->remote_key; subflow->local_key = msk->local_key; subflow->token = msk->token; @@ -1873,6 +1885,7 @@ static void subflow_ulp_clone(const struct request_sock *req, new_ctx->ssn_offset = subflow_req->ssn_offset; new_ctx->mp_join = 1; new_ctx->fully_established = 1; + new_ctx->remote_key_valid = 1; new_ctx->backup = subflow_req->backup; new_ctx->remote_id = subflow_req->remote_id; new_ctx->token = subflow_req->token; From dfc8d06030335a816d81aa92fe5d1f84d06998ad Mon Sep 17 00:00:00 2001 From: Dmytro Shytyi Date: Fri, 25 Nov 2022 23:29:50 +0100 Subject: [PATCH 1306/2002] mptcp: implement delayed seq generation for passive fastopen With fastopen in place, the first subflow socket is created before the MPC handshake completes, and we need to properly initialize the sequence numbers at MPC ACK reception. Co-developed-by: Paolo Abeni Signed-off-by: Paolo Abeni Co-developed-by: Matthieu Baerts Signed-off-by: Matthieu Baerts Signed-off-by: Dmytro Shytyi Signed-off-by: Matthieu Baerts Signed-off-by: Jakub Kicinski --- net/mptcp/Makefile | 2 +- net/mptcp/fastopen.c | 28 ++++++++++++++++++++++++++++ net/mptcp/options.c | 9 ++++++--- net/mptcp/protocol.c | 9 --------- net/mptcp/protocol.h | 16 +++++++++++++++- net/mptcp/subflow.c | 5 ++++- 6 files changed, 54 insertions(+), 15 deletions(-) create mode 100644 net/mptcp/fastopen.c diff --git a/net/mptcp/Makefile b/net/mptcp/Makefile index 6e7df47c9584..a3829ce548f9 100644 --- a/net/mptcp/Makefile +++ b/net/mptcp/Makefile @@ -2,7 +2,7 @@ obj-$(CONFIG_MPTCP) += mptcp.o mptcp-y := protocol.o subflow.o options.o token.o crypto.o ctrl.o pm.o diag.o \ - mib.o pm_netlink.o sockopt.o pm_userspace.o + mib.o pm_netlink.o sockopt.o pm_userspace.o fastopen.o obj-$(CONFIG_SYN_COOKIES) += syncookies.o obj-$(CONFIG_INET_MPTCP_DIAG) += mptcp_diag.o diff --git a/net/mptcp/fastopen.c b/net/mptcp/fastopen.c new file mode 100644 index 000000000000..19c332af0834 --- /dev/null +++ b/net/mptcp/fastopen.c @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: GPL-2.0 +/* MPTCP Fast Open Mechanism + * + * Copyright (c) 2021-2022, Dmytro SHYTYI + */ + +#include "protocol.h" + +void mptcp_fastopen_gen_msk_ackseq(struct mptcp_sock *msk, struct mptcp_subflow_context *subflow, + const struct mptcp_options_received *mp_opt) +{ + struct sock *sk = (struct sock *)msk; + struct sk_buff *skb; + + mptcp_data_lock(sk); + skb = skb_peek_tail(&sk->sk_receive_queue); + if (skb) { + WARN_ON_ONCE(MPTCP_SKB_CB(skb)->end_seq); + pr_debug("msk %p moving seq %llx -> %llx end_seq %llx -> %llx", sk, + MPTCP_SKB_CB(skb)->map_seq, MPTCP_SKB_CB(skb)->map_seq + msk->ack_seq, + MPTCP_SKB_CB(skb)->end_seq, MPTCP_SKB_CB(skb)->end_seq + msk->ack_seq); + MPTCP_SKB_CB(skb)->map_seq += msk->ack_seq; + MPTCP_SKB_CB(skb)->end_seq += msk->ack_seq; + } + + pr_debug("msk=%p ack_seq=%llx", msk, msk->ack_seq); + mptcp_data_unlock(sk); +} diff --git a/net/mptcp/options.c b/net/mptcp/options.c index ae076468fcb9..5ded85e2c374 100644 --- a/net/mptcp/options.c +++ b/net/mptcp/options.c @@ -939,7 +939,7 @@ static bool check_fully_established(struct mptcp_sock *msk, struct sock *ssk, subflow->mp_join && (mp_opt->suboptions & OPTIONS_MPTCP_MPJ) && !subflow->request_join) tcp_send_ack(ssk); - goto fully_established; + goto check_notify; } /* we must process OoO packets before the first subflow is fully @@ -950,6 +950,8 @@ static bool check_fully_established(struct mptcp_sock *msk, struct sock *ssk, if (TCP_SKB_CB(skb)->seq != subflow->ssn_offset + 1) { if (subflow->mp_join) goto reset; + if (subflow->is_mptfo && mp_opt->suboptions & OPTION_MPTCP_MPC_ACK) + goto set_fully_established; return subflow->mp_capable; } @@ -961,7 +963,7 @@ static bool check_fully_established(struct mptcp_sock *msk, struct sock *ssk, */ subflow->fully_established = 1; WRITE_ONCE(msk->fully_established, true); - goto fully_established; + goto check_notify; } /* If the first established packet does not contain MP_CAPABLE + data @@ -980,11 +982,12 @@ static bool check_fully_established(struct mptcp_sock *msk, struct sock *ssk, if (mp_opt->deny_join_id0) WRITE_ONCE(msk->pm.remote_deny_join_id0, true); +set_fully_established: if (unlikely(!READ_ONCE(msk->pm.server_side))) pr_warn_once("bogus mpc option on established client sk"); mptcp_subflow_fully_established(subflow, mp_opt); -fully_established: +check_notify: /* if the subflow is not already linked into the conn_list, we can't * notify the PM: this subflow is still on the listener queue * and the PM possibly acquiring the subflow lock could race with diff --git a/net/mptcp/protocol.c b/net/mptcp/protocol.c index 66a599d2ee19..3fa0aa44bf5b 100644 --- a/net/mptcp/protocol.c +++ b/net/mptcp/protocol.c @@ -36,15 +36,6 @@ struct mptcp6_sock { }; #endif -struct mptcp_skb_cb { - u64 map_seq; - u64 end_seq; - u32 offset; - u8 has_rxtstamp:1; -}; - -#define MPTCP_SKB_CB(__skb) ((struct mptcp_skb_cb *)&((__skb)->cb[0])) - enum { MPTCP_CMSG_TS = BIT(0), MPTCP_CMSG_INQ = BIT(1), diff --git a/net/mptcp/protocol.h b/net/mptcp/protocol.h index b5abea3d1a9c..618ac85abaaf 100644 --- a/net/mptcp/protocol.h +++ b/net/mptcp/protocol.h @@ -126,6 +126,15 @@ #define MPTCP_CONNECTED 6 #define MPTCP_RESET_SCHEDULER 7 +struct mptcp_skb_cb { + u64 map_seq; + u64 end_seq; + u32 offset; + u8 has_rxtstamp:1; +}; + +#define MPTCP_SKB_CB(__skb) ((struct mptcp_skb_cb *)&((__skb)->cb[0])) + static inline bool before64(__u64 seq1, __u64 seq2) { return (__s64)(seq1 - seq2) < 0; @@ -471,7 +480,9 @@ struct mptcp_subflow_context { disposable : 1, /* ctx can be free at ulp release time */ stale : 1, /* unable to snd/rcv data, do not use for xmit */ local_id_valid : 1, /* local_id is correctly initialized */ - valid_csum_seen : 1; /* at least one csum validated */ + valid_csum_seen : 1, /* at least one csum validated */ + is_mptfo : 1, /* subflow is doing TFO */ + __unused : 8; enum mptcp_data_avail data_avail; u32 remote_nonce; u64 thmac; @@ -829,6 +840,9 @@ void mptcp_event_addr_announced(const struct sock *ssk, const struct mptcp_addr_ void mptcp_event_addr_removed(const struct mptcp_sock *msk, u8 id); bool mptcp_userspace_pm_active(const struct mptcp_sock *msk); +void mptcp_fastopen_gen_msk_ackseq(struct mptcp_sock *msk, struct mptcp_subflow_context *subflow, + const struct mptcp_options_received *mp_opt); + static inline bool mptcp_pm_should_add_signal(struct mptcp_sock *msk) { return READ_ONCE(msk->pm.addr_signal) & diff --git a/net/mptcp/subflow.c b/net/mptcp/subflow.c index eca4de08ca9c..cbeb7718f092 100644 --- a/net/mptcp/subflow.c +++ b/net/mptcp/subflow.c @@ -664,6 +664,9 @@ void mptcp_subflow_fully_established(struct mptcp_subflow_context *subflow, subflow_set_remote_key(msk, subflow, mp_opt); subflow->fully_established = 1; WRITE_ONCE(msk->fully_established, true); + + if (subflow->is_mptfo) + mptcp_fastopen_gen_msk_ackseq(msk, subflow, mp_opt); } static struct sock *subflow_syn_recv_sock(const struct sock *sk, @@ -779,7 +782,7 @@ create_child: /* with OoO packets we can reach here without ingress * mpc option */ - if (mp_opt.suboptions & OPTIONS_MPTCP_MPC) + if (mp_opt.suboptions & OPTION_MPTCP_MPC_ACK) mptcp_subflow_fully_established(ctx, &mp_opt); } else if (ctx->mp_join) { struct mptcp_sock *owner; From 36b122baf6a8bd46b4a591f12f4ed17b22257408 Mon Sep 17 00:00:00 2001 From: Dmytro Shytyi Date: Fri, 25 Nov 2022 23:29:51 +0100 Subject: [PATCH 1307/2002] mptcp: add subflow_v(4,6)_send_synack() The send_synack() needs to be overridden for MPTCP to support TFO for two reasons: - There is not be enough space in the TCP options if the TFO cookie has to be added in the SYN+ACK with other options: MSS (4), SACK OK (2), Timestamps (10), Window Scale (3+1), TFO (10+2), MP_CAPABLE (12). MPTCPv1 specs -- RFC 8684, section B.1 [1] -- suggest to drop the TCP timestamps option in this case. - The data received in the SYN has to be handled: the SKB can be dequeued from the subflow sk and transferred to the MPTCP sk. Counters need to be updated accordingly and the application can be notified at the end because some bytes have been received. [1] https://www.rfc-editor.org/rfc/rfc8684.html#section-b.1 Co-developed-by: Paolo Abeni Signed-off-by: Paolo Abeni Co-developed-by: Matthieu Baerts Signed-off-by: Matthieu Baerts Signed-off-by: Dmytro Shytyi Signed-off-by: Matthieu Baerts Signed-off-by: Jakub Kicinski --- net/mptcp/fastopen.c | 45 ++++++++++++++++++++++++++++++++++++++++++++ net/mptcp/protocol.c | 2 +- net/mptcp/protocol.h | 3 +++ net/mptcp/subflow.c | 43 ++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 92 insertions(+), 1 deletion(-) diff --git a/net/mptcp/fastopen.c b/net/mptcp/fastopen.c index 19c332af0834..d237d142171c 100644 --- a/net/mptcp/fastopen.c +++ b/net/mptcp/fastopen.c @@ -6,6 +6,51 @@ #include "protocol.h" +void mptcp_fastopen_subflow_synack_set_params(struct mptcp_subflow_context *subflow, + struct request_sock *req) +{ + struct sock *ssk = subflow->tcp_sock; + struct sock *sk = subflow->conn; + struct sk_buff *skb; + struct tcp_sock *tp; + + tp = tcp_sk(ssk); + + subflow->is_mptfo = 1; + + skb = skb_peek(&ssk->sk_receive_queue); + if (WARN_ON_ONCE(!skb)) + return; + + /* dequeue the skb from sk receive queue */ + __skb_unlink(skb, &ssk->sk_receive_queue); + skb_ext_reset(skb); + skb_orphan(skb); + + /* We copy the fastopen data, but that don't belong to the mptcp sequence + * space, need to offset it in the subflow sequence, see mptcp_subflow_get_map_offset() + */ + tp->copied_seq += skb->len; + subflow->ssn_offset += skb->len; + + /* initialize a dummy sequence number, we will update it at MPC + * completion, if needed + */ + MPTCP_SKB_CB(skb)->map_seq = -skb->len; + MPTCP_SKB_CB(skb)->end_seq = 0; + MPTCP_SKB_CB(skb)->offset = 0; + MPTCP_SKB_CB(skb)->has_rxtstamp = TCP_SKB_CB(skb)->has_rxtstamp; + + mptcp_data_lock(sk); + + mptcp_set_owner_r(skb, sk); + __skb_queue_tail(&sk->sk_receive_queue, skb); + + sk->sk_data_ready(sk); + + mptcp_data_unlock(sk); +} + void mptcp_fastopen_gen_msk_ackseq(struct mptcp_sock *msk, struct mptcp_subflow_context *subflow, const struct mptcp_options_received *mp_opt) { diff --git a/net/mptcp/protocol.c b/net/mptcp/protocol.c index 3fa0aa44bf5b..b0d387be500a 100644 --- a/net/mptcp/protocol.c +++ b/net/mptcp/protocol.c @@ -191,7 +191,7 @@ static void mptcp_rfree(struct sk_buff *skb) mptcp_rmem_uncharge(sk, len); } -static void mptcp_set_owner_r(struct sk_buff *skb, struct sock *sk) +void mptcp_set_owner_r(struct sk_buff *skb, struct sock *sk) { skb_orphan(skb); skb->sk = sk; diff --git a/net/mptcp/protocol.h b/net/mptcp/protocol.h index 618ac85abaaf..8b4379a2cd85 100644 --- a/net/mptcp/protocol.h +++ b/net/mptcp/protocol.h @@ -633,6 +633,7 @@ void mptcp_sock_graft(struct sock *sk, struct socket *parent); struct socket *__mptcp_nmpc_socket(const struct mptcp_sock *msk); bool __mptcp_close(struct sock *sk, long timeout); void mptcp_cancel_work(struct sock *sk); +void mptcp_set_owner_r(struct sk_buff *skb, struct sock *sk); bool mptcp_addresses_equal(const struct mptcp_addr_info *a, const struct mptcp_addr_info *b, bool use_port); @@ -842,6 +843,8 @@ bool mptcp_userspace_pm_active(const struct mptcp_sock *msk); void mptcp_fastopen_gen_msk_ackseq(struct mptcp_sock *msk, struct mptcp_subflow_context *subflow, const struct mptcp_options_received *mp_opt); +void mptcp_fastopen_subflow_synack_set_params(struct mptcp_subflow_context *subflow, + struct request_sock *req); static inline bool mptcp_pm_should_add_signal(struct mptcp_sock *msk) { diff --git a/net/mptcp/subflow.c b/net/mptcp/subflow.c index cbeb7718f092..29904303f5c2 100644 --- a/net/mptcp/subflow.c +++ b/net/mptcp/subflow.c @@ -307,7 +307,48 @@ static struct dst_entry *subflow_v4_route_req(const struct sock *sk, return NULL; } +static void subflow_prep_synack(const struct sock *sk, struct request_sock *req, + struct tcp_fastopen_cookie *foc, + enum tcp_synack_type synack_type) +{ + struct mptcp_subflow_context *subflow = mptcp_subflow_ctx(sk); + struct inet_request_sock *ireq = inet_rsk(req); + + /* clear tstamp_ok, as needed depending on cookie */ + if (foc && foc->len > -1) + ireq->tstamp_ok = 0; + + if (synack_type == TCP_SYNACK_FASTOPEN) + mptcp_fastopen_subflow_synack_set_params(subflow, req); +} + +static int subflow_v4_send_synack(const struct sock *sk, struct dst_entry *dst, + struct flowi *fl, + struct request_sock *req, + struct tcp_fastopen_cookie *foc, + enum tcp_synack_type synack_type, + struct sk_buff *syn_skb) +{ + subflow_prep_synack(sk, req, foc, synack_type); + + return tcp_request_sock_ipv4_ops.send_synack(sk, dst, fl, req, foc, + synack_type, syn_skb); +} + #if IS_ENABLED(CONFIG_MPTCP_IPV6) +static int subflow_v6_send_synack(const struct sock *sk, struct dst_entry *dst, + struct flowi *fl, + struct request_sock *req, + struct tcp_fastopen_cookie *foc, + enum tcp_synack_type synack_type, + struct sk_buff *syn_skb) +{ + subflow_prep_synack(sk, req, foc, synack_type); + + return tcp_request_sock_ipv6_ops.send_synack(sk, dst, fl, req, foc, + synack_type, syn_skb); +} + static struct dst_entry *subflow_v6_route_req(const struct sock *sk, struct sk_buff *skb, struct flowi *fl, @@ -1945,6 +1986,7 @@ void __init mptcp_subflow_init(void) subflow_request_sock_ipv4_ops = tcp_request_sock_ipv4_ops; subflow_request_sock_ipv4_ops.route_req = subflow_v4_route_req; + subflow_request_sock_ipv4_ops.send_synack = subflow_v4_send_synack; subflow_specific = ipv4_specific; subflow_specific.conn_request = subflow_v4_conn_request; @@ -1958,6 +2000,7 @@ void __init mptcp_subflow_init(void) #if IS_ENABLED(CONFIG_MPTCP_IPV6) subflow_request_sock_ipv6_ops = tcp_request_sock_ipv6_ops; subflow_request_sock_ipv6_ops.route_req = subflow_v6_route_req; + subflow_request_sock_ipv6_ops.send_synack = subflow_v6_send_synack; subflow_v6_specific = ipv6_specific; subflow_v6_specific.conn_request = subflow_v6_conn_request; From 4ffb0a02346c14f5e83711668f19b6b551cd32ed Mon Sep 17 00:00:00 2001 From: Dmytro Shytyi Date: Fri, 25 Nov 2022 23:29:52 +0100 Subject: [PATCH 1308/2002] mptcp: add TCP_FASTOPEN sock option The TCP_FASTOPEN socket option is one way for the application to tell the kernel TFO support has to be enabled for the listener socket. The only thing to do here with MPTCP is to relay the request to the first subflow like it is already done for the other TCP_FASTOPEN* socket options. Acked-by: Paolo Abeni Signed-off-by: Dmytro Shytyi Signed-off-by: Matthieu Baerts Signed-off-by: Jakub Kicinski --- net/mptcp/sockopt.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/net/mptcp/sockopt.c b/net/mptcp/sockopt.c index f62f6483ef77..c1bca711c35c 100644 --- a/net/mptcp/sockopt.c +++ b/net/mptcp/sockopt.c @@ -559,6 +559,7 @@ static bool mptcp_supported_sockopt(int level, int optname) case TCP_NOTSENT_LOWAT: case TCP_TX_DELAY: case TCP_INQ: + case TCP_FASTOPEN: case TCP_FASTOPEN_CONNECT: case TCP_FASTOPEN_NO_COOKIE: return true; @@ -569,7 +570,7 @@ static bool mptcp_supported_sockopt(int level, int optname) /* TCP_REPAIR, TCP_REPAIR_QUEUE, TCP_QUEUE_SEQ, TCP_REPAIR_OPTIONS, * TCP_REPAIR_WINDOW are not supported, better avoid this mess */ - /* TCP_FASTOPEN_KEY, TCP_FASTOPEN are not supported because + /* TCP_FASTOPEN_KEY is not supported because * fastopen for the listener side is currently unsupported */ } @@ -801,6 +802,7 @@ static int mptcp_setsockopt_sol_tcp(struct mptcp_sock *msk, int optname, /* See tcp.c: TCP_DEFER_ACCEPT does not fail */ mptcp_setsockopt_first_sf_only(msk, SOL_TCP, optname, optval, optlen); return 0; + case TCP_FASTOPEN: case TCP_FASTOPEN_CONNECT: case TCP_FASTOPEN_NO_COOKIE: return mptcp_setsockopt_first_sf_only(msk, SOL_TCP, optname, @@ -1166,6 +1168,7 @@ static int mptcp_getsockopt_sol_tcp(struct mptcp_sock *msk, int optname, case TCP_INFO: case TCP_CC_INFO: case TCP_DEFER_ACCEPT: + case TCP_FASTOPEN: case TCP_FASTOPEN_CONNECT: case TCP_FASTOPEN_NO_COOKIE: return mptcp_getsockopt_first_sf_only(msk, SOL_TCP, optname, From cb99816cb59d4253758827186d5a5616f9825b6c Mon Sep 17 00:00:00 2001 From: Matthieu Baerts Date: Fri, 25 Nov 2022 23:29:53 +0100 Subject: [PATCH 1309/2002] mptcp: add support for TCP_FASTOPEN_KEY sockopt The goal of this socket option is to set different keys per listener, see commit 1fba70e5b6be ("tcp: socket option to set TCP fast open key") for more details about this socket option. The only thing to do here with MPTCP is to relay the request to the first subflow like it is already done for the other TCP_FASTOPEN* socket options. Acked-by: Paolo Abeni Signed-off-by: Matthieu Baerts Signed-off-by: Jakub Kicinski --- net/mptcp/sockopt.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/net/mptcp/sockopt.c b/net/mptcp/sockopt.c index c1bca711c35c..a47423ebb33a 100644 --- a/net/mptcp/sockopt.c +++ b/net/mptcp/sockopt.c @@ -561,6 +561,7 @@ static bool mptcp_supported_sockopt(int level, int optname) case TCP_INQ: case TCP_FASTOPEN: case TCP_FASTOPEN_CONNECT: + case TCP_FASTOPEN_KEY: case TCP_FASTOPEN_NO_COOKIE: return true; } @@ -570,9 +571,6 @@ static bool mptcp_supported_sockopt(int level, int optname) /* TCP_REPAIR, TCP_REPAIR_QUEUE, TCP_QUEUE_SEQ, TCP_REPAIR_OPTIONS, * TCP_REPAIR_WINDOW are not supported, better avoid this mess */ - /* TCP_FASTOPEN_KEY is not supported because - * fastopen for the listener side is currently unsupported - */ } return false; } @@ -804,6 +802,7 @@ static int mptcp_setsockopt_sol_tcp(struct mptcp_sock *msk, int optname, return 0; case TCP_FASTOPEN: case TCP_FASTOPEN_CONNECT: + case TCP_FASTOPEN_KEY: case TCP_FASTOPEN_NO_COOKIE: return mptcp_setsockopt_first_sf_only(msk, SOL_TCP, optname, optval, optlen); @@ -1170,6 +1169,7 @@ static int mptcp_getsockopt_sol_tcp(struct mptcp_sock *msk, int optname, case TCP_DEFER_ACCEPT: case TCP_FASTOPEN: case TCP_FASTOPEN_CONNECT: + case TCP_FASTOPEN_KEY: case TCP_FASTOPEN_NO_COOKIE: return mptcp_getsockopt_first_sf_only(msk, SOL_TCP, optname, optval, optlen); From ca7ae89160434cd045a4795a235eb16587bd8f73 Mon Sep 17 00:00:00 2001 From: Dmytro Shytyi Date: Fri, 25 Nov 2022 23:29:54 +0100 Subject: [PATCH 1310/2002] selftests: mptcp: mptfo Initiator/Listener This patch first adds TFO support in mptcp_connect.c. This can be enabled via a new option: -o MPTFO. Once enabled, the TCP_FASTOPEN socket option is enabled for the server side and a sendto() with MSG_FASTOPEN is used instead of a connect() for the client side. Note that the first SYN has a limit of bytes it can carry. In other words, it is allowed to send less data than the provided one. We then need to track more status info to properly allow the next sendmsg() starting from the next part of the data to send the rest. Also in TFO scenarios, we need to completely spool the partially xmitted buffer -- and account for that -- before starting sendfile/mmap xmit, otherwise the relevant tests will fail. Co-developed-by: Paolo Abeni Signed-off-by: Paolo Abeni Signed-off-by: Dmytro Shytyi Signed-off-by: Matthieu Baerts Signed-off-by: Jakub Kicinski --- .../selftests/net/mptcp/mptcp_connect.c | 171 +++++++++++++----- .../selftests/net/mptcp/mptcp_connect.sh | 21 +++ 2 files changed, 150 insertions(+), 42 deletions(-) diff --git a/tools/testing/selftests/net/mptcp/mptcp_connect.c b/tools/testing/selftests/net/mptcp/mptcp_connect.c index e54653ea2ed4..8a8266957bc5 100644 --- a/tools/testing/selftests/net/mptcp/mptcp_connect.c +++ b/tools/testing/selftests/net/mptcp/mptcp_connect.c @@ -83,6 +83,7 @@ struct cfg_cmsg_types { struct cfg_sockopt_types { unsigned int transparent:1; + unsigned int mptfo:1; }; struct tcp_inq_state { @@ -90,6 +91,13 @@ struct tcp_inq_state { bool expect_eof; }; +struct wstate { + char buf[8192]; + unsigned int len; + unsigned int off; + unsigned int total_len; +}; + static struct tcp_inq_state tcp_inq; static struct cfg_cmsg_types cfg_cmsg_types; @@ -232,6 +240,14 @@ static void set_transparent(int fd, int pf) } } +static void set_mptfo(int fd, int pf) +{ + int qlen = 25; + + if (setsockopt(fd, IPPROTO_TCP, TCP_FASTOPEN, &qlen, sizeof(qlen)) == -1) + perror("TCP_FASTOPEN"); +} + static int do_ulp_so(int sock, const char *name) { return setsockopt(sock, IPPROTO_TCP, TCP_ULP, name, strlen(name)); @@ -300,6 +316,9 @@ static int sock_listen_mptcp(const char * const listenaddr, if (cfg_sockopt_types.transparent) set_transparent(sock, pf); + if (cfg_sockopt_types.mptfo) + set_mptfo(sock, pf); + if (bind(sock, a->ai_addr, a->ai_addrlen) == 0) break; /* success */ @@ -330,13 +349,15 @@ static int sock_listen_mptcp(const char * const listenaddr, static int sock_connect_mptcp(const char * const remoteaddr, const char * const port, int proto, - struct addrinfo **peer) + struct addrinfo **peer, + int infd, struct wstate *winfo) { struct addrinfo hints = { .ai_protocol = IPPROTO_TCP, .ai_socktype = SOCK_STREAM, }; struct addrinfo *a, *addr; + int syn_copied = 0; int sock = -1; hints.ai_family = pf; @@ -354,14 +375,34 @@ static int sock_connect_mptcp(const char * const remoteaddr, if (cfg_mark) set_mark(sock, cfg_mark); - if (connect(sock, a->ai_addr, a->ai_addrlen) == 0) { - *peer = a; - break; /* success */ - } + if (cfg_sockopt_types.mptfo) { + if (!winfo->total_len) + winfo->total_len = winfo->len = read(infd, winfo->buf, + sizeof(winfo->buf)); - perror("connect()"); - close(sock); - sock = -1; + syn_copied = sendto(sock, winfo->buf, winfo->len, MSG_FASTOPEN, + a->ai_addr, a->ai_addrlen); + if (syn_copied >= 0) { + winfo->off = syn_copied; + winfo->len -= syn_copied; + *peer = a; + break; /* success */ + } + } else { + if (connect(sock, a->ai_addr, a->ai_addrlen) == 0) { + *peer = a; + break; /* success */ + } + } + if (cfg_sockopt_types.mptfo) { + perror("sendto()"); + close(sock); + sock = -1; + } else { + perror("connect()"); + close(sock); + sock = -1; + } } freeaddrinfo(addr); @@ -571,14 +612,14 @@ static void shut_wr(int fd) shutdown(fd, SHUT_WR); } -static int copyfd_io_poll(int infd, int peerfd, int outfd, bool *in_closed_after_out) +static int copyfd_io_poll(int infd, int peerfd, int outfd, + bool *in_closed_after_out, struct wstate *winfo) { struct pollfd fds = { .fd = peerfd, .events = POLLIN | POLLOUT, }; - unsigned int woff = 0, wlen = 0, total_wlen = 0, total_rlen = 0; - char wbuf[8192]; + unsigned int total_wlen = 0, total_rlen = 0; set_nonblock(peerfd, true); @@ -638,19 +679,19 @@ static int copyfd_io_poll(int infd, int peerfd, int outfd, bool *in_closed_after } if (fds.revents & POLLOUT) { - if (wlen == 0) { - woff = 0; - wlen = read(infd, wbuf, sizeof(wbuf)); + if (winfo->len == 0) { + winfo->off = 0; + winfo->len = read(infd, winfo->buf, sizeof(winfo->buf)); } - if (wlen > 0) { + if (winfo->len > 0) { ssize_t bw; /* limit the total amount of written data to the trunc value */ - if (cfg_truncate > 0 && wlen + total_wlen > cfg_truncate) - wlen = cfg_truncate - total_wlen; + if (cfg_truncate > 0 && winfo->len + total_wlen > cfg_truncate) + winfo->len = cfg_truncate - total_wlen; - bw = do_rnd_write(peerfd, wbuf + woff, wlen); + bw = do_rnd_write(peerfd, winfo->buf + winfo->off, winfo->len); if (bw < 0) { if (cfg_rcv_trunc) return 0; @@ -658,10 +699,10 @@ static int copyfd_io_poll(int infd, int peerfd, int outfd, bool *in_closed_after return 111; } - woff += bw; - wlen -= bw; + winfo->off += bw; + winfo->len -= bw; total_wlen += bw; - } else if (wlen == 0) { + } else if (winfo->len == 0) { /* We have no more data to send. */ fds.events &= ~POLLOUT; @@ -717,10 +758,26 @@ static int do_recvfile(int infd, int outfd) return (int)r; } -static int do_mmap(int infd, int outfd, unsigned int size) +static int spool_buf(int fd, struct wstate *winfo) +{ + while (winfo->len) { + int ret = write(fd, winfo->buf + winfo->off, winfo->len); + + if (ret < 0) { + perror("write"); + return 4; + } + winfo->off += ret; + winfo->len -= ret; + } + return 0; +} + +static int do_mmap(int infd, int outfd, unsigned int size, + struct wstate *winfo) { char *inbuf = mmap(NULL, size, PROT_READ, MAP_SHARED, infd, 0); - ssize_t ret = 0, off = 0; + ssize_t ret = 0, off = winfo->total_len; size_t rem; if (inbuf == MAP_FAILED) { @@ -728,7 +785,11 @@ static int do_mmap(int infd, int outfd, unsigned int size) return 1; } - rem = size; + ret = spool_buf(outfd, winfo); + if (ret < 0) + return ret; + + rem = size - winfo->total_len; while (rem > 0) { ret = write(outfd, inbuf + off, rem); @@ -772,8 +833,16 @@ static int get_infd_size(int fd) return (int)count; } -static int do_sendfile(int infd, int outfd, unsigned int count) +static int do_sendfile(int infd, int outfd, unsigned int count, + struct wstate *winfo) { + int ret = spool_buf(outfd, winfo); + + if (ret < 0) + return ret; + + count -= winfo->total_len; + while (count > 0) { ssize_t r; @@ -790,7 +859,8 @@ static int do_sendfile(int infd, int outfd, unsigned int count) } static int copyfd_io_mmap(int infd, int peerfd, int outfd, - unsigned int size, bool *in_closed_after_out) + unsigned int size, bool *in_closed_after_out, + struct wstate *winfo) { int err; @@ -799,9 +869,9 @@ static int copyfd_io_mmap(int infd, int peerfd, int outfd, if (err) return err; - err = do_mmap(infd, peerfd, size); + err = do_mmap(infd, peerfd, size, winfo); } else { - err = do_mmap(infd, peerfd, size); + err = do_mmap(infd, peerfd, size, winfo); if (err) return err; @@ -815,7 +885,7 @@ static int copyfd_io_mmap(int infd, int peerfd, int outfd, } static int copyfd_io_sendfile(int infd, int peerfd, int outfd, - unsigned int size, bool *in_closed_after_out) + unsigned int size, bool *in_closed_after_out, struct wstate *winfo) { int err; @@ -824,9 +894,9 @@ static int copyfd_io_sendfile(int infd, int peerfd, int outfd, if (err) return err; - err = do_sendfile(infd, peerfd, size); + err = do_sendfile(infd, peerfd, size, winfo); } else { - err = do_sendfile(infd, peerfd, size); + err = do_sendfile(infd, peerfd, size, winfo); if (err) return err; @@ -839,7 +909,7 @@ static int copyfd_io_sendfile(int infd, int peerfd, int outfd, return err; } -static int copyfd_io(int infd, int peerfd, int outfd, bool close_peerfd) +static int copyfd_io(int infd, int peerfd, int outfd, bool close_peerfd, struct wstate *winfo) { bool in_closed_after_out = false; struct timespec start, end; @@ -851,21 +921,24 @@ static int copyfd_io(int infd, int peerfd, int outfd, bool close_peerfd) switch (cfg_mode) { case CFG_MODE_POLL: - ret = copyfd_io_poll(infd, peerfd, outfd, &in_closed_after_out); + ret = copyfd_io_poll(infd, peerfd, outfd, &in_closed_after_out, + winfo); break; case CFG_MODE_MMAP: file_size = get_infd_size(infd); if (file_size < 0) return file_size; - ret = copyfd_io_mmap(infd, peerfd, outfd, file_size, &in_closed_after_out); + ret = copyfd_io_mmap(infd, peerfd, outfd, file_size, + &in_closed_after_out, winfo); break; case CFG_MODE_SENDFILE: file_size = get_infd_size(infd); if (file_size < 0) return file_size; - ret = copyfd_io_sendfile(infd, peerfd, outfd, file_size, &in_closed_after_out); + ret = copyfd_io_sendfile(infd, peerfd, outfd, file_size, + &in_closed_after_out, winfo); break; default: @@ -999,6 +1072,7 @@ static void maybe_close(int fd) int main_loop_s(int listensock) { struct sockaddr_storage ss; + struct wstate winfo; struct pollfd polls; socklen_t salen; int remotesock; @@ -1033,7 +1107,8 @@ again: SOCK_TEST_TCPULP(remotesock, 0); - copyfd_io(fd, remotesock, 1, true); + memset(&winfo, 0, sizeof(winfo)); + copyfd_io(fd, remotesock, 1, true, &winfo); } else { perror("accept"); return 1; @@ -1130,6 +1205,11 @@ static void parse_setsock_options(const char *name) return; } + if (strncmp(name, "MPTFO", len) == 0) { + cfg_sockopt_types.mptfo = 1; + return; + } + fprintf(stderr, "Unrecognized setsockopt option %s\n", name); exit(1); } @@ -1166,11 +1246,18 @@ void xdisconnect(int fd, int addrlen) int main_loop(void) { - int fd, ret, fd_in = 0; + int fd = 0, ret, fd_in = 0; struct addrinfo *peer; + struct wstate winfo; - /* listener is ready. */ - fd = sock_connect_mptcp(cfg_host, cfg_port, cfg_sock_proto, &peer); + if (cfg_input && cfg_sockopt_types.mptfo) { + fd_in = open(cfg_input, O_RDONLY); + if (fd < 0) + xerror("can't open %s:%d", cfg_input, errno); + } + + memset(&winfo, 0, sizeof(winfo)); + fd = sock_connect_mptcp(cfg_host, cfg_port, cfg_sock_proto, &peer, fd_in, &winfo); if (fd < 0) return 2; @@ -1186,14 +1273,13 @@ again: if (cfg_cmsg_types.cmsg_enabled) apply_cmsg_types(fd, &cfg_cmsg_types); - if (cfg_input) { + if (cfg_input && !cfg_sockopt_types.mptfo) { fd_in = open(cfg_input, O_RDONLY); if (fd < 0) xerror("can't open %s:%d", cfg_input, errno); } - /* close the client socket open only if we are not going to reconnect */ - ret = copyfd_io(fd_in, fd, 1, 0); + ret = copyfd_io(fd_in, fd, 1, 0, &winfo); if (ret) return ret; @@ -1210,6 +1296,7 @@ again: xerror("can't reconnect: %d", errno); if (cfg_input) close(fd_in); + memset(&winfo, 0, sizeof(winfo)); goto again; } else { close(fd); diff --git a/tools/testing/selftests/net/mptcp/mptcp_connect.sh b/tools/testing/selftests/net/mptcp/mptcp_connect.sh index 621af6895f4d..60198b91a530 100755 --- a/tools/testing/selftests/net/mptcp/mptcp_connect.sh +++ b/tools/testing/selftests/net/mptcp/mptcp_connect.sh @@ -762,6 +762,23 @@ run_tests_peekmode() run_tests_lo "$ns1" "$ns1" dead:beef:1::1 1 "-P ${peekmode}" } +run_tests_mptfo() +{ + echo "INFO: with MPTFO start" + ip netns exec "$ns1" sysctl -q net.ipv4.tcp_fastopen=2 + ip netns exec "$ns2" sysctl -q net.ipv4.tcp_fastopen=1 + + run_tests_lo "$ns1" "$ns2" 10.0.1.1 0 "-o MPTFO" + run_tests_lo "$ns1" "$ns2" 10.0.1.1 0 "-o MPTFO" + + run_tests_lo "$ns1" "$ns2" dead:beef:1::1 0 "-o MPTFO" + run_tests_lo "$ns1" "$ns2" dead:beef:1::1 0 "-o MPTFO" + + ip netns exec "$ns1" sysctl -q net.ipv4.tcp_fastopen=0 + ip netns exec "$ns2" sysctl -q net.ipv4.tcp_fastopen=0 + echo "INFO: with MPTFO end" +} + run_tests_disconnect() { local peekmode="$1" @@ -901,6 +918,10 @@ run_tests_peekmode "saveWithPeek" run_tests_peekmode "saveAfterPeek" stop_if_error "Tests with peek mode have failed" +# MPTFO (MultiPath TCP Fatopen tests) +run_tests_mptfo +stop_if_error "Tests with MPTFO have failed" + # connect to ns4 ip address, ns2 should intercept/proxy run_test_transparent 10.0.3.1 "tproxy ipv4" run_test_transparent dead:beef:3::1 "tproxy ipv6" From d1a0ff5ff9efba55cc39ca520ba076943cd9a425 Mon Sep 17 00:00:00 2001 From: Maxime Chevallier Date: Fri, 25 Nov 2022 14:17:59 +0100 Subject: [PATCH 1311/2002] net: pcs: altera-tse: use read_poll_timeout to wait for reset Software resets on the TSE PCS don't clear registers, but rather reset all internal state machines regarding AN, comma detection and encoding/decoding. Use read_poll_timeout to wait for the reset to clear instead of manually polling the register. Signed-off-by: Maxime Chevallier Signed-off-by: Jakub Kicinski --- drivers/net/pcs/pcs-altera-tse.c | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/drivers/net/pcs/pcs-altera-tse.c b/drivers/net/pcs/pcs-altera-tse.c index 97a7cabff962..e86cadc391e8 100644 --- a/drivers/net/pcs/pcs-altera-tse.c +++ b/drivers/net/pcs/pcs-altera-tse.c @@ -60,7 +60,6 @@ static void tse_pcs_write(struct altera_tse_pcs *tse_pcs, int regnum, static int tse_pcs_reset(struct altera_tse_pcs *tse_pcs) { - int i = 0; u16 bmcr; /* Reset PCS block */ @@ -68,13 +67,9 @@ static int tse_pcs_reset(struct altera_tse_pcs *tse_pcs) bmcr |= BMCR_RESET; tse_pcs_write(tse_pcs, MII_BMCR, bmcr); - for (i = 0; i < SGMII_PCS_SW_RESET_TIMEOUT; i++) { - if (!(tse_pcs_read(tse_pcs, MII_BMCR) & BMCR_RESET)) - return 0; - udelay(1); - } - - return -ETIMEDOUT; + return read_poll_timeout(tse_pcs_read, bmcr, (bmcr & BMCR_RESET), + 10, SGMII_PCS_SW_RESET_TIMEOUT, 1, + tse_pcs, MII_BMCR); } static int alt_tse_pcs_validate(struct phylink_pcs *pcs, From b4a7bf9f5bb8e6b21d728d00dc3afe9fbee2420c Mon Sep 17 00:00:00 2001 From: Maxime Chevallier Date: Fri, 25 Nov 2022 14:18:00 +0100 Subject: [PATCH 1312/2002] net: pcs: altera-tse: don't set the speed for 1000BaseX When disabling the SGMII mode bit, the PCS defaults to 1000BaseX mode. In that mode, we don't need to set the speed since it's always 1000Mbps. Signed-off-by: Maxime Chevallier Signed-off-by: Jakub Kicinski --- drivers/net/pcs/pcs-altera-tse.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/net/pcs/pcs-altera-tse.c b/drivers/net/pcs/pcs-altera-tse.c index e86cadc391e8..be65271ff5de 100644 --- a/drivers/net/pcs/pcs-altera-tse.c +++ b/drivers/net/pcs/pcs-altera-tse.c @@ -102,7 +102,6 @@ static int alt_tse_pcs_config(struct phylink_pcs *pcs, unsigned int mode, if_mode |= PCS_IF_MODE_USE_SGMII_AN | PCS_IF_MODE_SGMII_ENA; } else if (interface == PHY_INTERFACE_MODE_1000BASEX) { if_mode &= ~(PCS_IF_MODE_USE_SGMII_AN | PCS_IF_MODE_SGMII_ENA); - if_mode |= PCS_IF_MODE_SGMI_SPEED_1000; } ctrl |= (BMCR_SPEED1000 | BMCR_FULLDPLX | BMCR_ANENABLE); From befd851de29543205246468abe3e7793f26c7e2f Mon Sep 17 00:00:00 2001 From: Maxime Chevallier Date: Fri, 25 Nov 2022 14:18:01 +0100 Subject: [PATCH 1313/2002] net: pcs: altera-tse: remove unnecessary register definitions remove unused register definitions, left from the split with the altera-tse mac driver. Signed-off-by: Maxime Chevallier Signed-off-by: Jakub Kicinski --- drivers/net/pcs/pcs-altera-tse.c | 9 --------- 1 file changed, 9 deletions(-) diff --git a/drivers/net/pcs/pcs-altera-tse.c b/drivers/net/pcs/pcs-altera-tse.c index be65271ff5de..d616749761f4 100644 --- a/drivers/net/pcs/pcs-altera-tse.c +++ b/drivers/net/pcs/pcs-altera-tse.c @@ -12,22 +12,13 @@ /* SGMII PCS register addresses */ -#define SGMII_PCS_SCRATCH 0x10 -#define SGMII_PCS_REV 0x11 #define SGMII_PCS_LINK_TIMER_0 0x12 -#define SGMII_PCS_LINK_TIMER_REG(x) (0x12 + (x)) #define SGMII_PCS_LINK_TIMER_1 0x13 #define SGMII_PCS_IF_MODE 0x14 #define PCS_IF_MODE_SGMII_ENA BIT(0) #define PCS_IF_MODE_USE_SGMII_AN BIT(1) -#define PCS_IF_MODE_SGMI_SPEED_MASK GENMASK(3, 2) -#define PCS_IF_MODE_SGMI_SPEED_10 (0 << 2) -#define PCS_IF_MODE_SGMI_SPEED_100 (1 << 2) -#define PCS_IF_MODE_SGMI_SPEED_1000 (2 << 2) #define PCS_IF_MODE_SGMI_HALF_DUPLEX BIT(4) #define PCS_IF_MODE_SGMI_PHY_AN BIT(5) -#define SGMII_PCS_DIS_READ_TO 0x15 -#define SGMII_PCS_READ_TO 0x16 #define SGMII_PCS_SW_RESET_TIMEOUT 100 /* usecs */ struct altera_tse_pcs { From 02ca1732f41b23bfb5c062e52b5fc44105c0796a Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Sun, 27 Nov 2022 17:16:52 +0100 Subject: [PATCH 1314/2002] net/mlx5e: Remove unneeded io-mapping.h #include The mlx5 net files don't use io_mapping functionalities. So there is no point in including . Remove it. Signed-off-by: Christophe JAILLET Reviewed-by: Pavan Chebbi Signed-off-by: Saeed Mahameed --- drivers/net/ethernet/mellanox/mlx5/core/cmd.c | 1 - drivers/net/ethernet/mellanox/mlx5/core/main.c | 1 - drivers/net/ethernet/mellanox/mlx5/core/uar.c | 1 - 3 files changed, 3 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/cmd.c b/drivers/net/ethernet/mellanox/mlx5/core/cmd.c index e7a894ba5c3e..d3ca745d107d 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/cmd.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/cmd.c @@ -37,7 +37,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/net/ethernet/mellanox/mlx5/core/main.c b/drivers/net/ethernet/mellanox/mlx5/core/main.c index 70e8dc305bec..25e87e5d9270 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/main.c @@ -37,7 +37,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/net/ethernet/mellanox/mlx5/core/uar.c b/drivers/net/ethernet/mellanox/mlx5/core/uar.c index 8455e79bc44a..1513112ecec8 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/uar.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/uar.c @@ -31,7 +31,6 @@ */ #include -#include #include #include "mlx5_core.h" From 5df5365ae4f793ce2e555d7d836abde3f5bd55b4 Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Mon, 26 Sep 2022 16:50:42 -0500 Subject: [PATCH 1315/2002] net/mlx5e: Replace zero-length arrays with DECLARE_FLEX_ARRAY() helper Zero-length arrays are deprecated and we are moving towards adopting C99 flexible-array members, instead. So, replace zero-length arrays declarations in anonymous union with the new DECLARE_FLEX_ARRAY() helper macro. This helper allows for flexible-array members in unions. Link: https://github.com/KSPP/linux/issues/193 Link: https://github.com/KSPP/linux/issues/222 Link: https://gcc.gnu.org/onlinedocs/gcc/Zero-Length.html Signed-off-by: Gustavo A. R. Silva Reviewed-by: Kees Cook Signed-off-by: Saeed Mahameed --- drivers/net/ethernet/mellanox/mlx5/core/en_tc.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.h b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.h index 48241317a535..0db41fa4a9a6 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.h @@ -97,8 +97,8 @@ struct mlx5_flow_attr { } lag; /* keep this union last */ union { - struct mlx5_esw_flow_attr esw_attr[0]; - struct mlx5_nic_flow_attr nic_attr[0]; + DECLARE_FLEX_ARRAY(struct mlx5_esw_flow_attr, esw_attr); + DECLARE_FLEX_ARRAY(struct mlx5_nic_flow_attr, nic_attr); }; }; From 12eb0f84a601cec8920b56d7ffd4182a6bfd6521 Mon Sep 17 00:00:00 2001 From: Petr Pavlu Date: Thu, 29 Sep 2022 13:45:08 +0200 Subject: [PATCH 1316/2002] net/mlx5: Remove unused ctx variables Remove mlx5_priv.ctx_list and ctx_lock which are no longer used after commit 601c10c89cbb ("net/mlx5: Delete custom device management logic"). Signed-off-by: Petr Pavlu Signed-off-by: Saeed Mahameed --- drivers/net/ethernet/mellanox/mlx5/core/main.c | 2 -- include/linux/mlx5/driver.h | 2 -- 2 files changed, 4 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/main.c b/drivers/net/ethernet/mellanox/mlx5/core/main.c index 25e87e5d9270..7f5db13e3550 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/main.c @@ -1603,8 +1603,6 @@ int mlx5_mdev_init(struct mlx5_core_dev *dev, int profile_idx) int err; memcpy(&dev->profile, &profile[profile_idx], sizeof(dev->profile)); - INIT_LIST_HEAD(&priv->ctx_list); - spin_lock_init(&priv->ctx_lock); lockdep_register_key(&dev->lock_key); mutex_init(&dev->intf_state_mutex); lockdep_set_class(&dev->intf_state_mutex, &dev->lock_key); diff --git a/include/linux/mlx5/driver.h b/include/linux/mlx5/driver.h index 06cbad166225..d476255c9a3f 100644 --- a/include/linux/mlx5/driver.h +++ b/include/linux/mlx5/driver.h @@ -606,8 +606,6 @@ struct mlx5_priv { struct list_head pgdir_list; /* end: alloc staff */ - struct list_head ctx_list; - spinlock_t ctx_lock; struct mlx5_adev **adev; int adev_idx; int sw_vhca_id; From b146658f2ed90768b769222ca418617274242b32 Mon Sep 17 00:00:00 2001 From: Tariq Toukan Date: Mon, 31 Oct 2022 14:18:22 +0200 Subject: [PATCH 1317/2002] net/mlx5e: Add padding when needed in UMR WQEs Per the device spec, MTTs/KLMs list in a UMR WQE must be aligned to 64B. Per our SW design, the MTT/KLMs list would need alignment only if it's too small, for example on PPC when PAGE_SIZE is 64KB, and only 4 pages are needed to cover a MPWQE of size 256KB. Padding, if needed, is taken into account when calculating the UMR WQE fields (ds_cnt and xlt_octowords), however no entries are provided, instead garbage is passed. No real harm though, as these parts act as gaps between the RX MPWQEs and not used by any of them. Hence, in practice, device does not try to write any incoming packet to them. Still, prefer providing clean padding marking the end of the list, and do not map garbage into the RQ memory region. Signed-off-by: Tariq Toukan Reviewed-by: Gal Pressman Signed-off-by: Saeed Mahameed --- drivers/net/ethernet/mellanox/mlx5/core/en_rx.c | 11 +++++++++++ include/linux/mlx5/device.h | 1 + 2 files changed, 12 insertions(+) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c b/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c index b1ea0b995d9c..8d71736116e0 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c @@ -727,6 +727,17 @@ static int mlx5e_alloc_rx_mpwqe(struct mlx5e_rq *rq, u16 ix) }; } + /* Pad if needed, in case the value set to ucseg->xlt_octowords + * in mlx5e_build_umr_wqe() needed alignment. + */ + if (rq->mpwqe.pages_per_wqe & (MLX5_UMR_MTT_NUM_ENTRIES_ALIGNMENT - 1)) { + int pad = ALIGN(rq->mpwqe.pages_per_wqe, MLX5_UMR_MTT_NUM_ENTRIES_ALIGNMENT) - + rq->mpwqe.pages_per_wqe; + + memset(&umr_wqe->inline_mtts[rq->mpwqe.pages_per_wqe], 0, + sizeof(*umr_wqe->inline_mtts) * pad); + } + bitmap_zero(wi->xdp_xmit_bitmap, rq->mpwqe.pages_per_wqe); wi->consumed_strides = 0; diff --git a/include/linux/mlx5/device.h b/include/linux/mlx5/device.h index eb3fac30488b..97275965f156 100644 --- a/include/linux/mlx5/device.h +++ b/include/linux/mlx5/device.h @@ -294,6 +294,7 @@ enum { #define MLX5_UMR_MTT_ALIGNMENT 0x40 #define MLX5_UMR_MTT_MASK (MLX5_UMR_MTT_ALIGNMENT - 1) #define MLX5_UMR_MTT_MIN_CHUNK_SIZE MLX5_UMR_MTT_ALIGNMENT +#define MLX5_UMR_MTT_NUM_ENTRIES_ALIGNMENT (MLX5_UMR_MTT_ALIGNMENT / sizeof(struct mlx5_mtt)) #define MLX5_USER_INDEX_LEN (MLX5_FLD_SZ_BYTES(qpc, user_index) * 8) From 683d78a0d46235edfd3c50ddbc4af1065b422670 Mon Sep 17 00:00:00 2001 From: Tariq Toukan Date: Mon, 31 Oct 2022 14:30:23 +0200 Subject: [PATCH 1318/2002] net/mlx5: Remove unused UMR MTT definitions Defines MLX5_UMR_MTT_MASK and MLX5_UMR_MTT_MIN_CHUNK_SIZE are not in use. Remove them. Signed-off-by: Tariq Toukan Reviewed-by: Gal Pressman Signed-off-by: Saeed Mahameed --- include/linux/mlx5/device.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/include/linux/mlx5/device.h b/include/linux/mlx5/device.h index 97275965f156..ecf840e2989b 100644 --- a/include/linux/mlx5/device.h +++ b/include/linux/mlx5/device.h @@ -292,8 +292,6 @@ enum { #define MLX5_UMR_KLM_ALIGNMENT 4 #define MLX5_UMR_MTT_ALIGNMENT 0x40 -#define MLX5_UMR_MTT_MASK (MLX5_UMR_MTT_ALIGNMENT - 1) -#define MLX5_UMR_MTT_MIN_CHUNK_SIZE MLX5_UMR_MTT_ALIGNMENT #define MLX5_UMR_MTT_NUM_ENTRIES_ALIGNMENT (MLX5_UMR_MTT_ALIGNMENT / sizeof(struct mlx5_mtt)) #define MLX5_USER_INDEX_LEN (MLX5_FLD_SZ_BYTES(qpc, user_index) * 8) From 02648b4b09d506bd4df2e17bf109c229fc728640 Mon Sep 17 00:00:00 2001 From: Tariq Toukan Date: Mon, 31 Oct 2022 14:44:57 +0200 Subject: [PATCH 1319/2002] net/mlx5: Generalize name of UMR alignment definition Per the device spec, MLX5_UMR_MTT_ALIGNMENT is good not only for UMR MTT entries, but for all other entries as well, like KLMs and KSMs. Signed-off-by: Tariq Toukan Reviewed-by: Gal Pressman Signed-off-by: Saeed Mahameed --- drivers/infiniband/hw/mlx5/odp.c | 3 +-- drivers/infiniband/hw/mlx5/umr.c | 14 +++++++------- drivers/net/ethernet/mellanox/mlx5/core/en.h | 6 +++--- .../net/ethernet/mellanox/mlx5/core/en/params.c | 4 ++-- drivers/net/ethernet/mellanox/mlx5/core/en_main.c | 2 +- include/linux/mlx5/device.h | 4 ++-- 6 files changed, 16 insertions(+), 17 deletions(-) diff --git a/drivers/infiniband/hw/mlx5/odp.c b/drivers/infiniband/hw/mlx5/odp.c index bc97958818bb..e6e021af6aa9 100644 --- a/drivers/infiniband/hw/mlx5/odp.c +++ b/drivers/infiniband/hw/mlx5/odp.c @@ -230,8 +230,7 @@ static bool mlx5_ib_invalidate_range(struct mmu_interval_notifier *mni, struct ib_umem_odp *umem_odp = container_of(mni, struct ib_umem_odp, notifier); struct mlx5_ib_mr *mr; - const u64 umr_block_mask = (MLX5_UMR_MTT_ALIGNMENT / - sizeof(struct mlx5_mtt)) - 1; + const u64 umr_block_mask = MLX5_UMR_MTT_NUM_ENTRIES_ALIGNMENT - 1; u64 idx = 0, blk_start_idx = 0; u64 invalidations = 0; unsigned long start; diff --git a/drivers/infiniband/hw/mlx5/umr.c b/drivers/infiniband/hw/mlx5/umr.c index d5105b5c9979..029e9536ec28 100644 --- a/drivers/infiniband/hw/mlx5/umr.c +++ b/drivers/infiniband/hw/mlx5/umr.c @@ -418,7 +418,7 @@ int mlx5r_umr_rereg_pd_access(struct mlx5_ib_mr *mr, struct ib_pd *pd, } #define MLX5_MAX_UMR_CHUNK \ - ((1 << (MLX5_MAX_UMR_SHIFT + 4)) - MLX5_UMR_MTT_ALIGNMENT) + ((1 << (MLX5_MAX_UMR_SHIFT + 4)) - MLX5_UMR_FLEX_ALIGNMENT) #define MLX5_SPARE_UMR_CHUNK 0x10000 /* @@ -428,11 +428,11 @@ int mlx5r_umr_rereg_pd_access(struct mlx5_ib_mr *mr, struct ib_pd *pd, */ static void *mlx5r_umr_alloc_xlt(size_t *nents, size_t ent_size, gfp_t gfp_mask) { - const size_t xlt_chunk_align = MLX5_UMR_MTT_ALIGNMENT / ent_size; + const size_t xlt_chunk_align = MLX5_UMR_FLEX_ALIGNMENT / ent_size; size_t size; void *res = NULL; - static_assert(PAGE_SIZE % MLX5_UMR_MTT_ALIGNMENT == 0); + static_assert(PAGE_SIZE % MLX5_UMR_FLEX_ALIGNMENT == 0); /* * MLX5_IB_UPD_XLT_ATOMIC doesn't signal an atomic context just that the @@ -666,7 +666,7 @@ int mlx5r_umr_update_mr_pas(struct mlx5_ib_mr *mr, unsigned int flags) } final_size = (void *)cur_mtt - (void *)mtt; - sg.length = ALIGN(final_size, MLX5_UMR_MTT_ALIGNMENT); + sg.length = ALIGN(final_size, MLX5_UMR_FLEX_ALIGNMENT); memset(cur_mtt, 0, sg.length - final_size); mlx5r_umr_final_update_xlt(dev, &wqe, mr, &sg, flags); @@ -690,7 +690,7 @@ int mlx5r_umr_update_xlt(struct mlx5_ib_mr *mr, u64 idx, int npages, int desc_size = (flags & MLX5_IB_UPD_XLT_INDIRECT) ? sizeof(struct mlx5_klm) : sizeof(struct mlx5_mtt); - const int page_align = MLX5_UMR_MTT_ALIGNMENT / desc_size; + const int page_align = MLX5_UMR_FLEX_ALIGNMENT / desc_size; struct mlx5_ib_dev *dev = mr_to_mdev(mr); struct device *ddev = &dev->mdev->pdev->dev; const int page_mask = page_align - 1; @@ -711,7 +711,7 @@ int mlx5r_umr_update_xlt(struct mlx5_ib_mr *mr, u64 idx, int npages, if (WARN_ON(!mr->umem->is_odp)) return -EINVAL; - /* UMR copies MTTs in units of MLX5_UMR_MTT_ALIGNMENT bytes, + /* UMR copies MTTs in units of MLX5_UMR_FLEX_ALIGNMENT bytes, * so we need to align the offset and length accordingly */ if (idx & page_mask) { @@ -748,7 +748,7 @@ int mlx5r_umr_update_xlt(struct mlx5_ib_mr *mr, u64 idx, int npages, mlx5_odp_populate_xlt(xlt, idx, npages, mr, flags); dma_sync_single_for_device(ddev, sg.addr, sg.length, DMA_TO_DEVICE); - sg.length = ALIGN(size_to_map, MLX5_UMR_MTT_ALIGNMENT); + sg.length = ALIGN(size_to_map, MLX5_UMR_FLEX_ALIGNMENT); if (pages_mapped + pages_iter >= pages_to_map) mlx5r_umr_final_update_xlt(dev, &wqe, mr, &sg, flags); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en.h b/drivers/net/ethernet/mellanox/mlx5/core/en.h index ff5b302531d5..3cad59ac1b48 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en.h @@ -103,11 +103,11 @@ struct page_pool; * size actually used at runtime, but it's not a problem when calculating static * array sizes. */ -#define MLX5_UMR_MAX_MTT_SPACE \ +#define MLX5_UMR_MAX_FLEX_SPACE \ (ALIGN_DOWN(MLX5_SEND_WQE_MAX_SIZE - sizeof(struct mlx5e_umr_wqe), \ - MLX5_UMR_MTT_ALIGNMENT)) + MLX5_UMR_FLEX_ALIGNMENT)) #define MLX5_MPWRQ_MAX_PAGES_PER_WQE \ - rounddown_pow_of_two(MLX5_UMR_MAX_MTT_SPACE / sizeof(struct mlx5_mtt)) + rounddown_pow_of_two(MLX5_UMR_MAX_FLEX_SPACE / sizeof(struct mlx5_mtt)) #define MLX5E_MAX_RQ_NUM_MTTS \ (ALIGN_DOWN(U16_MAX, 4) * 2) /* Fits into u16 and aligned by WQEBB. */ diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/params.c b/drivers/net/ethernet/mellanox/mlx5/core/en/params.c index 9ec9662d1d0b..585bdc8383ee 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/params.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/params.c @@ -107,7 +107,7 @@ u8 mlx5e_mpwrq_log_wqe_sz(struct mlx5_core_dev *mdev, u8 page_shift, /* Keep in sync with MLX5_MPWRQ_MAX_PAGES_PER_WQE. */ max_wqe_size = mlx5e_get_max_sq_aligned_wqebbs(mdev) * MLX5_SEND_WQE_BB; max_pages_per_wqe = ALIGN_DOWN(max_wqe_size - sizeof(struct mlx5e_umr_wqe), - MLX5_UMR_MTT_ALIGNMENT) / umr_entry_size; + MLX5_UMR_FLEX_ALIGNMENT) / umr_entry_size; max_log_mpwqe_size = ilog2(max_pages_per_wqe) + page_shift; WARN_ON_ONCE(max_log_mpwqe_size < MLX5E_ORDER2_MAX_PACKET_MTU); @@ -146,7 +146,7 @@ u16 mlx5e_mpwrq_umr_wqe_sz(struct mlx5_core_dev *mdev, u8 page_shift, u16 umr_wqe_sz; umr_wqe_sz = sizeof(struct mlx5e_umr_wqe) + - ALIGN(pages_per_wqe * umr_entry_size, MLX5_UMR_MTT_ALIGNMENT); + ALIGN(pages_per_wqe * umr_entry_size, MLX5_UMR_FLEX_ALIGNMENT); WARN_ON_ONCE(DIV_ROUND_UP(umr_wqe_sz, MLX5_SEND_WQE_DS) > MLX5_WQE_CTRL_DS_MASK); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c index 217c8a478977..199387c6bf16 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c @@ -208,7 +208,7 @@ static u16 mlx5e_mpwrq_umr_octowords(u32 entries, enum mlx5e_mpwrq_umr_mode umr_ u8 umr_entry_size = mlx5e_mpwrq_umr_entry_size(umr_mode); u32 sz; - sz = ALIGN(entries * umr_entry_size, MLX5_UMR_MTT_ALIGNMENT); + sz = ALIGN(entries * umr_entry_size, MLX5_UMR_FLEX_ALIGNMENT); return sz / MLX5_OCTWORD; } diff --git a/include/linux/mlx5/device.h b/include/linux/mlx5/device.h index ecf840e2989b..a02f779f5c5b 100644 --- a/include/linux/mlx5/device.h +++ b/include/linux/mlx5/device.h @@ -291,8 +291,8 @@ enum { }; #define MLX5_UMR_KLM_ALIGNMENT 4 -#define MLX5_UMR_MTT_ALIGNMENT 0x40 -#define MLX5_UMR_MTT_NUM_ENTRIES_ALIGNMENT (MLX5_UMR_MTT_ALIGNMENT / sizeof(struct mlx5_mtt)) +#define MLX5_UMR_FLEX_ALIGNMENT 0x40 +#define MLX5_UMR_MTT_NUM_ENTRIES_ALIGNMENT (MLX5_UMR_FLEX_ALIGNMENT / sizeof(struct mlx5_mtt)) #define MLX5_USER_INDEX_LEN (MLX5_FLD_SZ_BYTES(qpc, user_index) * 8) From daab2e9c54a52dea8dbd1af516e6f986eeed2556 Mon Sep 17 00:00:00 2001 From: Tariq Toukan Date: Mon, 31 Oct 2022 14:24:02 +0200 Subject: [PATCH 1320/2002] net/mlx5: Use generic definition for UMR KLM alignment MLX5_UMR_KLM_ALIGNMENT is in units of number of entries, while MLX5_UMR_MTT_ALIGNMENT (generalized and renamed to MLX5_UMR_FLEX_ALIGNMENT) is in byte units. This is misleading and confusing. Replace this KLM definition with one based on the generic definition. Signed-off-by: Tariq Toukan Reviewed-by: Gal Pressman Signed-off-by: Saeed Mahameed --- drivers/net/ethernet/mellanox/mlx5/core/en.h | 2 +- drivers/net/ethernet/mellanox/mlx5/core/en_rx.c | 10 +++++----- include/linux/mlx5/device.h | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en.h b/drivers/net/ethernet/mellanox/mlx5/core/en.h index 3cad59ac1b48..65790ff58a74 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en.h @@ -160,7 +160,7 @@ struct page_pool; (((wqe_size) - sizeof(struct mlx5e_umr_wqe)) / sizeof(struct mlx5_klm)) #define MLX5E_KLM_ENTRIES_PER_WQE(wqe_size)\ - ALIGN_DOWN(MLX5E_KLM_MAX_ENTRIES_PER_WQE(wqe_size), MLX5_UMR_KLM_ALIGNMENT) + ALIGN_DOWN(MLX5E_KLM_MAX_ENTRIES_PER_WQE(wqe_size), MLX5_UMR_KLM_NUM_ENTRIES_ALIGNMENT) #define MLX5E_MAX_KLM_PER_WQE(mdev) \ MLX5E_KLM_ENTRIES_PER_WQE(MLX5_SEND_WQE_BB * mlx5e_get_max_sq_aligned_wqebbs(mdev)) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c b/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c index 8d71736116e0..c8820ab22169 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c @@ -593,8 +593,8 @@ static int mlx5e_build_shampo_hd_umr(struct mlx5e_rq *rq, int headroom, i; headroom = rq->buff.headroom; - new_entries = klm_entries - (shampo->pi & (MLX5_UMR_KLM_ALIGNMENT - 1)); - entries = ALIGN(klm_entries, MLX5_UMR_KLM_ALIGNMENT); + new_entries = klm_entries - (shampo->pi & (MLX5_UMR_KLM_NUM_ENTRIES_ALIGNMENT - 1)); + entries = ALIGN(klm_entries, MLX5_UMR_KLM_NUM_ENTRIES_ALIGNMENT); wqe_bbs = MLX5E_KLM_UMR_WQEBBS(entries); pi = mlx5e_icosq_get_next_pi(sq, wqe_bbs); umr_wqe = mlx5_wq_cyc_get_wqe(&sq->wq, pi); @@ -603,7 +603,7 @@ static int mlx5e_build_shampo_hd_umr(struct mlx5e_rq *rq, for (i = 0; i < entries; i++, index++) { dma_info = &shampo->info[index]; if (i >= klm_entries || (index < shampo->pi && shampo->pi - index < - MLX5_UMR_KLM_ALIGNMENT)) + MLX5_UMR_KLM_NUM_ENTRIES_ALIGNMENT)) goto update_klm; header_offset = (index & (MLX5E_SHAMPO_WQ_HEADER_PER_PAGE - 1)) << MLX5E_SHAMPO_LOG_MAX_HEADER_ENTRY_SIZE; @@ -668,8 +668,8 @@ static int mlx5e_alloc_rx_hd_mpwqe(struct mlx5e_rq *rq) if (!klm_entries) return 0; - klm_entries += (shampo->pi & (MLX5_UMR_KLM_ALIGNMENT - 1)); - index = ALIGN_DOWN(shampo->pi, MLX5_UMR_KLM_ALIGNMENT); + klm_entries += (shampo->pi & (MLX5_UMR_KLM_NUM_ENTRIES_ALIGNMENT - 1)); + index = ALIGN_DOWN(shampo->pi, MLX5_UMR_KLM_NUM_ENTRIES_ALIGNMENT); entries_before = shampo->hd_per_wq - index; if (unlikely(entries_before < klm_entries)) diff --git a/include/linux/mlx5/device.h b/include/linux/mlx5/device.h index a02f779f5c5b..5fe5d198b57a 100644 --- a/include/linux/mlx5/device.h +++ b/include/linux/mlx5/device.h @@ -290,9 +290,9 @@ enum { MLX5_UMR_INLINE = (1 << 7), }; -#define MLX5_UMR_KLM_ALIGNMENT 4 #define MLX5_UMR_FLEX_ALIGNMENT 0x40 #define MLX5_UMR_MTT_NUM_ENTRIES_ALIGNMENT (MLX5_UMR_FLEX_ALIGNMENT / sizeof(struct mlx5_mtt)) +#define MLX5_UMR_KLM_NUM_ENTRIES_ALIGNMENT (MLX5_UMR_FLEX_ALIGNMENT / sizeof(struct mlx5_klm)) #define MLX5_USER_INDEX_LEN (MLX5_FLD_SZ_BYTES(qpc, user_index) * 8) From 2d04e1ce52a8b2ba77820383c1305122e0238be2 Mon Sep 17 00:00:00 2001 From: Rahul Rameshbabu Date: Wed, 26 Oct 2022 12:04:40 -0700 Subject: [PATCH 1321/2002] net/mlx5: Fix orthography errors in documentation Improve general readability of the device driver documentation. Signed-off-by: Rahul Rameshbabu Reviewed-by: Tariq Toukan Reviewed-by: Gal Pressman Signed-off-by: Saeed Mahameed --- .../device_drivers/ethernet/mellanox/mlx5.rst | 82 +++++++++---------- 1 file changed, 41 insertions(+), 41 deletions(-) diff --git a/Documentation/networking/device_drivers/ethernet/mellanox/mlx5.rst b/Documentation/networking/device_drivers/ethernet/mellanox/mlx5.rst index 5edf50d7dbd5..e8fa7ac9e6b1 100644 --- a/Documentation/networking/device_drivers/ethernet/mellanox/mlx5.rst +++ b/Documentation/networking/device_drivers/ethernet/mellanox/mlx5.rst @@ -25,7 +25,7 @@ Enabling the driver and kconfig options | at build time via kernel Kconfig flags. | Basic features, ethernet net device rx/tx offloads and XDP, are available with the most basic flags | CONFIG_MLX5_CORE=y/m and CONFIG_MLX5_CORE_EN=y. -| For the list of advanced features please see below. +| For the list of advanced features, please see below. **CONFIG_MLX5_CORE=(y/m/n)** (module mlx5_core.ko) @@ -89,11 +89,11 @@ Enabling the driver and kconfig options **CONFIG_MLX5_EN_IPSEC=(y/n)** -| Enables `IPSec XFRM cryptography-offload accelaration `_. +| Enables `IPSec XFRM cryptography-offload acceleration `_. **CONFIG_MLX5_EN_TLS=(y/n)** -| TLS cryptography-offload accelaration. +| TLS cryptography-offload acceleration. **CONFIG_MLX5_INFINIBAND=(y/n/m)** (module mlx5_ib.ko) @@ -139,14 +139,14 @@ flow_steering_mode: Device flow steering mode The flow steering mode parameter controls the flow steering mode of the driver. Two modes are supported: 1. 'dmfs' - Device managed flow steering. -2. 'smfs - Software/Driver managed flow steering. +2. 'smfs' - Software/Driver managed flow steering. In DMFS mode, the HW steering entities are created and managed through the Firmware. In SMFS mode, the HW steering entities are created and managed though by -the driver directly into Hardware without firmware intervention. +the driver directly into hardware without firmware intervention. -SMFS mode is faster and provides better rule inserstion rate compared to default DMFS mode. +SMFS mode is faster and provides better rule insertion rate compared to default DMFS mode. User command examples: @@ -165,9 +165,9 @@ User command examples: enable_roce: RoCE enablement state ---------------------------------- RoCE enablement state controls driver support for RoCE traffic. -When RoCE is disabled, there is no gid table, only raw ethernet QPs are supported and traffic on the well known UDP RoCE port is handled as raw ethernet traffic. +When RoCE is disabled, there is no gid table, only raw ethernet QPs are supported and traffic on the well-known UDP RoCE port is handled as raw ethernet traffic. -To change RoCE enablement state a user must change the driverinit cmode value and run devlink reload. +To change RoCE enablement state, a user must change the driverinit cmode value and run devlink reload. User command examples: @@ -186,7 +186,7 @@ User command examples: esw_port_metadata: Eswitch port metadata state ---------------------------------------------- -When applicable, disabling Eswitch metadata can increase packet rate +When applicable, disabling eswitch metadata can increase packet rate up to 20% depending on the use case and packet sizes. Eswitch port metadata state controls whether to internally tag packets with @@ -253,26 +253,26 @@ mlx5 subfunction ================ mlx5 supports subfunction management using devlink port (see :ref:`Documentation/networking/devlink/devlink-port.rst `) interface. -A Subfunction has its own function capabilities and its own resources. This +A subfunction has its own function capabilities and its own resources. This means a subfunction has its own dedicated queues (txq, rxq, cq, eq). These queues are neither shared nor stolen from the parent PCI function. -When a subfunction is RDMA capable, it has its own QP1, GID table and rdma +When a subfunction is RDMA capable, it has its own QP1, GID table, and RDMA resources neither shared nor stolen from the parent PCI function. A subfunction has a dedicated window in PCI BAR space that is not shared -with ther other subfunctions or the parent PCI function. This ensures that all -devices (netdev, rdma, vdpa etc.) of the subfunction accesses only assigned +with the other subfunctions or the parent PCI function. This ensures that all +devices (netdev, rdma, vdpa, etc.) of the subfunction accesses only assigned PCI BAR space. -A Subfunction supports eswitch representation through which it supports tc +A subfunction supports eswitch representation through which it supports tc offloads. The user configures eswitch to send/receive packets from/to the subfunction port. Subfunctions share PCI level resources such as PCI MSI-X IRQs with other subfunctions and/or with its parent PCI function. -Example mlx5 software, system and device view:: +Example mlx5 software, system, and device view:: _______ | admin | @@ -310,7 +310,7 @@ Example mlx5 software, system and device view:: | (device add/del) _____|____ ____|________ | | | subfunction | - | PCI NIC |---- activate/deactive events---->| host driver | + | PCI NIC |--- activate/deactivate events--->| host driver | |__________| | (mlx5_core) | |_____________| @@ -320,7 +320,7 @@ Subfunction is created using devlink port interface. $ devlink dev eswitch set pci/0000:06:00.0 mode switchdev -- Add a devlink port of subfunction flaovur:: +- Add a devlink port of subfunction flavour:: $ devlink port add pci/0000:06:00.0 flavour pcisf pfnum 0 sfnum 88 pci/0000:06:00.0/32768: type eth netdev eth6 flavour pcisf controller 0 pfnum 0 sfnum 88 external false splittable false @@ -379,18 +379,18 @@ device created for the PCI VF/SF. function: hw_addr 00:00:00:00:00:00 -- Set the MAC address of the VF identified by its unique devlink port index:: +- Set the MAC address of the SF identified by its unique devlink port index:: $ devlink port function set pci/0000:06:00.0/32768 hw_addr 00:00:00:00:88:88 $ devlink port show pci/0000:06:00.0/32768 - pci/0000:06:00.0/32768: type eth netdev enp6s0pf0sf88 flavour pcivf pfnum 0 sfnum 88 + pci/0000:06:00.0/32768: type eth netdev enp6s0pf0sf88 flavour pcisf pfnum 0 sfnum 88 function: hw_addr 00:00:00:00:88:88 SF state setup -------------- -To use the SF, the user must active the SF using the SF function state +To use the SF, the user must activate the SF using the SF function state attribute. - Get the state of the SF identified by its unique devlink port index:: @@ -447,7 +447,7 @@ for it. Additionally, the SF port also gets the event when the driver attaches to the auxiliary device of the subfunction. This results in changing the operational -state of the function. This provides visiblity to the user to decide when is it +state of the function. This provides visibility to the user to decide when is it safe to delete the SF port for graceful termination of the subfunction. - Show the SF port operational state:: @@ -464,14 +464,14 @@ tx reporter ----------- The tx reporter is responsible for reporting and recovering of the following two error scenarios: -- TX timeout +- tx timeout Report on kernel tx timeout detection. Recover by searching lost interrupts. -- TX error completion +- tx error completion Report on error tx completion. - Recover by flushing the TX queue and reset it. + Recover by flushing the tx queue and reset it. -TX reporter also support on demand diagnose callback, on which it provides +tx reporter also support on demand diagnose callback, on which it provides real time information of its send queues status. User commands examples: @@ -491,32 +491,32 @@ rx reporter ----------- The rx reporter is responsible for reporting and recovering of the following two error scenarios: -- RX queues initialization (population) timeout - RX queues descriptors population on ring initialization is done in - napi context via triggering an irq, in case of a failure to get - the minimum amount of descriptors, a timeout would occur and it - could be recoverable by polling the EQ (Event Queue). -- RX completions with errors (reported by HW on interrupt context) +- rx queues' initialization (population) timeout + Population of rx queues' descriptors on ring initialization is done + in napi context via triggering an irq. In case of a failure to get + the minimum amount of descriptors, a timeout would occur, and + descriptors could be recovered by polling the EQ (Event Queue). +- rx completions with errors (reported by HW on interrupt context) Report on rx completion error. Recover (if needed) by flushing the related queue and reset it. -RX reporter also supports on demand diagnose callback, on which it -provides real time information of its receive queues status. +rx reporter also supports on demand diagnose callback, on which it +provides real time information of its receive queues' status. -- Diagnose rx queues status, and corresponding completion queue:: +- Diagnose rx queues' status and corresponding completion queue:: $ devlink health diagnose pci/0000:82:00.0 reporter rx -NOTE: This command has valid output only when interface is up, otherwise the command has empty output. +NOTE: This command has valid output only when interface is up. Otherwise, the command has empty output. - Show number of rx errors indicated, number of recover flows ended successfully, - is autorecover enabled and graceful period from last recover:: + is autorecover enabled, and graceful period from last recover:: $ devlink health show pci/0000:82:00.0 reporter rx fw reporter ----------- -The fw reporter implements diagnose and dump callbacks. +The fw reporter implements `diagnose` and `dump` callbacks. It follows symptoms of fw error such as fw syndrome by triggering fw core dump and storing it into the dump buffer. The fw reporter diagnose command can be triggered any time by the user to check @@ -537,7 +537,7 @@ running it on other PF or any VF will return "Operation not permitted". fw fatal reporter ----------------- -The fw fatal reporter implements dump and recover callbacks. +The fw fatal reporter implements `dump` and `recover` callbacks. It follows fatal errors indications by CR-space dump and recover flow. The CR-space dump uses vsc interface which is valid even if the FW command interface is not functional, which is the case in most FW fatal errors. @@ -552,7 +552,7 @@ User commands examples: $ devlink health recover pci/0000:82:00.0 reporter fw_fatal -- Read FW CR-space dump if already strored or trigger new one:: +- Read FW CR-space dump if already stored or trigger new one:: $ devlink health dump show pci/0000:82:00.1 reporter fw_fatal @@ -561,10 +561,10 @@ NOTE: This command can run only on PF. mlx5 tracepoints ================ -mlx5 driver provides internal trace points for tracking and debugging using +mlx5 driver provides internal tracepoints for tracking and debugging using kernel tracepoints interfaces (refer to Documentation/trace/ftrace.rst). -For the list of support mlx5 events check /sys/kernel/debug/tracing/events/mlx5/ +For the list of support mlx5 events, check `/sys/kernel/debug/tracing/events/mlx5/`. tc and eswitch offloads tracepoints: From 14624d7247fcd0f3114a6f5f17b3c8d1020fbbb7 Mon Sep 17 00:00:00 2001 From: Roi Dayan Date: Wed, 16 Nov 2022 09:47:37 +0200 Subject: [PATCH 1322/2002] net/mlx5e: Don't use termination table when redundant Current code used termination table for each vport destination while it's only required for hairpin, i.e. uplink to uplink, or when vlan push on rx action being used. Fix to skip using termination table for vport destinations that do not require it. Signed-off-by: Roi Dayan Reviewed-by: Maor Dickman Signed-off-by: Saeed Mahameed --- .../mlx5/core/eswitch_offloads_termtbl.c | 32 ++++++++++++++++--- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads_termtbl.c b/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads_termtbl.c index edd910258314..3a9a6bb9158d 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads_termtbl.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads_termtbl.c @@ -210,6 +210,18 @@ static bool mlx5_eswitch_offload_is_uplink_port(const struct mlx5_eswitch *esw, return (port_mask & port_value) == MLX5_VPORT_UPLINK; } +static bool +mlx5_eswitch_is_push_vlan_no_cap(struct mlx5_eswitch *esw, + struct mlx5_flow_act *flow_act) +{ + if (flow_act->action & MLX5_FLOW_CONTEXT_ACTION_VLAN_PUSH && + !(mlx5_fs_get_capabilities(esw->dev, MLX5_FLOW_NAMESPACE_FDB) & + MLX5_FLOW_STEERING_CAP_VLAN_PUSH_ON_RX)) + return true; + + return false; +} + bool mlx5_eswitch_termtbl_required(struct mlx5_eswitch *esw, struct mlx5_flow_attr *attr, @@ -225,10 +237,7 @@ mlx5_eswitch_termtbl_required(struct mlx5_eswitch *esw, (!mlx5_eswitch_offload_is_uplink_port(esw, spec) && !esw_attr->int_port)) return false; - /* push vlan on RX */ - if (flow_act->action & MLX5_FLOW_CONTEXT_ACTION_VLAN_PUSH && - !(mlx5_fs_get_capabilities(esw->dev, MLX5_FLOW_NAMESPACE_FDB) & - MLX5_FLOW_STEERING_CAP_VLAN_PUSH_ON_RX)) + if (mlx5_eswitch_is_push_vlan_no_cap(esw, flow_act)) return true; /* hairpin */ @@ -252,19 +261,31 @@ mlx5_eswitch_add_termtbl_rule(struct mlx5_eswitch *esw, struct mlx5_flow_act term_tbl_act = {}; struct mlx5_flow_handle *rule = NULL; bool term_table_created = false; + bool is_push_vlan_on_rx; int num_vport_dests = 0; int i, curr_dest; + is_push_vlan_on_rx = mlx5_eswitch_is_push_vlan_no_cap(esw, flow_act); mlx5_eswitch_termtbl_actions_move(flow_act, &term_tbl_act); term_tbl_act.action |= MLX5_FLOW_CONTEXT_ACTION_FWD_DEST; for (i = 0; i < num_dest; i++) { struct mlx5_termtbl_handle *tt; + bool hairpin = false; /* only vport destinations can be terminated */ if (dest[i].type != MLX5_FLOW_DESTINATION_TYPE_VPORT) continue; + if (attr->dests[num_vport_dests].rep && + attr->dests[num_vport_dests].rep->vport == MLX5_VPORT_UPLINK) + hairpin = true; + + if (!is_push_vlan_on_rx && !hairpin) { + num_vport_dests++; + continue; + } + if (attr->dests[num_vport_dests].flags & MLX5_ESW_DEST_ENCAP) { term_tbl_act.action |= MLX5_FLOW_CONTEXT_ACTION_PACKET_REFORMAT; term_tbl_act.pkt_reformat = attr->dests[num_vport_dests].pkt_reformat; @@ -312,6 +333,9 @@ revert_changes: for (curr_dest = 0; curr_dest < num_vport_dests; curr_dest++) { struct mlx5_termtbl_handle *tt = attr->dests[curr_dest].termtbl; + if (!tt) + continue; + attr->dests[curr_dest].termtbl = NULL; /* search for the destination associated with the From 7c11eae2fdc8ff429b74f6f9cd91ebea9725d917 Mon Sep 17 00:00:00 2001 From: Leon Romanovsky Date: Wed, 12 Oct 2022 15:28:10 +0300 Subject: [PATCH 1323/2002] net/mlx5e: Don't access directly DMA device pointer Use specialized helper to fetch DMA device pointer. Reviewed-by: Saeed Mahameed Signed-off-by: Leon Romanovsky Signed-off-by: Saeed Mahameed --- drivers/net/ethernet/mellanox/mlx5/core/en_accel/macsec.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/macsec.c b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/macsec.c index f900709639f6..7d5a27f7423f 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/macsec.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/macsec.c @@ -186,7 +186,7 @@ static int mlx5e_macsec_aso_reg_mr(struct mlx5_core_dev *mdev, struct mlx5e_macs return err; } - dma_device = &mdev->pdev->dev; + dma_device = mlx5_core_dma_dev(mdev); dma_addr = dma_map_single(dma_device, umr->ctx, sizeof(umr->ctx), DMA_BIDIRECTIONAL); err = dma_mapping_error(dma_device, dma_addr); if (err) { From d11c0ec2b831ee8420ff042819edeec7b5bb2418 Mon Sep 17 00:00:00 2001 From: Leon Romanovsky Date: Thu, 13 Oct 2022 15:13:39 +0300 Subject: [PATCH 1324/2002] net/mlx5e: Delete always true DMA check DMA address always exists for MACsec ASO object. Reviewed-by: Saeed Mahameed Signed-off-by: Leon Romanovsky Signed-off-by: Saeed Mahameed --- .../net/ethernet/mellanox/mlx5/core/en_accel/macsec.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/macsec.c b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/macsec.c index 7d5a27f7423f..9369a580743e 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/macsec.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/macsec.c @@ -1299,12 +1299,12 @@ static void macsec_aso_build_wqe_ctrl_seg(struct mlx5e_macsec_aso *macsec_aso, struct mlx5_wqe_aso_ctrl_seg *aso_ctrl, struct mlx5_aso_ctrl_param *param) { + struct mlx5e_macsec_umr *umr = macsec_aso->umr; + memset(aso_ctrl, 0, sizeof(*aso_ctrl)); - if (macsec_aso->umr->dma_addr) { - aso_ctrl->va_l = cpu_to_be32(macsec_aso->umr->dma_addr | ASO_CTRL_READ_EN); - aso_ctrl->va_h = cpu_to_be32((u64)macsec_aso->umr->dma_addr >> 32); - aso_ctrl->l_key = cpu_to_be32(macsec_aso->umr->mkey); - } + aso_ctrl->va_l = cpu_to_be32(umr->dma_addr | ASO_CTRL_READ_EN); + aso_ctrl->va_h = cpu_to_be32((u64)umr->dma_addr >> 32); + aso_ctrl->l_key = cpu_to_be32(umr->mkey); if (!param) return; From 3c683429b0786634fc75a2a4bf760d3f0fc3f25c Mon Sep 17 00:00:00 2001 From: Leon Romanovsky Date: Thu, 13 Oct 2022 15:48:43 +0300 Subject: [PATCH 1325/2002] net/mlx5: Remove redundant check If ASO failed in creation, it won't be called to destroy either. The kernel coding pattern is to make sure that callers are calling to destroy only for valid objects. Reviewed-by: Saeed Mahameed Signed-off-by: Leon Romanovsky Signed-off-by: Saeed Mahameed --- drivers/net/ethernet/mellanox/mlx5/core/lib/aso.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lib/aso.c b/drivers/net/ethernet/mellanox/mlx5/core/lib/aso.c index c971ff04dd04..0f9e4f01c85a 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/lib/aso.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/lib/aso.c @@ -334,9 +334,6 @@ err_cq: void mlx5_aso_destroy(struct mlx5_aso *aso) { - if (IS_ERR_OR_NULL(aso)) - return; - mlx5_aso_destroy_sq(aso); mlx5_aso_destroy_cq(&aso->cq); kfree(aso); From 42760d95a0c192218b57f033ef39f7b493ffec59 Mon Sep 17 00:00:00 2001 From: Roi Dayan Date: Mon, 21 Nov 2022 12:14:50 +0200 Subject: [PATCH 1326/2002] net/mlx5e: Do early return when setup vports dests for slow path flow Adding flow flag cases in setup vport dests before the slow path case is incorrect as the slow path should take precedence. Current code doesn't show this importance so make the slow path case return early and separate from the other cases and remove the redundant comparison of it in the sample case. Signed-off-by: Roi Dayan Reviewed-by: Chris Mi Reviewed-by: Oz Shlomo Signed-off-by: Saeed Mahameed --- .../ethernet/mellanox/mlx5/core/eswitch_offloads.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c b/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c index 8c6c9bcb3dc3..4b358b87fead 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c @@ -479,13 +479,15 @@ esw_setup_dests(struct mlx5_flow_destination *dest, esw_src_port_rewrite_supported(esw)) attr->flags |= MLX5_ATTR_FLAG_SRC_REWRITE; - if (attr->flags & MLX5_ATTR_FLAG_SAMPLE && - !(attr->flags & MLX5_ATTR_FLAG_SLOW_PATH)) { - esw_setup_sampler_dest(dest, flow_act, attr->sample_attr.sampler_id, *i); - (*i)++; - } else if (attr->flags & MLX5_ATTR_FLAG_SLOW_PATH) { + if (attr->flags & MLX5_ATTR_FLAG_SLOW_PATH) { esw_setup_slow_path_dest(dest, flow_act, esw, *i); (*i)++; + goto out; + } + + if (attr->flags & MLX5_ATTR_FLAG_SAMPLE) { + esw_setup_sampler_dest(dest, flow_act, attr->sample_attr.sampler_id, *i); + (*i)++; } else if (attr->flags & MLX5_ATTR_FLAG_ACCEPT) { esw_setup_accept_dest(dest, flow_act, chains, *i); (*i)++; @@ -506,6 +508,7 @@ esw_setup_dests(struct mlx5_flow_destination *dest, } } +out: return err; } From dcf19b9ce4fd2ad6d2fbfa1c3039919fc43968f4 Mon Sep 17 00:00:00 2001 From: Maor Dickman Date: Thu, 24 Nov 2022 14:21:03 +0200 Subject: [PATCH 1327/2002] net/mlx5e: TC, Add offload support for trap with additional actions TC trap action offload is currently supported only when trap is the sole action in the flow. This patch remove this limitation by changing trap action offload to not use MLX5_ATTR_FLAG_SLOW_PATH flag and instead set the flow destination table explicitly to be the slow table. This will allow offload of the additional actions. TC flow example: tc filter add dev $REP2 protocol ip prio 2 root \ flower skip_sw dst_mac $mac0 \ action mirred egress redirect dev $REP3 \ action pedit ex munge eth dst set $mac2 pipe \ action trap Signed-off-by: Maor Dickman Reviewed-by: Raed Salem Reviewed-by: Roi Dayan Signed-off-by: Saeed Mahameed --- .../mellanox/mlx5/core/en/tc/act/trap.c | 10 ++------- .../net/ethernet/mellanox/mlx5/core/eswitch.h | 5 +++++ .../mellanox/mlx5/core/eswitch_offloads.c | 22 +++++++++---------- 3 files changed, 18 insertions(+), 19 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/trap.c b/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/trap.c index 53b270f652b9..915ce201aeb2 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/trap.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/trap.c @@ -3,6 +3,7 @@ #include "act.h" #include "en/tc_priv.h" +#include "eswitch.h" static bool tc_act_can_offload_trap(struct mlx5e_tc_act_parse_state *parse_state, @@ -10,13 +11,6 @@ tc_act_can_offload_trap(struct mlx5e_tc_act_parse_state *parse_state, int act_index, struct mlx5_flow_attr *attr) { - struct netlink_ext_ack *extack = parse_state->extack; - - if (parse_state->flow_action->num_entries != 1) { - NL_SET_ERR_MSG_MOD(extack, "action trap is supported as a sole action only"); - return false; - } - return true; } @@ -27,7 +21,7 @@ tc_act_parse_trap(struct mlx5e_tc_act_parse_state *parse_state, struct mlx5_flow_attr *attr) { attr->action |= MLX5_FLOW_CONTEXT_ACTION_FWD_DEST; - attr->flags |= MLX5_ATTR_FLAG_SLOW_PATH; + attr->dest_ft = mlx5_eswitch_get_slow_fdb(priv->mdev->priv.eswitch); return 0; } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h index 3029bc1c0dd0..42d9df417e20 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h @@ -744,6 +744,11 @@ static inline int mlx5_eswitch_num_vfs(struct mlx5_eswitch *esw) return 0; } +static inline struct mlx5_flow_table * +mlx5_eswitch_get_slow_fdb(struct mlx5_eswitch *esw) +{ + return esw->fdb_table.offloads.slow_fdb; +} #else /* CONFIG_MLX5_ESWITCH */ /* eswitch API stubs */ static inline int mlx5_eswitch_init(struct mlx5_core_dev *dev) { return 0; } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c b/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c index 4b358b87fead..9b6fbb19c22a 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c @@ -248,7 +248,7 @@ esw_setup_slow_path_dest(struct mlx5_flow_destination *dest, struct mlx5_flow_ac if (MLX5_CAP_ESW_FLOWTABLE_FDB(esw->dev, ignore_flow_level)) flow_act->flags |= FLOW_ACT_IGNORE_FLOW_LEVEL; dest[i].type = MLX5_FLOW_DESTINATION_TYPE_FLOW_TABLE; - dest[i].ft = esw->fdb_table.offloads.slow_fdb; + dest[i].ft = mlx5_eswitch_get_slow_fdb(esw); } static int @@ -1049,7 +1049,7 @@ mlx5_eswitch_add_send_to_vport_rule(struct mlx5_eswitch *on_esw, if (rep->vport == MLX5_VPORT_UPLINK) spec->flow_context.flow_source = MLX5_FLOW_CONTEXT_FLOW_SOURCE_LOCAL_VPORT; - flow_rule = mlx5_add_flow_rules(on_esw->fdb_table.offloads.slow_fdb, + flow_rule = mlx5_add_flow_rules(mlx5_eswitch_get_slow_fdb(on_esw), spec, &flow_act, &dest, 1); if (IS_ERR(flow_rule)) esw_warn(on_esw->dev, "FDB: Failed to add send to vport rule err %ld\n", @@ -1098,7 +1098,7 @@ mlx5_eswitch_add_send_to_vport_meta_rule(struct mlx5_eswitch *esw, u16 vport_num mlx5_eswitch_get_vport_metadata_for_match(esw, vport_num)); dest.vport.num = vport_num; - flow_rule = mlx5_add_flow_rules(esw->fdb_table.offloads.slow_fdb, + flow_rule = mlx5_add_flow_rules(mlx5_eswitch_get_slow_fdb(esw), spec, &flow_act, &dest, 1); if (IS_ERR(flow_rule)) esw_warn(esw->dev, "FDB: Failed to add send to vport meta rule vport %d, err %ld\n", @@ -1251,7 +1251,7 @@ static int esw_add_fdb_peer_miss_rules(struct mlx5_eswitch *esw, esw_set_peer_miss_rule_source_port(esw, peer_dev->priv.eswitch, spec, MLX5_VPORT_PF); - flow = mlx5_add_flow_rules(esw->fdb_table.offloads.slow_fdb, + flow = mlx5_add_flow_rules(mlx5_eswitch_get_slow_fdb(esw), spec, &flow_act, &dest, 1); if (IS_ERR(flow)) { err = PTR_ERR(flow); @@ -1263,7 +1263,7 @@ static int esw_add_fdb_peer_miss_rules(struct mlx5_eswitch *esw, if (mlx5_ecpf_vport_exists(esw->dev)) { vport = mlx5_eswitch_get_vport(esw, MLX5_VPORT_ECPF); MLX5_SET(fte_match_set_misc, misc, source_port, MLX5_VPORT_ECPF); - flow = mlx5_add_flow_rules(esw->fdb_table.offloads.slow_fdb, + flow = mlx5_add_flow_rules(mlx5_eswitch_get_slow_fdb(esw), spec, &flow_act, &dest, 1); if (IS_ERR(flow)) { err = PTR_ERR(flow); @@ -1277,7 +1277,7 @@ static int esw_add_fdb_peer_miss_rules(struct mlx5_eswitch *esw, peer_dev->priv.eswitch, spec, vport->vport); - flow = mlx5_add_flow_rules(esw->fdb_table.offloads.slow_fdb, + flow = mlx5_add_flow_rules(mlx5_eswitch_get_slow_fdb(esw), spec, &flow_act, &dest, 1); if (IS_ERR(flow)) { err = PTR_ERR(flow); @@ -1366,7 +1366,7 @@ static int esw_add_fdb_miss_rule(struct mlx5_eswitch *esw) dest.vport.num = esw->manager_vport; flow_act.action = MLX5_FLOW_CONTEXT_ACTION_FWD_DEST; - flow_rule = mlx5_add_flow_rules(esw->fdb_table.offloads.slow_fdb, + flow_rule = mlx5_add_flow_rules(mlx5_eswitch_get_slow_fdb(esw), spec, &flow_act, &dest, 1); if (IS_ERR(flow_rule)) { err = PTR_ERR(flow_rule); @@ -1381,7 +1381,7 @@ static int esw_add_fdb_miss_rule(struct mlx5_eswitch *esw) dmac_v = MLX5_ADDR_OF(fte_match_param, headers_v, outer_headers.dmac_47_16); dmac_v[0] = 0x01; - flow_rule = mlx5_add_flow_rules(esw->fdb_table.offloads.slow_fdb, + flow_rule = mlx5_add_flow_rules(mlx5_eswitch_get_slow_fdb(esw), spec, &flow_act, &dest, 1); if (IS_ERR(flow_rule)) { err = PTR_ERR(flow_rule); @@ -1930,7 +1930,7 @@ send_vport_err: fdb_chains_err: mlx5_destroy_flow_table(esw->fdb_table.offloads.tc_miss_table); tc_miss_table_err: - mlx5_destroy_flow_table(esw->fdb_table.offloads.slow_fdb); + mlx5_destroy_flow_table(mlx5_eswitch_get_slow_fdb(esw)); slow_fdb_err: /* Holds true only as long as DMFS is the default */ mlx5_flow_namespace_set_mode(root_ns, MLX5_FLOW_STEERING_MODE_DMFS); @@ -1941,7 +1941,7 @@ ns_err: static void esw_destroy_offloads_fdb_tables(struct mlx5_eswitch *esw) { - if (!esw->fdb_table.offloads.slow_fdb) + if (!mlx5_eswitch_get_slow_fdb(esw)) return; esw_debug(esw->dev, "Destroy offloads FDB Tables\n"); @@ -1957,7 +1957,7 @@ static void esw_destroy_offloads_fdb_tables(struct mlx5_eswitch *esw) esw_chains_destroy(esw, esw_chains(esw)); mlx5_destroy_flow_table(esw->fdb_table.offloads.tc_miss_table); - mlx5_destroy_flow_table(esw->fdb_table.offloads.slow_fdb); + mlx5_destroy_flow_table(mlx5_eswitch_get_slow_fdb(esw)); /* Holds true only as long as DMFS is the default */ mlx5_flow_namespace_set_mode(esw->fdb_table.offloads.ns, MLX5_FLOW_STEERING_MODE_DMFS); From 953d771587e232e537665d34086a94ed29b89e5f Mon Sep 17 00:00:00 2001 From: Leon Romanovsky Date: Mon, 22 Aug 2022 13:38:55 +0300 Subject: [PATCH 1328/2002] net/mlx5e: Support devlink reload of IPsec core Change IPsec initialization flow to allow future creation of hardware resources that should be released and allocated during devlink reload operation. As part of that change, update function signature to be void as no callers are actually interested in it. Reviewed-by: Saeed Mahameed Signed-off-by: Leon Romanovsky Signed-off-by: Saeed Mahameed --- .../mellanox/mlx5/core/en_accel/ipsec.c | 17 ++++++++--------- .../mellanox/mlx5/core/en_accel/ipsec.h | 5 ++--- .../net/ethernet/mellanox/mlx5/core/en_main.c | 7 ++----- .../net/ethernet/mellanox/mlx5/core/en_rep.c | 10 ++++------ 4 files changed, 16 insertions(+), 23 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.c b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.c index a715601865d3..1b03ab03fc5a 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.c @@ -345,29 +345,27 @@ static void mlx5e_xfrm_free_state(struct xfrm_state *x) kfree(sa_entry); } -int mlx5e_ipsec_init(struct mlx5e_priv *priv) +void mlx5e_ipsec_init(struct mlx5e_priv *priv) { struct mlx5e_ipsec *ipsec; - int ret; + int ret = -ENOMEM; if (!mlx5_ipsec_device_caps(priv->mdev)) { netdev_dbg(priv->netdev, "Not an IPSec offload device\n"); - return 0; + return; } ipsec = kzalloc(sizeof(*ipsec), GFP_KERNEL); if (!ipsec) - return -ENOMEM; + return; hash_init(ipsec->sadb_rx); spin_lock_init(&ipsec->sadb_rx_lock); ipsec->mdev = priv->mdev; ipsec->wq = alloc_ordered_workqueue("mlx5e_ipsec: %s", 0, priv->netdev->name); - if (!ipsec->wq) { - ret = -ENOMEM; + if (!ipsec->wq) goto err_wq; - } ret = mlx5e_accel_ipsec_fs_init(ipsec); if (ret) @@ -375,13 +373,14 @@ int mlx5e_ipsec_init(struct mlx5e_priv *priv) priv->ipsec = ipsec; netdev_dbg(priv->netdev, "IPSec attached to netdevice\n"); - return 0; + return; err_fs_init: destroy_workqueue(ipsec->wq); err_wq: kfree(ipsec); - return (ret != -EOPNOTSUPP) ? ret : 0; + mlx5_core_err(priv->mdev, "IPSec initialization failed, %d\n", ret); + return; } void mlx5e_ipsec_cleanup(struct mlx5e_priv *priv) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.h b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.h index 16bcceec16c4..4c47347d0ee2 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.h @@ -146,7 +146,7 @@ struct mlx5e_ipsec_sa_entry { struct mlx5e_ipsec_modify_state_work modify_work; }; -int mlx5e_ipsec_init(struct mlx5e_priv *priv); +void mlx5e_ipsec_init(struct mlx5e_priv *priv); void mlx5e_ipsec_cleanup(struct mlx5e_priv *priv); void mlx5e_ipsec_build_netdev(struct mlx5e_priv *priv); @@ -174,9 +174,8 @@ mlx5e_ipsec_sa2dev(struct mlx5e_ipsec_sa_entry *sa_entry) return sa_entry->ipsec->mdev; } #else -static inline int mlx5e_ipsec_init(struct mlx5e_priv *priv) +static inline void mlx5e_ipsec_init(struct mlx5e_priv *priv) { - return 0; } static inline void mlx5e_ipsec_cleanup(struct mlx5e_priv *priv) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c index 199387c6bf16..8d36e2de53a9 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c @@ -5238,10 +5238,6 @@ static int mlx5e_nic_init(struct mlx5_core_dev *mdev, } priv->fs = fs; - err = mlx5e_ipsec_init(priv); - if (err) - mlx5_core_err(mdev, "IPSec initialization failed, %d\n", err); - err = mlx5e_ktls_init(priv); if (err) mlx5_core_err(mdev, "TLS initialization failed, %d\n", err); @@ -5254,7 +5250,6 @@ static void mlx5e_nic_cleanup(struct mlx5e_priv *priv) { mlx5e_health_destroy_reporters(priv); mlx5e_ktls_cleanup(priv); - mlx5e_ipsec_cleanup(priv); mlx5e_fs_cleanup(priv->fs); } @@ -5383,6 +5378,7 @@ static void mlx5e_nic_enable(struct mlx5e_priv *priv) int err; mlx5e_fs_init_l2_addr(priv->fs, netdev); + mlx5e_ipsec_init(priv); err = mlx5e_macsec_init(priv); if (err) @@ -5446,6 +5442,7 @@ static void mlx5e_nic_disable(struct mlx5e_priv *priv) mlx5_lag_remove_netdev(mdev, priv->netdev); mlx5_vxlan_reset_to_default(mdev->vxlan); mlx5e_macsec_cleanup(priv); + mlx5e_ipsec_cleanup(priv); } int mlx5e_update_nic_rx(struct mlx5e_priv *priv) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c b/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c index 1b53e8852c86..623886462c10 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c @@ -751,7 +751,6 @@ static int mlx5e_init_ul_rep(struct mlx5_core_dev *mdev, struct net_device *netdev) { struct mlx5e_priv *priv = netdev_priv(netdev); - int err; priv->fs = mlx5e_fs_init(priv->profile, mdev, !test_bit(MLX5E_STATE_DESTROYING, &priv->state)); @@ -760,10 +759,6 @@ static int mlx5e_init_ul_rep(struct mlx5_core_dev *mdev, return -ENOMEM; } - err = mlx5e_ipsec_init(priv); - if (err) - mlx5_core_err(mdev, "Uplink rep IPsec initialization failed, %d\n", err); - mlx5e_vxlan_set_netdev_info(priv); mlx5e_build_rep_params(netdev); mlx5e_timestamp_init(priv); @@ -773,7 +768,6 @@ static int mlx5e_init_ul_rep(struct mlx5_core_dev *mdev, static void mlx5e_cleanup_rep(struct mlx5e_priv *priv) { mlx5e_fs_cleanup(priv->fs); - mlx5e_ipsec_cleanup(priv); } static int mlx5e_create_rep_ttc_table(struct mlx5e_priv *priv) @@ -1112,6 +1106,8 @@ static void mlx5e_uplink_rep_enable(struct mlx5e_priv *priv) struct mlx5_core_dev *mdev = priv->mdev; u16 max_mtu; + mlx5e_ipsec_init(priv); + netdev->min_mtu = ETH_MIN_MTU; mlx5_query_port_max_mtu(priv->mdev, &max_mtu, 1); netdev->max_mtu = MLX5E_HW2SW_MTU(&priv->channels.params, max_mtu); @@ -1158,6 +1154,8 @@ static void mlx5e_uplink_rep_disable(struct mlx5e_priv *priv) mlx5e_rep_tc_disable(priv); mlx5_lag_remove_netdev(mdev, priv->netdev); mlx5_vxlan_reset_to_default(mdev->vxlan); + + mlx5e_ipsec_cleanup(priv); } static MLX5E_DEFINE_STATS_GRP(sw_rep, 0); From 91a7de85600d5dfa272cea3cef83052e067dc0ab Mon Sep 17 00:00:00 2001 From: Willem de Bruijn Date: Mon, 28 Nov 2022 09:02:10 -0500 Subject: [PATCH 1329/2002] selftests/net: add csum offload test Test NIC hardware checksum offload: - Rx + Tx - IPv4 + IPv6 - TCP + UDP Optional features: - zero checksum 0xFFFF - checksum disable 0x0000 - transport encap headers - randomization See file header for detailed comments. Expected results differ depending on NIC features: - CHECKSUM_UNNECESSARY vs CHECKSUM_COMPLETE - NETIF_F_HW_CSUM (csum_start/csum_off) vs NETIF_F_IP(V6)_CSUM Signed-off-by: Willem de Bruijn Link: https://lore.kernel.org/r/20221128140210.553391-1-willemdebruijn.kernel@gmail.com Signed-off-by: Jakub Kicinski --- tools/testing/selftests/net/.gitignore | 1 + tools/testing/selftests/net/Makefile | 1 + tools/testing/selftests/net/csum.c | 986 +++++++++++++++++++++++++ 3 files changed, 988 insertions(+) create mode 100644 tools/testing/selftests/net/csum.c diff --git a/tools/testing/selftests/net/.gitignore b/tools/testing/selftests/net/.gitignore index ff8807cc9c2e..ee38ca888244 100644 --- a/tools/testing/selftests/net/.gitignore +++ b/tools/testing/selftests/net/.gitignore @@ -1,5 +1,6 @@ # SPDX-License-Identifier: GPL-2.0-only bind_bhash +csum cmsg_sender fin_ack_lat gro diff --git a/tools/testing/selftests/net/Makefile b/tools/testing/selftests/net/Makefile index 880e6ded6ed5..3007e98a6d64 100644 --- a/tools/testing/selftests/net/Makefile +++ b/tools/testing/selftests/net/Makefile @@ -74,6 +74,7 @@ TEST_PROGS += test_ingress_egress_chaining.sh TEST_GEN_PROGS += so_incoming_cpu TEST_PROGS += sctp_vrf.sh TEST_GEN_FILES += sctp_hello +TEST_GEN_FILES += csum TEST_FILES := settings diff --git a/tools/testing/selftests/net/csum.c b/tools/testing/selftests/net/csum.c new file mode 100644 index 000000000000..82a1c1839da6 --- /dev/null +++ b/tools/testing/selftests/net/csum.c @@ -0,0 +1,986 @@ +// SPDX-License-Identifier: GPL-2.0 + +/* Test hardware checksum offload: Rx + Tx, IPv4 + IPv6, TCP + UDP. + * + * The test runs on two machines to exercise the NIC. For this reason it + * is not integrated in kselftests. + * + * CMD=$((./csum -[46] -[tu] -S $SADDR -D $DADDR -[RT] -r 1 $EXTRA_ARGS)) + * + * Rx: + * + * The sender sends packets with a known checksum field using PF_INET(6) + * SOCK_RAW sockets. + * + * good packet: $CMD [-t] + * bad packet: $CMD [-t] -E + * + * The receiver reads UDP packets with a UDP socket. This is not an + * option for TCP packets ('-t'). Optionally insert an iptables filter + * to avoid these entering the real protocol stack. + * + * The receiver also reads all packets with a PF_PACKET socket, to + * observe whether both good and bad packets arrive on the host. And to + * read the optional TP_STATUS_CSUM_VALID bit. This requires setting + * option PACKET_AUXDATA, and works only for CHECKSUM_UNNECESSARY. + * + * Tx: + * + * The sender needs to build CHECKSUM_PARTIAL packets to exercise tx + * checksum offload. + * + * The sender can sends packets with a UDP socket. + * + * Optionally crafts a packet that sums up to zero to verify that the + * device writes negative zero 0xFFFF in this case to distinguish from + * 0x0000 (checksum disabled), as required by RFC 768. Hit this case + * by choosing a specific source port. + * + * good packet: $CMD -U + * zero csum: $CMD -U -Z + * + * The sender can also build packets with PF_PACKET with PACKET_VNET_HDR, + * to cover more protocols. PF_PACKET requires passing src and dst mac + * addresses. + * + * good packet: $CMD -s $smac -d $dmac -p [-t] + * + * Argument '-z' sends UDP packets with a 0x000 checksum disabled field, + * to verify that the NIC passes these packets unmodified. + * + * Argument '-e' adds a transport mode encapsulation header between + * network and transport header. This will fail for devices that parse + * headers. Should work on devices that implement protocol agnostic tx + * checksum offload (NETIF_F_HW_CSUM). + * + * Argument '-r $SEED' optionally randomizes header, payload and length + * to increase coverage between packets sent. SEED 1 further chooses a + * different seed for each run (and logs this for reproducibility). It + * is advised to enable this for extra coverage in continuous testing. + */ + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static bool cfg_bad_csum; +static int cfg_family = PF_INET6; +static int cfg_num_pkt = 4; +static bool cfg_do_rx = true; +static bool cfg_do_tx = true; +static bool cfg_encap; +static char *cfg_ifname = "eth0"; +static char *cfg_mac_dst; +static char *cfg_mac_src; +static int cfg_proto = IPPROTO_UDP; +static int cfg_payload_char = 'a'; +static int cfg_payload_len = 100; +static uint16_t cfg_port_dst = 34000; +static uint16_t cfg_port_src = 33000; +static uint16_t cfg_port_src_encap = 33001; +static unsigned int cfg_random_seed; +static int cfg_rcvbuf = 1 << 22; /* be able to queue large cfg_num_pkt */ +static bool cfg_send_pfpacket; +static bool cfg_send_udp; +static int cfg_timeout_ms = 2000; +static bool cfg_zero_disable; /* skip checksum: set to zero (udp only) */ +static bool cfg_zero_sum; /* create packet that adds up to zero */ + +static struct sockaddr_in cfg_daddr4 = {.sin_family = AF_INET}; +static struct sockaddr_in cfg_saddr4 = {.sin_family = AF_INET}; +static struct sockaddr_in6 cfg_daddr6 = {.sin6_family = AF_INET6}; +static struct sockaddr_in6 cfg_saddr6 = {.sin6_family = AF_INET6}; + +#define ENC_HEADER_LEN (sizeof(struct udphdr) + sizeof(struct udp_encap_hdr)) +#define MAX_HEADER_LEN (sizeof(struct ipv6hdr) + ENC_HEADER_LEN + sizeof(struct tcphdr)) +#define MAX_PAYLOAD_LEN 1024 + +/* Trivial demo encap. Stand-in for transport layer protocols like ESP or PSP */ +struct udp_encap_hdr { + uint8_t nexthdr; + uint8_t padding[3]; +}; + +/* Ipaddrs, for pseudo csum. Global var is ugly, pass through funcs was worse */ +static void *iph_addr_p; + +static unsigned long gettimeofday_ms(void) +{ + struct timeval tv; + + gettimeofday(&tv, NULL); + return (tv.tv_sec * 1000UL) + (tv.tv_usec / 1000UL); +} + +static uint32_t checksum_nofold(char *data, size_t len, uint32_t sum) +{ + uint16_t *words = (uint16_t *)data; + int i; + + for (i = 0; i < len / 2; i++) + sum += words[i]; + + if (len & 1) + sum += ((unsigned char *)data)[len - 1]; + + return sum; +} + +static uint16_t checksum_fold(void *data, size_t len, uint32_t sum) +{ + sum = checksum_nofold(data, len, sum); + + while (sum > 0xFFFF) + sum = (sum & 0xFFFF) + (sum >> 16); + + return ~sum; +} + +static uint16_t checksum(void *th, uint16_t proto, size_t len) +{ + uint32_t sum; + int alen; + + alen = cfg_family == PF_INET6 ? 32 : 8; + + sum = checksum_nofold(iph_addr_p, alen, 0); + sum += htons(proto); + sum += htons(len); + + /* With CHECKSUM_PARTIAL kernel expects non-inverted pseudo csum */ + if (cfg_do_tx && cfg_send_pfpacket) + return ~checksum_fold(NULL, 0, sum); + else + return checksum_fold(th, len, sum); +} + +static void *build_packet_ipv4(void *_iph, uint8_t proto, unsigned int len) +{ + struct iphdr *iph = _iph; + + memset(iph, 0, sizeof(*iph)); + + iph->version = 4; + iph->ihl = 5; + iph->ttl = 8; + iph->protocol = proto; + iph->saddr = cfg_saddr4.sin_addr.s_addr; + iph->daddr = cfg_daddr4.sin_addr.s_addr; + iph->tot_len = htons(sizeof(*iph) + len); + iph->check = checksum_fold(iph, sizeof(*iph), 0); + + iph_addr_p = &iph->saddr; + + return iph + 1; +} + +static void *build_packet_ipv6(void *_ip6h, uint8_t proto, unsigned int len) +{ + struct ipv6hdr *ip6h = _ip6h; + + memset(ip6h, 0, sizeof(*ip6h)); + + ip6h->version = 6; + ip6h->payload_len = htons(len); + ip6h->nexthdr = proto; + ip6h->hop_limit = 64; + ip6h->saddr = cfg_saddr6.sin6_addr; + ip6h->daddr = cfg_daddr6.sin6_addr; + + iph_addr_p = &ip6h->saddr; + + return ip6h + 1; +} + +static void *build_packet_udp(void *_uh) +{ + struct udphdr *uh = _uh; + + uh->source = htons(cfg_port_src); + uh->dest = htons(cfg_port_dst); + uh->len = htons(sizeof(*uh) + cfg_payload_len); + uh->check = 0; + + /* choose source port so that uh->check adds up to zero */ + if (cfg_zero_sum) { + uh->source = 0; + uh->source = checksum(uh, IPPROTO_UDP, sizeof(*uh) + cfg_payload_len); + + fprintf(stderr, "tx: changing sport: %hu -> %hu\n", + cfg_port_src, ntohs(uh->source)); + cfg_port_src = ntohs(uh->source); + } + + if (cfg_zero_disable) + uh->check = 0; + else + uh->check = checksum(uh, IPPROTO_UDP, sizeof(*uh) + cfg_payload_len); + + if (cfg_bad_csum) + uh->check = ~uh->check; + + fprintf(stderr, "tx: sending checksum: 0x%x\n", uh->check); + return uh + 1; +} + +static void *build_packet_tcp(void *_th) +{ + struct tcphdr *th = _th; + + th->source = htons(cfg_port_src); + th->dest = htons(cfg_port_dst); + th->doff = 5; + th->check = 0; + + th->check = checksum(th, IPPROTO_TCP, sizeof(*th) + cfg_payload_len); + + if (cfg_bad_csum) + th->check = ~th->check; + + fprintf(stderr, "tx: sending checksum: 0x%x\n", th->check); + return th + 1; +} + +static char *build_packet_udp_encap(void *_uh) +{ + struct udphdr *uh = _uh; + struct udp_encap_hdr *eh = _uh + sizeof(*uh); + + /* outer dst == inner dst, to simplify BPF filter + * outer src != inner src, to demultiplex on recv + */ + uh->dest = htons(cfg_port_dst); + uh->source = htons(cfg_port_src_encap); + uh->check = 0; + uh->len = htons(sizeof(*uh) + + sizeof(*eh) + + sizeof(struct tcphdr) + + cfg_payload_len); + + eh->nexthdr = IPPROTO_TCP; + + return build_packet_tcp(eh + 1); +} + +static char *build_packet(char *buf, int max_len, int *len) +{ + uint8_t proto; + char *off; + int tlen; + + if (cfg_random_seed) { + int *buf32 = (void *)buf; + int i; + + for (i = 0; i < (max_len / sizeof(int)); i++) + buf32[i] = rand(); + } else { + memset(buf, cfg_payload_char, max_len); + } + + if (cfg_proto == IPPROTO_UDP) + tlen = sizeof(struct udphdr) + cfg_payload_len; + else + tlen = sizeof(struct tcphdr) + cfg_payload_len; + + if (cfg_encap) { + proto = IPPROTO_UDP; + tlen += ENC_HEADER_LEN; + } else { + proto = cfg_proto; + } + + if (cfg_family == PF_INET) + off = build_packet_ipv4(buf, proto, tlen); + else + off = build_packet_ipv6(buf, proto, tlen); + + if (cfg_encap) + off = build_packet_udp_encap(off); + else if (cfg_proto == IPPROTO_UDP) + off = build_packet_udp(off); + else + off = build_packet_tcp(off); + + /* only pass the payload, but still compute headers for cfg_zero_sum */ + if (cfg_send_udp) { + *len = cfg_payload_len; + return off; + } + + *len = off - buf + cfg_payload_len; + return buf; +} + +static int open_inet(int ipproto, int protocol) +{ + int fd; + + fd = socket(cfg_family, ipproto, protocol); + if (fd == -1) + error(1, errno, "socket inet"); + + if (cfg_family == PF_INET6) { + /* may have been updated by cfg_zero_sum */ + cfg_saddr6.sin6_port = htons(cfg_port_src); + + if (bind(fd, (void *)&cfg_saddr6, sizeof(cfg_saddr6))) + error(1, errno, "bind dgram 6"); + if (connect(fd, (void *)&cfg_daddr6, sizeof(cfg_daddr6))) + error(1, errno, "connect dgram 6"); + } else { + /* may have been updated by cfg_zero_sum */ + cfg_saddr4.sin_port = htons(cfg_port_src); + + if (bind(fd, (void *)&cfg_saddr4, sizeof(cfg_saddr4))) + error(1, errno, "bind dgram 4"); + if (connect(fd, (void *)&cfg_daddr4, sizeof(cfg_daddr4))) + error(1, errno, "connect dgram 4"); + } + + return fd; +} + +static int open_packet(void) +{ + int fd, one = 1; + + fd = socket(PF_PACKET, SOCK_RAW, 0); + if (fd == -1) + error(1, errno, "socket packet"); + + if (setsockopt(fd, SOL_PACKET, PACKET_VNET_HDR, &one, sizeof(one))) + error(1, errno, "setsockopt packet_vnet_ndr"); + + return fd; +} + +static void send_inet(int fd, const char *buf, int len) +{ + int ret; + + ret = write(fd, buf, len); + if (ret == -1) + error(1, errno, "write"); + if (ret != len) + error(1, 0, "write: %d", ret); +} + +static void eth_str_to_addr(const char *str, unsigned char *eth) +{ + if (sscanf(str, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx", + ð[0], ð[1], ð[2], ð[3], ð[4], ð[5]) != 6) + error(1, 0, "cannot parse mac addr %s", str); +} + +static void send_packet(int fd, const char *buf, int len) +{ + struct virtio_net_hdr vh = {0}; + struct sockaddr_ll addr = {0}; + struct msghdr msg = {0}; + struct ethhdr eth; + struct iovec iov[3]; + int ret; + + addr.sll_family = AF_PACKET; + addr.sll_halen = ETH_ALEN; + addr.sll_ifindex = if_nametoindex(cfg_ifname); + if (!addr.sll_ifindex) + error(1, errno, "if_nametoindex %s", cfg_ifname); + + vh.flags = VIRTIO_NET_HDR_F_NEEDS_CSUM; + if (cfg_family == PF_INET6) { + vh.csum_start = sizeof(struct ethhdr) + sizeof(struct ipv6hdr); + addr.sll_protocol = htons(ETH_P_IPV6); + } else { + vh.csum_start = sizeof(struct ethhdr) + sizeof(struct iphdr); + addr.sll_protocol = htons(ETH_P_IP); + } + + if (cfg_encap) + vh.csum_start += ENC_HEADER_LEN; + + if (cfg_proto == IPPROTO_TCP) { + vh.csum_offset = __builtin_offsetof(struct tcphdr, check); + vh.hdr_len = vh.csum_start + sizeof(struct tcphdr); + } else { + vh.csum_offset = __builtin_offsetof(struct udphdr, check); + vh.hdr_len = vh.csum_start + sizeof(struct udphdr); + } + + eth_str_to_addr(cfg_mac_src, eth.h_source); + eth_str_to_addr(cfg_mac_dst, eth.h_dest); + eth.h_proto = addr.sll_protocol; + + iov[0].iov_base = &vh; + iov[0].iov_len = sizeof(vh); + + iov[1].iov_base = ð + iov[1].iov_len = sizeof(eth); + + iov[2].iov_base = (void *)buf; + iov[2].iov_len = len; + + msg.msg_iov = iov; + msg.msg_iovlen = sizeof(iov) / sizeof(iov[0]); + + msg.msg_name = &addr; + msg.msg_namelen = sizeof(addr); + + ret = sendmsg(fd, &msg, 0); + if (ret == -1) + error(1, errno, "sendmsg packet"); + if (ret != sizeof(vh) + sizeof(eth) + len) + error(1, errno, "sendmsg packet: %u", ret); +} + +static int recv_prepare_udp(void) +{ + int fd; + + fd = socket(cfg_family, SOCK_DGRAM, 0); + if (fd == -1) + error(1, errno, "socket r"); + + if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, + &cfg_rcvbuf, sizeof(cfg_rcvbuf))) + error(1, errno, "setsockopt SO_RCVBUF r"); + + if (cfg_family == PF_INET6) { + if (bind(fd, (void *)&cfg_daddr6, sizeof(cfg_daddr6))) + error(1, errno, "bind r"); + } else { + if (bind(fd, (void *)&cfg_daddr4, sizeof(cfg_daddr4))) + error(1, errno, "bind r"); + } + + return fd; +} + +/* Filter out all traffic that is not cfg_proto with our destination port. + * + * Otherwise background noise may cause PF_PACKET receive queue overflow, + * dropping the expected packets and failing the test. + */ +static void __recv_prepare_packet_filter(int fd, int off_nexthdr, int off_dport) +{ + struct sock_filter filter[] = { + BPF_STMT(BPF_LD + BPF_B + BPF_ABS, SKF_AD_OFF + SKF_AD_PKTTYPE), + BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, PACKET_HOST, 0, 4), + BPF_STMT(BPF_LD + BPF_B + BPF_ABS, off_nexthdr), + BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, cfg_encap ? IPPROTO_UDP : cfg_proto, 0, 2), + BPF_STMT(BPF_LD + BPF_H + BPF_ABS, off_dport), + BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, cfg_port_dst, 1, 0), + BPF_STMT(BPF_RET + BPF_K, 0), + BPF_STMT(BPF_RET + BPF_K, 0xFFFF), + }; + struct sock_fprog prog = {}; + + prog.filter = filter; + prog.len = sizeof(filter) / sizeof(struct sock_filter); + if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &prog, sizeof(prog))) + error(1, errno, "setsockopt filter"); +} + +static void recv_prepare_packet_filter(int fd) +{ + const int off_dport = offsetof(struct tcphdr, dest); /* same for udp */ + + if (cfg_family == AF_INET) + __recv_prepare_packet_filter(fd, offsetof(struct iphdr, protocol), + sizeof(struct iphdr) + off_dport); + else + __recv_prepare_packet_filter(fd, offsetof(struct ipv6hdr, nexthdr), + sizeof(struct ipv6hdr) + off_dport); +} + +static void recv_prepare_packet_bind(int fd) +{ + struct sockaddr_ll laddr = {0}; + + laddr.sll_family = AF_PACKET; + + if (cfg_family == PF_INET) + laddr.sll_protocol = htons(ETH_P_IP); + else + laddr.sll_protocol = htons(ETH_P_IPV6); + + laddr.sll_ifindex = if_nametoindex(cfg_ifname); + if (!laddr.sll_ifindex) + error(1, 0, "if_nametoindex %s", cfg_ifname); + + if (bind(fd, (void *)&laddr, sizeof(laddr))) + error(1, errno, "bind pf_packet"); +} + +static int recv_prepare_packet(void) +{ + int fd, one = 1; + + fd = socket(PF_PACKET, SOCK_DGRAM, 0); + if (fd == -1) + error(1, errno, "socket p"); + + if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, + &cfg_rcvbuf, sizeof(cfg_rcvbuf))) + error(1, errno, "setsockopt SO_RCVBUF p"); + + /* enable auxdata to recv checksum status (valid vs unknown) */ + if (setsockopt(fd, SOL_PACKET, PACKET_AUXDATA, &one, sizeof(one))) + error(1, errno, "setsockopt auxdata"); + + /* install filter to restrict packet flow to match */ + recv_prepare_packet_filter(fd); + + /* bind to address family to start packet flow */ + recv_prepare_packet_bind(fd); + + return fd; +} + +static int recv_udp(int fd) +{ + static char buf[MAX_PAYLOAD_LEN]; + int ret, count = 0; + + while (1) { + ret = recv(fd, buf, sizeof(buf), MSG_DONTWAIT); + if (ret == -1 && errno == EAGAIN) + break; + if (ret == -1) + error(1, errno, "recv r"); + + fprintf(stderr, "rx: udp: len=%u\n", ret); + count++; + } + + return count; +} + +static int recv_verify_csum(void *th, int len, uint16_t sport, uint16_t csum_field) +{ + uint16_t csum; + + csum = checksum(th, cfg_proto, len); + + fprintf(stderr, "rx: pkt: sport=%hu len=%u csum=0x%hx verify=0x%hx\n", + sport, len, csum_field, csum); + + /* csum must be zero unless cfg_bad_csum indicates bad csum */ + if (csum && !cfg_bad_csum) { + fprintf(stderr, "pkt: bad csum\n"); + return 1; + } else if (cfg_bad_csum && !csum) { + fprintf(stderr, "pkt: good csum, while bad expected\n"); + return 1; + } + + if (cfg_zero_sum && csum_field != 0xFFFF) { + fprintf(stderr, "pkt: zero csum: field should be 0xFFFF, is 0x%hx\n", csum_field); + return 1; + } + + return 0; +} + +static int recv_verify_packet_tcp(void *th, int len) +{ + struct tcphdr *tcph = th; + + if (len < sizeof(*tcph) || tcph->dest != htons(cfg_port_dst)) + return -1; + + return recv_verify_csum(th, len, ntohs(tcph->source), tcph->check); +} + +static int recv_verify_packet_udp_encap(void *th, int len) +{ + struct udp_encap_hdr *eh = th; + + if (len < sizeof(*eh) || eh->nexthdr != IPPROTO_TCP) + return -1; + + return recv_verify_packet_tcp(eh + 1, len - sizeof(*eh)); +} + +static int recv_verify_packet_udp(void *th, int len) +{ + struct udphdr *udph = th; + + if (len < sizeof(*udph)) + return -1; + + if (udph->dest != htons(cfg_port_dst)) + return -1; + + if (udph->source == htons(cfg_port_src_encap)) + return recv_verify_packet_udp_encap(udph + 1, + len - sizeof(*udph)); + + return recv_verify_csum(th, len, ntohs(udph->source), udph->check); +} + +static int recv_verify_packet_ipv4(void *nh, int len) +{ + struct iphdr *iph = nh; + uint16_t proto = cfg_encap ? IPPROTO_UDP : cfg_proto; + + if (len < sizeof(*iph) || iph->protocol != proto) + return -1; + + iph_addr_p = &iph->saddr; + if (proto == IPPROTO_TCP) + return recv_verify_packet_tcp(iph + 1, len - sizeof(*iph)); + else + return recv_verify_packet_udp(iph + 1, len - sizeof(*iph)); +} + +static int recv_verify_packet_ipv6(void *nh, int len) +{ + struct ipv6hdr *ip6h = nh; + uint16_t proto = cfg_encap ? IPPROTO_UDP : cfg_proto; + + if (len < sizeof(*ip6h) || ip6h->nexthdr != proto) + return -1; + + iph_addr_p = &ip6h->saddr; + + if (proto == IPPROTO_TCP) + return recv_verify_packet_tcp(ip6h + 1, len - sizeof(*ip6h)); + else + return recv_verify_packet_udp(ip6h + 1, len - sizeof(*ip6h)); +} + +/* return whether auxdata includes TP_STATUS_CSUM_VALID */ +static bool recv_verify_packet_csum(struct msghdr *msg) +{ + struct tpacket_auxdata *aux = NULL; + struct cmsghdr *cm; + + if (msg->msg_flags & MSG_CTRUNC) + error(1, 0, "cmsg: truncated"); + + for (cm = CMSG_FIRSTHDR(msg); cm; cm = CMSG_NXTHDR(msg, cm)) { + if (cm->cmsg_level != SOL_PACKET || + cm->cmsg_type != PACKET_AUXDATA) + error(1, 0, "cmsg: level=%d type=%d\n", + cm->cmsg_level, cm->cmsg_type); + + if (cm->cmsg_len != CMSG_LEN(sizeof(struct tpacket_auxdata))) + error(1, 0, "cmsg: len=%lu expected=%lu", + cm->cmsg_len, CMSG_LEN(sizeof(struct tpacket_auxdata))); + + aux = (void *)CMSG_DATA(cm); + } + + if (!aux) + error(1, 0, "cmsg: no auxdata"); + + return aux->tp_status & TP_STATUS_CSUM_VALID; +} + +static int recv_packet(int fd) +{ + static char _buf[MAX_HEADER_LEN + MAX_PAYLOAD_LEN]; + unsigned long total = 0, bad_csums = 0, bad_validations = 0; + char ctrl[CMSG_SPACE(sizeof(struct tpacket_auxdata))]; + struct pkt *buf = (void *)_buf; + struct msghdr msg = {0}; + struct iovec iov; + int len, ret; + + iov.iov_base = _buf; + iov.iov_len = sizeof(_buf); + + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + + msg.msg_control = ctrl; + msg.msg_controllen = sizeof(ctrl); + + while (1) { + msg.msg_flags = 0; + + len = recvmsg(fd, &msg, MSG_DONTWAIT); + if (len == -1 && errno == EAGAIN) + break; + if (len == -1) + error(1, errno, "recv p"); + + if (cfg_family == PF_INET6) + ret = recv_verify_packet_ipv6(buf, len); + else + ret = recv_verify_packet_ipv4(buf, len); + + if (ret == -1 /* skip: non-matching */) + continue; + + total++; + if (ret == 1) + bad_csums++; + + /* Fail if kernel returns valid for known bad csum. + * Do not fail if kernel does not validate a good csum: + * Absence of validation does not imply invalid. + */ + if (recv_verify_packet_csum(&msg) && cfg_bad_csum) { + fprintf(stderr, "cmsg: expected bad csum, pf_packet returns valid\n"); + bad_validations++; + } + } + + if (bad_csums || bad_validations) + error(1, 0, "rx: errors at pf_packet: total=%lu bad_csums=%lu bad_valids=%lu\n", + total, bad_csums, bad_validations); + + return total; +} + +static void parse_args(int argc, char *const argv[]) +{ + const char *daddr = NULL, *saddr = NULL; + int c; + + while ((c = getopt(argc, argv, "46d:D:eEi:l:L:n:r:PRs:S:tTuUzZ")) != -1) { + switch (c) { + case '4': + cfg_family = PF_INET; + break; + case '6': + cfg_family = PF_INET6; + break; + case 'd': + cfg_mac_dst = optarg; + break; + case 'D': + daddr = optarg; + break; + case 'e': + cfg_encap = true; + break; + case 'E': + cfg_bad_csum = true; + break; + case 'i': + cfg_ifname = optarg; + break; + case 'l': + cfg_payload_len = strtol(optarg, NULL, 0); + break; + case 'L': + cfg_timeout_ms = strtol(optarg, NULL, 0) * 1000; + break; + case 'n': + cfg_num_pkt = strtol(optarg, NULL, 0); + break; + case 'r': + cfg_random_seed = strtol(optarg, NULL, 0); + break; + case 'P': + cfg_send_pfpacket = true; + break; + case 'R': + /* only Rx: used with two machine tests */ + cfg_do_tx = false; + break; + case 's': + cfg_mac_src = optarg; + break; + case 'S': + saddr = optarg; + break; + case 't': + cfg_proto = IPPROTO_TCP; + break; + case 'T': + /* only Tx: used with two machine tests */ + cfg_do_rx = false; + break; + case 'u': + cfg_proto = IPPROTO_UDP; + break; + case 'U': + /* send using real udp socket, + * to exercise tx checksum offload + */ + cfg_send_udp = true; + break; + case 'z': + cfg_zero_disable = true; + break; + case 'Z': + cfg_zero_sum = true; + break; + default: + error(1, 0, "unknown arg %c", c); + } + } + + if (!daddr || !saddr) + error(1, 0, "Must pass -D and -S "); + + if (cfg_do_tx && cfg_send_pfpacket && (!cfg_mac_src || !cfg_mac_dst)) + error(1, 0, "Transmit with pf_packet requires mac addresses"); + + if (cfg_payload_len > MAX_PAYLOAD_LEN) + error(1, 0, "Payload length exceeds max"); + + if (cfg_proto != IPPROTO_UDP && (cfg_zero_sum || cfg_zero_disable)) + error(1, 0, "Only UDP supports zero csum"); + + if (cfg_zero_sum && !cfg_send_udp) + error(1, 0, "Zero checksum conversion requires -U for tx csum offload"); + if (cfg_zero_sum && cfg_bad_csum) + error(1, 0, "Cannot combine zero checksum conversion and invalid checksum"); + if (cfg_zero_sum && cfg_random_seed) + error(1, 0, "Cannot combine zero checksum conversion with randomization"); + + if (cfg_family == PF_INET6) { + cfg_saddr6.sin6_port = htons(cfg_port_src); + cfg_daddr6.sin6_port = htons(cfg_port_dst); + + if (inet_pton(cfg_family, daddr, &cfg_daddr6.sin6_addr) != 1) + error(1, errno, "Cannot parse ipv6 -D"); + if (inet_pton(cfg_family, saddr, &cfg_saddr6.sin6_addr) != 1) + error(1, errno, "Cannot parse ipv6 -S"); + } else { + cfg_saddr4.sin_port = htons(cfg_port_src); + cfg_daddr4.sin_port = htons(cfg_port_dst); + + if (inet_pton(cfg_family, daddr, &cfg_daddr4.sin_addr) != 1) + error(1, errno, "Cannot parse ipv4 -D"); + if (inet_pton(cfg_family, saddr, &cfg_saddr4.sin_addr) != 1) + error(1, errno, "Cannot parse ipv4 -S"); + } + + if (cfg_do_tx && cfg_random_seed) { + /* special case: time-based seed */ + if (cfg_random_seed == 1) + cfg_random_seed = (unsigned int)gettimeofday_ms(); + srand(cfg_random_seed); + fprintf(stderr, "randomization seed: %u\n", cfg_random_seed); + } +} + +static void do_tx(void) +{ + static char _buf[MAX_HEADER_LEN + MAX_PAYLOAD_LEN]; + char *buf; + int fd, len, i; + + buf = build_packet(_buf, sizeof(_buf), &len); + + if (cfg_send_pfpacket) + fd = open_packet(); + else if (cfg_send_udp) + fd = open_inet(SOCK_DGRAM, 0); + else + fd = open_inet(SOCK_RAW, IPPROTO_RAW); + + for (i = 0; i < cfg_num_pkt; i++) { + if (cfg_send_pfpacket) + send_packet(fd, buf, len); + else + send_inet(fd, buf, len); + + /* randomize each packet individually to increase coverage */ + if (cfg_random_seed) { + cfg_payload_len = rand() % MAX_PAYLOAD_LEN; + buf = build_packet(_buf, sizeof(_buf), &len); + } + } + + if (close(fd)) + error(1, errno, "close tx"); +} + +static void do_rx(int fdp, int fdr) +{ + unsigned long count_udp = 0, count_pkt = 0; + long tleft, tstop; + struct pollfd pfd; + + tstop = gettimeofday_ms() + cfg_timeout_ms; + tleft = cfg_timeout_ms; + + do { + pfd.events = POLLIN; + pfd.fd = fdp; + if (poll(&pfd, 1, tleft) == -1) + error(1, errno, "poll"); + + if (pfd.revents & POLLIN) + count_pkt += recv_packet(fdp); + + if (cfg_proto == IPPROTO_UDP) + count_udp += recv_udp(fdr); + + tleft = tstop - gettimeofday_ms(); + } while (tleft > 0); + + if (close(fdr)) + error(1, errno, "close r"); + if (close(fdp)) + error(1, errno, "close p"); + + if (count_pkt < cfg_num_pkt) + error(1, 0, "rx: missing packets at pf_packet: %lu < %u", + count_pkt, cfg_num_pkt); + + if (cfg_proto == IPPROTO_UDP) { + if (cfg_bad_csum && count_udp) + error(1, 0, "rx: unexpected packets at udp"); + if (!cfg_bad_csum && !count_udp) + error(1, 0, "rx: missing packets at udp"); + } +} + +int main(int argc, char *const argv[]) +{ + int fdp = -1, fdr = -1; /* -1 to silence -Wmaybe-uninitialized */ + + parse_args(argc, argv); + + /* open receive sockets before transmitting */ + if (cfg_do_rx) { + fdp = recv_prepare_packet(); + fdr = recv_prepare_udp(); + } + + if (cfg_do_tx) + do_tx(); + + if (cfg_do_rx) + do_rx(fdp, fdr); + + fprintf(stderr, "OK\n"); + return 0; +} From bff3d0534804452e19c097ae6b4eb4b4d846d67f Mon Sep 17 00:00:00 2001 From: Sriram Yagnaraman Date: Fri, 4 Nov 2022 18:18:35 +0100 Subject: [PATCH 1330/2002] netfilter: conntrack: add sctp DATA_SENT state SCTP conntrack currently assumes that the SCTP endpoints will probe secondary paths using HEARTBEAT before sending traffic. But, according to RFC 9260, SCTP endpoints can send any traffic on any of the confirmed paths after SCTP association is up. SCTP endpoints that sends INIT will confirm all peer addresses that upper layer configures, and the SCTP endpoint that receives COOKIE_ECHO will only confirm the address it sent the INIT_ACK to. So, we can have a situation where the INIT sender can start to use secondary paths without the need to send HEARTBEAT. This patch allows DATA/SACK packets to create new connection tracking entry. A new state has been added to indicate that a DATA/SACK chunk has been seen in the original direction - SCTP_CONNTRACK_DATA_SENT. State transitions mostly follows the HEARTBEAT_SENT, except on receiving HEARTBEAT/HEARTBEAT_ACK/DATA/SACK in the reply direction. State transitions in original direction: - DATA_SENT behaves similar to HEARTBEAT_SENT for all chunks, except that it remains in DATA_SENT on receving HEARTBEAT, HEARTBEAT_ACK/DATA/SACK chunks State transitions in reply direction: - DATA_SENT behaves similar to HEARTBEAT_SENT for all chunks, except that it moves to HEARTBEAT_ACKED on receiving HEARTBEAT/HEARTBEAT_ACK/DATA/SACK chunks Note: This patch still doesn't solve the problem when the SCTP endpoint decides to use primary paths for association establishment but uses a secondary path for association shutdown. We still have to depend on timeout for connections to expire in such a case. Signed-off-by: Sriram Yagnaraman Reviewed-by: Marcelo Ricardo Leitner Signed-off-by: Pablo Neira Ayuso --- .../uapi/linux/netfilter/nf_conntrack_sctp.h | 1 + .../linux/netfilter/nfnetlink_cttimeout.h | 1 + net/netfilter/nf_conntrack_proto_sctp.c | 104 ++++++++++-------- net/netfilter/nf_conntrack_standalone.c | 8 ++ 4 files changed, 71 insertions(+), 43 deletions(-) diff --git a/include/uapi/linux/netfilter/nf_conntrack_sctp.h b/include/uapi/linux/netfilter/nf_conntrack_sctp.h index edc6ddab0de6..c742469afe21 100644 --- a/include/uapi/linux/netfilter/nf_conntrack_sctp.h +++ b/include/uapi/linux/netfilter/nf_conntrack_sctp.h @@ -16,6 +16,7 @@ enum sctp_conntrack { SCTP_CONNTRACK_SHUTDOWN_ACK_SENT, SCTP_CONNTRACK_HEARTBEAT_SENT, SCTP_CONNTRACK_HEARTBEAT_ACKED, + SCTP_CONNTRACK_DATA_SENT, SCTP_CONNTRACK_MAX }; diff --git a/include/uapi/linux/netfilter/nfnetlink_cttimeout.h b/include/uapi/linux/netfilter/nfnetlink_cttimeout.h index 6b20fb22717b..94e74034706d 100644 --- a/include/uapi/linux/netfilter/nfnetlink_cttimeout.h +++ b/include/uapi/linux/netfilter/nfnetlink_cttimeout.h @@ -95,6 +95,7 @@ enum ctattr_timeout_sctp { CTA_TIMEOUT_SCTP_SHUTDOWN_ACK_SENT, CTA_TIMEOUT_SCTP_HEARTBEAT_SENT, CTA_TIMEOUT_SCTP_HEARTBEAT_ACKED, + CTA_TIMEOUT_SCTP_DATA_SENT, __CTA_TIMEOUT_SCTP_MAX }; #define CTA_TIMEOUT_SCTP_MAX (__CTA_TIMEOUT_SCTP_MAX - 1) diff --git a/net/netfilter/nf_conntrack_proto_sctp.c b/net/netfilter/nf_conntrack_proto_sctp.c index 5a936334b517..d88b92a8ffca 100644 --- a/net/netfilter/nf_conntrack_proto_sctp.c +++ b/net/netfilter/nf_conntrack_proto_sctp.c @@ -60,6 +60,7 @@ static const unsigned int sctp_timeouts[SCTP_CONNTRACK_MAX] = { [SCTP_CONNTRACK_SHUTDOWN_ACK_SENT] = 3 SECS, [SCTP_CONNTRACK_HEARTBEAT_SENT] = 30 SECS, [SCTP_CONNTRACK_HEARTBEAT_ACKED] = 210 SECS, + [SCTP_CONNTRACK_DATA_SENT] = 30 SECS, }; #define SCTP_FLAG_HEARTBEAT_VTAG_FAILED 1 @@ -74,6 +75,7 @@ static const unsigned int sctp_timeouts[SCTP_CONNTRACK_MAX] = { #define sSA SCTP_CONNTRACK_SHUTDOWN_ACK_SENT #define sHS SCTP_CONNTRACK_HEARTBEAT_SENT #define sHA SCTP_CONNTRACK_HEARTBEAT_ACKED +#define sDS SCTP_CONNTRACK_DATA_SENT #define sIV SCTP_CONNTRACK_MAX /* @@ -90,15 +92,16 @@ COOKIE WAIT - We have seen an INIT chunk in the original direction, or als COOKIE ECHOED - We have seen a COOKIE_ECHO chunk in the original direction. ESTABLISHED - We have seen a COOKIE_ACK in the reply direction. SHUTDOWN_SENT - We have seen a SHUTDOWN chunk in the original direction. -SHUTDOWN_RECD - We have seen a SHUTDOWN chunk in the reply directoin. +SHUTDOWN_RECD - We have seen a SHUTDOWN chunk in the reply direction. SHUTDOWN_ACK_SENT - We have seen a SHUTDOWN_ACK chunk in the direction opposite to that of the SHUTDOWN chunk. CLOSED - We have seen a SHUTDOWN_COMPLETE chunk in the direction of the SHUTDOWN chunk. Connection is closed. HEARTBEAT_SENT - We have seen a HEARTBEAT in a new flow. -HEARTBEAT_ACKED - We have seen a HEARTBEAT-ACK in the direction opposite to - that of the HEARTBEAT chunk. Secondary connection is - established. +HEARTBEAT_ACKED - We have seen a HEARTBEAT-ACK/DATA/SACK in the direction + opposite to that of the HEARTBEAT/DATA chunk. Secondary connection + is established. +DATA_SENT - We have seen a DATA/SACK in a new flow. */ /* TODO @@ -112,36 +115,38 @@ cookie echoed to closed. */ /* SCTP conntrack state transitions */ -static const u8 sctp_conntracks[2][11][SCTP_CONNTRACK_MAX] = { +static const u8 sctp_conntracks[2][12][SCTP_CONNTRACK_MAX] = { { /* ORIGINAL */ -/* sNO, sCL, sCW, sCE, sES, sSS, sSR, sSA, sHS, sHA */ -/* init */ {sCL, sCL, sCW, sCE, sES, sSS, sSR, sSA, sCW, sHA}, -/* init_ack */ {sCL, sCL, sCW, sCE, sES, sSS, sSR, sSA, sCL, sHA}, -/* abort */ {sCL, sCL, sCL, sCL, sCL, sCL, sCL, sCL, sCL, sCL}, -/* shutdown */ {sCL, sCL, sCW, sCE, sSS, sSS, sSR, sSA, sCL, sSS}, -/* shutdown_ack */ {sSA, sCL, sCW, sCE, sES, sSA, sSA, sSA, sSA, sHA}, -/* error */ {sCL, sCL, sCW, sCE, sES, sSS, sSR, sSA, sCL, sHA},/* Can't have Stale cookie*/ -/* cookie_echo */ {sCL, sCL, sCE, sCE, sES, sSS, sSR, sSA, sCL, sHA},/* 5.2.4 - Big TODO */ -/* cookie_ack */ {sCL, sCL, sCW, sCE, sES, sSS, sSR, sSA, sCL, sHA},/* Can't come in orig dir */ -/* shutdown_comp*/ {sCL, sCL, sCW, sCE, sES, sSS, sSR, sCL, sCL, sHA}, -/* heartbeat */ {sHS, sCL, sCW, sCE, sES, sSS, sSR, sSA, sHS, sHA}, -/* heartbeat_ack*/ {sCL, sCL, sCW, sCE, sES, sSS, sSR, sSA, sHS, sHA} +/* sNO, sCL, sCW, sCE, sES, sSS, sSR, sSA, sHS, sHA, sDS */ +/* init */ {sCL, sCL, sCW, sCE, sES, sSS, sSR, sSA, sCW, sHA, sCW}, +/* init_ack */ {sCL, sCL, sCW, sCE, sES, sSS, sSR, sSA, sCL, sHA, sCL}, +/* abort */ {sCL, sCL, sCL, sCL, sCL, sCL, sCL, sCL, sCL, sCL, sCL}, +/* shutdown */ {sCL, sCL, sCW, sCE, sSS, sSS, sSR, sSA, sCL, sSS, sCL}, +/* shutdown_ack */ {sSA, sCL, sCW, sCE, sES, sSA, sSA, sSA, sSA, sHA, sSA}, +/* error */ {sCL, sCL, sCW, sCE, sES, sSS, sSR, sSA, sCL, sHA, sCL},/* Can't have Stale cookie*/ +/* cookie_echo */ {sCL, sCL, sCE, sCE, sES, sSS, sSR, sSA, sCL, sHA, sCL},/* 5.2.4 - Big TODO */ +/* cookie_ack */ {sCL, sCL, sCW, sCE, sES, sSS, sSR, sSA, sCL, sHA, sCL},/* Can't come in orig dir */ +/* shutdown_comp*/ {sCL, sCL, sCW, sCE, sES, sSS, sSR, sCL, sCL, sHA, sCL}, +/* heartbeat */ {sHS, sCL, sCW, sCE, sES, sSS, sSR, sSA, sHS, sHA, sDS}, +/* heartbeat_ack*/ {sCL, sCL, sCW, sCE, sES, sSS, sSR, sSA, sHS, sHA, sDS}, +/* data/sack */ {sDS, sCL, sCW, sCE, sES, sSS, sSR, sSA, sHS, sHA, sDS} }, { /* REPLY */ -/* sNO, sCL, sCW, sCE, sES, sSS, sSR, sSA, sHS, sHA */ -/* init */ {sIV, sCL, sCW, sCE, sES, sSS, sSR, sSA, sIV, sHA},/* INIT in sCL Big TODO */ -/* init_ack */ {sIV, sCW, sCW, sCE, sES, sSS, sSR, sSA, sIV, sHA}, -/* abort */ {sIV, sCL, sCL, sCL, sCL, sCL, sCL, sCL, sIV, sCL}, -/* shutdown */ {sIV, sCL, sCW, sCE, sSR, sSS, sSR, sSA, sIV, sSR}, -/* shutdown_ack */ {sIV, sCL, sCW, sCE, sES, sSA, sSA, sSA, sIV, sHA}, -/* error */ {sIV, sCL, sCW, sCL, sES, sSS, sSR, sSA, sIV, sHA}, -/* cookie_echo */ {sIV, sCL, sCW, sCE, sES, sSS, sSR, sSA, sIV, sHA},/* Can't come in reply dir */ -/* cookie_ack */ {sIV, sCL, sCW, sES, sES, sSS, sSR, sSA, sIV, sHA}, -/* shutdown_comp*/ {sIV, sCL, sCW, sCE, sES, sSS, sSR, sCL, sIV, sHA}, -/* heartbeat */ {sIV, sCL, sCW, sCE, sES, sSS, sSR, sSA, sHS, sHA}, -/* heartbeat_ack*/ {sIV, sCL, sCW, sCE, sES, sSS, sSR, sSA, sHA, sHA} +/* sNO, sCL, sCW, sCE, sES, sSS, sSR, sSA, sHS, sHA, sDS */ +/* init */ {sIV, sCL, sCW, sCE, sES, sSS, sSR, sSA, sIV, sHA, sIV},/* INIT in sCL Big TODO */ +/* init_ack */ {sIV, sCW, sCW, sCE, sES, sSS, sSR, sSA, sIV, sHA, sIV}, +/* abort */ {sIV, sCL, sCL, sCL, sCL, sCL, sCL, sCL, sIV, sCL, sIV}, +/* shutdown */ {sIV, sCL, sCW, sCE, sSR, sSS, sSR, sSA, sIV, sSR, sIV}, +/* shutdown_ack */ {sIV, sCL, sCW, sCE, sES, sSA, sSA, sSA, sIV, sHA, sIV}, +/* error */ {sIV, sCL, sCW, sCL, sES, sSS, sSR, sSA, sIV, sHA, sIV}, +/* cookie_echo */ {sIV, sCL, sCW, sCE, sES, sSS, sSR, sSA, sIV, sHA, sIV},/* Can't come in reply dir */ +/* cookie_ack */ {sIV, sCL, sCW, sES, sES, sSS, sSR, sSA, sIV, sHA, sIV}, +/* shutdown_comp*/ {sIV, sCL, sCW, sCE, sES, sSS, sSR, sCL, sIV, sHA, sIV}, +/* heartbeat */ {sIV, sCL, sCW, sCE, sES, sSS, sSR, sSA, sHS, sHA, sHA}, +/* heartbeat_ack*/ {sIV, sCL, sCW, sCE, sES, sSS, sSR, sSA, sHA, sHA, sHA}, +/* data/sack */ {sIV, sCL, sCW, sCE, sES, sSS, sSR, sSA, sHA, sHA, sHA}, } }; @@ -253,6 +258,11 @@ static int sctp_new_state(enum ip_conntrack_dir dir, pr_debug("SCTP_CID_HEARTBEAT_ACK"); i = 10; break; + case SCTP_CID_DATA: + case SCTP_CID_SACK: + pr_debug("SCTP_CID_DATA/SACK"); + i = 11; + break; default: /* Other chunks like DATA or SACK do not change the state */ pr_debug("Unknown chunk type, Will stay in %s\n", @@ -306,7 +316,9 @@ sctp_new(struct nf_conn *ct, const struct sk_buff *skb, ih->init_tag); ct->proto.sctp.vtag[IP_CT_DIR_REPLY] = ih->init_tag; - } else if (sch->type == SCTP_CID_HEARTBEAT) { + } else if (sch->type == SCTP_CID_HEARTBEAT || + sch->type == SCTP_CID_DATA || + sch->type == SCTP_CID_SACK) { pr_debug("Setting vtag %x for secondary conntrack\n", sh->vtag); ct->proto.sctp.vtag[IP_CT_DIR_ORIGINAL] = sh->vtag; @@ -392,19 +404,19 @@ int nf_conntrack_sctp_packet(struct nf_conn *ct, if (!sctp_new(ct, skb, sh, dataoff)) return -NF_ACCEPT; - } - - /* Check the verification tag (Sec 8.5) */ - if (!test_bit(SCTP_CID_INIT, map) && - !test_bit(SCTP_CID_SHUTDOWN_COMPLETE, map) && - !test_bit(SCTP_CID_COOKIE_ECHO, map) && - !test_bit(SCTP_CID_ABORT, map) && - !test_bit(SCTP_CID_SHUTDOWN_ACK, map) && - !test_bit(SCTP_CID_HEARTBEAT, map) && - !test_bit(SCTP_CID_HEARTBEAT_ACK, map) && - sh->vtag != ct->proto.sctp.vtag[dir]) { - pr_debug("Verification tag check failed\n"); - goto out; + } else { + /* Check the verification tag (Sec 8.5) */ + if (!test_bit(SCTP_CID_INIT, map) && + !test_bit(SCTP_CID_SHUTDOWN_COMPLETE, map) && + !test_bit(SCTP_CID_COOKIE_ECHO, map) && + !test_bit(SCTP_CID_ABORT, map) && + !test_bit(SCTP_CID_SHUTDOWN_ACK, map) && + !test_bit(SCTP_CID_HEARTBEAT, map) && + !test_bit(SCTP_CID_HEARTBEAT_ACK, map) && + sh->vtag != ct->proto.sctp.vtag[dir]) { + pr_debug("Verification tag check failed\n"); + goto out; + } } old_state = new_state = SCTP_CONNTRACK_NONE; @@ -464,6 +476,11 @@ int nf_conntrack_sctp_packet(struct nf_conn *ct, } else if (ct->proto.sctp.flags & SCTP_FLAG_HEARTBEAT_VTAG_FAILED) { ct->proto.sctp.flags &= ~SCTP_FLAG_HEARTBEAT_VTAG_FAILED; } + } else if (sch->type == SCTP_CID_DATA || sch->type == SCTP_CID_SACK) { + if (ct->proto.sctp.vtag[dir] == 0) { + pr_debug("Setting vtag %x for dir %d\n", sh->vtag, dir); + ct->proto.sctp.vtag[dir] = sh->vtag; + } } old_state = ct->proto.sctp.state; @@ -684,6 +701,7 @@ sctp_timeout_nla_policy[CTA_TIMEOUT_SCTP_MAX+1] = { [CTA_TIMEOUT_SCTP_SHUTDOWN_ACK_SENT] = { .type = NLA_U32 }, [CTA_TIMEOUT_SCTP_HEARTBEAT_SENT] = { .type = NLA_U32 }, [CTA_TIMEOUT_SCTP_HEARTBEAT_ACKED] = { .type = NLA_U32 }, + [CTA_TIMEOUT_SCTP_DATA_SENT] = { .type = NLA_U32 }, }; #endif /* CONFIG_NF_CONNTRACK_TIMEOUT */ diff --git a/net/netfilter/nf_conntrack_standalone.c b/net/netfilter/nf_conntrack_standalone.c index 4ffe84c5a82c..a884c06abf09 100644 --- a/net/netfilter/nf_conntrack_standalone.c +++ b/net/netfilter/nf_conntrack_standalone.c @@ -602,6 +602,7 @@ enum nf_ct_sysctl_index { NF_SYSCTL_CT_PROTO_TIMEOUT_SCTP_SHUTDOWN_ACK_SENT, NF_SYSCTL_CT_PROTO_TIMEOUT_SCTP_HEARTBEAT_SENT, NF_SYSCTL_CT_PROTO_TIMEOUT_SCTP_HEARTBEAT_ACKED, + NF_SYSCTL_CT_PROTO_TIMEOUT_SCTP_DATA_SENT, #endif #ifdef CONFIG_NF_CT_PROTO_DCCP NF_SYSCTL_CT_PROTO_TIMEOUT_DCCP_REQUEST, @@ -892,6 +893,12 @@ static struct ctl_table nf_ct_sysctl_table[] = { .mode = 0644, .proc_handler = proc_dointvec_jiffies, }, + [NF_SYSCTL_CT_PROTO_TIMEOUT_SCTP_DATA_SENT] = { + .procname = "nf_conntrack_sctp_timeout_data_sent", + .maxlen = sizeof(unsigned int), + .mode = 0644, + .proc_handler = proc_dointvec_jiffies, + }, #endif #ifdef CONFIG_NF_CT_PROTO_DCCP [NF_SYSCTL_CT_PROTO_TIMEOUT_DCCP_REQUEST] = { @@ -1036,6 +1043,7 @@ static void nf_conntrack_standalone_init_sctp_sysctl(struct net *net, XASSIGN(SHUTDOWN_ACK_SENT, sn); XASSIGN(HEARTBEAT_SENT, sn); XASSIGN(HEARTBEAT_ACKED, sn); + XASSIGN(DATA_SENT, sn); #undef XASSIGN #endif } From a70e483460d58e64504dd679fd127e9549385c86 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Wed, 9 Nov 2022 12:21:58 +0100 Subject: [PATCH 1331/2002] netfilter: conntrack: merge ipv4+ipv6 confirm functions No need to have distinct functions. After merge, ipv6 can avoid protooff computation if the connection neither needs sequence adjustment nor helper invocation -- this is the normal case. Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/nf_conntrack_core.h | 3 +- net/bridge/netfilter/nf_conntrack_bridge.c | 32 +---- net/netfilter/nf_conntrack_proto.c | 136 +++++++++------------ 3 files changed, 63 insertions(+), 108 deletions(-) diff --git a/include/net/netfilter/nf_conntrack_core.h b/include/net/netfilter/nf_conntrack_core.h index b2b9de70d9f4..71d1269fe4d4 100644 --- a/include/net/netfilter/nf_conntrack_core.h +++ b/include/net/netfilter/nf_conntrack_core.h @@ -71,8 +71,7 @@ static inline int nf_conntrack_confirm(struct sk_buff *skb) return ret; } -unsigned int nf_confirm(struct sk_buff *skb, unsigned int protoff, - struct nf_conn *ct, enum ip_conntrack_info ctinfo); +unsigned int nf_confirm(void *priv, struct sk_buff *skb, const struct nf_hook_state *state); void print_tuple(struct seq_file *s, const struct nf_conntrack_tuple *tuple, const struct nf_conntrack_l4proto *proto); diff --git a/net/bridge/netfilter/nf_conntrack_bridge.c b/net/bridge/netfilter/nf_conntrack_bridge.c index 73242962be5d..5c5dd437f1c2 100644 --- a/net/bridge/netfilter/nf_conntrack_bridge.c +++ b/net/bridge/netfilter/nf_conntrack_bridge.c @@ -366,42 +366,12 @@ static int nf_ct_bridge_refrag_post(struct net *net, struct sock *sk, return br_dev_queue_push_xmit(net, sk, skb); } -static unsigned int nf_ct_bridge_confirm(struct sk_buff *skb) -{ - enum ip_conntrack_info ctinfo; - struct nf_conn *ct; - int protoff; - - ct = nf_ct_get(skb, &ctinfo); - if (!ct || ctinfo == IP_CT_RELATED_REPLY) - return nf_conntrack_confirm(skb); - - switch (skb->protocol) { - case htons(ETH_P_IP): - protoff = skb_network_offset(skb) + ip_hdrlen(skb); - break; - case htons(ETH_P_IPV6): { - unsigned char pnum = ipv6_hdr(skb)->nexthdr; - __be16 frag_off; - - protoff = ipv6_skip_exthdr(skb, sizeof(struct ipv6hdr), &pnum, - &frag_off); - if (protoff < 0 || (frag_off & htons(~0x7)) != 0) - return nf_conntrack_confirm(skb); - } - break; - default: - return NF_ACCEPT; - } - return nf_confirm(skb, protoff, ct, ctinfo); -} - static unsigned int nf_ct_bridge_post(void *priv, struct sk_buff *skb, const struct nf_hook_state *state) { int ret; - ret = nf_ct_bridge_confirm(skb); + ret = nf_confirm(priv, skb, state); if (ret != NF_ACCEPT) return ret; diff --git a/net/netfilter/nf_conntrack_proto.c b/net/netfilter/nf_conntrack_proto.c index 895b09cbd7cf..99323fb12d0f 100644 --- a/net/netfilter/nf_conntrack_proto.c +++ b/net/netfilter/nf_conntrack_proto.c @@ -121,40 +121,6 @@ const struct nf_conntrack_l4proto *nf_ct_l4proto_find(u8 l4proto) }; EXPORT_SYMBOL_GPL(nf_ct_l4proto_find); -unsigned int nf_confirm(struct sk_buff *skb, unsigned int protoff, - struct nf_conn *ct, enum ip_conntrack_info ctinfo) -{ - const struct nf_conn_help *help; - - help = nfct_help(ct); - if (help) { - const struct nf_conntrack_helper *helper; - int ret; - - /* rcu_read_lock()ed by nf_hook_thresh */ - helper = rcu_dereference(help->helper); - if (helper) { - ret = helper->help(skb, - protoff, - ct, ctinfo); - if (ret != NF_ACCEPT) - return ret; - } - } - - if (test_bit(IPS_SEQ_ADJUST_BIT, &ct->status) && - !nf_is_loopback_packet(skb)) { - if (!nf_ct_seq_adjust(skb, ct, ctinfo, protoff)) { - NF_CT_STAT_INC_ATOMIC(nf_ct_net(ct), drop); - return NF_DROP; - } - } - - /* We've seen it coming out the other side: confirm it */ - return nf_conntrack_confirm(skb); -} -EXPORT_SYMBOL_GPL(nf_confirm); - static bool in_vrf_postrouting(const struct nf_hook_state *state) { #if IS_ENABLED(CONFIG_NET_L3_MASTER_DEV) @@ -165,24 +131,71 @@ static bool in_vrf_postrouting(const struct nf_hook_state *state) return false; } -static unsigned int ipv4_confirm(void *priv, - struct sk_buff *skb, - const struct nf_hook_state *state) +unsigned int nf_confirm(void *priv, + struct sk_buff *skb, + const struct nf_hook_state *state) { + const struct nf_conn_help *help; enum ip_conntrack_info ctinfo; + unsigned int protoff; struct nf_conn *ct; + bool seqadj_needed; + __be16 frag_off; + u8 pnum; ct = nf_ct_get(skb, &ctinfo); - if (!ct || ctinfo == IP_CT_RELATED_REPLY) - return nf_conntrack_confirm(skb); - - if (in_vrf_postrouting(state)) + if (!ct || in_vrf_postrouting(state)) return NF_ACCEPT; - return nf_confirm(skb, - skb_network_offset(skb) + ip_hdrlen(skb), - ct, ctinfo); + help = nfct_help(ct); + + seqadj_needed = test_bit(IPS_SEQ_ADJUST_BIT, &ct->status) && !nf_is_loopback_packet(skb); + if (!help && !seqadj_needed) + return nf_conntrack_confirm(skb); + + /* helper->help() do not expect ICMP packets */ + if (ctinfo == IP_CT_RELATED_REPLY) + return nf_conntrack_confirm(skb); + + switch (nf_ct_l3num(ct)) { + case NFPROTO_IPV4: + protoff = skb_network_offset(skb) + ip_hdrlen(skb); + break; + case NFPROTO_IPV6: + pnum = ipv6_hdr(skb)->nexthdr; + protoff = ipv6_skip_exthdr(skb, sizeof(struct ipv6hdr), &pnum, &frag_off); + if (protoff < 0 || (frag_off & htons(~0x7)) != 0) + return nf_conntrack_confirm(skb); + break; + default: + return nf_conntrack_confirm(skb); + } + + if (help) { + const struct nf_conntrack_helper *helper; + int ret; + + /* rcu_read_lock()ed by nf_hook */ + helper = rcu_dereference(help->helper); + if (helper) { + ret = helper->help(skb, + protoff, + ct, ctinfo); + if (ret != NF_ACCEPT) + return ret; + } + } + + if (seqadj_needed && + !nf_ct_seq_adjust(skb, ct, ctinfo, protoff)) { + NF_CT_STAT_INC_ATOMIC(nf_ct_net(ct), drop); + return NF_DROP; + } + + /* We've seen it coming out the other side: confirm it */ + return nf_conntrack_confirm(skb); } +EXPORT_SYMBOL_GPL(nf_confirm); static unsigned int ipv4_conntrack_in(void *priv, struct sk_buff *skb, @@ -230,13 +243,13 @@ static const struct nf_hook_ops ipv4_conntrack_ops[] = { .priority = NF_IP_PRI_CONNTRACK, }, { - .hook = ipv4_confirm, + .hook = nf_confirm, .pf = NFPROTO_IPV4, .hooknum = NF_INET_POST_ROUTING, .priority = NF_IP_PRI_CONNTRACK_CONFIRM, }, { - .hook = ipv4_confirm, + .hook = nf_confirm, .pf = NFPROTO_IPV4, .hooknum = NF_INET_LOCAL_IN, .priority = NF_IP_PRI_CONNTRACK_CONFIRM, @@ -373,33 +386,6 @@ static struct nf_sockopt_ops so_getorigdst6 = { .owner = THIS_MODULE, }; -static unsigned int ipv6_confirm(void *priv, - struct sk_buff *skb, - const struct nf_hook_state *state) -{ - struct nf_conn *ct; - enum ip_conntrack_info ctinfo; - unsigned char pnum = ipv6_hdr(skb)->nexthdr; - __be16 frag_off; - int protoff; - - ct = nf_ct_get(skb, &ctinfo); - if (!ct || ctinfo == IP_CT_RELATED_REPLY) - return nf_conntrack_confirm(skb); - - if (in_vrf_postrouting(state)) - return NF_ACCEPT; - - protoff = ipv6_skip_exthdr(skb, sizeof(struct ipv6hdr), &pnum, - &frag_off); - if (protoff < 0 || (frag_off & htons(~0x7)) != 0) { - pr_debug("proto header not found\n"); - return nf_conntrack_confirm(skb); - } - - return nf_confirm(skb, protoff, ct, ctinfo); -} - static unsigned int ipv6_conntrack_in(void *priv, struct sk_buff *skb, const struct nf_hook_state *state) @@ -428,13 +414,13 @@ static const struct nf_hook_ops ipv6_conntrack_ops[] = { .priority = NF_IP6_PRI_CONNTRACK, }, { - .hook = ipv6_confirm, + .hook = nf_confirm, .pf = NFPROTO_IPV6, .hooknum = NF_INET_POST_ROUTING, .priority = NF_IP6_PRI_LAST, }, { - .hook = ipv6_confirm, + .hook = nf_confirm, .pf = NFPROTO_IPV6, .hooknum = NF_INET_LOCAL_IN, .priority = NF_IP6_PRI_LAST - 1, From e9374524950512a1769f610a868fcdf89ea59b8e Mon Sep 17 00:00:00 2001 From: Vishwanath Pai Date: Tue, 22 Nov 2022 20:30:57 +0100 Subject: [PATCH 1332/2002] netfilter: ipset: Add support for new bitmask parameter Add a new parameter to complement the existing 'netmask' option. The main difference between netmask and bitmask is that bitmask takes any arbitrary ip address as input, it does not have to be a valid netmask. The name of the new parameter is 'bitmask'. This lets us mask out arbitrary bits in the ip address, for example: ipset create set1 hash:ip bitmask 255.128.255.0 ipset create set2 hash:ip,port family inet6 bitmask ffff::ff80 Signed-off-by: Vishwanath Pai Signed-off-by: Joshua Hunt Signed-off-by: Jozsef Kadlecsik Signed-off-by: Pablo Neira Ayuso --- include/linux/netfilter/ipset/ip_set.h | 10 +++ include/uapi/linux/netfilter/ipset/ip_set.h | 2 + net/netfilter/ipset/ip_set_hash_gen.h | 71 ++++++++++++++++++--- net/netfilter/ipset/ip_set_hash_ip.c | 19 +++--- net/netfilter/ipset/ip_set_hash_ipport.c | 24 ++++++- net/netfilter/ipset/ip_set_hash_netnet.c | 26 ++++++-- 6 files changed, 126 insertions(+), 26 deletions(-) diff --git a/include/linux/netfilter/ipset/ip_set.h b/include/linux/netfilter/ipset/ip_set.h index ada1296c87d5..ab934ad951a8 100644 --- a/include/linux/netfilter/ipset/ip_set.h +++ b/include/linux/netfilter/ipset/ip_set.h @@ -515,6 +515,16 @@ ip_set_init_skbinfo(struct ip_set_skbinfo *skbinfo, *skbinfo = ext->skbinfo; } +static inline void +nf_inet_addr_mask_inplace(union nf_inet_addr *a1, + const union nf_inet_addr *mask) +{ + a1->all[0] &= mask->all[0]; + a1->all[1] &= mask->all[1]; + a1->all[2] &= mask->all[2]; + a1->all[3] &= mask->all[3]; +} + #define IP_SET_INIT_KEXT(skb, opt, set) \ { .bytes = (skb)->len, .packets = 1, .target = true,\ .timeout = ip_set_adt_opt_timeout(opt, set) } diff --git a/include/uapi/linux/netfilter/ipset/ip_set.h b/include/uapi/linux/netfilter/ipset/ip_set.h index 79e5d68b87af..333807efd32b 100644 --- a/include/uapi/linux/netfilter/ipset/ip_set.h +++ b/include/uapi/linux/netfilter/ipset/ip_set.h @@ -85,6 +85,7 @@ enum { IPSET_ATTR_CADT_LINENO = IPSET_ATTR_LINENO, /* 9 */ IPSET_ATTR_MARK, /* 10 */ IPSET_ATTR_MARKMASK, /* 11 */ + IPSET_ATTR_BITMASK, /* 12 */ /* Reserve empty slots */ IPSET_ATTR_CADT_MAX = 16, /* Create-only specific attributes */ @@ -153,6 +154,7 @@ enum ipset_errno { IPSET_ERR_COMMENT, IPSET_ERR_INVALID_MARKMASK, IPSET_ERR_SKBINFO, + IPSET_ERR_BITMASK_NETMASK_EXCL, /* Type specific error codes */ IPSET_ERR_TYPE_SPECIFIC = 4352, diff --git a/net/netfilter/ipset/ip_set_hash_gen.h b/net/netfilter/ipset/ip_set_hash_gen.h index 3adc291d9ce1..fdb5225a3e5c 100644 --- a/net/netfilter/ipset/ip_set_hash_gen.h +++ b/net/netfilter/ipset/ip_set_hash_gen.h @@ -159,6 +159,17 @@ htable_size(u8 hbits) (SET_WITH_TIMEOUT(set) && \ ip_set_timeout_expired(ext_timeout(d, set))) +#if defined(IP_SET_HASH_WITH_NETMASK) || defined(IP_SET_HASH_WITH_BITMASK) +static const union nf_inet_addr onesmask = { + .all[0] = 0xffffffff, + .all[1] = 0xffffffff, + .all[2] = 0xffffffff, + .all[3] = 0xffffffff +}; + +static const union nf_inet_addr zeromask = {}; +#endif + #endif /* _IP_SET_HASH_GEN_H */ #ifndef MTYPE @@ -283,8 +294,9 @@ struct htype { u32 markmask; /* markmask value for mark mask to store */ #endif u8 bucketsize; /* max elements in an array block */ -#ifdef IP_SET_HASH_WITH_NETMASK +#if defined(IP_SET_HASH_WITH_NETMASK) || defined(IP_SET_HASH_WITH_BITMASK) u8 netmask; /* netmask value for subnets to store */ + union nf_inet_addr bitmask; /* stores bitmask */ #endif struct list_head ad; /* Resize add|del backlist */ struct mtype_elem next; /* temporary storage for uadd */ @@ -459,8 +471,8 @@ mtype_same_set(const struct ip_set *a, const struct ip_set *b) /* Resizing changes htable_bits, so we ignore it */ return x->maxelem == y->maxelem && a->timeout == b->timeout && -#ifdef IP_SET_HASH_WITH_NETMASK - x->netmask == y->netmask && +#if defined(IP_SET_HASH_WITH_NETMASK) || defined(IP_SET_HASH_WITH_BITMASK) + nf_inet_addr_cmp(&x->bitmask, &y->bitmask) && #endif #ifdef IP_SET_HASH_WITH_MARKMASK x->markmask == y->markmask && @@ -1264,9 +1276,21 @@ mtype_head(struct ip_set *set, struct sk_buff *skb) htonl(jhash_size(htable_bits))) || nla_put_net32(skb, IPSET_ATTR_MAXELEM, htonl(h->maxelem))) goto nla_put_failure; +#ifdef IP_SET_HASH_WITH_BITMASK + /* if netmask is set to anything other than HOST_MASK we know that the user supplied netmask + * and not bitmask. These two are mutually exclusive. */ + if (h->netmask == HOST_MASK && !nf_inet_addr_cmp(&onesmask, &h->bitmask)) { + if (set->family == NFPROTO_IPV4) { + if (nla_put_ipaddr4(skb, IPSET_ATTR_BITMASK, h->bitmask.ip)) + goto nla_put_failure; + } else if (set->family == NFPROTO_IPV6) { + if (nla_put_ipaddr6(skb, IPSET_ATTR_BITMASK, &h->bitmask.in6)) + goto nla_put_failure; + } + } +#endif #ifdef IP_SET_HASH_WITH_NETMASK - if (h->netmask != HOST_MASK && - nla_put_u8(skb, IPSET_ATTR_NETMASK, h->netmask)) + if (h->netmask != HOST_MASK && nla_put_u8(skb, IPSET_ATTR_NETMASK, h->netmask)) goto nla_put_failure; #endif #ifdef IP_SET_HASH_WITH_MARKMASK @@ -1429,8 +1453,10 @@ IPSET_TOKEN(HTYPE, _create)(struct net *net, struct ip_set *set, u32 markmask; #endif u8 hbits; -#ifdef IP_SET_HASH_WITH_NETMASK - u8 netmask; +#if defined(IP_SET_HASH_WITH_NETMASK) || defined(IP_SET_HASH_WITH_BITMASK) + int ret __attribute__((unused)) = 0; + u8 netmask = set->family == NFPROTO_IPV4 ? 32 : 128; + union nf_inet_addr bitmask = onesmask; #endif size_t hsize; struct htype *h; @@ -1468,7 +1494,6 @@ IPSET_TOKEN(HTYPE, _create)(struct net *net, struct ip_set *set, #endif #ifdef IP_SET_HASH_WITH_NETMASK - netmask = set->family == NFPROTO_IPV4 ? 32 : 128; if (tb[IPSET_ATTR_NETMASK]) { netmask = nla_get_u8(tb[IPSET_ATTR_NETMASK]); @@ -1476,6 +1501,33 @@ IPSET_TOKEN(HTYPE, _create)(struct net *net, struct ip_set *set, (set->family == NFPROTO_IPV6 && netmask > 128) || netmask == 0) return -IPSET_ERR_INVALID_NETMASK; + + /* we convert netmask to bitmask and store it */ + if (set->family == NFPROTO_IPV4) + bitmask.ip = ip_set_netmask(netmask); + else + ip6_netmask(&bitmask, netmask); + } +#endif + +#ifdef IP_SET_HASH_WITH_BITMASK + if (tb[IPSET_ATTR_BITMASK]) { + /* bitmask and netmask do the same thing, allow only one of these options */ + if (tb[IPSET_ATTR_NETMASK]) + return -IPSET_ERR_BITMASK_NETMASK_EXCL; + + if (set->family == NFPROTO_IPV4) { + ret = ip_set_get_ipaddr4(tb[IPSET_ATTR_BITMASK], &bitmask.ip); + if (ret || !bitmask.ip) + return -IPSET_ERR_INVALID_NETMASK; + } else if (set->family == NFPROTO_IPV6) { + ret = ip_set_get_ipaddr6(tb[IPSET_ATTR_BITMASK], &bitmask); + if (ret || ipv6_addr_any(&bitmask.in6)) + return -IPSET_ERR_INVALID_NETMASK; + } + + if (nf_inet_addr_cmp(&bitmask, &zeromask)) + return -IPSET_ERR_INVALID_NETMASK; } #endif @@ -1518,7 +1570,8 @@ IPSET_TOKEN(HTYPE, _create)(struct net *net, struct ip_set *set, for (i = 0; i < ahash_numof_locks(hbits); i++) spin_lock_init(&t->hregion[i].lock); h->maxelem = maxelem; -#ifdef IP_SET_HASH_WITH_NETMASK +#if defined(IP_SET_HASH_WITH_NETMASK) || defined(IP_SET_HASH_WITH_BITMASK) + h->bitmask = bitmask; h->netmask = netmask; #endif #ifdef IP_SET_HASH_WITH_MARKMASK diff --git a/net/netfilter/ipset/ip_set_hash_ip.c b/net/netfilter/ipset/ip_set_hash_ip.c index dd30c03d5a23..556f74268a28 100644 --- a/net/netfilter/ipset/ip_set_hash_ip.c +++ b/net/netfilter/ipset/ip_set_hash_ip.c @@ -24,7 +24,8 @@ /* 2 Comments support */ /* 3 Forceadd support */ /* 4 skbinfo support */ -#define IPSET_TYPE_REV_MAX 5 /* bucketsize, initval support */ +/* 5 bucketsize, initval support */ +#define IPSET_TYPE_REV_MAX 6 /* bitmask support */ MODULE_LICENSE("GPL"); MODULE_AUTHOR("Jozsef Kadlecsik "); @@ -34,6 +35,7 @@ MODULE_ALIAS("ip_set_hash:ip"); /* Type specific function prefix */ #define HTYPE hash_ip #define IP_SET_HASH_WITH_NETMASK +#define IP_SET_HASH_WITH_BITMASK /* IPv4 variant */ @@ -86,7 +88,7 @@ hash_ip4_kadt(struct ip_set *set, const struct sk_buff *skb, __be32 ip; ip4addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &ip); - ip &= ip_set_netmask(h->netmask); + ip &= h->bitmask.ip; if (ip == 0) return -EINVAL; @@ -119,7 +121,7 @@ hash_ip4_uadt(struct ip_set *set, struct nlattr *tb[], if (ret) return ret; - ip &= ip_set_hostmask(h->netmask); + ip &= ntohl(h->bitmask.ip); e.ip = htonl(ip); if (e.ip == 0) return -IPSET_ERR_HASH_ELEM; @@ -187,12 +189,6 @@ hash_ip6_data_equal(const struct hash_ip6_elem *ip1, return ipv6_addr_equal(&ip1->ip.in6, &ip2->ip.in6); } -static void -hash_ip6_netmask(union nf_inet_addr *ip, u8 prefix) -{ - ip6_netmask(ip, prefix); -} - static bool hash_ip6_data_list(struct sk_buff *skb, const struct hash_ip6_elem *e) { @@ -229,7 +225,7 @@ hash_ip6_kadt(struct ip_set *set, const struct sk_buff *skb, struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set); ip6addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &e.ip.in6); - hash_ip6_netmask(&e.ip, h->netmask); + nf_inet_addr_mask_inplace(&e.ip, &h->bitmask); if (ipv6_addr_any(&e.ip.in6)) return -EINVAL; @@ -268,7 +264,7 @@ hash_ip6_uadt(struct ip_set *set, struct nlattr *tb[], if (ret) return ret; - hash_ip6_netmask(&e.ip, h->netmask); + nf_inet_addr_mask_inplace(&e.ip, &h->bitmask); if (ipv6_addr_any(&e.ip.in6)) return -IPSET_ERR_HASH_ELEM; @@ -295,6 +291,7 @@ static struct ip_set_type hash_ip_type __read_mostly = { [IPSET_ATTR_RESIZE] = { .type = NLA_U8 }, [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 }, [IPSET_ATTR_NETMASK] = { .type = NLA_U8 }, + [IPSET_ATTR_BITMASK] = { .type = NLA_NESTED }, [IPSET_ATTR_CADT_FLAGS] = { .type = NLA_U32 }, }, .adt_policy = { diff --git a/net/netfilter/ipset/ip_set_hash_ipport.c b/net/netfilter/ipset/ip_set_hash_ipport.c index 7303138e46be..2ffbd0b78a8c 100644 --- a/net/netfilter/ipset/ip_set_hash_ipport.c +++ b/net/netfilter/ipset/ip_set_hash_ipport.c @@ -26,7 +26,8 @@ /* 3 Comments support added */ /* 4 Forceadd support added */ /* 5 skbinfo support added */ -#define IPSET_TYPE_REV_MAX 6 /* bucketsize, initval support added */ +/* 6 bucketsize, initval support added */ +#define IPSET_TYPE_REV_MAX 7 /* bitmask support added */ MODULE_LICENSE("GPL"); MODULE_AUTHOR("Jozsef Kadlecsik "); @@ -35,6 +36,8 @@ MODULE_ALIAS("ip_set_hash:ip,port"); /* Type specific function prefix */ #define HTYPE hash_ipport +#define IP_SET_HASH_WITH_NETMASK +#define IP_SET_HASH_WITH_BITMASK /* IPv4 variant */ @@ -92,12 +95,16 @@ hash_ipport4_kadt(struct ip_set *set, const struct sk_buff *skb, ipset_adtfn adtfn = set->variant->adt[adt]; struct hash_ipport4_elem e = { .ip = 0 }; struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set); + const struct MTYPE *h = set->data; if (!ip_set_get_ip4_port(skb, opt->flags & IPSET_DIM_TWO_SRC, &e.port, &e.proto)) return -EINVAL; ip4addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &e.ip); + e.ip &= h->bitmask.ip; + if (e.ip == 0) + return -EINVAL; return adtfn(set, &e, &ext, &opt->ext, opt->cmdflags); } @@ -129,6 +136,10 @@ hash_ipport4_uadt(struct ip_set *set, struct nlattr *tb[], if (ret) return ret; + e.ip &= h->bitmask.ip; + if (e.ip == 0) + return -EINVAL; + e.port = nla_get_be16(tb[IPSET_ATTR_PORT]); if (tb[IPSET_ATTR_PROTO]) { @@ -253,12 +264,17 @@ hash_ipport6_kadt(struct ip_set *set, const struct sk_buff *skb, ipset_adtfn adtfn = set->variant->adt[adt]; struct hash_ipport6_elem e = { .ip = { .all = { 0 } } }; struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set); + const struct MTYPE *h = set->data; if (!ip_set_get_ip6_port(skb, opt->flags & IPSET_DIM_TWO_SRC, &e.port, &e.proto)) return -EINVAL; ip6addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &e.ip.in6); + nf_inet_addr_mask_inplace(&e.ip, &h->bitmask); + if (ipv6_addr_any(&e.ip.in6)) + return -EINVAL; + return adtfn(set, &e, &ext, &opt->ext, opt->cmdflags); } @@ -298,6 +314,10 @@ hash_ipport6_uadt(struct ip_set *set, struct nlattr *tb[], if (ret) return ret; + nf_inet_addr_mask_inplace(&e.ip, &h->bitmask); + if (ipv6_addr_any(&e.ip.in6)) + return -EINVAL; + e.port = nla_get_be16(tb[IPSET_ATTR_PORT]); if (tb[IPSET_ATTR_PROTO]) { @@ -356,6 +376,8 @@ static struct ip_set_type hash_ipport_type __read_mostly = { [IPSET_ATTR_PROTO] = { .type = NLA_U8 }, [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 }, [IPSET_ATTR_CADT_FLAGS] = { .type = NLA_U32 }, + [IPSET_ATTR_NETMASK] = { .type = NLA_U8 }, + [IPSET_ATTR_BITMASK] = { .type = NLA_NESTED }, }, .adt_policy = { [IPSET_ATTR_IP] = { .type = NLA_NESTED }, diff --git a/net/netfilter/ipset/ip_set_hash_netnet.c b/net/netfilter/ipset/ip_set_hash_netnet.c index 3d09eefe998a..cdfb78c6e0d3 100644 --- a/net/netfilter/ipset/ip_set_hash_netnet.c +++ b/net/netfilter/ipset/ip_set_hash_netnet.c @@ -23,7 +23,8 @@ #define IPSET_TYPE_REV_MIN 0 /* 1 Forceadd support added */ /* 2 skbinfo support added */ -#define IPSET_TYPE_REV_MAX 3 /* bucketsize, initval support added */ +/* 3 bucketsize, initval support added */ +#define IPSET_TYPE_REV_MAX 4 /* bitmask support added */ MODULE_LICENSE("GPL"); MODULE_AUTHOR("Oliver Smith "); @@ -33,6 +34,8 @@ MODULE_ALIAS("ip_set_hash:net,net"); /* Type specific function prefix */ #define HTYPE hash_netnet #define IP_SET_HASH_WITH_NETS +#define IP_SET_HASH_WITH_NETMASK +#define IP_SET_HASH_WITH_BITMASK #define IPSET_NET_COUNT 2 /* IPv4 variants */ @@ -153,8 +156,8 @@ hash_netnet4_kadt(struct ip_set *set, const struct sk_buff *skb, ip4addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &e.ip[0]); ip4addrptr(skb, opt->flags & IPSET_DIM_TWO_SRC, &e.ip[1]); - e.ip[0] &= ip_set_netmask(e.cidr[0]); - e.ip[1] &= ip_set_netmask(e.cidr[1]); + e.ip[0] &= (ip_set_netmask(e.cidr[0]) & h->bitmask.ip); + e.ip[1] &= (ip_set_netmask(e.cidr[1]) & h->bitmask.ip); return adtfn(set, &e, &ext, &opt->ext, opt->cmdflags); } @@ -213,8 +216,8 @@ hash_netnet4_uadt(struct ip_set *set, struct nlattr *tb[], if (adt == IPSET_TEST || !(tb[IPSET_ATTR_IP_TO] || tb[IPSET_ATTR_IP2_TO])) { - e.ip[0] = htonl(ip & ip_set_hostmask(e.cidr[0])); - e.ip[1] = htonl(ip2_from & ip_set_hostmask(e.cidr[1])); + e.ip[0] = htonl(ip & ntohl(h->bitmask.ip) & ip_set_hostmask(e.cidr[0])); + e.ip[1] = htonl(ip2_from & ntohl(h->bitmask.ip) & ip_set_hostmask(e.cidr[1])); ret = adtfn(set, &e, &ext, &ext, flags); return ip_set_enomatch(ret, flags, adt, set) ? -ret : ip_set_eexist(ret, flags) ? 0 : ret; @@ -404,6 +407,11 @@ hash_netnet6_kadt(struct ip_set *set, const struct sk_buff *skb, ip6_netmask(&e.ip[0], e.cidr[0]); ip6_netmask(&e.ip[1], e.cidr[1]); + nf_inet_addr_mask_inplace(&e.ip[0], &h->bitmask); + nf_inet_addr_mask_inplace(&e.ip[1], &h->bitmask); + if (e.cidr[0] == HOST_MASK && ipv6_addr_any(&e.ip[0].in6)) + return -EINVAL; + return adtfn(set, &e, &ext, &opt->ext, opt->cmdflags); } @@ -414,6 +422,7 @@ hash_netnet6_uadt(struct ip_set *set, struct nlattr *tb[], ipset_adtfn adtfn = set->variant->adt[adt]; struct hash_netnet6_elem e = { }; struct ip_set_ext ext = IP_SET_INIT_UEXT(set); + const struct hash_netnet6 *h = set->data; int ret; if (tb[IPSET_ATTR_LINENO]) @@ -453,6 +462,11 @@ hash_netnet6_uadt(struct ip_set *set, struct nlattr *tb[], ip6_netmask(&e.ip[0], e.cidr[0]); ip6_netmask(&e.ip[1], e.cidr[1]); + nf_inet_addr_mask_inplace(&e.ip[0], &h->bitmask); + nf_inet_addr_mask_inplace(&e.ip[1], &h->bitmask); + if (e.cidr[0] == HOST_MASK && ipv6_addr_any(&e.ip[0].in6)) + return -IPSET_ERR_HASH_ELEM; + if (tb[IPSET_ATTR_CADT_FLAGS]) { u32 cadt_flags = ip_set_get_h32(tb[IPSET_ATTR_CADT_FLAGS]); @@ -484,6 +498,8 @@ static struct ip_set_type hash_netnet_type __read_mostly = { [IPSET_ATTR_RESIZE] = { .type = NLA_U8 }, [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 }, [IPSET_ATTR_CADT_FLAGS] = { .type = NLA_U32 }, + [IPSET_ATTR_NETMASK] = { .type = NLA_U8 }, + [IPSET_ATTR_BITMASK] = { .type = NLA_NESTED }, }, .adt_policy = { [IPSET_ATTR_IP] = { .type = NLA_NESTED }, From 052c82dcdcbb6eb89d0967c309c010cd293076d0 Mon Sep 17 00:00:00 2001 From: Martin KaFai Lau Date: Mon, 28 Nov 2022 23:08:54 -0800 Subject: [PATCH 1333/2002] selftests/bpf: Use if_nametoindex instead of reading the /sys/net/class/*/ifindex When switching netns, the setns_by_fd() is doing dances in mount/umounting the /sys directories. One reason is the tc_redirect.c test is depending on the /sys/net/class/*/ifindex instead of using the if_nametoindex(). if_nametoindex() uses ioctl() to get the ifindex. This patch is to move all /sys/net/class/*/ifindex usages to if_nametoindex(). The current code checks ifindex >= 0 which is incorrect. ifindex > 0 should be checked instead. This patch also stores ifindex_veth_src and ifindex_veth_dst since the latter patch will need them. Signed-off-by: Martin KaFai Lau Signed-off-by: Daniel Borkmann Acked-by: Stanislav Fomichev Link: https://lore.kernel.org/bpf/20221129070900.3142427-2-martin.lau@linux.dev --- .../selftests/bpf/prog_tests/tc_redirect.c | 46 ++++++++----------- 1 file changed, 18 insertions(+), 28 deletions(-) diff --git a/tools/testing/selftests/bpf/prog_tests/tc_redirect.c b/tools/testing/selftests/bpf/prog_tests/tc_redirect.c index cb6a53b3e023..2d85742efdd3 100644 --- a/tools/testing/selftests/bpf/prog_tests/tc_redirect.c +++ b/tools/testing/selftests/bpf/prog_tests/tc_redirect.c @@ -11,12 +11,12 @@ */ #include -#include #include #include #include #include #include +#include #include #include #include @@ -115,7 +115,9 @@ static void netns_setup_namespaces_nofail(const char *verb) } struct netns_setup_result { + int ifindex_veth_src; int ifindex_veth_src_fwd; + int ifindex_veth_dst; int ifindex_veth_dst_fwd; }; @@ -139,27 +141,6 @@ static int get_ifaddr(const char *name, char *ifaddr) return 0; } -static int get_ifindex(const char *name) -{ - char path[PATH_MAX]; - char buf[32]; - FILE *f; - int ret; - - snprintf(path, PATH_MAX, "/sys/class/net/%s/ifindex", name); - f = fopen(path, "r"); - if (!ASSERT_OK_PTR(f, path)) - return -1; - - ret = fread(buf, 1, sizeof(buf), f); - if (!ASSERT_GT(ret, 0, "fread ifindex")) { - fclose(f); - return -1; - } - fclose(f); - return atoi(buf); -} - #define SYS(fmt, ...) \ ({ \ char cmd[1024]; \ @@ -182,11 +163,20 @@ static int netns_setup_links_and_routes(struct netns_setup_result *result) if (get_ifaddr("veth_src_fwd", veth_src_fwd_addr)) goto fail; - result->ifindex_veth_src_fwd = get_ifindex("veth_src_fwd"); - if (result->ifindex_veth_src_fwd < 0) + result->ifindex_veth_src = if_nametoindex("veth_src"); + if (!ASSERT_GT(result->ifindex_veth_src, 0, "ifindex_veth_src")) goto fail; - result->ifindex_veth_dst_fwd = get_ifindex("veth_dst_fwd"); - if (result->ifindex_veth_dst_fwd < 0) + + result->ifindex_veth_src_fwd = if_nametoindex("veth_src_fwd"); + if (!ASSERT_GT(result->ifindex_veth_src_fwd, 0, "ifindex_veth_src_fwd")) + goto fail; + + result->ifindex_veth_dst = if_nametoindex("veth_dst"); + if (!ASSERT_GT(result->ifindex_veth_dst, 0, "ifindex_veth_dst")) + goto fail; + + result->ifindex_veth_dst_fwd = if_nametoindex("veth_dst_fwd"); + if (!ASSERT_GT(result->ifindex_veth_dst_fwd, 0, "ifindex_veth_dst_fwd")) goto fail; SYS("ip link set veth_src netns " NS_SRC); @@ -1034,8 +1024,8 @@ static void test_tc_redirect_peer_l3(struct netns_setup_result *setup_result) if (!ASSERT_OK_PTR(skel, "test_tc_peer__open")) goto fail; - ifindex = get_ifindex("tun_fwd"); - if (!ASSERT_GE(ifindex, 0, "get_ifindex tun_fwd")) + ifindex = if_nametoindex("tun_fwd"); + if (!ASSERT_GT(ifindex, 0, "if_indextoname tun_fwd")) goto fail; skel->rodata->IFINDEX_SRC = ifindex; From 57d0863f1d2812da543ef49172f60d6aa14bedcf Mon Sep 17 00:00:00 2001 From: Martin KaFai Lau Date: Mon, 28 Nov 2022 23:08:55 -0800 Subject: [PATCH 1334/2002] selftests/bpf: Avoid pinning bpf prog in the tc_redirect_dtime test This patch removes the need to pin prog in the tc_redirect_dtime test by directly using the bpf_tc_hook_create() and bpf_tc_attach(). The clsact qdisc will go away together with the test netns, so no need to do bpf_tc_hook_destroy(). Signed-off-by: Martin KaFai Lau Signed-off-by: Daniel Borkmann Acked-by: Stanislav Fomichev Link: https://lore.kernel.org/bpf/20221129070900.3142427-3-martin.lau@linux.dev --- .../selftests/bpf/prog_tests/tc_redirect.c | 149 ++++++++++++------ 1 file changed, 100 insertions(+), 49 deletions(-) diff --git a/tools/testing/selftests/bpf/prog_tests/tc_redirect.c b/tools/testing/selftests/bpf/prog_tests/tc_redirect.c index 2d85742efdd3..690102f1ceda 100644 --- a/tools/testing/selftests/bpf/prog_tests/tc_redirect.c +++ b/tools/testing/selftests/bpf/prog_tests/tc_redirect.c @@ -250,6 +250,56 @@ fail: return -1; } +static int qdisc_clsact_create(struct bpf_tc_hook *qdisc_hook, int ifindex) +{ + char err_str[128], ifname[16]; + int err; + + qdisc_hook->ifindex = ifindex; + qdisc_hook->attach_point = BPF_TC_INGRESS | BPF_TC_EGRESS; + err = bpf_tc_hook_create(qdisc_hook); + snprintf(err_str, sizeof(err_str), + "qdisc add dev %s clsact", + if_indextoname(qdisc_hook->ifindex, ifname) ? : ""); + err_str[sizeof(err_str) - 1] = 0; + ASSERT_OK(err, err_str); + + return err; +} + +static int xgress_filter_add(struct bpf_tc_hook *qdisc_hook, + enum bpf_tc_attach_point xgress, + const struct bpf_program *prog, int priority) +{ + LIBBPF_OPTS(bpf_tc_opts, tc_attach); + char err_str[128], ifname[16]; + int err; + + qdisc_hook->attach_point = xgress; + tc_attach.prog_fd = bpf_program__fd(prog); + tc_attach.priority = priority; + err = bpf_tc_attach(qdisc_hook, &tc_attach); + snprintf(err_str, sizeof(err_str), + "filter add dev %s %s prio %d bpf da %s", + if_indextoname(qdisc_hook->ifindex, ifname) ? : "", + xgress == BPF_TC_INGRESS ? "ingress" : "egress", + priority, bpf_program__name(prog)); + err_str[sizeof(err_str) - 1] = 0; + ASSERT_OK(err, err_str); + + return err; +} + +#define QDISC_CLSACT_CREATE(qdisc_hook, ifindex) ({ \ + if ((err = qdisc_clsact_create(qdisc_hook, ifindex))) \ + goto fail; \ +}) + +#define XGRESS_FILTER_ADD(qdisc_hook, xgress, prog, priority) ({ \ + if ((err = xgress_filter_add(qdisc_hook, xgress, prog, priority))) \ + goto fail; \ +}) + static int netns_load_bpf(void) { SYS("tc qdisc add dev veth_src_fwd clsact"); @@ -489,78 +539,79 @@ done: close(client_fd); } -static int netns_load_dtime_bpf(struct test_tc_dtime *skel) +static int netns_load_dtime_bpf(struct test_tc_dtime *skel, + const struct netns_setup_result *setup_result) { + LIBBPF_OPTS(bpf_tc_hook, qdisc_veth_src_fwd); + LIBBPF_OPTS(bpf_tc_hook, qdisc_veth_dst_fwd); + LIBBPF_OPTS(bpf_tc_hook, qdisc_veth_src); + LIBBPF_OPTS(bpf_tc_hook, qdisc_veth_dst); struct nstoken *nstoken; - -#define PIN_FNAME(__file) "/sys/fs/bpf/" #__file -#define PIN(__prog) ({ \ - int err = bpf_program__pin(skel->progs.__prog, PIN_FNAME(__prog)); \ - if (!ASSERT_OK(err, "pin " #__prog)) \ - goto fail; \ - }) + int err; /* setup ns_src tc progs */ nstoken = open_netns(NS_SRC); if (!ASSERT_OK_PTR(nstoken, "setns " NS_SRC)) return -1; - PIN(egress_host); - PIN(ingress_host); - SYS("tc qdisc add dev veth_src clsact"); - SYS("tc filter add dev veth_src ingress bpf da object-pinned " - PIN_FNAME(ingress_host)); - SYS("tc filter add dev veth_src egress bpf da object-pinned " - PIN_FNAME(egress_host)); + /* tc qdisc add dev veth_src clsact */ + QDISC_CLSACT_CREATE(&qdisc_veth_src, setup_result->ifindex_veth_src); + /* tc filter add dev veth_src ingress bpf da ingress_host */ + XGRESS_FILTER_ADD(&qdisc_veth_src, BPF_TC_INGRESS, skel->progs.ingress_host, 0); + /* tc filter add dev veth_src egress bpf da egress_host */ + XGRESS_FILTER_ADD(&qdisc_veth_src, BPF_TC_EGRESS, skel->progs.egress_host, 0); close_netns(nstoken); /* setup ns_dst tc progs */ nstoken = open_netns(NS_DST); if (!ASSERT_OK_PTR(nstoken, "setns " NS_DST)) return -1; - PIN(egress_host); - PIN(ingress_host); - SYS("tc qdisc add dev veth_dst clsact"); - SYS("tc filter add dev veth_dst ingress bpf da object-pinned " - PIN_FNAME(ingress_host)); - SYS("tc filter add dev veth_dst egress bpf da object-pinned " - PIN_FNAME(egress_host)); + /* tc qdisc add dev veth_dst clsact */ + QDISC_CLSACT_CREATE(&qdisc_veth_dst, setup_result->ifindex_veth_dst); + /* tc filter add dev veth_dst ingress bpf da ingress_host */ + XGRESS_FILTER_ADD(&qdisc_veth_dst, BPF_TC_INGRESS, skel->progs.ingress_host, 0); + /* tc filter add dev veth_dst egress bpf da egress_host */ + XGRESS_FILTER_ADD(&qdisc_veth_dst, BPF_TC_EGRESS, skel->progs.egress_host, 0); close_netns(nstoken); /* setup ns_fwd tc progs */ nstoken = open_netns(NS_FWD); if (!ASSERT_OK_PTR(nstoken, "setns " NS_FWD)) return -1; - PIN(ingress_fwdns_prio100); - PIN(egress_fwdns_prio100); - PIN(ingress_fwdns_prio101); - PIN(egress_fwdns_prio101); - SYS("tc qdisc add dev veth_dst_fwd clsact"); - SYS("tc filter add dev veth_dst_fwd ingress prio 100 bpf da object-pinned " - PIN_FNAME(ingress_fwdns_prio100)); - SYS("tc filter add dev veth_dst_fwd ingress prio 101 bpf da object-pinned " - PIN_FNAME(ingress_fwdns_prio101)); - SYS("tc filter add dev veth_dst_fwd egress prio 100 bpf da object-pinned " - PIN_FNAME(egress_fwdns_prio100)); - SYS("tc filter add dev veth_dst_fwd egress prio 101 bpf da object-pinned " - PIN_FNAME(egress_fwdns_prio101)); - SYS("tc qdisc add dev veth_src_fwd clsact"); - SYS("tc filter add dev veth_src_fwd ingress prio 100 bpf da object-pinned " - PIN_FNAME(ingress_fwdns_prio100)); - SYS("tc filter add dev veth_src_fwd ingress prio 101 bpf da object-pinned " - PIN_FNAME(ingress_fwdns_prio101)); - SYS("tc filter add dev veth_src_fwd egress prio 100 bpf da object-pinned " - PIN_FNAME(egress_fwdns_prio100)); - SYS("tc filter add dev veth_src_fwd egress prio 101 bpf da object-pinned " - PIN_FNAME(egress_fwdns_prio101)); + /* tc qdisc add dev veth_dst_fwd clsact */ + QDISC_CLSACT_CREATE(&qdisc_veth_dst_fwd, setup_result->ifindex_veth_dst_fwd); + /* tc filter add dev veth_dst_fwd ingress prio 100 bpf da ingress_fwdns_prio100 */ + XGRESS_FILTER_ADD(&qdisc_veth_dst_fwd, BPF_TC_INGRESS, + skel->progs.ingress_fwdns_prio100, 100); + /* tc filter add dev veth_dst_fwd ingress prio 101 bpf da ingress_fwdns_prio101 */ + XGRESS_FILTER_ADD(&qdisc_veth_dst_fwd, BPF_TC_INGRESS, + skel->progs.ingress_fwdns_prio101, 101); + /* tc filter add dev veth_dst_fwd egress prio 100 bpf da egress_fwdns_prio100 */ + XGRESS_FILTER_ADD(&qdisc_veth_dst_fwd, BPF_TC_EGRESS, + skel->progs.egress_fwdns_prio100, 100); + /* tc filter add dev veth_dst_fwd egress prio 101 bpf da egress_fwdns_prio101 */ + XGRESS_FILTER_ADD(&qdisc_veth_dst_fwd, BPF_TC_EGRESS, + skel->progs.egress_fwdns_prio101, 101); + + /* tc qdisc add dev veth_src_fwd clsact */ + QDISC_CLSACT_CREATE(&qdisc_veth_src_fwd, setup_result->ifindex_veth_src_fwd); + /* tc filter add dev veth_src_fwd ingress prio 100 bpf da ingress_fwdns_prio100 */ + XGRESS_FILTER_ADD(&qdisc_veth_src_fwd, BPF_TC_INGRESS, + skel->progs.ingress_fwdns_prio100, 100); + /* tc filter add dev veth_src_fwd ingress prio 101 bpf da ingress_fwdns_prio101 */ + XGRESS_FILTER_ADD(&qdisc_veth_src_fwd, BPF_TC_INGRESS, + skel->progs.ingress_fwdns_prio101, 101); + /* tc filter add dev veth_src_fwd egress prio 100 bpf da egress_fwdns_prio100 */ + XGRESS_FILTER_ADD(&qdisc_veth_src_fwd, BPF_TC_EGRESS, + skel->progs.egress_fwdns_prio100, 100); + /* tc filter add dev veth_src_fwd egress prio 101 bpf da egress_fwdns_prio101 */ + XGRESS_FILTER_ADD(&qdisc_veth_src_fwd, BPF_TC_EGRESS, + skel->progs.egress_fwdns_prio101, 101); close_netns(nstoken); - -#undef PIN - return 0; fail: close_netns(nstoken); - return -1; + return err; } enum { @@ -736,7 +787,7 @@ static void test_tc_redirect_dtime(struct netns_setup_result *setup_result) if (!ASSERT_OK(err, "test_tc_dtime__load")) goto done; - if (netns_load_dtime_bpf(skel)) + if (netns_load_dtime_bpf(skel, setup_result)) goto done; nstoken = open_netns(NS_FWD); From f1b73577bb3c5b23872fde2907386635ea726d6d Mon Sep 17 00:00:00 2001 From: Martin KaFai Lau Date: Mon, 28 Nov 2022 23:08:56 -0800 Subject: [PATCH 1335/2002] selftests/bpf: Avoid pinning bpf prog in the tc_redirect_peer_l3 test This patch removes the need to pin prog in the tc_redirect_peer_l3 test by directly using the bpf_tc_hook_create() and bpf_tc_attach(). The clsact qdisc will go away together with the test netns, so no need to do bpf_tc_hook_destroy(). Signed-off-by: Martin KaFai Lau Signed-off-by: Daniel Borkmann Acked-by: Stanislav Fomichev Link: https://lore.kernel.org/bpf/20221129070900.3142427-4-martin.lau@linux.dev --- .../selftests/bpf/prog_tests/tc_redirect.c | 32 +++++++------------ 1 file changed, 12 insertions(+), 20 deletions(-) diff --git a/tools/testing/selftests/bpf/prog_tests/tc_redirect.c b/tools/testing/selftests/bpf/prog_tests/tc_redirect.c index 690102f1ceda..cefde6491749 100644 --- a/tools/testing/selftests/bpf/prog_tests/tc_redirect.c +++ b/tools/testing/selftests/bpf/prog_tests/tc_redirect.c @@ -1032,6 +1032,8 @@ static int tun_relay_loop(int src_fd, int target_fd) static void test_tc_redirect_peer_l3(struct netns_setup_result *setup_result) { + LIBBPF_OPTS(bpf_tc_hook, qdisc_tun_fwd); + LIBBPF_OPTS(bpf_tc_hook, qdisc_veth_dst_fwd); struct test_tc_peer *skel = NULL; struct nstoken *nstoken = NULL; int err; @@ -1086,31 +1088,21 @@ static void test_tc_redirect_peer_l3(struct netns_setup_result *setup_result) if (!ASSERT_OK(err, "test_tc_peer__load")) goto fail; - err = bpf_program__pin(skel->progs.tc_src_l3, SRC_PROG_PIN_FILE); - if (!ASSERT_OK(err, "pin " SRC_PROG_PIN_FILE)) - goto fail; - - err = bpf_program__pin(skel->progs.tc_dst_l3, DST_PROG_PIN_FILE); - if (!ASSERT_OK(err, "pin " DST_PROG_PIN_FILE)) - goto fail; - - err = bpf_program__pin(skel->progs.tc_chk, CHK_PROG_PIN_FILE); - if (!ASSERT_OK(err, "pin " CHK_PROG_PIN_FILE)) - goto fail; - /* Load "tc_src_l3" to the tun_fwd interface to redirect packets * towards dst, and "tc_dst" to redirect packets * and "tc_chk" on veth_dst_fwd to drop non-redirected packets. */ - SYS("tc qdisc add dev tun_fwd clsact"); - SYS("tc filter add dev tun_fwd ingress bpf da object-pinned " - SRC_PROG_PIN_FILE); + /* tc qdisc add dev tun_fwd clsact */ + QDISC_CLSACT_CREATE(&qdisc_tun_fwd, ifindex); + /* tc filter add dev tun_fwd ingress bpf da tc_src_l3 */ + XGRESS_FILTER_ADD(&qdisc_tun_fwd, BPF_TC_INGRESS, skel->progs.tc_src_l3, 0); - SYS("tc qdisc add dev veth_dst_fwd clsact"); - SYS("tc filter add dev veth_dst_fwd ingress bpf da object-pinned " - DST_PROG_PIN_FILE); - SYS("tc filter add dev veth_dst_fwd egress bpf da object-pinned " - CHK_PROG_PIN_FILE); + /* tc qdisc add dev veth_dst_fwd clsact */ + QDISC_CLSACT_CREATE(&qdisc_veth_dst_fwd, setup_result->ifindex_veth_dst_fwd); + /* tc filter add dev veth_dst_fwd ingress bpf da tc_dst_l3 */ + XGRESS_FILTER_ADD(&qdisc_veth_dst_fwd, BPF_TC_INGRESS, skel->progs.tc_dst_l3, 0); + /* tc filter add dev veth_dst_fwd egress bpf da tc_chk */ + XGRESS_FILTER_ADD(&qdisc_veth_dst_fwd, BPF_TC_EGRESS, skel->progs.tc_chk, 0); /* Setup route and neigh tables */ SYS("ip -netns " NS_SRC " addr add dev tun_src " IP4_TUN_SRC "/24"); From 5dc42a7fc2866a56bc5616babace0a252458fe01 Mon Sep 17 00:00:00 2001 From: Martin KaFai Lau Date: Mon, 28 Nov 2022 23:08:57 -0800 Subject: [PATCH 1336/2002] selftests/bpf: Avoid pinning bpf prog in the netns_load_bpf() callers This patch removes the need to pin prog in the remaining tests in tc_redirect.c by directly using the bpf_tc_hook_create() and bpf_tc_attach(). The clsact qdisc will go away together with the test netns, so no need to do bpf_tc_hook_destroy(). Signed-off-by: Martin KaFai Lau Signed-off-by: Daniel Borkmann Acked-by: Stanislav Fomichev Link: https://lore.kernel.org/bpf/20221129070900.3142427-5-martin.lau@linux.dev --- .../selftests/bpf/prog_tests/tc_redirect.c | 81 ++++++------------- 1 file changed, 26 insertions(+), 55 deletions(-) diff --git a/tools/testing/selftests/bpf/prog_tests/tc_redirect.c b/tools/testing/selftests/bpf/prog_tests/tc_redirect.c index cefde6491749..6f800381f924 100644 --- a/tools/testing/selftests/bpf/prog_tests/tc_redirect.c +++ b/tools/testing/selftests/bpf/prog_tests/tc_redirect.c @@ -59,10 +59,6 @@ #define IFADDR_STR_LEN 18 #define PING_ARGS "-i 0.2 -c 3 -w 10 -q" -#define SRC_PROG_PIN_FILE "/sys/fs/bpf/test_tc_src" -#define DST_PROG_PIN_FILE "/sys/fs/bpf/test_tc_dst" -#define CHK_PROG_PIN_FILE "/sys/fs/bpf/test_tc_chk" - #define TIMEOUT_MILLIS 10000 #define NSEC_PER_SEC 1000000000ULL @@ -300,19 +296,28 @@ static int xgress_filter_add(struct bpf_tc_hook *qdisc_hook, goto fail; \ }) -static int netns_load_bpf(void) +static int netns_load_bpf(const struct bpf_program *src_prog, + const struct bpf_program *dst_prog, + const struct bpf_program *chk_prog, + const struct netns_setup_result *setup_result) { - SYS("tc qdisc add dev veth_src_fwd clsact"); - SYS("tc filter add dev veth_src_fwd ingress bpf da object-pinned " - SRC_PROG_PIN_FILE); - SYS("tc filter add dev veth_src_fwd egress bpf da object-pinned " - CHK_PROG_PIN_FILE); + LIBBPF_OPTS(bpf_tc_hook, qdisc_veth_src_fwd); + LIBBPF_OPTS(bpf_tc_hook, qdisc_veth_dst_fwd); + int err; - SYS("tc qdisc add dev veth_dst_fwd clsact"); - SYS("tc filter add dev veth_dst_fwd ingress bpf da object-pinned " - DST_PROG_PIN_FILE); - SYS("tc filter add dev veth_dst_fwd egress bpf da object-pinned " - CHK_PROG_PIN_FILE); + /* tc qdisc add dev veth_src_fwd clsact */ + QDISC_CLSACT_CREATE(&qdisc_veth_src_fwd, setup_result->ifindex_veth_src_fwd); + /* tc filter add dev veth_src_fwd ingress bpf da src_prog */ + XGRESS_FILTER_ADD(&qdisc_veth_src_fwd, BPF_TC_INGRESS, src_prog, 0); + /* tc filter add dev veth_src_fwd egress bpf da chk_prog */ + XGRESS_FILTER_ADD(&qdisc_veth_src_fwd, BPF_TC_EGRESS, chk_prog, 0); + + /* tc qdisc add dev veth_dst_fwd clsact */ + QDISC_CLSACT_CREATE(&qdisc_veth_dst_fwd, setup_result->ifindex_veth_dst_fwd); + /* tc filter add dev veth_dst_fwd ingress bpf da dst_prog */ + XGRESS_FILTER_ADD(&qdisc_veth_dst_fwd, BPF_TC_INGRESS, dst_prog, 0); + /* tc filter add dev veth_dst_fwd egress bpf da chk_prog */ + XGRESS_FILTER_ADD(&qdisc_veth_dst_fwd, BPF_TC_EGRESS, chk_prog, 0); return 0; fail: @@ -829,7 +834,6 @@ static void test_tc_redirect_neigh_fib(struct netns_setup_result *setup_result) { struct nstoken *nstoken = NULL; struct test_tc_neigh_fib *skel = NULL; - int err; nstoken = open_netns(NS_FWD); if (!ASSERT_OK_PTR(nstoken, "setns fwd")) @@ -842,19 +846,8 @@ static void test_tc_redirect_neigh_fib(struct netns_setup_result *setup_result) if (!ASSERT_OK(test_tc_neigh_fib__load(skel), "test_tc_neigh_fib__load")) goto done; - err = bpf_program__pin(skel->progs.tc_src, SRC_PROG_PIN_FILE); - if (!ASSERT_OK(err, "pin " SRC_PROG_PIN_FILE)) - goto done; - - err = bpf_program__pin(skel->progs.tc_chk, CHK_PROG_PIN_FILE); - if (!ASSERT_OK(err, "pin " CHK_PROG_PIN_FILE)) - goto done; - - err = bpf_program__pin(skel->progs.tc_dst, DST_PROG_PIN_FILE); - if (!ASSERT_OK(err, "pin " DST_PROG_PIN_FILE)) - goto done; - - if (netns_load_bpf()) + if (netns_load_bpf(skel->progs.tc_src, skel->progs.tc_dst, + skel->progs.tc_chk, setup_result)) goto done; /* bpf_fib_lookup() checks if forwarding is enabled */ @@ -890,19 +883,8 @@ static void test_tc_redirect_neigh(struct netns_setup_result *setup_result) if (!ASSERT_OK(err, "test_tc_neigh__load")) goto done; - err = bpf_program__pin(skel->progs.tc_src, SRC_PROG_PIN_FILE); - if (!ASSERT_OK(err, "pin " SRC_PROG_PIN_FILE)) - goto done; - - err = bpf_program__pin(skel->progs.tc_chk, CHK_PROG_PIN_FILE); - if (!ASSERT_OK(err, "pin " CHK_PROG_PIN_FILE)) - goto done; - - err = bpf_program__pin(skel->progs.tc_dst, DST_PROG_PIN_FILE); - if (!ASSERT_OK(err, "pin " DST_PROG_PIN_FILE)) - goto done; - - if (netns_load_bpf()) + if (netns_load_bpf(skel->progs.tc_src, skel->progs.tc_dst, + skel->progs.tc_chk, setup_result)) goto done; if (!ASSERT_OK(set_forwarding(false), "disable forwarding")) @@ -937,19 +919,8 @@ static void test_tc_redirect_peer(struct netns_setup_result *setup_result) if (!ASSERT_OK(err, "test_tc_peer__load")) goto done; - err = bpf_program__pin(skel->progs.tc_src, SRC_PROG_PIN_FILE); - if (!ASSERT_OK(err, "pin " SRC_PROG_PIN_FILE)) - goto done; - - err = bpf_program__pin(skel->progs.tc_chk, CHK_PROG_PIN_FILE); - if (!ASSERT_OK(err, "pin " CHK_PROG_PIN_FILE)) - goto done; - - err = bpf_program__pin(skel->progs.tc_dst, DST_PROG_PIN_FILE); - if (!ASSERT_OK(err, "pin " DST_PROG_PIN_FILE)) - goto done; - - if (netns_load_bpf()) + if (netns_load_bpf(skel->progs.tc_src, skel->progs.tc_dst, + skel->progs.tc_chk, setup_result)) goto done; if (!ASSERT_OK(set_forwarding(false), "disable forwarding")) From 3084097c369c57c9bbd81e5bf47b36b6b27390fc Mon Sep 17 00:00:00 2001 From: Martin KaFai Lau Date: Mon, 28 Nov 2022 23:08:58 -0800 Subject: [PATCH 1337/2002] selftests/bpf: Remove the "/sys" mount and umount dance in {open,close}_netns The previous patches have removed the need to do the mount and umount dance when switching netns. In particular: * Avoid remounting /sys/fs/bpf to have a clean start * Avoid remounting /sys to get a ifindex of a particular netns This patch can finally remove the mount and umount dance in {open,close}_netns which is unnecessarily complicated and error-prone. Signed-off-by: Martin KaFai Lau Signed-off-by: Daniel Borkmann Acked-by: Stanislav Fomichev Link: https://lore.kernel.org/bpf/20221129070900.3142427-6-martin.lau@linux.dev --- tools/testing/selftests/bpf/network_helpers.c | 51 ++----------------- 1 file changed, 5 insertions(+), 46 deletions(-) diff --git a/tools/testing/selftests/bpf/network_helpers.c b/tools/testing/selftests/bpf/network_helpers.c index 1f37adff7632..01de33191226 100644 --- a/tools/testing/selftests/bpf/network_helpers.c +++ b/tools/testing/selftests/bpf/network_helpers.c @@ -390,49 +390,6 @@ struct nstoken { int orig_netns_fd; }; -static int setns_by_fd(int nsfd) -{ - int err; - - err = setns(nsfd, CLONE_NEWNET); - close(nsfd); - - if (!ASSERT_OK(err, "setns")) - return err; - - /* Switch /sys to the new namespace so that e.g. /sys/class/net - * reflects the devices in the new namespace. - */ - err = unshare(CLONE_NEWNS); - if (!ASSERT_OK(err, "unshare")) - return err; - - /* Make our /sys mount private, so the following umount won't - * trigger the global umount in case it's shared. - */ - err = mount("none", "/sys", NULL, MS_PRIVATE, NULL); - if (!ASSERT_OK(err, "remount private /sys")) - return err; - - err = umount2("/sys", MNT_DETACH); - if (!ASSERT_OK(err, "umount2 /sys")) - return err; - - err = mount("sysfs", "/sys", "sysfs", 0, NULL); - if (!ASSERT_OK(err, "mount /sys")) - return err; - - err = mount("bpffs", "/sys/fs/bpf", "bpf", 0, NULL); - if (!ASSERT_OK(err, "mount /sys/fs/bpf")) - return err; - - err = mount("debugfs", "/sys/kernel/debug", "debugfs", 0, NULL); - if (!ASSERT_OK(err, "mount /sys/kernel/debug")) - return err; - - return 0; -} - struct nstoken *open_netns(const char *name) { int nsfd; @@ -453,8 +410,9 @@ struct nstoken *open_netns(const char *name) if (!ASSERT_GE(nsfd, 0, "open netns fd")) goto fail; - err = setns_by_fd(nsfd); - if (!ASSERT_OK(err, "setns_by_fd")) + err = setns(nsfd, CLONE_NEWNET); + close(nsfd); + if (!ASSERT_OK(err, "setns")) goto fail; return token; @@ -465,6 +423,7 @@ fail: void close_netns(struct nstoken *token) { - ASSERT_OK(setns_by_fd(token->orig_netns_fd), "setns_by_fd"); + ASSERT_OK(setns(token->orig_netns_fd, CLONE_NEWNET), "setns"); + close(token->orig_netns_fd); free(token); } From 9b6a7773973707bad13f6c8b2a27e93ef4d46182 Mon Sep 17 00:00:00 2001 From: Martin KaFai Lau Date: Mon, 28 Nov 2022 23:08:59 -0800 Subject: [PATCH 1338/2002] selftests/bpf: Remove serial from tests using {open,close}_netns After removing the mount/umount dance from {open,close}_netns() in the pervious patch, "serial_" can be removed from the tests using {open,close}_netns(). Signed-off-by: Martin KaFai Lau Signed-off-by: Daniel Borkmann Acked-by: Stanislav Fomichev Link: https://lore.kernel.org/bpf/20221129070900.3142427-7-martin.lau@linux.dev --- tools/testing/selftests/bpf/prog_tests/empty_skb.c | 2 +- tools/testing/selftests/bpf/prog_tests/tc_redirect.c | 2 +- tools/testing/selftests/bpf/prog_tests/test_tunnel.c | 2 +- tools/testing/selftests/bpf/prog_tests/xdp_do_redirect.c | 2 +- tools/testing/selftests/bpf/prog_tests/xdp_synproxy.c | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/tools/testing/selftests/bpf/prog_tests/empty_skb.c b/tools/testing/selftests/bpf/prog_tests/empty_skb.c index 0613f3bb8b5e..32dd731e9070 100644 --- a/tools/testing/selftests/bpf/prog_tests/empty_skb.c +++ b/tools/testing/selftests/bpf/prog_tests/empty_skb.c @@ -9,7 +9,7 @@ goto out; \ }) -void serial_test_empty_skb(void) +void test_empty_skb(void) { LIBBPF_OPTS(bpf_test_run_opts, tattr); struct empty_skb *bpf_obj = NULL; diff --git a/tools/testing/selftests/bpf/prog_tests/tc_redirect.c b/tools/testing/selftests/bpf/prog_tests/tc_redirect.c index 6f800381f924..bca5e6839ac4 100644 --- a/tools/testing/selftests/bpf/prog_tests/tc_redirect.c +++ b/tools/testing/selftests/bpf/prog_tests/tc_redirect.c @@ -1138,7 +1138,7 @@ static void *test_tc_redirect_run_tests(void *arg) return NULL; } -void serial_test_tc_redirect(void) +void test_tc_redirect(void) { pthread_t test_thread; int err; diff --git a/tools/testing/selftests/bpf/prog_tests/test_tunnel.c b/tools/testing/selftests/bpf/prog_tests/test_tunnel.c index eea274110267..07ad457f3370 100644 --- a/tools/testing/selftests/bpf/prog_tests/test_tunnel.c +++ b/tools/testing/selftests/bpf/prog_tests/test_tunnel.c @@ -421,7 +421,7 @@ static void *test_tunnel_run_tests(void *arg) return NULL; } -void serial_test_tunnel(void) +void test_tunnel(void) { pthread_t test_thread; int err; diff --git a/tools/testing/selftests/bpf/prog_tests/xdp_do_redirect.c b/tools/testing/selftests/bpf/prog_tests/xdp_do_redirect.c index 9ac6f6a268db..a50971c6cf4a 100644 --- a/tools/testing/selftests/bpf/prog_tests/xdp_do_redirect.c +++ b/tools/testing/selftests/bpf/prog_tests/xdp_do_redirect.c @@ -85,7 +85,7 @@ static void test_max_pkt_size(int fd) } #define NUM_PKTS 10000 -void serial_test_xdp_do_redirect(void) +void test_xdp_do_redirect(void) { int err, xdp_prog_fd, tc_prog_fd, ifindex_src, ifindex_dst; char data[sizeof(pkt_udp) + sizeof(__u32)]; diff --git a/tools/testing/selftests/bpf/prog_tests/xdp_synproxy.c b/tools/testing/selftests/bpf/prog_tests/xdp_synproxy.c index 13daa3746064..c72083885b6d 100644 --- a/tools/testing/selftests/bpf/prog_tests/xdp_synproxy.c +++ b/tools/testing/selftests/bpf/prog_tests/xdp_synproxy.c @@ -174,7 +174,7 @@ out: system("ip netns del synproxy"); } -void serial_test_xdp_synproxy(void) +void test_xdp_synproxy(void) { if (test__start_subtest("xdp")) test_synproxy(true); From 443f216448ab5ddd1b4d08ad6c9b69628ac25adf Mon Sep 17 00:00:00 2001 From: Martin KaFai Lau Date: Mon, 28 Nov 2022 23:09:00 -0800 Subject: [PATCH 1339/2002] selftests/bpf: Avoid pinning prog when attaching to tc ingress in btf_skc_cls_ingress This patch removes the need to pin prog when attaching to tc ingress in the btf_skc_cls_ingress test. Instead, directly use the bpf_tc_hook_create() and bpf_tc_attach(). The qdisc clsact will go away together with the netns, so no need to bpf_tc_hook_destroy(). Signed-off-by: Martin KaFai Lau Signed-off-by: Daniel Borkmann Acked-by: Stanislav Fomichev Link: https://lore.kernel.org/bpf/20221129070900.3142427-8-martin.lau@linux.dev --- .../bpf/prog_tests/btf_skc_cls_ingress.c | 25 ++++++++----------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/tools/testing/selftests/bpf/prog_tests/btf_skc_cls_ingress.c b/tools/testing/selftests/bpf/prog_tests/btf_skc_cls_ingress.c index 7a277035c275..ef4d6a3ae423 100644 --- a/tools/testing/selftests/bpf/prog_tests/btf_skc_cls_ingress.c +++ b/tools/testing/selftests/bpf/prog_tests/btf_skc_cls_ingress.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include @@ -20,10 +21,12 @@ static struct test_btf_skc_cls_ingress *skel; static struct sockaddr_in6 srv_sa6; static __u32 duration; -#define PROG_PIN_FILE "/sys/fs/bpf/btf_skc_cls_ingress" - static int prepare_netns(void) { + LIBBPF_OPTS(bpf_tc_hook, qdisc_lo, .attach_point = BPF_TC_INGRESS); + LIBBPF_OPTS(bpf_tc_opts, tc_attach, + .prog_fd = bpf_program__fd(skel->progs.cls_ingress)); + if (CHECK(unshare(CLONE_NEWNET), "create netns", "unshare(CLONE_NEWNET): %s (%d)", strerror(errno), errno)) @@ -33,12 +36,12 @@ static int prepare_netns(void) "ip link set dev lo up", "failed\n")) return -1; - if (CHECK(system("tc qdisc add dev lo clsact"), - "tc qdisc add dev lo clsact", "failed\n")) + qdisc_lo.ifindex = if_nametoindex("lo"); + if (!ASSERT_OK(bpf_tc_hook_create(&qdisc_lo), "qdisc add dev lo clsact")) return -1; - if (CHECK(system("tc filter add dev lo ingress bpf direct-action object-pinned " PROG_PIN_FILE), - "install tc cls-prog at ingress", "failed\n")) + if (!ASSERT_OK(bpf_tc_attach(&qdisc_lo, &tc_attach), + "filter add dev lo ingress")) return -1; /* Ensure 20 bytes options (i.e. in total 40 bytes tcp header) for the @@ -195,19 +198,12 @@ static struct test tests[] = { void test_btf_skc_cls_ingress(void) { - int i, err; + int i; skel = test_btf_skc_cls_ingress__open_and_load(); if (CHECK(!skel, "test_btf_skc_cls_ingress__open_and_load", "failed\n")) return; - err = bpf_program__pin(skel->progs.cls_ingress, PROG_PIN_FILE); - if (CHECK(err, "bpf_program__pin", - "cannot pin bpf prog to %s. err:%d\n", PROG_PIN_FILE, err)) { - test_btf_skc_cls_ingress__destroy(skel); - return; - } - for (i = 0; i < ARRAY_SIZE(tests); i++) { if (!test__start_subtest(tests[i].desc)) continue; @@ -221,6 +217,5 @@ void test_btf_skc_cls_ingress(void) reset_test(); } - bpf_program__unpin(skel->progs.cls_ingress, PROG_PIN_FILE); test_btf_skc_cls_ingress__destroy(skel); } From b42693415b86f608049cf1b4870adc1dc65e58b0 Mon Sep 17 00:00:00 2001 From: Andrii Nakryiko Date: Wed, 30 Nov 2022 12:00:12 -0800 Subject: [PATCH 1340/2002] libbpf: Avoid enum forward-declarations in public API in C++ mode C++ enum forward declarations are fundamentally not compatible with pure C enum definitions, and so libbpf's use of `enum bpf_stats_type;` forward declaration in libbpf/bpf.h public API header is causing C++ compilation issues. More details can be found in [0], but it comes down to C++ supporting enum forward declaration only with explicitly specified backing type: enum bpf_stats_type: int; In C (and I believe it's a GCC extension also), such forward declaration is simply: enum bpf_stats_type; Further, in Linux UAPI this enum is defined in pure C way: enum bpf_stats_type { BPF_STATS_RUN_TIME = 0; } And even though in both cases backing type is int, which can be confirmed by looking at DWARF information, for C++ compiler actual enum definition and forward declaration are incompatible. To eliminate this problem, for C++ mode define input argument as int, which makes enum unnecessary in libbpf public header. This solves the issue and as demonstrated by next patch doesn't cause any unwanted compiler warnings, at least with default warnings setting. [0] https://stackoverflow.com/questions/42766839/c11-enum-forward-causes-underlying-type-mismatch [1] Closes: https://github.com/libbpf/libbpf/issues/249 Signed-off-by: Andrii Nakryiko Signed-off-by: Daniel Borkmann Link: https://lore.kernel.org/bpf/20221130200013.2997831-1-andrii@kernel.org --- tools/lib/bpf/bpf.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tools/lib/bpf/bpf.h b/tools/lib/bpf/bpf.h index a112e0ed1b19..7468978d3c27 100644 --- a/tools/lib/bpf/bpf.h +++ b/tools/lib/bpf/bpf.h @@ -409,8 +409,15 @@ LIBBPF_API int bpf_task_fd_query(int pid, int fd, __u32 flags, char *buf, __u32 *buf_len, __u32 *prog_id, __u32 *fd_type, __u64 *probe_offset, __u64 *probe_addr); +#ifdef __cplusplus +/* forward-declaring enums in C++ isn't compatible with pure C enums, so + * instead define bpf_enable_stats() as accepting int as an input + */ +LIBBPF_API int bpf_enable_stats(int type); +#else enum bpf_stats_type; /* defined in up-to-date linux/bpf.h */ LIBBPF_API int bpf_enable_stats(enum bpf_stats_type type); +#endif struct bpf_prog_bind_opts { size_t sz; /* size of this struct for forward/backward compatibility */ From f8186bf65ae6a4ce96d5cf52c2c9481c0e2193ce Mon Sep 17 00:00:00 2001 From: Andrii Nakryiko Date: Wed, 30 Nov 2022 12:00:13 -0800 Subject: [PATCH 1341/2002] selftests/bpf: Make sure enum-less bpf_enable_stats() API works in C++ mode Just a simple test to make sure we don't introduce unwanted compiler warnings and API still supports passing enums as input argument. Signed-off-by: Andrii Nakryiko Signed-off-by: Daniel Borkmann Link: https://lore.kernel.org/bpf/20221130200013.2997831-2-andrii@kernel.org --- tools/testing/selftests/bpf/test_cpp.cpp | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/tools/testing/selftests/bpf/test_cpp.cpp b/tools/testing/selftests/bpf/test_cpp.cpp index 19ad172036da..0bd9990e83fa 100644 --- a/tools/testing/selftests/bpf/test_cpp.cpp +++ b/tools/testing/selftests/bpf/test_cpp.cpp @@ -1,9 +1,9 @@ /* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */ #include -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wdeprecated-declarations" +#include +#include +#include #include -#pragma GCC diagnostic pop #include #include #include "test_core_extern.skel.h" @@ -99,6 +99,7 @@ int main(int argc, char *argv[]) struct btf_dump_opts opts = { }; struct test_core_extern *skel; struct btf *btf; + int fd; try_skeleton_template(); @@ -117,6 +118,12 @@ int main(int argc, char *argv[]) skel = test_core_extern__open_and_load(); test_core_extern__destroy(skel); + fd = bpf_enable_stats(BPF_STATS_RUN_TIME); + if (fd < 0) + std::cout << "FAILED to enable stats: " << fd << std::endl; + else + ::close(fd); + std::cout << "DONE!" << std::endl; return 0; From 7d7cfb48d81353e826493d24c7cec7360950968f Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Tue, 22 Nov 2022 16:00:09 +0100 Subject: [PATCH 1342/2002] netfilter: conntrack: set icmpv6 redirects as RELATED icmp conntrack will set icmp redirects as RELATED, but icmpv6 will not do this. For icmpv6, only icmp errors (code <= 128) are examined for RELATED state. ICMPV6 Redirects are part of neighbour discovery mechanism, those are handled by marking a selected subset (e.g. neighbour solicitations) as UNTRACKED, but not REDIRECT -- they will thus be flagged as INVALID. Add minimal support for REDIRECTs. No parsing of neighbour options is added for simplicity, so this will only check that we have the embeeded original header (ND_OPT_REDIRECT_HDR), and then attempt to do a flow lookup for this tuple. Also extend the existing test case to cover redirects. Fixes: 9fb9cbb1082d ("[NETFILTER]: Add nf_conntrack subsystem.") Reported-by: Eric Garver Link: https://github.com/firewalld/firewalld/issues/1046 Signed-off-by: Florian Westphal Acked-by: Eric Garver Signed-off-by: Pablo Neira Ayuso --- net/netfilter/nf_conntrack_proto_icmpv6.c | 53 +++++++++++++++++++ .../netfilter/conntrack_icmp_related.sh | 36 ++++++++++++- 2 files changed, 87 insertions(+), 2 deletions(-) diff --git a/net/netfilter/nf_conntrack_proto_icmpv6.c b/net/netfilter/nf_conntrack_proto_icmpv6.c index 61e3b05cf02c..1020d67600a9 100644 --- a/net/netfilter/nf_conntrack_proto_icmpv6.c +++ b/net/netfilter/nf_conntrack_proto_icmpv6.c @@ -129,6 +129,56 @@ static void icmpv6_error_log(const struct sk_buff *skb, nf_l4proto_log_invalid(skb, state, IPPROTO_ICMPV6, "%s", msg); } +static noinline_for_stack int +nf_conntrack_icmpv6_redirect(struct nf_conn *tmpl, struct sk_buff *skb, + unsigned int dataoff, + const struct nf_hook_state *state) +{ + u8 hl = ipv6_hdr(skb)->hop_limit; + union nf_inet_addr outer_daddr; + union { + struct nd_opt_hdr nd_opt; + struct rd_msg rd_msg; + } tmp; + const struct nd_opt_hdr *nd_opt; + const struct rd_msg *rd_msg; + + rd_msg = skb_header_pointer(skb, dataoff, sizeof(*rd_msg), &tmp.rd_msg); + if (!rd_msg) { + icmpv6_error_log(skb, state, "short redirect"); + return -NF_ACCEPT; + } + + if (rd_msg->icmph.icmp6_code != 0) + return NF_ACCEPT; + + if (hl != 255 || !(ipv6_addr_type(&ipv6_hdr(skb)->saddr) & IPV6_ADDR_LINKLOCAL)) { + icmpv6_error_log(skb, state, "invalid saddr or hoplimit for redirect"); + return -NF_ACCEPT; + } + + dataoff += sizeof(*rd_msg); + + /* warning: rd_msg no longer usable after this call */ + nd_opt = skb_header_pointer(skb, dataoff, sizeof(*nd_opt), &tmp.nd_opt); + if (!nd_opt || nd_opt->nd_opt_len == 0) { + icmpv6_error_log(skb, state, "redirect without options"); + return -NF_ACCEPT; + } + + /* We could call ndisc_parse_options(), but it would need + * skb_linearize() and a bit more work. + */ + if (nd_opt->nd_opt_type != ND_OPT_REDIRECT_HDR) + return NF_ACCEPT; + + memcpy(&outer_daddr.ip6, &ipv6_hdr(skb)->daddr, + sizeof(outer_daddr.ip6)); + dataoff += 8; + return nf_conntrack_inet_error(tmpl, skb, dataoff, state, + IPPROTO_ICMPV6, &outer_daddr); +} + int nf_conntrack_icmpv6_error(struct nf_conn *tmpl, struct sk_buff *skb, unsigned int dataoff, @@ -159,6 +209,9 @@ int nf_conntrack_icmpv6_error(struct nf_conn *tmpl, return NF_ACCEPT; } + if (icmp6h->icmp6_type == NDISC_REDIRECT) + return nf_conntrack_icmpv6_redirect(tmpl, skb, dataoff, state); + /* is not error message ? */ if (icmp6h->icmp6_type >= 128) return NF_ACCEPT; diff --git a/tools/testing/selftests/netfilter/conntrack_icmp_related.sh b/tools/testing/selftests/netfilter/conntrack_icmp_related.sh index b48e1833bc89..76645aaf2b58 100755 --- a/tools/testing/selftests/netfilter/conntrack_icmp_related.sh +++ b/tools/testing/selftests/netfilter/conntrack_icmp_related.sh @@ -35,6 +35,8 @@ cleanup() { for i in 1 2;do ip netns del nsrouter$i;done } +trap cleanup EXIT + ipv4() { echo -n 192.168.$1.2 } @@ -146,11 +148,17 @@ ip netns exec nsclient1 nft -f - < /dev/null + +expect="packets 1 bytes 112" +check_counter nsclient1 "redir4" "$expect" +if [ $? -ne 0 ];then + ret=1 +fi + +ip netns exec "nsclient1" ping -c 1 dead:1::42 > /dev/null +expect="packets 1 bytes 192" +check_counter nsclient1 "redir6" "$expect" +if [ $? -ne 0 ];then + ret=1 +fi + +if [ $ret -eq 0 ];then + echo "PASS: icmp redirects had RELATED state" +else + echo "ERROR: icmp redirect RELATED state test has failed" +fi + exit $ret From 996c060e2bb90e5caef42849846b56da21ea88d9 Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Wed, 30 Nov 2022 23:20:49 +0100 Subject: [PATCH 1343/2002] selftests/bpf: Add bench test to arm64 and s390x denylist BPF CI fails for arm64 and s390x each with the following result: [...] All error logs: serial_test_kprobe_multi_bench_attach:PASS:get_syms 0 nsec serial_test_kprobe_multi_bench_attach:PASS:kprobe_multi_empty__open_and_load 0 nsec libbpf: prog 'test_kprobe_empty': failed to attach: Operation not supported serial_test_kprobe_multi_bench_attach:FAIL:bpf_program__attach_kprobe_multi_opts unexpected error: -95 #92 kprobe_multi_bench_attach:FAIL [...] Add the test to the deny list. Fixes: 5b6c7e5c4434 ("selftests/bpf: Add attach bench test") Signed-off-by: Daniel Borkmann --- tools/testing/selftests/bpf/DENYLIST.aarch64 | 1 + tools/testing/selftests/bpf/DENYLIST.s390x | 1 + 2 files changed, 2 insertions(+) diff --git a/tools/testing/selftests/bpf/DENYLIST.aarch64 b/tools/testing/selftests/bpf/DENYLIST.aarch64 index 8e77515d56f6..99cc33c51eaa 100644 --- a/tools/testing/selftests/bpf/DENYLIST.aarch64 +++ b/tools/testing/selftests/bpf/DENYLIST.aarch64 @@ -28,6 +28,7 @@ kfree_skb # attach fentry unexpected erro kfunc_call/subprog # extern (var ksym) 'bpf_prog_active': not found in kernel BTF kfunc_call/subprog_lskel # skel unexpected error: -2 kfunc_dynptr_param/dynptr_data_null # libbpf: prog 'dynptr_data_null': failed to attach: ERROR: strerror_r(-524)=22 +kprobe_multi_bench_attach # bpf_program__attach_kprobe_multi_opts unexpected error: -95 kprobe_multi_test/attach_api_addrs # bpf_program__attach_kprobe_multi_opts unexpected error: -95 kprobe_multi_test/attach_api_pattern # bpf_program__attach_kprobe_multi_opts unexpected error: -95 kprobe_multi_test/attach_api_syms # bpf_program__attach_kprobe_multi_opts unexpected error: -95 diff --git a/tools/testing/selftests/bpf/DENYLIST.s390x b/tools/testing/selftests/bpf/DENYLIST.s390x index 648a8a1b6b78..3481f3a5ea6f 100644 --- a/tools/testing/selftests/bpf/DENYLIST.s390x +++ b/tools/testing/selftests/bpf/DENYLIST.s390x @@ -29,6 +29,7 @@ htab_update # failed to attach: ERROR: strerror_r(- kfree_skb # attach fentry unexpected error: -524 (trampoline) kfunc_call # 'bpf_prog_active': not found in kernel BTF (?) kfunc_dynptr_param # JIT does not support calling kernel function (kfunc) +kprobe_multi_bench_attach # bpf_program__attach_kprobe_multi_opts unexpected error: -95 kprobe_multi_test # relies on fentry ksyms_module # test_ksyms_module__open_and_load unexpected error: -9 (?) ksyms_module_libbpf # JIT does not support calling kernel function (kfunc) From c67cae551f0df80421b5703ee56ff5e2fe9c4de6 Mon Sep 17 00:00:00 2001 From: Alexei Starovoitov Date: Fri, 25 Nov 2022 14:06:17 -0800 Subject: [PATCH 1344/2002] bpf: Tighten ptr_to_btf_id checks. The networking programs typically don't require CAP_PERFMON, but through kfuncs like bpf_cast_to_kern_ctx() they can access memory through PTR_TO_BTF_ID. In such case enforce CAP_PERFMON. Also make sure that only GPL programs can access kernel data structures. All kfuncs require GPL already. Also remove allow_ptr_to_map_access. It's the same as allow_ptr_leaks and different name for the same check only causes confusion. Fixes: fd264ca02094 ("bpf: Add a kfunc to type cast from bpf uapi ctx to kernel ctx") Fixes: 50c6b8a9aea2 ("selftests/bpf: Add a test for btf_type_tag "percpu"") Signed-off-by: Alexei Starovoitov Signed-off-by: Andrii Nakryiko Acked-by: Yonghong Song Link: https://lore.kernel.org/bpf/20221125220617.26846-1-alexei.starovoitov@gmail.com --- include/linux/bpf.h | 5 ----- include/linux/bpf_verifier.h | 1 - kernel/bpf/verifier.c | 17 ++++++++++++++--- .../selftests/bpf/progs/btf_type_tag_percpu.c | 1 + tools/testing/selftests/bpf/verifier/map_ptr.c | 8 ++++---- 5 files changed, 19 insertions(+), 13 deletions(-) diff --git a/include/linux/bpf.h b/include/linux/bpf.h index 67452103bb86..4920ac252754 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -1909,11 +1909,6 @@ static inline bool bpf_allow_uninit_stack(void) return perfmon_capable(); } -static inline bool bpf_allow_ptr_to_map_access(void) -{ - return perfmon_capable(); -} - static inline bool bpf_bypass_spec_v1(void) { return perfmon_capable(); diff --git a/include/linux/bpf_verifier.h b/include/linux/bpf_verifier.h index c05aa6e1f6f5..b5090e89cb3f 100644 --- a/include/linux/bpf_verifier.h +++ b/include/linux/bpf_verifier.h @@ -531,7 +531,6 @@ struct bpf_verifier_env { bool explore_alu_limits; bool allow_ptr_leaks; bool allow_uninit_stack; - bool allow_ptr_to_map_access; bool bpf_capable; bool bypass_spec_v1; bool bypass_spec_v4; diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 4e7f1d085e53..d0ecc0b18b20 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -4703,6 +4703,18 @@ static int check_ptr_to_btf_access(struct bpf_verifier_env *env, u32 btf_id; int ret; + if (!env->allow_ptr_leaks) { + verbose(env, + "'struct %s' access is allowed only to CAP_PERFMON and CAP_SYS_ADMIN\n", + tname); + return -EPERM; + } + if (!env->prog->gpl_compatible && btf_is_kernel(reg->btf)) { + verbose(env, + "Cannot access kernel 'struct %s' from non-GPL compatible program\n", + tname); + return -EINVAL; + } if (off < 0) { verbose(env, "R%d is ptr_%s invalid negative access: off=%d\n", @@ -4823,9 +4835,9 @@ static int check_ptr_to_map_access(struct bpf_verifier_env *env, t = btf_type_by_id(btf_vmlinux, *map->ops->map_btf_id); tname = btf_name_by_offset(btf_vmlinux, t->name_off); - if (!env->allow_ptr_to_map_access) { + if (!env->allow_ptr_leaks) { verbose(env, - "%s access is allowed only to CAP_PERFMON and CAP_SYS_ADMIN\n", + "'struct %s' access is allowed only to CAP_PERFMON and CAP_SYS_ADMIN\n", tname); return -EPERM; } @@ -16679,7 +16691,6 @@ int bpf_check(struct bpf_prog **prog, union bpf_attr *attr, bpfptr_t uattr) env->allow_ptr_leaks = bpf_allow_ptr_leaks(); env->allow_uninit_stack = bpf_allow_uninit_stack(); - env->allow_ptr_to_map_access = bpf_allow_ptr_to_map_access(); env->bypass_spec_v1 = bpf_bypass_spec_v1(); env->bypass_spec_v4 = bpf_bypass_spec_v4(); env->bpf_capable = bpf_capable(); diff --git a/tools/testing/selftests/bpf/progs/btf_type_tag_percpu.c b/tools/testing/selftests/bpf/progs/btf_type_tag_percpu.c index 8feddb8289cf..38f78d9345de 100644 --- a/tools/testing/selftests/bpf/progs/btf_type_tag_percpu.c +++ b/tools/testing/selftests/bpf/progs/btf_type_tag_percpu.c @@ -64,3 +64,4 @@ int BPF_PROG(test_percpu_helper, struct cgroup *cgrp, const char *path) return 0; } +char _license[] SEC("license") = "GPL"; diff --git a/tools/testing/selftests/bpf/verifier/map_ptr.c b/tools/testing/selftests/bpf/verifier/map_ptr.c index 1f82021429bf..17ee84dc7766 100644 --- a/tools/testing/selftests/bpf/verifier/map_ptr.c +++ b/tools/testing/selftests/bpf/verifier/map_ptr.c @@ -9,7 +9,7 @@ }, .fixup_map_array_48b = { 1 }, .result_unpriv = REJECT, - .errstr_unpriv = "bpf_array access is allowed only to CAP_PERFMON and CAP_SYS_ADMIN", + .errstr_unpriv = "access is allowed only to CAP_PERFMON and CAP_SYS_ADMIN", .result = REJECT, .errstr = "R1 is bpf_array invalid negative access: off=-8", }, @@ -26,7 +26,7 @@ }, .fixup_map_array_48b = { 3 }, .result_unpriv = REJECT, - .errstr_unpriv = "bpf_array access is allowed only to CAP_PERFMON and CAP_SYS_ADMIN", + .errstr_unpriv = "access is allowed only to CAP_PERFMON and CAP_SYS_ADMIN", .result = REJECT, .errstr = "only read from bpf_array is supported", }, @@ -41,7 +41,7 @@ }, .fixup_map_array_48b = { 1 }, .result_unpriv = REJECT, - .errstr_unpriv = "bpf_array access is allowed only to CAP_PERFMON and CAP_SYS_ADMIN", + .errstr_unpriv = "access is allowed only to CAP_PERFMON and CAP_SYS_ADMIN", .result = REJECT, .errstr = "cannot access ptr member ops with moff 0 in struct bpf_map with off 1 size 4", .flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS, @@ -57,7 +57,7 @@ }, .fixup_map_array_48b = { 1 }, .result_unpriv = REJECT, - .errstr_unpriv = "bpf_array access is allowed only to CAP_PERFMON and CAP_SYS_ADMIN", + .errstr_unpriv = "access is allowed only to CAP_PERFMON and CAP_SYS_ADMIN", .result = ACCEPT, .retval = 1, }, From 7a9841ca025275b5b0edfb0b618934abb6ceec15 Mon Sep 17 00:00:00 2001 From: Pengcheng Yang Date: Tue, 29 Nov 2022 18:40:38 +0800 Subject: [PATCH 1345/2002] bpf, sockmap: Fix repeated calls to sock_put() when msg has more_data In tcp_bpf_send_verdict() redirection, the eval variable is assigned to __SK_REDIRECT after the apply_bytes data is sent, if msg has more_data, sock_put() will be called multiple times. We should reset the eval variable to __SK_NONE every time more_data starts. This causes: IPv4: Attempt to release TCP socket in state 1 00000000b4c925d7 ------------[ cut here ]------------ refcount_t: addition on 0; use-after-free. WARNING: CPU: 5 PID: 4482 at lib/refcount.c:25 refcount_warn_saturate+0x7d/0x110 Modules linked in: CPU: 5 PID: 4482 Comm: sockhash_bypass Kdump: loaded Not tainted 6.0.0 #1 Hardware name: Red Hat KVM, BIOS 1.11.0-2.el7 04/01/2014 Call Trace: __tcp_transmit_skb+0xa1b/0xb90 ? __alloc_skb+0x8c/0x1a0 ? __kmalloc_node_track_caller+0x184/0x320 tcp_write_xmit+0x22a/0x1110 __tcp_push_pending_frames+0x32/0xf0 do_tcp_sendpages+0x62d/0x640 tcp_bpf_push+0xae/0x2c0 tcp_bpf_sendmsg_redir+0x260/0x410 ? preempt_count_add+0x70/0xa0 tcp_bpf_send_verdict+0x386/0x4b0 tcp_bpf_sendmsg+0x21b/0x3b0 sock_sendmsg+0x58/0x70 __sys_sendto+0xfa/0x170 ? xfd_validate_state+0x1d/0x80 ? switch_fpu_return+0x59/0xe0 __x64_sys_sendto+0x24/0x30 do_syscall_64+0x37/0x90 entry_SYSCALL_64_after_hwframe+0x63/0xcd Fixes: cd9733f5d75c ("tcp_bpf: Fix one concurrency problem in the tcp_bpf_send_verdict function") Signed-off-by: Pengcheng Yang Signed-off-by: Daniel Borkmann Acked-by: Jakub Sitnicki Link: https://lore.kernel.org/bpf/1669718441-2654-2-git-send-email-yangpc@wangsu.com --- net/ipv4/tcp_bpf.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/net/ipv4/tcp_bpf.c b/net/ipv4/tcp_bpf.c index cf9c3e8f7ccb..f3e868f4cd9e 100644 --- a/net/ipv4/tcp_bpf.c +++ b/net/ipv4/tcp_bpf.c @@ -279,7 +279,7 @@ static int tcp_bpf_send_verdict(struct sock *sk, struct sk_psock *psock, bool cork = false, enospc = sk_msg_full(msg); struct sock *sk_redir; u32 tosend, origsize, sent, delta = 0; - u32 eval = __SK_NONE; + u32 eval; int ret; more_data: @@ -310,6 +310,7 @@ more_data: tosend = msg->sg.size; if (psock->apply_bytes && psock->apply_bytes < tosend) tosend = psock->apply_bytes; + eval = __SK_NONE; switch (psock->eval) { case __SK_PASS: From a351d6087bf7d3d8440d58d3bf244ec64b89394a Mon Sep 17 00:00:00 2001 From: Pengcheng Yang Date: Tue, 29 Nov 2022 18:40:39 +0800 Subject: [PATCH 1346/2002] bpf, sockmap: Fix missing BPF_F_INGRESS flag when using apply_bytes When redirecting, we use sk_msg_to_ingress() to get the BPF_F_INGRESS flag from the msg->flags. If apply_bytes is used and it is larger than the current data being processed, sk_psock_msg_verdict() will not be called when sendmsg() is called again. At this time, the msg->flags is 0, and we lost the BPF_F_INGRESS flag. So we need to save the BPF_F_INGRESS flag in sk_psock and use it when redirection. Fixes: 8934ce2fd081 ("bpf: sockmap redirect ingress support") Signed-off-by: Pengcheng Yang Signed-off-by: Daniel Borkmann Acked-by: Jakub Sitnicki Link: https://lore.kernel.org/bpf/1669718441-2654-3-git-send-email-yangpc@wangsu.com --- include/linux/skmsg.h | 1 + include/net/tcp.h | 4 ++-- net/core/skmsg.c | 9 ++++++--- net/ipv4/tcp_bpf.c | 11 ++++++----- net/tls/tls_sw.c | 6 ++++-- 5 files changed, 19 insertions(+), 12 deletions(-) diff --git a/include/linux/skmsg.h b/include/linux/skmsg.h index 70d6cb94e580..84f787416a54 100644 --- a/include/linux/skmsg.h +++ b/include/linux/skmsg.h @@ -82,6 +82,7 @@ struct sk_psock { u32 apply_bytes; u32 cork_bytes; u32 eval; + bool redir_ingress; /* undefined if sk_redir is null */ struct sk_msg *cork; struct sk_psock_progs progs; #if IS_ENABLED(CONFIG_BPF_STREAM_PARSER) diff --git a/include/net/tcp.h b/include/net/tcp.h index 6b814e788f00..b87e7381bddf 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -2319,8 +2319,8 @@ int tcp_bpf_update_proto(struct sock *sk, struct sk_psock *psock, bool restore); void tcp_bpf_clone(const struct sock *sk, struct sock *newsk); #endif /* CONFIG_BPF_SYSCALL */ -int tcp_bpf_sendmsg_redir(struct sock *sk, struct sk_msg *msg, u32 bytes, - int flags); +int tcp_bpf_sendmsg_redir(struct sock *sk, bool ingress, + struct sk_msg *msg, u32 bytes, int flags); #endif /* CONFIG_NET_SOCK_MSG */ #if !defined(CONFIG_BPF_SYSCALL) || !defined(CONFIG_NET_SOCK_MSG) diff --git a/net/core/skmsg.c b/net/core/skmsg.c index e6b9ced3eda8..53d0251788aa 100644 --- a/net/core/skmsg.c +++ b/net/core/skmsg.c @@ -886,13 +886,16 @@ int sk_psock_msg_verdict(struct sock *sk, struct sk_psock *psock, ret = sk_psock_map_verd(ret, msg->sk_redir); psock->apply_bytes = msg->apply_bytes; if (ret == __SK_REDIRECT) { - if (psock->sk_redir) + if (psock->sk_redir) { sock_put(psock->sk_redir); - psock->sk_redir = msg->sk_redir; - if (!psock->sk_redir) { + psock->sk_redir = NULL; + } + if (!msg->sk_redir) { ret = __SK_DROP; goto out; } + psock->redir_ingress = sk_msg_to_ingress(msg); + psock->sk_redir = msg->sk_redir; sock_hold(psock->sk_redir); } out: diff --git a/net/ipv4/tcp_bpf.c b/net/ipv4/tcp_bpf.c index f3e868f4cd9e..275c5ca9e04d 100644 --- a/net/ipv4/tcp_bpf.c +++ b/net/ipv4/tcp_bpf.c @@ -131,10 +131,9 @@ static int tcp_bpf_push_locked(struct sock *sk, struct sk_msg *msg, return ret; } -int tcp_bpf_sendmsg_redir(struct sock *sk, struct sk_msg *msg, - u32 bytes, int flags) +int tcp_bpf_sendmsg_redir(struct sock *sk, bool ingress, + struct sk_msg *msg, u32 bytes, int flags) { - bool ingress = sk_msg_to_ingress(msg); struct sk_psock *psock = sk_psock_get(sk); int ret; @@ -276,7 +275,7 @@ msg_bytes_ready: static int tcp_bpf_send_verdict(struct sock *sk, struct sk_psock *psock, struct sk_msg *msg, int *copied, int flags) { - bool cork = false, enospc = sk_msg_full(msg); + bool cork = false, enospc = sk_msg_full(msg), redir_ingress; struct sock *sk_redir; u32 tosend, origsize, sent, delta = 0; u32 eval; @@ -322,6 +321,7 @@ more_data: sk_msg_apply_bytes(psock, tosend); break; case __SK_REDIRECT: + redir_ingress = psock->redir_ingress; sk_redir = psock->sk_redir; sk_msg_apply_bytes(psock, tosend); if (!psock->apply_bytes) { @@ -338,7 +338,8 @@ more_data: release_sock(sk); origsize = msg->sg.size; - ret = tcp_bpf_sendmsg_redir(sk_redir, msg, tosend, flags); + ret = tcp_bpf_sendmsg_redir(sk_redir, redir_ingress, + msg, tosend, flags); sent = origsize - msg->sg.size; if (eval == __SK_REDIRECT) diff --git a/net/tls/tls_sw.c b/net/tls/tls_sw.c index 264cf367e265..9ed978634125 100644 --- a/net/tls/tls_sw.c +++ b/net/tls/tls_sw.c @@ -792,7 +792,7 @@ static int bpf_exec_tx_verdict(struct sk_msg *msg, struct sock *sk, struct sk_psock *psock; struct sock *sk_redir; struct tls_rec *rec; - bool enospc, policy; + bool enospc, policy, redir_ingress; int err = 0, send; u32 delta = 0; @@ -837,6 +837,7 @@ more_data: } break; case __SK_REDIRECT: + redir_ingress = psock->redir_ingress; sk_redir = psock->sk_redir; memcpy(&msg_redir, msg, sizeof(*msg)); if (msg->apply_bytes < send) @@ -846,7 +847,8 @@ more_data: sk_msg_return_zero(sk, msg, send); msg->sg.size -= send; release_sock(sk); - err = tcp_bpf_sendmsg_redir(sk_redir, &msg_redir, send, flags); + err = tcp_bpf_sendmsg_redir(sk_redir, redir_ingress, + &msg_redir, send, flags); lock_sock(sk); if (err < 0) { *copied -= sk_msg_free_nocharge(sk, &msg_redir); From 9072931f020bfd907d6d89ee21ff1481cd78b407 Mon Sep 17 00:00:00 2001 From: Pengcheng Yang Date: Tue, 29 Nov 2022 18:40:40 +0800 Subject: [PATCH 1347/2002] bpf, sockmap: Fix data loss caused by using apply_bytes on ingress redirect Use apply_bytes on ingress redirect, when apply_bytes is less than the length of msg data, some data may be skipped and lost in bpf_tcp_ingress(). If there is still data in the scatterlist that has not been consumed, we cannot move the msg iter. Fixes: 604326b41a6f ("bpf, sockmap: convert to generic sk_msg interface") Signed-off-by: Pengcheng Yang Signed-off-by: Daniel Borkmann Acked-by: Jakub Sitnicki Link: https://lore.kernel.org/bpf/1669718441-2654-4-git-send-email-yangpc@wangsu.com --- net/ipv4/tcp_bpf.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/net/ipv4/tcp_bpf.c b/net/ipv4/tcp_bpf.c index 275c5ca9e04d..94aad3870c5f 100644 --- a/net/ipv4/tcp_bpf.c +++ b/net/ipv4/tcp_bpf.c @@ -45,8 +45,11 @@ static int bpf_tcp_ingress(struct sock *sk, struct sk_psock *psock, tmp->sg.end = i; if (apply) { apply_bytes -= size; - if (!apply_bytes) + if (!apply_bytes) { + if (sge->length) + sk_msg_iter_var_prev(i); break; + } } } while (i != msg->sg.end); From 89903dcb3c2e134fb101de7921a19dd9f8418b4c Mon Sep 17 00:00:00 2001 From: Pengcheng Yang Date: Tue, 29 Nov 2022 18:40:41 +0800 Subject: [PATCH 1348/2002] selftests/bpf: Add ingress tests for txmsg with apply_bytes Currently, the ingress redirect is not covered in "txmsg test apply". Signed-off-by: Pengcheng Yang Signed-off-by: Daniel Borkmann Acked-by: Jakub Sitnicki Link: https://lore.kernel.org/bpf/1669718441-2654-5-git-send-email-yangpc@wangsu.com --- tools/testing/selftests/bpf/test_sockmap.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/tools/testing/selftests/bpf/test_sockmap.c b/tools/testing/selftests/bpf/test_sockmap.c index e768181a1bd7..024a0faafb3b 100644 --- a/tools/testing/selftests/bpf/test_sockmap.c +++ b/tools/testing/selftests/bpf/test_sockmap.c @@ -1690,24 +1690,42 @@ static void test_txmsg_apply(int cgrp, struct sockmap_options *opt) { txmsg_pass = 1; txmsg_redir = 0; + txmsg_ingress = 0; txmsg_apply = 1; txmsg_cork = 0; test_send_one(opt, cgrp); txmsg_pass = 0; txmsg_redir = 1; + txmsg_ingress = 0; + txmsg_apply = 1; + txmsg_cork = 0; + test_send_one(opt, cgrp); + + txmsg_pass = 0; + txmsg_redir = 1; + txmsg_ingress = 1; txmsg_apply = 1; txmsg_cork = 0; test_send_one(opt, cgrp); txmsg_pass = 1; txmsg_redir = 0; + txmsg_ingress = 0; txmsg_apply = 1024; txmsg_cork = 0; test_send_large(opt, cgrp); txmsg_pass = 0; txmsg_redir = 1; + txmsg_ingress = 0; + txmsg_apply = 1024; + txmsg_cork = 0; + test_send_large(opt, cgrp); + + txmsg_pass = 0; + txmsg_redir = 1; + txmsg_ingress = 1; txmsg_apply = 1024; txmsg_cork = 0; test_send_large(opt, cgrp); From 3144bfa5078e0df7507a4de72061501e6a0e56be Mon Sep 17 00:00:00 2001 From: Yonghong Song Date: Tue, 29 Nov 2022 21:21:47 -0800 Subject: [PATCH 1349/2002] bpf: Fix a compilation failure with clang lto build When building the kernel with clang lto (CONFIG_LTO_CLANG_FULL=y), the following compilation error will appear: $ make LLVM=1 LLVM_IAS=1 -j ... ld.lld: error: ld-temp.o :26889:1: symbol 'cgroup_storage_map_btf_ids' is already defined cgroup_storage_map_btf_ids:; ^ make[1]: *** [/.../bpf-next/scripts/Makefile.vmlinux_o:61: vmlinux.o] Error 1 In local_storage.c, we have BTF_ID_LIST_SINGLE(cgroup_storage_map_btf_ids, struct, bpf_local_storage_map) Commit c4bcfb38a95e ("bpf: Implement cgroup storage available to non-cgroup-attached bpf progs") added the above identical BTF_ID_LIST_SINGLE definition in bpf_cgrp_storage.c. With duplicated definitions, llvm linker complains with lto build. Also, extracting btf_id of 'struct bpf_local_storage_map' is defined four times for sk, inode, task and cgrp local storages. Let us define a single global one with a different name than cgroup_storage_map_btf_ids, which also fixed the lto compilation error. Fixes: c4bcfb38a95e ("bpf: Implement cgroup storage available to non-cgroup-attached bpf progs") Signed-off-by: Yonghong Song Signed-off-by: Andrii Nakryiko Link: https://lore.kernel.org/bpf/20221130052147.1591625-1-yhs@fb.com --- include/linux/btf_ids.h | 1 + kernel/bpf/bpf_cgrp_storage.c | 3 +-- kernel/bpf/bpf_inode_storage.c | 4 +--- kernel/bpf/bpf_task_storage.c | 4 ++-- net/core/bpf_sk_storage.c | 3 +-- 5 files changed, 6 insertions(+), 9 deletions(-) diff --git a/include/linux/btf_ids.h b/include/linux/btf_ids.h index 93397711a68c..3a4f7cd882ca 100644 --- a/include/linux/btf_ids.h +++ b/include/linux/btf_ids.h @@ -266,5 +266,6 @@ MAX_BTF_TRACING_TYPE, extern u32 btf_tracing_ids[]; extern u32 bpf_cgroup_btf_id[]; +extern u32 bpf_local_storage_map_btf_id[]; #endif diff --git a/kernel/bpf/bpf_cgrp_storage.c b/kernel/bpf/bpf_cgrp_storage.c index 309403800f82..6cdf6d9ed91d 100644 --- a/kernel/bpf/bpf_cgrp_storage.c +++ b/kernel/bpf/bpf_cgrp_storage.c @@ -211,7 +211,6 @@ BPF_CALL_2(bpf_cgrp_storage_delete, struct bpf_map *, map, struct cgroup *, cgro return ret; } -BTF_ID_LIST_SINGLE(cgroup_storage_map_btf_ids, struct, bpf_local_storage_map) const struct bpf_map_ops cgrp_storage_map_ops = { .map_meta_equal = bpf_map_meta_equal, .map_alloc_check = bpf_local_storage_map_alloc_check, @@ -222,7 +221,7 @@ const struct bpf_map_ops cgrp_storage_map_ops = { .map_update_elem = bpf_cgrp_storage_update_elem, .map_delete_elem = bpf_cgrp_storage_delete_elem, .map_check_btf = bpf_local_storage_map_check_btf, - .map_btf_id = &cgroup_storage_map_btf_ids[0], + .map_btf_id = &bpf_local_storage_map_btf_id[0], .map_owner_storage_ptr = cgroup_storage_ptr, }; diff --git a/kernel/bpf/bpf_inode_storage.c b/kernel/bpf/bpf_inode_storage.c index 6a1d4d22816a..05f4c66c9089 100644 --- a/kernel/bpf/bpf_inode_storage.c +++ b/kernel/bpf/bpf_inode_storage.c @@ -213,8 +213,6 @@ static void inode_storage_map_free(struct bpf_map *map) bpf_local_storage_map_free(map, &inode_cache, NULL); } -BTF_ID_LIST_SINGLE(inode_storage_map_btf_ids, struct, - bpf_local_storage_map) const struct bpf_map_ops inode_storage_map_ops = { .map_meta_equal = bpf_map_meta_equal, .map_alloc_check = bpf_local_storage_map_alloc_check, @@ -225,7 +223,7 @@ const struct bpf_map_ops inode_storage_map_ops = { .map_update_elem = bpf_fd_inode_storage_update_elem, .map_delete_elem = bpf_fd_inode_storage_delete_elem, .map_check_btf = bpf_local_storage_map_check_btf, - .map_btf_id = &inode_storage_map_btf_ids[0], + .map_btf_id = &bpf_local_storage_map_btf_id[0], .map_owner_storage_ptr = inode_storage_ptr, }; diff --git a/kernel/bpf/bpf_task_storage.c b/kernel/bpf/bpf_task_storage.c index 8e832db8151a..1e486055a523 100644 --- a/kernel/bpf/bpf_task_storage.c +++ b/kernel/bpf/bpf_task_storage.c @@ -324,7 +324,7 @@ static void task_storage_map_free(struct bpf_map *map) bpf_local_storage_map_free(map, &task_cache, &bpf_task_storage_busy); } -BTF_ID_LIST_SINGLE(task_storage_map_btf_ids, struct, bpf_local_storage_map) +BTF_ID_LIST_GLOBAL_SINGLE(bpf_local_storage_map_btf_id, struct, bpf_local_storage_map) const struct bpf_map_ops task_storage_map_ops = { .map_meta_equal = bpf_map_meta_equal, .map_alloc_check = bpf_local_storage_map_alloc_check, @@ -335,7 +335,7 @@ const struct bpf_map_ops task_storage_map_ops = { .map_update_elem = bpf_pid_task_storage_update_elem, .map_delete_elem = bpf_pid_task_storage_delete_elem, .map_check_btf = bpf_local_storage_map_check_btf, - .map_btf_id = &task_storage_map_btf_ids[0], + .map_btf_id = &bpf_local_storage_map_btf_id[0], .map_owner_storage_ptr = task_storage_ptr, }; diff --git a/net/core/bpf_sk_storage.c b/net/core/bpf_sk_storage.c index 9d2288c0736e..bb378c33f542 100644 --- a/net/core/bpf_sk_storage.c +++ b/net/core/bpf_sk_storage.c @@ -310,7 +310,6 @@ bpf_sk_storage_ptr(void *owner) return &sk->sk_bpf_storage; } -BTF_ID_LIST_SINGLE(sk_storage_map_btf_ids, struct, bpf_local_storage_map) const struct bpf_map_ops sk_storage_map_ops = { .map_meta_equal = bpf_map_meta_equal, .map_alloc_check = bpf_local_storage_map_alloc_check, @@ -321,7 +320,7 @@ const struct bpf_map_ops sk_storage_map_ops = { .map_update_elem = bpf_fd_sk_storage_update_elem, .map_delete_elem = bpf_fd_sk_storage_delete_elem, .map_check_btf = bpf_local_storage_map_check_btf, - .map_btf_id = &sk_storage_map_btf_ids[0], + .map_btf_id = &bpf_local_storage_map_btf_id[0], .map_local_storage_charge = bpf_sk_storage_charge, .map_local_storage_uncharge = bpf_sk_storage_uncharge, .map_owner_storage_ptr = bpf_sk_storage_ptr, From 2450d7d93fd2424dfacbf4aebf3fa8829df9d16e Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Fri, 25 Nov 2022 13:23:57 +0100 Subject: [PATCH 1350/2002] octeontx2-af: Fix a potentially spurious error message When this error message is displayed, we know that the all the bits in the bitmap are set. So, bitmap_weight() will return the number of bits of the bitmap, which is 'table->tot_ids'. It is unlikely that a bit will be cleared between mutex_unlock() and dev_err(), but, in order to simplify the code and avoid this possibility, just take 'table->tot_ids'. Signed-off-by: Christophe JAILLET Link: https://lore.kernel.org/r/5ce01c402f86412dc57884ff0994b63f0c5b3871.1669378798.git.christophe.jaillet@wanadoo.fr Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_hash.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_hash.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_hash.c index 594029007f85..5e6c54577a97 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_hash.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_hash.c @@ -490,7 +490,7 @@ static bool rvu_npc_exact_alloc_id(struct rvu *rvu, u32 *seq_id) if (idx == table->tot_ids) { mutex_unlock(&table->lock); dev_err(rvu->dev, "%s: No space in id bitmap (%d)\n", - __func__, bitmap_weight(table->id_bmap, table->tot_ids)); + __func__, table->tot_ids); return false; } From b6a0ecaee2e683479353d0403fa31dcb4bd2be3a Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Fri, 25 Nov 2022 13:23:58 +0100 Subject: [PATCH 1351/2002] octeontx2-af: Slightly simplify rvu_npc_exact_init() Use kzalloc() instead of kmalloc()/memset(). Signed-off-by: Christophe JAILLET Link: https://lore.kernel.org/r/60ea220ccf3b61963f7d5a97e3df2c76a5feb837.1669378798.git.christophe.jaillet@wanadoo.fr Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_hash.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_hash.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_hash.c index 5e6c54577a97..c584680f2d2b 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_hash.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_hash.c @@ -1870,12 +1870,11 @@ int rvu_npc_exact_init(struct rvu *rvu) /* Set capability to true */ rvu->hw->cap.npc_exact_match_enabled = true; - table = kmalloc(sizeof(*table), GFP_KERNEL); + table = kzalloc(sizeof(*table), GFP_KERNEL); if (!table) return -ENOMEM; dev_dbg(rvu->dev, "%s: Memory allocation for table success\n", __func__); - memset(table, 0, sizeof(*table)); rvu->hw->table = table; /* Read table size, ways and depth */ From 05a7b52ee5e489a0caf52299ee73c18409c086b3 Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Fri, 25 Nov 2022 13:23:59 +0100 Subject: [PATCH 1352/2002] octeontx2-af: Use the bitmap API to allocate bitmaps Use devm_bitmap_zalloc() instead of hand-writing it. This also makes the comment "Allocate bitmap for 32 entry mcam" more explicit because now 32 is really used in the allocation function, instead of an obscure 'sizeof(long)'. Signed-off-by: Christophe JAILLET Link: https://lore.kernel.org/r/24177a9ee7043259448b735263d9cfd6a70e89a4.1669378798.git.christophe.jaillet@wanadoo.fr Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_hash.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_hash.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_hash.c index c584680f2d2b..3f94b620ef5a 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_hash.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_hash.c @@ -1898,15 +1898,15 @@ int rvu_npc_exact_init(struct rvu *rvu) table_size = table->mem_table.depth * table->mem_table.ways; /* Allocate bitmap for 4way 2K table */ - table->mem_table.bmap = devm_kcalloc(rvu->dev, BITS_TO_LONGS(table_size), - sizeof(long), GFP_KERNEL); + table->mem_table.bmap = devm_bitmap_zalloc(rvu->dev, table_size, + GFP_KERNEL); if (!table->mem_table.bmap) return -ENOMEM; dev_dbg(rvu->dev, "%s: Allocated bitmap for 4way 2K entry table\n", __func__); /* Allocate bitmap for 32 entry mcam */ - table->cam_table.bmap = devm_kcalloc(rvu->dev, 1, sizeof(long), GFP_KERNEL); + table->cam_table.bmap = devm_bitmap_zalloc(rvu->dev, 32, GFP_KERNEL); if (!table->cam_table.bmap) return -ENOMEM; From 6d135d9e2b004024b84bd7c308f58300b64d29b6 Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Fri, 25 Nov 2022 13:24:00 +0100 Subject: [PATCH 1353/2002] octeontx2-af: Fix the size of memory allocated for the 'id_bmap' bitmap This allocation is really spurious. The size of the bitmap is 'tot_ids' and it is used as such in the driver. So we could expect something like: table->id_bmap = devm_kcalloc(rvu->dev, BITS_TO_LONGS(table->tot_ids), sizeof(long), GFP_KERNEL); However, when the bitmap is allocated, we allocate: BITS_TO_LONGS(table->tot_ids) * table->tot_ids ~= table->tot_ids / 32 * table->tot_ids ~= table->tot_ids^2 / 32 It is proportional to the square of 'table->tot_ids' which seems to potentially be big. Allocate the expected amount of memory, and switch to the bitmap API to have it more straightforward. Signed-off-by: Christophe JAILLET Link: https://lore.kernel.org/r/ce2710771939065d68f95d86a27cf7cea7966365.1669378798.git.christophe.jaillet@wanadoo.fr Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_hash.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_hash.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_hash.c index 3f94b620ef5a..ae34746341c4 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_hash.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_hash.c @@ -1914,8 +1914,8 @@ int rvu_npc_exact_init(struct rvu *rvu) dev_dbg(rvu->dev, "%s: Allocated bitmap for 32 entry cam\n", __func__); table->tot_ids = (table->mem_table.depth * table->mem_table.ways) + table->cam_table.depth; - table->id_bmap = devm_kcalloc(rvu->dev, BITS_TO_LONGS(table->tot_ids), - table->tot_ids, GFP_KERNEL); + table->id_bmap = devm_bitmap_zalloc(rvu->dev, table->tot_ids, + GFP_KERNEL); if (!table->id_bmap) return -ENOMEM; From 450f065053967f446afafc39328776b473305378 Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Fri, 25 Nov 2022 13:24:01 +0100 Subject: [PATCH 1354/2002] octeontx2-af: Simplify a size computation in rvu_npc_exact_init() We know that table_size = table->mem_table.depth * table->mem_table.ways, so use it instead, it is less verbose. Signed-off-by: Christophe JAILLET Link: https://lore.kernel.org/r/5230dabe27f48948a9fd0f50a62e2437b65e6a6e.1669378798.git.christophe.jaillet@wanadoo.fr Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_hash.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_hash.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_hash.c index ae34746341c4..00aef8f5ac29 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_hash.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_hash.c @@ -1913,7 +1913,7 @@ int rvu_npc_exact_init(struct rvu *rvu) dev_dbg(rvu->dev, "%s: Allocated bitmap for 32 entry cam\n", __func__); - table->tot_ids = (table->mem_table.depth * table->mem_table.ways) + table->cam_table.depth; + table->tot_ids = table_size + table->cam_table.depth; table->id_bmap = devm_bitmap_zalloc(rvu->dev, table->tot_ids, GFP_KERNEL); From c1d8e3fb1a3bef6d1b24a9b1326e4ed11a1fbf0b Mon Sep 17 00:00:00 2001 From: Horatiu Vultur Date: Mon, 28 Nov 2022 15:29:59 +0100 Subject: [PATCH 1355/2002] net: microchip: vcap: Change how the rule id is generated Currently whenever a new rule id is generated, it picks up the next number bigger than previous id. So it would always be 1, 2, 3, etc. When the rule with id 1 will be deleted and a new rule will be added, it will have the id 4 and not id 1. In theory this can be a problem if at some point a rule will be added and removed ~0 times. Then no more rules can be added because there are no more ids. Change this such that when a new rule is added, search for an empty rule id starting with value of 1 as value 0 is reserved. Fixes: c9da1ac1c212 ("net: microchip: sparx5: Adding initial tc flower support for VCAP API") Signed-off-by: Horatiu Vultur Link: https://lore.kernel.org/r/20221128142959.8325-1-horatiu.vultur@microchip.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/microchip/vcap/vcap_api.c | 7 +------ drivers/net/ethernet/microchip/vcap/vcap_api.h | 1 - 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/drivers/net/ethernet/microchip/vcap/vcap_api.c b/drivers/net/ethernet/microchip/vcap/vcap_api.c index 93efa8243b02..f2435d7ab515 100644 --- a/drivers/net/ethernet/microchip/vcap/vcap_api.c +++ b/drivers/net/ethernet/microchip/vcap/vcap_api.c @@ -985,17 +985,12 @@ static u32 vcap_next_rule_addr(u32 addr, struct vcap_rule_internal *ri) /* Assign a unique rule id and autogenerate one if id == 0 */ static u32 vcap_set_rule_id(struct vcap_rule_internal *ri) { - u32 next_id; - if (ri->data.id != 0) return ri->data.id; - next_id = ri->vctrl->rule_id + 1; - - for (next_id = ri->vctrl->rule_id + 1; next_id < ~0; ++next_id) { + for (u32 next_id = 1; next_id < ~0; ++next_id) { if (!vcap_lookup_rule(ri->vctrl, next_id)) { ri->data.id = next_id; - ri->vctrl->rule_id = next_id; break; } } diff --git a/drivers/net/ethernet/microchip/vcap/vcap_api.h b/drivers/net/ethernet/microchip/vcap/vcap_api.h index ca4499838306..689c7270f2a8 100644 --- a/drivers/net/ethernet/microchip/vcap/vcap_api.h +++ b/drivers/net/ethernet/microchip/vcap/vcap_api.h @@ -268,7 +268,6 @@ struct vcap_operations { /* VCAP API Client control interface */ struct vcap_control { - u32 rule_id; /* last used rule id (unique across VCAP instances) */ struct vcap_operations *ops; /* client supplied operations */ const struct vcap_info *vcaps; /* client supplied vcap models */ const struct vcap_statistics *stats; /* client supplied vcap stats */ From 28e0c250f17ab3b6eb4dcee1a8208125bc77c61e Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Mon, 28 Nov 2022 12:36:39 -0800 Subject: [PATCH 1356/2002] devlink: use min_t to calculate data_size The calculation for the data_size in the devlink_nl_read_snapshot_fill function uses an if statement that is better expressed using the min_t macro. Signed-off-by: Jacob Keller Acked-by: Jakub Kicinski Reviewed-by: Jiri Pirko Signed-off-by: Jakub Kicinski --- net/core/devlink.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/net/core/devlink.c b/net/core/devlink.c index 0e10a8a68c5e..6ef9974f5357 100644 --- a/net/core/devlink.c +++ b/net/core/devlink.c @@ -6485,10 +6485,8 @@ static int devlink_nl_region_read_snapshot_fill(struct sk_buff *skb, u32 data_size; u8 *data; - if (end_offset - curr_offset < DEVLINK_REGION_READ_CHUNK_SIZE) - data_size = end_offset - curr_offset; - else - data_size = DEVLINK_REGION_READ_CHUNK_SIZE; + data_size = min_t(u32, end_offset - curr_offset, + DEVLINK_REGION_READ_CHUNK_SIZE); data = &snapshot->data[curr_offset]; err = devlink_nl_cmd_region_read_chunk_fill(skb, devlink, From 611fd12ce0fbc809d5e54febb1f39e93532c9deb Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Mon, 28 Nov 2022 12:36:40 -0800 Subject: [PATCH 1357/2002] devlink: report extended error message in region_read_dumpit() Report extended error details in the devlink_nl_cmd_region_read_dumpit() function, by using the extack structure from the netlink_callback. Signed-off-by: Jacob Keller Acked-by: Jakub Kicinski Reviewed-by: Jiri Pirko Signed-off-by: Jakub Kicinski --- net/core/devlink.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/net/core/devlink.c b/net/core/devlink.c index 6ef9974f5357..fee69280deb4 100644 --- a/net/core/devlink.c +++ b/net/core/devlink.c @@ -6507,10 +6507,10 @@ static int devlink_nl_cmd_region_read_dumpit(struct sk_buff *skb, { const struct genl_dumpit_info *info = genl_dumpit_info(cb); u64 ret_offset, start_offset, end_offset = U64_MAX; + struct nlattr *chunks_attr, *region_attr; struct nlattr **attrs = info->attrs; struct devlink_port *port = NULL; struct devlink_region *region; - struct nlattr *chunks_attr; const char *region_name; struct devlink *devlink; unsigned int index; @@ -6525,8 +6525,14 @@ static int devlink_nl_cmd_region_read_dumpit(struct sk_buff *skb, devl_lock(devlink); - if (!attrs[DEVLINK_ATTR_REGION_NAME] || - !attrs[DEVLINK_ATTR_REGION_SNAPSHOT_ID]) { + if (!attrs[DEVLINK_ATTR_REGION_NAME]) { + NL_SET_ERR_MSG(cb->extack, "No region name provided"); + err = -EINVAL; + goto out_unlock; + } + + if (!attrs[DEVLINK_ATTR_REGION_SNAPSHOT_ID]) { + NL_SET_ERR_MSG(cb->extack, "No snapshot id provided"); err = -EINVAL; goto out_unlock; } @@ -6541,7 +6547,8 @@ static int devlink_nl_cmd_region_read_dumpit(struct sk_buff *skb, } } - region_name = nla_data(attrs[DEVLINK_ATTR_REGION_NAME]); + region_attr = attrs[DEVLINK_ATTR_REGION_NAME]; + region_name = nla_data(region_attr); if (port) region = devlink_port_region_get_by_name(port, region_name); @@ -6549,6 +6556,7 @@ static int devlink_nl_cmd_region_read_dumpit(struct sk_buff *skb, region = devlink_region_get_by_name(devlink, region_name); if (!region) { + NL_SET_ERR_MSG_ATTR(cb->extack, region_attr, "Requested region does not exist"); err = -EINVAL; goto out_unlock; } From e004ea10599d1a8279e959c86e63fc9d0bf2032b Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Mon, 28 Nov 2022 12:36:41 -0800 Subject: [PATCH 1358/2002] devlink: find snapshot in devlink_nl_cmd_region_read_dumpit The snapshot pointer is obtained inside of the function devlink_nl_region_read_snapshot_fill. Simplify this function by locating the snapshot upfront in devlink_nl_cmd_region_read_dumpit instead. This aligns with how other netlink attributes are handled, and allows us to exit slightly earlier if an invalid snapshot ID is provided. It also allows us to pass the snapshot pointer directly to the devlink_nl_region_read_snapshot_fill, and remove the now unused attrs parameter. Signed-off-by: Jacob Keller Acked-by: Jakub Kicinski Reviewed-by: Jiri Pirko Signed-off-by: Jakub Kicinski --- net/core/devlink.c | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/net/core/devlink.c b/net/core/devlink.c index fee69280deb4..e3166c9b5954 100644 --- a/net/core/devlink.c +++ b/net/core/devlink.c @@ -6463,24 +6463,16 @@ nla_put_failure: static int devlink_nl_region_read_snapshot_fill(struct sk_buff *skb, struct devlink *devlink, - struct devlink_region *region, - struct nlattr **attrs, + struct devlink_snapshot *snapshot, u64 start_offset, u64 end_offset, u64 *new_offset) { - struct devlink_snapshot *snapshot; u64 curr_offset = start_offset; - u32 snapshot_id; int err = 0; *new_offset = start_offset; - snapshot_id = nla_get_u32(attrs[DEVLINK_ATTR_REGION_SNAPSHOT_ID]); - snapshot = devlink_region_snapshot_get_by_id(region, snapshot_id); - if (!snapshot) - return -EINVAL; - while (curr_offset < end_offset) { u32 data_size; u8 *data; @@ -6506,14 +6498,16 @@ static int devlink_nl_cmd_region_read_dumpit(struct sk_buff *skb, struct netlink_callback *cb) { const struct genl_dumpit_info *info = genl_dumpit_info(cb); + struct nlattr *chunks_attr, *region_attr, *snapshot_attr; u64 ret_offset, start_offset, end_offset = U64_MAX; - struct nlattr *chunks_attr, *region_attr; struct nlattr **attrs = info->attrs; struct devlink_port *port = NULL; + struct devlink_snapshot *snapshot; struct devlink_region *region; const char *region_name; struct devlink *devlink; unsigned int index; + u32 snapshot_id; void *hdr; int err; @@ -6561,6 +6555,15 @@ static int devlink_nl_cmd_region_read_dumpit(struct sk_buff *skb, goto out_unlock; } + snapshot_attr = attrs[DEVLINK_ATTR_REGION_SNAPSHOT_ID]; + snapshot_id = nla_get_u32(snapshot_attr); + snapshot = devlink_region_snapshot_get_by_id(region, snapshot_id); + if (!snapshot) { + NL_SET_ERR_MSG_ATTR(cb->extack, snapshot_attr, "Requested snapshot does not exist"); + err = -EINVAL; + goto out_unlock; + } + if (attrs[DEVLINK_ATTR_REGION_CHUNK_ADDR] && attrs[DEVLINK_ATTR_REGION_CHUNK_LEN]) { if (!start_offset) @@ -6610,7 +6613,7 @@ static int devlink_nl_cmd_region_read_dumpit(struct sk_buff *skb, } err = devlink_nl_region_read_snapshot_fill(skb, devlink, - region, attrs, + snapshot, start_offset, end_offset, &ret_offset); From 284e9d1ebbe2944d95120f85054d161a6fccbf7f Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Mon, 28 Nov 2022 12:36:42 -0800 Subject: [PATCH 1359/2002] devlink: remove unnecessary parameter from chunk_fill function The devlink parameter of the devlink_nl_cmd_region_read_chunk_fill function is not used. Remove it, to simplify the function signature. Once removed, it is also obvious that the devlink parameter is not necessary for the devlink_nl_region_read_snapshot_fill either. Signed-off-by: Jacob Keller Acked-by: Jakub Kicinski Reviewed-by: Jiri Pirko Signed-off-by: Jakub Kicinski --- net/core/devlink.c | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/net/core/devlink.c b/net/core/devlink.c index e3166c9b5954..5b8a9bdbc13d 100644 --- a/net/core/devlink.c +++ b/net/core/devlink.c @@ -6431,7 +6431,6 @@ unlock: } static int devlink_nl_cmd_region_read_chunk_fill(struct sk_buff *msg, - struct devlink *devlink, u8 *chunk, u32 chunk_size, u64 addr) { @@ -6462,7 +6461,6 @@ nla_put_failure: #define DEVLINK_REGION_READ_CHUNK_SIZE 256 static int devlink_nl_region_read_snapshot_fill(struct sk_buff *skb, - struct devlink *devlink, struct devlink_snapshot *snapshot, u64 start_offset, u64 end_offset, @@ -6481,9 +6479,7 @@ static int devlink_nl_region_read_snapshot_fill(struct sk_buff *skb, DEVLINK_REGION_READ_CHUNK_SIZE); data = &snapshot->data[curr_offset]; - err = devlink_nl_cmd_region_read_chunk_fill(skb, devlink, - data, data_size, - curr_offset); + err = devlink_nl_cmd_region_read_chunk_fill(skb, data, data_size, curr_offset); if (err) break; @@ -6612,9 +6608,7 @@ static int devlink_nl_cmd_region_read_dumpit(struct sk_buff *skb, goto nla_put_failure; } - err = devlink_nl_region_read_snapshot_fill(skb, devlink, - snapshot, - start_offset, + err = devlink_nl_region_read_snapshot_fill(skb, snapshot, start_offset, end_offset, &ret_offset); if (err && err != -EMSGSIZE) From 2d4caf0988bd3e9663c300e95cead91f2f954fae Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Mon, 28 Nov 2022 12:36:43 -0800 Subject: [PATCH 1360/2002] devlink: refactor region_read_snapshot_fill to use a callback function The devlink_nl_region_read_snapshot_fill is used to copy the contents of a snapshot into a message for reporting to userspace via the DEVLINK_CMG_REGION_READ netlink message. A future change is going to add support for directly reading from a region. Almost all of the logic for this new capability is identical. To help reduce code duplication and make this logic more generic, refactor the function to take a cb and cb_priv pointer for doing the actual copy. Add a devlink_region_snapshot_fill implementation that will simply copy the relevant chunk of the region. This does require allocating some storage for the chunk as opposed to simply passing the correct address forward to the devlink_nl_cmg_region_read_chunk_fill function. A future change to implement support for directly reading from a region without a snapshot will provide a separate implementation that calls the newly added devlink region operation. Signed-off-by: Jacob Keller Acked-by: Jakub Kicinski Reviewed-by: Jiri Pirko Signed-off-by: Jakub Kicinski --- net/core/devlink.c | 44 +++++++++++++++++++++++++++++++++++--------- 1 file changed, 35 insertions(+), 9 deletions(-) diff --git a/net/core/devlink.c b/net/core/devlink.c index 5b8a9bdbc13d..6c05cfaa571d 100644 --- a/net/core/devlink.c +++ b/net/core/devlink.c @@ -6460,25 +6460,36 @@ nla_put_failure: #define DEVLINK_REGION_READ_CHUNK_SIZE 256 -static int devlink_nl_region_read_snapshot_fill(struct sk_buff *skb, - struct devlink_snapshot *snapshot, - u64 start_offset, - u64 end_offset, - u64 *new_offset) +typedef int devlink_chunk_fill_t(void *cb_priv, u8 *chunk, u32 chunk_size, + u64 curr_offset, + struct netlink_ext_ack *extack); + +static int +devlink_nl_region_read_fill(struct sk_buff *skb, devlink_chunk_fill_t *cb, + void *cb_priv, u64 start_offset, u64 end_offset, + u64 *new_offset, struct netlink_ext_ack *extack) { u64 curr_offset = start_offset; int err = 0; + u8 *data; + + /* Allocate and re-use a single buffer */ + data = kmalloc(DEVLINK_REGION_READ_CHUNK_SIZE, GFP_KERNEL); + if (!data) + return -ENOMEM; *new_offset = start_offset; while (curr_offset < end_offset) { u32 data_size; - u8 *data; data_size = min_t(u32, end_offset - curr_offset, DEVLINK_REGION_READ_CHUNK_SIZE); - data = &snapshot->data[curr_offset]; + err = cb(cb_priv, data, data_size, curr_offset, extack); + if (err) + break; + err = devlink_nl_cmd_region_read_chunk_fill(skb, data, data_size, curr_offset); if (err) break; @@ -6487,9 +6498,23 @@ static int devlink_nl_region_read_snapshot_fill(struct sk_buff *skb, } *new_offset = curr_offset; + kfree(data); + return err; } +static int +devlink_region_snapshot_fill(void *cb_priv, u8 *chunk, u32 chunk_size, + u64 curr_offset, + struct netlink_ext_ack __always_unused *extack) +{ + struct devlink_snapshot *snapshot = cb_priv; + + memcpy(chunk, &snapshot->data[curr_offset], chunk_size); + + return 0; +} + static int devlink_nl_cmd_region_read_dumpit(struct sk_buff *skb, struct netlink_callback *cb) { @@ -6608,8 +6633,9 @@ static int devlink_nl_cmd_region_read_dumpit(struct sk_buff *skb, goto nla_put_failure; } - err = devlink_nl_region_read_snapshot_fill(skb, snapshot, start_offset, - end_offset, &ret_offset); + err = devlink_nl_region_read_fill(skb, &devlink_region_snapshot_fill, + snapshot, start_offset, end_offset, + &ret_offset, cb->extack); if (err && err != -EMSGSIZE) goto nla_put_failure; From af6397c9ee2b42988c912dcad2fca1f43d5c1c99 Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Mon, 28 Nov 2022 12:36:44 -0800 Subject: [PATCH 1361/2002] devlink: support directly reading from region memory To read from a region, user space must currently request a new snapshot of the region and then read from that snapshot. This can sometimes be overkill if user space only reads a tiny portion. They first create the snapshot, then request a read, then destroy the snapshot. For regions which have a single underlying "contents", it makes sense to allow supporting direct reading of the region data. Extend the DEVLINK_CMD_REGION_READ to allow direct reading from a region if requested via the new DEVLINK_ATTR_REGION_DIRECT. If this attribute is set, then perform a direct read instead of using a snapshot. Direct read is mutually exclusive with DEVLINK_ATTR_REGION_SNAPSHOT_ID, and care is taken to ensure that we reject commands which provide incorrect attributes. Regions must enable support for direct read by implementing the .read() callback function. If a region does not support such direct reads, a suitable extended error message is reported. Signed-off-by: Jacob Keller Reviewed-by: Jiri Pirko Signed-off-by: Jakub Kicinski --- .../networking/devlink/devlink-region.rst | 13 +++ include/net/devlink.h | 16 ++++ include/uapi/linux/devlink.h | 2 + net/core/devlink.c | 80 +++++++++++++++---- 4 files changed, 94 insertions(+), 17 deletions(-) diff --git a/Documentation/networking/devlink/devlink-region.rst b/Documentation/networking/devlink/devlink-region.rst index f06dca9a1eb6..9232cd7da301 100644 --- a/Documentation/networking/devlink/devlink-region.rst +++ b/Documentation/networking/devlink/devlink-region.rst @@ -31,6 +31,15 @@ in its ``devlink_region_ops`` structure. If snapshot id is not set in the ``DEVLINK_CMD_REGION_NEW`` request kernel will allocate one and send the snapshot information to user space. +Regions may optionally allow directly reading from their contents without a +snapshot. Direct read requests are not atomic. In particular a read request +of size 256 bytes or larger will be split into multiple chunks. If atomic +access is required, use a snapshot. A driver wishing to enable this for a +region should implement the ``.read`` callback in the ``devlink_region_ops`` +structure. User space can request a direct read by using the +``DEVLINK_ATTR_REGION_DIRECT`` attribute instead of specifying a snapshot +id. + example usage ------------- @@ -65,6 +74,10 @@ example usage $ devlink region read pci/0000:00:05.0/fw-health snapshot 1 address 0 length 16 0000000000000000 0014 95dc 0014 9514 0035 1670 0034 db30 + # Read from the region without a snapshot + $ devlink region read pci/0000:00:05.0/fw-health address 16 length 16 + 0000000000000010 0000 0000 ffff ff04 0029 8c00 0028 8cc8 + As regions are likely very device or driver specific, no generic regions are defined. See the driver-specific documentation files for information on the specific regions a driver supports. diff --git a/include/net/devlink.h b/include/net/devlink.h index 074a79b8933f..02528f736f65 100644 --- a/include/net/devlink.h +++ b/include/net/devlink.h @@ -650,6 +650,10 @@ struct devlink_info_req; * the data variable must be updated to point to the snapshot data. * The function will be called while the devlink instance lock is * held. + * @read: callback to directly read a portion of the region. On success, + * the data pointer will be updated with the contents of the + * requested portion of the region. The function will be called + * while the devlink instance lock is held. * @priv: Pointer to driver private data for the region operation */ struct devlink_region_ops { @@ -659,6 +663,10 @@ struct devlink_region_ops { const struct devlink_region_ops *ops, struct netlink_ext_ack *extack, u8 **data); + int (*read)(struct devlink *devlink, + const struct devlink_region_ops *ops, + struct netlink_ext_ack *extack, + u64 offset, u32 size, u8 *data); void *priv; }; @@ -670,6 +678,10 @@ struct devlink_region_ops { * the data variable must be updated to point to the snapshot data. * The function will be called while the devlink instance lock is * held. + * @read: callback to directly read a portion of the region. On success, + * the data pointer will be updated with the contents of the + * requested portion of the region. The function will be called + * while the devlink instance lock is held. * @priv: Pointer to driver private data for the region operation */ struct devlink_port_region_ops { @@ -679,6 +691,10 @@ struct devlink_port_region_ops { const struct devlink_port_region_ops *ops, struct netlink_ext_ack *extack, u8 **data); + int (*read)(struct devlink_port *port, + const struct devlink_port_region_ops *ops, + struct netlink_ext_ack *extack, + u64 offset, u32 size, u8 *data); void *priv; }; diff --git a/include/uapi/linux/devlink.h b/include/uapi/linux/devlink.h index 498d0d5d0957..70191d96af89 100644 --- a/include/uapi/linux/devlink.h +++ b/include/uapi/linux/devlink.h @@ -610,6 +610,8 @@ enum devlink_attr { DEVLINK_ATTR_RATE_TX_PRIORITY, /* u32 */ DEVLINK_ATTR_RATE_TX_WEIGHT, /* u32 */ + DEVLINK_ATTR_REGION_DIRECT, /* flag */ + /* add new attributes above here, update the policy in devlink.c */ __DEVLINK_ATTR_MAX, diff --git a/net/core/devlink.c b/net/core/devlink.c index 6c05cfaa571d..298041a44aa8 100644 --- a/net/core/devlink.c +++ b/net/core/devlink.c @@ -6515,6 +6515,26 @@ devlink_region_snapshot_fill(void *cb_priv, u8 *chunk, u32 chunk_size, return 0; } +static int +devlink_region_port_direct_fill(void *cb_priv, u8 *chunk, u32 chunk_size, + u64 curr_offset, struct netlink_ext_ack *extack) +{ + struct devlink_region *region = cb_priv; + + return region->port_ops->read(region->port, region->port_ops, extack, + curr_offset, chunk_size, chunk); +} + +static int +devlink_region_direct_fill(void *cb_priv, u8 *chunk, u32 chunk_size, + u64 curr_offset, struct netlink_ext_ack *extack) +{ + struct devlink_region *region = cb_priv; + + return region->ops->read(region->devlink, region->ops, extack, + curr_offset, chunk_size, chunk); +} + static int devlink_nl_cmd_region_read_dumpit(struct sk_buff *skb, struct netlink_callback *cb) { @@ -6523,12 +6543,12 @@ static int devlink_nl_cmd_region_read_dumpit(struct sk_buff *skb, u64 ret_offset, start_offset, end_offset = U64_MAX; struct nlattr **attrs = info->attrs; struct devlink_port *port = NULL; - struct devlink_snapshot *snapshot; + devlink_chunk_fill_t *region_cb; struct devlink_region *region; const char *region_name; struct devlink *devlink; unsigned int index; - u32 snapshot_id; + void *region_cb_priv; void *hdr; int err; @@ -6546,12 +6566,6 @@ static int devlink_nl_cmd_region_read_dumpit(struct sk_buff *skb, goto out_unlock; } - if (!attrs[DEVLINK_ATTR_REGION_SNAPSHOT_ID]) { - NL_SET_ERR_MSG(cb->extack, "No snapshot id provided"); - err = -EINVAL; - goto out_unlock; - } - if (info->attrs[DEVLINK_ATTR_PORT_INDEX]) { index = nla_get_u32(info->attrs[DEVLINK_ATTR_PORT_INDEX]); @@ -6577,12 +6591,43 @@ static int devlink_nl_cmd_region_read_dumpit(struct sk_buff *skb, } snapshot_attr = attrs[DEVLINK_ATTR_REGION_SNAPSHOT_ID]; - snapshot_id = nla_get_u32(snapshot_attr); - snapshot = devlink_region_snapshot_get_by_id(region, snapshot_id); - if (!snapshot) { - NL_SET_ERR_MSG_ATTR(cb->extack, snapshot_attr, "Requested snapshot does not exist"); - err = -EINVAL; - goto out_unlock; + if (!snapshot_attr) { + if (!nla_get_flag(attrs[DEVLINK_ATTR_REGION_DIRECT])) { + NL_SET_ERR_MSG(cb->extack, "No snapshot id provided"); + err = -EINVAL; + goto out_unlock; + } + + if (!region->ops->read) { + NL_SET_ERR_MSG(cb->extack, "Requested region does not support direct read"); + err = -EOPNOTSUPP; + goto out_unlock; + } + + if (port) + region_cb = &devlink_region_port_direct_fill; + else + region_cb = &devlink_region_direct_fill; + region_cb_priv = region; + } else { + struct devlink_snapshot *snapshot; + u32 snapshot_id; + + if (nla_get_flag(attrs[DEVLINK_ATTR_REGION_DIRECT])) { + NL_SET_ERR_MSG_ATTR(cb->extack, snapshot_attr, "Direct region read does not use snapshot"); + err = -EINVAL; + goto out_unlock; + } + + snapshot_id = nla_get_u32(snapshot_attr); + snapshot = devlink_region_snapshot_get_by_id(region, snapshot_id); + if (!snapshot) { + NL_SET_ERR_MSG_ATTR(cb->extack, snapshot_attr, "Requested snapshot does not exist"); + err = -EINVAL; + goto out_unlock; + } + region_cb = &devlink_region_snapshot_fill; + region_cb_priv = snapshot; } if (attrs[DEVLINK_ATTR_REGION_CHUNK_ADDR] && @@ -6633,9 +6678,9 @@ static int devlink_nl_cmd_region_read_dumpit(struct sk_buff *skb, goto nla_put_failure; } - err = devlink_nl_region_read_fill(skb, &devlink_region_snapshot_fill, - snapshot, start_offset, end_offset, - &ret_offset, cb->extack); + err = devlink_nl_region_read_fill(skb, region_cb, region_cb_priv, + start_offset, end_offset, &ret_offset, + cb->extack); if (err && err != -EMSGSIZE) goto nla_put_failure; @@ -9280,6 +9325,7 @@ static const struct nla_policy devlink_nl_policy[DEVLINK_ATTR_MAX + 1] = { [DEVLINK_ATTR_SELFTESTS] = { .type = NLA_NESTED }, [DEVLINK_ATTR_RATE_TX_PRIORITY] = { .type = NLA_U32 }, [DEVLINK_ATTR_RATE_TX_WEIGHT] = { .type = NLA_U32 }, + [DEVLINK_ATTR_REGION_DIRECT] = { .type = NLA_FLAG }, }; static const struct genl_small_ops devlink_nl_ops[] = { From ed23debec5d197956fbda4ad8fad66d910c6a6b6 Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Mon, 28 Nov 2022 12:36:45 -0800 Subject: [PATCH 1362/2002] ice: use same function to snapshot both NVM and Shadow RAM The ice driver supports a region for both the flat NVM contents as well as the Shadow RAM which is a layer built on top of the flash during device initialization. These regions use an almost identical read function, except that the NVM needs to set the direct flag when reading, while Shadow RAM needs to read without the direct flag set. They each call ice_read_flat_nvm with the only difference being whether to set the direct flash flag. The NVM region read function also was fixed to read the NVM in blocks to avoid a situation where the firmware reclaims the lock due to taking too long. Note that the region snapshot function takes the ops pointer so the function can easily determine which region to read. Make use of this and re-use the NVM snapshot function for both the NVM and Shadow RAM regions. This makes Shadow RAM benefit from the same block approach as the NVM region. It also reduces code in the ice driver. Signed-off-by: Jacob Keller Acked-by: Jakub Kicinski Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/intel/ice/ice_devlink.c | 95 +++++--------------- 1 file changed, 23 insertions(+), 72 deletions(-) diff --git a/drivers/net/ethernet/intel/ice/ice_devlink.c b/drivers/net/ethernet/intel/ice/ice_devlink.c index 1d638216484d..9d3500291d2e 100644 --- a/drivers/net/ethernet/intel/ice/ice_devlink.c +++ b/drivers/net/ethernet/intel/ice/ice_devlink.c @@ -1596,21 +1596,22 @@ void ice_devlink_destroy_vf_port(struct ice_vf *vf) #define ICE_DEVLINK_READ_BLK_SIZE (1024 * 1024) +static const struct devlink_region_ops ice_nvm_region_ops; +static const struct devlink_region_ops ice_sram_region_ops; + /** * ice_devlink_nvm_snapshot - Capture a snapshot of the NVM flash contents * @devlink: the devlink instance - * @ops: the devlink region being snapshotted + * @ops: the devlink region to snapshot * @extack: extended ACK response structure * @data: on exit points to snapshot data buffer * - * This function is called in response to the DEVLINK_CMD_REGION_TRIGGER for - * the nvm-flash devlink region. It captures a snapshot of the full NVM flash - * contents, including both banks of flash. This snapshot can later be viewed - * via the devlink-region interface. + * This function is called in response to a DEVLINK_CMD_REGION_NEW for either + * the nvm-flash or shadow-ram region. * - * It captures the flash using the FLASH_ONLY bit set when reading via - * firmware, so it does not read the current Shadow RAM contents. For that, - * use the shadow-ram region. + * It captures a snapshot of the NVM or Shadow RAM flash contents. This + * snapshot can then later be viewed via the DEVLINK_CMD_REGION_READ netlink + * interface. * * @returns zero on success, and updates the data pointer. Returns a non-zero * error code on failure. @@ -1622,17 +1623,27 @@ static int ice_devlink_nvm_snapshot(struct devlink *devlink, struct ice_pf *pf = devlink_priv(devlink); struct device *dev = ice_pf_to_dev(pf); struct ice_hw *hw = &pf->hw; + bool read_shadow_ram; u8 *nvm_data, *tmp, i; u32 nvm_size, left; s8 num_blks; int status; - nvm_size = hw->flash.flash_size; + if (ops == &ice_nvm_region_ops) { + read_shadow_ram = false; + nvm_size = hw->flash.flash_size; + } else if (ops == &ice_sram_region_ops) { + read_shadow_ram = true; + nvm_size = hw->flash.sr_words * 2u; + } else { + NL_SET_ERR_MSG_MOD(extack, "Unexpected region in snapshot function"); + return -EOPNOTSUPP; + } + nvm_data = vzalloc(nvm_size); if (!nvm_data) return -ENOMEM; - num_blks = DIV_ROUND_UP(nvm_size, ICE_DEVLINK_READ_BLK_SIZE); tmp = nvm_data; left = nvm_size; @@ -1656,7 +1667,7 @@ static int ice_devlink_nvm_snapshot(struct devlink *devlink, } status = ice_read_flat_nvm(hw, i * ICE_DEVLINK_READ_BLK_SIZE, - &read_sz, tmp, false); + &read_sz, tmp, read_shadow_ram); if (status) { dev_dbg(dev, "ice_read_flat_nvm failed after reading %u bytes, err %d aq_err %d\n", read_sz, status, hw->adminq.sq_last_status); @@ -1676,66 +1687,6 @@ static int ice_devlink_nvm_snapshot(struct devlink *devlink, return 0; } -/** - * ice_devlink_sram_snapshot - Capture a snapshot of the Shadow RAM contents - * @devlink: the devlink instance - * @ops: the devlink region being snapshotted - * @extack: extended ACK response structure - * @data: on exit points to snapshot data buffer - * - * This function is called in response to the DEVLINK_CMD_REGION_TRIGGER for - * the shadow-ram devlink region. It captures a snapshot of the shadow ram - * contents. This snapshot can later be viewed via the devlink-region - * interface. - * - * @returns zero on success, and updates the data pointer. Returns a non-zero - * error code on failure. - */ -static int -ice_devlink_sram_snapshot(struct devlink *devlink, - const struct devlink_region_ops __always_unused *ops, - struct netlink_ext_ack *extack, u8 **data) -{ - struct ice_pf *pf = devlink_priv(devlink); - struct device *dev = ice_pf_to_dev(pf); - struct ice_hw *hw = &pf->hw; - u8 *sram_data; - u32 sram_size; - int err; - - sram_size = hw->flash.sr_words * 2u; - sram_data = vzalloc(sram_size); - if (!sram_data) - return -ENOMEM; - - err = ice_acquire_nvm(hw, ICE_RES_READ); - if (err) { - dev_dbg(dev, "ice_acquire_nvm failed, err %d aq_err %d\n", - err, hw->adminq.sq_last_status); - NL_SET_ERR_MSG_MOD(extack, "Failed to acquire NVM semaphore"); - vfree(sram_data); - return err; - } - - /* Read from the Shadow RAM, rather than directly from NVM */ - err = ice_read_flat_nvm(hw, 0, &sram_size, sram_data, true); - if (err) { - dev_dbg(dev, "ice_read_flat_nvm failed after reading %u bytes, err %d aq_err %d\n", - sram_size, err, hw->adminq.sq_last_status); - NL_SET_ERR_MSG_MOD(extack, - "Failed to read Shadow RAM contents"); - ice_release_nvm(hw); - vfree(sram_data); - return err; - } - - ice_release_nvm(hw); - - *data = sram_data; - - return 0; -} - /** * ice_devlink_devcaps_snapshot - Capture snapshot of device capabilities * @devlink: the devlink instance @@ -1789,7 +1740,7 @@ static const struct devlink_region_ops ice_nvm_region_ops = { static const struct devlink_region_ops ice_sram_region_ops = { .name = "shadow-ram", .destructor = vfree, - .snapshot = ice_devlink_sram_snapshot, + .snapshot = ice_devlink_nvm_snapshot, }; static const struct devlink_region_ops ice_devcaps_region_ops = { From 2d0197843f9e23c72e3c34303c3a4832bd81b3c3 Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Mon, 28 Nov 2022 12:36:46 -0800 Subject: [PATCH 1363/2002] ice: document 'shadow-ram' devlink region 78ad87da9978 ("ice: devlink: add shadow-ram region to snapshot Shadow RAM") added support for the 'shadow-ram' devlink region, but did not document it in the ice devlink documentation. Fix this. Signed-off-by: Jacob Keller Acked-by: Jakub Kicinski Signed-off-by: Jakub Kicinski --- Documentation/networking/devlink/ice.rst | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Documentation/networking/devlink/ice.rst b/Documentation/networking/devlink/ice.rst index 890062da7820..bcd4839fbf79 100644 --- a/Documentation/networking/devlink/ice.rst +++ b/Documentation/networking/devlink/ice.rst @@ -189,6 +189,11 @@ device data. * - ``nvm-flash`` - The contents of the entire flash chip, sometimes referred to as the device's Non Volatile Memory. + * - ``shadow-ram`` + - The contents of the Shadow RAM, which is loaded from the beginning + of the flash. Although the contents are primarily from the flash, + this area also contains data generated during device boot which is + not stored in flash. * - ``device-caps`` - The contents of the device firmware's capabilities buffer. Useful to determine the current state and configuration of the device. From 3af4b40b0f2f194d32e90cc5d25ebb3daa96cd1f Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Mon, 28 Nov 2022 12:36:47 -0800 Subject: [PATCH 1364/2002] ice: implement direct read for NVM and Shadow RAM regions Implement the .read handler for the NVM and Shadow RAM regions. This enables user space to read a small chunk of the flash without needing the overhead of creating a full snapshot. Update the documentation for ice to detail which regions have direct read support. Signed-off-by: Jacob Keller Acked-by: Jakub Kicinski Signed-off-by: Jakub Kicinski --- Documentation/networking/devlink/ice.rst | 8 ++- drivers/net/ethernet/intel/ice/ice_devlink.c | 69 ++++++++++++++++++++ 2 files changed, 75 insertions(+), 2 deletions(-) diff --git a/Documentation/networking/devlink/ice.rst b/Documentation/networking/devlink/ice.rst index bcd4839fbf79..625efb3777d5 100644 --- a/Documentation/networking/devlink/ice.rst +++ b/Documentation/networking/devlink/ice.rst @@ -198,8 +198,12 @@ device data. - The contents of the device firmware's capabilities buffer. Useful to determine the current state and configuration of the device. -Users can request an immediate capture of a snapshot via the -``DEVLINK_CMD_REGION_NEW`` +Both the ``nvm-flash`` and ``shadow-ram`` regions can be accessed without a +snapshot. The ``device-caps`` region requires a snapshot as the contents are +sent by firmware and can't be split into separate reads. + +Users can request an immediate capture of a snapshot for all three regions +via the ``DEVLINK_CMD_REGION_NEW`` command. .. code:: shell diff --git a/drivers/net/ethernet/intel/ice/ice_devlink.c b/drivers/net/ethernet/intel/ice/ice_devlink.c index 9d3500291d2e..946d64e577c9 100644 --- a/drivers/net/ethernet/intel/ice/ice_devlink.c +++ b/drivers/net/ethernet/intel/ice/ice_devlink.c @@ -1687,6 +1687,73 @@ static int ice_devlink_nvm_snapshot(struct devlink *devlink, return 0; } +/** + * ice_devlink_nvm_read - Read a portion of NVM flash contents + * @devlink: the devlink instance + * @ops: the devlink region to snapshot + * @extack: extended ACK response structure + * @offset: the offset to start at + * @size: the amount to read + * @data: the data buffer to read into + * + * This function is called in response to DEVLINK_CMD_REGION_READ to directly + * read a section of the NVM contents. + * + * It reads from either the nvm-flash or shadow-ram region contents. + * + * @returns zero on success, and updates the data pointer. Returns a non-zero + * error code on failure. + */ +static int ice_devlink_nvm_read(struct devlink *devlink, + const struct devlink_region_ops *ops, + struct netlink_ext_ack *extack, + u64 offset, u32 size, u8 *data) +{ + struct ice_pf *pf = devlink_priv(devlink); + struct device *dev = ice_pf_to_dev(pf); + struct ice_hw *hw = &pf->hw; + bool read_shadow_ram; + u64 nvm_size; + int status; + + if (ops == &ice_nvm_region_ops) { + read_shadow_ram = false; + nvm_size = hw->flash.flash_size; + } else if (ops == &ice_sram_region_ops) { + read_shadow_ram = true; + nvm_size = hw->flash.sr_words * 2u; + } else { + NL_SET_ERR_MSG_MOD(extack, "Unexpected region in snapshot function"); + return -EOPNOTSUPP; + } + + if (offset + size >= nvm_size) { + NL_SET_ERR_MSG_MOD(extack, "Cannot read beyond the region size"); + return -ERANGE; + } + + status = ice_acquire_nvm(hw, ICE_RES_READ); + if (status) { + dev_dbg(dev, "ice_acquire_nvm failed, err %d aq_err %d\n", + status, hw->adminq.sq_last_status); + NL_SET_ERR_MSG_MOD(extack, "Failed to acquire NVM semaphore"); + return -EIO; + } + + status = ice_read_flat_nvm(hw, (u32)offset, &size, data, + read_shadow_ram); + if (status) { + dev_dbg(dev, "ice_read_flat_nvm failed after reading %u bytes, err %d aq_err %d\n", + size, status, hw->adminq.sq_last_status); + NL_SET_ERR_MSG_MOD(extack, "Failed to read NVM contents"); + ice_release_nvm(hw); + return -EIO; + } + ice_release_nvm(hw); + + return 0; +} + /** * ice_devlink_devcaps_snapshot - Capture snapshot of device capabilities * @devlink: the devlink instance @@ -1735,12 +1802,14 @@ static const struct devlink_region_ops ice_nvm_region_ops = { .name = "nvm-flash", .destructor = vfree, .snapshot = ice_devlink_nvm_snapshot, + .read = ice_devlink_nvm_read, }; static const struct devlink_region_ops ice_sram_region_ops = { .name = "shadow-ram", .destructor = vfree, .snapshot = ice_devlink_nvm_snapshot, + .read = ice_devlink_nvm_read, }; static const struct devlink_region_ops ice_devcaps_region_ops = { From a933e7f05bd413bcfd5fb103ad86519526a49446 Mon Sep 17 00:00:00 2001 From: Luca Weiss Date: Mon, 28 Nov 2022 18:37:43 +0100 Subject: [PATCH 1365/2002] dt-bindings: nfc: nxp,nci: Document NQ310 compatible The NQ310 is another NFC chip from NXP, document the compatible in the bindings. Signed-off-by: Luca Weiss Acked-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20221128173744.833018-1-luca@z3ntu.xyz Signed-off-by: Jakub Kicinski --- Documentation/devicetree/bindings/net/nfc/nxp,nci.yaml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/net/nfc/nxp,nci.yaml b/Documentation/devicetree/bindings/net/nfc/nxp,nci.yaml index b2558421268a..6924aff0b2c5 100644 --- a/Documentation/devicetree/bindings/net/nfc/nxp,nci.yaml +++ b/Documentation/devicetree/bindings/net/nfc/nxp,nci.yaml @@ -14,7 +14,9 @@ properties: oneOf: - const: nxp,nxp-nci-i2c - items: - - const: nxp,pn547 + - enum: + - nxp,nq310 + - nxp,pn547 - const: nxp,nxp-nci-i2c enable-gpios: From 226bf980550627c88549b112ac6c8fb40873afb4 Mon Sep 17 00:00:00 2001 From: Vincent Mailhol Date: Tue, 29 Nov 2022 18:51:38 +0900 Subject: [PATCH 1366/2002] net: devlink: let the core report the driver name instead of the drivers The driver name is available in device_driver::name. Right now, drivers still have to report this piece of information themselves in their devlink_ops::info_get callback function. In order to factorize code, make devlink_nl_info_fill() add the driver name attribute. Now that the core sets the driver name attribute, drivers are not supposed to call devlink_info_driver_name_put() anymore. Remove devlink_info_driver_name_put() and clean-up all the drivers using this function in their callback. Signed-off-by: Vincent Mailhol Tested-by: Ido Schimmel # mlxsw Reviewed-by: Jacob Keller Reviewed-by: Jiri Pirko Signed-off-by: Jakub Kicinski --- .../marvell/octeontx2/otx2_cpt_devlink.c | 4 --- drivers/net/dsa/hirschmann/hellcreek.c | 5 ---- drivers/net/dsa/mv88e6xxx/devlink.c | 5 ---- drivers/net/dsa/sja1105/sja1105_devlink.c | 12 +++------ .../net/ethernet/broadcom/bnxt/bnxt_devlink.c | 4 --- .../freescale/dpaa2/dpaa2-eth-devlink.c | 11 +------- .../ethernet/fungible/funeth/funeth_devlink.c | 2 +- .../hisilicon/hns3/hns3pf/hclge_devlink.c | 5 ---- .../hisilicon/hns3/hns3vf/hclgevf_devlink.c | 5 ---- drivers/net/ethernet/intel/ice/ice_devlink.c | 6 ----- .../marvell/octeontx2/af/rvu_devlink.c | 2 +- .../marvell/octeontx2/nic/otx2_devlink.c | 9 +------ .../marvell/prestera/prestera_devlink.c | 5 ---- .../net/ethernet/mellanox/mlx5/core/devlink.c | 4 --- drivers/net/ethernet/mellanox/mlxsw/core.c | 5 ---- .../net/ethernet/netronome/nfp/nfp_devlink.c | 4 --- .../ethernet/pensando/ionic/ionic_devlink.c | 4 --- drivers/net/ethernet/qlogic/qed/qed_devlink.c | 4 --- drivers/net/netdevsim/dev.c | 3 --- drivers/ptp/ptp_ocp.c | 4 --- include/net/devlink.h | 2 -- net/core/devlink.c | 26 +++++++++++++------ 22 files changed, 25 insertions(+), 106 deletions(-) diff --git a/drivers/crypto/marvell/octeontx2/otx2_cpt_devlink.c b/drivers/crypto/marvell/octeontx2/otx2_cpt_devlink.c index 7503f6b18ac5..a2aba0b0d68a 100644 --- a/drivers/crypto/marvell/octeontx2/otx2_cpt_devlink.c +++ b/drivers/crypto/marvell/octeontx2/otx2_cpt_devlink.c @@ -76,10 +76,6 @@ static int otx2_cpt_devlink_info_get(struct devlink *dl, struct otx2_cptpf_dev *cptpf = cpt_dl->cptpf; int err; - err = devlink_info_driver_name_put(req, "rvu_cptpf"); - if (err) - return err; - err = otx2_cpt_dl_info_firmware_version_put(req, cptpf->eng_grps.grp, "fw.ae", OTX2_CPT_AE_TYPES); if (err) diff --git a/drivers/net/dsa/hirschmann/hellcreek.c b/drivers/net/dsa/hirschmann/hellcreek.c index 951f7935c872..595a548bb0a8 100644 --- a/drivers/net/dsa/hirschmann/hellcreek.c +++ b/drivers/net/dsa/hirschmann/hellcreek.c @@ -1176,11 +1176,6 @@ static int hellcreek_devlink_info_get(struct dsa_switch *ds, struct netlink_ext_ack *extack) { struct hellcreek *hellcreek = ds->priv; - int ret; - - ret = devlink_info_driver_name_put(req, "hellcreek"); - if (ret) - return ret; return devlink_info_version_fixed_put(req, DEVLINK_INFO_VERSION_GENERIC_ASIC_ID, diff --git a/drivers/net/dsa/mv88e6xxx/devlink.c b/drivers/net/dsa/mv88e6xxx/devlink.c index 1266eabee086..a08dab75e0c0 100644 --- a/drivers/net/dsa/mv88e6xxx/devlink.c +++ b/drivers/net/dsa/mv88e6xxx/devlink.c @@ -821,11 +821,6 @@ int mv88e6xxx_devlink_info_get(struct dsa_switch *ds, struct netlink_ext_ack *extack) { struct mv88e6xxx_chip *chip = ds->priv; - int err; - - err = devlink_info_driver_name_put(req, "mv88e6xxx"); - if (err) - return err; return devlink_info_version_fixed_put(req, DEVLINK_INFO_VERSION_GENERIC_ASIC_ID, diff --git a/drivers/net/dsa/sja1105/sja1105_devlink.c b/drivers/net/dsa/sja1105/sja1105_devlink.c index 10c6fea1227f..da532614f34a 100644 --- a/drivers/net/dsa/sja1105/sja1105_devlink.c +++ b/drivers/net/dsa/sja1105/sja1105_devlink.c @@ -120,16 +120,10 @@ int sja1105_devlink_info_get(struct dsa_switch *ds, struct netlink_ext_ack *extack) { struct sja1105_private *priv = ds->priv; - int rc; - rc = devlink_info_driver_name_put(req, "sja1105"); - if (rc) - return rc; - - rc = devlink_info_version_fixed_put(req, - DEVLINK_INFO_VERSION_GENERIC_ASIC_ID, - priv->info->name); - return rc; + return devlink_info_version_fixed_put(req, + DEVLINK_INFO_VERSION_GENERIC_ASIC_ID, + priv->info->name); } int sja1105_devlink_setup(struct dsa_switch *ds) diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_devlink.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_devlink.c index 8a6f788f6294..26913dc816d3 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_devlink.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_devlink.c @@ -892,10 +892,6 @@ static int bnxt_dl_info_get(struct devlink *dl, struct devlink_info_req *req, u32 ver = 0; int rc; - rc = devlink_info_driver_name_put(req, DRV_MODULE_NAME); - if (rc) - return rc; - if (BNXT_PF(bp) && (bp->flags & BNXT_FLAG_DSN_VALID)) { sprintf(buf, "%02X-%02X-%02X-%02X-%02X-%02X-%02X-%02X", bp->dsn[7], bp->dsn[6], bp->dsn[5], bp->dsn[4], diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth-devlink.c b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth-devlink.c index 5c6dd3029e2f..76f808d38066 100644 --- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth-devlink.c +++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth-devlink.c @@ -37,18 +37,9 @@ static int dpaa2_eth_dl_info_get(struct devlink *devlink, struct dpaa2_eth_devlink_priv *dl_priv = devlink_priv(devlink); struct dpaa2_eth_priv *priv = dl_priv->dpaa2_priv; char buf[10]; - int err; - - err = devlink_info_driver_name_put(req, KBUILD_MODNAME); - if (err) - return err; scnprintf(buf, 10, "%d.%d", priv->dpni_ver_major, priv->dpni_ver_minor); - err = devlink_info_version_running_put(req, "dpni", buf); - if (err) - return err; - - return 0; + return devlink_info_version_running_put(req, "dpni", buf); } static struct dpaa2_eth_trap_item * diff --git a/drivers/net/ethernet/fungible/funeth/funeth_devlink.c b/drivers/net/ethernet/fungible/funeth/funeth_devlink.c index d50c222948b4..6668375edff6 100644 --- a/drivers/net/ethernet/fungible/funeth/funeth_devlink.c +++ b/drivers/net/ethernet/fungible/funeth/funeth_devlink.c @@ -6,7 +6,7 @@ static int fun_dl_info_get(struct devlink *dl, struct devlink_info_req *req, struct netlink_ext_ack *extack) { - return devlink_info_driver_name_put(req, KBUILD_MODNAME); + return 0; } static const struct devlink_ops fun_dl_ops = { diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_devlink.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_devlink.c index 4c441e6a5082..3d3b69605423 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_devlink.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_devlink.c @@ -13,11 +13,6 @@ static int hclge_devlink_info_get(struct devlink *devlink, struct hclge_devlink_priv *priv = devlink_priv(devlink); char version_str[HCLGE_DEVLINK_FW_STRING_LEN]; struct hclge_dev *hdev = priv->hdev; - int ret; - - ret = devlink_info_driver_name_put(req, KBUILD_MODNAME); - if (ret) - return ret; snprintf(version_str, sizeof(version_str), "%lu.%lu.%lu.%lu", hnae3_get_field(hdev->fw_version, HNAE3_FW_VERSION_BYTE3_MASK, diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_devlink.c b/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_devlink.c index fdc19868b818..a6c3c5e8f0ab 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_devlink.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_devlink.c @@ -13,11 +13,6 @@ static int hclgevf_devlink_info_get(struct devlink *devlink, struct hclgevf_devlink_priv *priv = devlink_priv(devlink); char version_str[HCLGEVF_DEVLINK_FW_STRING_LEN]; struct hclgevf_dev *hdev = priv->hdev; - int ret; - - ret = devlink_info_driver_name_put(req, KBUILD_MODNAME); - if (ret) - return ret; snprintf(version_str, sizeof(version_str), "%lu.%lu.%lu.%lu", hnae3_get_field(hdev->fw_version, HNAE3_FW_VERSION_BYTE3_MASK, diff --git a/drivers/net/ethernet/intel/ice/ice_devlink.c b/drivers/net/ethernet/intel/ice/ice_devlink.c index 946d64e577c9..8286e47b4bae 100644 --- a/drivers/net/ethernet/intel/ice/ice_devlink.c +++ b/drivers/net/ethernet/intel/ice/ice_devlink.c @@ -311,12 +311,6 @@ static int ice_devlink_info_get(struct devlink *devlink, } } - err = devlink_info_driver_name_put(req, KBUILD_MODNAME); - if (err) { - NL_SET_ERR_MSG_MOD(extack, "Unable to set driver name"); - goto out_free_ctx; - } - ice_info_get_dsn(pf, ctx); err = devlink_info_serial_number_put(req, ctx->buf); diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_devlink.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_devlink.c index 88dee589cb21..f15439d26d21 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_devlink.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_devlink.c @@ -1550,7 +1550,7 @@ static int rvu_devlink_eswitch_mode_set(struct devlink *devlink, u16 mode, static int rvu_devlink_info_get(struct devlink *devlink, struct devlink_info_req *req, struct netlink_ext_ack *extack) { - return devlink_info_driver_name_put(req, DRV_NAME); + return 0; } static const struct devlink_ops rvu_devlink_ops = { diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_devlink.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_devlink.c index 777a27047c8e..5cc6416cf1a6 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_devlink.c +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_devlink.c @@ -77,18 +77,11 @@ static const struct devlink_param otx2_dl_params[] = { otx2_dl_mcam_count_validate), }; -/* Devlink OPs */ static int otx2_devlink_info_get(struct devlink *devlink, struct devlink_info_req *req, struct netlink_ext_ack *extack) { - struct otx2_devlink *otx2_dl = devlink_priv(devlink); - struct otx2_nic *pfvf = otx2_dl->pfvf; - - if (is_otx2_vf(pfvf->pcifunc)) - return devlink_info_driver_name_put(req, "rvu_nicvf"); - - return devlink_info_driver_name_put(req, "rvu_nicpf"); + return 0; } static const struct devlink_ops otx2_devlink_ops = { diff --git a/drivers/net/ethernet/marvell/prestera/prestera_devlink.c b/drivers/net/ethernet/marvell/prestera/prestera_devlink.c index 84ad05c9f12d..2a4c9df4eb79 100644 --- a/drivers/net/ethernet/marvell/prestera/prestera_devlink.c +++ b/drivers/net/ethernet/marvell/prestera/prestera_devlink.c @@ -355,11 +355,6 @@ static int prestera_dl_info_get(struct devlink *dl, { struct prestera_switch *sw = devlink_priv(dl); char buf[16]; - int err; - - err = devlink_info_driver_name_put(req, PRESTERA_DRV_NAME); - if (err) - return err; snprintf(buf, sizeof(buf), "%d.%d.%d", sw->dev->fw_rev.maj, diff --git a/drivers/net/ethernet/mellanox/mlx5/core/devlink.c b/drivers/net/ethernet/mellanox/mlx5/core/devlink.c index cc2ae427dcb0..751bc4a9edcf 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/devlink.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/devlink.c @@ -46,10 +46,6 @@ mlx5_devlink_info_get(struct devlink *devlink, struct devlink_info_req *req, u32 running_fw, stored_fw; int err; - err = devlink_info_driver_name_put(req, KBUILD_MODNAME); - if (err) - return err; - err = devlink_info_version_fixed_put(req, "fw.psid", dev->board_id); if (err) return err; diff --git a/drivers/net/ethernet/mellanox/mlxsw/core.c b/drivers/net/ethernet/mellanox/mlxsw/core.c index a83f6bc30072..a0a06e2eff82 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/core.c +++ b/drivers/net/ethernet/mellanox/mlxsw/core.c @@ -1459,11 +1459,6 @@ mlxsw_devlink_info_get(struct devlink *devlink, struct devlink_info_req *req, char buf[32]; int err; - err = devlink_info_driver_name_put(req, - mlxsw_core->bus_info->device_kind); - if (err) - return err; - mlxsw_reg_mgir_pack(mgir_pl); err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(mgir), mgir_pl); if (err) diff --git a/drivers/net/ethernet/netronome/nfp/nfp_devlink.c b/drivers/net/ethernet/netronome/nfp/nfp_devlink.c index 784f23602a8a..bf6bae557158 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_devlink.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_devlink.c @@ -239,10 +239,6 @@ nfp_devlink_info_get(struct devlink *devlink, struct devlink_info_req *req, char *buf = NULL; int err; - err = devlink_info_driver_name_put(req, "nfp"); - if (err) - return err; - vendor = nfp_hwinfo_lookup(pf->hwinfo, "assembly.vendor"); part = nfp_hwinfo_lookup(pf->hwinfo, "assembly.partno"); sn = nfp_hwinfo_lookup(pf->hwinfo, "assembly.serial"); diff --git a/drivers/net/ethernet/pensando/ionic/ionic_devlink.c b/drivers/net/ethernet/pensando/ionic/ionic_devlink.c index 567f778433e2..e6ff757895ab 100644 --- a/drivers/net/ethernet/pensando/ionic/ionic_devlink.c +++ b/drivers/net/ethernet/pensando/ionic/ionic_devlink.c @@ -26,10 +26,6 @@ static int ionic_dl_info_get(struct devlink *dl, struct devlink_info_req *req, char buf[16]; int err = 0; - err = devlink_info_driver_name_put(req, IONIC_DRV_NAME); - if (err) - return err; - err = devlink_info_version_running_put(req, DEVLINK_INFO_VERSION_GENERIC_FW, idev->dev_info.fw_version); diff --git a/drivers/net/ethernet/qlogic/qed/qed_devlink.c b/drivers/net/ethernet/qlogic/qed/qed_devlink.c index 6bb4e165b592..922c47797af6 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_devlink.c +++ b/drivers/net/ethernet/qlogic/qed/qed_devlink.c @@ -162,10 +162,6 @@ static int qed_devlink_info_get(struct devlink *devlink, dev_info = &cdev->common_dev_info; - err = devlink_info_driver_name_put(req, KBUILD_MODNAME); - if (err) - return err; - memcpy(buf, cdev->hwfns[0].hw_info.part_num, sizeof(cdev->hwfns[0].hw_info.part_num)); buf[sizeof(cdev->hwfns[0].hw_info.part_num)] = 0; diff --git a/drivers/net/netdevsim/dev.c b/drivers/net/netdevsim/dev.c index e14686594a71..b962fc8e1397 100644 --- a/drivers/net/netdevsim/dev.c +++ b/drivers/net/netdevsim/dev.c @@ -994,9 +994,6 @@ static int nsim_dev_info_get(struct devlink *devlink, { int err; - err = devlink_info_driver_name_put(req, DRV_NAME); - if (err) - return err; err = devlink_info_version_stored_put_ext(req, "fw.mgmt", "10.20.30", DEVLINK_INFO_VERSION_TYPE_COMPONENT); if (err) diff --git a/drivers/ptp/ptp_ocp.c b/drivers/ptp/ptp_ocp.c index 154d58cbd9ce..4bbaccd543ad 100644 --- a/drivers/ptp/ptp_ocp.c +++ b/drivers/ptp/ptp_ocp.c @@ -1647,10 +1647,6 @@ ptp_ocp_devlink_info_get(struct devlink *devlink, struct devlink_info_req *req, char buf[32]; int err; - err = devlink_info_driver_name_put(req, KBUILD_MODNAME); - if (err) - return err; - fw_image = bp->fw_loader ? "loader" : "fw"; sprintf(buf, "%d.%d", bp->fw_tag, bp->fw_version); err = devlink_info_version_running_put(req, fw_image, buf); diff --git a/include/net/devlink.h b/include/net/devlink.h index 02528f736f65..5f6eca5e4a40 100644 --- a/include/net/devlink.h +++ b/include/net/devlink.h @@ -1762,8 +1762,6 @@ int devlink_region_snapshot_create(struct devlink_region *region, u8 *data, u32 snapshot_id); int devlink_info_serial_number_put(struct devlink_info_req *req, const char *sn); -int devlink_info_driver_name_put(struct devlink_info_req *req, - const char *name); int devlink_info_board_serial_number_put(struct devlink_info_req *req, const char *bsn); diff --git a/net/core/devlink.c b/net/core/devlink.c index 298041a44aa8..60eb0f46520f 100644 --- a/net/core/devlink.c +++ b/net/core/devlink.c @@ -6707,14 +6707,6 @@ out_unlock: return err; } -int devlink_info_driver_name_put(struct devlink_info_req *req, const char *name) -{ - if (!req->msg) - return 0; - return nla_put_string(req->msg, DEVLINK_ATTR_INFO_DRIVER_NAME, name); -} -EXPORT_SYMBOL_GPL(devlink_info_driver_name_put); - int devlink_info_serial_number_put(struct devlink_info_req *req, const char *sn) { if (!req->msg) @@ -6823,11 +6815,25 @@ int devlink_info_version_running_put_ext(struct devlink_info_req *req, } EXPORT_SYMBOL_GPL(devlink_info_version_running_put_ext); +static int devlink_nl_driver_info_get(struct device_driver *drv, + struct devlink_info_req *req) +{ + if (!drv) + return 0; + + if (drv->name[0]) + return nla_put_string(req->msg, DEVLINK_ATTR_INFO_DRIVER_NAME, + drv->name); + + return 0; +} + static int devlink_nl_info_fill(struct sk_buff *msg, struct devlink *devlink, enum devlink_command cmd, u32 portid, u32 seq, int flags, struct netlink_ext_ack *extack) { + struct device *dev = devlink_to_dev(devlink); struct devlink_info_req req = {}; void *hdr; int err; @@ -6845,6 +6851,10 @@ devlink_nl_info_fill(struct sk_buff *msg, struct devlink *devlink, if (err) goto err_cancel_msg; + err = devlink_nl_driver_info_get(dev->driver, &req); + if (err) + goto err_cancel_msg; + genlmsg_end(msg, hdr); return 0; From c5cd7c86847cda0fdd44956561c0f3d9adea032b Mon Sep 17 00:00:00 2001 From: Vincent Mailhol Date: Tue, 29 Nov 2022 18:51:39 +0900 Subject: [PATCH 1367/2002] net: devlink: make the devlink_ops::info_get() callback optional Some drivers only reported the driver name in their devlink_ops::info_get() callback. Now that the core provides this information, the callback became empty. For such drivers, just removing the callback would prevent the core from executing devlink_nl_info_fill() meaning that "devlink dev info" would not return anything. Make the callback function optional by executing devlink_nl_info_fill() even if devlink_ops::info_get() is NULL. N.B.: the drivers with devlink support which previously did not implement devlink_ops::info_get() will now also be able to report the driver name. Signed-off-by: Vincent Mailhol Reviewed-by: Jacob Keller Reviewed-by: Jiri Pirko Signed-off-by: Jakub Kicinski --- net/core/devlink.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/net/core/devlink.c b/net/core/devlink.c index 60eb0f46520f..fca3ebee97b0 100644 --- a/net/core/devlink.c +++ b/net/core/devlink.c @@ -6847,9 +6847,11 @@ devlink_nl_info_fill(struct sk_buff *msg, struct devlink *devlink, goto err_cancel_msg; req.msg = msg; - err = devlink->ops->info_get(devlink, &req, extack); - if (err) - goto err_cancel_msg; + if (devlink->ops->info_get) { + err = devlink->ops->info_get(devlink, &req, extack); + if (err) + goto err_cancel_msg; + } err = devlink_nl_driver_info_get(dev->driver, &req); if (err) @@ -6870,9 +6872,6 @@ static int devlink_nl_cmd_info_get_doit(struct sk_buff *skb, struct sk_buff *msg; int err; - if (!devlink->ops->info_get) - return -EOPNOTSUPP; - msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); if (!msg) return -ENOMEM; @@ -6898,7 +6897,7 @@ static int devlink_nl_cmd_info_get_dumpit(struct sk_buff *msg, int err = 0; devlinks_xa_for_each_registered_get(sock_net(msg->sk), index, devlink) { - if (idx < start || !devlink->ops->info_get) + if (idx < start) goto inc; devl_lock(devlink); From cf4590b91db4083b8716289dcfd95097195c6829 Mon Sep 17 00:00:00 2001 From: Vincent Mailhol Date: Tue, 29 Nov 2022 18:51:40 +0900 Subject: [PATCH 1368/2002] net: devlink: clean-up empty devlink_ops::info_get() devlink_ops::info_get() is now optional and devlink will continue to report information even if that callback gets removed. Remove all the empty devlink_ops::info_get() callbacks from the drivers. Signed-off-by: Vincent Mailhol Reviewed-by: Jacob Keller Reviewed-by: Jiri Pirko Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/fungible/funeth/funeth_devlink.c | 7 ------- drivers/net/ethernet/marvell/octeontx2/af/rvu_devlink.c | 7 ------- drivers/net/ethernet/marvell/octeontx2/nic/otx2_devlink.c | 8 -------- 3 files changed, 22 deletions(-) diff --git a/drivers/net/ethernet/fungible/funeth/funeth_devlink.c b/drivers/net/ethernet/fungible/funeth/funeth_devlink.c index 6668375edff6..4fbeb3fd71a8 100644 --- a/drivers/net/ethernet/fungible/funeth/funeth_devlink.c +++ b/drivers/net/ethernet/fungible/funeth/funeth_devlink.c @@ -3,14 +3,7 @@ #include "funeth.h" #include "funeth_devlink.h" -static int fun_dl_info_get(struct devlink *dl, struct devlink_info_req *req, - struct netlink_ext_ack *extack) -{ - return 0; -} - static const struct devlink_ops fun_dl_ops = { - .info_get = fun_dl_info_get, }; struct devlink *fun_devlink_alloc(struct device *dev) diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_devlink.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_devlink.c index f15439d26d21..bda1a6fa2ec4 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_devlink.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_devlink.c @@ -1547,14 +1547,7 @@ static int rvu_devlink_eswitch_mode_set(struct devlink *devlink, u16 mode, return 0; } -static int rvu_devlink_info_get(struct devlink *devlink, struct devlink_info_req *req, - struct netlink_ext_ack *extack) -{ - return 0; -} - static const struct devlink_ops rvu_devlink_ops = { - .info_get = rvu_devlink_info_get, .eswitch_mode_get = rvu_devlink_eswitch_mode_get, .eswitch_mode_set = rvu_devlink_eswitch_mode_set, }; diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_devlink.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_devlink.c index 5cc6416cf1a6..63ef7c41d18d 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_devlink.c +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_devlink.c @@ -77,15 +77,7 @@ static const struct devlink_param otx2_dl_params[] = { otx2_dl_mcam_count_validate), }; -static int otx2_devlink_info_get(struct devlink *devlink, - struct devlink_info_req *req, - struct netlink_ext_ack *extack) -{ - return 0; -} - static const struct devlink_ops otx2_devlink_ops = { - .info_get = otx2_devlink_info_get, }; int otx2_register_dl(struct otx2_nic *pfvf) From bc66fa87d4fda9053a8145e5718fc278c2b88253 Mon Sep 17 00:00:00 2001 From: Xiaolei Wang Date: Wed, 30 Nov 2022 10:12:16 +0800 Subject: [PATCH 1369/2002] net: phy: Add link between phy dev and mac dev If the external phy used by current mac interface is managed by another mac interface, it means that this network port cannot work independently, especially when the system suspends and resumes, the following trace may appear, so we should create a device link between phy dev and mac dev. WARNING: CPU: 0 PID: 24 at drivers/net/phy/phy.c:983 phy_error+0x20/0x68 Modules linked in: CPU: 0 PID: 24 Comm: kworker/0:2 Not tainted 6.1.0-rc3-00011-g5aaef24b5c6d-dirty #34 Hardware name: Freescale i.MX6 SoloX (Device Tree) Workqueue: events_power_efficient phy_state_machine unwind_backtrace from show_stack+0x10/0x14 show_stack from dump_stack_lvl+0x68/0x90 dump_stack_lvl from __warn+0xb4/0x24c __warn from warn_slowpath_fmt+0x5c/0xd8 warn_slowpath_fmt from phy_error+0x20/0x68 phy_error from phy_state_machine+0x22c/0x23c phy_state_machine from process_one_work+0x288/0x744 process_one_work from worker_thread+0x3c/0x500 worker_thread from kthread+0xf0/0x114 kthread from ret_from_fork+0x14/0x28 Exception stack(0xf0951fb0 to 0xf0951ff8) Signed-off-by: Xiaolei Wang Tested-by: Florian Fainelli Reviewed-by: Florian Fainelli Link: https://lore.kernel.org/r/20221130021216.1052230-1-xiaolei.wang@windriver.com Signed-off-by: Jakub Kicinski --- drivers/net/phy/phy_device.c | 12 ++++++++++++ include/linux/phy.h | 4 ++++ 2 files changed, 16 insertions(+) diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c index 8cff61dbc4b5..716870a4499c 100644 --- a/drivers/net/phy/phy_device.c +++ b/drivers/net/phy/phy_device.c @@ -1512,6 +1512,15 @@ int phy_attach_direct(struct net_device *dev, struct phy_device *phydev, phy_resume(phydev); phy_led_triggers_register(phydev); + /** + * If the external phy used by current mac interface is managed by + * another mac interface, so we should create a device link between + * phy dev and mac dev. + */ + if (phydev->mdio.bus->parent && dev->dev.parent != phydev->mdio.bus->parent) + phydev->devlink = device_link_add(dev->dev.parent, &phydev->mdio.dev, + DL_FLAG_PM_RUNTIME | DL_FLAG_STATELESS); + return err; error: @@ -1750,6 +1759,9 @@ void phy_detach(struct phy_device *phydev) struct module *ndev_owner = NULL; struct mii_bus *bus; + if (phydev->devlink) + device_link_del(phydev->devlink); + if (phydev->sysfs_links) { if (dev) sysfs_remove_link(&dev->dev.kobj, "phydev"); diff --git a/include/linux/phy.h b/include/linux/phy.h index 9a3752c0c444..71eeb4e3b1fd 100644 --- a/include/linux/phy.h +++ b/include/linux/phy.h @@ -529,6 +529,8 @@ struct macsec_ops; * * @mdio: MDIO bus this PHY is on * @drv: Pointer to the driver for this PHY instance + * @devlink: Create a link between phy dev and mac dev, if the external phy + * used by current mac interface is managed by another mac interface. * @phy_id: UID for this device found during discovery * @c45_ids: 802.3-c45 Device Identifiers if is_c45. * @is_c45: Set to true if this PHY uses clause 45 addressing. @@ -618,6 +620,8 @@ struct phy_device { /* And management functions */ struct phy_driver *drv; + struct device_link *devlink; + u32 phy_id; struct phy_c45_device_ids c45_ids; From e5214f363dabca240446272dac54d404501ad5e5 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Mon, 28 Nov 2022 14:06:14 +0300 Subject: [PATCH 1370/2002] bonding: uninitialized variable in bond_miimon_inspect() The "ignore_updelay" variable needs to be initialized to false. Fixes: f8a65ab2f3ff ("bonding: fix link recovery in mode 2 when updelay is nonzero") Signed-off-by: Dan Carpenter Reviewed-by: Pavan Chebbi Acked-by: Jay Vosburgh Link: https://lore.kernel.org/r/Y4SWJlh3ohJ6EPTL@kili Signed-off-by: Paolo Abeni --- drivers/net/bonding/bond_main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index c87481033995..e01bb0412f1c 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -2524,10 +2524,10 @@ static int bond_slave_info_query(struct net_device *bond_dev, struct ifslave *in /* called with rcu_read_lock() */ static int bond_miimon_inspect(struct bonding *bond) { + bool ignore_updelay = false; int link_state, commit = 0; struct list_head *iter; struct slave *slave; - bool ignore_updelay; if (BOND_MODE(bond) == BOND_MODE_ACTIVEBACKUP) { ignore_updelay = !rcu_dereference(bond->curr_active_slave); From 4920ab131b2dbae7464b72bdcac465d070254209 Mon Sep 17 00:00:00 2001 From: Minsuk Kang Date: Wed, 16 Nov 2022 23:29:52 +0900 Subject: [PATCH 1371/2002] wifi: brcmfmac: Check the count value of channel spec to prevent out-of-bounds reads This patch fixes slab-out-of-bounds reads in brcmfmac that occur in brcmf_construct_chaninfo() and brcmf_enable_bw40_2g() when the count value of channel specifications provided by the device is greater than the length of 'list->element[]', decided by the size of the 'list' allocated with kzalloc(). The patch adds checks that make the functions free the buffer and return -EINVAL if that is the case. Note that the negative return is handled by the caller, brcmf_setup_wiphybands() or brcmf_cfg80211_attach(). Found by a modified version of syzkaller. Crash Report from brcmf_construct_chaninfo(): ================================================================== BUG: KASAN: slab-out-of-bounds in brcmf_setup_wiphybands+0x1238/0x1430 Read of size 4 at addr ffff888115f24600 by task kworker/0:2/1896 CPU: 0 PID: 1896 Comm: kworker/0:2 Tainted: G W O 5.14.0+ #132 Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS rel-1.12.1-0-ga5cab58e9a3f-prebuilt.qemu.org 04/01/2014 Workqueue: usb_hub_wq hub_event Call Trace: dump_stack_lvl+0x57/0x7d print_address_description.constprop.0.cold+0x93/0x334 kasan_report.cold+0x83/0xdf brcmf_setup_wiphybands+0x1238/0x1430 brcmf_cfg80211_attach+0x2118/0x3fd0 brcmf_attach+0x389/0xd40 brcmf_usb_probe+0x12de/0x1690 usb_probe_interface+0x25f/0x710 really_probe+0x1be/0xa90 __driver_probe_device+0x2ab/0x460 driver_probe_device+0x49/0x120 __device_attach_driver+0x18a/0x250 bus_for_each_drv+0x123/0x1a0 __device_attach+0x207/0x330 bus_probe_device+0x1a2/0x260 device_add+0xa61/0x1ce0 usb_set_configuration+0x984/0x1770 usb_generic_driver_probe+0x69/0x90 usb_probe_device+0x9c/0x220 really_probe+0x1be/0xa90 __driver_probe_device+0x2ab/0x460 driver_probe_device+0x49/0x120 __device_attach_driver+0x18a/0x250 bus_for_each_drv+0x123/0x1a0 __device_attach+0x207/0x330 bus_probe_device+0x1a2/0x260 device_add+0xa61/0x1ce0 usb_new_device.cold+0x463/0xf66 hub_event+0x10d5/0x3330 process_one_work+0x873/0x13e0 worker_thread+0x8b/0xd10 kthread+0x379/0x450 ret_from_fork+0x1f/0x30 Allocated by task 1896: kasan_save_stack+0x1b/0x40 __kasan_kmalloc+0x7c/0x90 kmem_cache_alloc_trace+0x19e/0x330 brcmf_setup_wiphybands+0x290/0x1430 brcmf_cfg80211_attach+0x2118/0x3fd0 brcmf_attach+0x389/0xd40 brcmf_usb_probe+0x12de/0x1690 usb_probe_interface+0x25f/0x710 really_probe+0x1be/0xa90 __driver_probe_device+0x2ab/0x460 driver_probe_device+0x49/0x120 __device_attach_driver+0x18a/0x250 bus_for_each_drv+0x123/0x1a0 __device_attach+0x207/0x330 bus_probe_device+0x1a2/0x260 device_add+0xa61/0x1ce0 usb_set_configuration+0x984/0x1770 usb_generic_driver_probe+0x69/0x90 usb_probe_device+0x9c/0x220 really_probe+0x1be/0xa90 __driver_probe_device+0x2ab/0x460 driver_probe_device+0x49/0x120 __device_attach_driver+0x18a/0x250 bus_for_each_drv+0x123/0x1a0 __device_attach+0x207/0x330 bus_probe_device+0x1a2/0x260 device_add+0xa61/0x1ce0 usb_new_device.cold+0x463/0xf66 hub_event+0x10d5/0x3330 process_one_work+0x873/0x13e0 worker_thread+0x8b/0xd10 kthread+0x379/0x450 ret_from_fork+0x1f/0x30 The buggy address belongs to the object at ffff888115f24000 which belongs to the cache kmalloc-2k of size 2048 The buggy address is located 1536 bytes inside of 2048-byte region [ffff888115f24000, ffff888115f24800) Memory state around the buggy address: ffff888115f24500: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ffff888115f24580: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 >ffff888115f24600: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc ^ ffff888115f24680: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc ffff888115f24700: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc ================================================================== Crash Report from brcmf_enable_bw40_2g(): ================================================================== BUG: KASAN: slab-out-of-bounds in brcmf_cfg80211_attach+0x3d11/0x3fd0 Read of size 4 at addr ffff888103787600 by task kworker/0:2/1896 CPU: 0 PID: 1896 Comm: kworker/0:2 Tainted: G W O 5.14.0+ #132 Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS rel-1.12.1-0-ga5cab58e9a3f-prebuilt.qemu.org 04/01/2014 Workqueue: usb_hub_wq hub_event Call Trace: dump_stack_lvl+0x57/0x7d print_address_description.constprop.0.cold+0x93/0x334 kasan_report.cold+0x83/0xdf brcmf_cfg80211_attach+0x3d11/0x3fd0 brcmf_attach+0x389/0xd40 brcmf_usb_probe+0x12de/0x1690 usb_probe_interface+0x25f/0x710 really_probe+0x1be/0xa90 __driver_probe_device+0x2ab/0x460 driver_probe_device+0x49/0x120 __device_attach_driver+0x18a/0x250 bus_for_each_drv+0x123/0x1a0 __device_attach+0x207/0x330 bus_probe_device+0x1a2/0x260 device_add+0xa61/0x1ce0 usb_set_configuration+0x984/0x1770 usb_generic_driver_probe+0x69/0x90 usb_probe_device+0x9c/0x220 really_probe+0x1be/0xa90 __driver_probe_device+0x2ab/0x460 driver_probe_device+0x49/0x120 __device_attach_driver+0x18a/0x250 bus_for_each_drv+0x123/0x1a0 __device_attach+0x207/0x330 bus_probe_device+0x1a2/0x260 device_add+0xa61/0x1ce0 usb_new_device.cold+0x463/0xf66 hub_event+0x10d5/0x3330 process_one_work+0x873/0x13e0 worker_thread+0x8b/0xd10 kthread+0x379/0x450 ret_from_fork+0x1f/0x30 Allocated by task 1896: kasan_save_stack+0x1b/0x40 __kasan_kmalloc+0x7c/0x90 kmem_cache_alloc_trace+0x19e/0x330 brcmf_cfg80211_attach+0x3302/0x3fd0 brcmf_attach+0x389/0xd40 brcmf_usb_probe+0x12de/0x1690 usb_probe_interface+0x25f/0x710 really_probe+0x1be/0xa90 __driver_probe_device+0x2ab/0x460 driver_probe_device+0x49/0x120 __device_attach_driver+0x18a/0x250 bus_for_each_drv+0x123/0x1a0 __device_attach+0x207/0x330 bus_probe_device+0x1a2/0x260 device_add+0xa61/0x1ce0 usb_set_configuration+0x984/0x1770 usb_generic_driver_probe+0x69/0x90 usb_probe_device+0x9c/0x220 really_probe+0x1be/0xa90 __driver_probe_device+0x2ab/0x460 driver_probe_device+0x49/0x120 __device_attach_driver+0x18a/0x250 bus_for_each_drv+0x123/0x1a0 __device_attach+0x207/0x330 bus_probe_device+0x1a2/0x260 device_add+0xa61/0x1ce0 usb_new_device.cold+0x463/0xf66 hub_event+0x10d5/0x3330 process_one_work+0x873/0x13e0 worker_thread+0x8b/0xd10 kthread+0x379/0x450 ret_from_fork+0x1f/0x30 The buggy address belongs to the object at ffff888103787000 which belongs to the cache kmalloc-2k of size 2048 The buggy address is located 1536 bytes inside of 2048-byte region [ffff888103787000, ffff888103787800) Memory state around the buggy address: ffff888103787500: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ffff888103787580: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 >ffff888103787600: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc ^ ffff888103787680: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc ffff888103787700: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc ================================================================== Reported-by: Dokyung Song Reported-by: Jisoo Jang Reported-by: Minsuk Kang Reviewed-by: Arend van Spriel Signed-off-by: Minsuk Kang Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20221116142952.518241-1-linuxlovemin@yonsei.ac.kr --- .../broadcom/brcm80211/brcmfmac/cfg80211.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c index ae9507dec74a..bff3128c2f26 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c @@ -101,6 +101,9 @@ #define BRCMF_ASSOC_PARAMS_FIXED_SIZE \ (sizeof(struct brcmf_assoc_params_le) - sizeof(u16)) +#define BRCMF_MAX_CHANSPEC_LIST \ + (BRCMF_DCMD_MEDLEN / sizeof(__le32) - 1) + struct brcmf_dump_survey { u32 obss; u32 ibss; @@ -6840,6 +6843,13 @@ static int brcmf_construct_chaninfo(struct brcmf_cfg80211_info *cfg, band->channels[i].flags = IEEE80211_CHAN_DISABLED; total = le32_to_cpu(list->count); + if (total > BRCMF_MAX_CHANSPEC_LIST) { + bphy_err(drvr, "Invalid count of channel Spec. (%u)\n", + total); + err = -EINVAL; + goto fail_pbuf; + } + for (i = 0; i < total; i++) { ch.chspec = (u16)le32_to_cpu(list->element[i]); cfg->d11inf.decchspec(&ch); @@ -6985,6 +6995,13 @@ static int brcmf_enable_bw40_2g(struct brcmf_cfg80211_info *cfg) band = cfg_to_wiphy(cfg)->bands[NL80211_BAND_2GHZ]; list = (struct brcmf_chanspec_list *)pbuf; num_chan = le32_to_cpu(list->count); + if (num_chan > BRCMF_MAX_CHANSPEC_LIST) { + bphy_err(drvr, "Invalid count of channel Spec. (%u)\n", + num_chan); + kfree(pbuf); + return -EINVAL; + } + for (i = 0; i < num_chan; i++) { ch.chspec = (u16)le32_to_cpu(list->element[i]); cfg->d11inf.decchspec(&ch); From cd9b6b3baf5278c73c91e242d41387684fc7f8d8 Mon Sep 17 00:00:00 2001 From: Zong-Zhe Yang Date: Fri, 25 Nov 2022 15:24:14 +0800 Subject: [PATCH 1372/2002] wifi: rtw89: enable mac80211 virtual monitor interface For running with mac80211 channel context ops and using only as monitor, we need to enable WANT_MONITOR_VIF to let mac80211 process virtual monitor interface. Then, we are able to set channel on the monitor from user space. Signed-off-by: Zong-Zhe Yang Signed-off-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20221125072416.94752-2-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/core.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c index a5fe45d79f18..af8daa101f45 100644 --- a/drivers/net/wireless/realtek/rtw89/core.c +++ b/drivers/net/wireless/realtek/rtw89/core.c @@ -1405,6 +1405,9 @@ static void rtw89_vif_rx_stats_iter(void *data, u8 *mac, struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; const u8 *bssid = iter_data->bssid; + if (!vif->bss_conf.bssid) + return; + if (ieee80211_is_trigger(hdr->frame_control)) { rtw89_stats_trigger_frame(rtwdev, vif, skb); return; @@ -2386,6 +2389,8 @@ void rtw89_vif_type_mapping(struct ieee80211_vif *vif, bool assoc) rtwvif->self_role = RTW89_SELF_ROLE_CLIENT; rtwvif->addr_cam.sec_ent_mode = RTW89_ADDR_CAM_SEC_NORMAL; break; + case NL80211_IFTYPE_MONITOR: + break; default: WARN_ON(1); break; @@ -3266,6 +3271,7 @@ static int rtw89_core_register_hw(struct rtw89_dev *rtwdev) ieee80211_hw_set(hw, SUPPORTS_DYNAMIC_PS); ieee80211_hw_set(hw, SINGLE_SCAN_ON_ALL_BANDS); ieee80211_hw_set(hw, SUPPORTS_MULTI_BSSID); + ieee80211_hw_set(hw, WANT_MONITOR_VIF); hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_AP) | From 51e8ed4e44b5efcf8da2c1f3478e52120a12cdf8 Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Fri, 25 Nov 2022 15:24:15 +0800 Subject: [PATCH 1373/2002] wifi: rtw89: add HE radiotap for monitor mode With basic HE radiotap, we can check data rate in sniffer data. To store the radiotap data, we reserve headroom of aligned 64 bytes, and then update HE radiotap in monitor mode, so it doesn't affect performance in normal mode. Signed-off-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20221125072416.94752-3-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/core.c | 22 ++++++++++++++++++++++ drivers/net/wireless/realtek/rtw89/core.h | 18 ++++++++++++++++++ drivers/net/wireless/realtek/rtw89/pci.c | 2 +- 3 files changed, 41 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c index af8daa101f45..190d72df800f 100644 --- a/drivers/net/wireless/realtek/rtw89/core.c +++ b/drivers/net/wireless/realtek/rtw89/core.c @@ -1480,6 +1480,27 @@ static void rtw89_core_hw_to_sband_rate(struct ieee80211_rx_status *rx_status) rx_status->rate_idx -= 4; } +static void rtw89_core_update_radiotap(struct rtw89_dev *rtwdev, + struct sk_buff *skb, + struct ieee80211_rx_status *rx_status) +{ + static const struct ieee80211_radiotap_he known_he = { + .data1 = cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA1_DATA_MCS_KNOWN | + IEEE80211_RADIOTAP_HE_DATA1_BW_RU_ALLOC_KNOWN), + .data2 = cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA2_GI_KNOWN), + }; + struct ieee80211_radiotap_he *he; + + if (!(rtwdev->hw->conf.flags & IEEE80211_CONF_MONITOR)) + return; + + if (rx_status->encoding == RX_ENC_HE) { + rx_status->flag |= RX_FLAG_RADIOTAP_HE; + he = skb_push(skb, sizeof(*he)); + *he = known_he; + } +} + static void rtw89_core_rx_to_mac80211(struct rtw89_dev *rtwdev, struct rtw89_rx_phy_ppdu *phy_ppdu, struct rtw89_rx_desc_info *desc_info, @@ -1494,6 +1515,7 @@ static void rtw89_core_rx_to_mac80211(struct rtw89_dev *rtwdev, rtw89_core_hw_to_sband_rate(rx_status); rtw89_core_rx_stats(rtwdev, phy_ppdu, desc_info, skb_ppdu); + rtw89_core_update_radiotap(rtwdev, skb_ppdu, rx_status); /* In low power mode, it does RX in thread context. */ local_bh_disable(); ieee80211_rx_napi(rtwdev->hw, NULL, skb_ppdu, napi); diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index 1b0acb1c5450..0be8f7bd3ca2 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -35,6 +35,7 @@ extern const struct ieee80211_ops rtw89_ops; #define RSSI_FACTOR 1 #define RTW89_RSSI_RAW_TO_DBM(rssi) ((s8)((rssi) >> RSSI_FACTOR) - MAX_RSSI) #define RTW89_TX_DIV_RSSI_RAW_TH (2 << RSSI_FACTOR) +#define RTW89_RADIOTAP_ROOM ALIGN(sizeof(struct ieee80211_radiotap_he), 64) #define RTW89_HTC_MASK_VARIANT GENMASK(1, 0) #define RTW89_HTC_VARIANT_HE 3 @@ -4383,6 +4384,23 @@ static inline struct rtw89_fw_suit *rtw89_fw_suit_get(struct rtw89_dev *rtwdev, return &fw_info->normal; } +static inline struct sk_buff *rtw89_alloc_skb_for_rx(struct rtw89_dev *rtwdev, + unsigned int length) +{ + struct sk_buff *skb; + + if (rtwdev->hw->conf.flags & IEEE80211_CONF_MONITOR) { + skb = dev_alloc_skb(length + RTW89_RADIOTAP_ROOM); + if (!skb) + return NULL; + + skb_reserve(skb, RTW89_RADIOTAP_ROOM); + return skb; + } + + return dev_alloc_skb(length); +} + int rtw89_core_tx_write(struct rtw89_dev *rtwdev, struct ieee80211_vif *vif, struct ieee80211_sta *sta, struct sk_buff *skb, int *qsel); int rtw89_h2c_tx(struct rtw89_dev *rtwdev, diff --git a/drivers/net/wireless/realtek/rtw89/pci.c b/drivers/net/wireless/realtek/rtw89/pci.c index 7aa0af18cdd5..1c4500ba777c 100644 --- a/drivers/net/wireless/realtek/rtw89/pci.c +++ b/drivers/net/wireless/realtek/rtw89/pci.c @@ -267,7 +267,7 @@ static u32 rtw89_pci_rxbd_deliver_skbs(struct rtw89_dev *rtwdev, rtw89_core_query_rxdesc(rtwdev, desc_info, skb->data, rxinfo_size); - new = dev_alloc_skb(desc_info->pkt_size); + new = rtw89_alloc_skb_for_rx(rtwdev, desc_info->pkt_size); if (!new) goto err_sync_device; From a215b2b7055f02d8f7666f457d442e77097bb604 Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Fri, 25 Nov 2022 15:24:16 +0800 Subject: [PATCH 1374/2002] wifi: rtw89: 8852b: turn off PoP function in monitor mode PoP stands for Packet on Packet that can improve performance in noisy environment, but it could get RX stuck suddenly. In normal mode, firmware can help to resolve the stuck, but firmware doesn't work in monitor mode. Therefore, turn off PoP to avoid RX stuck. Signed-off-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20221125072416.94752-4-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/reg.h | 2 ++ drivers/net/wireless/realtek/rtw89/rtw8852b.c | 7 +++++++ 2 files changed, 9 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/reg.h b/drivers/net/wireless/realtek/rtw89/reg.h index dbe06542f443..f2634062f377 100644 --- a/drivers/net/wireless/realtek/rtw89/reg.h +++ b/drivers/net/wireless/realtek/rtw89/reg.h @@ -4236,6 +4236,8 @@ #define R_P1_NBIIDX 0x4770 #define B_P1_NBIIDX_VAL GENMASK(11, 0) #define B_P1_NBIIDX_NOTCH_EN BIT(12) +#define R_PKT_CTRL 0x47D4 +#define B_PKT_POP_EN BIT(8) #define R_SEG0R_PD 0x481C #define R_SEG0R_PD_V1 0x4860 #define B_SEG0R_PD_SPATIAL_REUSE_EN_MSK_V1 BIT(30) diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852b.c b/drivers/net/wireless/realtek/rtw89/rtw8852b.c index 2d4d572dc601..b635ac1d1ca2 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852b.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852b.c @@ -1411,6 +1411,12 @@ static void rtw8852b_bb_sethw(struct rtw89_dev *rtwdev) rtw89_phy_read32_mask(rtwdev, R_P1_RPL1, B_P0_RPL1_BIAS_MASK); } +static void rtw8852b_bb_set_pop(struct rtw89_dev *rtwdev) +{ + if (rtwdev->hw->conf.flags & IEEE80211_CONF_MONITOR) + rtw89_phy_write32_clr(rtwdev, R_PKT_CTRL, B_PKT_POP_EN); +} + static void rtw8852b_set_channel_bb(struct rtw89_dev *rtwdev, const struct rtw89_chan *chan, enum rtw89_phy_idx phy_idx) { @@ -1441,6 +1447,7 @@ static void rtw8852b_set_channel_bb(struct rtw89_dev *rtwdev, const struct rtw89 rtw89_phy_write32_mask(rtwdev, R_MAC_PIN_SEL, B_CH_IDX_SEG0, chan->primary_channel); rtw8852b_5m_mask(rtwdev, chan, phy_idx); + rtw8852b_bb_set_pop(rtwdev); rtw8852b_bb_reset_all(rtwdev, phy_idx); } From 7c57d3dc43812653726902aae95ac9b36aa7e3ec Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Mon, 28 Nov 2022 15:56:53 +0800 Subject: [PATCH 1375/2002] wifi: rtw88: 8821c: enable BT device recovery mechanism 8821ce is a combo card, and BT is a USB device that could get card lost during stress test, and need WiFi firmware to detect and recover it, so driver sends a H2C to enable this mechanism. Signed-off-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20221128075653.5221-1-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw88/fw.c | 10 ++++++++++ drivers/net/wireless/realtek/rtw88/fw.h | 6 ++++++ drivers/net/wireless/realtek/rtw88/mac.c | 18 +++++++++++++++++- 3 files changed, 33 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/realtek/rtw88/fw.c b/drivers/net/wireless/realtek/rtw88/fw.c index b290811d75e1..5e760c884f89 100644 --- a/drivers/net/wireless/realtek/rtw88/fw.c +++ b/drivers/net/wireless/realtek/rtw88/fw.c @@ -822,6 +822,16 @@ void rtw_fw_set_nlo_info(struct rtw_dev *rtwdev, bool enable) rtw_fw_send_h2c_command(rtwdev, h2c_pkt); } +void rtw_fw_set_recover_bt_device(struct rtw_dev *rtwdev) +{ + u8 h2c_pkt[H2C_PKT_SIZE] = {0}; + + SET_H2C_CMD_ID_CLASS(h2c_pkt, H2C_CMD_RECOVER_BT_DEV); + SET_RECOVER_BT_DEV_EN(h2c_pkt, 1); + + rtw_fw_send_h2c_command(rtwdev, h2c_pkt); +} + void rtw_fw_set_pg_info(struct rtw_dev *rtwdev) { struct rtw_lps_conf *conf = &rtwdev->lps_conf; diff --git a/drivers/net/wireless/realtek/rtw88/fw.h b/drivers/net/wireless/realtek/rtw88/fw.h index bca610dc99ab..0a386e6d6e0d 100644 --- a/drivers/net/wireless/realtek/rtw88/fw.h +++ b/drivers/net/wireless/realtek/rtw88/fw.h @@ -555,6 +555,8 @@ static inline void rtw_h2c_pkt_set_header(u8 *h2c_pkt, u8 sub_id) #define H2C_CMD_AOAC_GLOBAL_INFO 0x82 #define H2C_CMD_NLO_INFO 0x8C +#define H2C_CMD_RECOVER_BT_DEV 0xD1 + #define SET_H2C_CMD_ID_CLASS(h2c_pkt, value) \ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x00, value, GENMASK(7, 0)) @@ -754,6 +756,9 @@ static inline void rtw_h2c_pkt_set_header(u8 *h2c_pkt, u8 sub_id) #define SET_NLO_LOC_NLO_INFO(h2c_pkt, value) \ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x00, value, GENMASK(23, 16)) +#define SET_RECOVER_BT_DEV_EN(h2c_pkt, value) \ + le32p_replace_bits((__le32 *)(h2c_pkt) + 0x00, value, BIT(8)) + #define GET_FW_DUMP_LEN(_header) \ le32_get_bits(*((__le32 *)(_header) + 0x00), GENMASK(15, 0)) #define GET_FW_DUMP_SEQ(_header) \ @@ -843,6 +848,7 @@ void rtw_fw_set_aoac_global_info_cmd(struct rtw_dev *rtwdev, u8 group_key_enc); void rtw_fw_set_nlo_info(struct rtw_dev *rtwdev, bool enable); +void rtw_fw_set_recover_bt_device(struct rtw_dev *rtwdev); void rtw_fw_update_pkt_probe_req(struct rtw_dev *rtwdev, struct cfg80211_ssid *ssid); void rtw_fw_channel_switch(struct rtw_dev *rtwdev, bool enable); diff --git a/drivers/net/wireless/realtek/rtw88/mac.c b/drivers/net/wireless/realtek/rtw88/mac.c index 52076e89d59a..c7e64f7036ac 100644 --- a/drivers/net/wireless/realtek/rtw88/mac.c +++ b/drivers/net/wireless/realtek/rtw88/mac.c @@ -906,7 +906,8 @@ out: return ret; } -int rtw_download_firmware(struct rtw_dev *rtwdev, struct rtw_fw_state *fw) +static +int _rtw_download_firmware(struct rtw_dev *rtwdev, struct rtw_fw_state *fw) { if (rtw_chip_wcpu_11n(rtwdev)) return __rtw_download_firmware_legacy(rtwdev, fw); @@ -914,6 +915,21 @@ int rtw_download_firmware(struct rtw_dev *rtwdev, struct rtw_fw_state *fw) return __rtw_download_firmware(rtwdev, fw); } +int rtw_download_firmware(struct rtw_dev *rtwdev, struct rtw_fw_state *fw) +{ + int ret; + + ret = _rtw_download_firmware(rtwdev, fw); + if (ret) + return ret; + + if (rtw_hci_type(rtwdev) == RTW_HCI_TYPE_PCIE && + rtwdev->chip->id == RTW_CHIP_TYPE_8821C) + rtw_fw_set_recover_bt_device(rtwdev); + + return 0; +} + static u32 get_priority_queues(struct rtw_dev *rtwdev, u32 queues) { const struct rtw_rqpn *rqpn = rtwdev->fifo.rqpn; From 38f25dec521edfa289fa0b829676927b13fede91 Mon Sep 17 00:00:00 2001 From: Zong-Zhe Yang Date: Tue, 29 Nov 2022 16:31:25 +0800 Subject: [PATCH 1376/2002] wifi: rtw89: rfk: rename rtw89_mcc_info to rtw89_rfk_mcc_info The `rtw89_mcc_info mcc` is only for RFK MCC stuffs instead of common MCC management info. Replace it with `rtw89_rfk_mcc_info rfk_mcc` to avoid confusion and reserve `struct rtw89_mcc_info mcc` for MCC management code. (No logic changes.) Signed-off-by: Zong-Zhe Yang Signed-off-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20221129083130.45708-2-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/core.h | 4 ++-- drivers/net/wireless/realtek/rtw89/fw.c | 10 +++++----- drivers/net/wireless/realtek/rtw89/rtw8852c.c | 4 ++-- .../net/wireless/realtek/rtw89/rtw8852c_rfk.c | 20 +++++++++---------- 4 files changed, 19 insertions(+), 19 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index 0be8f7bd3ca2..d0efeeabcb77 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -3032,7 +3032,7 @@ struct rtw89_dack_info { #define RTW89_IQK_CHS_NR 2 #define RTW89_IQK_PATH_NR 4 -struct rtw89_mcc_info { +struct rtw89_rfk_mcc_info { u8 ch[RTW89_IQK_CHS_NR]; u8 band[RTW89_IQK_CHS_NR]; u8 table_idx; @@ -3578,7 +3578,7 @@ struct rtw89_dev { struct rtw89_dack_info dack; struct rtw89_iqk_info iqk; struct rtw89_dpk_info dpk; - struct rtw89_mcc_info mcc; + struct rtw89_rfk_mcc_info rfk_mcc; struct rtw89_lck_info lck; struct rtw89_rx_dck_info rx_dck; bool is_tssi_mode[RF_PATH_MAX]; diff --git a/drivers/net/wireless/realtek/rtw89/fw.c b/drivers/net/wireless/realtek/rtw89/fw.c index 10c5ed21da02..3d7b99e15967 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.c +++ b/drivers/net/wireless/realtek/rtw89/fw.c @@ -2263,7 +2263,7 @@ fail: int rtw89_fw_h2c_rf_ntfy_mcc(struct rtw89_dev *rtwdev) { const struct rtw89_chan *chan = rtw89_chan_get(rtwdev, RTW89_SUB_ENTITY_0); - struct rtw89_mcc_info *mcc_info = &rtwdev->mcc; + struct rtw89_rfk_mcc_info *rfk_mcc = &rtwdev->rfk_mcc; struct rtw89_fw_h2c_rf_get_mccch *mccch; struct sk_buff *skb; int ret; @@ -2276,10 +2276,10 @@ int rtw89_fw_h2c_rf_ntfy_mcc(struct rtw89_dev *rtwdev) skb_put(skb, sizeof(*mccch)); mccch = (struct rtw89_fw_h2c_rf_get_mccch *)skb->data; - mccch->ch_0 = cpu_to_le32(mcc_info->ch[0]); - mccch->ch_1 = cpu_to_le32(mcc_info->ch[1]); - mccch->band_0 = cpu_to_le32(mcc_info->band[0]); - mccch->band_1 = cpu_to_le32(mcc_info->band[1]); + mccch->ch_0 = cpu_to_le32(rfk_mcc->ch[0]); + mccch->ch_1 = cpu_to_le32(rfk_mcc->ch[1]); + mccch->band_0 = cpu_to_le32(rfk_mcc->band[0]); + mccch->band_1 = cpu_to_le32(rfk_mcc->band[1]); mccch->current_channel = cpu_to_le32(chan->channel); mccch->current_band_type = cpu_to_le32(chan->band_type); diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852c.c b/drivers/net/wireless/realtek/rtw89/rtw8852c.c index 9bc98fd5d4ac..a87482cc25f5 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852c.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852c.c @@ -1832,11 +1832,11 @@ static void rtw8852c_set_channel_help(struct rtw89_dev *rtwdev, bool enter, static void rtw8852c_rfk_init(struct rtw89_dev *rtwdev) { - struct rtw89_mcc_info *mcc_info = &rtwdev->mcc; + struct rtw89_rfk_mcc_info *rfk_mcc = &rtwdev->rfk_mcc; rtwdev->is_tssi_mode[RF_PATH_A] = false; rtwdev->is_tssi_mode[RF_PATH_B] = false; - memset(mcc_info, 0, sizeof(*mcc_info)); + memset(rfk_mcc, 0, sizeof(*rfk_mcc)); rtw8852c_lck_init(rtwdev); rtw8852c_rck(rtwdev); diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852c_rfk.c b/drivers/net/wireless/realtek/rtw89/rtw8852c_rfk.c index b0672b906e7b..60cd676fe22c 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852c_rfk.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852c_rfk.c @@ -1030,9 +1030,9 @@ static bool _iqk_nbtxk(struct rtw89_dev *rtwdev, static bool _lok_finetune_check(struct rtw89_dev *rtwdev, u8 path) { - struct rtw89_mcc_info *mcc_info = &rtwdev->mcc; + struct rtw89_rfk_mcc_info *rfk_mcc = &rtwdev->rfk_mcc; struct rtw89_iqk_info *iqk_info = &rtwdev->iqk; - u8 idx = mcc_info->table_idx; + u8 idx = rfk_mcc->table_idx; bool is_fail1, is_fail2; u32 val; u32 core_i; @@ -1375,10 +1375,10 @@ static void _iqk_afebb_restore(struct rtw89_dev *rtwdev, static void _iqk_preset(struct rtw89_dev *rtwdev, u8 path) { - struct rtw89_mcc_info *mcc_info = &rtwdev->mcc; + struct rtw89_rfk_mcc_info *rfk_mcc = &rtwdev->rfk_mcc; u8 idx = 0; - idx = mcc_info->table_idx; + idx = rfk_mcc->table_idx; rtw89_phy_write32_mask(rtwdev, R_COEF_SEL + (path << 8), B_COEF_SEL_IQC, idx); rtw89_phy_write32_mask(rtwdev, R_CFIR_LUT + (path << 8), B_CFIR_LUT_G3, idx); rtw89_write_rf(rtwdev, path, RR_RSV1, RR_RSV1_RST, 0x0); @@ -3824,20 +3824,20 @@ void rtw8852c_set_channel_rf(struct rtw89_dev *rtwdev, void rtw8852c_mcc_get_ch_info(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx) { const struct rtw89_chan *chan = rtw89_chan_get(rtwdev, RTW89_SUB_ENTITY_0); - struct rtw89_mcc_info *mcc_info = &rtwdev->mcc; - u8 idx = mcc_info->table_idx; + struct rtw89_rfk_mcc_info *rfk_mcc = &rtwdev->rfk_mcc; + u8 idx = rfk_mcc->table_idx; int i; for (i = 0; i < RTW89_IQK_CHS_NR; i++) { - if (mcc_info->ch[idx] == 0) + if (rfk_mcc->ch[idx] == 0) break; if (++idx >= RTW89_IQK_CHS_NR) idx = 0; } - mcc_info->table_idx = idx; - mcc_info->ch[idx] = chan->channel; - mcc_info->band[idx] = chan->band_type; + rfk_mcc->table_idx = idx; + rfk_mcc->ch[idx] = chan->channel; + rfk_mcc->band[idx] = chan->band_type; } void rtw8852c_rck(struct rtw89_dev *rtwdev) From 860e8263ae92667a2002163886fd2ebd8c67f699 Mon Sep 17 00:00:00 2001 From: Zong-Zhe Yang Date: Tue, 29 Nov 2022 16:31:26 +0800 Subject: [PATCH 1377/2002] wifi: rtw89: check if atomic before queuing c2h Before queuing C2H work, we check atomicity of the C2H's handler first now. If atomic or lock-free, handle it directly; otherwise, handle it with mutex in work as previous. This prepares for MAC MCC C2Hs which require to be processed directly. And, their handlers will be functions which can be considered atomic. Signed-off-by: Zong-Zhe Yang Signed-off-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20221129083130.45708-3-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/fw.c | 47 ++++++++++++++++++++++-- drivers/net/wireless/realtek/rtw89/fw.h | 14 +++++++ drivers/net/wireless/realtek/rtw89/mac.c | 10 +++++ drivers/net/wireless/realtek/rtw89/mac.h | 1 + 4 files changed, 68 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/fw.c b/drivers/net/wireless/realtek/rtw89/fw.c index 3d7b99e15967..544f6d4b6873 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.c +++ b/drivers/net/wireless/realtek/rtw89/fw.c @@ -11,6 +11,9 @@ #include "phy.h" #include "reg.h" +static void rtw89_fw_c2h_cmd_handle(struct rtw89_dev *rtwdev, + struct sk_buff *skb); + static struct sk_buff *rtw89_fw_h2c_alloc_skb(struct rtw89_dev *rtwdev, u32 len, bool header) { @@ -2382,8 +2385,43 @@ void rtw89_fw_free_all_early_h2c(struct rtw89_dev *rtwdev) mutex_unlock(&rtwdev->mutex); } +static void rtw89_fw_c2h_parse_attr(struct sk_buff *c2h) +{ + struct rtw89_fw_c2h_attr *attr = RTW89_SKB_C2H_CB(c2h); + + attr->category = RTW89_GET_C2H_CATEGORY(c2h->data); + attr->class = RTW89_GET_C2H_CLASS(c2h->data); + attr->func = RTW89_GET_C2H_FUNC(c2h->data); + attr->len = RTW89_GET_C2H_LEN(c2h->data); +} + +static bool rtw89_fw_c2h_chk_atomic(struct rtw89_dev *rtwdev, + struct sk_buff *c2h) +{ + struct rtw89_fw_c2h_attr *attr = RTW89_SKB_C2H_CB(c2h); + u8 category = attr->category; + u8 class = attr->class; + u8 func = attr->func; + + switch (category) { + default: + return false; + case RTW89_C2H_CAT_MAC: + return rtw89_mac_c2h_chk_atomic(rtwdev, class, func); + } +} + void rtw89_fw_c2h_irqsafe(struct rtw89_dev *rtwdev, struct sk_buff *c2h) { + rtw89_fw_c2h_parse_attr(c2h); + if (!rtw89_fw_c2h_chk_atomic(rtwdev, c2h)) + goto enqueue; + + rtw89_fw_c2h_cmd_handle(rtwdev, c2h); + dev_kfree_skb_any(c2h); + return; + +enqueue: skb_queue_tail(&rtwdev->c2h_queue, c2h); ieee80211_queue_work(rtwdev->hw, &rtwdev->c2h_work); } @@ -2391,10 +2429,11 @@ void rtw89_fw_c2h_irqsafe(struct rtw89_dev *rtwdev, struct sk_buff *c2h) static void rtw89_fw_c2h_cmd_handle(struct rtw89_dev *rtwdev, struct sk_buff *skb) { - u8 category = RTW89_GET_C2H_CATEGORY(skb->data); - u8 class = RTW89_GET_C2H_CLASS(skb->data); - u8 func = RTW89_GET_C2H_FUNC(skb->data); - u16 len = RTW89_GET_C2H_LEN(skb->data); + struct rtw89_fw_c2h_attr *attr = RTW89_SKB_C2H_CB(skb); + u8 category = attr->category; + u8 class = attr->class; + u8 func = attr->func; + u16 len = attr->len; bool dump = true; if (!test_bit(RTW89_FLAG_RUNNING, rtwdev->flags)) diff --git a/drivers/net/wireless/realtek/rtw89/fw.h b/drivers/net/wireless/realtek/rtw89/fw.h index 509a3eac5ffe..d76d0c80f025 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.h +++ b/drivers/net/wireless/realtek/rtw89/fw.h @@ -2778,6 +2778,20 @@ static inline void RTW89_SET_FWCMD_TSF32_TOGL_EARLY(void *cmd, u32 val) #define RTW89_GET_C2H_LEN(c2h) \ le32_get_bits(*((const __le32 *)(c2h) + 1), GENMASK(13, 0)) +struct rtw89_fw_c2h_attr { + u8 category; + u8 class; + u8 func; + u16 len; +}; + +static inline struct rtw89_fw_c2h_attr *RTW89_SKB_C2H_CB(struct sk_buff *skb) +{ + static_assert(sizeof(skb->cb) >= sizeof(struct rtw89_fw_c2h_attr)); + + return (struct rtw89_fw_c2h_attr *)skb->cb; +} + #define RTW89_GET_C2H_LOG_SRT_PRT(c2h) (char *)((__le32 *)(c2h) + 2) #define RTW89_GET_C2H_LOG_LEN(len) ((len) - RTW89_C2H_HEADER_LEN) diff --git a/drivers/net/wireless/realtek/rtw89/mac.c b/drivers/net/wireless/realtek/rtw89/mac.c index 6587cdf6ba62..098637a84895 100644 --- a/drivers/net/wireless/realtek/rtw89/mac.c +++ b/drivers/net/wireless/realtek/rtw89/mac.c @@ -4208,6 +4208,16 @@ void (* const rtw89_mac_c2h_info_handler[])(struct rtw89_dev *rtwdev, [RTW89_MAC_C2H_FUNC_BCN_CNT] = rtw89_mac_c2h_bcn_cnt, }; +bool rtw89_mac_c2h_chk_atomic(struct rtw89_dev *rtwdev, u8 class, u8 func) +{ + switch (class) { + default: + return false; + case RTW89_MAC_C2H_CLASS_MCC: + return true; + } +} + void rtw89_mac_c2h_handle(struct rtw89_dev *rtwdev, struct sk_buff *skb, u32 len, u8 class, u8 func) { diff --git a/drivers/net/wireless/realtek/rtw89/mac.h b/drivers/net/wireless/realtek/rtw89/mac.h index 045e8ec61a41..82b9e81fe474 100644 --- a/drivers/net/wireless/realtek/rtw89/mac.h +++ b/drivers/net/wireless/realtek/rtw89/mac.h @@ -894,6 +894,7 @@ static inline int rtw89_chip_disable_bb_rf(struct rtw89_dev *rtwdev) u32 rtw89_mac_get_err_status(struct rtw89_dev *rtwdev); int rtw89_mac_set_err_status(struct rtw89_dev *rtwdev, u32 err); +bool rtw89_mac_c2h_chk_atomic(struct rtw89_dev *rtwdev, u8 class, u8 func); void rtw89_mac_c2h_handle(struct rtw89_dev *rtwdev, struct sk_buff *skb, u32 len, u8 class, u8 func); int rtw89_mac_setup_phycap(struct rtw89_dev *rtwdev); From 22b10cdb73921cfb28ccde5ce8b47d7fc434e7c6 Mon Sep 17 00:00:00 2001 From: Zong-Zhe Yang Date: Tue, 29 Nov 2022 16:31:27 +0800 Subject: [PATCH 1378/2002] wifi: rtw89: introduce helpers to wait/complete on condition MCC (multi-channel concurrency) related H2Cs (host to chip commands) require to wait for C2H (chip to host events) responses to judge the execution result and data. We introduce helpers to assist this process. Besides, we would like the helpers to be generic for use in driver even outside of MCC H2C/C2H, so we make a independent patch for them. In the following, I describe the things first. ``` (A) C2H is generated by FW, and then transferred upto driver. Hence, driver cannot get it immediately without a bit waitting/blocking. For this, we choose to use wait_for_completion_*() instead of busy polling. (B) From the driver management perspective, a scenario, e.g. MCC, may have mulitple kind of H2C functions requiring this process to wait for corresponding C2Hs. But, the driver management flow uses mutex to protect each behavior. So, one scenario triggers one H2C function at one time. To avoid rampant instances of struct completion for each H2C function, we choose to use one struct completion with one condition flag for one scenario. (C) C2Hs, which H2Cs will be waitting for, cannot be ordered with driver management flow, i.e. cannot enqueue work to the same ordered workqueue and cannot lock by the same mutex, to prevent H2C side from getting no C2H responses. So, those C2Hs are parsed in interrupt context directly as done in previous commit. (D) Following (C), the above underline H2Cs and C2Hs will be handled in different contexts without sync. So, we use atomic_cmpxchg() to compare and change the condition in atomic. ``` So, we introduce struct rtw89_wait_info which combines struct completion and atomic_t. Then, the below are the descriptions for helper functions. * rtw89_wait_for_cond() to wait for a completion based on a condition. * rtw89_complete_cond() to complete a given condition and carry data. Each rtw89_wait_info instance independently determines the meaning of its waitting conditions. But, RTW89_WAIT_COND_IDLE (UINT_MAX) is reserved. Signed-off-by: Zong-Zhe Yang Signed-off-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20221129083130.45708-4-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/core.c | 35 +++++++++++++++++++++++ drivers/net/wireless/realtek/rtw89/core.h | 25 ++++++++++++++++ 2 files changed, 60 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c index 190d72df800f..9a45193c68cc 100644 --- a/drivers/net/wireless/realtek/rtw89/core.c +++ b/drivers/net/wireless/realtek/rtw89/core.c @@ -2974,6 +2974,41 @@ void rtw89_core_update_beacon_work(struct work_struct *work) mutex_unlock(&rtwdev->mutex); } +int rtw89_wait_for_cond(struct rtw89_wait_info *wait, unsigned int cond) +{ + struct completion *cmpl = &wait->completion; + unsigned long timeout; + unsigned int cur; + + cur = atomic_cmpxchg(&wait->cond, RTW89_WAIT_COND_IDLE, cond); + if (cur != RTW89_WAIT_COND_IDLE) + return -EBUSY; + + timeout = wait_for_completion_timeout(cmpl, RTW89_WAIT_FOR_COND_TIMEOUT); + if (timeout == 0) { + atomic_set(&wait->cond, RTW89_WAIT_COND_IDLE); + return -ETIMEDOUT; + } + + if (wait->data.err) + return -EFAULT; + + return 0; +} + +void rtw89_complete_cond(struct rtw89_wait_info *wait, unsigned int cond, + const struct rtw89_completion_data *data) +{ + unsigned int cur; + + cur = atomic_cmpxchg(&wait->cond, cond, RTW89_WAIT_COND_IDLE); + if (cur != cond) + return; + + wait->data = *data; + complete(&wait->completion); +} + int rtw89_core_start(struct rtw89_dev *rtwdev) { int ret; diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index d0efeeabcb77..a9b9b1c901c1 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -2812,6 +2812,28 @@ struct rtw89_mac_info { u8 cpwm_seq_num; }; +#define RTW89_COMPLETION_BUF_SIZE 24 +#define RTW89_WAIT_COND_IDLE UINT_MAX + +struct rtw89_completion_data { + bool err; + u8 buf[RTW89_COMPLETION_BUF_SIZE]; +}; + +struct rtw89_wait_info { + atomic_t cond; + struct completion completion; + struct rtw89_completion_data data; +}; + +#define RTW89_WAIT_FOR_COND_TIMEOUT msecs_to_jiffies(100) + +static inline void rtw89_init_wait(struct rtw89_wait_info *wait) +{ + init_completion(&wait->completion); + atomic_set(&wait->cond, RTW89_WAIT_COND_IDLE); +} + enum rtw89_fw_type { RTW89_FW_NORMAL = 1, RTW89_FW_WOWLAN = 3, @@ -4469,6 +4491,9 @@ int rtw89_regd_init(struct rtw89_dev *rtwdev, void rtw89_regd_notifier(struct wiphy *wiphy, struct regulatory_request *request); void rtw89_traffic_stats_init(struct rtw89_dev *rtwdev, struct rtw89_traffic_stats *stats); +int rtw89_wait_for_cond(struct rtw89_wait_info *wait, unsigned int cond); +void rtw89_complete_cond(struct rtw89_wait_info *wait, unsigned int cond, + const struct rtw89_completion_data *data); int rtw89_core_start(struct rtw89_dev *rtwdev); void rtw89_core_stop(struct rtw89_dev *rtwdev); void rtw89_core_update_beacon_work(struct work_struct *work); From ef9dff4cb491210518ad3d249919a0971eff601b Mon Sep 17 00:00:00 2001 From: Zong-Zhe Yang Date: Tue, 29 Nov 2022 16:31:28 +0800 Subject: [PATCH 1379/2002] wifi: rtw89: mac: process MCC related C2H Process C2H(s) related to MCC (multi-channel concurrency). These handling, which either call rtw89_complete_cond() or show message in debug mode, can be considered atomic/lock-free. So, they should be safe to be processed directly after C2H pre-check in previous patch. Signed-off-by: Zong-Zhe Yang Signed-off-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20221129083130.45708-5-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/core.c | 2 + drivers/net/wireless/realtek/rtw89/core.h | 5 + drivers/net/wireless/realtek/rtw89/fw.h | 68 +++++++++ drivers/net/wireless/realtek/rtw89/mac.c | 171 ++++++++++++++++++++++ drivers/net/wireless/realtek/rtw89/mac.h | 34 +++++ 5 files changed, 280 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c index 9a45193c68cc..5ab95250755d 100644 --- a/drivers/net/wireless/realtek/rtw89/core.c +++ b/drivers/net/wireless/realtek/rtw89/core.c @@ -3130,6 +3130,8 @@ int rtw89_core_init(struct rtw89_dev *rtwdev) mutex_init(&rtwdev->rf_mutex); rtwdev->total_sta_assoc = 0; + rtw89_init_wait(&rtwdev->mcc.wait); + INIT_WORK(&rtwdev->c2h_work, rtw89_fw_c2h_work); INIT_WORK(&rtwdev->ips_work, rtw89_ips_work); skb_queue_head_init(&rtwdev->c2h_queue); diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index a9b9b1c901c1..f4603c2ce8f1 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -3547,6 +3547,10 @@ struct rtw89_wow_param { struct list_head pkt_list; }; +struct rtw89_mcc_info { + struct rtw89_wait_info wait; +}; + struct rtw89_dev { struct ieee80211_hw *hw; struct device *dev; @@ -3557,6 +3561,7 @@ struct rtw89_dev { const struct rtw89_chip_info *chip; const struct rtw89_pci_info *pci_info; struct rtw89_hal hal; + struct rtw89_mcc_info mcc; struct rtw89_mac_info mac; struct rtw89_fw_info fw; struct rtw89_hci_info hci; diff --git a/drivers/net/wireless/realtek/rtw89/fw.h b/drivers/net/wireless/realtek/rtw89/fw.h index d76d0c80f025..5fb8faad9c67 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.h +++ b/drivers/net/wireless/realtek/rtw89/fw.h @@ -2859,6 +2859,55 @@ static inline struct rtw89_fw_c2h_attr *RTW89_SKB_C2H_CB(struct sk_buff *skb) #define RTW89_GET_MAC_C2H_SCANOFLD_BAND(c2h) \ le32_get_bits(*((const __le32 *)(c2h) + 5), GENMASK(25, 24)) +#define RTW89_GET_MAC_C2H_MCC_RCV_ACK_GROUP(c2h) \ + le32_get_bits(*((const __le32 *)(c2h)), GENMASK(1, 0)) +#define RTW89_GET_MAC_C2H_MCC_RCV_ACK_H2C_FUNC(c2h) \ + le32_get_bits(*((const __le32 *)(c2h)), GENMASK(15, 8)) + +#define RTW89_GET_MAC_C2H_MCC_REQ_ACK_GROUP(c2h) \ + le32_get_bits(*((const __le32 *)(c2h)), GENMASK(1, 0)) +#define RTW89_GET_MAC_C2H_MCC_REQ_ACK_H2C_RETURN(c2h) \ + le32_get_bits(*((const __le32 *)(c2h)), GENMASK(7, 2)) +#define RTW89_GET_MAC_C2H_MCC_REQ_ACK_H2C_FUNC(c2h) \ + le32_get_bits(*((const __le32 *)(c2h)), GENMASK(15, 8)) + +struct rtw89_mac_mcc_tsf_rpt { + u32 macid_x; + u32 macid_y; + u32 tsf_x_low; + u32 tsf_x_high; + u32 tsf_y_low; + u32 tsf_y_high; +}; + +static_assert(sizeof(struct rtw89_mac_mcc_tsf_rpt) <= RTW89_COMPLETION_BUF_SIZE); + +#define RTW89_GET_MAC_C2H_MCC_TSF_RPT_MACID_X(c2h) \ + le32_get_bits(*((const __le32 *)(c2h)), GENMASK(7, 0)) +#define RTW89_GET_MAC_C2H_MCC_TSF_RPT_MACID_Y(c2h) \ + le32_get_bits(*((const __le32 *)(c2h)), GENMASK(15, 8)) +#define RTW89_GET_MAC_C2H_MCC_TSF_RPT_GROUP(c2h) \ + le32_get_bits(*((const __le32 *)(c2h)), GENMASK(17, 16)) +#define RTW89_GET_MAC_C2H_MCC_TSF_RPT_TSF_LOW_X(c2h) \ + le32_get_bits(*((const __le32 *)(c2h) + 1), GENMASK(31, 0)) +#define RTW89_GET_MAC_C2H_MCC_TSF_RPT_TSF_HIGH_X(c2h) \ + le32_get_bits(*((const __le32 *)(c2h) + 2), GENMASK(31, 0)) +#define RTW89_GET_MAC_C2H_MCC_TSF_RPT_TSF_LOW_Y(c2h) \ + le32_get_bits(*((const __le32 *)(c2h) + 3), GENMASK(31, 0)) +#define RTW89_GET_MAC_C2H_MCC_TSF_RPT_TSF_HIGH_Y(c2h) \ + le32_get_bits(*((const __le32 *)(c2h) + 4), GENMASK(31, 0)) + +#define RTW89_GET_MAC_C2H_MCC_STATUS_RPT_STATUS(c2h) \ + le32_get_bits(*((const __le32 *)(c2h)), GENMASK(5, 0)) +#define RTW89_GET_MAC_C2H_MCC_STATUS_RPT_GROUP(c2h) \ + le32_get_bits(*((const __le32 *)(c2h)), GENMASK(7, 6)) +#define RTW89_GET_MAC_C2H_MCC_STATUS_RPT_MACID(c2h) \ + le32_get_bits(*((const __le32 *)(c2h)), GENMASK(15, 8)) +#define RTW89_GET_MAC_C2H_MCC_STATUS_RPT_TSF_LOW(c2h) \ + le32_get_bits(*((const __le32 *)(c2h) + 1), GENMASK(31, 0)) +#define RTW89_GET_MAC_C2H_MCC_STATUS_RPT_TSF_HIGH(c2h) \ + le32_get_bits(*((const __le32 *)(c2h) + 2), GENMASK(31, 0)) + #define RTW89_FW_HDR_SIZE 32 #define RTW89_FW_SECTION_HDR_SIZE 16 @@ -2980,6 +3029,25 @@ struct rtw89_fw_h2c_rf_reg_info { #define H2C_CL_BA_CAM 0xc #define H2C_FUNC_MAC_BA_CAM 0x0 +/* CLASS 14 - MCC */ +#define H2C_CL_MCC 0xe +enum rtw89_mcc_h2c_func { + H2C_FUNC_ADD_MCC = 0x0, + H2C_FUNC_START_MCC = 0x1, + H2C_FUNC_STOP_MCC = 0x2, + H2C_FUNC_DEL_MCC_GROUP = 0x3, + H2C_FUNC_RESET_MCC_GROUP = 0x4, + H2C_FUNC_MCC_REQ_TSF = 0x5, + H2C_FUNC_MCC_MACID_BITMAP = 0x6, + H2C_FUNC_MCC_SYNC = 0x7, + H2C_FUNC_MCC_SET_DURATION = 0x8, + + NUM_OF_RTW89_MCC_H2C_FUNC, +}; + +#define RTW89_MCC_WAIT_COND(group, func) \ + ((group) * NUM_OF_RTW89_MCC_H2C_FUNC + (func)) + #define H2C_CAT_OUTSRC 0x2 #define H2C_CL_OUTSRC_RA 0x1 diff --git a/drivers/net/wireless/realtek/rtw89/mac.c b/drivers/net/wireless/realtek/rtw89/mac.c index 098637a84895..d80050c2e9b3 100644 --- a/drivers/net/wireless/realtek/rtw89/mac.c +++ b/drivers/net/wireless/realtek/rtw89/mac.c @@ -4187,6 +4187,164 @@ rtw89_mac_c2h_tsf32_toggle_rpt(struct rtw89_dev *rtwdev, struct sk_buff *c2h, { } +static void +rtw89_mac_c2h_mcc_rcv_ack(struct rtw89_dev *rtwdev, struct sk_buff *c2h, u32 len) +{ + u8 group = RTW89_GET_MAC_C2H_MCC_RCV_ACK_GROUP(c2h->data); + u8 func = RTW89_GET_MAC_C2H_MCC_RCV_ACK_H2C_FUNC(c2h->data); + + switch (func) { + case H2C_FUNC_ADD_MCC: + case H2C_FUNC_START_MCC: + case H2C_FUNC_STOP_MCC: + case H2C_FUNC_DEL_MCC_GROUP: + case H2C_FUNC_RESET_MCC_GROUP: + case H2C_FUNC_MCC_REQ_TSF: + case H2C_FUNC_MCC_MACID_BITMAP: + case H2C_FUNC_MCC_SYNC: + case H2C_FUNC_MCC_SET_DURATION: + break; + default: + rtw89_debug(rtwdev, RTW89_DBG_FW, + "invalid MCC C2H RCV ACK: func %d\n", func); + return; + } + + rtw89_debug(rtwdev, RTW89_DBG_FW, + "MCC C2H RCV ACK: group %d, func %d\n", group, func); +} + +static void +rtw89_mac_c2h_mcc_req_ack(struct rtw89_dev *rtwdev, struct sk_buff *c2h, u32 len) +{ + u8 group = RTW89_GET_MAC_C2H_MCC_REQ_ACK_GROUP(c2h->data); + u8 func = RTW89_GET_MAC_C2H_MCC_REQ_ACK_H2C_FUNC(c2h->data); + u8 retcode = RTW89_GET_MAC_C2H_MCC_REQ_ACK_H2C_RETURN(c2h->data); + struct rtw89_completion_data data = {}; + unsigned int cond; + bool next = false; + + switch (func) { + case H2C_FUNC_MCC_REQ_TSF: + next = true; + break; + case H2C_FUNC_MCC_MACID_BITMAP: + case H2C_FUNC_MCC_SYNC: + case H2C_FUNC_MCC_SET_DURATION: + break; + case H2C_FUNC_ADD_MCC: + case H2C_FUNC_START_MCC: + case H2C_FUNC_STOP_MCC: + case H2C_FUNC_DEL_MCC_GROUP: + case H2C_FUNC_RESET_MCC_GROUP: + default: + rtw89_debug(rtwdev, RTW89_DBG_FW, + "invalid MCC C2H REQ ACK: func %d\n", func); + return; + } + + rtw89_debug(rtwdev, RTW89_DBG_FW, + "MCC C2H REQ ACK: group %d, func %d, return code %d\n", + group, func, retcode); + + if (!retcode && next) + return; + + data.err = !!retcode; + cond = RTW89_MCC_WAIT_COND(group, func); + rtw89_complete_cond(&rtwdev->mcc.wait, cond, &data); +} + +static void +rtw89_mac_c2h_mcc_tsf_rpt(struct rtw89_dev *rtwdev, struct sk_buff *c2h, u32 len) +{ + u8 group = RTW89_GET_MAC_C2H_MCC_TSF_RPT_GROUP(c2h->data); + struct rtw89_completion_data data = {}; + struct rtw89_mac_mcc_tsf_rpt *rpt; + unsigned int cond; + + rpt = (struct rtw89_mac_mcc_tsf_rpt *)data.buf; + rpt->macid_x = RTW89_GET_MAC_C2H_MCC_TSF_RPT_MACID_X(c2h->data); + rpt->macid_y = RTW89_GET_MAC_C2H_MCC_TSF_RPT_MACID_Y(c2h->data); + rpt->tsf_x_low = RTW89_GET_MAC_C2H_MCC_TSF_RPT_TSF_LOW_X(c2h->data); + rpt->tsf_x_high = RTW89_GET_MAC_C2H_MCC_TSF_RPT_TSF_HIGH_X(c2h->data); + rpt->tsf_y_low = RTW89_GET_MAC_C2H_MCC_TSF_RPT_TSF_LOW_Y(c2h->data); + rpt->tsf_y_high = RTW89_GET_MAC_C2H_MCC_TSF_RPT_TSF_HIGH_Y(c2h->data); + + cond = RTW89_MCC_WAIT_COND(group, H2C_FUNC_MCC_REQ_TSF); + rtw89_complete_cond(&rtwdev->mcc.wait, cond, &data); +} + +static void +rtw89_mac_c2h_mcc_status_rpt(struct rtw89_dev *rtwdev, struct sk_buff *c2h, u32 len) +{ + u8 group = RTW89_GET_MAC_C2H_MCC_STATUS_RPT_GROUP(c2h->data); + u8 macid = RTW89_GET_MAC_C2H_MCC_STATUS_RPT_MACID(c2h->data); + u8 status = RTW89_GET_MAC_C2H_MCC_STATUS_RPT_STATUS(c2h->data); + u32 tsf_low = RTW89_GET_MAC_C2H_MCC_STATUS_RPT_TSF_LOW(c2h->data); + u32 tsf_high = RTW89_GET_MAC_C2H_MCC_STATUS_RPT_TSF_HIGH(c2h->data); + struct rtw89_completion_data data = {}; + unsigned int cond; + bool rsp = true; + bool err; + u8 func; + + switch (status) { + case RTW89_MAC_MCC_ADD_ROLE_OK: + case RTW89_MAC_MCC_ADD_ROLE_FAIL: + func = H2C_FUNC_ADD_MCC; + err = status == RTW89_MAC_MCC_ADD_ROLE_FAIL; + break; + case RTW89_MAC_MCC_START_GROUP_OK: + case RTW89_MAC_MCC_START_GROUP_FAIL: + func = H2C_FUNC_START_MCC; + err = status == RTW89_MAC_MCC_START_GROUP_FAIL; + break; + case RTW89_MAC_MCC_STOP_GROUP_OK: + case RTW89_MAC_MCC_STOP_GROUP_FAIL: + func = H2C_FUNC_STOP_MCC; + err = status == RTW89_MAC_MCC_STOP_GROUP_FAIL; + break; + case RTW89_MAC_MCC_DEL_GROUP_OK: + case RTW89_MAC_MCC_DEL_GROUP_FAIL: + func = H2C_FUNC_DEL_MCC_GROUP; + err = status == RTW89_MAC_MCC_DEL_GROUP_FAIL; + break; + case RTW89_MAC_MCC_RESET_GROUP_OK: + case RTW89_MAC_MCC_RESET_GROUP_FAIL: + func = H2C_FUNC_RESET_MCC_GROUP; + err = status == RTW89_MAC_MCC_RESET_GROUP_FAIL; + break; + case RTW89_MAC_MCC_SWITCH_CH_OK: + case RTW89_MAC_MCC_SWITCH_CH_FAIL: + case RTW89_MAC_MCC_TXNULL0_OK: + case RTW89_MAC_MCC_TXNULL0_FAIL: + case RTW89_MAC_MCC_TXNULL1_OK: + case RTW89_MAC_MCC_TXNULL1_FAIL: + case RTW89_MAC_MCC_SWITCH_EARLY: + case RTW89_MAC_MCC_TBTT: + case RTW89_MAC_MCC_DURATION_START: + case RTW89_MAC_MCC_DURATION_END: + rsp = false; + break; + default: + rtw89_debug(rtwdev, RTW89_DBG_FW, + "invalid MCC C2H STS RPT: status %d\n", status); + return; + } + + rtw89_debug(rtwdev, RTW89_DBG_FW, + "MCC C2H STS RPT: group %d, macid %d, status %d, tsf {%d, %d}\n", + group, macid, status, tsf_low, tsf_high); + + if (!rsp) + return; + + data.err = err; + cond = RTW89_MCC_WAIT_COND(group, func); + rtw89_complete_cond(&rtwdev->mcc.wait, cond, &data); +} + static void (* const rtw89_mac_c2h_ofld_handler[])(struct rtw89_dev *rtwdev, struct sk_buff *c2h, u32 len) = { @@ -4208,6 +4366,15 @@ void (* const rtw89_mac_c2h_info_handler[])(struct rtw89_dev *rtwdev, [RTW89_MAC_C2H_FUNC_BCN_CNT] = rtw89_mac_c2h_bcn_cnt, }; +static +void (* const rtw89_mac_c2h_mcc_handler[])(struct rtw89_dev *rtwdev, + struct sk_buff *c2h, u32 len) = { + [RTW89_MAC_C2H_FUNC_MCC_RCV_ACK] = rtw89_mac_c2h_mcc_rcv_ack, + [RTW89_MAC_C2H_FUNC_MCC_REQ_ACK] = rtw89_mac_c2h_mcc_req_ack, + [RTW89_MAC_C2H_FUNC_MCC_TSF_RPT] = rtw89_mac_c2h_mcc_tsf_rpt, + [RTW89_MAC_C2H_FUNC_MCC_STATUS_RPT] = rtw89_mac_c2h_mcc_status_rpt, +}; + bool rtw89_mac_c2h_chk_atomic(struct rtw89_dev *rtwdev, u8 class, u8 func) { switch (class) { @@ -4233,6 +4400,10 @@ void rtw89_mac_c2h_handle(struct rtw89_dev *rtwdev, struct sk_buff *skb, if (func < RTW89_MAC_C2H_FUNC_OFLD_MAX) handler = rtw89_mac_c2h_ofld_handler[func]; break; + case RTW89_MAC_C2H_CLASS_MCC: + if (func < NUM_OF_RTW89_MAC_C2H_FUNC_MCC) + handler = rtw89_mac_c2h_mcc_handler[func]; + break; case RTW89_MAC_C2H_CLASS_FWDBG: return; default: diff --git a/drivers/net/wireless/realtek/rtw89/mac.h b/drivers/net/wireless/realtek/rtw89/mac.h index 82b9e81fe474..adb0c86a98d3 100644 --- a/drivers/net/wireless/realtek/rtw89/mac.h +++ b/drivers/net/wireless/realtek/rtw89/mac.h @@ -368,6 +368,15 @@ enum rtw89_mac_c2h_info_func { RTW89_MAC_C2H_FUNC_INFO_MAX, }; +enum rtw89_mac_c2h_mcc_func { + RTW89_MAC_C2H_FUNC_MCC_RCV_ACK = 0, + RTW89_MAC_C2H_FUNC_MCC_REQ_ACK = 1, + RTW89_MAC_C2H_FUNC_MCC_TSF_RPT = 2, + RTW89_MAC_C2H_FUNC_MCC_STATUS_RPT = 3, + + NUM_OF_RTW89_MAC_C2H_FUNC_MCC, +}; + enum rtw89_mac_c2h_class { RTW89_MAC_C2H_CLASS_INFO, RTW89_MAC_C2H_CLASS_OFLD, @@ -378,6 +387,31 @@ enum rtw89_mac_c2h_class { RTW89_MAC_C2H_CLASS_MAX, }; +enum rtw89_mac_mcc_status { + RTW89_MAC_MCC_ADD_ROLE_OK = 0, + RTW89_MAC_MCC_START_GROUP_OK = 1, + RTW89_MAC_MCC_STOP_GROUP_OK = 2, + RTW89_MAC_MCC_DEL_GROUP_OK = 3, + RTW89_MAC_MCC_RESET_GROUP_OK = 4, + RTW89_MAC_MCC_SWITCH_CH_OK = 5, + RTW89_MAC_MCC_TXNULL0_OK = 6, + RTW89_MAC_MCC_TXNULL1_OK = 7, + + RTW89_MAC_MCC_SWITCH_EARLY = 10, + RTW89_MAC_MCC_TBTT = 11, + RTW89_MAC_MCC_DURATION_START = 12, + RTW89_MAC_MCC_DURATION_END = 13, + + RTW89_MAC_MCC_ADD_ROLE_FAIL = 20, + RTW89_MAC_MCC_START_GROUP_FAIL = 21, + RTW89_MAC_MCC_STOP_GROUP_FAIL = 22, + RTW89_MAC_MCC_DEL_GROUP_FAIL = 23, + RTW89_MAC_MCC_RESET_GROUP_FAIL = 24, + RTW89_MAC_MCC_SWITCH_CH_FAIL = 25, + RTW89_MAC_MCC_TXNULL0_FAIL = 26, + RTW89_MAC_MCC_TXNULL1_FAIL = 27, +}; + struct rtw89_mac_ax_coex { #define RTW89_MAC_AX_COEX_RTK_MODE 0 #define RTW89_MAC_AX_COEX_CSR_MODE 1 From c008c4b011baa26b9545f7be10e746c97409d45b Mon Sep 17 00:00:00 2001 From: Zong-Zhe Yang Date: Tue, 29 Nov 2022 16:31:29 +0800 Subject: [PATCH 1380/2002] wifi: rtw89: fw: implement MCC related H2C These MCC H2C(s) require to wait for MCC C2H to determine if the execution is successful. Through rtw89_wait_for_cond(), we make them wait for either a completion with data from MCC C2H handlers, which calls rtw89_complete_cond(), or timeout. Signed-off-by: Zong-Zhe Yang Signed-off-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20221129083130.45708-6-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/fw.c | 329 +++++++++++++++++++++ drivers/net/wireless/realtek/rtw89/fw.h | 369 +++++++++++++++++++++++- 2 files changed, 697 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/realtek/rtw89/fw.c b/drivers/net/wireless/realtek/rtw89/fw.c index 544f6d4b6873..7e682709232d 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.c +++ b/drivers/net/wireless/realtek/rtw89/fw.c @@ -3263,3 +3263,332 @@ fail: return ret; } + +static int rtw89_h2c_tx_and_wait(struct rtw89_dev *rtwdev, struct sk_buff *skb, + struct rtw89_wait_info *wait, unsigned int cond) +{ + int ret; + + ret = rtw89_h2c_tx(rtwdev, skb, false); + if (ret) { + rtw89_err(rtwdev, "failed to send h2c\n"); + dev_kfree_skb_any(skb); + return -EBUSY; + } + + return rtw89_wait_for_cond(wait, cond); +} + +#define H2C_ADD_MCC_LEN 16 +int rtw89_fw_h2c_add_mcc(struct rtw89_dev *rtwdev, + const struct rtw89_fw_mcc_add_req *p) +{ + struct rtw89_wait_info *wait = &rtwdev->mcc.wait; + struct sk_buff *skb; + unsigned int cond; + + skb = rtw89_fw_h2c_alloc_skb_with_hdr(rtwdev, H2C_ADD_MCC_LEN); + if (!skb) { + rtw89_err(rtwdev, + "failed to alloc skb for add mcc\n"); + return -ENOMEM; + } + + skb_put(skb, H2C_ADD_MCC_LEN); + RTW89_SET_FWCMD_ADD_MCC_MACID(skb->data, p->macid); + RTW89_SET_FWCMD_ADD_MCC_CENTRAL_CH_SEG0(skb->data, p->central_ch_seg0); + RTW89_SET_FWCMD_ADD_MCC_CENTRAL_CH_SEG1(skb->data, p->central_ch_seg1); + RTW89_SET_FWCMD_ADD_MCC_PRIMARY_CH(skb->data, p->primary_ch); + RTW89_SET_FWCMD_ADD_MCC_BANDWIDTH(skb->data, p->bandwidth); + RTW89_SET_FWCMD_ADD_MCC_GROUP(skb->data, p->group); + RTW89_SET_FWCMD_ADD_MCC_C2H_RPT(skb->data, p->c2h_rpt); + RTW89_SET_FWCMD_ADD_MCC_DIS_TX_NULL(skb->data, p->dis_tx_null); + RTW89_SET_FWCMD_ADD_MCC_DIS_SW_RETRY(skb->data, p->dis_sw_retry); + RTW89_SET_FWCMD_ADD_MCC_IN_CURR_CH(skb->data, p->in_curr_ch); + RTW89_SET_FWCMD_ADD_MCC_SW_RETRY_COUNT(skb->data, p->sw_retry_count); + RTW89_SET_FWCMD_ADD_MCC_TX_NULL_EARLY(skb->data, p->tx_null_early); + RTW89_SET_FWCMD_ADD_MCC_BTC_IN_2G(skb->data, p->btc_in_2g); + RTW89_SET_FWCMD_ADD_MCC_PTA_EN(skb->data, p->pta_en); + RTW89_SET_FWCMD_ADD_MCC_RFK_BY_PASS(skb->data, p->rfk_by_pass); + RTW89_SET_FWCMD_ADD_MCC_CH_BAND_TYPE(skb->data, p->ch_band_type); + RTW89_SET_FWCMD_ADD_MCC_DURATION(skb->data, p->duration); + RTW89_SET_FWCMD_ADD_MCC_COURTESY_EN(skb->data, p->courtesy_en); + RTW89_SET_FWCMD_ADD_MCC_COURTESY_NUM(skb->data, p->courtesy_num); + RTW89_SET_FWCMD_ADD_MCC_COURTESY_TARGET(skb->data, p->courtesy_target); + + rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C, + H2C_CAT_MAC, + H2C_CL_MCC, + H2C_FUNC_ADD_MCC, 0, 0, + H2C_ADD_MCC_LEN); + + cond = RTW89_MCC_WAIT_COND(p->group, H2C_FUNC_ADD_MCC); + return rtw89_h2c_tx_and_wait(rtwdev, skb, wait, cond); +} + +#define H2C_START_MCC_LEN 12 +int rtw89_fw_h2c_start_mcc(struct rtw89_dev *rtwdev, + const struct rtw89_fw_mcc_start_req *p) +{ + struct rtw89_wait_info *wait = &rtwdev->mcc.wait; + struct sk_buff *skb; + unsigned int cond; + + skb = rtw89_fw_h2c_alloc_skb_with_hdr(rtwdev, H2C_START_MCC_LEN); + if (!skb) { + rtw89_err(rtwdev, + "failed to alloc skb for start mcc\n"); + return -ENOMEM; + } + + skb_put(skb, H2C_START_MCC_LEN); + RTW89_SET_FWCMD_START_MCC_GROUP(skb->data, p->group); + RTW89_SET_FWCMD_START_MCC_BTC_IN_GROUP(skb->data, p->btc_in_group); + RTW89_SET_FWCMD_START_MCC_OLD_GROUP_ACTION(skb->data, p->old_group_action); + RTW89_SET_FWCMD_START_MCC_OLD_GROUP(skb->data, p->old_group); + RTW89_SET_FWCMD_START_MCC_NOTIFY_CNT(skb->data, p->notify_cnt); + RTW89_SET_FWCMD_START_MCC_NOTIFY_RXDBG_EN(skb->data, p->notify_rxdbg_en); + RTW89_SET_FWCMD_START_MCC_MACID(skb->data, p->macid); + RTW89_SET_FWCMD_START_MCC_TSF_LOW(skb->data, p->tsf_low); + RTW89_SET_FWCMD_START_MCC_TSF_HIGH(skb->data, p->tsf_high); + + rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C, + H2C_CAT_MAC, + H2C_CL_MCC, + H2C_FUNC_START_MCC, 0, 0, + H2C_START_MCC_LEN); + + cond = RTW89_MCC_WAIT_COND(p->group, H2C_FUNC_START_MCC); + return rtw89_h2c_tx_and_wait(rtwdev, skb, wait, cond); +} + +#define H2C_STOP_MCC_LEN 4 +int rtw89_fw_h2c_stop_mcc(struct rtw89_dev *rtwdev, u8 group, u8 macid, + bool prev_groups) +{ + struct rtw89_wait_info *wait = &rtwdev->mcc.wait; + struct sk_buff *skb; + unsigned int cond; + + skb = rtw89_fw_h2c_alloc_skb_with_hdr(rtwdev, H2C_STOP_MCC_LEN); + if (!skb) { + rtw89_err(rtwdev, + "failed to alloc skb for stop mcc\n"); + return -ENOMEM; + } + + skb_put(skb, H2C_STOP_MCC_LEN); + RTW89_SET_FWCMD_STOP_MCC_MACID(skb->data, macid); + RTW89_SET_FWCMD_STOP_MCC_GROUP(skb->data, group); + RTW89_SET_FWCMD_STOP_MCC_PREV_GROUPS(skb->data, prev_groups); + + rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C, + H2C_CAT_MAC, + H2C_CL_MCC, + H2C_FUNC_STOP_MCC, 0, 0, + H2C_STOP_MCC_LEN); + + cond = RTW89_MCC_WAIT_COND(group, H2C_FUNC_STOP_MCC); + return rtw89_h2c_tx_and_wait(rtwdev, skb, wait, cond); +} + +#define H2C_DEL_MCC_GROUP_LEN 4 +int rtw89_fw_h2c_del_mcc_group(struct rtw89_dev *rtwdev, u8 group, + bool prev_groups) +{ + struct rtw89_wait_info *wait = &rtwdev->mcc.wait; + struct sk_buff *skb; + unsigned int cond; + + skb = rtw89_fw_h2c_alloc_skb_with_hdr(rtwdev, H2C_DEL_MCC_GROUP_LEN); + if (!skb) { + rtw89_err(rtwdev, + "failed to alloc skb for del mcc group\n"); + return -ENOMEM; + } + + skb_put(skb, H2C_DEL_MCC_GROUP_LEN); + RTW89_SET_FWCMD_DEL_MCC_GROUP_GROUP(skb->data, group); + RTW89_SET_FWCMD_DEL_MCC_GROUP_PREV_GROUPS(skb->data, prev_groups); + + rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C, + H2C_CAT_MAC, + H2C_CL_MCC, + H2C_FUNC_DEL_MCC_GROUP, 0, 0, + H2C_DEL_MCC_GROUP_LEN); + + cond = RTW89_MCC_WAIT_COND(group, H2C_FUNC_DEL_MCC_GROUP); + return rtw89_h2c_tx_and_wait(rtwdev, skb, wait, cond); +} + +#define H2C_RESET_MCC_GROUP_LEN 4 +int rtw89_fw_h2c_reset_mcc_group(struct rtw89_dev *rtwdev, u8 group) +{ + struct rtw89_wait_info *wait = &rtwdev->mcc.wait; + struct sk_buff *skb; + unsigned int cond; + + skb = rtw89_fw_h2c_alloc_skb_with_hdr(rtwdev, H2C_RESET_MCC_GROUP_LEN); + if (!skb) { + rtw89_err(rtwdev, + "failed to alloc skb for reset mcc group\n"); + return -ENOMEM; + } + + skb_put(skb, H2C_RESET_MCC_GROUP_LEN); + RTW89_SET_FWCMD_RESET_MCC_GROUP_GROUP(skb->data, group); + + rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C, + H2C_CAT_MAC, + H2C_CL_MCC, + H2C_FUNC_RESET_MCC_GROUP, 0, 0, + H2C_RESET_MCC_GROUP_LEN); + + cond = RTW89_MCC_WAIT_COND(group, H2C_FUNC_RESET_MCC_GROUP); + return rtw89_h2c_tx_and_wait(rtwdev, skb, wait, cond); +} + +#define H2C_MCC_REQ_TSF_LEN 4 +int rtw89_fw_h2c_mcc_req_tsf(struct rtw89_dev *rtwdev, + const struct rtw89_fw_mcc_tsf_req *req, + struct rtw89_mac_mcc_tsf_rpt *rpt) +{ + struct rtw89_wait_info *wait = &rtwdev->mcc.wait; + struct rtw89_mac_mcc_tsf_rpt *tmp; + struct sk_buff *skb; + unsigned int cond; + int ret; + + skb = rtw89_fw_h2c_alloc_skb_with_hdr(rtwdev, H2C_MCC_REQ_TSF_LEN); + if (!skb) { + rtw89_err(rtwdev, + "failed to alloc skb for mcc req tsf\n"); + return -ENOMEM; + } + + skb_put(skb, H2C_MCC_REQ_TSF_LEN); + RTW89_SET_FWCMD_MCC_REQ_TSF_GROUP(skb->data, req->group); + RTW89_SET_FWCMD_MCC_REQ_TSF_MACID_X(skb->data, req->macid_x); + RTW89_SET_FWCMD_MCC_REQ_TSF_MACID_Y(skb->data, req->macid_y); + + rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C, + H2C_CAT_MAC, + H2C_CL_MCC, + H2C_FUNC_MCC_REQ_TSF, 0, 0, + H2C_MCC_REQ_TSF_LEN); + + cond = RTW89_MCC_WAIT_COND(req->group, H2C_FUNC_MCC_REQ_TSF); + ret = rtw89_h2c_tx_and_wait(rtwdev, skb, wait, cond); + if (ret) + return ret; + + tmp = (struct rtw89_mac_mcc_tsf_rpt *)wait->data.buf; + *rpt = *tmp; + + return 0; +} + +#define H2C_MCC_MACID_BITMAP_DSC_LEN 4 +int rtw89_fw_h2c_mcc_macid_bitamp(struct rtw89_dev *rtwdev, u8 group, u8 macid, + u8 *bitmap) +{ + struct rtw89_wait_info *wait = &rtwdev->mcc.wait; + struct sk_buff *skb; + unsigned int cond; + u8 map_len; + u8 h2c_len; + + BUILD_BUG_ON(RTW89_MAX_MAC_ID_NUM % 8); + map_len = RTW89_MAX_MAC_ID_NUM / 8; + h2c_len = H2C_MCC_MACID_BITMAP_DSC_LEN + map_len; + skb = rtw89_fw_h2c_alloc_skb_with_hdr(rtwdev, h2c_len); + if (!skb) { + rtw89_err(rtwdev, + "failed to alloc skb for mcc macid bitmap\n"); + return -ENOMEM; + } + + skb_put(skb, h2c_len); + RTW89_SET_FWCMD_MCC_MACID_BITMAP_GROUP(skb->data, group); + RTW89_SET_FWCMD_MCC_MACID_BITMAP_MACID(skb->data, macid); + RTW89_SET_FWCMD_MCC_MACID_BITMAP_BITMAP_LENGTH(skb->data, map_len); + RTW89_SET_FWCMD_MCC_MACID_BITMAP_BITMAP(skb->data, bitmap, map_len); + + rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C, + H2C_CAT_MAC, + H2C_CL_MCC, + H2C_FUNC_MCC_MACID_BITMAP, 0, 0, + h2c_len); + + cond = RTW89_MCC_WAIT_COND(group, H2C_FUNC_MCC_MACID_BITMAP); + return rtw89_h2c_tx_and_wait(rtwdev, skb, wait, cond); +} + +#define H2C_MCC_SYNC_LEN 4 +int rtw89_fw_h2c_mcc_sync(struct rtw89_dev *rtwdev, u8 group, u8 source, + u8 target, u8 offset) +{ + struct rtw89_wait_info *wait = &rtwdev->mcc.wait; + struct sk_buff *skb; + unsigned int cond; + + skb = rtw89_fw_h2c_alloc_skb_with_hdr(rtwdev, H2C_MCC_SYNC_LEN); + if (!skb) { + rtw89_err(rtwdev, + "failed to alloc skb for mcc sync\n"); + return -ENOMEM; + } + + skb_put(skb, H2C_MCC_SYNC_LEN); + RTW89_SET_FWCMD_MCC_SYNC_GROUP(skb->data, group); + RTW89_SET_FWCMD_MCC_SYNC_MACID_SOURCE(skb->data, source); + RTW89_SET_FWCMD_MCC_SYNC_MACID_TARGET(skb->data, target); + RTW89_SET_FWCMD_MCC_SYNC_SYNC_OFFSET(skb->data, offset); + + rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C, + H2C_CAT_MAC, + H2C_CL_MCC, + H2C_FUNC_MCC_SYNC, 0, 0, + H2C_MCC_SYNC_LEN); + + cond = RTW89_MCC_WAIT_COND(group, H2C_FUNC_MCC_SYNC); + return rtw89_h2c_tx_and_wait(rtwdev, skb, wait, cond); +} + +#define H2C_MCC_SET_DURATION_LEN 20 +int rtw89_fw_h2c_mcc_set_duration(struct rtw89_dev *rtwdev, + const struct rtw89_fw_mcc_duration *p) +{ + struct rtw89_wait_info *wait = &rtwdev->mcc.wait; + struct sk_buff *skb; + unsigned int cond; + + skb = rtw89_fw_h2c_alloc_skb_with_hdr(rtwdev, H2C_MCC_SET_DURATION_LEN); + if (!skb) { + rtw89_err(rtwdev, + "failed to alloc skb for mcc set duration\n"); + return -ENOMEM; + } + + skb_put(skb, H2C_MCC_SET_DURATION_LEN); + RTW89_SET_FWCMD_MCC_SET_DURATION_GROUP(skb->data, p->group); + RTW89_SET_FWCMD_MCC_SET_DURATION_BTC_IN_GROUP(skb->data, p->btc_in_group); + RTW89_SET_FWCMD_MCC_SET_DURATION_START_MACID(skb->data, p->start_macid); + RTW89_SET_FWCMD_MCC_SET_DURATION_MACID_X(skb->data, p->macid_x); + RTW89_SET_FWCMD_MCC_SET_DURATION_MACID_Y(skb->data, p->macid_y); + RTW89_SET_FWCMD_MCC_SET_DURATION_START_TSF_LOW(skb->data, + p->start_tsf_low); + RTW89_SET_FWCMD_MCC_SET_DURATION_START_TSF_HIGH(skb->data, + p->start_tsf_high); + RTW89_SET_FWCMD_MCC_SET_DURATION_DURATION_X(skb->data, p->duration_x); + RTW89_SET_FWCMD_MCC_SET_DURATION_DURATION_Y(skb->data, p->duration_y); + + rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C, + H2C_CAT_MAC, + H2C_CL_MCC, + H2C_FUNC_MCC_SET_DURATION, 0, 0, + H2C_MCC_SET_DURATION_LEN); + + cond = RTW89_MCC_WAIT_COND(p->group, H2C_FUNC_MCC_SET_DURATION); + return rtw89_h2c_tx_and_wait(rtwdev, skb, wait, cond); +} diff --git a/drivers/net/wireless/realtek/rtw89/fw.h b/drivers/net/wireless/realtek/rtw89/fw.h index 5fb8faad9c67..46d57414f24e 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.h +++ b/drivers/net/wireless/realtek/rtw89/fw.h @@ -2767,6 +2767,355 @@ static inline void RTW89_SET_FWCMD_TSF32_TOGL_EARLY(void *cmd, u32 val) le32p_replace_bits((__le32 *)cmd, val, GENMASK(31, 16)); } +enum rtw89_fw_mcc_c2h_rpt_cfg { + RTW89_FW_MCC_C2H_RPT_OFF = 0, + RTW89_FW_MCC_C2H_RPT_FAIL_ONLY = 1, + RTW89_FW_MCC_C2H_RPT_ALL = 2, +}; + +struct rtw89_fw_mcc_add_req { + u8 macid; + u8 central_ch_seg0; + u8 central_ch_seg1; + u8 primary_ch; + enum rtw89_bandwidth bandwidth: 4; + u32 group: 2; + u32 c2h_rpt: 2; + u32 dis_tx_null: 1; + u32 dis_sw_retry: 1; + u32 in_curr_ch: 1; + u32 sw_retry_count: 3; + u32 tx_null_early: 4; + u32 btc_in_2g: 1; + u32 pta_en: 1; + u32 rfk_by_pass: 1; + u32 ch_band_type: 2; + u32 rsvd0: 9; + u32 duration; + u8 courtesy_en; + u8 courtesy_num; + u8 courtesy_target; + u8 rsvd1; +}; + +static inline void RTW89_SET_FWCMD_ADD_MCC_MACID(void *cmd, u32 val) +{ + le32p_replace_bits((__le32 *)cmd, val, GENMASK(7, 0)); +} + +static inline void RTW89_SET_FWCMD_ADD_MCC_CENTRAL_CH_SEG0(void *cmd, u32 val) +{ + le32p_replace_bits((__le32 *)cmd, val, GENMASK(15, 8)); +} + +static inline void RTW89_SET_FWCMD_ADD_MCC_CENTRAL_CH_SEG1(void *cmd, u32 val) +{ + le32p_replace_bits((__le32 *)cmd, val, GENMASK(23, 16)); +} + +static inline void RTW89_SET_FWCMD_ADD_MCC_PRIMARY_CH(void *cmd, u32 val) +{ + le32p_replace_bits((__le32 *)cmd, val, GENMASK(31, 24)); +} + +static inline void RTW89_SET_FWCMD_ADD_MCC_BANDWIDTH(void *cmd, u32 val) +{ + le32p_replace_bits((__le32 *)cmd + 1, val, GENMASK(3, 0)); +} + +static inline void RTW89_SET_FWCMD_ADD_MCC_GROUP(void *cmd, u32 val) +{ + le32p_replace_bits((__le32 *)cmd + 1, val, GENMASK(5, 4)); +} + +static inline void RTW89_SET_FWCMD_ADD_MCC_C2H_RPT(void *cmd, u32 val) +{ + le32p_replace_bits((__le32 *)cmd + 1, val, GENMASK(7, 6)); +} + +static inline void RTW89_SET_FWCMD_ADD_MCC_DIS_TX_NULL(void *cmd, u32 val) +{ + le32p_replace_bits((__le32 *)cmd + 1, val, BIT(8)); +} + +static inline void RTW89_SET_FWCMD_ADD_MCC_DIS_SW_RETRY(void *cmd, u32 val) +{ + le32p_replace_bits((__le32 *)cmd + 1, val, BIT(9)); +} + +static inline void RTW89_SET_FWCMD_ADD_MCC_IN_CURR_CH(void *cmd, u32 val) +{ + le32p_replace_bits((__le32 *)cmd + 1, val, BIT(10)); +} + +static inline void RTW89_SET_FWCMD_ADD_MCC_SW_RETRY_COUNT(void *cmd, u32 val) +{ + le32p_replace_bits((__le32 *)cmd + 1, val, GENMASK(13, 11)); +} + +static inline void RTW89_SET_FWCMD_ADD_MCC_TX_NULL_EARLY(void *cmd, u32 val) +{ + le32p_replace_bits((__le32 *)cmd + 1, val, GENMASK(17, 14)); +} + +static inline void RTW89_SET_FWCMD_ADD_MCC_BTC_IN_2G(void *cmd, u32 val) +{ + le32p_replace_bits((__le32 *)cmd + 1, val, BIT(18)); +} + +static inline void RTW89_SET_FWCMD_ADD_MCC_PTA_EN(void *cmd, u32 val) +{ + le32p_replace_bits((__le32 *)cmd + 1, val, BIT(19)); +} + +static inline void RTW89_SET_FWCMD_ADD_MCC_RFK_BY_PASS(void *cmd, u32 val) +{ + le32p_replace_bits((__le32 *)cmd + 1, val, BIT(20)); +} + +static inline void RTW89_SET_FWCMD_ADD_MCC_CH_BAND_TYPE(void *cmd, u32 val) +{ + le32p_replace_bits((__le32 *)cmd + 1, val, GENMASK(22, 21)); +} + +static inline void RTW89_SET_FWCMD_ADD_MCC_DURATION(void *cmd, u32 val) +{ + le32p_replace_bits((__le32 *)cmd + 2, val, GENMASK(31, 0)); +} + +static inline void RTW89_SET_FWCMD_ADD_MCC_COURTESY_EN(void *cmd, u32 val) +{ + le32p_replace_bits((__le32 *)cmd + 3, val, BIT(0)); +} + +static inline void RTW89_SET_FWCMD_ADD_MCC_COURTESY_NUM(void *cmd, u32 val) +{ + le32p_replace_bits((__le32 *)cmd + 3, val, GENMASK(15, 8)); +} + +static inline void RTW89_SET_FWCMD_ADD_MCC_COURTESY_TARGET(void *cmd, u32 val) +{ + le32p_replace_bits((__le32 *)cmd + 3, val, GENMASK(23, 16)); +} + +struct rtw89_fw_mcc_start_req { + u32 group: 2; + u32 btc_in_group: 1; + u32 old_group_action: 2; + u32 old_group: 2; + u32 rsvd0: 9; + u32 notify_cnt: 3; + u32 rsvd1: 2; + u32 notify_rxdbg_en: 1; + u32 rsvd2: 2; + u32 macid: 8; + u32 tsf_low; + u32 tsf_high; +}; + +static inline void RTW89_SET_FWCMD_START_MCC_GROUP(void *cmd, u32 val) +{ + le32p_replace_bits((__le32 *)cmd, val, GENMASK(1, 0)); +} + +static inline void RTW89_SET_FWCMD_START_MCC_BTC_IN_GROUP(void *cmd, u32 val) +{ + le32p_replace_bits((__le32 *)cmd, val, BIT(2)); +} + +static inline void RTW89_SET_FWCMD_START_MCC_OLD_GROUP_ACTION(void *cmd, u32 val) +{ + le32p_replace_bits((__le32 *)cmd, val, GENMASK(4, 3)); +} + +static inline void RTW89_SET_FWCMD_START_MCC_OLD_GROUP(void *cmd, u32 val) +{ + le32p_replace_bits((__le32 *)cmd, val, GENMASK(6, 5)); +} + +static inline void RTW89_SET_FWCMD_START_MCC_NOTIFY_CNT(void *cmd, u32 val) +{ + le32p_replace_bits((__le32 *)cmd, val, GENMASK(18, 16)); +} + +static inline void RTW89_SET_FWCMD_START_MCC_NOTIFY_RXDBG_EN(void *cmd, u32 val) +{ + le32p_replace_bits((__le32 *)cmd, val, BIT(21)); +} + +static inline void RTW89_SET_FWCMD_START_MCC_MACID(void *cmd, u32 val) +{ + le32p_replace_bits((__le32 *)cmd, val, GENMASK(31, 24)); +} + +static inline void RTW89_SET_FWCMD_START_MCC_TSF_LOW(void *cmd, u32 val) +{ + le32p_replace_bits((__le32 *)cmd + 1, val, GENMASK(31, 0)); +} + +static inline void RTW89_SET_FWCMD_START_MCC_TSF_HIGH(void *cmd, u32 val) +{ + le32p_replace_bits((__le32 *)cmd + 2, val, GENMASK(31, 0)); +} + +static inline void RTW89_SET_FWCMD_STOP_MCC_MACID(void *cmd, u32 val) +{ + le32p_replace_bits((__le32 *)cmd, val, GENMASK(7, 0)); +} + +static inline void RTW89_SET_FWCMD_STOP_MCC_GROUP(void *cmd, u32 val) +{ + le32p_replace_bits((__le32 *)cmd, val, GENMASK(9, 8)); +} + +static inline void RTW89_SET_FWCMD_STOP_MCC_PREV_GROUPS(void *cmd, u32 val) +{ + le32p_replace_bits((__le32 *)cmd, val, BIT(10)); +} + +static inline void RTW89_SET_FWCMD_DEL_MCC_GROUP_GROUP(void *cmd, u32 val) +{ + le32p_replace_bits((__le32 *)cmd, val, GENMASK(1, 0)); +} + +static inline void RTW89_SET_FWCMD_DEL_MCC_GROUP_PREV_GROUPS(void *cmd, u32 val) +{ + le32p_replace_bits((__le32 *)cmd, val, BIT(2)); +} + +static inline void RTW89_SET_FWCMD_RESET_MCC_GROUP_GROUP(void *cmd, u32 val) +{ + le32p_replace_bits((__le32 *)cmd, val, GENMASK(1, 0)); +} + +struct rtw89_fw_mcc_tsf_req { + u8 group: 2; + u8 rsvd0: 6; + u8 macid_x; + u8 macid_y; + u8 rsvd1; +}; + +static inline void RTW89_SET_FWCMD_MCC_REQ_TSF_GROUP(void *cmd, u32 val) +{ + le32p_replace_bits((__le32 *)cmd, val, GENMASK(1, 0)); +} + +static inline void RTW89_SET_FWCMD_MCC_REQ_TSF_MACID_X(void *cmd, u32 val) +{ + le32p_replace_bits((__le32 *)cmd, val, GENMASK(15, 8)); +} + +static inline void RTW89_SET_FWCMD_MCC_REQ_TSF_MACID_Y(void *cmd, u32 val) +{ + le32p_replace_bits((__le32 *)cmd, val, GENMASK(23, 16)); +} + +static inline void RTW89_SET_FWCMD_MCC_MACID_BITMAP_GROUP(void *cmd, u32 val) +{ + le32p_replace_bits((__le32 *)cmd, val, GENMASK(1, 0)); +} + +static inline void RTW89_SET_FWCMD_MCC_MACID_BITMAP_MACID(void *cmd, u32 val) +{ + le32p_replace_bits((__le32 *)cmd, val, GENMASK(15, 8)); +} + +static inline void RTW89_SET_FWCMD_MCC_MACID_BITMAP_BITMAP_LENGTH(void *cmd, u32 val) +{ + le32p_replace_bits((__le32 *)cmd, val, GENMASK(23, 16)); +} + +static inline void RTW89_SET_FWCMD_MCC_MACID_BITMAP_BITMAP(void *cmd, + u8 *bitmap, u8 len) +{ + memcpy((__le32 *)cmd + 1, bitmap, len); +} + +static inline void RTW89_SET_FWCMD_MCC_SYNC_GROUP(void *cmd, u32 val) +{ + le32p_replace_bits((__le32 *)cmd, val, GENMASK(1, 0)); +} + +static inline void RTW89_SET_FWCMD_MCC_SYNC_MACID_SOURCE(void *cmd, u32 val) +{ + le32p_replace_bits((__le32 *)cmd, val, GENMASK(15, 8)); +} + +static inline void RTW89_SET_FWCMD_MCC_SYNC_MACID_TARGET(void *cmd, u32 val) +{ + le32p_replace_bits((__le32 *)cmd, val, GENMASK(23, 16)); +} + +static inline void RTW89_SET_FWCMD_MCC_SYNC_SYNC_OFFSET(void *cmd, u32 val) +{ + le32p_replace_bits((__le32 *)cmd, val, GENMASK(31, 24)); +} + +struct rtw89_fw_mcc_duration { + u32 group: 2; + u32 btc_in_group: 1; + u32 rsvd0: 5; + u32 start_macid: 8; + u32 macid_x: 8; + u32 macid_y: 8; + u32 start_tsf_low; + u32 start_tsf_high; + u32 duration_x; + u32 duration_y; +}; + +static inline void RTW89_SET_FWCMD_MCC_SET_DURATION_GROUP(void *cmd, u32 val) +{ + le32p_replace_bits((__le32 *)cmd, val, GENMASK(1, 0)); +} + +static +inline void RTW89_SET_FWCMD_MCC_SET_DURATION_BTC_IN_GROUP(void *cmd, u32 val) +{ + le32p_replace_bits((__le32 *)cmd, val, BIT(2)); +} + +static +inline void RTW89_SET_FWCMD_MCC_SET_DURATION_START_MACID(void *cmd, u32 val) +{ + le32p_replace_bits((__le32 *)cmd, val, GENMASK(15, 8)); +} + +static inline void RTW89_SET_FWCMD_MCC_SET_DURATION_MACID_X(void *cmd, u32 val) +{ + le32p_replace_bits((__le32 *)cmd, val, GENMASK(23, 16)); +} + +static inline void RTW89_SET_FWCMD_MCC_SET_DURATION_MACID_Y(void *cmd, u32 val) +{ + le32p_replace_bits((__le32 *)cmd, val, GENMASK(31, 24)); +} + +static +inline void RTW89_SET_FWCMD_MCC_SET_DURATION_START_TSF_LOW(void *cmd, u32 val) +{ + le32p_replace_bits((__le32 *)cmd + 1, val, GENMASK(31, 0)); +} + +static +inline void RTW89_SET_FWCMD_MCC_SET_DURATION_START_TSF_HIGH(void *cmd, u32 val) +{ + le32p_replace_bits((__le32 *)cmd + 2, val, GENMASK(31, 0)); +} + +static +inline void RTW89_SET_FWCMD_MCC_SET_DURATION_DURATION_X(void *cmd, u32 val) +{ + le32p_replace_bits((__le32 *)cmd + 3, val, GENMASK(31, 0)); +} + +static +inline void RTW89_SET_FWCMD_MCC_SET_DURATION_DURATION_Y(void *cmd, u32 val) +{ + le32p_replace_bits((__le32 *)cmd + 4, val, GENMASK(31, 0)); +} + #define RTW89_C2H_HEADER_LEN 8 #define RTW89_GET_C2H_CATEGORY(c2h) \ @@ -3185,9 +3534,27 @@ int rtw89_fw_h2c_wow_global(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif, bool enable); int rtw89_fw_h2c_wow_wakeup_ctrl(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif, bool enable); - int rtw89_fw_wow_cam_update(struct rtw89_dev *rtwdev, struct rtw89_wow_cam_info *cam_info); +int rtw89_fw_h2c_add_mcc(struct rtw89_dev *rtwdev, + const struct rtw89_fw_mcc_add_req *p); +int rtw89_fw_h2c_start_mcc(struct rtw89_dev *rtwdev, + const struct rtw89_fw_mcc_start_req *p); +int rtw89_fw_h2c_stop_mcc(struct rtw89_dev *rtwdev, u8 group, u8 macid, + bool prev_groups); +int rtw89_fw_h2c_del_mcc_group(struct rtw89_dev *rtwdev, u8 group, + bool prev_groups); +int rtw89_fw_h2c_reset_mcc_group(struct rtw89_dev *rtwdev, u8 group); +int rtw89_fw_h2c_mcc_req_tsf(struct rtw89_dev *rtwdev, + const struct rtw89_fw_mcc_tsf_req *req, + struct rtw89_mac_mcc_tsf_rpt *rpt); +int rtw89_fw_h2c_mcc_macid_bitamp(struct rtw89_dev *rtwdev, u8 group, u8 macid, + u8 *bitmap); +int rtw89_fw_h2c_mcc_sync(struct rtw89_dev *rtwdev, u8 group, u8 source, + u8 target, u8 offset); +int rtw89_fw_h2c_mcc_set_duration(struct rtw89_dev *rtwdev, + const struct rtw89_fw_mcc_duration *p); + static inline void rtw89_fw_h2c_init_ba_cam(struct rtw89_dev *rtwdev) { const struct rtw89_chip_info *chip = rtwdev->chip; From 75ee07b03fc6ec0a7ac7407f9ea7b3e981efb28f Mon Sep 17 00:00:00 2001 From: Zong-Zhe Yang Date: Tue, 29 Nov 2022 16:31:30 +0800 Subject: [PATCH 1381/2002] wifi: rtw89: link rtw89_vif and chanctx stuffs First, introduce struct rtw89_sub_entity for chanctx related stuffs. Second, add enum rtw89_sub_entity_idx to rtw89_vif for vif operation to access its/right chanctx stuffs after future multi-channel support. Besides, RTW89_SUB_ENTITY_0 is the default chanctx entry throughout driver, i.e. it's used for things which may not have a target chanctx yet. So, we need to ensure that RTW89_SUB_ENTITY_0 is always working. If there is at least one alive chanctx, then one of them must take RTW89_SUB_ENTITY_0. If no alive chanctx, RTW89_SUB_ENTITY_0 will be filled by rtw89_config_default_chandef(). Signed-off-by: Zong-Zhe Yang Signed-off-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20221129083130.45708-7-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/chan.c | 40 +++++++++++++++++-- drivers/net/wireless/realtek/rtw89/core.h | 20 ++++++---- drivers/net/wireless/realtek/rtw89/mac80211.c | 1 + 3 files changed, 50 insertions(+), 11 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/chan.c b/drivers/net/wireless/realtek/rtw89/chan.c index a4f61c2f6512..90596806bc93 100644 --- a/drivers/net/wireless/realtek/rtw89/chan.c +++ b/drivers/net/wireless/realtek/rtw89/chan.c @@ -4,6 +4,7 @@ #include "chan.h" #include "debug.h" +#include "util.h" static enum rtw89_subband rtw89_get_subband_type(enum rtw89_band band, u8 center_chan) @@ -108,8 +109,8 @@ bool rtw89_assign_entity_chan(struct rtw89_dev *rtwdev, const struct rtw89_chan *new) { struct rtw89_hal *hal = &rtwdev->hal; - struct rtw89_chan *chan = &hal->chan[idx]; - struct rtw89_chan_rcd *rcd = &hal->chan_rcd[idx]; + struct rtw89_chan *chan = &hal->sub[idx].chan; + struct rtw89_chan_rcd *rcd = &hal->sub[idx].rcd; bool band_changed; rcd->prev_primary_channel = chan->primary_channel; @@ -127,7 +128,7 @@ static void __rtw89_config_entity_chandef(struct rtw89_dev *rtwdev, { struct rtw89_hal *hal = &rtwdev->hal; - hal->chandef[idx] = *chandef; + hal->sub[idx].chandef = *chandef; if (from_stack) set_bit(idx, hal->entity_map); @@ -195,6 +196,7 @@ int rtw89_chanctx_ops_add(struct rtw89_dev *rtwdev, rtw89_config_entity_chandef(rtwdev, idx, &ctx->def); rtw89_set_channel(rtwdev); cfg->idx = idx; + hal->sub[idx].cfg = cfg; return 0; } @@ -203,8 +205,34 @@ void rtw89_chanctx_ops_remove(struct rtw89_dev *rtwdev, { struct rtw89_hal *hal = &rtwdev->hal; struct rtw89_chanctx_cfg *cfg = (struct rtw89_chanctx_cfg *)ctx->drv_priv; + struct rtw89_vif *rtwvif; + u8 drop, roll; - clear_bit(cfg->idx, hal->entity_map); + drop = cfg->idx; + if (drop != RTW89_SUB_ENTITY_0) + goto out; + + roll = find_next_bit(hal->entity_map, NUM_OF_RTW89_SUB_ENTITY, drop + 1); + + /* Follow rtw89_config_default_chandef() when rtw89_entity_recalc(). */ + if (roll == NUM_OF_RTW89_SUB_ENTITY) + goto out; + + /* RTW89_SUB_ENTITY_0 is going to release, and another exists. + * Make another roll down to RTW89_SUB_ENTITY_0 to replace. + */ + hal->sub[roll].cfg->idx = RTW89_SUB_ENTITY_0; + hal->sub[RTW89_SUB_ENTITY_0] = hal->sub[roll]; + + rtw89_for_each_rtwvif(rtwdev, rtwvif) { + if (rtwvif->sub_entity_idx == roll) + rtwvif->sub_entity_idx = RTW89_SUB_ENTITY_0; + } + + drop = roll; + +out: + clear_bit(drop, hal->entity_map); rtw89_set_channel(rtwdev); } @@ -225,6 +253,9 @@ int rtw89_chanctx_ops_assign_vif(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif, struct ieee80211_chanctx_conf *ctx) { + struct rtw89_chanctx_cfg *cfg = (struct rtw89_chanctx_cfg *)ctx->drv_priv; + + rtwvif->sub_entity_idx = cfg->idx; return 0; } @@ -232,4 +263,5 @@ void rtw89_chanctx_ops_unassign_vif(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif, struct ieee80211_chanctx_conf *ctx) { + rtwvif->sub_entity_idx = RTW89_SUB_ENTITY_0; } diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index f4603c2ce8f1..2badb96d2ae3 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -2240,6 +2240,8 @@ struct rtw89_phy_rate_pattern { struct rtw89_vif { struct list_head list; struct rtw89_dev *rtwdev; + enum rtw89_sub_entity_idx sub_entity_idx; + u8 mac_id; u8 port; u8 mac_addr[ETH_ALEN]; @@ -2953,6 +2955,13 @@ enum rtw89_entity_mode { RTW89_ENTITY_MODE_SCC, }; +struct rtw89_sub_entity { + struct cfg80211_chan_def chandef; + struct rtw89_chan chan; + struct rtw89_chan_rcd rcd; + struct rtw89_chanctx_cfg *cfg; +}; + struct rtw89_hal { u32 rx_fltr; u8 cv; @@ -2966,13 +2975,10 @@ struct rtw89_hal { bool support_igi; DECLARE_BITMAP(entity_map, NUM_OF_RTW89_SUB_ENTITY); - struct cfg80211_chan_def chandef[NUM_OF_RTW89_SUB_ENTITY]; + struct rtw89_sub_entity sub[NUM_OF_RTW89_SUB_ENTITY]; bool entity_active; enum rtw89_entity_mode entity_mode; - - struct rtw89_chan chan[NUM_OF_RTW89_SUB_ENTITY]; - struct rtw89_chan_rcd chan_rcd[NUM_OF_RTW89_SUB_ENTITY]; }; #define RTW89_MAX_MAC_ID_NUM 128 @@ -4138,7 +4144,7 @@ const struct cfg80211_chan_def *rtw89_chandef_get(struct rtw89_dev *rtwdev, { struct rtw89_hal *hal = &rtwdev->hal; - return &hal->chandef[idx]; + return &hal->sub[idx].chandef; } static inline @@ -4147,7 +4153,7 @@ const struct rtw89_chan *rtw89_chan_get(struct rtw89_dev *rtwdev, { struct rtw89_hal *hal = &rtwdev->hal; - return &hal->chan[idx]; + return &hal->sub[idx].chan; } static inline @@ -4156,7 +4162,7 @@ const struct rtw89_chan_rcd *rtw89_chan_rcd_get(struct rtw89_dev *rtwdev, { struct rtw89_hal *hal = &rtwdev->hal; - return &hal->chan_rcd[idx]; + return &hal->sub[idx].rcd; } static inline void rtw89_chip_fem_setup(struct rtw89_dev *rtwdev) diff --git a/drivers/net/wireless/realtek/rtw89/mac80211.c b/drivers/net/wireless/realtek/rtw89/mac80211.c index ce980d2f22c4..1a99267d710d 100644 --- a/drivers/net/wireless/realtek/rtw89/mac80211.c +++ b/drivers/net/wireless/realtek/rtw89/mac80211.c @@ -131,6 +131,7 @@ static int rtw89_ops_add_interface(struct ieee80211_hw *hw, rtwvif->bcn_hit_cond = 0; rtwvif->mac_idx = RTW89_MAC_0; rtwvif->phy_idx = RTW89_PHY_0; + rtwvif->sub_entity_idx = RTW89_SUB_ENTITY_0; rtwvif->hit_rule = 0; ether_addr_copy(rtwvif->mac_addr, vif->addr); From 682f560b8a87bfdf174e836640e6ec66a2b3cd9c Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Tue, 29 Nov 2022 12:43:47 +0300 Subject: [PATCH 1382/2002] net: microchip: sparx5: Fix error handling in vcap_show_admin() If vcap_dup_rule() fails that leads to an error pointer dereference side the call to vcap_free_rule(). Also it only returns an error if the very last call to vcap_read_rule() fails and it returns success for other errors. I've changed it to just stop printing after the first error and return an error code. Fixes: 3a7921560d2f ("net: microchip: sparx5: Add VCAP rule debugFS support for the VCAP API") Signed-off-by: Dan Carpenter Reviewed-by: Steen Hegelund Link: https://lore.kernel.org/r/Y4XUUx9kzurBN+BV@kili Signed-off-by: Paolo Abeni --- .../ethernet/microchip/vcap/vcap_api_debugfs.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/drivers/net/ethernet/microchip/vcap/vcap_api_debugfs.c b/drivers/net/ethernet/microchip/vcap/vcap_api_debugfs.c index 5df00e940333..3b8d165dc832 100644 --- a/drivers/net/ethernet/microchip/vcap/vcap_api_debugfs.c +++ b/drivers/net/ethernet/microchip/vcap/vcap_api_debugfs.c @@ -639,17 +639,24 @@ static int vcap_show_admin(struct vcap_control *vctrl, mutex_lock(&admin->lock); list_for_each_entry(elem, &admin->rules, list) { ri = vcap_dup_rule(elem); - if (IS_ERR(ri)) - goto free_rule; + if (IS_ERR(ri)) { + ret = PTR_ERR(ri); + goto err_unlock; + } /* Read data from VCAP */ ret = vcap_read_rule(ri); if (ret) - goto free_rule; + goto err_free_rule; out->prf(out->dst, "\n"); vcap_show_admin_rule(vctrl, admin, out, ri); -free_rule: vcap_free_rule((struct vcap_rule *)ri); } + mutex_unlock(&admin->lock); + return 0; + +err_free_rule: + vcap_free_rule((struct vcap_rule *)ri); +err_unlock: mutex_unlock(&admin->lock); return ret; } From 91c71bf14da49edaee5831ed3abd5837402e7a81 Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Tue, 29 Nov 2022 16:12:10 +0200 Subject: [PATCH 1383/2002] net: dpaa2-eth: don't use -ENOTSUPP error code dpaa2_eth_setup_dpni() is called from the probe path and dpaa2_eth_set_link_ksettings() is propagated to user space. include/linux/errno.h says that ENOTSUPP is "Defined for the NFSv3 protocol". Conventional wisdom has it to not use it in networking drivers. Replace it with -EOPNOTSUPP. Signed-off-by: Vladimir Oltean Reviewed-by: Andrew Lunn Reviewed-by: Ioana Ciornei Tested-by: Ioana Ciornei Signed-off-by: Paolo Abeni --- drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c | 2 +- drivers/net/ethernet/freescale/dpaa2/dpaa2-ethtool.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c index 97e1856641b4..515fcd18ed72 100644 --- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c +++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c @@ -3791,7 +3791,7 @@ static int dpaa2_eth_setup_dpni(struct fsl_mc_device *ls_dev) dev_err(dev, "DPNI version %u.%u not supported, need >= %u.%u\n", priv->dpni_ver_major, priv->dpni_ver_minor, DPNI_VER_MAJOR, DPNI_VER_MINOR); - err = -ENOTSUPP; + err = -EOPNOTSUPP; goto close; } diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-ethtool.c b/drivers/net/ethernet/freescale/dpaa2/dpaa2-ethtool.c index 32a38a03db57..ac3a7f2897be 100644 --- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-ethtool.c +++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-ethtool.c @@ -117,7 +117,7 @@ dpaa2_eth_set_link_ksettings(struct net_device *net_dev, struct dpaa2_eth_priv *priv = netdev_priv(net_dev); if (!dpaa2_eth_is_type_phy(priv)) - return -ENOTSUPP; + return -EOPNOTSUPP; return phylink_ethtool_ksettings_set(priv->mac->phylink, link_settings); } From 320fefa9e2edc67011e235ea1d50f0d00ddfe004 Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Tue, 29 Nov 2022 16:12:11 +0200 Subject: [PATCH 1384/2002] net: dpaa2: replace dpaa2_mac_is_type_fixed() with dpaa2_mac_is_type_phy() dpaa2_mac_is_type_fixed() is a header with no implementation and no callers, which is referenced from the documentation though. It can be deleted. On the other hand, it would be useful to reuse the code between dpaa2_eth_is_type_phy() and dpaa2_switch_port_is_type_phy(). That common code should be called dpaa2_mac_is_type_phy(), so let's create that. The removal and the addition are merged into the same patch because, in fact, is_type_phy() is the logical opposite of is_type_fixed(). Signed-off-by: Vladimir Oltean Reviewed-by: Andrew Lunn Reviewed-by: Ioana Ciornei Tested-by: Ioana Ciornei Signed-off-by: Paolo Abeni --- .../ethernet/freescale/dpaa2/mac-phy-support.rst | 9 ++++++--- drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.h | 7 +------ drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.h | 10 ++++++++-- drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.h | 7 +------ 4 files changed, 16 insertions(+), 17 deletions(-) diff --git a/Documentation/networking/device_drivers/ethernet/freescale/dpaa2/mac-phy-support.rst b/Documentation/networking/device_drivers/ethernet/freescale/dpaa2/mac-phy-support.rst index 51e6624fb774..1d2f55feca24 100644 --- a/Documentation/networking/device_drivers/ethernet/freescale/dpaa2/mac-phy-support.rst +++ b/Documentation/networking/device_drivers/ethernet/freescale/dpaa2/mac-phy-support.rst @@ -181,10 +181,13 @@ when necessary using the below listed API:: - int dpaa2_mac_connect(struct dpaa2_mac *mac); - void dpaa2_mac_disconnect(struct dpaa2_mac *mac); -A phylink integration is necessary only when the partner DPMAC is not of TYPE_FIXED. -One can check for this condition using the below API:: +A phylink integration is necessary only when the partner DPMAC is not of +``TYPE_FIXED``. This means it is either of ``TYPE_PHY``, or of +``TYPE_BACKPLANE`` (the difference being the two that in the ``TYPE_BACKPLANE`` +mode, the MC firmware does not access the PCS registers). One can check for +this condition using the following helper:: - - bool dpaa2_mac_is_type_fixed(struct fsl_mc_device *dpmac_dev,struct fsl_mc_io *mc_io); + - static inline bool dpaa2_mac_is_type_phy(struct dpaa2_mac *mac); Before connection to a MAC, the caller must allocate and populate the dpaa2_mac structure with the associated net_device, a pointer to the MC portal diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.h b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.h index 5d0fc432e5b2..04270ae44d84 100644 --- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.h +++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.h @@ -768,12 +768,7 @@ static inline unsigned int dpaa2_eth_rx_head_room(struct dpaa2_eth_priv *priv) static inline bool dpaa2_eth_is_type_phy(struct dpaa2_eth_priv *priv) { - if (priv->mac && - (priv->mac->attr.link_type == DPMAC_LINK_TYPE_PHY || - priv->mac->attr.link_type == DPMAC_LINK_TYPE_BACKPLANE)) - return true; - - return false; + return dpaa2_mac_is_type_phy(priv->mac); } static inline bool dpaa2_eth_has_mac(struct dpaa2_eth_priv *priv) diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.h b/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.h index a58cab188a99..c1ec9efd413a 100644 --- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.h +++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.h @@ -30,8 +30,14 @@ struct dpaa2_mac { struct phy *serdes_phy; }; -bool dpaa2_mac_is_type_fixed(struct fsl_mc_device *dpmac_dev, - struct fsl_mc_io *mc_io); +static inline bool dpaa2_mac_is_type_phy(struct dpaa2_mac *mac) +{ + if (!mac) + return false; + + return mac->attr.link_type == DPMAC_LINK_TYPE_PHY || + mac->attr.link_type == DPMAC_LINK_TYPE_BACKPLANE; +} int dpaa2_mac_open(struct dpaa2_mac *mac); diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.h b/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.h index 0002dca4d417..9898073abe01 100644 --- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.h +++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.h @@ -230,12 +230,7 @@ static inline bool dpaa2_switch_supports_cpu_traffic(struct ethsw_core *ethsw) static inline bool dpaa2_switch_port_is_type_phy(struct ethsw_port_priv *port_priv) { - if (port_priv->mac && - (port_priv->mac->attr.link_type == DPMAC_LINK_TYPE_PHY || - port_priv->mac->attr.link_type == DPMAC_LINK_TYPE_BACKPLANE)) - return true; - - return false; + return dpaa2_mac_is_type_phy(port_priv->mac); } static inline bool dpaa2_switch_port_has_mac(struct ethsw_port_priv *port_priv) From 3853338881541093ce708beb6f26ce74b2a4fe0e Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Tue, 29 Nov 2022 16:12:12 +0200 Subject: [PATCH 1385/2002] net: dpaa2-mac: absorb phylink_start() call into dpaa2_mac_start() The phylink handling is intended to be hidden inside the dpaa2_mac object. Move the phylink_start() call into dpaa2_mac_start(), and phylink_stop() into dpaa2_mac_stop(). Signed-off-by: Vladimir Oltean Reviewed-by: Andrew Lunn Reviewed-by: Ioana Ciornei Tested-by: Ioana Ciornei Signed-off-by: Paolo Abeni --- drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c | 5 +---- drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c | 8 ++++++++ drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c | 5 +---- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c index 515fcd18ed72..8896a3198bd2 100644 --- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c +++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c @@ -2201,10 +2201,8 @@ static int dpaa2_eth_open(struct net_device *net_dev) goto enable_err; } - if (dpaa2_eth_is_type_phy(priv)) { + if (dpaa2_eth_is_type_phy(priv)) dpaa2_mac_start(priv->mac); - phylink_start(priv->mac->phylink); - } return 0; @@ -2278,7 +2276,6 @@ static int dpaa2_eth_stop(struct net_device *net_dev) int retries = 10; if (dpaa2_eth_is_type_phy(priv)) { - phylink_stop(priv->mac->phylink); dpaa2_mac_stop(priv->mac); } else { netif_tx_stop_all_queues(net_dev); diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c b/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c index 51c9da8e1be2..38e88c354885 100644 --- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c +++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c @@ -338,12 +338,20 @@ static void dpaa2_mac_set_supported_interfaces(struct dpaa2_mac *mac) void dpaa2_mac_start(struct dpaa2_mac *mac) { + ASSERT_RTNL(); + if (mac->serdes_phy) phy_power_on(mac->serdes_phy); + + phylink_start(mac->phylink); } void dpaa2_mac_stop(struct dpaa2_mac *mac) { + ASSERT_RTNL(); + + phylink_stop(mac->phylink); + if (mac->serdes_phy) phy_power_off(mac->serdes_phy); } diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c b/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c index 14f739e04a3c..42d3290ccd8b 100644 --- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c +++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c @@ -702,10 +702,8 @@ static int dpaa2_switch_port_open(struct net_device *netdev) dpaa2_switch_enable_ctrl_if_napi(ethsw); - if (dpaa2_switch_port_is_type_phy(port_priv)) { + if (dpaa2_switch_port_is_type_phy(port_priv)) dpaa2_mac_start(port_priv->mac); - phylink_start(port_priv->mac->phylink); - } return 0; } @@ -717,7 +715,6 @@ static int dpaa2_switch_port_stop(struct net_device *netdev) int err; if (dpaa2_switch_port_is_type_phy(port_priv)) { - phylink_stop(port_priv->mac->phylink); dpaa2_mac_stop(port_priv->mac); } else { netif_tx_stop_all_queues(netdev); From ccbd7822950fe9c7212d85a140ea18174f9b2c0f Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Tue, 29 Nov 2022 16:12:13 +0200 Subject: [PATCH 1386/2002] net: dpaa2-mac: remove defensive check in dpaa2_mac_disconnect() dpaa2_mac_disconnect() will only be called with a NULL mac->phylink if dpaa2_mac_connect() failed, or was never called. The callers are these: dpaa2_eth_disconnect_mac(): if (dpaa2_eth_is_type_phy(priv)) dpaa2_mac_disconnect(priv->mac); dpaa2_switch_port_disconnect_mac(): if (dpaa2_switch_port_is_type_phy(port_priv)) dpaa2_mac_disconnect(port_priv->mac); priv->mac can be NULL, but in that case, dpaa2_eth_is_type_phy() returns false, and dpaa2_mac_disconnect() is never called. Similar for dpaa2-switch. When priv->mac is non-NULL, it means that dpaa2_mac_connect() returned zero (success), and therefore, priv->mac->phylink is also a valid pointer. Signed-off-by: Vladimir Oltean Reviewed-by: Andrew Lunn Reviewed-by: Ioana Ciornei Tested-by: Ioana Ciornei Signed-off-by: Paolo Abeni --- drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c b/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c index 38e88c354885..3096810120ee 100644 --- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c +++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c @@ -448,9 +448,6 @@ err_pcs_destroy: void dpaa2_mac_disconnect(struct dpaa2_mac *mac) { - if (!mac->phylink) - return; - phylink_disconnect_phy(mac->phylink); phylink_destroy(mac->phylink); dpaa2_pcs_destroy(mac); From 02d61948e8daf3844d0af41ba5d563ef03cc7c4f Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Tue, 29 Nov 2022 16:12:14 +0200 Subject: [PATCH 1387/2002] net: dpaa2-eth: assign priv->mac after dpaa2_mac_connect() call There are 2 requirements for correct code: - Any time the driver accesses the priv->mac pointer at runtime, it either holds NULL to indicate a DPNI-DPNI connection (or unconnected DPNI), or a struct dpaa2_mac whose phylink instance was fully initialized (created and connected to the PHY). No changes are made to priv->mac while it is being used. Currently, rtnl_lock() watches over the call to dpaa2_eth_connect_mac(), so it serves the purpose of serializing this with all readers of priv->mac. - dpaa2_mac_connect() should run unlocked, because inside it are 2 phylink calls with incompatible locking requirements: phylink_create() requires that the rtnl_mutex isn't held, and phylink_fwnode_phy_connect() requires that the rtnl_mutex is held. The only way to solve those contradictory requirements is to let dpaa2_mac_connect() take rtnl_lock() when it needs to. To solve both requirements, we need to identify the writer side of the priv->mac pointer, which can be wrapped in a mutex private to the driver in a future patch. The dpaa2_mac_connect() cannot be part of the writer side critical section, because of an AB/BA deadlock with rtnl_lock(). So the strategy needs to be that where we prepare the DPMAC by calling dpaa2_mac_connect(), and only make priv->mac point to it once it's fully prepared. This ensures that the writer side critical section has the absolute minimum surface it can. The reverse strategy is adopted in the dpaa2_eth_disconnect_mac() code path. This makes sure that priv->mac is NULL when we start tearing down the DPMAC that we disconnected from, and concurrent code will simply not see it. No locking changes in this patch (concurrent code is still blocked by the rtnl_mutex). Signed-off-by: Vladimir Oltean Reviewed-by: Ioana Ciornei Tested-by: Ioana Ciornei Signed-off-by: Paolo Abeni --- .../net/ethernet/freescale/dpaa2/dpaa2-eth.c | 21 +++++++++++-------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c index 8896a3198bd2..4dbf8a1651cd 100644 --- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c +++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c @@ -4624,9 +4624,8 @@ static int dpaa2_eth_connect_mac(struct dpaa2_eth_priv *priv) err = dpaa2_mac_open(mac); if (err) goto err_free_mac; - priv->mac = mac; - if (dpaa2_eth_is_type_phy(priv)) { + if (dpaa2_mac_is_type_phy(mac)) { err = dpaa2_mac_connect(mac); if (err) { if (err == -EPROBE_DEFER) @@ -4640,11 +4639,12 @@ static int dpaa2_eth_connect_mac(struct dpaa2_eth_priv *priv) } } + priv->mac = mac; + return 0; err_close_mac: dpaa2_mac_close(mac); - priv->mac = NULL; err_free_mac: kfree(mac); return err; @@ -4652,15 +4652,18 @@ err_free_mac: static void dpaa2_eth_disconnect_mac(struct dpaa2_eth_priv *priv) { - if (dpaa2_eth_is_type_phy(priv)) - dpaa2_mac_disconnect(priv->mac); + struct dpaa2_mac *mac = priv->mac; - if (!dpaa2_eth_has_mac(priv)) + priv->mac = NULL; + + if (!mac) return; - dpaa2_mac_close(priv->mac); - kfree(priv->mac); - priv->mac = NULL; + if (dpaa2_mac_is_type_phy(mac)) + dpaa2_mac_disconnect(mac); + + dpaa2_mac_close(mac); + kfree(mac); } static irqreturn_t dpni_irq0_handler_thread(int irq_num, void *arg) From 88d64367cea019fa6197d0d97a85ac90279919b7 Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Tue, 29 Nov 2022 16:12:15 +0200 Subject: [PATCH 1388/2002] net: dpaa2-switch: assign port_priv->mac after dpaa2_mac_connect() call The dpaa2-switch has the exact same locking requirements when connected to a DPMAC, so it needs port_priv->mac to always point either to NULL, or to a DPMAC with a fully initialized phylink instance. Make the same preparatory change in the dpaa2-switch driver as in the dpaa2-eth one. Signed-off-by: Vladimir Oltean Reviewed-by: Ioana Ciornei Tested-by: Ioana Ciornei Signed-off-by: Paolo Abeni --- .../ethernet/freescale/dpaa2/dpaa2-switch.c | 21 +++++++++++-------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c b/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c index 42d3290ccd8b..3b0963d95f67 100644 --- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c +++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c @@ -1449,9 +1449,8 @@ static int dpaa2_switch_port_connect_mac(struct ethsw_port_priv *port_priv) err = dpaa2_mac_open(mac); if (err) goto err_free_mac; - port_priv->mac = mac; - if (dpaa2_switch_port_is_type_phy(port_priv)) { + if (dpaa2_mac_is_type_phy(mac)) { err = dpaa2_mac_connect(mac); if (err) { netdev_err(port_priv->netdev, @@ -1461,11 +1460,12 @@ static int dpaa2_switch_port_connect_mac(struct ethsw_port_priv *port_priv) } } + port_priv->mac = mac; + return 0; err_close_mac: dpaa2_mac_close(mac); - port_priv->mac = NULL; err_free_mac: kfree(mac); return err; @@ -1473,15 +1473,18 @@ err_free_mac: static void dpaa2_switch_port_disconnect_mac(struct ethsw_port_priv *port_priv) { - if (dpaa2_switch_port_is_type_phy(port_priv)) - dpaa2_mac_disconnect(port_priv->mac); + struct dpaa2_mac *mac = port_priv->mac; - if (!dpaa2_switch_port_has_mac(port_priv)) + port_priv->mac = NULL; + + if (!mac) return; - dpaa2_mac_close(port_priv->mac); - kfree(port_priv->mac); - port_priv->mac = NULL; + if (dpaa2_mac_is_type_phy(mac)) + dpaa2_mac_disconnect(mac); + + dpaa2_mac_close(mac); + kfree(mac); } static irqreturn_t dpaa2_switch_irq0_handler_thread(int irq_num, void *arg) From 29811d6e19d795efcf26644b66c4152abbac35a6 Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Tue, 29 Nov 2022 16:12:16 +0200 Subject: [PATCH 1389/2002] net: dpaa2: publish MAC stringset to ethtool -S even if MAC is missing DPNIs and DPSW objects can connect and disconnect at runtime from DPMAC objects on the same fsl-mc bus. The DPMAC object also holds "ethtool -S" unstructured counters. Those counters are only shown for the entity owning the netdev (DPNI, DPSW) if it's connected to a DPMAC. The ethtool stringset code path is split into multiple callbacks, but currently, connecting and disconnecting the DPMAC takes the rtnl_lock(). This blocks the entire ethtool code path from running, see ethnl_default_doit() -> rtnl_lock() -> ops->prepare_data() -> strset_prepare_data(). This is going to be a problem if we are going to no longer require rtnl_lock() when connecting/disconnecting the DPMAC, because the DPMAC could appear between ops->get_sset_count() and ops->get_strings(). If it appears out of the blue, we will provide a stringset into an array that was dimensioned thinking the DPMAC wouldn't be there => array accessed out of bounds. There isn't really a good way to work around that, and I don't want to put too much pressure on the ethtool framework by playing locking games. Just make the DPMAC counters be always available. They'll be zeroes if the DPNI or DPSW isn't connected to a DPMAC. Signed-off-by: Vladimir Oltean Reviewed-by: Andrew Lunn Reviewed-by: Ioana Ciornei Tested-by: Ioana Ciornei Signed-off-by: Paolo Abeni --- drivers/net/ethernet/freescale/dpaa2/dpaa2-ethtool.c | 12 +++--------- .../ethernet/freescale/dpaa2/dpaa2-switch-ethtool.c | 11 ++--------- 2 files changed, 5 insertions(+), 18 deletions(-) diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-ethtool.c b/drivers/net/ethernet/freescale/dpaa2/dpaa2-ethtool.c index ac3a7f2897be..bd87aa9ef686 100644 --- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-ethtool.c +++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-ethtool.c @@ -185,7 +185,6 @@ static int dpaa2_eth_set_pauseparam(struct net_device *net_dev, static void dpaa2_eth_get_strings(struct net_device *netdev, u32 stringset, u8 *data) { - struct dpaa2_eth_priv *priv = netdev_priv(netdev); u8 *p = data; int i; @@ -199,22 +198,17 @@ static void dpaa2_eth_get_strings(struct net_device *netdev, u32 stringset, strscpy(p, dpaa2_ethtool_extras[i], ETH_GSTRING_LEN); p += ETH_GSTRING_LEN; } - if (dpaa2_eth_has_mac(priv)) - dpaa2_mac_get_strings(p); + dpaa2_mac_get_strings(p); break; } } static int dpaa2_eth_get_sset_count(struct net_device *net_dev, int sset) { - int num_ss_stats = DPAA2_ETH_NUM_STATS + DPAA2_ETH_NUM_EXTRA_STATS; - struct dpaa2_eth_priv *priv = netdev_priv(net_dev); - switch (sset) { case ETH_SS_STATS: /* ethtool_get_stats(), ethtool_get_drvinfo() */ - if (dpaa2_eth_has_mac(priv)) - num_ss_stats += dpaa2_mac_get_sset_count(); - return num_ss_stats; + return DPAA2_ETH_NUM_STATS + DPAA2_ETH_NUM_EXTRA_STATS + + dpaa2_mac_get_sset_count(); default: return -EOPNOTSUPP; } diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch-ethtool.c b/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch-ethtool.c index 720c9230cab5..40ee57ef55be 100644 --- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch-ethtool.c +++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch-ethtool.c @@ -145,14 +145,9 @@ dpaa2_switch_set_link_ksettings(struct net_device *netdev, static int dpaa2_switch_ethtool_get_sset_count(struct net_device *netdev, int sset) { - struct ethsw_port_priv *port_priv = netdev_priv(netdev); - int num_ss_stats = DPAA2_SWITCH_NUM_COUNTERS; - switch (sset) { case ETH_SS_STATS: - if (port_priv->mac) - num_ss_stats += dpaa2_mac_get_sset_count(); - return num_ss_stats; + return DPAA2_SWITCH_NUM_COUNTERS + dpaa2_mac_get_sset_count(); default: return -EOPNOTSUPP; } @@ -161,7 +156,6 @@ dpaa2_switch_ethtool_get_sset_count(struct net_device *netdev, int sset) static void dpaa2_switch_ethtool_get_strings(struct net_device *netdev, u32 stringset, u8 *data) { - struct ethsw_port_priv *port_priv = netdev_priv(netdev); u8 *p = data; int i; @@ -172,8 +166,7 @@ static void dpaa2_switch_ethtool_get_strings(struct net_device *netdev, ETH_GSTRING_LEN); p += ETH_GSTRING_LEN; } - if (port_priv->mac) - dpaa2_mac_get_strings(p); + dpaa2_mac_get_strings(p); break; } } From bc230671bfb25c2d3c225f674fe6c03cea88d22e Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Tue, 29 Nov 2022 16:12:17 +0200 Subject: [PATCH 1390/2002] net: dpaa2-switch replace direct MAC access with dpaa2_switch_port_has_mac() The helper function will gain a lockdep annotation in a future patch. Make sure to benefit from it. Signed-off-by: Vladimir Oltean Reviewed-by: Ioana Ciornei Tested-by: Ioana Ciornei Signed-off-by: Paolo Abeni --- drivers/net/ethernet/freescale/dpaa2/dpaa2-switch-ethtool.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch-ethtool.c b/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch-ethtool.c index 40ee57ef55be..76a4b09e2854 100644 --- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch-ethtool.c +++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch-ethtool.c @@ -189,7 +189,7 @@ static void dpaa2_switch_ethtool_get_stats(struct net_device *netdev, dpaa2_switch_ethtool_counters[i].name, err); } - if (port_priv->mac) + if (dpaa2_switch_port_has_mac(port_priv)) dpaa2_mac_get_ethtool_stats(port_priv->mac, data + i); } From 55f90a4d07ec32279c622a25be4e2555ee9d7dff Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Tue, 29 Nov 2022 16:12:18 +0200 Subject: [PATCH 1391/2002] net: dpaa2-eth: connect to MAC before requesting the "endpoint changed" IRQ dpaa2_eth_connect_mac() is called both from dpaa2_eth_probe() and from dpni_irq0_handler_thread(). It could happen that the DPNI gets connected to a DPMAC on the fsl-mc bus exactly during probe, as soon as the "endpoint change" interrupt is requested in dpaa2_eth_setup_irqs(). This will cause the dpni_irq0_handler_thread() to register a phylink instance for that DPMAC. Then, the probing function will also try to register a phylink instance for the same DPMAC, operation which should fail (and this will fail the probing of the driver). Reorder dpaa2_eth_setup_irqs() and dpaa2_eth_connect_mac(), such that dpni_irq0_handler_thread() never races with the DPMAC-related portion of the probing path. Also reorder dpaa2_eth_disconnect_mac() to be in the mirror position of dpaa2_eth_connect_mac() in the teardown path. Signed-off-by: Vladimir Oltean Reviewed-by: Ioana Ciornei Tested-by: Ioana Ciornei Signed-off-by: Paolo Abeni --- .../net/ethernet/freescale/dpaa2/dpaa2-eth.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c index 4dbf8a1651cd..b77d292cd960 100644 --- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c +++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c @@ -4899,6 +4899,10 @@ static int dpaa2_eth_probe(struct fsl_mc_device *dpni_dev) } #endif + err = dpaa2_eth_connect_mac(priv); + if (err) + goto err_connect_mac; + err = dpaa2_eth_setup_irqs(dpni_dev); if (err) { netdev_warn(net_dev, "Failed to set link interrupt, fall back to polling\n"); @@ -4911,10 +4915,6 @@ static int dpaa2_eth_probe(struct fsl_mc_device *dpni_dev) priv->do_link_poll = true; } - err = dpaa2_eth_connect_mac(priv); - if (err) - goto err_connect_mac; - err = dpaa2_eth_dl_alloc(priv); if (err) goto err_dl_register; @@ -4948,13 +4948,13 @@ err_dl_port_add: err_dl_trap_register: dpaa2_eth_dl_free(priv); err_dl_register: - dpaa2_eth_disconnect_mac(priv); -err_connect_mac: if (priv->do_link_poll) kthread_stop(priv->poll_thread); else fsl_mc_free_irqs(dpni_dev); err_poll_thread: + dpaa2_eth_disconnect_mac(priv); +err_connect_mac: dpaa2_eth_free_rings(priv); err_alloc_rings: err_csum: @@ -5002,9 +5002,6 @@ static int dpaa2_eth_remove(struct fsl_mc_device *ls_dev) #endif unregister_netdev(net_dev); - rtnl_lock(); - dpaa2_eth_disconnect_mac(priv); - rtnl_unlock(); dpaa2_eth_dl_port_del(priv); dpaa2_eth_dl_traps_unregister(priv); @@ -5015,6 +5012,9 @@ static int dpaa2_eth_remove(struct fsl_mc_device *ls_dev) else fsl_mc_free_irqs(ls_dev); + rtnl_lock(); + dpaa2_eth_disconnect_mac(priv); + rtnl_unlock(); dpaa2_eth_free_rings(priv); free_percpu(priv->fd); free_percpu(priv->sgt_cache); From 2291982e29b1aa6e628d3072ef533aeb6299945f Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Tue, 29 Nov 2022 16:12:19 +0200 Subject: [PATCH 1392/2002] net: dpaa2-eth: serialize changes to priv->mac with a mutex The dpaa2 architecture permits dynamic connections between objects on the fsl-mc bus, specifically between a DPNI object (represented by a struct net_device) and a DPMAC object (represented by a struct phylink). The DPNI driver is notified when those connections are created/broken through the dpni_irq0_handler_thread() method. To ensure that ethtool operations, as well as netdev up/down operations serialize with the connection/disconnection of the DPNI with a DPMAC, dpni_irq0_handler_thread() takes the rtnl_lock() to block those other operations from taking place. There is code called by dpaa2_mac_connect() which wants to acquire the rtnl_mutex once again, see phylink_create() -> phylink_register_sfp() -> sfp_bus_add_upstream() -> rtnl_lock(). So the strategy doesn't quite work out, even though it's fairly simple. Create a different strategy, where all code paths in the dpaa2-eth driver access priv->mac only while they are holding priv->mac_lock. The phylink instance is not created or connected to the PHY under the priv->mac_lock, but only assigned to priv->mac then. This will eliminate the reliance on the rtnl_mutex. Add lockdep annotations and put comments where holding the lock is not necessary, and priv->mac can be dereferenced freely. Signed-off-by: Vladimir Oltean Reviewed-by: Ioana Ciornei Tested-by: Ioana Ciornei Signed-off-by: Paolo Abeni --- .../net/ethernet/freescale/dpaa2/dpaa2-eth.c | 43 ++++++++++++-- .../net/ethernet/freescale/dpaa2/dpaa2-eth.h | 6 ++ .../ethernet/freescale/dpaa2/dpaa2-ethtool.c | 58 +++++++++++++++---- 3 files changed, 91 insertions(+), 16 deletions(-) diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c index b77d292cd960..3ed54c147e98 100644 --- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c +++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c @@ -2147,8 +2147,11 @@ static int dpaa2_eth_link_state_update(struct dpaa2_eth_priv *priv) /* When we manage the MAC/PHY using phylink there is no need * to manually update the netif_carrier. + * We can avoid locking because we are called from the "link changed" + * IRQ handler, which is the same as the "endpoint changed" IRQ handler + * (the writer to priv->mac), so we cannot race with it. */ - if (dpaa2_eth_is_type_phy(priv)) + if (dpaa2_mac_is_type_phy(priv->mac)) goto out; /* Chech link state; speed / duplex changes are not treated yet */ @@ -2179,6 +2182,8 @@ static int dpaa2_eth_open(struct net_device *net_dev) dpaa2_eth_seed_pools(priv); + mutex_lock(&priv->mac_lock); + if (!dpaa2_eth_is_type_phy(priv)) { /* We'll only start the txqs when the link is actually ready; * make sure we don't race against the link up notification, @@ -2197,6 +2202,7 @@ static int dpaa2_eth_open(struct net_device *net_dev) err = dpni_enable(priv->mc_io, 0, priv->mc_token); if (err < 0) { + mutex_unlock(&priv->mac_lock); netdev_err(net_dev, "dpni_enable() failed\n"); goto enable_err; } @@ -2204,6 +2210,8 @@ static int dpaa2_eth_open(struct net_device *net_dev) if (dpaa2_eth_is_type_phy(priv)) dpaa2_mac_start(priv->mac); + mutex_unlock(&priv->mac_lock); + return 0; enable_err: @@ -2275,6 +2283,8 @@ static int dpaa2_eth_stop(struct net_device *net_dev) int dpni_enabled = 0; int retries = 10; + mutex_lock(&priv->mac_lock); + if (dpaa2_eth_is_type_phy(priv)) { dpaa2_mac_stop(priv->mac); } else { @@ -2282,6 +2292,8 @@ static int dpaa2_eth_stop(struct net_device *net_dev) netif_carrier_off(net_dev); } + mutex_unlock(&priv->mac_lock); + /* On dpni_disable(), the MC firmware will: * - stop MAC Rx and wait for all Rx frames to be enqueued to software * - cut off WRIOP dequeues from egress FQs and wait until transmission @@ -2607,12 +2619,20 @@ static int dpaa2_eth_ts_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) static int dpaa2_eth_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) { struct dpaa2_eth_priv *priv = netdev_priv(dev); + int err; if (cmd == SIOCSHWTSTAMP) return dpaa2_eth_ts_ioctl(dev, rq, cmd); - if (dpaa2_eth_is_type_phy(priv)) - return phylink_mii_ioctl(priv->mac->phylink, rq, cmd); + mutex_lock(&priv->mac_lock); + + if (dpaa2_eth_is_type_phy(priv)) { + err = phylink_mii_ioctl(priv->mac->phylink, rq, cmd); + mutex_unlock(&priv->mac_lock); + return err; + } + + mutex_unlock(&priv->mac_lock); return -EOPNOTSUPP; } @@ -4639,7 +4659,9 @@ static int dpaa2_eth_connect_mac(struct dpaa2_eth_priv *priv) } } + mutex_lock(&priv->mac_lock); priv->mac = mac; + mutex_unlock(&priv->mac_lock); return 0; @@ -4652,9 +4674,12 @@ err_free_mac: static void dpaa2_eth_disconnect_mac(struct dpaa2_eth_priv *priv) { - struct dpaa2_mac *mac = priv->mac; + struct dpaa2_mac *mac; + mutex_lock(&priv->mac_lock); + mac = priv->mac; priv->mac = NULL; + mutex_unlock(&priv->mac_lock); if (!mac) return; @@ -4673,6 +4698,7 @@ static irqreturn_t dpni_irq0_handler_thread(int irq_num, void *arg) struct fsl_mc_device *dpni_dev = to_fsl_mc_device(dev); struct net_device *net_dev = dev_get_drvdata(dev); struct dpaa2_eth_priv *priv = netdev_priv(net_dev); + bool had_mac; int err; err = dpni_get_irq_status(dpni_dev->mc_io, 0, dpni_dev->mc_handle, @@ -4690,7 +4716,12 @@ static irqreturn_t dpni_irq0_handler_thread(int irq_num, void *arg) dpaa2_eth_update_tx_fqids(priv); rtnl_lock(); - if (dpaa2_eth_has_mac(priv)) + /* We can avoid locking because the "endpoint changed" IRQ + * handler is the only one who changes priv->mac at runtime, + * so we are not racing with anyone. + */ + had_mac = !!priv->mac; + if (had_mac) dpaa2_eth_disconnect_mac(priv); else dpaa2_eth_connect_mac(priv); @@ -4792,6 +4823,8 @@ static int dpaa2_eth_probe(struct fsl_mc_device *dpni_dev) priv->net_dev = net_dev; SET_NETDEV_DEVLINK_PORT(net_dev, &priv->devlink_port); + mutex_init(&priv->mac_lock); + priv->iommu_domain = iommu_get_domain_for_dev(dev); priv->tx_tstamp_type = HWTSTAMP_TX_OFF; diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.h b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.h index 04270ae44d84..d56d7a13262e 100644 --- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.h +++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.h @@ -615,6 +615,8 @@ struct dpaa2_eth_priv { #endif struct dpaa2_mac *mac; + /* Serializes changes to priv->mac */ + struct mutex mac_lock; struct workqueue_struct *dpaa2_ptp_wq; struct work_struct tx_onestep_tstamp; struct sk_buff_head tx_skbs; @@ -768,11 +770,15 @@ static inline unsigned int dpaa2_eth_rx_head_room(struct dpaa2_eth_priv *priv) static inline bool dpaa2_eth_is_type_phy(struct dpaa2_eth_priv *priv) { + lockdep_assert_held(&priv->mac_lock); + return dpaa2_mac_is_type_phy(priv->mac); } static inline bool dpaa2_eth_has_mac(struct dpaa2_eth_priv *priv) { + lockdep_assert_held(&priv->mac_lock); + return priv->mac ? true : false; } diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-ethtool.c b/drivers/net/ethernet/freescale/dpaa2/dpaa2-ethtool.c index bd87aa9ef686..e80e9388c71f 100644 --- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-ethtool.c +++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-ethtool.c @@ -85,11 +85,16 @@ static void dpaa2_eth_get_drvinfo(struct net_device *net_dev, static int dpaa2_eth_nway_reset(struct net_device *net_dev) { struct dpaa2_eth_priv *priv = netdev_priv(net_dev); + int err = -EOPNOTSUPP; + + mutex_lock(&priv->mac_lock); if (dpaa2_eth_is_type_phy(priv)) - return phylink_ethtool_nway_reset(priv->mac->phylink); + err = phylink_ethtool_nway_reset(priv->mac->phylink); - return -EOPNOTSUPP; + mutex_unlock(&priv->mac_lock); + + return err; } static int @@ -97,10 +102,18 @@ dpaa2_eth_get_link_ksettings(struct net_device *net_dev, struct ethtool_link_ksettings *link_settings) { struct dpaa2_eth_priv *priv = netdev_priv(net_dev); + int err; - if (dpaa2_eth_is_type_phy(priv)) - return phylink_ethtool_ksettings_get(priv->mac->phylink, - link_settings); + mutex_lock(&priv->mac_lock); + + if (dpaa2_eth_is_type_phy(priv)) { + err = phylink_ethtool_ksettings_get(priv->mac->phylink, + link_settings); + mutex_unlock(&priv->mac_lock); + return err; + } + + mutex_unlock(&priv->mac_lock); link_settings->base.autoneg = AUTONEG_DISABLE; if (!(priv->link_state.options & DPNI_LINK_OPT_HALF_DUPLEX)) @@ -115,11 +128,17 @@ dpaa2_eth_set_link_ksettings(struct net_device *net_dev, const struct ethtool_link_ksettings *link_settings) { struct dpaa2_eth_priv *priv = netdev_priv(net_dev); + int err = -EOPNOTSUPP; - if (!dpaa2_eth_is_type_phy(priv)) - return -EOPNOTSUPP; + mutex_lock(&priv->mac_lock); - return phylink_ethtool_ksettings_set(priv->mac->phylink, link_settings); + if (dpaa2_eth_is_type_phy(priv)) + err = phylink_ethtool_ksettings_set(priv->mac->phylink, + link_settings); + + mutex_unlock(&priv->mac_lock); + + return err; } static void dpaa2_eth_get_pauseparam(struct net_device *net_dev, @@ -128,11 +147,16 @@ static void dpaa2_eth_get_pauseparam(struct net_device *net_dev, struct dpaa2_eth_priv *priv = netdev_priv(net_dev); u64 link_options = priv->link_state.options; + mutex_lock(&priv->mac_lock); + if (dpaa2_eth_is_type_phy(priv)) { phylink_ethtool_get_pauseparam(priv->mac->phylink, pause); + mutex_unlock(&priv->mac_lock); return; } + mutex_unlock(&priv->mac_lock); + pause->rx_pause = dpaa2_eth_rx_pause_enabled(link_options); pause->tx_pause = dpaa2_eth_tx_pause_enabled(link_options); pause->autoneg = AUTONEG_DISABLE; @@ -151,9 +175,17 @@ static int dpaa2_eth_set_pauseparam(struct net_device *net_dev, return -EOPNOTSUPP; } - if (dpaa2_eth_is_type_phy(priv)) - return phylink_ethtool_set_pauseparam(priv->mac->phylink, - pause); + mutex_lock(&priv->mac_lock); + + if (dpaa2_eth_is_type_phy(priv)) { + err = phylink_ethtool_set_pauseparam(priv->mac->phylink, + pause); + mutex_unlock(&priv->mac_lock); + return err; + } + + mutex_unlock(&priv->mac_lock); + if (pause->autoneg) return -EOPNOTSUPP; @@ -309,8 +341,12 @@ static void dpaa2_eth_get_ethtool_stats(struct net_device *net_dev, } *(data + i++) = buf_cnt_total; + mutex_lock(&priv->mac_lock); + if (dpaa2_eth_has_mac(priv)) dpaa2_mac_get_ethtool_stats(priv->mac, data + i); + + mutex_unlock(&priv->mac_lock); } static int dpaa2_eth_prep_eth_rule(struct ethhdr *eth_value, struct ethhdr *eth_mask, From 3c7f44fa9c4c8a9154935ca49e4cf45c14240335 Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Tue, 29 Nov 2022 16:12:20 +0200 Subject: [PATCH 1393/2002] net: dpaa2-switch: serialize changes to priv->mac with a mutex The dpaa2-switch driver uses a DPMAC in the same way as the dpaa2-eth driver, so we need to duplicate the locking solution established by the previous change to the switch driver as well. Signed-off-by: Vladimir Oltean Reviewed-by: Ioana Ciornei Tested-by: Ioana Ciornei Signed-off-by: Paolo Abeni --- .../freescale/dpaa2/dpaa2-switch-ethtool.c | 32 +++++++++++++++---- .../ethernet/freescale/dpaa2/dpaa2-switch.c | 31 ++++++++++++++++-- .../ethernet/freescale/dpaa2/dpaa2-switch.h | 2 ++ 3 files changed, 55 insertions(+), 10 deletions(-) diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch-ethtool.c b/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch-ethtool.c index 76a4b09e2854..6bc1988be311 100644 --- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch-ethtool.c +++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch-ethtool.c @@ -60,11 +60,18 @@ dpaa2_switch_get_link_ksettings(struct net_device *netdev, { struct ethsw_port_priv *port_priv = netdev_priv(netdev); struct dpsw_link_state state = {0}; - int err = 0; + int err; - if (dpaa2_switch_port_is_type_phy(port_priv)) - return phylink_ethtool_ksettings_get(port_priv->mac->phylink, - link_ksettings); + mutex_lock(&port_priv->mac_lock); + + if (dpaa2_switch_port_is_type_phy(port_priv)) { + err = phylink_ethtool_ksettings_get(port_priv->mac->phylink, + link_ksettings); + mutex_unlock(&port_priv->mac_lock); + return err; + } + + mutex_unlock(&port_priv->mac_lock); err = dpsw_if_get_link_state(port_priv->ethsw_data->mc_io, 0, port_priv->ethsw_data->dpsw_handle, @@ -99,9 +106,16 @@ dpaa2_switch_set_link_ksettings(struct net_device *netdev, bool if_running; int err = 0, ret; - if (dpaa2_switch_port_is_type_phy(port_priv)) - return phylink_ethtool_ksettings_set(port_priv->mac->phylink, - link_ksettings); + mutex_lock(&port_priv->mac_lock); + + if (dpaa2_switch_port_is_type_phy(port_priv)) { + err = phylink_ethtool_ksettings_set(port_priv->mac->phylink, + link_ksettings); + mutex_unlock(&port_priv->mac_lock); + return err; + } + + mutex_unlock(&port_priv->mac_lock); /* Interface needs to be down to change link settings */ if_running = netif_running(netdev); @@ -189,8 +203,12 @@ static void dpaa2_switch_ethtool_get_stats(struct net_device *netdev, dpaa2_switch_ethtool_counters[i].name, err); } + mutex_lock(&port_priv->mac_lock); + if (dpaa2_switch_port_has_mac(port_priv)) dpaa2_mac_get_ethtool_stats(port_priv->mac, data + i); + + mutex_unlock(&port_priv->mac_lock); } const struct ethtool_ops dpaa2_switch_port_ethtool_ops = { diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c b/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c index 3b0963d95f67..0472e24191ad 100644 --- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c +++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c @@ -602,8 +602,11 @@ static int dpaa2_switch_port_link_state_update(struct net_device *netdev) /* When we manage the MAC/PHY using phylink there is no need * to manually update the netif_carrier. + * We can avoid locking because we are called from the "link changed" + * IRQ handler, which is the same as the "endpoint changed" IRQ handler + * (the writer to port_priv->mac), so we cannot race with it. */ - if (dpaa2_switch_port_is_type_phy(port_priv)) + if (dpaa2_mac_is_type_phy(port_priv->mac)) return 0; /* Interrupts are received even though no one issued an 'ifconfig up' @@ -683,6 +686,8 @@ static int dpaa2_switch_port_open(struct net_device *netdev) struct ethsw_core *ethsw = port_priv->ethsw_data; int err; + mutex_lock(&port_priv->mac_lock); + if (!dpaa2_switch_port_is_type_phy(port_priv)) { /* Explicitly set carrier off, otherwise * netif_carrier_ok() will return true and cause 'ip link show' @@ -696,6 +701,7 @@ static int dpaa2_switch_port_open(struct net_device *netdev) port_priv->ethsw_data->dpsw_handle, port_priv->idx); if (err) { + mutex_unlock(&port_priv->mac_lock); netdev_err(netdev, "dpsw_if_enable err %d\n", err); return err; } @@ -705,6 +711,8 @@ static int dpaa2_switch_port_open(struct net_device *netdev) if (dpaa2_switch_port_is_type_phy(port_priv)) dpaa2_mac_start(port_priv->mac); + mutex_unlock(&port_priv->mac_lock); + return 0; } @@ -714,6 +722,8 @@ static int dpaa2_switch_port_stop(struct net_device *netdev) struct ethsw_core *ethsw = port_priv->ethsw_data; int err; + mutex_lock(&port_priv->mac_lock); + if (dpaa2_switch_port_is_type_phy(port_priv)) { dpaa2_mac_stop(port_priv->mac); } else { @@ -721,6 +731,8 @@ static int dpaa2_switch_port_stop(struct net_device *netdev) netif_carrier_off(netdev); } + mutex_unlock(&port_priv->mac_lock); + err = dpsw_if_disable(port_priv->ethsw_data->mc_io, 0, port_priv->ethsw_data->dpsw_handle, port_priv->idx); @@ -1460,7 +1472,9 @@ static int dpaa2_switch_port_connect_mac(struct ethsw_port_priv *port_priv) } } + mutex_lock(&port_priv->mac_lock); port_priv->mac = mac; + mutex_unlock(&port_priv->mac_lock); return 0; @@ -1473,9 +1487,12 @@ err_free_mac: static void dpaa2_switch_port_disconnect_mac(struct ethsw_port_priv *port_priv) { - struct dpaa2_mac *mac = port_priv->mac; + struct dpaa2_mac *mac; + mutex_lock(&port_priv->mac_lock); + mac = port_priv->mac; port_priv->mac = NULL; + mutex_unlock(&port_priv->mac_lock); if (!mac) return; @@ -1494,6 +1511,7 @@ static irqreturn_t dpaa2_switch_irq0_handler_thread(int irq_num, void *arg) struct ethsw_port_priv *port_priv; u32 status = ~0; int err, if_id; + bool had_mac; err = dpsw_get_irq_status(ethsw->mc_io, 0, ethsw->dpsw_handle, DPSW_IRQ_INDEX_IF, &status); @@ -1512,7 +1530,12 @@ static irqreturn_t dpaa2_switch_irq0_handler_thread(int irq_num, void *arg) if (status & DPSW_IRQ_EVENT_ENDPOINT_CHANGED) { rtnl_lock(); - if (dpaa2_switch_port_has_mac(port_priv)) + /* We can avoid locking because the "endpoint changed" IRQ + * handler is the only one who changes priv->mac at runtime, + * so we are not racing with anyone. + */ + had_mac = !!port_priv->mac; + if (had_mac) dpaa2_switch_port_disconnect_mac(port_priv); else dpaa2_switch_port_connect_mac(port_priv); @@ -3255,6 +3278,8 @@ static int dpaa2_switch_probe_port(struct ethsw_core *ethsw, port_priv->netdev = port_netdev; port_priv->ethsw_data = ethsw; + mutex_init(&port_priv->mac_lock); + port_priv->idx = port_idx; port_priv->stp_state = BR_STATE_FORWARDING; diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.h b/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.h index 9898073abe01..42b3ca73f55d 100644 --- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.h +++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.h @@ -161,6 +161,8 @@ struct ethsw_port_priv { struct dpaa2_switch_filter_block *filter_block; struct dpaa2_mac *mac; + /* Protects against changes to port_priv->mac */ + struct mutex mac_lock; }; /* Switch data */ From 87db82cb614973a87a03ac298ad4ab4c5ca5d38e Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Tue, 29 Nov 2022 16:12:21 +0200 Subject: [PATCH 1394/2002] net: dpaa2-mac: move rtnl_lock() only around phylink_{,dis}connect_phy() After the introduction of a private mac_lock that serializes access to priv->mac (and port_priv->mac in the switch), the only remaining purpose of rtnl_lock() is to satisfy the locking requirements of phylink_fwnode_phy_connect() and phylink_disconnect_phy(). But the functions these live in, dpaa2_mac_connect() and dpaa2_mac_disconnect(), have contradictory locking requirements. While phylink_fwnode_phy_connect() wants rtnl_lock() to be held, phylink_create() wants it to not be held. Move the rtnl_lock() from top-level (in the dpaa2-eth and dpaa2-switch drivers) to only surround the phylink calls that require it, in the dpaa2-mac library code. This is possible because dpaa2_mac_connect() and dpaa2_mac_disconnect() run unlocked, and there isn't any danger of an AB/BA deadlock between the rtnl_mutex and other private locks. Signed-off-by: Vladimir Oltean Reviewed-by: Ioana Ciornei Tested-by: Ioana Ciornei Signed-off-by: Paolo Abeni --- drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c | 4 ---- drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c | 5 +++++ drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c | 4 ---- 3 files changed, 5 insertions(+), 8 deletions(-) diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c index 3ed54c147e98..0c35abb7d065 100644 --- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c +++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c @@ -4715,7 +4715,6 @@ static irqreturn_t dpni_irq0_handler_thread(int irq_num, void *arg) dpaa2_eth_set_mac_addr(netdev_priv(net_dev)); dpaa2_eth_update_tx_fqids(priv); - rtnl_lock(); /* We can avoid locking because the "endpoint changed" IRQ * handler is the only one who changes priv->mac at runtime, * so we are not racing with anyone. @@ -4725,7 +4724,6 @@ static irqreturn_t dpni_irq0_handler_thread(int irq_num, void *arg) dpaa2_eth_disconnect_mac(priv); else dpaa2_eth_connect_mac(priv); - rtnl_unlock(); } return IRQ_HANDLED; @@ -5045,9 +5043,7 @@ static int dpaa2_eth_remove(struct fsl_mc_device *ls_dev) else fsl_mc_free_irqs(ls_dev); - rtnl_lock(); dpaa2_eth_disconnect_mac(priv); - rtnl_unlock(); dpaa2_eth_free_rings(priv); free_percpu(priv->fd); free_percpu(priv->sgt_cache); diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c b/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c index 3096810120ee..c886f33f8c6f 100644 --- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c +++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c @@ -430,7 +430,9 @@ int dpaa2_mac_connect(struct dpaa2_mac *mac) } mac->phylink = phylink; + rtnl_lock(); err = phylink_fwnode_phy_connect(mac->phylink, dpmac_node, 0); + rtnl_unlock(); if (err) { netdev_err(net_dev, "phylink_fwnode_phy_connect() = %d\n", err); goto err_phylink_destroy; @@ -448,7 +450,10 @@ err_pcs_destroy: void dpaa2_mac_disconnect(struct dpaa2_mac *mac) { + rtnl_lock(); phylink_disconnect_phy(mac->phylink); + rtnl_unlock(); + phylink_destroy(mac->phylink); dpaa2_pcs_destroy(mac); of_phy_put(mac->serdes_phy); diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c b/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c index 0472e24191ad..f4ae4289c41a 100644 --- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c +++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c @@ -1529,7 +1529,6 @@ static irqreturn_t dpaa2_switch_irq0_handler_thread(int irq_num, void *arg) } if (status & DPSW_IRQ_EVENT_ENDPOINT_CHANGED) { - rtnl_lock(); /* We can avoid locking because the "endpoint changed" IRQ * handler is the only one who changes priv->mac at runtime, * so we are not racing with anyone. @@ -1539,7 +1538,6 @@ static irqreturn_t dpaa2_switch_irq0_handler_thread(int irq_num, void *arg) dpaa2_switch_port_disconnect_mac(port_priv); else dpaa2_switch_port_connect_mac(port_priv); - rtnl_unlock(); } out: @@ -2957,9 +2955,7 @@ static void dpaa2_switch_remove_port(struct ethsw_core *ethsw, { struct ethsw_port_priv *port_priv = ethsw->ports[port_idx]; - rtnl_lock(); dpaa2_switch_port_disconnect_mac(port_priv); - rtnl_unlock(); free_netdev(port_priv->netdev); ethsw->ports[port_idx] = NULL; } From 278ab9793116a8531ffaa52b4e61644971131e35 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Wed, 30 Nov 2022 13:26:45 -0800 Subject: [PATCH 1395/2002] wifi: ieee80211: Do not open-code qos address offsets When building with -Wstringop-overflow, GCC's KASAN implementation does not correctly perform bounds checking within some complex structures when faced with literal offsets, and can get very confused. For example, this warning is seen due to literal offsets into sturct ieee80211_hdr that may or may not be large enough: drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c: In function 'iwl_mvm_rx_mpdu_mq': drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c:2022:29: warning: writing 1 byte into a region of size 0 [-Wstringop-overflow=] 2022 | *qc &= ~IEEE80211_QOS_CTL_A_MSDU_PRESENT; In file included from drivers/net/wireless/intel/iwlwifi/mvm/fw-api.h:32, from drivers/net/wireless/intel/iwlwifi/mvm/sta.h:15, from drivers/net/wireless/intel/iwlwifi/mvm/mvm.h:27, from drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c:10: drivers/net/wireless/intel/iwlwifi/mvm/../fw/api/rx.h:559:16: note: at offset [78, 166] into destination object 'mpdu_len' of size 2 559 | __le16 mpdu_len; | ^~~~~~~~ Refactor ieee80211_get_qos_ctl() to avoid using literal offsets, requiring the creation of the actual structure that is described in the comments. Explicitly choose the desired offset, making the code more human-readable too. This is one of the last remaining warning to fix before enabling -Wstringop-overflow globally. Link: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=97490 Link: https://github.com/KSPP/linux/issues/181 Cc: Johannes Berg Cc: Kalle Valo Cc: Gregory Greenman Cc: "Gustavo A. R. Silva" Cc: linux-wireless@vger.kernel.org Cc: netdev@vger.kernel.org Signed-off-by: Kees Cook Link: https://lore.kernel.org/r/20221130212641.never.627-kees@kernel.org Signed-off-by: Johannes Berg --- include/linux/ieee80211.h | 28 ++++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h index 6252f02f38b7..80d6308dea06 100644 --- a/include/linux/ieee80211.h +++ b/include/linux/ieee80211.h @@ -338,6 +338,17 @@ struct ieee80211_qos_hdr { __le16 qos_ctrl; } __packed __aligned(2); +struct ieee80211_qos_hdr_4addr { + __le16 frame_control; + __le16 duration_id; + u8 addr1[ETH_ALEN]; + u8 addr2[ETH_ALEN]; + u8 addr3[ETH_ALEN]; + __le16 seq_ctrl; + u8 addr4[ETH_ALEN]; + __le16 qos_ctrl; +} __packed __aligned(2); + struct ieee80211_trigger { __le16 frame_control; __le16 duration; @@ -4060,16 +4071,21 @@ struct ieee80211_he_6ghz_capa { * @hdr: the frame * * The qos ctrl bytes come after the frame_control, duration, seq_num - * and 3 or 4 addresses of length ETH_ALEN. - * 3 addr: 2 + 2 + 2 + 3*6 = 24 - * 4 addr: 2 + 2 + 2 + 4*6 = 30 + * and 3 or 4 addresses of length ETH_ALEN. Checks frame_control to choose + * between struct ieee80211_qos_hdr_4addr and struct ieee80211_qos_hdr. */ static inline u8 *ieee80211_get_qos_ctl(struct ieee80211_hdr *hdr) { - if (ieee80211_has_a4(hdr->frame_control)) - return (u8 *)hdr + 30; + union { + struct ieee80211_qos_hdr addr3; + struct ieee80211_qos_hdr_4addr addr4; + } *qos; + + qos = (void *)hdr; + if (ieee80211_has_a4(qos->addr3.frame_control)) + return (u8 *)&qos->addr4.qos_ctrl; else - return (u8 *)hdr + 24; + return (u8 *)&qos->addr3.qos_ctrl; } /** From 5cc58b376675981386c6192405fe887cd29c527a Mon Sep 17 00:00:00 2001 From: Yuan Can Date: Tue, 29 Nov 2022 01:42:11 +0000 Subject: [PATCH 1396/2002] wifi: nl80211: Add checks for nla_nest_start() in nl80211_send_iface() As the nla_nest_start() may fail with NULL returned, the return value needs to be checked. Fixes: ce08cd344a00 ("wifi: nl80211: expose link information for interfaces") Signed-off-by: Yuan Can Link: https://lore.kernel.org/r/20221129014211.56558-1-yuancan@huawei.com Signed-off-by: Johannes Berg --- net/wireless/nl80211.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 1ad0326ff4dc..33a82ecab9d5 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -3868,6 +3868,9 @@ static int nl80211_send_iface(struct sk_buff *msg, u32 portid, u32 seq, int flag struct cfg80211_chan_def chandef = {}; int ret; + if (!link) + goto nla_put_failure; + if (nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID, link_id)) goto nla_put_failure; if (nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, From 13e5afd3d773c6fc6ca2b89027befaaaa1ea7293 Mon Sep 17 00:00:00 2001 From: Zhengchao Shao Date: Thu, 17 Nov 2022 14:45:00 +0800 Subject: [PATCH 1397/2002] wifi: mac80211: fix memory leak in ieee80211_if_add() When register_netdevice() failed in ieee80211_if_add(), ndev->tstats isn't released. Fix it. Fixes: 5a490510ba5f ("mac80211: use per-CPU TX/RX statistics") Signed-off-by: Zhengchao Shao Link: https://lore.kernel.org/r/20221117064500.319983-1-shaozhengchao@huawei.com Signed-off-by: Johannes Berg --- net/mac80211/iface.c | 1 + 1 file changed, 1 insertion(+) diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index 7c4ce716c939..a5782927a93b 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -2195,6 +2195,7 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name, ret = cfg80211_register_netdevice(ndev); if (ret) { + ieee80211_if_free(ndev); free_netdev(ndev); return ret; } From 4d371d6e3746d84056f150fdaa66c85f65f60124 Mon Sep 17 00:00:00 2001 From: Philipp Hortmann Date: Mon, 14 Nov 2022 21:01:35 +0100 Subject: [PATCH 1398/2002] wifi: cfg80211: Correct example of ieee80211_iface_limit Correct wrong closing bracket. Signed-off-by: Philipp Hortmann Link: https://lore.kernel.org/r/20221114200135.GA100176@matrix-ESPRIMO-P710 Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 11a370e64143..03d4f4deadae 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -4742,7 +4742,7 @@ struct ieee80211_iface_limit { * * struct ieee80211_iface_limit limits1[] = { * { .max = 1, .types = BIT(NL80211_IFTYPE_STATION), }, - * { .max = 1, .types = BIT(NL80211_IFTYPE_AP}, }, + * { .max = 1, .types = BIT(NL80211_IFTYPE_AP), }, * }; * struct ieee80211_iface_combination combination1 = { * .limits = limits1, From 4b6c6065fca123d419afef005a696f51e6590470 Mon Sep 17 00:00:00 2001 From: Juhee Kang Date: Wed, 30 Nov 2022 01:12:44 +0900 Subject: [PATCH 1399/2002] r8169: use tp_to_dev instead of open code The open code is defined as a helper function(tp_to_dev) on r8169_main.c, which the open code is &tp->pci_dev->dev. The helper function was added in commit 1e1205b7d3e9 ("r8169: add helper tp_to_dev"). And then later, commit f1e911d5d0df ("r8169: add basic phylib support") added r8169_phylink_handler function but it didn't use the helper function. Thus, tp_to_dev() replaces the open code. This patch doesn't change logic. Signed-off-by: Juhee Kang Reviewed-by: Heiner Kallweit Link: https://lore.kernel.org/r/20221129161244.5356-1-claudiajkang@gmail.com Signed-off-by: Paolo Abeni --- drivers/net/ethernet/realtek/r8169_main.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/realtek/r8169_main.c b/drivers/net/ethernet/realtek/r8169_main.c index 5bc1181f829b..ec157885da13 100644 --- a/drivers/net/ethernet/realtek/r8169_main.c +++ b/drivers/net/ethernet/realtek/r8169_main.c @@ -4559,12 +4559,13 @@ static int rtl8169_poll(struct napi_struct *napi, int budget) static void r8169_phylink_handler(struct net_device *ndev) { struct rtl8169_private *tp = netdev_priv(ndev); + struct device *d = tp_to_dev(tp); if (netif_carrier_ok(ndev)) { rtl_link_chg_patch(tp); - pm_request_resume(&tp->pci_dev->dev); + pm_request_resume(d); } else { - pm_runtime_idle(&tp->pci_dev->dev); + pm_runtime_idle(d); } phy_print_status(tp->phydev); From 84924aac08a43169811b4814c67994a9154a6a82 Mon Sep 17 00:00:00 2001 From: David Howells Date: Thu, 1 Dec 2022 12:24:36 +0000 Subject: [PATCH 1400/2002] rxrpc: Fix checker warning Fix the following checker warning: ../net/rxrpc/key.c:692:9: error: subtraction of different types can't work (different address spaces) Checker is wrong in this case, but cast the pointers to unsigned long to avoid the warning. Whilst we're at it, reduce the assertions to WARN_ON() and return an error. Signed-off-by: David Howells cc: Marc Dionne cc: linux-afs@lists.infradead.org --- net/rxrpc/key.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/net/rxrpc/key.c b/net/rxrpc/key.c index 8d2073e0e3da..830eeffe2d5b 100644 --- a/net/rxrpc/key.c +++ b/net/rxrpc/key.c @@ -602,7 +602,8 @@ static long rxrpc_read(const struct key *key, } _debug("token[%u]: toksize=%u", ntoks, toksize); - ASSERTCMP(toksize, <=, AFSTOKEN_LENGTH_MAX); + if (WARN_ON(toksize > AFSTOKEN_LENGTH_MAX)) + return -EIO; toksizes[ntoks++] = toksize; size += toksize + 4; /* each token has a length word */ @@ -679,8 +680,9 @@ static long rxrpc_read(const struct key *key, return -ENOPKG; } - ASSERTCMP((unsigned long)xdr - (unsigned long)oldxdr, ==, - toksize); + if (WARN_ON((unsigned long)xdr - (unsigned long)oldxdr == + toksize)) + return -EIO; } #undef ENCODE_STR @@ -688,8 +690,10 @@ static long rxrpc_read(const struct key *key, #undef ENCODE64 #undef ENCODE - ASSERTCMP(tok, ==, ntoks); - ASSERTCMP((char __user *) xdr - buffer, ==, size); + if (WARN_ON(tok != ntoks)) + return -EIO; + if (WARN_ON((unsigned long)xdr - (unsigned long)buffer != size)) + return -EIO; _leave(" = %zu", size); return size; } From 75bfdbf2fca372e2709bcaa43e8cf1147766ae96 Mon Sep 17 00:00:00 2001 From: David Howells Date: Thu, 3 Nov 2022 22:27:52 +0000 Subject: [PATCH 1401/2002] rxrpc: Implement an in-kernel rxperf server for testing purposes Implement an in-kernel rxperf server to allow kernel-based rxrpc services to be tested directly, unlike with AFS where they're accessed by the fileserver when the latter decides it wants to. This is implemented as a module that, if loaded, opens UDP port 7009 (afs3-rmtsys) and listens on it for incoming calls. Calls can be generated using the rxperf command shipped with OpenAFS, for example. Changes ======= ver #2) - Use min_t() instead of min(). Signed-off-by: David Howells cc: Marc Dionne cc: linux-afs@lists.infradead.org cc: Jakub Kicinski --- include/net/af_rxrpc.h | 1 + net/rxrpc/Kconfig | 7 + net/rxrpc/Makefile | 3 + net/rxrpc/rxperf.c | 619 +++++++++++++++++++++++++++++++++++++++++ net/rxrpc/server_key.c | 25 ++ 5 files changed, 655 insertions(+) create mode 100644 net/rxrpc/rxperf.c diff --git a/include/net/af_rxrpc.h b/include/net/af_rxrpc.h index b69ca695935c..dc033f08191e 100644 --- a/include/net/af_rxrpc.h +++ b/include/net/af_rxrpc.h @@ -71,5 +71,6 @@ void rxrpc_kernel_set_max_life(struct socket *, struct rxrpc_call *, unsigned long); int rxrpc_sock_set_min_security_level(struct sock *sk, unsigned int val); +int rxrpc_sock_set_security_keyring(struct sock *, struct key *); #endif /* _NET_RXRPC_H */ diff --git a/net/rxrpc/Kconfig b/net/rxrpc/Kconfig index accd35c05577..7ae023b37a83 100644 --- a/net/rxrpc/Kconfig +++ b/net/rxrpc/Kconfig @@ -58,4 +58,11 @@ config RXKAD See Documentation/networking/rxrpc.rst. +config RXPERF + tristate "RxRPC test service" + help + Provide an rxperf service tester. This listens on UDP port 7009 for + incoming calls from the rxperf program (an example of which can be + found in OpenAFS). + endif diff --git a/net/rxrpc/Makefile b/net/rxrpc/Makefile index fdeba488fc6e..79687477d93c 100644 --- a/net/rxrpc/Makefile +++ b/net/rxrpc/Makefile @@ -36,3 +36,6 @@ rxrpc-y := \ rxrpc-$(CONFIG_PROC_FS) += proc.o rxrpc-$(CONFIG_RXKAD) += rxkad.o rxrpc-$(CONFIG_SYSCTL) += sysctl.o + + +obj-$(CONFIG_RXPERF) += rxperf.o diff --git a/net/rxrpc/rxperf.c b/net/rxrpc/rxperf.c new file mode 100644 index 000000000000..66f5eea291ff --- /dev/null +++ b/net/rxrpc/rxperf.c @@ -0,0 +1,619 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* In-kernel rxperf server for testing purposes. + * + * Copyright (C) 2022 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + */ + +#define pr_fmt(fmt) "rxperf: " fmt +#include +#include +#include +#include + +MODULE_DESCRIPTION("rxperf test server (afs)"); +MODULE_AUTHOR("Red Hat, Inc."); +MODULE_LICENSE("GPL"); + +#define RXPERF_PORT 7009 +#define RX_PERF_SERVICE 147 +#define RX_PERF_VERSION 3 +#define RX_PERF_SEND 0 +#define RX_PERF_RECV 1 +#define RX_PERF_RPC 3 +#define RX_PERF_FILE 4 +#define RX_PERF_MAGIC_COOKIE 0x4711 + +struct rxperf_proto_params { + __be32 version; + __be32 type; + __be32 rsize; + __be32 wsize; +} __packed; + +static const u8 rxperf_magic_cookie[] = { 0x00, 0x00, 0x47, 0x11 }; +static const u8 secret[8] = { 0xa7, 0x83, 0x8a, 0xcb, 0xc7, 0x83, 0xec, 0x94 }; + +enum rxperf_call_state { + RXPERF_CALL_SV_AWAIT_PARAMS, /* Server: Awaiting parameter block */ + RXPERF_CALL_SV_AWAIT_REQUEST, /* Server: Awaiting request data */ + RXPERF_CALL_SV_REPLYING, /* Server: Replying */ + RXPERF_CALL_SV_AWAIT_ACK, /* Server: Awaiting final ACK */ + RXPERF_CALL_COMPLETE, /* Completed or failed */ +}; + +struct rxperf_call { + struct rxrpc_call *rxcall; + struct iov_iter iter; + struct kvec kvec[1]; + struct work_struct work; + const char *type; + size_t iov_len; + size_t req_len; /* Size of request blob */ + size_t reply_len; /* Size of reply blob */ + unsigned int debug_id; + unsigned int operation_id; + struct rxperf_proto_params params; + __be32 tmp[2]; + s32 abort_code; + enum rxperf_call_state state; + short error; + unsigned short unmarshal; + u16 service_id; + int (*deliver)(struct rxperf_call *call); + void (*processor)(struct work_struct *work); +}; + +static struct socket *rxperf_socket; +static struct key *rxperf_sec_keyring; /* Ring of security/crypto keys */ +static struct workqueue_struct *rxperf_workqueue; + +static void rxperf_deliver_to_call(struct work_struct *work); +static int rxperf_deliver_param_block(struct rxperf_call *call); +static int rxperf_deliver_request(struct rxperf_call *call); +static int rxperf_process_call(struct rxperf_call *call); +static void rxperf_charge_preallocation(struct work_struct *work); + +static DECLARE_WORK(rxperf_charge_preallocation_work, + rxperf_charge_preallocation); + +static inline void rxperf_set_call_state(struct rxperf_call *call, + enum rxperf_call_state to) +{ + call->state = to; +} + +static inline void rxperf_set_call_complete(struct rxperf_call *call, + int error, s32 remote_abort) +{ + if (call->state != RXPERF_CALL_COMPLETE) { + call->abort_code = remote_abort; + call->error = error; + call->state = RXPERF_CALL_COMPLETE; + } +} + +static void rxperf_rx_discard_new_call(struct rxrpc_call *rxcall, + unsigned long user_call_ID) +{ + kfree((struct rxperf_call *)user_call_ID); +} + +static void rxperf_rx_new_call(struct sock *sk, struct rxrpc_call *rxcall, + unsigned long user_call_ID) +{ + queue_work(rxperf_workqueue, &rxperf_charge_preallocation_work); +} + +static void rxperf_queue_call_work(struct rxperf_call *call) +{ + queue_work(rxperf_workqueue, &call->work); +} + +static void rxperf_notify_rx(struct sock *sk, struct rxrpc_call *rxcall, + unsigned long call_user_ID) +{ + struct rxperf_call *call = (struct rxperf_call *)call_user_ID; + + if (call->state != RXPERF_CALL_COMPLETE) + rxperf_queue_call_work(call); +} + +static void rxperf_rx_attach(struct rxrpc_call *rxcall, unsigned long user_call_ID) +{ + struct rxperf_call *call = (struct rxperf_call *)user_call_ID; + + call->rxcall = rxcall; +} + +static void rxperf_notify_end_reply_tx(struct sock *sock, + struct rxrpc_call *rxcall, + unsigned long call_user_ID) +{ + rxperf_set_call_state((struct rxperf_call *)call_user_ID, + RXPERF_CALL_SV_AWAIT_ACK); +} + +/* + * Charge the incoming call preallocation. + */ +static void rxperf_charge_preallocation(struct work_struct *work) +{ + struct rxperf_call *call; + + for (;;) { + call = kzalloc(sizeof(*call), GFP_KERNEL); + if (!call) + break; + + call->type = "unset"; + call->debug_id = atomic_inc_return(&rxrpc_debug_id); + call->deliver = rxperf_deliver_param_block; + call->state = RXPERF_CALL_SV_AWAIT_PARAMS; + call->service_id = RX_PERF_SERVICE; + call->iov_len = sizeof(call->params); + call->kvec[0].iov_len = sizeof(call->params); + call->kvec[0].iov_base = &call->params; + iov_iter_kvec(&call->iter, READ, call->kvec, 1, call->iov_len); + INIT_WORK(&call->work, rxperf_deliver_to_call); + + if (rxrpc_kernel_charge_accept(rxperf_socket, + rxperf_notify_rx, + rxperf_rx_attach, + (unsigned long)call, + GFP_KERNEL, + call->debug_id) < 0) + break; + call = NULL; + } + + kfree(call); +} + +/* + * Open an rxrpc socket and bind it to be a server for callback notifications + * - the socket is left in blocking mode and non-blocking ops use MSG_DONTWAIT + */ +static int rxperf_open_socket(void) +{ + struct sockaddr_rxrpc srx; + struct socket *socket; + int ret; + + ret = sock_create_kern(&init_net, AF_RXRPC, SOCK_DGRAM, PF_INET6, + &socket); + if (ret < 0) + goto error_1; + + socket->sk->sk_allocation = GFP_NOFS; + + /* bind the callback manager's address to make this a server socket */ + memset(&srx, 0, sizeof(srx)); + srx.srx_family = AF_RXRPC; + srx.srx_service = RX_PERF_SERVICE; + srx.transport_type = SOCK_DGRAM; + srx.transport_len = sizeof(srx.transport.sin6); + srx.transport.sin6.sin6_family = AF_INET6; + srx.transport.sin6.sin6_port = htons(RXPERF_PORT); + + ret = rxrpc_sock_set_min_security_level(socket->sk, + RXRPC_SECURITY_ENCRYPT); + if (ret < 0) + goto error_2; + + ret = rxrpc_sock_set_security_keyring(socket->sk, rxperf_sec_keyring); + + ret = kernel_bind(socket, (struct sockaddr *)&srx, sizeof(srx)); + if (ret < 0) + goto error_2; + + rxrpc_kernel_new_call_notification(socket, rxperf_rx_new_call, + rxperf_rx_discard_new_call); + + ret = kernel_listen(socket, INT_MAX); + if (ret < 0) + goto error_2; + + rxperf_socket = socket; + rxperf_charge_preallocation(&rxperf_charge_preallocation_work); + return 0; + +error_2: + sock_release(socket); +error_1: + pr_err("Can't set up rxperf socket: %d\n", ret); + return ret; +} + +/* + * close the rxrpc socket rxperf was using + */ +static void rxperf_close_socket(void) +{ + kernel_listen(rxperf_socket, 0); + kernel_sock_shutdown(rxperf_socket, SHUT_RDWR); + flush_workqueue(rxperf_workqueue); + sock_release(rxperf_socket); +} + +/* + * Log remote abort codes that indicate that we have a protocol disagreement + * with the server. + */ +static void rxperf_log_error(struct rxperf_call *call, s32 remote_abort) +{ + static int max = 0; + const char *msg; + int m; + + switch (remote_abort) { + case RX_EOF: msg = "unexpected EOF"; break; + case RXGEN_CC_MARSHAL: msg = "client marshalling"; break; + case RXGEN_CC_UNMARSHAL: msg = "client unmarshalling"; break; + case RXGEN_SS_MARSHAL: msg = "server marshalling"; break; + case RXGEN_SS_UNMARSHAL: msg = "server unmarshalling"; break; + case RXGEN_DECODE: msg = "opcode decode"; break; + case RXGEN_SS_XDRFREE: msg = "server XDR cleanup"; break; + case RXGEN_CC_XDRFREE: msg = "client XDR cleanup"; break; + case -32: msg = "insufficient data"; break; + default: + return; + } + + m = max; + if (m < 3) { + max = m + 1; + pr_info("Peer reported %s failure on %s\n", msg, call->type); + } +} + +/* + * deliver messages to a call + */ +static void rxperf_deliver_to_call(struct work_struct *work) +{ + struct rxperf_call *call = container_of(work, struct rxperf_call, work); + enum rxperf_call_state state; + u32 abort_code, remote_abort = 0; + int ret; + + if (call->state == RXPERF_CALL_COMPLETE) + return; + + while (state = call->state, + state == RXPERF_CALL_SV_AWAIT_PARAMS || + state == RXPERF_CALL_SV_AWAIT_REQUEST || + state == RXPERF_CALL_SV_AWAIT_ACK + ) { + if (state == RXPERF_CALL_SV_AWAIT_ACK) { + if (!rxrpc_kernel_check_life(rxperf_socket, call->rxcall)) + goto call_complete; + return; + } + + ret = call->deliver(call); + if (ret == 0) + ret = rxperf_process_call(call); + + switch (ret) { + case 0: + continue; + case -EINPROGRESS: + case -EAGAIN: + return; + case -ECONNABORTED: + rxperf_log_error(call, call->abort_code); + goto call_complete; + case -EOPNOTSUPP: + abort_code = RXGEN_OPCODE; + rxrpc_kernel_abort_call(rxperf_socket, call->rxcall, + abort_code, ret, "GOP"); + goto call_complete; + case -ENOTSUPP: + abort_code = RX_USER_ABORT; + rxrpc_kernel_abort_call(rxperf_socket, call->rxcall, + abort_code, ret, "GUA"); + goto call_complete; + case -EIO: + pr_err("Call %u in bad state %u\n", + call->debug_id, call->state); + fallthrough; + case -ENODATA: + case -EBADMSG: + case -EMSGSIZE: + case -ENOMEM: + case -EFAULT: + rxrpc_kernel_abort_call(rxperf_socket, call->rxcall, + RXGEN_SS_UNMARSHAL, ret, "GUM"); + goto call_complete; + default: + rxrpc_kernel_abort_call(rxperf_socket, call->rxcall, + RX_CALL_DEAD, ret, "GER"); + goto call_complete; + } + } + +call_complete: + rxperf_set_call_complete(call, ret, remote_abort); + /* The call may have been requeued */ + rxrpc_kernel_end_call(rxperf_socket, call->rxcall); + cancel_work(&call->work); + kfree(call); +} + +/* + * Extract a piece of data from the received data socket buffers. + */ +static int rxperf_extract_data(struct rxperf_call *call, bool want_more) +{ + u32 remote_abort = 0; + int ret; + + ret = rxrpc_kernel_recv_data(rxperf_socket, call->rxcall, &call->iter, + &call->iov_len, want_more, &remote_abort, + &call->service_id); + pr_debug("Extract i=%zu l=%zu m=%u ret=%d\n", + iov_iter_count(&call->iter), call->iov_len, want_more, ret); + if (ret == 0 || ret == -EAGAIN) + return ret; + + if (ret == 1) { + switch (call->state) { + case RXPERF_CALL_SV_AWAIT_REQUEST: + rxperf_set_call_state(call, RXPERF_CALL_SV_REPLYING); + break; + case RXPERF_CALL_COMPLETE: + pr_debug("premature completion %d", call->error); + return call->error; + default: + break; + } + return 0; + } + + rxperf_set_call_complete(call, ret, remote_abort); + return ret; +} + +/* + * Grab the operation ID from an incoming manager call. + */ +static int rxperf_deliver_param_block(struct rxperf_call *call) +{ + u32 version; + int ret; + + /* Extract the parameter block */ + ret = rxperf_extract_data(call, true); + if (ret < 0) + return ret; + + version = ntohl(call->params.version); + call->operation_id = ntohl(call->params.type); + call->deliver = rxperf_deliver_request; + + if (version != RX_PERF_VERSION) { + pr_info("Version mismatch %x\n", version); + return -ENOTSUPP; + } + + switch (call->operation_id) { + case RX_PERF_SEND: + call->type = "send"; + call->reply_len = 0; + call->iov_len = 4; /* Expect req size */ + break; + case RX_PERF_RECV: + call->type = "recv"; + call->req_len = 0; + call->iov_len = 4; /* Expect reply size */ + break; + case RX_PERF_RPC: + call->type = "rpc"; + call->iov_len = 8; /* Expect req size and reply size */ + break; + case RX_PERF_FILE: + call->type = "file"; + fallthrough; + default: + return -EOPNOTSUPP; + } + + rxperf_set_call_state(call, RXPERF_CALL_SV_AWAIT_REQUEST); + return call->deliver(call); +} + +/* + * Deliver the request data. + */ +static int rxperf_deliver_request(struct rxperf_call *call) +{ + int ret; + + switch (call->unmarshal) { + case 0: + call->kvec[0].iov_len = call->iov_len; + call->kvec[0].iov_base = call->tmp; + iov_iter_kvec(&call->iter, READ, call->kvec, 1, call->iov_len); + call->unmarshal++; + fallthrough; + case 1: + ret = rxperf_extract_data(call, true); + if (ret < 0) + return ret; + + switch (call->operation_id) { + case RX_PERF_SEND: + call->type = "send"; + call->req_len = ntohl(call->tmp[0]); + call->reply_len = 0; + break; + case RX_PERF_RECV: + call->type = "recv"; + call->req_len = 0; + call->reply_len = ntohl(call->tmp[0]); + break; + case RX_PERF_RPC: + call->type = "rpc"; + call->req_len = ntohl(call->tmp[0]); + call->reply_len = ntohl(call->tmp[1]); + break; + default: + pr_info("Can't parse extra params\n"); + return -EIO; + } + + pr_debug("CALL op=%s rq=%zx rp=%zx\n", + call->type, call->req_len, call->reply_len); + + call->iov_len = call->req_len; + iov_iter_discard(&call->iter, READ, call->req_len); + call->unmarshal++; + fallthrough; + case 2: + ret = rxperf_extract_data(call, false); + if (ret < 0) + return ret; + call->unmarshal++; + fallthrough; + default: + return 0; + } +} + +/* + * Process a call for which we've received the request. + */ +static int rxperf_process_call(struct rxperf_call *call) +{ + struct msghdr msg = {}; + struct bio_vec bv[1]; + struct kvec iov[1]; + ssize_t n; + size_t reply_len = call->reply_len, len; + + rxrpc_kernel_set_tx_length(rxperf_socket, call->rxcall, + reply_len + sizeof(rxperf_magic_cookie)); + + while (reply_len > 0) { + len = min_t(size_t, reply_len, PAGE_SIZE); + bv[0].bv_page = ZERO_PAGE(0); + bv[0].bv_offset = 0; + bv[0].bv_len = len; + iov_iter_bvec(&msg.msg_iter, WRITE, bv, 1, len); + msg.msg_flags = MSG_MORE; + n = rxrpc_kernel_send_data(rxperf_socket, call->rxcall, &msg, + len, rxperf_notify_end_reply_tx); + if (n < 0) + return n; + if (n == 0) + return -EIO; + reply_len -= n; + } + + len = sizeof(rxperf_magic_cookie); + iov[0].iov_base = (void *)rxperf_magic_cookie; + iov[0].iov_len = len; + iov_iter_kvec(&msg.msg_iter, WRITE, iov, 1, len); + msg.msg_flags = 0; + n = rxrpc_kernel_send_data(rxperf_socket, call->rxcall, &msg, len, + rxperf_notify_end_reply_tx); + if (n >= 0) + return 0; /* Success */ + + if (n == -ENOMEM) + rxrpc_kernel_abort_call(rxperf_socket, call->rxcall, + RXGEN_SS_MARSHAL, -ENOMEM, "GOM"); + return n; +} + +/* + * Add a key to the security keyring. + */ +static int rxperf_add_key(struct key *keyring) +{ + key_ref_t kref; + int ret; + + kref = key_create_or_update(make_key_ref(keyring, true), + "rxrpc_s", + __stringify(RX_PERF_SERVICE) ":2", + secret, + sizeof(secret), + KEY_POS_VIEW | KEY_POS_READ | KEY_POS_SEARCH + | KEY_USR_VIEW, + KEY_ALLOC_NOT_IN_QUOTA); + + if (IS_ERR(kref)) { + pr_err("Can't allocate rxperf server key: %ld\n", PTR_ERR(kref)); + return PTR_ERR(kref); + } + + ret = key_link(keyring, key_ref_to_ptr(kref)); + if (ret < 0) + pr_err("Can't link rxperf server key: %d\n", ret); + key_ref_put(kref); + return ret; +} + +/* + * Initialise the rxperf server. + */ +static int __init rxperf_init(void) +{ + struct key *keyring; + int ret = -ENOMEM; + + pr_info("Server registering\n"); + + rxperf_workqueue = alloc_workqueue("rxperf", 0, 0); + if (!rxperf_workqueue) + goto error_workqueue; + + keyring = keyring_alloc("rxperf_server", + GLOBAL_ROOT_UID, GLOBAL_ROOT_GID, current_cred(), + KEY_POS_VIEW | KEY_POS_READ | KEY_POS_SEARCH | + KEY_POS_WRITE | + KEY_USR_VIEW | KEY_USR_READ | KEY_USR_SEARCH | + KEY_USR_WRITE | + KEY_OTH_VIEW | KEY_OTH_READ | KEY_OTH_SEARCH, + KEY_ALLOC_NOT_IN_QUOTA, + NULL, NULL); + if (IS_ERR(keyring)) { + pr_err("Can't allocate rxperf server keyring: %ld\n", + PTR_ERR(keyring)); + goto error_keyring; + } + rxperf_sec_keyring = keyring; + ret = rxperf_add_key(keyring); + if (ret < 0) + goto error_key; + + ret = rxperf_open_socket(); + if (ret < 0) + goto error_socket; + return 0; + +error_socket: +error_key: + key_put(rxperf_sec_keyring); +error_keyring: + destroy_workqueue(rxperf_workqueue); + rcu_barrier(); +error_workqueue: + pr_err("Failed to register: %d\n", ret); + return ret; +} +late_initcall(rxperf_init); /* Must be called after net/ to create socket */ + +static void __exit rxperf_exit(void) +{ + pr_info("Server unregistering.\n"); + + rxperf_close_socket(); + key_put(rxperf_sec_keyring); + destroy_workqueue(rxperf_workqueue); + rcu_barrier(); +} +module_exit(rxperf_exit); + diff --git a/net/rxrpc/server_key.c b/net/rxrpc/server_key.c index ee269e0e6ee8..e51940589ee5 100644 --- a/net/rxrpc/server_key.c +++ b/net/rxrpc/server_key.c @@ -144,3 +144,28 @@ int rxrpc_server_keyring(struct rxrpc_sock *rx, sockptr_t optval, int optlen) _leave(" = 0 [key %x]", key->serial); return 0; } + +/** + * rxrpc_sock_set_security_keyring - Set the security keyring for a kernel service + * @sk: The socket to set the keyring on + * @keyring: The keyring to set + * + * Set the server security keyring on an rxrpc socket. This is used to provide + * the encryption keys for a kernel service. + */ +int rxrpc_sock_set_security_keyring(struct sock *sk, struct key *keyring) +{ + struct rxrpc_sock *rx = rxrpc_sk(sk); + int ret = 0; + + lock_sock(sk); + if (rx->securities) + ret = -EINVAL; + else if (rx->sk.sk_state != RXRPC_UNBOUND) + ret = -EISCONN; + else + rx->securities = key_get(keyring); + release_sock(sk); + return ret; +} +EXPORT_SYMBOL(rxrpc_sock_set_security_keyring); From 49df54a6b2953195243d037682cffb9038f9456a Mon Sep 17 00:00:00 2001 From: David Howells Date: Mon, 28 Nov 2022 09:24:06 +0000 Subject: [PATCH 1402/2002] rxrpc: Fix call leak When retransmitting a packet, rxrpc_resend() shouldn't be attaching a ref to the call to the txbuf as that pins the call and prevents the call from clearing the packet buffer. Signed-off-by: David Howells Fixes: d57a3a151660 ("rxrpc: Save last ACK's SACK table rather than marking txbufs") cc: Marc Dionne cc: linux-afs@lists.infradead.org --- net/rxrpc/call_event.c | 1 - 1 file changed, 1 deletion(-) diff --git a/net/rxrpc/call_event.c b/net/rxrpc/call_event.c index 1e21a708390e..349f3df569ba 100644 --- a/net/rxrpc/call_event.c +++ b/net/rxrpc/call_event.c @@ -198,7 +198,6 @@ static void rxrpc_resend(struct rxrpc_call *call, unsigned long now_j) if (list_empty(&txb->tx_link)) { rxrpc_get_txbuf(txb, rxrpc_txbuf_get_retrans); - rxrpc_get_call(call, rxrpc_call_got_tx); list_add_tail(&txb->tx_link, &retrans_queue); set_bit(RXRPC_TXBUF_RESENT, &txb->flags); } From 2ed83ed2be1b2395f11a95f0fec2b87ed71aebd8 Mon Sep 17 00:00:00 2001 From: David Howells Date: Thu, 17 Nov 2022 10:04:15 +0000 Subject: [PATCH 1403/2002] rxrpc: Remove decl for rxrpc_kernel_call_is_complete() rxrpc_kernel_call_is_complete() has been removed, so remove its declaration too. Signed-off-by: David Howells cc: Marc Dionne cc: linux-afs@lists.infradead.org --- include/net/af_rxrpc.h | 1 - 1 file changed, 1 deletion(-) diff --git a/include/net/af_rxrpc.h b/include/net/af_rxrpc.h index dc033f08191e..d5a5ae926380 100644 --- a/include/net/af_rxrpc.h +++ b/include/net/af_rxrpc.h @@ -66,7 +66,6 @@ int rxrpc_kernel_charge_accept(struct socket *, rxrpc_notify_rx_t, void rxrpc_kernel_set_tx_length(struct socket *, struct rxrpc_call *, s64); bool rxrpc_kernel_check_life(const struct socket *, const struct rxrpc_call *); u32 rxrpc_kernel_get_epoch(struct socket *, struct rxrpc_call *); -bool rxrpc_kernel_call_is_complete(struct rxrpc_call *); void rxrpc_kernel_set_max_life(struct socket *, struct rxrpc_call *, unsigned long); From 30efa3ce109d9e852a1a7bb9be19a414e633b1f0 Mon Sep 17 00:00:00 2001 From: David Howells Date: Fri, 11 Nov 2022 09:41:34 +0000 Subject: [PATCH 1404/2002] rxrpc: Remove handling of duplicate packets in recvmsg_queue We should not now see duplicate packets in the recvmsg_queue. At one point, jumbo packets that overlapped with already queued data would be added to the queue and dealt with in recvmsg rather than in the softirq input code, but now jumbo packets are split/cloned before being processed by the input code and the subpackets can be discarded individually. So remove the recvmsg-side code for handling this. Signed-off-by: David Howells cc: Marc Dionne cc: linux-afs@lists.infradead.org --- net/rxrpc/recvmsg.c | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/net/rxrpc/recvmsg.c b/net/rxrpc/recvmsg.c index efb85f983657..134122f5961a 100644 --- a/net/rxrpc/recvmsg.c +++ b/net/rxrpc/recvmsg.c @@ -228,7 +228,6 @@ static void rxrpc_rotate_rx_window(struct rxrpc_call *call) _enter("%d", call->debug_id); -further_rotation: skb = skb_dequeue(&call->recvmsg_queue); rxrpc_see_skb(skb, rxrpc_skb_rotated); @@ -250,17 +249,6 @@ further_rotation: return; } - /* The next packet on the queue might entirely overlap with the one we - * just consumed; if so, rotate that away also. - */ - skb = skb_peek(&call->recvmsg_queue); - if (skb) { - sp = rxrpc_skb(skb); - if (sp->hdr.seq != call->rx_consumed && - after_eq(call->rx_consumed, sp->hdr.seq)) - goto further_rotation; - } - /* Check to see if there's an ACK that needs sending. */ acked = atomic_add_return(call->rx_consumed - old_consumed, &call->ackr_nr_consumed); @@ -318,11 +306,6 @@ static int rxrpc_recvmsg_data(struct socket *sock, struct rxrpc_call *call, sp = rxrpc_skb(skb); seq = sp->hdr.seq; - if (after_eq(call->rx_consumed, seq)) { - kdebug("obsolete %x %x", call->rx_consumed, seq); - goto skip_obsolete; - } - if (!(flags & MSG_PEEK)) trace_rxrpc_receive(call, rxrpc_receive_front, sp->hdr.serial, seq); @@ -373,7 +356,6 @@ static int rxrpc_recvmsg_data(struct socket *sock, struct rxrpc_call *call, break; } - skip_obsolete: /* The whole packet has been transferred. */ if (sp->hdr.flags & RXRPC_LAST_PACKET) ret = 1; From 2ebdb26e6abd2a773ab5f009ac38a6de973a2bcf Mon Sep 17 00:00:00 2001 From: David Howells Date: Thu, 20 Oct 2022 11:51:06 +0100 Subject: [PATCH 1405/2002] rxrpc: Remove the [k_]proto() debugging macros Remove the kproto() and _proto() debugging macros in preference to using tracepoints for this. Signed-off-by: David Howells cc: Marc Dionne cc: linux-afs@lists.infradead.org --- include/trace/events/rxrpc.h | 60 ++++++++++++++++++++++++++++++++++++ net/rxrpc/ar-internal.h | 10 ------ net/rxrpc/conn_event.c | 4 --- net/rxrpc/input.c | 17 ---------- net/rxrpc/local_event.c | 3 -- net/rxrpc/output.c | 2 -- net/rxrpc/peer_event.c | 4 --- net/rxrpc/rxkad.c | 9 ++---- 8 files changed, 63 insertions(+), 46 deletions(-) diff --git a/include/trace/events/rxrpc.h b/include/trace/events/rxrpc.h index b9886d1df825..2b77f9a75bf7 100644 --- a/include/trace/events/rxrpc.h +++ b/include/trace/events/rxrpc.h @@ -733,6 +733,66 @@ TRACE_EVENT(rxrpc_rx_abort, __entry->abort_code) ); +TRACE_EVENT(rxrpc_rx_challenge, + TP_PROTO(struct rxrpc_connection *conn, rxrpc_serial_t serial, + u32 version, u32 nonce, u32 min_level), + + TP_ARGS(conn, serial, version, nonce, min_level), + + TP_STRUCT__entry( + __field(unsigned int, conn ) + __field(rxrpc_serial_t, serial ) + __field(u32, version ) + __field(u32, nonce ) + __field(u32, min_level ) + ), + + TP_fast_assign( + __entry->conn = conn->debug_id; + __entry->serial = serial; + __entry->version = version; + __entry->nonce = nonce; + __entry->min_level = min_level; + ), + + TP_printk("C=%08x CHALLENGE %08x v=%x n=%x ml=%x", + __entry->conn, + __entry->serial, + __entry->version, + __entry->nonce, + __entry->min_level) + ); + +TRACE_EVENT(rxrpc_rx_response, + TP_PROTO(struct rxrpc_connection *conn, rxrpc_serial_t serial, + u32 version, u32 kvno, u32 ticket_len), + + TP_ARGS(conn, serial, version, kvno, ticket_len), + + TP_STRUCT__entry( + __field(unsigned int, conn ) + __field(rxrpc_serial_t, serial ) + __field(u32, version ) + __field(u32, kvno ) + __field(u32, ticket_len ) + ), + + TP_fast_assign( + __entry->conn = conn->debug_id; + __entry->serial = serial; + __entry->version = version; + __entry->kvno = kvno; + __entry->ticket_len = ticket_len; + ), + + TP_printk("C=%08x RESPONSE %08x v=%x kvno=%x tl=%x", + __entry->conn, + __entry->serial, + __entry->version, + __entry->kvno, + __entry->ticket_len) + ); + TRACE_EVENT(rxrpc_rx_rwind_change, TP_PROTO(struct rxrpc_call *call, rxrpc_serial_t serial, u32 rwind, bool wake), diff --git a/net/rxrpc/ar-internal.h b/net/rxrpc/ar-internal.h index f5c538ce3e23..a3a29390e12b 100644 --- a/net/rxrpc/ar-internal.h +++ b/net/rxrpc/ar-internal.h @@ -1190,7 +1190,6 @@ extern unsigned int rxrpc_debug; #define kenter(FMT,...) dbgprintk("==> %s("FMT")",__func__ ,##__VA_ARGS__) #define kleave(FMT,...) dbgprintk("<== %s()"FMT"",__func__ ,##__VA_ARGS__) #define kdebug(FMT,...) dbgprintk(" "FMT ,##__VA_ARGS__) -#define kproto(FMT,...) dbgprintk("### "FMT ,##__VA_ARGS__) #define knet(FMT,...) dbgprintk("@@@ "FMT ,##__VA_ARGS__) @@ -1198,14 +1197,12 @@ extern unsigned int rxrpc_debug; #define _enter(FMT,...) kenter(FMT,##__VA_ARGS__) #define _leave(FMT,...) kleave(FMT,##__VA_ARGS__) #define _debug(FMT,...) kdebug(FMT,##__VA_ARGS__) -#define _proto(FMT,...) kproto(FMT,##__VA_ARGS__) #define _net(FMT,...) knet(FMT,##__VA_ARGS__) #elif defined(CONFIG_AF_RXRPC_DEBUG) #define RXRPC_DEBUG_KENTER 0x01 #define RXRPC_DEBUG_KLEAVE 0x02 #define RXRPC_DEBUG_KDEBUG 0x04 -#define RXRPC_DEBUG_KPROTO 0x08 #define RXRPC_DEBUG_KNET 0x10 #define _enter(FMT,...) \ @@ -1226,12 +1223,6 @@ do { \ kdebug(FMT,##__VA_ARGS__); \ } while (0) -#define _proto(FMT,...) \ -do { \ - if (unlikely(rxrpc_debug & RXRPC_DEBUG_KPROTO)) \ - kproto(FMT,##__VA_ARGS__); \ -} while (0) - #define _net(FMT,...) \ do { \ if (unlikely(rxrpc_debug & RXRPC_DEBUG_KNET)) \ @@ -1242,7 +1233,6 @@ do { \ #define _enter(FMT,...) no_printk("==> %s("FMT")",__func__ ,##__VA_ARGS__) #define _leave(FMT,...) no_printk("<== %s()"FMT"",__func__ ,##__VA_ARGS__) #define _debug(FMT,...) no_printk(" "FMT ,##__VA_ARGS__) -#define _proto(FMT,...) no_printk("### "FMT ,##__VA_ARGS__) #define _net(FMT,...) no_printk("@@@ "FMT ,##__VA_ARGS__) #endif diff --git a/net/rxrpc/conn_event.c b/net/rxrpc/conn_event.c index aab069701398..d5549cbfc71b 100644 --- a/net/rxrpc/conn_event.c +++ b/net/rxrpc/conn_event.c @@ -122,14 +122,12 @@ static void rxrpc_conn_retransmit_call(struct rxrpc_connection *conn, switch (chan->last_type) { case RXRPC_PACKET_TYPE_ABORT: - _proto("Tx ABORT %%%u { %d } [re]", serial, conn->abort_code); break; case RXRPC_PACKET_TYPE_ACK: trace_rxrpc_tx_ack(chan->call_debug_id, serial, ntohl(pkt.ack.firstPacket), ntohl(pkt.ack.serial), pkt.ack.reason, 0); - _proto("Tx ACK %%%u [re]", serial); break; } @@ -242,7 +240,6 @@ static int rxrpc_abort_connection(struct rxrpc_connection *conn, serial = atomic_inc_return(&conn->serial); rxrpc_abort_calls(conn, RXRPC_CALL_LOCALLY_ABORTED, serial); whdr.serial = htonl(serial); - _proto("Tx CONN ABORT %%%u { %d }", serial, conn->abort_code); ret = kernel_sendmsg(conn->params.local->socket, &msg, iov, 2, len); if (ret < 0) { @@ -315,7 +312,6 @@ static int rxrpc_process_event(struct rxrpc_connection *conn, return -EPROTO; } abort_code = ntohl(wtmp); - _proto("Rx ABORT %%%u { ac=%d }", sp->hdr.serial, abort_code); conn->error = -ECONNABORTED; conn->abort_code = abort_code; diff --git a/net/rxrpc/input.c b/net/rxrpc/input.c index bdf70b81addc..646ee61af40e 100644 --- a/net/rxrpc/input.c +++ b/net/rxrpc/input.c @@ -551,9 +551,6 @@ static void rxrpc_input_data(struct rxrpc_call *call, struct sk_buff *skb) atomic64_read(&call->ackr_window), call->rx_highest_seq, skb->len, seq0); - _proto("Rx DATA %%%u { #%u f=%02x }", - sp->hdr.serial, seq0, sp->hdr.flags); - state = READ_ONCE(call->state); if (state >= RXRPC_CALL_COMPLETE) { rxrpc_free_skb(skb, rxrpc_skb_freed); @@ -708,11 +705,6 @@ static void rxrpc_input_ackinfo(struct rxrpc_call *call, struct sk_buff *skb, bool wake = false; u32 rwind = ntohl(ackinfo->rwind); - _proto("Rx ACK %%%u Info { rx=%u max=%u rwin=%u jm=%u }", - sp->hdr.serial, - ntohl(ackinfo->rxMTU), ntohl(ackinfo->maxMTU), - rwind, ntohl(ackinfo->jumbo_max)); - if (rwind > RXRPC_TX_MAX_WINDOW) rwind = RXRPC_TX_MAX_WINDOW; if (call->tx_winsize != rwind) { @@ -855,7 +847,6 @@ static void rxrpc_input_ack(struct rxrpc_call *call, struct sk_buff *skb) } if (ack.reason == RXRPC_ACK_PING) { - _proto("Rx ACK %%%u PING Request", ack_serial); rxrpc_send_ACK(call, RXRPC_ACK_PING_RESPONSE, ack_serial, rxrpc_propose_ack_respond_to_ping); } else if (sp->hdr.flags & RXRPC_REQUEST_ACK) { @@ -1014,9 +1005,6 @@ out_not_locked: static void rxrpc_input_ackall(struct rxrpc_call *call, struct sk_buff *skb) { struct rxrpc_ack_summary summary = { 0 }; - struct rxrpc_skb_priv *sp = rxrpc_skb(skb); - - _proto("Rx ACKALL %%%u", sp->hdr.serial); spin_lock(&call->input_lock); @@ -1044,8 +1032,6 @@ static void rxrpc_input_abort(struct rxrpc_call *call, struct sk_buff *skb) trace_rxrpc_rx_abort(call, sp->hdr.serial, abort_code); - _proto("Rx ABORT %%%u { %x }", sp->hdr.serial, abort_code); - rxrpc_set_call_completion(call, RXRPC_CALL_REMOTELY_ABORTED, abort_code, -ECONNABORTED); } @@ -1081,8 +1067,6 @@ static void rxrpc_input_call_packet(struct rxrpc_call *call, goto no_free; case RXRPC_PACKET_TYPE_BUSY: - _proto("Rx BUSY %%%u", sp->hdr.serial); - /* Just ignore BUSY packets from the server; the retry and * lifespan timers will take care of business. BUSY packets * from the client don't make sense. @@ -1325,7 +1309,6 @@ int rxrpc_input_packet(struct sock *udp_sk, struct sk_buff *skb) goto discard; default: - _proto("Rx Bad Packet Type %u", sp->hdr.type); goto bad_message; } diff --git a/net/rxrpc/local_event.c b/net/rxrpc/local_event.c index 19e929c7c38b..f23a3fbabbda 100644 --- a/net/rxrpc/local_event.c +++ b/net/rxrpc/local_event.c @@ -63,8 +63,6 @@ static void rxrpc_send_version_request(struct rxrpc_local *local, len = iov[0].iov_len + iov[1].iov_len; - _proto("Tx VERSION (reply)"); - ret = kernel_sendmsg(local->socket, &msg, iov, 2, len); if (ret < 0) trace_rxrpc_tx_fail(local->debug_id, 0, ret, @@ -98,7 +96,6 @@ void rxrpc_process_local_events(struct rxrpc_local *local) if (skb_copy_bits(skb, sizeof(struct rxrpc_wire_header), &v, 1) < 0) return; - _proto("Rx VERSION { %02x }", v); if (v == 0) rxrpc_send_version_request(local, &sp->hdr, skb); break; diff --git a/net/rxrpc/output.c b/net/rxrpc/output.c index c5eed0e83e47..635acf3dbd77 100644 --- a/net/rxrpc/output.c +++ b/net/rxrpc/output.c @@ -701,8 +701,6 @@ void rxrpc_send_keepalive(struct rxrpc_peer *peer) len = iov[0].iov_len + iov[1].iov_len; - _proto("Tx VERSION (keepalive)"); - iov_iter_kvec(&msg.msg_iter, WRITE, iov, 2, len); ret = do_udp_sendmsg(peer->local->socket, &msg, len); if (ret < 0) diff --git a/net/rxrpc/peer_event.c b/net/rxrpc/peer_event.c index cda3890657a9..be781c156e89 100644 --- a/net/rxrpc/peer_event.c +++ b/net/rxrpc/peer_event.c @@ -253,15 +253,12 @@ static void rxrpc_store_error(struct rxrpc_peer *peer, break; default: - _proto("Rx Received ICMP error { type=%u code=%u }", - ee->ee_type, ee->ee_code); break; } break; case SO_EE_ORIGIN_NONE: case SO_EE_ORIGIN_LOCAL: - _proto("Rx Received local error { error=%d }", err); compl = RXRPC_CALL_LOCAL_ERROR; break; @@ -270,7 +267,6 @@ static void rxrpc_store_error(struct rxrpc_peer *peer, err = EHOSTUNREACH; fallthrough; default: - _proto("Rx Received error report { orig=%u }", ee->ee_origin); break; } diff --git a/net/rxrpc/rxkad.c b/net/rxrpc/rxkad.c index 110a5550c0a6..36cf40442a7e 100644 --- a/net/rxrpc/rxkad.c +++ b/net/rxrpc/rxkad.c @@ -704,7 +704,6 @@ static int rxkad_issue_challenge(struct rxrpc_connection *conn) serial = atomic_inc_return(&conn->serial); whdr.serial = htonl(serial); - _proto("Tx CHALLENGE %%%u", serial); ret = kernel_sendmsg(conn->params.local->socket, &msg, iov, 2, len); if (ret < 0) { @@ -762,7 +761,6 @@ static int rxkad_send_response(struct rxrpc_connection *conn, serial = atomic_inc_return(&conn->serial); whdr.serial = htonl(serial); - _proto("Tx RESPONSE %%%u", serial); ret = kernel_sendmsg(conn->params.local->socket, &msg, iov, 3, len); if (ret < 0) { @@ -856,8 +854,7 @@ static int rxkad_respond_to_challenge(struct rxrpc_connection *conn, nonce = ntohl(challenge.nonce); min_level = ntohl(challenge.min_level); - _proto("Rx CHALLENGE %%%u { v=%u n=%u ml=%u }", - sp->hdr.serial, version, nonce, min_level); + trace_rxrpc_rx_challenge(conn, sp->hdr.serial, version, nonce, min_level); eproto = tracepoint_string("chall_ver"); abort_code = RXKADINCONSISTENCY; @@ -1139,8 +1136,8 @@ static int rxkad_verify_response(struct rxrpc_connection *conn, version = ntohl(response->version); ticket_len = ntohl(response->ticket_len); kvno = ntohl(response->kvno); - _proto("Rx RESPONSE %%%u { v=%u kv=%u tl=%u }", - sp->hdr.serial, version, kvno, ticket_len); + + trace_rxrpc_rx_response(conn, sp->hdr.serial, version, kvno, ticket_len); eproto = tracepoint_string("rxkad_rsp_ver"); abort_code = RXKADINCONSISTENCY; From e969c92ce597baf6aeff3f619d6c082d736575e0 Mon Sep 17 00:00:00 2001 From: David Howells Date: Thu, 20 Oct 2022 12:04:20 +0100 Subject: [PATCH 1406/2002] rxrpc: Remove the [_k]net() debugging macros Remove the _net() and knet() debugging macros in favour of tracepoints. Signed-off-by: David Howells cc: Marc Dionne cc: linux-afs@lists.infradead.org --- net/rxrpc/ar-internal.h | 10 --------- net/rxrpc/call_object.c | 6 ----- net/rxrpc/conn_client.c | 2 -- net/rxrpc/conn_object.c | 2 -- net/rxrpc/conn_service.c | 2 -- net/rxrpc/input.c | 1 - net/rxrpc/local_object.c | 8 ------- net/rxrpc/peer_event.c | 48 ++-------------------------------------- net/rxrpc/peer_object.c | 6 +---- 9 files changed, 3 insertions(+), 82 deletions(-) diff --git a/net/rxrpc/ar-internal.h b/net/rxrpc/ar-internal.h index a3a29390e12b..36ece1efb1d4 100644 --- a/net/rxrpc/ar-internal.h +++ b/net/rxrpc/ar-internal.h @@ -1190,20 +1190,17 @@ extern unsigned int rxrpc_debug; #define kenter(FMT,...) dbgprintk("==> %s("FMT")",__func__ ,##__VA_ARGS__) #define kleave(FMT,...) dbgprintk("<== %s()"FMT"",__func__ ,##__VA_ARGS__) #define kdebug(FMT,...) dbgprintk(" "FMT ,##__VA_ARGS__) -#define knet(FMT,...) dbgprintk("@@@ "FMT ,##__VA_ARGS__) #if defined(__KDEBUG) #define _enter(FMT,...) kenter(FMT,##__VA_ARGS__) #define _leave(FMT,...) kleave(FMT,##__VA_ARGS__) #define _debug(FMT,...) kdebug(FMT,##__VA_ARGS__) -#define _net(FMT,...) knet(FMT,##__VA_ARGS__) #elif defined(CONFIG_AF_RXRPC_DEBUG) #define RXRPC_DEBUG_KENTER 0x01 #define RXRPC_DEBUG_KLEAVE 0x02 #define RXRPC_DEBUG_KDEBUG 0x04 -#define RXRPC_DEBUG_KNET 0x10 #define _enter(FMT,...) \ do { \ @@ -1223,17 +1220,10 @@ do { \ kdebug(FMT,##__VA_ARGS__); \ } while (0) -#define _net(FMT,...) \ -do { \ - if (unlikely(rxrpc_debug & RXRPC_DEBUG_KNET)) \ - knet(FMT,##__VA_ARGS__); \ -} while (0) - #else #define _enter(FMT,...) no_printk("==> %s("FMT")",__func__ ,##__VA_ARGS__) #define _leave(FMT,...) no_printk("<== %s()"FMT"",__func__ ,##__VA_ARGS__) #define _debug(FMT,...) no_printk(" "FMT ,##__VA_ARGS__) -#define _net(FMT,...) no_printk("@@@ "FMT ,##__VA_ARGS__) #endif /* diff --git a/net/rxrpc/call_object.c b/net/rxrpc/call_object.c index 1befe22cd301..e36a317b2e9a 100644 --- a/net/rxrpc/call_object.c +++ b/net/rxrpc/call_object.c @@ -349,8 +349,6 @@ struct rxrpc_call *rxrpc_new_client_call(struct rxrpc_sock *rx, rxrpc_start_call_timer(call); - _net("CALL new %d on CONN %d", call->debug_id, call->conn->debug_id); - _leave(" = %p [new]", call); return call; @@ -423,8 +421,6 @@ void rxrpc_incoming_call(struct rxrpc_sock *rx, hlist_add_head_rcu(&call->error_link, &conn->params.peer->error_targets); spin_unlock(&conn->params.peer->lock); - _net("CALL incoming %d on CONN %d", call->debug_id, call->conn->debug_id); - rxrpc_start_call_timer(call); _leave(""); } @@ -669,8 +665,6 @@ void rxrpc_cleanup_call(struct rxrpc_call *call) { struct rxrpc_txbuf *txb; - _net("DESTROY CALL %d", call->debug_id); - memset(&call->sock_node, 0xcd, sizeof(call->sock_node)); ASSERTCMP(call->state, ==, RXRPC_CALL_COMPLETE); diff --git a/net/rxrpc/conn_client.c b/net/rxrpc/conn_client.c index f11c97e28d2a..2b76fbffd4dd 100644 --- a/net/rxrpc/conn_client.c +++ b/net/rxrpc/conn_client.c @@ -541,8 +541,6 @@ static void rxrpc_activate_one_channel(struct rxrpc_connection *conn, call->service_id = conn->service_id; trace_rxrpc_connect_call(call); - _net("CONNECT call %08x:%08x as call %d on conn %d", - call->cid, call->call_id, call->debug_id, conn->debug_id); write_lock_bh(&call->state_lock); call->state = RXRPC_CALL_CLIENT_SEND_REQUEST; diff --git a/net/rxrpc/conn_object.c b/net/rxrpc/conn_object.c index 156bd26daf74..d5d15389406f 100644 --- a/net/rxrpc/conn_object.c +++ b/net/rxrpc/conn_object.c @@ -356,8 +356,6 @@ static void rxrpc_destroy_connection(struct rcu_head *rcu) ASSERTCMP(refcount_read(&conn->ref), ==, 0); - _net("DESTROY CONN %d", conn->debug_id); - del_timer_sync(&conn->timer); rxrpc_purge_queue(&conn->rx_queue); diff --git a/net/rxrpc/conn_service.c b/net/rxrpc/conn_service.c index 6e6aa02c6f9e..75f903099eb0 100644 --- a/net/rxrpc/conn_service.c +++ b/net/rxrpc/conn_service.c @@ -184,8 +184,6 @@ void rxrpc_new_incoming_connection(struct rxrpc_sock *rx, /* Make the connection a target for incoming packets. */ rxrpc_publish_service_conn(conn->params.peer, conn); - - _net("CONNECTION new %d {%x}", conn->debug_id, conn->proto.cid); } /* diff --git a/net/rxrpc/input.c b/net/rxrpc/input.c index 646ee61af40e..e2461f29d765 100644 --- a/net/rxrpc/input.c +++ b/net/rxrpc/input.c @@ -725,7 +725,6 @@ static void rxrpc_input_ackinfo(struct rxrpc_call *call, struct sk_buff *skb, peer->maxdata = mtu; peer->mtu = mtu + peer->hdrsize; spin_unlock_bh(&peer->lock); - _net("Net MTU %u (maxdata %u)", peer->mtu, peer->maxdata); } if (wake) diff --git a/net/rxrpc/local_object.c b/net/rxrpc/local_object.c index a943fdf91e24..11080c335d42 100644 --- a/net/rxrpc/local_object.c +++ b/net/rxrpc/local_object.c @@ -198,7 +198,6 @@ struct rxrpc_local *rxrpc_lookup_local(struct net *net, struct rxrpc_local *local; struct rxrpc_net *rxnet = rxrpc_net(net); struct hlist_node *cursor; - const char *age; long diff; int ret; @@ -232,7 +231,6 @@ struct rxrpc_local *rxrpc_lookup_local(struct net *net, if (!rxrpc_use_local(local)) break; - age = "old"; goto found; } @@ -250,14 +248,9 @@ struct rxrpc_local *rxrpc_lookup_local(struct net *net, } else { hlist_add_head_rcu(&local->link, &rxnet->local_endpoints); } - age = "new"; found: mutex_unlock(&rxnet->local_mutex); - - _net("LOCAL %s %d {%pISp}", - age, local->debug_id, &local->srx.transport); - _leave(" = %p", local); return local; @@ -467,7 +460,6 @@ static void rxrpc_local_rcu(struct rcu_head *rcu) ASSERT(!work_pending(&local->processor)); - _net("DESTROY LOCAL %d", local->debug_id); kfree(local); _leave(""); } diff --git a/net/rxrpc/peer_event.c b/net/rxrpc/peer_event.c index be781c156e89..ad4d1769e02b 100644 --- a/net/rxrpc/peer_event.c +++ b/net/rxrpc/peer_event.c @@ -48,13 +48,11 @@ static struct rxrpc_peer *rxrpc_lookup_peer_local_rcu(struct rxrpc_local *local, srx->transport.sin.sin_port = serr->port; switch (serr->ee.ee_origin) { case SO_EE_ORIGIN_ICMP: - _net("Rx ICMP"); memcpy(&srx->transport.sin.sin_addr, skb_network_header(skb) + serr->addr_offset, sizeof(struct in_addr)); break; case SO_EE_ORIGIN_ICMP6: - _net("Rx ICMP6 on v4 sock"); memcpy(&srx->transport.sin.sin_addr, skb_network_header(skb) + serr->addr_offset + 12, sizeof(struct in_addr)); @@ -70,14 +68,12 @@ static struct rxrpc_peer *rxrpc_lookup_peer_local_rcu(struct rxrpc_local *local, case AF_INET6: switch (serr->ee.ee_origin) { case SO_EE_ORIGIN_ICMP6: - _net("Rx ICMP6"); srx->transport.sin6.sin6_port = serr->port; memcpy(&srx->transport.sin6.sin6_addr, skb_network_header(skb) + serr->addr_offset, sizeof(struct in6_addr)); break; case SO_EE_ORIGIN_ICMP: - _net("Rx ICMP on v6 sock"); srx->transport_len = sizeof(srx->transport.sin); srx->transport.family = AF_INET; srx->transport.sin.sin_port = serr->port; @@ -106,13 +102,9 @@ static struct rxrpc_peer *rxrpc_lookup_peer_local_rcu(struct rxrpc_local *local, */ static void rxrpc_adjust_mtu(struct rxrpc_peer *peer, unsigned int mtu) { - _net("Rx ICMP Fragmentation Needed (%d)", mtu); - /* wind down the local interface MTU */ - if (mtu > 0 && peer->if_mtu == 65535 && mtu < peer->if_mtu) { + if (mtu > 0 && peer->if_mtu == 65535 && mtu < peer->if_mtu) peer->if_mtu = mtu; - _net("I/F MTU %u", mtu); - } if (mtu == 0) { /* they didn't give us a size, estimate one */ @@ -133,8 +125,6 @@ static void rxrpc_adjust_mtu(struct rxrpc_peer *peer, unsigned int mtu) peer->mtu = mtu; peer->maxdata = peer->mtu - peer->hdrsize; spin_unlock_bh(&peer->lock); - _net("Net MTU %u (maxdata %u)", - peer->mtu, peer->maxdata); } } @@ -222,41 +212,6 @@ static void rxrpc_store_error(struct rxrpc_peer *peer, err = ee->ee_errno; switch (ee->ee_origin) { - case SO_EE_ORIGIN_ICMP: - switch (ee->ee_type) { - case ICMP_DEST_UNREACH: - switch (ee->ee_code) { - case ICMP_NET_UNREACH: - _net("Rx Received ICMP Network Unreachable"); - break; - case ICMP_HOST_UNREACH: - _net("Rx Received ICMP Host Unreachable"); - break; - case ICMP_PORT_UNREACH: - _net("Rx Received ICMP Port Unreachable"); - break; - case ICMP_NET_UNKNOWN: - _net("Rx Received ICMP Unknown Network"); - break; - case ICMP_HOST_UNKNOWN: - _net("Rx Received ICMP Unknown Host"); - break; - default: - _net("Rx Received ICMP DestUnreach code=%u", - ee->ee_code); - break; - } - break; - - case ICMP_TIME_EXCEEDED: - _net("Rx Received ICMP TTL Exceeded"); - break; - - default: - break; - } - break; - case SO_EE_ORIGIN_NONE: case SO_EE_ORIGIN_LOCAL: compl = RXRPC_CALL_LOCAL_ERROR; @@ -266,6 +221,7 @@ static void rxrpc_store_error(struct rxrpc_peer *peer, if (err == EACCES) err = EHOSTUNREACH; fallthrough; + case SO_EE_ORIGIN_ICMP: default: break; } diff --git a/net/rxrpc/peer_object.c b/net/rxrpc/peer_object.c index 041a51225c5f..b3c3c1c344fc 100644 --- a/net/rxrpc/peer_object.c +++ b/net/rxrpc/peer_object.c @@ -138,10 +138,8 @@ struct rxrpc_peer *rxrpc_lookup_peer_rcu(struct rxrpc_local *local, unsigned long hash_key = rxrpc_peer_hash_key(local, srx); peer = __rxrpc_lookup_peer_rcu(local, srx, hash_key); - if (peer) { - _net("PEER %d {%pISp}", peer->debug_id, &peer->srx.transport); + if (peer) _leave(" = %p {u=%d}", peer, refcount_read(&peer->ref)); - } return peer; } @@ -371,8 +369,6 @@ struct rxrpc_peer *rxrpc_lookup_peer(struct rxrpc_sock *rx, peer = candidate; } - _net("PEER %d {%pISp}", peer->debug_id, &peer->srx.transport); - _leave(" = %p {u=%d}", peer, refcount_read(&peer->ref)); return peer; } From 2cc800863c49a1f4be1b10b756c09a878d3a3f00 Mon Sep 17 00:00:00 2001 From: David Howells Date: Wed, 19 Oct 2022 13:49:02 +0100 Subject: [PATCH 1407/2002] rxrpc: Drop rxrpc_conn_parameters from rxrpc_connection and rxrpc_bundle Remove the rxrpc_conn_parameters struct from the rxrpc_connection and rxrpc_bundle structs and emplace the members directly. These are going to get filled in from the rxrpc_call struct in future. Signed-off-by: David Howells cc: Marc Dionne cc: linux-afs@lists.infradead.org --- net/rxrpc/ar-internal.h | 16 ++++++++++-- net/rxrpc/call_accept.c | 6 ++--- net/rxrpc/call_event.c | 2 +- net/rxrpc/call_object.c | 6 ++--- net/rxrpc/conn_client.c | 53 +++++++++++++++++++++++---------------- net/rxrpc/conn_event.c | 26 +++++++++---------- net/rxrpc/conn_object.c | 22 ++++++++-------- net/rxrpc/conn_service.c | 6 ++--- net/rxrpc/input.c | 4 +-- net/rxrpc/key.c | 2 +- net/rxrpc/output.c | 32 ++++++++++++------------ net/rxrpc/proc.c | 6 ++--- net/rxrpc/rxkad.c | 54 ++++++++++++++++++++-------------------- net/rxrpc/security.c | 4 +-- 14 files changed, 131 insertions(+), 108 deletions(-) diff --git a/net/rxrpc/ar-internal.h b/net/rxrpc/ar-internal.h index 36ece1efb1d4..7c48b0163032 100644 --- a/net/rxrpc/ar-internal.h +++ b/net/rxrpc/ar-internal.h @@ -403,12 +403,18 @@ enum rxrpc_conn_proto_state { * RxRPC client connection bundle. */ struct rxrpc_bundle { - struct rxrpc_conn_parameters params; + struct rxrpc_local *local; /* Representation of local endpoint */ + struct rxrpc_peer *peer; /* Remote endpoint */ + struct key *key; /* Security details */ refcount_t ref; atomic_t active; /* Number of active users */ unsigned int debug_id; + u32 security_level; /* Security level selected */ + u16 service_id; /* Service ID for this connection */ bool try_upgrade; /* True if the bundle is attempting upgrade */ bool alloc_conn; /* True if someone's getting a conn */ + bool exclusive; /* T if conn is exclusive */ + bool upgrade; /* T if service ID can be upgraded */ short alloc_error; /* Error from last conn allocation */ spinlock_t channel_lock; struct rb_node local_node; /* Node in local->client_conns */ @@ -424,7 +430,9 @@ struct rxrpc_bundle { */ struct rxrpc_connection { struct rxrpc_conn_proto proto; - struct rxrpc_conn_parameters params; + struct rxrpc_local *local; /* Representation of local endpoint */ + struct rxrpc_peer *peer; /* Remote endpoint */ + struct key *key; /* Security details */ refcount_t ref; struct rcu_head rcu; @@ -471,9 +479,13 @@ struct rxrpc_connection { atomic_t serial; /* packet serial number counter */ unsigned int hi_serial; /* highest serial number received */ u32 service_id; /* Service ID, possibly upgraded */ + u32 security_level; /* Security level selected */ u8 security_ix; /* security type */ u8 out_clientflag; /* RXRPC_CLIENT_INITIATED if we are client */ u8 bundle_shift; /* Index into bundle->avail_chans */ + bool exclusive; /* T if conn is exclusive */ + bool upgrade; /* T if service ID can be upgraded */ + u16 orig_service_id; /* Originally requested service ID */ short error; /* Local error code */ }; diff --git a/net/rxrpc/call_accept.c b/net/rxrpc/call_accept.c index 48790ee77019..4888959e4727 100644 --- a/net/rxrpc/call_accept.c +++ b/net/rxrpc/call_accept.c @@ -305,8 +305,8 @@ static struct rxrpc_call *rxrpc_alloc_incoming_call(struct rxrpc_sock *rx, b->conn_backlog[conn_tail] = NULL; smp_store_release(&b->conn_backlog_tail, (conn_tail + 1) & (RXRPC_BACKLOG_MAX - 1)); - conn->params.local = rxrpc_get_local(local); - conn->params.peer = peer; + conn->local = rxrpc_get_local(local); + conn->peer = peer; rxrpc_see_connection(conn); rxrpc_new_incoming_connection(rx, conn, sec, skb); } else { @@ -323,7 +323,7 @@ static struct rxrpc_call *rxrpc_alloc_incoming_call(struct rxrpc_sock *rx, call->conn = conn; call->security = conn->security; call->security_ix = conn->security_ix; - call->peer = rxrpc_get_peer(conn->params.peer); + call->peer = rxrpc_get_peer(conn->peer); call->cong_ssthresh = call->peer->cong_ssthresh; call->tx_last_sent = ktime_get_real(); return call; diff --git a/net/rxrpc/call_event.c b/net/rxrpc/call_event.c index 349f3df569ba..b17ed37434bd 100644 --- a/net/rxrpc/call_event.c +++ b/net/rxrpc/call_event.c @@ -69,7 +69,7 @@ void rxrpc_propose_delay_ACK(struct rxrpc_call *call, rxrpc_serial_t serial, void rxrpc_send_ACK(struct rxrpc_call *call, u8 ack_reason, rxrpc_serial_t serial, enum rxrpc_propose_ack_trace why) { - struct rxrpc_local *local = call->conn->params.local; + struct rxrpc_local *local = call->conn->local; struct rxrpc_txbuf *txb; if (test_bit(RXRPC_CALL_DISCONNECTED, &call->flags)) diff --git a/net/rxrpc/call_object.c b/net/rxrpc/call_object.c index e36a317b2e9a..59928f0a8fe1 100644 --- a/net/rxrpc/call_object.c +++ b/net/rxrpc/call_object.c @@ -417,9 +417,9 @@ void rxrpc_incoming_call(struct rxrpc_sock *rx, conn->channels[chan].call_id = call->call_id; rcu_assign_pointer(conn->channels[chan].call, call); - spin_lock(&conn->params.peer->lock); - hlist_add_head_rcu(&call->error_link, &conn->params.peer->error_targets); - spin_unlock(&conn->params.peer->lock); + spin_lock(&conn->peer->lock); + hlist_add_head_rcu(&call->error_link, &conn->peer->error_targets); + spin_unlock(&conn->peer->lock); rxrpc_start_call_timer(call); _leave(""); diff --git a/net/rxrpc/conn_client.c b/net/rxrpc/conn_client.c index 2b76fbffd4dd..71404b33623f 100644 --- a/net/rxrpc/conn_client.c +++ b/net/rxrpc/conn_client.c @@ -51,7 +51,7 @@ static void rxrpc_deactivate_bundle(struct rxrpc_bundle *bundle); static int rxrpc_get_client_connection_id(struct rxrpc_connection *conn, gfp_t gfp) { - struct rxrpc_net *rxnet = conn->params.local->rxnet; + struct rxrpc_net *rxnet = conn->local->rxnet; int id; _enter(""); @@ -122,8 +122,13 @@ static struct rxrpc_bundle *rxrpc_alloc_bundle(struct rxrpc_conn_parameters *cp, bundle = kzalloc(sizeof(*bundle), gfp); if (bundle) { - bundle->params = *cp; - rxrpc_get_peer(bundle->params.peer); + bundle->local = cp->local; + bundle->peer = rxrpc_get_peer(cp->peer); + bundle->key = cp->key; + bundle->exclusive = cp->exclusive; + bundle->upgrade = cp->upgrade; + bundle->service_id = cp->service_id; + bundle->security_level = cp->security_level; refcount_set(&bundle->ref, 1); atomic_set(&bundle->active, 1); spin_lock_init(&bundle->channel_lock); @@ -140,7 +145,7 @@ struct rxrpc_bundle *rxrpc_get_bundle(struct rxrpc_bundle *bundle) static void rxrpc_free_bundle(struct rxrpc_bundle *bundle) { - rxrpc_put_peer(bundle->params.peer); + rxrpc_put_peer(bundle->peer); kfree(bundle); } @@ -164,7 +169,7 @@ static struct rxrpc_connection * rxrpc_alloc_client_connection(struct rxrpc_bundle *bundle, gfp_t gfp) { struct rxrpc_connection *conn; - struct rxrpc_net *rxnet = bundle->params.local->rxnet; + struct rxrpc_net *rxnet = bundle->local->rxnet; int ret; _enter(""); @@ -177,10 +182,16 @@ rxrpc_alloc_client_connection(struct rxrpc_bundle *bundle, gfp_t gfp) refcount_set(&conn->ref, 1); conn->bundle = bundle; - conn->params = bundle->params; + conn->local = bundle->local; + conn->peer = bundle->peer; + conn->key = bundle->key; + conn->exclusive = bundle->exclusive; + conn->upgrade = bundle->upgrade; + conn->orig_service_id = bundle->service_id; + conn->security_level = bundle->security_level; conn->out_clientflag = RXRPC_CLIENT_INITIATED; conn->state = RXRPC_CONN_CLIENT; - conn->service_id = conn->params.service_id; + conn->service_id = conn->orig_service_id; ret = rxrpc_get_client_connection_id(conn, gfp); if (ret < 0) @@ -196,9 +207,9 @@ rxrpc_alloc_client_connection(struct rxrpc_bundle *bundle, gfp_t gfp) write_unlock(&rxnet->conn_lock); rxrpc_get_bundle(bundle); - rxrpc_get_peer(conn->params.peer); - rxrpc_get_local(conn->params.local); - key_get(conn->params.key); + rxrpc_get_peer(conn->peer); + rxrpc_get_local(conn->local); + key_get(conn->key); trace_rxrpc_conn(conn->debug_id, rxrpc_conn_new_client, refcount_read(&conn->ref), @@ -228,7 +239,7 @@ static bool rxrpc_may_reuse_conn(struct rxrpc_connection *conn) if (!conn) goto dont_reuse; - rxnet = conn->params.local->rxnet; + rxnet = conn->local->rxnet; if (test_bit(RXRPC_CONN_DONT_REUSE, &conn->flags)) goto dont_reuse; @@ -285,7 +296,7 @@ static struct rxrpc_bundle *rxrpc_look_up_bundle(struct rxrpc_conn_parameters *c while (p) { bundle = rb_entry(p, struct rxrpc_bundle, local_node); -#define cmp(X) ((long)bundle->params.X - (long)cp->X) +#define cmp(X) ((long)bundle->X - (long)cp->X) diff = (cmp(peer) ?: cmp(key) ?: cmp(security_level) ?: @@ -314,7 +325,7 @@ static struct rxrpc_bundle *rxrpc_look_up_bundle(struct rxrpc_conn_parameters *c parent = *pp; bundle = rb_entry(parent, struct rxrpc_bundle, local_node); -#define cmp(X) ((long)bundle->params.X - (long)cp->X) +#define cmp(X) ((long)bundle->X - (long)cp->X) diff = (cmp(peer) ?: cmp(key) ?: cmp(security_level) ?: @@ -532,7 +543,7 @@ static void rxrpc_activate_one_channel(struct rxrpc_connection *conn, rxrpc_see_call(call); list_del_init(&call->chan_wait_link); - call->peer = rxrpc_get_peer(conn->params.peer); + call->peer = rxrpc_get_peer(conn->peer); call->conn = rxrpc_get_connection(conn); call->cid = conn->proto.cid | channel; call->call_id = call_id; @@ -569,7 +580,7 @@ static void rxrpc_activate_one_channel(struct rxrpc_connection *conn, */ static void rxrpc_unidle_conn(struct rxrpc_bundle *bundle, struct rxrpc_connection *conn) { - struct rxrpc_net *rxnet = bundle->params.local->rxnet; + struct rxrpc_net *rxnet = bundle->local->rxnet; bool drop_ref; if (!list_empty(&conn->cache_link)) { @@ -795,7 +806,7 @@ void rxrpc_disconnect_client_call(struct rxrpc_bundle *bundle, struct rxrpc_call { struct rxrpc_connection *conn; struct rxrpc_channel *chan = NULL; - struct rxrpc_net *rxnet = bundle->params.local->rxnet; + struct rxrpc_net *rxnet = bundle->local->rxnet; unsigned int channel; bool may_reuse; u32 cid; @@ -936,11 +947,11 @@ static void rxrpc_unbundle_conn(struct rxrpc_connection *conn) */ static void rxrpc_deactivate_bundle(struct rxrpc_bundle *bundle) { - struct rxrpc_local *local = bundle->params.local; + struct rxrpc_local *local = bundle->local; bool need_put = false; if (atomic_dec_and_lock(&bundle->active, &local->client_bundles_lock)) { - if (!bundle->params.exclusive) { + if (!bundle->exclusive) { _debug("erase bundle"); rb_erase(&bundle->local_node, &local->client_bundles); need_put = true; @@ -957,7 +968,7 @@ static void rxrpc_deactivate_bundle(struct rxrpc_bundle *bundle) */ static void rxrpc_kill_client_conn(struct rxrpc_connection *conn) { - struct rxrpc_local *local = conn->params.local; + struct rxrpc_local *local = conn->local; struct rxrpc_net *rxnet = local->rxnet; _enter("C=%x", conn->debug_id); @@ -1036,7 +1047,7 @@ next: expiry = rxrpc_conn_idle_client_expiry; if (nr_conns > rxrpc_reap_client_connections) expiry = rxrpc_conn_idle_client_fast_expiry; - if (conn->params.local->service_closed) + if (conn->local->service_closed) expiry = rxrpc_closed_conn_expiry * HZ; conn_expires_at = conn->idle_timestamp + expiry; @@ -1110,7 +1121,7 @@ void rxrpc_clean_up_local_conns(struct rxrpc_local *local) list_for_each_entry_safe(conn, tmp, &rxnet->idle_client_conns, cache_link) { - if (conn->params.local == local) { + if (conn->local == local) { trace_rxrpc_client(conn, -1, rxrpc_client_discard); list_move(&conn->cache_link, &graveyard); } diff --git a/net/rxrpc/conn_event.c b/net/rxrpc/conn_event.c index d5549cbfc71b..71ed6b9dc63a 100644 --- a/net/rxrpc/conn_event.c +++ b/net/rxrpc/conn_event.c @@ -52,8 +52,8 @@ static void rxrpc_conn_retransmit_call(struct rxrpc_connection *conn, if (skb && call_id != sp->hdr.callNumber) return; - msg.msg_name = &conn->params.peer->srx.transport; - msg.msg_namelen = conn->params.peer->srx.transport_len; + msg.msg_name = &conn->peer->srx.transport; + msg.msg_namelen = conn->peer->srx.transport_len; msg.msg_control = NULL; msg.msg_controllen = 0; msg.msg_flags = 0; @@ -86,8 +86,8 @@ static void rxrpc_conn_retransmit_call(struct rxrpc_connection *conn, break; case RXRPC_PACKET_TYPE_ACK: - mtu = conn->params.peer->if_mtu; - mtu -= conn->params.peer->hdrsize; + mtu = conn->peer->if_mtu; + mtu -= conn->peer->hdrsize; pkt.ack.bufferSpace = 0; pkt.ack.maxSkew = htons(skb ? skb->priority : 0); pkt.ack.firstPacket = htonl(chan->last_seq + 1); @@ -131,8 +131,8 @@ static void rxrpc_conn_retransmit_call(struct rxrpc_connection *conn, break; } - ret = kernel_sendmsg(conn->params.local->socket, &msg, iov, ioc, len); - conn->params.peer->last_tx_at = ktime_get_seconds(); + ret = kernel_sendmsg(conn->local->socket, &msg, iov, ioc, len); + conn->peer->last_tx_at = ktime_get_seconds(); if (ret < 0) trace_rxrpc_tx_fail(chan->call_debug_id, serial, ret, rxrpc_tx_point_call_final_resend); @@ -211,8 +211,8 @@ static int rxrpc_abort_connection(struct rxrpc_connection *conn, set_bit(RXRPC_CONN_DONT_REUSE, &conn->flags); spin_unlock_bh(&conn->state_lock); - msg.msg_name = &conn->params.peer->srx.transport; - msg.msg_namelen = conn->params.peer->srx.transport_len; + msg.msg_name = &conn->peer->srx.transport; + msg.msg_namelen = conn->peer->srx.transport_len; msg.msg_control = NULL; msg.msg_controllen = 0; msg.msg_flags = 0; @@ -241,7 +241,7 @@ static int rxrpc_abort_connection(struct rxrpc_connection *conn, rxrpc_abort_calls(conn, RXRPC_CALL_LOCALLY_ABORTED, serial); whdr.serial = htonl(serial); - ret = kernel_sendmsg(conn->params.local->socket, &msg, iov, 2, len); + ret = kernel_sendmsg(conn->local->socket, &msg, iov, 2, len); if (ret < 0) { trace_rxrpc_tx_fail(conn->debug_id, serial, ret, rxrpc_tx_point_conn_abort); @@ -251,7 +251,7 @@ static int rxrpc_abort_connection(struct rxrpc_connection *conn, trace_rxrpc_tx_packet(conn->debug_id, &whdr, rxrpc_tx_point_conn_abort); - conn->params.peer->last_tx_at = ktime_get_seconds(); + conn->peer->last_tx_at = ktime_get_seconds(); _leave(" = 0"); return 0; @@ -330,7 +330,7 @@ static int rxrpc_process_event(struct rxrpc_connection *conn, return ret; ret = conn->security->init_connection_security( - conn, conn->params.key->payload.data[0]); + conn, conn->key->payload.data[0]); if (ret < 0) return ret; @@ -484,9 +484,9 @@ void rxrpc_process_connection(struct work_struct *work) rxrpc_see_connection(conn); - if (__rxrpc_use_local(conn->params.local)) { + if (__rxrpc_use_local(conn->local)) { rxrpc_do_process_connection(conn); - rxrpc_unuse_local(conn->params.local); + rxrpc_unuse_local(conn->local); } rxrpc_put_connection(conn); diff --git a/net/rxrpc/conn_object.c b/net/rxrpc/conn_object.c index d5d15389406f..ad6e5ee1f069 100644 --- a/net/rxrpc/conn_object.c +++ b/net/rxrpc/conn_object.c @@ -120,10 +120,10 @@ struct rxrpc_connection *rxrpc_find_connection_rcu(struct rxrpc_local *local, } if (conn->proto.epoch != k.epoch || - conn->params.local != local) + conn->local != local) goto not_found; - peer = conn->params.peer; + peer = conn->peer; switch (srx.transport.family) { case AF_INET: if (peer->srx.transport.sin.sin_port != @@ -231,7 +231,7 @@ void rxrpc_disconnect_call(struct rxrpc_call *call) */ void rxrpc_kill_connection(struct rxrpc_connection *conn) { - struct rxrpc_net *rxnet = conn->params.local->rxnet; + struct rxrpc_net *rxnet = conn->local->rxnet; ASSERT(!rcu_access_pointer(conn->channels[0].call) && !rcu_access_pointer(conn->channels[1].call) && @@ -340,7 +340,7 @@ void rxrpc_put_service_conn(struct rxrpc_connection *conn) __refcount_dec(&conn->ref, &r); trace_rxrpc_conn(debug_id, rxrpc_conn_put_service, r - 1, here); if (r - 1 == 1) - rxrpc_set_service_reap_timer(conn->params.local->rxnet, + rxrpc_set_service_reap_timer(conn->local->rxnet, jiffies + rxrpc_connection_expiry); } @@ -360,13 +360,13 @@ static void rxrpc_destroy_connection(struct rcu_head *rcu) rxrpc_purge_queue(&conn->rx_queue); conn->security->clear(conn); - key_put(conn->params.key); + key_put(conn->key); rxrpc_put_bundle(conn->bundle); - rxrpc_put_peer(conn->params.peer); + rxrpc_put_peer(conn->peer); - if (atomic_dec_and_test(&conn->params.local->rxnet->nr_conns)) - wake_up_var(&conn->params.local->rxnet->nr_conns); - rxrpc_put_local(conn->params.local); + if (atomic_dec_and_test(&conn->local->rxnet->nr_conns)) + wake_up_var(&conn->local->rxnet->nr_conns); + rxrpc_put_local(conn->local); kfree(conn); _leave(""); @@ -397,10 +397,10 @@ void rxrpc_service_connection_reaper(struct work_struct *work) if (conn->state == RXRPC_CONN_SERVICE_PREALLOC) continue; - if (rxnet->live && !conn->params.local->dead) { + if (rxnet->live && !conn->local->dead) { idle_timestamp = READ_ONCE(conn->idle_timestamp); expire_at = idle_timestamp + rxrpc_connection_expiry * HZ; - if (conn->params.local->service_closed) + if (conn->local->service_closed) expire_at = idle_timestamp + rxrpc_closed_conn_expiry * HZ; _debug("reap CONN %d { u=%d,t=%ld }", diff --git a/net/rxrpc/conn_service.c b/net/rxrpc/conn_service.c index 75f903099eb0..a3b91864ef21 100644 --- a/net/rxrpc/conn_service.c +++ b/net/rxrpc/conn_service.c @@ -164,7 +164,7 @@ void rxrpc_new_incoming_connection(struct rxrpc_sock *rx, conn->proto.epoch = sp->hdr.epoch; conn->proto.cid = sp->hdr.cid & RXRPC_CIDMASK; - conn->params.service_id = sp->hdr.serviceId; + conn->orig_service_id = sp->hdr.serviceId; conn->service_id = sp->hdr.serviceId; conn->security_ix = sp->hdr.securityIndex; conn->out_clientflag = 0; @@ -183,7 +183,7 @@ void rxrpc_new_incoming_connection(struct rxrpc_sock *rx, conn->service_id = rx->service_upgrade.to; /* Make the connection a target for incoming packets. */ - rxrpc_publish_service_conn(conn->params.peer, conn); + rxrpc_publish_service_conn(conn->peer, conn); } /* @@ -192,7 +192,7 @@ void rxrpc_new_incoming_connection(struct rxrpc_sock *rx, */ void rxrpc_unpublish_service_conn(struct rxrpc_connection *conn) { - struct rxrpc_peer *peer = conn->params.peer; + struct rxrpc_peer *peer = conn->peer; write_seqlock_bh(&peer->service_conn_lock); if (test_and_clear_bit(RXRPC_CONN_IN_SERVICE_CONNS, &conn->flags)) diff --git a/net/rxrpc/input.c b/net/rxrpc/input.c index e2461f29d765..44caf88e04b8 100644 --- a/net/rxrpc/input.c +++ b/net/rxrpc/input.c @@ -1339,10 +1339,10 @@ int rxrpc_input_packet(struct sock *udp_sk, struct sk_buff *skb) if (!test_bit(RXRPC_CONN_PROBING_FOR_UPGRADE, &conn->flags)) goto reupgrade; - old_id = cmpxchg(&conn->service_id, conn->params.service_id, + old_id = cmpxchg(&conn->service_id, conn->orig_service_id, sp->hdr.serviceId); - if (old_id != conn->params.service_id && + if (old_id != conn->orig_service_id && old_id != sp->hdr.serviceId) goto reupgrade; } diff --git a/net/rxrpc/key.c b/net/rxrpc/key.c index 830eeffe2d5b..8d53aded09c4 100644 --- a/net/rxrpc/key.c +++ b/net/rxrpc/key.c @@ -513,7 +513,7 @@ int rxrpc_get_server_data_key(struct rxrpc_connection *conn, if (ret < 0) goto error; - conn->params.key = key; + conn->key = key; _leave(" = 0 [%d]", key_serial(key)); return 0; diff --git a/net/rxrpc/output.c b/net/rxrpc/output.c index 635acf3dbd77..b5d8eac8c49c 100644 --- a/net/rxrpc/output.c +++ b/net/rxrpc/output.c @@ -142,8 +142,8 @@ retry: txb->ack.reason = RXRPC_ACK_IDLE; } - mtu = conn->params.peer->if_mtu; - mtu -= conn->params.peer->hdrsize; + mtu = conn->peer->if_mtu; + mtu -= conn->peer->hdrsize; jmax = rxrpc_rx_jumbo_max; qsize = (window - 1) - call->rx_consumed; rsize = max_t(int, call->rx_winsize - qsize, 0); @@ -259,7 +259,7 @@ static int rxrpc_send_ack_packet(struct rxrpc_local *local, struct rxrpc_txbuf * txb->ack.previousPacket = htonl(call->rx_highest_seq); iov_iter_kvec(&msg.msg_iter, WRITE, iov, 1, len); - ret = do_udp_sendmsg(conn->params.local->socket, &msg, len); + ret = do_udp_sendmsg(conn->local->socket, &msg, len); call->peer->last_tx_at = ktime_get_seconds(); if (ret < 0) trace_rxrpc_tx_fail(call->debug_id, serial, ret, @@ -368,8 +368,8 @@ int rxrpc_send_abort_packet(struct rxrpc_call *call) pkt.whdr.serial = htonl(serial); iov_iter_kvec(&msg.msg_iter, WRITE, iov, 1, sizeof(pkt)); - ret = do_udp_sendmsg(conn->params.local->socket, &msg, sizeof(pkt)); - conn->params.peer->last_tx_at = ktime_get_seconds(); + ret = do_udp_sendmsg(conn->local->socket, &msg, sizeof(pkt)); + conn->peer->last_tx_at = ktime_get_seconds(); if (ret < 0) trace_rxrpc_tx_fail(call->debug_id, serial, ret, rxrpc_tx_point_call_abort); @@ -473,7 +473,7 @@ dont_set_request_ack: if (txb->len >= call->peer->maxdata) goto send_fragmentable; - down_read(&conn->params.local->defrag_sem); + down_read(&conn->local->defrag_sem); txb->last_sent = ktime_get_real(); if (txb->wire.flags & RXRPC_REQUEST_ACK) @@ -486,10 +486,10 @@ dont_set_request_ack: * message and update the peer record */ rxrpc_inc_stat(call->rxnet, stat_tx_data_send); - ret = do_udp_sendmsg(conn->params.local->socket, &msg, len); - conn->params.peer->last_tx_at = ktime_get_seconds(); + ret = do_udp_sendmsg(conn->local->socket, &msg, len); + conn->peer->last_tx_at = ktime_get_seconds(); - up_read(&conn->params.local->defrag_sem); + up_read(&conn->local->defrag_sem); if (ret < 0) { rxrpc_cancel_rtt_probe(call, serial, rtt_slot); trace_rxrpc_tx_fail(call->debug_id, serial, ret, @@ -549,22 +549,22 @@ send_fragmentable: /* attempt to send this message with fragmentation enabled */ _debug("send fragment"); - down_write(&conn->params.local->defrag_sem); + down_write(&conn->local->defrag_sem); txb->last_sent = ktime_get_real(); if (txb->wire.flags & RXRPC_REQUEST_ACK) rtt_slot = rxrpc_begin_rtt_probe(call, serial, rxrpc_rtt_tx_data); - switch (conn->params.local->srx.transport.family) { + switch (conn->local->srx.transport.family) { case AF_INET6: case AF_INET: - ip_sock_set_mtu_discover(conn->params.local->socket->sk, + ip_sock_set_mtu_discover(conn->local->socket->sk, IP_PMTUDISC_DONT); rxrpc_inc_stat(call->rxnet, stat_tx_data_send_frag); - ret = do_udp_sendmsg(conn->params.local->socket, &msg, len); - conn->params.peer->last_tx_at = ktime_get_seconds(); + ret = do_udp_sendmsg(conn->local->socket, &msg, len); + conn->peer->last_tx_at = ktime_get_seconds(); - ip_sock_set_mtu_discover(conn->params.local->socket->sk, + ip_sock_set_mtu_discover(conn->local->socket->sk, IP_PMTUDISC_DO); break; @@ -582,7 +582,7 @@ send_fragmentable: } rxrpc_tx_backoff(call, ret); - up_write(&conn->params.local->defrag_sem); + up_write(&conn->local->defrag_sem); goto done; } diff --git a/net/rxrpc/proc.c b/net/rxrpc/proc.c index fae22a8b38d6..bb2edf6db896 100644 --- a/net/rxrpc/proc.c +++ b/net/rxrpc/proc.c @@ -172,9 +172,9 @@ static int rxrpc_connection_seq_show(struct seq_file *seq, void *v) goto print; } - sprintf(lbuff, "%pISpc", &conn->params.local->srx.transport); + sprintf(lbuff, "%pISpc", &conn->local->srx.transport); - sprintf(rbuff, "%pISpc", &conn->params.peer->srx.transport); + sprintf(rbuff, "%pISpc", &conn->peer->srx.transport); print: seq_printf(seq, "UDP %-47.47s %-47.47s %4x %08x %s %3u" @@ -186,7 +186,7 @@ print: rxrpc_conn_is_service(conn) ? "Svc" : "Clt", refcount_read(&conn->ref), rxrpc_conn_states[conn->state], - key_serial(conn->params.key), + key_serial(conn->key), atomic_read(&conn->serial), conn->hi_serial, conn->channels[0].call_id, diff --git a/net/rxrpc/rxkad.c b/net/rxrpc/rxkad.c index 36cf40442a7e..d1233720e05f 100644 --- a/net/rxrpc/rxkad.c +++ b/net/rxrpc/rxkad.c @@ -103,7 +103,7 @@ static int rxkad_init_connection_security(struct rxrpc_connection *conn, struct crypto_sync_skcipher *ci; int ret; - _enter("{%d},{%x}", conn->debug_id, key_serial(conn->params.key)); + _enter("{%d},{%x}", conn->debug_id, key_serial(conn->key)); conn->security_ix = token->security_index; @@ -118,7 +118,7 @@ static int rxkad_init_connection_security(struct rxrpc_connection *conn, sizeof(token->kad->session_key)) < 0) BUG(); - switch (conn->params.security_level) { + switch (conn->security_level) { case RXRPC_SECURITY_PLAIN: case RXRPC_SECURITY_AUTH: case RXRPC_SECURITY_ENCRYPT: @@ -150,7 +150,7 @@ static int rxkad_how_much_data(struct rxrpc_call *call, size_t remain, { size_t shdr, buf_size, chunk; - switch (call->conn->params.security_level) { + switch (call->conn->security_level) { default: buf_size = chunk = min_t(size_t, remain, RXRPC_JUMBO_DATALEN); shdr = 0; @@ -192,7 +192,7 @@ static int rxkad_prime_packet_security(struct rxrpc_connection *conn, _enter(""); - if (!conn->params.key) + if (!conn->key) return 0; tmpbuf = kmalloc(tmpsize, GFP_KERNEL); @@ -205,7 +205,7 @@ static int rxkad_prime_packet_security(struct rxrpc_connection *conn, return -ENOMEM; } - token = conn->params.key->payload.data[0]; + token = conn->key->payload.data[0]; memcpy(&iv, token->kad->session_key, sizeof(iv)); tmpbuf[0] = htonl(conn->proto.epoch); @@ -317,7 +317,7 @@ static int rxkad_secure_packet_encrypt(const struct rxrpc_call *call, } /* encrypt from the session key */ - token = call->conn->params.key->payload.data[0]; + token = call->conn->key->payload.data[0]; memcpy(&iv, token->kad->session_key, sizeof(iv)); sg_init_one(&sg, txb->data, txb->len); @@ -344,13 +344,13 @@ static int rxkad_secure_packet(struct rxrpc_call *call, struct rxrpc_txbuf *txb) int ret; _enter("{%d{%x}},{#%u},%u,", - call->debug_id, key_serial(call->conn->params.key), + call->debug_id, key_serial(call->conn->key), txb->seq, txb->len); if (!call->conn->rxkad.cipher) return 0; - ret = key_validate(call->conn->params.key); + ret = key_validate(call->conn->key); if (ret < 0) return ret; @@ -380,7 +380,7 @@ static int rxkad_secure_packet(struct rxrpc_call *call, struct rxrpc_txbuf *txb) y = 1; /* zero checksums are not permitted */ txb->wire.cksum = htons(y); - switch (call->conn->params.security_level) { + switch (call->conn->security_level) { case RXRPC_SECURITY_PLAIN: ret = 0; break; @@ -525,7 +525,7 @@ static int rxkad_verify_packet_2(struct rxrpc_call *call, struct sk_buff *skb, } /* decrypt from the session key */ - token = call->conn->params.key->payload.data[0]; + token = call->conn->key->payload.data[0]; memcpy(&iv, token->kad->session_key, sizeof(iv)); skcipher_request_set_sync_tfm(req, call->conn->rxkad.cipher); @@ -596,7 +596,7 @@ static int rxkad_verify_packet(struct rxrpc_call *call, struct sk_buff *skb) u32 x, y; _enter("{%d{%x}},{#%u}", - call->debug_id, key_serial(call->conn->params.key), seq); + call->debug_id, key_serial(call->conn->key), seq); if (!call->conn->rxkad.cipher) return 0; @@ -632,7 +632,7 @@ static int rxkad_verify_packet(struct rxrpc_call *call, struct sk_buff *skb) goto protocol_error; } - switch (call->conn->params.security_level) { + switch (call->conn->security_level) { case RXRPC_SECURITY_PLAIN: ret = 0; break; @@ -678,8 +678,8 @@ static int rxkad_issue_challenge(struct rxrpc_connection *conn) challenge.min_level = htonl(0); challenge.__padding = 0; - msg.msg_name = &conn->params.peer->srx.transport; - msg.msg_namelen = conn->params.peer->srx.transport_len; + msg.msg_name = &conn->peer->srx.transport; + msg.msg_namelen = conn->peer->srx.transport_len; msg.msg_control = NULL; msg.msg_controllen = 0; msg.msg_flags = 0; @@ -705,14 +705,14 @@ static int rxkad_issue_challenge(struct rxrpc_connection *conn) serial = atomic_inc_return(&conn->serial); whdr.serial = htonl(serial); - ret = kernel_sendmsg(conn->params.local->socket, &msg, iov, 2, len); + ret = kernel_sendmsg(conn->local->socket, &msg, iov, 2, len); if (ret < 0) { trace_rxrpc_tx_fail(conn->debug_id, serial, ret, rxrpc_tx_point_rxkad_challenge); return -EAGAIN; } - conn->params.peer->last_tx_at = ktime_get_seconds(); + conn->peer->last_tx_at = ktime_get_seconds(); trace_rxrpc_tx_packet(conn->debug_id, &whdr, rxrpc_tx_point_rxkad_challenge); _leave(" = 0"); @@ -736,8 +736,8 @@ static int rxkad_send_response(struct rxrpc_connection *conn, _enter(""); - msg.msg_name = &conn->params.peer->srx.transport; - msg.msg_namelen = conn->params.peer->srx.transport_len; + msg.msg_name = &conn->peer->srx.transport; + msg.msg_namelen = conn->peer->srx.transport_len; msg.msg_control = NULL; msg.msg_controllen = 0; msg.msg_flags = 0; @@ -762,14 +762,14 @@ static int rxkad_send_response(struct rxrpc_connection *conn, serial = atomic_inc_return(&conn->serial); whdr.serial = htonl(serial); - ret = kernel_sendmsg(conn->params.local->socket, &msg, iov, 3, len); + ret = kernel_sendmsg(conn->local->socket, &msg, iov, 3, len); if (ret < 0) { trace_rxrpc_tx_fail(conn->debug_id, serial, ret, rxrpc_tx_point_rxkad_response); return -EAGAIN; } - conn->params.peer->last_tx_at = ktime_get_seconds(); + conn->peer->last_tx_at = ktime_get_seconds(); _leave(" = 0"); return 0; } @@ -832,15 +832,15 @@ static int rxkad_respond_to_challenge(struct rxrpc_connection *conn, u32 version, nonce, min_level, abort_code; int ret; - _enter("{%d,%x}", conn->debug_id, key_serial(conn->params.key)); + _enter("{%d,%x}", conn->debug_id, key_serial(conn->key)); eproto = tracepoint_string("chall_no_key"); abort_code = RX_PROTOCOL_ERROR; - if (!conn->params.key) + if (!conn->key) goto protocol_error; abort_code = RXKADEXPIRED; - ret = key_validate(conn->params.key); + ret = key_validate(conn->key); if (ret < 0) goto other_error; @@ -863,10 +863,10 @@ static int rxkad_respond_to_challenge(struct rxrpc_connection *conn, abort_code = RXKADLEVELFAIL; ret = -EACCES; - if (conn->params.security_level < min_level) + if (conn->security_level < min_level) goto other_error; - token = conn->params.key->payload.data[0]; + token = conn->key->payload.data[0]; /* build the response packet */ resp = kzalloc(sizeof(struct rxkad_response), GFP_NOFS); @@ -878,7 +878,7 @@ static int rxkad_respond_to_challenge(struct rxrpc_connection *conn, resp->encrypted.cid = htonl(conn->proto.cid); resp->encrypted.securityIndex = htonl(conn->security_ix); resp->encrypted.inc_nonce = htonl(nonce + 1); - resp->encrypted.level = htonl(conn->params.security_level); + resp->encrypted.level = htonl(conn->security_level); resp->kvno = htonl(token->kad->kvno); resp->ticket_len = htonl(token->kad->ticket_len); resp->encrypted.call_id[0] = htonl(conn->channels[0].call_counter); @@ -1226,7 +1226,7 @@ static int rxkad_verify_response(struct rxrpc_connection *conn, level = ntohl(response->encrypted.level); if (level > RXRPC_SECURITY_ENCRYPT) goto protocol_error_free; - conn->params.security_level = level; + conn->security_level = level; /* create a key to hold the security data and expiration time - after * this the connection security can be handled in exactly the same way diff --git a/net/rxrpc/security.c b/net/rxrpc/security.c index 50cb5f1ee0c0..e6ddac9b3732 100644 --- a/net/rxrpc/security.c +++ b/net/rxrpc/security.c @@ -69,7 +69,7 @@ int rxrpc_init_client_conn_security(struct rxrpc_connection *conn) { const struct rxrpc_security *sec; struct rxrpc_key_token *token; - struct key *key = conn->params.key; + struct key *key = conn->key; int ret; _enter("{%d},{%x}", conn->debug_id, key_serial(key)); @@ -163,7 +163,7 @@ struct key *rxrpc_look_up_server_security(struct rxrpc_connection *conn, rcu_read_lock(); - rx = rcu_dereference(conn->params.local->service); + rx = rcu_dereference(conn->local->service); if (!rx) goto out; From f14febd8df5a490acc40b919808f163e997d7f03 Mon Sep 17 00:00:00 2001 From: David Howells Date: Mon, 14 Nov 2022 12:21:32 +0000 Subject: [PATCH 1408/2002] rxrpc: Extract the code from a received ABORT packet much earlier Extract the code from a received rx ABORT packet much earlier and in a single place and harmonise the responses to malformed ABORT packets. Signed-off-by: David Howells cc: Marc Dionne cc: linux-afs@lists.infradead.org --- net/rxrpc/conn_event.c | 12 +----------- net/rxrpc/input.c | 31 +++++++++++++++++++------------ 2 files changed, 20 insertions(+), 23 deletions(-) diff --git a/net/rxrpc/conn_event.c b/net/rxrpc/conn_event.c index 71ed6b9dc63a..f890a30c4df6 100644 --- a/net/rxrpc/conn_event.c +++ b/net/rxrpc/conn_event.c @@ -282,8 +282,6 @@ static int rxrpc_process_event(struct rxrpc_connection *conn, u32 *_abort_code) { struct rxrpc_skb_priv *sp = rxrpc_skb(skb); - __be32 wtmp; - u32 abort_code; int loop, ret; if (conn->state >= RXRPC_CONN_REMOTELY_ABORTED) { @@ -305,16 +303,8 @@ static int rxrpc_process_event(struct rxrpc_connection *conn, return 0; case RXRPC_PACKET_TYPE_ABORT: - if (skb_copy_bits(skb, sizeof(struct rxrpc_wire_header), - &wtmp, sizeof(wtmp)) < 0) { - trace_rxrpc_rx_eproto(NULL, sp->hdr.serial, - tracepoint_string("bad_abort")); - return -EPROTO; - } - abort_code = ntohl(wtmp); - conn->error = -ECONNABORTED; - conn->abort_code = abort_code; + conn->abort_code = skb->priority; conn->state = RXRPC_CONN_REMOTELY_ABORTED; set_bit(RXRPC_CONN_DONT_REUSE, &conn->flags); rxrpc_abort_calls(conn, RXRPC_CALL_REMOTELY_ABORTED, sp->hdr.serial); diff --git a/net/rxrpc/input.c b/net/rxrpc/input.c index 44caf88e04b8..42c8257158f7 100644 --- a/net/rxrpc/input.c +++ b/net/rxrpc/input.c @@ -1019,20 +1019,11 @@ static void rxrpc_input_ackall(struct rxrpc_call *call, struct sk_buff *skb) static void rxrpc_input_abort(struct rxrpc_call *call, struct sk_buff *skb) { struct rxrpc_skb_priv *sp = rxrpc_skb(skb); - __be32 wtmp; - u32 abort_code = RX_CALL_DEAD; - _enter(""); - - if (skb->len >= 4 && - skb_copy_bits(skb, sizeof(struct rxrpc_wire_header), - &wtmp, sizeof(wtmp)) >= 0) - abort_code = ntohl(wtmp); - - trace_rxrpc_rx_abort(call, sp->hdr.serial, abort_code); + trace_rxrpc_rx_abort(call, sp->hdr.serial, skb->priority); rxrpc_set_call_completion(call, RXRPC_CALL_REMOTELY_ABORTED, - abort_code, -ECONNABORTED); + skb->priority, -ECONNABORTED); } /* @@ -1193,6 +1184,20 @@ int rxrpc_extract_header(struct rxrpc_skb_priv *sp, struct sk_buff *skb) return 0; } +/* + * Extract the abort code from an ABORT packet and stash it in skb->priority. + */ +static bool rxrpc_extract_abort(struct sk_buff *skb) +{ + __be32 wtmp; + + if (skb_copy_bits(skb, sizeof(struct rxrpc_wire_header), + &wtmp, sizeof(wtmp)) < 0) + return false; + skb->priority = ntohl(wtmp); + return true; +} + /* * handle data received on the local endpoint * - may be called in interrupt context @@ -1264,8 +1269,10 @@ int rxrpc_input_packet(struct sock *udp_sk, struct sk_buff *skb) case RXRPC_PACKET_TYPE_ACKALL: if (sp->hdr.callNumber == 0) goto bad_message; - fallthrough; + break; case RXRPC_PACKET_TYPE_ABORT: + if (!rxrpc_extract_abort(skb)) + return true; /* Just discard if malformed */ break; case RXRPC_PACKET_TYPE_DATA: From 0fde882fc9ee9cc2e66e8c5a5a93c83932d7ca95 Mon Sep 17 00:00:00 2001 From: David Howells Date: Fri, 21 Oct 2022 13:00:34 +0100 Subject: [PATCH 1409/2002] rxrpc: trace: Don't use __builtin_return_address for rxrpc_local tracing In rxrpc tracing, use enums to generate lists of points of interest rather than __builtin_return_address() for the rxrpc_local tracepoint Signed-off-by: David Howells cc: Marc Dionne cc: linux-afs@lists.infradead.org --- include/trace/events/rxrpc.h | 49 ++++++++++++++++------ net/rxrpc/af_rxrpc.c | 8 ++-- net/rxrpc/ar-internal.h | 41 ++++++++++++++----- net/rxrpc/call_accept.c | 4 +- net/rxrpc/call_event.c | 2 +- net/rxrpc/conn_client.c | 2 +- net/rxrpc/conn_event.c | 4 +- net/rxrpc/conn_object.c | 2 +- net/rxrpc/input.c | 4 +- net/rxrpc/local_object.c | 78 ++++++++++++++++++++---------------- net/rxrpc/output.c | 3 +- net/rxrpc/peer_event.c | 4 +- net/rxrpc/peer_object.c | 4 +- 13 files changed, 129 insertions(+), 76 deletions(-) diff --git a/include/trace/events/rxrpc.h b/include/trace/events/rxrpc.h index 2b77f9a75bf7..015569845b1d 100644 --- a/include/trace/events/rxrpc.h +++ b/include/trace/events/rxrpc.h @@ -32,12 +32,35 @@ E_(rxrpc_skb_unshared_nomem, "US0") #define rxrpc_local_traces \ - EM(rxrpc_local_got, "GOT") \ - EM(rxrpc_local_new, "NEW") \ - EM(rxrpc_local_processing, "PRO") \ - EM(rxrpc_local_put, "PUT") \ - EM(rxrpc_local_queued, "QUE") \ - E_(rxrpc_local_tx_ack, "TAK") + EM(rxrpc_local_free, "FREE ") \ + EM(rxrpc_local_get_client_conn, "GET conn-cln") \ + EM(rxrpc_local_get_for_use, "GET for-use ") \ + EM(rxrpc_local_get_peer, "GET peer ") \ + EM(rxrpc_local_get_prealloc_conn, "GET conn-pre") \ + EM(rxrpc_local_get_queue, "GET queue ") \ + EM(rxrpc_local_new, "NEW ") \ + EM(rxrpc_local_processing, "PROCESSING ") \ + EM(rxrpc_local_put_already_queued, "PUT alreadyq") \ + EM(rxrpc_local_put_bind, "PUT bind ") \ + EM(rxrpc_local_put_for_use, "PUT for-use ") \ + EM(rxrpc_local_put_kill_conn, "PUT conn-kil") \ + EM(rxrpc_local_put_peer, "PUT peer ") \ + EM(rxrpc_local_put_prealloc_conn, "PUT conn-pre") \ + EM(rxrpc_local_put_release_sock, "PUT rel-sock") \ + EM(rxrpc_local_put_queue, "PUT queue ") \ + EM(rxrpc_local_queued, "QUEUED ") \ + EM(rxrpc_local_see_tx_ack, "SEE tx-ack ") \ + EM(rxrpc_local_stop, "STOP ") \ + EM(rxrpc_local_stopped, "STOPPED ") \ + EM(rxrpc_local_unuse_bind, "UNU bind ") \ + EM(rxrpc_local_unuse_conn_work, "UNU conn-wrk") \ + EM(rxrpc_local_unuse_peer_keepalive, "UNU peer-kpa") \ + EM(rxrpc_local_unuse_release_sock, "UNU rel-sock") \ + EM(rxrpc_local_unuse_work, "UNU work ") \ + EM(rxrpc_local_use_conn_work, "USE conn-wrk") \ + EM(rxrpc_local_use_lookup, "USE lookup ") \ + EM(rxrpc_local_use_peer_keepalive, "USE peer-kpa") \ + E_(rxrpc_local_use_work, "USE work ") #define rxrpc_peer_traces \ EM(rxrpc_peer_got, "GOT") \ @@ -345,29 +368,29 @@ rxrpc_txqueue_traces; TRACE_EVENT(rxrpc_local, TP_PROTO(unsigned int local_debug_id, enum rxrpc_local_trace op, - int usage, const void *where), + int ref, int usage), - TP_ARGS(local_debug_id, op, usage, where), + TP_ARGS(local_debug_id, op, ref, usage), TP_STRUCT__entry( __field(unsigned int, local ) __field(int, op ) + __field(int, ref ) __field(int, usage ) - __field(const void *, where ) ), TP_fast_assign( __entry->local = local_debug_id; __entry->op = op; + __entry->ref = ref; __entry->usage = usage; - __entry->where = where; ), - TP_printk("L=%08x %s u=%d sp=%pSR", + TP_printk("L=%08x %s r=%d u=%d", __entry->local, __print_symbolic(__entry->op, rxrpc_local_traces), - __entry->usage, - __entry->where) + __entry->ref, + __entry->usage) ); TRACE_EVENT(rxrpc_peer, diff --git a/net/rxrpc/af_rxrpc.c b/net/rxrpc/af_rxrpc.c index aacdd96a9886..989ebca899f3 100644 --- a/net/rxrpc/af_rxrpc.c +++ b/net/rxrpc/af_rxrpc.c @@ -194,8 +194,8 @@ static int rxrpc_bind(struct socket *sock, struct sockaddr *saddr, int len) service_in_use: write_unlock(&local->services_lock); - rxrpc_unuse_local(local); - rxrpc_put_local(local); + rxrpc_unuse_local(local, rxrpc_local_unuse_bind); + rxrpc_put_local(local, rxrpc_local_put_bind); ret = -EADDRINUSE; error_unlock: release_sock(&rx->sk); @@ -888,8 +888,8 @@ static int rxrpc_release_sock(struct sock *sk) flush_workqueue(rxrpc_workqueue); rxrpc_purge_queue(&sk->sk_receive_queue); - rxrpc_unuse_local(rx->local); - rxrpc_put_local(rx->local); + rxrpc_unuse_local(rx->local, rxrpc_local_unuse_release_sock); + rxrpc_put_local(rx->local, rxrpc_local_put_release_sock); rx->local = NULL; key_put(rx->key); rx->key = NULL; diff --git a/net/rxrpc/ar-internal.h b/net/rxrpc/ar-internal.h index 7c48b0163032..dde9ce21ef48 100644 --- a/net/rxrpc/ar-internal.h +++ b/net/rxrpc/ar-internal.h @@ -979,22 +979,45 @@ extern void rxrpc_process_local_events(struct rxrpc_local *); * local_object.c */ struct rxrpc_local *rxrpc_lookup_local(struct net *, const struct sockaddr_rxrpc *); -struct rxrpc_local *rxrpc_get_local(struct rxrpc_local *); -struct rxrpc_local *rxrpc_get_local_maybe(struct rxrpc_local *); -void rxrpc_put_local(struct rxrpc_local *); -struct rxrpc_local *rxrpc_use_local(struct rxrpc_local *); -void rxrpc_unuse_local(struct rxrpc_local *); +struct rxrpc_local *rxrpc_get_local(struct rxrpc_local *, enum rxrpc_local_trace); +struct rxrpc_local *rxrpc_get_local_maybe(struct rxrpc_local *, enum rxrpc_local_trace); +void rxrpc_put_local(struct rxrpc_local *, enum rxrpc_local_trace); +struct rxrpc_local *rxrpc_use_local(struct rxrpc_local *, enum rxrpc_local_trace); +void rxrpc_unuse_local(struct rxrpc_local *, enum rxrpc_local_trace); void rxrpc_queue_local(struct rxrpc_local *); void rxrpc_destroy_all_locals(struct rxrpc_net *); -static inline bool __rxrpc_unuse_local(struct rxrpc_local *local) +static inline bool __rxrpc_unuse_local(struct rxrpc_local *local, + enum rxrpc_local_trace why) { - return atomic_dec_return(&local->active_users) == 0; + unsigned int debug_id = local->debug_id; + int r, u; + + r = refcount_read(&local->ref); + u = atomic_dec_return(&local->active_users); + trace_rxrpc_local(debug_id, why, r, u); + return u == 0; } -static inline bool __rxrpc_use_local(struct rxrpc_local *local) +static inline bool __rxrpc_use_local(struct rxrpc_local *local, + enum rxrpc_local_trace why) { - return atomic_fetch_add_unless(&local->active_users, 1, 0) != 0; + int r, u; + + r = refcount_read(&local->ref); + u = atomic_fetch_add_unless(&local->active_users, 1, 0); + trace_rxrpc_local(local->debug_id, why, r, u); + return u != 0; +} + +static inline void rxrpc_see_local(struct rxrpc_local *local, + enum rxrpc_local_trace why) +{ + int r, u; + + r = refcount_read(&local->ref); + u = atomic_read(&local->active_users); + trace_rxrpc_local(local->debug_id, why, r, u); } /* diff --git a/net/rxrpc/call_accept.c b/net/rxrpc/call_accept.c index 4888959e4727..1b12d4e28373 100644 --- a/net/rxrpc/call_accept.c +++ b/net/rxrpc/call_accept.c @@ -197,7 +197,7 @@ void rxrpc_discard_prealloc(struct rxrpc_sock *rx) tail = b->peer_backlog_tail; while (CIRC_CNT(head, tail, size) > 0) { struct rxrpc_peer *peer = b->peer_backlog[tail]; - rxrpc_put_local(peer->local); + rxrpc_put_local(peer->local, rxrpc_local_put_prealloc_conn); kfree(peer); tail = (tail + 1) & (size - 1); } @@ -305,7 +305,7 @@ static struct rxrpc_call *rxrpc_alloc_incoming_call(struct rxrpc_sock *rx, b->conn_backlog[conn_tail] = NULL; smp_store_release(&b->conn_backlog_tail, (conn_tail + 1) & (RXRPC_BACKLOG_MAX - 1)); - conn->local = rxrpc_get_local(local); + conn->local = rxrpc_get_local(local, rxrpc_local_get_prealloc_conn); conn->peer = peer; rxrpc_see_connection(conn); rxrpc_new_incoming_connection(rx, conn, sec, skb); diff --git a/net/rxrpc/call_event.c b/net/rxrpc/call_event.c index b17ed37434bd..591af8e2e3d0 100644 --- a/net/rxrpc/call_event.c +++ b/net/rxrpc/call_event.c @@ -114,7 +114,7 @@ void rxrpc_send_ACK(struct rxrpc_call *call, u8 ack_reason, if (in_task()) { rxrpc_transmit_ack_packets(call->peer->local); } else { - rxrpc_get_local(local); + rxrpc_get_local(local, rxrpc_local_get_queue); rxrpc_queue_local(local); } } diff --git a/net/rxrpc/conn_client.c b/net/rxrpc/conn_client.c index 71404b33623f..9a69b4c1b182 100644 --- a/net/rxrpc/conn_client.c +++ b/net/rxrpc/conn_client.c @@ -208,7 +208,7 @@ rxrpc_alloc_client_connection(struct rxrpc_bundle *bundle, gfp_t gfp) rxrpc_get_bundle(bundle); rxrpc_get_peer(conn->peer); - rxrpc_get_local(conn->local); + rxrpc_get_local(conn->local, rxrpc_local_get_client_conn); key_get(conn->key); trace_rxrpc_conn(conn->debug_id, rxrpc_conn_new_client, diff --git a/net/rxrpc/conn_event.c b/net/rxrpc/conn_event.c index f890a30c4df6..225edaf019f1 100644 --- a/net/rxrpc/conn_event.c +++ b/net/rxrpc/conn_event.c @@ -474,9 +474,9 @@ void rxrpc_process_connection(struct work_struct *work) rxrpc_see_connection(conn); - if (__rxrpc_use_local(conn->local)) { + if (__rxrpc_use_local(conn->local, rxrpc_local_use_conn_work)) { rxrpc_do_process_connection(conn); - rxrpc_unuse_local(conn->local); + rxrpc_unuse_local(conn->local, rxrpc_local_unuse_conn_work); } rxrpc_put_connection(conn); diff --git a/net/rxrpc/conn_object.c b/net/rxrpc/conn_object.c index ad6e5ee1f069..725359afeac0 100644 --- a/net/rxrpc/conn_object.c +++ b/net/rxrpc/conn_object.c @@ -366,7 +366,7 @@ static void rxrpc_destroy_connection(struct rcu_head *rcu) if (atomic_dec_and_test(&conn->local->rxnet->nr_conns)) wake_up_var(&conn->local->rxnet->nr_conns); - rxrpc_put_local(conn->local); + rxrpc_put_local(conn->local, rxrpc_local_put_kill_conn); kfree(conn); _leave(""); diff --git a/net/rxrpc/input.c b/net/rxrpc/input.c index 42c8257158f7..cecfd201d832 100644 --- a/net/rxrpc/input.c +++ b/net/rxrpc/input.c @@ -1133,7 +1133,7 @@ static void rxrpc_post_packet_to_local(struct rxrpc_local *local, { _enter("%p,%p", local, skb); - if (rxrpc_get_local_maybe(local)) { + if (rxrpc_get_local_maybe(local, rxrpc_local_get_queue)) { skb_queue_tail(&local->event_queue, skb); rxrpc_queue_local(local); } else { @@ -1146,7 +1146,7 @@ static void rxrpc_post_packet_to_local(struct rxrpc_local *local, */ static void rxrpc_reject_packet(struct rxrpc_local *local, struct sk_buff *skb) { - if (rxrpc_get_local_maybe(local)) { + if (rxrpc_get_local_maybe(local, rxrpc_local_get_queue)) { skb_queue_tail(&local->reject_queue, skb); rxrpc_queue_local(local); } else { diff --git a/net/rxrpc/local_object.c b/net/rxrpc/local_object.c index 11080c335d42..1617ce651b9b 100644 --- a/net/rxrpc/local_object.c +++ b/net/rxrpc/local_object.c @@ -110,7 +110,7 @@ static struct rxrpc_local *rxrpc_alloc_local(struct rxrpc_net *rxnet, local->debug_id = atomic_inc_return(&rxrpc_debug_id); memcpy(&local->srx, srx, sizeof(*srx)); local->srx.srx_service = 0; - trace_rxrpc_local(local->debug_id, rxrpc_local_new, 1, NULL); + trace_rxrpc_local(local->debug_id, rxrpc_local_new, 1, 1); } _leave(" = %p", local); @@ -228,7 +228,7 @@ struct rxrpc_local *rxrpc_lookup_local(struct net *net, * we're attempting to use a local address that the dying * object is still using. */ - if (!rxrpc_use_local(local)) + if (!rxrpc_use_local(local, rxrpc_local_use_lookup)) break; goto found; @@ -272,32 +272,32 @@ addr_in_use: /* * Get a ref on a local endpoint. */ -struct rxrpc_local *rxrpc_get_local(struct rxrpc_local *local) +struct rxrpc_local *rxrpc_get_local(struct rxrpc_local *local, + enum rxrpc_local_trace why) { - const void *here = __builtin_return_address(0); - int r; + int r, u; + u = atomic_read(&local->active_users); __refcount_inc(&local->ref, &r); - trace_rxrpc_local(local->debug_id, rxrpc_local_got, r + 1, here); + trace_rxrpc_local(local->debug_id, why, r + 1, u); return local; } /* * Get a ref on a local endpoint unless its usage has already reached 0. */ -struct rxrpc_local *rxrpc_get_local_maybe(struct rxrpc_local *local) +struct rxrpc_local *rxrpc_get_local_maybe(struct rxrpc_local *local, + enum rxrpc_local_trace why) { - const void *here = __builtin_return_address(0); - int r; + int r, u; - if (local) { - if (__refcount_inc_not_zero(&local->ref, &r)) - trace_rxrpc_local(local->debug_id, rxrpc_local_got, - r + 1, here); - else - local = NULL; + if (local && __refcount_inc_not_zero(&local->ref, &r)) { + u = atomic_read(&local->active_users); + trace_rxrpc_local(local->debug_id, why, r + 1, u); + return local; } - return local; + + return NULL; } /* @@ -305,31 +305,31 @@ struct rxrpc_local *rxrpc_get_local_maybe(struct rxrpc_local *local) */ void rxrpc_queue_local(struct rxrpc_local *local) { - const void *here = __builtin_return_address(0); unsigned int debug_id = local->debug_id; int r = refcount_read(&local->ref); + int u = atomic_read(&local->active_users); if (rxrpc_queue_work(&local->processor)) - trace_rxrpc_local(debug_id, rxrpc_local_queued, r + 1, here); + trace_rxrpc_local(debug_id, rxrpc_local_queued, r, u); else - rxrpc_put_local(local); + rxrpc_put_local(local, rxrpc_local_put_already_queued); } /* * Drop a ref on a local endpoint. */ -void rxrpc_put_local(struct rxrpc_local *local) +void rxrpc_put_local(struct rxrpc_local *local, enum rxrpc_local_trace why) { - const void *here = __builtin_return_address(0); unsigned int debug_id; bool dead; - int r; + int r, u; if (local) { debug_id = local->debug_id; + u = atomic_read(&local->active_users); dead = __refcount_dec_and_test(&local->ref, &r); - trace_rxrpc_local(debug_id, rxrpc_local_put, r, here); + trace_rxrpc_local(debug_id, why, r, u); if (dead) call_rcu(&local->rcu, rxrpc_local_rcu); @@ -339,14 +339,15 @@ void rxrpc_put_local(struct rxrpc_local *local) /* * Start using a local endpoint. */ -struct rxrpc_local *rxrpc_use_local(struct rxrpc_local *local) +struct rxrpc_local *rxrpc_use_local(struct rxrpc_local *local, + enum rxrpc_local_trace why) { - local = rxrpc_get_local_maybe(local); + local = rxrpc_get_local_maybe(local, rxrpc_local_get_for_use); if (!local) return NULL; - if (!__rxrpc_use_local(local)) { - rxrpc_put_local(local); + if (!__rxrpc_use_local(local, why)) { + rxrpc_put_local(local, rxrpc_local_put_for_use); return NULL; } @@ -357,11 +358,18 @@ struct rxrpc_local *rxrpc_use_local(struct rxrpc_local *local) * Cease using a local endpoint. Once the number of active users reaches 0, we * start the closure of the transport in the work processor. */ -void rxrpc_unuse_local(struct rxrpc_local *local) +void rxrpc_unuse_local(struct rxrpc_local *local, enum rxrpc_local_trace why) { + unsigned int debug_id; + int r, u; + if (local) { - if (__rxrpc_unuse_local(local)) { - rxrpc_get_local(local); + debug_id = local->debug_id; + r = refcount_read(&local->ref); + u = atomic_dec_return(&local->active_users); + trace_rxrpc_local(debug_id, why, r, u); + if (u == 0) { + rxrpc_get_local(local, rxrpc_local_get_queue); rxrpc_queue_local(local); } } @@ -418,12 +426,11 @@ static void rxrpc_local_processor(struct work_struct *work) if (local->dead) return; - trace_rxrpc_local(local->debug_id, rxrpc_local_processing, - refcount_read(&local->ref), NULL); + rxrpc_see_local(local, rxrpc_local_processing); do { again = false; - if (!__rxrpc_use_local(local)) { + if (!__rxrpc_use_local(local, rxrpc_local_use_work)) { rxrpc_local_destroyer(local); break; } @@ -443,10 +450,10 @@ static void rxrpc_local_processor(struct work_struct *work) again = true; } - __rxrpc_unuse_local(local); + __rxrpc_unuse_local(local, rxrpc_local_unuse_work); } while (again); - rxrpc_put_local(local); + rxrpc_put_local(local, rxrpc_local_put_queue); } /* @@ -460,6 +467,7 @@ static void rxrpc_local_rcu(struct rcu_head *rcu) ASSERT(!work_pending(&local->processor)); + rxrpc_see_local(local, rxrpc_local_free); kfree(local); _leave(""); } diff --git a/net/rxrpc/output.c b/net/rxrpc/output.c index b5d8eac8c49c..2762b7ada9ae 100644 --- a/net/rxrpc/output.c +++ b/net/rxrpc/output.c @@ -288,8 +288,7 @@ void rxrpc_transmit_ack_packets(struct rxrpc_local *local) LIST_HEAD(queue); int ret; - trace_rxrpc_local(local->debug_id, rxrpc_local_tx_ack, - refcount_read(&local->ref), NULL); + rxrpc_see_local(local, rxrpc_local_see_tx_ack); if (list_empty(&local->ack_tx_queue)) return; diff --git a/net/rxrpc/peer_event.c b/net/rxrpc/peer_event.c index ad4d1769e02b..3f8d104ecaa7 100644 --- a/net/rxrpc/peer_event.c +++ b/net/rxrpc/peer_event.c @@ -266,7 +266,7 @@ static void rxrpc_peer_keepalive_dispatch(struct rxrpc_net *rxnet, if (!rxrpc_get_peer_maybe(peer)) continue; - if (__rxrpc_use_local(peer->local)) { + if (__rxrpc_use_local(peer->local, rxrpc_local_use_peer_keepalive)) { spin_unlock_bh(&rxnet->peer_hash_lock); keepalive_at = peer->last_tx_at + RXRPC_KEEPALIVE_TIME; @@ -289,7 +289,7 @@ static void rxrpc_peer_keepalive_dispatch(struct rxrpc_net *rxnet, spin_lock_bh(&rxnet->peer_hash_lock); list_add_tail(&peer->keepalive_link, &rxnet->peer_keepalive[slot & mask]); - rxrpc_unuse_local(peer->local); + rxrpc_unuse_local(peer->local, rxrpc_local_unuse_peer_keepalive); } rxrpc_put_peer_locked(peer); } diff --git a/net/rxrpc/peer_object.c b/net/rxrpc/peer_object.c index b3c3c1c344fc..bcef897560e7 100644 --- a/net/rxrpc/peer_object.c +++ b/net/rxrpc/peer_object.c @@ -215,7 +215,7 @@ struct rxrpc_peer *rxrpc_alloc_peer(struct rxrpc_local *local, gfp_t gfp) peer = kzalloc(sizeof(struct rxrpc_peer), gfp); if (peer) { refcount_set(&peer->ref, 1); - peer->local = rxrpc_get_local(local); + peer->local = rxrpc_get_local(local, rxrpc_local_get_peer); INIT_HLIST_HEAD(&peer->error_targets); peer->service_conns = RB_ROOT; seqlock_init(&peer->service_conn_lock); @@ -294,7 +294,7 @@ static struct rxrpc_peer *rxrpc_create_peer(struct rxrpc_sock *rx, static void rxrpc_free_peer(struct rxrpc_peer *peer) { - rxrpc_put_local(peer->local); + rxrpc_put_local(peer->local, rxrpc_local_put_peer); kfree_rcu(peer, rcu); } From 47c810a79844462d3468d831edc00971757693e0 Mon Sep 17 00:00:00 2001 From: David Howells Date: Fri, 21 Oct 2022 13:39:34 +0100 Subject: [PATCH 1410/2002] rxrpc: trace: Don't use __builtin_return_address for rxrpc_peer tracing In rxrpc tracing, use enums to generate lists of points of interest rather than __builtin_return_address() for the rxrpc_peer tracepoint Signed-off-by: David Howells cc: Marc Dionne cc: linux-afs@lists.infradead.org --- include/trace/events/rxrpc.h | 43 ++++++++++++++++++++++-------------- net/rxrpc/af_rxrpc.c | 2 +- net/rxrpc/ar-internal.h | 11 ++++----- net/rxrpc/call_accept.c | 8 ++++--- net/rxrpc/call_object.c | 2 +- net/rxrpc/conn_client.c | 8 +++---- net/rxrpc/conn_object.c | 2 +- net/rxrpc/peer_event.c | 8 +++---- net/rxrpc/peer_object.c | 34 ++++++++++++++-------------- net/rxrpc/sendmsg.c | 2 +- 10 files changed, 65 insertions(+), 55 deletions(-) diff --git a/include/trace/events/rxrpc.h b/include/trace/events/rxrpc.h index 015569845b1d..1c74143a51c1 100644 --- a/include/trace/events/rxrpc.h +++ b/include/trace/events/rxrpc.h @@ -63,10 +63,23 @@ E_(rxrpc_local_use_work, "USE work ") #define rxrpc_peer_traces \ - EM(rxrpc_peer_got, "GOT") \ - EM(rxrpc_peer_new, "NEW") \ - EM(rxrpc_peer_processing, "PRO") \ - E_(rxrpc_peer_put, "PUT") + EM(rxrpc_peer_free, "FREE ") \ + EM(rxrpc_peer_get_accept, "GET accept ") \ + EM(rxrpc_peer_get_activate_call, "GET act-call") \ + EM(rxrpc_peer_get_bundle, "GET bundle ") \ + EM(rxrpc_peer_get_client_conn, "GET cln-conn") \ + EM(rxrpc_peer_get_input_error, "GET inpt-err") \ + EM(rxrpc_peer_get_keepalive, "GET keepaliv") \ + EM(rxrpc_peer_get_lookup_client, "GET look-cln") \ + EM(rxrpc_peer_get_service_conn, "GET srv-conn") \ + EM(rxrpc_peer_new_client, "NEW client ") \ + EM(rxrpc_peer_new_prealloc, "NEW prealloc") \ + EM(rxrpc_peer_put_bundle, "PUT bundle ") \ + EM(rxrpc_peer_put_call, "PUT call ") \ + EM(rxrpc_peer_put_conn, "PUT conn ") \ + EM(rxrpc_peer_put_discard_tmp, "PUT disc-tmp") \ + EM(rxrpc_peer_put_input_error, "PUT inpt-err") \ + E_(rxrpc_peer_put_keepalive, "PUT keepaliv") #define rxrpc_conn_traces \ EM(rxrpc_conn_got, "GOT") \ @@ -394,30 +407,26 @@ TRACE_EVENT(rxrpc_local, ); TRACE_EVENT(rxrpc_peer, - TP_PROTO(unsigned int peer_debug_id, enum rxrpc_peer_trace op, - int usage, const void *where), + TP_PROTO(unsigned int peer_debug_id, int ref, enum rxrpc_peer_trace why), - TP_ARGS(peer_debug_id, op, usage, where), + TP_ARGS(peer_debug_id, ref, why), TP_STRUCT__entry( __field(unsigned int, peer ) - __field(int, op ) - __field(int, usage ) - __field(const void *, where ) + __field(int, ref ) + __field(int, why ) ), TP_fast_assign( __entry->peer = peer_debug_id; - __entry->op = op; - __entry->usage = usage; - __entry->where = where; + __entry->ref = ref; + __entry->why = why; ), - TP_printk("P=%08x %s u=%d sp=%pSR", + TP_printk("P=%08x %s r=%d", __entry->peer, - __print_symbolic(__entry->op, rxrpc_peer_traces), - __entry->usage, - __entry->where) + __print_symbolic(__entry->why, rxrpc_peer_traces), + __entry->ref) ); TRACE_EVENT(rxrpc_conn, diff --git a/net/rxrpc/af_rxrpc.c b/net/rxrpc/af_rxrpc.c index 989ebca899f3..7a0dc01741e7 100644 --- a/net/rxrpc/af_rxrpc.c +++ b/net/rxrpc/af_rxrpc.c @@ -328,7 +328,7 @@ struct rxrpc_call *rxrpc_kernel_begin_call(struct socket *sock, mutex_unlock(&call->user_mutex); } - rxrpc_put_peer(cp.peer); + rxrpc_put_peer(cp.peer, rxrpc_peer_put_discard_tmp); _leave(" = %p", call); return call; } diff --git a/net/rxrpc/ar-internal.h b/net/rxrpc/ar-internal.h index dde9ce21ef48..6cb111e9761c 100644 --- a/net/rxrpc/ar-internal.h +++ b/net/rxrpc/ar-internal.h @@ -1063,14 +1063,15 @@ struct rxrpc_peer *rxrpc_lookup_peer_rcu(struct rxrpc_local *, const struct sockaddr_rxrpc *); struct rxrpc_peer *rxrpc_lookup_peer(struct rxrpc_sock *, struct rxrpc_local *, struct sockaddr_rxrpc *, gfp_t); -struct rxrpc_peer *rxrpc_alloc_peer(struct rxrpc_local *, gfp_t); +struct rxrpc_peer *rxrpc_alloc_peer(struct rxrpc_local *, gfp_t, + enum rxrpc_peer_trace); void rxrpc_new_incoming_peer(struct rxrpc_sock *, struct rxrpc_local *, struct rxrpc_peer *); void rxrpc_destroy_all_peers(struct rxrpc_net *); -struct rxrpc_peer *rxrpc_get_peer(struct rxrpc_peer *); -struct rxrpc_peer *rxrpc_get_peer_maybe(struct rxrpc_peer *); -void rxrpc_put_peer(struct rxrpc_peer *); -void rxrpc_put_peer_locked(struct rxrpc_peer *); +struct rxrpc_peer *rxrpc_get_peer(struct rxrpc_peer *, enum rxrpc_peer_trace); +struct rxrpc_peer *rxrpc_get_peer_maybe(struct rxrpc_peer *, enum rxrpc_peer_trace); +void rxrpc_put_peer(struct rxrpc_peer *, enum rxrpc_peer_trace); +void rxrpc_put_peer_locked(struct rxrpc_peer *, enum rxrpc_peer_trace); /* * proc.c diff --git a/net/rxrpc/call_accept.c b/net/rxrpc/call_accept.c index 1b12d4e28373..f6bc3b07c3e5 100644 --- a/net/rxrpc/call_accept.c +++ b/net/rxrpc/call_accept.c @@ -70,7 +70,9 @@ static int rxrpc_service_prealloc_one(struct rxrpc_sock *rx, head = b->peer_backlog_head; tail = READ_ONCE(b->peer_backlog_tail); if (CIRC_CNT(head, tail, size) < max) { - struct rxrpc_peer *peer = rxrpc_alloc_peer(rx->local, gfp); + struct rxrpc_peer *peer; + + peer = rxrpc_alloc_peer(rx->local, gfp, rxrpc_peer_new_prealloc); if (!peer) return -ENOMEM; b->peer_backlog[head] = peer; @@ -286,7 +288,7 @@ static struct rxrpc_call *rxrpc_alloc_incoming_call(struct rxrpc_sock *rx, return NULL; if (!conn) { - if (peer && !rxrpc_get_peer_maybe(peer)) + if (peer && !rxrpc_get_peer_maybe(peer, rxrpc_peer_get_service_conn)) peer = NULL; if (!peer) { peer = b->peer_backlog[peer_tail]; @@ -323,7 +325,7 @@ static struct rxrpc_call *rxrpc_alloc_incoming_call(struct rxrpc_sock *rx, call->conn = conn; call->security = conn->security; call->security_ix = conn->security_ix; - call->peer = rxrpc_get_peer(conn->peer); + call->peer = rxrpc_get_peer(conn->peer, rxrpc_peer_get_accept); call->cong_ssthresh = call->peer->cong_ssthresh; call->tx_last_sent = ktime_get_real(); return call; diff --git a/net/rxrpc/call_object.c b/net/rxrpc/call_object.c index 59928f0a8fe1..1b725afd6e2c 100644 --- a/net/rxrpc/call_object.c +++ b/net/rxrpc/call_object.c @@ -636,7 +636,7 @@ static void rxrpc_destroy_call(struct work_struct *work) rxrpc_delete_call_timer(call); rxrpc_put_connection(call->conn); - rxrpc_put_peer(call->peer); + rxrpc_put_peer(call->peer, rxrpc_peer_put_call); kmem_cache_free(rxrpc_call_jar, call); if (atomic_dec_and_test(&rxnet->nr_calls)) wake_up_var(&rxnet->nr_calls); diff --git a/net/rxrpc/conn_client.c b/net/rxrpc/conn_client.c index 9a69b4c1b182..9444da235a48 100644 --- a/net/rxrpc/conn_client.c +++ b/net/rxrpc/conn_client.c @@ -123,7 +123,7 @@ static struct rxrpc_bundle *rxrpc_alloc_bundle(struct rxrpc_conn_parameters *cp, bundle = kzalloc(sizeof(*bundle), gfp); if (bundle) { bundle->local = cp->local; - bundle->peer = rxrpc_get_peer(cp->peer); + bundle->peer = rxrpc_get_peer(cp->peer, rxrpc_peer_get_bundle); bundle->key = cp->key; bundle->exclusive = cp->exclusive; bundle->upgrade = cp->upgrade; @@ -145,7 +145,7 @@ struct rxrpc_bundle *rxrpc_get_bundle(struct rxrpc_bundle *bundle) static void rxrpc_free_bundle(struct rxrpc_bundle *bundle) { - rxrpc_put_peer(bundle->peer); + rxrpc_put_peer(bundle->peer, rxrpc_peer_put_bundle); kfree(bundle); } @@ -207,7 +207,7 @@ rxrpc_alloc_client_connection(struct rxrpc_bundle *bundle, gfp_t gfp) write_unlock(&rxnet->conn_lock); rxrpc_get_bundle(bundle); - rxrpc_get_peer(conn->peer); + rxrpc_get_peer(conn->peer, rxrpc_peer_get_client_conn); rxrpc_get_local(conn->local, rxrpc_local_get_client_conn); key_get(conn->key); @@ -543,7 +543,7 @@ static void rxrpc_activate_one_channel(struct rxrpc_connection *conn, rxrpc_see_call(call); list_del_init(&call->chan_wait_link); - call->peer = rxrpc_get_peer(conn->peer); + call->peer = rxrpc_get_peer(conn->peer, rxrpc_peer_get_activate_call); call->conn = rxrpc_get_connection(conn); call->cid = conn->proto.cid | channel; call->call_id = call_id; diff --git a/net/rxrpc/conn_object.c b/net/rxrpc/conn_object.c index 725359afeac0..554ee5dd3325 100644 --- a/net/rxrpc/conn_object.c +++ b/net/rxrpc/conn_object.c @@ -362,7 +362,7 @@ static void rxrpc_destroy_connection(struct rcu_head *rcu) conn->security->clear(conn); key_put(conn->key); rxrpc_put_bundle(conn->bundle); - rxrpc_put_peer(conn->peer); + rxrpc_put_peer(conn->peer, rxrpc_peer_put_conn); if (atomic_dec_and_test(&conn->local->rxnet->nr_conns)) wake_up_var(&conn->local->rxnet->nr_conns); diff --git a/net/rxrpc/peer_event.c b/net/rxrpc/peer_event.c index 3f8d104ecaa7..5e97d321ac38 100644 --- a/net/rxrpc/peer_event.c +++ b/net/rxrpc/peer_event.c @@ -168,7 +168,7 @@ void rxrpc_error_report(struct sock *sk) } peer = rxrpc_lookup_peer_local_rcu(local, skb, &srx); - if (peer && !rxrpc_get_peer_maybe(peer)) + if (peer && !rxrpc_get_peer_maybe(peer, rxrpc_peer_get_input_error)) peer = NULL; if (!peer) { rcu_read_unlock(); @@ -190,7 +190,7 @@ void rxrpc_error_report(struct sock *sk) out: rcu_read_unlock(); rxrpc_free_skb(skb, rxrpc_skb_freed); - rxrpc_put_peer(peer); + rxrpc_put_peer(peer, rxrpc_peer_put_input_error); _leave(""); } @@ -263,7 +263,7 @@ static void rxrpc_peer_keepalive_dispatch(struct rxrpc_net *rxnet, struct rxrpc_peer, keepalive_link); list_del_init(&peer->keepalive_link); - if (!rxrpc_get_peer_maybe(peer)) + if (!rxrpc_get_peer_maybe(peer, rxrpc_peer_get_keepalive)) continue; if (__rxrpc_use_local(peer->local, rxrpc_local_use_peer_keepalive)) { @@ -291,7 +291,7 @@ static void rxrpc_peer_keepalive_dispatch(struct rxrpc_net *rxnet, &rxnet->peer_keepalive[slot & mask]); rxrpc_unuse_local(peer->local, rxrpc_local_unuse_peer_keepalive); } - rxrpc_put_peer_locked(peer); + rxrpc_put_peer_locked(peer, rxrpc_peer_put_keepalive); } spin_unlock_bh(&rxnet->peer_hash_lock); diff --git a/net/rxrpc/peer_object.c b/net/rxrpc/peer_object.c index bcef897560e7..9e682a60a800 100644 --- a/net/rxrpc/peer_object.c +++ b/net/rxrpc/peer_object.c @@ -205,9 +205,9 @@ static void rxrpc_assess_MTU_size(struct rxrpc_sock *rx, /* * Allocate a peer. */ -struct rxrpc_peer *rxrpc_alloc_peer(struct rxrpc_local *local, gfp_t gfp) +struct rxrpc_peer *rxrpc_alloc_peer(struct rxrpc_local *local, gfp_t gfp, + enum rxrpc_peer_trace why) { - const void *here = __builtin_return_address(0); struct rxrpc_peer *peer; _enter(""); @@ -226,7 +226,7 @@ struct rxrpc_peer *rxrpc_alloc_peer(struct rxrpc_local *local, gfp_t gfp) rxrpc_peer_init_rtt(peer); peer->cong_ssthresh = RXRPC_TX_MAX_WINDOW; - trace_rxrpc_peer(peer->debug_id, rxrpc_peer_new, 1, here); + trace_rxrpc_peer(peer->debug_id, why, 1); } _leave(" = %p", peer); @@ -282,7 +282,7 @@ static struct rxrpc_peer *rxrpc_create_peer(struct rxrpc_sock *rx, _enter(""); - peer = rxrpc_alloc_peer(local, gfp); + peer = rxrpc_alloc_peer(local, gfp, rxrpc_peer_new_client); if (peer) { memcpy(&peer->srx, srx, sizeof(*srx)); rxrpc_init_peer(rx, peer, hash_key); @@ -294,6 +294,7 @@ static struct rxrpc_peer *rxrpc_create_peer(struct rxrpc_sock *rx, static void rxrpc_free_peer(struct rxrpc_peer *peer) { + trace_rxrpc_peer(peer->debug_id, 0, rxrpc_peer_free); rxrpc_put_local(peer->local, rxrpc_local_put_peer); kfree_rcu(peer, rcu); } @@ -334,7 +335,7 @@ struct rxrpc_peer *rxrpc_lookup_peer(struct rxrpc_sock *rx, /* search the peer list first */ rcu_read_lock(); peer = __rxrpc_lookup_peer_rcu(local, srx, hash_key); - if (peer && !rxrpc_get_peer_maybe(peer)) + if (peer && !rxrpc_get_peer_maybe(peer, rxrpc_peer_get_lookup_client)) peer = NULL; rcu_read_unlock(); @@ -352,7 +353,7 @@ struct rxrpc_peer *rxrpc_lookup_peer(struct rxrpc_sock *rx, /* Need to check that we aren't racing with someone else */ peer = __rxrpc_lookup_peer_rcu(local, srx, hash_key); - if (peer && !rxrpc_get_peer_maybe(peer)) + if (peer && !rxrpc_get_peer_maybe(peer, rxrpc_peer_get_lookup_client)) peer = NULL; if (!peer) { hash_add_rcu(rxnet->peer_hash, @@ -376,27 +377,26 @@ struct rxrpc_peer *rxrpc_lookup_peer(struct rxrpc_sock *rx, /* * Get a ref on a peer record. */ -struct rxrpc_peer *rxrpc_get_peer(struct rxrpc_peer *peer) +struct rxrpc_peer *rxrpc_get_peer(struct rxrpc_peer *peer, enum rxrpc_peer_trace why) { - const void *here = __builtin_return_address(0); int r; __refcount_inc(&peer->ref, &r); - trace_rxrpc_peer(peer->debug_id, rxrpc_peer_got, r + 1, here); + trace_rxrpc_peer(peer->debug_id, why, r + 1); return peer; } /* * Get a ref on a peer record unless its usage has already reached 0. */ -struct rxrpc_peer *rxrpc_get_peer_maybe(struct rxrpc_peer *peer) +struct rxrpc_peer *rxrpc_get_peer_maybe(struct rxrpc_peer *peer, + enum rxrpc_peer_trace why) { - const void *here = __builtin_return_address(0); int r; if (peer) { if (__refcount_inc_not_zero(&peer->ref, &r)) - trace_rxrpc_peer(peer->debug_id, rxrpc_peer_got, r + 1, here); + trace_rxrpc_peer(peer->debug_id, r + 1, why); else peer = NULL; } @@ -423,9 +423,8 @@ static void __rxrpc_put_peer(struct rxrpc_peer *peer) /* * Drop a ref on a peer record. */ -void rxrpc_put_peer(struct rxrpc_peer *peer) +void rxrpc_put_peer(struct rxrpc_peer *peer, enum rxrpc_peer_trace why) { - const void *here = __builtin_return_address(0); unsigned int debug_id; bool dead; int r; @@ -433,7 +432,7 @@ void rxrpc_put_peer(struct rxrpc_peer *peer) if (peer) { debug_id = peer->debug_id; dead = __refcount_dec_and_test(&peer->ref, &r); - trace_rxrpc_peer(debug_id, rxrpc_peer_put, r - 1, here); + trace_rxrpc_peer(debug_id, r - 1, why); if (dead) __rxrpc_put_peer(peer); } @@ -443,15 +442,14 @@ void rxrpc_put_peer(struct rxrpc_peer *peer) * Drop a ref on a peer record where the caller already holds the * peer_hash_lock. */ -void rxrpc_put_peer_locked(struct rxrpc_peer *peer) +void rxrpc_put_peer_locked(struct rxrpc_peer *peer, enum rxrpc_peer_trace why) { - const void *here = __builtin_return_address(0); unsigned int debug_id = peer->debug_id; bool dead; int r; dead = __refcount_dec_and_test(&peer->ref, &r); - trace_rxrpc_peer(debug_id, rxrpc_peer_put, r - 1, here); + trace_rxrpc_peer(debug_id, r - 1, why); if (dead) { hash_del_rcu(&peer->hash_link); list_del_init(&peer->keepalive_link); diff --git a/net/rxrpc/sendmsg.c b/net/rxrpc/sendmsg.c index e5fd8a95bf71..cfe0badba0b3 100644 --- a/net/rxrpc/sendmsg.c +++ b/net/rxrpc/sendmsg.c @@ -604,7 +604,7 @@ rxrpc_new_client_call_for_sendmsg(struct rxrpc_sock *rx, struct msghdr *msg, atomic_inc_return(&rxrpc_debug_id)); /* The socket is now unlocked */ - rxrpc_put_peer(cp.peer); + rxrpc_put_peer(cp.peer, rxrpc_peer_put_discard_tmp); _leave(" = %p\n", call); return call; } From 7fa25105b2d32fcb0f38668bc20d0adf6508322f Mon Sep 17 00:00:00 2001 From: David Howells Date: Fri, 21 Oct 2022 14:06:16 +0100 Subject: [PATCH 1411/2002] rxrpc: trace: Don't use __builtin_return_address for rxrpc_conn tracing In rxrpc tracing, use enums to generate lists of points of interest rather than __builtin_return_address() for the rxrpc_conn tracepoint Signed-off-by: David Howells cc: Marc Dionne cc: linux-afs@lists.infradead.org --- include/trace/events/rxrpc.h | 58 +++++++++++++++++++++++------------- net/rxrpc/ar-internal.h | 21 +++++++------ net/rxrpc/call_accept.c | 9 ++---- net/rxrpc/call_object.c | 2 +- net/rxrpc/conn_client.c | 28 ++++++++--------- net/rxrpc/conn_event.c | 4 +-- net/rxrpc/conn_object.c | 40 +++++++++++++------------ net/rxrpc/conn_service.c | 4 +-- net/rxrpc/input.c | 2 +- 9 files changed, 92 insertions(+), 76 deletions(-) diff --git a/include/trace/events/rxrpc.h b/include/trace/events/rxrpc.h index 1c74143a51c1..e09568a8c173 100644 --- a/include/trace/events/rxrpc.h +++ b/include/trace/events/rxrpc.h @@ -82,14 +82,34 @@ E_(rxrpc_peer_put_keepalive, "PUT keepaliv") #define rxrpc_conn_traces \ - EM(rxrpc_conn_got, "GOT") \ - EM(rxrpc_conn_new_client, "NWc") \ - EM(rxrpc_conn_new_service, "NWs") \ - EM(rxrpc_conn_put_client, "PTc") \ - EM(rxrpc_conn_put_service, "PTs") \ - EM(rxrpc_conn_queued, "QUE") \ - EM(rxrpc_conn_reap_service, "RPs") \ - E_(rxrpc_conn_seen, "SEE") + EM(rxrpc_conn_free, "FREE ") \ + EM(rxrpc_conn_get_activate_call, "GET act-call") \ + EM(rxrpc_conn_get_call_input, "GET inp-call") \ + EM(rxrpc_conn_get_conn_input, "GET inp-conn") \ + EM(rxrpc_conn_get_idle, "GET idle ") \ + EM(rxrpc_conn_get_poke, "GET poke ") \ + EM(rxrpc_conn_get_service_conn, "GET svc-conn") \ + EM(rxrpc_conn_new_client, "NEW client ") \ + EM(rxrpc_conn_new_service, "NEW service ") \ + EM(rxrpc_conn_put_already_queued, "PUT alreadyq") \ + EM(rxrpc_conn_put_call, "PUT call ") \ + EM(rxrpc_conn_put_call_input, "PUT inp-call") \ + EM(rxrpc_conn_put_conn_input, "PUT inp-conn") \ + EM(rxrpc_conn_put_discard, "PUT discard ") \ + EM(rxrpc_conn_put_discard_idle, "PUT disc-idl") \ + EM(rxrpc_conn_put_local_dead, "PUT loc-dead") \ + EM(rxrpc_conn_put_noreuse, "PUT noreuse ") \ + EM(rxrpc_conn_put_poke, "PUT poke ") \ + EM(rxrpc_conn_put_unbundle, "PUT unbundle") \ + EM(rxrpc_conn_put_unidle, "PUT unidle ") \ + EM(rxrpc_conn_put_work, "PUT work ") \ + EM(rxrpc_conn_queue_challenge, "GQ chall ") \ + EM(rxrpc_conn_queue_retry_work, "GQ retry-wk") \ + EM(rxrpc_conn_queue_rx_work, "GQ rx-work ") \ + EM(rxrpc_conn_queue_timer, "GQ timer ") \ + EM(rxrpc_conn_see_new_service_conn, "SEE new-svc ") \ + EM(rxrpc_conn_see_reap_service, "SEE reap-svc") \ + E_(rxrpc_conn_see_work, "SEE work ") #define rxrpc_client_traces \ EM(rxrpc_client_activate_chans, "Activa") \ @@ -430,30 +450,26 @@ TRACE_EVENT(rxrpc_peer, ); TRACE_EVENT(rxrpc_conn, - TP_PROTO(unsigned int conn_debug_id, enum rxrpc_conn_trace op, - int usage, const void *where), + TP_PROTO(unsigned int conn_debug_id, int ref, enum rxrpc_conn_trace why), - TP_ARGS(conn_debug_id, op, usage, where), + TP_ARGS(conn_debug_id, ref, why), TP_STRUCT__entry( __field(unsigned int, conn ) - __field(int, op ) - __field(int, usage ) - __field(const void *, where ) + __field(int, ref ) + __field(int, why ) ), TP_fast_assign( __entry->conn = conn_debug_id; - __entry->op = op; - __entry->usage = usage; - __entry->where = where; + __entry->ref = ref; + __entry->why = why; ), - TP_printk("C=%08x %s u=%d sp=%pSR", + TP_printk("C=%08x %s r=%d", __entry->conn, - __print_symbolic(__entry->op, rxrpc_conn_traces), - __entry->usage, - __entry->where) + __print_symbolic(__entry->why, rxrpc_conn_traces), + __entry->ref) ); TRACE_EVENT(rxrpc_client, diff --git a/net/rxrpc/ar-internal.h b/net/rxrpc/ar-internal.h index 6cb111e9761c..bc8281c410c5 100644 --- a/net/rxrpc/ar-internal.h +++ b/net/rxrpc/ar-internal.h @@ -882,7 +882,7 @@ int rxrpc_connect_call(struct rxrpc_sock *, struct rxrpc_call *, gfp_t); void rxrpc_expose_client_call(struct rxrpc_call *); void rxrpc_disconnect_client_call(struct rxrpc_bundle *, struct rxrpc_call *); -void rxrpc_put_client_conn(struct rxrpc_connection *); +void rxrpc_put_client_conn(struct rxrpc_connection *, enum rxrpc_conn_trace); void rxrpc_discard_expired_client_conns(struct work_struct *); void rxrpc_destroy_all_client_connections(struct rxrpc_net *); void rxrpc_clean_up_local_conns(struct rxrpc_local *); @@ -906,11 +906,13 @@ struct rxrpc_connection *rxrpc_find_connection_rcu(struct rxrpc_local *, void __rxrpc_disconnect_call(struct rxrpc_connection *, struct rxrpc_call *); void rxrpc_disconnect_call(struct rxrpc_call *); void rxrpc_kill_connection(struct rxrpc_connection *); -bool rxrpc_queue_conn(struct rxrpc_connection *); -void rxrpc_see_connection(struct rxrpc_connection *); -struct rxrpc_connection *rxrpc_get_connection(struct rxrpc_connection *); -struct rxrpc_connection *rxrpc_get_connection_maybe(struct rxrpc_connection *); -void rxrpc_put_service_conn(struct rxrpc_connection *); +bool rxrpc_queue_conn(struct rxrpc_connection *, enum rxrpc_conn_trace); +void rxrpc_see_connection(struct rxrpc_connection *, enum rxrpc_conn_trace); +struct rxrpc_connection *rxrpc_get_connection(struct rxrpc_connection *, + enum rxrpc_conn_trace); +struct rxrpc_connection *rxrpc_get_connection_maybe(struct rxrpc_connection *, + enum rxrpc_conn_trace); +void rxrpc_put_service_conn(struct rxrpc_connection *, enum rxrpc_conn_trace); void rxrpc_service_connection_reaper(struct work_struct *); void rxrpc_destroy_all_connections(struct rxrpc_net *); @@ -924,15 +926,16 @@ static inline bool rxrpc_conn_is_service(const struct rxrpc_connection *conn) return !rxrpc_conn_is_client(conn); } -static inline void rxrpc_put_connection(struct rxrpc_connection *conn) +static inline void rxrpc_put_connection(struct rxrpc_connection *conn, + enum rxrpc_conn_trace why) { if (!conn) return; if (rxrpc_conn_is_client(conn)) - rxrpc_put_client_conn(conn); + rxrpc_put_client_conn(conn, why); else - rxrpc_put_service_conn(conn); + rxrpc_put_service_conn(conn, why); } static inline void rxrpc_reduce_conn_timer(struct rxrpc_connection *conn, diff --git a/net/rxrpc/call_accept.c b/net/rxrpc/call_accept.c index f6bc3b07c3e5..04b52e28e0cc 100644 --- a/net/rxrpc/call_accept.c +++ b/net/rxrpc/call_accept.c @@ -91,9 +91,6 @@ static int rxrpc_service_prealloc_one(struct rxrpc_sock *rx, b->conn_backlog[head] = conn; smp_store_release(&b->conn_backlog_head, (head + 1) & (size - 1)); - - trace_rxrpc_conn(conn->debug_id, rxrpc_conn_new_service, - refcount_read(&conn->ref), here); } /* Now it gets complicated, because calls get registered with the @@ -309,10 +306,10 @@ static struct rxrpc_call *rxrpc_alloc_incoming_call(struct rxrpc_sock *rx, (conn_tail + 1) & (RXRPC_BACKLOG_MAX - 1)); conn->local = rxrpc_get_local(local, rxrpc_local_get_prealloc_conn); conn->peer = peer; - rxrpc_see_connection(conn); + rxrpc_see_connection(conn, rxrpc_conn_see_new_service_conn); rxrpc_new_incoming_connection(rx, conn, sec, skb); } else { - rxrpc_get_connection(conn); + rxrpc_get_connection(conn, rxrpc_conn_get_service_conn); } /* And now we can allocate and set up a new call */ @@ -402,7 +399,7 @@ struct rxrpc_call *rxrpc_new_incoming_call(struct rxrpc_local *local, case RXRPC_CONN_SERVICE_UNSECURED: conn->state = RXRPC_CONN_SERVICE_CHALLENGING; set_bit(RXRPC_CONN_EV_CHALLENGE, &call->conn->events); - rxrpc_queue_conn(call->conn); + rxrpc_queue_conn(call->conn, rxrpc_conn_queue_challenge); break; case RXRPC_CONN_SERVICE: diff --git a/net/rxrpc/call_object.c b/net/rxrpc/call_object.c index 1b725afd6e2c..29ec4013aa0b 100644 --- a/net/rxrpc/call_object.c +++ b/net/rxrpc/call_object.c @@ -635,7 +635,7 @@ static void rxrpc_destroy_call(struct work_struct *work) rxrpc_delete_call_timer(call); - rxrpc_put_connection(call->conn); + rxrpc_put_connection(call->conn, rxrpc_conn_put_call); rxrpc_put_peer(call->peer, rxrpc_peer_put_call); kmem_cache_free(rxrpc_call_jar, call); if (atomic_dec_and_test(&rxnet->nr_calls)) diff --git a/net/rxrpc/conn_client.c b/net/rxrpc/conn_client.c index 9444da235a48..dcfec6a45255 100644 --- a/net/rxrpc/conn_client.c +++ b/net/rxrpc/conn_client.c @@ -211,9 +211,8 @@ rxrpc_alloc_client_connection(struct rxrpc_bundle *bundle, gfp_t gfp) rxrpc_get_local(conn->local, rxrpc_local_get_client_conn); key_get(conn->key); - trace_rxrpc_conn(conn->debug_id, rxrpc_conn_new_client, - refcount_read(&conn->ref), - __builtin_return_address(0)); + trace_rxrpc_conn(conn->debug_id, refcount_read(&conn->ref), + rxrpc_conn_new_client); atomic_inc(&rxnet->nr_client_conns); trace_rxrpc_client(conn, -1, rxrpc_client_alloc); @@ -467,10 +466,10 @@ static void rxrpc_add_conn_to_bundle(struct rxrpc_bundle *bundle, gfp_t gfp) if (candidate) { _debug("discard C=%x", candidate->debug_id); trace_rxrpc_client(candidate, -1, rxrpc_client_duplicate); - rxrpc_put_connection(candidate); + rxrpc_put_connection(candidate, rxrpc_conn_put_discard); } - rxrpc_put_connection(old); + rxrpc_put_connection(old, rxrpc_conn_put_noreuse); _leave(""); } @@ -544,7 +543,7 @@ static void rxrpc_activate_one_channel(struct rxrpc_connection *conn, rxrpc_see_call(call); list_del_init(&call->chan_wait_link); call->peer = rxrpc_get_peer(conn->peer, rxrpc_peer_get_activate_call); - call->conn = rxrpc_get_connection(conn); + call->conn = rxrpc_get_connection(conn, rxrpc_conn_get_activate_call); call->cid = conn->proto.cid | channel; call->call_id = call_id; call->security = conn->security; @@ -592,7 +591,7 @@ static void rxrpc_unidle_conn(struct rxrpc_bundle *bundle, struct rxrpc_connecti } spin_unlock(&rxnet->client_conn_cache_lock); if (drop_ref) - rxrpc_put_connection(conn); + rxrpc_put_connection(conn, rxrpc_conn_put_unidle); } } @@ -896,7 +895,7 @@ void rxrpc_disconnect_client_call(struct rxrpc_bundle *bundle, struct rxrpc_call trace_rxrpc_client(conn, channel, rxrpc_client_to_idle); conn->idle_timestamp = jiffies; - rxrpc_get_connection(conn); + rxrpc_get_connection(conn, rxrpc_conn_get_idle); spin_lock(&rxnet->client_conn_cache_lock); list_move_tail(&conn->cache_link, &rxnet->idle_client_conns); spin_unlock(&rxnet->client_conn_cache_lock); @@ -938,7 +937,7 @@ static void rxrpc_unbundle_conn(struct rxrpc_connection *conn) if (need_drop) { rxrpc_deactivate_bundle(bundle); - rxrpc_put_connection(conn); + rxrpc_put_connection(conn, rxrpc_conn_put_unbundle); } } @@ -983,15 +982,15 @@ static void rxrpc_kill_client_conn(struct rxrpc_connection *conn) /* * Clean up a dead client connections. */ -void rxrpc_put_client_conn(struct rxrpc_connection *conn) +void rxrpc_put_client_conn(struct rxrpc_connection *conn, + enum rxrpc_conn_trace why) { - const void *here = __builtin_return_address(0); unsigned int debug_id = conn->debug_id; bool dead; int r; dead = __refcount_dec_and_test(&conn->ref, &r); - trace_rxrpc_conn(debug_id, rxrpc_conn_put_client, r - 1, here); + trace_rxrpc_conn(debug_id, r - 1, why); if (dead) rxrpc_kill_client_conn(conn); } @@ -1063,7 +1062,8 @@ next: spin_unlock(&rxnet->client_conn_cache_lock); rxrpc_unbundle_conn(conn); - rxrpc_put_connection(conn); /* Drop the ->cache_link ref */ + /* Drop the ->cache_link ref */ + rxrpc_put_connection(conn, rxrpc_conn_put_discard_idle); nr_conns--; goto next; @@ -1134,7 +1134,7 @@ void rxrpc_clean_up_local_conns(struct rxrpc_local *local) struct rxrpc_connection, cache_link); list_del_init(&conn->cache_link); rxrpc_unbundle_conn(conn); - rxrpc_put_connection(conn); + rxrpc_put_connection(conn, rxrpc_conn_put_local_dead); } _leave(" [culled]"); diff --git a/net/rxrpc/conn_event.c b/net/rxrpc/conn_event.c index 225edaf019f1..817f895c77ca 100644 --- a/net/rxrpc/conn_event.c +++ b/net/rxrpc/conn_event.c @@ -472,14 +472,14 @@ void rxrpc_process_connection(struct work_struct *work) struct rxrpc_connection *conn = container_of(work, struct rxrpc_connection, processor); - rxrpc_see_connection(conn); + rxrpc_see_connection(conn, rxrpc_conn_see_work); if (__rxrpc_use_local(conn->local, rxrpc_local_use_conn_work)) { rxrpc_do_process_connection(conn); rxrpc_unuse_local(conn->local, rxrpc_local_unuse_conn_work); } - rxrpc_put_connection(conn); + rxrpc_put_connection(conn, rxrpc_conn_put_work); _leave(""); return; } diff --git a/net/rxrpc/conn_object.c b/net/rxrpc/conn_object.c index 554ee5dd3325..bbace8d9953d 100644 --- a/net/rxrpc/conn_object.c +++ b/net/rxrpc/conn_object.c @@ -26,7 +26,7 @@ static void rxrpc_connection_timer(struct timer_list *timer) struct rxrpc_connection *conn = container_of(timer, struct rxrpc_connection, timer); - rxrpc_queue_conn(conn); + rxrpc_queue_conn(conn, rxrpc_conn_queue_timer); } /* @@ -260,43 +260,42 @@ void rxrpc_kill_connection(struct rxrpc_connection *conn) * Queue a connection's work processor, getting a ref to pass to the work * queue. */ -bool rxrpc_queue_conn(struct rxrpc_connection *conn) +bool rxrpc_queue_conn(struct rxrpc_connection *conn, enum rxrpc_conn_trace why) { - const void *here = __builtin_return_address(0); int r; if (!__refcount_inc_not_zero(&conn->ref, &r)) return false; if (rxrpc_queue_work(&conn->processor)) - trace_rxrpc_conn(conn->debug_id, rxrpc_conn_queued, r + 1, here); + trace_rxrpc_conn(conn->debug_id, why, r + 1); else - rxrpc_put_connection(conn); + rxrpc_put_connection(conn, rxrpc_conn_put_already_queued); return true; } /* * Note the re-emergence of a connection. */ -void rxrpc_see_connection(struct rxrpc_connection *conn) +void rxrpc_see_connection(struct rxrpc_connection *conn, + enum rxrpc_conn_trace why) { - const void *here = __builtin_return_address(0); if (conn) { - int n = refcount_read(&conn->ref); + int r = refcount_read(&conn->ref); - trace_rxrpc_conn(conn->debug_id, rxrpc_conn_seen, n, here); + trace_rxrpc_conn(conn->debug_id, r, why); } } /* * Get a ref on a connection. */ -struct rxrpc_connection *rxrpc_get_connection(struct rxrpc_connection *conn) +struct rxrpc_connection *rxrpc_get_connection(struct rxrpc_connection *conn, + enum rxrpc_conn_trace why) { - const void *here = __builtin_return_address(0); int r; __refcount_inc(&conn->ref, &r); - trace_rxrpc_conn(conn->debug_id, rxrpc_conn_got, r, here); + trace_rxrpc_conn(conn->debug_id, r + 1, why); return conn; } @@ -304,14 +303,14 @@ struct rxrpc_connection *rxrpc_get_connection(struct rxrpc_connection *conn) * Try to get a ref on a connection. */ struct rxrpc_connection * -rxrpc_get_connection_maybe(struct rxrpc_connection *conn) +rxrpc_get_connection_maybe(struct rxrpc_connection *conn, + enum rxrpc_conn_trace why) { - const void *here = __builtin_return_address(0); int r; if (conn) { if (__refcount_inc_not_zero(&conn->ref, &r)) - trace_rxrpc_conn(conn->debug_id, rxrpc_conn_got, r + 1, here); + trace_rxrpc_conn(conn->debug_id, r + 1, why); else conn = NULL; } @@ -331,14 +330,14 @@ static void rxrpc_set_service_reap_timer(struct rxrpc_net *rxnet, /* * Release a service connection */ -void rxrpc_put_service_conn(struct rxrpc_connection *conn) +void rxrpc_put_service_conn(struct rxrpc_connection *conn, + enum rxrpc_conn_trace why) { - const void *here = __builtin_return_address(0); unsigned int debug_id = conn->debug_id; int r; __refcount_dec(&conn->ref, &r); - trace_rxrpc_conn(debug_id, rxrpc_conn_put_service, r - 1, here); + trace_rxrpc_conn(debug_id, r - 1, why); if (r - 1 == 1) rxrpc_set_service_reap_timer(conn->local->rxnet, jiffies + rxrpc_connection_expiry); @@ -354,6 +353,9 @@ static void rxrpc_destroy_connection(struct rcu_head *rcu) _enter("{%d,u=%d}", conn->debug_id, refcount_read(&conn->ref)); + trace_rxrpc_conn(conn->debug_id, refcount_read(&conn->ref), + rxrpc_conn_free); + ASSERTCMP(refcount_read(&conn->ref), ==, 0); del_timer_sync(&conn->timer); @@ -419,7 +421,7 @@ void rxrpc_service_connection_reaper(struct work_struct *work) */ if (!refcount_dec_if_one(&conn->ref)) continue; - trace_rxrpc_conn(conn->debug_id, rxrpc_conn_reap_service, 0, NULL); + rxrpc_see_connection(conn, rxrpc_conn_see_reap_service); if (rxrpc_conn_is_client(conn)) BUG(); diff --git a/net/rxrpc/conn_service.c b/net/rxrpc/conn_service.c index a3b91864ef21..bf087213bd4d 100644 --- a/net/rxrpc/conn_service.c +++ b/net/rxrpc/conn_service.c @@ -141,9 +141,7 @@ struct rxrpc_connection *rxrpc_prealloc_service_connection(struct rxrpc_net *rxn list_add_tail(&conn->proc_link, &rxnet->conn_proc_list); write_unlock(&rxnet->conn_lock); - trace_rxrpc_conn(conn->debug_id, rxrpc_conn_new_service, - refcount_read(&conn->ref), - __builtin_return_address(0)); + rxrpc_see_connection(conn, rxrpc_conn_new_service); } return conn; diff --git a/net/rxrpc/input.c b/net/rxrpc/input.c index cecfd201d832..c8ff7489b412 100644 --- a/net/rxrpc/input.c +++ b/net/rxrpc/input.c @@ -1121,7 +1121,7 @@ static void rxrpc_post_packet_to_conn(struct rxrpc_connection *conn, _enter("%p,%p", conn, skb); skb_queue_tail(&conn->rx_queue, skb); - rxrpc_queue_conn(conn); + rxrpc_queue_conn(conn, rxrpc_conn_queue_rx_work); } /* From cb0fc0c9722c0c001510e2a6d9b0a78b80421487 Mon Sep 17 00:00:00 2001 From: David Howells Date: Fri, 21 Oct 2022 14:39:26 +0100 Subject: [PATCH 1412/2002] rxrpc: trace: Don't use __builtin_return_address for rxrpc_call tracing In rxrpc tracing, use enums to generate lists of points of interest rather than __builtin_return_address() for the rxrpc_call tracepoint Signed-off-by: David Howells cc: Marc Dionne cc: linux-afs@lists.infradead.org --- include/trace/events/rxrpc.h | 83 ++++++++++++++++------------ net/rxrpc/ar-internal.h | 8 +-- net/rxrpc/call_accept.c | 16 +++--- net/rxrpc/call_event.c | 8 +-- net/rxrpc/call_object.c | 102 +++++++++++++++-------------------- net/rxrpc/conn_client.c | 2 +- net/rxrpc/input.c | 8 +-- net/rxrpc/output.c | 2 +- net/rxrpc/peer_event.c | 2 +- net/rxrpc/recvmsg.c | 8 +-- net/rxrpc/sendmsg.c | 4 +- 11 files changed, 121 insertions(+), 122 deletions(-) diff --git a/include/trace/events/rxrpc.h b/include/trace/events/rxrpc.h index e09568a8c173..3f6de4294148 100644 --- a/include/trace/events/rxrpc.h +++ b/include/trace/events/rxrpc.h @@ -127,26 +127,44 @@ E_(rxrpc_client_to_idle, "->Idle") #define rxrpc_call_traces \ - EM(rxrpc_call_connected, "CON") \ - EM(rxrpc_call_error, "*E*") \ - EM(rxrpc_call_got, "GOT") \ - EM(rxrpc_call_got_kernel, "Gke") \ - EM(rxrpc_call_got_timer, "GTM") \ - EM(rxrpc_call_got_tx, "Gtx") \ - EM(rxrpc_call_got_userid, "Gus") \ - EM(rxrpc_call_new_client, "NWc") \ - EM(rxrpc_call_new_service, "NWs") \ - EM(rxrpc_call_put, "PUT") \ - EM(rxrpc_call_put_kernel, "Pke") \ - EM(rxrpc_call_put_noqueue, "PnQ") \ - EM(rxrpc_call_put_notimer, "PnT") \ - EM(rxrpc_call_put_timer, "PTM") \ - EM(rxrpc_call_put_tx, "Ptx") \ - EM(rxrpc_call_put_userid, "Pus") \ - EM(rxrpc_call_queued, "QUE") \ - EM(rxrpc_call_queued_ref, "QUR") \ - EM(rxrpc_call_release, "RLS") \ - E_(rxrpc_call_seen, "SEE") + EM(rxrpc_call_get_input, "GET input ") \ + EM(rxrpc_call_get_kernel_service, "GET krnl-srv") \ + EM(rxrpc_call_get_notify_socket, "GET notify ") \ + EM(rxrpc_call_get_recvmsg, "GET recvmsg ") \ + EM(rxrpc_call_get_release_sock, "GET rel-sock") \ + EM(rxrpc_call_get_sendmsg, "GET sendmsg ") \ + EM(rxrpc_call_get_send_ack, "GET send-ack") \ + EM(rxrpc_call_get_timer, "GET timer ") \ + EM(rxrpc_call_get_userid, "GET user-id ") \ + EM(rxrpc_call_new_client, "NEW client ") \ + EM(rxrpc_call_new_prealloc_service, "NEW prealloc") \ + EM(rxrpc_call_put_already_queued, "PUT alreadyq") \ + EM(rxrpc_call_put_discard_prealloc, "PUT disc-pre") \ + EM(rxrpc_call_put_input, "PUT input ") \ + EM(rxrpc_call_put_kernel, "PUT kernel ") \ + EM(rxrpc_call_put_recvmsg, "PUT recvmsg ") \ + EM(rxrpc_call_put_release_sock, "PUT rls-sock") \ + EM(rxrpc_call_put_release_sock_tba, "PUT rls-sk-a") \ + EM(rxrpc_call_put_send_ack, "PUT send-ack") \ + EM(rxrpc_call_put_sendmsg, "PUT sendmsg ") \ + EM(rxrpc_call_put_timer, "PUT timer ") \ + EM(rxrpc_call_put_timer_already, "PUT timer-al") \ + EM(rxrpc_call_put_unnotify, "PUT unnotify") \ + EM(rxrpc_call_put_userid_exists, "PUT u-exists") \ + EM(rxrpc_call_put_work, "PUT work ") \ + EM(rxrpc_call_queue_abort, "QUE abort ") \ + EM(rxrpc_call_queue_requeue, "QUE requeue ") \ + EM(rxrpc_call_queue_resend, "QUE resend ") \ + EM(rxrpc_call_queue_timer, "QUE timer ") \ + EM(rxrpc_call_see_accept, "SEE accept ") \ + EM(rxrpc_call_see_activate_client, "SEE act-clnt") \ + EM(rxrpc_call_see_connect_failed, "SEE con-fail") \ + EM(rxrpc_call_see_connected, "SEE connect ") \ + EM(rxrpc_call_see_distribute_error, "SEE dist-err") \ + EM(rxrpc_call_see_input, "SEE input ") \ + EM(rxrpc_call_see_release, "SEE release ") \ + EM(rxrpc_call_see_userid_exists, "SEE u-exists") \ + E_(rxrpc_call_see_zap, "SEE zap ") #define rxrpc_txqueue_traces \ EM(rxrpc_txqueue_await_reply, "AWR") \ @@ -503,32 +521,29 @@ TRACE_EVENT(rxrpc_client, ); TRACE_EVENT(rxrpc_call, - TP_PROTO(unsigned int call_debug_id, enum rxrpc_call_trace op, - int usage, const void *where, const void *aux), + TP_PROTO(unsigned int call_debug_id, int ref, unsigned long aux, + enum rxrpc_call_trace why), - TP_ARGS(call_debug_id, op, usage, where, aux), + TP_ARGS(call_debug_id, ref, aux, why), TP_STRUCT__entry( __field(unsigned int, call ) - __field(int, op ) - __field(int, usage ) - __field(const void *, where ) - __field(const void *, aux ) + __field(int, ref ) + __field(int, why ) + __field(unsigned long, aux ) ), TP_fast_assign( __entry->call = call_debug_id; - __entry->op = op; - __entry->usage = usage; - __entry->where = where; + __entry->ref = ref; + __entry->why = why; __entry->aux = aux; ), - TP_printk("c=%08x %s u=%d sp=%pSR a=%p", + TP_printk("c=%08x %s r=%d a=%lx", __entry->call, - __print_symbolic(__entry->op, rxrpc_call_traces), - __entry->usage, - __entry->where, + __print_symbolic(__entry->why, rxrpc_call_traces), + __entry->ref, __entry->aux) ); diff --git a/net/rxrpc/ar-internal.h b/net/rxrpc/ar-internal.h index bc8281c410c5..82eb09b961a0 100644 --- a/net/rxrpc/ar-internal.h +++ b/net/rxrpc/ar-internal.h @@ -847,10 +847,10 @@ void rxrpc_incoming_call(struct rxrpc_sock *, struct rxrpc_call *, struct sk_buff *); void rxrpc_release_call(struct rxrpc_sock *, struct rxrpc_call *); void rxrpc_release_calls_on_socket(struct rxrpc_sock *); -bool __rxrpc_queue_call(struct rxrpc_call *); -bool rxrpc_queue_call(struct rxrpc_call *); -void rxrpc_see_call(struct rxrpc_call *); -bool rxrpc_try_get_call(struct rxrpc_call *call, enum rxrpc_call_trace op); +bool __rxrpc_queue_call(struct rxrpc_call *, enum rxrpc_call_trace); +bool rxrpc_queue_call(struct rxrpc_call *, enum rxrpc_call_trace); +void rxrpc_see_call(struct rxrpc_call *, enum rxrpc_call_trace); +bool rxrpc_try_get_call(struct rxrpc_call *, enum rxrpc_call_trace); void rxrpc_get_call(struct rxrpc_call *, enum rxrpc_call_trace); void rxrpc_put_call(struct rxrpc_call *, enum rxrpc_call_trace); void rxrpc_cleanup_call(struct rxrpc_call *); diff --git a/net/rxrpc/call_accept.c b/net/rxrpc/call_accept.c index 04b52e28e0cc..dd4ca4bee77f 100644 --- a/net/rxrpc/call_accept.c +++ b/net/rxrpc/call_accept.c @@ -38,7 +38,6 @@ static int rxrpc_service_prealloc_one(struct rxrpc_sock *rx, unsigned long user_call_ID, gfp_t gfp, unsigned int debug_id) { - const void *here = __builtin_return_address(0); struct rxrpc_call *call, *xcall; struct rxrpc_net *rxnet = rxrpc_net(sock_net(&rx->sk)); struct rb_node *parent, **pp; @@ -102,9 +101,8 @@ static int rxrpc_service_prealloc_one(struct rxrpc_sock *rx, call->flags |= (1 << RXRPC_CALL_IS_SERVICE); call->state = RXRPC_CALL_SERVER_PREALLOC; - trace_rxrpc_call(call->debug_id, rxrpc_call_new_service, - refcount_read(&call->ref), - here, (const void *)user_call_ID); + trace_rxrpc_call(call->debug_id, refcount_read(&call->ref), + user_call_ID, rxrpc_call_new_prealloc_service); write_lock(&rx->call_lock); @@ -125,11 +123,11 @@ static int rxrpc_service_prealloc_one(struct rxrpc_sock *rx, call->user_call_ID = user_call_ID; call->notify_rx = notify_rx; if (user_attach_call) { - rxrpc_get_call(call, rxrpc_call_got_kernel); + rxrpc_get_call(call, rxrpc_call_get_kernel_service); user_attach_call(call, user_call_ID); } - rxrpc_get_call(call, rxrpc_call_got_userid); + rxrpc_get_call(call, rxrpc_call_get_userid); rb_link_node(&call->sock_node, parent, pp); rb_insert_color(&call->sock_node, &rx->calls); set_bit(RXRPC_CALL_HAS_USERID, &call->flags); @@ -229,7 +227,7 @@ void rxrpc_discard_prealloc(struct rxrpc_sock *rx) } rxrpc_call_completed(call); rxrpc_release_call(rx, call); - rxrpc_put_call(call, rxrpc_call_put); + rxrpc_put_call(call, rxrpc_call_put_discard_prealloc); tail = (tail + 1) & (size - 1); } @@ -318,7 +316,7 @@ static struct rxrpc_call *rxrpc_alloc_incoming_call(struct rxrpc_sock *rx, smp_store_release(&b->call_backlog_tail, (call_tail + 1) & (RXRPC_BACKLOG_MAX - 1)); - rxrpc_see_call(call); + rxrpc_see_call(call, rxrpc_call_see_accept); call->conn = conn; call->security = conn->security; call->security_ix = conn->security_ix; @@ -430,7 +428,7 @@ struct rxrpc_call *rxrpc_new_incoming_call(struct rxrpc_local *local, * (recvmsg queue, to-be-accepted queue or user ID tree) or the kernel * service to prevent the call from being deallocated too early. */ - rxrpc_put_call(call, rxrpc_call_put); + rxrpc_put_call(call, rxrpc_call_put_discard_prealloc); _leave(" = %p{%d}", call, call->debug_id); return call; diff --git a/net/rxrpc/call_event.c b/net/rxrpc/call_event.c index 591af8e2e3d0..0c8d2186cda8 100644 --- a/net/rxrpc/call_event.c +++ b/net/rxrpc/call_event.c @@ -101,7 +101,7 @@ void rxrpc_send_ACK(struct rxrpc_call *call, u8 ack_reason, txb->ack.reason = ack_reason; txb->ack.nAcks = 0; - if (!rxrpc_try_get_call(call, rxrpc_call_got)) { + if (!rxrpc_try_get_call(call, rxrpc_call_get_send_ack)) { rxrpc_put_txbuf(txb, rxrpc_txbuf_put_nomem); return; } @@ -302,7 +302,7 @@ void rxrpc_process_call(struct work_struct *work) unsigned int iterations = 0; rxrpc_serial_t ackr_serial; - rxrpc_see_call(call); + rxrpc_see_call(call, rxrpc_call_see_input); //printk("\n--------------------\n"); _enter("{%d,%s,%lx}", @@ -436,12 +436,12 @@ recheck_state: goto requeue; out_put: - rxrpc_put_call(call, rxrpc_call_put); + rxrpc_put_call(call, rxrpc_call_put_work); out: _leave(""); return; requeue: - __rxrpc_queue_call(call); + __rxrpc_queue_call(call, rxrpc_call_queue_requeue); goto out; } diff --git a/net/rxrpc/call_object.c b/net/rxrpc/call_object.c index 29ec4013aa0b..afd957f6dc1c 100644 --- a/net/rxrpc/call_object.c +++ b/net/rxrpc/call_object.c @@ -53,9 +53,9 @@ static void rxrpc_call_timer_expired(struct timer_list *t) if (call->state < RXRPC_CALL_COMPLETE) { trace_rxrpc_timer_expired(call, jiffies); - __rxrpc_queue_call(call); + __rxrpc_queue_call(call, rxrpc_call_queue_timer); } else { - rxrpc_put_call(call, rxrpc_call_put); + rxrpc_put_call(call, rxrpc_call_put_already_queued); } } @@ -64,10 +64,10 @@ void rxrpc_reduce_call_timer(struct rxrpc_call *call, unsigned long now, enum rxrpc_timer_trace why) { - if (rxrpc_try_get_call(call, rxrpc_call_got_timer)) { + if (rxrpc_try_get_call(call, rxrpc_call_get_timer)) { trace_rxrpc_timer(call, why, now); if (timer_reduce(&call->timer, expire_at)) - rxrpc_put_call(call, rxrpc_call_put_notimer); + rxrpc_put_call(call, rxrpc_call_put_timer_already); } } @@ -110,7 +110,7 @@ struct rxrpc_call *rxrpc_find_call_by_user_ID(struct rxrpc_sock *rx, return NULL; found_extant_call: - rxrpc_get_call(call, rxrpc_call_got); + rxrpc_get_call(call, rxrpc_call_get_sendmsg); read_unlock(&rx->call_lock); _leave(" = %p [%d]", call, refcount_read(&call->ref)); return call; @@ -270,7 +270,6 @@ struct rxrpc_call *rxrpc_new_client_call(struct rxrpc_sock *rx, struct rxrpc_net *rxnet; struct semaphore *limiter; struct rb_node *parent, **pp; - const void *here = __builtin_return_address(0); int ret; _enter("%p,%lx", rx, p->user_call_ID); @@ -291,9 +290,8 @@ struct rxrpc_call *rxrpc_new_client_call(struct rxrpc_sock *rx, call->interruptibility = p->interruptibility; call->tx_total_len = p->tx_total_len; - trace_rxrpc_call(call->debug_id, rxrpc_call_new_client, - refcount_read(&call->ref), - here, (const void *)p->user_call_ID); + trace_rxrpc_call(call->debug_id, refcount_read(&call->ref), + p->user_call_ID, rxrpc_call_new_client); if (p->kernel) __set_bit(RXRPC_CALL_KERNEL, &call->flags); @@ -322,7 +320,7 @@ struct rxrpc_call *rxrpc_new_client_call(struct rxrpc_sock *rx, rcu_assign_pointer(call->socket, rx); call->user_call_ID = p->user_call_ID; __set_bit(RXRPC_CALL_HAS_USERID, &call->flags); - rxrpc_get_call(call, rxrpc_call_got_userid); + rxrpc_get_call(call, rxrpc_call_get_userid); rb_link_node(&call->sock_node, parent, pp); rb_insert_color(&call->sock_node, &rx->calls); list_add(&call->sock_link, &rx->sock_calls); @@ -344,8 +342,7 @@ struct rxrpc_call *rxrpc_new_client_call(struct rxrpc_sock *rx, if (ret < 0) goto error_attached_to_socket; - trace_rxrpc_call(call->debug_id, rxrpc_call_connected, - refcount_read(&call->ref), here, NULL); + rxrpc_see_call(call, rxrpc_call_see_connected); rxrpc_start_call_timer(call); @@ -362,11 +359,11 @@ error_dup_user_ID: release_sock(&rx->sk); __rxrpc_set_call_completion(call, RXRPC_CALL_LOCAL_ERROR, RX_CALL_DEAD, -EEXIST); - trace_rxrpc_call(call->debug_id, rxrpc_call_error, - refcount_read(&call->ref), here, ERR_PTR(-EEXIST)); + trace_rxrpc_call(call->debug_id, refcount_read(&call->ref), 0, + rxrpc_call_see_userid_exists); rxrpc_release_call(rx, call); mutex_unlock(&call->user_mutex); - rxrpc_put_call(call, rxrpc_call_put); + rxrpc_put_call(call, rxrpc_call_put_userid_exists); _leave(" = -EEXIST"); return ERR_PTR(-EEXIST); @@ -376,8 +373,8 @@ error_dup_user_ID: * leave the error to recvmsg() to deal with. */ error_attached_to_socket: - trace_rxrpc_call(call->debug_id, rxrpc_call_error, - refcount_read(&call->ref), here, ERR_PTR(ret)); + trace_rxrpc_call(call->debug_id, refcount_read(&call->ref), ret, + rxrpc_call_see_connect_failed); set_bit(RXRPC_CALL_DISCONNECTED, &call->flags); __rxrpc_set_call_completion(call, RXRPC_CALL_LOCAL_ERROR, RX_CALL_DEAD, ret); @@ -428,72 +425,65 @@ void rxrpc_incoming_call(struct rxrpc_sock *rx, /* * Queue a call's work processor, getting a ref to pass to the work queue. */ -bool rxrpc_queue_call(struct rxrpc_call *call) +bool rxrpc_queue_call(struct rxrpc_call *call, enum rxrpc_call_trace why) { - const void *here = __builtin_return_address(0); int n; if (!__refcount_inc_not_zero(&call->ref, &n)) return false; if (rxrpc_queue_work(&call->processor)) - trace_rxrpc_call(call->debug_id, rxrpc_call_queued, n + 1, - here, NULL); + trace_rxrpc_call(call->debug_id, n + 1, 0, why); else - rxrpc_put_call(call, rxrpc_call_put_noqueue); + rxrpc_put_call(call, rxrpc_call_put_already_queued); return true; } /* * Queue a call's work processor, passing the callers ref to the work queue. */ -bool __rxrpc_queue_call(struct rxrpc_call *call) +bool __rxrpc_queue_call(struct rxrpc_call *call, enum rxrpc_call_trace why) { - const void *here = __builtin_return_address(0); int n = refcount_read(&call->ref); + ASSERTCMP(n, >=, 1); if (rxrpc_queue_work(&call->processor)) - trace_rxrpc_call(call->debug_id, rxrpc_call_queued_ref, n, - here, NULL); + trace_rxrpc_call(call->debug_id, n, 0, why); else - rxrpc_put_call(call, rxrpc_call_put_noqueue); + rxrpc_put_call(call, rxrpc_call_put_already_queued); return true; } /* * Note the re-emergence of a call. */ -void rxrpc_see_call(struct rxrpc_call *call) +void rxrpc_see_call(struct rxrpc_call *call, enum rxrpc_call_trace why) { - const void *here = __builtin_return_address(0); if (call) { - int n = refcount_read(&call->ref); + int r = refcount_read(&call->ref); - trace_rxrpc_call(call->debug_id, rxrpc_call_seen, n, - here, NULL); + trace_rxrpc_call(call->debug_id, r, 0, why); } } -bool rxrpc_try_get_call(struct rxrpc_call *call, enum rxrpc_call_trace op) +bool rxrpc_try_get_call(struct rxrpc_call *call, enum rxrpc_call_trace why) { - const void *here = __builtin_return_address(0); - int n; + int r; - if (!__refcount_inc_not_zero(&call->ref, &n)) + if (!__refcount_inc_not_zero(&call->ref, &r)) return false; - trace_rxrpc_call(call->debug_id, op, n + 1, here, NULL); + trace_rxrpc_call(call->debug_id, r + 1, 0, why); return true; } /* * Note the addition of a ref on a call. */ -void rxrpc_get_call(struct rxrpc_call *call, enum rxrpc_call_trace op) +void rxrpc_get_call(struct rxrpc_call *call, enum rxrpc_call_trace why) { - const void *here = __builtin_return_address(0); - int n; + int r; - __refcount_inc(&call->ref, &n); - trace_rxrpc_call(call->debug_id, op, n + 1, here, NULL); + __refcount_inc(&call->ref, &r); + trace_rxrpc_call(call->debug_id, r + 1, 0, why); } /* @@ -510,15 +500,13 @@ static void rxrpc_cleanup_ring(struct rxrpc_call *call) */ void rxrpc_release_call(struct rxrpc_sock *rx, struct rxrpc_call *call) { - const void *here = __builtin_return_address(0); struct rxrpc_connection *conn = call->conn; bool put = false; _enter("{%d,%d}", call->debug_id, refcount_read(&call->ref)); - trace_rxrpc_call(call->debug_id, rxrpc_call_release, - refcount_read(&call->ref), - here, (const void *)call->flags); + trace_rxrpc_call(call->debug_id, refcount_read(&call->ref), + call->flags, rxrpc_call_see_release); ASSERTCMP(call->state, ==, RXRPC_CALL_COMPLETE); @@ -544,14 +532,14 @@ void rxrpc_release_call(struct rxrpc_sock *rx, struct rxrpc_call *call) write_unlock_bh(&rx->recvmsg_lock); if (put) - rxrpc_put_call(call, rxrpc_call_put); + rxrpc_put_call(call, rxrpc_call_put_unnotify); write_lock(&rx->call_lock); if (test_and_clear_bit(RXRPC_CALL_HAS_USERID, &call->flags)) { rb_erase(&call->sock_node, &rx->calls); memset(&call->sock_node, 0xdd, sizeof(call->sock_node)); - rxrpc_put_call(call, rxrpc_call_put_userid); + rxrpc_put_call(call, rxrpc_call_put_userid_exists); } list_del(&call->sock_link); @@ -580,17 +568,17 @@ void rxrpc_release_calls_on_socket(struct rxrpc_sock *rx) struct rxrpc_call, accept_link); list_del(&call->accept_link); rxrpc_abort_call("SKR", call, 0, RX_CALL_DEAD, -ECONNRESET); - rxrpc_put_call(call, rxrpc_call_put); + rxrpc_put_call(call, rxrpc_call_put_release_sock_tba); } while (!list_empty(&rx->sock_calls)) { call = list_entry(rx->sock_calls.next, struct rxrpc_call, sock_link); - rxrpc_get_call(call, rxrpc_call_got); + rxrpc_get_call(call, rxrpc_call_get_release_sock); rxrpc_abort_call("SKT", call, 0, RX_CALL_DEAD, -ECONNRESET); rxrpc_send_abort_packet(call); rxrpc_release_call(rx, call); - rxrpc_put_call(call, rxrpc_call_put); + rxrpc_put_call(call, rxrpc_call_put_release_sock); } _leave(""); @@ -599,20 +587,18 @@ void rxrpc_release_calls_on_socket(struct rxrpc_sock *rx) /* * release a call */ -void rxrpc_put_call(struct rxrpc_call *call, enum rxrpc_call_trace op) +void rxrpc_put_call(struct rxrpc_call *call, enum rxrpc_call_trace why) { struct rxrpc_net *rxnet = call->rxnet; - const void *here = __builtin_return_address(0); unsigned int debug_id = call->debug_id; bool dead; - int n; + int r; ASSERT(call != NULL); - dead = __refcount_dec_and_test(&call->ref, &n); - trace_rxrpc_call(debug_id, op, n, here, NULL); + dead = __refcount_dec_and_test(&call->ref, &r); + trace_rxrpc_call(debug_id, r - 1, 0, why); if (dead) { - _debug("call %d dead", call->debug_id); ASSERTCMP(call->state, ==, RXRPC_CALL_COMPLETE); if (!list_empty(&call->link)) { @@ -701,7 +687,7 @@ void rxrpc_destroy_all_calls(struct rxrpc_net *rxnet) struct rxrpc_call, link); _debug("Zapping call %p", call); - rxrpc_see_call(call); + rxrpc_see_call(call, rxrpc_call_see_zap); list_del_init(&call->link); pr_err("Call %p still in use (%d,%s,%lx,%lx)!\n", diff --git a/net/rxrpc/conn_client.c b/net/rxrpc/conn_client.c index dcfec6a45255..4352e777aa2a 100644 --- a/net/rxrpc/conn_client.c +++ b/net/rxrpc/conn_client.c @@ -540,7 +540,7 @@ static void rxrpc_activate_one_channel(struct rxrpc_connection *conn, clear_bit(RXRPC_CONN_FINAL_ACK_0 + channel, &conn->flags); clear_bit(conn->bundle_shift + channel, &bundle->avail_chans); - rxrpc_see_call(call); + rxrpc_see_call(call, rxrpc_call_see_activate_client); list_del_init(&call->chan_wait_link); call->peer = rxrpc_get_peer(conn->peer, rxrpc_peer_get_activate_call); call->conn = rxrpc_get_connection(conn, rxrpc_conn_get_activate_call); diff --git a/net/rxrpc/input.c b/net/rxrpc/input.c index c8ff7489b412..09b44cd11c9b 100644 --- a/net/rxrpc/input.c +++ b/net/rxrpc/input.c @@ -14,7 +14,7 @@ static void rxrpc_proto_abort(const char *why, { if (rxrpc_abort_call(why, call, seq, RX_PROTOCOL_ERROR, -EBADMSG)) { set_bit(RXRPC_CALL_EV_ABORT, &call->events); - rxrpc_queue_call(call); + rxrpc_queue_call(call, rxrpc_call_queue_abort); } } @@ -175,7 +175,7 @@ out_no_clear_ca: call->cong_cumul_acks = cumulative_acks; trace_rxrpc_congest(call, summary, acked_serial, change); if (resend && !test_and_set_bit(RXRPC_CALL_EV_RESEND, &call->events)) - rxrpc_queue_call(call); + rxrpc_queue_call(call, rxrpc_call_queue_resend); return; packet_loss_detected: @@ -678,7 +678,7 @@ static void rxrpc_input_check_for_lost_ack(struct rxrpc_call *call) { if (after(call->acks_lost_top, call->acks_prev_seq) && !test_and_set_bit(RXRPC_CALL_EV_RESEND, &call->events)) - rxrpc_queue_call(call); + rxrpc_queue_call(call, rxrpc_call_queue_resend); } /* @@ -1099,7 +1099,7 @@ static void rxrpc_input_implicit_end_call(struct rxrpc_sock *rx, default: if (rxrpc_abort_call("IMP", call, 0, RX_CALL_DEAD, -ESHUTDOWN)) { set_bit(RXRPC_CALL_EV_ABORT, &call->events); - rxrpc_queue_call(call); + rxrpc_queue_call(call, rxrpc_call_queue_abort); } trace_rxrpc_improper_term(call); break; diff --git a/net/rxrpc/output.c b/net/rxrpc/output.c index 2762b7ada9ae..d324e88f7642 100644 --- a/net/rxrpc/output.c +++ b/net/rxrpc/output.c @@ -310,7 +310,7 @@ void rxrpc_transmit_ack_packets(struct rxrpc_local *local) } list_del_init(&txb->tx_link); - rxrpc_put_call(txb->call, rxrpc_call_put); + rxrpc_put_call(txb->call, rxrpc_call_put_send_ack); rxrpc_put_txbuf(txb, rxrpc_txbuf_put_ack_tx); } } diff --git a/net/rxrpc/peer_event.c b/net/rxrpc/peer_event.c index 5e97d321ac38..b28739d10927 100644 --- a/net/rxrpc/peer_event.c +++ b/net/rxrpc/peer_event.c @@ -238,7 +238,7 @@ static void rxrpc_distribute_error(struct rxrpc_peer *peer, int error, struct rxrpc_call *call; hlist_for_each_entry_rcu(call, &peer->error_targets, error_link) { - rxrpc_see_call(call); + rxrpc_see_call(call, rxrpc_call_see_distribute_error); rxrpc_set_call_completion(call, compl, 0, -error); } } diff --git a/net/rxrpc/recvmsg.c b/net/rxrpc/recvmsg.c index 134122f5961a..c84d2b620396 100644 --- a/net/rxrpc/recvmsg.c +++ b/net/rxrpc/recvmsg.c @@ -42,7 +42,7 @@ void rxrpc_notify_socket(struct rxrpc_call *call) } else { write_lock_bh(&rx->recvmsg_lock); if (list_empty(&call->recvmsg_link)) { - rxrpc_get_call(call, rxrpc_call_got); + rxrpc_get_call(call, rxrpc_call_get_notify_socket); list_add_tail(&call->recvmsg_link, &rx->recvmsg_q); } write_unlock_bh(&rx->recvmsg_lock); @@ -451,7 +451,7 @@ try_again: if (!(flags & MSG_PEEK)) list_del_init(&call->recvmsg_link); else - rxrpc_get_call(call, rxrpc_call_got); + rxrpc_get_call(call, rxrpc_call_get_recvmsg); write_unlock_bh(&rx->recvmsg_lock); trace_rxrpc_recvmsg(call, rxrpc_recvmsg_dequeue, 0); @@ -537,7 +537,7 @@ try_again: error_unlock_call: mutex_unlock(&call->user_mutex); - rxrpc_put_call(call, rxrpc_call_put); + rxrpc_put_call(call, rxrpc_call_put_recvmsg); trace_rxrpc_recvmsg(call, rxrpc_recvmsg_return, ret); return ret; @@ -548,7 +548,7 @@ error_requeue_call: write_unlock_bh(&rx->recvmsg_lock); trace_rxrpc_recvmsg(call, rxrpc_recvmsg_requeue, 0); } else { - rxrpc_put_call(call, rxrpc_call_put); + rxrpc_put_call(call, rxrpc_call_put_recvmsg); } error_no_call: release_sock(&rx->sk); diff --git a/net/rxrpc/sendmsg.c b/net/rxrpc/sendmsg.c index cfe0badba0b3..76b1e2e89c1e 100644 --- a/net/rxrpc/sendmsg.c +++ b/net/rxrpc/sendmsg.c @@ -667,7 +667,7 @@ int rxrpc_do_sendmsg(struct rxrpc_sock *rx, struct msghdr *msg, size_t len) case RXRPC_CALL_CLIENT_AWAIT_CONN: case RXRPC_CALL_SERVER_PREALLOC: case RXRPC_CALL_SERVER_SECURING: - rxrpc_put_call(call, rxrpc_call_put); + rxrpc_put_call(call, rxrpc_call_put_sendmsg); ret = -EBUSY; goto error_release_sock; default: @@ -737,7 +737,7 @@ out_put_unlock: if (!dropped_lock) mutex_unlock(&call->user_mutex); error_put: - rxrpc_put_call(call, rxrpc_call_put); + rxrpc_put_call(call, rxrpc_call_put_sendmsg); _leave(" = %d", ret); return ret; From fa3492abb64b93b2b5d6fdf7a5b687a1fa810d74 Mon Sep 17 00:00:00 2001 From: David Howells Date: Wed, 2 Nov 2022 21:54:46 +0000 Subject: [PATCH 1413/2002] rxrpc: Trace rxrpc_bundle refcount Add a tracepoint for the rxrpc_bundle refcounting. Signed-off-by: David Howells cc: Marc Dionne cc: linux-afs@lists.infradead.org --- include/trace/events/rxrpc.h | 34 ++++++++++++++++++++++++++++++++++ net/rxrpc/ar-internal.h | 4 ++-- net/rxrpc/conn_client.c | 27 ++++++++++++++++----------- net/rxrpc/conn_object.c | 2 +- net/rxrpc/conn_service.c | 3 ++- 5 files changed, 55 insertions(+), 15 deletions(-) diff --git a/include/trace/events/rxrpc.h b/include/trace/events/rxrpc.h index 3f6de4294148..6f5be7ac7f6b 100644 --- a/include/trace/events/rxrpc.h +++ b/include/trace/events/rxrpc.h @@ -81,6 +81,15 @@ EM(rxrpc_peer_put_input_error, "PUT inpt-err") \ E_(rxrpc_peer_put_keepalive, "PUT keepaliv") +#define rxrpc_bundle_traces \ + EM(rxrpc_bundle_free, "FREE ") \ + EM(rxrpc_bundle_get_client_call, "GET clt-call") \ + EM(rxrpc_bundle_get_client_conn, "GET clt-conn") \ + EM(rxrpc_bundle_get_service_conn, "GET svc-conn") \ + EM(rxrpc_bundle_put_conn, "PUT conn ") \ + EM(rxrpc_bundle_put_discard, "PUT discard ") \ + E_(rxrpc_bundle_new, "NEW ") + #define rxrpc_conn_traces \ EM(rxrpc_conn_free, "FREE ") \ EM(rxrpc_conn_get_activate_call, "GET act-call") \ @@ -361,6 +370,7 @@ #define EM(a, b) a, #define E_(a, b) a +enum rxrpc_bundle_trace { rxrpc_bundle_traces } __mode(byte); enum rxrpc_call_trace { rxrpc_call_traces } __mode(byte); enum rxrpc_client_trace { rxrpc_client_traces } __mode(byte); enum rxrpc_congest_change { rxrpc_congest_changes } __mode(byte); @@ -390,6 +400,7 @@ enum rxrpc_txqueue_trace { rxrpc_txqueue_traces } __mode(byte); #define EM(a, b) TRACE_DEFINE_ENUM(a); #define E_(a, b) TRACE_DEFINE_ENUM(a); +rxrpc_bundle_traces; rxrpc_call_traces; rxrpc_client_traces; rxrpc_congest_changes; @@ -467,6 +478,29 @@ TRACE_EVENT(rxrpc_peer, __entry->ref) ); +TRACE_EVENT(rxrpc_bundle, + TP_PROTO(unsigned int bundle_debug_id, int ref, enum rxrpc_bundle_trace why), + + TP_ARGS(bundle_debug_id, ref, why), + + TP_STRUCT__entry( + __field(unsigned int, bundle ) + __field(int, ref ) + __field(int, why ) + ), + + TP_fast_assign( + __entry->bundle = bundle_debug_id; + __entry->ref = ref; + __entry->why = why; + ), + + TP_printk("CB=%08x %s r=%d", + __entry->bundle, + __print_symbolic(__entry->why, rxrpc_bundle_traces), + __entry->ref) + ); + TRACE_EVENT(rxrpc_conn, TP_PROTO(unsigned int conn_debug_id, int ref, enum rxrpc_conn_trace why), diff --git a/net/rxrpc/ar-internal.h b/net/rxrpc/ar-internal.h index 82eb09b961a0..c588c0e81f63 100644 --- a/net/rxrpc/ar-internal.h +++ b/net/rxrpc/ar-internal.h @@ -875,8 +875,8 @@ extern unsigned long rxrpc_conn_idle_client_fast_expiry; extern struct idr rxrpc_client_conn_ids; void rxrpc_destroy_client_conn_ids(void); -struct rxrpc_bundle *rxrpc_get_bundle(struct rxrpc_bundle *); -void rxrpc_put_bundle(struct rxrpc_bundle *); +struct rxrpc_bundle *rxrpc_get_bundle(struct rxrpc_bundle *, enum rxrpc_bundle_trace); +void rxrpc_put_bundle(struct rxrpc_bundle *, enum rxrpc_bundle_trace); int rxrpc_connect_call(struct rxrpc_sock *, struct rxrpc_call *, struct rxrpc_conn_parameters *, struct sockaddr_rxrpc *, gfp_t); diff --git a/net/rxrpc/conn_client.c b/net/rxrpc/conn_client.c index 4352e777aa2a..34ff6fa85c32 100644 --- a/net/rxrpc/conn_client.c +++ b/net/rxrpc/conn_client.c @@ -133,31 +133,36 @@ static struct rxrpc_bundle *rxrpc_alloc_bundle(struct rxrpc_conn_parameters *cp, atomic_set(&bundle->active, 1); spin_lock_init(&bundle->channel_lock); INIT_LIST_HEAD(&bundle->waiting_calls); + trace_rxrpc_bundle(bundle->debug_id, 1, rxrpc_bundle_new); } return bundle; } -struct rxrpc_bundle *rxrpc_get_bundle(struct rxrpc_bundle *bundle) +struct rxrpc_bundle *rxrpc_get_bundle(struct rxrpc_bundle *bundle, + enum rxrpc_bundle_trace why) { - refcount_inc(&bundle->ref); + int r; + + __refcount_inc(&bundle->ref, &r); + trace_rxrpc_bundle(bundle->debug_id, r + 1, why); return bundle; } static void rxrpc_free_bundle(struct rxrpc_bundle *bundle) { + trace_rxrpc_bundle(bundle->debug_id, 1, rxrpc_bundle_free); rxrpc_put_peer(bundle->peer, rxrpc_peer_put_bundle); kfree(bundle); } -void rxrpc_put_bundle(struct rxrpc_bundle *bundle) +void rxrpc_put_bundle(struct rxrpc_bundle *bundle, enum rxrpc_bundle_trace why) { - unsigned int d = bundle->debug_id; + unsigned int id = bundle->debug_id; bool dead; int r; dead = __refcount_dec_and_test(&bundle->ref, &r); - - _debug("PUT B=%x %d", d, r - 1); + trace_rxrpc_bundle(id, r - 1, why); if (dead) rxrpc_free_bundle(bundle); } @@ -206,7 +211,7 @@ rxrpc_alloc_client_connection(struct rxrpc_bundle *bundle, gfp_t gfp) list_add_tail(&conn->proc_link, &rxnet->conn_proc_list); write_unlock(&rxnet->conn_lock); - rxrpc_get_bundle(bundle); + rxrpc_get_bundle(bundle, rxrpc_bundle_get_client_conn); rxrpc_get_peer(conn->peer, rxrpc_peer_get_client_conn); rxrpc_get_local(conn->local, rxrpc_local_get_client_conn); key_get(conn->key); @@ -342,7 +347,7 @@ static struct rxrpc_bundle *rxrpc_look_up_bundle(struct rxrpc_conn_parameters *c candidate->debug_id = atomic_inc_return(&rxrpc_bundle_id); rb_link_node(&candidate->local_node, parent, pp); rb_insert_color(&candidate->local_node, &local->client_bundles); - rxrpc_get_bundle(candidate); + rxrpc_get_bundle(candidate, rxrpc_bundle_get_client_call); spin_unlock(&local->client_bundles_lock); _leave(" = %u [new]", candidate->debug_id); return candidate; @@ -350,7 +355,7 @@ static struct rxrpc_bundle *rxrpc_look_up_bundle(struct rxrpc_conn_parameters *c found_bundle_free: rxrpc_free_bundle(candidate); found_bundle: - rxrpc_get_bundle(bundle); + rxrpc_get_bundle(bundle, rxrpc_bundle_get_client_call); atomic_inc(&bundle->active); spin_unlock(&local->client_bundles_lock); _leave(" = %u [found]", bundle->debug_id); @@ -740,7 +745,7 @@ granted_channel: out_put_bundle: rxrpc_deactivate_bundle(bundle); - rxrpc_put_bundle(bundle); + rxrpc_put_bundle(bundle, rxrpc_bundle_get_client_call); out: _leave(" = %d", ret); return ret; @@ -958,7 +963,7 @@ static void rxrpc_deactivate_bundle(struct rxrpc_bundle *bundle) spin_unlock(&local->client_bundles_lock); if (need_put) - rxrpc_put_bundle(bundle); + rxrpc_put_bundle(bundle, rxrpc_bundle_put_discard); } } diff --git a/net/rxrpc/conn_object.c b/net/rxrpc/conn_object.c index bbace8d9953d..f7c271a740ed 100644 --- a/net/rxrpc/conn_object.c +++ b/net/rxrpc/conn_object.c @@ -363,7 +363,7 @@ static void rxrpc_destroy_connection(struct rcu_head *rcu) conn->security->clear(conn); key_put(conn->key); - rxrpc_put_bundle(conn->bundle); + rxrpc_put_bundle(conn->bundle, rxrpc_bundle_put_conn); rxrpc_put_peer(conn->peer, rxrpc_peer_put_conn); if (atomic_dec_and_test(&conn->local->rxnet->nr_conns)) diff --git a/net/rxrpc/conn_service.c b/net/rxrpc/conn_service.c index bf087213bd4d..2c44d67b43dc 100644 --- a/net/rxrpc/conn_service.c +++ b/net/rxrpc/conn_service.c @@ -133,7 +133,8 @@ struct rxrpc_connection *rxrpc_prealloc_service_connection(struct rxrpc_net *rxn */ conn->state = RXRPC_CONN_SERVICE_PREALLOC; refcount_set(&conn->ref, 2); - conn->bundle = rxrpc_get_bundle(&rxrpc_service_dummy_bundle); + conn->bundle = rxrpc_get_bundle(&rxrpc_service_dummy_bundle, + rxrpc_bundle_get_service_conn); atomic_inc(&rxnet->nr_conns); write_lock(&rxnet->conn_lock); From 9a36a6bc22ca1c0a9d82228171e05dc785fa1154 Mon Sep 17 00:00:00 2001 From: David Howells Date: Fri, 21 Oct 2022 15:31:21 +0100 Subject: [PATCH 1414/2002] rxrpc: trace: Don't use __builtin_return_address for sk_buff tracing In rxrpc tracing, use enums to generate lists of points of interest rather than __builtin_return_address() for the sk_buff tracepoint. Signed-off-by: David Howells cc: Marc Dionne cc: linux-afs@lists.infradead.org --- include/trace/events/rxrpc.h | 57 +++++++++++++++++++++--------------- net/rxrpc/call_event.c | 4 +-- net/rxrpc/call_object.c | 2 +- net/rxrpc/conn_event.c | 6 ++-- net/rxrpc/input.c | 36 +++++++++++------------ net/rxrpc/local_event.c | 4 +-- net/rxrpc/output.c | 6 ++-- net/rxrpc/peer_event.c | 8 ++--- net/rxrpc/recvmsg.c | 6 ++-- net/rxrpc/skbuff.c | 36 ++++++++++------------- 10 files changed, 84 insertions(+), 81 deletions(-) diff --git a/include/trace/events/rxrpc.h b/include/trace/events/rxrpc.h index 6f5be7ac7f6b..5a2292baffc8 100644 --- a/include/trace/events/rxrpc.h +++ b/include/trace/events/rxrpc.h @@ -17,19 +17,31 @@ * Declare tracing information enums and their string mappings for display. */ #define rxrpc_skb_traces \ - EM(rxrpc_skb_ack, "ACK") \ - EM(rxrpc_skb_cleaned, "CLN") \ - EM(rxrpc_skb_cloned_jumbo, "CLJ") \ - EM(rxrpc_skb_freed, "FRE") \ - EM(rxrpc_skb_got, "GOT") \ - EM(rxrpc_skb_lost, "*L*") \ - EM(rxrpc_skb_new, "NEW") \ - EM(rxrpc_skb_purged, "PUR") \ - EM(rxrpc_skb_received, "RCV") \ - EM(rxrpc_skb_rotated, "ROT") \ - EM(rxrpc_skb_seen, "SEE") \ - EM(rxrpc_skb_unshared, "UNS") \ - E_(rxrpc_skb_unshared_nomem, "US0") + EM(rxrpc_skb_eaten_by_unshare, "ETN unshare ") \ + EM(rxrpc_skb_eaten_by_unshare_nomem, "ETN unshar-nm") \ + EM(rxrpc_skb_get_ack, "GET ack ") \ + EM(rxrpc_skb_get_conn_work, "GET conn-work") \ + EM(rxrpc_skb_get_to_recvmsg, "GET to-recv ") \ + EM(rxrpc_skb_get_to_recvmsg_oos, "GET to-recv-o") \ + EM(rxrpc_skb_new_encap_rcv, "NEW encap-rcv") \ + EM(rxrpc_skb_new_error_report, "NEW error-rpt") \ + EM(rxrpc_skb_new_jumbo_subpacket, "NEW jumbo-sub") \ + EM(rxrpc_skb_new_unshared, "NEW unshared ") \ + EM(rxrpc_skb_put_ack, "PUT ack ") \ + EM(rxrpc_skb_put_conn_work, "PUT conn-work") \ + EM(rxrpc_skb_put_error_report, "PUT error-rep") \ + EM(rxrpc_skb_put_input, "PUT input ") \ + EM(rxrpc_skb_put_jumbo_subpacket, "PUT jumbo-sub") \ + EM(rxrpc_skb_put_lose, "PUT lose ") \ + EM(rxrpc_skb_put_purge, "PUT purge ") \ + EM(rxrpc_skb_put_rotate, "PUT rotate ") \ + EM(rxrpc_skb_put_unknown, "PUT unknown ") \ + EM(rxrpc_skb_see_conn_work, "SEE conn-work") \ + EM(rxrpc_skb_see_local_work, "SEE locl-work") \ + EM(rxrpc_skb_see_recvmsg, "SEE recvmsg ") \ + EM(rxrpc_skb_see_reject, "SEE reject ") \ + EM(rxrpc_skb_see_rotate, "SEE rotate ") \ + E_(rxrpc_skb_see_version, "SEE version ") #define rxrpc_local_traces \ EM(rxrpc_local_free, "FREE ") \ @@ -582,33 +594,30 @@ TRACE_EVENT(rxrpc_call, ); TRACE_EVENT(rxrpc_skb, - TP_PROTO(struct sk_buff *skb, enum rxrpc_skb_trace op, - int usage, int mod_count, const void *where), + TP_PROTO(struct sk_buff *skb, int usage, int mod_count, + enum rxrpc_skb_trace why), - TP_ARGS(skb, op, usage, mod_count, where), + TP_ARGS(skb, usage, mod_count, why), TP_STRUCT__entry( __field(struct sk_buff *, skb ) - __field(enum rxrpc_skb_trace, op ) __field(int, usage ) __field(int, mod_count ) - __field(const void *, where ) + __field(enum rxrpc_skb_trace, why ) ), TP_fast_assign( __entry->skb = skb; - __entry->op = op; __entry->usage = usage; __entry->mod_count = mod_count; - __entry->where = where; + __entry->why = why; ), - TP_printk("s=%p Rx %s u=%d m=%d p=%pSR", + TP_printk("s=%p Rx %s u=%d m=%d", __entry->skb, - __print_symbolic(__entry->op, rxrpc_skb_traces), + __print_symbolic(__entry->why, rxrpc_skb_traces), __entry->usage, - __entry->mod_count, - __entry->where) + __entry->mod_count) ); TRACE_EVENT(rxrpc_rx_packet, diff --git a/net/rxrpc/call_event.c b/net/rxrpc/call_event.c index 0c8d2186cda8..29ca02e53c47 100644 --- a/net/rxrpc/call_event.c +++ b/net/rxrpc/call_event.c @@ -153,7 +153,7 @@ static void rxrpc_resend(struct rxrpc_call *call, unsigned long now_j) spin_lock_bh(&call->acks_ack_lock); ack_skb = call->acks_soft_tbl; if (ack_skb) { - rxrpc_get_skb(ack_skb, rxrpc_skb_ack); + rxrpc_get_skb(ack_skb, rxrpc_skb_get_ack); ack = (void *)ack_skb->data + sizeof(struct rxrpc_wire_header); } spin_unlock_bh(&call->acks_ack_lock); @@ -251,7 +251,7 @@ static void rxrpc_resend(struct rxrpc_call *call, unsigned long now_j) no_further_resend: spin_unlock(&call->tx_lock); no_resend: - rxrpc_free_skb(ack_skb, rxrpc_skb_freed); + rxrpc_free_skb(ack_skb, rxrpc_skb_put_ack); resend_at = nsecs_to_jiffies(ktime_to_ns(ktime_sub(now, oldest))); resend_at += jiffies + rxrpc_get_rto_backoff(call->peer, diff --git a/net/rxrpc/call_object.c b/net/rxrpc/call_object.c index afd957f6dc1c..815209673115 100644 --- a/net/rxrpc/call_object.c +++ b/net/rxrpc/call_object.c @@ -663,7 +663,7 @@ void rxrpc_cleanup_call(struct rxrpc_call *call) rxrpc_put_txbuf(txb, rxrpc_txbuf_put_cleaned); } rxrpc_put_txbuf(call->tx_pending, rxrpc_txbuf_put_cleaned); - rxrpc_free_skb(call->acks_soft_tbl, rxrpc_skb_cleaned); + rxrpc_free_skb(call->acks_soft_tbl, rxrpc_skb_put_ack); call_rcu(&call->rcu, rxrpc_rcu_destroy_call); } diff --git a/net/rxrpc/conn_event.c b/net/rxrpc/conn_event.c index 817f895c77ca..49d885f73fa5 100644 --- a/net/rxrpc/conn_event.c +++ b/net/rxrpc/conn_event.c @@ -437,7 +437,7 @@ static void rxrpc_do_process_connection(struct rxrpc_connection *conn) /* go through the conn-level event packets, releasing the ref on this * connection that each one has when we've finished with it */ while ((skb = skb_dequeue(&conn->rx_queue))) { - rxrpc_see_skb(skb, rxrpc_skb_seen); + rxrpc_see_skb(skb, rxrpc_skb_see_conn_work); ret = rxrpc_process_event(conn, skb, &abort_code); switch (ret) { case -EPROTO: @@ -449,7 +449,7 @@ static void rxrpc_do_process_connection(struct rxrpc_connection *conn) goto requeue_and_leave; case -ECONNABORTED: default: - rxrpc_free_skb(skb, rxrpc_skb_freed); + rxrpc_free_skb(skb, rxrpc_skb_put_conn_work); break; } } @@ -463,7 +463,7 @@ requeue_and_leave: protocol_error: if (rxrpc_abort_connection(conn, ret, abort_code) < 0) goto requeue_and_leave; - rxrpc_free_skb(skb, rxrpc_skb_freed); + rxrpc_free_skb(skb, rxrpc_skb_put_conn_work); return; } diff --git a/net/rxrpc/input.c b/net/rxrpc/input.c index 09b44cd11c9b..ab8b7a1be935 100644 --- a/net/rxrpc/input.c +++ b/net/rxrpc/input.c @@ -485,7 +485,7 @@ send_ack: rxrpc_propose_ack_input_data); err_free: - rxrpc_free_skb(skb, rxrpc_skb_freed); + rxrpc_free_skb(skb, rxrpc_skb_put_input); } /* @@ -513,7 +513,7 @@ static bool rxrpc_input_split_jumbo(struct rxrpc_call *call, struct sk_buff *skb kdebug("couldn't clone"); return false; } - rxrpc_new_skb(jskb, rxrpc_skb_cloned_jumbo); + rxrpc_new_skb(jskb, rxrpc_skb_new_jumbo_subpacket); jsp = rxrpc_skb(jskb); jsp->offset = offset; jsp->len = RXRPC_JUMBO_DATALEN; @@ -553,7 +553,7 @@ static void rxrpc_input_data(struct rxrpc_call *call, struct sk_buff *skb) state = READ_ONCE(call->state); if (state >= RXRPC_CALL_COMPLETE) { - rxrpc_free_skb(skb, rxrpc_skb_freed); + rxrpc_free_skb(skb, rxrpc_skb_put_input); return; } @@ -563,14 +563,14 @@ static void rxrpc_input_data(struct rxrpc_call *call, struct sk_buff *skb) if (sp->hdr.securityIndex != 0) { struct sk_buff *nskb = skb_unshare(skb, GFP_ATOMIC); if (!nskb) { - rxrpc_eaten_skb(skb, rxrpc_skb_unshared_nomem); + rxrpc_eaten_skb(skb, rxrpc_skb_eaten_by_unshare_nomem); return; } if (nskb != skb) { - rxrpc_eaten_skb(skb, rxrpc_skb_received); + rxrpc_eaten_skb(skb, rxrpc_skb_eaten_by_unshare); skb = nskb; - rxrpc_new_skb(skb, rxrpc_skb_unshared); + rxrpc_new_skb(skb, rxrpc_skb_new_unshared); sp = rxrpc_skb(skb); } } @@ -609,7 +609,7 @@ out: rxrpc_notify_socket(call); spin_unlock(&call->input_lock); - rxrpc_free_skb(skb, rxrpc_skb_freed); + rxrpc_free_skb(skb, rxrpc_skb_put_input); _leave(" [queued]"); } @@ -994,8 +994,8 @@ static void rxrpc_input_ack(struct rxrpc_call *call, struct sk_buff *skb) out: spin_unlock(&call->input_lock); out_not_locked: - rxrpc_free_skb(skb_put, rxrpc_skb_freed); - rxrpc_free_skb(skb_old, rxrpc_skb_freed); + rxrpc_free_skb(skb_put, rxrpc_skb_put_input); + rxrpc_free_skb(skb_old, rxrpc_skb_put_ack); } /* @@ -1075,7 +1075,7 @@ static void rxrpc_input_call_packet(struct rxrpc_call *call, break; } - rxrpc_free_skb(skb, rxrpc_skb_freed); + rxrpc_free_skb(skb, rxrpc_skb_put_input); no_free: _leave(""); } @@ -1137,7 +1137,7 @@ static void rxrpc_post_packet_to_local(struct rxrpc_local *local, skb_queue_tail(&local->event_queue, skb); rxrpc_queue_local(local); } else { - rxrpc_free_skb(skb, rxrpc_skb_freed); + rxrpc_free_skb(skb, rxrpc_skb_put_input); } } @@ -1150,7 +1150,7 @@ static void rxrpc_reject_packet(struct rxrpc_local *local, struct sk_buff *skb) skb_queue_tail(&local->reject_queue, skb); rxrpc_queue_local(local); } else { - rxrpc_free_skb(skb, rxrpc_skb_freed); + rxrpc_free_skb(skb, rxrpc_skb_put_input); } } @@ -1228,7 +1228,7 @@ int rxrpc_input_packet(struct sock *udp_sk, struct sk_buff *skb) if (skb->tstamp == 0) skb->tstamp = ktime_get_real(); - rxrpc_new_skb(skb, rxrpc_skb_received); + rxrpc_new_skb(skb, rxrpc_skb_new_encap_rcv); skb_pull(skb, sizeof(struct udphdr)); @@ -1245,7 +1245,7 @@ int rxrpc_input_packet(struct sock *udp_sk, struct sk_buff *skb) static int lose; if ((lose++ & 7) == 7) { trace_rxrpc_rx_lose(sp); - rxrpc_free_skb(skb, rxrpc_skb_lost); + rxrpc_free_skb(skb, rxrpc_skb_put_lose); return 0; } } @@ -1286,14 +1286,14 @@ int rxrpc_input_packet(struct sock *udp_sk, struct sk_buff *skb) if (sp->hdr.securityIndex != 0) { struct sk_buff *nskb = skb_unshare(skb, GFP_ATOMIC); if (!nskb) { - rxrpc_eaten_skb(skb, rxrpc_skb_unshared_nomem); + rxrpc_eaten_skb(skb, rxrpc_skb_eaten_by_unshare_nomem); goto out; } if (nskb != skb) { - rxrpc_eaten_skb(skb, rxrpc_skb_received); + rxrpc_eaten_skb(skb, rxrpc_skb_eaten_by_unshare); skb = nskb; - rxrpc_new_skb(skb, rxrpc_skb_unshared); + rxrpc_new_skb(skb, rxrpc_skb_new_unshared); sp = rxrpc_skb(skb); } } @@ -1434,7 +1434,7 @@ int rxrpc_input_packet(struct sock *udp_sk, struct sk_buff *skb) goto out; discard: - rxrpc_free_skb(skb, rxrpc_skb_freed); + rxrpc_free_skb(skb, rxrpc_skb_put_input); out: trace_rxrpc_rx_done(0, 0); return 0; diff --git a/net/rxrpc/local_event.c b/net/rxrpc/local_event.c index f23a3fbabbda..c344383a20b2 100644 --- a/net/rxrpc/local_event.c +++ b/net/rxrpc/local_event.c @@ -88,7 +88,7 @@ void rxrpc_process_local_events(struct rxrpc_local *local) if (skb) { struct rxrpc_skb_priv *sp = rxrpc_skb(skb); - rxrpc_see_skb(skb, rxrpc_skb_seen); + rxrpc_see_skb(skb, rxrpc_skb_see_local_work); _debug("{%d},{%u}", local->debug_id, sp->hdr.type); switch (sp->hdr.type) { @@ -105,7 +105,7 @@ void rxrpc_process_local_events(struct rxrpc_local *local) break; } - rxrpc_free_skb(skb, rxrpc_skb_freed); + rxrpc_free_skb(skb, rxrpc_skb_put_input); } _leave(""); diff --git a/net/rxrpc/output.c b/net/rxrpc/output.c index d324e88f7642..131c7a76fb06 100644 --- a/net/rxrpc/output.c +++ b/net/rxrpc/output.c @@ -615,7 +615,7 @@ void rxrpc_reject_packets(struct rxrpc_local *local) memset(&whdr, 0, sizeof(whdr)); while ((skb = skb_dequeue(&local->reject_queue))) { - rxrpc_see_skb(skb, rxrpc_skb_seen); + rxrpc_see_skb(skb, rxrpc_skb_see_reject); sp = rxrpc_skb(skb); switch (skb->mark) { @@ -631,7 +631,7 @@ void rxrpc_reject_packets(struct rxrpc_local *local) ioc = 2; break; default: - rxrpc_free_skb(skb, rxrpc_skb_freed); + rxrpc_free_skb(skb, rxrpc_skb_put_input); continue; } @@ -656,7 +656,7 @@ void rxrpc_reject_packets(struct rxrpc_local *local) rxrpc_tx_point_reject); } - rxrpc_free_skb(skb, rxrpc_skb_freed); + rxrpc_free_skb(skb, rxrpc_skb_put_input); } _leave(""); diff --git a/net/rxrpc/peer_event.c b/net/rxrpc/peer_event.c index b28739d10927..f35cfc458dcf 100644 --- a/net/rxrpc/peer_event.c +++ b/net/rxrpc/peer_event.c @@ -158,12 +158,12 @@ void rxrpc_error_report(struct sock *sk) _leave("UDP socket errqueue empty"); return; } - rxrpc_new_skb(skb, rxrpc_skb_received); + rxrpc_new_skb(skb, rxrpc_skb_new_error_report); serr = SKB_EXT_ERR(skb); if (!skb->len && serr->ee.ee_origin == SO_EE_ORIGIN_TIMESTAMPING) { _leave("UDP empty message"); rcu_read_unlock(); - rxrpc_free_skb(skb, rxrpc_skb_freed); + rxrpc_free_skb(skb, rxrpc_skb_put_error_report); return; } @@ -172,7 +172,7 @@ void rxrpc_error_report(struct sock *sk) peer = NULL; if (!peer) { rcu_read_unlock(); - rxrpc_free_skb(skb, rxrpc_skb_freed); + rxrpc_free_skb(skb, rxrpc_skb_put_error_report); _leave(" [no peer]"); return; } @@ -189,7 +189,7 @@ void rxrpc_error_report(struct sock *sk) rxrpc_store_error(peer, serr); out: rcu_read_unlock(); - rxrpc_free_skb(skb, rxrpc_skb_freed); + rxrpc_free_skb(skb, rxrpc_skb_put_error_report); rxrpc_put_peer(peer, rxrpc_peer_put_input_error); _leave(""); diff --git a/net/rxrpc/recvmsg.c b/net/rxrpc/recvmsg.c index c84d2b620396..bfac9e09347e 100644 --- a/net/rxrpc/recvmsg.c +++ b/net/rxrpc/recvmsg.c @@ -229,7 +229,7 @@ static void rxrpc_rotate_rx_window(struct rxrpc_call *call) _enter("%d", call->debug_id); skb = skb_dequeue(&call->recvmsg_queue); - rxrpc_see_skb(skb, rxrpc_skb_rotated); + rxrpc_see_skb(skb, rxrpc_skb_see_rotate); sp = rxrpc_skb(skb); tseq = sp->hdr.seq; @@ -240,7 +240,7 @@ static void rxrpc_rotate_rx_window(struct rxrpc_call *call) if (after(tseq, call->rx_consumed)) smp_store_release(&call->rx_consumed, tseq); - rxrpc_free_skb(skb, rxrpc_skb_freed); + rxrpc_free_skb(skb, rxrpc_skb_put_rotate); trace_rxrpc_receive(call, last ? rxrpc_receive_rotate_last : rxrpc_receive_rotate, serial, call->rx_consumed); @@ -302,7 +302,7 @@ static int rxrpc_recvmsg_data(struct socket *sock, struct rxrpc_call *call, */ skb = skb_peek(&call->recvmsg_queue); while (skb) { - rxrpc_see_skb(skb, rxrpc_skb_seen); + rxrpc_see_skb(skb, rxrpc_skb_see_recvmsg); sp = rxrpc_skb(skb); seq = sp->hdr.seq; diff --git a/net/rxrpc/skbuff.c b/net/rxrpc/skbuff.c index 0c827d5bb2b8..ebe0c75e7b07 100644 --- a/net/rxrpc/skbuff.c +++ b/net/rxrpc/skbuff.c @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0-or-later -/* ar-skbuff.c: socket buffer destruction handling +/* Socket buffer accounting * * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. * Written by David Howells (dhowells@redhat.com) @@ -19,56 +19,50 @@ /* * Note the allocation or reception of a socket buffer. */ -void rxrpc_new_skb(struct sk_buff *skb, enum rxrpc_skb_trace op) +void rxrpc_new_skb(struct sk_buff *skb, enum rxrpc_skb_trace why) { - const void *here = __builtin_return_address(0); int n = atomic_inc_return(select_skb_count(skb)); - trace_rxrpc_skb(skb, op, refcount_read(&skb->users), n, here); + trace_rxrpc_skb(skb, refcount_read(&skb->users), n, why); } /* * Note the re-emergence of a socket buffer from a queue or buffer. */ -void rxrpc_see_skb(struct sk_buff *skb, enum rxrpc_skb_trace op) +void rxrpc_see_skb(struct sk_buff *skb, enum rxrpc_skb_trace why) { - const void *here = __builtin_return_address(0); if (skb) { int n = atomic_read(select_skb_count(skb)); - trace_rxrpc_skb(skb, op, refcount_read(&skb->users), n, here); + trace_rxrpc_skb(skb, refcount_read(&skb->users), n, why); } } /* * Note the addition of a ref on a socket buffer. */ -void rxrpc_get_skb(struct sk_buff *skb, enum rxrpc_skb_trace op) +void rxrpc_get_skb(struct sk_buff *skb, enum rxrpc_skb_trace why) { - const void *here = __builtin_return_address(0); int n = atomic_inc_return(select_skb_count(skb)); - trace_rxrpc_skb(skb, op, refcount_read(&skb->users), n, here); + trace_rxrpc_skb(skb, refcount_read(&skb->users), n, why); skb_get(skb); } /* * Note the dropping of a ref on a socket buffer by the core. */ -void rxrpc_eaten_skb(struct sk_buff *skb, enum rxrpc_skb_trace op) +void rxrpc_eaten_skb(struct sk_buff *skb, enum rxrpc_skb_trace why) { - const void *here = __builtin_return_address(0); int n = atomic_inc_return(&rxrpc_n_rx_skbs); - trace_rxrpc_skb(skb, op, 0, n, here); + trace_rxrpc_skb(skb, 0, n, why); } /* * Note the destruction of a socket buffer. */ -void rxrpc_free_skb(struct sk_buff *skb, enum rxrpc_skb_trace op) +void rxrpc_free_skb(struct sk_buff *skb, enum rxrpc_skb_trace why) { - const void *here = __builtin_return_address(0); if (skb) { - int n; - n = atomic_dec_return(select_skb_count(skb)); - trace_rxrpc_skb(skb, op, refcount_read(&skb->users), n, here); + int n = atomic_dec_return(select_skb_count(skb)); + trace_rxrpc_skb(skb, refcount_read(&skb->users), n, why); kfree_skb(skb); } } @@ -78,12 +72,12 @@ void rxrpc_free_skb(struct sk_buff *skb, enum rxrpc_skb_trace op) */ void rxrpc_purge_queue(struct sk_buff_head *list) { - const void *here = __builtin_return_address(0); struct sk_buff *skb; + while ((skb = skb_dequeue((list))) != NULL) { int n = atomic_dec_return(select_skb_count(skb)); - trace_rxrpc_skb(skb, rxrpc_skb_purged, - refcount_read(&skb->users), n, here); + trace_rxrpc_skb(skb, refcount_read(&skb->users), n, + rxrpc_skb_put_purge); kfree_skb(skb); } } From 3feda9d69c83983b530cea6287ba4fea0e5c3b87 Mon Sep 17 00:00:00 2001 From: David Howells Date: Fri, 25 Nov 2022 09:00:55 +0000 Subject: [PATCH 1415/2002] rxrpc: Don't hold a ref for call timer or workqueue Currently, rxrpc gives the call timer a ref on the call when it starts it and this is passed along to the workqueue by the timer expiration function. The problem comes when queue_work() fails (ie. the work item is already queued): the timer routine must put the ref - but this may cause the cleanup code to run. This has the unfortunate effect that the cleanup code may then be run in softirq context - which means that any spinlocks it might need to touch have to be guarded to disable softirqs (ie. they need a "_bh" suffix). Fix this by: (1) Don't give a ref to the timer. (2) Making the expiration function not do anything if the refcount is 0. Note that this is more of an optimisation. (3) Make sure that the cleanup routine waits for timer to complete. However, this has a consequence that timer cannot give a ref to the work item. Therefore the following fixes are also necessary: (4) Don't give a ref to the work item. (5) Make the work item return asap if it sees the ref count is 0. (6) Make sure that the cleanup routine waits for the work item to complete. Unfortunately, neither the timer nor the work item can simply get around the problem by just using refcount_inc_not_zero() as the waits would still have to be done, and there would still be the possibility of having to put the ref in the expiration function. Note the call work item is going to go away with the work being transferred to the I/O thread, so the wait in (6) will become obsolete. Signed-off-by: David Howells cc: Marc Dionne cc: linux-afs@lists.infradead.org --- include/trace/events/rxrpc.h | 6 +- net/rxrpc/ar-internal.h | 6 +- net/rxrpc/call_event.c | 11 ++-- net/rxrpc/call_object.c | 117 ++++++++++++++--------------------- net/rxrpc/txbuf.c | 2 + 5 files changed, 55 insertions(+), 87 deletions(-) diff --git a/include/trace/events/rxrpc.h b/include/trace/events/rxrpc.h index 5a2292baffc8..4538de0079a5 100644 --- a/include/trace/events/rxrpc.h +++ b/include/trace/events/rxrpc.h @@ -155,11 +155,9 @@ EM(rxrpc_call_get_release_sock, "GET rel-sock") \ EM(rxrpc_call_get_sendmsg, "GET sendmsg ") \ EM(rxrpc_call_get_send_ack, "GET send-ack") \ - EM(rxrpc_call_get_timer, "GET timer ") \ EM(rxrpc_call_get_userid, "GET user-id ") \ EM(rxrpc_call_new_client, "NEW client ") \ EM(rxrpc_call_new_prealloc_service, "NEW prealloc") \ - EM(rxrpc_call_put_already_queued, "PUT alreadyq") \ EM(rxrpc_call_put_discard_prealloc, "PUT disc-pre") \ EM(rxrpc_call_put_input, "PUT input ") \ EM(rxrpc_call_put_kernel, "PUT kernel ") \ @@ -168,11 +166,8 @@ EM(rxrpc_call_put_release_sock_tba, "PUT rls-sk-a") \ EM(rxrpc_call_put_send_ack, "PUT send-ack") \ EM(rxrpc_call_put_sendmsg, "PUT sendmsg ") \ - EM(rxrpc_call_put_timer, "PUT timer ") \ - EM(rxrpc_call_put_timer_already, "PUT timer-al") \ EM(rxrpc_call_put_unnotify, "PUT unnotify") \ EM(rxrpc_call_put_userid_exists, "PUT u-exists") \ - EM(rxrpc_call_put_work, "PUT work ") \ EM(rxrpc_call_queue_abort, "QUE abort ") \ EM(rxrpc_call_queue_requeue, "QUE requeue ") \ EM(rxrpc_call_queue_resend, "QUE resend ") \ @@ -368,6 +363,7 @@ EM(rxrpc_txbuf_put_rotated, "PUT ROTATED") \ EM(rxrpc_txbuf_put_send_aborted, "PUT SEND-X ") \ EM(rxrpc_txbuf_put_trans, "PUT TRANS ") \ + EM(rxrpc_txbuf_see_out_of_step, "OUT-OF-STEP") \ EM(rxrpc_txbuf_see_send_more, "SEE SEND+ ") \ E_(rxrpc_txbuf_see_unacked, "SEE UNACKED") diff --git a/net/rxrpc/ar-internal.h b/net/rxrpc/ar-internal.h index c588c0e81f63..03523a864c11 100644 --- a/net/rxrpc/ar-internal.h +++ b/net/rxrpc/ar-internal.h @@ -598,6 +598,7 @@ struct rxrpc_call { u32 next_req_timo; /* Timeout for next Rx request packet (jif) */ struct timer_list timer; /* Combined event timer */ struct work_struct processor; /* Event processor */ + struct work_struct destroyer; /* In-process-context destroyer */ rxrpc_notify_rx_t notify_rx; /* kernel service Rx notification function */ struct list_head link; /* link in master call list */ struct list_head chan_wait_link; /* Link in conn->bundle->waiting_calls */ @@ -827,8 +828,6 @@ void rxrpc_reduce_call_timer(struct rxrpc_call *call, unsigned long now, enum rxrpc_timer_trace why); -void rxrpc_delete_call_timer(struct rxrpc_call *call); - /* * call_object.c */ @@ -847,8 +846,7 @@ void rxrpc_incoming_call(struct rxrpc_sock *, struct rxrpc_call *, struct sk_buff *); void rxrpc_release_call(struct rxrpc_sock *, struct rxrpc_call *); void rxrpc_release_calls_on_socket(struct rxrpc_sock *); -bool __rxrpc_queue_call(struct rxrpc_call *, enum rxrpc_call_trace); -bool rxrpc_queue_call(struct rxrpc_call *, enum rxrpc_call_trace); +void rxrpc_queue_call(struct rxrpc_call *, enum rxrpc_call_trace); void rxrpc_see_call(struct rxrpc_call *, enum rxrpc_call_trace); bool rxrpc_try_get_call(struct rxrpc_call *, enum rxrpc_call_trace); void rxrpc_get_call(struct rxrpc_call *, enum rxrpc_call_trace); diff --git a/net/rxrpc/call_event.c b/net/rxrpc/call_event.c index 29ca02e53c47..049b92b1c040 100644 --- a/net/rxrpc/call_event.c +++ b/net/rxrpc/call_event.c @@ -323,8 +323,8 @@ recheck_state: rxrpc_shrink_call_tx_buffer(call); if (call->state == RXRPC_CALL_COMPLETE) { - rxrpc_delete_call_timer(call); - goto out_put; + del_timer_sync(&call->timer); + goto out; } /* Work out if any timeouts tripped */ @@ -432,16 +432,15 @@ recheck_state: rxrpc_reduce_call_timer(call, next, now, rxrpc_timer_restart); /* other events may have been raised since we started checking */ - if (call->events && call->state < RXRPC_CALL_COMPLETE) + if (call->events) goto requeue; -out_put: - rxrpc_put_call(call, rxrpc_call_put_work); out: _leave(""); return; requeue: - __rxrpc_queue_call(call, rxrpc_call_queue_requeue); + if (call->state < RXRPC_CALL_COMPLETE) + rxrpc_queue_call(call, rxrpc_call_queue_requeue); goto out; } diff --git a/net/rxrpc/call_object.c b/net/rxrpc/call_object.c index 815209673115..9cd7e0190ef4 100644 --- a/net/rxrpc/call_object.c +++ b/net/rxrpc/call_object.c @@ -53,9 +53,7 @@ static void rxrpc_call_timer_expired(struct timer_list *t) if (call->state < RXRPC_CALL_COMPLETE) { trace_rxrpc_timer_expired(call, jiffies); - __rxrpc_queue_call(call, rxrpc_call_queue_timer); - } else { - rxrpc_put_call(call, rxrpc_call_put_already_queued); + rxrpc_queue_call(call, rxrpc_call_queue_timer); } } @@ -64,21 +62,14 @@ void rxrpc_reduce_call_timer(struct rxrpc_call *call, unsigned long now, enum rxrpc_timer_trace why) { - if (rxrpc_try_get_call(call, rxrpc_call_get_timer)) { - trace_rxrpc_timer(call, why, now); - if (timer_reduce(&call->timer, expire_at)) - rxrpc_put_call(call, rxrpc_call_put_timer_already); - } -} - -void rxrpc_delete_call_timer(struct rxrpc_call *call) -{ - if (del_timer_sync(&call->timer)) - rxrpc_put_call(call, rxrpc_call_put_timer); + trace_rxrpc_timer(call, why, now); + timer_reduce(&call->timer, expire_at); } static struct lock_class_key rxrpc_call_user_mutex_lock_class_key; +static void rxrpc_destroy_call(struct work_struct *); + /* * find an extant server call * - called in process context with IRQs enabled @@ -139,7 +130,8 @@ struct rxrpc_call *rxrpc_alloc_call(struct rxrpc_sock *rx, gfp_t gfp, &rxrpc_call_user_mutex_lock_class_key); timer_setup(&call->timer, rxrpc_call_timer_expired, 0); - INIT_WORK(&call->processor, &rxrpc_process_call); + INIT_WORK(&call->processor, rxrpc_process_call); + INIT_WORK(&call->destroyer, rxrpc_destroy_call); INIT_LIST_HEAD(&call->link); INIT_LIST_HEAD(&call->chan_wait_link); INIT_LIST_HEAD(&call->accept_link); @@ -423,34 +415,12 @@ void rxrpc_incoming_call(struct rxrpc_sock *rx, } /* - * Queue a call's work processor, getting a ref to pass to the work queue. + * Queue a call's work processor. */ -bool rxrpc_queue_call(struct rxrpc_call *call, enum rxrpc_call_trace why) +void rxrpc_queue_call(struct rxrpc_call *call, enum rxrpc_call_trace why) { - int n; - - if (!__refcount_inc_not_zero(&call->ref, &n)) - return false; if (rxrpc_queue_work(&call->processor)) - trace_rxrpc_call(call->debug_id, n + 1, 0, why); - else - rxrpc_put_call(call, rxrpc_call_put_already_queued); - return true; -} - -/* - * Queue a call's work processor, passing the callers ref to the work queue. - */ -bool __rxrpc_queue_call(struct rxrpc_call *call, enum rxrpc_call_trace why) -{ - int n = refcount_read(&call->ref); - - ASSERTCMP(n, >=, 1); - if (rxrpc_queue_work(&call->processor)) - trace_rxrpc_call(call->debug_id, n, 0, why); - else - rxrpc_put_call(call, rxrpc_call_put_already_queued); - return true; + trace_rxrpc_call(call->debug_id, refcount_read(&call->ref), 0, why); } /* @@ -514,7 +484,7 @@ void rxrpc_release_call(struct rxrpc_sock *rx, struct rxrpc_call *call) BUG(); rxrpc_put_call_slot(call); - rxrpc_delete_call_timer(call); + del_timer_sync(&call->timer); /* Make sure we don't get any more notifications */ write_lock_bh(&rx->recvmsg_lock); @@ -612,49 +582,29 @@ void rxrpc_put_call(struct rxrpc_call *call, enum rxrpc_call_trace why) } /* - * Final call destruction - but must be done in process context. + * Free up the call under RCU. */ -static void rxrpc_destroy_call(struct work_struct *work) +static void rxrpc_rcu_free_call(struct rcu_head *rcu) { - struct rxrpc_call *call = container_of(work, struct rxrpc_call, processor); - struct rxrpc_net *rxnet = call->rxnet; + struct rxrpc_call *call = container_of(rcu, struct rxrpc_call, rcu); + struct rxrpc_net *rxnet = READ_ONCE(call->rxnet); - rxrpc_delete_call_timer(call); - - rxrpc_put_connection(call->conn, rxrpc_conn_put_call); - rxrpc_put_peer(call->peer, rxrpc_peer_put_call); kmem_cache_free(rxrpc_call_jar, call); if (atomic_dec_and_test(&rxnet->nr_calls)) wake_up_var(&rxnet->nr_calls); } /* - * Final call destruction under RCU. + * Final call destruction - but must be done in process context. */ -static void rxrpc_rcu_destroy_call(struct rcu_head *rcu) -{ - struct rxrpc_call *call = container_of(rcu, struct rxrpc_call, rcu); - - if (in_softirq()) { - INIT_WORK(&call->processor, rxrpc_destroy_call); - if (!rxrpc_queue_work(&call->processor)) - BUG(); - } else { - rxrpc_destroy_call(&call->processor); - } -} - -/* - * clean up a call - */ -void rxrpc_cleanup_call(struct rxrpc_call *call) +static void rxrpc_destroy_call(struct work_struct *work) { + struct rxrpc_call *call = container_of(work, struct rxrpc_call, destroyer); struct rxrpc_txbuf *txb; - memset(&call->sock_node, 0xcd, sizeof(call->sock_node)); - - ASSERTCMP(call->state, ==, RXRPC_CALL_COMPLETE); - ASSERT(test_bit(RXRPC_CALL_RELEASED, &call->flags)); + del_timer_sync(&call->timer); + cancel_work_sync(&call->processor); /* The processor may restart the timer */ + del_timer_sync(&call->timer); rxrpc_cleanup_ring(call); while ((txb = list_first_entry_or_null(&call->tx_buffer, @@ -664,8 +614,31 @@ void rxrpc_cleanup_call(struct rxrpc_call *call) } rxrpc_put_txbuf(call->tx_pending, rxrpc_txbuf_put_cleaned); rxrpc_free_skb(call->acks_soft_tbl, rxrpc_skb_put_ack); + rxrpc_put_connection(call->conn, rxrpc_conn_put_call); + rxrpc_put_peer(call->peer, rxrpc_peer_put_call); + call_rcu(&call->rcu, rxrpc_rcu_free_call); +} - call_rcu(&call->rcu, rxrpc_rcu_destroy_call); +/* + * clean up a call + */ +void rxrpc_cleanup_call(struct rxrpc_call *call) +{ + memset(&call->sock_node, 0xcd, sizeof(call->sock_node)); + + ASSERTCMP(call->state, ==, RXRPC_CALL_COMPLETE); + ASSERT(test_bit(RXRPC_CALL_RELEASED, &call->flags)); + + del_timer_sync(&call->timer); + cancel_work(&call->processor); + + if (in_softirq() || work_busy(&call->processor)) + /* Can't use the rxrpc workqueue as we need to cancel/flush + * something that may be running/waiting there. + */ + schedule_work(&call->destroyer); + else + rxrpc_destroy_call(&call->destroyer); } /* diff --git a/net/rxrpc/txbuf.c b/net/rxrpc/txbuf.c index 96bfee89927b..f93dc666a3a0 100644 --- a/net/rxrpc/txbuf.c +++ b/net/rxrpc/txbuf.c @@ -120,6 +120,8 @@ void rxrpc_shrink_call_tx_buffer(struct rxrpc_call *call) if (before(hard_ack, txb->seq)) break; + if (txb->seq != call->tx_bottom + 1) + rxrpc_see_txbuf(txb, rxrpc_txbuf_see_out_of_step); ASSERTCMP(txb->seq, ==, call->tx_bottom + 1); call->tx_bottom++; list_del_rcu(&txb->call_link); From 3cec055c56958c5498eeb3ed9fb2aef2d28c030f Mon Sep 17 00:00:00 2001 From: David Howells Date: Fri, 25 Nov 2022 12:43:50 +0000 Subject: [PATCH 1416/2002] rxrpc: Don't hold a ref for connection workqueue Currently, rxrpc gives the connection's work item a ref on the connection when it queues it - and this is called from the timer expiration function. The problem comes when queue_work() fails (ie. the work item is already queued): the timer routine must put the ref - but this may cause the cleanup code to run. This has the unfortunate effect that the cleanup code may then be run in softirq context - which means that any spinlocks it might need to touch have to be guarded to disable softirqs (ie. they need a "_bh" suffix). (1) Don't give a ref to the work item. (2) Simplify handling of service connections by adding a separate active count so that the refcount isn't also used for this. (3) Connection destruction for both client and service connections can then be cleaned up by putting rxrpc_put_connection() out of line and making a tidy progression through the destruction code (offloaded to a workqueue if put from softirq or processor function context). The RCU part of the cleanup then only deals with the freeing at the end. (4) Make rxrpc_queue_conn() return immediately if it sees the active count is -1 rather then queuing the connection. (5) Make sure that the cleanup routine waits for the work item to complete. (6) Stash the rxrpc_net pointer in the conn struct so that the rcu free routine can use it, even if the local endpoint has been freed. Unfortunately, neither the timer nor the work item can simply get around the problem by just using refcount_inc_not_zero() as the waits would still have to be done, and there would still be the possibility of having to put the ref in the expiration function. Note the connection work item is mostly going to go away with the main event work being transferred to the I/O thread, so the wait in (6) will become obsolete. Signed-off-by: David Howells cc: Marc Dionne cc: linux-afs@lists.infradead.org --- include/trace/events/rxrpc.h | 11 ++- net/rxrpc/ar-internal.h | 25 ++---- net/rxrpc/call_accept.c | 1 + net/rxrpc/conn_client.c | 31 ++----- net/rxrpc/conn_event.c | 4 - net/rxrpc/conn_object.c | 169 +++++++++++++++++++---------------- net/rxrpc/conn_service.c | 4 +- net/rxrpc/net_ns.c | 2 +- net/rxrpc/proc.c | 5 +- 9 files changed, 123 insertions(+), 129 deletions(-) diff --git a/include/trace/events/rxrpc.h b/include/trace/events/rxrpc.h index 4538de0079a5..44a9be9836f9 100644 --- a/include/trace/events/rxrpc.h +++ b/include/trace/events/rxrpc.h @@ -112,7 +112,6 @@ EM(rxrpc_conn_get_service_conn, "GET svc-conn") \ EM(rxrpc_conn_new_client, "NEW client ") \ EM(rxrpc_conn_new_service, "NEW service ") \ - EM(rxrpc_conn_put_already_queued, "PUT alreadyq") \ EM(rxrpc_conn_put_call, "PUT call ") \ EM(rxrpc_conn_put_call_input, "PUT inp-call") \ EM(rxrpc_conn_put_conn_input, "PUT inp-conn") \ @@ -121,13 +120,13 @@ EM(rxrpc_conn_put_local_dead, "PUT loc-dead") \ EM(rxrpc_conn_put_noreuse, "PUT noreuse ") \ EM(rxrpc_conn_put_poke, "PUT poke ") \ + EM(rxrpc_conn_put_service_reaped, "PUT svc-reap") \ EM(rxrpc_conn_put_unbundle, "PUT unbundle") \ EM(rxrpc_conn_put_unidle, "PUT unidle ") \ - EM(rxrpc_conn_put_work, "PUT work ") \ - EM(rxrpc_conn_queue_challenge, "GQ chall ") \ - EM(rxrpc_conn_queue_retry_work, "GQ retry-wk") \ - EM(rxrpc_conn_queue_rx_work, "GQ rx-work ") \ - EM(rxrpc_conn_queue_timer, "GQ timer ") \ + EM(rxrpc_conn_queue_challenge, "QUE chall ") \ + EM(rxrpc_conn_queue_retry_work, "QUE retry-wk") \ + EM(rxrpc_conn_queue_rx_work, "QUE rx-work ") \ + EM(rxrpc_conn_queue_timer, "QUE timer ") \ EM(rxrpc_conn_see_new_service_conn, "SEE new-svc ") \ EM(rxrpc_conn_see_reap_service, "SEE reap-svc") \ E_(rxrpc_conn_see_work, "SEE work ") diff --git a/net/rxrpc/ar-internal.h b/net/rxrpc/ar-internal.h index 03523a864c11..41a57c145f2b 100644 --- a/net/rxrpc/ar-internal.h +++ b/net/rxrpc/ar-internal.h @@ -76,7 +76,7 @@ struct rxrpc_net { bool kill_all_client_conns; atomic_t nr_client_conns; spinlock_t client_conn_cache_lock; /* Lock for ->*_client_conns */ - spinlock_t client_conn_discard_lock; /* Prevent multiple discarders */ + struct mutex client_conn_discard_lock; /* Prevent multiple discarders */ struct list_head idle_client_conns; struct work_struct client_conn_reaper; struct timer_list client_conn_reap_timer; @@ -432,9 +432,11 @@ struct rxrpc_connection { struct rxrpc_conn_proto proto; struct rxrpc_local *local; /* Representation of local endpoint */ struct rxrpc_peer *peer; /* Remote endpoint */ + struct rxrpc_net *rxnet; /* Network namespace to which call belongs */ struct key *key; /* Security details */ refcount_t ref; + atomic_t active; /* Active count for service conns */ struct rcu_head rcu; struct list_head cache_link; @@ -455,6 +457,7 @@ struct rxrpc_connection { struct timer_list timer; /* Conn event timer */ struct work_struct processor; /* connection event processor */ + struct work_struct destructor; /* In-process-context destroyer */ struct rxrpc_bundle *bundle; /* Client connection bundle */ struct rb_node service_node; /* Node in peer->service_conns */ struct list_head proc_link; /* link in procfs list */ @@ -897,20 +900,20 @@ void rxrpc_process_delayed_final_acks(struct rxrpc_connection *, bool); extern unsigned int rxrpc_connection_expiry; extern unsigned int rxrpc_closed_conn_expiry; -struct rxrpc_connection *rxrpc_alloc_connection(gfp_t); +struct rxrpc_connection *rxrpc_alloc_connection(struct rxrpc_net *, gfp_t); struct rxrpc_connection *rxrpc_find_connection_rcu(struct rxrpc_local *, struct sk_buff *, struct rxrpc_peer **); void __rxrpc_disconnect_call(struct rxrpc_connection *, struct rxrpc_call *); void rxrpc_disconnect_call(struct rxrpc_call *); -void rxrpc_kill_connection(struct rxrpc_connection *); -bool rxrpc_queue_conn(struct rxrpc_connection *, enum rxrpc_conn_trace); +void rxrpc_kill_client_conn(struct rxrpc_connection *); +void rxrpc_queue_conn(struct rxrpc_connection *, enum rxrpc_conn_trace); void rxrpc_see_connection(struct rxrpc_connection *, enum rxrpc_conn_trace); struct rxrpc_connection *rxrpc_get_connection(struct rxrpc_connection *, enum rxrpc_conn_trace); struct rxrpc_connection *rxrpc_get_connection_maybe(struct rxrpc_connection *, enum rxrpc_conn_trace); -void rxrpc_put_service_conn(struct rxrpc_connection *, enum rxrpc_conn_trace); +void rxrpc_put_connection(struct rxrpc_connection *, enum rxrpc_conn_trace); void rxrpc_service_connection_reaper(struct work_struct *); void rxrpc_destroy_all_connections(struct rxrpc_net *); @@ -924,18 +927,6 @@ static inline bool rxrpc_conn_is_service(const struct rxrpc_connection *conn) return !rxrpc_conn_is_client(conn); } -static inline void rxrpc_put_connection(struct rxrpc_connection *conn, - enum rxrpc_conn_trace why) -{ - if (!conn) - return; - - if (rxrpc_conn_is_client(conn)) - rxrpc_put_client_conn(conn, why); - else - rxrpc_put_service_conn(conn, why); -} - static inline void rxrpc_reduce_conn_timer(struct rxrpc_connection *conn, unsigned long expire_at) { diff --git a/net/rxrpc/call_accept.c b/net/rxrpc/call_accept.c index dd4ca4bee77f..8d106b626aa3 100644 --- a/net/rxrpc/call_accept.c +++ b/net/rxrpc/call_accept.c @@ -308,6 +308,7 @@ static struct rxrpc_call *rxrpc_alloc_incoming_call(struct rxrpc_sock *rx, rxrpc_new_incoming_connection(rx, conn, sec, skb); } else { rxrpc_get_connection(conn, rxrpc_conn_get_service_conn); + atomic_inc(&conn->active); } /* And now we can allocate and set up a new call */ diff --git a/net/rxrpc/conn_client.c b/net/rxrpc/conn_client.c index 34ff6fa85c32..9485a3d18f29 100644 --- a/net/rxrpc/conn_client.c +++ b/net/rxrpc/conn_client.c @@ -51,7 +51,7 @@ static void rxrpc_deactivate_bundle(struct rxrpc_bundle *bundle); static int rxrpc_get_client_connection_id(struct rxrpc_connection *conn, gfp_t gfp) { - struct rxrpc_net *rxnet = conn->local->rxnet; + struct rxrpc_net *rxnet = conn->rxnet; int id; _enter(""); @@ -179,7 +179,7 @@ rxrpc_alloc_client_connection(struct rxrpc_bundle *bundle, gfp_t gfp) _enter(""); - conn = rxrpc_alloc_connection(gfp); + conn = rxrpc_alloc_connection(rxnet, gfp); if (!conn) { _leave(" = -ENOMEM"); return ERR_PTR(-ENOMEM); @@ -243,7 +243,7 @@ static bool rxrpc_may_reuse_conn(struct rxrpc_connection *conn) if (!conn) goto dont_reuse; - rxnet = conn->local->rxnet; + rxnet = conn->rxnet; if (test_bit(RXRPC_CONN_DONT_REUSE, &conn->flags)) goto dont_reuse; @@ -970,7 +970,7 @@ static void rxrpc_deactivate_bundle(struct rxrpc_bundle *bundle) /* * Clean up a dead client connection. */ -static void rxrpc_kill_client_conn(struct rxrpc_connection *conn) +void rxrpc_kill_client_conn(struct rxrpc_connection *conn) { struct rxrpc_local *local = conn->local; struct rxrpc_net *rxnet = local->rxnet; @@ -981,23 +981,6 @@ static void rxrpc_kill_client_conn(struct rxrpc_connection *conn) atomic_dec(&rxnet->nr_client_conns); rxrpc_put_client_connection_id(conn); - rxrpc_kill_connection(conn); -} - -/* - * Clean up a dead client connections. - */ -void rxrpc_put_client_conn(struct rxrpc_connection *conn, - enum rxrpc_conn_trace why) -{ - unsigned int debug_id = conn->debug_id; - bool dead; - int r; - - dead = __refcount_dec_and_test(&conn->ref, &r); - trace_rxrpc_conn(debug_id, r - 1, why); - if (dead) - rxrpc_kill_client_conn(conn); } /* @@ -1023,7 +1006,7 @@ void rxrpc_discard_expired_client_conns(struct work_struct *work) } /* Don't double up on the discarding */ - if (!spin_trylock(&rxnet->client_conn_discard_lock)) { + if (!mutex_trylock(&rxnet->client_conn_discard_lock)) { _leave(" [already]"); return; } @@ -1061,6 +1044,7 @@ next: goto not_yet_expired; } + atomic_dec(&conn->active); trace_rxrpc_client(conn, -1, rxrpc_client_discard); list_del_init(&conn->cache_link); @@ -1087,7 +1071,7 @@ not_yet_expired: out: spin_unlock(&rxnet->client_conn_cache_lock); - spin_unlock(&rxnet->client_conn_discard_lock); + mutex_unlock(&rxnet->client_conn_discard_lock); _leave(""); } @@ -1127,6 +1111,7 @@ void rxrpc_clean_up_local_conns(struct rxrpc_local *local) list_for_each_entry_safe(conn, tmp, &rxnet->idle_client_conns, cache_link) { if (conn->local == local) { + atomic_dec(&conn->active); trace_rxrpc_client(conn, -1, rxrpc_client_discard); list_move(&conn->cache_link, &graveyard); } diff --git a/net/rxrpc/conn_event.c b/net/rxrpc/conn_event.c index 49d885f73fa5..23a74e35052d 100644 --- a/net/rxrpc/conn_event.c +++ b/net/rxrpc/conn_event.c @@ -478,8 +478,4 @@ void rxrpc_process_connection(struct work_struct *work) rxrpc_do_process_connection(conn); rxrpc_unuse_local(conn->local, rxrpc_local_unuse_conn_work); } - - rxrpc_put_connection(conn, rxrpc_conn_put_work); - _leave(""); - return; } diff --git a/net/rxrpc/conn_object.c b/net/rxrpc/conn_object.c index f7c271a740ed..c2e05ea29f12 100644 --- a/net/rxrpc/conn_object.c +++ b/net/rxrpc/conn_object.c @@ -19,7 +19,9 @@ unsigned int __read_mostly rxrpc_connection_expiry = 10 * 60; unsigned int __read_mostly rxrpc_closed_conn_expiry = 10; -static void rxrpc_destroy_connection(struct rcu_head *); +static void rxrpc_clean_up_connection(struct work_struct *work); +static void rxrpc_set_service_reap_timer(struct rxrpc_net *rxnet, + unsigned long reap_at); static void rxrpc_connection_timer(struct timer_list *timer) { @@ -32,7 +34,8 @@ static void rxrpc_connection_timer(struct timer_list *timer) /* * allocate a new connection */ -struct rxrpc_connection *rxrpc_alloc_connection(gfp_t gfp) +struct rxrpc_connection *rxrpc_alloc_connection(struct rxrpc_net *rxnet, + gfp_t gfp) { struct rxrpc_connection *conn; @@ -42,10 +45,12 @@ struct rxrpc_connection *rxrpc_alloc_connection(gfp_t gfp) if (conn) { INIT_LIST_HEAD(&conn->cache_link); timer_setup(&conn->timer, &rxrpc_connection_timer, 0); - INIT_WORK(&conn->processor, &rxrpc_process_connection); + INIT_WORK(&conn->processor, rxrpc_process_connection); + INIT_WORK(&conn->destructor, rxrpc_clean_up_connection); INIT_LIST_HEAD(&conn->proc_link); INIT_LIST_HEAD(&conn->link); skb_queue_head_init(&conn->rx_queue); + conn->rxnet = rxnet; conn->security = &rxrpc_no_security; spin_lock_init(&conn->state_lock); conn->debug_id = atomic_inc_return(&rxrpc_debug_id); @@ -224,53 +229,20 @@ void rxrpc_disconnect_call(struct rxrpc_call *call) set_bit(RXRPC_CALL_DISCONNECTED, &call->flags); conn->idle_timestamp = jiffies; -} - -/* - * Kill off a connection. - */ -void rxrpc_kill_connection(struct rxrpc_connection *conn) -{ - struct rxrpc_net *rxnet = conn->local->rxnet; - - ASSERT(!rcu_access_pointer(conn->channels[0].call) && - !rcu_access_pointer(conn->channels[1].call) && - !rcu_access_pointer(conn->channels[2].call) && - !rcu_access_pointer(conn->channels[3].call)); - ASSERT(list_empty(&conn->cache_link)); - - write_lock(&rxnet->conn_lock); - list_del_init(&conn->proc_link); - write_unlock(&rxnet->conn_lock); - - /* Drain the Rx queue. Note that even though we've unpublished, an - * incoming packet could still be being added to our Rx queue, so we - * will need to drain it again in the RCU cleanup handler. - */ - rxrpc_purge_queue(&conn->rx_queue); - - /* Leave final destruction to RCU. The connection processor work item - * must carry a ref on the connection to prevent us getting here whilst - * it is queued or running. - */ - call_rcu(&conn->rcu, rxrpc_destroy_connection); + if (atomic_dec_and_test(&conn->active)) + rxrpc_set_service_reap_timer(conn->rxnet, + jiffies + rxrpc_connection_expiry); } /* * Queue a connection's work processor, getting a ref to pass to the work * queue. */ -bool rxrpc_queue_conn(struct rxrpc_connection *conn, enum rxrpc_conn_trace why) +void rxrpc_queue_conn(struct rxrpc_connection *conn, enum rxrpc_conn_trace why) { - int r; - - if (!__refcount_inc_not_zero(&conn->ref, &r)) - return false; - if (rxrpc_queue_work(&conn->processor)) - trace_rxrpc_conn(conn->debug_id, why, r + 1); - else - rxrpc_put_connection(conn, rxrpc_conn_put_already_queued); - return true; + if (atomic_read(&conn->active) >= 0 && + rxrpc_queue_work(&conn->processor)) + rxrpc_see_connection(conn, why); } /* @@ -327,51 +299,96 @@ static void rxrpc_set_service_reap_timer(struct rxrpc_net *rxnet, timer_reduce(&rxnet->service_conn_reap_timer, reap_at); } -/* - * Release a service connection - */ -void rxrpc_put_service_conn(struct rxrpc_connection *conn, - enum rxrpc_conn_trace why) -{ - unsigned int debug_id = conn->debug_id; - int r; - - __refcount_dec(&conn->ref, &r); - trace_rxrpc_conn(debug_id, r - 1, why); - if (r - 1 == 1) - rxrpc_set_service_reap_timer(conn->local->rxnet, - jiffies + rxrpc_connection_expiry); -} - /* * destroy a virtual connection */ -static void rxrpc_destroy_connection(struct rcu_head *rcu) +static void rxrpc_rcu_free_connection(struct rcu_head *rcu) { struct rxrpc_connection *conn = container_of(rcu, struct rxrpc_connection, rcu); + struct rxrpc_net *rxnet = conn->rxnet; _enter("{%d,u=%d}", conn->debug_id, refcount_read(&conn->ref)); trace_rxrpc_conn(conn->debug_id, refcount_read(&conn->ref), rxrpc_conn_free); + kfree(conn); - ASSERTCMP(refcount_read(&conn->ref), ==, 0); + if (atomic_dec_and_test(&rxnet->nr_conns)) + wake_up_var(&rxnet->nr_conns); +} + +/* + * Clean up a dead connection. + */ +static void rxrpc_clean_up_connection(struct work_struct *work) +{ + struct rxrpc_connection *conn = + container_of(work, struct rxrpc_connection, destructor); + struct rxrpc_net *rxnet = conn->rxnet; + + ASSERT(!rcu_access_pointer(conn->channels[0].call) && + !rcu_access_pointer(conn->channels[1].call) && + !rcu_access_pointer(conn->channels[2].call) && + !rcu_access_pointer(conn->channels[3].call)); + ASSERT(list_empty(&conn->cache_link)); del_timer_sync(&conn->timer); + cancel_work_sync(&conn->processor); /* Processing may restart the timer */ + del_timer_sync(&conn->timer); + + write_lock(&rxnet->conn_lock); + list_del_init(&conn->proc_link); + write_unlock(&rxnet->conn_lock); + rxrpc_purge_queue(&conn->rx_queue); + rxrpc_kill_client_conn(conn); + conn->security->clear(conn); key_put(conn->key); rxrpc_put_bundle(conn->bundle, rxrpc_bundle_put_conn); rxrpc_put_peer(conn->peer, rxrpc_peer_put_conn); - - if (atomic_dec_and_test(&conn->local->rxnet->nr_conns)) - wake_up_var(&conn->local->rxnet->nr_conns); rxrpc_put_local(conn->local, rxrpc_local_put_kill_conn); - kfree(conn); - _leave(""); + /* Drain the Rx queue. Note that even though we've unpublished, an + * incoming packet could still be being added to our Rx queue, so we + * will need to drain it again in the RCU cleanup handler. + */ + rxrpc_purge_queue(&conn->rx_queue); + + call_rcu(&conn->rcu, rxrpc_rcu_free_connection); +} + +/* + * Drop a ref on a connection. + */ +void rxrpc_put_connection(struct rxrpc_connection *conn, + enum rxrpc_conn_trace why) +{ + unsigned int debug_id; + bool dead; + int r; + + if (!conn) + return; + + debug_id = conn->debug_id; + dead = __refcount_dec_and_test(&conn->ref, &r); + trace_rxrpc_conn(debug_id, r - 1, why); + if (dead) { + del_timer(&conn->timer); + cancel_work(&conn->processor); + + if (in_softirq() || work_busy(&conn->processor) || + timer_pending(&conn->timer)) + /* Can't use the rxrpc workqueue as we need to cancel/flush + * something that may be running/waiting there. + */ + schedule_work(&conn->destructor); + else + rxrpc_clean_up_connection(&conn->destructor); + } } /* @@ -383,6 +400,7 @@ void rxrpc_service_connection_reaper(struct work_struct *work) struct rxrpc_net *rxnet = container_of(work, struct rxrpc_net, service_conn_reaper); unsigned long expire_at, earliest, idle_timestamp, now; + int active; LIST_HEAD(graveyard); @@ -393,8 +411,8 @@ void rxrpc_service_connection_reaper(struct work_struct *work) write_lock(&rxnet->conn_lock); list_for_each_entry_safe(conn, _p, &rxnet->service_conns, link) { - ASSERTCMP(refcount_read(&conn->ref), >, 0); - if (likely(refcount_read(&conn->ref) > 1)) + ASSERTCMP(atomic_read(&conn->active), >=, 0); + if (likely(atomic_read(&conn->active) > 0)) continue; if (conn->state == RXRPC_CONN_SERVICE_PREALLOC) continue; @@ -405,8 +423,8 @@ void rxrpc_service_connection_reaper(struct work_struct *work) if (conn->local->service_closed) expire_at = idle_timestamp + rxrpc_closed_conn_expiry * HZ; - _debug("reap CONN %d { u=%d,t=%ld }", - conn->debug_id, refcount_read(&conn->ref), + _debug("reap CONN %d { a=%d,t=%ld }", + conn->debug_id, atomic_read(&conn->active), (long)expire_at - (long)now); if (time_before(now, expire_at)) { @@ -416,10 +434,11 @@ void rxrpc_service_connection_reaper(struct work_struct *work) } } - /* The usage count sits at 1 whilst the object is unused on the - * list; we reduce that to 0 to make the object unavailable. + /* The activity count sits at 0 whilst the conn is unused on + * the list; we reduce that to -1 to make the conn unavailable. */ - if (!refcount_dec_if_one(&conn->ref)) + active = 0; + if (!atomic_try_cmpxchg(&conn->active, &active, -1)) continue; rxrpc_see_connection(conn, rxrpc_conn_see_reap_service); @@ -443,8 +462,8 @@ void rxrpc_service_connection_reaper(struct work_struct *work) link); list_del_init(&conn->link); - ASSERTCMP(refcount_read(&conn->ref), ==, 0); - rxrpc_kill_connection(conn); + ASSERTCMP(atomic_read(&conn->active), ==, -1); + rxrpc_put_connection(conn, rxrpc_conn_put_service_reaped); } _leave(""); diff --git a/net/rxrpc/conn_service.c b/net/rxrpc/conn_service.c index 2c44d67b43dc..b5ae7c753fc3 100644 --- a/net/rxrpc/conn_service.c +++ b/net/rxrpc/conn_service.c @@ -125,7 +125,7 @@ replace_old_connection: struct rxrpc_connection *rxrpc_prealloc_service_connection(struct rxrpc_net *rxnet, gfp_t gfp) { - struct rxrpc_connection *conn = rxrpc_alloc_connection(gfp); + struct rxrpc_connection *conn = rxrpc_alloc_connection(rxnet, gfp); if (conn) { /* We maintain an extra ref on the connection whilst it is on @@ -181,6 +181,8 @@ void rxrpc_new_incoming_connection(struct rxrpc_sock *rx, conn->service_id == rx->service_upgrade.from) conn->service_id = rx->service_upgrade.to; + atomic_set(&conn->active, 1); + /* Make the connection a target for incoming packets. */ rxrpc_publish_service_conn(conn->peer, conn); } diff --git a/net/rxrpc/net_ns.c b/net/rxrpc/net_ns.c index 84242c0e467c..5905530e2f33 100644 --- a/net/rxrpc/net_ns.c +++ b/net/rxrpc/net_ns.c @@ -65,7 +65,7 @@ static __net_init int rxrpc_init_net(struct net *net) atomic_set(&rxnet->nr_client_conns, 0); rxnet->kill_all_client_conns = false; spin_lock_init(&rxnet->client_conn_cache_lock); - spin_lock_init(&rxnet->client_conn_discard_lock); + mutex_init(&rxnet->client_conn_discard_lock); INIT_LIST_HEAD(&rxnet->idle_client_conns); INIT_WORK(&rxnet->client_conn_reaper, rxrpc_discard_expired_client_conns); diff --git a/net/rxrpc/proc.c b/net/rxrpc/proc.c index bb2edf6db896..d3a6d24cf871 100644 --- a/net/rxrpc/proc.c +++ b/net/rxrpc/proc.c @@ -159,7 +159,7 @@ static int rxrpc_connection_seq_show(struct seq_file *seq, void *v) seq_puts(seq, "Proto Local " " Remote " - " SvID ConnID End Use State Key " + " SvID ConnID End Ref Act State Key " " Serial ISerial CallId0 CallId1 CallId2 CallId3\n" ); return 0; @@ -177,7 +177,7 @@ static int rxrpc_connection_seq_show(struct seq_file *seq, void *v) sprintf(rbuff, "%pISpc", &conn->peer->srx.transport); print: seq_printf(seq, - "UDP %-47.47s %-47.47s %4x %08x %s %3u" + "UDP %-47.47s %-47.47s %4x %08x %s %3u %3d" " %s %08x %08x %08x %08x %08x %08x %08x\n", lbuff, rbuff, @@ -185,6 +185,7 @@ print: conn->proto.cid, rxrpc_conn_is_service(conn) ? "Svc" : "Clt", refcount_read(&conn->ref), + atomic_read(&conn->active), rxrpc_conn_states[conn->state], key_serial(conn->key), atomic_read(&conn->serial), From 96b2d69b43a075a38df600597133f17d28525f24 Mon Sep 17 00:00:00 2001 From: David Howells Date: Thu, 23 Jan 2020 13:01:33 +0000 Subject: [PATCH 1417/2002] rxrpc: Split the receive code Split the code that handles packet reception in softirq mode as a prelude to moving all the packet processing beyond routing to the appropriate call and setting up of a new call out into process context. Signed-off-by: David Howells cc: Marc Dionne cc: linux-afs@lists.infradead.org --- net/rxrpc/Makefile | 1 + net/rxrpc/ar-internal.h | 7 + net/rxrpc/input.c | 372 +--------------------------------------- net/rxrpc/io_thread.c | 370 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 384 insertions(+), 366 deletions(-) create mode 100644 net/rxrpc/io_thread.c diff --git a/net/rxrpc/Makefile b/net/rxrpc/Makefile index 79687477d93c..e76d3459d78e 100644 --- a/net/rxrpc/Makefile +++ b/net/rxrpc/Makefile @@ -16,6 +16,7 @@ rxrpc-y := \ conn_service.o \ input.o \ insecure.o \ + io_thread.o \ key.o \ local_event.o \ local_object.o \ diff --git a/net/rxrpc/ar-internal.h b/net/rxrpc/ar-internal.h index 41a57c145f2b..523cc9c5ab12 100644 --- a/net/rxrpc/ar-internal.h +++ b/net/rxrpc/ar-internal.h @@ -946,6 +946,13 @@ void rxrpc_unpublish_service_conn(struct rxrpc_connection *); /* * input.c */ +void rxrpc_input_call_packet(struct rxrpc_call *, struct sk_buff *); +void rxrpc_input_implicit_end_call(struct rxrpc_sock *, struct rxrpc_connection *, + struct rxrpc_call *); + +/* + * io_thread.c + */ int rxrpc_input_packet(struct sock *, struct sk_buff *); /* diff --git a/net/rxrpc/input.c b/net/rxrpc/input.c index ab8b7a1be935..f4f6f3c62d03 100644 --- a/net/rxrpc/input.c +++ b/net/rxrpc/input.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later -/* RxRPC packet reception +/* Processing of received RxRPC packets * - * Copyright (C) 2007, 2016 Red Hat, Inc. All Rights Reserved. + * Copyright (C) 2020 Red Hat, Inc. All Rights Reserved. * Written by David Howells (dhowells@redhat.com) */ @@ -1029,7 +1029,7 @@ static void rxrpc_input_abort(struct rxrpc_call *call, struct sk_buff *skb) /* * Process an incoming call packet. */ -static void rxrpc_input_call_packet(struct rxrpc_call *call, +void rxrpc_input_call_packet(struct rxrpc_call *call, struct sk_buff *skb) { struct rxrpc_skb_priv *sp = rxrpc_skb(skb); @@ -1086,9 +1086,9 @@ no_free: * * TODO: If callNumber > call_id + 1, renegotiate security. */ -static void rxrpc_input_implicit_end_call(struct rxrpc_sock *rx, - struct rxrpc_connection *conn, - struct rxrpc_call *call) +void rxrpc_input_implicit_end_call(struct rxrpc_sock *rx, + struct rxrpc_connection *conn, + struct rxrpc_call *call) { switch (READ_ONCE(call->state)) { case RXRPC_CALL_SERVER_AWAIT_ACK: @@ -1109,363 +1109,3 @@ static void rxrpc_input_implicit_end_call(struct rxrpc_sock *rx, __rxrpc_disconnect_call(conn, call); spin_unlock(&rx->incoming_lock); } - -/* - * post connection-level events to the connection - * - this includes challenges, responses, some aborts and call terminal packet - * retransmission. - */ -static void rxrpc_post_packet_to_conn(struct rxrpc_connection *conn, - struct sk_buff *skb) -{ - _enter("%p,%p", conn, skb); - - skb_queue_tail(&conn->rx_queue, skb); - rxrpc_queue_conn(conn, rxrpc_conn_queue_rx_work); -} - -/* - * post endpoint-level events to the local endpoint - * - this includes debug and version messages - */ -static void rxrpc_post_packet_to_local(struct rxrpc_local *local, - struct sk_buff *skb) -{ - _enter("%p,%p", local, skb); - - if (rxrpc_get_local_maybe(local, rxrpc_local_get_queue)) { - skb_queue_tail(&local->event_queue, skb); - rxrpc_queue_local(local); - } else { - rxrpc_free_skb(skb, rxrpc_skb_put_input); - } -} - -/* - * put a packet up for transport-level abort - */ -static void rxrpc_reject_packet(struct rxrpc_local *local, struct sk_buff *skb) -{ - if (rxrpc_get_local_maybe(local, rxrpc_local_get_queue)) { - skb_queue_tail(&local->reject_queue, skb); - rxrpc_queue_local(local); - } else { - rxrpc_free_skb(skb, rxrpc_skb_put_input); - } -} - -/* - * Extract the wire header from a packet and translate the byte order. - */ -static noinline -int rxrpc_extract_header(struct rxrpc_skb_priv *sp, struct sk_buff *skb) -{ - struct rxrpc_wire_header whdr; - - /* dig out the RxRPC connection details */ - if (skb_copy_bits(skb, 0, &whdr, sizeof(whdr)) < 0) { - trace_rxrpc_rx_eproto(NULL, sp->hdr.serial, - tracepoint_string("bad_hdr")); - return -EBADMSG; - } - - memset(sp, 0, sizeof(*sp)); - sp->hdr.epoch = ntohl(whdr.epoch); - sp->hdr.cid = ntohl(whdr.cid); - sp->hdr.callNumber = ntohl(whdr.callNumber); - sp->hdr.seq = ntohl(whdr.seq); - sp->hdr.serial = ntohl(whdr.serial); - sp->hdr.flags = whdr.flags; - sp->hdr.type = whdr.type; - sp->hdr.userStatus = whdr.userStatus; - sp->hdr.securityIndex = whdr.securityIndex; - sp->hdr._rsvd = ntohs(whdr._rsvd); - sp->hdr.serviceId = ntohs(whdr.serviceId); - return 0; -} - -/* - * Extract the abort code from an ABORT packet and stash it in skb->priority. - */ -static bool rxrpc_extract_abort(struct sk_buff *skb) -{ - __be32 wtmp; - - if (skb_copy_bits(skb, sizeof(struct rxrpc_wire_header), - &wtmp, sizeof(wtmp)) < 0) - return false; - skb->priority = ntohl(wtmp); - return true; -} - -/* - * handle data received on the local endpoint - * - may be called in interrupt context - * - * [!] Note that as this is called from the encap_rcv hook, the socket is not - * held locked by the caller and nothing prevents sk_user_data on the UDP from - * being cleared in the middle of processing this function. - * - * Called with the RCU read lock held from the IP layer via UDP. - */ -int rxrpc_input_packet(struct sock *udp_sk, struct sk_buff *skb) -{ - struct rxrpc_local *local = rcu_dereference_sk_user_data(udp_sk); - struct rxrpc_connection *conn; - struct rxrpc_channel *chan; - struct rxrpc_call *call = NULL; - struct rxrpc_skb_priv *sp; - struct rxrpc_peer *peer = NULL; - struct rxrpc_sock *rx = NULL; - unsigned int channel; - - _enter("%p", udp_sk); - - if (unlikely(!local)) { - kfree_skb(skb); - return 0; - } - if (skb->tstamp == 0) - skb->tstamp = ktime_get_real(); - - rxrpc_new_skb(skb, rxrpc_skb_new_encap_rcv); - - skb_pull(skb, sizeof(struct udphdr)); - - /* The UDP protocol already released all skb resources; - * we are free to add our own data there. - */ - sp = rxrpc_skb(skb); - - /* dig out the RxRPC connection details */ - if (rxrpc_extract_header(sp, skb) < 0) - goto bad_message; - - if (IS_ENABLED(CONFIG_AF_RXRPC_INJECT_LOSS)) { - static int lose; - if ((lose++ & 7) == 7) { - trace_rxrpc_rx_lose(sp); - rxrpc_free_skb(skb, rxrpc_skb_put_lose); - return 0; - } - } - - if (skb->tstamp == 0) - skb->tstamp = ktime_get_real(); - trace_rxrpc_rx_packet(sp); - - switch (sp->hdr.type) { - case RXRPC_PACKET_TYPE_VERSION: - if (rxrpc_to_client(sp)) - goto discard; - rxrpc_post_packet_to_local(local, skb); - goto out; - - case RXRPC_PACKET_TYPE_BUSY: - if (rxrpc_to_server(sp)) - goto discard; - fallthrough; - case RXRPC_PACKET_TYPE_ACK: - case RXRPC_PACKET_TYPE_ACKALL: - if (sp->hdr.callNumber == 0) - goto bad_message; - break; - case RXRPC_PACKET_TYPE_ABORT: - if (!rxrpc_extract_abort(skb)) - return true; /* Just discard if malformed */ - break; - - case RXRPC_PACKET_TYPE_DATA: - if (sp->hdr.callNumber == 0 || - sp->hdr.seq == 0) - goto bad_message; - - /* Unshare the packet so that it can be modified for in-place - * decryption. - */ - if (sp->hdr.securityIndex != 0) { - struct sk_buff *nskb = skb_unshare(skb, GFP_ATOMIC); - if (!nskb) { - rxrpc_eaten_skb(skb, rxrpc_skb_eaten_by_unshare_nomem); - goto out; - } - - if (nskb != skb) { - rxrpc_eaten_skb(skb, rxrpc_skb_eaten_by_unshare); - skb = nskb; - rxrpc_new_skb(skb, rxrpc_skb_new_unshared); - sp = rxrpc_skb(skb); - } - } - break; - - case RXRPC_PACKET_TYPE_CHALLENGE: - if (rxrpc_to_server(sp)) - goto discard; - break; - case RXRPC_PACKET_TYPE_RESPONSE: - if (rxrpc_to_client(sp)) - goto discard; - break; - - /* Packet types 9-11 should just be ignored. */ - case RXRPC_PACKET_TYPE_PARAMS: - case RXRPC_PACKET_TYPE_10: - case RXRPC_PACKET_TYPE_11: - goto discard; - - default: - goto bad_message; - } - - if (sp->hdr.serviceId == 0) - goto bad_message; - - if (rxrpc_to_server(sp)) { - /* Weed out packets to services we're not offering. Packets - * that would begin a call are explicitly rejected and the rest - * are just discarded. - */ - rx = rcu_dereference(local->service); - if (!rx || (sp->hdr.serviceId != rx->srx.srx_service && - sp->hdr.serviceId != rx->second_service)) { - if (sp->hdr.type == RXRPC_PACKET_TYPE_DATA && - sp->hdr.seq == 1) - goto unsupported_service; - goto discard; - } - } - - conn = rxrpc_find_connection_rcu(local, skb, &peer); - if (conn) { - if (sp->hdr.securityIndex != conn->security_ix) - goto wrong_security; - - if (sp->hdr.serviceId != conn->service_id) { - int old_id; - - if (!test_bit(RXRPC_CONN_PROBING_FOR_UPGRADE, &conn->flags)) - goto reupgrade; - old_id = cmpxchg(&conn->service_id, conn->orig_service_id, - sp->hdr.serviceId); - - if (old_id != conn->orig_service_id && - old_id != sp->hdr.serviceId) - goto reupgrade; - } - - if (sp->hdr.callNumber == 0) { - /* Connection-level packet */ - _debug("CONN %p {%d}", conn, conn->debug_id); - rxrpc_post_packet_to_conn(conn, skb); - goto out; - } - - if ((int)sp->hdr.serial - (int)conn->hi_serial > 0) - conn->hi_serial = sp->hdr.serial; - - /* Call-bound packets are routed by connection channel. */ - channel = sp->hdr.cid & RXRPC_CHANNELMASK; - chan = &conn->channels[channel]; - - /* Ignore really old calls */ - if (sp->hdr.callNumber < chan->last_call) - goto discard; - - if (sp->hdr.callNumber == chan->last_call) { - if (chan->call || - sp->hdr.type == RXRPC_PACKET_TYPE_ABORT) - goto discard; - - /* For the previous service call, if completed - * successfully, we discard all further packets. - */ - if (rxrpc_conn_is_service(conn) && - chan->last_type == RXRPC_PACKET_TYPE_ACK) - goto discard; - - /* But otherwise we need to retransmit the final packet - * from data cached in the connection record. - */ - if (sp->hdr.type == RXRPC_PACKET_TYPE_DATA) - trace_rxrpc_rx_data(chan->call_debug_id, - sp->hdr.seq, - sp->hdr.serial, - sp->hdr.flags); - rxrpc_post_packet_to_conn(conn, skb); - goto out; - } - - call = rcu_dereference(chan->call); - - if (sp->hdr.callNumber > chan->call_id) { - if (rxrpc_to_client(sp)) - goto reject_packet; - if (call) - rxrpc_input_implicit_end_call(rx, conn, call); - call = NULL; - } - - if (call) { - if (sp->hdr.serviceId != call->service_id) - call->service_id = sp->hdr.serviceId; - if ((int)sp->hdr.serial - (int)call->rx_serial > 0) - call->rx_serial = sp->hdr.serial; - if (!test_bit(RXRPC_CALL_RX_HEARD, &call->flags)) - set_bit(RXRPC_CALL_RX_HEARD, &call->flags); - } - } - - if (!call || refcount_read(&call->ref) == 0) { - if (rxrpc_to_client(sp) || - sp->hdr.type != RXRPC_PACKET_TYPE_DATA) - goto bad_message; - if (sp->hdr.seq != 1) - goto discard; - call = rxrpc_new_incoming_call(local, rx, skb); - if (!call) - goto reject_packet; - } - - /* Process a call packet; this either discards or passes on the ref - * elsewhere. - */ - rxrpc_input_call_packet(call, skb); - goto out; - -discard: - rxrpc_free_skb(skb, rxrpc_skb_put_input); -out: - trace_rxrpc_rx_done(0, 0); - return 0; - -wrong_security: - trace_rxrpc_abort(0, "SEC", sp->hdr.cid, sp->hdr.callNumber, sp->hdr.seq, - RXKADINCONSISTENCY, EBADMSG); - skb->priority = RXKADINCONSISTENCY; - goto post_abort; - -unsupported_service: - trace_rxrpc_abort(0, "INV", sp->hdr.cid, sp->hdr.callNumber, sp->hdr.seq, - RX_INVALID_OPERATION, EOPNOTSUPP); - skb->priority = RX_INVALID_OPERATION; - goto post_abort; - -reupgrade: - trace_rxrpc_abort(0, "UPG", sp->hdr.cid, sp->hdr.callNumber, sp->hdr.seq, - RX_PROTOCOL_ERROR, EBADMSG); - goto protocol_error; - -bad_message: - trace_rxrpc_abort(0, "BAD", sp->hdr.cid, sp->hdr.callNumber, sp->hdr.seq, - RX_PROTOCOL_ERROR, EBADMSG); -protocol_error: - skb->priority = RX_PROTOCOL_ERROR; -post_abort: - skb->mark = RXRPC_SKB_MARK_REJECT_ABORT; -reject_packet: - trace_rxrpc_rx_done(skb->mark, skb->priority); - rxrpc_reject_packet(local, skb); - _leave(" [badmsg]"); - return 0; -} diff --git a/net/rxrpc/io_thread.c b/net/rxrpc/io_thread.c new file mode 100644 index 000000000000..d2aaad5afa1d --- /dev/null +++ b/net/rxrpc/io_thread.c @@ -0,0 +1,370 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* RxRPC packet reception + * + * Copyright (C) 2007, 2016 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include "ar-internal.h" + +/* + * post connection-level events to the connection + * - this includes challenges, responses, some aborts and call terminal packet + * retransmission. + */ +static void rxrpc_post_packet_to_conn(struct rxrpc_connection *conn, + struct sk_buff *skb) +{ + _enter("%p,%p", conn, skb); + + skb_queue_tail(&conn->rx_queue, skb); + rxrpc_queue_conn(conn, rxrpc_conn_queue_rx_work); +} + +/* + * post endpoint-level events to the local endpoint + * - this includes debug and version messages + */ +static void rxrpc_post_packet_to_local(struct rxrpc_local *local, + struct sk_buff *skb) +{ + _enter("%p,%p", local, skb); + + if (rxrpc_get_local_maybe(local, rxrpc_local_get_queue)) { + skb_queue_tail(&local->event_queue, skb); + rxrpc_queue_local(local); + } else { + rxrpc_free_skb(skb, rxrpc_skb_put_input); + } +} + +/* + * put a packet up for transport-level abort + */ +static void rxrpc_reject_packet(struct rxrpc_local *local, struct sk_buff *skb) +{ + if (rxrpc_get_local_maybe(local, rxrpc_local_get_queue)) { + skb_queue_tail(&local->reject_queue, skb); + rxrpc_queue_local(local); + } else { + rxrpc_free_skb(skb, rxrpc_skb_put_input); + } +} + +/* + * Extract the wire header from a packet and translate the byte order. + */ +static noinline +int rxrpc_extract_header(struct rxrpc_skb_priv *sp, struct sk_buff *skb) +{ + struct rxrpc_wire_header whdr; + + /* dig out the RxRPC connection details */ + if (skb_copy_bits(skb, 0, &whdr, sizeof(whdr)) < 0) { + trace_rxrpc_rx_eproto(NULL, sp->hdr.serial, + tracepoint_string("bad_hdr")); + return -EBADMSG; + } + + memset(sp, 0, sizeof(*sp)); + sp->hdr.epoch = ntohl(whdr.epoch); + sp->hdr.cid = ntohl(whdr.cid); + sp->hdr.callNumber = ntohl(whdr.callNumber); + sp->hdr.seq = ntohl(whdr.seq); + sp->hdr.serial = ntohl(whdr.serial); + sp->hdr.flags = whdr.flags; + sp->hdr.type = whdr.type; + sp->hdr.userStatus = whdr.userStatus; + sp->hdr.securityIndex = whdr.securityIndex; + sp->hdr._rsvd = ntohs(whdr._rsvd); + sp->hdr.serviceId = ntohs(whdr.serviceId); + return 0; +} + +/* + * Extract the abort code from an ABORT packet and stash it in skb->priority. + */ +static bool rxrpc_extract_abort(struct sk_buff *skb) +{ + __be32 wtmp; + + if (skb_copy_bits(skb, sizeof(struct rxrpc_wire_header), + &wtmp, sizeof(wtmp)) < 0) + return false; + skb->priority = ntohl(wtmp); + return true; +} + +/* + * handle data received on the local endpoint + * - may be called in interrupt context + * + * [!] Note that as this is called from the encap_rcv hook, the socket is not + * held locked by the caller and nothing prevents sk_user_data on the UDP from + * being cleared in the middle of processing this function. + * + * Called with the RCU read lock held from the IP layer via UDP. + */ +int rxrpc_input_packet(struct sock *udp_sk, struct sk_buff *skb) +{ + struct rxrpc_local *local = rcu_dereference_sk_user_data(udp_sk); + struct rxrpc_connection *conn; + struct rxrpc_channel *chan; + struct rxrpc_call *call = NULL; + struct rxrpc_skb_priv *sp; + struct rxrpc_peer *peer = NULL; + struct rxrpc_sock *rx = NULL; + unsigned int channel; + + _enter("%p", udp_sk); + + if (unlikely(!local)) { + kfree_skb(skb); + return 0; + } + if (skb->tstamp == 0) + skb->tstamp = ktime_get_real(); + + rxrpc_new_skb(skb, rxrpc_skb_new_encap_rcv); + + skb_pull(skb, sizeof(struct udphdr)); + + /* The UDP protocol already released all skb resources; + * we are free to add our own data there. + */ + sp = rxrpc_skb(skb); + + /* dig out the RxRPC connection details */ + if (rxrpc_extract_header(sp, skb) < 0) + goto bad_message; + + if (IS_ENABLED(CONFIG_AF_RXRPC_INJECT_LOSS)) { + static int lose; + if ((lose++ & 7) == 7) { + trace_rxrpc_rx_lose(sp); + rxrpc_free_skb(skb, rxrpc_skb_put_lose); + return 0; + } + } + + if (skb->tstamp == 0) + skb->tstamp = ktime_get_real(); + trace_rxrpc_rx_packet(sp); + + switch (sp->hdr.type) { + case RXRPC_PACKET_TYPE_VERSION: + if (rxrpc_to_client(sp)) + goto discard; + rxrpc_post_packet_to_local(local, skb); + goto out; + + case RXRPC_PACKET_TYPE_BUSY: + if (rxrpc_to_server(sp)) + goto discard; + fallthrough; + case RXRPC_PACKET_TYPE_ACK: + case RXRPC_PACKET_TYPE_ACKALL: + if (sp->hdr.callNumber == 0) + goto bad_message; + break; + case RXRPC_PACKET_TYPE_ABORT: + if (!rxrpc_extract_abort(skb)) + return true; /* Just discard if malformed */ + break; + + case RXRPC_PACKET_TYPE_DATA: + if (sp->hdr.callNumber == 0 || + sp->hdr.seq == 0) + goto bad_message; + + /* Unshare the packet so that it can be modified for in-place + * decryption. + */ + if (sp->hdr.securityIndex != 0) { + struct sk_buff *nskb = skb_unshare(skb, GFP_ATOMIC); + if (!nskb) { + rxrpc_eaten_skb(skb, rxrpc_skb_eaten_by_unshare_nomem); + goto out; + } + + if (nskb != skb) { + rxrpc_eaten_skb(skb, rxrpc_skb_eaten_by_unshare); + skb = nskb; + rxrpc_new_skb(skb, rxrpc_skb_new_unshared); + sp = rxrpc_skb(skb); + } + } + break; + + case RXRPC_PACKET_TYPE_CHALLENGE: + if (rxrpc_to_server(sp)) + goto discard; + break; + case RXRPC_PACKET_TYPE_RESPONSE: + if (rxrpc_to_client(sp)) + goto discard; + break; + + /* Packet types 9-11 should just be ignored. */ + case RXRPC_PACKET_TYPE_PARAMS: + case RXRPC_PACKET_TYPE_10: + case RXRPC_PACKET_TYPE_11: + goto discard; + + default: + goto bad_message; + } + + if (sp->hdr.serviceId == 0) + goto bad_message; + + if (rxrpc_to_server(sp)) { + /* Weed out packets to services we're not offering. Packets + * that would begin a call are explicitly rejected and the rest + * are just discarded. + */ + rx = rcu_dereference(local->service); + if (!rx || (sp->hdr.serviceId != rx->srx.srx_service && + sp->hdr.serviceId != rx->second_service)) { + if (sp->hdr.type == RXRPC_PACKET_TYPE_DATA && + sp->hdr.seq == 1) + goto unsupported_service; + goto discard; + } + } + + conn = rxrpc_find_connection_rcu(local, skb, &peer); + if (conn) { + if (sp->hdr.securityIndex != conn->security_ix) + goto wrong_security; + + if (sp->hdr.serviceId != conn->service_id) { + int old_id; + + if (!test_bit(RXRPC_CONN_PROBING_FOR_UPGRADE, &conn->flags)) + goto reupgrade; + old_id = cmpxchg(&conn->service_id, conn->orig_service_id, + sp->hdr.serviceId); + + if (old_id != conn->orig_service_id && + old_id != sp->hdr.serviceId) + goto reupgrade; + } + + if (sp->hdr.callNumber == 0) { + /* Connection-level packet */ + _debug("CONN %p {%d}", conn, conn->debug_id); + rxrpc_post_packet_to_conn(conn, skb); + goto out; + } + + if ((int)sp->hdr.serial - (int)conn->hi_serial > 0) + conn->hi_serial = sp->hdr.serial; + + /* Call-bound packets are routed by connection channel. */ + channel = sp->hdr.cid & RXRPC_CHANNELMASK; + chan = &conn->channels[channel]; + + /* Ignore really old calls */ + if (sp->hdr.callNumber < chan->last_call) + goto discard; + + if (sp->hdr.callNumber == chan->last_call) { + if (chan->call || + sp->hdr.type == RXRPC_PACKET_TYPE_ABORT) + goto discard; + + /* For the previous service call, if completed + * successfully, we discard all further packets. + */ + if (rxrpc_conn_is_service(conn) && + chan->last_type == RXRPC_PACKET_TYPE_ACK) + goto discard; + + /* But otherwise we need to retransmit the final packet + * from data cached in the connection record. + */ + if (sp->hdr.type == RXRPC_PACKET_TYPE_DATA) + trace_rxrpc_rx_data(chan->call_debug_id, + sp->hdr.seq, + sp->hdr.serial, + sp->hdr.flags); + rxrpc_post_packet_to_conn(conn, skb); + goto out; + } + + call = rcu_dereference(chan->call); + + if (sp->hdr.callNumber > chan->call_id) { + if (rxrpc_to_client(sp)) + goto reject_packet; + if (call) + rxrpc_input_implicit_end_call(rx, conn, call); + call = NULL; + } + + if (call) { + if (sp->hdr.serviceId != call->service_id) + call->service_id = sp->hdr.serviceId; + if ((int)sp->hdr.serial - (int)call->rx_serial > 0) + call->rx_serial = sp->hdr.serial; + if (!test_bit(RXRPC_CALL_RX_HEARD, &call->flags)) + set_bit(RXRPC_CALL_RX_HEARD, &call->flags); + } + } + + if (!call || refcount_read(&call->ref) == 0) { + if (rxrpc_to_client(sp) || + sp->hdr.type != RXRPC_PACKET_TYPE_DATA) + goto bad_message; + if (sp->hdr.seq != 1) + goto discard; + call = rxrpc_new_incoming_call(local, rx, skb); + if (!call) + goto reject_packet; + } + + /* Process a call packet; this either discards or passes on the ref + * elsewhere. + */ + rxrpc_input_call_packet(call, skb); + goto out; + +discard: + rxrpc_free_skb(skb, rxrpc_skb_put_input); +out: + trace_rxrpc_rx_done(0, 0); + return 0; + +wrong_security: + trace_rxrpc_abort(0, "SEC", sp->hdr.cid, sp->hdr.callNumber, sp->hdr.seq, + RXKADINCONSISTENCY, EBADMSG); + skb->priority = RXKADINCONSISTENCY; + goto post_abort; + +unsupported_service: + trace_rxrpc_abort(0, "INV", sp->hdr.cid, sp->hdr.callNumber, sp->hdr.seq, + RX_INVALID_OPERATION, EOPNOTSUPP); + skb->priority = RX_INVALID_OPERATION; + goto post_abort; + +reupgrade: + trace_rxrpc_abort(0, "UPG", sp->hdr.cid, sp->hdr.callNumber, sp->hdr.seq, + RX_PROTOCOL_ERROR, EBADMSG); + goto protocol_error; + +bad_message: + trace_rxrpc_abort(0, "BAD", sp->hdr.cid, sp->hdr.callNumber, sp->hdr.seq, + RX_PROTOCOL_ERROR, EBADMSG); +protocol_error: + skb->priority = RX_PROTOCOL_ERROR; +post_abort: + skb->mark = RXRPC_SKB_MARK_REJECT_ABORT; +reject_packet: + trace_rxrpc_rx_done(skb->mark, skb->priority); + rxrpc_reject_packet(local, skb); + _leave(" [badmsg]"); + return 0; +} From a275da62e8c111b897b9cb73eb91df2f4e475ca5 Mon Sep 17 00:00:00 2001 From: David Howells Date: Mon, 10 Oct 2022 08:45:20 +0100 Subject: [PATCH 1418/2002] rxrpc: Create a per-local endpoint receive queue and I/O thread Create a per-local receive queue to which, in a future patch, all incoming packets will be directed and an I/O thread that will process those packets and perform all transmission of packets. Destruction of the local endpoint is also moved from the local processor work item (which will be absorbed) to the thread. Signed-off-by: David Howells cc: Marc Dionne cc: linux-afs@lists.infradead.org --- net/rxrpc/ar-internal.h | 10 ++++++++ net/rxrpc/io_thread.c | 51 +++++++++++++++++++++++++++++++++++++++- net/rxrpc/local_object.c | 39 ++++++++++++++++-------------- net/rxrpc/proc.c | 12 +++++++--- 4 files changed, 91 insertions(+), 21 deletions(-) diff --git a/net/rxrpc/ar-internal.h b/net/rxrpc/ar-internal.h index 523cc9c5ab12..de82c25956a6 100644 --- a/net/rxrpc/ar-internal.h +++ b/net/rxrpc/ar-internal.h @@ -110,6 +110,8 @@ struct rxrpc_net { atomic_t stat_rx_acks[256]; atomic_t stat_why_req_ack[8]; + + atomic_t stat_io_loop; }; /* @@ -280,12 +282,14 @@ struct rxrpc_local { struct hlist_node link; struct socket *socket; /* my UDP socket */ struct work_struct processor; + struct task_struct *io_thread; struct list_head ack_tx_queue; /* List of ACKs that need sending */ spinlock_t ack_tx_lock; /* ACK list lock */ struct rxrpc_sock __rcu *service; /* Service(s) listening on this endpoint */ struct rw_semaphore defrag_sem; /* control re-enablement of IP DF bit */ struct sk_buff_head reject_queue; /* packets awaiting rejection */ struct sk_buff_head event_queue; /* endpoint event packets awaiting processing */ + struct sk_buff_head rx_queue; /* Received packets */ struct rb_root client_bundles; /* Client connection bundles by socket params */ spinlock_t client_bundles_lock; /* Lock for client_bundles */ spinlock_t lock; /* access lock */ @@ -954,6 +958,11 @@ void rxrpc_input_implicit_end_call(struct rxrpc_sock *, struct rxrpc_connection * io_thread.c */ int rxrpc_input_packet(struct sock *, struct sk_buff *); +int rxrpc_io_thread(void *data); +static inline void rxrpc_wake_up_io_thread(struct rxrpc_local *local) +{ + wake_up_process(local->io_thread); +} /* * insecure.c @@ -984,6 +993,7 @@ void rxrpc_put_local(struct rxrpc_local *, enum rxrpc_local_trace); struct rxrpc_local *rxrpc_use_local(struct rxrpc_local *, enum rxrpc_local_trace); void rxrpc_unuse_local(struct rxrpc_local *, enum rxrpc_local_trace); void rxrpc_queue_local(struct rxrpc_local *); +void rxrpc_destroy_local(struct rxrpc_local *local); void rxrpc_destroy_all_locals(struct rxrpc_net *); static inline bool __rxrpc_unuse_local(struct rxrpc_local *local, diff --git a/net/rxrpc/io_thread.c b/net/rxrpc/io_thread.c index d2aaad5afa1d..0b3e096e3d50 100644 --- a/net/rxrpc/io_thread.c +++ b/net/rxrpc/io_thread.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* RxRPC packet reception * - * Copyright (C) 2007, 2016 Red Hat, Inc. All Rights Reserved. + * Copyright (C) 2007, 2016, 2022 Red Hat, Inc. All Rights Reserved. * Written by David Howells (dhowells@redhat.com) */ @@ -368,3 +368,52 @@ reject_packet: _leave(" [badmsg]"); return 0; } + +/* + * I/O and event handling thread. + */ +int rxrpc_io_thread(void *data) +{ + struct sk_buff_head rx_queue; + struct rxrpc_local *local = data; + struct sk_buff *skb; + + skb_queue_head_init(&rx_queue); + + set_user_nice(current, MIN_NICE); + + for (;;) { + rxrpc_inc_stat(local->rxnet, stat_io_loop); + + /* Process received packets and errors. */ + if ((skb = __skb_dequeue(&rx_queue))) { + // TODO: Input packet + rxrpc_free_skb(skb, rxrpc_skb_put_input); + continue; + } + + if (!skb_queue_empty(&local->rx_queue)) { + spin_lock_irq(&local->rx_queue.lock); + skb_queue_splice_tail_init(&local->rx_queue, &rx_queue); + spin_unlock_irq(&local->rx_queue.lock); + continue; + } + + set_current_state(TASK_INTERRUPTIBLE); + if (!skb_queue_empty(&local->rx_queue)) { + __set_current_state(TASK_RUNNING); + continue; + } + + if (kthread_should_stop()) + break; + schedule(); + } + + __set_current_state(TASK_RUNNING); + rxrpc_see_local(local, rxrpc_local_stop); + rxrpc_destroy_local(local); + local->io_thread = NULL; + rxrpc_see_local(local, rxrpc_local_stopped); + return 0; +} diff --git a/net/rxrpc/local_object.c b/net/rxrpc/local_object.c index 1617ce651b9b..7c61349984e3 100644 --- a/net/rxrpc/local_object.c +++ b/net/rxrpc/local_object.c @@ -103,6 +103,7 @@ static struct rxrpc_local *rxrpc_alloc_local(struct rxrpc_net *rxnet, init_rwsem(&local->defrag_sem); skb_queue_head_init(&local->reject_queue); skb_queue_head_init(&local->event_queue); + skb_queue_head_init(&local->rx_queue); local->client_bundles = RB_ROOT; spin_lock_init(&local->client_bundles_lock); spin_lock_init(&local->lock); @@ -126,6 +127,7 @@ static int rxrpc_open_socket(struct rxrpc_local *local, struct net *net) struct udp_tunnel_sock_cfg tuncfg = {NULL}; struct sockaddr_rxrpc *srx = &local->srx; struct udp_port_cfg udp_conf = {0}; + struct task_struct *io_thread; struct sock *usk; int ret; @@ -185,8 +187,23 @@ static int rxrpc_open_socket(struct rxrpc_local *local, struct net *net) BUG(); } + io_thread = kthread_run(rxrpc_io_thread, local, + "krxrpcio/%u", ntohs(udp_conf.local_udp_port)); + if (IS_ERR(io_thread)) { + ret = PTR_ERR(io_thread); + goto error_sock; + } + + local->io_thread = io_thread; _leave(" = 0"); return 0; + +error_sock: + kernel_sock_shutdown(local->socket, SHUT_RDWR); + local->socket->sk->sk_user_data = NULL; + sock_release(local->socket); + local->socket = NULL; + return ret; } /* @@ -360,19 +377,8 @@ struct rxrpc_local *rxrpc_use_local(struct rxrpc_local *local, */ void rxrpc_unuse_local(struct rxrpc_local *local, enum rxrpc_local_trace why) { - unsigned int debug_id; - int r, u; - - if (local) { - debug_id = local->debug_id; - r = refcount_read(&local->ref); - u = atomic_dec_return(&local->active_users); - trace_rxrpc_local(debug_id, why, r, u); - if (u == 0) { - rxrpc_get_local(local, rxrpc_local_get_queue); - rxrpc_queue_local(local); - } - } + if (local && __rxrpc_unuse_local(local, why)) + kthread_stop(local->io_thread); } /* @@ -382,7 +388,7 @@ void rxrpc_unuse_local(struct rxrpc_local *local, enum rxrpc_local_trace why) * Closing the socket cannot be done from bottom half context or RCU callback * context because it might sleep. */ -static void rxrpc_local_destroyer(struct rxrpc_local *local) +void rxrpc_destroy_local(struct rxrpc_local *local) { struct socket *socket = local->socket; struct rxrpc_net *rxnet = local->rxnet; @@ -411,6 +417,7 @@ static void rxrpc_local_destroyer(struct rxrpc_local *local) */ rxrpc_purge_queue(&local->reject_queue); rxrpc_purge_queue(&local->event_queue); + rxrpc_purge_queue(&local->rx_queue); } /* @@ -430,10 +437,8 @@ static void rxrpc_local_processor(struct work_struct *work) do { again = false; - if (!__rxrpc_use_local(local, rxrpc_local_use_work)) { - rxrpc_local_destroyer(local); + if (!__rxrpc_use_local(local, rxrpc_local_use_work)) break; - } if (!list_empty(&local->ack_tx_queue)) { rxrpc_transmit_ack_packets(local); diff --git a/net/rxrpc/proc.c b/net/rxrpc/proc.c index d3a6d24cf871..35d5b43c677e 100644 --- a/net/rxrpc/proc.c +++ b/net/rxrpc/proc.c @@ -342,7 +342,7 @@ static int rxrpc_local_seq_show(struct seq_file *seq, void *v) if (v == SEQ_START_TOKEN) { seq_puts(seq, "Proto Local " - " Use Act\n"); + " Use Act RxQ\n"); return 0; } @@ -351,10 +351,11 @@ static int rxrpc_local_seq_show(struct seq_file *seq, void *v) sprintf(lbuff, "%pISpc", &local->srx.transport); seq_printf(seq, - "UDP %-47.47s %3u %3u\n", + "UDP %-47.47s %3u %3u %3u\n", lbuff, refcount_read(&local->ref), - atomic_read(&local->active_users)); + atomic_read(&local->active_users), + local->rx_queue.qlen); return 0; } @@ -463,6 +464,9 @@ int rxrpc_stats_show(struct seq_file *seq, void *v) "Buffers : txb=%u rxb=%u\n", atomic_read(&rxrpc_nr_txbuf), atomic_read(&rxrpc_n_rx_skbs)); + seq_printf(seq, + "IO-thread: loops=%u\n", + atomic_read(&rxnet->stat_io_loop)); return 0; } @@ -492,5 +496,7 @@ int rxrpc_stats_clear(struct file *file, char *buf, size_t size) memset(&rxnet->stat_rx_acks, 0, sizeof(rxnet->stat_rx_acks)); memset(&rxnet->stat_why_req_ack, 0, sizeof(rxnet->stat_why_req_ack)); + + atomic_set(&rxnet->stat_io_loop, 0); return size; } From 446b3e14525b477e441a6bb8ce56cea12512acc2 Mon Sep 17 00:00:00 2001 From: David Howells Date: Mon, 10 Oct 2022 10:55:24 +0100 Subject: [PATCH 1419/2002] rxrpc: Move packet reception processing into I/O thread Split the packet input handler to make the softirq side just dump the received packet into the local endpoint receive queue and then call the remainder of the input function from the I/O thread. Signed-off-by: David Howells cc: Marc Dionne cc: linux-afs@lists.infradead.org --- net/rxrpc/ar-internal.h | 3 +- net/rxrpc/call_event.c | 4 +-- net/rxrpc/call_object.c | 2 +- net/rxrpc/io_thread.c | 61 +++++++++++++++++++++++++++------------- net/rxrpc/local_object.c | 2 +- 5 files changed, 47 insertions(+), 25 deletions(-) diff --git a/net/rxrpc/ar-internal.h b/net/rxrpc/ar-internal.h index de82c25956a6..044815ba2b49 100644 --- a/net/rxrpc/ar-internal.h +++ b/net/rxrpc/ar-internal.h @@ -36,6 +36,7 @@ struct rxrpc_txbuf; * to pass supplementary information. */ enum rxrpc_skb_mark { + RXRPC_SKB_MARK_PACKET, /* Received packet */ RXRPC_SKB_MARK_REJECT_BUSY, /* Reject with BUSY */ RXRPC_SKB_MARK_REJECT_ABORT, /* Reject with ABORT (code in skb->priority) */ }; @@ -957,7 +958,7 @@ void rxrpc_input_implicit_end_call(struct rxrpc_sock *, struct rxrpc_connection /* * io_thread.c */ -int rxrpc_input_packet(struct sock *, struct sk_buff *); +int rxrpc_encap_rcv(struct sock *, struct sk_buff *); int rxrpc_io_thread(void *data); static inline void rxrpc_wake_up_io_thread(struct rxrpc_local *local) { diff --git a/net/rxrpc/call_event.c b/net/rxrpc/call_event.c index 049b92b1c040..3925b55e2064 100644 --- a/net/rxrpc/call_event.c +++ b/net/rxrpc/call_event.c @@ -83,7 +83,7 @@ void rxrpc_send_ACK(struct rxrpc_call *call, u8 ack_reason, rxrpc_inc_stat(call->rxnet, stat_tx_acks[ack_reason]); txb = rxrpc_alloc_txbuf(call, RXRPC_PACKET_TYPE_ACK, - in_softirq() ? GFP_ATOMIC | __GFP_NOWARN : GFP_NOFS); + rcu_read_lock_held() ? GFP_ATOMIC | __GFP_NOWARN : GFP_NOFS); if (!txb) { kleave(" = -ENOMEM"); return; @@ -111,7 +111,7 @@ void rxrpc_send_ACK(struct rxrpc_call *call, u8 ack_reason, spin_unlock_bh(&local->ack_tx_lock); trace_rxrpc_send_ack(call, why, ack_reason, serial); - if (in_task()) { + if (!rcu_read_lock_held()) { rxrpc_transmit_ack_packets(call->peer->local); } else { rxrpc_get_local(local, rxrpc_local_get_queue); diff --git a/net/rxrpc/call_object.c b/net/rxrpc/call_object.c index 9cd7e0190ef4..57c8d4cc900a 100644 --- a/net/rxrpc/call_object.c +++ b/net/rxrpc/call_object.c @@ -632,7 +632,7 @@ void rxrpc_cleanup_call(struct rxrpc_call *call) del_timer_sync(&call->timer); cancel_work(&call->processor); - if (in_softirq() || work_busy(&call->processor)) + if (rcu_read_lock_held() || work_busy(&call->processor)) /* Can't use the rxrpc workqueue as we need to cancel/flush * something that may be running/waiting there. */ diff --git a/net/rxrpc/io_thread.c b/net/rxrpc/io_thread.c index 0b3e096e3d50..ee2e36c46ae2 100644 --- a/net/rxrpc/io_thread.c +++ b/net/rxrpc/io_thread.c @@ -9,6 +9,34 @@ #include "ar-internal.h" +/* + * handle data received on the local endpoint + * - may be called in interrupt context + * + * [!] Note that as this is called from the encap_rcv hook, the socket is not + * held locked by the caller and nothing prevents sk_user_data on the UDP from + * being cleared in the middle of processing this function. + * + * Called with the RCU read lock held from the IP layer via UDP. + */ +int rxrpc_encap_rcv(struct sock *udp_sk, struct sk_buff *skb) +{ + struct rxrpc_local *local = rcu_dereference_sk_user_data(udp_sk); + + if (unlikely(!local)) { + kfree_skb(skb); + return 0; + } + if (skb->tstamp == 0) + skb->tstamp = ktime_get_real(); + + skb->mark = RXRPC_SKB_MARK_PACKET; + rxrpc_new_skb(skb, rxrpc_skb_new_encap_rcv); + skb_queue_tail(&local->rx_queue, skb); + rxrpc_wake_up_io_thread(local); + return 0; +} + /* * post connection-level events to the connection * - this includes challenges, responses, some aborts and call terminal packet @@ -98,18 +126,10 @@ static bool rxrpc_extract_abort(struct sk_buff *skb) } /* - * handle data received on the local endpoint - * - may be called in interrupt context - * - * [!] Note that as this is called from the encap_rcv hook, the socket is not - * held locked by the caller and nothing prevents sk_user_data on the UDP from - * being cleared in the middle of processing this function. - * - * Called with the RCU read lock held from the IP layer via UDP. + * Process packets received on the local endpoint */ -int rxrpc_input_packet(struct sock *udp_sk, struct sk_buff *skb) +static int rxrpc_input_packet(struct rxrpc_local *local, struct sk_buff *skb) { - struct rxrpc_local *local = rcu_dereference_sk_user_data(udp_sk); struct rxrpc_connection *conn; struct rxrpc_channel *chan; struct rxrpc_call *call = NULL; @@ -118,17 +138,9 @@ int rxrpc_input_packet(struct sock *udp_sk, struct sk_buff *skb) struct rxrpc_sock *rx = NULL; unsigned int channel; - _enter("%p", udp_sk); - - if (unlikely(!local)) { - kfree_skb(skb); - return 0; - } if (skb->tstamp == 0) skb->tstamp = ktime_get_real(); - rxrpc_new_skb(skb, rxrpc_skb_new_encap_rcv); - skb_pull(skb, sizeof(struct udphdr)); /* The UDP protocol already released all skb resources; @@ -387,8 +399,17 @@ int rxrpc_io_thread(void *data) /* Process received packets and errors. */ if ((skb = __skb_dequeue(&rx_queue))) { - // TODO: Input packet - rxrpc_free_skb(skb, rxrpc_skb_put_input); + switch (skb->mark) { + case RXRPC_SKB_MARK_PACKET: + rcu_read_lock(); + rxrpc_input_packet(local, skb); + rcu_read_unlock(); + break; + default: + WARN_ON_ONCE(1); + rxrpc_free_skb(skb, rxrpc_skb_put_unknown); + break; + } continue; } diff --git a/net/rxrpc/local_object.c b/net/rxrpc/local_object.c index 7c61349984e3..6b4d77219f36 100644 --- a/net/rxrpc/local_object.c +++ b/net/rxrpc/local_object.c @@ -154,7 +154,7 @@ static int rxrpc_open_socket(struct rxrpc_local *local, struct net *net) } tuncfg.encap_type = UDP_ENCAP_RXRPC; - tuncfg.encap_rcv = rxrpc_input_packet; + tuncfg.encap_rcv = rxrpc_encap_rcv; tuncfg.encap_err_rcv = rxrpc_encap_err_rcv; tuncfg.sk_user_data = local; setup_udp_tunnel_sock(net, local->socket, &tuncfg); From ff7348254e704b6d0121970e311a6b699268e1ac Mon Sep 17 00:00:00 2001 From: David Howells Date: Mon, 10 Oct 2022 11:47:31 +0100 Subject: [PATCH 1420/2002] rxrpc: Move error processing into the local endpoint I/O thread Move the processing of error packets into the local endpoint I/O thread, leaving the handover from UDP to merely transfer them into the local endpoint queue. Signed-off-by: David Howells cc: Marc Dionne cc: linux-afs@lists.infradead.org --- net/rxrpc/ar-internal.h | 4 +++- net/rxrpc/io_thread.c | 29 +++++++++++++++++++++++++++++ net/rxrpc/peer_event.c | 41 ++++++----------------------------------- 3 files changed, 38 insertions(+), 36 deletions(-) diff --git a/net/rxrpc/ar-internal.h b/net/rxrpc/ar-internal.h index 044815ba2b49..566377c64184 100644 --- a/net/rxrpc/ar-internal.h +++ b/net/rxrpc/ar-internal.h @@ -37,6 +37,7 @@ struct rxrpc_txbuf; */ enum rxrpc_skb_mark { RXRPC_SKB_MARK_PACKET, /* Received packet */ + RXRPC_SKB_MARK_ERROR, /* Error notification */ RXRPC_SKB_MARK_REJECT_BUSY, /* Reject with BUSY */ RXRPC_SKB_MARK_REJECT_ABORT, /* Reject with ABORT (code in skb->priority) */ }; @@ -959,6 +960,7 @@ void rxrpc_input_implicit_end_call(struct rxrpc_sock *, struct rxrpc_connection * io_thread.c */ int rxrpc_encap_rcv(struct sock *, struct sk_buff *); +void rxrpc_error_report(struct sock *); int rxrpc_io_thread(void *data); static inline void rxrpc_wake_up_io_thread(struct rxrpc_local *local) { @@ -1063,7 +1065,7 @@ void rxrpc_send_keepalive(struct rxrpc_peer *); /* * peer_event.c */ -void rxrpc_error_report(struct sock *); +void rxrpc_input_error(struct rxrpc_local *, struct sk_buff *); void rxrpc_peer_keepalive_worker(struct work_struct *); /* diff --git a/net/rxrpc/io_thread.c b/net/rxrpc/io_thread.c index ee2e36c46ae2..416c6101cf78 100644 --- a/net/rxrpc/io_thread.c +++ b/net/rxrpc/io_thread.c @@ -37,6 +37,31 @@ int rxrpc_encap_rcv(struct sock *udp_sk, struct sk_buff *skb) return 0; } +/* + * Handle an error received on the local endpoint. + */ +void rxrpc_error_report(struct sock *sk) +{ + struct rxrpc_local *local; + struct sk_buff *skb; + + rcu_read_lock(); + local = rcu_dereference_sk_user_data(sk); + if (unlikely(!local)) { + rcu_read_unlock(); + return; + } + + while ((skb = skb_dequeue(&sk->sk_error_queue))) { + skb->mark = RXRPC_SKB_MARK_ERROR; + rxrpc_new_skb(skb, rxrpc_skb_new_error_report); + skb_queue_tail(&local->rx_queue, skb); + } + + rxrpc_wake_up_io_thread(local); + rcu_read_unlock(); +} + /* * post connection-level events to the connection * - this includes challenges, responses, some aborts and call terminal packet @@ -405,6 +430,10 @@ int rxrpc_io_thread(void *data) rxrpc_input_packet(local, skb); rcu_read_unlock(); break; + case RXRPC_SKB_MARK_ERROR: + rxrpc_input_error(local, skb); + rxrpc_free_skb(skb, rxrpc_skb_put_error_report); + break; default: WARN_ON_ONCE(1); rxrpc_free_skb(skb, rxrpc_skb_put_unknown); diff --git a/net/rxrpc/peer_event.c b/net/rxrpc/peer_event.c index f35cfc458dcf..94f63fb1bd67 100644 --- a/net/rxrpc/peer_event.c +++ b/net/rxrpc/peer_event.c @@ -131,51 +131,26 @@ static void rxrpc_adjust_mtu(struct rxrpc_peer *peer, unsigned int mtu) /* * Handle an error received on the local endpoint. */ -void rxrpc_error_report(struct sock *sk) +void rxrpc_input_error(struct rxrpc_local *local, struct sk_buff *skb) { - struct sock_exterr_skb *serr; + struct sock_exterr_skb *serr = SKB_EXT_ERR(skb); struct sockaddr_rxrpc srx; - struct rxrpc_local *local; struct rxrpc_peer *peer = NULL; - struct sk_buff *skb; - rcu_read_lock(); - local = rcu_dereference_sk_user_data(sk); - if (unlikely(!local)) { - rcu_read_unlock(); - return; - } - _enter("%p{%d}", sk, local->debug_id); + _enter("L=%x", local->debug_id); - /* Clear the outstanding error value on the socket so that it doesn't - * cause kernel_sendmsg() to return it later. - */ - sock_error(sk); - - skb = sock_dequeue_err_skb(sk); - if (!skb) { - rcu_read_unlock(); - _leave("UDP socket errqueue empty"); - return; - } - rxrpc_new_skb(skb, rxrpc_skb_new_error_report); - serr = SKB_EXT_ERR(skb); if (!skb->len && serr->ee.ee_origin == SO_EE_ORIGIN_TIMESTAMPING) { _leave("UDP empty message"); - rcu_read_unlock(); - rxrpc_free_skb(skb, rxrpc_skb_put_error_report); return; } + rcu_read_lock(); peer = rxrpc_lookup_peer_local_rcu(local, skb, &srx); if (peer && !rxrpc_get_peer_maybe(peer, rxrpc_peer_get_input_error)) peer = NULL; - if (!peer) { - rcu_read_unlock(); - rxrpc_free_skb(skb, rxrpc_skb_put_error_report); - _leave(" [no peer]"); + rcu_read_unlock(); + if (!peer) return; - } trace_rxrpc_rx_icmp(peer, &serr->ee, &srx); @@ -188,11 +163,7 @@ void rxrpc_error_report(struct sock *sk) rxrpc_store_error(peer, serr); out: - rcu_read_unlock(); - rxrpc_free_skb(skb, rxrpc_skb_put_error_report); rxrpc_put_peer(peer, rxrpc_peer_put_input_error); - - _leave(""); } /* From 4041a8ff653ec4e4d9e6395802cb3f4fca59f7f3 Mon Sep 17 00:00:00 2001 From: David Howells Date: Thu, 23 Jan 2020 22:21:59 +0000 Subject: [PATCH 1421/2002] rxrpc: Remove call->input_lock Remove call->input_lock as it was only necessary to serialise access to the state stored in the rxrpc_call struct by simultaneous softirq handlers presenting received packets. They now dump the packets in a queue and a single process-context handler now processes them. Signed-off-by: David Howells cc: Marc Dionne cc: linux-afs@lists.infradead.org --- net/rxrpc/ar-internal.h | 1 - net/rxrpc/call_object.c | 1 - net/rxrpc/input.c | 22 +++++----------------- 3 files changed, 5 insertions(+), 19 deletions(-) diff --git a/net/rxrpc/ar-internal.h b/net/rxrpc/ar-internal.h index 566377c64184..654e9dab107c 100644 --- a/net/rxrpc/ar-internal.h +++ b/net/rxrpc/ar-internal.h @@ -657,7 +657,6 @@ struct rxrpc_call { rxrpc_seq_t rx_consumed; /* Highest packet consumed */ rxrpc_serial_t rx_serial; /* Highest serial received for this call */ u8 rx_winsize; /* Size of Rx window */ - spinlock_t input_lock; /* Lock for packet input to this call */ /* TCP-style slow-start congestion control [RFC5681]. Since the SMSS * is fixed, we keep these numbers in terms of segments (ie. DATA diff --git a/net/rxrpc/call_object.c b/net/rxrpc/call_object.c index 57c8d4cc900a..f6d1b3a6f8c6 100644 --- a/net/rxrpc/call_object.c +++ b/net/rxrpc/call_object.c @@ -143,7 +143,6 @@ struct rxrpc_call *rxrpc_alloc_call(struct rxrpc_sock *rx, gfp_t gfp, init_waitqueue_head(&call->waitq); spin_lock_init(&call->notify_lock); spin_lock_init(&call->tx_lock); - spin_lock_init(&call->input_lock); spin_lock_init(&call->acks_ack_lock); rwlock_init(&call->state_lock); refcount_set(&call->ref, 1); diff --git a/net/rxrpc/input.c b/net/rxrpc/input.c index f4f6f3c62d03..13c52145a926 100644 --- a/net/rxrpc/input.c +++ b/net/rxrpc/input.c @@ -588,8 +588,6 @@ static void rxrpc_input_data(struct rxrpc_call *call, struct sk_buff *skb) } } - spin_lock(&call->input_lock); - /* Received data implicitly ACKs all of the request packets we sent * when we're acting as a client. */ @@ -607,8 +605,6 @@ static void rxrpc_input_data(struct rxrpc_call *call, struct sk_buff *skb) out: trace_rxrpc_notify_socket(call->debug_id, serial); rxrpc_notify_socket(call); - - spin_unlock(&call->input_lock); rxrpc_free_skb(skb, rxrpc_skb_put_input); _leave(" [queued]"); } @@ -811,7 +807,7 @@ static void rxrpc_input_ack(struct rxrpc_call *call, struct sk_buff *skb) offset = sizeof(struct rxrpc_wire_header); if (skb_copy_bits(skb, offset, &ack, sizeof(ack)) < 0) { rxrpc_proto_abort("XAK", call, 0); - goto out_not_locked; + goto out; } offset += sizeof(ack); @@ -863,7 +859,7 @@ static void rxrpc_input_ack(struct rxrpc_call *call, struct sk_buff *skb) rxrpc_is_client_call(call)) { rxrpc_set_call_completion(call, RXRPC_CALL_REMOTELY_ABORTED, 0, -ENETRESET); - return; + goto out; } /* If we get an OUT_OF_SEQUENCE ACK from the server, that can also @@ -877,7 +873,7 @@ static void rxrpc_input_ack(struct rxrpc_call *call, struct sk_buff *skb) rxrpc_is_client_call(call)) { rxrpc_set_call_completion(call, RXRPC_CALL_REMOTELY_ABORTED, 0, -ENETRESET); - return; + goto out; } /* Discard any out-of-order or duplicate ACKs (outside lock). */ @@ -885,7 +881,7 @@ static void rxrpc_input_ack(struct rxrpc_call *call, struct sk_buff *skb) trace_rxrpc_rx_discard_ack(call->debug_id, ack_serial, first_soft_ack, call->acks_first_seq, prev_pkt, call->acks_prev_seq); - goto out_not_locked; + goto out; } info.rxMTU = 0; @@ -893,14 +889,12 @@ static void rxrpc_input_ack(struct rxrpc_call *call, struct sk_buff *skb) if (skb->len >= ioffset + sizeof(info) && skb_copy_bits(skb, ioffset, &info, sizeof(info)) < 0) { rxrpc_proto_abort("XAI", call, 0); - goto out_not_locked; + goto out; } if (nr_acks > 0) skb_condense(skb); - spin_lock(&call->input_lock); - /* Discard any out-of-order or duplicate ACKs (inside lock). */ if (!rxrpc_is_ack_valid(call, first_soft_ack, prev_pkt)) { trace_rxrpc_rx_discard_ack(call->debug_id, ack_serial, @@ -992,8 +986,6 @@ static void rxrpc_input_ack(struct rxrpc_call *call, struct sk_buff *skb) rxrpc_congestion_management(call, skb, &summary, acked_serial); out: - spin_unlock(&call->input_lock); -out_not_locked: rxrpc_free_skb(skb_put, rxrpc_skb_put_input); rxrpc_free_skb(skb_old, rxrpc_skb_put_ack); } @@ -1005,12 +997,8 @@ static void rxrpc_input_ackall(struct rxrpc_call *call, struct sk_buff *skb) { struct rxrpc_ack_summary summary = { 0 }; - spin_lock(&call->input_lock); - if (rxrpc_rotate_tx_window(call, call->tx_top, &summary)) rxrpc_end_tx_phase(call, false, "ETL"); - - spin_unlock(&call->input_lock); } /* From 81f2e8adc0fd10847637873dafe8610f3fb4cdff Mon Sep 17 00:00:00 2001 From: David Howells Date: Mon, 10 Oct 2022 13:13:47 +0100 Subject: [PATCH 1422/2002] rxrpc: Don't use sk->sk_receive_queue.lock to guard socket state changes Don't use sk->sk_receive_queue.lock to guard socket state changes as the socket mutex is sufficient. Signed-off-by: David Howells cc: Marc Dionne cc: linux-afs@lists.infradead.org --- net/rxrpc/af_rxrpc.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/net/rxrpc/af_rxrpc.c b/net/rxrpc/af_rxrpc.c index 7a0dc01741e7..8ad4d85acb0b 100644 --- a/net/rxrpc/af_rxrpc.c +++ b/net/rxrpc/af_rxrpc.c @@ -812,14 +812,12 @@ static int rxrpc_shutdown(struct socket *sock, int flags) lock_sock(sk); - spin_lock_bh(&sk->sk_receive_queue.lock); if (sk->sk_state < RXRPC_CLOSE) { sk->sk_state = RXRPC_CLOSE; sk->sk_shutdown = SHUTDOWN_MASK; } else { ret = -ESHUTDOWN; } - spin_unlock_bh(&sk->sk_receive_queue.lock); rxrpc_discard_prealloc(rx); @@ -872,9 +870,7 @@ static int rxrpc_release_sock(struct sock *sk) break; } - spin_lock_bh(&sk->sk_receive_queue.lock); sk->sk_state = RXRPC_CLOSE; - spin_unlock_bh(&sk->sk_receive_queue.lock); if (rx->local && rcu_access_pointer(rx->local->service) == rx) { write_lock(&rx->local->services_lock); From 15f661dc95daec9b38e8e4cc931c95afe0ae0cef Mon Sep 17 00:00:00 2001 From: David Howells Date: Mon, 10 Oct 2022 15:51:39 +0100 Subject: [PATCH 1423/2002] rxrpc: Implement a mechanism to send an event notification to a call Provide a means by which an event notification can be sent to a call such that the I/O thread can process it rather than it being done in a separate workqueue. This will allow a lot of locking to be removed. Signed-off-by: David Howells cc: Marc Dionne cc: linux-afs@lists.infradead.org --- include/trace/events/rxrpc.h | 52 ++++++++++++++++++++++++++++++++++++ net/rxrpc/ar-internal.h | 5 +++- net/rxrpc/call_object.c | 24 +++++++++++++++++ net/rxrpc/input.c | 3 +-- net/rxrpc/io_thread.c | 20 ++++++++++++-- net/rxrpc/local_object.c | 1 + 6 files changed, 100 insertions(+), 5 deletions(-) diff --git a/include/trace/events/rxrpc.h b/include/trace/events/rxrpc.h index 44a9be9836f9..0b12d96c7921 100644 --- a/include/trace/events/rxrpc.h +++ b/include/trace/events/rxrpc.h @@ -16,6 +16,13 @@ /* * Declare tracing information enums and their string mappings for display. */ +#define rxrpc_call_poke_traces \ + EM(rxrpc_call_poke_error, "Error") \ + EM(rxrpc_call_poke_idle, "Idle") \ + EM(rxrpc_call_poke_start, "Start") \ + EM(rxrpc_call_poke_timer, "Timer") \ + E_(rxrpc_call_poke_timer_now, "Timer-now") + #define rxrpc_skb_traces \ EM(rxrpc_skb_eaten_by_unshare, "ETN unshare ") \ EM(rxrpc_skb_eaten_by_unshare_nomem, "ETN unshar-nm") \ @@ -150,6 +157,7 @@ EM(rxrpc_call_get_input, "GET input ") \ EM(rxrpc_call_get_kernel_service, "GET krnl-srv") \ EM(rxrpc_call_get_notify_socket, "GET notify ") \ + EM(rxrpc_call_get_poke, "GET poke ") \ EM(rxrpc_call_get_recvmsg, "GET recvmsg ") \ EM(rxrpc_call_get_release_sock, "GET rel-sock") \ EM(rxrpc_call_get_sendmsg, "GET sendmsg ") \ @@ -160,6 +168,7 @@ EM(rxrpc_call_put_discard_prealloc, "PUT disc-pre") \ EM(rxrpc_call_put_input, "PUT input ") \ EM(rxrpc_call_put_kernel, "PUT kernel ") \ + EM(rxrpc_call_put_poke, "PUT poke ") \ EM(rxrpc_call_put_recvmsg, "PUT recvmsg ") \ EM(rxrpc_call_put_release_sock, "PUT rls-sock") \ EM(rxrpc_call_put_release_sock_tba, "PUT rls-sk-a") \ @@ -378,6 +387,7 @@ #define E_(a, b) a enum rxrpc_bundle_trace { rxrpc_bundle_traces } __mode(byte); +enum rxrpc_call_poke_trace { rxrpc_call_poke_traces } __mode(byte); enum rxrpc_call_trace { rxrpc_call_traces } __mode(byte); enum rxrpc_client_trace { rxrpc_client_traces } __mode(byte); enum rxrpc_congest_change { rxrpc_congest_changes } __mode(byte); @@ -408,6 +418,7 @@ enum rxrpc_txqueue_trace { rxrpc_txqueue_traces } __mode(byte); #define E_(a, b) TRACE_DEFINE_ENUM(a); rxrpc_bundle_traces; +rxrpc_call_poke_traces; rxrpc_call_traces; rxrpc_client_traces; rxrpc_congest_changes; @@ -1747,6 +1758,47 @@ TRACE_EVENT(rxrpc_txbuf, __entry->ref) ); +TRACE_EVENT(rxrpc_poke_call, + TP_PROTO(struct rxrpc_call *call, bool busy, + enum rxrpc_call_poke_trace what), + + TP_ARGS(call, busy, what), + + TP_STRUCT__entry( + __field(unsigned int, call_debug_id ) + __field(bool, busy ) + __field(enum rxrpc_call_poke_trace, what ) + ), + + TP_fast_assign( + __entry->call_debug_id = call->debug_id; + __entry->busy = busy; + __entry->what = what; + ), + + TP_printk("c=%08x %s%s", + __entry->call_debug_id, + __print_symbolic(__entry->what, rxrpc_call_poke_traces), + __entry->busy ? "!" : "") + ); + +TRACE_EVENT(rxrpc_call_poked, + TP_PROTO(struct rxrpc_call *call), + + TP_ARGS(call), + + TP_STRUCT__entry( + __field(unsigned int, call_debug_id ) + ), + + TP_fast_assign( + __entry->call_debug_id = call->debug_id; + ), + + TP_printk("c=%08x", + __entry->call_debug_id) + ); + #undef EM #undef E_ #endif /* _TRACE_RXRPC_H */ diff --git a/net/rxrpc/ar-internal.h b/net/rxrpc/ar-internal.h index 654e9dab107c..a80655fa9dfb 100644 --- a/net/rxrpc/ar-internal.h +++ b/net/rxrpc/ar-internal.h @@ -292,6 +292,7 @@ struct rxrpc_local { struct sk_buff_head reject_queue; /* packets awaiting rejection */ struct sk_buff_head event_queue; /* endpoint event packets awaiting processing */ struct sk_buff_head rx_queue; /* Received packets */ + struct list_head call_attend_q; /* Calls requiring immediate attention */ struct rb_root client_bundles; /* Client connection bundles by socket params */ spinlock_t client_bundles_lock; /* Lock for client_bundles */ spinlock_t lock; /* access lock */ @@ -616,6 +617,7 @@ struct rxrpc_call { struct list_head recvmsg_link; /* Link in rx->recvmsg_q */ struct list_head sock_link; /* Link in rx->sock_calls */ struct rb_node sock_node; /* Node in rx->calls */ + struct list_head attend_link; /* Link in local->call_attend_q */ struct rxrpc_txbuf *tx_pending; /* Tx buffer being filled */ wait_queue_head_t waitq; /* Wait queue for channel or Tx */ s64 tx_total_len; /* Total length left to be transmitted (or -1) */ @@ -843,6 +845,7 @@ extern const char *const rxrpc_call_states[]; extern const char *const rxrpc_call_completions[]; extern struct kmem_cache *rxrpc_call_jar; +void rxrpc_poke_call(struct rxrpc_call *call, enum rxrpc_call_poke_trace what); struct rxrpc_call *rxrpc_find_call_by_user_ID(struct rxrpc_sock *, unsigned long); struct rxrpc_call *rxrpc_alloc_call(struct rxrpc_sock *, gfp_t, unsigned int); struct rxrpc_call *rxrpc_new_client_call(struct rxrpc_sock *, @@ -951,7 +954,7 @@ void rxrpc_unpublish_service_conn(struct rxrpc_connection *); /* * input.c */ -void rxrpc_input_call_packet(struct rxrpc_call *, struct sk_buff *); +void rxrpc_input_call_event(struct rxrpc_call *, struct sk_buff *); void rxrpc_input_implicit_end_call(struct rxrpc_sock *, struct rxrpc_connection *, struct rxrpc_call *); diff --git a/net/rxrpc/call_object.c b/net/rxrpc/call_object.c index f6d1b3a6f8c6..997641e3d1c8 100644 --- a/net/rxrpc/call_object.c +++ b/net/rxrpc/call_object.c @@ -45,6 +45,29 @@ static struct semaphore rxrpc_call_limiter = static struct semaphore rxrpc_kernel_call_limiter = __SEMAPHORE_INITIALIZER(rxrpc_kernel_call_limiter, 1000); +void rxrpc_poke_call(struct rxrpc_call *call, enum rxrpc_call_poke_trace what) +{ + struct rxrpc_local *local; + struct rxrpc_peer *peer = call->peer; + bool busy; + + if (WARN_ON_ONCE(!peer)) + return; + local = peer->local; + + if (call->state < RXRPC_CALL_COMPLETE) { + spin_lock_bh(&local->lock); + busy = !list_empty(&call->attend_link); + trace_rxrpc_poke_call(call, busy, what); + if (!busy) { + rxrpc_get_call(call, rxrpc_call_get_poke); + list_add_tail(&call->attend_link, &local->call_attend_q); + } + spin_unlock_bh(&local->lock); + rxrpc_wake_up_io_thread(local); + } +} + static void rxrpc_call_timer_expired(struct timer_list *t) { struct rxrpc_call *call = from_timer(call, t, timer); @@ -137,6 +160,7 @@ struct rxrpc_call *rxrpc_alloc_call(struct rxrpc_sock *rx, gfp_t gfp, INIT_LIST_HEAD(&call->accept_link); INIT_LIST_HEAD(&call->recvmsg_link); INIT_LIST_HEAD(&call->sock_link); + INIT_LIST_HEAD(&call->attend_link); INIT_LIST_HEAD(&call->tx_buffer); skb_queue_head_init(&call->recvmsg_queue); skb_queue_head_init(&call->rx_oos_queue); diff --git a/net/rxrpc/input.c b/net/rxrpc/input.c index 13c52145a926..036f02371051 100644 --- a/net/rxrpc/input.c +++ b/net/rxrpc/input.c @@ -1017,8 +1017,7 @@ static void rxrpc_input_abort(struct rxrpc_call *call, struct sk_buff *skb) /* * Process an incoming call packet. */ -void rxrpc_input_call_packet(struct rxrpc_call *call, - struct sk_buff *skb) +void rxrpc_input_call_event(struct rxrpc_call *call, struct sk_buff *skb) { struct rxrpc_skb_priv *sp = rxrpc_skb(skb); unsigned long timo; diff --git a/net/rxrpc/io_thread.c b/net/rxrpc/io_thread.c index 416c6101cf78..cc249bc6b8cd 100644 --- a/net/rxrpc/io_thread.c +++ b/net/rxrpc/io_thread.c @@ -366,7 +366,7 @@ static int rxrpc_input_packet(struct rxrpc_local *local, struct sk_buff *skb) /* Process a call packet; this either discards or passes on the ref * elsewhere. */ - rxrpc_input_call_packet(call, skb); + rxrpc_input_call_event(call, skb); goto out; discard: @@ -413,6 +413,7 @@ int rxrpc_io_thread(void *data) { struct sk_buff_head rx_queue; struct rxrpc_local *local = data; + struct rxrpc_call *call; struct sk_buff *skb; skb_queue_head_init(&rx_queue); @@ -422,6 +423,20 @@ int rxrpc_io_thread(void *data) for (;;) { rxrpc_inc_stat(local->rxnet, stat_io_loop); + /* Deal with calls that want immediate attention. */ + if ((call = list_first_entry_or_null(&local->call_attend_q, + struct rxrpc_call, + attend_link))) { + spin_lock_bh(&local->lock); + list_del_init(&call->attend_link); + spin_unlock_bh(&local->lock); + + trace_rxrpc_call_poked(call); + rxrpc_input_call_event(call, NULL); + rxrpc_put_call(call, rxrpc_call_put_poke); + continue; + } + /* Process received packets and errors. */ if ((skb = __skb_dequeue(&rx_queue))) { switch (skb->mark) { @@ -450,7 +465,8 @@ int rxrpc_io_thread(void *data) } set_current_state(TASK_INTERRUPTIBLE); - if (!skb_queue_empty(&local->rx_queue)) { + if (!skb_queue_empty(&local->rx_queue) || + !list_empty(&local->call_attend_q)) { __set_current_state(TASK_RUNNING); continue; } diff --git a/net/rxrpc/local_object.c b/net/rxrpc/local_object.c index 6b4d77219f36..03f491cc23ef 100644 --- a/net/rxrpc/local_object.c +++ b/net/rxrpc/local_object.c @@ -104,6 +104,7 @@ static struct rxrpc_local *rxrpc_alloc_local(struct rxrpc_net *rxnet, skb_queue_head_init(&local->reject_queue); skb_queue_head_init(&local->event_queue); skb_queue_head_init(&local->rx_queue); + INIT_LIST_HEAD(&local->call_attend_q); local->client_bundles = RB_ROOT; spin_lock_init(&local->client_bundles_lock); spin_lock_init(&local->lock); From f3441d4125fc98995858550a5521b8d7daf0504a Mon Sep 17 00:00:00 2001 From: David Howells Date: Thu, 20 Oct 2022 21:58:36 +0100 Subject: [PATCH 1424/2002] rxrpc: Copy client call parameters into rxrpc_call earlier Copy client call parameters into rxrpc_call earlier so that that can be used to convey them to the connection code - which can then be offloaded to the I/O thread. Signed-off-by: David Howells cc: Marc Dionne cc: linux-afs@lists.infradead.org --- include/trace/events/rxrpc.h | 3 +++ net/rxrpc/ar-internal.h | 7 ++++- net/rxrpc/call_accept.c | 2 ++ net/rxrpc/call_object.c | 50 ++++++++++++++++++++++-------------- net/rxrpc/conn_client.c | 2 +- net/rxrpc/io_thread.c | 4 +-- net/rxrpc/output.c | 2 +- net/rxrpc/proc.c | 25 +++++------------- net/rxrpc/recvmsg.c | 8 +++--- net/rxrpc/security.c | 30 ++++++++++++++++++++++ net/rxrpc/txbuf.c | 2 +- 11 files changed, 87 insertions(+), 48 deletions(-) diff --git a/include/trace/events/rxrpc.h b/include/trace/events/rxrpc.h index 0b12d96c7921..8bd48358f757 100644 --- a/include/trace/events/rxrpc.h +++ b/include/trace/events/rxrpc.h @@ -52,6 +52,7 @@ #define rxrpc_local_traces \ EM(rxrpc_local_free, "FREE ") \ + EM(rxrpc_local_get_call, "GET call ") \ EM(rxrpc_local_get_client_conn, "GET conn-cln") \ EM(rxrpc_local_get_for_use, "GET for-use ") \ EM(rxrpc_local_get_peer, "GET peer ") \ @@ -61,6 +62,7 @@ EM(rxrpc_local_processing, "PROCESSING ") \ EM(rxrpc_local_put_already_queued, "PUT alreadyq") \ EM(rxrpc_local_put_bind, "PUT bind ") \ + EM(rxrpc_local_put_call, "PUT call ") \ EM(rxrpc_local_put_for_use, "PUT for-use ") \ EM(rxrpc_local_put_kill_conn, "PUT conn-kil") \ EM(rxrpc_local_put_peer, "PUT peer ") \ @@ -166,6 +168,7 @@ EM(rxrpc_call_new_client, "NEW client ") \ EM(rxrpc_call_new_prealloc_service, "NEW prealloc") \ EM(rxrpc_call_put_discard_prealloc, "PUT disc-pre") \ + EM(rxrpc_call_put_discard_error, "PUT disc-err") \ EM(rxrpc_call_put_input, "PUT input ") \ EM(rxrpc_call_put_kernel, "PUT kernel ") \ EM(rxrpc_call_put_poke, "PUT poke ") \ diff --git a/net/rxrpc/ar-internal.h b/net/rxrpc/ar-internal.h index a80655fa9dfb..3bd6a5eb2fb7 100644 --- a/net/rxrpc/ar-internal.h +++ b/net/rxrpc/ar-internal.h @@ -530,6 +530,7 @@ enum rxrpc_call_flag { RXRPC_CALL_UPGRADE, /* Service upgrade was requested for the call */ RXRPC_CALL_DELAY_ACK_PENDING, /* DELAY ACK generation is pending */ RXRPC_CALL_IDLE_ACK_PENDING, /* IDLE ACK generation is pending */ + RXRPC_CALL_EXCLUSIVE, /* The call uses a once-only connection */ }; /* @@ -592,10 +593,13 @@ struct rxrpc_call { struct rcu_head rcu; struct rxrpc_connection *conn; /* connection carrying call */ struct rxrpc_peer *peer; /* Peer record for remote address */ + struct rxrpc_local *local; /* Representation of local endpoint */ struct rxrpc_sock __rcu *socket; /* socket responsible */ struct rxrpc_net *rxnet; /* Network namespace to which call belongs */ + struct key *key; /* Security details */ const struct rxrpc_security *security; /* applied security module */ struct mutex user_mutex; /* User access mutex */ + struct sockaddr_rxrpc dest_srx; /* Destination address */ unsigned long delay_ack_at; /* When DELAY ACK needs to happen */ unsigned long ack_lost_at; /* When ACK is figured as lost */ unsigned long resend_at; /* When next resend needs to happen */ @@ -631,11 +635,11 @@ struct rxrpc_call { enum rxrpc_call_state state; /* current state of call */ enum rxrpc_call_completion completion; /* Call completion condition */ refcount_t ref; - u16 service_id; /* service ID */ u8 security_ix; /* Security type */ enum rxrpc_interruptibility interruptibility; /* At what point call may be interrupted */ u32 call_id; /* call ID on connection */ u32 cid; /* connection ID plus channel index */ + u32 security_level; /* Security level selected */ int debug_id; /* debug ID for printks */ unsigned short rx_pkt_offset; /* Current recvmsg packet offset */ unsigned short rx_pkt_len; /* Current recvmsg packet len */ @@ -1147,6 +1151,7 @@ extern const struct rxrpc_security rxkad; int __init rxrpc_init_security(void); const struct rxrpc_security *rxrpc_security_lookup(u8); void rxrpc_exit_security(void); +int rxrpc_init_client_call_security(struct rxrpc_call *); int rxrpc_init_client_conn_security(struct rxrpc_connection *); const struct rxrpc_security *rxrpc_get_incoming_security(struct rxrpc_sock *, struct sk_buff *); diff --git a/net/rxrpc/call_accept.c b/net/rxrpc/call_accept.c index 8d106b626aa3..8bc327aa2beb 100644 --- a/net/rxrpc/call_accept.c +++ b/net/rxrpc/call_accept.c @@ -318,10 +318,12 @@ static struct rxrpc_call *rxrpc_alloc_incoming_call(struct rxrpc_sock *rx, (call_tail + 1) & (RXRPC_BACKLOG_MAX - 1)); rxrpc_see_call(call, rxrpc_call_see_accept); + call->local = rxrpc_get_local(conn->local, rxrpc_local_get_call); call->conn = conn; call->security = conn->security; call->security_ix = conn->security_ix; call->peer = rxrpc_get_peer(conn->peer, rxrpc_peer_get_accept); + call->dest_srx = peer->srx; call->cong_ssthresh = call->peer->cong_ssthresh; call->tx_last_sent = ktime_get_real(); return call; diff --git a/net/rxrpc/call_object.c b/net/rxrpc/call_object.c index 997641e3d1c8..2622d06bb0d6 100644 --- a/net/rxrpc/call_object.c +++ b/net/rxrpc/call_object.c @@ -47,14 +47,9 @@ static struct semaphore rxrpc_kernel_call_limiter = void rxrpc_poke_call(struct rxrpc_call *call, enum rxrpc_call_poke_trace what) { - struct rxrpc_local *local; - struct rxrpc_peer *peer = call->peer; + struct rxrpc_local *local = call->local; bool busy; - if (WARN_ON_ONCE(!peer)) - return; - local = peer->local; - if (call->state < RXRPC_CALL_COMPLETE) { spin_lock_bh(&local->lock); busy = !list_empty(&call->attend_link); @@ -200,22 +195,45 @@ struct rxrpc_call *rxrpc_alloc_call(struct rxrpc_sock *rx, gfp_t gfp, */ static struct rxrpc_call *rxrpc_alloc_client_call(struct rxrpc_sock *rx, struct sockaddr_rxrpc *srx, + struct rxrpc_conn_parameters *cp, + struct rxrpc_call_params *p, gfp_t gfp, unsigned int debug_id) { struct rxrpc_call *call; ktime_t now; + int ret; _enter(""); call = rxrpc_alloc_call(rx, gfp, debug_id); if (!call) return ERR_PTR(-ENOMEM); - call->state = RXRPC_CALL_CLIENT_AWAIT_CONN; - call->service_id = srx->srx_service; now = ktime_get_real(); - call->acks_latest_ts = now; - call->cong_tstamp = now; + call->acks_latest_ts = now; + call->cong_tstamp = now; + call->state = RXRPC_CALL_CLIENT_AWAIT_CONN; + call->dest_srx = *srx; + call->interruptibility = p->interruptibility; + call->tx_total_len = p->tx_total_len; + call->key = key_get(cp->key); + call->local = rxrpc_get_local(cp->local, rxrpc_local_get_call); + if (p->kernel) + __set_bit(RXRPC_CALL_KERNEL, &call->flags); + if (cp->upgrade) + __set_bit(RXRPC_CALL_UPGRADE, &call->flags); + if (cp->exclusive) + __set_bit(RXRPC_CALL_EXCLUSIVE, &call->flags); + + ret = rxrpc_init_client_call_security(call); + if (ret < 0) { + __rxrpc_set_call_completion(call, RXRPC_CALL_LOCAL_ERROR, 0, ret); + rxrpc_put_call(call, rxrpc_call_put_discard_error); + return ERR_PTR(ret); + } + + trace_rxrpc_call(call->debug_id, refcount_read(&call->ref), + p->user_call_ID, rxrpc_call_new_client); _leave(" = %p", call); return call; @@ -295,7 +313,7 @@ struct rxrpc_call *rxrpc_new_client_call(struct rxrpc_sock *rx, return ERR_PTR(-ERESTARTSYS); } - call = rxrpc_alloc_client_call(rx, srx, gfp, debug_id); + call = rxrpc_alloc_client_call(rx, srx, cp, p, gfp, debug_id); if (IS_ERR(call)) { release_sock(&rx->sk); up(limiter); @@ -303,13 +321,6 @@ struct rxrpc_call *rxrpc_new_client_call(struct rxrpc_sock *rx, return call; } - call->interruptibility = p->interruptibility; - call->tx_total_len = p->tx_total_len; - trace_rxrpc_call(call->debug_id, refcount_read(&call->ref), - p->user_call_ID, rxrpc_call_new_client); - if (p->kernel) - __set_bit(RXRPC_CALL_KERNEL, &call->flags); - /* We need to protect a partially set up call against the user as we * will be acting outside the socket lock. */ @@ -413,7 +424,7 @@ void rxrpc_incoming_call(struct rxrpc_sock *rx, rcu_assign_pointer(call->socket, rx); call->call_id = sp->hdr.callNumber; - call->service_id = sp->hdr.serviceId; + call->dest_srx.srx_service = sp->hdr.serviceId; call->cid = sp->hdr.cid; call->state = RXRPC_CALL_SERVER_SECURING; call->cong_tstamp = skb->tstamp; @@ -639,6 +650,7 @@ static void rxrpc_destroy_call(struct work_struct *work) rxrpc_free_skb(call->acks_soft_tbl, rxrpc_skb_put_ack); rxrpc_put_connection(call->conn, rxrpc_conn_put_call); rxrpc_put_peer(call->peer, rxrpc_peer_put_call); + rxrpc_put_local(call->local, rxrpc_local_put_call); call_rcu(&call->rcu, rxrpc_rcu_free_call); } diff --git a/net/rxrpc/conn_client.c b/net/rxrpc/conn_client.c index 9485a3d18f29..ab3dd22fadc0 100644 --- a/net/rxrpc/conn_client.c +++ b/net/rxrpc/conn_client.c @@ -553,7 +553,7 @@ static void rxrpc_activate_one_channel(struct rxrpc_connection *conn, call->call_id = call_id; call->security = conn->security; call->security_ix = conn->security_ix; - call->service_id = conn->service_id; + call->dest_srx.srx_service = conn->service_id; trace_rxrpc_connect_call(call); diff --git a/net/rxrpc/io_thread.c b/net/rxrpc/io_thread.c index cc249bc6b8cd..2119941b6d6c 100644 --- a/net/rxrpc/io_thread.c +++ b/net/rxrpc/io_thread.c @@ -343,8 +343,8 @@ static int rxrpc_input_packet(struct rxrpc_local *local, struct sk_buff *skb) } if (call) { - if (sp->hdr.serviceId != call->service_id) - call->service_id = sp->hdr.serviceId; + if (sp->hdr.serviceId != call->dest_srx.srx_service) + call->dest_srx.srx_service = sp->hdr.serviceId; if ((int)sp->hdr.serial - (int)call->rx_serial > 0) call->rx_serial = sp->hdr.serial; if (!test_bit(RXRPC_CALL_RX_HEARD, &call->flags)) diff --git a/net/rxrpc/output.c b/net/rxrpc/output.c index 131c7a76fb06..e2ce7dadbb7a 100644 --- a/net/rxrpc/output.c +++ b/net/rxrpc/output.c @@ -357,7 +357,7 @@ int rxrpc_send_abort_packet(struct rxrpc_call *call) pkt.whdr.userStatus = 0; pkt.whdr.securityIndex = call->security_ix; pkt.whdr._rsvd = 0; - pkt.whdr.serviceId = htons(call->service_id); + pkt.whdr.serviceId = htons(call->dest_srx.srx_service); pkt.abort_code = htonl(call->abort_code); iov[0].iov_base = &pkt; diff --git a/net/rxrpc/proc.c b/net/rxrpc/proc.c index 35d5b43c677e..5af7c8ee4b1a 100644 --- a/net/rxrpc/proc.c +++ b/net/rxrpc/proc.c @@ -49,8 +49,6 @@ static void rxrpc_call_seq_stop(struct seq_file *seq, void *v) static int rxrpc_call_seq_show(struct seq_file *seq, void *v) { struct rxrpc_local *local; - struct rxrpc_sock *rx; - struct rxrpc_peer *peer; struct rxrpc_call *call; struct rxrpc_net *rxnet = rxrpc_net(seq_file_net(seq)); unsigned long timeout = 0; @@ -69,22 +67,13 @@ static int rxrpc_call_seq_show(struct seq_file *seq, void *v) call = list_entry(v, struct rxrpc_call, link); - rx = rcu_dereference(call->socket); - if (rx) { - local = READ_ONCE(rx->local); - if (local) - sprintf(lbuff, "%pISpc", &local->srx.transport); - else - strcpy(lbuff, "no_local"); - } else { - strcpy(lbuff, "no_socket"); - } - - peer = call->peer; - if (peer) - sprintf(rbuff, "%pISpc", &peer->srx.transport); + local = call->local; + if (local) + sprintf(lbuff, "%pISpc", &local->srx.transport); else - strcpy(rbuff, "no_connection"); + strcpy(lbuff, "no_local"); + + sprintf(rbuff, "%pISpc", &call->dest_srx.transport); if (call->state != RXRPC_CALL_SERVER_PREALLOC) { timeout = READ_ONCE(call->expect_rx_by); @@ -98,7 +87,7 @@ static int rxrpc_call_seq_show(struct seq_file *seq, void *v) " %-8.8s %08x %08x %08x %02x %08x %02x %08x %06lx\n", lbuff, rbuff, - call->service_id, + call->dest_srx.srx_service, call->cid, call->call_id, rxrpc_is_service_call(call) ? "Svc" : "Clt", diff --git a/net/rxrpc/recvmsg.c b/net/rxrpc/recvmsg.c index bfac9e09347e..5df7f468abed 100644 --- a/net/rxrpc/recvmsg.c +++ b/net/rxrpc/recvmsg.c @@ -490,11 +490,9 @@ try_again: } if (msg->msg_name && call->peer) { - struct sockaddr_rxrpc *srx = msg->msg_name; - size_t len = sizeof(call->peer->srx); + size_t len = sizeof(call->dest_srx); - memcpy(msg->msg_name, &call->peer->srx, len); - srx->srx_service = call->service_id; + memcpy(msg->msg_name, &call->dest_srx, len); msg->msg_namelen = len; } @@ -639,7 +637,7 @@ read_phase_complete: out: rxrpc_transmit_ack_packets(call->peer->local); if (_service) - *_service = call->service_id; + *_service = call->dest_srx.srx_service; mutex_unlock(&call->user_mutex); _leave(" = %d [%zu,%d]", ret, iov_iter_count(iter), *_abort); return ret; diff --git a/net/rxrpc/security.c b/net/rxrpc/security.c index e6ddac9b3732..209f2c25a0da 100644 --- a/net/rxrpc/security.c +++ b/net/rxrpc/security.c @@ -62,6 +62,36 @@ const struct rxrpc_security *rxrpc_security_lookup(u8 security_index) return rxrpc_security_types[security_index]; } +/* + * Initialise the security on a client call. + */ +int rxrpc_init_client_call_security(struct rxrpc_call *call) +{ + const struct rxrpc_security *sec; + struct rxrpc_key_token *token; + struct key *key = call->key; + int ret; + + if (!key) + return 0; + + ret = key_validate(key); + if (ret < 0) + return ret; + + for (token = key->payload.data[0]; token; token = token->next) { + sec = rxrpc_security_lookup(token->security_index); + if (sec) + goto found; + } + return -EKEYREJECTED; + +found: + call->security = sec; + _leave(" = 0"); + return 0; +} + /* * initialise the security on a client connection */ diff --git a/net/rxrpc/txbuf.c b/net/rxrpc/txbuf.c index f93dc666a3a0..90ff00c340cd 100644 --- a/net/rxrpc/txbuf.c +++ b/net/rxrpc/txbuf.c @@ -44,7 +44,7 @@ struct rxrpc_txbuf *rxrpc_alloc_txbuf(struct rxrpc_call *call, u8 packet_type, txb->wire.userStatus = 0; txb->wire.securityIndex = call->security_ix; txb->wire._rsvd = 0; - txb->wire.serviceId = htons(call->service_id); + txb->wire.serviceId = htons(call->dest_srx.srx_service); trace_rxrpc_txbuf(txb->debug_id, txb->call_debug_id, txb->seq, 1, From cf37b5987508878647161ec3cdba0bb00a1b607a Mon Sep 17 00:00:00 2001 From: David Howells Date: Thu, 31 Mar 2022 23:55:08 +0100 Subject: [PATCH 1425/2002] rxrpc: Move DATA transmission into call processor work item Move DATA transmission into the call processor work item. In a future patch, this will be called from the I/O thread rather than being itsown work item. This will allow DATA transmission to be driven directly by incoming ACKs, pokes and timers as those are processed. The Tx queue is also split: The queue of packets prepared by sendmsg is now places in call->tx_sendmsg and the packet dispatcher decants the packets into call->tx_buffer as space becomes available in the transmission window. This allows sendmsg to run ahead of the available space to try and prevent an underflow in transmission. Signed-off-by: David Howells cc: Marc Dionne cc: linux-afs@lists.infradead.org --- include/trace/events/rxrpc.h | 6 ++- net/rxrpc/ar-internal.h | 5 ++- net/rxrpc/call_event.c | 83 +++++++++++++++++++++++++++++++++--- net/rxrpc/call_object.c | 6 +++ net/rxrpc/output.c | 48 +++++++++++++++++++++ net/rxrpc/sendmsg.c | 83 ++++++------------------------------ net/rxrpc/txbuf.c | 10 ++++- 7 files changed, 161 insertions(+), 80 deletions(-) diff --git a/include/trace/events/rxrpc.h b/include/trace/events/rxrpc.h index 8bd48358f757..c3043fbea0e6 100644 --- a/include/trace/events/rxrpc.h +++ b/include/trace/events/rxrpc.h @@ -183,6 +183,7 @@ EM(rxrpc_call_queue_requeue, "QUE requeue ") \ EM(rxrpc_call_queue_resend, "QUE resend ") \ EM(rxrpc_call_queue_timer, "QUE timer ") \ + EM(rxrpc_call_queue_tx_data, "QUE tx-data ") \ EM(rxrpc_call_see_accept, "SEE accept ") \ EM(rxrpc_call_see_activate_client, "SEE act-clnt") \ EM(rxrpc_call_see_connect_failed, "SEE con-fail") \ @@ -738,6 +739,7 @@ TRACE_EVENT(rxrpc_txqueue, __field(rxrpc_seq_t, acks_hard_ack ) __field(rxrpc_seq_t, tx_bottom ) __field(rxrpc_seq_t, tx_top ) + __field(rxrpc_seq_t, tx_prepared ) __field(int, tx_winsize ) ), @@ -747,16 +749,18 @@ TRACE_EVENT(rxrpc_txqueue, __entry->acks_hard_ack = call->acks_hard_ack; __entry->tx_bottom = call->tx_bottom; __entry->tx_top = call->tx_top; + __entry->tx_prepared = call->tx_prepared; __entry->tx_winsize = call->tx_winsize; ), - TP_printk("c=%08x %s f=%08x h=%08x n=%u/%u/%u", + TP_printk("c=%08x %s f=%08x h=%08x n=%u/%u/%u/%u", __entry->call, __print_symbolic(__entry->why, rxrpc_txqueue_traces), __entry->tx_bottom, __entry->acks_hard_ack, __entry->tx_top - __entry->tx_bottom, __entry->tx_top - __entry->acks_hard_ack, + __entry->tx_prepared - __entry->tx_bottom, __entry->tx_winsize) ); diff --git a/net/rxrpc/ar-internal.h b/net/rxrpc/ar-internal.h index 3bd6a5eb2fb7..6af7298af39b 100644 --- a/net/rxrpc/ar-internal.h +++ b/net/rxrpc/ar-internal.h @@ -646,9 +646,11 @@ struct rxrpc_call { /* Transmitted data tracking. */ spinlock_t tx_lock; /* Transmit queue lock */ + struct list_head tx_sendmsg; /* Sendmsg prepared packets */ struct list_head tx_buffer; /* Buffer of transmissible packets */ rxrpc_seq_t tx_bottom; /* First packet in buffer */ rxrpc_seq_t tx_transmitted; /* Highest packet transmitted */ + rxrpc_seq_t tx_prepared; /* Highest Tx slot prepared. */ rxrpc_seq_t tx_top; /* Highest Tx slot allocated. */ u16 tx_backoff; /* Delay to insert due to Tx failure */ u8 tx_winsize; /* Maximum size of Tx window */ @@ -766,7 +768,7 @@ struct rxrpc_send_params { */ struct rxrpc_txbuf { struct rcu_head rcu; - struct list_head call_link; /* Link in call->tx_queue */ + struct list_head call_link; /* Link in call->tx_sendmsg/tx_buffer */ struct list_head tx_link; /* Link in live Enc queue or Tx queue */ struct rxrpc_call *call; /* Call to which belongs */ ktime_t last_sent; /* Time at which last transmitted */ @@ -1067,6 +1069,7 @@ int rxrpc_send_abort_packet(struct rxrpc_call *); int rxrpc_send_data_packet(struct rxrpc_call *, struct rxrpc_txbuf *); void rxrpc_reject_packets(struct rxrpc_local *); void rxrpc_send_keepalive(struct rxrpc_peer *); +void rxrpc_transmit_one(struct rxrpc_call *call, struct rxrpc_txbuf *txb); /* * peer_event.c diff --git a/net/rxrpc/call_event.c b/net/rxrpc/call_event.c index 3925b55e2064..c9f835292f7b 100644 --- a/net/rxrpc/call_event.c +++ b/net/rxrpc/call_event.c @@ -291,6 +291,72 @@ out: _leave(""); } +static bool rxrpc_tx_window_has_space(struct rxrpc_call *call) +{ + unsigned int winsize = min_t(unsigned int, call->tx_winsize, + call->cong_cwnd + call->cong_extra); + rxrpc_seq_t window = call->acks_hard_ack, wtop = window + winsize; + rxrpc_seq_t tx_top = call->tx_top; + int space; + + space = wtop - tx_top; + return space > 0; +} + +/* + * Decant some if the sendmsg prepared queue into the transmission buffer. + */ +static void rxrpc_decant_prepared_tx(struct rxrpc_call *call) +{ + struct rxrpc_txbuf *txb; + + if (rxrpc_is_client_call(call) && + !test_bit(RXRPC_CALL_EXPOSED, &call->flags)) + rxrpc_expose_client_call(call); + + while ((txb = list_first_entry_or_null(&call->tx_sendmsg, + struct rxrpc_txbuf, call_link))) { + spin_lock(&call->tx_lock); + list_del(&txb->call_link); + spin_unlock(&call->tx_lock); + + call->tx_top = txb->seq; + list_add_tail(&txb->call_link, &call->tx_buffer); + + rxrpc_transmit_one(call, txb); + + // TODO: Drain the transmission buffers. Do this somewhere better + if (after(call->acks_hard_ack, call->tx_bottom + 16)) + rxrpc_shrink_call_tx_buffer(call); + + if (!rxrpc_tx_window_has_space(call)) + break; + } +} + +static void rxrpc_transmit_some_data(struct rxrpc_call *call) +{ + switch (call->state) { + case RXRPC_CALL_SERVER_ACK_REQUEST: + if (list_empty(&call->tx_sendmsg)) + return; + fallthrough; + + case RXRPC_CALL_SERVER_SEND_REPLY: + case RXRPC_CALL_SERVER_AWAIT_ACK: + case RXRPC_CALL_CLIENT_SEND_REQUEST: + case RXRPC_CALL_CLIENT_AWAIT_REPLY: + if (!rxrpc_tx_window_has_space(call)) + return; + if (list_empty(&call->tx_sendmsg)) + return; + rxrpc_decant_prepared_tx(call); + break; + default: + return; + } +} + /* * Handle retransmission and deferred ACK/abort generation. */ @@ -309,19 +375,22 @@ void rxrpc_process_call(struct work_struct *work) call->debug_id, rxrpc_call_states[call->state], call->events); recheck_state: + if (call->acks_hard_ack != call->tx_bottom) + rxrpc_shrink_call_tx_buffer(call); + /* Limit the number of times we do this before returning to the manager */ - iterations++; - if (iterations > 5) - goto requeue; + if (!rxrpc_tx_window_has_space(call) || + list_empty(&call->tx_sendmsg)) { + iterations++; + if (iterations > 5) + goto requeue; + } if (test_and_clear_bit(RXRPC_CALL_EV_ABORT, &call->events)) { rxrpc_send_abort_packet(call); goto recheck_state; } - if (READ_ONCE(call->acks_hard_ack) != call->tx_bottom) - rxrpc_shrink_call_tx_buffer(call); - if (call->state == RXRPC_CALL_COMPLETE) { del_timer_sync(&call->timer); goto out; @@ -387,6 +456,8 @@ recheck_state: set_bit(RXRPC_CALL_EV_RESEND, &call->events); } + rxrpc_transmit_some_data(call); + /* Process events */ if (test_and_clear_bit(RXRPC_CALL_EV_EXPIRED, &call->events)) { if (test_bit(RXRPC_CALL_RX_HEARD, &call->flags) && diff --git a/net/rxrpc/call_object.c b/net/rxrpc/call_object.c index 2622d06bb0d6..96a7edd3a842 100644 --- a/net/rxrpc/call_object.c +++ b/net/rxrpc/call_object.c @@ -156,6 +156,7 @@ struct rxrpc_call *rxrpc_alloc_call(struct rxrpc_sock *rx, gfp_t gfp, INIT_LIST_HEAD(&call->recvmsg_link); INIT_LIST_HEAD(&call->sock_link); INIT_LIST_HEAD(&call->attend_link); + INIT_LIST_HEAD(&call->tx_sendmsg); INIT_LIST_HEAD(&call->tx_buffer); skb_queue_head_init(&call->recvmsg_queue); skb_queue_head_init(&call->rx_oos_queue); @@ -641,6 +642,11 @@ static void rxrpc_destroy_call(struct work_struct *work) del_timer_sync(&call->timer); rxrpc_cleanup_ring(call); + while ((txb = list_first_entry_or_null(&call->tx_sendmsg, + struct rxrpc_txbuf, call_link))) { + list_del(&txb->call_link); + rxrpc_put_txbuf(txb, rxrpc_txbuf_put_cleaned); + } while ((txb = list_first_entry_or_null(&call->tx_buffer, struct rxrpc_txbuf, call_link))) { list_del(&txb->call_link); diff --git a/net/rxrpc/output.c b/net/rxrpc/output.c index e2ce7dadbb7a..c8147e50060b 100644 --- a/net/rxrpc/output.c +++ b/net/rxrpc/output.c @@ -465,6 +465,14 @@ dont_set_request_ack: trace_rxrpc_tx_data(call, txb->seq, serial, txb->wire.flags, test_bit(RXRPC_TXBUF_RESENT, &txb->flags), false); + + /* Track what we've attempted to transmit at least once so that the + * retransmission algorithm doesn't try to resend what we haven't sent + * yet. However, this can race as we can receive an ACK before we get + * to this point. But, OTOH, if we won't get an ACK mentioning this + * packet unless the far side received it (though it could have + * discarded it anyway and NAK'd it). + */ cmpxchg(&call->tx_transmitted, txb->seq - 1, txb->seq); /* send the packet with the don't fragment bit set if we currently @@ -712,3 +720,43 @@ void rxrpc_send_keepalive(struct rxrpc_peer *peer) peer->last_tx_at = ktime_get_seconds(); _leave(""); } + +/* + * Schedule an instant Tx resend. + */ +static inline void rxrpc_instant_resend(struct rxrpc_call *call, + struct rxrpc_txbuf *txb) +{ + if (call->state < RXRPC_CALL_COMPLETE) + kdebug("resend"); +} + +/* + * Transmit one packet. + */ +void rxrpc_transmit_one(struct rxrpc_call *call, struct rxrpc_txbuf *txb) +{ + int ret; + + ret = rxrpc_send_data_packet(call, txb); + if (ret < 0) { + switch (ret) { + case -ENETUNREACH: + case -EHOSTUNREACH: + case -ECONNREFUSED: + rxrpc_set_call_completion(call, RXRPC_CALL_LOCAL_ERROR, + 0, ret); + break; + default: + _debug("need instant resend %d", ret); + rxrpc_instant_resend(call, txb); + } + } else { + unsigned long now = jiffies; + unsigned long resend_at = now + call->peer->rto_j; + + WRITE_ONCE(call->resend_at, resend_at); + rxrpc_reduce_call_timer(call, resend_at, now, + rxrpc_timer_set_for_send); + } +} diff --git a/net/rxrpc/sendmsg.c b/net/rxrpc/sendmsg.c index 76b1e2e89c1e..11af37275d5b 100644 --- a/net/rxrpc/sendmsg.c +++ b/net/rxrpc/sendmsg.c @@ -22,30 +22,9 @@ */ static bool rxrpc_check_tx_space(struct rxrpc_call *call, rxrpc_seq_t *_tx_win) { - unsigned int win_size; - rxrpc_seq_t tx_win = smp_load_acquire(&call->acks_hard_ack); - - /* If we haven't transmitted anything for >1RTT, we should reset the - * congestion management state. - */ - if (ktime_before(ktime_add_us(call->tx_last_sent, - call->peer->srtt_us >> 3), - ktime_get_real())) { - if (RXRPC_TX_SMSS > 2190) - win_size = 2; - else if (RXRPC_TX_SMSS > 1095) - win_size = 3; - else - win_size = 4; - win_size += call->cong_extra; - } else { - win_size = min_t(unsigned int, call->tx_winsize, - call->cong_cwnd + call->cong_extra); - } - if (_tx_win) - *_tx_win = tx_win; - return call->tx_top - tx_win < win_size; + *_tx_win = call->tx_bottom; + return call->tx_prepared - call->tx_bottom < 256; } /* @@ -66,11 +45,6 @@ static int rxrpc_wait_for_tx_window_intr(struct rxrpc_sock *rx, if (signal_pending(current)) return sock_intr_errno(*timeo); - if (READ_ONCE(call->acks_hard_ack) != call->tx_bottom) { - rxrpc_shrink_call_tx_buffer(call); - continue; - } - trace_rxrpc_txqueue(call, rxrpc_txqueue_wait); *timeo = schedule_timeout(*timeo); } @@ -107,11 +81,6 @@ static int rxrpc_wait_for_tx_window_waitall(struct rxrpc_sock *rx, tx_win == tx_start && signal_pending(current)) return -EINTR; - if (READ_ONCE(call->acks_hard_ack) != call->tx_bottom) { - rxrpc_shrink_call_tx_buffer(call); - continue; - } - if (tx_win != tx_start) { timeout = rtt; tx_start = tx_win; @@ -137,11 +106,6 @@ static int rxrpc_wait_for_tx_window_nonintr(struct rxrpc_sock *rx, if (call->state >= RXRPC_CALL_COMPLETE) return call->error; - if (READ_ONCE(call->acks_hard_ack) != call->tx_bottom) { - rxrpc_shrink_call_tx_buffer(call); - continue; - } - trace_rxrpc_txqueue(call, rxrpc_txqueue_wait); *timeo = schedule_timeout(*timeo); } @@ -207,29 +171,27 @@ static void rxrpc_queue_packet(struct rxrpc_sock *rx, struct rxrpc_call *call, unsigned long now; rxrpc_seq_t seq = txb->seq; bool last = test_bit(RXRPC_TXBUF_LAST, &txb->flags); - int ret; rxrpc_inc_stat(call->rxnet, stat_tx_data); - ASSERTCMP(seq, ==, call->tx_top + 1); + ASSERTCMP(txb->seq, ==, call->tx_prepared + 1); /* We have to set the timestamp before queueing as the retransmit * algorithm can see the packet as soon as we queue it. */ txb->last_sent = ktime_get_real(); - /* Add the packet to the call's output buffer */ - rxrpc_get_txbuf(txb, rxrpc_txbuf_get_buffer); - spin_lock(&call->tx_lock); - list_add_tail(&txb->call_link, &call->tx_buffer); - call->tx_top = seq; - spin_unlock(&call->tx_lock); - if (last) trace_rxrpc_txqueue(call, rxrpc_txqueue_queue_last); else trace_rxrpc_txqueue(call, rxrpc_txqueue_queue); + /* Add the packet to the call's output buffer */ + spin_lock(&call->tx_lock); + list_add_tail(&txb->call_link, &call->tx_sendmsg); + call->tx_prepared = seq; + spin_unlock(&call->tx_lock); + if (last || call->state == RXRPC_CALL_SERVER_ACK_REQUEST) { _debug("________awaiting reply/ACK__________"); write_lock_bh(&call->state_lock); @@ -258,30 +220,11 @@ static void rxrpc_queue_packet(struct rxrpc_sock *rx, struct rxrpc_call *call, write_unlock_bh(&call->state_lock); } - if (seq == 1 && rxrpc_is_client_call(call)) - rxrpc_expose_client_call(call); - ret = rxrpc_send_data_packet(call, txb); - if (ret < 0) { - switch (ret) { - case -ENETUNREACH: - case -EHOSTUNREACH: - case -ECONNREFUSED: - rxrpc_set_call_completion(call, RXRPC_CALL_LOCAL_ERROR, - 0, ret); - goto out; - } - } else { - unsigned long now = jiffies; - unsigned long resend_at = now + call->peer->rto_j; - - WRITE_ONCE(call->resend_at, resend_at); - rxrpc_reduce_call_timer(call, resend_at, now, - rxrpc_timer_set_for_send); - } - -out: - rxrpc_put_txbuf(txb, rxrpc_txbuf_put_trans); + /* Stick the packet on the crypto queue or the transmission queue as + * appropriate. + */ + rxrpc_queue_call(call, rxrpc_call_queue_tx_data); } /* diff --git a/net/rxrpc/txbuf.c b/net/rxrpc/txbuf.c index 90ff00c340cd..a5054389dfbb 100644 --- a/net/rxrpc/txbuf.c +++ b/net/rxrpc/txbuf.c @@ -34,7 +34,7 @@ struct rxrpc_txbuf *rxrpc_alloc_txbuf(struct rxrpc_call *call, u8 packet_type, txb->offset = 0; txb->flags = 0; txb->ack_why = 0; - txb->seq = call->tx_top + 1; + txb->seq = call->tx_prepared + 1; txb->wire.epoch = htonl(call->conn->proto.epoch); txb->wire.cid = htonl(call->cid); txb->wire.callNumber = htonl(call->call_id); @@ -107,6 +107,7 @@ void rxrpc_shrink_call_tx_buffer(struct rxrpc_call *call) { struct rxrpc_txbuf *txb; rxrpc_seq_t hard_ack = smp_load_acquire(&call->acks_hard_ack); + bool wake = false; _enter("%x/%x/%x", call->tx_bottom, call->acks_hard_ack, call->tx_top); @@ -123,7 +124,7 @@ void rxrpc_shrink_call_tx_buffer(struct rxrpc_call *call) if (txb->seq != call->tx_bottom + 1) rxrpc_see_txbuf(txb, rxrpc_txbuf_see_out_of_step); ASSERTCMP(txb->seq, ==, call->tx_bottom + 1); - call->tx_bottom++; + smp_store_release(&call->tx_bottom, call->tx_bottom + 1); list_del_rcu(&txb->call_link); trace_rxrpc_txqueue(call, rxrpc_txqueue_dequeue); @@ -131,7 +132,12 @@ void rxrpc_shrink_call_tx_buffer(struct rxrpc_call *call) spin_unlock(&call->tx_lock); rxrpc_put_txbuf(txb, rxrpc_txbuf_put_rotated); + if (after(call->acks_hard_ack, call->tx_bottom + 128)) + wake = true; } spin_unlock(&call->tx_lock); + + if (wake) + wake_up(&call->waitq); } From 29fb4ec385f18db98d9188c2173a0b07d2de6917 Mon Sep 17 00:00:00 2001 From: David Howells Date: Wed, 12 Oct 2022 15:42:06 +0100 Subject: [PATCH 1426/2002] rxrpc: Remove RCU from peer->error_targets list Remove the RCU requirements from the peer's list of error targets so that the error distributor can call sleeping functions. Signed-off-by: David Howells cc: Marc Dionne cc: linux-afs@lists.infradead.org --- net/rxrpc/call_accept.c | 6 ++++++ net/rxrpc/call_object.c | 2 +- net/rxrpc/conn_client.c | 4 ++++ net/rxrpc/conn_object.c | 6 +++--- net/rxrpc/output.c | 6 ------ net/rxrpc/peer_event.c | 15 ++++++++++++++- 6 files changed, 28 insertions(+), 11 deletions(-) diff --git a/net/rxrpc/call_accept.c b/net/rxrpc/call_accept.c index 8bc327aa2beb..5f978b0b2404 100644 --- a/net/rxrpc/call_accept.c +++ b/net/rxrpc/call_accept.c @@ -433,6 +433,12 @@ struct rxrpc_call *rxrpc_new_incoming_call(struct rxrpc_local *local, */ rxrpc_put_call(call, rxrpc_call_put_discard_prealloc); + if (hlist_unhashed(&call->error_link)) { + spin_lock(&call->peer->lock); + hlist_add_head(&call->error_link, &call->peer->error_targets); + spin_unlock(&call->peer->lock); + } + _leave(" = %p{%d}", call, call->debug_id); return call; diff --git a/net/rxrpc/call_object.c b/net/rxrpc/call_object.c index 96a7edd3a842..7570b4e67bc5 100644 --- a/net/rxrpc/call_object.c +++ b/net/rxrpc/call_object.c @@ -442,7 +442,7 @@ void rxrpc_incoming_call(struct rxrpc_sock *rx, rcu_assign_pointer(conn->channels[chan].call, call); spin_lock(&conn->peer->lock); - hlist_add_head_rcu(&call->error_link, &conn->peer->error_targets); + hlist_add_head(&call->error_link, &conn->peer->error_targets); spin_unlock(&conn->peer->lock); rxrpc_start_call_timer(call); diff --git a/net/rxrpc/conn_client.c b/net/rxrpc/conn_client.c index ab3dd22fadc0..3c7b1bdec0db 100644 --- a/net/rxrpc/conn_client.c +++ b/net/rxrpc/conn_client.c @@ -786,6 +786,10 @@ void rxrpc_expose_client_call(struct rxrpc_call *call) if (chan->call_counter >= INT_MAX) set_bit(RXRPC_CONN_DONT_REUSE, &conn->flags); trace_rxrpc_client(conn, channel, rxrpc_client_exposed); + + spin_lock(&call->peer->lock); + hlist_add_head(&call->error_link, &call->peer->error_targets); + spin_unlock(&call->peer->lock); } } diff --git a/net/rxrpc/conn_object.c b/net/rxrpc/conn_object.c index c2e05ea29f12..5a39255ea014 100644 --- a/net/rxrpc/conn_object.c +++ b/net/rxrpc/conn_object.c @@ -215,9 +215,9 @@ void rxrpc_disconnect_call(struct rxrpc_call *call) call->peer->cong_ssthresh = call->cong_ssthresh; if (!hlist_unhashed(&call->error_link)) { - spin_lock_bh(&call->peer->lock); - hlist_del_rcu(&call->error_link); - spin_unlock_bh(&call->peer->lock); + spin_lock(&call->peer->lock); + hlist_del_init(&call->error_link); + spin_unlock(&call->peer->lock); } if (rxrpc_is_client_call(call)) diff --git a/net/rxrpc/output.c b/net/rxrpc/output.c index c8147e50060b..71963b4523be 100644 --- a/net/rxrpc/output.c +++ b/net/rxrpc/output.c @@ -394,12 +394,6 @@ int rxrpc_send_data_packet(struct rxrpc_call *call, struct rxrpc_txbuf *txb) _enter("%x,{%d}", txb->seq, txb->len); - if (hlist_unhashed(&call->error_link)) { - spin_lock_bh(&call->peer->lock); - hlist_add_head_rcu(&call->error_link, &call->peer->error_targets); - spin_unlock_bh(&call->peer->lock); - } - /* Each transmission of a Tx packet needs a new serial number */ serial = atomic_inc_return(&conn->serial); txb->wire.serial = htonl(serial); diff --git a/net/rxrpc/peer_event.c b/net/rxrpc/peer_event.c index 94f63fb1bd67..97d017ca3dc4 100644 --- a/net/rxrpc/peer_event.c +++ b/net/rxrpc/peer_event.c @@ -207,11 +207,24 @@ static void rxrpc_distribute_error(struct rxrpc_peer *peer, int error, enum rxrpc_call_completion compl) { struct rxrpc_call *call; + HLIST_HEAD(error_targets); + + spin_lock(&peer->lock); + hlist_move_list(&peer->error_targets, &error_targets); + + while (!hlist_empty(&error_targets)) { + call = hlist_entry(error_targets.first, + struct rxrpc_call, error_link); + hlist_del_init(&call->error_link); + spin_unlock(&peer->lock); - hlist_for_each_entry_rcu(call, &peer->error_targets, error_link) { rxrpc_see_call(call, rxrpc_call_see_distribute_error); rxrpc_set_call_completion(call, compl, 0, -error); + + spin_lock(&peer->lock); } + + spin_unlock(&peer->lock); } /* From 2d1faf7a0ca3c0b327cf064c80e4e775532c9319 Mon Sep 17 00:00:00 2001 From: David Howells Date: Thu, 6 Oct 2022 15:43:51 +0100 Subject: [PATCH 1427/2002] rxrpc: Simplify skbuff accounting in receive path A received skbuff needs a ref when it gets put on a call data queue or conn packet queue, and rxrpc_input_packet() and co. jump through a lot of hoops to avoid double-dropping the skbuff ref so that we can avoid getting a ref when we queue the packet. Change this so that the skbuff ref is unconditionally dropped by the caller of rxrpc_input_packet(). An additional ref is then taken on the packet if it is pushed onto a queue. Signed-off-by: David Howells cc: Marc Dionne cc: linux-afs@lists.infradead.org --- include/trace/events/rxrpc.h | 3 +- net/rxrpc/input.c | 45 ++++++++++++----------- net/rxrpc/io_thread.c | 70 +++++++++++++++++------------------- 3 files changed, 56 insertions(+), 62 deletions(-) diff --git a/include/trace/events/rxrpc.h b/include/trace/events/rxrpc.h index c3043fbea0e6..82b1327c2ba6 100644 --- a/include/trace/events/rxrpc.h +++ b/include/trace/events/rxrpc.h @@ -28,6 +28,8 @@ EM(rxrpc_skb_eaten_by_unshare_nomem, "ETN unshar-nm") \ EM(rxrpc_skb_get_ack, "GET ack ") \ EM(rxrpc_skb_get_conn_work, "GET conn-work") \ + EM(rxrpc_skb_get_local_work, "GET locl-work") \ + EM(rxrpc_skb_get_reject_work, "GET rej-work ") \ EM(rxrpc_skb_get_to_recvmsg, "GET to-recv ") \ EM(rxrpc_skb_get_to_recvmsg_oos, "GET to-recv-o") \ EM(rxrpc_skb_new_encap_rcv, "NEW encap-rcv") \ @@ -39,7 +41,6 @@ EM(rxrpc_skb_put_error_report, "PUT error-rep") \ EM(rxrpc_skb_put_input, "PUT input ") \ EM(rxrpc_skb_put_jumbo_subpacket, "PUT jumbo-sub") \ - EM(rxrpc_skb_put_lose, "PUT lose ") \ EM(rxrpc_skb_put_purge, "PUT purge ") \ EM(rxrpc_skb_put_rotate, "PUT rotate ") \ EM(rxrpc_skb_put_unknown, "PUT unknown ") \ diff --git a/net/rxrpc/input.c b/net/rxrpc/input.c index 036f02371051..42addbcf59f9 100644 --- a/net/rxrpc/input.c +++ b/net/rxrpc/input.c @@ -338,7 +338,8 @@ static void rxrpc_input_queue_data(struct rxrpc_call *call, struct sk_buff *skb, /* * Process a DATA packet. */ -static void rxrpc_input_data_one(struct rxrpc_call *call, struct sk_buff *skb) +static void rxrpc_input_data_one(struct rxrpc_call *call, struct sk_buff *skb, + bool *_notify) { struct rxrpc_skb_priv *sp = rxrpc_skb(skb); struct sk_buff *oos; @@ -361,7 +362,7 @@ static void rxrpc_input_data_one(struct rxrpc_call *call, struct sk_buff *skb) if (test_and_set_bit(RXRPC_CALL_RX_LAST, &call->flags) && seq + 1 != wtop) { rxrpc_proto_abort("LSN", call, seq); - goto err_free; + return; } } else { if (test_bit(RXRPC_CALL_RX_LAST, &call->flags) && @@ -369,7 +370,7 @@ static void rxrpc_input_data_one(struct rxrpc_call *call, struct sk_buff *skb) pr_warn("Packet beyond last: c=%x q=%x window=%x-%x wlimit=%x\n", call->debug_id, seq, window, wtop, wlimit); rxrpc_proto_abort("LSA", call, seq); - goto err_free; + return; } } @@ -402,9 +403,11 @@ static void rxrpc_input_data_one(struct rxrpc_call *call, struct sk_buff *skb) if (after(window, wtop)) wtop = window; + rxrpc_get_skb(skb, rxrpc_skb_get_to_recvmsg); + spin_lock(&call->recvmsg_queue.lock); rxrpc_input_queue_data(call, skb, window, wtop, rxrpc_receive_queue); - skb = NULL; + *_notify = true; while ((oos = skb_peek(&call->rx_oos_queue))) { struct rxrpc_skb_priv *osp = rxrpc_skb(oos); @@ -456,16 +459,17 @@ static void rxrpc_input_data_one(struct rxrpc_call *call, struct sk_buff *skb) struct rxrpc_skb_priv *osp = rxrpc_skb(oos); if (after(osp->hdr.seq, seq)) { + rxrpc_get_skb(skb, rxrpc_skb_get_to_recvmsg_oos); __skb_queue_before(&call->rx_oos_queue, oos, skb); goto oos_queued; } } + rxrpc_get_skb(skb, rxrpc_skb_get_to_recvmsg_oos); __skb_queue_tail(&call->rx_oos_queue, skb); oos_queued: trace_rxrpc_receive(call, last ? rxrpc_receive_oos_last : rxrpc_receive_oos, sp->hdr.serial, sp->hdr.seq); - skb = NULL; } send_ack: @@ -483,9 +487,6 @@ send_ack: else rxrpc_propose_delay_ACK(call, serial, rxrpc_propose_ack_input_data); - -err_free: - rxrpc_free_skb(skb, rxrpc_skb_put_input); } /* @@ -498,6 +499,7 @@ static bool rxrpc_input_split_jumbo(struct rxrpc_call *call, struct sk_buff *skb struct sk_buff *jskb; unsigned int offset = sizeof(struct rxrpc_wire_header); unsigned int len = skb->len - offset; + bool notify = false; while (sp->hdr.flags & RXRPC_JUMBO_PACKET) { if (len < RXRPC_JUMBO_SUBPKTLEN) @@ -517,7 +519,8 @@ static bool rxrpc_input_split_jumbo(struct rxrpc_call *call, struct sk_buff *skb jsp = rxrpc_skb(jskb); jsp->offset = offset; jsp->len = RXRPC_JUMBO_DATALEN; - rxrpc_input_data_one(call, jskb); + rxrpc_input_data_one(call, jskb, ¬ify); + rxrpc_free_skb(jskb, rxrpc_skb_put_jumbo_subpacket); sp->hdr.flags = jhdr.flags; sp->hdr._rsvd = ntohs(jhdr._rsvd); @@ -529,7 +532,11 @@ static bool rxrpc_input_split_jumbo(struct rxrpc_call *call, struct sk_buff *skb sp->offset = offset; sp->len = len; - rxrpc_input_data_one(call, skb); + rxrpc_input_data_one(call, skb, ¬ify); + if (notify) { + trace_rxrpc_notify_socket(call->debug_id, sp->hdr.serial); + rxrpc_notify_socket(call); + } return true; protocol_error: @@ -552,10 +559,8 @@ static void rxrpc_input_data(struct rxrpc_call *call, struct sk_buff *skb) skb->len, seq0); state = READ_ONCE(call->state); - if (state >= RXRPC_CALL_COMPLETE) { - rxrpc_free_skb(skb, rxrpc_skb_put_input); + if (state >= RXRPC_CALL_COMPLETE) return; - } /* Unshare the packet so that it can be modified for in-place * decryption. @@ -605,7 +610,6 @@ static void rxrpc_input_data(struct rxrpc_call *call, struct sk_buff *skb) out: trace_rxrpc_notify_socket(call->debug_id, serial); rxrpc_notify_socket(call); - rxrpc_free_skb(skb, rxrpc_skb_put_input); _leave(" [queued]"); } @@ -797,7 +801,7 @@ static void rxrpc_input_ack(struct rxrpc_call *call, struct sk_buff *skb) struct rxrpc_ackpacket ack; struct rxrpc_skb_priv *sp = rxrpc_skb(skb); struct rxrpc_ackinfo info; - struct sk_buff *skb_old = NULL, *skb_put = skb; + struct sk_buff *skb_old = NULL; rxrpc_serial_t ack_serial, acked_serial; rxrpc_seq_t first_soft_ack, hard_ack, prev_pkt; int nr_acks, offset, ioffset; @@ -963,6 +967,7 @@ static void rxrpc_input_ack(struct rxrpc_call *call, struct sk_buff *skb) goto out; } + rxrpc_get_skb(skb, rxrpc_skb_get_ack); spin_lock(&call->acks_ack_lock); skb_old = call->acks_soft_tbl; call->acks_soft_tbl = skb; @@ -970,7 +975,6 @@ static void rxrpc_input_ack(struct rxrpc_call *call, struct sk_buff *skb) rxrpc_input_soft_acks(call, skb->data + offset, first_soft_ack, nr_acks, &summary); - skb_put = NULL; } else if (call->acks_soft_tbl) { spin_lock(&call->acks_ack_lock); skb_old = call->acks_soft_tbl; @@ -986,7 +990,6 @@ static void rxrpc_input_ack(struct rxrpc_call *call, struct sk_buff *skb) rxrpc_congestion_management(call, skb, &summary, acked_serial); out: - rxrpc_free_skb(skb_put, rxrpc_skb_put_input); rxrpc_free_skb(skb_old, rxrpc_skb_put_ack); } @@ -1037,11 +1040,11 @@ void rxrpc_input_call_event(struct rxrpc_call *call, struct sk_buff *skb) switch (sp->hdr.type) { case RXRPC_PACKET_TYPE_DATA: rxrpc_input_data(call, skb); - goto no_free; + break; case RXRPC_PACKET_TYPE_ACK: rxrpc_input_ack(call, skb); - goto no_free; + break; case RXRPC_PACKET_TYPE_BUSY: /* Just ignore BUSY packets from the server; the retry and @@ -1061,10 +1064,6 @@ void rxrpc_input_call_event(struct rxrpc_call *call, struct sk_buff *skb) default: break; } - - rxrpc_free_skb(skb, rxrpc_skb_put_input); -no_free: - _leave(""); } /* diff --git a/net/rxrpc/io_thread.c b/net/rxrpc/io_thread.c index 2119941b6d6c..91b8ba5b90db 100644 --- a/net/rxrpc/io_thread.c +++ b/net/rxrpc/io_thread.c @@ -72,6 +72,7 @@ static void rxrpc_post_packet_to_conn(struct rxrpc_connection *conn, { _enter("%p,%p", conn, skb); + rxrpc_get_skb(skb, rxrpc_skb_get_conn_work); skb_queue_tail(&conn->rx_queue, skb); rxrpc_queue_conn(conn, rxrpc_conn_queue_rx_work); } @@ -86,10 +87,9 @@ static void rxrpc_post_packet_to_local(struct rxrpc_local *local, _enter("%p,%p", local, skb); if (rxrpc_get_local_maybe(local, rxrpc_local_get_queue)) { + rxrpc_get_skb(skb, rxrpc_skb_get_local_work); skb_queue_tail(&local->event_queue, skb); rxrpc_queue_local(local); - } else { - rxrpc_free_skb(skb, rxrpc_skb_put_input); } } @@ -99,10 +99,9 @@ static void rxrpc_post_packet_to_local(struct rxrpc_local *local, static void rxrpc_reject_packet(struct rxrpc_local *local, struct sk_buff *skb) { if (rxrpc_get_local_maybe(local, rxrpc_local_get_queue)) { + rxrpc_get_skb(skb, rxrpc_skb_get_reject_work); skb_queue_tail(&local->reject_queue, skb); rxrpc_queue_local(local); - } else { - rxrpc_free_skb(skb, rxrpc_skb_put_input); } } @@ -153,7 +152,7 @@ static bool rxrpc_extract_abort(struct sk_buff *skb) /* * Process packets received on the local endpoint */ -static int rxrpc_input_packet(struct rxrpc_local *local, struct sk_buff *skb) +static int rxrpc_input_packet(struct rxrpc_local *local, struct sk_buff **_skb) { struct rxrpc_connection *conn; struct rxrpc_channel *chan; @@ -161,6 +160,7 @@ static int rxrpc_input_packet(struct rxrpc_local *local, struct sk_buff *skb) struct rxrpc_skb_priv *sp; struct rxrpc_peer *peer = NULL; struct rxrpc_sock *rx = NULL; + struct sk_buff *skb = *_skb; unsigned int channel; if (skb->tstamp == 0) @@ -181,7 +181,6 @@ static int rxrpc_input_packet(struct rxrpc_local *local, struct sk_buff *skb) static int lose; if ((lose++ & 7) == 7) { trace_rxrpc_rx_lose(sp); - rxrpc_free_skb(skb, rxrpc_skb_put_lose); return 0; } } @@ -193,13 +192,13 @@ static int rxrpc_input_packet(struct rxrpc_local *local, struct sk_buff *skb) switch (sp->hdr.type) { case RXRPC_PACKET_TYPE_VERSION: if (rxrpc_to_client(sp)) - goto discard; + return 0; rxrpc_post_packet_to_local(local, skb); - goto out; + return 0; case RXRPC_PACKET_TYPE_BUSY: if (rxrpc_to_server(sp)) - goto discard; + return 0; fallthrough; case RXRPC_PACKET_TYPE_ACK: case RXRPC_PACKET_TYPE_ACKALL: @@ -208,7 +207,7 @@ static int rxrpc_input_packet(struct rxrpc_local *local, struct sk_buff *skb) break; case RXRPC_PACKET_TYPE_ABORT: if (!rxrpc_extract_abort(skb)) - return true; /* Just discard if malformed */ + return 0; /* Just discard if malformed */ break; case RXRPC_PACKET_TYPE_DATA: @@ -220,15 +219,16 @@ static int rxrpc_input_packet(struct rxrpc_local *local, struct sk_buff *skb) * decryption. */ if (sp->hdr.securityIndex != 0) { - struct sk_buff *nskb = skb_unshare(skb, GFP_ATOMIC); - if (!nskb) { - rxrpc_eaten_skb(skb, rxrpc_skb_eaten_by_unshare_nomem); - goto out; + skb = skb_unshare(skb, GFP_ATOMIC); + if (!skb) { + rxrpc_eaten_skb(*_skb, rxrpc_skb_eaten_by_unshare_nomem); + *_skb = NULL; + return 0; } - if (nskb != skb) { - rxrpc_eaten_skb(skb, rxrpc_skb_eaten_by_unshare); - skb = nskb; + if (skb != *_skb) { + rxrpc_eaten_skb(*_skb, rxrpc_skb_eaten_by_unshare); + *_skb = skb; rxrpc_new_skb(skb, rxrpc_skb_new_unshared); sp = rxrpc_skb(skb); } @@ -237,18 +237,18 @@ static int rxrpc_input_packet(struct rxrpc_local *local, struct sk_buff *skb) case RXRPC_PACKET_TYPE_CHALLENGE: if (rxrpc_to_server(sp)) - goto discard; + return 0; break; case RXRPC_PACKET_TYPE_RESPONSE: if (rxrpc_to_client(sp)) - goto discard; + return 0; break; /* Packet types 9-11 should just be ignored. */ case RXRPC_PACKET_TYPE_PARAMS: case RXRPC_PACKET_TYPE_10: case RXRPC_PACKET_TYPE_11: - goto discard; + return 0; default: goto bad_message; @@ -268,7 +268,7 @@ static int rxrpc_input_packet(struct rxrpc_local *local, struct sk_buff *skb) if (sp->hdr.type == RXRPC_PACKET_TYPE_DATA && sp->hdr.seq == 1) goto unsupported_service; - goto discard; + return 0; } } @@ -294,7 +294,7 @@ static int rxrpc_input_packet(struct rxrpc_local *local, struct sk_buff *skb) /* Connection-level packet */ _debug("CONN %p {%d}", conn, conn->debug_id); rxrpc_post_packet_to_conn(conn, skb); - goto out; + return 0; } if ((int)sp->hdr.serial - (int)conn->hi_serial > 0) @@ -306,19 +306,19 @@ static int rxrpc_input_packet(struct rxrpc_local *local, struct sk_buff *skb) /* Ignore really old calls */ if (sp->hdr.callNumber < chan->last_call) - goto discard; + return 0; if (sp->hdr.callNumber == chan->last_call) { if (chan->call || sp->hdr.type == RXRPC_PACKET_TYPE_ABORT) - goto discard; + return 0; /* For the previous service call, if completed * successfully, we discard all further packets. */ if (rxrpc_conn_is_service(conn) && chan->last_type == RXRPC_PACKET_TYPE_ACK) - goto discard; + return 0; /* But otherwise we need to retransmit the final packet * from data cached in the connection record. @@ -329,7 +329,7 @@ static int rxrpc_input_packet(struct rxrpc_local *local, struct sk_buff *skb) sp->hdr.serial, sp->hdr.flags); rxrpc_post_packet_to_conn(conn, skb); - goto out; + return 0; } call = rcu_dereference(chan->call); @@ -357,21 +357,14 @@ static int rxrpc_input_packet(struct rxrpc_local *local, struct sk_buff *skb) sp->hdr.type != RXRPC_PACKET_TYPE_DATA) goto bad_message; if (sp->hdr.seq != 1) - goto discard; + return 0; call = rxrpc_new_incoming_call(local, rx, skb); if (!call) goto reject_packet; } - /* Process a call packet; this either discards or passes on the ref - * elsewhere. - */ + /* Process a call packet. */ rxrpc_input_call_event(call, skb); - goto out; - -discard: - rxrpc_free_skb(skb, rxrpc_skb_put_input); -out: trace_rxrpc_rx_done(0, 0); return 0; @@ -400,9 +393,7 @@ protocol_error: post_abort: skb->mark = RXRPC_SKB_MARK_REJECT_ABORT; reject_packet: - trace_rxrpc_rx_done(skb->mark, skb->priority); rxrpc_reject_packet(local, skb); - _leave(" [badmsg]"); return 0; } @@ -441,9 +432,12 @@ int rxrpc_io_thread(void *data) if ((skb = __skb_dequeue(&rx_queue))) { switch (skb->mark) { case RXRPC_SKB_MARK_PACKET: + skb->priority = 0; rcu_read_lock(); - rxrpc_input_packet(local, skb); + rxrpc_input_packet(local, &skb); rcu_read_unlock(); + trace_rxrpc_rx_done(skb->mark, skb->priority); + rxrpc_free_skb(skb, rxrpc_skb_put_input); break; case RXRPC_SKB_MARK_ERROR: rxrpc_input_error(local, skb); From cd21effb0552d666b2f8609560be764a1a56adbe Mon Sep 17 00:00:00 2001 From: David Howells Date: Sat, 8 Oct 2022 14:33:50 +0100 Subject: [PATCH 1428/2002] rxrpc: Reduce the use of RCU in packet input Shrink the region of rxrpc_input_packet() that is covered by the RCU read lock so that it only covers the connection and call lookup. This means that the bits now outside of that can call sleepable functions such as kmalloc and sendmsg. Also take a ref on the conn or call we're going to use before we drop the RCU read lock. Signed-off-by: David Howells cc: Marc Dionne cc: linux-afs@lists.infradead.org --- net/rxrpc/ar-internal.h | 3 +- net/rxrpc/call_accept.c | 13 ++------ net/rxrpc/input.c | 7 ++--- net/rxrpc/io_thread.c | 68 +++++++++++++++++++++++++++++++---------- 4 files changed, 59 insertions(+), 32 deletions(-) diff --git a/net/rxrpc/ar-internal.h b/net/rxrpc/ar-internal.h index 6af7298af39b..cfd16f1e5c83 100644 --- a/net/rxrpc/ar-internal.h +++ b/net/rxrpc/ar-internal.h @@ -961,8 +961,7 @@ void rxrpc_unpublish_service_conn(struct rxrpc_connection *); * input.c */ void rxrpc_input_call_event(struct rxrpc_call *, struct sk_buff *); -void rxrpc_input_implicit_end_call(struct rxrpc_sock *, struct rxrpc_connection *, - struct rxrpc_call *); +void rxrpc_input_implicit_end_call(struct rxrpc_connection *, struct rxrpc_call *); /* * io_thread.c diff --git a/net/rxrpc/call_accept.c b/net/rxrpc/call_accept.c index 5f978b0b2404..beb8efa2e7a9 100644 --- a/net/rxrpc/call_accept.c +++ b/net/rxrpc/call_accept.c @@ -336,13 +336,13 @@ static struct rxrpc_call *rxrpc_alloc_incoming_call(struct rxrpc_sock *rx, * If this is for a kernel service, when we allocate the call, it will have * three refs on it: (1) the kernel service, (2) the user_call_ID tree, (3) the * retainer ref obtained from the backlog buffer. Prealloc calls for userspace - * services only have the ref from the backlog buffer. We want to pass this - * ref to non-BH context to dispose of. + * services only have the ref from the backlog buffer. We pass this ref to the + * caller. * * If we want to report an error, we mark the skb with the packet type and * abort code and return NULL. * - * The call is returned with the user access mutex held. + * The call is returned with the user access mutex held and a ref on it. */ struct rxrpc_call *rxrpc_new_incoming_call(struct rxrpc_local *local, struct rxrpc_sock *rx, @@ -426,13 +426,6 @@ struct rxrpc_call *rxrpc_new_incoming_call(struct rxrpc_local *local, rxrpc_send_ping(call, skb); - /* We have to discard the prealloc queue's ref here and rely on a - * combination of the RCU read lock and refs held either by the socket - * (recvmsg queue, to-be-accepted queue or user ID tree) or the kernel - * service to prevent the call from being deallocated too early. - */ - rxrpc_put_call(call, rxrpc_call_put_discard_prealloc); - if (hlist_unhashed(&call->error_link)) { spin_lock(&call->peer->lock); hlist_add_head(&call->error_link, &call->peer->error_targets); diff --git a/net/rxrpc/input.c b/net/rxrpc/input.c index 42addbcf59f9..01d32f817a7a 100644 --- a/net/rxrpc/input.c +++ b/net/rxrpc/input.c @@ -1072,8 +1072,7 @@ void rxrpc_input_call_event(struct rxrpc_call *call, struct sk_buff *skb) * * TODO: If callNumber > call_id + 1, renegotiate security. */ -void rxrpc_input_implicit_end_call(struct rxrpc_sock *rx, - struct rxrpc_connection *conn, +void rxrpc_input_implicit_end_call(struct rxrpc_connection *conn, struct rxrpc_call *call) { switch (READ_ONCE(call->state)) { @@ -1091,7 +1090,7 @@ void rxrpc_input_implicit_end_call(struct rxrpc_sock *rx, break; } - spin_lock(&rx->incoming_lock); + spin_lock(&conn->bundle->channel_lock); __rxrpc_disconnect_call(conn, call); - spin_unlock(&rx->incoming_lock); + spin_unlock(&conn->bundle->channel_lock); } diff --git a/net/rxrpc/io_thread.c b/net/rxrpc/io_thread.c index 91b8ba5b90db..3b6927610677 100644 --- a/net/rxrpc/io_thread.c +++ b/net/rxrpc/io_thread.c @@ -257,6 +257,8 @@ static int rxrpc_input_packet(struct rxrpc_local *local, struct sk_buff **_skb) if (sp->hdr.serviceId == 0) goto bad_message; + rcu_read_lock(); + if (rxrpc_to_server(sp)) { /* Weed out packets to services we're not offering. Packets * that would begin a call are explicitly rejected and the rest @@ -264,7 +266,9 @@ static int rxrpc_input_packet(struct rxrpc_local *local, struct sk_buff **_skb) */ rx = rcu_dereference(local->service); if (!rx || (sp->hdr.serviceId != rx->srx.srx_service && - sp->hdr.serviceId != rx->second_service)) { + sp->hdr.serviceId != rx->second_service) + ) { + rcu_read_unlock(); if (sp->hdr.type == RXRPC_PACKET_TYPE_DATA && sp->hdr.seq == 1) goto unsupported_service; @@ -293,7 +297,12 @@ static int rxrpc_input_packet(struct rxrpc_local *local, struct sk_buff **_skb) if (sp->hdr.callNumber == 0) { /* Connection-level packet */ _debug("CONN %p {%d}", conn, conn->debug_id); - rxrpc_post_packet_to_conn(conn, skb); + conn = rxrpc_get_connection_maybe(conn, rxrpc_conn_get_conn_input); + rcu_read_unlock(); + if (conn) { + rxrpc_post_packet_to_conn(conn, skb); + rxrpc_put_connection(conn, rxrpc_conn_put_conn_input); + } return 0; } @@ -305,20 +314,26 @@ static int rxrpc_input_packet(struct rxrpc_local *local, struct sk_buff **_skb) chan = &conn->channels[channel]; /* Ignore really old calls */ - if (sp->hdr.callNumber < chan->last_call) + if (sp->hdr.callNumber < chan->last_call) { + rcu_read_unlock(); return 0; + } if (sp->hdr.callNumber == chan->last_call) { if (chan->call || - sp->hdr.type == RXRPC_PACKET_TYPE_ABORT) + sp->hdr.type == RXRPC_PACKET_TYPE_ABORT) { + rcu_read_unlock(); return 0; + } /* For the previous service call, if completed * successfully, we discard all further packets. */ if (rxrpc_conn_is_service(conn) && - chan->last_type == RXRPC_PACKET_TYPE_ACK) + chan->last_type == RXRPC_PACKET_TYPE_ACK) { + rcu_read_unlock(); return 0; + } /* But otherwise we need to retransmit the final packet * from data cached in the connection record. @@ -328,20 +343,32 @@ static int rxrpc_input_packet(struct rxrpc_local *local, struct sk_buff **_skb) sp->hdr.seq, sp->hdr.serial, sp->hdr.flags); - rxrpc_post_packet_to_conn(conn, skb); + conn = rxrpc_get_connection_maybe(conn, rxrpc_conn_get_call_input); + rcu_read_unlock(); + if (conn) { + rxrpc_post_packet_to_conn(conn, skb); + rxrpc_put_connection(conn, rxrpc_conn_put_call_input); + } return 0; } call = rcu_dereference(chan->call); if (sp->hdr.callNumber > chan->call_id) { - if (rxrpc_to_client(sp)) + if (rxrpc_to_client(sp)) { + rcu_read_unlock(); goto reject_packet; - if (call) - rxrpc_input_implicit_end_call(rx, conn, call); - call = NULL; + } + if (call) { + rxrpc_input_implicit_end_call(conn, call); + chan->call = NULL; + call = NULL; + } } + if (call && !rxrpc_try_get_call(call, rxrpc_call_get_input)) + call = NULL; + if (call) { if (sp->hdr.serviceId != call->dest_srx.srx_service) call->dest_srx.srx_service = sp->hdr.serviceId; @@ -352,23 +379,33 @@ static int rxrpc_input_packet(struct rxrpc_local *local, struct sk_buff **_skb) } } - if (!call || refcount_read(&call->ref) == 0) { + if (!call) { if (rxrpc_to_client(sp) || - sp->hdr.type != RXRPC_PACKET_TYPE_DATA) + sp->hdr.type != RXRPC_PACKET_TYPE_DATA) { + rcu_read_unlock(); goto bad_message; - if (sp->hdr.seq != 1) + } + if (sp->hdr.seq != 1) { + rcu_read_unlock(); return 0; + } call = rxrpc_new_incoming_call(local, rx, skb); - if (!call) + if (!call) { + rcu_read_unlock(); goto reject_packet; + } } + rcu_read_unlock(); + /* Process a call packet. */ rxrpc_input_call_event(call, skb); + rxrpc_put_call(call, rxrpc_call_put_input); trace_rxrpc_rx_done(0, 0); return 0; wrong_security: + rcu_read_unlock(); trace_rxrpc_abort(0, "SEC", sp->hdr.cid, sp->hdr.callNumber, sp->hdr.seq, RXKADINCONSISTENCY, EBADMSG); skb->priority = RXKADINCONSISTENCY; @@ -381,6 +418,7 @@ unsupported_service: goto post_abort; reupgrade: + rcu_read_unlock(); trace_rxrpc_abort(0, "UPG", sp->hdr.cid, sp->hdr.callNumber, sp->hdr.seq, RX_PROTOCOL_ERROR, EBADMSG); goto protocol_error; @@ -433,9 +471,7 @@ int rxrpc_io_thread(void *data) switch (skb->mark) { case RXRPC_SKB_MARK_PACKET: skb->priority = 0; - rcu_read_lock(); rxrpc_input_packet(local, &skb); - rcu_read_unlock(); trace_rxrpc_rx_done(skb->mark, skb->priority); rxrpc_free_skb(skb, rxrpc_skb_put_input); break; From 393a2a2007d13df7ae54c94328b45b6c2269b6a9 Mon Sep 17 00:00:00 2001 From: David Howells Date: Thu, 20 Oct 2022 22:36:20 +0100 Subject: [PATCH 1429/2002] rxrpc: Extract the peer address from an incoming packet earlier Extract the peer address from an incoming packet earlier, at the beginning of rxrpc_input_packet() and thence pass a pointer to it to various functions that use it as part of the lookup rather than doing it on several separate paths. Signed-off-by: David Howells cc: Marc Dionne cc: linux-afs@lists.infradead.org --- net/rxrpc/ar-internal.h | 2 ++ net/rxrpc/call_accept.c | 10 ++++++---- net/rxrpc/conn_object.c | 29 ++++++++--------------------- net/rxrpc/io_thread.c | 17 +++++++++++++++-- 4 files changed, 31 insertions(+), 27 deletions(-) diff --git a/net/rxrpc/ar-internal.h b/net/rxrpc/ar-internal.h index cfd16f1e5c83..c3c915a05627 100644 --- a/net/rxrpc/ar-internal.h +++ b/net/rxrpc/ar-internal.h @@ -824,6 +824,7 @@ int rxrpc_service_prealloc(struct rxrpc_sock *, gfp_t); void rxrpc_discard_prealloc(struct rxrpc_sock *); struct rxrpc_call *rxrpc_new_incoming_call(struct rxrpc_local *, struct rxrpc_sock *, + struct sockaddr_rxrpc *, struct sk_buff *); void rxrpc_accept_incoming_calls(struct rxrpc_local *); int rxrpc_user_charge_accept(struct rxrpc_sock *, unsigned long); @@ -916,6 +917,7 @@ extern unsigned int rxrpc_closed_conn_expiry; struct rxrpc_connection *rxrpc_alloc_connection(struct rxrpc_net *, gfp_t); struct rxrpc_connection *rxrpc_find_connection_rcu(struct rxrpc_local *, + struct sockaddr_rxrpc *, struct sk_buff *, struct rxrpc_peer **); void __rxrpc_disconnect_call(struct rxrpc_connection *, struct rxrpc_call *); diff --git a/net/rxrpc/call_accept.c b/net/rxrpc/call_accept.c index beb8efa2e7a9..11134b7cec17 100644 --- a/net/rxrpc/call_accept.c +++ b/net/rxrpc/call_accept.c @@ -258,6 +258,7 @@ static struct rxrpc_call *rxrpc_alloc_incoming_call(struct rxrpc_sock *rx, struct rxrpc_peer *peer, struct rxrpc_connection *conn, const struct rxrpc_security *sec, + struct sockaddr_rxrpc *peer_srx, struct sk_buff *skb) { struct rxrpc_backlog *b = rx->backlog; @@ -287,8 +288,7 @@ static struct rxrpc_call *rxrpc_alloc_incoming_call(struct rxrpc_sock *rx, peer = NULL; if (!peer) { peer = b->peer_backlog[peer_tail]; - if (rxrpc_extract_addr_from_skb(&peer->srx, skb) < 0) - return NULL; + peer->srx = *peer_srx; b->peer_backlog[peer_tail] = NULL; smp_store_release(&b->peer_backlog_tail, (peer_tail + 1) & @@ -346,6 +346,7 @@ static struct rxrpc_call *rxrpc_alloc_incoming_call(struct rxrpc_sock *rx, */ struct rxrpc_call *rxrpc_new_incoming_call(struct rxrpc_local *local, struct rxrpc_sock *rx, + struct sockaddr_rxrpc *peer_srx, struct sk_buff *skb) { struct rxrpc_skb_priv *sp = rxrpc_skb(skb); @@ -371,7 +372,7 @@ struct rxrpc_call *rxrpc_new_incoming_call(struct rxrpc_local *local, * we have to recheck the routing. However, we're now holding * rx->incoming_lock, so the values should remain stable. */ - conn = rxrpc_find_connection_rcu(local, skb, &peer); + conn = rxrpc_find_connection_rcu(local, peer_srx, skb, &peer); if (!conn) { sec = rxrpc_get_incoming_security(rx, skb); @@ -379,7 +380,8 @@ struct rxrpc_call *rxrpc_new_incoming_call(struct rxrpc_local *local, goto no_call; } - call = rxrpc_alloc_incoming_call(rx, local, peer, conn, sec, skb); + call = rxrpc_alloc_incoming_call(rx, local, peer, conn, sec, peer_srx, + skb); if (!call) { skb->mark = RXRPC_SKB_MARK_REJECT_BUSY; goto no_call; diff --git a/net/rxrpc/conn_object.c b/net/rxrpc/conn_object.c index 5a39255ea014..98e49646ca1d 100644 --- a/net/rxrpc/conn_object.c +++ b/net/rxrpc/conn_object.c @@ -73,29 +73,17 @@ struct rxrpc_connection *rxrpc_alloc_connection(struct rxrpc_net *rxnet, * The caller must be holding the RCU read lock. */ struct rxrpc_connection *rxrpc_find_connection_rcu(struct rxrpc_local *local, + struct sockaddr_rxrpc *srx, struct sk_buff *skb, struct rxrpc_peer **_peer) { struct rxrpc_connection *conn; struct rxrpc_conn_proto k; struct rxrpc_skb_priv *sp = rxrpc_skb(skb); - struct sockaddr_rxrpc srx; struct rxrpc_peer *peer; _enter(",%x", sp->hdr.cid & RXRPC_CIDMASK); - if (rxrpc_extract_addr_from_skb(&srx, skb) < 0) - goto not_found; - - if (srx.transport.family != local->srx.transport.family && - (srx.transport.family == AF_INET && - local->srx.transport.family != AF_INET6)) { - pr_warn_ratelimited("AF_RXRPC: Protocol mismatch %u not %u\n", - srx.transport.family, - local->srx.transport.family); - goto not_found; - } - k.epoch = sp->hdr.epoch; k.cid = sp->hdr.cid & RXRPC_CIDMASK; @@ -104,7 +92,7 @@ struct rxrpc_connection *rxrpc_find_connection_rcu(struct rxrpc_local *local, * parameter set. We look up the peer first as an intermediate * step and then the connection from the peer's tree. */ - peer = rxrpc_lookup_peer_rcu(local, &srx); + peer = rxrpc_lookup_peer_rcu(local, srx); if (!peer) goto not_found; *_peer = peer; @@ -117,8 +105,7 @@ struct rxrpc_connection *rxrpc_find_connection_rcu(struct rxrpc_local *local, /* Look up client connections by connection ID alone as their * IDs are unique for this machine. */ - conn = idr_find(&rxrpc_client_conn_ids, - sp->hdr.cid >> RXRPC_CIDSHIFT); + conn = idr_find(&rxrpc_client_conn_ids, sp->hdr.cid >> RXRPC_CIDSHIFT); if (!conn || refcount_read(&conn->ref) == 0) { _debug("no conn"); goto not_found; @@ -129,20 +116,20 @@ struct rxrpc_connection *rxrpc_find_connection_rcu(struct rxrpc_local *local, goto not_found; peer = conn->peer; - switch (srx.transport.family) { + switch (srx->transport.family) { case AF_INET: if (peer->srx.transport.sin.sin_port != - srx.transport.sin.sin_port || + srx->transport.sin.sin_port || peer->srx.transport.sin.sin_addr.s_addr != - srx.transport.sin.sin_addr.s_addr) + srx->transport.sin.sin_addr.s_addr) goto not_found; break; #ifdef CONFIG_AF_RXRPC_IPV6 case AF_INET6: if (peer->srx.transport.sin6.sin6_port != - srx.transport.sin6.sin6_port || + srx->transport.sin6.sin6_port || memcmp(&peer->srx.transport.sin6.sin6_addr, - &srx.transport.sin6.sin6_addr, + &srx->transport.sin6.sin6_addr, sizeof(struct in6_addr)) != 0) goto not_found; break; diff --git a/net/rxrpc/io_thread.c b/net/rxrpc/io_thread.c index 3b6927610677..bc65d83fab88 100644 --- a/net/rxrpc/io_thread.c +++ b/net/rxrpc/io_thread.c @@ -155,6 +155,7 @@ static bool rxrpc_extract_abort(struct sk_buff *skb) static int rxrpc_input_packet(struct rxrpc_local *local, struct sk_buff **_skb) { struct rxrpc_connection *conn; + struct sockaddr_rxrpc peer_srx; struct rxrpc_channel *chan; struct rxrpc_call *call = NULL; struct rxrpc_skb_priv *sp; @@ -257,6 +258,18 @@ static int rxrpc_input_packet(struct rxrpc_local *local, struct sk_buff **_skb) if (sp->hdr.serviceId == 0) goto bad_message; + if (WARN_ON_ONCE(rxrpc_extract_addr_from_skb(&peer_srx, skb) < 0)) + return 0; /* Unsupported address type - discard. */ + + if (peer_srx.transport.family != local->srx.transport.family && + (peer_srx.transport.family == AF_INET && + local->srx.transport.family != AF_INET6)) { + pr_warn_ratelimited("AF_RXRPC: Protocol mismatch %u not %u\n", + peer_srx.transport.family, + local->srx.transport.family); + return 0; /* Wrong address type - discard. */ + } + rcu_read_lock(); if (rxrpc_to_server(sp)) { @@ -276,7 +289,7 @@ static int rxrpc_input_packet(struct rxrpc_local *local, struct sk_buff **_skb) } } - conn = rxrpc_find_connection_rcu(local, skb, &peer); + conn = rxrpc_find_connection_rcu(local, &peer_srx, skb, &peer); if (conn) { if (sp->hdr.securityIndex != conn->security_ix) goto wrong_security; @@ -389,7 +402,7 @@ static int rxrpc_input_packet(struct rxrpc_local *local, struct sk_buff **_skb) rcu_read_unlock(); return 0; } - call = rxrpc_new_incoming_call(local, rx, skb); + call = rxrpc_new_incoming_call(local, rx, &peer_srx, skb); if (!call) { rcu_read_unlock(); goto reject_packet; From 5e6ef4f1017c7f844e305283bbd8875af475e2fc Mon Sep 17 00:00:00 2001 From: David Howells Date: Thu, 23 Jan 2020 13:13:41 +0000 Subject: [PATCH 1430/2002] rxrpc: Make the I/O thread take over the call and local processor work Move the functions from the call->processor and local->processor work items into the domain of the I/O thread. The call event processor, now called from the I/O thread, then takes over the job of cranking the call state machine, processing incoming packets and transmitting DATA, ACK and ABORT packets. In a future patch, rxrpc_send_ACK() will transmit the ACK on the spot rather than queuing it for later transmission. The call event processor becomes purely received-skb driven. It only transmits things in response to events. We use "pokes" to queue a dummy skb to make it do things like start/resume transmitting data. Timer expiry also results in pokes. The connection event processor, becomes similar, though crypto events, such as dealing with CHALLENGE and RESPONSE packets is offloaded to a work item to avoid doing crypto in the I/O thread. The local event processor is removed and VERSION response packets are generated directly from the packet parser. Similarly, ABORTs generated in response to protocol errors will be transmitted immediately rather than being pushed onto a queue for later transmission. Changes: ======== ver #2) - Fix a couple of introduced lock context imbalances. Signed-off-by: David Howells cc: Marc Dionne cc: linux-afs@lists.infradead.org --- include/trace/events/rxrpc.h | 42 ++--- net/rxrpc/ar-internal.h | 50 ++--- net/rxrpc/call_accept.c | 124 ++++++------- net/rxrpc/call_event.c | 173 +++++++---------- net/rxrpc/call_object.c | 56 +++--- net/rxrpc/conn_event.c | 60 ++++++ net/rxrpc/conn_object.c | 101 ++++------ net/rxrpc/input.c | 167 +++++------------ net/rxrpc/io_thread.c | 351 ++++++++++++++++------------------- net/rxrpc/local_event.c | 43 +---- net/rxrpc/local_object.c | 69 +------ net/rxrpc/output.c | 96 ++++------ net/rxrpc/peer_event.c | 29 ++- net/rxrpc/recvmsg.c | 9 +- net/rxrpc/sendmsg.c | 10 +- 15 files changed, 567 insertions(+), 813 deletions(-) diff --git a/include/trace/events/rxrpc.h b/include/trace/events/rxrpc.h index 82b1327c2ba6..c49b0c233594 100644 --- a/include/trace/events/rxrpc.h +++ b/include/trace/events/rxrpc.h @@ -26,7 +26,6 @@ #define rxrpc_skb_traces \ EM(rxrpc_skb_eaten_by_unshare, "ETN unshare ") \ EM(rxrpc_skb_eaten_by_unshare_nomem, "ETN unshar-nm") \ - EM(rxrpc_skb_get_ack, "GET ack ") \ EM(rxrpc_skb_get_conn_work, "GET conn-work") \ EM(rxrpc_skb_get_local_work, "GET locl-work") \ EM(rxrpc_skb_get_reject_work, "GET rej-work ") \ @@ -36,7 +35,6 @@ EM(rxrpc_skb_new_error_report, "NEW error-rpt") \ EM(rxrpc_skb_new_jumbo_subpacket, "NEW jumbo-sub") \ EM(rxrpc_skb_new_unshared, "NEW unshared ") \ - EM(rxrpc_skb_put_ack, "PUT ack ") \ EM(rxrpc_skb_put_conn_work, "PUT conn-work") \ EM(rxrpc_skb_put_error_report, "PUT error-rep") \ EM(rxrpc_skb_put_input, "PUT input ") \ @@ -45,7 +43,6 @@ EM(rxrpc_skb_put_rotate, "PUT rotate ") \ EM(rxrpc_skb_put_unknown, "PUT unknown ") \ EM(rxrpc_skb_see_conn_work, "SEE conn-work") \ - EM(rxrpc_skb_see_local_work, "SEE locl-work") \ EM(rxrpc_skb_see_recvmsg, "SEE recvmsg ") \ EM(rxrpc_skb_see_reject, "SEE reject ") \ EM(rxrpc_skb_see_rotate, "SEE rotate ") \ @@ -58,10 +55,7 @@ EM(rxrpc_local_get_for_use, "GET for-use ") \ EM(rxrpc_local_get_peer, "GET peer ") \ EM(rxrpc_local_get_prealloc_conn, "GET conn-pre") \ - EM(rxrpc_local_get_queue, "GET queue ") \ EM(rxrpc_local_new, "NEW ") \ - EM(rxrpc_local_processing, "PROCESSING ") \ - EM(rxrpc_local_put_already_queued, "PUT alreadyq") \ EM(rxrpc_local_put_bind, "PUT bind ") \ EM(rxrpc_local_put_call, "PUT call ") \ EM(rxrpc_local_put_for_use, "PUT for-use ") \ @@ -69,8 +63,6 @@ EM(rxrpc_local_put_peer, "PUT peer ") \ EM(rxrpc_local_put_prealloc_conn, "PUT conn-pre") \ EM(rxrpc_local_put_release_sock, "PUT rel-sock") \ - EM(rxrpc_local_put_queue, "PUT queue ") \ - EM(rxrpc_local_queued, "QUEUED ") \ EM(rxrpc_local_see_tx_ack, "SEE tx-ack ") \ EM(rxrpc_local_stop, "STOP ") \ EM(rxrpc_local_stopped, "STOPPED ") \ @@ -78,11 +70,9 @@ EM(rxrpc_local_unuse_conn_work, "UNU conn-wrk") \ EM(rxrpc_local_unuse_peer_keepalive, "UNU peer-kpa") \ EM(rxrpc_local_unuse_release_sock, "UNU rel-sock") \ - EM(rxrpc_local_unuse_work, "UNU work ") \ EM(rxrpc_local_use_conn_work, "USE conn-wrk") \ EM(rxrpc_local_use_lookup, "USE lookup ") \ - EM(rxrpc_local_use_peer_keepalive, "USE peer-kpa") \ - E_(rxrpc_local_use_work, "USE work ") + E_(rxrpc_local_use_peer_keepalive, "USE peer-kpa") #define rxrpc_peer_traces \ EM(rxrpc_peer_free, "FREE ") \ @@ -90,6 +80,7 @@ EM(rxrpc_peer_get_activate_call, "GET act-call") \ EM(rxrpc_peer_get_bundle, "GET bundle ") \ EM(rxrpc_peer_get_client_conn, "GET cln-conn") \ + EM(rxrpc_peer_get_input, "GET input ") \ EM(rxrpc_peer_get_input_error, "GET inpt-err") \ EM(rxrpc_peer_get_keepalive, "GET keepaliv") \ EM(rxrpc_peer_get_lookup_client, "GET look-cln") \ @@ -100,6 +91,7 @@ EM(rxrpc_peer_put_call, "PUT call ") \ EM(rxrpc_peer_put_conn, "PUT conn ") \ EM(rxrpc_peer_put_discard_tmp, "PUT disc-tmp") \ + EM(rxrpc_peer_put_input, "PUT input ") \ EM(rxrpc_peer_put_input_error, "PUT inpt-err") \ E_(rxrpc_peer_put_keepalive, "PUT keepaliv") @@ -180,11 +172,6 @@ EM(rxrpc_call_put_sendmsg, "PUT sendmsg ") \ EM(rxrpc_call_put_unnotify, "PUT unnotify") \ EM(rxrpc_call_put_userid_exists, "PUT u-exists") \ - EM(rxrpc_call_queue_abort, "QUE abort ") \ - EM(rxrpc_call_queue_requeue, "QUE requeue ") \ - EM(rxrpc_call_queue_resend, "QUE resend ") \ - EM(rxrpc_call_queue_timer, "QUE timer ") \ - EM(rxrpc_call_queue_tx_data, "QUE tx-data ") \ EM(rxrpc_call_see_accept, "SEE accept ") \ EM(rxrpc_call_see_activate_client, "SEE act-clnt") \ EM(rxrpc_call_see_connect_failed, "SEE con-fail") \ @@ -282,6 +269,7 @@ EM(rxrpc_propose_ack_respond_to_ping, "Rsp2Png") \ EM(rxrpc_propose_ack_retry_tx, "RetryTx") \ EM(rxrpc_propose_ack_rotate_rx, "RxAck ") \ + EM(rxrpc_propose_ack_rx_idle, "RxIdle ") \ E_(rxrpc_propose_ack_terminal_ack, "ClTerm ") #define rxrpc_congest_modes \ @@ -1532,6 +1520,7 @@ TRACE_EVENT(rxrpc_connect_call, __field(unsigned long, user_call_ID ) __field(u32, cid ) __field(u32, call_id ) + __field_struct(struct sockaddr_rxrpc, srx ) ), TP_fast_assign( @@ -1539,33 +1528,42 @@ TRACE_EVENT(rxrpc_connect_call, __entry->user_call_ID = call->user_call_ID; __entry->cid = call->cid; __entry->call_id = call->call_id; + __entry->srx = call->dest_srx; ), - TP_printk("c=%08x u=%p %08x:%08x", + TP_printk("c=%08x u=%p %08x:%08x dst=%pISp", __entry->call, (void *)__entry->user_call_ID, __entry->cid, - __entry->call_id) + __entry->call_id, + &__entry->srx.transport) ); TRACE_EVENT(rxrpc_resend, - TP_PROTO(struct rxrpc_call *call), + TP_PROTO(struct rxrpc_call *call, struct sk_buff *ack), - TP_ARGS(call), + TP_ARGS(call, ack), TP_STRUCT__entry( __field(unsigned int, call ) __field(rxrpc_seq_t, seq ) + __field(rxrpc_seq_t, transmitted ) + __field(rxrpc_serial_t, ack_serial ) ), TP_fast_assign( + struct rxrpc_skb_priv *sp = ack ? rxrpc_skb(ack) : NULL; __entry->call = call->debug_id; __entry->seq = call->acks_hard_ack; + __entry->transmitted = call->tx_transmitted; + __entry->ack_serial = sp ? sp->hdr.serial : 0; ), - TP_printk("c=%08x q=%x", + TP_printk("c=%08x r=%x q=%x tq=%x", __entry->call, - __entry->seq) + __entry->ack_serial, + __entry->seq, + __entry->transmitted) ); TRACE_EVENT(rxrpc_rx_icmp, diff --git a/net/rxrpc/ar-internal.h b/net/rxrpc/ar-internal.h index c3c915a05627..6b993a3d4186 100644 --- a/net/rxrpc/ar-internal.h +++ b/net/rxrpc/ar-internal.h @@ -283,14 +283,11 @@ struct rxrpc_local { struct rxrpc_net *rxnet; /* The network ns in which this resides */ struct hlist_node link; struct socket *socket; /* my UDP socket */ - struct work_struct processor; struct task_struct *io_thread; struct list_head ack_tx_queue; /* List of ACKs that need sending */ spinlock_t ack_tx_lock; /* ACK list lock */ struct rxrpc_sock __rcu *service; /* Service(s) listening on this endpoint */ struct rw_semaphore defrag_sem; /* control re-enablement of IP DF bit */ - struct sk_buff_head reject_queue; /* packets awaiting rejection */ - struct sk_buff_head event_queue; /* endpoint event packets awaiting processing */ struct sk_buff_head rx_queue; /* Received packets */ struct list_head call_attend_q; /* Calls requiring immediate attention */ struct rb_root client_bundles; /* Client connection bundles by socket params */ @@ -524,23 +521,19 @@ enum rxrpc_call_flag { RXRPC_CALL_RETRANS_TIMEOUT, /* Retransmission due to timeout occurred */ RXRPC_CALL_BEGAN_RX_TIMER, /* We began the expect_rx_by timer */ RXRPC_CALL_RX_HEARD, /* The peer responded at least once to this call */ - RXRPC_CALL_RX_UNDERRUN, /* Got data underrun */ RXRPC_CALL_DISCONNECTED, /* The call has been disconnected */ RXRPC_CALL_KERNEL, /* The call was made by the kernel */ RXRPC_CALL_UPGRADE, /* Service upgrade was requested for the call */ - RXRPC_CALL_DELAY_ACK_PENDING, /* DELAY ACK generation is pending */ - RXRPC_CALL_IDLE_ACK_PENDING, /* IDLE ACK generation is pending */ RXRPC_CALL_EXCLUSIVE, /* The call uses a once-only connection */ + RXRPC_CALL_RX_IS_IDLE, /* Reception is idle - send an ACK */ }; /* * Events that can be raised on a call. */ enum rxrpc_call_event { - RXRPC_CALL_EV_ABORT, /* need to generate abort */ - RXRPC_CALL_EV_RESEND, /* Tx resend required */ - RXRPC_CALL_EV_EXPIRED, /* Expiry occurred */ RXRPC_CALL_EV_ACK_LOST, /* ACK may be lost, send ping */ + RXRPC_CALL_EV_INITIAL_PING, /* Send initial ping for a new service call */ }; /* @@ -611,7 +604,6 @@ struct rxrpc_call { u32 next_rx_timo; /* Timeout for next Rx packet (jif) */ u32 next_req_timo; /* Timeout for next Rx request packet (jif) */ struct timer_list timer; /* Combined event timer */ - struct work_struct processor; /* Event processor */ struct work_struct destroyer; /* In-process-context destroyer */ rxrpc_notify_rx_t notify_rx; /* kernel service Rx notification function */ struct list_head link; /* link in master call list */ @@ -705,11 +697,7 @@ struct rxrpc_call { rxrpc_seq_t acks_prev_seq; /* Highest previousPacket received */ rxrpc_seq_t acks_hard_ack; /* Latest hard-ack point */ rxrpc_seq_t acks_lowest_nak; /* Lowest NACK in the buffer (or ==tx_hard_ack) */ - rxrpc_seq_t acks_lost_top; /* tx_top at the time lost-ack ping sent */ - rxrpc_serial_t acks_lost_ping; /* Serial number of probe ACK */ rxrpc_serial_t acks_highest_serial; /* Highest serial number ACK'd */ - struct sk_buff *acks_soft_tbl; /* The last ACK packet with NAKs in it */ - spinlock_t acks_ack_lock; /* Access to ->acks_last_ack */ }; /* @@ -822,10 +810,9 @@ extern struct workqueue_struct *rxrpc_workqueue; */ int rxrpc_service_prealloc(struct rxrpc_sock *, gfp_t); void rxrpc_discard_prealloc(struct rxrpc_sock *); -struct rxrpc_call *rxrpc_new_incoming_call(struct rxrpc_local *, - struct rxrpc_sock *, - struct sockaddr_rxrpc *, - struct sk_buff *); +bool rxrpc_new_incoming_call(struct rxrpc_local *, struct rxrpc_peer *, + struct rxrpc_connection *, struct sockaddr_rxrpc *, + struct sk_buff *); void rxrpc_accept_incoming_calls(struct rxrpc_local *); int rxrpc_user_charge_accept(struct rxrpc_sock *, unsigned long); @@ -838,13 +825,15 @@ void rxrpc_send_ACK(struct rxrpc_call *, u8, rxrpc_serial_t, enum rxrpc_propose_ void rxrpc_propose_delay_ACK(struct rxrpc_call *, rxrpc_serial_t, enum rxrpc_propose_ack_trace); void rxrpc_shrink_call_tx_buffer(struct rxrpc_call *); -void rxrpc_process_call(struct work_struct *); +void rxrpc_resend(struct rxrpc_call *call, struct sk_buff *ack_skb); void rxrpc_reduce_call_timer(struct rxrpc_call *call, unsigned long expire_at, unsigned long now, enum rxrpc_timer_trace why); +void rxrpc_input_call_event(struct rxrpc_call *call, struct sk_buff *skb); + /* * call_object.c */ @@ -864,9 +853,8 @@ void rxrpc_incoming_call(struct rxrpc_sock *, struct rxrpc_call *, struct sk_buff *); void rxrpc_release_call(struct rxrpc_sock *, struct rxrpc_call *); void rxrpc_release_calls_on_socket(struct rxrpc_sock *); -void rxrpc_queue_call(struct rxrpc_call *, enum rxrpc_call_trace); void rxrpc_see_call(struct rxrpc_call *, enum rxrpc_call_trace); -bool rxrpc_try_get_call(struct rxrpc_call *, enum rxrpc_call_trace); +struct rxrpc_call *rxrpc_try_get_call(struct rxrpc_call *, enum rxrpc_call_trace); void rxrpc_get_call(struct rxrpc_call *, enum rxrpc_call_trace); void rxrpc_put_call(struct rxrpc_call *, enum rxrpc_call_trace); void rxrpc_cleanup_call(struct rxrpc_call *); @@ -908,6 +896,7 @@ void rxrpc_clean_up_local_conns(struct rxrpc_local *); */ void rxrpc_process_connection(struct work_struct *); void rxrpc_process_delayed_final_acks(struct rxrpc_connection *, bool); +int rxrpc_input_conn_packet(struct rxrpc_connection *conn, struct sk_buff *skb); /* * conn_object.c @@ -916,10 +905,9 @@ extern unsigned int rxrpc_connection_expiry; extern unsigned int rxrpc_closed_conn_expiry; struct rxrpc_connection *rxrpc_alloc_connection(struct rxrpc_net *, gfp_t); -struct rxrpc_connection *rxrpc_find_connection_rcu(struct rxrpc_local *, - struct sockaddr_rxrpc *, - struct sk_buff *, - struct rxrpc_peer **); +struct rxrpc_connection *rxrpc_find_client_connection_rcu(struct rxrpc_local *, + struct sockaddr_rxrpc *, + struct sk_buff *); void __rxrpc_disconnect_call(struct rxrpc_connection *, struct rxrpc_call *); void rxrpc_disconnect_call(struct rxrpc_call *); void rxrpc_kill_client_conn(struct rxrpc_connection *); @@ -962,8 +950,8 @@ void rxrpc_unpublish_service_conn(struct rxrpc_connection *); /* * input.c */ -void rxrpc_input_call_event(struct rxrpc_call *, struct sk_buff *); -void rxrpc_input_implicit_end_call(struct rxrpc_connection *, struct rxrpc_call *); +void rxrpc_input_call_packet(struct rxrpc_call *, struct sk_buff *); +void rxrpc_implicit_end_call(struct rxrpc_call *, struct sk_buff *); /* * io_thread.c @@ -993,7 +981,9 @@ int rxrpc_get_server_data_key(struct rxrpc_connection *, const void *, time64_t, /* * local_event.c */ -extern void rxrpc_process_local_events(struct rxrpc_local *); +void rxrpc_send_version_request(struct rxrpc_local *local, + struct rxrpc_host_header *hdr, + struct sk_buff *skb); /* * local_object.c @@ -1004,7 +994,6 @@ struct rxrpc_local *rxrpc_get_local_maybe(struct rxrpc_local *, enum rxrpc_local void rxrpc_put_local(struct rxrpc_local *, enum rxrpc_local_trace); struct rxrpc_local *rxrpc_use_local(struct rxrpc_local *, enum rxrpc_local_trace); void rxrpc_unuse_local(struct rxrpc_local *, enum rxrpc_local_trace); -void rxrpc_queue_local(struct rxrpc_local *); void rxrpc_destroy_local(struct rxrpc_local *local); void rxrpc_destroy_all_locals(struct rxrpc_net *); @@ -1068,7 +1057,7 @@ static inline struct rxrpc_net *rxrpc_net(struct net *net) void rxrpc_transmit_ack_packets(struct rxrpc_local *); int rxrpc_send_abort_packet(struct rxrpc_call *); int rxrpc_send_data_packet(struct rxrpc_call *, struct rxrpc_txbuf *); -void rxrpc_reject_packets(struct rxrpc_local *); +void rxrpc_reject_packet(struct rxrpc_local *local, struct sk_buff *skb); void rxrpc_send_keepalive(struct rxrpc_peer *); void rxrpc_transmit_one(struct rxrpc_call *call, struct rxrpc_txbuf *txb); @@ -1178,7 +1167,6 @@ int rxrpc_server_keyring(struct rxrpc_sock *, sockptr_t, int); * skbuff.c */ void rxrpc_kernel_data_consumed(struct rxrpc_call *, struct sk_buff *); -void rxrpc_packet_destructor(struct sk_buff *); void rxrpc_new_skb(struct sk_buff *, enum rxrpc_skb_trace); void rxrpc_see_skb(struct sk_buff *, enum rxrpc_skb_trace); void rxrpc_eaten_skb(struct sk_buff *, enum rxrpc_skb_trace); diff --git a/net/rxrpc/call_accept.c b/net/rxrpc/call_accept.c index 11134b7cec17..87b46c2a1985 100644 --- a/net/rxrpc/call_accept.c +++ b/net/rxrpc/call_accept.c @@ -100,6 +100,7 @@ static int rxrpc_service_prealloc_one(struct rxrpc_sock *rx, return -ENOMEM; call->flags |= (1 << RXRPC_CALL_IS_SERVICE); call->state = RXRPC_CALL_SERVER_PREALLOC; + __set_bit(RXRPC_CALL_EV_INITIAL_PING, &call->events); trace_rxrpc_call(call->debug_id, refcount_read(&call->ref), user_call_ID, rxrpc_call_new_prealloc_service); @@ -234,21 +235,6 @@ void rxrpc_discard_prealloc(struct rxrpc_sock *rx) kfree(b); } -/* - * Ping the other end to fill our RTT cache and to retrieve the rwind - * and MTU parameters. - */ -static void rxrpc_send_ping(struct rxrpc_call *call, struct sk_buff *skb) -{ - struct rxrpc_skb_priv *sp = rxrpc_skb(skb); - ktime_t now = skb->tstamp; - - if (call->peer->rtt_count < 3 || - ktime_before(ktime_add_ms(call->peer->rtt_last_req, 1000), now)) - rxrpc_send_ACK(call, RXRPC_ACK_PING, sp->hdr.serial, - rxrpc_propose_ack_ping_for_params); -} - /* * Allocate a new incoming call from the prealloc pool, along with a connection * and a peer as necessary. @@ -330,33 +316,56 @@ static struct rxrpc_call *rxrpc_alloc_incoming_call(struct rxrpc_sock *rx, } /* - * Set up a new incoming call. Called in BH context with the RCU read lock - * held. + * Set up a new incoming call. Called from the I/O thread. * * If this is for a kernel service, when we allocate the call, it will have * three refs on it: (1) the kernel service, (2) the user_call_ID tree, (3) the * retainer ref obtained from the backlog buffer. Prealloc calls for userspace - * services only have the ref from the backlog buffer. We pass this ref to the - * caller. + * services only have the ref from the backlog buffer. * * If we want to report an error, we mark the skb with the packet type and - * abort code and return NULL. - * - * The call is returned with the user access mutex held and a ref on it. + * abort code and return false. */ -struct rxrpc_call *rxrpc_new_incoming_call(struct rxrpc_local *local, - struct rxrpc_sock *rx, - struct sockaddr_rxrpc *peer_srx, - struct sk_buff *skb) +bool rxrpc_new_incoming_call(struct rxrpc_local *local, + struct rxrpc_peer *peer, + struct rxrpc_connection *conn, + struct sockaddr_rxrpc *peer_srx, + struct sk_buff *skb) { - struct rxrpc_skb_priv *sp = rxrpc_skb(skb); const struct rxrpc_security *sec = NULL; - struct rxrpc_connection *conn; - struct rxrpc_peer *peer = NULL; + struct rxrpc_skb_priv *sp = rxrpc_skb(skb); struct rxrpc_call *call = NULL; + struct rxrpc_sock *rx; _enter(""); + /* Don't set up a call for anything other than the first DATA packet. */ + if (sp->hdr.seq != 1 || + sp->hdr.type != RXRPC_PACKET_TYPE_DATA) + return true; /* Just discard */ + + rcu_read_lock(); + + /* Weed out packets to services we're not offering. Packets that would + * begin a call are explicitly rejected and the rest are just + * discarded. + */ + rx = rcu_dereference(local->service); + if (!rx || (sp->hdr.serviceId != rx->srx.srx_service && + sp->hdr.serviceId != rx->second_service) + ) { + if (sp->hdr.type == RXRPC_PACKET_TYPE_DATA && + sp->hdr.seq == 1) + goto unsupported_service; + goto discard; + } + + if (!conn) { + sec = rxrpc_get_incoming_security(rx, skb); + if (!sec) + goto reject; + } + spin_lock(&rx->incoming_lock); if (rx->sk.sk_state == RXRPC_SERVER_LISTEN_DISABLED || rx->sk.sk_state == RXRPC_CLOSE) { @@ -367,19 +376,6 @@ struct rxrpc_call *rxrpc_new_incoming_call(struct rxrpc_local *local, goto no_call; } - /* The peer, connection and call may all have sprung into existence due - * to a duplicate packet being handled on another CPU in parallel, so - * we have to recheck the routing. However, we're now holding - * rx->incoming_lock, so the values should remain stable. - */ - conn = rxrpc_find_connection_rcu(local, peer_srx, skb, &peer); - - if (!conn) { - sec = rxrpc_get_incoming_security(rx, skb); - if (!sec) - goto no_call; - } - call = rxrpc_alloc_incoming_call(rx, local, peer, conn, sec, peer_srx, skb); if (!call) { @@ -398,35 +394,15 @@ struct rxrpc_call *rxrpc_new_incoming_call(struct rxrpc_local *local, rx->notify_new_call(&rx->sk, call, call->user_call_ID); spin_lock(&conn->state_lock); - switch (conn->state) { - case RXRPC_CONN_SERVICE_UNSECURED: + if (conn->state == RXRPC_CONN_SERVICE_UNSECURED) { conn->state = RXRPC_CONN_SERVICE_CHALLENGING; set_bit(RXRPC_CONN_EV_CHALLENGE, &call->conn->events); rxrpc_queue_conn(call->conn, rxrpc_conn_queue_challenge); - break; - - case RXRPC_CONN_SERVICE: - write_lock(&call->state_lock); - if (call->state < RXRPC_CALL_COMPLETE) - call->state = RXRPC_CALL_SERVER_RECV_REQUEST; - write_unlock(&call->state_lock); - break; - - case RXRPC_CONN_REMOTELY_ABORTED: - rxrpc_set_call_completion(call, RXRPC_CALL_REMOTELY_ABORTED, - conn->abort_code, conn->error); - break; - case RXRPC_CONN_LOCALLY_ABORTED: - rxrpc_abort_call("CON", call, sp->hdr.seq, - conn->abort_code, conn->error); - break; - default: - BUG(); } spin_unlock(&conn->state_lock); - spin_unlock(&rx->incoming_lock); - rxrpc_send_ping(call, skb); + spin_unlock(&rx->incoming_lock); + rcu_read_unlock(); if (hlist_unhashed(&call->error_link)) { spin_lock(&call->peer->lock); @@ -435,12 +411,24 @@ struct rxrpc_call *rxrpc_new_incoming_call(struct rxrpc_local *local, } _leave(" = %p{%d}", call, call->debug_id); - return call; + rxrpc_input_call_event(call, skb); + rxrpc_put_call(call, rxrpc_call_put_input); + return true; +unsupported_service: + trace_rxrpc_abort(0, "INV", sp->hdr.cid, sp->hdr.callNumber, sp->hdr.seq, + RX_INVALID_OPERATION, EOPNOTSUPP); + skb->priority = RX_INVALID_OPERATION; + goto reject; no_call: spin_unlock(&rx->incoming_lock); - _leave(" = NULL [%u]", skb->mark); - return NULL; +reject: + rcu_read_unlock(); + _leave(" = f [%u]", skb->mark); + return false; +discard: + rcu_read_unlock(); + return true; } /* diff --git a/net/rxrpc/call_event.c b/net/rxrpc/call_event.c index c9f835292f7b..9db62fa55c62 100644 --- a/net/rxrpc/call_event.c +++ b/net/rxrpc/call_event.c @@ -74,11 +74,6 @@ void rxrpc_send_ACK(struct rxrpc_call *call, u8 ack_reason, if (test_bit(RXRPC_CALL_DISCONNECTED, &call->flags)) return; - if (ack_reason == RXRPC_ACK_DELAY && - test_and_set_bit(RXRPC_CALL_DELAY_ACK_PENDING, &call->flags)) { - trace_rxrpc_drop_ack(call, why, ack_reason, serial, false); - return; - } rxrpc_inc_stat(call->rxnet, stat_tx_acks[ack_reason]); @@ -111,12 +106,7 @@ void rxrpc_send_ACK(struct rxrpc_call *call, u8 ack_reason, spin_unlock_bh(&local->ack_tx_lock); trace_rxrpc_send_ack(call, why, ack_reason, serial); - if (!rcu_read_lock_held()) { - rxrpc_transmit_ack_packets(call->peer->local); - } else { - rxrpc_get_local(local, rxrpc_local_get_queue); - rxrpc_queue_local(local); - } + rxrpc_wake_up_io_thread(local); } /* @@ -130,11 +120,10 @@ static void rxrpc_congestion_timeout(struct rxrpc_call *call) /* * Perform retransmission of NAK'd and unack'd packets. */ -static void rxrpc_resend(struct rxrpc_call *call, unsigned long now_j) +void rxrpc_resend(struct rxrpc_call *call, struct sk_buff *ack_skb) { struct rxrpc_ackpacket *ack = NULL; struct rxrpc_txbuf *txb; - struct sk_buff *ack_skb = NULL; unsigned long resend_at; rxrpc_seq_t transmitted = READ_ONCE(call->tx_transmitted); ktime_t now, max_age, oldest, ack_ts; @@ -148,32 +137,21 @@ static void rxrpc_resend(struct rxrpc_call *call, unsigned long now_j) max_age = ktime_sub_us(now, jiffies_to_usecs(call->peer->rto_j)); oldest = now; - /* See if there's an ACK saved with a soft-ACK table in it. */ - if (call->acks_soft_tbl) { - spin_lock_bh(&call->acks_ack_lock); - ack_skb = call->acks_soft_tbl; - if (ack_skb) { - rxrpc_get_skb(ack_skb, rxrpc_skb_get_ack); - ack = (void *)ack_skb->data + sizeof(struct rxrpc_wire_header); - } - spin_unlock_bh(&call->acks_ack_lock); - } - if (list_empty(&call->tx_buffer)) goto no_resend; - spin_lock(&call->tx_lock); - if (list_empty(&call->tx_buffer)) goto no_further_resend; - trace_rxrpc_resend(call); + trace_rxrpc_resend(call, ack_skb); txb = list_first_entry(&call->tx_buffer, struct rxrpc_txbuf, call_link); /* Scan the soft ACK table without dropping the lock and resend any * explicitly NAK'd packets. */ - if (ack) { + if (ack_skb) { + ack = (void *)ack_skb->data + sizeof(struct rxrpc_wire_header); + for (i = 0; i < ack->nAcks; i++) { rxrpc_seq_t seq; @@ -197,7 +175,6 @@ static void rxrpc_resend(struct rxrpc_call *call, unsigned long now_j) rxrpc_see_txbuf(txb, rxrpc_txbuf_see_unacked); if (list_empty(&txb->tx_link)) { - rxrpc_get_txbuf(txb, rxrpc_txbuf_get_retrans); list_add_tail(&txb->tx_link, &retrans_queue); set_bit(RXRPC_TXBUF_RESENT, &txb->flags); } @@ -241,7 +218,6 @@ static void rxrpc_resend(struct rxrpc_call *call, unsigned long now_j) do_resend: unacked = true; if (list_empty(&txb->tx_link)) { - rxrpc_get_txbuf(txb, rxrpc_txbuf_get_retrans); list_add_tail(&txb->tx_link, &retrans_queue); set_bit(RXRPC_TXBUF_RESENT, &txb->flags); rxrpc_inc_stat(call->rxnet, stat_tx_data_retrans); @@ -249,10 +225,7 @@ static void rxrpc_resend(struct rxrpc_call *call, unsigned long now_j) } no_further_resend: - spin_unlock(&call->tx_lock); no_resend: - rxrpc_free_skb(ack_skb, rxrpc_skb_put_ack); - resend_at = nsecs_to_jiffies(ktime_to_ns(ktime_sub(now, oldest))); resend_at += jiffies + rxrpc_get_rto_backoff(call->peer, !list_empty(&retrans_queue)); @@ -266,7 +239,7 @@ no_resend: * retransmitting data. */ if (list_empty(&retrans_queue)) { - rxrpc_reduce_call_timer(call, resend_at, now_j, + rxrpc_reduce_call_timer(call, resend_at, jiffies, rxrpc_timer_set_for_resend); ack_ts = ktime_sub(now, call->acks_latest_ts); if (ktime_to_us(ack_ts) < (call->peer->srtt_us >> 3)) @@ -276,15 +249,11 @@ no_resend: goto out; } + /* Retransmit the queue */ while ((txb = list_first_entry_or_null(&retrans_queue, struct rxrpc_txbuf, tx_link))) { list_del_init(&txb->tx_link); - rxrpc_send_data_packet(call, txb); - rxrpc_put_txbuf(txb, rxrpc_txbuf_put_trans); - - trace_rxrpc_retransmit(call, txb->seq, - ktime_to_ns(ktime_sub(txb->last_sent, - max_age))); + rxrpc_transmit_one(call, txb); } out: @@ -357,16 +326,27 @@ static void rxrpc_transmit_some_data(struct rxrpc_call *call) } } +/* + * Ping the other end to fill our RTT cache and to retrieve the rwind + * and MTU parameters. + */ +static void rxrpc_send_initial_ping(struct rxrpc_call *call) +{ + if (call->peer->rtt_count < 3 || + ktime_before(ktime_add_ms(call->peer->rtt_last_req, 1000), + ktime_get_real())) + rxrpc_send_ACK(call, RXRPC_ACK_PING, 0, + rxrpc_propose_ack_ping_for_params); +} + /* * Handle retransmission and deferred ACK/abort generation. */ -void rxrpc_process_call(struct work_struct *work) +void rxrpc_input_call_event(struct rxrpc_call *call, struct sk_buff *skb) { - struct rxrpc_call *call = - container_of(work, struct rxrpc_call, processor); unsigned long now, next, t; - unsigned int iterations = 0; rxrpc_serial_t ackr_serial; + bool resend = false, expired = false; rxrpc_see_call(call, rxrpc_call_see_input); @@ -374,47 +354,31 @@ void rxrpc_process_call(struct work_struct *work) _enter("{%d,%s,%lx}", call->debug_id, rxrpc_call_states[call->state], call->events); -recheck_state: - if (call->acks_hard_ack != call->tx_bottom) - rxrpc_shrink_call_tx_buffer(call); - - /* Limit the number of times we do this before returning to the manager */ - if (!rxrpc_tx_window_has_space(call) || - list_empty(&call->tx_sendmsg)) { - iterations++; - if (iterations > 5) - goto requeue; - } - - if (test_and_clear_bit(RXRPC_CALL_EV_ABORT, &call->events)) { - rxrpc_send_abort_packet(call); - goto recheck_state; - } - - if (call->state == RXRPC_CALL_COMPLETE) { - del_timer_sync(&call->timer); + if (call->state == RXRPC_CALL_COMPLETE) goto out; - } - /* Work out if any timeouts tripped */ + if (skb && skb->mark == RXRPC_SKB_MARK_ERROR) + goto out; + + /* If we see our async-event poke, check for timeout trippage. */ now = jiffies; t = READ_ONCE(call->expect_rx_by); if (time_after_eq(now, t)) { trace_rxrpc_timer(call, rxrpc_timer_exp_normal, now); - set_bit(RXRPC_CALL_EV_EXPIRED, &call->events); + expired = true; } t = READ_ONCE(call->expect_req_by); if (call->state == RXRPC_CALL_SERVER_RECV_REQUEST && time_after_eq(now, t)) { trace_rxrpc_timer(call, rxrpc_timer_exp_idle, now); - set_bit(RXRPC_CALL_EV_EXPIRED, &call->events); + expired = true; } t = READ_ONCE(call->expect_term_by); if (time_after_eq(now, t)) { trace_rxrpc_timer(call, rxrpc_timer_exp_hard, now); - set_bit(RXRPC_CALL_EV_EXPIRED, &call->events); + expired = true; } t = READ_ONCE(call->delay_ack_at); @@ -453,13 +417,19 @@ recheck_state: if (time_after_eq(now, t)) { trace_rxrpc_timer(call, rxrpc_timer_exp_resend, now); cmpxchg(&call->resend_at, t, now + MAX_JIFFY_OFFSET); - set_bit(RXRPC_CALL_EV_RESEND, &call->events); + resend = true; } + if (skb) + rxrpc_input_call_packet(call, skb); + rxrpc_transmit_some_data(call); + if (test_and_clear_bit(RXRPC_CALL_EV_INITIAL_PING, &call->events)) + rxrpc_send_initial_ping(call); + /* Process events */ - if (test_and_clear_bit(RXRPC_CALL_EV_EXPIRED, &call->events)) { + if (expired) { if (test_bit(RXRPC_CALL_RX_HEARD, &call->flags) && (int)call->conn->hi_serial - (int)call->rx_serial > 0) { trace_rxrpc_call_reset(call); @@ -467,51 +437,50 @@ recheck_state: } else { rxrpc_abort_call("EXP", call, 0, RX_CALL_TIMEOUT, -ETIME); } - set_bit(RXRPC_CALL_EV_ABORT, &call->events); - goto recheck_state; + rxrpc_send_abort_packet(call); + goto out; } - if (test_and_clear_bit(RXRPC_CALL_EV_ACK_LOST, &call->events)) { - call->acks_lost_top = call->tx_top; + if (test_and_clear_bit(RXRPC_CALL_EV_ACK_LOST, &call->events)) rxrpc_send_ACK(call, RXRPC_ACK_PING, 0, rxrpc_propose_ack_ping_for_lost_ack); - } - if (test_and_clear_bit(RXRPC_CALL_EV_RESEND, &call->events) && - call->state != RXRPC_CALL_CLIENT_RECV_REPLY) { - rxrpc_resend(call, now); - goto recheck_state; - } + if (resend && call->state != RXRPC_CALL_CLIENT_RECV_REPLY) + rxrpc_resend(call, NULL); + + if (test_and_clear_bit(RXRPC_CALL_RX_IS_IDLE, &call->flags)) + rxrpc_send_ACK(call, RXRPC_ACK_IDLE, 0, + rxrpc_propose_ack_rx_idle); + + if (atomic_read(&call->ackr_nr_unacked) > 2) + rxrpc_send_ACK(call, RXRPC_ACK_IDLE, 0, + rxrpc_propose_ack_input_data); /* Make sure the timer is restarted */ - next = call->expect_rx_by; + if (call->state != RXRPC_CALL_COMPLETE) { + next = call->expect_rx_by; #define set(T) { t = READ_ONCE(T); if (time_before(t, next)) next = t; } - set(call->expect_req_by); - set(call->expect_term_by); - set(call->delay_ack_at); - set(call->ack_lost_at); - set(call->resend_at); - set(call->keepalive_at); - set(call->ping_at); + set(call->expect_req_by); + set(call->expect_term_by); + set(call->delay_ack_at); + set(call->ack_lost_at); + set(call->resend_at); + set(call->keepalive_at); + set(call->ping_at); - now = jiffies; - if (time_after_eq(now, next)) - goto recheck_state; + now = jiffies; + if (time_after_eq(now, next)) + rxrpc_poke_call(call, rxrpc_call_poke_timer_now); - rxrpc_reduce_call_timer(call, next, now, rxrpc_timer_restart); - - /* other events may have been raised since we started checking */ - if (call->events) - goto requeue; + rxrpc_reduce_call_timer(call, next, now, rxrpc_timer_restart); + } out: + if (call->state == RXRPC_CALL_COMPLETE) + del_timer_sync(&call->timer); + if (call->acks_hard_ack != call->tx_bottom) + rxrpc_shrink_call_tx_buffer(call); _leave(""); - return; - -requeue: - if (call->state < RXRPC_CALL_COMPLETE) - rxrpc_queue_call(call, rxrpc_call_queue_requeue); - goto out; } diff --git a/net/rxrpc/call_object.c b/net/rxrpc/call_object.c index 7570b4e67bc5..d441a715d988 100644 --- a/net/rxrpc/call_object.c +++ b/net/rxrpc/call_object.c @@ -71,7 +71,7 @@ static void rxrpc_call_timer_expired(struct timer_list *t) if (call->state < RXRPC_CALL_COMPLETE) { trace_rxrpc_timer_expired(call, jiffies); - rxrpc_queue_call(call, rxrpc_call_queue_timer); + rxrpc_poke_call(call, rxrpc_call_poke_timer); } } @@ -148,7 +148,6 @@ struct rxrpc_call *rxrpc_alloc_call(struct rxrpc_sock *rx, gfp_t gfp, &rxrpc_call_user_mutex_lock_class_key); timer_setup(&call->timer, rxrpc_call_timer_expired, 0); - INIT_WORK(&call->processor, rxrpc_process_call); INIT_WORK(&call->destroyer, rxrpc_destroy_call); INIT_LIST_HEAD(&call->link); INIT_LIST_HEAD(&call->chan_wait_link); @@ -163,7 +162,6 @@ struct rxrpc_call *rxrpc_alloc_call(struct rxrpc_sock *rx, gfp_t gfp, init_waitqueue_head(&call->waitq); spin_lock_init(&call->notify_lock); spin_lock_init(&call->tx_lock); - spin_lock_init(&call->acks_ack_lock); rwlock_init(&call->state_lock); refcount_set(&call->ref, 1); call->debug_id = debug_id; @@ -252,6 +250,7 @@ static void rxrpc_start_call_timer(struct rxrpc_call *call) call->ack_lost_at = j; call->resend_at = j; call->ping_at = j; + call->keepalive_at = j; call->expect_rx_by = j; call->expect_req_by = j; call->expect_term_by = j; @@ -430,6 +429,29 @@ void rxrpc_incoming_call(struct rxrpc_sock *rx, call->state = RXRPC_CALL_SERVER_SECURING; call->cong_tstamp = skb->tstamp; + spin_lock(&conn->state_lock); + + switch (conn->state) { + case RXRPC_CONN_SERVICE_UNSECURED: + case RXRPC_CONN_SERVICE_CHALLENGING: + call->state = RXRPC_CALL_SERVER_SECURING; + break; + case RXRPC_CONN_SERVICE: + call->state = RXRPC_CALL_SERVER_RECV_REQUEST; + break; + + case RXRPC_CONN_REMOTELY_ABORTED: + __rxrpc_set_call_completion(call, RXRPC_CALL_REMOTELY_ABORTED, + conn->abort_code, conn->error); + break; + case RXRPC_CONN_LOCALLY_ABORTED: + __rxrpc_abort_call("CON", call, 1, + conn->abort_code, conn->error); + break; + default: + BUG(); + } + /* Set the channel for this call. We don't get channel_lock as we're * only defending against the data_ready handler (which we're called * from) and the RESPONSE packet parser (which is only really @@ -440,6 +462,7 @@ void rxrpc_incoming_call(struct rxrpc_sock *rx, conn->channels[chan].call_counter = call->call_id; conn->channels[chan].call_id = call->call_id; rcu_assign_pointer(conn->channels[chan].call, call); + spin_unlock(&conn->state_lock); spin_lock(&conn->peer->lock); hlist_add_head(&call->error_link, &conn->peer->error_targets); @@ -449,15 +472,6 @@ void rxrpc_incoming_call(struct rxrpc_sock *rx, _leave(""); } -/* - * Queue a call's work processor. - */ -void rxrpc_queue_call(struct rxrpc_call *call, enum rxrpc_call_trace why) -{ - if (rxrpc_queue_work(&call->processor)) - trace_rxrpc_call(call->debug_id, refcount_read(&call->ref), 0, why); -} - /* * Note the re-emergence of a call. */ @@ -470,14 +484,15 @@ void rxrpc_see_call(struct rxrpc_call *call, enum rxrpc_call_trace why) } } -bool rxrpc_try_get_call(struct rxrpc_call *call, enum rxrpc_call_trace why) +struct rxrpc_call *rxrpc_try_get_call(struct rxrpc_call *call, + enum rxrpc_call_trace why) { int r; - if (!__refcount_inc_not_zero(&call->ref, &r)) - return false; + if (!call || !__refcount_inc_not_zero(&call->ref, &r)) + return NULL; trace_rxrpc_call(call->debug_id, r + 1, 0, why); - return true; + return call; } /* @@ -637,8 +652,6 @@ static void rxrpc_destroy_call(struct work_struct *work) struct rxrpc_call *call = container_of(work, struct rxrpc_call, destroyer); struct rxrpc_txbuf *txb; - del_timer_sync(&call->timer); - cancel_work_sync(&call->processor); /* The processor may restart the timer */ del_timer_sync(&call->timer); rxrpc_cleanup_ring(call); @@ -652,8 +665,8 @@ static void rxrpc_destroy_call(struct work_struct *work) list_del(&txb->call_link); rxrpc_put_txbuf(txb, rxrpc_txbuf_put_cleaned); } + rxrpc_put_txbuf(call->tx_pending, rxrpc_txbuf_put_cleaned); - rxrpc_free_skb(call->acks_soft_tbl, rxrpc_skb_put_ack); rxrpc_put_connection(call->conn, rxrpc_conn_put_call); rxrpc_put_peer(call->peer, rxrpc_peer_put_call); rxrpc_put_local(call->local, rxrpc_local_put_call); @@ -670,10 +683,9 @@ void rxrpc_cleanup_call(struct rxrpc_call *call) ASSERTCMP(call->state, ==, RXRPC_CALL_COMPLETE); ASSERT(test_bit(RXRPC_CALL_RELEASED, &call->flags)); - del_timer_sync(&call->timer); - cancel_work(&call->processor); + del_timer(&call->timer); - if (rcu_read_lock_held() || work_busy(&call->processor)) + if (rcu_read_lock_held()) /* Can't use the rxrpc workqueue as we need to cancel/flush * something that may be running/waiting there. */ diff --git a/net/rxrpc/conn_event.c b/net/rxrpc/conn_event.c index 23a74e35052d..643a56322224 100644 --- a/net/rxrpc/conn_event.c +++ b/net/rxrpc/conn_event.c @@ -479,3 +479,63 @@ void rxrpc_process_connection(struct work_struct *work) rxrpc_unuse_local(conn->local, rxrpc_local_unuse_conn_work); } } + +/* + * post connection-level events to the connection + * - this includes challenges, responses, some aborts and call terminal packet + * retransmission. + */ +static void rxrpc_post_packet_to_conn(struct rxrpc_connection *conn, + struct sk_buff *skb) +{ + _enter("%p,%p", conn, skb); + + rxrpc_get_skb(skb, rxrpc_skb_get_conn_work); + skb_queue_tail(&conn->rx_queue, skb); + rxrpc_queue_conn(conn, rxrpc_conn_queue_rx_work); +} + +/* + * Input a connection-level packet. + */ +int rxrpc_input_conn_packet(struct rxrpc_connection *conn, struct sk_buff *skb) +{ + struct rxrpc_skb_priv *sp = rxrpc_skb(skb); + + if (conn->state >= RXRPC_CONN_REMOTELY_ABORTED) { + _leave(" = -ECONNABORTED [%u]", conn->state); + return -ECONNABORTED; + } + + _enter("{%d},{%u,%%%u},", conn->debug_id, sp->hdr.type, sp->hdr.serial); + + switch (sp->hdr.type) { + case RXRPC_PACKET_TYPE_DATA: + case RXRPC_PACKET_TYPE_ACK: + rxrpc_conn_retransmit_call(conn, skb, + sp->hdr.cid & RXRPC_CHANNELMASK); + return 0; + + case RXRPC_PACKET_TYPE_BUSY: + /* Just ignore BUSY packets for now. */ + return 0; + + case RXRPC_PACKET_TYPE_ABORT: + conn->error = -ECONNABORTED; + conn->abort_code = skb->priority; + conn->state = RXRPC_CONN_REMOTELY_ABORTED; + set_bit(RXRPC_CONN_DONT_REUSE, &conn->flags); + rxrpc_abort_calls(conn, RXRPC_CALL_REMOTELY_ABORTED, sp->hdr.serial); + return -ECONNABORTED; + + case RXRPC_PACKET_TYPE_CHALLENGE: + case RXRPC_PACKET_TYPE_RESPONSE: + rxrpc_post_packet_to_conn(conn, skb); + return 0; + + default: + trace_rxrpc_rx_eproto(NULL, sp->hdr.serial, + tracepoint_string("bad_conn_pkt")); + return -EPROTO; + } +} diff --git a/net/rxrpc/conn_object.c b/net/rxrpc/conn_object.c index 98e49646ca1d..3c8f83dacb2b 100644 --- a/net/rxrpc/conn_object.c +++ b/net/rxrpc/conn_object.c @@ -72,76 +72,55 @@ struct rxrpc_connection *rxrpc_alloc_connection(struct rxrpc_net *rxnet, * * The caller must be holding the RCU read lock. */ -struct rxrpc_connection *rxrpc_find_connection_rcu(struct rxrpc_local *local, - struct sockaddr_rxrpc *srx, - struct sk_buff *skb, - struct rxrpc_peer **_peer) +struct rxrpc_connection *rxrpc_find_client_connection_rcu(struct rxrpc_local *local, + struct sockaddr_rxrpc *srx, + struct sk_buff *skb) { struct rxrpc_connection *conn; - struct rxrpc_conn_proto k; struct rxrpc_skb_priv *sp = rxrpc_skb(skb); struct rxrpc_peer *peer; _enter(",%x", sp->hdr.cid & RXRPC_CIDMASK); - k.epoch = sp->hdr.epoch; - k.cid = sp->hdr.cid & RXRPC_CIDMASK; - - if (rxrpc_to_server(sp)) { - /* We need to look up service connections by the full protocol - * parameter set. We look up the peer first as an intermediate - * step and then the connection from the peer's tree. - */ - peer = rxrpc_lookup_peer_rcu(local, srx); - if (!peer) - goto not_found; - *_peer = peer; - conn = rxrpc_find_service_conn_rcu(peer, skb); - if (!conn || refcount_read(&conn->ref) == 0) - goto not_found; - _leave(" = %p", conn); - return conn; - } else { - /* Look up client connections by connection ID alone as their - * IDs are unique for this machine. - */ - conn = idr_find(&rxrpc_client_conn_ids, sp->hdr.cid >> RXRPC_CIDSHIFT); - if (!conn || refcount_read(&conn->ref) == 0) { - _debug("no conn"); - goto not_found; - } - - if (conn->proto.epoch != k.epoch || - conn->local != local) - goto not_found; - - peer = conn->peer; - switch (srx->transport.family) { - case AF_INET: - if (peer->srx.transport.sin.sin_port != - srx->transport.sin.sin_port || - peer->srx.transport.sin.sin_addr.s_addr != - srx->transport.sin.sin_addr.s_addr) - goto not_found; - break; -#ifdef CONFIG_AF_RXRPC_IPV6 - case AF_INET6: - if (peer->srx.transport.sin6.sin6_port != - srx->transport.sin6.sin6_port || - memcmp(&peer->srx.transport.sin6.sin6_addr, - &srx->transport.sin6.sin6_addr, - sizeof(struct in6_addr)) != 0) - goto not_found; - break; -#endif - default: - BUG(); - } - - _leave(" = %p", conn); - return conn; + /* Look up client connections by connection ID alone as their IDs are + * unique for this machine. + */ + conn = idr_find(&rxrpc_client_conn_ids, sp->hdr.cid >> RXRPC_CIDSHIFT); + if (!conn || refcount_read(&conn->ref) == 0) { + _debug("no conn"); + goto not_found; } + if (conn->proto.epoch != sp->hdr.epoch || + conn->local != local) + goto not_found; + + peer = conn->peer; + switch (srx->transport.family) { + case AF_INET: + if (peer->srx.transport.sin.sin_port != + srx->transport.sin.sin_port || + peer->srx.transport.sin.sin_addr.s_addr != + srx->transport.sin.sin_addr.s_addr) + goto not_found; + break; +#ifdef CONFIG_AF_RXRPC_IPV6 + case AF_INET6: + if (peer->srx.transport.sin6.sin6_port != + srx->transport.sin6.sin6_port || + memcmp(&peer->srx.transport.sin6.sin6_addr, + &srx->transport.sin6.sin6_addr, + sizeof(struct in6_addr)) != 0) + goto not_found; + break; +#endif + default: + BUG(); + } + + _leave(" = %p", conn); + return conn; + not_found: _leave(" = NULL"); return NULL; diff --git a/net/rxrpc/input.c b/net/rxrpc/input.c index 01d32f817a7a..7ae7046f0b03 100644 --- a/net/rxrpc/input.c +++ b/net/rxrpc/input.c @@ -12,10 +12,8 @@ static void rxrpc_proto_abort(const char *why, struct rxrpc_call *call, rxrpc_seq_t seq) { - if (rxrpc_abort_call(why, call, seq, RX_PROTOCOL_ERROR, -EBADMSG)) { - set_bit(RXRPC_CALL_EV_ABORT, &call->events); - rxrpc_queue_call(call, rxrpc_call_queue_abort); - } + if (rxrpc_abort_call(why, call, seq, RX_PROTOCOL_ERROR, -EBADMSG)) + rxrpc_send_abort_packet(call); } /* @@ -174,8 +172,8 @@ out_no_clear_ca: call->cong_cwnd = cwnd; call->cong_cumul_acks = cumulative_acks; trace_rxrpc_congest(call, summary, acked_serial, change); - if (resend && !test_and_set_bit(RXRPC_CALL_EV_RESEND, &call->events)) - rxrpc_queue_call(call, rxrpc_call_queue_resend); + if (resend) + rxrpc_resend(call, skb); return; packet_loss_detected: @@ -398,6 +396,8 @@ static void rxrpc_input_data_one(struct rxrpc_call *call, struct sk_buff *skb, /* Send an immediate ACK if we fill in a hole */ else if (!skb_queue_empty(&call->rx_oos_queue)) ack_reason = RXRPC_ACK_DELAY; + else + atomic_inc_return(&call->ackr_nr_unacked); window++; if (after(window, wtop)) @@ -473,14 +473,6 @@ static void rxrpc_input_data_one(struct rxrpc_call *call, struct sk_buff *skb, } send_ack: - if (ack_reason < 0 && - atomic_inc_return(&call->ackr_nr_unacked) > 2 && - test_and_set_bit(RXRPC_CALL_IDLE_ACK_PENDING, &call->flags)) { - ack_reason = RXRPC_ACK_IDLE; - } else if (ack_reason >= 0) { - set_bit(RXRPC_CALL_IDLE_ACK_PENDING, &call->flags); - } - if (ack_reason >= 0) rxrpc_send_ACK(call, ack_reason, serial, rxrpc_propose_ack_input_data); @@ -510,7 +502,7 @@ static bool rxrpc_input_split_jumbo(struct rxrpc_call *call, struct sk_buff *skb &jhdr, sizeof(jhdr)) < 0) goto protocol_error; - jskb = skb_clone(skb, GFP_ATOMIC); + jskb = skb_clone(skb, GFP_NOFS); if (!jskb) { kdebug("couldn't clone"); return false; @@ -562,24 +554,6 @@ static void rxrpc_input_data(struct rxrpc_call *call, struct sk_buff *skb) if (state >= RXRPC_CALL_COMPLETE) return; - /* Unshare the packet so that it can be modified for in-place - * decryption. - */ - if (sp->hdr.securityIndex != 0) { - struct sk_buff *nskb = skb_unshare(skb, GFP_ATOMIC); - if (!nskb) { - rxrpc_eaten_skb(skb, rxrpc_skb_eaten_by_unshare_nomem); - return; - } - - if (nskb != skb) { - rxrpc_eaten_skb(skb, rxrpc_skb_eaten_by_unshare); - skb = nskb; - rxrpc_new_skb(skb, rxrpc_skb_new_unshared); - sp = rxrpc_skb(skb); - } - } - if (state == RXRPC_CALL_SERVER_RECV_REQUEST) { unsigned long timo = READ_ONCE(call->next_req_timo); unsigned long now, expect_req_by; @@ -599,15 +573,15 @@ static void rxrpc_input_data(struct rxrpc_call *call, struct sk_buff *skb) if ((state == RXRPC_CALL_CLIENT_SEND_REQUEST || state == RXRPC_CALL_CLIENT_AWAIT_REPLY) && !rxrpc_receiving_reply(call)) - goto out; + goto out_notify; if (!rxrpc_input_split_jumbo(call, skb)) { rxrpc_proto_abort("VLD", call, sp->hdr.seq); - goto out; + goto out_notify; } skb = NULL; -out: +out_notify: trace_rxrpc_notify_socket(call->debug_id, serial); rxrpc_notify_socket(call); _leave(" [queued]"); @@ -667,32 +641,6 @@ static void rxrpc_complete_rtt_probe(struct rxrpc_call *call, trace_rxrpc_rtt_rx(call, rxrpc_rtt_rx_lost, 9, 0, acked_serial, 0, 0); } -/* - * Process the response to a ping that we sent to find out if we lost an ACK. - * - * If we got back a ping response that indicates a lower tx_top than what we - * had at the time of the ping transmission, we adjudge all the DATA packets - * sent between the response tx_top and the ping-time tx_top to have been lost. - */ -static void rxrpc_input_check_for_lost_ack(struct rxrpc_call *call) -{ - if (after(call->acks_lost_top, call->acks_prev_seq) && - !test_and_set_bit(RXRPC_CALL_EV_RESEND, &call->events)) - rxrpc_queue_call(call, rxrpc_call_queue_resend); -} - -/* - * Process a ping response. - */ -static void rxrpc_input_ping_response(struct rxrpc_call *call, - ktime_t resp_time, - rxrpc_serial_t acked_serial, - rxrpc_serial_t ack_serial) -{ - if (acked_serial == call->acks_lost_ping) - rxrpc_input_check_for_lost_ack(call); -} - /* * Process the extra information that may be appended to an ACK packet */ @@ -801,7 +749,6 @@ static void rxrpc_input_ack(struct rxrpc_call *call, struct sk_buff *skb) struct rxrpc_ackpacket ack; struct rxrpc_skb_priv *sp = rxrpc_skb(skb); struct rxrpc_ackinfo info; - struct sk_buff *skb_old = NULL; rxrpc_serial_t ack_serial, acked_serial; rxrpc_seq_t first_soft_ack, hard_ack, prev_pkt; int nr_acks, offset, ioffset; @@ -809,10 +756,8 @@ static void rxrpc_input_ack(struct rxrpc_call *call, struct sk_buff *skb) _enter(""); offset = sizeof(struct rxrpc_wire_header); - if (skb_copy_bits(skb, offset, &ack, sizeof(ack)) < 0) { - rxrpc_proto_abort("XAK", call, 0); - goto out; - } + if (skb_copy_bits(skb, offset, &ack, sizeof(ack)) < 0) + return rxrpc_proto_abort("XAK", call, 0); offset += sizeof(ack); ack_serial = sp->hdr.serial; @@ -863,7 +808,7 @@ static void rxrpc_input_ack(struct rxrpc_call *call, struct sk_buff *skb) rxrpc_is_client_call(call)) { rxrpc_set_call_completion(call, RXRPC_CALL_REMOTELY_ABORTED, 0, -ENETRESET); - goto out; + return; } /* If we get an OUT_OF_SEQUENCE ACK from the server, that can also @@ -877,7 +822,7 @@ static void rxrpc_input_ack(struct rxrpc_call *call, struct sk_buff *skb) rxrpc_is_client_call(call)) { rxrpc_set_call_completion(call, RXRPC_CALL_REMOTELY_ABORTED, 0, -ENETRESET); - goto out; + return; } /* Discard any out-of-order or duplicate ACKs (outside lock). */ @@ -885,39 +830,25 @@ static void rxrpc_input_ack(struct rxrpc_call *call, struct sk_buff *skb) trace_rxrpc_rx_discard_ack(call->debug_id, ack_serial, first_soft_ack, call->acks_first_seq, prev_pkt, call->acks_prev_seq); - goto out; + return; } info.rxMTU = 0; ioffset = offset + nr_acks + 3; if (skb->len >= ioffset + sizeof(info) && - skb_copy_bits(skb, ioffset, &info, sizeof(info)) < 0) { - rxrpc_proto_abort("XAI", call, 0); - goto out; - } + skb_copy_bits(skb, ioffset, &info, sizeof(info)) < 0) + return rxrpc_proto_abort("XAI", call, 0); if (nr_acks > 0) skb_condense(skb); - /* Discard any out-of-order or duplicate ACKs (inside lock). */ - if (!rxrpc_is_ack_valid(call, first_soft_ack, prev_pkt)) { - trace_rxrpc_rx_discard_ack(call->debug_id, ack_serial, - first_soft_ack, call->acks_first_seq, - prev_pkt, call->acks_prev_seq); - goto out; - } call->acks_latest_ts = skb->tstamp; - call->acks_first_seq = first_soft_ack; call->acks_prev_seq = prev_pkt; switch (ack.reason) { case RXRPC_ACK_PING: break; - case RXRPC_ACK_PING_RESPONSE: - rxrpc_input_ping_response(call, skb->tstamp, acked_serial, - ack_serial); - fallthrough; default: if (after(acked_serial, call->acks_highest_serial)) call->acks_highest_serial = acked_serial; @@ -928,10 +859,8 @@ static void rxrpc_input_ack(struct rxrpc_call *call, struct sk_buff *skb) if (info.rxMTU) rxrpc_input_ackinfo(call, skb, &info); - if (first_soft_ack == 0) { - rxrpc_proto_abort("AK0", call, 0); - goto out; - } + if (first_soft_ack == 0) + return rxrpc_proto_abort("AK0", call, 0); /* Ignore ACKs unless we are or have just been transmitting. */ switch (READ_ONCE(call->state)) { @@ -941,45 +870,27 @@ static void rxrpc_input_ack(struct rxrpc_call *call, struct sk_buff *skb) case RXRPC_CALL_SERVER_AWAIT_ACK: break; default: - goto out; + return; } if (before(hard_ack, call->acks_hard_ack) || - after(hard_ack, call->tx_top)) { - rxrpc_proto_abort("AKW", call, 0); - goto out; - } - if (nr_acks > call->tx_top - hard_ack) { - rxrpc_proto_abort("AKN", call, 0); - goto out; - } + after(hard_ack, call->tx_top)) + return rxrpc_proto_abort("AKW", call, 0); + if (nr_acks > call->tx_top - hard_ack) + return rxrpc_proto_abort("AKN", call, 0); if (after(hard_ack, call->acks_hard_ack)) { if (rxrpc_rotate_tx_window(call, hard_ack, &summary)) { rxrpc_end_tx_phase(call, false, "ETA"); - goto out; + return; } } if (nr_acks > 0) { - if (offset > (int)skb->len - nr_acks) { - rxrpc_proto_abort("XSA", call, 0); - goto out; - } - - rxrpc_get_skb(skb, rxrpc_skb_get_ack); - spin_lock(&call->acks_ack_lock); - skb_old = call->acks_soft_tbl; - call->acks_soft_tbl = skb; - spin_unlock(&call->acks_ack_lock); - + if (offset > (int)skb->len - nr_acks) + return rxrpc_proto_abort("XSA", call, 0); rxrpc_input_soft_acks(call, skb->data + offset, first_soft_ack, nr_acks, &summary); - } else if (call->acks_soft_tbl) { - spin_lock(&call->acks_ack_lock); - skb_old = call->acks_soft_tbl; - call->acks_soft_tbl = NULL; - spin_unlock(&call->acks_ack_lock); } if (test_bit(RXRPC_CALL_TX_LAST, &call->flags) && @@ -989,8 +900,6 @@ static void rxrpc_input_ack(struct rxrpc_call *call, struct sk_buff *skb) rxrpc_propose_ack_ping_for_lost_reply); rxrpc_congestion_management(call, skb, &summary, acked_serial); -out: - rxrpc_free_skb(skb_old, rxrpc_skb_put_ack); } /* @@ -1020,13 +929,20 @@ static void rxrpc_input_abort(struct rxrpc_call *call, struct sk_buff *skb) /* * Process an incoming call packet. */ -void rxrpc_input_call_event(struct rxrpc_call *call, struct sk_buff *skb) +void rxrpc_input_call_packet(struct rxrpc_call *call, struct sk_buff *skb) { struct rxrpc_skb_priv *sp = rxrpc_skb(skb); unsigned long timo; _enter("%p,%p", call, skb); + if (sp->hdr.serviceId != call->dest_srx.srx_service) + call->dest_srx.srx_service = sp->hdr.serviceId; + if ((int)sp->hdr.serial - (int)call->rx_serial > 0) + call->rx_serial = sp->hdr.serial; + if (!test_bit(RXRPC_CALL_RX_HEARD, &call->flags)) + set_bit(RXRPC_CALL_RX_HEARD, &call->flags); + timo = READ_ONCE(call->next_rx_timo); if (timo) { unsigned long now = jiffies, expect_rx_by; @@ -1072,9 +988,10 @@ void rxrpc_input_call_event(struct rxrpc_call *call, struct sk_buff *skb) * * TODO: If callNumber > call_id + 1, renegotiate security. */ -void rxrpc_input_implicit_end_call(struct rxrpc_connection *conn, - struct rxrpc_call *call) +void rxrpc_implicit_end_call(struct rxrpc_call *call, struct sk_buff *skb) { + struct rxrpc_connection *conn = call->conn; + switch (READ_ONCE(call->state)) { case RXRPC_CALL_SERVER_AWAIT_ACK: rxrpc_call_completed(call); @@ -1082,14 +999,14 @@ void rxrpc_input_implicit_end_call(struct rxrpc_connection *conn, case RXRPC_CALL_COMPLETE: break; default: - if (rxrpc_abort_call("IMP", call, 0, RX_CALL_DEAD, -ESHUTDOWN)) { - set_bit(RXRPC_CALL_EV_ABORT, &call->events); - rxrpc_queue_call(call, rxrpc_call_queue_abort); - } + if (rxrpc_abort_call("IMP", call, 0, RX_CALL_DEAD, -ESHUTDOWN)) + rxrpc_send_abort_packet(call); trace_rxrpc_improper_term(call); break; } + rxrpc_input_call_event(call, skb); + spin_lock(&conn->bundle->channel_lock); __rxrpc_disconnect_call(conn, call); spin_unlock(&conn->bundle->channel_lock); diff --git a/net/rxrpc/io_thread.c b/net/rxrpc/io_thread.c index bc65d83fab88..19aa315eddf5 100644 --- a/net/rxrpc/io_thread.c +++ b/net/rxrpc/io_thread.c @@ -9,6 +9,10 @@ #include "ar-internal.h" +static int rxrpc_input_packet_on_conn(struct rxrpc_connection *conn, + struct sockaddr_rxrpc *peer_srx, + struct sk_buff *skb); + /* * handle data received on the local endpoint * - may be called in interrupt context @@ -63,45 +67,19 @@ void rxrpc_error_report(struct sock *sk) } /* - * post connection-level events to the connection - * - this includes challenges, responses, some aborts and call terminal packet - * retransmission. + * Process event packets targeted at a local endpoint. */ -static void rxrpc_post_packet_to_conn(struct rxrpc_connection *conn, - struct sk_buff *skb) +static void rxrpc_input_version(struct rxrpc_local *local, struct sk_buff *skb) { - _enter("%p,%p", conn, skb); + struct rxrpc_skb_priv *sp = rxrpc_skb(skb); + char v; - rxrpc_get_skb(skb, rxrpc_skb_get_conn_work); - skb_queue_tail(&conn->rx_queue, skb); - rxrpc_queue_conn(conn, rxrpc_conn_queue_rx_work); -} + _enter(""); -/* - * post endpoint-level events to the local endpoint - * - this includes debug and version messages - */ -static void rxrpc_post_packet_to_local(struct rxrpc_local *local, - struct sk_buff *skb) -{ - _enter("%p,%p", local, skb); - - if (rxrpc_get_local_maybe(local, rxrpc_local_get_queue)) { - rxrpc_get_skb(skb, rxrpc_skb_get_local_work); - skb_queue_tail(&local->event_queue, skb); - rxrpc_queue_local(local); - } -} - -/* - * put a packet up for transport-level abort - */ -static void rxrpc_reject_packet(struct rxrpc_local *local, struct sk_buff *skb) -{ - if (rxrpc_get_local_maybe(local, rxrpc_local_get_queue)) { - rxrpc_get_skb(skb, rxrpc_skb_get_reject_work); - skb_queue_tail(&local->reject_queue, skb); - rxrpc_queue_local(local); + rxrpc_see_skb(skb, rxrpc_skb_see_version); + if (skb_copy_bits(skb, sizeof(struct rxrpc_wire_header), &v, 1) >= 0) { + if (v == 0) + rxrpc_send_version_request(local, &sp->hdr, skb); } } @@ -156,22 +134,13 @@ static int rxrpc_input_packet(struct rxrpc_local *local, struct sk_buff **_skb) { struct rxrpc_connection *conn; struct sockaddr_rxrpc peer_srx; - struct rxrpc_channel *chan; - struct rxrpc_call *call = NULL; struct rxrpc_skb_priv *sp; struct rxrpc_peer *peer = NULL; - struct rxrpc_sock *rx = NULL; struct sk_buff *skb = *_skb; - unsigned int channel; - - if (skb->tstamp == 0) - skb->tstamp = ktime_get_real(); + int ret = 0; skb_pull(skb, sizeof(struct udphdr)); - /* The UDP protocol already released all skb resources; - * we are free to add our own data there. - */ sp = rxrpc_skb(skb); /* dig out the RxRPC connection details */ @@ -186,15 +155,13 @@ static int rxrpc_input_packet(struct rxrpc_local *local, struct sk_buff **_skb) } } - if (skb->tstamp == 0) - skb->tstamp = ktime_get_real(); trace_rxrpc_rx_packet(sp); switch (sp->hdr.type) { case RXRPC_PACKET_TYPE_VERSION: if (rxrpc_to_client(sp)) return 0; - rxrpc_post_packet_to_local(local, skb); + rxrpc_input_version(local, skb); return 0; case RXRPC_PACKET_TYPE_BUSY: @@ -259,7 +226,7 @@ static int rxrpc_input_packet(struct rxrpc_local *local, struct sk_buff **_skb) goto bad_message; if (WARN_ON_ONCE(rxrpc_extract_addr_from_skb(&peer_srx, skb) < 0)) - return 0; /* Unsupported address type - discard. */ + return true; /* Unsupported address type - discard. */ if (peer_srx.transport.family != local->srx.transport.family && (peer_srx.transport.family == AF_INET && @@ -267,171 +234,172 @@ static int rxrpc_input_packet(struct rxrpc_local *local, struct sk_buff **_skb) pr_warn_ratelimited("AF_RXRPC: Protocol mismatch %u not %u\n", peer_srx.transport.family, local->srx.transport.family); - return 0; /* Wrong address type - discard. */ + return true; /* Wrong address type - discard. */ + } + + if (rxrpc_to_client(sp)) { + rcu_read_lock(); + conn = rxrpc_find_client_connection_rcu(local, &peer_srx, skb); + conn = rxrpc_get_connection_maybe(conn, rxrpc_conn_get_call_input); + rcu_read_unlock(); + if (!conn) { + trace_rxrpc_abort(0, "NCC", sp->hdr.cid, + sp->hdr.callNumber, sp->hdr.seq, + RXKADINCONSISTENCY, EBADMSG); + goto protocol_error; + } + + ret = rxrpc_input_packet_on_conn(conn, &peer_srx, skb); + rxrpc_put_connection(conn, rxrpc_conn_put_call_input); + return ret; + } + + /* We need to look up service connections by the full protocol + * parameter set. We look up the peer first as an intermediate step + * and then the connection from the peer's tree. + */ + rcu_read_lock(); + + peer = rxrpc_lookup_peer_rcu(local, &peer_srx); + if (!peer) { + rcu_read_unlock(); + return rxrpc_new_incoming_call(local, NULL, NULL, &peer_srx, skb); + } + + conn = rxrpc_find_service_conn_rcu(peer, skb); + conn = rxrpc_get_connection_maybe(conn, rxrpc_conn_get_call_input); + if (conn) { + rcu_read_unlock(); + ret = rxrpc_input_packet_on_conn(conn, &peer_srx, skb); + rxrpc_put_connection(conn, rxrpc_conn_put_call_input); + return ret; + } + + peer = rxrpc_get_peer_maybe(peer, rxrpc_peer_get_input); + rcu_read_unlock(); + + ret = rxrpc_new_incoming_call(local, peer, NULL, &peer_srx, skb); + rxrpc_put_peer(peer, rxrpc_peer_put_input); + if (ret < 0) + goto reject_packet; + return 0; + +bad_message: + trace_rxrpc_abort(0, "BAD", sp->hdr.cid, sp->hdr.callNumber, sp->hdr.seq, + RX_PROTOCOL_ERROR, EBADMSG); +protocol_error: + skb->priority = RX_PROTOCOL_ERROR; + skb->mark = RXRPC_SKB_MARK_REJECT_ABORT; +reject_packet: + rxrpc_reject_packet(local, skb); + return ret; +} + +/* + * Deal with a packet that's associated with an extant connection. + */ +static int rxrpc_input_packet_on_conn(struct rxrpc_connection *conn, + struct sockaddr_rxrpc *peer_srx, + struct sk_buff *skb) +{ + struct rxrpc_skb_priv *sp = rxrpc_skb(skb); + struct rxrpc_channel *chan; + struct rxrpc_call *call = NULL; + unsigned int channel; + + if (sp->hdr.securityIndex != conn->security_ix) + goto wrong_security; + + if (sp->hdr.serviceId != conn->service_id) { + int old_id; + + if (!test_bit(RXRPC_CONN_PROBING_FOR_UPGRADE, &conn->flags)) + goto reupgrade; + old_id = cmpxchg(&conn->service_id, conn->orig_service_id, + sp->hdr.serviceId); + + if (old_id != conn->orig_service_id && + old_id != sp->hdr.serviceId) + goto reupgrade; + } + + if (after(sp->hdr.serial, conn->hi_serial)) + conn->hi_serial = sp->hdr.serial; + + /* It's a connection-level packet if the call number is 0. */ + if (sp->hdr.callNumber == 0) + return rxrpc_input_conn_packet(conn, skb); + + /* Call-bound packets are routed by connection channel. */ + channel = sp->hdr.cid & RXRPC_CHANNELMASK; + chan = &conn->channels[channel]; + + /* Ignore really old calls */ + if (sp->hdr.callNumber < chan->last_call) + return 0; + + if (sp->hdr.callNumber == chan->last_call) { + if (chan->call || + sp->hdr.type == RXRPC_PACKET_TYPE_ABORT) + return 0; + + /* For the previous service call, if completed successfully, we + * discard all further packets. + */ + if (rxrpc_conn_is_service(conn) && + chan->last_type == RXRPC_PACKET_TYPE_ACK) + return 0; + + /* But otherwise we need to retransmit the final packet from + * data cached in the connection record. + */ + if (sp->hdr.type == RXRPC_PACKET_TYPE_DATA) + trace_rxrpc_rx_data(chan->call_debug_id, + sp->hdr.seq, + sp->hdr.serial, + sp->hdr.flags); + rxrpc_input_conn_packet(conn, skb); + return 0; } rcu_read_lock(); + call = rxrpc_try_get_call(rcu_dereference(chan->call), + rxrpc_call_get_input); + rcu_read_unlock(); - if (rxrpc_to_server(sp)) { - /* Weed out packets to services we're not offering. Packets - * that would begin a call are explicitly rejected and the rest - * are just discarded. - */ - rx = rcu_dereference(local->service); - if (!rx || (sp->hdr.serviceId != rx->srx.srx_service && - sp->hdr.serviceId != rx->second_service) - ) { - rcu_read_unlock(); - if (sp->hdr.type == RXRPC_PACKET_TYPE_DATA && - sp->hdr.seq == 1) - goto unsupported_service; - return 0; + if (sp->hdr.callNumber > chan->call_id) { + if (rxrpc_to_client(sp)) { + rxrpc_put_call(call, rxrpc_call_put_input); + goto reject_packet; } - } - - conn = rxrpc_find_connection_rcu(local, &peer_srx, skb, &peer); - if (conn) { - if (sp->hdr.securityIndex != conn->security_ix) - goto wrong_security; - - if (sp->hdr.serviceId != conn->service_id) { - int old_id; - - if (!test_bit(RXRPC_CONN_PROBING_FOR_UPGRADE, &conn->flags)) - goto reupgrade; - old_id = cmpxchg(&conn->service_id, conn->orig_service_id, - sp->hdr.serviceId); - - if (old_id != conn->orig_service_id && - old_id != sp->hdr.serviceId) - goto reupgrade; - } - - if (sp->hdr.callNumber == 0) { - /* Connection-level packet */ - _debug("CONN %p {%d}", conn, conn->debug_id); - conn = rxrpc_get_connection_maybe(conn, rxrpc_conn_get_conn_input); - rcu_read_unlock(); - if (conn) { - rxrpc_post_packet_to_conn(conn, skb); - rxrpc_put_connection(conn, rxrpc_conn_put_conn_input); - } - return 0; - } - - if ((int)sp->hdr.serial - (int)conn->hi_serial > 0) - conn->hi_serial = sp->hdr.serial; - - /* Call-bound packets are routed by connection channel. */ - channel = sp->hdr.cid & RXRPC_CHANNELMASK; - chan = &conn->channels[channel]; - - /* Ignore really old calls */ - if (sp->hdr.callNumber < chan->last_call) { - rcu_read_unlock(); - return 0; - } - - if (sp->hdr.callNumber == chan->last_call) { - if (chan->call || - sp->hdr.type == RXRPC_PACKET_TYPE_ABORT) { - rcu_read_unlock(); - return 0; - } - - /* For the previous service call, if completed - * successfully, we discard all further packets. - */ - if (rxrpc_conn_is_service(conn) && - chan->last_type == RXRPC_PACKET_TYPE_ACK) { - rcu_read_unlock(); - return 0; - } - - /* But otherwise we need to retransmit the final packet - * from data cached in the connection record. - */ - if (sp->hdr.type == RXRPC_PACKET_TYPE_DATA) - trace_rxrpc_rx_data(chan->call_debug_id, - sp->hdr.seq, - sp->hdr.serial, - sp->hdr.flags); - conn = rxrpc_get_connection_maybe(conn, rxrpc_conn_get_call_input); - rcu_read_unlock(); - if (conn) { - rxrpc_post_packet_to_conn(conn, skb); - rxrpc_put_connection(conn, rxrpc_conn_put_call_input); - } - return 0; - } - - call = rcu_dereference(chan->call); - - if (sp->hdr.callNumber > chan->call_id) { - if (rxrpc_to_client(sp)) { - rcu_read_unlock(); - goto reject_packet; - } - if (call) { - rxrpc_input_implicit_end_call(conn, call); - chan->call = NULL; - call = NULL; - } - } - - if (call && !rxrpc_try_get_call(call, rxrpc_call_get_input)) - call = NULL; if (call) { - if (sp->hdr.serviceId != call->dest_srx.srx_service) - call->dest_srx.srx_service = sp->hdr.serviceId; - if ((int)sp->hdr.serial - (int)call->rx_serial > 0) - call->rx_serial = sp->hdr.serial; - if (!test_bit(RXRPC_CALL_RX_HEARD, &call->flags)) - set_bit(RXRPC_CALL_RX_HEARD, &call->flags); + rxrpc_implicit_end_call(call, skb); + rxrpc_put_call(call, rxrpc_call_put_input); + call = NULL; } } if (!call) { - if (rxrpc_to_client(sp) || - sp->hdr.type != RXRPC_PACKET_TYPE_DATA) { - rcu_read_unlock(); + if (rxrpc_to_client(sp)) goto bad_message; - } - if (sp->hdr.seq != 1) { - rcu_read_unlock(); + if (rxrpc_new_incoming_call(conn->local, conn->peer, conn, + peer_srx, skb)) return 0; - } - call = rxrpc_new_incoming_call(local, rx, &peer_srx, skb); - if (!call) { - rcu_read_unlock(); - goto reject_packet; - } + goto reject_packet; } - rcu_read_unlock(); - - /* Process a call packet. */ rxrpc_input_call_event(call, skb); rxrpc_put_call(call, rxrpc_call_put_input); - trace_rxrpc_rx_done(0, 0); return 0; wrong_security: - rcu_read_unlock(); trace_rxrpc_abort(0, "SEC", sp->hdr.cid, sp->hdr.callNumber, sp->hdr.seq, RXKADINCONSISTENCY, EBADMSG); skb->priority = RXKADINCONSISTENCY; goto post_abort; -unsupported_service: - trace_rxrpc_abort(0, "INV", sp->hdr.cid, sp->hdr.callNumber, sp->hdr.seq, - RX_INVALID_OPERATION, EOPNOTSUPP); - skb->priority = RX_INVALID_OPERATION; - goto post_abort; - reupgrade: - rcu_read_unlock(); trace_rxrpc_abort(0, "UPG", sp->hdr.cid, sp->hdr.callNumber, sp->hdr.seq, RX_PROTOCOL_ERROR, EBADMSG); goto protocol_error; @@ -444,7 +412,7 @@ protocol_error: post_abort: skb->mark = RXRPC_SKB_MARK_REJECT_ABORT; reject_packet: - rxrpc_reject_packet(local, skb); + rxrpc_reject_packet(conn->local, skb); return 0; } @@ -479,6 +447,11 @@ int rxrpc_io_thread(void *data) continue; } + if (!list_empty(&local->ack_tx_queue)) { + rxrpc_transmit_ack_packets(local); + continue; + } + /* Process received packets and errors. */ if ((skb = __skb_dequeue(&rx_queue))) { switch (skb->mark) { diff --git a/net/rxrpc/local_event.c b/net/rxrpc/local_event.c index c344383a20b2..5e69ea6b233d 100644 --- a/net/rxrpc/local_event.c +++ b/net/rxrpc/local_event.c @@ -21,9 +21,9 @@ static const char rxrpc_version_string[65] = "linux-" UTS_RELEASE " AF_RXRPC"; /* * Reply to a version request */ -static void rxrpc_send_version_request(struct rxrpc_local *local, - struct rxrpc_host_header *hdr, - struct sk_buff *skb) +void rxrpc_send_version_request(struct rxrpc_local *local, + struct rxrpc_host_header *hdr, + struct sk_buff *skb) { struct rxrpc_wire_header whdr; struct rxrpc_skb_priv *sp = rxrpc_skb(skb); @@ -73,40 +73,3 @@ static void rxrpc_send_version_request(struct rxrpc_local *local, _leave(""); } - -/* - * Process event packets targeted at a local endpoint. - */ -void rxrpc_process_local_events(struct rxrpc_local *local) -{ - struct sk_buff *skb; - char v; - - _enter(""); - - skb = skb_dequeue(&local->event_queue); - if (skb) { - struct rxrpc_skb_priv *sp = rxrpc_skb(skb); - - rxrpc_see_skb(skb, rxrpc_skb_see_local_work); - _debug("{%d},{%u}", local->debug_id, sp->hdr.type); - - switch (sp->hdr.type) { - case RXRPC_PACKET_TYPE_VERSION: - if (skb_copy_bits(skb, sizeof(struct rxrpc_wire_header), - &v, 1) < 0) - return; - if (v == 0) - rxrpc_send_version_request(local, &sp->hdr, skb); - break; - - default: - /* Just ignore anything we don't understand */ - break; - } - - rxrpc_free_skb(skb, rxrpc_skb_put_input); - } - - _leave(""); -} diff --git a/net/rxrpc/local_object.c b/net/rxrpc/local_object.c index 03f491cc23ef..c73a5a1bc088 100644 --- a/net/rxrpc/local_object.c +++ b/net/rxrpc/local_object.c @@ -20,7 +20,6 @@ #include #include "ar-internal.h" -static void rxrpc_local_processor(struct work_struct *); static void rxrpc_local_rcu(struct rcu_head *); /* @@ -97,12 +96,9 @@ static struct rxrpc_local *rxrpc_alloc_local(struct rxrpc_net *rxnet, atomic_set(&local->active_users, 1); local->rxnet = rxnet; INIT_HLIST_NODE(&local->link); - INIT_WORK(&local->processor, rxrpc_local_processor); INIT_LIST_HEAD(&local->ack_tx_queue); spin_lock_init(&local->ack_tx_lock); init_rwsem(&local->defrag_sem); - skb_queue_head_init(&local->reject_queue); - skb_queue_head_init(&local->event_queue); skb_queue_head_init(&local->rx_queue); INIT_LIST_HEAD(&local->call_attend_q); local->client_bundles = RB_ROOT; @@ -318,21 +314,6 @@ struct rxrpc_local *rxrpc_get_local_maybe(struct rxrpc_local *local, return NULL; } -/* - * Queue a local endpoint and pass the caller's reference to the work item. - */ -void rxrpc_queue_local(struct rxrpc_local *local) -{ - unsigned int debug_id = local->debug_id; - int r = refcount_read(&local->ref); - int u = atomic_read(&local->active_users); - - if (rxrpc_queue_work(&local->processor)) - trace_rxrpc_local(debug_id, rxrpc_local_queued, r, u); - else - rxrpc_put_local(local, rxrpc_local_put_already_queued); -} - /* * Drop a ref on a local endpoint. */ @@ -374,7 +355,7 @@ struct rxrpc_local *rxrpc_use_local(struct rxrpc_local *local, /* * Cease using a local endpoint. Once the number of active users reaches 0, we - * start the closure of the transport in the work processor. + * start the closure of the transport in the I/O thread.. */ void rxrpc_unuse_local(struct rxrpc_local *local, enum rxrpc_local_trace why) { @@ -416,52 +397,9 @@ void rxrpc_destroy_local(struct rxrpc_local *local) /* At this point, there should be no more packets coming in to the * local endpoint. */ - rxrpc_purge_queue(&local->reject_queue); - rxrpc_purge_queue(&local->event_queue); rxrpc_purge_queue(&local->rx_queue); } -/* - * Process events on an endpoint. The work item carries a ref which - * we must release. - */ -static void rxrpc_local_processor(struct work_struct *work) -{ - struct rxrpc_local *local = - container_of(work, struct rxrpc_local, processor); - bool again; - - if (local->dead) - return; - - rxrpc_see_local(local, rxrpc_local_processing); - - do { - again = false; - if (!__rxrpc_use_local(local, rxrpc_local_use_work)) - break; - - if (!list_empty(&local->ack_tx_queue)) { - rxrpc_transmit_ack_packets(local); - again = true; - } - - if (!skb_queue_empty(&local->reject_queue)) { - rxrpc_reject_packets(local); - again = true; - } - - if (!skb_queue_empty(&local->event_queue)) { - rxrpc_process_local_events(local); - again = true; - } - - __rxrpc_unuse_local(local, rxrpc_local_unuse_work); - } while (again); - - rxrpc_put_local(local, rxrpc_local_put_queue); -} - /* * Destroy a local endpoint after the RCU grace period expires. */ @@ -469,13 +407,8 @@ static void rxrpc_local_rcu(struct rcu_head *rcu) { struct rxrpc_local *local = container_of(rcu, struct rxrpc_local, rcu); - _enter("%d", local->debug_id); - - ASSERT(!work_pending(&local->processor)); - rxrpc_see_local(local, rxrpc_local_free); kfree(local); - _leave(""); } /* diff --git a/net/rxrpc/output.c b/net/rxrpc/output.c index 71963b4523be..2ea1fa1b8a6f 100644 --- a/net/rxrpc/output.c +++ b/net/rxrpc/output.c @@ -229,11 +229,6 @@ static int rxrpc_send_ack_packet(struct rxrpc_local *local, struct rxrpc_txbuf * if (txb->ack.reason == RXRPC_ACK_PING) txb->wire.flags |= RXRPC_REQUEST_ACK; - if (txb->ack.reason == RXRPC_ACK_DELAY) - clear_bit(RXRPC_CALL_DELAY_ACK_PENDING, &call->flags); - if (txb->ack.reason == RXRPC_ACK_IDLE) - clear_bit(RXRPC_CALL_IDLE_ACK_PENDING, &call->flags); - n = rxrpc_fill_out_ack(conn, call, txb); if (n == 0) return 0; @@ -247,8 +242,6 @@ static int rxrpc_send_ack_packet(struct rxrpc_local *local, struct rxrpc_txbuf * trace_rxrpc_tx_ack(call->debug_id, serial, ntohl(txb->ack.firstPacket), ntohl(txb->ack.serial), txb->ack.reason, txb->ack.nAcks); - if (txb->ack_why == rxrpc_propose_ack_ping_for_lost_ack) - call->acks_lost_ping = serial; if (txb->ack.reason == RXRPC_ACK_PING) rtt_slot = rxrpc_begin_rtt_probe(call, serial, rxrpc_rtt_tx_ping); @@ -588,21 +581,20 @@ send_fragmentable: } /* - * reject packets through the local endpoint + * Reject a packet through the local endpoint. */ -void rxrpc_reject_packets(struct rxrpc_local *local) +void rxrpc_reject_packet(struct rxrpc_local *local, struct sk_buff *skb) { - struct sockaddr_rxrpc srx; - struct rxrpc_skb_priv *sp; struct rxrpc_wire_header whdr; - struct sk_buff *skb; + struct sockaddr_rxrpc srx; + struct rxrpc_skb_priv *sp = rxrpc_skb(skb); struct msghdr msg; struct kvec iov[2]; size_t size; __be32 code; int ret, ioc; - _enter("%d", local->debug_id); + rxrpc_see_skb(skb, rxrpc_skb_see_reject); iov[0].iov_base = &whdr; iov[0].iov_len = sizeof(whdr); @@ -616,52 +608,42 @@ void rxrpc_reject_packets(struct rxrpc_local *local) memset(&whdr, 0, sizeof(whdr)); - while ((skb = skb_dequeue(&local->reject_queue))) { - rxrpc_see_skb(skb, rxrpc_skb_see_reject); - sp = rxrpc_skb(skb); - - switch (skb->mark) { - case RXRPC_SKB_MARK_REJECT_BUSY: - whdr.type = RXRPC_PACKET_TYPE_BUSY; - size = sizeof(whdr); - ioc = 1; - break; - case RXRPC_SKB_MARK_REJECT_ABORT: - whdr.type = RXRPC_PACKET_TYPE_ABORT; - code = htonl(skb->priority); - size = sizeof(whdr) + sizeof(code); - ioc = 2; - break; - default: - rxrpc_free_skb(skb, rxrpc_skb_put_input); - continue; - } - - if (rxrpc_extract_addr_from_skb(&srx, skb) == 0) { - msg.msg_namelen = srx.transport_len; - - whdr.epoch = htonl(sp->hdr.epoch); - whdr.cid = htonl(sp->hdr.cid); - whdr.callNumber = htonl(sp->hdr.callNumber); - whdr.serviceId = htons(sp->hdr.serviceId); - whdr.flags = sp->hdr.flags; - whdr.flags ^= RXRPC_CLIENT_INITIATED; - whdr.flags &= RXRPC_CLIENT_INITIATED; - - iov_iter_kvec(&msg.msg_iter, WRITE, iov, ioc, size); - ret = do_udp_sendmsg(local->socket, &msg, size); - if (ret < 0) - trace_rxrpc_tx_fail(local->debug_id, 0, ret, - rxrpc_tx_point_reject); - else - trace_rxrpc_tx_packet(local->debug_id, &whdr, - rxrpc_tx_point_reject); - } - - rxrpc_free_skb(skb, rxrpc_skb_put_input); + switch (skb->mark) { + case RXRPC_SKB_MARK_REJECT_BUSY: + whdr.type = RXRPC_PACKET_TYPE_BUSY; + size = sizeof(whdr); + ioc = 1; + break; + case RXRPC_SKB_MARK_REJECT_ABORT: + whdr.type = RXRPC_PACKET_TYPE_ABORT; + code = htonl(skb->priority); + size = sizeof(whdr) + sizeof(code); + ioc = 2; + break; + default: + return; } - _leave(""); + if (rxrpc_extract_addr_from_skb(&srx, skb) == 0) { + msg.msg_namelen = srx.transport_len; + + whdr.epoch = htonl(sp->hdr.epoch); + whdr.cid = htonl(sp->hdr.cid); + whdr.callNumber = htonl(sp->hdr.callNumber); + whdr.serviceId = htons(sp->hdr.serviceId); + whdr.flags = sp->hdr.flags; + whdr.flags ^= RXRPC_CLIENT_INITIATED; + whdr.flags &= RXRPC_CLIENT_INITIATED; + + iov_iter_kvec(&msg.msg_iter, WRITE, iov, ioc, size); + ret = do_udp_sendmsg(local->socket, &msg, size); + if (ret < 0) + trace_rxrpc_tx_fail(local->debug_id, 0, ret, + rxrpc_tx_point_reject); + else + trace_rxrpc_tx_packet(local->debug_id, &whdr, + rxrpc_tx_point_reject); + } } /* diff --git a/net/rxrpc/peer_event.c b/net/rxrpc/peer_event.c index 97d017ca3dc4..fb8096e93d2c 100644 --- a/net/rxrpc/peer_event.c +++ b/net/rxrpc/peer_event.c @@ -18,9 +18,9 @@ #include #include "ar-internal.h" -static void rxrpc_store_error(struct rxrpc_peer *, struct sock_exterr_skb *); -static void rxrpc_distribute_error(struct rxrpc_peer *, int, - enum rxrpc_call_completion); +static void rxrpc_store_error(struct rxrpc_peer *, struct sk_buff *); +static void rxrpc_distribute_error(struct rxrpc_peer *, struct sk_buff *, + enum rxrpc_call_completion, int); /* * Find the peer associated with a local error. @@ -161,7 +161,7 @@ void rxrpc_input_error(struct rxrpc_local *local, struct sk_buff *skb) goto out; } - rxrpc_store_error(peer, serr); + rxrpc_store_error(peer, skb); out: rxrpc_put_peer(peer, rxrpc_peer_put_input_error); } @@ -169,19 +169,15 @@ out: /* * Map an error report to error codes on the peer record. */ -static void rxrpc_store_error(struct rxrpc_peer *peer, - struct sock_exterr_skb *serr) +static void rxrpc_store_error(struct rxrpc_peer *peer, struct sk_buff *skb) { enum rxrpc_call_completion compl = RXRPC_CALL_NETWORK_ERROR; - struct sock_extended_err *ee; - int err; + struct sock_exterr_skb *serr = SKB_EXT_ERR(skb); + struct sock_extended_err *ee = &serr->ee; + int err = ee->ee_errno; _enter(""); - ee = &serr->ee; - - err = ee->ee_errno; - switch (ee->ee_origin) { case SO_EE_ORIGIN_NONE: case SO_EE_ORIGIN_LOCAL: @@ -197,14 +193,14 @@ static void rxrpc_store_error(struct rxrpc_peer *peer, break; } - rxrpc_distribute_error(peer, err, compl); + rxrpc_distribute_error(peer, skb, compl, err); } /* * Distribute an error that occurred on a peer. */ -static void rxrpc_distribute_error(struct rxrpc_peer *peer, int error, - enum rxrpc_call_completion compl) +static void rxrpc_distribute_error(struct rxrpc_peer *peer, struct sk_buff *skb, + enum rxrpc_call_completion compl, int err) { struct rxrpc_call *call; HLIST_HEAD(error_targets); @@ -219,7 +215,8 @@ static void rxrpc_distribute_error(struct rxrpc_peer *peer, int error, spin_unlock(&peer->lock); rxrpc_see_call(call, rxrpc_call_see_distribute_error); - rxrpc_set_call_completion(call, compl, 0, -error); + rxrpc_set_call_completion(call, compl, 0, -err); + rxrpc_input_call_event(call, skb); spin_lock(&peer->lock); } diff --git a/net/rxrpc/recvmsg.c b/net/rxrpc/recvmsg.c index 5df7f468abed..77d03b9e4c4c 100644 --- a/net/rxrpc/recvmsg.c +++ b/net/rxrpc/recvmsg.c @@ -253,11 +253,8 @@ static void rxrpc_rotate_rx_window(struct rxrpc_call *call) acked = atomic_add_return(call->rx_consumed - old_consumed, &call->ackr_nr_consumed); if (acked > 2 && - !test_and_set_bit(RXRPC_CALL_IDLE_ACK_PENDING, &call->flags)) { - rxrpc_send_ACK(call, RXRPC_ACK_IDLE, serial, - rxrpc_propose_ack_rotate_rx); - rxrpc_transmit_ack_packets(call->peer->local); - } + !test_and_set_bit(RXRPC_CALL_RX_IS_IDLE, &call->flags)) + rxrpc_poke_call(call, rxrpc_call_poke_idle); } /* @@ -377,7 +374,7 @@ done: trace_rxrpc_recvdata(call, rxrpc_recvmsg_data_return, seq, rx_pkt_offset, rx_pkt_len, ret); if (ret == -EAGAIN) - set_bit(RXRPC_CALL_RX_UNDERRUN, &call->flags); + set_bit(RXRPC_CALL_RX_IS_IDLE, &call->flags); return ret; } diff --git a/net/rxrpc/sendmsg.c b/net/rxrpc/sendmsg.c index 11af37275d5b..58e0a36f6aa9 100644 --- a/net/rxrpc/sendmsg.c +++ b/net/rxrpc/sendmsg.c @@ -170,7 +170,7 @@ static void rxrpc_queue_packet(struct rxrpc_sock *rx, struct rxrpc_call *call, { unsigned long now; rxrpc_seq_t seq = txb->seq; - bool last = test_bit(RXRPC_TXBUF_LAST, &txb->flags); + bool last = test_bit(RXRPC_TXBUF_LAST, &txb->flags), poke; rxrpc_inc_stat(call->rxnet, stat_tx_data); @@ -188,6 +188,7 @@ static void rxrpc_queue_packet(struct rxrpc_sock *rx, struct rxrpc_call *call, /* Add the packet to the call's output buffer */ spin_lock(&call->tx_lock); + poke = list_empty(&call->tx_sendmsg); list_add_tail(&txb->call_link, &call->tx_sendmsg); call->tx_prepared = seq; spin_unlock(&call->tx_lock); @@ -220,11 +221,8 @@ static void rxrpc_queue_packet(struct rxrpc_sock *rx, struct rxrpc_call *call, write_unlock_bh(&call->state_lock); } - - /* Stick the packet on the crypto queue or the transmission queue as - * appropriate. - */ - rxrpc_queue_call(call, rxrpc_call_queue_tx_data); + if (poke) + rxrpc_poke_call(call, rxrpc_call_poke_start); } /* From 3dd9c8b5f09fd24652729a3da5c5efa3ec2c4590 Mon Sep 17 00:00:00 2001 From: David Howells Date: Fri, 24 Jan 2020 10:21:15 +0000 Subject: [PATCH 1431/2002] rxrpc: Remove the _bh annotation from all the spinlocks None of the spinlocks in rxrpc need a _bh annotation now as the RCU callback routines no longer take spinlocks and the bulk of the packet wrangling code is now run in the I/O thread, not softirq context. Signed-off-by: David Howells cc: Marc Dionne cc: linux-afs@lists.infradead.org --- net/rxrpc/af_rxrpc.c | 4 ++-- net/rxrpc/call_accept.c | 8 ++++---- net/rxrpc/call_event.c | 4 ++-- net/rxrpc/call_object.c | 20 ++++++++++---------- net/rxrpc/conn_client.c | 4 ++-- net/rxrpc/conn_event.c | 16 ++++++++-------- net/rxrpc/conn_service.c | 10 +++++----- net/rxrpc/input.c | 4 ++-- net/rxrpc/output.c | 8 ++++---- net/rxrpc/peer_event.c | 16 ++++++++-------- net/rxrpc/peer_object.c | 8 ++++---- net/rxrpc/recvmsg.c | 36 ++++++++++++++++++------------------ net/rxrpc/sendmsg.c | 12 ++++++------ 13 files changed, 75 insertions(+), 75 deletions(-) diff --git a/net/rxrpc/af_rxrpc.c b/net/rxrpc/af_rxrpc.c index 8ad4d85acb0b..7ea576f6ba4b 100644 --- a/net/rxrpc/af_rxrpc.c +++ b/net/rxrpc/af_rxrpc.c @@ -359,9 +359,9 @@ void rxrpc_kernel_end_call(struct socket *sock, struct rxrpc_call *call) /* Make sure we're not going to call back into a kernel service */ if (call->notify_rx) { - spin_lock_bh(&call->notify_lock); + spin_lock(&call->notify_lock); call->notify_rx = rxrpc_dummy_notify_rx; - spin_unlock_bh(&call->notify_lock); + spin_unlock(&call->notify_lock); } mutex_unlock(&call->user_mutex); diff --git a/net/rxrpc/call_accept.c b/net/rxrpc/call_accept.c index 87b46c2a1985..d1850863507f 100644 --- a/net/rxrpc/call_accept.c +++ b/net/rxrpc/call_accept.c @@ -138,9 +138,9 @@ static int rxrpc_service_prealloc_one(struct rxrpc_sock *rx, write_unlock(&rx->call_lock); rxnet = call->rxnet; - spin_lock_bh(&rxnet->call_lock); + spin_lock(&rxnet->call_lock); list_add_tail_rcu(&call->link, &rxnet->calls); - spin_unlock_bh(&rxnet->call_lock); + spin_unlock(&rxnet->call_lock); b->call_backlog[call_head] = call; smp_store_release(&b->call_backlog_head, (call_head + 1) & (size - 1)); @@ -188,8 +188,8 @@ void rxrpc_discard_prealloc(struct rxrpc_sock *rx) /* Make sure that there aren't any incoming calls in progress before we * clear the preallocation buffers. */ - spin_lock_bh(&rx->incoming_lock); - spin_unlock_bh(&rx->incoming_lock); + spin_lock(&rx->incoming_lock); + spin_unlock(&rx->incoming_lock); head = b->peer_backlog_head; tail = b->peer_backlog_tail; diff --git a/net/rxrpc/call_event.c b/net/rxrpc/call_event.c index 9db62fa55c62..18591f9ecc6a 100644 --- a/net/rxrpc/call_event.c +++ b/net/rxrpc/call_event.c @@ -101,9 +101,9 @@ void rxrpc_send_ACK(struct rxrpc_call *call, u8 ack_reason, return; } - spin_lock_bh(&local->ack_tx_lock); + spin_lock(&local->ack_tx_lock); list_add_tail(&txb->tx_link, &local->ack_tx_queue); - spin_unlock_bh(&local->ack_tx_lock); + spin_unlock(&local->ack_tx_lock); trace_rxrpc_send_ack(call, why, ack_reason, serial); rxrpc_wake_up_io_thread(local); diff --git a/net/rxrpc/call_object.c b/net/rxrpc/call_object.c index d441a715d988..be5eb8cdf549 100644 --- a/net/rxrpc/call_object.c +++ b/net/rxrpc/call_object.c @@ -354,9 +354,9 @@ struct rxrpc_call *rxrpc_new_client_call(struct rxrpc_sock *rx, write_unlock(&rx->call_lock); rxnet = call->rxnet; - spin_lock_bh(&rxnet->call_lock); + spin_lock(&rxnet->call_lock); list_add_tail_rcu(&call->link, &rxnet->calls); - spin_unlock_bh(&rxnet->call_lock); + spin_unlock(&rxnet->call_lock); /* From this point on, the call is protected by its own lock. */ release_sock(&rx->sk); @@ -537,7 +537,7 @@ void rxrpc_release_call(struct rxrpc_sock *rx, struct rxrpc_call *call) del_timer_sync(&call->timer); /* Make sure we don't get any more notifications */ - write_lock_bh(&rx->recvmsg_lock); + write_lock(&rx->recvmsg_lock); if (!list_empty(&call->recvmsg_link)) { _debug("unlinking once-pending call %p { e=%lx f=%lx }", @@ -550,7 +550,7 @@ void rxrpc_release_call(struct rxrpc_sock *rx, struct rxrpc_call *call) call->recvmsg_link.next = NULL; call->recvmsg_link.prev = NULL; - write_unlock_bh(&rx->recvmsg_lock); + write_unlock(&rx->recvmsg_lock); if (put) rxrpc_put_call(call, rxrpc_call_put_unnotify); @@ -622,9 +622,9 @@ void rxrpc_put_call(struct rxrpc_call *call, enum rxrpc_call_trace why) ASSERTCMP(call->state, ==, RXRPC_CALL_COMPLETE); if (!list_empty(&call->link)) { - spin_lock_bh(&rxnet->call_lock); + spin_lock(&rxnet->call_lock); list_del_init(&call->link); - spin_unlock_bh(&rxnet->call_lock); + spin_unlock(&rxnet->call_lock); } rxrpc_cleanup_call(call); @@ -706,7 +706,7 @@ void rxrpc_destroy_all_calls(struct rxrpc_net *rxnet) _enter(""); if (!list_empty(&rxnet->calls)) { - spin_lock_bh(&rxnet->call_lock); + spin_lock(&rxnet->call_lock); while (!list_empty(&rxnet->calls)) { call = list_entry(rxnet->calls.next, @@ -721,12 +721,12 @@ void rxrpc_destroy_all_calls(struct rxrpc_net *rxnet) rxrpc_call_states[call->state], call->flags, call->events); - spin_unlock_bh(&rxnet->call_lock); + spin_unlock(&rxnet->call_lock); cond_resched(); - spin_lock_bh(&rxnet->call_lock); + spin_lock(&rxnet->call_lock); } - spin_unlock_bh(&rxnet->call_lock); + spin_unlock(&rxnet->call_lock); } atomic_dec(&rxnet->nr_calls); diff --git a/net/rxrpc/conn_client.c b/net/rxrpc/conn_client.c index 3c7b1bdec0db..a08e33c9e54b 100644 --- a/net/rxrpc/conn_client.c +++ b/net/rxrpc/conn_client.c @@ -557,9 +557,9 @@ static void rxrpc_activate_one_channel(struct rxrpc_connection *conn, trace_rxrpc_connect_call(call); - write_lock_bh(&call->state_lock); + write_lock(&call->state_lock); call->state = RXRPC_CALL_CLIENT_SEND_REQUEST; - write_unlock_bh(&call->state_lock); + write_unlock(&call->state_lock); /* Paired with the read barrier in rxrpc_connect_call(). This orders * cid and epoch in the connection wrt to call_id without the need to diff --git a/net/rxrpc/conn_event.c b/net/rxrpc/conn_event.c index 643a56322224..480364bcbf85 100644 --- a/net/rxrpc/conn_event.c +++ b/net/rxrpc/conn_event.c @@ -198,9 +198,9 @@ static int rxrpc_abort_connection(struct rxrpc_connection *conn, _enter("%d,,%u,%u", conn->debug_id, error, abort_code); /* generate a connection-level abort */ - spin_lock_bh(&conn->state_lock); + spin_lock(&conn->state_lock); if (conn->state >= RXRPC_CONN_REMOTELY_ABORTED) { - spin_unlock_bh(&conn->state_lock); + spin_unlock(&conn->state_lock); _leave(" = 0 [already dead]"); return 0; } @@ -209,7 +209,7 @@ static int rxrpc_abort_connection(struct rxrpc_connection *conn, conn->abort_code = abort_code; conn->state = RXRPC_CONN_LOCALLY_ABORTED; set_bit(RXRPC_CONN_DONT_REUSE, &conn->flags); - spin_unlock_bh(&conn->state_lock); + spin_unlock(&conn->state_lock); msg.msg_name = &conn->peer->srx.transport; msg.msg_namelen = conn->peer->srx.transport_len; @@ -265,12 +265,12 @@ static void rxrpc_call_is_secure(struct rxrpc_call *call) { _enter("%p", call); if (call) { - write_lock_bh(&call->state_lock); + write_lock(&call->state_lock); if (call->state == RXRPC_CALL_SERVER_SECURING) { call->state = RXRPC_CALL_SERVER_RECV_REQUEST; rxrpc_notify_socket(call); } - write_unlock_bh(&call->state_lock); + write_unlock(&call->state_lock); } } @@ -325,18 +325,18 @@ static int rxrpc_process_event(struct rxrpc_connection *conn, return ret; spin_lock(&conn->bundle->channel_lock); - spin_lock_bh(&conn->state_lock); + spin_lock(&conn->state_lock); if (conn->state == RXRPC_CONN_SERVICE_CHALLENGING) { conn->state = RXRPC_CONN_SERVICE; - spin_unlock_bh(&conn->state_lock); + spin_unlock(&conn->state_lock); for (loop = 0; loop < RXRPC_MAXCALLS; loop++) rxrpc_call_is_secure( rcu_dereference_protected( conn->channels[loop].call, lockdep_is_held(&conn->bundle->channel_lock))); } else { - spin_unlock_bh(&conn->state_lock); + spin_unlock(&conn->state_lock); } spin_unlock(&conn->bundle->channel_lock); diff --git a/net/rxrpc/conn_service.c b/net/rxrpc/conn_service.c index b5ae7c753fc3..2a55a88b2a5b 100644 --- a/net/rxrpc/conn_service.c +++ b/net/rxrpc/conn_service.c @@ -73,7 +73,7 @@ static void rxrpc_publish_service_conn(struct rxrpc_peer *peer, struct rxrpc_conn_proto k = conn->proto; struct rb_node **pp, *parent; - write_seqlock_bh(&peer->service_conn_lock); + write_seqlock(&peer->service_conn_lock); pp = &peer->service_conns.rb_node; parent = NULL; @@ -94,14 +94,14 @@ static void rxrpc_publish_service_conn(struct rxrpc_peer *peer, rb_insert_color(&conn->service_node, &peer->service_conns); conn_published: set_bit(RXRPC_CONN_IN_SERVICE_CONNS, &conn->flags); - write_sequnlock_bh(&peer->service_conn_lock); + write_sequnlock(&peer->service_conn_lock); _leave(" = %d [new]", conn->debug_id); return; found_extant_conn: if (refcount_read(&cursor->ref) == 0) goto replace_old_connection; - write_sequnlock_bh(&peer->service_conn_lock); + write_sequnlock(&peer->service_conn_lock); /* We should not be able to get here. rxrpc_incoming_connection() is * called in a non-reentrant context, so there can't be a race to * insert a new connection. @@ -195,8 +195,8 @@ void rxrpc_unpublish_service_conn(struct rxrpc_connection *conn) { struct rxrpc_peer *peer = conn->peer; - write_seqlock_bh(&peer->service_conn_lock); + write_seqlock(&peer->service_conn_lock); if (test_and_clear_bit(RXRPC_CONN_IN_SERVICE_CONNS, &conn->flags)) rb_erase(&conn->service_node, &peer->service_conns); - write_sequnlock_bh(&peer->service_conn_lock); + write_sequnlock(&peer->service_conn_lock); } diff --git a/net/rxrpc/input.c b/net/rxrpc/input.c index 7ae7046f0b03..dd2ac5d55e1c 100644 --- a/net/rxrpc/input.c +++ b/net/rxrpc/input.c @@ -669,10 +669,10 @@ static void rxrpc_input_ackinfo(struct rxrpc_call *call, struct sk_buff *skb, peer = call->peer; if (mtu < peer->maxdata) { - spin_lock_bh(&peer->lock); + spin_lock(&peer->lock); peer->maxdata = mtu; peer->mtu = mtu + peer->hdrsize; - spin_unlock_bh(&peer->lock); + spin_unlock(&peer->lock); } if (wake) diff --git a/net/rxrpc/output.c b/net/rxrpc/output.c index 2ea1fa1b8a6f..e5d715b855fc 100644 --- a/net/rxrpc/output.c +++ b/net/rxrpc/output.c @@ -286,9 +286,9 @@ void rxrpc_transmit_ack_packets(struct rxrpc_local *local) if (list_empty(&local->ack_tx_queue)) return; - spin_lock_bh(&local->ack_tx_lock); + spin_lock(&local->ack_tx_lock); list_splice_tail_init(&local->ack_tx_queue, &queue); - spin_unlock_bh(&local->ack_tx_lock); + spin_unlock(&local->ack_tx_lock); while (!list_empty(&queue)) { struct rxrpc_txbuf *txb = @@ -296,9 +296,9 @@ void rxrpc_transmit_ack_packets(struct rxrpc_local *local) ret = rxrpc_send_ack_packet(local, txb); if (ret < 0 && ret != -ECONNRESET) { - spin_lock_bh(&local->ack_tx_lock); + spin_lock(&local->ack_tx_lock); list_splice_init(&queue, &local->ack_tx_queue); - spin_unlock_bh(&local->ack_tx_lock); + spin_unlock(&local->ack_tx_lock); break; } diff --git a/net/rxrpc/peer_event.c b/net/rxrpc/peer_event.c index fb8096e93d2c..6685bf917aa6 100644 --- a/net/rxrpc/peer_event.c +++ b/net/rxrpc/peer_event.c @@ -121,10 +121,10 @@ static void rxrpc_adjust_mtu(struct rxrpc_peer *peer, unsigned int mtu) } if (mtu < peer->mtu) { - spin_lock_bh(&peer->lock); + spin_lock(&peer->lock); peer->mtu = mtu; peer->maxdata = peer->mtu - peer->hdrsize; - spin_unlock_bh(&peer->lock); + spin_unlock(&peer->lock); } } @@ -237,7 +237,7 @@ static void rxrpc_peer_keepalive_dispatch(struct rxrpc_net *rxnet, time64_t keepalive_at; int slot; - spin_lock_bh(&rxnet->peer_hash_lock); + spin_lock(&rxnet->peer_hash_lock); while (!list_empty(collector)) { peer = list_entry(collector->next, @@ -248,7 +248,7 @@ static void rxrpc_peer_keepalive_dispatch(struct rxrpc_net *rxnet, continue; if (__rxrpc_use_local(peer->local, rxrpc_local_use_peer_keepalive)) { - spin_unlock_bh(&rxnet->peer_hash_lock); + spin_unlock(&rxnet->peer_hash_lock); keepalive_at = peer->last_tx_at + RXRPC_KEEPALIVE_TIME; slot = keepalive_at - base; @@ -267,7 +267,7 @@ static void rxrpc_peer_keepalive_dispatch(struct rxrpc_net *rxnet, */ slot += cursor; slot &= mask; - spin_lock_bh(&rxnet->peer_hash_lock); + spin_lock(&rxnet->peer_hash_lock); list_add_tail(&peer->keepalive_link, &rxnet->peer_keepalive[slot & mask]); rxrpc_unuse_local(peer->local, rxrpc_local_unuse_peer_keepalive); @@ -275,7 +275,7 @@ static void rxrpc_peer_keepalive_dispatch(struct rxrpc_net *rxnet, rxrpc_put_peer_locked(peer, rxrpc_peer_put_keepalive); } - spin_unlock_bh(&rxnet->peer_hash_lock); + spin_unlock(&rxnet->peer_hash_lock); } /* @@ -305,7 +305,7 @@ void rxrpc_peer_keepalive_worker(struct work_struct *work) * second; the bucket at cursor + 1 goes at now + 1s and so * on... */ - spin_lock_bh(&rxnet->peer_hash_lock); + spin_lock(&rxnet->peer_hash_lock); list_splice_init(&rxnet->peer_keepalive_new, &collector); stop = cursor + ARRAY_SIZE(rxnet->peer_keepalive); @@ -317,7 +317,7 @@ void rxrpc_peer_keepalive_worker(struct work_struct *work) } base = now; - spin_unlock_bh(&rxnet->peer_hash_lock); + spin_unlock(&rxnet->peer_hash_lock); rxnet->peer_keepalive_base = base; rxnet->peer_keepalive_cursor = cursor; diff --git a/net/rxrpc/peer_object.c b/net/rxrpc/peer_object.c index 9e682a60a800..608946dcc505 100644 --- a/net/rxrpc/peer_object.c +++ b/net/rxrpc/peer_object.c @@ -349,7 +349,7 @@ struct rxrpc_peer *rxrpc_lookup_peer(struct rxrpc_sock *rx, return NULL; } - spin_lock_bh(&rxnet->peer_hash_lock); + spin_lock(&rxnet->peer_hash_lock); /* Need to check that we aren't racing with someone else */ peer = __rxrpc_lookup_peer_rcu(local, srx, hash_key); @@ -362,7 +362,7 @@ struct rxrpc_peer *rxrpc_lookup_peer(struct rxrpc_sock *rx, &rxnet->peer_keepalive_new); } - spin_unlock_bh(&rxnet->peer_hash_lock); + spin_unlock(&rxnet->peer_hash_lock); if (peer) rxrpc_free_peer(candidate); @@ -412,10 +412,10 @@ static void __rxrpc_put_peer(struct rxrpc_peer *peer) ASSERT(hlist_empty(&peer->error_targets)); - spin_lock_bh(&rxnet->peer_hash_lock); + spin_lock(&rxnet->peer_hash_lock); hash_del_rcu(&peer->hash_link); list_del_init(&peer->keepalive_link); - spin_unlock_bh(&rxnet->peer_hash_lock); + spin_unlock(&rxnet->peer_hash_lock); rxrpc_free_peer(peer); } diff --git a/net/rxrpc/recvmsg.c b/net/rxrpc/recvmsg.c index 77d03b9e4c4c..3a8576e9daf3 100644 --- a/net/rxrpc/recvmsg.c +++ b/net/rxrpc/recvmsg.c @@ -36,16 +36,16 @@ void rxrpc_notify_socket(struct rxrpc_call *call) sk = &rx->sk; if (rx && sk->sk_state < RXRPC_CLOSE) { if (call->notify_rx) { - spin_lock_bh(&call->notify_lock); + spin_lock(&call->notify_lock); call->notify_rx(sk, call, call->user_call_ID); - spin_unlock_bh(&call->notify_lock); + spin_unlock(&call->notify_lock); } else { - write_lock_bh(&rx->recvmsg_lock); + write_lock(&rx->recvmsg_lock); if (list_empty(&call->recvmsg_link)) { rxrpc_get_call(call, rxrpc_call_get_notify_socket); list_add_tail(&call->recvmsg_link, &rx->recvmsg_q); } - write_unlock_bh(&rx->recvmsg_lock); + write_unlock(&rx->recvmsg_lock); if (!sock_flag(sk, SOCK_DEAD)) { _debug("call %ps", sk->sk_data_ready); @@ -87,9 +87,9 @@ bool rxrpc_set_call_completion(struct rxrpc_call *call, bool ret = false; if (call->state < RXRPC_CALL_COMPLETE) { - write_lock_bh(&call->state_lock); + write_lock(&call->state_lock); ret = __rxrpc_set_call_completion(call, compl, abort_code, error); - write_unlock_bh(&call->state_lock); + write_unlock(&call->state_lock); } return ret; } @@ -107,9 +107,9 @@ bool rxrpc_call_completed(struct rxrpc_call *call) bool ret = false; if (call->state < RXRPC_CALL_COMPLETE) { - write_lock_bh(&call->state_lock); + write_lock(&call->state_lock); ret = __rxrpc_call_completed(call); - write_unlock_bh(&call->state_lock); + write_unlock(&call->state_lock); } return ret; } @@ -131,9 +131,9 @@ bool rxrpc_abort_call(const char *why, struct rxrpc_call *call, { bool ret; - write_lock_bh(&call->state_lock); + write_lock(&call->state_lock); ret = __rxrpc_abort_call(why, call, seq, abort_code, error); - write_unlock_bh(&call->state_lock); + write_unlock(&call->state_lock); return ret; } @@ -193,23 +193,23 @@ static void rxrpc_end_rx_phase(struct rxrpc_call *call, rxrpc_serial_t serial) if (call->state == RXRPC_CALL_CLIENT_RECV_REPLY) rxrpc_propose_delay_ACK(call, serial, rxrpc_propose_ack_terminal_ack); - write_lock_bh(&call->state_lock); + write_lock(&call->state_lock); switch (call->state) { case RXRPC_CALL_CLIENT_RECV_REPLY: __rxrpc_call_completed(call); - write_unlock_bh(&call->state_lock); + write_unlock(&call->state_lock); break; case RXRPC_CALL_SERVER_RECV_REQUEST: call->state = RXRPC_CALL_SERVER_ACK_REQUEST; call->expect_req_by = jiffies + MAX_JIFFY_OFFSET; - write_unlock_bh(&call->state_lock); + write_unlock(&call->state_lock); rxrpc_propose_delay_ACK(call, serial, rxrpc_propose_ack_processing_op); break; default: - write_unlock_bh(&call->state_lock); + write_unlock(&call->state_lock); break; } } @@ -442,14 +442,14 @@ try_again: /* Find the next call and dequeue it if we're not just peeking. If we * do dequeue it, that comes with a ref that we will need to release. */ - write_lock_bh(&rx->recvmsg_lock); + write_lock(&rx->recvmsg_lock); l = rx->recvmsg_q.next; call = list_entry(l, struct rxrpc_call, recvmsg_link); if (!(flags & MSG_PEEK)) list_del_init(&call->recvmsg_link); else rxrpc_get_call(call, rxrpc_call_get_recvmsg); - write_unlock_bh(&rx->recvmsg_lock); + write_unlock(&rx->recvmsg_lock); trace_rxrpc_recvmsg(call, rxrpc_recvmsg_dequeue, 0); @@ -538,9 +538,9 @@ error_unlock_call: error_requeue_call: if (!(flags & MSG_PEEK)) { - write_lock_bh(&rx->recvmsg_lock); + write_lock(&rx->recvmsg_lock); list_add(&call->recvmsg_link, &rx->recvmsg_q); - write_unlock_bh(&rx->recvmsg_lock); + write_unlock(&rx->recvmsg_lock); trace_rxrpc_recvmsg(call, rxrpc_recvmsg_requeue, 0); } else { rxrpc_put_call(call, rxrpc_call_put_recvmsg); diff --git a/net/rxrpc/sendmsg.c b/net/rxrpc/sendmsg.c index 58e0a36f6aa9..2c861c55ed70 100644 --- a/net/rxrpc/sendmsg.c +++ b/net/rxrpc/sendmsg.c @@ -195,7 +195,7 @@ static void rxrpc_queue_packet(struct rxrpc_sock *rx, struct rxrpc_call *call, if (last || call->state == RXRPC_CALL_SERVER_ACK_REQUEST) { _debug("________awaiting reply/ACK__________"); - write_lock_bh(&call->state_lock); + write_lock(&call->state_lock); switch (call->state) { case RXRPC_CALL_CLIENT_SEND_REQUEST: call->state = RXRPC_CALL_CLIENT_AWAIT_REPLY; @@ -218,7 +218,7 @@ static void rxrpc_queue_packet(struct rxrpc_sock *rx, struct rxrpc_call *call, default: break; } - write_unlock_bh(&call->state_lock); + write_unlock(&call->state_lock); } if (poke) @@ -357,10 +357,10 @@ reload: success: ret = copied; if (READ_ONCE(call->state) == RXRPC_CALL_COMPLETE) { - read_lock_bh(&call->state_lock); + read_lock(&call->state_lock); if (call->error < 0) ret = call->error; - read_unlock_bh(&call->state_lock); + read_unlock(&call->state_lock); } out: call->tx_pending = txb; @@ -725,9 +725,9 @@ int rxrpc_kernel_send_data(struct socket *sock, struct rxrpc_call *call, notify_end_tx, &dropped_lock); break; case RXRPC_CALL_COMPLETE: - read_lock_bh(&call->state_lock); + read_lock(&call->state_lock); ret = call->error; - read_unlock_bh(&call->state_lock); + read_unlock(&call->state_lock); break; default: /* Request phase complete for this client call */ From 32cf8edb079a6a687a2b5dba39a813a0bbd0ddf9 Mon Sep 17 00:00:00 2001 From: David Howells Date: Fri, 11 Nov 2022 13:47:35 +0000 Subject: [PATCH 1432/2002] rxrpc: Trace/count transmission underflows and cwnd resets Add a tracepoint to log when a cwnd reset occurs due to lack of transmission on a call. Add stat counters to count transmission underflows (ie. when we have tx window space, but sendmsg doesn't manage to keep up), cwnd resets and transmission failures. Signed-off-by: David Howells cc: Marc Dionne cc: linux-afs@lists.infradead.org --- include/trace/events/rxrpc.h | 38 ++++++++++++++++++++++++++++++++++++ net/rxrpc/ar-internal.h | 3 +++ net/rxrpc/call_event.c | 4 +++- net/rxrpc/input.c | 7 +++++-- net/rxrpc/output.c | 2 ++ net/rxrpc/proc.c | 14 +++++++++---- 6 files changed, 61 insertions(+), 7 deletions(-) diff --git a/include/trace/events/rxrpc.h b/include/trace/events/rxrpc.h index c49b0c233594..b41e913ae78a 100644 --- a/include/trace/events/rxrpc.h +++ b/include/trace/events/rxrpc.h @@ -1446,6 +1446,44 @@ TRACE_EVENT(rxrpc_congest, __entry->sum.retrans_timeo ? " rTxTo" : "") ); +TRACE_EVENT(rxrpc_reset_cwnd, + TP_PROTO(struct rxrpc_call *call, ktime_t now), + + TP_ARGS(call, now), + + TP_STRUCT__entry( + __field(unsigned int, call ) + __field(enum rxrpc_congest_mode, mode ) + __field(unsigned short, cwnd ) + __field(unsigned short, extra ) + __field(rxrpc_seq_t, hard_ack ) + __field(rxrpc_seq_t, prepared ) + __field(ktime_t, since_last_tx ) + __field(bool, has_data ) + ), + + TP_fast_assign( + __entry->call = call->debug_id; + __entry->mode = call->cong_mode; + __entry->cwnd = call->cong_cwnd; + __entry->extra = call->cong_extra; + __entry->hard_ack = call->acks_hard_ack; + __entry->prepared = call->tx_prepared - call->tx_bottom; + __entry->since_last_tx = ktime_sub(now, call->tx_last_sent); + __entry->has_data = !list_empty(&call->tx_sendmsg); + ), + + TP_printk("c=%08x q=%08x %s cw=%u+%u pr=%u tm=%llu d=%u", + __entry->call, + __entry->hard_ack, + __print_symbolic(__entry->mode, rxrpc_congest_modes), + __entry->cwnd, + __entry->extra, + __entry->prepared, + ktime_to_ns(__entry->since_last_tx), + __entry->has_data) + ); + TRACE_EVENT(rxrpc_disconnect_call, TP_PROTO(struct rxrpc_call *call), diff --git a/net/rxrpc/ar-internal.h b/net/rxrpc/ar-internal.h index 6b993a3d4186..6cfe366ee224 100644 --- a/net/rxrpc/ar-internal.h +++ b/net/rxrpc/ar-internal.h @@ -101,6 +101,9 @@ struct rxrpc_net { atomic_t stat_tx_data_retrans; atomic_t stat_tx_data_send; atomic_t stat_tx_data_send_frag; + atomic_t stat_tx_data_send_fail; + atomic_t stat_tx_data_underflow; + atomic_t stat_tx_data_cwnd_reset; atomic_t stat_rx_data; atomic_t stat_rx_data_reqack; atomic_t stat_rx_data_jumbo; diff --git a/net/rxrpc/call_event.c b/net/rxrpc/call_event.c index 18591f9ecc6a..9f1e490ab976 100644 --- a/net/rxrpc/call_event.c +++ b/net/rxrpc/call_event.c @@ -317,8 +317,10 @@ static void rxrpc_transmit_some_data(struct rxrpc_call *call) case RXRPC_CALL_CLIENT_AWAIT_REPLY: if (!rxrpc_tx_window_has_space(call)) return; - if (list_empty(&call->tx_sendmsg)) + if (list_empty(&call->tx_sendmsg)) { + rxrpc_inc_stat(call->rxnet, stat_tx_data_underflow); return; + } rxrpc_decant_prepared_tx(call); break; default: diff --git a/net/rxrpc/input.c b/net/rxrpc/input.c index dd2ac5d55e1c..2988e3d0c1f6 100644 --- a/net/rxrpc/input.c +++ b/net/rxrpc/input.c @@ -27,6 +27,7 @@ static void rxrpc_congestion_management(struct rxrpc_call *call, enum rxrpc_congest_change change = rxrpc_cong_no_change; unsigned int cumulative_acks = call->cong_cumul_acks; unsigned int cwnd = call->cong_cwnd; + ktime_t now; bool resend = false; summary->flight_size = @@ -59,13 +60,15 @@ static void rxrpc_congestion_management(struct rxrpc_call *call, /* If we haven't transmitted anything for >1RTT, we should reset the * congestion management state. */ + now = ktime_get_real(); if ((call->cong_mode == RXRPC_CALL_SLOW_START || call->cong_mode == RXRPC_CALL_CONGEST_AVOIDANCE) && ktime_before(ktime_add_us(call->tx_last_sent, - call->peer->srtt_us >> 3), - ktime_get_real()) + call->peer->srtt_us >> 3), now) ) { + trace_rxrpc_reset_cwnd(call, now); change = rxrpc_cong_idle_reset; + rxrpc_inc_stat(call->rxnet, stat_tx_data_cwnd_reset); summary->mode = RXRPC_CALL_SLOW_START; if (RXRPC_TX_SMSS > 2190) summary->cwnd = 2; diff --git a/net/rxrpc/output.c b/net/rxrpc/output.c index e5d715b855fc..8147a47d1702 100644 --- a/net/rxrpc/output.c +++ b/net/rxrpc/output.c @@ -485,6 +485,7 @@ dont_set_request_ack: up_read(&conn->local->defrag_sem); if (ret < 0) { + rxrpc_inc_stat(call->rxnet, stat_tx_data_send_fail); rxrpc_cancel_rtt_probe(call, serial, rtt_slot); trace_rxrpc_tx_fail(call->debug_id, serial, ret, rxrpc_tx_point_call_data_nofrag); @@ -567,6 +568,7 @@ send_fragmentable: } if (ret < 0) { + rxrpc_inc_stat(call->rxnet, stat_tx_data_send_fail); rxrpc_cancel_rtt_probe(call, serial, rtt_slot); trace_rxrpc_tx_fail(call->debug_id, serial, ret, rxrpc_tx_point_call_data_frag); diff --git a/net/rxrpc/proc.c b/net/rxrpc/proc.c index 5af7c8ee4b1a..6816934cb4cf 100644 --- a/net/rxrpc/proc.c +++ b/net/rxrpc/proc.c @@ -398,13 +398,16 @@ int rxrpc_stats_show(struct seq_file *seq, void *v) struct rxrpc_net *rxnet = rxrpc_net(seq_file_single_net(seq)); seq_printf(seq, - "Data : send=%u sendf=%u\n", + "Data : send=%u sendf=%u fail=%u\n", atomic_read(&rxnet->stat_tx_data_send), - atomic_read(&rxnet->stat_tx_data_send_frag)); + atomic_read(&rxnet->stat_tx_data_send_frag), + atomic_read(&rxnet->stat_tx_data_send_fail)); seq_printf(seq, - "Data-Tx : nr=%u retrans=%u\n", + "Data-Tx : nr=%u retrans=%u uf=%u cwr=%u\n", atomic_read(&rxnet->stat_tx_data), - atomic_read(&rxnet->stat_tx_data_retrans)); + atomic_read(&rxnet->stat_tx_data_retrans), + atomic_read(&rxnet->stat_tx_data_underflow), + atomic_read(&rxnet->stat_tx_data_cwnd_reset)); seq_printf(seq, "Data-Rx : nr=%u reqack=%u jumbo=%u\n", atomic_read(&rxnet->stat_rx_data), @@ -472,8 +475,11 @@ int rxrpc_stats_clear(struct file *file, char *buf, size_t size) atomic_set(&rxnet->stat_tx_data, 0); atomic_set(&rxnet->stat_tx_data_retrans, 0); + atomic_set(&rxnet->stat_tx_data_underflow, 0); + atomic_set(&rxnet->stat_tx_data_cwnd_reset, 0); atomic_set(&rxnet->stat_tx_data_send, 0); atomic_set(&rxnet->stat_tx_data_send_frag, 0); + atomic_set(&rxnet->stat_tx_data_send_fail, 0); atomic_set(&rxnet->stat_rx_data, 0); atomic_set(&rxnet->stat_rx_data_reqack, 0); atomic_set(&rxnet->stat_rx_data_jumbo, 0); From 5086d9a9dfec4866806da303115489b0606decb7 Mon Sep 17 00:00:00 2001 From: David Howells Date: Fri, 11 Nov 2022 13:47:35 +0000 Subject: [PATCH 1433/2002] rxrpc: Move the cwnd degradation after transmitting packets When we've gone for >1RTT without transmitting a packet, we should reduce the ssthresh and cut the cwnd by half (as suggested in RFC2861 sec 3.1). However, we may receive ACK packets in a batch and the first of these may cut the cwnd, preventing further transmission, and each subsequent one cuts the cwnd yet further, reducing it to the floor and killing performance. Fix this by moving the cwnd reset to after doing the transmission and resetting the base time such that we don't cut the cwnd by half again for at least another RTT. Signed-off-by: David Howells cc: Marc Dionne cc: linux-afs@lists.infradead.org --- net/rxrpc/ar-internal.h | 2 ++ net/rxrpc/call_event.c | 7 ++++++ net/rxrpc/input.c | 49 +++++++++++++++++++++++------------------ net/rxrpc/proc.c | 5 +++-- 4 files changed, 39 insertions(+), 24 deletions(-) diff --git a/net/rxrpc/ar-internal.h b/net/rxrpc/ar-internal.h index 6cfe366ee224..785cd0dd1eea 100644 --- a/net/rxrpc/ar-internal.h +++ b/net/rxrpc/ar-internal.h @@ -666,6 +666,7 @@ struct rxrpc_call { * packets) rather than bytes. */ #define RXRPC_TX_SMSS RXRPC_JUMBO_DATALEN +#define RXRPC_MIN_CWND (RXRPC_TX_SMSS > 2190 ? 2 : RXRPC_TX_SMSS > 1095 ? 3 : 4) u8 cong_cwnd; /* Congestion window size */ u8 cong_extra; /* Extra to send for congestion management */ u8 cong_ssthresh; /* Slow-start threshold */ @@ -953,6 +954,7 @@ void rxrpc_unpublish_service_conn(struct rxrpc_connection *); /* * input.c */ +void rxrpc_congestion_degrade(struct rxrpc_call *); void rxrpc_input_call_packet(struct rxrpc_call *, struct sk_buff *); void rxrpc_implicit_end_call(struct rxrpc_call *, struct sk_buff *); diff --git a/net/rxrpc/call_event.c b/net/rxrpc/call_event.c index 9f1e490ab976..fd122e3726bd 100644 --- a/net/rxrpc/call_event.c +++ b/net/rxrpc/call_event.c @@ -427,6 +427,13 @@ void rxrpc_input_call_event(struct rxrpc_call *call, struct sk_buff *skb) rxrpc_transmit_some_data(call); + if (skb) { + struct rxrpc_skb_priv *sp = rxrpc_skb(skb); + + if (sp->hdr.type == RXRPC_PACKET_TYPE_ACK) + rxrpc_congestion_degrade(call); + } + if (test_and_clear_bit(RXRPC_CALL_EV_INITIAL_PING, &call->events)) rxrpc_send_initial_ping(call); diff --git a/net/rxrpc/input.c b/net/rxrpc/input.c index 2988e3d0c1f6..d0e20e946e48 100644 --- a/net/rxrpc/input.c +++ b/net/rxrpc/input.c @@ -27,7 +27,6 @@ static void rxrpc_congestion_management(struct rxrpc_call *call, enum rxrpc_congest_change change = rxrpc_cong_no_change; unsigned int cumulative_acks = call->cong_cumul_acks; unsigned int cwnd = call->cong_cwnd; - ktime_t now; bool resend = false; summary->flight_size = @@ -57,27 +56,6 @@ static void rxrpc_congestion_management(struct rxrpc_call *call, summary->cumulative_acks = cumulative_acks; summary->dup_acks = call->cong_dup_acks; - /* If we haven't transmitted anything for >1RTT, we should reset the - * congestion management state. - */ - now = ktime_get_real(); - if ((call->cong_mode == RXRPC_CALL_SLOW_START || - call->cong_mode == RXRPC_CALL_CONGEST_AVOIDANCE) && - ktime_before(ktime_add_us(call->tx_last_sent, - call->peer->srtt_us >> 3), now) - ) { - trace_rxrpc_reset_cwnd(call, now); - change = rxrpc_cong_idle_reset; - rxrpc_inc_stat(call->rxnet, stat_tx_data_cwnd_reset); - summary->mode = RXRPC_CALL_SLOW_START; - if (RXRPC_TX_SMSS > 2190) - summary->cwnd = 2; - else if (RXRPC_TX_SMSS > 1095) - summary->cwnd = 3; - else - summary->cwnd = 4; - } - switch (call->cong_mode) { case RXRPC_CALL_SLOW_START: if (summary->saw_nacks) @@ -197,6 +175,33 @@ send_extra_data: goto out_no_clear_ca; } +/* + * Degrade the congestion window if we haven't transmitted a packet for >1RTT. + */ +void rxrpc_congestion_degrade(struct rxrpc_call *call) +{ + ktime_t rtt, now; + + if (call->cong_mode != RXRPC_CALL_SLOW_START && + call->cong_mode != RXRPC_CALL_CONGEST_AVOIDANCE) + return; + if (call->state == RXRPC_CALL_CLIENT_AWAIT_REPLY) + return; + + rtt = ns_to_ktime(call->peer->srtt_us * (1000 / 8)); + now = ktime_get_real(); + if (!ktime_before(ktime_add(call->tx_last_sent, rtt), now)) + return; + + trace_rxrpc_reset_cwnd(call, now); + rxrpc_inc_stat(call->rxnet, stat_tx_data_cwnd_reset); + call->tx_last_sent = now; + call->cong_mode = RXRPC_CALL_SLOW_START; + call->cong_ssthresh = max_t(unsigned int, call->cong_ssthresh, + call->cong_cwnd * 3 / 4); + call->cong_cwnd = max_t(unsigned int, call->cong_cwnd / 2, RXRPC_MIN_CWND); +} + /* * Apply a hard ACK by advancing the Tx window. */ diff --git a/net/rxrpc/proc.c b/net/rxrpc/proc.c index 6816934cb4cf..3a59591ec061 100644 --- a/net/rxrpc/proc.c +++ b/net/rxrpc/proc.c @@ -61,7 +61,7 @@ static int rxrpc_call_seq_show(struct seq_file *seq, void *v) "Proto Local " " Remote " " SvID ConnID CallID End Use State Abort " - " DebugId TxSeq TW RxSeq RW RxSerial RxTimo\n"); + " DebugId TxSeq TW RxSeq RW RxSerial CW RxTimo\n"); return 0; } @@ -84,7 +84,7 @@ static int rxrpc_call_seq_show(struct seq_file *seq, void *v) wtmp = atomic64_read_acquire(&call->ackr_window); seq_printf(seq, "UDP %-47.47s %-47.47s %4x %08x %08x %s %3u" - " %-8.8s %08x %08x %08x %02x %08x %02x %08x %06lx\n", + " %-8.8s %08x %08x %08x %02x %08x %02x %08x %02x %06lx\n", lbuff, rbuff, call->dest_srx.srx_service, @@ -98,6 +98,7 @@ static int rxrpc_call_seq_show(struct seq_file *seq, void *v) acks_hard_ack, READ_ONCE(call->tx_top) - acks_hard_ack, lower_32_bits(wtmp), upper_32_bits(wtmp) - lower_32_bits(wtmp), call->rx_serial, + call->cong_cwnd, timeout); return 0; From a2cf3264f331acfeb7e463ad7b7fe1ac647a829d Mon Sep 17 00:00:00 2001 From: David Howells Date: Wed, 16 Nov 2022 12:02:22 +0000 Subject: [PATCH 1434/2002] rxrpc: Fold __rxrpc_unuse_local() into rxrpc_unuse_local() Fold __rxrpc_unuse_local() into rxrpc_unuse_local() as the latter is now the only user of the former. Signed-off-by: David Howells cc: Marc Dionne cc: linux-afs@lists.infradead.org --- net/rxrpc/ar-internal.h | 12 ------------ net/rxrpc/local_object.c | 12 ++++++++++-- 2 files changed, 10 insertions(+), 14 deletions(-) diff --git a/net/rxrpc/ar-internal.h b/net/rxrpc/ar-internal.h index 785cd0dd1eea..2a4928249a64 100644 --- a/net/rxrpc/ar-internal.h +++ b/net/rxrpc/ar-internal.h @@ -1002,18 +1002,6 @@ void rxrpc_unuse_local(struct rxrpc_local *, enum rxrpc_local_trace); void rxrpc_destroy_local(struct rxrpc_local *local); void rxrpc_destroy_all_locals(struct rxrpc_net *); -static inline bool __rxrpc_unuse_local(struct rxrpc_local *local, - enum rxrpc_local_trace why) -{ - unsigned int debug_id = local->debug_id; - int r, u; - - r = refcount_read(&local->ref); - u = atomic_dec_return(&local->active_users); - trace_rxrpc_local(debug_id, why, r, u); - return u == 0; -} - static inline bool __rxrpc_use_local(struct rxrpc_local *local, enum rxrpc_local_trace why) { diff --git a/net/rxrpc/local_object.c b/net/rxrpc/local_object.c index c73a5a1bc088..1e994a83db2b 100644 --- a/net/rxrpc/local_object.c +++ b/net/rxrpc/local_object.c @@ -359,8 +359,16 @@ struct rxrpc_local *rxrpc_use_local(struct rxrpc_local *local, */ void rxrpc_unuse_local(struct rxrpc_local *local, enum rxrpc_local_trace why) { - if (local && __rxrpc_unuse_local(local, why)) - kthread_stop(local->io_thread); + unsigned int debug_id = local->debug_id; + int r, u; + + if (local) { + r = refcount_read(&local->ref); + u = atomic_dec_return(&local->active_users); + trace_rxrpc_local(debug_id, why, r, u); + if (u == 0) + kthread_stop(local->io_thread); + } } /* From b0346843b1076b34a0278ff601f8f287535cb064 Mon Sep 17 00:00:00 2001 From: David Howells Date: Thu, 30 Jan 2020 21:48:13 +0000 Subject: [PATCH 1435/2002] rxrpc: Transmit ACKs at the point of generation For ACKs generated inside the I/O thread, transmit the ACK at the point of generation. Where the ACK is generated outside of the I/O thread, it's offloaded to the I/O thread to transmit it. Signed-off-by: David Howells cc: Marc Dionne cc: linux-afs@lists.infradead.org --- include/trace/events/rxrpc.h | 3 --- net/rxrpc/ar-internal.h | 5 +---- net/rxrpc/call_event.c | 17 ++------------- net/rxrpc/io_thread.c | 5 ----- net/rxrpc/local_object.c | 2 -- net/rxrpc/output.c | 42 ++---------------------------------- net/rxrpc/recvmsg.c | 3 --- net/rxrpc/sendmsg.c | 2 -- net/rxrpc/txbuf.c | 1 - 9 files changed, 5 insertions(+), 75 deletions(-) diff --git a/include/trace/events/rxrpc.h b/include/trace/events/rxrpc.h index b41e913ae78a..049b52e7aa6a 100644 --- a/include/trace/events/rxrpc.h +++ b/include/trace/events/rxrpc.h @@ -63,7 +63,6 @@ EM(rxrpc_local_put_peer, "PUT peer ") \ EM(rxrpc_local_put_prealloc_conn, "PUT conn-pre") \ EM(rxrpc_local_put_release_sock, "PUT rel-sock") \ - EM(rxrpc_local_see_tx_ack, "SEE tx-ack ") \ EM(rxrpc_local_stop, "STOP ") \ EM(rxrpc_local_stopped, "STOPPED ") \ EM(rxrpc_local_unuse_bind, "UNU bind ") \ @@ -156,7 +155,6 @@ EM(rxrpc_call_get_recvmsg, "GET recvmsg ") \ EM(rxrpc_call_get_release_sock, "GET rel-sock") \ EM(rxrpc_call_get_sendmsg, "GET sendmsg ") \ - EM(rxrpc_call_get_send_ack, "GET send-ack") \ EM(rxrpc_call_get_userid, "GET user-id ") \ EM(rxrpc_call_new_client, "NEW client ") \ EM(rxrpc_call_new_prealloc_service, "NEW prealloc") \ @@ -168,7 +166,6 @@ EM(rxrpc_call_put_recvmsg, "PUT recvmsg ") \ EM(rxrpc_call_put_release_sock, "PUT rls-sock") \ EM(rxrpc_call_put_release_sock_tba, "PUT rls-sk-a") \ - EM(rxrpc_call_put_send_ack, "PUT send-ack") \ EM(rxrpc_call_put_sendmsg, "PUT sendmsg ") \ EM(rxrpc_call_put_unnotify, "PUT unnotify") \ EM(rxrpc_call_put_userid_exists, "PUT u-exists") \ diff --git a/net/rxrpc/ar-internal.h b/net/rxrpc/ar-internal.h index 2a4928249a64..e7dccab7b741 100644 --- a/net/rxrpc/ar-internal.h +++ b/net/rxrpc/ar-internal.h @@ -287,8 +287,6 @@ struct rxrpc_local { struct hlist_node link; struct socket *socket; /* my UDP socket */ struct task_struct *io_thread; - struct list_head ack_tx_queue; /* List of ACKs that need sending */ - spinlock_t ack_tx_lock; /* ACK list lock */ struct rxrpc_sock __rcu *service; /* Service(s) listening on this endpoint */ struct rw_semaphore defrag_sem; /* control re-enablement of IP DF bit */ struct sk_buff_head rx_queue; /* Received packets */ @@ -762,7 +760,6 @@ struct rxrpc_txbuf { struct rcu_head rcu; struct list_head call_link; /* Link in call->tx_sendmsg/tx_buffer */ struct list_head tx_link; /* Link in live Enc queue or Tx queue */ - struct rxrpc_call *call; /* Call to which belongs */ ktime_t last_sent; /* Time at which last transmitted */ refcount_t ref; rxrpc_seq_t seq; /* Sequence number of this packet */ @@ -1047,7 +1044,7 @@ static inline struct rxrpc_net *rxrpc_net(struct net *net) /* * output.c */ -void rxrpc_transmit_ack_packets(struct rxrpc_local *); +int rxrpc_send_ack_packet(struct rxrpc_call *call, struct rxrpc_txbuf *txb); int rxrpc_send_abort_packet(struct rxrpc_call *); int rxrpc_send_data_packet(struct rxrpc_call *, struct rxrpc_txbuf *); void rxrpc_reject_packet(struct rxrpc_local *local, struct sk_buff *skb); diff --git a/net/rxrpc/call_event.c b/net/rxrpc/call_event.c index fd122e3726bd..b2cf448fb02c 100644 --- a/net/rxrpc/call_event.c +++ b/net/rxrpc/call_event.c @@ -69,7 +69,6 @@ void rxrpc_propose_delay_ACK(struct rxrpc_call *call, rxrpc_serial_t serial, void rxrpc_send_ACK(struct rxrpc_call *call, u8 ack_reason, rxrpc_serial_t serial, enum rxrpc_propose_ack_trace why) { - struct rxrpc_local *local = call->conn->local; struct rxrpc_txbuf *txb; if (test_bit(RXRPC_CALL_DISCONNECTED, &call->flags)) @@ -96,17 +95,9 @@ void rxrpc_send_ACK(struct rxrpc_call *call, u8 ack_reason, txb->ack.reason = ack_reason; txb->ack.nAcks = 0; - if (!rxrpc_try_get_call(call, rxrpc_call_get_send_ack)) { - rxrpc_put_txbuf(txb, rxrpc_txbuf_put_nomem); - return; - } - - spin_lock(&local->ack_tx_lock); - list_add_tail(&txb->tx_link, &local->ack_tx_queue); - spin_unlock(&local->ack_tx_lock); trace_rxrpc_send_ack(call, why, ack_reason, serial); - - rxrpc_wake_up_io_thread(local); + rxrpc_send_ack_packet(call, txb); + rxrpc_put_txbuf(txb, rxrpc_txbuf_put_ack_tx); } /* @@ -294,10 +285,6 @@ static void rxrpc_decant_prepared_tx(struct rxrpc_call *call) rxrpc_transmit_one(call, txb); - // TODO: Drain the transmission buffers. Do this somewhere better - if (after(call->acks_hard_ack, call->tx_bottom + 16)) - rxrpc_shrink_call_tx_buffer(call); - if (!rxrpc_tx_window_has_space(call)) break; } diff --git a/net/rxrpc/io_thread.c b/net/rxrpc/io_thread.c index 19aa315eddf5..d83ae3193032 100644 --- a/net/rxrpc/io_thread.c +++ b/net/rxrpc/io_thread.c @@ -447,11 +447,6 @@ int rxrpc_io_thread(void *data) continue; } - if (!list_empty(&local->ack_tx_queue)) { - rxrpc_transmit_ack_packets(local); - continue; - } - /* Process received packets and errors. */ if ((skb = __skb_dequeue(&rx_queue))) { switch (skb->mark) { diff --git a/net/rxrpc/local_object.c b/net/rxrpc/local_object.c index 1e994a83db2b..44222923c0d1 100644 --- a/net/rxrpc/local_object.c +++ b/net/rxrpc/local_object.c @@ -96,8 +96,6 @@ static struct rxrpc_local *rxrpc_alloc_local(struct rxrpc_net *rxnet, atomic_set(&local->active_users, 1); local->rxnet = rxnet; INIT_HLIST_NODE(&local->link); - INIT_LIST_HEAD(&local->ack_tx_queue); - spin_lock_init(&local->ack_tx_lock); init_rwsem(&local->defrag_sem); skb_queue_head_init(&local->rx_queue); INIT_LIST_HEAD(&local->call_attend_q); diff --git a/net/rxrpc/output.c b/net/rxrpc/output.c index 8147a47d1702..3d8c9f830ee0 100644 --- a/net/rxrpc/output.c +++ b/net/rxrpc/output.c @@ -203,12 +203,11 @@ static void rxrpc_cancel_rtt_probe(struct rxrpc_call *call, } /* - * Send an ACK call packet. + * Transmit an ACK packet. */ -static int rxrpc_send_ack_packet(struct rxrpc_local *local, struct rxrpc_txbuf *txb) +int rxrpc_send_ack_packet(struct rxrpc_call *call, struct rxrpc_txbuf *txb) { struct rxrpc_connection *conn; - struct rxrpc_call *call = txb->call; struct msghdr msg; struct kvec iov[1]; rxrpc_serial_t serial; @@ -271,43 +270,6 @@ static int rxrpc_send_ack_packet(struct rxrpc_local *local, struct rxrpc_txbuf * return ret; } -/* - * ACK transmitter for a local endpoint. The UDP socket locks around each - * transmission, so we can only transmit one packet at a time, ACK, DATA or - * otherwise. - */ -void rxrpc_transmit_ack_packets(struct rxrpc_local *local) -{ - LIST_HEAD(queue); - int ret; - - rxrpc_see_local(local, rxrpc_local_see_tx_ack); - - if (list_empty(&local->ack_tx_queue)) - return; - - spin_lock(&local->ack_tx_lock); - list_splice_tail_init(&local->ack_tx_queue, &queue); - spin_unlock(&local->ack_tx_lock); - - while (!list_empty(&queue)) { - struct rxrpc_txbuf *txb = - list_entry(queue.next, struct rxrpc_txbuf, tx_link); - - ret = rxrpc_send_ack_packet(local, txb); - if (ret < 0 && ret != -ECONNRESET) { - spin_lock(&local->ack_tx_lock); - list_splice_init(&queue, &local->ack_tx_queue); - spin_unlock(&local->ack_tx_lock); - break; - } - - list_del_init(&txb->tx_link); - rxrpc_put_call(txb->call, rxrpc_call_put_send_ack); - rxrpc_put_txbuf(txb, rxrpc_txbuf_put_ack_tx); - } -} - /* * Send an ABORT call packet. */ diff --git a/net/rxrpc/recvmsg.c b/net/rxrpc/recvmsg.c index 3a8576e9daf3..36b25d003cf0 100644 --- a/net/rxrpc/recvmsg.c +++ b/net/rxrpc/recvmsg.c @@ -320,7 +320,6 @@ static int rxrpc_recvmsg_data(struct socket *sock, struct rxrpc_call *call, ret = ret2; goto out; } - rxrpc_transmit_ack_packets(call->peer->local); } else { trace_rxrpc_recvdata(call, rxrpc_recvmsg_cont, seq, rx_pkt_offset, rx_pkt_len, 0); @@ -502,7 +501,6 @@ try_again: if (ret == -EAGAIN) ret = 0; - rxrpc_transmit_ack_packets(call->peer->local); if (!skb_queue_empty(&call->recvmsg_queue)) rxrpc_notify_socket(call); break; @@ -632,7 +630,6 @@ int rxrpc_kernel_recv_data(struct socket *sock, struct rxrpc_call *call, read_phase_complete: ret = 1; out: - rxrpc_transmit_ack_packets(call->peer->local); if (_service) *_service = call->dest_srx.srx_service; mutex_unlock(&call->user_mutex); diff --git a/net/rxrpc/sendmsg.c b/net/rxrpc/sendmsg.c index 2c861c55ed70..9fa7e37f7155 100644 --- a/net/rxrpc/sendmsg.c +++ b/net/rxrpc/sendmsg.c @@ -276,8 +276,6 @@ reload: rxrpc_see_txbuf(txb, rxrpc_txbuf_see_send_more); do { - rxrpc_transmit_ack_packets(call->peer->local); - if (!txb) { size_t remain, bufsize, chunk, offset; diff --git a/net/rxrpc/txbuf.c b/net/rxrpc/txbuf.c index a5054389dfbb..d2cf2aac3adb 100644 --- a/net/rxrpc/txbuf.c +++ b/net/rxrpc/txbuf.c @@ -26,7 +26,6 @@ struct rxrpc_txbuf *rxrpc_alloc_txbuf(struct rxrpc_call *call, u8 packet_type, INIT_LIST_HEAD(&txb->call_link); INIT_LIST_HEAD(&txb->tx_link); refcount_set(&txb->ref, 1); - txb->call = call; txb->call_debug_id = call->debug_id; txb->debug_id = atomic_inc_return(&rxrpc_txbuf_debug_ids); txb->space = sizeof(txb->data); From 09d838a457a89883a926b8b0104d575158fd4b92 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=8D=C3=B1igo=20Huguet?= Date: Fri, 11 Nov 2022 16:36:22 +0100 Subject: [PATCH 1436/2002] wifi: mac80211: fix maybe-unused warning MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In ieee80211_lookup_key, the variable named `local` is unused if compiled without lockdep, getting this warning: net/mac80211/cfg.c: In function ‘ieee80211_lookup_key’: net/mac80211/cfg.c:542:26: error: unused variable ‘local’ [-Werror=unused-variable] struct ieee80211_local *local = sdata->local; ^~~~~ Fix it with __maybe_unused. Fixes: 8cbf0c2ab6df ("wifi: mac80211: refactor some key code") Signed-off-by: Íñigo Huguet Link: https://lore.kernel.org/r/20221111153622.29016-1-ihuguet@redhat.com Signed-off-by: Johannes Berg --- net/mac80211/cfg.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index c848fe04dd44..8f9a2ab502b3 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -576,7 +576,7 @@ static struct ieee80211_key * ieee80211_lookup_key(struct ieee80211_sub_if_data *sdata, int link_id, u8 key_idx, bool pairwise, const u8 *mac_addr) { - struct ieee80211_local *local = sdata->local; + struct ieee80211_local *local __maybe_unused = sdata->local; struct ieee80211_link_data *link = &sdata->deflink; struct ieee80211_key *key; From c1d3214d61d93eb4a3959c6b402230988d0f362d Mon Sep 17 00:00:00 2001 From: JUN-KYU SHIN Date: Fri, 11 Nov 2022 11:33:04 +0900 Subject: [PATCH 1437/2002] wifi: cfg80211: fix comparison of BSS frequencies If the "channel->freq_offset" comparison is omitted in cmp_bss(), BSS with different kHz units cannot be distinguished in the S1G Band. So "freq_offset" should also be included in the comparison. Signed-off-by: JUN-KYU SHIN Link: https://lore.kernel.org/r/20221111023301.6395-1-jk.shin@newratek.com Signed-off-by: Johannes Berg --- net/wireless/scan.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/net/wireless/scan.c b/net/wireless/scan.c index e70302a30013..a39c93753213 100644 --- a/net/wireless/scan.c +++ b/net/wireless/scan.c @@ -1289,7 +1289,8 @@ static int cmp_bss(struct cfg80211_bss *a, int i, r; if (a->channel != b->channel) - return b->channel->center_freq - a->channel->center_freq; + return (b->channel->center_freq * 1000 + b->channel->freq_offset) - + (a->channel->center_freq * 1000 + a->channel->freq_offset); a_ies = rcu_access_pointer(a->ies); if (!a_ies) From 833a9fd28c9b7ccb39a334721379e992dc1c0c89 Mon Sep 17 00:00:00 2001 From: Chen Zhongjin Date: Wed, 9 Nov 2022 17:02:37 +0800 Subject: [PATCH 1438/2002] wifi: cfg80211: Fix not unregister reg_pdev when load_builtin_regdb_keys() fails In regulatory_init_db(), when it's going to return a error, reg_pdev should be unregistered. When load_builtin_regdb_keys() fails it doesn't do it and makes cfg80211 can't be reload with report: sysfs: cannot create duplicate filename '/devices/platform/regulatory.0' ... dump_stack_lvl+0x79/0x9b sysfs_warn_dup.cold+0x1c/0x29 sysfs_create_dir_ns+0x22d/0x290 kobject_add_internal+0x247/0x800 kobject_add+0x135/0x1b0 device_add+0x389/0x1be0 platform_device_add+0x28f/0x790 platform_device_register_full+0x376/0x4b0 regulatory_init+0x9a/0x4b2 [cfg80211] cfg80211_init+0x84/0x113 [cfg80211] ... Fixes: 90a53e4432b1 ("cfg80211: implement regdb signature checking") Signed-off-by: Chen Zhongjin Link: https://lore.kernel.org/r/20221109090237.214127-1-chenzhongjin@huawei.com Signed-off-by: Johannes Berg --- net/wireless/reg.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/net/wireless/reg.c b/net/wireless/reg.c index c3d950d29432..4f3f31244e8b 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -4311,8 +4311,10 @@ static int __init regulatory_init_db(void) return -EINVAL; err = load_builtin_regdb_keys(); - if (err) + if (err) { + platform_device_unregister(reg_pdev); return err; + } /* We always try to get an update for the static regdomain */ err = regulatory_hint_core(cfg80211_world_regdom->alpha2); From 9445096319206814e462b904915b645c4f2bf514 Mon Sep 17 00:00:00 2001 From: Alexander Wetzel Date: Mon, 7 Nov 2022 17:13:28 +0100 Subject: [PATCH 1439/2002] wifi: mac80211: Drop not needed check for NULL ieee80211_get_txq() can only be called with vif != NULL. Remove not needed NULL test in function. Signed-off-by: Alexander Wetzel Reported-by: kernel test robot Reported-by: Dan Carpenter Link: https://lore.kernel.org/r/20221107161328.2883-1-alexander@wetzel-home.de Signed-off-by: Johannes Berg --- net/mac80211/tx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 165ac0711d71..3ed5ca4a2bae 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -1343,7 +1343,7 @@ static struct txq_info *ieee80211_get_txq(struct ieee80211_local *local, return NULL; txq = sta->sta.txq[tid]; - } else if (vif) { + } else { txq = vif->txq; } From b2ddde566de409a3dbf708fe89ecbe114dd14cc3 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 2 Nov 2022 09:47:47 +0100 Subject: [PATCH 1440/2002] wifi: mac80211: remove unnecessary synchronize_net() The call to ieee80211_do_stop() right after will also do synchronize_rcu() to ensure the SDATA_STATE_RUNNING bit is cleared, so we don't need to synchronize_net() here. Change-Id: Id9f9ffcf195002013e5d9fde288877d219780864 Signed-off-by: Johannes Berg --- net/mac80211/iface.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index a5782927a93b..7b2843df3813 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -1849,8 +1849,7 @@ static int ieee80211_runtime_change_iftype(struct ieee80211_sub_if_data *sdata, ieee80211_stop_vif_queues(local, sdata, IEEE80211_QUEUE_STOP_REASON_IFTYPE_CHANGE); - synchronize_net(); - + /* do_stop will synchronize_rcu() first thing */ ieee80211_do_stop(sdata, false); ieee80211_teardown_sdata(sdata); From 61e41e5dfcc22e5e65b6537453fd2f03ac768b82 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 17 Oct 2022 09:03:48 +0200 Subject: [PATCH 1441/2002] wifi: cfg80211: use bss_from_pub() instead of container_of() There's no need to open-code container_of() when we have bss_from_pub(). Use it. Change-Id: I074723717909ba211a40e6499f0c36df0e2ba4be Signed-off-by: Johannes Berg --- net/wireless/scan.c | 41 +++++++++++------------------------------ 1 file changed, 11 insertions(+), 30 deletions(-) diff --git a/net/wireless/scan.c b/net/wireless/scan.c index a39c93753213..8cf405776dde 100644 --- a/net/wireless/scan.c +++ b/net/wireless/scan.c @@ -158,9 +158,8 @@ static inline void bss_ref_put(struct cfg80211_registered_device *rdev, if (bss->pub.hidden_beacon_bss) { struct cfg80211_internal_bss *hbss; - hbss = container_of(bss->pub.hidden_beacon_bss, - struct cfg80211_internal_bss, - pub); + + hbss = bss_from_pub(bss->pub.hidden_beacon_bss); hbss->refcount--; if (hbss->refcount == 0) bss_free(hbss); @@ -169,9 +168,7 @@ static inline void bss_ref_put(struct cfg80211_registered_device *rdev, if (bss->pub.transmitted_bss) { struct cfg80211_internal_bss *tbss; - tbss = container_of(bss->pub.transmitted_bss, - struct cfg80211_internal_bss, - pub); + tbss = bss_from_pub(bss->pub.transmitted_bss); tbss->refcount--; if (tbss->refcount == 0) bss_free(tbss); @@ -1791,13 +1788,8 @@ cfg80211_bss_update(struct cfg80211_registered_device *rdev, /* This must be before the call to bss_ref_get */ if (tmp->pub.transmitted_bss) { - struct cfg80211_internal_bss *pbss = - container_of(tmp->pub.transmitted_bss, - struct cfg80211_internal_bss, - pub); - new->pub.transmitted_bss = tmp->pub.transmitted_bss; - bss_ref_get(rdev, pbss); + bss_ref_get(rdev, bss_from_pub(tmp->pub.transmitted_bss)); } list_add_tail(&new->list, &rdev->bss_list); @@ -2570,15 +2562,12 @@ EXPORT_SYMBOL(cfg80211_inform_bss_frame_data); void cfg80211_ref_bss(struct wiphy *wiphy, struct cfg80211_bss *pub) { struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); - struct cfg80211_internal_bss *bss; if (!pub) return; - bss = container_of(pub, struct cfg80211_internal_bss, pub); - spin_lock_bh(&rdev->bss_lock); - bss_ref_get(rdev, bss); + bss_ref_get(rdev, bss_from_pub(pub)); spin_unlock_bh(&rdev->bss_lock); } EXPORT_SYMBOL(cfg80211_ref_bss); @@ -2586,15 +2575,12 @@ EXPORT_SYMBOL(cfg80211_ref_bss); void cfg80211_put_bss(struct wiphy *wiphy, struct cfg80211_bss *pub) { struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); - struct cfg80211_internal_bss *bss; if (!pub) return; - bss = container_of(pub, struct cfg80211_internal_bss, pub); - spin_lock_bh(&rdev->bss_lock); - bss_ref_put(rdev, bss); + bss_ref_put(rdev, bss_from_pub(pub)); spin_unlock_bh(&rdev->bss_lock); } EXPORT_SYMBOL(cfg80211_put_bss); @@ -2608,7 +2594,7 @@ void cfg80211_unlink_bss(struct wiphy *wiphy, struct cfg80211_bss *pub) if (WARN_ON(!pub)) return; - bss = container_of(pub, struct cfg80211_internal_bss, pub); + bss = bss_from_pub(pub); spin_lock_bh(&rdev->bss_lock); if (list_empty(&bss->list)) @@ -2617,8 +2603,7 @@ void cfg80211_unlink_bss(struct wiphy *wiphy, struct cfg80211_bss *pub) list_for_each_entry_safe(nontrans_bss, tmp, &pub->nontrans_list, nontrans_list) { - tmp1 = container_of(nontrans_bss, - struct cfg80211_internal_bss, pub); + tmp1 = bss_from_pub(nontrans_bss); if (__cfg80211_unlink_bss(rdev, tmp1)) rdev->bss_generation++; } @@ -2675,9 +2660,7 @@ void cfg80211_update_assoc_bss_entry(struct wireless_dev *wdev, /* use transmitting bss */ if (cbss->pub.transmitted_bss) - cbss = container_of(cbss->pub.transmitted_bss, - struct cfg80211_internal_bss, - pub); + cbss = bss_from_pub(cbss->pub.transmitted_bss); cbss->pub.channel = chan; @@ -2706,8 +2689,7 @@ void cfg80211_update_assoc_bss_entry(struct wireless_dev *wdev, list_for_each_entry_safe(nontrans_bss, tmp, &new->pub.nontrans_list, nontrans_list) { - bss = container_of(nontrans_bss, - struct cfg80211_internal_bss, pub); + bss = bss_from_pub(nontrans_bss); if (__cfg80211_unlink_bss(rdev, bss)) rdev->bss_generation++; } @@ -2724,8 +2706,7 @@ void cfg80211_update_assoc_bss_entry(struct wireless_dev *wdev, list_for_each_entry_safe(nontrans_bss, tmp, &cbss->pub.nontrans_list, nontrans_list) { - bss = container_of(nontrans_bss, - struct cfg80211_internal_bss, pub); + bss = bss_from_pub(nontrans_bss); bss->pub.channel = chan; rb_erase(&bss->rbn, &rdev->bss_tree); rb_insert_bss(rdev, bss); From 8950b5988a9a2957b4c2c6f20143dfc2a0230a74 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 14 Oct 2022 18:56:11 +0200 Subject: [PATCH 1442/2002] wifi: mac80211: don't parse multi-BSSID in assoc resp It's not valid to have the multiple BSSID element in the association response (per 802.11 REVme D1.0), so don't try to parse it there, but only in the fallback beacon elements if needed. The other case that was parsing association requests was already changed in a previous commit. Change-Id: I659d2ef1253e079cc71c46a017044e116e31c024 Signed-off-by: Johannes Berg --- net/mac80211/mlme.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index a804e0220ed7..0aee2392dd29 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -3932,7 +3932,6 @@ static bool ieee80211_assoc_config_link(struct ieee80211_link_data *link, struct ieee80211_elems_parse_params parse_params = { .start = elem_start, .len = elem_len, - .bss = cbss, .link_id = link_id == assoc_data->assoc_link_id ? -1 : link_id, .from_ap = true, }; @@ -4017,6 +4016,7 @@ static bool ieee80211_assoc_config_link(struct ieee80211_link_data *link, parse_params.start = bss_ies->data; parse_params.len = bss_ies->len; + parse_params.bss = cbss; bss_elems = ieee802_11_parse_elems_full(&parse_params); if (!bss_elems) { ret = false; From 209d70d34a7fd32e6fb6784f107913b0d18fe501 Mon Sep 17 00:00:00 2001 From: Kieran Frewen Date: Mon, 7 Nov 2022 11:16:02 +1300 Subject: [PATCH 1443/2002] wifi: mac80211: update TIM for S1G specification changes Updates to the TIM information element to match changes made in the IEEE Std 802.11ah-2020. Signed-off-by: Kieran Frewen Co-developed-by: Gilad Itzkovitch Signed-off-by: Gilad Itzkovitch Link: https://lore.kernel.org/r/20221106221602.25714-1-gilad.itzkovitch@morsemicro.com [use skb_put_data/skb_put_u8] Signed-off-by: Johannes Berg --- net/mac80211/tx.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 3ed5ca4a2bae..774e62860c45 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -4774,9 +4774,9 @@ static void __ieee80211_beacon_add_tim(struct ieee80211_sub_if_data *sdata, ps->dtim_count--; } - tim = pos = skb_put(skb, 6); + tim = pos = skb_put(skb, 5); *pos++ = WLAN_EID_TIM; - *pos++ = 4; + *pos++ = 3; *pos++ = ps->dtim_count; *pos++ = link_conf->dtim_period; @@ -4807,13 +4807,17 @@ static void __ieee80211_beacon_add_tim(struct ieee80211_sub_if_data *sdata, /* Bitmap control */ *pos++ = n1 | aid0; /* Part Virt Bitmap */ - skb_put(skb, n2 - n1); - memcpy(pos, ps->tim + n1, n2 - n1 + 1); + skb_put_data(skb, ps->tim + n1, n2 - n1 + 1); tim[1] = n2 - n1 + 4; } else { *pos++ = aid0; /* Bitmap control */ - *pos++ = 0; /* Part Virt Bitmap */ + + if (ieee80211_get_link_sband(link)->band != NL80211_BAND_S1GHZ) { + tim[1] = 4; + /* Part Virt Bitmap */ + skb_put_u8(skb, 0); + } } } From 7d360f6061db01830adfdb1eaa3977b19db0c30b Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Mon, 10 Oct 2022 11:43:38 +0200 Subject: [PATCH 1444/2002] wifi: mac80211: add support for restricting netdev features per vif This can be used to selectively disable feature flags for checksum offload, scatter/gather or GSO by changing vif->netdev_features. Removing features from vif->netdev_features does not affect the netdev features themselves, but instead fixes up skbs in the tx path so that the offloads are not needed in the driver. Aside from making it easier to deal with vif type based hardware limitations, this also makes it possible to optimize performance on hardware without native GSO support by declaring GSO support in hw->netdev_features and removing it from vif->netdev_features. This allows mac80211 to handle GSO segmentation after the sta lookup, but before itxq enqueue, thus reducing the number of unnecessary sta lookups, as well as some other per-packet processing. Signed-off-by: Felix Fietkau Link: https://lore.kernel.org/r/20221010094338.78070-1-nbd@nbd.name Signed-off-by: Johannes Berg --- include/net/fq_impl.h | 16 +- include/net/mac80211.h | 5 + net/mac80211/iface.c | 1 + net/mac80211/tx.c | 345 ++++++++++++++++++++++++++--------------- 4 files changed, 233 insertions(+), 134 deletions(-) diff --git a/include/net/fq_impl.h b/include/net/fq_impl.h index 524b510f1c68..9467e33dfb36 100644 --- a/include/net/fq_impl.h +++ b/include/net/fq_impl.h @@ -200,6 +200,7 @@ static void fq_tin_enqueue(struct fq *fq, fq_skb_free_t free_func) { struct fq_flow *flow; + struct sk_buff *next; bool oom; lockdep_assert_held(&fq->lock); @@ -214,11 +215,15 @@ static void fq_tin_enqueue(struct fq *fq, } flow->tin = tin; - flow->backlog += skb->len; - tin->backlog_bytes += skb->len; - tin->backlog_packets++; - fq->memory_usage += skb->truesize; - fq->backlog++; + skb_list_walk_safe(skb, skb, next) { + skb_mark_not_on_list(skb); + flow->backlog += skb->len; + tin->backlog_bytes += skb->len; + tin->backlog_packets++; + fq->memory_usage += skb->truesize; + fq->backlog++; + __skb_queue_tail(&flow->queue, skb); + } if (list_empty(&flow->flowchain)) { flow->deficit = fq->quantum; @@ -226,7 +231,6 @@ static void fq_tin_enqueue(struct fq *fq, &tin->new_flows); } - __skb_queue_tail(&flow->queue, skb); oom = (fq->memory_usage > fq->memory_limit); while (fq->backlog > fq->limit || oom) { flow = fq_find_fattest_flow(fq); diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 721c450a9ccd..689da327ce2e 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -1807,6 +1807,10 @@ struct ieee80211_vif_cfg { * @addr: address of this interface * @p2p: indicates whether this AP or STA interface is a p2p * interface, i.e. a GO or p2p-sta respectively + * @netdev_features: tx netdev features supported by the hardware for this + * vif. mac80211 initializes this to hw->netdev_features, and the driver + * can mask out specific tx features. mac80211 will handle software fixup + * for masked offloads (GSO, CSUM) * @driver_flags: flags/capabilities the driver has for this interface, * these need to be set (or cleared) when the interface is added * or, if supported by the driver, the interface type is changed @@ -1848,6 +1852,7 @@ struct ieee80211_vif { struct ieee80211_txq *txq; + netdev_features_t netdev_features; u32 driver_flags; u32 offload_flags; diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index 7b2843df3813..d49a5906a943 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -2178,6 +2178,7 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name, ndev->priv_flags |= IFF_LIVE_ADDR_CHANGE; ndev->hw_features |= ndev->features & MAC80211_SUPPORTED_FEATURES_TX; + sdata->vif.netdev_features = local->hw.netdev_features; netdev_set_default_ethtool_ops(ndev, &ieee80211_ethtool_ops); diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 774e62860c45..2171cd1ca807 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -1355,7 +1355,11 @@ static struct txq_info *ieee80211_get_txq(struct ieee80211_local *local, static void ieee80211_set_skb_enqueue_time(struct sk_buff *skb) { - IEEE80211_SKB_CB(skb)->control.enqueue_time = codel_get_time(); + struct sk_buff *next; + codel_time_t now = codel_get_time(); + + skb_list_walk_safe(skb, skb, next) + IEEE80211_SKB_CB(skb)->control.enqueue_time = now; } static u32 codel_skb_len_func(const struct sk_buff *skb) @@ -3578,21 +3582,156 @@ ieee80211_xmit_fast_finish(struct ieee80211_sub_if_data *sdata, return TX_CONTINUE; } +static netdev_features_t +ieee80211_sdata_netdev_features(struct ieee80211_sub_if_data *sdata) +{ + if (sdata->vif.type != NL80211_IFTYPE_AP_VLAN) + return sdata->vif.netdev_features; + + if (!sdata->bss) + return 0; + + sdata = container_of(sdata->bss, struct ieee80211_sub_if_data, u.ap); + return sdata->vif.netdev_features; +} + +static struct sk_buff * +ieee80211_tx_skb_fixup(struct sk_buff *skb, netdev_features_t features) +{ + if (skb_is_gso(skb)) { + struct sk_buff *segs; + + segs = skb_gso_segment(skb, features); + if (!segs) + return skb; + if (IS_ERR(segs)) + goto free; + + consume_skb(skb); + return segs; + } + + if (skb_needs_linearize(skb, features) && __skb_linearize(skb)) + goto free; + + if (skb->ip_summed == CHECKSUM_PARTIAL) { + int ofs = skb_checksum_start_offset(skb); + + if (skb->encapsulation) + skb_set_inner_transport_header(skb, ofs); + else + skb_set_transport_header(skb, ofs); + + if (skb_csum_hwoffload_help(skb, features)) + goto free; + } + + skb_mark_not_on_list(skb); + return skb; + +free: + kfree_skb(skb); + return NULL; +} + +static void __ieee80211_xmit_fast(struct ieee80211_sub_if_data *sdata, + struct sta_info *sta, + struct ieee80211_fast_tx *fast_tx, + struct sk_buff *skb, u8 tid, bool ampdu) +{ + struct ieee80211_local *local = sdata->local; + struct ieee80211_hdr *hdr = (void *)fast_tx->hdr; + struct ieee80211_tx_info *info; + struct ieee80211_tx_data tx; + ieee80211_tx_result r; + int hw_headroom = sdata->local->hw.extra_tx_headroom; + int extra_head = fast_tx->hdr_len - (ETH_HLEN - 2); + struct ethhdr eth; + + skb = skb_share_check(skb, GFP_ATOMIC); + if (unlikely(!skb)) + return; + + if ((hdr->frame_control & cpu_to_le16(IEEE80211_STYPE_QOS_DATA)) && + ieee80211_amsdu_aggregate(sdata, sta, fast_tx, skb)) + return; + + /* will not be crypto-handled beyond what we do here, so use false + * as the may-encrypt argument for the resize to not account for + * more room than we already have in 'extra_head' + */ + if (unlikely(ieee80211_skb_resize(sdata, skb, + max_t(int, extra_head + hw_headroom - + skb_headroom(skb), 0), + ENCRYPT_NO))) + goto free; + + memcpy(ð, skb->data, ETH_HLEN - 2); + hdr = skb_push(skb, extra_head); + memcpy(skb->data, fast_tx->hdr, fast_tx->hdr_len); + memcpy(skb->data + fast_tx->da_offs, eth.h_dest, ETH_ALEN); + memcpy(skb->data + fast_tx->sa_offs, eth.h_source, ETH_ALEN); + + info = IEEE80211_SKB_CB(skb); + memset(info, 0, sizeof(*info)); + info->band = fast_tx->band; + info->control.vif = &sdata->vif; + info->flags = IEEE80211_TX_CTL_FIRST_FRAGMENT | + IEEE80211_TX_CTL_DONTFRAG | + (ampdu ? IEEE80211_TX_CTL_AMPDU : 0); + info->control.flags = IEEE80211_TX_CTRL_FAST_XMIT | + u32_encode_bits(IEEE80211_LINK_UNSPECIFIED, + IEEE80211_TX_CTRL_MLO_LINK); + +#ifdef CONFIG_MAC80211_DEBUGFS + if (local->force_tx_status) + info->flags |= IEEE80211_TX_CTL_REQ_TX_STATUS; +#endif + + if (hdr->frame_control & cpu_to_le16(IEEE80211_STYPE_QOS_DATA)) { + tid = skb->priority & IEEE80211_QOS_CTL_TAG1D_MASK; + *ieee80211_get_qos_ctl(hdr) = tid; + } + + __skb_queue_head_init(&tx.skbs); + + tx.flags = IEEE80211_TX_UNICAST; + tx.local = local; + tx.sdata = sdata; + tx.sta = sta; + tx.key = fast_tx->key; + + if (ieee80211_queue_skb(local, sdata, sta, skb)) + return; + + tx.skb = skb; + r = ieee80211_xmit_fast_finish(sdata, sta, fast_tx->pn_offs, + fast_tx->key, &tx); + tx.skb = NULL; + if (r == TX_DROP) + goto free; + + if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) + sdata = container_of(sdata->bss, + struct ieee80211_sub_if_data, u.ap); + + __skb_queue_tail(&tx.skbs, skb); + ieee80211_tx_frags(local, &sdata->vif, sta, &tx.skbs, false); + return; + +free: + kfree_skb(skb); +} + static bool ieee80211_xmit_fast(struct ieee80211_sub_if_data *sdata, struct sta_info *sta, struct ieee80211_fast_tx *fast_tx, struct sk_buff *skb) { - struct ieee80211_local *local = sdata->local; u16 ethertype = (skb->data[12] << 8) | skb->data[13]; - int extra_head = fast_tx->hdr_len - (ETH_HLEN - 2); - int hw_headroom = sdata->local->hw.extra_tx_headroom; - struct ethhdr eth; - struct ieee80211_tx_info *info; struct ieee80211_hdr *hdr = (void *)fast_tx->hdr; - struct ieee80211_tx_data tx; - ieee80211_tx_result r; struct tid_ampdu_tx *tid_tx = NULL; + struct sk_buff *next; u8 tid = IEEE80211_NUM_TIDS; /* control port protocol needs a lot of special handling */ @@ -3619,80 +3758,15 @@ static bool ieee80211_xmit_fast(struct ieee80211_sub_if_data *sdata, } /* after this point (skb is modified) we cannot return false */ - - skb = skb_share_check(skb, GFP_ATOMIC); - if (unlikely(!skb)) + skb = ieee80211_tx_skb_fixup(skb, ieee80211_sdata_netdev_features(sdata)); + if (!skb) return true; - if ((hdr->frame_control & cpu_to_le16(IEEE80211_STYPE_QOS_DATA)) && - ieee80211_amsdu_aggregate(sdata, sta, fast_tx, skb)) - return true; - - /* will not be crypto-handled beyond what we do here, so use false - * as the may-encrypt argument for the resize to not account for - * more room than we already have in 'extra_head' - */ - if (unlikely(ieee80211_skb_resize(sdata, skb, - max_t(int, extra_head + hw_headroom - - skb_headroom(skb), 0), - ENCRYPT_NO))) { - kfree_skb(skb); - return true; + skb_list_walk_safe(skb, skb, next) { + skb_mark_not_on_list(skb); + __ieee80211_xmit_fast(sdata, sta, fast_tx, skb, tid, tid_tx); } - memcpy(ð, skb->data, ETH_HLEN - 2); - hdr = skb_push(skb, extra_head); - memcpy(skb->data, fast_tx->hdr, fast_tx->hdr_len); - memcpy(skb->data + fast_tx->da_offs, eth.h_dest, ETH_ALEN); - memcpy(skb->data + fast_tx->sa_offs, eth.h_source, ETH_ALEN); - - info = IEEE80211_SKB_CB(skb); - memset(info, 0, sizeof(*info)); - info->band = fast_tx->band; - info->control.vif = &sdata->vif; - info->flags = IEEE80211_TX_CTL_FIRST_FRAGMENT | - IEEE80211_TX_CTL_DONTFRAG | - (tid_tx ? IEEE80211_TX_CTL_AMPDU : 0); - info->control.flags = IEEE80211_TX_CTRL_FAST_XMIT | - u32_encode_bits(IEEE80211_LINK_UNSPECIFIED, - IEEE80211_TX_CTRL_MLO_LINK); - -#ifdef CONFIG_MAC80211_DEBUGFS - if (local->force_tx_status) - info->flags |= IEEE80211_TX_CTL_REQ_TX_STATUS; -#endif - - if (hdr->frame_control & cpu_to_le16(IEEE80211_STYPE_QOS_DATA)) { - tid = skb->priority & IEEE80211_QOS_CTL_TAG1D_MASK; - *ieee80211_get_qos_ctl(hdr) = tid; - } - - __skb_queue_head_init(&tx.skbs); - - tx.flags = IEEE80211_TX_UNICAST; - tx.local = local; - tx.sdata = sdata; - tx.sta = sta; - tx.key = fast_tx->key; - - if (ieee80211_queue_skb(local, sdata, sta, skb)) - return true; - - tx.skb = skb; - r = ieee80211_xmit_fast_finish(sdata, sta, fast_tx->pn_offs, - fast_tx->key, &tx); - tx.skb = NULL; - if (r == TX_DROP) { - kfree_skb(skb); - return true; - } - - if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) - sdata = container_of(sdata->bss, - struct ieee80211_sub_if_data, u.ap); - - __skb_queue_tail(&tx.skbs, skb); - ieee80211_tx_frags(local, &sdata->vif, sta, &tx.skbs, false); return true; } @@ -4192,31 +4266,14 @@ void __ieee80211_subif_start_xmit(struct sk_buff *skb, goto out; } - if (skb_is_gso(skb)) { - struct sk_buff *segs; - - segs = skb_gso_segment(skb, 0); - if (IS_ERR(segs)) { - goto out_free; - } else if (segs) { - consume_skb(skb); - skb = segs; - } - } else { - /* we cannot process non-linear frames on this path */ - if (skb_linearize(skb)) - goto out_free; - - /* the frame could be fragmented, software-encrypted, and other - * things so we cannot really handle checksum offload with it - - * fix it up in software before we handle anything else. - */ - if (skb->ip_summed == CHECKSUM_PARTIAL) { - skb_set_transport_header(skb, - skb_checksum_start_offset(skb)); - if (skb_checksum_help(skb)) - goto out_free; - } + /* the frame could be fragmented, software-encrypted, and other + * things so we cannot really handle checksum or GSO offload. + * fix it up in software before we handle anything else. + */ + skb = ieee80211_tx_skb_fixup(skb, 0); + if (!skb) { + len = 0; + goto out; } skb_list_walk_safe(skb, skb, next) { @@ -4434,9 +4491,11 @@ normal: return NETDEV_TX_OK; } -static bool ieee80211_tx_8023(struct ieee80211_sub_if_data *sdata, - struct sk_buff *skb, struct sta_info *sta, - bool txpending) + + +static bool __ieee80211_tx_8023(struct ieee80211_sub_if_data *sdata, + struct sk_buff *skb, struct sta_info *sta, + bool txpending) { struct ieee80211_local *local = sdata->local; struct ieee80211_tx_control control = {}; @@ -4445,14 +4504,6 @@ static bool ieee80211_tx_8023(struct ieee80211_sub_if_data *sdata, unsigned long flags; int q = info->hw_queue; - if (sta) - sk_pacing_shift_update(skb->sk, local->hw.tx_sk_pacing_shift); - - ieee80211_tpt_led_trig_tx(local, skb->len); - - if (ieee80211_queue_skb(local, sdata, sta, skb)) - return true; - spin_lock_irqsave(&local->queue_stop_reason_lock, flags); if (local->queue_stop_reasons[q] || @@ -4479,6 +4530,26 @@ static bool ieee80211_tx_8023(struct ieee80211_sub_if_data *sdata, return true; } +static bool ieee80211_tx_8023(struct ieee80211_sub_if_data *sdata, + struct sk_buff *skb, struct sta_info *sta, + bool txpending) +{ + struct ieee80211_local *local = sdata->local; + struct sk_buff *next; + bool ret = true; + + if (ieee80211_queue_skb(local, sdata, sta, skb)) + return true; + + skb_list_walk_safe(skb, skb, next) { + skb_mark_not_on_list(skb); + if (!__ieee80211_tx_8023(sdata, skb, sta, txpending)) + ret = false; + } + + return ret; +} + static void ieee80211_8023_xmit(struct ieee80211_sub_if_data *sdata, struct net_device *dev, struct sta_info *sta, struct ieee80211_key *key, struct sk_buff *skb) @@ -4486,9 +4557,13 @@ static void ieee80211_8023_xmit(struct ieee80211_sub_if_data *sdata, struct ieee80211_tx_info *info; struct ieee80211_local *local = sdata->local; struct tid_ampdu_tx *tid_tx; + struct sk_buff *seg, *next; + unsigned int skbs = 0, len = 0; + u16 queue; u8 tid; - skb_set_queue_mapping(skb, ieee80211_select_queue(sdata, sta, skb)); + queue = ieee80211_select_queue(sdata, sta, skb); + skb_set_queue_mapping(skb, queue); if (unlikely(test_bit(SCAN_SW_SCANNING, &local->scanning)) && test_bit(SDATA_STATE_OFFCHANNEL, &sdata->state)) @@ -4498,9 +4573,6 @@ static void ieee80211_8023_xmit(struct ieee80211_sub_if_data *sdata, if (unlikely(!skb)) return; - info = IEEE80211_SKB_CB(skb); - memset(info, 0, sizeof(*info)); - ieee80211_aggr_check(sdata, sta, skb); tid = skb->priority & IEEE80211_QOS_CTL_TAG1D_MASK; @@ -4514,22 +4586,20 @@ static void ieee80211_8023_xmit(struct ieee80211_sub_if_data *sdata, return; } - info->flags |= IEEE80211_TX_CTL_AMPDU; if (tid_tx->timeout) tid_tx->last_tx = jiffies; } - if (unlikely(skb->sk && - skb_shinfo(skb)->tx_flags & SKBTX_WIFI_STATUS)) - info->ack_frame_id = ieee80211_store_ack_skb(local, skb, - &info->flags, NULL); + skb = ieee80211_tx_skb_fixup(skb, ieee80211_sdata_netdev_features(sdata)); + if (!skb) + return; - info->hw_queue = sdata->vif.hw_queue[skb_get_queue_mapping(skb)]; + info = IEEE80211_SKB_CB(skb); + memset(info, 0, sizeof(*info)); + if (tid_tx) + info->flags |= IEEE80211_TX_CTL_AMPDU; - dev_sw_netstats_tx_add(dev, 1, skb->len); - - sta->deflink.tx_stats.bytes[skb_get_queue_mapping(skb)] += skb->len; - sta->deflink.tx_stats.packets[skb_get_queue_mapping(skb)]++; + info->hw_queue = sdata->vif.hw_queue[queue]; if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) sdata = container_of(sdata->bss, @@ -4541,6 +4611,24 @@ static void ieee80211_8023_xmit(struct ieee80211_sub_if_data *sdata, if (key) info->control.hw_key = &key->conf; + skb_list_walk_safe(skb, seg, next) { + skbs++; + len += seg->len; + if (seg != skb) + memcpy(IEEE80211_SKB_CB(seg), info, sizeof(*info)); + } + + if (unlikely(skb->sk && + skb_shinfo(skb)->tx_flags & SKBTX_WIFI_STATUS)) + info->ack_frame_id = ieee80211_store_ack_skb(local, skb, + &info->flags, NULL); + + dev_sw_netstats_tx_add(dev, skbs, len); + sta->deflink.tx_stats.packets[queue] += skbs; + sta->deflink.tx_stats.bytes[queue] += len; + + ieee80211_tpt_led_trig_tx(local, len); + ieee80211_tx_8023(sdata, skb, sta, false); return; @@ -4582,6 +4670,7 @@ netdev_tx_t ieee80211_subif_start_xmit_8023(struct sk_buff *skb, key->conf.cipher == WLAN_CIPHER_SUITE_TKIP)) goto skip_offload; + sk_pacing_shift_update(skb->sk, sdata->local->hw.tx_sk_pacing_shift); ieee80211_8023_xmit(sdata, dev, sta, key, skb); goto out; From 94b9b9de05b62ac54d8766caa9865fb4d82cc47e Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Thu, 1 Dec 2022 14:57:30 +0100 Subject: [PATCH 1445/2002] wifi: mac80211: fix and simplify unencrypted drop check for mesh ieee80211_drop_unencrypted is called from ieee80211_rx_h_mesh_fwding and ieee80211_frame_allowed. Since ieee80211_rx_h_mesh_fwding can forward packets for other mesh nodes and is called earlier, it needs to check the decryptions status and if the packet is using the control protocol on its own, instead of deferring to the later call from ieee80211_frame_allowed. Because of that, ieee80211_drop_unencrypted has a mesh specific check that skips over the mesh header in order to check the payload protocol. This code is invalid when called from ieee80211_frame_allowed, since that happens after the 802.11->802.3 conversion. Fix this by moving the mesh specific check directly into ieee80211_rx_h_mesh_fwding. Signed-off-by: Felix Fietkau Link: https://lore.kernel.org/r/20221201135730.19723-1-nbd@nbd.name Signed-off-by: Johannes Berg --- net/mac80211/rx.c | 38 ++++++++++---------------------------- 1 file changed, 10 insertions(+), 28 deletions(-) diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index c28c6fbf786e..7e3ab6e1b28f 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -2403,7 +2403,6 @@ static int ieee80211_802_1x_port_control(struct ieee80211_rx_data *rx) static int ieee80211_drop_unencrypted(struct ieee80211_rx_data *rx, __le16 fc) { - struct ieee80211_hdr *hdr = (void *)rx->skb->data; struct sk_buff *skb = rx->skb; struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb); @@ -2414,31 +2413,6 @@ static int ieee80211_drop_unencrypted(struct ieee80211_rx_data *rx, __le16 fc) if (status->flag & RX_FLAG_DECRYPTED) return 0; - /* check mesh EAPOL frames first */ - if (unlikely(rx->sta && ieee80211_vif_is_mesh(&rx->sdata->vif) && - ieee80211_is_data(fc))) { - struct ieee80211s_hdr *mesh_hdr; - u16 hdr_len = ieee80211_hdrlen(fc); - u16 ethertype_offset; - __be16 ethertype; - - if (!ether_addr_equal(hdr->addr1, rx->sdata->vif.addr)) - goto drop_check; - - /* make sure fixed part of mesh header is there, also checks skb len */ - if (!pskb_may_pull(rx->skb, hdr_len + 6)) - goto drop_check; - - mesh_hdr = (struct ieee80211s_hdr *)(skb->data + hdr_len); - ethertype_offset = hdr_len + ieee80211_get_mesh_hdrlen(mesh_hdr) + - sizeof(rfc1042_header); - - if (skb_copy_bits(rx->skb, ethertype_offset, ðertype, 2) == 0 && - ethertype == rx->sdata->control_port_protocol) - return 0; - } - -drop_check: /* Drop unencrypted frames if key is set. */ if (unlikely(!ieee80211_has_protected(fc) && !ieee80211_is_any_nullfunc(fc) && @@ -2892,8 +2866,16 @@ ieee80211_rx_h_mesh_fwding(struct ieee80211_rx_data *rx) hdr = (struct ieee80211_hdr *) skb->data; mesh_hdr = (struct ieee80211s_hdr *) (skb->data + hdrlen); - if (ieee80211_drop_unencrypted(rx, hdr->frame_control)) - return RX_DROP_MONITOR; + if (ieee80211_drop_unencrypted(rx, hdr->frame_control)) { + int offset = hdrlen + ieee80211_get_mesh_hdrlen(mesh_hdr) + + sizeof(rfc1042_header); + __be16 ethertype; + + if (!ether_addr_equal(hdr->addr1, rx->sdata->vif.addr) || + skb_copy_bits(rx->skb, offset, ðertype, 2) != 0 || + ethertype != rx->sdata->control_port_protocol) + return RX_DROP_MONITOR; + } /* frame is in RMC, don't forward */ if (ieee80211_is_data(hdr->frame_control) && From b376d96362d815331dff0c498a338ebe4c8c5bde Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Wed, 28 Sep 2022 10:49:11 +0200 Subject: [PATCH 1446/2002] wifi: mt76: move mt76_rate_power from core to mt76x02 driver code Its layout and code is mt76x02 specific Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/debugfs.c | 19 ------------------- drivers/net/wireless/mediatek/mt76/mt76.h | 15 --------------- .../wireless/mediatek/mt76/mt76x0/eeprom.c | 4 ++-- .../wireless/mediatek/mt76/mt76x0/eeprom.h | 2 +- .../net/wireless/mediatek/mt76/mt76x0/init.c | 2 +- .../net/wireless/mediatek/mt76/mt76x0/phy.c | 10 +++++----- drivers/net/wireless/mediatek/mt76/mt76x02.h | 15 +++++++++++++++ .../wireless/mediatek/mt76/mt76x02_debugfs.c | 19 +++++++++++++++++++ .../net/wireless/mediatek/mt76/mt76x02_phy.c | 8 ++++---- .../net/wireless/mediatek/mt76/mt76x02_phy.h | 6 +++--- .../net/wireless/mediatek/mt76/mt76x02_txrx.c | 12 ++++++------ .../wireless/mediatek/mt76/mt76x2/eeprom.c | 2 +- .../wireless/mediatek/mt76/mt76x2/eeprom.h | 2 +- .../net/wireless/mediatek/mt76/mt76x2/init.c | 2 +- .../net/wireless/mediatek/mt76/mt76x2/phy.c | 6 +++--- 15 files changed, 62 insertions(+), 62 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/debugfs.c b/drivers/net/wireless/mediatek/mt76/debugfs.c index 47e9911ee9fe..11b0b3d62f29 100644 --- a/drivers/net/wireless/mediatek/mt76/debugfs.c +++ b/drivers/net/wireless/mediatek/mt76/debugfs.c @@ -100,23 +100,6 @@ void mt76_seq_puts_array(struct seq_file *file, const char *str, } EXPORT_SYMBOL_GPL(mt76_seq_puts_array); -static int mt76_read_rate_txpower(struct seq_file *s, void *data) -{ - struct mt76_dev *dev = dev_get_drvdata(s->private); - - mt76_seq_puts_array(s, "CCK", dev->rate_power.cck, - ARRAY_SIZE(dev->rate_power.cck)); - mt76_seq_puts_array(s, "OFDM", dev->rate_power.ofdm, - ARRAY_SIZE(dev->rate_power.ofdm)); - mt76_seq_puts_array(s, "STBC", dev->rate_power.stbc, - ARRAY_SIZE(dev->rate_power.stbc)); - mt76_seq_puts_array(s, "HT", dev->rate_power.ht, - ARRAY_SIZE(dev->rate_power.ht)); - mt76_seq_puts_array(s, "VHT", dev->rate_power.vht, - ARRAY_SIZE(dev->rate_power.vht)); - return 0; -} - struct dentry * mt76_register_debugfs_fops(struct mt76_phy *phy, const struct file_operations *ops) @@ -137,8 +120,6 @@ mt76_register_debugfs_fops(struct mt76_phy *phy, debugfs_create_blob("eeprom", 0400, dir, &dev->eeprom); if (dev->otp.data) debugfs_create_blob("otp", 0400, dir, &dev->otp); - debugfs_create_devm_seqfile(dev->dev, "rate_txpower", dir, - mt76_read_rate_txpower); debugfs_create_devm_seqfile(dev->dev, "rx-queues", dir, mt76_rx_queues_read); diff --git a/drivers/net/wireless/mediatek/mt76/mt76.h b/drivers/net/wireless/mediatek/mt76/mt76.h index 87db9498dea4..26febc0c261a 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76.h +++ b/drivers/net/wireless/mediatek/mt76/mt76.h @@ -470,19 +470,6 @@ struct mt76_sband { struct mt76_channel_state *chan; }; -struct mt76_rate_power { - union { - struct { - s8 cck[4]; - s8 ofdm[8]; - s8 stbc[10]; - s8 ht[16]; - s8 vht[10]; - }; - s8 all[48]; - }; -}; - /* addr req mask */ #define MT_VEND_TYPE_EEPROM BIT(31) #define MT_VEND_TYPE_CFG BIT(30) @@ -802,8 +789,6 @@ struct mt76_dev { struct debugfs_blob_wrapper eeprom; struct debugfs_blob_wrapper otp; - struct mt76_rate_power rate_power; - char alpha2[3]; enum nl80211_dfs_regions region; diff --git a/drivers/net/wireless/mediatek/mt76/mt76x0/eeprom.c b/drivers/net/wireless/mediatek/mt76/mt76x0/eeprom.c index da2ca2563ac9..103a6d3a1e13 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76x0/eeprom.c +++ b/drivers/net/wireless/mediatek/mt76/mt76x0/eeprom.c @@ -151,7 +151,7 @@ static s8 mt76x0_get_delta(struct mt76x02_dev *dev) void mt76x0_get_tx_power_per_rate(struct mt76x02_dev *dev, struct ieee80211_channel *chan, - struct mt76_rate_power *t) + struct mt76x02_rate_power *t) { bool is_2ghz = chan->band == NL80211_BAND_2GHZ; u16 val, addr; @@ -235,7 +235,7 @@ void mt76x0_get_power_info(struct mt76x02_dev *dev, data = mt76x02_eeprom_get(dev, MT_EE_5G_TARGET_POWER); else data = mt76x02_eeprom_get(dev, MT_EE_2G_TARGET_POWER); - target_power = (data & 0xff) - dev->mt76.rate_power.ofdm[7]; + target_power = (data & 0xff) - dev->rate_power.ofdm[7]; *tp = target_power + mt76x0_get_delta(dev); return; diff --git a/drivers/net/wireless/mediatek/mt76/mt76x0/eeprom.h b/drivers/net/wireless/mediatek/mt76/mt76x0/eeprom.h index 15540ce8db87..08f1b10bf3ba 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76x0/eeprom.h +++ b/drivers/net/wireless/mediatek/mt76/mt76x0/eeprom.h @@ -19,7 +19,7 @@ int mt76x0_eeprom_init(struct mt76x02_dev *dev); void mt76x0_read_rx_gain(struct mt76x02_dev *dev); void mt76x0_get_tx_power_per_rate(struct mt76x02_dev *dev, struct ieee80211_channel *chan, - struct mt76_rate_power *t); + struct mt76x02_rate_power *t); void mt76x0_get_power_info(struct mt76x02_dev *dev, struct ieee80211_channel *chan, s8 *tp); diff --git a/drivers/net/wireless/mediatek/mt76/mt76x0/init.c b/drivers/net/wireless/mediatek/mt76/mt76x0/init.c index 66d47c70111a..6257460f8de5 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76x0/init.c +++ b/drivers/net/wireless/mediatek/mt76/mt76x0/init.c @@ -217,7 +217,7 @@ mt76x0_init_txpower(struct mt76x02_dev *dev, struct ieee80211_supported_band *sband) { struct ieee80211_channel *chan; - struct mt76_rate_power t; + struct mt76x02_rate_power t; s8 tp; int i; diff --git a/drivers/net/wireless/mediatek/mt76/mt76x0/phy.c b/drivers/net/wireless/mediatek/mt76/mt76x0/phy.c index e91c314cdfac..8a89fe49db36 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76x0/phy.c +++ b/drivers/net/wireless/mediatek/mt76/mt76x0/phy.c @@ -598,7 +598,7 @@ mt76x0_phy_get_target_power(struct mt76x02_dev *dev, u8 tx_mode, if (tx_rate > 3) return -EINVAL; - *target_power = cur_power + dev->mt76.rate_power.cck[tx_rate]; + *target_power = cur_power + dev->rate_power.cck[tx_rate]; *target_pa_power = mt76x0_phy_get_rf_pa_mode(dev, 0, tx_rate); break; case 1: { @@ -635,7 +635,7 @@ mt76x0_phy_get_target_power(struct mt76x02_dev *dev, u8 tx_mode, return -EINVAL; } - *target_power = cur_power + dev->mt76.rate_power.ofdm[index]; + *target_power = cur_power + dev->rate_power.ofdm[index]; *target_pa_power = mt76x0_phy_get_rf_pa_mode(dev, 0, index + 4); break; } @@ -645,7 +645,7 @@ mt76x0_phy_get_target_power(struct mt76x02_dev *dev, u8 tx_mode, if (tx_rate > 9) return -EINVAL; - *target_power = cur_power + dev->mt76.rate_power.vht[tx_rate]; + *target_power = cur_power + dev->rate_power.vht[tx_rate]; *target_pa_power = mt76x0_phy_get_rf_pa_mode(dev, 1, tx_rate); break; default: @@ -654,7 +654,7 @@ mt76x0_phy_get_target_power(struct mt76x02_dev *dev, u8 tx_mode, if (tx_rate > 9) return -EINVAL; - *target_power = cur_power + dev->mt76.rate_power.ht[tx_rate]; + *target_power = cur_power + dev->rate_power.ht[tx_rate]; *target_pa_power = mt76x0_phy_get_rf_pa_mode(dev, 1, tx_rate); break; } @@ -841,7 +841,7 @@ static void mt76x0_phy_tssi_calibrate(struct mt76x02_dev *dev) void mt76x0_phy_set_txpower(struct mt76x02_dev *dev) { - struct mt76_rate_power *t = &dev->mt76.rate_power; + struct mt76x02_rate_power *t = &dev->rate_power; s8 info; mt76x0_get_tx_power_per_rate(dev, dev->mphy.chandef.chan, t); diff --git a/drivers/net/wireless/mediatek/mt76/mt76x02.h b/drivers/net/wireless/mediatek/mt76/mt76x02.h index 50eaeff11af3..ae6ba35e863d 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76x02.h +++ b/drivers/net/wireless/mediatek/mt76/mt76x02.h @@ -72,6 +72,19 @@ struct mt76x02_beacon_ops { #define mt76x02_pre_tbtt_enable(dev, enable) \ (dev)->beacon_ops->pre_tbtt_enable(dev, enable) +struct mt76x02_rate_power { + union { + struct { + s8 cck[4]; + s8 ofdm[8]; + s8 stbc[10]; + s8 ht[16]; + s8 vht[10]; + }; + s8 all[48]; + }; +}; + struct mt76x02_dev { union { /* must be first */ struct mt76_dev mt76; @@ -107,6 +120,8 @@ struct mt76x02_dev { u8 beacon_hang_check; u8 mcu_timeout; + struct mt76x02_rate_power rate_power; + struct mt76x02_calibration cal; int txpower_conf; diff --git a/drivers/net/wireless/mediatek/mt76/mt76x02_debugfs.c b/drivers/net/wireless/mediatek/mt76/mt76x02_debugfs.c index c4fe1c436aaa..066801620eb2 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76x02_debugfs.c +++ b/drivers/net/wireless/mediatek/mt76/mt76x02_debugfs.c @@ -114,6 +114,23 @@ mt76_edcca_get(void *data, u64 *val) DEFINE_DEBUGFS_ATTRIBUTE(fops_edcca, mt76_edcca_get, mt76_edcca_set, "%lld\n"); +static int mt76x02_read_rate_txpower(struct seq_file *s, void *data) +{ + struct mt76x02_dev *dev = dev_get_drvdata(s->private); + + mt76_seq_puts_array(s, "CCK", dev->rate_power.cck, + ARRAY_SIZE(dev->rate_power.cck)); + mt76_seq_puts_array(s, "OFDM", dev->rate_power.ofdm, + ARRAY_SIZE(dev->rate_power.ofdm)); + mt76_seq_puts_array(s, "STBC", dev->rate_power.stbc, + ARRAY_SIZE(dev->rate_power.stbc)); + mt76_seq_puts_array(s, "HT", dev->rate_power.ht, + ARRAY_SIZE(dev->rate_power.ht)); + mt76_seq_puts_array(s, "VHT", dev->rate_power.vht, + ARRAY_SIZE(dev->rate_power.vht)); + return 0; +} + void mt76x02_init_debugfs(struct mt76x02_dev *dev) { struct dentry *dir; @@ -133,6 +150,8 @@ void mt76x02_init_debugfs(struct mt76x02_dev *dev) debugfs_create_devm_seqfile(dev->mt76.dev, "txpower", dir, read_txpower); + debugfs_create_devm_seqfile(dev->mt76.dev, "rate_txpower", dir, + mt76x02_read_rate_txpower); debugfs_create_devm_seqfile(dev->mt76.dev, "agc", dir, read_agc); debugfs_create_u32("tx_hang_reset", 0400, dir, &dev->tx_hang_reset); diff --git a/drivers/net/wireless/mediatek/mt76/mt76x02_phy.c b/drivers/net/wireless/mediatek/mt76/mt76x02_phy.c index 2e53b0c1afdd..722dc2a7dea6 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76x02_phy.c +++ b/drivers/net/wireless/mediatek/mt76/mt76x02_phy.c @@ -59,7 +59,7 @@ mt76x02_tx_power_mask(u8 v1, u8 v2, u8 v3, u8 v4) return val; } -int mt76x02_get_max_rate_power(struct mt76_rate_power *r) +int mt76x02_get_max_rate_power(struct mt76x02_rate_power *r) { s8 ret = 0; int i; @@ -71,7 +71,7 @@ int mt76x02_get_max_rate_power(struct mt76_rate_power *r) } EXPORT_SYMBOL_GPL(mt76x02_get_max_rate_power); -void mt76x02_limit_rate_power(struct mt76_rate_power *r, int limit) +void mt76x02_limit_rate_power(struct mt76x02_rate_power *r, int limit) { int i; @@ -81,7 +81,7 @@ void mt76x02_limit_rate_power(struct mt76_rate_power *r, int limit) } EXPORT_SYMBOL_GPL(mt76x02_limit_rate_power); -void mt76x02_add_rate_power_offset(struct mt76_rate_power *r, int offset) +void mt76x02_add_rate_power_offset(struct mt76x02_rate_power *r, int offset) { int i; @@ -92,7 +92,7 @@ EXPORT_SYMBOL_GPL(mt76x02_add_rate_power_offset); void mt76x02_phy_set_txpower(struct mt76x02_dev *dev, int txp_0, int txp_1) { - struct mt76_rate_power *t = &dev->mt76.rate_power; + struct mt76x02_rate_power *t = &dev->rate_power; mt76_rmw_field(dev, MT_TX_ALC_CFG_0, MT_TX_ALC_CFG_0_CH_INIT_0, txp_0); mt76_rmw_field(dev, MT_TX_ALC_CFG_0, MT_TX_ALC_CFG_0_CH_INIT_1, txp_1); diff --git a/drivers/net/wireless/mediatek/mt76/mt76x02_phy.h b/drivers/net/wireless/mediatek/mt76/mt76x02_phy.h index 1def25bf735a..84d8a6138b3e 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76x02_phy.h +++ b/drivers/net/wireless/mediatek/mt76/mt76x02_phy.h @@ -34,10 +34,10 @@ mt76x02_get_low_rssi_gain_thresh(struct mt76x02_dev *dev) } } -void mt76x02_add_rate_power_offset(struct mt76_rate_power *r, int offset); +void mt76x02_add_rate_power_offset(struct mt76x02_rate_power *r, int offset); void mt76x02_phy_set_txpower(struct mt76x02_dev *dev, int txp_0, int txp_2); -void mt76x02_limit_rate_power(struct mt76_rate_power *r, int limit); -int mt76x02_get_max_rate_power(struct mt76_rate_power *r); +void mt76x02_limit_rate_power(struct mt76x02_rate_power *r, int limit); +int mt76x02_get_max_rate_power(struct mt76x02_rate_power *r); void mt76x02_phy_set_rxpath(struct mt76x02_dev *dev); void mt76x02_phy_set_txdac(struct mt76x02_dev *dev); void mt76x02_phy_set_bw(struct mt76x02_dev *dev, int width, u8 ctrl); diff --git a/drivers/net/wireless/mediatek/mt76/mt76x02_txrx.c b/drivers/net/wireless/mediatek/mt76/mt76x02_txrx.c index 96fdf423a348..1e1ef2499977 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76x02_txrx.c +++ b/drivers/net/wireless/mediatek/mt76/mt76x02_txrx.c @@ -62,23 +62,23 @@ s8 mt76x02_tx_get_max_txpwr_adj(struct mt76x02_dev *dev, u8 mcs = ieee80211_rate_get_vht_mcs(rate); if (mcs == 8 || mcs == 9) { - max_txpwr = dev->mt76.rate_power.vht[8]; + max_txpwr = dev->rate_power.vht[8]; } else { u8 nss, idx; nss = ieee80211_rate_get_vht_nss(rate); idx = ((nss - 1) << 3) + mcs; - max_txpwr = dev->mt76.rate_power.ht[idx & 0xf]; + max_txpwr = dev->rate_power.ht[idx & 0xf]; } } else if (rate->flags & IEEE80211_TX_RC_MCS) { - max_txpwr = dev->mt76.rate_power.ht[rate->idx & 0xf]; + max_txpwr = dev->rate_power.ht[rate->idx & 0xf]; } else { enum nl80211_band band = dev->mphy.chandef.chan->band; if (band == NL80211_BAND_2GHZ) { const struct ieee80211_rate *r; struct wiphy *wiphy = dev->mt76.hw->wiphy; - struct mt76_rate_power *rp = &dev->mt76.rate_power; + struct mt76x02_rate_power *rp = &dev->rate_power; r = &wiphy->bands[band]->bitrates[rate->idx]; if (r->flags & IEEE80211_RATE_SHORT_PREAMBLE) @@ -86,7 +86,7 @@ s8 mt76x02_tx_get_max_txpwr_adj(struct mt76x02_dev *dev, else max_txpwr = rp->ofdm[r->hw_value & 0x7]; } else { - max_txpwr = dev->mt76.rate_power.ofdm[rate->idx & 0x7]; + max_txpwr = dev->rate_power.ofdm[rate->idx & 0x7]; } } @@ -112,7 +112,7 @@ void mt76x02_tx_set_txpwr_auto(struct mt76x02_dev *dev, s8 txpwr) s8 txpwr_adj; txpwr_adj = mt76x02_tx_get_txpwr_adj(dev, txpwr, - dev->mt76.rate_power.ofdm[4]); + dev->rate_power.ofdm[4]); mt76_rmw_field(dev, MT_PROT_AUTO_TX_CFG, MT_PROT_AUTO_TX_CFG_PROT_PADJ, txpwr_adj); mt76_rmw_field(dev, MT_PROT_AUTO_TX_CFG, diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2/eeprom.c b/drivers/net/wireless/mediatek/mt76/mt76x2/eeprom.c index c57e05a5c65e..d1d7e06c3fa5 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76x2/eeprom.c +++ b/drivers/net/wireless/mediatek/mt76/mt76x2/eeprom.c @@ -280,7 +280,7 @@ void mt76x2_read_rx_gain(struct mt76x02_dev *dev) } EXPORT_SYMBOL_GPL(mt76x2_read_rx_gain); -void mt76x2_get_rate_power(struct mt76x02_dev *dev, struct mt76_rate_power *t, +void mt76x2_get_rate_power(struct mt76x02_dev *dev, struct mt76x02_rate_power *t, struct ieee80211_channel *chan) { bool is_5ghz; diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2/eeprom.h b/drivers/net/wireless/mediatek/mt76/mt76x2/eeprom.h index 3755632e6494..43430ef98b11 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76x2/eeprom.h +++ b/drivers/net/wireless/mediatek/mt76/mt76x2/eeprom.h @@ -40,7 +40,7 @@ struct mt76x2_temp_comp { unsigned int low_slope; /* J / dB */ }; -void mt76x2_get_rate_power(struct mt76x02_dev *dev, struct mt76_rate_power *t, +void mt76x2_get_rate_power(struct mt76x02_dev *dev, struct mt76x02_rate_power *t, struct ieee80211_channel *chan); void mt76x2_get_power_info(struct mt76x02_dev *dev, struct mt76x2_tx_power_info *t, diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2/init.c b/drivers/net/wireless/mediatek/mt76/mt76x2/init.c index 7b01a06d7f8d..19c139290adb 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76x2/init.c +++ b/drivers/net/wireless/mediatek/mt76/mt76x2/init.c @@ -182,7 +182,7 @@ void mt76x2_init_txpower(struct mt76x02_dev *dev, { struct ieee80211_channel *chan; struct mt76x2_tx_power_info txp; - struct mt76_rate_power t = {}; + struct mt76x02_rate_power t = {}; int i; for (i = 0; i < sband->n_channels; i++) { diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2/phy.c b/drivers/net/wireless/mediatek/mt76/mt76x2/phy.c index ed2dcb05d614..f84517d932dc 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76x2/phy.c +++ b/drivers/net/wireless/mediatek/mt76/mt76x2/phy.c @@ -116,7 +116,7 @@ void mt76x2_phy_set_txpower_regs(struct mt76x02_dev *dev, EXPORT_SYMBOL_GPL(mt76x2_phy_set_txpower_regs); static int -mt76x2_get_min_rate_power(struct mt76_rate_power *r) +mt76x2_get_min_rate_power(struct mt76x02_rate_power *r) { int i; s8 ret = 0; @@ -140,7 +140,7 @@ void mt76x2_phy_set_txpower(struct mt76x02_dev *dev) struct ieee80211_channel *chan = dev->mphy.chandef.chan; struct mt76x2_tx_power_info txp; int txp_0, txp_1, delta = 0; - struct mt76_rate_power t = {}; + struct mt76x02_rate_power t = {}; int base_power, gain; mt76x2_get_power_info(dev, &txp, chan); @@ -175,7 +175,7 @@ void mt76x2_phy_set_txpower(struct mt76x02_dev *dev) dev->target_power = txp.target_power; dev->target_power_delta[0] = txp_0 - txp.chain[0].target_power; dev->target_power_delta[1] = txp_1 - txp.chain[0].target_power; - dev->mt76.rate_power = t; + dev->rate_power = t; mt76x02_phy_set_txpower(dev, txp_0, txp_1); } From ba45841ca5eb29245f9c9f452a39586d9d68bc12 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Wed, 28 Sep 2022 11:29:45 +0200 Subject: [PATCH 1447/2002] wifi: mt76: mt76x02: simplify struct mt76x02_rate_power - remove stbc, because mt76x0 doesn't support it and mt76x2 uses the same values as HT/VHT - reduce vht array to 2 elements, because VHT MCS0-7 are the same as HT Signed-off-by: Felix Fietkau --- .../wireless/mediatek/mt76/mt76x0/eeprom.c | 24 +++++-------------- drivers/net/wireless/mediatek/mt76/mt76x02.h | 5 ++-- .../wireless/mediatek/mt76/mt76x02_debugfs.c | 2 -- .../wireless/mediatek/mt76/mt76x02_eeprom.h | 2 -- .../net/wireless/mediatek/mt76/mt76x02_phy.c | 14 +++++------ .../net/wireless/mediatek/mt76/mt76x02_txrx.c | 2 +- .../wireless/mediatek/mt76/mt76x2/eeprom.c | 14 +---------- 7 files changed, 17 insertions(+), 46 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt76x0/eeprom.c b/drivers/net/wireless/mediatek/mt76/mt76x0/eeprom.c index 103a6d3a1e13..c3a392a1a659 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76x0/eeprom.c +++ b/drivers/net/wireless/mediatek/mt76/mt76x0/eeprom.c @@ -179,31 +179,19 @@ void mt76x0_get_tx_power_per_rate(struct mt76x02_dev *dev, /* ht-vht mcs 1ss 0, 1, 2, 3 */ addr = is_2ghz ? MT_EE_TX_POWER_BYRATE_BASE + 6 : 0x124; val = mt76x02_eeprom_get(dev, addr); - t->ht[0] = t->ht[1] = t->vht[0] = t->vht[1] = s6_to_s8(val); - t->ht[2] = t->ht[3] = t->vht[2] = t->vht[3] = s6_to_s8(val >> 8); + t->ht[0] = t->ht[1] = s6_to_s8(val); + t->ht[2] = t->ht[3] = s6_to_s8(val >> 8); /* ht-vht mcs 1ss 4, 5, 6 */ addr = is_2ghz ? MT_EE_TX_POWER_BYRATE_BASE + 8 : 0x126; val = mt76x02_eeprom_get(dev, addr); - t->ht[4] = t->ht[5] = t->vht[4] = t->vht[5] = s6_to_s8(val); - t->ht[6] = t->ht[7] = t->vht[6] = t->vht[7] = s6_to_s8(val >> 8); - - /* ht-vht mcs 1ss 0, 1, 2, 3 stbc */ - addr = is_2ghz ? MT_EE_TX_POWER_BYRATE_BASE + 14 : 0xec; - val = mt76x02_eeprom_get(dev, addr); - t->stbc[0] = t->stbc[1] = s6_to_s8(val); - t->stbc[2] = t->stbc[3] = s6_to_s8(val >> 8); - - /* ht-vht mcs 1ss 4, 5, 6 stbc */ - addr = is_2ghz ? MT_EE_TX_POWER_BYRATE_BASE + 16 : 0xee; - val = mt76x02_eeprom_get(dev, addr); - t->stbc[4] = t->stbc[5] = s6_to_s8(val); - t->stbc[6] = t->stbc[7] = s6_to_s8(val >> 8); + t->ht[4] = t->ht[5] = s6_to_s8(val); + t->ht[6] = t->ht[7] = s6_to_s8(val >> 8); /* vht mcs 8, 9 5GHz */ val = mt76x02_eeprom_get(dev, 0x12c); - t->vht[8] = s6_to_s8(val); - t->vht[9] = s6_to_s8(val >> 8); + t->vht[0] = s6_to_s8(val); + t->vht[1] = s6_to_s8(val >> 8); delta = mt76x0_tssi_enabled(dev) ? 0 : mt76x0_get_delta(dev); mt76x02_add_rate_power_offset(t, delta); diff --git a/drivers/net/wireless/mediatek/mt76/mt76x02.h b/drivers/net/wireless/mediatek/mt76/mt76x02.h index ae6ba35e863d..a19176bb2433 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76x02.h +++ b/drivers/net/wireless/mediatek/mt76/mt76x02.h @@ -77,11 +77,10 @@ struct mt76x02_rate_power { struct { s8 cck[4]; s8 ofdm[8]; - s8 stbc[10]; s8 ht[16]; - s8 vht[10]; + s8 vht[2]; }; - s8 all[48]; + s8 all[30]; }; }; diff --git a/drivers/net/wireless/mediatek/mt76/mt76x02_debugfs.c b/drivers/net/wireless/mediatek/mt76/mt76x02_debugfs.c index 066801620eb2..981ac8e84807 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76x02_debugfs.c +++ b/drivers/net/wireless/mediatek/mt76/mt76x02_debugfs.c @@ -122,8 +122,6 @@ static int mt76x02_read_rate_txpower(struct seq_file *s, void *data) ARRAY_SIZE(dev->rate_power.cck)); mt76_seq_puts_array(s, "OFDM", dev->rate_power.ofdm, ARRAY_SIZE(dev->rate_power.ofdm)); - mt76_seq_puts_array(s, "STBC", dev->rate_power.stbc, - ARRAY_SIZE(dev->rate_power.stbc)); mt76_seq_puts_array(s, "HT", dev->rate_power.ht, ARRAY_SIZE(dev->rate_power.ht)); mt76_seq_puts_array(s, "VHT", dev->rate_power.vht, diff --git a/drivers/net/wireless/mediatek/mt76/mt76x02_eeprom.h b/drivers/net/wireless/mediatek/mt76/mt76x02_eeprom.h index 99941a4700f3..13fa70853b0d 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76x02_eeprom.h +++ b/drivers/net/wireless/mediatek/mt76/mt76x02_eeprom.h @@ -62,8 +62,6 @@ enum mt76x02_eeprom_field { MT_EE_TX_POWER_HT_MCS4 = 0x0a8, MT_EE_TX_POWER_HT_MCS8 = 0x0aa, MT_EE_TX_POWER_HT_MCS12 = 0x0ac, - MT_EE_TX_POWER_VHT_MCS0 = 0x0ba, - MT_EE_TX_POWER_VHT_MCS4 = 0x0bc, MT_EE_TX_POWER_VHT_MCS8 = 0x0be, MT_EE_2G_TARGET_POWER = 0x0d0, diff --git a/drivers/net/wireless/mediatek/mt76/mt76x02_phy.c b/drivers/net/wireless/mediatek/mt76/mt76x02_phy.c index 722dc2a7dea6..cbe7e6f0c29a 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76x02_phy.c +++ b/drivers/net/wireless/mediatek/mt76/mt76x02_phy.c @@ -107,17 +107,17 @@ void mt76x02_phy_set_txpower(struct mt76x02_dev *dev, int txp_0, int txp_1) mt76x02_tx_power_mask(t->ht[4], t->ht[6], t->ht[8], t->ht[10])); mt76_wr(dev, MT_TX_PWR_CFG_3, - mt76x02_tx_power_mask(t->ht[12], t->ht[14], t->stbc[0], - t->stbc[2])); + mt76x02_tx_power_mask(t->ht[12], t->ht[14], t->ht[0], + t->ht[2])); mt76_wr(dev, MT_TX_PWR_CFG_4, - mt76x02_tx_power_mask(t->stbc[4], t->stbc[6], 0, 0)); + mt76x02_tx_power_mask(t->ht[4], t->ht[6], 0, 0)); mt76_wr(dev, MT_TX_PWR_CFG_7, - mt76x02_tx_power_mask(t->ofdm[7], t->vht[8], t->ht[7], - t->vht[9])); + mt76x02_tx_power_mask(t->ofdm[7], t->vht[0], t->ht[7], + t->vht[1])); mt76_wr(dev, MT_TX_PWR_CFG_8, - mt76x02_tx_power_mask(t->ht[14], 0, t->vht[8], t->vht[9])); + mt76x02_tx_power_mask(t->ht[14], 0, t->vht[0], t->vht[1])); mt76_wr(dev, MT_TX_PWR_CFG_9, - mt76x02_tx_power_mask(t->ht[7], 0, t->stbc[8], t->stbc[9])); + mt76x02_tx_power_mask(t->ht[7], 0, t->vht[0], t->vht[1])); } EXPORT_SYMBOL_GPL(mt76x02_phy_set_txpower); diff --git a/drivers/net/wireless/mediatek/mt76/mt76x02_txrx.c b/drivers/net/wireless/mediatek/mt76/mt76x02_txrx.c index 1e1ef2499977..3a313075a9e3 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76x02_txrx.c +++ b/drivers/net/wireless/mediatek/mt76/mt76x02_txrx.c @@ -62,7 +62,7 @@ s8 mt76x02_tx_get_max_txpwr_adj(struct mt76x02_dev *dev, u8 mcs = ieee80211_rate_get_vht_mcs(rate); if (mcs == 8 || mcs == 9) { - max_txpwr = dev->rate_power.vht[8]; + max_txpwr = dev->rate_power.vht[0]; } else { u8 nss, idx; diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2/eeprom.c b/drivers/net/wireless/mediatek/mt76/mt76x2/eeprom.c index d1d7e06c3fa5..d5809408d1d3 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76x2/eeprom.c +++ b/drivers/net/wireless/mediatek/mt76/mt76x2/eeprom.c @@ -324,22 +324,10 @@ void mt76x2_get_rate_power(struct mt76x02_dev *dev, struct mt76x02_rate_power *t t->ht[12] = t->ht[13] = mt76x02_rate_power_val(val); t->ht[14] = t->ht[15] = mt76x02_rate_power_val(val >> 8); - val = mt76x02_eeprom_get(dev, MT_EE_TX_POWER_VHT_MCS0); - t->vht[0] = t->vht[1] = mt76x02_rate_power_val(val); - t->vht[2] = t->vht[3] = mt76x02_rate_power_val(val >> 8); - - val = mt76x02_eeprom_get(dev, MT_EE_TX_POWER_VHT_MCS4); - t->vht[4] = t->vht[5] = mt76x02_rate_power_val(val); - t->vht[6] = t->vht[7] = mt76x02_rate_power_val(val >> 8); - val = mt76x02_eeprom_get(dev, MT_EE_TX_POWER_VHT_MCS8); if (!is_5ghz) val >>= 8; - t->vht[8] = t->vht[9] = mt76x02_rate_power_val(val >> 8); - - memcpy(t->stbc, t->ht, sizeof(t->stbc[0]) * 8); - t->stbc[8] = t->vht[8]; - t->stbc[9] = t->vht[9]; + t->vht[0] = t->vht[1] = mt76x02_rate_power_val(val >> 8); } EXPORT_SYMBOL_GPL(mt76x2_get_rate_power); From c256ba6b1909f28b517274282b6845567e974143 Mon Sep 17 00:00:00 2001 From: Sean Wang Date: Sat, 17 Sep 2022 06:46:45 +0800 Subject: [PATCH 1448/2002] wifi: mt76: mt7921: fix antenna signal are way off in monitor mode Group 3 in RxD is disabled in monitor mode. We should use the group 5 in RxD instead to fix antenna signal way off issue, e.g we would see the incorrect antenna signal value in wireshark. On the other hand, Group 5 wouldn't be used in STA or AP mode, so the patch shouldn't cause any harm to those modes. Fixes: cbaa0a404f8d ("mt76: mt7921: fix up the monitor mode") Reported-by: Adrian Granados Co-developed-by: Deren Wu Signed-off-by: Deren Wu Signed-off-by: Sean Wang Signed-off-by: Felix Fietkau --- .../net/wireless/mediatek/mt76/mt7921/mac.c | 32 ++++++++++++------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/mac.c b/drivers/net/wireless/mediatek/mt76/mt7921/mac.c index 650ab97ae052..6860468ed191 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7921/mac.c @@ -396,6 +396,27 @@ mt7921_mac_fill_rx(struct mt7921_dev *dev, struct sk_buff *skb) if (v0 & MT_PRXV_HT_AD_CODE) status->enc_flags |= RX_ENC_FLAG_LDPC; + ret = mt76_connac2_mac_fill_rx_rate(&dev->mt76, status, sband, + rxv, &mode); + if (ret < 0) + return ret; + + if (rxd1 & MT_RXD1_NORMAL_GROUP_5) { + rxd += 6; + if ((u8 *)rxd - skb->data >= skb->len) + return -EINVAL; + + rxv = rxd; + /* Monitor mode would use RCPI described in GROUP 5 + * instead. + */ + v1 = le32_to_cpu(rxv[0]); + + rxd += 12; + if ((u8 *)rxd - skb->data >= skb->len) + return -EINVAL; + } + status->chains = mphy->antenna_mask; status->chain_signal[0] = to_rssi(MT_PRXV_RCPI0, v1); status->chain_signal[1] = to_rssi(MT_PRXV_RCPI1, v1); @@ -410,17 +431,6 @@ mt7921_mac_fill_rx(struct mt7921_dev *dev, struct sk_buff *skb) status->signal = max(status->signal, status->chain_signal[i]); } - - ret = mt76_connac2_mac_fill_rx_rate(&dev->mt76, status, sband, - rxv, &mode); - if (ret < 0) - return ret; - - if (rxd1 & MT_RXD1_NORMAL_GROUP_5) { - rxd += 18; - if ((u8 *)rxd - skb->data >= skb->len) - return -EINVAL; - } } amsdu_info = FIELD_GET(MT_RXD4_NORMAL_PAYLOAD_FORMAT, rxd4); From 03c2dd4d01a20edbadee408a622dee5e8a27f1e6 Mon Sep 17 00:00:00 2001 From: Gaosheng Cui Date: Thu, 22 Sep 2022 15:47:11 +0800 Subject: [PATCH 1449/2002] wifi: mt76: Remove unused inline function mt76_wcid_mask_test() All uses of mt76_wcid_mask_test() have been removed since commit 8950a62f19c9 ("mt76: get rid of mt76_wcid_hw routine"), so remove it. Signed-off-by: Gaosheng Cui Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/util.h | 6 ------ 1 file changed, 6 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/util.h b/drivers/net/wireless/mediatek/mt76/util.h index 49c52d781f40..260965dde94c 100644 --- a/drivers/net/wireless/mediatek/mt76/util.h +++ b/drivers/net/wireless/mediatek/mt76/util.h @@ -29,12 +29,6 @@ enum { int mt76_wcid_alloc(u32 *mask, int size); -static inline bool -mt76_wcid_mask_test(u32 *mask, int idx) -{ - return mask[idx / 32] & BIT(idx % 32); -} - static inline void mt76_wcid_mask_set(u32 *mask, int idx) { From 2b685ba7d4247d7707af795719f4f33c9019feb6 Mon Sep 17 00:00:00 2001 From: Ben Greear Date: Thu, 29 Sep 2022 08:15:25 -0700 Subject: [PATCH 1450/2002] wifi: mt76: mt7915: fix bounds checking for tx-free-done command According to the tx-free-done documentation, the DW4 can be repeated, so have to be more careful about how we test for walking off the end of the array. Signed-off-by: Ben Greear Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt7915/mac.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mac.c b/drivers/net/wireless/mediatek/mt76/mt7915/mac.c index a4bcc617c1a3..89a3810ee53f 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mac.c @@ -905,17 +905,19 @@ mt7915_mac_tx_free(struct mt7915_dev *dev, void *data, int len) total = le16_get_bits(free->ctrl, MT_TX_FREE_MSDU_CNT); v3 = (FIELD_GET(MT_TX_FREE_VER, txd) == 0x4); - if (WARN_ON_ONCE((void *)&tx_info[total >> v3] > end)) - return; for (cur_info = tx_info; count < total; cur_info++) { - u32 msdu, info = le32_to_cpu(*cur_info); + u32 msdu, info; u8 i; + if (WARN_ON_ONCE((void *)cur_info >= end)) + return; + /* * 1'b1: new wcid pair. * 1'b0: msdu_id with the same 'wcid pair' as above. */ + info = le32_to_cpu(*cur_info); if (info & MT_TX_FREE_PAIR) { struct mt7915_sta *msta; struct mt76_wcid *wcid; From 7624ffcd4edf2668e6db90f39c7343967a94c88f Mon Sep 17 00:00:00 2001 From: Evelyn Tsai Date: Fri, 30 Sep 2022 22:14:41 +0800 Subject: [PATCH 1451/2002] wifi: mt76: mt7915: reserve 8 bits for the index of rf registers The value of regidx of rf registers is combined with WF selection and offset. Extend the WF selection field from 4 to 8 bits since the adie index should also be specified. Signed-off-by: Evelyn Tsai Signed-off-by: Shayne Chen Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt7915/debugfs.c | 2 +- drivers/net/wireless/mediatek/mt76/mt7915/mcu.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/debugfs.c b/drivers/net/wireless/mediatek/mt76/mt7915/debugfs.c index 6ef3431cad64..e1d15394a621 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/debugfs.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/debugfs.c @@ -963,7 +963,7 @@ mt7915_twt_stats(struct seq_file *s, void *data) } /* The index of RF registers use the generic regidx, combined with two parts: - * WF selection [31:28] and offset [27:0]. + * WF selection [31:24] and offset [23:0]. */ static int mt7915_rf_regval_get(void *data, u64 *val) diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c index 8d297e4aa7d4..1b9f3da8ae96 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c @@ -3447,8 +3447,8 @@ int mt7915_mcu_rf_regval(struct mt7915_dev *dev, u32 regidx, u32 *val, bool set) __le32 ofs; __le32 data; } __packed req = { - .idx = cpu_to_le32(u32_get_bits(regidx, GENMASK(31, 28))), - .ofs = cpu_to_le32(u32_get_bits(regidx, GENMASK(27, 0))), + .idx = cpu_to_le32(u32_get_bits(regidx, GENMASK(31, 24))), + .ofs = cpu_to_le32(u32_get_bits(regidx, GENMASK(23, 0))), .data = set ? cpu_to_le32(*val) : 0, }; struct sk_buff *skb; From a7ec8bcf00034ce84d4c9a15dffd7577fbed4db2 Mon Sep 17 00:00:00 2001 From: Shayne Chen Date: Fri, 30 Sep 2022 23:13:10 +0800 Subject: [PATCH 1452/2002] wifi: mt76: mt7915: rework eeprom tx paths and streams init Rework tx paths and streams init part to improve readability, and make sure that the available tx streams should be smaller than or equal to the available tx paths. Signed-off-by: Shayne Chen Signed-off-by: Felix Fietkau --- .../wireless/mediatek/mt76/mt7915/eeprom.c | 57 ++++++++----------- .../wireless/mediatek/mt76/mt7915/eeprom.h | 5 -- 2 files changed, 23 insertions(+), 39 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/eeprom.c b/drivers/net/wireless/mediatek/mt76/mt7915/eeprom.c index 4b1a9811646f..83bced0c0785 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/eeprom.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/eeprom.c @@ -173,60 +173,49 @@ static void mt7915_eeprom_parse_band_config(struct mt7915_phy *phy) void mt7915_eeprom_parse_hw_cap(struct mt7915_dev *dev, struct mt7915_phy *phy) { - u8 nss, nss_band, nss_band_max, *eeprom = dev->mt76.eeprom.data; + u8 path, nss, nss_max = 4, *eeprom = dev->mt76.eeprom.data; struct mt76_phy *mphy = phy->mt76; - bool ext_phy = phy != &dev->phy; mt7915_eeprom_parse_band_config(phy); - /* read tx/rx mask from eeprom */ + /* read tx/rx path from eeprom */ if (is_mt7915(&dev->mt76)) { - nss = FIELD_GET(MT_EE_WIFI_CONF0_TX_PATH, - eeprom[MT_EE_WIFI_CONF]); + path = FIELD_GET(MT_EE_WIFI_CONF0_TX_PATH, + eeprom[MT_EE_WIFI_CONF]); } else { - nss = FIELD_GET(MT_EE_WIFI_CONF0_TX_PATH, - eeprom[MT_EE_WIFI_CONF + phy->band_idx]); + path = FIELD_GET(MT_EE_WIFI_CONF0_TX_PATH, + eeprom[MT_EE_WIFI_CONF + phy->band_idx]); } - if (!nss || nss > 4) - nss = 4; + if (!path || path > 4) + path = 4; /* read tx/rx stream */ - nss_band = nss; - + nss = path; if (dev->dbdc_support) { if (is_mt7915(&dev->mt76)) { - nss_band = FIELD_GET(MT_EE_WIFI_CONF3_TX_PATH_B0, - eeprom[MT_EE_WIFI_CONF + 3]); + nss = FIELD_GET(MT_EE_WIFI_CONF3_TX_PATH_B0, + eeprom[MT_EE_WIFI_CONF + 3]); if (phy->band_idx) - nss_band = FIELD_GET(MT_EE_WIFI_CONF3_TX_PATH_B1, - eeprom[MT_EE_WIFI_CONF + 3]); + nss = FIELD_GET(MT_EE_WIFI_CONF3_TX_PATH_B1, + eeprom[MT_EE_WIFI_CONF + 3]); } else { - nss_band = FIELD_GET(MT_EE_WIFI_CONF_STREAM_NUM, - eeprom[MT_EE_WIFI_CONF + 2 + phy->band_idx]); + nss = FIELD_GET(MT_EE_WIFI_CONF_STREAM_NUM, + eeprom[MT_EE_WIFI_CONF + 2 + phy->band_idx]); } - nss_band_max = is_mt7986(&dev->mt76) ? - MT_EE_NSS_MAX_DBDC_MA7986 : MT_EE_NSS_MAX_DBDC_MA7915; - } else { - nss_band_max = is_mt7986(&dev->mt76) ? - MT_EE_NSS_MAX_MA7986 : MT_EE_NSS_MAX_MA7915; + if (!is_mt7986(&dev->mt76)) + nss_max = 2; } - if (!nss_band || nss_band > nss_band_max) - nss_band = nss_band_max; + if (!nss) + nss = nss_max; + nss = min_t(u8, min_t(u8, nss_max, nss), path); - if (nss_band > nss) { - dev_warn(dev->mt76.dev, - "nss mismatch, nss(%d) nss_band(%d) band(%d) ext_phy(%d)\n", - nss, nss_band, phy->band_idx, ext_phy); - nss = nss_band; - } - - mphy->chainmask = BIT(nss) - 1; - if (ext_phy) + mphy->chainmask = BIT(path) - 1; + if (phy->band_idx) mphy->chainmask <<= dev->chainshift; - mphy->antenna_mask = BIT(nss_band) - 1; + mphy->antenna_mask = BIT(nss) - 1; dev->chainmask |= mphy->chainmask; dev->chainshift = hweight8(dev->mphy.chainmask); } diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/eeprom.h b/drivers/net/wireless/mediatek/mt76/mt7915/eeprom.h index 7578ac6d0be6..f3e56817d36e 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/eeprom.h +++ b/drivers/net/wireless/mediatek/mt76/mt7915/eeprom.h @@ -58,11 +58,6 @@ enum mt7915_eeprom_field { #define MT_EE_RATE_DELTA_SIGN BIT(6) #define MT_EE_RATE_DELTA_EN BIT(7) -#define MT_EE_NSS_MAX_MA7915 4 -#define MT_EE_NSS_MAX_DBDC_MA7915 2 -#define MT_EE_NSS_MAX_MA7986 4 -#define MT_EE_NSS_MAX_DBDC_MA7986 4 - enum mt7915_adie_sku { MT7976_ONE_ADIE_DBDC = 0x7, MT7975_ONE_ADIE = 0x8, From ee0863aecdecbd9035cf45acb8017bce67fc835c Mon Sep 17 00:00:00 2001 From: Peter Chiu Date: Fri, 30 Sep 2022 23:13:11 +0800 Subject: [PATCH 1453/2002] wifi: mt76: mt7915: deal with special variant of mt7916 A variant of mt7916 supports up to 3 tx/rx paths but with only 2 spatial streams. An example usage of the 3rd path is to server as an auxiliary for beamforming. In order to deal with this case, this patch reworks some parts to correctly use paths or streams. Signed-off-by: Peter Chiu Signed-off-by: Shayne Chen Signed-off-by: Felix Fietkau --- .../net/wireless/mediatek/mt76/mt7915/init.c | 37 +++++++++---------- .../net/wireless/mediatek/mt76/mt7915/main.c | 11 +++--- .../net/wireless/mediatek/mt76/mt7915/mcu.c | 16 ++++---- 3 files changed, 32 insertions(+), 32 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/init.c b/drivers/net/wireless/mediatek/mt76/mt7915/init.c index cc2aac86bcfb..324db5291c85 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/init.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/init.c @@ -700,13 +700,13 @@ mt7915_init_hardware(struct mt7915_dev *dev, struct mt7915_phy *phy2) void mt7915_set_stream_vht_txbf_caps(struct mt7915_phy *phy) { - int nss; + int sts; u32 *cap; if (!phy->mt76->cap.has_5ghz) return; - nss = hweight8(phy->mt76->chainmask); + sts = hweight8(phy->mt76->chainmask); cap = &phy->mt76->sband_5g.sband.vht_cap.cap; *cap |= IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE | @@ -717,28 +717,27 @@ void mt7915_set_stream_vht_txbf_caps(struct mt7915_phy *phy) IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE | IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE); - if (nss < 2) + if (sts < 2) return; *cap |= IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE | IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE | FIELD_PREP(IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_MASK, - nss - 1); + sts - 1); } static void -mt7915_set_stream_he_txbf_caps(struct mt7915_dev *dev, - struct ieee80211_sta_he_cap *he_cap, - int vif, int nss) +mt7915_set_stream_he_txbf_caps(struct mt7915_phy *phy, + struct ieee80211_sta_he_cap *he_cap, int vif) { + struct mt7915_dev *dev = phy->dev; struct ieee80211_he_cap_elem *elem = &he_cap->he_cap_elem; - u8 c, nss_160; + int sts = hweight8(phy->mt76->chainmask); + u8 c, sts_160 = sts; - /* Can do 1/2 of NSS streams in 160Mhz mode for mt7915 */ + /* Can do 1/2 of STS in 160Mhz mode for mt7915 */ if (is_mt7915(&dev->mt76) && !dev->dbdc_support) - nss_160 = nss / 2; - else - nss_160 = nss; + sts_160 /= 2; #ifdef CONFIG_MAC80211_MESH if (vif == NL80211_IFTYPE_MESH_POINT) @@ -778,11 +777,11 @@ mt7915_set_stream_he_txbf_caps(struct mt7915_dev *dev, elem->phy_cap_info[6] |= c; - if (nss < 2) + if (sts < 2) return; /* the maximum cap is 4 x 3, (Nr, Nc) = (3, 2) */ - elem->phy_cap_info[7] |= min_t(int, nss - 1, 2) << 3; + elem->phy_cap_info[7] |= min_t(int, sts - 1, 2) << 3; if (vif != NL80211_IFTYPE_AP) return; @@ -791,12 +790,12 @@ mt7915_set_stream_he_txbf_caps(struct mt7915_dev *dev, elem->phy_cap_info[4] |= IEEE80211_HE_PHY_CAP4_MU_BEAMFORMER; /* num_snd_dim - * for mt7915, max supported nss is 2 for bw > 80MHz + * for mt7915, max supported sts is 2 for bw > 80MHz */ c = FIELD_PREP(IEEE80211_HE_PHY_CAP5_BEAMFORMEE_NUM_SND_DIM_UNDER_80MHZ_MASK, - nss - 1) | + sts - 1) | FIELD_PREP(IEEE80211_HE_PHY_CAP5_BEAMFORMEE_NUM_SND_DIM_ABOVE_80MHZ_MASK, - nss_160 - 1); + sts_160 - 1); elem->phy_cap_info[5] |= c; c = IEEE80211_HE_PHY_CAP6_TRIG_SU_BEAMFORMING_FB | @@ -836,7 +835,7 @@ mt7915_init_he_caps(struct mt7915_phy *phy, enum nl80211_band band, struct ieee80211_sband_iftype_data *data) { struct mt7915_dev *dev = phy->dev; - int i, idx = 0, nss = hweight8(phy->mt76->chainmask); + int i, idx = 0, nss = hweight8(phy->mt76->antenna_mask); u16 mcs_map = 0; u16 mcs_map_160 = 0; u8 nss_160; @@ -969,7 +968,7 @@ mt7915_init_he_caps(struct mt7915_phy *phy, enum nl80211_band band, he_mcs->rx_mcs_80p80 = cpu_to_le16(mcs_map_160); he_mcs->tx_mcs_80p80 = cpu_to_le16(mcs_map_160); - mt7915_set_stream_he_txbf_caps(dev, he_cap, i, nss); + mt7915_set_stream_he_txbf_caps(phy, he_cap, i); memset(he_cap->ppe_thres, 0, sizeof(he_cap->ppe_thres)); if (he_cap_elem->phy_cap_info[6] & diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/main.c b/drivers/net/wireless/mediatek/mt76/mt7915/main.c index 89b519cfd14c..0e852a7d3ecd 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/main.c @@ -953,7 +953,7 @@ mt7915_set_antenna(struct ieee80211_hw *hw, u32 tx_ant, u32 rx_ant) struct mt7915_dev *dev = mt7915_hw_dev(hw); struct mt7915_phy *phy = mt7915_hw_phy(hw); int max_nss = hweight8(hw->wiphy->available_antennas_tx); - bool ext_phy = phy != &dev->phy; + u8 chainshift = dev->chainshift; if (!tx_ant || tx_ant != rx_ant || ffs(tx_ant) > max_nss) return -EINVAL; @@ -965,10 +965,11 @@ mt7915_set_antenna(struct ieee80211_hw *hw, u32 tx_ant, u32 rx_ant) phy->mt76->antenna_mask = tx_ant; - if (ext_phy) - tx_ant <<= dev->chainshift; - - phy->mt76->chainmask = tx_ant; + /* handle a variant of mt7916 which has 3T3R but nss2 on 5 GHz band */ + if (is_mt7916(&dev->mt76) && phy->band_idx && hweight8(tx_ant) == max_nss) + phy->mt76->chainmask = (dev->chainmask >> chainshift) << chainshift; + else + phy->mt76->chainmask = tx_ant << (chainshift * phy->band_idx); mt76_set_stream_caps(phy->mt76, true); mt7915_set_stream_vht_txbf_caps(phy); diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c index 1b9f3da8ae96..b9ad24b1935a 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c @@ -485,7 +485,7 @@ static void mt7915_mcu_bss_ra_tlv(struct sk_buff *skb, struct ieee80211_vif *vif, struct mt7915_phy *phy) { - int max_nss = hweight8(phy->mt76->chainmask); + int max_nss = hweight8(phy->mt76->antenna_mask); struct bss_info_ra *ra; struct tlv *tlv; @@ -2617,8 +2617,8 @@ int mt7915_mcu_set_chan_info(struct mt7915_phy *phy, int cmd) u8 control_ch; u8 center_ch; u8 bw; - u8 tx_streams_num; - u8 rx_streams; /* mask or num */ + u8 tx_path_num; + u8 rx_path; /* mask or num */ u8 switch_reason; u8 band_idx; u8 center_ch2; /* for 80+80 only */ @@ -2634,8 +2634,8 @@ int mt7915_mcu_set_chan_info(struct mt7915_phy *phy, int cmd) .control_ch = chandef->chan->hw_value, .center_ch = ieee80211_frequency_to_channel(freq1), .bw = mt76_connac_chan_bw(chandef), - .tx_streams_num = hweight8(phy->mt76->antenna_mask), - .rx_streams = phy->mt76->antenna_mask, + .tx_path_num = hweight16(phy->mt76->chainmask), + .rx_path = phy->mt76->chainmask >> (dev->chainshift * phy->band_idx), .band_idx = phy->band_idx, .channel_band = ch_band[chandef->chan->band], }; @@ -2645,8 +2645,8 @@ int mt7915_mcu_set_chan_info(struct mt7915_phy *phy, int cmd) (phy->mt76->test.state == MT76_TM_STATE_TX_FRAMES || phy->mt76->test.state == MT76_TM_STATE_RX_FRAMES || phy->mt76->test.state == MT76_TM_STATE_TX_CONT)) { - req.tx_streams_num = fls(phy->mt76->test.tx_antenna_mask); - req.rx_streams = phy->mt76->test.tx_antenna_mask; + req.tx_path_num = fls(phy->mt76->test.tx_antenna_mask); + req.rx_path = phy->mt76->test.tx_antenna_mask; if (phy != &dev->phy) req.rx_streams >>= dev->chainshift; @@ -2665,7 +2665,7 @@ int mt7915_mcu_set_chan_info(struct mt7915_phy *phy, int cmd) req.switch_reason = CH_SWITCH_NORMAL; if (cmd == MCU_EXT_CMD(CHANNEL_SWITCH)) - req.rx_streams = hweight8(req.rx_streams); + req.rx_path = hweight8(req.rx_path); if (chandef->width == NL80211_CHAN_WIDTH_80P80) { int freq2 = chandef->center_freq2; From b61699d2cd5692cd8c366409c4715b2a1e0ff1af Mon Sep 17 00:00:00 2001 From: Shayne Chen Date: Fri, 30 Sep 2022 23:13:12 +0800 Subject: [PATCH 1454/2002] wifi: mt76: mt7915: rework testmode tx antenna setting Let the configuration of testmode antenna mask on both band0 and band1 become antenna_mask rather than chainmask. This could simplify the settings for user and get rid of the conversion when sending fw command. Signed-off-by: Shayne Chen Signed-off-by: Felix Fietkau --- .../net/wireless/mediatek/mt76/mt7915/mcu.c | 7 +------ .../wireless/mediatek/mt76/mt7915/testmode.c | 18 ++++++++---------- 2 files changed, 9 insertions(+), 16 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c index b9ad24b1935a..4a2196f9df61 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c @@ -2642,14 +2642,9 @@ int mt7915_mcu_set_chan_info(struct mt7915_phy *phy, int cmd) #ifdef CONFIG_NL80211_TESTMODE if (phy->mt76->test.tx_antenna_mask && - (phy->mt76->test.state == MT76_TM_STATE_TX_FRAMES || - phy->mt76->test.state == MT76_TM_STATE_RX_FRAMES || - phy->mt76->test.state == MT76_TM_STATE_TX_CONT)) { + mt76_testmode_enabled(phy->mt76)) { req.tx_path_num = fls(phy->mt76->test.tx_antenna_mask); req.rx_path = phy->mt76->test.tx_antenna_mask; - - if (phy != &dev->phy) - req.rx_streams >>= dev->chainshift; } #endif diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/testmode.c b/drivers/net/wireless/mediatek/mt76/mt7915/testmode.c index efb9bb8231e2..e1838f046568 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/testmode.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/testmode.c @@ -447,15 +447,10 @@ mt7915_tm_set_tx_frames(struct mt7915_phy *phy, bool en) if (en) { mt7915_tm_update_channel(phy); - if (td->tx_spe_idx) { + if (td->tx_spe_idx) phy->test.spe_idx = td->tx_spe_idx; - } else { - u8 tx_ant = td->tx_antenna_mask; - - if (phy != &dev->phy) - tx_ant >>= dev->chainshift; - phy->test.spe_idx = spe_idx_map[tx_ant]; - } + else + phy->test.spe_idx = spe_idx_map[td->tx_antenna_mask]; } mt7915_tm_set_tam_arb(phy, en, @@ -696,7 +691,9 @@ mt7915_tm_set_params(struct mt76_phy *mphy, struct nlattr **tb, { struct mt76_testmode_data *td = &mphy->test; struct mt7915_phy *phy = mphy->priv; - u32 changed = 0; + struct mt7915_dev *dev = phy->dev; + u32 chainmask = mphy->chainmask, changed = 0; + bool ext_phy = phy != &dev->phy; int i; BUILD_BUG_ON(NUM_TM_CHANGED >= 32); @@ -705,7 +702,8 @@ mt7915_tm_set_params(struct mt76_phy *mphy, struct nlattr **tb, td->state == MT76_TM_STATE_OFF) return 0; - if (td->tx_antenna_mask & ~mphy->chainmask) + chainmask = ext_phy ? chainmask >> dev->chainshift : chainmask; + if (td->tx_antenna_mask > chainmask) return -EINVAL; for (i = 0; i < ARRAY_SIZE(tm_change_map); i++) { From faf2e7b5de08acd296bd12cfa20a32b92a8170cf Mon Sep 17 00:00:00 2001 From: Shayne Chen Date: Fri, 30 Sep 2022 23:13:13 +0800 Subject: [PATCH 1455/2002] wifi: mt76: connac: introduce mt76_connac_spe_idx() Add mt76_connac_spe_idx() for antenna mask to spatial extension index conversion. This is used to support flexible control of tx antenna. Signed-off-by: Shayne Chen Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt76_connac.h | 11 +++++++++++ drivers/net/wireless/mediatek/mt76/mt76_connac_mac.c | 11 ++++++++--- drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h | 2 +- 3 files changed, 20 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac.h b/drivers/net/wireless/mediatek/mt76/mt76_connac.h index 635192c878cb..0915eb579539 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76_connac.h +++ b/drivers/net/wireless/mediatek/mt76/mt76_connac.h @@ -261,6 +261,17 @@ mt76_connac_txwi_to_txp(struct mt76_dev *dev, struct mt76_txwi_cache *t) return (void *)(txwi + MT_TXD_SIZE); } +static inline u8 mt76_connac_spe_idx(u8 antenna_mask) +{ + static const u8 ant_to_spe[] = {0, 0, 1, 0, 3, 2, 4, 0, + 9, 8, 6, 10, 16, 12, 18, 0}; + + if (antenna_mask >= sizeof(ant_to_spe)) + return 0; + + return ant_to_spe[antenna_mask]; +} + int mt76_connac_pm_wake(struct mt76_phy *phy, struct mt76_connac_pm *pm); void mt76_connac_power_save_sched(struct mt76_phy *phy, struct mt76_connac_pm *pm); diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac_mac.c b/drivers/net/wireless/mediatek/mt76/mt76_connac_mac.c index 34ac3d81a510..8b7ec64abc95 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76_connac_mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt76_connac_mac.c @@ -417,9 +417,6 @@ mt76_connac2_mac_write_txwi_80211(struct mt76_dev *dev, __le32 *txwi, if (ieee80211_is_beacon(fc)) { txwi[3] &= ~cpu_to_le32(MT_TXD3_SW_POWER_MGMT); txwi[3] |= cpu_to_le32(MT_TXD3_REM_TX_COUNT); - if (!is_mt7921(dev)) - txwi[7] |= cpu_to_le32(FIELD_PREP(MT_TXD7_SPE_IDX, - 0x18)); } if (info->flags & IEEE80211_TX_CTL_INJECTED) { @@ -550,6 +547,14 @@ void mt76_connac2_mac_write_txwi(struct mt76_dev *dev, __le32 *txwi, val |= FIELD_PREP(MT_TXD6_TX_RATE, rate); txwi[6] |= cpu_to_le32(val); txwi[3] |= cpu_to_le32(MT_TXD3_BA_DISABLE); + + if (!is_mt7921(dev)) { + u8 spe_idx = mt76_connac_spe_idx(mphy->antenna_mask); + + if (!spe_idx) + spe_idx = 24 + phy_idx; + txwi[7] |= cpu_to_le32(FIELD_PREP(MT_TXD7_SPE_IDX, spe_idx)); + } } } EXPORT_SYMBOL_GPL(mt76_connac2_mac_write_txwi); diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h index 718f427d8f6b..cf4ce3b1fc21 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h +++ b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h @@ -580,7 +580,7 @@ struct sta_rec_ra_fixed { struct sta_phy phy; - u8 spe_en; + u8 spe_idx; u8 short_preamble; u8 is_5g; u8 mmps_mode; From 7a9a957b2be6e894d7d207d987c343b566d71b1d Mon Sep 17 00:00:00 2001 From: Shayne Chen Date: Fri, 30 Sep 2022 23:13:14 +0800 Subject: [PATCH 1456/2002] wifi: mt76: mt7915: add spatial extension index support In previous, we only allow user to configure tx antenna mask contiguously (e.g. 0x3, 0xf). This patch allows user to configure tx antenna mask interleavingly (e.g. 0x5, 0x8). By setting proper antenna mask and nss, user can prioritized the signal of different antennas, which helps to test their performance in normal mode. Signed-off-by: Shayne Chen Signed-off-by: Felix Fietkau --- .../net/wireless/mediatek/mt76/mt7915/main.c | 3 --- .../net/wireless/mediatek/mt76/mt7915/mcu.c | 20 ++++++++++++++++++- .../net/wireless/mediatek/mt76/mt7915/mcu.h | 1 + .../wireless/mediatek/mt76/mt7915/testmode.c | 4 +--- 4 files changed, 21 insertions(+), 7 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/main.c b/drivers/net/wireless/mediatek/mt76/mt7915/main.c index 0e852a7d3ecd..409f57382cc7 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/main.c @@ -958,9 +958,6 @@ mt7915_set_antenna(struct ieee80211_hw *hw, u32 tx_ant, u32 rx_ant) if (!tx_ant || tx_ant != rx_ant || ffs(tx_ant) > max_nss) return -EINVAL; - if ((BIT(hweight8(tx_ant)) - 1) != tx_ant) - tx_ant = BIT(ffs(tx_ant) - 1) - 1; - mutex_lock(&dev->mt76.mutex); phy->mt76->antenna_mask = tx_ant; diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c index 4a2196f9df61..0c90d3666255 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c @@ -1306,6 +1306,9 @@ int mt7915_mcu_set_fixed_rate_ctrl(struct mt7915_dev *dev, case RATE_PARAM_MMPS_UPDATE: ra->mmps_mode = mt7915_mcu_get_mmps_mode(sta->deflink.smps_mode); break; + case RATE_PARAM_SPE_UPDATE: + ra->spe_idx = *(u8 *)data; + break; default: break; } @@ -1348,6 +1351,18 @@ int mt7915_mcu_add_smps(struct mt7915_dev *dev, struct ieee80211_vif *vif, RATE_PARAM_MMPS_UPDATE); } +static int +mt7915_mcu_set_spe_idx(struct mt7915_dev *dev, struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + struct mt7915_vif *mvif = (struct mt7915_vif *)vif->drv_priv; + struct mt76_phy *mphy = mvif->phy->mt76; + u8 spe_idx = mt76_connac_spe_idx(mphy->antenna_mask); + + return mt7915_mcu_set_fixed_rate_ctrl(dev, vif, sta, &spe_idx, + RATE_PARAM_SPE_UPDATE); +} + static int mt7915_mcu_add_rate_ctrl_fixed(struct mt7915_dev *dev, struct ieee80211_vif *vif, @@ -1435,7 +1450,7 @@ mt7915_mcu_add_rate_ctrl_fixed(struct mt7915_dev *dev, return ret; } - return 0; + return mt7915_mcu_set_spe_idx(dev, vif, sta); } static void @@ -2648,6 +2663,9 @@ int mt7915_mcu_set_chan_info(struct mt7915_phy *phy, int cmd) } #endif + if (mt76_connac_spe_idx(phy->mt76->antenna_mask)) + req.tx_path_num = fls(phy->mt76->antenna_mask); + if (cmd == MCU_EXT_CMD(SET_RX_PATH) || dev->mt76.hw->conf.flags & IEEE80211_CONF_MONITOR) req.switch_reason = CH_SWITCH_NORMAL; diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.h b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.h index cd1edf553fc1..87cd1bfff3e6 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.h +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.h @@ -394,6 +394,7 @@ enum { RATE_PARAM_FIXED_MCS, RATE_PARAM_FIXED_GI = 11, RATE_PARAM_AUTO = 20, + RATE_PARAM_SPE_UPDATE = 22, }; #define RATE_CFG_MCS GENMASK(3, 0) diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/testmode.c b/drivers/net/wireless/mediatek/mt76/mt7915/testmode.c index e1838f046568..a979460fad2d 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/testmode.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/testmode.c @@ -432,8 +432,6 @@ mt7915_tm_update_channel(struct mt7915_phy *phy) static void mt7915_tm_set_tx_frames(struct mt7915_phy *phy, bool en) { - static const u8 spe_idx_map[] = {0, 0, 1, 0, 3, 2, 4, 0, - 9, 8, 6, 10, 16, 12, 18, 0}; struct mt76_testmode_data *td = &phy->mt76->test; struct mt7915_dev *dev = phy->dev; struct ieee80211_tx_info *info; @@ -450,7 +448,7 @@ mt7915_tm_set_tx_frames(struct mt7915_phy *phy, bool en) if (td->tx_spe_idx) phy->test.spe_idx = td->tx_spe_idx; else - phy->test.spe_idx = spe_idx_map[td->tx_antenna_mask]; + phy->test.spe_idx = mt76_connac_spe_idx(td->tx_antenna_mask); } mt7915_tm_set_tam_arb(phy, en, From 7a12e06dacdcf2db82e11d6be42e66e366b467d6 Mon Sep 17 00:00:00 2001 From: Shayne Chen Date: Fri, 30 Sep 2022 23:13:15 +0800 Subject: [PATCH 1457/2002] wifi: mt76: mt7915: set correct antenna for radar detection on MT7915D For MT7915D, correct antenna index should be set to let DFS radar detection work on the 5 GHz band. Signed-off-by: Shayne Chen Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt7915/mac.c | 15 +++++++++++++++ .../net/wireless/mediatek/mt76/mt7915/mt7915.h | 1 + 2 files changed, 16 insertions(+) diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mac.c b/drivers/net/wireless/mediatek/mt76/mt7915/mac.c index 89a3810ee53f..d0285a02775a 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mac.c @@ -1818,6 +1818,13 @@ static int mt7915_dfs_start_rdd(struct mt7915_dev *dev, int chain) if (err < 0) return err; + if (is_mt7915(&dev->mt76)) { + err = mt76_connac_mcu_rdd_cmd(&dev->mt76, RDD_SET_WF_ANT, chain, + 0, dev->dbdc_support ? 2 : 0); + if (err < 0) + return err; + } + return mt76_connac_mcu_rdd_cmd(&dev->mt76, RDD_DET_MODE, chain, MT_RX_SEL0, 1); } @@ -1938,6 +1945,14 @@ stop: if (err < 0) return err; + if (is_mt7915(&dev->mt76)) { + err = mt76_connac_mcu_rdd_cmd(&dev->mt76, RDD_SET_WF_ANT, + phy->band_idx, 0, + dev->dbdc_support ? 2 : 0); + if (err < 0) + return err; + } + mt7915_dfs_stop_radar_detector(phy); phy->mt76->dfs_state = MT_DFS_STATE_DISABLED; diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h b/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h index 1eb11617a625..c90a148d6239 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h @@ -357,6 +357,7 @@ enum mt7915_rdd_cmd { RDD_DET_MODE, RDD_RADAR_EMULATE, RDD_START_TXQ = 20, + RDD_SET_WF_ANT = 30, RDD_CAC_START = 50, RDD_CAC_END, RDD_NORMAL_START, From 0c881dc08fd71ca2673f31a64989fbb28eac26f4 Mon Sep 17 00:00:00 2001 From: Ryder Lee Date: Sat, 1 Oct 2022 09:42:44 +0800 Subject: [PATCH 1458/2002] wifi: mt76: mt7915: fix mt7915_mac_set_timing() Correct mac timiing settings for different hardware generations. This improves 40-60Mbps performance. Fixes: 9aac2969fe5f ("mt76: mt7915: update mac timing settings") Reported-By: Carson Vandegriffe Tested-by: Chad Monroe Signed-off-by: Ryder Lee Signed-off-by: Felix Fietkau --- .../net/wireless/mediatek/mt76/mt7915/mac.c | 21 +++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mac.c b/drivers/net/wireless/mediatek/mt76/mt7915/mac.c index d0285a02775a..324f0f58572b 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mac.c @@ -1153,7 +1153,7 @@ void mt7915_mac_set_timing(struct mt7915_phy *phy) FIELD_PREP(MT_TIMEOUT_VAL_CCA, 48); u32 ofdm = FIELD_PREP(MT_TIMEOUT_VAL_PLCP, 60) | FIELD_PREP(MT_TIMEOUT_VAL_CCA, 28); - int offset; + int eifs_ofdm = 360, sifs = 10, offset; bool a_band = !(phy->mt76->chandef.chan->band == NL80211_BAND_2GHZ); if (!test_bit(MT76_STATE_RUNNING, &phy->mt76->state)) @@ -1171,17 +1171,26 @@ void mt7915_mac_set_timing(struct mt7915_phy *phy) reg_offset = FIELD_PREP(MT_TIMEOUT_VAL_PLCP, offset) | FIELD_PREP(MT_TIMEOUT_VAL_CCA, offset); + if (!is_mt7915(&dev->mt76)) { + if (!a_band) { + mt76_wr(dev, MT_TMAC_ICR1(phy->band_idx), + FIELD_PREP(MT_IFS_EIFS_CCK, 314)); + eifs_ofdm = 78; + } else { + eifs_ofdm = 84; + } + } else if (a_band) { + sifs = 16; + } + mt76_wr(dev, MT_TMAC_CDTR(phy->band_idx), cck + reg_offset); mt76_wr(dev, MT_TMAC_ODTR(phy->band_idx), ofdm + reg_offset); mt76_wr(dev, MT_TMAC_ICR0(phy->band_idx), - FIELD_PREP(MT_IFS_EIFS_OFDM, a_band ? 84 : 78) | + FIELD_PREP(MT_IFS_EIFS_OFDM, eifs_ofdm) | FIELD_PREP(MT_IFS_RIFS, 2) | - FIELD_PREP(MT_IFS_SIFS, 10) | + FIELD_PREP(MT_IFS_SIFS, sifs) | FIELD_PREP(MT_IFS_SLOT, phy->slottime)); - mt76_wr(dev, MT_TMAC_ICR1(phy->band_idx), - FIELD_PREP(MT_IFS_EIFS_CCK, 314)); - if (phy->slottime < 20 || a_band) val = MT7915_CFEND_RATE_DEFAULT; else From 5498cee3c84f117286f70c687a9e78e7aa137f4e Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Wed, 28 Sep 2022 11:19:41 +0200 Subject: [PATCH 1459/2002] wifi: mt76: mt7915: move wed init routines in mmio.c This is a preliminary patch to enable wed support for mt7986-wmac chipset. Tested-by: Daniel Golle Co-developed-by: Bo Jiao Signed-off-by: Bo Jiao Co-developed-by: Sujuan Chen Signed-off-by: Sujuan Chen Signed-off-by: Lorenzo Bianconi Signed-off-by: Felix Fietkau --- .../net/wireless/mediatek/mt76/mt7915/mmio.c | 91 ++++++++++++++++++ .../wireless/mediatek/mt76/mt7915/mt7915.h | 2 + .../net/wireless/mediatek/mt76/mt7915/pci.c | 93 +------------------ 3 files changed, 94 insertions(+), 92 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mmio.c b/drivers/net/wireless/mediatek/mt76/mt7915/mmio.c index 7bd5f6725d7b..e75f2229b6a1 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mmio.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mmio.c @@ -10,6 +10,9 @@ #include "mac.h" #include "../trace.h" +static bool wed_enable; +module_param(wed_enable, bool, 0644); + static const u32 mt7915_reg[] = { [INT_SOURCE_CSR] = 0xd7010, [INT_MASK_CSR] = 0xd7014, @@ -472,6 +475,94 @@ static u32 mt7915_rmw(struct mt76_dev *mdev, u32 offset, u32 mask, u32 val) return dev->bus_ops->rmw(mdev, addr, mask, val); } +#ifdef CONFIG_NET_MEDIATEK_SOC_WED +static int mt7915_mmio_wed_offload_enable(struct mtk_wed_device *wed) +{ + struct mt7915_dev *dev; + struct mt7915_phy *phy; + int ret; + + dev = container_of(wed, struct mt7915_dev, mt76.mmio.wed); + + spin_lock_bh(&dev->mt76.token_lock); + dev->mt76.token_size = wed->wlan.token_start; + spin_unlock_bh(&dev->mt76.token_lock); + + ret = wait_event_timeout(dev->mt76.tx_wait, + !dev->mt76.wed_token_count, HZ); + if (!ret) + return -EAGAIN; + + phy = &dev->phy; + mt76_set(dev, MT_AGG_ACR4(phy->band_idx), MT_AGG_ACR_PPDU_TXS2H); + + phy = dev->mt76.phys[MT_BAND1] ? dev->mt76.phys[MT_BAND1]->priv : NULL; + if (phy) + mt76_set(dev, MT_AGG_ACR4(phy->band_idx), + MT_AGG_ACR_PPDU_TXS2H); + + return 0; +} + +static void mt7915_mmio_wed_offload_disable(struct mtk_wed_device *wed) +{ + struct mt7915_dev *dev; + struct mt7915_phy *phy; + + dev = container_of(wed, struct mt7915_dev, mt76.mmio.wed); + + spin_lock_bh(&dev->mt76.token_lock); + dev->mt76.token_size = MT7915_TOKEN_SIZE; + spin_unlock_bh(&dev->mt76.token_lock); + + /* MT_TXD5_TX_STATUS_HOST (MPDU format) has higher priority than + * MT_AGG_ACR_PPDU_TXS2H (PPDU format) even though ACR bit is set. + */ + phy = &dev->phy; + mt76_clear(dev, MT_AGG_ACR4(phy->band_idx), MT_AGG_ACR_PPDU_TXS2H); + + phy = dev->mt76.phys[MT_BAND1] ? dev->mt76.phys[MT_BAND1]->priv : NULL; + if (phy) + mt76_clear(dev, MT_AGG_ACR4(phy->band_idx), + MT_AGG_ACR_PPDU_TXS2H); +} +#endif + +int mt7915_mmio_wed_init(struct mt7915_dev *dev, struct pci_dev *pdev, + int *irq) +{ +#ifdef CONFIG_NET_MEDIATEK_SOC_WED + struct mtk_wed_device *wed = &dev->mt76.mmio.wed; + int ret; + + if (!wed_enable) + return 0; + + wed->wlan.pci_dev = pdev; + wed->wlan.wpdma_phys = pci_resource_start(pdev, 0) + + MT_WFDMA_EXT_CSR_BASE; + wed->wlan.nbuf = 4096; + wed->wlan.token_start = MT7915_TOKEN_SIZE - wed->wlan.nbuf; + wed->wlan.init_buf = mt7915_wed_init_buf; + wed->wlan.offload_enable = mt7915_mmio_wed_offload_enable; + wed->wlan.offload_disable = mt7915_mmio_wed_offload_disable; + + if (mtk_wed_device_attach(wed) != 0) + return 0; + + *irq = wed->irq; + dev->mt76.dma_dev = wed->dev; + + ret = dma_set_mask(wed->dev, DMA_BIT_MASK(32)); + if (ret) + return ret; + + return 1; +#else + return 0; +#endif +} + static int mt7915_mmio_init(struct mt76_dev *mdev, void __iomem *mem_base, u32 device_id) diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h b/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h index c90a148d6239..1c1e4058d048 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h @@ -591,5 +591,7 @@ bool mt7915_debugfs_rx_log(struct mt7915_dev *dev, const void *data, int len); void mt7915_sta_add_debugfs(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_sta *sta, struct dentry *dir); #endif +int mt7915_mmio_wed_init(struct mt7915_dev *dev, struct pci_dev *pdev, + int *irq); #endif diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/pci.c b/drivers/net/wireless/mediatek/mt76/mt7915/pci.c index 728a879c3b00..d73b78bba3e5 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/pci.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/pci.c @@ -12,9 +12,6 @@ #include "mac.h" #include "../trace.h" -static bool wed_enable = false; -module_param(wed_enable, bool, 0644); - static LIST_HEAD(hif_list); static DEFINE_SPINLOCK(hif_lock); static u32 hif_idx; @@ -95,94 +92,6 @@ static int mt7915_pci_hif2_probe(struct pci_dev *pdev) return 0; } -#ifdef CONFIG_NET_MEDIATEK_SOC_WED -static int mt7915_wed_offload_enable(struct mtk_wed_device *wed) -{ - struct mt7915_dev *dev; - struct mt7915_phy *phy; - int ret; - - dev = container_of(wed, struct mt7915_dev, mt76.mmio.wed); - - spin_lock_bh(&dev->mt76.token_lock); - dev->mt76.token_size = wed->wlan.token_start; - spin_unlock_bh(&dev->mt76.token_lock); - - ret = wait_event_timeout(dev->mt76.tx_wait, - !dev->mt76.wed_token_count, HZ); - if (!ret) - return -EAGAIN; - - phy = &dev->phy; - mt76_set(dev, MT_AGG_ACR4(phy->band_idx), MT_AGG_ACR_PPDU_TXS2H); - - phy = dev->mt76.phys[MT_BAND1] ? dev->mt76.phys[MT_BAND1]->priv : NULL; - if (phy) - mt76_set(dev, MT_AGG_ACR4(phy->band_idx), - MT_AGG_ACR_PPDU_TXS2H); - - return 0; -} - -static void mt7915_wed_offload_disable(struct mtk_wed_device *wed) -{ - struct mt7915_dev *dev; - struct mt7915_phy *phy; - - dev = container_of(wed, struct mt7915_dev, mt76.mmio.wed); - - spin_lock_bh(&dev->mt76.token_lock); - dev->mt76.token_size = MT7915_TOKEN_SIZE; - spin_unlock_bh(&dev->mt76.token_lock); - - /* MT_TXD5_TX_STATUS_HOST (MPDU format) has higher priority than - * MT_AGG_ACR_PPDU_TXS2H (PPDU format) even though ACR bit is set. - */ - phy = &dev->phy; - mt76_clear(dev, MT_AGG_ACR4(phy->band_idx), MT_AGG_ACR_PPDU_TXS2H); - - phy = dev->mt76.phys[MT_BAND1] ? dev->mt76.phys[MT_BAND1]->priv : NULL; - if (phy) - mt76_clear(dev, MT_AGG_ACR4(phy->band_idx), - MT_AGG_ACR_PPDU_TXS2H); -} -#endif - -static int -mt7915_pci_wed_init(struct mt7915_dev *dev, struct pci_dev *pdev, int *irq) -{ -#ifdef CONFIG_NET_MEDIATEK_SOC_WED - struct mtk_wed_device *wed = &dev->mt76.mmio.wed; - int ret; - - if (!wed_enable) - return 0; - - wed->wlan.pci_dev = pdev; - wed->wlan.wpdma_phys = pci_resource_start(pdev, 0) + - MT_WFDMA_EXT_CSR_BASE; - wed->wlan.nbuf = 4096; - wed->wlan.token_start = MT7915_TOKEN_SIZE - wed->wlan.nbuf; - wed->wlan.init_buf = mt7915_wed_init_buf; - wed->wlan.offload_enable = mt7915_wed_offload_enable; - wed->wlan.offload_disable = mt7915_wed_offload_disable; - - if (mtk_wed_device_attach(wed) != 0) - return 0; - - *irq = wed->irq; - dev->mt76.dma_dev = wed->dev; - - ret = dma_set_mask(wed->dev, DMA_BIT_MASK(32)); - if (ret) - return ret; - - return 1; -#else - return 0; -#endif -} - static int mt7915_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) { @@ -220,7 +129,7 @@ static int mt7915_pci_probe(struct pci_dev *pdev, mt7915_wfsys_reset(dev); hif2 = mt7915_pci_init_hif2(pdev); - ret = mt7915_pci_wed_init(dev, pdev, &irq); + ret = mt7915_mmio_wed_init(dev, pdev, &irq); if (ret < 0) goto free_wed_or_irq_vector; From b7ebf46e2526c860d46f98ab93cab547069461f6 Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Wed, 28 Sep 2022 11:19:42 +0200 Subject: [PATCH 1460/2002] wifi: mt76: mt7915: enable wed for mt7986 chipset Introduce wed tx support for MT7986 chipset Tested-by: Daniel Golle Co-developed-by: Bo Jiao Signed-off-by: Bo Jiao Co-developed-by: Sujuan Chen Signed-off-by: Sujuan Chen Signed-off-by: Lorenzo Bianconi Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt7915/dma.c | 1 + drivers/net/wireless/mediatek/mt76/mt7915/mmio.c | 11 +++++++++++ drivers/net/wireless/mediatek/mt76/mt7915/regs.h | 8 ++++++++ 3 files changed, 20 insertions(+) diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/dma.c b/drivers/net/wireless/mediatek/mt76/mt7915/dma.c index 00aafc2422f3..90a89cf493a9 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/dma.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/dma.c @@ -324,6 +324,7 @@ static int mt7915_dma_enable(struct mt7915_dev *dev) wed_irq_mask |= MT_INT_TX_DONE_BAND0 | MT_INT_TX_DONE_BAND1; mt76_wr(dev, MT_INT_WED_MASK_CSR, wed_irq_mask); + mt76_wr(dev, MT_INT_MASK_CSR, wed_irq_mask); mtk_wed_device_start(&dev->mt76.mmio.wed, wed_irq_mask); } diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mmio.c b/drivers/net/wireless/mediatek/mt76/mt7915/mmio.c index e75f2229b6a1..5676d62933ad 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mmio.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mmio.c @@ -541,7 +541,18 @@ int mt7915_mmio_wed_init(struct mt7915_dev *dev, struct pci_dev *pdev, wed->wlan.pci_dev = pdev; wed->wlan.wpdma_phys = pci_resource_start(pdev, 0) + MT_WFDMA_EXT_CSR_BASE; + wed->wlan.wpdma_int = pci_resource_start(pdev, 0) + + MT_INT_WED_SOURCE_CSR; + wed->wlan.wpdma_mask = pci_resource_start(pdev, 0) + + MT_INT_WED_MASK_CSR; + wed->wlan.wpdma_tx = pci_resource_start(pdev, 0) + + MT_TXQ_WED_RING_BASE; + wed->wlan.wpdma_txfree = pci_resource_start(pdev, 0) + + MT_RXQ_WED_RING_BASE; wed->wlan.nbuf = 4096; + wed->wlan.tx_tbit[0] = MT_WED_TX_DONE_BAND0; + wed->wlan.tx_tbit[1] = MT_WED_TX_DONE_BAND1; + wed->wlan.txfree_tbit = MT_WED_TX_FREE_DONE; wed->wlan.token_start = MT7915_TOKEN_SIZE - wed->wlan.nbuf; wed->wlan.init_buf = mt7915_wed_init_buf; wed->wlan.offload_enable = mt7915_mmio_wed_offload_enable; diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/regs.h b/drivers/net/wireless/mediatek/mt76/mt7915/regs.h index 5920e705835a..d9642c5817e9 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/regs.h +++ b/drivers/net/wireless/mediatek/mt76/mt7915/regs.h @@ -596,6 +596,7 @@ enum offs_rev { #define MT_PCIE_RECOG_ID_MASK GENMASK(30, 0) #define MT_PCIE_RECOG_ID_SEM BIT(31) +#define MT_INT_WED_SOURCE_CSR MT_WFDMA_EXT_CSR(0x200) #define MT_INT_WED_MASK_CSR MT_WFDMA_EXT_CSR(0x204) #define MT_WED_TX_RING_BASE MT_WFDMA_EXT_CSR(0x300) @@ -642,6 +643,13 @@ enum offs_rev { #define MT_TXQ_EXT_CTRL(q) (MT_Q_BASE(__TXQ(q)) + 0x600 + \ MT_TXQ_ID(q)* 0x4) +#define MT_TXQ_WED_RING_BASE 0xd7300 +#define MT_RXQ_WED_RING_BASE 0xd7410 + +#define MT_WED_TX_DONE_BAND0 4 +#define MT_WED_TX_DONE_BAND1 5 +#define MT_WED_TX_FREE_DONE 1 + #define MT_INT_SOURCE_CSR __REG(INT_SOURCE_CSR) #define MT_INT_MASK_CSR __REG(INT_MASK_CSR) From eebb70976be5054feff92bc6b96174f1cf342a32 Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Wed, 28 Sep 2022 11:19:43 +0200 Subject: [PATCH 1461/2002] wifi: mt76: mt7915: enable wed for mt7986-wmac chipset Enable WED tx support for mt7986-wmac chipset available on mt7986 board. Tested-by: Daniel Golle Co-developed-by: Bo Jiao Signed-off-by: Bo Jiao Co-developed-by: Sujuan Chen Signed-off-by: Sujuan Chen Signed-off-by: Lorenzo Bianconi Signed-off-by: Felix Fietkau --- .../net/wireless/mediatek/mt76/mt7915/dma.c | 72 ++++++++++++++----- .../net/wireless/mediatek/mt76/mt7915/main.c | 2 +- .../net/wireless/mediatek/mt76/mt7915/mcu.c | 2 +- .../net/wireless/mediatek/mt76/mt7915/mmio.c | 67 ++++++++++++----- .../wireless/mediatek/mt76/mt7915/mt7915.h | 4 +- .../net/wireless/mediatek/mt76/mt7915/pci.c | 2 +- .../net/wireless/mediatek/mt76/mt7915/regs.h | 17 +++-- .../net/wireless/mediatek/mt76/mt7915/soc.c | 21 ++++-- 8 files changed, 133 insertions(+), 54 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/dma.c b/drivers/net/wireless/mediatek/mt76/mt7915/dma.c index 90a89cf493a9..e4fa240834d8 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/dma.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/dma.c @@ -11,7 +11,11 @@ mt7915_init_tx_queues(struct mt7915_phy *phy, int idx, int n_desc, int ring_base struct mt7915_dev *dev = phy->dev; if (mtk_wed_device_active(&phy->dev->mt76.mmio.wed)) { - ring_base = MT_WED_TX_RING_BASE; + if (is_mt7986(&dev->mt76)) + ring_base += MT_TXQ_ID(0) * MT_RING_SIZE; + else + ring_base = MT_WED_TX_RING_BASE; + idx -= MT_TXQ_ID(0); } @@ -58,17 +62,32 @@ static void mt7915_dma_config(struct mt7915_dev *dev) MCUQ_CONFIG(MT_MCUQ_WA, WFDMA1, MT_INT_TX_DONE_MCU_WA, MT7915_TXQ_MCU_WA); MCUQ_CONFIG(MT_MCUQ_FWDL, WFDMA1, MT_INT_TX_DONE_FWDL, MT7915_TXQ_FWDL); } else { - RXQ_CONFIG(MT_RXQ_MAIN, WFDMA0, MT_INT_RX_DONE_BAND0_MT7916, MT7916_RXQ_BAND0); RXQ_CONFIG(MT_RXQ_MCU, WFDMA0, MT_INT_RX_DONE_WM, MT7916_RXQ_MCU_WM); - RXQ_CONFIG(MT_RXQ_MCU_WA, WFDMA0, MT_INT_RX_DONE_WA, MT7916_RXQ_MCU_WA); - RXQ_CONFIG(MT_RXQ_BAND1, WFDMA0, MT_INT_RX_DONE_BAND1_MT7916, MT7916_RXQ_BAND1); RXQ_CONFIG(MT_RXQ_BAND1_WA, WFDMA0, MT_INT_RX_DONE_WA_EXT_MT7916, MT7916_RXQ_MCU_WA_EXT); - RXQ_CONFIG(MT_RXQ_MAIN_WA, WFDMA0, MT_INT_RX_DONE_WA_MAIN_MT7916, MT7916_RXQ_MCU_WA_MAIN); - TXQ_CONFIG(0, WFDMA0, MT_INT_TX_DONE_BAND0, MT7915_TXQ_BAND0); - TXQ_CONFIG(1, WFDMA0, MT_INT_TX_DONE_BAND1, MT7915_TXQ_BAND1); MCUQ_CONFIG(MT_MCUQ_WM, WFDMA0, MT_INT_TX_DONE_MCU_WM, MT7915_TXQ_MCU_WM); MCUQ_CONFIG(MT_MCUQ_WA, WFDMA0, MT_INT_TX_DONE_MCU_WA_MT7916, MT7915_TXQ_MCU_WA); MCUQ_CONFIG(MT_MCUQ_FWDL, WFDMA0, MT_INT_TX_DONE_FWDL, MT7915_TXQ_FWDL); + + if (is_mt7916(&dev->mt76) && mtk_wed_device_active(&dev->mt76.mmio.wed)) { + RXQ_CONFIG(MT_RXQ_MAIN, WFDMA0, MT_INT_WED_RX_DONE_BAND0_MT7916, + MT7916_RXQ_BAND0); + RXQ_CONFIG(MT_RXQ_MCU_WA, WFDMA0, MT_INT_WED_RX_DONE_WA_MT7916, + MT7916_RXQ_MCU_WA); + RXQ_CONFIG(MT_RXQ_BAND1, WFDMA0, MT_INT_WED_RX_DONE_BAND1_MT7916, + MT7916_RXQ_BAND1); + RXQ_CONFIG(MT_RXQ_MAIN_WA, WFDMA0, MT_INT_WED_RX_DONE_WA_MAIN_MT7916, + MT7916_RXQ_MCU_WA_MAIN); + TXQ_CONFIG(0, WFDMA0, MT_INT_WED_TX_DONE_BAND0, MT7915_TXQ_BAND0); + TXQ_CONFIG(1, WFDMA0, MT_INT_WED_TX_DONE_BAND1, MT7915_TXQ_BAND1); + } else { + RXQ_CONFIG(MT_RXQ_MAIN, WFDMA0, MT_INT_RX_DONE_BAND0_MT7916, MT7916_RXQ_BAND0); + RXQ_CONFIG(MT_RXQ_MCU_WA, WFDMA0, MT_INT_RX_DONE_WA, MT7916_RXQ_MCU_WA); + RXQ_CONFIG(MT_RXQ_BAND1, WFDMA0, MT_INT_RX_DONE_BAND1_MT7916, MT7916_RXQ_BAND1); + RXQ_CONFIG(MT_RXQ_MAIN_WA, WFDMA0, MT_INT_RX_DONE_WA_MAIN_MT7916, + MT7916_RXQ_MCU_WA_MAIN); + TXQ_CONFIG(0, WFDMA0, MT_INT_TX_DONE_BAND0, MT7915_TXQ_BAND0); + TXQ_CONFIG(1, WFDMA0, MT_INT_TX_DONE_BAND1, MT7915_TXQ_BAND1); + } } } @@ -323,7 +342,8 @@ static int mt7915_dma_enable(struct mt7915_dev *dev) u32 wed_irq_mask = irq_mask; wed_irq_mask |= MT_INT_TX_DONE_BAND0 | MT_INT_TX_DONE_BAND1; - mt76_wr(dev, MT_INT_WED_MASK_CSR, wed_irq_mask); + if (!is_mt7986(&dev->mt76)) + mt76_wr(dev, MT_INT_WED_MASK_CSR, wed_irq_mask); mt76_wr(dev, MT_INT_MASK_CSR, wed_irq_mask); mtk_wed_device_start(&dev->mt76.mmio.wed, wed_irq_mask); } @@ -349,13 +369,18 @@ int mt7915_dma_init(struct mt7915_dev *dev, struct mt7915_phy *phy2) mt7915_dma_disable(dev, true); - if (mtk_wed_device_active(&dev->mt76.mmio.wed)) { - mt76_set(dev, MT_WFDMA_HOST_CONFIG, MT_WFDMA_HOST_CONFIG_WED); + if (mtk_wed_device_active(&mdev->mmio.wed)) { + if (!is_mt7986(mdev)) { + u8 wed_control_rx1 = is_mt7915(mdev) ? 1 : 2; - mt76_wr(dev, MT_WFDMA_WED_RING_CONTROL, - FIELD_PREP(MT_WFDMA_WED_RING_CONTROL_TX0, 18) | - FIELD_PREP(MT_WFDMA_WED_RING_CONTROL_TX1, 19) | - FIELD_PREP(MT_WFDMA_WED_RING_CONTROL_RX1, 1)); + mt76_set(dev, MT_WFDMA_HOST_CONFIG, + MT_WFDMA_HOST_CONFIG_WED); + mt76_wr(dev, MT_WFDMA_WED_RING_CONTROL, + FIELD_PREP(MT_WFDMA_WED_RING_CONTROL_TX0, 18) | + FIELD_PREP(MT_WFDMA_WED_RING_CONTROL_TX1, 19) | + FIELD_PREP(MT_WFDMA_WED_RING_CONTROL_RX1, + wed_control_rx1)); + } } else { mt76_clear(dev, MT_WFDMA_HOST_CONFIG, MT_WFDMA_HOST_CONFIG_WED); } @@ -411,7 +436,7 @@ int mt7915_dma_init(struct mt7915_dev *dev, struct mt7915_phy *phy2) return ret; /* event from WA */ - if (mtk_wed_device_active(&dev->mt76.mmio.wed)) { + if (mtk_wed_device_active(&mdev->mmio.wed) && is_mt7915(mdev)) { wa_rx_base = MT_WED_RX_RING_BASE; wa_rx_idx = MT7915_RXQ_MCU_WA; dev->mt76.q_rx[MT_RXQ_MCU_WA].flags = MT_WED_Q_TXFREE; @@ -438,11 +463,20 @@ int mt7915_dma_init(struct mt7915_dev *dev, struct mt7915_phy *phy2) /* tx free notify event from WA for band0 */ if (!is_mt7915(mdev)) { + wa_rx_base = MT_RXQ_RING_BASE(MT_RXQ_MAIN_WA); + wa_rx_idx = MT_RXQ_ID(MT_RXQ_MAIN_WA); + + if (mtk_wed_device_active(&mdev->mmio.wed)) { + mdev->q_rx[MT_RXQ_MAIN_WA].flags = MT_WED_Q_TXFREE; + if (is_mt7916(mdev)) { + wa_rx_base = MT_WED_RX_RING_BASE; + wa_rx_idx = MT7915_RXQ_MCU_WA; + } + } + ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_MAIN_WA], - MT_RXQ_ID(MT_RXQ_MAIN_WA), - MT7915_RX_MCU_RING_SIZE, - MT_RX_BUF_SIZE, - MT_RXQ_RING_BASE(MT_RXQ_MAIN_WA)); + wa_rx_idx, MT7915_RX_MCU_RING_SIZE, + MT_RX_BUF_SIZE, wa_rx_base); if (ret) return ret; } diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/main.c b/drivers/net/wireless/mediatek/mt76/mt7915/main.c index 409f57382cc7..0ebaed174d5d 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/main.c @@ -1429,7 +1429,7 @@ mt7915_net_fill_forward_path(struct ieee80211_hw *hw, path->dev = ctx->dev; path->mtk_wdma.wdma_idx = wed->wdma_idx; path->mtk_wdma.bss = mvif->mt76.idx; - path->mtk_wdma.wcid = msta->wcid.idx; + path->mtk_wdma.wcid = is_mt7915(&dev->mt76) ? msta->wcid.idx : 0x3ff; path->mtk_wdma.queue = phy != &dev->phy; ctx->dev = NULL; diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c index 0c90d3666255..790d690fabc3 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c @@ -2289,7 +2289,7 @@ int mt7915_mcu_init(struct mt7915_dev *dev) if (ret) return ret; - if (mtk_wed_device_active(&dev->mt76.mmio.wed)) + if (mtk_wed_device_active(&dev->mt76.mmio.wed) && is_mt7915(&dev->mt76)) mt7915_mcu_wa_cmd(dev, MCU_WA_PARAM_CMD(CAPABILITY), 0, 0, 0); ret = mt7915_mcu_set_mwds(dev, 1); diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mmio.c b/drivers/net/wireless/mediatek/mt76/mt7915/mmio.c index 5676d62933ad..2ffb509cad5c 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mmio.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mmio.c @@ -27,6 +27,8 @@ static const u32 mt7915_reg[] = { [INFRA_MCU_ADDR_END] = 0x7c3fffff, [FW_EXCEPTION_ADDR] = 0x219848, [SWDEF_BASE_ADDR] = 0x41f200, + [TXQ_WED_RING_BASE] = 0xd7300, + [RXQ_WED_RING_BASE] = 0xd7410, }; static const u32 mt7916_reg[] = { @@ -43,6 +45,8 @@ static const u32 mt7916_reg[] = { [INFRA_MCU_ADDR_END] = 0x7c085fff, [FW_EXCEPTION_ADDR] = 0x022050bc, [SWDEF_BASE_ADDR] = 0x411400, + [TXQ_WED_RING_BASE] = 0xd7300, + [RXQ_WED_RING_BASE] = 0xd7410, }; static const u32 mt7986_reg[] = { @@ -59,6 +63,8 @@ static const u32 mt7986_reg[] = { [INFRA_MCU_ADDR_END] = 0x7c085fff, [FW_EXCEPTION_ADDR] = 0x02204ffc, [SWDEF_BASE_ADDR] = 0x411400, + [TXQ_WED_RING_BASE] = 0x24420, + [RXQ_WED_RING_BASE] = 0x24520, }; static const u32 mt7915_offs[] = { @@ -528,8 +534,8 @@ static void mt7915_mmio_wed_offload_disable(struct mtk_wed_device *wed) } #endif -int mt7915_mmio_wed_init(struct mt7915_dev *dev, struct pci_dev *pdev, - int *irq) +int mt7915_mmio_wed_init(struct mt7915_dev *dev, void *pdev_ptr, + bool pci, int *irq) { #ifdef CONFIG_NET_MEDIATEK_SOC_WED struct mtk_wed_device *wed = &dev->mt76.mmio.wed; @@ -538,27 +544,46 @@ int mt7915_mmio_wed_init(struct mt7915_dev *dev, struct pci_dev *pdev, if (!wed_enable) return 0; - wed->wlan.pci_dev = pdev; - wed->wlan.wpdma_phys = pci_resource_start(pdev, 0) + - MT_WFDMA_EXT_CSR_BASE; - wed->wlan.wpdma_int = pci_resource_start(pdev, 0) + - MT_INT_WED_SOURCE_CSR; - wed->wlan.wpdma_mask = pci_resource_start(pdev, 0) + - MT_INT_WED_MASK_CSR; - wed->wlan.wpdma_tx = pci_resource_start(pdev, 0) + - MT_TXQ_WED_RING_BASE; - wed->wlan.wpdma_txfree = pci_resource_start(pdev, 0) + - MT_RXQ_WED_RING_BASE; + if (pci) { + struct pci_dev *pci_dev = pdev_ptr; + + wed->wlan.pci_dev = pci_dev; + wed->wlan.bus_type = MTK_WED_BUS_PCIE; + wed->wlan.wpdma_int = pci_resource_start(pci_dev, 0) + + MT_INT_WED_SOURCE_CSR; + wed->wlan.wpdma_mask = pci_resource_start(pci_dev, 0) + + MT_INT_WED_MASK_CSR; + wed->wlan.wpdma_phys = pci_resource_start(pci_dev, 0) + + MT_WFDMA_EXT_CSR_BASE; + wed->wlan.wpdma_tx = pci_resource_start(pci_dev, 0) + + MT_TXQ_WED_RING_BASE; + wed->wlan.wpdma_txfree = pci_resource_start(pci_dev, 0) + + MT_RXQ_WED_RING_BASE; + } else { + struct platform_device *plat_dev = pdev_ptr; + struct resource *res; + + res = platform_get_resource(plat_dev, IORESOURCE_MEM, 0); + if (!res) + return -ENOMEM; + + wed->wlan.platform_dev = plat_dev; + wed->wlan.bus_type = MTK_WED_BUS_AXI; + wed->wlan.wpdma_int = res->start + MT_INT_SOURCE_CSR; + wed->wlan.wpdma_mask = res->start + MT_INT_MASK_CSR; + wed->wlan.wpdma_tx = res->start + MT_TXQ_WED_RING_BASE; + wed->wlan.wpdma_txfree = res->start + MT_RXQ_WED_RING_BASE; + } wed->wlan.nbuf = 4096; - wed->wlan.tx_tbit[0] = MT_WED_TX_DONE_BAND0; - wed->wlan.tx_tbit[1] = MT_WED_TX_DONE_BAND1; - wed->wlan.txfree_tbit = MT_WED_TX_FREE_DONE; + wed->wlan.tx_tbit[0] = is_mt7915(&dev->mt76) ? 4 : 30; + wed->wlan.tx_tbit[1] = is_mt7915(&dev->mt76) ? 5 : 31; + wed->wlan.txfree_tbit = is_mt7915(&dev->mt76) ? 1 : 2; wed->wlan.token_start = MT7915_TOKEN_SIZE - wed->wlan.nbuf; wed->wlan.init_buf = mt7915_wed_init_buf; wed->wlan.offload_enable = mt7915_mmio_wed_offload_enable; wed->wlan.offload_disable = mt7915_mmio_wed_offload_disable; - if (mtk_wed_device_attach(wed) != 0) + if (mtk_wed_device_attach(wed)) return 0; *irq = wed->irq; @@ -638,7 +663,11 @@ void mt7915_dual_hif_set_irq_mask(struct mt7915_dev *dev, mdev->mmio.irqmask |= set; if (write_reg) { - mt76_wr(dev, MT_INT_MASK_CSR, mdev->mmio.irqmask); + if (mtk_wed_device_active(&mdev->mmio.wed)) + mtk_wed_device_irq_set_mask(&mdev->mmio.wed, + mdev->mmio.irqmask); + else + mt76_wr(dev, MT_INT_MASK_CSR, mdev->mmio.irqmask); mt76_wr(dev, MT_INT1_MASK_CSR, mdev->mmio.irqmask); } @@ -662,6 +691,8 @@ static void mt7915_irq_tasklet(struct tasklet_struct *t) if (mtk_wed_device_active(wed)) { mtk_wed_device_irq_set_mask(wed, 0); + if (dev->hif2) + mt76_wr(dev, MT_INT1_MASK_CSR, 0); intr = mtk_wed_device_irq_get(wed, dev->mt76.mmio.irqmask); } else { mt76_wr(dev, MT_INT_MASK_CSR, 0); diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h b/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h index 1c1e4058d048..fe6a6d3b0a32 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h @@ -591,7 +591,7 @@ bool mt7915_debugfs_rx_log(struct mt7915_dev *dev, const void *data, int len); void mt7915_sta_add_debugfs(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_sta *sta, struct dentry *dir); #endif -int mt7915_mmio_wed_init(struct mt7915_dev *dev, struct pci_dev *pdev, - int *irq); +int mt7915_mmio_wed_init(struct mt7915_dev *dev, void *pdev_ptr, + bool pci, int *irq); #endif diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/pci.c b/drivers/net/wireless/mediatek/mt76/mt7915/pci.c index d73b78bba3e5..743f01f6eae9 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/pci.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/pci.c @@ -129,7 +129,7 @@ static int mt7915_pci_probe(struct pci_dev *pdev, mt7915_wfsys_reset(dev); hif2 = mt7915_pci_init_hif2(pdev); - ret = mt7915_mmio_wed_init(dev, pdev, &irq); + ret = mt7915_mmio_wed_init(dev, pdev, true, &irq); if (ret < 0) goto free_wed_or_irq_vector; diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/regs.h b/drivers/net/wireless/mediatek/mt76/mt7915/regs.h index d9642c5817e9..5180dd931835 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/regs.h +++ b/drivers/net/wireless/mediatek/mt76/mt7915/regs.h @@ -26,6 +26,8 @@ enum reg_rev { INFRA_MCU_ADDR_END, FW_EXCEPTION_ADDR, SWDEF_BASE_ADDR, + TXQ_WED_RING_BASE, + RXQ_WED_RING_BASE, __MT_REG_MAX, }; @@ -643,12 +645,8 @@ enum offs_rev { #define MT_TXQ_EXT_CTRL(q) (MT_Q_BASE(__TXQ(q)) + 0x600 + \ MT_TXQ_ID(q)* 0x4) -#define MT_TXQ_WED_RING_BASE 0xd7300 -#define MT_RXQ_WED_RING_BASE 0xd7410 - -#define MT_WED_TX_DONE_BAND0 4 -#define MT_WED_TX_DONE_BAND1 5 -#define MT_WED_TX_FREE_DONE 1 +#define MT_TXQ_WED_RING_BASE __REG(TXQ_WED_RING_BASE) +#define MT_RXQ_WED_RING_BASE __REG(RXQ_WED_RING_BASE) #define MT_INT_SOURCE_CSR __REG(INT_SOURCE_CSR) #define MT_INT_MASK_CSR __REG(INT_MASK_CSR) @@ -668,6 +666,11 @@ enum offs_rev { #define MT_INT_RX_DONE_WA_MAIN_MT7916 BIT(2) #define MT_INT_RX_DONE_WA_EXT_MT7916 BIT(3) +#define MT_INT_WED_RX_DONE_BAND0_MT7916 BIT(18) +#define MT_INT_WED_RX_DONE_BAND1_MT7916 BIT(19) +#define MT_INT_WED_RX_DONE_WA_MAIN_MT7916 BIT(1) +#define MT_INT_WED_RX_DONE_WA_MT7916 BIT(17) + #define MT_INT_RX(q) (dev->q_int_mask[__RXQ(q)]) #define MT_INT_TX_MCU(q) (dev->q_int_mask[(q)]) @@ -691,6 +694,8 @@ enum offs_rev { #define MT_INT_TX_DONE_BAND0 BIT(30) #define MT_INT_TX_DONE_BAND1 BIT(31) #define MT_INT_TX_DONE_MCU_WA_MT7916 BIT(25) +#define MT_INT_WED_TX_DONE_BAND0 BIT(4) +#define MT_INT_WED_TX_DONE_BAND1 BIT(5) #define MT_INT_TX_DONE_MCU (MT_INT_TX_MCU(MT_MCUQ_WA) | \ MT_INT_TX_MCU(MT_MCUQ_WM) | \ diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/soc.c b/drivers/net/wireless/mediatek/mt76/mt7915/soc.c index c74afa746251..c06c56a0270d 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/soc.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/soc.c @@ -1172,10 +1172,6 @@ static int mt7986_wmac_probe(struct platform_device *pdev) chip_id = (uintptr_t)of_device_get_match_data(&pdev->dev); - irq = platform_get_irq(pdev, 0); - if (irq < 0) - return irq; - mem_base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(mem_base)) { dev_err(&pdev->dev, "Failed to get memory resource\n"); @@ -1187,6 +1183,18 @@ static int mt7986_wmac_probe(struct platform_device *pdev) return PTR_ERR(dev); mdev = &dev->mt76; + ret = mt7915_mmio_wed_init(dev, pdev, false, &irq); + if (ret < 0) + goto free_device; + + if (!ret) { + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + ret = irq; + goto free_device; + } + } + ret = devm_request_irq(mdev->dev, irq, mt7915_irq_handler, IRQF_SHARED, KBUILD_MODNAME, dev); if (ret) @@ -1206,9 +1214,10 @@ static int mt7986_wmac_probe(struct platform_device *pdev) free_irq: devm_free_irq(mdev->dev, irq, dev); - free_device: - mt76_free_device(&dev->mt76); + if (mtk_wed_device_active(&mdev->mmio.wed)) + mtk_wed_device_detach(&mdev->mmio.wed); + mt76_free_device(mdev); return ret; } From 5b0fb852237622bd4f48e80079216107c2b9fc1c Mon Sep 17 00:00:00 2001 From: Ben Greear Date: Sat, 1 Oct 2022 07:19:20 -0700 Subject: [PATCH 1462/2002] Revert "mt76: use IEEE80211_OFFLOAD_ENCAP_ENABLED instead of MT_DRV_AMSDU_OFFLOAD" This reverts commit f17f4864504d754bcbf31e4c89412cdf9946c409 and adds the MT_DRV_AMSDU_OFFLOAD flag for MT7921 USB/SDIO The reverted commit significantly decreases performance when running a test where two MT7915 radios have 16 station vdevs each, configured for AC mode, and transmitting UDP traffic to AP. Co-developed-by: Felix Fietkau Reported-by: Carson Vandegriffe Signed-off-by: Ben Greear Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mac80211.c | 8 ++++++-- drivers/net/wireless/mediatek/mt76/mt76.h | 1 + drivers/net/wireless/mediatek/mt76/mt7915/mmio.c | 3 ++- drivers/net/wireless/mediatek/mt76/mt7921/pci.c | 3 ++- drivers/net/wireless/mediatek/mt76/mt7921/sdio.c | 1 + drivers/net/wireless/mediatek/mt76/mt7921/usb.c | 3 ++- 6 files changed, 14 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mac80211.c b/drivers/net/wireless/mediatek/mt76/mac80211.c index 6de13d641438..4bdbfd0f6233 100644 --- a/drivers/net/wireless/mediatek/mt76/mac80211.c +++ b/drivers/net/wireless/mediatek/mt76/mac80211.c @@ -443,8 +443,12 @@ mt76_phy_init(struct mt76_phy *phy, struct ieee80211_hw *hw) ieee80211_hw_set(hw, SUPPORTS_CLONED_SKBS); ieee80211_hw_set(hw, SUPPORTS_AMSDU_IN_AMPDU); ieee80211_hw_set(hw, SUPPORTS_REORDERING_BUFFER); - ieee80211_hw_set(hw, TX_AMSDU); - ieee80211_hw_set(hw, TX_FRAG_LIST); + + if (!(dev->drv->drv_flags & MT_DRV_AMSDU_OFFLOAD)) { + ieee80211_hw_set(hw, TX_AMSDU); + ieee80211_hw_set(hw, TX_FRAG_LIST); + } + ieee80211_hw_set(hw, MFP_CAPABLE); ieee80211_hw_set(hw, AP_LINK_PS); ieee80211_hw_set(hw, REPORTS_TX_ACK_STATUS); diff --git a/drivers/net/wireless/mediatek/mt76/mt76.h b/drivers/net/wireless/mediatek/mt76/mt76.h index 26febc0c261a..f9bdf16fc6c4 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76.h +++ b/drivers/net/wireless/mediatek/mt76/mt76.h @@ -415,6 +415,7 @@ struct mt76_hw_cap { #define MT_DRV_SW_RX_AIRTIME BIT(2) #define MT_DRV_RX_DMA_HDR BIT(3) #define MT_DRV_HW_MGMT_TXQ BIT(4) +#define MT_DRV_AMSDU_OFFLOAD BIT(5) struct mt76_driver_ops { u32 drv_flags; diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mmio.c b/drivers/net/wireless/mediatek/mt76/mt7915/mmio.c index 2ffb509cad5c..be1b8ea711c7 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mmio.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mmio.c @@ -781,7 +781,8 @@ struct mt7915_dev *mt7915_mmio_probe(struct device *pdev, static const struct mt76_driver_ops drv_ops = { /* txwi_size = txd size + txp size */ .txwi_size = MT_TXD_SIZE + sizeof(struct mt76_connac_fw_txp), - .drv_flags = MT_DRV_TXWI_NO_FREE | MT_DRV_HW_MGMT_TXQ, + .drv_flags = MT_DRV_TXWI_NO_FREE | MT_DRV_HW_MGMT_TXQ | + MT_DRV_AMSDU_OFFLOAD, .survey_flags = SURVEY_INFO_TIME_TX | SURVEY_INFO_TIME_RX | SURVEY_INFO_TIME_BSS_RX, diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/pci.c b/drivers/net/wireless/mediatek/mt76/mt7921/pci.c index 8a53d8f286db..b38d119b2ea9 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/pci.c +++ b/drivers/net/wireless/mediatek/mt76/mt7921/pci.c @@ -228,7 +228,8 @@ static int mt7921_pci_probe(struct pci_dev *pdev, static const struct mt76_driver_ops drv_ops = { /* txwi_size = txd size + txp size */ .txwi_size = MT_TXD_SIZE + sizeof(struct mt76_connac_hw_txp), - .drv_flags = MT_DRV_TXWI_NO_FREE | MT_DRV_HW_MGMT_TXQ, + .drv_flags = MT_DRV_TXWI_NO_FREE | MT_DRV_HW_MGMT_TXQ | + MT_DRV_AMSDU_OFFLOAD, .survey_flags = SURVEY_INFO_TIME_TX | SURVEY_INFO_TIME_RX | SURVEY_INFO_TIME_BSS_RX, diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/sdio.c b/drivers/net/wireless/mediatek/mt76/mt7921/sdio.c index 3b25a06fd946..377ca5fa3f6e 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/sdio.c +++ b/drivers/net/wireless/mediatek/mt76/mt7921/sdio.c @@ -89,6 +89,7 @@ static int mt7921s_probe(struct sdio_func *func, { static const struct mt76_driver_ops drv_ops = { .txwi_size = MT_SDIO_TXD_SIZE, + .drv_flags = MT_DRV_AMSDU_OFFLOAD, .survey_flags = SURVEY_INFO_TIME_TX | SURVEY_INFO_TIME_RX | SURVEY_INFO_TIME_BSS_RX, diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/usb.c b/drivers/net/wireless/mediatek/mt76/mt7921/usb.c index 29c0ee330dbe..89249f0b6aba 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/usb.c +++ b/drivers/net/wireless/mediatek/mt76/mt7921/usb.c @@ -170,7 +170,8 @@ static int mt7921u_probe(struct usb_interface *usb_intf, { static const struct mt76_driver_ops drv_ops = { .txwi_size = MT_SDIO_TXD_SIZE, - .drv_flags = MT_DRV_RX_DMA_HDR | MT_DRV_HW_MGMT_TXQ, + .drv_flags = MT_DRV_RX_DMA_HDR | MT_DRV_HW_MGMT_TXQ | + MT_DRV_AMSDU_OFFLOAD, .survey_flags = SURVEY_INFO_TIME_TX | SURVEY_INFO_TIME_RX | SURVEY_INFO_TIME_BSS_RX, From b0bfa00595be6883a139a0b3a96c9c3d62874624 Mon Sep 17 00:00:00 2001 From: Ryder Lee Date: Fri, 14 Oct 2022 10:57:47 +0800 Subject: [PATCH 1463/2002] wifi: mt76: mt7915: improve accuracy of time_busy calculation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The MIB INFO command is fetching MIB_BUSY_TIME, MIB_TX_TIME, MIB_RX_TIME and MIB_OBSS_AIRTIME from the radio and filling out cc_busy, cc_tx, cc_bss_rx and cc_rx respectively. busy should be >= tx + rx >= tx + bss_rx but we don’t always quite see this. Sometimes tx + rx is a bit higher than busy due to inaccurate accounting, so this patch recalculates numbers to make them more reasonable. Reported-By: Kevin Schneider Tested-by: Kevin Schneider Tested-by: Chad Monroe Signed-off-by: Ryder Lee Signed-off-by: Felix Fietkau --- .../net/wireless/mediatek/mt76/mt7915/init.c | 17 +++++++++ .../net/wireless/mediatek/mt76/mt7915/mcu.c | 35 +++++++++++++------ .../net/wireless/mediatek/mt76/mt7915/mcu.h | 8 +++-- .../net/wireless/mediatek/mt76/mt7915/regs.h | 14 ++++++++ 4 files changed, 61 insertions(+), 13 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/init.c b/drivers/net/wireless/mediatek/mt76/mt7915/init.c index 324db5291c85..e864c0ab6c7f 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/init.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/init.c @@ -444,6 +444,23 @@ mt7915_mac_init_band(struct mt7915_dev *dev, u8 band) /* mt7915: disable rx rate report by default due to hw issues */ mt76_clear(dev, MT_DMA_DCR0(band), MT_DMA_DCR0_RXD_G5_EN); + + /* clear estimated value of EIFS for Rx duration & OBSS time */ + mt76_wr(dev, MT_WF_RMAC_RSVD0(band), MT_WF_RMAC_RSVD0_EIFS_CLR); + + /* clear backoff time for Rx duration */ + mt76_clear(dev, MT_WF_RMAC_MIB_AIRTIME1(band), + MT_WF_RMAC_MIB_NONQOSD_BACKOFF); + mt76_clear(dev, MT_WF_RMAC_MIB_AIRTIME3(band), + MT_WF_RMAC_MIB_QOS01_BACKOFF); + mt76_clear(dev, MT_WF_RMAC_MIB_AIRTIME4(band), + MT_WF_RMAC_MIB_QOS23_BACKOFF); + + /* clear backoff time and set software compensation for OBSS time */ + mask = MT_WF_RMAC_MIB_OBSS_BACKOFF | MT_WF_RMAC_MIB_ED_OFFSET; + set = FIELD_PREP(MT_WF_RMAC_MIB_OBSS_BACKOFF, 0) | + FIELD_PREP(MT_WF_RMAC_MIB_ED_OFFSET, 4); + mt76_rmw(dev, MT_WF_RMAC_MIB_AIRTIME0(band), mask, set); } static void mt7915_mac_init(struct mt7915_dev *dev) diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c index 790d690fabc3..4000fcd7132a 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c @@ -2940,25 +2940,36 @@ int mt7915_mcu_get_chan_mib_info(struct mt7915_phy *phy, bool chan_switch) { /* strict order */ static const u32 offs[] = { - MIB_BUSY_TIME, MIB_TX_TIME, MIB_RX_TIME, MIB_OBSS_AIRTIME, - MIB_BUSY_TIME_V2, MIB_TX_TIME_V2, MIB_RX_TIME_V2, + MIB_NON_WIFI_TIME, + MIB_TX_TIME, + MIB_RX_TIME, + MIB_OBSS_AIRTIME, + MIB_TXOP_INIT_COUNT, + /* v2 */ + MIB_NON_WIFI_TIME_V2, + MIB_TX_TIME_V2, + MIB_RX_TIME_V2, MIB_OBSS_AIRTIME_V2 }; struct mt76_channel_state *state = phy->mt76->chan_state; struct mt76_channel_state *state_ts = &phy->state_ts; struct mt7915_dev *dev = phy->dev; - struct mt7915_mcu_mib *res, req[4]; + struct mt7915_mcu_mib *res, req[5]; struct sk_buff *skb; int i, ret, start = 0, ofs = 20; + u64 cc_tx; if (!is_mt7915(&dev->mt76)) { - start = 4; + start = 5; ofs = 0; } - for (i = 0; i < 4; i++) { + for (i = 0; i < 5; i++) { req[i].band = cpu_to_le32(phy != &dev->phy); req[i].offs = cpu_to_le32(offs[i + start]); + + if (!is_mt7915(&dev->mt76) && i == 3) + break; } ret = mt76_mcu_send_and_get_msg(&dev->mt76, MCU_EXT_CMD(GET_MIB_INFO), @@ -2968,20 +2979,24 @@ int mt7915_mcu_get_chan_mib_info(struct mt7915_phy *phy, bool chan_switch) res = (struct mt7915_mcu_mib *)(skb->data + ofs); +#define __res_u64(s) le64_to_cpu(res[s].data) + /* subtract Tx backoff time from Tx duration */ + cc_tx = is_mt7915(&dev->mt76) ? __res_u64(1) - __res_u64(4) : __res_u64(1); + if (chan_switch) goto out; -#define __res_u64(s) le64_to_cpu(res[s].data) - state->cc_busy += __res_u64(0) - state_ts->cc_busy; - state->cc_tx += __res_u64(1) - state_ts->cc_tx; + state->cc_tx += cc_tx - state_ts->cc_tx; state->cc_bss_rx += __res_u64(2) - state_ts->cc_bss_rx; state->cc_rx += __res_u64(2) + __res_u64(3) - state_ts->cc_rx; + state->cc_busy += __res_u64(0) + cc_tx + __res_u64(2) + __res_u64(3) - + state_ts->cc_busy; out: - state_ts->cc_busy = __res_u64(0); - state_ts->cc_tx = __res_u64(1); + state_ts->cc_tx = cc_tx; state_ts->cc_bss_rx = __res_u64(2); state_ts->cc_rx = __res_u64(2) + __res_u64(3); + state_ts->cc_busy = __res_u64(0) + cc_tx + __res_u64(2) + __res_u64(3); #undef __res_u64 dev_kfree_skb(skb); diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.h b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.h index 87cd1bfff3e6..a4f903f88078 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.h +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.h @@ -160,15 +160,17 @@ struct mt7915_mcu_mib { enum mt7915_chan_mib_offs { /* mt7915 */ - MIB_BUSY_TIME = 14, MIB_TX_TIME = 81, MIB_RX_TIME, MIB_OBSS_AIRTIME = 86, + MIB_NON_WIFI_TIME, + MIB_TXOP_INIT_COUNT, + /* mt7916 */ - MIB_BUSY_TIME_V2 = 0, MIB_TX_TIME_V2 = 6, MIB_RX_TIME_V2 = 8, - MIB_OBSS_AIRTIME_V2 = 490 + MIB_OBSS_AIRTIME_V2 = 490, + MIB_NON_WIFI_TIME_V2 }; struct edca { diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/regs.h b/drivers/net/wireless/mediatek/mt76/mt7915/regs.h index 5180dd931835..7db809671230 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/regs.h +++ b/drivers/net/wireless/mediatek/mt76/mt7915/regs.h @@ -525,8 +525,22 @@ enum offs_rev { #define MT_WF_RFCR1_DROP_CFEND BIT(7) #define MT_WF_RFCR1_DROP_CFACK BIT(8) +#define MT_WF_RMAC_RSVD0(_band) MT_WF_RMAC(_band, 0x02e0) +#define MT_WF_RMAC_RSVD0_EIFS_CLR BIT(21) + #define MT_WF_RMAC_MIB_AIRTIME0(_band) MT_WF_RMAC(_band, 0x0380) #define MT_WF_RMAC_MIB_RXTIME_CLR BIT(31) +#define MT_WF_RMAC_MIB_OBSS_BACKOFF GENMASK(15, 0) +#define MT_WF_RMAC_MIB_ED_OFFSET GENMASK(20, 16) + +#define MT_WF_RMAC_MIB_AIRTIME1(_band) MT_WF_RMAC(_band, 0x0384) +#define MT_WF_RMAC_MIB_NONQOSD_BACKOFF GENMASK(31, 16) + +#define MT_WF_RMAC_MIB_AIRTIME3(_band) MT_WF_RMAC(_band, 0x038c) +#define MT_WF_RMAC_MIB_QOS01_BACKOFF GENMASK(31, 0) + +#define MT_WF_RMAC_MIB_AIRTIME4(_band) MT_WF_RMAC(_band, 0x0390) +#define MT_WF_RMAC_MIB_QOS23_BACKOFF GENMASK(31, 0) /* WFDMA0 */ #define MT_WFDMA0_BASE __REG(WFDMA0_ADDR) From a71b648e352787db13184b9355c0b3ffc8a0824a Mon Sep 17 00:00:00 2001 From: Ryder Lee Date: Fri, 14 Oct 2022 10:57:48 +0800 Subject: [PATCH 1464/2002] wifi: mt76: mt7915: add ack signal support This reports signal strength of ACK packets from the peer as measured at each interface. Tested-by: Shurong Wen Signed-off-by: Ryder Lee Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mac80211.c | 9 ++++----- drivers/net/wireless/mediatek/mt76/mt76.h | 1 + .../net/wireless/mediatek/mt76/mt7915/init.c | 7 +++++++ .../net/wireless/mediatek/mt76/mt7915/mac.c | 19 ++++++++++++++++++- .../net/wireless/mediatek/mt76/mt7915/main.c | 8 ++++++++ .../wireless/mediatek/mt76/mt7915/mt7915.h | 6 +++++- .../net/wireless/mediatek/mt76/mt7915/regs.h | 8 ++++++++ 7 files changed, 51 insertions(+), 7 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mac80211.c b/drivers/net/wireless/mediatek/mt76/mac80211.c index 4bdbfd0f6233..c59d12004459 100644 --- a/drivers/net/wireless/mediatek/mt76/mac80211.c +++ b/drivers/net/wireless/mediatek/mt76/mac80211.c @@ -951,14 +951,12 @@ void mt76_wcid_key_setup(struct mt76_dev *dev, struct mt76_wcid *wcid, } EXPORT_SYMBOL(mt76_wcid_key_setup); -static int -mt76_rx_signal(struct mt76_rx_status *status) +int mt76_rx_signal(u8 chain_mask, s8 *chain_signal) { - s8 *chain_signal = status->chain_signal; int signal = -128; u8 chains; - for (chains = status->chains; chains; chains >>= 1, chain_signal++) { + for (chains = chain_mask; chains; chains >>= 1, chain_signal++) { int cur, diff; cur = *chain_signal; @@ -980,6 +978,7 @@ mt76_rx_signal(struct mt76_rx_status *status) return signal; } +EXPORT_SYMBOL(mt76_rx_signal); static void mt76_rx_convert(struct mt76_dev *dev, struct sk_buff *skb, @@ -1009,7 +1008,7 @@ mt76_rx_convert(struct mt76_dev *dev, struct sk_buff *skb, status->ampdu_reference = mstat.ampdu_ref; status->device_timestamp = mstat.timestamp; status->mactime = mstat.timestamp; - status->signal = mt76_rx_signal(&mstat); + status->signal = mt76_rx_signal(mstat.chains, mstat.chain_signal); if (status->signal <= -128) status->flag |= RX_FLAG_NO_SIGNAL_VAL; diff --git a/drivers/net/wireless/mediatek/mt76/mt76.h b/drivers/net/wireless/mediatek/mt76/mt76.h index f9bdf16fc6c4..c91fb9bde632 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76.h +++ b/drivers/net/wireless/mediatek/mt76/mt76.h @@ -1149,6 +1149,7 @@ void mt76_update_survey(struct mt76_phy *phy); void mt76_update_survey_active_time(struct mt76_phy *phy, ktime_t time); int mt76_get_survey(struct ieee80211_hw *hw, int idx, struct survey_info *survey); +int mt76_rx_signal(u8 chain_mask, s8 *chain_signal); void mt76_set_stream_caps(struct mt76_phy *phy, bool vht); int mt76_rx_aggr_start(struct mt76_dev *dev, struct mt76_wcid *wcid, u8 tid, diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/init.c b/drivers/net/wireless/mediatek/mt76/mt7915/init.c index e864c0ab6c7f..a042e0154211 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/init.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/init.c @@ -353,6 +353,7 @@ mt7915_init_wiphy(struct ieee80211_hw *hw) wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_BEACON_RATE_HE); wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_UNSOL_BCAST_PROBE_RESP); wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_FILS_DISCOVERY); + wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_ACK_SIGNAL_SUPPORT); if (!mdev->dev->of_node || !of_property_read_bool(mdev->dev->of_node, @@ -461,6 +462,12 @@ mt7915_mac_init_band(struct mt7915_dev *dev, u8 band) set = FIELD_PREP(MT_WF_RMAC_MIB_OBSS_BACKOFF, 0) | FIELD_PREP(MT_WF_RMAC_MIB_ED_OFFSET, 4); mt76_rmw(dev, MT_WF_RMAC_MIB_AIRTIME0(band), mask, set); + + /* filter out non-resp frames and get instanstaeous signal reporting */ + mask = MT_WTBLOFF_TOP_RSCR_RCPI_MODE | MT_WTBLOFF_TOP_RSCR_RCPI_PARAM; + set = FIELD_PREP(MT_WTBLOFF_TOP_RSCR_RCPI_MODE, 0) | + FIELD_PREP(MT_WTBLOFF_TOP_RSCR_RCPI_PARAM, 0x3); + mt76_rmw(dev, MT_WTBLOFF_TOP_RSCR(band), mask, set); } static void mt7915_mac_init(struct mt7915_dev *dev) diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mac.c b/drivers/net/wireless/mediatek/mt76/mt7915/mac.c index 324f0f58572b..760ad786e566 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mac.c @@ -8,7 +8,7 @@ #include "mac.h" #include "mcu.h" -#define to_rssi(field, rxv) ((FIELD_GET(field, rxv) - 220) / 2) +#define to_rssi(field, rcpi) ((FIELD_GET(field, rcpi) - 220) / 2) static const struct mt7915_dfs_radar_spec etsi_radar_specs = { .pulse_th = { 110, -10, -80, 40, 5200, 128, 5200 }, @@ -118,6 +118,7 @@ static void mt7915_mac_sta_poll(struct mt7915_dev *dev) bool clear = false; u32 addr, val; u16 idx; + s8 rssi[4]; u8 bw; spin_lock_bh(&dev->sta_poll_lock); @@ -131,6 +132,8 @@ static void mt7915_mac_sta_poll(struct mt7915_dev *dev) spin_unlock_bh(&dev->sta_poll_lock); idx = msta->wcid.idx; + + /* refresh peer's airtime reporting */ addr = mt7915_mac_wtbl_lmac_addr(dev, idx, 20); for (i = 0; i < IEEE80211_NUM_ACS; i++) { @@ -209,6 +212,20 @@ static void mt7915_mac_sta_poll(struct mt7915_dev *dev) else rate->flags &= ~RATE_INFO_FLAGS_SHORT_GI; } + + /* get signal strength of resp frames (CTS/BA/ACK) */ + addr = mt7915_mac_wtbl_lmac_addr(dev, idx, 30); + val = mt76_rr(dev, addr); + + rssi[0] = to_rssi(GENMASK(7, 0), val); + rssi[1] = to_rssi(GENMASK(15, 8), val); + rssi[2] = to_rssi(GENMASK(23, 16), val); + rssi[3] = to_rssi(GENMASK(31, 14), val); + + msta->ack_signal = + mt76_rx_signal(msta->vif->phy->mt76->antenna_mask, rssi); + + ewma_avg_signal_add(&msta->avg_ack_signal, -msta->ack_signal); } rcu_read_unlock(); diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/main.c b/drivers/net/wireless/mediatek/mt76/mt7915/main.c index 0ebaed174d5d..7f1e76d59118 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/main.c @@ -665,6 +665,8 @@ int mt7915_mac_sta_add(struct mt76_dev *mdev, struct ieee80211_vif *vif, msta->wcid.tx_info |= MT_WCID_TX_INFO_SET; msta->jiffies = jiffies; + ewma_avg_signal_init(&msta->avg_ack_signal); + mt7915_mac_wtbl_update(dev, idx, MT_WTBL_UPDATE_ADM_COUNT_CLEAR); @@ -1025,6 +1027,12 @@ static void mt7915_sta_statistics(struct ieee80211_hw *hw, sinfo->tx_retries = msta->wcid.stats.tx_retries; sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_RETRIES); } + + sinfo->ack_signal = (s8)msta->ack_signal; + sinfo->filled |= BIT_ULL(NL80211_STA_INFO_ACK_SIGNAL); + + sinfo->avg_ack_signal = -(s8)ewma_avg_signal_read(&msta->avg_ack_signal); + sinfo->filled |= BIT_ULL(NL80211_STA_INFO_ACK_SIGNAL_AVG); } static void mt7915_sta_rc_work(void *data, struct ieee80211_sta *sta) diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h b/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h index fe6a6d3b0a32..e6ae02eb2731 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h @@ -114,6 +114,8 @@ struct mt7915_twt_flow { u8 sched:1; }; +DECLARE_EWMA(avg_signal, 10, 8) + struct mt7915_sta { struct mt76_wcid wcid; /* must be first */ @@ -123,10 +125,12 @@ struct mt7915_sta { struct list_head rc_list; u32 airtime_ac[8]; + int ack_signal; + struct ewma_avg_signal avg_ack_signal; + unsigned long changed; unsigned long jiffies; unsigned long ampdu_state; - struct mt76_connac_sta_key_conf bip; struct { diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/regs.h b/drivers/net/wireless/mediatek/mt76/mt7915/regs.h index 7db809671230..9924271d8e36 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/regs.h +++ b/drivers/net/wireless/mediatek/mt76/mt7915/regs.h @@ -226,6 +226,14 @@ enum offs_rev { #define MT_DMA_DCR0_MAX_RX_LEN GENMASK(15, 3) #define MT_DMA_DCR0_RXD_G5_EN BIT(23) +/* WTBLOFF TOP: band 0(0x820e9000),band 1(0x820f9000) */ +#define MT_WTBLOFF_TOP_BASE(_band) ((_band) ? 0x820f9000 : 0x820e9000) +#define MT_WTBLOFF_TOP(_band, ofs) (MT_WTBLOFF_TOP_BASE(_band) + (ofs)) + +#define MT_WTBLOFF_TOP_RSCR(_band) MT_WTBLOFF_TOP(_band, 0x008) +#define MT_WTBLOFF_TOP_RSCR_RCPI_MODE GENMASK(31, 30) +#define MT_WTBLOFF_TOP_RSCR_RCPI_PARAM GENMASK(25, 24) + /* ETBF: band 0(0x820ea000), band 1(0x820fa000) */ #define MT_WF_ETBF_BASE(_band) ((_band) ? 0x820fa000 : 0x820ea000) #define MT_WF_ETBF(_band, ofs) (MT_WF_ETBF_BASE(_band) + (ofs)) From df2632b3cea3524f4bb20246346f9e729873dc0f Mon Sep 17 00:00:00 2001 From: Ming Yen Hsieh Date: Mon, 24 Oct 2022 20:45:08 +0800 Subject: [PATCH 1465/2002] wifi: mt76: fix bandwidth 80MHz link fail in 6GHz band Due to information missing, the firmware may be fail on bandwidth related settings in mt7921/mt7922. Add new cmd STA_REC_HE_V2 to apply additional capabilities in 6GHz band. Tested-by: Ben Greear Co-developed-by: Deren Wu Signed-off-by: Deren Wu Signed-off-by: Ming Yen Hsieh Signed-off-by: Felix Fietkau --- .../wireless/mediatek/mt76/mt76_connac_mcu.c | 34 +++++++++++++++++++ .../wireless/mediatek/mt76/mt76_connac_mcu.h | 11 ++++++ 2 files changed, 45 insertions(+) diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c index 011fc9729b38..9bba18d24c71 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c @@ -744,6 +744,39 @@ mt76_connac_mcu_sta_he_tlv(struct sk_buff *skb, struct ieee80211_sta *sta) he->pkt_ext = 2; } +static void +mt76_connac_mcu_sta_he_tlv_v2(struct sk_buff *skb, struct ieee80211_sta *sta) +{ + struct ieee80211_sta_he_cap *he_cap = &sta->deflink.he_cap; + struct ieee80211_he_cap_elem *elem = &he_cap->he_cap_elem; + struct sta_rec_he_v2 *he; + struct tlv *tlv; + + tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_HE_V2, sizeof(*he)); + + he = (struct sta_rec_he_v2 *)tlv; + memcpy(he->he_phy_cap, elem->phy_cap_info, sizeof(he->he_phy_cap)); + memcpy(he->he_mac_cap, elem->mac_cap_info, sizeof(he->he_mac_cap)); + + switch (sta->deflink.bandwidth) { + case IEEE80211_STA_RX_BW_160: + if (elem->phy_cap_info[0] & + IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_80PLUS80_MHZ_IN_5G) + he->max_nss_mcs[CMD_HE_MCS_BW8080] = + he_cap->he_mcs_nss_supp.rx_mcs_80p80; + + he->max_nss_mcs[CMD_HE_MCS_BW160] = + he_cap->he_mcs_nss_supp.rx_mcs_160; + fallthrough; + default: + he->max_nss_mcs[CMD_HE_MCS_BW80] = + he_cap->he_mcs_nss_supp.rx_mcs_80; + break; + } + + he->pkt_ext = IEEE80211_HE_PHY_CAP9_NOMINAL_PKT_PADDING_16US; +} + static u8 mt76_connac_get_phy_mode_v2(struct mt76_phy *mphy, struct ieee80211_vif *vif, enum nl80211_band band, struct ieee80211_sta *sta) @@ -838,6 +871,7 @@ void mt76_connac_mcu_sta_tlv(struct mt76_phy *mphy, struct sk_buff *skb, /* starec he */ if (sta->deflink.he_cap.has_he) { mt76_connac_mcu_sta_he_tlv(skb, sta); + mt76_connac_mcu_sta_he_tlv_v2(skb, sta); if (band == NL80211_BAND_6GHZ && sta_state == MT76_STA_INFO_STATE_ASSOC) { struct sta_rec_he_6g_capa *he_6g_capa; diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h index cf4ce3b1fc21..8166722d4717 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h +++ b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h @@ -354,6 +354,16 @@ struct sta_rec_he { u8 rsv2[2]; } __packed; +struct sta_rec_he_v2 { + __le16 tag; + __le16 len; + u8 he_mac_cap[6]; + u8 he_phy_cap[11]; + u8 pkt_ext; + /* 0: BW80, 1: BW160, 2: BW8080 */ + __le16 max_nss_mcs[CMD_HE_MCS_BW_NUM]; +} __packed; + struct sta_rec_amsdu { __le16 tag; __le16 len; @@ -779,6 +789,7 @@ enum { STA_REC_BFEE, STA_REC_PHY = 0x15, STA_REC_HE_6G = 0x17, + STA_REC_HE_V2 = 0x19, STA_REC_MAX_NUM }; From 150b91419d3dee828cf23a6aa7b24925b2783cea Mon Sep 17 00:00:00 2001 From: Ryder Lee Date: Tue, 25 Oct 2022 08:31:00 +0800 Subject: [PATCH 1466/2002] wifi: mt76: mt7915: enable use_cts_prot support This adds selectable RTC/CTS enablement for each interface. Signed-off-by: Ryder Lee Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt7915/mac.c | 13 +++++++++++++ drivers/net/wireless/mediatek/mt76/mt7915/main.c | 3 +++ drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h | 2 ++ 3 files changed, 18 insertions(+) diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mac.c b/drivers/net/wireless/mediatek/mt76/mt7915/mac.c index 760ad786e566..741ccffda5ee 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mac.c @@ -231,6 +231,19 @@ static void mt7915_mac_sta_poll(struct mt7915_dev *dev) rcu_read_unlock(); } +void mt7915_mac_enable_rtscts(struct mt7915_dev *dev, + struct ieee80211_vif *vif, bool enable) +{ + struct mt7915_vif *mvif = (struct mt7915_vif *)vif->drv_priv; + u32 addr; + + addr = mt7915_mac_wtbl_lmac_addr(dev, mvif->sta.wcid.idx, 5); + if (enable) + mt76_set(dev, addr, BIT(5)); + else + mt76_clear(dev, addr, BIT(5)); +} + static int mt7915_mac_fill_rx(struct mt7915_dev *dev, struct sk_buff *skb) { diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/main.c b/drivers/net/wireless/mediatek/mt76/mt7915/main.c index 7f1e76d59118..56f43ba5ea05 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/main.c @@ -598,6 +598,9 @@ static void mt7915_bss_info_changed(struct ieee80211_hw *hw, mt7915_mcu_add_obss_spr(dev, vif, info->he_obss_pd.enable); } + if (changed & BSS_CHANGED_ERP_CTS_PROT) + mt7915_mac_enable_rtscts(dev, vif, info->use_cts_prot); + if (changed & BSS_CHANGED_ERP_SLOT) { int slottime = info->use_short_slot ? 9 : 20; diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h b/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h index e6ae02eb2731..98c13b6c47d4 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h @@ -552,6 +552,8 @@ bool mt7915_mac_wtbl_update(struct mt7915_dev *dev, int idx, u32 mask); void mt7915_mac_reset_counters(struct mt7915_phy *phy); void mt7915_mac_cca_stats_reset(struct mt7915_phy *phy); void mt7915_mac_enable_nf(struct mt7915_dev *dev, bool ext_phy); +void mt7915_mac_enable_rtscts(struct mt7915_dev *dev, + struct ieee80211_vif *vif, bool enable); void mt7915_mac_write_txwi(struct mt76_dev *dev, __le32 *txwi, struct sk_buff *skb, struct mt76_wcid *wcid, int pid, struct ieee80211_key_conf *key, From e34235ccc5e378a234157ba4142f396c9662b985 Mon Sep 17 00:00:00 2001 From: Ryder Lee Date: Tue, 25 Oct 2022 08:31:01 +0800 Subject: [PATCH 1467/2002] wifi: mt76: mt7615: enable use_cts_prot support This adds selectable RTC/CTS enablement for each interface. Signed-off-by: Ryder Lee Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt7615/mac.c | 15 +++++++++++++++ drivers/net/wireless/mediatek/mt76/mt7615/main.c | 3 +++ .../net/wireless/mediatek/mt76/mt7615/mt7615.h | 2 ++ drivers/net/wireless/mediatek/mt76/mt7615/regs.h | 2 ++ 4 files changed, 22 insertions(+) diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/mac.c b/drivers/net/wireless/mediatek/mt76/mt7615/mac.c index 2ce1705c0f43..45cad911687d 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7615/mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7615/mac.c @@ -1177,6 +1177,21 @@ void mt7615_mac_set_rates(struct mt7615_phy *phy, struct mt7615_sta *sta, } EXPORT_SYMBOL_GPL(mt7615_mac_set_rates); +void mt7615_mac_enable_rtscts(struct mt7615_dev *dev, + struct ieee80211_vif *vif, bool enable) +{ + struct mt7615_vif *mvif = (struct mt7615_vif *)vif->drv_priv; + u32 addr; + + addr = mt7615_mac_wtbl_addr(dev, mvif->sta.wcid.idx) + 3 * 4; + + if (enable) + mt76_set(dev, addr, MT_WTBL_W3_RTS); + else + mt76_clear(dev, addr, MT_WTBL_W3_RTS); +} +EXPORT_SYMBOL_GPL(mt7615_mac_enable_rtscts); + static int mt7615_mac_wtbl_update_key(struct mt7615_dev *dev, struct mt76_wcid *wcid, struct ieee80211_key_conf *key, diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/main.c b/drivers/net/wireless/mediatek/mt76/mt7615/main.c index 8d4733f87cda..98f9e2155009 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7615/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7615/main.c @@ -572,6 +572,9 @@ static void mt7615_bss_info_changed(struct ieee80211_hw *hw, } } + if (changed & BSS_CHANGED_ERP_CTS_PROT) + mt7615_mac_enable_rtscts(dev, vif, info->use_cts_prot); + if (changed & BSS_CHANGED_BEACON_ENABLED && info->enable_beacon) { mt7615_mcu_add_bss_info(phy, vif, NULL, true); mt7615_mcu_sta_add(phy, vif, NULL, true); diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/mt7615.h b/drivers/net/wireless/mediatek/mt76/mt7615/mt7615.h index 060d52c81d9e..f632e6a99038 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7615/mt7615.h +++ b/drivers/net/wireless/mediatek/mt76/mt7615/mt7615.h @@ -473,6 +473,8 @@ void mt7615_mac_reset_counters(struct mt7615_dev *dev); void mt7615_mac_cca_stats_reset(struct mt7615_phy *phy); void mt7615_mac_set_scs(struct mt7615_phy *phy, bool enable); void mt7615_mac_enable_nf(struct mt7615_dev *dev, bool ext_phy); +void mt7615_mac_enable_rtscts(struct mt7615_dev *dev, + struct ieee80211_vif *vif, bool enable); void mt7615_mac_sta_poll(struct mt7615_dev *dev); int mt7615_mac_write_txwi(struct mt7615_dev *dev, __le32 *txwi, struct sk_buff *skb, struct mt76_wcid *wcid, diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/regs.h b/drivers/net/wireless/mediatek/mt76/mt7615/regs.h index 6712ad9faeaa..fa1b9b26b399 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7615/regs.h +++ b/drivers/net/wireless/mediatek/mt76/mt7615/regs.h @@ -446,6 +446,8 @@ enum mt7615_reg_base { #define MT_WTBL_RIUCR3_RATE6 GENMASK(19, 8) #define MT_WTBL_RIUCR3_RATE7 GENMASK(31, 20) +#define MT_WTBL_W3_RTS BIT(22) + #define MT_WTBL_W5_CHANGE_BW_RATE GENMASK(7, 5) #define MT_WTBL_W5_SHORT_GI_20 BIT(8) #define MT_WTBL_W5_SHORT_GI_40 BIT(9) From 528d13e7f033b54d50e0077922dd52f005d648cf Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Wed, 2 Nov 2022 13:35:01 +0100 Subject: [PATCH 1468/2002] wifi: mt76: mt7915: fix reporting of TX AGGR histogram Fix stats clash between bins [4-7] in 802.11 tx aggregation histogram. Fixes: e57b7901469fc ("mt76: add mac80211 driver for MT7915 PCIe-based chipsets") Signed-off-by: Lorenzo Bianconi Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt7915/mac.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mac.c b/drivers/net/wireless/mediatek/mt76/mt7915/mac.c index 741ccffda5ee..0147936cc7b8 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mac.c @@ -1641,7 +1641,7 @@ void mt7915_mac_update_stats(struct mt7915_phy *phy) aggr0 = phy->band_idx ? ARRAY_SIZE(dev->mt76.aggr_stats) / 2 : 0; if (is_mt7915(&dev->mt76)) { - for (i = 0, aggr1 = aggr0 + 4; i < 4; i++) { + for (i = 0, aggr1 = aggr0 + 8; i < 4; i++) { val = mt76_rr(dev, MT_MIB_MB_SDR1(phy->band_idx, (i << 4))); mib->ba_miss_cnt += FIELD_GET(MT_MIB_BA_MISS_COUNT_MASK, val); From 028b4f22b37b88821fd87b56ce47b180583c774e Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Wed, 2 Nov 2022 13:46:50 +0100 Subject: [PATCH 1469/2002] wifi: mt76: mt7921: fix reporting of TX AGGR histogram Similar to mt7915, fix stats clash between bins [4-7] in 802.11 tx aggregation histogram. Fixes: 163f4d22c118d ("mt76: mt7921: add MAC support") Signed-off-by: Lorenzo Bianconi Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt7921/mac.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/mac.c b/drivers/net/wireless/mediatek/mt76/mt7921/mac.c index 6860468ed191..1c0d8cf19b8e 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7921/mac.c @@ -984,7 +984,7 @@ void mt7921_mac_update_mib_stats(struct mt7921_phy *phy) mib->tx_amsdu_cnt += val; } - for (i = 0, aggr1 = aggr0 + 4; i < 4; i++) { + for (i = 0, aggr1 = aggr0 + 8; i < 4; i++) { u32 val2; val = mt76_rr(dev, MT_TX_AGG_CNT(0, i)); From 1b9ba30ec265c38aaef94a7c93f3f0c8f3aff3e4 Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Wed, 2 Nov 2022 23:29:14 +0100 Subject: [PATCH 1470/2002] wifi: mt76: mt7615: rely on mt7615_phy in mt7615_mac_reset_counters This is a preliminary patch to move aggr_stats array in mt76_phy structure. Signed-off-by: Lorenzo Bianconi Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt7615/mac.c | 8 +++----- drivers/net/wireless/mediatek/mt76/mt7615/main.c | 4 ++-- drivers/net/wireless/mediatek/mt76/mt7615/mt7615.h | 2 +- 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/mac.c b/drivers/net/wireless/mediatek/mt76/mt7615/mac.c index 45cad911687d..c55d753e467a 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7615/mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7615/mac.c @@ -107,9 +107,9 @@ static struct mt76_wcid *mt7615_rx_get_wcid(struct mt7615_dev *dev, return &sta->vif->sta.wcid; } -void mt7615_mac_reset_counters(struct mt7615_dev *dev) +void mt7615_mac_reset_counters(struct mt7615_phy *phy) { - struct mt76_phy *mphy_ext = dev->mt76.phys[MT_BAND1]; + struct mt7615_dev *dev = phy->dev; int i; for (i = 0; i < 4; i++) { @@ -118,9 +118,7 @@ void mt7615_mac_reset_counters(struct mt7615_dev *dev) } memset(dev->mt76.aggr_stats, 0, sizeof(dev->mt76.aggr_stats)); - dev->mt76.phy.survey_time = ktime_get_boottime(); - if (mphy_ext) - mphy_ext->survey_time = ktime_get_boottime(); + phy->mt76->survey_time = ktime_get_boottime(); /* reset airtime counters */ mt76_rr(dev, MT_MIB_SDR9(0)); diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/main.c b/drivers/net/wireless/mediatek/mt76/mt7615/main.c index 98f9e2155009..ab4c1b4478aa 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7615/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7615/main.c @@ -83,7 +83,7 @@ static int mt7615_start(struct ieee80211_hw *hw) ieee80211_queue_delayed_work(hw, &phy->mt76->mac_work, timeout); if (!running) - mt7615_mac_reset_counters(dev); + mt7615_mac_reset_counters(phy); out: mt7615_mutex_release(dev); @@ -320,7 +320,7 @@ int mt7615_set_channel(struct mt7615_phy *phy) if (ret) goto out; - mt7615_mac_reset_counters(dev); + mt7615_mac_reset_counters(phy); phy->noise = 0; phy->chfreq = mt76_rr(dev, MT_CHFREQ(ext_phy)); diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/mt7615.h b/drivers/net/wireless/mediatek/mt76/mt7615/mt7615.h index f632e6a99038..8b37f8259f52 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7615/mt7615.h +++ b/drivers/net/wireless/mediatek/mt76/mt7615/mt7615.h @@ -469,7 +469,7 @@ void mt7615_init_work(struct mt7615_dev *dev); int mt7615_mcu_restart(struct mt76_dev *dev); void mt7615_update_channel(struct mt76_phy *mphy); bool mt7615_mac_wtbl_update(struct mt7615_dev *dev, int idx, u32 mask); -void mt7615_mac_reset_counters(struct mt7615_dev *dev); +void mt7615_mac_reset_counters(struct mt7615_phy *phy); void mt7615_mac_cca_stats_reset(struct mt7615_phy *phy); void mt7615_mac_set_scs(struct mt7615_phy *phy, bool enable); void mt7615_mac_enable_nf(struct mt7615_dev *dev, bool ext_phy); From d107501ae881c20c1c822c8080a645f73a042c68 Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Wed, 2 Nov 2022 23:29:15 +0100 Subject: [PATCH 1471/2002] wifi: mt76: move aggr_stats array in mt76_phy Move aggregation stats array per-phy instead of share it between multiple interfaces. This is a preliminary patch to add mt7996 driver support. Signed-off-by: Lorenzo Bianconi Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt76.h | 4 ++-- .../wireless/mediatek/mt76/mt7603/debugfs.c | 2 +- .../net/wireless/mediatek/mt76/mt7603/mac.c | 6 +++--- .../wireless/mediatek/mt76/mt7615/debugfs.c | 6 ++---- .../net/wireless/mediatek/mt76/mt7615/mac.c | 9 ++++---- .../wireless/mediatek/mt76/mt76x02_debugfs.c | 2 +- .../net/wireless/mediatek/mt76/mt76x02_mac.c | 6 +++--- .../wireless/mediatek/mt76/mt7915/debugfs.c | 5 ++--- .../net/wireless/mediatek/mt76/mt7915/mac.c | 21 +++++++------------ .../net/wireless/mediatek/mt76/mt7915/main.c | 5 ++--- .../wireless/mediatek/mt76/mt7921/debugfs.c | 2 +- .../net/wireless/mediatek/mt76/mt7921/mac.c | 10 ++++----- .../net/wireless/mediatek/mt76/mt7921/main.c | 2 +- 13 files changed, 35 insertions(+), 45 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt76.h b/drivers/net/wireless/mediatek/mt76/mt76.h index c91fb9bde632..a2bccf6b6c54 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76.h +++ b/drivers/net/wireless/mediatek/mt76/mt76.h @@ -693,6 +693,8 @@ struct mt76_phy { enum mt76_dfs_state dfs_state; ktime_t survey_time; + u32 aggr_stats[32]; + struct mt76_hw_cap cap; struct mt76_sband sband_2g; struct mt76_sband sband_5g; @@ -781,8 +783,6 @@ struct mt76_dev { u32 rev; - u32 aggr_stats[32]; - struct tasklet_struct pre_tbtt_tasklet; int beacon_int; u8 beacon_mask; diff --git a/drivers/net/wireless/mediatek/mt76/mt7603/debugfs.c b/drivers/net/wireless/mediatek/mt76/mt7603/debugfs.c index f52165dff422..3967f2f05774 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7603/debugfs.c +++ b/drivers/net/wireless/mediatek/mt76/mt7603/debugfs.c @@ -85,7 +85,7 @@ mt7603_ampdu_stat_show(struct seq_file *file, void *data) bound[i], bound[i + 1]); seq_puts(file, "\nCount: "); for (i = 0; i < ARRAY_SIZE(bound); i++) - seq_printf(file, "%8d | ", dev->mt76.aggr_stats[i]); + seq_printf(file, "%8d | ", dev->mphy.aggr_stats[i]); seq_puts(file, "\n"); return 0; diff --git a/drivers/net/wireless/mediatek/mt76/mt7603/mac.c b/drivers/net/wireless/mediatek/mt76/mt7603/mac.c index 49a511ae8161..70a7f84af028 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7603/mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7603/mac.c @@ -39,7 +39,7 @@ void mt7603_mac_reset_counters(struct mt7603_dev *dev) for (i = 0; i < 2; i++) mt76_rr(dev, MT_TX_AGG_CNT(i)); - memset(dev->mt76.aggr_stats, 0, sizeof(dev->mt76.aggr_stats)); + memset(dev->mphy.aggr_stats, 0, sizeof(dev->mphy.aggr_stats)); } void mt7603_mac_set_timing(struct mt7603_dev *dev) @@ -1827,8 +1827,8 @@ void mt7603_mac_work(struct work_struct *work) for (i = 0, idx = 0; i < 2; i++) { u32 val = mt76_rr(dev, MT_TX_AGG_CNT(i)); - dev->mt76.aggr_stats[idx++] += val & 0xffff; - dev->mt76.aggr_stats[idx++] += val >> 16; + dev->mphy.aggr_stats[idx++] += val & 0xffff; + dev->mphy.aggr_stats[idx++] += val >> 16; } if (dev->mphy.mac_work_count == 10) diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/debugfs.c b/drivers/net/wireless/mediatek/mt76/mt7615/debugfs.c index c26b45a09923..2a6d317db5e0 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7615/debugfs.c +++ b/drivers/net/wireless/mediatek/mt76/mt7615/debugfs.c @@ -278,7 +278,6 @@ mt7615_ampdu_stat_read_phy(struct mt7615_phy *phy, { struct mt7615_dev *dev = file->private; u32 reg = is_mt7663(&dev->mt76) ? MT_MIB_ARNG(0) : MT_AGG_ASRCR0; - bool ext_phy = phy != &dev->phy; int bound[7], i, range; if (!phy) @@ -292,7 +291,7 @@ mt7615_ampdu_stat_read_phy(struct mt7615_phy *phy, for (i = 0; i < 3; i++) bound[i + 4] = MT_AGG_ASRCR_RANGE(range, i) + 1; - seq_printf(file, "\nPhy %d\n", ext_phy); + seq_printf(file, "\nPhy %d\n", phy != &dev->phy); seq_printf(file, "Length: %8d | ", bound[0]); for (i = 0; i < ARRAY_SIZE(bound) - 1; i++) @@ -300,9 +299,8 @@ mt7615_ampdu_stat_read_phy(struct mt7615_phy *phy, bound[i], bound[i + 1]); seq_puts(file, "\nCount: "); - range = ext_phy ? ARRAY_SIZE(dev->mt76.aggr_stats) / 2 : 0; for (i = 0; i < ARRAY_SIZE(bound); i++) - seq_printf(file, "%8d | ", dev->mt76.aggr_stats[i + range]); + seq_printf(file, "%8d | ", phy->mt76->aggr_stats[i]); seq_puts(file, "\n"); seq_printf(file, "BA miss count: %d\n", phy->mib.ba_miss_cnt); diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/mac.c b/drivers/net/wireless/mediatek/mt76/mt7615/mac.c index c55d753e467a..305bf1826a02 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7615/mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7615/mac.c @@ -117,7 +117,7 @@ void mt7615_mac_reset_counters(struct mt7615_phy *phy) mt76_rr(dev, MT_TX_AGG_CNT(1, i)); } - memset(dev->mt76.aggr_stats, 0, sizeof(dev->mt76.aggr_stats)); + memset(phy->mt76->aggr_stats, 0, sizeof(phy->mt76->aggr_stats)); phy->mt76->survey_time = ktime_get_boottime(); /* reset airtime counters */ @@ -2012,7 +2012,7 @@ mt7615_mac_update_mib_stats(struct mt7615_phy *phy) struct mt7615_dev *dev = phy->dev; struct mib_stats *mib = &phy->mib; bool ext_phy = phy != &dev->phy; - int i, aggr; + int i, aggr = 0; u32 val, val2; mib->fcs_err_cnt += mt76_get_field(dev, MT_MIB_SDR3(ext_phy), @@ -2026,7 +2026,6 @@ mt7615_mac_update_mib_stats(struct mt7615_phy *phy) mib->aggr_per = 1000 * (val - val2) / val; } - aggr = ext_phy ? ARRAY_SIZE(dev->mt76.aggr_stats) / 2 : 0; for (i = 0; i < 4; i++) { val = mt76_rr(dev, MT_MIB_MB_SDR1(ext_phy, i)); mib->ba_miss_cnt += FIELD_GET(MT_MIB_BA_MISS_COUNT_MASK, val); @@ -2039,8 +2038,8 @@ mt7615_mac_update_mib_stats(struct mt7615_phy *phy) val); val = mt76_rr(dev, MT_TX_AGG_CNT(ext_phy, i)); - dev->mt76.aggr_stats[aggr++] += val & 0xffff; - dev->mt76.aggr_stats[aggr++] += val >> 16; + phy->mt76->aggr_stats[aggr++] += val & 0xffff; + phy->mt76->aggr_stats[aggr++] += val >> 16; } } diff --git a/drivers/net/wireless/mediatek/mt76/mt76x02_debugfs.c b/drivers/net/wireless/mediatek/mt76/mt76x02_debugfs.c index 981ac8e84807..8ce4bf44733d 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76x02_debugfs.c +++ b/drivers/net/wireless/mediatek/mt76/mt76x02_debugfs.c @@ -20,7 +20,7 @@ mt76x02_ampdu_stat_show(struct seq_file *file, void *data) seq_puts(file, "Count: "); for (j = 0; j < 8; j++) seq_printf(file, "%8d | ", - dev->mt76.aggr_stats[i * 8 + j]); + dev->mphy.aggr_stats[i * 8 + j]); seq_puts(file, "\n"); seq_puts(file, "--------"); for (j = 0; j < 8; j++) diff --git a/drivers/net/wireless/mediatek/mt76/mt76x02_mac.c b/drivers/net/wireless/mediatek/mt76/mt76x02_mac.c index 93d96739f802..d3f74473e6fb 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76x02_mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt76x02_mac.c @@ -25,7 +25,7 @@ void mt76x02_mac_reset_counters(struct mt76x02_dev *dev) for (i = 0; i < 16; i++) mt76_rr(dev, MT_TX_STAT_FIFO); - memset(dev->mt76.aggr_stats, 0, sizeof(dev->mt76.aggr_stats)); + memset(dev->mphy.aggr_stats, 0, sizeof(dev->mphy.aggr_stats)); } EXPORT_SYMBOL_GPL(mt76x02_mac_reset_counters); @@ -1191,8 +1191,8 @@ void mt76x02_mac_work(struct work_struct *work) for (i = 0, idx = 0; i < 16; i++) { u32 val = mt76_rr(dev, MT_TX_AGG_CNT(i)); - dev->mt76.aggr_stats[idx++] += val & 0xffff; - dev->mt76.aggr_stats[idx++] += val >> 16; + dev->mphy.aggr_stats[idx++] += val & 0xffff; + dev->mphy.aggr_stats[idx++] += val >> 16; } mt76x02_check_mac_err(dev); diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/debugfs.c b/drivers/net/wireless/mediatek/mt76/mt7915/debugfs.c index e1d15394a621..cd774c1bc49f 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/debugfs.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/debugfs.c @@ -639,7 +639,7 @@ mt7915_ampdu_stat_read_phy(struct mt7915_phy *phy, { struct mt7915_dev *dev = phy->dev; bool ext_phy = phy != &dev->phy; - int bound[15], range[4], i, n; + int bound[15], range[4], i; /* Tx ampdu stat */ for (i = 0; i < ARRAY_SIZE(range); i++) @@ -656,9 +656,8 @@ mt7915_ampdu_stat_read_phy(struct mt7915_phy *phy, bound[i] + 1, bound[i + 1]); seq_puts(file, "\nCount: "); - n = phy->band_idx ? ARRAY_SIZE(dev->mt76.aggr_stats) / 2 : 0; for (i = 0; i < ARRAY_SIZE(bound); i++) - seq_printf(file, "%8d | ", dev->mt76.aggr_stats[i + n]); + seq_printf(file, "%8d | ", phy->mt76->aggr_stats[i]); seq_puts(file, "\n"); seq_printf(file, "BA miss count: %d\n", phy->mib.ba_miss_cnt); diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mac.c b/drivers/net/wireless/mediatek/mt76/mt7915/mac.c index 0147936cc7b8..27f9f6a8aaa8 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mac.c @@ -1159,12 +1159,8 @@ void mt7915_mac_reset_counters(struct mt7915_phy *phy) mt76_rr(dev, MT_TX_AGG_CNT2(phy->band_idx, i)); } - i = 0; phy->mt76->survey_time = ktime_get_boottime(); - if (phy->band_idx) - i = ARRAY_SIZE(dev->mt76.aggr_stats) / 2; - - memset(&dev->mt76.aggr_stats[i], 0, sizeof(dev->mt76.aggr_stats) / 2); + memset(phy->mt76->aggr_stats, 0, sizeof(phy->mt76->aggr_stats)); /* reset airtime counters */ mt76_set(dev, MT_WF_RMAC_MIB_AIRTIME0(phy->band_idx), @@ -1507,7 +1503,7 @@ void mt7915_mac_update_stats(struct mt7915_phy *phy) { struct mt7915_dev *dev = phy->dev; struct mib_stats *mib = &phy->mib; - int i, aggr0, aggr1, cnt; + int i, aggr0 = 0, aggr1, cnt; u32 val; cnt = mt76_rr(dev, MT_MIB_SDR3(phy->band_idx)); @@ -1639,7 +1635,6 @@ void mt7915_mac_update_stats(struct mt7915_phy *phy) mib->tx_amsdu_cnt += cnt; } - aggr0 = phy->band_idx ? ARRAY_SIZE(dev->mt76.aggr_stats) / 2 : 0; if (is_mt7915(&dev->mt76)) { for (i = 0, aggr1 = aggr0 + 8; i < 4; i++) { val = mt76_rr(dev, MT_MIB_MB_SDR1(phy->band_idx, (i << 4))); @@ -1654,12 +1649,12 @@ void mt7915_mac_update_stats(struct mt7915_phy *phy) FIELD_GET(MT_MIB_RTS_RETRIES_COUNT_MASK, val); val = mt76_rr(dev, MT_TX_AGG_CNT(phy->band_idx, i)); - dev->mt76.aggr_stats[aggr0++] += val & 0xffff; - dev->mt76.aggr_stats[aggr0++] += val >> 16; + phy->mt76->aggr_stats[aggr0++] += val & 0xffff; + phy->mt76->aggr_stats[aggr0++] += val >> 16; val = mt76_rr(dev, MT_TX_AGG_CNT2(phy->band_idx, i)); - dev->mt76.aggr_stats[aggr1++] += val & 0xffff; - dev->mt76.aggr_stats[aggr1++] += val >> 16; + phy->mt76->aggr_stats[aggr1++] += val & 0xffff; + phy->mt76->aggr_stats[aggr1++] += val >> 16; } cnt = mt76_rr(dev, MT_MIB_SDR32(phy->band_idx)); @@ -1706,8 +1701,8 @@ void mt7915_mac_update_stats(struct mt7915_phy *phy) for (i = 0; i < 8; i++) { val = mt76_rr(dev, MT_TX_AGG_CNT(phy->band_idx, i)); - dev->mt76.aggr_stats[aggr0++] += FIELD_GET(GENMASK(15, 0), val); - dev->mt76.aggr_stats[aggr0++] += FIELD_GET(GENMASK(31, 16), val); + phy->mt76->aggr_stats[aggr0++] += FIELD_GET(GENMASK(15, 0), val); + phy->mt76->aggr_stats[aggr0++] += FIELD_GET(GENMASK(31, 16), val); } cnt = mt76_rr(dev, MT_MIB_SDR32(phy->band_idx)); diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/main.c b/drivers/net/wireless/mediatek/mt76/mt7915/main.c index 56f43ba5ea05..820c7132bf3f 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/main.c @@ -1267,7 +1267,7 @@ void mt7915_get_et_stats(struct ieee80211_hw *hw, }; struct mib_stats *mib = &phy->mib; /* See mt7915_ampdu_stat_read_phy, etc */ - int i, n, ei = 0; + int i, ei = 0; mutex_lock(&dev->mt76.mutex); @@ -1283,9 +1283,8 @@ void mt7915_get_et_stats(struct ieee80211_hw *hw, data[ei++] = mib->tx_pkt_ibf_cnt; /* Tx ampdu stat */ - n = phy->band_idx ? ARRAY_SIZE(dev->mt76.aggr_stats) / 2 : 0; for (i = 0; i < 15 /*ARRAY_SIZE(bound)*/; i++) - data[ei++] = dev->mt76.aggr_stats[i + n]; + data[ei++] = phy->mt76->aggr_stats[i]; data[ei++] = phy->mib.ba_miss_cnt; diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/debugfs.c b/drivers/net/wireless/mediatek/mt76/mt7921/debugfs.c index bce76417f95d..29d8883268f6 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/debugfs.c +++ b/drivers/net/wireless/mediatek/mt76/mt7921/debugfs.c @@ -85,7 +85,7 @@ mt7921_ampdu_stat_read_phy(struct mt7921_phy *phy, seq_puts(file, "\nCount: "); for (i = 0; i < ARRAY_SIZE(bound); i++) - seq_printf(file, "%8d | ", dev->mt76.aggr_stats[i]); + seq_printf(file, "%8d | ", phy->mt76->aggr_stats[i]); seq_puts(file, "\n"); seq_printf(file, "BA miss count: %d\n", phy->mib.ba_miss_cnt); diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/mac.c b/drivers/net/wireless/mediatek/mt76/mt7921/mac.c index 1c0d8cf19b8e..9a4f307f8f24 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7921/mac.c @@ -745,7 +745,7 @@ void mt7921_mac_reset_counters(struct mt7921_phy *phy) } dev->mt76.phy.survey_time = ktime_get_boottime(); - memset(&dev->mt76.aggr_stats[0], 0, sizeof(dev->mt76.aggr_stats) / 2); + memset(phy->mt76->aggr_stats, 0, sizeof(phy->mt76->aggr_stats)); /* reset airtime counters */ mt76_rr(dev, MT_MIB_SDR9(0)); @@ -990,10 +990,10 @@ void mt7921_mac_update_mib_stats(struct mt7921_phy *phy) val = mt76_rr(dev, MT_TX_AGG_CNT(0, i)); val2 = mt76_rr(dev, MT_TX_AGG_CNT2(0, i)); - dev->mt76.aggr_stats[aggr0++] += val & 0xffff; - dev->mt76.aggr_stats[aggr0++] += val >> 16; - dev->mt76.aggr_stats[aggr1++] += val2 & 0xffff; - dev->mt76.aggr_stats[aggr1++] += val2 >> 16; + phy->mt76->aggr_stats[aggr0++] += val & 0xffff; + phy->mt76->aggr_stats[aggr0++] += val >> 16; + phy->mt76->aggr_stats[aggr1++] += val2 & 0xffff; + phy->mt76->aggr_stats[aggr1++] += val2 >> 16; } } diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/main.c b/drivers/net/wireless/mediatek/mt76/mt7921/main.c index 7e409ac7d9a8..092f683572f4 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7921/main.c @@ -1075,7 +1075,7 @@ void mt7921_get_et_stats(struct ieee80211_hw *hw, struct ieee80211_vif *vif, /* Tx ampdu stat */ for (i = 0; i < 15; i++) - data[ei++] = dev->mt76.aggr_stats[i]; + data[ei++] = phy->mt76->aggr_stats[i]; data[ei++] = phy->mib.ba_miss_cnt; From d493bb5b9d98cbbd37eda1619ae56a626298c8e3 Mon Sep 17 00:00:00 2001 From: Bo Jiao Date: Thu, 10 Nov 2022 04:36:31 +0800 Subject: [PATCH 1472/2002] wifi: mt76: mt7915: rework mt7915_dma_reset() Reuse mt7915_dma_disable() to reduce duplicated code. This is a preliminary patch to enable full system reset. Co-developed-by: Ryder Lee Signed-off-by: Ryder Lee Signed-off-by: Bo Jiao Signed-off-by: Felix Fietkau --- .../net/wireless/mediatek/mt76/mt7915/dma.c | 110 ++++++++++++++---- .../net/wireless/mediatek/mt76/mt7915/mac.c | 69 +---------- .../wireless/mediatek/mt76/mt7915/mt7915.h | 1 + 3 files changed, 91 insertions(+), 89 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/dma.c b/drivers/net/wireless/mediatek/mt76/mt7915/dma.c index e4fa240834d8..38360f940747 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/dma.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/dma.c @@ -50,23 +50,37 @@ static void mt7915_dma_config(struct mt7915_dev *dev) #define TXQ_CONFIG(q, wfdma, int, id) Q_CONFIG(__TXQ(q), (wfdma), (int), (id)) if (is_mt7915(&dev->mt76)) { - RXQ_CONFIG(MT_RXQ_MAIN, WFDMA0, MT_INT_RX_DONE_BAND0, MT7915_RXQ_BAND0); - RXQ_CONFIG(MT_RXQ_MCU, WFDMA1, MT_INT_RX_DONE_WM, MT7915_RXQ_MCU_WM); - RXQ_CONFIG(MT_RXQ_MCU_WA, WFDMA1, MT_INT_RX_DONE_WA, MT7915_RXQ_MCU_WA); - RXQ_CONFIG(MT_RXQ_BAND1, WFDMA0, MT_INT_RX_DONE_BAND1, MT7915_RXQ_BAND1); - RXQ_CONFIG(MT_RXQ_BAND1_WA, WFDMA1, MT_INT_RX_DONE_WA_EXT, MT7915_RXQ_MCU_WA_EXT); - RXQ_CONFIG(MT_RXQ_MAIN_WA, WFDMA1, MT_INT_RX_DONE_WA_MAIN, MT7915_RXQ_MCU_WA); + RXQ_CONFIG(MT_RXQ_MAIN, WFDMA0, MT_INT_RX_DONE_BAND0, + MT7915_RXQ_BAND0); + RXQ_CONFIG(MT_RXQ_MCU, WFDMA1, MT_INT_RX_DONE_WM, + MT7915_RXQ_MCU_WM); + RXQ_CONFIG(MT_RXQ_MCU_WA, WFDMA1, MT_INT_RX_DONE_WA, + MT7915_RXQ_MCU_WA); + RXQ_CONFIG(MT_RXQ_BAND1, WFDMA0, MT_INT_RX_DONE_BAND1, + MT7915_RXQ_BAND1); + RXQ_CONFIG(MT_RXQ_BAND1_WA, WFDMA1, MT_INT_RX_DONE_WA_EXT, + MT7915_RXQ_MCU_WA_EXT); + RXQ_CONFIG(MT_RXQ_MAIN_WA, WFDMA1, MT_INT_RX_DONE_WA_MAIN, + MT7915_RXQ_MCU_WA); TXQ_CONFIG(0, WFDMA1, MT_INT_TX_DONE_BAND0, MT7915_TXQ_BAND0); TXQ_CONFIG(1, WFDMA1, MT_INT_TX_DONE_BAND1, MT7915_TXQ_BAND1); - MCUQ_CONFIG(MT_MCUQ_WM, WFDMA1, MT_INT_TX_DONE_MCU_WM, MT7915_TXQ_MCU_WM); - MCUQ_CONFIG(MT_MCUQ_WA, WFDMA1, MT_INT_TX_DONE_MCU_WA, MT7915_TXQ_MCU_WA); - MCUQ_CONFIG(MT_MCUQ_FWDL, WFDMA1, MT_INT_TX_DONE_FWDL, MT7915_TXQ_FWDL); + MCUQ_CONFIG(MT_MCUQ_WM, WFDMA1, MT_INT_TX_DONE_MCU_WM, + MT7915_TXQ_MCU_WM); + MCUQ_CONFIG(MT_MCUQ_WA, WFDMA1, MT_INT_TX_DONE_MCU_WA, + MT7915_TXQ_MCU_WA); + MCUQ_CONFIG(MT_MCUQ_FWDL, WFDMA1, MT_INT_TX_DONE_FWDL, + MT7915_TXQ_FWDL); } else { - RXQ_CONFIG(MT_RXQ_MCU, WFDMA0, MT_INT_RX_DONE_WM, MT7916_RXQ_MCU_WM); - RXQ_CONFIG(MT_RXQ_BAND1_WA, WFDMA0, MT_INT_RX_DONE_WA_EXT_MT7916, MT7916_RXQ_MCU_WA_EXT); - MCUQ_CONFIG(MT_MCUQ_WM, WFDMA0, MT_INT_TX_DONE_MCU_WM, MT7915_TXQ_MCU_WM); - MCUQ_CONFIG(MT_MCUQ_WA, WFDMA0, MT_INT_TX_DONE_MCU_WA_MT7916, MT7915_TXQ_MCU_WA); - MCUQ_CONFIG(MT_MCUQ_FWDL, WFDMA0, MT_INT_TX_DONE_FWDL, MT7915_TXQ_FWDL); + RXQ_CONFIG(MT_RXQ_MCU, WFDMA0, MT_INT_RX_DONE_WM, + MT7916_RXQ_MCU_WM); + RXQ_CONFIG(MT_RXQ_BAND1_WA, WFDMA0, MT_INT_RX_DONE_WA_EXT_MT7916, + MT7916_RXQ_MCU_WA_EXT); + MCUQ_CONFIG(MT_MCUQ_WM, WFDMA0, MT_INT_TX_DONE_MCU_WM, + MT7915_TXQ_MCU_WM); + MCUQ_CONFIG(MT_MCUQ_WA, WFDMA0, MT_INT_TX_DONE_MCU_WA_MT7916, + MT7915_TXQ_MCU_WA); + MCUQ_CONFIG(MT_MCUQ_FWDL, WFDMA0, MT_INT_TX_DONE_FWDL, + MT7915_TXQ_FWDL); if (is_mt7916(&dev->mt76) && mtk_wed_device_active(&dev->mt76.mmio.wed)) { RXQ_CONFIG(MT_RXQ_MAIN, WFDMA0, MT_INT_WED_RX_DONE_BAND0_MT7916, @@ -77,16 +91,23 @@ static void mt7915_dma_config(struct mt7915_dev *dev) MT7916_RXQ_BAND1); RXQ_CONFIG(MT_RXQ_MAIN_WA, WFDMA0, MT_INT_WED_RX_DONE_WA_MAIN_MT7916, MT7916_RXQ_MCU_WA_MAIN); - TXQ_CONFIG(0, WFDMA0, MT_INT_WED_TX_DONE_BAND0, MT7915_TXQ_BAND0); - TXQ_CONFIG(1, WFDMA0, MT_INT_WED_TX_DONE_BAND1, MT7915_TXQ_BAND1); + TXQ_CONFIG(0, WFDMA0, MT_INT_WED_TX_DONE_BAND0, + MT7915_TXQ_BAND0); + TXQ_CONFIG(1, WFDMA0, MT_INT_WED_TX_DONE_BAND1, + MT7915_TXQ_BAND1); } else { - RXQ_CONFIG(MT_RXQ_MAIN, WFDMA0, MT_INT_RX_DONE_BAND0_MT7916, MT7916_RXQ_BAND0); - RXQ_CONFIG(MT_RXQ_MCU_WA, WFDMA0, MT_INT_RX_DONE_WA, MT7916_RXQ_MCU_WA); - RXQ_CONFIG(MT_RXQ_BAND1, WFDMA0, MT_INT_RX_DONE_BAND1_MT7916, MT7916_RXQ_BAND1); + RXQ_CONFIG(MT_RXQ_MAIN, WFDMA0, MT_INT_RX_DONE_BAND0_MT7916, + MT7916_RXQ_BAND0); + RXQ_CONFIG(MT_RXQ_MCU_WA, WFDMA0, MT_INT_RX_DONE_WA, + MT7916_RXQ_MCU_WA); + RXQ_CONFIG(MT_RXQ_BAND1, WFDMA0, MT_INT_RX_DONE_BAND1_MT7916, + MT7916_RXQ_BAND1); RXQ_CONFIG(MT_RXQ_MAIN_WA, WFDMA0, MT_INT_RX_DONE_WA_MAIN_MT7916, MT7916_RXQ_MCU_WA_MAIN); - TXQ_CONFIG(0, WFDMA0, MT_INT_TX_DONE_BAND0, MT7915_TXQ_BAND0); - TXQ_CONFIG(1, WFDMA0, MT_INT_TX_DONE_BAND1, MT7915_TXQ_BAND1); + TXQ_CONFIG(0, WFDMA0, MT_INT_TX_DONE_BAND0, + MT7915_TXQ_BAND0); + TXQ_CONFIG(1, WFDMA0, MT_INT_TX_DONE_BAND1, + MT7915_TXQ_BAND1); } } } @@ -514,6 +535,53 @@ int mt7915_dma_init(struct mt7915_dev *dev, struct mt7915_phy *phy2) return 0; } +int mt7915_dma_reset(struct mt7915_dev *dev, bool force) +{ + struct mt76_phy *mphy_ext = dev->mt76.phys[MT_BAND1]; + int i; + + /* clean up hw queues */ + for (i = 0; i < ARRAY_SIZE(dev->mt76.phy.q_tx); i++) { + mt76_queue_tx_cleanup(dev, dev->mphy.q_tx[i], true); + if (mphy_ext) + mt76_queue_tx_cleanup(dev, mphy_ext->q_tx[i], true); + } + + for (i = 0; i < ARRAY_SIZE(dev->mt76.q_mcu); i++) + mt76_queue_tx_cleanup(dev, dev->mt76.q_mcu[i], true); + + mt76_for_each_q_rx(&dev->mt76, i) + mt76_queue_rx_cleanup(dev, &dev->mt76.q_rx[i]); + + /* reset wfsys */ + if (force) + mt7915_wfsys_reset(dev); + + mt7915_dma_disable(dev, force); + + /* reset hw queues */ + for (i = 0; i < __MT_TXQ_MAX; i++) { + mt76_queue_reset(dev, dev->mphy.q_tx[i]); + if (mphy_ext) + mt76_queue_reset(dev, mphy_ext->q_tx[i]); + } + + for (i = 0; i < __MT_MCUQ_MAX; i++) + mt76_queue_reset(dev, dev->mt76.q_mcu[i]); + + mt76_for_each_q_rx(&dev->mt76, i) + mt76_queue_reset(dev, &dev->mt76.q_rx[i]); + + mt76_tx_status_check(&dev->mt76, true); + + mt7915_dma_enable(dev); + + mt76_for_each_q_rx(&dev->mt76, i) + mt76_queue_rx_reset(dev, i); + + return 0; +} + void mt7915_dma_cleanup(struct mt7915_dev *dev) { mt7915_dma_disable(dev, true); diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mac.c b/drivers/net/wireless/mediatek/mt76/mt7915/mac.c index 27f9f6a8aaa8..662671621cf0 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mac.c @@ -1332,73 +1332,6 @@ mt7915_update_beacons(struct mt7915_dev *dev) mt7915_update_vif_beacon, mphy_ext->hw); } -static void -mt7915_dma_reset(struct mt7915_dev *dev) -{ - struct mt76_phy *mphy_ext = dev->mt76.phys[MT_BAND1]; - u32 hif1_ofs = MT_WFDMA0_PCIE1(0) - MT_WFDMA0(0); - int i; - - mt76_clear(dev, MT_WFDMA0_GLO_CFG, - MT_WFDMA0_GLO_CFG_TX_DMA_EN | - MT_WFDMA0_GLO_CFG_RX_DMA_EN); - - if (is_mt7915(&dev->mt76)) - mt76_clear(dev, MT_WFDMA1_GLO_CFG, - MT_WFDMA1_GLO_CFG_TX_DMA_EN | - MT_WFDMA1_GLO_CFG_RX_DMA_EN); - if (dev->hif2) { - mt76_clear(dev, MT_WFDMA0_GLO_CFG + hif1_ofs, - MT_WFDMA0_GLO_CFG_TX_DMA_EN | - MT_WFDMA0_GLO_CFG_RX_DMA_EN); - - if (is_mt7915(&dev->mt76)) - mt76_clear(dev, MT_WFDMA1_GLO_CFG + hif1_ofs, - MT_WFDMA1_GLO_CFG_TX_DMA_EN | - MT_WFDMA1_GLO_CFG_RX_DMA_EN); - } - - usleep_range(1000, 2000); - - for (i = 0; i < __MT_TXQ_MAX; i++) { - mt76_queue_tx_cleanup(dev, dev->mphy.q_tx[i], true); - if (mphy_ext) - mt76_queue_tx_cleanup(dev, mphy_ext->q_tx[i], true); - } - - for (i = 0; i < __MT_MCUQ_MAX; i++) - mt76_queue_tx_cleanup(dev, dev->mt76.q_mcu[i], true); - - mt76_for_each_q_rx(&dev->mt76, i) - mt76_queue_rx_reset(dev, i); - - mt76_tx_status_check(&dev->mt76, true); - - /* re-init prefetch settings after reset */ - mt7915_dma_prefetch(dev); - - mt76_set(dev, MT_WFDMA0_GLO_CFG, - MT_WFDMA0_GLO_CFG_TX_DMA_EN | MT_WFDMA0_GLO_CFG_RX_DMA_EN); - if (is_mt7915(&dev->mt76)) - mt76_set(dev, MT_WFDMA1_GLO_CFG, - MT_WFDMA1_GLO_CFG_TX_DMA_EN | - MT_WFDMA1_GLO_CFG_RX_DMA_EN | - MT_WFDMA1_GLO_CFG_OMIT_TX_INFO | - MT_WFDMA1_GLO_CFG_OMIT_RX_INFO); - if (dev->hif2) { - mt76_set(dev, MT_WFDMA0_GLO_CFG + hif1_ofs, - MT_WFDMA0_GLO_CFG_TX_DMA_EN | - MT_WFDMA0_GLO_CFG_RX_DMA_EN); - - if (is_mt7915(&dev->mt76)) - mt76_set(dev, MT_WFDMA1_GLO_CFG + hif1_ofs, - MT_WFDMA1_GLO_CFG_TX_DMA_EN | - MT_WFDMA1_GLO_CFG_RX_DMA_EN | - MT_WFDMA1_GLO_CFG_OMIT_TX_INFO | - MT_WFDMA1_GLO_CFG_OMIT_RX_INFO); - } -} - void mt7915_tx_token_put(struct mt7915_dev *dev) { struct mt76_txwi_cache *txwi; @@ -1450,7 +1383,7 @@ void mt7915_mac_reset_work(struct work_struct *work) mt76_wr(dev, MT_MCU_INT_EVENT, MT_MCU_INT_EVENT_DMA_STOPPED); if (mt7915_wait_reset_state(dev, MT_MCU_CMD_RESET_DONE)) { - mt7915_dma_reset(dev); + mt7915_dma_reset(dev, false); mt7915_tx_token_put(dev); idr_init(&dev->mt76.token); diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h b/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h index 98c13b6c47d4..ecbab762b6ed 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h @@ -447,6 +447,7 @@ s8 mt7915_eeprom_get_power_delta(struct mt7915_dev *dev, int band); int mt7915_dma_init(struct mt7915_dev *dev, struct mt7915_phy *phy2); void mt7915_dma_prefetch(struct mt7915_dev *dev); void mt7915_dma_cleanup(struct mt7915_dev *dev); +int mt7915_dma_reset(struct mt7915_dev *dev, bool force); int mt7915_mcu_init(struct mt7915_dev *dev); int mt7915_mcu_twt_agrt_update(struct mt7915_dev *dev, struct mt7915_vif *mvif, From 8a55712d124fd8a919e8a69b70643e1a97280b4b Mon Sep 17 00:00:00 2001 From: Bo Jiao Date: Thu, 10 Nov 2022 04:36:32 +0800 Subject: [PATCH 1473/2002] wifi: mt76: mt7915: enable full system reset support Add mt7915_reset() and refactor mt7915_mac_reset_work() to support full system recovery. Co-developed-by: Ryder Lee Signed-off-by: Ryder Lee Signed-off-by: Bo Jiao Signed-off-by: Felix Fietkau --- .../net/wireless/mediatek/mt76/mt7915/init.c | 11 +- .../net/wireless/mediatek/mt76/mt7915/mac.c | 211 +++++++++++++++++- .../net/wireless/mediatek/mt76/mt7915/main.c | 18 +- .../net/wireless/mediatek/mt76/mt7915/mcu.c | 24 +- .../net/wireless/mediatek/mt76/mt7915/mmio.c | 7 +- .../wireless/mediatek/mt76/mt7915/mt7915.h | 15 +- .../net/wireless/mediatek/mt76/mt7915/regs.h | 4 + 7 files changed, 264 insertions(+), 26 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/init.c b/drivers/net/wireless/mediatek/mt76/mt7915/init.c index a042e0154211..51593f380f84 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/init.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/init.c @@ -262,9 +262,8 @@ static void mt7915_led_set_brightness(struct led_classdev *led_cdev, mt7915_led_set_config(led_cdev, 0xff, 0); } -static void -mt7915_init_txpower(struct mt7915_dev *dev, - struct ieee80211_supported_band *sband) +void mt7915_init_txpower(struct mt7915_dev *dev, + struct ieee80211_supported_band *sband) { int i, n_chains = hweight8(dev->mphy.antenna_mask); int nss_delta = mt76_tx_power_nss_delta(n_chains); @@ -470,7 +469,7 @@ mt7915_mac_init_band(struct mt7915_dev *dev, u8 band) mt76_rmw(dev, MT_WTBLOFF_TOP_RSCR(band), mask, set); } -static void mt7915_mac_init(struct mt7915_dev *dev) +void mt7915_mac_init(struct mt7915_dev *dev) { int i; u32 rx_len = is_mt7915(&dev->mt76) ? 0x400 : 0x680; @@ -500,7 +499,7 @@ static void mt7915_mac_init(struct mt7915_dev *dev) } } -static int mt7915_txbf_init(struct mt7915_dev *dev) +int mt7915_txbf_init(struct mt7915_dev *dev) { int ret; @@ -1141,6 +1140,8 @@ int mt7915_register_device(struct mt7915_dev *dev) goto unreg_thermal; } + dev->recovery.hw_init_done = true; + mt7915_init_debugfs(&dev->phy); return 0; diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mac.c b/drivers/net/wireless/mediatek/mt76/mt7915/mac.c index 662671621cf0..383b260eb0aa 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mac.c @@ -1291,7 +1291,7 @@ mt7915_wait_reset_state(struct mt7915_dev *dev, u32 state) bool ret; ret = wait_event_timeout(dev->reset_wait, - (READ_ONCE(dev->reset_state) & state), + (READ_ONCE(dev->recovery.state) & state), MT7915_RESET_TIMEOUT); WARN(!ret, "Timeout waiting for MCU reset state %x\n", state); @@ -1346,6 +1346,168 @@ void mt7915_tx_token_put(struct mt7915_dev *dev) idr_destroy(&dev->mt76.token); } +static int +mt7915_mac_restart(struct mt7915_dev *dev) +{ + struct mt7915_phy *phy2; + struct mt76_phy *ext_phy; + struct mt76_dev *mdev = &dev->mt76; + int i, ret; + + ext_phy = dev->mt76.phys[MT_BAND1]; + phy2 = ext_phy ? ext_phy->priv : NULL; + + if (dev->hif2) { + mt76_wr(dev, MT_INT1_MASK_CSR, 0x0); + mt76_wr(dev, MT_INT1_SOURCE_CSR, ~0); + } + + if (dev_is_pci(mdev->dev)) { + mt76_wr(dev, MT_PCIE_MAC_INT_ENABLE, 0x0); + if (dev->hif2) + mt76_wr(dev, MT_PCIE1_MAC_INT_ENABLE, 0x0); + } + + set_bit(MT76_RESET, &dev->mphy.state); + set_bit(MT76_MCU_RESET, &dev->mphy.state); + wake_up(&dev->mt76.mcu.wait); + if (ext_phy) { + set_bit(MT76_RESET, &ext_phy->state); + set_bit(MT76_MCU_RESET, &ext_phy->state); + } + + /* lock/unlock all queues to ensure that no tx is pending */ + mt76_txq_schedule_all(&dev->mphy); + if (ext_phy) + mt76_txq_schedule_all(ext_phy); + + /* disable all tx/rx napi */ + mt76_worker_disable(&dev->mt76.tx_worker); + mt76_for_each_q_rx(mdev, i) { + if (mdev->q_rx[i].ndesc) + napi_disable(&dev->mt76.napi[i]); + } + napi_disable(&dev->mt76.tx_napi); + + /* token reinit */ + mt7915_tx_token_put(dev); + idr_init(&dev->mt76.token); + + mt7915_dma_reset(dev, true); + + local_bh_disable(); + mt76_for_each_q_rx(mdev, i) { + if (mdev->q_rx[i].ndesc) { + napi_enable(&dev->mt76.napi[i]); + napi_schedule(&dev->mt76.napi[i]); + } + } + local_bh_enable(); + clear_bit(MT76_MCU_RESET, &dev->mphy.state); + clear_bit(MT76_STATE_MCU_RUNNING, &dev->mphy.state); + + mt76_wr(dev, MT_INT_MASK_CSR, dev->mt76.mmio.irqmask); + mt76_wr(dev, MT_INT_SOURCE_CSR, ~0); + + if (dev->hif2) { + mt76_wr(dev, MT_INT1_MASK_CSR, dev->mt76.mmio.irqmask); + mt76_wr(dev, MT_INT1_SOURCE_CSR, ~0); + } + if (dev_is_pci(mdev->dev)) { + mt76_wr(dev, MT_PCIE_MAC_INT_ENABLE, 0xff); + if (dev->hif2) + mt76_wr(dev, MT_PCIE1_MAC_INT_ENABLE, 0xff); + } + + /* load firmware */ + ret = mt7915_mcu_init_firmware(dev); + if (ret) + goto out; + + /* set the necessary init items */ + ret = mt7915_mcu_set_eeprom(dev); + if (ret) + goto out; + + mt7915_mac_init(dev); + mt7915_init_txpower(dev, &dev->mphy.sband_2g.sband); + mt7915_init_txpower(dev, &dev->mphy.sband_5g.sband); + ret = mt7915_txbf_init(dev); + + if (test_bit(MT76_STATE_RUNNING, &dev->mphy.state)) { + ret = mt7915_run(dev->mphy.hw); + if (ret) + goto out; + } + + if (ext_phy && test_bit(MT76_STATE_RUNNING, &ext_phy->state)) { + ret = mt7915_run(ext_phy->hw); + if (ret) + goto out; + } + +out: + /* reset done */ + clear_bit(MT76_RESET, &dev->mphy.state); + if (phy2) + clear_bit(MT76_RESET, &phy2->mt76->state); + + local_bh_disable(); + napi_enable(&dev->mt76.tx_napi); + napi_schedule(&dev->mt76.tx_napi); + local_bh_enable(); + + mt76_worker_enable(&dev->mt76.tx_worker); + + return ret; +} + +static void +mt7915_mac_full_reset(struct mt7915_dev *dev) +{ + struct mt76_phy *ext_phy; + int i; + + ext_phy = dev->mt76.phys[MT_BAND1]; + + dev->recovery.hw_full_reset = true; + + wake_up(&dev->mt76.mcu.wait); + ieee80211_stop_queues(mt76_hw(dev)); + if (ext_phy) + ieee80211_stop_queues(ext_phy->hw); + + cancel_delayed_work_sync(&dev->mphy.mac_work); + if (ext_phy) + cancel_delayed_work_sync(&ext_phy->mac_work); + + mutex_lock(&dev->mt76.mutex); + for (i = 0; i < 10; i++) { + if (!mt7915_mac_restart(dev)) + break; + } + mutex_unlock(&dev->mt76.mutex); + + if (i == 10) + dev_err(dev->mt76.dev, "chip full reset failed\n"); + + ieee80211_restart_hw(mt76_hw(dev)); + if (ext_phy) + ieee80211_restart_hw(ext_phy->hw); + + ieee80211_wake_queues(mt76_hw(dev)); + if (ext_phy) + ieee80211_wake_queues(ext_phy->hw); + + dev->recovery.hw_full_reset = false; + ieee80211_queue_delayed_work(mt76_hw(dev), &dev->mphy.mac_work, + MT7915_WATCHDOG_TIME); + if (ext_phy) + ieee80211_queue_delayed_work(ext_phy->hw, + &ext_phy->mac_work, + MT7915_WATCHDOG_TIME); +} + /* system error recovery */ void mt7915_mac_reset_work(struct work_struct *work) { @@ -1358,7 +1520,28 @@ void mt7915_mac_reset_work(struct work_struct *work) ext_phy = dev->mt76.phys[MT_BAND1]; phy2 = ext_phy ? ext_phy->priv : NULL; - if (!(READ_ONCE(dev->reset_state) & MT_MCU_CMD_STOP_DMA)) + /* chip full reset */ + if (dev->recovery.restart) { + /* disable WA/WM WDT */ + mt76_clear(dev, MT_WFDMA0_MCU_HOST_INT_ENA, + MT_MCU_CMD_WDT_MASK); + + mt7915_mac_full_reset(dev); + + /* enable mcu irq */ + mt7915_irq_enable(dev, MT_INT_MCU_CMD); + mt7915_irq_disable(dev, 0); + + /* enable WA/WM WDT */ + mt76_set(dev, MT_WFDMA0_MCU_HOST_INT_ENA, MT_MCU_CMD_WDT_MASK); + + dev->recovery.state = MT_MCU_CMD_NORMAL_STATE; + dev->recovery.restart = false; + return; + } + + /* chip partial reset */ + if (!(READ_ONCE(dev->recovery.state) & MT_MCU_CMD_STOP_DMA)) return; ieee80211_stop_queues(mt76_hw(dev)); @@ -1432,6 +1615,30 @@ void mt7915_mac_reset_work(struct work_struct *work) MT7915_WATCHDOG_TIME); } +void mt7915_reset(struct mt7915_dev *dev) +{ + if (!dev->recovery.hw_init_done) + return; + + if (dev->recovery.hw_full_reset) + return; + + /* wm/wa exception: do full recovery */ + if (READ_ONCE(dev->recovery.state) & MT_MCU_CMD_WDT_MASK) { + dev->recovery.restart = true; + dev_info(dev->mt76.dev, + "%s indicated firmware crash, attempting recovery\n", + wiphy_name(dev->mt76.hw->wiphy)); + + mt7915_irq_disable(dev, MT_INT_MCU_CMD); + queue_work(dev->mt76.wq, &dev->reset_work); + return; + } + + queue_work(dev->mt76.wq, &dev->reset_work); + wake_up(&dev->reset_wait); +} + void mt7915_mac_update_stats(struct mt7915_phy *phy) { struct mt7915_dev *dev = phy->dev; diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/main.c b/drivers/net/wireless/mediatek/mt76/mt7915/main.c index 820c7132bf3f..3933f4f2d71d 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/main.c @@ -20,17 +20,13 @@ static bool mt7915_dev_running(struct mt7915_dev *dev) return phy && test_bit(MT76_STATE_RUNNING, &phy->mt76->state); } -static int mt7915_start(struct ieee80211_hw *hw) +int mt7915_run(struct ieee80211_hw *hw) { struct mt7915_dev *dev = mt7915_hw_dev(hw); struct mt7915_phy *phy = mt7915_hw_phy(hw); bool running; int ret; - flush_work(&dev->init_work); - - mutex_lock(&dev->mt76.mutex); - running = mt7915_dev_running(dev); if (!running) { @@ -80,6 +76,18 @@ static int mt7915_start(struct ieee80211_hw *hw) mt7915_mac_reset_counters(phy); out: + return ret; +} + +static int mt7915_start(struct ieee80211_hw *hw) +{ + struct mt7915_dev *dev = mt7915_hw_dev(hw); + int ret; + + flush_work(&dev->init_work); + + mutex_lock(&dev->mt76.mutex); + ret = mt7915_run(hw); mutex_unlock(&dev->mt76.mutex); return ret; diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c index 4000fcd7132a..17e13fd0ede9 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c @@ -2249,18 +2249,10 @@ mt7915_mcu_init_rx_airtime(struct mt7915_dev *dev) sizeof(req), true); } -int mt7915_mcu_init(struct mt7915_dev *dev) +int mt7915_mcu_init_firmware(struct mt7915_dev *dev) { - static const struct mt76_mcu_ops mt7915_mcu_ops = { - .headroom = sizeof(struct mt76_connac2_mcu_txd), - .mcu_skb_send_msg = mt7915_mcu_send_message, - .mcu_parse_response = mt7915_mcu_parse_response, - .mcu_restart = mt76_connac_mcu_restart, - }; int ret; - dev->mt76.mcu_ops = &mt7915_mcu_ops; - /* force firmware operation mode into normal state, * which should be set before firmware download stage. */ @@ -2309,6 +2301,20 @@ int mt7915_mcu_init(struct mt7915_dev *dev) MCU_WA_PARAM_RED, 0, 0); } +int mt7915_mcu_init(struct mt7915_dev *dev) +{ + static const struct mt76_mcu_ops mt7915_mcu_ops = { + .headroom = sizeof(struct mt76_connac2_mcu_txd), + .mcu_skb_send_msg = mt7915_mcu_send_message, + .mcu_parse_response = mt7915_mcu_parse_response, + .mcu_restart = mt76_connac_mcu_restart, + }; + + dev->mt76.mcu_ops = &mt7915_mcu_ops; + + return mt7915_mcu_init_firmware(dev); +} + void mt7915_mcu_exit(struct mt7915_dev *dev) { __mt76_mcu_restart(&dev->mt76); diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mmio.c b/drivers/net/wireless/mediatek/mt76/mt7915/mmio.c index be1b8ea711c7..032af46289af 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mmio.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mmio.c @@ -746,10 +746,9 @@ static void mt7915_irq_tasklet(struct tasklet_struct *t) u32 val = mt76_rr(dev, MT_MCU_CMD); mt76_wr(dev, MT_MCU_CMD, val); - if (val & MT_MCU_CMD_ERROR_MASK) { - dev->reset_state = val; - queue_work(dev->mt76.wq, &dev->reset_work); - wake_up(&dev->reset_wait); + if (val & (MT_MCU_CMD_ERROR_MASK | MT_MCU_CMD_WDT_MASK)) { + dev->recovery.state = val; + mt7915_reset(dev); } } } diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h b/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h index ecbab762b6ed..fce1fde72116 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h @@ -307,7 +307,13 @@ struct mt7915_dev { struct work_struct rc_work; struct work_struct reset_work; wait_queue_head_t reset_wait; - u32 reset_state; + + struct { + u32 state; + bool hw_full_reset:1; + bool hw_init_done:1; + bool restart:1; + } recovery; struct list_head sta_rc_list; struct list_head sta_poll_list; @@ -448,7 +454,13 @@ int mt7915_dma_init(struct mt7915_dev *dev, struct mt7915_phy *phy2); void mt7915_dma_prefetch(struct mt7915_dev *dev); void mt7915_dma_cleanup(struct mt7915_dev *dev); int mt7915_dma_reset(struct mt7915_dev *dev, bool force); +int mt7915_txbf_init(struct mt7915_dev *dev); +void mt7915_init_txpower(struct mt7915_dev *dev, + struct ieee80211_supported_band *sband); +void mt7915_reset(struct mt7915_dev *dev); +int mt7915_run(struct ieee80211_hw *hw); int mt7915_mcu_init(struct mt7915_dev *dev); +int mt7915_mcu_init_firmware(struct mt7915_dev *dev); int mt7915_mcu_twt_agrt_update(struct mt7915_dev *dev, struct mt7915_vif *mvif, struct mt7915_twt_flow *flow, @@ -548,6 +560,7 @@ static inline void mt7915_irq_disable(struct mt7915_dev *dev, u32 mask) mt76_set_irq_mask(&dev->mt76, MT_INT_MASK_CSR, mask, 0); } +void mt7915_mac_init(struct mt7915_dev *dev); u32 mt7915_mac_wtbl_lmac_addr(struct mt7915_dev *dev, u16 wcid, u8 dw); bool mt7915_mac_wtbl_update(struct mt7915_dev *dev, int idx, u32 mask); void mt7915_mac_reset_counters(struct mt7915_phy *phy); diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/regs.h b/drivers/net/wireless/mediatek/mt76/mt7915/regs.h index 9924271d8e36..16214cee5d93 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/regs.h +++ b/drivers/net/wireless/mediatek/mt76/mt7915/regs.h @@ -563,6 +563,8 @@ enum offs_rev { #define MT_WFDMA0_BUSY_ENA_TX_FIFO1 BIT(1) #define MT_WFDMA0_BUSY_ENA_RX_FIFO BIT(2) +#define MT_WFDMA0_MCU_HOST_INT_ENA MT_WFDMA0(0x1f4) + #define MT_WFDMA0_GLO_CFG MT_WFDMA0(0x208) #define MT_WFDMA0_GLO_CFG_TX_DMA_EN BIT(0) #define MT_WFDMA0_GLO_CFG_RX_DMA_EN BIT(2) @@ -731,6 +733,8 @@ enum offs_rev { #define MT_MCU_CMD_NORMAL_STATE BIT(5) #define MT_MCU_CMD_ERROR_MASK GENMASK(5, 1) +#define MT_MCU_CMD_WDT_MASK GENMASK(31, 30) + /* TOP RGU */ #define MT_TOP_RGU_BASE 0x18000000 #define MT_TOP_PWR_CTRL (MT_TOP_RGU_BASE + (0x0)) From b662b71ac3cccb50e9a45aae194591fc50e433ce Mon Sep 17 00:00:00 2001 From: Ryder Lee Date: Thu, 10 Nov 2022 04:36:33 +0800 Subject: [PATCH 1474/2002] wifi: mt76: mt7915: add full system reset into debugfs Trigger firmware crash and enable full system recovery through debugfs. This also renames knob "fw_ser" to a clear-cut name "sys_recovery". Signed-off-by: Ryder Lee Signed-off-by: Felix Fietkau --- .../wireless/mediatek/mt76/mt7915/debugfs.c | 81 +++++++++++++++---- .../net/wireless/mediatek/mt76/mt7915/mac.c | 5 ++ .../net/wireless/mediatek/mt76/mt7915/mcu.h | 2 + .../wireless/mediatek/mt76/mt7915/mt7915.h | 2 + .../net/wireless/mediatek/mt76/mt7915/regs.h | 4 + 5 files changed, 80 insertions(+), 14 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/debugfs.c b/drivers/net/wireless/mediatek/mt76/mt7915/debugfs.c index cd774c1bc49f..096cb8a4db3d 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/debugfs.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/debugfs.c @@ -46,8 +46,8 @@ DEFINE_DEBUGFS_ATTRIBUTE(fops_implicit_txbf, mt7915_implicit_txbf_get, /* test knob of system error recovery */ static ssize_t -mt7915_fw_ser_set(struct file *file, const char __user *user_buf, - size_t count, loff_t *ppos) +mt7915_sys_recovery_set(struct file *file, const char __user *user_buf, + size_t count, loff_t *ppos) { struct mt7915_phy *phy = file->private_data; struct mt7915_dev *dev = phy->dev; @@ -71,8 +71,18 @@ mt7915_fw_ser_set(struct file *file, const char __user *user_buf, return -EINVAL; switch (val) { + /* + * 0: grab firmware current SER state. + * 1: trigger & enable system error L1 recovery. + * 2: trigger & enable system error L2 recovery. + * 3: trigger & enable system error L3 rx abort. + * 4: trigger & enable system error L3 tx abort + * 5: trigger & enable system error L3 tx disable. + * 6: trigger & enable system error L3 bf recovery. + * 7: trigger & enable system error full recovery. + * 8: trigger firmware crash. + */ case SER_QUERY: - /* grab firmware SER stats */ ret = mt7915_mcu_set_ser(dev, 0, 0, ext_phy); break; case SER_SET_RECOVER_L1: @@ -87,6 +97,23 @@ mt7915_fw_ser_set(struct file *file, const char __user *user_buf, ret = mt7915_mcu_set_ser(dev, SER_RECOVER, val, ext_phy); break; + + /* enable full chip reset */ + case SER_SET_RECOVER_FULL: + mt76_set(dev, MT_WFDMA0_MCU_HOST_INT_ENA, MT_MCU_CMD_WDT_MASK); + ret = mt7915_mcu_set_ser(dev, 1, 3, ext_phy); + if (ret) + return ret; + + dev->recovery.state |= MT_MCU_CMD_WDT_MASK; + mt7915_reset(dev); + break; + + /* WARNING: trigger firmware crash */ + case SER_SET_SYSTEM_ASSERT: + mt76_wr(dev, MT_MCU_WM_CIRQ_EINT_MASK_CLR_ADDR, BIT(18)); + mt76_wr(dev, MT_MCU_WM_CIRQ_EINT_SOFT_ADDR, BIT(18)); + break; default: break; } @@ -95,20 +122,45 @@ mt7915_fw_ser_set(struct file *file, const char __user *user_buf, } static ssize_t -mt7915_fw_ser_get(struct file *file, char __user *user_buf, - size_t count, loff_t *ppos) +mt7915_sys_recovery_get(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) { struct mt7915_phy *phy = file->private_data; struct mt7915_dev *dev = phy->dev; char *buff; int desc = 0; ssize_t ret; - static const size_t bufsz = 400; + static const size_t bufsz = 1024; buff = kmalloc(bufsz, GFP_KERNEL); if (!buff) return -ENOMEM; + /* HELP */ + desc += scnprintf(buff + desc, bufsz - desc, + "Please echo the correct value ...\n"); + desc += scnprintf(buff + desc, bufsz - desc, + "0: grab firmware transient SER state\n"); + desc += scnprintf(buff + desc, bufsz - desc, + "1: trigger system error L1 recovery\n"); + desc += scnprintf(buff + desc, bufsz - desc, + "2: trigger system error L2 recovery\n"); + desc += scnprintf(buff + desc, bufsz - desc, + "3: trigger system error L3 rx abort\n"); + desc += scnprintf(buff + desc, bufsz - desc, + "4: trigger system error L3 tx abort\n"); + desc += scnprintf(buff + desc, bufsz - desc, + "5: trigger system error L3 tx disable\n"); + desc += scnprintf(buff + desc, bufsz - desc, + "6: trigger system error L3 bf recovery\n"); + desc += scnprintf(buff + desc, bufsz - desc, + "7: trigger system error full recovery\n"); + desc += scnprintf(buff + desc, bufsz - desc, + "8: trigger firmware crash\n"); + + /* SER statistics */ + desc += scnprintf(buff + desc, bufsz - desc, + "\nlet's dump firmware SER statistics...\n"); desc += scnprintf(buff + desc, bufsz - desc, "::E R , SER_STATUS = 0x%08x\n", mt76_rr(dev, MT_SWDEF_SER_STATS)); @@ -139,15 +191,19 @@ mt7915_fw_ser_get(struct file *file, char __user *user_buf, desc += scnprintf(buff + desc, bufsz - desc, "::E R , SER_LMAC_WISR7_B1 = 0x%08x\n", mt76_rr(dev, MT_SWDEF_LAMC_WISR7_BN1_STATS)); + desc += scnprintf(buff + desc, bufsz - desc, + "\nSYS_RESET_COUNT: WM %d, WA %d\n", + dev->recovery.wm_reset_count, + dev->recovery.wa_reset_count); ret = simple_read_from_buffer(user_buf, count, ppos, buff, desc); kfree(buff); return ret; } -static const struct file_operations mt7915_fw_ser_ops = { - .write = mt7915_fw_ser_set, - .read = mt7915_fw_ser_get, +static const struct file_operations mt7915_sys_recovery_ops = { + .write = mt7915_sys_recovery_set, + .read = mt7915_sys_recovery_get, .open = simple_open, .llseek = default_llseek, }; @@ -598,10 +654,6 @@ mt7915_fw_util_wm_show(struct seq_file *file, void *data) struct mt7915_dev *dev = file->private; seq_printf(file, "Program counter: 0x%x\n", mt76_rr(dev, MT_WM_MCU_PC)); - seq_printf(file, "Exception state: 0x%x\n", - is_mt7915(&dev->mt76) ? - (u32)mt76_get_field(dev, MT_FW_EXCEPTION, GENMASK(15, 8)) : - (u32)mt76_get_field(dev, MT_FW_EXCEPTION, GENMASK(7, 0))); if (dev->fw.debug_wm) { seq_printf(file, "Busy: %u%% Peak busy: %u%%\n", @@ -1009,7 +1061,8 @@ int mt7915_init_debugfs(struct mt7915_phy *phy) debugfs_create_file("xmit-queues", 0400, dir, phy, &mt7915_xmit_queues_fops); debugfs_create_file("tx_stats", 0400, dir, phy, &mt7915_tx_stats_fops); - debugfs_create_file("fw_ser", 0600, dir, phy, &mt7915_fw_ser_ops); + debugfs_create_file("sys_recovery", 0600, dir, phy, + &mt7915_sys_recovery_ops); debugfs_create_file("fw_debug_wm", 0600, dir, dev, &fops_fw_debug_wm); debugfs_create_file("fw_debug_wa", 0600, dir, dev, &fops_fw_debug_wa); debugfs_create_file("fw_debug_bin", 0600, dir, dev, &fops_fw_debug_bin); diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mac.c b/drivers/net/wireless/mediatek/mt76/mt7915/mac.c index 383b260eb0aa..3ac143a9e804 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mac.c @@ -1526,6 +1526,11 @@ void mt7915_mac_reset_work(struct work_struct *work) mt76_clear(dev, MT_WFDMA0_MCU_HOST_INT_ENA, MT_MCU_CMD_WDT_MASK); + if (READ_ONCE(dev->recovery.state) & MT_MCU_CMD_WA_WDT) + dev->recovery.wa_reset_count++; + else + dev->recovery.wm_reset_count++; + mt7915_mac_full_reset(dev); /* enable mcu irq */ diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.h b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.h index a4f903f88078..c19b5d66c0e1 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.h +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.h @@ -450,6 +450,8 @@ enum { SER_SET_RECOVER_L3_TX_ABORT, SER_SET_RECOVER_L3_TX_DISABLE, SER_SET_RECOVER_L3_BF, + SER_SET_RECOVER_FULL, + SER_SET_SYSTEM_ASSERT, /* action */ SER_ENABLE = 2, SER_RECOVER diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h b/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h index fce1fde72116..8615a38bec06 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h @@ -310,6 +310,8 @@ struct mt7915_dev { struct { u32 state; + u32 wa_reset_count; + u32 wm_reset_count; bool hw_full_reset:1; bool hw_init_done:1; bool restart:1; diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/regs.h b/drivers/net/wireless/mediatek/mt76/mt7915/regs.h index 16214cee5d93..dc360d52f49b 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/regs.h +++ b/drivers/net/wireless/mediatek/mt76/mt7915/regs.h @@ -733,6 +733,8 @@ enum offs_rev { #define MT_MCU_CMD_NORMAL_STATE BIT(5) #define MT_MCU_CMD_ERROR_MASK GENMASK(5, 1) +#define MT_MCU_CMD_WA_WDT BIT(31) +#define MT_MCU_CMD_WM_WDT BIT(30) #define MT_MCU_CMD_WDT_MASK GENMASK(31, 30) /* TOP RGU */ @@ -1151,5 +1153,7 @@ enum offs_rev { #define MT_MCU_WM_CIRQ(ofs) (MT_MCU_WM_CIRQ_BASE + (ofs)) #define MT_MCU_WM_CIRQ_IRQ_MASK_CLR_ADDR MT_MCU_WM_CIRQ(0x80) #define MT_MCU_WM_CIRQ_IRQ_SOFT_ADDR MT_MCU_WM_CIRQ(0xc0) +#define MT_MCU_WM_CIRQ_EINT_MASK_CLR_ADDR MT_MCU_WM_CIRQ(0x108) +#define MT_MCU_WM_CIRQ_EINT_SOFT_ADDR MT_MCU_WM_CIRQ(0x118) #endif From 4dbcb9125cc3e10a6d879c10e4f5816d05a87c49 Mon Sep 17 00:00:00 2001 From: Ryder Lee Date: Thu, 10 Nov 2022 04:36:34 +0800 Subject: [PATCH 1475/2002] wifi: mt76: mt7915: enable coredump support Host triggered and catastrophic event triggered firmware core dumping for basic firmware issues triage, including state reporting, task/irq info, function calltrace and MCU memory dump. Signed-off-by: Ryder Lee Signed-off-by: Felix Fietkau --- .../net/wireless/mediatek/mt76/mt7915/Kconfig | 1 + .../wireless/mediatek/mt76/mt7915/Makefile | 3 +- .../wireless/mediatek/mt76/mt7915/coredump.c | 410 ++++++++++++++++++ .../wireless/mediatek/mt76/mt7915/coredump.h | 136 ++++++ .../net/wireless/mediatek/mt76/mt7915/init.c | 12 +- .../net/wireless/mediatek/mt76/mt7915/mac.c | 73 +++- .../net/wireless/mediatek/mt76/mt7915/mmio.c | 59 ++- .../wireless/mediatek/mt76/mt7915/mt7915.h | 22 + .../net/wireless/mediatek/mt76/mt7915/regs.h | 34 +- 9 files changed, 742 insertions(+), 8 deletions(-) create mode 100644 drivers/net/wireless/mediatek/mt76/mt7915/coredump.c create mode 100644 drivers/net/wireless/mediatek/mt76/mt7915/coredump.h diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/Kconfig b/drivers/net/wireless/mediatek/mt76/mt7915/Kconfig index f21282cea845..d710726d47bf 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/Kconfig +++ b/drivers/net/wireless/mediatek/mt76/mt7915/Kconfig @@ -2,6 +2,7 @@ config MT7915E tristate "MediaTek MT7915E (PCIe) support" select MT76_CONNAC_LIB + select WANT_DEV_COREDUMP depends on MAC80211 depends on PCI select RELAY diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/Makefile b/drivers/net/wireless/mediatek/mt76/mt7915/Makefile index b794ceb79c37..797ae49805c3 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/Makefile +++ b/drivers/net/wireless/mediatek/mt76/mt7915/Makefile @@ -6,4 +6,5 @@ mt7915e-y := pci.o init.o dma.o eeprom.o main.o mcu.o mac.o \ debugfs.o mmio.o mt7915e-$(CONFIG_NL80211_TESTMODE) += testmode.o -mt7915e-$(CONFIG_MT7986_WMAC) += soc.o \ No newline at end of file +mt7915e-$(CONFIG_MT7986_WMAC) += soc.o +mt7915e-$(CONFIG_DEV_COREDUMP) += coredump.o diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/coredump.c b/drivers/net/wireless/mediatek/mt76/mt7915/coredump.c new file mode 100644 index 000000000000..d097a56dd33d --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7915/coredump.c @@ -0,0 +1,410 @@ +// SPDX-License-Identifier: ISC +/* Copyright (C) 2022 MediaTek Inc. */ + +#include +#include +#include +#include +#include "coredump.h" + +static bool coredump_memdump; +module_param(coredump_memdump, bool, 0644); +MODULE_PARM_DESC(coredump_memdump, "Optional ability to dump firmware memory"); + +static const struct mt7915_mem_region mt7915_mem_regions[] = { + { + .start = 0xe003b400, + .len = 0x00003bff, + .name = "CRAM", + }, +}; + +static const struct mt7915_mem_region mt7916_mem_regions[] = { + { + .start = 0x00800000, + .len = 0x0005ffff, + .name = "ROM", + }, + { + .start = 0x00900000, + .len = 0x00013fff, + .name = "ULM1", + }, + { + .start = 0x02200000, + .len = 0x0004ffff, + .name = "ULM2", + }, + { + .start = 0x02300000, + .len = 0x0004ffff, + .name = "ULM3", + }, + { + .start = 0x00400000, + .len = 0x00027fff, + .name = "SRAM", + }, + { + .start = 0xe0000000, + .len = 0x00157fff, + .name = "CRAM", + }, +}; + +static const struct mt7915_mem_region mt7986_mem_regions[] = { + { + .start = 0x00800000, + .len = 0x0005ffff, + .name = "ROM", + }, + { + .start = 0x00900000, + .len = 0x0000ffff, + .name = "ULM1", + }, + { + .start = 0x02200000, + .len = 0x0004ffff, + .name = "ULM2", + }, + { + .start = 0x02300000, + .len = 0x0004ffff, + .name = "ULM3", + }, + { + .start = 0x00400000, + .len = 0x00017fff, + .name = "SRAM", + }, + { + .start = 0xe0000000, + .len = 0x00113fff, + .name = "CRAM", + }, +}; + +const struct mt7915_mem_region* +mt7915_coredump_get_mem_layout(struct mt7915_dev *dev, u32 *num) +{ + switch (mt76_chip(&dev->mt76)) { + case 0x7915: + *num = ARRAY_SIZE(mt7915_mem_regions); + return &mt7915_mem_regions[0]; + case 0x7986: + *num = ARRAY_SIZE(mt7986_mem_regions); + return &mt7986_mem_regions[0]; + case 0x7916: + *num = ARRAY_SIZE(mt7916_mem_regions); + return &mt7916_mem_regions[0]; + default: + return NULL; + } +} + +static int mt7915_coredump_get_mem_size(struct mt7915_dev *dev) +{ + const struct mt7915_mem_region *mem_region; + size_t size = 0; + u32 num; + int i; + + mem_region = mt7915_coredump_get_mem_layout(dev, &num); + if (!mem_region) + return 0; + + for (i = 0; i < num; i++) { + size += mem_region->len; + mem_region++; + } + + /* reserve space for the headers */ + size += num * sizeof(struct mt7915_mem_hdr); + /* make sure it is aligned 4 bytes for debug message print out */ + size = ALIGN(size, 4); + + return size; +} + +struct mt7915_crash_data *mt7915_coredump_new(struct mt7915_dev *dev) +{ + struct mt7915_crash_data *crash_data = dev->coredump.crash_data; + + lockdep_assert_held(&dev->dump_mutex); + + guid_gen(&crash_data->guid); + ktime_get_real_ts64(&crash_data->timestamp); + + return crash_data; +} + +static void +mt7915_coredump_fw_state(struct mt7915_dev *dev, struct mt7915_coredump *dump, + bool *exception) +{ + u32 state, count, type; + + type = (u32)mt76_get_field(dev, MT_FW_EXCEPT_TYPE, GENMASK(7, 0)); + state = (u32)mt76_get_field(dev, MT_FW_ASSERT_STAT, GENMASK(7, 0)); + count = is_mt7915(&dev->mt76) ? + (u32)mt76_get_field(dev, MT_FW_EXCEPT_COUNT, GENMASK(15, 8)) : + (u32)mt76_get_field(dev, MT_FW_EXCEPT_COUNT, GENMASK(7, 0)); + + /* normal mode: driver can manually trigger assert for detail info */ + if (!count) + strscpy(dump->fw_state, "normal", sizeof(dump->fw_state)); + else if (state > 1 && (count == 1) && type == 5) + strscpy(dump->fw_state, "assert", sizeof(dump->fw_state)); + else if ((state > 1 && count == 1) || count > 1) + strscpy(dump->fw_state, "exception", sizeof(dump->fw_state)); + + *exception = !!count; +} + +static void +mt7915_coredump_fw_trace(struct mt7915_dev *dev, struct mt7915_coredump *dump, + bool exception) +{ + u32 n, irq, sch, base = MT_FW_EINT_INFO; + + /* trap or run? */ + dump->last_msg_id = mt76_rr(dev, MT_FW_LAST_MSG_ID); + + n = is_mt7915(&dev->mt76) ? + (u32)mt76_get_field(dev, base, GENMASK(7, 0)) : + (u32)mt76_get_field(dev, base, GENMASK(15, 8)); + dump->eint_info_idx = n; + + irq = mt76_rr(dev, base + 0x8); + n = is_mt7915(&dev->mt76) ? + FIELD_GET(GENMASK(7, 0), irq) : FIELD_GET(GENMASK(23, 16), irq); + dump->irq_info_idx = n; + + sch = mt76_rr(dev, MT_FW_SCHED_INFO); + n = is_mt7915(&dev->mt76) ? + FIELD_GET(GENMASK(7, 0), sch) : FIELD_GET(GENMASK(15, 8), sch); + dump->sched_info_idx = n; + + if (exception) { + u32 i, y; + + /* sched trace */ + n = is_mt7915(&dev->mt76) ? + FIELD_GET(GENMASK(15, 8), sch) : FIELD_GET(GENMASK(7, 0), sch); + n = n > 60 ? 60 : n; + + strscpy(dump->trace_sched, "(sched_info) id, time", + sizeof(dump->trace_sched)); + + for (y = dump->sched_info_idx, i = 0; i < n; i++, y++) { + mt7915_memcpy_fromio(dev, dump->sched, base + 0xc + y * 12, + sizeof(dump->sched)); + y = y >= n ? 0 : y; + } + + /* irq trace */ + n = is_mt7915(&dev->mt76) ? + FIELD_GET(GENMASK(15, 8), irq) : FIELD_GET(GENMASK(7, 0), irq); + n = n > 60 ? 60 : n; + + strscpy(dump->trace_irq, "(irq_info) id, time", + sizeof(dump->trace_irq)); + + for (y = dump->irq_info_idx, i = 0; i < n; i++, y++) { + mt7915_memcpy_fromio(dev, dump->irq, base + 0x4 + y * 16, + sizeof(dump->irq)); + y = y >= n ? 0 : y; + } + } +} + +static void +mt7915_coredump_fw_stack(struct mt7915_dev *dev, struct mt7915_coredump *dump, + bool exception) +{ + u32 oldest, i, idx; + + /* stop call stack record */ + if (!exception) + mt76_clear(dev, 0x89050200, BIT(0)); + + oldest = (u32)mt76_get_field(dev, 0x89050200, GENMASK(20, 16)) + 2; + for (i = 0; i < 16; i++) { + idx = ((oldest + 2 * i + 1) % 32); + dump->call_stack[i] = mt76_rr(dev, 0x89050204 + idx * 4); + } + + /* start call stack record */ + if (!exception) + mt76_set(dev, 0x89050200, BIT(0)); +} + +static void +mt7915_coredump_fw_task(struct mt7915_dev *dev, struct mt7915_coredump *dump) +{ + u32 offs = is_mt7915(&dev->mt76) ? 0xe0 : 0x170; + + strscpy(dump->task_qid, "(task queue id) read, write", + sizeof(dump->task_qid)); + + dump->taskq[0].read = mt76_rr(dev, MT_FW_TASK_QID1); + dump->taskq[0].write = mt76_rr(dev, MT_FW_TASK_QID1 - 4); + dump->taskq[1].read = mt76_rr(dev, MT_FW_TASK_QID2); + dump->taskq[1].write = mt76_rr(dev, MT_FW_TASK_QID2 - 4); + + strscpy(dump->task_info, "(task stack) start, end, size", + sizeof(dump->task_info)); + + dump->taski[0].start = mt76_rr(dev, MT_FW_TASK_START); + dump->taski[0].end = mt76_rr(dev, MT_FW_TASK_END); + dump->taski[0].size = mt76_rr(dev, MT_FW_TASK_SIZE); + dump->taski[1].start = mt76_rr(dev, MT_FW_TASK_START + offs); + dump->taski[1].end = mt76_rr(dev, MT_FW_TASK_END + offs); + dump->taski[1].size = mt76_rr(dev, MT_FW_TASK_SIZE + offs); +} + +static void +mt7915_coredump_fw_context(struct mt7915_dev *dev, struct mt7915_coredump *dump) +{ + u32 count, idx, id; + + count = mt76_rr(dev, MT_FW_CIRQ_COUNT); + + /* current context */ + if (!count) { + strscpy(dump->fw_context, "(context) interrupt", + sizeof(dump->fw_context)); + + idx = is_mt7915(&dev->mt76) ? + (u32)mt76_get_field(dev, MT_FW_CIRQ_IDX, GENMASK(31, 16)) : + (u32)mt76_get_field(dev, MT_FW_CIRQ_IDX, GENMASK(15, 0)); + dump->context.idx = idx; + dump->context.handler = mt76_rr(dev, MT_FW_CIRQ_LISR); + } else { + idx = mt76_rr(dev, MT_FW_TASK_IDX); + id = mt76_rr(dev, MT_FW_TASK_ID); + + if (!id && idx == 3) { + strscpy(dump->fw_context, "(context) idle", + sizeof(dump->fw_context)); + } else if (id && idx != 3) { + strscpy(dump->fw_context, "(context) task", + sizeof(dump->fw_context)); + + dump->context.idx = idx; + dump->context.handler = id; + } + } +} + +static struct mt7915_coredump *mt7915_coredump_build(struct mt7915_dev *dev) +{ + struct mt7915_crash_data *crash_data = dev->coredump.crash_data; + struct mt7915_coredump *dump; + struct mt7915_coredump_mem *dump_mem; + size_t len, sofar = 0, hdr_len = sizeof(*dump); + unsigned char *buf; + bool exception; + + len = hdr_len; + + if (coredump_memdump && crash_data->memdump_buf_len) + len += sizeof(*dump_mem) + crash_data->memdump_buf_len; + + sofar += hdr_len; + + /* this is going to get big when we start dumping memory and such, + * so go ahead and use vmalloc. + */ + buf = vzalloc(len); + if (!buf) + return NULL; + + mutex_lock(&dev->dump_mutex); + + dump = (struct mt7915_coredump *)(buf); + dump->len = len; + + /* plain text */ + strscpy(dump->magic, "mt76-crash-dump", sizeof(dump->magic)); + strscpy(dump->kernel, init_utsname()->release, sizeof(dump->kernel)); + strscpy(dump->fw_ver, dev->mt76.hw->wiphy->fw_version, + sizeof(dump->fw_ver)); + + guid_copy(&dump->guid, &crash_data->guid); + dump->tv_sec = crash_data->timestamp.tv_sec; + dump->tv_nsec = crash_data->timestamp.tv_nsec; + dump->device_id = mt76_chip(&dev->mt76); + + mt7915_coredump_fw_state(dev, dump, &exception); + mt7915_coredump_fw_trace(dev, dump, exception); + mt7915_coredump_fw_task(dev, dump); + mt7915_coredump_fw_context(dev, dump); + mt7915_coredump_fw_stack(dev, dump, exception); + + /* gather memory content */ + dump_mem = (struct mt7915_coredump_mem *)(buf + sofar); + dump_mem->len = crash_data->memdump_buf_len; + if (coredump_memdump && crash_data->memdump_buf_len) + memcpy(dump_mem->data, crash_data->memdump_buf, + crash_data->memdump_buf_len); + + mutex_unlock(&dev->dump_mutex); + + return dump; +} + +int mt7915_coredump_submit(struct mt7915_dev *dev) +{ + struct mt7915_coredump *dump; + + dump = mt7915_coredump_build(dev); + if (!dump) { + dev_warn(dev->mt76.dev, "no crash dump data found\n"); + return -ENODATA; + } + + dev_coredumpv(dev->mt76.dev, dump, dump->len, GFP_KERNEL); + + return 0; +} + +int mt7915_coredump_register(struct mt7915_dev *dev) +{ + struct mt7915_crash_data *crash_data; + + crash_data = vzalloc(sizeof(*dev->coredump.crash_data)); + if (!crash_data) + return -ENOMEM; + + dev->coredump.crash_data = crash_data; + + if (coredump_memdump) { + crash_data->memdump_buf_len = mt7915_coredump_get_mem_size(dev); + if (!crash_data->memdump_buf_len) + /* no memory content */ + return 0; + + crash_data->memdump_buf = vzalloc(crash_data->memdump_buf_len); + if (!crash_data->memdump_buf) { + vfree(crash_data); + return -ENOMEM; + } + } + + return 0; +} + +void mt7915_coredump_unregister(struct mt7915_dev *dev) +{ + if (dev->coredump.crash_data->memdump_buf) { + vfree(dev->coredump.crash_data->memdump_buf); + dev->coredump.crash_data->memdump_buf = NULL; + dev->coredump.crash_data->memdump_buf_len = 0; + } + + vfree(dev->coredump.crash_data); + dev->coredump.crash_data = NULL; +} + diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/coredump.h b/drivers/net/wireless/mediatek/mt76/mt7915/coredump.h new file mode 100644 index 000000000000..709f8e9c795c --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7915/coredump.h @@ -0,0 +1,136 @@ +/* SPDX-License-Identifier: ISC */ +/* Copyright (C) 2022 MediaTek Inc. */ + +#ifndef _COREDUMP_H_ +#define _COREDUMP_H_ + +#include "mt7915.h" + +struct trace { + u32 id; + u32 timestamp; +}; + +struct mt7915_coredump { + char magic[16]; + + u32 len; + + guid_t guid; + + /* time-of-day stamp */ + u64 tv_sec; + /* time-of-day stamp, nano-seconds */ + u64 tv_nsec; + /* kernel version */ + char kernel[64]; + /* firmware version */ + char fw_ver[ETHTOOL_FWVERS_LEN]; + + u32 device_id; + + /* exception state */ + char fw_state[12]; + + u32 last_msg_id; + u32 eint_info_idx; + u32 irq_info_idx; + u32 sched_info_idx; + + /* schedule info */ + char trace_sched[32]; + struct { + struct trace t; + u32 pc; + } sched[60]; + + /* irq info */ + char trace_irq[32]; + struct trace irq[60]; + + /* task queue status */ + char task_qid[32]; + struct { + u32 read; + u32 write; + } taskq[2]; + + /* task stack info */ + char task_info[32]; + struct { + u32 start; + u32 end; + u32 size; + } taski[2]; + + /* firmware context */ + char fw_context[24]; + struct { + u32 idx; + u32 handler; + } context; + + /* link registers calltrace */ + u32 call_stack[16]; + + /* memory content */ + u8 data[]; +} __packed; + +struct mt7915_coredump_mem { + u32 len; + u8 data[]; +} __packed; + +struct mt7915_mem_hdr { + u32 start; + u32 len; + u8 data[]; +}; + +struct mt7915_mem_region { + u32 start; + size_t len; + + const char *name; +}; + +#ifdef CONFIG_DEV_COREDUMP + +const struct mt7915_mem_region * +mt7915_coredump_get_mem_layout(struct mt7915_dev *dev, u32 *num); +struct mt7915_crash_data *mt7915_coredump_new(struct mt7915_dev *dev); +int mt7915_coredump_submit(struct mt7915_dev *dev); +int mt7915_coredump_register(struct mt7915_dev *dev); +void mt7915_coredump_unregister(struct mt7915_dev *dev); + +#else /* CONFIG_DEV_COREDUMP */ + +static inline const struct mt7915_mem_region * +mt7915_coredump_get_mem_layout(struct mt7915_dev *dev, u32 *num) +{ + return NULL; +} + +static inline int mt7915_coredump_submit(struct mt7915_dev *dev) +{ + return 0; +} + +static inline struct mt7915_crash_data *mt7915_coredump_new(struct mt7915_dev *dev) +{ + return NULL; +} + +static inline int mt7915_coredump_register(struct mt7915_dev *dev) +{ + return 0; +} + +static inline void mt7915_coredump_unregister(struct mt7915_dev *dev) +{ +} + +#endif /* CONFIG_DEV_COREDUMP */ + +#endif /* _COREDUMP_H_ */ diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/init.c b/drivers/net/wireless/mediatek/mt76/mt7915/init.c index 51593f380f84..e2e43a96416e 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/init.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/init.c @@ -8,6 +8,7 @@ #include "mt7915.h" #include "mac.h" #include "mcu.h" +#include "coredump.h" #include "eeprom.h" static const struct ieee80211_iface_limit if_limits[] = { @@ -1100,6 +1101,8 @@ int mt7915_register_device(struct mt7915_dev *dev) init_waitqueue_head(&dev->reset_wait); INIT_WORK(&dev->reset_work, mt7915_mac_reset_work); + INIT_WORK(&dev->dump_work, mt7915_mac_dump_work); + mutex_init(&dev->dump_mutex); dev->dbdc_support = mt7915_band_config(dev); @@ -1142,7 +1145,13 @@ int mt7915_register_device(struct mt7915_dev *dev) dev->recovery.hw_init_done = true; - mt7915_init_debugfs(&dev->phy); + ret = mt7915_init_debugfs(&dev->phy); + if (ret) + goto unreg_thermal; + + ret = mt7915_coredump_register(dev); + if (ret) + goto unreg_thermal; return 0; @@ -1161,6 +1170,7 @@ free_phy2: void mt7915_unregister_device(struct mt7915_dev *dev) { mt7915_unregister_ext_phy(dev); + mt7915_coredump_unregister(dev); mt7915_unregister_thermal(&dev->phy); mt76_unregister_device(&dev->mt76); mt7915_stop_hardware(dev); diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mac.c b/drivers/net/wireless/mediatek/mt76/mt7915/mac.c index 3ac143a9e804..117ddb00348c 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mac.c @@ -3,6 +3,7 @@ #include #include +#include "coredump.h" #include "mt7915.h" #include "../dma.h" #include "mac.h" @@ -1620,6 +1621,76 @@ void mt7915_mac_reset_work(struct work_struct *work) MT7915_WATCHDOG_TIME); } +/* firmware coredump */ +void mt7915_mac_dump_work(struct work_struct *work) +{ + const struct mt7915_mem_region *mem_region; + struct mt7915_crash_data *crash_data; + struct mt7915_dev *dev; + struct mt7915_mem_hdr *hdr; + size_t buf_len; + int i; + u32 num; + u8 *buf; + + dev = container_of(work, struct mt7915_dev, dump_work); + + mutex_lock(&dev->dump_mutex); + + crash_data = mt7915_coredump_new(dev); + if (!crash_data) { + mutex_unlock(&dev->dump_mutex); + goto skip_coredump; + } + + mem_region = mt7915_coredump_get_mem_layout(dev, &num); + if (!mem_region || !crash_data->memdump_buf_len) { + mutex_unlock(&dev->dump_mutex); + goto skip_memdump; + } + + buf = crash_data->memdump_buf; + buf_len = crash_data->memdump_buf_len; + + /* dumping memory content... */ + memset(buf, 0, buf_len); + for (i = 0; i < num; i++) { + if (mem_region->len > buf_len) { + dev_warn(dev->mt76.dev, "%s len %lu is too large\n", + mem_region->name, + (unsigned long)mem_region->len); + break; + } + + /* reserve space for the header */ + hdr = (void *)buf; + buf += sizeof(*hdr); + buf_len -= sizeof(*hdr); + + mt7915_memcpy_fromio(dev, buf, mem_region->start, + mem_region->len); + + hdr->start = mem_region->start; + hdr->len = mem_region->len; + + if (!mem_region->len) + /* note: the header remains, just with zero length */ + break; + + buf += mem_region->len; + buf_len -= mem_region->len; + + mem_region++; + } + + mutex_unlock(&dev->dump_mutex); + +skip_memdump: + mt7915_coredump_submit(dev); +skip_coredump: + queue_work(dev->mt76.wq, &dev->reset_work); +} + void mt7915_reset(struct mt7915_dev *dev) { if (!dev->recovery.hw_init_done) @@ -1636,7 +1707,7 @@ void mt7915_reset(struct mt7915_dev *dev) wiphy_name(dev->mt76.hw->wiphy)); mt7915_irq_disable(dev, MT_INT_MCU_CMD); - queue_work(dev->mt76.wq, &dev->reset_work); + queue_work(dev->mt76.wq, &dev->dump_work); return; } diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mmio.c b/drivers/net/wireless/mediatek/mt76/mt7915/mmio.c index 032af46289af..3c840853a2c9 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mmio.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mmio.c @@ -25,7 +25,22 @@ static const u32 mt7915_reg[] = { [WFDMA_EXT_CSR_ADDR] = 0xd7000, [CBTOP1_PHY_END] = 0x77ffffff, [INFRA_MCU_ADDR_END] = 0x7c3fffff, - [FW_EXCEPTION_ADDR] = 0x219848, + [FW_ASSERT_STAT_ADDR] = 0x219848, + [FW_EXCEPT_TYPE_ADDR] = 0x21987c, + [FW_EXCEPT_COUNT_ADDR] = 0x219848, + [FW_CIRQ_COUNT_ADDR] = 0x216f94, + [FW_CIRQ_IDX_ADDR] = 0x216ef8, + [FW_CIRQ_LISR_ADDR] = 0x2170ac, + [FW_TASK_ID_ADDR] = 0x216f90, + [FW_TASK_IDX_ADDR] = 0x216f9c, + [FW_TASK_QID1_ADDR] = 0x219680, + [FW_TASK_QID2_ADDR] = 0x219760, + [FW_TASK_START_ADDR] = 0x219558, + [FW_TASK_END_ADDR] = 0x219554, + [FW_TASK_SIZE_ADDR] = 0x219560, + [FW_LAST_MSG_ID_ADDR] = 0x216f70, + [FW_EINT_INFO_ADDR] = 0x219818, + [FW_SCHED_INFO_ADDR] = 0x219828, [SWDEF_BASE_ADDR] = 0x41f200, [TXQ_WED_RING_BASE] = 0xd7300, [RXQ_WED_RING_BASE] = 0xd7410, @@ -43,7 +58,22 @@ static const u32 mt7916_reg[] = { [WFDMA_EXT_CSR_ADDR] = 0xd7000, [CBTOP1_PHY_END] = 0x7fffffff, [INFRA_MCU_ADDR_END] = 0x7c085fff, - [FW_EXCEPTION_ADDR] = 0x022050bc, + [FW_ASSERT_STAT_ADDR] = 0x02204c14, + [FW_EXCEPT_TYPE_ADDR] = 0x022051a4, + [FW_EXCEPT_COUNT_ADDR] = 0x022050bc, + [FW_CIRQ_COUNT_ADDR] = 0x022001ac, + [FW_CIRQ_IDX_ADDR] = 0x02204f84, + [FW_CIRQ_LISR_ADDR] = 0x022050d0, + [FW_TASK_ID_ADDR] = 0x0220406c, + [FW_TASK_IDX_ADDR] = 0x0220500c, + [FW_TASK_QID1_ADDR] = 0x022028c8, + [FW_TASK_QID2_ADDR] = 0x02202a38, + [FW_TASK_START_ADDR] = 0x0220286c, + [FW_TASK_END_ADDR] = 0x02202870, + [FW_TASK_SIZE_ADDR] = 0x02202878, + [FW_LAST_MSG_ID_ADDR] = 0x02204fe8, + [FW_EINT_INFO_ADDR] = 0x0220525c, + [FW_SCHED_INFO_ADDR] = 0x0220516c, [SWDEF_BASE_ADDR] = 0x411400, [TXQ_WED_RING_BASE] = 0xd7300, [RXQ_WED_RING_BASE] = 0xd7410, @@ -61,7 +91,22 @@ static const u32 mt7986_reg[] = { [WFDMA_EXT_CSR_ADDR] = 0x27000, [CBTOP1_PHY_END] = 0x7fffffff, [INFRA_MCU_ADDR_END] = 0x7c085fff, - [FW_EXCEPTION_ADDR] = 0x02204ffc, + [FW_ASSERT_STAT_ADDR] = 0x02204b54, + [FW_EXCEPT_TYPE_ADDR] = 0x022050dc, + [FW_EXCEPT_COUNT_ADDR] = 0x02204ffc, + [FW_CIRQ_COUNT_ADDR] = 0x022001ac, + [FW_CIRQ_IDX_ADDR] = 0x02204ec4, + [FW_CIRQ_LISR_ADDR] = 0x02205010, + [FW_TASK_ID_ADDR] = 0x02204fac, + [FW_TASK_IDX_ADDR] = 0x02204f4c, + [FW_TASK_QID1_ADDR] = 0x02202814, + [FW_TASK_QID2_ADDR] = 0x02202984, + [FW_TASK_START_ADDR] = 0x022027b8, + [FW_TASK_END_ADDR] = 0x022027bc, + [FW_TASK_SIZE_ADDR] = 0x022027c4, + [FW_LAST_MSG_ID_ADDR] = 0x02204f28, + [FW_EINT_INFO_ADDR] = 0x02205194, + [FW_SCHED_INFO_ADDR] = 0x022051a4, [SWDEF_BASE_ADDR] = 0x411400, [TXQ_WED_RING_BASE] = 0x24420, [RXQ_WED_RING_BASE] = 0x24520, @@ -457,6 +502,14 @@ static u32 __mt7915_reg_addr(struct mt7915_dev *dev, u32 addr) return mt7915_reg_map_l2(dev, addr); } +void mt7915_memcpy_fromio(struct mt7915_dev *dev, void *buf, u32 offset, + size_t len) +{ + u32 addr = __mt7915_reg_addr(dev, offset); + + memcpy_fromio(buf, dev->mt76.mmio.regs + addr, len); +} + static u32 mt7915_rr(struct mt76_dev *mdev, u32 offset) { struct mt7915_dev *dev = container_of(mdev, struct mt7915_dev, mt76); diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h b/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h index 8615a38bec06..9cb680e7f223 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h @@ -224,6 +224,15 @@ struct mib_stats { u32 tx_amsdu_cnt; }; +/* crash-dump */ +struct mt7915_crash_data { + guid_t guid; + struct timespec64 timestamp; + + u8 *memdump_buf; + size_t memdump_buf_len; +}; + struct mt7915_hif { struct list_head list; @@ -305,6 +314,7 @@ struct mt7915_dev { struct work_struct init_work; struct work_struct rc_work; + struct work_struct dump_work; struct work_struct reset_work; wait_queue_head_t reset_wait; @@ -317,6 +327,14 @@ struct mt7915_dev { bool restart:1; } recovery; + /* protects coredump data */ + struct mutex dump_mutex; +#ifdef CONFIG_DEV_COREDUMP + struct { + struct mt7915_crash_data *crash_data; + } coredump; +#endif + struct list_head sta_rc_list; struct list_head sta_poll_list; struct list_head twt_list; @@ -562,6 +580,9 @@ static inline void mt7915_irq_disable(struct mt7915_dev *dev, u32 mask) mt76_set_irq_mask(&dev->mt76, MT_INT_MASK_CSR, mask, 0); } +void mt7915_memcpy_fromio(struct mt7915_dev *dev, void *buf, u32 offset, + size_t len); + void mt7915_mac_init(struct mt7915_dev *dev); u32 mt7915_mac_wtbl_lmac_addr(struct mt7915_dev *dev, u16 wcid, u8 dw); bool mt7915_mac_wtbl_update(struct mt7915_dev *dev, int idx, u32 mask); @@ -581,6 +602,7 @@ void mt7915_mac_sta_remove(struct mt76_dev *mdev, struct ieee80211_vif *vif, struct ieee80211_sta *sta); void mt7915_mac_work(struct work_struct *work); void mt7915_mac_reset_work(struct work_struct *work); +void mt7915_mac_dump_work(struct work_struct *work); void mt7915_mac_sta_rc_work(struct work_struct *work); void mt7915_mac_update_stats(struct mt7915_phy *phy); void mt7915_mac_twt_teardown_flow(struct mt7915_dev *dev, diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/regs.h b/drivers/net/wireless/mediatek/mt76/mt7915/regs.h index dc360d52f49b..0c61f1256f3b 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/regs.h +++ b/drivers/net/wireless/mediatek/mt76/mt7915/regs.h @@ -24,7 +24,22 @@ enum reg_rev { WFDMA_EXT_CSR_ADDR, CBTOP1_PHY_END, INFRA_MCU_ADDR_END, - FW_EXCEPTION_ADDR, + FW_ASSERT_STAT_ADDR, + FW_EXCEPT_TYPE_ADDR, + FW_EXCEPT_COUNT_ADDR, + FW_CIRQ_COUNT_ADDR, + FW_CIRQ_IDX_ADDR, + FW_CIRQ_LISR_ADDR, + FW_TASK_ID_ADDR, + FW_TASK_IDX_ADDR, + FW_TASK_QID1_ADDR, + FW_TASK_QID2_ADDR, + FW_TASK_START_ADDR, + FW_TASK_END_ADDR, + FW_TASK_SIZE_ADDR, + FW_LAST_MSG_ID_ADDR, + FW_EINT_INFO_ADDR, + FW_SCHED_INFO_ADDR, SWDEF_BASE_ADDR, TXQ_WED_RING_BASE, RXQ_WED_RING_BASE, @@ -979,7 +994,22 @@ enum offs_rev { #define MT_ADIE_TYPE_MASK BIT(1) /* FW MODE SYNC */ -#define MT_FW_EXCEPTION __REG(FW_EXCEPTION_ADDR) +#define MT_FW_ASSERT_STAT __REG(FW_ASSERT_STAT_ADDR) +#define MT_FW_EXCEPT_TYPE __REG(FW_EXCEPT_TYPE_ADDR) +#define MT_FW_EXCEPT_COUNT __REG(FW_EXCEPT_COUNT_ADDR) +#define MT_FW_CIRQ_COUNT __REG(FW_CIRQ_COUNT_ADDR) +#define MT_FW_CIRQ_IDX __REG(FW_CIRQ_IDX_ADDR) +#define MT_FW_CIRQ_LISR __REG(FW_CIRQ_LISR_ADDR) +#define MT_FW_TASK_ID __REG(FW_TASK_ID_ADDR) +#define MT_FW_TASK_IDX __REG(FW_TASK_IDX_ADDR) +#define MT_FW_TASK_QID1 __REG(FW_TASK_QID1_ADDR) +#define MT_FW_TASK_QID2 __REG(FW_TASK_QID2_ADDR) +#define MT_FW_TASK_START __REG(FW_TASK_START_ADDR) +#define MT_FW_TASK_END __REG(FW_TASK_END_ADDR) +#define MT_FW_TASK_SIZE __REG(FW_TASK_SIZE_ADDR) +#define MT_FW_LAST_MSG_ID __REG(FW_LAST_MSG_ID_ADDR) +#define MT_FW_EINT_INFO __REG(FW_EINT_INFO_ADDR) +#define MT_FW_SCHED_INFO __REG(FW_SCHED_INFO_ADDR) #define MT_SWDEF_BASE __REG(SWDEF_BASE_ADDR) From de147cc28985a2a09e5d6d179fc5ef59b22fc058 Mon Sep 17 00:00:00 2001 From: Nicolas Cavallari Date: Thu, 10 Nov 2022 16:39:51 +0100 Subject: [PATCH 1476/2002] wifi: mt76: mt7915: Fix chainmask calculation on mt7915 DBDC mt7915 does not have a per-band number of chains unlike the other chips, it only has a total number of chains. Yet the current code would consider the total number as a per-band number. For example, it would report that a 2x2 + 2x2 DBDC card have 4 chains on each band and set chainmask to 0b1111 for the first interface and 0b11110000 for the second. Fixes: 99ad32a4ca3a ("mt76: mt7915: add support for MT7986") Co-developed-by: Felix Fietkau Signed-off-by: Nicolas Cavallari Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt7915/eeprom.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/eeprom.c b/drivers/net/wireless/mediatek/mt76/mt7915/eeprom.c index 83bced0c0785..0bce0ce51be0 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/eeprom.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/eeprom.c @@ -194,6 +194,7 @@ void mt7915_eeprom_parse_hw_cap(struct mt7915_dev *dev, nss = path; if (dev->dbdc_support) { if (is_mt7915(&dev->mt76)) { + path = min_t(u8, path, 2); nss = FIELD_GET(MT_EE_WIFI_CONF3_TX_PATH_B0, eeprom[MT_EE_WIFI_CONF + 3]); if (phy->band_idx) From d44f5bca5166b5de65580c5854c3255e5980f6ad Mon Sep 17 00:00:00 2001 From: Nicolas Cavallari Date: Thu, 10 Nov 2022 16:39:52 +0100 Subject: [PATCH 1477/2002] wifi: mt76: mt7915: Fix VHT beamforming capabilities with DBDC Do not hardcode beamformee 4 space-time streams support, because DBDC can only support up to 2. Fixes: 00b2e16e0063 ("mt76: mt7915: add TxBF capabilities") Signed-off-by: Nicolas Cavallari Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt7915/init.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/init.c b/drivers/net/wireless/mediatek/mt76/mt7915/init.c index e2e43a96416e..dbda7496bb5e 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/init.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/init.c @@ -735,7 +735,8 @@ void mt7915_set_stream_vht_txbf_caps(struct mt7915_phy *phy) *cap |= IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE | IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE | - (3 << IEEE80211_VHT_CAP_BEAMFORMEE_STS_SHIFT); + FIELD_PREP(IEEE80211_VHT_CAP_BEAMFORMEE_STS_MASK, + sts - 1); *cap &= ~(IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_MASK | IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE | From 510ef2269428f87146ad5076229994d8e05b8193 Mon Sep 17 00:00:00 2001 From: Nicolas Cavallari Date: Thu, 10 Nov 2022 16:39:53 +0100 Subject: [PATCH 1478/2002] wifi: mt76: mt7915: don't claim 160MHz support with mt7915 DBDC The hardware simply does not support this in DBDC mode. Remove it from the HE capabilities. Signed-off-by: Nicolas Cavallari Signed-off-by: Felix Fietkau --- .../net/wireless/mediatek/mt76/mt7915/init.c | 47 ++++++++++++------- 1 file changed, 31 insertions(+), 16 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/init.c b/drivers/net/wireless/mediatek/mt76/mt7915/init.c index dbda7496bb5e..b48c2ba9273d 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/init.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/init.c @@ -761,8 +761,12 @@ mt7915_set_stream_he_txbf_caps(struct mt7915_phy *phy, u8 c, sts_160 = sts; /* Can do 1/2 of STS in 160Mhz mode for mt7915 */ - if (is_mt7915(&dev->mt76) && !dev->dbdc_support) - sts_160 /= 2; + if (is_mt7915(&dev->mt76)) { + if (!dev->dbdc_support) + sts_160 /= 2; + else + sts_160 = 0; + } #ifdef CONFIG_MAC80211_MESH if (vif == NL80211_IFTYPE_MESH_POINT) @@ -772,8 +776,9 @@ mt7915_set_stream_he_txbf_caps(struct mt7915_phy *phy, elem->phy_cap_info[3] &= ~IEEE80211_HE_PHY_CAP3_SU_BEAMFORMER; elem->phy_cap_info[4] &= ~IEEE80211_HE_PHY_CAP4_MU_BEAMFORMER; - c = IEEE80211_HE_PHY_CAP5_BEAMFORMEE_NUM_SND_DIM_UNDER_80MHZ_MASK | - IEEE80211_HE_PHY_CAP5_BEAMFORMEE_NUM_SND_DIM_ABOVE_80MHZ_MASK; + c = IEEE80211_HE_PHY_CAP5_BEAMFORMEE_NUM_SND_DIM_UNDER_80MHZ_MASK; + if (sts_160) + c |= IEEE80211_HE_PHY_CAP5_BEAMFORMEE_NUM_SND_DIM_ABOVE_80MHZ_MASK; elem->phy_cap_info[5] &= ~c; c = IEEE80211_HE_PHY_CAP6_TRIG_SU_BEAMFORMING_FB | @@ -789,8 +794,9 @@ mt7915_set_stream_he_txbf_caps(struct mt7915_phy *phy, elem->phy_cap_info[2] |= c; c = IEEE80211_HE_PHY_CAP4_SU_BEAMFORMEE | - IEEE80211_HE_PHY_CAP4_BEAMFORMEE_MAX_STS_UNDER_80MHZ_4 | - IEEE80211_HE_PHY_CAP4_BEAMFORMEE_MAX_STS_ABOVE_80MHZ_4; + IEEE80211_HE_PHY_CAP4_BEAMFORMEE_MAX_STS_UNDER_80MHZ_4; + if (sts_160) + c |= IEEE80211_HE_PHY_CAP4_BEAMFORMEE_MAX_STS_ABOVE_80MHZ_4; elem->phy_cap_info[4] |= c; /* do not support NG16 due to spec D4.0 changes subcarrier idx */ @@ -815,12 +821,13 @@ mt7915_set_stream_he_txbf_caps(struct mt7915_phy *phy, elem->phy_cap_info[4] |= IEEE80211_HE_PHY_CAP4_MU_BEAMFORMER; /* num_snd_dim - * for mt7915, max supported sts is 2 for bw > 80MHz + * for mt7915, max supported sts is 2 for bw > 80MHz and 0 if dbdc */ c = FIELD_PREP(IEEE80211_HE_PHY_CAP5_BEAMFORMEE_NUM_SND_DIM_UNDER_80MHZ_MASK, - sts - 1) | - FIELD_PREP(IEEE80211_HE_PHY_CAP5_BEAMFORMEE_NUM_SND_DIM_ABOVE_80MHZ_MASK, - sts_160 - 1); + sts - 1); + if (sts_160) + c |= FIELD_PREP(IEEE80211_HE_PHY_CAP5_BEAMFORMEE_NUM_SND_DIM_ABOVE_80MHZ_MASK, + sts_160 - 1); elem->phy_cap_info[5] |= c; c = IEEE80211_HE_PHY_CAP6_TRIG_SU_BEAMFORMING_FB | @@ -865,11 +872,14 @@ mt7915_init_he_caps(struct mt7915_phy *phy, enum nl80211_band band, u16 mcs_map_160 = 0; u8 nss_160; - /* Can do 1/2 of NSS streams in 160Mhz mode for mt7915 */ - if (is_mt7915(&dev->mt76) && !dev->dbdc_support) + if (!is_mt7915(&dev->mt76)) + nss_160 = nss; + else if (!dev->dbdc_support) + /* Can do 1/2 of NSS streams in 160Mhz mode for mt7915 */ nss_160 = nss / 2; else - nss_160 = nss; + /* Can't do 160MHz with mt7915 dbdc */ + nss_160 = 0; for (i = 0; i < 8; i++) { if (i < nss) @@ -915,11 +925,14 @@ mt7915_init_he_caps(struct mt7915_phy *phy, enum nl80211_band band, if (band == NL80211_BAND_2GHZ) he_cap_elem->phy_cap_info[0] = IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_IN_2G; - else + else if (nss_160) he_cap_elem->phy_cap_info[0] = IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G | IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G | IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_80PLUS80_MHZ_IN_5G; + else + he_cap_elem->phy_cap_info[0] = + IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G; he_cap_elem->phy_cap_info[1] = IEEE80211_HE_PHY_CAP1_LDPC_CODING_IN_PAYLOAD; @@ -973,9 +986,11 @@ mt7915_init_he_caps(struct mt7915_phy *phy, enum nl80211_band band, IEEE80211_HE_PHY_CAP7_HE_SU_MU_PPDU_4XLTF_AND_08_US_GI; he_cap_elem->phy_cap_info[8] |= IEEE80211_HE_PHY_CAP8_20MHZ_IN_40MHZ_HE_PPDU_IN_2G | - IEEE80211_HE_PHY_CAP8_20MHZ_IN_160MHZ_HE_PPDU | - IEEE80211_HE_PHY_CAP8_80MHZ_IN_160MHZ_HE_PPDU | IEEE80211_HE_PHY_CAP8_DCM_MAX_RU_484; + if (nss_160) + he_cap_elem->phy_cap_info[8] |= + IEEE80211_HE_PHY_CAP8_20MHZ_IN_160MHZ_HE_PPDU | + IEEE80211_HE_PHY_CAP8_80MHZ_IN_160MHZ_HE_PPDU; he_cap_elem->phy_cap_info[9] |= IEEE80211_HE_PHY_CAP9_LONGER_THAN_16_SIGB_OFDM_SYM | IEEE80211_HE_PHY_CAP9_NON_TRIGGERED_CQI_FEEDBACK | From fa67120bc8c78e66bc23d385e6dfbc1de6c25134 Mon Sep 17 00:00:00 2001 From: Evelyn Tsai Date: Fri, 11 Nov 2022 15:36:15 +0800 Subject: [PATCH 1479/2002] wifi: mt76: connac: update nss calculation in txs The hardware reports the number of transmitting antennas not the actual number of spatial streams Co-developed-by: Peter Chiu Signed-off-by: Peter Chiu Signed-off-by: Evelyn Tsai Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt76_connac_mac.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac_mac.c b/drivers/net/wireless/mediatek/mt76/mt76_connac_mac.c index 8b7ec64abc95..fd60123fb284 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76_connac_mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt76_connac_mac.c @@ -567,7 +567,7 @@ bool mt76_connac2_mac_fill_txs(struct mt76_dev *dev, struct mt76_wcid *wcid, struct mt76_phy *mphy; struct rate_info rate = {}; bool cck = false; - u32 txrate, txs, mode; + u32 txrate, txs, mode, stbc; txs = le32_to_cpu(txs_data[0]); @@ -587,6 +587,10 @@ bool mt76_connac2_mac_fill_txs(struct mt76_dev *dev, struct mt76_wcid *wcid, rate.mcs = FIELD_GET(MT_TX_RATE_IDX, txrate); rate.nss = FIELD_GET(MT_TX_RATE_NSS, txrate) + 1; + stbc = FIELD_GET(MT_TX_RATE_STBC, txrate); + + if (stbc && rate.nss > 1) + rate.nss >>= 1; if (rate.nss - 1 < ARRAY_SIZE(stats->tx_nss)) stats->tx_nss[rate.nss - 1]++; From bd5dac7ced5a7c9faa4dc468ac9560c3256df845 Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Thu, 13 Oct 2022 21:07:37 +0200 Subject: [PATCH 1480/2002] wifi: mt76: do not run mt76u_status_worker if the device is not running Fix the following NULL pointer dereference avoiding to run mt76u_status_worker thread if the device is not running yet. KASAN: null-ptr-deref in range [0x0000000000000000-0x0000000000000007] CPU: 0 PID: 98 Comm: kworker/u2:2 Not tainted 5.14.0+ #78 Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS rel-1.12.1-0-ga5cab58e9a3f-prebuilt.qemu.org 04/01/2014 Workqueue: mt76 mt76u_tx_status_data RIP: 0010:mt76x02_mac_fill_tx_status.isra.0+0x82c/0x9e0 Code: c5 48 b8 00 00 00 00 00 fc ff df 80 3c 02 00 0f 85 94 01 00 00 48 b8 00 00 00 00 00 fc ff df 4d 8b 34 24 4c 89 f2 48 c1 ea 03 <0f> b6 04 02 84 c0 74 08 3c 03 0f 8e 89 01 00 00 41 8b 16 41 0f b7 RSP: 0018:ffffc900005af988 EFLAGS: 00010246 RAX: dffffc0000000000 RBX: ffffc900005afae8 RCX: 0000000000000000 RDX: 0000000000000000 RSI: ffffffff832fc661 RDI: ffffc900005afc2a RBP: ffffc900005afae0 R08: 0000000000000001 R09: fffff520000b5f3c R10: 0000000000000003 R11: fffff520000b5f3b R12: ffff88810b6132d8 R13: 000000000000ffff R14: 0000000000000000 R15: ffffc900005afc28 FS: 0000000000000000(0000) GS:ffff88811aa00000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 00007fa0eda6a000 CR3: 0000000118f17000 CR4: 0000000000750ef0 PKRU: 55555554 Call Trace: mt76x02_send_tx_status+0x1d2/0xeb0 mt76x02_tx_status_data+0x8e/0xd0 mt76u_tx_status_data+0xe1/0x240 process_one_work+0x92b/0x1460 worker_thread+0x95/0xe00 kthread+0x3a1/0x480 ret_from_fork+0x1f/0x30 Modules linked in: --[ end trace 8df5d20fc5040f65 ]-- RIP: 0010:mt76x02_mac_fill_tx_status.isra.0+0x82c/0x9e0 Code: c5 48 b8 00 00 00 00 00 fc ff df 80 3c 02 00 0f 85 94 01 00 00 48 b8 00 00 00 00 00 fc ff df 4d 8b 34 24 4c 89 f2 48 c1 ea 03 <0f> b6 04 02 84 c0 74 08 3c 03 0f 8e 89 01 00 00 41 8b 16 41 0f b7 RSP: 0018:ffffc900005af988 EFLAGS: 00010246 RAX: dffffc0000000000 RBX: ffffc900005afae8 RCX: 0000000000000000 RDX: 0000000000000000 RSI: ffffffff832fc661 RDI: ffffc900005afc2a RBP: ffffc900005afae0 R08: 0000000000000001 R09: fffff520000b5f3c R10: 0000000000000003 R11: fffff520000b5f3b R12: ffff88810b6132d8 R13: 000000000000ffff R14: 0000000000000000 R15: ffffc900005afc28 FS: 0000000000000000(0000) GS:ffff88811aa00000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 00007fa0eda6a000 CR3: 0000000118f17000 CR4: 0000000000750ef0 PKRU: 55555554 Moreover move stat_work schedule out of the for loop. Reported-by: Dokyung Song Co-developed-by: Deren Wu Signed-off-by: Deren Wu Signed-off-by: Lorenzo Bianconi Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/usb.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/usb.c b/drivers/net/wireless/mediatek/mt76/usb.c index 4c4033bb1bb3..0597df2729a6 100644 --- a/drivers/net/wireless/mediatek/mt76/usb.c +++ b/drivers/net/wireless/mediatek/mt76/usb.c @@ -766,6 +766,9 @@ static void mt76u_status_worker(struct mt76_worker *w) struct mt76_queue *q; int i; + if (!test_bit(MT76_STATE_RUNNING, &dev->phy.state)) + return; + for (i = 0; i < IEEE80211_NUM_ACS; i++) { q = dev->phy.q_tx[i]; if (!q) @@ -785,11 +788,11 @@ static void mt76u_status_worker(struct mt76_worker *w) wake_up(&dev->tx_wait); mt76_worker_schedule(&dev->tx_worker); - - if (dev->drv->tx_status_data && - !test_and_set_bit(MT76_READING_STATS, &dev->phy.state)) - queue_work(dev->wq, &dev->usb.stat_work); } + + if (dev->drv->tx_status_data && + !test_and_set_bit(MT76_READING_STATS, &dev->phy.state)) + queue_work(dev->wq, &dev->usb.stat_work); } static void mt76u_tx_status_data(struct work_struct *work) From c1eab2418fa44164a74448e62a5967f1e813e063 Mon Sep 17 00:00:00 2001 From: Sean Wang Date: Fri, 11 Nov 2022 05:23:43 +0800 Subject: [PATCH 1481/2002] wifi: mt76: connac: add mt76_connac_mcu_uni_set_chctx add mt76_connac_mcu_uni_set_chctx to set up the channel context per BSS in the firmware Signed-off-by: Sean Wang Signed-off-by: Felix Fietkau --- .../wireless/mediatek/mt76/mt76_connac_mcu.c | 147 ++++++++++-------- .../wireless/mediatek/mt76/mt76_connac_mcu.h | 3 + 2 files changed, 82 insertions(+), 68 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c index 9bba18d24c71..aa1e2faeeafe 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c @@ -1347,44 +1347,13 @@ mt76_connac_mcu_uni_bss_he_tlv(struct mt76_phy *phy, struct ieee80211_vif *vif, he->max_nss_mcs[CMD_HE_MCS_BW8080] = cap->he_mcs_nss_supp.tx_mcs_80p80; } -int mt76_connac_mcu_uni_add_bss(struct mt76_phy *phy, - struct ieee80211_vif *vif, - struct mt76_wcid *wcid, - bool enable) +int mt76_connac_mcu_uni_set_chctx(struct mt76_phy *phy, struct mt76_vif *mvif, + struct ieee80211_chanctx_conf *ctx) { - struct mt76_vif *mvif = (struct mt76_vif *)vif->drv_priv; - struct cfg80211_chan_def *chandef = &phy->chandef; + struct cfg80211_chan_def *chandef = ctx ? &ctx->def : &phy->chandef; int freq1 = chandef->center_freq1, freq2 = chandef->center_freq2; enum nl80211_band band = chandef->chan->band; struct mt76_dev *mdev = phy->dev; - struct { - struct { - u8 bss_idx; - u8 pad[3]; - } __packed hdr; - struct mt76_connac_bss_basic_tlv basic; - struct mt76_connac_bss_qos_tlv qos; - } basic_req = { - .hdr = { - .bss_idx = mvif->idx, - }, - .basic = { - .tag = cpu_to_le16(UNI_BSS_INFO_BASIC), - .len = cpu_to_le16(sizeof(struct mt76_connac_bss_basic_tlv)), - .bcn_interval = cpu_to_le16(vif->bss_conf.beacon_int), - .dtim_period = vif->bss_conf.dtim_period, - .omac_idx = mvif->omac_idx, - .band_idx = mvif->band_idx, - .wmm_idx = mvif->wmm_idx, - .active = true, /* keep bss deactivated */ - .phymode = mt76_connac_get_phy_mode(phy, vif, band, NULL), - }, - .qos = { - .tag = cpu_to_le16(UNI_BSS_INFO_QBSS), - .len = cpu_to_le16(sizeof(struct mt76_connac_bss_qos_tlv)), - .qos = vif->bss_conf.qos, - }, - }; struct { struct { u8 bss_idx; @@ -1422,6 +1391,81 @@ int mt76_connac_mcu_uni_add_bss(struct mt76_phy *phy, .band = band, }, }; + + switch (chandef->width) { + case NL80211_CHAN_WIDTH_40: + rlm_req.rlm.bw = CMD_CBW_40MHZ; + break; + case NL80211_CHAN_WIDTH_80: + rlm_req.rlm.bw = CMD_CBW_80MHZ; + break; + case NL80211_CHAN_WIDTH_80P80: + rlm_req.rlm.bw = CMD_CBW_8080MHZ; + break; + case NL80211_CHAN_WIDTH_160: + rlm_req.rlm.bw = CMD_CBW_160MHZ; + break; + case NL80211_CHAN_WIDTH_5: + rlm_req.rlm.bw = CMD_CBW_5MHZ; + break; + case NL80211_CHAN_WIDTH_10: + rlm_req.rlm.bw = CMD_CBW_10MHZ; + break; + case NL80211_CHAN_WIDTH_20_NOHT: + case NL80211_CHAN_WIDTH_20: + default: + rlm_req.rlm.bw = CMD_CBW_20MHZ; + rlm_req.rlm.ht_op_info = 0; + break; + } + + if (rlm_req.rlm.control_channel < rlm_req.rlm.center_chan) + rlm_req.rlm.sco = 1; /* SCA */ + else if (rlm_req.rlm.control_channel > rlm_req.rlm.center_chan) + rlm_req.rlm.sco = 3; /* SCB */ + + return mt76_mcu_send_msg(mdev, MCU_UNI_CMD(BSS_INFO_UPDATE), &rlm_req, + sizeof(rlm_req), true); +} +EXPORT_SYMBOL_GPL(mt76_connac_mcu_uni_set_chctx); + +int mt76_connac_mcu_uni_add_bss(struct mt76_phy *phy, + struct ieee80211_vif *vif, + struct mt76_wcid *wcid, + bool enable) +{ + struct mt76_vif *mvif = (struct mt76_vif *)vif->drv_priv; + struct cfg80211_chan_def *chandef = &phy->chandef; + enum nl80211_band band = chandef->chan->band; + struct mt76_dev *mdev = phy->dev; + struct { + struct { + u8 bss_idx; + u8 pad[3]; + } __packed hdr; + struct mt76_connac_bss_basic_tlv basic; + struct mt76_connac_bss_qos_tlv qos; + } basic_req = { + .hdr = { + .bss_idx = mvif->idx, + }, + .basic = { + .tag = cpu_to_le16(UNI_BSS_INFO_BASIC), + .len = cpu_to_le16(sizeof(struct mt76_connac_bss_basic_tlv)), + .bcn_interval = cpu_to_le16(vif->bss_conf.beacon_int), + .dtim_period = vif->bss_conf.dtim_period, + .omac_idx = mvif->omac_idx, + .band_idx = mvif->band_idx, + .wmm_idx = mvif->wmm_idx, + .active = true, /* keep bss deactivated */ + .phymode = mt76_connac_get_phy_mode(phy, vif, band, NULL), + }, + .qos = { + .tag = cpu_to_le16(UNI_BSS_INFO_QBSS), + .len = cpu_to_le16(sizeof(struct mt76_connac_bss_qos_tlv)), + .qos = vif->bss_conf.qos, + }, + }; int err, conn_type; u8 idx, basic_phy; @@ -1508,40 +1552,7 @@ int mt76_connac_mcu_uni_add_bss(struct mt76_phy *phy, return err; } - switch (chandef->width) { - case NL80211_CHAN_WIDTH_40: - rlm_req.rlm.bw = CMD_CBW_40MHZ; - break; - case NL80211_CHAN_WIDTH_80: - rlm_req.rlm.bw = CMD_CBW_80MHZ; - break; - case NL80211_CHAN_WIDTH_80P80: - rlm_req.rlm.bw = CMD_CBW_8080MHZ; - break; - case NL80211_CHAN_WIDTH_160: - rlm_req.rlm.bw = CMD_CBW_160MHZ; - break; - case NL80211_CHAN_WIDTH_5: - rlm_req.rlm.bw = CMD_CBW_5MHZ; - break; - case NL80211_CHAN_WIDTH_10: - rlm_req.rlm.bw = CMD_CBW_10MHZ; - break; - case NL80211_CHAN_WIDTH_20_NOHT: - case NL80211_CHAN_WIDTH_20: - default: - rlm_req.rlm.bw = CMD_CBW_20MHZ; - rlm_req.rlm.ht_op_info = 0; - break; - } - - if (rlm_req.rlm.control_channel < rlm_req.rlm.center_chan) - rlm_req.rlm.sco = 1; /* SCA */ - else if (rlm_req.rlm.control_channel > rlm_req.rlm.center_chan) - rlm_req.rlm.sco = 3; /* SCB */ - - return mt76_mcu_send_msg(mdev, MCU_UNI_CMD(BSS_INFO_UPDATE), &rlm_req, - sizeof(rlm_req), true); + return mt76_connac_mcu_uni_set_chctx(phy, mvif, NULL); } EXPORT_SYMBOL_GPL(mt76_connac_mcu_uni_add_bss); diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h index 8166722d4717..7e1c9e8029cd 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h +++ b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h @@ -1747,6 +1747,9 @@ int mt76_connac_mcu_uni_add_dev(struct mt76_phy *phy, int mt76_connac_mcu_sta_ba(struct mt76_dev *dev, struct mt76_vif *mvif, struct ieee80211_ampdu_params *params, int cmd, bool enable, bool tx); +int mt76_connac_mcu_uni_set_chctx(struct mt76_phy *phy, + struct mt76_vif *vif, + struct ieee80211_chanctx_conf *ctx); int mt76_connac_mcu_uni_add_bss(struct mt76_phy *phy, struct ieee80211_vif *vif, struct mt76_wcid *wcid, From a0ab9c3160dfafece67ed39f43f35f533eeea428 Mon Sep 17 00:00:00 2001 From: Sean Wang Date: Fri, 11 Nov 2022 05:23:44 +0800 Subject: [PATCH 1482/2002] wifi: mt76: mt7921: add chanctx parameter to mt76_connac_mcu_uni_add_bss signature Add a chanctx parameter to mt76_connac_mcu_uni_add_bss signature to allow the firmware binds the BSS into the specific channel context. Signed-off-by: Sean Wang Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt7615/mcu.c | 2 +- drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c | 7 ++++--- drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h | 3 ++- drivers/net/wireless/mediatek/mt76/mt7921/mac.c | 2 +- drivers/net/wireless/mediatek/mt76/mt7921/main.c | 10 ++++++---- 5 files changed, 14 insertions(+), 10 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7615/mcu.c index 3dac76e6df4d..83f30305414d 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7615/mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt7615/mcu.c @@ -1119,7 +1119,7 @@ mt7615_mcu_uni_add_bss(struct mt7615_phy *phy, struct ieee80211_vif *vif, struct mt7615_vif *mvif = (struct mt7615_vif *)vif->drv_priv; return mt76_connac_mcu_uni_add_bss(phy->mt76, vif, &mvif->sta.wcid, - enable); + enable, NULL); } static inline int diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c index aa1e2faeeafe..92855d8264d1 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c @@ -1432,10 +1432,11 @@ EXPORT_SYMBOL_GPL(mt76_connac_mcu_uni_set_chctx); int mt76_connac_mcu_uni_add_bss(struct mt76_phy *phy, struct ieee80211_vif *vif, struct mt76_wcid *wcid, - bool enable) + bool enable, + struct ieee80211_chanctx_conf *ctx) { struct mt76_vif *mvif = (struct mt76_vif *)vif->drv_priv; - struct cfg80211_chan_def *chandef = &phy->chandef; + struct cfg80211_chan_def *chandef = ctx ? &ctx->def : &phy->chandef; enum nl80211_band band = chandef->chan->band; struct mt76_dev *mdev = phy->dev; struct { @@ -1552,7 +1553,7 @@ int mt76_connac_mcu_uni_add_bss(struct mt76_phy *phy, return err; } - return mt76_connac_mcu_uni_set_chctx(phy, mvif, NULL); + return mt76_connac_mcu_uni_set_chctx(phy, mvif, ctx); } EXPORT_SYMBOL_GPL(mt76_connac_mcu_uni_add_bss); diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h index 7e1c9e8029cd..cf34192a9d90 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h +++ b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h @@ -1753,7 +1753,8 @@ int mt76_connac_mcu_uni_set_chctx(struct mt76_phy *phy, int mt76_connac_mcu_uni_add_bss(struct mt76_phy *phy, struct ieee80211_vif *vif, struct mt76_wcid *wcid, - bool enable); + bool enable, + struct ieee80211_chanctx_conf *ctx); int mt76_connac_mcu_sta_cmd(struct mt76_phy *phy, struct mt76_sta_cmd_info *info); void mt76_connac_mcu_beacon_loss_iter(void *priv, u8 *mac, diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/mac.c b/drivers/net/wireless/mediatek/mt76/mt7921/mac.c index 9a4f307f8f24..eeab756240a2 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7921/mac.c @@ -866,7 +866,7 @@ mt7921_vif_connect_iter(void *priv, u8 *mac, if (vif->type == NL80211_IFTYPE_AP) { mt76_connac_mcu_uni_add_bss(dev->phy.mt76, vif, &mvif->sta.wcid, - true); + true, NULL); mt7921_mcu_sta_update(dev, NULL, vif, true, MT76_STA_INFO_STATE_NONE); mt7921_mcu_uni_add_beacon_offload(dev, hw, vif, true); diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/main.c b/drivers/net/wireless/mediatek/mt76/mt7921/main.c index 092f683572f4..cc3365c4f8c5 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7921/main.c @@ -748,7 +748,7 @@ void mt7921_mac_sta_assoc(struct mt76_dev *mdev, struct ieee80211_vif *vif, if (vif->type == NL80211_IFTYPE_STATION && !sta->tdls) mt76_connac_mcu_uni_add_bss(&dev->mphy, vif, &mvif->sta.wcid, - true); + true, NULL); mt7921_mac_wtbl_update(dev, msta->wcid.idx, MT_WTBL_UPDATE_ADM_COUNT_CLEAR); @@ -780,7 +780,8 @@ void mt7921_mac_sta_remove(struct mt76_dev *mdev, struct ieee80211_vif *vif, ewma_rssi_init(&mvif->rssi); if (!sta->tdls) mt76_connac_mcu_uni_add_bss(&dev->mphy, vif, - &mvif->sta.wcid, false); + &mvif->sta.wcid, false, + NULL); } spin_lock_bh(&dev->sta_poll_lock); @@ -1534,7 +1535,7 @@ mt7921_start_ap(struct ieee80211_hw *hw, struct ieee80211_vif *vif, mt7921_mutex_acquire(dev); err = mt76_connac_mcu_uni_add_bss(phy->mt76, vif, &mvif->sta.wcid, - true); + true, NULL); if (err) goto out; @@ -1565,7 +1566,8 @@ mt7921_stop_ap(struct ieee80211_hw *hw, struct ieee80211_vif *vif, if (err) goto out; - mt76_connac_mcu_uni_add_bss(phy->mt76, vif, &mvif->sta.wcid, false); + mt76_connac_mcu_uni_add_bss(phy->mt76, vif, &mvif->sta.wcid, false, + NULL); out: mt7921_mutex_release(dev); From 5b55b6da982c974fca3e521edb239163d133834f Mon Sep 17 00:00:00 2001 From: Quan Zhou Date: Fri, 11 Nov 2022 05:23:45 +0800 Subject: [PATCH 1483/2002] wifi: mt76: mt7921: add unified ROC cmd/event support Add unified ROC cmd/event which is only supported by the newer fw. Co-developed-by: Sean Wang Signed-off-by: Sean Wang Co-developed-by: Deren Wu Signed-off-by: Deren Wu Co-developed-by: Kaikai Hu Signed-off-by: Kaikai Hu Signed-off-by: Quan Zhou Signed-off-by: Felix Fietkau --- .../wireless/mediatek/mt76/mt76_connac_mcu.h | 10 +- .../net/wireless/mediatek/mt76/mt7921/mcu.c | 119 ++++++++++++++++++ .../wireless/mediatek/mt76/mt7921/mt7921.h | 19 +++ 3 files changed, 146 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h index cf34192a9d90..a4893331b7c3 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h +++ b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h @@ -121,11 +121,13 @@ struct mt76_connac2_mcu_rxd { u8 eid; u8 seq; - u8 rsv[2]; - + u8 option; + u8 rsv; u8 ext_eid; u8 rsv1[2]; u8 s2d_index; + + u8 tlv[0]; }; struct mt76_connac2_patch_hdr { @@ -957,6 +959,9 @@ enum { DEV_INFO_MAX_NUM }; +#define MCU_UNI_CMD_EVENT BIT(1) +#define MCU_UNI_CMD_UNSOLICITED_EVENT BIT(2) + /* event table */ enum { MCU_EVENT_TARGET_ADDRESS_LEN = 0x01, @@ -1163,6 +1168,7 @@ enum { MCU_UNI_CMD_OFFLOAD = 0x06, MCU_UNI_CMD_HIF_CTRL = 0x07, MCU_UNI_CMD_SNIFFER = 0x24, + MCU_UNI_CMD_ROC = 0x27, }; enum { diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7921/mcu.c index 67bf92969a7b..2b36c5f6eb40 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt7921/mcu.c @@ -302,6 +302,23 @@ mt7921_mcu_rx_unsolicited_event(struct mt7921_dev *dev, struct sk_buff *skb) dev_kfree_skb(skb); } +static void +mt7921_mcu_uni_rx_unsolicited_event(struct mt7921_dev *dev, + struct sk_buff *skb) +{ + struct mt76_connac2_mcu_rxd *rxd; + + rxd = (struct mt76_connac2_mcu_rxd *)skb->data; + + switch (rxd->eid) { + case MCU_UNI_EVENT_ROC: + break; + default: + break; + } + dev_kfree_skb(skb); +} + void mt7921_mcu_rx_event(struct mt7921_dev *dev, struct sk_buff *skb) { struct mt76_connac2_mcu_rxd *rxd; @@ -311,6 +328,11 @@ void mt7921_mcu_rx_event(struct mt7921_dev *dev, struct sk_buff *skb) rxd = (struct mt76_connac2_mcu_rxd *)skb->data; + if (rxd->option & MCU_UNI_CMD_UNSOLICITED_EVENT) { + mt7921_mcu_uni_rx_unsolicited_event(dev, skb); + return; + } + if (rxd->eid == 0x6) { mt76_mcu_rx_event(&dev->mt76, skb); return; @@ -636,6 +658,103 @@ int mt7921_mcu_set_tx(struct mt7921_dev *dev, struct ieee80211_vif *vif) &req_mu, sizeof(req_mu), false); } +int mt7921_mcu_set_roc(struct mt7921_phy *phy, struct mt7921_vif *vif, + struct ieee80211_channel *chan, int duration, + enum mt7921_roc_req type, u8 token_id) +{ + int center_ch = ieee80211_frequency_to_channel(chan->center_freq); + struct mt7921_dev *dev = phy->dev; + struct { + struct { + u8 rsv[4]; + } __packed hdr; + struct roc_acquire_tlv { + __le16 tag; + __le16 len; + u8 bss_idx; + u8 tokenid; + u8 control_channel; + u8 sco; + u8 band; + u8 bw; + u8 center_chan; + u8 center_chan2; + u8 bw_from_ap; + u8 center_chan_from_ap; + u8 center_chan2_from_ap; + u8 reqtype; + __le32 maxinterval; + u8 dbdcband; + u8 rsv[3]; + } __packed roc; + } __packed req = { + .roc = { + .tag = cpu_to_le16(UNI_ROC_ACQUIRE), + .len = cpu_to_le16(sizeof(struct roc_acquire_tlv)), + .tokenid = token_id, + .reqtype = type, + .maxinterval = cpu_to_le32(duration), + .bss_idx = vif->mt76.idx, + .control_channel = chan->hw_value, + .bw = CMD_CBW_20MHZ, + .bw_from_ap = CMD_CBW_20MHZ, + .center_chan = center_ch, + .center_chan_from_ap = center_ch, + .dbdcband = 0xff, /* auto */ + }, + }; + + if (chan->hw_value < center_ch) + req.roc.sco = 1; /* SCA */ + else if (chan->hw_value > center_ch) + req.roc.sco = 3; /* SCB */ + + switch (chan->band) { + case NL80211_BAND_6GHZ: + req.roc.band = 3; + break; + case NL80211_BAND_5GHZ: + req.roc.band = 2; + break; + default: + req.roc.band = 1; + break; + } + + return mt76_mcu_send_msg(&dev->mt76, MCU_UNI_CMD(ROC), + &req, sizeof(req), false); +} + +int mt7921_mcu_abort_roc(struct mt7921_phy *phy, struct mt7921_vif *vif, + u8 token_id) +{ + struct mt7921_dev *dev = phy->dev; + struct { + struct { + u8 rsv[4]; + } __packed hdr; + struct roc_abort_tlv { + __le16 tag; + __le16 len; + u8 bss_idx; + u8 tokenid; + u8 dbdcband; + u8 rsv[5]; + } __packed abort; + } __packed req = { + .abort = { + .tag = cpu_to_le16(UNI_ROC_ABORT), + .len = cpu_to_le16(sizeof(struct roc_abort_tlv)), + .tokenid = token_id, + .bss_idx = vif->mt76.idx, + .dbdcband = 0xff, /* auto*/ + }, + }; + + return mt76_mcu_send_msg(&dev->mt76, MCU_UNI_CMD(ROC), + &req, sizeof(req), false); +} + int mt7921_mcu_set_chan_info(struct mt7921_phy *phy, int cmd) { struct mt7921_dev *dev = phy->dev; diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/mt7921.h b/drivers/net/wireless/mediatek/mt76/mt7921/mt7921.h index eaba114a9c7e..d9d78f6b088e 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/mt7921.h +++ b/drivers/net/wireless/mediatek/mt76/mt7921/mt7921.h @@ -53,6 +53,20 @@ #define MT7921_SDIO_HDR_TX_BYTES GENMASK(15, 0) #define MT7921_SDIO_HDR_PKT_TYPE GENMASK(17, 16) +#define MCU_UNI_EVENT_ROC 0x27 + +enum { + UNI_ROC_ACQUIRE, + UNI_ROC_ABORT, + UNI_ROC_NUM +}; + +enum mt7921_roc_req { + MT7921_ROC_REQ_JOIN, + MT7921_ROC_REQ_ROC, + MT7921_ROC_REQ_NUM +}; + enum mt7921_sdio_pkt_type { MT7921_SDIO_TXD, MT7921_SDIO_DATA, @@ -508,4 +522,9 @@ int mt7921_set_tx_sar_pwr(struct ieee80211_hw *hw, int mt7921_mcu_set_clc(struct mt7921_dev *dev, u8 *alpha2, enum environment_cap env_cap); +int mt7921_mcu_set_roc(struct mt7921_phy *phy, struct mt7921_vif *vif, + struct ieee80211_channel *chan, int duration, + enum mt7921_roc_req type, u8 token_id); +int mt7921_mcu_abort_roc(struct mt7921_phy *phy, struct mt7921_vif *vif, + u8 token_id); #endif From 98678fc03c9599be3bac7a1e253d2697b3a26624 Mon Sep 17 00:00:00 2001 From: Sean Wang Date: Fri, 11 Nov 2022 05:23:46 +0800 Subject: [PATCH 1484/2002] wifi: mt76: mt7921: drop ieee80211_[start, stop]_queues in driver The firmware would be in charge of braking and continuing the traffic while the channel contexts are switching between different BSS and HW SCAN in the background. Signed-off-by: Sean Wang Signed-off-by: Felix Fietkau --- .../net/wireless/mediatek/mt76/mt7921/mcu.c | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7921/mcu.c index 2b36c5f6eb40..7a74abecb269 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt7921/mcu.c @@ -199,20 +199,6 @@ mt7921_mcu_connection_loss_event(struct mt7921_dev *dev, struct sk_buff *skb) mt7921_mcu_connection_loss_iter, event); } -static void -mt7921_mcu_bss_event(struct mt7921_dev *dev, struct sk_buff *skb) -{ - struct mt76_phy *mphy = &dev->mt76.phy; - struct mt76_connac_mcu_bss_event *event; - - skb_pull(skb, sizeof(struct mt76_connac2_mcu_rxd)); - event = (struct mt76_connac_mcu_bss_event *)skb->data; - if (event->is_absent) - ieee80211_stop_queues(mphy->hw); - else - ieee80211_wake_queues(mphy->hw); -} - static void mt7921_mcu_debug_msg_event(struct mt7921_dev *dev, struct sk_buff *skb) { @@ -279,9 +265,6 @@ mt7921_mcu_rx_unsolicited_event(struct mt7921_dev *dev, struct sk_buff *skb) case MCU_EVENT_SCAN_DONE: mt7921_mcu_scan_event(dev, skb); return; - case MCU_EVENT_BSS_ABSENCE: - mt7921_mcu_bss_event(dev, skb); - break; case MCU_EVENT_DBG_MSG: mt7921_mcu_debug_msg_event(dev, skb); break; @@ -341,7 +324,6 @@ void mt7921_mcu_rx_event(struct mt7921_dev *dev, struct sk_buff *skb) if (rxd->ext_eid == MCU_EXT_EVENT_RATE_REPORT || rxd->eid == MCU_EVENT_BSS_BEACON_LOSS || rxd->eid == MCU_EVENT_SCHED_SCAN_DONE || - rxd->eid == MCU_EVENT_BSS_ABSENCE || rxd->eid == MCU_EVENT_SCAN_DONE || rxd->eid == MCU_EVENT_TX_DONE || rxd->eid == MCU_EVENT_DBG_MSG || From fe62788b6233df0fe43920e32f30c9583d584117 Mon Sep 17 00:00:00 2001 From: Sean Wang Date: Fri, 11 Nov 2022 05:23:47 +0800 Subject: [PATCH 1485/2002] wifi: mt76: connac: accept hw scan request at a time The current hw scan cannot accept multiple hw scan requests at a time, we return the proper error code when the hw scan is still undergoing. Signed-off-by: Sean Wang Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c index 92855d8264d1..6b2ce5fd3e4f 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c @@ -1571,6 +1571,9 @@ int mt76_connac_mcu_hw_scan(struct mt76_phy *phy, struct ieee80211_vif *vif, struct mt76_connac_hw_scan_req *req; struct sk_buff *skb; + if (test_bit(MT76_HW_SCANNING, &phy->state)) + return -EBUSY; + skb = mt76_mcu_msg_alloc(mdev, NULL, sizeof(*req)); if (!skb) return -ENOMEM; From 034ae28b56f13dc1f2beb3fa294b455f57ede9cb Mon Sep 17 00:00:00 2001 From: Sean Wang Date: Tue, 15 Nov 2022 08:11:23 +0800 Subject: [PATCH 1486/2002] wifi: mt76: mt7921: introduce remain_on_channel support Introduce remain_on_channel support. Additionally, we add mt7921_check_offload_capability to disable .remain_on_channel and .cancel_remain_on_channel and related configuration because those operations would rely on the fundamental MCU commands that will be only supported with newer firmware. Co-developed-by: Deren Wu Signed-off-by: Deren Wu Signed-off-by: Sean Wang Signed-off-by: Felix Fietkau --- .../net/wireless/mediatek/mt76/mt7921/init.c | 63 ++++++++++ .../net/wireless/mediatek/mt76/mt7921/main.c | 112 ++++++++++++++++++ .../net/wireless/mediatek/mt76/mt7921/mcu.c | 24 ++++ .../wireless/mediatek/mt76/mt7921/mt7921.h | 48 ++++++++ .../net/wireless/mediatek/mt76/mt7921/pci.c | 33 ++++-- .../net/wireless/mediatek/mt76/mt7921/sdio.c | 23 +++- .../net/wireless/mediatek/mt76/mt7921/usb.c | 12 +- 7 files changed, 303 insertions(+), 12 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/init.c b/drivers/net/wireless/mediatek/mt76/mt7921/init.c index dcdb3cf04ac1..e9353e9929a9 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/init.c +++ b/drivers/net/wireless/mediatek/mt76/mt7921/init.c @@ -2,6 +2,7 @@ /* Copyright (C) 2020 MediaTek Inc. */ #include +#include #include "mt7921.h" #include "mac.h" #include "mcu.h" @@ -65,12 +66,18 @@ mt7921_init_wiphy(struct ieee80211_hw *hw) hw->sta_data_size = sizeof(struct mt7921_sta); hw->vif_data_size = sizeof(struct mt7921_vif); + if (dev->fw_features & MT7921_FW_CAP_CNM) + wiphy->flags |= WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL; + else + wiphy->flags &= ~WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL; + wiphy->iface_combinations = if_comb; wiphy->flags &= ~(WIPHY_FLAG_IBSS_RSN | WIPHY_FLAG_4ADDR_AP | WIPHY_FLAG_4ADDR_STATION); wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_AP); wiphy->n_iface_combinations = ARRAY_SIZE(if_comb); + wiphy->max_remain_on_channel_duration = 5000; wiphy->max_scan_ie_len = MT76_CONNAC_SCAN_IE_LEN; wiphy->max_scan_ssids = 4; wiphy->max_sched_scan_plan_interval = @@ -129,6 +136,58 @@ mt7921_mac_init_band(struct mt7921_dev *dev, u8 band) mt76_clear(dev, MT_DMA_DCR0(band), MT_DMA_DCR0_RXD_G5_EN); } +u8 mt7921_check_offload_capability(struct device *dev, const char *fw_wm) +{ + struct mt7921_fw_features *features = NULL; + const struct mt76_connac2_fw_trailer *hdr; + struct mt7921_realease_info *rel_info; + const struct firmware *fw; + int ret, i, offset = 0; + const u8 *data, *end; + + ret = request_firmware(&fw, fw_wm, dev); + if (ret) + return ret; + + if (!fw || !fw->data || fw->size < sizeof(*hdr)) { + dev_err(dev, "Invalid firmware\n"); + return -EINVAL; + } + + data = fw->data; + hdr = (const void *)(fw->data + fw->size - sizeof(*hdr)); + + for (i = 0; i < hdr->n_region; i++) { + const struct mt76_connac2_fw_region *region; + + region = (const void *)((const u8 *)hdr - + (hdr->n_region - i) * sizeof(*region)); + offset += le32_to_cpu(region->len); + } + + data += offset + 16; + rel_info = (struct mt7921_realease_info *)data; + data += sizeof(*rel_info); + end = data + le16_to_cpu(rel_info->len); + + while (data < end) { + rel_info = (struct mt7921_realease_info *)data; + data += sizeof(*rel_info); + + if (rel_info->tag == MT7921_FW_TAG_FEATURE) { + features = (struct mt7921_fw_features *)data; + break; + } + + data += le16_to_cpu(rel_info->len) + rel_info->pad_len; + } + + release_firmware(fw); + + return features ? features->data : 0; +} +EXPORT_SYMBOL_GPL(mt7921_check_offload_capability); + int mt7921_mac_init(struct mt7921_dev *dev) { int i; @@ -278,6 +337,10 @@ int mt7921_register_device(struct mt7921_dev *dev) INIT_WORK(&dev->reset_work, mt7921_mac_reset_work); INIT_WORK(&dev->init_work, mt7921_init_work); + INIT_WORK(&dev->phy.roc_work, mt7921_roc_work); + timer_setup(&dev->phy.roc_timer, mt7921_roc_timer, 0); + init_waitqueue_head(&dev->phy.roc_wait); + dev->pm.idle_timeout = MT7921_PM_TIMEOUT; dev->pm.stats.last_wake_event = jiffies; dev->pm.stats.last_doze_event = jiffies; diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/main.c b/drivers/net/wireless/mediatek/mt76/mt7921/main.c index cc3365c4f8c5..012c92d49f49 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7921/main.c @@ -386,6 +386,116 @@ static void mt7921_remove_interface(struct ieee80211_hw *hw, mt76_packet_id_flush(&dev->mt76, &msta->wcid); } +static void mt7921_roc_iter(void *priv, u8 *mac, + struct ieee80211_vif *vif) +{ + struct mt7921_vif *mvif = (struct mt7921_vif *)vif->drv_priv; + struct mt7921_phy *phy = priv; + + mt7921_mcu_abort_roc(phy, mvif, phy->roc_token_id); +} + +void mt7921_roc_work(struct work_struct *work) +{ + struct mt7921_phy *phy; + + phy = (struct mt7921_phy *)container_of(work, struct mt7921_phy, + roc_work); + + if (!test_and_clear_bit(MT76_STATE_ROC, &phy->mt76->state)) + return; + + mt7921_mutex_acquire(phy->dev); + ieee80211_iterate_active_interfaces(phy->mt76->hw, + IEEE80211_IFACE_ITER_RESUME_ALL, + mt7921_roc_iter, phy); + mt7921_mutex_release(phy->dev); + ieee80211_remain_on_channel_expired(phy->mt76->hw); +} + +void mt7921_roc_timer(struct timer_list *timer) +{ + struct mt7921_phy *phy = from_timer(phy, timer, roc_timer); + + ieee80211_queue_work(phy->mt76->hw, &phy->roc_work); +} + +static int mt7921_abort_roc(struct mt7921_phy *phy, struct mt7921_vif *vif) +{ + int err; + + if (!test_and_clear_bit(MT76_STATE_ROC, &phy->mt76->state)) + return 0; + + del_timer_sync(&phy->roc_timer); + cancel_work_sync(&phy->roc_work); + err = mt7921_mcu_abort_roc(phy, vif, phy->roc_token_id); + clear_bit(MT76_STATE_ROC, &phy->mt76->state); + + return err; +} + +static int mt7921_set_roc(struct mt7921_phy *phy, + struct mt7921_vif *vif, + struct ieee80211_channel *chan, + int duration, + enum mt7921_roc_req type) +{ + int err; + + if (test_and_set_bit(MT76_STATE_ROC, &phy->mt76->state)) + return -EBUSY; + + phy->roc_grant = false; + + err = mt7921_mcu_set_roc(phy, vif, chan, duration, type, + ++phy->roc_token_id); + if (err < 0) { + clear_bit(MT76_STATE_ROC, &phy->mt76->state); + goto out; + } + + if (!wait_event_timeout(phy->roc_wait, phy->roc_grant, HZ)) { + mt7921_mcu_abort_roc(phy, vif, phy->roc_token_id); + clear_bit(MT76_STATE_ROC, &phy->mt76->state); + err = -ETIMEDOUT; + } + +out: + return err; +} + +static int mt7921_remain_on_channel(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_channel *chan, + int duration, + enum ieee80211_roc_type type) +{ + struct mt7921_vif *mvif = (struct mt7921_vif *)vif->drv_priv; + struct mt7921_phy *phy = mt7921_hw_phy(hw); + int err; + + mt7921_mutex_acquire(phy->dev); + err = mt7921_set_roc(phy, mvif, chan, duration, MT7921_ROC_REQ_ROC); + mt7921_mutex_release(phy->dev); + + return err; +} + +static int mt7921_cancel_remain_on_channel(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) +{ + struct mt7921_vif *mvif = (struct mt7921_vif *)vif->drv_priv; + struct mt7921_phy *phy = mt7921_hw_phy(hw); + int err; + + mt7921_mutex_acquire(phy->dev); + err = mt7921_abort_roc(phy, mvif); + mt7921_mutex_release(phy->dev); + + return err; +} + static int mt7921_set_channel(struct mt7921_phy *phy) { struct mt7921_dev *dev = phy->dev; @@ -1623,6 +1733,8 @@ const struct ieee80211_ops mt7921_ops = { #endif /* CONFIG_PM */ .flush = mt7921_flush, .set_sar_specs = mt7921_set_sar_specs, + .remain_on_channel = mt7921_remain_on_channel, + .cancel_remain_on_channel = mt7921_cancel_remain_on_channel, }; EXPORT_SYMBOL_GPL(mt7921_ops); diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7921/mcu.c index 7a74abecb269..fb9c0f66cb27 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt7921/mcu.c @@ -154,6 +154,29 @@ void mt7921_mcu_set_suspend_iter(void *priv, u8 *mac, struct ieee80211_vif *vif) #endif /* CONFIG_PM */ +static void +mt7921_mcu_uni_roc_event(struct mt7921_dev *dev, struct sk_buff *skb) +{ + struct mt7921_roc_grant_tlv *grant; + struct mt76_connac2_mcu_rxd *rxd; + int duration; + + rxd = (struct mt76_connac2_mcu_rxd *)skb->data; + grant = (struct mt7921_roc_grant_tlv *)(rxd->tlv + 4); + + /* should never happen */ + WARN_ON_ONCE((le16_to_cpu(grant->tag) != UNI_EVENT_ROC_GRANT)); + + if (grant->reqtype == MT7921_ROC_REQ_ROC) + ieee80211_ready_on_channel(dev->mt76.phy.hw); + + dev->phy.roc_grant = true; + wake_up(&dev->phy.roc_wait); + duration = le32_to_cpu(grant->max_interval); + mod_timer(&dev->phy.roc_timer, + round_jiffies_up(jiffies + msecs_to_jiffies(duration))); +} + static void mt7921_mcu_scan_event(struct mt7921_dev *dev, struct sk_buff *skb) { @@ -295,6 +318,7 @@ mt7921_mcu_uni_rx_unsolicited_event(struct mt7921_dev *dev, switch (rxd->eid) { case MCU_UNI_EVENT_ROC: + mt7921_mcu_uni_roc_event(dev, skb); break; default: break; diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/mt7921.h b/drivers/net/wireless/mediatek/mt76/mt7921/mt7921.h index d9d78f6b088e..4f0147e40bc3 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/mt7921.h +++ b/drivers/net/wireless/mediatek/mt76/mt7921/mt7921.h @@ -32,6 +32,9 @@ #define MT7921_MCU_INIT_RETRY_COUNT 10 #define MT7921_WFSYS_INIT_RETRY_COUNT 2 +#define MT7921_FW_TAG_FEATURE 4 +#define MT7921_FW_CAP_CNM BIT(7) + #define MT7921_FIRMWARE_WM "mediatek/WIFI_RAM_CODE_MT7961_1.bin" #define MT7921_ROM_PATCH "mediatek/WIFI_MT7961_patch_mcu_1_2_hdr.bin" @@ -67,6 +70,41 @@ enum mt7921_roc_req { MT7921_ROC_REQ_NUM }; +enum { + UNI_EVENT_ROC_GRANT = 0, + UNI_EVENT_ROC_TAG_NUM +}; + +struct mt7921_realease_info { + __le16 len; + u8 pad_len; + u8 tag; +} __packed; + +struct mt7921_fw_features { + u8 segment; + u8 data; + u8 rsv[14]; +} __packed; + +struct mt7921_roc_grant_tlv { + __le16 tag; + __le16 len; + u8 bss_idx; + u8 tokenid; + u8 status; + u8 primarychannel; + u8 rfsco; + u8 rfband; + u8 channelwidth; + u8 centerfreqseg1; + u8 centerfreqseg2; + u8 reqtype; + u8 dbdcband; + u8 rsv[1]; + __le32 max_interval; +} __packed; + enum mt7921_sdio_pkt_type { MT7921_SDIO_TXD, MT7921_SDIO_DATA, @@ -214,6 +252,12 @@ struct mt7921_phy { #endif struct mt7921_clc *clc[MT7921_CLC_MAX_NUM]; + + struct work_struct roc_work; + struct timer_list roc_timer; + wait_queue_head_t roc_wait; + u8 roc_token_id; + bool roc_grant; }; #define mt7921_init_reset(dev) ((dev)->hif_ops->init_reset(dev)) @@ -250,6 +294,7 @@ struct mt7921_dev { struct work_struct init_work; u8 fw_debug; + u8 fw_features; struct mt76_connac_pm pm; struct mt76_connac_coredump coredump; @@ -439,6 +484,8 @@ int mt7921_mcu_uni_rx_ba(struct mt7921_dev *dev, struct ieee80211_ampdu_params *params, bool enable); void mt7921_scan_work(struct work_struct *work); +void mt7921_roc_work(struct work_struct *work); +void mt7921_roc_timer(struct timer_list *timer); int mt7921_mcu_uni_bss_ps(struct mt7921_dev *dev, struct ieee80211_vif *vif); int mt7921_mcu_drv_pmctrl(struct mt7921_dev *dev); int mt7921_mcu_fw_pmctrl(struct mt7921_dev *dev); @@ -527,4 +574,5 @@ int mt7921_mcu_set_roc(struct mt7921_phy *phy, struct mt7921_vif *vif, enum mt7921_roc_req type, u8 token_id); int mt7921_mcu_abort_roc(struct mt7921_phy *phy, struct mt7921_vif *vif, u8 token_id); +u8 mt7921_check_offload_capability(struct device *dev, const char *fw_wm); #endif diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/pci.c b/drivers/net/wireless/mediatek/mt76/mt7921/pci.c index b38d119b2ea9..32ad26ef3a68 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/pci.c +++ b/drivers/net/wireless/mediatek/mt76/mt7921/pci.c @@ -13,10 +13,14 @@ #include "../trace.h" static const struct pci_device_id mt7921_pci_device_table[] = { - { PCI_DEVICE(PCI_VENDOR_ID_MEDIATEK, 0x7961) }, - { PCI_DEVICE(PCI_VENDOR_ID_MEDIATEK, 0x7922) }, - { PCI_DEVICE(PCI_VENDOR_ID_MEDIATEK, 0x0608) }, - { PCI_DEVICE(PCI_VENDOR_ID_MEDIATEK, 0x0616) }, + { PCI_DEVICE(PCI_VENDOR_ID_MEDIATEK, 0x7961), + .driver_data = (kernel_ulong_t)MT7921_FIRMWARE_WM }, + { PCI_DEVICE(PCI_VENDOR_ID_MEDIATEK, 0x7922), + .driver_data = (kernel_ulong_t)MT7922_FIRMWARE_WM }, + { PCI_DEVICE(PCI_VENDOR_ID_MEDIATEK, 0x0608), + .driver_data = (kernel_ulong_t)MT7921_FIRMWARE_WM }, + { PCI_DEVICE(PCI_VENDOR_ID_MEDIATEK, 0x0616), + .driver_data = (kernel_ulong_t)MT7921_FIRMWARE_WM }, { }, }; @@ -253,9 +257,11 @@ static int mt7921_pci_probe(struct pci_dev *pdev, .fw_own = mt7921e_mcu_fw_pmctrl, }; + struct ieee80211_ops *ops; struct mt76_bus_ops *bus_ops; struct mt7921_dev *dev; struct mt76_dev *mdev; + u8 features; int ret; ret = pcim_enable_device(pdev); @@ -279,8 +285,21 @@ static int mt7921_pci_probe(struct pci_dev *pdev, if (mt7921_disable_aspm) mt76_pci_disable_aspm(pdev); - mdev = mt76_alloc_device(&pdev->dev, sizeof(*dev), &mt7921_ops, - &drv_ops); + features = mt7921_check_offload_capability(&pdev->dev, (const char *) + id->driver_data); + ops = devm_kmemdup(&pdev->dev, &mt7921_ops, sizeof(mt7921_ops), + GFP_KERNEL); + if (!ops) { + ret = -ENOMEM; + goto err_free_pci_vec; + } + + if (!(features & MT7921_FW_CAP_CNM)) { + ops->remain_on_channel = NULL; + ops->cancel_remain_on_channel = NULL; + } + + mdev = mt76_alloc_device(&pdev->dev, sizeof(*dev), ops, &drv_ops); if (!mdev) { ret = -ENOMEM; goto err_free_pci_vec; @@ -289,8 +308,8 @@ static int mt7921_pci_probe(struct pci_dev *pdev, pci_set_drvdata(pdev, mdev); dev = container_of(mdev, struct mt7921_dev, mt76); + dev->fw_features = features; dev->hif_ops = &mt7921_pcie_ops; - mt76_mmio_init(&dev->mt76, pcim_iomap_table(pdev)[0]); tasklet_init(&dev->irq_tasklet, mt7921_irq_tasklet, (unsigned long)dev); diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/sdio.c b/drivers/net/wireless/mediatek/mt76/mt7921/sdio.c index 377ca5fa3f6e..e07a8fb87421 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/sdio.c +++ b/drivers/net/wireless/mediatek/mt76/mt7921/sdio.c @@ -17,7 +17,8 @@ #include "mcu.h" static const struct sdio_device_id mt7921s_table[] = { - { SDIO_DEVICE(SDIO_VENDOR_ID_MEDIATEK, 0x7901) }, + { SDIO_DEVICE(SDIO_VENDOR_ID_MEDIATEK, 0x7901), + .driver_data = (kernel_ulong_t)MT7921_FIRMWARE_WM }, { } /* Terminating entry */ }; @@ -122,18 +123,32 @@ static int mt7921s_probe(struct sdio_func *func, .fw_own = mt7921s_mcu_fw_pmctrl, }; + struct ieee80211_ops *ops; struct mt7921_dev *dev; struct mt76_dev *mdev; + u8 features; int ret; - mdev = mt76_alloc_device(&func->dev, sizeof(*dev), &mt7921_ops, - &drv_ops); + features = mt7921_check_offload_capability(&func->dev, (const char *) + id->driver_data); + + ops = devm_kmemdup(&func->dev, &mt7921_ops, sizeof(mt7921_ops), + GFP_KERNEL); + if (!ops) + return -ENOMEM; + + if (!(features & MT7921_FW_CAP_CNM)) { + ops->remain_on_channel = NULL; + ops->cancel_remain_on_channel = NULL; + } + + mdev = mt76_alloc_device(&func->dev, sizeof(*dev), ops, &drv_ops); if (!mdev) return -ENOMEM; dev = container_of(mdev, struct mt7921_dev, mt76); + dev->fw_features = features; dev->hif_ops = &mt7921_sdio_ops; - sdio_set_drvdata(func, dev); ret = mt76s_init(mdev, func, &mt7921s_ops); diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/usb.c b/drivers/net/wireless/mediatek/mt76/mt7921/usb.c index 89249f0b6aba..8a49d3de2073 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/usb.c +++ b/drivers/net/wireless/mediatek/mt76/mt7921/usb.c @@ -13,7 +13,8 @@ #include "mac.h" static const struct usb_device_id mt7921u_device_table[] = { - { USB_DEVICE_AND_INTERFACE_INFO(0x0e8d, 0x7961, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(0x0e8d, 0x7961, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)MT7921_FIRMWARE_WM }, { }, }; @@ -204,13 +205,21 @@ static int mt7921u_probe(struct usb_interface *usb_intf, struct ieee80211_hw *hw; struct mt7921_dev *dev; struct mt76_dev *mdev; + u8 features; int ret; + features = mt7921_check_offload_capability(&usb_intf->dev, (const char *) + id->driver_info); ops = devm_kmemdup(&usb_intf->dev, &mt7921_ops, sizeof(mt7921_ops), GFP_KERNEL); if (!ops) return -ENOMEM; + if (!(features & MT7921_FW_CAP_CNM)) { + ops->remain_on_channel = NULL; + ops->cancel_remain_on_channel = NULL; + } + ops->stop = mt7921u_stop; mdev = mt76_alloc_device(&usb_intf->dev, sizeof(*dev), ops, &drv_ops); @@ -218,6 +227,7 @@ static int mt7921u_probe(struct usb_interface *usb_intf, return -ENOMEM; dev = container_of(mdev, struct mt7921_dev, mt76); + dev->fw_features = features; dev->hif_ops = &hif_ops; udev = usb_get_dev(udev); From 41ac53c899bd1493a75ab9b52c8f76176b7419e1 Mon Sep 17 00:00:00 2001 From: Sean Wang Date: Tue, 15 Nov 2022 08:11:24 +0800 Subject: [PATCH 1487/2002] wifi: mt76: mt7921: introduce chanctx support The firmware can have the capability to manage the channel context scheduling on multiple roles running on the device including Station, AP and P2P GC/GO mode (will be extended based on the future patchset) to help users sharing the network with others on a single device. The firmware is able to support the channel chanctx up to 2 interface simultaneously running on the different channels. Another thing to be noted is that before the driver is going sent out the management frames, the driver has to get the privilege from the firmware to occupy the current channel context until the frame handshake is completed and then get the privilege back to the firmware. Co-developed-by: Deren Wu Signed-off-by: Deren Wu Signed-off-by: Sean Wang Signed-off-by: Felix Fietkau --- .../net/wireless/mediatek/mt76/mt7921/init.c | 33 +++++- .../net/wireless/mediatek/mt76/mt7921/mac.c | 8 -- .../net/wireless/mediatek/mt76/mt7921/main.c | 111 +++++++++++++++++- .../wireless/mediatek/mt76/mt7921/mt7921.h | 1 + .../net/wireless/mediatek/mt76/mt7921/pci.c | 7 ++ .../net/wireless/mediatek/mt76/mt7921/sdio.c | 7 ++ .../net/wireless/mediatek/mt76/mt7921/usb.c | 7 ++ 7 files changed, 157 insertions(+), 17 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/init.c b/drivers/net/wireless/mediatek/mt76/mt7921/init.c index e9353e9929a9..79b8055ce4c4 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/init.c +++ b/drivers/net/wireless/mediatek/mt76/mt7921/init.c @@ -26,6 +26,27 @@ static const struct ieee80211_iface_combination if_comb[] = { .max_interfaces = MT7921_MAX_INTERFACES, .num_different_channels = 1, .beacon_int_infra_match = true, + }, +}; + +static const struct ieee80211_iface_limit if_limits_chanctx[] = { + { + .max = 2, + .types = BIT(NL80211_IFTYPE_STATION), + }, + { + .max = 1, + .types = BIT(NL80211_IFTYPE_AP), + } +}; + +static const struct ieee80211_iface_combination if_comb_chanctx[] = { + { + .limits = if_limits_chanctx, + .n_limits = ARRAY_SIZE(if_limits_chanctx), + .max_interfaces = 2, + .num_different_channels = 2, + .beacon_int_infra_match = false, } }; @@ -66,17 +87,19 @@ mt7921_init_wiphy(struct ieee80211_hw *hw) hw->sta_data_size = sizeof(struct mt7921_sta); hw->vif_data_size = sizeof(struct mt7921_vif); - if (dev->fw_features & MT7921_FW_CAP_CNM) + if (dev->fw_features & MT7921_FW_CAP_CNM) { wiphy->flags |= WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL; - else + wiphy->iface_combinations = if_comb_chanctx; + wiphy->n_iface_combinations = ARRAY_SIZE(if_comb_chanctx); + } else { wiphy->flags &= ~WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL; - - wiphy->iface_combinations = if_comb; + wiphy->iface_combinations = if_comb; + wiphy->n_iface_combinations = ARRAY_SIZE(if_comb); + } wiphy->flags &= ~(WIPHY_FLAG_IBSS_RSN | WIPHY_FLAG_4ADDR_AP | WIPHY_FLAG_4ADDR_STATION); wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_AP); - wiphy->n_iface_combinations = ARRAY_SIZE(if_comb); wiphy->max_remain_on_channel_duration = 5000; wiphy->max_scan_ie_len = MT76_CONNAC_SCAN_IE_LEN; wiphy->max_scan_ssids = 4; diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/mac.c b/drivers/net/wireless/mediatek/mt76/mt7921/mac.c index eeab756240a2..443a4a49bc9e 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7921/mac.c @@ -168,14 +168,6 @@ static void mt7921_get_status_freq_info(struct mt7921_dev *dev, struct mt76_phy *mphy, struct mt76_rx_status *status, u8 chfreq) { - if (!test_bit(MT76_HW_SCANNING, &mphy->state) && - !test_bit(MT76_HW_SCHED_SCANNING, &mphy->state) && - !test_bit(MT76_STATE_ROC, &mphy->state)) { - status->freq = mphy->chandef.chan->center_freq; - status->band = mphy->chandef.chan->band; - return; - } - if (chfreq > 180) { status->band = NL80211_BAND_6GHZ; chfreq = (chfreq - 181) * 4 + 1; diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/main.c b/drivers/net/wireless/mediatek/mt76/mt7921/main.c index 012c92d49f49..41df17efdb3a 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7921/main.c @@ -858,7 +858,7 @@ void mt7921_mac_sta_assoc(struct mt76_dev *mdev, struct ieee80211_vif *vif, if (vif->type == NL80211_IFTYPE_STATION && !sta->tdls) mt76_connac_mcu_uni_add_bss(&dev->mphy, vif, &mvif->sta.wcid, - true, NULL); + true, mvif->ctx); mt7921_mac_wtbl_update(dev, msta->wcid.idx, MT_WTBL_UPDATE_ADM_COUNT_CLEAR); @@ -891,7 +891,7 @@ void mt7921_mac_sta_remove(struct mt76_dev *mdev, struct ieee80211_vif *vif, if (!sta->tdls) mt76_connac_mcu_uni_add_bss(&dev->mphy, vif, &mvif->sta.wcid, false, - NULL); + mvif->ctx); } spin_lock_bh(&dev->sta_poll_lock); @@ -1645,7 +1645,7 @@ mt7921_start_ap(struct ieee80211_hw *hw, struct ieee80211_vif *vif, mt7921_mutex_acquire(dev); err = mt76_connac_mcu_uni_add_bss(phy->mt76, vif, &mvif->sta.wcid, - true, NULL); + true, mvif->ctx); if (err) goto out; @@ -1677,12 +1677,108 @@ mt7921_stop_ap(struct ieee80211_hw *hw, struct ieee80211_vif *vif, goto out; mt76_connac_mcu_uni_add_bss(phy->mt76, vif, &mvif->sta.wcid, false, - NULL); + mvif->ctx); out: mt7921_mutex_release(dev); } +static int +mt7921_add_chanctx(struct ieee80211_hw *hw, + struct ieee80211_chanctx_conf *ctx) +{ + return 0; +} + +static void +mt7921_remove_chanctx(struct ieee80211_hw *hw, + struct ieee80211_chanctx_conf *ctx) +{ +} + +static void mt7921_ctx_iter(void *priv, u8 *mac, + struct ieee80211_vif *vif) +{ + struct mt7921_vif *mvif = (struct mt7921_vif *)vif->drv_priv; + struct ieee80211_chanctx_conf *ctx = priv; + + if (ctx != mvif->ctx) + return; + + mt76_connac_mcu_uni_set_chctx(mvif->phy->mt76, &mvif->mt76, ctx); +} + +static void +mt7921_change_chanctx(struct ieee80211_hw *hw, + struct ieee80211_chanctx_conf *ctx, + u32 changed) +{ + struct mt7921_phy *phy = mt7921_hw_phy(hw); + + mt7921_mutex_acquire(phy->dev); + ieee80211_iterate_active_interfaces(phy->mt76->hw, + IEEE80211_IFACE_ITER_ACTIVE, + mt7921_ctx_iter, ctx); + mt7921_mutex_release(phy->dev); +} + +static int +mt7921_assign_vif_chanctx(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *link_conf, + struct ieee80211_chanctx_conf *ctx) +{ + struct mt7921_vif *mvif = (struct mt7921_vif *)vif->drv_priv; + struct mt7921_dev *dev = mt7921_hw_dev(hw); + + mutex_lock(&dev->mt76.mutex); + mvif->ctx = ctx; + mutex_unlock(&dev->mt76.mutex); + + return 0; +} + +static void +mt7921_unassign_vif_chanctx(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *link_conf, + struct ieee80211_chanctx_conf *ctx) +{ + struct mt7921_vif *mvif = (struct mt7921_vif *)vif->drv_priv; + struct mt7921_dev *dev = mt7921_hw_dev(hw); + + mutex_lock(&dev->mt76.mutex); + mvif->ctx = NULL; + mutex_unlock(&dev->mt76.mutex); +} + +static void mt7921_mgd_prepare_tx(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_prep_tx_info *info) +{ + struct mt7921_vif *mvif = (struct mt7921_vif *)vif->drv_priv; + struct mt7921_dev *dev = mt7921_hw_dev(hw); + u16 duration = info->duration ? info->duration : + jiffies_to_msecs(HZ); + + mt7921_mutex_acquire(dev); + mt7921_set_roc(mvif->phy, mvif, mvif->ctx->def.chan, duration, + MT7921_ROC_REQ_JOIN); + mt7921_mutex_release(dev); +} + +static void mt7921_mgd_complete_tx(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_prep_tx_info *info) +{ + struct mt7921_vif *mvif = (struct mt7921_vif *)vif->drv_priv; + struct mt7921_dev *dev = mt7921_hw_dev(hw); + + mt7921_mutex_acquire(dev); + mt7921_abort_roc(mvif->phy, mvif); + mt7921_mutex_release(dev); +} + const struct ieee80211_ops mt7921_ops = { .tx = mt7921_tx, .start = mt7921_start, @@ -1735,6 +1831,13 @@ const struct ieee80211_ops mt7921_ops = { .set_sar_specs = mt7921_set_sar_specs, .remain_on_channel = mt7921_remain_on_channel, .cancel_remain_on_channel = mt7921_cancel_remain_on_channel, + .add_chanctx = mt7921_add_chanctx, + .remove_chanctx = mt7921_remove_chanctx, + .change_chanctx = mt7921_change_chanctx, + .assign_vif_chanctx = mt7921_assign_vif_chanctx, + .unassign_vif_chanctx = mt7921_unassign_vif_chanctx, + .mgd_prepare_tx = mt7921_mgd_prepare_tx, + .mgd_complete_tx = mt7921_mgd_complete_tx, }; EXPORT_SYMBOL_GPL(mt7921_ops); diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/mt7921.h b/drivers/net/wireless/mediatek/mt76/mt7921/mt7921.h index 4f0147e40bc3..ac70a978dbed 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/mt7921.h +++ b/drivers/net/wireless/mediatek/mt76/mt7921/mt7921.h @@ -171,6 +171,7 @@ struct mt7921_vif { struct ewma_rssi rssi; struct ieee80211_tx_queue_params queue_params[IEEE80211_NUM_ACS]; + struct ieee80211_chanctx_conf *ctx; }; struct mib_stats { diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/pci.c b/drivers/net/wireless/mediatek/mt76/mt7921/pci.c index 32ad26ef3a68..28342ec940f0 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/pci.c +++ b/drivers/net/wireless/mediatek/mt76/mt7921/pci.c @@ -297,6 +297,13 @@ static int mt7921_pci_probe(struct pci_dev *pdev, if (!(features & MT7921_FW_CAP_CNM)) { ops->remain_on_channel = NULL; ops->cancel_remain_on_channel = NULL; + ops->add_chanctx = NULL; + ops->remove_chanctx = NULL; + ops->change_chanctx = NULL; + ops->assign_vif_chanctx = NULL; + ops->unassign_vif_chanctx = NULL; + ops->mgd_prepare_tx = NULL; + ops->mgd_complete_tx = NULL; } mdev = mt76_alloc_device(&pdev->dev, sizeof(*dev), ops, &drv_ops); diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/sdio.c b/drivers/net/wireless/mediatek/mt76/mt7921/sdio.c index e07a8fb87421..8ce4252b8ae7 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/sdio.c +++ b/drivers/net/wireless/mediatek/mt76/mt7921/sdio.c @@ -140,6 +140,13 @@ static int mt7921s_probe(struct sdio_func *func, if (!(features & MT7921_FW_CAP_CNM)) { ops->remain_on_channel = NULL; ops->cancel_remain_on_channel = NULL; + ops->add_chanctx = NULL; + ops->remove_chanctx = NULL; + ops->change_chanctx = NULL; + ops->assign_vif_chanctx = NULL; + ops->unassign_vif_chanctx = NULL; + ops->mgd_prepare_tx = NULL; + ops->mgd_complete_tx = NULL; } mdev = mt76_alloc_device(&func->dev, sizeof(*dev), ops, &drv_ops); diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/usb.c b/drivers/net/wireless/mediatek/mt76/mt7921/usb.c index 8a49d3de2073..5321d20dcdcb 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/usb.c +++ b/drivers/net/wireless/mediatek/mt76/mt7921/usb.c @@ -218,6 +218,13 @@ static int mt7921u_probe(struct usb_interface *usb_intf, if (!(features & MT7921_FW_CAP_CNM)) { ops->remain_on_channel = NULL; ops->cancel_remain_on_channel = NULL; + ops->add_chanctx = NULL; + ops->remove_chanctx = NULL; + ops->change_chanctx = NULL; + ops->assign_vif_chanctx = NULL; + ops->unassign_vif_chanctx = NULL; + ops->mgd_prepare_tx = NULL; + ops->mgd_complete_tx = NULL; } ops->stop = mt7921u_stop; From 4c07129b05e42a0415ee409bf5958ac62e7e969e Mon Sep 17 00:00:00 2001 From: Shayne Chen Date: Thu, 10 Nov 2022 17:35:17 +0800 Subject: [PATCH 1488/2002] wifi: mt76: connac: rework macros for unified command Rework some macros for connac3 unified command. This is a preliminary patch to add mt7996 chipset support. Signed-off-by: Peter Chiu Signed-off-by: Shayne Chen Signed-off-by: Felix Fietkau --- .../wireless/mediatek/mt76/mt76_connac_mcu.h | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h index a4893331b7c3..d0a4331f6f63 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h +++ b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h @@ -63,7 +63,7 @@ struct mt76_connac2_mcu_txd { } __packed __aligned(4); /** - * struct mt76_connac2_mcu_uni_txd - mcu command descriptor for firmware v3 + * struct mt76_connac2_mcu_uni_txd - mcu command descriptor for connac2 and connac3 * @txd: hardware descriptor * @len: total length not including txd * @cid: command identifier @@ -1079,10 +1079,11 @@ enum { #define MCU_CMD_ACK BIT(0) #define MCU_CMD_UNI BIT(1) -#define MCU_CMD_QUERY BIT(2) +#define MCU_CMD_SET BIT(2) #define MCU_CMD_UNI_EXT_ACK (MCU_CMD_ACK | MCU_CMD_UNI | \ - MCU_CMD_QUERY) + MCU_CMD_SET) +#define MCU_CMD_UNI_QUERY_ACK (MCU_CMD_ACK | MCU_CMD_UNI) #define __MCU_CMD_FIELD_ID GENMASK(7, 0) #define __MCU_CMD_FIELD_EXT_ID GENMASK(15, 8) @@ -1090,6 +1091,7 @@ enum { #define __MCU_CMD_FIELD_UNI BIT(17) #define __MCU_CMD_FIELD_CE BIT(18) #define __MCU_CMD_FIELD_WA BIT(19) +#define __MCU_CMD_FIELD_WM BIT(20) #define MCU_CMD(_t) FIELD_PREP(__MCU_CMD_FIELD_ID, \ MCU_CMD_##_t) @@ -1111,6 +1113,16 @@ enum { FIELD_PREP(__MCU_CMD_FIELD_EXT_ID, \ MCU_WA_PARAM_CMD_##_t)) +#define MCU_WM_UNI_CMD(_t) (MCU_UNI_CMD(_t) | \ + __MCU_CMD_FIELD_WM) +#define MCU_WM_UNI_CMD_QUERY(_t) (MCU_UNI_CMD(_t) | \ + __MCU_CMD_FIELD_QUERY | \ + __MCU_CMD_FIELD_WM) +#define MCU_WA_UNI_CMD(_t) (MCU_UNI_CMD(_t) | \ + __MCU_CMD_FIELD_WA) +#define MCU_WMWA_UNI_CMD(_t) (MCU_WM_UNI_CMD(_t) | \ + __MCU_CMD_FIELD_WA) + enum { MCU_EXT_CMD_EFUSE_ACCESS = 0x01, MCU_EXT_CMD_RF_REG_ACCESS = 0x02, From 6deaf96ddd0b6894d7f36513d74635b40e337a26 Mon Sep 17 00:00:00 2001 From: Shayne Chen Date: Thu, 10 Nov 2022 17:35:18 +0800 Subject: [PATCH 1489/2002] wifi: mt76: connac: update struct sta_rec_phy For connac3 generation, max_ampdu_len should be filled in struct sta_rec_phy. This is a preliminary patch to add mt7996 chipset support. Signed-off-by: Peter Chiu Signed-off-by: Shayne Chen Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h index d0a4331f6f63..97965b03c5e2 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h +++ b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h @@ -403,7 +403,8 @@ struct sta_rec_phy { u8 ampdu; u8 rts_policy; u8 rcpi; - u8 rsv[2]; + u8 max_ampdu_len; /* connac3 */ + u8 rsv[1]; } __packed; struct sta_rec_he_6g_capa { From cade693944ea0799cb5f15168dcc4447401f7d9e Mon Sep 17 00:00:00 2001 From: Shayne Chen Date: Thu, 10 Nov 2022 17:35:19 +0800 Subject: [PATCH 1490/2002] wifi: mt76: connac: rework fields for larger bandwidth support in sta_rec_bf Rename nrow_bw160 and ncol_bw160 since channel width 320 MHz will share the same field. This is a preliminary patch to add mt7996 chipset support. Signed-off-by: MeiChia Chiu Signed-off-by: Shayne Chen Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h | 4 ++-- drivers/net/wireless/mediatek/mt76/mt7915/mcu.c | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h index 97965b03c5e2..d79fa68cf8b6 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h +++ b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h @@ -465,8 +465,8 @@ struct sta_rec_bf { u8 ibf_dbw; u8 ibf_ncol; u8 ibf_nrow; - u8 nrow_bw160; - u8 ncol_bw160; + u8 nrow_gt_bw80; + u8 ncol_gt_bw80; u8 ru_start_idx; u8 ru_end_idx; diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c index 17e13fd0ede9..8b903a7636a8 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c @@ -1131,7 +1131,7 @@ mt7915_mcu_sta_bfer_he(struct ieee80211_sta *sta, struct ieee80211_vif *vif, mcs_map = le16_to_cpu(pc->he_mcs_nss_supp.rx_mcs_160); nss_mcs = mt7915_mcu_get_sta_nss(mcs_map); - bf->ncol_bw160 = nss_mcs; + bf->ncol_gt_bw80 = nss_mcs; } if (pe->phy_cap_info[0] & @@ -1139,10 +1139,10 @@ mt7915_mcu_sta_bfer_he(struct ieee80211_sta *sta, struct ieee80211_vif *vif, mcs_map = le16_to_cpu(pc->he_mcs_nss_supp.rx_mcs_80p80); nss_mcs = mt7915_mcu_get_sta_nss(mcs_map); - if (bf->ncol_bw160) - bf->ncol_bw160 = min_t(u8, bf->ncol_bw160, nss_mcs); + if (bf->ncol_gt_bw80) + bf->ncol_gt_bw80 = min_t(u8, bf->ncol_gt_bw80, nss_mcs); else - bf->ncol_bw160 = nss_mcs; + bf->ncol_gt_bw80 = nss_mcs; } snd_dim = HE_PHY(CAP5_BEAMFORMEE_NUM_SND_DIM_ABOVE_80MHZ_MASK, @@ -1150,7 +1150,7 @@ mt7915_mcu_sta_bfer_he(struct ieee80211_sta *sta, struct ieee80211_vif *vif, sts = HE_PHY(CAP4_BEAMFORMEE_MAX_STS_ABOVE_80MHZ_MASK, pe->phy_cap_info[4]); - bf->nrow_bw160 = min_t(int, snd_dim, sts); + bf->nrow_gt_bw80 = min_t(int, snd_dim, sts); } static void From 779d34de055eb8b4cdf54d5429a50b560eb88727 Mon Sep 17 00:00:00 2001 From: Shayne Chen Date: Thu, 10 Nov 2022 17:35:20 +0800 Subject: [PATCH 1491/2002] wifi: mt76: connac: add more unified command IDs Add more unified command IDs which will be used in new chipset. This is the preliminary patch for mt7996 support. Signed-off-by: Shayne Chen Signed-off-by: Felix Fietkau --- .../wireless/mediatek/mt76/mt76_connac_mcu.h | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h index d79fa68cf8b6..e3c59b63295d 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h +++ b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h @@ -1177,11 +1177,33 @@ enum { MCU_UNI_CMD_DEV_INFO_UPDATE = 0x01, MCU_UNI_CMD_BSS_INFO_UPDATE = 0x02, MCU_UNI_CMD_STA_REC_UPDATE = 0x03, + MCU_UNI_CMD_EDCA_UPDATE = 0x04, MCU_UNI_CMD_SUSPEND = 0x05, MCU_UNI_CMD_OFFLOAD = 0x06, MCU_UNI_CMD_HIF_CTRL = 0x07, + MCU_UNI_CMD_BAND_CONFIG = 0x08, + MCU_UNI_CMD_REPT_MUAR = 0x09, + MCU_UNI_CMD_WSYS_CONFIG = 0x0b, + MCU_UNI_CMD_REG_ACCESS = 0x0d, + MCU_UNI_CMD_POWER_CREL = 0x0f, + MCU_UNI_CMD_RX_HDR_TRANS = 0x12, + MCU_UNI_CMD_SER = 0x13, + MCU_UNI_CMD_TWT = 0x14, + MCU_UNI_CMD_RDD_CTRL = 0x19, + MCU_UNI_CMD_GET_MIB_INFO = 0x22, MCU_UNI_CMD_SNIFFER = 0x24, + MCU_UNI_CMD_SR = 0x25, MCU_UNI_CMD_ROC = 0x27, + MCU_UNI_CMD_TXPOWER = 0x2b, + MCU_UNI_CMD_EFUSE_CTRL = 0x2d, + MCU_UNI_CMD_RA = 0x2f, + MCU_UNI_CMD_MURU = 0x31, + MCU_UNI_CMD_BF = 0x33, + MCU_UNI_CMD_CHANNEL_SWITCH = 0x34, + MCU_UNI_CMD_THERMAL = 0x35, + MCU_UNI_CMD_VOW = 0x37, + MCU_UNI_CMD_RRO = 0x57, + MCU_UNI_CMD_OFFCH_SCAN_CTRL = 0x58, }; enum { From ec361f7e415297dc07254e93ac6af1dbce4af87b Mon Sep 17 00:00:00 2001 From: Shayne Chen Date: Thu, 10 Nov 2022 17:35:21 +0800 Subject: [PATCH 1492/2002] wifi: mt76: connac: introduce unified event table Add event IDs and related bit fields for unified event support. This is a preliminary patch to add mt7996 chipset support. Signed-off-by: Bo Jiao Signed-off-by: Peter Chiu Signed-off-by: Shayne Chen Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h index e3c59b63295d..c46a93466727 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h +++ b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h @@ -998,6 +998,17 @@ enum { MCU_EXT_EVENT_MURU_CTRL = 0x9f, }; +/* unified event table */ +enum { + MCU_UNI_EVENT_RESULT = 0x01, + MCU_UNI_EVENT_FW_LOG_2_HOST = 0x04, + MCU_UNI_EVENT_IE_COUNTDOWN = 0x09, + MCU_UNI_EVENT_RDD_REPORT = 0x11, +}; + +#define MCU_UNI_CMD_EVENT BIT(1) +#define MCU_UNI_CMD_UNSOLICITED_EVENT BIT(2) + enum { MCU_Q_QUERY, MCU_Q_SET, From 9c402ac1c517907ae375d71dd2fee4703efc8bb4 Mon Sep 17 00:00:00 2001 From: Shayne Chen Date: Thu, 10 Nov 2022 17:35:22 +0800 Subject: [PATCH 1493/2002] wifi: mt76: connac: add more bss info command tags Add bss info tags which will be used in new chipset. This is a preliminary patch to add mt7996 chipset support. Signed-off-by: Shayne Chen Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h index c46a93466727..90cc9b8f628c 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h +++ b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h @@ -1264,14 +1264,23 @@ enum { enum { UNI_BSS_INFO_BASIC = 0, + UNI_BSS_INFO_RA = 1, UNI_BSS_INFO_RLM = 2, UNI_BSS_INFO_BSS_COLOR = 4, UNI_BSS_INFO_HE_BASIC = 5, UNI_BSS_INFO_BCN_CONTENT = 7, + UNI_BSS_INFO_BCN_CSA = 8, + UNI_BSS_INFO_BCN_BCC = 9, + UNI_BSS_INFO_BCN_MBSSID = 10, + UNI_BSS_INFO_RATE = 11, UNI_BSS_INFO_QBSS = 15, + UNI_BSS_INFO_SEC = 16, + UNI_BSS_INFO_TXCMD = 18, UNI_BSS_INFO_UAPSD = 19, UNI_BSS_INFO_PS = 21, UNI_BSS_INFO_BCNFT = 22, + UNI_BSS_INFO_OFFLOAD = 25, + UNI_BSS_INFO_MLD = 26, }; enum { From 98f191b1d782f069709a0d77f5980ae83a17a6ab Mon Sep 17 00:00:00 2001 From: Shayne Chen Date: Thu, 10 Nov 2022 17:35:23 +0800 Subject: [PATCH 1494/2002] wifi: mt76: connac: add more starec command tags Add starec tags which will be used in new chipset. This is a preliminary patch to add mt7996 chipset support. Signed-off-by: Shayne Chen Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h index 90cc9b8f628c..e8049ac12a84 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h +++ b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h @@ -793,6 +793,8 @@ enum { STA_REC_PHY = 0x15, STA_REC_HE_6G = 0x17, STA_REC_HE_V2 = 0x19, + STA_REC_HDRT = 0x28, + STA_REC_HDR_TRANS = 0x2B, STA_REC_MAX_NUM }; From a6cf2b65fb5b37aef61f168b676a7ecd1ca85294 Mon Sep 17 00:00:00 2001 From: Shayne Chen Date: Thu, 10 Nov 2022 17:35:24 +0800 Subject: [PATCH 1495/2002] wifi: mt76: connac: introduce helper for mt7996 chipset Introduce is_mt7996() helper for new chipset. This is a preliminary patch to support mt7996 driver. Signed-off-by: Shayne Chen Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt76_connac.h | 5 +++++ drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c | 3 ++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac.h b/drivers/net/wireless/mediatek/mt76/mt76_connac.h index 0915eb579539..8ba883b03e50 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76_connac.h +++ b/drivers/net/wireless/mediatek/mt76/mt76_connac.h @@ -187,6 +187,11 @@ static inline bool is_mt7986(struct mt76_dev *dev) return mt76_chip(dev) == 0x7986; } +static inline bool is_mt7996(struct mt76_dev *dev) +{ + return mt76_chip(dev) == 0x7990; +} + static inline bool is_mt7622(struct mt76_dev *dev) { if (!IS_ENABLED(CONFIG_MT7622_WMAC)) diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c index 6b2ce5fd3e4f..b8480d97c0f8 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c @@ -65,7 +65,8 @@ int mt76_connac_mcu_init_download(struct mt76_dev *dev, u32 addr, u32 len, int cmd; if ((!is_connac_v1(dev) && addr == MCU_PATCH_ADDRESS) || - (is_mt7921(dev) && addr == 0x900000)) + (is_mt7921(dev) && addr == 0x900000) || + (is_mt7996(dev) && addr == 0x900000)) cmd = MCU_CMD(PATCH_START_REQ); else cmd = MCU_CMD(TARGET_ADDRESS_LEN_REQ); From 7eefb93d4a6fbccd859e538d208c50fd10b44cb7 Mon Sep 17 00:00:00 2001 From: YN Chen Date: Wed, 16 Nov 2022 22:43:02 +0800 Subject: [PATCH 1496/2002] wifi: mt76: mt7921: fix wrong power after multiple SAR set We should update CLC config before SAR set to synchronize all related settings. Fixes: 23bdc5d8cadf ("wifi: mt76: mt7921: introduce Country Location Control support") Signed-off-by: YN Chen Signed-off-by: Deren Wu Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt7921/init.c | 1 + drivers/net/wireless/mediatek/mt76/mt7921/main.c | 6 ++++++ drivers/net/wireless/mediatek/mt76/mt7921/mt7921.h | 2 ++ 3 files changed, 9 insertions(+) diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/init.c b/drivers/net/wireless/mediatek/mt76/mt7921/init.c index 79b8055ce4c4..542dfd425129 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/init.c +++ b/drivers/net/wireless/mediatek/mt76/mt7921/init.c @@ -59,6 +59,7 @@ mt7921_regd_notifier(struct wiphy *wiphy, memcpy(dev->mt76.alpha2, request->alpha2, sizeof(dev->mt76.alpha2)); dev->mt76.region = request->dfs_region; + dev->country_ie_env = request->country_ie_env; mt7921_mutex_acquire(dev); mt7921_mcu_set_clc(dev, request->alpha2, request->country_ie_env); diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/main.c b/drivers/net/wireless/mediatek/mt76/mt7921/main.c index 41df17efdb3a..76ac5069638f 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7921/main.c @@ -1615,7 +1615,13 @@ static int mt7921_set_sar_specs(struct ieee80211_hw *hw, int err; mt7921_mutex_acquire(dev); + err = mt7921_mcu_set_clc(dev, dev->mt76.alpha2, + dev->country_ie_env); + if (err < 0) + goto out; + err = mt7921_set_tx_sar_pwr(hw, sar); +out: mt7921_mutex_release(dev); return err; diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/mt7921.h b/drivers/net/wireless/mediatek/mt76/mt7921/mt7921.h index ac70a978dbed..2398c6fd0b39 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/mt7921.h +++ b/drivers/net/wireless/mediatek/mt76/mt7921/mt7921.h @@ -304,6 +304,8 @@ struct mt7921_dev { struct work_struct ipv6_ns_work; /* IPv6 addresses for WoWLAN */ struct sk_buff_head ipv6_ns_list; + + enum environment_cap country_ie_env; }; enum { From 2666bece0905a3e8ccb792602dbc76a63aaafe4b Mon Sep 17 00:00:00 2001 From: Sujuan Chen Date: Sat, 12 Nov 2022 16:40:34 +0100 Subject: [PATCH 1497/2002] wifi: mt76: introduce rxwi and rx token utility routines This is a preliminary patch to introduce WED RX support for mt7915. Tested-by: Daniel Golle Co-developed-by: Lorenzo Bianconi Signed-off-by: Lorenzo Bianconi Signed-off-by: Sujuan Chen Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/dma.c | 68 +++++++++++++++++++ drivers/net/wireless/mediatek/mt76/dma.h | 8 +++ drivers/net/wireless/mediatek/mt76/mac80211.c | 5 ++ drivers/net/wireless/mediatek/mt76/mt76.h | 16 ++++- drivers/net/wireless/mediatek/mt76/tx.c | 30 ++++++++ 5 files changed, 126 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/mediatek/mt76/dma.c b/drivers/net/wireless/mediatek/mt76/dma.c index 7378c4d1e156..d316bde01c6b 100644 --- a/drivers/net/wireless/mediatek/mt76/dma.c +++ b/drivers/net/wireless/mediatek/mt76/dma.c @@ -59,6 +59,19 @@ mt76_alloc_txwi(struct mt76_dev *dev) return t; } +static struct mt76_txwi_cache * +mt76_alloc_rxwi(struct mt76_dev *dev) +{ + struct mt76_txwi_cache *t; + + t = kzalloc(L1_CACHE_ALIGN(sizeof(*t)), GFP_ATOMIC); + if (!t) + return NULL; + + t->ptr = NULL; + return t; +} + static struct mt76_txwi_cache * __mt76_get_txwi(struct mt76_dev *dev) { @@ -75,6 +88,22 @@ __mt76_get_txwi(struct mt76_dev *dev) return t; } +static struct mt76_txwi_cache * +__mt76_get_rxwi(struct mt76_dev *dev) +{ + struct mt76_txwi_cache *t = NULL; + + spin_lock(&dev->wed_lock); + if (!list_empty(&dev->rxwi_cache)) { + t = list_first_entry(&dev->rxwi_cache, struct mt76_txwi_cache, + list); + list_del(&t->list); + } + spin_unlock(&dev->wed_lock); + + return t; +} + static struct mt76_txwi_cache * mt76_get_txwi(struct mt76_dev *dev) { @@ -86,6 +115,18 @@ mt76_get_txwi(struct mt76_dev *dev) return mt76_alloc_txwi(dev); } +struct mt76_txwi_cache * +mt76_get_rxwi(struct mt76_dev *dev) +{ + struct mt76_txwi_cache *t = __mt76_get_rxwi(dev); + + if (t) + return t; + + return mt76_alloc_rxwi(dev); +} +EXPORT_SYMBOL_GPL(mt76_get_rxwi); + void mt76_put_txwi(struct mt76_dev *dev, struct mt76_txwi_cache *t) { @@ -98,6 +139,18 @@ mt76_put_txwi(struct mt76_dev *dev, struct mt76_txwi_cache *t) } EXPORT_SYMBOL_GPL(mt76_put_txwi); +void +mt76_put_rxwi(struct mt76_dev *dev, struct mt76_txwi_cache *t) +{ + if (!t) + return; + + spin_lock(&dev->wed_lock); + list_add(&t->list, &dev->rxwi_cache); + spin_unlock(&dev->wed_lock); +} +EXPORT_SYMBOL_GPL(mt76_put_rxwi); + static void mt76_free_pending_txwi(struct mt76_dev *dev) { @@ -112,6 +165,20 @@ mt76_free_pending_txwi(struct mt76_dev *dev) local_bh_enable(); } +static void +mt76_free_pending_rxwi(struct mt76_dev *dev) +{ + struct mt76_txwi_cache *t; + + local_bh_disable(); + while ((t = __mt76_get_rxwi(dev)) != NULL) { + if (t->ptr) + skb_free_frag(t->ptr); + kfree(t); + } + local_bh_enable(); +} + static void mt76_dma_sync_idx(struct mt76_dev *dev, struct mt76_queue *q) { @@ -808,6 +875,7 @@ void mt76_dma_cleanup(struct mt76_dev *dev) } mt76_free_pending_txwi(dev); + mt76_free_pending_rxwi(dev); if (mtk_wed_device_active(&dev->mmio.wed)) mtk_wed_device_detach(&dev->mmio.wed); diff --git a/drivers/net/wireless/mediatek/mt76/dma.h b/drivers/net/wireless/mediatek/mt76/dma.h index fdf786f975ea..53c6ce2528b2 100644 --- a/drivers/net/wireless/mediatek/mt76/dma.h +++ b/drivers/net/wireless/mediatek/mt76/dma.h @@ -15,6 +15,14 @@ #define MT_DMA_CTL_SD_LEN0 GENMASK(29, 16) #define MT_DMA_CTL_LAST_SEC0 BIT(30) #define MT_DMA_CTL_DMA_DONE BIT(31) +#define MT_DMA_CTL_TO_HOST BIT(8) +#define MT_DMA_CTL_TO_HOST_A BIT(12) +#define MT_DMA_CTL_DROP BIT(14) +#define MT_DMA_CTL_TOKEN GENMASK(31, 16) + +#define MT_DMA_PPE_CPU_REASON GENMASK(15, 11) +#define MT_DMA_PPE_ENTRY GENMASK(30, 16) +#define MT_DMA_INFO_PPE_VLD BIT(31) #define MT_DMA_HDR_LEN 4 #define MT_RX_INFO_LEN 4 diff --git a/drivers/net/wireless/mediatek/mt76/mac80211.c b/drivers/net/wireless/mediatek/mt76/mac80211.c index c59d12004459..3cd37a013dcc 100644 --- a/drivers/net/wireless/mediatek/mt76/mac80211.c +++ b/drivers/net/wireless/mediatek/mt76/mac80211.c @@ -572,6 +572,7 @@ mt76_alloc_device(struct device *pdev, unsigned int size, spin_lock_init(&dev->lock); spin_lock_init(&dev->cc_lock); spin_lock_init(&dev->status_lock); + spin_lock_init(&dev->wed_lock); mutex_init(&dev->mutex); init_waitqueue_head(&dev->tx_wait); @@ -594,9 +595,13 @@ mt76_alloc_device(struct device *pdev, unsigned int size, spin_lock_init(&dev->token_lock); idr_init(&dev->token); + spin_lock_init(&dev->rx_token_lock); + idr_init(&dev->rx_token); + INIT_LIST_HEAD(&dev->wcid_list); INIT_LIST_HEAD(&dev->txwi_cache); + INIT_LIST_HEAD(&dev->rxwi_cache); dev->token_size = dev->drv->token_size; for (i = 0; i < ARRAY_SIZE(dev->q_rx); i++) diff --git a/drivers/net/wireless/mediatek/mt76/mt76.h b/drivers/net/wireless/mediatek/mt76/mt76.h index a2bccf6b6c54..149dc6eb7eb9 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76.h +++ b/drivers/net/wireless/mediatek/mt76/mt76.h @@ -339,7 +339,10 @@ struct mt76_txwi_cache { struct list_head list; dma_addr_t dma_addr; - struct sk_buff *skb; + union { + struct sk_buff *skb; + void *ptr; + }; }; struct mt76_rx_tid { @@ -728,6 +731,7 @@ struct mt76_dev { struct ieee80211_hw *hw; + spinlock_t wed_lock; spinlock_t lock; spinlock_t cc_lock; @@ -754,6 +758,7 @@ struct mt76_dev { struct sk_buff_head rx_skb[__MT_RXQ_MAX]; struct list_head txwi_cache; + struct list_head rxwi_cache; struct mt76_queue *q_mcu[__MT_MCUQ_MAX]; struct mt76_queue q_rx[__MT_RXQ_MAX]; const struct mt76_queue_ops *queue_ops; @@ -768,6 +773,10 @@ struct mt76_dev { u16 token_count; u16 token_size; + spinlock_t rx_token_lock; + struct idr rx_token; + u16 rx_token_size; + wait_queue_head_t tx_wait; /* spinclock used to protect wcid pktid linked list */ spinlock_t status_lock; @@ -1247,6 +1256,8 @@ mt76_tx_status_get_hw(struct mt76_dev *dev, struct sk_buff *skb) } void mt76_put_txwi(struct mt76_dev *dev, struct mt76_txwi_cache *t); +void mt76_put_rxwi(struct mt76_dev *dev, struct mt76_txwi_cache *t); +struct mt76_txwi_cache *mt76_get_rxwi(struct mt76_dev *dev); void mt76_rx_complete(struct mt76_dev *dev, struct sk_buff_head *frames, struct napi_struct *napi); void mt76_rx_poll_complete(struct mt76_dev *dev, enum mt76_rxq_id q, @@ -1391,6 +1402,9 @@ struct mt76_txwi_cache * mt76_token_release(struct mt76_dev *dev, int token, bool *wake); int mt76_token_consume(struct mt76_dev *dev, struct mt76_txwi_cache **ptxwi); void __mt76_set_tx_blocked(struct mt76_dev *dev, bool blocked); +struct mt76_txwi_cache *mt76_rx_token_release(struct mt76_dev *dev, int token); +int mt76_rx_token_consume(struct mt76_dev *dev, void *ptr, + struct mt76_txwi_cache *r, dma_addr_t phys); static inline void mt76_set_tx_blocked(struct mt76_dev *dev, bool blocked) { diff --git a/drivers/net/wireless/mediatek/mt76/tx.c b/drivers/net/wireless/mediatek/mt76/tx.c index 6c054850363f..24568b98ed9d 100644 --- a/drivers/net/wireless/mediatek/mt76/tx.c +++ b/drivers/net/wireless/mediatek/mt76/tx.c @@ -756,6 +756,23 @@ int mt76_token_consume(struct mt76_dev *dev, struct mt76_txwi_cache **ptxwi) } EXPORT_SYMBOL_GPL(mt76_token_consume); +int mt76_rx_token_consume(struct mt76_dev *dev, void *ptr, + struct mt76_txwi_cache *t, dma_addr_t phys) +{ + int token; + + spin_lock_bh(&dev->rx_token_lock); + token = idr_alloc(&dev->rx_token, t, 0, dev->rx_token_size, + GFP_ATOMIC); + spin_unlock_bh(&dev->rx_token_lock); + + t->ptr = ptr; + t->dma_addr = phys; + + return token; +} +EXPORT_SYMBOL_GPL(mt76_rx_token_consume); + struct mt76_txwi_cache * mt76_token_release(struct mt76_dev *dev, int token, bool *wake) { @@ -784,3 +801,16 @@ mt76_token_release(struct mt76_dev *dev, int token, bool *wake) return txwi; } EXPORT_SYMBOL_GPL(mt76_token_release); + +struct mt76_txwi_cache * +mt76_rx_token_release(struct mt76_dev *dev, int token) +{ + struct mt76_txwi_cache *t; + + spin_lock_bh(&dev->rx_token_lock); + t = idr_remove(&dev->rx_token, token); + spin_unlock_bh(&dev->rx_token_lock); + + return t; +} +EXPORT_SYMBOL_GPL(mt76_rx_token_release); From cd372b8c99c5a5cf6a464acebb7e4a79af7ec8ae Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Sat, 12 Nov 2022 16:40:35 +0100 Subject: [PATCH 1498/2002] wifi: mt76: add WED RX support to mt76_dma_{add,get}_buf Introduce the capability to configure RX WED in mt76_dma_{add,get}_buf utility routines. Tested-by: Daniel Golle Co-developed-by: Sujuan Chen Signed-off-by: Sujuan Chen Signed-off-by: Lorenzo Bianconi Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/dma.c | 121 +++++++++++++++------- drivers/net/wireless/mediatek/mt76/mt76.h | 2 + 2 files changed, 86 insertions(+), 37 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/dma.c b/drivers/net/wireless/mediatek/mt76/dma.c index d316bde01c6b..4239adde4cca 100644 --- a/drivers/net/wireless/mediatek/mt76/dma.c +++ b/drivers/net/wireless/mediatek/mt76/dma.c @@ -215,11 +215,6 @@ mt76_dma_add_buf(struct mt76_dev *dev, struct mt76_queue *q, u32 ctrl; int i, idx = -1; - if (txwi) { - q->entry[q->head].txwi = DMA_DUMMY_DATA; - q->entry[q->head].skip_buf0 = true; - } - for (i = 0; i < nbufs; i += 2, buf += 2) { u32 buf0 = buf[0].addr, buf1 = 0; @@ -229,28 +224,48 @@ mt76_dma_add_buf(struct mt76_dev *dev, struct mt76_queue *q, desc = &q->desc[idx]; entry = &q->entry[idx]; - if (buf[0].skip_unmap) - entry->skip_buf0 = true; - entry->skip_buf1 = i == nbufs - 1; + if ((q->flags & MT_QFLAG_WED) && + FIELD_GET(MT_QFLAG_WED_TYPE, q->flags) == MT76_WED_Q_RX) { + struct mt76_txwi_cache *t = txwi; + int rx_token; - entry->dma_addr[0] = buf[0].addr; - entry->dma_len[0] = buf[0].len; + if (!t) + return -ENOMEM; - ctrl = FIELD_PREP(MT_DMA_CTL_SD_LEN0, buf[0].len); - if (i < nbufs - 1) { - entry->dma_addr[1] = buf[1].addr; - entry->dma_len[1] = buf[1].len; - buf1 = buf[1].addr; - ctrl |= FIELD_PREP(MT_DMA_CTL_SD_LEN1, buf[1].len); - if (buf[1].skip_unmap) - entry->skip_buf1 = true; + rx_token = mt76_rx_token_consume(dev, (void *)skb, t, + buf[0].addr); + buf1 |= FIELD_PREP(MT_DMA_CTL_TOKEN, rx_token); + ctrl = FIELD_PREP(MT_DMA_CTL_SD_LEN0, buf[0].len) | + MT_DMA_CTL_TO_HOST; + } else { + if (txwi) { + q->entry[q->head].txwi = DMA_DUMMY_DATA; + q->entry[q->head].skip_buf0 = true; + } + + if (buf[0].skip_unmap) + entry->skip_buf0 = true; + entry->skip_buf1 = i == nbufs - 1; + + entry->dma_addr[0] = buf[0].addr; + entry->dma_len[0] = buf[0].len; + + ctrl = FIELD_PREP(MT_DMA_CTL_SD_LEN0, buf[0].len); + if (i < nbufs - 1) { + entry->dma_addr[1] = buf[1].addr; + entry->dma_len[1] = buf[1].len; + buf1 = buf[1].addr; + ctrl |= FIELD_PREP(MT_DMA_CTL_SD_LEN1, buf[1].len); + if (buf[1].skip_unmap) + entry->skip_buf1 = true; + } + + if (i == nbufs - 1) + ctrl |= MT_DMA_CTL_LAST_SEC0; + else if (i == nbufs - 2) + ctrl |= MT_DMA_CTL_LAST_SEC1; } - if (i == nbufs - 1) - ctrl |= MT_DMA_CTL_LAST_SEC0; - else if (i == nbufs - 2) - ctrl |= MT_DMA_CTL_LAST_SEC1; - WRITE_ONCE(desc->buf0, cpu_to_le32(buf0)); WRITE_ONCE(desc->buf1, cpu_to_le32(buf1)); WRITE_ONCE(desc->info, cpu_to_le32(info)); @@ -339,33 +354,60 @@ mt76_dma_tx_cleanup(struct mt76_dev *dev, struct mt76_queue *q, bool flush) static void * mt76_dma_get_buf(struct mt76_dev *dev, struct mt76_queue *q, int idx, - int *len, u32 *info, bool *more) + int *len, u32 *info, bool *more, bool *drop) { struct mt76_queue_entry *e = &q->entry[idx]; struct mt76_desc *desc = &q->desc[idx]; - dma_addr_t buf_addr; - void *buf = e->buf; - int buf_len = SKB_WITH_OVERHEAD(q->buf_size); + void *buf; - buf_addr = e->dma_addr[0]; if (len) { - u32 ctl = le32_to_cpu(READ_ONCE(desc->ctrl)); - *len = FIELD_GET(MT_DMA_CTL_SD_LEN0, ctl); - *more = !(ctl & MT_DMA_CTL_LAST_SEC0); + u32 ctrl = le32_to_cpu(READ_ONCE(desc->ctrl)); + *len = FIELD_GET(MT_DMA_CTL_SD_LEN0, ctrl); + *more = !(ctrl & MT_DMA_CTL_LAST_SEC0); } if (info) *info = le32_to_cpu(desc->info); - dma_unmap_single(dev->dma_dev, buf_addr, buf_len, DMA_FROM_DEVICE); - e->buf = NULL; + if ((q->flags & MT_QFLAG_WED) && + FIELD_GET(MT_QFLAG_WED_TYPE, q->flags) == MT76_WED_Q_RX) { + u32 token = FIELD_GET(MT_DMA_CTL_TOKEN, + le32_to_cpu(desc->buf1)); + struct mt76_txwi_cache *t = mt76_rx_token_release(dev, token); + + if (!t) + return NULL; + + dma_unmap_single(dev->dma_dev, t->dma_addr, + SKB_WITH_OVERHEAD(q->buf_size), + DMA_FROM_DEVICE); + + buf = t->ptr; + t->dma_addr = 0; + t->ptr = NULL; + + mt76_put_rxwi(dev, t); + + if (drop) { + u32 ctrl = le32_to_cpu(READ_ONCE(desc->ctrl)); + + *drop = !!(ctrl & (MT_DMA_CTL_TO_HOST_A | + MT_DMA_CTL_DROP)); + } + } else { + buf = e->buf; + e->buf = NULL; + dma_unmap_single(dev->dma_dev, e->dma_addr[0], + SKB_WITH_OVERHEAD(q->buf_size), + DMA_FROM_DEVICE); + } return buf; } static void * mt76_dma_dequeue(struct mt76_dev *dev, struct mt76_queue *q, bool flush, - int *len, u32 *info, bool *more) + int *len, u32 *info, bool *more, bool *drop) { int idx = q->tail; @@ -381,7 +423,7 @@ mt76_dma_dequeue(struct mt76_dev *dev, struct mt76_queue *q, bool flush, q->tail = (q->tail + 1) % q->ndesc; q->queued--; - return mt76_dma_get_buf(dev, q, idx, len, info, more); + return mt76_dma_get_buf(dev, q, idx, len, info, more, drop); } static int @@ -641,7 +683,7 @@ mt76_dma_rx_cleanup(struct mt76_dev *dev, struct mt76_queue *q) spin_lock_bh(&q->lock); do { - buf = mt76_dma_dequeue(dev, q, true, NULL, NULL, &more); + buf = mt76_dma_dequeue(dev, q, true, NULL, NULL, &more, NULL); if (!buf) break; @@ -723,6 +765,7 @@ mt76_dma_rx_process(struct mt76_dev *dev, struct mt76_queue *q, int budget) } while (done < budget) { + bool drop = false; u32 info; if (check_ddone) { @@ -733,10 +776,14 @@ mt76_dma_rx_process(struct mt76_dev *dev, struct mt76_queue *q, int budget) break; } - data = mt76_dma_dequeue(dev, q, false, &len, &info, &more); + data = mt76_dma_dequeue(dev, q, false, &len, &info, &more, + &drop); if (!data) break; + if (drop) + goto free_frag; + if (q->rx_head) data_len = q->buf_size; else diff --git a/drivers/net/wireless/mediatek/mt76/mt76.h b/drivers/net/wireless/mediatek/mt76/mt76.h index 149dc6eb7eb9..738fb22d9198 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76.h +++ b/drivers/net/wireless/mediatek/mt76/mt76.h @@ -35,6 +35,7 @@ FIELD_PREP(MT_QFLAG_WED_TYPE, _type) | \ FIELD_PREP(MT_QFLAG_WED_RING, _n)) #define MT_WED_Q_TX(_n) __MT_WED_Q(MT76_WED_Q_TX, _n) +#define MT_WED_Q_RX(_n) __MT_WED_Q(MT76_WED_Q_RX, _n) #define MT_WED_Q_TXFREE __MT_WED_Q(MT76_WED_Q_TXFREE, 0) struct mt76_dev; @@ -56,6 +57,7 @@ enum mt76_bus_type { enum mt76_wed_type { MT76_WED_Q_TX, MT76_WED_Q_TXFREE, + MT76_WED_Q_RX, }; struct mt76_bus_ops { From c601baaf5cd89deda3643beee4b8a57ef151985c Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Sat, 12 Nov 2022 16:40:36 +0100 Subject: [PATCH 1499/2002] wifi: mt76: add WED RX support to mt76_dma_rx_fill Introduce the capability to refill WED RX buffers in mt76_dma_rx_fill utility routine. Tested-by: Daniel Golle Co-developed-by: Sujuan Chen Signed-off-by: Sujuan Chen Signed-off-by: Lorenzo Bianconi Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/dma.c | 33 +++++++++++++++++++----- 1 file changed, 27 insertions(+), 6 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/dma.c b/drivers/net/wireless/mediatek/mt76/dma.c index 4239adde4cca..cb6e3b358aca 100644 --- a/drivers/net/wireless/mediatek/mt76/dma.c +++ b/drivers/net/wireless/mediatek/mt76/dma.c @@ -550,14 +550,26 @@ free_skb: return ret; } +static struct page_frag_cache * +mt76_dma_rx_get_frag_cache(struct mt76_dev *dev, struct mt76_queue *q) +{ + struct page_frag_cache *rx_page = &q->rx_page; + +#ifdef CONFIG_NET_MEDIATEK_SOC_WED + if ((q->flags & MT_QFLAG_WED) && + FIELD_GET(MT_QFLAG_WED_TYPE, q->flags) == MT76_WED_Q_RX) + rx_page = &dev->mmio.wed.rx_buf_ring.rx_page; +#endif + return rx_page; +} + static int mt76_dma_rx_fill(struct mt76_dev *dev, struct mt76_queue *q) { - dma_addr_t addr; - void *buf; - int frames = 0; + struct page_frag_cache *rx_page = mt76_dma_rx_get_frag_cache(dev, q); int len = SKB_WITH_OVERHEAD(q->buf_size); - int offset = q->buf_offset; + int frames = 0, offset = q->buf_offset; + dma_addr_t addr; if (!q->ndesc) return 0; @@ -565,9 +577,18 @@ mt76_dma_rx_fill(struct mt76_dev *dev, struct mt76_queue *q) spin_lock_bh(&q->lock); while (q->queued < q->ndesc - 1) { + struct mt76_txwi_cache *t = NULL; struct mt76_queue_buf qbuf; + void *buf = NULL; - buf = page_frag_alloc(&q->rx_page, q->buf_size, GFP_ATOMIC); + if ((q->flags & MT_QFLAG_WED) && + FIELD_GET(MT_QFLAG_WED_TYPE, q->flags) == MT76_WED_Q_RX) { + t = mt76_get_rxwi(dev); + if (!t) + break; + } + + buf = page_frag_alloc(rx_page, q->buf_size, GFP_ATOMIC); if (!buf) break; @@ -580,7 +601,7 @@ mt76_dma_rx_fill(struct mt76_dev *dev, struct mt76_queue *q) qbuf.addr = addr + offset; qbuf.len = len - offset; qbuf.skip_unmap = false; - mt76_dma_add_buf(dev, q, &qbuf, 1, 0, buf, NULL); + mt76_dma_add_buf(dev, q, &qbuf, 1, 0, buf, t); frames++; } From 52546e27787ed005413ac622ae9e4c3087100b50 Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Sat, 12 Nov 2022 16:40:37 +0100 Subject: [PATCH 1500/2002] wifi: mt76: add WED RX support to dma queue alloc Introduce the capability to allocate WED RX buffers in mt76_dma_wed_setup routine. Tested-by: Daniel Golle Co-developed-by: Sujuan Chen Signed-off-by: Sujuan Chen Signed-off-by: Lorenzo Bianconi Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/dma.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/mediatek/mt76/dma.c b/drivers/net/wireless/mediatek/mt76/dma.c index cb6e3b358aca..58b41bda5eac 100644 --- a/drivers/net/wireless/mediatek/mt76/dma.c +++ b/drivers/net/wireless/mediatek/mt76/dma.c @@ -647,6 +647,11 @@ mt76_dma_wed_setup(struct mt76_dev *dev, struct mt76_queue *q) if (!ret) q->wed_regs = wed->txfree_ring.reg_base; break; + case MT76_WED_Q_RX: + ret = mtk_wed_device_rx_ring_setup(wed, ring, q->regs); + if (!ret) + q->wed_regs = wed->rx_ring[ring].reg_base; + break; default: ret = -EINVAL; } @@ -938,8 +943,11 @@ void mt76_dma_cleanup(struct mt76_dev *dev) mt76_dma_tx_cleanup(dev, dev->q_mcu[i], true); mt76_for_each_q_rx(dev, i) { + struct mt76_queue *q = &dev->q_rx[i]; + netif_napi_del(&dev->napi[i]); - mt76_dma_rx_cleanup(dev, &dev->q_rx[i]); + if (FIELD_GET(MT_QFLAG_WED_TYPE, q->flags)) + mt76_dma_rx_cleanup(dev, q); } mt76_free_pending_txwi(dev); From c3137942771eceef923158980b6cccae572e81d1 Mon Sep 17 00:00:00 2001 From: Sujuan Chen Date: Sat, 12 Nov 2022 16:40:38 +0100 Subject: [PATCH 1501/2002] wifi: mt76: add info parameter to rx_skb signature This is a preliminary patch to introduce WED RX support for mt7915. Tested-by: Daniel Golle Co-developed-by: Lorenzo Bianconi Signed-off-by: Lorenzo Bianconi Signed-off-by: Sujuan Chen Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/dma.c | 8 ++-- drivers/net/wireless/mediatek/mt76/mt76.h | 2 +- .../net/wireless/mediatek/mt76/mt7603/dma.c | 2 +- .../wireless/mediatek/mt76/mt7603/mt7603.h | 2 +- .../net/wireless/mediatek/mt76/mt7615/mac.c | 2 +- .../wireless/mediatek/mt76/mt7615/mt7615.h | 2 +- drivers/net/wireless/mediatek/mt76/mt76x02.h | 2 +- .../net/wireless/mediatek/mt76/mt76x02_txrx.c | 2 +- .../net/wireless/mediatek/mt76/mt7915/mac.c | 43 ++++++++++++++++--- .../wireless/mediatek/mt76/mt7915/mt7915.h | 2 +- .../net/wireless/mediatek/mt76/mt7921/mac.c | 2 +- .../wireless/mediatek/mt76/mt7921/mt7921.h | 2 +- drivers/net/wireless/mediatek/mt76/sdio.c | 2 +- drivers/net/wireless/mediatek/mt76/usb.c | 2 +- 14 files changed, 53 insertions(+), 22 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/dma.c b/drivers/net/wireless/mediatek/mt76/dma.c index 58b41bda5eac..8dca8d2447b7 100644 --- a/drivers/net/wireless/mediatek/mt76/dma.c +++ b/drivers/net/wireless/mediatek/mt76/dma.c @@ -750,7 +750,7 @@ mt76_dma_rx_reset(struct mt76_dev *dev, enum mt76_rxq_id qid) static void mt76_add_fragment(struct mt76_dev *dev, struct mt76_queue *q, void *data, - int len, bool more) + int len, bool more, u32 info) { struct sk_buff *skb = q->rx_head; struct skb_shared_info *shinfo = skb_shinfo(skb); @@ -770,7 +770,7 @@ mt76_add_fragment(struct mt76_dev *dev, struct mt76_queue *q, void *data, q->rx_head = NULL; if (nr_frags < ARRAY_SIZE(shinfo->frags)) - dev->drv->rx_skb(dev, q - dev->q_rx, skb); + dev->drv->rx_skb(dev, q - dev->q_rx, skb, &info); else dev_kfree_skb(skb); } @@ -822,7 +822,7 @@ mt76_dma_rx_process(struct mt76_dev *dev, struct mt76_queue *q, int budget) } if (q->rx_head) { - mt76_add_fragment(dev, q, data, len, more); + mt76_add_fragment(dev, q, data, len, more, info); continue; } @@ -846,7 +846,7 @@ mt76_dma_rx_process(struct mt76_dev *dev, struct mt76_queue *q, int budget) continue; } - dev->drv->rx_skb(dev, q - dev->q_rx, skb); + dev->drv->rx_skb(dev, q - dev->q_rx, skb, &info); continue; free_frag: diff --git a/drivers/net/wireless/mediatek/mt76/mt76.h b/drivers/net/wireless/mediatek/mt76/mt76.h index 738fb22d9198..bf4ad629df3f 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76.h +++ b/drivers/net/wireless/mediatek/mt76/mt76.h @@ -444,7 +444,7 @@ struct mt76_driver_ops { bool (*rx_check)(struct mt76_dev *dev, void *data, int len); void (*rx_skb)(struct mt76_dev *dev, enum mt76_rxq_id q, - struct sk_buff *skb); + struct sk_buff *skb, u32 *info); void (*rx_poll_complete)(struct mt76_dev *dev, enum mt76_rxq_id q); diff --git a/drivers/net/wireless/mediatek/mt76/mt7603/dma.c b/drivers/net/wireless/mediatek/mt76/mt7603/dma.c index f9e5857850e7..03ba11a61c90 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7603/dma.c +++ b/drivers/net/wireless/mediatek/mt76/mt7603/dma.c @@ -69,7 +69,7 @@ free: } void mt7603_queue_rx_skb(struct mt76_dev *mdev, enum mt76_rxq_id q, - struct sk_buff *skb) + struct sk_buff *skb, u32 *info) { struct mt7603_dev *dev = container_of(mdev, struct mt7603_dev, mt76); __le32 *rxd = (__le32 *)skb->data; diff --git a/drivers/net/wireless/mediatek/mt76/mt7603/mt7603.h b/drivers/net/wireless/mediatek/mt76/mt7603/mt7603.h index 0fd46d907638..7c3be596da09 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7603/mt7603.h +++ b/drivers/net/wireless/mediatek/mt76/mt7603/mt7603.h @@ -244,7 +244,7 @@ int mt7603_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr, void mt7603_tx_complete_skb(struct mt76_dev *mdev, struct mt76_queue_entry *e); void mt7603_queue_rx_skb(struct mt76_dev *mdev, enum mt76_rxq_id q, - struct sk_buff *skb); + struct sk_buff *skb, u32 *info); void mt7603_rx_poll_complete(struct mt76_dev *mdev, enum mt76_rxq_id q); void mt7603_sta_ps(struct mt76_dev *mdev, struct ieee80211_sta *sta, bool ps); int mt7603_sta_add(struct mt76_dev *mdev, struct ieee80211_vif *vif, diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/mac.c b/drivers/net/wireless/mediatek/mt76/mt7615/mac.c index 305bf1826a02..a95602473359 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7615/mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7615/mac.c @@ -1666,7 +1666,7 @@ bool mt7615_rx_check(struct mt76_dev *mdev, void *data, int len) EXPORT_SYMBOL_GPL(mt7615_rx_check); void mt7615_queue_rx_skb(struct mt76_dev *mdev, enum mt76_rxq_id q, - struct sk_buff *skb) + struct sk_buff *skb, u32 *info) { struct mt7615_dev *dev = container_of(mdev, struct mt7615_dev, mt76); __le32 *rxd = (__le32 *)skb->data; diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/mt7615.h b/drivers/net/wireless/mediatek/mt76/mt7615/mt7615.h index 8b37f8259f52..087d4886162e 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7615/mt7615.h +++ b/drivers/net/wireless/mediatek/mt76/mt7615/mt7615.h @@ -513,7 +513,7 @@ void mt7615_tx_worker(struct mt76_worker *w); void mt7615_tx_token_put(struct mt7615_dev *dev); bool mt7615_rx_check(struct mt76_dev *mdev, void *data, int len); void mt7615_queue_rx_skb(struct mt76_dev *mdev, enum mt76_rxq_id q, - struct sk_buff *skb); + struct sk_buff *skb, u32 *info); void mt7615_sta_ps(struct mt76_dev *mdev, struct ieee80211_sta *sta, bool ps); int mt7615_mac_sta_add(struct mt76_dev *mdev, struct ieee80211_vif *vif, struct ieee80211_sta *sta); diff --git a/drivers/net/wireless/mediatek/mt76/mt76x02.h b/drivers/net/wireless/mediatek/mt76/mt76x02.h index a19176bb2433..4cd63bacd742 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76x02.h +++ b/drivers/net/wireless/mediatek/mt76/mt76x02.h @@ -188,7 +188,7 @@ int mt76x02_set_rts_threshold(struct ieee80211_hw *hw, u32 val); void mt76x02_remove_hdr_pad(struct sk_buff *skb, int len); bool mt76x02_tx_status_data(struct mt76_dev *mdev, u8 *update); void mt76x02_queue_rx_skb(struct mt76_dev *mdev, enum mt76_rxq_id q, - struct sk_buff *skb); + struct sk_buff *skb, u32 *info); void mt76x02_rx_poll_complete(struct mt76_dev *mdev, enum mt76_rxq_id q); irqreturn_t mt76x02_irq_handler(int irq, void *dev_instance); void mt76x02_tx(struct ieee80211_hw *hw, struct ieee80211_tx_control *control, diff --git a/drivers/net/wireless/mediatek/mt76/mt76x02_txrx.c b/drivers/net/wireless/mediatek/mt76/mt76x02_txrx.c index 3a313075a9e3..d8bc4ae185f5 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76x02_txrx.c +++ b/drivers/net/wireless/mediatek/mt76/mt76x02_txrx.c @@ -33,7 +33,7 @@ void mt76x02_tx(struct ieee80211_hw *hw, struct ieee80211_tx_control *control, EXPORT_SYMBOL_GPL(mt76x02_tx); void mt76x02_queue_rx_skb(struct mt76_dev *mdev, enum mt76_rxq_id q, - struct sk_buff *skb) + struct sk_buff *skb, u32 *info) { struct mt76x02_dev *dev = container_of(mdev, struct mt76x02_dev, mt76); void *rxwi = skb->data; diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mac.c b/drivers/net/wireless/mediatek/mt76/mt7915/mac.c index 117ddb00348c..4f0b3c57e545 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mac.c @@ -165,9 +165,9 @@ static void mt7915_mac_sta_poll(struct mt7915_dev *dev) sta = container_of((void *)msta, struct ieee80211_sta, drv_priv); for (i = 0; i < IEEE80211_NUM_ACS; i++) { - u8 q = mt76_connac_lmac_mapping(i); - u32 tx_cur = tx_time[q]; - u32 rx_cur = rx_time[q]; + u8 queue = mt76_connac_lmac_mapping(i); + u32 tx_cur = tx_time[queue]; + u32 rx_cur = rx_time[queue]; u8 tid = ac_to_tid[i]; if (!tx_cur && !rx_cur) @@ -245,8 +245,37 @@ void mt7915_mac_enable_rtscts(struct mt7915_dev *dev, mt76_clear(dev, addr, BIT(5)); } +static void +mt7915_wed_check_ppe(struct mt7915_dev *dev, struct mt76_queue *q, + struct mt7915_sta *msta, struct sk_buff *skb, + u32 info) +{ + struct ieee80211_vif *vif; + struct wireless_dev *wdev; + + if (!msta || !msta->vif) + return; + + if (!(q->flags & MT_QFLAG_WED) || + FIELD_GET(MT_QFLAG_WED_TYPE, q->flags) != MT76_WED_Q_RX) + return; + + if (!(info & MT_DMA_INFO_PPE_VLD)) + return; + + vif = container_of((void *)msta->vif, struct ieee80211_vif, + drv_priv); + wdev = ieee80211_vif_to_wdev(vif); + skb->dev = wdev->netdev; + + mtk_wed_device_ppe_check(&dev->mt76.mmio.wed, skb, + FIELD_GET(MT_DMA_PPE_CPU_REASON, info), + FIELD_GET(MT_DMA_PPE_ENTRY, info)); +} + static int -mt7915_mac_fill_rx(struct mt7915_dev *dev, struct sk_buff *skb) +mt7915_mac_fill_rx(struct mt7915_dev *dev, struct sk_buff *skb, + enum mt76_rxq_id q, u32 *info) { struct mt76_rx_status *status = (struct mt76_rx_status *)skb->cb; struct mt76_phy *mphy = &dev->mt76.phy; @@ -513,6 +542,8 @@ mt7915_mac_fill_rx(struct mt7915_dev *dev, struct sk_buff *skb) } } else { status->flag |= RX_FLAG_8023; + mt7915_wed_check_ppe(dev, &dev->mt76.q_rx[q], msta, skb, + *info); } if (rxv && mode >= MT_PHY_TYPE_HE_SU && !(status->flag & RX_FLAG_8023)) @@ -1096,7 +1127,7 @@ bool mt7915_rx_check(struct mt76_dev *mdev, void *data, int len) } void mt7915_queue_rx_skb(struct mt76_dev *mdev, enum mt76_rxq_id q, - struct sk_buff *skb) + struct sk_buff *skb, u32 *info) { struct mt7915_dev *dev = container_of(mdev, struct mt7915_dev, mt76); __le32 *rxd = (__le32 *)skb->data; @@ -1130,7 +1161,7 @@ void mt7915_queue_rx_skb(struct mt76_dev *mdev, enum mt76_rxq_id q, dev_kfree_skb(skb); break; case PKT_TYPE_NORMAL: - if (!mt7915_mac_fill_rx(dev, skb)) { + if (!mt7915_mac_fill_rx(dev, skb, q, info)) { mt76_rx(&dev->mt76, q, skb); return; } diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h b/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h index 9cb680e7f223..460be184e617 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h @@ -617,7 +617,7 @@ int mt7915_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr, struct mt76_tx_info *tx_info); void mt7915_tx_token_put(struct mt7915_dev *dev); void mt7915_queue_rx_skb(struct mt76_dev *mdev, enum mt76_rxq_id q, - struct sk_buff *skb); + struct sk_buff *skb, u32 *info); bool mt7915_rx_check(struct mt76_dev *mdev, void *data, int len); void mt7915_sta_ps(struct mt76_dev *mdev, struct ieee80211_sta *sta, bool ps); void mt7915_stats_work(struct work_struct *work); diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/mac.c b/drivers/net/wireless/mediatek/mt76/mt7921/mac.c index 443a4a49bc9e..82db3762be33 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7921/mac.c @@ -684,7 +684,7 @@ bool mt7921_rx_check(struct mt76_dev *mdev, void *data, int len) EXPORT_SYMBOL_GPL(mt7921_rx_check); void mt7921_queue_rx_skb(struct mt76_dev *mdev, enum mt76_rxq_id q, - struct sk_buff *skb) + struct sk_buff *skb, u32 *info) { struct mt7921_dev *dev = container_of(mdev, struct mt7921_dev, mt76); __le32 *rxd = (__le32 *)skb->data; diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/mt7921.h b/drivers/net/wireless/mediatek/mt76/mt7921/mt7921.h index 2398c6fd0b39..6fc04ed34ec3 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/mt7921.h +++ b/drivers/net/wireless/mediatek/mt76/mt7921/mt7921.h @@ -470,7 +470,7 @@ void mt7921_tx_worker(struct mt76_worker *w); void mt7921_tx_token_put(struct mt7921_dev *dev); bool mt7921_rx_check(struct mt76_dev *mdev, void *data, int len); void mt7921_queue_rx_skb(struct mt76_dev *mdev, enum mt76_rxq_id q, - struct sk_buff *skb); + struct sk_buff *skb, u32 *info); void mt7921_sta_ps(struct mt76_dev *mdev, struct ieee80211_sta *sta, bool ps); void mt7921_stats_work(struct work_struct *work); void mt7921_set_stream_he_caps(struct mt7921_phy *phy); diff --git a/drivers/net/wireless/mediatek/mt76/sdio.c b/drivers/net/wireless/mediatek/mt76/sdio.c index 0ec308f99af5..228bc7d45011 100644 --- a/drivers/net/wireless/mediatek/mt76/sdio.c +++ b/drivers/net/wireless/mediatek/mt76/sdio.c @@ -395,7 +395,7 @@ mt76s_process_rx_queue(struct mt76_dev *dev, struct mt76_queue *q) if (!e || !e->skb) break; - dev->drv->rx_skb(dev, MT_RXQ_MAIN, e->skb); + dev->drv->rx_skb(dev, MT_RXQ_MAIN, e->skb, NULL); e->skb = NULL; nframes++; } diff --git a/drivers/net/wireless/mediatek/mt76/usb.c b/drivers/net/wireless/mediatek/mt76/usb.c index 0597df2729a6..3e281715fcd4 100644 --- a/drivers/net/wireless/mediatek/mt76/usb.c +++ b/drivers/net/wireless/mediatek/mt76/usb.c @@ -547,7 +547,7 @@ mt76u_process_rx_entry(struct mt76_dev *dev, struct urb *urb, len -= data_len; nsgs++; } - dev->drv->rx_skb(dev, MT_RXQ_MAIN, skb); + dev->drv->rx_skb(dev, MT_RXQ_MAIN, skb, NULL); return nsgs; } From d1369e515efe6b5d04e9a5f5884106643f7b1d8a Mon Sep 17 00:00:00 2001 From: Sujuan Chen Date: Sat, 12 Nov 2022 16:40:39 +0100 Subject: [PATCH 1502/2002] wifi: mt76: connac: introduce mt76_connac_mcu_sta_wed_update utility routine This is a preliminary patch to introduce WED RX support for mt7915. Tested-by: Daniel Golle Co-developed-by: Lorenzo Bianconi Signed-off-by: Lorenzo Bianconi Signed-off-by: Sujuan Chen Signed-off-by: Felix Fietkau --- .../wireless/mediatek/mt76/mt76_connac_mcu.c | 22 +++++++++++++++++++ .../wireless/mediatek/mt76/mt76_connac_mcu.h | 1 + 2 files changed, 23 insertions(+) diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c index b8480d97c0f8..96d91238ad8d 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c @@ -1219,6 +1219,16 @@ void mt76_connac_mcu_sta_ba_tlv(struct sk_buff *skb, } EXPORT_SYMBOL_GPL(mt76_connac_mcu_sta_ba_tlv); +int mt76_connac_mcu_sta_wed_update(struct mt76_dev *dev, struct sk_buff *skb) +{ + if (!mtk_wed_device_active(&dev->mmio.wed)) + return 0; + + return mtk_wed_device_update_msg(&dev->mmio.wed, WED_WO_STA_REC, + skb->data, skb->len); +} +EXPORT_SYMBOL_GPL(mt76_connac_mcu_sta_wed_update); + int mt76_connac_mcu_sta_ba(struct mt76_dev *dev, struct mt76_vif *mvif, struct ieee80211_ampdu_params *params, int cmd, bool enable, bool tx) @@ -1244,6 +1254,10 @@ int mt76_connac_mcu_sta_ba(struct mt76_dev *dev, struct mt76_vif *mvif, mt76_connac_mcu_wtbl_ba_tlv(dev, skb, params, enable, tx, sta_wtbl, wtbl_hdr); + ret = mt76_connac_mcu_sta_wed_update(dev, skb); + if (ret) + return ret; + ret = mt76_mcu_skb_send_msg(dev, skb, cmd, true); if (ret) return ret; @@ -1254,6 +1268,10 @@ int mt76_connac_mcu_sta_ba(struct mt76_dev *dev, struct mt76_vif *mvif, mt76_connac_mcu_sta_ba_tlv(skb, params, enable, tx); + ret = mt76_connac_mcu_sta_wed_update(dev, skb); + if (ret) + return ret; + return mt76_mcu_skb_send_msg(dev, skb, cmd, true); } EXPORT_SYMBOL_GPL(mt76_connac_mcu_sta_ba); @@ -2696,6 +2714,10 @@ int mt76_connac_mcu_add_key(struct mt76_dev *dev, struct ieee80211_vif *vif, if (ret) return ret; + ret = mt76_connac_mcu_sta_wed_update(dev, skb); + if (ret) + return ret; + return mt76_mcu_skb_send_msg(dev, skb, mcu_cmd, true); } EXPORT_SYMBOL_GPL(mt76_connac_mcu_add_key); diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h index e8049ac12a84..f1e942b9a887 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h +++ b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h @@ -1891,6 +1891,7 @@ int mt76_connac_mcu_set_pm(struct mt76_dev *dev, int band, int enter); int mt76_connac_mcu_restart(struct mt76_dev *dev); int mt76_connac_mcu_rdd_cmd(struct mt76_dev *dev, int cmd, u8 index, u8 rx_sel, u8 val); +int mt76_connac_mcu_sta_wed_update(struct mt76_dev *dev, struct sk_buff *skb); int mt76_connac2_load_ram(struct mt76_dev *dev, const char *fw_wm, const char *fw_wa); int mt76_connac2_load_patch(struct mt76_dev *dev, const char *fw_name); From 4f831d18d12da80cec0bebe5b8ca8702a528195a Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Sat, 12 Nov 2022 16:40:40 +0100 Subject: [PATCH 1503/2002] wifi: mt76: mt7915: enable WED RX support Enable RX Wireless Ethernet Dispatch available on MT7986 Soc in oreder to offlad traffic received by WLAN NIC and forwarded to LAN/WAN one. Tested-by: Daniel Golle Co-developed-by: Sujuan Chen Signed-off-by: Sujuan Chen Signed-off-by: Lorenzo Bianconi Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mac80211.c | 5 +- .../net/wireless/mediatek/mt76/mt7915/dma.c | 20 +- .../net/wireless/mediatek/mt76/mt7915/mcu.c | 4 + .../net/wireless/mediatek/mt76/mt7915/mmio.c | 293 ++++++++++++------ .../wireless/mediatek/mt76/mt7915/mt7915.h | 2 + .../net/wireless/mediatek/mt76/mt7915/regs.h | 7 + 6 files changed, 238 insertions(+), 93 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mac80211.c b/drivers/net/wireless/mediatek/mt76/mac80211.c index 3cd37a013dcc..fc608b369b3c 100644 --- a/drivers/net/wireless/mediatek/mt76/mac80211.c +++ b/drivers/net/wireless/mediatek/mt76/mac80211.c @@ -1297,7 +1297,10 @@ void mt76_rx_poll_complete(struct mt76_dev *dev, enum mt76_rxq_id q, while ((skb = __skb_dequeue(&dev->rx_skb[q])) != NULL) { mt76_check_sta(dev, skb); - mt76_rx_aggr_reorder(skb, &frames); + if (mtk_wed_device_active(&dev->mmio.wed)) + __skb_queue_tail(&frames, skb); + else + mt76_rx_aggr_reorder(skb, &frames); } mt76_rx_complete(dev, &frames, napi); diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/dma.c b/drivers/net/wireless/mediatek/mt76/mt7915/dma.c index 38360f940747..e102a717fc01 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/dma.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/dma.c @@ -365,7 +365,8 @@ static int mt7915_dma_enable(struct mt7915_dev *dev) wed_irq_mask |= MT_INT_TX_DONE_BAND0 | MT_INT_TX_DONE_BAND1; if (!is_mt7986(&dev->mt76)) mt76_wr(dev, MT_INT_WED_MASK_CSR, wed_irq_mask); - mt76_wr(dev, MT_INT_MASK_CSR, wed_irq_mask); + else + mt76_wr(dev, MT_INT_MASK_CSR, wed_irq_mask); mtk_wed_device_start(&dev->mt76.mmio.wed, wed_irq_mask); } @@ -401,6 +402,9 @@ int mt7915_dma_init(struct mt7915_dev *dev, struct mt7915_phy *phy2) FIELD_PREP(MT_WFDMA_WED_RING_CONTROL_TX1, 19) | FIELD_PREP(MT_WFDMA_WED_RING_CONTROL_RX1, wed_control_rx1)); + if (is_mt7915(mdev)) + mt76_rmw(dev, MT_WFDMA0_EXT0_CFG, MT_WFDMA0_EXT0_RXWB_KEEP, + MT_WFDMA0_EXT0_RXWB_KEEP); } } else { mt76_clear(dev, MT_WFDMA_HOST_CONFIG, MT_WFDMA_HOST_CONFIG_WED); @@ -473,6 +477,13 @@ int mt7915_dma_init(struct mt7915_dev *dev, struct mt7915_phy *phy2) /* rx data queue for band0 */ if (!dev->phy.band_idx) { + if (mtk_wed_device_active(&mdev->mmio.wed) && + mtk_wed_get_rx_capa(&mdev->mmio.wed)) { + dev->mt76.q_rx[MT_RXQ_MAIN].flags = + MT_WED_Q_RX(MT7915_RXQ_BAND0); + dev->mt76.rx_token_size += MT7915_RX_RING_SIZE; + } + ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_MAIN], MT_RXQ_ID(MT_RXQ_MAIN), MT7915_RX_RING_SIZE, @@ -503,6 +514,13 @@ int mt7915_dma_init(struct mt7915_dev *dev, struct mt7915_phy *phy2) } if (dev->dbdc_support || dev->phy.band_idx) { + if (mtk_wed_device_active(&mdev->mmio.wed) && + mtk_wed_get_rx_capa(&mdev->mmio.wed)) { + dev->mt76.q_rx[MT_RXQ_BAND1].flags = + MT_WED_Q_RX(MT7915_RXQ_BAND1); + dev->mt76.rx_token_size += MT7915_RX_RING_SIZE; + } + /* rx data queue for band1 */ ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_BAND1], MT_RXQ_ID(MT_RXQ_BAND1), diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c index 8b903a7636a8..c49efe20bf5e 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c @@ -1677,6 +1677,10 @@ int mt7915_mcu_add_sta(struct mt7915_dev *dev, struct ieee80211_vif *vif, return ret; } out: + ret = mt76_connac_mcu_sta_wed_update(&dev->mt76, skb); + if (ret) + return ret; + return mt76_mcu_skb_send_msg(&dev->mt76, skb, MCU_EXT_CMD(STA_REC_UPDATE), true); } diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mmio.c b/drivers/net/wireless/mediatek/mt76/mt7915/mmio.c index 3c840853a2c9..04f38755056f 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mmio.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mmio.c @@ -9,107 +9,111 @@ #include "mt7915.h" #include "mac.h" #include "../trace.h" +#include "../dma.h" static bool wed_enable; module_param(wed_enable, bool, 0644); static const u32 mt7915_reg[] = { - [INT_SOURCE_CSR] = 0xd7010, - [INT_MASK_CSR] = 0xd7014, - [INT1_SOURCE_CSR] = 0xd7088, - [INT1_MASK_CSR] = 0xd708c, - [INT_MCU_CMD_SOURCE] = 0xd51f0, - [INT_MCU_CMD_EVENT] = 0x3108, - [WFDMA0_ADDR] = 0xd4000, - [WFDMA0_PCIE1_ADDR] = 0xd8000, - [WFDMA_EXT_CSR_ADDR] = 0xd7000, - [CBTOP1_PHY_END] = 0x77ffffff, - [INFRA_MCU_ADDR_END] = 0x7c3fffff, - [FW_ASSERT_STAT_ADDR] = 0x219848, - [FW_EXCEPT_TYPE_ADDR] = 0x21987c, - [FW_EXCEPT_COUNT_ADDR] = 0x219848, - [FW_CIRQ_COUNT_ADDR] = 0x216f94, - [FW_CIRQ_IDX_ADDR] = 0x216ef8, - [FW_CIRQ_LISR_ADDR] = 0x2170ac, - [FW_TASK_ID_ADDR] = 0x216f90, - [FW_TASK_IDX_ADDR] = 0x216f9c, - [FW_TASK_QID1_ADDR] = 0x219680, - [FW_TASK_QID2_ADDR] = 0x219760, - [FW_TASK_START_ADDR] = 0x219558, - [FW_TASK_END_ADDR] = 0x219554, - [FW_TASK_SIZE_ADDR] = 0x219560, - [FW_LAST_MSG_ID_ADDR] = 0x216f70, - [FW_EINT_INFO_ADDR] = 0x219818, - [FW_SCHED_INFO_ADDR] = 0x219828, - [SWDEF_BASE_ADDR] = 0x41f200, - [TXQ_WED_RING_BASE] = 0xd7300, - [RXQ_WED_RING_BASE] = 0xd7410, + [INT_SOURCE_CSR] = 0xd7010, + [INT_MASK_CSR] = 0xd7014, + [INT1_SOURCE_CSR] = 0xd7088, + [INT1_MASK_CSR] = 0xd708c, + [INT_MCU_CMD_SOURCE] = 0xd51f0, + [INT_MCU_CMD_EVENT] = 0x3108, + [WFDMA0_ADDR] = 0xd4000, + [WFDMA0_PCIE1_ADDR] = 0xd8000, + [WFDMA_EXT_CSR_ADDR] = 0xd7000, + [CBTOP1_PHY_END] = 0x77ffffff, + [INFRA_MCU_ADDR_END] = 0x7c3fffff, + [FW_ASSERT_STAT_ADDR] = 0x219848, + [FW_EXCEPT_TYPE_ADDR] = 0x21987c, + [FW_EXCEPT_COUNT_ADDR] = 0x219848, + [FW_CIRQ_COUNT_ADDR] = 0x216f94, + [FW_CIRQ_IDX_ADDR] = 0x216ef8, + [FW_CIRQ_LISR_ADDR] = 0x2170ac, + [FW_TASK_ID_ADDR] = 0x216f90, + [FW_TASK_IDX_ADDR] = 0x216f9c, + [FW_TASK_QID1_ADDR] = 0x219680, + [FW_TASK_QID2_ADDR] = 0x219760, + [FW_TASK_START_ADDR] = 0x219558, + [FW_TASK_END_ADDR] = 0x219554, + [FW_TASK_SIZE_ADDR] = 0x219560, + [FW_LAST_MSG_ID_ADDR] = 0x216f70, + [FW_EINT_INFO_ADDR] = 0x219818, + [FW_SCHED_INFO_ADDR] = 0x219828, + [SWDEF_BASE_ADDR] = 0x41f200, + [TXQ_WED_RING_BASE] = 0xd7300, + [RXQ_WED_RING_BASE] = 0xd7410, + [RXQ_WED_DATA_RING_BASE] = 0xd4500, }; static const u32 mt7916_reg[] = { - [INT_SOURCE_CSR] = 0xd4200, - [INT_MASK_CSR] = 0xd4204, - [INT1_SOURCE_CSR] = 0xd8200, - [INT1_MASK_CSR] = 0xd8204, - [INT_MCU_CMD_SOURCE] = 0xd41f0, - [INT_MCU_CMD_EVENT] = 0x2108, - [WFDMA0_ADDR] = 0xd4000, - [WFDMA0_PCIE1_ADDR] = 0xd8000, - [WFDMA_EXT_CSR_ADDR] = 0xd7000, - [CBTOP1_PHY_END] = 0x7fffffff, - [INFRA_MCU_ADDR_END] = 0x7c085fff, - [FW_ASSERT_STAT_ADDR] = 0x02204c14, - [FW_EXCEPT_TYPE_ADDR] = 0x022051a4, - [FW_EXCEPT_COUNT_ADDR] = 0x022050bc, - [FW_CIRQ_COUNT_ADDR] = 0x022001ac, - [FW_CIRQ_IDX_ADDR] = 0x02204f84, - [FW_CIRQ_LISR_ADDR] = 0x022050d0, - [FW_TASK_ID_ADDR] = 0x0220406c, - [FW_TASK_IDX_ADDR] = 0x0220500c, - [FW_TASK_QID1_ADDR] = 0x022028c8, - [FW_TASK_QID2_ADDR] = 0x02202a38, - [FW_TASK_START_ADDR] = 0x0220286c, - [FW_TASK_END_ADDR] = 0x02202870, - [FW_TASK_SIZE_ADDR] = 0x02202878, - [FW_LAST_MSG_ID_ADDR] = 0x02204fe8, - [FW_EINT_INFO_ADDR] = 0x0220525c, - [FW_SCHED_INFO_ADDR] = 0x0220516c, - [SWDEF_BASE_ADDR] = 0x411400, - [TXQ_WED_RING_BASE] = 0xd7300, - [RXQ_WED_RING_BASE] = 0xd7410, + [INT_SOURCE_CSR] = 0xd4200, + [INT_MASK_CSR] = 0xd4204, + [INT1_SOURCE_CSR] = 0xd8200, + [INT1_MASK_CSR] = 0xd8204, + [INT_MCU_CMD_SOURCE] = 0xd41f0, + [INT_MCU_CMD_EVENT] = 0x2108, + [WFDMA0_ADDR] = 0xd4000, + [WFDMA0_PCIE1_ADDR] = 0xd8000, + [WFDMA_EXT_CSR_ADDR] = 0xd7000, + [CBTOP1_PHY_END] = 0x7fffffff, + [INFRA_MCU_ADDR_END] = 0x7c085fff, + [FW_ASSERT_STAT_ADDR] = 0x02204c14, + [FW_EXCEPT_TYPE_ADDR] = 0x022051a4, + [FW_EXCEPT_COUNT_ADDR] = 0x022050bc, + [FW_CIRQ_COUNT_ADDR] = 0x022001ac, + [FW_CIRQ_IDX_ADDR] = 0x02204f84, + [FW_CIRQ_LISR_ADDR] = 0x022050d0, + [FW_TASK_ID_ADDR] = 0x0220406c, + [FW_TASK_IDX_ADDR] = 0x0220500c, + [FW_TASK_QID1_ADDR] = 0x022028c8, + [FW_TASK_QID2_ADDR] = 0x02202a38, + [FW_TASK_START_ADDR] = 0x0220286c, + [FW_TASK_END_ADDR] = 0x02202870, + [FW_TASK_SIZE_ADDR] = 0x02202878, + [FW_LAST_MSG_ID_ADDR] = 0x02204fe8, + [FW_EINT_INFO_ADDR] = 0x0220525c, + [FW_SCHED_INFO_ADDR] = 0x0220516c, + [SWDEF_BASE_ADDR] = 0x411400, + [TXQ_WED_RING_BASE] = 0xd7300, + [RXQ_WED_RING_BASE] = 0xd7410, + [RXQ_WED_DATA_RING_BASE] = 0xd4540, }; static const u32 mt7986_reg[] = { - [INT_SOURCE_CSR] = 0x24200, - [INT_MASK_CSR] = 0x24204, - [INT1_SOURCE_CSR] = 0x28200, - [INT1_MASK_CSR] = 0x28204, - [INT_MCU_CMD_SOURCE] = 0x241f0, - [INT_MCU_CMD_EVENT] = 0x54000108, - [WFDMA0_ADDR] = 0x24000, - [WFDMA0_PCIE1_ADDR] = 0x28000, - [WFDMA_EXT_CSR_ADDR] = 0x27000, - [CBTOP1_PHY_END] = 0x7fffffff, - [INFRA_MCU_ADDR_END] = 0x7c085fff, - [FW_ASSERT_STAT_ADDR] = 0x02204b54, - [FW_EXCEPT_TYPE_ADDR] = 0x022050dc, - [FW_EXCEPT_COUNT_ADDR] = 0x02204ffc, - [FW_CIRQ_COUNT_ADDR] = 0x022001ac, - [FW_CIRQ_IDX_ADDR] = 0x02204ec4, - [FW_CIRQ_LISR_ADDR] = 0x02205010, - [FW_TASK_ID_ADDR] = 0x02204fac, - [FW_TASK_IDX_ADDR] = 0x02204f4c, - [FW_TASK_QID1_ADDR] = 0x02202814, - [FW_TASK_QID2_ADDR] = 0x02202984, - [FW_TASK_START_ADDR] = 0x022027b8, - [FW_TASK_END_ADDR] = 0x022027bc, - [FW_TASK_SIZE_ADDR] = 0x022027c4, - [FW_LAST_MSG_ID_ADDR] = 0x02204f28, - [FW_EINT_INFO_ADDR] = 0x02205194, - [FW_SCHED_INFO_ADDR] = 0x022051a4, - [SWDEF_BASE_ADDR] = 0x411400, - [TXQ_WED_RING_BASE] = 0x24420, - [RXQ_WED_RING_BASE] = 0x24520, + [INT_SOURCE_CSR] = 0x24200, + [INT_MASK_CSR] = 0x24204, + [INT1_SOURCE_CSR] = 0x28200, + [INT1_MASK_CSR] = 0x28204, + [INT_MCU_CMD_SOURCE] = 0x241f0, + [INT_MCU_CMD_EVENT] = 0x54000108, + [WFDMA0_ADDR] = 0x24000, + [WFDMA0_PCIE1_ADDR] = 0x28000, + [WFDMA_EXT_CSR_ADDR] = 0x27000, + [CBTOP1_PHY_END] = 0x7fffffff, + [INFRA_MCU_ADDR_END] = 0x7c085fff, + [FW_ASSERT_STAT_ADDR] = 0x02204b54, + [FW_EXCEPT_TYPE_ADDR] = 0x022050dc, + [FW_EXCEPT_COUNT_ADDR] = 0x02204ffc, + [FW_CIRQ_COUNT_ADDR] = 0x022001ac, + [FW_CIRQ_IDX_ADDR] = 0x02204ec4, + [FW_CIRQ_LISR_ADDR] = 0x02205010, + [FW_TASK_ID_ADDR] = 0x02204fac, + [FW_TASK_IDX_ADDR] = 0x02204f4c, + [FW_TASK_QID1_ADDR] = 0x02202814, + [FW_TASK_QID2_ADDR] = 0x02202984, + [FW_TASK_START_ADDR] = 0x022027b8, + [FW_TASK_END_ADDR] = 0x022027bc, + [FW_TASK_SIZE_ADDR] = 0x022027c4, + [FW_LAST_MSG_ID_ADDR] = 0x02204f28, + [FW_EINT_INFO_ADDR] = 0x02205194, + [FW_SCHED_INFO_ADDR] = 0x022051a4, + [SWDEF_BASE_ADDR] = 0x411400, + [TXQ_WED_RING_BASE] = 0x24420, + [RXQ_WED_RING_BASE] = 0x24520, + [RXQ_WED_DATA_RING_BASE] = 0x24540, }; static const u32 mt7915_offs[] = { @@ -585,6 +589,80 @@ static void mt7915_mmio_wed_offload_disable(struct mtk_wed_device *wed) mt76_clear(dev, MT_AGG_ACR4(phy->band_idx), MT_AGG_ACR_PPDU_TXS2H); } + +static void mt7915_wed_release_rx_buf(struct mtk_wed_device *wed) +{ + struct mt7915_dev *dev; + struct page *page; + int i; + + dev = container_of(wed, struct mt7915_dev, mt76.mmio.wed); + for (i = 0; i < dev->mt76.rx_token_size; i++) { + struct mt76_txwi_cache *t; + + t = mt76_rx_token_release(&dev->mt76, i); + if (!t || !t->ptr) + continue; + + dma_unmap_single(dev->mt76.dma_dev, t->dma_addr, + wed->wlan.rx_size, DMA_FROM_DEVICE); + skb_free_frag(t->ptr); + t->ptr = NULL; + + mt76_put_rxwi(&dev->mt76, t); + } + + if (!wed->rx_buf_ring.rx_page.va) + return; + + page = virt_to_page(wed->rx_buf_ring.rx_page.va); + __page_frag_cache_drain(page, wed->rx_buf_ring.rx_page.pagecnt_bias); + memset(&wed->rx_buf_ring.rx_page, 0, sizeof(wed->rx_buf_ring.rx_page)); +} + +static u32 mt7915_wed_init_rx_buf(struct mtk_wed_device *wed, int size) +{ + struct mtk_rxbm_desc *desc = wed->rx_buf_ring.desc; + struct mt7915_dev *dev; + u32 length; + int i; + + dev = container_of(wed, struct mt7915_dev, mt76.mmio.wed); + length = SKB_DATA_ALIGN(NET_SKB_PAD + wed->wlan.rx_size + + sizeof(struct skb_shared_info)); + + for (i = 0; i < size; i++) { + struct mt76_txwi_cache *t = mt76_get_rxwi(&dev->mt76); + dma_addr_t phy_addr; + int token; + void *ptr; + + ptr = page_frag_alloc(&wed->rx_buf_ring.rx_page, length, + GFP_KERNEL); + if (!ptr) + goto unmap; + + phy_addr = dma_map_single(dev->mt76.dma_dev, ptr, + wed->wlan.rx_size, + DMA_TO_DEVICE); + if (unlikely(dma_mapping_error(dev->mt76.dev, phy_addr))) { + skb_free_frag(ptr); + goto unmap; + } + + desc->buf0 = cpu_to_le32(phy_addr); + token = mt76_rx_token_consume(&dev->mt76, ptr, t, phy_addr); + desc->token |= cpu_to_le32(FIELD_PREP(MT_DMA_CTL_TOKEN, + token)); + desc++; + } + + return 0; + +unmap: + mt7915_wed_release_rx_buf(wed); + return -ENOMEM; +} #endif int mt7915_mmio_wed_init(struct mt7915_dev *dev, void *pdev_ptr, @@ -602,6 +680,10 @@ int mt7915_mmio_wed_init(struct mt7915_dev *dev, void *pdev_ptr, wed->wlan.pci_dev = pci_dev; wed->wlan.bus_type = MTK_WED_BUS_PCIE; + wed->wlan.base = devm_ioremap(dev->mt76.dev, + pci_resource_start(pci_dev, 0), + pci_resource_len(pci_dev, 0)); + wed->wlan.phy_base = pci_resource_start(pci_dev, 0); wed->wlan.wpdma_int = pci_resource_start(pci_dev, 0) + MT_INT_WED_SOURCE_CSR; wed->wlan.wpdma_mask = pci_resource_start(pci_dev, 0) + @@ -612,6 +694,10 @@ int mt7915_mmio_wed_init(struct mt7915_dev *dev, void *pdev_ptr, MT_TXQ_WED_RING_BASE; wed->wlan.wpdma_txfree = pci_resource_start(pci_dev, 0) + MT_RXQ_WED_RING_BASE; + wed->wlan.wpdma_rx_glo = pci_resource_start(pci_dev, 0) + + MT_WPDMA_GLO_CFG; + wed->wlan.wpdma_rx = pci_resource_start(pci_dev, 0) + + MT_RXQ_WED_DATA_RING_BASE; } else { struct platform_device *plat_dev = pdev_ptr; struct resource *res; @@ -622,19 +708,44 @@ int mt7915_mmio_wed_init(struct mt7915_dev *dev, void *pdev_ptr, wed->wlan.platform_dev = plat_dev; wed->wlan.bus_type = MTK_WED_BUS_AXI; + wed->wlan.base = devm_ioremap(dev->mt76.dev, res->start, + resource_size(res)); + wed->wlan.phy_base = res->start; wed->wlan.wpdma_int = res->start + MT_INT_SOURCE_CSR; wed->wlan.wpdma_mask = res->start + MT_INT_MASK_CSR; wed->wlan.wpdma_tx = res->start + MT_TXQ_WED_RING_BASE; wed->wlan.wpdma_txfree = res->start + MT_RXQ_WED_RING_BASE; + wed->wlan.wpdma_rx_glo = res->start + MT_WPDMA_GLO_CFG; + wed->wlan.wpdma_rx = res->start + MT_RXQ_WED_DATA_RING_BASE; } wed->wlan.nbuf = 4096; wed->wlan.tx_tbit[0] = is_mt7915(&dev->mt76) ? 4 : 30; wed->wlan.tx_tbit[1] = is_mt7915(&dev->mt76) ? 5 : 31; - wed->wlan.txfree_tbit = is_mt7915(&dev->mt76) ? 1 : 2; + wed->wlan.txfree_tbit = is_mt7986(&dev->mt76) ? 2 : 1; wed->wlan.token_start = MT7915_TOKEN_SIZE - wed->wlan.nbuf; + wed->wlan.wcid_512 = !is_mt7915(&dev->mt76); + + wed->wlan.rx_nbuf = 65536; + wed->wlan.rx_npkt = MT7915_WED_RX_TOKEN_SIZE; + wed->wlan.rx_size = SKB_WITH_OVERHEAD(MT_RX_BUF_SIZE); + if (is_mt7915(&dev->mt76)) { + wed->wlan.rx_tbit[0] = 16; + wed->wlan.rx_tbit[1] = 17; + } else if (is_mt7986(&dev->mt76)) { + wed->wlan.rx_tbit[0] = 22; + wed->wlan.rx_tbit[1] = 23; + } else { + wed->wlan.rx_tbit[0] = 18; + wed->wlan.rx_tbit[1] = 19; + } + wed->wlan.init_buf = mt7915_wed_init_buf; wed->wlan.offload_enable = mt7915_mmio_wed_offload_enable; wed->wlan.offload_disable = mt7915_mmio_wed_offload_disable; + wed->wlan.init_rx_buf = mt7915_wed_init_rx_buf; + wed->wlan.release_rx_buf = mt7915_wed_release_rx_buf; + + dev->mt76.rx_token_size = wed->wlan.rx_npkt; if (mtk_wed_device_attach(wed)) return 0; diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h b/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h index 460be184e617..574f712b5fe1 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h @@ -68,6 +68,8 @@ #define MT7915_MIN_TWT_DUR 64 #define MT7915_MAX_QUEUE (MT_RXQ_BAND2 + __MT_MCUQ_MAX + 2) +#define MT7915_WED_RX_TOKEN_SIZE 12288 + struct mt7915_vif; struct mt7915_sta; struct mt7915_dfs_pulse; diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/regs.h b/drivers/net/wireless/mediatek/mt76/mt7915/regs.h index 0c61f1256f3b..42a19e7b95d6 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/regs.h +++ b/drivers/net/wireless/mediatek/mt76/mt7915/regs.h @@ -43,6 +43,7 @@ enum reg_rev { SWDEF_BASE_ADDR, TXQ_WED_RING_BASE, RXQ_WED_RING_BASE, + RXQ_WED_DATA_RING_BASE, __MT_REG_MAX, }; @@ -588,9 +589,14 @@ enum offs_rev { #define MT_WFDMA0_GLO_CFG_OMIT_RX_INFO_PFET2 BIT(21) #define MT_WFDMA0_RST_DTX_PTR MT_WFDMA0(0x20c) + +#define MT_WFDMA0_EXT0_CFG MT_WFDMA0(0x2b0) +#define MT_WFDMA0_EXT0_RXWB_KEEP BIT(10) + #define MT_WFDMA0_PRI_DLY_INT_CFG0 MT_WFDMA0(0x2f0) #define MT_WFDMA0_PRI_DLY_INT_CFG1 MT_WFDMA0(0x2f4) #define MT_WFDMA0_PRI_DLY_INT_CFG2 MT_WFDMA0(0x2f8) +#define MT_WPDMA_GLO_CFG MT_WFDMA0(0x208) /* WFDMA1 */ #define MT_WFDMA1_BASE 0xd5000 @@ -686,6 +692,7 @@ enum offs_rev { #define MT_TXQ_WED_RING_BASE __REG(TXQ_WED_RING_BASE) #define MT_RXQ_WED_RING_BASE __REG(RXQ_WED_RING_BASE) +#define MT_RXQ_WED_DATA_RING_BASE __REG(RXQ_WED_DATA_RING_BASE) #define MT_INT_SOURCE_CSR __REG(INT_SOURCE_CSR) #define MT_INT_MASK_CSR __REG(INT_MASK_CSR) From c6cde7b751ee6fb0d4c2216c2edd9b0b5ffee04d Mon Sep 17 00:00:00 2001 From: Sujuan Chen Date: Sat, 12 Nov 2022 16:40:41 +0100 Subject: [PATCH 1504/2002] wifi: mt76: mt7915: enable WED RX stats Introduce the capability to report WED RX stats to mac80211. Tested-by: Daniel Golle Co-developed-by: Lorenzo Bianconi Signed-off-by: Lorenzo Bianconi Signed-off-by: Sujuan Chen Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt76.h | 6 +++++ .../net/wireless/mediatek/mt76/mt7915/dma.c | 6 +++++ .../net/wireless/mediatek/mt76/mt7915/main.c | 8 ++++++ .../net/wireless/mediatek/mt76/mt7915/mcu.c | 18 +++++++++++++ .../net/wireless/mediatek/mt76/mt7915/mmio.c | 26 +++++++++++++++++++ .../wireless/mediatek/mt76/mt7915/mt7915.h | 1 + 6 files changed, 65 insertions(+) diff --git a/drivers/net/wireless/mediatek/mt76/mt76.h b/drivers/net/wireless/mediatek/mt76/mt76.h index bf4ad629df3f..33f87e518d68 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76.h +++ b/drivers/net/wireless/mediatek/mt76/mt76.h @@ -273,9 +273,15 @@ struct mt76_sta_stats { u64 tx_nss[4]; /* 1, 2, 3, 4 */ u64 tx_mcs[16]; /* mcs idx */ u64 tx_bytes; + /* WED TX */ u32 tx_packets; u32 tx_retries; u32 tx_failed; + /* WED RX */ + u64 rx_bytes; + u32 rx_packets; + u32 rx_errors; + u32 rx_drops; }; enum mt76_wcid_flags { diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/dma.c b/drivers/net/wireless/mediatek/mt76/mt7915/dma.c index e102a717fc01..ae5be28fdd9d 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/dma.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/dma.c @@ -361,12 +361,18 @@ static int mt7915_dma_enable(struct mt7915_dev *dev) if (mtk_wed_device_active(&dev->mt76.mmio.wed)) { u32 wed_irq_mask = irq_mask; + int ret; wed_irq_mask |= MT_INT_TX_DONE_BAND0 | MT_INT_TX_DONE_BAND1; if (!is_mt7986(&dev->mt76)) mt76_wr(dev, MT_INT_WED_MASK_CSR, wed_irq_mask); else mt76_wr(dev, MT_INT_MASK_CSR, wed_irq_mask); + + ret = mt7915_mcu_wed_enable_rx_stats(dev); + if (ret) + return ret; + mtk_wed_device_start(&dev->mt76.mmio.wed, wed_irq_mask); } diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/main.c b/drivers/net/wireless/mediatek/mt76/mt7915/main.c index 3933f4f2d71d..90c5d8fe4f73 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/main.c @@ -1037,6 +1037,14 @@ static void mt7915_sta_statistics(struct ieee80211_hw *hw, sinfo->tx_retries = msta->wcid.stats.tx_retries; sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_RETRIES); + + if (mtk_wed_get_rx_capa(&phy->dev->mt76.mmio.wed)) { + sinfo->rx_bytes = msta->wcid.stats.rx_bytes; + sinfo->filled |= BIT_ULL(NL80211_STA_INFO_RX_BYTES64); + + sinfo->rx_packets = msta->wcid.stats.rx_packets; + sinfo->filled |= BIT_ULL(NL80211_STA_INFO_RX_PACKETS); + } } sinfo->ack_signal = (s8)msta->ack_signal; diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c index c49efe20bf5e..2769d6c897d9 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c @@ -1685,6 +1685,24 @@ out: MCU_EXT_CMD(STA_REC_UPDATE), true); } +int mt7915_mcu_wed_enable_rx_stats(struct mt7915_dev *dev) +{ +#ifdef CONFIG_NET_MEDIATEK_SOC_WED + struct mtk_wed_device *wed = &dev->mt76.mmio.wed; + struct { + __le32 args[2]; + } req = { + .args[0] = cpu_to_le32(1), + .args[1] = cpu_to_le32(6), + }; + + return mtk_wed_device_update_msg(wed, MTK_WED_WO_CMD_RXCNT_CTRL, + &req, sizeof(req)); +#else + return 0; +#endif +} + int mt7915_mcu_add_dev_info(struct mt7915_phy *phy, struct ieee80211_vif *vif, bool enable) { diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mmio.c b/drivers/net/wireless/mediatek/mt76/mt7915/mmio.c index 04f38755056f..1fcf34f57a16 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mmio.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mmio.c @@ -663,6 +663,31 @@ unmap: mt7915_wed_release_rx_buf(wed); return -ENOMEM; } + +static void mt7915_mmio_wed_update_rx_stats(struct mtk_wed_device *wed, + struct mtk_wed_wo_rx_stats *stats) +{ + int idx = le16_to_cpu(stats->wlan_idx); + struct mt7915_dev *dev; + struct mt76_wcid *wcid; + + dev = container_of(wed, struct mt7915_dev, mt76.mmio.wed); + + if (idx >= mt7915_wtbl_size(dev)) + return; + + rcu_read_lock(); + + wcid = rcu_dereference(dev->mt76.wcid[idx]); + if (wcid) { + wcid->stats.rx_bytes += le32_to_cpu(stats->rx_byte_cnt); + wcid->stats.rx_packets += le32_to_cpu(stats->rx_pkt_cnt); + wcid->stats.rx_errors += le32_to_cpu(stats->rx_err_cnt); + wcid->stats.rx_drops += le32_to_cpu(stats->rx_drop_cnt); + } + + rcu_read_unlock(); +} #endif int mt7915_mmio_wed_init(struct mt7915_dev *dev, void *pdev_ptr, @@ -744,6 +769,7 @@ int mt7915_mmio_wed_init(struct mt7915_dev *dev, void *pdev_ptr, wed->wlan.offload_disable = mt7915_mmio_wed_offload_disable; wed->wlan.init_rx_buf = mt7915_wed_init_rx_buf; wed->wlan.release_rx_buf = mt7915_wed_release_rx_buf; + wed->wlan.update_wo_rx_stats = mt7915_mmio_wed_update_rx_stats; dev->mt76.rx_token_size = wed->wlan.rx_npkt; diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h b/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h index 574f712b5fe1..3fc3c48997e4 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h @@ -630,6 +630,7 @@ void mt7915_set_stream_vht_txbf_caps(struct mt7915_phy *phy); void mt7915_update_channel(struct mt76_phy *mphy); int mt7915_mcu_muru_debug_set(struct mt7915_dev *dev, bool enable); int mt7915_mcu_muru_debug_get(struct mt7915_phy *phy, void *ms); +int mt7915_mcu_wed_enable_rx_stats(struct mt7915_dev *dev); int mt7915_init_debugfs(struct mt7915_phy *phy); void mt7915_debugfs_rx_fw_monitor(struct mt7915_dev *dev, const void *data, int len); bool mt7915_debugfs_rx_log(struct mt7915_dev *dev, const void *data, int len); From 3a46582c91502df34c81946350344dd70b1c0a90 Mon Sep 17 00:00:00 2001 From: Ryder Lee Date: Fri, 18 Nov 2022 01:09:46 +0800 Subject: [PATCH 1505/2002] wifi: mt76: mt7915: add missing MODULE_PARM_DESC Add documentation for module_param so that they're visible with modinfo command. Signed-off-by: Ryder Lee Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt7915/mmio.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mmio.c b/drivers/net/wireless/mediatek/mt76/mt7915/mmio.c index 1fcf34f57a16..3b4ede3b85ea 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mmio.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mmio.c @@ -13,6 +13,7 @@ static bool wed_enable; module_param(wed_enable, bool, 0644); +MODULE_PARM_DESC(wed_enable, "Enable Wireless Ethernet Dispatch support"); static const u32 mt7915_reg[] = { [INT_SOURCE_CSR] = 0xd7010, From 3dc00ecf242ee3d75aa0b2aebaf65f69eafc8d7d Mon Sep 17 00:00:00 2001 From: Ryder Lee Date: Fri, 18 Nov 2022 01:09:47 +0800 Subject: [PATCH 1506/2002] wifi: mt76: mt7915: add support to configure spatial reuse parameter set The SPR parameter set comprises OBSS PD threshold for SRG and non SRG and Bitmap of BSS color and partial BSSID. This adds support to configure fields of SPR element to firmware. User can disable firmware SR algorithms by turning sr_scene_detect off. Signed-off-by: Ryder Lee Signed-off-by: Felix Fietkau --- .../net/wireless/mediatek/mt76/mt7915/main.c | 6 +- .../net/wireless/mediatek/mt76/mt7915/mcu.c | 202 ++++++++++++++++-- .../net/wireless/mediatek/mt76/mt7915/mcu.h | 22 ++ .../wireless/mediatek/mt76/mt7915/mt7915.h | 4 +- 4 files changed, 210 insertions(+), 24 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/main.c b/drivers/net/wireless/mediatek/mt76/mt7915/main.c index 90c5d8fe4f73..6ef4634fac6b 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/main.c @@ -601,10 +601,8 @@ static void mt7915_bss_info_changed(struct ieee80211_hw *hw, mt7915_mcu_add_sta(dev, vif, NULL, join); } - if (changed & BSS_CHANGED_ASSOC) { + if (changed & BSS_CHANGED_ASSOC) mt7915_mcu_add_bss_info(phy, vif, vif->cfg.assoc); - mt7915_mcu_add_obss_spr(dev, vif, info->he_obss_pd.enable); - } if (changed & BSS_CHANGED_ERP_CTS_PROT) mt7915_mac_enable_rtscts(dev, vif, info->use_cts_prot); @@ -628,7 +626,7 @@ static void mt7915_bss_info_changed(struct ieee80211_hw *hw, mt7915_mcu_set_tx(dev, vif); if (changed & BSS_CHANGED_HE_OBSS_PD) - mt7915_mcu_add_obss_spr(dev, vif, info->he_obss_pd.enable); + mt7915_mcu_add_obss_spr(phy, vif, &info->he_obss_pd); if (changed & BSS_CHANGED_HE_BSS_COLOR) mt7915_update_bss_color(hw, vif, &info->he_bss_color); diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c index 2769d6c897d9..f27071f1b777 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c @@ -32,6 +32,10 @@ #define HE_PHY(p, c) u8_get_bits(c, IEEE80211_HE_PHY_##p) #define HE_MAC(m, c) u8_get_bits(c, IEEE80211_HE_MAC_##m) +static bool sr_scene_detect = true; +module_param(sr_scene_detect, bool, 0644); +MODULE_PARM_DESC(sr_scene_detect, "Enable firmware scene detection algorithm"); + static u8 mt7915_mcu_get_sta_nss(u16 mcs_map) { @@ -3292,31 +3296,193 @@ int mt7915_mcu_set_txbf(struct mt7915_dev *dev, u8 action) sizeof(req), true); } -int mt7915_mcu_add_obss_spr(struct mt7915_dev *dev, struct ieee80211_vif *vif, - bool enable) +static int +mt7915_mcu_enable_obss_spr(struct mt7915_phy *phy, u8 action, u8 val) { -#define MT_SPR_ENABLE 1 - struct mt7915_vif *mvif = (struct mt7915_vif *)vif->drv_priv; - struct { - u8 action; - u8 arg_num; - u8 band_idx; - u8 status; - u8 drop_tx_idx; - u8 sta_idx; /* 256 sta */ - u8 rsv[2]; - __le32 val; - } __packed req = { - .action = MT_SPR_ENABLE, - .arg_num = 1, - .band_idx = mvif->mt76.band_idx, - .val = cpu_to_le32(enable), + struct mt7915_dev *dev = phy->dev; + struct mt7915_mcu_sr_ctrl req = { + .action = action, + .argnum = 1, + .band_idx = phy != &dev->phy, + .val = cpu_to_le32(val), }; return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(SET_SPR), &req, sizeof(req), true); } +static int +mt7915_mcu_set_obss_spr_pd(struct mt7915_phy *phy, + struct ieee80211_he_obss_pd *he_obss_pd) +{ + struct mt7915_dev *dev = phy->dev; + struct { + struct mt7915_mcu_sr_ctrl ctrl; + struct { + u8 pd_th_non_srg; + u8 pd_th_srg; + u8 period_offs; + u8 rcpi_src; + __le16 obss_pd_min; + __le16 obss_pd_min_srg; + u8 resp_txpwr_mode; + u8 txpwr_restrict_mode; + u8 txpwr_ref; + u8 rsv[3]; + } __packed param; + } __packed req = { + .ctrl = { + .action = SPR_SET_PARAM, + .argnum = 9, + .band_idx = phy != &dev->phy, + }, + }; + int ret; + u8 max_th = 82, non_srg_max_th = 62; + + /* disable firmware dynamical PD asjustment */ + ret = mt7915_mcu_enable_obss_spr(phy, SPR_ENABLE_DPD, false); + if (ret) + return ret; + + if (he_obss_pd->sr_ctrl & + IEEE80211_HE_SPR_NON_SRG_OBSS_PD_SR_DISALLOWED) + req.param.pd_th_non_srg = max_th; + else if (he_obss_pd->sr_ctrl & IEEE80211_HE_SPR_NON_SRG_OFFSET_PRESENT) + req.param.pd_th_non_srg = max_th - he_obss_pd->non_srg_max_offset; + else + req.param.pd_th_non_srg = non_srg_max_th; + + if (he_obss_pd->sr_ctrl & IEEE80211_HE_SPR_SRG_INFORMATION_PRESENT) + req.param.pd_th_srg = max_th - he_obss_pd->max_offset; + + req.param.obss_pd_min = cpu_to_le16(82); + req.param.obss_pd_min_srg = cpu_to_le16(82); + req.param.txpwr_restrict_mode = 2; + req.param.txpwr_ref = 21; + + return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(SET_SPR), &req, + sizeof(req), true); +} + +static int +mt7915_mcu_set_obss_spr_siga(struct mt7915_phy *phy, struct ieee80211_vif *vif, + struct ieee80211_he_obss_pd *he_obss_pd) +{ + struct mt7915_vif *mvif = (struct mt7915_vif *)vif->drv_priv; + struct mt7915_dev *dev = phy->dev; + u8 omac = mvif->mt76.omac_idx; + struct { + struct mt7915_mcu_sr_ctrl ctrl; + struct { + u8 omac; + u8 rsv[3]; + u8 flag[20]; + } __packed siga; + } __packed req = { + .ctrl = { + .action = SPR_SET_SIGA, + .argnum = 1, + .band_idx = phy != &dev->phy, + }, + .siga = { + .omac = omac > HW_BSSID_MAX ? omac - 12 : omac, + }, + }; + int ret; + + if (he_obss_pd->sr_ctrl & IEEE80211_HE_SPR_HESIGA_SR_VAL15_ALLOWED) + req.siga.flag[req.siga.omac] = 0xf; + else + return 0; + + /* switch to normal AP mode */ + ret = mt7915_mcu_enable_obss_spr(phy, SPR_ENABLE_MODE, 0); + if (ret) + return ret; + + return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(SET_SPR), &req, + sizeof(req), true); +} + +static int +mt7915_mcu_set_obss_spr_bitmap(struct mt7915_phy *phy, + struct ieee80211_he_obss_pd *he_obss_pd) +{ + struct mt7915_dev *dev = phy->dev; + struct { + struct mt7915_mcu_sr_ctrl ctrl; + struct { + __le32 color_l[2]; + __le32 color_h[2]; + __le32 bssid_l[2]; + __le32 bssid_h[2]; + } __packed bitmap; + } __packed req = { + .ctrl = { + .action = SPR_SET_SRG_BITMAP, + .argnum = 4, + .band_idx = phy != &dev->phy, + }, + }; + u32 bitmap; + + memcpy(&bitmap, he_obss_pd->bss_color_bitmap, sizeof(bitmap)); + req.bitmap.color_l[req.ctrl.band_idx] = cpu_to_le32(bitmap); + + memcpy(&bitmap, he_obss_pd->bss_color_bitmap + 4, sizeof(bitmap)); + req.bitmap.color_h[req.ctrl.band_idx] = cpu_to_le32(bitmap); + + memcpy(&bitmap, he_obss_pd->partial_bssid_bitmap, sizeof(bitmap)); + req.bitmap.bssid_l[req.ctrl.band_idx] = cpu_to_le32(bitmap); + + memcpy(&bitmap, he_obss_pd->partial_bssid_bitmap + 4, sizeof(bitmap)); + req.bitmap.bssid_h[req.ctrl.band_idx] = cpu_to_le32(bitmap); + + return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(SET_SPR), &req, + sizeof(req), true); +} + +int mt7915_mcu_add_obss_spr(struct mt7915_phy *phy, struct ieee80211_vif *vif, + struct ieee80211_he_obss_pd *he_obss_pd) +{ + int ret; + + /* enable firmware scene detection algorithms */ + ret = mt7915_mcu_enable_obss_spr(phy, SPR_ENABLE_SD, sr_scene_detect); + if (ret) + return ret; + + /* firmware dynamically adjusts PD threshold so skip manual control */ + if (sr_scene_detect && !he_obss_pd->enable) + return 0; + + /* enable spatial reuse */ + ret = mt7915_mcu_enable_obss_spr(phy, SPR_ENABLE, he_obss_pd->enable); + if (ret) + return ret; + + if (sr_scene_detect || !he_obss_pd->enable) + return 0; + + ret = mt7915_mcu_enable_obss_spr(phy, SPR_ENABLE_TX, true); + if (ret) + return ret; + + /* set SRG/non-SRG OBSS PD threshold */ + ret = mt7915_mcu_set_obss_spr_pd(phy, he_obss_pd); + if (ret) + return ret; + + /* Set SR prohibit */ + ret = mt7915_mcu_set_obss_spr_siga(phy, vif, he_obss_pd); + if (ret) + return ret; + + /* set SRG BSS color/BSSID bitmap */ + return mt7915_mcu_set_obss_spr_bitmap(phy, he_obss_pd); +} + int mt7915_mcu_get_rx_rate(struct mt7915_phy *phy, struct ieee80211_vif *vif, struct ieee80211_sta *sta, struct rate_info *rate) { diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.h b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.h index c19b5d66c0e1..2fc09fd53777 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.h +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.h @@ -129,6 +129,17 @@ struct mt7915_mcu_background_chain_ctrl { u8 rsv[2]; } __packed; +struct mt7915_mcu_sr_ctrl { + u8 action; + u8 argnum; + u8 band_idx; + u8 status; + u8 drop_ta_idx; + u8 sta_idx; /* 256 sta */ + u8 rsv[2]; + __le32 val; +} __packed; + struct mt7915_mcu_eeprom { u8 buffer_mode; u8 format; @@ -408,6 +419,17 @@ enum { #define RATE_CFG_PHY_TYPE GENMASK(27, 24) #define RATE_CFG_HE_LTF GENMASK(31, 28) +enum { + SPR_ENABLE = 0x1, + SPR_ENABLE_SD = 0x3, + SPR_ENABLE_MODE = 0x5, + SPR_ENABLE_DPD = 0x23, + SPR_ENABLE_TX = 0x25, + SPR_SET_SRG_BITMAP = 0x80, + SPR_SET_PARAM = 0xc2, + SPR_SET_SIGA = 0xdc, +}; + enum { THERMAL_PROTECT_PARAMETER_CTRL, THERMAL_PROTECT_BASIC_INFO, diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h b/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h index 3fc3c48997e4..b5fa6c0b4775 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h @@ -503,8 +503,8 @@ int mt7915_mcu_update_bss_color(struct mt7915_dev *dev, struct ieee80211_vif *vi struct cfg80211_he_bss_color *he_bss_color); int mt7915_mcu_add_beacon(struct ieee80211_hw *hw, struct ieee80211_vif *vif, int enable, u32 changed); -int mt7915_mcu_add_obss_spr(struct mt7915_dev *dev, struct ieee80211_vif *vif, - bool enable); +int mt7915_mcu_add_obss_spr(struct mt7915_phy *phy, struct ieee80211_vif *vif, + struct ieee80211_he_obss_pd *he_obss_pd); int mt7915_mcu_add_rate_ctrl(struct mt7915_dev *dev, struct ieee80211_vif *vif, struct ieee80211_sta *sta, bool changed); int mt7915_mcu_add_smps(struct mt7915_dev *dev, struct ieee80211_vif *vif, From bd2404d428215eda4c2466d59a780d568546cdfd Mon Sep 17 00:00:00 2001 From: Ryder Lee Date: Tue, 22 Nov 2022 15:53:10 +0800 Subject: [PATCH 1507/2002] wifi: mt76: mt7915: add basedband Txpower info into debugfs This helps user to debug Txpower propagation path easily. Signed-off-by: Ryder Lee Signed-off-by: Felix Fietkau --- .../wireless/mediatek/mt76/mt7915/debugfs.c | 19 ++++++++++++++++--- .../net/wireless/mediatek/mt76/mt7915/regs.h | 4 ++++ 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/debugfs.c b/drivers/net/wireless/mediatek/mt76/mt7915/debugfs.c index 096cb8a4db3d..c322fcc19d67 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/debugfs.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/debugfs.c @@ -967,11 +967,18 @@ mt7915_rate_txpower_show(struct seq_file *file, void *data) "RU484/SU40", "RU996/SU80", "RU2x996/SU160" }; struct mt7915_phy *phy = file->private; + struct mt7915_dev *dev = phy->dev; s8 txpower[MT7915_SKU_RATE_NUM], *buf; - int i; + u32 reg; + int i, ret; + + ret = mt7915_mcu_get_txpower_sku(phy, txpower, sizeof(txpower)); + if (ret) + return ret; + + /* Txpower propagation path: TMAC -> TXV -> BBP */ + seq_printf(file, "\nPhy %d\n", phy != &dev->phy); - seq_printf(file, "\nBand %d\n", phy != &phy->dev->phy); - mt7915_mcu_get_txpower_sku(phy, txpower, sizeof(txpower)); for (i = 0, buf = txpower; i < ARRAY_SIZE(mt7915_sku_group_len); i++) { u8 mcs_num = mt7915_sku_group_len[i]; @@ -982,6 +989,12 @@ mt7915_rate_txpower_show(struct seq_file *file, void *data) buf += mt7915_sku_group_len[i]; } + reg = is_mt7915(&dev->mt76) ? MT_WF_PHY_TPC_CTRL_STAT(phy->band_idx) : + MT_WF_PHY_TPC_CTRL_STAT_MT7916(phy->band_idx); + + seq_printf(file, "\nBaseband transmit power %ld\n", + mt76_get_field(dev, reg, MT_WF_PHY_TPC_POWER)); + return 0; } diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/regs.h b/drivers/net/wireless/mediatek/mt76/mt7915/regs.h index 42a19e7b95d6..aca1b2f1e9e3 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/regs.h +++ b/drivers/net/wireless/mediatek/mt76/mt7915/regs.h @@ -1186,6 +1186,10 @@ enum offs_rev { #define MT_WF_PHY_RXTD12_IRPI_SW_CLR_ONLY BIT(18) #define MT_WF_PHY_RXTD12_IRPI_SW_CLR BIT(29) +#define MT_WF_PHY_TPC_CTRL_STAT(_phy) MT_WF_PHY(0xe7a0 + ((_phy) << 16)) +#define MT_WF_PHY_TPC_CTRL_STAT_MT7916(_phy) MT_WF_PHY(0xe7a0 + ((_phy) << 20)) +#define MT_WF_PHY_TPC_POWER GENMASK(15, 8) + #define MT_MCU_WM_CIRQ_BASE 0x89010000 #define MT_MCU_WM_CIRQ(ofs) (MT_MCU_WM_CIRQ_BASE + (ofs)) #define MT_MCU_WM_CIRQ_IRQ_MASK_CLR_ADDR MT_MCU_WM_CIRQ(0x80) From 66b181b8a9914a26ff424311f5ac19d7ec2199b9 Mon Sep 17 00:00:00 2001 From: Ryder Lee Date: Tue, 22 Nov 2022 15:53:11 +0800 Subject: [PATCH 1508/2002] wifi: mt76: mt7915: enable .sta_set_txpwr support This adds support for adjusting the Txpower level while pushing traffic to an associated station. The allowed range is from 0 to the maximum power of channel. Signed-off-by: Ryder Lee Signed-off-by: Felix Fietkau --- .../net/wireless/mediatek/mt76/mt7915/init.c | 3 + .../net/wireless/mediatek/mt76/mt7915/main.c | 34 +++++++ .../net/wireless/mediatek/mt76/mt7915/mcu.c | 94 +++++++++++++++++-- .../net/wireless/mediatek/mt76/mt7915/mcu.h | 8 ++ .../wireless/mediatek/mt76/mt7915/mt7915.h | 4 + 5 files changed, 137 insertions(+), 6 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/init.c b/drivers/net/wireless/mediatek/mt76/mt7915/init.c index b48c2ba9273d..79bd7bf93f33 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/init.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/init.c @@ -355,6 +355,9 @@ mt7915_init_wiphy(struct ieee80211_hw *hw) wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_FILS_DISCOVERY); wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_ACK_SIGNAL_SUPPORT); + if (!is_mt7915(&dev->mt76)) + wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_STA_TX_PWR); + if (!mdev->dev->of_node || !of_property_read_bool(mdev->dev->of_node, "mediatek,disable-radar-background")) diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/main.c b/drivers/net/wireless/mediatek/mt76/mt7915/main.c index 6ef4634fac6b..98ed56a5ed69 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/main.c @@ -1134,6 +1134,39 @@ static void mt7915_sta_set_decap_offload(struct ieee80211_hw *hw, mt76_connac_mcu_wtbl_update_hdr_trans(&dev->mt76, vif, sta); } +static int mt7915_sta_set_txpwr(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + struct mt7915_phy *phy = mt7915_hw_phy(hw); + struct mt7915_dev *dev = mt7915_hw_dev(hw); + s16 txpower = sta->deflink.txpwr.power; + int ret; + + if (sta->deflink.txpwr.type == NL80211_TX_POWER_AUTOMATIC) + txpower = 0; + + mutex_lock(&dev->mt76.mutex); + + /* NOTE: temporarily use 0 as minimum limit, which is a + * global setting and will be applied to all stations. + */ + ret = mt7915_mcu_set_txpower_frame_min(phy, 0); + if (ret) + goto out; + + /* This only applies to data frames while pushing traffic, + * whereas the management frames or other packets that are + * using fixed rate can be configured via TxD. + */ + ret = mt7915_mcu_set_txpower_frame(phy, vif, sta, txpower); + +out: + mutex_unlock(&dev->mt76.mutex); + + return ret; +} + static const char mt7915_gstrings_stats[][ETH_GSTRING_LEN] = { "tx_ampdu_cnt", "tx_stop_q_empty_cnt", @@ -1499,6 +1532,7 @@ const struct ieee80211_ops mt7915_ops = { .set_bitrate_mask = mt7915_set_bitrate_mask, .set_coverage_class = mt7915_set_coverage_class, .sta_statistics = mt7915_sta_statistics, + .sta_set_txpwr = mt7915_sta_set_txpwr, .sta_set_4addr = mt7915_sta_set_4addr, .sta_set_decap_offload = mt7915_sta_set_decap_offload, .add_twt_setup = mt7915_mac_add_twt_setup, diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c index f27071f1b777..ed20ffc58877 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c @@ -3105,6 +3105,88 @@ out: &req, sizeof(req), false); } +int mt7915_mcu_set_txpower_frame_min(struct mt7915_phy *phy, s8 txpower) +{ + struct mt7915_dev *dev = phy->dev; + struct { + u8 format_id; + u8 rsv; + u8 band_idx; + s8 txpower_min; + } __packed req = { + .format_id = TX_POWER_LIMIT_FRAME_MIN, + .band_idx = phy->band_idx, + .txpower_min = txpower * 2, /* 0.5db */ + }; + + return mt76_mcu_send_msg(&dev->mt76, + MCU_EXT_CMD(TX_POWER_FEATURE_CTRL), &req, + sizeof(req), true); +} + +int mt7915_mcu_set_txpower_frame(struct mt7915_phy *phy, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, s8 txpower) +{ + struct mt7915_sta *msta = (struct mt7915_sta *)sta->drv_priv; + struct mt7915_dev *dev = phy->dev; + struct mt76_phy *mphy = phy->mt76; + struct { + u8 format_id; + u8 rsv[3]; + u8 band_idx; + s8 txpower_max; + __le16 wcid; + s8 txpower_offs[48]; + } __packed req = { + .format_id = TX_POWER_LIMIT_FRAME, + .band_idx = phy->band_idx, + .txpower_max = DIV_ROUND_UP(mphy->txpower_cur, 2), + .wcid = cpu_to_le16(msta->wcid.idx), + }; + int ret, n_chains = hweight8(mphy->antenna_mask); + s8 txpower_sku[MT7915_SKU_RATE_NUM]; + + ret = mt7915_mcu_get_txpower_sku(phy, txpower_sku, sizeof(txpower_sku)); + if (ret) + return ret; + + txpower = txpower * 2 - mt76_tx_power_nss_delta(n_chains); + if (txpower > mphy->txpower_cur || txpower < 0) + return -EINVAL; + + if (txpower) { + u32 offs, len, i; + + if (sta->deflink.ht_cap.ht_supported) { + const u8 *sku_len = mt7915_sku_group_len; + + offs = sku_len[SKU_CCK] + sku_len[SKU_OFDM]; + len = sku_len[SKU_HT_BW20] + sku_len[SKU_HT_BW40]; + + if (sta->deflink.vht_cap.vht_supported) { + offs += len; + len = sku_len[SKU_VHT_BW20] * 4; + + if (sta->deflink.he_cap.has_he) { + offs += len + sku_len[SKU_HE_RU26] * 3; + len = sku_len[SKU_HE_RU242] * 4; + } + } + } else { + return -EINVAL; + } + + for (i = 0; i < len; i++, offs++) + req.txpower_offs[i] = + DIV_ROUND_UP(txpower - txpower_sku[offs], 2); + } + + return mt76_mcu_send_msg(&dev->mt76, + MCU_EXT_CMD(TX_POWER_FEATURE_CTRL), &req, + sizeof(req), true); +} + int mt7915_mcu_set_txpower_sku(struct mt7915_phy *phy) { struct mt7915_dev *dev = phy->dev; @@ -3116,7 +3198,7 @@ int mt7915_mcu_set_txpower_sku(struct mt7915_phy *phy) u8 dbdc_idx; s8 val[MT7915_SKU_RATE_NUM]; } __packed req = { - .format_id = 4, + .format_id = TX_POWER_LIMIT_TABLE, .dbdc_idx = phy != &dev->phy, }; struct mt76_power_limits limits_array; @@ -3166,11 +3248,11 @@ int mt7915_mcu_get_txpower_sku(struct mt7915_phy *phy, s8 *txpower, int len) u8 band; u8 _rsv; } __packed req = { - .format_id = 7, + .format_id = TX_POWER_LIMIT_INFO, .category = RATE_POWER_INFO, .band = phy != &dev->phy, }; - s8 res[MT7915_SKU_RATE_NUM][2]; + s8 txpower_sku[MT7915_SKU_RATE_NUM][2]; struct sk_buff *skb; int ret, i; @@ -3180,9 +3262,9 @@ int mt7915_mcu_get_txpower_sku(struct mt7915_phy *phy, s8 *txpower, int len) if (ret) return ret; - memcpy(res, skb->data + 4, sizeof(res)); + memcpy(txpower_sku, skb->data + 4, sizeof(txpower_sku)); for (i = 0; i < len; i++) - txpower[i] = res[i][req.band]; + txpower[i] = txpower_sku[i][req.band_idx]; dev_kfree_skb(skb); @@ -3220,7 +3302,7 @@ int mt7915_mcu_set_sku_en(struct mt7915_phy *phy, bool enable) u8 dbdc_idx; u8 rsv; } __packed req = { - .format_id = 0, + .format_id = TX_POWER_LIMIT_ENABLE, .dbdc_idx = phy != &dev->phy, .sku_enable = enable, }; diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.h b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.h index 2fc09fd53777..46c517e50ae4 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.h +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.h @@ -419,6 +419,14 @@ enum { #define RATE_CFG_PHY_TYPE GENMASK(27, 24) #define RATE_CFG_HE_LTF GENMASK(31, 28) +enum { + TX_POWER_LIMIT_ENABLE, + TX_POWER_LIMIT_TABLE = 0x4, + TX_POWER_LIMIT_INFO = 0x7, + TX_POWER_LIMIT_FRAME = 0x11, + TX_POWER_LIMIT_FRAME_MIN = 0x12, +}; + enum { SPR_ENABLE = 0x1, SPR_ENABLE_SD = 0x3, diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h b/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h index b5fa6c0b4775..42f213430758 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h @@ -528,6 +528,10 @@ int mt7915_mcu_set_ser(struct mt7915_dev *dev, u8 action, u8 set, u8 band); int mt7915_mcu_set_sku_en(struct mt7915_phy *phy, bool enable); int mt7915_mcu_set_txpower_sku(struct mt7915_phy *phy); int mt7915_mcu_get_txpower_sku(struct mt7915_phy *phy, s8 *txpower, int len); +int mt7915_mcu_set_txpower_frame_min(struct mt7915_phy *phy, s8 txpower); +int mt7915_mcu_set_txpower_frame(struct mt7915_phy *phy, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, s8 txpower); int mt7915_mcu_set_txbf(struct mt7915_dev *dev, u8 action); int mt7915_mcu_set_fcc5_lpn(struct mt7915_dev *dev, int val); int mt7915_mcu_set_pulse_th(struct mt7915_dev *dev, From 6f917bba8709c1eb73c0ed6201c305975c2cf729 Mon Sep 17 00:00:00 2001 From: Ryder Lee Date: Tue, 22 Nov 2022 15:53:12 +0800 Subject: [PATCH 1509/2002] wifi: mt76: mt7915: fix band_idx usage The commit 006b9d4ad5bf introduced phy->band_idx to accommodate the band definition change for mt7986 so that the band_idx of main_phy can be 0 or 1. Accordingly, the source of band_idx 1 has switched to "phy != &dev->phy" or "dev->phy.band_idx = 1". We still use ext_phy to represent band 1 somewhere in driver, so fix it. Also, band_idx sounds more reasonable than dbdc_idx, so change it. Fixes: 006b9d4ad5bf ("mt76: mt7915: introduce band_idx in mt7915_phy") Signed-off-by: Ryder Lee Signed-off-by: Felix Fietkau --- .../wireless/mediatek/mt76/mt7915/debugfs.c | 10 ++--- .../net/wireless/mediatek/mt76/mt7915/mac.c | 13 +++--- .../net/wireless/mediatek/mt76/mt7915/main.c | 36 ++++++++--------- .../net/wireless/mediatek/mt76/mt7915/mcu.c | 40 +++++++++---------- .../wireless/mediatek/mt76/mt7915/testmode.c | 18 ++++----- 5 files changed, 58 insertions(+), 59 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/debugfs.c b/drivers/net/wireless/mediatek/mt76/mt7915/debugfs.c index c322fcc19d67..fef0ec83185b 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/debugfs.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/debugfs.c @@ -51,7 +51,7 @@ mt7915_sys_recovery_set(struct file *file, const char __user *user_buf, { struct mt7915_phy *phy = file->private_data; struct mt7915_dev *dev = phy->dev; - bool ext_phy = phy != &dev->phy; + bool band = phy->band_idx; char buf[16]; int ret = 0; u16 val; @@ -83,7 +83,7 @@ mt7915_sys_recovery_set(struct file *file, const char __user *user_buf, * 8: trigger firmware crash. */ case SER_QUERY: - ret = mt7915_mcu_set_ser(dev, 0, 0, ext_phy); + ret = mt7915_mcu_set_ser(dev, 0, 0, band); break; case SER_SET_RECOVER_L1: case SER_SET_RECOVER_L2: @@ -91,17 +91,17 @@ mt7915_sys_recovery_set(struct file *file, const char __user *user_buf, case SER_SET_RECOVER_L3_TX_ABORT: case SER_SET_RECOVER_L3_TX_DISABLE: case SER_SET_RECOVER_L3_BF: - ret = mt7915_mcu_set_ser(dev, SER_ENABLE, BIT(val), ext_phy); + ret = mt7915_mcu_set_ser(dev, SER_ENABLE, BIT(val), band); if (ret) return ret; - ret = mt7915_mcu_set_ser(dev, SER_RECOVER, val, ext_phy); + ret = mt7915_mcu_set_ser(dev, SER_RECOVER, val, band); break; /* enable full chip reset */ case SER_SET_RECOVER_FULL: mt76_set(dev, MT_WFDMA0_MCU_HOST_INT_ENA, MT_MCU_CMD_WDT_MASK); - ret = mt7915_mcu_set_ser(dev, 1, 3, ext_phy); + ret = mt7915_mcu_set_ser(dev, 1, 3, band); if (ret) return ret; diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mac.c b/drivers/net/wireless/mediatek/mt76/mt7915/mac.c index 4f0b3c57e545..eac3008225b0 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mac.c @@ -1259,18 +1259,18 @@ void mt7915_mac_set_timing(struct mt7915_phy *phy) MT_ARB_SCR_TX_DISABLE | MT_ARB_SCR_RX_DISABLE); } -void mt7915_mac_enable_nf(struct mt7915_dev *dev, bool ext_phy) +void mt7915_mac_enable_nf(struct mt7915_dev *dev, bool band) { u32 reg; - reg = is_mt7915(&dev->mt76) ? MT_WF_PHY_RXTD12(ext_phy) : - MT_WF_PHY_RXTD12_MT7916(ext_phy); + reg = is_mt7915(&dev->mt76) ? MT_WF_PHY_RXTD12(band) : + MT_WF_PHY_RXTD12_MT7916(band); mt76_set(dev, reg, MT_WF_PHY_RXTD12_IRPI_SW_CLR_ONLY | MT_WF_PHY_RXTD12_IRPI_SW_CLR); - reg = is_mt7915(&dev->mt76) ? MT_WF_PHY_RX_CTRL1(ext_phy) : - MT_WF_PHY_RX_CTRL1_MT7916(ext_phy); + reg = is_mt7915(&dev->mt76) ? MT_WF_PHY_RX_CTRL1(band) : + MT_WF_PHY_RX_CTRL1_MT7916(band); mt76_set(dev, reg, FIELD_PREP(MT_WF_PHY_RX_CTRL1_IPI_EN, 0x5)); } @@ -1979,7 +1979,6 @@ void mt7915_mac_update_stats(struct mt7915_phy *phy) static void mt7915_mac_severe_check(struct mt7915_phy *phy) { struct mt7915_dev *dev = phy->dev; - bool ext_phy = phy != &dev->phy; u32 trb; if (!phy->omac_mask) @@ -1997,7 +1996,7 @@ static void mt7915_mac_severe_check(struct mt7915_phy *phy) FIELD_GET(MT_TRB_RXPSR0_RX_WTBL_PTR, phy->trb_ts)) && trb == phy->trb_ts) mt7915_mcu_set_ser(dev, SER_RECOVER, SER_SET_RECOVER_L3_RX_ABORT, - ext_phy); + phy->band_idx); phy->trb_ts = trb; } diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/main.c b/drivers/net/wireless/mediatek/mt76/mt7915/main.c index 98ed56a5ed69..c40b6098f19a 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/main.c @@ -30,31 +30,31 @@ int mt7915_run(struct ieee80211_hw *hw) running = mt7915_dev_running(dev); if (!running) { - ret = mt76_connac_mcu_set_pm(&dev->mt76, 0, 0); + ret = mt76_connac_mcu_set_pm(&dev->mt76, dev->phy.band_idx, 0); if (ret) goto out; - ret = mt7915_mcu_set_mac(dev, 0, true, true); + ret = mt7915_mcu_set_mac(dev, dev->phy.band_idx, true, true); if (ret) goto out; - mt7915_mac_enable_nf(dev, 0); + mt7915_mac_enable_nf(dev, dev->phy.band_idx); } - if (phy != &dev->phy || phy->band_idx) { - ret = mt76_connac_mcu_set_pm(&dev->mt76, 1, 0); + if (phy != &dev->phy) { + ret = mt76_connac_mcu_set_pm(&dev->mt76, phy->band_idx, 0); if (ret) goto out; - ret = mt7915_mcu_set_mac(dev, 1, true, true); + ret = mt7915_mcu_set_mac(dev, phy->band_idx, true, true); if (ret) goto out; - mt7915_mac_enable_nf(dev, 1); + mt7915_mac_enable_nf(dev, phy->band_idx); } ret = mt76_connac_mcu_set_rts_thresh(&dev->mt76, 0x92b, - phy != &dev->phy); + phy->band_idx); if (ret) goto out; @@ -107,13 +107,13 @@ static void mt7915_stop(struct ieee80211_hw *hw) clear_bit(MT76_STATE_RUNNING, &phy->mt76->state); if (phy != &dev->phy) { - mt76_connac_mcu_set_pm(&dev->mt76, 1, 1); - mt7915_mcu_set_mac(dev, 1, false, false); + mt76_connac_mcu_set_pm(&dev->mt76, phy->band_idx, 1); + mt7915_mcu_set_mac(dev, phy->band_idx, false, false); } if (!mt7915_dev_running(dev)) { - mt76_connac_mcu_set_pm(&dev->mt76, 0, 1); - mt7915_mcu_set_mac(dev, 0, false, false); + mt76_connac_mcu_set_pm(&dev->mt76, dev->phy.band_idx, 1); + mt7915_mcu_set_mac(dev, dev->phy.band_idx, false, false); } mutex_unlock(&dev->mt76.mutex); @@ -440,7 +440,6 @@ static int mt7915_config(struct ieee80211_hw *hw, u32 changed) { struct mt7915_dev *dev = mt7915_hw_dev(hw); struct mt7915_phy *phy = mt7915_hw_phy(hw); - bool band = phy != &dev->phy; int ret; if (changed & IEEE80211_CONF_CHANGE_CHANNEL) { @@ -468,6 +467,7 @@ static int mt7915_config(struct ieee80211_hw *hw, u32 changed) if (changed & IEEE80211_CONF_CHANGE_MONITOR) { bool enabled = !!(hw->conf.flags & IEEE80211_CONF_MONITOR); + bool band = phy->band_idx; if (!enabled) phy->rxfilter |= MT_WF_RFCR_DROP_OTHER_UC; @@ -506,7 +506,7 @@ static void mt7915_configure_filter(struct ieee80211_hw *hw, { struct mt7915_dev *dev = mt7915_hw_dev(hw); struct mt7915_phy *phy = mt7915_hw_phy(hw); - bool band = phy != &dev->phy; + bool band = phy->band_idx; u32 ctl_flags = MT_WF_RFCR1_DROP_ACK | MT_WF_RFCR1_DROP_BF_POLL | MT_WF_RFCR1_DROP_BA | @@ -743,7 +743,7 @@ static int mt7915_set_rts_threshold(struct ieee80211_hw *hw, u32 val) int ret; mutex_lock(&dev->mt76.mutex); - ret = mt76_connac_mcu_set_rts_thresh(&dev->mt76, val, phy != &dev->phy); + ret = mt76_connac_mcu_set_rts_thresh(&dev->mt76, val, phy->band_idx); mutex_unlock(&dev->mt76.mutex); return ret; @@ -846,7 +846,7 @@ u64 __mt7915_get_tsf(struct ieee80211_hw *hw, struct mt7915_vif *mvif) { struct mt7915_dev *dev = mt7915_hw_dev(hw); struct mt7915_phy *phy = mt7915_hw_phy(hw); - bool band = phy != &dev->phy; + bool band = phy->band_idx; union { u64 t64; u32 t32[2]; @@ -891,7 +891,7 @@ mt7915_set_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct mt7915_vif *mvif = (struct mt7915_vif *)vif->drv_priv; struct mt7915_dev *dev = mt7915_hw_dev(hw); struct mt7915_phy *phy = mt7915_hw_phy(hw); - bool band = phy != &dev->phy; + bool band = phy->band_idx; union { u64 t64; u32 t32[2]; @@ -922,7 +922,7 @@ mt7915_offset_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct mt7915_vif *mvif = (struct mt7915_vif *)vif->drv_priv; struct mt7915_dev *dev = mt7915_hw_dev(hw); struct mt7915_phy *phy = mt7915_hw_phy(hw); - bool band = phy != &dev->phy; + bool band = phy->band_idx; union { u64 t64; u32 t32[2]; diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c index ed20ffc58877..718e52744994 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c @@ -599,7 +599,7 @@ mt7915_mcu_muar_config(struct mt7915_phy *phy, struct ieee80211_vif *vif, .mode = !!mask || enable, .entry_count = 1, .write = 1, - .band = phy != &dev->phy, + .band = phy->band_idx, .index = idx * 2 + bssid, }; @@ -1715,7 +1715,7 @@ int mt7915_mcu_add_dev_info(struct mt7915_phy *phy, struct { struct req_hdr { u8 omac_idx; - u8 dbdc_idx; + u8 band_idx; __le16 tlv_num; u8 is_tlv_append; u8 rsv[3]; @@ -1724,13 +1724,13 @@ int mt7915_mcu_add_dev_info(struct mt7915_phy *phy, __le16 tag; __le16 len; u8 active; - u8 dbdc_idx; + u8 band_idx; u8 omac_addr[ETH_ALEN]; } __packed tlv; } data = { .hdr = { .omac_idx = mvif->mt76.omac_idx, - .dbdc_idx = mvif->mt76.band_idx, + .band_idx = mvif->mt76.band_idx, .tlv_num = cpu_to_le16(1), .is_tlv_append = 1, }, @@ -1738,7 +1738,7 @@ int mt7915_mcu_add_dev_info(struct mt7915_phy *phy, .tag = cpu_to_le16(DEV_INFO_ACTIVE), .len = cpu_to_le16(sizeof(struct req_tlv)), .active = enable, - .dbdc_idx = mvif->mt76.band_idx, + .band_idx = mvif->mt76.band_idx, }, }; @@ -2585,7 +2585,7 @@ mt7915_mcu_background_chain_ctrl(struct mt7915_phy *phy, req.monitor_central_chan = ieee80211_frequency_to_channel(chandef->center_freq1); req.monitor_bw = mt76_connac_chan_bw(chandef); - req.band_idx = phy != &dev->phy; + req.band_idx = phy->band_idx; req.scan_mode = 1; break; } @@ -2593,7 +2593,7 @@ mt7915_mcu_background_chain_ctrl(struct mt7915_phy *phy, req.monitor_chan = chandef->chan->hw_value; req.monitor_central_chan = ieee80211_frequency_to_channel(chandef->center_freq1); - req.band_idx = phy != &dev->phy; + req.band_idx = phy->band_idx; req.scan_mode = 2; break; case CH_SWITCH_BACKGROUND_SCAN_STOP: @@ -2997,7 +2997,7 @@ int mt7915_mcu_get_chan_mib_info(struct mt7915_phy *phy, bool chan_switch) } for (i = 0; i < 5; i++) { - req[i].band = cpu_to_le32(phy != &dev->phy); + req[i].band = cpu_to_le32(phy->band_idx); req[i].offs = cpu_to_le32(offs[i + start]); if (!is_mt7915(&dev->mt76) && i == 3) @@ -3042,11 +3042,11 @@ int mt7915_mcu_get_temperature(struct mt7915_phy *phy) struct { u8 ctrl_id; u8 action; - u8 dbdc_idx; + u8 band_idx; u8 rsv[5]; } req = { .ctrl_id = THERMAL_SENSOR_TEMP_QUERY, - .dbdc_idx = phy != &dev->phy, + .band_idx = phy->band_idx, }; return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(THERMAL_CTRL), &req, @@ -3195,11 +3195,11 @@ int mt7915_mcu_set_txpower_sku(struct mt7915_phy *phy) struct mt7915_sku_val { u8 format_id; u8 limit_type; - u8 dbdc_idx; + u8 band_idx; s8 val[MT7915_SKU_RATE_NUM]; } __packed req = { .format_id = TX_POWER_LIMIT_TABLE, - .dbdc_idx = phy != &dev->phy, + .band_idx = phy->band_idx, }; struct mt76_power_limits limits_array; s8 *la = (s8 *)&limits_array; @@ -3245,12 +3245,12 @@ int mt7915_mcu_get_txpower_sku(struct mt7915_phy *phy, s8 *txpower, int len) struct { u8 format_id; u8 category; - u8 band; + u8 band_idx; u8 _rsv; } __packed req = { .format_id = TX_POWER_LIMIT_INFO, .category = RATE_POWER_INFO, - .band = phy != &dev->phy, + .band_idx = phy->band_idx, }; s8 txpower_sku[MT7915_SKU_RATE_NUM][2]; struct sk_buff *skb; @@ -3299,11 +3299,11 @@ int mt7915_mcu_set_sku_en(struct mt7915_phy *phy, bool enable) struct mt7915_sku { u8 format_id; u8 sku_enable; - u8 dbdc_idx; + u8 band_idx; u8 rsv; } __packed req = { .format_id = TX_POWER_LIMIT_ENABLE, - .dbdc_idx = phy != &dev->phy, + .band_idx = phy->band_idx, .sku_enable = enable, }; @@ -3385,7 +3385,7 @@ mt7915_mcu_enable_obss_spr(struct mt7915_phy *phy, u8 action, u8 val) struct mt7915_mcu_sr_ctrl req = { .action = action, .argnum = 1, - .band_idx = phy != &dev->phy, + .band_idx = phy->band_idx, .val = cpu_to_le32(val), }; @@ -3416,7 +3416,7 @@ mt7915_mcu_set_obss_spr_pd(struct mt7915_phy *phy, .ctrl = { .action = SPR_SET_PARAM, .argnum = 9, - .band_idx = phy != &dev->phy, + .band_idx = phy->band_idx, }, }; int ret; @@ -3465,7 +3465,7 @@ mt7915_mcu_set_obss_spr_siga(struct mt7915_phy *phy, struct ieee80211_vif *vif, .ctrl = { .action = SPR_SET_SIGA, .argnum = 1, - .band_idx = phy != &dev->phy, + .band_idx = phy->band_idx, }, .siga = { .omac = omac > HW_BSSID_MAX ? omac - 12 : omac, @@ -3504,7 +3504,7 @@ mt7915_mcu_set_obss_spr_bitmap(struct mt7915_phy *phy, .ctrl = { .action = SPR_SET_SRG_BITMAP, .argnum = 4, - .band_idx = phy != &dev->phy, + .band_idx = phy->band_idx, }, }; u32 bitmap; diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/testmode.c b/drivers/net/wireless/mediatek/mt76/mt7915/testmode.c index a979460fad2d..7ace05e0b63b 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/testmode.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/testmode.c @@ -44,14 +44,14 @@ mt7915_tm_set_tx_power(struct mt7915_phy *phy) int ret; struct { u8 format_id; - u8 dbdc_idx; + u8 band_idx; s8 tx_power; u8 ant_idx; /* Only 0 is valid */ u8 center_chan; u8 rsv[3]; } __packed req = { .format_id = 0xf, - .dbdc_idx = phy != &dev->phy, + .band_idx = phy->band_idx, .center_chan = ieee80211_frequency_to_channel(freq), }; u8 *tx_power = NULL; @@ -77,7 +77,7 @@ mt7915_tm_set_freq_offset(struct mt7915_phy *phy, bool en, u32 val) struct mt7915_tm_cmd req = { .testmode_en = en, .param_idx = MCU_ATE_SET_FREQ_OFFSET, - .param.freq.band = phy != &dev->phy, + .param.freq.band = phy->band_idx, .param.freq.freq_offset = cpu_to_le32(val), }; @@ -111,7 +111,7 @@ mt7915_tm_set_trx(struct mt7915_phy *phy, int type, bool en) .param_idx = MCU_ATE_SET_TRX, .param.trx.type = type, .param.trx.enable = en, - .param.trx.band = phy != &dev->phy, + .param.trx.band = phy->band_idx, }; return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(ATE_CTRL), &req, @@ -126,7 +126,7 @@ mt7915_tm_clean_hwq(struct mt7915_phy *phy, u8 wcid) .testmode_en = 1, .param_idx = MCU_ATE_CLEAN_TXQUEUE, .param.clean.wcid = wcid, - .param.clean.band = phy != &dev->phy, + .param.clean.band = phy->band_idx, }; return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(ATE_CTRL), &req, @@ -144,7 +144,7 @@ mt7915_tm_set_slot_time(struct mt7915_phy *phy, u8 slot_time, u8 sifs) .param.slot.sifs = sifs, .param.slot.rifs = 2, .param.slot.eifs = cpu_to_le16(60), - .param.slot.band = phy != &dev->phy, + .param.slot.band = phy->band_idx, }; return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(ATE_CTRL), &req, @@ -488,7 +488,7 @@ mt7915_tm_set_rx_frames(struct mt7915_phy *phy, bool en) mt7915_tm_update_channel(phy); /* read-clear */ - mt76_rr(dev, MT_MIB_SDR3(phy != &dev->phy)); + mt76_rr(dev, MT_MIB_SDR3(phy->band_idx)); mt7915_tm_set_trx(phy, TM_MAC_RX_RXV, en); } } @@ -526,7 +526,7 @@ mt7915_tm_set_tx_cont(struct mt7915_phy *phy, bool en) tx_cont->control_ch = chandef->chan->hw_value; tx_cont->center_ch = freq1; tx_cont->tx_ant = td->tx_antenna_mask; - tx_cont->band = phy != &dev->phy; + tx_cont->band = phy->band_idx; switch (chandef->width) { case NL80211_CHAN_WIDTH_40: @@ -558,7 +558,7 @@ mt7915_tm_set_tx_cont(struct mt7915_phy *phy, bool en) } if (!en) { - req.op.rf.param.func_data = cpu_to_le32(phy != &dev->phy); + req.op.rf.param.func_data = cpu_to_le32(phy->band_idx); goto out; } From f4cfd3f95f297744e03fd494fcc21f3b65b95658 Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Tue, 22 Nov 2022 14:52:08 +0100 Subject: [PATCH 1510/2002] wifi: mt76: mt76x0: remove dead code in mt76x0_phy_get_target_power tx_rate can't be greater than 3 in mt76x0_phy_get_target_power routine for cck rates. Get rid of dead code. Signed-off-by: Lorenzo Bianconi Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt76x0/phy.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt76x0/phy.c b/drivers/net/wireless/mediatek/mt76/mt76x0/phy.c index 8a89fe49db36..6c6c8ada7943 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76x0/phy.c +++ b/drivers/net/wireless/mediatek/mt76/mt76x0/phy.c @@ -595,9 +595,6 @@ mt76x0_phy_get_target_power(struct mt76x02_dev *dev, u8 tx_mode, case 0: /* cck rates */ tx_rate = (info[0] & 0x60) >> 5; - if (tx_rate > 3) - return -EINVAL; - *target_power = cur_power + dev->rate_power.cck[tx_rate]; *target_pa_power = mt76x0_phy_get_rf_pa_mode(dev, 0, tx_rate); break; From 98686cd21624c75a043e96812beadddf4f6f48e5 Mon Sep 17 00:00:00 2001 From: Shayne Chen Date: Tue, 22 Nov 2022 16:45:46 +0800 Subject: [PATCH 1511/2002] wifi: mt76: mt7996: add driver for MediaTek Wi-Fi 7 (802.11be) devices The driver first supports Filogic 680 PCI device, which is a Wi-Fi 7 chipset supporting concurrent tri-band operation at 6 GHz, 5 GHz, and 2.4 GHz with 4x4 antennas on each band. Currently, mt7996 only supports tri-band HE or older mode. EHT mode and more variants of Filogic 680 support will be introduced in further patches. Reviewed-by: Ryder Lee Co-developed-by: Peter Chiu Signed-off-by: Peter Chiu Co-developed-by: Bo Jiao Signed-off-by: Bo Jiao Co-developed-by: Howard Hsu Signed-off-by: Howard Hsu Co-developed-by: MeiChia Chiu Signed-off-by: MeiChia Chiu Co-developed-by: StanleyYP Wang Signed-off-by: StanleyYP Wang Co-developed-by: Money Wang Signed-off-by: Money Wang Co-developed-by: Evelyn Tsai Signed-off-by: Evelyn Tsai Signed-off-by: Shayne Chen Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/Kconfig | 1 + drivers/net/wireless/mediatek/mt76/Makefile | 1 + .../net/wireless/mediatek/mt76/mt7996/Kconfig | 12 + .../wireless/mediatek/mt76/mt7996/Makefile | 6 + .../wireless/mediatek/mt76/mt7996/debugfs.c | 851 ++++ .../net/wireless/mediatek/mt76/mt7996/dma.c | 360 ++ .../wireless/mediatek/mt76/mt7996/eeprom.c | 229 ++ .../wireless/mediatek/mt76/mt7996/eeprom.h | 75 + .../net/wireless/mediatek/mt76/mt7996/init.c | 816 ++++ .../net/wireless/mediatek/mt76/mt7996/mac.c | 2468 ++++++++++++ .../net/wireless/mediatek/mt76/mt7996/mac.h | 398 ++ .../net/wireless/mediatek/mt76/mt7996/main.c | 1325 +++++++ .../net/wireless/mediatek/mt76/mt7996/mcu.c | 3429 +++++++++++++++++ .../net/wireless/mediatek/mt76/mt7996/mcu.h | 662 ++++ .../net/wireless/mediatek/mt76/mt7996/mmio.c | 385 ++ .../wireless/mediatek/mt76/mt7996/mt7996.h | 516 +++ .../net/wireless/mediatek/mt76/mt7996/pci.c | 222 ++ .../net/wireless/mediatek/mt76/mt7996/regs.h | 533 +++ 18 files changed, 12289 insertions(+) create mode 100644 drivers/net/wireless/mediatek/mt76/mt7996/Kconfig create mode 100644 drivers/net/wireless/mediatek/mt76/mt7996/Makefile create mode 100644 drivers/net/wireless/mediatek/mt76/mt7996/debugfs.c create mode 100644 drivers/net/wireless/mediatek/mt76/mt7996/dma.c create mode 100644 drivers/net/wireless/mediatek/mt76/mt7996/eeprom.c create mode 100644 drivers/net/wireless/mediatek/mt76/mt7996/eeprom.h create mode 100644 drivers/net/wireless/mediatek/mt76/mt7996/init.c create mode 100644 drivers/net/wireless/mediatek/mt76/mt7996/mac.c create mode 100644 drivers/net/wireless/mediatek/mt76/mt7996/mac.h create mode 100644 drivers/net/wireless/mediatek/mt76/mt7996/main.c create mode 100644 drivers/net/wireless/mediatek/mt76/mt7996/mcu.c create mode 100644 drivers/net/wireless/mediatek/mt76/mt7996/mcu.h create mode 100644 drivers/net/wireless/mediatek/mt76/mt7996/mmio.c create mode 100644 drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h create mode 100644 drivers/net/wireless/mediatek/mt76/mt7996/pci.c create mode 100644 drivers/net/wireless/mediatek/mt76/mt7996/regs.h diff --git a/drivers/net/wireless/mediatek/mt76/Kconfig b/drivers/net/wireless/mediatek/mt76/Kconfig index 9ff43f1fc50d..d7f90a0eb21e 100644 --- a/drivers/net/wireless/mediatek/mt76/Kconfig +++ b/drivers/net/wireless/mediatek/mt76/Kconfig @@ -34,3 +34,4 @@ source "drivers/net/wireless/mediatek/mt76/mt7603/Kconfig" source "drivers/net/wireless/mediatek/mt76/mt7615/Kconfig" source "drivers/net/wireless/mediatek/mt76/mt7915/Kconfig" source "drivers/net/wireless/mediatek/mt76/mt7921/Kconfig" +source "drivers/net/wireless/mediatek/mt76/mt7996/Kconfig" diff --git a/drivers/net/wireless/mediatek/mt76/Makefile b/drivers/net/wireless/mediatek/mt76/Makefile index c78ae4b89761..84c99b7e57f9 100644 --- a/drivers/net/wireless/mediatek/mt76/Makefile +++ b/drivers/net/wireless/mediatek/mt76/Makefile @@ -35,3 +35,4 @@ obj-$(CONFIG_MT7603E) += mt7603/ obj-$(CONFIG_MT7615_COMMON) += mt7615/ obj-$(CONFIG_MT7915E) += mt7915/ obj-$(CONFIG_MT7921_COMMON) += mt7921/ +obj-$(CONFIG_MT7996E) += mt7996/ diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/Kconfig b/drivers/net/wireless/mediatek/mt76/mt7996/Kconfig new file mode 100644 index 000000000000..5c5fc569e6d5 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7996/Kconfig @@ -0,0 +1,12 @@ +# SPDX-License-Identifier: ISC +config MT7996E + tristate "MediaTek MT7996 (PCIe) support" + select MT76_CONNAC_LIB + depends on MAC80211 + depends on PCI + help + This adds support for MT7996-based wireless PCIe devices, + which support concurrent tri-band operation at 6GHz, 5GHz, + and 2.4GHz IEEE 802.11be 4x4:4SS 4096-QAM, 320MHz channels. + + To compile this driver as a module, choose M here. diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/Makefile b/drivers/net/wireless/mediatek/mt76/mt7996/Makefile new file mode 100644 index 000000000000..bcb9a3c53149 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7996/Makefile @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: ISC + +obj-$(CONFIG_MT7996E) += mt7996e.o + +mt7996e-y := pci.o init.o dma.o eeprom.o main.o mcu.o mac.o \ + debugfs.o mmio.o diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/debugfs.c b/drivers/net/wireless/mediatek/mt76/mt7996/debugfs.c new file mode 100644 index 000000000000..2e4a8909b9e8 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7996/debugfs.c @@ -0,0 +1,851 @@ +// SPDX-License-Identifier: ISC +/* + * Copyright (C) 2022 MediaTek Inc. + */ + +#include +#include "mt7996.h" +#include "eeprom.h" +#include "mcu.h" +#include "mac.h" + +#define FW_BIN_LOG_MAGIC 0x44d9c99a + +/** global debugfs **/ + +struct hw_queue_map { + const char *name; + u8 index; + u8 pid; + u8 qid; +}; + +static int +mt7996_implicit_txbf_set(void *data, u64 val) +{ + struct mt7996_dev *dev = data; + + /* The existing connected stations shall reconnect to apply + * new implicit txbf configuration. + */ + dev->ibf = !!val; + + return mt7996_mcu_set_txbf(dev, BF_HW_EN_UPDATE); +} + +static int +mt7996_implicit_txbf_get(void *data, u64 *val) +{ + struct mt7996_dev *dev = data; + + *val = dev->ibf; + + return 0; +} + +DEFINE_DEBUGFS_ATTRIBUTE(fops_implicit_txbf, mt7996_implicit_txbf_get, + mt7996_implicit_txbf_set, "%lld\n"); + +/* test knob of system error recovery */ +static ssize_t +mt7996_fw_ser_set(struct file *file, const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct mt7996_phy *phy = file->private_data; + struct mt7996_dev *dev = phy->dev; + u8 band_idx = phy->mt76->band_idx; + char buf[16]; + int ret = 0; + u16 val; + + if (count >= sizeof(buf)) + return -EINVAL; + + if (copy_from_user(buf, user_buf, count)) + return -EFAULT; + + if (count && buf[count - 1] == '\n') + buf[count - 1] = '\0'; + else + buf[count] = '\0'; + + if (kstrtou16(buf, 0, &val)) + return -EINVAL; + + switch (val) { + case SER_SET_RECOVER_L1: + case SER_SET_RECOVER_L2: + case SER_SET_RECOVER_L3_RX_ABORT: + case SER_SET_RECOVER_L3_TX_ABORT: + case SER_SET_RECOVER_L3_TX_DISABLE: + case SER_SET_RECOVER_L3_BF: + ret = mt7996_mcu_set_ser(dev, SER_ENABLE, BIT(val), band_idx); + if (ret) + return ret; + + ret = mt7996_mcu_set_ser(dev, SER_RECOVER, val, band_idx); + break; + default: + break; + } + + return ret ? ret : count; +} + +static const struct file_operations mt7996_fw_ser_ops = { + .write = mt7996_fw_ser_set, + /* TODO: ser read */ + .open = simple_open, + .llseek = default_llseek, +}; + +static int +mt7996_radar_trigger(void *data, u64 val) +{ + struct mt7996_dev *dev = data; + + if (val > MT_RX_SEL2) + return -EINVAL; + + return mt7996_mcu_rdd_cmd(dev, RDD_RADAR_EMULATE, + val, 0, 0); +} + +DEFINE_DEBUGFS_ATTRIBUTE(fops_radar_trigger, NULL, + mt7996_radar_trigger, "%lld\n"); + +static int +mt7996_rdd_monitor(struct seq_file *s, void *data) +{ + struct mt7996_dev *dev = dev_get_drvdata(s->private); + struct cfg80211_chan_def *chandef = &dev->rdd2_chandef; + const char *bw; + int ret = 0; + + mutex_lock(&dev->mt76.mutex); + + if (!cfg80211_chandef_valid(chandef)) { + ret = -EINVAL; + goto out; + } + + if (!dev->rdd2_phy) { + seq_puts(s, "not running\n"); + goto out; + } + + switch (chandef->width) { + case NL80211_CHAN_WIDTH_40: + bw = "40"; + break; + case NL80211_CHAN_WIDTH_80: + bw = "80"; + break; + case NL80211_CHAN_WIDTH_160: + bw = "160"; + break; + case NL80211_CHAN_WIDTH_80P80: + bw = "80P80"; + break; + default: + bw = "20"; + break; + } + + seq_printf(s, "channel %d (%d MHz) width %s MHz center1: %d MHz\n", + chandef->chan->hw_value, chandef->chan->center_freq, + bw, chandef->center_freq1); +out: + mutex_unlock(&dev->mt76.mutex); + + return ret; +} + +static int +mt7996_fw_debug_wm_set(void *data, u64 val) +{ + struct mt7996_dev *dev = data; + enum { + DEBUG_TXCMD = 62, + DEBUG_CMD_RPT_TX, + DEBUG_CMD_RPT_TRIG, + DEBUG_SPL, + DEBUG_RPT_RX, + DEBUG_RPT_RA = 68, + } debug; + bool tx, rx, en; + int ret; + + dev->fw_debug_wm = val ? MCU_FW_LOG_TO_HOST : 0; + + if (dev->fw_debug_bin) + val = MCU_FW_LOG_RELAY; + else + val = dev->fw_debug_wm; + + tx = dev->fw_debug_wm || (dev->fw_debug_bin & BIT(1)); + rx = dev->fw_debug_wm || (dev->fw_debug_bin & BIT(2)); + en = dev->fw_debug_wm || (dev->fw_debug_bin & BIT(0)); + + ret = mt7996_mcu_fw_log_2_host(dev, MCU_FW_LOG_WM, val); + if (ret) + return ret; + + for (debug = DEBUG_TXCMD; debug <= DEBUG_RPT_RA; debug++) { + if (debug == 67) + continue; + + if (debug == DEBUG_RPT_RX) + val = en && rx; + else + val = en && tx; + + ret = mt7996_mcu_fw_dbg_ctrl(dev, debug, val); + if (ret) + return ret; + } + + return 0; +} + +static int +mt7996_fw_debug_wm_get(void *data, u64 *val) +{ + struct mt7996_dev *dev = data; + + *val = dev->fw_debug_wm; + + return 0; +} + +DEFINE_DEBUGFS_ATTRIBUTE(fops_fw_debug_wm, mt7996_fw_debug_wm_get, + mt7996_fw_debug_wm_set, "%lld\n"); + +static int +mt7996_fw_debug_wa_set(void *data, u64 val) +{ + struct mt7996_dev *dev = data; + int ret; + + dev->fw_debug_wa = val ? MCU_FW_LOG_TO_HOST : 0; + + ret = mt7996_mcu_fw_log_2_host(dev, MCU_FW_LOG_WA, dev->fw_debug_wa); + if (ret) + return ret; + + return mt7996_mcu_wa_cmd(dev, MCU_WA_PARAM_CMD(SET), MCU_WA_PARAM_PDMA_RX, + !!dev->fw_debug_wa, 0); +} + +static int +mt7996_fw_debug_wa_get(void *data, u64 *val) +{ + struct mt7996_dev *dev = data; + + *val = dev->fw_debug_wa; + + return 0; +} + +DEFINE_DEBUGFS_ATTRIBUTE(fops_fw_debug_wa, mt7996_fw_debug_wa_get, + mt7996_fw_debug_wa_set, "%lld\n"); + +static struct dentry * +create_buf_file_cb(const char *filename, struct dentry *parent, umode_t mode, + struct rchan_buf *buf, int *is_global) +{ + struct dentry *f; + + f = debugfs_create_file("fwlog_data", mode, parent, buf, + &relay_file_operations); + if (IS_ERR(f)) + return NULL; + + *is_global = 1; + + return f; +} + +static int +remove_buf_file_cb(struct dentry *f) +{ + debugfs_remove(f); + + return 0; +} + +static int +mt7996_fw_debug_bin_set(void *data, u64 val) +{ + static struct rchan_callbacks relay_cb = { + .create_buf_file = create_buf_file_cb, + .remove_buf_file = remove_buf_file_cb, + }; + struct mt7996_dev *dev = data; + + if (!dev->relay_fwlog) + dev->relay_fwlog = relay_open("fwlog_data", dev->debugfs_dir, + 1500, 512, &relay_cb, NULL); + if (!dev->relay_fwlog) + return -ENOMEM; + + dev->fw_debug_bin = val; + + relay_reset(dev->relay_fwlog); + + return mt7996_fw_debug_wm_set(dev, dev->fw_debug_wm); +} + +static int +mt7996_fw_debug_bin_get(void *data, u64 *val) +{ + struct mt7996_dev *dev = data; + + *val = dev->fw_debug_bin; + + return 0; +} + +DEFINE_DEBUGFS_ATTRIBUTE(fops_fw_debug_bin, mt7996_fw_debug_bin_get, + mt7996_fw_debug_bin_set, "%lld\n"); + +static int +mt7996_fw_util_wa_show(struct seq_file *file, void *data) +{ + struct mt7996_dev *dev = file->private; + + if (dev->fw_debug_wa) + return mt7996_mcu_wa_cmd(dev, MCU_WA_PARAM_CMD(QUERY), + MCU_WA_PARAM_CPU_UTIL, 0, 0); + + return 0; +} + +DEFINE_SHOW_ATTRIBUTE(mt7996_fw_util_wa); + +static void +mt7996_ampdu_stat_read_phy(struct mt7996_phy *phy, struct seq_file *file) +{ + struct mt7996_dev *dev = phy->dev; + int bound[15], range[8], i; + u8 band_idx = phy->mt76->band_idx; + + /* Tx ampdu stat */ + for (i = 0; i < ARRAY_SIZE(range); i++) + range[i] = mt76_rr(dev, MT_MIB_ARNG(band_idx, i)); + + for (i = 0; i < ARRAY_SIZE(bound); i++) + bound[i] = MT_MIB_ARNCR_RANGE(range[i / 2], i % 2) + 1; + + seq_printf(file, "\nPhy %s, Phy band %d\n", + wiphy_name(phy->mt76->hw->wiphy), band_idx); + + seq_printf(file, "Length: %8d | ", bound[0]); + for (i = 0; i < ARRAY_SIZE(bound) - 1; i++) + seq_printf(file, "%3d -%3d | ", + bound[i] + 1, bound[i + 1]); + + seq_puts(file, "\nCount: "); + for (i = 0; i < ARRAY_SIZE(bound); i++) + seq_printf(file, "%8d | ", phy->mt76->aggr_stats[i]); + seq_puts(file, "\n"); + + seq_printf(file, "BA miss count: %d\n", phy->mib.ba_miss_cnt); +} + +static void +mt7996_txbf_stat_read_phy(struct mt7996_phy *phy, struct seq_file *s) +{ + static const char * const bw[] = { + "BW20", "BW40", "BW80", "BW160" + }; + struct mib_stats *mib = &phy->mib; + + /* Tx Beamformer monitor */ + seq_puts(s, "\nTx Beamformer applied PPDU counts: "); + + seq_printf(s, "iBF: %d, eBF: %d\n", + mib->tx_bf_ibf_ppdu_cnt, + mib->tx_bf_ebf_ppdu_cnt); + + /* Tx Beamformer Rx feedback monitor */ + seq_puts(s, "Tx Beamformer Rx feedback statistics: "); + + seq_printf(s, "All: %d, HE: %d, VHT: %d, HT: %d, ", + mib->tx_bf_rx_fb_all_cnt, + mib->tx_bf_rx_fb_he_cnt, + mib->tx_bf_rx_fb_vht_cnt, + mib->tx_bf_rx_fb_ht_cnt); + + seq_printf(s, "%s, NC: %d, NR: %d\n", + bw[mib->tx_bf_rx_fb_bw], + mib->tx_bf_rx_fb_nc_cnt, + mib->tx_bf_rx_fb_nr_cnt); + + /* Tx Beamformee Rx NDPA & Tx feedback report */ + seq_printf(s, "Tx Beamformee successful feedback frames: %d\n", + mib->tx_bf_fb_cpl_cnt); + seq_printf(s, "Tx Beamformee feedback triggered counts: %d\n", + mib->tx_bf_fb_trig_cnt); + + /* Tx SU & MU counters */ + seq_printf(s, "Tx multi-user Beamforming counts: %d\n", + mib->tx_mu_bf_cnt); + seq_printf(s, "Tx multi-user MPDU counts: %d\n", mib->tx_mu_mpdu_cnt); + seq_printf(s, "Tx multi-user successful MPDU counts: %d\n", + mib->tx_mu_acked_mpdu_cnt); + seq_printf(s, "Tx single-user successful MPDU counts: %d\n", + mib->tx_su_acked_mpdu_cnt); + + seq_puts(s, "\n"); +} + +static int +mt7996_tx_stats_show(struct seq_file *file, void *data) +{ + struct mt7996_phy *phy = file->private; + struct mt7996_dev *dev = phy->dev; + struct mib_stats *mib = &phy->mib; + int i; + u32 attempts, success, per; + + mutex_lock(&dev->mt76.mutex); + + mt7996_mac_update_stats(phy); + mt7996_ampdu_stat_read_phy(phy, file); + + attempts = mib->tx_mpdu_attempts_cnt; + success = mib->tx_mpdu_success_cnt; + per = attempts ? 100 - success * 100 / attempts : 100; + seq_printf(file, "Tx attempts: %8u (MPDUs)\n", attempts); + seq_printf(file, "Tx success: %8u (MPDUs)\n", success); + seq_printf(file, "Tx PER: %u%%\n", per); + + mt7996_txbf_stat_read_phy(phy, file); + + /* Tx amsdu info */ + seq_puts(file, "Tx MSDU statistics:\n"); + for (i = 0; i < ARRAY_SIZE(mib->tx_amsdu); i++) { + seq_printf(file, "AMSDU pack count of %d MSDU in TXD: %8d ", + i + 1, mib->tx_amsdu[i]); + if (mib->tx_amsdu_cnt) + seq_printf(file, "(%3d%%)\n", + mib->tx_amsdu[i] * 100 / mib->tx_amsdu_cnt); + else + seq_puts(file, "\n"); + } + + mutex_unlock(&dev->mt76.mutex); + + return 0; +} + +DEFINE_SHOW_ATTRIBUTE(mt7996_tx_stats); + +static void +mt7996_hw_queue_read(struct seq_file *s, u32 size, + const struct hw_queue_map *map) +{ + struct mt7996_phy *phy = s->private; + struct mt7996_dev *dev = phy->dev; + u32 i, val; + + val = mt76_rr(dev, MT_FL_Q_EMPTY); + for (i = 0; i < size; i++) { + u32 ctrl, head, tail, queued; + + if (val & BIT(map[i].index)) + continue; + + ctrl = BIT(31) | (map[i].pid << 10) | (map[i].qid << 24); + mt76_wr(dev, MT_FL_Q0_CTRL, ctrl); + + head = mt76_get_field(dev, MT_FL_Q2_CTRL, + GENMASK(11, 0)); + tail = mt76_get_field(dev, MT_FL_Q2_CTRL, + GENMASK(27, 16)); + queued = mt76_get_field(dev, MT_FL_Q3_CTRL, + GENMASK(11, 0)); + + seq_printf(s, "\t%s: ", map[i].name); + seq_printf(s, "queued:0x%03x head:0x%03x tail:0x%03x\n", + queued, head, tail); + } +} + +static void +mt7996_sta_hw_queue_read(void *data, struct ieee80211_sta *sta) +{ + struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv; + struct mt7996_dev *dev = msta->vif->phy->dev; + struct seq_file *s = data; + u8 ac; + + for (ac = 0; ac < 4; ac++) { + u32 qlen, ctrl, val; + u32 idx = msta->wcid.idx >> 5; + u8 offs = msta->wcid.idx & GENMASK(4, 0); + + ctrl = BIT(31) | BIT(11) | (ac << 24); + val = mt76_rr(dev, MT_PLE_AC_QEMPTY(ac, idx)); + + if (val & BIT(offs)) + continue; + + mt76_wr(dev, MT_FL_Q0_CTRL, ctrl | msta->wcid.idx); + qlen = mt76_get_field(dev, MT_FL_Q3_CTRL, + GENMASK(11, 0)); + seq_printf(s, "\tSTA %pM wcid %d: AC%d%d queued:%d\n", + sta->addr, msta->wcid.idx, + msta->vif->mt76.wmm_idx, ac, qlen); + } +} + +static int +mt7996_hw_queues_show(struct seq_file *file, void *data) +{ + struct mt7996_phy *phy = file->private; + struct mt7996_dev *dev = phy->dev; + static const struct hw_queue_map ple_queue_map[] = { + { "CPU_Q0", 0, 1, MT_CTX0 }, + { "CPU_Q1", 1, 1, MT_CTX0 + 1 }, + { "CPU_Q2", 2, 1, MT_CTX0 + 2 }, + { "CPU_Q3", 3, 1, MT_CTX0 + 3 }, + { "ALTX_Q0", 8, 2, MT_LMAC_ALTX0 }, + { "BMC_Q0", 9, 2, MT_LMAC_BMC0 }, + { "BCN_Q0", 10, 2, MT_LMAC_BCN0 }, + { "PSMP_Q0", 11, 2, MT_LMAC_PSMP0 }, + { "ALTX_Q1", 12, 2, MT_LMAC_ALTX0 + 4 }, + { "BMC_Q1", 13, 2, MT_LMAC_BMC0 + 4 }, + { "BCN_Q1", 14, 2, MT_LMAC_BCN0 + 4 }, + { "PSMP_Q1", 15, 2, MT_LMAC_PSMP0 + 4 }, + }; + static const struct hw_queue_map pse_queue_map[] = { + { "CPU Q0", 0, 1, MT_CTX0 }, + { "CPU Q1", 1, 1, MT_CTX0 + 1 }, + { "CPU Q2", 2, 1, MT_CTX0 + 2 }, + { "CPU Q3", 3, 1, MT_CTX0 + 3 }, + { "HIF_Q0", 8, 0, MT_HIF0 }, + { "HIF_Q1", 9, 0, MT_HIF0 + 1 }, + { "HIF_Q2", 10, 0, MT_HIF0 + 2 }, + { "HIF_Q3", 11, 0, MT_HIF0 + 3 }, + { "HIF_Q4", 12, 0, MT_HIF0 + 4 }, + { "HIF_Q5", 13, 0, MT_HIF0 + 5 }, + { "LMAC_Q", 16, 2, 0 }, + { "MDP_TXQ", 17, 2, 1 }, + { "MDP_RXQ", 18, 2, 2 }, + { "SEC_TXQ", 19, 2, 3 }, + { "SEC_RXQ", 20, 2, 4 }, + }; + u32 val, head, tail; + + /* ple queue */ + val = mt76_rr(dev, MT_PLE_FREEPG_CNT); + head = mt76_get_field(dev, MT_PLE_FREEPG_HEAD_TAIL, GENMASK(11, 0)); + tail = mt76_get_field(dev, MT_PLE_FREEPG_HEAD_TAIL, GENMASK(27, 16)); + seq_puts(file, "PLE page info:\n"); + seq_printf(file, + "\tTotal free page: 0x%08x head: 0x%03x tail: 0x%03x\n", + val, head, tail); + + val = mt76_rr(dev, MT_PLE_PG_HIF_GROUP); + head = mt76_get_field(dev, MT_PLE_HIF_PG_INFO, GENMASK(11, 0)); + tail = mt76_get_field(dev, MT_PLE_HIF_PG_INFO, GENMASK(27, 16)); + seq_printf(file, "\tHIF free page: 0x%03x res: 0x%03x used: 0x%03x\n", + val, head, tail); + + seq_puts(file, "PLE non-empty queue info:\n"); + mt7996_hw_queue_read(file, ARRAY_SIZE(ple_queue_map), + &ple_queue_map[0]); + + /* iterate per-sta ple queue */ + ieee80211_iterate_stations_atomic(phy->mt76->hw, + mt7996_sta_hw_queue_read, file); + /* pse queue */ + seq_puts(file, "PSE non-empty queue info:\n"); + mt7996_hw_queue_read(file, ARRAY_SIZE(pse_queue_map), + &pse_queue_map[0]); + + return 0; +} + +DEFINE_SHOW_ATTRIBUTE(mt7996_hw_queues); + +static int +mt7996_xmit_queues_show(struct seq_file *file, void *data) +{ + struct mt7996_phy *phy = file->private; + struct mt7996_dev *dev = phy->dev; + struct { + struct mt76_queue *q; + char *queue; + } queue_map[] = { + { phy->mt76->q_tx[MT_TXQ_BE], " MAIN" }, + { dev->mt76.q_mcu[MT_MCUQ_WM], " MCUWM" }, + { dev->mt76.q_mcu[MT_MCUQ_WA], " MCUWA" }, + { dev->mt76.q_mcu[MT_MCUQ_FWDL], "MCUFWDL" }, + }; + int i; + + seq_puts(file, " queue | hw-queued | head | tail |\n"); + for (i = 0; i < ARRAY_SIZE(queue_map); i++) { + struct mt76_queue *q = queue_map[i].q; + + if (!q) + continue; + + seq_printf(file, " %s | %9d | %9d | %9d |\n", + queue_map[i].queue, q->queued, q->head, + q->tail); + } + + return 0; +} + +DEFINE_SHOW_ATTRIBUTE(mt7996_xmit_queues); + +static int +mt7996_twt_stats(struct seq_file *s, void *data) +{ + struct mt7996_dev *dev = dev_get_drvdata(s->private); + struct mt7996_twt_flow *iter; + + rcu_read_lock(); + + seq_puts(s, " wcid | id | flags | exp | mantissa"); + seq_puts(s, " | duration | tsf |\n"); + list_for_each_entry_rcu(iter, &dev->twt_list, list) + seq_printf(s, + "%9d | %8d | %5c%c%c%c | %8d | %8d | %8d | %14lld |\n", + iter->wcid, iter->id, + iter->sched ? 's' : 'u', + iter->protection ? 'p' : '-', + iter->trigger ? 't' : '-', + iter->flowtype ? '-' : 'a', + iter->exp, iter->mantissa, + iter->duration, iter->tsf); + + rcu_read_unlock(); + + return 0; +} + +/* The index of RF registers use the generic regidx, combined with two parts: + * WF selection [31:24] and offset [23:0]. + */ +static int +mt7996_rf_regval_get(void *data, u64 *val) +{ + struct mt7996_dev *dev = data; + u32 regval; + int ret; + + ret = mt7996_mcu_rf_regval(dev, dev->mt76.debugfs_reg, ®val, false); + if (ret) + return ret; + + *val = regval; + + return 0; +} + +static int +mt7996_rf_regval_set(void *data, u64 val) +{ + struct mt7996_dev *dev = data; + + return mt7996_mcu_rf_regval(dev, dev->mt76.debugfs_reg, (u32 *)&val, true); +} + +DEFINE_DEBUGFS_ATTRIBUTE(fops_rf_regval, mt7996_rf_regval_get, + mt7996_rf_regval_set, "0x%08llx\n"); + +int mt7996_init_debugfs(struct mt7996_phy *phy) +{ + struct mt7996_dev *dev = phy->dev; + struct dentry *dir; + + dir = mt76_register_debugfs_fops(phy->mt76, NULL); + if (!dir) + return -ENOMEM; + debugfs_create_file("hw-queues", 0400, dir, phy, + &mt7996_hw_queues_fops); + debugfs_create_file("xmit-queues", 0400, dir, phy, + &mt7996_xmit_queues_fops); + debugfs_create_file("tx_stats", 0400, dir, phy, &mt7996_tx_stats_fops); + debugfs_create_file("fw_debug_wm", 0600, dir, dev, &fops_fw_debug_wm); + debugfs_create_file("fw_debug_wa", 0600, dir, dev, &fops_fw_debug_wa); + debugfs_create_file("fw_debug_bin", 0600, dir, dev, &fops_fw_debug_bin); + /* TODO: wm fw cpu utilization */ + debugfs_create_file("fw_util_wa", 0400, dir, dev, + &mt7996_fw_util_wa_fops); + debugfs_create_file("implicit_txbf", 0600, dir, dev, + &fops_implicit_txbf); + debugfs_create_devm_seqfile(dev->mt76.dev, "twt_stats", dir, + mt7996_twt_stats); + debugfs_create_file("fw_ser", 0600, dir, phy, &mt7996_fw_ser_ops); + debugfs_create_file("rf_regval", 0600, dir, dev, &fops_rf_regval); + + if (phy->mt76->cap.has_5ghz) { + debugfs_create_u32("dfs_hw_pattern", 0400, dir, + &dev->hw_pattern); + debugfs_create_file("radar_trigger", 0200, dir, dev, + &fops_radar_trigger); + debugfs_create_devm_seqfile(dev->mt76.dev, "rdd_monitor", dir, + mt7996_rdd_monitor); + } + + if (phy == &dev->phy) + dev->debugfs_dir = dir; + + return 0; +} + +static void +mt7996_debugfs_write_fwlog(struct mt7996_dev *dev, const void *hdr, int hdrlen, + const void *data, int len) +{ + static DEFINE_SPINLOCK(lock); + unsigned long flags; + void *dest; + + spin_lock_irqsave(&lock, flags); + dest = relay_reserve(dev->relay_fwlog, hdrlen + len + 4); + if (dest) { + *(u32 *)dest = hdrlen + len; + dest += 4; + + if (hdrlen) { + memcpy(dest, hdr, hdrlen); + dest += hdrlen; + } + + memcpy(dest, data, len); + relay_flush(dev->relay_fwlog); + } + spin_unlock_irqrestore(&lock, flags); +} + +void mt7996_debugfs_rx_fw_monitor(struct mt7996_dev *dev, const void *data, int len) +{ + struct { + __le32 magic; + u8 version; + u8 _rsv; + __le16 serial_id; + __le32 timestamp; + __le16 msg_type; + __le16 len; + } hdr = { + .version = 0x1, + .magic = cpu_to_le32(FW_BIN_LOG_MAGIC), + .msg_type = cpu_to_le16(PKT_TYPE_RX_FW_MONITOR), + }; + + if (!dev->relay_fwlog) + return; + + hdr.serial_id = cpu_to_le16(dev->fw_debug_seq++); + hdr.timestamp = cpu_to_le32(mt76_rr(dev, MT_LPON_FRCR(0))); + hdr.len = *(__le16 *)data; + mt7996_debugfs_write_fwlog(dev, &hdr, sizeof(hdr), data, len); +} + +bool mt7996_debugfs_rx_log(struct mt7996_dev *dev, const void *data, int len) +{ + if (get_unaligned_le32(data) != FW_BIN_LOG_MAGIC) + return false; + + if (dev->relay_fwlog) + mt7996_debugfs_write_fwlog(dev, NULL, 0, data, len); + + return true; +} + +#ifdef CONFIG_MAC80211_DEBUGFS +/** per-station debugfs **/ + +static ssize_t mt7996_sta_fixed_rate_set(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ +#define SHORT_PREAMBLE 0 +#define LONG_PREAMBLE 1 + struct ieee80211_sta *sta = file->private_data; + struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv; + struct mt7996_dev *dev = msta->vif->phy->dev; + struct ra_rate phy = {}; + char buf[100]; + int ret; + u16 gi, ltf; + + if (count >= sizeof(buf)) + return -EINVAL; + + if (copy_from_user(buf, user_buf, count)) + return -EFAULT; + + if (count && buf[count - 1] == '\n') + buf[count - 1] = '\0'; + else + buf[count] = '\0'; + + /* mode - cck: 0, ofdm: 1, ht: 2, gf: 3, vht: 4, he_su: 8, he_er: 9 + * bw - bw20: 0, bw40: 1, bw80: 2, bw160: 3 + * nss - vht: 1~4, he: 1~4, others: ignore + * mcs - cck: 0~4, ofdm: 0~7, ht: 0~32, vht: 0~9, he_su: 0~11, he_er: 0~2 + * gi - (ht/vht) lgi: 0, sgi: 1; (he) 0.8us: 0, 1.6us: 1, 3.2us: 2 + * preamble - short: 1, long: 0 + * ldpc - off: 0, on: 1 + * stbc - off: 0, on: 1 + * ltf - 1xltf: 0, 2xltf: 1, 4xltf: 2 + */ + if (sscanf(buf, "%hhu %hhu %hhu %hhu %hu %hhu %hhu %hhu %hhu %hu", + &phy.mode, &phy.bw, &phy.mcs, &phy.nss, &gi, + &phy.preamble, &phy.stbc, &phy.ldpc, &phy.spe, <f) != 10) { + dev_warn(dev->mt76.dev, + "format: Mode BW MCS NSS GI Preamble STBC LDPC SPE ltf\n"); + goto out; + } + + phy.wlan_idx = cpu_to_le16(msta->wcid.idx); + phy.gi = cpu_to_le16(gi); + phy.ltf = cpu_to_le16(ltf); + phy.ldpc = phy.ldpc ? 7 : 0; + phy.preamble = phy.preamble ? SHORT_PREAMBLE : LONG_PREAMBLE; + + ret = mt7996_mcu_set_fixed_rate_ctrl(dev, &phy, 0); + if (ret) + return -EFAULT; + +out: + return count; +} + +static const struct file_operations fops_fixed_rate = { + .write = mt7996_sta_fixed_rate_set, + .open = simple_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +static int +mt7996_queues_show(struct seq_file *s, void *data) +{ + struct ieee80211_sta *sta = s->private; + + mt7996_sta_hw_queue_read(s, sta); + + return 0; +} + +DEFINE_SHOW_ATTRIBUTE(mt7996_queues); + +void mt7996_sta_add_debugfs(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_sta *sta, struct dentry *dir) +{ + debugfs_create_file("fixed_rate", 0600, dir, sta, &fops_fixed_rate); + debugfs_create_file("hw-queues", 0400, dir, sta, &mt7996_queues_fops); +} + +#endif diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/dma.c b/drivers/net/wireless/mediatek/mt76/mt7996/dma.c new file mode 100644 index 000000000000..c09fe4274935 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7996/dma.c @@ -0,0 +1,360 @@ +// SPDX-License-Identifier: ISC +/* + * Copyright (C) 2022 MediaTek Inc. + */ + +#include "mt7996.h" +#include "../dma.h" +#include "mac.h" + +static int mt7996_poll_tx(struct napi_struct *napi, int budget) +{ + struct mt7996_dev *dev; + + dev = container_of(napi, struct mt7996_dev, mt76.tx_napi); + + mt76_connac_tx_cleanup(&dev->mt76); + if (napi_complete_done(napi, 0)) + mt7996_irq_enable(dev, MT_INT_TX_DONE_MCU); + + return 0; +} + +static void mt7996_dma_config(struct mt7996_dev *dev) +{ +#define Q_CONFIG(q, wfdma, int, id) do { \ + if (wfdma) \ + dev->q_wfdma_mask |= (1 << (q)); \ + dev->q_int_mask[(q)] = int; \ + dev->q_id[(q)] = id; \ +} while (0) + +#define MCUQ_CONFIG(q, wfdma, int, id) Q_CONFIG(q, (wfdma), (int), (id)) +#define RXQ_CONFIG(q, wfdma, int, id) Q_CONFIG(__RXQ(q), (wfdma), (int), (id)) +#define TXQ_CONFIG(q, wfdma, int, id) Q_CONFIG(__TXQ(q), (wfdma), (int), (id)) + + /* rx queue */ + RXQ_CONFIG(MT_RXQ_MCU, WFDMA0, MT_INT_RX_DONE_WM, MT7996_RXQ_MCU_WM); + RXQ_CONFIG(MT_RXQ_MCU_WA, WFDMA0, MT_INT_RX_DONE_WA, MT7996_RXQ_MCU_WA); + + /* band0/band1 */ + RXQ_CONFIG(MT_RXQ_MAIN, WFDMA0, MT_INT_RX_DONE_BAND0, MT7996_RXQ_BAND0); + RXQ_CONFIG(MT_RXQ_MAIN_WA, WFDMA0, MT_INT_RX_DONE_WA_MAIN, MT7996_RXQ_MCU_WA_MAIN); + + /* band2 */ + RXQ_CONFIG(MT_RXQ_BAND2, WFDMA0, MT_INT_RX_DONE_BAND2, MT7996_RXQ_BAND2); + RXQ_CONFIG(MT_RXQ_BAND2_WA, WFDMA0, MT_INT_RX_DONE_WA_TRI, MT7996_RXQ_MCU_WA_TRI); + + /* data tx queue */ + TXQ_CONFIG(0, WFDMA0, MT_INT_TX_DONE_BAND0, MT7996_TXQ_BAND0); + TXQ_CONFIG(1, WFDMA0, MT_INT_TX_DONE_BAND1, MT7996_TXQ_BAND1); + TXQ_CONFIG(2, WFDMA0, MT_INT_TX_DONE_BAND2, MT7996_TXQ_BAND2); + + /* mcu tx queue */ + MCUQ_CONFIG(MT_MCUQ_WM, WFDMA0, MT_INT_TX_DONE_MCU_WM, MT7996_TXQ_MCU_WM); + MCUQ_CONFIG(MT_MCUQ_WA, WFDMA0, MT_INT_TX_DONE_MCU_WA, MT7996_TXQ_MCU_WA); + MCUQ_CONFIG(MT_MCUQ_FWDL, WFDMA0, MT_INT_TX_DONE_FWDL, MT7996_TXQ_FWDL); +} + +static void __mt7996_dma_prefetch(struct mt7996_dev *dev, u32 ofs) +{ +#define PREFETCH(_base, _depth) ((_base) << 16 | (_depth)) + /* prefetch SRAM wrapping boundary for tx/rx ring. */ + mt76_wr(dev, MT_MCUQ_EXT_CTRL(MT_MCUQ_FWDL) + ofs, PREFETCH(0x0, 0x2)); + mt76_wr(dev, MT_MCUQ_EXT_CTRL(MT_MCUQ_WM) + ofs, PREFETCH(0x20, 0x2)); + mt76_wr(dev, MT_TXQ_EXT_CTRL(0) + ofs, PREFETCH(0x40, 0x4)); + mt76_wr(dev, MT_TXQ_EXT_CTRL(1) + ofs, PREFETCH(0x80, 0x4)); + mt76_wr(dev, MT_MCUQ_EXT_CTRL(MT_MCUQ_WA) + ofs, PREFETCH(0xc0, 0x2)); + mt76_wr(dev, MT_TXQ_EXT_CTRL(2) + ofs, PREFETCH(0xe0, 0x4)); + mt76_wr(dev, MT_RXQ_BAND1_CTRL(MT_RXQ_MCU) + ofs, PREFETCH(0x120, 0x2)); + mt76_wr(dev, MT_RXQ_BAND1_CTRL(MT_RXQ_MCU_WA) + ofs, PREFETCH(0x140, 0x2)); + mt76_wr(dev, MT_RXQ_BAND1_CTRL(MT_RXQ_MAIN_WA) + ofs, PREFETCH(0x160, 0x2)); + mt76_wr(dev, MT_RXQ_BAND1_CTRL(MT_RXQ_BAND2_WA) + ofs, PREFETCH(0x180, 0x2)); + mt76_wr(dev, MT_RXQ_BAND1_CTRL(MT_RXQ_MAIN) + ofs, PREFETCH(0x1a0, 0x10)); + mt76_wr(dev, MT_RXQ_BAND1_CTRL(MT_RXQ_BAND2) + ofs, PREFETCH(0x2a0, 0x10)); + + mt76_set(dev, WF_WFDMA0_GLO_CFG_EXT1 + ofs, WF_WFDMA0_GLO_CFG_EXT1_CALC_MODE); +} + +void mt7996_dma_prefetch(struct mt7996_dev *dev) +{ + __mt7996_dma_prefetch(dev, 0); + if (dev->hif2) + __mt7996_dma_prefetch(dev, MT_WFDMA0_PCIE1(0) - MT_WFDMA0(0)); +} + +static void mt7996_dma_disable(struct mt7996_dev *dev, bool reset) +{ + u32 hif1_ofs = 0; + + if (dev->hif2) + hif1_ofs = MT_WFDMA0_PCIE1(0) - MT_WFDMA0(0); + + if (reset) { + mt76_clear(dev, MT_WFDMA0_RST, + MT_WFDMA0_RST_DMASHDL_ALL_RST | + MT_WFDMA0_RST_LOGIC_RST); + + mt76_set(dev, MT_WFDMA0_RST, + MT_WFDMA0_RST_DMASHDL_ALL_RST | + MT_WFDMA0_RST_LOGIC_RST); + + if (dev->hif2) { + mt76_clear(dev, MT_WFDMA0_RST + hif1_ofs, + MT_WFDMA0_RST_DMASHDL_ALL_RST | + MT_WFDMA0_RST_LOGIC_RST); + + mt76_set(dev, MT_WFDMA0_RST + hif1_ofs, + MT_WFDMA0_RST_DMASHDL_ALL_RST | + MT_WFDMA0_RST_LOGIC_RST); + } + } + + /* disable */ + mt76_clear(dev, MT_WFDMA0_GLO_CFG, + MT_WFDMA0_GLO_CFG_TX_DMA_EN | + MT_WFDMA0_GLO_CFG_RX_DMA_EN | + MT_WFDMA0_GLO_CFG_OMIT_TX_INFO | + MT_WFDMA0_GLO_CFG_OMIT_RX_INFO | + MT_WFDMA0_GLO_CFG_OMIT_RX_INFO_PFET2); + + if (dev->hif2) { + mt76_clear(dev, MT_WFDMA0_GLO_CFG + hif1_ofs, + MT_WFDMA0_GLO_CFG_TX_DMA_EN | + MT_WFDMA0_GLO_CFG_RX_DMA_EN | + MT_WFDMA0_GLO_CFG_OMIT_TX_INFO | + MT_WFDMA0_GLO_CFG_OMIT_RX_INFO | + MT_WFDMA0_GLO_CFG_OMIT_RX_INFO_PFET2); + } +} + +static int mt7996_dma_enable(struct mt7996_dev *dev) +{ + u32 hif1_ofs = 0; + u32 irq_mask; + + if (dev->hif2) + hif1_ofs = MT_WFDMA0_PCIE1(0) - MT_WFDMA0(0); + + /* reset dma idx */ + mt76_wr(dev, MT_WFDMA0_RST_DTX_PTR, ~0); + if (dev->hif2) + mt76_wr(dev, MT_WFDMA0_RST_DTX_PTR + hif1_ofs, ~0); + + /* configure delay interrupt off */ + mt76_wr(dev, MT_WFDMA0_PRI_DLY_INT_CFG0, 0); + mt76_wr(dev, MT_WFDMA0_PRI_DLY_INT_CFG1, 0); + mt76_wr(dev, MT_WFDMA0_PRI_DLY_INT_CFG2, 0); + + if (dev->hif2) { + mt76_wr(dev, MT_WFDMA0_PRI_DLY_INT_CFG0 + hif1_ofs, 0); + mt76_wr(dev, MT_WFDMA0_PRI_DLY_INT_CFG1 + hif1_ofs, 0); + mt76_wr(dev, MT_WFDMA0_PRI_DLY_INT_CFG2 + hif1_ofs, 0); + } + + /* configure perfetch settings */ + mt7996_dma_prefetch(dev); + + /* hif wait WFDMA idle */ + mt76_set(dev, MT_WFDMA0_BUSY_ENA, + MT_WFDMA0_BUSY_ENA_TX_FIFO0 | + MT_WFDMA0_BUSY_ENA_TX_FIFO1 | + MT_WFDMA0_BUSY_ENA_RX_FIFO); + + if (dev->hif2) + mt76_set(dev, MT_WFDMA0_BUSY_ENA + hif1_ofs, + MT_WFDMA0_PCIE1_BUSY_ENA_TX_FIFO0 | + MT_WFDMA0_PCIE1_BUSY_ENA_TX_FIFO1 | + MT_WFDMA0_PCIE1_BUSY_ENA_RX_FIFO); + + mt76_poll(dev, MT_WFDMA_EXT_CSR_HIF_MISC, + MT_WFDMA_EXT_CSR_HIF_MISC_BUSY, 0, 1000); + + /* set WFDMA Tx/Rx */ + mt76_set(dev, MT_WFDMA0_GLO_CFG, + MT_WFDMA0_GLO_CFG_TX_DMA_EN | + MT_WFDMA0_GLO_CFG_RX_DMA_EN | + MT_WFDMA0_GLO_CFG_OMIT_TX_INFO | + MT_WFDMA0_GLO_CFG_OMIT_RX_INFO_PFET2); + + /* GLO_CFG_EXT0 */ + mt76_set(dev, WF_WFDMA0_GLO_CFG_EXT0, + WF_WFDMA0_GLO_CFG_EXT0_RX_WB_RXD | + WF_WFDMA0_GLO_CFG_EXT0_WED_MERGE_MODE); + + /* GLO_CFG_EXT1 */ + mt76_set(dev, WF_WFDMA0_GLO_CFG_EXT1, + WF_WFDMA0_GLO_CFG_EXT1_TX_FCTRL_MODE); + + if (dev->hif2) { + mt76_set(dev, MT_WFDMA0_GLO_CFG + hif1_ofs, + MT_WFDMA0_GLO_CFG_TX_DMA_EN | + MT_WFDMA0_GLO_CFG_RX_DMA_EN | + MT_WFDMA0_GLO_CFG_OMIT_TX_INFO | + MT_WFDMA0_GLO_CFG_OMIT_RX_INFO_PFET2); + + /* GLO_CFG_EXT0 */ + mt76_set(dev, WF_WFDMA0_GLO_CFG_EXT0 + hif1_ofs, + WF_WFDMA0_GLO_CFG_EXT0_RX_WB_RXD | + WF_WFDMA0_GLO_CFG_EXT0_WED_MERGE_MODE); + + /* GLO_CFG_EXT1 */ + mt76_set(dev, WF_WFDMA0_GLO_CFG_EXT1 + hif1_ofs, + WF_WFDMA0_GLO_CFG_EXT1_TX_FCTRL_MODE); + + mt76_set(dev, MT_WFDMA_HOST_CONFIG, + MT_WFDMA_HOST_CONFIG_PDMA_BAND); + } + + if (dev->hif2) { + /* fix hardware limitation, pcie1's rx ring3 is not available + * so, redirect pcie0 rx ring3 interrupt to pcie1 + */ + mt76_set(dev, MT_WFDMA0_RX_INT_PCIE_SEL, + MT_WFDMA0_RX_INT_SEL_RING3); + + /* TODO: redirect rx ring6 interrupt to pcie0 for wed function */ + } + + /* enable interrupts for TX/RX rings */ + irq_mask = MT_INT_RX_DONE_MCU | + MT_INT_TX_DONE_MCU | + MT_INT_MCU_CMD; + + if (!dev->mphy.band_idx) + irq_mask |= MT_INT_BAND0_RX_DONE; + + if (dev->dbdc_support) + irq_mask |= MT_INT_BAND1_RX_DONE; + + if (dev->tbtc_support) + irq_mask |= MT_INT_BAND2_RX_DONE; + + mt7996_irq_enable(dev, irq_mask); + + return 0; +} + +int mt7996_dma_init(struct mt7996_dev *dev) +{ + u32 hif1_ofs = 0; + int ret; + + mt7996_dma_config(dev); + + mt76_dma_attach(&dev->mt76); + + if (dev->hif2) + hif1_ofs = MT_WFDMA0_PCIE1(0) - MT_WFDMA0(0); + + mt7996_dma_disable(dev, true); + + /* init tx queue */ + ret = mt76_connac_init_tx_queues(dev->phy.mt76, + MT_TXQ_ID(dev->mphy.band_idx), + MT7996_TX_RING_SIZE, + MT_TXQ_RING_BASE(0), 0); + if (ret) + return ret; + + /* command to WM */ + ret = mt76_init_mcu_queue(&dev->mt76, MT_MCUQ_WM, + MT_MCUQ_ID(MT_MCUQ_WM), + MT7996_TX_MCU_RING_SIZE, + MT_MCUQ_RING_BASE(MT_MCUQ_WM)); + if (ret) + return ret; + + /* command to WA */ + ret = mt76_init_mcu_queue(&dev->mt76, MT_MCUQ_WA, + MT_MCUQ_ID(MT_MCUQ_WA), + MT7996_TX_MCU_RING_SIZE, + MT_MCUQ_RING_BASE(MT_MCUQ_WA)); + if (ret) + return ret; + + /* firmware download */ + ret = mt76_init_mcu_queue(&dev->mt76, MT_MCUQ_FWDL, + MT_MCUQ_ID(MT_MCUQ_FWDL), + MT7996_TX_FWDL_RING_SIZE, + MT_MCUQ_RING_BASE(MT_MCUQ_FWDL)); + if (ret) + return ret; + + /* event from WM */ + ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_MCU], + MT_RXQ_ID(MT_RXQ_MCU), + MT7996_RX_MCU_RING_SIZE, + MT_RX_BUF_SIZE, + MT_RXQ_RING_BASE(MT_RXQ_MCU)); + if (ret) + return ret; + + /* event from WA */ + ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_MCU_WA], + MT_RXQ_ID(MT_RXQ_MCU_WA), + MT7996_RX_MCU_RING_SIZE, + MT_RX_BUF_SIZE, + MT_RXQ_RING_BASE(MT_RXQ_MCU_WA)); + if (ret) + return ret; + + /* rx data queue for band0 and band1 */ + ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_MAIN], + MT_RXQ_ID(MT_RXQ_MAIN), + MT7996_RX_RING_SIZE, + MT_RX_BUF_SIZE, + MT_RXQ_RING_BASE(MT_RXQ_MAIN)); + if (ret) + return ret; + + /* tx free notify event from WA for band0 */ + ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_MAIN_WA], + MT_RXQ_ID(MT_RXQ_MAIN_WA), + MT7996_RX_MCU_RING_SIZE, + MT_RX_BUF_SIZE, + MT_RXQ_RING_BASE(MT_RXQ_MAIN_WA)); + if (ret) + return ret; + + if (dev->tbtc_support || dev->mphy.band_idx == MT_BAND2) { + /* rx data queue for band2 */ + ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_BAND2], + MT_RXQ_ID(MT_RXQ_BAND2), + MT7996_RX_RING_SIZE, + MT_RX_BUF_SIZE, + MT_RXQ_RING_BASE(MT_RXQ_BAND2) + hif1_ofs); + if (ret) + return ret; + + /* tx free notify event from WA for band2 + * use pcie0's rx ring3, but, redirect pcie0 rx ring3 interrupt to pcie1 + */ + ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_BAND2_WA], + MT_RXQ_ID(MT_RXQ_BAND2_WA), + MT7996_RX_MCU_RING_SIZE, + MT_RX_BUF_SIZE, + MT_RXQ_RING_BASE(MT_RXQ_BAND2_WA)); + if (ret) + return ret; + } + + ret = mt76_init_queues(dev, mt76_dma_rx_poll); + if (ret < 0) + return ret; + + netif_napi_add_tx(&dev->mt76.tx_napi_dev, &dev->mt76.tx_napi, + mt7996_poll_tx); + napi_enable(&dev->mt76.tx_napi); + + mt7996_dma_enable(dev); + + return 0; +} + +void mt7996_dma_cleanup(struct mt7996_dev *dev) +{ + mt7996_dma_disable(dev, true); + + mt76_dma_cleanup(&dev->mt76); +} diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/eeprom.c b/drivers/net/wireless/mediatek/mt76/mt7996/eeprom.c new file mode 100644 index 000000000000..b9f62bedbc48 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7996/eeprom.c @@ -0,0 +1,229 @@ +// SPDX-License-Identifier: ISC +/* + * Copyright (C) 2022 MediaTek Inc. + */ + +#include +#include "mt7996.h" +#include "eeprom.h" + +static int mt7996_check_eeprom(struct mt7996_dev *dev) +{ + u8 *eeprom = dev->mt76.eeprom.data; + u16 val = get_unaligned_le16(eeprom); + + switch (val) { + case 0x7990: + return 0; + default: + return -EINVAL; + } +} + +static char *mt7996_eeprom_name(struct mt7996_dev *dev) +{ + /* reserve for future variants */ + return MT7996_EEPROM_DEFAULT; +} + +static int +mt7996_eeprom_load_default(struct mt7996_dev *dev) +{ + u8 *eeprom = dev->mt76.eeprom.data; + const struct firmware *fw = NULL; + int ret; + + ret = request_firmware(&fw, mt7996_eeprom_name(dev), dev->mt76.dev); + if (ret) + return ret; + + if (!fw || !fw->data) { + dev_err(dev->mt76.dev, "Invalid default bin\n"); + ret = -EINVAL; + goto out; + } + + memcpy(eeprom, fw->data, MT7996_EEPROM_SIZE); + dev->flash_mode = true; + +out: + release_firmware(fw); + + return ret; +} + +static int mt7996_eeprom_load(struct mt7996_dev *dev) +{ + int ret; + + ret = mt76_eeprom_init(&dev->mt76, MT7996_EEPROM_SIZE); + if (ret < 0) + return ret; + + if (ret) { + dev->flash_mode = true; + } else { + u8 free_block_num; + u32 block_num, i; + + /* TODO: check free block event */ + mt7996_mcu_get_eeprom_free_block(dev, &free_block_num); + /* efuse info not enough */ + if (free_block_num >= 59) + return -EINVAL; + + /* read eeprom data from efuse */ + block_num = DIV_ROUND_UP(MT7996_EEPROM_SIZE, MT7996_EEPROM_BLOCK_SIZE); + for (i = 0; i < block_num; i++) + mt7996_mcu_get_eeprom(dev, i * MT7996_EEPROM_BLOCK_SIZE); + } + + return mt7996_check_eeprom(dev); +} + +static int mt7996_eeprom_parse_band_config(struct mt7996_phy *phy) +{ + u8 *eeprom = phy->dev->mt76.eeprom.data; + u32 val = eeprom[MT_EE_WIFI_CONF]; + int ret = 0; + + switch (phy->mt76->band_idx) { + case MT_BAND1: + val = FIELD_GET(MT_EE_WIFI_CONF1_BAND_SEL, val); + break; + case MT_BAND2: + val = eeprom[MT_EE_WIFI_CONF + 1]; + val = FIELD_GET(MT_EE_WIFI_CONF2_BAND_SEL, val); + break; + default: + val = FIELD_GET(MT_EE_WIFI_CONF0_BAND_SEL, val); + break; + } + + switch (val) { + case MT_EE_BAND_SEL_2GHZ: + phy->mt76->cap.has_2ghz = true; + break; + case MT_EE_BAND_SEL_5GHZ: + phy->mt76->cap.has_5ghz = true; + break; + case MT_EE_BAND_SEL_6GHZ: + phy->mt76->cap.has_6ghz = true; + break; + case MT_EE_BAND_SEL_5GHZ_6GHZ: + phy->mt76->cap.has_5ghz = true; + phy->mt76->cap.has_6ghz = true; + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +int mt7996_eeprom_parse_hw_cap(struct mt7996_dev *dev, struct mt7996_phy *phy) +{ + u8 path, nss, band_idx = phy->mt76->band_idx; + u8 *eeprom = dev->mt76.eeprom.data; + struct mt76_phy *mphy = phy->mt76; + + switch (band_idx) { + case MT_BAND1: + path = FIELD_GET(MT_EE_WIFI_CONF2_TX_PATH_BAND1, + eeprom[MT_EE_WIFI_CONF + 2]); + nss = FIELD_GET(MT_EE_WIFI_CONF5_STREAM_NUM_BAND1, + eeprom[MT_EE_WIFI_CONF + 5]); + break; + case MT_BAND2: + path = FIELD_GET(MT_EE_WIFI_CONF2_TX_PATH_BAND2, + eeprom[MT_EE_WIFI_CONF + 2]); + nss = FIELD_GET(MT_EE_WIFI_CONF5_STREAM_NUM_BAND2, + eeprom[MT_EE_WIFI_CONF + 5]); + break; + default: + path = FIELD_GET(MT_EE_WIFI_CONF1_TX_PATH_BAND0, + eeprom[MT_EE_WIFI_CONF + 1]); + nss = FIELD_GET(MT_EE_WIFI_CONF4_STREAM_NUM_BAND0, + eeprom[MT_EE_WIFI_CONF + 4]); + break; + } + + if (!path || path > 4) + path = 4; + + nss = min_t(u8, min_t(u8, 4, nss), path); + + mphy->antenna_mask = BIT(nss) - 1; + mphy->chainmask = (BIT(path) - 1) << dev->chainshift[band_idx]; + dev->chainmask |= mphy->chainmask; + if (band_idx < MT_BAND2) + dev->chainshift[band_idx + 1] = dev->chainshift[band_idx] + + hweight16(mphy->chainmask); + + return mt7996_eeprom_parse_band_config(phy); +} + +int mt7996_eeprom_init(struct mt7996_dev *dev) +{ + int ret; + + ret = mt7996_eeprom_load(dev); + if (ret < 0) { + if (ret != -EINVAL) + return ret; + + dev_warn(dev->mt76.dev, "eeprom load fail, use default bin\n"); + ret = mt7996_eeprom_load_default(dev); + if (ret) + return ret; + } + + ret = mt7996_eeprom_parse_hw_cap(dev, &dev->phy); + if (ret < 0) + return ret; + + memcpy(dev->mphy.macaddr, dev->mt76.eeprom.data + MT_EE_MAC_ADDR, ETH_ALEN); + mt76_eeprom_override(&dev->mphy); + + return 0; +} + +int mt7996_eeprom_get_target_power(struct mt7996_dev *dev, + struct ieee80211_channel *chan) +{ + u8 *eeprom = dev->mt76.eeprom.data; + int target_power; + + if (chan->band == NL80211_BAND_5GHZ) + target_power = eeprom[MT_EE_TX0_POWER_5G + + mt7996_get_channel_group_5g(chan->hw_value)]; + else if (chan->band == NL80211_BAND_6GHZ) + target_power = eeprom[MT_EE_TX0_POWER_6G + + mt7996_get_channel_group_6g(chan->hw_value)]; + else + target_power = eeprom[MT_EE_TX0_POWER_2G]; + + return target_power; +} + +s8 mt7996_eeprom_get_power_delta(struct mt7996_dev *dev, int band) +{ + u8 *eeprom = dev->mt76.eeprom.data; + u32 val; + s8 delta; + + if (band == NL80211_BAND_5GHZ) + val = eeprom[MT_EE_RATE_DELTA_5G]; + else if (band == NL80211_BAND_6GHZ) + val = eeprom[MT_EE_RATE_DELTA_6G]; + else + val = eeprom[MT_EE_RATE_DELTA_2G]; + + if (!(val & MT_EE_RATE_DELTA_EN)) + return 0; + + delta = FIELD_GET(MT_EE_RATE_DELTA_MASK, val); + + return val & MT_EE_RATE_DELTA_SIGN ? delta : -delta; +} diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/eeprom.h b/drivers/net/wireless/mediatek/mt76/mt7996/eeprom.h new file mode 100644 index 000000000000..8da599e0abea --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7996/eeprom.h @@ -0,0 +1,75 @@ +/* SPDX-License-Identifier: ISC */ +/* + * Copyright (C) 2022 MediaTek Inc. + */ + +#ifndef __MT7996_EEPROM_H +#define __MT7996_EEPROM_H + +#include "mt7996.h" + +enum mt7996_eeprom_field { + MT_EE_CHIP_ID = 0x000, + MT_EE_VERSION = 0x002, + MT_EE_MAC_ADDR = 0x004, + MT_EE_MAC_ADDR2 = 0x00a, + MT_EE_WIFI_CONF = 0x190, + MT_EE_MAC_ADDR3 = 0x2c0, + MT_EE_RATE_DELTA_2G = 0x1400, + MT_EE_RATE_DELTA_5G = 0x147d, + MT_EE_RATE_DELTA_6G = 0x154a, + MT_EE_TX0_POWER_2G = 0x1300, + MT_EE_TX0_POWER_5G = 0x1301, + MT_EE_TX0_POWER_6G = 0x1310, + + __MT_EE_MAX = 0x1dff, +}; + +#define MT_EE_WIFI_CONF0_TX_PATH GENMASK(2, 0) +#define MT_EE_WIFI_CONF0_BAND_SEL GENMASK(2, 0) +#define MT_EE_WIFI_CONF1_BAND_SEL GENMASK(5, 3) +#define MT_EE_WIFI_CONF2_BAND_SEL GENMASK(2, 0) + +#define MT_EE_WIFI_CONF1_TX_PATH_BAND0 GENMASK(5, 3) +#define MT_EE_WIFI_CONF2_TX_PATH_BAND1 GENMASK(5, 3) +#define MT_EE_WIFI_CONF2_TX_PATH_BAND2 GENMASK(2, 0) +#define MT_EE_WIFI_CONF4_STREAM_NUM_BAND0 GENMASK(5, 3) +#define MT_EE_WIFI_CONF5_STREAM_NUM_BAND1 GENMASK(5, 3) +#define MT_EE_WIFI_CONF5_STREAM_NUM_BAND2 GENMASK(2, 0) + +#define MT_EE_RATE_DELTA_MASK GENMASK(5, 0) +#define MT_EE_RATE_DELTA_SIGN BIT(6) +#define MT_EE_RATE_DELTA_EN BIT(7) + +enum mt7996_eeprom_band { + MT_EE_BAND_SEL_DEFAULT, + MT_EE_BAND_SEL_2GHZ, + MT_EE_BAND_SEL_5GHZ, + MT_EE_BAND_SEL_6GHZ, + MT_EE_BAND_SEL_5GHZ_6GHZ, +}; + +static inline int +mt7996_get_channel_group_5g(int channel) +{ + if (channel <= 64) + return 0; + if (channel <= 96) + return 1; + if (channel <= 128) + return 2; + if (channel <= 144) + return 3; + return 4; +} + +static inline int +mt7996_get_channel_group_6g(int channel) +{ + if (channel <= 29) + return 0; + + return DIV_ROUND_UP(channel - 29, 32); +} + +#endif diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/init.c b/drivers/net/wireless/mediatek/mt76/mt7996/init.c new file mode 100644 index 000000000000..cd1657e3585d --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7996/init.c @@ -0,0 +1,816 @@ +// SPDX-License-Identifier: ISC +/* + * Copyright (C) 2022 MediaTek Inc. + */ + +#include +#include +#include "mt7996.h" +#include "mac.h" +#include "mcu.h" +#include "eeprom.h" + +static const struct ieee80211_iface_limit if_limits[] = { + { + .max = 1, + .types = BIT(NL80211_IFTYPE_ADHOC) + }, { + .max = 16, + .types = BIT(NL80211_IFTYPE_AP) +#ifdef CONFIG_MAC80211_MESH + | BIT(NL80211_IFTYPE_MESH_POINT) +#endif + }, { + .max = MT7996_MAX_INTERFACES, + .types = BIT(NL80211_IFTYPE_STATION) + } +}; + +static const struct ieee80211_iface_combination if_comb[] = { + { + .limits = if_limits, + .n_limits = ARRAY_SIZE(if_limits), + .max_interfaces = MT7996_MAX_INTERFACES, + .num_different_channels = 1, + .beacon_int_infra_match = true, + .radar_detect_widths = BIT(NL80211_CHAN_WIDTH_20_NOHT) | + BIT(NL80211_CHAN_WIDTH_20) | + BIT(NL80211_CHAN_WIDTH_40) | + BIT(NL80211_CHAN_WIDTH_80) | + BIT(NL80211_CHAN_WIDTH_160) | + BIT(NL80211_CHAN_WIDTH_80P80), + } +}; + +static void mt7996_led_set_config(struct led_classdev *led_cdev, + u8 delay_on, u8 delay_off) +{ + struct mt7996_dev *dev; + struct mt76_dev *mt76; + u32 val; + + mt76 = container_of(led_cdev, struct mt76_dev, led_cdev); + dev = container_of(mt76, struct mt7996_dev, mt76); + + /* select TX blink mode, 2: only data frames */ + mt76_rmw_field(dev, MT_TMAC_TCR0(0), MT_TMAC_TCR0_TX_BLINK, 2); + + /* enable LED */ + mt76_wr(dev, MT_LED_EN(0), 1); + + /* set LED Tx blink on/off time */ + val = FIELD_PREP(MT_LED_TX_BLINK_ON_MASK, delay_on) | + FIELD_PREP(MT_LED_TX_BLINK_OFF_MASK, delay_off); + mt76_wr(dev, MT_LED_TX_BLINK(0), val); + + /* control LED */ + val = MT_LED_CTRL_BLINK_MODE | MT_LED_CTRL_KICK; + if (dev->mt76.led_al) + val |= MT_LED_CTRL_POLARITY; + + mt76_wr(dev, MT_LED_CTRL(0), val); + mt76_clear(dev, MT_LED_CTRL(0), MT_LED_CTRL_KICK); +} + +static int mt7996_led_set_blink(struct led_classdev *led_cdev, + unsigned long *delay_on, + unsigned long *delay_off) +{ + u16 delta_on = 0, delta_off = 0; + +#define HW_TICK 10 +#define TO_HW_TICK(_t) (((_t) > HW_TICK) ? ((_t) / HW_TICK) : HW_TICK) + + if (*delay_on) + delta_on = TO_HW_TICK(*delay_on); + if (*delay_off) + delta_off = TO_HW_TICK(*delay_off); + + mt7996_led_set_config(led_cdev, delta_on, delta_off); + + return 0; +} + +static void mt7996_led_set_brightness(struct led_classdev *led_cdev, + enum led_brightness brightness) +{ + if (!brightness) + mt7996_led_set_config(led_cdev, 0, 0xff); + else + mt7996_led_set_config(led_cdev, 0xff, 0); +} + +static void +mt7996_init_txpower(struct mt7996_dev *dev, + struct ieee80211_supported_band *sband) +{ + int i, nss = hweight8(dev->mphy.antenna_mask); + int nss_delta = mt76_tx_power_nss_delta(nss); + int pwr_delta = mt7996_eeprom_get_power_delta(dev, sband->band); + struct mt76_power_limits limits; + + for (i = 0; i < sband->n_channels; i++) { + struct ieee80211_channel *chan = &sband->channels[i]; + int target_power = mt7996_eeprom_get_target_power(dev, chan); + + target_power += pwr_delta; + target_power = mt76_get_rate_power_limits(&dev->mphy, chan, + &limits, + target_power); + target_power += nss_delta; + target_power = DIV_ROUND_UP(target_power, 2); + chan->max_power = min_t(int, chan->max_reg_power, + target_power); + chan->orig_mpwr = target_power; + } +} + +static void +mt7996_regd_notifier(struct wiphy *wiphy, + struct regulatory_request *request) +{ + struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy); + struct mt7996_dev *dev = mt7996_hw_dev(hw); + struct mt7996_phy *phy = mt7996_hw_phy(hw); + + memcpy(dev->mt76.alpha2, request->alpha2, sizeof(dev->mt76.alpha2)); + dev->mt76.region = request->dfs_region; + + if (dev->mt76.region == NL80211_DFS_UNSET) + mt7996_mcu_rdd_background_enable(phy, NULL); + + mt7996_init_txpower(dev, &phy->mt76->sband_2g.sband); + mt7996_init_txpower(dev, &phy->mt76->sband_5g.sband); + mt7996_init_txpower(dev, &phy->mt76->sband_6g.sband); + + phy->mt76->dfs_state = MT_DFS_STATE_UNKNOWN; + mt7996_dfs_init_radar_detector(phy); +} + +static void +mt7996_init_wiphy(struct ieee80211_hw *hw) +{ + struct mt7996_phy *phy = mt7996_hw_phy(hw); + struct mt76_dev *mdev = &phy->dev->mt76; + struct wiphy *wiphy = hw->wiphy; + + hw->queues = 4; + hw->max_rx_aggregation_subframes = IEEE80211_MAX_AMPDU_BUF_HE; + hw->max_tx_aggregation_subframes = IEEE80211_MAX_AMPDU_BUF_HE; + hw->netdev_features = NETIF_F_RXCSUM; + + hw->radiotap_timestamp.units_pos = + IEEE80211_RADIOTAP_TIMESTAMP_UNIT_US; + + phy->slottime = 9; + + hw->sta_data_size = sizeof(struct mt7996_sta); + hw->vif_data_size = sizeof(struct mt7996_vif); + + wiphy->iface_combinations = if_comb; + wiphy->n_iface_combinations = ARRAY_SIZE(if_comb); + wiphy->reg_notifier = mt7996_regd_notifier; + wiphy->flags |= WIPHY_FLAG_HAS_CHANNEL_SWITCH; + + wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_BSS_COLOR); + wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_VHT_IBSS); + wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_BEACON_RATE_LEGACY); + wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_BEACON_RATE_HT); + wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_BEACON_RATE_VHT); + wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_BEACON_RATE_HE); + wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_UNSOL_BCAST_PROBE_RESP); + wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_FILS_DISCOVERY); + + if (!mdev->dev->of_node || + !of_property_read_bool(mdev->dev->of_node, + "mediatek,disable-radar-background")) + wiphy_ext_feature_set(wiphy, + NL80211_EXT_FEATURE_RADAR_BACKGROUND); + + ieee80211_hw_set(hw, HAS_RATE_CONTROL); + ieee80211_hw_set(hw, SUPPORTS_TX_ENCAP_OFFLOAD); + ieee80211_hw_set(hw, SUPPORTS_RX_DECAP_OFFLOAD); + ieee80211_hw_set(hw, WANT_MONITOR_VIF); + + hw->max_tx_fragments = 4; + + if (phy->mt76->cap.has_2ghz) + phy->mt76->sband_2g.sband.ht_cap.cap |= + IEEE80211_HT_CAP_LDPC_CODING | + IEEE80211_HT_CAP_MAX_AMSDU; + + if (phy->mt76->cap.has_5ghz) { + phy->mt76->sband_5g.sband.ht_cap.cap |= + IEEE80211_HT_CAP_LDPC_CODING | + IEEE80211_HT_CAP_MAX_AMSDU; + + phy->mt76->sband_5g.sband.vht_cap.cap |= + IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_11454 | + IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK | + IEEE80211_VHT_CAP_SHORT_GI_160 | + IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ; + } + + mt76_set_stream_caps(phy->mt76, true); + mt7996_set_stream_vht_txbf_caps(phy); + mt7996_set_stream_he_caps(phy); + + wiphy->available_antennas_rx = phy->mt76->antenna_mask; + wiphy->available_antennas_tx = phy->mt76->antenna_mask; +} + +static void +mt7996_mac_init_band(struct mt7996_dev *dev, u8 band) +{ + u32 mask, set; + + /* clear estimated value of EIFS for Rx duration & OBSS time */ + mt76_wr(dev, MT_WF_RMAC_RSVD0(band), MT_WF_RMAC_RSVD0_EIFS_CLR); + + /* clear backoff time for Rx duration */ + mt76_clear(dev, MT_WF_RMAC_MIB_AIRTIME1(band), + MT_WF_RMAC_MIB_NONQOSD_BACKOFF); + mt76_clear(dev, MT_WF_RMAC_MIB_AIRTIME3(band), + MT_WF_RMAC_MIB_QOS01_BACKOFF); + mt76_clear(dev, MT_WF_RMAC_MIB_AIRTIME4(band), + MT_WF_RMAC_MIB_QOS23_BACKOFF); + + /* clear backoff time and set software compensation for OBSS time */ + mask = MT_WF_RMAC_MIB_OBSS_BACKOFF | MT_WF_RMAC_MIB_ED_OFFSET; + set = FIELD_PREP(MT_WF_RMAC_MIB_OBSS_BACKOFF, 0) | + FIELD_PREP(MT_WF_RMAC_MIB_ED_OFFSET, 4); + mt76_rmw(dev, MT_WF_RMAC_MIB_AIRTIME0(band), mask, set); +} + +static void mt7996_mac_init(struct mt7996_dev *dev) +{ +#define HIF_TXD_V2_1 4 + int i; + + mt76_clear(dev, MT_MDP_DCR2, MT_MDP_DCR2_RX_TRANS_SHORT); + + for (i = 0; i < MT7996_WTBL_SIZE; i++) + mt7996_mac_wtbl_update(dev, i, + MT_WTBL_UPDATE_ADM_COUNT_CLEAR); + + if (IS_ENABLED(CONFIG_MT76_LEDS)) { + i = dev->mt76.led_pin ? MT_LED_GPIO_MUX3 : MT_LED_GPIO_MUX2; + mt76_rmw_field(dev, i, MT_LED_GPIO_SEL_MASK, 4); + } + + /* txs report queue */ + mt76_rmw_field(dev, MT_DMA_TCRF1(0), MT_DMA_TCRF1_QIDX, 0); + mt76_rmw_field(dev, MT_DMA_TCRF1(1), MT_DMA_TCRF1_QIDX, 6); + mt76_rmw_field(dev, MT_DMA_TCRF1(2), MT_DMA_TCRF1_QIDX, 0); + + /* rro module init */ + mt7996_mcu_set_rro(dev, UNI_RRO_SET_PLATFORM_TYPE, 2); + mt7996_mcu_set_rro(dev, UNI_RRO_SET_BYPASS_MODE, 3); + mt7996_mcu_set_rro(dev, UNI_RRO_SET_TXFREE_PATH, 1); + + mt7996_mcu_wa_cmd(dev, MCU_WA_PARAM_CMD(SET), + MCU_WA_PARAM_HW_PATH_HIF_VER, + HIF_TXD_V2_1, 0); + + for (i = MT_BAND0; i <= MT_BAND2; i++) + mt7996_mac_init_band(dev, i); +} + +static int mt7996_txbf_init(struct mt7996_dev *dev) +{ + int ret; + + if (dev->dbdc_support) { + ret = mt7996_mcu_set_txbf(dev, BF_MOD_EN_CTRL); + if (ret) + return ret; + } + + /* trigger sounding packets */ + ret = mt7996_mcu_set_txbf(dev, BF_SOUNDING_ON); + if (ret) + return ret; + + /* enable eBF */ + return mt7996_mcu_set_txbf(dev, BF_HW_EN_UPDATE); +} + +static int mt7996_register_phy(struct mt7996_dev *dev, struct mt7996_phy *phy, + enum mt76_band_id band) +{ + struct mt76_phy *mphy; + u32 mac_ofs, hif1_ofs = 0; + int ret; + + if (band != MT_BAND1 && band != MT_BAND2) + return 0; + + if ((band == MT_BAND1 && !dev->dbdc_support) || + (band == MT_BAND2 && !dev->tbtc_support)) + return 0; + + if (phy) + return 0; + + if (band == MT_BAND2 && dev->hif2) + hif1_ofs = MT_WFDMA0_PCIE1(0) - MT_WFDMA0(0); + + mphy = mt76_alloc_phy(&dev->mt76, sizeof(*phy), &mt7996_ops, band); + if (!mphy) + return -ENOMEM; + + phy = mphy->priv; + phy->dev = dev; + phy->mt76 = mphy; + mphy->dev->phys[band] = mphy; + + INIT_DELAYED_WORK(&mphy->mac_work, mt7996_mac_work); + + ret = mt7996_eeprom_parse_hw_cap(dev, phy); + if (ret) + goto error; + + mac_ofs = band == MT_BAND2 ? MT_EE_MAC_ADDR3 : MT_EE_MAC_ADDR2; + memcpy(mphy->macaddr, dev->mt76.eeprom.data + mac_ofs, ETH_ALEN); + /* Make the extra PHY MAC address local without overlapping with + * the usual MAC address allocation scheme on multiple virtual interfaces + */ + if (!is_valid_ether_addr(mphy->macaddr)) { + memcpy(mphy->macaddr, dev->mt76.eeprom.data + MT_EE_MAC_ADDR, + ETH_ALEN); + mphy->macaddr[0] |= 2; + mphy->macaddr[0] ^= BIT(7); + if (band == MT_BAND2) + mphy->macaddr[0] ^= BIT(6); + } + mt76_eeprom_override(mphy); + + /* init wiphy according to mphy and phy */ + mt7996_init_wiphy(mphy->hw); + ret = mt76_connac_init_tx_queues(phy->mt76, + MT_TXQ_ID(band), + MT7996_TX_RING_SIZE, + MT_TXQ_RING_BASE(band) + hif1_ofs, 0); + if (ret) + goto error; + + ret = mt76_register_phy(mphy, true, mt76_rates, + ARRAY_SIZE(mt76_rates)); + if (ret) + goto error; + + ret = mt7996_init_debugfs(phy); + if (ret) + goto error; + + return 0; + +error: + mphy->dev->phys[band] = NULL; + ieee80211_free_hw(mphy->hw); + return ret; +} + +static void +mt7996_unregister_phy(struct mt7996_phy *phy, enum mt76_band_id band) +{ + struct mt76_phy *mphy; + + if (!phy) + return; + + mphy = phy->dev->mt76.phys[band]; + mt76_unregister_phy(mphy); + ieee80211_free_hw(mphy->hw); + phy->dev->mt76.phys[band] = NULL; +} + +static void mt7996_init_work(struct work_struct *work) +{ + struct mt7996_dev *dev = container_of(work, struct mt7996_dev, + init_work); + + mt7996_mcu_set_eeprom(dev); + mt7996_mac_init(dev); + mt7996_init_txpower(dev, &dev->mphy.sband_2g.sband); + mt7996_init_txpower(dev, &dev->mphy.sband_5g.sband); + mt7996_init_txpower(dev, &dev->mphy.sband_6g.sband); + mt7996_txbf_init(dev); +} + +void mt7996_wfsys_reset(struct mt7996_dev *dev) +{ + mt76_set(dev, MT_WF_SUBSYS_RST, 0x1); + msleep(20); + + mt76_clear(dev, MT_WF_SUBSYS_RST, 0x1); + msleep(20); +} + +static int mt7996_init_hardware(struct mt7996_dev *dev) +{ + int ret, idx; + + mt76_wr(dev, MT_INT_SOURCE_CSR, ~0); + + INIT_WORK(&dev->init_work, mt7996_init_work); + + dev->dbdc_support = true; + dev->tbtc_support = true; + + ret = mt7996_dma_init(dev); + if (ret) + return ret; + + set_bit(MT76_STATE_INITIALIZED, &dev->mphy.state); + + ret = mt7996_mcu_init(dev); + if (ret) + return ret; + + ret = mt7996_eeprom_init(dev); + if (ret < 0) + return ret; + + /* Beacon and mgmt frames should occupy wcid 0 */ + idx = mt76_wcid_alloc(dev->mt76.wcid_mask, MT7996_WTBL_STA); + if (idx) + return -ENOSPC; + + dev->mt76.global_wcid.idx = idx; + dev->mt76.global_wcid.hw_key_idx = -1; + dev->mt76.global_wcid.tx_info |= MT_WCID_TX_INFO_SET; + rcu_assign_pointer(dev->mt76.wcid[idx], &dev->mt76.global_wcid); + + return 0; +} + +void mt7996_set_stream_vht_txbf_caps(struct mt7996_phy *phy) +{ + int sts; + u32 *cap; + + if (!phy->mt76->cap.has_5ghz) + return; + + sts = hweight16(phy->mt76->chainmask); + cap = &phy->mt76->sband_5g.sband.vht_cap.cap; + + *cap |= IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE | + IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE | + (3 << IEEE80211_VHT_CAP_BEAMFORMEE_STS_SHIFT); + + *cap &= ~(IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_MASK | + IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE | + IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE); + + if (sts < 2) + return; + + *cap |= IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE | + IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE | + FIELD_PREP(IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_MASK, sts - 1); +} + +static void +mt7996_set_stream_he_txbf_caps(struct mt7996_phy *phy, + struct ieee80211_sta_he_cap *he_cap, int vif) +{ + struct ieee80211_he_cap_elem *elem = &he_cap->he_cap_elem; + int sts = hweight16(phy->mt76->chainmask); + u8 c; + +#ifdef CONFIG_MAC80211_MESH + if (vif == NL80211_IFTYPE_MESH_POINT) + return; +#endif + + elem->phy_cap_info[3] &= ~IEEE80211_HE_PHY_CAP3_SU_BEAMFORMER; + elem->phy_cap_info[4] &= ~IEEE80211_HE_PHY_CAP4_MU_BEAMFORMER; + + c = IEEE80211_HE_PHY_CAP5_BEAMFORMEE_NUM_SND_DIM_UNDER_80MHZ_MASK | + IEEE80211_HE_PHY_CAP5_BEAMFORMEE_NUM_SND_DIM_ABOVE_80MHZ_MASK; + elem->phy_cap_info[5] &= ~c; + + c = IEEE80211_HE_PHY_CAP6_TRIG_SU_BEAMFORMING_FB | + IEEE80211_HE_PHY_CAP6_TRIG_MU_BEAMFORMING_PARTIAL_BW_FB; + elem->phy_cap_info[6] &= ~c; + + elem->phy_cap_info[7] &= ~IEEE80211_HE_PHY_CAP7_MAX_NC_MASK; + + c = IEEE80211_HE_PHY_CAP2_NDP_4x_LTF_AND_3_2US | + IEEE80211_HE_PHY_CAP2_UL_MU_FULL_MU_MIMO | + IEEE80211_HE_PHY_CAP2_UL_MU_PARTIAL_MU_MIMO; + elem->phy_cap_info[2] |= c; + + c = IEEE80211_HE_PHY_CAP4_SU_BEAMFORMEE | + IEEE80211_HE_PHY_CAP4_BEAMFORMEE_MAX_STS_UNDER_80MHZ_4 | + IEEE80211_HE_PHY_CAP4_BEAMFORMEE_MAX_STS_ABOVE_80MHZ_4; + elem->phy_cap_info[4] |= c; + + /* do not support NG16 due to spec D4.0 changes subcarrier idx */ + c = IEEE80211_HE_PHY_CAP6_CODEBOOK_SIZE_42_SU | + IEEE80211_HE_PHY_CAP6_CODEBOOK_SIZE_75_MU; + + if (vif == NL80211_IFTYPE_STATION) + c |= IEEE80211_HE_PHY_CAP6_PARTIAL_BANDWIDTH_DL_MUMIMO; + + elem->phy_cap_info[6] |= c; + + if (sts < 2) + return; + + /* the maximum cap is 4 x 3, (Nr, Nc) = (3, 2) */ + elem->phy_cap_info[7] |= min_t(int, sts - 1, 2) << 3; + + if (vif != NL80211_IFTYPE_AP) + return; + + elem->phy_cap_info[3] |= IEEE80211_HE_PHY_CAP3_SU_BEAMFORMER; + elem->phy_cap_info[4] |= IEEE80211_HE_PHY_CAP4_MU_BEAMFORMER; + + c = FIELD_PREP(IEEE80211_HE_PHY_CAP5_BEAMFORMEE_NUM_SND_DIM_UNDER_80MHZ_MASK, + sts - 1) | + FIELD_PREP(IEEE80211_HE_PHY_CAP5_BEAMFORMEE_NUM_SND_DIM_ABOVE_80MHZ_MASK, + sts - 1); + elem->phy_cap_info[5] |= c; + + c = IEEE80211_HE_PHY_CAP6_TRIG_SU_BEAMFORMING_FB | + IEEE80211_HE_PHY_CAP6_TRIG_MU_BEAMFORMING_PARTIAL_BW_FB; + elem->phy_cap_info[6] |= c; + + c = IEEE80211_HE_PHY_CAP7_STBC_TX_ABOVE_80MHZ | + IEEE80211_HE_PHY_CAP7_STBC_RX_ABOVE_80MHZ; + elem->phy_cap_info[7] |= c; +} + +static void +mt7996_gen_ppe_thresh(u8 *he_ppet, int nss) +{ + u8 i, ppet_bits, ppet_size, ru_bit_mask = 0x7; /* HE80 */ + static const u8 ppet16_ppet8_ru3_ru0[] = {0x1c, 0xc7, 0x71}; + + he_ppet[0] = FIELD_PREP(IEEE80211_PPE_THRES_NSS_MASK, nss - 1) | + FIELD_PREP(IEEE80211_PPE_THRES_RU_INDEX_BITMASK_MASK, + ru_bit_mask); + + ppet_bits = IEEE80211_PPE_THRES_INFO_PPET_SIZE * + nss * hweight8(ru_bit_mask) * 2; + ppet_size = DIV_ROUND_UP(ppet_bits, 8); + + for (i = 0; i < ppet_size - 1; i++) + he_ppet[i + 1] = ppet16_ppet8_ru3_ru0[i % 3]; + + he_ppet[i + 1] = ppet16_ppet8_ru3_ru0[i % 3] & + (0xff >> (8 - (ppet_bits - 1) % 8)); +} + +static int +mt7996_init_he_caps(struct mt7996_phy *phy, enum nl80211_band band, + struct ieee80211_sband_iftype_data *data) +{ + int i, idx = 0, nss = hweight8(phy->mt76->antenna_mask); + u16 mcs_map = 0; + + for (i = 0; i < 8; i++) { + if (i < nss) + mcs_map |= (IEEE80211_HE_MCS_SUPPORT_0_11 << (i * 2)); + else + mcs_map |= (IEEE80211_HE_MCS_NOT_SUPPORTED << (i * 2)); + } + + for (i = 0; i < NUM_NL80211_IFTYPES; i++) { + struct ieee80211_sta_he_cap *he_cap = &data[idx].he_cap; + struct ieee80211_he_cap_elem *he_cap_elem = + &he_cap->he_cap_elem; + struct ieee80211_he_mcs_nss_supp *he_mcs = + &he_cap->he_mcs_nss_supp; + + switch (i) { + case NL80211_IFTYPE_STATION: + case NL80211_IFTYPE_AP: +#ifdef CONFIG_MAC80211_MESH + case NL80211_IFTYPE_MESH_POINT: +#endif + break; + default: + continue; + } + + data[idx].types_mask = BIT(i); + he_cap->has_he = true; + + he_cap_elem->mac_cap_info[0] = + IEEE80211_HE_MAC_CAP0_HTC_HE; + he_cap_elem->mac_cap_info[3] = + IEEE80211_HE_MAC_CAP3_OMI_CONTROL | + IEEE80211_HE_MAC_CAP3_MAX_AMPDU_LEN_EXP_EXT_3; + he_cap_elem->mac_cap_info[4] = + IEEE80211_HE_MAC_CAP4_AMSDU_IN_AMPDU; + + if (band == NL80211_BAND_2GHZ) + he_cap_elem->phy_cap_info[0] = + IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_IN_2G; + else + he_cap_elem->phy_cap_info[0] = + IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G | + IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G | + IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_80PLUS80_MHZ_IN_5G; + + he_cap_elem->phy_cap_info[1] = + IEEE80211_HE_PHY_CAP1_LDPC_CODING_IN_PAYLOAD; + he_cap_elem->phy_cap_info[2] = + IEEE80211_HE_PHY_CAP2_STBC_TX_UNDER_80MHZ | + IEEE80211_HE_PHY_CAP2_STBC_RX_UNDER_80MHZ; + + switch (i) { + case NL80211_IFTYPE_AP: + he_cap_elem->mac_cap_info[0] |= + IEEE80211_HE_MAC_CAP0_TWT_RES; + he_cap_elem->mac_cap_info[2] |= + IEEE80211_HE_MAC_CAP2_BSR; + he_cap_elem->mac_cap_info[4] |= + IEEE80211_HE_MAC_CAP4_BQR; + he_cap_elem->mac_cap_info[5] |= + IEEE80211_HE_MAC_CAP5_OM_CTRL_UL_MU_DATA_DIS_RX; + he_cap_elem->phy_cap_info[3] |= + IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_TX_QPSK | + IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_RX_QPSK; + he_cap_elem->phy_cap_info[6] |= + IEEE80211_HE_PHY_CAP6_PARTIAL_BW_EXT_RANGE | + IEEE80211_HE_PHY_CAP6_PPE_THRESHOLD_PRESENT; + he_cap_elem->phy_cap_info[9] |= + IEEE80211_HE_PHY_CAP9_TX_1024_QAM_LESS_THAN_242_TONE_RU | + IEEE80211_HE_PHY_CAP9_RX_1024_QAM_LESS_THAN_242_TONE_RU; + break; + case NL80211_IFTYPE_STATION: + he_cap_elem->mac_cap_info[1] |= + IEEE80211_HE_MAC_CAP1_TF_MAC_PAD_DUR_16US; + + if (band == NL80211_BAND_2GHZ) + he_cap_elem->phy_cap_info[0] |= + IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_RU_MAPPING_IN_2G; + else + he_cap_elem->phy_cap_info[0] |= + IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_RU_MAPPING_IN_5G; + + he_cap_elem->phy_cap_info[1] |= + IEEE80211_HE_PHY_CAP1_DEVICE_CLASS_A | + IEEE80211_HE_PHY_CAP1_HE_LTF_AND_GI_FOR_HE_PPDUS_0_8US; + he_cap_elem->phy_cap_info[3] |= + IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_TX_QPSK | + IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_RX_QPSK; + he_cap_elem->phy_cap_info[6] |= + IEEE80211_HE_PHY_CAP6_TRIG_CQI_FB | + IEEE80211_HE_PHY_CAP6_PARTIAL_BW_EXT_RANGE | + IEEE80211_HE_PHY_CAP6_PPE_THRESHOLD_PRESENT; + he_cap_elem->phy_cap_info[7] |= + IEEE80211_HE_PHY_CAP7_POWER_BOOST_FACTOR_SUPP | + IEEE80211_HE_PHY_CAP7_HE_SU_MU_PPDU_4XLTF_AND_08_US_GI; + he_cap_elem->phy_cap_info[8] |= + IEEE80211_HE_PHY_CAP8_20MHZ_IN_40MHZ_HE_PPDU_IN_2G | + IEEE80211_HE_PHY_CAP8_20MHZ_IN_160MHZ_HE_PPDU | + IEEE80211_HE_PHY_CAP8_80MHZ_IN_160MHZ_HE_PPDU | + IEEE80211_HE_PHY_CAP8_DCM_MAX_RU_484; + he_cap_elem->phy_cap_info[9] |= + IEEE80211_HE_PHY_CAP9_LONGER_THAN_16_SIGB_OFDM_SYM | + IEEE80211_HE_PHY_CAP9_NON_TRIGGERED_CQI_FEEDBACK | + IEEE80211_HE_PHY_CAP9_TX_1024_QAM_LESS_THAN_242_TONE_RU | + IEEE80211_HE_PHY_CAP9_RX_1024_QAM_LESS_THAN_242_TONE_RU | + IEEE80211_HE_PHY_CAP9_RX_FULL_BW_SU_USING_MU_WITH_COMP_SIGB | + IEEE80211_HE_PHY_CAP9_RX_FULL_BW_SU_USING_MU_WITH_NON_COMP_SIGB; + break; + } + + he_mcs->rx_mcs_80 = cpu_to_le16(mcs_map); + he_mcs->tx_mcs_80 = cpu_to_le16(mcs_map); + he_mcs->rx_mcs_160 = cpu_to_le16(mcs_map); + he_mcs->tx_mcs_160 = cpu_to_le16(mcs_map); + he_mcs->rx_mcs_80p80 = cpu_to_le16(mcs_map); + he_mcs->tx_mcs_80p80 = cpu_to_le16(mcs_map); + + mt7996_set_stream_he_txbf_caps(phy, he_cap, i); + + memset(he_cap->ppe_thres, 0, sizeof(he_cap->ppe_thres)); + if (he_cap_elem->phy_cap_info[6] & + IEEE80211_HE_PHY_CAP6_PPE_THRESHOLD_PRESENT) { + mt7996_gen_ppe_thresh(he_cap->ppe_thres, nss); + } else { + he_cap_elem->phy_cap_info[9] |= + IEEE80211_HE_PHY_CAP9_NOMINAL_PKT_PADDING_16US; + } + + if (band == NL80211_BAND_6GHZ) { + u16 cap = IEEE80211_HE_6GHZ_CAP_TX_ANTPAT_CONS | + IEEE80211_HE_6GHZ_CAP_RX_ANTPAT_CONS; + + cap |= u16_encode_bits(IEEE80211_HT_MPDU_DENSITY_2, + IEEE80211_HE_6GHZ_CAP_MIN_MPDU_START) | + u16_encode_bits(IEEE80211_VHT_MAX_AMPDU_1024K, + IEEE80211_HE_6GHZ_CAP_MAX_AMPDU_LEN_EXP) | + u16_encode_bits(IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_11454, + IEEE80211_HE_6GHZ_CAP_MAX_MPDU_LEN); + + data[idx].he_6ghz_capa.capa = cpu_to_le16(cap); + } + + idx++; + } + + return idx; +} + +void mt7996_set_stream_he_caps(struct mt7996_phy *phy) +{ + struct ieee80211_sband_iftype_data *data; + struct ieee80211_supported_band *band; + int n; + + if (phy->mt76->cap.has_2ghz) { + data = phy->iftype[NL80211_BAND_2GHZ]; + n = mt7996_init_he_caps(phy, NL80211_BAND_2GHZ, data); + + band = &phy->mt76->sband_2g.sband; + band->iftype_data = data; + band->n_iftype_data = n; + } + + if (phy->mt76->cap.has_5ghz) { + data = phy->iftype[NL80211_BAND_5GHZ]; + n = mt7996_init_he_caps(phy, NL80211_BAND_5GHZ, data); + + band = &phy->mt76->sband_5g.sband; + band->iftype_data = data; + band->n_iftype_data = n; + } + + if (phy->mt76->cap.has_6ghz) { + data = phy->iftype[NL80211_BAND_6GHZ]; + n = mt7996_init_he_caps(phy, NL80211_BAND_6GHZ, data); + + band = &phy->mt76->sband_6g.sband; + band->iftype_data = data; + band->n_iftype_data = n; + } +} + +int mt7996_register_device(struct mt7996_dev *dev) +{ + struct ieee80211_hw *hw = mt76_hw(dev); + int ret; + + dev->phy.dev = dev; + dev->phy.mt76 = &dev->mt76.phy; + dev->mt76.phy.priv = &dev->phy; + INIT_WORK(&dev->rc_work, mt7996_mac_sta_rc_work); + INIT_DELAYED_WORK(&dev->mphy.mac_work, mt7996_mac_work); + INIT_LIST_HEAD(&dev->sta_rc_list); + INIT_LIST_HEAD(&dev->sta_poll_list); + INIT_LIST_HEAD(&dev->twt_list); + spin_lock_init(&dev->sta_poll_lock); + + init_waitqueue_head(&dev->reset_wait); + INIT_WORK(&dev->reset_work, mt7996_mac_reset_work); + + ret = mt7996_init_hardware(dev); + if (ret) + return ret; + + mt7996_init_wiphy(hw); + + /* init led callbacks */ + if (IS_ENABLED(CONFIG_MT76_LEDS)) { + dev->mt76.led_cdev.brightness_set = mt7996_led_set_brightness; + dev->mt76.led_cdev.blink_set = mt7996_led_set_blink; + } + + ret = mt76_register_device(&dev->mt76, true, mt76_rates, + ARRAY_SIZE(mt76_rates)); + if (ret) + return ret; + + ieee80211_queue_work(mt76_hw(dev), &dev->init_work); + + ret = mt7996_register_phy(dev, mt7996_phy2(dev), MT_BAND1); + if (ret) + return ret; + + ret = mt7996_register_phy(dev, mt7996_phy3(dev), MT_BAND2); + if (ret) + return ret; + + return mt7996_init_debugfs(&dev->phy); +} + +void mt7996_unregister_device(struct mt7996_dev *dev) +{ + mt7996_unregister_phy(mt7996_phy3(dev), MT_BAND2); + mt7996_unregister_phy(mt7996_phy2(dev), MT_BAND1); + mt76_unregister_device(&dev->mt76); + mt7996_mcu_exit(dev); + mt7996_tx_token_put(dev); + mt7996_dma_cleanup(dev); + tasklet_disable(&dev->irq_tasklet); + + mt76_free_device(&dev->mt76); +} diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mac.c b/drivers/net/wireless/mediatek/mt76/mt7996/mac.c new file mode 100644 index 000000000000..1c35f0f15452 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mac.c @@ -0,0 +1,2468 @@ +// SPDX-License-Identifier: ISC +/* + * Copyright (C) 2022 MediaTek Inc. + */ + +#include +#include +#include "mt7996.h" +#include "../dma.h" +#include "mac.h" +#include "mcu.h" + +#define to_rssi(field, rxv) ((FIELD_GET(field, rxv) - 220) / 2) + +#define HE_BITS(f) cpu_to_le16(IEEE80211_RADIOTAP_HE_##f) +#define HE_PREP(f, m, v) le16_encode_bits(le32_get_bits(v, MT_CRXV_HE_##m),\ + IEEE80211_RADIOTAP_HE_##f) + +static const struct mt7996_dfs_radar_spec etsi_radar_specs = { + .pulse_th = { 110, -10, -80, 40, 5200, 128, 5200 }, + .radar_pattern = { + [5] = { 1, 0, 6, 32, 28, 0, 990, 5010, 17, 1, 1 }, + [6] = { 1, 0, 9, 32, 28, 0, 615, 5010, 27, 1, 1 }, + [7] = { 1, 0, 15, 32, 28, 0, 240, 445, 27, 1, 1 }, + [8] = { 1, 0, 12, 32, 28, 0, 240, 510, 42, 1, 1 }, + [9] = { 1, 1, 0, 0, 0, 0, 2490, 3343, 14, 0, 0, 12, 32, 28, { }, 126 }, + [10] = { 1, 1, 0, 0, 0, 0, 2490, 3343, 14, 0, 0, 15, 32, 24, { }, 126 }, + [11] = { 1, 1, 0, 0, 0, 0, 823, 2510, 14, 0, 0, 18, 32, 28, { }, 54 }, + [12] = { 1, 1, 0, 0, 0, 0, 823, 2510, 14, 0, 0, 27, 32, 24, { }, 54 }, + }, +}; + +static const struct mt7996_dfs_radar_spec fcc_radar_specs = { + .pulse_th = { 110, -10, -80, 40, 5200, 128, 5200 }, + .radar_pattern = { + [0] = { 1, 0, 8, 32, 28, 0, 508, 3076, 13, 1, 1 }, + [1] = { 1, 0, 12, 32, 28, 0, 140, 240, 17, 1, 1 }, + [2] = { 1, 0, 8, 32, 28, 0, 190, 510, 22, 1, 1 }, + [3] = { 1, 0, 6, 32, 28, 0, 190, 510, 32, 1, 1 }, + [4] = { 1, 0, 9, 255, 28, 0, 323, 343, 13, 1, 32 }, + }, +}; + +static const struct mt7996_dfs_radar_spec jp_radar_specs = { + .pulse_th = { 110, -10, -80, 40, 5200, 128, 5200 }, + .radar_pattern = { + [0] = { 1, 0, 8, 32, 28, 0, 508, 3076, 13, 1, 1 }, + [1] = { 1, 0, 12, 32, 28, 0, 140, 240, 17, 1, 1 }, + [2] = { 1, 0, 8, 32, 28, 0, 190, 510, 22, 1, 1 }, + [3] = { 1, 0, 6, 32, 28, 0, 190, 510, 32, 1, 1 }, + [4] = { 1, 0, 9, 255, 28, 0, 323, 343, 13, 1, 32 }, + [13] = { 1, 0, 7, 32, 28, 0, 3836, 3856, 14, 1, 1 }, + [14] = { 1, 0, 6, 32, 28, 0, 615, 5010, 110, 1, 1 }, + [15] = { 1, 1, 0, 0, 0, 0, 15, 5010, 110, 0, 0, 12, 32, 28 }, + }, +}; + +static struct mt76_wcid *mt7996_rx_get_wcid(struct mt7996_dev *dev, + u16 idx, bool unicast) +{ + struct mt7996_sta *sta; + struct mt76_wcid *wcid; + + if (idx >= ARRAY_SIZE(dev->mt76.wcid)) + return NULL; + + wcid = rcu_dereference(dev->mt76.wcid[idx]); + if (unicast || !wcid) + return wcid; + + if (!wcid->sta) + return NULL; + + sta = container_of(wcid, struct mt7996_sta, wcid); + if (!sta->vif) + return NULL; + + return &sta->vif->sta.wcid; +} + +void mt7996_sta_ps(struct mt76_dev *mdev, struct ieee80211_sta *sta, bool ps) +{ +} + +bool mt7996_mac_wtbl_update(struct mt7996_dev *dev, int idx, u32 mask) +{ + mt76_rmw(dev, MT_WTBL_UPDATE, MT_WTBL_UPDATE_WLAN_IDX, + FIELD_PREP(MT_WTBL_UPDATE_WLAN_IDX, idx) | mask); + + return mt76_poll(dev, MT_WTBL_UPDATE, MT_WTBL_UPDATE_BUSY, + 0, 5000); +} + +u32 mt7996_mac_wtbl_lmac_addr(struct mt7996_dev *dev, u16 wcid, u8 dw) +{ + mt76_wr(dev, MT_WTBLON_TOP_WDUCR, + FIELD_PREP(MT_WTBLON_TOP_WDUCR_GROUP, (wcid >> 7))); + + return MT_WTBL_LMAC_OFFS(wcid, dw); +} + +static void mt7996_mac_sta_poll(struct mt7996_dev *dev) +{ + static const u8 ac_to_tid[] = { + [IEEE80211_AC_BE] = 0, + [IEEE80211_AC_BK] = 1, + [IEEE80211_AC_VI] = 4, + [IEEE80211_AC_VO] = 6 + }; + struct ieee80211_sta *sta; + struct mt7996_sta *msta; + struct rate_info *rate; + u32 tx_time[IEEE80211_NUM_ACS], rx_time[IEEE80211_NUM_ACS]; + LIST_HEAD(sta_poll_list); + int i; + + spin_lock_bh(&dev->sta_poll_lock); + list_splice_init(&dev->sta_poll_list, &sta_poll_list); + spin_unlock_bh(&dev->sta_poll_lock); + + rcu_read_lock(); + + while (true) { + bool clear = false; + u32 addr, val; + u16 idx; + u8 bw; + + spin_lock_bh(&dev->sta_poll_lock); + if (list_empty(&sta_poll_list)) { + spin_unlock_bh(&dev->sta_poll_lock); + break; + } + msta = list_first_entry(&sta_poll_list, + struct mt7996_sta, poll_list); + list_del_init(&msta->poll_list); + spin_unlock_bh(&dev->sta_poll_lock); + + idx = msta->wcid.idx; + addr = mt7996_mac_wtbl_lmac_addr(dev, idx, 20); + + for (i = 0; i < IEEE80211_NUM_ACS; i++) { + u32 tx_last = msta->airtime_ac[i]; + u32 rx_last = msta->airtime_ac[i + 4]; + + msta->airtime_ac[i] = mt76_rr(dev, addr); + msta->airtime_ac[i + 4] = mt76_rr(dev, addr + 4); + + tx_time[i] = msta->airtime_ac[i] - tx_last; + rx_time[i] = msta->airtime_ac[i + 4] - rx_last; + + if ((tx_last | rx_last) & BIT(30)) + clear = true; + + addr += 8; + } + + if (clear) { + mt7996_mac_wtbl_update(dev, idx, + MT_WTBL_UPDATE_ADM_COUNT_CLEAR); + memset(msta->airtime_ac, 0, sizeof(msta->airtime_ac)); + } + + if (!msta->wcid.sta) + continue; + + sta = container_of((void *)msta, struct ieee80211_sta, + drv_priv); + for (i = 0; i < IEEE80211_NUM_ACS; i++) { + u8 q = mt76_connac_lmac_mapping(i); + u32 tx_cur = tx_time[q]; + u32 rx_cur = rx_time[q]; + u8 tid = ac_to_tid[i]; + + if (!tx_cur && !rx_cur) + continue; + + ieee80211_sta_register_airtime(sta, tid, tx_cur, rx_cur); + } + + /* We don't support reading GI info from txs packets. + * For accurate tx status reporting and AQL improvement, + * we need to make sure that flags match so polling GI + * from per-sta counters directly. + */ + rate = &msta->wcid.rate; + + switch (rate->bw) { + case RATE_INFO_BW_160: + bw = IEEE80211_STA_RX_BW_160; + break; + case RATE_INFO_BW_80: + bw = IEEE80211_STA_RX_BW_80; + break; + case RATE_INFO_BW_40: + bw = IEEE80211_STA_RX_BW_40; + break; + default: + bw = IEEE80211_STA_RX_BW_20; + break; + } + + addr = mt7996_mac_wtbl_lmac_addr(dev, idx, 6); + val = mt76_rr(dev, addr); + if (rate->flags & RATE_INFO_FLAGS_HE_MCS) { + u8 offs = 24 + 2 * bw; + + rate->he_gi = (val & (0x3 << offs)) >> offs; + } else if (rate->flags & + (RATE_INFO_FLAGS_VHT_MCS | RATE_INFO_FLAGS_MCS)) { + if (val & BIT(12 + bw)) + rate->flags |= RATE_INFO_FLAGS_SHORT_GI; + else + rate->flags &= ~RATE_INFO_FLAGS_SHORT_GI; + } + } + + rcu_read_unlock(); +} + +static void +mt7996_mac_decode_he_radiotap_ru(struct mt76_rx_status *status, + struct ieee80211_radiotap_he *he, + __le32 *rxv) +{ + u32 ru_h, ru_l; + u8 ru, offs = 0; + + ru_l = le32_get_bits(rxv[0], MT_PRXV_HE_RU_ALLOC_L); + ru_h = le32_get_bits(rxv[1], MT_PRXV_HE_RU_ALLOC_H); + ru = (u8)(ru_l | ru_h << 4); + + status->bw = RATE_INFO_BW_HE_RU; + + switch (ru) { + case 0 ... 36: + status->he_ru = NL80211_RATE_INFO_HE_RU_ALLOC_26; + offs = ru; + break; + case 37 ... 52: + status->he_ru = NL80211_RATE_INFO_HE_RU_ALLOC_52; + offs = ru - 37; + break; + case 53 ... 60: + status->he_ru = NL80211_RATE_INFO_HE_RU_ALLOC_106; + offs = ru - 53; + break; + case 61 ... 64: + status->he_ru = NL80211_RATE_INFO_HE_RU_ALLOC_242; + offs = ru - 61; + break; + case 65 ... 66: + status->he_ru = NL80211_RATE_INFO_HE_RU_ALLOC_484; + offs = ru - 65; + break; + case 67: + status->he_ru = NL80211_RATE_INFO_HE_RU_ALLOC_996; + break; + case 68: + status->he_ru = NL80211_RATE_INFO_HE_RU_ALLOC_2x996; + break; + } + + he->data1 |= HE_BITS(DATA1_BW_RU_ALLOC_KNOWN); + he->data2 |= HE_BITS(DATA2_RU_OFFSET_KNOWN) | + le16_encode_bits(offs, + IEEE80211_RADIOTAP_HE_DATA2_RU_OFFSET); +} + +static void +mt7996_mac_decode_he_mu_radiotap(struct sk_buff *skb, __le32 *rxv) +{ + struct mt76_rx_status *status = (struct mt76_rx_status *)skb->cb; + static const struct ieee80211_radiotap_he_mu mu_known = { + .flags1 = HE_BITS(MU_FLAGS1_SIG_B_MCS_KNOWN) | + HE_BITS(MU_FLAGS1_SIG_B_DCM_KNOWN) | + HE_BITS(MU_FLAGS1_CH1_RU_KNOWN) | + HE_BITS(MU_FLAGS1_SIG_B_SYMS_USERS_KNOWN), + .flags2 = HE_BITS(MU_FLAGS2_BW_FROM_SIG_A_BW_KNOWN), + }; + struct ieee80211_radiotap_he_mu *he_mu = NULL; + + status->flag |= RX_FLAG_RADIOTAP_HE_MU; + + he_mu = skb_push(skb, sizeof(mu_known)); + memcpy(he_mu, &mu_known, sizeof(mu_known)); + +#define MU_PREP(f, v) le16_encode_bits(v, IEEE80211_RADIOTAP_HE_MU_##f) + + he_mu->flags1 |= MU_PREP(FLAGS1_SIG_B_MCS, status->rate_idx); + if (status->he_dcm) + he_mu->flags1 |= MU_PREP(FLAGS1_SIG_B_DCM, status->he_dcm); + + he_mu->flags2 |= MU_PREP(FLAGS2_BW_FROM_SIG_A_BW, status->bw) | + MU_PREP(FLAGS2_SIG_B_SYMS_USERS, + le32_get_bits(rxv[2], MT_CRXV_HE_NUM_USER)); + + he_mu->ru_ch1[0] = le32_get_bits(rxv[3], MT_CRXV_HE_RU0); + + if (status->bw >= RATE_INFO_BW_40) { + he_mu->flags1 |= HE_BITS(MU_FLAGS1_CH2_RU_KNOWN); + he_mu->ru_ch2[0] = le32_get_bits(rxv[3], MT_CRXV_HE_RU1); + } + + if (status->bw >= RATE_INFO_BW_80) { + he_mu->ru_ch1[1] = le32_get_bits(rxv[3], MT_CRXV_HE_RU2); + he_mu->ru_ch2[1] = le32_get_bits(rxv[3], MT_CRXV_HE_RU3); + } +} + +static void +mt7996_mac_decode_he_radiotap(struct sk_buff *skb, __le32 *rxv, u8 mode) +{ + struct mt76_rx_status *status = (struct mt76_rx_status *)skb->cb; + static const struct ieee80211_radiotap_he known = { + .data1 = HE_BITS(DATA1_DATA_MCS_KNOWN) | + HE_BITS(DATA1_DATA_DCM_KNOWN) | + HE_BITS(DATA1_STBC_KNOWN) | + HE_BITS(DATA1_CODING_KNOWN) | + HE_BITS(DATA1_LDPC_XSYMSEG_KNOWN) | + HE_BITS(DATA1_DOPPLER_KNOWN) | + HE_BITS(DATA1_SPTL_REUSE_KNOWN) | + HE_BITS(DATA1_BSS_COLOR_KNOWN), + .data2 = HE_BITS(DATA2_GI_KNOWN) | + HE_BITS(DATA2_TXBF_KNOWN) | + HE_BITS(DATA2_PE_DISAMBIG_KNOWN) | + HE_BITS(DATA2_TXOP_KNOWN), + }; + struct ieee80211_radiotap_he *he = NULL; + u32 ltf_size = le32_get_bits(rxv[2], MT_CRXV_HE_LTF_SIZE) + 1; + + status->flag |= RX_FLAG_RADIOTAP_HE; + + he = skb_push(skb, sizeof(known)); + memcpy(he, &known, sizeof(known)); + + he->data3 = HE_PREP(DATA3_BSS_COLOR, BSS_COLOR, rxv[14]) | + HE_PREP(DATA3_LDPC_XSYMSEG, LDPC_EXT_SYM, rxv[2]); + he->data4 = HE_PREP(DATA4_SU_MU_SPTL_REUSE, SR_MASK, rxv[11]); + he->data5 = HE_PREP(DATA5_PE_DISAMBIG, PE_DISAMBIG, rxv[2]) | + le16_encode_bits(ltf_size, + IEEE80211_RADIOTAP_HE_DATA5_LTF_SIZE); + if (le32_to_cpu(rxv[0]) & MT_PRXV_TXBF) + he->data5 |= HE_BITS(DATA5_TXBF); + he->data6 = HE_PREP(DATA6_TXOP, TXOP_DUR, rxv[14]) | + HE_PREP(DATA6_DOPPLER, DOPPLER, rxv[14]); + + switch (mode) { + case MT_PHY_TYPE_HE_SU: + he->data1 |= HE_BITS(DATA1_FORMAT_SU) | + HE_BITS(DATA1_UL_DL_KNOWN) | + HE_BITS(DATA1_BEAM_CHANGE_KNOWN) | + HE_BITS(DATA1_BW_RU_ALLOC_KNOWN); + + he->data3 |= HE_PREP(DATA3_BEAM_CHANGE, BEAM_CHNG, rxv[14]) | + HE_PREP(DATA3_UL_DL, UPLINK, rxv[2]); + break; + case MT_PHY_TYPE_HE_EXT_SU: + he->data1 |= HE_BITS(DATA1_FORMAT_EXT_SU) | + HE_BITS(DATA1_UL_DL_KNOWN) | + HE_BITS(DATA1_BW_RU_ALLOC_KNOWN); + + he->data3 |= HE_PREP(DATA3_UL_DL, UPLINK, rxv[2]); + break; + case MT_PHY_TYPE_HE_MU: + he->data1 |= HE_BITS(DATA1_FORMAT_MU) | + HE_BITS(DATA1_UL_DL_KNOWN); + + he->data3 |= HE_PREP(DATA3_UL_DL, UPLINK, rxv[2]); + he->data4 |= HE_PREP(DATA4_MU_STA_ID, MU_AID, rxv[7]); + + mt7996_mac_decode_he_radiotap_ru(status, he, rxv); + mt7996_mac_decode_he_mu_radiotap(skb, rxv); + break; + case MT_PHY_TYPE_HE_TB: + he->data1 |= HE_BITS(DATA1_FORMAT_TRIG) | + HE_BITS(DATA1_SPTL_REUSE2_KNOWN) | + HE_BITS(DATA1_SPTL_REUSE3_KNOWN) | + HE_BITS(DATA1_SPTL_REUSE4_KNOWN); + + he->data4 |= HE_PREP(DATA4_TB_SPTL_REUSE1, SR_MASK, rxv[11]) | + HE_PREP(DATA4_TB_SPTL_REUSE2, SR1_MASK, rxv[11]) | + HE_PREP(DATA4_TB_SPTL_REUSE3, SR2_MASK, rxv[11]) | + HE_PREP(DATA4_TB_SPTL_REUSE4, SR3_MASK, rxv[11]); + + mt7996_mac_decode_he_radiotap_ru(status, he, rxv); + break; + default: + break; + } +} + +/* The HW does not translate the mac header to 802.3 for mesh point */ +static int mt7996_reverse_frag0_hdr_trans(struct sk_buff *skb, u16 hdr_gap) +{ + struct mt76_rx_status *status = (struct mt76_rx_status *)skb->cb; + struct ethhdr *eth_hdr = (struct ethhdr *)(skb->data + hdr_gap); + struct mt7996_sta *msta = (struct mt7996_sta *)status->wcid; + __le32 *rxd = (__le32 *)skb->data; + struct ieee80211_sta *sta; + struct ieee80211_vif *vif; + struct ieee80211_hdr hdr; + u16 frame_control; + + if (le32_get_bits(rxd[3], MT_RXD3_NORMAL_ADDR_TYPE) != + MT_RXD3_NORMAL_U2M) + return -EINVAL; + + if (!(le32_to_cpu(rxd[1]) & MT_RXD1_NORMAL_GROUP_4)) + return -EINVAL; + + if (!msta || !msta->vif) + return -EINVAL; + + sta = container_of((void *)msta, struct ieee80211_sta, drv_priv); + vif = container_of((void *)msta->vif, struct ieee80211_vif, drv_priv); + + /* store the info from RXD and ethhdr to avoid being overridden */ + frame_control = le32_get_bits(rxd[8], MT_RXD8_FRAME_CONTROL); + hdr.frame_control = cpu_to_le16(frame_control); + hdr.seq_ctrl = cpu_to_le16(le32_get_bits(rxd[10], MT_RXD10_SEQ_CTRL)); + hdr.duration_id = 0; + + ether_addr_copy(hdr.addr1, vif->addr); + ether_addr_copy(hdr.addr2, sta->addr); + switch (frame_control & (IEEE80211_FCTL_TODS | + IEEE80211_FCTL_FROMDS)) { + case 0: + ether_addr_copy(hdr.addr3, vif->bss_conf.bssid); + break; + case IEEE80211_FCTL_FROMDS: + ether_addr_copy(hdr.addr3, eth_hdr->h_source); + break; + case IEEE80211_FCTL_TODS: + ether_addr_copy(hdr.addr3, eth_hdr->h_dest); + break; + case IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS: + ether_addr_copy(hdr.addr3, eth_hdr->h_dest); + ether_addr_copy(hdr.addr4, eth_hdr->h_source); + break; + default: + break; + } + + skb_pull(skb, hdr_gap + sizeof(struct ethhdr) - 2); + if (eth_hdr->h_proto == cpu_to_be16(ETH_P_AARP) || + eth_hdr->h_proto == cpu_to_be16(ETH_P_IPX)) + ether_addr_copy(skb_push(skb, ETH_ALEN), bridge_tunnel_header); + else if (be16_to_cpu(eth_hdr->h_proto) >= ETH_P_802_3_MIN) + ether_addr_copy(skb_push(skb, ETH_ALEN), rfc1042_header); + else + skb_pull(skb, 2); + + if (ieee80211_has_order(hdr.frame_control)) + memcpy(skb_push(skb, IEEE80211_HT_CTL_LEN), &rxd[11], + IEEE80211_HT_CTL_LEN); + if (ieee80211_is_data_qos(hdr.frame_control)) { + __le16 qos_ctrl; + + qos_ctrl = cpu_to_le16(le32_get_bits(rxd[10], MT_RXD10_QOS_CTL)); + memcpy(skb_push(skb, IEEE80211_QOS_CTL_LEN), &qos_ctrl, + IEEE80211_QOS_CTL_LEN); + } + + if (ieee80211_has_a4(hdr.frame_control)) + memcpy(skb_push(skb, sizeof(hdr)), &hdr, sizeof(hdr)); + else + memcpy(skb_push(skb, sizeof(hdr) - 6), &hdr, sizeof(hdr) - 6); + + return 0; +} + +static int +mt7996_mac_fill_rx_rate(struct mt7996_dev *dev, + struct mt76_rx_status *status, + struct ieee80211_supported_band *sband, + __le32 *rxv, u8 *mode) +{ + u32 v0, v2; + u8 stbc, gi, bw, dcm, nss; + int i, idx; + bool cck = false; + + v0 = le32_to_cpu(rxv[0]); + v2 = le32_to_cpu(rxv[2]); + + idx = FIELD_GET(MT_PRXV_TX_RATE, v0); + i = idx; + nss = FIELD_GET(MT_PRXV_NSTS, v0) + 1; + + stbc = FIELD_GET(MT_PRXV_HT_STBC, v2); + gi = FIELD_GET(MT_PRXV_HT_SHORT_GI, v2); + *mode = FIELD_GET(MT_PRXV_TX_MODE, v2); + dcm = FIELD_GET(MT_PRXV_DCM, v2); + bw = FIELD_GET(MT_PRXV_FRAME_MODE, v2); + + switch (*mode) { + case MT_PHY_TYPE_CCK: + cck = true; + fallthrough; + case MT_PHY_TYPE_OFDM: + i = mt76_get_rate(&dev->mt76, sband, i, cck); + break; + case MT_PHY_TYPE_HT_GF: + case MT_PHY_TYPE_HT: + status->encoding = RX_ENC_HT; + if (gi) + status->enc_flags |= RX_ENC_FLAG_SHORT_GI; + if (i > 31) + return -EINVAL; + break; + case MT_PHY_TYPE_VHT: + status->nss = nss; + status->encoding = RX_ENC_VHT; + if (gi) + status->enc_flags |= RX_ENC_FLAG_SHORT_GI; + if (i > 11) + return -EINVAL; + break; + case MT_PHY_TYPE_HE_MU: + case MT_PHY_TYPE_HE_SU: + case MT_PHY_TYPE_HE_EXT_SU: + case MT_PHY_TYPE_HE_TB: + status->nss = nss; + status->encoding = RX_ENC_HE; + i &= GENMASK(3, 0); + + if (gi <= NL80211_RATE_INFO_HE_GI_3_2) + status->he_gi = gi; + + status->he_dcm = dcm; + break; + default: + return -EINVAL; + } + status->rate_idx = i; + + switch (bw) { + case IEEE80211_STA_RX_BW_20: + break; + case IEEE80211_STA_RX_BW_40: + if (*mode & MT_PHY_TYPE_HE_EXT_SU && + (idx & MT_PRXV_TX_ER_SU_106T)) { + status->bw = RATE_INFO_BW_HE_RU; + status->he_ru = + NL80211_RATE_INFO_HE_RU_ALLOC_106; + } else { + status->bw = RATE_INFO_BW_40; + } + break; + case IEEE80211_STA_RX_BW_80: + status->bw = RATE_INFO_BW_80; + break; + case IEEE80211_STA_RX_BW_160: + status->bw = RATE_INFO_BW_160; + break; + default: + return -EINVAL; + } + + status->enc_flags |= RX_ENC_FLAG_STBC_MASK * stbc; + if (*mode < MT_PHY_TYPE_HE_SU && gi) + status->enc_flags |= RX_ENC_FLAG_SHORT_GI; + + return 0; +} + +static int +mt7996_mac_fill_rx(struct mt7996_dev *dev, struct sk_buff *skb) +{ + struct mt76_rx_status *status = (struct mt76_rx_status *)skb->cb; + struct mt76_phy *mphy = &dev->mt76.phy; + struct mt7996_phy *phy = &dev->phy; + struct ieee80211_supported_band *sband; + __le32 *rxd = (__le32 *)skb->data; + __le32 *rxv = NULL; + u32 rxd0 = le32_to_cpu(rxd[0]); + u32 rxd1 = le32_to_cpu(rxd[1]); + u32 rxd2 = le32_to_cpu(rxd[2]); + u32 rxd3 = le32_to_cpu(rxd[3]); + u32 rxd4 = le32_to_cpu(rxd[4]); + u32 csum_mask = MT_RXD0_NORMAL_IP_SUM | MT_RXD0_NORMAL_UDP_TCP_SUM; + u32 csum_status = *(u32 *)skb->cb; + bool unicast, insert_ccmp_hdr = false; + u8 remove_pad, amsdu_info, band_idx; + u8 mode = 0, qos_ctl = 0; + bool hdr_trans; + u16 hdr_gap; + u16 seq_ctrl = 0; + __le16 fc = 0; + int idx; + + memset(status, 0, sizeof(*status)); + + band_idx = FIELD_GET(MT_RXD1_NORMAL_BAND_IDX, rxd1); + mphy = dev->mt76.phys[band_idx]; + phy = mphy->priv; + status->phy_idx = mphy->band_idx; + + if (!test_bit(MT76_STATE_RUNNING, &mphy->state)) + return -EINVAL; + + if (rxd2 & MT_RXD2_NORMAL_AMSDU_ERR) + return -EINVAL; + + hdr_trans = rxd2 & MT_RXD2_NORMAL_HDR_TRANS; + if (hdr_trans && (rxd1 & MT_RXD1_NORMAL_CM)) + return -EINVAL; + + /* ICV error or CCMP/BIP/WPI MIC error */ + if (rxd1 & MT_RXD1_NORMAL_ICV_ERR) + status->flag |= RX_FLAG_ONLY_MONITOR; + + unicast = FIELD_GET(MT_RXD3_NORMAL_ADDR_TYPE, rxd3) == MT_RXD3_NORMAL_U2M; + idx = FIELD_GET(MT_RXD1_NORMAL_WLAN_IDX, rxd1); + status->wcid = mt7996_rx_get_wcid(dev, idx, unicast); + + if (status->wcid) { + struct mt7996_sta *msta; + + msta = container_of(status->wcid, struct mt7996_sta, wcid); + spin_lock_bh(&dev->sta_poll_lock); + if (list_empty(&msta->poll_list)) + list_add_tail(&msta->poll_list, &dev->sta_poll_list); + spin_unlock_bh(&dev->sta_poll_lock); + } + + status->freq = mphy->chandef.chan->center_freq; + status->band = mphy->chandef.chan->band; + if (status->band == NL80211_BAND_5GHZ) + sband = &mphy->sband_5g.sband; + else if (status->band == NL80211_BAND_6GHZ) + sband = &mphy->sband_6g.sband; + else + sband = &mphy->sband_2g.sband; + + if (!sband->channels) + return -EINVAL; + + if ((rxd0 & csum_mask) == csum_mask && + !(csum_status & (BIT(0) | BIT(2) | BIT(3)))) + skb->ip_summed = CHECKSUM_UNNECESSARY; + + if (rxd1 & MT_RXD3_NORMAL_FCS_ERR) + status->flag |= RX_FLAG_FAILED_FCS_CRC; + + if (rxd1 & MT_RXD1_NORMAL_TKIP_MIC_ERR) + status->flag |= RX_FLAG_MMIC_ERROR; + + if (FIELD_GET(MT_RXD2_NORMAL_SEC_MODE, rxd2) != 0 && + !(rxd1 & (MT_RXD1_NORMAL_CLM | MT_RXD1_NORMAL_CM))) { + status->flag |= RX_FLAG_DECRYPTED; + status->flag |= RX_FLAG_IV_STRIPPED; + status->flag |= RX_FLAG_MMIC_STRIPPED | RX_FLAG_MIC_STRIPPED; + } + + remove_pad = FIELD_GET(MT_RXD2_NORMAL_HDR_OFFSET, rxd2); + + if (rxd2 & MT_RXD2_NORMAL_MAX_LEN_ERROR) + return -EINVAL; + + rxd += 8; + if (rxd1 & MT_RXD1_NORMAL_GROUP_4) { + u32 v0 = le32_to_cpu(rxd[0]); + u32 v2 = le32_to_cpu(rxd[2]); + + fc = cpu_to_le16(FIELD_GET(MT_RXD8_FRAME_CONTROL, v0)); + qos_ctl = FIELD_GET(MT_RXD10_QOS_CTL, v2); + seq_ctrl = FIELD_GET(MT_RXD10_SEQ_CTRL, v2); + + rxd += 4; + if ((u8 *)rxd - skb->data >= skb->len) + return -EINVAL; + } + + if (rxd1 & MT_RXD1_NORMAL_GROUP_1) { + u8 *data = (u8 *)rxd; + + if (status->flag & RX_FLAG_DECRYPTED) { + switch (FIELD_GET(MT_RXD2_NORMAL_SEC_MODE, rxd2)) { + case MT_CIPHER_AES_CCMP: + case MT_CIPHER_CCMP_CCX: + case MT_CIPHER_CCMP_256: + insert_ccmp_hdr = + FIELD_GET(MT_RXD2_NORMAL_FRAG, rxd2); + fallthrough; + case MT_CIPHER_TKIP: + case MT_CIPHER_TKIP_NO_MIC: + case MT_CIPHER_GCMP: + case MT_CIPHER_GCMP_256: + status->iv[0] = data[5]; + status->iv[1] = data[4]; + status->iv[2] = data[3]; + status->iv[3] = data[2]; + status->iv[4] = data[1]; + status->iv[5] = data[0]; + break; + default: + break; + } + } + rxd += 4; + if ((u8 *)rxd - skb->data >= skb->len) + return -EINVAL; + } + + if (rxd1 & MT_RXD1_NORMAL_GROUP_2) { + status->timestamp = le32_to_cpu(rxd[0]); + status->flag |= RX_FLAG_MACTIME_START; + + if (!(rxd2 & MT_RXD2_NORMAL_NON_AMPDU)) { + status->flag |= RX_FLAG_AMPDU_DETAILS; + + /* all subframes of an A-MPDU have the same timestamp */ + if (phy->rx_ampdu_ts != status->timestamp) { + if (!++phy->ampdu_ref) + phy->ampdu_ref++; + } + phy->rx_ampdu_ts = status->timestamp; + + status->ampdu_ref = phy->ampdu_ref; + } + + rxd += 4; + if ((u8 *)rxd - skb->data >= skb->len) + return -EINVAL; + } + + /* RXD Group 3 - P-RXV */ + if (rxd1 & MT_RXD1_NORMAL_GROUP_3) { + u32 v3; + int ret; + + rxv = rxd; + rxd += 4; + if ((u8 *)rxd - skb->data >= skb->len) + return -EINVAL; + + v3 = le32_to_cpu(rxv[3]); + + status->chains = mphy->antenna_mask; + status->chain_signal[0] = to_rssi(MT_PRXV_RCPI0, v3); + status->chain_signal[1] = to_rssi(MT_PRXV_RCPI1, v3); + status->chain_signal[2] = to_rssi(MT_PRXV_RCPI2, v3); + status->chain_signal[3] = to_rssi(MT_PRXV_RCPI3, v3); + + /* RXD Group 5 - C-RXV */ + if (rxd1 & MT_RXD1_NORMAL_GROUP_5) { + rxd += 24; + if ((u8 *)rxd - skb->data >= skb->len) + return -EINVAL; + } + + ret = mt7996_mac_fill_rx_rate(dev, status, sband, rxv, &mode); + if (ret < 0) + return ret; + } + + amsdu_info = FIELD_GET(MT_RXD4_NORMAL_PAYLOAD_FORMAT, rxd4); + status->amsdu = !!amsdu_info; + if (status->amsdu) { + status->first_amsdu = amsdu_info == MT_RXD4_FIRST_AMSDU_FRAME; + status->last_amsdu = amsdu_info == MT_RXD4_LAST_AMSDU_FRAME; + } + + hdr_gap = (u8 *)rxd - skb->data + 2 * remove_pad; + if (hdr_trans && ieee80211_has_morefrags(fc)) { + if (mt7996_reverse_frag0_hdr_trans(skb, hdr_gap)) + return -EINVAL; + hdr_trans = false; + } else { + int pad_start = 0; + + skb_pull(skb, hdr_gap); + if (!hdr_trans && status->amsdu) { + pad_start = ieee80211_get_hdrlen_from_skb(skb); + } else if (hdr_trans && (rxd2 & MT_RXD2_NORMAL_HDR_TRANS_ERROR)) { + /* When header translation failure is indicated, + * the hardware will insert an extra 2-byte field + * containing the data length after the protocol + * type field. + */ + pad_start = 12; + if (get_unaligned_be16(skb->data + pad_start) == ETH_P_8021Q) + pad_start += 4; + else + pad_start = 0; + } + + if (pad_start) { + memmove(skb->data + 2, skb->data, pad_start); + skb_pull(skb, 2); + } + } + + if (!hdr_trans) { + struct ieee80211_hdr *hdr; + + if (insert_ccmp_hdr) { + u8 key_id = FIELD_GET(MT_RXD1_NORMAL_KEY_ID, rxd1); + + mt76_insert_ccmp_hdr(skb, key_id); + } + + hdr = mt76_skb_get_hdr(skb); + fc = hdr->frame_control; + if (ieee80211_is_data_qos(fc)) { + seq_ctrl = le16_to_cpu(hdr->seq_ctrl); + qos_ctl = *ieee80211_get_qos_ctl(hdr); + } + } else { + status->flag |= RX_FLAG_8023; + } + + if (rxv && mode >= MT_PHY_TYPE_HE_SU && !(status->flag & RX_FLAG_8023)) + mt7996_mac_decode_he_radiotap(skb, rxv, mode); + + if (!status->wcid || !ieee80211_is_data_qos(fc)) + return 0; + + status->aggr = unicast && + !ieee80211_is_qos_nullfunc(fc); + status->qos_ctl = qos_ctl; + status->seqno = IEEE80211_SEQ_TO_SN(seq_ctrl); + + return 0; +} + +static void +mt7996_mac_write_txwi_8023(struct mt7996_dev *dev, __le32 *txwi, + struct sk_buff *skb, struct mt76_wcid *wcid) +{ + u8 tid = skb->priority & IEEE80211_QOS_CTL_TID_MASK; + u8 fc_type, fc_stype; + u16 ethertype; + bool wmm = false; + u32 val; + + if (wcid->sta) { + struct ieee80211_sta *sta; + + sta = container_of((void *)wcid, struct ieee80211_sta, drv_priv); + wmm = sta->wme; + } + + val = FIELD_PREP(MT_TXD1_HDR_FORMAT, MT_HDR_FORMAT_802_3) | + FIELD_PREP(MT_TXD1_TID, tid); + + ethertype = get_unaligned_be16(&skb->data[12]); + if (ethertype >= ETH_P_802_3_MIN) + val |= MT_TXD1_ETH_802_3; + + txwi[1] |= cpu_to_le32(val); + + fc_type = IEEE80211_FTYPE_DATA >> 2; + fc_stype = wmm ? IEEE80211_STYPE_QOS_DATA >> 4 : 0; + + val = FIELD_PREP(MT_TXD2_FRAME_TYPE, fc_type) | + FIELD_PREP(MT_TXD2_SUB_TYPE, fc_stype); + + txwi[2] |= cpu_to_le32(val); +} + +static void +mt7996_mac_write_txwi_80211(struct mt7996_dev *dev, __le32 *txwi, + struct sk_buff *skb, struct ieee80211_key_conf *key) +{ + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; + struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)skb->data; + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + bool multicast = is_multicast_ether_addr(hdr->addr1); + u8 tid = skb->priority & IEEE80211_QOS_CTL_TID_MASK; + __le16 fc = hdr->frame_control; + u8 fc_type, fc_stype; + u32 val; + + if (ieee80211_is_action(fc) && + mgmt->u.action.category == WLAN_CATEGORY_BACK && + mgmt->u.action.u.addba_req.action_code == WLAN_ACTION_ADDBA_REQ) + tid = MT_TX_ADDBA; + else if (ieee80211_is_mgmt(hdr->frame_control)) + tid = MT_TX_NORMAL; + + val = FIELD_PREP(MT_TXD1_HDR_FORMAT, MT_HDR_FORMAT_802_11) | + FIELD_PREP(MT_TXD1_HDR_INFO, + ieee80211_get_hdrlen_from_skb(skb) / 2) | + FIELD_PREP(MT_TXD1_TID, tid); + + if (!ieee80211_is_data(fc) || multicast || + info->flags & IEEE80211_TX_CTL_USE_MINRATE) + val |= MT_TXD1_FIXED_RATE; + + if (key && multicast && ieee80211_is_robust_mgmt_frame(skb) && + key->cipher == WLAN_CIPHER_SUITE_AES_CMAC) { + val |= MT_TXD1_BIP; + txwi[3] &= ~cpu_to_le32(MT_TXD3_PROTECT_FRAME); + } + + txwi[1] |= cpu_to_le32(val); + + fc_type = (le16_to_cpu(fc) & IEEE80211_FCTL_FTYPE) >> 2; + fc_stype = (le16_to_cpu(fc) & IEEE80211_FCTL_STYPE) >> 4; + + val = FIELD_PREP(MT_TXD2_FRAME_TYPE, fc_type) | + FIELD_PREP(MT_TXD2_SUB_TYPE, fc_stype); + + txwi[2] |= cpu_to_le32(val); + + txwi[3] |= cpu_to_le32(FIELD_PREP(MT_TXD3_BCM, multicast)); + if (ieee80211_is_beacon(fc)) { + txwi[3] &= ~cpu_to_le32(MT_TXD3_SW_POWER_MGMT); + txwi[3] |= cpu_to_le32(MT_TXD3_REM_TX_COUNT); + } + + if (info->flags & IEEE80211_TX_CTL_INJECTED) { + u16 seqno = le16_to_cpu(hdr->seq_ctrl); + + if (ieee80211_is_back_req(hdr->frame_control)) { + struct ieee80211_bar *bar; + + bar = (struct ieee80211_bar *)skb->data; + seqno = le16_to_cpu(bar->start_seq_num); + } + + val = MT_TXD3_SN_VALID | + FIELD_PREP(MT_TXD3_SEQ, IEEE80211_SEQ_TO_SN(seqno)); + txwi[3] |= cpu_to_le32(val); + txwi[3] &= ~cpu_to_le32(MT_TXD3_HW_AMSDU); + } +} + +static u16 +mt7996_mac_tx_rate_val(struct mt76_phy *mphy, struct ieee80211_vif *vif, + bool beacon, bool mcast) +{ + u8 mode = 0, band = mphy->chandef.chan->band; + int rateidx = 0, mcast_rate; + + if (beacon) { + struct cfg80211_bitrate_mask *mask; + + mask = &vif->bss_conf.beacon_tx_rate; + if (hweight16(mask->control[band].he_mcs[0]) == 1) { + rateidx = ffs(mask->control[band].he_mcs[0]) - 1; + mode = MT_PHY_TYPE_HE_SU; + goto out; + } else if (hweight16(mask->control[band].vht_mcs[0]) == 1) { + rateidx = ffs(mask->control[band].vht_mcs[0]) - 1; + mode = MT_PHY_TYPE_VHT; + goto out; + } else if (hweight8(mask->control[band].ht_mcs[0]) == 1) { + rateidx = ffs(mask->control[band].ht_mcs[0]) - 1; + mode = MT_PHY_TYPE_HT; + goto out; + } else if (hweight32(mask->control[band].legacy) == 1) { + rateidx = ffs(mask->control[band].legacy) - 1; + goto legacy; + } + } + + mcast_rate = vif->bss_conf.mcast_rate[band]; + if (mcast && mcast_rate > 0) + rateidx = mcast_rate - 1; + else + rateidx = ffs(vif->bss_conf.basic_rates) - 1; + +legacy: + rateidx = mt76_calculate_default_rate(mphy, rateidx); + mode = rateidx >> 8; + rateidx &= GENMASK(7, 0); + +out: + return FIELD_PREP(MT_TX_RATE_IDX, rateidx) | + FIELD_PREP(MT_TX_RATE_MODE, mode); +} + +void mt7996_mac_write_txwi(struct mt7996_dev *dev, __le32 *txwi, + struct sk_buff *skb, struct mt76_wcid *wcid, int pid, + struct ieee80211_key_conf *key, u32 changed) +{ + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct ieee80211_vif *vif = info->control.vif; + struct mt76_phy *mphy = &dev->mphy; + u8 band_idx = (info->hw_queue & MT_TX_HW_QUEUE_PHY) >> 2; + u8 p_fmt, q_idx, omac_idx = 0, wmm_idx = 0; + bool is_8023 = info->flags & IEEE80211_TX_CTL_HW_80211_ENCAP; + u16 tx_count = 15; + u32 val; + bool beacon = !!(changed & (BSS_CHANGED_BEACON | + BSS_CHANGED_BEACON_ENABLED)); + bool inband_disc = !!(changed & (BSS_CHANGED_UNSOL_BCAST_PROBE_RESP | + BSS_CHANGED_FILS_DISCOVERY)); + + if (vif) { + struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv; + + omac_idx = mvif->mt76.omac_idx; + wmm_idx = mvif->mt76.wmm_idx; + band_idx = mvif->mt76.band_idx; + } + + mphy = mt76_dev_phy(&dev->mt76, band_idx); + + if (inband_disc) { + p_fmt = MT_TX_TYPE_FW; + q_idx = MT_LMAC_ALTX0; + } else if (beacon) { + p_fmt = MT_TX_TYPE_FW; + q_idx = MT_LMAC_BCN0; + } else if (skb_get_queue_mapping(skb) >= MT_TXQ_PSD) { + p_fmt = MT_TX_TYPE_CT; + q_idx = MT_LMAC_ALTX0; + } else { + p_fmt = MT_TX_TYPE_CT; + q_idx = wmm_idx * MT7996_MAX_WMM_SETS + + mt76_connac_lmac_mapping(skb_get_queue_mapping(skb)); + } + + val = FIELD_PREP(MT_TXD0_TX_BYTES, skb->len + MT_TXD_SIZE) | + FIELD_PREP(MT_TXD0_PKT_FMT, p_fmt) | + FIELD_PREP(MT_TXD0_Q_IDX, q_idx); + txwi[0] = cpu_to_le32(val); + + val = FIELD_PREP(MT_TXD1_WLAN_IDX, wcid->idx) | + FIELD_PREP(MT_TXD1_OWN_MAC, omac_idx); + + if (band_idx) + val |= FIELD_PREP(MT_TXD1_TGID, band_idx); + + txwi[1] = cpu_to_le32(val); + txwi[2] = 0; + + val = MT_TXD3_SW_POWER_MGMT | + FIELD_PREP(MT_TXD3_REM_TX_COUNT, tx_count); + if (key) + val |= MT_TXD3_PROTECT_FRAME; + if (info->flags & IEEE80211_TX_CTL_NO_ACK) + val |= MT_TXD3_NO_ACK; + if (wcid->amsdu) + val |= MT_TXD3_HW_AMSDU; + + txwi[3] = cpu_to_le32(val); + txwi[4] = 0; + + val = FIELD_PREP(MT_TXD5_PID, pid); + if (pid >= MT_PACKET_ID_FIRST) + val |= MT_TXD5_TX_STATUS_HOST; + txwi[5] = cpu_to_le32(val); + + val = MT_TXD6_DIS_MAT | MT_TXD6_DAS | + FIELD_PREP(MT_TXD6_MSDU_CNT, 1); + txwi[6] = cpu_to_le32(val); + txwi[7] = 0; + + if (is_8023) + mt7996_mac_write_txwi_8023(dev, txwi, skb, wcid); + else + mt7996_mac_write_txwi_80211(dev, txwi, skb, key); + + if (txwi[1] & cpu_to_le32(MT_TXD1_FIXED_RATE)) { + /* Fixed rata is available just for 802.11 txd */ + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; + bool multicast = is_multicast_ether_addr(hdr->addr1); + u16 rate = mt7996_mac_tx_rate_val(mphy, vif, beacon, multicast); + + /* fix to bw 20 */ + val = MT_TXD6_FIXED_BW | + FIELD_PREP(MT_TXD6_BW, 0) | + FIELD_PREP(MT_TXD6_TX_RATE, rate); + + txwi[6] |= cpu_to_le32(val); + txwi[3] |= cpu_to_le32(MT_TXD3_BA_DISABLE); + } +} + +int mt7996_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr, + enum mt76_txq_id qid, struct mt76_wcid *wcid, + struct ieee80211_sta *sta, + struct mt76_tx_info *tx_info) +{ + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)tx_info->skb->data; + struct mt7996_dev *dev = container_of(mdev, struct mt7996_dev, mt76); + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(tx_info->skb); + struct ieee80211_key_conf *key = info->control.hw_key; + struct ieee80211_vif *vif = info->control.vif; + struct mt76_txwi_cache *t; + struct mt7996_txp *txp; + int id, i, pid, nbuf = tx_info->nbuf - 1; + bool is_8023 = info->flags & IEEE80211_TX_CTL_HW_80211_ENCAP; + u8 *txwi = (u8 *)txwi_ptr; + + if (unlikely(tx_info->skb->len <= ETH_HLEN)) + return -EINVAL; + + if (!wcid) + wcid = &dev->mt76.global_wcid; + + if (sta) { + struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv; + + if (time_after(jiffies, msta->jiffies + HZ / 4)) { + info->flags |= IEEE80211_TX_CTL_REQ_TX_STATUS; + msta->jiffies = jiffies; + } + } + + t = (struct mt76_txwi_cache *)(txwi + mdev->drv->txwi_size); + t->skb = tx_info->skb; + + id = mt76_token_consume(mdev, &t); + if (id < 0) + return id; + + pid = mt76_tx_status_skb_add(mdev, wcid, tx_info->skb); + memset(txwi_ptr, 0, MT_TXD_SIZE); + /* Transmit non qos data by 802.11 header and need to fill txd by host*/ + if (!is_8023 || pid >= MT_PACKET_ID_FIRST) + mt7996_mac_write_txwi(dev, txwi_ptr, tx_info->skb, wcid, pid, + key, 0); + + txp = (struct mt7996_txp *)(txwi + MT_TXD_SIZE); + for (i = 0; i < nbuf; i++) { + txp->buf[i] = cpu_to_le32(tx_info->buf[i + 1].addr); + txp->len[i] = cpu_to_le16(tx_info->buf[i + 1].len); + } + txp->nbuf = nbuf; + + txp->flags = cpu_to_le16(MT_CT_INFO_FROM_HOST); + + if (!is_8023 || pid >= MT_PACKET_ID_FIRST) + txp->flags |= cpu_to_le16(MT_CT_INFO_APPLY_TXD); + + if (!key) + txp->flags |= cpu_to_le16(MT_CT_INFO_NONE_CIPHER_FRAME); + + if (!is_8023 && ieee80211_is_mgmt(hdr->frame_control)) + txp->flags |= cpu_to_le16(MT_CT_INFO_MGMT_FRAME); + + if (vif) { + struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv; + + txp->bss_idx = mvif->mt76.idx; + } + + txp->token = cpu_to_le16(id); + if (test_bit(MT_WCID_FLAG_4ADDR, &wcid->flags)) + txp->rept_wds_wcid = cpu_to_le16(wcid->idx); + else + txp->rept_wds_wcid = cpu_to_le16(0xfff); + tx_info->skb = DMA_DUMMY_DATA; + + /* pass partial skb header to fw */ + tx_info->buf[1].len = MT_CT_PARSE_LEN; + tx_info->buf[1].skip_unmap = true; + tx_info->nbuf = MT_CT_DMA_BUF_NUM; + + return 0; +} + +static void +mt7996_tx_check_aggr(struct ieee80211_sta *sta, __le32 *txwi) +{ + struct mt7996_sta *msta; + u16 fc, tid; + u32 val; + + if (!sta || !(sta->deflink.ht_cap.ht_supported || sta->deflink.he_cap.has_he)) + return; + + tid = le32_get_bits(txwi[1], MT_TXD1_TID); + if (tid >= 6) /* skip VO queue */ + return; + + val = le32_to_cpu(txwi[2]); + fc = FIELD_GET(MT_TXD2_FRAME_TYPE, val) << 2 | + FIELD_GET(MT_TXD2_SUB_TYPE, val) << 4; + if (unlikely(fc != (IEEE80211_FTYPE_DATA | IEEE80211_STYPE_QOS_DATA))) + return; + + msta = (struct mt7996_sta *)sta->drv_priv; + if (!test_and_set_bit(tid, &msta->ampdu_state)) + ieee80211_start_tx_ba_session(sta, tid, 0); +} + +static void +mt7996_txp_skb_unmap(struct mt76_dev *dev, struct mt76_txwi_cache *t) +{ + struct mt7996_txp *txp; + int i; + + txp = mt7996_txwi_to_txp(dev, t); + for (i = 0; i < txp->nbuf; i++) + dma_unmap_single(dev->dev, le32_to_cpu(txp->buf[i]), + le16_to_cpu(txp->len[i]), DMA_TO_DEVICE); +} + +static void +mt7996_txwi_free(struct mt7996_dev *dev, struct mt76_txwi_cache *t, + struct ieee80211_sta *sta, struct list_head *free_list) +{ + struct mt76_dev *mdev = &dev->mt76; + struct mt76_wcid *wcid; + __le32 *txwi; + u16 wcid_idx; + + mt7996_txp_skb_unmap(mdev, t); + if (!t->skb) + goto out; + + txwi = (__le32 *)mt76_get_txwi_ptr(mdev, t); + if (sta) { + wcid = (struct mt76_wcid *)sta->drv_priv; + wcid_idx = wcid->idx; + + if (likely(t->skb->protocol != cpu_to_be16(ETH_P_PAE))) + mt7996_tx_check_aggr(sta, txwi); + } else { + wcid_idx = le32_get_bits(txwi[1], MT_TXD1_WLAN_IDX); + } + + __mt76_tx_complete_skb(mdev, wcid_idx, t->skb, free_list); + +out: + t->skb = NULL; + mt76_put_txwi(mdev, t); +} + +static void +mt7996_mac_tx_free(struct mt7996_dev *dev, void *data, int len) +{ + __le32 *tx_free = (__le32 *)data, *cur_info; + struct mt76_dev *mdev = &dev->mt76; + struct mt76_phy *phy2 = mdev->phys[MT_BAND1]; + struct mt76_phy *phy3 = mdev->phys[MT_BAND2]; + struct mt76_txwi_cache *txwi; + struct ieee80211_sta *sta = NULL; + LIST_HEAD(free_list); + struct sk_buff *skb, *tmp; + void *end = data + len; + bool wake = false; + u16 total, count = 0; + + /* clean DMA queues and unmap buffers first */ + mt76_queue_tx_cleanup(dev, dev->mphy.q_tx[MT_TXQ_PSD], false); + mt76_queue_tx_cleanup(dev, dev->mphy.q_tx[MT_TXQ_BE], false); + if (phy2) { + mt76_queue_tx_cleanup(dev, phy2->q_tx[MT_TXQ_PSD], false); + mt76_queue_tx_cleanup(dev, phy2->q_tx[MT_TXQ_BE], false); + } + if (phy3) { + mt76_queue_tx_cleanup(dev, phy3->q_tx[MT_TXQ_PSD], false); + mt76_queue_tx_cleanup(dev, phy3->q_tx[MT_TXQ_BE], false); + } + + if (WARN_ON_ONCE(le32_get_bits(tx_free[1], MT_TXFREE1_VER) < 4)) + return; + + total = le32_get_bits(tx_free[0], MT_TXFREE0_MSDU_CNT); + for (cur_info = &tx_free[2]; count < total; cur_info++) { + u32 msdu, info; + u8 i; + + if (WARN_ON_ONCE((void *)cur_info >= end)) + return; + /* 1'b1: new wcid pair. + * 1'b0: msdu_id with the same 'wcid pair' as above. + */ + info = le32_to_cpu(*cur_info); + if (info & MT_TXFREE_INFO_PAIR) { + struct mt7996_sta *msta; + struct mt76_wcid *wcid; + u16 idx; + + idx = FIELD_GET(MT_TXFREE_INFO_WLAN_ID, info); + wcid = rcu_dereference(dev->mt76.wcid[idx]); + sta = wcid_to_sta(wcid); + if (!sta) + continue; + + msta = container_of(wcid, struct mt7996_sta, wcid); + spin_lock_bh(&dev->sta_poll_lock); + if (list_empty(&msta->poll_list)) + list_add_tail(&msta->poll_list, &dev->sta_poll_list); + spin_unlock_bh(&dev->sta_poll_lock); + continue; + } + + if (info & MT_TXFREE_INFO_HEADER) + continue; + + for (i = 0; i < 2; i++) { + msdu = (info >> (15 * i)) & MT_TXFREE_INFO_MSDU_ID; + if (msdu == MT_TXFREE_INFO_MSDU_ID) + continue; + + count++; + txwi = mt76_token_release(mdev, msdu, &wake); + if (!txwi) + continue; + + mt7996_txwi_free(dev, txwi, sta, &free_list); + } + } + + mt7996_mac_sta_poll(dev); + + if (wake) + mt76_set_tx_blocked(&dev->mt76, false); + + mt76_worker_schedule(&dev->mt76.tx_worker); + + list_for_each_entry_safe(skb, tmp, &free_list, list) { + skb_list_del_init(skb); + napi_consume_skb(skb, 1); + } +} + +static bool +mt7996_mac_add_txs_skb(struct mt7996_dev *dev, struct mt76_wcid *wcid, int pid, + __le32 *txs_data, struct mt76_sta_stats *stats) +{ + struct ieee80211_supported_band *sband; + struct mt76_dev *mdev = &dev->mt76; + struct mt76_phy *mphy; + struct ieee80211_tx_info *info; + struct sk_buff_head list; + struct rate_info rate = {}; + struct sk_buff *skb; + bool cck = false; + u32 txrate, txs, mode, stbc; + + mt76_tx_status_lock(mdev, &list); + skb = mt76_tx_status_skb_get(mdev, wcid, pid, &list); + if (!skb) + goto out_no_skb; + + txs = le32_to_cpu(txs_data[0]); + + info = IEEE80211_SKB_CB(skb); + if (!(txs & MT_TXS0_ACK_ERROR_MASK)) + info->flags |= IEEE80211_TX_STAT_ACK; + + info->status.ampdu_len = 1; + info->status.ampdu_ack_len = !!(info->flags & + IEEE80211_TX_STAT_ACK); + + info->status.rates[0].idx = -1; + + txrate = FIELD_GET(MT_TXS0_TX_RATE, txs); + + rate.mcs = FIELD_GET(MT_TX_RATE_IDX, txrate); + rate.nss = FIELD_GET(MT_TX_RATE_NSS, txrate) + 1; + stbc = le32_get_bits(txs_data[3], MT_TXS3_RATE_STBC); + + if (stbc && rate.nss > 1) + rate.nss >>= 1; + + if (rate.nss - 1 < ARRAY_SIZE(stats->tx_nss)) + stats->tx_nss[rate.nss - 1]++; + if (rate.mcs < ARRAY_SIZE(stats->tx_mcs)) + stats->tx_mcs[rate.mcs]++; + + mode = FIELD_GET(MT_TX_RATE_MODE, txrate); + switch (mode) { + case MT_PHY_TYPE_CCK: + cck = true; + fallthrough; + case MT_PHY_TYPE_OFDM: + mphy = mt76_dev_phy(mdev, wcid->phy_idx); + + if (mphy->chandef.chan->band == NL80211_BAND_5GHZ) + sband = &mphy->sband_5g.sband; + else if (mphy->chandef.chan->band == NL80211_BAND_6GHZ) + sband = &mphy->sband_6g.sband; + else + sband = &mphy->sband_2g.sband; + + rate.mcs = mt76_get_rate(mphy->dev, sband, rate.mcs, cck); + rate.legacy = sband->bitrates[rate.mcs].bitrate; + break; + case MT_PHY_TYPE_HT: + case MT_PHY_TYPE_HT_GF: + if (rate.mcs > 31) + goto out; + + rate.flags = RATE_INFO_FLAGS_MCS; + if (wcid->rate.flags & RATE_INFO_FLAGS_SHORT_GI) + rate.flags |= RATE_INFO_FLAGS_SHORT_GI; + break; + case MT_PHY_TYPE_VHT: + if (rate.mcs > 9) + goto out; + + rate.flags = RATE_INFO_FLAGS_VHT_MCS; + break; + case MT_PHY_TYPE_HE_SU: + case MT_PHY_TYPE_HE_EXT_SU: + case MT_PHY_TYPE_HE_TB: + case MT_PHY_TYPE_HE_MU: + if (rate.mcs > 11) + goto out; + + rate.he_gi = wcid->rate.he_gi; + rate.he_dcm = FIELD_GET(MT_TX_RATE_DCM, txrate); + rate.flags = RATE_INFO_FLAGS_HE_MCS; + break; + default: + goto out; + } + + stats->tx_mode[mode]++; + + switch (FIELD_GET(MT_TXS0_BW, txs)) { + case IEEE80211_STA_RX_BW_160: + rate.bw = RATE_INFO_BW_160; + stats->tx_bw[3]++; + break; + case IEEE80211_STA_RX_BW_80: + rate.bw = RATE_INFO_BW_80; + stats->tx_bw[2]++; + break; + case IEEE80211_STA_RX_BW_40: + rate.bw = RATE_INFO_BW_40; + stats->tx_bw[1]++; + break; + default: + rate.bw = RATE_INFO_BW_20; + stats->tx_bw[0]++; + break; + } + wcid->rate = rate; + +out: + mt76_tx_status_skb_done(mdev, skb, &list); + +out_no_skb: + mt76_tx_status_unlock(mdev, &list); + + return !!skb; +} + +static void mt7996_mac_add_txs(struct mt7996_dev *dev, void *data) +{ + struct mt7996_sta *msta = NULL; + struct mt76_wcid *wcid; + __le32 *txs_data = data; + u16 wcidx; + u8 pid; + + if (le32_get_bits(txs_data[0], MT_TXS0_TXS_FORMAT) > 1) + return; + + wcidx = le32_get_bits(txs_data[2], MT_TXS2_WCID); + pid = le32_get_bits(txs_data[3], MT_TXS3_PID); + + if (pid < MT_PACKET_ID_FIRST) + return; + + if (wcidx >= MT7996_WTBL_SIZE) + return; + + rcu_read_lock(); + + wcid = rcu_dereference(dev->mt76.wcid[wcidx]); + if (!wcid) + goto out; + + msta = container_of(wcid, struct mt7996_sta, wcid); + + mt7996_mac_add_txs_skb(dev, wcid, pid, txs_data, &msta->stats); + + if (!wcid->sta) + goto out; + + spin_lock_bh(&dev->sta_poll_lock); + if (list_empty(&msta->poll_list)) + list_add_tail(&msta->poll_list, &dev->sta_poll_list); + spin_unlock_bh(&dev->sta_poll_lock); + +out: + rcu_read_unlock(); +} + +bool mt7996_rx_check(struct mt76_dev *mdev, void *data, int len) +{ + struct mt7996_dev *dev = container_of(mdev, struct mt7996_dev, mt76); + __le32 *rxd = (__le32 *)data; + __le32 *end = (__le32 *)&rxd[len / 4]; + enum rx_pkt_type type; + + type = le32_get_bits(rxd[0], MT_RXD0_PKT_TYPE); + if (type != PKT_TYPE_NORMAL) { + u32 sw_type = le32_get_bits(rxd[0], MT_RXD0_SW_PKT_TYPE_MASK); + + if (unlikely((sw_type & MT_RXD0_SW_PKT_TYPE_MAP) == + MT_RXD0_SW_PKT_TYPE_FRAME)) + return true; + } + + switch (type) { + case PKT_TYPE_TXRX_NOTIFY: + mt7996_mac_tx_free(dev, data, len); + return false; + case PKT_TYPE_TXS: + for (rxd += 4; rxd + 8 <= end; rxd += 8) + mt7996_mac_add_txs(dev, rxd); + return false; + case PKT_TYPE_RX_FW_MONITOR: + mt7996_debugfs_rx_fw_monitor(dev, data, len); + return false; + default: + return true; + } +} + +void mt7996_queue_rx_skb(struct mt76_dev *mdev, enum mt76_rxq_id q, + struct sk_buff *skb, u32 *info) +{ + struct mt7996_dev *dev = container_of(mdev, struct mt7996_dev, mt76); + __le32 *rxd = (__le32 *)skb->data; + __le32 *end = (__le32 *)&skb->data[skb->len]; + enum rx_pkt_type type; + + type = le32_get_bits(rxd[0], MT_RXD0_PKT_TYPE); + if (type != PKT_TYPE_NORMAL) { + u32 sw_type = le32_get_bits(rxd[0], MT_RXD0_SW_PKT_TYPE_MASK); + + if (unlikely((sw_type & MT_RXD0_SW_PKT_TYPE_MAP) == + MT_RXD0_SW_PKT_TYPE_FRAME)) + type = PKT_TYPE_NORMAL; + } + + switch (type) { + case PKT_TYPE_TXRX_NOTIFY: + mt7996_mac_tx_free(dev, skb->data, skb->len); + napi_consume_skb(skb, 1); + break; + case PKT_TYPE_RX_EVENT: + mt7996_mcu_rx_event(dev, skb); + break; + case PKT_TYPE_TXS: + for (rxd += 4; rxd + 8 <= end; rxd += 8) + mt7996_mac_add_txs(dev, rxd); + dev_kfree_skb(skb); + break; + case PKT_TYPE_RX_FW_MONITOR: + mt7996_debugfs_rx_fw_monitor(dev, skb->data, skb->len); + dev_kfree_skb(skb); + break; + case PKT_TYPE_NORMAL: + if (!mt7996_mac_fill_rx(dev, skb)) { + mt76_rx(&dev->mt76, q, skb); + return; + } + fallthrough; + default: + dev_kfree_skb(skb); + break; + } +} + +void mt7996_tx_complete_skb(struct mt76_dev *mdev, struct mt76_queue_entry *e) +{ + if (!e->txwi) { + dev_kfree_skb_any(e->skb); + return; + } + + /* error path */ + if (e->skb == DMA_DUMMY_DATA) { + struct mt76_txwi_cache *t; + struct mt7996_txp *txp; + + txp = mt7996_txwi_to_txp(mdev, e->txwi); + t = mt76_token_put(mdev, le16_to_cpu(txp->token)); + e->skb = t ? t->skb : NULL; + } + + if (e->skb) + mt76_tx_complete_skb(mdev, e->wcid, e->skb); +} + +void mt7996_mac_cca_stats_reset(struct mt7996_phy *phy) +{ + struct mt7996_dev *dev = phy->dev; + u32 reg = MT_WF_PHYRX_BAND_RX_CTRL1(phy->mt76->band_idx); + + mt76_clear(dev, reg, MT_WF_PHYRX_BAND_RX_CTRL1_STSCNT_EN); + mt76_set(dev, reg, BIT(11) | BIT(9)); +} + +void mt7996_mac_reset_counters(struct mt7996_phy *phy) +{ + struct mt7996_dev *dev = phy->dev; + u8 band_idx = phy->mt76->band_idx; + int i; + + for (i = 0; i < 16; i++) + mt76_rr(dev, MT_TX_AGG_CNT(band_idx, i)); + + phy->mt76->survey_time = ktime_get_boottime(); + + memset(phy->mt76->aggr_stats, 0, sizeof(phy->mt76->aggr_stats)); + + /* reset airtime counters */ + mt76_set(dev, MT_WF_RMAC_MIB_AIRTIME0(band_idx), + MT_WF_RMAC_MIB_RXTIME_CLR); + + mt7996_mcu_get_chan_mib_info(phy, true); +} + +void mt7996_mac_set_timing(struct mt7996_phy *phy) +{ + s16 coverage_class = phy->coverage_class; + struct mt7996_dev *dev = phy->dev; + struct mt7996_phy *phy2 = mt7996_phy2(dev); + struct mt7996_phy *phy3 = mt7996_phy3(dev); + u32 val, reg_offset; + u32 cck = FIELD_PREP(MT_TIMEOUT_VAL_PLCP, 231) | + FIELD_PREP(MT_TIMEOUT_VAL_CCA, 48); + u32 ofdm = FIELD_PREP(MT_TIMEOUT_VAL_PLCP, 60) | + FIELD_PREP(MT_TIMEOUT_VAL_CCA, 28); + u8 band_idx = phy->mt76->band_idx; + int offset; + bool a_band = !(phy->mt76->chandef.chan->band == NL80211_BAND_2GHZ); + + if (!test_bit(MT76_STATE_RUNNING, &phy->mt76->state)) + return; + + if (phy2) + coverage_class = max_t(s16, dev->phy.coverage_class, + phy2->coverage_class); + + if (phy3) + coverage_class = max_t(s16, coverage_class, + phy3->coverage_class); + + mt76_set(dev, MT_ARB_SCR(band_idx), + MT_ARB_SCR_TX_DISABLE | MT_ARB_SCR_RX_DISABLE); + udelay(1); + + offset = 3 * coverage_class; + reg_offset = FIELD_PREP(MT_TIMEOUT_VAL_PLCP, offset) | + FIELD_PREP(MT_TIMEOUT_VAL_CCA, offset); + + mt76_wr(dev, MT_TMAC_CDTR(band_idx), cck + reg_offset); + mt76_wr(dev, MT_TMAC_ODTR(band_idx), ofdm + reg_offset); + mt76_wr(dev, MT_TMAC_ICR0(band_idx), + FIELD_PREP(MT_IFS_EIFS_OFDM, a_band ? 84 : 78) | + FIELD_PREP(MT_IFS_RIFS, 2) | + FIELD_PREP(MT_IFS_SIFS, 10) | + FIELD_PREP(MT_IFS_SLOT, phy->slottime)); + + if (!a_band) + mt76_wr(dev, MT_TMAC_ICR1(band_idx), + FIELD_PREP(MT_IFS_EIFS_CCK, 314)); + + if (phy->slottime < 20 || a_band) + val = MT7996_CFEND_RATE_DEFAULT; + else + val = MT7996_CFEND_RATE_11B; + + mt76_rmw_field(dev, MT_AGG_ACR0(band_idx), MT_AGG_ACR_CFEND_RATE, val); + mt76_clear(dev, MT_ARB_SCR(band_idx), + MT_ARB_SCR_TX_DISABLE | MT_ARB_SCR_RX_DISABLE); +} + +void mt7996_mac_enable_nf(struct mt7996_dev *dev, u8 band) +{ + mt76_set(dev, MT_WF_PHYRX_CSD_BAND_RXTD12(band), + MT_WF_PHYRX_CSD_BAND_RXTD12_IRPI_SW_CLR_ONLY | + MT_WF_PHYRX_CSD_BAND_RXTD12_IRPI_SW_CLR); + + mt76_set(dev, MT_WF_PHYRX_BAND_RX_CTRL1(band), + FIELD_PREP(MT_WF_PHYRX_BAND_RX_CTRL1_IPI_EN, 0x5)); +} + +static u8 +mt7996_phy_get_nf(struct mt7996_phy *phy, u8 band_idx) +{ + static const u8 nf_power[] = { 92, 89, 86, 83, 80, 75, 70, 65, 60, 55, 52 }; + struct mt7996_dev *dev = phy->dev; + u32 val, sum = 0, n = 0; + int ant, i; + + for (ant = 0; ant < hweight8(phy->mt76->antenna_mask); ant++) { + u32 reg = MT_WF_PHYRX_CSD_IRPI(band_idx, ant); + + for (i = 0; i < ARRAY_SIZE(nf_power); i++, reg += 4) { + val = mt76_rr(dev, reg); + sum += val * nf_power[i]; + n += val; + } + } + + return n ? sum / n : 0; +} + +void mt7996_update_channel(struct mt76_phy *mphy) +{ + struct mt7996_phy *phy = (struct mt7996_phy *)mphy->priv; + struct mt76_channel_state *state = mphy->chan_state; + int nf; + + mt7996_mcu_get_chan_mib_info(phy, false); + + nf = mt7996_phy_get_nf(phy, mphy->band_idx); + if (!phy->noise) + phy->noise = nf << 4; + else if (nf) + phy->noise += nf - (phy->noise >> 4); + + state->noise = -(phy->noise >> 4); +} + +static bool +mt7996_wait_reset_state(struct mt7996_dev *dev, u32 state) +{ + bool ret; + + ret = wait_event_timeout(dev->reset_wait, + (READ_ONCE(dev->reset_state) & state), + MT7996_RESET_TIMEOUT); + + WARN(!ret, "Timeout waiting for MCU reset state %x\n", state); + return ret; +} + +static void +mt7996_update_vif_beacon(void *priv, u8 *mac, struct ieee80211_vif *vif) +{ + struct ieee80211_hw *hw = priv; + + switch (vif->type) { + case NL80211_IFTYPE_MESH_POINT: + case NL80211_IFTYPE_ADHOC: + case NL80211_IFTYPE_AP: + mt7996_mcu_add_beacon(hw, vif, vif->bss_conf.enable_beacon); + break; + default: + break; + } +} + +static void +mt7996_update_beacons(struct mt7996_dev *dev) +{ + struct mt76_phy *phy2, *phy3; + + ieee80211_iterate_active_interfaces(dev->mt76.hw, + IEEE80211_IFACE_ITER_RESUME_ALL, + mt7996_update_vif_beacon, dev->mt76.hw); + + phy2 = dev->mt76.phys[MT_BAND1]; + if (!phy2) + return; + + ieee80211_iterate_active_interfaces(phy2->hw, + IEEE80211_IFACE_ITER_RESUME_ALL, + mt7996_update_vif_beacon, phy2->hw); + + phy3 = dev->mt76.phys[MT_BAND2]; + if (!phy3) + return; + + ieee80211_iterate_active_interfaces(phy3->hw, + IEEE80211_IFACE_ITER_RESUME_ALL, + mt7996_update_vif_beacon, phy3->hw); +} + +static void +mt7996_dma_reset(struct mt7996_dev *dev) +{ + struct mt76_phy *phy2 = dev->mt76.phys[MT_BAND1]; + struct mt76_phy *phy3 = dev->mt76.phys[MT_BAND2]; + u32 hif1_ofs = MT_WFDMA0_PCIE1(0) - MT_WFDMA0(0); + int i; + + mt76_clear(dev, MT_WFDMA0_GLO_CFG, + MT_WFDMA0_GLO_CFG_TX_DMA_EN | + MT_WFDMA0_GLO_CFG_RX_DMA_EN); + + if (dev->hif2) + mt76_clear(dev, MT_WFDMA0_GLO_CFG + hif1_ofs, + MT_WFDMA0_GLO_CFG_TX_DMA_EN | + MT_WFDMA0_GLO_CFG_RX_DMA_EN); + + usleep_range(1000, 2000); + + for (i = 0; i < __MT_TXQ_MAX; i++) { + mt76_queue_tx_cleanup(dev, dev->mphy.q_tx[i], true); + if (phy2) + mt76_queue_tx_cleanup(dev, phy2->q_tx[i], true); + if (phy3) + mt76_queue_tx_cleanup(dev, phy3->q_tx[i], true); + } + + for (i = 0; i < __MT_MCUQ_MAX; i++) + mt76_queue_tx_cleanup(dev, dev->mt76.q_mcu[i], true); + + mt76_for_each_q_rx(&dev->mt76, i) + mt76_queue_rx_reset(dev, i); + + mt76_tx_status_check(&dev->mt76, true); + + /* re-init prefetch settings after reset */ + mt7996_dma_prefetch(dev); + + mt76_set(dev, MT_WFDMA0_GLO_CFG, + MT_WFDMA0_GLO_CFG_TX_DMA_EN | MT_WFDMA0_GLO_CFG_RX_DMA_EN); + + if (dev->hif2) + mt76_set(dev, MT_WFDMA0_GLO_CFG + hif1_ofs, + MT_WFDMA0_GLO_CFG_TX_DMA_EN | + MT_WFDMA0_GLO_CFG_RX_DMA_EN); +} + +void mt7996_tx_token_put(struct mt7996_dev *dev) +{ + struct mt76_txwi_cache *txwi; + int id; + + spin_lock_bh(&dev->mt76.token_lock); + idr_for_each_entry(&dev->mt76.token, txwi, id) { + mt7996_txwi_free(dev, txwi, NULL, NULL); + dev->mt76.token_count--; + } + spin_unlock_bh(&dev->mt76.token_lock); + idr_destroy(&dev->mt76.token); +} + +/* system error recovery */ +void mt7996_mac_reset_work(struct work_struct *work) +{ + struct mt7996_phy *phy2, *phy3; + struct mt7996_dev *dev; + int i; + + dev = container_of(work, struct mt7996_dev, reset_work); + phy2 = mt7996_phy2(dev); + phy3 = mt7996_phy3(dev); + + if (!(READ_ONCE(dev->reset_state) & MT_MCU_CMD_STOP_DMA)) + return; + + ieee80211_stop_queues(mt76_hw(dev)); + if (phy2) + ieee80211_stop_queues(phy2->mt76->hw); + if (phy3) + ieee80211_stop_queues(phy3->mt76->hw); + + set_bit(MT76_RESET, &dev->mphy.state); + set_bit(MT76_MCU_RESET, &dev->mphy.state); + wake_up(&dev->mt76.mcu.wait); + cancel_delayed_work_sync(&dev->mphy.mac_work); + if (phy2) { + set_bit(MT76_RESET, &phy2->mt76->state); + cancel_delayed_work_sync(&phy2->mt76->mac_work); + } + if (phy3) { + set_bit(MT76_RESET, &phy3->mt76->state); + cancel_delayed_work_sync(&phy3->mt76->mac_work); + } + mt76_worker_disable(&dev->mt76.tx_worker); + mt76_for_each_q_rx(&dev->mt76, i) + napi_disable(&dev->mt76.napi[i]); + napi_disable(&dev->mt76.tx_napi); + + mutex_lock(&dev->mt76.mutex); + + mt76_wr(dev, MT_MCU_INT_EVENT, MT_MCU_INT_EVENT_DMA_STOPPED); + + if (mt7996_wait_reset_state(dev, MT_MCU_CMD_RESET_DONE)) { + mt7996_dma_reset(dev); + + mt7996_tx_token_put(dev); + idr_init(&dev->mt76.token); + + mt76_wr(dev, MT_MCU_INT_EVENT, MT_MCU_INT_EVENT_DMA_INIT); + mt7996_wait_reset_state(dev, MT_MCU_CMD_RECOVERY_DONE); + } + + clear_bit(MT76_MCU_RESET, &dev->mphy.state); + clear_bit(MT76_RESET, &dev->mphy.state); + if (phy2) + clear_bit(MT76_RESET, &phy2->mt76->state); + if (phy3) + clear_bit(MT76_RESET, &phy3->mt76->state); + + local_bh_disable(); + mt76_for_each_q_rx(&dev->mt76, i) { + napi_enable(&dev->mt76.napi[i]); + napi_schedule(&dev->mt76.napi[i]); + } + local_bh_enable(); + + tasklet_schedule(&dev->irq_tasklet); + + mt76_wr(dev, MT_MCU_INT_EVENT, MT_MCU_INT_EVENT_RESET_DONE); + mt7996_wait_reset_state(dev, MT_MCU_CMD_NORMAL_STATE); + + mt76_worker_enable(&dev->mt76.tx_worker); + + local_bh_disable(); + napi_enable(&dev->mt76.tx_napi); + napi_schedule(&dev->mt76.tx_napi); + local_bh_enable(); + + ieee80211_wake_queues(mt76_hw(dev)); + if (phy2) + ieee80211_wake_queues(phy2->mt76->hw); + if (phy3) + ieee80211_wake_queues(phy3->mt76->hw); + + mutex_unlock(&dev->mt76.mutex); + + mt7996_update_beacons(dev); + + ieee80211_queue_delayed_work(mt76_hw(dev), &dev->mphy.mac_work, + MT7996_WATCHDOG_TIME); + if (phy2) + ieee80211_queue_delayed_work(phy2->mt76->hw, + &phy2->mt76->mac_work, + MT7996_WATCHDOG_TIME); + if (phy3) + ieee80211_queue_delayed_work(phy3->mt76->hw, + &phy3->mt76->mac_work, + MT7996_WATCHDOG_TIME); +} + +void mt7996_mac_update_stats(struct mt7996_phy *phy) +{ + struct mt7996_dev *dev = phy->dev; + struct mib_stats *mib = &phy->mib; + u8 band_idx = phy->mt76->band_idx; + u32 cnt; + int i; + + cnt = mt76_rr(dev, MT_MIB_RSCR1(band_idx)); + mib->fcs_err_cnt += cnt; + + cnt = mt76_rr(dev, MT_MIB_RSCR33(band_idx)); + mib->rx_fifo_full_cnt += cnt; + + cnt = mt76_rr(dev, MT_MIB_RSCR31(band_idx)); + mib->rx_mpdu_cnt += cnt; + + cnt = mt76_rr(dev, MT_MIB_SDR6(band_idx)); + mib->channel_idle_cnt += FIELD_GET(MT_MIB_SDR6_CHANNEL_IDL_CNT_MASK, cnt); + + cnt = mt76_rr(dev, MT_MIB_RVSR0(band_idx)); + mib->rx_vector_mismatch_cnt += cnt; + + cnt = mt76_rr(dev, MT_MIB_RSCR35(band_idx)); + mib->rx_delimiter_fail_cnt += cnt; + + cnt = mt76_rr(dev, MT_MIB_RSCR36(band_idx)); + mib->rx_len_mismatch_cnt += cnt; + + cnt = mt76_rr(dev, MT_MIB_TSCR0(band_idx)); + mib->tx_ampdu_cnt += cnt; + + cnt = mt76_rr(dev, MT_MIB_TSCR2(band_idx)); + mib->tx_stop_q_empty_cnt += cnt; + + cnt = mt76_rr(dev, MT_MIB_TSCR3(band_idx)); + mib->tx_mpdu_attempts_cnt += cnt; + + cnt = mt76_rr(dev, MT_MIB_TSCR4(band_idx)); + mib->tx_mpdu_success_cnt += cnt; + + cnt = mt76_rr(dev, MT_MIB_RSCR27(band_idx)); + mib->rx_ampdu_cnt += cnt; + + cnt = mt76_rr(dev, MT_MIB_RSCR28(band_idx)); + mib->rx_ampdu_bytes_cnt += cnt; + + cnt = mt76_rr(dev, MT_MIB_RSCR29(band_idx)); + mib->rx_ampdu_valid_subframe_cnt += cnt; + + cnt = mt76_rr(dev, MT_MIB_RSCR30(band_idx)); + mib->rx_ampdu_valid_subframe_bytes_cnt += cnt; + + cnt = mt76_rr(dev, MT_MIB_SDR27(band_idx)); + mib->tx_rwp_fail_cnt += FIELD_GET(MT_MIB_SDR27_TX_RWP_FAIL_CNT, cnt); + + cnt = mt76_rr(dev, MT_MIB_SDR28(band_idx)); + mib->tx_rwp_need_cnt += FIELD_GET(MT_MIB_SDR28_TX_RWP_NEED_CNT, cnt); + + cnt = mt76_rr(dev, MT_UMIB_RPDCR(band_idx)); + mib->rx_pfdrop_cnt += cnt; + + cnt = mt76_rr(dev, MT_MIB_RVSR1(band_idx)); + mib->rx_vec_queue_overflow_drop_cnt += cnt; + + cnt = mt76_rr(dev, MT_MIB_TSCR1(band_idx)); + mib->rx_ba_cnt += cnt; + + cnt = mt76_rr(dev, MT_MIB_BSCR0(band_idx)); + mib->tx_bf_ebf_ppdu_cnt += cnt; + + cnt = mt76_rr(dev, MT_MIB_BSCR1(band_idx)); + mib->tx_bf_ibf_ppdu_cnt += cnt; + + cnt = mt76_rr(dev, MT_MIB_BSCR2(band_idx)); + mib->tx_mu_bf_cnt += cnt; + + cnt = mt76_rr(dev, MT_MIB_TSCR5(band_idx)); + mib->tx_mu_mpdu_cnt += cnt; + + cnt = mt76_rr(dev, MT_MIB_TSCR6(band_idx)); + mib->tx_mu_acked_mpdu_cnt += cnt; + + cnt = mt76_rr(dev, MT_MIB_TSCR7(band_idx)); + mib->tx_su_acked_mpdu_cnt += cnt; + + cnt = mt76_rr(dev, MT_MIB_BSCR3(band_idx)); + mib->tx_bf_rx_fb_ht_cnt += cnt; + mib->tx_bf_rx_fb_all_cnt += cnt; + + cnt = mt76_rr(dev, MT_MIB_BSCR4(band_idx)); + mib->tx_bf_rx_fb_vht_cnt += cnt; + mib->tx_bf_rx_fb_all_cnt += cnt; + + cnt = mt76_rr(dev, MT_MIB_BSCR5(band_idx)); + mib->tx_bf_rx_fb_he_cnt += cnt; + mib->tx_bf_rx_fb_all_cnt += cnt; + + cnt = mt76_rr(dev, MT_MIB_BSCR6(band_idx)); + mib->tx_bf_rx_fb_eht_cnt += cnt; + mib->tx_bf_rx_fb_all_cnt += cnt; + + cnt = mt76_rr(dev, MT_ETBF_RX_FB_CONT(band_idx)); + mib->tx_bf_rx_fb_bw = FIELD_GET(MT_ETBF_RX_FB_BW, cnt); + mib->tx_bf_rx_fb_nc_cnt += FIELD_GET(MT_ETBF_RX_FB_NC, cnt); + mib->tx_bf_rx_fb_nr_cnt += FIELD_GET(MT_ETBF_RX_FB_NR, cnt); + + cnt = mt76_rr(dev, MT_MIB_BSCR7(band_idx)); + mib->tx_bf_fb_trig_cnt += cnt; + + cnt = mt76_rr(dev, MT_MIB_BSCR17(band_idx)); + mib->tx_bf_fb_cpl_cnt += cnt; + + for (i = 0; i < ARRAY_SIZE(mib->tx_amsdu); i++) { + cnt = mt76_rr(dev, MT_PLE_AMSDU_PACK_MSDU_CNT(i)); + mib->tx_amsdu[i] += cnt; + mib->tx_amsdu_cnt += cnt; + } + + /* rts count */ + cnt = mt76_rr(dev, MT_MIB_BTSCR5(band_idx)); + mib->rts_cnt += cnt; + + /* rts retry count */ + cnt = mt76_rr(dev, MT_MIB_BTSCR6(band_idx)); + mib->rts_retries_cnt += cnt; + + /* ba miss count */ + cnt = mt76_rr(dev, MT_MIB_BTSCR0(band_idx)); + mib->ba_miss_cnt += cnt; + + /* ack fail count */ + cnt = mt76_rr(dev, MT_MIB_BFTFCR(band_idx)); + mib->ack_fail_cnt += cnt; + + for (i = 0; i < 16; i++) { + cnt = mt76_rr(dev, MT_TX_AGG_CNT(band_idx, i)); + phy->mt76->aggr_stats[i] += cnt; + } +} + +void mt7996_mac_sta_rc_work(struct work_struct *work) +{ + struct mt7996_dev *dev = container_of(work, struct mt7996_dev, rc_work); + struct ieee80211_sta *sta; + struct ieee80211_vif *vif; + struct mt7996_sta *msta; + u32 changed; + LIST_HEAD(list); + + spin_lock_bh(&dev->sta_poll_lock); + list_splice_init(&dev->sta_rc_list, &list); + + while (!list_empty(&list)) { + msta = list_first_entry(&list, struct mt7996_sta, rc_list); + list_del_init(&msta->rc_list); + changed = msta->changed; + msta->changed = 0; + spin_unlock_bh(&dev->sta_poll_lock); + + sta = container_of((void *)msta, struct ieee80211_sta, drv_priv); + vif = container_of((void *)msta->vif, struct ieee80211_vif, drv_priv); + + if (changed & (IEEE80211_RC_SUPP_RATES_CHANGED | + IEEE80211_RC_NSS_CHANGED | + IEEE80211_RC_BW_CHANGED)) + mt7996_mcu_add_rate_ctrl(dev, vif, sta, true); + + /* TODO: smps change */ + + spin_lock_bh(&dev->sta_poll_lock); + } + + spin_unlock_bh(&dev->sta_poll_lock); +} + +void mt7996_mac_work(struct work_struct *work) +{ + struct mt7996_phy *phy; + struct mt76_phy *mphy; + + mphy = (struct mt76_phy *)container_of(work, struct mt76_phy, + mac_work.work); + phy = mphy->priv; + + mutex_lock(&mphy->dev->mutex); + + mt76_update_survey(mphy); + if (++mphy->mac_work_count == 5) { + mphy->mac_work_count = 0; + + mt7996_mac_update_stats(phy); + } + + mutex_unlock(&mphy->dev->mutex); + + mt76_tx_status_check(mphy->dev, false); + + ieee80211_queue_delayed_work(mphy->hw, &mphy->mac_work, + MT7996_WATCHDOG_TIME); +} + +static void mt7996_dfs_stop_radar_detector(struct mt7996_phy *phy) +{ + struct mt7996_dev *dev = phy->dev; + + if (phy->rdd_state & BIT(0)) + mt7996_mcu_rdd_cmd(dev, RDD_STOP, 0, + MT_RX_SEL0, 0); + if (phy->rdd_state & BIT(1)) + mt7996_mcu_rdd_cmd(dev, RDD_STOP, 1, + MT_RX_SEL0, 0); +} + +static int mt7996_dfs_start_rdd(struct mt7996_dev *dev, int chain) +{ + int err, region; + + switch (dev->mt76.region) { + case NL80211_DFS_ETSI: + region = 0; + break; + case NL80211_DFS_JP: + region = 2; + break; + case NL80211_DFS_FCC: + default: + region = 1; + break; + } + + err = mt7996_mcu_rdd_cmd(dev, RDD_START, chain, + MT_RX_SEL0, region); + if (err < 0) + return err; + + return mt7996_mcu_rdd_cmd(dev, RDD_DET_MODE, chain, + MT_RX_SEL0, 1); +} + +static int mt7996_dfs_start_radar_detector(struct mt7996_phy *phy) +{ + struct cfg80211_chan_def *chandef = &phy->mt76->chandef; + struct mt7996_dev *dev = phy->dev; + u8 band_idx = phy->mt76->band_idx; + int err; + + /* start CAC */ + err = mt7996_mcu_rdd_cmd(dev, RDD_CAC_START, band_idx, + MT_RX_SEL0, 0); + if (err < 0) + return err; + + err = mt7996_dfs_start_rdd(dev, band_idx); + if (err < 0) + return err; + + phy->rdd_state |= BIT(band_idx); + + if (chandef->width == NL80211_CHAN_WIDTH_160 || + chandef->width == NL80211_CHAN_WIDTH_80P80) { + err = mt7996_dfs_start_rdd(dev, 1); + if (err < 0) + return err; + + phy->rdd_state |= BIT(1); + } + + return 0; +} + +static int +mt7996_dfs_init_radar_specs(struct mt7996_phy *phy) +{ + const struct mt7996_dfs_radar_spec *radar_specs; + struct mt7996_dev *dev = phy->dev; + int err, i; + + switch (dev->mt76.region) { + case NL80211_DFS_FCC: + radar_specs = &fcc_radar_specs; + err = mt7996_mcu_set_fcc5_lpn(dev, 8); + if (err < 0) + return err; + break; + case NL80211_DFS_ETSI: + radar_specs = &etsi_radar_specs; + break; + case NL80211_DFS_JP: + radar_specs = &jp_radar_specs; + break; + default: + return -EINVAL; + } + + for (i = 0; i < ARRAY_SIZE(radar_specs->radar_pattern); i++) { + err = mt7996_mcu_set_radar_th(dev, i, + &radar_specs->radar_pattern[i]); + if (err < 0) + return err; + } + + return mt7996_mcu_set_pulse_th(dev, &radar_specs->pulse_th); +} + +int mt7996_dfs_init_radar_detector(struct mt7996_phy *phy) +{ + struct mt7996_dev *dev = phy->dev; + enum mt76_dfs_state dfs_state, prev_state; + int err; + + prev_state = phy->mt76->dfs_state; + dfs_state = mt76_phy_dfs_state(phy->mt76); + + if (prev_state == dfs_state) + return 0; + + if (prev_state == MT_DFS_STATE_UNKNOWN) + mt7996_dfs_stop_radar_detector(phy); + + if (dfs_state == MT_DFS_STATE_DISABLED) + goto stop; + + if (prev_state <= MT_DFS_STATE_DISABLED) { + err = mt7996_dfs_init_radar_specs(phy); + if (err < 0) + return err; + + err = mt7996_dfs_start_radar_detector(phy); + if (err < 0) + return err; + + phy->mt76->dfs_state = MT_DFS_STATE_CAC; + } + + if (dfs_state == MT_DFS_STATE_CAC) + return 0; + + err = mt7996_mcu_rdd_cmd(dev, RDD_CAC_END, + phy->mt76->band_idx, MT_RX_SEL0, 0); + if (err < 0) { + phy->mt76->dfs_state = MT_DFS_STATE_UNKNOWN; + return err; + } + + phy->mt76->dfs_state = MT_DFS_STATE_ACTIVE; + return 0; + +stop: + err = mt7996_mcu_rdd_cmd(dev, RDD_NORMAL_START, + phy->mt76->band_idx, MT_RX_SEL0, 0); + if (err < 0) + return err; + + mt7996_dfs_stop_radar_detector(phy); + phy->mt76->dfs_state = MT_DFS_STATE_DISABLED; + + return 0; +} + +static int +mt7996_mac_twt_duration_align(int duration) +{ + return duration << 8; +} + +static u64 +mt7996_mac_twt_sched_list_add(struct mt7996_dev *dev, + struct mt7996_twt_flow *flow) +{ + struct mt7996_twt_flow *iter, *iter_next; + u32 duration = flow->duration << 8; + u64 start_tsf; + + iter = list_first_entry_or_null(&dev->twt_list, + struct mt7996_twt_flow, list); + if (!iter || !iter->sched || iter->start_tsf > duration) { + /* add flow as first entry in the list */ + list_add(&flow->list, &dev->twt_list); + return 0; + } + + list_for_each_entry_safe(iter, iter_next, &dev->twt_list, list) { + start_tsf = iter->start_tsf + + mt7996_mac_twt_duration_align(iter->duration); + if (list_is_last(&iter->list, &dev->twt_list)) + break; + + if (!iter_next->sched || + iter_next->start_tsf > start_tsf + duration) { + list_add(&flow->list, &iter->list); + goto out; + } + } + + /* add flow as last entry in the list */ + list_add_tail(&flow->list, &dev->twt_list); +out: + return start_tsf; +} + +static int mt7996_mac_check_twt_req(struct ieee80211_twt_setup *twt) +{ + struct ieee80211_twt_params *twt_agrt; + u64 interval, duration; + u16 mantissa; + u8 exp; + + /* only individual agreement supported */ + if (twt->control & IEEE80211_TWT_CONTROL_NEG_TYPE_BROADCAST) + return -EOPNOTSUPP; + + /* only 256us unit supported */ + if (twt->control & IEEE80211_TWT_CONTROL_WAKE_DUR_UNIT) + return -EOPNOTSUPP; + + twt_agrt = (struct ieee80211_twt_params *)twt->params; + + /* explicit agreement not supported */ + if (!(twt_agrt->req_type & cpu_to_le16(IEEE80211_TWT_REQTYPE_IMPLICIT))) + return -EOPNOTSUPP; + + exp = FIELD_GET(IEEE80211_TWT_REQTYPE_WAKE_INT_EXP, + le16_to_cpu(twt_agrt->req_type)); + mantissa = le16_to_cpu(twt_agrt->mantissa); + duration = twt_agrt->min_twt_dur << 8; + + interval = (u64)mantissa << exp; + if (interval < duration) + return -EOPNOTSUPP; + + return 0; +} + +void mt7996_mac_add_twt_setup(struct ieee80211_hw *hw, + struct ieee80211_sta *sta, + struct ieee80211_twt_setup *twt) +{ + enum ieee80211_twt_setup_cmd setup_cmd = TWT_SETUP_CMD_REJECT; + struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv; + struct ieee80211_twt_params *twt_agrt = (void *)twt->params; + u16 req_type = le16_to_cpu(twt_agrt->req_type); + enum ieee80211_twt_setup_cmd sta_setup_cmd; + struct mt7996_dev *dev = mt7996_hw_dev(hw); + struct mt7996_twt_flow *flow; + int flowid, table_id; + u8 exp; + + if (mt7996_mac_check_twt_req(twt)) + goto out; + + mutex_lock(&dev->mt76.mutex); + + if (dev->twt.n_agrt == MT7996_MAX_TWT_AGRT) + goto unlock; + + if (hweight8(msta->twt.flowid_mask) == ARRAY_SIZE(msta->twt.flow)) + goto unlock; + + flowid = ffs(~msta->twt.flowid_mask) - 1; + le16p_replace_bits(&twt_agrt->req_type, flowid, + IEEE80211_TWT_REQTYPE_FLOWID); + + table_id = ffs(~dev->twt.table_mask) - 1; + exp = FIELD_GET(IEEE80211_TWT_REQTYPE_WAKE_INT_EXP, req_type); + sta_setup_cmd = FIELD_GET(IEEE80211_TWT_REQTYPE_SETUP_CMD, req_type); + + flow = &msta->twt.flow[flowid]; + memset(flow, 0, sizeof(*flow)); + INIT_LIST_HEAD(&flow->list); + flow->wcid = msta->wcid.idx; + flow->table_id = table_id; + flow->id = flowid; + flow->duration = twt_agrt->min_twt_dur; + flow->mantissa = twt_agrt->mantissa; + flow->exp = exp; + flow->protection = !!(req_type & IEEE80211_TWT_REQTYPE_PROTECTION); + flow->flowtype = !!(req_type & IEEE80211_TWT_REQTYPE_FLOWTYPE); + flow->trigger = !!(req_type & IEEE80211_TWT_REQTYPE_TRIGGER); + + if (sta_setup_cmd == TWT_SETUP_CMD_REQUEST || + sta_setup_cmd == TWT_SETUP_CMD_SUGGEST) { + u64 interval = (u64)le16_to_cpu(twt_agrt->mantissa) << exp; + u64 flow_tsf, curr_tsf; + u32 rem; + + flow->sched = true; + flow->start_tsf = mt7996_mac_twt_sched_list_add(dev, flow); + curr_tsf = __mt7996_get_tsf(hw, msta->vif); + div_u64_rem(curr_tsf - flow->start_tsf, interval, &rem); + flow_tsf = curr_tsf + interval - rem; + twt_agrt->twt = cpu_to_le64(flow_tsf); + } else { + list_add_tail(&flow->list, &dev->twt_list); + } + flow->tsf = le64_to_cpu(twt_agrt->twt); + + if (mt7996_mcu_twt_agrt_update(dev, msta->vif, flow, MCU_TWT_AGRT_ADD)) + goto unlock; + + setup_cmd = TWT_SETUP_CMD_ACCEPT; + dev->twt.table_mask |= BIT(table_id); + msta->twt.flowid_mask |= BIT(flowid); + dev->twt.n_agrt++; + +unlock: + mutex_unlock(&dev->mt76.mutex); +out: + le16p_replace_bits(&twt_agrt->req_type, setup_cmd, + IEEE80211_TWT_REQTYPE_SETUP_CMD); + twt->control = (twt->control & IEEE80211_TWT_CONTROL_WAKE_DUR_UNIT) | + (twt->control & IEEE80211_TWT_CONTROL_RX_DISABLED); +} + +void mt7996_mac_twt_teardown_flow(struct mt7996_dev *dev, + struct mt7996_sta *msta, + u8 flowid) +{ + struct mt7996_twt_flow *flow; + + lockdep_assert_held(&dev->mt76.mutex); + + if (flowid >= ARRAY_SIZE(msta->twt.flow)) + return; + + if (!(msta->twt.flowid_mask & BIT(flowid))) + return; + + flow = &msta->twt.flow[flowid]; + if (mt7996_mcu_twt_agrt_update(dev, msta->vif, flow, + MCU_TWT_AGRT_DELETE)) + return; + + list_del_init(&flow->list); + msta->twt.flowid_mask &= ~BIT(flowid); + dev->twt.table_mask &= ~BIT(flow->table_id); + dev->twt.n_agrt--; +} diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mac.h b/drivers/net/wireless/mediatek/mt76/mt7996/mac.h new file mode 100644 index 000000000000..9f68852012b9 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mac.h @@ -0,0 +1,398 @@ +/* SPDX-License-Identifier: ISC */ +/* + * Copyright (C) 2022 MediaTek Inc. + */ + +#ifndef __MT7996_MAC_H +#define __MT7996_MAC_H + +#define MT_CT_PARSE_LEN 72 +#define MT_CT_DMA_BUF_NUM 2 + +#define MT_RXD0_LENGTH GENMASK(15, 0) +#define MT_RXD0_PKT_TYPE GENMASK(31, 27) + +#define MT_RXD0_NORMAL_ETH_TYPE_OFS GENMASK(22, 16) +#define MT_RXD0_NORMAL_IP_SUM BIT(23) +#define MT_RXD0_NORMAL_UDP_TCP_SUM BIT(24) + +#define MT_RXD0_SW_PKT_TYPE_MASK GENMASK(31, 16) +#define MT_RXD0_SW_PKT_TYPE_MAP 0x380F +#define MT_RXD0_SW_PKT_TYPE_FRAME 0x3801 + +enum rx_pkt_type { + PKT_TYPE_TXS, + PKT_TYPE_TXRXV, + PKT_TYPE_NORMAL, + PKT_TYPE_RX_DUP_RFB, + PKT_TYPE_RX_TMR, + PKT_TYPE_RETRIEVE, + PKT_TYPE_TXRX_NOTIFY, + PKT_TYPE_RX_EVENT, + PKT_TYPE_RX_FW_MONITOR = 0x0c, +}; + +/* RXD DW1 */ +#define MT_RXD1_NORMAL_WLAN_IDX GENMASK(11, 0) +#define MT_RXD1_NORMAL_GROUP_1 BIT(16) +#define MT_RXD1_NORMAL_GROUP_2 BIT(17) +#define MT_RXD1_NORMAL_GROUP_3 BIT(18) +#define MT_RXD1_NORMAL_GROUP_4 BIT(19) +#define MT_RXD1_NORMAL_GROUP_5 BIT(20) +#define MT_RXD1_NORMAL_KEY_ID GENMASK(22, 21) +#define MT_RXD1_NORMAL_CM BIT(23) +#define MT_RXD1_NORMAL_CLM BIT(24) +#define MT_RXD1_NORMAL_ICV_ERR BIT(25) +#define MT_RXD1_NORMAL_TKIP_MIC_ERR BIT(26) +#define MT_RXD1_NORMAL_BAND_IDX GENMASK(28, 27) +#define MT_RXD1_NORMAL_SPP_EN BIT(29) +#define MT_RXD1_NORMAL_ADD_OM BIT(30) +#define MT_RXD1_NORMAL_SEC_DONE BIT(31) + +/* RXD DW2 */ +#define MT_RXD2_NORMAL_BSSID GENMASK(5, 0) +#define MT_RXD2_NORMAL_MAC_HDR_LEN GENMASK(12, 8) +#define MT_RXD2_NORMAL_HDR_TRANS BIT(7) +#define MT_RXD2_NORMAL_HDR_OFFSET GENMASK(15, 13) +#define MT_RXD2_NORMAL_SEC_MODE GENMASK(20, 16) +#define MT_RXD2_NORMAL_MU_BAR BIT(21) +#define MT_RXD2_NORMAL_SW_BIT BIT(22) +#define MT_RXD2_NORMAL_AMSDU_ERR BIT(23) +#define MT_RXD2_NORMAL_MAX_LEN_ERROR BIT(24) +#define MT_RXD2_NORMAL_HDR_TRANS_ERROR BIT(25) +#define MT_RXD2_NORMAL_INT_FRAME BIT(26) +#define MT_RXD2_NORMAL_FRAG BIT(27) +#define MT_RXD2_NORMAL_NULL_FRAME BIT(28) +#define MT_RXD2_NORMAL_NDATA BIT(29) +#define MT_RXD2_NORMAL_NON_AMPDU BIT(30) +#define MT_RXD2_NORMAL_BF_REPORT BIT(31) + +/* RXD DW3 */ +#define MT_RXD3_NORMAL_RXV_SEQ GENMASK(7, 0) +#define MT_RXD3_NORMAL_CH_FREQ GENMASK(15, 8) +#define MT_RXD3_NORMAL_ADDR_TYPE GENMASK(17, 16) +#define MT_RXD3_NORMAL_U2M BIT(0) +#define MT_RXD3_NORMAL_HTC_VLD BIT(18) +#define MT_RXD3_NORMAL_BEACON_MC BIT(20) +#define MT_RXD3_NORMAL_BEACON_UC BIT(21) +#define MT_RXD3_NORMAL_CO_ANT BIT(22) +#define MT_RXD3_NORMAL_FCS_ERR BIT(24) +#define MT_RXD3_NORMAL_VLAN2ETH BIT(31) + +/* RXD DW4 */ +#define MT_RXD4_NORMAL_PAYLOAD_FORMAT GENMASK(1, 0) +#define MT_RXD4_FIRST_AMSDU_FRAME GENMASK(1, 0) +#define MT_RXD4_MID_AMSDU_FRAME BIT(1) +#define MT_RXD4_LAST_AMSDU_FRAME BIT(0) + +#define MT_RXV_HDR_BAND_IDX BIT(24) + +/* RXD GROUP4 */ +#define MT_RXD8_FRAME_CONTROL GENMASK(15, 0) + +#define MT_RXD10_SEQ_CTRL GENMASK(15, 0) +#define MT_RXD10_QOS_CTL GENMASK(31, 16) + +#define MT_RXD11_HT_CONTROL GENMASK(31, 0) + +/* P-RXV */ +#define MT_PRXV_TX_RATE GENMASK(6, 0) +#define MT_PRXV_TX_DCM BIT(4) +#define MT_PRXV_TX_ER_SU_106T BIT(5) +#define MT_PRXV_NSTS GENMASK(10, 7) +#define MT_PRXV_TXBF BIT(11) +#define MT_PRXV_HT_AD_CODE BIT(12) +#define MT_PRXV_HE_RU_ALLOC_L GENMASK(31, 28) +#define MT_PRXV_HE_RU_ALLOC_H GENMASK(3, 0) +#define MT_PRXV_RCPI3 GENMASK(31, 24) +#define MT_PRXV_RCPI2 GENMASK(23, 16) +#define MT_PRXV_RCPI1 GENMASK(15, 8) +#define MT_PRXV_RCPI0 GENMASK(7, 0) +#define MT_PRXV_HT_SHORT_GI GENMASK(4, 3) +#define MT_PRXV_HT_STBC GENMASK(10, 9) +#define MT_PRXV_TX_MODE GENMASK(14, 11) +#define MT_PRXV_FRAME_MODE GENMASK(2, 0) +#define MT_PRXV_DCM BIT(5) +#define MT_PRXV_NUM_RX BIT(8, 6) + +/* C-RXV */ +#define MT_CRXV_HT_STBC GENMASK(1, 0) +#define MT_CRXV_TX_MODE GENMASK(7, 4) +#define MT_CRXV_FRAME_MODE GENMASK(10, 8) +#define MT_CRXV_HT_SHORT_GI GENMASK(14, 13) +#define MT_CRXV_HE_LTF_SIZE GENMASK(18, 17) +#define MT_CRXV_HE_LDPC_EXT_SYM BIT(20) +#define MT_CRXV_HE_PE_DISAMBIG BIT(23) +#define MT_CRXV_HE_NUM_USER GENMASK(30, 24) +#define MT_CRXV_HE_UPLINK BIT(31) +#define MT_CRXV_HE_RU0 GENMASK(7, 0) +#define MT_CRXV_HE_RU1 GENMASK(15, 8) +#define MT_CRXV_HE_RU2 GENMASK(23, 16) +#define MT_CRXV_HE_RU3 GENMASK(31, 24) + +#define MT_CRXV_HE_MU_AID GENMASK(30, 20) + +#define MT_CRXV_HE_SR_MASK GENMASK(11, 8) +#define MT_CRXV_HE_SR1_MASK GENMASK(16, 12) +#define MT_CRXV_HE_SR2_MASK GENMASK(20, 17) +#define MT_CRXV_HE_SR3_MASK GENMASK(24, 21) + +#define MT_CRXV_HE_BSS_COLOR GENMASK(5, 0) +#define MT_CRXV_HE_TXOP_DUR GENMASK(12, 6) +#define MT_CRXV_HE_BEAM_CHNG BIT(13) +#define MT_CRXV_HE_DOPPLER BIT(16) + +enum tx_header_format { + MT_HDR_FORMAT_802_3, + MT_HDR_FORMAT_CMD, + MT_HDR_FORMAT_802_11, + MT_HDR_FORMAT_802_11_EXT, +}; + +enum tx_pkt_type { + MT_TX_TYPE_CT, + MT_TX_TYPE_SF, + MT_TX_TYPE_CMD, + MT_TX_TYPE_FW, +}; + +enum tx_port_idx { + MT_TX_PORT_IDX_LMAC, + MT_TX_PORT_IDX_MCU +}; + +enum tx_mcu_port_q_idx { + MT_TX_MCU_PORT_RX_Q0 = 0x20, + MT_TX_MCU_PORT_RX_Q1, + MT_TX_MCU_PORT_RX_Q2, + MT_TX_MCU_PORT_RX_Q3, + MT_TX_MCU_PORT_RX_FWDL = 0x3e +}; + +enum tx_mgnt_type { + MT_TX_NORMAL, + MT_TX_TIMING, + MT_TX_ADDBA, +}; + +#define MT_CT_INFO_APPLY_TXD BIT(0) +#define MT_CT_INFO_COPY_HOST_TXD_ALL BIT(1) +#define MT_CT_INFO_MGMT_FRAME BIT(2) +#define MT_CT_INFO_NONE_CIPHER_FRAME BIT(3) +#define MT_CT_INFO_HSR2_TX BIT(4) +#define MT_CT_INFO_FROM_HOST BIT(7) + +#define MT_TXD_SIZE (8 * 4) + +#define MT_TXD0_Q_IDX GENMASK(31, 25) +#define MT_TXD0_PKT_FMT GENMASK(24, 23) +#define MT_TXD0_ETH_TYPE_OFFSET GENMASK(22, 16) +#define MT_TXD0_TX_BYTES GENMASK(15, 0) + +#define MT_TXD1_FIXED_RATE BIT(31) +#define MT_TXD1_OWN_MAC GENMASK(30, 25) +#define MT_TXD1_TID GENMASK(24, 21) +#define MT_TXD1_BIP BIT(24) +#define MT_TXD1_ETH_802_3 BIT(20) +#define MT_TXD1_HDR_INFO GENMASK(20, 16) +#define MT_TXD1_HDR_FORMAT GENMASK(15, 14) +#define MT_TXD1_TGID GENMASK(13, 12) +#define MT_TXD1_WLAN_IDX GENMASK(11, 0) + +#define MT_TXD2_POWER_OFFSET GENMASK(31, 26) +#define MT_TXD2_MAX_TX_TIME GENMASK(25, 16) +#define MT_TXD2_FRAG GENMASK(15, 14) +#define MT_TXD2_HTC_VLD BIT(13) +#define MT_TXD2_DURATION BIT(12) +#define MT_TXD2_HDR_PAD GENMASK(11, 10) +#define MT_TXD2_RTS BIT(9) +#define MT_TXD2_OWN_MAC_MAP BIT(8) +#define MT_TXD2_BF_TYPE GENMASK(6, 7) +#define MT_TXD2_FRAME_TYPE GENMASK(5, 4) +#define MT_TXD2_SUB_TYPE GENMASK(3, 0) + +#define MT_TXD3_SN_VALID BIT(31) +#define MT_TXD3_PN_VALID BIT(30) +#define MT_TXD3_SW_POWER_MGMT BIT(29) +#define MT_TXD3_BA_DISABLE BIT(28) +#define MT_TXD3_SEQ GENMASK(27, 16) +#define MT_TXD3_REM_TX_COUNT GENMASK(15, 11) +#define MT_TXD3_TX_COUNT GENMASK(10, 6) +#define MT_TXD3_HW_AMSDU BIT(5) +#define MT_TXD3_BCM BIT(4) +#define MT_TXD3_EEOSP BIT(3) +#define MT_TXD3_EMRD BIT(2) +#define MT_TXD3_PROTECT_FRAME BIT(1) +#define MT_TXD3_NO_ACK BIT(0) + +#define MT_TXD4_PN_LOW GENMASK(31, 0) + +#define MT_TXD5_PN_HIGH GENMASK(31, 16) +#define MT_TXD5_FL BIT(15) +#define MT_TXD5_BYPASS_TBB BIT(14) +#define MT_TXD5_BYPASS_RBB BIT(13) +#define MT_TXD5_BSS_COLOR_ZERO BIT(12) +#define MT_TXD5_TX_STATUS_HOST BIT(10) +#define MT_TXD5_TX_STATUS_MCU BIT(9) +#define MT_TXD5_TX_STATUS_FMT BIT(8) +#define MT_TXD5_PID GENMASK(7, 0) + +#define MT_TXD6_TX_SRC GENMASK(31, 30) +#define MT_TXD6_VTA BIT(28) +#define MT_TXD6_FIXED_BW BIT(25) +#define MT_TXD6_BW GENMASK(24, 22) +#define MT_TXD6_TX_RATE GENMASK(21, 16) +#define MT_TXD6_TIMESTAMP_OFS_EN BIT(15) +#define MT_TXD6_TIMESTAMP_OFS_IDX GENMASK(14, 10) +#define MT_TXD6_MSDU_CNT GENMASK(9, 4) +#define MT_TXD6_SPE_ID_IDX BIT(10) +#define MT_TXD6_ANT_ID GENMASK(7, 4) +#define MT_TXD6_DIS_MAT BIT(3) +#define MT_TXD6_DAS BIT(2) +#define MT_TXD6_AMSDU_CAP BIT(1) + +#define MT_TXD7_TXD_LEN GENMASK(31, 30) +#define MT_TXD7_IP_SUM BIT(29) +#define MT_TXD7_DROP_BY_SDO BIT(28) +#define MT_TXD7_MAC_TXD BIT(27) +#define MT_TXD7_CTXD BIT(26) +#define MT_TXD7_CTXD_CNT GENMASK(25, 22) +#define MT_TXD7_UDP_TCP_SUM BIT(15) +#define MT_TXD7_TX_TIME GENMASK(9, 0) + +#define MT_TX_RATE_STBC BIT(13) +#define MT_TX_RATE_NSS GENMASK(13, 10) +#define MT_TX_RATE_MODE GENMASK(9, 6) +#define MT_TX_RATE_SU_EXT_TONE BIT(5) +#define MT_TX_RATE_DCM BIT(4) +/* VHT/HE only use bits 0-3 */ +#define MT_TX_RATE_IDX GENMASK(5, 0) + +struct mt7996_txp { + __le16 flags; + __le16 token; + u8 bss_idx; + __le16 rept_wds_wcid; + u8 nbuf; +#define MT_TXP_MAX_BUF_NUM 6 + __le32 buf[MT_TXP_MAX_BUF_NUM]; + __le16 len[MT_TXP_MAX_BUF_NUM]; +} __packed __aligned(4); + +#define MT_TXFREE0_PKT_TYPE GENMASK(31, 27) +#define MT_TXFREE0_MSDU_CNT GENMASK(25, 16) +#define MT_TXFREE0_RX_BYTE GENMASK(15, 0) + +#define MT_TXFREE1_VER GENMASK(18, 16) + +#define MT_TXFREE_INFO_PAIR BIT(31) +#define MT_TXFREE_INFO_HEADER BIT(30) +#define MT_TXFREE_INFO_WLAN_ID GENMASK(23, 12) +#define MT_TXFREE_INFO_MSDU_ID GENMASK(14, 0) + +#define MT_TXS0_BW GENMASK(31, 29) +#define MT_TXS0_TID GENMASK(28, 26) +#define MT_TXS0_AMPDU BIT(25) +#define MT_TXS0_TXS_FORMAT GENMASK(24, 23) +#define MT_TXS0_BA_ERROR BIT(22) +#define MT_TXS0_PS_FLAG BIT(21) +#define MT_TXS0_TXOP_TIMEOUT BIT(20) +#define MT_TXS0_BIP_ERROR BIT(19) + +#define MT_TXS0_QUEUE_TIMEOUT BIT(18) +#define MT_TXS0_RTS_TIMEOUT BIT(17) +#define MT_TXS0_ACK_TIMEOUT BIT(16) +#define MT_TXS0_ACK_ERROR_MASK GENMASK(18, 16) + +#define MT_TXS0_TX_STATUS_HOST BIT(15) +#define MT_TXS0_TX_STATUS_MCU BIT(14) +#define MT_TXS0_TX_RATE GENMASK(13, 0) + +#define MT_TXS1_SEQNO GENMASK(31, 20) +#define MT_TXS1_RESP_RATE GENMASK(19, 16) +#define MT_TXS1_RXV_SEQNO GENMASK(15, 8) +#define MT_TXS1_TX_POWER_DBM GENMASK(7, 0) + +#define MT_TXS2_BF_STATUS GENMASK(31, 30) +#define MT_TXS2_BAND GENMASK(29, 28) +#define MT_TXS2_WCID GENMASK(27, 16) +#define MT_TXS2_TX_DELAY GENMASK(15, 0) + +#define MT_TXS3_PID GENMASK(31, 24) +#define MT_TXS3_RATE_STBC BIT(7) +#define MT_TXS3_FIXED_RATE BIT(6) +#define MT_TXS3_SRC GENMASK(5, 4) +#define MT_TXS3_SHARED_ANTENNA BIT(3) +#define MT_TXS3_LAST_TX_RATE GENMASK(2, 0) + +#define MT_TXS4_TIMESTAMP GENMASK(31, 0) + +#define MT_TXS5_F0_FINAL_MPDU BIT(31) +#define MT_TXS5_F0_QOS BIT(30) +#define MT_TXS5_F0_TX_COUNT GENMASK(29, 25) +#define MT_TXS5_F0_FRONT_TIME GENMASK(24, 0) +#define MT_TXS5_F1_MPDU_TX_COUNT GENMASK(31, 24) +#define MT_TXS5_F1_MPDU_TX_BYTES GENMASK(23, 0) + +#define MT_TXS6_F0_NOISE_3 GENMASK(31, 24) +#define MT_TXS6_F0_NOISE_2 GENMASK(23, 16) +#define MT_TXS6_F0_NOISE_1 GENMASK(15, 8) +#define MT_TXS6_F0_NOISE_0 GENMASK(7, 0) +#define MT_TXS6_F1_MPDU_FAIL_COUNT GENMASK(31, 24) +#define MT_TXS6_F1_MPDU_FAIL_BYTES GENMASK(23, 0) + +#define MT_TXS7_F0_RCPI_3 GENMASK(31, 24) +#define MT_TXS7_F0_RCPI_2 GENMASK(23, 16) +#define MT_TXS7_F0_RCPI_1 GENMASK(15, 8) +#define MT_TXS7_F0_RCPI_0 GENMASK(7, 0) +#define MT_TXS7_F1_MPDU_RETRY_COUNT GENMASK(31, 24) +#define MT_TXS7_F1_MPDU_RETRY_BYTES GENMASK(23, 0) + +struct mt7996_dfs_pulse { + u32 max_width; /* us */ + int max_pwr; /* dbm */ + int min_pwr; /* dbm */ + u32 min_stgr_pri; /* us */ + u32 max_stgr_pri; /* us */ + u32 min_cr_pri; /* us */ + u32 max_cr_pri; /* us */ +}; + +struct mt7996_dfs_pattern { + u8 enb; + u8 stgr; + u8 min_crpn; + u8 max_crpn; + u8 min_crpr; + u8 min_pw; + u32 min_pri; + u32 max_pri; + u8 max_pw; + u8 min_crbn; + u8 max_crbn; + u8 min_stgpn; + u8 max_stgpn; + u8 min_stgpr; + u8 rsv[2]; + u32 min_stgpr_diff; +} __packed; + +struct mt7996_dfs_radar_spec { + struct mt7996_dfs_pulse pulse_th; + struct mt7996_dfs_pattern radar_pattern[16]; +}; + +static inline struct mt7996_txp * +mt7996_txwi_to_txp(struct mt76_dev *dev, struct mt76_txwi_cache *t) +{ + u8 *txwi; + + if (!t) + return NULL; + + txwi = mt76_get_txwi_ptr(dev, t); + + return (struct mt7996_txp *)(txwi + MT_TXD_SIZE); +} + +#endif diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/main.c b/drivers/net/wireless/mediatek/mt76/mt7996/main.c new file mode 100644 index 000000000000..21dea3fa7dc1 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7996/main.c @@ -0,0 +1,1325 @@ +// SPDX-License-Identifier: ISC +/* + * Copyright (C) 2022 MediaTek Inc. + */ + +#include "mt7996.h" +#include "mcu.h" + +static bool mt7996_dev_running(struct mt7996_dev *dev) +{ + struct mt7996_phy *phy; + + if (test_bit(MT76_STATE_RUNNING, &dev->mphy.state)) + return true; + + phy = mt7996_phy2(dev); + if (phy && test_bit(MT76_STATE_RUNNING, &phy->mt76->state)) + return true; + + phy = mt7996_phy3(dev); + + return phy && test_bit(MT76_STATE_RUNNING, &phy->mt76->state); +} + +static int mt7996_start(struct ieee80211_hw *hw) +{ + struct mt7996_dev *dev = mt7996_hw_dev(hw); + struct mt7996_phy *phy = mt7996_hw_phy(hw); + bool running; + int ret; + + flush_work(&dev->init_work); + + mutex_lock(&dev->mt76.mutex); + + running = mt7996_dev_running(dev); + if (!running) { + ret = mt7996_mcu_set_hdr_trans(dev, true); + if (ret) + goto out; + } + + mt7996_mac_enable_nf(dev, phy->mt76->band_idx); + + ret = mt7996_mcu_set_rts_thresh(phy, 0x92b); + if (ret) + goto out; + + ret = mt7996_mcu_set_chan_info(phy, UNI_CHANNEL_RX_PATH); + if (ret) + goto out; + + set_bit(MT76_STATE_RUNNING, &phy->mt76->state); + + ieee80211_iterate_interfaces(dev->mt76.hw, + IEEE80211_IFACE_ITER_RESUME_ALL, + mt7996_mcu_set_pm, dev->mt76.hw); + + ieee80211_queue_delayed_work(hw, &phy->mt76->mac_work, + MT7996_WATCHDOG_TIME); + + if (!running) + mt7996_mac_reset_counters(phy); + +out: + mutex_unlock(&dev->mt76.mutex); + + return ret; +} + +static void mt7996_stop(struct ieee80211_hw *hw) +{ + struct mt7996_dev *dev = mt7996_hw_dev(hw); + struct mt7996_phy *phy = mt7996_hw_phy(hw); + + cancel_delayed_work_sync(&phy->mt76->mac_work); + + mutex_lock(&dev->mt76.mutex); + + clear_bit(MT76_STATE_RUNNING, &phy->mt76->state); + + ieee80211_iterate_interfaces(dev->mt76.hw, + IEEE80211_IFACE_ITER_RESUME_ALL, + mt7996_mcu_set_pm, dev->mt76.hw); + + mutex_unlock(&dev->mt76.mutex); +} + +static inline int get_free_idx(u32 mask, u8 start, u8 end) +{ + return ffs(~mask & GENMASK(end, start)); +} + +static int get_omac_idx(enum nl80211_iftype type, u64 mask) +{ + int i; + + switch (type) { + case NL80211_IFTYPE_MESH_POINT: + case NL80211_IFTYPE_ADHOC: + case NL80211_IFTYPE_STATION: + /* prefer hw bssid slot 1-3 */ + i = get_free_idx(mask, HW_BSSID_1, HW_BSSID_3); + if (i) + return i - 1; + + if (type != NL80211_IFTYPE_STATION) + break; + + i = get_free_idx(mask, EXT_BSSID_1, EXT_BSSID_MAX); + if (i) + return i - 1; + + if (~mask & BIT(HW_BSSID_0)) + return HW_BSSID_0; + + break; + case NL80211_IFTYPE_MONITOR: + case NL80211_IFTYPE_AP: + /* ap uses hw bssid 0 and ext bssid */ + if (~mask & BIT(HW_BSSID_0)) + return HW_BSSID_0; + + i = get_free_idx(mask, EXT_BSSID_1, EXT_BSSID_MAX); + if (i) + return i - 1; + + break; + default: + WARN_ON(1); + break; + } + + return -1; +} + +static void mt7996_init_bitrate_mask(struct ieee80211_vif *vif) +{ + struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv; + int i; + + for (i = 0; i < ARRAY_SIZE(mvif->bitrate_mask.control); i++) { + mvif->bitrate_mask.control[i].gi = NL80211_TXRATE_DEFAULT_GI; + mvif->bitrate_mask.control[i].he_gi = 0xff; + mvif->bitrate_mask.control[i].he_ltf = 0xff; + mvif->bitrate_mask.control[i].legacy = GENMASK(31, 0); + memset(mvif->bitrate_mask.control[i].ht_mcs, 0xff, + sizeof(mvif->bitrate_mask.control[i].ht_mcs)); + memset(mvif->bitrate_mask.control[i].vht_mcs, 0xff, + sizeof(mvif->bitrate_mask.control[i].vht_mcs)); + memset(mvif->bitrate_mask.control[i].he_mcs, 0xff, + sizeof(mvif->bitrate_mask.control[i].he_mcs)); + } +} + +static int mt7996_add_interface(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) +{ + struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv; + struct mt7996_dev *dev = mt7996_hw_dev(hw); + struct mt7996_phy *phy = mt7996_hw_phy(hw); + struct mt76_txq *mtxq; + u8 band_idx = phy->mt76->band_idx; + int idx, ret = 0; + + mutex_lock(&dev->mt76.mutex); + + if (vif->type == NL80211_IFTYPE_MONITOR && + is_zero_ether_addr(vif->addr)) + phy->monitor_vif = vif; + + mvif->mt76.idx = __ffs64(~dev->mt76.vif_mask); + if (mvif->mt76.idx >= (MT7996_MAX_INTERFACES << dev->dbdc_support)) { + ret = -ENOSPC; + goto out; + } + + idx = get_omac_idx(vif->type, phy->omac_mask); + if (idx < 0) { + ret = -ENOSPC; + goto out; + } + mvif->mt76.omac_idx = idx; + mvif->phy = phy; + mvif->mt76.band_idx = band_idx; + mvif->mt76.wmm_idx = band_idx; + + ret = mt7996_mcu_add_dev_info(phy, vif, true); + if (ret) + goto out; + + ret = mt7996_mcu_set_radio_en(phy, true); + if (ret) + goto out; + + dev->mt76.vif_mask |= BIT_ULL(mvif->mt76.idx); + phy->omac_mask |= BIT_ULL(mvif->mt76.omac_idx); + + idx = MT7996_WTBL_RESERVED - mvif->mt76.idx; + + INIT_LIST_HEAD(&mvif->sta.rc_list); + INIT_LIST_HEAD(&mvif->sta.poll_list); + mvif->sta.wcid.idx = idx; + mvif->sta.wcid.phy_idx = band_idx; + mvif->sta.wcid.hw_key_idx = -1; + mvif->sta.wcid.tx_info |= MT_WCID_TX_INFO_SET; + mt76_packet_id_init(&mvif->sta.wcid); + + mt7996_mac_wtbl_update(dev, idx, + MT_WTBL_UPDATE_ADM_COUNT_CLEAR); + + if (vif->txq) { + mtxq = (struct mt76_txq *)vif->txq->drv_priv; + mtxq->wcid = idx; + } + + if (vif->type != NL80211_IFTYPE_AP && + (!mvif->mt76.omac_idx || mvif->mt76.omac_idx > 3)) + vif->offload_flags = 0; + vif->offload_flags |= IEEE80211_OFFLOAD_ENCAP_4ADDR; + + mt7996_init_bitrate_mask(vif); + memset(&mvif->cap, -1, sizeof(mvif->cap)); + + mt7996_mcu_add_bss_info(phy, vif, true); + mt7996_mcu_add_sta(dev, vif, NULL, true); + rcu_assign_pointer(dev->mt76.wcid[idx], &mvif->sta.wcid); + +out: + mutex_unlock(&dev->mt76.mutex); + + return ret; +} + +static void mt7996_remove_interface(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) +{ + struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv; + struct mt7996_sta *msta = &mvif->sta; + struct mt7996_dev *dev = mt7996_hw_dev(hw); + struct mt7996_phy *phy = mt7996_hw_phy(hw); + int idx = msta->wcid.idx; + + mt7996_mcu_add_bss_info(phy, vif, false); + mt7996_mcu_add_sta(dev, vif, NULL, false); + + if (vif == phy->monitor_vif) + phy->monitor_vif = NULL; + + mt7996_mcu_add_dev_info(phy, vif, false); + mt7996_mcu_set_radio_en(phy, false); + + rcu_assign_pointer(dev->mt76.wcid[idx], NULL); + + mutex_lock(&dev->mt76.mutex); + dev->mt76.vif_mask &= ~BIT_ULL(mvif->mt76.idx); + phy->omac_mask &= ~BIT_ULL(mvif->mt76.omac_idx); + mutex_unlock(&dev->mt76.mutex); + + spin_lock_bh(&dev->sta_poll_lock); + if (!list_empty(&msta->poll_list)) + list_del_init(&msta->poll_list); + spin_unlock_bh(&dev->sta_poll_lock); + + mt76_packet_id_flush(&dev->mt76, &msta->wcid); +} + +int mt7996_set_channel(struct mt7996_phy *phy) +{ + struct mt7996_dev *dev = phy->dev; + int ret; + + cancel_delayed_work_sync(&phy->mt76->mac_work); + + mutex_lock(&dev->mt76.mutex); + set_bit(MT76_RESET, &phy->mt76->state); + + mt76_set_channel(phy->mt76); + + ret = mt7996_mcu_set_chan_info(phy, UNI_CHANNEL_SWITCH); + if (ret) + goto out; + + mt7996_mac_set_timing(phy); + ret = mt7996_dfs_init_radar_detector(phy); + mt7996_mac_cca_stats_reset(phy); + + mt7996_mac_reset_counters(phy); + phy->noise = 0; + +out: + clear_bit(MT76_RESET, &phy->mt76->state); + mutex_unlock(&dev->mt76.mutex); + + mt76_txq_schedule_all(phy->mt76); + + ieee80211_queue_delayed_work(phy->mt76->hw, + &phy->mt76->mac_work, + MT7996_WATCHDOG_TIME); + + return ret; +} + +static int mt7996_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, + struct ieee80211_vif *vif, struct ieee80211_sta *sta, + struct ieee80211_key_conf *key) +{ + struct mt7996_dev *dev = mt7996_hw_dev(hw); + struct mt7996_phy *phy = mt7996_hw_phy(hw); + struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv; + struct mt7996_sta *msta = sta ? (struct mt7996_sta *)sta->drv_priv : + &mvif->sta; + struct mt76_wcid *wcid = &msta->wcid; + u8 *wcid_keyidx = &wcid->hw_key_idx; + int idx = key->keyidx; + int err = 0; + + /* The hardware does not support per-STA RX GTK, fallback + * to software mode for these. + */ + if ((vif->type == NL80211_IFTYPE_ADHOC || + vif->type == NL80211_IFTYPE_MESH_POINT) && + (key->cipher == WLAN_CIPHER_SUITE_TKIP || + key->cipher == WLAN_CIPHER_SUITE_CCMP) && + !(key->flags & IEEE80211_KEY_FLAG_PAIRWISE)) + return -EOPNOTSUPP; + + /* fall back to sw encryption for unsupported ciphers */ + switch (key->cipher) { + case WLAN_CIPHER_SUITE_AES_CMAC: + wcid_keyidx = &wcid->hw_key_idx2; + key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIE; + break; + case WLAN_CIPHER_SUITE_TKIP: + case WLAN_CIPHER_SUITE_CCMP: + case WLAN_CIPHER_SUITE_CCMP_256: + case WLAN_CIPHER_SUITE_GCMP: + case WLAN_CIPHER_SUITE_GCMP_256: + case WLAN_CIPHER_SUITE_SMS4: + break; + case WLAN_CIPHER_SUITE_WEP40: + case WLAN_CIPHER_SUITE_WEP104: + default: + return -EOPNOTSUPP; + } + + mutex_lock(&dev->mt76.mutex); + + if (cmd == SET_KEY && !sta && !mvif->mt76.cipher) { + mvif->mt76.cipher = mt76_connac_mcu_get_cipher(key->cipher); + mt7996_mcu_add_bss_info(phy, vif, true); + } + + if (cmd == SET_KEY) + *wcid_keyidx = idx; + else if (idx == *wcid_keyidx) + *wcid_keyidx = -1; + else + goto out; + + mt76_wcid_key_setup(&dev->mt76, wcid, + cmd == SET_KEY ? key : NULL); + + err = mt7996_mcu_add_key(&dev->mt76, vif, &msta->bip, + key, MCU_WMWA_UNI_CMD(STA_REC_UPDATE), + &msta->wcid, cmd); +out: + mutex_unlock(&dev->mt76.mutex); + + return err; +} + +static int mt7996_config(struct ieee80211_hw *hw, u32 changed) +{ + struct mt7996_dev *dev = mt7996_hw_dev(hw); + struct mt7996_phy *phy = mt7996_hw_phy(hw); + int ret; + + if (changed & IEEE80211_CONF_CHANGE_CHANNEL) { + ieee80211_stop_queues(hw); + ret = mt7996_set_channel(phy); + if (ret) + return ret; + ieee80211_wake_queues(hw); + } + + mutex_lock(&dev->mt76.mutex); + + if (changed & IEEE80211_CONF_CHANGE_MONITOR) { + bool enabled = !!(hw->conf.flags & IEEE80211_CONF_MONITOR); + + if (!enabled) + phy->rxfilter |= MT_WF_RFCR_DROP_OTHER_UC; + else + phy->rxfilter &= ~MT_WF_RFCR_DROP_OTHER_UC; + + mt76_rmw_field(dev, MT_DMA_DCR0(phy->mt76->band_idx), + MT_DMA_DCR0_RXD_G5_EN, enabled); + mt76_wr(dev, MT_WF_RFCR(phy->mt76->band_idx), phy->rxfilter); + } + + mutex_unlock(&dev->mt76.mutex); + + return 0; +} + +static int +mt7996_conf_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + unsigned int link_id, u16 queue, + const struct ieee80211_tx_queue_params *params) +{ + struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv; + + /* no need to update right away, we'll get BSS_CHANGED_QOS */ + queue = mt76_connac_lmac_mapping(queue); + mvif->queue_params[queue] = *params; + + return 0; +} + +static void mt7996_configure_filter(struct ieee80211_hw *hw, + unsigned int changed_flags, + unsigned int *total_flags, + u64 multicast) +{ + struct mt7996_dev *dev = mt7996_hw_dev(hw); + struct mt7996_phy *phy = mt7996_hw_phy(hw); + u32 ctl_flags = MT_WF_RFCR1_DROP_ACK | + MT_WF_RFCR1_DROP_BF_POLL | + MT_WF_RFCR1_DROP_BA | + MT_WF_RFCR1_DROP_CFEND | + MT_WF_RFCR1_DROP_CFACK; + u32 flags = 0; + +#define MT76_FILTER(_flag, _hw) do { \ + flags |= *total_flags & FIF_##_flag; \ + phy->rxfilter &= ~(_hw); \ + phy->rxfilter |= !(flags & FIF_##_flag) * (_hw); \ + } while (0) + + mutex_lock(&dev->mt76.mutex); + + phy->rxfilter &= ~(MT_WF_RFCR_DROP_OTHER_BSS | + MT_WF_RFCR_DROP_OTHER_BEACON | + MT_WF_RFCR_DROP_FRAME_REPORT | + MT_WF_RFCR_DROP_PROBEREQ | + MT_WF_RFCR_DROP_MCAST_FILTERED | + MT_WF_RFCR_DROP_MCAST | + MT_WF_RFCR_DROP_BCAST | + MT_WF_RFCR_DROP_DUPLICATE | + MT_WF_RFCR_DROP_A2_BSSID | + MT_WF_RFCR_DROP_UNWANTED_CTL | + MT_WF_RFCR_DROP_STBC_MULTI); + + MT76_FILTER(OTHER_BSS, MT_WF_RFCR_DROP_OTHER_TIM | + MT_WF_RFCR_DROP_A3_MAC | + MT_WF_RFCR_DROP_A3_BSSID); + + MT76_FILTER(FCSFAIL, MT_WF_RFCR_DROP_FCSFAIL); + + MT76_FILTER(CONTROL, MT_WF_RFCR_DROP_CTS | + MT_WF_RFCR_DROP_RTS | + MT_WF_RFCR_DROP_CTL_RSV | + MT_WF_RFCR_DROP_NDPA); + + *total_flags = flags; + mt76_wr(dev, MT_WF_RFCR(phy->mt76->band_idx), phy->rxfilter); + + if (*total_flags & FIF_CONTROL) + mt76_clear(dev, MT_WF_RFCR1(phy->mt76->band_idx), ctl_flags); + else + mt76_set(dev, MT_WF_RFCR1(phy->mt76->band_idx), ctl_flags); + + mutex_unlock(&dev->mt76.mutex); +} + +static void +mt7996_update_bss_color(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct cfg80211_he_bss_color *bss_color) +{ + struct mt7996_dev *dev = mt7996_hw_dev(hw); + + switch (vif->type) { + case NL80211_IFTYPE_AP: { + struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv; + + if (mvif->mt76.omac_idx > HW_BSSID_MAX) + return; + fallthrough; + } + case NL80211_IFTYPE_STATION: + mt7996_mcu_update_bss_color(dev, vif, bss_color); + break; + default: + break; + } +} + +static void mt7996_bss_info_changed(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *info, + u64 changed) +{ + struct mt7996_phy *phy = mt7996_hw_phy(hw); + struct mt7996_dev *dev = mt7996_hw_dev(hw); + + mutex_lock(&dev->mt76.mutex); + + /* station mode uses BSSID to map the wlan entry to a peer, + * and then peer references bss_info_rfch to set bandwidth cap. + */ + if (changed & BSS_CHANGED_BSSID && + vif->type == NL80211_IFTYPE_STATION) { + bool join = !is_zero_ether_addr(info->bssid); + + mt7996_mcu_add_bss_info(phy, vif, join); + mt7996_mcu_add_sta(dev, vif, NULL, join); + } + + if (changed & BSS_CHANGED_ASSOC) { + mt7996_mcu_add_bss_info(phy, vif, vif->cfg.assoc); + mt7996_mcu_add_obss_spr(dev, vif, info->he_obss_pd.enable); + } + + if (changed & BSS_CHANGED_ERP_SLOT) { + int slottime = info->use_short_slot ? 9 : 20; + + if (slottime != phy->slottime) { + phy->slottime = slottime; + mt7996_mac_set_timing(phy); + } + } + + if (changed & BSS_CHANGED_BEACON_ENABLED && info->enable_beacon) { + mt7996_mcu_add_bss_info(phy, vif, true); + mt7996_mcu_add_sta(dev, vif, NULL, true); + } + + /* ensure that enable txcmd_mode after bss_info */ + if (changed & (BSS_CHANGED_QOS | BSS_CHANGED_BEACON_ENABLED)) + mt7996_mcu_set_tx(dev, vif); + + if (changed & BSS_CHANGED_HE_OBSS_PD) + mt7996_mcu_add_obss_spr(dev, vif, info->he_obss_pd.enable); + + if (changed & BSS_CHANGED_HE_BSS_COLOR) + mt7996_update_bss_color(hw, vif, &info->he_bss_color); + + if (changed & (BSS_CHANGED_BEACON | + BSS_CHANGED_BEACON_ENABLED)) + mt7996_mcu_add_beacon(hw, vif, info->enable_beacon); + + if (changed & BSS_CHANGED_UNSOL_BCAST_PROBE_RESP || + changed & BSS_CHANGED_FILS_DISCOVERY) + mt7996_mcu_beacon_inband_discov(dev, vif, changed); + + mutex_unlock(&dev->mt76.mutex); +} + +static void +mt7996_channel_switch_beacon(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct cfg80211_chan_def *chandef) +{ + struct mt7996_dev *dev = mt7996_hw_dev(hw); + + mutex_lock(&dev->mt76.mutex); + mt7996_mcu_add_beacon(hw, vif, true); + mutex_unlock(&dev->mt76.mutex); +} + +int mt7996_mac_sta_add(struct mt76_dev *mdev, struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + struct mt7996_dev *dev = container_of(mdev, struct mt7996_dev, mt76); + struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv; + struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv; + u8 band_idx = mvif->phy->mt76->band_idx; + int ret, idx; + + idx = mt76_wcid_alloc(dev->mt76.wcid_mask, MT7996_WTBL_STA); + if (idx < 0) + return -ENOSPC; + + INIT_LIST_HEAD(&msta->rc_list); + INIT_LIST_HEAD(&msta->poll_list); + msta->vif = mvif; + msta->wcid.sta = 1; + msta->wcid.idx = idx; + msta->wcid.phy_idx = band_idx; + msta->wcid.tx_info |= MT_WCID_TX_INFO_SET; + msta->jiffies = jiffies; + + mt7996_mac_wtbl_update(dev, idx, + MT_WTBL_UPDATE_ADM_COUNT_CLEAR); + + ret = mt7996_mcu_add_sta(dev, vif, sta, true); + if (ret) + return ret; + + return mt7996_mcu_add_rate_ctrl(dev, vif, sta, false); +} + +void mt7996_mac_sta_remove(struct mt76_dev *mdev, struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + struct mt7996_dev *dev = container_of(mdev, struct mt7996_dev, mt76); + struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv; + int i; + + mt7996_mcu_add_sta(dev, vif, sta, false); + + mt7996_mac_wtbl_update(dev, msta->wcid.idx, + MT_WTBL_UPDATE_ADM_COUNT_CLEAR); + + for (i = 0; i < ARRAY_SIZE(msta->twt.flow); i++) + mt7996_mac_twt_teardown_flow(dev, msta, i); + + spin_lock_bh(&dev->sta_poll_lock); + if (!list_empty(&msta->poll_list)) + list_del_init(&msta->poll_list); + if (!list_empty(&msta->rc_list)) + list_del_init(&msta->rc_list); + spin_unlock_bh(&dev->sta_poll_lock); +} + +static void mt7996_tx(struct ieee80211_hw *hw, + struct ieee80211_tx_control *control, + struct sk_buff *skb) +{ + struct mt7996_dev *dev = mt7996_hw_dev(hw); + struct mt76_phy *mphy = hw->priv; + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct ieee80211_vif *vif = info->control.vif; + struct mt76_wcid *wcid = &dev->mt76.global_wcid; + + if (control->sta) { + struct mt7996_sta *sta; + + sta = (struct mt7996_sta *)control->sta->drv_priv; + wcid = &sta->wcid; + } + + if (vif && !control->sta) { + struct mt7996_vif *mvif; + + mvif = (struct mt7996_vif *)vif->drv_priv; + wcid = &mvif->sta.wcid; + } + + mt76_tx(mphy, control->sta, wcid, skb); +} + +static int mt7996_set_rts_threshold(struct ieee80211_hw *hw, u32 val) +{ + struct mt7996_phy *phy = mt7996_hw_phy(hw); + int ret; + + mutex_lock(&phy->dev->mt76.mutex); + ret = mt7996_mcu_set_rts_thresh(phy, val); + mutex_unlock(&phy->dev->mt76.mutex); + + return ret; +} + +static int +mt7996_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_ampdu_params *params) +{ + enum ieee80211_ampdu_mlme_action action = params->action; + struct mt7996_dev *dev = mt7996_hw_dev(hw); + struct ieee80211_sta *sta = params->sta; + struct ieee80211_txq *txq = sta->txq[params->tid]; + struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv; + u16 tid = params->tid; + u16 ssn = params->ssn; + struct mt76_txq *mtxq; + int ret = 0; + + if (!txq) + return -EINVAL; + + mtxq = (struct mt76_txq *)txq->drv_priv; + + mutex_lock(&dev->mt76.mutex); + switch (action) { + case IEEE80211_AMPDU_RX_START: + mt76_rx_aggr_start(&dev->mt76, &msta->wcid, tid, ssn, + params->buf_size); + ret = mt7996_mcu_add_rx_ba(dev, params, true); + break; + case IEEE80211_AMPDU_RX_STOP: + mt76_rx_aggr_stop(&dev->mt76, &msta->wcid, tid); + ret = mt7996_mcu_add_rx_ba(dev, params, false); + break; + case IEEE80211_AMPDU_TX_OPERATIONAL: + mtxq->aggr = true; + mtxq->send_bar = false; + ret = mt7996_mcu_add_tx_ba(dev, params, true); + break; + case IEEE80211_AMPDU_TX_STOP_FLUSH: + case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT: + mtxq->aggr = false; + clear_bit(tid, &msta->ampdu_state); + ret = mt7996_mcu_add_tx_ba(dev, params, false); + break; + case IEEE80211_AMPDU_TX_START: + set_bit(tid, &msta->ampdu_state); + ret = IEEE80211_AMPDU_TX_START_IMMEDIATE; + break; + case IEEE80211_AMPDU_TX_STOP_CONT: + mtxq->aggr = false; + clear_bit(tid, &msta->ampdu_state); + ret = mt7996_mcu_add_tx_ba(dev, params, false); + ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid); + break; + } + mutex_unlock(&dev->mt76.mutex); + + return ret; +} + +static int +mt7996_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + return mt76_sta_state(hw, vif, sta, IEEE80211_STA_NOTEXIST, + IEEE80211_STA_NONE); +} + +static int +mt7996_sta_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + return mt76_sta_state(hw, vif, sta, IEEE80211_STA_NONE, + IEEE80211_STA_NOTEXIST); +} + +static int +mt7996_get_stats(struct ieee80211_hw *hw, + struct ieee80211_low_level_stats *stats) +{ + struct mt7996_phy *phy = mt7996_hw_phy(hw); + struct mt7996_dev *dev = mt7996_hw_dev(hw); + struct mib_stats *mib = &phy->mib; + + mutex_lock(&dev->mt76.mutex); + + stats->dot11RTSSuccessCount = mib->rts_cnt; + stats->dot11RTSFailureCount = mib->rts_retries_cnt; + stats->dot11FCSErrorCount = mib->fcs_err_cnt; + stats->dot11ACKFailureCount = mib->ack_fail_cnt; + + mutex_unlock(&dev->mt76.mutex); + + return 0; +} + +u64 __mt7996_get_tsf(struct ieee80211_hw *hw, struct mt7996_vif *mvif) +{ + struct mt7996_dev *dev = mt7996_hw_dev(hw); + struct mt7996_phy *phy = mt7996_hw_phy(hw); + union { + u64 t64; + u32 t32[2]; + } tsf; + u16 n; + + lockdep_assert_held(&dev->mt76.mutex); + + n = mvif->mt76.omac_idx > HW_BSSID_MAX ? HW_BSSID_0 + : mvif->mt76.omac_idx; + /* TSF software read */ + mt76_rmw(dev, MT_LPON_TCR(phy->mt76->band_idx, n), MT_LPON_TCR_SW_MODE, + MT_LPON_TCR_SW_READ); + tsf.t32[0] = mt76_rr(dev, MT_LPON_UTTR0(phy->mt76->band_idx)); + tsf.t32[1] = mt76_rr(dev, MT_LPON_UTTR1(phy->mt76->band_idx)); + + return tsf.t64; +} + +static u64 +mt7996_get_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif) +{ + struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv; + struct mt7996_dev *dev = mt7996_hw_dev(hw); + u64 ret; + + mutex_lock(&dev->mt76.mutex); + ret = __mt7996_get_tsf(hw, mvif); + mutex_unlock(&dev->mt76.mutex); + + return ret; +} + +static void +mt7996_set_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + u64 timestamp) +{ + struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv; + struct mt7996_dev *dev = mt7996_hw_dev(hw); + struct mt7996_phy *phy = mt7996_hw_phy(hw); + union { + u64 t64; + u32 t32[2]; + } tsf = { .t64 = timestamp, }; + u16 n; + + mutex_lock(&dev->mt76.mutex); + + n = mvif->mt76.omac_idx > HW_BSSID_MAX ? HW_BSSID_0 + : mvif->mt76.omac_idx; + mt76_wr(dev, MT_LPON_UTTR0(phy->mt76->band_idx), tsf.t32[0]); + mt76_wr(dev, MT_LPON_UTTR1(phy->mt76->band_idx), tsf.t32[1]); + /* TSF software overwrite */ + mt76_rmw(dev, MT_LPON_TCR(phy->mt76->band_idx, n), MT_LPON_TCR_SW_MODE, + MT_LPON_TCR_SW_WRITE); + + mutex_unlock(&dev->mt76.mutex); +} + +static void +mt7996_offset_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + s64 timestamp) +{ + struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv; + struct mt7996_dev *dev = mt7996_hw_dev(hw); + struct mt7996_phy *phy = mt7996_hw_phy(hw); + union { + u64 t64; + u32 t32[2]; + } tsf = { .t64 = timestamp, }; + u16 n; + + mutex_lock(&dev->mt76.mutex); + + n = mvif->mt76.omac_idx > HW_BSSID_MAX ? HW_BSSID_0 + : mvif->mt76.omac_idx; + mt76_wr(dev, MT_LPON_UTTR0(phy->mt76->band_idx), tsf.t32[0]); + mt76_wr(dev, MT_LPON_UTTR1(phy->mt76->band_idx), tsf.t32[1]); + /* TSF software adjust*/ + mt76_rmw(dev, MT_LPON_TCR(phy->mt76->band_idx, n), MT_LPON_TCR_SW_MODE, + MT_LPON_TCR_SW_ADJUST); + + mutex_unlock(&dev->mt76.mutex); +} + +static void +mt7996_set_coverage_class(struct ieee80211_hw *hw, s16 coverage_class) +{ + struct mt7996_phy *phy = mt7996_hw_phy(hw); + struct mt7996_dev *dev = phy->dev; + + mutex_lock(&dev->mt76.mutex); + phy->coverage_class = max_t(s16, coverage_class, 0); + mt7996_mac_set_timing(phy); + mutex_unlock(&dev->mt76.mutex); +} + +static int +mt7996_set_antenna(struct ieee80211_hw *hw, u32 tx_ant, u32 rx_ant) +{ + struct mt7996_dev *dev = mt7996_hw_dev(hw); + struct mt7996_phy *phy = mt7996_hw_phy(hw); + int max_nss = hweight8(hw->wiphy->available_antennas_tx); + u8 band_idx = phy->mt76->band_idx, shift = dev->chainshift[band_idx]; + + if (!tx_ant || tx_ant != rx_ant || ffs(tx_ant) > max_nss) + return -EINVAL; + + if ((BIT(hweight8(tx_ant)) - 1) != tx_ant) + tx_ant = BIT(ffs(tx_ant) - 1) - 1; + + mutex_lock(&dev->mt76.mutex); + + phy->mt76->antenna_mask = tx_ant; + + /* restore to the origin chainmask which might have auxiliary path */ + if (hweight8(tx_ant) == max_nss) + phy->mt76->chainmask = (dev->chainmask >> shift) << shift; + else + phy->mt76->chainmask = tx_ant << shift; + + mt76_set_stream_caps(phy->mt76, true); + mt7996_set_stream_vht_txbf_caps(phy); + mt7996_set_stream_he_caps(phy); + + mutex_unlock(&dev->mt76.mutex); + + return 0; +} + +static void mt7996_sta_statistics(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct station_info *sinfo) +{ + struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv; + struct rate_info *txrate = &msta->wcid.rate; + + if (!txrate->legacy && !txrate->flags) + return; + + if (txrate->legacy) { + sinfo->txrate.legacy = txrate->legacy; + } else { + sinfo->txrate.mcs = txrate->mcs; + sinfo->txrate.nss = txrate->nss; + sinfo->txrate.bw = txrate->bw; + sinfo->txrate.he_gi = txrate->he_gi; + sinfo->txrate.he_dcm = txrate->he_dcm; + sinfo->txrate.he_ru_alloc = txrate->he_ru_alloc; + } + sinfo->txrate.flags = txrate->flags; + sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_BITRATE); +} + +static void mt7996_sta_rc_work(void *data, struct ieee80211_sta *sta) +{ + struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv; + struct mt7996_dev *dev = msta->vif->phy->dev; + u32 *changed = data; + + spin_lock_bh(&dev->sta_poll_lock); + msta->changed |= *changed; + if (list_empty(&msta->rc_list)) + list_add_tail(&msta->rc_list, &dev->sta_rc_list); + spin_unlock_bh(&dev->sta_poll_lock); +} + +static void mt7996_sta_rc_update(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + u32 changed) +{ + struct mt7996_phy *phy = mt7996_hw_phy(hw); + struct mt7996_dev *dev = phy->dev; + + mt7996_sta_rc_work(&changed, sta); + ieee80211_queue_work(hw, &dev->rc_work); +} + +static int +mt7996_set_bitrate_mask(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + const struct cfg80211_bitrate_mask *mask) +{ + struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv; + struct mt7996_phy *phy = mt7996_hw_phy(hw); + struct mt7996_dev *dev = phy->dev; + u32 changed = IEEE80211_RC_SUPP_RATES_CHANGED; + + mvif->bitrate_mask = *mask; + + /* if multiple rates across different preambles are given we can + * reconfigure this info with all peers using sta_rec command with + * the below exception cases. + * - single rate : if a rate is passed along with different preambles, + * we select the highest one as fixed rate. i.e VHT MCS for VHT peers. + * - multiple rates: if it's not in range format i.e 0-{7,8,9} for VHT + * then multiple MCS setting (MCS 4,5,6) is not supported. + */ + ieee80211_iterate_stations_atomic(hw, mt7996_sta_rc_work, &changed); + ieee80211_queue_work(hw, &dev->rc_work); + + return 0; +} + +static void mt7996_sta_set_4addr(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + bool enabled) +{ + struct mt7996_dev *dev = mt7996_hw_dev(hw); + struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv; + + if (enabled) + set_bit(MT_WCID_FLAG_4ADDR, &msta->wcid.flags); + else + clear_bit(MT_WCID_FLAG_4ADDR, &msta->wcid.flags); + + mt7996_mcu_wtbl_update_hdr_trans(dev, vif, sta); +} + +static void mt7996_sta_set_decap_offload(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + bool enabled) +{ + struct mt7996_dev *dev = mt7996_hw_dev(hw); + struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv; + + if (enabled) + set_bit(MT_WCID_FLAG_HDR_TRANS, &msta->wcid.flags); + else + clear_bit(MT_WCID_FLAG_HDR_TRANS, &msta->wcid.flags); + + mt7996_mcu_wtbl_update_hdr_trans(dev, vif, sta); +} + +static const char mt7996_gstrings_stats[][ETH_GSTRING_LEN] = { + "tx_ampdu_cnt", + "tx_stop_q_empty_cnt", + "tx_mpdu_attempts", + "tx_mpdu_success", + "tx_rwp_fail_cnt", + "tx_rwp_need_cnt", + "tx_pkt_ebf_cnt", + "tx_pkt_ibf_cnt", + "tx_ampdu_len:0-1", + "tx_ampdu_len:2-10", + "tx_ampdu_len:11-19", + "tx_ampdu_len:20-28", + "tx_ampdu_len:29-37", + "tx_ampdu_len:38-46", + "tx_ampdu_len:47-55", + "tx_ampdu_len:56-79", + "tx_ampdu_len:80-103", + "tx_ampdu_len:104-127", + "tx_ampdu_len:128-151", + "tx_ampdu_len:152-175", + "tx_ampdu_len:176-199", + "tx_ampdu_len:200-223", + "tx_ampdu_len:224-247", + "ba_miss_count", + "tx_beamformer_ppdu_iBF", + "tx_beamformer_ppdu_eBF", + "tx_beamformer_rx_feedback_all", + "tx_beamformer_rx_feedback_he", + "tx_beamformer_rx_feedback_vht", + "tx_beamformer_rx_feedback_ht", + "tx_beamformer_rx_feedback_bw", /* zero based idx: 20, 40, 80, 160 */ + "tx_beamformer_rx_feedback_nc", + "tx_beamformer_rx_feedback_nr", + "tx_beamformee_ok_feedback_pkts", + "tx_beamformee_feedback_trig", + "tx_mu_beamforming", + "tx_mu_mpdu", + "tx_mu_successful_mpdu", + "tx_su_successful_mpdu", + "tx_msdu_pack_1", + "tx_msdu_pack_2", + "tx_msdu_pack_3", + "tx_msdu_pack_4", + "tx_msdu_pack_5", + "tx_msdu_pack_6", + "tx_msdu_pack_7", + "tx_msdu_pack_8", + + /* rx counters */ + "rx_fifo_full_cnt", + "rx_mpdu_cnt", + "channel_idle_cnt", + "rx_vector_mismatch_cnt", + "rx_delimiter_fail_cnt", + "rx_len_mismatch_cnt", + "rx_ampdu_cnt", + "rx_ampdu_bytes_cnt", + "rx_ampdu_valid_subframe_cnt", + "rx_ampdu_valid_subframe_b_cnt", + "rx_pfdrop_cnt", + "rx_vec_queue_overflow_drop_cnt", + "rx_ba_cnt", + + /* per vif counters */ + "v_tx_mode_cck", + "v_tx_mode_ofdm", + "v_tx_mode_ht", + "v_tx_mode_ht_gf", + "v_tx_mode_vht", + "v_tx_mode_he_su", + "v_tx_mode_he_ext_su", + "v_tx_mode_he_tb", + "v_tx_mode_he_mu", + "v_tx_bw_20", + "v_tx_bw_40", + "v_tx_bw_80", + "v_tx_bw_160", + "v_tx_mcs_0", + "v_tx_mcs_1", + "v_tx_mcs_2", + "v_tx_mcs_3", + "v_tx_mcs_4", + "v_tx_mcs_5", + "v_tx_mcs_6", + "v_tx_mcs_7", + "v_tx_mcs_8", + "v_tx_mcs_9", + "v_tx_mcs_10", + "v_tx_mcs_11", +}; + +#define MT7996_SSTATS_LEN ARRAY_SIZE(mt7996_gstrings_stats) + +/* Ethtool related API */ +static +void mt7996_get_et_strings(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + u32 sset, u8 *data) +{ + if (sset == ETH_SS_STATS) + memcpy(data, *mt7996_gstrings_stats, + sizeof(mt7996_gstrings_stats)); +} + +static +int mt7996_get_et_sset_count(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, int sset) +{ + if (sset == ETH_SS_STATS) + return MT7996_SSTATS_LEN; + + return 0; +} + +static void mt7996_ethtool_worker(void *wi_data, struct ieee80211_sta *sta) +{ + struct mt76_ethtool_worker_info *wi = wi_data; + struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv; + + if (msta->vif->mt76.idx != wi->idx) + return; + + mt76_ethtool_worker(wi, &msta->stats); +} + +static +void mt7996_get_et_stats(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ethtool_stats *stats, u64 *data) +{ + struct mt7996_dev *dev = mt7996_hw_dev(hw); + struct mt7996_phy *phy = mt7996_hw_phy(hw); + struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv; + struct mt76_ethtool_worker_info wi = { + .data = data, + .idx = mvif->mt76.idx, + }; + struct mib_stats *mib = &phy->mib; + /* See mt7996_ampdu_stat_read_phy, etc */ + int i, ei = 0; + + mutex_lock(&dev->mt76.mutex); + + mt7996_mac_update_stats(phy); + + data[ei++] = mib->tx_ampdu_cnt; + data[ei++] = mib->tx_stop_q_empty_cnt; + data[ei++] = mib->tx_mpdu_attempts_cnt; + data[ei++] = mib->tx_mpdu_success_cnt; + data[ei++] = mib->tx_rwp_fail_cnt; + data[ei++] = mib->tx_rwp_need_cnt; + data[ei++] = mib->tx_bf_ebf_ppdu_cnt; + data[ei++] = mib->tx_bf_ibf_ppdu_cnt; + + /* Tx ampdu stat */ + for (i = 0; i < 15 /*ARRAY_SIZE(bound)*/; i++) + data[ei++] = phy->mt76->aggr_stats[i]; + data[ei++] = phy->mib.ba_miss_cnt; + + /* Tx Beamformer monitor */ + data[ei++] = mib->tx_bf_ibf_ppdu_cnt; + data[ei++] = mib->tx_bf_ebf_ppdu_cnt; + + /* Tx Beamformer Rx feedback monitor */ + data[ei++] = mib->tx_bf_rx_fb_all_cnt; + data[ei++] = mib->tx_bf_rx_fb_he_cnt; + data[ei++] = mib->tx_bf_rx_fb_vht_cnt; + data[ei++] = mib->tx_bf_rx_fb_ht_cnt; + + data[ei++] = mib->tx_bf_rx_fb_bw; + data[ei++] = mib->tx_bf_rx_fb_nc_cnt; + data[ei++] = mib->tx_bf_rx_fb_nr_cnt; + + /* Tx Beamformee Rx NDPA & Tx feedback report */ + data[ei++] = mib->tx_bf_fb_cpl_cnt; + data[ei++] = mib->tx_bf_fb_trig_cnt; + + /* Tx SU & MU counters */ + data[ei++] = mib->tx_mu_bf_cnt; + data[ei++] = mib->tx_mu_mpdu_cnt; + data[ei++] = mib->tx_mu_acked_mpdu_cnt; + data[ei++] = mib->tx_su_acked_mpdu_cnt; + + /* Tx amsdu info (pack-count histogram) */ + for (i = 0; i < ARRAY_SIZE(mib->tx_amsdu); i++) + data[ei++] = mib->tx_amsdu[i]; + + /* rx counters */ + data[ei++] = mib->rx_fifo_full_cnt; + data[ei++] = mib->rx_mpdu_cnt; + data[ei++] = mib->channel_idle_cnt; + data[ei++] = mib->rx_vector_mismatch_cnt; + data[ei++] = mib->rx_delimiter_fail_cnt; + data[ei++] = mib->rx_len_mismatch_cnt; + data[ei++] = mib->rx_ampdu_cnt; + data[ei++] = mib->rx_ampdu_bytes_cnt; + data[ei++] = mib->rx_ampdu_valid_subframe_cnt; + data[ei++] = mib->rx_ampdu_valid_subframe_bytes_cnt; + data[ei++] = mib->rx_pfdrop_cnt; + data[ei++] = mib->rx_vec_queue_overflow_drop_cnt; + data[ei++] = mib->rx_ba_cnt; + + /* Add values for all stations owned by this vif */ + wi.initial_stat_idx = ei; + ieee80211_iterate_stations_atomic(hw, mt7996_ethtool_worker, &wi); + + mutex_unlock(&dev->mt76.mutex); + + if (wi.sta_count == 0) + return; + + ei += wi.worker_stat_count; + if (ei != MT7996_SSTATS_LEN) + dev_err(dev->mt76.dev, "ei: %d MT7996_SSTATS_LEN: %d", + ei, (int)MT7996_SSTATS_LEN); +} + +static void +mt7996_twt_teardown_request(struct ieee80211_hw *hw, + struct ieee80211_sta *sta, + u8 flowid) +{ + struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv; + struct mt7996_dev *dev = mt7996_hw_dev(hw); + + mutex_lock(&dev->mt76.mutex); + mt7996_mac_twt_teardown_flow(dev, msta, flowid); + mutex_unlock(&dev->mt76.mutex); +} + +static int +mt7996_set_radar_background(struct ieee80211_hw *hw, + struct cfg80211_chan_def *chandef) +{ + struct mt7996_phy *phy = mt7996_hw_phy(hw); + struct mt7996_dev *dev = phy->dev; + int ret = -EINVAL; + bool running; + + mutex_lock(&dev->mt76.mutex); + + if (dev->mt76.region == NL80211_DFS_UNSET) + goto out; + + if (dev->rdd2_phy && dev->rdd2_phy != phy) { + /* rdd2 is already locked */ + ret = -EBUSY; + goto out; + } + + /* rdd2 already configured on a radar channel */ + running = dev->rdd2_phy && + cfg80211_chandef_valid(&dev->rdd2_chandef) && + !!(dev->rdd2_chandef.chan->flags & IEEE80211_CHAN_RADAR); + + if (!chandef || running || + !(chandef->chan->flags & IEEE80211_CHAN_RADAR)) { + ret = mt7996_mcu_rdd_background_enable(phy, NULL); + if (ret) + goto out; + + if (!running) + goto update_phy; + } + + ret = mt7996_mcu_rdd_background_enable(phy, chandef); + if (ret) + goto out; + +update_phy: + dev->rdd2_phy = chandef ? phy : NULL; + if (chandef) + dev->rdd2_chandef = *chandef; +out: + mutex_unlock(&dev->mt76.mutex); + + return ret; +} + +const struct ieee80211_ops mt7996_ops = { + .tx = mt7996_tx, + .start = mt7996_start, + .stop = mt7996_stop, + .add_interface = mt7996_add_interface, + .remove_interface = mt7996_remove_interface, + .config = mt7996_config, + .conf_tx = mt7996_conf_tx, + .configure_filter = mt7996_configure_filter, + .bss_info_changed = mt7996_bss_info_changed, + .sta_add = mt7996_sta_add, + .sta_remove = mt7996_sta_remove, + .sta_pre_rcu_remove = mt76_sta_pre_rcu_remove, + .sta_rc_update = mt7996_sta_rc_update, + .set_key = mt7996_set_key, + .ampdu_action = mt7996_ampdu_action, + .set_rts_threshold = mt7996_set_rts_threshold, + .wake_tx_queue = mt76_wake_tx_queue, + .sw_scan_start = mt76_sw_scan, + .sw_scan_complete = mt76_sw_scan_complete, + .release_buffered_frames = mt76_release_buffered_frames, + .get_txpower = mt76_get_txpower, + .channel_switch_beacon = mt7996_channel_switch_beacon, + .get_stats = mt7996_get_stats, + .get_et_sset_count = mt7996_get_et_sset_count, + .get_et_stats = mt7996_get_et_stats, + .get_et_strings = mt7996_get_et_strings, + .get_tsf = mt7996_get_tsf, + .set_tsf = mt7996_set_tsf, + .offset_tsf = mt7996_offset_tsf, + .get_survey = mt76_get_survey, + .get_antenna = mt76_get_antenna, + .set_antenna = mt7996_set_antenna, + .set_bitrate_mask = mt7996_set_bitrate_mask, + .set_coverage_class = mt7996_set_coverage_class, + .sta_statistics = mt7996_sta_statistics, + .sta_set_4addr = mt7996_sta_set_4addr, + .sta_set_decap_offload = mt7996_sta_set_decap_offload, + .add_twt_setup = mt7996_mac_add_twt_setup, + .twt_teardown_request = mt7996_twt_teardown_request, +#ifdef CONFIG_MAC80211_DEBUGFS + .sta_add_debugfs = mt7996_sta_add_debugfs, +#endif + .set_radar_background = mt7996_set_radar_background, +}; diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c new file mode 100644 index 000000000000..123076db8021 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c @@ -0,0 +1,3429 @@ +// SPDX-License-Identifier: ISC +/* + * Copyright (C) 2022 MediaTek Inc. + */ + +#include +#include +#include "mt7996.h" +#include "mcu.h" +#include "mac.h" +#include "eeprom.h" + +struct mt7996_patch_hdr { + char build_date[16]; + char platform[4]; + __be32 hw_sw_ver; + __be32 patch_ver; + __be16 checksum; + u16 reserved; + struct { + __be32 patch_ver; + __be32 subsys; + __be32 feature; + __be32 n_region; + __be32 crc; + u32 reserved[11]; + } desc; +} __packed; + +struct mt7996_patch_sec { + __be32 type; + __be32 offs; + __be32 size; + union { + __be32 spec[13]; + struct { + __be32 addr; + __be32 len; + __be32 sec_key_idx; + __be32 align_len; + u32 reserved[9]; + } info; + }; +} __packed; + +struct mt7996_fw_trailer { + u8 chip_id; + u8 eco_code; + u8 n_region; + u8 format_ver; + u8 format_flag; + u8 reserved[2]; + char fw_ver[10]; + char build_date[15]; + u32 crc; +} __packed; + +struct mt7996_fw_region { + __le32 decomp_crc; + __le32 decomp_len; + __le32 decomp_blk_sz; + u8 reserved[4]; + __le32 addr; + __le32 len; + u8 feature_set; + u8 reserved1[15]; +} __packed; + +#define MCU_PATCH_ADDRESS 0x200000 + +#define HE_PHY(p, c) u8_get_bits(c, IEEE80211_HE_PHY_##p) +#define HE_MAC(m, c) u8_get_bits(c, IEEE80211_HE_MAC_##m) + +static u8 +mt7996_mcu_get_sta_nss(u16 mcs_map) +{ + u8 nss; + + for (nss = 8; nss > 0; nss--) { + u8 nss_mcs = (mcs_map >> (2 * (nss - 1))) & 3; + + if (nss_mcs != IEEE80211_VHT_MCS_NOT_SUPPORTED) + break; + } + + return nss - 1; +} + +static void +mt7996_mcu_set_sta_he_mcs(struct ieee80211_sta *sta, __le16 *he_mcs, + u16 mcs_map) +{ + struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv; + enum nl80211_band band = msta->vif->phy->mt76->chandef.chan->band; + const u16 *mask = msta->vif->bitrate_mask.control[band].he_mcs; + int nss, max_nss = sta->deflink.rx_nss > 3 ? 4 : sta->deflink.rx_nss; + + for (nss = 0; nss < max_nss; nss++) { + int mcs; + + switch ((mcs_map >> (2 * nss)) & 0x3) { + case IEEE80211_HE_MCS_SUPPORT_0_11: + mcs = GENMASK(11, 0); + break; + case IEEE80211_HE_MCS_SUPPORT_0_9: + mcs = GENMASK(9, 0); + break; + case IEEE80211_HE_MCS_SUPPORT_0_7: + mcs = GENMASK(7, 0); + break; + default: + mcs = 0; + } + + mcs = mcs ? fls(mcs & mask[nss]) - 1 : -1; + + switch (mcs) { + case 0 ... 7: + mcs = IEEE80211_HE_MCS_SUPPORT_0_7; + break; + case 8 ... 9: + mcs = IEEE80211_HE_MCS_SUPPORT_0_9; + break; + case 10 ... 11: + mcs = IEEE80211_HE_MCS_SUPPORT_0_11; + break; + default: + mcs = IEEE80211_HE_MCS_NOT_SUPPORTED; + break; + } + mcs_map &= ~(0x3 << (nss * 2)); + mcs_map |= mcs << (nss * 2); + } + + *he_mcs = cpu_to_le16(mcs_map); +} + +static void +mt7996_mcu_set_sta_vht_mcs(struct ieee80211_sta *sta, __le16 *vht_mcs, + const u16 *mask) +{ + u16 mcs, mcs_map = le16_to_cpu(sta->deflink.vht_cap.vht_mcs.rx_mcs_map); + int nss, max_nss = sta->deflink.rx_nss > 3 ? 4 : sta->deflink.rx_nss; + + for (nss = 0; nss < max_nss; nss++, mcs_map >>= 2) { + switch (mcs_map & 0x3) { + case IEEE80211_VHT_MCS_SUPPORT_0_9: + mcs = GENMASK(9, 0); + break; + case IEEE80211_VHT_MCS_SUPPORT_0_8: + mcs = GENMASK(8, 0); + break; + case IEEE80211_VHT_MCS_SUPPORT_0_7: + mcs = GENMASK(7, 0); + break; + default: + mcs = 0; + } + + vht_mcs[nss] = cpu_to_le16(mcs & mask[nss]); + } +} + +static void +mt7996_mcu_set_sta_ht_mcs(struct ieee80211_sta *sta, u8 *ht_mcs, + const u8 *mask) +{ + int nss, max_nss = sta->deflink.rx_nss > 3 ? 4 : sta->deflink.rx_nss; + + for (nss = 0; nss < max_nss; nss++) + ht_mcs[nss] = sta->deflink.ht_cap.mcs.rx_mask[nss] & mask[nss]; +} + +static int +mt7996_mcu_parse_response(struct mt76_dev *mdev, int cmd, + struct sk_buff *skb, int seq) +{ + struct mt7996_mcu_rxd *rxd; + struct mt7996_mcu_uni_event *event; + int mcu_cmd = FIELD_GET(__MCU_CMD_FIELD_ID, cmd); + int ret = 0; + + if (!skb) { + dev_err(mdev->dev, "Message %08x (seq %d) timeout\n", + cmd, seq); + return -ETIMEDOUT; + } + + rxd = (struct mt7996_mcu_rxd *)skb->data; + if (seq != rxd->seq) + return -EAGAIN; + + if (cmd == MCU_CMD(PATCH_SEM_CONTROL)) { + skb_pull(skb, sizeof(*rxd) - 4); + ret = *skb->data; + } else if ((rxd->option & MCU_UNI_CMD_EVENT) && + rxd->eid == MCU_UNI_EVENT_RESULT) { + skb_pull(skb, sizeof(*rxd)); + event = (struct mt7996_mcu_uni_event *)skb->data; + ret = le32_to_cpu(event->status); + /* skip invalid event */ + if (mcu_cmd != event->cid) + ret = -EAGAIN; + } else { + skb_pull(skb, sizeof(struct mt7996_mcu_rxd)); + } + + return ret; +} + +static int +mt7996_mcu_send_message(struct mt76_dev *mdev, struct sk_buff *skb, + int cmd, int *wait_seq) +{ + struct mt7996_dev *dev = container_of(mdev, struct mt7996_dev, mt76); + int txd_len, mcu_cmd = FIELD_GET(__MCU_CMD_FIELD_ID, cmd); + struct mt76_connac2_mcu_uni_txd *uni_txd; + struct mt76_connac2_mcu_txd *mcu_txd; + enum mt76_mcuq_id qid; + __le32 *txd; + u32 val; + u8 seq; + + mdev->mcu.timeout = 20 * HZ; + + seq = ++dev->mt76.mcu.msg_seq & 0xf; + if (!seq) + seq = ++dev->mt76.mcu.msg_seq & 0xf; + + if (cmd == MCU_CMD(FW_SCATTER)) { + qid = MT_MCUQ_FWDL; + goto exit; + } + + txd_len = cmd & __MCU_CMD_FIELD_UNI ? sizeof(*uni_txd) : sizeof(*mcu_txd); + txd = (__le32 *)skb_push(skb, txd_len); + if (test_bit(MT76_STATE_MCU_RUNNING, &dev->mphy.state)) + qid = MT_MCUQ_WA; + else + qid = MT_MCUQ_WM; + + val = FIELD_PREP(MT_TXD0_TX_BYTES, skb->len) | + FIELD_PREP(MT_TXD0_PKT_FMT, MT_TX_TYPE_CMD) | + FIELD_PREP(MT_TXD0_Q_IDX, MT_TX_MCU_PORT_RX_Q0); + txd[0] = cpu_to_le32(val); + + val = FIELD_PREP(MT_TXD1_HDR_FORMAT, MT_HDR_FORMAT_CMD); + txd[1] = cpu_to_le32(val); + + if (cmd & __MCU_CMD_FIELD_UNI) { + uni_txd = (struct mt76_connac2_mcu_uni_txd *)txd; + uni_txd->len = cpu_to_le16(skb->len - sizeof(uni_txd->txd)); + uni_txd->cid = cpu_to_le16(mcu_cmd); + uni_txd->s2d_index = MCU_S2D_H2CN; + uni_txd->pkt_type = MCU_PKT_ID; + uni_txd->seq = seq; + + if (cmd & __MCU_CMD_FIELD_QUERY) + uni_txd->option = MCU_CMD_UNI_QUERY_ACK; + else + uni_txd->option = MCU_CMD_UNI_EXT_ACK; + + if ((cmd & __MCU_CMD_FIELD_WA) && (cmd & __MCU_CMD_FIELD_WM)) + uni_txd->s2d_index = MCU_S2D_H2CN; + else if (cmd & __MCU_CMD_FIELD_WA) + uni_txd->s2d_index = MCU_S2D_H2C; + else if (cmd & __MCU_CMD_FIELD_WM) + uni_txd->s2d_index = MCU_S2D_H2N; + + goto exit; + } + + mcu_txd = (struct mt76_connac2_mcu_txd *)txd; + mcu_txd->len = cpu_to_le16(skb->len - sizeof(mcu_txd->txd)); + mcu_txd->pq_id = cpu_to_le16(MCU_PQ_ID(MT_TX_PORT_IDX_MCU, + MT_TX_MCU_PORT_RX_Q0)); + mcu_txd->pkt_type = MCU_PKT_ID; + mcu_txd->seq = seq; + + mcu_txd->cid = FIELD_GET(__MCU_CMD_FIELD_ID, cmd); + mcu_txd->set_query = MCU_Q_NA; + mcu_txd->ext_cid = FIELD_GET(__MCU_CMD_FIELD_EXT_ID, cmd); + if (mcu_txd->ext_cid) { + mcu_txd->ext_cid_ack = 1; + + if (cmd & __MCU_CMD_FIELD_QUERY) + mcu_txd->set_query = MCU_Q_QUERY; + else + mcu_txd->set_query = MCU_Q_SET; + } + + if (cmd & __MCU_CMD_FIELD_WA) + mcu_txd->s2d_index = MCU_S2D_H2C; + else + mcu_txd->s2d_index = MCU_S2D_H2N; + +exit: + if (wait_seq) + *wait_seq = seq; + + return mt76_tx_queue_skb_raw(dev, mdev->q_mcu[qid], skb, 0); +} + +int mt7996_mcu_wa_cmd(struct mt7996_dev *dev, int cmd, u32 a1, u32 a2, u32 a3) +{ + struct { + __le32 args[3]; + } req = { + .args = { + cpu_to_le32(a1), + cpu_to_le32(a2), + cpu_to_le32(a3), + }, + }; + + return mt76_mcu_send_msg(&dev->mt76, cmd, &req, sizeof(req), false); +} + +static void +mt7996_mcu_csa_finish(void *priv, u8 *mac, struct ieee80211_vif *vif) +{ + if (vif->bss_conf.csa_active) + ieee80211_csa_finish(vif); +} + +static void +mt7996_mcu_rx_radar_detected(struct mt7996_dev *dev, struct sk_buff *skb) +{ + struct mt76_phy *mphy = &dev->mt76.phy; + struct mt7996_mcu_rdd_report *r; + + r = (struct mt7996_mcu_rdd_report *)skb->data; + + mphy = dev->mt76.phys[r->band_idx]; + if (!mphy) + return; + + if (r->band_idx == MT_RX_SEL2) + cfg80211_background_radar_event(mphy->hw->wiphy, + &dev->rdd2_chandef, + GFP_ATOMIC); + else + ieee80211_radar_detected(mphy->hw); + dev->hw_pattern++; +} + +static void +mt7996_mcu_rx_log_message(struct mt7996_dev *dev, struct sk_buff *skb) +{ +#define UNI_EVENT_FW_LOG_FORMAT 0 + struct mt7996_mcu_rxd *rxd = (struct mt7996_mcu_rxd *)skb->data; + const char *data = (char *)&rxd[1] + 4, *type; + struct tlv *tlv = (struct tlv *)data; + int len; + + if (!(rxd->option & MCU_UNI_CMD_EVENT)) { + len = skb->len - sizeof(*rxd); + data = (char *)&rxd[1]; + goto out; + } + + if (le16_to_cpu(tlv->tag) != UNI_EVENT_FW_LOG_FORMAT) + return; + + data += sizeof(*tlv) + 4; + len = le16_to_cpu(tlv->len) - sizeof(*tlv) - 4; + +out: + switch (rxd->s2d_index) { + case 0: + if (mt7996_debugfs_rx_log(dev, data, len)) + return; + + type = "WM"; + break; + case 2: + type = "WA"; + break; + default: + type = "unknown"; + break; + } + + wiphy_info(mt76_hw(dev)->wiphy, "%s: %.*s", type, len, data); +} + +static void +mt7996_mcu_cca_finish(void *priv, u8 *mac, struct ieee80211_vif *vif) +{ + if (!vif->bss_conf.color_change_active) + return; + + ieee80211_color_change_finish(vif); +} + +static void +mt7996_mcu_ie_countdown(struct mt7996_dev *dev, struct sk_buff *skb) +{ +#define UNI_EVENT_IE_COUNTDOWN_CSA 0 +#define UNI_EVENT_IE_COUNTDOWN_BCC 1 + struct header { + u8 band; + u8 rsv[3]; + }; + struct mt76_phy *mphy = &dev->mt76.phy; + struct mt7996_mcu_rxd *rxd = (struct mt7996_mcu_rxd *)skb->data; + const char *data = (char *)&rxd[1], *tail; + struct header *hdr = (struct header *)data; + struct tlv *tlv = (struct tlv *)(data + 4); + + if (hdr->band && dev->mt76.phys[hdr->band]) + mphy = dev->mt76.phys[hdr->band]; + + tail = skb->data + le16_to_cpu(rxd->len); + while (data + sizeof(struct tlv) < tail && le16_to_cpu(tlv->len)) { + switch (le16_to_cpu(tlv->tag)) { + case UNI_EVENT_IE_COUNTDOWN_CSA: + ieee80211_iterate_active_interfaces_atomic(mphy->hw, + IEEE80211_IFACE_ITER_RESUME_ALL, + mt7996_mcu_csa_finish, mphy->hw); + break; + case UNI_EVENT_IE_COUNTDOWN_BCC: + ieee80211_iterate_active_interfaces_atomic(mphy->hw, + IEEE80211_IFACE_ITER_RESUME_ALL, + mt7996_mcu_cca_finish, mphy->hw); + break; + } + + data += le16_to_cpu(tlv->len); + tlv = (struct tlv *)data; + } +} + +static void +mt7996_mcu_rx_ext_event(struct mt7996_dev *dev, struct sk_buff *skb) +{ + struct mt7996_mcu_rxd *rxd = (struct mt7996_mcu_rxd *)skb->data; + + switch (rxd->ext_eid) { + case MCU_EXT_EVENT_FW_LOG_2_HOST: + mt7996_mcu_rx_log_message(dev, skb); + break; + default: + break; + } +} + +static void +mt7996_mcu_rx_unsolicited_event(struct mt7996_dev *dev, struct sk_buff *skb) +{ + struct mt7996_mcu_rxd *rxd = (struct mt7996_mcu_rxd *)skb->data; + + switch (rxd->eid) { + case MCU_EVENT_EXT: + mt7996_mcu_rx_ext_event(dev, skb); + break; + default: + break; + } + dev_kfree_skb(skb); +} + +static void +mt7996_mcu_uni_rx_unsolicited_event(struct mt7996_dev *dev, struct sk_buff *skb) +{ + struct mt7996_mcu_rxd *rxd = (struct mt7996_mcu_rxd *)skb->data; + + switch (rxd->eid) { + case MCU_UNI_EVENT_FW_LOG_2_HOST: + mt7996_mcu_rx_log_message(dev, skb); + break; + case MCU_UNI_EVENT_IE_COUNTDOWN: + mt7996_mcu_ie_countdown(dev, skb); + break; + case MCU_UNI_EVENT_RDD_REPORT: + mt7996_mcu_rx_radar_detected(dev, skb); + break; + default: + break; + } + dev_kfree_skb(skb); +} + +void mt7996_mcu_rx_event(struct mt7996_dev *dev, struct sk_buff *skb) +{ + struct mt7996_mcu_rxd *rxd = (struct mt7996_mcu_rxd *)skb->data; + + if (rxd->option & MCU_UNI_CMD_UNSOLICITED_EVENT) { + mt7996_mcu_uni_rx_unsolicited_event(dev, skb); + return; + } + + /* WA still uses legacy event*/ + if (rxd->ext_eid == MCU_EXT_EVENT_FW_LOG_2_HOST || + !rxd->seq) + mt7996_mcu_rx_unsolicited_event(dev, skb); + else + mt76_mcu_rx_event(&dev->mt76, skb); +} + +static struct tlv * +mt7996_mcu_add_uni_tlv(struct sk_buff *skb, u16 tag, u16 len) +{ + struct tlv *ptlv, tlv = { + .tag = cpu_to_le16(tag), + .len = cpu_to_le16(len), + }; + + ptlv = skb_put(skb, len); + memcpy(ptlv, &tlv, sizeof(tlv)); + + return ptlv; +} + +static void +mt7996_mcu_bss_rfch_tlv(struct sk_buff *skb, struct ieee80211_vif *vif, + struct mt7996_phy *phy) +{ + static const u8 rlm_ch_band[] = { + [NL80211_BAND_2GHZ] = 1, + [NL80211_BAND_5GHZ] = 2, + [NL80211_BAND_6GHZ] = 3, + }; + struct cfg80211_chan_def *chandef = &phy->mt76->chandef; + struct bss_rlm_tlv *ch; + struct tlv *tlv; + int freq1 = chandef->center_freq1; + + tlv = mt7996_mcu_add_uni_tlv(skb, UNI_BSS_INFO_RLM, sizeof(*ch)); + + ch = (struct bss_rlm_tlv *)tlv; + ch->control_channel = chandef->chan->hw_value; + ch->center_chan = ieee80211_frequency_to_channel(freq1); + ch->bw = mt76_connac_chan_bw(chandef); + ch->tx_streams = hweight8(phy->mt76->antenna_mask); + ch->rx_streams = hweight8(phy->mt76->antenna_mask); + ch->band = rlm_ch_band[chandef->chan->band]; + + if (chandef->width == NL80211_CHAN_WIDTH_80P80) { + int freq2 = chandef->center_freq2; + + ch->center_chan2 = ieee80211_frequency_to_channel(freq2); + } +} + +static void +mt7996_mcu_bss_ra_tlv(struct sk_buff *skb, struct ieee80211_vif *vif, + struct mt7996_phy *phy) +{ + struct bss_ra_tlv *ra; + struct tlv *tlv; + + tlv = mt7996_mcu_add_uni_tlv(skb, UNI_BSS_INFO_RA, sizeof(*ra)); + + ra = (struct bss_ra_tlv *)tlv; + ra->short_preamble = true; +} + +static void +mt7996_mcu_bss_he_tlv(struct sk_buff *skb, struct ieee80211_vif *vif, + struct mt7996_phy *phy) +{ +#define DEFAULT_HE_PE_DURATION 4 +#define DEFAULT_HE_DURATION_RTS_THRES 1023 + const struct ieee80211_sta_he_cap *cap; + struct bss_info_uni_he *he; + struct tlv *tlv; + + cap = mt76_connac_get_he_phy_cap(phy->mt76, vif); + + tlv = mt7996_mcu_add_uni_tlv(skb, UNI_BSS_INFO_HE_BASIC, sizeof(*he)); + + he = (struct bss_info_uni_he *)tlv; + he->he_pe_duration = vif->bss_conf.htc_trig_based_pkt_ext; + if (!he->he_pe_duration) + he->he_pe_duration = DEFAULT_HE_PE_DURATION; + + he->he_rts_thres = cpu_to_le16(vif->bss_conf.frame_time_rts_th); + if (!he->he_rts_thres) + he->he_rts_thres = cpu_to_le16(DEFAULT_HE_DURATION_RTS_THRES); + + he->max_nss_mcs[CMD_HE_MCS_BW80] = cap->he_mcs_nss_supp.tx_mcs_80; + he->max_nss_mcs[CMD_HE_MCS_BW160] = cap->he_mcs_nss_supp.tx_mcs_160; + he->max_nss_mcs[CMD_HE_MCS_BW8080] = cap->he_mcs_nss_supp.tx_mcs_80p80; +} + +static void +mt7996_mcu_bss_bmc_tlv(struct sk_buff *skb, struct mt7996_phy *phy) +{ + struct bss_rate_tlv *bmc; + struct cfg80211_chan_def *chandef = &phy->mt76->chandef; + enum nl80211_band band = chandef->chan->band; + struct tlv *tlv; + + tlv = mt7996_mcu_add_uni_tlv(skb, UNI_BSS_INFO_RATE, sizeof(*bmc)); + + bmc = (struct bss_rate_tlv *)tlv; + if (band == NL80211_BAND_2GHZ) { + bmc->short_preamble = true; + } else { + bmc->bc_trans = cpu_to_le16(0x8080); + bmc->mc_trans = cpu_to_le16(0x8080); + bmc->bc_fixed_rate = 1; + bmc->mc_fixed_rate = 1; + bmc->short_preamble = 1; + } +} + +static void +mt7996_mcu_bss_txcmd_tlv(struct sk_buff *skb, bool en) +{ + struct bss_txcmd_tlv *txcmd; + struct tlv *tlv; + + tlv = mt7996_mcu_add_uni_tlv(skb, UNI_BSS_INFO_TXCMD, sizeof(*txcmd)); + + txcmd = (struct bss_txcmd_tlv *)tlv; + txcmd->txcmd_mode = en; +} + +static void +mt7996_mcu_bss_mld_tlv(struct sk_buff *skb, struct ieee80211_vif *vif) +{ + struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv; + struct bss_mld_tlv *mld; + struct tlv *tlv; + + tlv = mt7996_mcu_add_uni_tlv(skb, UNI_BSS_INFO_MLD, sizeof(*mld)); + + mld = (struct bss_mld_tlv *)tlv; + mld->group_mld_id = 0xff; + mld->own_mld_id = mvif->mt76.idx; + mld->remap_idx = 0xff; +} + +static void +mt7996_mcu_bss_sec_tlv(struct sk_buff *skb, struct ieee80211_vif *vif) +{ + struct mt76_vif *mvif = (struct mt76_vif *)vif->drv_priv; + struct bss_sec_tlv *sec; + struct tlv *tlv; + + tlv = mt7996_mcu_add_uni_tlv(skb, UNI_BSS_INFO_SEC, sizeof(*sec)); + + sec = (struct bss_sec_tlv *)tlv; + sec->cipher = mvif->cipher; +} + +static int +mt7996_mcu_muar_config(struct mt7996_phy *phy, struct ieee80211_vif *vif, + bool bssid, bool enable) +{ +#define UNI_MUAR_ENTRY 2 + struct mt7996_dev *dev = phy->dev; + struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv; + u32 idx = mvif->mt76.omac_idx - REPEATER_BSSID_START; + const u8 *addr = vif->addr; + + struct { + struct { + u8 band; + u8 __rsv[3]; + } hdr; + + __le16 tag; + __le16 len; + + bool smesh; + u8 bssid; + u8 index; + u8 entry_add; + u8 addr[ETH_ALEN]; + u8 __rsv[2]; + } __packed req = { + .hdr.band = phy->mt76->band_idx, + .tag = cpu_to_le16(UNI_MUAR_ENTRY), + .len = cpu_to_le16(sizeof(req) - sizeof(req.hdr)), + .smesh = false, + .index = idx * 2 + bssid, + .entry_add = true, + }; + + if (bssid) + addr = vif->bss_conf.bssid; + + if (enable) + memcpy(req.addr, addr, ETH_ALEN); + + return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(REPT_MUAR), &req, + sizeof(req), true); +} + +static int +mt7996_mcu_bss_basic_tlv(struct sk_buff *skb, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct mt76_phy *phy, u16 wlan_idx, + bool enable) +{ + struct mt76_vif *mvif = (struct mt76_vif *)vif->drv_priv; + struct cfg80211_chan_def *chandef = &phy->chandef; + struct mt76_connac_bss_basic_tlv *bss; + u32 type = CONNECTION_INFRA_AP; + struct tlv *tlv; + int idx; + + switch (vif->type) { + case NL80211_IFTYPE_MESH_POINT: + case NL80211_IFTYPE_AP: + case NL80211_IFTYPE_MONITOR: + break; + case NL80211_IFTYPE_STATION: + if (enable) { + rcu_read_lock(); + if (!sta) + sta = ieee80211_find_sta(vif, + vif->bss_conf.bssid); + /* TODO: enable BSS_INFO_UAPSD & BSS_INFO_PM */ + if (sta) { + struct mt76_wcid *wcid; + + wcid = (struct mt76_wcid *)sta->drv_priv; + wlan_idx = wcid->idx; + } + rcu_read_unlock(); + } + type = CONNECTION_INFRA_STA; + break; + case NL80211_IFTYPE_ADHOC: + type = CONNECTION_IBSS_ADHOC; + break; + default: + WARN_ON(1); + break; + } + + tlv = mt7996_mcu_add_uni_tlv(skb, UNI_BSS_INFO_BASIC, sizeof(*bss)); + + bss = (struct mt76_connac_bss_basic_tlv *)tlv; + bss->bcn_interval = cpu_to_le16(vif->bss_conf.beacon_int); + bss->dtim_period = vif->bss_conf.dtim_period; + bss->bmc_tx_wlan_idx = cpu_to_le16(wlan_idx); + bss->sta_idx = cpu_to_le16(wlan_idx); + bss->conn_type = cpu_to_le32(type); + bss->omac_idx = mvif->omac_idx; + bss->band_idx = mvif->band_idx; + bss->wmm_idx = mvif->wmm_idx; + bss->conn_state = !enable; + bss->active = enable; + + idx = mvif->omac_idx > EXT_BSSID_START ? HW_BSSID_0 : mvif->omac_idx; + bss->hw_bss_idx = idx; + + if (vif->type == NL80211_IFTYPE_MONITOR) { + memcpy(bss->bssid, phy->macaddr, ETH_ALEN); + return 0; + } + + memcpy(bss->bssid, vif->bss_conf.bssid, ETH_ALEN); + bss->bcn_interval = cpu_to_le16(vif->bss_conf.beacon_int); + bss->dtim_period = vif->bss_conf.dtim_period; + bss->phymode = mt76_connac_get_phy_mode(phy, vif, + chandef->chan->band, NULL); + + if (chandef->chan->band == NL80211_BAND_6GHZ) + bss->phymode_ext |= PHY_MODE_AX_6G; + + return 0; +} + +static struct sk_buff * +__mt7996_mcu_alloc_bss_req(struct mt76_dev *dev, struct mt76_vif *mvif, int len) +{ + struct bss_req_hdr hdr = { + .bss_idx = mvif->idx, + }; + struct sk_buff *skb; + + skb = mt76_mcu_msg_alloc(dev, NULL, len); + if (!skb) + return ERR_PTR(-ENOMEM); + + skb_put_data(skb, &hdr, sizeof(hdr)); + + return skb; +} + +int mt7996_mcu_add_bss_info(struct mt7996_phy *phy, + struct ieee80211_vif *vif, int enable) +{ + struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv; + struct mt7996_dev *dev = phy->dev; + struct sk_buff *skb; + + if (mvif->mt76.omac_idx >= REPEATER_BSSID_START) { + mt7996_mcu_muar_config(phy, vif, false, enable); + mt7996_mcu_muar_config(phy, vif, true, enable); + } + + skb = __mt7996_mcu_alloc_bss_req(&dev->mt76, &mvif->mt76, + MT7996_BSS_UPDATE_MAX_SIZE); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + /* bss_basic must be first */ + mt7996_mcu_bss_basic_tlv(skb, vif, NULL, phy->mt76, + mvif->sta.wcid.idx, enable); + mt7996_mcu_bss_sec_tlv(skb, vif); + + if (vif->type == NL80211_IFTYPE_MONITOR) + goto out; + + if (enable) { + mt7996_mcu_bss_rfch_tlv(skb, vif, phy); + mt7996_mcu_bss_bmc_tlv(skb, phy); + mt7996_mcu_bss_ra_tlv(skb, vif, phy); + mt7996_mcu_bss_txcmd_tlv(skb, true); + + if (vif->bss_conf.he_support) + mt7996_mcu_bss_he_tlv(skb, vif, phy); + + /* this tag is necessary no matter if the vif is MLD */ + mt7996_mcu_bss_mld_tlv(skb, vif); + } +out: + return mt76_mcu_skb_send_msg(&dev->mt76, skb, + MCU_WMWA_UNI_CMD(BSS_INFO_UPDATE), true); +} + +static int +mt7996_mcu_sta_ba(struct mt76_dev *dev, struct mt76_vif *mvif, + struct ieee80211_ampdu_params *params, + bool enable, bool tx) +{ + struct mt76_wcid *wcid = (struct mt76_wcid *)params->sta->drv_priv; + struct sta_rec_ba_uni *ba; + struct sk_buff *skb; + struct tlv *tlv; + + skb = __mt76_connac_mcu_alloc_sta_req(dev, mvif, wcid, + MT7996_STA_UPDATE_MAX_SIZE); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_BA, sizeof(*ba)); + + ba = (struct sta_rec_ba_uni *)tlv; + ba->ba_type = tx ? MT_BA_TYPE_ORIGINATOR : MT_BA_TYPE_RECIPIENT; + ba->winsize = cpu_to_le16(params->buf_size); + ba->ssn = cpu_to_le16(params->ssn); + ba->ba_en = enable << params->tid; + ba->amsdu = params->amsdu; + ba->tid = params->tid; + + return mt76_mcu_skb_send_msg(dev, skb, + MCU_WMWA_UNI_CMD(STA_REC_UPDATE), true); +} + +/** starec & wtbl **/ +int mt7996_mcu_add_tx_ba(struct mt7996_dev *dev, + struct ieee80211_ampdu_params *params, + bool enable) +{ + struct mt7996_sta *msta = (struct mt7996_sta *)params->sta->drv_priv; + struct mt7996_vif *mvif = msta->vif; + + if (enable && !params->amsdu) + msta->wcid.amsdu = false; + + return mt7996_mcu_sta_ba(&dev->mt76, &mvif->mt76, params, + enable, true); +} + +int mt7996_mcu_add_rx_ba(struct mt7996_dev *dev, + struct ieee80211_ampdu_params *params, + bool enable) +{ + struct mt7996_sta *msta = (struct mt7996_sta *)params->sta->drv_priv; + struct mt7996_vif *mvif = msta->vif; + + return mt7996_mcu_sta_ba(&dev->mt76, &mvif->mt76, params, + enable, false); +} + +static void +mt7996_mcu_sta_he_tlv(struct sk_buff *skb, struct ieee80211_sta *sta) +{ + struct ieee80211_he_cap_elem *elem = &sta->deflink.he_cap.he_cap_elem; + struct ieee80211_he_mcs_nss_supp mcs_map; + struct sta_rec_he_v2 *he; + struct tlv *tlv; + int i = 0; + + if (!sta->deflink.he_cap.has_he) + return; + + tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_HE_V2, sizeof(*he)); + + he = (struct sta_rec_he_v2 *)tlv; + for (i = 0; i < 11; i++) { + if (i < 6) + he->he_mac_cap[i] = cpu_to_le16(elem->mac_cap_info[i]); + he->he_phy_cap[i] = cpu_to_le16(elem->phy_cap_info[i]); + } + + mcs_map = sta->deflink.he_cap.he_mcs_nss_supp; + switch (sta->deflink.bandwidth) { + case IEEE80211_STA_RX_BW_160: + if (elem->phy_cap_info[0] & + IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_80PLUS80_MHZ_IN_5G) + mt7996_mcu_set_sta_he_mcs(sta, + &he->max_nss_mcs[CMD_HE_MCS_BW8080], + le16_to_cpu(mcs_map.rx_mcs_80p80)); + + mt7996_mcu_set_sta_he_mcs(sta, + &he->max_nss_mcs[CMD_HE_MCS_BW160], + le16_to_cpu(mcs_map.rx_mcs_160)); + fallthrough; + default: + mt7996_mcu_set_sta_he_mcs(sta, + &he->max_nss_mcs[CMD_HE_MCS_BW80], + le16_to_cpu(mcs_map.rx_mcs_80)); + break; + } + + he->pkt_ext = 2; +} + +static void +mt7996_mcu_sta_he_6g_tlv(struct sk_buff *skb, struct ieee80211_sta *sta) +{ + struct sta_rec_he_6g_capa *he_6g; + struct tlv *tlv; + + if (!sta->deflink.he_6ghz_capa.capa) + return; + + tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_HE_6G, sizeof(*he_6g)); + + he_6g = (struct sta_rec_he_6g_capa *)tlv; + he_6g->capa = sta->deflink.he_6ghz_capa.capa; +} + +static void +mt7996_mcu_sta_ht_tlv(struct sk_buff *skb, struct ieee80211_sta *sta) +{ + struct sta_rec_ht *ht; + struct tlv *tlv; + + if (!sta->deflink.ht_cap.ht_supported) + return; + + tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_HT, sizeof(*ht)); + + ht = (struct sta_rec_ht *)tlv; + ht->ht_cap = cpu_to_le16(sta->deflink.ht_cap.cap); +} + +static void +mt7996_mcu_sta_vht_tlv(struct sk_buff *skb, struct ieee80211_sta *sta) +{ + struct sta_rec_vht *vht; + struct tlv *tlv; + + /* For 6G band, this tlv is necessary to let hw work normally */ + if (!sta->deflink.he_6ghz_capa.capa && !sta->deflink.vht_cap.vht_supported) + return; + + tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_VHT, sizeof(*vht)); + + vht = (struct sta_rec_vht *)tlv; + vht->vht_cap = cpu_to_le32(sta->deflink.vht_cap.cap); + vht->vht_rx_mcs_map = sta->deflink.vht_cap.vht_mcs.rx_mcs_map; + vht->vht_tx_mcs_map = sta->deflink.vht_cap.vht_mcs.tx_mcs_map; +} + +static void +mt7996_mcu_sta_amsdu_tlv(struct mt7996_dev *dev, struct sk_buff *skb, + struct ieee80211_vif *vif, struct ieee80211_sta *sta) +{ + struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv; + struct sta_rec_amsdu *amsdu; + struct tlv *tlv; + + if (vif->type != NL80211_IFTYPE_STATION && + vif->type != NL80211_IFTYPE_AP) + return; + + if (!sta->deflink.agg.max_amsdu_len) + return; + + tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_HW_AMSDU, sizeof(*amsdu)); + amsdu = (struct sta_rec_amsdu *)tlv; + amsdu->max_amsdu_num = 8; + amsdu->amsdu_en = true; + msta->wcid.amsdu = true; + + switch (sta->deflink.agg.max_amsdu_len) { + case IEEE80211_MAX_MPDU_LEN_VHT_11454: + amsdu->max_mpdu_size = + IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_11454; + return; + case IEEE80211_MAX_MPDU_LEN_HT_7935: + case IEEE80211_MAX_MPDU_LEN_VHT_7991: + amsdu->max_mpdu_size = IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_7991; + return; + default: + amsdu->max_mpdu_size = IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_3895; + return; + } +} + +static inline bool +mt7996_is_ebf_supported(struct mt7996_phy *phy, struct ieee80211_vif *vif, + struct ieee80211_sta *sta, bool bfee) +{ + struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv; + int tx_ant = hweight8(phy->mt76->antenna_mask) - 1; + + if (vif->type != NL80211_IFTYPE_STATION && + vif->type != NL80211_IFTYPE_AP) + return false; + + if (!bfee && tx_ant < 2) + return false; + + if (sta->deflink.he_cap.has_he) { + struct ieee80211_he_cap_elem *pe = &sta->deflink.he_cap.he_cap_elem; + + if (bfee) + return mvif->cap.he_su_ebfee && + HE_PHY(CAP3_SU_BEAMFORMER, pe->phy_cap_info[3]); + else + return mvif->cap.he_su_ebfer && + HE_PHY(CAP4_SU_BEAMFORMEE, pe->phy_cap_info[4]); + } + + if (sta->deflink.vht_cap.vht_supported) { + u32 cap = sta->deflink.vht_cap.cap; + + if (bfee) + return mvif->cap.vht_su_ebfee && + (cap & IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE); + else + return mvif->cap.vht_su_ebfer && + (cap & IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE); + } + + return false; +} + +static void +mt7996_mcu_sta_sounding_rate(struct sta_rec_bf *bf) +{ + bf->sounding_phy = MT_PHY_TYPE_OFDM; + bf->ndp_rate = 0; /* mcs0 */ + bf->ndpa_rate = MT7996_CFEND_RATE_DEFAULT; /* ofdm 24m */ + bf->rept_poll_rate = MT7996_CFEND_RATE_DEFAULT; /* ofdm 24m */ +} + +static void +mt7996_mcu_sta_bfer_ht(struct ieee80211_sta *sta, struct mt7996_phy *phy, + struct sta_rec_bf *bf) +{ + struct ieee80211_mcs_info *mcs = &sta->deflink.ht_cap.mcs; + u8 n = 0; + + bf->tx_mode = MT_PHY_TYPE_HT; + + if ((mcs->tx_params & IEEE80211_HT_MCS_TX_RX_DIFF) && + (mcs->tx_params & IEEE80211_HT_MCS_TX_DEFINED)) + n = FIELD_GET(IEEE80211_HT_MCS_TX_MAX_STREAMS_MASK, + mcs->tx_params); + else if (mcs->rx_mask[3]) + n = 3; + else if (mcs->rx_mask[2]) + n = 2; + else if (mcs->rx_mask[1]) + n = 1; + + bf->nrow = hweight8(phy->mt76->antenna_mask) - 1; + bf->ncol = min_t(u8, bf->nrow, n); + bf->ibf_ncol = n; +} + +static void +mt7996_mcu_sta_bfer_vht(struct ieee80211_sta *sta, struct mt7996_phy *phy, + struct sta_rec_bf *bf, bool explicit) +{ + struct ieee80211_sta_vht_cap *pc = &sta->deflink.vht_cap; + struct ieee80211_sta_vht_cap *vc = &phy->mt76->sband_5g.sband.vht_cap; + u16 mcs_map = le16_to_cpu(pc->vht_mcs.rx_mcs_map); + u8 nss_mcs = mt7996_mcu_get_sta_nss(mcs_map); + u8 tx_ant = hweight8(phy->mt76->antenna_mask) - 1; + + bf->tx_mode = MT_PHY_TYPE_VHT; + + if (explicit) { + u8 sts, snd_dim; + + mt7996_mcu_sta_sounding_rate(bf); + + sts = FIELD_GET(IEEE80211_VHT_CAP_BEAMFORMEE_STS_MASK, + pc->cap); + snd_dim = FIELD_GET(IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_MASK, + vc->cap); + bf->nrow = min_t(u8, min_t(u8, snd_dim, sts), tx_ant); + bf->ncol = min_t(u8, nss_mcs, bf->nrow); + bf->ibf_ncol = bf->ncol; + + if (sta->deflink.bandwidth == IEEE80211_STA_RX_BW_160) + bf->nrow = 1; + } else { + bf->nrow = tx_ant; + bf->ncol = min_t(u8, nss_mcs, bf->nrow); + bf->ibf_ncol = nss_mcs; + + if (sta->deflink.bandwidth == IEEE80211_STA_RX_BW_160) + bf->ibf_nrow = 1; + } +} + +static void +mt7996_mcu_sta_bfer_he(struct ieee80211_sta *sta, struct ieee80211_vif *vif, + struct mt7996_phy *phy, struct sta_rec_bf *bf) +{ + struct ieee80211_sta_he_cap *pc = &sta->deflink.he_cap; + struct ieee80211_he_cap_elem *pe = &pc->he_cap_elem; + const struct ieee80211_sta_he_cap *vc = + mt76_connac_get_he_phy_cap(phy->mt76, vif); + const struct ieee80211_he_cap_elem *ve = &vc->he_cap_elem; + u16 mcs_map = le16_to_cpu(pc->he_mcs_nss_supp.rx_mcs_80); + u8 nss_mcs = mt7996_mcu_get_sta_nss(mcs_map); + u8 snd_dim, sts; + + bf->tx_mode = MT_PHY_TYPE_HE_SU; + + mt7996_mcu_sta_sounding_rate(bf); + + bf->trigger_su = HE_PHY(CAP6_TRIG_SU_BEAMFORMING_FB, + pe->phy_cap_info[6]); + bf->trigger_mu = HE_PHY(CAP6_TRIG_MU_BEAMFORMING_PARTIAL_BW_FB, + pe->phy_cap_info[6]); + snd_dim = HE_PHY(CAP5_BEAMFORMEE_NUM_SND_DIM_UNDER_80MHZ_MASK, + ve->phy_cap_info[5]); + sts = HE_PHY(CAP4_BEAMFORMEE_MAX_STS_UNDER_80MHZ_MASK, + pe->phy_cap_info[4]); + bf->nrow = min_t(u8, snd_dim, sts); + bf->ncol = min_t(u8, nss_mcs, bf->nrow); + bf->ibf_ncol = bf->ncol; + + if (sta->deflink.bandwidth != IEEE80211_STA_RX_BW_160) + return; + + /* go over for 160MHz and 80p80 */ + if (pe->phy_cap_info[0] & + IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G) { + mcs_map = le16_to_cpu(pc->he_mcs_nss_supp.rx_mcs_160); + nss_mcs = mt7996_mcu_get_sta_nss(mcs_map); + + bf->ncol_gt_bw80 = nss_mcs; + } + + if (pe->phy_cap_info[0] & + IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_80PLUS80_MHZ_IN_5G) { + mcs_map = le16_to_cpu(pc->he_mcs_nss_supp.rx_mcs_80p80); + nss_mcs = mt7996_mcu_get_sta_nss(mcs_map); + + if (bf->ncol_gt_bw80) + bf->ncol_gt_bw80 = min_t(u8, bf->ncol_gt_bw80, nss_mcs); + else + bf->ncol_gt_bw80 = nss_mcs; + } + + snd_dim = HE_PHY(CAP5_BEAMFORMEE_NUM_SND_DIM_ABOVE_80MHZ_MASK, + ve->phy_cap_info[5]); + sts = HE_PHY(CAP4_BEAMFORMEE_MAX_STS_ABOVE_80MHZ_MASK, + pe->phy_cap_info[4]); + + bf->nrow_gt_bw80 = min_t(int, snd_dim, sts); +} + +static void +mt7996_mcu_sta_bfer_tlv(struct mt7996_dev *dev, struct sk_buff *skb, + struct ieee80211_vif *vif, struct ieee80211_sta *sta) +{ + struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv; + struct mt7996_phy *phy = mvif->phy; + int tx_ant = hweight8(phy->mt76->antenna_mask) - 1; + struct sta_rec_bf *bf; + struct tlv *tlv; + const u8 matrix[4][4] = { + {0, 0, 0, 0}, + {1, 1, 0, 0}, /* 2x1, 2x2, 2x3, 2x4 */ + {2, 4, 4, 0}, /* 3x1, 3x2, 3x3, 3x4 */ + {3, 5, 6, 0} /* 4x1, 4x2, 4x3, 4x4 */ + }; + bool ebf; + + if (!(sta->deflink.ht_cap.ht_supported || sta->deflink.he_cap.has_he)) + return; + + ebf = mt7996_is_ebf_supported(phy, vif, sta, false); + if (!ebf && !dev->ibf) + return; + + tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_BF, sizeof(*bf)); + bf = (struct sta_rec_bf *)tlv; + + /* he: eBF only, in accordance with spec + * vht: support eBF and iBF + * ht: iBF only, since mac80211 lacks of eBF support + */ + if (sta->deflink.he_cap.has_he && ebf) + mt7996_mcu_sta_bfer_he(sta, vif, phy, bf); + else if (sta->deflink.vht_cap.vht_supported) + mt7996_mcu_sta_bfer_vht(sta, phy, bf, ebf); + else if (sta->deflink.ht_cap.ht_supported) + mt7996_mcu_sta_bfer_ht(sta, phy, bf); + else + return; + + bf->bf_cap = ebf ? ebf : dev->ibf << 1; + bf->bw = sta->deflink.bandwidth; + bf->ibf_dbw = sta->deflink.bandwidth; + bf->ibf_nrow = tx_ant; + + if (!ebf && sta->deflink.bandwidth <= IEEE80211_STA_RX_BW_40 && !bf->ncol) + bf->ibf_timeout = 0x48; + else + bf->ibf_timeout = 0x18; + + if (ebf && bf->nrow != tx_ant) + bf->mem_20m = matrix[tx_ant][bf->ncol]; + else + bf->mem_20m = matrix[bf->nrow][bf->ncol]; + + switch (sta->deflink.bandwidth) { + case IEEE80211_STA_RX_BW_160: + case IEEE80211_STA_RX_BW_80: + bf->mem_total = bf->mem_20m * 2; + break; + case IEEE80211_STA_RX_BW_40: + bf->mem_total = bf->mem_20m; + break; + case IEEE80211_STA_RX_BW_20: + default: + break; + } +} + +static void +mt7996_mcu_sta_bfee_tlv(struct mt7996_dev *dev, struct sk_buff *skb, + struct ieee80211_vif *vif, struct ieee80211_sta *sta) +{ + struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv; + struct mt7996_phy *phy = mvif->phy; + int tx_ant = hweight8(phy->mt76->antenna_mask) - 1; + struct sta_rec_bfee *bfee; + struct tlv *tlv; + u8 nrow = 0; + + if (!(sta->deflink.vht_cap.vht_supported || sta->deflink.he_cap.has_he)) + return; + + if (!mt7996_is_ebf_supported(phy, vif, sta, true)) + return; + + tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_BFEE, sizeof(*bfee)); + bfee = (struct sta_rec_bfee *)tlv; + + if (sta->deflink.he_cap.has_he) { + struct ieee80211_he_cap_elem *pe = &sta->deflink.he_cap.he_cap_elem; + + nrow = HE_PHY(CAP5_BEAMFORMEE_NUM_SND_DIM_UNDER_80MHZ_MASK, + pe->phy_cap_info[5]); + } else if (sta->deflink.vht_cap.vht_supported) { + struct ieee80211_sta_vht_cap *pc = &sta->deflink.vht_cap; + + nrow = FIELD_GET(IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_MASK, + pc->cap); + } + + /* reply with identity matrix to avoid 2x2 BF negative gain */ + bfee->fb_identity_matrix = (nrow == 1 && tx_ant == 2); +} + +static void +mt7996_mcu_sta_phy_tlv(struct mt7996_dev *dev, struct sk_buff *skb, + struct ieee80211_vif *vif, struct ieee80211_sta *sta) +{ + struct sta_rec_phy *phy; + struct tlv *tlv; + u8 af = 0, mm = 0; + + if (!sta->deflink.ht_cap.ht_supported && !sta->deflink.he_6ghz_capa.capa) + return; + + tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_PHY, sizeof(*phy)); + + phy = (struct sta_rec_phy *)tlv; + if (sta->deflink.ht_cap.ht_supported) { + af = sta->deflink.ht_cap.ampdu_factor; + mm = sta->deflink.ht_cap.ampdu_density; + } + + if (sta->deflink.vht_cap.vht_supported) { + u8 vht_af = FIELD_GET(IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK, + sta->deflink.vht_cap.cap); + + af = max_t(u8, af, vht_af); + } + + if (sta->deflink.he_6ghz_capa.capa) { + af = le16_get_bits(sta->deflink.he_6ghz_capa.capa, + IEEE80211_HE_6GHZ_CAP_MAX_AMPDU_LEN_EXP); + mm = le16_get_bits(sta->deflink.he_6ghz_capa.capa, + IEEE80211_HE_6GHZ_CAP_MIN_MPDU_START); + } + + phy->ampdu = FIELD_PREP(IEEE80211_HT_AMPDU_PARM_FACTOR, af) | + FIELD_PREP(IEEE80211_HT_AMPDU_PARM_DENSITY, mm); + phy->max_ampdu_len = af; +} + +static void +mt7996_mcu_sta_hdrt_tlv(struct mt7996_dev *dev, struct sk_buff *skb) +{ + struct sta_rec_hdrt *hdrt; + struct tlv *tlv; + + tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_HDRT, sizeof(*hdrt)); + + hdrt = (struct sta_rec_hdrt *)tlv; + hdrt->hdrt_mode = 1; +} + +static void +mt7996_mcu_sta_hdr_trans_tlv(struct mt7996_dev *dev, struct sk_buff *skb, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + struct sta_rec_hdr_trans *hdr_trans; + struct mt76_wcid *wcid; + struct tlv *tlv; + + tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_HDR_TRANS, sizeof(*hdr_trans)); + hdr_trans = (struct sta_rec_hdr_trans *)tlv; + hdr_trans->dis_rx_hdr_tran = true; + + if (vif->type == NL80211_IFTYPE_STATION) + hdr_trans->to_ds = true; + else + hdr_trans->from_ds = true; + + wcid = (struct mt76_wcid *)sta->drv_priv; + if (!wcid) + return; + + hdr_trans->dis_rx_hdr_tran = !test_bit(MT_WCID_FLAG_HDR_TRANS, &wcid->flags); + if (test_bit(MT_WCID_FLAG_4ADDR, &wcid->flags)) { + hdr_trans->to_ds = true; + hdr_trans->from_ds = true; + } +} + +static enum mcu_mmps_mode +mt7996_mcu_get_mmps_mode(enum ieee80211_smps_mode smps) +{ + switch (smps) { + case IEEE80211_SMPS_OFF: + return MCU_MMPS_DISABLE; + case IEEE80211_SMPS_STATIC: + return MCU_MMPS_STATIC; + case IEEE80211_SMPS_DYNAMIC: + return MCU_MMPS_DYNAMIC; + default: + return MCU_MMPS_DISABLE; + } +} + +int mt7996_mcu_set_fixed_rate_ctrl(struct mt7996_dev *dev, + void *data, u16 version) +{ + struct ra_fixed_rate *req; + struct uni_header hdr; + struct sk_buff *skb; + struct tlv *tlv; + int len; + + len = sizeof(hdr) + sizeof(*req); + + skb = mt76_mcu_msg_alloc(&dev->mt76, NULL, len); + if (!skb) + return -ENOMEM; + + skb_put_data(skb, &hdr, sizeof(hdr)); + + tlv = mt7996_mcu_add_uni_tlv(skb, UNI_RA_FIXED_RATE, sizeof(*req)); + req = (struct ra_fixed_rate *)tlv; + req->version = cpu_to_le16(version); + memcpy(&req->rate, data, sizeof(req->rate)); + + return mt76_mcu_skb_send_msg(&dev->mt76, skb, + MCU_WM_UNI_CMD(RA), true); +} + +static void +mt7996_mcu_sta_rate_ctrl_tlv(struct sk_buff *skb, struct mt7996_dev *dev, + struct ieee80211_vif *vif, struct ieee80211_sta *sta) +{ + struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv; + struct mt76_phy *mphy = mvif->phy->mt76; + struct cfg80211_chan_def *chandef = &mphy->chandef; + struct cfg80211_bitrate_mask *mask = &mvif->bitrate_mask; + enum nl80211_band band = chandef->chan->band; + struct sta_rec_ra *ra; + struct tlv *tlv; + u32 supp_rate = sta->deflink.supp_rates[band]; + u32 cap = sta->wme ? STA_CAP_WMM : 0; + + tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_RA, sizeof(*ra)); + ra = (struct sta_rec_ra *)tlv; + + ra->valid = true; + ra->auto_rate = true; + ra->phy_mode = mt76_connac_get_phy_mode(mphy, vif, band, sta); + ra->channel = chandef->chan->hw_value; + ra->bw = sta->deflink.bandwidth; + ra->phy.bw = sta->deflink.bandwidth; + ra->mmps_mode = mt7996_mcu_get_mmps_mode(sta->deflink.smps_mode); + + if (supp_rate) { + supp_rate &= mask->control[band].legacy; + ra->rate_len = hweight32(supp_rate); + + if (band == NL80211_BAND_2GHZ) { + ra->supp_mode = MODE_CCK; + ra->supp_cck_rate = supp_rate & GENMASK(3, 0); + + if (ra->rate_len > 4) { + ra->supp_mode |= MODE_OFDM; + ra->supp_ofdm_rate = supp_rate >> 4; + } + } else { + ra->supp_mode = MODE_OFDM; + ra->supp_ofdm_rate = supp_rate; + } + } + + if (sta->deflink.ht_cap.ht_supported) { + ra->supp_mode |= MODE_HT; + ra->af = sta->deflink.ht_cap.ampdu_factor; + ra->ht_gf = !!(sta->deflink.ht_cap.cap & IEEE80211_HT_CAP_GRN_FLD); + + cap |= STA_CAP_HT; + if (sta->deflink.ht_cap.cap & IEEE80211_HT_CAP_SGI_20) + cap |= STA_CAP_SGI_20; + if (sta->deflink.ht_cap.cap & IEEE80211_HT_CAP_SGI_40) + cap |= STA_CAP_SGI_40; + if (sta->deflink.ht_cap.cap & IEEE80211_HT_CAP_TX_STBC) + cap |= STA_CAP_TX_STBC; + if (sta->deflink.ht_cap.cap & IEEE80211_HT_CAP_RX_STBC) + cap |= STA_CAP_RX_STBC; + if (mvif->cap.ht_ldpc && + (sta->deflink.ht_cap.cap & IEEE80211_HT_CAP_LDPC_CODING)) + cap |= STA_CAP_LDPC; + + mt7996_mcu_set_sta_ht_mcs(sta, ra->ht_mcs, + mask->control[band].ht_mcs); + ra->supp_ht_mcs = *(__le32 *)ra->ht_mcs; + } + + if (sta->deflink.vht_cap.vht_supported) { + u8 af; + + ra->supp_mode |= MODE_VHT; + af = FIELD_GET(IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK, + sta->deflink.vht_cap.cap); + ra->af = max_t(u8, ra->af, af); + + cap |= STA_CAP_VHT; + if (sta->deflink.vht_cap.cap & IEEE80211_VHT_CAP_SHORT_GI_80) + cap |= STA_CAP_VHT_SGI_80; + if (sta->deflink.vht_cap.cap & IEEE80211_VHT_CAP_SHORT_GI_160) + cap |= STA_CAP_VHT_SGI_160; + if (sta->deflink.vht_cap.cap & IEEE80211_VHT_CAP_TXSTBC) + cap |= STA_CAP_VHT_TX_STBC; + if (sta->deflink.vht_cap.cap & IEEE80211_VHT_CAP_RXSTBC_1) + cap |= STA_CAP_VHT_RX_STBC; + if (mvif->cap.vht_ldpc && + (sta->deflink.vht_cap.cap & IEEE80211_VHT_CAP_RXLDPC)) + cap |= STA_CAP_VHT_LDPC; + + mt7996_mcu_set_sta_vht_mcs(sta, ra->supp_vht_mcs, + mask->control[band].vht_mcs); + } + + if (sta->deflink.he_cap.has_he) { + ra->supp_mode |= MODE_HE; + cap |= STA_CAP_HE; + + if (sta->deflink.he_6ghz_capa.capa) + ra->af = le16_get_bits(sta->deflink.he_6ghz_capa.capa, + IEEE80211_HE_6GHZ_CAP_MAX_AMPDU_LEN_EXP); + } + ra->sta_cap = cpu_to_le32(cap); +} + +int mt7996_mcu_add_rate_ctrl(struct mt7996_dev *dev, struct ieee80211_vif *vif, + struct ieee80211_sta *sta, bool changed) +{ + struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv; + struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv; + struct sk_buff *skb; + + skb = __mt76_connac_mcu_alloc_sta_req(&dev->mt76, &mvif->mt76, + &msta->wcid, + MT7996_STA_UPDATE_MAX_SIZE); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + /* firmware rc algorithm refers to sta_rec_he for HE control. + * once dev->rc_work changes the settings driver should also + * update sta_rec_he here. + */ + if (changed) + mt7996_mcu_sta_he_tlv(skb, sta); + + /* sta_rec_ra accommodates BW, NSS and only MCS range format + * i.e 0-{7,8,9} for VHT. + */ + mt7996_mcu_sta_rate_ctrl_tlv(skb, dev, vif, sta); + + return mt76_mcu_skb_send_msg(&dev->mt76, skb, + MCU_WMWA_UNI_CMD(STA_REC_UPDATE), true); +} + +static int +mt7996_mcu_add_group(struct mt7996_dev *dev, struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ +#define MT_STA_BSS_GROUP 1 + struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv; + struct mt7996_sta *msta; + struct { + u8 __rsv1[4]; + + __le16 tag; + __le16 len; + __le16 wlan_idx; + u8 __rsv2[2]; + __le32 action; + __le32 val; + u8 __rsv3[8]; + } __packed req = { + .tag = cpu_to_le16(UNI_VOW_DRR_CTRL), + .len = cpu_to_le16(sizeof(req) - 4), + .action = cpu_to_le32(MT_STA_BSS_GROUP), + .val = cpu_to_le32(mvif->mt76.idx % 16), + }; + + msta = sta ? (struct mt7996_sta *)sta->drv_priv : &mvif->sta; + req.wlan_idx = cpu_to_le16(msta->wcid.idx); + + return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(VOW), &req, + sizeof(req), true); +} + +int mt7996_mcu_add_sta(struct mt7996_dev *dev, struct ieee80211_vif *vif, + struct ieee80211_sta *sta, bool enable) +{ + struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv; + struct mt7996_sta *msta; + struct sk_buff *skb; + int ret; + + msta = sta ? (struct mt7996_sta *)sta->drv_priv : &mvif->sta; + + skb = __mt76_connac_mcu_alloc_sta_req(&dev->mt76, &mvif->mt76, + &msta->wcid, + MT7996_STA_UPDATE_MAX_SIZE); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + /* starec basic */ + mt76_connac_mcu_sta_basic_tlv(skb, vif, sta, enable, + !rcu_access_pointer(dev->mt76.wcid[msta->wcid.idx])); + if (!enable) + goto out; + + /* tag order is in accordance with firmware dependency. */ + if (sta) { + /* starec phy */ + mt7996_mcu_sta_phy_tlv(dev, skb, vif, sta); + /* starec hdrt mode */ + mt7996_mcu_sta_hdrt_tlv(dev, skb); + /* starec bfer */ + mt7996_mcu_sta_bfer_tlv(dev, skb, vif, sta); + /* starec ht */ + mt7996_mcu_sta_ht_tlv(skb, sta); + /* starec vht */ + mt7996_mcu_sta_vht_tlv(skb, sta); + /* starec uapsd */ + mt76_connac_mcu_sta_uapsd(skb, vif, sta); + /* starec amsdu */ + mt7996_mcu_sta_amsdu_tlv(dev, skb, vif, sta); + /* starec he */ + mt7996_mcu_sta_he_tlv(skb, sta); + /* starec he 6g*/ + mt7996_mcu_sta_he_6g_tlv(skb, sta); + /* TODO: starec muru */ + /* starec bfee */ + mt7996_mcu_sta_bfee_tlv(dev, skb, vif, sta); + /* starec hdr trans */ + mt7996_mcu_sta_hdr_trans_tlv(dev, skb, vif, sta); + } + + ret = mt7996_mcu_add_group(dev, vif, sta); + if (ret) { + dev_kfree_skb(skb); + return ret; + } +out: + return mt76_mcu_skb_send_msg(&dev->mt76, skb, + MCU_WMWA_UNI_CMD(STA_REC_UPDATE), true); +} + +static int +mt7996_mcu_sta_key_tlv(struct mt76_wcid *wcid, + struct mt76_connac_sta_key_conf *sta_key_conf, + struct sk_buff *skb, + struct ieee80211_key_conf *key, + enum set_key_cmd cmd) +{ + struct sta_rec_sec_uni *sec; + struct tlv *tlv; + + tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_KEY_V2, sizeof(*sec)); + sec = (struct sta_rec_sec_uni *)tlv; + sec->add = cmd; + + if (cmd == SET_KEY) { + struct sec_key_uni *sec_key; + u8 cipher; + + cipher = mt76_connac_mcu_get_cipher(key->cipher); + if (cipher == MCU_CIPHER_NONE) + return -EOPNOTSUPP; + + sec_key = &sec->key[0]; + sec_key->cipher_len = sizeof(*sec_key); + + if (cipher == MCU_CIPHER_BIP_CMAC_128) { + sec_key->wlan_idx = cpu_to_le16(wcid->idx); + sec_key->cipher_id = MCU_CIPHER_AES_CCMP; + sec_key->key_id = sta_key_conf->keyidx; + sec_key->key_len = 16; + memcpy(sec_key->key, sta_key_conf->key, 16); + + sec_key = &sec->key[1]; + sec_key->wlan_idx = cpu_to_le16(wcid->idx); + sec_key->cipher_id = MCU_CIPHER_BIP_CMAC_128; + sec_key->cipher_len = sizeof(*sec_key); + sec_key->key_len = 16; + memcpy(sec_key->key, key->key, 16); + sec->n_cipher = 2; + } else { + sec_key->wlan_idx = cpu_to_le16(wcid->idx); + sec_key->cipher_id = cipher; + sec_key->key_id = key->keyidx; + sec_key->key_len = key->keylen; + memcpy(sec_key->key, key->key, key->keylen); + + if (cipher == MCU_CIPHER_TKIP) { + /* Rx/Tx MIC keys are swapped */ + memcpy(sec_key->key + 16, key->key + 24, 8); + memcpy(sec_key->key + 24, key->key + 16, 8); + } + + /* store key_conf for BIP batch update */ + if (cipher == MCU_CIPHER_AES_CCMP) { + memcpy(sta_key_conf->key, key->key, key->keylen); + sta_key_conf->keyidx = key->keyidx; + } + + sec->n_cipher = 1; + } + } else { + sec->n_cipher = 0; + } + + return 0; +} + +int mt7996_mcu_add_key(struct mt76_dev *dev, struct ieee80211_vif *vif, + struct mt76_connac_sta_key_conf *sta_key_conf, + struct ieee80211_key_conf *key, int mcu_cmd, + struct mt76_wcid *wcid, enum set_key_cmd cmd) +{ + struct mt76_vif *mvif = (struct mt76_vif *)vif->drv_priv; + struct sk_buff *skb; + int ret; + + skb = __mt76_connac_mcu_alloc_sta_req(dev, mvif, wcid, + MT7996_STA_UPDATE_MAX_SIZE); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + ret = mt7996_mcu_sta_key_tlv(wcid, sta_key_conf, skb, key, cmd); + if (ret) + return ret; + + return mt76_mcu_skb_send_msg(dev, skb, mcu_cmd, true); +} + +int mt7996_mcu_add_dev_info(struct mt7996_phy *phy, + struct ieee80211_vif *vif, bool enable) +{ + struct mt7996_dev *dev = phy->dev; + struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv; + struct { + struct req_hdr { + u8 omac_idx; + u8 band_idx; + u8 __rsv[2]; + } __packed hdr; + struct req_tlv { + __le16 tag; + __le16 len; + u8 active; + u8 __rsv; + u8 omac_addr[ETH_ALEN]; + } __packed tlv; + } data = { + .hdr = { + .omac_idx = mvif->mt76.omac_idx, + .band_idx = mvif->mt76.band_idx, + }, + .tlv = { + .tag = cpu_to_le16(DEV_INFO_ACTIVE), + .len = cpu_to_le16(sizeof(struct req_tlv)), + .active = enable, + }, + }; + + if (mvif->mt76.omac_idx >= REPEATER_BSSID_START) + return mt7996_mcu_muar_config(phy, vif, false, enable); + + memcpy(data.tlv.omac_addr, vif->addr, ETH_ALEN); + return mt76_mcu_send_msg(&dev->mt76, MCU_WMWA_UNI_CMD(DEV_INFO_UPDATE), + &data, sizeof(data), true); +} + +static void +mt7996_mcu_beacon_cntdwn(struct ieee80211_vif *vif, struct sk_buff *rskb, + struct sk_buff *skb, + struct ieee80211_mutable_offsets *offs) +{ + struct bss_bcn_cntdwn_tlv *info; + struct tlv *tlv; + u16 tag; + + if (!offs->cntdwn_counter_offs[0]) + return; + + tag = vif->bss_conf.csa_active ? UNI_BSS_INFO_BCN_CSA : UNI_BSS_INFO_BCN_BCC; + + tlv = mt7996_mcu_add_uni_tlv(rskb, tag, sizeof(*info)); + + info = (struct bss_bcn_cntdwn_tlv *)tlv; + info->cnt = skb->data[offs->cntdwn_counter_offs[0]]; +} + +static void +mt7996_mcu_beacon_cont(struct mt7996_dev *dev, struct ieee80211_vif *vif, + struct sk_buff *rskb, struct sk_buff *skb, + struct bss_bcn_content_tlv *bcn, + struct ieee80211_mutable_offsets *offs) +{ + struct mt76_wcid *wcid = &dev->mt76.global_wcid; + u8 *buf; + + bcn->pkt_len = cpu_to_le16(MT_TXD_SIZE + skb->len); + bcn->tim_ie_pos = cpu_to_le16(offs->tim_offset); + + if (offs->cntdwn_counter_offs[0]) { + u16 offset = offs->cntdwn_counter_offs[0]; + + if (vif->bss_conf.csa_active) + bcn->csa_ie_pos = cpu_to_le16(offset - 4); + if (vif->bss_conf.color_change_active) + bcn->bcc_ie_pos = cpu_to_le16(offset - 3); + } + + buf = (u8 *)bcn + sizeof(*bcn) - MAX_BEACON_SIZE; + mt7996_mac_write_txwi(dev, (__le32 *)buf, skb, wcid, 0, NULL, + BSS_CHANGED_BEACON); + memcpy(buf + MT_TXD_SIZE, skb->data, skb->len); +} + +static void +mt7996_mcu_beacon_check_caps(struct mt7996_phy *phy, struct ieee80211_vif *vif, + struct sk_buff *skb) +{ + struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv; + struct mt7996_vif_cap *vc = &mvif->cap; + const struct ieee80211_he_cap_elem *he; + const struct ieee80211_vht_cap *vht; + const struct ieee80211_ht_cap *ht; + struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)skb->data; + const u8 *ie; + u32 len, bc; + + /* Check missing configuration options to allow AP mode in mac80211 + * to remain in sync with hostapd settings, and get a subset of + * beacon and hardware capabilities. + */ + if (WARN_ON_ONCE(skb->len <= (mgmt->u.beacon.variable - skb->data))) + return; + + memset(vc, 0, sizeof(*vc)); + + len = skb->len - (mgmt->u.beacon.variable - skb->data); + + ie = cfg80211_find_ie(WLAN_EID_HT_CAPABILITY, mgmt->u.beacon.variable, + len); + if (ie && ie[1] >= sizeof(*ht)) { + ht = (void *)(ie + 2); + vc->ht_ldpc |= !!(le16_to_cpu(ht->cap_info) & + IEEE80211_HT_CAP_LDPC_CODING); + } + + ie = cfg80211_find_ie(WLAN_EID_VHT_CAPABILITY, mgmt->u.beacon.variable, + len); + if (ie && ie[1] >= sizeof(*vht)) { + u32 pc = phy->mt76->sband_5g.sband.vht_cap.cap; + + vht = (void *)(ie + 2); + bc = le32_to_cpu(vht->vht_cap_info); + + vc->vht_ldpc |= !!(bc & IEEE80211_VHT_CAP_RXLDPC); + vc->vht_su_ebfer = + (bc & IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE) && + (pc & IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE); + vc->vht_su_ebfee = + (bc & IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE) && + (pc & IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE); + vc->vht_mu_ebfer = + (bc & IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE) && + (pc & IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE); + vc->vht_mu_ebfee = + (bc & IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE) && + (pc & IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE); + } + + ie = cfg80211_find_ext_ie(WLAN_EID_EXT_HE_CAPABILITY, + mgmt->u.beacon.variable, len); + if (ie && ie[1] >= sizeof(*he) + 1) { + const struct ieee80211_sta_he_cap *pc = + mt76_connac_get_he_phy_cap(phy->mt76, vif); + const struct ieee80211_he_cap_elem *pe = &pc->he_cap_elem; + + he = (void *)(ie + 3); + + vc->he_ldpc = + HE_PHY(CAP1_LDPC_CODING_IN_PAYLOAD, pe->phy_cap_info[1]); + vc->he_su_ebfer = + HE_PHY(CAP3_SU_BEAMFORMER, he->phy_cap_info[3]) && + HE_PHY(CAP3_SU_BEAMFORMER, pe->phy_cap_info[3]); + vc->he_su_ebfee = + HE_PHY(CAP4_SU_BEAMFORMEE, he->phy_cap_info[4]) && + HE_PHY(CAP4_SU_BEAMFORMEE, pe->phy_cap_info[4]); + vc->he_mu_ebfer = + HE_PHY(CAP4_MU_BEAMFORMER, he->phy_cap_info[4]) && + HE_PHY(CAP4_MU_BEAMFORMER, pe->phy_cap_info[4]); + } +} + +int mt7996_mcu_add_beacon(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, int en) +{ + struct mt7996_dev *dev = mt7996_hw_dev(hw); + struct mt7996_phy *phy = mt7996_hw_phy(hw); + struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv; + struct ieee80211_mutable_offsets offs; + struct ieee80211_tx_info *info; + struct sk_buff *skb, *rskb; + struct tlv *tlv; + struct bss_bcn_content_tlv *bcn; + + rskb = __mt7996_mcu_alloc_bss_req(&dev->mt76, &mvif->mt76, + MT7996_BEACON_UPDATE_SIZE); + if (IS_ERR(rskb)) + return PTR_ERR(rskb); + + tlv = mt7996_mcu_add_uni_tlv(rskb, + UNI_BSS_INFO_BCN_CONTENT, sizeof(*bcn)); + bcn = (struct bss_bcn_content_tlv *)tlv; + bcn->enable = en; + + if (!en) + goto out; + + skb = ieee80211_beacon_get_template(hw, vif, &offs, 0); + if (!skb) + return -EINVAL; + + if (skb->len > MAX_BEACON_SIZE - MT_TXD_SIZE) { + dev_err(dev->mt76.dev, "Bcn size limit exceed\n"); + dev_kfree_skb(skb); + return -EINVAL; + } + + info = IEEE80211_SKB_CB(skb); + info->hw_queue |= FIELD_PREP(MT_TX_HW_QUEUE_PHY, phy->mt76->band_idx); + + mt7996_mcu_beacon_check_caps(phy, vif, skb); + + mt7996_mcu_beacon_cont(dev, vif, rskb, skb, bcn, &offs); + /* TODO: subtag - 11v MBSSID */ + mt7996_mcu_beacon_cntdwn(vif, rskb, skb, &offs); + dev_kfree_skb(skb); +out: + return mt76_mcu_skb_send_msg(&phy->dev->mt76, rskb, + MCU_WMWA_UNI_CMD(BSS_INFO_UPDATE), true); +} + +int mt7996_mcu_beacon_inband_discov(struct mt7996_dev *dev, + struct ieee80211_vif *vif, u32 changed) +{ +#define OFFLOAD_TX_MODE_SU BIT(0) +#define OFFLOAD_TX_MODE_MU BIT(1) + struct ieee80211_hw *hw = mt76_hw(dev); + struct mt7996_phy *phy = mt7996_hw_phy(hw); + struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv; + struct cfg80211_chan_def *chandef = &mvif->phy->mt76->chandef; + enum nl80211_band band = chandef->chan->band; + struct mt76_wcid *wcid = &dev->mt76.global_wcid; + struct bss_inband_discovery_tlv *discov; + struct ieee80211_tx_info *info; + struct sk_buff *rskb, *skb = NULL; + struct tlv *tlv; + u8 *buf, interval; + + rskb = __mt7996_mcu_alloc_bss_req(&dev->mt76, &mvif->mt76, + MT7996_INBAND_FRAME_SIZE); + if (IS_ERR(rskb)) + return PTR_ERR(rskb); + + if (changed & BSS_CHANGED_FILS_DISCOVERY && + vif->bss_conf.fils_discovery.max_interval) { + interval = vif->bss_conf.fils_discovery.max_interval; + skb = ieee80211_get_fils_discovery_tmpl(hw, vif); + } else if (changed & BSS_CHANGED_UNSOL_BCAST_PROBE_RESP && + vif->bss_conf.unsol_bcast_probe_resp_interval) { + interval = vif->bss_conf.unsol_bcast_probe_resp_interval; + skb = ieee80211_get_unsol_bcast_probe_resp_tmpl(hw, vif); + } + + if (!skb) + return -EINVAL; + + if (skb->len > MAX_INBAND_FRAME_SIZE - MT_TXD_SIZE) { + dev_err(dev->mt76.dev, "inband discovery size limit exceed\n"); + dev_kfree_skb(skb); + return -EINVAL; + } + + info = IEEE80211_SKB_CB(skb); + info->control.vif = vif; + info->band = band; + info->hw_queue |= FIELD_PREP(MT_TX_HW_QUEUE_PHY, phy->mt76->band_idx); + + tlv = mt7996_mcu_add_uni_tlv(rskb, UNI_BSS_INFO_OFFLOAD, sizeof(*discov)); + + discov = (struct bss_inband_discovery_tlv *)tlv; + discov->tx_mode = OFFLOAD_TX_MODE_SU; + /* 0: UNSOL PROBE RESP, 1: FILS DISCOV */ + discov->tx_type = !!(changed & BSS_CHANGED_FILS_DISCOVERY); + discov->tx_interval = interval; + discov->prob_rsp_len = cpu_to_le16(MT_TXD_SIZE + skb->len); + discov->enable = true; + discov->wcid = cpu_to_le16(MT7996_WTBL_RESERVED); + + buf = (u8 *)tlv + sizeof(*discov) - MAX_INBAND_FRAME_SIZE; + + mt7996_mac_write_txwi(dev, (__le32 *)buf, skb, wcid, 0, NULL, + changed); + + memcpy(buf + MT_TXD_SIZE, skb->data, skb->len); + + dev_kfree_skb(skb); + + return mt76_mcu_skb_send_msg(&dev->mt76, rskb, + MCU_WMWA_UNI_CMD(BSS_INFO_UPDATE), true); +} + +static int mt7996_driver_own(struct mt7996_dev *dev, u8 band) +{ + mt76_wr(dev, MT_TOP_LPCR_HOST_BAND(band), MT_TOP_LPCR_HOST_DRV_OWN); + if (!mt76_poll_msec(dev, MT_TOP_LPCR_HOST_BAND(band), + MT_TOP_LPCR_HOST_FW_OWN_STAT, 0, 500)) { + dev_err(dev->mt76.dev, "Timeout for driver own\n"); + return -EIO; + } + + /* clear irq when the driver own success */ + mt76_wr(dev, MT_TOP_LPCR_HOST_BAND_IRQ_STAT(band), + MT_TOP_LPCR_HOST_BAND_STAT); + + return 0; +} + +static u32 mt7996_patch_sec_mode(u32 key_info) +{ + u32 sec = u32_get_bits(key_info, MT7996_PATCH_SEC), key = 0; + + if (key_info == GENMASK(31, 0) || sec == MT7996_SEC_MODE_PLAIN) + return 0; + + if (sec == MT7996_SEC_MODE_AES) + key = u32_get_bits(key_info, MT7996_PATCH_AES_KEY); + else + key = u32_get_bits(key_info, MT7996_PATCH_SCRAMBLE_KEY); + + return MT7996_SEC_ENCRYPT | MT7996_SEC_IV | + u32_encode_bits(key, MT7996_SEC_KEY_IDX); +} + +static int mt7996_load_patch(struct mt7996_dev *dev) +{ + const struct mt7996_patch_hdr *hdr; + const struct firmware *fw = NULL; + int i, ret, sem; + + sem = mt76_connac_mcu_patch_sem_ctrl(&dev->mt76, 1); + switch (sem) { + case PATCH_IS_DL: + return 0; + case PATCH_NOT_DL_SEM_SUCCESS: + break; + default: + dev_err(dev->mt76.dev, "Failed to get patch semaphore\n"); + return -EAGAIN; + } + + ret = request_firmware(&fw, MT7996_ROM_PATCH, dev->mt76.dev); + if (ret) + goto out; + + if (!fw || !fw->data || fw->size < sizeof(*hdr)) { + dev_err(dev->mt76.dev, "Invalid firmware\n"); + ret = -EINVAL; + goto out; + } + + hdr = (const struct mt7996_patch_hdr *)(fw->data); + + dev_info(dev->mt76.dev, "HW/SW Version: 0x%x, Build Time: %.16s\n", + be32_to_cpu(hdr->hw_sw_ver), hdr->build_date); + + for (i = 0; i < be32_to_cpu(hdr->desc.n_region); i++) { + struct mt7996_patch_sec *sec; + const u8 *dl; + u32 len, addr, sec_key_idx, mode = DL_MODE_NEED_RSP; + + sec = (struct mt7996_patch_sec *)(fw->data + sizeof(*hdr) + + i * sizeof(*sec)); + if ((be32_to_cpu(sec->type) & PATCH_SEC_TYPE_MASK) != + PATCH_SEC_TYPE_INFO) { + ret = -EINVAL; + goto out; + } + + addr = be32_to_cpu(sec->info.addr); + len = be32_to_cpu(sec->info.len); + sec_key_idx = be32_to_cpu(sec->info.sec_key_idx); + dl = fw->data + be32_to_cpu(sec->offs); + + mode |= mt7996_patch_sec_mode(sec_key_idx); + + ret = mt76_connac_mcu_init_download(&dev->mt76, addr, len, + mode); + if (ret) { + dev_err(dev->mt76.dev, "Download request failed\n"); + goto out; + } + + ret = __mt76_mcu_send_firmware(&dev->mt76, MCU_CMD(FW_SCATTER), + dl, len, 4096); + if (ret) { + dev_err(dev->mt76.dev, "Failed to send patch\n"); + goto out; + } + } + + ret = mt76_connac_mcu_start_patch(&dev->mt76); + if (ret) + dev_err(dev->mt76.dev, "Failed to start patch\n"); + +out: + sem = mt76_connac_mcu_patch_sem_ctrl(&dev->mt76, 0); + switch (sem) { + case PATCH_REL_SEM_SUCCESS: + break; + default: + ret = -EAGAIN; + dev_err(dev->mt76.dev, "Failed to release patch semaphore\n"); + break; + } + release_firmware(fw); + + return ret; +} + +static int +mt7996_mcu_send_ram_firmware(struct mt7996_dev *dev, + const struct mt7996_fw_trailer *hdr, + const u8 *data, bool is_wa) +{ + int i, offset = 0; + u32 override = 0, option = 0; + + for (i = 0; i < hdr->n_region; i++) { + const struct mt7996_fw_region *region; + int err; + u32 len, addr, mode; + + region = (const struct mt7996_fw_region *)((const u8 *)hdr - + (hdr->n_region - i) * sizeof(*region)); + mode = mt76_connac_mcu_gen_dl_mode(&dev->mt76, + region->feature_set, is_wa); + len = le32_to_cpu(region->len); + addr = le32_to_cpu(region->addr); + + if (region->feature_set & FW_FEATURE_OVERRIDE_ADDR) + override = addr; + + err = mt76_connac_mcu_init_download(&dev->mt76, addr, len, + mode); + if (err) { + dev_err(dev->mt76.dev, "Download request failed\n"); + return err; + } + + err = __mt76_mcu_send_firmware(&dev->mt76, MCU_CMD(FW_SCATTER), + data + offset, len, 4096); + if (err) { + dev_err(dev->mt76.dev, "Failed to send firmware.\n"); + return err; + } + + offset += len; + } + + if (override) + option |= FW_START_OVERRIDE; + + if (is_wa) + option |= FW_START_WORKING_PDA_CR4; + + return mt76_connac_mcu_start_firmware(&dev->mt76, override, option); +} + +static int mt7996_load_ram(struct mt7996_dev *dev) +{ + const struct mt7996_fw_trailer *hdr; + const struct firmware *fw; + int ret; + + ret = request_firmware(&fw, MT7996_FIRMWARE_WM, dev->mt76.dev); + if (ret) + return ret; + + if (!fw || !fw->data || fw->size < sizeof(*hdr)) { + dev_err(dev->mt76.dev, "Invalid firmware\n"); + ret = -EINVAL; + goto out; + } + + hdr = (const struct mt7996_fw_trailer *)(fw->data + fw->size - sizeof(*hdr)); + + dev_info(dev->mt76.dev, "WM Firmware Version: %.10s, Build Time: %.15s\n", + hdr->fw_ver, hdr->build_date); + + ret = mt7996_mcu_send_ram_firmware(dev, hdr, fw->data, false); + if (ret) { + dev_err(dev->mt76.dev, "Failed to start WM firmware\n"); + goto out; + } + + release_firmware(fw); + + ret = request_firmware(&fw, MT7996_FIRMWARE_WA, dev->mt76.dev); + if (ret) + return ret; + + if (!fw || !fw->data || fw->size < sizeof(*hdr)) { + dev_err(dev->mt76.dev, "Invalid firmware\n"); + ret = -EINVAL; + goto out; + } + + hdr = (const struct mt7996_fw_trailer *)(fw->data + fw->size - sizeof(*hdr)); + + dev_info(dev->mt76.dev, "WA Firmware Version: %.10s, Build Time: %.15s\n", + hdr->fw_ver, hdr->build_date); + + ret = mt7996_mcu_send_ram_firmware(dev, hdr, fw->data, true); + if (ret) { + dev_err(dev->mt76.dev, "Failed to start WA firmware\n"); + goto out; + } + + snprintf(dev->mt76.hw->wiphy->fw_version, + sizeof(dev->mt76.hw->wiphy->fw_version), + "%.10s-%.15s", hdr->fw_ver, hdr->build_date); + +out: + release_firmware(fw); + + return ret; +} + +static int +mt7996_firmware_state(struct mt7996_dev *dev, bool wa) +{ + u32 state = FIELD_PREP(MT_TOP_MISC_FW_STATE, + wa ? FW_STATE_RDY : FW_STATE_FW_DOWNLOAD); + + if (!mt76_poll_msec(dev, MT_TOP_MISC, MT_TOP_MISC_FW_STATE, + state, 1000)) { + dev_err(dev->mt76.dev, "Timeout for initializing firmware\n"); + return -EIO; + } + return 0; +} + +static int mt7996_load_firmware(struct mt7996_dev *dev) +{ + int ret; + + /* make sure fw is download state */ + if (mt7996_firmware_state(dev, false)) { + /* restart firmware once */ + __mt76_mcu_restart(&dev->mt76); + ret = mt7996_firmware_state(dev, false); + if (ret) { + dev_err(dev->mt76.dev, + "Firmware is not ready for download\n"); + return ret; + } + } + + ret = mt7996_load_patch(dev); + if (ret) + return ret; + + ret = mt7996_load_ram(dev); + if (ret) + return ret; + + ret = mt7996_firmware_state(dev, true); + if (ret) + return ret; + + mt76_queue_tx_cleanup(dev, dev->mt76.q_mcu[MT_MCUQ_FWDL], false); + + dev_dbg(dev->mt76.dev, "Firmware init done\n"); + + return 0; +} + +int mt7996_mcu_fw_log_2_host(struct mt7996_dev *dev, u8 type, u8 ctrl) +{ + struct { + u8 _rsv[4]; + + __le16 tag; + __le16 len; + u8 ctrl; + u8 interval; + u8 _rsv2[2]; + } __packed data = { + .tag = cpu_to_le16(UNI_WSYS_CONFIG_FW_LOG_CTRL), + .len = cpu_to_le16(sizeof(data) - 4), + .ctrl = ctrl, + }; + + if (type == MCU_FW_LOG_WA) + return mt76_mcu_send_msg(&dev->mt76, MCU_WA_UNI_CMD(WSYS_CONFIG), + &data, sizeof(data), true); + + return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(WSYS_CONFIG), &data, + sizeof(data), true); +} + +int mt7996_mcu_fw_dbg_ctrl(struct mt7996_dev *dev, u32 module, u8 level) +{ + struct { + u8 _rsv[4]; + + __le16 tag; + __le16 len; + __le32 module_idx; + u8 level; + u8 _rsv2[3]; + } data = { + .tag = cpu_to_le16(UNI_WSYS_CONFIG_FW_DBG_CTRL), + .len = cpu_to_le16(sizeof(data) - 4), + .module_idx = cpu_to_le32(module), + .level = level, + }; + + return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(WSYS_CONFIG), &data, + sizeof(data), false); +} + +static int mt7996_mcu_set_mwds(struct mt7996_dev *dev, bool enabled) +{ + struct { + u8 enable; + u8 _rsv[3]; + } __packed req = { + .enable = enabled + }; + + return mt76_mcu_send_msg(&dev->mt76, MCU_WA_EXT_CMD(MWDS_SUPPORT), &req, + sizeof(req), false); +} + +static void mt7996_add_rx_airtime_tlv(struct sk_buff *skb, u8 band_idx) +{ + struct vow_rx_airtime *req; + struct tlv *tlv; + + tlv = mt7996_mcu_add_uni_tlv(skb, UNI_VOW_RX_AT_AIRTIME_CLR_EN, sizeof(*req)); + req = (struct vow_rx_airtime *)tlv; + req->enable = true; + req->band = band_idx; + + tlv = mt7996_mcu_add_uni_tlv(skb, UNI_VOW_RX_AT_AIRTIME_EN, sizeof(*req)); + req = (struct vow_rx_airtime *)tlv; + req->enable = true; + req->band = band_idx; +} + +static int +mt7996_mcu_init_rx_airtime(struct mt7996_dev *dev) +{ + struct uni_header hdr = {}; + struct sk_buff *skb; + int len, num; + + num = 2 + 2 * (dev->dbdc_support + dev->tbtc_support); + len = sizeof(hdr) + num * sizeof(struct vow_rx_airtime); + skb = mt76_mcu_msg_alloc(&dev->mt76, NULL, len); + if (!skb) + return -ENOMEM; + + skb_put_data(skb, &hdr, sizeof(hdr)); + + mt7996_add_rx_airtime_tlv(skb, dev->mt76.phy.band_idx); + + if (dev->dbdc_support) + mt7996_add_rx_airtime_tlv(skb, MT_BAND1); + + if (dev->tbtc_support) + mt7996_add_rx_airtime_tlv(skb, MT_BAND2); + + return mt76_mcu_skb_send_msg(&dev->mt76, skb, + MCU_WM_UNI_CMD(VOW), true); +} + +static int +mt7996_mcu_restart(struct mt76_dev *dev) +{ + struct { + u8 __rsv1[4]; + + __le16 tag; + __le16 len; + u8 power_mode; + u8 __rsv2[3]; + } __packed req = { + .tag = cpu_to_le16(UNI_POWER_OFF), + .len = cpu_to_le16(sizeof(req) - 4), + .power_mode = 1, + }; + + return mt76_mcu_send_msg(dev, MCU_WM_UNI_CMD(POWER_CREL), &req, + sizeof(req), false); +} + +int mt7996_mcu_init(struct mt7996_dev *dev) +{ + static const struct mt76_mcu_ops mt7996_mcu_ops = { + .headroom = sizeof(struct mt76_connac2_mcu_txd), /* reuse */ + .mcu_skb_send_msg = mt7996_mcu_send_message, + .mcu_parse_response = mt7996_mcu_parse_response, + .mcu_restart = mt7996_mcu_restart, + }; + int ret; + + dev->mt76.mcu_ops = &mt7996_mcu_ops; + + /* force firmware operation mode into normal state, + * which should be set before firmware download stage. + */ + mt76_wr(dev, MT_SWDEF_MODE, MT_SWDEF_NORMAL_MODE); + + ret = mt7996_driver_own(dev, 0); + if (ret) + return ret; + /* set driver own for band1 when two hif exist */ + if (dev->hif2) { + ret = mt7996_driver_own(dev, 1); + if (ret) + return ret; + } + + ret = mt7996_load_firmware(dev); + if (ret) + return ret; + + set_bit(MT76_STATE_MCU_RUNNING, &dev->mphy.state); + ret = mt7996_mcu_fw_log_2_host(dev, MCU_FW_LOG_WM, 0); + if (ret) + return ret; + + ret = mt7996_mcu_fw_log_2_host(dev, MCU_FW_LOG_WA, 0); + if (ret) + return ret; + + ret = mt7996_mcu_set_mwds(dev, 1); + if (ret) + return ret; + + ret = mt7996_mcu_init_rx_airtime(dev); + if (ret) + return ret; + + return mt7996_mcu_wa_cmd(dev, MCU_WA_PARAM_CMD(SET), + MCU_WA_PARAM_RED, 0, 0); +} + +void mt7996_mcu_exit(struct mt7996_dev *dev) +{ + __mt76_mcu_restart(&dev->mt76); + if (mt7996_firmware_state(dev, false)) { + dev_err(dev->mt76.dev, "Failed to exit mcu\n"); + return; + } + + mt76_wr(dev, MT_TOP_LPCR_HOST_BAND(0), MT_TOP_LPCR_HOST_FW_OWN); + if (dev->hif2) + mt76_wr(dev, MT_TOP_LPCR_HOST_BAND(1), + MT_TOP_LPCR_HOST_FW_OWN); + skb_queue_purge(&dev->mt76.mcu.res_q); +} + +int mt7996_mcu_set_hdr_trans(struct mt7996_dev *dev, bool hdr_trans) +{ + struct { + u8 __rsv[4]; + } __packed hdr; + struct hdr_trans_blacklist *req_blacklist; + struct hdr_trans_en *req_en; + struct sk_buff *skb; + struct tlv *tlv; + int len = MT7996_HDR_TRANS_MAX_SIZE + sizeof(hdr); + + skb = mt76_mcu_msg_alloc(&dev->mt76, NULL, len); + if (!skb) + return -ENOMEM; + + skb_put_data(skb, &hdr, sizeof(hdr)); + + tlv = mt7996_mcu_add_uni_tlv(skb, UNI_HDR_TRANS_EN, sizeof(*req_en)); + req_en = (struct hdr_trans_en *)tlv; + req_en->enable = hdr_trans; + + tlv = mt7996_mcu_add_uni_tlv(skb, UNI_HDR_TRANS_VLAN, + sizeof(struct hdr_trans_vlan)); + + if (hdr_trans) { + tlv = mt7996_mcu_add_uni_tlv(skb, UNI_HDR_TRANS_BLACKLIST, + sizeof(*req_blacklist)); + req_blacklist = (struct hdr_trans_blacklist *)tlv; + req_blacklist->enable = 1; + req_blacklist->type = cpu_to_le16(ETH_P_PAE); + } + + return mt76_mcu_skb_send_msg(&dev->mt76, skb, + MCU_WM_UNI_CMD(RX_HDR_TRANS), true); +} + +int mt7996_mcu_set_tx(struct mt7996_dev *dev, struct ieee80211_vif *vif) +{ +#define MCU_EDCA_AC_PARAM 0 +#define WMM_AIFS_SET BIT(0) +#define WMM_CW_MIN_SET BIT(1) +#define WMM_CW_MAX_SET BIT(2) +#define WMM_TXOP_SET BIT(3) +#define WMM_PARAM_SET (WMM_AIFS_SET | WMM_CW_MIN_SET | \ + WMM_CW_MAX_SET | WMM_TXOP_SET) + struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv; + struct { + u8 bss_idx; + u8 __rsv[3]; + } __packed hdr = { + .bss_idx = mvif->mt76.idx, + }; + struct sk_buff *skb; + int len = sizeof(hdr) + IEEE80211_NUM_ACS * sizeof(struct edca); + int ac; + + skb = mt76_mcu_msg_alloc(&dev->mt76, NULL, len); + if (!skb) + return -ENOMEM; + + skb_put_data(skb, &hdr, sizeof(hdr)); + + for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) { + struct ieee80211_tx_queue_params *q = &mvif->queue_params[ac]; + struct edca *e; + struct tlv *tlv; + + tlv = mt7996_mcu_add_uni_tlv(skb, MCU_EDCA_AC_PARAM, sizeof(*e)); + + e = (struct edca *)tlv; + e->set = WMM_PARAM_SET; + e->queue = ac + mvif->mt76.wmm_idx * MT7996_MAX_WMM_SETS; + e->aifs = q->aifs; + e->txop = cpu_to_le16(q->txop); + + if (q->cw_min) + e->cw_min = fls(q->cw_min); + else + e->cw_min = 5; + + if (q->cw_max) + e->cw_max = fls(q->cw_max); + else + e->cw_max = 10; + } + + return mt76_mcu_skb_send_msg(&dev->mt76, skb, + MCU_WM_UNI_CMD(EDCA_UPDATE), true); +} + +int mt7996_mcu_set_fcc5_lpn(struct mt7996_dev *dev, int val) +{ + struct { + u8 _rsv[4]; + + __le16 tag; + __le16 len; + + __le32 ctrl; + __le16 min_lpn; + u8 rsv[2]; + } __packed req = { + .tag = cpu_to_le16(UNI_RDD_CTRL_SET_TH), + .len = cpu_to_le16(sizeof(req) - 4), + + .ctrl = cpu_to_le32(0x1), + .min_lpn = cpu_to_le16(val), + }; + + return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(RDD_CTRL), + &req, sizeof(req), true); +} + +int mt7996_mcu_set_pulse_th(struct mt7996_dev *dev, + const struct mt7996_dfs_pulse *pulse) +{ + struct { + u8 _rsv[4]; + + __le16 tag; + __le16 len; + + __le32 ctrl; + + __le32 max_width; /* us */ + __le32 max_pwr; /* dbm */ + __le32 min_pwr; /* dbm */ + __le32 min_stgr_pri; /* us */ + __le32 max_stgr_pri; /* us */ + __le32 min_cr_pri; /* us */ + __le32 max_cr_pri; /* us */ + } __packed req = { + .tag = cpu_to_le16(UNI_RDD_CTRL_SET_TH), + .len = cpu_to_le16(sizeof(req) - 4), + + .ctrl = cpu_to_le32(0x3), + +#define __req_field(field) .field = cpu_to_le32(pulse->field) + __req_field(max_width), + __req_field(max_pwr), + __req_field(min_pwr), + __req_field(min_stgr_pri), + __req_field(max_stgr_pri), + __req_field(min_cr_pri), + __req_field(max_cr_pri), +#undef __req_field + }; + + return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(RDD_CTRL), + &req, sizeof(req), true); +} + +int mt7996_mcu_set_radar_th(struct mt7996_dev *dev, int index, + const struct mt7996_dfs_pattern *pattern) +{ + struct { + u8 _rsv[4]; + + __le16 tag; + __le16 len; + + __le32 ctrl; + __le16 radar_type; + + u8 enb; + u8 stgr; + u8 min_crpn; + u8 max_crpn; + u8 min_crpr; + u8 min_pw; + __le32 min_pri; + __le32 max_pri; + u8 max_pw; + u8 min_crbn; + u8 max_crbn; + u8 min_stgpn; + u8 max_stgpn; + u8 min_stgpr; + u8 rsv[2]; + __le32 min_stgpr_diff; + } __packed req = { + .tag = cpu_to_le16(UNI_RDD_CTRL_SET_TH), + .len = cpu_to_le16(sizeof(req) - 4), + + .ctrl = cpu_to_le32(0x2), + .radar_type = cpu_to_le16(index), + +#define __req_field_u8(field) .field = pattern->field +#define __req_field_u32(field) .field = cpu_to_le32(pattern->field) + __req_field_u8(enb), + __req_field_u8(stgr), + __req_field_u8(min_crpn), + __req_field_u8(max_crpn), + __req_field_u8(min_crpr), + __req_field_u8(min_pw), + __req_field_u32(min_pri), + __req_field_u32(max_pri), + __req_field_u8(max_pw), + __req_field_u8(min_crbn), + __req_field_u8(max_crbn), + __req_field_u8(min_stgpn), + __req_field_u8(max_stgpn), + __req_field_u8(min_stgpr), + __req_field_u32(min_stgpr_diff), +#undef __req_field_u8 +#undef __req_field_u32 + }; + + return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(RDD_CTRL), + &req, sizeof(req), true); +} + +static int +mt7996_mcu_background_chain_ctrl(struct mt7996_phy *phy, + struct cfg80211_chan_def *chandef, + int cmd) +{ + struct mt7996_dev *dev = phy->dev; + struct mt76_phy *mphy = phy->mt76; + struct ieee80211_channel *chan = mphy->chandef.chan; + int freq = mphy->chandef.center_freq1; + struct mt7996_mcu_background_chain_ctrl req = { + .tag = cpu_to_le16(0), + .len = cpu_to_le16(sizeof(req) - 4), + .monitor_scan_type = 2, /* simple rx */ + }; + + if (!chandef && cmd != CH_SWITCH_BACKGROUND_SCAN_STOP) + return -EINVAL; + + if (!cfg80211_chandef_valid(&mphy->chandef)) + return -EINVAL; + + switch (cmd) { + case CH_SWITCH_BACKGROUND_SCAN_START: { + req.chan = chan->hw_value; + req.central_chan = ieee80211_frequency_to_channel(freq); + req.bw = mt76_connac_chan_bw(&mphy->chandef); + req.monitor_chan = chandef->chan->hw_value; + req.monitor_central_chan = + ieee80211_frequency_to_channel(chandef->center_freq1); + req.monitor_bw = mt76_connac_chan_bw(chandef); + req.band_idx = phy->mt76->band_idx; + req.scan_mode = 1; + break; + } + case CH_SWITCH_BACKGROUND_SCAN_RUNNING: + req.monitor_chan = chandef->chan->hw_value; + req.monitor_central_chan = + ieee80211_frequency_to_channel(chandef->center_freq1); + req.band_idx = phy->mt76->band_idx; + req.scan_mode = 2; + break; + case CH_SWITCH_BACKGROUND_SCAN_STOP: + req.chan = chan->hw_value; + req.central_chan = ieee80211_frequency_to_channel(freq); + req.bw = mt76_connac_chan_bw(&mphy->chandef); + req.tx_stream = hweight8(mphy->antenna_mask); + req.rx_stream = mphy->antenna_mask; + break; + default: + return -EINVAL; + } + req.band = chandef ? chandef->chan->band == NL80211_BAND_5GHZ : 1; + + return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(OFFCH_SCAN_CTRL), + &req, sizeof(req), false); +} + +int mt7996_mcu_rdd_background_enable(struct mt7996_phy *phy, + struct cfg80211_chan_def *chandef) +{ + struct mt7996_dev *dev = phy->dev; + int err, region; + + if (!chandef) { /* disable offchain */ + err = mt7996_mcu_rdd_cmd(dev, RDD_STOP, MT_RX_SEL2, + 0, 0); + if (err) + return err; + + return mt7996_mcu_background_chain_ctrl(phy, NULL, + CH_SWITCH_BACKGROUND_SCAN_STOP); + } + + err = mt7996_mcu_background_chain_ctrl(phy, chandef, + CH_SWITCH_BACKGROUND_SCAN_START); + if (err) + return err; + + switch (dev->mt76.region) { + case NL80211_DFS_ETSI: + region = 0; + break; + case NL80211_DFS_JP: + region = 2; + break; + case NL80211_DFS_FCC: + default: + region = 1; + break; + } + + return mt7996_mcu_rdd_cmd(dev, RDD_START, MT_RX_SEL2, + 0, region); +} + +int mt7996_mcu_set_chan_info(struct mt7996_phy *phy, u16 tag) +{ + static const u8 ch_band[] = { + [NL80211_BAND_2GHZ] = 0, + [NL80211_BAND_5GHZ] = 1, + [NL80211_BAND_6GHZ] = 2, + }; + struct mt7996_dev *dev = phy->dev; + struct cfg80211_chan_def *chandef = &phy->mt76->chandef; + int freq1 = chandef->center_freq1; + u8 band_idx = phy->mt76->band_idx; + struct { + /* fixed field */ + u8 __rsv[4]; + + __le16 tag; + __le16 len; + u8 control_ch; + u8 center_ch; + u8 bw; + u8 tx_path_num; + u8 rx_path; /* mask or num */ + u8 switch_reason; + u8 band_idx; + u8 center_ch2; /* for 80+80 only */ + __le16 cac_case; + u8 channel_band; + u8 rsv0; + __le32 outband_freq; + u8 txpower_drop; + u8 ap_bw; + u8 ap_center_ch; + u8 rsv1[53]; + } __packed req = { + .tag = cpu_to_le16(tag), + .len = cpu_to_le16(sizeof(req) - 4), + .control_ch = chandef->chan->hw_value, + .center_ch = ieee80211_frequency_to_channel(freq1), + .bw = mt76_connac_chan_bw(chandef), + .tx_path_num = hweight16(phy->mt76->chainmask), + .rx_path = phy->mt76->chainmask >> dev->chainshift[band_idx], + .band_idx = band_idx, + .channel_band = ch_band[chandef->chan->band], + }; + + if (tag == UNI_CHANNEL_RX_PATH || + dev->mt76.hw->conf.flags & IEEE80211_CONF_MONITOR) + req.switch_reason = CH_SWITCH_NORMAL; + else if (phy->mt76->hw->conf.flags & IEEE80211_CONF_OFFCHANNEL) + req.switch_reason = CH_SWITCH_SCAN_BYPASS_DPD; + else if (!cfg80211_reg_can_beacon(phy->mt76->hw->wiphy, chandef, + NL80211_IFTYPE_AP)) + req.switch_reason = CH_SWITCH_DFS; + else + req.switch_reason = CH_SWITCH_NORMAL; + + if (tag == UNI_CHANNEL_SWITCH) + req.rx_path = hweight8(req.rx_path); + + if (chandef->width == NL80211_CHAN_WIDTH_80P80) { + int freq2 = chandef->center_freq2; + + req.center_ch2 = ieee80211_frequency_to_channel(freq2); + } + + return mt76_mcu_send_msg(&dev->mt76, MCU_WMWA_UNI_CMD(CHANNEL_SWITCH), + &req, sizeof(req), true); +} + +static int mt7996_mcu_set_eeprom_flash(struct mt7996_dev *dev) +{ +#define MAX_PAGE_IDX_MASK GENMASK(7, 5) +#define PAGE_IDX_MASK GENMASK(4, 2) +#define PER_PAGE_SIZE 0x400 + struct mt7996_mcu_eeprom req = { + .tag = cpu_to_le16(UNI_EFUSE_BUFFER_MODE), + .buffer_mode = EE_MODE_BUFFER + }; + u16 eeprom_size = MT7996_EEPROM_SIZE; + u8 total = DIV_ROUND_UP(eeprom_size, PER_PAGE_SIZE); + u8 *eep = (u8 *)dev->mt76.eeprom.data; + int eep_len, i; + + for (i = 0; i < total; i++, eep += eep_len) { + struct sk_buff *skb; + int ret, msg_len; + + if (i == total - 1 && !!(eeprom_size % PER_PAGE_SIZE)) + eep_len = eeprom_size % PER_PAGE_SIZE; + else + eep_len = PER_PAGE_SIZE; + + msg_len = sizeof(req) + eep_len; + skb = mt76_mcu_msg_alloc(&dev->mt76, NULL, msg_len); + if (!skb) + return -ENOMEM; + + req.len = cpu_to_le16(msg_len - 4); + req.format = FIELD_PREP(MAX_PAGE_IDX_MASK, total - 1) | + FIELD_PREP(PAGE_IDX_MASK, i) | EE_FORMAT_WHOLE; + req.buf_len = cpu_to_le16(eep_len); + + skb_put_data(skb, &req, sizeof(req)); + skb_put_data(skb, eep, eep_len); + + ret = mt76_mcu_skb_send_msg(&dev->mt76, skb, + MCU_WM_UNI_CMD(EFUSE_CTRL), true); + if (ret) + return ret; + } + + return 0; +} + +int mt7996_mcu_set_eeprom(struct mt7996_dev *dev) +{ + struct mt7996_mcu_eeprom req = { + .tag = cpu_to_le16(UNI_EFUSE_BUFFER_MODE), + .len = cpu_to_le16(sizeof(req) - 4), + .buffer_mode = EE_MODE_EFUSE, + .format = EE_FORMAT_WHOLE + }; + + if (dev->flash_mode) + return mt7996_mcu_set_eeprom_flash(dev); + + return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(EFUSE_CTRL), + &req, sizeof(req), true); +} + +int mt7996_mcu_get_eeprom(struct mt7996_dev *dev, u32 offset) +{ + struct { + u8 _rsv[4]; + + __le16 tag; + __le16 len; + __le32 addr; + __le32 valid; + u8 data[16]; + } __packed req = { + .tag = cpu_to_le16(UNI_EFUSE_ACCESS), + .len = cpu_to_le16(sizeof(req) - 4), + .addr = cpu_to_le32(round_down(offset, + MT7996_EEPROM_BLOCK_SIZE)), + }; + struct sk_buff *skb; + bool valid; + int ret; + + ret = mt76_mcu_send_and_get_msg(&dev->mt76, MCU_WM_UNI_CMD_QUERY(EFUSE_CTRL), &req, + sizeof(req), true, &skb); + if (ret) + return ret; + + valid = le32_to_cpu(*(__le32 *)(skb->data + 16)); + if (valid) { + u32 addr = le32_to_cpu(*(__le32 *)(skb->data + 12)); + u8 *buf = (u8 *)dev->mt76.eeprom.data + addr; + + skb_pull(skb, 64); + memcpy(buf, skb->data, MT7996_EEPROM_BLOCK_SIZE); + } + + dev_kfree_skb(skb); + + return 0; +} + +int mt7996_mcu_get_eeprom_free_block(struct mt7996_dev *dev, u8 *block_num) +{ + struct { + u8 _rsv[4]; + + __le16 tag; + __le16 len; + u8 num; + u8 version; + u8 die_idx; + u8 _rsv2; + } __packed req = { + .tag = cpu_to_le16(UNI_EFUSE_FREE_BLOCK), + .len = cpu_to_le16(sizeof(req) - 4), + .version = 2, + }; + struct sk_buff *skb; + int ret; + + ret = mt76_mcu_send_and_get_msg(&dev->mt76, MCU_WM_UNI_CMD_QUERY(EFUSE_CTRL), &req, + sizeof(req), true, &skb); + if (ret) + return ret; + + *block_num = *(u8 *)(skb->data + 8); + dev_kfree_skb(skb); + + return 0; +} + +int mt7996_mcu_get_chan_mib_info(struct mt7996_phy *phy, bool chan_switch) +{ + struct { + struct { + u8 band; + u8 __rsv[3]; + } hdr; + struct { + __le16 tag; + __le16 len; + __le32 offs; + } data[4]; + } __packed req = { + .hdr.band = phy->mt76->band_idx, + }; + /* strict order */ + static const u32 offs[] = { + UNI_MIB_TX_TIME, + UNI_MIB_RX_TIME, + UNI_MIB_OBSS_AIRTIME, + UNI_MIB_NON_WIFI_TIME, + }; + struct mt76_channel_state *state = phy->mt76->chan_state; + struct mt76_channel_state *state_ts = &phy->state_ts; + struct mt7996_dev *dev = phy->dev; + struct mt7996_mcu_mib *res; + struct sk_buff *skb; + int i, ret; + + for (i = 0; i < 4; i++) { + req.data[i].tag = cpu_to_le16(UNI_CMD_MIB_DATA); + req.data[i].len = cpu_to_le16(sizeof(req.data[i])); + req.data[i].offs = cpu_to_le32(offs[i]); + } + + ret = mt76_mcu_send_and_get_msg(&dev->mt76, MCU_WM_UNI_CMD_QUERY(GET_MIB_INFO), + &req, sizeof(req), true, &skb); + if (ret) + return ret; + + skb_pull(skb, sizeof(req.hdr)); + + res = (struct mt7996_mcu_mib *)(skb->data); + + if (chan_switch) + goto out; + +#define __res_u64(s) le64_to_cpu(res[s].data) + state->cc_tx += __res_u64(1) - state_ts->cc_tx; + state->cc_bss_rx += __res_u64(2) - state_ts->cc_bss_rx; + state->cc_rx += __res_u64(2) + __res_u64(3) - state_ts->cc_rx; + state->cc_busy += __res_u64(0) + __res_u64(1) + __res_u64(2) + __res_u64(3) - + state_ts->cc_busy; + +out: + state_ts->cc_tx = __res_u64(1); + state_ts->cc_bss_rx = __res_u64(2); + state_ts->cc_rx = __res_u64(2) + __res_u64(3); + state_ts->cc_busy = __res_u64(0) + __res_u64(1) + __res_u64(2) + __res_u64(3); +#undef __res_u64 + + dev_kfree_skb(skb); + + return 0; +} + +int mt7996_mcu_set_ser(struct mt7996_dev *dev, u8 action, u8 val, u8 band) +{ + struct { + u8 rsv[4]; + + __le16 tag; + __le16 len; + + union { + struct { + __le32 mask; + } __packed set; + + struct { + u8 method; + u8 band; + u8 rsv2[2]; + } __packed trigger; + }; + } __packed req = { + .tag = cpu_to_le16(action), + .len = cpu_to_le16(sizeof(req) - 4), + }; + + switch (action) { + case UNI_CMD_SER_SET: + req.set.mask = cpu_to_le32(val); + break; + case UNI_CMD_SER_TRIGGER: + req.trigger.method = val; + req.trigger.band = band; + break; + default: + return -EINVAL; + } + + return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(SER), + &req, sizeof(req), false); +} + +int mt7996_mcu_set_txbf(struct mt7996_dev *dev, u8 action) +{ +#define MT7996_BF_MAX_SIZE sizeof(union bf_tag_tlv) +#define BF_PROCESSING 4 + struct uni_header hdr; + struct sk_buff *skb; + struct tlv *tlv; + int len = sizeof(hdr) + MT7996_BF_MAX_SIZE; + + memset(&hdr, 0, sizeof(hdr)); + + skb = mt76_mcu_msg_alloc(&dev->mt76, NULL, len); + if (!skb) + return -ENOMEM; + + skb_put_data(skb, &hdr, sizeof(hdr)); + + switch (action) { + case BF_SOUNDING_ON: { + struct bf_sounding_on *req_snd_on; + + tlv = mt7996_mcu_add_uni_tlv(skb, action, sizeof(*req_snd_on)); + req_snd_on = (struct bf_sounding_on *)tlv; + req_snd_on->snd_mode = BF_PROCESSING; + break; + } + case BF_HW_EN_UPDATE: { + struct bf_hw_en_status_update *req_hw_en; + + tlv = mt7996_mcu_add_uni_tlv(skb, action, sizeof(*req_hw_en)); + req_hw_en = (struct bf_hw_en_status_update *)tlv; + req_hw_en->ebf = true; + req_hw_en->ibf = dev->ibf; + break; + } + case BF_MOD_EN_CTRL: { + struct bf_mod_en_ctrl *req_mod_en; + + tlv = mt7996_mcu_add_uni_tlv(skb, action, sizeof(*req_mod_en)); + req_mod_en = (struct bf_mod_en_ctrl *)tlv; + req_mod_en->bf_num = 2; + req_mod_en->bf_bitmap = GENMASK(0, 0); + break; + } + default: + return -EINVAL; + } + + return mt76_mcu_skb_send_msg(&dev->mt76, skb, MCU_WM_UNI_CMD(BF), true); +} + +int mt7996_mcu_add_obss_spr(struct mt7996_dev *dev, struct ieee80211_vif *vif, + bool enable) +{ +#define MT_SPR_ENABLE 1 + struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv; + struct { + u8 band_idx; + u8 __rsv[3]; + + __le16 tag; + __le16 len; + __le32 val; + } __packed req = { + .band_idx = mvif->mt76.band_idx, + .tag = cpu_to_le16(UNI_CMD_SR_ENABLE), + .len = cpu_to_le16(sizeof(req) - 4), + .val = cpu_to_le32(enable), + }; + + return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(SR), + &req, sizeof(req), true); +} + +int mt7996_mcu_update_bss_color(struct mt7996_dev *dev, struct ieee80211_vif *vif, + struct cfg80211_he_bss_color *he_bss_color) +{ + int len = sizeof(struct bss_req_hdr) + sizeof(struct bss_color_tlv); + struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv; + struct bss_color_tlv *bss_color; + struct sk_buff *skb; + struct tlv *tlv; + + skb = __mt7996_mcu_alloc_bss_req(&dev->mt76, &mvif->mt76, len); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + tlv = mt76_connac_mcu_add_tlv(skb, UNI_BSS_INFO_BSS_COLOR, + sizeof(*bss_color)); + bss_color = (struct bss_color_tlv *)tlv; + bss_color->enable = he_bss_color->enabled; + bss_color->color = he_bss_color->color; + + return mt76_mcu_skb_send_msg(&dev->mt76, skb, + MCU_WMWA_UNI_CMD(BSS_INFO_UPDATE), true); +} + +#define TWT_AGRT_TRIGGER BIT(0) +#define TWT_AGRT_ANNOUNCE BIT(1) +#define TWT_AGRT_PROTECT BIT(2) + +int mt7996_mcu_twt_agrt_update(struct mt7996_dev *dev, + struct mt7996_vif *mvif, + struct mt7996_twt_flow *flow, + int cmd) +{ + struct { + u8 _rsv[4]; + + __le16 tag; + __le16 len; + u8 tbl_idx; + u8 cmd; + u8 own_mac_idx; + u8 flowid; /* 0xff for group id */ + __le16 peer_id; /* specify the peer_id (msb=0) + * or group_id (msb=1) + */ + u8 duration; /* 256 us */ + u8 bss_idx; + __le64 start_tsf; + __le16 mantissa; + u8 exponent; + u8 is_ap; + u8 agrt_params; + u8 __rsv2[135]; + } __packed req = { + .tag = cpu_to_le16(UNI_CMD_TWT_ARGT_UPDATE), + .len = cpu_to_le16(sizeof(req) - 4), + .tbl_idx = flow->table_id, + .cmd = cmd, + .own_mac_idx = mvif->mt76.omac_idx, + .flowid = flow->id, + .peer_id = cpu_to_le16(flow->wcid), + .duration = flow->duration, + .bss_idx = mvif->mt76.idx, + .start_tsf = cpu_to_le64(flow->tsf), + .mantissa = flow->mantissa, + .exponent = flow->exp, + .is_ap = true, + }; + + if (flow->protection) + req.agrt_params |= TWT_AGRT_PROTECT; + if (!flow->flowtype) + req.agrt_params |= TWT_AGRT_ANNOUNCE; + if (flow->trigger) + req.agrt_params |= TWT_AGRT_TRIGGER; + + return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(TWT), + &req, sizeof(req), true); +} + +void mt7996_mcu_set_pm(void *priv, u8 *mac, struct ieee80211_vif *vif) +{ +#define EXIT_PM_STATE 0 +#define ENTER_PM_STATE 1 + struct ieee80211_hw *hw = priv; + struct mt7996_dev *dev = mt7996_hw_dev(hw); + struct mt7996_phy *phy = mt7996_hw_phy(hw); + struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv; + struct bss_power_save *ps; + struct sk_buff *skb; + struct tlv *tlv; + bool running = test_bit(MT76_STATE_RUNNING, &phy->mt76->state); + + skb = __mt7996_mcu_alloc_bss_req(&dev->mt76, &mvif->mt76, + MT7996_BSS_UPDATE_MAX_SIZE); + if (IS_ERR(skb)) + return; + + tlv = mt7996_mcu_add_uni_tlv(skb, UNI_BSS_INFO_PS, sizeof(*ps)); + ps = (struct bss_power_save *)tlv; + ps->profile = running ? EXIT_PM_STATE : ENTER_PM_STATE; + + mt76_mcu_skb_send_msg(&dev->mt76, skb, + MCU_WMWA_UNI_CMD(BSS_INFO_UPDATE), true); +} + +int mt7996_mcu_set_rts_thresh(struct mt7996_phy *phy, u32 val) +{ + struct { + u8 band_idx; + u8 _rsv[3]; + + __le16 tag; + __le16 len; + __le32 len_thresh; + __le32 pkt_thresh; + } __packed req = { + .band_idx = phy->mt76->band_idx, + .tag = cpu_to_le16(UNI_BAND_CONFIG_RTS_THRESHOLD), + .len = cpu_to_le16(sizeof(req) - 4), + .len_thresh = cpu_to_le32(val), + .pkt_thresh = cpu_to_le32(0x2), + }; + + return mt76_mcu_send_msg(&phy->dev->mt76, MCU_WM_UNI_CMD(BAND_CONFIG), + &req, sizeof(req), true); +} + +int mt7996_mcu_set_radio_en(struct mt7996_phy *phy, bool enable) +{ + struct { + u8 band_idx; + u8 _rsv[3]; + + __le16 tag; + __le16 len; + u8 enable; + u8 _rsv2[3]; + } __packed req = { + .band_idx = phy->mt76->band_idx, + .tag = cpu_to_le16(UNI_BAND_CONFIG_RADIO_ENABLE), + .len = cpu_to_le16(sizeof(req) - 4), + .enable = enable, + }; + + return mt76_mcu_send_msg(&phy->dev->mt76, MCU_WM_UNI_CMD(BAND_CONFIG), + &req, sizeof(req), true); +} + +int mt7996_mcu_rdd_cmd(struct mt7996_dev *dev, int cmd, u8 index, + u8 rx_sel, u8 val) +{ + struct { + u8 _rsv[4]; + + __le16 tag; + __le16 len; + + u8 ctrl; + u8 rdd_idx; + u8 rdd_rx_sel; + u8 val; + u8 rsv[4]; + } __packed req = { + .tag = cpu_to_le16(UNI_RDD_CTRL_PARM), + .len = cpu_to_le16(sizeof(req) - 4), + .ctrl = cmd, + .rdd_idx = index, + .rdd_rx_sel = rx_sel, + .val = val, + }; + + return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(RDD_CTRL), + &req, sizeof(req), true); +} + +int mt7996_mcu_wtbl_update_hdr_trans(struct mt7996_dev *dev, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv; + struct mt7996_sta *msta; + struct sk_buff *skb; + + msta = sta ? (struct mt7996_sta *)sta->drv_priv : &mvif->sta; + + skb = __mt76_connac_mcu_alloc_sta_req(&dev->mt76, &mvif->mt76, + &msta->wcid, + MT7996_STA_UPDATE_MAX_SIZE); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + /* starec hdr trans */ + mt7996_mcu_sta_hdr_trans_tlv(dev, skb, vif, sta); + return mt76_mcu_skb_send_msg(&dev->mt76, skb, + MCU_WMWA_UNI_CMD(STA_REC_UPDATE), true); +} + +int mt7996_mcu_rf_regval(struct mt7996_dev *dev, u32 regidx, u32 *val, bool set) +{ + struct { + u8 __rsv1[4]; + + __le16 tag; + __le16 len; + __le16 idx; + u8 __rsv2[2]; + __le32 ofs; + __le32 data; + } __packed *res, req = { + .tag = cpu_to_le16(UNI_CMD_ACCESS_RF_REG_BASIC), + .len = cpu_to_le16(sizeof(req) - 4), + + .idx = cpu_to_le16(u32_get_bits(regidx, GENMASK(31, 24))), + .ofs = cpu_to_le32(u32_get_bits(regidx, GENMASK(23, 0))), + .data = set ? cpu_to_le32(*val) : 0, + }; + struct sk_buff *skb; + int ret; + + if (set) + return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(REG_ACCESS), + &req, sizeof(req), true); + + ret = mt76_mcu_send_and_get_msg(&dev->mt76, + MCU_WM_UNI_CMD_QUERY(REG_ACCESS), + &req, sizeof(req), true, &skb); + if (ret) + return ret; + + res = (void *)skb->data; + *val = le32_to_cpu(res->data); + dev_kfree_skb(skb); + + return 0; +} + +int mt7996_mcu_set_rro(struct mt7996_dev *dev, u16 tag, u8 val) +{ + struct { + u8 __rsv1[4]; + + __le16 tag; + __le16 len; + + union { + struct { + u8 type; + u8 __rsv2[3]; + } __packed platform_type; + struct { + u8 type; + u8 dest; + u8 __rsv2[2]; + } __packed bypass_mode; + struct { + u8 path; + u8 __rsv2[3]; + } __packed txfree_path; + }; + } __packed req = { + .tag = cpu_to_le16(tag), + .len = cpu_to_le16(sizeof(req) - 4), + }; + + switch (tag) { + case UNI_RRO_SET_PLATFORM_TYPE: + req.platform_type.type = val; + break; + case UNI_RRO_SET_BYPASS_MODE: + req.bypass_mode.type = val; + break; + case UNI_RRO_SET_TXFREE_PATH: + req.txfree_path.path = val; + break; + default: + return -EINVAL; + } + + return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(RRO), &req, + sizeof(req), true); +} diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mcu.h b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.h new file mode 100644 index 000000000000..ff12a7168bd8 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.h @@ -0,0 +1,662 @@ +/* SPDX-License-Identifier: ISC */ +/* + * Copyright (C) 2022 MediaTek Inc. + */ + +#ifndef __MT7996_MCU_H +#define __MT7996_MCU_H + +#include "../mt76_connac_mcu.h" + +struct mt7996_mcu_rxd { + __le32 rxd[8]; + + __le16 len; + __le16 pkt_type_id; + + u8 eid; + u8 seq; + u8 option; + u8 __rsv; + + u8 ext_eid; + u8 __rsv1[2]; + u8 s2d_index; +}; + +struct mt7996_mcu_uni_event { + u8 cid; + u8 __rsv[3]; + __le32 status; /* 0: success, others: fail */ +} __packed; + +struct mt7996_mcu_csa_notify { + struct mt7996_mcu_rxd rxd; + + u8 omac_idx; + u8 csa_count; + u8 band_idx; + u8 rsv; +} __packed; + +struct mt7996_mcu_rdd_report { + struct mt7996_mcu_rxd rxd; + + u8 __rsv1[4]; + + __le16 tag; + __le16 len; + + u8 band_idx; + u8 long_detected; + u8 constant_prf_detected; + u8 staggered_prf_detected; + u8 radar_type_idx; + u8 periodic_pulse_num; + u8 long_pulse_num; + u8 hw_pulse_num; + + u8 out_lpn; + u8 out_spn; + u8 out_crpn; + u8 out_crpw; + u8 out_crbn; + u8 out_stgpn; + u8 out_stgpw; + + u8 __rsv2; + + __le32 out_pri_const; + __le32 out_pri_stg[3]; + __le32 out_pri_stg_dmin; + + struct { + __le32 start; + __le16 pulse_width; + __le16 pulse_power; + u8 mdrdy_flag; + u8 rsv[3]; + } long_pulse[32]; + + struct { + __le32 start; + __le16 pulse_width; + __le16 pulse_power; + u8 mdrdy_flag; + u8 rsv[3]; + } periodic_pulse[32]; + + struct { + __le32 start; + __le16 pulse_width; + __le16 pulse_power; + u8 sc_pass; + u8 sw_reset; + u8 mdrdy_flag; + u8 tx_active; + } hw_pulse[32]; +} __packed; + +struct mt7996_mcu_background_chain_ctrl { + u8 _rsv[4]; + + __le16 tag; + __le16 len; + + u8 chan; /* primary channel */ + u8 central_chan; /* central channel */ + u8 bw; + u8 tx_stream; + u8 rx_stream; + + u8 monitor_chan; /* monitor channel */ + u8 monitor_central_chan;/* monitor central channel */ + u8 monitor_bw; + u8 monitor_tx_stream; + u8 monitor_rx_stream; + + u8 scan_mode; /* 0: ScanStop + * 1: ScanStart + * 2: ScanRunning + */ + u8 band_idx; /* DBDC */ + u8 monitor_scan_type; + u8 band; /* 0: 2.4GHz, 1: 5GHz */ + u8 rsv[2]; +} __packed; + +struct mt7996_mcu_eeprom { + u8 _rsv[4]; + + __le16 tag; + __le16 len; + u8 buffer_mode; + u8 format; + __le16 buf_len; +} __packed; + +struct mt7996_mcu_phy_rx_info { + u8 category; + u8 rate; + u8 mode; + u8 nsts; + u8 gi; + u8 coding; + u8 stbc; + u8 bw; +}; + +struct mt7996_mcu_mib { + __le16 tag; + __le16 len; + __le32 offs; + __le64 data; +} __packed; + +enum mt7996_chan_mib_offs { + UNI_MIB_OBSS_AIRTIME = 26, + UNI_MIB_NON_WIFI_TIME = 27, + UNI_MIB_TX_TIME = 28, + UNI_MIB_RX_TIME = 29 +}; + +struct edca { + __le16 tag; + __le16 len; + + u8 queue; + u8 set; + u8 cw_min; + u8 cw_max; + __le16 txop; + u8 aifs; + u8 __rsv; +}; + +#define MCU_PQ_ID(p, q) (((p) << 15) | ((q) << 10)) +#define MCU_PKT_ID 0xa0 + +enum { + MCU_FW_LOG_WM, + MCU_FW_LOG_WA, + MCU_FW_LOG_TO_HOST, + MCU_FW_LOG_RELAY = 16 +}; + +enum { + MCU_TWT_AGRT_ADD, + MCU_TWT_AGRT_MODIFY, + MCU_TWT_AGRT_DELETE, + MCU_TWT_AGRT_TEARDOWN, + MCU_TWT_AGRT_GET_TSF, +}; + +enum { + MCU_WA_PARAM_CMD_QUERY, + MCU_WA_PARAM_CMD_SET, + MCU_WA_PARAM_CMD_CAPABILITY, + MCU_WA_PARAM_CMD_DEBUG, +}; + +enum { + MCU_WA_PARAM_PDMA_RX = 0x04, + MCU_WA_PARAM_CPU_UTIL = 0x0b, + MCU_WA_PARAM_RED = 0x0e, + MCU_WA_PARAM_HW_PATH_HIF_VER = 0x2f, +}; + +enum mcu_mmps_mode { + MCU_MMPS_STATIC, + MCU_MMPS_DYNAMIC, + MCU_MMPS_RSV, + MCU_MMPS_DISABLE, +}; + +struct bss_rate_tlv { + __le16 tag; + __le16 len; + u8 __rsv1[4]; + __le16 bc_trans; + __le16 mc_trans; + u8 short_preamble; + u8 bc_fixed_rate; + u8 mc_fixed_rate; + u8 __rsv2[1]; +} __packed; + +struct bss_ra_tlv { + __le16 tag; + __le16 len; + u8 short_preamble; + u8 force_sgi; + u8 force_gf; + u8 ht_mode; + u8 se_off; + u8 antenna_idx; + __le16 max_phyrate; + u8 force_tx_streams; + u8 __rsv[3]; +} __packed; + +struct bss_rlm_tlv { + __le16 tag; + __le16 len; + u8 control_channel; + u8 center_chan; + u8 center_chan2; + u8 bw; + u8 tx_streams; + u8 rx_streams; + u8 ht_op_info; + u8 sco; + u8 band; + u8 __rsv[3]; +} __packed; + +struct bss_color_tlv { + __le16 tag; + __le16 len; + u8 enable; + u8 color; + u8 rsv[2]; +} __packed; + +struct bss_inband_discovery_tlv { + __le16 tag; + __le16 len; + u8 tx_type; + u8 tx_mode; + u8 tx_interval; + u8 enable; + __le16 wcid; + __le16 prob_rsp_len; +#define MAX_INBAND_FRAME_SIZE 512 + u8 pkt[MAX_INBAND_FRAME_SIZE]; +} __packed; + +struct bss_bcn_content_tlv { + __le16 tag; + __le16 len; + __le16 tim_ie_pos; + __le16 csa_ie_pos; + __le16 bcc_ie_pos; + u8 enable; + u8 type; + __le16 pkt_len; +#define MAX_BEACON_SIZE 512 + u8 pkt[MAX_BEACON_SIZE]; +} __packed; + +struct bss_bcn_cntdwn_tlv { + __le16 tag; + __le16 len; + u8 cnt; + u8 rsv[3]; +} __packed; + +struct bss_bcn_mbss_tlv { + __le16 tag; + __le16 len; + __le32 bitmap; +#define MAX_BEACON_NUM 32 + __le16 offset[MAX_BEACON_NUM]; +} __packed __aligned(4); + +struct bss_txcmd_tlv { + __le16 tag; + __le16 len; + u8 txcmd_mode; + u8 __rsv[3]; +} __packed; + +struct bss_sec_tlv { + __le16 tag; + __le16 len; + u8 __rsv1[2]; + u8 cipher; + u8 __rsv2[1]; +} __packed; + +struct bss_power_save { + __le16 tag; + __le16 len; + u8 profile; + u8 _rsv[3]; +} __packed; + +struct bss_mld_tlv { + __le16 tag; + __le16 len; + u8 group_mld_id; + u8 own_mld_id; + u8 mac_addr[ETH_ALEN]; + u8 remap_idx; + u8 __rsv[3]; +} __packed; + +struct sta_rec_ba_uni { + __le16 tag; + __le16 len; + u8 tid; + u8 ba_type; + u8 amsdu; + u8 ba_en; + __le16 ssn; + __le16 winsize; + u8 ba_rdd_rro; + u8 __rsv[3]; +} __packed; + +struct sec_key_uni { + __le16 wlan_idx; + u8 mgmt_prot; + u8 cipher_id; + u8 cipher_len; + u8 key_id; + u8 key_len; + u8 need_resp; + u8 key[32]; +} __packed; + +struct sta_rec_sec_uni { + __le16 tag; + __le16 len; + u8 add; + u8 n_cipher; + u8 rsv[2]; + + struct sec_key_uni key[2]; +} __packed; + +struct sta_rec_hdrt { + __le16 tag; + __le16 len; + u8 hdrt_mode; + u8 rsv[3]; +} __packed; + +struct sta_rec_hdr_trans { + __le16 tag; + __le16 len; + u8 from_ds; + u8 to_ds; + u8 dis_rx_hdr_tran; + u8 rsv; +} __packed; + +struct hdr_trans_en { + __le16 tag; + __le16 len; + u8 enable; + u8 check_bssid; + u8 mode; + u8 __rsv; +} __packed; + +struct hdr_trans_vlan { + __le16 tag; + __le16 len; + u8 insert_vlan; + u8 remove_vlan; + u8 tid; + u8 __rsv; +} __packed; + +struct hdr_trans_blacklist { + __le16 tag; + __le16 len; + u8 idx; + u8 enable; + __le16 type; +} __packed; + +struct uni_header { + u8 __rsv[4]; +} __packed; + +struct vow_rx_airtime { + __le16 tag; + __le16 len; + + u8 enable; + u8 band; + u8 __rsv[2]; +} __packed; + +struct bf_sounding_on { + __le16 tag; + __le16 len; + + u8 snd_mode; + u8 sta_num; + u8 __rsv[2]; + __le16 wlan_id[4]; + __le32 snd_period; +} __packed; + +struct bf_hw_en_status_update { + __le16 tag; + __le16 len; + + bool ebf; + bool ibf; + u8 __rsv[2]; +} __packed; + +struct bf_mod_en_ctrl { + __le16 tag; + __le16 len; + + u8 bf_num; + u8 bf_bitmap; + u8 bf_sel[8]; + u8 __rsv[2]; +} __packed; + +union bf_tag_tlv { + struct bf_sounding_on bf_snd; + struct bf_hw_en_status_update bf_hw_en; + struct bf_mod_en_ctrl bf_mod_en; +}; + +struct ra_rate { + __le16 wlan_idx; + u8 mode; + u8 stbc; + __le16 gi; + u8 bw; + u8 ldpc; + u8 mcs; + u8 nss; + __le16 ltf; + u8 spe; + u8 preamble; + u8 __rsv[2]; +} __packed; + +struct ra_fixed_rate { + __le16 tag; + __le16 len; + + __le16 version; + struct ra_rate rate; +} __packed; + +enum { + UNI_RA_FIXED_RATE = 0xf, +}; + +#define MT7996_HDR_TRANS_MAX_SIZE (sizeof(struct hdr_trans_en) + \ + sizeof(struct hdr_trans_vlan) + \ + sizeof(struct hdr_trans_blacklist)) + +enum { + UNI_HDR_TRANS_EN, + UNI_HDR_TRANS_VLAN, + UNI_HDR_TRANS_BLACKLIST, +}; + +enum { + RATE_PARAM_FIXED = 3, + RATE_PARAM_MMPS_UPDATE = 5, + RATE_PARAM_FIXED_HE_LTF = 7, + RATE_PARAM_FIXED_MCS, + RATE_PARAM_FIXED_GI = 11, + RATE_PARAM_AUTO = 20, +}; + +enum { + BF_SOUNDING_ON = 1, + BF_HW_EN_UPDATE = 17, + BF_MOD_EN_CTRL = 20, +}; + +enum { + CMD_BAND_NONE, + CMD_BAND_24G, + CMD_BAND_5G, + CMD_BAND_6G, +}; + +struct bss_req_hdr { + u8 bss_idx; + u8 __rsv[3]; +} __packed; + +enum { + UNI_CHANNEL_SWITCH, + UNI_CHANNEL_RX_PATH, +}; + +#define MT7996_BSS_UPDATE_MAX_SIZE (sizeof(struct bss_req_hdr) + \ + sizeof(struct mt76_connac_bss_basic_tlv) + \ + sizeof(struct bss_rlm_tlv) + \ + sizeof(struct bss_ra_tlv) + \ + sizeof(struct bss_info_uni_he) + \ + sizeof(struct bss_rate_tlv) + \ + sizeof(struct bss_txcmd_tlv) + \ + sizeof(struct bss_power_save) + \ + sizeof(struct bss_sec_tlv) + \ + sizeof(struct bss_mld_tlv)) + +#define MT7996_STA_UPDATE_MAX_SIZE (sizeof(struct sta_req_hdr) + \ + sizeof(struct sta_rec_basic) + \ + sizeof(struct sta_rec_bf) + \ + sizeof(struct sta_rec_ht) + \ + sizeof(struct sta_rec_he_v2) + \ + sizeof(struct sta_rec_ba_uni) + \ + sizeof(struct sta_rec_vht) + \ + sizeof(struct sta_rec_uapsd) + \ + sizeof(struct sta_rec_amsdu) + \ + sizeof(struct sta_rec_bfee) + \ + sizeof(struct sta_rec_phy) + \ + sizeof(struct sta_rec_ra) + \ + sizeof(struct sta_rec_sec) + \ + sizeof(struct sta_rec_ra_fixed) + \ + sizeof(struct sta_rec_he_6g_capa) + \ + sizeof(struct sta_rec_hdrt) + \ + sizeof(struct sta_rec_hdr_trans) + \ + sizeof(struct tlv)) + +#define MT7996_BEACON_UPDATE_SIZE (sizeof(struct bss_req_hdr) + \ + sizeof(struct bss_bcn_content_tlv) + \ + sizeof(struct bss_bcn_cntdwn_tlv) + \ + sizeof(struct bss_bcn_mbss_tlv)) + +#define MT7996_INBAND_FRAME_SIZE (sizeof(struct bss_req_hdr) + \ + sizeof(struct bss_inband_discovery_tlv)) + +enum { + UNI_BAND_CONFIG_RADIO_ENABLE, + UNI_BAND_CONFIG_RTS_THRESHOLD = 0x08, +}; + +enum { + UNI_WSYS_CONFIG_FW_LOG_CTRL, + UNI_WSYS_CONFIG_FW_DBG_CTRL, +}; + +enum { + UNI_RDD_CTRL_PARM, + UNI_RDD_CTRL_SET_TH = 0x3, +}; + +enum { + UNI_EFUSE_ACCESS = 1, + UNI_EFUSE_BUFFER_MODE, + UNI_EFUSE_FREE_BLOCK, + UNI_EFUSE_BUFFER_RD, +}; + +enum { + UNI_VOW_DRR_CTRL, + UNI_VOW_RX_AT_AIRTIME_EN = 0x0b, + UNI_VOW_RX_AT_AIRTIME_CLR_EN = 0x0e, +}; + +enum { + UNI_CMD_MIB_DATA, +}; + +enum { + UNI_POWER_OFF, +}; + +enum { + UNI_CMD_TWT_ARGT_UPDATE = 0x0, + UNI_CMD_TWT_MGMT_OFFLOAD, +}; + +enum { + UNI_RRO_DEL_ENTRY = 0x1, + UNI_RRO_SET_PLATFORM_TYPE, + UNI_RRO_GET_BA_SESSION_TABLE, + UNI_RRO_SET_BYPASS_MODE, + UNI_RRO_SET_TXFREE_PATH, +}; + +enum{ + UNI_CMD_SR_ENABLE = 0x1, +}; + +enum { + UNI_CMD_ACCESS_REG_BASIC = 0x0, + UNI_CMD_ACCESS_RF_REG_BASIC, +}; + +enum { + UNI_CMD_SER_QUERY = 0x0, + UNI_CMD_SER_SET = 0x2, + UNI_CMD_SER_TRIGGER = 0x3, +}; + +enum { + SER_QUERY, + /* recovery */ + SER_SET_RECOVER_L1, + SER_SET_RECOVER_L2, + SER_SET_RECOVER_L3_RX_ABORT, + SER_SET_RECOVER_L3_TX_ABORT, + SER_SET_RECOVER_L3_TX_DISABLE, + SER_SET_RECOVER_L3_BF, + /* action */ + SER_ENABLE = 2, + SER_RECOVER +}; + +enum { + MT7996_SEC_MODE_PLAIN, + MT7996_SEC_MODE_AES, + MT7996_SEC_MODE_SCRAMBLE, + MT7996_SEC_MODE_MAX, +}; + +#define MT7996_PATCH_SEC GENMASK(31, 24) +#define MT7996_PATCH_SCRAMBLE_KEY GENMASK(15, 8) +#define MT7996_PATCH_AES_KEY GENMASK(7, 0) + +#define MT7996_SEC_ENCRYPT BIT(0) +#define MT7996_SEC_KEY_IDX GENMASK(2, 1) +#define MT7996_SEC_IV BIT(3) + +#endif diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mmio.c b/drivers/net/wireless/mediatek/mt76/mt7996/mmio.c new file mode 100644 index 000000000000..0d097cda4da7 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mmio.c @@ -0,0 +1,385 @@ +// SPDX-License-Identifier: ISC +/* + * Copyright (C) 2022 MediaTek Inc. + */ + +#include +#include +#include + +#include "mt7996.h" +#include "mac.h" +#include "../trace.h" + +static const struct __base mt7996_reg_base[] = { + [WF_AGG_BASE] = { { 0x820e2000, 0x820f2000, 0x830e2000 } }, + [WF_MIB_BASE] = { { 0x820ed000, 0x820fd000, 0x830ed000 } }, + [WF_TMAC_BASE] = { { 0x820e4000, 0x820f4000, 0x830e4000 } }, + [WF_RMAC_BASE] = { { 0x820e5000, 0x820f5000, 0x830e5000 } }, + [WF_ARB_BASE] = { { 0x820e3000, 0x820f3000, 0x830e3000 } }, + [WF_LPON_BASE] = { { 0x820eb000, 0x820fb000, 0x830eb000 } }, + [WF_ETBF_BASE] = { { 0x820ea000, 0x820fa000, 0x830ea000 } }, + [WF_DMA_BASE] = { { 0x820e7000, 0x820f7000, 0x830e7000 } }, +}; + +static const struct __map mt7996_reg_map[] = { + { 0x54000000, 0x02000, 0x1000 }, /* WFDMA_0 (PCIE0 MCU DMA0) */ + { 0x55000000, 0x03000, 0x1000 }, /* WFDMA_1 (PCIE0 MCU DMA1) */ + { 0x56000000, 0x04000, 0x1000 }, /* WFDMA reserved */ + { 0x57000000, 0x05000, 0x1000 }, /* WFDMA MCU wrap CR */ + { 0x58000000, 0x06000, 0x1000 }, /* WFDMA PCIE1 MCU DMA0 (MEM_DMA) */ + { 0x59000000, 0x07000, 0x1000 }, /* WFDMA PCIE1 MCU DMA1 */ + { 0x820c0000, 0x08000, 0x4000 }, /* WF_UMAC_TOP (PLE) */ + { 0x820c8000, 0x0c000, 0x2000 }, /* WF_UMAC_TOP (PSE) */ + { 0x820cc000, 0x0e000, 0x1000 }, /* WF_UMAC_TOP (PP) */ + { 0x74030000, 0x10000, 0x1000 }, /* PCIe MAC */ + { 0x820e0000, 0x20000, 0x0400 }, /* WF_LMAC_TOP BN0 (WF_CFG) */ + { 0x820e1000, 0x20400, 0x0200 }, /* WF_LMAC_TOP BN0 (WF_TRB) */ + { 0x820e2000, 0x20800, 0x0400 }, /* WF_LMAC_TOP BN0 (WF_AGG) */ + { 0x820e3000, 0x20c00, 0x0400 }, /* WF_LMAC_TOP BN0 (WF_ARB) */ + { 0x820e4000, 0x21000, 0x0400 }, /* WF_LMAC_TOP BN0 (WF_TMAC) */ + { 0x820e5000, 0x21400, 0x0800 }, /* WF_LMAC_TOP BN0 (WF_RMAC) */ + { 0x820ce000, 0x21c00, 0x0200 }, /* WF_LMAC_TOP (WF_SEC) */ + { 0x820e7000, 0x21e00, 0x0200 }, /* WF_LMAC_TOP BN0 (WF_DMA) */ + { 0x820cf000, 0x22000, 0x1000 }, /* WF_LMAC_TOP (WF_PF) */ + { 0x820e9000, 0x23400, 0x0200 }, /* WF_LMAC_TOP BN0 (WF_WTBLOFF) */ + { 0x820ea000, 0x24000, 0x0200 }, /* WF_LMAC_TOP BN0 (WF_ETBF) */ + { 0x820eb000, 0x24200, 0x0400 }, /* WF_LMAC_TOP BN0 (WF_LPON) */ + { 0x820ec000, 0x24600, 0x0200 }, /* WF_LMAC_TOP BN0 (WF_INT) */ + { 0x820ed000, 0x24800, 0x0800 }, /* WF_LMAC_TOP BN0 (WF_MIB) */ + { 0x820ca000, 0x26000, 0x2000 }, /* WF_LMAC_TOP BN0 (WF_MUCOP) */ + { 0x820d0000, 0x30000, 0x10000 }, /* WF_LMAC_TOP (WF_WTBLON) */ + { 0x40000000, 0x70000, 0x10000 }, /* WF_UMAC_SYSRAM */ + { 0x00400000, 0x80000, 0x10000 }, /* WF_MCU_SYSRAM */ + { 0x00410000, 0x90000, 0x10000 }, /* WF_MCU_SYSRAM (configure register) */ + { 0x820f0000, 0xa0000, 0x0400 }, /* WF_LMAC_TOP BN1 (WF_CFG) */ + { 0x820f1000, 0xa0600, 0x0200 }, /* WF_LMAC_TOP BN1 (WF_TRB) */ + { 0x820f2000, 0xa0800, 0x0400 }, /* WF_LMAC_TOP BN1 (WF_AGG) */ + { 0x820f3000, 0xa0c00, 0x0400 }, /* WF_LMAC_TOP BN1 (WF_ARB) */ + { 0x820f4000, 0xa1000, 0x0400 }, /* WF_LMAC_TOP BN1 (WF_TMAC) */ + { 0x820f5000, 0xa1400, 0x0800 }, /* WF_LMAC_TOP BN1 (WF_RMAC) */ + { 0x820f7000, 0xa1e00, 0x0200 }, /* WF_LMAC_TOP BN1 (WF_DMA) */ + { 0x820f9000, 0xa3400, 0x0200 }, /* WF_LMAC_TOP BN1 (WF_WTBLOFF) */ + { 0x820fa000, 0xa4000, 0x0200 }, /* WF_LMAC_TOP BN1 (WF_ETBF) */ + { 0x820fb000, 0xa4200, 0x0400 }, /* WF_LMAC_TOP BN1 (WF_LPON) */ + { 0x820fc000, 0xa4600, 0x0200 }, /* WF_LMAC_TOP BN1 (WF_INT) */ + { 0x820fd000, 0xa4800, 0x0800 }, /* WF_LMAC_TOP BN1 (WF_MIB) */ + { 0x820cc000, 0xa5000, 0x2000 }, /* WF_LMAC_TOP BN1 (WF_MUCOP) */ + { 0x820c4000, 0xa8000, 0x4000 }, /* WF_LMAC_TOP BN1 (WF_MUCOP) */ + { 0x820b0000, 0xae000, 0x1000 }, /* [APB2] WFSYS_ON */ + { 0x80020000, 0xb0000, 0x10000 }, /* WF_TOP_MISC_OFF */ + { 0x81020000, 0xc0000, 0x10000 }, /* WF_TOP_MISC_ON */ + { 0x7c020000, 0xd0000, 0x10000 }, /* CONN_INFRA, wfdma */ + { 0x7c060000, 0xe0000, 0x10000 }, /* CONN_INFRA, conn_host_csr_top */ + { 0x7c000000, 0xf0000, 0x10000 }, /* CONN_INFRA */ + { 0x0, 0x0, 0x0 }, /* imply end of search */ +}; + +static u32 mt7996_reg_map_l1(struct mt7996_dev *dev, u32 addr) +{ + u32 offset = FIELD_GET(MT_HIF_REMAP_L1_OFFSET, addr); + u32 base = FIELD_GET(MT_HIF_REMAP_L1_BASE, addr); + + dev->reg_l1_backup = dev->bus_ops->rr(&dev->mt76, MT_HIF_REMAP_L1); + dev->bus_ops->rmw(&dev->mt76, MT_HIF_REMAP_L1, + MT_HIF_REMAP_L1_MASK, + FIELD_PREP(MT_HIF_REMAP_L1_MASK, base)); + /* use read to push write */ + dev->bus_ops->rr(&dev->mt76, MT_HIF_REMAP_L1); + + return MT_HIF_REMAP_BASE_L1 + offset; +} + +static u32 mt7996_reg_map_l2(struct mt7996_dev *dev, u32 addr) +{ + u32 offset = FIELD_GET(MT_HIF_REMAP_L2_OFFSET, addr); + u32 base = FIELD_GET(MT_HIF_REMAP_L2_BASE, addr); + + dev->reg_l2_backup = dev->bus_ops->rr(&dev->mt76, MT_HIF_REMAP_L2); + dev->bus_ops->rmw(&dev->mt76, MT_HIF_REMAP_L2, + MT_HIF_REMAP_L2_MASK, + FIELD_PREP(MT_HIF_REMAP_L2_MASK, base)); + /* use read to push write */ + dev->bus_ops->rr(&dev->mt76, MT_HIF_REMAP_L2); + + return MT_HIF_REMAP_BASE_L2 + offset; +} + +static void mt7996_reg_remap_restore(struct mt7996_dev *dev) +{ + /* remap to ori status */ + if (unlikely(dev->reg_l1_backup)) { + dev->bus_ops->wr(&dev->mt76, MT_HIF_REMAP_L1, dev->reg_l1_backup); + dev->reg_l1_backup = 0; + } + + if (dev->reg_l2_backup) { + dev->bus_ops->wr(&dev->mt76, MT_HIF_REMAP_L2, dev->reg_l2_backup); + dev->reg_l2_backup = 0; + } +} + +static u32 __mt7996_reg_addr(struct mt7996_dev *dev, u32 addr) +{ + int i; + + mt7996_reg_remap_restore(dev); + + if (addr < 0x100000) + return addr; + + for (i = 0; i < dev->reg.map_size; i++) { + u32 ofs; + + if (addr < dev->reg.map[i].phys) + continue; + + ofs = addr - dev->reg.map[i].phys; + if (ofs > dev->reg.map[i].size) + continue; + + return dev->reg.map[i].mapped + ofs; + } + + if ((addr >= MT_INFRA_BASE && addr < MT_WFSYS0_PHY_START) || + (addr >= MT_WFSYS0_PHY_START && addr < MT_WFSYS1_PHY_START) || + (addr >= MT_WFSYS1_PHY_START && addr <= MT_WFSYS1_PHY_END)) + return mt7996_reg_map_l1(dev, addr); + + if (dev_is_pci(dev->mt76.dev) && + ((addr >= MT_CBTOP1_PHY_START && addr <= MT_CBTOP1_PHY_END) || + (addr >= MT_CBTOP2_PHY_START && addr <= MT_CBTOP2_PHY_END))) + return mt7996_reg_map_l1(dev, addr); + + /* CONN_INFRA: covert to phyiscal addr and use layer 1 remap */ + if (addr >= MT_INFRA_MCU_START && addr <= MT_INFRA_MCU_END) { + addr = addr - MT_INFRA_MCU_START + MT_INFRA_BASE; + return mt7996_reg_map_l1(dev, addr); + } + + return mt7996_reg_map_l2(dev, addr); +} + +static u32 mt7996_rr(struct mt76_dev *mdev, u32 offset) +{ + struct mt7996_dev *dev = container_of(mdev, struct mt7996_dev, mt76); + + return dev->bus_ops->rr(mdev, __mt7996_reg_addr(dev, offset)); +} + +static void mt7996_wr(struct mt76_dev *mdev, u32 offset, u32 val) +{ + struct mt7996_dev *dev = container_of(mdev, struct mt7996_dev, mt76); + + dev->bus_ops->wr(mdev, __mt7996_reg_addr(dev, offset), val); +} + +static u32 mt7996_rmw(struct mt76_dev *mdev, u32 offset, u32 mask, u32 val) +{ + struct mt7996_dev *dev = container_of(mdev, struct mt7996_dev, mt76); + + return dev->bus_ops->rmw(mdev, __mt7996_reg_addr(dev, offset), mask, val); +} + +static int mt7996_mmio_init(struct mt76_dev *mdev, + void __iomem *mem_base, + u32 device_id) +{ + struct mt76_bus_ops *bus_ops; + struct mt7996_dev *dev; + + dev = container_of(mdev, struct mt7996_dev, mt76); + mt76_mmio_init(&dev->mt76, mem_base); + + switch (device_id) { + case 0x7990: + dev->reg.base = mt7996_reg_base; + dev->reg.map = mt7996_reg_map; + dev->reg.map_size = ARRAY_SIZE(mt7996_reg_map); + break; + default: + return -EINVAL; + } + + dev->bus_ops = dev->mt76.bus; + bus_ops = devm_kmemdup(dev->mt76.dev, dev->bus_ops, sizeof(*bus_ops), + GFP_KERNEL); + if (!bus_ops) + return -ENOMEM; + + bus_ops->rr = mt7996_rr; + bus_ops->wr = mt7996_wr; + bus_ops->rmw = mt7996_rmw; + dev->mt76.bus = bus_ops; + + mdev->rev = (device_id << 16) | (mt76_rr(dev, MT_HW_REV) & 0xff); + + dev_dbg(mdev->dev, "ASIC revision: %04x\n", mdev->rev); + + return 0; +} + +void mt7996_dual_hif_set_irq_mask(struct mt7996_dev *dev, bool write_reg, + u32 clear, u32 set) +{ + struct mt76_dev *mdev = &dev->mt76; + unsigned long flags; + + spin_lock_irqsave(&mdev->mmio.irq_lock, flags); + + mdev->mmio.irqmask &= ~clear; + mdev->mmio.irqmask |= set; + + if (write_reg) { + mt76_wr(dev, MT_INT_MASK_CSR, mdev->mmio.irqmask); + mt76_wr(dev, MT_INT1_MASK_CSR, mdev->mmio.irqmask); + } + + spin_unlock_irqrestore(&mdev->mmio.irq_lock, flags); +} + +static void mt7996_rx_poll_complete(struct mt76_dev *mdev, + enum mt76_rxq_id q) +{ + struct mt7996_dev *dev = container_of(mdev, struct mt7996_dev, mt76); + + mt7996_irq_enable(dev, MT_INT_RX(q)); +} + +/* TODO: support 2/4/6/8 MSI-X vectors */ +static void mt7996_irq_tasklet(struct tasklet_struct *t) +{ + struct mt7996_dev *dev = from_tasklet(dev, t, irq_tasklet); + u32 i, intr, mask, intr1; + + mt76_wr(dev, MT_INT_MASK_CSR, 0); + if (dev->hif2) + mt76_wr(dev, MT_INT1_MASK_CSR, 0); + + intr = mt76_rr(dev, MT_INT_SOURCE_CSR); + intr &= dev->mt76.mmio.irqmask; + mt76_wr(dev, MT_INT_SOURCE_CSR, intr); + + if (dev->hif2) { + intr1 = mt76_rr(dev, MT_INT1_SOURCE_CSR); + intr1 &= dev->mt76.mmio.irqmask; + mt76_wr(dev, MT_INT1_SOURCE_CSR, intr1); + + intr |= intr1; + } + + trace_dev_irq(&dev->mt76, intr, dev->mt76.mmio.irqmask); + + mask = intr & MT_INT_RX_DONE_ALL; + if (intr & MT_INT_TX_DONE_MCU) + mask |= MT_INT_TX_DONE_MCU; + mt7996_irq_disable(dev, mask); + + if (intr & MT_INT_TX_DONE_MCU) + napi_schedule(&dev->mt76.tx_napi); + + for (i = 0; i < __MT_RXQ_MAX; i++) { + if ((intr & MT_INT_RX(i))) + napi_schedule(&dev->mt76.napi[i]); + } + + if (intr & MT_INT_MCU_CMD) { + u32 val = mt76_rr(dev, MT_MCU_CMD); + + mt76_wr(dev, MT_MCU_CMD, val); + if (val & MT_MCU_CMD_ERROR_MASK) { + dev->reset_state = val; + ieee80211_queue_work(mt76_hw(dev), &dev->reset_work); + wake_up(&dev->reset_wait); + } + } +} + +irqreturn_t mt7996_irq_handler(int irq, void *dev_instance) +{ + struct mt7996_dev *dev = dev_instance; + + mt76_wr(dev, MT_INT_MASK_CSR, 0); + if (dev->hif2) + mt76_wr(dev, MT_INT1_MASK_CSR, 0); + + if (!test_bit(MT76_STATE_INITIALIZED, &dev->mphy.state)) + return IRQ_NONE; + + tasklet_schedule(&dev->irq_tasklet); + + return IRQ_HANDLED; +} + +struct mt7996_dev *mt7996_mmio_probe(struct device *pdev, + void __iomem *mem_base, u32 device_id) +{ + static const struct mt76_driver_ops drv_ops = { + /* txwi_size = txd size + txp size */ + .txwi_size = MT_TXD_SIZE + sizeof(struct mt7996_txp), + .drv_flags = MT_DRV_TXWI_NO_FREE | + MT_DRV_HW_MGMT_TXQ, + .survey_flags = SURVEY_INFO_TIME_TX | + SURVEY_INFO_TIME_RX | + SURVEY_INFO_TIME_BSS_RX, + .token_size = MT7996_TOKEN_SIZE, + .tx_prepare_skb = mt7996_tx_prepare_skb, + .tx_complete_skb = mt7996_tx_complete_skb, + .rx_skb = mt7996_queue_rx_skb, + .rx_check = mt7996_rx_check, + .rx_poll_complete = mt7996_rx_poll_complete, + .sta_ps = mt7996_sta_ps, + .sta_add = mt7996_mac_sta_add, + .sta_remove = mt7996_mac_sta_remove, + .update_survey = mt7996_update_channel, + }; + struct mt7996_dev *dev; + struct mt76_dev *mdev; + int ret; + + mdev = mt76_alloc_device(pdev, sizeof(*dev), &mt7996_ops, &drv_ops); + if (!mdev) + return ERR_PTR(-ENOMEM); + + dev = container_of(mdev, struct mt7996_dev, mt76); + + ret = mt7996_mmio_init(mdev, mem_base, device_id); + if (ret) + goto error; + + tasklet_setup(&dev->irq_tasklet, mt7996_irq_tasklet); + + mt76_wr(dev, MT_INT_MASK_CSR, 0); + + return dev; + +error: + mt76_free_device(&dev->mt76); + + return ERR_PTR(ret); +} + +static int __init mt7996_init(void) +{ + int ret; + + ret = pci_register_driver(&mt7996_hif_driver); + if (ret) + return ret; + + ret = pci_register_driver(&mt7996_pci_driver); + if (ret) + pci_unregister_driver(&mt7996_hif_driver); + + return ret; +} + +static void __exit mt7996_exit(void) +{ + pci_unregister_driver(&mt7996_pci_driver); + pci_unregister_driver(&mt7996_hif_driver); +} + +module_init(mt7996_init); +module_exit(mt7996_exit); +MODULE_LICENSE("Dual BSD/GPL"); diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h b/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h new file mode 100644 index 000000000000..167f615f099e --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h @@ -0,0 +1,516 @@ +/* SPDX-License-Identifier: ISC */ +/* + * Copyright (C) 2022 MediaTek Inc. + */ + +#ifndef __MT7996_H +#define __MT7996_H + +#include +#include +#include "../mt76_connac.h" +#include "regs.h" + +#define MT7996_MAX_INTERFACES 19 +#define MT7996_MAX_WMM_SETS 4 +#define MT7996_WTBL_SIZE 544 +#define MT7996_WTBL_RESERVED (MT7996_WTBL_SIZE - 1) +#define MT7996_WTBL_STA (MT7996_WTBL_RESERVED - \ + MT7996_MAX_INTERFACES) + +#define MT7996_WATCHDOG_TIME (HZ / 10) +#define MT7996_RESET_TIMEOUT (30 * HZ) + +#define MT7996_TX_RING_SIZE 2048 +#define MT7996_TX_MCU_RING_SIZE 256 +#define MT7996_TX_FWDL_RING_SIZE 128 + +#define MT7996_RX_RING_SIZE 1536 +#define MT7996_RX_MCU_RING_SIZE 512 + +#define MT7996_FIRMWARE_WA "mediatek/mt7996/mt7996_wa.bin" +#define MT7996_FIRMWARE_WM "mediatek/mt7996/mt7996_wm.bin" +#define MT7996_ROM_PATCH "mediatek/mt7996/mt7996_rom_patch.bin" + +#define MT7996_EEPROM_DEFAULT "mediatek/mt7996/mt7996_eeprom.bin" +#define MT7996_EEPROM_SIZE 7680 +#define MT7996_EEPROM_BLOCK_SIZE 16 +#define MT7996_TOKEN_SIZE 8192 + +#define MT7996_CFEND_RATE_DEFAULT 0x49 /* OFDM 24M */ +#define MT7996_CFEND_RATE_11B 0x03 /* 11B LP, 11M */ + +#define MT7996_MAX_TWT_AGRT 16 +#define MT7996_MAX_STA_TWT_AGRT 8 +#define MT7996_MAX_QUEUE (__MT_RXQ_MAX + __MT_MCUQ_MAX + 3) + +struct mt7996_vif; +struct mt7996_sta; +struct mt7996_dfs_pulse; +struct mt7996_dfs_pattern; + +enum mt7996_txq_id { + MT7996_TXQ_FWDL = 16, + MT7996_TXQ_MCU_WM, + MT7996_TXQ_BAND0, + MT7996_TXQ_BAND1, + MT7996_TXQ_MCU_WA, + MT7996_TXQ_BAND2, +}; + +enum mt7996_rxq_id { + MT7996_RXQ_MCU_WM = 0, + MT7996_RXQ_MCU_WA, + MT7996_RXQ_MCU_WA_MAIN = 2, + MT7996_RXQ_MCU_WA_EXT = 2,/* unused */ + MT7996_RXQ_MCU_WA_TRI = 3, + MT7996_RXQ_BAND0 = 4, + MT7996_RXQ_BAND1 = 4,/* unused */ + MT7996_RXQ_BAND2 = 5, +}; + +struct mt7996_twt_flow { + struct list_head list; + u64 start_tsf; + u64 tsf; + u32 duration; + u16 wcid; + __le16 mantissa; + u8 exp; + u8 table_id; + u8 id; + u8 protection:1; + u8 flowtype:1; + u8 trigger:1; + u8 sched:1; +}; + +struct mt7996_sta { + struct mt76_wcid wcid; /* must be first */ + + struct mt7996_vif *vif; + + struct list_head poll_list; + struct list_head rc_list; + u32 airtime_ac[8]; + + unsigned long changed; + unsigned long jiffies; + unsigned long ampdu_state; + + struct mt76_sta_stats stats; + + struct mt76_connac_sta_key_conf bip; + + struct { + u8 flowid_mask; + struct mt7996_twt_flow flow[MT7996_MAX_STA_TWT_AGRT]; + } twt; +}; + +struct mt7996_vif_cap { + bool ht_ldpc:1; + bool vht_ldpc:1; + bool he_ldpc:1; + bool vht_su_ebfer:1; + bool vht_su_ebfee:1; + bool vht_mu_ebfer:1; + bool vht_mu_ebfee:1; + bool he_su_ebfer:1; + bool he_su_ebfee:1; + bool he_mu_ebfer:1; +}; + +struct mt7996_vif { + struct mt76_vif mt76; /* must be first */ + + struct mt7996_vif_cap cap; + struct mt7996_sta sta; + struct mt7996_phy *phy; + + struct ieee80211_tx_queue_params queue_params[IEEE80211_NUM_ACS]; + struct cfg80211_bitrate_mask bitrate_mask; +}; + +/* per-phy stats. */ +struct mib_stats { + u32 ack_fail_cnt; + u32 fcs_err_cnt; + u32 rts_cnt; + u32 rts_retries_cnt; + u32 ba_miss_cnt; + u32 tx_mu_bf_cnt; + u32 tx_mu_mpdu_cnt; + u32 tx_mu_acked_mpdu_cnt; + u32 tx_su_acked_mpdu_cnt; + u32 tx_bf_ibf_ppdu_cnt; + u32 tx_bf_ebf_ppdu_cnt; + + u32 tx_bf_rx_fb_all_cnt; + u32 tx_bf_rx_fb_eht_cnt; + u32 tx_bf_rx_fb_he_cnt; + u32 tx_bf_rx_fb_vht_cnt; + u32 tx_bf_rx_fb_ht_cnt; + + u32 tx_bf_rx_fb_bw; /* value of last sample, not cumulative */ + u32 tx_bf_rx_fb_nc_cnt; + u32 tx_bf_rx_fb_nr_cnt; + u32 tx_bf_fb_cpl_cnt; + u32 tx_bf_fb_trig_cnt; + + u32 tx_ampdu_cnt; + u32 tx_stop_q_empty_cnt; + u32 tx_mpdu_attempts_cnt; + u32 tx_mpdu_success_cnt; + /* BF counter is PPDU-based, so remove MPDU-based BF counter */ + + u32 tx_rwp_fail_cnt; + u32 tx_rwp_need_cnt; + + /* rx stats */ + u32 rx_fifo_full_cnt; + u32 channel_idle_cnt; + u32 rx_vector_mismatch_cnt; + u32 rx_delimiter_fail_cnt; + u32 rx_len_mismatch_cnt; + u32 rx_mpdu_cnt; + u32 rx_ampdu_cnt; + u32 rx_ampdu_bytes_cnt; + u32 rx_ampdu_valid_subframe_cnt; + u32 rx_ampdu_valid_subframe_bytes_cnt; + u32 rx_pfdrop_cnt; + u32 rx_vec_queue_overflow_drop_cnt; + u32 rx_ba_cnt; + + u32 tx_amsdu[8]; + u32 tx_amsdu_cnt; +}; + +struct mt7996_hif { + struct list_head list; + + struct device *dev; + void __iomem *regs; + int irq; +}; + +struct mt7996_phy { + struct mt76_phy *mt76; + struct mt7996_dev *dev; + + struct ieee80211_sband_iftype_data iftype[NUM_NL80211_BANDS][NUM_NL80211_IFTYPES]; + + struct ieee80211_vif *monitor_vif; + + u32 rxfilter; + u64 omac_mask; + + u16 noise; + + s16 coverage_class; + u8 slottime; + + u8 rdd_state; + + u32 rx_ampdu_ts; + u32 ampdu_ref; + + struct mib_stats mib; + struct mt76_channel_state state_ts; +}; + +struct mt7996_dev { + union { /* must be first */ + struct mt76_dev mt76; + struct mt76_phy mphy; + }; + + struct mt7996_hif *hif2; + struct mt7996_reg_desc reg; + u8 q_id[MT7996_MAX_QUEUE]; + u32 q_int_mask[MT7996_MAX_QUEUE]; + u32 q_wfdma_mask; + + const struct mt76_bus_ops *bus_ops; + struct tasklet_struct irq_tasklet; + struct mt7996_phy phy; + + /* monitor rx chain configured channel */ + struct cfg80211_chan_def rdd2_chandef; + struct mt7996_phy *rdd2_phy; + + u16 chainmask; + u8 chainshift[__MT_MAX_BAND]; + u32 hif_idx; + + struct work_struct init_work; + struct work_struct rc_work; + struct work_struct reset_work; + wait_queue_head_t reset_wait; + u32 reset_state; + + struct list_head sta_rc_list; + struct list_head sta_poll_list; + struct list_head twt_list; + spinlock_t sta_poll_lock; + + u32 hw_pattern; + + bool dbdc_support:1; + bool tbtc_support:1; + bool flash_mode:1; + + bool ibf; + u8 fw_debug_wm; + u8 fw_debug_wa; + u8 fw_debug_bin; + u16 fw_debug_seq; + + struct dentry *debugfs_dir; + struct rchan *relay_fwlog; + + struct { + u8 table_mask; + u8 n_agrt; + } twt; + + u32 reg_l1_backup; + u32 reg_l2_backup; +}; + +enum { + WFDMA0 = 0x0, + WFDMA1, + WFDMA_EXT, + __MT_WFDMA_MAX, +}; + +enum { + MT_CTX0, + MT_HIF0 = 0x0, + + MT_LMAC_AC00 = 0x0, + MT_LMAC_AC01, + MT_LMAC_AC02, + MT_LMAC_AC03, + MT_LMAC_ALTX0 = 0x10, + MT_LMAC_BMC0, + MT_LMAC_BCN0, + MT_LMAC_PSMP0, +}; + +enum { + MT_RX_SEL0, + MT_RX_SEL1, + MT_RX_SEL2, /* monitor chain */ +}; + +enum mt7996_rdd_cmd { + RDD_STOP, + RDD_START, + RDD_DET_MODE, + RDD_RADAR_EMULATE, + RDD_START_TXQ = 20, + RDD_CAC_START = 50, + RDD_CAC_END, + RDD_NORMAL_START, + RDD_DISABLE_DFS_CAL, + RDD_PULSE_DBG, + RDD_READ_PULSE, + RDD_RESUME_BF, + RDD_IRQ_OFF, +}; + +static inline struct mt7996_phy * +mt7996_hw_phy(struct ieee80211_hw *hw) +{ + struct mt76_phy *phy = hw->priv; + + return phy->priv; +} + +static inline struct mt7996_dev * +mt7996_hw_dev(struct ieee80211_hw *hw) +{ + struct mt76_phy *phy = hw->priv; + + return container_of(phy->dev, struct mt7996_dev, mt76); +} + +static inline struct mt7996_phy * +__mt7996_phy(struct mt7996_dev *dev, enum mt76_band_id band) +{ + struct mt76_phy *phy = dev->mt76.phys[band]; + + if (!phy) + return NULL; + + return phy->priv; +} + +static inline struct mt7996_phy * +mt7996_phy2(struct mt7996_dev *dev) +{ + return __mt7996_phy(dev, MT_BAND1); +} + +static inline struct mt7996_phy * +mt7996_phy3(struct mt7996_dev *dev) +{ + return __mt7996_phy(dev, MT_BAND2); +} + +extern const struct ieee80211_ops mt7996_ops; +extern struct pci_driver mt7996_pci_driver; +extern struct pci_driver mt7996_hif_driver; + +struct mt7996_dev *mt7996_mmio_probe(struct device *pdev, + void __iomem *mem_base, u32 device_id); +void mt7996_wfsys_reset(struct mt7996_dev *dev); +irqreturn_t mt7996_irq_handler(int irq, void *dev_instance); +u64 __mt7996_get_tsf(struct ieee80211_hw *hw, struct mt7996_vif *mvif); +int mt7996_register_device(struct mt7996_dev *dev); +void mt7996_unregister_device(struct mt7996_dev *dev); +int mt7996_eeprom_init(struct mt7996_dev *dev); +int mt7996_eeprom_parse_hw_cap(struct mt7996_dev *dev, struct mt7996_phy *phy); +int mt7996_eeprom_get_target_power(struct mt7996_dev *dev, + struct ieee80211_channel *chan); +s8 mt7996_eeprom_get_power_delta(struct mt7996_dev *dev, int band); +int mt7996_dma_init(struct mt7996_dev *dev); +void mt7996_dma_prefetch(struct mt7996_dev *dev); +void mt7996_dma_cleanup(struct mt7996_dev *dev); +int mt7996_mcu_init(struct mt7996_dev *dev); +int mt7996_mcu_twt_agrt_update(struct mt7996_dev *dev, + struct mt7996_vif *mvif, + struct mt7996_twt_flow *flow, + int cmd); +int mt7996_mcu_add_dev_info(struct mt7996_phy *phy, + struct ieee80211_vif *vif, bool enable); +int mt7996_mcu_add_bss_info(struct mt7996_phy *phy, + struct ieee80211_vif *vif, int enable); +int mt7996_mcu_add_sta(struct mt7996_dev *dev, struct ieee80211_vif *vif, + struct ieee80211_sta *sta, bool enable); +int mt7996_mcu_add_tx_ba(struct mt7996_dev *dev, + struct ieee80211_ampdu_params *params, + bool add); +int mt7996_mcu_add_rx_ba(struct mt7996_dev *dev, + struct ieee80211_ampdu_params *params, + bool add); +int mt7996_mcu_update_bss_color(struct mt7996_dev *dev, struct ieee80211_vif *vif, + struct cfg80211_he_bss_color *he_bss_color); +int mt7996_mcu_add_beacon(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + int enable); +int mt7996_mcu_beacon_inband_discov(struct mt7996_dev *dev, + struct ieee80211_vif *vif, u32 changed); +int mt7996_mcu_add_obss_spr(struct mt7996_dev *dev, struct ieee80211_vif *vif, + bool enable); +int mt7996_mcu_add_rate_ctrl(struct mt7996_dev *dev, struct ieee80211_vif *vif, + struct ieee80211_sta *sta, bool changed); +int mt7996_set_channel(struct mt7996_phy *phy); +int mt7996_mcu_set_chan_info(struct mt7996_phy *phy, u16 tag); +int mt7996_mcu_set_tx(struct mt7996_dev *dev, struct ieee80211_vif *vif); +int mt7996_mcu_set_fixed_rate_ctrl(struct mt7996_dev *dev, + void *data, u16 version); +int mt7996_mcu_set_eeprom(struct mt7996_dev *dev); +int mt7996_mcu_get_eeprom(struct mt7996_dev *dev, u32 offset); +int mt7996_mcu_get_eeprom_free_block(struct mt7996_dev *dev, u8 *block_num); +int mt7996_mcu_set_ser(struct mt7996_dev *dev, u8 action, u8 set, u8 band); +int mt7996_mcu_set_txbf(struct mt7996_dev *dev, u8 action); +int mt7996_mcu_set_fcc5_lpn(struct mt7996_dev *dev, int val); +int mt7996_mcu_set_pulse_th(struct mt7996_dev *dev, + const struct mt7996_dfs_pulse *pulse); +int mt7996_mcu_set_radar_th(struct mt7996_dev *dev, int index, + const struct mt7996_dfs_pattern *pattern); +int mt7996_mcu_set_radio_en(struct mt7996_phy *phy, bool enable); +void mt7996_mcu_set_pm(void *priv, u8 *mac, struct ieee80211_vif *vif); +int mt7996_mcu_set_rts_thresh(struct mt7996_phy *phy, u32 val); +int mt7996_mcu_get_chan_mib_info(struct mt7996_phy *phy, bool chan_switch); +int mt7996_mcu_rdd_cmd(struct mt7996_dev *dev, int cmd, u8 index, + u8 rx_sel, u8 val); +int mt7996_mcu_rdd_background_enable(struct mt7996_phy *phy, + struct cfg80211_chan_def *chandef); +int mt7996_mcu_rf_regval(struct mt7996_dev *dev, u32 regidx, u32 *val, bool set); +int mt7996_mcu_set_hdr_trans(struct mt7996_dev *dev, bool hdr_trans); +int mt7996_mcu_set_rro(struct mt7996_dev *dev, u16 tag, u8 val); +int mt7996_mcu_wa_cmd(struct mt7996_dev *dev, int cmd, u32 a1, u32 a2, u32 a3); +int mt7996_mcu_fw_log_2_host(struct mt7996_dev *dev, u8 type, u8 ctrl); +int mt7996_mcu_fw_dbg_ctrl(struct mt7996_dev *dev, u32 module, u8 level); +void mt7996_mcu_rx_event(struct mt7996_dev *dev, struct sk_buff *skb); +void mt7996_mcu_exit(struct mt7996_dev *dev); + +void mt7996_dual_hif_set_irq_mask(struct mt7996_dev *dev, bool write_reg, + u32 clear, u32 set); + +static inline void mt7996_irq_enable(struct mt7996_dev *dev, u32 mask) +{ + if (dev->hif2) + mt7996_dual_hif_set_irq_mask(dev, false, 0, mask); + else + mt76_set_irq_mask(&dev->mt76, 0, 0, mask); + + tasklet_schedule(&dev->irq_tasklet); +} + +static inline void mt7996_irq_disable(struct mt7996_dev *dev, u32 mask) +{ + if (dev->hif2) + mt7996_dual_hif_set_irq_mask(dev, true, mask, 0); + else + mt76_set_irq_mask(&dev->mt76, MT_INT_MASK_CSR, mask, 0); +} + +u32 mt7996_mac_wtbl_lmac_addr(struct mt7996_dev *dev, u16 wcid, u8 dw); +bool mt7996_mac_wtbl_update(struct mt7996_dev *dev, int idx, u32 mask); +void mt7996_mac_reset_counters(struct mt7996_phy *phy); +void mt7996_mac_cca_stats_reset(struct mt7996_phy *phy); +void mt7996_mac_enable_nf(struct mt7996_dev *dev, u8 band); +void mt7996_mac_write_txwi(struct mt7996_dev *dev, __le32 *txwi, + struct sk_buff *skb, struct mt76_wcid *wcid, int pid, + struct ieee80211_key_conf *key, u32 changed); +void mt7996_mac_set_timing(struct mt7996_phy *phy); +int mt7996_mac_sta_add(struct mt76_dev *mdev, struct ieee80211_vif *vif, + struct ieee80211_sta *sta); +void mt7996_mac_sta_remove(struct mt76_dev *mdev, struct ieee80211_vif *vif, + struct ieee80211_sta *sta); +void mt7996_mac_work(struct work_struct *work); +void mt7996_mac_reset_work(struct work_struct *work); +void mt7996_mac_sta_rc_work(struct work_struct *work); +void mt7996_mac_update_stats(struct mt7996_phy *phy); +void mt7996_mac_twt_teardown_flow(struct mt7996_dev *dev, + struct mt7996_sta *msta, + u8 flowid); +void mt7996_mac_add_twt_setup(struct ieee80211_hw *hw, + struct ieee80211_sta *sta, + struct ieee80211_twt_setup *twt); +int mt7996_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr, + enum mt76_txq_id qid, struct mt76_wcid *wcid, + struct ieee80211_sta *sta, + struct mt76_tx_info *tx_info); +void mt7996_tx_complete_skb(struct mt76_dev *mdev, struct mt76_queue_entry *e); +void mt7996_tx_token_put(struct mt7996_dev *dev); +void mt7996_queue_rx_skb(struct mt76_dev *mdev, enum mt76_rxq_id q, + struct sk_buff *skb, u32 *info); +bool mt7996_rx_check(struct mt76_dev *mdev, void *data, int len); +void mt7996_sta_ps(struct mt76_dev *mdev, struct ieee80211_sta *sta, bool ps); +void mt7996_stats_work(struct work_struct *work); +int mt76_dfs_start_rdd(struct mt7996_dev *dev, bool force); +int mt7996_dfs_init_radar_detector(struct mt7996_phy *phy); +void mt7996_set_stream_he_caps(struct mt7996_phy *phy); +void mt7996_set_stream_vht_txbf_caps(struct mt7996_phy *phy); +void mt7996_update_channel(struct mt76_phy *mphy); +int mt7996_init_debugfs(struct mt7996_phy *phy); +void mt7996_debugfs_rx_fw_monitor(struct mt7996_dev *dev, const void *data, int len); +bool mt7996_debugfs_rx_log(struct mt7996_dev *dev, const void *data, int len); +int mt7996_mcu_add_key(struct mt76_dev *dev, struct ieee80211_vif *vif, + struct mt76_connac_sta_key_conf *sta_key_conf, + struct ieee80211_key_conf *key, int mcu_cmd, + struct mt76_wcid *wcid, enum set_key_cmd cmd); +int mt7996_mcu_wtbl_update_hdr_trans(struct mt7996_dev *dev, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta); +#ifdef CONFIG_MAC80211_DEBUGFS +void mt7996_sta_add_debugfs(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_sta *sta, struct dentry *dir); +#endif + +#endif diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/pci.c b/drivers/net/wireless/mediatek/mt76/mt7996/pci.c new file mode 100644 index 000000000000..64aee3fb5445 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7996/pci.c @@ -0,0 +1,222 @@ +// SPDX-License-Identifier: ISC +/* + * Copyright (C) 2022 MediaTek Inc. + */ + +#include +#include +#include + +#include "mt7996.h" +#include "mac.h" +#include "../trace.h" + +static LIST_HEAD(hif_list); +static DEFINE_SPINLOCK(hif_lock); +static u32 hif_idx; + +static const struct pci_device_id mt7996_pci_device_table[] = { + { PCI_DEVICE(PCI_VENDOR_ID_MEDIATEK, 0x7990) }, + { }, +}; + +static const struct pci_device_id mt7996_hif_device_table[] = { + { PCI_DEVICE(PCI_VENDOR_ID_MEDIATEK, 0x7991) }, + { }, +}; + +static struct mt7996_hif *mt7996_pci_get_hif2(u32 idx) +{ + struct mt7996_hif *hif; + u32 val; + + spin_lock_bh(&hif_lock); + + list_for_each_entry(hif, &hif_list, list) { + val = readl(hif->regs + MT_PCIE_RECOG_ID); + val &= MT_PCIE_RECOG_ID_MASK; + if (val != idx) + continue; + + get_device(hif->dev); + goto out; + } + hif = NULL; + +out: + spin_unlock_bh(&hif_lock); + + return hif; +} + +static void mt7996_put_hif2(struct mt7996_hif *hif) +{ + if (!hif) + return; + + put_device(hif->dev); +} + +static struct mt7996_hif *mt7996_pci_init_hif2(struct pci_dev *pdev) +{ + hif_idx++; + if (!pci_get_device(PCI_VENDOR_ID_MEDIATEK, 0x7991, NULL)) + return NULL; + + writel(hif_idx | MT_PCIE_RECOG_ID_SEM, + pcim_iomap_table(pdev)[0] + MT_PCIE_RECOG_ID); + + return mt7996_pci_get_hif2(hif_idx); +} + +static int mt7996_pci_hif2_probe(struct pci_dev *pdev) +{ + struct mt7996_hif *hif; + + hif = devm_kzalloc(&pdev->dev, sizeof(*hif), GFP_KERNEL); + if (!hif) + return -ENOMEM; + + hif->dev = &pdev->dev; + hif->regs = pcim_iomap_table(pdev)[0]; + hif->irq = pdev->irq; + spin_lock_bh(&hif_lock); + list_add(&hif->list, &hif_list); + spin_unlock_bh(&hif_lock); + pci_set_drvdata(pdev, hif); + + return 0; +} + +static int mt7996_pci_probe(struct pci_dev *pdev, + const struct pci_device_id *id) +{ + struct pci_dev *hif2_dev; + struct mt7996_dev *dev; + struct mt76_dev *mdev; + struct mt7996_hif *hif2; + int irq, ret; + + ret = pcim_enable_device(pdev); + if (ret) + return ret; + + ret = pcim_iomap_regions(pdev, BIT(0), pci_name(pdev)); + if (ret) + return ret; + + pci_set_master(pdev); + + ret = dma_set_mask(&pdev->dev, DMA_BIT_MASK(32)); + if (ret) + return ret; + + mt76_pci_disable_aspm(pdev); + + if (id->device == 0x7991) + return mt7996_pci_hif2_probe(pdev); + + dev = mt7996_mmio_probe(&pdev->dev, pcim_iomap_table(pdev)[0], + id->device); + if (IS_ERR(dev)) + return PTR_ERR(dev); + + mdev = &dev->mt76; + mt7996_wfsys_reset(dev); + hif2 = mt7996_pci_init_hif2(pdev); + + ret = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_ALL_TYPES); + if (ret < 0) + goto free_device; + + irq = pdev->irq; + ret = devm_request_irq(mdev->dev, irq, mt7996_irq_handler, + IRQF_SHARED, KBUILD_MODNAME, dev); + if (ret) + goto free_irq_vector; + + mt76_wr(dev, MT_INT_MASK_CSR, 0); + /* master switch of PCIe tnterrupt enable */ + mt76_wr(dev, MT_PCIE_MAC_INT_ENABLE, 0xff); + + if (hif2) { + hif2_dev = container_of(hif2->dev, struct pci_dev, dev); + dev->hif2 = hif2; + + ret = pci_alloc_irq_vectors(hif2_dev, 1, 1, PCI_IRQ_ALL_TYPES); + if (ret < 0) + goto free_hif2; + + dev->hif2->irq = hif2_dev->irq; + ret = devm_request_irq(mdev->dev, dev->hif2->irq, + mt7996_irq_handler, IRQF_SHARED, + KBUILD_MODNAME "-hif", dev); + if (ret) + goto free_hif2_irq_vector; + + mt76_wr(dev, MT_INT1_MASK_CSR, 0); + /* master switch of PCIe tnterrupt enable */ + mt76_wr(dev, MT_PCIE1_MAC_INT_ENABLE, 0xff); + } + + ret = mt7996_register_device(dev); + if (ret) + goto free_hif2_irq; + + return 0; + +free_hif2_irq: + if (dev->hif2) + devm_free_irq(mdev->dev, dev->hif2->irq, dev); +free_hif2_irq_vector: + if (dev->hif2) + pci_free_irq_vectors(hif2_dev); +free_hif2: + if (dev->hif2) + put_device(dev->hif2->dev); + devm_free_irq(mdev->dev, irq, dev); +free_irq_vector: + pci_free_irq_vectors(pdev); +free_device: + mt76_free_device(&dev->mt76); + + return ret; +} + +static void mt7996_hif_remove(struct pci_dev *pdev) +{ + struct mt7996_hif *hif = pci_get_drvdata(pdev); + + list_del(&hif->list); +} + +static void mt7996_pci_remove(struct pci_dev *pdev) +{ + struct mt76_dev *mdev; + struct mt7996_dev *dev; + + mdev = pci_get_drvdata(pdev); + dev = container_of(mdev, struct mt7996_dev, mt76); + mt7996_put_hif2(dev->hif2); + mt7996_unregister_device(dev); +} + +struct pci_driver mt7996_hif_driver = { + .name = KBUILD_MODNAME "_hif", + .id_table = mt7996_hif_device_table, + .probe = mt7996_pci_probe, + .remove = mt7996_hif_remove, +}; + +struct pci_driver mt7996_pci_driver = { + .name = KBUILD_MODNAME, + .id_table = mt7996_pci_device_table, + .probe = mt7996_pci_probe, + .remove = mt7996_pci_remove, +}; + +MODULE_DEVICE_TABLE(pci, mt7996_pci_device_table); +MODULE_DEVICE_TABLE(pci, mt7996_hif_device_table); +MODULE_FIRMWARE(MT7996_FIRMWARE_WA); +MODULE_FIRMWARE(MT7996_FIRMWARE_WM); +MODULE_FIRMWARE(MT7996_ROM_PATCH); diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/regs.h b/drivers/net/wireless/mediatek/mt76/mt7996/regs.h new file mode 100644 index 000000000000..1d53611da4d8 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7996/regs.h @@ -0,0 +1,533 @@ +/* SPDX-License-Identifier: ISC */ +/* + * Copyright (C) 2022 MediaTek Inc. + */ + +#ifndef __MT7996_REGS_H +#define __MT7996_REGS_H + +struct __map { + u32 phys; + u32 mapped; + u32 size; +}; + +struct __base { + u32 band_base[__MT_MAX_BAND]; +}; + +/* used to differentiate between generations */ +struct mt7996_reg_desc { + const struct __base *base; + const struct __map *map; + u32 map_size; +}; + +enum base_rev { + WF_AGG_BASE, + WF_MIB_BASE, + WF_TMAC_BASE, + WF_RMAC_BASE, + WF_ARB_BASE, + WF_LPON_BASE, + WF_ETBF_BASE, + WF_DMA_BASE, + __MT_REG_BASE_MAX, +}; + +#define __BASE(_id, _band) (dev->reg.base[(_id)].band_base[(_band)]) + +#define MT_MCU_INT_EVENT 0x2108 +#define MT_MCU_INT_EVENT_DMA_STOPPED BIT(0) +#define MT_MCU_INT_EVENT_DMA_INIT BIT(1) +#define MT_MCU_INT_EVENT_RESET_DONE BIT(3) + +/* PLE */ +#define MT_PLE_BASE 0x820c0000 +#define MT_PLE(ofs) (MT_PLE_BASE + (ofs)) + +#define MT_FL_Q_EMPTY MT_PLE(0x360) +#define MT_FL_Q0_CTRL MT_PLE(0x3e0) +#define MT_FL_Q2_CTRL MT_PLE(0x3e8) +#define MT_FL_Q3_CTRL MT_PLE(0x3ec) + +#define MT_PLE_FREEPG_CNT MT_PLE(0x380) +#define MT_PLE_FREEPG_HEAD_TAIL MT_PLE(0x384) +#define MT_PLE_PG_HIF_GROUP MT_PLE(0x00c) +#define MT_PLE_HIF_PG_INFO MT_PLE(0x388) + +#define MT_PLE_AC_QEMPTY(ac, n) MT_PLE(0x600 + 0x80 * (ac) + ((n) << 2)) +#define MT_PLE_AMSDU_PACK_MSDU_CNT(n) MT_PLE(0x10e0 + ((n) << 2)) + +/* WF MDP TOP */ +#define MT_MDP_BASE 0x820cc000 +#define MT_MDP(ofs) (MT_MDP_BASE + (ofs)) + +#define MT_MDP_DCR2 MT_MDP(0x8e8) +#define MT_MDP_DCR2_RX_TRANS_SHORT BIT(2) + +/* TMAC: band 0(0x820e4000), band 1(0x820f4000), band 2(0x830e4000) */ +#define MT_WF_TMAC_BASE(_band) __BASE(WF_TMAC_BASE, (_band)) +#define MT_WF_TMAC(_band, ofs) (MT_WF_TMAC_BASE(_band) + (ofs)) + +#define MT_TMAC_TCR0(_band) MT_WF_TMAC(_band, 0) +#define MT_TMAC_TCR0_TX_BLINK GENMASK(7, 6) + +#define MT_TMAC_CDTR(_band) MT_WF_TMAC(_band, 0x0c8) +#define MT_TMAC_ODTR(_band) MT_WF_TMAC(_band, 0x0cc) +#define MT_TIMEOUT_VAL_PLCP GENMASK(15, 0) +#define MT_TIMEOUT_VAL_CCA GENMASK(31, 16) + +#define MT_TMAC_ICR0(_band) MT_WF_TMAC(_band, 0x014) +#define MT_IFS_EIFS_OFDM GENMASK(8, 0) +#define MT_IFS_RIFS GENMASK(14, 10) +#define MT_IFS_SIFS GENMASK(22, 16) +#define MT_IFS_SLOT GENMASK(30, 24) + +#define MT_TMAC_ICR1(_band) MT_WF_TMAC(_band, 0x018) +#define MT_IFS_EIFS_CCK GENMASK(8, 0) + +/* WF DMA TOP: band 0(0x820e7000), band 1(0x820f7000), band 2(0x830e7000) */ +#define MT_WF_DMA_BASE(_band) __BASE(WF_DMA_BASE, (_band)) +#define MT_WF_DMA(_band, ofs) (MT_WF_DMA_BASE(_band) + (ofs)) + +#define MT_DMA_DCR0(_band) MT_WF_DMA(_band, 0x000) +#define MT_DMA_DCR0_RXD_G5_EN BIT(23) + +#define MT_DMA_TCRF1(_band) MT_WF_DMA(_band, 0x054) +#define MT_DMA_TCRF1_QIDX GENMASK(15, 13) + +/* ETBF: band 0(0x820ea000), band 1(0x820fa000), band 2(0x830ea000) */ +#define MT_WF_ETBF_BASE(_band) __BASE(WF_ETBF_BASE, (_band)) +#define MT_WF_ETBF(_band, ofs) (MT_WF_ETBF_BASE(_band) + (ofs)) + +#define MT_ETBF_RX_FB_CONT(_band) MT_WF_ETBF(_band, 0x100) +#define MT_ETBF_RX_FB_BW GENMASK(10, 8) +#define MT_ETBF_RX_FB_NC GENMASK(7, 4) +#define MT_ETBF_RX_FB_NR GENMASK(3, 0) + +/* LPON: band 0(0x820eb000), band 1(0x820fb000), band 2(0x830eb000) */ +#define MT_WF_LPON_BASE(_band) __BASE(WF_LPON_BASE, (_band)) +#define MT_WF_LPON(_band, ofs) (MT_WF_LPON_BASE(_band) + (ofs)) + +#define MT_LPON_UTTR0(_band) MT_WF_LPON(_band, 0x360) +#define MT_LPON_UTTR1(_band) MT_WF_LPON(_band, 0x364) +#define MT_LPON_FRCR(_band) MT_WF_LPON(_band, 0x37c) + +#define MT_LPON_TCR(_band, n) MT_WF_LPON(_band, 0x0a8 + (((n) * 4) << 4)) +#define MT_LPON_TCR_SW_MODE GENMASK(1, 0) +#define MT_LPON_TCR_SW_WRITE BIT(0) +#define MT_LPON_TCR_SW_ADJUST BIT(1) +#define MT_LPON_TCR_SW_READ GENMASK(1, 0) + +/* MIB: band 0(0x820ed000), band 1(0x820fd000), band 2(0x830ed000)*/ +/* These counters are (mostly?) clear-on-read. So, some should not + * be read at all in case firmware is already reading them. These + * are commented with 'DNR' below. The DNR stats will be read by querying + * the firmware API for the appropriate message. For counters the driver + * does read, the driver should accumulate the counters. + */ +#define MT_WF_MIB_BASE(_band) __BASE(WF_MIB_BASE, (_band)) +#define MT_WF_MIB(_band, ofs) (MT_WF_MIB_BASE(_band) + (ofs)) + +#define MT_MIB_BSCR0(_band) MT_WF_MIB(_band, 0x9cc) +#define MT_MIB_BSCR1(_band) MT_WF_MIB(_band, 0x9d0) +#define MT_MIB_BSCR2(_band) MT_WF_MIB(_band, 0x9d4) +#define MT_MIB_BSCR3(_band) MT_WF_MIB(_band, 0x9d8) +#define MT_MIB_BSCR4(_band) MT_WF_MIB(_band, 0x9dc) +#define MT_MIB_BSCR5(_band) MT_WF_MIB(_band, 0x9e0) +#define MT_MIB_BSCR6(_band) MT_WF_MIB(_band, 0x9e4) +#define MT_MIB_BSCR7(_band) MT_WF_MIB(_band, 0x9e8) +#define MT_MIB_BSCR17(_band) MT_WF_MIB(_band, 0xa10) + +#define MT_MIB_TSCR5(_band) MT_WF_MIB(_band, 0x6c4) +#define MT_MIB_TSCR6(_band) MT_WF_MIB(_band, 0x6c8) +#define MT_MIB_TSCR7(_band) MT_WF_MIB(_band, 0x6d0) + +#define MT_MIB_RSCR1(_band) MT_WF_MIB(_band, 0x7ac) +/* rx mpdu counter, full 32 bits */ +#define MT_MIB_RSCR31(_band) MT_WF_MIB(_band, 0x964) +#define MT_MIB_RSCR33(_band) MT_WF_MIB(_band, 0x96c) + +#define MT_MIB_SDR6(_band) MT_WF_MIB(_band, 0x020) +#define MT_MIB_SDR6_CHANNEL_IDL_CNT_MASK GENMASK(15, 0) + +#define MT_MIB_RVSR0(_band) MT_WF_MIB(_band, 0x720) + +#define MT_MIB_RSCR35(_band) MT_WF_MIB(_band, 0x974) +#define MT_MIB_RSCR36(_band) MT_WF_MIB(_band, 0x978) + +/* tx ampdu cnt, full 32 bits */ +#define MT_MIB_TSCR0(_band) MT_WF_MIB(_band, 0x6b0) +#define MT_MIB_TSCR2(_band) MT_WF_MIB(_band, 0x6b8) + +/* counts all mpdus in ampdu, regardless of success */ +#define MT_MIB_TSCR3(_band) MT_WF_MIB(_band, 0x6bc) + +/* counts all successfully tx'd mpdus in ampdu */ +#define MT_MIB_TSCR4(_band) MT_WF_MIB(_band, 0x6c0) + +/* rx ampdu count, 32-bit */ +#define MT_MIB_RSCR27(_band) MT_WF_MIB(_band, 0x954) + +/* rx ampdu bytes count, 32-bit */ +#define MT_MIB_RSCR28(_band) MT_WF_MIB(_band, 0x958) + +/* rx ampdu valid subframe count */ +#define MT_MIB_RSCR29(_band) MT_WF_MIB(_band, 0x95c) + +/* rx ampdu valid subframe bytes count, 32bits */ +#define MT_MIB_RSCR30(_band) MT_WF_MIB(_band, 0x960) + +/* remaining windows protected stats */ +#define MT_MIB_SDR27(_band) MT_WF_MIB(_band, 0x080) +#define MT_MIB_SDR27_TX_RWP_FAIL_CNT GENMASK(15, 0) + +#define MT_MIB_SDR28(_band) MT_WF_MIB(_band, 0x084) +#define MT_MIB_SDR28_TX_RWP_NEED_CNT GENMASK(15, 0) + +#define MT_MIB_RVSR1(_band) MT_WF_MIB(_band, 0x724) + +/* rx blockack count, 32 bits */ +#define MT_MIB_TSCR1(_band) MT_WF_MIB(_band, 0x6b4) + +#define MT_MIB_BTSCR0(_band) MT_WF_MIB(_band, 0x5e0) +#define MT_MIB_BTSCR5(_band) MT_WF_MIB(_band, 0x788) +#define MT_MIB_BTSCR6(_band) MT_WF_MIB(_band, 0x798) + +#define MT_MIB_BFTFCR(_band) MT_WF_MIB(_band, 0x5d0) + +#define MT_TX_AGG_CNT(_band, n) MT_WF_MIB(_band, 0xa28 + ((n) << 2)) +#define MT_MIB_ARNG(_band, n) MT_WF_MIB(_band, 0x0b0 + ((n) << 2)) +#define MT_MIB_ARNCR_RANGE(val, n) (((val) >> ((n) << 4)) & GENMASK(9, 0)) + +/* UMIB */ +#define MT_WF_UMIB_BASE 0x820cd000 +#define MT_WF_UMIB(ofs) (MT_WF_UMIB_BASE + (ofs)) + +#define MT_UMIB_RPDCR(_band) (MT_WF_UMIB(0x594) + (_band) * 0x164) + +/* WTBLON TOP */ +#define MT_WTBLON_TOP_BASE 0x820d4000 +#define MT_WTBLON_TOP(ofs) (MT_WTBLON_TOP_BASE + (ofs)) +#define MT_WTBLON_TOP_WDUCR MT_WTBLON_TOP(0x370) +#define MT_WTBLON_TOP_WDUCR_GROUP GENMASK(4, 0) + +#define MT_WTBL_UPDATE MT_WTBLON_TOP(0x380) +#define MT_WTBL_UPDATE_WLAN_IDX GENMASK(11, 0) +#define MT_WTBL_UPDATE_ADM_COUNT_CLEAR BIT(14) +#define MT_WTBL_UPDATE_BUSY BIT(31) + +/* WTBL */ +#define MT_WTBL_BASE 0x820d8000 +#define MT_WTBL_LMAC_ID GENMASK(14, 8) +#define MT_WTBL_LMAC_DW GENMASK(7, 2) +#define MT_WTBL_LMAC_OFFS(_id, _dw) (MT_WTBL_BASE | \ + FIELD_PREP(MT_WTBL_LMAC_ID, _id) | \ + FIELD_PREP(MT_WTBL_LMAC_DW, _dw)) + +/* AGG: band 0(0x820e2000), band 1(0x820f2000), band 2(0x830e2000) */ +#define MT_WF_AGG_BASE(_band) __BASE(WF_AGG_BASE, (_band)) +#define MT_WF_AGG(_band, ofs) (MT_WF_AGG_BASE(_band) + (ofs)) + +#define MT_AGG_ACR0(_band) MT_WF_AGG(_band, 0x054) +#define MT_AGG_ACR_CFEND_RATE GENMASK(13, 0) + +/* ARB: band 0(0x820e3000), band 1(0x820f3000), band 2(0x830e3000) */ +#define MT_WF_ARB_BASE(_band) __BASE(WF_ARB_BASE, (_band)) +#define MT_WF_ARB(_band, ofs) (MT_WF_ARB_BASE(_band) + (ofs)) + +#define MT_ARB_SCR(_band) MT_WF_ARB(_band, 0x000) +#define MT_ARB_SCR_TX_DISABLE BIT(8) +#define MT_ARB_SCR_RX_DISABLE BIT(9) + +/* RMAC: band 0(0x820e5000), band 1(0x820f5000), band 2(0x830e5000), */ +#define MT_WF_RMAC_BASE(_band) __BASE(WF_RMAC_BASE, (_band)) +#define MT_WF_RMAC(_band, ofs) (MT_WF_RMAC_BASE(_band) + (ofs)) + +#define MT_WF_RFCR(_band) MT_WF_RMAC(_band, 0x000) +#define MT_WF_RFCR_DROP_STBC_MULTI BIT(0) +#define MT_WF_RFCR_DROP_FCSFAIL BIT(1) +#define MT_WF_RFCR_DROP_PROBEREQ BIT(4) +#define MT_WF_RFCR_DROP_MCAST BIT(5) +#define MT_WF_RFCR_DROP_BCAST BIT(6) +#define MT_WF_RFCR_DROP_MCAST_FILTERED BIT(7) +#define MT_WF_RFCR_DROP_A3_MAC BIT(8) +#define MT_WF_RFCR_DROP_A3_BSSID BIT(9) +#define MT_WF_RFCR_DROP_A2_BSSID BIT(10) +#define MT_WF_RFCR_DROP_OTHER_BEACON BIT(11) +#define MT_WF_RFCR_DROP_FRAME_REPORT BIT(12) +#define MT_WF_RFCR_DROP_CTL_RSV BIT(13) +#define MT_WF_RFCR_DROP_CTS BIT(14) +#define MT_WF_RFCR_DROP_RTS BIT(15) +#define MT_WF_RFCR_DROP_DUPLICATE BIT(16) +#define MT_WF_RFCR_DROP_OTHER_BSS BIT(17) +#define MT_WF_RFCR_DROP_OTHER_UC BIT(18) +#define MT_WF_RFCR_DROP_OTHER_TIM BIT(19) +#define MT_WF_RFCR_DROP_NDPA BIT(20) +#define MT_WF_RFCR_DROP_UNWANTED_CTL BIT(21) + +#define MT_WF_RFCR1(_band) MT_WF_RMAC(_band, 0x004) +#define MT_WF_RFCR1_DROP_ACK BIT(4) +#define MT_WF_RFCR1_DROP_BF_POLL BIT(5) +#define MT_WF_RFCR1_DROP_BA BIT(6) +#define MT_WF_RFCR1_DROP_CFEND BIT(7) +#define MT_WF_RFCR1_DROP_CFACK BIT(8) + +#define MT_WF_RMAC_MIB_AIRTIME0(_band) MT_WF_RMAC(_band, 0x0380) +#define MT_WF_RMAC_MIB_RXTIME_CLR BIT(31) +#define MT_WF_RMAC_MIB_ED_OFFSET GENMASK(20, 16) +#define MT_WF_RMAC_MIB_OBSS_BACKOFF GENMASK(15, 0) + +#define MT_WF_RMAC_MIB_AIRTIME1(_band) MT_WF_RMAC(_band, 0x0384) +#define MT_WF_RMAC_MIB_NONQOSD_BACKOFF GENMASK(31, 16) + +#define MT_WF_RMAC_MIB_AIRTIME3(_band) MT_WF_RMAC(_band, 0x038c) +#define MT_WF_RMAC_MIB_QOS01_BACKOFF GENMASK(31, 0) + +#define MT_WF_RMAC_MIB_AIRTIME4(_band) MT_WF_RMAC(_band, 0x0390) +#define MT_WF_RMAC_MIB_QOS23_BACKOFF GENMASK(31, 0) + +#define MT_WF_RMAC_RSVD0(_band) MT_WF_RMAC(_band, 0x03e0) +#define MT_WF_RMAC_RSVD0_EIFS_CLR BIT(21) + +/* WFDMA0 */ +#define MT_WFDMA0_BASE 0xd4000 +#define MT_WFDMA0(ofs) (MT_WFDMA0_BASE + (ofs)) + +#define MT_WFDMA0_RST MT_WFDMA0(0x100) +#define MT_WFDMA0_RST_LOGIC_RST BIT(4) +#define MT_WFDMA0_RST_DMASHDL_ALL_RST BIT(5) + +#define MT_WFDMA0_BUSY_ENA MT_WFDMA0(0x13c) +#define MT_WFDMA0_BUSY_ENA_TX_FIFO0 BIT(0) +#define MT_WFDMA0_BUSY_ENA_TX_FIFO1 BIT(1) +#define MT_WFDMA0_BUSY_ENA_RX_FIFO BIT(2) + +#define MT_WFDMA0_RX_INT_PCIE_SEL MT_WFDMA0(0x154) +#define MT_WFDMA0_RX_INT_SEL_RING3 BIT(3) + +#define MT_WFDMA0_GLO_CFG MT_WFDMA0(0x208) +#define MT_WFDMA0_GLO_CFG_TX_DMA_EN BIT(0) +#define MT_WFDMA0_GLO_CFG_RX_DMA_EN BIT(2) +#define MT_WFDMA0_GLO_CFG_OMIT_TX_INFO BIT(28) +#define MT_WFDMA0_GLO_CFG_OMIT_RX_INFO BIT(27) +#define MT_WFDMA0_GLO_CFG_OMIT_RX_INFO_PFET2 BIT(21) + +#define WF_WFDMA0_GLO_CFG_EXT0 MT_WFDMA0(0x2b0) +#define WF_WFDMA0_GLO_CFG_EXT0_RX_WB_RXD BIT(18) +#define WF_WFDMA0_GLO_CFG_EXT0_WED_MERGE_MODE BIT(14) + +#define WF_WFDMA0_GLO_CFG_EXT1 MT_WFDMA0(0x2b4) +#define WF_WFDMA0_GLO_CFG_EXT1_CALC_MODE BIT(31) +#define WF_WFDMA0_GLO_CFG_EXT1_TX_FCTRL_MODE BIT(28) + +#define MT_WFDMA0_RST_DTX_PTR MT_WFDMA0(0x20c) +#define MT_WFDMA0_PRI_DLY_INT_CFG0 MT_WFDMA0(0x2f0) +#define MT_WFDMA0_PRI_DLY_INT_CFG1 MT_WFDMA0(0x2f4) +#define MT_WFDMA0_PRI_DLY_INT_CFG2 MT_WFDMA0(0x2f8) + +/* WFDMA1 */ +#define MT_WFDMA1_BASE 0xd5000 + +/* WFDMA CSR */ +#define MT_WFDMA_EXT_CSR_BASE 0xd7000 +#define MT_WFDMA_EXT_CSR(ofs) (MT_WFDMA_EXT_CSR_BASE + (ofs)) + +#define MT_WFDMA_HOST_CONFIG MT_WFDMA_EXT_CSR(0x30) +#define MT_WFDMA_HOST_CONFIG_PDMA_BAND BIT(0) + +#define MT_WFDMA_EXT_CSR_HIF_MISC MT_WFDMA_EXT_CSR(0x44) +#define MT_WFDMA_EXT_CSR_HIF_MISC_BUSY BIT(0) + +#define MT_PCIE_RECOG_ID 0xd7090 +#define MT_PCIE_RECOG_ID_MASK GENMASK(30, 0) +#define MT_PCIE_RECOG_ID_SEM BIT(31) + +/* WFDMA0 PCIE1 */ +#define MT_WFDMA0_PCIE1_BASE 0xd8000 +#define MT_WFDMA0_PCIE1(ofs) (MT_WFDMA0_PCIE1_BASE + (ofs)) + +#define MT_WFDMA0_PCIE1_BUSY_ENA MT_WFDMA0_PCIE1(0x13c) +#define MT_WFDMA0_PCIE1_BUSY_ENA_TX_FIFO0 BIT(0) +#define MT_WFDMA0_PCIE1_BUSY_ENA_TX_FIFO1 BIT(1) +#define MT_WFDMA0_PCIE1_BUSY_ENA_RX_FIFO BIT(2) + +/* WFDMA COMMON */ +#define __RXQ(q) ((q) + __MT_MCUQ_MAX) +#define __TXQ(q) (__RXQ(q) + __MT_RXQ_MAX) + +#define MT_Q_ID(q) (dev->q_id[(q)]) +#define MT_Q_BASE(q) ((dev->q_wfdma_mask >> (q)) & 0x1 ? \ + MT_WFDMA1_BASE : MT_WFDMA0_BASE) + +#define MT_MCUQ_ID(q) MT_Q_ID(q) +#define MT_TXQ_ID(q) MT_Q_ID(__TXQ(q)) +#define MT_RXQ_ID(q) MT_Q_ID(__RXQ(q)) + +#define MT_MCUQ_RING_BASE(q) (MT_Q_BASE(q) + 0x300) +#define MT_TXQ_RING_BASE(q) (MT_Q_BASE(__TXQ(q)) + 0x300) +#define MT_RXQ_RING_BASE(q) (MT_Q_BASE(__RXQ(q)) + 0x500) + +#define MT_MCUQ_EXT_CTRL(q) (MT_Q_BASE(q) + 0x600 + \ + MT_MCUQ_ID(q) * 0x4) +#define MT_RXQ_BAND1_CTRL(q) (MT_Q_BASE(__RXQ(q)) + 0x680 + \ + MT_RXQ_ID(q) * 0x4) +#define MT_TXQ_EXT_CTRL(q) (MT_Q_BASE(__TXQ(q)) + 0x600 + \ + MT_TXQ_ID(q) * 0x4) + +#define MT_INT_SOURCE_CSR MT_WFDMA0(0x200) +#define MT_INT_MASK_CSR MT_WFDMA0(0x204) + +#define MT_INT1_SOURCE_CSR MT_WFDMA0_PCIE1(0x200) +#define MT_INT1_MASK_CSR MT_WFDMA0_PCIE1(0x204) + +#define MT_INT_RX_DONE_BAND0 BIT(12) +#define MT_INT_RX_DONE_BAND1 BIT(12) +#define MT_INT_RX_DONE_BAND2 BIT(13) +#define MT_INT_RX_DONE_WM BIT(0) +#define MT_INT_RX_DONE_WA BIT(1) +#define MT_INT_RX_DONE_WA_MAIN BIT(2) +#define MT_INT_RX_DONE_WA_EXT BIT(2) +#define MT_INT_RX_DONE_WA_TRI BIT(3) +#define MT_INT_RX_TXFREE_MAIN BIT(17) +#define MT_INT_RX_TXFREE_TRI BIT(15) +#define MT_INT_MCU_CMD BIT(29) + +#define MT_INT_RX(q) (dev->q_int_mask[__RXQ(q)]) +#define MT_INT_TX_MCU(q) (dev->q_int_mask[(q)]) + +#define MT_INT_RX_DONE_MCU (MT_INT_RX(MT_RXQ_MCU) | \ + MT_INT_RX(MT_RXQ_MCU_WA)) + +#define MT_INT_BAND0_RX_DONE (MT_INT_RX(MT_RXQ_MAIN) | \ + MT_INT_RX(MT_RXQ_MAIN_WA)) + +#define MT_INT_BAND1_RX_DONE (MT_INT_RX(MT_RXQ_BAND1) | \ + MT_INT_RX(MT_RXQ_BAND1_WA) | \ + MT_INT_RX(MT_RXQ_MAIN_WA)) + +#define MT_INT_BAND2_RX_DONE (MT_INT_RX(MT_RXQ_BAND2) | \ + MT_INT_RX(MT_RXQ_BAND2_WA) | \ + MT_INT_RX(MT_RXQ_MAIN_WA)) + +#define MT_INT_RX_DONE_ALL (MT_INT_RX_DONE_MCU | \ + MT_INT_BAND0_RX_DONE | \ + MT_INT_BAND1_RX_DONE | \ + MT_INT_BAND2_RX_DONE) + +#define MT_INT_TX_DONE_FWDL BIT(26) +#define MT_INT_TX_DONE_MCU_WM BIT(27) +#define MT_INT_TX_DONE_MCU_WA BIT(22) +#define MT_INT_TX_DONE_BAND0 BIT(30) +#define MT_INT_TX_DONE_BAND1 BIT(31) +#define MT_INT_TX_DONE_BAND2 BIT(15) + +#define MT_INT_TX_DONE_MCU (MT_INT_TX_MCU(MT_MCUQ_WA) | \ + MT_INT_TX_MCU(MT_MCUQ_WM) | \ + MT_INT_TX_MCU(MT_MCUQ_FWDL)) + +#define MT_MCU_CMD MT_WFDMA0(0x1f0) +#define MT_MCU_CMD_STOP_DMA BIT(2) +#define MT_MCU_CMD_RESET_DONE BIT(3) +#define MT_MCU_CMD_RECOVERY_DONE BIT(4) +#define MT_MCU_CMD_NORMAL_STATE BIT(5) +#define MT_MCU_CMD_ERROR_MASK GENMASK(5, 1) + +/* l1/l2 remap */ +#define MT_HIF_REMAP_L1 0x155024 +#define MT_HIF_REMAP_L1_MASK GENMASK(31, 16) +#define MT_HIF_REMAP_L1_OFFSET GENMASK(15, 0) +#define MT_HIF_REMAP_L1_BASE GENMASK(31, 16) +#define MT_HIF_REMAP_BASE_L1 0x130000 + +#define MT_HIF_REMAP_L2 0x1b4 +#define MT_HIF_REMAP_L2_MASK GENMASK(19, 0) +#define MT_HIF_REMAP_L2_OFFSET GENMASK(11, 0) +#define MT_HIF_REMAP_L2_BASE GENMASK(31, 12) +#define MT_HIF_REMAP_BASE_L2 0x1000 + +#define MT_INFRA_BASE 0x18000000 +#define MT_WFSYS0_PHY_START 0x18400000 +#define MT_WFSYS1_PHY_START 0x18800000 +#define MT_WFSYS1_PHY_END 0x18bfffff +#define MT_CBTOP1_PHY_START 0x70000000 +#define MT_CBTOP1_PHY_END 0x77ffffff +#define MT_CBTOP2_PHY_START 0xf0000000 +#define MT_CBTOP2_PHY_END 0xffffffff +#define MT_INFRA_MCU_START 0x7c000000 +#define MT_INFRA_MCU_END 0x7c3fffff + +/* FW MODE SYNC */ +#define MT_SWDEF_MODE 0x9143c +#define MT_SWDEF_NORMAL_MODE 0 + +/* LED */ +#define MT_LED_TOP_BASE 0x18013000 +#define MT_LED_PHYS(_n) (MT_LED_TOP_BASE + (_n)) + +#define MT_LED_CTRL(_n) MT_LED_PHYS(0x00 + ((_n) * 4)) +#define MT_LED_CTRL_KICK BIT(7) +#define MT_LED_CTRL_BLINK_MODE BIT(2) +#define MT_LED_CTRL_POLARITY BIT(1) + +#define MT_LED_TX_BLINK(_n) MT_LED_PHYS(0x10 + ((_n) * 4)) +#define MT_LED_TX_BLINK_ON_MASK GENMASK(7, 0) +#define MT_LED_TX_BLINK_OFF_MASK GENMASK(15, 8) + +#define MT_LED_EN(_n) MT_LED_PHYS(0x40 + ((_n) * 4)) + +#define MT_LED_GPIO_MUX2 0x70005058 /* GPIO 18 */ +#define MT_LED_GPIO_MUX3 0x7000505C /* GPIO 26 */ +#define MT_LED_GPIO_SEL_MASK GENMASK(11, 8) + +/* MT TOP */ +#define MT_TOP_BASE 0xe0000 +#define MT_TOP(ofs) (MT_TOP_BASE + (ofs)) + +#define MT_TOP_LPCR_HOST_BAND(_band) MT_TOP(0x10 + ((_band) * 0x10)) +#define MT_TOP_LPCR_HOST_FW_OWN BIT(0) +#define MT_TOP_LPCR_HOST_DRV_OWN BIT(1) +#define MT_TOP_LPCR_HOST_FW_OWN_STAT BIT(2) + +#define MT_TOP_LPCR_HOST_BAND_IRQ_STAT(_band) MT_TOP(0x14 + ((_band) * 0x10)) +#define MT_TOP_LPCR_HOST_BAND_STAT BIT(0) + +#define MT_TOP_MISC MT_TOP(0xf0) +#define MT_TOP_MISC_FW_STATE GENMASK(2, 0) + +#define MT_HW_REV 0x70010204 +#define MT_WF_SUBSYS_RST 0x70002600 + +/* PCIE MAC */ +#define MT_PCIE_MAC_BASE 0x74030000 +#define MT_PCIE_MAC(ofs) (MT_PCIE_MAC_BASE + (ofs)) +#define MT_PCIE_MAC_INT_ENABLE MT_PCIE_MAC(0x188) + +#define MT_PCIE1_MAC_BASE 0x74090000 +#define MT_PCIE1_MAC(ofs) (MT_PCIE1_MAC_BASE + (ofs)) + +#define MT_PCIE1_MAC_INT_ENABLE MT_PCIE1_MAC(0x188) + +/* PHYRX CTRL */ +#define MT_WF_PHYRX_BAND_BASE 0x83080000 +#define MT_WF_PHYRX_BAND(_band, ofs) (MT_WF_PHYRX_BAND_BASE + \ + ((_band) << 20) + (ofs)) + +#define MT_WF_PHYRX_BAND_RX_CTRL1(_band) MT_WF_PHYRX_BAND(_band, 0x2004) +#define MT_WF_PHYRX_BAND_RX_CTRL1_IPI_EN GENMASK(2, 0) +#define MT_WF_PHYRX_BAND_RX_CTRL1_STSCNT_EN GENMASK(11, 9) + +/* PHYRX CSD */ +#define MT_WF_PHYRX_CSD_BASE 0x83000000 +#define MT_WF_PHYRX_CSD(_band, _wf, ofs) (MT_WF_PHYRX_CSD_BASE + \ + ((_band) << 20) + \ + ((_wf) << 16) + (ofs)) +#define MT_WF_PHYRX_CSD_IRPI(_band, _wf) MT_WF_PHYRX_CSD(_band, _wf, 0x1000) + +/* PHYRX CSD BAND */ +#define MT_WF_PHYRX_CSD_BAND_RXTD12(_band) MT_WF_PHYRX_BAND(_band, 0x8230) +#define MT_WF_PHYRX_CSD_BAND_RXTD12_IRPI_SW_CLR_ONLY BIT(18) +#define MT_WF_PHYRX_CSD_BAND_RXTD12_IRPI_SW_CLR BIT(29) + +#endif From 03dd0d49de7db680a856fa566963bb8421f46368 Mon Sep 17 00:00:00 2001 From: Deren Wu Date: Sun, 27 Nov 2022 10:35:37 +0800 Subject: [PATCH 1512/2002] wifi: mt76: fix coverity overrun-call in mt76_get_txpower() Make sure the nss is valid for nss_delta array. Return zero if the index is invalid. Coverity message: Event overrun-call: Overrunning callee's array of size 4 by passing argument "n_chains" (which evaluates to 15) in call to "mt76_tx_power_nss_delta". int delta = mt76_tx_power_nss_delta(n_chains); Fixes: 07cda406308b ("mt76: fix rounding issues on converting per-chain and combined txpower") Signed-off-by: Deren Wu Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt76.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt76.h b/drivers/net/wireless/mediatek/mt76/mt76.h index 33f87e518d68..32a77a0ae9da 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76.h +++ b/drivers/net/wireless/mediatek/mt76/mt76.h @@ -1110,8 +1110,9 @@ static inline bool mt76_is_skb_pktid(u8 pktid) static inline u8 mt76_tx_power_nss_delta(u8 nss) { static const u8 nss_delta[4] = { 0, 6, 9, 12 }; + u8 idx = nss - 1; - return nss_delta[nss - 1]; + return (idx < ARRAY_SIZE(nss_delta)) ? nss_delta[idx] : 0; } static inline bool mt76_testmode_enabled(struct mt76_phy *phy) From e5c6bc6f19d8c293f86b347cddab54d5a3830b38 Mon Sep 17 00:00:00 2001 From: Deren Wu Date: Mon, 28 Nov 2022 15:04:21 +0800 Subject: [PATCH 1513/2002] wifi: mt76: mt7921: Add missing __packed annotation of struct mt7921_clc Add __packed annotation to avoid potential CLC parsing error Fixes: 23bdc5d8cadf ("wifi: mt76: mt7921: introduce Country Location Control support") Signed-off-by: Deren Wu Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt7921/mt7921.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/mt7921.h b/drivers/net/wireless/mediatek/mt76/mt7921/mt7921.h index 6fc04ed34ec3..15d6b7fe1c6c 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/mt7921.h +++ b/drivers/net/wireless/mediatek/mt76/mt7921/mt7921.h @@ -224,7 +224,7 @@ struct mt7921_clc { u8 type; u8 rsv[8]; u8 data[]; -}; +} __packed; struct mt7921_phy { struct mt76_phy *mt76; From f37f76d43865c58cb96aa13c87164abb41f22d0b Mon Sep 17 00:00:00 2001 From: Deren Wu Date: Thu, 24 Nov 2022 22:20:38 +0800 Subject: [PATCH 1514/2002] wifi: mt76: do not send firmware FW_FEATURE_NON_DL region skip invalid section to avoid potential risks Fixes: 23bdc5d8cadf ("wifi: mt76: mt7921: introduce Country Location Control support") Signed-off-by: Deren Wu Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c index 96d91238ad8d..5a047e630860 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c @@ -2906,6 +2906,9 @@ mt76_connac_mcu_send_ram_firmware(struct mt76_dev *dev, len = le32_to_cpu(region->len); addr = le32_to_cpu(region->addr); + if (region->feature_set & FW_FEATURE_NON_DL) + goto next; + if (region->feature_set & FW_FEATURE_OVERRIDE_ADDR) override = addr; @@ -2922,6 +2925,7 @@ mt76_connac_mcu_send_ram_firmware(struct mt76_dev *dev, return err; } +next: offset += len; } From 5938196cc188ba4323bc6357f5ac55127d715888 Mon Sep 17 00:00:00 2001 From: Xiongfeng Wang Date: Fri, 25 Nov 2022 10:58:31 +0800 Subject: [PATCH 1515/2002] mt76: mt7915: Fix PCI device refcount leak in mt7915_pci_init_hif2() As comment of pci_get_device() says, it returns a pci_device with its refcount increased. We need to call pci_dev_put() to decrease the refcount. Save the return value of pci_get_device() and call pci_dev_put() to decrease the refcount. Fixes: 9093cfff72e3 ("mt76: mt7915: add support for using a secondary PCIe link for gen1") Fixes: 2e30db0dde61 ("mt76: mt7915: add device id for mt7916") Signed-off-by: Xiongfeng Wang Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt7915/pci.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/pci.c b/drivers/net/wireless/mediatek/mt76/mt7915/pci.c index 743f01f6eae9..39132894e8ea 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/pci.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/pci.c @@ -62,10 +62,17 @@ static void mt7915_put_hif2(struct mt7915_hif *hif) static struct mt7915_hif *mt7915_pci_init_hif2(struct pci_dev *pdev) { + struct pci_dev *tmp_pdev; + hif_idx++; - if (!pci_get_device(PCI_VENDOR_ID_MEDIATEK, 0x7916, NULL) && - !pci_get_device(PCI_VENDOR_ID_MEDIATEK, 0x790a, NULL)) - return NULL; + + tmp_pdev = pci_get_device(PCI_VENDOR_ID_MEDIATEK, 0x7916, NULL); + if (!tmp_pdev) { + tmp_pdev = pci_get_device(PCI_VENDOR_ID_MEDIATEK, 0x790a, NULL); + if (!tmp_pdev) + return NULL; + } + pci_dev_put(tmp_pdev); writel(hif_idx | MT_PCIE_RECOG_ID_SEM, pcim_iomap_table(pdev)[0] + MT_PCIE_RECOG_ID); From 54dd1dc796f7b5ad12e8aa02945cc669bc870591 Mon Sep 17 00:00:00 2001 From: Ryder Lee Date: Thu, 24 Nov 2022 03:59:10 +0800 Subject: [PATCH 1516/2002] wifi: mt76: mt7915: introduce mt7915_get_power_bound() Add a helper for common boundary check. This is a preliminary patch to add per bandwidth power control. Signed-off-by: Ryder Lee Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt7915/mcu.c | 12 +++++------- drivers/net/wireless/mediatek/mt76/mt7915/mcu.h | 12 ++++++++++++ 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c index 718e52744994..b640ce9249c9 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c @@ -3144,14 +3144,14 @@ int mt7915_mcu_set_txpower_frame(struct mt7915_phy *phy, .txpower_max = DIV_ROUND_UP(mphy->txpower_cur, 2), .wcid = cpu_to_le16(msta->wcid.idx), }; - int ret, n_chains = hweight8(mphy->antenna_mask); + int ret; s8 txpower_sku[MT7915_SKU_RATE_NUM]; ret = mt7915_mcu_get_txpower_sku(phy, txpower_sku, sizeof(txpower_sku)); if (ret) return ret; - txpower = txpower * 2 - mt76_tx_power_nss_delta(n_chains); + txpower = mt7915_get_power_bound(phy, txpower); if (txpower > mphy->txpower_cur || txpower < 0) return -EINVAL; @@ -3203,12 +3203,10 @@ int mt7915_mcu_set_txpower_sku(struct mt7915_phy *phy) }; struct mt76_power_limits limits_array; s8 *la = (s8 *)&limits_array; - int i, idx, n_chains = hweight8(mphy->antenna_mask); - int tx_power = hw->conf.power_level * 2; + int i, idx; + int tx_power; - tx_power = mt76_get_sar_power(mphy, mphy->chandef.chan, - tx_power); - tx_power -= mt76_tx_power_nss_delta(n_chains); + tx_power = mt7915_get_power_bound(phy, hw->conf.power_level); tx_power = mt76_get_rate_power_limits(mphy, mphy->chandef.chan, &limits_array, tx_power); mphy->txpower_cur = tx_power; diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.h b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.h index 46c517e50ae4..382dcbda50bb 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.h +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.h @@ -509,4 +509,16 @@ enum { sizeof(struct bss_info_bcn_cont) + \ sizeof(struct bss_info_inband_discovery)) +static inline s8 +mt7915_get_power_bound(struct mt7915_phy *phy, s8 txpower) +{ + struct mt76_phy *mphy = phy->mt76; + int n_chains = hweight8(mphy->antenna_mask); + + txpower = mt76_get_sar_power(mphy, mphy->chandef.chan, txpower * 2); + txpower -= mt76_tx_power_nss_delta(n_chains); + + return txpower; +} + #endif From e3296759f34752ea2562678706dbb5bf607af530 Mon Sep 17 00:00:00 2001 From: Ryder Lee Date: Thu, 24 Nov 2022 03:59:11 +0800 Subject: [PATCH 1517/2002] wifi: mt76: mt7915: enable per bandwidth power limit support This power should override the per bandwidth max power that the device emits. Signed-off-by: Ryder Lee Signed-off-by: Felix Fietkau --- .../wireless/mediatek/mt76/mt7915/debugfs.c | 204 +++++++++++++++--- .../net/wireless/mediatek/mt76/mt7915/mcu.c | 9 +- .../net/wireless/mediatek/mt76/mt7915/mcu.h | 7 + 3 files changed, 186 insertions(+), 34 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/debugfs.c b/drivers/net/wireless/mediatek/mt76/mt7915/debugfs.c index fef0ec83185b..8e586d101438 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/debugfs.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/debugfs.c @@ -957,48 +957,198 @@ mt7915_xmit_queues_show(struct seq_file *file, void *data) DEFINE_SHOW_ATTRIBUTE(mt7915_xmit_queues); -static int -mt7915_rate_txpower_show(struct seq_file *file, void *data) -{ - static const char * const sku_group_name[] = { - "CCK", "OFDM", "HT20", "HT40", - "VHT20", "VHT40", "VHT80", "VHT160", - "RU26", "RU52", "RU106", "RU242/SU20", - "RU484/SU40", "RU996/SU80", "RU2x996/SU160" - }; - struct mt7915_phy *phy = file->private; - struct mt7915_dev *dev = phy->dev; - s8 txpower[MT7915_SKU_RATE_NUM], *buf; - u32 reg; - int i, ret; +#define mt7915_txpower_puts(prefix, rate) \ +({ \ + len += scnprintf(buf + len, sz - len, "%-16s:", #prefix " (tmac)"); \ + for (i = 0; i < mt7915_sku_group_len[rate]; i++, offs++) \ + len += scnprintf(buf + len, sz - len, " %6d", txpwr[offs]); \ + len += scnprintf(buf + len, sz - len, "\n"); \ +}) - ret = mt7915_mcu_get_txpower_sku(phy, txpower, sizeof(txpower)); +#define mt7915_txpower_sets(rate, pwr, flag) \ +({ \ + offs += len; \ + len = mt7915_sku_group_len[rate]; \ + if (mode == flag) { \ + for (i = 0; i < len; i++) \ + req.txpower_sku[offs + i] = pwr; \ + } \ +}) + +static ssize_t +mt7915_rate_txpower_get(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct mt7915_phy *phy = file->private_data; + struct mt7915_dev *dev = phy->dev; + s8 txpwr[MT7915_SKU_RATE_NUM]; + static const size_t sz = 2048; + int i, offs = 0, len = 0; + ssize_t ret; + char *buf; + u32 reg; + + buf = kzalloc(sz, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + ret = mt7915_mcu_get_txpower_sku(phy, txpwr, sizeof(txpwr)); if (ret) return ret; /* Txpower propagation path: TMAC -> TXV -> BBP */ - seq_printf(file, "\nPhy %d\n", phy != &dev->phy); + len += scnprintf(buf + len, sz - len, + "\nPhy%d Tx power table (channel %d)\n", + phy != &dev->phy, phy->mt76->chandef.chan->hw_value); + len += scnprintf(buf + len, sz - len, "%-16s %6s %6s %6s %6s\n", + " ", "1m", "2m", "5m", "11m"); + mt7915_txpower_puts(CCK, SKU_CCK); - for (i = 0, buf = txpower; i < ARRAY_SIZE(mt7915_sku_group_len); i++) { - u8 mcs_num = mt7915_sku_group_len[i]; + len += scnprintf(buf + len, sz - len, + "%-16s %6s %6s %6s %6s %6s %6s %6s %6s\n", + " ", "6m", "9m", "12m", "18m", "24m", "36m", "48m", + "54m"); + mt7915_txpower_puts(OFDM, SKU_OFDM); - if (i >= SKU_VHT_BW20 && i <= SKU_VHT_BW160) - mcs_num = 10; + len += scnprintf(buf + len, sz - len, + "%-16s %6s %6s %6s %6s %6s %6s %6s %6s\n", + " ", "mcs0", "mcs1", "mcs2", "mcs3", "mcs4", + "mcs5", "mcs6", "mcs7"); + mt7915_txpower_puts(HT20, SKU_HT_BW20); - mt76_seq_puts_array(file, sku_group_name[i], buf, mcs_num); - buf += mt7915_sku_group_len[i]; - } + len += scnprintf(buf + len, sz - len, + "%-16s %6s %6s %6s %6s %6s %6s %6s %6s %6s\n", + " ", "mcs0", "mcs1", "mcs2", "mcs3", "mcs4", "mcs5", + "mcs6", "mcs7", "mcs32"); + mt7915_txpower_puts(HT40, SKU_HT_BW40); + + len += scnprintf(buf + len, sz - len, + "%-16s %6s %6s %6s %6s %6s %6s %6s %6s %6s %6s %6s %6s\n", + " ", "mcs0", "mcs1", "mcs2", "mcs3", "mcs4", "mcs5", + "mcs6", "mcs7", "mcs8", "mcs9", "mcs10", "mcs11"); + mt7915_txpower_puts(VHT20, SKU_VHT_BW20); + mt7915_txpower_puts(VHT40, SKU_VHT_BW40); + mt7915_txpower_puts(VHT80, SKU_VHT_BW80); + mt7915_txpower_puts(VHT160, SKU_VHT_BW160); + mt7915_txpower_puts(HE26, SKU_HE_RU26); + mt7915_txpower_puts(HE52, SKU_HE_RU52); + mt7915_txpower_puts(HE106, SKU_HE_RU106); + mt7915_txpower_puts(HE242, SKU_HE_RU242); + mt7915_txpower_puts(HE484, SKU_HE_RU484); + mt7915_txpower_puts(HE996, SKU_HE_RU996); + mt7915_txpower_puts(HE996x2, SKU_HE_RU2x996); reg = is_mt7915(&dev->mt76) ? MT_WF_PHY_TPC_CTRL_STAT(phy->band_idx) : MT_WF_PHY_TPC_CTRL_STAT_MT7916(phy->band_idx); - seq_printf(file, "\nBaseband transmit power %ld\n", - mt76_get_field(dev, reg, MT_WF_PHY_TPC_POWER)); + len += scnprintf(buf + len, sz - len, "\nTx power (bbp) : %6ld\n", + mt76_get_field(dev, reg, MT_WF_PHY_TPC_POWER)); - return 0; + ret = simple_read_from_buffer(user_buf, count, ppos, buf, len); + kfree(buf); + return ret; } -DEFINE_SHOW_ATTRIBUTE(mt7915_rate_txpower); +static ssize_t +mt7915_rate_txpower_set(struct file *file, const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct mt7915_phy *phy = file->private_data; + struct mt7915_dev *dev = phy->dev; + struct mt76_phy *mphy = phy->mt76; + struct mt7915_mcu_txpower_sku req = { + .format_id = TX_POWER_LIMIT_TABLE, + .band_idx = phy->band_idx, + }; + char buf[100]; + int i, ret, pwr160 = 0, pwr80 = 0, pwr40 = 0, pwr20 = 0; + enum mac80211_rx_encoding mode; + u32 offs = 0, len = 0; + + if (count >= sizeof(buf)) + return -EINVAL; + + if (copy_from_user(buf, user_buf, count)) + return -EFAULT; + + if (count && buf[count - 1] == '\n') + buf[count - 1] = '\0'; + else + buf[count] = '\0'; + + if (sscanf(buf, "%u %u %u %u %u", + &mode, &pwr160, &pwr80, &pwr40, &pwr20) != 5) { + dev_warn(dev->mt76.dev, + "per bandwidth power limit: Mode BW160 BW80 BW40 BW20"); + return -EINVAL; + } + + if (mode > RX_ENC_HE) + return -EINVAL; + + if (pwr160) + pwr160 = mt7915_get_power_bound(phy, pwr160); + if (pwr80) + pwr80 = mt7915_get_power_bound(phy, pwr80); + if (pwr40) + pwr40 = mt7915_get_power_bound(phy, pwr40); + if (pwr20) + pwr20 = mt7915_get_power_bound(phy, pwr20); + + if (pwr160 < 0 || pwr80 < 0 || pwr40 < 0 || pwr20 < 0) + return -EINVAL; + + mutex_lock(&dev->mt76.mutex); + ret = mt7915_mcu_get_txpower_sku(phy, req.txpower_sku, + sizeof(req.txpower_sku)); + if (ret) + goto out; + + mt7915_txpower_sets(SKU_CCK, pwr20, RX_ENC_LEGACY); + mt7915_txpower_sets(SKU_OFDM, pwr20, RX_ENC_LEGACY); + if (mode == RX_ENC_LEGACY) + goto skip; + + mt7915_txpower_sets(SKU_HT_BW20, pwr20, RX_ENC_HT); + mt7915_txpower_sets(SKU_HT_BW40, pwr40, RX_ENC_HT); + if (mode == RX_ENC_HT) + goto skip; + + mt7915_txpower_sets(SKU_VHT_BW20, pwr20, RX_ENC_VHT); + mt7915_txpower_sets(SKU_VHT_BW40, pwr40, RX_ENC_VHT); + mt7915_txpower_sets(SKU_VHT_BW80, pwr80, RX_ENC_VHT); + mt7915_txpower_sets(SKU_VHT_BW160, pwr160, RX_ENC_VHT); + if (mode == RX_ENC_VHT) + goto skip; + + mt7915_txpower_sets(SKU_HE_RU26, pwr20, RX_ENC_HE + 1); + mt7915_txpower_sets(SKU_HE_RU52, pwr20, RX_ENC_HE + 1); + mt7915_txpower_sets(SKU_HE_RU106, pwr20, RX_ENC_HE + 1); + mt7915_txpower_sets(SKU_HE_RU242, pwr20, RX_ENC_HE); + mt7915_txpower_sets(SKU_HE_RU484, pwr40, RX_ENC_HE); + mt7915_txpower_sets(SKU_HE_RU996, pwr80, RX_ENC_HE); + mt7915_txpower_sets(SKU_HE_RU2x996, pwr160, RX_ENC_HE); +skip: + ret = mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(TX_POWER_FEATURE_CTRL), + &req, sizeof(req), true); + if (ret) + goto out; + + mphy->txpower_cur = max(mphy->txpower_cur, + max(pwr160, max(pwr80, max(pwr40, pwr20)))); +out: + mutex_unlock(&dev->mt76.mutex); + + return ret ? ret : count; +} + +static const struct file_operations mt7915_rate_txpower_fops = { + .write = mt7915_rate_txpower_set, + .read = mt7915_rate_txpower_get, + .open = simple_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; static int mt7915_twt_stats(struct seq_file *s, void *data) diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c index b640ce9249c9..765bf634db76 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c @@ -3192,12 +3192,7 @@ int mt7915_mcu_set_txpower_sku(struct mt7915_phy *phy) struct mt7915_dev *dev = phy->dev; struct mt76_phy *mphy = phy->mt76; struct ieee80211_hw *hw = mphy->hw; - struct mt7915_sku_val { - u8 format_id; - u8 limit_type; - u8 band_idx; - s8 val[MT7915_SKU_RATE_NUM]; - } __packed req = { + struct mt7915_mcu_txpower_sku req = { .format_id = TX_POWER_LIMIT_TABLE, .band_idx = phy->band_idx, }; @@ -3225,7 +3220,7 @@ int mt7915_mcu_set_txpower_sku(struct mt7915_phy *phy) } for (j = 0; j < min_t(u8, mcs_num, len); j++) - req.val[idx + j] = la[j]; + req.txpower_sku[idx + j] = la[j]; la += mcs_num; idx += len; diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.h b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.h index 382dcbda50bb..29b5434bfdb8 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.h +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.h @@ -184,6 +184,13 @@ enum mt7915_chan_mib_offs { MIB_NON_WIFI_TIME_V2 }; +struct mt7915_mcu_txpower_sku { + u8 format_id; + u8 limit_type; + u8 band_idx; + s8 txpower_sku[MT7915_SKU_RATE_NUM]; +} __packed; + struct edca { u8 queue; u8 set; From 3eb50cc90534376484ee0836d966c75d4c778d5f Mon Sep 17 00:00:00 2001 From: Ryder Lee Date: Thu, 1 Dec 2022 11:44:41 +0800 Subject: [PATCH 1518/2002] wifi: mt76: mt7915: rely on band_idx of mt76_phy The commit dc44c45c8cd0 added band_idx into mt76_phy, so switching to rely on that. Signed-off-by: Ryder Lee Signed-off-by: Felix Fietkau --- .../wireless/mediatek/mt76/mt7915/debugfs.c | 16 +- .../net/wireless/mediatek/mt76/mt7915/dma.c | 12 +- .../wireless/mediatek/mt76/mt7915/eeprom.c | 14 +- .../net/wireless/mediatek/mt76/mt7915/init.c | 6 +- .../net/wireless/mediatek/mt76/mt7915/mac.c | 150 +++++++++--------- .../net/wireless/mediatek/mt76/mt7915/main.c | 46 +++--- .../net/wireless/mediatek/mt76/mt7915/mcu.c | 48 +++--- .../net/wireless/mediatek/mt76/mt7915/mmio.c | 8 +- .../wireless/mediatek/mt76/mt7915/mt7915.h | 1 - .../wireless/mediatek/mt76/mt7915/testmode.c | 49 +++--- 10 files changed, 184 insertions(+), 166 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/debugfs.c b/drivers/net/wireless/mediatek/mt76/mt7915/debugfs.c index 8e586d101438..fb46c2c1784f 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/debugfs.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/debugfs.c @@ -51,7 +51,7 @@ mt7915_sys_recovery_set(struct file *file, const char __user *user_buf, { struct mt7915_phy *phy = file->private_data; struct mt7915_dev *dev = phy->dev; - bool band = phy->band_idx; + bool band = phy->mt76->band_idx; char buf[16]; int ret = 0; u16 val; @@ -692,15 +692,16 @@ mt7915_ampdu_stat_read_phy(struct mt7915_phy *phy, struct mt7915_dev *dev = phy->dev; bool ext_phy = phy != &dev->phy; int bound[15], range[4], i; + u8 band = phy->mt76->band_idx; /* Tx ampdu stat */ for (i = 0; i < ARRAY_SIZE(range); i++) - range[i] = mt76_rr(dev, MT_MIB_ARNG(phy->band_idx, i)); + range[i] = mt76_rr(dev, MT_MIB_ARNG(band, i)); for (i = 0; i < ARRAY_SIZE(bound); i++) bound[i] = MT_MIB_ARNCR_RANGE(range[i / 4], i % 4) + 1; - seq_printf(file, "\nPhy %d, Phy band %d\n", ext_phy, phy->band_idx); + seq_printf(file, "\nPhy %d, Phy band %d\n", ext_phy, band); seq_printf(file, "Length: %8d | ", bound[0]); for (i = 0; i < ARRAY_SIZE(bound) - 1; i++) @@ -983,6 +984,7 @@ mt7915_rate_txpower_get(struct file *file, char __user *user_buf, struct mt7915_dev *dev = phy->dev; s8 txpwr[MT7915_SKU_RATE_NUM]; static const size_t sz = 2048; + u8 band = phy->mt76->band_idx; int i, offs = 0, len = 0; ssize_t ret; char *buf; @@ -1038,8 +1040,8 @@ mt7915_rate_txpower_get(struct file *file, char __user *user_buf, mt7915_txpower_puts(HE996, SKU_HE_RU996); mt7915_txpower_puts(HE996x2, SKU_HE_RU2x996); - reg = is_mt7915(&dev->mt76) ? MT_WF_PHY_TPC_CTRL_STAT(phy->band_idx) : - MT_WF_PHY_TPC_CTRL_STAT_MT7916(phy->band_idx); + reg = is_mt7915(&dev->mt76) ? MT_WF_PHY_TPC_CTRL_STAT(band) : + MT_WF_PHY_TPC_CTRL_STAT_MT7916(band); len += scnprintf(buf + len, sz - len, "\nTx power (bbp) : %6ld\n", mt76_get_field(dev, reg, MT_WF_PHY_TPC_POWER)); @@ -1058,7 +1060,7 @@ mt7915_rate_txpower_set(struct file *file, const char __user *user_buf, struct mt76_phy *mphy = phy->mt76; struct mt7915_mcu_txpower_sku req = { .format_id = TX_POWER_LIMIT_TABLE, - .band_idx = phy->band_idx, + .band_idx = phy->mt76->band_idx, }; char buf[100]; int i, ret, pwr160 = 0, pwr80 = 0, pwr40 = 0, pwr20 = 0; @@ -1241,7 +1243,7 @@ int mt7915_init_debugfs(struct mt7915_phy *phy) mt7915_twt_stats); debugfs_create_file("rf_regval", 0600, dir, dev, &fops_rf_regval); - if (!dev->dbdc_support || phy->band_idx) { + if (!dev->dbdc_support || phy->mt76->band_idx) { debugfs_create_u32("dfs_hw_pattern", 0400, dir, &dev->hw_pattern); debugfs_create_file("radar_trigger", 0200, dir, dev, diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/dma.c b/drivers/net/wireless/mediatek/mt76/mt7915/dma.c index ae5be28fdd9d..e3fa064918bf 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/dma.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/dma.c @@ -353,10 +353,10 @@ static int mt7915_dma_enable(struct mt7915_dev *dev) MT_INT_TX_DONE_MCU | MT_INT_MCU_CMD; - if (!dev->phy.band_idx) + if (!dev->phy.mt76->band_idx) irq_mask |= MT_INT_BAND0_RX_DONE; - if (dev->dbdc_support || dev->phy.band_idx) + if (dev->dbdc_support || dev->phy.mt76->band_idx) irq_mask |= MT_INT_BAND1_RX_DONE; if (mtk_wed_device_active(&dev->mt76.mmio.wed)) { @@ -418,7 +418,7 @@ int mt7915_dma_init(struct mt7915_dev *dev, struct mt7915_phy *phy2) /* init tx queue */ ret = mt7915_init_tx_queues(&dev->phy, - MT_TXQ_ID(dev->phy.band_idx), + MT_TXQ_ID(dev->phy.mt76->band_idx), MT7915_TX_RING_SIZE, MT_TXQ_RING_BASE(0)); if (ret) @@ -426,7 +426,7 @@ int mt7915_dma_init(struct mt7915_dev *dev, struct mt7915_phy *phy2) if (phy2) { ret = mt7915_init_tx_queues(phy2, - MT_TXQ_ID(phy2->band_idx), + MT_TXQ_ID(phy2->mt76->band_idx), MT7915_TX_RING_SIZE, MT_TXQ_RING_BASE(1)); if (ret) @@ -482,7 +482,7 @@ int mt7915_dma_init(struct mt7915_dev *dev, struct mt7915_phy *phy2) return ret; /* rx data queue for band0 */ - if (!dev->phy.band_idx) { + if (!dev->phy.mt76->band_idx) { if (mtk_wed_device_active(&mdev->mmio.wed) && mtk_wed_get_rx_capa(&mdev->mmio.wed)) { dev->mt76.q_rx[MT_RXQ_MAIN].flags = @@ -519,7 +519,7 @@ int mt7915_dma_init(struct mt7915_dev *dev, struct mt7915_phy *phy2) return ret; } - if (dev->dbdc_support || dev->phy.band_idx) { + if (dev->dbdc_support || dev->phy.mt76->band_idx) { if (mtk_wed_device_active(&mdev->mmio.wed) && mtk_wed_get_rx_capa(&mdev->mmio.wed)) { dev->mt76.q_rx[MT_RXQ_BAND1].flags = diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/eeprom.c b/drivers/net/wireless/mediatek/mt76/mt7915/eeprom.c index 0bce0ce51be0..59069fb86414 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/eeprom.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/eeprom.c @@ -131,9 +131,10 @@ static void mt7915_eeprom_parse_band_config(struct mt7915_phy *phy) { struct mt7915_dev *dev = phy->dev; u8 *eeprom = dev->mt76.eeprom.data; + u8 band = phy->mt76->band_idx; u32 val; - val = eeprom[MT_EE_WIFI_CONF + phy->band_idx]; + val = eeprom[MT_EE_WIFI_CONF + band]; val = FIELD_GET(MT_EE_WIFI_CONF0_BAND_SEL, val); if (!is_mt7915(&dev->mt76)) { @@ -153,7 +154,7 @@ static void mt7915_eeprom_parse_band_config(struct mt7915_phy *phy) return; } } else if (val == MT_EE_BAND_SEL_DEFAULT && dev->dbdc_support) { - val = phy->band_idx ? MT_EE_BAND_SEL_5GHZ : MT_EE_BAND_SEL_2GHZ; + val = band ? MT_EE_BAND_SEL_5GHZ : MT_EE_BAND_SEL_2GHZ; } switch (val) { @@ -175,6 +176,7 @@ void mt7915_eeprom_parse_hw_cap(struct mt7915_dev *dev, { u8 path, nss, nss_max = 4, *eeprom = dev->mt76.eeprom.data; struct mt76_phy *mphy = phy->mt76; + u8 band = phy->mt76->band_idx; mt7915_eeprom_parse_band_config(phy); @@ -184,7 +186,7 @@ void mt7915_eeprom_parse_hw_cap(struct mt7915_dev *dev, eeprom[MT_EE_WIFI_CONF]); } else { path = FIELD_GET(MT_EE_WIFI_CONF0_TX_PATH, - eeprom[MT_EE_WIFI_CONF + phy->band_idx]); + eeprom[MT_EE_WIFI_CONF + band]); } if (!path || path > 4) @@ -197,12 +199,12 @@ void mt7915_eeprom_parse_hw_cap(struct mt7915_dev *dev, path = min_t(u8, path, 2); nss = FIELD_GET(MT_EE_WIFI_CONF3_TX_PATH_B0, eeprom[MT_EE_WIFI_CONF + 3]); - if (phy->band_idx) + if (band) nss = FIELD_GET(MT_EE_WIFI_CONF3_TX_PATH_B1, eeprom[MT_EE_WIFI_CONF + 3]); } else { nss = FIELD_GET(MT_EE_WIFI_CONF_STREAM_NUM, - eeprom[MT_EE_WIFI_CONF + 2 + phy->band_idx]); + eeprom[MT_EE_WIFI_CONF + 2 + band]); } if (!is_mt7986(&dev->mt76)) @@ -214,7 +216,7 @@ void mt7915_eeprom_parse_hw_cap(struct mt7915_dev *dev, nss = min_t(u8, min_t(u8, nss_max, nss), path); mphy->chainmask = BIT(path) - 1; - if (phy->band_idx) + if (band) mphy->chainmask <<= dev->chainshift; mphy->antenna_mask = BIT(nss) - 1; dev->chainmask |= mphy->chainmask; diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/init.c b/drivers/net/wireless/mediatek/mt76/mt7915/init.c index 79bd7bf93f33..c810c31fbd6e 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/init.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/init.c @@ -540,7 +540,7 @@ mt7915_alloc_ext_phy(struct mt7915_dev *dev) phy->mt76 = mphy; /* Bind main phy to band0 and ext_phy to band1 for dbdc case */ - phy->band_idx = 1; + phy->mt76->band_idx = 1; return phy; } @@ -660,7 +660,7 @@ static bool mt7915_band_config(struct mt7915_dev *dev) { bool ret = true; - dev->phy.band_idx = 0; + dev->phy.mt76->band_idx = 0; if (is_mt7986(&dev->mt76)) { u32 sku = mt7915_check_adie(dev, true); @@ -671,7 +671,7 @@ static bool mt7915_band_config(struct mt7915_dev *dev) * dbdc is disabled. */ if (sku == MT7975_ONE_ADIE || sku == MT7976_ONE_ADIE) { - dev->phy.band_idx = 1; + dev->phy.mt76->band_idx = 1; ret = false; } } else { diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mac.c b/drivers/net/wireless/mediatek/mt76/mt7915/mac.c index eac3008225b0..f0d5a3603902 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mac.c @@ -302,7 +302,7 @@ mt7915_mac_fill_rx(struct mt7915_dev *dev, struct sk_buff *skb, memset(status, 0, sizeof(*status)); - if ((rxd1 & MT_RXD1_NORMAL_BAND_IDX) && !phy->band_idx) { + if ((rxd1 & MT_RXD1_NORMAL_BAND_IDX) && !phy->mt76->band_idx) { mphy = dev->mt76.phys[MT_BAND1]; if (!mphy) return -EINVAL; @@ -575,7 +575,7 @@ mt7915_mac_fill_rx_vector(struct mt7915_dev *dev, struct sk_buff *skb) int i; band_idx = le32_get_bits(rxv_hdr[1], MT_RXV_HDR_BAND_IDX); - if (band_idx && !phy->band_idx) { + if (band_idx && !phy->mt76->band_idx) { phy = mt7915_ext_phy(dev); if (!phy) goto out; @@ -1175,7 +1175,7 @@ void mt7915_queue_rx_skb(struct mt76_dev *mdev, enum mt76_rxq_id q, void mt7915_mac_cca_stats_reset(struct mt7915_phy *phy) { struct mt7915_dev *dev = phy->dev; - u32 reg = MT_WF_PHY_RX_CTRL1(phy->band_idx); + u32 reg = MT_WF_PHY_RX_CTRL1(phy->mt76->band_idx); mt76_clear(dev, reg, MT_WF_PHY_RX_CTRL1_STSCNT_EN); mt76_set(dev, reg, BIT(11) | BIT(9)); @@ -1187,15 +1187,15 @@ void mt7915_mac_reset_counters(struct mt7915_phy *phy) int i; for (i = 0; i < 4; i++) { - mt76_rr(dev, MT_TX_AGG_CNT(phy->band_idx, i)); - mt76_rr(dev, MT_TX_AGG_CNT2(phy->band_idx, i)); + mt76_rr(dev, MT_TX_AGG_CNT(phy->mt76->band_idx, i)); + mt76_rr(dev, MT_TX_AGG_CNT2(phy->mt76->band_idx, i)); } phy->mt76->survey_time = ktime_get_boottime(); memset(phy->mt76->aggr_stats, 0, sizeof(phy->mt76->aggr_stats)); /* reset airtime counters */ - mt76_set(dev, MT_WF_RMAC_MIB_AIRTIME0(phy->band_idx), + mt76_set(dev, MT_WF_RMAC_MIB_AIRTIME0(phy->mt76->band_idx), MT_WF_RMAC_MIB_RXTIME_CLR); mt7915_mcu_get_chan_mib_info(phy, true); @@ -1211,6 +1211,7 @@ void mt7915_mac_set_timing(struct mt7915_phy *phy) FIELD_PREP(MT_TIMEOUT_VAL_CCA, 48); u32 ofdm = FIELD_PREP(MT_TIMEOUT_VAL_PLCP, 60) | FIELD_PREP(MT_TIMEOUT_VAL_CCA, 28); + u8 band = phy->mt76->band_idx; int eifs_ofdm = 360, sifs = 10, offset; bool a_band = !(phy->mt76->chandef.chan->band == NL80211_BAND_2GHZ); @@ -1221,7 +1222,7 @@ void mt7915_mac_set_timing(struct mt7915_phy *phy) coverage_class = max_t(s16, dev->phy.coverage_class, ext_phy->coverage_class); - mt76_set(dev, MT_ARB_SCR(phy->band_idx), + mt76_set(dev, MT_ARB_SCR(band), MT_ARB_SCR_TX_DISABLE | MT_ARB_SCR_RX_DISABLE); udelay(1); @@ -1231,7 +1232,7 @@ void mt7915_mac_set_timing(struct mt7915_phy *phy) if (!is_mt7915(&dev->mt76)) { if (!a_band) { - mt76_wr(dev, MT_TMAC_ICR1(phy->band_idx), + mt76_wr(dev, MT_TMAC_ICR1(band), FIELD_PREP(MT_IFS_EIFS_CCK, 314)); eifs_ofdm = 78; } else { @@ -1241,9 +1242,9 @@ void mt7915_mac_set_timing(struct mt7915_phy *phy) sifs = 16; } - mt76_wr(dev, MT_TMAC_CDTR(phy->band_idx), cck + reg_offset); - mt76_wr(dev, MT_TMAC_ODTR(phy->band_idx), ofdm + reg_offset); - mt76_wr(dev, MT_TMAC_ICR0(phy->band_idx), + mt76_wr(dev, MT_TMAC_CDTR(band), cck + reg_offset); + mt76_wr(dev, MT_TMAC_ODTR(band), ofdm + reg_offset); + mt76_wr(dev, MT_TMAC_ICR0(band), FIELD_PREP(MT_IFS_EIFS_OFDM, eifs_ofdm) | FIELD_PREP(MT_IFS_RIFS, 2) | FIELD_PREP(MT_IFS_SIFS, sifs) | @@ -1254,8 +1255,8 @@ void mt7915_mac_set_timing(struct mt7915_phy *phy) else val = MT7915_CFEND_RATE_11B; - mt76_rmw_field(dev, MT_AGG_ACR0(phy->band_idx), MT_AGG_ACR_CFEND_RATE, val); - mt76_clear(dev, MT_ARB_SCR(phy->band_idx), + mt76_rmw_field(dev, MT_AGG_ACR0(band), MT_AGG_ACR_CFEND_RATE, val); + mt76_clear(dev, MT_ARB_SCR(band), MT_ARB_SCR_TX_DISABLE | MT_ARB_SCR_RX_DISABLE); } @@ -1308,7 +1309,7 @@ void mt7915_update_channel(struct mt76_phy *mphy) mt7915_mcu_get_chan_mib_info(phy, false); - nf = mt7915_phy_get_nf(phy, phy->band_idx); + nf = mt7915_phy_get_nf(phy, phy->mt76->band_idx); if (!phy->noise) phy->noise = nf << 4; else if (nf) @@ -1751,127 +1752,128 @@ void mt7915_mac_update_stats(struct mt7915_phy *phy) struct mt7915_dev *dev = phy->dev; struct mib_stats *mib = &phy->mib; int i, aggr0 = 0, aggr1, cnt; + u8 band = phy->mt76->band_idx; u32 val; - cnt = mt76_rr(dev, MT_MIB_SDR3(phy->band_idx)); + cnt = mt76_rr(dev, MT_MIB_SDR3(band)); mib->fcs_err_cnt += is_mt7915(&dev->mt76) ? FIELD_GET(MT_MIB_SDR3_FCS_ERR_MASK, cnt) : FIELD_GET(MT_MIB_SDR3_FCS_ERR_MASK_MT7916, cnt); - cnt = mt76_rr(dev, MT_MIB_SDR4(phy->band_idx)); + cnt = mt76_rr(dev, MT_MIB_SDR4(band)); mib->rx_fifo_full_cnt += FIELD_GET(MT_MIB_SDR4_RX_FIFO_FULL_MASK, cnt); - cnt = mt76_rr(dev, MT_MIB_SDR5(phy->band_idx)); + cnt = mt76_rr(dev, MT_MIB_SDR5(band)); mib->rx_mpdu_cnt += cnt; - cnt = mt76_rr(dev, MT_MIB_SDR6(phy->band_idx)); + cnt = mt76_rr(dev, MT_MIB_SDR6(band)); mib->channel_idle_cnt += FIELD_GET(MT_MIB_SDR6_CHANNEL_IDL_CNT_MASK, cnt); - cnt = mt76_rr(dev, MT_MIB_SDR7(phy->band_idx)); + cnt = mt76_rr(dev, MT_MIB_SDR7(band)); mib->rx_vector_mismatch_cnt += FIELD_GET(MT_MIB_SDR7_RX_VECTOR_MISMATCH_CNT_MASK, cnt); - cnt = mt76_rr(dev, MT_MIB_SDR8(phy->band_idx)); + cnt = mt76_rr(dev, MT_MIB_SDR8(band)); mib->rx_delimiter_fail_cnt += FIELD_GET(MT_MIB_SDR8_RX_DELIMITER_FAIL_CNT_MASK, cnt); - cnt = mt76_rr(dev, MT_MIB_SDR10(phy->band_idx)); + cnt = mt76_rr(dev, MT_MIB_SDR10(band)); mib->rx_mrdy_cnt += is_mt7915(&dev->mt76) ? FIELD_GET(MT_MIB_SDR10_MRDY_COUNT_MASK, cnt) : FIELD_GET(MT_MIB_SDR10_MRDY_COUNT_MASK_MT7916, cnt); - cnt = mt76_rr(dev, MT_MIB_SDR11(phy->band_idx)); + cnt = mt76_rr(dev, MT_MIB_SDR11(band)); mib->rx_len_mismatch_cnt += FIELD_GET(MT_MIB_SDR11_RX_LEN_MISMATCH_CNT_MASK, cnt); - cnt = mt76_rr(dev, MT_MIB_SDR12(phy->band_idx)); + cnt = mt76_rr(dev, MT_MIB_SDR12(band)); mib->tx_ampdu_cnt += cnt; - cnt = mt76_rr(dev, MT_MIB_SDR13(phy->band_idx)); + cnt = mt76_rr(dev, MT_MIB_SDR13(band)); mib->tx_stop_q_empty_cnt += FIELD_GET(MT_MIB_SDR13_TX_STOP_Q_EMPTY_CNT_MASK, cnt); - cnt = mt76_rr(dev, MT_MIB_SDR14(phy->band_idx)); + cnt = mt76_rr(dev, MT_MIB_SDR14(band)); mib->tx_mpdu_attempts_cnt += is_mt7915(&dev->mt76) ? FIELD_GET(MT_MIB_SDR14_TX_MPDU_ATTEMPTS_CNT_MASK, cnt) : FIELD_GET(MT_MIB_SDR14_TX_MPDU_ATTEMPTS_CNT_MASK_MT7916, cnt); - cnt = mt76_rr(dev, MT_MIB_SDR15(phy->band_idx)); + cnt = mt76_rr(dev, MT_MIB_SDR15(band)); mib->tx_mpdu_success_cnt += is_mt7915(&dev->mt76) ? FIELD_GET(MT_MIB_SDR15_TX_MPDU_SUCCESS_CNT_MASK, cnt) : FIELD_GET(MT_MIB_SDR15_TX_MPDU_SUCCESS_CNT_MASK_MT7916, cnt); - cnt = mt76_rr(dev, MT_MIB_SDR16(phy->band_idx)); + cnt = mt76_rr(dev, MT_MIB_SDR16(band)); mib->primary_cca_busy_time += FIELD_GET(MT_MIB_SDR16_PRIMARY_CCA_BUSY_TIME_MASK, cnt); - cnt = mt76_rr(dev, MT_MIB_SDR17(phy->band_idx)); + cnt = mt76_rr(dev, MT_MIB_SDR17(band)); mib->secondary_cca_busy_time += FIELD_GET(MT_MIB_SDR17_SECONDARY_CCA_BUSY_TIME_MASK, cnt); - cnt = mt76_rr(dev, MT_MIB_SDR18(phy->band_idx)); + cnt = mt76_rr(dev, MT_MIB_SDR18(band)); mib->primary_energy_detect_time += FIELD_GET(MT_MIB_SDR18_PRIMARY_ENERGY_DETECT_TIME_MASK, cnt); - cnt = mt76_rr(dev, MT_MIB_SDR19(phy->band_idx)); + cnt = mt76_rr(dev, MT_MIB_SDR19(band)); mib->cck_mdrdy_time += FIELD_GET(MT_MIB_SDR19_CCK_MDRDY_TIME_MASK, cnt); - cnt = mt76_rr(dev, MT_MIB_SDR20(phy->band_idx)); + cnt = mt76_rr(dev, MT_MIB_SDR20(band)); mib->ofdm_mdrdy_time += FIELD_GET(MT_MIB_SDR20_OFDM_VHT_MDRDY_TIME_MASK, cnt); - cnt = mt76_rr(dev, MT_MIB_SDR21(phy->band_idx)); + cnt = mt76_rr(dev, MT_MIB_SDR21(band)); mib->green_mdrdy_time += FIELD_GET(MT_MIB_SDR21_GREEN_MDRDY_TIME_MASK, cnt); - cnt = mt76_rr(dev, MT_MIB_SDR22(phy->band_idx)); + cnt = mt76_rr(dev, MT_MIB_SDR22(band)); mib->rx_ampdu_cnt += cnt; - cnt = mt76_rr(dev, MT_MIB_SDR23(phy->band_idx)); + cnt = mt76_rr(dev, MT_MIB_SDR23(band)); mib->rx_ampdu_bytes_cnt += cnt; - cnt = mt76_rr(dev, MT_MIB_SDR24(phy->band_idx)); + cnt = mt76_rr(dev, MT_MIB_SDR24(band)); mib->rx_ampdu_valid_subframe_cnt += is_mt7915(&dev->mt76) ? FIELD_GET(MT_MIB_SDR24_RX_AMPDU_SF_CNT_MASK, cnt) : FIELD_GET(MT_MIB_SDR24_RX_AMPDU_SF_CNT_MASK_MT7916, cnt); - cnt = mt76_rr(dev, MT_MIB_SDR25(phy->band_idx)); + cnt = mt76_rr(dev, MT_MIB_SDR25(band)); mib->rx_ampdu_valid_subframe_bytes_cnt += cnt; - cnt = mt76_rr(dev, MT_MIB_SDR27(phy->band_idx)); + cnt = mt76_rr(dev, MT_MIB_SDR27(band)); mib->tx_rwp_fail_cnt += FIELD_GET(MT_MIB_SDR27_TX_RWP_FAIL_CNT_MASK, cnt); - cnt = mt76_rr(dev, MT_MIB_SDR28(phy->band_idx)); + cnt = mt76_rr(dev, MT_MIB_SDR28(band)); mib->tx_rwp_need_cnt += FIELD_GET(MT_MIB_SDR28_TX_RWP_NEED_CNT_MASK, cnt); - cnt = mt76_rr(dev, MT_MIB_SDR29(phy->band_idx)); + cnt = mt76_rr(dev, MT_MIB_SDR29(band)); mib->rx_pfdrop_cnt += is_mt7915(&dev->mt76) ? FIELD_GET(MT_MIB_SDR29_RX_PFDROP_CNT_MASK, cnt) : FIELD_GET(MT_MIB_SDR29_RX_PFDROP_CNT_MASK_MT7916, cnt); - cnt = mt76_rr(dev, MT_MIB_SDRVEC(phy->band_idx)); + cnt = mt76_rr(dev, MT_MIB_SDRVEC(band)); mib->rx_vec_queue_overflow_drop_cnt += is_mt7915(&dev->mt76) ? FIELD_GET(MT_MIB_SDR30_RX_VEC_QUEUE_OVERFLOW_DROP_CNT_MASK, cnt) : FIELD_GET(MT_MIB_SDR30_RX_VEC_QUEUE_OVERFLOW_DROP_CNT_MASK_MT7916, cnt); - cnt = mt76_rr(dev, MT_MIB_SDR31(phy->band_idx)); + cnt = mt76_rr(dev, MT_MIB_SDR31(band)); mib->rx_ba_cnt += cnt; - cnt = mt76_rr(dev, MT_MIB_SDRMUBF(phy->band_idx)); + cnt = mt76_rr(dev, MT_MIB_SDRMUBF(band)); mib->tx_bf_cnt += FIELD_GET(MT_MIB_MU_BF_TX_CNT, cnt); - cnt = mt76_rr(dev, MT_MIB_DR8(phy->band_idx)); + cnt = mt76_rr(dev, MT_MIB_DR8(band)); mib->tx_mu_mpdu_cnt += cnt; - cnt = mt76_rr(dev, MT_MIB_DR9(phy->band_idx)); + cnt = mt76_rr(dev, MT_MIB_DR9(band)); mib->tx_mu_acked_mpdu_cnt += cnt; - cnt = mt76_rr(dev, MT_MIB_DR11(phy->band_idx)); + cnt = mt76_rr(dev, MT_MIB_DR11(band)); mib->tx_su_acked_mpdu_cnt += cnt; - cnt = mt76_rr(dev, MT_ETBF_PAR_RPT0(phy->band_idx)); + cnt = mt76_rr(dev, MT_ETBF_PAR_RPT0(band)); mib->tx_bf_rx_fb_bw = FIELD_GET(MT_ETBF_PAR_RPT0_FB_BW, cnt); mib->tx_bf_rx_fb_nc_cnt += FIELD_GET(MT_ETBF_PAR_RPT0_FB_NC, cnt); mib->tx_bf_rx_fb_nr_cnt += FIELD_GET(MT_ETBF_PAR_RPT0_FB_NR, cnt); @@ -1884,41 +1886,41 @@ void mt7915_mac_update_stats(struct mt7915_phy *phy) if (is_mt7915(&dev->mt76)) { for (i = 0, aggr1 = aggr0 + 8; i < 4; i++) { - val = mt76_rr(dev, MT_MIB_MB_SDR1(phy->band_idx, (i << 4))); + val = mt76_rr(dev, MT_MIB_MB_SDR1(band, (i << 4))); mib->ba_miss_cnt += FIELD_GET(MT_MIB_BA_MISS_COUNT_MASK, val); mib->ack_fail_cnt += FIELD_GET(MT_MIB_ACK_FAIL_COUNT_MASK, val); - val = mt76_rr(dev, MT_MIB_MB_SDR0(phy->band_idx, (i << 4))); + val = mt76_rr(dev, MT_MIB_MB_SDR0(band, (i << 4))); mib->rts_cnt += FIELD_GET(MT_MIB_RTS_COUNT_MASK, val); mib->rts_retries_cnt += FIELD_GET(MT_MIB_RTS_RETRIES_COUNT_MASK, val); - val = mt76_rr(dev, MT_TX_AGG_CNT(phy->band_idx, i)); + val = mt76_rr(dev, MT_TX_AGG_CNT(band, i)); phy->mt76->aggr_stats[aggr0++] += val & 0xffff; phy->mt76->aggr_stats[aggr0++] += val >> 16; - val = mt76_rr(dev, MT_TX_AGG_CNT2(phy->band_idx, i)); + val = mt76_rr(dev, MT_TX_AGG_CNT2(band, i)); phy->mt76->aggr_stats[aggr1++] += val & 0xffff; phy->mt76->aggr_stats[aggr1++] += val >> 16; } - cnt = mt76_rr(dev, MT_MIB_SDR32(phy->band_idx)); + cnt = mt76_rr(dev, MT_MIB_SDR32(band)); mib->tx_pkt_ebf_cnt += FIELD_GET(MT_MIB_SDR32_TX_PKT_EBF_CNT, cnt); - cnt = mt76_rr(dev, MT_MIB_SDR33(phy->band_idx)); + cnt = mt76_rr(dev, MT_MIB_SDR33(band)); mib->tx_pkt_ibf_cnt += FIELD_GET(MT_MIB_SDR33_TX_PKT_IBF_CNT, cnt); - cnt = mt76_rr(dev, MT_ETBF_TX_APP_CNT(phy->band_idx)); + cnt = mt76_rr(dev, MT_ETBF_TX_APP_CNT(band)); mib->tx_bf_ibf_ppdu_cnt += FIELD_GET(MT_ETBF_TX_IBF_CNT, cnt); mib->tx_bf_ebf_ppdu_cnt += FIELD_GET(MT_ETBF_TX_EBF_CNT, cnt); - cnt = mt76_rr(dev, MT_ETBF_TX_NDP_BFRP(phy->band_idx)); + cnt = mt76_rr(dev, MT_ETBF_TX_NDP_BFRP(band)); mib->tx_bf_fb_cpl_cnt += FIELD_GET(MT_ETBF_TX_FB_CPL, cnt); mib->tx_bf_fb_trig_cnt += FIELD_GET(MT_ETBF_TX_FB_TRI, cnt); - cnt = mt76_rr(dev, MT_ETBF_RX_FB_CNT(phy->band_idx)); + cnt = mt76_rr(dev, MT_ETBF_RX_FB_CNT(band)); mib->tx_bf_rx_fb_all_cnt += FIELD_GET(MT_ETBF_RX_FB_ALL, cnt); mib->tx_bf_rx_fb_he_cnt += FIELD_GET(MT_ETBF_RX_FB_HE, cnt); mib->tx_bf_rx_fb_vht_cnt += FIELD_GET(MT_ETBF_RX_FB_VHT, cnt); @@ -1926,51 +1928,51 @@ void mt7915_mac_update_stats(struct mt7915_phy *phy) } else { for (i = 0; i < 2; i++) { /* rts count */ - val = mt76_rr(dev, MT_MIB_MB_SDR0(phy->band_idx, (i << 2))); + val = mt76_rr(dev, MT_MIB_MB_SDR0(band, (i << 2))); mib->rts_cnt += FIELD_GET(GENMASK(15, 0), val); mib->rts_cnt += FIELD_GET(GENMASK(31, 16), val); /* rts retry count */ - val = mt76_rr(dev, MT_MIB_MB_SDR1(phy->band_idx, (i << 2))); + val = mt76_rr(dev, MT_MIB_MB_SDR1(band, (i << 2))); mib->rts_retries_cnt += FIELD_GET(GENMASK(15, 0), val); mib->rts_retries_cnt += FIELD_GET(GENMASK(31, 16), val); /* ba miss count */ - val = mt76_rr(dev, MT_MIB_MB_SDR2(phy->band_idx, (i << 2))); + val = mt76_rr(dev, MT_MIB_MB_SDR2(band, (i << 2))); mib->ba_miss_cnt += FIELD_GET(GENMASK(15, 0), val); mib->ba_miss_cnt += FIELD_GET(GENMASK(31, 16), val); /* ack fail count */ - val = mt76_rr(dev, MT_MIB_MB_BFTF(phy->band_idx, (i << 2))); + val = mt76_rr(dev, MT_MIB_MB_BFTF(band, (i << 2))); mib->ack_fail_cnt += FIELD_GET(GENMASK(15, 0), val); mib->ack_fail_cnt += FIELD_GET(GENMASK(31, 16), val); } for (i = 0; i < 8; i++) { - val = mt76_rr(dev, MT_TX_AGG_CNT(phy->band_idx, i)); + val = mt76_rr(dev, MT_TX_AGG_CNT(band, i)); phy->mt76->aggr_stats[aggr0++] += FIELD_GET(GENMASK(15, 0), val); phy->mt76->aggr_stats[aggr0++] += FIELD_GET(GENMASK(31, 16), val); } - cnt = mt76_rr(dev, MT_MIB_SDR32(phy->band_idx)); + cnt = mt76_rr(dev, MT_MIB_SDR32(band)); mib->tx_pkt_ibf_cnt += FIELD_GET(MT_MIB_SDR32_TX_PKT_IBF_CNT, cnt); mib->tx_bf_ibf_ppdu_cnt += FIELD_GET(MT_MIB_SDR32_TX_PKT_IBF_CNT, cnt); mib->tx_pkt_ebf_cnt += FIELD_GET(MT_MIB_SDR32_TX_PKT_EBF_CNT, cnt); mib->tx_bf_ebf_ppdu_cnt += FIELD_GET(MT_MIB_SDR32_TX_PKT_EBF_CNT, cnt); - cnt = mt76_rr(dev, MT_MIB_BFCR7(phy->band_idx)); + cnt = mt76_rr(dev, MT_MIB_BFCR7(band)); mib->tx_bf_fb_cpl_cnt += FIELD_GET(MT_MIB_BFCR7_BFEE_TX_FB_CPL, cnt); - cnt = mt76_rr(dev, MT_MIB_BFCR2(phy->band_idx)); + cnt = mt76_rr(dev, MT_MIB_BFCR2(band)); mib->tx_bf_fb_trig_cnt += FIELD_GET(MT_MIB_BFCR2_BFEE_TX_FB_TRIG, cnt); - cnt = mt76_rr(dev, MT_MIB_BFCR0(phy->band_idx)); + cnt = mt76_rr(dev, MT_MIB_BFCR0(band)); mib->tx_bf_rx_fb_vht_cnt += FIELD_GET(MT_MIB_BFCR0_RX_FB_VHT, cnt); mib->tx_bf_rx_fb_all_cnt += FIELD_GET(MT_MIB_BFCR0_RX_FB_VHT, cnt); mib->tx_bf_rx_fb_ht_cnt += FIELD_GET(MT_MIB_BFCR0_RX_FB_HT, cnt); mib->tx_bf_rx_fb_all_cnt += FIELD_GET(MT_MIB_BFCR0_RX_FB_HT, cnt); - cnt = mt76_rr(dev, MT_MIB_BFCR1(phy->band_idx)); + cnt = mt76_rr(dev, MT_MIB_BFCR1(band)); mib->tx_bf_rx_fb_he_cnt += FIELD_GET(MT_MIB_BFCR1_RX_FB_HE, cnt); mib->tx_bf_rx_fb_all_cnt += FIELD_GET(MT_MIB_BFCR1_RX_FB_HE, cnt); } @@ -1988,7 +1990,7 @@ static void mt7915_mac_severe_check(struct mt7915_phy *phy) * stopping Rx, so check status periodically to see if TRB hardware * requires minimal recovery. */ - trb = mt76_rr(dev, MT_TRB_RXPSR0(phy->band_idx)); + trb = mt76_rr(dev, MT_TRB_RXPSR0(phy->mt76->band_idx)); if ((FIELD_GET(MT_TRB_RXPSR0_RX_RMAC_PTR, trb) != FIELD_GET(MT_TRB_RXPSR0_RX_WTBL_PTR, trb)) && @@ -1996,7 +1998,7 @@ static void mt7915_mac_severe_check(struct mt7915_phy *phy) FIELD_GET(MT_TRB_RXPSR0_RX_WTBL_PTR, phy->trb_ts)) && trb == phy->trb_ts) mt7915_mcu_set_ser(dev, SER_RECOVER, SER_SET_RECOVER_L3_RX_ABORT, - phy->band_idx); + phy->mt76->band_idx); phy->trb_ts = trb; } @@ -2116,16 +2118,16 @@ static int mt7915_dfs_start_radar_detector(struct mt7915_phy *phy) int err; /* start CAC */ - err = mt76_connac_mcu_rdd_cmd(&dev->mt76, RDD_CAC_START, phy->band_idx, - MT_RX_SEL0, 0); + err = mt76_connac_mcu_rdd_cmd(&dev->mt76, RDD_CAC_START, + phy->mt76->band_idx, MT_RX_SEL0, 0); if (err < 0) return err; - err = mt7915_dfs_start_rdd(dev, phy->band_idx); + err = mt7915_dfs_start_rdd(dev, phy->mt76->band_idx); if (err < 0) return err; - phy->rdd_state |= BIT(phy->band_idx); + phy->rdd_state |= BIT(phy->mt76->band_idx); if (!is_mt7915(&dev->mt76)) return 0; @@ -2210,7 +2212,7 @@ int mt7915_dfs_init_radar_detector(struct mt7915_phy *phy) return 0; err = mt76_connac_mcu_rdd_cmd(&dev->mt76, RDD_CAC_END, - phy->band_idx, MT_RX_SEL0, 0); + phy->mt76->band_idx, MT_RX_SEL0, 0); if (err < 0) { phy->mt76->dfs_state = MT_DFS_STATE_UNKNOWN; return err; @@ -2221,13 +2223,13 @@ int mt7915_dfs_init_radar_detector(struct mt7915_phy *phy) stop: err = mt76_connac_mcu_rdd_cmd(&dev->mt76, RDD_NORMAL_START, - phy->band_idx, MT_RX_SEL0, 0); + phy->mt76->band_idx, MT_RX_SEL0, 0); if (err < 0) return err; if (is_mt7915(&dev->mt76)) { err = mt76_connac_mcu_rdd_cmd(&dev->mt76, RDD_SET_WF_ANT, - phy->band_idx, 0, + phy->mt76->band_idx, 0, dev->dbdc_support ? 2 : 0); if (err < 0) return err; diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/main.c b/drivers/net/wireless/mediatek/mt76/mt7915/main.c index c40b6098f19a..0511d6a505b0 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/main.c @@ -30,31 +30,35 @@ int mt7915_run(struct ieee80211_hw *hw) running = mt7915_dev_running(dev); if (!running) { - ret = mt76_connac_mcu_set_pm(&dev->mt76, dev->phy.band_idx, 0); + ret = mt76_connac_mcu_set_pm(&dev->mt76, + dev->phy.mt76->band_idx, 0); if (ret) goto out; - ret = mt7915_mcu_set_mac(dev, dev->phy.band_idx, true, true); + ret = mt7915_mcu_set_mac(dev, dev->phy.mt76->band_idx, + true, true); if (ret) goto out; - mt7915_mac_enable_nf(dev, dev->phy.band_idx); + mt7915_mac_enable_nf(dev, dev->phy.mt76->band_idx); } if (phy != &dev->phy) { - ret = mt76_connac_mcu_set_pm(&dev->mt76, phy->band_idx, 0); + ret = mt76_connac_mcu_set_pm(&dev->mt76, + phy->mt76->band_idx, 0); if (ret) goto out; - ret = mt7915_mcu_set_mac(dev, phy->band_idx, true, true); + ret = mt7915_mcu_set_mac(dev, phy->mt76->band_idx, + true, true); if (ret) goto out; - mt7915_mac_enable_nf(dev, phy->band_idx); + mt7915_mac_enable_nf(dev, phy->mt76->band_idx); } ret = mt76_connac_mcu_set_rts_thresh(&dev->mt76, 0x92b, - phy->band_idx); + phy->mt76->band_idx); if (ret) goto out; @@ -107,13 +111,13 @@ static void mt7915_stop(struct ieee80211_hw *hw) clear_bit(MT76_STATE_RUNNING, &phy->mt76->state); if (phy != &dev->phy) { - mt76_connac_mcu_set_pm(&dev->mt76, phy->band_idx, 1); - mt7915_mcu_set_mac(dev, phy->band_idx, false, false); + mt76_connac_mcu_set_pm(&dev->mt76, phy->mt76->band_idx, 1); + mt7915_mcu_set_mac(dev, phy->mt76->band_idx, false, false); } if (!mt7915_dev_running(dev)) { - mt76_connac_mcu_set_pm(&dev->mt76, dev->phy.band_idx, 1); - mt7915_mcu_set_mac(dev, dev->phy.band_idx, false, false); + mt76_connac_mcu_set_pm(&dev->mt76, dev->phy.mt76->band_idx, 1); + mt7915_mcu_set_mac(dev, dev->phy.mt76->band_idx, false, false); } mutex_unlock(&dev->mt76.mutex); @@ -217,7 +221,7 @@ static int mt7915_add_interface(struct ieee80211_hw *hw, } mvif->mt76.omac_idx = idx; mvif->phy = phy; - mvif->mt76.band_idx = phy->band_idx; + mvif->mt76.band_idx = phy->mt76->band_idx; mvif->mt76.wmm_idx = vif->type != NL80211_IFTYPE_AP; if (ext_phy) @@ -467,7 +471,7 @@ static int mt7915_config(struct ieee80211_hw *hw, u32 changed) if (changed & IEEE80211_CONF_CHANGE_MONITOR) { bool enabled = !!(hw->conf.flags & IEEE80211_CONF_MONITOR); - bool band = phy->band_idx; + bool band = phy->mt76->band_idx; if (!enabled) phy->rxfilter |= MT_WF_RFCR_DROP_OTHER_UC; @@ -506,7 +510,7 @@ static void mt7915_configure_filter(struct ieee80211_hw *hw, { struct mt7915_dev *dev = mt7915_hw_dev(hw); struct mt7915_phy *phy = mt7915_hw_phy(hw); - bool band = phy->band_idx; + bool band = phy->mt76->band_idx; u32 ctl_flags = MT_WF_RFCR1_DROP_ACK | MT_WF_RFCR1_DROP_BF_POLL | MT_WF_RFCR1_DROP_BA | @@ -743,7 +747,8 @@ static int mt7915_set_rts_threshold(struct ieee80211_hw *hw, u32 val) int ret; mutex_lock(&dev->mt76.mutex); - ret = mt76_connac_mcu_set_rts_thresh(&dev->mt76, val, phy->band_idx); + ret = mt76_connac_mcu_set_rts_thresh(&dev->mt76, val, + phy->mt76->band_idx); mutex_unlock(&dev->mt76.mutex); return ret; @@ -846,7 +851,7 @@ u64 __mt7915_get_tsf(struct ieee80211_hw *hw, struct mt7915_vif *mvif) { struct mt7915_dev *dev = mt7915_hw_dev(hw); struct mt7915_phy *phy = mt7915_hw_phy(hw); - bool band = phy->band_idx; + bool band = phy->mt76->band_idx; union { u64 t64; u32 t32[2]; @@ -891,7 +896,7 @@ mt7915_set_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct mt7915_vif *mvif = (struct mt7915_vif *)vif->drv_priv; struct mt7915_dev *dev = mt7915_hw_dev(hw); struct mt7915_phy *phy = mt7915_hw_phy(hw); - bool band = phy->band_idx; + bool band = phy->mt76->band_idx; union { u64 t64; u32 t32[2]; @@ -922,7 +927,7 @@ mt7915_offset_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct mt7915_vif *mvif = (struct mt7915_vif *)vif->drv_priv; struct mt7915_dev *dev = mt7915_hw_dev(hw); struct mt7915_phy *phy = mt7915_hw_phy(hw); - bool band = phy->band_idx; + bool band = phy->mt76->band_idx; union { u64 t64; u32 t32[2]; @@ -965,6 +970,7 @@ mt7915_set_antenna(struct ieee80211_hw *hw, u32 tx_ant, u32 rx_ant) struct mt7915_phy *phy = mt7915_hw_phy(hw); int max_nss = hweight8(hw->wiphy->available_antennas_tx); u8 chainshift = dev->chainshift; + u8 band = phy->mt76->band_idx; if (!tx_ant || tx_ant != rx_ant || ffs(tx_ant) > max_nss) return -EINVAL; @@ -974,10 +980,10 @@ mt7915_set_antenna(struct ieee80211_hw *hw, u32 tx_ant, u32 rx_ant) phy->mt76->antenna_mask = tx_ant; /* handle a variant of mt7916 which has 3T3R but nss2 on 5 GHz band */ - if (is_mt7916(&dev->mt76) && phy->band_idx && hweight8(tx_ant) == max_nss) + if (is_mt7916(&dev->mt76) && band && hweight8(tx_ant) == max_nss) phy->mt76->chainmask = (dev->chainmask >> chainshift) << chainshift; else - phy->mt76->chainmask = tx_ant << (chainshift * phy->band_idx); + phy->mt76->chainmask = tx_ant << (chainshift * band); mt76_set_stream_caps(phy->mt76, true); mt7915_set_stream_vht_txbf_caps(phy); diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c index 765bf634db76..b2652de082ba 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c @@ -232,7 +232,8 @@ mt7915_mcu_rx_csa_notify(struct mt7915_dev *dev, struct sk_buff *skb) c = (struct mt7915_mcu_csa_notify *)skb->data; - if ((c->band_idx && !dev->phy.band_idx) && dev->mt76.phys[MT_BAND1]) + if ((c->band_idx && !dev->phy.mt76->band_idx) && + dev->mt76.phys[MT_BAND1]) mphy = dev->mt76.phys[MT_BAND1]; ieee80211_iterate_active_interfaces_atomic(mphy->hw, @@ -251,7 +252,8 @@ mt7915_mcu_rx_thermal_notify(struct mt7915_dev *dev, struct sk_buff *skb) if (t->ctrl.ctrl_id != THERMAL_PROTECT_ENABLE) return; - if ((t->ctrl.band_idx && !dev->phy.band_idx) && dev->mt76.phys[MT_BAND1]) + if ((t->ctrl.band_idx && !dev->phy.mt76->band_idx) && + dev->mt76.phys[MT_BAND1]) mphy = dev->mt76.phys[MT_BAND1]; phy = (struct mt7915_phy *)mphy->priv; @@ -266,7 +268,8 @@ mt7915_mcu_rx_radar_detected(struct mt7915_dev *dev, struct sk_buff *skb) r = (struct mt7915_mcu_rdd_report *)skb->data; - if ((r->band_idx && !dev->phy.band_idx) && dev->mt76.phys[MT_BAND1]) + if ((r->band_idx && !dev->phy.mt76->band_idx) && + dev->mt76.phys[MT_BAND1]) mphy = dev->mt76.phys[MT_BAND1]; if (r->band_idx == MT_RX_SEL2) @@ -323,7 +326,7 @@ mt7915_mcu_rx_bcc_notify(struct mt7915_dev *dev, struct sk_buff *skb) b = (struct mt7915_mcu_bcc_notify *)skb->data; - if ((b->band_idx && !dev->phy.band_idx) && dev->mt76.phys[MT_BAND1]) + if ((b->band_idx && !dev->phy.mt76->band_idx) && dev->mt76.phys[MT_BAND1]) mphy = dev->mt76.phys[MT_BAND1]; ieee80211_iterate_active_interfaces_atomic(mphy->hw, @@ -599,7 +602,7 @@ mt7915_mcu_muar_config(struct mt7915_phy *phy, struct ieee80211_vif *vif, .mode = !!mask || enable, .entry_count = 1, .write = 1, - .band = phy->band_idx, + .band = phy->mt76->band_idx, .index = idx * 2 + bssid, }; @@ -2192,7 +2195,7 @@ int mt7915_mcu_muru_debug_get(struct mt7915_phy *phy, void *ms) u8 band_idx; } req = { .cmd = cpu_to_le32(MURU_GET_TXC_TX_STATS), - .band_idx = phy->band_idx, + .band_idx = phy->mt76->band_idx, }; ret = mt76_mcu_send_and_get_msg(&dev->mt76, MCU_EXT_CMD(MURU_CTRL), @@ -2585,7 +2588,7 @@ mt7915_mcu_background_chain_ctrl(struct mt7915_phy *phy, req.monitor_central_chan = ieee80211_frequency_to_channel(chandef->center_freq1); req.monitor_bw = mt76_connac_chan_bw(chandef); - req.band_idx = phy->band_idx; + req.band_idx = phy->mt76->band_idx; req.scan_mode = 1; break; } @@ -2593,7 +2596,7 @@ mt7915_mcu_background_chain_ctrl(struct mt7915_phy *phy, req.monitor_chan = chandef->chan->hw_value; req.monitor_central_chan = ieee80211_frequency_to_channel(chandef->center_freq1); - req.band_idx = phy->band_idx; + req.band_idx = phy->mt76->band_idx; req.scan_mode = 2; break; case CH_SWITCH_BACKGROUND_SCAN_STOP: @@ -2660,6 +2663,7 @@ int mt7915_mcu_set_chan_info(struct mt7915_phy *phy, int cmd) struct mt7915_dev *dev = phy->dev; struct cfg80211_chan_def *chandef = &phy->mt76->chandef; int freq1 = chandef->center_freq1; + u8 band = phy->mt76->band_idx; struct { u8 control_ch; u8 center_ch; @@ -2682,8 +2686,8 @@ int mt7915_mcu_set_chan_info(struct mt7915_phy *phy, int cmd) .center_ch = ieee80211_frequency_to_channel(freq1), .bw = mt76_connac_chan_bw(chandef), .tx_path_num = hweight16(phy->mt76->chainmask), - .rx_path = phy->mt76->chainmask >> (dev->chainshift * phy->band_idx), - .band_idx = phy->band_idx, + .rx_path = phy->mt76->chainmask >> (dev->chainshift * band), + .band_idx = band, .channel_band = ch_band[chandef->chan->band], }; @@ -2997,7 +3001,7 @@ int mt7915_mcu_get_chan_mib_info(struct mt7915_phy *phy, bool chan_switch) } for (i = 0; i < 5; i++) { - req[i].band = cpu_to_le32(phy->band_idx); + req[i].band = cpu_to_le32(phy->mt76->band_idx); req[i].offs = cpu_to_le32(offs[i + start]); if (!is_mt7915(&dev->mt76) && i == 3) @@ -3046,7 +3050,7 @@ int mt7915_mcu_get_temperature(struct mt7915_phy *phy) u8 rsv[5]; } req = { .ctrl_id = THERMAL_SENSOR_TEMP_QUERY, - .band_idx = phy->band_idx, + .band_idx = phy->mt76->band_idx, }; return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(THERMAL_CTRL), &req, @@ -3065,7 +3069,7 @@ int mt7915_mcu_set_thermal_throttling(struct mt7915_phy *phy, u8 state) u8 rsv[2]; } __packed req = { .ctrl = { - .band_idx = phy->band_idx, + .band_idx = phy->mt76->band_idx, }, }; int level; @@ -3115,7 +3119,7 @@ int mt7915_mcu_set_txpower_frame_min(struct mt7915_phy *phy, s8 txpower) s8 txpower_min; } __packed req = { .format_id = TX_POWER_LIMIT_FRAME_MIN, - .band_idx = phy->band_idx, + .band_idx = phy->mt76->band_idx, .txpower_min = txpower * 2, /* 0.5db */ }; @@ -3140,7 +3144,7 @@ int mt7915_mcu_set_txpower_frame(struct mt7915_phy *phy, s8 txpower_offs[48]; } __packed req = { .format_id = TX_POWER_LIMIT_FRAME, - .band_idx = phy->band_idx, + .band_idx = phy->mt76->band_idx, .txpower_max = DIV_ROUND_UP(mphy->txpower_cur, 2), .wcid = cpu_to_le16(msta->wcid.idx), }; @@ -3194,7 +3198,7 @@ int mt7915_mcu_set_txpower_sku(struct mt7915_phy *phy) struct ieee80211_hw *hw = mphy->hw; struct mt7915_mcu_txpower_sku req = { .format_id = TX_POWER_LIMIT_TABLE, - .band_idx = phy->band_idx, + .band_idx = phy->mt76->band_idx, }; struct mt76_power_limits limits_array; s8 *la = (s8 *)&limits_array; @@ -3243,7 +3247,7 @@ int mt7915_mcu_get_txpower_sku(struct mt7915_phy *phy, s8 *txpower, int len) } __packed req = { .format_id = TX_POWER_LIMIT_INFO, .category = RATE_POWER_INFO, - .band_idx = phy->band_idx, + .band_idx = phy->mt76->band_idx, }; s8 txpower_sku[MT7915_SKU_RATE_NUM][2]; struct sk_buff *skb; @@ -3296,7 +3300,7 @@ int mt7915_mcu_set_sku_en(struct mt7915_phy *phy, bool enable) u8 rsv; } __packed req = { .format_id = TX_POWER_LIMIT_ENABLE, - .band_idx = phy->band_idx, + .band_idx = phy->mt76->band_idx, .sku_enable = enable, }; @@ -3378,7 +3382,7 @@ mt7915_mcu_enable_obss_spr(struct mt7915_phy *phy, u8 action, u8 val) struct mt7915_mcu_sr_ctrl req = { .action = action, .argnum = 1, - .band_idx = phy->band_idx, + .band_idx = phy->mt76->band_idx, .val = cpu_to_le32(val), }; @@ -3409,7 +3413,7 @@ mt7915_mcu_set_obss_spr_pd(struct mt7915_phy *phy, .ctrl = { .action = SPR_SET_PARAM, .argnum = 9, - .band_idx = phy->band_idx, + .band_idx = phy->mt76->band_idx, }, }; int ret; @@ -3458,7 +3462,7 @@ mt7915_mcu_set_obss_spr_siga(struct mt7915_phy *phy, struct ieee80211_vif *vif, .ctrl = { .action = SPR_SET_SIGA, .argnum = 1, - .band_idx = phy->band_idx, + .band_idx = phy->mt76->band_idx, }, .siga = { .omac = omac > HW_BSSID_MAX ? omac - 12 : omac, @@ -3497,7 +3501,7 @@ mt7915_mcu_set_obss_spr_bitmap(struct mt7915_phy *phy, .ctrl = { .action = SPR_SET_SRG_BITMAP, .argnum = 4, - .band_idx = phy->band_idx, + .band_idx = phy->mt76->band_idx, }, }; u32 bitmap; diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mmio.c b/drivers/net/wireless/mediatek/mt76/mt7915/mmio.c index 3b4ede3b85ea..5a4141682f04 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mmio.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mmio.c @@ -558,11 +558,11 @@ static int mt7915_mmio_wed_offload_enable(struct mtk_wed_device *wed) return -EAGAIN; phy = &dev->phy; - mt76_set(dev, MT_AGG_ACR4(phy->band_idx), MT_AGG_ACR_PPDU_TXS2H); + mt76_set(dev, MT_AGG_ACR4(phy->mt76->band_idx), MT_AGG_ACR_PPDU_TXS2H); phy = dev->mt76.phys[MT_BAND1] ? dev->mt76.phys[MT_BAND1]->priv : NULL; if (phy) - mt76_set(dev, MT_AGG_ACR4(phy->band_idx), + mt76_set(dev, MT_AGG_ACR4(phy->mt76->band_idx), MT_AGG_ACR_PPDU_TXS2H); return 0; @@ -583,11 +583,11 @@ static void mt7915_mmio_wed_offload_disable(struct mtk_wed_device *wed) * MT_AGG_ACR_PPDU_TXS2H (PPDU format) even though ACR bit is set. */ phy = &dev->phy; - mt76_clear(dev, MT_AGG_ACR4(phy->band_idx), MT_AGG_ACR_PPDU_TXS2H); + mt76_clear(dev, MT_AGG_ACR4(phy->mt76->band_idx), MT_AGG_ACR_PPDU_TXS2H); phy = dev->mt76.phys[MT_BAND1] ? dev->mt76.phys[MT_BAND1]->priv : NULL; if (phy) - mt76_clear(dev, MT_AGG_ACR4(phy->band_idx), + mt76_clear(dev, MT_AGG_ACR4(phy->mt76->band_idx), MT_AGG_ACR_PPDU_TXS2H); } diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h b/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h index 42f213430758..6351feba6bdf 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h @@ -258,7 +258,6 @@ struct mt7915_phy { u32 rxfilter; u64 omac_mask; - u8 band_idx; u16 noise; diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/testmode.c b/drivers/net/wireless/mediatek/mt76/mt7915/testmode.c index 7ace05e0b63b..0d76ae31b376 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/testmode.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/testmode.c @@ -51,7 +51,7 @@ mt7915_tm_set_tx_power(struct mt7915_phy *phy) u8 rsv[3]; } __packed req = { .format_id = 0xf, - .band_idx = phy->band_idx, + .band_idx = phy->mt76->band_idx, .center_chan = ieee80211_frequency_to_channel(freq), }; u8 *tx_power = NULL; @@ -77,7 +77,7 @@ mt7915_tm_set_freq_offset(struct mt7915_phy *phy, bool en, u32 val) struct mt7915_tm_cmd req = { .testmode_en = en, .param_idx = MCU_ATE_SET_FREQ_OFFSET, - .param.freq.band = phy->band_idx, + .param.freq.band = phy->mt76->band_idx, .param.freq.freq_offset = cpu_to_le32(val), }; @@ -111,7 +111,7 @@ mt7915_tm_set_trx(struct mt7915_phy *phy, int type, bool en) .param_idx = MCU_ATE_SET_TRX, .param.trx.type = type, .param.trx.enable = en, - .param.trx.band = phy->band_idx, + .param.trx.band = phy->mt76->band_idx, }; return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(ATE_CTRL), &req, @@ -126,7 +126,7 @@ mt7915_tm_clean_hwq(struct mt7915_phy *phy, u8 wcid) .testmode_en = 1, .param_idx = MCU_ATE_CLEAN_TXQUEUE, .param.clean.wcid = wcid, - .param.clean.band = phy->band_idx, + .param.clean.band = phy->mt76->band_idx, }; return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(ATE_CTRL), &req, @@ -144,7 +144,7 @@ mt7915_tm_set_slot_time(struct mt7915_phy *phy, u8 slot_time, u8 sifs) .param.slot.sifs = sifs, .param.slot.rifs = 2, .param.slot.eifs = cpu_to_le16(60), - .param.slot.band = phy->band_idx, + .param.slot.band = phy->mt76->band_idx, }; return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(ATE_CTRL), &req, @@ -198,6 +198,7 @@ mt7915_tm_set_ipg_params(struct mt7915_phy *phy, u32 ipg, u8 mode) u8 sig_ext = (mode == MT76_TM_TX_MODE_CCK) ? 0 : 6; u8 slot_time = 9, sifs = TM_DEFAULT_SIFS; u8 aifsn = TM_MIN_AIFSN; + u8 band = phy->mt76->band_idx; u32 i2t_time, tr2t_time, txv_time; u16 cw = 0; @@ -232,14 +233,14 @@ mt7915_tm_set_ipg_params(struct mt7915_phy *phy, u32 ipg, u8 mode) sifs = min_t(u32, ipg, TM_MAX_SIFS); } done: - txv_time = mt76_get_field(dev, MT_TMAC_ATCR(phy->band_idx), + txv_time = mt76_get_field(dev, MT_TMAC_ATCR(band), MT_TMAC_ATCR_TXV_TOUT); txv_time *= 50; /* normal clock time */ i2t_time = (slot_time * 1000 - txv_time - BBP_PROC_TIME) / 50; tr2t_time = (sifs * 1000 - txv_time - BBP_PROC_TIME) / 50; - mt76_set(dev, MT_TMAC_TRCR0(phy->band_idx), + mt76_set(dev, MT_TMAC_TRCR0(band), FIELD_PREP(MT_TMAC_TRCR0_TR2T_CHK, tr2t_time) | FIELD_PREP(MT_TMAC_TRCR0_I2T_CHK, i2t_time)); @@ -336,6 +337,7 @@ mt7915_tm_reg_backup_restore(struct mt7915_phy *phy) int n_regs = ARRAY_SIZE(reg_backup_list); struct mt7915_dev *dev = phy->dev; u32 *b = phy->test.reg_backup; + u8 band = phy->mt76->band_idx; int i; REG_BAND_IDX(reg_backup_list[0], AGG_PCR0, 0); @@ -358,7 +360,7 @@ mt7915_tm_reg_backup_restore(struct mt7915_phy *phy) if (phy->mt76->test.state == MT76_TM_STATE_OFF) { for (i = 0; i < n_regs; i++) - mt76_wr(dev, reg_backup_list[i].band[phy->band_idx], b[i]); + mt76_wr(dev, reg_backup_list[i].band[band], b[i]); return; } @@ -369,33 +371,33 @@ mt7915_tm_reg_backup_restore(struct mt7915_phy *phy) phy->test.reg_backup = b; for (i = 0; i < n_regs; i++) - b[i] = mt76_rr(dev, reg_backup_list[i].band[phy->band_idx]); + b[i] = mt76_rr(dev, reg_backup_list[i].band[band]); } - mt76_clear(dev, MT_AGG_PCR0(phy->band_idx, 0), MT_AGG_PCR0_MM_PROT | + mt76_clear(dev, MT_AGG_PCR0(band, 0), MT_AGG_PCR0_MM_PROT | MT_AGG_PCR0_GF_PROT | MT_AGG_PCR0_ERP_PROT | MT_AGG_PCR0_VHT_PROT | MT_AGG_PCR0_BW20_PROT | MT_AGG_PCR0_BW40_PROT | MT_AGG_PCR0_BW80_PROT); - mt76_set(dev, MT_AGG_PCR0(phy->band_idx, 0), MT_AGG_PCR0_PTA_WIN_DIS); + mt76_set(dev, MT_AGG_PCR0(band, 0), MT_AGG_PCR0_PTA_WIN_DIS); - mt76_wr(dev, MT_AGG_PCR0(phy->band_idx, 1), MT_AGG_PCR1_RTS0_NUM_THRES | + mt76_wr(dev, MT_AGG_PCR0(band, 1), MT_AGG_PCR1_RTS0_NUM_THRES | MT_AGG_PCR1_RTS0_LEN_THRES); - mt76_clear(dev, MT_AGG_MRCR(phy->band_idx), MT_AGG_MRCR_BAR_CNT_LIMIT | + mt76_clear(dev, MT_AGG_MRCR(band), MT_AGG_MRCR_BAR_CNT_LIMIT | MT_AGG_MRCR_LAST_RTS_CTS_RN | MT_AGG_MRCR_RTS_FAIL_LIMIT | MT_AGG_MRCR_TXCMD_RTS_FAIL_LIMIT); - mt76_rmw(dev, MT_AGG_MRCR(phy->band_idx), MT_AGG_MRCR_RTS_FAIL_LIMIT | + mt76_rmw(dev, MT_AGG_MRCR(band), MT_AGG_MRCR_RTS_FAIL_LIMIT | MT_AGG_MRCR_TXCMD_RTS_FAIL_LIMIT, FIELD_PREP(MT_AGG_MRCR_RTS_FAIL_LIMIT, 1) | FIELD_PREP(MT_AGG_MRCR_TXCMD_RTS_FAIL_LIMIT, 1)); - mt76_wr(dev, MT_TMAC_TFCR0(phy->band_idx), 0); - mt76_clear(dev, MT_TMAC_TCR0(phy->band_idx), MT_TMAC_TCR0_TBTT_STOP_CTRL); + mt76_wr(dev, MT_TMAC_TFCR0(band), 0); + mt76_clear(dev, MT_TMAC_TCR0(band), MT_TMAC_TCR0_TBTT_STOP_CTRL); /* config rx filter for testmode rx */ - mt76_wr(dev, MT_WF_RFCR(phy->band_idx), 0xcf70a); - mt76_wr(dev, MT_WF_RFCR1(phy->band_idx), 0); + mt76_wr(dev, MT_WF_RFCR(band), 0xcf70a); + mt76_wr(dev, MT_WF_RFCR1(band), 0); } static void @@ -488,7 +490,7 @@ mt7915_tm_set_rx_frames(struct mt7915_phy *phy, bool en) mt7915_tm_update_channel(phy); /* read-clear */ - mt76_rr(dev, MT_MIB_SDR3(phy->band_idx)); + mt76_rr(dev, MT_MIB_SDR3(phy->mt76->band_idx)); mt7915_tm_set_trx(phy, TM_MAC_RX_RXV, en); } } @@ -515,6 +517,7 @@ mt7915_tm_set_tx_cont(struct mt7915_phy *phy, bool en) struct mt76_testmode_data *td = &phy->mt76->test; u32 func_idx = en ? TX_CONT_START : TX_CONT_STOP; u8 rate_idx = td->tx_rate_idx, mode; + u8 band = phy->mt76->band_idx; u16 rateval; struct mt7915_tm_rf_test req = { .action = 1, @@ -526,7 +529,7 @@ mt7915_tm_set_tx_cont(struct mt7915_phy *phy, bool en) tx_cont->control_ch = chandef->chan->hw_value; tx_cont->center_ch = freq1; tx_cont->tx_ant = td->tx_antenna_mask; - tx_cont->band = phy->band_idx; + tx_cont->band = band; switch (chandef->width) { case NL80211_CHAN_WIDTH_40: @@ -558,7 +561,7 @@ mt7915_tm_set_tx_cont(struct mt7915_phy *phy, bool en) } if (!en) { - req.op.rf.param.func_data = cpu_to_le32(phy->band_idx); + req.op.rf.param.func_data = cpu_to_le32(band); goto out; } @@ -767,11 +770,11 @@ mt7915_tm_dump_stats(struct mt76_phy *mphy, struct sk_buff *msg) nla_nest_end(msg, rx); - cnt = mt76_rr(dev, MT_MIB_SDR3(phy->band_idx)); + cnt = mt76_rr(dev, MT_MIB_SDR3(phy->mt76->band_idx)); fcs_err = is_mt7915(&dev->mt76) ? FIELD_GET(MT_MIB_SDR3_FCS_ERR_MASK, cnt) : FIELD_GET(MT_MIB_SDR3_FCS_ERR_MASK_MT7916, cnt); - q = phy->band_idx ? MT_RXQ_BAND1 : MT_RXQ_MAIN; + q = phy->mt76->band_idx ? MT_RXQ_BAND1 : MT_RXQ_MAIN; mphy->test.rx_stats.packets[q] += fcs_err; mphy->test.rx_stats.fcs_error[q] += fcs_err; From d75e739bba5957464e0065cae52b3feb78ed9ff9 Mon Sep 17 00:00:00 2001 From: Ryder Lee Date: Thu, 1 Dec 2022 11:44:42 +0800 Subject: [PATCH 1519/2002] wifi: mt76: mt7996: enable use_cts_prot support This adds selectable RTC/CTS enablement for each interface. Signed-off-by: Ryder Lee Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt7996/mac.c | 13 +++++++++++++ drivers/net/wireless/mediatek/mt76/mt7996/main.c | 3 +++ drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h | 2 ++ 3 files changed, 18 insertions(+) diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mac.c b/drivers/net/wireless/mediatek/mt76/mt7996/mac.c index 1c35f0f15452..9e6aa9c3e566 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mac.c @@ -218,6 +218,19 @@ static void mt7996_mac_sta_poll(struct mt7996_dev *dev) rcu_read_unlock(); } +void mt7996_mac_enable_rtscts(struct mt7996_dev *dev, + struct ieee80211_vif *vif, bool enable) +{ + struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv; + u32 addr; + + addr = mt7996_mac_wtbl_lmac_addr(dev, mvif->sta.wcid.idx, 5); + if (enable) + mt76_set(dev, addr, BIT(5)); + else + mt76_clear(dev, addr, BIT(5)); +} + static void mt7996_mac_decode_he_radiotap_ru(struct mt76_rx_status *status, struct ieee80211_radiotap_he *he, diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/main.c b/drivers/net/wireless/mediatek/mt76/mt7996/main.c index 21dea3fa7dc1..d43530a9786b 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/main.c @@ -523,6 +523,9 @@ static void mt7996_bss_info_changed(struct ieee80211_hw *hw, mt7996_mcu_add_obss_spr(dev, vif, info->he_obss_pd.enable); } + if (changed & BSS_CHANGED_ERP_CTS_PROT) + mt7996_mac_enable_rtscts(dev, vif, info->use_cts_prot); + if (changed & BSS_CHANGED_ERP_SLOT) { int slottime = info->use_short_slot ? 9 : 20; diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h b/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h index 167f615f099e..00c58878524e 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h @@ -464,6 +464,8 @@ bool mt7996_mac_wtbl_update(struct mt7996_dev *dev, int idx, u32 mask); void mt7996_mac_reset_counters(struct mt7996_phy *phy); void mt7996_mac_cca_stats_reset(struct mt7996_phy *phy); void mt7996_mac_enable_nf(struct mt7996_dev *dev, u8 band); +void mt7996_mac_enable_rtscts(struct mt7996_dev *dev, + struct ieee80211_vif *vif, bool enable); void mt7996_mac_write_txwi(struct mt7996_dev *dev, __le32 *txwi, struct sk_buff *skb, struct mt76_wcid *wcid, int pid, struct ieee80211_key_conf *key, u32 changed); From ea5d99d07fbff328f2ed56733178608fa1add793 Mon Sep 17 00:00:00 2001 From: Ryder Lee Date: Thu, 1 Dec 2022 11:44:43 +0800 Subject: [PATCH 1520/2002] wifi: mt76: mt7996: enable ack signal support This reports signal strength of ACK packets from the peer as measured at each interface. Signed-off-by: Ryder Lee Signed-off-by: Felix Fietkau --- .../net/wireless/mediatek/mt76/mt7996/init.c | 7 +++++++ .../net/wireless/mediatek/mt76/mt7996/mac.c | 19 ++++++++++++++++++- .../net/wireless/mediatek/mt76/mt7996/main.c | 8 ++++++++ .../net/wireless/mediatek/mt76/mt7996/mmio.c | 17 +++++++++-------- .../wireless/mediatek/mt76/mt7996/mt7996.h | 5 +++++ .../net/wireless/mediatek/mt76/mt7996/regs.h | 17 +++++++++++++---- 6 files changed, 60 insertions(+), 13 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/init.c b/drivers/net/wireless/mediatek/mt76/mt7996/init.c index cd1657e3585d..46b290526092 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/init.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/init.c @@ -180,6 +180,7 @@ mt7996_init_wiphy(struct ieee80211_hw *hw) wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_BEACON_RATE_HE); wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_UNSOL_BCAST_PROBE_RESP); wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_FILS_DISCOVERY); + wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_ACK_SIGNAL_SUPPORT); if (!mdev->dev->of_node || !of_property_read_bool(mdev->dev->of_node, @@ -240,6 +241,12 @@ mt7996_mac_init_band(struct mt7996_dev *dev, u8 band) set = FIELD_PREP(MT_WF_RMAC_MIB_OBSS_BACKOFF, 0) | FIELD_PREP(MT_WF_RMAC_MIB_ED_OFFSET, 4); mt76_rmw(dev, MT_WF_RMAC_MIB_AIRTIME0(band), mask, set); + + /* filter out non-resp frames and get instanstaeous signal reporting */ + mask = MT_WTBLOFF_RSCR_RCPI_MODE | MT_WTBLOFF_RSCR_RCPI_PARAM; + set = FIELD_PREP(MT_WTBLOFF_RSCR_RCPI_MODE, 0) | + FIELD_PREP(MT_WTBLOFF_RSCR_RCPI_PARAM, 0x3); + mt76_rmw(dev, MT_WTBLOFF_RSCR(band), mask, set); } static void mt7996_mac_init(struct mt7996_dev *dev) diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mac.c b/drivers/net/wireless/mediatek/mt76/mt7996/mac.c index 9e6aa9c3e566..0b3e28748e76 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mac.c @@ -10,7 +10,7 @@ #include "mac.h" #include "mcu.h" -#define to_rssi(field, rxv) ((FIELD_GET(field, rxv) - 220) / 2) +#define to_rssi(field, rcpi) ((FIELD_GET(field, rcpi) - 220) / 2) #define HE_BITS(f) cpu_to_le16(IEEE80211_RADIOTAP_HE_##f) #define HE_PREP(f, m, v) le16_encode_bits(le32_get_bits(v, MT_CRXV_HE_##m),\ @@ -124,6 +124,7 @@ static void mt7996_mac_sta_poll(struct mt7996_dev *dev) bool clear = false; u32 addr, val; u16 idx; + s8 rssi[4]; u8 bw; spin_lock_bh(&dev->sta_poll_lock); @@ -137,6 +138,8 @@ static void mt7996_mac_sta_poll(struct mt7996_dev *dev) spin_unlock_bh(&dev->sta_poll_lock); idx = msta->wcid.idx; + + /* refresh peer's airtime reporting */ addr = mt7996_mac_wtbl_lmac_addr(dev, idx, 20); for (i = 0; i < IEEE80211_NUM_ACS; i++) { @@ -213,6 +216,20 @@ static void mt7996_mac_sta_poll(struct mt7996_dev *dev) else rate->flags &= ~RATE_INFO_FLAGS_SHORT_GI; } + + /* get signal strength of resp frames (CTS/BA/ACK) */ + addr = mt7996_mac_wtbl_lmac_addr(dev, idx, 34); + val = mt76_rr(dev, addr); + + rssi[0] = to_rssi(GENMASK(7, 0), val); + rssi[1] = to_rssi(GENMASK(15, 8), val); + rssi[2] = to_rssi(GENMASK(23, 16), val); + rssi[3] = to_rssi(GENMASK(31, 14), val); + + msta->ack_signal = + mt76_rx_signal(msta->vif->phy->mt76->antenna_mask, rssi); + + ewma_avg_signal_add(&msta->avg_ack_signal, -msta->ack_signal); } rcu_read_unlock(); diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/main.c b/drivers/net/wireless/mediatek/mt76/mt7996/main.c index d43530a9786b..f2129be25d99 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/main.c @@ -595,6 +595,8 @@ int mt7996_mac_sta_add(struct mt76_dev *mdev, struct ieee80211_vif *vif, msta->wcid.tx_info |= MT_WCID_TX_INFO_SET; msta->jiffies = jiffies; + ewma_avg_signal_init(&msta->avg_ack_signal); + mt7996_mac_wtbl_update(dev, idx, MT_WTBL_UPDATE_ADM_COUNT_CLEAR); @@ -917,6 +919,12 @@ static void mt7996_sta_statistics(struct ieee80211_hw *hw, } sinfo->txrate.flags = txrate->flags; sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_BITRATE); + + sinfo->ack_signal = (s8)msta->ack_signal; + sinfo->filled |= BIT_ULL(NL80211_STA_INFO_ACK_SIGNAL); + + sinfo->avg_ack_signal = -(s8)ewma_avg_signal_read(&msta->avg_ack_signal); + sinfo->filled |= BIT_ULL(NL80211_STA_INFO_ACK_SIGNAL_AVG); } static void mt7996_sta_rc_work(void *data, struct ieee80211_sta *sta) diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mmio.c b/drivers/net/wireless/mediatek/mt76/mt7996/mmio.c index 0d097cda4da7..521769eb6b0e 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/mmio.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mmio.c @@ -12,14 +12,15 @@ #include "../trace.h" static const struct __base mt7996_reg_base[] = { - [WF_AGG_BASE] = { { 0x820e2000, 0x820f2000, 0x830e2000 } }, - [WF_MIB_BASE] = { { 0x820ed000, 0x820fd000, 0x830ed000 } }, - [WF_TMAC_BASE] = { { 0x820e4000, 0x820f4000, 0x830e4000 } }, - [WF_RMAC_BASE] = { { 0x820e5000, 0x820f5000, 0x830e5000 } }, - [WF_ARB_BASE] = { { 0x820e3000, 0x820f3000, 0x830e3000 } }, - [WF_LPON_BASE] = { { 0x820eb000, 0x820fb000, 0x830eb000 } }, - [WF_ETBF_BASE] = { { 0x820ea000, 0x820fa000, 0x830ea000 } }, - [WF_DMA_BASE] = { { 0x820e7000, 0x820f7000, 0x830e7000 } }, + [WF_AGG_BASE] = { { 0x820e2000, 0x820f2000, 0x830e2000 } }, + [WF_ARB_BASE] = { { 0x820e3000, 0x820f3000, 0x830e3000 } }, + [WF_TMAC_BASE] = { { 0x820e4000, 0x820f4000, 0x830e4000 } }, + [WF_RMAC_BASE] = { { 0x820e5000, 0x820f5000, 0x830e5000 } }, + [WF_DMA_BASE] = { { 0x820e7000, 0x820f7000, 0x830e7000 } }, + [WF_WTBLOFF_BASE] = { { 0x820e9000, 0x820f9000, 0x830e9000 } }, + [WF_ETBF_BASE] = { { 0x820ea000, 0x820fa000, 0x830ea000 } }, + [WF_LPON_BASE] = { { 0x820eb000, 0x820fb000, 0x830eb000 } }, + [WF_MIB_BASE] = { { 0x820ed000, 0x820fd000, 0x830ed000 } }, }; static const struct __map mt7996_reg_map[] = { diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h b/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h index 00c58878524e..17dcd05d3459 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h @@ -85,6 +85,8 @@ struct mt7996_twt_flow { u8 sched:1; }; +DECLARE_EWMA(avg_signal, 10, 8) + struct mt7996_sta { struct mt76_wcid wcid; /* must be first */ @@ -94,6 +96,9 @@ struct mt7996_sta { struct list_head rc_list; u32 airtime_ac[8]; + int ack_signal; + struct ewma_avg_signal avg_ack_signal; + unsigned long changed; unsigned long jiffies; unsigned long ampdu_state; diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/regs.h b/drivers/net/wireless/mediatek/mt76/mt7996/regs.h index 1d53611da4d8..794f61b93a46 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/regs.h +++ b/drivers/net/wireless/mediatek/mt76/mt7996/regs.h @@ -25,13 +25,14 @@ struct mt7996_reg_desc { enum base_rev { WF_AGG_BASE, - WF_MIB_BASE, + WF_ARB_BASE, WF_TMAC_BASE, WF_RMAC_BASE, - WF_ARB_BASE, - WF_LPON_BASE, - WF_ETBF_BASE, WF_DMA_BASE, + WF_WTBLOFF_BASE, + WF_ETBF_BASE, + WF_LPON_BASE, + WF_MIB_BASE, __MT_REG_BASE_MAX, }; @@ -97,6 +98,14 @@ enum base_rev { #define MT_DMA_TCRF1(_band) MT_WF_DMA(_band, 0x054) #define MT_DMA_TCRF1_QIDX GENMASK(15, 13) +/* WTBLOFF TOP: band 0(0x820e9000), band 1(0x820f9000), band 2(0x830e9000) */ +#define MT_WTBLOFF_BASE(_band) __BASE(WF_WTBLOFF_BASE, (_band)) +#define MT_WTBLOFF(_band, ofs) (MT_WTBLOFF_BASE(_band) + (ofs)) + +#define MT_WTBLOFF_RSCR(_band) MT_WTBLOFF(_band, 0x008) +#define MT_WTBLOFF_RSCR_RCPI_MODE GENMASK(31, 30) +#define MT_WTBLOFF_RSCR_RCPI_PARAM GENMASK(25, 24) + /* ETBF: band 0(0x820ea000), band 1(0x820fa000), band 2(0x830ea000) */ #define MT_WF_ETBF_BASE(_band) __BASE(WF_ETBF_BASE, (_band)) #define MT_WF_ETBF(_band, ofs) (MT_WF_ETBF_BASE(_band) + (ofs)) From cf6dc2db17fecd0bbae498f12c1b515f0964763b Mon Sep 17 00:00:00 2001 From: Ryder Lee Date: Thu, 1 Dec 2022 16:03:32 +0800 Subject: [PATCH 1521/2002] wifi: mt76: mt7996: add support to configure spatial reuse parameter set The SPR parameter set comprises OBSS PD threshold for SRG and non SRG and Bitmap of BSS color and partial BSSID. This adds support to configure fields of SPR element to firmware. User can disable firmware SR algorithms by turning sr_scene_detect off. Signed-off-by: Ryder Lee Signed-off-by: Felix Fietkau --- .../net/wireless/mediatek/mt76/mt7996/main.c | 6 +- .../net/wireless/mediatek/mt76/mt7996/mcu.c | 192 +++++++++++++++++- .../net/wireless/mediatek/mt76/mt7996/mcu.h | 7 + .../wireless/mediatek/mt76/mt7996/mt7996.h | 4 +- 4 files changed, 196 insertions(+), 13 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/main.c b/drivers/net/wireless/mediatek/mt76/mt7996/main.c index f2129be25d99..4421cd54311b 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/main.c @@ -518,10 +518,8 @@ static void mt7996_bss_info_changed(struct ieee80211_hw *hw, mt7996_mcu_add_sta(dev, vif, NULL, join); } - if (changed & BSS_CHANGED_ASSOC) { + if (changed & BSS_CHANGED_ASSOC) mt7996_mcu_add_bss_info(phy, vif, vif->cfg.assoc); - mt7996_mcu_add_obss_spr(dev, vif, info->he_obss_pd.enable); - } if (changed & BSS_CHANGED_ERP_CTS_PROT) mt7996_mac_enable_rtscts(dev, vif, info->use_cts_prot); @@ -545,7 +543,7 @@ static void mt7996_bss_info_changed(struct ieee80211_hw *hw, mt7996_mcu_set_tx(dev, vif); if (changed & BSS_CHANGED_HE_OBSS_PD) - mt7996_mcu_add_obss_spr(dev, vif, info->he_obss_pd.enable); + mt7996_mcu_add_obss_spr(phy, vif, &info->he_obss_pd); if (changed & BSS_CHANGED_HE_BSS_COLOR) mt7996_update_bss_color(hw, vif, &info->he_bss_color); diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c index 123076db8021..04e1d10bbd21 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c @@ -71,6 +71,10 @@ struct mt7996_fw_region { #define HE_PHY(p, c) u8_get_bits(c, IEEE80211_HE_PHY_##p) #define HE_MAC(m, c) u8_get_bits(c, IEEE80211_HE_MAC_##m) +static bool sr_scene_detect = true; +module_param(sr_scene_detect, bool, 0644); +MODULE_PARM_DESC(sr_scene_detect, "Enable firmware scene detection algorithm"); + static u8 mt7996_mcu_get_sta_nss(u16 mcs_map) { @@ -3123,29 +3127,203 @@ int mt7996_mcu_set_txbf(struct mt7996_dev *dev, u8 action) return mt76_mcu_skb_send_msg(&dev->mt76, skb, MCU_WM_UNI_CMD(BF), true); } -int mt7996_mcu_add_obss_spr(struct mt7996_dev *dev, struct ieee80211_vif *vif, - bool enable) +static int +mt7996_mcu_enable_obss_spr(struct mt7996_phy *phy, u16 action, u8 val) { -#define MT_SPR_ENABLE 1 - struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv; + struct mt7996_dev *dev = phy->dev; struct { u8 band_idx; u8 __rsv[3]; __le16 tag; __le16 len; + __le32 val; } __packed req = { - .band_idx = mvif->mt76.band_idx, - .tag = cpu_to_le16(UNI_CMD_SR_ENABLE), + .band_idx = phy->mt76->band_idx, + .tag = cpu_to_le16(action), .len = cpu_to_le16(sizeof(req) - 4), - .val = cpu_to_le32(enable), + .val = cpu_to_le32(val), }; return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(SR), &req, sizeof(req), true); } +static int +mt7996_mcu_set_obss_spr_pd(struct mt7996_phy *phy, + struct ieee80211_he_obss_pd *he_obss_pd) +{ + struct mt7996_dev *dev = phy->dev; + u8 max_th = 82, non_srg_max_th = 62; + struct { + u8 band_idx; + u8 __rsv[3]; + + __le16 tag; + __le16 len; + + u8 pd_th_non_srg; + u8 pd_th_srg; + u8 period_offs; + u8 rcpi_src; + __le16 obss_pd_min; + __le16 obss_pd_min_srg; + u8 resp_txpwr_mode; + u8 txpwr_restrict_mode; + u8 txpwr_ref; + u8 __rsv2[3]; + } __packed req = { + .band_idx = phy->mt76->band_idx, + .tag = cpu_to_le16(UNI_CMD_SR_SET_PARAM), + .len = cpu_to_le16(sizeof(req) - 4), + .obss_pd_min = cpu_to_le16(max_th), + .obss_pd_min_srg = cpu_to_le16(max_th), + .txpwr_restrict_mode = 2, + .txpwr_ref = 21 + }; + int ret; + + /* disable firmware dynamical PD asjustment */ + ret = mt7996_mcu_enable_obss_spr(phy, UNI_CMD_SR_ENABLE_DPD, false); + if (ret) + return ret; + + if (he_obss_pd->sr_ctrl & + IEEE80211_HE_SPR_NON_SRG_OBSS_PD_SR_DISALLOWED) + req.pd_th_non_srg = max_th; + else if (he_obss_pd->sr_ctrl & IEEE80211_HE_SPR_NON_SRG_OFFSET_PRESENT) + req.pd_th_non_srg = max_th - he_obss_pd->non_srg_max_offset; + else + req.pd_th_non_srg = non_srg_max_th; + + if (he_obss_pd->sr_ctrl & IEEE80211_HE_SPR_SRG_INFORMATION_PRESENT) + req.pd_th_srg = max_th - he_obss_pd->max_offset; + + return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(SR), + &req, sizeof(req), true); +} + +static int +mt7996_mcu_set_obss_spr_siga(struct mt7996_phy *phy, struct ieee80211_vif *vif, + struct ieee80211_he_obss_pd *he_obss_pd) +{ + struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv; + struct mt7996_dev *dev = phy->dev; + u8 omac = mvif->mt76.omac_idx; + struct { + u8 band_idx; + u8 __rsv[3]; + + __le16 tag; + __le16 len; + + u8 omac; + u8 __rsv2[3]; + u8 flag[20]; + } __packed req = { + .band_idx = phy->mt76->band_idx, + .tag = cpu_to_le16(UNI_CMD_SR_SET_SIGA), + .len = cpu_to_le16(sizeof(req) - 4), + .omac = omac > HW_BSSID_MAX ? omac - 12 : omac, + }; + int ret; + + if (he_obss_pd->sr_ctrl & IEEE80211_HE_SPR_HESIGA_SR_VAL15_ALLOWED) + req.flag[req.omac] = 0xf; + else + return 0; + + /* switch to normal AP mode */ + ret = mt7996_mcu_enable_obss_spr(phy, UNI_CMD_SR_ENABLE_MODE, 0); + if (ret) + return ret; + + return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(SR), + &req, sizeof(req), true); +} + +static int +mt7996_mcu_set_obss_spr_bitmap(struct mt7996_phy *phy, + struct ieee80211_he_obss_pd *he_obss_pd) +{ + struct mt7996_dev *dev = phy->dev; + struct { + u8 band_idx; + u8 __rsv[3]; + + __le16 tag; + __le16 len; + + __le32 color_l[2]; + __le32 color_h[2]; + __le32 bssid_l[2]; + __le32 bssid_h[2]; + } __packed req = { + .band_idx = phy->mt76->band_idx, + .tag = cpu_to_le16(UNI_CMD_SR_SET_SRG_BITMAP), + .len = cpu_to_le16(sizeof(req) - 4), + }; + u32 bitmap; + + memcpy(&bitmap, he_obss_pd->bss_color_bitmap, sizeof(bitmap)); + req.color_l[req.band_idx] = cpu_to_le32(bitmap); + + memcpy(&bitmap, he_obss_pd->bss_color_bitmap + 4, sizeof(bitmap)); + req.color_h[req.band_idx] = cpu_to_le32(bitmap); + + memcpy(&bitmap, he_obss_pd->partial_bssid_bitmap, sizeof(bitmap)); + req.bssid_l[req.band_idx] = cpu_to_le32(bitmap); + + memcpy(&bitmap, he_obss_pd->partial_bssid_bitmap + 4, sizeof(bitmap)); + req.bssid_h[req.band_idx] = cpu_to_le32(bitmap); + + return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(SR), &req, + sizeof(req), true); +} + +int mt7996_mcu_add_obss_spr(struct mt7996_phy *phy, struct ieee80211_vif *vif, + struct ieee80211_he_obss_pd *he_obss_pd) +{ + int ret; + + /* enable firmware scene detection algorithms */ + ret = mt7996_mcu_enable_obss_spr(phy, UNI_CMD_SR_ENABLE_SD, + sr_scene_detect); + if (ret) + return ret; + + /* firmware dynamically adjusts PD threshold so skip manual control */ + if (sr_scene_detect && !he_obss_pd->enable) + return 0; + + /* enable spatial reuse */ + ret = mt7996_mcu_enable_obss_spr(phy, UNI_CMD_SR_ENABLE, + he_obss_pd->enable); + if (ret) + return ret; + + if (sr_scene_detect || !he_obss_pd->enable) + return 0; + + ret = mt7996_mcu_enable_obss_spr(phy, UNI_CMD_SR_ENABLE_TX, true); + if (ret) + return ret; + + /* set SRG/non-SRG OBSS PD threshold */ + ret = mt7996_mcu_set_obss_spr_pd(phy, he_obss_pd); + if (ret) + return ret; + + /* Set SR prohibit */ + ret = mt7996_mcu_set_obss_spr_siga(phy, vif, he_obss_pd); + if (ret) + return ret; + + /* set SRG BSS color/BSSID bitmap */ + return mt7996_mcu_set_obss_spr_bitmap(phy, he_obss_pd); +} + int mt7996_mcu_update_bss_color(struct mt7996_dev *dev, struct ieee80211_vif *vif, struct cfg80211_he_bss_color *he_bss_color) { diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mcu.h b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.h index ff12a7168bd8..6084b2337598 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/mcu.h +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.h @@ -617,6 +617,13 @@ enum { enum{ UNI_CMD_SR_ENABLE = 0x1, + UNI_CMD_SR_ENABLE_SD, + UNI_CMD_SR_ENABLE_MODE, + UNI_CMD_SR_ENABLE_DPD = 0x12, + UNI_CMD_SR_ENABLE_TX, + UNI_CMD_SR_SET_SRG_BITMAP = 0x80, + UNI_CMD_SR_SET_PARAM = 0xc1, + UNI_CMD_SR_SET_SIGA = 0xd0, }; enum { diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h b/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h index 17dcd05d3459..725344791b4c 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h @@ -407,8 +407,8 @@ int mt7996_mcu_add_beacon(struct ieee80211_hw *hw, struct ieee80211_vif *vif, int enable); int mt7996_mcu_beacon_inband_discov(struct mt7996_dev *dev, struct ieee80211_vif *vif, u32 changed); -int mt7996_mcu_add_obss_spr(struct mt7996_dev *dev, struct ieee80211_vif *vif, - bool enable); +int mt7996_mcu_add_obss_spr(struct mt7996_phy *phy, struct ieee80211_vif *vif, + struct ieee80211_he_obss_pd *he_obss_pd); int mt7996_mcu_add_rate_ctrl(struct mt7996_dev *dev, struct ieee80211_vif *vif, struct ieee80211_sta *sta, bool changed); int mt7996_set_channel(struct mt7996_phy *phy); From 6a70a90dc1a74aa85598184f5a4c2f14316422b1 Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Thu, 1 Dec 2022 09:51:55 +0100 Subject: [PATCH 1522/2002] wifi: mt76: mt7915: mmio: fix naming convention Rename mt7915_wed_release_rx_buf in mt7915_mmio_wed_release_rx_buf, mt7915_wed_init_rx_buf in mt7915_mmio_wed_init_rx_buf and mt7915_wed_release_rx_buf in mt7915_mmio_wed_release_rx_buf Signed-off-by: Lorenzo Bianconi Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt7915/mmio.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mmio.c b/drivers/net/wireless/mediatek/mt76/mt7915/mmio.c index 5a4141682f04..0a95c3da241b 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mmio.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mmio.c @@ -591,7 +591,7 @@ static void mt7915_mmio_wed_offload_disable(struct mtk_wed_device *wed) MT_AGG_ACR_PPDU_TXS2H); } -static void mt7915_wed_release_rx_buf(struct mtk_wed_device *wed) +static void mt7915_mmio_wed_release_rx_buf(struct mtk_wed_device *wed) { struct mt7915_dev *dev; struct page *page; @@ -621,7 +621,7 @@ static void mt7915_wed_release_rx_buf(struct mtk_wed_device *wed) memset(&wed->rx_buf_ring.rx_page, 0, sizeof(wed->rx_buf_ring.rx_page)); } -static u32 mt7915_wed_init_rx_buf(struct mtk_wed_device *wed, int size) +static u32 mt7915_mmio_wed_init_rx_buf(struct mtk_wed_device *wed, int size) { struct mtk_rxbm_desc *desc = wed->rx_buf_ring.desc; struct mt7915_dev *dev; @@ -661,7 +661,7 @@ static u32 mt7915_wed_init_rx_buf(struct mtk_wed_device *wed, int size) return 0; unmap: - mt7915_wed_release_rx_buf(wed); + mt7915_mmio_wed_release_rx_buf(wed); return -ENOMEM; } @@ -768,8 +768,8 @@ int mt7915_mmio_wed_init(struct mt7915_dev *dev, void *pdev_ptr, wed->wlan.init_buf = mt7915_wed_init_buf; wed->wlan.offload_enable = mt7915_mmio_wed_offload_enable; wed->wlan.offload_disable = mt7915_mmio_wed_offload_disable; - wed->wlan.init_rx_buf = mt7915_wed_init_rx_buf; - wed->wlan.release_rx_buf = mt7915_wed_release_rx_buf; + wed->wlan.init_rx_buf = mt7915_mmio_wed_init_rx_buf; + wed->wlan.release_rx_buf = mt7915_mmio_wed_release_rx_buf; wed->wlan.update_wo_rx_stats = mt7915_mmio_wed_update_rx_stats; dev->mt76.rx_token_size = wed->wlan.rx_npkt; From f23a0cea8bd62f4a348974340bd9e753dc9b2941 Mon Sep 17 00:00:00 2001 From: Leon Yen Date: Thu, 1 Dec 2022 18:38:42 +0800 Subject: [PATCH 1523/2002] wifi: mt76: mt7921e: add pci .shutdown() support Some combinations of hosts cannnot detect mt7921e after reboot. The interoperability issue is caused by the status mismatch between host and chip fw. In such cases, the driver should stop chip activities and reset chip to default state before reboot. Suggested-by: angelogioacchino.delregno@collabora.com Co-developed-by: Deren Wu Signed-off-by: Deren Wu Signed-off-by: Leon Yen Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt7921/pci.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/pci.c b/drivers/net/wireless/mediatek/mt76/mt7921/pci.c index 28342ec940f0..cb72ded37256 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/pci.c +++ b/drivers/net/wireless/mediatek/mt76/mt7921/pci.c @@ -507,6 +507,21 @@ failed: return err; } +static void mt7921_pci_shutdown(struct pci_dev *pdev) +{ + struct mt76_dev *mdev = pci_get_drvdata(pdev); + struct mt7921_dev *dev = container_of(mdev, struct mt7921_dev, mt76); + struct mt76_connac_pm *pm = &dev->pm; + + cancel_delayed_work_sync(&pm->ps_work); + cancel_work_sync(&pm->wake_work); + + /* chip cleanup before reboot */ + mt7921_mcu_drv_pmctrl(dev); + mt7921_dma_cleanup(dev); + mt7921_wfsys_reset(dev); +} + static DEFINE_SIMPLE_DEV_PM_OPS(mt7921_pm_ops, mt7921_pci_suspend, mt7921_pci_resume); static struct pci_driver mt7921_pci_driver = { @@ -514,6 +529,7 @@ static struct pci_driver mt7921_pci_driver = { .id_table = mt7921_pci_device_table, .probe = mt7921_pci_probe, .remove = mt7921_pci_remove, + .shutdown = mt7921_pci_shutdown, .driver.pm = pm_sleep_ptr(&mt7921_pm_ops), }; From eb8c507296f6038d46010396d91b42a05c3b64d9 Mon Sep 17 00:00:00 2001 From: Dmitry Safonov Date: Wed, 23 Nov 2022 17:38:55 +0000 Subject: [PATCH 1524/2002] jump_label: Prevent key->enabled int overflow 1. With CONFIG_JUMP_LABEL=n static_key_slow_inc() doesn't have any protection against key->enabled refcounter overflow. 2. With CONFIG_JUMP_LABEL=y static_key_slow_inc_cpuslocked() still may turn the refcounter negative as (v + 1) may overflow. key->enabled is indeed a ref-counter as it's documented in multiple places: top comment in jump_label.h, Documentation/staging/static-keys.rst, etc. As -1 is reserved for static key that's in process of being enabled, functions would break with negative key->enabled refcount: - for CONFIG_JUMP_LABEL=n negative return of static_key_count() breaks static_key_false(), static_key_true() - the ref counter may become 0 from negative side by too many static_key_slow_inc() calls and lead to use-after-free issues. These flaws result in that some users have to introduce an additional mutex and prevent the reference counter from overflowing themselves, see bpf_enable_runtime_stats() checking the counter against INT_MAX / 2. Prevent the reference counter overflow by checking if (v + 1) > 0. Change functions API to return whether the increment was successful. Signed-off-by: Dmitry Safonov Acked-by: Jakub Kicinski Acked-by: Peter Zijlstra (Intel) Signed-off-by: Jakub Kicinski --- include/linux/jump_label.h | 21 +++++++++++--- kernel/jump_label.c | 56 ++++++++++++++++++++++++++++++-------- 2 files changed, 61 insertions(+), 16 deletions(-) diff --git a/include/linux/jump_label.h b/include/linux/jump_label.h index 570831ca9951..4e968ebadce6 100644 --- a/include/linux/jump_label.h +++ b/include/linux/jump_label.h @@ -224,9 +224,10 @@ extern bool arch_jump_label_transform_queue(struct jump_entry *entry, enum jump_label_type type); extern void arch_jump_label_transform_apply(void); extern int jump_label_text_reserved(void *start, void *end); -extern void static_key_slow_inc(struct static_key *key); +extern bool static_key_slow_inc(struct static_key *key); +extern bool static_key_fast_inc_not_disabled(struct static_key *key); extern void static_key_slow_dec(struct static_key *key); -extern void static_key_slow_inc_cpuslocked(struct static_key *key); +extern bool static_key_slow_inc_cpuslocked(struct static_key *key); extern void static_key_slow_dec_cpuslocked(struct static_key *key); extern int static_key_count(struct static_key *key); extern void static_key_enable(struct static_key *key); @@ -278,11 +279,23 @@ static __always_inline bool static_key_true(struct static_key *key) return false; } -static inline void static_key_slow_inc(struct static_key *key) +static inline bool static_key_fast_inc_not_disabled(struct static_key *key) { + int v; + STATIC_KEY_CHECK_USE(key); - atomic_inc(&key->enabled); + /* + * Prevent key->enabled getting negative to follow the same semantics + * as for CONFIG_JUMP_LABEL=y, see kernel/jump_label.c comment. + */ + v = atomic_read(&key->enabled); + do { + if (v < 0 || (v + 1) < 0) + return false; + } while (!likely(atomic_try_cmpxchg(&key->enabled, &v, v + 1))); + return true; } +#define static_key_slow_inc(key) static_key_fast_inc_not_disabled(key) static inline void static_key_slow_dec(struct static_key *key) { diff --git a/kernel/jump_label.c b/kernel/jump_label.c index 4d6c6f5f60db..d9c822bbffb8 100644 --- a/kernel/jump_label.c +++ b/kernel/jump_label.c @@ -113,9 +113,40 @@ int static_key_count(struct static_key *key) } EXPORT_SYMBOL_GPL(static_key_count); -void static_key_slow_inc_cpuslocked(struct static_key *key) +/* + * static_key_fast_inc_not_disabled - adds a user for a static key + * @key: static key that must be already enabled + * + * The caller must make sure that the static key can't get disabled while + * in this function. It doesn't patch jump labels, only adds a user to + * an already enabled static key. + * + * Returns true if the increment was done. Unlike refcount_t the ref counter + * is not saturated, but will fail to increment on overflow. + */ +bool static_key_fast_inc_not_disabled(struct static_key *key) { + int v; + STATIC_KEY_CHECK_USE(key); + /* + * Negative key->enabled has a special meaning: it sends + * static_key_slow_inc() down the slow path, and it is non-zero + * so it counts as "enabled" in jump_label_update(). Note that + * atomic_inc_unless_negative() checks >= 0, so roll our own. + */ + v = atomic_read(&key->enabled); + do { + if (v <= 0 || (v + 1) < 0) + return false; + } while (!likely(atomic_try_cmpxchg(&key->enabled, &v, v + 1))); + + return true; +} +EXPORT_SYMBOL_GPL(static_key_fast_inc_not_disabled); + +bool static_key_slow_inc_cpuslocked(struct static_key *key) +{ lockdep_assert_cpus_held(); /* @@ -124,15 +155,9 @@ void static_key_slow_inc_cpuslocked(struct static_key *key) * jump_label_update() process. At the same time, however, * the jump_label_update() call below wants to see * static_key_enabled(&key) for jumps to be updated properly. - * - * So give a special meaning to negative key->enabled: it sends - * static_key_slow_inc() down the slow path, and it is non-zero - * so it counts as "enabled" in jump_label_update(). Note that - * atomic_inc_unless_negative() checks >= 0, so roll our own. */ - for (int v = atomic_read(&key->enabled); v > 0; ) - if (likely(atomic_try_cmpxchg(&key->enabled, &v, v + 1))) - return; + if (static_key_fast_inc_not_disabled(key)) + return true; jump_label_lock(); if (atomic_read(&key->enabled) == 0) { @@ -144,16 +169,23 @@ void static_key_slow_inc_cpuslocked(struct static_key *key) */ atomic_set_release(&key->enabled, 1); } else { - atomic_inc(&key->enabled); + if (WARN_ON_ONCE(!static_key_fast_inc_not_disabled(key))) { + jump_label_unlock(); + return false; + } } jump_label_unlock(); + return true; } -void static_key_slow_inc(struct static_key *key) +bool static_key_slow_inc(struct static_key *key) { + bool ret; + cpus_read_lock(); - static_key_slow_inc_cpuslocked(key); + ret = static_key_slow_inc_cpuslocked(key); cpus_read_unlock(); + return ret; } EXPORT_SYMBOL_GPL(static_key_slow_inc); From f62c7517ffa1378cc60cb5646567fa98e4b388cd Mon Sep 17 00:00:00 2001 From: Dmitry Safonov Date: Wed, 23 Nov 2022 17:38:56 +0000 Subject: [PATCH 1525/2002] net/tcp: Separate tcp_md5sig_info allocation into tcp_md5sig_info_add() Add a helper to allocate tcp_md5sig_info, that will help later to do/allocate things when info allocated, once per socket. Signed-off-by: Dmitry Safonov Reviewed-by: Eric Dumazet Acked-by: Jakub Kicinski Signed-off-by: Jakub Kicinski --- net/ipv4/tcp_ipv4.c | 30 +++++++++++++++++++++--------- 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 1215fa4c1b9f..c72e53835397 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -1161,6 +1161,24 @@ struct tcp_md5sig_key *tcp_v4_md5_lookup(const struct sock *sk, } EXPORT_SYMBOL(tcp_v4_md5_lookup); +static int tcp_md5sig_info_add(struct sock *sk, gfp_t gfp) +{ + struct tcp_sock *tp = tcp_sk(sk); + struct tcp_md5sig_info *md5sig; + + if (rcu_dereference_protected(tp->md5sig_info, lockdep_sock_is_held(sk))) + return 0; + + md5sig = kmalloc(sizeof(*md5sig), gfp); + if (!md5sig) + return -ENOMEM; + + sk_gso_disable(sk); + INIT_HLIST_HEAD(&md5sig->head); + rcu_assign_pointer(tp->md5sig_info, md5sig); + return 0; +} + /* This can be called on a newly created socket, from other files */ int tcp_md5_do_add(struct sock *sk, const union tcp_md5_addr *addr, int family, u8 prefixlen, int l3index, u8 flags, @@ -1191,17 +1209,11 @@ int tcp_md5_do_add(struct sock *sk, const union tcp_md5_addr *addr, return 0; } + if (tcp_md5sig_info_add(sk, gfp)) + return -ENOMEM; + md5sig = rcu_dereference_protected(tp->md5sig_info, lockdep_sock_is_held(sk)); - if (!md5sig) { - md5sig = kmalloc(sizeof(*md5sig), gfp); - if (!md5sig) - return -ENOMEM; - - sk_gso_disable(sk); - INIT_HLIST_HEAD(&md5sig->head); - rcu_assign_pointer(tp->md5sig_info, md5sig); - } key = sock_kmalloc(sk, sizeof(*key), gfp | __GFP_ZERO); if (!key) From 459837b522f7dff3b6681f534d8fff4eca19b7d1 Mon Sep 17 00:00:00 2001 From: Dmitry Safonov Date: Wed, 23 Nov 2022 17:38:57 +0000 Subject: [PATCH 1526/2002] net/tcp: Disable TCP-MD5 static key on tcp_md5sig_info destruction To do that, separate two scenarios: - where it's the first MD5 key on the system, which means that enabling of the static key may need to sleep; - copying of an existing key from a listening socket to the request socket upon receiving a signed TCP segment, where static key was already enabled (when the key was added to the listening socket). Now the life-time of the static branch for TCP-MD5 is until: - last tcp_md5sig_info is destroyed - last socket in time-wait state with MD5 key is closed. Which means that after all sockets with TCP-MD5 keys are gone, the system gets back the performance of disabled md5-key static branch. While at here, provide static_key_fast_inc() helper that does ref counter increment in atomic fashion (without grabbing cpus_read_lock() on CONFIG_JUMP_LABEL=y). This is needed to add a new user for a static_key when the caller controls the lifetime of another user. Signed-off-by: Dmitry Safonov Acked-by: Jakub Kicinski Reviewed-by: Eric Dumazet Signed-off-by: Jakub Kicinski --- include/net/tcp.h | 10 ++++-- net/ipv4/tcp.c | 5 +-- net/ipv4/tcp_ipv4.c | 71 ++++++++++++++++++++++++++++++++-------- net/ipv4/tcp_minisocks.c | 16 ++++++--- net/ipv4/tcp_output.c | 4 +-- net/ipv6/tcp_ipv6.c | 10 +++--- 6 files changed, 84 insertions(+), 32 deletions(-) diff --git a/include/net/tcp.h b/include/net/tcp.h index 6b814e788f00..f925377066fe 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -1675,7 +1675,11 @@ int tcp_v4_md5_hash_skb(char *md5_hash, const struct tcp_md5sig_key *key, const struct sock *sk, const struct sk_buff *skb); int tcp_md5_do_add(struct sock *sk, const union tcp_md5_addr *addr, int family, u8 prefixlen, int l3index, u8 flags, - const u8 *newkey, u8 newkeylen, gfp_t gfp); + const u8 *newkey, u8 newkeylen); +int tcp_md5_key_copy(struct sock *sk, const union tcp_md5_addr *addr, + int family, u8 prefixlen, int l3index, + struct tcp_md5sig_key *key); + int tcp_md5_do_del(struct sock *sk, const union tcp_md5_addr *addr, int family, u8 prefixlen, int l3index, u8 flags); struct tcp_md5sig_key *tcp_v4_md5_lookup(const struct sock *sk, @@ -1683,7 +1687,7 @@ struct tcp_md5sig_key *tcp_v4_md5_lookup(const struct sock *sk, #ifdef CONFIG_TCP_MD5SIG #include -extern struct static_key_false tcp_md5_needed; +extern struct static_key_false_deferred tcp_md5_needed; struct tcp_md5sig_key *__tcp_md5_do_lookup(const struct sock *sk, int l3index, const union tcp_md5_addr *addr, int family); @@ -1691,7 +1695,7 @@ static inline struct tcp_md5sig_key * tcp_md5_do_lookup(const struct sock *sk, int l3index, const union tcp_md5_addr *addr, int family) { - if (!static_branch_unlikely(&tcp_md5_needed)) + if (!static_branch_unlikely(&tcp_md5_needed.key)) return NULL; return __tcp_md5_do_lookup(sk, l3index, addr, family); } diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index 24602a5184b0..001947136b0a 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -4464,11 +4464,8 @@ bool tcp_alloc_md5sig_pool(void) if (unlikely(!READ_ONCE(tcp_md5sig_pool_populated))) { mutex_lock(&tcp_md5sig_mutex); - if (!tcp_md5sig_pool_populated) { + if (!tcp_md5sig_pool_populated) __tcp_alloc_md5sig_pool(); - if (tcp_md5sig_pool_populated) - static_branch_inc(&tcp_md5_needed); - } mutex_unlock(&tcp_md5sig_mutex); } diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index c72e53835397..5d83a332f1dd 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -1053,7 +1053,7 @@ static void tcp_v4_reqsk_destructor(struct request_sock *req) * We need to maintain these in the sk structure. */ -DEFINE_STATIC_KEY_FALSE(tcp_md5_needed); +DEFINE_STATIC_KEY_DEFERRED_FALSE(tcp_md5_needed, HZ); EXPORT_SYMBOL(tcp_md5_needed); static bool better_md5_match(struct tcp_md5sig_key *old, struct tcp_md5sig_key *new) @@ -1166,9 +1166,6 @@ static int tcp_md5sig_info_add(struct sock *sk, gfp_t gfp) struct tcp_sock *tp = tcp_sk(sk); struct tcp_md5sig_info *md5sig; - if (rcu_dereference_protected(tp->md5sig_info, lockdep_sock_is_held(sk))) - return 0; - md5sig = kmalloc(sizeof(*md5sig), gfp); if (!md5sig) return -ENOMEM; @@ -1180,9 +1177,9 @@ static int tcp_md5sig_info_add(struct sock *sk, gfp_t gfp) } /* This can be called on a newly created socket, from other files */ -int tcp_md5_do_add(struct sock *sk, const union tcp_md5_addr *addr, - int family, u8 prefixlen, int l3index, u8 flags, - const u8 *newkey, u8 newkeylen, gfp_t gfp) +static int __tcp_md5_do_add(struct sock *sk, const union tcp_md5_addr *addr, + int family, u8 prefixlen, int l3index, u8 flags, + const u8 *newkey, u8 newkeylen, gfp_t gfp) { /* Add Key to the list */ struct tcp_md5sig_key *key; @@ -1209,9 +1206,6 @@ int tcp_md5_do_add(struct sock *sk, const union tcp_md5_addr *addr, return 0; } - if (tcp_md5sig_info_add(sk, gfp)) - return -ENOMEM; - md5sig = rcu_dereference_protected(tp->md5sig_info, lockdep_sock_is_held(sk)); @@ -1235,8 +1229,59 @@ int tcp_md5_do_add(struct sock *sk, const union tcp_md5_addr *addr, hlist_add_head_rcu(&key->node, &md5sig->head); return 0; } + +int tcp_md5_do_add(struct sock *sk, const union tcp_md5_addr *addr, + int family, u8 prefixlen, int l3index, u8 flags, + const u8 *newkey, u8 newkeylen) +{ + struct tcp_sock *tp = tcp_sk(sk); + + if (!rcu_dereference_protected(tp->md5sig_info, lockdep_sock_is_held(sk))) { + if (tcp_md5sig_info_add(sk, GFP_KERNEL)) + return -ENOMEM; + + if (!static_branch_inc(&tcp_md5_needed.key)) { + struct tcp_md5sig_info *md5sig; + + md5sig = rcu_dereference_protected(tp->md5sig_info, lockdep_sock_is_held(sk)); + rcu_assign_pointer(tp->md5sig_info, NULL); + kfree_rcu(md5sig); + return -EUSERS; + } + } + + return __tcp_md5_do_add(sk, addr, family, prefixlen, l3index, flags, + newkey, newkeylen, GFP_KERNEL); +} EXPORT_SYMBOL(tcp_md5_do_add); +int tcp_md5_key_copy(struct sock *sk, const union tcp_md5_addr *addr, + int family, u8 prefixlen, int l3index, + struct tcp_md5sig_key *key) +{ + struct tcp_sock *tp = tcp_sk(sk); + + if (!rcu_dereference_protected(tp->md5sig_info, lockdep_sock_is_held(sk))) { + if (tcp_md5sig_info_add(sk, sk_gfp_mask(sk, GFP_ATOMIC))) + return -ENOMEM; + + if (!static_key_fast_inc_not_disabled(&tcp_md5_needed.key.key)) { + struct tcp_md5sig_info *md5sig; + + md5sig = rcu_dereference_protected(tp->md5sig_info, lockdep_sock_is_held(sk)); + net_warn_ratelimited("Too many TCP-MD5 keys in the system\n"); + rcu_assign_pointer(tp->md5sig_info, NULL); + kfree_rcu(md5sig); + return -EUSERS; + } + } + + return __tcp_md5_do_add(sk, addr, family, prefixlen, l3index, + key->flags, key->key, key->keylen, + sk_gfp_mask(sk, GFP_ATOMIC)); +} +EXPORT_SYMBOL(tcp_md5_key_copy); + int tcp_md5_do_del(struct sock *sk, const union tcp_md5_addr *addr, int family, u8 prefixlen, int l3index, u8 flags) { @@ -1323,7 +1368,7 @@ static int tcp_v4_parse_md5_keys(struct sock *sk, int optname, return -EINVAL; return tcp_md5_do_add(sk, addr, AF_INET, prefixlen, l3index, flags, - cmd.tcpm_key, cmd.tcpm_keylen, GFP_KERNEL); + cmd.tcpm_key, cmd.tcpm_keylen); } static int tcp_v4_md5_hash_headers(struct tcp_md5sig_pool *hp, @@ -1580,8 +1625,7 @@ struct sock *tcp_v4_syn_recv_sock(const struct sock *sk, struct sk_buff *skb, * memory, then we end up not copying the key * across. Shucks. */ - tcp_md5_do_add(newsk, addr, AF_INET, 32, l3index, key->flags, - key->key, key->keylen, GFP_ATOMIC); + tcp_md5_key_copy(newsk, addr, AF_INET, 32, l3index, key); sk_gso_disable(newsk); } #endif @@ -2273,6 +2317,7 @@ void tcp_v4_destroy_sock(struct sock *sk) tcp_clear_md5_list(sk); kfree_rcu(rcu_dereference_protected(tp->md5sig_info, 1), rcu); tp->md5sig_info = NULL; + static_branch_slow_dec_deferred(&tcp_md5_needed); } #endif diff --git a/net/ipv4/tcp_minisocks.c b/net/ipv4/tcp_minisocks.c index c375f603a16c..6908812d50d3 100644 --- a/net/ipv4/tcp_minisocks.c +++ b/net/ipv4/tcp_minisocks.c @@ -291,13 +291,19 @@ void tcp_time_wait(struct sock *sk, int state, int timeo) */ do { tcptw->tw_md5_key = NULL; - if (static_branch_unlikely(&tcp_md5_needed)) { + if (static_branch_unlikely(&tcp_md5_needed.key)) { struct tcp_md5sig_key *key; key = tp->af_specific->md5_lookup(sk, sk); if (key) { tcptw->tw_md5_key = kmemdup(key, sizeof(*key), GFP_ATOMIC); - BUG_ON(tcptw->tw_md5_key && !tcp_alloc_md5sig_pool()); + if (!tcptw->tw_md5_key) + break; + BUG_ON(!tcp_alloc_md5sig_pool()); + if (!static_key_fast_inc_not_disabled(&tcp_md5_needed.key.key)) { + kfree(tcptw->tw_md5_key); + tcptw->tw_md5_key = NULL; + } } } } while (0); @@ -337,11 +343,13 @@ EXPORT_SYMBOL(tcp_time_wait); void tcp_twsk_destructor(struct sock *sk) { #ifdef CONFIG_TCP_MD5SIG - if (static_branch_unlikely(&tcp_md5_needed)) { + if (static_branch_unlikely(&tcp_md5_needed.key)) { struct tcp_timewait_sock *twsk = tcp_twsk(sk); - if (twsk->tw_md5_key) + if (twsk->tw_md5_key) { kfree_rcu(twsk->tw_md5_key, rcu); + static_branch_slow_dec_deferred(&tcp_md5_needed); + } } #endif } diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index 894410dc9293..71d01cf3c13e 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -766,7 +766,7 @@ static unsigned int tcp_syn_options(struct sock *sk, struct sk_buff *skb, *md5 = NULL; #ifdef CONFIG_TCP_MD5SIG - if (static_branch_unlikely(&tcp_md5_needed) && + if (static_branch_unlikely(&tcp_md5_needed.key) && rcu_access_pointer(tp->md5sig_info)) { *md5 = tp->af_specific->md5_lookup(sk, sk); if (*md5) { @@ -922,7 +922,7 @@ static unsigned int tcp_established_options(struct sock *sk, struct sk_buff *skb *md5 = NULL; #ifdef CONFIG_TCP_MD5SIG - if (static_branch_unlikely(&tcp_md5_needed) && + if (static_branch_unlikely(&tcp_md5_needed.key) && rcu_access_pointer(tp->md5sig_info)) { *md5 = tp->af_specific->md5_lookup(sk, sk); if (*md5) { diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index f52b6f271a24..80df917ced5f 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -665,12 +665,11 @@ static int tcp_v6_parse_md5_keys(struct sock *sk, int optname, if (ipv6_addr_v4mapped(&sin6->sin6_addr)) return tcp_md5_do_add(sk, (union tcp_md5_addr *)&sin6->sin6_addr.s6_addr32[3], AF_INET, prefixlen, l3index, flags, - cmd.tcpm_key, cmd.tcpm_keylen, - GFP_KERNEL); + cmd.tcpm_key, cmd.tcpm_keylen); return tcp_md5_do_add(sk, (union tcp_md5_addr *)&sin6->sin6_addr, AF_INET6, prefixlen, l3index, flags, - cmd.tcpm_key, cmd.tcpm_keylen, GFP_KERNEL); + cmd.tcpm_key, cmd.tcpm_keylen); } static int tcp_v6_md5_hash_headers(struct tcp_md5sig_pool *hp, @@ -1370,9 +1369,8 @@ static struct sock *tcp_v6_syn_recv_sock(const struct sock *sk, struct sk_buff * * memory, then we end up not copying the key * across. Shucks. */ - tcp_md5_do_add(newsk, (union tcp_md5_addr *)&newsk->sk_v6_daddr, - AF_INET6, 128, l3index, key->flags, key->key, key->keylen, - sk_gfp_mask(sk, GFP_ATOMIC)); + tcp_md5_key_copy(newsk, (union tcp_md5_addr *)&newsk->sk_v6_daddr, + AF_INET6, 128, l3index, key); } #endif From b389d1affc2cc2dc8686cdab303a30b2ad3a81d4 Mon Sep 17 00:00:00 2001 From: Dmitry Safonov Date: Wed, 23 Nov 2022 17:38:58 +0000 Subject: [PATCH 1527/2002] net/tcp: Do cleanup on tcp_md5_key_copy() failure If the kernel was short on (atomic) memory and failed to allocate it - don't proceed to creation of request socket. Otherwise the socket would be unsigned and userspace likely doesn't expect that the TCP is not MD5-signed anymore. Signed-off-by: Dmitry Safonov Acked-by: Jakub Kicinski Reviewed-by: Eric Dumazet Signed-off-by: Jakub Kicinski --- net/ipv4/tcp_ipv4.c | 9 ++------- net/ipv6/tcp_ipv6.c | 15 ++++++++------- 2 files changed, 10 insertions(+), 14 deletions(-) diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 5d83a332f1dd..7fae586405cf 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -1619,13 +1619,8 @@ struct sock *tcp_v4_syn_recv_sock(const struct sock *sk, struct sk_buff *skb, addr = (union tcp_md5_addr *)&newinet->inet_daddr; key = tcp_md5_do_lookup(sk, l3index, addr, AF_INET); if (key) { - /* - * We're using one, so create a matching key - * on the newsk structure. If we fail to get - * memory, then we end up not copying the key - * across. Shucks. - */ - tcp_md5_key_copy(newsk, addr, AF_INET, 32, l3index, key); + if (tcp_md5_key_copy(newsk, addr, AF_INET, 32, l3index, key)) + goto put_and_exit; sk_gso_disable(newsk); } #endif diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index 80df917ced5f..11b736a76bd7 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -1364,13 +1364,14 @@ static struct sock *tcp_v6_syn_recv_sock(const struct sock *sk, struct sk_buff * /* Copy over the MD5 key from the original socket */ key = tcp_v6_md5_do_lookup(sk, &newsk->sk_v6_daddr, l3index); if (key) { - /* We're using one, so create a matching key - * on the newsk structure. If we fail to get - * memory, then we end up not copying the key - * across. Shucks. - */ - tcp_md5_key_copy(newsk, (union tcp_md5_addr *)&newsk->sk_v6_daddr, - AF_INET6, 128, l3index, key); + const union tcp_md5_addr *addr; + + addr = (union tcp_md5_addr *)&newsk->sk_v6_daddr; + if (tcp_md5_key_copy(newsk, addr, AF_INET6, 128, l3index, key)) { + inet_csk_prepare_forced_close(newsk); + tcp_done(newsk); + goto out; + } } #endif From c5b8b515a211377e78bb7807fe3e6e7212626545 Mon Sep 17 00:00:00 2001 From: Dmitry Safonov Date: Wed, 23 Nov 2022 17:38:59 +0000 Subject: [PATCH 1528/2002] net/tcp: Separate initialization of twsk Convert BUG_ON() to WARN_ON_ONCE() and warn as well for unlikely static key int overflow error-path. Signed-off-by: Dmitry Safonov Acked-by: Jakub Kicinski Reviewed-by: Eric Dumazet Signed-off-by: Jakub Kicinski --- net/ipv4/tcp_minisocks.c | 61 +++++++++++++++++++++++----------------- 1 file changed, 35 insertions(+), 26 deletions(-) diff --git a/net/ipv4/tcp_minisocks.c b/net/ipv4/tcp_minisocks.c index 6908812d50d3..e002f2e1d4f2 100644 --- a/net/ipv4/tcp_minisocks.c +++ b/net/ipv4/tcp_minisocks.c @@ -240,6 +240,40 @@ kill: } EXPORT_SYMBOL(tcp_timewait_state_process); +static void tcp_time_wait_init(struct sock *sk, struct tcp_timewait_sock *tcptw) +{ +#ifdef CONFIG_TCP_MD5SIG + const struct tcp_sock *tp = tcp_sk(sk); + struct tcp_md5sig_key *key; + + /* + * The timewait bucket does not have the key DB from the + * sock structure. We just make a quick copy of the + * md5 key being used (if indeed we are using one) + * so the timewait ack generating code has the key. + */ + tcptw->tw_md5_key = NULL; + if (!static_branch_unlikely(&tcp_md5_needed.key)) + return; + + key = tp->af_specific->md5_lookup(sk, sk); + if (key) { + tcptw->tw_md5_key = kmemdup(key, sizeof(*key), GFP_ATOMIC); + if (!tcptw->tw_md5_key) + return; + if (!tcp_alloc_md5sig_pool()) + goto out_free; + if (!static_key_fast_inc_not_disabled(&tcp_md5_needed.key.key)) + goto out_free; + } + return; +out_free: + WARN_ON_ONCE(1); + kfree(tcptw->tw_md5_key); + tcptw->tw_md5_key = NULL; +#endif +} + /* * Move a socket to time-wait or dead fin-wait-2 state. */ @@ -282,32 +316,7 @@ void tcp_time_wait(struct sock *sk, int state, int timeo) } #endif -#ifdef CONFIG_TCP_MD5SIG - /* - * The timewait bucket does not have the key DB from the - * sock structure. We just make a quick copy of the - * md5 key being used (if indeed we are using one) - * so the timewait ack generating code has the key. - */ - do { - tcptw->tw_md5_key = NULL; - if (static_branch_unlikely(&tcp_md5_needed.key)) { - struct tcp_md5sig_key *key; - - key = tp->af_specific->md5_lookup(sk, sk); - if (key) { - tcptw->tw_md5_key = kmemdup(key, sizeof(*key), GFP_ATOMIC); - if (!tcptw->tw_md5_key) - break; - BUG_ON(!tcp_alloc_md5sig_pool()); - if (!static_key_fast_inc_not_disabled(&tcp_md5_needed.key.key)) { - kfree(tcptw->tw_md5_key); - tcptw->tw_md5_key = NULL; - } - } - } - } while (0); -#endif + tcp_time_wait_init(sk, tcptw); /* Get the TIME_WAIT timeout firing. */ if (timeo < rto) From ce36d7ef4e085ad0dc415de2db32aeb0a12f7976 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ar=C4=B1n=C3=A7=20=C3=9CNAL?= Date: Wed, 30 Nov 2022 17:10:36 +0300 Subject: [PATCH 1529/2002] dt-bindings: net: qca,ar71xx: remove label = "cpu" from examples MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is not used by the DSA dt-binding, so remove it from the examples. Signed-off-by: Arınç ÜNAL Reviewed-by: Oleksij Rempel Acked-by: Rob Herring Reviewed-by: Andrew Lunn Signed-off-by: Jakub Kicinski --- Documentation/devicetree/bindings/net/qca,ar71xx.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/Documentation/devicetree/bindings/net/qca,ar71xx.yaml b/Documentation/devicetree/bindings/net/qca,ar71xx.yaml index 1ebf9e8c8a1d..89f94b31b546 100644 --- a/Documentation/devicetree/bindings/net/qca,ar71xx.yaml +++ b/Documentation/devicetree/bindings/net/qca,ar71xx.yaml @@ -123,7 +123,6 @@ examples: switch_port0: port@0 { reg = <0x0>; - label = "cpu"; ethernet = <ð1>; phy-mode = "gmii"; From a802073d1c9ca2ffd757ab8df5afa9d73ba7e6b1 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Tue, 29 Nov 2022 17:31:08 -0800 Subject: [PATCH 1530/2002] bnxt: report FEC block stats via standard interface I must have missed that these stats are only exposed via the unstructured ethtool -S when they got merged. Plumb in the structured form. Reviewed-by: Michael Chan Link: https://lore.kernel.org/r/20221130013108.90062-1-kuba@kernel.org Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c index c2f663770a7f..cbf17fcfb7ab 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c @@ -2007,6 +2007,14 @@ static void bnxt_get_fec_stats(struct net_device *dev, rx = bp->rx_port_stats_ext.sw_stats; fec_stats->corrected_bits.total = *(rx + BNXT_RX_STATS_EXT_OFFSET(rx_corrected_bits)); + + if (bp->fw_rx_stats_ext_size <= BNXT_RX_STATS_EXT_NUM_LEGACY) + return; + + fec_stats->corrected_blocks.total = + *(rx + BNXT_RX_STATS_EXT_OFFSET(rx_fec_corrected_blocks)); + fec_stats->uncorrectable_blocks.total = + *(rx + BNXT_RX_STATS_EXT_OFFSET(rx_fec_uncorrectable_blocks)); } static u32 bnxt_ethtool_forced_fec_to_fw(struct bnxt_link_info *link_info, From 1f82dffc10ff8e44bd0c2c85ba6e21189b4a5695 Mon Sep 17 00:00:00 2001 From: Dave Marchevsky Date: Thu, 1 Dec 2022 10:34:05 -0800 Subject: [PATCH 1531/2002] bpf: Fix release_on_unlock release logic for multiple refs Consider a verifier state with three acquired references, all with release_on_unlock = true: idx 0 1 2 state->refs = [2 4 6] (with 2, 4, and 6 being the ref ids). When bpf_spin_unlock is called, process_spin_lock will loop through all acquired_refs and, for each ref, if it's release_on_unlock, calls release_reference on it. That function in turn calls release_reference_state, which removes the reference from state->refs by swapping the reference state with the last reference state in refs array and decrements acquired_refs count. process_spin_lock's loop logic, which is essentially: for (i = 0; i < state->acquired_refs; i++) { if (!state->refs[i].release_on_unlock) continue; release_reference(state->refs[i].id); } will fail to release release_on_unlock references which are swapped from the end. Running this logic on our example demonstrates: state->refs = [2 4 6] (start of idx=0 iter) release state->refs[0] by swapping w/ state->refs[2] state->refs = [6 4] (start of idx=1) release state->refs[1], no need to swap as it's the last idx state->refs = [6] (start of idx=2, loop terminates) ref_id 6 should have been removed but was skipped. Fix this by looping from back-to-front, which results in refs that are candidates for removal being swapped with refs which have already been examined and kept. If we modify our initial example such that ref 6 is replaced with ref 7, which is _not_ release_on_unlock, and loop from the back, we'd see: state->refs = [2 4 7] (start of idx=2) state->refs = [2 4 7] (start of idx=1) state->refs = [2 7] (start of idx=0, refs 7 and 4 swapped) state->refs = [7] (after idx=0, 7 and 2 swapped, loop terminates) Signed-off-by: Dave Marchevsky Acked-by: Yonghong Song cc: Kumar Kartikeya Dwivedi Fixes: 534e86bc6c66 ("bpf: Add 'release on unlock' logic for bpf_list_push_{front,back}") Link: https://lore.kernel.org/r/20221201183406.1203621-1-davemarchevsky@fb.com Signed-off-by: Alexei Starovoitov --- kernel/bpf/verifier.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index d0ecc0b18b20..b0db9c10567b 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -5738,7 +5738,7 @@ static int process_spin_lock(struct bpf_verifier_env *env, int regno, cur->active_lock.ptr = NULL; cur->active_lock.id = 0; - for (i = 0; i < fstate->acquired_refs; i++) { + for (i = fstate->acquired_refs - 1; i >= 0; i--) { int err; /* Complain on error because this reference state cannot From 78b037bd402df8eca0f45ef003c6d0ab25a26ecc Mon Sep 17 00:00:00 2001 From: Dave Marchevsky Date: Thu, 1 Dec 2022 10:34:06 -0800 Subject: [PATCH 1532/2002] selftests/bpf: Validate multiple ref release_on_unlock logic Modify list_push_pop_multiple to alloc and insert nodes 2-at-a-time. Without the previous patch's fix, this block of code: bpf_spin_lock(lock); bpf_list_push_front(head, &f[i]->node); bpf_list_push_front(head, &f[i + 1]->node); bpf_spin_unlock(lock); would fail check_reference_leak check as release_on_unlock logic would miss a ref that should've been released. Signed-off-by: Dave Marchevsky cc: Kumar Kartikeya Dwivedi Acked-by: Yonghong Song Link: https://lore.kernel.org/r/20221201183406.1203621-2-davemarchevsky@fb.com Signed-off-by: Alexei Starovoitov --- tools/testing/selftests/bpf/progs/linked_list.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/tools/testing/selftests/bpf/progs/linked_list.c b/tools/testing/selftests/bpf/progs/linked_list.c index 2c7b615c6d41..4ad88da5cda2 100644 --- a/tools/testing/selftests/bpf/progs/linked_list.c +++ b/tools/testing/selftests/bpf/progs/linked_list.c @@ -99,13 +99,28 @@ int list_push_pop_multiple(struct bpf_spin_lock *lock, struct bpf_list_head *hea struct foo *f[8], *pf; int i; - for (i = 0; i < ARRAY_SIZE(f); i++) { + /* Loop following this check adds nodes 2-at-a-time in order to + * validate multiple release_on_unlock release logic + */ + if (ARRAY_SIZE(f) % 2) + return 10; + + for (i = 0; i < ARRAY_SIZE(f); i += 2) { f[i] = bpf_obj_new(typeof(**f)); if (!f[i]) return 2; f[i]->data = i; + + f[i + 1] = bpf_obj_new(typeof(**f)); + if (!f[i + 1]) { + bpf_obj_drop(f[i]); + return 9; + } + f[i + 1]->data = i + 1; + bpf_spin_lock(lock); bpf_list_push_front(head, &f[i]->node); + bpf_list_push_front(head, &f[i + 1]->node); bpf_spin_unlock(lock); } From b4e0df4cafe112220c19d30439732d22289147f1 Mon Sep 17 00:00:00 2001 From: Matthieu Baerts Date: Wed, 30 Nov 2022 15:06:23 +0100 Subject: [PATCH 1533/2002] selftests: mptcp: run mptcp_inq from a clean netns A new "sandbox" net namespace is available where no other netfilter rules have been added. Use this new netns instead of re-using "ns1" and clean it. Reviewed-by: Mat Martineau Signed-off-by: Matthieu Baerts Signed-off-by: Jakub Kicinski --- tools/testing/selftests/net/mptcp/mptcp_sockopt.sh | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/tools/testing/selftests/net/mptcp/mptcp_sockopt.sh b/tools/testing/selftests/net/mptcp/mptcp_sockopt.sh index 80d36f7cfee8..8a83100b212e 100755 --- a/tools/testing/selftests/net/mptcp/mptcp_sockopt.sh +++ b/tools/testing/selftests/net/mptcp/mptcp_sockopt.sh @@ -282,7 +282,7 @@ run_tests() do_tcpinq_test() { - ip netns exec "$ns1" ./mptcp_inq "$@" + ip netns exec "$ns_sbox" ./mptcp_inq "$@" lret=$? if [ $lret -ne 0 ];then ret=$lret @@ -298,9 +298,6 @@ do_tcpinq_tests() { local lret=0 - ip netns exec "$ns1" iptables -F - ip netns exec "$ns1" ip6tables -F - for args in "-t tcp" "-r tcp"; do do_tcpinq_test $args lret=$? From b71dd705179cfd493b17cdf67f90b19ccfe45069 Mon Sep 17 00:00:00 2001 From: Matthieu Baerts Date: Wed, 30 Nov 2022 15:06:24 +0100 Subject: [PATCH 1534/2002] selftests: mptcp: removed defined but unused vars Some variables were set but never used. This was not causing any issues except adding some confusion and having shellcheck complaining about them. Reviewed-by: Mat Martineau Signed-off-by: Matthieu Baerts Signed-off-by: Jakub Kicinski --- tools/testing/selftests/net/mptcp/mptcp_connect.sh | 3 --- tools/testing/selftests/net/mptcp/mptcp_sockopt.sh | 1 - tools/testing/selftests/net/mptcp/simult_flows.sh | 3 --- 3 files changed, 7 deletions(-) diff --git a/tools/testing/selftests/net/mptcp/mptcp_connect.sh b/tools/testing/selftests/net/mptcp/mptcp_connect.sh index 60198b91a530..63b722b505e5 100755 --- a/tools/testing/selftests/net/mptcp/mptcp_connect.sh +++ b/tools/testing/selftests/net/mptcp/mptcp_connect.sh @@ -781,7 +781,6 @@ run_tests_mptfo() run_tests_disconnect() { - local peekmode="$1" local old_cin=$cin local old_sin=$sin @@ -789,7 +788,6 @@ run_tests_disconnect() # force do_transfer to cope with the multiple tranmissions sin="$cin.disconnect" - sin_disconnect=$old_sin cin="$cin.disconnect" cin_disconnect="$old_cin" connect_per_transfer=3 @@ -800,7 +798,6 @@ run_tests_disconnect() # restore previous status sin=$old_sin - sin_disconnect="$cout".disconnect cin=$old_cin cin_disconnect="$cin".disconnect connect_per_transfer=1 diff --git a/tools/testing/selftests/net/mptcp/mptcp_sockopt.sh b/tools/testing/selftests/net/mptcp/mptcp_sockopt.sh index 8a83100b212e..6e8f4599cc44 100755 --- a/tools/testing/selftests/net/mptcp/mptcp_sockopt.sh +++ b/tools/testing/selftests/net/mptcp/mptcp_sockopt.sh @@ -10,7 +10,6 @@ ksft_skip=4 timeout_poll=30 timeout_test=$((timeout_poll * 2 + 1)) mptcp_connect="" -do_all_tests=1 add_mark_rules() { diff --git a/tools/testing/selftests/net/mptcp/simult_flows.sh b/tools/testing/selftests/net/mptcp/simult_flows.sh index a5aeefd58ab3..189a664aed81 100755 --- a/tools/testing/selftests/net/mptcp/simult_flows.sh +++ b/tools/testing/selftests/net/mptcp/simult_flows.sh @@ -148,9 +148,6 @@ do_transfer() :> "$sout" :> "$capout" - local addr_port - addr_port=$(printf "%s:%d" ${connect_addr} ${port}) - if $capture; then local capuser if [ -z $SUDO_USER ] ; then From 787eb1e4df93f469e932ef686dd9dc767a9a2392 Mon Sep 17 00:00:00 2001 From: Matthieu Baerts Date: Wed, 30 Nov 2022 15:06:25 +0100 Subject: [PATCH 1535/2002] selftests: mptcp: uniform 'rndh' variable The definition of 'rndh' was probably copied from one script to another but some times, 'sec' was not defined, not used and/or not spelled properly. Here all the 'rndh' are now defined the same way. Reviewed-by: Mat Martineau Signed-off-by: Matthieu Baerts Signed-off-by: Jakub Kicinski --- tools/testing/selftests/net/mptcp/diag.sh | 1 + tools/testing/selftests/net/mptcp/mptcp_connect.sh | 3 +-- tools/testing/selftests/net/mptcp/mptcp_join.sh | 5 +++-- tools/testing/selftests/net/mptcp/mptcp_sockopt.sh | 2 ++ tools/testing/selftests/net/mptcp/simult_flows.sh | 1 + tools/testing/selftests/net/mptcp/userspace_pm.sh | 2 +- 6 files changed, 9 insertions(+), 5 deletions(-) diff --git a/tools/testing/selftests/net/mptcp/diag.sh b/tools/testing/selftests/net/mptcp/diag.sh index 515859a5168b..24bcd7b9bdb2 100755 --- a/tools/testing/selftests/net/mptcp/diag.sh +++ b/tools/testing/selftests/net/mptcp/diag.sh @@ -1,6 +1,7 @@ #!/bin/bash # SPDX-License-Identifier: GPL-2.0 +sec=$(date +%s) rndh=$(printf %x $sec)-$(mktemp -u XXXXXX) ns="ns1-$rndh" ksft_skip=4 diff --git a/tools/testing/selftests/net/mptcp/mptcp_connect.sh b/tools/testing/selftests/net/mptcp/mptcp_connect.sh index 63b722b505e5..a43d3e2f59bb 100755 --- a/tools/testing/selftests/net/mptcp/mptcp_connect.sh +++ b/tools/testing/selftests/net/mptcp/mptcp_connect.sh @@ -274,8 +274,7 @@ check_transfer() check_mptcp_disabled() { - local disabled_ns - disabled_ns="ns_disabled-$sech-$(mktemp -u XXXXXX)" + local disabled_ns="ns_disabled-$rndh" ip netns add ${disabled_ns} || exit $ksft_skip # net.mptcp.enabled should be enabled by default diff --git a/tools/testing/selftests/net/mptcp/mptcp_join.sh b/tools/testing/selftests/net/mptcp/mptcp_join.sh index 2eeaf4aca644..2a402b3b771f 100755 --- a/tools/testing/selftests/net/mptcp/mptcp_join.sh +++ b/tools/testing/selftests/net/mptcp/mptcp_join.sh @@ -59,8 +59,9 @@ init_partial() { capout=$(mktemp) - local rndh - rndh=$(mktemp -u XXXXXX) + local sec rndh + sec=$(date +%s) + rndh=$(printf %x $sec)-$(mktemp -u XXXXXX) ns1="ns1-$rndh" ns2="ns2-$rndh" diff --git a/tools/testing/selftests/net/mptcp/mptcp_sockopt.sh b/tools/testing/selftests/net/mptcp/mptcp_sockopt.sh index 6e8f4599cc44..dbee386450f3 100755 --- a/tools/testing/selftests/net/mptcp/mptcp_sockopt.sh +++ b/tools/testing/selftests/net/mptcp/mptcp_sockopt.sh @@ -30,6 +30,8 @@ add_mark_rules() init() { + local sec rndh + sec=$(date +%s) rndh=$(printf %x $sec)-$(mktemp -u XXXXXX) ns1="ns1-$rndh" diff --git a/tools/testing/selftests/net/mptcp/simult_flows.sh b/tools/testing/selftests/net/mptcp/simult_flows.sh index 189a664aed81..9f22f7e5027d 100755 --- a/tools/testing/selftests/net/mptcp/simult_flows.sh +++ b/tools/testing/selftests/net/mptcp/simult_flows.sh @@ -1,6 +1,7 @@ #!/bin/bash # SPDX-License-Identifier: GPL-2.0 +sec=$(date +%s) rndh=$(printf %x $sec)-$(mktemp -u XXXXXX) ns1="ns1-$rndh" ns2="ns2-$rndh" diff --git a/tools/testing/selftests/net/mptcp/userspace_pm.sh b/tools/testing/selftests/net/mptcp/userspace_pm.sh index 3229725b64b0..5dfc3ee74b98 100755 --- a/tools/testing/selftests/net/mptcp/userspace_pm.sh +++ b/tools/testing/selftests/net/mptcp/userspace_pm.sh @@ -33,7 +33,7 @@ client_addr_id=${RANDOM:0:2} server_addr_id=${RANDOM:0:2} sec=$(date +%s) -rndh=$(stdbuf -o0 -e0 printf %x "$sec")-$(mktemp -u XXXXXX) +rndh=$(printf %x "$sec")-$(mktemp -u XXXXXX) ns1="ns1-$rndh" ns2="ns2-$rndh" From de2392028a19e6841cd0ef14b9c832a7a2bc3a69 Mon Sep 17 00:00:00 2001 From: Matthieu Baerts Date: Wed, 30 Nov 2022 15:06:26 +0100 Subject: [PATCH 1536/2002] selftests: mptcp: clearly declare global ns vars It is clearer to declare these global variables at the beginning of the file as it is done in other MPTCP selftests rather than in functions in the middle of the script. So for uniformity reason, we can do the same here in mptcp_sockopt.sh. Suggested-by: Geliang Tang Reviewed-by: Mat Martineau Signed-off-by: Matthieu Baerts Signed-off-by: Jakub Kicinski --- tools/testing/selftests/net/mptcp/mptcp_sockopt.sh | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/tools/testing/selftests/net/mptcp/mptcp_sockopt.sh b/tools/testing/selftests/net/mptcp/mptcp_sockopt.sh index dbee386450f3..f74b237bcb32 100755 --- a/tools/testing/selftests/net/mptcp/mptcp_sockopt.sh +++ b/tools/testing/selftests/net/mptcp/mptcp_sockopt.sh @@ -11,6 +11,12 @@ timeout_poll=30 timeout_test=$((timeout_poll * 2 + 1)) mptcp_connect="" +sec=$(date +%s) +rndh=$(printf %x $sec)-$(mktemp -u XXXXXX) +ns1="ns1-$rndh" +ns2="ns2-$rndh" +ns_sbox="ns_sbox-$rndh" + add_mark_rules() { local ns=$1 @@ -30,14 +36,6 @@ add_mark_rules() init() { - local sec rndh - sec=$(date +%s) - rndh=$(printf %x $sec)-$(mktemp -u XXXXXX) - - ns1="ns1-$rndh" - ns2="ns2-$rndh" - ns_sbox="ns_sbox-$rndh" - for netns in "$ns1" "$ns2" "$ns_sbox";do ip netns add $netns || exit $ksft_skip ip -net $netns link set lo up From 5f17f8e315ad77c7ba45afe169b4598efd5d5bc3 Mon Sep 17 00:00:00 2001 From: Matthieu Baerts Date: Wed, 30 Nov 2022 15:06:27 +0100 Subject: [PATCH 1537/2002] selftests: mptcp: declare var as local Just to avoid classical Bash pitfall where variables are accidentally overridden by other functions because the proper scope has not been defined. That's also what is done in other MPTCP selftests scripts where all non local variables are defined at the beginning of the script and the others are defined with the "local" keyword. Reviewed-by: Mat Martineau Signed-off-by: Matthieu Baerts Signed-off-by: Jakub Kicinski --- .../selftests/net/mptcp/mptcp_sockopt.sh | 51 +++++++++++-------- 1 file changed, 29 insertions(+), 22 deletions(-) diff --git a/tools/testing/selftests/net/mptcp/mptcp_sockopt.sh b/tools/testing/selftests/net/mptcp/mptcp_sockopt.sh index f74b237bcb32..1b70c0a304ce 100755 --- a/tools/testing/selftests/net/mptcp/mptcp_sockopt.sh +++ b/tools/testing/selftests/net/mptcp/mptcp_sockopt.sh @@ -22,6 +22,7 @@ add_mark_rules() local ns=$1 local m=$2 + local t for t in iptables ip6tables; do # just to debug: check we have multiple subflows connection requests ip netns exec $ns $t -A OUTPUT -p tcp --syn -m mark --mark $m -j ACCEPT @@ -36,6 +37,7 @@ add_mark_rules() init() { + local netns for netns in "$ns1" "$ns2" "$ns_sbox";do ip netns add $netns || exit $ksft_skip ip -net $netns link set lo up @@ -44,6 +46,7 @@ init() ip netns exec $netns sysctl -q net.ipv4.conf.default.rp_filter=0 done + local i for i in `seq 1 4`; do ip link add ns1eth$i netns "$ns1" type veth peer name ns2eth$i netns "$ns2" ip -net "$ns1" addr add 10.0.$i.1/24 dev ns1eth$i @@ -73,6 +76,7 @@ init() cleanup() { + local netns for netns in "$ns1" "$ns2" "$ns_sbox"; do ip netns del $netns done @@ -103,15 +107,17 @@ check_mark() local ns=$1 local af=$2 - tables=iptables + local tables=iptables if [ $af -eq 6 ];then tables=ip6tables fi + local counters values counters=$(ip netns exec $ns $tables -v -L OUTPUT | grep DROP) values=${counters%DROP*} + local v for v in $values; do if [ $v -ne 0 ]; then echo "FAIL: got $tables $values in ns $ns , not 0 - not all expected packets marked" 1>&2 @@ -131,9 +137,9 @@ print_file_err() check_transfer() { - in=$1 - out=$2 - what=$3 + local in=$1 + local out=$2 + local what=$3 cmp "$in" "$out" > /dev/null 2>&1 if [ $? -ne 0 ] ;then @@ -156,18 +162,18 @@ is_v6() do_transfer() { - listener_ns="$1" - connector_ns="$2" - cl_proto="$3" - srv_proto="$4" - connect_addr="$5" + local listener_ns="$1" + local connector_ns="$2" + local cl_proto="$3" + local srv_proto="$4" + local connect_addr="$5" - port=12001 + local port=12001 :> "$cout" :> "$sout" - mptcp_connect="./mptcp_connect -r 20" + local mptcp_connect="./mptcp_connect -r 20" local local_addr if is_v6 "${connect_addr}"; then @@ -180,7 +186,7 @@ do_transfer() ip netns exec ${listener_ns} \ $mptcp_connect -t ${timeout_poll} -l -M 1 -p $port -s ${srv_proto} -c TIMESTAMPNS,TCPINQ \ ${local_addr} < "$sin" > "$sout" & - spid=$! + local spid=$! sleep 1 @@ -189,12 +195,12 @@ do_transfer() $mptcp_connect -t ${timeout_poll} -M 2 -p $port -s ${cl_proto} -c TIMESTAMPNS,TCPINQ \ $connect_addr < "$cin" > "$cout" & - cpid=$! + local cpid=$! wait $cpid - retc=$? + local retc=$? wait $spid - rets=$? + local rets=$? if [ ${rets} -ne 0 ] || [ ${retc} -ne 0 ]; then echo " client exit code $retc, server $rets" 1>&2 @@ -229,9 +235,9 @@ do_transfer() make_file() { - name=$1 - who=$2 - size=$3 + local name=$1 + local who=$2 + local size=$3 dd if=/dev/urandom of="$name" bs=1024 count=$size 2> /dev/null echo -e "\nMPTCP_TEST_FILE_END_MARKER" >> "$name" @@ -264,9 +270,9 @@ do_mptcp_sockopt_tests() run_tests() { - listener_ns="$1" - connector_ns="$2" - connect_addr="$3" + local listener_ns="$1" + local connector_ns="$2" + local connect_addr="$3" local lret=0 do_transfer ${listener_ns} ${connector_ns} MPTCP MPTCP ${connect_addr} @@ -282,7 +288,7 @@ run_tests() do_tcpinq_test() { ip netns exec "$ns_sbox" ./mptcp_inq "$@" - lret=$? + local lret=$? if [ $lret -ne 0 ];then ret=$lret echo "FAIL: mptcp_inq $@" 1>&2 @@ -297,6 +303,7 @@ do_tcpinq_tests() { local lret=0 + local args for args in "-t tcp" "-r tcp"; do do_tcpinq_test $args lret=$? From f8c9dfbd875b17fee59c7f1aa35a4944d4e6d810 Mon Sep 17 00:00:00 2001 From: Geliang Tang Date: Wed, 30 Nov 2022 15:06:28 +0100 Subject: [PATCH 1538/2002] mptcp: add pm listener events This patch adds two new MPTCP netlink event types for PM listening socket create and close, named MPTCP_EVENT_LISTENER_CREATED and MPTCP_EVENT_LISTENER_CLOSED. Add a new function mptcp_event_pm_listener() to push the new events with family, port and addr to userspace. Invoke mptcp_event_pm_listener() with MPTCP_EVENT_LISTENER_CREATED in mptcp_listen() and mptcp_pm_nl_create_listen_socket(), invoke it with MPTCP_EVENT_LISTENER_CLOSED in __mptcp_close_ssk(). Signed-off-by: Geliang Tang Reviewed-by: Mat Martineau Signed-off-by: Matthieu Baerts Signed-off-by: Jakub Kicinski --- include/uapi/linux/mptcp.h | 9 ++++++ net/mptcp/pm_netlink.c | 57 ++++++++++++++++++++++++++++++++++++++ net/mptcp/protocol.c | 3 ++ net/mptcp/protocol.h | 2 ++ 4 files changed, 71 insertions(+) diff --git a/include/uapi/linux/mptcp.h b/include/uapi/linux/mptcp.h index dfe19bf13f4c..32af2d278cb4 100644 --- a/include/uapi/linux/mptcp.h +++ b/include/uapi/linux/mptcp.h @@ -160,6 +160,12 @@ struct mptcp_info { * daddr4 | daddr6, sport, dport, backup, if_idx * [, error] * The priority of a subflow has changed. 'error' should not be set. + * + * MPTCP_EVENT_LISTENER_CREATED: family, sport, saddr4 | saddr6 + * A new PM listener is created. + * + * MPTCP_EVENT_LISTENER_CLOSED: family, sport, saddr4 | saddr6 + * A PM listener is closed. */ enum mptcp_event_type { MPTCP_EVENT_UNSPEC = 0, @@ -174,6 +180,9 @@ enum mptcp_event_type { MPTCP_EVENT_SUB_CLOSED = 11, MPTCP_EVENT_SUB_PRIORITY = 13, + + MPTCP_EVENT_LISTENER_CREATED = 15, + MPTCP_EVENT_LISTENER_CLOSED = 16, }; enum mptcp_event_attr { diff --git a/net/mptcp/pm_netlink.c b/net/mptcp/pm_netlink.c index d66fbd558263..eef69d0e44ec 100644 --- a/net/mptcp/pm_netlink.c +++ b/net/mptcp/pm_netlink.c @@ -1029,6 +1029,8 @@ static int mptcp_pm_nl_create_listen_socket(struct sock *sk, if (err) return err; + mptcp_event_pm_listener(ssock->sk, MPTCP_EVENT_LISTENER_CREATED); + return 0; } @@ -2152,6 +2154,58 @@ nla_put_failure: kfree_skb(skb); } +void mptcp_event_pm_listener(const struct sock *ssk, + enum mptcp_event_type event) +{ + const struct inet_sock *issk = inet_sk(ssk); + struct net *net = sock_net(ssk); + struct nlmsghdr *nlh; + struct sk_buff *skb; + + if (!genl_has_listeners(&mptcp_genl_family, net, MPTCP_PM_EV_GRP_OFFSET)) + return; + + skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); + if (!skb) + return; + + nlh = genlmsg_put(skb, 0, 0, &mptcp_genl_family, 0, event); + if (!nlh) + goto nla_put_failure; + + if (nla_put_u16(skb, MPTCP_ATTR_FAMILY, ssk->sk_family)) + goto nla_put_failure; + + if (nla_put_be16(skb, MPTCP_ATTR_SPORT, issk->inet_sport)) + goto nla_put_failure; + + switch (ssk->sk_family) { + case AF_INET: + if (nla_put_in_addr(skb, MPTCP_ATTR_SADDR4, issk->inet_saddr)) + goto nla_put_failure; + break; +#if IS_ENABLED(CONFIG_MPTCP_IPV6) + case AF_INET6: { + const struct ipv6_pinfo *np = inet6_sk(ssk); + + if (nla_put_in6_addr(skb, MPTCP_ATTR_SADDR6, &np->saddr)) + goto nla_put_failure; + break; + } +#endif + default: + WARN_ON_ONCE(1); + goto nla_put_failure; + } + + genlmsg_end(skb, nlh); + mptcp_nl_mcast_send(net, skb, GFP_KERNEL); + return; + +nla_put_failure: + kfree_skb(skb); +} + void mptcp_event(enum mptcp_event_type type, const struct mptcp_sock *msk, const struct sock *ssk, gfp_t gfp) { @@ -2197,6 +2251,9 @@ void mptcp_event(enum mptcp_event_type type, const struct mptcp_sock *msk, if (mptcp_event_sub_closed(skb, msk, ssk) < 0) goto nla_put_failure; break; + case MPTCP_EVENT_LISTENER_CREATED: + case MPTCP_EVENT_LISTENER_CLOSED: + break; } genlmsg_end(skb, nlh); diff --git a/net/mptcp/protocol.c b/net/mptcp/protocol.c index b0d387be500a..f6f93957275b 100644 --- a/net/mptcp/protocol.c +++ b/net/mptcp/protocol.c @@ -2355,6 +2355,7 @@ static void __mptcp_close_ssk(struct sock *sk, struct sock *ssk, tcp_set_state(ssk, TCP_CLOSE); mptcp_subflow_queue_clean(ssk); inet_csk_listen_stop(ssk); + mptcp_event_pm_listener(ssk, MPTCP_EVENT_LISTENER_CLOSED); } __tcp_close(ssk, 0); @@ -3647,6 +3648,8 @@ static int mptcp_listen(struct socket *sock, int backlog) if (!err) mptcp_copy_inaddrs(sock->sk, ssock->sk); + mptcp_event_pm_listener(ssock->sk, MPTCP_EVENT_LISTENER_CREATED); + unlock: release_sock(sock->sk); return err; diff --git a/net/mptcp/protocol.h b/net/mptcp/protocol.h index 8b4379a2cd85..955fb3d88eb3 100644 --- a/net/mptcp/protocol.h +++ b/net/mptcp/protocol.h @@ -839,6 +839,8 @@ void mptcp_event(enum mptcp_event_type type, const struct mptcp_sock *msk, const struct sock *ssk, gfp_t gfp); void mptcp_event_addr_announced(const struct sock *ssk, const struct mptcp_addr_info *info); void mptcp_event_addr_removed(const struct mptcp_sock *msk, u8 id); +void mptcp_event_pm_listener(const struct sock *ssk, + enum mptcp_event_type event); bool mptcp_userspace_pm_active(const struct mptcp_sock *msk); void mptcp_fastopen_gen_msk_ackseq(struct mptcp_sock *msk, struct mptcp_subflow_context *subflow, From 7dff74f5716edf2f0288fe4cdcb4feeeb6a9383b Mon Sep 17 00:00:00 2001 From: Geliang Tang Date: Wed, 30 Nov 2022 15:06:29 +0100 Subject: [PATCH 1539/2002] selftests: mptcp: enhance userspace pm tests Some userspace pm tests failed since pm listener events have been added. Now MPTCP_EVENT_LISTENER_CREATED event becomes the first item in the events list like this: type:15,family:2,sport:10006,saddr4:0.0.0.0 type:1,token:3701282876,server_side:1,family:2,saddr4:10.0.1.1,... And no token value in this MPTCP_EVENT_LISTENER_CREATED event. This patch fixes this by specifying the type 1 item to search for token values. Signed-off-by: Geliang Tang Reviewed-by: Mat Martineau Signed-off-by: Matthieu Baerts Signed-off-by: Jakub Kicinski --- tools/testing/selftests/net/mptcp/mptcp_join.sh | 3 ++- tools/testing/selftests/net/mptcp/userspace_pm.sh | 7 ++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/tools/testing/selftests/net/mptcp/mptcp_join.sh b/tools/testing/selftests/net/mptcp/mptcp_join.sh index 2a402b3b771f..f10ef65a7009 100755 --- a/tools/testing/selftests/net/mptcp/mptcp_join.sh +++ b/tools/testing/selftests/net/mptcp/mptcp_join.sh @@ -830,7 +830,8 @@ do_transfer() if [ $userspace_pm -eq 0 ]; then pm_nl_add_endpoint $ns1 $addr flags signal else - tk=$(sed -n 's/.*\(token:\)\([[:digit:]]*\).*$/\2/p;q' "$evts_ns1") + tk=$(grep "type:1," "$evts_ns1" | + sed -n 's/.*\(token:\)\([[:digit:]]*\).*$/\2/p;q') ip netns exec ${listener_ns} ./pm_nl_ctl ann $addr token $tk id $id sleep 1 ip netns exec ${listener_ns} ./pm_nl_ctl rem token $tk id $id diff --git a/tools/testing/selftests/net/mptcp/userspace_pm.sh b/tools/testing/selftests/net/mptcp/userspace_pm.sh index 5dfc3ee74b98..08a88ea47a29 100755 --- a/tools/testing/selftests/net/mptcp/userspace_pm.sh +++ b/tools/testing/selftests/net/mptcp/userspace_pm.sh @@ -172,9 +172,10 @@ make_connection() client_serverside=$(sed --unbuffered -n 's/.*\(server_side:\)\([[:digit:]]*\).*$/\2/p;q'\ "$client_evts") kill_wait $server_evts_pid - server_token=$(sed --unbuffered -n 's/.*\(token:\)\([[:digit:]]*\).*$/\2/p;q' "$server_evts") - server_serverside=$(sed --unbuffered -n 's/.*\(server_side:\)\([[:digit:]]*\).*$/\2/p;q'\ - "$server_evts") + server_token=$(grep "type:1," "$server_evts" | + sed --unbuffered -n 's/.*\(token:\)\([[:digit:]]*\).*$/\2/p;q') + server_serverside=$(grep "type:1," "$server_evts" | + sed --unbuffered -n 's/.*\(server_side:\)\([[:digit:]]*\).*$/\2/p;q') rm -f "$client_evts" "$server_evts" "$file" if [ "$client_token" != "" ] && [ "$server_token" != "" ] && [ "$client_serverside" = 0 ] && From 1cc94ac1af4b18c69981425df6f0355f13d9304d Mon Sep 17 00:00:00 2001 From: Geliang Tang Date: Wed, 30 Nov 2022 15:06:30 +0100 Subject: [PATCH 1540/2002] selftests: mptcp: make evts global in userspace_pm This patch makes server_evts and client_evts global in userspace_pm.sh, then these two variables could be used in test_announce(), test_remove() and test_subflows(). The local variable 'evts' in these three functions then could be dropped. Also move local variable 'file' as a global one. Suggested-by: Paolo Abeni Signed-off-by: Geliang Tang Acked-by: Paolo Abeni Signed-off-by: Matthieu Baerts Signed-off-by: Jakub Kicinski --- .../selftests/net/mptcp/userspace_pm.sh | 213 ++++++++---------- 1 file changed, 93 insertions(+), 120 deletions(-) diff --git a/tools/testing/selftests/net/mptcp/userspace_pm.sh b/tools/testing/selftests/net/mptcp/userspace_pm.sh index 08a88ea47a29..91d6f30e2fe4 100755 --- a/tools/testing/selftests/net/mptcp/userspace_pm.sh +++ b/tools/testing/selftests/net/mptcp/userspace_pm.sh @@ -15,7 +15,11 @@ SUB_CLOSED=11 # MPTCP_EVENT_SUB_CLOSED AF_INET=2 AF_INET6=10 -evts_pid=0 +file="" +server_evts="" +client_evts="" +server_evts_pid=0 +client_evts_pid=0 client4_pid=0 server4_pid=0 client6_pid=0 @@ -47,7 +51,7 @@ cleanup() { echo "cleanup" - rm -rf $file + rm -rf $file $client_evts $server_evts # Terminate the MPTCP connection and related processes if [ $client4_pid -ne 0 ]; then @@ -62,8 +66,11 @@ cleanup() if [ $server6_pid -ne 0 ]; then kill_wait $server6_pid fi - if [ $evts_pid -ne 0 ]; then - kill_wait $evts_pid + if [ $server_evts_pid -ne 0 ]; then + kill_wait $server_evts_pid + fi + if [ $client_evts_pid -ne 0 ]; then + kill_wait $client_evts_pid fi local netns for netns in "$ns1" "$ns2" ;do @@ -113,8 +120,9 @@ make_file() make_connection() { - local file - file=$(mktemp) + if [ -z "$file" ]; then + file=$(mktemp) + fi make_file "$file" "client" local is_v6=$1 @@ -132,16 +140,24 @@ make_connection() # Capture netlink events over the two network namespaces running # the MPTCP client and server - local client_evts - client_evts=$(mktemp) + if [ -z "$client_evts" ]; then + client_evts=$(mktemp) + fi :>"$client_evts" + if [ $client_evts_pid -ne 0 ]; then + kill_wait $client_evts_pid + fi ip netns exec "$ns2" ./pm_nl_ctl events >> "$client_evts" 2>&1 & - local client_evts_pid=$! - local server_evts - server_evts=$(mktemp) + client_evts_pid=$! + if [ -z "$server_evts" ]; then + server_evts=$(mktemp) + fi :>"$server_evts" + if [ $server_evts_pid -ne 0 ]; then + kill_wait $server_evts_pid + fi ip netns exec "$ns1" ./pm_nl_ctl events >> "$server_evts" 2>&1 & - local server_evts_pid=$! + server_evts_pid=$! sleep 0.5 # Run the server @@ -159,7 +175,6 @@ make_connection() sleep 1 # Capture client/server attributes from MPTCP connection netlink events - kill_wait $client_evts_pid local client_token local client_port @@ -171,12 +186,10 @@ make_connection() client_port=$(sed --unbuffered -n 's/.*\(sport:\)\([[:digit:]]*\).*$/\2/p;q' "$client_evts") client_serverside=$(sed --unbuffered -n 's/.*\(server_side:\)\([[:digit:]]*\).*$/\2/p;q'\ "$client_evts") - kill_wait $server_evts_pid server_token=$(grep "type:1," "$server_evts" | sed --unbuffered -n 's/.*\(token:\)\([[:digit:]]*\).*$/\2/p;q') server_serverside=$(grep "type:1," "$server_evts" | sed --unbuffered -n 's/.*\(server_side:\)\([[:digit:]]*\).*$/\2/p;q') - rm -f "$client_evts" "$server_evts" "$file" if [ "$client_token" != "" ] && [ "$server_token" != "" ] && [ "$client_serverside" = 0 ] && [ "$server_serverside" = 1 ] @@ -240,13 +253,8 @@ verify_announce_event() test_announce() { - local evts - evts=$(mktemp) # Capture events on the network namespace running the server - :>"$evts" - ip netns exec "$ns1" ./pm_nl_ctl events >> "$evts" 2>&1 & - evts_pid=$! - sleep 0.5 + :>"$server_evts" # ADD_ADDR using an invalid token should result in no action local invalid_token=$(( client4_token - 1)) @@ -254,7 +262,7 @@ test_announce() $client_addr_id dev ns2eth1 > /dev/null 2>&1 local type - type=$(sed --unbuffered -n 's/.*\(type:\)\([[:digit:]]*\).*$/\2/p;q' "$evts") + type=$(sed --unbuffered -n 's/.*\(type:\)\([[:digit:]]*\).*$/\2/p;q' "$server_evts") stdbuf -o0 -e0 printf "ADD_ADDR 10.0.2.2 (ns2) => ns1, invalid token \t\t" if [ "$type" = "" ] then @@ -265,71 +273,63 @@ test_announce() fi # ADD_ADDR from the client to server machine reusing the subflow port - :>"$evts" + :>"$server_evts" ip netns exec "$ns2"\ ./pm_nl_ctl ann 10.0.2.2 token "$client4_token" id $client_addr_id dev\ ns2eth1 > /dev/null 2>&1 stdbuf -o0 -e0 printf "ADD_ADDR id:%d 10.0.2.2 (ns2) => ns1, reuse port \t\t" $client_addr_id sleep 0.5 - verify_announce_event "$evts" "$ANNOUNCED" "$server4_token" "10.0.2.2" "$client_addr_id"\ + verify_announce_event $server_evts $ANNOUNCED $server4_token "10.0.2.2" $client_addr_id \ "$client4_port" # ADD_ADDR6 from the client to server machine reusing the subflow port - :>"$evts" + :>"$server_evts" ip netns exec "$ns2" ./pm_nl_ctl ann\ dead:beef:2::2 token "$client6_token" id $client_addr_id dev ns2eth1 > /dev/null 2>&1 stdbuf -o0 -e0 printf "ADD_ADDR6 id:%d dead:beef:2::2 (ns2) => ns1, reuse port\t\t" $client_addr_id sleep 0.5 - verify_announce_event "$evts" "$ANNOUNCED" "$server6_token" "dead:beef:2::2"\ + verify_announce_event "$server_evts" "$ANNOUNCED" "$server6_token" "dead:beef:2::2"\ "$client_addr_id" "$client6_port" "v6" # ADD_ADDR from the client to server machine using a new port - :>"$evts" + :>"$server_evts" client_addr_id=$((client_addr_id+1)) ip netns exec "$ns2" ./pm_nl_ctl ann 10.0.2.2 token "$client4_token" id\ $client_addr_id dev ns2eth1 port $new4_port > /dev/null 2>&1 stdbuf -o0 -e0 printf "ADD_ADDR id:%d 10.0.2.2 (ns2) => ns1, new port \t\t\t" $client_addr_id sleep 0.5 - verify_announce_event "$evts" "$ANNOUNCED" "$server4_token" "10.0.2.2"\ + verify_announce_event "$server_evts" "$ANNOUNCED" "$server4_token" "10.0.2.2"\ "$client_addr_id" "$new4_port" - kill_wait $evts_pid - # Capture events on the network namespace running the client - :>"$evts" - ip netns exec "$ns2" ./pm_nl_ctl events >> "$evts" 2>&1 & - evts_pid=$! - sleep 0.5 + :>"$client_evts" # ADD_ADDR from the server to client machine reusing the subflow port ip netns exec "$ns1" ./pm_nl_ctl ann 10.0.2.1 token "$server4_token" id\ $server_addr_id dev ns1eth2 > /dev/null 2>&1 stdbuf -o0 -e0 printf "ADD_ADDR id:%d 10.0.2.1 (ns1) => ns2, reuse port \t\t" $server_addr_id sleep 0.5 - verify_announce_event "$evts" "$ANNOUNCED" "$client4_token" "10.0.2.1"\ + verify_announce_event "$client_evts" "$ANNOUNCED" "$client4_token" "10.0.2.1"\ "$server_addr_id" "$app4_port" # ADD_ADDR6 from the server to client machine reusing the subflow port - :>"$evts" + :>"$client_evts" ip netns exec "$ns1" ./pm_nl_ctl ann dead:beef:2::1 token "$server6_token" id\ $server_addr_id dev ns1eth2 > /dev/null 2>&1 stdbuf -o0 -e0 printf "ADD_ADDR6 id:%d dead:beef:2::1 (ns1) => ns2, reuse port\t\t" $server_addr_id sleep 0.5 - verify_announce_event "$evts" "$ANNOUNCED" "$client6_token" "dead:beef:2::1"\ + verify_announce_event "$client_evts" "$ANNOUNCED" "$client6_token" "dead:beef:2::1"\ "$server_addr_id" "$app6_port" "v6" # ADD_ADDR from the server to client machine using a new port - :>"$evts" + :>"$client_evts" server_addr_id=$((server_addr_id+1)) ip netns exec "$ns1" ./pm_nl_ctl ann 10.0.2.1 token "$server4_token" id\ $server_addr_id dev ns1eth2 port $new4_port > /dev/null 2>&1 stdbuf -o0 -e0 printf "ADD_ADDR id:%d 10.0.2.1 (ns1) => ns2, new port \t\t\t" $server_addr_id sleep 0.5 - verify_announce_event "$evts" "$ANNOUNCED" "$client4_token" "10.0.2.1"\ + verify_announce_event "$client_evts" "$ANNOUNCED" "$client4_token" "10.0.2.1"\ "$server_addr_id" "$new4_port" - - kill_wait $evts_pid - rm -f "$evts" } verify_remove_event() @@ -357,14 +357,8 @@ verify_remove_event() test_remove() { - local evts - evts=$(mktemp) - # Capture events on the network namespace running the server - :>"$evts" - ip netns exec "$ns1" ./pm_nl_ctl events >> "$evts" 2>&1 & - evts_pid=$! - sleep 0.5 + :>"$server_evts" # RM_ADDR using an invalid token should result in no action local invalid_token=$(( client4_token - 1 )) @@ -373,7 +367,7 @@ test_remove() stdbuf -o0 -e0 printf "RM_ADDR id:%d ns2 => ns1, invalid token \t"\ $client_addr_id local type - type=$(sed --unbuffered -n 's/.*\(type:\)\([[:digit:]]*\).*$/\2/p;q' "$evts") + type=$(sed --unbuffered -n 's/.*\(type:\)\([[:digit:]]*\).*$/\2/p;q' "$server_evts") if [ "$type" = "" ] then stdbuf -o0 -e0 printf "[OK]\n" @@ -387,7 +381,7 @@ test_remove() $invalid_id > /dev/null 2>&1 stdbuf -o0 -e0 printf "RM_ADDR id:%d ns2 => ns1, invalid id \t"\ $invalid_id - type=$(sed --unbuffered -n 's/.*\(type:\)\([[:digit:]]*\).*$/\2/p;q' "$evts") + type=$(sed --unbuffered -n 's/.*\(type:\)\([[:digit:]]*\).*$/\2/p;q' "$server_evts") if [ "$type" = "" ] then stdbuf -o0 -e0 printf "[OK]\n" @@ -396,40 +390,35 @@ test_remove() fi # RM_ADDR from the client to server machine - :>"$evts" + :>"$server_evts" ip netns exec "$ns2" ./pm_nl_ctl rem token "$client4_token" id\ $client_addr_id > /dev/null 2>&1 stdbuf -o0 -e0 printf "RM_ADDR id:%d ns2 => ns1 \t"\ $client_addr_id sleep 0.5 - verify_remove_event "$evts" "$REMOVED" "$server4_token" "$client_addr_id" + verify_remove_event "$server_evts" "$REMOVED" "$server4_token" "$client_addr_id" # RM_ADDR from the client to server machine - :>"$evts" + :>"$server_evts" client_addr_id=$(( client_addr_id - 1 )) ip netns exec "$ns2" ./pm_nl_ctl rem token "$client4_token" id\ $client_addr_id > /dev/null 2>&1 stdbuf -o0 -e0 printf "RM_ADDR id:%d ns2 => ns1 \t"\ $client_addr_id sleep 0.5 - verify_remove_event "$evts" "$REMOVED" "$server4_token" "$client_addr_id" + verify_remove_event "$server_evts" "$REMOVED" "$server4_token" "$client_addr_id" # RM_ADDR6 from the client to server machine - :>"$evts" + :>"$server_evts" ip netns exec "$ns2" ./pm_nl_ctl rem token "$client6_token" id\ $client_addr_id > /dev/null 2>&1 stdbuf -o0 -e0 printf "RM_ADDR6 id:%d ns2 => ns1 \t"\ $client_addr_id sleep 0.5 - verify_remove_event "$evts" "$REMOVED" "$server6_token" "$client_addr_id" - - kill_wait $evts_pid + verify_remove_event "$server_evts" "$REMOVED" "$server6_token" "$client_addr_id" # Capture events on the network namespace running the client - :>"$evts" - ip netns exec "$ns2" ./pm_nl_ctl events >> "$evts" 2>&1 & - evts_pid=$! - sleep 0.5 + :>"$client_evts" # RM_ADDR from the server to client machine ip netns exec "$ns1" ./pm_nl_ctl rem token "$server4_token" id\ @@ -437,27 +426,24 @@ test_remove() stdbuf -o0 -e0 printf "RM_ADDR id:%d ns1 => ns2 \t"\ $server_addr_id sleep 0.5 - verify_remove_event "$evts" "$REMOVED" "$client4_token" "$server_addr_id" + verify_remove_event "$client_evts" "$REMOVED" "$client4_token" "$server_addr_id" # RM_ADDR from the server to client machine - :>"$evts" + :>"$client_evts" server_addr_id=$(( server_addr_id - 1 )) ip netns exec "$ns1" ./pm_nl_ctl rem token "$server4_token" id\ $server_addr_id > /dev/null 2>&1 stdbuf -o0 -e0 printf "RM_ADDR id:%d ns1 => ns2 \t" $server_addr_id sleep 0.5 - verify_remove_event "$evts" "$REMOVED" "$client4_token" "$server_addr_id" + verify_remove_event "$client_evts" "$REMOVED" "$client4_token" "$server_addr_id" # RM_ADDR6 from the server to client machine - :>"$evts" + :>"$client_evts" ip netns exec "$ns1" ./pm_nl_ctl rem token "$server6_token" id\ $server_addr_id > /dev/null 2>&1 stdbuf -o0 -e0 printf "RM_ADDR6 id:%d ns1 => ns2 \t" $server_addr_id sleep 0.5 - verify_remove_event "$evts" "$REMOVED" "$client6_token" "$server_addr_id" - - kill_wait $evts_pid - rm -f "$evts" + verify_remove_event "$client_evts" "$REMOVED" "$client6_token" "$server_addr_id" } verify_subflow_events() @@ -533,13 +519,8 @@ verify_subflow_events() test_subflows() { - local evts - evts=$(mktemp) # Capture events on the network namespace running the server - :>"$evts" - ip netns exec "$ns1" ./pm_nl_ctl events >> "$evts" 2>&1 & - evts_pid=$! - sleep 0.5 + :>"$server_evts" # Attempt to add a listener at 10.0.2.2: ip netns exec "$ns2" ./pm_nl_ctl listen 10.0.2.2\ @@ -552,25 +533,25 @@ test_subflows() sleep 0.5 # CREATE_SUBFLOW from server to client machine - :>"$evts" + :>"$server_evts" ip netns exec "$ns1" ./pm_nl_ctl csf lip 10.0.2.1 lid 23 rip 10.0.2.2\ rport "$client4_port" token "$server4_token" > /dev/null 2>&1 sleep 0.5 - verify_subflow_events "$evts" "$SUB_ESTABLISHED" "$server4_token" "$AF_INET" "10.0.2.1"\ + verify_subflow_events $server_evts $SUB_ESTABLISHED $server4_token $AF_INET "10.0.2.1" \ "10.0.2.2" "$client4_port" "23" "$client_addr_id" "ns1" "ns2" # Delete the listener from the client ns, if one was created kill_wait $listener_pid local sport - sport=$(sed --unbuffered -n 's/.*\(sport:\)\([[:digit:]]*\).*$/\2/p;q' "$evts") + sport=$(sed --unbuffered -n 's/.*\(sport:\)\([[:digit:]]*\).*$/\2/p;q' "$server_evts") # DESTROY_SUBFLOW from server to client machine - :>"$evts" + :>"$server_evts" ip netns exec "$ns1" ./pm_nl_ctl dsf lip 10.0.2.1 lport "$sport" rip 10.0.2.2 rport\ "$client4_port" token "$server4_token" > /dev/null 2>&1 sleep 0.5 - verify_subflow_events "$evts" "$SUB_CLOSED" "$server4_token" "$AF_INET" "10.0.2.1"\ + verify_subflow_events "$server_evts" "$SUB_CLOSED" "$server4_token" "$AF_INET" "10.0.2.1"\ "10.0.2.2" "$client4_port" "23" "$client_addr_id" "ns1" "ns2" # RM_ADDR from client to server machine @@ -584,31 +565,31 @@ test_subflows() listener_pid=$! # ADD_ADDR6 from client to server machine reusing the subflow port - :>"$evts" + :>"$server_evts" ip netns exec "$ns2" ./pm_nl_ctl ann dead:beef:2::2 token "$client6_token" id\ $client_addr_id > /dev/null 2>&1 sleep 0.5 # CREATE_SUBFLOW6 from server to client machine - :>"$evts" + :>"$server_evts" ip netns exec "$ns1" ./pm_nl_ctl csf lip dead:beef:2::1 lid 23 rip\ dead:beef:2::2 rport "$client6_port" token "$server6_token" > /dev/null 2>&1 sleep 0.5 - verify_subflow_events "$evts" "$SUB_ESTABLISHED" "$server6_token" "$AF_INET6"\ + verify_subflow_events "$server_evts" "$SUB_ESTABLISHED" "$server6_token" "$AF_INET6"\ "dead:beef:2::1" "dead:beef:2::2" "$client6_port" "23"\ "$client_addr_id" "ns1" "ns2" # Delete the listener from the client ns, if one was created kill_wait $listener_pid - sport=$(sed --unbuffered -n 's/.*\(sport:\)\([[:digit:]]*\).*$/\2/p;q' "$evts") + sport=$(sed --unbuffered -n 's/.*\(sport:\)\([[:digit:]]*\).*$/\2/p;q' "$server_evts") # DESTROY_SUBFLOW6 from server to client machine - :>"$evts" + :>"$server_evts" ip netns exec "$ns1" ./pm_nl_ctl dsf lip dead:beef:2::1 lport "$sport" rip\ dead:beef:2::2 rport "$client6_port" token "$server6_token" > /dev/null 2>&1 sleep 0.5 - verify_subflow_events "$evts" "$SUB_CLOSED" "$server6_token" "$AF_INET6"\ + verify_subflow_events "$server_evts" "$SUB_CLOSED" "$server6_token" "$AF_INET6"\ "dead:beef:2::1" "dead:beef:2::2" "$client6_port" "23"\ "$client_addr_id" "ns1" "ns2" @@ -623,44 +604,39 @@ test_subflows() listener_pid=$! # ADD_ADDR from client to server machine using a new port - :>"$evts" + :>"$server_evts" ip netns exec "$ns2" ./pm_nl_ctl ann 10.0.2.2 token "$client4_token" id\ $client_addr_id port $new4_port > /dev/null 2>&1 sleep 0.5 # CREATE_SUBFLOW from server to client machine - :>"$evts" + :>"$server_evts" ip netns exec "$ns1" ./pm_nl_ctl csf lip 10.0.2.1 lid 23 rip 10.0.2.2 rport\ $new4_port token "$server4_token" > /dev/null 2>&1 sleep 0.5 - verify_subflow_events "$evts" "$SUB_ESTABLISHED" "$server4_token" "$AF_INET"\ + verify_subflow_events "$server_evts" "$SUB_ESTABLISHED" "$server4_token" "$AF_INET"\ "10.0.2.1" "10.0.2.2" "$new4_port" "23"\ "$client_addr_id" "ns1" "ns2" # Delete the listener from the client ns, if one was created kill_wait $listener_pid - sport=$(sed --unbuffered -n 's/.*\(sport:\)\([[:digit:]]*\).*$/\2/p;q' "$evts") + sport=$(sed --unbuffered -n 's/.*\(sport:\)\([[:digit:]]*\).*$/\2/p;q' "$server_evts") # DESTROY_SUBFLOW from server to client machine - :>"$evts" + :>"$server_evts" ip netns exec "$ns1" ./pm_nl_ctl dsf lip 10.0.2.1 lport "$sport" rip 10.0.2.2 rport\ $new4_port token "$server4_token" > /dev/null 2>&1 sleep 0.5 - verify_subflow_events "$evts" "$SUB_CLOSED" "$server4_token" "$AF_INET" "10.0.2.1"\ + verify_subflow_events "$server_evts" "$SUB_CLOSED" "$server4_token" "$AF_INET" "10.0.2.1"\ "10.0.2.2" "$new4_port" "23" "$client_addr_id" "ns1" "ns2" # RM_ADDR from client to server machine ip netns exec "$ns2" ./pm_nl_ctl rem id $client_addr_id token\ "$client4_token" > /dev/null 2>&1 - kill_wait $evts_pid - # Capture events on the network namespace running the client - :>"$evts" - ip netns exec "$ns2" ./pm_nl_ctl events >> "$evts" 2>&1 & - evts_pid=$! - sleep 0.5 + :>"$client_evts" # Attempt to add a listener at 10.0.2.1: ip netns exec "$ns1" ./pm_nl_ctl listen 10.0.2.1\ @@ -673,24 +649,24 @@ test_subflows() sleep 0.5 # CREATE_SUBFLOW from client to server machine - :>"$evts" + :>"$client_evts" ip netns exec "$ns2" ./pm_nl_ctl csf lip 10.0.2.2 lid 23 rip 10.0.2.1 rport\ $app4_port token "$client4_token" > /dev/null 2>&1 sleep 0.5 - verify_subflow_events "$evts" "$SUB_ESTABLISHED" "$client4_token" "$AF_INET" "10.0.2.2"\ + verify_subflow_events $client_evts $SUB_ESTABLISHED $client4_token $AF_INET "10.0.2.2"\ "10.0.2.1" "$app4_port" "23" "$server_addr_id" "ns2" "ns1" # Delete the listener from the server ns, if one was created kill_wait $listener_pid - sport=$(sed --unbuffered -n 's/.*\(sport:\)\([[:digit:]]*\).*$/\2/p;q' "$evts") + sport=$(sed --unbuffered -n 's/.*\(sport:\)\([[:digit:]]*\).*$/\2/p;q' "$client_evts") # DESTROY_SUBFLOW from client to server machine - :>"$evts" + :>"$client_evts" ip netns exec "$ns2" ./pm_nl_ctl dsf lip 10.0.2.2 lport "$sport" rip 10.0.2.1 rport\ $app4_port token "$client4_token" > /dev/null 2>&1 sleep 0.5 - verify_subflow_events "$evts" "$SUB_CLOSED" "$client4_token" "$AF_INET" "10.0.2.2"\ + verify_subflow_events "$client_evts" "$SUB_CLOSED" "$client4_token" "$AF_INET" "10.0.2.2"\ "10.0.2.1" "$app4_port" "23" "$server_addr_id" "ns2" "ns1" # RM_ADDR from server to client machine @@ -704,17 +680,17 @@ test_subflows() listener_pid=$! # ADD_ADDR6 from server to client machine reusing the subflow port - :>"$evts" + :>"$client_evts" ip netns exec "$ns1" ./pm_nl_ctl ann dead:beef:2::1 token "$server6_token" id\ $server_addr_id > /dev/null 2>&1 sleep 0.5 # CREATE_SUBFLOW6 from client to server machine - :>"$evts" + :>"$client_evts" ip netns exec "$ns2" ./pm_nl_ctl csf lip dead:beef:2::2 lid 23 rip\ dead:beef:2::1 rport $app6_port token "$client6_token" > /dev/null 2>&1 sleep 0.5 - verify_subflow_events "$evts" "$SUB_ESTABLISHED" "$client6_token"\ + verify_subflow_events "$client_evts" "$SUB_ESTABLISHED" "$client6_token"\ "$AF_INET6" "dead:beef:2::2"\ "dead:beef:2::1" "$app6_port" "23"\ "$server_addr_id" "ns2" "ns1" @@ -722,14 +698,14 @@ test_subflows() # Delete the listener from the server ns, if one was created kill_wait $listener_pid - sport=$(sed --unbuffered -n 's/.*\(sport:\)\([[:digit:]]*\).*$/\2/p;q' "$evts") + sport=$(sed --unbuffered -n 's/.*\(sport:\)\([[:digit:]]*\).*$/\2/p;q' "$client_evts") # DESTROY_SUBFLOW6 from client to server machine - :>"$evts" + :>"$client_evts" ip netns exec "$ns2" ./pm_nl_ctl dsf lip dead:beef:2::2 lport "$sport" rip\ dead:beef:2::1 rport $app6_port token "$client6_token" > /dev/null 2>&1 sleep 0.5 - verify_subflow_events "$evts" "$SUB_CLOSED" "$client6_token" "$AF_INET6" "dead:beef:2::2"\ + verify_subflow_events $client_evts $SUB_CLOSED $client6_token $AF_INET6 "dead:beef:2::2"\ "dead:beef:2::1" "$app6_port" "23" "$server_addr_id" "ns2" "ns1" # RM_ADDR6 from server to client machine @@ -743,38 +719,35 @@ test_subflows() listener_pid=$! # ADD_ADDR from server to client machine using a new port - :>"$evts" + :>"$client_evts" ip netns exec "$ns1" ./pm_nl_ctl ann 10.0.2.1 token "$server4_token" id\ $server_addr_id port $new4_port > /dev/null 2>&1 sleep 0.5 # CREATE_SUBFLOW from client to server machine - :>"$evts" + :>"$client_evts" ip netns exec "$ns2" ./pm_nl_ctl csf lip 10.0.2.2 lid 23 rip 10.0.2.1 rport\ $new4_port token "$client4_token" > /dev/null 2>&1 sleep 0.5 - verify_subflow_events "$evts" "$SUB_ESTABLISHED" "$client4_token" "$AF_INET"\ + verify_subflow_events "$client_evts" "$SUB_ESTABLISHED" "$client4_token" "$AF_INET"\ "10.0.2.2" "10.0.2.1" "$new4_port" "23" "$server_addr_id" "ns2" "ns1" # Delete the listener from the server ns, if one was created kill_wait $listener_pid - sport=$(sed --unbuffered -n 's/.*\(sport:\)\([[:digit:]]*\).*$/\2/p;q' "$evts") + sport=$(sed --unbuffered -n 's/.*\(sport:\)\([[:digit:]]*\).*$/\2/p;q' "$client_evts") # DESTROY_SUBFLOW from client to server machine - :>"$evts" + :>"$client_evts" ip netns exec "$ns2" ./pm_nl_ctl dsf lip 10.0.2.2 lport "$sport" rip 10.0.2.1 rport\ $new4_port token "$client4_token" > /dev/null 2>&1 sleep 0.5 - verify_subflow_events "$evts" "$SUB_CLOSED" "$client4_token" "$AF_INET" "10.0.2.2"\ + verify_subflow_events "$client_evts" "$SUB_CLOSED" "$client4_token" "$AF_INET" "10.0.2.2"\ "10.0.2.1" "$new4_port" "23" "$server_addr_id" "ns2" "ns1" # RM_ADDR from server to client machine ip netns exec "$ns1" ./pm_nl_ctl rem id $server_addr_id token\ "$server4_token" > /dev/null 2>&1 - - kill_wait $evts_pid - rm -f "$evts" } test_prio() From 6c73008aa301b7456b80d0e1416a240815fe947b Mon Sep 17 00:00:00 2001 From: Geliang Tang Date: Wed, 30 Nov 2022 15:06:31 +0100 Subject: [PATCH 1541/2002] selftests: mptcp: listener test for userspace PM This patch adds test coverage for listening sockets created by userspace processes. It adds a new test named test_listener() and a new verifying helper verify_listener_events(). The new output looks like this: CREATE_SUBFLOW 10.0.2.2 (ns2) => 10.0.2.1 (ns1) [OK] DESTROY_SUBFLOW 10.0.2.2 (ns2) => 10.0.2.1 (ns1) [OK] MP_PRIO TX [OK] MP_PRIO RX [OK] CREATE_LISTENER 10.0.2.2:37106 [OK] CLOSE_LISTENER 10.0.2.2:37106 [OK] Signed-off-by: Geliang Tang Reviewed-by: Mat Martineau Signed-off-by: Matthieu Baerts Signed-off-by: Jakub Kicinski --- .../selftests/net/mptcp/userspace_pm.sh | 76 +++++++++++++++++++ 1 file changed, 76 insertions(+) diff --git a/tools/testing/selftests/net/mptcp/userspace_pm.sh b/tools/testing/selftests/net/mptcp/userspace_pm.sh index 91d6f30e2fe4..a29deb9fa024 100755 --- a/tools/testing/selftests/net/mptcp/userspace_pm.sh +++ b/tools/testing/selftests/net/mptcp/userspace_pm.sh @@ -11,6 +11,8 @@ ANNOUNCED=6 # MPTCP_EVENT_ANNOUNCED REMOVED=7 # MPTCP_EVENT_REMOVED SUB_ESTABLISHED=10 # MPTCP_EVENT_SUB_ESTABLISHED SUB_CLOSED=11 # MPTCP_EVENT_SUB_CLOSED +LISTENER_CREATED=15 #MPTCP_EVENT_LISTENER_CREATED +LISTENER_CLOSED=16 #MPTCP_EVENT_LISTENER_CLOSED AF_INET=2 AF_INET6=10 @@ -781,11 +783,85 @@ test_prio() fi } +verify_listener_events() +{ + local evt=$1 + local e_type=$2 + local e_family=$3 + local e_saddr=$4 + local e_sport=$5 + local type + local family + local saddr + local sport + + if [ $e_type = $LISTENER_CREATED ]; then + stdbuf -o0 -e0 printf "CREATE_LISTENER %s:%s\t\t\t\t\t"\ + $e_saddr $e_sport + elif [ $e_type = $LISTENER_CLOSED ]; then + stdbuf -o0 -e0 printf "CLOSE_LISTENER %s:%s\t\t\t\t\t"\ + $e_saddr $e_sport + fi + + type=$(grep "type:$e_type," $evt | + sed --unbuffered -n 's/.*\(type:\)\([[:digit:]]*\).*$/\2/p;q') + family=$(grep "type:$e_type," $evt | + sed --unbuffered -n 's/.*\(family:\)\([[:digit:]]*\).*$/\2/p;q') + sport=$(grep "type:$e_type," $evt | + sed --unbuffered -n 's/.*\(sport:\)\([[:digit:]]*\).*$/\2/p;q') + if [ $family ] && [ $family = $AF_INET6 ]; then + saddr=$(grep "type:$e_type," $evt | + sed --unbuffered -n 's/.*\(saddr6:\)\([0-9a-f:.]*\).*$/\2/p;q') + else + saddr=$(grep "type:$e_type," $evt | + sed --unbuffered -n 's/.*\(saddr4:\)\([0-9.]*\).*$/\2/p;q') + fi + + if [ $type ] && [ $type = $e_type ] && + [ $family ] && [ $family = $e_family ] && + [ $saddr ] && [ $saddr = $e_saddr ] && + [ $sport ] && [ $sport = $e_sport ]; then + stdbuf -o0 -e0 printf "[OK]\n" + return 0 + fi + stdbuf -o0 -e0 printf "[FAIL]\n" + exit 1 +} + +test_listener() +{ + # Capture events on the network namespace running the client + :>$client_evts + + # Attempt to add a listener at 10.0.2.2: + ip netns exec $ns2 ./pm_nl_ctl listen 10.0.2.2\ + $client4_port > /dev/null 2>&1 & + local listener_pid=$! + + verify_listener_events $client_evts $LISTENER_CREATED $AF_INET 10.0.2.2 $client4_port + + # ADD_ADDR from client to server machine reusing the subflow port + ip netns exec $ns2 ./pm_nl_ctl ann 10.0.2.2 token $client4_token id\ + $client_addr_id > /dev/null 2>&1 + sleep 0.5 + + # CREATE_SUBFLOW from server to client machine + ip netns exec $ns1 ./pm_nl_ctl csf lip 10.0.2.1 lid 23 rip 10.0.2.2\ + rport $client4_port token $server4_token > /dev/null 2>&1 + sleep 0.5 + + # Delete the listener from the client ns, if one was created + kill_wait $listener_pid + + verify_listener_events $client_evts $LISTENER_CLOSED $AF_INET 10.0.2.2 $client4_port +} + make_connection make_connection "v6" test_announce test_remove test_subflows test_prio +test_listener exit 0 From a3735625572d8f9cac3eb12a43c869d97ccbf584 Mon Sep 17 00:00:00 2001 From: Geliang Tang Date: Wed, 30 Nov 2022 15:06:32 +0100 Subject: [PATCH 1542/2002] selftests: mptcp: make evts global in mptcp_join This patch moves evts_ns1 and evts_ns2 out of do_transfer() as two global variables in mptcp_join.sh. Init them in init() and remove them in cleanup(). Add a new helper reset_with_events() to save the outputs of 'pm_nl_ctl events' command in them. And a new helper kill_events_pids() to kill pids of 'pm_nl_ctl events' command. Use these helpers in userspace pm tests. Suggested-by: Paolo Abeni Signed-off-by: Geliang Tang Acked-by: Paolo Abeni Signed-off-by: Matthieu Baerts Signed-off-by: Jakub Kicinski --- .../testing/selftests/net/mptcp/mptcp_join.sh | 52 +++++++++++-------- 1 file changed, 29 insertions(+), 23 deletions(-) diff --git a/tools/testing/selftests/net/mptcp/mptcp_join.sh b/tools/testing/selftests/net/mptcp/mptcp_join.sh index f10ef65a7009..32a3694c57fb 100755 --- a/tools/testing/selftests/net/mptcp/mptcp_join.sh +++ b/tools/testing/selftests/net/mptcp/mptcp_join.sh @@ -26,6 +26,10 @@ ip_mptcp=0 check_invert=0 validate_checksum=0 init=0 +evts_ns1="" +evts_ns2="" +evts_ns1_pid=0 +evts_ns2_pid=0 declare -A all_tests declare -a only_tests_ids @@ -154,6 +158,8 @@ init() { cin=$(mktemp) cinsent=$(mktemp) cout=$(mktemp) + evts_ns1=$(mktemp) + evts_ns2=$(mktemp) trap cleanup EXIT @@ -165,6 +171,7 @@ cleanup() { rm -f "$cin" "$cout" "$sinfail" rm -f "$sin" "$sout" "$cinsent" "$cinfail" + rm -rf $evts_ns1 $evts_ns2 cleanup_partial } @@ -320,6 +327,18 @@ reset_with_fail() index 100 || exit 1 } +reset_with_events() +{ + reset "${1}" || return 1 + + :> "$evts_ns1" + :> "$evts_ns2" + ip netns exec $ns1 ./pm_nl_ctl events >> "$evts_ns1" 2>&1 & + evts_ns1_pid=$! + ip netns exec $ns2 ./pm_nl_ctl events >> "$evts_ns2" 2>&1 & + evts_ns2_pid=$! +} + fail_test() { ret=1 @@ -473,6 +492,12 @@ kill_wait() wait $1 2>/dev/null } +kill_events_pids() +{ + kill_wait $evts_ns1_pid + kill_wait $evts_ns2_pid +} + pm_nl_set_limits() { local ns=$1 @@ -673,10 +698,6 @@ do_transfer() local port=$((10000 + TEST_COUNT - 1)) local cappid local userspace_pm=0 - local evts_ns1 - local evts_ns1_pid - local evts_ns2 - local evts_ns2_pid :> "$cout" :> "$sout" @@ -753,17 +774,6 @@ do_transfer() addr_nr_ns2=${addr_nr_ns2:9} fi - if [ $userspace_pm -eq 1 ]; then - evts_ns1=$(mktemp) - evts_ns2=$(mktemp) - :> "$evts_ns1" - :> "$evts_ns2" - ip netns exec ${listener_ns} ./pm_nl_ctl events >> "$evts_ns1" 2>&1 & - evts_ns1_pid=$! - ip netns exec ${connector_ns} ./pm_nl_ctl events >> "$evts_ns2" 2>&1 & - evts_ns2_pid=$! - fi - local local_addr if is_v6 "${connect_addr}"; then local_addr="::" @@ -982,12 +992,6 @@ do_transfer() kill $cappid fi - if [ $userspace_pm -eq 1 ]; then - kill_wait $evts_ns1_pid - kill_wait $evts_ns2_pid - rm -rf $evts_ns1 $evts_ns2 - fi - NSTAT_HISTORY=/tmp/${listener_ns}.nstat ip netns exec ${listener_ns} \ nstat | grep Tcp > /tmp/${listener_ns}.out NSTAT_HISTORY=/tmp/${connector_ns}.nstat ip netns exec ${connector_ns} \ @@ -2961,22 +2965,24 @@ userspace_tests() fi # userspace pm add & remove address - if reset "userspace pm add & remove address"; then + if reset_with_events "userspace pm add & remove address"; then set_userspace_pm $ns1 pm_nl_set_limits $ns2 1 1 run_tests $ns1 $ns2 10.0.1.1 0 userspace_1 0 slow chk_join_nr 1 1 1 chk_add_nr 1 1 chk_rm_nr 1 1 invert + kill_events_pids fi # userspace pm create destroy subflow - if reset "userspace pm create destroy subflow"; then + if reset_with_events "userspace pm create destroy subflow"; then set_userspace_pm $ns2 pm_nl_set_limits $ns1 0 1 run_tests $ns1 $ns2 10.0.1.1 0 0 userspace_1 slow chk_join_nr 1 1 1 chk_rm_nr 0 1 + kill_events_pids fi } From 178d023208ebbc082de91d43a3b7c4c32a3c953f Mon Sep 17 00:00:00 2001 From: Geliang Tang Date: Wed, 30 Nov 2022 15:06:33 +0100 Subject: [PATCH 1543/2002] selftests: mptcp: listener test for in-kernel PM This patch adds test coverage for listening sockets created by the in-kernel path manager in mptcp_join.sh. It adds the listener event checking in the existing "remove single address with port" test. The output looks like this: 003 remove single address with port syn[ ok ] - synack[ ok ] - ack[ ok ] add[ ok ] - echo [ ok ] - pt [ ok ] syn[ ok ] - synack[ ok ] - ack[ ok ] syn[ ok ] - ack [ ok ] rm [ ok ] - rmsf [ ok ] invert CREATE_LISTENER 10.0.2.1:10100[ ok ] CLOSE_LISTENER 10.0.2.1:10100 [ ok ] Signed-off-by: Geliang Tang Reviewed-by: Mat Martineau Signed-off-by: Matthieu Baerts Signed-off-by: Jakub Kicinski --- .../testing/selftests/net/mptcp/mptcp_join.sh | 58 ++++++++++++++++++- 1 file changed, 57 insertions(+), 1 deletion(-) diff --git a/tools/testing/selftests/net/mptcp/mptcp_join.sh b/tools/testing/selftests/net/mptcp/mptcp_join.sh index 32a3694c57fb..d11d3d566608 100755 --- a/tools/testing/selftests/net/mptcp/mptcp_join.sh +++ b/tools/testing/selftests/net/mptcp/mptcp_join.sh @@ -2513,6 +2513,57 @@ backup_tests() fi } +LISTENER_CREATED=15 #MPTCP_EVENT_LISTENER_CREATED +LISTENER_CLOSED=16 #MPTCP_EVENT_LISTENER_CLOSED + +AF_INET=2 +AF_INET6=10 + +verify_listener_events() +{ + local evt=$1 + local e_type=$2 + local e_family=$3 + local e_saddr=$4 + local e_sport=$5 + local type + local family + local saddr + local sport + + if [ $e_type = $LISTENER_CREATED ]; then + stdbuf -o0 -e0 printf "\t\t\t\t\t CREATE_LISTENER %s:%s"\ + $e_saddr $e_sport + elif [ $e_type = $LISTENER_CLOSED ]; then + stdbuf -o0 -e0 printf "\t\t\t\t\t CLOSE_LISTENER %s:%s "\ + $e_saddr $e_sport + fi + + type=$(grep "type:$e_type," $evt | + sed --unbuffered -n 's/.*\(type:\)\([[:digit:]]*\).*$/\2/p;q') + family=$(grep "type:$e_type," $evt | + sed --unbuffered -n 's/.*\(family:\)\([[:digit:]]*\).*$/\2/p;q') + sport=$(grep "type:$e_type," $evt | + sed --unbuffered -n 's/.*\(sport:\)\([[:digit:]]*\).*$/\2/p;q') + if [ $family ] && [ $family = $AF_INET6 ]; then + saddr=$(grep "type:$e_type," $evt | + sed --unbuffered -n 's/.*\(saddr6:\)\([0-9a-f:.]*\).*$/\2/p;q') + else + saddr=$(grep "type:$e_type," $evt | + sed --unbuffered -n 's/.*\(saddr4:\)\([0-9.]*\).*$/\2/p;q') + fi + + if [ $type ] && [ $type = $e_type ] && + [ $family ] && [ $family = $e_family ] && + [ $saddr ] && [ $saddr = $e_saddr ] && + [ $sport ] && [ $sport = $e_sport ]; then + stdbuf -o0 -e0 printf "[ ok ]\n" + return 0 + fi + fail_test + stdbuf -o0 -e0 printf "[fail]\n" +} + add_addr_ports_tests() { # signal address with port @@ -2537,7 +2588,8 @@ add_addr_ports_tests() fi # single address with port, remove - if reset "remove single address with port"; then + # pm listener events + if reset_with_events "remove single address with port"; then pm_nl_set_limits $ns1 0 1 pm_nl_add_endpoint $ns1 10.0.2.1 flags signal port 10100 pm_nl_set_limits $ns2 1 1 @@ -2545,6 +2597,10 @@ add_addr_ports_tests() chk_join_nr 1 1 1 chk_add_nr 1 1 1 chk_rm_nr 1 1 invert + + verify_listener_events $evts_ns1 $LISTENER_CREATED $AF_INET 10.0.2.1 10100 + verify_listener_events $evts_ns1 $LISTENER_CLOSED $AF_INET 10.0.2.1 10100 + kill_events_pids fi # subflow and signal with port, remove From 7d802c8098c50fb7dcf5dfcb6466482e1f2b15e4 Mon Sep 17 00:00:00 2001 From: Xin Long Date: Wed, 30 Nov 2022 18:04:31 -0500 Subject: [PATCH 1544/2002] sctp: delete free member from struct sctp_sched_ops After commit 9ed7bfc79542 ("sctp: fix memory leak in sctp_stream_outq_migrate()"), sctp_sched_set_sched() is the only place calling sched->free(), and it can actually be replaced by sched->free_sid() on each stream, and yet there's already a loop to traverse all streams in sctp_sched_set_sched(). This patch adds a function sctp_sched_free_sched() where it calls sched->free_sid() for each stream to replace sched->free() calls in sctp_sched_set_sched() and then deletes the unused free member from struct sctp_sched_ops. Signed-off-by: Xin Long Acked-by: Marcelo Ricardo Leitner Link: https://lore.kernel.org/r/e10aac150aca2686cb0bd0570299ec716da5a5c0.1669849471.git.lucien.xin@gmail.com Signed-off-by: Jakub Kicinski --- include/net/sctp/stream_sched.h | 2 -- net/sctp/stream_sched.c | 38 +++++++++++++++++---------------- net/sctp/stream_sched_prio.c | 27 ----------------------- net/sctp/stream_sched_rr.c | 6 ------ 4 files changed, 20 insertions(+), 53 deletions(-) diff --git a/include/net/sctp/stream_sched.h b/include/net/sctp/stream_sched.h index 65058faea4db..fa00dc20a0d7 100644 --- a/include/net/sctp/stream_sched.h +++ b/include/net/sctp/stream_sched.h @@ -28,8 +28,6 @@ struct sctp_sched_ops { int (*init_sid)(struct sctp_stream *stream, __u16 sid, gfp_t gfp); /* free a stream */ void (*free_sid)(struct sctp_stream *stream, __u16 sid); - /* Frees the entire thing */ - void (*free)(struct sctp_stream *stream); /* Enqueue a chunk */ void (*enqueue)(struct sctp_outq *q, struct sctp_datamsg *msg); diff --git a/net/sctp/stream_sched.c b/net/sctp/stream_sched.c index 7c8f9d89e16a..330067002deb 100644 --- a/net/sctp/stream_sched.c +++ b/net/sctp/stream_sched.c @@ -50,10 +50,6 @@ static void sctp_sched_fcfs_free_sid(struct sctp_stream *stream, __u16 sid) { } -static void sctp_sched_fcfs_free(struct sctp_stream *stream) -{ -} - static void sctp_sched_fcfs_enqueue(struct sctp_outq *q, struct sctp_datamsg *msg) { @@ -101,7 +97,6 @@ static struct sctp_sched_ops sctp_sched_fcfs = { .init = sctp_sched_fcfs_init, .init_sid = sctp_sched_fcfs_init_sid, .free_sid = sctp_sched_fcfs_free_sid, - .free = sctp_sched_fcfs_free, .enqueue = sctp_sched_fcfs_enqueue, .dequeue = sctp_sched_fcfs_dequeue, .dequeue_done = sctp_sched_fcfs_dequeue_done, @@ -131,6 +126,23 @@ void sctp_sched_ops_init(void) sctp_sched_ops_rr_init(); } +static void sctp_sched_free_sched(struct sctp_stream *stream) +{ + struct sctp_sched_ops *sched = sctp_sched_ops_from_stream(stream); + struct sctp_stream_out_ext *soute; + int i; + + sched->unsched_all(stream); + for (i = 0; i < stream->outcnt; i++) { + soute = SCTP_SO(stream, i)->ext; + if (!soute) + continue; + sched->free_sid(stream, i); + /* Give the next scheduler a clean slate. */ + memset_after(soute, 0, outq); + } +} + int sctp_sched_set_sched(struct sctp_association *asoc, enum sctp_sched_type sched) { @@ -146,18 +158,8 @@ int sctp_sched_set_sched(struct sctp_association *asoc, if (sched > SCTP_SS_MAX) return -EINVAL; - if (old) { - old->free(&asoc->stream); - - /* Give the next scheduler a clean slate. */ - for (i = 0; i < asoc->stream.outcnt; i++) { - struct sctp_stream_out_ext *ext = SCTP_SO(&asoc->stream, i)->ext; - - if (!ext) - continue; - memset_after(ext, 0, outq); - } - } + if (old) + sctp_sched_free_sched(&asoc->stream); asoc->outqueue.sched = n; n->init(&asoc->stream); @@ -181,7 +183,7 @@ int sctp_sched_set_sched(struct sctp_association *asoc, return ret; err: - n->free(&asoc->stream); + sctp_sched_free_sched(&asoc->stream); asoc->outqueue.sched = &sctp_sched_fcfs; /* Always safe */ return ret; diff --git a/net/sctp/stream_sched_prio.c b/net/sctp/stream_sched_prio.c index 4fc9f2923ed1..42d4800f263d 100644 --- a/net/sctp/stream_sched_prio.c +++ b/net/sctp/stream_sched_prio.c @@ -222,32 +222,6 @@ static void sctp_sched_prio_free_sid(struct sctp_stream *stream, __u16 sid) kfree(prio); } -static void sctp_sched_prio_free(struct sctp_stream *stream) -{ - struct sctp_stream_priorities *prio, *n; - LIST_HEAD(list); - int i; - - /* As we don't keep a list of priorities, to avoid multiple - * frees we have to do it in 3 steps: - * 1. unsched everyone, so the lists are free to use in 2. - * 2. build the list of the priorities - * 3. free the list - */ - sctp_sched_prio_unsched_all(stream); - for (i = 0; i < stream->outcnt; i++) { - if (!SCTP_SO(stream, i)->ext) - continue; - prio = SCTP_SO(stream, i)->ext->prio_head; - if (prio && list_empty(&prio->prio_sched)) - list_add(&prio->prio_sched, &list); - } - list_for_each_entry_safe(prio, n, &list, prio_sched) { - list_del_init(&prio->prio_sched); - kfree(prio); - } -} - static void sctp_sched_prio_enqueue(struct sctp_outq *q, struct sctp_datamsg *msg) { @@ -342,7 +316,6 @@ static struct sctp_sched_ops sctp_sched_prio = { .init = sctp_sched_prio_init, .init_sid = sctp_sched_prio_init_sid, .free_sid = sctp_sched_prio_free_sid, - .free = sctp_sched_prio_free, .enqueue = sctp_sched_prio_enqueue, .dequeue = sctp_sched_prio_dequeue, .dequeue_done = sctp_sched_prio_dequeue_done, diff --git a/net/sctp/stream_sched_rr.c b/net/sctp/stream_sched_rr.c index cc444fe0d67c..1f235e7f643a 100644 --- a/net/sctp/stream_sched_rr.c +++ b/net/sctp/stream_sched_rr.c @@ -94,11 +94,6 @@ static void sctp_sched_rr_free_sid(struct sctp_stream *stream, __u16 sid) { } -static void sctp_sched_rr_free(struct sctp_stream *stream) -{ - sctp_sched_rr_unsched_all(stream); -} - static void sctp_sched_rr_enqueue(struct sctp_outq *q, struct sctp_datamsg *msg) { @@ -182,7 +177,6 @@ static struct sctp_sched_ops sctp_sched_rr = { .init = sctp_sched_rr_init, .init_sid = sctp_sched_rr_init_sid, .free_sid = sctp_sched_rr_free_sid, - .free = sctp_sched_rr_free, .enqueue = sctp_sched_rr_enqueue, .dequeue = sctp_sched_rr_dequeue, .dequeue_done = sctp_sched_rr_dequeue_done, From e012764cebf6e33097f6833ff15a936fbe7b846c Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Tue, 29 Nov 2022 17:48:08 +0100 Subject: [PATCH 1545/2002] Revert "net: hsr: use hlist_head instead of list_head for mac addresses" The hlist optimisation (which not only uses hlist_head instead of list_head but also splits hsr_priv::node_db into an array of 256 slots) does not consider the "node merge": Upon starting the hsr network (with three nodes) a packet that is sent from node1 to node3 will also be sent from node1 to node2 and then forwarded to node3. As a result node3 will receive 2 packets because it is not able to filter out the duplicate. Each packet received will create a new struct hsr_node with macaddress_A only set the MAC address it received from (the two MAC addesses from node1). At some point (early in the process) two supervision frames will be received from node1. They will be processed by hsr_handle_sup_frame() and one frame will leave early ("Node has already been merged") and does nothing. The other frame will be merged as portB and have its MAC address written to macaddress_B and the hsr_node (that was created for it as macaddress_A) will be removed. From now on HSR is able to identify a duplicate because both packets sent from one node will result in the same struct hsr_node because hsr_get_node() will find the MAC address either on macaddress_A or macaddress_B. Things get tricky with the optimisation: If sender's MAC address is saved as macaddress_A then the lookup will work as usual. If the MAC address has been merged into macaddress_B of another hsr_node then the lookup won't work because it is likely that the data structure is in another bucket. This results in creating a new struct hsr_node and not recognising a possible duplicate. A way around it would be to add another hsr_node::mac_list_B and attach it to the other bucket to ensure that this hsr_node will be looked up either via macaddress_A _or_ macaddress_B. I however prefer to revert it because it sounds like an academic problem rather than real life workload plus it adds complexity. I'm not an HSR expert with what is usual size of a network but I would guess 40 to 60 nodes. With 10.000 nodes and assuming 60us for pass-through (from node to node) then it would take almost 600ms for a packet to almost wrap around which sounds a lot. Revert the hash MAC addresses optimisation. Fixes: 4acc45db71158 ("net: hsr: use hlist_head instead of list_head for mac addresses") Cc: Juhee Kang Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Jakub Kicinski --- net/hsr/hsr_debugfs.c | 38 +++----- net/hsr/hsr_device.c | 10 +- net/hsr/hsr_forward.c | 7 +- net/hsr/hsr_framereg.c | 201 +++++++++++++++-------------------------- net/hsr/hsr_framereg.h | 14 +-- net/hsr/hsr_main.h | 9 +- net/hsr/hsr_netlink.c | 4 +- 7 files changed, 101 insertions(+), 182 deletions(-) diff --git a/net/hsr/hsr_debugfs.c b/net/hsr/hsr_debugfs.c index de476a417631..1a195efc79cd 100644 --- a/net/hsr/hsr_debugfs.c +++ b/net/hsr/hsr_debugfs.c @@ -9,7 +9,6 @@ #include #include #include -#include #include "hsr_main.h" #include "hsr_framereg.h" @@ -21,7 +20,6 @@ hsr_node_table_show(struct seq_file *sfp, void *data) { struct hsr_priv *priv = (struct hsr_priv *)sfp->private; struct hsr_node *node; - int i; seq_printf(sfp, "Node Table entries for (%s) device\n", (priv->prot_version == PRP_V1 ? "PRP" : "HSR")); @@ -33,28 +31,22 @@ hsr_node_table_show(struct seq_file *sfp, void *data) seq_puts(sfp, "DAN-H\n"); rcu_read_lock(); + list_for_each_entry_rcu(node, &priv->node_db, mac_list) { + /* skip self node */ + if (hsr_addr_is_self(priv, node->macaddress_A)) + continue; + seq_printf(sfp, "%pM ", &node->macaddress_A[0]); + seq_printf(sfp, "%pM ", &node->macaddress_B[0]); + seq_printf(sfp, "%10lx, ", node->time_in[HSR_PT_SLAVE_A]); + seq_printf(sfp, "%10lx, ", node->time_in[HSR_PT_SLAVE_B]); + seq_printf(sfp, "%14x, ", node->addr_B_port); - for (i = 0 ; i < priv->hash_buckets; i++) { - hlist_for_each_entry_rcu(node, &priv->node_db[i], mac_list) { - /* skip self node */ - if (hsr_addr_is_self(priv, node->macaddress_A)) - continue; - seq_printf(sfp, "%pM ", &node->macaddress_A[0]); - seq_printf(sfp, "%pM ", &node->macaddress_B[0]); - seq_printf(sfp, "%10lx, ", - node->time_in[HSR_PT_SLAVE_A]); - seq_printf(sfp, "%10lx, ", - node->time_in[HSR_PT_SLAVE_B]); - seq_printf(sfp, "%14x, ", node->addr_B_port); - - if (priv->prot_version == PRP_V1) - seq_printf(sfp, "%5x, %5x, %5x\n", - node->san_a, node->san_b, - (node->san_a == 0 && - node->san_b == 0)); - else - seq_printf(sfp, "%5x\n", 1); - } + if (priv->prot_version == PRP_V1) + seq_printf(sfp, "%5x, %5x, %5x\n", + node->san_a, node->san_b, + (node->san_a == 0 && node->san_b == 0)); + else + seq_printf(sfp, "%5x\n", 1); } rcu_read_unlock(); return 0; diff --git a/net/hsr/hsr_device.c b/net/hsr/hsr_device.c index 6ffef47e9be5..7518f7e93043 100644 --- a/net/hsr/hsr_device.c +++ b/net/hsr/hsr_device.c @@ -485,16 +485,12 @@ int hsr_dev_finalize(struct net_device *hsr_dev, struct net_device *slave[2], { bool unregister = false; struct hsr_priv *hsr; - int res, i; + int res; hsr = netdev_priv(hsr_dev); INIT_LIST_HEAD(&hsr->ports); - INIT_HLIST_HEAD(&hsr->self_node_db); - hsr->hash_buckets = HSR_HSIZE; - get_random_bytes(&hsr->hash_seed, sizeof(hsr->hash_seed)); - for (i = 0; i < hsr->hash_buckets; i++) - INIT_HLIST_HEAD(&hsr->node_db[i]); - + INIT_LIST_HEAD(&hsr->node_db); + INIT_LIST_HEAD(&hsr->self_node_db); spin_lock_init(&hsr->list_lock); eth_hw_addr_set(hsr_dev, slave[0]->dev_addr); diff --git a/net/hsr/hsr_forward.c b/net/hsr/hsr_forward.c index 56bb27d67a2e..3e58d310b4d4 100644 --- a/net/hsr/hsr_forward.c +++ b/net/hsr/hsr_forward.c @@ -571,23 +571,20 @@ static int fill_frame_info(struct hsr_frame_info *frame, struct ethhdr *ethhdr; __be16 proto; int ret; - u32 hash; /* Check if skb contains ethhdr */ if (skb->mac_len < sizeof(struct ethhdr)) return -EINVAL; memset(frame, 0, sizeof(*frame)); - - ethhdr = (struct ethhdr *)skb_mac_header(skb); - hash = hsr_mac_hash(port->hsr, ethhdr->h_source); frame->is_supervision = is_supervision_frame(port->hsr, skb); - frame->node_src = hsr_get_node(port, &hsr->node_db[hash], skb, + frame->node_src = hsr_get_node(port, &hsr->node_db, skb, frame->is_supervision, port->type); if (!frame->node_src) return -1; /* Unknown node and !is_supervision, or no mem */ + ethhdr = (struct ethhdr *)skb_mac_header(skb); frame->is_vlan = false; proto = ethhdr->h_proto; diff --git a/net/hsr/hsr_framereg.c b/net/hsr/hsr_framereg.c index 584e21788799..9b8eaebce254 100644 --- a/net/hsr/hsr_framereg.c +++ b/net/hsr/hsr_framereg.c @@ -15,37 +15,10 @@ #include #include #include -#include #include "hsr_main.h" #include "hsr_framereg.h" #include "hsr_netlink.h" -#ifdef CONFIG_LOCKDEP -int lockdep_hsr_is_held(spinlock_t *lock) -{ - return lockdep_is_held(lock); -} -#endif - -u32 hsr_mac_hash(struct hsr_priv *hsr, const unsigned char *addr) -{ - u32 hash = jhash(addr, ETH_ALEN, hsr->hash_seed); - - return reciprocal_scale(hash, hsr->hash_buckets); -} - -struct hsr_node *hsr_node_get_first(struct hlist_head *head, spinlock_t *lock) -{ - struct hlist_node *first; - - first = rcu_dereference_bh_check(hlist_first_rcu(head), - lockdep_hsr_is_held(lock)); - if (first) - return hlist_entry(first, struct hsr_node, mac_list); - - return NULL; -} - /* seq_nr_after(a, b) - return true if a is after (higher in sequence than) b, * false otherwise. */ @@ -67,7 +40,8 @@ bool hsr_addr_is_self(struct hsr_priv *hsr, unsigned char *addr) { struct hsr_node *node; - node = hsr_node_get_first(&hsr->self_node_db, &hsr->list_lock); + node = list_first_or_null_rcu(&hsr->self_node_db, struct hsr_node, + mac_list); if (!node) { WARN_ONCE(1, "HSR: No self node\n"); return false; @@ -83,12 +57,12 @@ bool hsr_addr_is_self(struct hsr_priv *hsr, unsigned char *addr) /* Search for mac entry. Caller must hold rcu read lock. */ -static struct hsr_node *find_node_by_addr_A(struct hlist_head *node_db, +static struct hsr_node *find_node_by_addr_A(struct list_head *node_db, const unsigned char addr[ETH_ALEN]) { struct hsr_node *node; - hlist_for_each_entry_rcu(node, node_db, mac_list) { + list_for_each_entry_rcu(node, node_db, mac_list) { if (ether_addr_equal(node->macaddress_A, addr)) return node; } @@ -103,7 +77,7 @@ int hsr_create_self_node(struct hsr_priv *hsr, const unsigned char addr_a[ETH_ALEN], const unsigned char addr_b[ETH_ALEN]) { - struct hlist_head *self_node_db = &hsr->self_node_db; + struct list_head *self_node_db = &hsr->self_node_db; struct hsr_node *node, *oldnode; node = kmalloc(sizeof(*node), GFP_KERNEL); @@ -114,13 +88,14 @@ int hsr_create_self_node(struct hsr_priv *hsr, ether_addr_copy(node->macaddress_B, addr_b); spin_lock_bh(&hsr->list_lock); - oldnode = hsr_node_get_first(self_node_db, &hsr->list_lock); + oldnode = list_first_or_null_rcu(self_node_db, + struct hsr_node, mac_list); if (oldnode) { - hlist_replace_rcu(&oldnode->mac_list, &node->mac_list); + list_replace_rcu(&oldnode->mac_list, &node->mac_list); spin_unlock_bh(&hsr->list_lock); kfree_rcu(oldnode, rcu_head); } else { - hlist_add_tail_rcu(&node->mac_list, self_node_db); + list_add_tail_rcu(&node->mac_list, self_node_db); spin_unlock_bh(&hsr->list_lock); } @@ -129,25 +104,25 @@ int hsr_create_self_node(struct hsr_priv *hsr, void hsr_del_self_node(struct hsr_priv *hsr) { - struct hlist_head *self_node_db = &hsr->self_node_db; + struct list_head *self_node_db = &hsr->self_node_db; struct hsr_node *node; spin_lock_bh(&hsr->list_lock); - node = hsr_node_get_first(self_node_db, &hsr->list_lock); + node = list_first_or_null_rcu(self_node_db, struct hsr_node, mac_list); if (node) { - hlist_del_rcu(&node->mac_list); + list_del_rcu(&node->mac_list); kfree_rcu(node, rcu_head); } spin_unlock_bh(&hsr->list_lock); } -void hsr_del_nodes(struct hlist_head *node_db) +void hsr_del_nodes(struct list_head *node_db) { struct hsr_node *node; - struct hlist_node *tmp; + struct hsr_node *tmp; - hlist_for_each_entry_safe(node, tmp, node_db, mac_list) - kfree_rcu(node, rcu_head); + list_for_each_entry_safe(node, tmp, node_db, mac_list) + kfree(node); } void prp_handle_san_frame(bool san, enum hsr_port_type port, @@ -168,7 +143,7 @@ void prp_handle_san_frame(bool san, enum hsr_port_type port, * originating from the newly added node. */ static struct hsr_node *hsr_add_node(struct hsr_priv *hsr, - struct hlist_head *node_db, + struct list_head *node_db, unsigned char addr[], u16 seq_out, bool san, enum hsr_port_type rx_port) @@ -198,14 +173,14 @@ static struct hsr_node *hsr_add_node(struct hsr_priv *hsr, hsr->proto_ops->handle_san_frame(san, rx_port, new_node); spin_lock_bh(&hsr->list_lock); - hlist_for_each_entry_rcu(node, node_db, mac_list, - lockdep_hsr_is_held(&hsr->list_lock)) { + list_for_each_entry_rcu(node, node_db, mac_list, + lockdep_is_held(&hsr->list_lock)) { if (ether_addr_equal(node->macaddress_A, addr)) goto out; if (ether_addr_equal(node->macaddress_B, addr)) goto out; } - hlist_add_tail_rcu(&new_node->mac_list, node_db); + list_add_tail_rcu(&new_node->mac_list, node_db); spin_unlock_bh(&hsr->list_lock); return new_node; out: @@ -225,7 +200,7 @@ void prp_update_san_info(struct hsr_node *node, bool is_sup) /* Get the hsr_node from which 'skb' was sent. */ -struct hsr_node *hsr_get_node(struct hsr_port *port, struct hlist_head *node_db, +struct hsr_node *hsr_get_node(struct hsr_port *port, struct list_head *node_db, struct sk_buff *skb, bool is_sup, enum hsr_port_type rx_port) { @@ -241,7 +216,7 @@ struct hsr_node *hsr_get_node(struct hsr_port *port, struct hlist_head *node_db, ethhdr = (struct ethhdr *)skb_mac_header(skb); - hlist_for_each_entry_rcu(node, node_db, mac_list) { + list_for_each_entry_rcu(node, node_db, mac_list) { if (ether_addr_equal(node->macaddress_A, ethhdr->h_source)) { if (hsr->proto_ops->update_san_info) hsr->proto_ops->update_san_info(node, is_sup); @@ -291,12 +266,11 @@ void hsr_handle_sup_frame(struct hsr_frame_info *frame) struct hsr_sup_tlv *hsr_sup_tlv; struct hsr_node *node_real; struct sk_buff *skb = NULL; - struct hlist_head *node_db; + struct list_head *node_db; struct ethhdr *ethhdr; int i; unsigned int pull_size = 0; unsigned int total_pull_size = 0; - u32 hash; /* Here either frame->skb_hsr or frame->skb_prp should be * valid as supervision frame always will have protocol @@ -334,13 +308,11 @@ void hsr_handle_sup_frame(struct hsr_frame_info *frame) hsr_sp = (struct hsr_sup_payload *)skb->data; /* Merge node_curr (registered on macaddress_B) into node_real */ - node_db = port_rcv->hsr->node_db; - hash = hsr_mac_hash(hsr, hsr_sp->macaddress_A); - node_real = find_node_by_addr_A(&node_db[hash], hsr_sp->macaddress_A); + node_db = &port_rcv->hsr->node_db; + node_real = find_node_by_addr_A(node_db, hsr_sp->macaddress_A); if (!node_real) /* No frame received from AddrA of this node yet */ - node_real = hsr_add_node(hsr, &node_db[hash], - hsr_sp->macaddress_A, + node_real = hsr_add_node(hsr, node_db, hsr_sp->macaddress_A, HSR_SEQNR_START - 1, true, port_rcv->type); if (!node_real) @@ -374,8 +346,7 @@ void hsr_handle_sup_frame(struct hsr_frame_info *frame) hsr_sp = (struct hsr_sup_payload *)skb->data; /* Check if redbox mac and node mac are equal. */ - if (!ether_addr_equal(node_real->macaddress_A, - hsr_sp->macaddress_A)) { + if (!ether_addr_equal(node_real->macaddress_A, hsr_sp->macaddress_A)) { /* This is a redbox supervision frame for a VDAN! */ goto done; } @@ -395,7 +366,7 @@ void hsr_handle_sup_frame(struct hsr_frame_info *frame) node_real->addr_B_port = port_rcv->type; spin_lock_bh(&hsr->list_lock); - hlist_del_rcu(&node_curr->mac_list); + list_del_rcu(&node_curr->mac_list); spin_unlock_bh(&hsr->list_lock); kfree_rcu(node_curr, rcu_head); @@ -433,7 +404,6 @@ void hsr_addr_subst_dest(struct hsr_node *node_src, struct sk_buff *skb, struct hsr_port *port) { struct hsr_node *node_dst; - u32 hash; if (!skb_mac_header_was_set(skb)) { WARN_ONCE(1, "%s: Mac header not set\n", __func__); @@ -443,8 +413,7 @@ void hsr_addr_subst_dest(struct hsr_node *node_src, struct sk_buff *skb, if (!is_unicast_ether_addr(eth_hdr(skb)->h_dest)) return; - hash = hsr_mac_hash(port->hsr, eth_hdr(skb)->h_dest); - node_dst = find_node_by_addr_A(&port->hsr->node_db[hash], + node_dst = find_node_by_addr_A(&port->hsr->node_db, eth_hdr(skb)->h_dest); if (!node_dst) { if (net_ratelimit()) @@ -520,73 +489,59 @@ static struct hsr_port *get_late_port(struct hsr_priv *hsr, void hsr_prune_nodes(struct timer_list *t) { struct hsr_priv *hsr = from_timer(hsr, t, prune_timer); - struct hlist_node *tmp; struct hsr_node *node; + struct hsr_node *tmp; struct hsr_port *port; unsigned long timestamp; unsigned long time_a, time_b; - int i; spin_lock_bh(&hsr->list_lock); + list_for_each_entry_safe(node, tmp, &hsr->node_db, mac_list) { + /* Don't prune own node. Neither time_in[HSR_PT_SLAVE_A] + * nor time_in[HSR_PT_SLAVE_B], will ever be updated for + * the master port. Thus the master node will be repeatedly + * pruned leading to packet loss. + */ + if (hsr_addr_is_self(hsr, node->macaddress_A)) + continue; - for (i = 0; i < hsr->hash_buckets; i++) { - hlist_for_each_entry_safe(node, tmp, &hsr->node_db[i], - mac_list) { - /* Don't prune own node. - * Neither time_in[HSR_PT_SLAVE_A] - * nor time_in[HSR_PT_SLAVE_B], will ever be updated - * for the master port. Thus the master node will be - * repeatedly pruned leading to packet loss. - */ - if (hsr_addr_is_self(hsr, node->macaddress_A)) - continue; + /* Shorthand */ + time_a = node->time_in[HSR_PT_SLAVE_A]; + time_b = node->time_in[HSR_PT_SLAVE_B]; - /* Shorthand */ - time_a = node->time_in[HSR_PT_SLAVE_A]; - time_b = node->time_in[HSR_PT_SLAVE_B]; + /* Check for timestamps old enough to risk wrap-around */ + if (time_after(jiffies, time_a + MAX_JIFFY_OFFSET / 2)) + node->time_in_stale[HSR_PT_SLAVE_A] = true; + if (time_after(jiffies, time_b + MAX_JIFFY_OFFSET / 2)) + node->time_in_stale[HSR_PT_SLAVE_B] = true; - /* Check for timestamps old enough to - * risk wrap-around - */ - if (time_after(jiffies, time_a + MAX_JIFFY_OFFSET / 2)) - node->time_in_stale[HSR_PT_SLAVE_A] = true; - if (time_after(jiffies, time_b + MAX_JIFFY_OFFSET / 2)) - node->time_in_stale[HSR_PT_SLAVE_B] = true; + /* Get age of newest frame from node. + * At least one time_in is OK here; nodes get pruned long + * before both time_ins can get stale + */ + timestamp = time_a; + if (node->time_in_stale[HSR_PT_SLAVE_A] || + (!node->time_in_stale[HSR_PT_SLAVE_B] && + time_after(time_b, time_a))) + timestamp = time_b; - /* Get age of newest frame from node. - * At least one time_in is OK here; nodes get pruned - * long before both time_ins can get stale - */ - timestamp = time_a; - if (node->time_in_stale[HSR_PT_SLAVE_A] || - (!node->time_in_stale[HSR_PT_SLAVE_B] && - time_after(time_b, time_a))) - timestamp = time_b; + /* Warn of ring error only as long as we get frames at all */ + if (time_is_after_jiffies(timestamp + + msecs_to_jiffies(1.5 * MAX_SLAVE_DIFF))) { + rcu_read_lock(); + port = get_late_port(hsr, node); + if (port) + hsr_nl_ringerror(hsr, node->macaddress_A, port); + rcu_read_unlock(); + } - /* Warn of ring error only as long as we get - * frames at all - */ - if (time_is_after_jiffies(timestamp + - msecs_to_jiffies(1.5 * MAX_SLAVE_DIFF))) { - rcu_read_lock(); - port = get_late_port(hsr, node); - if (port) - hsr_nl_ringerror(hsr, - node->macaddress_A, - port); - rcu_read_unlock(); - } - - /* Prune old entries */ - if (time_is_before_jiffies(timestamp + - msecs_to_jiffies(HSR_NODE_FORGET_TIME))) { - hsr_nl_nodedown(hsr, node->macaddress_A); - hlist_del_rcu(&node->mac_list); - /* Note that we need to free this - * entry later: - */ - kfree_rcu(node, rcu_head); - } + /* Prune old entries */ + if (time_is_before_jiffies(timestamp + + msecs_to_jiffies(HSR_NODE_FORGET_TIME))) { + hsr_nl_nodedown(hsr, node->macaddress_A); + list_del_rcu(&node->mac_list); + /* Note that we need to free this entry later: */ + kfree_rcu(node, rcu_head); } } spin_unlock_bh(&hsr->list_lock); @@ -600,20 +555,17 @@ void *hsr_get_next_node(struct hsr_priv *hsr, void *_pos, unsigned char addr[ETH_ALEN]) { struct hsr_node *node; - u32 hash; - - hash = hsr_mac_hash(hsr, addr); if (!_pos) { - node = hsr_node_get_first(&hsr->node_db[hash], - &hsr->list_lock); + node = list_first_or_null_rcu(&hsr->node_db, + struct hsr_node, mac_list); if (node) ether_addr_copy(addr, node->macaddress_A); return node; } node = _pos; - hlist_for_each_entry_continue_rcu(node, mac_list) { + list_for_each_entry_continue_rcu(node, &hsr->node_db, mac_list) { ether_addr_copy(addr, node->macaddress_A); return node; } @@ -633,11 +585,8 @@ int hsr_get_node_data(struct hsr_priv *hsr, struct hsr_node *node; struct hsr_port *port; unsigned long tdiff; - u32 hash; - hash = hsr_mac_hash(hsr, addr); - - node = find_node_by_addr_A(&hsr->node_db[hash], addr); + node = find_node_by_addr_A(&hsr->node_db, addr); if (!node) return -ENOENT; diff --git a/net/hsr/hsr_framereg.h b/net/hsr/hsr_framereg.h index f3762e9e42b5..bdbb8c822ba1 100644 --- a/net/hsr/hsr_framereg.h +++ b/net/hsr/hsr_framereg.h @@ -28,17 +28,9 @@ struct hsr_frame_info { bool is_from_san; }; -#ifdef CONFIG_LOCKDEP -int lockdep_hsr_is_held(spinlock_t *lock); -#else -#define lockdep_hsr_is_held(lock) 1 -#endif - -u32 hsr_mac_hash(struct hsr_priv *hsr, const unsigned char *addr); -struct hsr_node *hsr_node_get_first(struct hlist_head *head, spinlock_t *lock); void hsr_del_self_node(struct hsr_priv *hsr); -void hsr_del_nodes(struct hlist_head *node_db); -struct hsr_node *hsr_get_node(struct hsr_port *port, struct hlist_head *node_db, +void hsr_del_nodes(struct list_head *node_db); +struct hsr_node *hsr_get_node(struct hsr_port *port, struct list_head *node_db, struct sk_buff *skb, bool is_sup, enum hsr_port_type rx_port); void hsr_handle_sup_frame(struct hsr_frame_info *frame); @@ -76,7 +68,7 @@ void prp_handle_san_frame(bool san, enum hsr_port_type port, void prp_update_san_info(struct hsr_node *node, bool is_sup); struct hsr_node { - struct hlist_node mac_list; + struct list_head mac_list; unsigned char macaddress_A[ETH_ALEN]; unsigned char macaddress_B[ETH_ALEN]; /* Local slave through which AddrB frames are received from this node */ diff --git a/net/hsr/hsr_main.h b/net/hsr/hsr_main.h index b158ba409f9a..16ae9fb09ccd 100644 --- a/net/hsr/hsr_main.h +++ b/net/hsr/hsr_main.h @@ -47,9 +47,6 @@ #define HSR_V1_SUP_LSDUSIZE 52 -#define HSR_HSIZE_SHIFT 8 -#define HSR_HSIZE BIT(HSR_HSIZE_SHIFT) - /* The helper functions below assumes that 'path' occupies the 4 most * significant bits of the 16-bit field shared by 'path' and 'LSDU_size' (or * equivalently, the 4 most significant bits of HSR tag byte 14). @@ -188,8 +185,8 @@ struct hsr_proto_ops { struct hsr_priv { struct rcu_head rcu_head; struct list_head ports; - struct hlist_head node_db[HSR_HSIZE]; /* Known HSR nodes */ - struct hlist_head self_node_db; /* MACs of slaves */ + struct list_head node_db; /* Known HSR nodes */ + struct list_head self_node_db; /* MACs of slaves */ struct timer_list announce_timer; /* Supervision frame dispatch */ struct timer_list prune_timer; int announce_count; @@ -199,8 +196,6 @@ struct hsr_priv { spinlock_t seqnr_lock; /* locking for sequence_nr */ spinlock_t list_lock; /* locking for node list */ struct hsr_proto_ops *proto_ops; - u32 hash_buckets; - u32 hash_seed; #define PRP_LAN_ID 0x5 /* 0x1010 for A and 0x1011 for B. Bit 0 is set * based on SLAVE_A or SLAVE_B */ diff --git a/net/hsr/hsr_netlink.c b/net/hsr/hsr_netlink.c index 7174a9092900..78fe40eb9f01 100644 --- a/net/hsr/hsr_netlink.c +++ b/net/hsr/hsr_netlink.c @@ -105,7 +105,6 @@ static int hsr_newlink(struct net *src_net, struct net_device *dev, static void hsr_dellink(struct net_device *dev, struct list_head *head) { struct hsr_priv *hsr = netdev_priv(dev); - int i; del_timer_sync(&hsr->prune_timer); del_timer_sync(&hsr->announce_timer); @@ -114,8 +113,7 @@ static void hsr_dellink(struct net_device *dev, struct list_head *head) hsr_del_ports(hsr); hsr_del_self_node(hsr); - for (i = 0; i < hsr->hash_buckets; i++) - hsr_del_nodes(&hsr->node_db[i]); + hsr_del_nodes(&hsr->node_db); unregister_netdevice_queue(dev, head); } From 5aa2820177af650293b2f9f1873c1f6f8e4ad7a4 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Tue, 29 Nov 2022 17:48:09 +0100 Subject: [PATCH 1546/2002] hsr: Add a rcu-read lock to hsr_forward_skb(). hsr_forward_skb() a skb and keeps information in an on-stack hsr_frame_info. hsr_get_node() assigns hsr_frame_info::node_src which is from a RCU list. This pointer is used later in hsr_forward_do(). I don't see a reason why this pointer can't vanish midway since there is no guarantee that hsr_forward_skb() is invoked from an RCU read section. Use rcu_read_lock() to protect hsr_frame_info::node_src from its assignment until it is no longer used. Fixes: f266a683a4804 ("net/hsr: Better frame dispatch") Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Jakub Kicinski --- net/hsr/hsr_forward.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/hsr/hsr_forward.c b/net/hsr/hsr_forward.c index 3e58d310b4d4..a8befa35841e 100644 --- a/net/hsr/hsr_forward.c +++ b/net/hsr/hsr_forward.c @@ -614,11 +614,13 @@ void hsr_forward_skb(struct sk_buff *skb, struct hsr_port *port) { struct hsr_frame_info frame; + rcu_read_lock(); if (fill_frame_info(&frame, skb, port) < 0) goto out_drop; hsr_register_frame_in(frame.node_src, port, frame.sequence_nr); hsr_forward_do(&frame); + rcu_read_unlock(); /* Gets called for ingress frames as well as egress from master port. * So check and increment stats for master port only here. */ @@ -633,6 +635,7 @@ void hsr_forward_skb(struct sk_buff *skb, struct hsr_port *port) return; out_drop: + rcu_read_unlock(); port->dev->stats.tx_dropped++; kfree_skb(skb); } From 0c74d9f79ec4299365bbe803baa736ae0068179e Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Tue, 29 Nov 2022 17:48:10 +0100 Subject: [PATCH 1547/2002] hsr: Avoid double remove of a node. Due to the hashed-MAC optimisation one problem become visible: hsr_handle_sup_frame() walks over the list of available nodes and merges two node entries into one if based on the information in the supervision both MAC addresses belong to one node. The list-walk happens on a RCU protected list and delete operation happens under a lock. If the supervision arrives on both slave interfaces at the same time then this delete operation can occur simultaneously on two CPUs. The result is the first-CPU deletes the from the list and the second CPUs BUGs while attempting to dereference a poisoned list-entry. This happens more likely with the optimisation because a new node for the mac_B entry is created once a packet has been received and removed (merged) once the supervision frame has been received. Avoid removing/ cleaning up a hsr_node twice by adding a `removed' field which is set to true after the removal and checked before the removal. Fixes: f266a683a4804 ("net/hsr: Better frame dispatch") Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Jakub Kicinski --- net/hsr/hsr_framereg.c | 16 +++++++++++----- net/hsr/hsr_framereg.h | 1 + 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/net/hsr/hsr_framereg.c b/net/hsr/hsr_framereg.c index 9b8eaebce254..f2dd846ff903 100644 --- a/net/hsr/hsr_framereg.c +++ b/net/hsr/hsr_framereg.c @@ -366,9 +366,12 @@ void hsr_handle_sup_frame(struct hsr_frame_info *frame) node_real->addr_B_port = port_rcv->type; spin_lock_bh(&hsr->list_lock); - list_del_rcu(&node_curr->mac_list); + if (!node_curr->removed) { + list_del_rcu(&node_curr->mac_list); + node_curr->removed = true; + kfree_rcu(node_curr, rcu_head); + } spin_unlock_bh(&hsr->list_lock); - kfree_rcu(node_curr, rcu_head); done: /* Push back here */ @@ -539,9 +542,12 @@ void hsr_prune_nodes(struct timer_list *t) if (time_is_before_jiffies(timestamp + msecs_to_jiffies(HSR_NODE_FORGET_TIME))) { hsr_nl_nodedown(hsr, node->macaddress_A); - list_del_rcu(&node->mac_list); - /* Note that we need to free this entry later: */ - kfree_rcu(node, rcu_head); + if (!node->removed) { + list_del_rcu(&node->mac_list); + node->removed = true; + /* Note that we need to free this entry later: */ + kfree_rcu(node, rcu_head); + } } } spin_unlock_bh(&hsr->list_lock); diff --git a/net/hsr/hsr_framereg.h b/net/hsr/hsr_framereg.h index bdbb8c822ba1..b5f902397bf1 100644 --- a/net/hsr/hsr_framereg.h +++ b/net/hsr/hsr_framereg.h @@ -80,6 +80,7 @@ struct hsr_node { bool san_a; bool san_b; u16 seq_out[HSR_PT_PORTS]; + bool removed; struct rcu_head rcu_head; }; From d5c7652eb16fa203d82546e0285136d7b321ffa9 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Tue, 29 Nov 2022 17:48:11 +0100 Subject: [PATCH 1548/2002] hsr: Disable netpoll. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The hsr device is a software device. Its net_device_ops::ndo_start_xmit() routine will process the packet and then pass the resulting skb to dev_queue_xmit(). During processing, hsr acquires a lock with spin_lock_bh() (hsr_add_node()) which needs to be promoted to the _irq() suffix in order to avoid a potential deadlock. Then there are the warnings in dev_queue_xmit() (due to local_bh_disable() with disabled interrupts) left. Instead trying to address those (there is qdisc and…) for netpoll sake, just disable netpoll on hsr. Disable netpoll on hsr and replace the _irqsave() locking with _bh(). Fixes: f421436a591d3 ("net/hsr: Add support for the High-availability Seamless Redundancy protocol (HSRv0)") Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Jakub Kicinski --- net/hsr/hsr_device.c | 14 ++++++-------- net/hsr/hsr_forward.c | 5 ++--- 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/net/hsr/hsr_device.c b/net/hsr/hsr_device.c index 7518f7e93043..84fba2a402a5 100644 --- a/net/hsr/hsr_device.c +++ b/net/hsr/hsr_device.c @@ -278,7 +278,6 @@ static void send_hsr_supervision_frame(struct hsr_port *master, __u8 type = HSR_TLV_LIFE_CHECK; struct hsr_sup_payload *hsr_sp; struct hsr_sup_tag *hsr_stag; - unsigned long irqflags; struct sk_buff *skb; *interval = msecs_to_jiffies(HSR_LIFE_CHECK_INTERVAL); @@ -299,7 +298,7 @@ static void send_hsr_supervision_frame(struct hsr_port *master, set_hsr_stag_HSR_ver(hsr_stag, hsr->prot_version); /* From HSRv1 on we have separate supervision sequence numbers. */ - spin_lock_irqsave(&master->hsr->seqnr_lock, irqflags); + spin_lock_bh(&hsr->seqnr_lock); if (hsr->prot_version > 0) { hsr_stag->sequence_nr = htons(hsr->sup_sequence_nr); hsr->sup_sequence_nr++; @@ -307,7 +306,7 @@ static void send_hsr_supervision_frame(struct hsr_port *master, hsr_stag->sequence_nr = htons(hsr->sequence_nr); hsr->sequence_nr++; } - spin_unlock_irqrestore(&master->hsr->seqnr_lock, irqflags); + spin_unlock_bh(&hsr->seqnr_lock); hsr_stag->tlv.HSR_TLV_type = type; /* TODO: Why 12 in HSRv0? */ @@ -332,7 +331,6 @@ static void send_prp_supervision_frame(struct hsr_port *master, struct hsr_priv *hsr = master->hsr; struct hsr_sup_payload *hsr_sp; struct hsr_sup_tag *hsr_stag; - unsigned long irqflags; struct sk_buff *skb; skb = hsr_init_skb(master); @@ -347,7 +345,7 @@ static void send_prp_supervision_frame(struct hsr_port *master, set_hsr_stag_HSR_ver(hsr_stag, (hsr->prot_version ? 1 : 0)); /* From HSRv1 on we have separate supervision sequence numbers. */ - spin_lock_irqsave(&master->hsr->seqnr_lock, irqflags); + spin_lock_bh(&hsr->seqnr_lock); hsr_stag->sequence_nr = htons(hsr->sup_sequence_nr); hsr->sup_sequence_nr++; hsr_stag->tlv.HSR_TLV_type = PRP_TLV_LIFE_CHECK_DD; @@ -358,11 +356,11 @@ static void send_prp_supervision_frame(struct hsr_port *master, ether_addr_copy(hsr_sp->macaddress_A, master->dev->dev_addr); if (skb_put_padto(skb, ETH_ZLEN)) { - spin_unlock_irqrestore(&master->hsr->seqnr_lock, irqflags); + spin_unlock_bh(&hsr->seqnr_lock); return; } - spin_unlock_irqrestore(&master->hsr->seqnr_lock, irqflags); + spin_unlock_bh(&hsr->seqnr_lock); hsr_forward_skb(skb, master); } @@ -444,7 +442,7 @@ void hsr_dev_setup(struct net_device *dev) dev->header_ops = &hsr_header_ops; dev->netdev_ops = &hsr_device_ops; SET_NETDEV_DEVTYPE(dev, &hsr_type); - dev->priv_flags |= IFF_NO_QUEUE; + dev->priv_flags |= IFF_NO_QUEUE | IFF_DISABLE_NETPOLL; dev->needs_free_netdev = true; diff --git a/net/hsr/hsr_forward.c b/net/hsr/hsr_forward.c index a8befa35841e..a828221335bd 100644 --- a/net/hsr/hsr_forward.c +++ b/net/hsr/hsr_forward.c @@ -500,7 +500,6 @@ static void handle_std_frame(struct sk_buff *skb, { struct hsr_port *port = frame->port_rcv; struct hsr_priv *hsr = port->hsr; - unsigned long irqflags; frame->skb_hsr = NULL; frame->skb_prp = NULL; @@ -510,10 +509,10 @@ static void handle_std_frame(struct sk_buff *skb, frame->is_from_san = true; } else { /* Sequence nr for the master node */ - spin_lock_irqsave(&hsr->seqnr_lock, irqflags); + spin_lock_bh(&hsr->seqnr_lock); frame->sequence_nr = hsr->sequence_nr; hsr->sequence_nr++; - spin_unlock_irqrestore(&hsr->seqnr_lock, irqflags); + spin_unlock_bh(&hsr->seqnr_lock); } } From 06afd2c31d338fa762548580c1bf088703dd1e03 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Tue, 29 Nov 2022 17:48:12 +0100 Subject: [PATCH 1549/2002] hsr: Synchronize sending frames to have always incremented outgoing seq nr. Sending frames via the hsr (master) device requires a sequence number which is tracked in hsr_priv::sequence_nr and protected by hsr_priv::seqnr_lock. Each time a new frame is sent, it will obtain a new id and then send it via the slave devices. Each time a packet is sent (via hsr_forward_do()) the sequence number is checked via hsr_register_frame_out() to ensure that a frame is not handled twice. This make sense for the receiving side to ensure that the frame is not injected into the stack twice after it has been received from both slave ports. There is no locking to cover the sending path which means the following scenario is possible: CPU0 CPU1 hsr_dev_xmit(skb1) hsr_dev_xmit(skb2) fill_frame_info() fill_frame_info() hsr_fill_frame_info() hsr_fill_frame_info() handle_std_frame() handle_std_frame() skb1's sequence_nr = 1 skb2's sequence_nr = 2 hsr_forward_do() hsr_forward_do() hsr_register_frame_out(, 2) // okay, send) hsr_register_frame_out(, 1) // stop, lower seq duplicate Both skbs (or their struct hsr_frame_info) received an unique id. However since skb2 was sent before skb1, the higher sequence number was recorded in hsr_register_frame_out() and the late arriving skb1 was dropped and never sent. This scenario has been observed in a three node HSR setup, with node1 + node2 having ping and iperf running in parallel. From time to time ping reported a missing packet. Based on tracing that missing ping packet did not leave the system. It might be possible (didn't check) to drop the sequence number check on the sending side. But if the higher sequence number leaves on wire before the lower does and the destination receives them in that order and it will drop the packet with the lower sequence number and never inject into the stack. Therefore it seems the only way is to lock the whole path from obtaining the sequence number and sending via dev_queue_xmit() and assuming the packets leave on wire in the same order (and don't get reordered by the NIC). Cover the whole path for the master interface from obtaining the ID until after it has been forwarded via hsr_forward_skb() to ensure the skbs are sent to the NIC in the order of the assigned sequence numbers. Fixes: f421436a591d3 ("net/hsr: Add support for the High-availability Seamless Redundancy protocol (HSRv0)") Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Jakub Kicinski --- net/hsr/hsr_device.c | 12 +++++++----- net/hsr/hsr_forward.c | 3 +-- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/net/hsr/hsr_device.c b/net/hsr/hsr_device.c index 84fba2a402a5..b1e86a7265b3 100644 --- a/net/hsr/hsr_device.c +++ b/net/hsr/hsr_device.c @@ -219,7 +219,9 @@ static netdev_tx_t hsr_dev_xmit(struct sk_buff *skb, struct net_device *dev) skb->dev = master->dev; skb_reset_mac_header(skb); skb_reset_mac_len(skb); + spin_lock_bh(&hsr->seqnr_lock); hsr_forward_skb(skb, master); + spin_unlock_bh(&hsr->seqnr_lock); } else { dev_core_stats_tx_dropped_inc(dev); dev_kfree_skb_any(skb); @@ -306,7 +308,6 @@ static void send_hsr_supervision_frame(struct hsr_port *master, hsr_stag->sequence_nr = htons(hsr->sequence_nr); hsr->sequence_nr++; } - spin_unlock_bh(&hsr->seqnr_lock); hsr_stag->tlv.HSR_TLV_type = type; /* TODO: Why 12 in HSRv0? */ @@ -317,11 +318,13 @@ static void send_hsr_supervision_frame(struct hsr_port *master, hsr_sp = skb_put(skb, sizeof(struct hsr_sup_payload)); ether_addr_copy(hsr_sp->macaddress_A, master->dev->dev_addr); - if (skb_put_padto(skb, ETH_ZLEN)) + if (skb_put_padto(skb, ETH_ZLEN)) { + spin_unlock_bh(&hsr->seqnr_lock); return; + } hsr_forward_skb(skb, master); - + spin_unlock_bh(&hsr->seqnr_lock); return; } @@ -360,9 +363,8 @@ static void send_prp_supervision_frame(struct hsr_port *master, return; } - spin_unlock_bh(&hsr->seqnr_lock); - hsr_forward_skb(skb, master); + spin_unlock_bh(&hsr->seqnr_lock); } /* Announce (supervision frame) timer function diff --git a/net/hsr/hsr_forward.c b/net/hsr/hsr_forward.c index a828221335bd..629daacc9607 100644 --- a/net/hsr/hsr_forward.c +++ b/net/hsr/hsr_forward.c @@ -509,10 +509,9 @@ static void handle_std_frame(struct sk_buff *skb, frame->is_from_san = true; } else { /* Sequence nr for the master node */ - spin_lock_bh(&hsr->seqnr_lock); + lockdep_assert_held(&hsr->seqnr_lock); frame->sequence_nr = hsr->sequence_nr; hsr->sequence_nr++; - spin_unlock_bh(&hsr->seqnr_lock); } } From 5c7aa13210c3abdd34fd421f62347665ec6eb551 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Tue, 29 Nov 2022 17:48:13 +0100 Subject: [PATCH 1550/2002] hsr: Synchronize sequence number updates. hsr_register_frame_out() compares new sequence_nr vs the old one recorded in hsr_node::seq_out and if the new sequence_nr is higher then it will be written to hsr_node::seq_out as the new value. This operation isn't locked so it is possible that two frames with the same sequence number arrive (via the two slave devices) and are fed to hsr_register_frame_out() at the same time. Both will pass the check and update the sequence counter later to the same value. As a result the content of the same packet is fed into the stack twice. This was noticed by running ping and observing DUP being reported from time to time. Instead of using the hsr_priv::seqnr_lock for the whole receive path (as it is for sending in the master node) add an additional lock that is only used for sequence number checks and updates. Add a per-node lock that is used during sequence number reads and updates. Fixes: f421436a591d3 ("net/hsr: Add support for the High-availability Seamless Redundancy protocol (HSRv0)") Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Jakub Kicinski --- net/hsr/hsr_framereg.c | 9 ++++++++- net/hsr/hsr_framereg.h | 2 ++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/net/hsr/hsr_framereg.c b/net/hsr/hsr_framereg.c index f2dd846ff903..39a6088080e9 100644 --- a/net/hsr/hsr_framereg.c +++ b/net/hsr/hsr_framereg.c @@ -157,6 +157,7 @@ static struct hsr_node *hsr_add_node(struct hsr_priv *hsr, return NULL; ether_addr_copy(new_node->macaddress_A, addr); + spin_lock_init(&new_node->seq_out_lock); /* We are only interested in time diffs here, so use current jiffies * as initialization. (0 could trigger an spurious ring error warning). @@ -353,6 +354,7 @@ void hsr_handle_sup_frame(struct hsr_frame_info *frame) } ether_addr_copy(node_real->macaddress_B, ethhdr->h_source); + spin_lock_bh(&node_real->seq_out_lock); for (i = 0; i < HSR_PT_PORTS; i++) { if (!node_curr->time_in_stale[i] && time_after(node_curr->time_in[i], node_real->time_in[i])) { @@ -363,6 +365,7 @@ void hsr_handle_sup_frame(struct hsr_frame_info *frame) if (seq_nr_after(node_curr->seq_out[i], node_real->seq_out[i])) node_real->seq_out[i] = node_curr->seq_out[i]; } + spin_unlock_bh(&node_real->seq_out_lock); node_real->addr_B_port = port_rcv->type; spin_lock_bh(&hsr->list_lock); @@ -456,13 +459,17 @@ void hsr_register_frame_in(struct hsr_node *node, struct hsr_port *port, int hsr_register_frame_out(struct hsr_port *port, struct hsr_node *node, u16 sequence_nr) { + spin_lock_bh(&node->seq_out_lock); if (seq_nr_before_or_eq(sequence_nr, node->seq_out[port->type]) && time_is_after_jiffies(node->time_out[port->type] + - msecs_to_jiffies(HSR_ENTRY_FORGET_TIME))) + msecs_to_jiffies(HSR_ENTRY_FORGET_TIME))) { + spin_unlock_bh(&node->seq_out_lock); return 1; + } node->time_out[port->type] = jiffies; node->seq_out[port->type] = sequence_nr; + spin_unlock_bh(&node->seq_out_lock); return 0; } diff --git a/net/hsr/hsr_framereg.h b/net/hsr/hsr_framereg.h index b5f902397bf1..b23556251d62 100644 --- a/net/hsr/hsr_framereg.h +++ b/net/hsr/hsr_framereg.h @@ -69,6 +69,8 @@ void prp_update_san_info(struct hsr_node *node, bool is_sup); struct hsr_node { struct list_head mac_list; + /* Protect R/W access to seq_out */ + spinlock_t seq_out_lock; unsigned char macaddress_A[ETH_ALEN]; unsigned char macaddress_B[ETH_ALEN]; /* Local slave through which AddrB frames are received from this node */ From 20d3c1e9b861b85e1a774e1876d6adeeb0251fc3 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Tue, 29 Nov 2022 17:48:14 +0100 Subject: [PATCH 1551/2002] hsr: Use a single struct for self_node. self_node_db is a list_head with one entry of struct hsr_node. The purpose is to hold the two MAC addresses of the node itself. It is convenient to recycle the structure. However having a list_head and fetching always the first entry is not really optimal. Created a new data strucure contaning the two MAC addresses named hsr_self_node. Access that structure like an RCU protected pointer so it can be replaced on the fly without blocking the reader. Signed-off-by: Sebastian Andrzej Siewior Reviewed-by: Kurt Kanzenbach Signed-off-by: Jakub Kicinski --- net/hsr/hsr_device.c | 1 - net/hsr/hsr_framereg.c | 63 +++++++++++++++++++----------------------- net/hsr/hsr_main.h | 8 +++++- 3 files changed, 35 insertions(+), 37 deletions(-) diff --git a/net/hsr/hsr_device.c b/net/hsr/hsr_device.c index b1e86a7265b3..5a236aae2366 100644 --- a/net/hsr/hsr_device.c +++ b/net/hsr/hsr_device.c @@ -490,7 +490,6 @@ int hsr_dev_finalize(struct net_device *hsr_dev, struct net_device *slave[2], hsr = netdev_priv(hsr_dev); INIT_LIST_HEAD(&hsr->ports); INIT_LIST_HEAD(&hsr->node_db); - INIT_LIST_HEAD(&hsr->self_node_db); spin_lock_init(&hsr->list_lock); eth_hw_addr_set(hsr_dev, slave[0]->dev_addr); diff --git a/net/hsr/hsr_framereg.c b/net/hsr/hsr_framereg.c index 39a6088080e9..00db74d96583 100644 --- a/net/hsr/hsr_framereg.c +++ b/net/hsr/hsr_framereg.c @@ -38,21 +38,22 @@ static bool seq_nr_after(u16 a, u16 b) bool hsr_addr_is_self(struct hsr_priv *hsr, unsigned char *addr) { - struct hsr_node *node; + struct hsr_self_node *sn; + bool ret = false; - node = list_first_or_null_rcu(&hsr->self_node_db, struct hsr_node, - mac_list); - if (!node) { + rcu_read_lock(); + sn = rcu_dereference(hsr->self_node); + if (!sn) { WARN_ONCE(1, "HSR: No self node\n"); - return false; + goto out; } - if (ether_addr_equal(addr, node->macaddress_A)) - return true; - if (ether_addr_equal(addr, node->macaddress_B)) - return true; - - return false; + if (ether_addr_equal(addr, sn->macaddress_A) || + ether_addr_equal(addr, sn->macaddress_B)) + ret = true; +out: + rcu_read_unlock(); + return ret; } /* Search for mac entry. Caller must hold rcu read lock. @@ -70,50 +71,42 @@ static struct hsr_node *find_node_by_addr_A(struct list_head *node_db, return NULL; } -/* Helper for device init; the self_node_db is used in hsr_rcv() to recognize +/* Helper for device init; the self_node is used in hsr_rcv() to recognize * frames from self that's been looped over the HSR ring. */ int hsr_create_self_node(struct hsr_priv *hsr, const unsigned char addr_a[ETH_ALEN], const unsigned char addr_b[ETH_ALEN]) { - struct list_head *self_node_db = &hsr->self_node_db; - struct hsr_node *node, *oldnode; + struct hsr_self_node *sn, *old; - node = kmalloc(sizeof(*node), GFP_KERNEL); - if (!node) + sn = kmalloc(sizeof(*sn), GFP_KERNEL); + if (!sn) return -ENOMEM; - ether_addr_copy(node->macaddress_A, addr_a); - ether_addr_copy(node->macaddress_B, addr_b); + ether_addr_copy(sn->macaddress_A, addr_a); + ether_addr_copy(sn->macaddress_B, addr_b); spin_lock_bh(&hsr->list_lock); - oldnode = list_first_or_null_rcu(self_node_db, - struct hsr_node, mac_list); - if (oldnode) { - list_replace_rcu(&oldnode->mac_list, &node->mac_list); - spin_unlock_bh(&hsr->list_lock); - kfree_rcu(oldnode, rcu_head); - } else { - list_add_tail_rcu(&node->mac_list, self_node_db); - spin_unlock_bh(&hsr->list_lock); - } + old = rcu_replace_pointer(hsr->self_node, sn, + lockdep_is_held(&hsr->list_lock)); + spin_unlock_bh(&hsr->list_lock); + if (old) + kfree_rcu(old, rcu_head); return 0; } void hsr_del_self_node(struct hsr_priv *hsr) { - struct list_head *self_node_db = &hsr->self_node_db; - struct hsr_node *node; + struct hsr_self_node *old; spin_lock_bh(&hsr->list_lock); - node = list_first_or_null_rcu(self_node_db, struct hsr_node, mac_list); - if (node) { - list_del_rcu(&node->mac_list); - kfree_rcu(node, rcu_head); - } + old = rcu_replace_pointer(hsr->self_node, NULL, + lockdep_is_held(&hsr->list_lock)); spin_unlock_bh(&hsr->list_lock); + if (old) + kfree_rcu(old, rcu_head); } void hsr_del_nodes(struct list_head *node_db) diff --git a/net/hsr/hsr_main.h b/net/hsr/hsr_main.h index 16ae9fb09ccd..5584c80a5c79 100644 --- a/net/hsr/hsr_main.h +++ b/net/hsr/hsr_main.h @@ -182,11 +182,17 @@ struct hsr_proto_ops { void (*update_san_info)(struct hsr_node *node, bool is_sup); }; +struct hsr_self_node { + unsigned char macaddress_A[ETH_ALEN]; + unsigned char macaddress_B[ETH_ALEN]; + struct rcu_head rcu_head; +}; + struct hsr_priv { struct rcu_head rcu_head; struct list_head ports; struct list_head node_db; /* Known HSR nodes */ - struct list_head self_node_db; /* MACs of slaves */ + struct hsr_self_node __rcu *self_node; /* MACs of slaves */ struct timer_list announce_timer; /* Supervision frame dispatch */ struct timer_list prune_timer; int announce_count; From 7d0455e97072b81c03b6062dbcc1403ebf9d9da5 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Tue, 29 Nov 2022 17:48:15 +0100 Subject: [PATCH 1552/2002] selftests: Add a basic HSR test. This test adds a basic HSRv0 network with 3 nodes. In its current shape it sends and forwards packets, announcements and so merges nodes based on MAC A/B information. It is able to detect duplicate packets and packetloss should any occur. Cc: Shuah Khan Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Jakub Kicinski --- tools/testing/selftests/Makefile | 1 + tools/testing/selftests/net/hsr/Makefile | 7 + tools/testing/selftests/net/hsr/config | 4 + tools/testing/selftests/net/hsr/hsr_ping.sh | 256 ++++++++++++++++++++ 4 files changed, 268 insertions(+) create mode 100644 tools/testing/selftests/net/hsr/Makefile create mode 100644 tools/testing/selftests/net/hsr/config create mode 100755 tools/testing/selftests/net/hsr/hsr_ping.sh diff --git a/tools/testing/selftests/Makefile b/tools/testing/selftests/Makefile index f07aef7c592c..b57b091d8026 100644 --- a/tools/testing/selftests/Makefile +++ b/tools/testing/selftests/Makefile @@ -48,6 +48,7 @@ TARGETS += nci TARGETS += net TARGETS += net/af_unix TARGETS += net/forwarding +TARGETS += net/hsr TARGETS += net/mptcp TARGETS += net/openvswitch TARGETS += netfilter diff --git a/tools/testing/selftests/net/hsr/Makefile b/tools/testing/selftests/net/hsr/Makefile new file mode 100644 index 000000000000..92c1d9d080cd --- /dev/null +++ b/tools/testing/selftests/net/hsr/Makefile @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: GPL-2.0 + +top_srcdir = ../../../../.. + +TEST_PROGS := hsr_ping.sh + +include ../../lib.mk diff --git a/tools/testing/selftests/net/hsr/config b/tools/testing/selftests/net/hsr/config new file mode 100644 index 000000000000..22061204fb69 --- /dev/null +++ b/tools/testing/selftests/net/hsr/config @@ -0,0 +1,4 @@ +CONFIG_IPV6=y +CONFIG_NET_SCH_NETEM=m +CONFIG_HSR=y +CONFIG_VETH=y diff --git a/tools/testing/selftests/net/hsr/hsr_ping.sh b/tools/testing/selftests/net/hsr/hsr_ping.sh new file mode 100755 index 000000000000..df9143538708 --- /dev/null +++ b/tools/testing/selftests/net/hsr/hsr_ping.sh @@ -0,0 +1,256 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0 + +ret=0 +ksft_skip=4 +ipv6=true + +optstring="h4" +usage() { + echo "Usage: $0 [OPTION]" + echo -e "\t-4: IPv4 only: disable IPv6 tests (default: test both IPv4 and IPv6)" +} + +while getopts "$optstring" option;do + case "$option" in + "h") + usage $0 + exit 0 + ;; + "4") + ipv6=false + ;; + "?") + usage $0 + exit 1 + ;; +esac +done + +sec=$(date +%s) +rndh=$(printf %x $sec)-$(mktemp -u XXXXXX) +ns1="ns1-$rndh" +ns2="ns2-$rndh" +ns3="ns3-$rndh" + +cleanup() +{ + local netns + for netns in "$ns1" "$ns2" "$ns3" ;do + ip netns del $netns + done +} + +ip -Version > /dev/null 2>&1 +if [ $? -ne 0 ];then + echo "SKIP: Could not run test without ip tool" + exit $ksft_skip +fi + +trap cleanup EXIT + +for i in "$ns1" "$ns2" "$ns3" ;do + ip netns add $i || exit $ksft_skip + ip -net $i link set lo up +done + +echo "INFO: preparing interfaces." +# Three HSR nodes. Each node has one link to each of its neighbour, two links in total. +# +# ns1eth1 ----- ns2eth1 +# hsr1 hsr2 +# ns1eth2 ns2eth2 +# | | +# ns3eth1 ns3eth2 +# \ / +# hsr3 +# +# Interfaces +ip link add ns1eth1 netns "$ns1" type veth peer name ns2eth1 netns "$ns2" +ip link add ns1eth2 netns "$ns1" type veth peer name ns3eth1 netns "$ns3" +ip link add ns3eth2 netns "$ns3" type veth peer name ns2eth2 netns "$ns2" + +# HSRv0. +ip -net "$ns1" link add name hsr1 type hsr slave1 ns1eth1 slave2 ns1eth2 supervision 45 version 0 proto 0 +ip -net "$ns2" link add name hsr2 type hsr slave1 ns2eth1 slave2 ns2eth2 supervision 45 version 0 proto 0 +ip -net "$ns3" link add name hsr3 type hsr slave1 ns3eth1 slave2 ns3eth2 supervision 45 version 0 proto 0 + +# IP for HSR +ip -net "$ns1" addr add 100.64.0.1/24 dev hsr1 +ip -net "$ns1" addr add dead:beef:1::1/64 dev hsr1 nodad +ip -net "$ns2" addr add 100.64.0.2/24 dev hsr2 +ip -net "$ns2" addr add dead:beef:1::2/64 dev hsr2 nodad +ip -net "$ns3" addr add 100.64.0.3/24 dev hsr3 +ip -net "$ns3" addr add dead:beef:1::3/64 dev hsr3 nodad + +# All Links up +ip -net "$ns1" link set ns1eth1 up +ip -net "$ns1" link set ns1eth2 up +ip -net "$ns1" link set hsr1 up + +ip -net "$ns2" link set ns2eth1 up +ip -net "$ns2" link set ns2eth2 up +ip -net "$ns2" link set hsr2 up + +ip -net "$ns3" link set ns3eth1 up +ip -net "$ns3" link set ns3eth2 up +ip -net "$ns3" link set hsr3 up + +# $1: IP address +is_v6() +{ + [ -z "${1##*:*}" ] +} + +do_ping() +{ + local netns="$1" + local connect_addr="$2" + local ping_args="-q -c 2" + + if is_v6 "${connect_addr}"; then + $ipv6 || return 0 + ping_args="${ping_args} -6" + fi + + ip netns exec ${netns} ping ${ping_args} $connect_addr >/dev/null + if [ $? -ne 0 ] ; then + echo "$netns -> $connect_addr connectivity [ FAIL ]" 1>&2 + ret=1 + return 1 + fi + + return 0 +} + +do_ping_long() +{ + local netns="$1" + local connect_addr="$2" + local ping_args="-q -c 10" + + if is_v6 "${connect_addr}"; then + $ipv6 || return 0 + ping_args="${ping_args} -6" + fi + + OUT="$(LANG=C ip netns exec ${netns} ping ${ping_args} $connect_addr | grep received)" + if [ $? -ne 0 ] ; then + echo "$netns -> $connect_addr ping [ FAIL ]" 1>&2 + ret=1 + return 1 + fi + + VAL="$(echo $OUT | cut -d' ' -f1-8)" + if [ "$VAL" != "10 packets transmitted, 10 received, 0% packet loss," ] + then + echo "$netns -> $connect_addr ping TEST [ FAIL ]" + echo "Expect to send and receive 10 packets and no duplicates." + echo "Full message: ${OUT}." + ret=1 + return 1 + fi + + return 0 +} + +stop_if_error() +{ + local msg="$1" + + if [ ${ret} -ne 0 ]; then + echo "FAIL: ${msg}" 1>&2 + exit ${ret} + fi +} + + +echo "INFO: Initial validation ping." +# Each node has to be able each one. +do_ping "$ns1" 100.64.0.2 +do_ping "$ns2" 100.64.0.1 +do_ping "$ns3" 100.64.0.1 +stop_if_error "Initial validation failed." + +do_ping "$ns1" 100.64.0.3 +do_ping "$ns2" 100.64.0.3 +do_ping "$ns3" 100.64.0.2 + +do_ping "$ns1" dead:beef:1::2 +do_ping "$ns1" dead:beef:1::3 +do_ping "$ns2" dead:beef:1::1 +do_ping "$ns2" dead:beef:1::2 +do_ping "$ns3" dead:beef:1::1 +do_ping "$ns3" dead:beef:1::2 + +stop_if_error "Initial validation failed." + +# Wait until supervisor all supervision frames have been processed and the node +# entries have been merged. Otherwise duplicate frames will be observed which is +# valid at this stage. +WAIT=5 +while [ ${WAIT} -gt 0 ] +do + grep 00:00:00:00:00:00 /sys/kernel/debug/hsr/hsr*/node_table + if [ $? -ne 0 ] + then + break + fi + sleep 1 + let WAIT = WAIT - 1 +done + +# Just a safety delay in case the above check didn't handle it. +sleep 1 + +echo "INFO: Longer ping test." +do_ping_long "$ns1" 100.64.0.2 +do_ping_long "$ns1" dead:beef:1::2 +do_ping_long "$ns1" 100.64.0.3 +do_ping_long "$ns1" dead:beef:1::3 + +stop_if_error "Longer ping test failed." + +do_ping_long "$ns2" 100.64.0.1 +do_ping_long "$ns2" dead:beef:1::1 +do_ping_long "$ns2" 100.64.0.3 +do_ping_long "$ns2" dead:beef:1::2 +stop_if_error "Longer ping test failed." + +do_ping_long "$ns3" 100.64.0.1 +do_ping_long "$ns3" dead:beef:1::1 +do_ping_long "$ns3" 100.64.0.2 +do_ping_long "$ns3" dead:beef:1::2 +stop_if_error "Longer ping test failed." + +echo "INFO: Cutting one link." +do_ping_long "$ns1" 100.64.0.3 & + +sleep 3 +ip -net "$ns3" link set ns3eth1 down +wait + +ip -net "$ns3" link set ns3eth1 up + +stop_if_error "Failed with one link down." + +echo "INFO: Delay the link and drop a few packages." +tc -net "$ns3" qdisc add dev ns3eth1 root netem delay 50ms +tc -net "$ns2" qdisc add dev ns2eth1 root netem delay 5ms loss 25% + +do_ping_long "$ns1" 100.64.0.2 +do_ping_long "$ns1" 100.64.0.3 + +stop_if_error "Failed with delay and packetloss." + +do_ping_long "$ns2" 100.64.0.1 +do_ping_long "$ns2" 100.64.0.3 + +stop_if_error "Failed with delay and packetloss." + +do_ping_long "$ns3" 100.64.0.1 +do_ping_long "$ns3" 100.64.0.2 +stop_if_error "Failed with delay and packetloss." + +echo "INFO: All good." +exit $ret From 47b438cc27254fa68b7360de153db4093c9259f4 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Wed, 30 Nov 2022 09:52:50 +0100 Subject: [PATCH 1553/2002] net: devlink: convert port_list into xarray Some devlink instances may contain thousands of ports. Storing them in linked list and looking them up is not scalable. Convert the linked list into xarray. Signed-off-by: Jiri Pirko Acked-by: Jakub Kicinski Signed-off-by: David S. Miller --- net/core/devlink.c | 57 ++++++++++++++++++++++------------------------ 1 file changed, 27 insertions(+), 30 deletions(-) diff --git a/net/core/devlink.c b/net/core/devlink.c index fca3ebee97b0..907df7124157 100644 --- a/net/core/devlink.c +++ b/net/core/devlink.c @@ -41,7 +41,7 @@ struct devlink_dev_stats { struct devlink { u32 index; - struct list_head port_list; + struct xarray ports; struct list_head rate_list; struct list_head sb_list; struct list_head dpipe_table_list; @@ -382,19 +382,7 @@ static struct devlink *devlink_get_from_attrs(struct net *net, static struct devlink_port *devlink_port_get_by_index(struct devlink *devlink, unsigned int port_index) { - struct devlink_port *devlink_port; - - list_for_each_entry(devlink_port, &devlink->port_list, list) { - if (devlink_port->index == port_index) - return devlink_port; - } - return NULL; -} - -static bool devlink_port_index_exists(struct devlink *devlink, - unsigned int port_index) -{ - return devlink_port_get_by_index(devlink, port_index); + return xa_load(&devlink->ports, port_index); } static struct devlink_port *devlink_port_get_from_attrs(struct devlink *devlink, @@ -1565,14 +1553,14 @@ static int devlink_nl_cmd_port_get_dumpit(struct sk_buff *msg, { struct devlink *devlink; struct devlink_port *devlink_port; + unsigned long index, port_index; int start = cb->args[0]; - unsigned long index; int idx = 0; int err; devlinks_xa_for_each_registered_get(sock_net(msg->sk), index, devlink) { devl_lock(devlink); - list_for_each_entry(devlink_port, &devlink->port_list, list) { + xa_for_each(&devlink->ports, port_index, devlink_port) { if (idx < start) { idx++; continue; @@ -2886,10 +2874,11 @@ static int __sb_port_pool_get_dumpit(struct sk_buff *msg, int start, int *p_idx, { struct devlink_port *devlink_port; u16 pool_count = devlink_sb_pool_count(devlink_sb); + unsigned long port_index; u16 pool_index; int err; - list_for_each_entry(devlink_port, &devlink->port_list, list) { + xa_for_each(&devlink->ports, port_index, devlink_port) { for (pool_index = 0; pool_index < pool_count; pool_index++) { if (*p_idx < start) { (*p_idx)++; @@ -3107,10 +3096,11 @@ static int __sb_tc_pool_bind_get_dumpit(struct sk_buff *msg, u32 portid, u32 seq) { struct devlink_port *devlink_port; + unsigned long port_index; u16 tc_index; int err; - list_for_each_entry(devlink_port, &devlink->port_list, list) { + xa_for_each(&devlink->ports, port_index, devlink_port) { for (tc_index = 0; tc_index < devlink_sb->ingress_tc_count; tc_index++) { if (*p_idx < start) { @@ -6207,6 +6197,7 @@ static int devlink_nl_cmd_region_get_devlink_dumpit(struct sk_buff *msg, { struct devlink_region *region; struct devlink_port *port; + unsigned long port_index; int err = 0; devl_lock(devlink); @@ -6225,7 +6216,7 @@ static int devlink_nl_cmd_region_get_devlink_dumpit(struct sk_buff *msg, (*idx)++; } - list_for_each_entry(port, &devlink->port_list, list) { + xa_for_each(&devlink->ports, port_index, port) { err = devlink_nl_cmd_region_get_port_dumpit(msg, cb, port, idx, start); if (err) @@ -8057,10 +8048,10 @@ devlink_nl_cmd_health_reporter_get_dumpit(struct sk_buff *msg, struct netlink_callback *cb) { struct devlink_health_reporter *reporter; + unsigned long index, port_index; struct devlink_port *port; struct devlink *devlink; int start = cb->args[0]; - unsigned long index; int idx = 0; int err; @@ -8089,7 +8080,7 @@ devlink_nl_cmd_health_reporter_get_dumpit(struct sk_buff *msg, devlinks_xa_for_each_registered_get(sock_net(msg->sk), index, devlink) { devl_lock(devlink); - list_for_each_entry(port, &devlink->port_list, list) { + xa_for_each(&devlink->ports, port_index, port) { mutex_lock(&port->reporters_lock); list_for_each_entry(reporter, &port->reporter_list, list) { if (idx < start) { @@ -9808,9 +9799,9 @@ struct devlink *devlink_alloc_ns(const struct devlink_ops *ops, devlink->dev = dev; devlink->ops = ops; + xa_init_flags(&devlink->ports, XA_FLAGS_ALLOC); xa_init_flags(&devlink->snapshot_ids, XA_FLAGS_ALLOC); write_pnet(&devlink->_net, net); - INIT_LIST_HEAD(&devlink->port_list); INIT_LIST_HEAD(&devlink->rate_list); INIT_LIST_HEAD(&devlink->linecard_list); INIT_LIST_HEAD(&devlink->sb_list); @@ -9862,12 +9853,13 @@ static void devlink_notify_register(struct devlink *devlink) struct devlink_linecard *linecard; struct devlink_rate *rate_node; struct devlink_region *region; + unsigned long port_index; devlink_notify(devlink, DEVLINK_CMD_NEW); list_for_each_entry(linecard, &devlink->linecard_list, list) devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_NEW); - list_for_each_entry(devlink_port, &devlink->port_list, list) + xa_for_each(&devlink->ports, port_index, devlink_port) devlink_port_notify(devlink_port, DEVLINK_CMD_PORT_NEW); list_for_each_entry(policer_item, &devlink->trap_policer_list, list) @@ -9901,6 +9893,7 @@ static void devlink_notify_unregister(struct devlink *devlink) struct devlink_port *devlink_port; struct devlink_rate *rate_node; struct devlink_region *region; + unsigned long port_index; list_for_each_entry_reverse(param_item, &devlink->param_list, list) devlink_param_notify(devlink, 0, param_item, @@ -9923,7 +9916,7 @@ static void devlink_notify_unregister(struct devlink *devlink) devlink_trap_policer_notify(devlink, policer_item, DEVLINK_CMD_TRAP_POLICER_DEL); - list_for_each_entry_reverse(devlink_port, &devlink->port_list, list) + xa_for_each(&devlink->ports, port_index, devlink_port) devlink_port_notify(devlink_port, DEVLINK_CMD_PORT_DEL); devlink_notify(devlink, DEVLINK_CMD_DEL); } @@ -9987,9 +9980,10 @@ void devlink_free(struct devlink *devlink) WARN_ON(!list_empty(&devlink->sb_list)); WARN_ON(!list_empty(&devlink->rate_list)); WARN_ON(!list_empty(&devlink->linecard_list)); - WARN_ON(!list_empty(&devlink->port_list)); + WARN_ON(!xa_empty(&devlink->ports)); xa_destroy(&devlink->snapshot_ids); + xa_destroy(&devlink->ports); WARN_ON_ONCE(unregister_netdevice_notifier_net(devlink_net(devlink), &devlink->netdevice_nb)); @@ -10088,10 +10082,9 @@ int devl_port_register(struct devlink *devlink, struct devlink_port *devlink_port, unsigned int port_index) { - devl_assert_locked(devlink); + int err; - if (devlink_port_index_exists(devlink, port_index)) - return -EEXIST; + devl_assert_locked(devlink); ASSERT_DEVLINK_PORT_NOT_REGISTERED(devlink_port); @@ -10101,7 +10094,11 @@ int devl_port_register(struct devlink *devlink, spin_lock_init(&devlink_port->type_lock); INIT_LIST_HEAD(&devlink_port->reporter_list); mutex_init(&devlink_port->reporters_lock); - list_add_tail(&devlink_port->list, &devlink->port_list); + err = xa_insert(&devlink->ports, port_index, devlink_port, GFP_KERNEL); + if (err) { + mutex_destroy(&devlink_port->reporters_lock); + return err; + } INIT_DELAYED_WORK(&devlink_port->type_warn_dw, &devlink_port_type_warn); devlink_port_type_warn_schedule(devlink_port); @@ -10150,7 +10147,7 @@ void devl_port_unregister(struct devlink_port *devlink_port) devlink_port_type_warn_cancel(devlink_port); devlink_port_notify(devlink_port, DEVLINK_CMD_PORT_DEL); - list_del(&devlink_port->list); + xa_erase(&devlink_port->devlink->ports, devlink_port->index); WARN_ON(!list_empty(&devlink_port->reporter_list)); mutex_destroy(&devlink_port->reporters_lock); devlink_port->registered = false; From 0bbe50f3e85aadeb7417905b1011fc5f98d8c897 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 30 Nov 2022 14:36:12 +0200 Subject: [PATCH 1554/2002] net: thunderbolt: Switch from __maybe_unused to pm_sleep_ptr() etc Letting the compiler remove these functions when the kernel is built without CONFIG_PM_SLEEP support is simpler and less heavier for builds than the use of __maybe_unused attributes. Signed-off-by: Andy Shevchenko Acked-by: Mika Westerberg Signed-off-by: David S. Miller --- drivers/net/thunderbolt.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/drivers/net/thunderbolt.c b/drivers/net/thunderbolt.c index b20cd370b7f2..c73d419f1456 100644 --- a/drivers/net/thunderbolt.c +++ b/drivers/net/thunderbolt.c @@ -1319,7 +1319,7 @@ static void tbnet_shutdown(struct tb_service *svc) tbnet_tear_down(tb_service_get_drvdata(svc), true); } -static int __maybe_unused tbnet_suspend(struct device *dev) +static int tbnet_suspend(struct device *dev) { struct tb_service *svc = tb_to_service(dev); struct tbnet *net = tb_service_get_drvdata(svc); @@ -1334,7 +1334,7 @@ static int __maybe_unused tbnet_suspend(struct device *dev) return 0; } -static int __maybe_unused tbnet_resume(struct device *dev) +static int tbnet_resume(struct device *dev) { struct tb_service *svc = tb_to_service(dev); struct tbnet *net = tb_service_get_drvdata(svc); @@ -1350,9 +1350,7 @@ static int __maybe_unused tbnet_resume(struct device *dev) return 0; } -static const struct dev_pm_ops tbnet_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(tbnet_suspend, tbnet_resume) -}; +static DEFINE_SIMPLE_DEV_PM_OPS(tbnet_pm_ops, tbnet_suspend, tbnet_resume); static const struct tb_service_id tbnet_ids[] = { { TB_SERVICE("network", 1) }, @@ -1364,7 +1362,7 @@ static struct tb_service_driver tbnet_driver = { .driver = { .owner = THIS_MODULE, .name = "thunderbolt-net", - .pm = &tbnet_pm_ops, + .pm = pm_sleep_ptr(&tbnet_pm_ops), }, .probe = tbnet_probe, .remove = tbnet_remove, From a479f9264bddfd0c266615b83f784222eccf9b91 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 30 Nov 2022 14:36:13 +0200 Subject: [PATCH 1555/2002] net: thunderbolt: Use bitwise types in the struct thunderbolt_ip_frame_header The main usage of the struct thunderbolt_ip_frame_header is to handle the packets on the media layer. The header is bound to the protocol in which the byte ordering is crucial. However the data type definition doesn't use that and sparse is unhappy, for example (17 altogether): .../thunderbolt.c:718:23: warning: cast to restricted __le32 .../thunderbolt.c:966:42: warning: incorrect type in assignment (different base types) .../thunderbolt.c:966:42: expected unsigned int [usertype] frame_count .../thunderbolt.c:966:42: got restricted __le32 [usertype] Switch to the bitwise types in the struct thunderbolt_ip_frame_header to reduce this, but not completely solving (9 left), because the same data type is used for Rx header handled locally (in CPU byte order). Signed-off-by: Andy Shevchenko Acked-by: Mika Westerberg Signed-off-by: David S. Miller --- drivers/net/thunderbolt.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/net/thunderbolt.c b/drivers/net/thunderbolt.c index c73d419f1456..4ed7f5b547e3 100644 --- a/drivers/net/thunderbolt.c +++ b/drivers/net/thunderbolt.c @@ -58,10 +58,10 @@ * supported then @frame_id is filled, otherwise it stays %0. */ struct thunderbolt_ip_frame_header { - u32 frame_size; - u16 frame_index; - u16 frame_id; - u32 frame_count; + __le32 frame_size; + __le16 frame_index; + __le16 frame_id; + __le32 frame_count; }; enum thunderbolt_ip_frame_pdf { From f036b97da67f4551d703f189b9a90686cbaf0adb Mon Sep 17 00:00:00 2001 From: Jonathan Toppins Date: Wed, 30 Nov 2022 15:12:06 -0500 Subject: [PATCH 1556/2002] Documentation: bonding: update miimon default to 100 With commit c1f897ce186a ("bonding: set default miimon value for non-arp modes if not set") the miimon default was changed from zero to 100 if arp_interval is also zero. Document this fact in bonding.rst. Fixes: c1f897ce186a ("bonding: set default miimon value for non-arp modes if not set") Signed-off-by: Jonathan Toppins Signed-off-by: David S. Miller --- Documentation/networking/bonding.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Documentation/networking/bonding.rst b/Documentation/networking/bonding.rst index 96cd7a26f3d9..da57aac73ffc 100644 --- a/Documentation/networking/bonding.rst +++ b/Documentation/networking/bonding.rst @@ -566,7 +566,8 @@ miimon link monitoring. A value of 100 is a good starting point. The use_carrier option, below, affects how the link state is determined. See the High Availability section for additional - information. The default value is 0. + information. The default value is 100 if arp_interval is not + set. min_links From 95cce3fae4d980af92bbccb56a6a64261a75be6d Mon Sep 17 00:00:00 2001 From: Jonathan Toppins Date: Wed, 30 Nov 2022 15:12:07 -0500 Subject: [PATCH 1557/2002] Documentation: bonding: correct xmit hash steps Correct xmit hash steps for layer3+4 as introduced by commit 49aefd131739 ("bonding: do not discard lowest hash bit for non layer3+4 hashing"). Signed-off-by: Jonathan Toppins Signed-off-by: David S. Miller --- Documentation/networking/bonding.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/networking/bonding.rst b/Documentation/networking/bonding.rst index da57aac73ffc..adc4bf4f3c50 100644 --- a/Documentation/networking/bonding.rst +++ b/Documentation/networking/bonding.rst @@ -957,6 +957,7 @@ xmit_hash_policy hash = hash XOR source IP XOR destination IP hash = hash XOR (hash RSHIFT 16) hash = hash XOR (hash RSHIFT 8) + hash = hash RSHIFT 1 And then hash is reduced modulo slave count. If the protocol is IPv6 then the source and destination From 91644df1ba01dd584d6f0fb2b2abd9014d484957 Mon Sep 17 00:00:00 2001 From: Gerhard Engleder Date: Wed, 30 Nov 2022 20:37:05 +0100 Subject: [PATCH 1558/2002] tsnep: Consistent naming of struct net_device Signed-off-by: Gerhard Engleder Reviewed-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/ethernet/engleder/tsnep_ethtool.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/net/ethernet/engleder/tsnep_ethtool.c b/drivers/net/ethernet/engleder/tsnep_ethtool.c index a713a126b227..c2640e88f347 100644 --- a/drivers/net/ethernet/engleder/tsnep_ethtool.c +++ b/drivers/net/ethernet/engleder/tsnep_ethtool.c @@ -250,10 +250,10 @@ static int tsnep_ethtool_get_sset_count(struct net_device *netdev, int sset) } } -static int tsnep_ethtool_get_rxnfc(struct net_device *dev, +static int tsnep_ethtool_get_rxnfc(struct net_device *netdev, struct ethtool_rxnfc *cmd, u32 *rule_locs) { - struct tsnep_adapter *adapter = netdev_priv(dev); + struct tsnep_adapter *adapter = netdev_priv(netdev); switch (cmd->cmd) { case ETHTOOL_GRXRINGS: @@ -273,10 +273,10 @@ static int tsnep_ethtool_get_rxnfc(struct net_device *dev, } } -static int tsnep_ethtool_set_rxnfc(struct net_device *dev, +static int tsnep_ethtool_set_rxnfc(struct net_device *netdev, struct ethtool_rxnfc *cmd) { - struct tsnep_adapter *adapter = netdev_priv(dev); + struct tsnep_adapter *adapter = netdev_priv(netdev); switch (cmd->cmd) { case ETHTOOL_SRXCLSRLINS: @@ -288,10 +288,10 @@ static int tsnep_ethtool_set_rxnfc(struct net_device *dev, } } -static int tsnep_ethtool_get_ts_info(struct net_device *dev, +static int tsnep_ethtool_get_ts_info(struct net_device *netdev, struct ethtool_ts_info *info) { - struct tsnep_adapter *adapter = netdev_priv(dev); + struct tsnep_adapter *adapter = netdev_priv(netdev); info->so_timestamping = SOF_TIMESTAMPING_TX_SOFTWARE | SOF_TIMESTAMPING_RX_SOFTWARE | From 4f661ccfcac70f3838f7e5ca53ac51c86e1acbb1 Mon Sep 17 00:00:00 2001 From: Gerhard Engleder Date: Wed, 30 Nov 2022 20:37:06 +0100 Subject: [PATCH 1559/2002] tsnep: Add ethtool::get_channels support Allow user space to read number of TX and RX queue. This is useful for device dependent qdisc configurations like TAPRIO with hardware offload. Also ethtool::get_per_queue_coalesce / set_per_queue_coalesce requires that interface. Signed-off-by: Gerhard Engleder Reviewed-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/ethernet/engleder/tsnep_ethtool.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/drivers/net/ethernet/engleder/tsnep_ethtool.c b/drivers/net/ethernet/engleder/tsnep_ethtool.c index c2640e88f347..517ac8de32bb 100644 --- a/drivers/net/ethernet/engleder/tsnep_ethtool.c +++ b/drivers/net/ethernet/engleder/tsnep_ethtool.c @@ -288,6 +288,17 @@ static int tsnep_ethtool_set_rxnfc(struct net_device *netdev, } } +static void tsnep_ethtool_get_channels(struct net_device *netdev, + struct ethtool_channels *ch) +{ + struct tsnep_adapter *adapter = netdev_priv(netdev); + + ch->max_rx = adapter->num_rx_queues; + ch->max_tx = adapter->num_tx_queues; + ch->rx_count = adapter->num_rx_queues; + ch->tx_count = adapter->num_tx_queues; +} + static int tsnep_ethtool_get_ts_info(struct net_device *netdev, struct ethtool_ts_info *info) { @@ -327,6 +338,7 @@ const struct ethtool_ops tsnep_ethtool_ops = { .get_sset_count = tsnep_ethtool_get_sset_count, .get_rxnfc = tsnep_ethtool_get_rxnfc, .set_rxnfc = tsnep_ethtool_set_rxnfc, + .get_channels = tsnep_ethtool_get_channels, .get_ts_info = tsnep_ethtool_get_ts_info, .get_link_ksettings = phy_ethtool_get_link_ksettings, .set_link_ksettings = phy_ethtool_set_link_ksettings, From d3dfe8d6c04061a3eadfc299ba8009b38f4bc25b Mon Sep 17 00:00:00 2001 From: Gerhard Engleder Date: Wed, 30 Nov 2022 20:37:07 +0100 Subject: [PATCH 1560/2002] tsnep: Throttle interrupts Without interrupt throttling, iperf server mode generates a CPU load of 100% (A53 1.2GHz). Also the throughput suffers with less than 900Mbit/s on a 1Gbit/s link. The reason is a high interrupt load with interrupts every ~20us. Reduce interrupt load by throttling of interrupts. Interrupt delay default is 64us. For iperf server mode the CPU load is significantly reduced to ~20% and the throughput reaches the maximum of 941MBit/s. Interrupts are generated every ~140us. RX and TX coalesce can be configured with ethtool. RX coalesce has priority over TX coalesce if the same interrupt is used. Signed-off-by: Gerhard Engleder Reviewed-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/ethernet/engleder/tsnep.h | 4 + drivers/net/ethernet/engleder/tsnep_ethtool.c | 134 ++++++++++++++++++ drivers/net/ethernet/engleder/tsnep_hw.h | 7 + drivers/net/ethernet/engleder/tsnep_main.c | 42 ++++++ 4 files changed, 187 insertions(+) diff --git a/drivers/net/ethernet/engleder/tsnep.h b/drivers/net/ethernet/engleder/tsnep.h index 09a723b827c7..6bb74dd36544 100644 --- a/drivers/net/ethernet/engleder/tsnep.h +++ b/drivers/net/ethernet/engleder/tsnep.h @@ -132,6 +132,8 @@ struct tsnep_queue { int irq; u32 irq_mask; + void __iomem *irq_delay_addr; + u8 irq_delay; }; struct tsnep_adapter { @@ -223,5 +225,7 @@ static inline void tsnep_ethtool_self_test(struct net_device *dev, #endif /* CONFIG_TSNEP_SELFTESTS */ void tsnep_get_system_time(struct tsnep_adapter *adapter, u64 *time); +int tsnep_set_irq_coalesce(struct tsnep_queue *queue, u32 usecs); +u32 tsnep_get_irq_coalesce(struct tsnep_queue *queue); #endif /* _TSNEP_H */ diff --git a/drivers/net/ethernet/engleder/tsnep_ethtool.c b/drivers/net/ethernet/engleder/tsnep_ethtool.c index 517ac8de32bb..b97ba3544708 100644 --- a/drivers/net/ethernet/engleder/tsnep_ethtool.c +++ b/drivers/net/ethernet/engleder/tsnep_ethtool.c @@ -324,7 +324,137 @@ static int tsnep_ethtool_get_ts_info(struct net_device *netdev, return 0; } +static struct tsnep_queue *tsnep_get_queue_with_tx(struct tsnep_adapter *adapter, + int index) +{ + int i; + + for (i = 0; i < adapter->num_queues; i++) { + if (adapter->queue[i].tx) { + if (index == 0) + return &adapter->queue[i]; + + index--; + } + } + + return NULL; +} + +static struct tsnep_queue *tsnep_get_queue_with_rx(struct tsnep_adapter *adapter, + int index) +{ + int i; + + for (i = 0; i < adapter->num_queues; i++) { + if (adapter->queue[i].rx) { + if (index == 0) + return &adapter->queue[i]; + + index--; + } + } + + return NULL; +} + +static int tsnep_ethtool_get_coalesce(struct net_device *netdev, + struct ethtool_coalesce *ec, + struct kernel_ethtool_coalesce *kernel_coal, + struct netlink_ext_ack *extack) +{ + struct tsnep_adapter *adapter = netdev_priv(netdev); + struct tsnep_queue *queue; + + queue = tsnep_get_queue_with_rx(adapter, 0); + if (queue) + ec->rx_coalesce_usecs = tsnep_get_irq_coalesce(queue); + + queue = tsnep_get_queue_with_tx(adapter, 0); + if (queue) + ec->tx_coalesce_usecs = tsnep_get_irq_coalesce(queue); + + return 0; +} + +static int tsnep_ethtool_set_coalesce(struct net_device *netdev, + struct ethtool_coalesce *ec, + struct kernel_ethtool_coalesce *kernel_coal, + struct netlink_ext_ack *extack) +{ + struct tsnep_adapter *adapter = netdev_priv(netdev); + int i; + int retval; + + for (i = 0; i < adapter->num_queues; i++) { + /* RX coalesce has priority for queues with TX and RX */ + if (adapter->queue[i].rx) + retval = tsnep_set_irq_coalesce(&adapter->queue[i], + ec->rx_coalesce_usecs); + else + retval = tsnep_set_irq_coalesce(&adapter->queue[i], + ec->tx_coalesce_usecs); + if (retval != 0) + return retval; + } + + return 0; +} + +static int tsnep_ethtool_get_per_queue_coalesce(struct net_device *netdev, + u32 queue, + struct ethtool_coalesce *ec) +{ + struct tsnep_adapter *adapter = netdev_priv(netdev); + struct tsnep_queue *queue_with_rx; + struct tsnep_queue *queue_with_tx; + + if (queue >= max(adapter->num_tx_queues, adapter->num_rx_queues)) + return -EINVAL; + + queue_with_rx = tsnep_get_queue_with_rx(adapter, queue); + if (queue_with_rx) + ec->rx_coalesce_usecs = tsnep_get_irq_coalesce(queue_with_rx); + + queue_with_tx = tsnep_get_queue_with_tx(adapter, queue); + if (queue_with_tx) + ec->tx_coalesce_usecs = tsnep_get_irq_coalesce(queue_with_tx); + + return 0; +} + +static int tsnep_ethtool_set_per_queue_coalesce(struct net_device *netdev, + u32 queue, + struct ethtool_coalesce *ec) +{ + struct tsnep_adapter *adapter = netdev_priv(netdev); + struct tsnep_queue *queue_with_rx; + struct tsnep_queue *queue_with_tx; + int retval; + + if (queue >= max(adapter->num_tx_queues, adapter->num_rx_queues)) + return -EINVAL; + + queue_with_rx = tsnep_get_queue_with_rx(adapter, queue); + if (queue_with_rx) { + retval = tsnep_set_irq_coalesce(queue_with_rx, ec->rx_coalesce_usecs); + if (retval != 0) + return retval; + } + + /* RX coalesce has priority for queues with TX and RX */ + queue_with_tx = tsnep_get_queue_with_tx(adapter, queue); + if (queue_with_tx && !queue_with_tx->rx) { + retval = tsnep_set_irq_coalesce(queue_with_tx, ec->tx_coalesce_usecs); + if (retval != 0) + return retval; + } + + return 0; +} + const struct ethtool_ops tsnep_ethtool_ops = { + .supported_coalesce_params = ETHTOOL_COALESCE_USECS, .get_drvinfo = tsnep_ethtool_get_drvinfo, .get_regs_len = tsnep_ethtool_get_regs_len, .get_regs = tsnep_ethtool_get_regs, @@ -340,6 +470,10 @@ const struct ethtool_ops tsnep_ethtool_ops = { .set_rxnfc = tsnep_ethtool_set_rxnfc, .get_channels = tsnep_ethtool_get_channels, .get_ts_info = tsnep_ethtool_get_ts_info, + .get_coalesce = tsnep_ethtool_get_coalesce, + .set_coalesce = tsnep_ethtool_set_coalesce, + .get_per_queue_coalesce = tsnep_ethtool_get_per_queue_coalesce, + .set_per_queue_coalesce = tsnep_ethtool_set_per_queue_coalesce, .get_link_ksettings = phy_ethtool_get_link_ksettings, .set_link_ksettings = phy_ethtool_set_link_ksettings, }; diff --git a/drivers/net/ethernet/engleder/tsnep_hw.h b/drivers/net/ethernet/engleder/tsnep_hw.h index 315dada75323..55e1caf193a6 100644 --- a/drivers/net/ethernet/engleder/tsnep_hw.h +++ b/drivers/net/ethernet/engleder/tsnep_hw.h @@ -48,6 +48,13 @@ #define ECM_COUNTER_LOW 0x0028 #define ECM_COUNTER_HIGH 0x002C +/* interrupt delay */ +#define ECM_INT_DELAY 0x0030 +#define ECM_INT_DELAY_MASK 0xF0 +#define ECM_INT_DELAY_SHIFT 4 +#define ECM_INT_DELAY_BASE_US 16 +#define ECM_INT_DELAY_OFFSET 1 + /* control and status */ #define ECM_STATUS 0x0080 #define ECM_LINK_MODE_OFF 0x01000000 diff --git a/drivers/net/ethernet/engleder/tsnep_main.c b/drivers/net/ethernet/engleder/tsnep_main.c index 13d5ff4e0e02..5e0d23dd2d42 100644 --- a/drivers/net/ethernet/engleder/tsnep_main.c +++ b/drivers/net/ethernet/engleder/tsnep_main.c @@ -39,6 +39,10 @@ #endif #define DMA_ADDR_LOW(dma_addr) ((u32)((dma_addr) & 0xFFFFFFFF)) +#define TSNEP_COALESCE_USECS_DEFAULT 64 +#define TSNEP_COALESCE_USECS_MAX ((ECM_INT_DELAY_MASK >> ECM_INT_DELAY_SHIFT) * \ + ECM_INT_DELAY_BASE_US + ECM_INT_DELAY_BASE_US - 1) + static void tsnep_enable_irq(struct tsnep_adapter *adapter, u32 mask) { iowrite32(mask, adapter->addr + ECM_INT_ENABLE); @@ -83,6 +87,33 @@ static irqreturn_t tsnep_irq_txrx(int irq, void *arg) return IRQ_HANDLED; } +int tsnep_set_irq_coalesce(struct tsnep_queue *queue, u32 usecs) +{ + if (usecs > TSNEP_COALESCE_USECS_MAX) + return -ERANGE; + + usecs /= ECM_INT_DELAY_BASE_US; + usecs <<= ECM_INT_DELAY_SHIFT; + usecs &= ECM_INT_DELAY_MASK; + + queue->irq_delay &= ~ECM_INT_DELAY_MASK; + queue->irq_delay |= usecs; + iowrite8(queue->irq_delay, queue->irq_delay_addr); + + return 0; +} + +u32 tsnep_get_irq_coalesce(struct tsnep_queue *queue) +{ + u32 usecs; + + usecs = (queue->irq_delay & ECM_INT_DELAY_MASK); + usecs >>= ECM_INT_DELAY_SHIFT; + usecs *= ECM_INT_DELAY_BASE_US; + + return usecs; +} + static int tsnep_mdiobus_read(struct mii_bus *bus, int addr, int regnum) { struct tsnep_adapter *adapter = bus->priv; @@ -1371,6 +1402,11 @@ static int tsnep_queue_init(struct tsnep_adapter *adapter, int queue_count) adapter->queue[0].tx = &adapter->tx[0]; adapter->queue[0].rx = &adapter->rx[0]; adapter->queue[0].irq_mask = irq_mask; + adapter->queue[0].irq_delay_addr = adapter->addr + ECM_INT_DELAY; + retval = tsnep_set_irq_coalesce(&adapter->queue[0], + TSNEP_COALESCE_USECS_DEFAULT); + if (retval < 0) + return retval; adapter->netdev->irq = adapter->queue[0].irq; @@ -1391,6 +1427,12 @@ static int tsnep_queue_init(struct tsnep_adapter *adapter, int queue_count) adapter->queue[i].rx = &adapter->rx[i]; adapter->queue[i].irq_mask = irq_mask << (ECM_INT_TXRX_SHIFT * i); + adapter->queue[i].irq_delay_addr = + adapter->addr + ECM_INT_DELAY + ECM_INT_DELAY_OFFSET * i; + retval = tsnep_set_irq_coalesce(&adapter->queue[i], + TSNEP_COALESCE_USECS_DEFAULT); + if (retval < 0) + return retval; } return 0; From dbadae92728788d61e910506b0cb61986c7fdb4a Mon Sep 17 00:00:00 2001 From: Gerhard Engleder Date: Wed, 30 Nov 2022 20:37:08 +0100 Subject: [PATCH 1561/2002] tsnep: Rework RX buffer allocation Refill RX queue in batches of descriptors to improve performance. Refill is allowed to fail as long as a minimum number of descriptors is active. Thus, a limited number of failed RX buffer allocations is now allowed for normal operation. Previously every failed allocation resulted in a dropped frame. If the minimum number of active descriptors is reached, then RX buffers are still reused and frames are dropped. This ensures that the RX queue never runs empty and always continues to operate. Prework for future XDP support. Signed-off-by: Gerhard Engleder Signed-off-by: David S. Miller --- drivers/net/ethernet/engleder/tsnep.h | 4 + drivers/net/ethernet/engleder/tsnep_ethtool.c | 7 + drivers/net/ethernet/engleder/tsnep_main.c | 209 +++++++++++------- 3 files changed, 143 insertions(+), 77 deletions(-) diff --git a/drivers/net/ethernet/engleder/tsnep.h b/drivers/net/ethernet/engleder/tsnep.h index 6bb74dd36544..f93ba48bac3f 100644 --- a/drivers/net/ethernet/engleder/tsnep.h +++ b/drivers/net/ethernet/engleder/tsnep.h @@ -18,6 +18,8 @@ #define TSNEP "tsnep" #define TSNEP_RING_SIZE 256 +#define TSNEP_RING_RX_REFILL 16 +#define TSNEP_RING_RX_REUSE (TSNEP_RING_SIZE - TSNEP_RING_SIZE / 4) #define TSNEP_RING_ENTRIES_PER_PAGE (PAGE_SIZE / TSNEP_DESC_SIZE) #define TSNEP_RING_PAGE_COUNT (TSNEP_RING_SIZE / TSNEP_RING_ENTRIES_PER_PAGE) @@ -110,6 +112,7 @@ struct tsnep_rx { dma_addr_t page_dma[TSNEP_RING_PAGE_COUNT]; struct tsnep_rx_entry entry[TSNEP_RING_SIZE]; + int write; int read; u32 owner_counter; int increment_owner_counter; @@ -119,6 +122,7 @@ struct tsnep_rx { u32 bytes; u32 dropped; u32 multicast; + u32 alloc_failed; }; struct tsnep_queue { diff --git a/drivers/net/ethernet/engleder/tsnep_ethtool.c b/drivers/net/ethernet/engleder/tsnep_ethtool.c index b97ba3544708..716815dad7d2 100644 --- a/drivers/net/ethernet/engleder/tsnep_ethtool.c +++ b/drivers/net/ethernet/engleder/tsnep_ethtool.c @@ -8,6 +8,7 @@ static const char tsnep_stats_strings[][ETH_GSTRING_LEN] = { "rx_bytes", "rx_dropped", "rx_multicast", + "rx_alloc_failed", "rx_phy_errors", "rx_forwarded_phy_errors", "rx_invalid_frame_errors", @@ -21,6 +22,7 @@ struct tsnep_stats { u64 rx_bytes; u64 rx_dropped; u64 rx_multicast; + u64 rx_alloc_failed; u64 rx_phy_errors; u64 rx_forwarded_phy_errors; u64 rx_invalid_frame_errors; @@ -36,6 +38,7 @@ static const char tsnep_rx_queue_stats_strings[][ETH_GSTRING_LEN] = { "rx_%d_bytes", "rx_%d_dropped", "rx_%d_multicast", + "rx_%d_alloc_failed", "rx_%d_no_descriptor_errors", "rx_%d_buffer_too_small_errors", "rx_%d_fifo_overflow_errors", @@ -47,6 +50,7 @@ struct tsnep_rx_queue_stats { u64 rx_bytes; u64 rx_dropped; u64 rx_multicast; + u64 rx_alloc_failed; u64 rx_no_descriptor_errors; u64 rx_buffer_too_small_errors; u64 rx_fifo_overflow_errors; @@ -178,6 +182,7 @@ static void tsnep_ethtool_get_ethtool_stats(struct net_device *netdev, tsnep_stats.rx_bytes += adapter->rx[i].bytes; tsnep_stats.rx_dropped += adapter->rx[i].dropped; tsnep_stats.rx_multicast += adapter->rx[i].multicast; + tsnep_stats.rx_alloc_failed += adapter->rx[i].alloc_failed; } reg = ioread32(adapter->addr + ECM_STAT); tsnep_stats.rx_phy_errors = @@ -200,6 +205,8 @@ static void tsnep_ethtool_get_ethtool_stats(struct net_device *netdev, tsnep_rx_queue_stats.rx_bytes = adapter->rx[i].bytes; tsnep_rx_queue_stats.rx_dropped = adapter->rx[i].dropped; tsnep_rx_queue_stats.rx_multicast = adapter->rx[i].multicast; + tsnep_rx_queue_stats.rx_alloc_failed = + adapter->rx[i].alloc_failed; reg = ioread32(adapter->addr + TSNEP_QUEUE(i) + TSNEP_RX_STATISTIC); tsnep_rx_queue_stats.rx_no_descriptor_errors = diff --git a/drivers/net/ethernet/engleder/tsnep_main.c b/drivers/net/ethernet/engleder/tsnep_main.c index 5e0d23dd2d42..bf0190e1d2ea 100644 --- a/drivers/net/ethernet/engleder/tsnep_main.c +++ b/drivers/net/ethernet/engleder/tsnep_main.c @@ -660,23 +660,6 @@ static void tsnep_rx_ring_cleanup(struct tsnep_rx *rx) } } -static int tsnep_rx_alloc_buffer(struct tsnep_rx *rx, - struct tsnep_rx_entry *entry) -{ - struct page *page; - - page = page_pool_dev_alloc_pages(rx->page_pool); - if (unlikely(!page)) - return -ENOMEM; - - entry->page = page; - entry->len = TSNEP_MAX_RX_BUF_SIZE; - entry->dma = page_pool_get_dma_addr(entry->page); - entry->desc->rx = __cpu_to_le64(entry->dma + TSNEP_SKB_PAD); - - return 0; -} - static int tsnep_rx_ring_init(struct tsnep_rx *rx) { struct device *dmadev = rx->adapter->dmadev; @@ -723,10 +706,6 @@ static int tsnep_rx_ring_init(struct tsnep_rx *rx) entry = &rx->entry[i]; next_entry = &rx->entry[(i + 1) % TSNEP_RING_SIZE]; entry->desc->next = __cpu_to_le64(next_entry->desc_dma); - - retval = tsnep_rx_alloc_buffer(rx, entry); - if (retval) - goto failed; } return 0; @@ -736,6 +715,45 @@ failed: return retval; } +static int tsnep_rx_desc_available(struct tsnep_rx *rx) +{ + if (rx->read <= rx->write) + return TSNEP_RING_SIZE - rx->write + rx->read - 1; + else + return rx->read - rx->write - 1; +} + +static void tsnep_rx_set_page(struct tsnep_rx *rx, struct tsnep_rx_entry *entry, + struct page *page) +{ + entry->page = page; + entry->len = TSNEP_MAX_RX_BUF_SIZE; + entry->dma = page_pool_get_dma_addr(entry->page); + entry->desc->rx = __cpu_to_le64(entry->dma + TSNEP_SKB_PAD); +} + +static int tsnep_rx_alloc_buffer(struct tsnep_rx *rx, int index) +{ + struct tsnep_rx_entry *entry = &rx->entry[index]; + struct page *page; + + page = page_pool_dev_alloc_pages(rx->page_pool); + if (unlikely(!page)) + return -ENOMEM; + tsnep_rx_set_page(rx, entry, page); + + return 0; +} + +static void tsnep_rx_reuse_buffer(struct tsnep_rx *rx, int index) +{ + struct tsnep_rx_entry *entry = &rx->entry[index]; + struct tsnep_rx_entry *read = &rx->entry[rx->read]; + + tsnep_rx_set_page(rx, entry, read->page); + read->page = NULL; +} + static void tsnep_rx_activate(struct tsnep_rx *rx, int index) { struct tsnep_rx_entry *entry = &rx->entry[index]; @@ -763,6 +781,48 @@ static void tsnep_rx_activate(struct tsnep_rx *rx, int index) entry->desc->properties = __cpu_to_le32(entry->properties); } +static int tsnep_rx_refill(struct tsnep_rx *rx, int count, bool reuse) +{ + int index; + bool alloc_failed = false; + bool enable = false; + int i; + int retval; + + for (i = 0; i < count && !alloc_failed; i++) { + index = (rx->write + i) % TSNEP_RING_SIZE; + + retval = tsnep_rx_alloc_buffer(rx, index); + if (unlikely(retval)) { + rx->alloc_failed++; + alloc_failed = true; + + /* reuse only if no other allocation was successful */ + if (i == 0 && reuse) + tsnep_rx_reuse_buffer(rx, index); + else + break; + } + + tsnep_rx_activate(rx, index); + + enable = true; + } + + if (enable) { + rx->write = (rx->write + i) % TSNEP_RING_SIZE; + + /* descriptor properties shall be valid before hardware is + * notified + */ + dma_wmb(); + + iowrite32(TSNEP_CONTROL_RX_ENABLE, rx->addr + TSNEP_CONTROL); + } + + return i; +} + static struct sk_buff *tsnep_build_skb(struct tsnep_rx *rx, struct page *page, int length) { @@ -798,23 +858,42 @@ static int tsnep_rx_poll(struct tsnep_rx *rx, struct napi_struct *napi, int budget) { struct device *dmadev = rx->adapter->dmadev; + int desc_available; int done = 0; enum dma_data_direction dma_dir; struct tsnep_rx_entry *entry; - struct page *page; struct sk_buff *skb; int length; - bool enable = false; - int retval; + desc_available = tsnep_rx_desc_available(rx); dma_dir = page_pool_get_dma_dir(rx->page_pool); - while (likely(done < budget)) { + while (likely(done < budget) && (rx->read != rx->write)) { entry = &rx->entry[rx->read]; if ((__le32_to_cpu(entry->desc_wb->properties) & TSNEP_DESC_OWNER_COUNTER_MASK) != (entry->properties & TSNEP_DESC_OWNER_COUNTER_MASK)) break; + done++; + + if (desc_available >= TSNEP_RING_RX_REFILL) { + bool reuse = desc_available >= TSNEP_RING_RX_REUSE; + + desc_available -= tsnep_rx_refill(rx, desc_available, + reuse); + if (!entry->page) { + /* buffer has been reused for refill to prevent + * empty RX ring, thus buffer cannot be used for + * RX processing + */ + rx->read = (rx->read + 1) % TSNEP_RING_SIZE; + desc_available++; + + rx->dropped++; + + continue; + } + } /* descriptor properties shall be read first, because valid data * is signaled there @@ -826,49 +905,30 @@ static int tsnep_rx_poll(struct tsnep_rx *rx, struct napi_struct *napi, TSNEP_DESC_LENGTH_MASK; dma_sync_single_range_for_cpu(dmadev, entry->dma, TSNEP_SKB_PAD, length, dma_dir); - page = entry->page; - - /* forward skb only if allocation is successful, otherwise - * page is reused and frame dropped - */ - retval = tsnep_rx_alloc_buffer(rx, entry); - if (!retval) { - skb = tsnep_build_skb(rx, page, length); - if (skb) { - page_pool_release_page(rx->page_pool, page); - - rx->packets++; - rx->bytes += length - - TSNEP_RX_INLINE_METADATA_SIZE; - if (skb->pkt_type == PACKET_MULTICAST) - rx->multicast++; - - napi_gro_receive(napi, skb); - } else { - page_pool_recycle_direct(rx->page_pool, page); - - rx->dropped++; - } - done++; - } else { - rx->dropped++; - } - - tsnep_rx_activate(rx, rx->read); - - enable = true; rx->read = (rx->read + 1) % TSNEP_RING_SIZE; + desc_available++; + + skb = tsnep_build_skb(rx, entry->page, length); + if (skb) { + page_pool_release_page(rx->page_pool, entry->page); + + rx->packets++; + rx->bytes += length - TSNEP_RX_INLINE_METADATA_SIZE; + if (skb->pkt_type == PACKET_MULTICAST) + rx->multicast++; + + napi_gro_receive(napi, skb); + } else { + page_pool_recycle_direct(rx->page_pool, entry->page); + + rx->dropped++; + } + entry->page = NULL; } - if (enable) { - /* descriptor properties shall be valid before hardware is - * notified - */ - dma_wmb(); - - iowrite32(TSNEP_CONTROL_RX_ENABLE, rx->addr + TSNEP_CONTROL); - } + if (desc_available) + tsnep_rx_refill(rx, desc_available, false); return done; } @@ -877,11 +937,13 @@ static bool tsnep_rx_pending(struct tsnep_rx *rx) { struct tsnep_rx_entry *entry; - entry = &rx->entry[rx->read]; - if ((__le32_to_cpu(entry->desc_wb->properties) & - TSNEP_DESC_OWNER_COUNTER_MASK) == - (entry->properties & TSNEP_DESC_OWNER_COUNTER_MASK)) - return true; + if (rx->read != rx->write) { + entry = &rx->entry[rx->read]; + if ((__le32_to_cpu(entry->desc_wb->properties) & + TSNEP_DESC_OWNER_COUNTER_MASK) == + (entry->properties & TSNEP_DESC_OWNER_COUNTER_MASK)) + return true; + } return false; } @@ -890,7 +952,6 @@ static int tsnep_rx_open(struct tsnep_adapter *adapter, void __iomem *addr, int queue_index, struct tsnep_rx *rx) { dma_addr_t dma; - int i; int retval; memset(rx, 0, sizeof(*rx)); @@ -908,13 +969,7 @@ static int tsnep_rx_open(struct tsnep_adapter *adapter, void __iomem *addr, rx->owner_counter = 1; rx->increment_owner_counter = TSNEP_RING_SIZE - 1; - for (i = 0; i < TSNEP_RING_SIZE; i++) - tsnep_rx_activate(rx, i); - - /* descriptor properties shall be valid before hardware is notified */ - dma_wmb(); - - iowrite32(TSNEP_CONTROL_RX_ENABLE, rx->addr + TSNEP_CONTROL); + tsnep_rx_refill(rx, tsnep_rx_desc_available(rx), false); return 0; } From d03407183d97554dfffea70f385b5bdd520f846c Mon Sep 17 00:00:00 2001 From: Kalle Valo Date: Fri, 2 Dec 2022 12:30:27 +0200 Subject: [PATCH 1562/2002] wifi: ath10k: fix QCOM_SMEM dependency Nathan noticed that when HWSPINLOCK is disabled there's a Kconfig warning: WARNING: unmet direct dependencies detected for QCOM_SMEM Depends on [n]: (ARCH_QCOM [=y] || COMPILE_TEST [=n]) && HWSPINLOCK [=n] Selected by [m]: - ATH10K_SNOC [=m] && NETDEVICES [=y] && WLAN [=y] && WLAN_VENDOR_ATH [=y] && ATH10K [=m] && (ARCH_QCOM [=y] || COMPILE_TEST [=n]) The problem here is that QCOM_SMEM depends on HWSPINLOCK so we cannot select QCOM_SMEM and instead we neeed to use 'depends on'. Reported-by: Nathan Chancellor Link: https://lore.kernel.org/all/Y4YsyaIW+CPdHWv3@dev-arch.thelio-3990X/ Fixes: 4d79f6f34bbb ("wifi: ath10k: Store WLAN firmware version in SMEM image table") Signed-off-by: Kalle Valo Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20221202103027.25974-1-kvalo@kernel.org --- drivers/net/wireless/ath/ath10k/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath10k/Kconfig b/drivers/net/wireless/ath/ath10k/Kconfig index e0a51dad8e42..e6ea884cafc1 100644 --- a/drivers/net/wireless/ath/ath10k/Kconfig +++ b/drivers/net/wireless/ath/ath10k/Kconfig @@ -44,7 +44,7 @@ config ATH10K_SNOC tristate "Qualcomm ath10k SNOC support" depends on ATH10K depends on ARCH_QCOM || COMPILE_TEST - select QCOM_SMEM + depends on QCOM_SMEM select QCOM_SCM select QCOM_QMI_HELPERS help From bc067cacb69c6ee0da10d357458fa7f1fe54e472 Mon Sep 17 00:00:00 2001 From: Zheng Yejian Date: Tue, 29 Nov 2022 21:45:58 +0800 Subject: [PATCH 1563/2002] bpf, docs: Correct the example of BPF_XOR Refer to description of BPF_XOR, dst_reg should be used but not src_reg in the examples. Fixes: be3193cded9d ("bpf, docs: Add subsections for ALU and JMP instructions") Signed-off-by: Zheng Yejian Signed-off-by: Daniel Borkmann Link: https://lore.kernel.org/bpf/20221129134558.2757043-1-zhengyejian1@huawei.com --- Documentation/bpf/instruction-set.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Documentation/bpf/instruction-set.rst b/Documentation/bpf/instruction-set.rst index 5d798437dad4..e672d5ec6cc7 100644 --- a/Documentation/bpf/instruction-set.rst +++ b/Documentation/bpf/instruction-set.rst @@ -122,11 +122,11 @@ BPF_END 0xd0 byte swap operations (see `Byte swap instructions`_ below) ``BPF_XOR | BPF_K | BPF_ALU`` means:: - src_reg = (u32) src_reg ^ (u32) imm32 + dst_reg = (u32) dst_reg ^ (u32) imm32 ``BPF_XOR | BPF_K | BPF_ALU64`` means:: - src_reg = src_reg ^ imm32 + dst_reg = dst_reg ^ imm32 Byte swap instructions From f16a7aa5c2be3134d3b72078b94f36d639552888 Mon Sep 17 00:00:00 2001 From: James Hilliard Date: Thu, 1 Dec 2022 12:09:39 -0700 Subject: [PATCH 1564/2002] selftests/bpf: Add GCC compatible builtins to bpf_legacy.h The bpf_legacy.h header uses llvm specific load functions, add GCC compatible variants as well to fix tests using these functions under GCC. Signed-off-by: James Hilliard Signed-off-by: Andrii Nakryiko Link: https://lore.kernel.org/bpf/20221201190939.3230513-1-james.hilliard1@gmail.com --- tools/testing/selftests/bpf/bpf_legacy.h | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/tools/testing/selftests/bpf/bpf_legacy.h b/tools/testing/selftests/bpf/bpf_legacy.h index 845209581440..bc4555a003a7 100644 --- a/tools/testing/selftests/bpf/bpf_legacy.h +++ b/tools/testing/selftests/bpf/bpf_legacy.h @@ -2,15 +2,22 @@ #ifndef __BPF_LEGACY__ #define __BPF_LEGACY__ +#if __GNUC__ && !__clang__ +/* Functions to emit BPF_LD_ABS and BPF_LD_IND instructions. We + * provide the "standard" names as synonyms of the corresponding GCC + * builtins. Note how the SKB argument is ignored. + */ +#define load_byte(skb, off) __builtin_bpf_load_byte(off) +#define load_half(skb, off) __builtin_bpf_load_half(off) +#define load_word(skb, off) __builtin_bpf_load_word(off) +#else /* llvm builtin functions that eBPF C program may use to * emit BPF_LD_ABS and BPF_LD_IND instructions */ -unsigned long long load_byte(void *skb, - unsigned long long off) asm("llvm.bpf.load.byte"); -unsigned long long load_half(void *skb, - unsigned long long off) asm("llvm.bpf.load.half"); -unsigned long long load_word(void *skb, - unsigned long long off) asm("llvm.bpf.load.word"); +unsigned long long load_byte(void *skb, unsigned long long off) asm("llvm.bpf.load.byte"); +unsigned long long load_half(void *skb, unsigned long long off) asm("llvm.bpf.load.half"); +unsigned long long load_word(void *skb, unsigned long long off) asm("llvm.bpf.load.word"); +#endif #endif From 706819495921ddad6b3780140b9d9e9293b6dedc Mon Sep 17 00:00:00 2001 From: Xin Liu Date: Fri, 2 Dec 2022 16:17:38 +0800 Subject: [PATCH 1565/2002] libbpf: Improve usability of libbpf Makefile Current libbpf Makefile does not contain the help command, which is inconvenient to use. Similar to the Makefile help command of the perf, a help command is provided to list the commands supported by libbpf make and the functions of the commands. Signed-off-by: Xin Liu Signed-off-by: Andrii Nakryiko Link: https://lore.kernel.org/bpf/20221202081738.128513-1-liuxin350@huawei.com --- tools/lib/bpf/Makefile | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/tools/lib/bpf/Makefile b/tools/lib/bpf/Makefile index 4c904ef0b47e..477666f3d496 100644 --- a/tools/lib/bpf/Makefile +++ b/tools/lib/bpf/Makefile @@ -286,3 +286,20 @@ tags: # Delete partially updated (corrupted) files on error .DELETE_ON_ERROR: + +help: + @echo 'libbpf common targets:' + @echo ' HINT: use "V=1" to enable verbose build' + @echo ' all - build libraries and pkgconfig' + @echo ' clean - remove all generated files' + @echo ' check - check abi and version info' + @echo '' + @echo 'libbpf install targets:' + @echo ' HINT: use "prefix"(defaults to "/usr/local") or "DESTDIR" (defaults to "/")' + @echo ' to adjust target desitantion, e.g. "make prefix=/usr/local install"' + @echo ' install - build and install all headers, libraries and pkgconfig' + @echo ' install_headers - install only headers to include/bpf' + @echo '' + @echo 'libbpf make targets:' + @echo ' tags - use ctags to make tag information for source code browsing' + @echo ' cscope - use cscope to make interactive source code browsing database' From 55fb80d518c7323d05b71eda0c9f9d657b373816 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Fri, 2 Dec 2022 05:28:47 +0000 Subject: [PATCH 1566/2002] tcp: use 2-arg optimal variant of kfree_rcu() kfree_rcu(1-arg) should be avoided as much as possible, since this is only possible from sleepable contexts, and incurr extra rcu barriers. I wish the 1-arg variant of kfree_rcu() would get a distinct name, like kfree_rcu_slow() to avoid it being abused. Fixes: 459837b522f7 ("net/tcp: Disable TCP-MD5 static key on tcp_md5sig_info destruction") Signed-off-by: Eric Dumazet Cc: Paul E. McKenney Reviewed-by: Pavan Chebbi Reviewed-by: Dmitry Safonov Link: https://lore.kernel.org/r/20221202052847.2623997-1-edumazet@google.com Signed-off-by: Jakub Kicinski --- net/ipv4/tcp_ipv4.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 7fae586405cf..8320d0ecb13a 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -1245,7 +1245,7 @@ int tcp_md5_do_add(struct sock *sk, const union tcp_md5_addr *addr, md5sig = rcu_dereference_protected(tp->md5sig_info, lockdep_sock_is_held(sk)); rcu_assign_pointer(tp->md5sig_info, NULL); - kfree_rcu(md5sig); + kfree_rcu(md5sig, rcu); return -EUSERS; } } @@ -1271,7 +1271,7 @@ int tcp_md5_key_copy(struct sock *sk, const union tcp_md5_addr *addr, md5sig = rcu_dereference_protected(tp->md5sig_info, lockdep_sock_is_held(sk)); net_warn_ratelimited("Too many TCP-MD5 keys in the system\n"); rcu_assign_pointer(tp->md5sig_info, NULL); - kfree_rcu(md5sig); + kfree_rcu(md5sig, rcu); return -EUSERS; } } From 65e6af6cebefbf7d8d8ac52b71cd251c2071ad00 Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Thu, 1 Dec 2022 16:26:53 +0100 Subject: [PATCH 1567/2002] net: ethernet: mtk_wed: fix sleep while atomic in mtk_wed_wo_queue_refill In order to fix the following sleep while atomic bug always alloc pages with GFP_ATOMIC in mtk_wed_wo_queue_refill since page_frag_alloc runs in spin_lock critical section. [ 9.049719] Hardware name: MediaTek MT7986a RFB (DT) [ 9.054665] Call trace: [ 9.057096] dump_backtrace+0x0/0x154 [ 9.060751] show_stack+0x14/0x1c [ 9.064052] dump_stack_lvl+0x64/0x7c [ 9.067702] dump_stack+0x14/0x2c [ 9.071001] ___might_sleep+0xec/0x120 [ 9.074736] __might_sleep+0x4c/0x9c [ 9.078296] __alloc_pages+0x184/0x2e4 [ 9.082030] page_frag_alloc_align+0x98/0x1ac [ 9.086369] mtk_wed_wo_queue_refill+0x134/0x234 [ 9.090974] mtk_wed_wo_init+0x174/0x2c0 [ 9.094881] mtk_wed_attach+0x7c8/0x7e0 [ 9.098701] mt7915_mmio_wed_init+0x1f0/0x3a0 [mt7915e] [ 9.103940] mt7915_pci_probe+0xec/0x3bc [mt7915e] [ 9.108727] pci_device_probe+0xac/0x13c [ 9.112638] really_probe.part.0+0x98/0x2f4 [ 9.116807] __driver_probe_device+0x94/0x13c [ 9.121147] driver_probe_device+0x40/0x114 [ 9.125314] __driver_attach+0x7c/0x180 [ 9.129133] bus_for_each_dev+0x5c/0x90 [ 9.132953] driver_attach+0x20/0x2c [ 9.136513] bus_add_driver+0x104/0x1fc [ 9.140333] driver_register+0x74/0x120 [ 9.144153] __pci_register_driver+0x40/0x50 [ 9.148407] mt7915_init+0x5c/0x1000 [mt7915e] [ 9.152848] do_one_initcall+0x40/0x25c [ 9.156669] do_init_module+0x44/0x230 [ 9.160403] load_module+0x1f30/0x2750 [ 9.164135] __do_sys_init_module+0x150/0x200 [ 9.168475] __arm64_sys_init_module+0x18/0x20 [ 9.172901] invoke_syscall.constprop.0+0x4c/0xe0 [ 9.177589] do_el0_svc+0x48/0xe0 [ 9.180889] el0_svc+0x14/0x50 [ 9.183929] el0t_64_sync_handler+0x9c/0x120 [ 9.188183] el0t_64_sync+0x158/0x15c Fixes: 799684448e3e ("net: ethernet: mtk_wed: introduce wed wo support") Signed-off-by: Lorenzo Bianconi Reviewed-by: Pavan Chebbi Link: https://lore.kernel.org/r/67ca94bdd3d9eaeb86e52b3050fbca0bcf7bb02f.1669908312.git.lorenzo@kernel.org Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/mediatek/mtk_wed_wo.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/drivers/net/ethernet/mediatek/mtk_wed_wo.c b/drivers/net/ethernet/mediatek/mtk_wed_wo.c index 4754b6db009e..a219da85f4db 100644 --- a/drivers/net/ethernet/mediatek/mtk_wed_wo.c +++ b/drivers/net/ethernet/mediatek/mtk_wed_wo.c @@ -133,17 +133,18 @@ mtk_wed_wo_dequeue(struct mtk_wed_wo *wo, struct mtk_wed_wo_queue *q, u32 *len, static int mtk_wed_wo_queue_refill(struct mtk_wed_wo *wo, struct mtk_wed_wo_queue *q, - gfp_t gfp, bool rx) + bool rx) { enum dma_data_direction dir = rx ? DMA_FROM_DEVICE : DMA_TO_DEVICE; int n_buf = 0; spin_lock_bh(&q->lock); while (q->queued < q->n_desc) { - void *buf = page_frag_alloc(&q->cache, q->buf_size, gfp); struct mtk_wed_wo_queue_entry *entry; dma_addr_t addr; + void *buf; + buf = page_frag_alloc(&q->cache, q->buf_size, GFP_ATOMIC); if (!buf) break; @@ -215,7 +216,7 @@ mtk_wed_wo_rx_run_queue(struct mtk_wed_wo *wo, struct mtk_wed_wo_queue *q) mtk_wed_mcu_rx_unsolicited_event(wo, skb); } - if (mtk_wed_wo_queue_refill(wo, q, GFP_ATOMIC, true)) { + if (mtk_wed_wo_queue_refill(wo, q, true)) { u32 index = (q->head - 1) % q->n_desc; mtk_wed_wo_queue_kick(wo, q, index); @@ -432,7 +433,7 @@ mtk_wed_wo_hardware_init(struct mtk_wed_wo *wo) if (ret) goto error; - mtk_wed_wo_queue_refill(wo, &wo->q_tx, GFP_KERNEL, false); + mtk_wed_wo_queue_refill(wo, &wo->q_tx, false); mtk_wed_wo_queue_reset(wo, &wo->q_tx); regs.desc_base = MTK_WED_WO_CCIF_DUMMY5; @@ -446,7 +447,7 @@ mtk_wed_wo_hardware_init(struct mtk_wed_wo *wo) if (ret) goto error; - mtk_wed_wo_queue_refill(wo, &wo->q_rx, GFP_KERNEL, true); + mtk_wed_wo_queue_refill(wo, &wo->q_rx, true); mtk_wed_wo_queue_reset(wo, &wo->q_rx); /* rx queue irqmask */ From d93607082e982223cf92750f2d9039ff365b9d24 Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Wed, 30 Nov 2022 23:28:26 +0100 Subject: [PATCH 1568/2002] net: add netdev_sw_irq_coalesce_default_on() Add a helper for drivers wanting to set SW IRQ coalescing by default. The related sysfs attributes can be used to override the default values. Follow Jakub's suggestion and put this functionality into net core so that drivers wanting to use software interrupt coalescing per default don't have to open-code it. Note that this function needs to be called before the netdevice is registered. Suggested-by: Jakub Kicinski Signed-off-by: Heiner Kallweit Signed-off-by: David S. Miller --- include/linux/netdevice.h | 1 + net/core/dev.c | 16 ++++++++++++++++ 2 files changed, 17 insertions(+) diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 5aa35c58c342..f78db610ada5 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -78,6 +78,7 @@ struct xdp_buff; void synchronize_net(void); void netdev_set_default_ethtool_ops(struct net_device *dev, const struct ethtool_ops *ops); +void netdev_sw_irq_coalesce_default_on(struct net_device *dev); /* Backlog congestion levels */ #define NET_RX_SUCCESS 0 /* keep 'em coming, baby */ diff --git a/net/core/dev.c b/net/core/dev.c index 7627c475d991..b76fb37b381e 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -10517,6 +10517,22 @@ void netdev_set_default_ethtool_ops(struct net_device *dev, } EXPORT_SYMBOL_GPL(netdev_set_default_ethtool_ops); +/** + * netdev_sw_irq_coalesce_default_on() - enable SW IRQ coalescing by default + * @dev: netdev to enable the IRQ coalescing on + * + * Sets a conservative default for SW IRQ coalescing. Users can use + * sysfs attributes to override the default values. + */ +void netdev_sw_irq_coalesce_default_on(struct net_device *dev) +{ + WARN_ON(dev->reg_state == NETREG_REGISTERED); + + dev->gro_flush_timeout = 20000; + dev->napi_defer_hard_irqs = 1; +} +EXPORT_SYMBOL_GPL(netdev_sw_irq_coalesce_default_on); + void netdev_freemem(struct net_device *dev) { char *addr = (char *)dev - dev->padded; From 42f66a44d83715bef810a543dfd66008b883a7a5 Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Wed, 30 Nov 2022 23:30:15 +0100 Subject: [PATCH 1569/2002] r8169: enable GRO software interrupt coalescing per default There are reports about r8169 not reaching full line speed on certain systems (e.g. SBC's) with a 2.5Gbps link. There was a time when hardware interrupt coalescing was enabled per default, but this was changed due to ASPM-related issues on few systems. So let's use software interrupt coalescing instead and enable it using new function netdev_sw_irq_coalesce_default_on(). Even with these conservative settings interrupt load on my 1Gbps test system reduced significantly. Signed-off-by: Heiner Kallweit Signed-off-by: David S. Miller --- drivers/net/ethernet/realtek/r8169_main.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/ethernet/realtek/r8169_main.c b/drivers/net/ethernet/realtek/r8169_main.c index ec157885da13..a9dcc98b6af1 100644 --- a/drivers/net/ethernet/realtek/r8169_main.c +++ b/drivers/net/ethernet/realtek/r8169_main.c @@ -5283,6 +5283,8 @@ static int rtl_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) dev->hw_features |= NETIF_F_RXALL; dev->hw_features |= NETIF_F_RXFCS; + netdev_sw_irq_coalesce_default_on(dev); + /* configure chip for default features */ rtl8169_set_features(dev, dev->features); From fca1aa75518c03b04c3c249e9a9134faf9ca18c5 Mon Sep 17 00:00:00 2001 From: Yonghong Song Date: Sat, 3 Dec 2022 10:46:02 -0800 Subject: [PATCH 1570/2002] bpf: Handle MEM_RCU type properly Commit 9bb00b2895cb ("bpf: Add kfunc bpf_rcu_read_lock/unlock()") introduced MEM_RCU and bpf_rcu_read_lock/unlock() support. In that commit, a rcu pointer is tagged with both MEM_RCU and PTR_TRUSTED so that it can be passed into kfuncs or helpers as an argument. Martin raised a good question in [1] such that the rcu pointer, although being able to accessing the object, might have reference count of 0. This might cause a problem if the rcu pointer is passed to a kfunc which expects trusted arguments where ref count should be greater than 0. This patch makes the following changes related to MEM_RCU pointer: - MEM_RCU pointer might be NULL (PTR_MAYBE_NULL). - Introduce KF_RCU so MEM_RCU ptr can be acquired with a KF_RCU tagged kfunc which assumes ref count of rcu ptr could be zero. - For mem access 'b = ptr->a', say 'ptr' is a MEM_RCU ptr, and 'a' is tagged with __rcu as well. Let us mark 'b' as MEM_RCU | PTR_MAYBE_NULL. [1] https://lore.kernel.org/bpf/ac70f574-4023-664e-b711-e0d3b18117fd@linux.dev/ Fixes: 9bb00b2895cb ("bpf: Add kfunc bpf_rcu_read_lock/unlock()") Signed-off-by: Yonghong Song Link: https://lore.kernel.org/r/20221203184602.477272-1-yhs@fb.com Signed-off-by: Alexei Starovoitov --- include/linux/bpf_verifier.h | 2 +- include/linux/btf.h | 1 + kernel/bpf/helpers.c | 14 +++++++++++ kernel/bpf/verifier.c | 45 +++++++++++++++++++++++++----------- 4 files changed, 48 insertions(+), 14 deletions(-) diff --git a/include/linux/bpf_verifier.h b/include/linux/bpf_verifier.h index b5090e89cb3f..c07b351a5bc7 100644 --- a/include/linux/bpf_verifier.h +++ b/include/linux/bpf_verifier.h @@ -682,7 +682,7 @@ static inline bool bpf_prog_check_recur(const struct bpf_prog *prog) } } -#define BPF_REG_TRUSTED_MODIFIERS (MEM_ALLOC | MEM_RCU | PTR_TRUSTED) +#define BPF_REG_TRUSTED_MODIFIERS (MEM_ALLOC | PTR_TRUSTED) static inline bool bpf_type_has_unsafe_modifiers(u32 type) { diff --git a/include/linux/btf.h b/include/linux/btf.h index 9ed00077db6e..cbd6e4096f8c 100644 --- a/include/linux/btf.h +++ b/include/linux/btf.h @@ -70,6 +70,7 @@ #define KF_TRUSTED_ARGS (1 << 4) /* kfunc only takes trusted pointer arguments */ #define KF_SLEEPABLE (1 << 5) /* kfunc may sleep */ #define KF_DESTRUCTIVE (1 << 6) /* kfunc performs destructive actions */ +#define KF_RCU (1 << 7) /* kfunc only takes rcu pointer arguments */ /* * Return the name of the passed struct, if exists, or halt the build if for diff --git a/kernel/bpf/helpers.c b/kernel/bpf/helpers.c index a5a511430f2a..cca642358e80 100644 --- a/kernel/bpf/helpers.c +++ b/kernel/bpf/helpers.c @@ -1837,6 +1837,19 @@ struct task_struct *bpf_task_acquire(struct task_struct *p) return p; } +/** + * bpf_task_acquire_not_zero - Acquire a reference to a rcu task object. A task + * acquired by this kfunc which is not stored in a map as a kptr, must be + * released by calling bpf_task_release(). + * @p: The task on which a reference is being acquired. + */ +struct task_struct *bpf_task_acquire_not_zero(struct task_struct *p) +{ + if (!refcount_inc_not_zero(&p->rcu_users)) + return NULL; + return p; +} + /** * bpf_task_kptr_get - Acquire a reference on a struct task_struct kptr. A task * kptr acquired by this kfunc which is not subsequently stored in a map, must @@ -2013,6 +2026,7 @@ BTF_ID_FLAGS(func, bpf_list_push_back) BTF_ID_FLAGS(func, bpf_list_pop_front, KF_ACQUIRE | KF_RET_NULL) BTF_ID_FLAGS(func, bpf_list_pop_back, KF_ACQUIRE | KF_RET_NULL) BTF_ID_FLAGS(func, bpf_task_acquire, KF_ACQUIRE | KF_TRUSTED_ARGS) +BTF_ID_FLAGS(func, bpf_task_acquire_not_zero, KF_ACQUIRE | KF_RCU | KF_RET_NULL) BTF_ID_FLAGS(func, bpf_task_kptr_get, KF_ACQUIRE | KF_KPTR_GET | KF_RET_NULL) BTF_ID_FLAGS(func, bpf_task_release, KF_RELEASE) #ifdef CONFIG_CGROUPS diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index b0db9c10567b..66b82f52b5bc 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -4275,7 +4275,7 @@ static bool is_trusted_reg(const struct bpf_reg_state *reg) return true; /* If a register is not referenced, it is trusted if it has the - * MEM_ALLOC, MEM_RCU or PTR_TRUSTED type modifiers, and no others. Some of the + * MEM_ALLOC or PTR_TRUSTED type modifiers, and no others. Some of the * other type modifiers may be safe, but we elect to take an opt-in * approach here as some (e.g. PTR_UNTRUSTED and PTR_MAYBE_NULL) are * not. @@ -4287,6 +4287,11 @@ static bool is_trusted_reg(const struct bpf_reg_state *reg) !bpf_type_has_unsafe_modifiers(reg->type); } +static bool is_rcu_reg(const struct bpf_reg_state *reg) +{ + return reg->type & MEM_RCU; +} + static int check_pkt_ptr_alignment(struct bpf_verifier_env *env, const struct bpf_reg_state *reg, int off, int size, bool strict) @@ -4785,14 +4790,16 @@ static int check_ptr_to_btf_access(struct bpf_verifier_env *env, if (flag & MEM_RCU) { /* Mark value register as MEM_RCU only if it is protected by - * bpf_rcu_read_lock() and the ptr reg is trusted. MEM_RCU + * bpf_rcu_read_lock() and the ptr reg is rcu or trusted. MEM_RCU * itself can already indicate trustedness inside the rcu - * read lock region. Also mark it as PTR_TRUSTED. + * read lock region. Also mark rcu pointer as PTR_MAYBE_NULL since + * it could be null in some cases. */ - if (!env->cur_state->active_rcu_lock || !is_trusted_reg(reg)) + if (!env->cur_state->active_rcu_lock || + !(is_trusted_reg(reg) || is_rcu_reg(reg))) flag &= ~MEM_RCU; else - flag |= PTR_TRUSTED; + flag |= PTR_MAYBE_NULL; } else if (reg->type & MEM_RCU) { /* ptr (reg) is marked as MEM_RCU, but the struct field is not tagged * with __rcu. Mark the flag as PTR_UNTRUSTED conservatively. @@ -5957,7 +5964,7 @@ static const struct bpf_reg_types btf_ptr_types = { .types = { PTR_TO_BTF_ID, PTR_TO_BTF_ID | PTR_TRUSTED, - PTR_TO_BTF_ID | MEM_RCU | PTR_TRUSTED, + PTR_TO_BTF_ID | MEM_RCU, }, }; static const struct bpf_reg_types percpu_btf_ptr_types = { @@ -6136,7 +6143,7 @@ int check_func_arg_reg_off(struct bpf_verifier_env *env, case PTR_TO_BTF_ID: case PTR_TO_BTF_ID | MEM_ALLOC: case PTR_TO_BTF_ID | PTR_TRUSTED: - case PTR_TO_BTF_ID | MEM_RCU | PTR_TRUSTED: + case PTR_TO_BTF_ID | MEM_RCU: case PTR_TO_BTF_ID | MEM_ALLOC | PTR_TRUSTED: /* When referenced PTR_TO_BTF_ID is passed to release function, * it's fixed offset must be 0. In the other cases, fixed offset @@ -8038,6 +8045,11 @@ static bool is_kfunc_destructive(struct bpf_kfunc_call_arg_meta *meta) return meta->kfunc_flags & KF_DESTRUCTIVE; } +static bool is_kfunc_rcu(struct bpf_kfunc_call_arg_meta *meta) +{ + return meta->kfunc_flags & KF_RCU; +} + static bool is_kfunc_arg_kptr_get(struct bpf_kfunc_call_arg_meta *meta, int arg) { return arg == 0 && (meta->kfunc_flags & KF_KPTR_GET); @@ -8722,13 +8734,20 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_kfunc_call_ switch (kf_arg_type) { case KF_ARG_PTR_TO_ALLOC_BTF_ID: case KF_ARG_PTR_TO_BTF_ID: - if (!is_kfunc_trusted_args(meta)) + if (!is_kfunc_trusted_args(meta) && !is_kfunc_rcu(meta)) break; if (!is_trusted_reg(reg)) { - verbose(env, "R%d must be referenced or trusted\n", regno); - return -EINVAL; + if (!is_kfunc_rcu(meta)) { + verbose(env, "R%d must be referenced or trusted\n", regno); + return -EINVAL; + } + if (!is_rcu_reg(reg)) { + verbose(env, "R%d must be a rcu pointer\n", regno); + return -EINVAL; + } } + fallthrough; case KF_ARG_PTR_TO_CTX: /* Trusted arguments have the same offset checks as release arguments */ @@ -8839,7 +8858,7 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_kfunc_call_ case KF_ARG_PTR_TO_BTF_ID: /* Only base_type is checked, further checks are done here */ if ((base_type(reg->type) != PTR_TO_BTF_ID || - bpf_type_has_unsafe_modifiers(reg->type)) && + (bpf_type_has_unsafe_modifiers(reg->type) && !is_rcu_reg(reg))) && !reg2btf_ids[base_type(reg->type)]) { verbose(env, "arg#%d is %s ", i, reg_type_str(env, reg->type)); verbose(env, "expected %s or socket\n", @@ -8954,7 +8973,7 @@ static int check_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn, } else if (rcu_unlock) { bpf_for_each_reg_in_vstate(env->cur_state, state, reg, ({ if (reg->type & MEM_RCU) { - reg->type &= ~(MEM_RCU | PTR_TRUSTED); + reg->type &= ~(MEM_RCU | PTR_MAYBE_NULL); reg->type |= PTR_UNTRUSTED; } })); @@ -11294,7 +11313,7 @@ static void mark_ptr_or_null_reg(struct bpf_func_state *state, bool is_null) { if (type_may_be_null(reg->type) && reg->id == id && - !WARN_ON_ONCE(!reg->id)) { + (is_rcu_reg(reg) || !WARN_ON_ONCE(!reg->id))) { /* Old offset (both fixed and variable parts) should have been * known-zero, because we don't allow pointer arithmetic on * pointers that might be NULL. If we see this happening, don't From 8723ec22a31db3f400fce440c235ada0fff95657 Mon Sep 17 00:00:00 2001 From: Yonghong Song Date: Sat, 3 Dec 2022 10:46:07 -0800 Subject: [PATCH 1571/2002] selftests/bpf: Fix rcu_read_lock test with new MEM_RCU semantics Add MEM_RCU pointer null checking for related tests. Also modified task_acquire test so it takes a rcu ptr 'ptr' where 'ptr = rcu_ptr->rcu_field'. Signed-off-by: Yonghong Song Link: https://lore.kernel.org/r/20221203184607.478314-1-yhs@fb.com Signed-off-by: Alexei Starovoitov --- .../selftests/bpf/progs/rcu_read_lock.c | 55 +++++++++++++++---- 1 file changed, 45 insertions(+), 10 deletions(-) diff --git a/tools/testing/selftests/bpf/progs/rcu_read_lock.c b/tools/testing/selftests/bpf/progs/rcu_read_lock.c index 94a970076b98..cf06a34fcb02 100644 --- a/tools/testing/selftests/bpf/progs/rcu_read_lock.c +++ b/tools/testing/selftests/bpf/progs/rcu_read_lock.c @@ -23,13 +23,14 @@ struct bpf_key *bpf_lookup_user_key(__u32 serial, __u64 flags) __ksym; void bpf_key_put(struct bpf_key *key) __ksym; void bpf_rcu_read_lock(void) __ksym; void bpf_rcu_read_unlock(void) __ksym; -struct task_struct *bpf_task_acquire(struct task_struct *p) __ksym; +struct task_struct *bpf_task_acquire_not_zero(struct task_struct *p) __ksym; void bpf_task_release(struct task_struct *p) __ksym; SEC("?fentry.s/" SYS_PREFIX "sys_getpgid") int get_cgroup_id(void *ctx) { struct task_struct *task; + struct css_set *cgroups; task = bpf_get_current_task_btf(); if (task->pid != target_pid) @@ -37,7 +38,11 @@ int get_cgroup_id(void *ctx) /* simulate bpf_get_current_cgroup_id() helper */ bpf_rcu_read_lock(); - cgroup_id = task->cgroups->dfl_cgrp->kn->id; + cgroups = task->cgroups; + if (!cgroups) + goto unlock; + cgroup_id = cgroups->dfl_cgrp->kn->id; +unlock: bpf_rcu_read_unlock(); return 0; } @@ -56,6 +61,8 @@ int task_succ(void *ctx) bpf_rcu_read_lock(); /* region including helper using rcu ptr real_parent */ real_parent = task->real_parent; + if (!real_parent) + goto out; ptr = bpf_task_storage_get(&map_a, real_parent, &init_val, BPF_LOCAL_STORAGE_GET_F_CREATE); if (!ptr) @@ -92,7 +99,10 @@ int two_regions(void *ctx) bpf_rcu_read_unlock(); bpf_rcu_read_lock(); real_parent = task->real_parent; + if (!real_parent) + goto out; (void)bpf_task_storage_get(&map_a, real_parent, 0, 0); +out: bpf_rcu_read_unlock(); return 0; } @@ -105,7 +115,10 @@ int non_sleepable_1(void *ctx) task = bpf_get_current_task_btf(); bpf_rcu_read_lock(); real_parent = task->real_parent; + if (!real_parent) + goto out; (void)bpf_task_storage_get(&map_a, real_parent, 0, 0); +out: bpf_rcu_read_unlock(); return 0; } @@ -121,7 +134,10 @@ int non_sleepable_2(void *ctx) bpf_rcu_read_lock(); real_parent = task->real_parent; + if (!real_parent) + goto out; (void)bpf_task_storage_get(&map_a, real_parent, 0, 0); +out: bpf_rcu_read_unlock(); return 0; } @@ -129,16 +145,28 @@ int non_sleepable_2(void *ctx) SEC("?fentry.s/" SYS_PREFIX "sys_nanosleep") int task_acquire(void *ctx) { - struct task_struct *task, *real_parent; + struct task_struct *task, *real_parent, *gparent; task = bpf_get_current_task_btf(); bpf_rcu_read_lock(); real_parent = task->real_parent; + if (!real_parent) + goto out; + + /* rcu_ptr->rcu_field */ + gparent = real_parent->real_parent; + if (!gparent) + goto out; + /* acquire a reference which can be used outside rcu read lock region */ - real_parent = bpf_task_acquire(real_parent); + gparent = bpf_task_acquire_not_zero(gparent); + if (!gparent) + goto out; + + (void)bpf_task_storage_get(&map_a, gparent, 0, 0); + bpf_task_release(gparent); +out: bpf_rcu_read_unlock(); - (void)bpf_task_storage_get(&map_a, real_parent, 0, 0); - bpf_task_release(real_parent); return 0; } @@ -181,9 +209,12 @@ int non_sleepable_rcu_mismatch(void *ctx) /* non-sleepable: missing bpf_rcu_read_unlock() in one path */ bpf_rcu_read_lock(); real_parent = task->real_parent; + if (!real_parent) + goto out; (void)bpf_task_storage_get(&map_a, real_parent, 0, 0); if (real_parent) bpf_rcu_read_unlock(); +out: return 0; } @@ -199,16 +230,17 @@ int inproper_sleepable_helper(void *ctx) /* sleepable helper in rcu read lock region */ bpf_rcu_read_lock(); real_parent = task->real_parent; + if (!real_parent) + goto out; regs = (struct pt_regs *)bpf_task_pt_regs(real_parent); - if (!regs) { - bpf_rcu_read_unlock(); - return 0; - } + if (!regs) + goto out; ptr = (void *)PT_REGS_IP(regs); (void)bpf_copy_from_user_task(&value, sizeof(uint32_t), ptr, task, 0); user_data = value; (void)bpf_task_storage_get(&map_a, real_parent, 0, 0); +out: bpf_rcu_read_unlock(); return 0; } @@ -239,7 +271,10 @@ int nested_rcu_region(void *ctx) bpf_rcu_read_lock(); bpf_rcu_read_lock(); real_parent = task->real_parent; + if (!real_parent) + goto out; (void)bpf_task_storage_get(&map_a, real_parent, 0, 0); +out: bpf_rcu_read_unlock(); bpf_rcu_read_unlock(); return 0; From f53625649888c6ec9eeca151f802e905482c6698 Mon Sep 17 00:00:00 2001 From: Yonghong Song Date: Sat, 3 Dec 2022 10:46:13 -0800 Subject: [PATCH 1572/2002] docs/bpf: Add KF_RCU documentation Add proper KF_RCU documentation in kfuncs.rst. Signed-off-by: Yonghong Song Link: https://lore.kernel.org/r/20221203184613.478967-1-yhs@fb.com Signed-off-by: Alexei Starovoitov --- Documentation/bpf/kfuncs.rst | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/Documentation/bpf/kfuncs.rst b/Documentation/bpf/kfuncs.rst index 90774479ab7a..b027fe16ee66 100644 --- a/Documentation/bpf/kfuncs.rst +++ b/Documentation/bpf/kfuncs.rst @@ -191,6 +191,15 @@ rebooting or panicking. Due to this additional restrictions apply to these calls. At the moment they only require CAP_SYS_BOOT capability, but more can be added later. +2.4.8 KF_RCU flag +----------------- + +The KF_RCU flag is used for kfuncs which have a rcu ptr as its argument. +When used together with KF_ACQUIRE, it indicates the kfunc should have a +single argument which must be a trusted argument or a MEM_RCU pointer. +The argument may have reference count of 0 and the kfunc must take this +into consideration. + 2.5 Registering the kfuncs -------------------------- From c0c852dd1876dc1db4600ce951a92aadd3073b1c Mon Sep 17 00:00:00 2001 From: Yonghong Song Date: Sat, 3 Dec 2022 12:49:54 -0800 Subject: [PATCH 1573/2002] bpf: Do not mark certain LSM hook arguments as trusted Martin mentioned that the verifier cannot assume arguments from LSM hook sk_alloc_security being trusted since after the hook is called, the sk ref_count is set to 1. This will overwrite the ref_count changed by the bpf program and may cause ref_count underflow later on. I then further checked some other hooks. For example, for bpf_lsm_file_alloc() hook in fs/file_table.c, f->f_cred = get_cred(cred); error = security_file_alloc(f); if (unlikely(error)) { file_free_rcu(&f->f_rcuhead); return ERR_PTR(error); } atomic_long_set(&f->f_count, 1); The input parameter 'f' to security_file_alloc() cannot be trusted as well. Specifically, I investiaged bpf_map/bpf_prog/file/sk/task alloc/free lsm hooks. Except bpf_map_alloc and task_alloc, arguments for all other hooks should not be considered as trusted. This may not be a complete list, but it covers common usage for sk and task. Fixes: 3f00c5239344 ("bpf: Allow trusted pointers to be passed to KF_TRUSTED_ARGS kfuncs") Signed-off-by: Yonghong Song Link: https://lore.kernel.org/r/20221203204954.2043348-1-yhs@fb.com Signed-off-by: Alexei Starovoitov --- include/linux/bpf_lsm.h | 6 ++++++ kernel/bpf/bpf_lsm.c | 16 ++++++++++++++++ kernel/bpf/btf.c | 2 ++ .../selftests/bpf/prog_tests/task_kfunc.c | 1 + .../selftests/bpf/progs/task_kfunc_failure.c | 11 +++++++++++ 5 files changed, 36 insertions(+) diff --git a/include/linux/bpf_lsm.h b/include/linux/bpf_lsm.h index 4bcf76a9bb06..1de7ece5d36d 100644 --- a/include/linux/bpf_lsm.h +++ b/include/linux/bpf_lsm.h @@ -28,6 +28,7 @@ int bpf_lsm_verify_prog(struct bpf_verifier_log *vlog, const struct bpf_prog *prog); bool bpf_lsm_is_sleepable_hook(u32 btf_id); +bool bpf_lsm_is_trusted(const struct bpf_prog *prog); static inline struct bpf_storage_blob *bpf_inode( const struct inode *inode) @@ -51,6 +52,11 @@ static inline bool bpf_lsm_is_sleepable_hook(u32 btf_id) return false; } +static inline bool bpf_lsm_is_trusted(const struct bpf_prog *prog) +{ + return false; +} + static inline int bpf_lsm_verify_prog(struct bpf_verifier_log *vlog, const struct bpf_prog *prog) { diff --git a/kernel/bpf/bpf_lsm.c b/kernel/bpf/bpf_lsm.c index ae0267f150b5..9ea42a45da47 100644 --- a/kernel/bpf/bpf_lsm.c +++ b/kernel/bpf/bpf_lsm.c @@ -345,11 +345,27 @@ BTF_ID(func, bpf_lsm_task_to_inode) BTF_ID(func, bpf_lsm_userns_create) BTF_SET_END(sleepable_lsm_hooks) +BTF_SET_START(untrusted_lsm_hooks) +BTF_ID(func, bpf_lsm_bpf_map_free_security) +BTF_ID(func, bpf_lsm_bpf_prog_alloc_security) +BTF_ID(func, bpf_lsm_bpf_prog_free_security) +BTF_ID(func, bpf_lsm_file_alloc_security) +BTF_ID(func, bpf_lsm_file_free_security) +BTF_ID(func, bpf_lsm_sk_alloc_security) +BTF_ID(func, bpf_lsm_sk_free_security) +BTF_ID(func, bpf_lsm_task_free) +BTF_SET_END(untrusted_lsm_hooks) + bool bpf_lsm_is_sleepable_hook(u32 btf_id) { return btf_id_set_contains(&sleepable_lsm_hooks, btf_id); } +bool bpf_lsm_is_trusted(const struct bpf_prog *prog) +{ + return !btf_id_set_contains(&untrusted_lsm_hooks, prog->aux->attach_btf_id); +} + const struct bpf_prog_ops lsm_prog_ops = { }; diff --git a/kernel/bpf/btf.c b/kernel/bpf/btf.c index d11cbf8cece7..c80bd8709e69 100644 --- a/kernel/bpf/btf.c +++ b/kernel/bpf/btf.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -5829,6 +5830,7 @@ static bool prog_args_trusted(const struct bpf_prog *prog) case BPF_PROG_TYPE_TRACING: return atype == BPF_TRACE_RAW_TP || atype == BPF_TRACE_ITER; case BPF_PROG_TYPE_LSM: + return bpf_lsm_is_trusted(prog); case BPF_PROG_TYPE_STRUCT_OPS: return true; default: diff --git a/tools/testing/selftests/bpf/prog_tests/task_kfunc.c b/tools/testing/selftests/bpf/prog_tests/task_kfunc.c index ffd8ef4303c8..18848c31e36f 100644 --- a/tools/testing/selftests/bpf/prog_tests/task_kfunc.c +++ b/tools/testing/selftests/bpf/prog_tests/task_kfunc.c @@ -103,6 +103,7 @@ static struct { {"task_kfunc_release_null", "arg#0 is ptr_or_null_ expected ptr_ or socket"}, {"task_kfunc_release_unacquired", "release kernel function bpf_task_release expects"}, {"task_kfunc_from_pid_no_null_check", "arg#0 is ptr_or_null_ expected ptr_ or socket"}, + {"task_kfunc_from_lsm_task_free", "reg type unsupported for arg#0 function"}, }; static void verify_fail(const char *prog_name, const char *expected_err_msg) diff --git a/tools/testing/selftests/bpf/progs/task_kfunc_failure.c b/tools/testing/selftests/bpf/progs/task_kfunc_failure.c index e310473190d5..87fa1db9d9b5 100644 --- a/tools/testing/selftests/bpf/progs/task_kfunc_failure.c +++ b/tools/testing/selftests/bpf/progs/task_kfunc_failure.c @@ -271,3 +271,14 @@ int BPF_PROG(task_kfunc_from_pid_no_null_check, struct task_struct *task, u64 cl return 0; } + +SEC("lsm/task_free") +int BPF_PROG(task_kfunc_from_lsm_task_free, struct task_struct *task) +{ + struct task_struct *acquired; + + /* the argument of lsm task_free hook is untrusted. */ + acquired = bpf_task_acquire(task); + bpf_task_release(acquired); + return 0; +} From 8972e18a439d6c47e838b600d71ff0a4f102f0e0 Mon Sep 17 00:00:00 2001 From: Sreevani Sreejith Date: Fri, 2 Dec 2022 14:17:10 -0800 Subject: [PATCH 1574/2002] bpf, docs: BPF Iterator Document Document that describes how BPF iterators work, how to use iterators, and how to pass parameters in BPF iterators. Acked-by: David Vernet Signed-off-by: Sreevani Sreejith Acked-by: Yonghong Song Link: https://lore.kernel.org/r/20221202221710.320810-2-ssreevani@meta.com Signed-off-by: Alexei Starovoitov --- Documentation/bpf/bpf_iterators.rst | 485 ++++++++++++++++++++++++++++ Documentation/bpf/index.rst | 1 + 2 files changed, 486 insertions(+) create mode 100644 Documentation/bpf/bpf_iterators.rst diff --git a/Documentation/bpf/bpf_iterators.rst b/Documentation/bpf/bpf_iterators.rst new file mode 100644 index 000000000000..6d7770793fab --- /dev/null +++ b/Documentation/bpf/bpf_iterators.rst @@ -0,0 +1,485 @@ +============= +BPF Iterators +============= + + +---------- +Motivation +---------- + +There are a few existing ways to dump kernel data into user space. The most +popular one is the ``/proc`` system. For example, ``cat /proc/net/tcp6`` dumps +all tcp6 sockets in the system, and ``cat /proc/net/netlink`` dumps all netlink +sockets in the system. However, their output format tends to be fixed, and if +users want more information about these sockets, they have to patch the kernel, +which often takes time to publish upstream and release. The same is true for popular +tools like `ss `_ where any +additional information needs a kernel patch. + +To solve this problem, the `drgn +`_ tool is often used to +dig out the kernel data with no kernel change. However, the main drawback for +drgn is performance, as it cannot do pointer tracing inside the kernel. In +addition, drgn cannot validate a pointer value and may read invalid data if the +pointer becomes invalid inside the kernel. + +The BPF iterator solves the above problem by providing flexibility on what data +(e.g., tasks, bpf_maps, etc.) to collect by calling BPF programs for each kernel +data object. + +---------------------- +How BPF Iterators Work +---------------------- + +A BPF iterator is a type of BPF program that allows users to iterate over +specific types of kernel objects. Unlike traditional BPF tracing programs that +allow users to define callbacks that are invoked at particular points of +execution in the kernel, BPF iterators allow users to define callbacks that +should be executed for every entry in a variety of kernel data structures. + +For example, users can define a BPF iterator that iterates over every task on +the system and dumps the total amount of CPU runtime currently used by each of +them. Another BPF task iterator may instead dump the cgroup information for each +task. Such flexibility is the core value of BPF iterators. + +A BPF program is always loaded into the kernel at the behest of a user space +process. A user space process loads a BPF program by opening and initializing +the program skeleton as required and then invoking a syscall to have the BPF +program verified and loaded by the kernel. + +In traditional tracing programs, a program is activated by having user space +obtain a ``bpf_link`` to the program with ``bpf_program__attach()``. Once +activated, the program callback will be invoked whenever the tracepoint is +triggered in the main kernel. For BPF iterator programs, a ``bpf_link`` to the +program is obtained using ``bpf_link_create()``, and the program callback is +invoked by issuing system calls from user space. + +Next, let us see how you can use the iterators to iterate on kernel objects and +read data. + +------------------------ +How to Use BPF iterators +------------------------ + +BPF selftests are a great resource to illustrate how to use the iterators. In +this section, we’ll walk through a BPF selftest which shows how to load and use +a BPF iterator program. To begin, we’ll look at `bpf_iter.c +`_, +which illustrates how to load and trigger BPF iterators on the user space side. +Later, we’ll look at a BPF program that runs in kernel space. + +Loading a BPF iterator in the kernel from user space typically involves the +following steps: + +* The BPF program is loaded into the kernel through ``libbpf``. Once the kernel + has verified and loaded the program, it returns a file descriptor (fd) to user + space. +* Obtain a ``link_fd`` to the BPF program by calling the ``bpf_link_create()`` + specified with the BPF program file descriptor received from the kernel. +* Next, obtain a BPF iterator file descriptor (``bpf_iter_fd``) by calling the + ``bpf_iter_create()`` specified with the ``bpf_link`` received from Step 2. +* Trigger the iteration by calling ``read(bpf_iter_fd)`` until no data is + available. +* Close the iterator fd using ``close(bpf_iter_fd)``. +* If needed to reread the data, get a new ``bpf_iter_fd`` and do the read again. + +The following are a few examples of selftest BPF iterator programs: + +* `bpf_iter_tcp4.c `_ +* `bpf_iter_task_vma.c `_ +* `bpf_iter_task_file.c `_ + +Let us look at ``bpf_iter_task_file.c``, which runs in kernel space: + +Here is the definition of ``bpf_iter__task_file`` in `vmlinux.h +`_. +Any struct name in ``vmlinux.h`` in the format ``bpf_iter__`` +represents a BPF iterator. The suffix ```` represents the type of +iterator. + +:: + + struct bpf_iter__task_file { + union { + struct bpf_iter_meta *meta; + }; + union { + struct task_struct *task; + }; + u32 fd; + union { + struct file *file; + }; + }; + +In the above code, the field 'meta' contains the metadata, which is the same for +all BPF iterator programs. The rest of the fields are specific to different +iterators. For example, for task_file iterators, the kernel layer provides the +'task', 'fd' and 'file' field values. The 'task' and 'file' are `reference +counted +`_, +so they won't go away when the BPF program runs. + +Here is a snippet from the ``bpf_iter_task_file.c`` file: + +:: + + SEC("iter/task_file") + int dump_task_file(struct bpf_iter__task_file *ctx) + { + struct seq_file *seq = ctx->meta->seq; + struct task_struct *task = ctx->task; + struct file *file = ctx->file; + __u32 fd = ctx->fd; + + if (task == NULL || file == NULL) + return 0; + + if (ctx->meta->seq_num == 0) { + count = 0; + BPF_SEQ_PRINTF(seq, " tgid gid fd file\n"); + } + + if (tgid == task->tgid && task->tgid != task->pid) + count++; + + if (last_tgid != task->tgid) { + last_tgid = task->tgid; + unique_tgid_count++; + } + + BPF_SEQ_PRINTF(seq, "%8d %8d %8d %lx\n", task->tgid, task->pid, fd, + (long)file->f_op); + return 0; + } + +In the above example, the section name ``SEC(iter/task_file)``, indicates that +the program is a BPF iterator program to iterate all files from all tasks. The +context of the program is ``bpf_iter__task_file`` struct. + +The user space program invokes the BPF iterator program running in the kernel +by issuing a ``read()`` syscall. Once invoked, the BPF +program can export data to user space using a variety of BPF helper functions. +You can use either ``bpf_seq_printf()`` (and BPF_SEQ_PRINTF helper macro) or +``bpf_seq_write()`` function based on whether you need formatted output or just +binary data, respectively. For binary-encoded data, the user space applications +can process the data from ``bpf_seq_write()`` as needed. For the formatted data, +you can use ``cat `` to print the results similar to ``cat +/proc/net/netlink`` after pinning the BPF iterator to the bpffs mount. Later, +use ``rm -f `` to remove the pinned iterator. + +For example, you can use the following command to create a BPF iterator from the +``bpf_iter_ipv6_route.o`` object file and pin it to the ``/sys/fs/bpf/my_route`` +path: + +:: + + $ bpftool iter pin ./bpf_iter_ipv6_route.o /sys/fs/bpf/my_route + +And then print out the results using the following command: + +:: + + $ cat /sys/fs/bpf/my_route + + +------------------------------------------------------- +Implement Kernel Support for BPF Iterator Program Types +------------------------------------------------------- + +To implement a BPF iterator in the kernel, the developer must make a one-time +change to the following key data structure defined in the `bpf.h +`_ +file. + +:: + + struct bpf_iter_reg { + const char *target; + bpf_iter_attach_target_t attach_target; + bpf_iter_detach_target_t detach_target; + bpf_iter_show_fdinfo_t show_fdinfo; + bpf_iter_fill_link_info_t fill_link_info; + bpf_iter_get_func_proto_t get_func_proto; + u32 ctx_arg_info_size; + u32 feature; + struct bpf_ctx_arg_aux ctx_arg_info[BPF_ITER_CTX_ARG_MAX]; + const struct bpf_iter_seq_info *seq_info; + }; + +After filling the data structure fields, call ``bpf_iter_reg_target()`` to +register the iterator to the main BPF iterator subsystem. + +The following is the breakdown for each field in struct ``bpf_iter_reg``. + +.. list-table:: + :widths: 25 50 + :header-rows: 1 + + * - Fields + - Description + * - target + - Specifies the name of the BPF iterator. For example: ``bpf_map``, + ``bpf_map_elem``. The name should be different from other ``bpf_iter`` target names in the kernel. + * - attach_target and detach_target + - Allows for target specific ``link_create`` action since some targets + may need special processing. Called during the user space link_create stage. + * - show_fdinfo and fill_link_info + - Called to fill target specific information when user tries to get link + info associated with the iterator. + * - get_func_proto + - Permits a BPF iterator to access BPF helpers specific to the iterator. + * - ctx_arg_info_size and ctx_arg_info + - Specifies the verifier states for BPF program arguments associated with + the bpf iterator. + * - feature + - Specifies certain action requests in the kernel BPF iterator + infrastructure. Currently, only BPF_ITER_RESCHED is supported. This means + that the kernel function cond_resched() is called to avoid other kernel + subsystem (e.g., rcu) misbehaving. + * - seq_info + - Specifies certain action requests in the kernel BPF iterator + infrastructure. Currently, only BPF_ITER_RESCHED is supported. This means + that the kernel function cond_resched() is called to avoid other kernel + subsystem (e.g., rcu) misbehaving. + + +`Click here +`_ +to see an implementation of the ``task_vma`` BPF iterator in the kernel. + +--------------------------------- +Parameterizing BPF Task Iterators +--------------------------------- + +By default, BPF iterators walk through all the objects of the specified types +(processes, cgroups, maps, etc.) across the entire system to read relevant +kernel data. But often, there are cases where we only care about a much smaller +subset of iterable kernel objects, such as only iterating tasks within a +specific process. Therefore, BPF iterator programs support filtering out objects +from iteration by allowing user space to configure the iterator program when it +is attached. + +-------------------------- +BPF Task Iterator Program +-------------------------- + +The following code is a BPF iterator program to print files and task information +through the ``seq_file`` of the iterator. It is a standard BPF iterator program +that visits every file of an iterator. We will use this BPF program in our +example later. + +:: + + #include + #include + + char _license[] SEC("license") = "GPL"; + + SEC("iter/task_file") + int dump_task_file(struct bpf_iter__task_file *ctx) + { + struct seq_file *seq = ctx->meta->seq; + struct task_struct *task = ctx->task; + struct file *file = ctx->file; + __u32 fd = ctx->fd; + if (task == NULL || file == NULL) + return 0; + if (ctx->meta->seq_num == 0) { + BPF_SEQ_PRINTF(seq, " tgid pid fd file\n"); + } + BPF_SEQ_PRINTF(seq, "%8d %8d %8d %lx\n", task->tgid, task->pid, fd, + (long)file->f_op); + return 0; + } + +---------------------------------------- +Creating a File Iterator with Parameters +---------------------------------------- + +Now, let us look at how to create an iterator that includes only files of a +process. + +First, fill the ``bpf_iter_attach_opts`` struct as shown below: + +:: + + LIBBPF_OPTS(bpf_iter_attach_opts, opts); + union bpf_iter_link_info linfo; + memset(&linfo, 0, sizeof(linfo)); + linfo.task.pid = getpid(); + opts.link_info = &linfo; + opts.link_info_len = sizeof(linfo); + +``linfo.task.pid``, if it is non-zero, directs the kernel to create an iterator +that only includes opened files for the process with the specified ``pid``. In +this example, we will only be iterating files for our process. If +``linfo.task.pid`` is zero, the iterator will visit every opened file of every +process. Similarly, ``linfo.task.tid`` directs the kernel to create an iterator +that visits opened files of a specific thread, not a process. In this example, +``linfo.task.tid`` is different from ``linfo.task.pid`` only if the thread has a +separate file descriptor table. In most circumstances, all process threads share +a single file descriptor table. + +Now, in the userspace program, pass the pointer of struct to the +``bpf_program__attach_iter()``. + +:: + + link = bpf_program__attach_iter(prog, &opts); iter_fd = + bpf_iter_create(bpf_link__fd(link)); + +If both *tid* and *pid* are zero, an iterator created from this struct +``bpf_iter_attach_opts`` will include every opened file of every task in the +system (in the namespace, actually.) It is the same as passing a NULL as the +second argument to ``bpf_program__attach_iter()``. + +The whole program looks like the following code: + +:: + + #include + #include + #include + #include + #include "bpf_iter_task_ex.skel.h" + + static int do_read_opts(struct bpf_program *prog, struct bpf_iter_attach_opts *opts) + { + struct bpf_link *link; + char buf[16] = {}; + int iter_fd = -1, len; + int ret = 0; + + link = bpf_program__attach_iter(prog, opts); + if (!link) { + fprintf(stderr, "bpf_program__attach_iter() fails\n"); + return -1; + } + iter_fd = bpf_iter_create(bpf_link__fd(link)); + if (iter_fd < 0) { + fprintf(stderr, "bpf_iter_create() fails\n"); + ret = -1; + goto free_link; + } + /* not check contents, but ensure read() ends without error */ + while ((len = read(iter_fd, buf, sizeof(buf) - 1)) > 0) { + buf[len] = 0; + printf("%s", buf); + } + printf("\n"); + free_link: + if (iter_fd >= 0) + close(iter_fd); + bpf_link__destroy(link); + return 0; + } + + static void test_task_file(void) + { + LIBBPF_OPTS(bpf_iter_attach_opts, opts); + struct bpf_iter_task_ex *skel; + union bpf_iter_link_info linfo; + skel = bpf_iter_task_ex__open_and_load(); + if (skel == NULL) + return; + memset(&linfo, 0, sizeof(linfo)); + linfo.task.pid = getpid(); + opts.link_info = &linfo; + opts.link_info_len = sizeof(linfo); + printf("PID %d\n", getpid()); + do_read_opts(skel->progs.dump_task_file, &opts); + bpf_iter_task_ex__destroy(skel); + } + + int main(int argc, const char * const * argv) + { + test_task_file(); + return 0; + } + +The following lines are the output of the program. +:: + + PID 1859 + + tgid pid fd file + 1859 1859 0 ffffffff82270aa0 + 1859 1859 1 ffffffff82270aa0 + 1859 1859 2 ffffffff82270aa0 + 1859 1859 3 ffffffff82272980 + 1859 1859 4 ffffffff8225e120 + 1859 1859 5 ffffffff82255120 + 1859 1859 6 ffffffff82254f00 + 1859 1859 7 ffffffff82254d80 + 1859 1859 8 ffffffff8225abe0 + +------------------ +Without Parameters +------------------ + +Let us look at how a BPF iterator without parameters skips files of other +processes in the system. In this case, the BPF program has to check the pid or +the tid of tasks, or it will receive every opened file in the system (in the +current *pid* namespace, actually). So, we usually add a global variable in the +BPF program to pass a *pid* to the BPF program. + +The BPF program would look like the following block. + + :: + + ...... + int target_pid = 0; + + SEC("iter/task_file") + int dump_task_file(struct bpf_iter__task_file *ctx) + { + ...... + if (task->tgid != target_pid) /* Check task->pid instead to check thread IDs */ + return 0; + BPF_SEQ_PRINTF(seq, "%8d %8d %8d %lx\n", task->tgid, task->pid, fd, + (long)file->f_op); + return 0; + } + +The user space program would look like the following block: + + :: + + ...... + static void test_task_file(void) + { + ...... + skel = bpf_iter_task_ex__open_and_load(); + if (skel == NULL) + return; + skel->bss->target_pid = getpid(); /* process ID. For thread id, use gettid() */ + memset(&linfo, 0, sizeof(linfo)); + linfo.task.pid = getpid(); + opts.link_info = &linfo; + opts.link_info_len = sizeof(linfo); + ...... + } + +``target_pid`` is a global variable in the BPF program. The user space program +should initialize the variable with a process ID to skip opened files of other +processes in the BPF program. When you parametrize a BPF iterator, the iterator +calls the BPF program fewer times which can save significant resources. + +--------------------------- +Parametrizing VMA Iterators +--------------------------- + +By default, a BPF VMA iterator includes every VMA in every process. However, +you can still specify a process or a thread to include only its VMAs. Unlike +files, a thread can not have a separate address space (since Linux 2.6.0-test6). +Here, using *tid* makes no difference from using *pid*. + +---------------------------- +Parametrizing Task Iterators +---------------------------- + +A BPF task iterator with *pid* includes all tasks (threads) of a process. The +BPF program receives these tasks one after another. You can specify a BPF task +iterator with *tid* parameter to include only the tasks that match the given +*tid*. diff --git a/Documentation/bpf/index.rst b/Documentation/bpf/index.rst index 1088d44634d6..b81533d8b061 100644 --- a/Documentation/bpf/index.rst +++ b/Documentation/bpf/index.rst @@ -24,6 +24,7 @@ that goes into great technical depth about the BPF Architecture. maps bpf_prog_run classic_vs_extended.rst + bpf_iterators bpf_licensing test_debug clang-notes From 2c40d97da1a25be815c91d6ebcd734d96d5732e8 Mon Sep 17 00:00:00 2001 From: Yonghong Song Date: Wed, 30 Nov 2022 21:04:44 -0800 Subject: [PATCH 1575/2002] bpf: Enable sleeptable support for cgrp local storage Similar to sk/inode/task local storage, enable sleepable support for cgrp local storage. Signed-off-by: Yonghong Song Link: https://lore.kernel.org/r/20221201050444.2785007-1-yhs@fb.com Signed-off-by: Alexei Starovoitov --- kernel/bpf/verifier.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 66b82f52b5bc..1d51bd9596da 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -14154,10 +14154,11 @@ static int check_map_prog_compatibility(struct bpf_verifier_env *env, case BPF_MAP_TYPE_INODE_STORAGE: case BPF_MAP_TYPE_SK_STORAGE: case BPF_MAP_TYPE_TASK_STORAGE: + case BPF_MAP_TYPE_CGRP_STORAGE: break; default: verbose(env, - "Sleepable programs can only use array, hash, and ringbuf maps\n"); + "Sleepable programs can only use array, hash, ringbuf and local storage maps\n"); return -EINVAL; } From 41d76c721c5c743470078d30e9bb8df08c489b1c Mon Sep 17 00:00:00 2001 From: Yonghong Song Date: Wed, 30 Nov 2022 21:04:49 -0800 Subject: [PATCH 1576/2002] bpf: Add sleepable prog tests for cgrp local storage Add three tests for cgrp local storage support for sleepable progs. Two tests can load and run properly, one for cgroup_iter, another for passing current->cgroups->dfl_cgrp to bpf_cgrp_storage_get() helper. One test has bpf_rcu_read_lock() and failed to load. Signed-off-by: Yonghong Song Link: https://lore.kernel.org/r/20221201050449.2785613-1-yhs@fb.com Signed-off-by: Alexei Starovoitov --- .../bpf/prog_tests/cgrp_local_storage.c | 94 +++++++++++++++++++ .../selftests/bpf/progs/cgrp_ls_sleepable.c | 80 ++++++++++++++++ 2 files changed, 174 insertions(+) create mode 100644 tools/testing/selftests/bpf/progs/cgrp_ls_sleepable.c diff --git a/tools/testing/selftests/bpf/prog_tests/cgrp_local_storage.c b/tools/testing/selftests/bpf/prog_tests/cgrp_local_storage.c index 1c30412ba132..33a2776737e7 100644 --- a/tools/testing/selftests/bpf/prog_tests/cgrp_local_storage.c +++ b/tools/testing/selftests/bpf/prog_tests/cgrp_local_storage.c @@ -10,7 +10,9 @@ #include "cgrp_ls_recursion.skel.h" #include "cgrp_ls_attach_cgroup.skel.h" #include "cgrp_ls_negative.skel.h" +#include "cgrp_ls_sleepable.skel.h" #include "network_helpers.h" +#include "cgroup_helpers.h" struct socket_cookie { __u64 cookie_key; @@ -150,14 +152,100 @@ static void test_negative(void) } } +static void test_cgroup_iter_sleepable(int cgroup_fd, __u64 cgroup_id) +{ + DECLARE_LIBBPF_OPTS(bpf_iter_attach_opts, opts); + union bpf_iter_link_info linfo; + struct cgrp_ls_sleepable *skel; + struct bpf_link *link; + int err, iter_fd; + char buf[16]; + + skel = cgrp_ls_sleepable__open(); + if (!ASSERT_OK_PTR(skel, "skel_open")) + return; + + bpf_program__set_autoload(skel->progs.cgroup_iter, true); + err = cgrp_ls_sleepable__load(skel); + if (!ASSERT_OK(err, "skel_load")) + goto out; + + memset(&linfo, 0, sizeof(linfo)); + linfo.cgroup.cgroup_fd = cgroup_fd; + linfo.cgroup.order = BPF_CGROUP_ITER_SELF_ONLY; + opts.link_info = &linfo; + opts.link_info_len = sizeof(linfo); + link = bpf_program__attach_iter(skel->progs.cgroup_iter, &opts); + if (!ASSERT_OK_PTR(link, "attach_iter")) + goto out; + + iter_fd = bpf_iter_create(bpf_link__fd(link)); + if (!ASSERT_GE(iter_fd, 0, "iter_create")) + goto out; + + /* trigger the program run */ + (void)read(iter_fd, buf, sizeof(buf)); + + ASSERT_EQ(skel->bss->cgroup_id, cgroup_id, "cgroup_id"); + + close(iter_fd); +out: + cgrp_ls_sleepable__destroy(skel); +} + +static void test_no_rcu_lock(__u64 cgroup_id) +{ + struct cgrp_ls_sleepable *skel; + int err; + + skel = cgrp_ls_sleepable__open(); + if (!ASSERT_OK_PTR(skel, "skel_open")) + return; + + skel->bss->target_pid = syscall(SYS_gettid); + + bpf_program__set_autoload(skel->progs.no_rcu_lock, true); + err = cgrp_ls_sleepable__load(skel); + if (!ASSERT_OK(err, "skel_load")) + goto out; + + err = cgrp_ls_sleepable__attach(skel); + if (!ASSERT_OK(err, "skel_attach")) + goto out; + + syscall(SYS_getpgid); + + ASSERT_EQ(skel->bss->cgroup_id, cgroup_id, "cgroup_id"); +out: + cgrp_ls_sleepable__destroy(skel); +} + +static void test_rcu_lock(void) +{ + struct cgrp_ls_sleepable *skel; + int err; + + skel = cgrp_ls_sleepable__open(); + if (!ASSERT_OK_PTR(skel, "skel_open")) + return; + + bpf_program__set_autoload(skel->progs.yes_rcu_lock, true); + err = cgrp_ls_sleepable__load(skel); + ASSERT_ERR(err, "skel_load"); + + cgrp_ls_sleepable__destroy(skel); +} + void test_cgrp_local_storage(void) { + __u64 cgroup_id; int cgroup_fd; cgroup_fd = test__join_cgroup("/cgrp_local_storage"); if (!ASSERT_GE(cgroup_fd, 0, "join_cgroup /cgrp_local_storage")) return; + cgroup_id = get_cgroup_id("/cgrp_local_storage"); if (test__start_subtest("tp_btf")) test_tp_btf(cgroup_fd); if (test__start_subtest("attach_cgroup")) @@ -166,6 +254,12 @@ void test_cgrp_local_storage(void) test_recursion(cgroup_fd); if (test__start_subtest("negative")) test_negative(); + if (test__start_subtest("cgroup_iter_sleepable")) + test_cgroup_iter_sleepable(cgroup_fd, cgroup_id); + if (test__start_subtest("no_rcu_lock")) + test_no_rcu_lock(cgroup_id); + if (test__start_subtest("rcu_lock")) + test_rcu_lock(); close(cgroup_fd); } diff --git a/tools/testing/selftests/bpf/progs/cgrp_ls_sleepable.c b/tools/testing/selftests/bpf/progs/cgrp_ls_sleepable.c new file mode 100644 index 000000000000..2d11ed528b6f --- /dev/null +++ b/tools/testing/selftests/bpf/progs/cgrp_ls_sleepable.c @@ -0,0 +1,80 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2022 Meta Platforms, Inc. and affiliates. */ + +#include "bpf_iter.h" +#include +#include +#include "bpf_misc.h" + +char _license[] SEC("license") = "GPL"; + +struct { + __uint(type, BPF_MAP_TYPE_CGRP_STORAGE); + __uint(map_flags, BPF_F_NO_PREALLOC); + __type(key, int); + __type(value, long); +} map_a SEC(".maps"); + +__u32 target_pid; +__u64 cgroup_id; + +void bpf_rcu_read_lock(void) __ksym; +void bpf_rcu_read_unlock(void) __ksym; + +SEC("?iter.s/cgroup") +int cgroup_iter(struct bpf_iter__cgroup *ctx) +{ + struct seq_file *seq = ctx->meta->seq; + struct cgroup *cgrp = ctx->cgroup; + long *ptr; + + if (cgrp == NULL) + return 0; + + ptr = bpf_cgrp_storage_get(&map_a, cgrp, 0, + BPF_LOCAL_STORAGE_GET_F_CREATE); + if (ptr) + cgroup_id = cgrp->kn->id; + return 0; +} + +SEC("?fentry.s/" SYS_PREFIX "sys_getpgid") +int no_rcu_lock(void *ctx) +{ + struct task_struct *task; + struct cgroup *cgrp; + long *ptr; + + task = bpf_get_current_task_btf(); + if (task->pid != target_pid) + return 0; + + /* ptr_to_btf_id semantics. should work. */ + cgrp = task->cgroups->dfl_cgrp; + ptr = bpf_cgrp_storage_get(&map_a, cgrp, 0, + BPF_LOCAL_STORAGE_GET_F_CREATE); + if (ptr) + cgroup_id = cgrp->kn->id; + return 0; +} + +SEC("?fentry.s/" SYS_PREFIX "sys_getpgid") +int yes_rcu_lock(void *ctx) +{ + struct task_struct *task; + struct cgroup *cgrp; + long *ptr; + + task = bpf_get_current_task_btf(); + if (task->pid != target_pid) + return 0; + + bpf_rcu_read_lock(); + cgrp = task->cgroups->dfl_cgrp; + /* cgrp is untrusted and cannot pass to bpf_cgrp_storage_get() helper. */ + ptr = bpf_cgrp_storage_get(&map_a, cgrp, 0, BPF_LOCAL_STORAGE_GET_F_CREATE); + if (ptr) + cgroup_id = cgrp->kn->id; + bpf_rcu_read_unlock(); + return 0; +} From 578ce69ffda49d6c1a252490553290d1f27199f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Toke=20H=C3=B8iland-J=C3=B8rgensen?= Date: Thu, 1 Dec 2022 13:39:39 +0100 Subject: [PATCH 1577/2002] bpf: Add dummy type reference to nf_conn___init to fix type deduplication MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The bpf_ct_set_nat_info() kfunc is defined in the nf_nat.ko module, and takes as a parameter the nf_conn___init struct, which is allocated through the bpf_xdp_ct_alloc() helper defined in the nf_conntrack.ko module. However, because kernel modules can't deduplicate BTF types between each other, and the nf_conn___init struct is not referenced anywhere in vmlinux BTF, this leads to two distinct BTF IDs for the same type (one in each module). This confuses the verifier, as described here: https://lore.kernel.org/all/87leoh372s.fsf@toke.dk/ As a workaround, add an explicit BTF_TYPE_EMIT for the type in net/filter.c, so the type definition gets included in vmlinux BTF. This way, both modules can refer to the same type ID (as they both build on top of vmlinux BTF), and the verifier is no longer confused. v2: - Use BTF_TYPE_EMIT (which is a statement so it has to be inside a function definition; use xdp_func_proto() for this, since this is mostly xdp-related). Fixes: 820dc0523e05 ("net: netfilter: move bpf_ct_set_nat_info kfunc in nf_nat_bpf.c") Signed-off-by: Toke Høiland-Jørgensen Link: https://lore.kernel.org/r/20221201123939.696558-1-toke@redhat.com Signed-off-by: Alexei Starovoitov --- net/core/filter.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/net/core/filter.c b/net/core/filter.c index 37baaa6b8fc3..8607136b6e2c 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -80,6 +80,7 @@ #include #include #include +#include static const struct bpf_func_proto * bpf_sk_base_func_proto(enum bpf_func_id func_id); @@ -7992,6 +7993,19 @@ xdp_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog) default: return bpf_sk_base_func_proto(func_id); } + +#if IS_MODULE(CONFIG_NF_CONNTRACK) && IS_ENABLED(CONFIG_DEBUG_INFO_BTF_MODULES) + /* The nf_conn___init type is used in the NF_CONNTRACK kfuncs. The + * kfuncs are defined in two different modules, and we want to be able + * to use them interchangably with the same BTF type ID. Because modules + * can't de-duplicate BTF IDs between each other, we need the type to be + * referenced in the vmlinux BTF or the verifier will get confused about + * the different types. So we add this dummy type reference which will + * be included in vmlinux BTF, allowing both modules to refer to the + * same type ID. + */ + BTF_TYPE_EMIT(struct nf_conn___init); +#endif } const struct bpf_func_proto bpf_sock_map_update_proto __weak; From 0a182f8d607464911756b4dbef5d6cad8de22469 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Fri, 2 Dec 2022 11:16:40 +0000 Subject: [PATCH 1578/2002] bpf, sockmap: fix race in sock_map_free() sock_map_free() calls release_sock(sk) without owning a reference on the socket. This can cause use-after-free as syzbot found [1] Jakub Sitnicki already took care of a similar issue in sock_hash_free() in commit 75e68e5bf2c7 ("bpf, sockhash: Synchronize delete from bucket list on map free") [1] refcount_t: decrement hit 0; leaking memory. WARNING: CPU: 0 PID: 3785 at lib/refcount.c:31 refcount_warn_saturate+0x17c/0x1a0 lib/refcount.c:31 Modules linked in: CPU: 0 PID: 3785 Comm: kworker/u4:6 Not tainted 6.1.0-rc7-syzkaller-00103-gef4d3ea40565 #0 Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 10/26/2022 Workqueue: events_unbound bpf_map_free_deferred RIP: 0010:refcount_warn_saturate+0x17c/0x1a0 lib/refcount.c:31 Code: 68 8b 31 c0 e8 75 71 15 fd 0f 0b e9 64 ff ff ff e8 d9 6e 4e fd c6 05 62 9c 3d 0a 01 48 c7 c7 80 bb 68 8b 31 c0 e8 54 71 15 fd <0f> 0b e9 43 ff ff ff 89 d9 80 e1 07 80 c1 03 38 c1 0f 8c a2 fe ff RSP: 0018:ffffc9000456fb60 EFLAGS: 00010246 RAX: eae59bab72dcd700 RBX: 0000000000000004 RCX: ffff8880207057c0 RDX: 0000000000000000 RSI: 0000000000000201 RDI: 0000000000000000 RBP: 0000000000000004 R08: ffffffff816fdabd R09: fffff520008adee5 R10: fffff520008adee5 R11: 1ffff920008adee4 R12: 0000000000000004 R13: dffffc0000000000 R14: ffff88807b1c6c00 R15: 1ffff1100f638dcf FS: 0000000000000000(0000) GS:ffff8880b9800000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 0000001b30c30000 CR3: 000000000d08e000 CR4: 00000000003506f0 DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000 DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7: 0000000000000400 Call Trace: __refcount_dec include/linux/refcount.h:344 [inline] refcount_dec include/linux/refcount.h:359 [inline] __sock_put include/net/sock.h:779 [inline] tcp_release_cb+0x2d0/0x360 net/ipv4/tcp_output.c:1092 release_sock+0xaf/0x1c0 net/core/sock.c:3468 sock_map_free+0x219/0x2c0 net/core/sock_map.c:356 process_one_work+0x81c/0xd10 kernel/workqueue.c:2289 worker_thread+0xb14/0x1330 kernel/workqueue.c:2436 kthread+0x266/0x300 kernel/kthread.c:376 ret_from_fork+0x1f/0x30 arch/x86/entry/entry_64.S:306 Fixes: 7e81a3530206 ("bpf: Sockmap, ensure sock lock held during tear down") Signed-off-by: Eric Dumazet Reported-by: syzbot Cc: Jakub Sitnicki Cc: John Fastabend Cc: Alexei Starovoitov Cc: Daniel Borkmann Cc: Song Liu Acked-by: John Fastabend Link: https://lore.kernel.org/r/20221202111640.2745533-1-edumazet@google.com Signed-off-by: Alexei Starovoitov --- net/core/sock_map.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/net/core/sock_map.c b/net/core/sock_map.c index 81beb16ab1eb..22fa2c5bc6ec 100644 --- a/net/core/sock_map.c +++ b/net/core/sock_map.c @@ -349,11 +349,13 @@ static void sock_map_free(struct bpf_map *map) sk = xchg(psk, NULL); if (sk) { + sock_hold(sk); lock_sock(sk); rcu_read_lock(); sock_map_unref(sk, psk); rcu_read_unlock(); release_sock(sk); + sock_put(sk); } } From ab0350c743d5c93fd88742f02b3dff12168ab435 Mon Sep 17 00:00:00 2001 From: James Hilliard Date: Fri, 2 Dec 2022 18:08:44 -0700 Subject: [PATCH 1579/2002] selftests/bpf: Fix conflicts with built-in functions in bpf_iter_ksym Both tolower and toupper are built in c functions, we should not redefine them as this can result in a build error. Fixes the following errors: progs/bpf_iter_ksym.c:10:20: error: conflicting types for built-in function 'tolower'; expected 'int(int)' [-Werror=builtin-declaration-mismatch] 10 | static inline char tolower(char c) | ^~~~~~~ progs/bpf_iter_ksym.c:5:1: note: 'tolower' is declared in header '' 4 | #include +++ |+#include 5 | progs/bpf_iter_ksym.c:17:20: error: conflicting types for built-in function 'toupper'; expected 'int(int)' [-Werror=builtin-declaration-mismatch] 17 | static inline char toupper(char c) | ^~~~~~~ progs/bpf_iter_ksym.c:17:20: note: 'toupper' is declared in header '' See background on this sort of issue: https://stackoverflow.com/a/20582607 https://gcc.gnu.org/bugzilla/show_bug.cgi?id=12213 (C99, 7.1.3p1) "All identifiers with external linkage in any of the following subclauses (including the future library directions) are always reserved for use as identifiers with external linkage." This is documented behavior in GCC: https://gcc.gnu.org/onlinedocs/gcc/Other-Builtins.html#index-std-2 Signed-off-by: James Hilliard Acked-by: Andrii Nakryiko Link: https://lore.kernel.org/r/20221203010847.2191265-1-james.hilliard1@gmail.com Signed-off-by: Alexei Starovoitov --- tools/testing/selftests/bpf/progs/bpf_iter_ksym.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/testing/selftests/bpf/progs/bpf_iter_ksym.c b/tools/testing/selftests/bpf/progs/bpf_iter_ksym.c index 285c008cbf9c..9ba14c37bbcc 100644 --- a/tools/testing/selftests/bpf/progs/bpf_iter_ksym.c +++ b/tools/testing/selftests/bpf/progs/bpf_iter_ksym.c @@ -7,14 +7,14 @@ char _license[] SEC("license") = "GPL"; unsigned long last_sym_value = 0; -static inline char tolower(char c) +static inline char to_lower(char c) { if (c >= 'A' && c <= 'Z') c += ('a' - 'A'); return c; } -static inline char toupper(char c) +static inline char to_upper(char c) { if (c >= 'a' && c <= 'z') c -= ('a' - 'A'); @@ -54,7 +54,7 @@ int dump_ksym(struct bpf_iter__ksym *ctx) type = iter->type; if (iter->module_name[0]) { - type = iter->exported ? toupper(type) : tolower(type); + type = iter->exported ? to_upper(type) : to_lower(type); BPF_SEQ_PRINTF(seq, "0x%llx %c %s [ %s ] ", value, type, iter->name, iter->module_name); } else { From d14f28b8c1de668bab863bf5892a49c824cb110d Mon Sep 17 00:00:00 2001 From: Leon Romanovsky Date: Fri, 2 Dec 2022 20:41:27 +0200 Subject: [PATCH 1580/2002] xfrm: add new packet offload flag In the next patches, the xfrm core code will be extended to support new type of offload - packet offload. In that mode, both policy and state should be specially configured in order to perform whole offloaded data path. Full offload takes care of encryption, decryption, encapsulation and other operations with headers. As this mode is new for XFRM policy flow, we can "start fresh" with flag bits and release first and second bit for future use. Reviewed-by: Raed Salem Signed-off-by: Leon Romanovsky Signed-off-by: Steffen Klassert --- include/net/xfrm.h | 7 +++++++ include/uapi/linux/xfrm.h | 6 ++++++ net/xfrm/xfrm_device.c | 3 +++ net/xfrm/xfrm_user.c | 2 ++ 4 files changed, 18 insertions(+) diff --git a/include/net/xfrm.h b/include/net/xfrm.h index e0cc6791c001..b39d24fa2ef0 100644 --- a/include/net/xfrm.h +++ b/include/net/xfrm.h @@ -131,12 +131,19 @@ enum { XFRM_DEV_OFFLOAD_OUT, }; +enum { + XFRM_DEV_OFFLOAD_UNSPECIFIED, + XFRM_DEV_OFFLOAD_CRYPTO, + XFRM_DEV_OFFLOAD_PACKET, +}; + struct xfrm_dev_offload { struct net_device *dev; netdevice_tracker dev_tracker; struct net_device *real_dev; unsigned long offload_handle; u8 dir : 2; + u8 type : 2; }; struct xfrm_mode { diff --git a/include/uapi/linux/xfrm.h b/include/uapi/linux/xfrm.h index 4f84ea7ee14c..23543c33fee8 100644 --- a/include/uapi/linux/xfrm.h +++ b/include/uapi/linux/xfrm.h @@ -519,6 +519,12 @@ struct xfrm_user_offload { */ #define XFRM_OFFLOAD_IPV6 1 #define XFRM_OFFLOAD_INBOUND 2 +/* Two bits above are relevant for state path only, while + * offload is used for both policy and state flows. + * + * In policy offload mode, they are free and can be safely reused. + */ +#define XFRM_OFFLOAD_PACKET 4 struct xfrm_userpolicy_default { #define XFRM_USERPOLICY_UNSPEC 0 diff --git a/net/xfrm/xfrm_device.c b/net/xfrm/xfrm_device.c index 21269e8f2db4..3b0c1ca8d4bb 100644 --- a/net/xfrm/xfrm_device.c +++ b/net/xfrm/xfrm_device.c @@ -291,12 +291,15 @@ int xfrm_dev_state_add(struct net *net, struct xfrm_state *x, else xso->dir = XFRM_DEV_OFFLOAD_OUT; + xso->type = XFRM_DEV_OFFLOAD_CRYPTO; + err = dev->xfrmdev_ops->xdo_dev_state_add(x); if (err) { xso->dev = NULL; xso->dir = 0; xso->real_dev = NULL; netdev_put(dev, &xso->dev_tracker); + xso->type = XFRM_DEV_OFFLOAD_UNSPECIFIED; if (err != -EOPNOTSUPP) { NL_SET_ERR_MSG(extack, "Device failed to offload this state"); diff --git a/net/xfrm/xfrm_user.c b/net/xfrm/xfrm_user.c index 0eb4696661c8..c3b8c1532718 100644 --- a/net/xfrm/xfrm_user.c +++ b/net/xfrm/xfrm_user.c @@ -956,6 +956,8 @@ static int copy_user_offload(struct xfrm_dev_offload *xso, struct sk_buff *skb) xuo->ifindex = xso->dev->ifindex; if (xso->dir == XFRM_DEV_OFFLOAD_IN) xuo->flags = XFRM_OFFLOAD_INBOUND; + if (xso->type == XFRM_DEV_OFFLOAD_PACKET) + xuo->flags |= XFRM_OFFLOAD_PACKET; return 0; } From 62f6eca5de103c6823f6ca2abbf2ee242e132207 Mon Sep 17 00:00:00 2001 From: Leon Romanovsky Date: Fri, 2 Dec 2022 20:41:28 +0200 Subject: [PATCH 1581/2002] xfrm: allow state packet offload mode Allow users to configure xfrm states with packet offload mode. The packet mode must be requested both for policy and state, and such requires us to do not implement fallback. We explicitly return an error if requested packet mode can't be configured. Reviewed-by: Raed Salem Signed-off-by: Leon Romanovsky Signed-off-by: Steffen Klassert --- .../inline_crypto/ch_ipsec/chcr_ipsec.c | 4 ++++ .../net/ethernet/intel/ixgbe/ixgbe_ipsec.c | 5 ++++ drivers/net/ethernet/intel/ixgbevf/ipsec.c | 5 ++++ .../mellanox/mlx5/core/en_accel/ipsec.c | 4 ++++ .../net/ethernet/netronome/nfp/crypto/ipsec.c | 5 ++++ drivers/net/netdevsim/ipsec.c | 5 ++++ net/xfrm/xfrm_device.c | 24 +++++++++++++++---- 7 files changed, 47 insertions(+), 5 deletions(-) diff --git a/drivers/net/ethernet/chelsio/inline_crypto/ch_ipsec/chcr_ipsec.c b/drivers/net/ethernet/chelsio/inline_crypto/ch_ipsec/chcr_ipsec.c index 585590520076..ca21794281d6 100644 --- a/drivers/net/ethernet/chelsio/inline_crypto/ch_ipsec/chcr_ipsec.c +++ b/drivers/net/ethernet/chelsio/inline_crypto/ch_ipsec/chcr_ipsec.c @@ -283,6 +283,10 @@ static int ch_ipsec_xfrm_add_state(struct xfrm_state *x) pr_debug("Cannot offload xfrm states with geniv other than seqiv\n"); return -EINVAL; } + if (x->xso.type != XFRM_DEV_OFFLOAD_CRYPTO) { + pr_debug("Unsupported xfrm offload\n"); + return -EINVAL; + } sa_entry = kzalloc(sizeof(*sa_entry), GFP_KERNEL); if (!sa_entry) { diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_ipsec.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_ipsec.c index 774de63dd93a..53a969e34883 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_ipsec.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_ipsec.c @@ -585,6 +585,11 @@ static int ixgbe_ipsec_add_sa(struct xfrm_state *xs) return -EINVAL; } + if (xs->xso.type != XFRM_DEV_OFFLOAD_CRYPTO) { + netdev_err(dev, "Unsupported ipsec offload type\n"); + return -EINVAL; + } + if (xs->xso.dir == XFRM_DEV_OFFLOAD_IN) { struct rx_sa rsa; diff --git a/drivers/net/ethernet/intel/ixgbevf/ipsec.c b/drivers/net/ethernet/intel/ixgbevf/ipsec.c index 9984ebc62d78..c1cf540d162a 100644 --- a/drivers/net/ethernet/intel/ixgbevf/ipsec.c +++ b/drivers/net/ethernet/intel/ixgbevf/ipsec.c @@ -280,6 +280,11 @@ static int ixgbevf_ipsec_add_sa(struct xfrm_state *xs) return -EINVAL; } + if (xs->xso.type != XFRM_DEV_OFFLOAD_CRYPTO) { + netdev_err(dev, "Unsupported ipsec offload type\n"); + return -EINVAL; + } + if (xs->xso.dir == XFRM_DEV_OFFLOAD_IN) { struct rx_sa rsa; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.c b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.c index 1b03ab03fc5a..e6411533f911 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.c @@ -253,6 +253,10 @@ static inline int mlx5e_xfrm_validate_state(struct xfrm_state *x) netdev_info(netdev, "Cannot offload xfrm states with geniv other than seqiv\n"); return -EINVAL; } + if (x->xso.type != XFRM_DEV_OFFLOAD_CRYPTO) { + netdev_info(netdev, "Unsupported xfrm offload type\n"); + return -EINVAL; + } return 0; } diff --git a/drivers/net/ethernet/netronome/nfp/crypto/ipsec.c b/drivers/net/ethernet/netronome/nfp/crypto/ipsec.c index 3728870d8e9c..4632268695cb 100644 --- a/drivers/net/ethernet/netronome/nfp/crypto/ipsec.c +++ b/drivers/net/ethernet/netronome/nfp/crypto/ipsec.c @@ -302,6 +302,11 @@ static int nfp_net_xfrm_add_state(struct xfrm_state *x) return -EINVAL; } + if (x->xso.type != XFRM_DEV_OFFLOAD_CRYPTO) { + nn_err(nn, "Unsupported xfrm offload tyoe\n"); + return -EINVAL; + } + cfg->spi = ntohl(x->id.spi); /* Hash/Authentication */ diff --git a/drivers/net/netdevsim/ipsec.c b/drivers/net/netdevsim/ipsec.c index 386336a38f34..b93baf5c8bee 100644 --- a/drivers/net/netdevsim/ipsec.c +++ b/drivers/net/netdevsim/ipsec.c @@ -149,6 +149,11 @@ static int nsim_ipsec_add_sa(struct xfrm_state *xs) return -EINVAL; } + if (xs->xso.type != XFRM_DEV_OFFLOAD_CRYPTO) { + netdev_err(dev, "Unsupported ipsec offload type\n"); + return -EINVAL; + } + /* find the first unused index */ ret = nsim_ipsec_find_empty_idx(ipsec); if (ret < 0) { diff --git a/net/xfrm/xfrm_device.c b/net/xfrm/xfrm_device.c index 3b0c1ca8d4bb..3184b2c394b6 100644 --- a/net/xfrm/xfrm_device.c +++ b/net/xfrm/xfrm_device.c @@ -229,6 +229,7 @@ int xfrm_dev_state_add(struct net *net, struct xfrm_state *x, struct xfrm_dev_offload *xso = &x->xso; xfrm_address_t *saddr; xfrm_address_t *daddr; + bool is_packet_offload; if (!x->type_offload) { NL_SET_ERR_MSG(extack, "Type doesn't support offload"); @@ -241,11 +242,13 @@ int xfrm_dev_state_add(struct net *net, struct xfrm_state *x, return -EINVAL; } - if (xuo->flags & ~(XFRM_OFFLOAD_IPV6 | XFRM_OFFLOAD_INBOUND)) { + if (xuo->flags & + ~(XFRM_OFFLOAD_IPV6 | XFRM_OFFLOAD_INBOUND | XFRM_OFFLOAD_PACKET)) { NL_SET_ERR_MSG(extack, "Unrecognized flags in offload request"); return -EINVAL; } + is_packet_offload = xuo->flags & XFRM_OFFLOAD_PACKET; dev = dev_get_by_index(net, xuo->ifindex); if (!dev) { if (!(xuo->flags & XFRM_OFFLOAD_INBOUND)) { @@ -260,7 +263,7 @@ int xfrm_dev_state_add(struct net *net, struct xfrm_state *x, x->props.family, xfrm_smark_get(0, x)); if (IS_ERR(dst)) - return 0; + return (is_packet_offload) ? -EINVAL : 0; dev = dst->dev; @@ -271,7 +274,7 @@ int xfrm_dev_state_add(struct net *net, struct xfrm_state *x, if (!dev->xfrmdev_ops || !dev->xfrmdev_ops->xdo_dev_state_add) { xso->dev = NULL; dev_put(dev); - return 0; + return (is_packet_offload) ? -EINVAL : 0; } if (x->props.flags & XFRM_STATE_ESN && @@ -291,7 +294,10 @@ int xfrm_dev_state_add(struct net *net, struct xfrm_state *x, else xso->dir = XFRM_DEV_OFFLOAD_OUT; - xso->type = XFRM_DEV_OFFLOAD_CRYPTO; + if (is_packet_offload) + xso->type = XFRM_DEV_OFFLOAD_PACKET; + else + xso->type = XFRM_DEV_OFFLOAD_CRYPTO; err = dev->xfrmdev_ops->xdo_dev_state_add(x); if (err) { @@ -301,7 +307,15 @@ int xfrm_dev_state_add(struct net *net, struct xfrm_state *x, netdev_put(dev, &xso->dev_tracker); xso->type = XFRM_DEV_OFFLOAD_UNSPECIFIED; - if (err != -EOPNOTSUPP) { + /* User explicitly requested packet offload mode and configured + * policy in addition to the XFRM state. So be civil to users, + * and return an error instead of taking fallback path. + * + * This WARN_ON() can be seen as a documentation for driver + * authors to do not return -EOPNOTSUPP in packet offload mode. + */ + WARN_ON(err == -EOPNOTSUPP && is_packet_offload); + if (err != -EOPNOTSUPP || is_packet_offload) { NL_SET_ERR_MSG(extack, "Device failed to offload this state"); return err; } From 919e43fad5163a8ceb39826ecdee897a9f799351 Mon Sep 17 00:00:00 2001 From: Leon Romanovsky Date: Fri, 2 Dec 2022 20:41:29 +0200 Subject: [PATCH 1582/2002] xfrm: add an interface to offload policy Extend netlink interface to add and delete XFRM policy from the device. This functionality is a first step to implement packet IPsec offload solution. Signed-off-by: Raed Salem Signed-off-by: Leon Romanovsky Signed-off-by: Steffen Klassert --- include/linux/netdevice.h | 3 ++ include/net/xfrm.h | 45 +++++++++++++++++++++++++ net/xfrm/xfrm_device.c | 67 ++++++++++++++++++++++++++++++++++++- net/xfrm/xfrm_policy.c | 69 +++++++++++++++++++++++++++++++++++++++ net/xfrm/xfrm_user.c | 18 ++++++++++ 5 files changed, 201 insertions(+), 1 deletion(-) diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 5aa35c58c342..4096e3fe8e4a 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -1040,6 +1040,9 @@ struct xfrmdev_ops { bool (*xdo_dev_offload_ok) (struct sk_buff *skb, struct xfrm_state *x); void (*xdo_dev_state_advance_esn) (struct xfrm_state *x); + int (*xdo_dev_policy_add) (struct xfrm_policy *x); + void (*xdo_dev_policy_delete) (struct xfrm_policy *x); + void (*xdo_dev_policy_free) (struct xfrm_policy *x); }; #endif diff --git a/include/net/xfrm.h b/include/net/xfrm.h index b39d24fa2ef0..6fea34cbdf48 100644 --- a/include/net/xfrm.h +++ b/include/net/xfrm.h @@ -129,6 +129,7 @@ struct xfrm_state_walk { enum { XFRM_DEV_OFFLOAD_IN = 1, XFRM_DEV_OFFLOAD_OUT, + XFRM_DEV_OFFLOAD_FWD, }; enum { @@ -541,6 +542,8 @@ struct xfrm_policy { struct xfrm_tmpl xfrm_vec[XFRM_MAX_DEPTH]; struct hlist_node bydst_inexact_list; struct rcu_head rcu; + + struct xfrm_dev_offload xdo; }; static inline struct net *xp_net(const struct xfrm_policy *xp) @@ -1585,6 +1588,8 @@ struct xfrm_state *xfrm_find_acq_byseq(struct net *net, u32 mark, u32 seq); int xfrm_state_delete(struct xfrm_state *x); int xfrm_state_flush(struct net *net, u8 proto, bool task_valid, bool sync); int xfrm_dev_state_flush(struct net *net, struct net_device *dev, bool task_valid); +int xfrm_dev_policy_flush(struct net *net, struct net_device *dev, + bool task_valid); void xfrm_sad_getinfo(struct net *net, struct xfrmk_sadinfo *si); void xfrm_spd_getinfo(struct net *net, struct xfrmk_spdinfo *si); u32 xfrm_replay_seqhi(struct xfrm_state *x, __be32 net_seq); @@ -1899,6 +1904,9 @@ struct sk_buff *validate_xmit_xfrm(struct sk_buff *skb, netdev_features_t featur int xfrm_dev_state_add(struct net *net, struct xfrm_state *x, struct xfrm_user_offload *xuo, struct netlink_ext_ack *extack); +int xfrm_dev_policy_add(struct net *net, struct xfrm_policy *xp, + struct xfrm_user_offload *xuo, u8 dir, + struct netlink_ext_ack *extack); bool xfrm_dev_offload_ok(struct sk_buff *skb, struct xfrm_state *x); static inline void xfrm_dev_state_advance_esn(struct xfrm_state *x) @@ -1947,6 +1955,28 @@ static inline void xfrm_dev_state_free(struct xfrm_state *x) netdev_put(dev, &xso->dev_tracker); } } + +static inline void xfrm_dev_policy_delete(struct xfrm_policy *x) +{ + struct xfrm_dev_offload *xdo = &x->xdo; + struct net_device *dev = xdo->dev; + + if (dev && dev->xfrmdev_ops && dev->xfrmdev_ops->xdo_dev_policy_delete) + dev->xfrmdev_ops->xdo_dev_policy_delete(x); +} + +static inline void xfrm_dev_policy_free(struct xfrm_policy *x) +{ + struct xfrm_dev_offload *xdo = &x->xdo; + struct net_device *dev = xdo->dev; + + if (dev && dev->xfrmdev_ops) { + if (dev->xfrmdev_ops->xdo_dev_policy_free) + dev->xfrmdev_ops->xdo_dev_policy_free(x); + xdo->dev = NULL; + netdev_put(dev, &xdo->dev_tracker); + } +} #else static inline void xfrm_dev_resume(struct sk_buff *skb) { @@ -1974,6 +2004,21 @@ static inline void xfrm_dev_state_free(struct xfrm_state *x) { } +static inline int xfrm_dev_policy_add(struct net *net, struct xfrm_policy *xp, + struct xfrm_user_offload *xuo, u8 dir, + struct netlink_ext_ack *extack) +{ + return 0; +} + +static inline void xfrm_dev_policy_delete(struct xfrm_policy *x) +{ +} + +static inline void xfrm_dev_policy_free(struct xfrm_policy *x) +{ +} + static inline bool xfrm_dev_offload_ok(struct sk_buff *skb, struct xfrm_state *x) { return false; diff --git a/net/xfrm/xfrm_device.c b/net/xfrm/xfrm_device.c index 3184b2c394b6..04ae510dcc66 100644 --- a/net/xfrm/xfrm_device.c +++ b/net/xfrm/xfrm_device.c @@ -325,6 +325,69 @@ int xfrm_dev_state_add(struct net *net, struct xfrm_state *x, } EXPORT_SYMBOL_GPL(xfrm_dev_state_add); +int xfrm_dev_policy_add(struct net *net, struct xfrm_policy *xp, + struct xfrm_user_offload *xuo, u8 dir, + struct netlink_ext_ack *extack) +{ + struct xfrm_dev_offload *xdo = &xp->xdo; + struct net_device *dev; + int err; + + if (!xuo->flags || xuo->flags & ~XFRM_OFFLOAD_PACKET) { + /* We support only packet offload mode and it means + * that user must set XFRM_OFFLOAD_PACKET bit. + */ + NL_SET_ERR_MSG(extack, "Unrecognized flags in offload request"); + return -EINVAL; + } + + dev = dev_get_by_index(net, xuo->ifindex); + if (!dev) + return -EINVAL; + + if (!dev->xfrmdev_ops || !dev->xfrmdev_ops->xdo_dev_policy_add) { + xdo->dev = NULL; + dev_put(dev); + NL_SET_ERR_MSG(extack, "Policy offload is not supported"); + return -EINVAL; + } + + xdo->dev = dev; + netdev_tracker_alloc(dev, &xdo->dev_tracker, GFP_ATOMIC); + xdo->real_dev = dev; + xdo->type = XFRM_DEV_OFFLOAD_PACKET; + switch (dir) { + case XFRM_POLICY_IN: + xdo->dir = XFRM_DEV_OFFLOAD_IN; + break; + case XFRM_POLICY_OUT: + xdo->dir = XFRM_DEV_OFFLOAD_OUT; + break; + case XFRM_POLICY_FWD: + xdo->dir = XFRM_DEV_OFFLOAD_FWD; + break; + default: + xdo->dev = NULL; + dev_put(dev); + NL_SET_ERR_MSG(extack, "Unrecognized oflload direction"); + return -EINVAL; + } + + err = dev->xfrmdev_ops->xdo_dev_policy_add(xp); + if (err) { + xdo->dev = NULL; + xdo->real_dev = NULL; + xdo->type = XFRM_DEV_OFFLOAD_UNSPECIFIED; + xdo->dir = 0; + netdev_put(dev, &xdo->dev_tracker); + NL_SET_ERR_MSG(extack, "Device failed to offload this policy"); + return err; + } + + return 0; +} +EXPORT_SYMBOL_GPL(xfrm_dev_policy_add); + bool xfrm_dev_offload_ok(struct sk_buff *skb, struct xfrm_state *x) { int mtu; @@ -427,8 +490,10 @@ static int xfrm_api_check(struct net_device *dev) static int xfrm_dev_down(struct net_device *dev) { - if (dev->features & NETIF_F_HW_ESP) + if (dev->features & NETIF_F_HW_ESP) { xfrm_dev_state_flush(dev_net(dev), dev, true); + xfrm_dev_policy_flush(dev_net(dev), dev, true); + } return NOTIFY_DONE; } diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c index 9b9e2765363d..8b8760907563 100644 --- a/net/xfrm/xfrm_policy.c +++ b/net/xfrm/xfrm_policy.c @@ -425,6 +425,7 @@ void xfrm_policy_destroy(struct xfrm_policy *policy) if (del_timer(&policy->timer) || del_timer(&policy->polq.hold_timer)) BUG(); + xfrm_dev_policy_free(policy); call_rcu(&policy->rcu, xfrm_policy_destroy_rcu); } EXPORT_SYMBOL(xfrm_policy_destroy); @@ -1769,12 +1770,41 @@ xfrm_policy_flush_secctx_check(struct net *net, u8 type, bool task_valid) } return err; } + +static inline int xfrm_dev_policy_flush_secctx_check(struct net *net, + struct net_device *dev, + bool task_valid) +{ + struct xfrm_policy *pol; + int err = 0; + + list_for_each_entry(pol, &net->xfrm.policy_all, walk.all) { + if (pol->walk.dead || + xfrm_policy_id2dir(pol->index) >= XFRM_POLICY_MAX || + pol->xdo.dev != dev) + continue; + + err = security_xfrm_policy_delete(pol->security); + if (err) { + xfrm_audit_policy_delete(pol, 0, task_valid); + return err; + } + } + return err; +} #else static inline int xfrm_policy_flush_secctx_check(struct net *net, u8 type, bool task_valid) { return 0; } + +static inline int xfrm_dev_policy_flush_secctx_check(struct net *net, + struct net_device *dev, + bool task_valid) +{ + return 0; +} #endif int xfrm_policy_flush(struct net *net, u8 type, bool task_valid) @@ -1814,6 +1844,44 @@ out: } EXPORT_SYMBOL(xfrm_policy_flush); +int xfrm_dev_policy_flush(struct net *net, struct net_device *dev, + bool task_valid) +{ + int dir, err = 0, cnt = 0; + struct xfrm_policy *pol; + + spin_lock_bh(&net->xfrm.xfrm_policy_lock); + + err = xfrm_dev_policy_flush_secctx_check(net, dev, task_valid); + if (err) + goto out; + +again: + list_for_each_entry(pol, &net->xfrm.policy_all, walk.all) { + dir = xfrm_policy_id2dir(pol->index); + if (pol->walk.dead || + dir >= XFRM_POLICY_MAX || + pol->xdo.dev != dev) + continue; + + __xfrm_policy_unlink(pol, dir); + spin_unlock_bh(&net->xfrm.xfrm_policy_lock); + cnt++; + xfrm_audit_policy_delete(pol, 1, task_valid); + xfrm_policy_kill(pol); + spin_lock_bh(&net->xfrm.xfrm_policy_lock); + goto again; + } + if (cnt) + __xfrm_policy_inexact_flush(net); + else + err = -ESRCH; +out: + spin_unlock_bh(&net->xfrm.xfrm_policy_lock); + return err; +} +EXPORT_SYMBOL(xfrm_dev_policy_flush); + int xfrm_policy_walk(struct net *net, struct xfrm_policy_walk *walk, int (*func)(struct xfrm_policy *, int, int, void*), void *data) @@ -2245,6 +2313,7 @@ int xfrm_policy_delete(struct xfrm_policy *pol, int dir) pol = __xfrm_policy_unlink(pol, dir); spin_unlock_bh(&net->xfrm.xfrm_policy_lock); if (pol) { + xfrm_dev_policy_delete(pol); xfrm_policy_kill(pol); return 0; } diff --git a/net/xfrm/xfrm_user.c b/net/xfrm/xfrm_user.c index c3b8c1532718..cf5172d4ce68 100644 --- a/net/xfrm/xfrm_user.c +++ b/net/xfrm/xfrm_user.c @@ -1892,6 +1892,15 @@ static struct xfrm_policy *xfrm_policy_construct(struct net *net, if (attrs[XFRMA_IF_ID]) xp->if_id = nla_get_u32(attrs[XFRMA_IF_ID]); + /* configure the hardware if offload is requested */ + if (attrs[XFRMA_OFFLOAD_DEV]) { + err = xfrm_dev_policy_add(net, xp, + nla_data(attrs[XFRMA_OFFLOAD_DEV]), + p->dir, extack); + if (err) + goto error; + } + return xp; error: *errp = err; @@ -1931,6 +1940,7 @@ static int xfrm_add_policy(struct sk_buff *skb, struct nlmsghdr *nlh, xfrm_audit_policy_add(xp, err ? 0 : 1, true); if (err) { + xfrm_dev_policy_delete(xp); security_xfrm_policy_free(xp->security); kfree(xp); return err; @@ -2043,6 +2053,8 @@ static int dump_one_policy(struct xfrm_policy *xp, int dir, int count, void *ptr err = xfrm_mark_put(skb, &xp->mark); if (!err) err = xfrm_if_id_put(skb, xp->if_id); + if (!err && xp->xdo.dev) + err = copy_user_offload(&xp->xdo, skb); if (err) { nlmsg_cancel(skb, nlh); return err; @@ -3381,6 +3393,8 @@ static int build_acquire(struct sk_buff *skb, struct xfrm_state *x, err = xfrm_mark_put(skb, &xp->mark); if (!err) err = xfrm_if_id_put(skb, xp->if_id); + if (!err && xp->xdo.dev) + err = copy_user_offload(&xp->xdo, skb); if (err) { nlmsg_cancel(skb, nlh); return err; @@ -3499,6 +3513,8 @@ static int build_polexpire(struct sk_buff *skb, struct xfrm_policy *xp, err = xfrm_mark_put(skb, &xp->mark); if (!err) err = xfrm_if_id_put(skb, xp->if_id); + if (!err && xp->xdo.dev) + err = copy_user_offload(&xp->xdo, skb); if (err) { nlmsg_cancel(skb, nlh); return err; @@ -3582,6 +3598,8 @@ static int xfrm_notify_policy(struct xfrm_policy *xp, int dir, const struct km_e err = xfrm_mark_put(skb, &xp->mark); if (!err) err = xfrm_if_id_put(skb, xp->if_id); + if (!err && xp->xdo.dev) + err = copy_user_offload(&xp->xdo, skb); if (err) goto out_free_skb; From f8a70afafc1759b1fca4baaa891625dde49c10b7 Mon Sep 17 00:00:00 2001 From: Leon Romanovsky Date: Fri, 2 Dec 2022 20:41:30 +0200 Subject: [PATCH 1583/2002] xfrm: add TX datapath support for IPsec packet offload mode In IPsec packet mode, the device is going to encrypt and encapsulate packets that are associated with offloaded policy. After successful policy lookup to indicate if packets should be offloaded or not, the stack forwards packets to the device to do the magic. Signed-off-by: Raed Salem Signed-off-by: Huy Nguyen Signed-off-by: Leon Romanovsky Signed-off-by: Steffen Klassert --- net/xfrm/xfrm_device.c | 15 +++++- net/xfrm/xfrm_output.c | 12 ++++- net/xfrm/xfrm_state.c | 119 ++++++++++++++++++++++++++++++++++++++++- 3 files changed, 141 insertions(+), 5 deletions(-) diff --git a/net/xfrm/xfrm_device.c b/net/xfrm/xfrm_device.c index 04ae510dcc66..3e9e874522a8 100644 --- a/net/xfrm/xfrm_device.c +++ b/net/xfrm/xfrm_device.c @@ -132,6 +132,16 @@ struct sk_buff *validate_xmit_xfrm(struct sk_buff *skb, netdev_features_t featur if (xo->flags & XFRM_GRO || x->xso.dir == XFRM_DEV_OFFLOAD_IN) return skb; + /* The packet was sent to HW IPsec packet offload engine, + * but to wrong device. Drop the packet, so it won't skip + * XFRM stack. + */ + if (x->xso.type == XFRM_DEV_OFFLOAD_PACKET && x->xso.dev != dev) { + kfree_skb(skb); + dev_core_stats_tx_dropped_inc(dev); + return NULL; + } + /* This skb was already validated on the upper/virtual dev */ if ((x->xso.dev != dev) && (x->xso.real_dev == dev)) return skb; @@ -398,8 +408,9 @@ bool xfrm_dev_offload_ok(struct sk_buff *skb, struct xfrm_state *x) if (!x->type_offload || x->encap) return false; - if ((!dev || (dev == xfrm_dst_path(dst)->dev)) && - (!xdst->child->xfrm)) { + if (x->xso.type == XFRM_DEV_OFFLOAD_PACKET || + ((!dev || (dev == xfrm_dst_path(dst)->dev)) && + !xdst->child->xfrm)) { mtu = xfrm_state_mtu(x, xdst->child_mtu_cached); if (skb->len <= mtu) goto ok; diff --git a/net/xfrm/xfrm_output.c b/net/xfrm/xfrm_output.c index 78cb8d0a6a18..ff114d68cc43 100644 --- a/net/xfrm/xfrm_output.c +++ b/net/xfrm/xfrm_output.c @@ -492,7 +492,7 @@ static int xfrm_output_one(struct sk_buff *skb, int err) struct xfrm_state *x = dst->xfrm; struct net *net = xs_net(x); - if (err <= 0) + if (err <= 0 || x->xso.type == XFRM_DEV_OFFLOAD_PACKET) goto resume; do { @@ -717,6 +717,16 @@ int xfrm_output(struct sock *sk, struct sk_buff *skb) break; } + if (x->xso.type == XFRM_DEV_OFFLOAD_PACKET) { + if (!xfrm_dev_offload_ok(skb, x)) { + XFRM_INC_STATS(net, LINUX_MIB_XFRMOUTERROR); + kfree_skb(skb); + return -EHOSTUNREACH; + } + + return xfrm_output_resume(sk, skb, 0); + } + secpath_reset(skb); if (xfrm_dev_offload_ok(skb, x)) { diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c index 9ec481fbfb63..4d315e1a88fa 100644 --- a/net/xfrm/xfrm_state.c +++ b/net/xfrm/xfrm_state.c @@ -951,6 +951,49 @@ xfrm_init_tempstate(struct xfrm_state *x, const struct flowi *fl, x->props.family = tmpl->encap_family; } +static struct xfrm_state *__xfrm_state_lookup_all(struct net *net, u32 mark, + const xfrm_address_t *daddr, + __be32 spi, u8 proto, + unsigned short family, + struct xfrm_dev_offload *xdo) +{ + unsigned int h = xfrm_spi_hash(net, daddr, spi, proto, family); + struct xfrm_state *x; + + hlist_for_each_entry_rcu(x, net->xfrm.state_byspi + h, byspi) { +#ifdef CONFIG_XFRM_OFFLOAD + if (xdo->type == XFRM_DEV_OFFLOAD_PACKET) { + if (x->xso.type != XFRM_DEV_OFFLOAD_PACKET) + /* HW states are in the head of list, there is + * no need to iterate further. + */ + break; + + /* Packet offload: both policy and SA should + * have same device. + */ + if (xdo->dev != x->xso.dev) + continue; + } else if (x->xso.type == XFRM_DEV_OFFLOAD_PACKET) + /* Skip HW policy for SW lookups */ + continue; +#endif + if (x->props.family != family || + x->id.spi != spi || + x->id.proto != proto || + !xfrm_addr_equal(&x->id.daddr, daddr, family)) + continue; + + if ((mark & x->mark.m) != x->mark.v) + continue; + if (!xfrm_state_hold_rcu(x)) + continue; + return x; + } + + return NULL; +} + static struct xfrm_state *__xfrm_state_lookup(struct net *net, u32 mark, const xfrm_address_t *daddr, __be32 spi, u8 proto, @@ -1092,6 +1135,23 @@ xfrm_state_find(const xfrm_address_t *daddr, const xfrm_address_t *saddr, rcu_read_lock(); h = xfrm_dst_hash(net, daddr, saddr, tmpl->reqid, encap_family); hlist_for_each_entry_rcu(x, net->xfrm.state_bydst + h, bydst) { +#ifdef CONFIG_XFRM_OFFLOAD + if (pol->xdo.type == XFRM_DEV_OFFLOAD_PACKET) { + if (x->xso.type != XFRM_DEV_OFFLOAD_PACKET) + /* HW states are in the head of list, there is + * no need to iterate further. + */ + break; + + /* Packet offload: both policy and SA should + * have same device. + */ + if (pol->xdo.dev != x->xso.dev) + continue; + } else if (x->xso.type == XFRM_DEV_OFFLOAD_PACKET) + /* Skip HW policy for SW lookups */ + continue; +#endif if (x->props.family == encap_family && x->props.reqid == tmpl->reqid && (mark & x->mark.m) == x->mark.v && @@ -1109,6 +1169,23 @@ xfrm_state_find(const xfrm_address_t *daddr, const xfrm_address_t *saddr, h_wildcard = xfrm_dst_hash(net, daddr, &saddr_wildcard, tmpl->reqid, encap_family); hlist_for_each_entry_rcu(x, net->xfrm.state_bydst + h_wildcard, bydst) { +#ifdef CONFIG_XFRM_OFFLOAD + if (pol->xdo.type == XFRM_DEV_OFFLOAD_PACKET) { + if (x->xso.type != XFRM_DEV_OFFLOAD_PACKET) + /* HW states are in the head of list, there is + * no need to iterate further. + */ + break; + + /* Packet offload: both policy and SA should + * have same device. + */ + if (pol->xdo.dev != x->xso.dev) + continue; + } else if (x->xso.type == XFRM_DEV_OFFLOAD_PACKET) + /* Skip HW policy for SW lookups */ + continue; +#endif if (x->props.family == encap_family && x->props.reqid == tmpl->reqid && (mark & x->mark.m) == x->mark.v && @@ -1126,8 +1203,10 @@ found: x = best; if (!x && !error && !acquire_in_progress) { if (tmpl->id.spi && - (x0 = __xfrm_state_lookup(net, mark, daddr, tmpl->id.spi, - tmpl->id.proto, encap_family)) != NULL) { + (x0 = __xfrm_state_lookup_all(net, mark, daddr, + tmpl->id.spi, tmpl->id.proto, + encap_family, + &pol->xdo)) != NULL) { to_put = x0; error = -EEXIST; goto out; @@ -1161,7 +1240,31 @@ found: x = NULL; goto out; } +#ifdef CONFIG_XFRM_OFFLOAD + if (pol->xdo.type == XFRM_DEV_OFFLOAD_PACKET) { + struct xfrm_dev_offload *xdo = &pol->xdo; + struct xfrm_dev_offload *xso = &x->xso; + xso->type = XFRM_DEV_OFFLOAD_PACKET; + xso->dir = xdo->dir; + xso->dev = xdo->dev; + xso->real_dev = xdo->real_dev; + netdev_tracker_alloc(xso->dev, &xso->dev_tracker, + GFP_ATOMIC); + error = xso->dev->xfrmdev_ops->xdo_dev_state_add(x); + if (error) { + xso->dir = 0; + netdev_put(xso->dev, &xso->dev_tracker); + xso->dev = NULL; + xso->real_dev = NULL; + xso->type = XFRM_DEV_OFFLOAD_UNSPECIFIED; + x->km.state = XFRM_STATE_DEAD; + to_put = x; + x = NULL; + goto out; + } + } +#endif if (km_query(x, tmpl, pol) == 0) { spin_lock_bh(&net->xfrm.xfrm_state_lock); x->km.state = XFRM_STATE_ACQ; @@ -1185,6 +1288,18 @@ found: xfrm_hash_grow_check(net, x->bydst.next != NULL); spin_unlock_bh(&net->xfrm.xfrm_state_lock); } else { +#ifdef CONFIG_XFRM_OFFLOAD + struct xfrm_dev_offload *xso = &x->xso; + + if (xso->type == XFRM_DEV_OFFLOAD_PACKET) { + xso->dev->xfrmdev_ops->xdo_dev_state_delete(x); + xso->dir = 0; + netdev_put(xso->dev, &xso->dev_tracker); + xso->dev = NULL; + xso->real_dev = NULL; + xso->type = XFRM_DEV_OFFLOAD_UNSPECIFIED; + } +#endif x->km.state = XFRM_STATE_DEAD; to_put = x; x = NULL; From 5958372ddf628fe6f4c3e49425734ad32fcfb13c Mon Sep 17 00:00:00 2001 From: Leon Romanovsky Date: Fri, 2 Dec 2022 20:41:31 +0200 Subject: [PATCH 1584/2002] xfrm: add RX datapath protection for IPsec packet offload mode Traffic received by device with enabled IPsec packet offload should be forwarded to the stack only after decryption, packet headers and trailers removed. Such packets are expected to be seen as normal (non-XFRM) ones, while not-supported packets should be dropped by the HW. Reviewed-by: Raed Salem Signed-off-by: Leon Romanovsky Signed-off-by: Steffen Klassert --- include/net/xfrm.h | 55 +++++++++++++++++++++++++++------------------- 1 file changed, 32 insertions(+), 23 deletions(-) diff --git a/include/net/xfrm.h b/include/net/xfrm.h index 6fea34cbdf48..b6ee14991a32 100644 --- a/include/net/xfrm.h +++ b/include/net/xfrm.h @@ -1102,6 +1102,29 @@ xfrm_state_addr_cmp(const struct xfrm_tmpl *tmpl, const struct xfrm_state *x, un return !0; } +#ifdef CONFIG_XFRM +static inline struct xfrm_state *xfrm_input_state(struct sk_buff *skb) +{ + struct sec_path *sp = skb_sec_path(skb); + + return sp->xvec[sp->len - 1]; +} +#endif + +static inline struct xfrm_offload *xfrm_offload(struct sk_buff *skb) +{ +#ifdef CONFIG_XFRM + struct sec_path *sp = skb_sec_path(skb); + + if (!sp || !sp->olen || sp->len != sp->olen) + return NULL; + + return &sp->ovec[sp->olen - 1]; +#else + return NULL; +#endif +} + #ifdef CONFIG_XFRM int __xfrm_policy_check(struct sock *, int dir, struct sk_buff *skb, unsigned short family); @@ -1133,10 +1156,19 @@ static inline int __xfrm_policy_check2(struct sock *sk, int dir, { struct net *net = dev_net(skb->dev); int ndir = dir | (reverse ? XFRM_POLICY_MASK + 1 : 0); + struct xfrm_offload *xo = xfrm_offload(skb); + struct xfrm_state *x; if (sk && sk->sk_policy[XFRM_POLICY_IN]) return __xfrm_policy_check(sk, ndir, skb, family); + if (xo) { + x = xfrm_input_state(skb); + if (x->xso.type == XFRM_DEV_OFFLOAD_PACKET) + return (xo->flags & CRYPTO_DONE) && + (xo->status & CRYPTO_SUCCESS); + } + return __xfrm_check_nopolicy(net, skb, dir) || __xfrm_check_dev_nopolicy(skb, dir, family) || __xfrm_policy_check(sk, ndir, skb, family); @@ -1872,29 +1904,6 @@ static inline void xfrm_states_delete(struct xfrm_state **states, int n) } #endif -#ifdef CONFIG_XFRM -static inline struct xfrm_state *xfrm_input_state(struct sk_buff *skb) -{ - struct sec_path *sp = skb_sec_path(skb); - - return sp->xvec[sp->len - 1]; -} -#endif - -static inline struct xfrm_offload *xfrm_offload(struct sk_buff *skb) -{ -#ifdef CONFIG_XFRM - struct sec_path *sp = skb_sec_path(skb); - - if (!sp || !sp->olen || sp->len != sp->olen) - return NULL; - - return &sp->ovec[sp->olen - 1]; -#else - return NULL; -#endif -} - void __init xfrm_dev_init(void); #ifdef CONFIG_XFRM_OFFLOAD From 3c611d40c6923c81e6a83a67156cd30a9503c155 Mon Sep 17 00:00:00 2001 From: Leon Romanovsky Date: Fri, 2 Dec 2022 20:41:32 +0200 Subject: [PATCH 1585/2002] xfrm: speed-up lookup of HW policies Devices that implement IPsec packet offload mode should offload SA and policies too. In RX path, it causes to the situation that HW will always have higher priority over any SW policies. It means that we don't need to perform any search of inexact policies and/or priority checks if HW policy was discovered. In such situation, the HW will catch the packets anyway and HW can still implement inexact lookups. In case specific policy is not found, we will continue with packet lookup and check for existence of HW policies in inexact list. HW policies are added to the head of SPD to ensure fast lookup, as XFRM iterates over all policies in the loop. The same solution of adding HW SAs at the begging of the list is applied to SA database too. However, we don't need to change lookups as they are sorted by insertion order and not priority. Signed-off-by: Leon Romanovsky Signed-off-by: Steffen Klassert --- net/xfrm/xfrm_policy.c | 16 ++++++---- net/xfrm/xfrm_state.c | 66 ++++++++++++++++++++++++++++++++---------- 2 files changed, 62 insertions(+), 20 deletions(-) diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c index 8b8760907563..e9eb82c5457d 100644 --- a/net/xfrm/xfrm_policy.c +++ b/net/xfrm/xfrm_policy.c @@ -536,7 +536,7 @@ redo: __get_hash_thresh(net, pol->family, dir, &dbits, &sbits); h = __addr_hash(&pol->selector.daddr, &pol->selector.saddr, pol->family, nhashmask, dbits, sbits); - if (!entry0) { + if (!entry0 || pol->xdo.type == XFRM_DEV_OFFLOAD_PACKET) { hlist_del_rcu(&pol->bydst); hlist_add_head_rcu(&pol->bydst, ndsttable + h); h0 = h; @@ -867,7 +867,7 @@ static void xfrm_policy_inexact_list_reinsert(struct net *net, break; } - if (newpos) + if (newpos && policy->xdo.type != XFRM_DEV_OFFLOAD_PACKET) hlist_add_behind_rcu(&policy->bydst, newpos); else hlist_add_head_rcu(&policy->bydst, &n->hhead); @@ -1348,7 +1348,7 @@ static void xfrm_hash_rebuild(struct work_struct *work) else break; } - if (newpos) + if (newpos && policy->xdo.type != XFRM_DEV_OFFLOAD_PACKET) hlist_add_behind_rcu(&policy->bydst, newpos); else hlist_add_head_rcu(&policy->bydst, chain); @@ -1525,7 +1525,7 @@ static void xfrm_policy_insert_inexact_list(struct hlist_head *chain, break; } - if (newpos) + if (newpos && policy->xdo.type != XFRM_DEV_OFFLOAD_PACKET) hlist_add_behind_rcu(&policy->bydst_inexact_list, newpos); else hlist_add_head_rcu(&policy->bydst_inexact_list, chain); @@ -1562,9 +1562,12 @@ static struct xfrm_policy *xfrm_policy_insert_list(struct hlist_head *chain, break; } - if (newpos) + if (newpos && policy->xdo.type != XFRM_DEV_OFFLOAD_PACKET) hlist_add_behind_rcu(&policy->bydst, &newpos->bydst); else + /* Packet offload policies enter to the head + * to speed-up lookups. + */ hlist_add_head_rcu(&policy->bydst, chain); return delpol; @@ -2181,6 +2184,9 @@ static struct xfrm_policy *xfrm_policy_lookup_bytype(struct net *net, u8 type, break; } } + if (ret && ret->xdo.type == XFRM_DEV_OFFLOAD_PACKET) + goto skip_inexact; + bin = xfrm_policy_inexact_lookup_rcu(net, type, family, dir, if_id); if (!bin || !xfrm_policy_find_inexact_candidates(&cand, bin, saddr, daddr)) diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c index 4d315e1a88fa..2a190e85da80 100644 --- a/net/xfrm/xfrm_state.c +++ b/net/xfrm/xfrm_state.c @@ -84,6 +84,25 @@ static unsigned int xfrm_seq_hash(struct net *net, u32 seq) return __xfrm_seq_hash(seq, net->xfrm.state_hmask); } +#define XFRM_STATE_INSERT(by, _n, _h, _type) \ + { \ + struct xfrm_state *_x = NULL; \ + \ + if (_type != XFRM_DEV_OFFLOAD_PACKET) { \ + hlist_for_each_entry_rcu(_x, _h, by) { \ + if (_x->xso.type == XFRM_DEV_OFFLOAD_PACKET) \ + continue; \ + break; \ + } \ + } \ + \ + if (!_x || _x->xso.type == XFRM_DEV_OFFLOAD_PACKET) \ + /* SAD is empty or consist from HW SAs only */ \ + hlist_add_head_rcu(_n, _h); \ + else \ + hlist_add_before_rcu(_n, &_x->by); \ + } + static void xfrm_hash_transfer(struct hlist_head *list, struct hlist_head *ndsttable, struct hlist_head *nsrctable, @@ -100,23 +119,25 @@ static void xfrm_hash_transfer(struct hlist_head *list, h = __xfrm_dst_hash(&x->id.daddr, &x->props.saddr, x->props.reqid, x->props.family, nhashmask); - hlist_add_head_rcu(&x->bydst, ndsttable + h); + XFRM_STATE_INSERT(bydst, &x->bydst, ndsttable + h, x->xso.type); h = __xfrm_src_hash(&x->id.daddr, &x->props.saddr, x->props.family, nhashmask); - hlist_add_head_rcu(&x->bysrc, nsrctable + h); + XFRM_STATE_INSERT(bysrc, &x->bysrc, nsrctable + h, x->xso.type); if (x->id.spi) { h = __xfrm_spi_hash(&x->id.daddr, x->id.spi, x->id.proto, x->props.family, nhashmask); - hlist_add_head_rcu(&x->byspi, nspitable + h); + XFRM_STATE_INSERT(byspi, &x->byspi, nspitable + h, + x->xso.type); } if (x->km.seq) { h = __xfrm_seq_hash(x->km.seq, nhashmask); - hlist_add_head_rcu(&x->byseq, nseqtable + h); + XFRM_STATE_INSERT(byseq, &x->byseq, nseqtable + h, + x->xso.type); } } } @@ -1269,16 +1290,24 @@ found: spin_lock_bh(&net->xfrm.xfrm_state_lock); x->km.state = XFRM_STATE_ACQ; list_add(&x->km.all, &net->xfrm.state_all); - hlist_add_head_rcu(&x->bydst, net->xfrm.state_bydst + h); + XFRM_STATE_INSERT(bydst, &x->bydst, + net->xfrm.state_bydst + h, + x->xso.type); h = xfrm_src_hash(net, daddr, saddr, encap_family); - hlist_add_head_rcu(&x->bysrc, net->xfrm.state_bysrc + h); + XFRM_STATE_INSERT(bysrc, &x->bysrc, + net->xfrm.state_bysrc + h, + x->xso.type); if (x->id.spi) { h = xfrm_spi_hash(net, &x->id.daddr, x->id.spi, x->id.proto, encap_family); - hlist_add_head_rcu(&x->byspi, net->xfrm.state_byspi + h); + XFRM_STATE_INSERT(byspi, &x->byspi, + net->xfrm.state_byspi + h, + x->xso.type); } if (x->km.seq) { h = xfrm_seq_hash(net, x->km.seq); - hlist_add_head_rcu(&x->byseq, net->xfrm.state_byseq + h); + XFRM_STATE_INSERT(byseq, &x->byseq, + net->xfrm.state_byseq + h, + x->xso.type); } x->lft.hard_add_expires_seconds = net->xfrm.sysctl_acq_expires; hrtimer_start(&x->mtimer, @@ -1395,22 +1424,26 @@ static void __xfrm_state_insert(struct xfrm_state *x) h = xfrm_dst_hash(net, &x->id.daddr, &x->props.saddr, x->props.reqid, x->props.family); - hlist_add_head_rcu(&x->bydst, net->xfrm.state_bydst + h); + XFRM_STATE_INSERT(bydst, &x->bydst, net->xfrm.state_bydst + h, + x->xso.type); h = xfrm_src_hash(net, &x->id.daddr, &x->props.saddr, x->props.family); - hlist_add_head_rcu(&x->bysrc, net->xfrm.state_bysrc + h); + XFRM_STATE_INSERT(bysrc, &x->bysrc, net->xfrm.state_bysrc + h, + x->xso.type); if (x->id.spi) { h = xfrm_spi_hash(net, &x->id.daddr, x->id.spi, x->id.proto, x->props.family); - hlist_add_head_rcu(&x->byspi, net->xfrm.state_byspi + h); + XFRM_STATE_INSERT(byspi, &x->byspi, net->xfrm.state_byspi + h, + x->xso.type); } if (x->km.seq) { h = xfrm_seq_hash(net, x->km.seq); - hlist_add_head_rcu(&x->byseq, net->xfrm.state_byseq + h); + XFRM_STATE_INSERT(byseq, &x->byseq, net->xfrm.state_byseq + h, + x->xso.type); } hrtimer_start(&x->mtimer, ktime_set(1, 0), HRTIMER_MODE_REL_SOFT); @@ -1524,9 +1557,11 @@ static struct xfrm_state *__find_acq_core(struct net *net, ktime_set(net->xfrm.sysctl_acq_expires, 0), HRTIMER_MODE_REL_SOFT); list_add(&x->km.all, &net->xfrm.state_all); - hlist_add_head_rcu(&x->bydst, net->xfrm.state_bydst + h); + XFRM_STATE_INSERT(bydst, &x->bydst, net->xfrm.state_bydst + h, + x->xso.type); h = xfrm_src_hash(net, daddr, saddr, family); - hlist_add_head_rcu(&x->bysrc, net->xfrm.state_bysrc + h); + XFRM_STATE_INSERT(bysrc, &x->bysrc, net->xfrm.state_bysrc + h, + x->xso.type); net->xfrm.state_num++; @@ -2209,7 +2244,8 @@ int xfrm_alloc_spi(struct xfrm_state *x, u32 low, u32 high, spin_lock_bh(&net->xfrm.xfrm_state_lock); x->id.spi = newspi; h = xfrm_spi_hash(net, &x->id.daddr, x->id.spi, x->id.proto, x->props.family); - hlist_add_head_rcu(&x->byspi, net->xfrm.state_byspi + h); + XFRM_STATE_INSERT(byspi, &x->byspi, net->xfrm.state_byspi + h, + x->xso.type); spin_unlock_bh(&net->xfrm.xfrm_state_lock); err = 0; From f3da86dc2c8c9004445cfbb15ac086773622d853 Mon Sep 17 00:00:00 2001 From: Leon Romanovsky Date: Fri, 2 Dec 2022 20:41:33 +0200 Subject: [PATCH 1586/2002] xfrm: add support to HW update soft and hard limits Both in RX and TX, the traffic that performs IPsec packet offload transformation is accounted by HW. It is needed to properly handle hard limits that require to drop the packet. It means that XFRM core needs to update internal counters with the one that accounted by the HW, so new callbacks are introduced in this patch. In case of soft or hard limit is occurred, the driver should call to xfrm_state_check_expire() that will perform key rekeying exactly as done by XFRM core. Signed-off-by: Leon Romanovsky Signed-off-by: Steffen Klassert --- include/linux/netdevice.h | 1 + include/net/xfrm.h | 17 +++++++++++++++++ net/xfrm/xfrm_state.c | 4 ++++ 3 files changed, 22 insertions(+) diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 4096e3fe8e4a..29ae964e3b89 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -1040,6 +1040,7 @@ struct xfrmdev_ops { bool (*xdo_dev_offload_ok) (struct sk_buff *skb, struct xfrm_state *x); void (*xdo_dev_state_advance_esn) (struct xfrm_state *x); + void (*xdo_dev_state_update_curlft) (struct xfrm_state *x); int (*xdo_dev_policy_add) (struct xfrm_policy *x); void (*xdo_dev_policy_delete) (struct xfrm_policy *x); void (*xdo_dev_policy_free) (struct xfrm_policy *x); diff --git a/include/net/xfrm.h b/include/net/xfrm.h index b6ee14991a32..5413cdd5ad62 100644 --- a/include/net/xfrm.h +++ b/include/net/xfrm.h @@ -1571,6 +1571,23 @@ struct xfrm_state *xfrm_stateonly_find(struct net *net, u32 mark, u32 if_id, struct xfrm_state *xfrm_state_lookup_byspi(struct net *net, __be32 spi, unsigned short family); int xfrm_state_check_expire(struct xfrm_state *x); +#ifdef CONFIG_XFRM_OFFLOAD +static inline void xfrm_dev_state_update_curlft(struct xfrm_state *x) +{ + struct xfrm_dev_offload *xdo = &x->xso; + struct net_device *dev = xdo->dev; + + if (x->xso.type != XFRM_DEV_OFFLOAD_PACKET) + return; + + if (dev && dev->xfrmdev_ops && + dev->xfrmdev_ops->xdo_dev_state_update_curlft) + dev->xfrmdev_ops->xdo_dev_state_update_curlft(x); + +} +#else +static inline void xfrm_dev_state_update_curlft(struct xfrm_state *x) {} +#endif void xfrm_state_insert(struct xfrm_state *x); int xfrm_state_add(struct xfrm_state *x); int xfrm_state_update(struct xfrm_state *x); diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c index 2a190e85da80..cc1d0ea42672 100644 --- a/net/xfrm/xfrm_state.c +++ b/net/xfrm/xfrm_state.c @@ -570,6 +570,8 @@ static enum hrtimer_restart xfrm_timer_handler(struct hrtimer *me) int err = 0; spin_lock(&x->lock); + xfrm_dev_state_update_curlft(x); + if (x->km.state == XFRM_STATE_DEAD) goto out; if (x->km.state == XFRM_STATE_EXPIRED) @@ -1936,6 +1938,8 @@ EXPORT_SYMBOL(xfrm_state_update); int xfrm_state_check_expire(struct xfrm_state *x) { + xfrm_dev_state_update_curlft(x); + if (!x->curlft.use_time) x->curlft.use_time = ktime_get_real_seconds(); From 2b7c72e0e5f98696f58490c7e4583412ad8be8f6 Mon Sep 17 00:00:00 2001 From: Leon Romanovsky Date: Fri, 2 Dec 2022 20:41:34 +0200 Subject: [PATCH 1587/2002] xfrm: document IPsec packet offload mode Extend XFRM device offload API description with newly added packet offload mode. Signed-off-by: Leon Romanovsky Signed-off-by: Steffen Klassert --- Documentation/networking/xfrm_device.rst | 62 ++++++++++++++++++++---- 1 file changed, 53 insertions(+), 9 deletions(-) diff --git a/Documentation/networking/xfrm_device.rst b/Documentation/networking/xfrm_device.rst index 01391dfd37d9..c43ace79e320 100644 --- a/Documentation/networking/xfrm_device.rst +++ b/Documentation/networking/xfrm_device.rst @@ -5,6 +5,7 @@ XFRM device - offloading the IPsec computations =============================================== Shannon Nelson +Leon Romanovsky Overview @@ -18,10 +19,21 @@ can radically increase throughput and decrease CPU utilization. The XFRM Device interface allows NIC drivers to offer to the stack access to the hardware offload. +Right now, there are two types of hardware offload that kernel supports. + * IPsec crypto offload: + * NIC performs encrypt/decrypt + * Kernel does everything else + * IPsec packet offload: + * NIC performs encrypt/decrypt + * NIC does encapsulation + * Kernel and NIC have SA and policy in-sync + * NIC handles the SA and policies states + * The Kernel talks to the keymanager + Userland access to the offload is typically through a system such as libreswan or KAME/raccoon, but the iproute2 'ip xfrm' command set can be handy when experimenting. An example command might look something -like this:: +like this for crypto offload: ip x s add proto esp dst 14.0.0.70 src 14.0.0.52 spi 0x07 mode transport \ reqid 0x07 replay-window 32 \ @@ -29,6 +41,17 @@ like this:: sel src 14.0.0.52/24 dst 14.0.0.70/24 proto tcp \ offload dev eth4 dir in +and for packet offload + + ip x s add proto esp dst 14.0.0.70 src 14.0.0.52 spi 0x07 mode transport \ + reqid 0x07 replay-window 32 \ + aead 'rfc4106(gcm(aes))' 0x44434241343332312423222114131211f4f3f2f1 128 \ + sel src 14.0.0.52/24 dst 14.0.0.70/24 proto tcp \ + offload packet dev eth4 dir in + + ip x p add src 14.0.0.70 dst 14.0.0.52 offload packet dev eth4 dir in + tmpl src 14.0.0.70 dst 14.0.0.52 proto esp reqid 10000 mode transport + Yes, that's ugly, but that's what shell scripts and/or libreswan are for. @@ -40,17 +63,24 @@ Callbacks to implement /* from include/linux/netdevice.h */ struct xfrmdev_ops { + /* Crypto and Packet offload callbacks */ int (*xdo_dev_state_add) (struct xfrm_state *x); void (*xdo_dev_state_delete) (struct xfrm_state *x); void (*xdo_dev_state_free) (struct xfrm_state *x); bool (*xdo_dev_offload_ok) (struct sk_buff *skb, struct xfrm_state *x); void (*xdo_dev_state_advance_esn) (struct xfrm_state *x); + + /* Solely packet offload callbacks */ + void (*xdo_dev_state_update_curlft) (struct xfrm_state *x); + int (*xdo_dev_policy_add) (struct xfrm_policy *x); + void (*xdo_dev_policy_delete) (struct xfrm_policy *x); + void (*xdo_dev_policy_free) (struct xfrm_policy *x); }; -The NIC driver offering ipsec offload will need to implement these -callbacks to make the offload available to the network stack's -XFRM subsystem. Additionally, the feature bits NETIF_F_HW_ESP and +The NIC driver offering ipsec offload will need to implement callbacks +relevant to supported offload to make the offload available to the network +stack's XFRM subsystem. Additionally, the feature bits NETIF_F_HW_ESP and NETIF_F_HW_ESP_TX_CSUM will signal the availability of the offload. @@ -79,7 +109,8 @@ and an indication of whether it is for Rx or Tx. The driver should =========== =================================== 0 success - -EOPNETSUPP offload not supported, try SW IPsec + -EOPNETSUPP offload not supported, try SW IPsec, + not applicable for packet offload mode other fail the request =========== =================================== @@ -96,6 +127,7 @@ will serviceable. This can check the packet information to be sure the offload can be supported (e.g. IPv4 or IPv6, no IPv4 options, etc) and return true of false to signify its support. +Crypto offload mode: When ready to send, the driver needs to inspect the Tx packet for the offload information, including the opaque context, and set up the packet send accordingly:: @@ -139,13 +171,25 @@ the stack in xfrm_input(). In ESN mode, xdo_dev_state_advance_esn() is called from xfrm_replay_advance_esn(). Driver will check packet seq number and update HW ESN state machine if needed. +Packet offload mode: +HW adds and deletes XFRM headers. So in RX path, XFRM stack is bypassed if HW +reported success. In TX path, the packet lefts kernel without extra header +and not encrypted, the HW is responsible to perform it. + When the SA is removed by the user, the driver's xdo_dev_state_delete() -is asked to disable the offload. Later, xdo_dev_state_free() is called -from a garbage collection routine after all reference counts to the state +and xdo_dev_policy_delete() are asked to disable the offload. Later, +xdo_dev_state_free() and xdo_dev_policy_free() are called from a garbage +collection routine after all reference counts to the state and policy have been removed and any remaining resources can be cleared for the offload state. How these are used by the driver will depend on specific hardware needs. As a netdev is set to DOWN the XFRM stack's netdev listener will call -xdo_dev_state_delete() and xdo_dev_state_free() on any remaining offloaded -states. +xdo_dev_state_delete(), xdo_dev_policy_delete(), xdo_dev_state_free() and +xdo_dev_policy_free() on any remaining offloaded states. + +Outcome of HW handling packets, the XFRM core can't count hard, soft limits. +The HW/driver are responsible to perform it and provide accurate data when +xdo_dev_state_update_curlft() is called. In case of one of these limits +occuried, the driver needs to call to xfrm_state_check_expire() to make sure +that XFRM performs rekeying sequence. From a46e9010124256f5bf5fc2c241a45cf1944b768e Mon Sep 17 00:00:00 2001 From: Revanth Kumar Uppala Date: Thu, 1 Dec 2022 15:58:43 +0000 Subject: [PATCH 1588/2002] net: stmmac: Power up SERDES after the PHY link The Tegra MGBE ethernet controller requires that the SERDES link is powered-up after the PHY link is up, otherwise the link fails to become ready following a resume from suspend. Add a variable to indicate that the SERDES link must be powered-up after the PHY link. Signed-off-by: Revanth Kumar Uppala Signed-off-by: Jon Hunter Signed-off-by: David S. Miller --- drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 7 +++++-- include/linux/stmmac.h | 1 + 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index 18c7ca29da2c..f36590d0c830 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -988,6 +988,9 @@ static void stmmac_mac_link_up(struct phylink_config *config, struct stmmac_priv *priv = netdev_priv(to_net_dev(config->dev)); u32 old_ctrl, ctrl; + if (priv->plat->serdes_up_after_phy_linkup && priv->plat->serdes_powerup) + priv->plat->serdes_powerup(priv->dev, priv->plat->bsp_priv); + old_ctrl = readl(priv->ioaddr + MAC_CTRL_REG); ctrl = old_ctrl & ~priv->hw->link.speed_mask; @@ -3809,7 +3812,7 @@ static int __stmmac_open(struct net_device *dev, stmmac_reset_queues_param(priv); - if (priv->plat->serdes_powerup) { + if (!priv->plat->serdes_up_after_phy_linkup && priv->plat->serdes_powerup) { ret = priv->plat->serdes_powerup(dev, priv->plat->bsp_priv); if (ret < 0) { netdev_err(priv->dev, "%s: Serdes powerup failed\n", @@ -7518,7 +7521,7 @@ int stmmac_resume(struct device *dev) stmmac_mdio_reset(priv->mii); } - if (priv->plat->serdes_powerup) { + if (!priv->plat->serdes_up_after_phy_linkup && priv->plat->serdes_powerup) { ret = priv->plat->serdes_powerup(ndev, priv->plat->bsp_priv); diff --git a/include/linux/stmmac.h b/include/linux/stmmac.h index fb2e88614f5d..83ca2e8eb6b5 100644 --- a/include/linux/stmmac.h +++ b/include/linux/stmmac.h @@ -271,5 +271,6 @@ struct plat_stmmacenet_data { int msi_tx_base_vec; bool use_phy_wol; bool sph_disable; + bool serdes_up_after_phy_linkup; }; #endif From d8ca113724e79b324f553914cefa9dd6961de152 Mon Sep 17 00:00:00 2001 From: Bhadram Varka Date: Thu, 1 Dec 2022 15:58:44 +0000 Subject: [PATCH 1589/2002] net: stmmac: tegra: Add MGBE support Add support for the Multi-Gigabit Ethernet (MGBE/XPCS) IP found on NVIDIA Tegra234 SoCs. Signed-off-by: Thierry Reding Signed-off-by: Bhadram Varka Co-developed-by: Revanth Kumar Uppala Signed-off-by: Revanth Kumar Uppala Signed-off-by: Jon Hunter Signed-off-by: David S. Miller --- drivers/net/ethernet/stmicro/stmmac/Kconfig | 9 + drivers/net/ethernet/stmicro/stmmac/Makefile | 1 + .../net/ethernet/stmicro/stmmac/dwmac-tegra.c | 388 ++++++++++++++++++ 3 files changed, 398 insertions(+) create mode 100644 drivers/net/ethernet/stmicro/stmmac/dwmac-tegra.c diff --git a/drivers/net/ethernet/stmicro/stmmac/Kconfig b/drivers/net/ethernet/stmicro/stmmac/Kconfig index 31ff35174034..f77511fe4e87 100644 --- a/drivers/net/ethernet/stmicro/stmmac/Kconfig +++ b/drivers/net/ethernet/stmicro/stmmac/Kconfig @@ -235,6 +235,15 @@ config DWMAC_INTEL_PLAT the stmmac device driver. This driver is used for the Intel Keem Bay SoC. +config DWMAC_TEGRA + tristate "NVIDIA Tegra MGBE support" + depends on ARCH_TEGRA || COMPILE_TEST + help + This selects the Multi-GigaBit Ethernet (MGBE) Controller that is + found on the NVIDIA Tegra SoC devices. This driver provides the glue + layer on top of the stmmac driver required for these NVIDIA Tegra SoC + devices. + config DWMAC_VISCONTI tristate "Toshiba Visconti DWMAC support" default ARCH_VISCONTI diff --git a/drivers/net/ethernet/stmicro/stmmac/Makefile b/drivers/net/ethernet/stmicro/stmmac/Makefile index d4e12e9ace4f..057e4bab5c08 100644 --- a/drivers/net/ethernet/stmicro/stmmac/Makefile +++ b/drivers/net/ethernet/stmicro/stmmac/Makefile @@ -31,6 +31,7 @@ obj-$(CONFIG_DWMAC_DWC_QOS_ETH) += dwmac-dwc-qos-eth.o obj-$(CONFIG_DWMAC_INTEL_PLAT) += dwmac-intel-plat.o obj-$(CONFIG_DWMAC_GENERIC) += dwmac-generic.o obj-$(CONFIG_DWMAC_IMX8) += dwmac-imx.o +obj-$(CONFIG_DWMAC_TEGRA) += dwmac-tegra.o obj-$(CONFIG_DWMAC_VISCONTI) += dwmac-visconti.o stmmac-platform-objs:= stmmac_platform.o dwmac-altr-socfpga-objs := altr_tse_pcs.o dwmac-socfpga.o diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-tegra.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-tegra.c new file mode 100644 index 000000000000..bdf990cf2f31 --- /dev/null +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-tegra.c @@ -0,0 +1,388 @@ +// SPDX-License-Identifier: GPL-2.0-only +#include +#include +#include +#include +#include + +#include "stmmac_platform.h" + +static const char *const mgbe_clks[] = { + "rx-pcs", "tx", "tx-pcs", "mac-divider", "mac", "mgbe", "ptp-ref", "mac" +}; + +struct tegra_mgbe { + struct device *dev; + + struct clk_bulk_data *clks; + + struct reset_control *rst_mac; + struct reset_control *rst_pcs; + + void __iomem *hv; + void __iomem *regs; + void __iomem *xpcs; + + struct mii_bus *mii; +}; + +#define XPCS_WRAP_UPHY_RX_CONTROL 0x801c +#define XPCS_WRAP_UPHY_RX_CONTROL_RX_SW_OVRD BIT(31) +#define XPCS_WRAP_UPHY_RX_CONTROL_RX_PCS_PHY_RDY BIT(10) +#define XPCS_WRAP_UPHY_RX_CONTROL_RX_CDR_RESET BIT(9) +#define XPCS_WRAP_UPHY_RX_CONTROL_RX_CAL_EN BIT(8) +#define XPCS_WRAP_UPHY_RX_CONTROL_RX_SLEEP (BIT(7) | BIT(6)) +#define XPCS_WRAP_UPHY_RX_CONTROL_AUX_RX_IDDQ BIT(5) +#define XPCS_WRAP_UPHY_RX_CONTROL_RX_IDDQ BIT(4) +#define XPCS_WRAP_UPHY_RX_CONTROL_RX_DATA_EN BIT(0) +#define XPCS_WRAP_UPHY_HW_INIT_CTRL 0x8020 +#define XPCS_WRAP_UPHY_HW_INIT_CTRL_TX_EN BIT(0) +#define XPCS_WRAP_UPHY_HW_INIT_CTRL_RX_EN BIT(2) +#define XPCS_WRAP_UPHY_STATUS 0x8044 +#define XPCS_WRAP_UPHY_STATUS_TX_P_UP BIT(0) +#define XPCS_WRAP_IRQ_STATUS 0x8050 +#define XPCS_WRAP_IRQ_STATUS_PCS_LINK_STS BIT(6) + +#define XPCS_REG_ADDR_SHIFT 10 +#define XPCS_REG_ADDR_MASK 0x1fff +#define XPCS_ADDR 0x3fc + +#define MGBE_WRAP_COMMON_INTR_ENABLE 0x8704 +#define MAC_SBD_INTR BIT(2) +#define MGBE_WRAP_AXI_ASID0_CTRL 0x8400 +#define MGBE_SID 0x6 + +static int __maybe_unused tegra_mgbe_suspend(struct device *dev) +{ + struct tegra_mgbe *mgbe = get_stmmac_bsp_priv(dev); + int err; + + err = stmmac_suspend(dev); + if (err) + return err; + + clk_bulk_disable_unprepare(ARRAY_SIZE(mgbe_clks), mgbe->clks); + + return reset_control_assert(mgbe->rst_mac); +} + +static int __maybe_unused tegra_mgbe_resume(struct device *dev) +{ + struct tegra_mgbe *mgbe = get_stmmac_bsp_priv(dev); + u32 value; + int err; + + err = clk_bulk_prepare_enable(ARRAY_SIZE(mgbe_clks), mgbe->clks); + if (err < 0) + return err; + + err = reset_control_deassert(mgbe->rst_mac); + if (err < 0) + return err; + + /* Enable common interrupt at wrapper level */ + writel(MAC_SBD_INTR, mgbe->regs + MGBE_WRAP_COMMON_INTR_ENABLE); + + /* Program SID */ + writel(MGBE_SID, mgbe->hv + MGBE_WRAP_AXI_ASID0_CTRL); + + value = readl(mgbe->xpcs + XPCS_WRAP_UPHY_STATUS); + if ((value & XPCS_WRAP_UPHY_STATUS_TX_P_UP) == 0) { + value = readl(mgbe->xpcs + XPCS_WRAP_UPHY_HW_INIT_CTRL); + value |= XPCS_WRAP_UPHY_HW_INIT_CTRL_TX_EN; + writel(value, mgbe->xpcs + XPCS_WRAP_UPHY_HW_INIT_CTRL); + } + + err = readl_poll_timeout(mgbe->xpcs + XPCS_WRAP_UPHY_HW_INIT_CTRL, value, + (value & XPCS_WRAP_UPHY_HW_INIT_CTRL_TX_EN) == 0, + 500, 500 * 2000); + if (err < 0) { + dev_err(mgbe->dev, "timeout waiting for TX lane to become enabled\n"); + clk_bulk_disable_unprepare(ARRAY_SIZE(mgbe_clks), mgbe->clks); + return err; + } + + err = stmmac_resume(dev); + if (err < 0) + clk_bulk_disable_unprepare(ARRAY_SIZE(mgbe_clks), mgbe->clks); + + return err; +} + +static int mgbe_uphy_lane_bringup_serdes_up(struct net_device *ndev, void *mgbe_data) +{ + struct tegra_mgbe *mgbe = (struct tegra_mgbe *)mgbe_data; + u32 value; + int err; + + value = readl(mgbe->xpcs + XPCS_WRAP_UPHY_RX_CONTROL); + value |= XPCS_WRAP_UPHY_RX_CONTROL_RX_SW_OVRD; + writel(value, mgbe->xpcs + XPCS_WRAP_UPHY_RX_CONTROL); + + value = readl(mgbe->xpcs + XPCS_WRAP_UPHY_RX_CONTROL); + value &= ~XPCS_WRAP_UPHY_RX_CONTROL_RX_IDDQ; + writel(value, mgbe->xpcs + XPCS_WRAP_UPHY_RX_CONTROL); + + value = readl(mgbe->xpcs + XPCS_WRAP_UPHY_RX_CONTROL); + value &= ~XPCS_WRAP_UPHY_RX_CONTROL_AUX_RX_IDDQ; + writel(value, mgbe->xpcs + XPCS_WRAP_UPHY_RX_CONTROL); + + value = readl(mgbe->xpcs + XPCS_WRAP_UPHY_RX_CONTROL); + value &= ~XPCS_WRAP_UPHY_RX_CONTROL_RX_SLEEP; + writel(value, mgbe->xpcs + XPCS_WRAP_UPHY_RX_CONTROL); + + value = readl(mgbe->xpcs + XPCS_WRAP_UPHY_RX_CONTROL); + value |= XPCS_WRAP_UPHY_RX_CONTROL_RX_CAL_EN; + writel(value, mgbe->xpcs + XPCS_WRAP_UPHY_RX_CONTROL); + + err = readl_poll_timeout(mgbe->xpcs + XPCS_WRAP_UPHY_RX_CONTROL, value, + (value & XPCS_WRAP_UPHY_RX_CONTROL_RX_CAL_EN) == 0, + 1000, 1000 * 2000); + if (err < 0) { + dev_err(mgbe->dev, "timeout waiting for RX calibration to become enabled\n"); + return err; + } + + value = readl(mgbe->xpcs + XPCS_WRAP_UPHY_RX_CONTROL); + value |= XPCS_WRAP_UPHY_RX_CONTROL_RX_DATA_EN; + writel(value, mgbe->xpcs + XPCS_WRAP_UPHY_RX_CONTROL); + + value = readl(mgbe->xpcs + XPCS_WRAP_UPHY_RX_CONTROL); + value |= XPCS_WRAP_UPHY_RX_CONTROL_RX_CDR_RESET; + writel(value, mgbe->xpcs + XPCS_WRAP_UPHY_RX_CONTROL); + + value = readl(mgbe->xpcs + XPCS_WRAP_UPHY_RX_CONTROL); + value &= ~XPCS_WRAP_UPHY_RX_CONTROL_RX_CDR_RESET; + writel(value, mgbe->xpcs + XPCS_WRAP_UPHY_RX_CONTROL); + + value = readl(mgbe->xpcs + XPCS_WRAP_UPHY_RX_CONTROL); + value |= XPCS_WRAP_UPHY_RX_CONTROL_RX_PCS_PHY_RDY; + writel(value, mgbe->xpcs + XPCS_WRAP_UPHY_RX_CONTROL); + + err = readl_poll_timeout(mgbe->xpcs + XPCS_WRAP_IRQ_STATUS, value, + value & XPCS_WRAP_IRQ_STATUS_PCS_LINK_STS, + 500, 500 * 2000); + if (err < 0) { + dev_err(mgbe->dev, "timeout waiting for link to become ready\n"); + return err; + } + + /* clear status */ + writel(value, mgbe->xpcs + XPCS_WRAP_IRQ_STATUS); + + return 0; +} + +static void mgbe_uphy_lane_bringup_serdes_down(struct net_device *ndev, void *mgbe_data) +{ + struct tegra_mgbe *mgbe = (struct tegra_mgbe *)mgbe_data; + u32 value; + + value = readl(mgbe->xpcs + XPCS_WRAP_UPHY_RX_CONTROL); + value |= XPCS_WRAP_UPHY_RX_CONTROL_RX_SW_OVRD; + writel(value, mgbe->xpcs + XPCS_WRAP_UPHY_RX_CONTROL); + + value = readl(mgbe->xpcs + XPCS_WRAP_UPHY_RX_CONTROL); + value &= ~XPCS_WRAP_UPHY_RX_CONTROL_RX_DATA_EN; + writel(value, mgbe->xpcs + XPCS_WRAP_UPHY_RX_CONTROL); + + value = readl(mgbe->xpcs + XPCS_WRAP_UPHY_RX_CONTROL); + value |= XPCS_WRAP_UPHY_RX_CONTROL_RX_SLEEP; + writel(value, mgbe->xpcs + XPCS_WRAP_UPHY_RX_CONTROL); + + value = readl(mgbe->xpcs + XPCS_WRAP_UPHY_RX_CONTROL); + value |= XPCS_WRAP_UPHY_RX_CONTROL_AUX_RX_IDDQ; + writel(value, mgbe->xpcs + XPCS_WRAP_UPHY_RX_CONTROL); + + value = readl(mgbe->xpcs + XPCS_WRAP_UPHY_RX_CONTROL); + value |= XPCS_WRAP_UPHY_RX_CONTROL_RX_IDDQ; + writel(value, mgbe->xpcs + XPCS_WRAP_UPHY_RX_CONTROL); +} + +static int tegra_mgbe_probe(struct platform_device *pdev) +{ + struct plat_stmmacenet_data *plat; + struct stmmac_resources res; + struct tegra_mgbe *mgbe; + int irq, err, i; + u32 value; + + mgbe = devm_kzalloc(&pdev->dev, sizeof(*mgbe), GFP_KERNEL); + if (!mgbe) + return -ENOMEM; + + mgbe->dev = &pdev->dev; + + memset(&res, 0, sizeof(res)); + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + mgbe->hv = devm_platform_ioremap_resource_byname(pdev, "hypervisor"); + if (IS_ERR(mgbe->hv)) + return PTR_ERR(mgbe->hv); + + mgbe->regs = devm_platform_ioremap_resource_byname(pdev, "mac"); + if (IS_ERR(mgbe->regs)) + return PTR_ERR(mgbe->regs); + + mgbe->xpcs = devm_platform_ioremap_resource_byname(pdev, "xpcs"); + if (IS_ERR(mgbe->xpcs)) + return PTR_ERR(mgbe->xpcs); + + res.addr = mgbe->regs; + res.irq = irq; + + mgbe->clks = devm_kzalloc(&pdev->dev, sizeof(*mgbe->clks), GFP_KERNEL); + if (!mgbe->clks) + return -ENOMEM; + + for (i = 0; i < ARRAY_SIZE(mgbe_clks); i++) + mgbe->clks[i].id = mgbe_clks[i]; + + err = devm_clk_bulk_get(mgbe->dev, ARRAY_SIZE(mgbe_clks), mgbe->clks); + if (err < 0) + return err; + + err = clk_bulk_prepare_enable(ARRAY_SIZE(mgbe_clks), mgbe->clks); + if (err < 0) + return err; + + /* Perform MAC reset */ + mgbe->rst_mac = devm_reset_control_get(&pdev->dev, "mac"); + if (IS_ERR(mgbe->rst_mac)) { + err = PTR_ERR(mgbe->rst_mac); + goto disable_clks; + } + + err = reset_control_assert(mgbe->rst_mac); + if (err < 0) + goto disable_clks; + + usleep_range(2000, 4000); + + err = reset_control_deassert(mgbe->rst_mac); + if (err < 0) + goto disable_clks; + + /* Perform PCS reset */ + mgbe->rst_pcs = devm_reset_control_get(&pdev->dev, "pcs"); + if (IS_ERR(mgbe->rst_pcs)) { + err = PTR_ERR(mgbe->rst_pcs); + goto disable_clks; + } + + err = reset_control_assert(mgbe->rst_pcs); + if (err < 0) + goto disable_clks; + + usleep_range(2000, 4000); + + err = reset_control_deassert(mgbe->rst_pcs); + if (err < 0) + goto disable_clks; + + plat = stmmac_probe_config_dt(pdev, res.mac); + if (IS_ERR(plat)) { + err = PTR_ERR(plat); + goto disable_clks; + } + + plat->has_xgmac = 1; + plat->tso_en = 1; + plat->pmt = 1; + plat->bsp_priv = mgbe; + + if (!plat->mdio_node) + plat->mdio_node = of_get_child_by_name(pdev->dev.of_node, "mdio"); + + if (!plat->mdio_bus_data) { + plat->mdio_bus_data = devm_kzalloc(&pdev->dev, sizeof(*plat->mdio_bus_data), + GFP_KERNEL); + if (!plat->mdio_bus_data) { + err = -ENOMEM; + goto remove; + } + } + + plat->mdio_bus_data->needs_reset = true; + + value = readl(mgbe->xpcs + XPCS_WRAP_UPHY_STATUS); + if ((value & XPCS_WRAP_UPHY_STATUS_TX_P_UP) == 0) { + value = readl(mgbe->xpcs + XPCS_WRAP_UPHY_HW_INIT_CTRL); + value |= XPCS_WRAP_UPHY_HW_INIT_CTRL_TX_EN; + writel(value, mgbe->xpcs + XPCS_WRAP_UPHY_HW_INIT_CTRL); + } + + err = readl_poll_timeout(mgbe->xpcs + XPCS_WRAP_UPHY_HW_INIT_CTRL, value, + (value & XPCS_WRAP_UPHY_HW_INIT_CTRL_TX_EN) == 0, + 500, 500 * 2000); + if (err < 0) { + dev_err(mgbe->dev, "timeout waiting for TX lane to become enabled\n"); + goto remove; + } + + plat->serdes_powerup = mgbe_uphy_lane_bringup_serdes_up; + plat->serdes_powerdown = mgbe_uphy_lane_bringup_serdes_down; + + /* Tx FIFO Size - 128KB */ + plat->tx_fifo_size = 131072; + /* Rx FIFO Size - 192KB */ + plat->rx_fifo_size = 196608; + + /* Enable common interrupt at wrapper level */ + writel(MAC_SBD_INTR, mgbe->regs + MGBE_WRAP_COMMON_INTR_ENABLE); + + /* Program SID */ + writel(MGBE_SID, mgbe->hv + MGBE_WRAP_AXI_ASID0_CTRL); + + plat->serdes_up_after_phy_linkup = 1; + + err = stmmac_dvr_probe(&pdev->dev, plat, &res); + if (err < 0) + goto remove; + + return 0; + +remove: + stmmac_remove_config_dt(pdev, plat); +disable_clks: + clk_bulk_disable_unprepare(ARRAY_SIZE(mgbe_clks), mgbe->clks); + + return err; +} + +static int tegra_mgbe_remove(struct platform_device *pdev) +{ + struct tegra_mgbe *mgbe = get_stmmac_bsp_priv(&pdev->dev); + + clk_bulk_disable_unprepare(ARRAY_SIZE(mgbe_clks), mgbe->clks); + + stmmac_pltfr_remove(pdev); + + return 0; +} + +static const struct of_device_id tegra_mgbe_match[] = { + { .compatible = "nvidia,tegra234-mgbe", }, + { } +}; +MODULE_DEVICE_TABLE(of, tegra_mgbe_match); + +static SIMPLE_DEV_PM_OPS(tegra_mgbe_pm_ops, tegra_mgbe_suspend, tegra_mgbe_resume); + +static struct platform_driver tegra_mgbe_driver = { + .probe = tegra_mgbe_probe, + .remove = tegra_mgbe_remove, + .driver = { + .name = "tegra-mgbe", + .pm = &tegra_mgbe_pm_ops, + .of_match_table = tegra_mgbe_match, + }, +}; +module_platform_driver(tegra_mgbe_driver); + +MODULE_AUTHOR("Thierry Reding "); +MODULE_DESCRIPTION("NVIDIA Tegra MGBE driver"); +MODULE_LICENSE("GPL"); From 38db82e29404d462b71bc3ac2149fbaf0c41fc7c Mon Sep 17 00:00:00 2001 From: ye xingchen Date: Fri, 2 Dec 2022 16:42:14 +0800 Subject: [PATCH 1590/2002] net: ipa: use sysfs_emit() to instead of scnprintf() Follow the advice of the Documentation/filesystems/sysfs.rst and show() should only use sysfs_emit() or sysfs_emit_at() when formatting the value to be returned to user space. Signed-off-by: ye xingchen Reviewed-by: Alex Elder Signed-off-by: David S. Miller --- drivers/net/ipa/ipa_sysfs.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/net/ipa/ipa_sysfs.c b/drivers/net/ipa/ipa_sysfs.c index 5cbc15a971f9..14bd2f903045 100644 --- a/drivers/net/ipa/ipa_sysfs.c +++ b/drivers/net/ipa/ipa_sysfs.c @@ -46,7 +46,7 @@ version_show(struct device *dev, struct device_attribute *attr, char *buf) { struct ipa *ipa = dev_get_drvdata(dev); - return scnprintf(buf, PAGE_SIZE, "%s\n", ipa_version_string(ipa)); + return sysfs_emit(buf, "%s\n", ipa_version_string(ipa)); } static DEVICE_ATTR_RO(version); @@ -70,7 +70,7 @@ static ssize_t rx_offload_show(struct device *dev, { struct ipa *ipa = dev_get_drvdata(dev); - return scnprintf(buf, PAGE_SIZE, "%s\n", ipa_offload_string(ipa)); + return sysfs_emit(buf, "%s\n", ipa_offload_string(ipa)); } static DEVICE_ATTR_RO(rx_offload); @@ -80,7 +80,7 @@ static ssize_t tx_offload_show(struct device *dev, { struct ipa *ipa = dev_get_drvdata(dev); - return scnprintf(buf, PAGE_SIZE, "%s\n", ipa_offload_string(ipa)); + return sysfs_emit(buf, "%s\n", ipa_offload_string(ipa)); } static DEVICE_ATTR_RO(tx_offload); From de624864496628356eecdfa6751f7995bbb996d1 Mon Sep 17 00:00:00 2001 From: Diana Wang Date: Fri, 2 Dec 2022 10:42:14 +0100 Subject: [PATCH 1591/2002] nfp: add support for multicast filter Rewrite nfp_net_set_rx_mode() to implement interface to delivery mc address and operations to firmware by using general mailbox for filtering multicast packets. The operations include add mc address and delete mc address. And the limitation of mc addresses number is 1024 for each net device. User triggers adding mc address by using command below: ip maddress add dev User triggers deleting mc address by using command below: ip maddress del dev Signed-off-by: Diana Wang Signed-off-by: Simon Horman Reviewed-by: Leon Romanovsky Signed-off-by: David S. Miller --- drivers/net/ethernet/netronome/nfp/nfp_net.h | 5 ++ .../ethernet/netronome/nfp/nfp_net_common.c | 63 +++++++++++++++++-- .../net/ethernet/netronome/nfp/nfp_net_ctrl.h | 15 +++++ 3 files changed, 79 insertions(+), 4 deletions(-) diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net.h b/drivers/net/ethernet/netronome/nfp/nfp_net.h index 6c83e47d8b3d..da33f09facb9 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net.h +++ b/drivers/net/ethernet/netronome/nfp/nfp_net.h @@ -88,6 +88,9 @@ #define NFP_NET_FL_BATCH 16 /* Add freelist in this Batch size */ #define NFP_NET_XDP_MAX_COMPLETE 2048 /* XDP bufs to reclaim in NAPI poll */ +/* MC definitions */ +#define NFP_NET_CFG_MAC_MC_MAX 1024 /* The maximum number of MC address per port*/ + /* Offload definitions */ #define NFP_NET_N_VXLAN_PORTS (NFP_NET_CFG_VXLAN_SZ / sizeof(__be16)) @@ -476,6 +479,7 @@ struct nfp_stat_pair { * @rx_dma_off: Offset at which DMA packets (for XDP headroom) * @rx_offset: Offset in the RX buffers where packet data starts * @ctrl: Local copy of the control register/word. + * @ctrl_w1: Local copy of the control register/word1. * @fl_bufsz: Currently configured size of the freelist buffers * @xdp_prog: Installed XDP program * @tx_rings: Array of pre-allocated TX ring structures @@ -508,6 +512,7 @@ struct nfp_net_dp { u32 rx_dma_off; u32 ctrl; + u32 ctrl_w1; u32 fl_bufsz; struct bpf_prog *xdp_prog; diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c index 682a9198fb54..2314cf55e821 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c @@ -1007,6 +1007,7 @@ static int nfp_net_set_config_and_enable(struct nfp_net *nn) new_ctrl |= NFP_NET_CFG_CTRL_RINGCFG; nn_writel(nn, NFP_NET_CFG_CTRL, new_ctrl); + nn_writel(nn, NFP_NET_CFG_CTRL_WORD1, nn->dp.ctrl_w1); err = nfp_net_reconfig(nn, update); if (err) { nfp_net_clear_config_and_disable(nn); @@ -1333,18 +1334,59 @@ err_unlock: return err; } +static int nfp_net_mc_cfg(struct net_device *netdev, const unsigned char *addr, const u32 cmd) +{ + struct nfp_net *nn = netdev_priv(netdev); + int ret; + + ret = nfp_net_mbox_lock(nn, NFP_NET_CFG_MULTICAST_SZ); + if (ret) + return ret; + + nn_writel(nn, nn->tlv_caps.mbox_off + NFP_NET_CFG_MULTICAST_MAC_HI, + get_unaligned_be32(addr)); + nn_writew(nn, nn->tlv_caps.mbox_off + NFP_NET_CFG_MULTICAST_MAC_LO, + get_unaligned_be16(addr + 4)); + + return nfp_net_mbox_reconfig_and_unlock(nn, cmd); +} + +static int nfp_net_mc_sync(struct net_device *netdev, const unsigned char *addr) +{ + struct nfp_net *nn = netdev_priv(netdev); + + if (netdev_mc_count(netdev) > NFP_NET_CFG_MAC_MC_MAX) { + nn_err(nn, "Requested number of MC addresses (%d) exceeds maximum (%d).\n", + netdev_mc_count(netdev), NFP_NET_CFG_MAC_MC_MAX); + return -EINVAL; + } + + return nfp_net_mc_cfg(netdev, addr, NFP_NET_CFG_MBOX_CMD_MULTICAST_ADD); +} + +static int nfp_net_mc_unsync(struct net_device *netdev, const unsigned char *addr) +{ + return nfp_net_mc_cfg(netdev, addr, NFP_NET_CFG_MBOX_CMD_MULTICAST_DEL); +} + static void nfp_net_set_rx_mode(struct net_device *netdev) { struct nfp_net *nn = netdev_priv(netdev); - u32 new_ctrl; + u32 new_ctrl, new_ctrl_w1; new_ctrl = nn->dp.ctrl; + new_ctrl_w1 = nn->dp.ctrl_w1; if (!netdev_mc_empty(netdev) || netdev->flags & IFF_ALLMULTI) new_ctrl |= nn->cap & NFP_NET_CFG_CTRL_L2MC; else new_ctrl &= ~NFP_NET_CFG_CTRL_L2MC; + if (netdev->flags & IFF_ALLMULTI) + new_ctrl_w1 &= ~NFP_NET_CFG_CTRL_MCAST_FILTER; + else + new_ctrl_w1 |= nn->cap_w1 & NFP_NET_CFG_CTRL_MCAST_FILTER; + if (netdev->flags & IFF_PROMISC) { if (nn->cap & NFP_NET_CFG_CTRL_PROMISC) new_ctrl |= NFP_NET_CFG_CTRL_PROMISC; @@ -1354,13 +1396,21 @@ static void nfp_net_set_rx_mode(struct net_device *netdev) new_ctrl &= ~NFP_NET_CFG_CTRL_PROMISC; } - if (new_ctrl == nn->dp.ctrl) + if ((nn->cap_w1 & NFP_NET_CFG_CTRL_MCAST_FILTER) && + __dev_mc_sync(netdev, nfp_net_mc_sync, nfp_net_mc_unsync)) + netdev_err(netdev, "Sync mc address failed\n"); + + if (new_ctrl == nn->dp.ctrl && new_ctrl_w1 == nn->dp.ctrl_w1) return; - nn_writel(nn, NFP_NET_CFG_CTRL, new_ctrl); + if (new_ctrl != nn->dp.ctrl) + nn_writel(nn, NFP_NET_CFG_CTRL, new_ctrl); + if (new_ctrl_w1 != nn->dp.ctrl_w1) + nn_writel(nn, NFP_NET_CFG_CTRL_WORD1, new_ctrl_w1); nfp_net_reconfig_post(nn, NFP_NET_CFG_UPDATE_GEN); nn->dp.ctrl = new_ctrl; + nn->dp.ctrl_w1 = new_ctrl_w1; } static void nfp_net_rss_init_itbl(struct nfp_net *nn) @@ -2092,7 +2142,7 @@ void nfp_net_info(struct nfp_net *nn) nn->fw_ver.extend, nn->fw_ver.class, nn->fw_ver.major, nn->fw_ver.minor, nn->max_mtu); - nn_info(nn, "CAP: %#x %s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n", + nn_info(nn, "CAP: %#x %s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n", nn->cap, nn->cap & NFP_NET_CFG_CTRL_PROMISC ? "PROMISC " : "", nn->cap & NFP_NET_CFG_CTRL_L2BC ? "L2BCFILT " : "", @@ -2120,6 +2170,7 @@ void nfp_net_info(struct nfp_net *nn) nn->cap & NFP_NET_CFG_CTRL_CSUM_COMPLETE ? "RXCSUM_COMPLETE " : "", nn->cap & NFP_NET_CFG_CTRL_LIVE_ADDR ? "LIVE_ADDR " : "", + nn->cap_w1 & NFP_NET_CFG_CTRL_MCAST_FILTER ? "MULTICAST_FILTER " : "", nfp_app_extra_cap(nn->app, nn)); } @@ -2548,6 +2599,9 @@ int nfp_net_init(struct nfp_net *nn) if (nn->cap & NFP_NET_CFG_CTRL_TXRWB) nn->dp.ctrl |= NFP_NET_CFG_CTRL_TXRWB; + if (nn->cap_w1 & NFP_NET_CFG_CTRL_MCAST_FILTER) + nn->dp.ctrl_w1 |= NFP_NET_CFG_CTRL_MCAST_FILTER; + /* Stash the re-configuration queue away. First odd queue in TX Bar */ nn->qcp_cfg = nn->tx_bar + NFP_QCP_QUEUE_ADDR_SZ; @@ -2555,6 +2609,7 @@ int nfp_net_init(struct nfp_net *nn) nn_writel(nn, NFP_NET_CFG_CTRL, 0); nn_writeq(nn, NFP_NET_CFG_TXRS_ENABLE, 0); nn_writeq(nn, NFP_NET_CFG_RXRS_ENABLE, 0); + nn_writel(nn, NFP_NET_CFG_CTRL_WORD1, 0); err = nfp_net_reconfig(nn, NFP_NET_CFG_UPDATE_RING | NFP_NET_CFG_UPDATE_GEN); if (err) diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_ctrl.h b/drivers/net/ethernet/netronome/nfp/nfp_net_ctrl.h index cc11b3dc1252..51124309ae1f 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_ctrl.h +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_ctrl.h @@ -267,6 +267,7 @@ #define NFP_NET_CFG_CTRL_WORD1 0x0098 #define NFP_NET_CFG_CTRL_PKT_TYPE (0x1 << 0) /* Pkttype offload */ #define NFP_NET_CFG_CTRL_IPSEC (0x1 << 1) /* IPsec offload */ +#define NFP_NET_CFG_CTRL_MCAST_FILTER (0x1 << 2) /* Multicast Filter */ #define NFP_NET_CFG_CAP_WORD1 0x00a4 @@ -413,6 +414,9 @@ #define NFP_NET_CFG_MBOX_CMD_PCI_DSCP_PRIOMAP_SET 5 #define NFP_NET_CFG_MBOX_CMD_TLV_CMSG 6 +#define NFP_NET_CFG_MBOX_CMD_MULTICAST_ADD 8 +#define NFP_NET_CFG_MBOX_CMD_MULTICAST_DEL 9 + /* VLAN filtering using general use mailbox * %NFP_NET_CFG_VLAN_FILTER: Base address of VLAN filter mailbox * %NFP_NET_CFG_VLAN_FILTER_VID: VLAN ID to filter @@ -424,6 +428,17 @@ #define NFP_NET_CFG_VLAN_FILTER_PROTO (NFP_NET_CFG_VLAN_FILTER + 2) #define NFP_NET_CFG_VLAN_FILTER_SZ 0x0004 +/* Multicast filtering using general use mailbox + * %NFP_NET_CFG_MULTICAST: Base address of Multicast filter mailbox + * %NFP_NET_CFG_MULTICAST_MAC_HI: High 32-bits of Multicast MAC address + * %NFP_NET_CFG_MULTICAST_MAC_LO: Low 16-bits of Multicast MAC address + * %NFP_NET_CFG_MULTICAST_SZ: Size of the Multicast filter mailbox in bytes + */ +#define NFP_NET_CFG_MULTICAST NFP_NET_CFG_MBOX_SIMPLE_VAL +#define NFP_NET_CFG_MULTICAST_MAC_HI NFP_NET_CFG_MULTICAST +#define NFP_NET_CFG_MULTICAST_MAC_LO (NFP_NET_CFG_MULTICAST + 6) +#define NFP_NET_CFG_MULTICAST_SZ 0x0006 + /* TLV capabilities * %NFP_NET_CFG_TLV_TYPE: Offset of type within the TLV * %NFP_NET_CFG_TLV_TYPE_REQUIRED: Driver must be able to parse the TLV From 343a5d358e4ab5597e90e1eafa7eba55eb42e96b Mon Sep 17 00:00:00 2001 From: Michael Walle Date: Fri, 2 Dec 2022 15:49:00 +0100 Subject: [PATCH 1592/2002] net: phy: mxl-gpy: rename MMD_VEND1 macros to match datasheet Rename the temperature sensors macros to match the names in the datasheet. Signed-off-by: Michael Walle Reviewed-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/phy/mxl-gpy.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/net/phy/mxl-gpy.c b/drivers/net/phy/mxl-gpy.c index 27c0f161623e..0ff7ef076072 100644 --- a/drivers/net/phy/mxl-gpy.c +++ b/drivers/net/phy/mxl-gpy.c @@ -78,8 +78,8 @@ VSPEC1_SGMII_CTRL_ANRS) /* Temperature sensor */ -#define VPSPEC1_TEMP_STA 0x0E -#define VPSPEC1_TEMP_STA_DATA GENMASK(9, 0) +#define VSPEC1_TEMP_STA 0x0E +#define VSPEC1_TEMP_STA_DATA GENMASK(9, 0) /* WoL */ #define VPSPEC2_WOL_CTL 0x0E06 @@ -140,14 +140,14 @@ static int gpy_hwmon_read(struct device *dev, struct phy_device *phydev = dev_get_drvdata(dev); int ret; - ret = phy_read_mmd(phydev, MDIO_MMD_VEND1, VPSPEC1_TEMP_STA); + ret = phy_read_mmd(phydev, MDIO_MMD_VEND1, VSPEC1_TEMP_STA); if (ret < 0) return ret; if (!ret) return -ENODATA; *value = polynomial_calc(&poly_N_to_temp, - FIELD_GET(VPSPEC1_TEMP_STA_DATA, ret)); + FIELD_GET(VSPEC1_TEMP_STA_DATA, ret)); return 0; } From 7112a04664bfc10ae4709b2079fe3991cbd1fe18 Mon Sep 17 00:00:00 2001 From: Sudheer Mogilappagari Date: Thu, 1 Dec 2022 16:25:55 -0800 Subject: [PATCH 1593/2002] ethtool: add netlink based get rss support Add netlink based support for "ethtool -x [context x]" command by implementing ETHTOOL_MSG_RSS_GET netlink message. This is equivalent to functionality provided via ETHTOOL_GRSSH in ioctl path. It sends RSS table, hash key and hash function of an interface to user space. This patch implements existing functionality available in ioctl path and enables addition of new RSS context based parameters in future. Signed-off-by: Sudheer Mogilappagari Link: https://lore.kernel.org/r/20221202002555.241580-1-sudheer.mogilappagari@intel.com Signed-off-by: Jakub Kicinski --- Documentation/networking/ethtool-netlink.rst | 31 +++- include/uapi/linux/ethtool_netlink.h | 14 ++ net/ethtool/Makefile | 2 +- net/ethtool/netlink.c | 7 + net/ethtool/netlink.h | 2 + net/ethtool/rss.c | 153 +++++++++++++++++++ 6 files changed, 207 insertions(+), 2 deletions(-) create mode 100644 net/ethtool/rss.c diff --git a/Documentation/networking/ethtool-netlink.rst b/Documentation/networking/ethtool-netlink.rst index bede24ef44fd..f10f8eb44255 100644 --- a/Documentation/networking/ethtool-netlink.rst +++ b/Documentation/networking/ethtool-netlink.rst @@ -222,6 +222,7 @@ Userspace to kernel: ``ETHTOOL_MSG_MODULE_GET`` get transceiver module parameters ``ETHTOOL_MSG_PSE_SET`` set PSE parameters ``ETHTOOL_MSG_PSE_GET`` get PSE parameters + ``ETHTOOL_MSG_RSS_GET`` get RSS settings ===================================== ================================= Kernel to userspace: @@ -263,6 +264,7 @@ Kernel to userspace: ``ETHTOOL_MSG_PHC_VCLOCKS_GET_REPLY`` PHC virtual clocks info ``ETHTOOL_MSG_MODULE_GET_REPLY`` transceiver module parameters ``ETHTOOL_MSG_PSE_GET_REPLY`` PSE parameters + ``ETHTOOL_MSG_RSS_GET_REPLY`` RSS settings ======================================== ================================= ``GET`` requests are sent by userspace applications to retrieve device @@ -1687,6 +1689,33 @@ to control PoDL PSE Admin functions. This option is implementing ``IEEE 802.3-2018`` 30.15.1.2.1 acPoDLPSEAdminControl. See ``ETHTOOL_A_PODL_PSE_ADMIN_STATE`` for supported values. +RSS_GET +======= + +Get indirection table, hash key and hash function info associated with a +RSS context of an interface similar to ``ETHTOOL_GRSSH`` ioctl request. + +Request contents: + +===================================== ====== ========================== + ``ETHTOOL_A_RSS_HEADER`` nested request header + ``ETHTOOL_A_RSS_CONTEXT`` u32 context number +===================================== ====== ========================== + +Kernel response contents: + +===================================== ====== ========================== + ``ETHTOOL_A_RSS_HEADER`` nested reply header + ``ETHTOOL_A_RSS_HFUNC`` u32 RSS hash func + ``ETHTOOL_A_RSS_INDIR`` binary Indir table bytes + ``ETHTOOL_A_RSS_HKEY`` binary Hash key bytes +===================================== ====== ========================== + +ETHTOOL_A_RSS_HFUNC attribute is bitmap indicating the hash function +being used. Current supported options are toeplitz, xor or crc32. +ETHTOOL_A_RSS_INDIR attribute returns RSS indrection table where each byte +indicates queue number. + Request translation =================== @@ -1768,7 +1797,7 @@ are netlink only. ``ETHTOOL_GMODULEEEPROM`` ``ETHTOOL_MSG_MODULE_EEPROM_GET`` ``ETHTOOL_GEEE`` ``ETHTOOL_MSG_EEE_GET`` ``ETHTOOL_SEEE`` ``ETHTOOL_MSG_EEE_SET`` - ``ETHTOOL_GRSSH`` n/a + ``ETHTOOL_GRSSH`` ``ETHTOOL_MSG_RSS_GET`` ``ETHTOOL_SRSSH`` n/a ``ETHTOOL_GTUNABLE`` n/a ``ETHTOOL_STUNABLE`` n/a diff --git a/include/uapi/linux/ethtool_netlink.h b/include/uapi/linux/ethtool_netlink.h index aaf7c6963d61..5799a9db034e 100644 --- a/include/uapi/linux/ethtool_netlink.h +++ b/include/uapi/linux/ethtool_netlink.h @@ -51,6 +51,7 @@ enum { ETHTOOL_MSG_MODULE_SET, ETHTOOL_MSG_PSE_GET, ETHTOOL_MSG_PSE_SET, + ETHTOOL_MSG_RSS_GET, /* add new constants above here */ __ETHTOOL_MSG_USER_CNT, @@ -97,6 +98,7 @@ enum { ETHTOOL_MSG_MODULE_GET_REPLY, ETHTOOL_MSG_MODULE_NTF, ETHTOOL_MSG_PSE_GET_REPLY, + ETHTOOL_MSG_RSS_GET_REPLY, /* add new constants above here */ __ETHTOOL_MSG_KERNEL_CNT, @@ -880,6 +882,18 @@ enum { ETHTOOL_A_PSE_MAX = (__ETHTOOL_A_PSE_CNT - 1) }; +enum { + ETHTOOL_A_RSS_UNSPEC, + ETHTOOL_A_RSS_HEADER, + ETHTOOL_A_RSS_CONTEXT, /* u32 */ + ETHTOOL_A_RSS_HFUNC, /* u32 */ + ETHTOOL_A_RSS_INDIR, /* binary */ + ETHTOOL_A_RSS_HKEY, /* binary */ + + __ETHTOOL_A_RSS_CNT, + ETHTOOL_A_RSS_MAX = (__ETHTOOL_A_RSS_CNT - 1), +}; + /* generic netlink info */ #define ETHTOOL_GENL_NAME "ethtool" #define ETHTOOL_GENL_VERSION 1 diff --git a/net/ethtool/Makefile b/net/ethtool/Makefile index 72ab0944262a..228f13df2e18 100644 --- a/net/ethtool/Makefile +++ b/net/ethtool/Makefile @@ -4,7 +4,7 @@ obj-y += ioctl.o common.o obj-$(CONFIG_ETHTOOL_NETLINK) += ethtool_nl.o -ethtool_nl-y := netlink.o bitset.o strset.o linkinfo.o linkmodes.o \ +ethtool_nl-y := netlink.o bitset.o strset.o linkinfo.o linkmodes.o rss.o \ linkstate.o debug.o wol.o features.o privflags.o rings.o \ channels.o coalesce.o pause.o eee.o tsinfo.o cabletest.o \ tunnels.o fec.o eeprom.o stats.o phc_vclocks.o module.o \ diff --git a/net/ethtool/netlink.c b/net/ethtool/netlink.c index 1a4c11356c96..aee98be6237f 100644 --- a/net/ethtool/netlink.c +++ b/net/ethtool/netlink.c @@ -287,6 +287,7 @@ ethnl_default_requests[__ETHTOOL_MSG_USER_CNT] = { [ETHTOOL_MSG_PHC_VCLOCKS_GET] = ðnl_phc_vclocks_request_ops, [ETHTOOL_MSG_MODULE_GET] = ðnl_module_request_ops, [ETHTOOL_MSG_PSE_GET] = ðnl_pse_request_ops, + [ETHTOOL_MSG_RSS_GET] = ðnl_rss_request_ops, }; static struct ethnl_dump_ctx *ethnl_dump_context(struct netlink_callback *cb) @@ -1040,6 +1041,12 @@ static const struct genl_ops ethtool_genl_ops[] = { .policy = ethnl_pse_set_policy, .maxattr = ARRAY_SIZE(ethnl_pse_set_policy) - 1, }, + { + .cmd = ETHTOOL_MSG_RSS_GET, + .doit = ethnl_default_doit, + .policy = ethnl_rss_get_policy, + .maxattr = ARRAY_SIZE(ethnl_rss_get_policy) - 1, + }, }; static const struct genl_multicast_group ethtool_nl_mcgrps[] = { diff --git a/net/ethtool/netlink.h b/net/ethtool/netlink.h index 1bfd374f9718..3753787ba233 100644 --- a/net/ethtool/netlink.h +++ b/net/ethtool/netlink.h @@ -346,6 +346,7 @@ extern const struct ethnl_request_ops ethnl_stats_request_ops; extern const struct ethnl_request_ops ethnl_phc_vclocks_request_ops; extern const struct ethnl_request_ops ethnl_module_request_ops; extern const struct ethnl_request_ops ethnl_pse_request_ops; +extern const struct ethnl_request_ops ethnl_rss_request_ops; extern const struct nla_policy ethnl_header_policy[ETHTOOL_A_HEADER_FLAGS + 1]; extern const struct nla_policy ethnl_header_policy_stats[ETHTOOL_A_HEADER_FLAGS + 1]; @@ -386,6 +387,7 @@ extern const struct nla_policy ethnl_module_get_policy[ETHTOOL_A_MODULE_HEADER + extern const struct nla_policy ethnl_module_set_policy[ETHTOOL_A_MODULE_POWER_MODE_POLICY + 1]; extern const struct nla_policy ethnl_pse_get_policy[ETHTOOL_A_PSE_HEADER + 1]; extern const struct nla_policy ethnl_pse_set_policy[ETHTOOL_A_PSE_MAX + 1]; +extern const struct nla_policy ethnl_rss_get_policy[ETHTOOL_A_RSS_CONTEXT + 1]; int ethnl_set_linkinfo(struct sk_buff *skb, struct genl_info *info); int ethnl_set_linkmodes(struct sk_buff *skb, struct genl_info *info); diff --git a/net/ethtool/rss.c b/net/ethtool/rss.c new file mode 100644 index 000000000000..ebe6145aed3f --- /dev/null +++ b/net/ethtool/rss.c @@ -0,0 +1,153 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include "netlink.h" +#include "common.h" + +struct rss_req_info { + struct ethnl_req_info base; + u32 rss_context; +}; + +struct rss_reply_data { + struct ethnl_reply_data base; + u32 indir_size; + u32 hkey_size; + u32 hfunc; + u32 *indir_table; + u8 *hkey; +}; + +#define RSS_REQINFO(__req_base) \ + container_of(__req_base, struct rss_req_info, base) + +#define RSS_REPDATA(__reply_base) \ + container_of(__reply_base, struct rss_reply_data, base) + +const struct nla_policy ethnl_rss_get_policy[] = { + [ETHTOOL_A_RSS_HEADER] = NLA_POLICY_NESTED(ethnl_header_policy), + [ETHTOOL_A_RSS_CONTEXT] = { .type = NLA_U32 }, +}; + +static int +rss_parse_request(struct ethnl_req_info *req_info, struct nlattr **tb, + struct netlink_ext_ack *extack) +{ + struct rss_req_info *request = RSS_REQINFO(req_info); + + if (tb[ETHTOOL_A_RSS_CONTEXT]) + request->rss_context = nla_get_u32(tb[ETHTOOL_A_RSS_CONTEXT]); + + return 0; +} + +static int +rss_prepare_data(const struct ethnl_req_info *req_base, + struct ethnl_reply_data *reply_base, struct genl_info *info) +{ + struct rss_reply_data *data = RSS_REPDATA(reply_base); + struct rss_req_info *request = RSS_REQINFO(req_base); + struct net_device *dev = reply_base->dev; + const struct ethtool_ops *ops; + u32 total_size, indir_bytes; + u8 dev_hfunc = 0; + u8 *rss_config; + int ret; + + ops = dev->ethtool_ops; + if (!ops->get_rxfh) + return -EOPNOTSUPP; + + /* Some drivers don't handle rss_context */ + if (request->rss_context && !ops->get_rxfh_context) + return -EOPNOTSUPP; + + ret = ethnl_ops_begin(dev); + if (ret < 0) + return ret; + + data->indir_size = 0; + data->hkey_size = 0; + if (ops->get_rxfh_indir_size) + data->indir_size = ops->get_rxfh_indir_size(dev); + if (ops->get_rxfh_key_size) + data->hkey_size = ops->get_rxfh_key_size(dev); + + indir_bytes = data->indir_size * sizeof(u32); + total_size = indir_bytes + data->hkey_size; + rss_config = kzalloc(total_size, GFP_KERNEL); + if (!rss_config) { + ret = -ENOMEM; + goto out_ops; + } + + if (data->indir_size) + data->indir_table = (u32 *)rss_config; + + if (data->hkey_size) + data->hkey = rss_config + indir_bytes; + + if (request->rss_context) + ret = ops->get_rxfh_context(dev, data->indir_table, data->hkey, + &dev_hfunc, request->rss_context); + else + ret = ops->get_rxfh(dev, data->indir_table, data->hkey, + &dev_hfunc); + + if (ret) + goto out_ops; + + data->hfunc = dev_hfunc; +out_ops: + ethnl_ops_complete(dev); + return ret; +} + +static int +rss_reply_size(const struct ethnl_req_info *req_base, + const struct ethnl_reply_data *reply_base) +{ + const struct rss_reply_data *data = RSS_REPDATA(reply_base); + int len; + + len = nla_total_size(sizeof(u32)) + /* _RSS_HFUNC */ + nla_total_size(sizeof(u32) * data->indir_size) + /* _RSS_INDIR */ + nla_total_size(data->hkey_size); /* _RSS_HKEY */ + + return len; +} + +static int +rss_fill_reply(struct sk_buff *skb, const struct ethnl_req_info *req_base, + const struct ethnl_reply_data *reply_base) +{ + const struct rss_reply_data *data = RSS_REPDATA(reply_base); + + if (nla_put_u32(skb, ETHTOOL_A_RSS_HFUNC, data->hfunc) || + nla_put(skb, ETHTOOL_A_RSS_INDIR, + sizeof(u32) * data->indir_size, data->indir_table) || + nla_put(skb, ETHTOOL_A_RSS_HKEY, data->hkey_size, data->hkey)) + return -EMSGSIZE; + + return 0; +} + +static void rss_cleanup_data(struct ethnl_reply_data *reply_base) +{ + const struct rss_reply_data *data = RSS_REPDATA(reply_base); + + kfree(data->indir_table); +} + +const struct ethnl_request_ops ethnl_rss_request_ops = { + .request_cmd = ETHTOOL_MSG_RSS_GET, + .reply_cmd = ETHTOOL_MSG_RSS_GET_REPLY, + .hdr_attr = ETHTOOL_A_RSS_HEADER, + .req_info_size = sizeof(struct rss_req_info), + .reply_data_size = sizeof(struct rss_reply_data), + + .parse_request = rss_parse_request, + .prepare_data = rss_prepare_data, + .reply_size = rss_reply_size, + .fill_reply = rss_fill_reply, + .cleanup_data = rss_cleanup_data, +}; From c9f8d73645b6f76c8d14f49bc860f7143d001cb7 Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Sat, 3 Dec 2022 14:20:37 +0100 Subject: [PATCH 1594/2002] net: mtk_eth_soc: enable flow offload support for MT7986 SoC Since Wireless Ethernet Dispatcher is now available for mt7986 in mt76, enable hw flow support for MT7986 SoC. Signed-off-by: Lorenzo Bianconi Link: https://lore.kernel.org/r/fdcaacd827938e6a8c4aa1ac2c13e46d2c08c821.1670072898.git.lorenzo@kernel.org Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/mediatek/mtk_eth_soc.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c index 8b93dab79141..e3de9a53b2d9 100644 --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c @@ -4593,6 +4593,7 @@ static const struct mtk_soc_data mt7986_data = { .hw_features = MTK_HW_FEATURES, .required_clks = MT7986_CLKS_BITMAP, .required_pctl = false, + .offload_version = 2, .hash_offset = 4, .foe_entry_size = sizeof(struct mtk_foe_entry), .txrx = { From ee9a113ab63468137802898bcd2c598998c96938 Mon Sep 17 00:00:00 2001 From: Eyal Birger Date: Sat, 3 Dec 2022 10:46:56 +0200 Subject: [PATCH 1595/2002] xfrm: interface: rename xfrm_interface.c to xfrm_interface_core.c This change allows adding additional files to the xfrm_interface module. Signed-off-by: Eyal Birger Link: https://lore.kernel.org/r/20221203084659.1837829-2-eyal.birger@gmail.com Signed-off-by: Martin KaFai Lau --- net/xfrm/Makefile | 2 ++ net/xfrm/{xfrm_interface.c => xfrm_interface_core.c} | 0 2 files changed, 2 insertions(+) rename net/xfrm/{xfrm_interface.c => xfrm_interface_core.c} (100%) diff --git a/net/xfrm/Makefile b/net/xfrm/Makefile index 494aa744bfb9..08a2870fdd36 100644 --- a/net/xfrm/Makefile +++ b/net/xfrm/Makefile @@ -3,6 +3,8 @@ # Makefile for the XFRM subsystem. # +xfrm_interface-$(CONFIG_XFRM_INTERFACE) += xfrm_interface_core.o + obj-$(CONFIG_XFRM) := xfrm_policy.o xfrm_state.o xfrm_hash.o \ xfrm_input.o xfrm_output.o \ xfrm_sysctl.o xfrm_replay.o xfrm_device.o diff --git a/net/xfrm/xfrm_interface.c b/net/xfrm/xfrm_interface_core.c similarity index 100% rename from net/xfrm/xfrm_interface.c rename to net/xfrm/xfrm_interface_core.c From 94151f5aa9667c562281abeaaa5e89b9d5c17729 Mon Sep 17 00:00:00 2001 From: Eyal Birger Date: Sat, 3 Dec 2022 10:46:57 +0200 Subject: [PATCH 1596/2002] xfrm: interface: Add unstable helpers for setting/getting XFRM metadata from TC-BPF This change adds xfrm metadata helpers using the unstable kfunc call interface for the TC-BPF hooks. This allows steering traffic towards different IPsec connections based on logic implemented in bpf programs. This object is built based on the availability of BTF debug info. When setting the xfrm metadata, percpu metadata dsts are used in order to avoid allocating a metadata dst per packet. In order to guarantee safe module unload, the percpu dsts are allocated on first use and never freed. The percpu pointer is stored in net/core/filter.c so that it can be reused on module reload. The metadata percpu dsts take ownership of the original skb dsts so that they may be used as part of the xfrm transmission logic - e.g. for MTU calculations. Signed-off-by: Eyal Birger Link: https://lore.kernel.org/r/20221203084659.1837829-3-eyal.birger@gmail.com Signed-off-by: Martin KaFai Lau --- include/net/dst_metadata.h | 1 + include/net/xfrm.h | 17 +++++ net/core/dst.c | 8 ++- net/core/filter.c | 9 +++ net/xfrm/Makefile | 6 ++ net/xfrm/xfrm_interface_bpf.c | 115 +++++++++++++++++++++++++++++++++ net/xfrm/xfrm_interface_core.c | 14 ++++ 7 files changed, 168 insertions(+), 2 deletions(-) create mode 100644 net/xfrm/xfrm_interface_bpf.c diff --git a/include/net/dst_metadata.h b/include/net/dst_metadata.h index a454cf4327fe..1b7fae4c6b24 100644 --- a/include/net/dst_metadata.h +++ b/include/net/dst_metadata.h @@ -26,6 +26,7 @@ struct macsec_info { struct xfrm_md_info { u32 if_id; int link; + struct dst_entry *dst_orig; }; struct metadata_dst { diff --git a/include/net/xfrm.h b/include/net/xfrm.h index e0cc6791c001..3707e6b34e67 100644 --- a/include/net/xfrm.h +++ b/include/net/xfrm.h @@ -2086,4 +2086,21 @@ static inline bool xfrm6_local_dontfrag(const struct sock *sk) return false; } #endif + +#if (IS_BUILTIN(CONFIG_XFRM_INTERFACE) && IS_ENABLED(CONFIG_DEBUG_INFO_BTF)) || \ + (IS_MODULE(CONFIG_XFRM_INTERFACE) && IS_ENABLED(CONFIG_DEBUG_INFO_BTF_MODULES)) + +extern struct metadata_dst __percpu *xfrm_bpf_md_dst; + +int register_xfrm_interface_bpf(void); + +#else + +static inline int register_xfrm_interface_bpf(void) +{ + return 0; +} + +#endif + #endif /* _NET_XFRM_H */ diff --git a/net/core/dst.c b/net/core/dst.c index bc9c9be4e080..bb14a0392388 100644 --- a/net/core/dst.c +++ b/net/core/dst.c @@ -316,6 +316,8 @@ void metadata_dst_free(struct metadata_dst *md_dst) if (md_dst->type == METADATA_IP_TUNNEL) dst_cache_destroy(&md_dst->u.tun_info.dst_cache); #endif + if (md_dst->type == METADATA_XFRM) + dst_release(md_dst->u.xfrm_info.dst_orig); kfree(md_dst); } EXPORT_SYMBOL_GPL(metadata_dst_free); @@ -340,16 +342,18 @@ EXPORT_SYMBOL_GPL(metadata_dst_alloc_percpu); void metadata_dst_free_percpu(struct metadata_dst __percpu *md_dst) { -#ifdef CONFIG_DST_CACHE int cpu; for_each_possible_cpu(cpu) { struct metadata_dst *one_md_dst = per_cpu_ptr(md_dst, cpu); +#ifdef CONFIG_DST_CACHE if (one_md_dst->type == METADATA_IP_TUNNEL) dst_cache_destroy(&one_md_dst->u.tun_info.dst_cache); - } #endif + if (one_md_dst->type == METADATA_XFRM) + dst_release(one_md_dst->u.xfrm_info.dst_orig); + } free_percpu(md_dst); } EXPORT_SYMBOL_GPL(metadata_dst_free_percpu); diff --git a/net/core/filter.c b/net/core/filter.c index 8607136b6e2c..929358677183 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -5631,6 +5631,15 @@ static const struct bpf_func_proto bpf_bind_proto = { }; #ifdef CONFIG_XFRM + +#if (IS_BUILTIN(CONFIG_XFRM_INTERFACE) && IS_ENABLED(CONFIG_DEBUG_INFO_BTF)) || \ + (IS_MODULE(CONFIG_XFRM_INTERFACE) && IS_ENABLED(CONFIG_DEBUG_INFO_BTF_MODULES)) + +struct metadata_dst __percpu *xfrm_bpf_md_dst; +EXPORT_SYMBOL_GPL(xfrm_bpf_md_dst); + +#endif + BPF_CALL_5(bpf_skb_get_xfrm_state, struct sk_buff *, skb, u32, index, struct bpf_xfrm_state *, to, u32, size, u64, flags) { diff --git a/net/xfrm/Makefile b/net/xfrm/Makefile index 08a2870fdd36..cd47f88921f5 100644 --- a/net/xfrm/Makefile +++ b/net/xfrm/Makefile @@ -5,6 +5,12 @@ xfrm_interface-$(CONFIG_XFRM_INTERFACE) += xfrm_interface_core.o +ifeq ($(CONFIG_XFRM_INTERFACE),m) +xfrm_interface-$(CONFIG_DEBUG_INFO_BTF_MODULES) += xfrm_interface_bpf.o +else ifeq ($(CONFIG_XFRM_INTERFACE),y) +xfrm_interface-$(CONFIG_DEBUG_INFO_BTF) += xfrm_interface_bpf.o +endif + obj-$(CONFIG_XFRM) := xfrm_policy.o xfrm_state.o xfrm_hash.o \ xfrm_input.o xfrm_output.o \ xfrm_sysctl.o xfrm_replay.o xfrm_device.o diff --git a/net/xfrm/xfrm_interface_bpf.c b/net/xfrm/xfrm_interface_bpf.c new file mode 100644 index 000000000000..1ef2162cebcf --- /dev/null +++ b/net/xfrm/xfrm_interface_bpf.c @@ -0,0 +1,115 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* Unstable XFRM Helpers for TC-BPF hook + * + * These are called from SCHED_CLS BPF programs. Note that it is + * allowed to break compatibility for these functions since the interface they + * are exposed through to BPF programs is explicitly unstable. + */ + +#include +#include + +#include +#include + +/* bpf_xfrm_info - XFRM metadata information + * + * Members: + * @if_id - XFRM if_id: + * Transmit: if_id to be used in policy and state lookups + * Receive: if_id of the state matched for the incoming packet + * @link - Underlying device ifindex: + * Transmit: used as the underlying device in VRF routing + * Receive: the device on which the packet had been received + */ +struct bpf_xfrm_info { + u32 if_id; + int link; +}; + +__diag_push(); +__diag_ignore_all("-Wmissing-prototypes", + "Global functions as their definitions will be in xfrm_interface BTF"); + +/* bpf_skb_get_xfrm_info - Get XFRM metadata + * + * Parameters: + * @skb_ctx - Pointer to ctx (__sk_buff) in TC program + * Cannot be NULL + * @to - Pointer to memory to which the metadata will be copied + * Cannot be NULL + */ +__used noinline +int bpf_skb_get_xfrm_info(struct __sk_buff *skb_ctx, struct bpf_xfrm_info *to) +{ + struct sk_buff *skb = (struct sk_buff *)skb_ctx; + struct xfrm_md_info *info; + + info = skb_xfrm_md_info(skb); + if (!info) + return -EINVAL; + + to->if_id = info->if_id; + to->link = info->link; + return 0; +} + +/* bpf_skb_get_xfrm_info - Set XFRM metadata + * + * Parameters: + * @skb_ctx - Pointer to ctx (__sk_buff) in TC program + * Cannot be NULL + * @from - Pointer to memory from which the metadata will be copied + * Cannot be NULL + */ +__used noinline +int bpf_skb_set_xfrm_info(struct __sk_buff *skb_ctx, + const struct bpf_xfrm_info *from) +{ + struct sk_buff *skb = (struct sk_buff *)skb_ctx; + struct metadata_dst *md_dst; + struct xfrm_md_info *info; + + if (unlikely(skb_metadata_dst(skb))) + return -EINVAL; + + if (!xfrm_bpf_md_dst) { + struct metadata_dst __percpu *tmp; + + tmp = metadata_dst_alloc_percpu(0, METADATA_XFRM, GFP_ATOMIC); + if (!tmp) + return -ENOMEM; + if (cmpxchg(&xfrm_bpf_md_dst, NULL, tmp)) + metadata_dst_free_percpu(tmp); + } + md_dst = this_cpu_ptr(xfrm_bpf_md_dst); + + info = &md_dst->u.xfrm_info; + + info->if_id = from->if_id; + info->link = from->link; + skb_dst_force(skb); + info->dst_orig = skb_dst(skb); + + dst_hold((struct dst_entry *)md_dst); + skb_dst_set(skb, (struct dst_entry *)md_dst); + return 0; +} + +__diag_pop() + +BTF_SET8_START(xfrm_ifc_kfunc_set) +BTF_ID_FLAGS(func, bpf_skb_get_xfrm_info) +BTF_ID_FLAGS(func, bpf_skb_set_xfrm_info) +BTF_SET8_END(xfrm_ifc_kfunc_set) + +static const struct btf_kfunc_id_set xfrm_interface_kfunc_set = { + .owner = THIS_MODULE, + .set = &xfrm_ifc_kfunc_set, +}; + +int __init register_xfrm_interface_bpf(void) +{ + return register_btf_kfunc_id_set(BPF_PROG_TYPE_SCHED_CLS, + &xfrm_interface_kfunc_set); +} diff --git a/net/xfrm/xfrm_interface_core.c b/net/xfrm/xfrm_interface_core.c index 5a67b120c4db..1f99dc469027 100644 --- a/net/xfrm/xfrm_interface_core.c +++ b/net/xfrm/xfrm_interface_core.c @@ -396,6 +396,14 @@ xfrmi_xmit2(struct sk_buff *skb, struct net_device *dev, struct flowi *fl) if_id = md_info->if_id; fl->flowi_oif = md_info->link; + if (md_info->dst_orig) { + struct dst_entry *tmp_dst = dst; + + dst = md_info->dst_orig; + skb_dst_set(skb, dst); + md_info->dst_orig = NULL; + dst_release(tmp_dst); + } } else { if_id = xi->p.if_id; } @@ -1162,12 +1170,18 @@ static int __init xfrmi_init(void) if (err < 0) goto rtnl_link_failed; + err = register_xfrm_interface_bpf(); + if (err < 0) + goto kfunc_failed; + lwtunnel_encap_add_ops(&xfrmi_encap_ops, LWTUNNEL_ENCAP_XFRM); xfrm_if_register_cb(&xfrm_if_cb); return err; +kfunc_failed: + rtnl_link_unregister(&xfrmi_link_ops); rtnl_link_failed: xfrmi6_fini(); xfrmi6_failed: From 4f4ac4d9106efeec9c84469725c04c4237c7fb6c Mon Sep 17 00:00:00 2001 From: Eyal Birger Date: Sat, 3 Dec 2022 10:46:58 +0200 Subject: [PATCH 1597/2002] tools: add IFLA_XFRM_COLLECT_METADATA to uapi/linux/if_link.h Needed for XFRM metadata tests. Signed-off-by: Eyal Birger Link: https://lore.kernel.org/r/20221203084659.1837829-4-eyal.birger@gmail.com Signed-off-by: Martin KaFai Lau --- tools/include/uapi/linux/if_link.h | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/include/uapi/linux/if_link.h b/tools/include/uapi/linux/if_link.h index 0242f31e339c..901d98b865a1 100644 --- a/tools/include/uapi/linux/if_link.h +++ b/tools/include/uapi/linux/if_link.h @@ -673,6 +673,7 @@ enum { IFLA_XFRM_UNSPEC, IFLA_XFRM_LINK, IFLA_XFRM_IF_ID, + IFLA_XFRM_COLLECT_METADATA, __IFLA_XFRM_MAX }; From 90a3a05eb33ff7a263f1e93e444d40e427cddf1a Mon Sep 17 00:00:00 2001 From: Eyal Birger Date: Sat, 3 Dec 2022 10:46:59 +0200 Subject: [PATCH 1598/2002] selftests/bpf: add xfrm_info tests Test the xfrm_info kfunc helpers. The test setup creates three name spaces - NS0, NS1, NS2. XFRM tunnels are setup between NS0 and the two other NSs. The kfunc helpers are used to steer traffic from NS0 to the other NSs based on a userspace populated bpf global variable and validate that the return traffic had arrived from the desired NS. Signed-off-by: Eyal Birger Link: https://lore.kernel.org/r/20221203084659.1837829-5-eyal.birger@gmail.com Signed-off-by: Martin KaFai Lau --- tools/testing/selftests/bpf/DENYLIST.s390x | 1 + tools/testing/selftests/bpf/config | 2 + .../selftests/bpf/prog_tests/xfrm_info.c | 362 ++++++++++++++++++ .../selftests/bpf/progs/bpf_tracing_net.h | 3 + tools/testing/selftests/bpf/progs/xfrm_info.c | 35 ++ 5 files changed, 403 insertions(+) create mode 100644 tools/testing/selftests/bpf/prog_tests/xfrm_info.c create mode 100644 tools/testing/selftests/bpf/progs/xfrm_info.c diff --git a/tools/testing/selftests/bpf/DENYLIST.s390x b/tools/testing/selftests/bpf/DENYLIST.s390x index 3481f3a5ea6f..585fcf73c731 100644 --- a/tools/testing/selftests/bpf/DENYLIST.s390x +++ b/tools/testing/selftests/bpf/DENYLIST.s390x @@ -85,3 +85,4 @@ xdp_bonding # failed to auto-attach program 'trace_ xdp_bpf2bpf # failed to auto-attach program 'trace_on_entry': -524 (trampoline) xdp_do_redirect # prog_run_max_size unexpected error: -22 (errno 22) xdp_synproxy # JIT does not support calling kernel function (kfunc) +xfrm_info # JIT does not support calling kernel function (kfunc) diff --git a/tools/testing/selftests/bpf/config b/tools/testing/selftests/bpf/config index f9034ea00bc9..3543c76cef56 100644 --- a/tools/testing/selftests/bpf/config +++ b/tools/testing/selftests/bpf/config @@ -23,6 +23,7 @@ CONFIG_IKCONFIG_PROC=y CONFIG_IMA=y CONFIG_IMA_READ_POLICY=y CONFIG_IMA_WRITE_POLICY=y +CONFIG_INET_ESP=y CONFIG_IP_NF_FILTER=y CONFIG_IP_NF_RAW=y CONFIG_IP_NF_TARGET_SYNPROXY=y @@ -74,3 +75,4 @@ CONFIG_TEST_BPF=y CONFIG_USERFAULTFD=y CONFIG_VXLAN=y CONFIG_XDP_SOCKETS=y +CONFIG_XFRM_INTERFACE=y diff --git a/tools/testing/selftests/bpf/prog_tests/xfrm_info.c b/tools/testing/selftests/bpf/prog_tests/xfrm_info.c new file mode 100644 index 000000000000..8b03c9bb4862 --- /dev/null +++ b/tools/testing/selftests/bpf/prog_tests/xfrm_info.c @@ -0,0 +1,362 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause + +/* + * Topology: + * --------- + * NS0 namespace | NS1 namespace | NS2 namespace + * | | + * +---------------+ | +---------------+ | + * | ipsec0 |---------| ipsec0 | | + * | 192.168.1.100 | | | 192.168.1.200 | | + * | if_id: bpf | | +---------------+ | + * +---------------+ | | + * | | | +---------------+ + * | | | | ipsec0 | + * \------------------------------------------| 192.168.1.200 | + * | | +---------------+ + * | | + * | | (overlay network) + * ------------------------------------------------------ + * | | (underlay network) + * +--------------+ | +--------------+ | + * | veth01 |----------| veth10 | | + * | 172.16.1.100 | | | 172.16.1.200 | | + * ---------------+ | +--------------+ | + * | | + * +--------------+ | | +--------------+ + * | veth02 |-----------------------------------| veth20 | + * | 172.16.2.100 | | | | 172.16.2.200 | + * +--------------+ | | +--------------+ + * + * + * Test Packet flow + * ----------- + * The tests perform 'ping 192.168.1.200' from the NS0 namespace: + * 1) request is routed to NS0 ipsec0 + * 2) NS0 ipsec0 tc egress BPF program is triggered and sets the if_id based + * on the requested value. This makes the ipsec0 device in external mode + * select the destination tunnel + * 3) ping reaches the other namespace (NS1 or NS2 based on which if_id was + * used) and response is sent + * 4) response is received on NS0 ipsec0, tc ingress program is triggered and + * records the response if_id + * 5) requested if_id is compared with received if_id + */ + +#include +#include +#include + +#include "test_progs.h" +#include "network_helpers.h" +#include "xfrm_info.skel.h" + +#define NS0 "xfrm_test_ns0" +#define NS1 "xfrm_test_ns1" +#define NS2 "xfrm_test_ns2" + +#define IF_ID_0_TO_1 1 +#define IF_ID_0_TO_2 2 +#define IF_ID_1 3 +#define IF_ID_2 4 + +#define IP4_ADDR_VETH01 "172.16.1.100" +#define IP4_ADDR_VETH10 "172.16.1.200" +#define IP4_ADDR_VETH02 "172.16.2.100" +#define IP4_ADDR_VETH20 "172.16.2.200" + +#define ESP_DUMMY_PARAMS \ + "proto esp aead 'rfc4106(gcm(aes))' " \ + "0xe4d8f4b4da1df18a3510b3781496daa82488b713 128 mode tunnel " + +#define SYS(fmt, ...) \ + ({ \ + char cmd[1024]; \ + snprintf(cmd, sizeof(cmd), fmt, ##__VA_ARGS__); \ + if (!ASSERT_OK(system(cmd), cmd)) \ + goto fail; \ + }) + +#define SYS_NOFAIL(fmt, ...) \ + ({ \ + char cmd[1024]; \ + snprintf(cmd, sizeof(cmd), fmt, ##__VA_ARGS__); \ + system(cmd); \ + }) + +static int attach_tc_prog(struct bpf_tc_hook *hook, int igr_fd, int egr_fd) +{ + LIBBPF_OPTS(bpf_tc_opts, opts1, .handle = 1, .priority = 1, + .prog_fd = igr_fd); + LIBBPF_OPTS(bpf_tc_opts, opts2, .handle = 1, .priority = 1, + .prog_fd = egr_fd); + int ret; + + ret = bpf_tc_hook_create(hook); + if (!ASSERT_OK(ret, "create tc hook")) + return ret; + + if (igr_fd >= 0) { + hook->attach_point = BPF_TC_INGRESS; + ret = bpf_tc_attach(hook, &opts1); + if (!ASSERT_OK(ret, "bpf_tc_attach")) { + bpf_tc_hook_destroy(hook); + return ret; + } + } + + if (egr_fd >= 0) { + hook->attach_point = BPF_TC_EGRESS; + ret = bpf_tc_attach(hook, &opts2); + if (!ASSERT_OK(ret, "bpf_tc_attach")) { + bpf_tc_hook_destroy(hook); + return ret; + } + } + + return 0; +} + +static void cleanup(void) +{ + SYS_NOFAIL("test -f /var/run/netns/" NS0 " && ip netns delete " NS0); + SYS_NOFAIL("test -f /var/run/netns/" NS1 " && ip netns delete " NS1); + SYS_NOFAIL("test -f /var/run/netns/" NS2 " && ip netns delete " NS2); +} + +static int config_underlay(void) +{ + SYS("ip netns add " NS0); + SYS("ip netns add " NS1); + SYS("ip netns add " NS2); + + /* NS0 <-> NS1 [veth01 <-> veth10] */ + SYS("ip link add veth01 netns " NS0 " type veth peer name veth10 netns " NS1); + SYS("ip -net " NS0 " addr add " IP4_ADDR_VETH01 "/24 dev veth01"); + SYS("ip -net " NS0 " link set dev veth01 up"); + SYS("ip -net " NS1 " addr add " IP4_ADDR_VETH10 "/24 dev veth10"); + SYS("ip -net " NS1 " link set dev veth10 up"); + + /* NS0 <-> NS2 [veth02 <-> veth20] */ + SYS("ip link add veth02 netns " NS0 " type veth peer name veth20 netns " NS2); + SYS("ip -net " NS0 " addr add " IP4_ADDR_VETH02 "/24 dev veth02"); + SYS("ip -net " NS0 " link set dev veth02 up"); + SYS("ip -net " NS2 " addr add " IP4_ADDR_VETH20 "/24 dev veth20"); + SYS("ip -net " NS2 " link set dev veth20 up"); + + return 0; +fail: + return -1; +} + +static int setup_xfrm_tunnel_ns(const char *ns, const char *ipv4_local, + const char *ipv4_remote, int if_id) +{ + /* State: local -> remote */ + SYS("ip -net %s xfrm state add src %s dst %s spi 1 " + ESP_DUMMY_PARAMS "if_id %d", ns, ipv4_local, ipv4_remote, if_id); + + /* State: local <- remote */ + SYS("ip -net %s xfrm state add src %s dst %s spi 1 " + ESP_DUMMY_PARAMS "if_id %d", ns, ipv4_remote, ipv4_local, if_id); + + /* Policy: local -> remote */ + SYS("ip -net %s xfrm policy add dir out src 0.0.0.0/0 dst 0.0.0.0/0 " + "if_id %d tmpl src %s dst %s proto esp mode tunnel if_id %d", ns, + if_id, ipv4_local, ipv4_remote, if_id); + + /* Policy: local <- remote */ + SYS("ip -net %s xfrm policy add dir in src 0.0.0.0/0 dst 0.0.0.0/0 " + "if_id %d tmpl src %s dst %s proto esp mode tunnel if_id %d", ns, + if_id, ipv4_remote, ipv4_local, if_id); + + return 0; +fail: + return -1; +} + +static int setup_xfrm_tunnel(const char *ns_a, const char *ns_b, + const char *ipv4_a, const char *ipv4_b, + int if_id_a, int if_id_b) +{ + return setup_xfrm_tunnel_ns(ns_a, ipv4_a, ipv4_b, if_id_a) || + setup_xfrm_tunnel_ns(ns_b, ipv4_b, ipv4_a, if_id_b); +} + +static struct rtattr *rtattr_add(struct nlmsghdr *nh, unsigned short type, + unsigned short len) +{ + struct rtattr *rta = + (struct rtattr *)((uint8_t *)nh + RTA_ALIGN(nh->nlmsg_len)); + rta->rta_type = type; + rta->rta_len = RTA_LENGTH(len); + nh->nlmsg_len = RTA_ALIGN(nh->nlmsg_len) + RTA_ALIGN(rta->rta_len); + return rta; +} + +static struct rtattr *rtattr_add_str(struct nlmsghdr *nh, unsigned short type, + const char *s) +{ + struct rtattr *rta = rtattr_add(nh, type, strlen(s)); + + memcpy(RTA_DATA(rta), s, strlen(s)); + return rta; +} + +static struct rtattr *rtattr_begin(struct nlmsghdr *nh, unsigned short type) +{ + return rtattr_add(nh, type, 0); +} + +static void rtattr_end(struct nlmsghdr *nh, struct rtattr *attr) +{ + uint8_t *end = (uint8_t *)nh + nh->nlmsg_len; + + attr->rta_len = end - (uint8_t *)attr; +} + +static int setup_xfrmi_external_dev(const char *ns) +{ + struct { + struct nlmsghdr nh; + struct ifinfomsg info; + unsigned char data[128]; + } req; + struct rtattr *link_info, *info_data; + struct nstoken *nstoken; + int ret = -1, sock = -1; + struct nlmsghdr *nh; + + memset(&req, 0, sizeof(req)); + nh = &req.nh; + nh->nlmsg_len = NLMSG_LENGTH(sizeof(req.info)); + nh->nlmsg_type = RTM_NEWLINK; + nh->nlmsg_flags |= NLM_F_CREATE | NLM_F_REQUEST; + + rtattr_add_str(nh, IFLA_IFNAME, "ipsec0"); + link_info = rtattr_begin(nh, IFLA_LINKINFO); + rtattr_add_str(nh, IFLA_INFO_KIND, "xfrm"); + info_data = rtattr_begin(nh, IFLA_INFO_DATA); + rtattr_add(nh, IFLA_XFRM_COLLECT_METADATA, 0); + rtattr_end(nh, info_data); + rtattr_end(nh, link_info); + + nstoken = open_netns(ns); + if (!ASSERT_OK_PTR(nstoken, "setns")) + goto done; + + sock = socket(AF_NETLINK, SOCK_RAW | SOCK_CLOEXEC, NETLINK_ROUTE); + if (!ASSERT_GE(sock, 0, "netlink socket")) + goto done; + ret = send(sock, nh, nh->nlmsg_len, 0); + if (!ASSERT_EQ(ret, nh->nlmsg_len, "netlink send length")) + goto done; + + ret = 0; +done: + if (sock != -1) + close(sock); + if (nstoken) + close_netns(nstoken); + return ret; +} + +static int config_overlay(void) +{ + if (setup_xfrm_tunnel(NS0, NS1, IP4_ADDR_VETH01, IP4_ADDR_VETH10, + IF_ID_0_TO_1, IF_ID_1)) + goto fail; + if (setup_xfrm_tunnel(NS0, NS2, IP4_ADDR_VETH02, IP4_ADDR_VETH20, + IF_ID_0_TO_2, IF_ID_2)) + goto fail; + + /* Older iproute2 doesn't support this option */ + if (!ASSERT_OK(setup_xfrmi_external_dev(NS0), "xfrmi")) + goto fail; + + SYS("ip -net " NS0 " addr add 192.168.1.100/24 dev ipsec0"); + SYS("ip -net " NS0 " link set dev ipsec0 up"); + + SYS("ip -net " NS1 " link add ipsec0 type xfrm if_id %d", IF_ID_1); + SYS("ip -net " NS1 " addr add 192.168.1.200/24 dev ipsec0"); + SYS("ip -net " NS1 " link set dev ipsec0 up"); + + SYS("ip -net " NS2 " link add ipsec0 type xfrm if_id %d", IF_ID_2); + SYS("ip -net " NS2 " addr add 192.168.1.200/24 dev ipsec0"); + SYS("ip -net " NS2 " link set dev ipsec0 up"); + + return 0; +fail: + return -1; +} + +static int test_xfrm_ping(struct xfrm_info *skel, u32 if_id) +{ + skel->bss->req_if_id = if_id; + + SYS("ping -i 0.01 -c 3 -w 10 -q 192.168.1.200 > /dev/null"); + + if (!ASSERT_EQ(skel->bss->resp_if_id, if_id, "if_id")) + goto fail; + + return 0; +fail: + return -1; +} + +static void _test_xfrm_info(void) +{ + LIBBPF_OPTS(bpf_tc_hook, tc_hook, .attach_point = BPF_TC_INGRESS); + int get_xfrm_info_prog_fd, set_xfrm_info_prog_fd; + struct nstoken *nstoken = NULL; + struct xfrm_info *skel; + int ifindex; + + /* load and attach bpf progs to ipsec dev tc hook point */ + skel = xfrm_info__open_and_load(); + if (!ASSERT_OK_PTR(skel, "xfrm_info__open_and_load")) + goto done; + nstoken = open_netns(NS0); + if (!ASSERT_OK_PTR(nstoken, "setns " NS0)) + goto done; + ifindex = if_nametoindex("ipsec0"); + if (!ASSERT_NEQ(ifindex, 0, "ipsec0 ifindex")) + goto done; + tc_hook.ifindex = ifindex; + set_xfrm_info_prog_fd = bpf_program__fd(skel->progs.set_xfrm_info); + get_xfrm_info_prog_fd = bpf_program__fd(skel->progs.get_xfrm_info); + if (!ASSERT_GE(set_xfrm_info_prog_fd, 0, "bpf_program__fd")) + goto done; + if (!ASSERT_GE(get_xfrm_info_prog_fd, 0, "bpf_program__fd")) + goto done; + if (attach_tc_prog(&tc_hook, get_xfrm_info_prog_fd, + set_xfrm_info_prog_fd)) + goto done; + + /* perform test */ + if (!ASSERT_EQ(test_xfrm_ping(skel, IF_ID_0_TO_1), 0, "ping " NS1)) + goto done; + if (!ASSERT_EQ(test_xfrm_ping(skel, IF_ID_0_TO_2), 0, "ping " NS2)) + goto done; + +done: + if (nstoken) + close_netns(nstoken); + xfrm_info__destroy(skel); +} + +void test_xfrm_info(void) +{ + cleanup(); + + if (!ASSERT_OK(config_underlay(), "config_underlay")) + goto done; + if (!ASSERT_OK(config_overlay(), "config_overlay")) + goto done; + + if (test__start_subtest("xfrm_info")) + _test_xfrm_info(); + +done: + cleanup(); +} diff --git a/tools/testing/selftests/bpf/progs/bpf_tracing_net.h b/tools/testing/selftests/bpf/progs/bpf_tracing_net.h index adb087aecc9e..b394817126cf 100644 --- a/tools/testing/selftests/bpf/progs/bpf_tracing_net.h +++ b/tools/testing/selftests/bpf/progs/bpf_tracing_net.h @@ -25,6 +25,9 @@ #define IPV6_TCLASS 67 #define IPV6_AUTOFLOWLABEL 70 +#define TC_ACT_UNSPEC (-1) +#define TC_ACT_SHOT 2 + #define SOL_TCP 6 #define TCP_NODELAY 1 #define TCP_MAXSEG 2 diff --git a/tools/testing/selftests/bpf/progs/xfrm_info.c b/tools/testing/selftests/bpf/progs/xfrm_info.c new file mode 100644 index 000000000000..3acedcdd962d --- /dev/null +++ b/tools/testing/selftests/bpf/progs/xfrm_info.c @@ -0,0 +1,35 @@ +// SPDX-License-Identifier: GPL-2.0 +#include "vmlinux.h" +#include "bpf_tracing_net.h" +#include + +__u32 req_if_id; +__u32 resp_if_id; + +int bpf_skb_set_xfrm_info(struct __sk_buff *skb_ctx, + const struct bpf_xfrm_info *from) __ksym; +int bpf_skb_get_xfrm_info(struct __sk_buff *skb_ctx, + struct bpf_xfrm_info *to) __ksym; + +SEC("tc") +int set_xfrm_info(struct __sk_buff *skb) +{ + struct bpf_xfrm_info info = { .if_id = req_if_id }; + + return bpf_skb_set_xfrm_info(skb, &info) ? TC_ACT_SHOT : TC_ACT_UNSPEC; +} + +SEC("tc") +int get_xfrm_info(struct __sk_buff *skb) +{ + struct bpf_xfrm_info info = {}; + + if (bpf_skb_get_xfrm_info(skb, &info) < 0) + return TC_ACT_SHOT; + + resp_if_id = info.if_id; + + return TC_ACT_UNSPEC; +} + +char _license[] SEC("license") = "GPL"; From 610c32b2ce66d4aaa07b3a77a709bd4d2b268bb1 Mon Sep 17 00:00:00 2001 From: Horatiu Vultur Date: Sat, 3 Dec 2022 11:43:45 +0100 Subject: [PATCH 1599/2002] net: microchip: vcap: Add vcap_get_rule Add function vcap_get_rule which returns a rule based on the internal rule id. The entire functionality of reading and decoding the rule from the VCAP was inside vcap_api_debugfs file. So move the entire implementation in vcap_api as this is used also by vcap_get_rule. Signed-off-by: Horatiu Vultur Signed-off-by: Paolo Abeni --- .../net/ethernet/microchip/vcap/vcap_api.c | 772 ++++++++++++++++++ .../ethernet/microchip/vcap/vcap_api_client.h | 2 + .../microchip/vcap/vcap_api_debugfs.c | 498 ++--------- .../microchip/vcap/vcap_api_private.h | 14 + 4 files changed, 848 insertions(+), 438 deletions(-) diff --git a/drivers/net/ethernet/microchip/vcap/vcap_api.c b/drivers/net/ethernet/microchip/vcap/vcap_api.c index f2435d7ab515..27128313f15f 100644 --- a/drivers/net/ethernet/microchip/vcap/vcap_api.c +++ b/drivers/net/ethernet/microchip/vcap/vcap_api.c @@ -169,6 +169,227 @@ static void vcap_encode_typegroups(u32 *stream, int sw_width, } } +static bool vcap_bitarray_zero(int width, u8 *value) +{ + int bytes = DIV_ROUND_UP(width, BITS_PER_BYTE); + u8 total = 0, bmask = 0xff; + int rwidth = width; + int idx; + + for (idx = 0; idx < bytes; ++idx, rwidth -= BITS_PER_BYTE) { + if (rwidth && rwidth < BITS_PER_BYTE) + bmask = (1 << rwidth) - 1; + total += value[idx] & bmask; + } + return total == 0; +} + +static bool vcap_get_bit(u32 *stream, struct vcap_stream_iter *itr) +{ + u32 mask = BIT(itr->reg_bitpos); + u32 *p = &stream[itr->reg_idx]; + + return !!(*p & mask); +} + +static void vcap_decode_field(u32 *stream, struct vcap_stream_iter *itr, + int width, u8 *value) +{ + int idx; + + /* Loop over the field value bits and get the field bits and + * set them in the output value byte array + */ + for (idx = 0; idx < width; idx++) { + u8 bidx = idx & 0x7; + + /* Decode one field value bit */ + if (vcap_get_bit(stream, itr)) + *value |= 1 << bidx; + vcap_iter_next(itr); + if (bidx == 7) + value++; + } +} + +/* Verify that the type id in the stream matches the type id of the keyset */ +static bool vcap_verify_keystream_keyset(struct vcap_control *vctrl, + enum vcap_type vt, + u32 *keystream, + u32 *mskstream, + enum vcap_keyfield_set keyset) +{ + const struct vcap_info *vcap = &vctrl->vcaps[vt]; + const struct vcap_field *typefld; + const struct vcap_typegroup *tgt; + const struct vcap_field *fields; + struct vcap_stream_iter iter; + const struct vcap_set *info; + u32 value = 0; + u32 mask = 0; + + if (vcap_keyfield_count(vctrl, vt, keyset) == 0) + return false; + + info = vcap_keyfieldset(vctrl, vt, keyset); + /* Check that the keyset is valid */ + if (!info) + return false; + + /* a type_id of value -1 means that there is no type field */ + if (info->type_id == (u8)-1) + return true; + + /* Get a valid typegroup for the specific keyset */ + tgt = vcap_keyfield_typegroup(vctrl, vt, keyset); + if (!tgt) + return false; + + fields = vcap_keyfields(vctrl, vt, keyset); + if (!fields) + return false; + + typefld = &fields[VCAP_KF_TYPE]; + vcap_iter_init(&iter, vcap->sw_width, tgt, typefld->offset); + vcap_decode_field(mskstream, &iter, typefld->width, (u8 *)&mask); + /* no type info if there are no mask bits */ + if (vcap_bitarray_zero(typefld->width, (u8 *)&mask)) + return false; + + /* Get the value of the type field in the stream and compare to the + * one define in the vcap keyset + */ + vcap_iter_init(&iter, vcap->sw_width, tgt, typefld->offset); + vcap_decode_field(keystream, &iter, typefld->width, (u8 *)&value); + + return (value & mask) == (info->type_id & mask); +} + +/* Verify that the typegroup bits have the correct values */ +static int vcap_verify_typegroups(u32 *stream, int sw_width, + const struct vcap_typegroup *tgt, bool mask, + int sw_max) +{ + struct vcap_stream_iter iter; + int sw_cnt, idx; + + vcap_iter_set(&iter, sw_width, tgt, 0); + sw_cnt = 0; + while (iter.tg->width) { + u32 value = 0; + u32 tg_value = iter.tg->value; + + if (mask) + tg_value = (1 << iter.tg->width) - 1; + /* Set position to current typegroup bit */ + iter.offset = iter.tg->offset; + vcap_iter_update(&iter); + for (idx = 0; idx < iter.tg->width; idx++) { + /* Decode one typegroup bit */ + if (vcap_get_bit(stream, &iter)) + value |= 1 << idx; + iter.offset++; + vcap_iter_update(&iter); + } + if (value != tg_value) + return -EINVAL; + iter.tg++; /* next typegroup */ + sw_cnt++; + /* Stop checking more typegroups */ + if (sw_max && sw_cnt >= sw_max) + break; + } + return 0; +} + +/* Find the subword width of the key typegroup that matches the stream data */ +static int vcap_find_keystream_typegroup_sw(struct vcap_control *vctrl, + enum vcap_type vt, u32 *stream, + bool mask, int sw_max) +{ + const struct vcap_typegroup **tgt; + int sw_idx, res; + + tgt = vctrl->vcaps[vt].keyfield_set_typegroups; + /* Try the longest subword match first */ + for (sw_idx = vctrl->vcaps[vt].sw_count; sw_idx >= 0; sw_idx--) { + if (!tgt[sw_idx]) + continue; + + res = vcap_verify_typegroups(stream, vctrl->vcaps[vt].sw_width, + tgt[sw_idx], mask, sw_max); + if (res == 0) + return sw_idx; + } + return -EINVAL; +} + +/* Verify that the typegroup information, subword count, keyset and type id + * are in sync and correct, return the list of matchin keysets + */ +int +vcap_find_keystream_keysets(struct vcap_control *vctrl, + enum vcap_type vt, + u32 *keystream, + u32 *mskstream, + bool mask, int sw_max, + struct vcap_keyset_list *kslist) +{ + const struct vcap_set *keyfield_set; + int sw_count, idx; + + sw_count = vcap_find_keystream_typegroup_sw(vctrl, vt, keystream, mask, + sw_max); + if (sw_count < 0) + return sw_count; + + keyfield_set = vctrl->vcaps[vt].keyfield_set; + for (idx = 0; idx < vctrl->vcaps[vt].keyfield_set_size; ++idx) { + if (keyfield_set[idx].sw_per_item != sw_count) + continue; + + if (vcap_verify_keystream_keyset(vctrl, vt, keystream, + mskstream, idx)) + vcap_keyset_list_add(kslist, idx); + } + if (kslist->cnt > 0) + return 0; + return -EINVAL; +} +EXPORT_SYMBOL_GPL(vcap_find_keystream_keysets); + +/* Read key data from a VCAP address and discover if there are any rule keysets + * here + */ +int vcap_addr_keysets(struct vcap_control *vctrl, + struct net_device *ndev, + struct vcap_admin *admin, + int addr, + struct vcap_keyset_list *kslist) +{ + enum vcap_type vt = admin->vtype; + int keyset_sw_regs, idx; + u32 key = 0, mask = 0; + + /* Read the cache at the specified address */ + keyset_sw_regs = DIV_ROUND_UP(vctrl->vcaps[vt].sw_width, 32); + vctrl->ops->update(ndev, admin, VCAP_CMD_READ, VCAP_SEL_ALL, addr); + vctrl->ops->cache_read(ndev, admin, VCAP_SEL_ENTRY, 0, + keyset_sw_regs); + /* Skip uninitialized key/mask entries */ + for (idx = 0; idx < keyset_sw_regs; ++idx) { + key |= ~admin->cache.keystream[idx]; + mask |= admin->cache.maskstream[idx]; + } + if (key == 0 && mask == 0) + return -EINVAL; + /* Decode and locate the keysets */ + return vcap_find_keystream_keysets(vctrl, vt, admin->cache.keystream, + admin->cache.maskstream, false, 0, + kslist); +} +EXPORT_SYMBOL_GPL(vcap_addr_keysets); + /* Return the list of keyfields for the keyset */ const struct vcap_field *vcap_keyfields(struct vcap_control *vctrl, enum vcap_type vt, @@ -618,6 +839,517 @@ struct vcap_rule_internal *vcap_dup_rule(struct vcap_rule_internal *ri) return duprule; } +static void vcap_apply_width(u8 *dst, int width, int bytes) +{ + u8 bmask; + int idx; + + for (idx = 0; idx < bytes; idx++) { + if (width > 0) + if (width < 8) + bmask = (1 << width) - 1; + else + bmask = ~0; + else + bmask = 0; + dst[idx] &= bmask; + width -= 8; + } +} + +static void vcap_copy_from_w32be(u8 *dst, u8 *src, int size, int width) +{ + int idx, ridx, wstart, nidx; + int tail_bytes = (((size + 4) >> 2) << 2) - size; + + for (idx = 0, ridx = size - 1; idx < size; ++idx, --ridx) { + wstart = (idx >> 2) << 2; + nidx = wstart + 3 - (idx & 0x3); + if (nidx >= size) + nidx -= tail_bytes; + dst[nidx] = src[ridx]; + } + + vcap_apply_width(dst, width, size); +} + +static void vcap_copy_action_bit_field(struct vcap_u1_action *field, u8 *value) +{ + field->value = (*value) & 0x1; +} + +static void vcap_copy_limited_actionfield(u8 *dstvalue, u8 *srcvalue, + int width, int bytes) +{ + memcpy(dstvalue, srcvalue, bytes); + vcap_apply_width(dstvalue, width, bytes); +} + +static void vcap_copy_to_client_actionfield(struct vcap_rule_internal *ri, + struct vcap_client_actionfield *field, + u8 *value, u16 width) +{ + int field_size = actionfield_size_table[field->ctrl.type]; + + if (ri->admin->w32be) { + switch (field->ctrl.type) { + case VCAP_FIELD_BIT: + vcap_copy_action_bit_field(&field->data.u1, value); + break; + case VCAP_FIELD_U32: + vcap_copy_limited_actionfield((u8 *)&field->data.u32.value, + value, + width, field_size); + break; + case VCAP_FIELD_U48: + vcap_copy_from_w32be(field->data.u48.value, value, + field_size, width); + break; + case VCAP_FIELD_U56: + vcap_copy_from_w32be(field->data.u56.value, value, + field_size, width); + break; + case VCAP_FIELD_U64: + vcap_copy_from_w32be(field->data.u64.value, value, + field_size, width); + break; + case VCAP_FIELD_U72: + vcap_copy_from_w32be(field->data.u72.value, value, + field_size, width); + break; + case VCAP_FIELD_U112: + vcap_copy_from_w32be(field->data.u112.value, value, + field_size, width); + break; + case VCAP_FIELD_U128: + vcap_copy_from_w32be(field->data.u128.value, value, + field_size, width); + break; + }; + } else { + switch (field->ctrl.type) { + case VCAP_FIELD_BIT: + vcap_copy_action_bit_field(&field->data.u1, value); + break; + case VCAP_FIELD_U32: + vcap_copy_limited_actionfield((u8 *)&field->data.u32.value, + value, + width, field_size); + break; + case VCAP_FIELD_U48: + vcap_copy_limited_actionfield(field->data.u48.value, + value, + width, field_size); + break; + case VCAP_FIELD_U56: + vcap_copy_limited_actionfield(field->data.u56.value, + value, + width, field_size); + break; + case VCAP_FIELD_U64: + vcap_copy_limited_actionfield(field->data.u64.value, + value, + width, field_size); + break; + case VCAP_FIELD_U72: + vcap_copy_limited_actionfield(field->data.u72.value, + value, + width, field_size); + break; + case VCAP_FIELD_U112: + vcap_copy_limited_actionfield(field->data.u112.value, + value, + width, field_size); + break; + case VCAP_FIELD_U128: + vcap_copy_limited_actionfield(field->data.u128.value, + value, + width, field_size); + break; + }; + } +} + +static void vcap_copy_key_bit_field(struct vcap_u1_key *field, + u8 *value, u8 *mask) +{ + field->value = (*value) & 0x1; + field->mask = (*mask) & 0x1; +} + +static void vcap_copy_limited_keyfield(u8 *dstvalue, u8 *dstmask, + u8 *srcvalue, u8 *srcmask, + int width, int bytes) +{ + memcpy(dstvalue, srcvalue, bytes); + vcap_apply_width(dstvalue, width, bytes); + memcpy(dstmask, srcmask, bytes); + vcap_apply_width(dstmask, width, bytes); +} + +static void vcap_copy_to_client_keyfield(struct vcap_rule_internal *ri, + struct vcap_client_keyfield *field, + u8 *value, u8 *mask, u16 width) +{ + int field_size = keyfield_size_table[field->ctrl.type] / 2; + + if (ri->admin->w32be) { + switch (field->ctrl.type) { + case VCAP_FIELD_BIT: + vcap_copy_key_bit_field(&field->data.u1, value, mask); + break; + case VCAP_FIELD_U32: + vcap_copy_limited_keyfield((u8 *)&field->data.u32.value, + (u8 *)&field->data.u32.mask, + value, mask, + width, field_size); + break; + case VCAP_FIELD_U48: + vcap_copy_from_w32be(field->data.u48.value, value, + field_size, width); + vcap_copy_from_w32be(field->data.u48.mask, mask, + field_size, width); + break; + case VCAP_FIELD_U56: + vcap_copy_from_w32be(field->data.u56.value, value, + field_size, width); + vcap_copy_from_w32be(field->data.u56.mask, mask, + field_size, width); + break; + case VCAP_FIELD_U64: + vcap_copy_from_w32be(field->data.u64.value, value, + field_size, width); + vcap_copy_from_w32be(field->data.u64.mask, mask, + field_size, width); + break; + case VCAP_FIELD_U72: + vcap_copy_from_w32be(field->data.u72.value, value, + field_size, width); + vcap_copy_from_w32be(field->data.u72.mask, mask, + field_size, width); + break; + case VCAP_FIELD_U112: + vcap_copy_from_w32be(field->data.u112.value, value, + field_size, width); + vcap_copy_from_w32be(field->data.u112.mask, mask, + field_size, width); + break; + case VCAP_FIELD_U128: + vcap_copy_from_w32be(field->data.u128.value, value, + field_size, width); + vcap_copy_from_w32be(field->data.u128.mask, mask, + field_size, width); + break; + }; + } else { + switch (field->ctrl.type) { + case VCAP_FIELD_BIT: + vcap_copy_key_bit_field(&field->data.u1, value, mask); + break; + case VCAP_FIELD_U32: + vcap_copy_limited_keyfield((u8 *)&field->data.u32.value, + (u8 *)&field->data.u32.mask, + value, mask, + width, field_size); + break; + case VCAP_FIELD_U48: + vcap_copy_limited_keyfield(field->data.u48.value, + field->data.u48.mask, + value, mask, + width, field_size); + break; + case VCAP_FIELD_U56: + vcap_copy_limited_keyfield(field->data.u56.value, + field->data.u56.mask, + value, mask, + width, field_size); + break; + case VCAP_FIELD_U64: + vcap_copy_limited_keyfield(field->data.u64.value, + field->data.u64.mask, + value, mask, + width, field_size); + break; + case VCAP_FIELD_U72: + vcap_copy_limited_keyfield(field->data.u72.value, + field->data.u72.mask, + value, mask, + width, field_size); + break; + case VCAP_FIELD_U112: + vcap_copy_limited_keyfield(field->data.u112.value, + field->data.u112.mask, + value, mask, + width, field_size); + break; + case VCAP_FIELD_U128: + vcap_copy_limited_keyfield(field->data.u128.value, + field->data.u128.mask, + value, mask, + width, field_size); + break; + }; + } +} + +static void vcap_rule_alloc_keyfield(struct vcap_rule_internal *ri, + const struct vcap_field *keyfield, + enum vcap_key_field key, + u8 *value, u8 *mask) +{ + struct vcap_client_keyfield *field; + + field = kzalloc(sizeof(*field), GFP_KERNEL); + if (!field) + return; + INIT_LIST_HEAD(&field->ctrl.list); + field->ctrl.key = key; + field->ctrl.type = keyfield->type; + vcap_copy_to_client_keyfield(ri, field, value, mask, keyfield->width); + list_add_tail(&field->ctrl.list, &ri->data.keyfields); +} + +/* Read key data from a VCAP address and discover if there is a rule keyset + * here + */ +static bool +vcap_verify_actionstream_actionset(struct vcap_control *vctrl, + enum vcap_type vt, + u32 *actionstream, + enum vcap_actionfield_set actionset) +{ + const struct vcap_typegroup *tgt; + const struct vcap_field *fields; + const struct vcap_set *info; + + if (vcap_actionfield_count(vctrl, vt, actionset) == 0) + return false; + + info = vcap_actionfieldset(vctrl, vt, actionset); + /* Check that the actionset is valid */ + if (!info) + return false; + + /* a type_id of value -1 means that there is no type field */ + if (info->type_id == (u8)-1) + return true; + + /* Get a valid typegroup for the specific actionset */ + tgt = vcap_actionfield_typegroup(vctrl, vt, actionset); + if (!tgt) + return false; + + fields = vcap_actionfields(vctrl, vt, actionset); + if (!fields) + return false; + + /* Later this will be expanded with a check of the type id */ + return true; +} + +/* Find the subword width of the action typegroup that matches the stream data + */ +static int vcap_find_actionstream_typegroup_sw(struct vcap_control *vctrl, + enum vcap_type vt, u32 *stream, + int sw_max) +{ + const struct vcap_typegroup **tgt; + int sw_idx, res; + + tgt = vctrl->vcaps[vt].actionfield_set_typegroups; + /* Try the longest subword match first */ + for (sw_idx = vctrl->vcaps[vt].sw_count; sw_idx >= 0; sw_idx--) { + if (!tgt[sw_idx]) + continue; + res = vcap_verify_typegroups(stream, vctrl->vcaps[vt].act_width, + tgt[sw_idx], false, sw_max); + if (res == 0) + return sw_idx; + } + return -EINVAL; +} + +/* Verify that the typegroup information, subword count, actionset and type id + * are in sync and correct, return the actionset + */ +static enum vcap_actionfield_set +vcap_find_actionstream_actionset(struct vcap_control *vctrl, + enum vcap_type vt, + u32 *stream, + int sw_max) +{ + const struct vcap_set *actionfield_set; + int sw_count, idx; + bool res; + + sw_count = vcap_find_actionstream_typegroup_sw(vctrl, vt, stream, + sw_max); + if (sw_count < 0) + return sw_count; + + actionfield_set = vctrl->vcaps[vt].actionfield_set; + for (idx = 0; idx < vctrl->vcaps[vt].actionfield_set_size; ++idx) { + if (actionfield_set[idx].sw_per_item != sw_count) + continue; + + res = vcap_verify_actionstream_actionset(vctrl, vt, + stream, idx); + if (res) + return idx; + } + return -EINVAL; +} + +/* Store action value in an element in a list for the client */ +static void vcap_rule_alloc_actionfield(struct vcap_rule_internal *ri, + const struct vcap_field *actionfield, + enum vcap_action_field action, + u8 *value) +{ + struct vcap_client_actionfield *field; + + field = kzalloc(sizeof(*field), GFP_KERNEL); + if (!field) + return; + INIT_LIST_HEAD(&field->ctrl.list); + field->ctrl.action = action; + field->ctrl.type = actionfield->type; + vcap_copy_to_client_actionfield(ri, field, value, actionfield->width); + list_add_tail(&field->ctrl.list, &ri->data.actionfields); +} + +static int vcap_decode_actionset(struct vcap_rule_internal *ri) +{ + struct vcap_control *vctrl = ri->vctrl; + struct vcap_admin *admin = ri->admin; + const struct vcap_field *actionfield; + enum vcap_actionfield_set actionset; + enum vcap_type vt = admin->vtype; + const struct vcap_typegroup *tgt; + struct vcap_stream_iter iter; + int idx, res, actfield_count; + u32 *actstream; + u8 value[16]; + + actstream = admin->cache.actionstream; + res = vcap_find_actionstream_actionset(vctrl, vt, actstream, 0); + if (res < 0) { + pr_err("%s:%d: could not find valid actionset: %d\n", + __func__, __LINE__, res); + return -EINVAL; + } + actionset = res; + actfield_count = vcap_actionfield_count(vctrl, vt, actionset); + actionfield = vcap_actionfields(vctrl, vt, actionset); + tgt = vcap_actionfield_typegroup(vctrl, vt, actionset); + /* Start decoding the stream */ + for (idx = 0; idx < actfield_count; ++idx) { + if (actionfield[idx].width <= 0) + continue; + /* Get the action */ + memset(value, 0, DIV_ROUND_UP(actionfield[idx].width, 8)); + vcap_iter_init(&iter, vctrl->vcaps[vt].act_width, tgt, + actionfield[idx].offset); + vcap_decode_field(actstream, &iter, actionfield[idx].width, + value); + /* Skip if no bits are set */ + if (vcap_bitarray_zero(actionfield[idx].width, value)) + continue; + vcap_rule_alloc_actionfield(ri, &actionfield[idx], idx, value); + /* Later the action id will also be checked */ + } + return vcap_set_rule_set_actionset((struct vcap_rule *)ri, actionset); +} + +static int vcap_decode_keyset(struct vcap_rule_internal *ri) +{ + struct vcap_control *vctrl = ri->vctrl; + struct vcap_stream_iter kiter, miter; + struct vcap_admin *admin = ri->admin; + enum vcap_keyfield_set keysets[10]; + const struct vcap_field *keyfield; + enum vcap_type vt = admin->vtype; + const struct vcap_typegroup *tgt; + struct vcap_keyset_list matches; + enum vcap_keyfield_set keyset; + int idx, res, keyfield_count; + u32 *maskstream; + u32 *keystream; + u8 value[16]; + u8 mask[16]; + + keystream = admin->cache.keystream; + maskstream = admin->cache.maskstream; + matches.keysets = keysets; + matches.cnt = 0; + matches.max = ARRAY_SIZE(keysets); + res = vcap_find_keystream_keysets(vctrl, vt, keystream, maskstream, + false, 0, &matches); + if (res < 0) { + pr_err("%s:%d: could not find valid keysets: %d\n", + __func__, __LINE__, res); + return -EINVAL; + } + keyset = matches.keysets[0]; + keyfield_count = vcap_keyfield_count(vctrl, vt, keyset); + keyfield = vcap_keyfields(vctrl, vt, keyset); + tgt = vcap_keyfield_typegroup(vctrl, vt, keyset); + /* Start decoding the streams */ + for (idx = 0; idx < keyfield_count; ++idx) { + if (keyfield[idx].width <= 0) + continue; + /* First get the mask */ + memset(mask, 0, DIV_ROUND_UP(keyfield[idx].width, 8)); + vcap_iter_init(&miter, vctrl->vcaps[vt].sw_width, tgt, + keyfield[idx].offset); + vcap_decode_field(maskstream, &miter, keyfield[idx].width, + mask); + /* Skip if no mask bits are set */ + if (vcap_bitarray_zero(keyfield[idx].width, mask)) + continue; + /* Get the key */ + memset(value, 0, DIV_ROUND_UP(keyfield[idx].width, 8)); + vcap_iter_init(&kiter, vctrl->vcaps[vt].sw_width, tgt, + keyfield[idx].offset); + vcap_decode_field(keystream, &kiter, keyfield[idx].width, + value); + vcap_rule_alloc_keyfield(ri, &keyfield[idx], idx, value, mask); + } + return vcap_set_rule_set_keyset((struct vcap_rule *)ri, keyset); +} + +/* Read VCAP content into the VCAP cache */ +static int vcap_read_rule(struct vcap_rule_internal *ri) +{ + struct vcap_admin *admin = ri->admin; + int sw_idx, ent_idx = 0, act_idx = 0; + u32 addr = ri->addr; + + if (!ri->size || !ri->keyset_sw_regs || !ri->actionset_sw_regs) { + pr_err("%s:%d: rule is empty\n", __func__, __LINE__); + return -EINVAL; + } + vcap_erase_cache(ri); + /* Use the values in the streams to read the VCAP cache */ + for (sw_idx = 0; sw_idx < ri->size; sw_idx++, addr++) { + ri->vctrl->ops->update(ri->ndev, admin, VCAP_CMD_READ, + VCAP_SEL_ALL, addr); + ri->vctrl->ops->cache_read(ri->ndev, admin, + VCAP_SEL_ENTRY, ent_idx, + ri->keyset_sw_regs); + ri->vctrl->ops->cache_read(ri->ndev, admin, + VCAP_SEL_ACTION, act_idx, + ri->actionset_sw_regs); + if (sw_idx == 0) + ri->vctrl->ops->cache_read(ri->ndev, admin, + VCAP_SEL_COUNTER, + ri->counter_id, 0); + ent_idx += ri->keyset_sw_regs; + act_idx += ri->actionset_sw_regs; + } + return 0; +} + /* Write VCAP cache content to the VCAP HW instance */ static int vcap_write_rule(struct vcap_rule_internal *ri) { @@ -1183,6 +1915,46 @@ void vcap_free_rule(struct vcap_rule *rule) } EXPORT_SYMBOL_GPL(vcap_free_rule); +struct vcap_rule *vcap_get_rule(struct vcap_control *vctrl, u32 id) +{ + struct vcap_rule_internal *elem; + struct vcap_rule_internal *ri; + int err; + + ri = NULL; + + err = vcap_api_check(vctrl); + if (err) + return ERR_PTR(err); + elem = vcap_lookup_rule(vctrl, id); + if (!elem) + return NULL; + mutex_lock(&elem->admin->lock); + ri = vcap_dup_rule(elem); + if (IS_ERR(ri)) + goto unlock; + err = vcap_read_rule(ri); + if (err) { + ri = ERR_PTR(err); + goto unlock; + } + err = vcap_decode_keyset(ri); + if (err) { + ri = ERR_PTR(err); + goto unlock; + } + err = vcap_decode_actionset(ri); + if (err) { + ri = ERR_PTR(err); + goto unlock; + } + +unlock: + mutex_unlock(&elem->admin->lock); + return (struct vcap_rule *)ri; +} +EXPORT_SYMBOL_GPL(vcap_get_rule); + /* Return the alignment offset for a new rule address */ static int vcap_valid_rule_move(struct vcap_rule_internal *el, int offset) { diff --git a/drivers/net/ethernet/microchip/vcap/vcap_api_client.h b/drivers/net/ethernet/microchip/vcap/vcap_api_client.h index 93a0fcb12a81..a354dcd741e2 100644 --- a/drivers/net/ethernet/microchip/vcap/vcap_api_client.h +++ b/drivers/net/ethernet/microchip/vcap/vcap_api_client.h @@ -170,6 +170,8 @@ int vcap_add_rule(struct vcap_rule *rule); int vcap_del_rule(struct vcap_control *vctrl, struct net_device *ndev, u32 id); /* Make a full copy of an existing rule with a new rule id */ struct vcap_rule *vcap_copy_rule(struct vcap_rule *rule); +/* Get rule from a VCAP instance */ +struct vcap_rule *vcap_get_rule(struct vcap_control *vctrl, u32 id); /* Update the keyset for the rule */ int vcap_set_rule_set_keyset(struct vcap_rule *rule, diff --git a/drivers/net/ethernet/microchip/vcap/vcap_api_debugfs.c b/drivers/net/ethernet/microchip/vcap/vcap_api_debugfs.c index 3b8d165dc832..895bfff550d2 100644 --- a/drivers/net/ethernet/microchip/vcap/vcap_api_debugfs.c +++ b/drivers/net/ethernet/microchip/vcap/vcap_api_debugfs.c @@ -18,355 +18,15 @@ struct vcap_port_debugfs_info { struct net_device *ndev; }; -static bool vcap_bitarray_zero(int width, u8 *value) -{ - int bytes = DIV_ROUND_UP(width, BITS_PER_BYTE); - u8 total = 0, bmask = 0xff; - int rwidth = width; - int idx; - - for (idx = 0; idx < bytes; ++idx, rwidth -= BITS_PER_BYTE) { - if (rwidth && rwidth < BITS_PER_BYTE) - bmask = (1 << rwidth) - 1; - total += value[idx] & bmask; - } - return total == 0; -} - -static bool vcap_get_bit(u32 *stream, struct vcap_stream_iter *itr) -{ - u32 mask = BIT(itr->reg_bitpos); - u32 *p = &stream[itr->reg_idx]; - - return !!(*p & mask); -} - -static void vcap_decode_field(u32 *stream, struct vcap_stream_iter *itr, - int width, u8 *value) -{ - int idx; - - /* Loop over the field value bits and get the field bits and - * set them in the output value byte array - */ - for (idx = 0; idx < width; idx++) { - u8 bidx = idx & 0x7; - - /* Decode one field value bit */ - if (vcap_get_bit(stream, itr)) - *value |= 1 << bidx; - vcap_iter_next(itr); - if (bidx == 7) - value++; - } -} - -/* Verify that the typegroup bits have the correct values */ -static int vcap_verify_typegroups(u32 *stream, int sw_width, - const struct vcap_typegroup *tgt, bool mask, - int sw_max) -{ - struct vcap_stream_iter iter; - int sw_cnt, idx; - - vcap_iter_set(&iter, sw_width, tgt, 0); - sw_cnt = 0; - while (iter.tg->width) { - u32 value = 0; - u32 tg_value = iter.tg->value; - - if (mask) - tg_value = (1 << iter.tg->width) - 1; - /* Set position to current typegroup bit */ - iter.offset = iter.tg->offset; - vcap_iter_update(&iter); - for (idx = 0; idx < iter.tg->width; idx++) { - /* Decode one typegroup bit */ - if (vcap_get_bit(stream, &iter)) - value |= 1 << idx; - iter.offset++; - vcap_iter_update(&iter); - } - if (value != tg_value) - return -EINVAL; - iter.tg++; /* next typegroup */ - sw_cnt++; - /* Stop checking more typegroups */ - if (sw_max && sw_cnt >= sw_max) - break; - } - return 0; -} - -/* Find the subword width of the key typegroup that matches the stream data */ -static int vcap_find_keystream_typegroup_sw(struct vcap_control *vctrl, - enum vcap_type vt, u32 *stream, - bool mask, int sw_max) -{ - const struct vcap_typegroup **tgt; - int sw_idx, res; - - tgt = vctrl->vcaps[vt].keyfield_set_typegroups; - /* Try the longest subword match first */ - for (sw_idx = vctrl->vcaps[vt].sw_count; sw_idx >= 0; sw_idx--) { - if (!tgt[sw_idx]) - continue; - - res = vcap_verify_typegroups(stream, vctrl->vcaps[vt].sw_width, - tgt[sw_idx], mask, sw_max); - if (res == 0) - return sw_idx; - } - return -EINVAL; -} - -/* Find the subword width of the action typegroup that matches the stream data - */ -static int vcap_find_actionstream_typegroup_sw(struct vcap_control *vctrl, - enum vcap_type vt, u32 *stream, - int sw_max) -{ - const struct vcap_typegroup **tgt; - int sw_idx, res; - - tgt = vctrl->vcaps[vt].actionfield_set_typegroups; - /* Try the longest subword match first */ - for (sw_idx = vctrl->vcaps[vt].sw_count; sw_idx >= 0; sw_idx--) { - if (!tgt[sw_idx]) - continue; - res = vcap_verify_typegroups(stream, vctrl->vcaps[vt].act_width, - tgt[sw_idx], false, sw_max); - if (res == 0) - return sw_idx; - } - return -EINVAL; -} - -/* Verify that the type id in the stream matches the type id of the keyset */ -static bool vcap_verify_keystream_keyset(struct vcap_control *vctrl, - enum vcap_type vt, - u32 *keystream, - u32 *mskstream, - enum vcap_keyfield_set keyset) -{ - const struct vcap_info *vcap = &vctrl->vcaps[vt]; - const struct vcap_field *typefld; - const struct vcap_typegroup *tgt; - const struct vcap_field *fields; - struct vcap_stream_iter iter; - const struct vcap_set *info; - u32 value = 0; - u32 mask = 0; - - if (vcap_keyfield_count(vctrl, vt, keyset) == 0) - return false; - - info = vcap_keyfieldset(vctrl, vt, keyset); - /* Check that the keyset is valid */ - if (!info) - return false; - - /* a type_id of value -1 means that there is no type field */ - if (info->type_id == (u8)-1) - return true; - - /* Get a valid typegroup for the specific keyset */ - tgt = vcap_keyfield_typegroup(vctrl, vt, keyset); - if (!tgt) - return false; - - fields = vcap_keyfields(vctrl, vt, keyset); - if (!fields) - return false; - - typefld = &fields[VCAP_KF_TYPE]; - vcap_iter_init(&iter, vcap->sw_width, tgt, typefld->offset); - vcap_decode_field(mskstream, &iter, typefld->width, (u8 *)&mask); - /* no type info if there are no mask bits */ - if (vcap_bitarray_zero(typefld->width, (u8 *)&mask)) - return false; - - /* Get the value of the type field in the stream and compare to the - * one define in the vcap keyset - */ - vcap_iter_init(&iter, vcap->sw_width, tgt, typefld->offset); - vcap_decode_field(keystream, &iter, typefld->width, (u8 *)&value); - - return (value & mask) == (info->type_id & mask); -} - -/* Verify that the typegroup information, subword count, keyset and type id - * are in sync and correct, return the list of matching keysets - */ -static int -vcap_find_keystream_keysets(struct vcap_control *vctrl, - enum vcap_type vt, - u32 *keystream, - u32 *mskstream, - bool mask, int sw_max, - struct vcap_keyset_list *kslist) -{ - const struct vcap_set *keyfield_set; - int sw_count, idx; - - sw_count = vcap_find_keystream_typegroup_sw(vctrl, vt, keystream, mask, - sw_max); - if (sw_count < 0) - return sw_count; - - keyfield_set = vctrl->vcaps[vt].keyfield_set; - for (idx = 0; idx < vctrl->vcaps[vt].keyfield_set_size; ++idx) { - if (keyfield_set[idx].sw_per_item != sw_count) - continue; - - if (vcap_verify_keystream_keyset(vctrl, vt, keystream, - mskstream, idx)) - vcap_keyset_list_add(kslist, idx); - } - if (kslist->cnt > 0) - return 0; - return -EINVAL; -} - -/* Read key data from a VCAP address and discover if there is a rule keyset - * here - */ -static bool -vcap_verify_actionstream_actionset(struct vcap_control *vctrl, - enum vcap_type vt, - u32 *actionstream, - enum vcap_actionfield_set actionset) -{ - const struct vcap_typegroup *tgt; - const struct vcap_field *fields; - const struct vcap_set *info; - - if (vcap_actionfield_count(vctrl, vt, actionset) == 0) - return false; - - info = vcap_actionfieldset(vctrl, vt, actionset); - /* Check that the actionset is valid */ - if (!info) - return false; - - /* a type_id of value -1 means that there is no type field */ - if (info->type_id == (u8)-1) - return true; - - /* Get a valid typegroup for the specific actionset */ - tgt = vcap_actionfield_typegroup(vctrl, vt, actionset); - if (!tgt) - return false; - - fields = vcap_actionfields(vctrl, vt, actionset); - if (!fields) - return false; - - /* Later this will be expanded with a check of the type id */ - return true; -} - -/* Verify that the typegroup information, subword count, actionset and type id - * are in sync and correct, return the actionset - */ -static enum vcap_actionfield_set -vcap_find_actionstream_actionset(struct vcap_control *vctrl, - enum vcap_type vt, - u32 *stream, - int sw_max) -{ - const struct vcap_set *actionfield_set; - int sw_count, idx; - bool res; - - sw_count = vcap_find_actionstream_typegroup_sw(vctrl, vt, stream, - sw_max); - if (sw_count < 0) - return sw_count; - - actionfield_set = vctrl->vcaps[vt].actionfield_set; - for (idx = 0; idx < vctrl->vcaps[vt].actionfield_set_size; ++idx) { - if (actionfield_set[idx].sw_per_item != sw_count) - continue; - - res = vcap_verify_actionstream_actionset(vctrl, vt, - stream, idx); - if (res) - return idx; - } - return -EINVAL; -} - -/* Read key data from a VCAP address and discover if there are any rule keysets - * here - */ -static int vcap_addr_keysets(struct vcap_control *vctrl, - struct net_device *ndev, - struct vcap_admin *admin, - int addr, - struct vcap_keyset_list *kslist) -{ - enum vcap_type vt = admin->vtype; - int keyset_sw_regs, idx; - u32 key = 0, mask = 0; - - /* Read the cache at the specified address */ - keyset_sw_regs = DIV_ROUND_UP(vctrl->vcaps[vt].sw_width, 32); - vctrl->ops->update(ndev, admin, VCAP_CMD_READ, VCAP_SEL_ALL, addr); - vctrl->ops->cache_read(ndev, admin, VCAP_SEL_ENTRY, 0, - keyset_sw_regs); - /* Skip uninitialized key/mask entries */ - for (idx = 0; idx < keyset_sw_regs; ++idx) { - key |= ~admin->cache.keystream[idx]; - mask |= admin->cache.maskstream[idx]; - } - if (key == 0 && mask == 0) - return -EINVAL; - /* Decode and locate the keysets */ - return vcap_find_keystream_keysets(vctrl, vt, admin->cache.keystream, - admin->cache.maskstream, false, 0, - kslist); -} - -static int vcap_read_rule(struct vcap_rule_internal *ri) -{ - struct vcap_admin *admin = ri->admin; - int sw_idx, ent_idx = 0, act_idx = 0; - u32 addr = ri->addr; - - if (!ri->size || !ri->keyset_sw_regs || !ri->actionset_sw_regs) { - pr_err("%s:%d: rule is empty\n", __func__, __LINE__); - return -EINVAL; - } - vcap_erase_cache(ri); - /* Use the values in the streams to read the VCAP cache */ - for (sw_idx = 0; sw_idx < ri->size; sw_idx++, addr++) { - ri->vctrl->ops->update(ri->ndev, admin, VCAP_CMD_READ, - VCAP_SEL_ALL, addr); - ri->vctrl->ops->cache_read(ri->ndev, admin, - VCAP_SEL_ENTRY, ent_idx, - ri->keyset_sw_regs); - ri->vctrl->ops->cache_read(ri->ndev, admin, - VCAP_SEL_ACTION, act_idx, - ri->actionset_sw_regs); - if (sw_idx == 0) - ri->vctrl->ops->cache_read(ri->ndev, admin, - VCAP_SEL_COUNTER, - ri->counter_id, 0); - ent_idx += ri->keyset_sw_regs; - act_idx += ri->actionset_sw_regs; - } - return 0; -} - /* Dump the keyfields value and mask values */ static void vcap_debugfs_show_rule_keyfield(struct vcap_control *vctrl, struct vcap_output_print *out, enum vcap_key_field key, const struct vcap_field *keyfield, - u8 *value, u8 *mask) + struct vcap_client_keyfield_data *data) { bool hex = false; + u8 *value, *mask; int idx, bytes; out->prf(out->dst, " %s: W%d: ", vcap_keyfield_name(vctrl, key), @@ -374,40 +34,62 @@ static void vcap_debugfs_show_rule_keyfield(struct vcap_control *vctrl, switch (keyfield[key].type) { case VCAP_FIELD_BIT: - out->prf(out->dst, "%d/%d", value[0], mask[0]); + out->prf(out->dst, "%d/%d", data->u1.value, data->u1.mask); break; case VCAP_FIELD_U32: + value = (u8 *)(&data->u32.value); + mask = (u8 *)(&data->u32.mask); + if (key == VCAP_KF_L3_IP4_SIP || key == VCAP_KF_L3_IP4_DIP) { - out->prf(out->dst, "%pI4h/%pI4h", value, mask); + out->prf(out->dst, "%pI4h/%pI4h", &data->u32.value, + &data->u32.mask); } else if (key == VCAP_KF_ETYPE || key == VCAP_KF_IF_IGR_PORT_MASK) { hex = true; } else { u32 fmsk = (1 << keyfield[key].width) - 1; - u32 val = *(u32 *)value; - u32 msk = *(u32 *)mask; - out->prf(out->dst, "%u/%u", val & fmsk, msk & fmsk); + out->prf(out->dst, "%u/%u", data->u32.value & fmsk, + data->u32.mask & fmsk); } break; case VCAP_FIELD_U48: + value = data->u48.value; + mask = data->u48.mask; if (key == VCAP_KF_L2_SMAC || key == VCAP_KF_L2_DMAC) - out->prf(out->dst, "%pMR/%pMR", value, mask); + out->prf(out->dst, "%pMR/%pMR", data->u48.value, + data->u48.mask); else hex = true; break; case VCAP_FIELD_U56: + value = data->u56.value; + mask = data->u56.mask; + hex = true; + break; case VCAP_FIELD_U64: + value = data->u64.value; + mask = data->u64.mask; + hex = true; + break; case VCAP_FIELD_U72: + value = data->u72.value; + mask = data->u72.mask; + hex = true; + break; case VCAP_FIELD_U112: + value = data->u112.value; + mask = data->u112.mask; hex = true; break; case VCAP_FIELD_U128: if (key == VCAP_KF_L3_IP6_SIP || key == VCAP_KF_L3_IP6_DIP) { u8 nvalue[16], nmask[16]; - vcap_netbytes_copy(nvalue, value, sizeof(nvalue)); - vcap_netbytes_copy(nmask, mask, sizeof(nmask)); + vcap_netbytes_copy(nvalue, data->u128.value, + sizeof(nvalue)); + vcap_netbytes_copy(nmask, data->u128.mask, + sizeof(nmask)); out->prf(out->dst, "%pI6/%pI6", nvalue, nmask); } else { hex = true; @@ -472,19 +154,15 @@ static int vcap_debugfs_show_rule_keyset(struct vcap_rule_internal *ri, struct vcap_output_print *out) { struct vcap_control *vctrl = ri->vctrl; - struct vcap_stream_iter kiter, miter; struct vcap_admin *admin = ri->admin; enum vcap_keyfield_set keysets[10]; const struct vcap_field *keyfield; enum vcap_type vt = admin->vtype; - const struct vcap_typegroup *tgt; + struct vcap_client_keyfield *ckf; struct vcap_keyset_list matches; - enum vcap_keyfield_set keyset; - int idx, res, keyfield_count; u32 *maskstream; u32 *keystream; - u8 value[16]; - u8 mask[16]; + int res; keystream = admin->cache.keystream; maskstream = admin->cache.maskstream; @@ -498,39 +176,20 @@ static int vcap_debugfs_show_rule_keyset(struct vcap_rule_internal *ri, __func__, __LINE__, res); return -EINVAL; } - keyset = matches.keysets[0]; out->prf(out->dst, " keysets:"); - for (idx = 0; idx < matches.cnt; ++idx) + for (int idx = 0; idx < matches.cnt; ++idx) out->prf(out->dst, " %s", vcap_keyset_name(vctrl, matches.keysets[idx])); out->prf(out->dst, "\n"); out->prf(out->dst, " keyset_sw: %d\n", ri->keyset_sw); out->prf(out->dst, " keyset_sw_regs: %d\n", ri->keyset_sw_regs); - keyfield_count = vcap_keyfield_count(vctrl, vt, keyset); - keyfield = vcap_keyfields(vctrl, vt, keyset); - tgt = vcap_keyfield_typegroup(vctrl, vt, keyset); - /* Start decoding the streams */ - for (idx = 0; idx < keyfield_count; ++idx) { - if (keyfield[idx].width <= 0) - continue; - /* First get the mask */ - memset(mask, 0, DIV_ROUND_UP(keyfield[idx].width, 8)); - vcap_iter_init(&miter, vctrl->vcaps[vt].sw_width, tgt, - keyfield[idx].offset); - vcap_decode_field(maskstream, &miter, keyfield[idx].width, - mask); - /* Skip if no mask bits are set */ - if (vcap_bitarray_zero(keyfield[idx].width, mask)) - continue; - /* Get the key */ - memset(value, 0, DIV_ROUND_UP(keyfield[idx].width, 8)); - vcap_iter_init(&kiter, vctrl->vcaps[vt].sw_width, tgt, - keyfield[idx].offset); - vcap_decode_field(keystream, &kiter, keyfield[idx].width, - value); - vcap_debugfs_show_rule_keyfield(vctrl, out, idx, keyfield, - value, mask); + + list_for_each_entry(ckf, &ri->data.keyfields, ctrl.list) { + keyfield = vcap_keyfields(vctrl, admin->vtype, ri->data.keyset); + vcap_debugfs_show_rule_keyfield(vctrl, out, ckf->ctrl.key, + keyfield, &ckf->data); } + return 0; } @@ -540,48 +199,21 @@ static int vcap_debugfs_show_rule_actionset(struct vcap_rule_internal *ri, struct vcap_control *vctrl = ri->vctrl; struct vcap_admin *admin = ri->admin; const struct vcap_field *actionfield; - enum vcap_actionfield_set actionset; - enum vcap_type vt = admin->vtype; - const struct vcap_typegroup *tgt; - struct vcap_stream_iter iter; - int idx, res, actfield_count; - u32 *actstream; - u8 value[16]; - bool no_bits; + struct vcap_client_actionfield *caf; - actstream = admin->cache.actionstream; - res = vcap_find_actionstream_actionset(vctrl, vt, actstream, 0); - if (res < 0) { - pr_err("%s:%d: could not find valid actionset: %d\n", - __func__, __LINE__, res); - return -EINVAL; - } - actionset = res; out->prf(out->dst, " actionset: %s\n", vcap_actionset_name(vctrl, ri->data.actionset)); out->prf(out->dst, " actionset_sw: %d\n", ri->actionset_sw); out->prf(out->dst, " actionset_sw_regs: %d\n", ri->actionset_sw_regs); - actfield_count = vcap_actionfield_count(vctrl, vt, actionset); - actionfield = vcap_actionfields(vctrl, vt, actionset); - tgt = vcap_actionfield_typegroup(vctrl, vt, actionset); - /* Start decoding the stream */ - for (idx = 0; idx < actfield_count; ++idx) { - if (actionfield[idx].width <= 0) - continue; - /* Get the action */ - memset(value, 0, DIV_ROUND_UP(actionfield[idx].width, 8)); - vcap_iter_init(&iter, vctrl->vcaps[vt].act_width, tgt, - actionfield[idx].offset); - vcap_decode_field(actstream, &iter, actionfield[idx].width, - value); - /* Skip if no bits are set */ - no_bits = vcap_bitarray_zero(actionfield[idx].width, value); - if (no_bits) - continue; - /* Later the action id will also be checked */ - vcap_debugfs_show_rule_actionfield(vctrl, out, idx, actionfield, - value); + + list_for_each_entry(caf, &ri->data.actionfields, ctrl.list) { + actionfield = vcap_actionfields(vctrl, admin->vtype, + ri->data.actionset); + vcap_debugfs_show_rule_actionfield(vctrl, out, caf->ctrl.action, + actionfield, + &caf->data.u1.value); } + return 0; } @@ -632,32 +264,22 @@ static int vcap_show_admin(struct vcap_control *vctrl, struct vcap_admin *admin, struct vcap_output_print *out) { - struct vcap_rule_internal *elem, *ri; + struct vcap_rule_internal *elem; + struct vcap_rule *vrule; int ret = 0; vcap_show_admin_info(vctrl, admin, out); - mutex_lock(&admin->lock); list_for_each_entry(elem, &admin->rules, list) { - ri = vcap_dup_rule(elem); - if (IS_ERR(ri)) { - ret = PTR_ERR(ri); - goto err_unlock; + vrule = vcap_get_rule(vctrl, elem->data.id); + if (IS_ERR_OR_NULL(vrule)) { + ret = PTR_ERR(vrule); + break; } - /* Read data from VCAP */ - ret = vcap_read_rule(ri); - if (ret) - goto err_free_rule; - out->prf(out->dst, "\n"); - vcap_show_admin_rule(vctrl, admin, out, ri); - vcap_free_rule((struct vcap_rule *)ri); - } - mutex_unlock(&admin->lock); - return 0; -err_free_rule: - vcap_free_rule((struct vcap_rule *)ri); -err_unlock: - mutex_unlock(&admin->lock); + out->prf(out->dst, "\n"); + vcap_show_admin_rule(vctrl, admin, out, to_intrule(vrule)); + vcap_free_rule(vrule); + } return ret; } diff --git a/drivers/net/ethernet/microchip/vcap/vcap_api_private.h b/drivers/net/ethernet/microchip/vcap/vcap_api_private.h index 9ac1b1d55f22..4fd21da97679 100644 --- a/drivers/net/ethernet/microchip/vcap/vcap_api_private.h +++ b/drivers/net/ethernet/microchip/vcap/vcap_api_private.h @@ -96,4 +96,18 @@ const char *vcap_actionset_name(struct vcap_control *vctrl, const char *vcap_actionfield_name(struct vcap_control *vctrl, enum vcap_action_field action); +/* Read key data from a VCAP address and discover if there are any rule keysets + * here + */ +int vcap_addr_keysets(struct vcap_control *vctrl, struct net_device *ndev, + struct vcap_admin *admin, int addr, + struct vcap_keyset_list *kslist); + +/* Verify that the typegroup information, subword count, keyset and type id + * are in sync and correct, return the list of matchin keysets + */ +int vcap_find_keystream_keysets(struct vcap_control *vctrl, enum vcap_type vt, + u32 *keystream, u32 *mskstream, bool mask, + int sw_max, struct vcap_keyset_list *kslist); + #endif /* __VCAP_API_PRIVATE__ */ From 2662b3f93d2617719e68bbb1031491461afa2812 Mon Sep 17 00:00:00 2001 From: Horatiu Vultur Date: Sat, 3 Dec 2022 11:43:46 +0100 Subject: [PATCH 1600/2002] net: microchip: vcap: Add vcap_mod_rule Add the function vcap_mod_rule which allows to update an existing rule in the vcap. It is required for the rule to exist in the vcap to be able to modify it. Signed-off-by: Horatiu Vultur Signed-off-by: Paolo Abeni --- .../net/ethernet/microchip/vcap/vcap_api.c | 36 +++++++++++++++++++ .../ethernet/microchip/vcap/vcap_api_client.h | 2 ++ 2 files changed, 38 insertions(+) diff --git a/drivers/net/ethernet/microchip/vcap/vcap_api.c b/drivers/net/ethernet/microchip/vcap/vcap_api.c index 27128313f15f..eae4e9fe0e14 100644 --- a/drivers/net/ethernet/microchip/vcap/vcap_api.c +++ b/drivers/net/ethernet/microchip/vcap/vcap_api.c @@ -1955,6 +1955,42 @@ unlock: } EXPORT_SYMBOL_GPL(vcap_get_rule); +/* Update existing rule */ +int vcap_mod_rule(struct vcap_rule *rule) +{ + struct vcap_rule_internal *ri = to_intrule(rule); + struct vcap_counter ctr; + int err; + + err = vcap_api_check(ri->vctrl); + if (err) + return err; + + if (!vcap_lookup_rule(ri->vctrl, ri->data.id)) + return -ENOENT; + + mutex_lock(&ri->admin->lock); + /* Encode the bitstreams to the VCAP cache */ + vcap_erase_cache(ri); + err = vcap_encode_rule(ri); + if (err) + goto out; + + err = vcap_write_rule(ri); + if (err) + goto out; + + memset(&ctr, 0, sizeof(ctr)); + err = vcap_write_counter(ri, &ctr); + if (err) + goto out; + +out: + mutex_unlock(&ri->admin->lock); + return err; +} +EXPORT_SYMBOL_GPL(vcap_mod_rule); + /* Return the alignment offset for a new rule address */ static int vcap_valid_rule_move(struct vcap_rule_internal *el, int offset) { diff --git a/drivers/net/ethernet/microchip/vcap/vcap_api_client.h b/drivers/net/ethernet/microchip/vcap/vcap_api_client.h index a354dcd741e2..fdfc5d58813b 100644 --- a/drivers/net/ethernet/microchip/vcap/vcap_api_client.h +++ b/drivers/net/ethernet/microchip/vcap/vcap_api_client.h @@ -172,6 +172,8 @@ int vcap_del_rule(struct vcap_control *vctrl, struct net_device *ndev, u32 id); struct vcap_rule *vcap_copy_rule(struct vcap_rule *rule); /* Get rule from a VCAP instance */ struct vcap_rule *vcap_get_rule(struct vcap_control *vctrl, u32 id); +/* Update existing rule */ +int vcap_mod_rule(struct vcap_rule *rule); /* Update the keyset for the rule */ int vcap_set_rule_set_keyset(struct vcap_rule *rule, From 6009b61f80e0b87b06f56a9bf16a32b6f562f822 Mon Sep 17 00:00:00 2001 From: Horatiu Vultur Date: Sat, 3 Dec 2022 11:43:47 +0100 Subject: [PATCH 1601/2002] net: microchip: vcap: Add vcap_rule_get_key_u32 Add the function vcap_rule_get_key_u32 which allows to get the value and the mask of a key that exist on the rule. If the key doesn't exist, it would return error. Signed-off-by: Horatiu Vultur Signed-off-by: Paolo Abeni --- drivers/net/ethernet/microchip/vcap/vcap_api.c | 16 ++++++++++++++++ .../ethernet/microchip/vcap/vcap_api_client.h | 4 ++++ 2 files changed, 20 insertions(+) diff --git a/drivers/net/ethernet/microchip/vcap/vcap_api.c b/drivers/net/ethernet/microchip/vcap/vcap_api.c index eae4e9fe0e14..05e915ea858d 100644 --- a/drivers/net/ethernet/microchip/vcap/vcap_api.c +++ b/drivers/net/ethernet/microchip/vcap/vcap_api.c @@ -2338,6 +2338,22 @@ int vcap_rule_add_key_u128(struct vcap_rule *rule, enum vcap_key_field key, } EXPORT_SYMBOL_GPL(vcap_rule_add_key_u128); +int vcap_rule_get_key_u32(struct vcap_rule *rule, enum vcap_key_field key, + u32 *value, u32 *mask) +{ + struct vcap_client_keyfield *ckf; + + ckf = vcap_find_keyfield(rule, key); + if (!ckf) + return -ENOENT; + + *value = ckf->data.u32.value; + *mask = ckf->data.u32.mask; + + return 0; +} +EXPORT_SYMBOL_GPL(vcap_rule_get_key_u32); + /* Find a client action field in a rule */ static struct vcap_client_actionfield * vcap_find_actionfield(struct vcap_rule *rule, enum vcap_action_field act) diff --git a/drivers/net/ethernet/microchip/vcap/vcap_api_client.h b/drivers/net/ethernet/microchip/vcap/vcap_api_client.h index fdfc5d58813b..0319866f9c94 100644 --- a/drivers/net/ethernet/microchip/vcap/vcap_api_client.h +++ b/drivers/net/ethernet/microchip/vcap/vcap_api_client.h @@ -258,4 +258,8 @@ int vcap_rule_mod_action_u32(struct vcap_rule *rule, enum vcap_action_field action, u32 value); +/* Get a 32 bit key field value and mask from the rule */ +int vcap_rule_get_key_u32(struct vcap_rule *rule, enum vcap_key_field key, + u32 *value, u32 *mask); + #endif /* __VCAP_API_CLIENT__ */ From 72df3489fb10324e30616d278ea9b37a8a801b6f Mon Sep 17 00:00:00 2001 From: Horatiu Vultur Date: Sat, 3 Dec 2022 11:43:48 +0100 Subject: [PATCH 1602/2002] net: lan966x: Add ptp trap rules Currently lan966x, doesn't allow to run PTP over interfaces that are part of the bridge. The reason is when the lan966x was receiving a PTP frame (regardless if L2/IPv4/IPv6) the HW it would flood this frame. Now that it is possible to add VCAP rules to the HW, such to trap these frames to the CPU, it is possible to run PTP also over interfaces that are part of the bridge. Signed-off-by: Horatiu Vultur Signed-off-by: Paolo Abeni --- .../ethernet/microchip/lan966x/lan966x_main.c | 19 +- .../ethernet/microchip/lan966x/lan966x_main.h | 14 ++ .../ethernet/microchip/lan966x/lan966x_ptp.c | 236 +++++++++++++++++- .../microchip/lan966x/lan966x_tc_flower.c | 8 - .../microchip/lan966x/lan966x_vcap_impl.c | 11 +- 5 files changed, 265 insertions(+), 23 deletions(-) diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_main.c b/drivers/net/ethernet/microchip/lan966x/lan966x_main.c index f6092983d028..cadde20505ba 100644 --- a/drivers/net/ethernet/microchip/lan966x/lan966x_main.c +++ b/drivers/net/ethernet/microchip/lan966x/lan966x_main.c @@ -443,11 +443,22 @@ static int lan966x_port_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) { struct lan966x_port *port = netdev_priv(dev); + int err; + + if (cmd == SIOCSHWTSTAMP) { + err = lan966x_ptp_setup_traps(port, ifr); + if (err) + return err; + } if (!phy_has_hwtstamp(dev->phydev) && port->lan966x->ptp) { switch (cmd) { case SIOCSHWTSTAMP: - return lan966x_ptp_hwtstamp_set(port, ifr); + err = lan966x_ptp_hwtstamp_set(port, ifr); + if (err) + lan966x_ptp_del_traps(port); + + return err; case SIOCGHWTSTAMP: return lan966x_ptp_hwtstamp_get(port, ifr); } @@ -456,7 +467,11 @@ static int lan966x_port_ioctl(struct net_device *dev, struct ifreq *ifr, if (!dev->phydev) return -ENODEV; - return phy_mii_ioctl(dev->phydev, ifr, cmd); + err = phy_mii_ioctl(dev->phydev, ifr, cmd); + if (err && cmd == SIOCSHWTSTAMP) + lan966x_ptp_del_traps(port); + + return err; } static const struct net_device_ops lan966x_port_netdev_ops = { diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_main.h b/drivers/net/ethernet/microchip/lan966x/lan966x_main.h index f2e45da7ffd4..3491f1961835 100644 --- a/drivers/net/ethernet/microchip/lan966x/lan966x_main.h +++ b/drivers/net/ethernet/microchip/lan966x/lan966x_main.h @@ -88,6 +88,10 @@ #define SE_IDX_QUEUE 0 /* 0-79 : Queue scheduler elements */ #define SE_IDX_PORT 80 /* 80-89 : Port schedular elements */ +#define LAN966X_VCAP_CID_IS2_L0 VCAP_CID_INGRESS_STAGE2_L0 /* IS2 lookup 0 */ +#define LAN966X_VCAP_CID_IS2_L1 VCAP_CID_INGRESS_STAGE2_L1 /* IS2 lookup 1 */ +#define LAN966X_VCAP_CID_IS2_MAX (VCAP_CID_INGRESS_STAGE2_L2 - 1) /* IS2 Max */ + /* MAC table entry types. * ENTRYTYPE_NORMAL is subject to aging. * ENTRYTYPE_LOCKED is not subject to aging. @@ -116,6 +120,14 @@ enum lan966x_fdma_action { FDMA_REDIRECT, }; +/* Controls how PORT_MASK is applied */ +enum LAN966X_PORT_MASK_MODE { + LAN966X_PMM_NO_ACTION, + LAN966X_PMM_REPLACE, + LAN966X_PMM_FORWARDING, + LAN966X_PMM_REDIRECT, +}; + struct lan966x_port; struct lan966x_db { @@ -473,6 +485,8 @@ irqreturn_t lan966x_ptp_irq_handler(int irq, void *args); irqreturn_t lan966x_ptp_ext_irq_handler(int irq, void *args); u32 lan966x_ptp_get_period_ps(void); int lan966x_ptp_gettime64(struct ptp_clock_info *ptp, struct timespec64 *ts); +int lan966x_ptp_setup_traps(struct lan966x_port *port, struct ifreq *ifr); +int lan966x_ptp_del_traps(struct lan966x_port *port); int lan966x_fdma_xmit(struct sk_buff *skb, __be32 *ifh, struct net_device *dev); int lan966x_fdma_xmit_xdpf(struct lan966x_port *port, diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_ptp.c b/drivers/net/ethernet/microchip/lan966x/lan966x_ptp.c index e5a2bbe064f8..300fe4005919 100644 --- a/drivers/net/ethernet/microchip/lan966x/lan966x_ptp.c +++ b/drivers/net/ethernet/microchip/lan966x/lan966x_ptp.c @@ -3,6 +3,8 @@ #include #include "lan966x_main.h" +#include "vcap_api.h" +#include "vcap_api_client.h" #define LAN966X_MAX_PTP_ID 512 @@ -18,6 +20,17 @@ #define TOD_ACC_PIN 0x7 +/* This represents the base rule ID for the PTP rules that are added in the + * VCAP to trap frames to CPU. This number needs to be bigger than the maximum + * number of entries that can exist in the VCAP. + */ +#define LAN966X_VCAP_PTP_RULE_ID 1000000 +#define LAN966X_VCAP_L2_PTP_TRAP (LAN966X_VCAP_PTP_RULE_ID + 0) +#define LAN966X_VCAP_IPV4_EV_PTP_TRAP (LAN966X_VCAP_PTP_RULE_ID + 1) +#define LAN966X_VCAP_IPV4_GEN_PTP_TRAP (LAN966X_VCAP_PTP_RULE_ID + 2) +#define LAN966X_VCAP_IPV6_EV_PTP_TRAP (LAN966X_VCAP_PTP_RULE_ID + 3) +#define LAN966X_VCAP_IPV6_GEN_PTP_TRAP (LAN966X_VCAP_PTP_RULE_ID + 4) + enum { PTP_PIN_ACTION_IDLE = 0, PTP_PIN_ACTION_LOAD, @@ -35,19 +48,228 @@ static u64 lan966x_ptp_get_nominal_value(void) return 0x304d4873ecade305; } +static int lan966x_ptp_add_trap(struct lan966x_port *port, + int (*add_ptp_key)(struct vcap_rule *vrule, + struct lan966x_port*), + u32 rule_id, + u16 proto) +{ + struct lan966x *lan966x = port->lan966x; + struct vcap_rule *vrule; + int err; + + vrule = vcap_get_rule(lan966x->vcap_ctrl, rule_id); + if (vrule) { + u32 value, mask; + + /* Just modify the ingress port mask and exit */ + vcap_rule_get_key_u32(vrule, VCAP_KF_IF_IGR_PORT_MASK, + &value, &mask); + mask &= ~BIT(port->chip_port); + vcap_rule_mod_key_u32(vrule, VCAP_KF_IF_IGR_PORT_MASK, + value, mask); + + err = vcap_mod_rule(vrule); + goto free_rule; + } + + vrule = vcap_alloc_rule(lan966x->vcap_ctrl, port->dev, + LAN966X_VCAP_CID_IS2_L0, + VCAP_USER_PTP, 0, rule_id); + if (!vrule) + return -ENOMEM; + if (IS_ERR(vrule)) + return PTR_ERR(vrule); + + err = add_ptp_key(vrule, port); + if (err) + goto free_rule; + + err = vcap_set_rule_set_actionset(vrule, VCAP_AFS_BASE_TYPE); + err |= vcap_rule_add_action_bit(vrule, VCAP_AF_CPU_COPY_ENA, VCAP_BIT_1); + err |= vcap_rule_add_action_u32(vrule, VCAP_AF_MASK_MODE, LAN966X_PMM_REPLACE); + err |= vcap_val_rule(vrule, proto); + if (err) + goto free_rule; + + err = vcap_add_rule(vrule); + +free_rule: + /* Free the local copy of the rule */ + vcap_free_rule(vrule); + return err; +} + +static int lan966x_ptp_del_trap(struct lan966x_port *port, + u32 rule_id) +{ + struct lan966x *lan966x = port->lan966x; + struct vcap_rule *vrule; + u32 value, mask; + int err; + + vrule = vcap_get_rule(lan966x->vcap_ctrl, rule_id); + if (!vrule) + return -EEXIST; + + vcap_rule_get_key_u32(vrule, VCAP_KF_IF_IGR_PORT_MASK, &value, &mask); + mask |= BIT(port->chip_port); + + /* No other port requires this trap, so it is safe to remove it */ + if (mask == GENMASK(lan966x->num_phys_ports, 0)) { + err = vcap_del_rule(lan966x->vcap_ctrl, port->dev, rule_id); + goto free_rule; + } + + vcap_rule_mod_key_u32(vrule, VCAP_KF_IF_IGR_PORT_MASK, value, mask); + err = vcap_mod_rule(vrule); + +free_rule: + vcap_free_rule(vrule); + return err; +} + +static int lan966x_ptp_add_l2_key(struct vcap_rule *vrule, + struct lan966x_port *port) +{ + return vcap_rule_add_key_u32(vrule, VCAP_KF_ETYPE, ETH_P_1588, ~0); +} + +static int lan966x_ptp_add_ip_event_key(struct vcap_rule *vrule, + struct lan966x_port *port) +{ + return vcap_rule_add_key_u32(vrule, VCAP_KF_L4_DPORT, PTP_EV_PORT, ~0) || + vcap_rule_add_key_bit(vrule, VCAP_KF_TCP_IS, VCAP_BIT_0); +} + +static int lan966x_ptp_add_ip_general_key(struct vcap_rule *vrule, + struct lan966x_port *port) +{ + return vcap_rule_add_key_u32(vrule, VCAP_KF_L4_DPORT, PTP_GEN_PORT, ~0) || + vcap_rule_add_key_bit(vrule, VCAP_KF_TCP_IS, VCAP_BIT_0); +} + +static int lan966x_ptp_add_l2_rule(struct lan966x_port *port) +{ + return lan966x_ptp_add_trap(port, lan966x_ptp_add_l2_key, + LAN966X_VCAP_L2_PTP_TRAP, ETH_P_ALL); +} + +static int lan966x_ptp_add_ipv4_rules(struct lan966x_port *port) +{ + int err; + + err = lan966x_ptp_add_trap(port, lan966x_ptp_add_ip_event_key, + LAN966X_VCAP_IPV4_EV_PTP_TRAP, ETH_P_IP); + if (err) + return err; + + err = lan966x_ptp_add_trap(port, lan966x_ptp_add_ip_general_key, + LAN966X_VCAP_IPV4_GEN_PTP_TRAP, ETH_P_IP); + if (err) + lan966x_ptp_del_trap(port, LAN966X_VCAP_IPV4_EV_PTP_TRAP); + + return err; +} + +static int lan966x_ptp_add_ipv6_rules(struct lan966x_port *port) +{ + int err; + + err = lan966x_ptp_add_trap(port, lan966x_ptp_add_ip_event_key, + LAN966X_VCAP_IPV6_EV_PTP_TRAP, ETH_P_IPV6); + if (err) + return err; + + err = lan966x_ptp_add_trap(port, lan966x_ptp_add_ip_general_key, + LAN966X_VCAP_IPV6_GEN_PTP_TRAP, ETH_P_IPV6); + if (err) + lan966x_ptp_del_trap(port, LAN966X_VCAP_IPV6_EV_PTP_TRAP); + + return err; +} + +static int lan966x_ptp_del_l2_rule(struct lan966x_port *port) +{ + return lan966x_ptp_del_trap(port, LAN966X_VCAP_L2_PTP_TRAP); +} + +static int lan966x_ptp_del_ipv4_rules(struct lan966x_port *port) +{ + int err; + + err = lan966x_ptp_del_trap(port, LAN966X_VCAP_IPV4_EV_PTP_TRAP); + err |= lan966x_ptp_del_trap(port, LAN966X_VCAP_IPV4_GEN_PTP_TRAP); + + return err; +} + +static int lan966x_ptp_del_ipv6_rules(struct lan966x_port *port) +{ + int err; + + err = lan966x_ptp_del_trap(port, LAN966X_VCAP_IPV6_EV_PTP_TRAP); + err |= lan966x_ptp_del_trap(port, LAN966X_VCAP_IPV6_GEN_PTP_TRAP); + + return err; +} + +static int lan966x_ptp_add_traps(struct lan966x_port *port) +{ + int err; + + err = lan966x_ptp_add_l2_rule(port); + if (err) + goto err_l2; + + err = lan966x_ptp_add_ipv4_rules(port); + if (err) + goto err_ipv4; + + err = lan966x_ptp_add_ipv6_rules(port); + if (err) + goto err_ipv6; + + return err; + +err_ipv6: + lan966x_ptp_del_ipv4_rules(port); +err_ipv4: + lan966x_ptp_del_l2_rule(port); +err_l2: + return err; +} + +int lan966x_ptp_del_traps(struct lan966x_port *port) +{ + int err; + + err = lan966x_ptp_del_l2_rule(port); + err |= lan966x_ptp_del_ipv4_rules(port); + err |= lan966x_ptp_del_ipv6_rules(port); + + return err; +} + +int lan966x_ptp_setup_traps(struct lan966x_port *port, struct ifreq *ifr) +{ + struct hwtstamp_config cfg; + + if (copy_from_user(&cfg, ifr->ifr_data, sizeof(cfg))) + return -EFAULT; + + if (cfg.rx_filter == HWTSTAMP_FILTER_NONE) + return lan966x_ptp_del_traps(port); + else + return lan966x_ptp_add_traps(port); +} + int lan966x_ptp_hwtstamp_set(struct lan966x_port *port, struct ifreq *ifr) { struct lan966x *lan966x = port->lan966x; struct hwtstamp_config cfg; struct lan966x_phc *phc; - /* For now don't allow to run ptp on ports that are part of a bridge, - * because in case of transparent clock the HW will still forward the - * frames, so there would be duplicate frames - */ - if (lan966x->bridge_mask & BIT(port->chip_port)) - return -EINVAL; - if (copy_from_user(&cfg, ifr->ifr_data, sizeof(cfg))) return -EFAULT; diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_tc_flower.c b/drivers/net/ethernet/microchip/lan966x/lan966x_tc_flower.c index 04a2afd683cc..ba3fa917d6b7 100644 --- a/drivers/net/ethernet/microchip/lan966x/lan966x_tc_flower.c +++ b/drivers/net/ethernet/microchip/lan966x/lan966x_tc_flower.c @@ -4,14 +4,6 @@ #include "vcap_api.h" #include "vcap_api_client.h" -/* Controls how PORT_MASK is applied */ -enum LAN966X_PORT_MASK_MODE { - LAN966X_PMM_NO_ACTION, - LAN966X_PMM_REPLACE, - LAN966X_PMM_FORWARDING, - LAN966X_PMM_REDIRECT, -}; - struct lan966x_tc_flower_parse_usage { struct flow_cls_offload *f; struct flow_rule *frule; diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_vcap_impl.c b/drivers/net/ethernet/microchip/lan966x/lan966x_vcap_impl.c index 44f40d914947..d8dc9fbb81e1 100644 --- a/drivers/net/ethernet/microchip/lan966x/lan966x_vcap_impl.c +++ b/drivers/net/ethernet/microchip/lan966x/lan966x_vcap_impl.c @@ -5,10 +5,6 @@ #include "vcap_api.h" #include "vcap_api_client.h" -#define LAN966X_VCAP_CID_IS2_L0 VCAP_CID_INGRESS_STAGE2_L0 /* IS2 lookup 0 */ -#define LAN966X_VCAP_CID_IS2_L1 VCAP_CID_INGRESS_STAGE2_L1 /* IS2 lookup 1 */ -#define LAN966X_VCAP_CID_IS2_MAX (VCAP_CID_INGRESS_STAGE2_L2 - 1) /* IS2 Max */ - #define STREAMSIZE (64 * 4) #define LAN966X_IS2_LOOKUPS 2 @@ -219,9 +215,12 @@ static void lan966x_vcap_add_default_fields(struct net_device *dev, struct vcap_rule *rule) { struct lan966x_port *port = netdev_priv(dev); + u32 value, mask; - vcap_rule_add_key_u32(rule, VCAP_KF_IF_IGR_PORT_MASK, 0, - ~BIT(port->chip_port)); + if (vcap_rule_get_key_u32(rule, VCAP_KF_IF_IGR_PORT_MASK, + &value, &mask)) + vcap_rule_add_key_u32(rule, VCAP_KF_IF_IGR_PORT_MASK, 0, + ~BIT(port->chip_port)); if (lan966x_vcap_is_first_chain(rule)) vcap_rule_add_key_bit(rule, VCAP_KF_LOOKUP_FIRST_IS, From e77bbde73ed0a2d5342a98fb050287cdbe5db524 Mon Sep 17 00:00:00 2001 From: Leon Romanovsky Date: Fri, 2 Dec 2022 22:10:22 +0200 Subject: [PATCH 1603/2002] net/mlx5: Return ready to use ASO WQE There is no need in hiding returned ASO WQE type by providing void*, use the real type instead. Do it together with zeroing that memory, so ASO WQE will be ready to use immediately. Reviewed-by: Saeed Mahameed Signed-off-by: Leon Romanovsky Signed-off-by: Steffen Klassert --- drivers/net/ethernet/mellanox/mlx5/core/en/tc/meter.c | 1 - drivers/net/ethernet/mellanox/mlx5/core/lib/aso.c | 7 +++++-- drivers/net/ethernet/mellanox/mlx5/core/lib/aso.h | 2 +- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/tc/meter.c b/drivers/net/ethernet/mellanox/mlx5/core/en/tc/meter.c index be74e1403328..25cd449e8aad 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/tc/meter.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/tc/meter.c @@ -162,7 +162,6 @@ mlx5e_tc_meter_modify(struct mlx5_core_dev *mdev, MLX5_ACCESS_ASO_OPC_MOD_FLOW_METER); aso_ctrl = &aso_wqe->aso_ctrl; - memset(aso_ctrl, 0, sizeof(*aso_ctrl)); aso_ctrl->data_mask_mode = MLX5_ASO_DATA_MASK_MODE_BYTEWISE_64BYTE << 6; aso_ctrl->condition_1_0_operand = MLX5_ASO_ALWAYS_TRUE | MLX5_ASO_ALWAYS_TRUE << 4; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lib/aso.c b/drivers/net/ethernet/mellanox/mlx5/core/lib/aso.c index 0f9e4f01c85a..5a80fb7dbbca 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/lib/aso.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/lib/aso.c @@ -353,12 +353,15 @@ void mlx5_aso_build_wqe(struct mlx5_aso *aso, u8 ds_cnt, cseg->general_id = cpu_to_be32(obj_id); } -void *mlx5_aso_get_wqe(struct mlx5_aso *aso) +struct mlx5_aso_wqe *mlx5_aso_get_wqe(struct mlx5_aso *aso) { + struct mlx5_aso_wqe *wqe; u16 pi; pi = mlx5_wq_cyc_ctr2ix(&aso->wq, aso->pc); - return mlx5_wq_cyc_get_wqe(&aso->wq, pi); + wqe = mlx5_wq_cyc_get_wqe(&aso->wq, pi); + memset(wqe, 0, sizeof(*wqe)); + return wqe; } void mlx5_aso_post_wqe(struct mlx5_aso *aso, bool with_data, diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lib/aso.h b/drivers/net/ethernet/mellanox/mlx5/core/lib/aso.h index 2d40dcf9d42e..4312614bf3bc 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/lib/aso.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/lib/aso.h @@ -77,7 +77,7 @@ enum { struct mlx5_aso; -void *mlx5_aso_get_wqe(struct mlx5_aso *aso); +struct mlx5_aso_wqe *mlx5_aso_get_wqe(struct mlx5_aso *aso); void mlx5_aso_build_wqe(struct mlx5_aso *aso, u8 ds_cnt, struct mlx5_aso_wqe *aso_wqe, u32 obj_id, u32 opc_mode); From 3afee4ed336ed7a6cff78d23339879ffb654e02e Mon Sep 17 00:00:00 2001 From: Leon Romanovsky Date: Fri, 2 Dec 2022 22:10:23 +0200 Subject: [PATCH 1604/2002] net/mlx5: Add HW definitions for IPsec packet offload Add all needed bits to support IPsec packet offload mode. Reviewed-by: Raed Salem Reviewed-by: Saeed Mahameed Signed-off-by: Leon Romanovsky Signed-off-by: Steffen Klassert --- .../net/ethernet/mellanox/mlx5/core/lib/aso.h | 1 + include/linux/mlx5/mlx5_ifc.h | 53 +++++++++++++++++-- 2 files changed, 51 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lib/aso.h b/drivers/net/ethernet/mellanox/mlx5/core/lib/aso.h index 4312614bf3bc..c8fc3c838642 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/lib/aso.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/lib/aso.h @@ -71,6 +71,7 @@ enum { }; enum { + MLX5_ACCESS_ASO_OPC_MOD_IPSEC = 0x0, MLX5_ACCESS_ASO_OPC_MOD_FLOW_METER = 0x2, MLX5_ACCESS_ASO_OPC_MOD_MACSEC = 0x5, }; diff --git a/include/linux/mlx5/mlx5_ifc.h b/include/linux/mlx5/mlx5_ifc.h index 5a4e914e2a6f..300b56ea5ff4 100644 --- a/include/linux/mlx5/mlx5_ifc.h +++ b/include/linux/mlx5/mlx5_ifc.h @@ -445,7 +445,10 @@ struct mlx5_ifc_flow_table_prop_layout_bits { u8 max_modify_header_actions[0x8]; u8 max_ft_level[0x8]; - u8 reserved_at_40[0x6]; + u8 reformat_add_esp_trasport[0x1]; + u8 reserved_at_41[0x2]; + u8 reformat_del_esp_trasport[0x1]; + u8 reserved_at_44[0x2]; u8 execute_aso[0x1]; u8 reserved_at_47[0x19]; @@ -638,8 +641,10 @@ struct mlx5_ifc_fte_match_set_misc2_bits { u8 reserved_at_1a0[0x8]; u8 macsec_syndrome[0x8]; + u8 ipsec_syndrome[0x8]; + u8 reserved_at_1b8[0x8]; - u8 reserved_at_1b0[0x50]; + u8 reserved_at_1c0[0x40]; }; struct mlx5_ifc_fte_match_set_misc3_bits { @@ -6384,6 +6389,9 @@ enum mlx5_reformat_ctx_type { MLX5_REFORMAT_TYPE_L2_TO_L2_TUNNEL = 0x2, MLX5_REFORMAT_TYPE_L3_TUNNEL_TO_L2 = 0x3, MLX5_REFORMAT_TYPE_L2_TO_L3_TUNNEL = 0x4, + MLX5_REFORMAT_TYPE_ADD_ESP_TRANSPORT_OVER_IPV4 = 0x5, + MLX5_REFORMAT_TYPE_DEL_ESP_TRANSPORT = 0x8, + MLX5_REFORMAT_TYPE_ADD_ESP_TRANSPORT_OVER_IPV6 = 0xb, MLX5_REFORMAT_TYPE_INSERT_HDR = 0xf, MLX5_REFORMAT_TYPE_REMOVE_HDR = 0x10, MLX5_REFORMAT_TYPE_ADD_MACSEC = 0x11, @@ -11563,6 +11571,41 @@ enum { MLX5_IPSEC_OBJECT_ICV_LEN_16B, }; +enum { + MLX5_IPSEC_ASO_REG_C_0_1 = 0x0, + MLX5_IPSEC_ASO_REG_C_2_3 = 0x1, + MLX5_IPSEC_ASO_REG_C_4_5 = 0x2, + MLX5_IPSEC_ASO_REG_C_6_7 = 0x3, +}; + +enum { + MLX5_IPSEC_ASO_MODE = 0x0, + MLX5_IPSEC_ASO_REPLAY_PROTECTION = 0x1, + MLX5_IPSEC_ASO_INC_SN = 0x2, +}; + +struct mlx5_ifc_ipsec_aso_bits { + u8 valid[0x1]; + u8 reserved_at_201[0x1]; + u8 mode[0x2]; + u8 window_sz[0x2]; + u8 soft_lft_arm[0x1]; + u8 hard_lft_arm[0x1]; + u8 remove_flow_enable[0x1]; + u8 esn_event_arm[0x1]; + u8 reserved_at_20a[0x16]; + + u8 remove_flow_pkt_cnt[0x20]; + + u8 remove_flow_soft_lft[0x20]; + + u8 reserved_at_260[0x80]; + + u8 mode_parameter[0x20]; + + u8 replay_protection_window[0x100]; +}; + struct mlx5_ifc_ipsec_obj_bits { u8 modify_field_select[0x40]; u8 full_offload[0x1]; @@ -11584,7 +11627,11 @@ struct mlx5_ifc_ipsec_obj_bits { u8 implicit_iv[0x40]; - u8 reserved_at_100[0x700]; + u8 reserved_at_100[0x8]; + u8 ipsec_aso_access_pd[0x18]; + u8 reserved_at_120[0xe0]; + + struct mlx5_ifc_ipsec_aso_bits ipsec_aso; }; struct mlx5_ifc_create_ipsec_obj_in_bits { From 59592cfdf8a44fd1f25cf16f4e25edc7f1a4e709 Mon Sep 17 00:00:00 2001 From: Leon Romanovsky Date: Fri, 2 Dec 2022 22:10:24 +0200 Subject: [PATCH 1605/2002] net/mlx5e: Advertise IPsec packet offload support Add needed capabilities check to determine if device supports IPsec packet offload mode. Reviewed-by: Raed Salem Reviewed-by: Saeed Mahameed Signed-off-by: Leon Romanovsky Signed-off-by: Steffen Klassert --- drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.h | 1 + .../ethernet/mellanox/mlx5/core/en_accel/ipsec_offload.c | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.h b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.h index 4c47347d0ee2..fa052a89c4dd 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.h @@ -88,6 +88,7 @@ struct mlx5_accel_esp_xfrm_attrs { enum mlx5_ipsec_cap { MLX5_IPSEC_CAP_CRYPTO = 1 << 0, MLX5_IPSEC_CAP_ESN = 1 << 1, + MLX5_IPSEC_CAP_PACKET_OFFLOAD = 1 << 2, }; struct mlx5e_priv; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_offload.c b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_offload.c index 792724ce7336..3d5a1f875398 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_offload.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_offload.c @@ -31,6 +31,12 @@ u32 mlx5_ipsec_device_caps(struct mlx5_core_dev *mdev) MLX5_CAP_ETH(mdev, insert_trailer) && MLX5_CAP_ETH(mdev, swp)) caps |= MLX5_IPSEC_CAP_CRYPTO; + if (MLX5_CAP_IPSEC(mdev, ipsec_full_offload) && + MLX5_CAP_FLOWTABLE_NIC_TX(mdev, reformat_add_esp_trasport) && + MLX5_CAP_FLOWTABLE_NIC_RX(mdev, reformat_del_esp_trasport) && + MLX5_CAP_FLOWTABLE_NIC_RX(mdev, decap)) + caps |= MLX5_IPSEC_CAP_PACKET_OFFLOAD; + if (!caps) return 0; From cded6d80129babde53627efe32d164842cdd23a0 Mon Sep 17 00:00:00 2001 From: Leon Romanovsky Date: Fri, 2 Dec 2022 22:10:25 +0200 Subject: [PATCH 1606/2002] net/mlx5e: Store replay window in XFRM attributes As a preparation for future extension of IPsec hardware object to allow configuration of packet offload mode, extend the XFRM validator to check replay window values. Reviewed-by: Raed Salem Reviewed-by: Saeed Mahameed Signed-off-by: Leon Romanovsky Signed-off-by: Steffen Klassert --- .../net/ethernet/mellanox/mlx5/core/en_accel/ipsec.c | 12 ++++++++++++ .../net/ethernet/mellanox/mlx5/core/en_accel/ipsec.h | 1 + 2 files changed, 13 insertions(+) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.c b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.c index e6411533f911..734b486db5d6 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.c @@ -166,6 +166,7 @@ mlx5e_ipsec_build_accel_xfrm_attrs(struct mlx5e_ipsec_sa_entry *sa_entry, attrs->esn = sa_entry->esn_state.esn; if (sa_entry->esn_state.overlap) attrs->flags |= MLX5_ACCEL_ESP_FLAGS_ESN_STATE_OVERLAP; + attrs->replay_window = x->replay_esn->replay_window; } /* action */ @@ -257,6 +258,17 @@ static inline int mlx5e_xfrm_validate_state(struct xfrm_state *x) netdev_info(netdev, "Unsupported xfrm offload type\n"); return -EINVAL; } + if (x->xso.type == XFRM_DEV_OFFLOAD_PACKET) { + if (x->replay_esn && x->replay_esn->replay_window != 32 && + x->replay_esn->replay_window != 64 && + x->replay_esn->replay_window != 128 && + x->replay_esn->replay_window != 256) { + netdev_info(netdev, + "Unsupported replay window size %u\n", + x->replay_esn->replay_window); + return -EINVAL; + } + } return 0; } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.h b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.h index fa052a89c4dd..6fe55675bee9 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.h @@ -83,6 +83,7 @@ struct mlx5_accel_esp_xfrm_attrs { } daddr; u8 is_ipv6; + u32 replay_window; }; enum mlx5_ipsec_cap { From e3840530b4c8d0964f8757f2cffbbce5741ad327 Mon Sep 17 00:00:00 2001 From: Leon Romanovsky Date: Fri, 2 Dec 2022 22:10:26 +0200 Subject: [PATCH 1607/2002] net/mlx5e: Remove extra layers of defines Instead of performing redefinition of XFRM core defines to same values but with MLX5_* prefix, cache the input values as is by making sure that the proper storage objects are used. Reviewed-by: Raed Salem Reviewed-by: Saeed Mahameed Signed-off-by: Leon Romanovsky Signed-off-by: Steffen Klassert --- .../mellanox/mlx5/core/en_accel/ipsec.c | 17 ++++----------- .../mellanox/mlx5/core/en_accel/ipsec.h | 18 ++++------------ .../mellanox/mlx5/core/en_accel/ipsec_fs.c | 21 ++++++++++--------- .../mlx5/core/en_accel/ipsec_offload.c | 10 ++++----- 4 files changed, 23 insertions(+), 43 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.c b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.c index 734b486db5d6..14ed72b26bbe 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.c @@ -162,29 +162,20 @@ mlx5e_ipsec_build_accel_xfrm_attrs(struct mlx5e_ipsec_sa_entry *sa_entry, /* esn */ if (sa_entry->esn_state.trigger) { - attrs->flags |= MLX5_ACCEL_ESP_FLAGS_ESN_TRIGGERED; + attrs->esn_trigger = true; attrs->esn = sa_entry->esn_state.esn; - if (sa_entry->esn_state.overlap) - attrs->flags |= MLX5_ACCEL_ESP_FLAGS_ESN_STATE_OVERLAP; + attrs->esn_overlap = sa_entry->esn_state.overlap; attrs->replay_window = x->replay_esn->replay_window; } - /* action */ - attrs->action = (x->xso.dir == XFRM_DEV_OFFLOAD_OUT) ? - MLX5_ACCEL_ESP_ACTION_ENCRYPT : - MLX5_ACCEL_ESP_ACTION_DECRYPT; - /* flags */ - attrs->flags |= (x->props.mode == XFRM_MODE_TRANSPORT) ? - MLX5_ACCEL_ESP_FLAGS_TRANSPORT : - MLX5_ACCEL_ESP_FLAGS_TUNNEL; - + attrs->dir = x->xso.dir; /* spi */ attrs->spi = be32_to_cpu(x->id.spi); /* source , destination ips */ memcpy(&attrs->saddr, x->props.saddr.a6, sizeof(attrs->saddr)); memcpy(&attrs->daddr, x->id.daddr.a6, sizeof(attrs->daddr)); - attrs->is_ipv6 = (x->props.family != AF_INET); + attrs->family = x->props.family; } static inline int mlx5e_xfrm_validate_state(struct xfrm_state *x) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.h b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.h index 6fe55675bee9..05790b7d062f 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.h @@ -43,18 +43,6 @@ #define MLX5E_IPSEC_SADB_RX_BITS 10 #define MLX5E_IPSEC_ESN_SCOPE_MID 0x80000000L -enum mlx5_accel_esp_flags { - MLX5_ACCEL_ESP_FLAGS_TUNNEL = 0, /* Default */ - MLX5_ACCEL_ESP_FLAGS_TRANSPORT = 1UL << 0, - MLX5_ACCEL_ESP_FLAGS_ESN_TRIGGERED = 1UL << 1, - MLX5_ACCEL_ESP_FLAGS_ESN_STATE_OVERLAP = 1UL << 2, -}; - -enum mlx5_accel_esp_action { - MLX5_ACCEL_ESP_ACTION_DECRYPT, - MLX5_ACCEL_ESP_ACTION_ENCRYPT, -}; - struct aes_gcm_keymat { u64 seq_iv; @@ -66,7 +54,6 @@ struct aes_gcm_keymat { }; struct mlx5_accel_esp_xfrm_attrs { - enum mlx5_accel_esp_action action; u32 esn; u32 spi; u32 flags; @@ -82,7 +69,10 @@ struct mlx5_accel_esp_xfrm_attrs { __be32 a6[4]; } daddr; - u8 is_ipv6; + u8 dir : 2; + u8 esn_overlap : 1; + u8 esn_trigger : 1; + u8 family; u32 replay_window; }; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_fs.c b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_fs.c index b859e4a4c744..886263228b5d 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_fs.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_fs.c @@ -341,7 +341,7 @@ static void setup_fte_common(struct mlx5_accel_esp_xfrm_attrs *attrs, struct mlx5_flow_spec *spec, struct mlx5_flow_act *flow_act) { - u8 ip_version = attrs->is_ipv6 ? 6 : 4; + u8 ip_version = (attrs->family == AF_INET) ? 4 : 6; spec->match_criteria_enable = MLX5_MATCH_OUTER_HEADERS | MLX5_MATCH_MISC_PARAMETERS; @@ -411,7 +411,7 @@ static int rx_add_rule(struct mlx5e_priv *priv, int err = 0; accel_esp = priv->ipsec->rx_fs; - type = attrs->is_ipv6 ? ACCEL_FS_ESP6 : ACCEL_FS_ESP4; + type = (attrs->family == AF_INET) ? ACCEL_FS_ESP4 : ACCEL_FS_ESP6; fs_prot = &accel_esp->fs_prot[type]; err = rx_ft_get(priv, type); @@ -453,8 +453,8 @@ static int rx_add_rule(struct mlx5e_priv *priv, rule = mlx5_add_flow_rules(fs_prot->ft, spec, &flow_act, &dest, 1); if (IS_ERR(rule)) { err = PTR_ERR(rule); - netdev_err(priv->netdev, "fail to add ipsec rule attrs->action=0x%x, err=%d\n", - attrs->action, err); + netdev_err(priv->netdev, "fail to add RX ipsec rule err=%d\n", + err); goto out_err; } @@ -505,8 +505,8 @@ static int tx_add_rule(struct mlx5e_priv *priv, rule = mlx5_add_flow_rules(priv->ipsec->tx_fs->ft, spec, &flow_act, NULL, 0); if (IS_ERR(rule)) { err = PTR_ERR(rule); - netdev_err(priv->netdev, "fail to add ipsec rule attrs->action=0x%x, err=%d\n", - sa_entry->attrs.action, err); + netdev_err(priv->netdev, "fail to add TX ipsec rule err=%d\n", + err); goto out; } @@ -522,7 +522,7 @@ out: int mlx5e_accel_ipsec_fs_add_rule(struct mlx5e_priv *priv, struct mlx5e_ipsec_sa_entry *sa_entry) { - if (sa_entry->attrs.action == MLX5_ACCEL_ESP_ACTION_ENCRYPT) + if (sa_entry->attrs.dir == XFRM_DEV_OFFLOAD_OUT) return tx_add_rule(priv, sa_entry); return rx_add_rule(priv, sa_entry); @@ -533,17 +533,18 @@ void mlx5e_accel_ipsec_fs_del_rule(struct mlx5e_priv *priv, { struct mlx5e_ipsec_rule *ipsec_rule = &sa_entry->ipsec_rule; struct mlx5_core_dev *mdev = mlx5e_ipsec_sa2dev(sa_entry); + enum accel_fs_esp_type type; mlx5_del_flow_rules(ipsec_rule->rule); - if (sa_entry->attrs.action == MLX5_ACCEL_ESP_ACTION_ENCRYPT) { + if (sa_entry->attrs.dir == XFRM_DEV_OFFLOAD_OUT) { tx_ft_put(priv); return; } mlx5_modify_header_dealloc(mdev, ipsec_rule->set_modify_hdr); - rx_ft_put(priv, - sa_entry->attrs.is_ipv6 ? ACCEL_FS_ESP6 : ACCEL_FS_ESP4); + type = (sa_entry->attrs.family == AF_INET) ? ACCEL_FS_ESP4 : ACCEL_FS_ESP6; + rx_ft_put(priv, type); } void mlx5e_accel_ipsec_fs_cleanup(struct mlx5e_ipsec *ipsec) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_offload.c b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_offload.c index 3d5a1f875398..3f2aeb07ea84 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_offload.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_offload.c @@ -72,11 +72,10 @@ static int mlx5_create_ipsec_obj(struct mlx5e_ipsec_sa_entry *sa_entry) salt_iv_p = MLX5_ADDR_OF(ipsec_obj, obj, implicit_iv); memcpy(salt_iv_p, &aes_gcm->seq_iv, sizeof(aes_gcm->seq_iv)); /* esn */ - if (attrs->flags & MLX5_ACCEL_ESP_FLAGS_ESN_TRIGGERED) { + if (attrs->esn_trigger) { MLX5_SET(ipsec_obj, obj, esn_en, 1); MLX5_SET(ipsec_obj, obj, esn_msb, attrs->esn); - if (attrs->flags & MLX5_ACCEL_ESP_FLAGS_ESN_STATE_OVERLAP) - MLX5_SET(ipsec_obj, obj, esn_overlap, 1); + MLX5_SET(ipsec_obj, obj, esn_overlap, attrs->esn_overlap); } MLX5_SET(ipsec_obj, obj, dekn, sa_entry->enc_key_id); @@ -158,7 +157,7 @@ static int mlx5_modify_ipsec_obj(struct mlx5e_ipsec_sa_entry *sa_entry, void *obj; int err; - if (!(attrs->flags & MLX5_ACCEL_ESP_FLAGS_ESN_TRIGGERED)) + if (!attrs->esn_trigger) return 0; general_obj_types = MLX5_CAP_GEN_64(mdev, general_obj_types); @@ -189,8 +188,7 @@ static int mlx5_modify_ipsec_obj(struct mlx5e_ipsec_sa_entry *sa_entry, MLX5_MODIFY_IPSEC_BITMASK_ESN_OVERLAP | MLX5_MODIFY_IPSEC_BITMASK_ESN_MSB); MLX5_SET(ipsec_obj, obj, esn_msb, attrs->esn); - if (attrs->flags & MLX5_ACCEL_ESP_FLAGS_ESN_STATE_OVERLAP) - MLX5_SET(ipsec_obj, obj, esn_overlap, 1); + MLX5_SET(ipsec_obj, obj, esn_overlap, attrs->esn_overlap); /* general object fields set */ MLX5_SET(general_obj_in_cmd_hdr, in, opcode, MLX5_CMD_OP_MODIFY_GENERAL_OBJECT); From 9e5286dcbbbc47b4666df1733590260ac0b475af Mon Sep 17 00:00:00 2001 From: Leon Romanovsky Date: Fri, 2 Dec 2022 22:10:27 +0200 Subject: [PATCH 1608/2002] net/mlx5e: Create symmetric IPsec RX and TX flow steering structs Remove AF family obfuscation by creating symmetric structs for RX and TX IPsec flow steering chains. This simplifies to us low level IPsec FS creation logic without need to dig into multiple levels of structs. Reviewed-by: Raed Salem Reviewed-by: Saeed Mahameed Signed-off-by: Leon Romanovsky Signed-off-by: Steffen Klassert --- .../mellanox/mlx5/core/en_accel/ipsec.h | 7 +- .../mellanox/mlx5/core/en_accel/ipsec_fs.c | 275 ++++++++---------- 2 files changed, 129 insertions(+), 153 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.h b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.h index 05790b7d062f..6b961ff08ed7 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.h @@ -94,7 +94,7 @@ struct mlx5e_ipsec_sw_stats { atomic64_t ipsec_tx_drop_trailer; }; -struct mlx5e_accel_fs_esp; +struct mlx5e_ipsec_rx; struct mlx5e_ipsec_tx; struct mlx5e_ipsec { @@ -103,8 +103,9 @@ struct mlx5e_ipsec { spinlock_t sadb_rx_lock; /* Protects sadb_rx */ struct mlx5e_ipsec_sw_stats sw_stats; struct workqueue_struct *wq; - struct mlx5e_accel_fs_esp *rx_fs; - struct mlx5e_ipsec_tx *tx_fs; + struct mlx5e_ipsec_rx *rx_ipv4; + struct mlx5e_ipsec_rx *rx_ipv6; + struct mlx5e_ipsec_tx *tx; }; struct mlx5e_ipsec_esn_state { diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_fs.c b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_fs.c index 886263228b5d..a8cf3f8d0515 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_fs.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_fs.c @@ -9,49 +9,40 @@ #define NUM_IPSEC_FTE BIT(15) -enum accel_fs_esp_type { - ACCEL_FS_ESP4, - ACCEL_FS_ESP6, - ACCEL_FS_ESP_NUM_TYPES, -}; - struct mlx5e_ipsec_rx_err { struct mlx5_flow_table *ft; struct mlx5_flow_handle *rule; struct mlx5_modify_hdr *copy_modify_hdr; }; -struct mlx5e_accel_fs_esp_prot { - struct mlx5_flow_table *ft; +struct mlx5e_ipsec_ft { + struct mutex mutex; /* Protect changes to this struct */ + struct mlx5_flow_table *sa; + u32 refcnt; +}; + +struct mlx5e_ipsec_rx { + struct mlx5e_ipsec_ft ft; struct mlx5_flow_group *miss_group; struct mlx5_flow_handle *miss_rule; struct mlx5_flow_destination default_dest; struct mlx5e_ipsec_rx_err rx_err; - u32 refcnt; - struct mutex prot_mutex; /* protect ESP4/ESP6 protocol */ -}; - -struct mlx5e_accel_fs_esp { - struct mlx5e_accel_fs_esp_prot fs_prot[ACCEL_FS_ESP_NUM_TYPES]; }; struct mlx5e_ipsec_tx { + struct mlx5e_ipsec_ft ft; struct mlx5_flow_namespace *ns; - struct mlx5_flow_table *ft; - struct mutex mutex; /* Protect IPsec TX steering */ - u32 refcnt; }; /* IPsec RX flow steering */ -static enum mlx5_traffic_types fs_esp2tt(enum accel_fs_esp_type i) +static enum mlx5_traffic_types family2tt(u32 family) { - if (i == ACCEL_FS_ESP4) + if (family == AF_INET) return MLX5_TT_IPV4_IPSEC_ESP; return MLX5_TT_IPV6_IPSEC_ESP; } -static int rx_err_add_rule(struct mlx5e_priv *priv, - struct mlx5e_accel_fs_esp_prot *fs_prot, +static int rx_err_add_rule(struct mlx5e_priv *priv, struct mlx5e_ipsec_rx *rx, struct mlx5e_ipsec_rx_err *rx_err) { u8 action[MLX5_UN_SZ_BYTES(set_add_copy_action_in_auto)] = {}; @@ -89,7 +80,7 @@ static int rx_err_add_rule(struct mlx5e_priv *priv, MLX5_FLOW_CONTEXT_ACTION_FWD_DEST; flow_act.modify_hdr = modify_hdr; fte = mlx5_add_flow_rules(rx_err->ft, spec, &flow_act, - &fs_prot->default_dest, 1); + &rx->default_dest, 1); if (IS_ERR(fte)) { err = PTR_ERR(fte); netdev_err(priv->netdev, "fail to add ipsec rx err copy rule err=%d\n", err); @@ -108,11 +99,10 @@ out_spec: return err; } -static int rx_fs_create(struct mlx5e_priv *priv, - struct mlx5e_accel_fs_esp_prot *fs_prot) +static int rx_fs_create(struct mlx5e_priv *priv, struct mlx5e_ipsec_rx *rx) { int inlen = MLX5_ST_SZ_BYTES(create_flow_group_in); - struct mlx5_flow_table *ft = fs_prot->ft; + struct mlx5_flow_table *ft = rx->ft.sa; struct mlx5_flow_group *miss_group; struct mlx5_flow_handle *miss_rule; MLX5_DECLARE_FLOW_ACT(flow_act); @@ -136,56 +126,45 @@ static int rx_fs_create(struct mlx5e_priv *priv, netdev_err(priv->netdev, "fail to create ipsec rx miss_group err=%d\n", err); goto out; } - fs_prot->miss_group = miss_group; + rx->miss_group = miss_group; /* Create miss rule */ - miss_rule = mlx5_add_flow_rules(ft, spec, &flow_act, &fs_prot->default_dest, 1); + miss_rule = + mlx5_add_flow_rules(ft, spec, &flow_act, &rx->default_dest, 1); if (IS_ERR(miss_rule)) { - mlx5_destroy_flow_group(fs_prot->miss_group); + mlx5_destroy_flow_group(rx->miss_group); err = PTR_ERR(miss_rule); netdev_err(priv->netdev, "fail to create ipsec rx miss_rule err=%d\n", err); goto out; } - fs_prot->miss_rule = miss_rule; + rx->miss_rule = miss_rule; out: kvfree(flow_group_in); kvfree(spec); return err; } -static void rx_destroy(struct mlx5e_priv *priv, enum accel_fs_esp_type type) +static void rx_destroy(struct mlx5e_priv *priv, struct mlx5e_ipsec_rx *rx) { - struct mlx5e_accel_fs_esp_prot *fs_prot; - struct mlx5e_accel_fs_esp *accel_esp; + mlx5_del_flow_rules(rx->miss_rule); + mlx5_destroy_flow_group(rx->miss_group); + mlx5_destroy_flow_table(rx->ft.sa); - accel_esp = priv->ipsec->rx_fs; - - /* The netdev unreg already happened, so all offloaded rule are already removed */ - fs_prot = &accel_esp->fs_prot[type]; - - mlx5_del_flow_rules(fs_prot->miss_rule); - mlx5_destroy_flow_group(fs_prot->miss_group); - mlx5_destroy_flow_table(fs_prot->ft); - - mlx5_del_flow_rules(fs_prot->rx_err.rule); - mlx5_modify_header_dealloc(priv->mdev, fs_prot->rx_err.copy_modify_hdr); - mlx5_destroy_flow_table(fs_prot->rx_err.ft); + mlx5_del_flow_rules(rx->rx_err.rule); + mlx5_modify_header_dealloc(priv->mdev, rx->rx_err.copy_modify_hdr); + mlx5_destroy_flow_table(rx->rx_err.ft); } -static int rx_create(struct mlx5e_priv *priv, enum accel_fs_esp_type type) +static int rx_create(struct mlx5e_priv *priv, struct mlx5e_ipsec_rx *rx, + u32 family) { struct mlx5_flow_namespace *ns = mlx5e_fs_get_ns(priv->fs, false); struct mlx5_ttc_table *ttc = mlx5e_fs_get_ttc(priv->fs, false); struct mlx5_flow_table_attr ft_attr = {}; - struct mlx5e_accel_fs_esp_prot *fs_prot; - struct mlx5e_accel_fs_esp *accel_esp; struct mlx5_flow_table *ft; int err; - accel_esp = priv->ipsec->rx_fs; - fs_prot = &accel_esp->fs_prot[type]; - fs_prot->default_dest = - mlx5_ttc_get_default_dest(ttc, fs_esp2tt(type)); + rx->default_dest = mlx5_ttc_get_default_dest(ttc, family2tt(family)); ft_attr.max_fte = 1; ft_attr.autogroup.max_num_groups = 1; @@ -195,8 +174,8 @@ static int rx_create(struct mlx5e_priv *priv, enum accel_fs_esp_type type) if (IS_ERR(ft)) return PTR_ERR(ft); - fs_prot->rx_err.ft = ft; - err = rx_err_add_rule(priv, fs_prot, &fs_prot->rx_err); + rx->rx_err.ft = ft; + err = rx_err_add_rule(priv, rx, &rx->rx_err); if (err) goto err_add; @@ -211,76 +190,82 @@ static int rx_create(struct mlx5e_priv *priv, enum accel_fs_esp_type type) err = PTR_ERR(ft); goto err_fs_ft; } - fs_prot->ft = ft; + rx->ft.sa = ft; - err = rx_fs_create(priv, fs_prot); + err = rx_fs_create(priv, rx); if (err) goto err_fs; return 0; err_fs: - mlx5_destroy_flow_table(fs_prot->ft); + mlx5_destroy_flow_table(rx->ft.sa); err_fs_ft: - mlx5_del_flow_rules(fs_prot->rx_err.rule); - mlx5_modify_header_dealloc(priv->mdev, fs_prot->rx_err.copy_modify_hdr); + mlx5_del_flow_rules(rx->rx_err.rule); + mlx5_modify_header_dealloc(priv->mdev, rx->rx_err.copy_modify_hdr); err_add: - mlx5_destroy_flow_table(fs_prot->rx_err.ft); + mlx5_destroy_flow_table(rx->rx_err.ft); return err; } -static int rx_ft_get(struct mlx5e_priv *priv, enum accel_fs_esp_type type) +static struct mlx5e_ipsec_rx *rx_ft_get(struct mlx5e_priv *priv, u32 family) { struct mlx5_ttc_table *ttc = mlx5e_fs_get_ttc(priv->fs, false); - struct mlx5e_accel_fs_esp_prot *fs_prot; struct mlx5_flow_destination dest = {}; - struct mlx5e_accel_fs_esp *accel_esp; + struct mlx5e_ipsec_rx *rx; int err = 0; - accel_esp = priv->ipsec->rx_fs; - fs_prot = &accel_esp->fs_prot[type]; - mutex_lock(&fs_prot->prot_mutex); - if (fs_prot->refcnt) + if (family == AF_INET) + rx = priv->ipsec->rx_ipv4; + else + rx = priv->ipsec->rx_ipv6; + + mutex_lock(&rx->ft.mutex); + if (rx->ft.refcnt) goto skip; /* create FT */ - err = rx_create(priv, type); + err = rx_create(priv, rx, family); if (err) goto out; /* connect */ dest.type = MLX5_FLOW_DESTINATION_TYPE_FLOW_TABLE; - dest.ft = fs_prot->ft; - mlx5_ttc_fwd_dest(ttc, fs_esp2tt(type), &dest); + dest.ft = rx->ft.sa; + mlx5_ttc_fwd_dest(ttc, family2tt(family), &dest); skip: - fs_prot->refcnt++; + rx->ft.refcnt++; out: - mutex_unlock(&fs_prot->prot_mutex); - return err; + mutex_unlock(&rx->ft.mutex); + if (err) + return ERR_PTR(err); + return rx; } -static void rx_ft_put(struct mlx5e_priv *priv, enum accel_fs_esp_type type) +static void rx_ft_put(struct mlx5e_priv *priv, u32 family) { struct mlx5_ttc_table *ttc = mlx5e_fs_get_ttc(priv->fs, false); - struct mlx5e_accel_fs_esp_prot *fs_prot; - struct mlx5e_accel_fs_esp *accel_esp; + struct mlx5e_ipsec_rx *rx; - accel_esp = priv->ipsec->rx_fs; - fs_prot = &accel_esp->fs_prot[type]; - mutex_lock(&fs_prot->prot_mutex); - fs_prot->refcnt--; - if (fs_prot->refcnt) + if (family == AF_INET) + rx = priv->ipsec->rx_ipv4; + else + rx = priv->ipsec->rx_ipv6; + + mutex_lock(&rx->ft.mutex); + rx->ft.refcnt--; + if (rx->ft.refcnt) goto out; /* disconnect */ - mlx5_ttc_fwd_default_dest(ttc, fs_esp2tt(type)); + mlx5_ttc_fwd_default_dest(ttc, family2tt(family)); /* remove FT */ - rx_destroy(priv, type); + rx_destroy(priv, rx); out: - mutex_unlock(&fs_prot->prot_mutex); + mutex_unlock(&rx->ft.mutex); } /* IPsec TX flow steering */ @@ -293,47 +278,49 @@ static int tx_create(struct mlx5e_priv *priv) ft_attr.max_fte = NUM_IPSEC_FTE; ft_attr.autogroup.max_num_groups = 1; - ft = mlx5_create_auto_grouped_flow_table(ipsec->tx_fs->ns, &ft_attr); + ft = mlx5_create_auto_grouped_flow_table(ipsec->tx->ns, &ft_attr); if (IS_ERR(ft)) { err = PTR_ERR(ft); netdev_err(priv->netdev, "fail to create ipsec tx ft err=%d\n", err); return err; } - ipsec->tx_fs->ft = ft; + ipsec->tx->ft.sa = ft; return 0; } -static int tx_ft_get(struct mlx5e_priv *priv) +static struct mlx5e_ipsec_tx *tx_ft_get(struct mlx5e_priv *priv) { - struct mlx5e_ipsec_tx *tx_fs = priv->ipsec->tx_fs; + struct mlx5e_ipsec_tx *tx = priv->ipsec->tx; int err = 0; - mutex_lock(&tx_fs->mutex); - if (tx_fs->refcnt) + mutex_lock(&tx->ft.mutex); + if (tx->ft.refcnt) goto skip; err = tx_create(priv); if (err) goto out; skip: - tx_fs->refcnt++; + tx->ft.refcnt++; out: - mutex_unlock(&tx_fs->mutex); - return err; + mutex_unlock(&tx->ft.mutex); + if (err) + return ERR_PTR(err); + return tx; } static void tx_ft_put(struct mlx5e_priv *priv) { - struct mlx5e_ipsec_tx *tx_fs = priv->ipsec->tx_fs; + struct mlx5e_ipsec_tx *tx = priv->ipsec->tx; - mutex_lock(&tx_fs->mutex); - tx_fs->refcnt--; - if (tx_fs->refcnt) + mutex_lock(&tx->ft.mutex); + tx->ft.refcnt--; + if (tx->ft.refcnt) goto out; - mlx5_destroy_flow_table(tx_fs->ft); + mlx5_destroy_flow_table(tx->ft.sa); out: - mutex_unlock(&tx_fs->mutex); + mutex_unlock(&tx->ft.mutex); } static void setup_fte_common(struct mlx5_accel_esp_xfrm_attrs *attrs, @@ -401,22 +388,16 @@ static int rx_add_rule(struct mlx5e_priv *priv, struct mlx5_accel_esp_xfrm_attrs *attrs = &sa_entry->attrs; u32 ipsec_obj_id = sa_entry->ipsec_obj_id; struct mlx5_modify_hdr *modify_hdr = NULL; - struct mlx5e_accel_fs_esp_prot *fs_prot; struct mlx5_flow_destination dest = {}; - struct mlx5e_accel_fs_esp *accel_esp; struct mlx5_flow_act flow_act = {}; struct mlx5_flow_handle *rule; - enum accel_fs_esp_type type; struct mlx5_flow_spec *spec; + struct mlx5e_ipsec_rx *rx; int err = 0; - accel_esp = priv->ipsec->rx_fs; - type = (attrs->family == AF_INET) ? ACCEL_FS_ESP4 : ACCEL_FS_ESP6; - fs_prot = &accel_esp->fs_prot[type]; - - err = rx_ft_get(priv, type); - if (err) - return err; + rx = rx_ft_get(priv, attrs->family); + if (IS_ERR(rx)) + return PTR_ERR(rx); spec = kvzalloc(sizeof(*spec), GFP_KERNEL); if (!spec) { @@ -449,8 +430,8 @@ static int rx_add_rule(struct mlx5e_priv *priv, MLX5_FLOW_CONTEXT_ACTION_MOD_HDR; dest.type = MLX5_FLOW_DESTINATION_TYPE_FLOW_TABLE; flow_act.modify_hdr = modify_hdr; - dest.ft = fs_prot->rx_err.ft; - rule = mlx5_add_flow_rules(fs_prot->ft, spec, &flow_act, &dest, 1); + dest.ft = rx->rx_err.ft; + rule = mlx5_add_flow_rules(rx->ft.sa, spec, &flow_act, &dest, 1); if (IS_ERR(rule)) { err = PTR_ERR(rule); netdev_err(priv->netdev, "fail to add RX ipsec rule err=%d\n", @@ -465,7 +446,7 @@ static int rx_add_rule(struct mlx5e_priv *priv, out_err: if (modify_hdr) mlx5_modify_header_dealloc(priv->mdev, modify_hdr); - rx_ft_put(priv, type); + rx_ft_put(priv, attrs->family); out: kvfree(spec); @@ -478,11 +459,12 @@ static int tx_add_rule(struct mlx5e_priv *priv, struct mlx5_flow_act flow_act = {}; struct mlx5_flow_handle *rule; struct mlx5_flow_spec *spec; + struct mlx5e_ipsec_tx *tx; int err = 0; - err = tx_ft_get(priv); - if (err) - return err; + tx = tx_ft_get(priv); + if (IS_ERR(tx)) + return PTR_ERR(tx); spec = kvzalloc(sizeof(*spec), GFP_KERNEL); if (!spec) { @@ -502,7 +484,7 @@ static int tx_add_rule(struct mlx5e_priv *priv, flow_act.action = MLX5_FLOW_CONTEXT_ACTION_ALLOW | MLX5_FLOW_CONTEXT_ACTION_CRYPTO_ENCRYPT; - rule = mlx5_add_flow_rules(priv->ipsec->tx_fs->ft, spec, &flow_act, NULL, 0); + rule = mlx5_add_flow_rules(tx->ft.sa, spec, &flow_act, NULL, 0); if (IS_ERR(rule)) { err = PTR_ERR(rule); netdev_err(priv->netdev, "fail to add TX ipsec rule err=%d\n", @@ -533,7 +515,6 @@ void mlx5e_accel_ipsec_fs_del_rule(struct mlx5e_priv *priv, { struct mlx5e_ipsec_rule *ipsec_rule = &sa_entry->ipsec_rule; struct mlx5_core_dev *mdev = mlx5e_ipsec_sa2dev(sa_entry); - enum accel_fs_esp_type type; mlx5_del_flow_rules(ipsec_rule->rule); @@ -543,38 +524,30 @@ void mlx5e_accel_ipsec_fs_del_rule(struct mlx5e_priv *priv, } mlx5_modify_header_dealloc(mdev, ipsec_rule->set_modify_hdr); - type = (sa_entry->attrs.family == AF_INET) ? ACCEL_FS_ESP4 : ACCEL_FS_ESP6; - rx_ft_put(priv, type); + rx_ft_put(priv, sa_entry->attrs.family); } void mlx5e_accel_ipsec_fs_cleanup(struct mlx5e_ipsec *ipsec) { - struct mlx5e_accel_fs_esp_prot *fs_prot; - struct mlx5e_accel_fs_esp *accel_esp; - enum accel_fs_esp_type i; - - if (!ipsec->rx_fs) + if (!ipsec->tx) return; - mutex_destroy(&ipsec->tx_fs->mutex); - WARN_ON(ipsec->tx_fs->refcnt); - kfree(ipsec->tx_fs); + mutex_destroy(&ipsec->tx->ft.mutex); + WARN_ON(ipsec->tx->ft.refcnt); + kfree(ipsec->tx); - accel_esp = ipsec->rx_fs; - for (i = 0; i < ACCEL_FS_ESP_NUM_TYPES; i++) { - fs_prot = &accel_esp->fs_prot[i]; - mutex_destroy(&fs_prot->prot_mutex); - WARN_ON(fs_prot->refcnt); - } - kfree(ipsec->rx_fs); + mutex_destroy(&ipsec->rx_ipv4->ft.mutex); + WARN_ON(ipsec->rx_ipv4->ft.refcnt); + kfree(ipsec->rx_ipv4); + + mutex_destroy(&ipsec->rx_ipv6->ft.mutex); + WARN_ON(ipsec->rx_ipv6->ft.refcnt); + kfree(ipsec->rx_ipv6); } int mlx5e_accel_ipsec_fs_init(struct mlx5e_ipsec *ipsec) { - struct mlx5e_accel_fs_esp_prot *fs_prot; - struct mlx5e_accel_fs_esp *accel_esp; struct mlx5_flow_namespace *ns; - enum accel_fs_esp_type i; int err = -ENOMEM; ns = mlx5_get_flow_namespace(ipsec->mdev, @@ -582,26 +555,28 @@ int mlx5e_accel_ipsec_fs_init(struct mlx5e_ipsec *ipsec) if (!ns) return -EOPNOTSUPP; - ipsec->tx_fs = kzalloc(sizeof(*ipsec->tx_fs), GFP_KERNEL); - if (!ipsec->tx_fs) + ipsec->tx = kzalloc(sizeof(*ipsec->tx), GFP_KERNEL); + if (!ipsec->tx) return -ENOMEM; - ipsec->rx_fs = kzalloc(sizeof(*ipsec->rx_fs), GFP_KERNEL); - if (!ipsec->rx_fs) - goto err_rx; + ipsec->rx_ipv4 = kzalloc(sizeof(*ipsec->rx_ipv4), GFP_KERNEL); + if (!ipsec->rx_ipv4) + goto err_rx_ipv4; - mutex_init(&ipsec->tx_fs->mutex); - ipsec->tx_fs->ns = ns; + ipsec->rx_ipv6 = kzalloc(sizeof(*ipsec->rx_ipv6), GFP_KERNEL); + if (!ipsec->rx_ipv6) + goto err_rx_ipv6; - accel_esp = ipsec->rx_fs; - for (i = 0; i < ACCEL_FS_ESP_NUM_TYPES; i++) { - fs_prot = &accel_esp->fs_prot[i]; - mutex_init(&fs_prot->prot_mutex); - } + mutex_init(&ipsec->tx->ft.mutex); + mutex_init(&ipsec->rx_ipv4->ft.mutex); + mutex_init(&ipsec->rx_ipv6->ft.mutex); + ipsec->tx->ns = ns; return 0; -err_rx: - kfree(ipsec->tx_fs); +err_rx_ipv6: + kfree(ipsec->rx_ipv4); +err_rx_ipv4: + kfree(ipsec->tx); return err; } From fb2caa711f690f71cc6cd256faadeff7ab76f900 Mon Sep 17 00:00:00 2001 From: Leon Romanovsky Date: Fri, 2 Dec 2022 22:10:28 +0200 Subject: [PATCH 1609/2002] net/mlx5e: Use mlx5 print routines for low level IPsec code Low level mlx5 code needs to use mlx5_core print routines and not netdev ones, as the failures are relevant to the HW itself and not to its netdev. This change allows us to remove access to mlx5 priv structure, which holds high level driver data that isn't needed for mlx5 IPsec code. Reviewed-by: Saeed Mahameed Signed-off-by: Leon Romanovsky Signed-off-by: Steffen Klassert --- .../mellanox/mlx5/core/en_accel/ipsec_fs.c | 26 ++++++++++--------- 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_fs.c b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_fs.c index a8cf3f8d0515..08feff765032 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_fs.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_fs.c @@ -70,8 +70,8 @@ static int rx_err_add_rule(struct mlx5e_priv *priv, struct mlx5e_ipsec_rx *rx, if (IS_ERR(modify_hdr)) { err = PTR_ERR(modify_hdr); - netdev_err(priv->netdev, - "fail to alloc ipsec copy modify_header_id err=%d\n", err); + mlx5_core_err(mdev, + "fail to alloc ipsec copy modify_header_id err=%d\n", err); goto out_spec; } @@ -83,7 +83,7 @@ static int rx_err_add_rule(struct mlx5e_priv *priv, struct mlx5e_ipsec_rx *rx, &rx->default_dest, 1); if (IS_ERR(fte)) { err = PTR_ERR(fte); - netdev_err(priv->netdev, "fail to add ipsec rx err copy rule err=%d\n", err); + mlx5_core_err(mdev, "fail to add ipsec rx err copy rule err=%d\n", err); goto out; } @@ -103,6 +103,7 @@ static int rx_fs_create(struct mlx5e_priv *priv, struct mlx5e_ipsec_rx *rx) { int inlen = MLX5_ST_SZ_BYTES(create_flow_group_in); struct mlx5_flow_table *ft = rx->ft.sa; + struct mlx5_core_dev *mdev = priv->mdev; struct mlx5_flow_group *miss_group; struct mlx5_flow_handle *miss_rule; MLX5_DECLARE_FLOW_ACT(flow_act); @@ -123,7 +124,7 @@ static int rx_fs_create(struct mlx5e_priv *priv, struct mlx5e_ipsec_rx *rx) miss_group = mlx5_create_flow_group(ft, flow_group_in); if (IS_ERR(miss_group)) { err = PTR_ERR(miss_group); - netdev_err(priv->netdev, "fail to create ipsec rx miss_group err=%d\n", err); + mlx5_core_err(mdev, "fail to create ipsec rx miss_group err=%d\n", err); goto out; } rx->miss_group = miss_group; @@ -134,7 +135,7 @@ static int rx_fs_create(struct mlx5e_priv *priv, struct mlx5e_ipsec_rx *rx) if (IS_ERR(miss_rule)) { mlx5_destroy_flow_group(rx->miss_group); err = PTR_ERR(miss_rule); - netdev_err(priv->netdev, "fail to create ipsec rx miss_rule err=%d\n", err); + mlx5_core_err(mdev, "fail to create ipsec rx miss_rule err=%d\n", err); goto out; } rx->miss_rule = miss_rule; @@ -273,6 +274,7 @@ static int tx_create(struct mlx5e_priv *priv) { struct mlx5_flow_table_attr ft_attr = {}; struct mlx5e_ipsec *ipsec = priv->ipsec; + struct mlx5_core_dev *mdev = priv->mdev; struct mlx5_flow_table *ft; int err; @@ -281,7 +283,7 @@ static int tx_create(struct mlx5e_priv *priv) ft = mlx5_create_auto_grouped_flow_table(ipsec->tx->ns, &ft_attr); if (IS_ERR(ft)) { err = PTR_ERR(ft); - netdev_err(priv->netdev, "fail to create ipsec tx ft err=%d\n", err); + mlx5_core_err(mdev, "fail to create ipsec tx ft err=%d\n", err); return err; } ipsec->tx->ft.sa = ft; @@ -386,6 +388,7 @@ static int rx_add_rule(struct mlx5e_priv *priv, u8 action[MLX5_UN_SZ_BYTES(set_add_copy_action_in_auto)] = {}; struct mlx5e_ipsec_rule *ipsec_rule = &sa_entry->ipsec_rule; struct mlx5_accel_esp_xfrm_attrs *attrs = &sa_entry->attrs; + struct mlx5_core_dev *mdev = mlx5e_ipsec_sa2dev(sa_entry); u32 ipsec_obj_id = sa_entry->ipsec_obj_id; struct mlx5_modify_hdr *modify_hdr = NULL; struct mlx5_flow_destination dest = {}; @@ -419,8 +422,8 @@ static int rx_add_rule(struct mlx5e_priv *priv, 1, action); if (IS_ERR(modify_hdr)) { err = PTR_ERR(modify_hdr); - netdev_err(priv->netdev, - "fail to alloc ipsec set modify_header_id err=%d\n", err); + mlx5_core_err(mdev, + "fail to alloc ipsec set modify_header_id err=%d\n", err); modify_hdr = NULL; goto out_err; } @@ -434,8 +437,7 @@ static int rx_add_rule(struct mlx5e_priv *priv, rule = mlx5_add_flow_rules(rx->ft.sa, spec, &flow_act, &dest, 1); if (IS_ERR(rule)) { err = PTR_ERR(rule); - netdev_err(priv->netdev, "fail to add RX ipsec rule err=%d\n", - err); + mlx5_core_err(mdev, "fail to add RX ipsec rule err=%d\n", err); goto out_err; } @@ -456,6 +458,7 @@ out: static int tx_add_rule(struct mlx5e_priv *priv, struct mlx5e_ipsec_sa_entry *sa_entry) { + struct mlx5_core_dev *mdev = mlx5e_ipsec_sa2dev(sa_entry); struct mlx5_flow_act flow_act = {}; struct mlx5_flow_handle *rule; struct mlx5_flow_spec *spec; @@ -487,8 +490,7 @@ static int tx_add_rule(struct mlx5e_priv *priv, rule = mlx5_add_flow_rules(tx->ft.sa, spec, &flow_act, NULL, 0); if (IS_ERR(rule)) { err = PTR_ERR(rule); - netdev_err(priv->netdev, "fail to add TX ipsec rule err=%d\n", - err); + mlx5_core_err(mdev, "fail to add TX ipsec rule err=%d\n", err); goto out; } From c7049ca6213732e5d511a4b8d6fdabea4c3b4811 Mon Sep 17 00:00:00 2001 From: Leon Romanovsky Date: Fri, 2 Dec 2022 22:10:29 +0200 Subject: [PATCH 1610/2002] net/mlx5e: Remove accesses to priv for low level IPsec FS code mlx5 priv structure is driver main structure that holds high level data. That information is not needed for IPsec flow steering logic and the pointer to mlx5e_priv was not supposed to be passed in the first place. This change "cleans" the logic to rely on internal to IPsec structures without touching global mlx5e_priv. Reviewed-by: Raed Salem Reviewed-by: Saeed Mahameed Signed-off-by: Leon Romanovsky Signed-off-by: Steffen Klassert --- .../mellanox/mlx5/core/en_accel/ipsec.c | 8 +- .../mellanox/mlx5/core/en_accel/ipsec.h | 7 +- .../mellanox/mlx5/core/en_accel/ipsec_fs.c | 100 +++++++++--------- 3 files changed, 56 insertions(+), 59 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.c b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.c index 14ed72b26bbe..f518322c1ac1 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.c @@ -306,7 +306,7 @@ static int mlx5e_xfrm_add_state(struct xfrm_state *x) if (err) goto err_xfrm; - err = mlx5e_accel_ipsec_fs_add_rule(priv, sa_entry); + err = mlx5e_accel_ipsec_fs_add_rule(sa_entry); if (err) goto err_hw_ctx; @@ -324,7 +324,7 @@ static int mlx5e_xfrm_add_state(struct xfrm_state *x) goto out; err_add_rule: - mlx5e_accel_ipsec_fs_del_rule(priv, sa_entry); + mlx5e_accel_ipsec_fs_del_rule(sa_entry); err_hw_ctx: mlx5_ipsec_free_sa_ctx(sa_entry); err_xfrm: @@ -344,10 +344,9 @@ static void mlx5e_xfrm_del_state(struct xfrm_state *x) static void mlx5e_xfrm_free_state(struct xfrm_state *x) { struct mlx5e_ipsec_sa_entry *sa_entry = to_ipsec_sa_entry(x); - struct mlx5e_priv *priv = netdev_priv(x->xso.dev); cancel_work_sync(&sa_entry->modify_work.work); - mlx5e_accel_ipsec_fs_del_rule(priv, sa_entry); + mlx5e_accel_ipsec_fs_del_rule(sa_entry); mlx5_ipsec_free_sa_ctx(sa_entry); kfree(sa_entry); } @@ -378,6 +377,7 @@ void mlx5e_ipsec_init(struct mlx5e_priv *priv) if (ret) goto err_fs_init; + ipsec->fs = priv->fs; priv->ipsec = ipsec; netdev_dbg(priv->netdev, "IPSec attached to netdevice\n"); return; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.h b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.h index 6b961ff08ed7..db0ccf2a797a 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.h @@ -103,6 +103,7 @@ struct mlx5e_ipsec { spinlock_t sadb_rx_lock; /* Protects sadb_rx */ struct mlx5e_ipsec_sw_stats sw_stats; struct workqueue_struct *wq; + struct mlx5e_flow_steering *fs; struct mlx5e_ipsec_rx *rx_ipv4; struct mlx5e_ipsec_rx *rx_ipv6; struct mlx5e_ipsec_tx *tx; @@ -148,10 +149,8 @@ struct xfrm_state *mlx5e_ipsec_sadb_rx_lookup(struct mlx5e_ipsec *dev, void mlx5e_accel_ipsec_fs_cleanup(struct mlx5e_ipsec *ipsec); int mlx5e_accel_ipsec_fs_init(struct mlx5e_ipsec *ipsec); -int mlx5e_accel_ipsec_fs_add_rule(struct mlx5e_priv *priv, - struct mlx5e_ipsec_sa_entry *sa_entry); -void mlx5e_accel_ipsec_fs_del_rule(struct mlx5e_priv *priv, - struct mlx5e_ipsec_sa_entry *sa_entry); +int mlx5e_accel_ipsec_fs_add_rule(struct mlx5e_ipsec_sa_entry *sa_entry); +void mlx5e_accel_ipsec_fs_del_rule(struct mlx5e_ipsec_sa_entry *sa_entry); int mlx5_ipsec_create_sa_ctx(struct mlx5e_ipsec_sa_entry *sa_entry); void mlx5_ipsec_free_sa_ctx(struct mlx5e_ipsec_sa_entry *sa_entry); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_fs.c b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_fs.c index 08feff765032..8e87d8d02511 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_fs.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_fs.c @@ -42,11 +42,11 @@ static enum mlx5_traffic_types family2tt(u32 family) return MLX5_TT_IPV6_IPSEC_ESP; } -static int rx_err_add_rule(struct mlx5e_priv *priv, struct mlx5e_ipsec_rx *rx, +static int rx_err_add_rule(struct mlx5_core_dev *mdev, + struct mlx5e_ipsec_rx *rx, struct mlx5e_ipsec_rx_err *rx_err) { u8 action[MLX5_UN_SZ_BYTES(set_add_copy_action_in_auto)] = {}; - struct mlx5_core_dev *mdev = priv->mdev; struct mlx5_flow_act flow_act = {}; struct mlx5_modify_hdr *modify_hdr; struct mlx5_flow_handle *fte; @@ -99,11 +99,10 @@ out_spec: return err; } -static int rx_fs_create(struct mlx5e_priv *priv, struct mlx5e_ipsec_rx *rx) +static int rx_fs_create(struct mlx5_core_dev *mdev, struct mlx5e_ipsec_rx *rx) { int inlen = MLX5_ST_SZ_BYTES(create_flow_group_in); struct mlx5_flow_table *ft = rx->ft.sa; - struct mlx5_core_dev *mdev = priv->mdev; struct mlx5_flow_group *miss_group; struct mlx5_flow_handle *miss_rule; MLX5_DECLARE_FLOW_ACT(flow_act); @@ -145,22 +144,22 @@ out: return err; } -static void rx_destroy(struct mlx5e_priv *priv, struct mlx5e_ipsec_rx *rx) +static void rx_destroy(struct mlx5_core_dev *mdev, struct mlx5e_ipsec_rx *rx) { mlx5_del_flow_rules(rx->miss_rule); mlx5_destroy_flow_group(rx->miss_group); mlx5_destroy_flow_table(rx->ft.sa); mlx5_del_flow_rules(rx->rx_err.rule); - mlx5_modify_header_dealloc(priv->mdev, rx->rx_err.copy_modify_hdr); + mlx5_modify_header_dealloc(mdev, rx->rx_err.copy_modify_hdr); mlx5_destroy_flow_table(rx->rx_err.ft); } -static int rx_create(struct mlx5e_priv *priv, struct mlx5e_ipsec_rx *rx, - u32 family) +static int rx_create(struct mlx5_core_dev *mdev, struct mlx5e_ipsec *ipsec, + struct mlx5e_ipsec_rx *rx, u32 family) { - struct mlx5_flow_namespace *ns = mlx5e_fs_get_ns(priv->fs, false); - struct mlx5_ttc_table *ttc = mlx5e_fs_get_ttc(priv->fs, false); + struct mlx5_flow_namespace *ns = mlx5e_fs_get_ns(ipsec->fs, false); + struct mlx5_ttc_table *ttc = mlx5e_fs_get_ttc(ipsec->fs, false); struct mlx5_flow_table_attr ft_attr = {}; struct mlx5_flow_table *ft; int err; @@ -176,7 +175,7 @@ static int rx_create(struct mlx5e_priv *priv, struct mlx5e_ipsec_rx *rx, return PTR_ERR(ft); rx->rx_err.ft = ft; - err = rx_err_add_rule(priv, rx, &rx->rx_err); + err = rx_err_add_rule(mdev, rx, &rx->rx_err); if (err) goto err_add; @@ -193,7 +192,7 @@ static int rx_create(struct mlx5e_priv *priv, struct mlx5e_ipsec_rx *rx, } rx->ft.sa = ft; - err = rx_fs_create(priv, rx); + err = rx_fs_create(mdev, rx); if (err) goto err_fs; @@ -203,30 +202,31 @@ err_fs: mlx5_destroy_flow_table(rx->ft.sa); err_fs_ft: mlx5_del_flow_rules(rx->rx_err.rule); - mlx5_modify_header_dealloc(priv->mdev, rx->rx_err.copy_modify_hdr); + mlx5_modify_header_dealloc(mdev, rx->rx_err.copy_modify_hdr); err_add: mlx5_destroy_flow_table(rx->rx_err.ft); return err; } -static struct mlx5e_ipsec_rx *rx_ft_get(struct mlx5e_priv *priv, u32 family) +static struct mlx5e_ipsec_rx *rx_ft_get(struct mlx5_core_dev *mdev, + struct mlx5e_ipsec *ipsec, u32 family) { - struct mlx5_ttc_table *ttc = mlx5e_fs_get_ttc(priv->fs, false); + struct mlx5_ttc_table *ttc = mlx5e_fs_get_ttc(ipsec->fs, false); struct mlx5_flow_destination dest = {}; struct mlx5e_ipsec_rx *rx; int err = 0; if (family == AF_INET) - rx = priv->ipsec->rx_ipv4; + rx = ipsec->rx_ipv4; else - rx = priv->ipsec->rx_ipv6; + rx = ipsec->rx_ipv6; mutex_lock(&rx->ft.mutex); if (rx->ft.refcnt) goto skip; /* create FT */ - err = rx_create(priv, rx, family); + err = rx_create(mdev, ipsec, rx, family); if (err) goto out; @@ -244,15 +244,16 @@ out: return rx; } -static void rx_ft_put(struct mlx5e_priv *priv, u32 family) +static void rx_ft_put(struct mlx5_core_dev *mdev, struct mlx5e_ipsec *ipsec, + u32 family) { - struct mlx5_ttc_table *ttc = mlx5e_fs_get_ttc(priv->fs, false); + struct mlx5_ttc_table *ttc = mlx5e_fs_get_ttc(ipsec->fs, false); struct mlx5e_ipsec_rx *rx; if (family == AF_INET) - rx = priv->ipsec->rx_ipv4; + rx = ipsec->rx_ipv4; else - rx = priv->ipsec->rx_ipv6; + rx = ipsec->rx_ipv6; mutex_lock(&rx->ft.mutex); rx->ft.refcnt--; @@ -263,43 +264,42 @@ static void rx_ft_put(struct mlx5e_priv *priv, u32 family) mlx5_ttc_fwd_default_dest(ttc, family2tt(family)); /* remove FT */ - rx_destroy(priv, rx); + rx_destroy(mdev, rx); out: mutex_unlock(&rx->ft.mutex); } /* IPsec TX flow steering */ -static int tx_create(struct mlx5e_priv *priv) +static int tx_create(struct mlx5_core_dev *mdev, struct mlx5e_ipsec_tx *tx) { struct mlx5_flow_table_attr ft_attr = {}; - struct mlx5e_ipsec *ipsec = priv->ipsec; - struct mlx5_core_dev *mdev = priv->mdev; struct mlx5_flow_table *ft; int err; ft_attr.max_fte = NUM_IPSEC_FTE; ft_attr.autogroup.max_num_groups = 1; - ft = mlx5_create_auto_grouped_flow_table(ipsec->tx->ns, &ft_attr); + ft = mlx5_create_auto_grouped_flow_table(tx->ns, &ft_attr); if (IS_ERR(ft)) { err = PTR_ERR(ft); mlx5_core_err(mdev, "fail to create ipsec tx ft err=%d\n", err); return err; } - ipsec->tx->ft.sa = ft; + tx->ft.sa = ft; return 0; } -static struct mlx5e_ipsec_tx *tx_ft_get(struct mlx5e_priv *priv) +static struct mlx5e_ipsec_tx *tx_ft_get(struct mlx5_core_dev *mdev, + struct mlx5e_ipsec *ipsec) { - struct mlx5e_ipsec_tx *tx = priv->ipsec->tx; + struct mlx5e_ipsec_tx *tx = ipsec->tx; int err = 0; mutex_lock(&tx->ft.mutex); if (tx->ft.refcnt) goto skip; - err = tx_create(priv); + err = tx_create(mdev, tx); if (err) goto out; skip: @@ -311,9 +311,9 @@ out: return tx; } -static void tx_ft_put(struct mlx5e_priv *priv) +static void tx_ft_put(struct mlx5e_ipsec *ipsec) { - struct mlx5e_ipsec_tx *tx = priv->ipsec->tx; + struct mlx5e_ipsec_tx *tx = ipsec->tx; mutex_lock(&tx->ft.mutex); tx->ft.refcnt--; @@ -382,13 +382,13 @@ static void setup_fte_common(struct mlx5_accel_esp_xfrm_attrs *attrs, flow_act->flags |= FLOW_ACT_NO_APPEND; } -static int rx_add_rule(struct mlx5e_priv *priv, - struct mlx5e_ipsec_sa_entry *sa_entry) +static int rx_add_rule(struct mlx5e_ipsec_sa_entry *sa_entry) { u8 action[MLX5_UN_SZ_BYTES(set_add_copy_action_in_auto)] = {}; struct mlx5e_ipsec_rule *ipsec_rule = &sa_entry->ipsec_rule; struct mlx5_accel_esp_xfrm_attrs *attrs = &sa_entry->attrs; struct mlx5_core_dev *mdev = mlx5e_ipsec_sa2dev(sa_entry); + struct mlx5e_ipsec *ipsec = sa_entry->ipsec; u32 ipsec_obj_id = sa_entry->ipsec_obj_id; struct mlx5_modify_hdr *modify_hdr = NULL; struct mlx5_flow_destination dest = {}; @@ -398,7 +398,7 @@ static int rx_add_rule(struct mlx5e_priv *priv, struct mlx5e_ipsec_rx *rx; int err = 0; - rx = rx_ft_get(priv, attrs->family); + rx = rx_ft_get(mdev, ipsec, attrs->family); if (IS_ERR(rx)) return PTR_ERR(rx); @@ -418,7 +418,7 @@ static int rx_add_rule(struct mlx5e_priv *priv, MLX5_SET(set_action_in, action, offset, 0); MLX5_SET(set_action_in, action, length, 32); - modify_hdr = mlx5_modify_header_alloc(priv->mdev, MLX5_FLOW_NAMESPACE_KERNEL, + modify_hdr = mlx5_modify_header_alloc(mdev, MLX5_FLOW_NAMESPACE_KERNEL, 1, action); if (IS_ERR(modify_hdr)) { err = PTR_ERR(modify_hdr); @@ -447,25 +447,25 @@ static int rx_add_rule(struct mlx5e_priv *priv, out_err: if (modify_hdr) - mlx5_modify_header_dealloc(priv->mdev, modify_hdr); - rx_ft_put(priv, attrs->family); + mlx5_modify_header_dealloc(mdev, modify_hdr); + rx_ft_put(mdev, ipsec, attrs->family); out: kvfree(spec); return err; } -static int tx_add_rule(struct mlx5e_priv *priv, - struct mlx5e_ipsec_sa_entry *sa_entry) +static int tx_add_rule(struct mlx5e_ipsec_sa_entry *sa_entry) { struct mlx5_core_dev *mdev = mlx5e_ipsec_sa2dev(sa_entry); + struct mlx5e_ipsec *ipsec = sa_entry->ipsec; struct mlx5_flow_act flow_act = {}; struct mlx5_flow_handle *rule; struct mlx5_flow_spec *spec; struct mlx5e_ipsec_tx *tx; int err = 0; - tx = tx_ft_get(priv); + tx = tx_ft_get(mdev, ipsec); if (IS_ERR(tx)) return PTR_ERR(tx); @@ -499,21 +499,19 @@ static int tx_add_rule(struct mlx5e_priv *priv, out: kvfree(spec); if (err) - tx_ft_put(priv); + tx_ft_put(ipsec); return err; } -int mlx5e_accel_ipsec_fs_add_rule(struct mlx5e_priv *priv, - struct mlx5e_ipsec_sa_entry *sa_entry) +int mlx5e_accel_ipsec_fs_add_rule(struct mlx5e_ipsec_sa_entry *sa_entry) { if (sa_entry->attrs.dir == XFRM_DEV_OFFLOAD_OUT) - return tx_add_rule(priv, sa_entry); + return tx_add_rule(sa_entry); - return rx_add_rule(priv, sa_entry); + return rx_add_rule(sa_entry); } -void mlx5e_accel_ipsec_fs_del_rule(struct mlx5e_priv *priv, - struct mlx5e_ipsec_sa_entry *sa_entry) +void mlx5e_accel_ipsec_fs_del_rule(struct mlx5e_ipsec_sa_entry *sa_entry) { struct mlx5e_ipsec_rule *ipsec_rule = &sa_entry->ipsec_rule; struct mlx5_core_dev *mdev = mlx5e_ipsec_sa2dev(sa_entry); @@ -521,12 +519,12 @@ void mlx5e_accel_ipsec_fs_del_rule(struct mlx5e_priv *priv, mlx5_del_flow_rules(ipsec_rule->rule); if (sa_entry->attrs.dir == XFRM_DEV_OFFLOAD_OUT) { - tx_ft_put(priv); + tx_ft_put(sa_entry->ipsec); return; } mlx5_modify_header_dealloc(mdev, ipsec_rule->set_modify_hdr); - rx_ft_put(priv, sa_entry->attrs.family); + rx_ft_put(mdev, sa_entry->ipsec, sa_entry->attrs.family); } void mlx5e_accel_ipsec_fs_cleanup(struct mlx5e_ipsec *ipsec) From 8518d05b8f9add527041edd2f12ffc841866b01a Mon Sep 17 00:00:00 2001 From: Leon Romanovsky Date: Fri, 2 Dec 2022 22:10:30 +0200 Subject: [PATCH 1611/2002] net/mlx5e: Create Advanced Steering Operation object for IPsec Setup the ASO (Advanced Steering Operation) object that is needed for IPsec to interact with SW stack about various fast changing events: replay window, lifetime limits, e.t.c Reviewed-by: Raed Salem Signed-off-by: Leon Romanovsky Signed-off-by: Steffen Klassert --- drivers/net/ethernet/mellanox/mlx5/core/en.h | 1 + .../mellanox/mlx5/core/en_accel/ipsec.c | 12 +++++ .../mellanox/mlx5/core/en_accel/ipsec.h | 13 +++++ .../mlx5/core/en_accel/ipsec_offload.c | 54 +++++++++++++++++++ 4 files changed, 80 insertions(+) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en.h b/drivers/net/ethernet/mellanox/mlx5/core/en.h index 65790ff58a74..2d77fb8a8a01 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en.h @@ -1245,4 +1245,5 @@ int mlx5e_set_vf_rate(struct net_device *dev, int vf, int min_tx_rate, int max_t int mlx5e_get_vf_config(struct net_device *dev, int vf, struct ifla_vf_info *ivi); int mlx5e_get_vf_stats(struct net_device *dev, int vf, struct ifla_vf_stats *vf_stats); #endif +int mlx5e_create_mkey(struct mlx5_core_dev *mdev, u32 pdn, u32 *mkey); #endif /* __MLX5_EN_H__ */ diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.c b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.c index f518322c1ac1..d2c814e7af97 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.c @@ -373,6 +373,13 @@ void mlx5e_ipsec_init(struct mlx5e_priv *priv) if (!ipsec->wq) goto err_wq; + if (mlx5_ipsec_device_caps(priv->mdev) & + MLX5_IPSEC_CAP_PACKET_OFFLOAD) { + ret = mlx5e_ipsec_aso_init(ipsec); + if (ret) + goto err_aso; + } + ret = mlx5e_accel_ipsec_fs_init(ipsec); if (ret) goto err_fs_init; @@ -383,6 +390,9 @@ void mlx5e_ipsec_init(struct mlx5e_priv *priv) return; err_fs_init: + if (mlx5_ipsec_device_caps(priv->mdev) & MLX5_IPSEC_CAP_PACKET_OFFLOAD) + mlx5e_ipsec_aso_cleanup(ipsec); +err_aso: destroy_workqueue(ipsec->wq); err_wq: kfree(ipsec); @@ -398,6 +408,8 @@ void mlx5e_ipsec_cleanup(struct mlx5e_priv *priv) return; mlx5e_accel_ipsec_fs_cleanup(ipsec); + if (mlx5_ipsec_device_caps(priv->mdev) & MLX5_IPSEC_CAP_PACKET_OFFLOAD) + mlx5e_ipsec_aso_cleanup(ipsec); destroy_workqueue(ipsec->wq); kfree(ipsec); priv->ipsec = NULL; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.h b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.h index db0ccf2a797a..8e2f88f269ac 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.h @@ -39,6 +39,7 @@ #include #include #include +#include "lib/aso.h" #define MLX5E_IPSEC_SADB_RX_BITS 10 #define MLX5E_IPSEC_ESN_SCOPE_MID 0x80000000L @@ -97,6 +98,14 @@ struct mlx5e_ipsec_sw_stats { struct mlx5e_ipsec_rx; struct mlx5e_ipsec_tx; +struct mlx5e_ipsec_aso { + u8 ctx[MLX5_ST_SZ_BYTES(ipsec_aso)]; + dma_addr_t dma_addr; + struct mlx5_aso *aso; + u32 pdn; + u32 mkey; +}; + struct mlx5e_ipsec { struct mlx5_core_dev *mdev; DECLARE_HASHTABLE(sadb_rx, MLX5E_IPSEC_SADB_RX_BITS); @@ -107,6 +116,7 @@ struct mlx5e_ipsec { struct mlx5e_ipsec_rx *rx_ipv4; struct mlx5e_ipsec_rx *rx_ipv6; struct mlx5e_ipsec_tx *tx; + struct mlx5e_ipsec_aso *aso; }; struct mlx5e_ipsec_esn_state { @@ -160,6 +170,9 @@ u32 mlx5_ipsec_device_caps(struct mlx5_core_dev *mdev); void mlx5_accel_esp_modify_xfrm(struct mlx5e_ipsec_sa_entry *sa_entry, const struct mlx5_accel_esp_xfrm_attrs *attrs); +int mlx5e_ipsec_aso_init(struct mlx5e_ipsec *ipsec); +void mlx5e_ipsec_aso_cleanup(struct mlx5e_ipsec *ipsec); + static inline struct mlx5_core_dev * mlx5e_ipsec_sa2dev(struct mlx5e_ipsec_sa_entry *sa_entry) { diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_offload.c b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_offload.c index 3f2aeb07ea84..7fef5de55229 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_offload.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_offload.c @@ -2,6 +2,7 @@ /* Copyright (c) 2017, Mellanox Technologies inc. All rights reserved. */ #include "mlx5_core.h" +#include "en.h" #include "ipsec.h" #include "lib/mlx5.h" @@ -207,3 +208,56 @@ void mlx5_accel_esp_modify_xfrm(struct mlx5e_ipsec_sa_entry *sa_entry, memcpy(&sa_entry->attrs, attrs, sizeof(sa_entry->attrs)); } + +int mlx5e_ipsec_aso_init(struct mlx5e_ipsec *ipsec) +{ + struct mlx5_core_dev *mdev = ipsec->mdev; + struct mlx5e_ipsec_aso *aso; + struct mlx5e_hw_objs *res; + struct device *pdev; + int err; + + aso = kzalloc(sizeof(*ipsec->aso), GFP_KERNEL); + if (!aso) + return -ENOMEM; + + res = &mdev->mlx5e_res.hw_objs; + + pdev = mlx5_core_dma_dev(mdev); + aso->dma_addr = dma_map_single(pdev, aso->ctx, sizeof(aso->ctx), + DMA_BIDIRECTIONAL); + err = dma_mapping_error(pdev, aso->dma_addr); + if (err) + goto err_dma; + + aso->aso = mlx5_aso_create(mdev, res->pdn); + if (IS_ERR(aso->aso)) { + err = PTR_ERR(aso->aso); + goto err_aso_create; + } + + ipsec->aso = aso; + return 0; + +err_aso_create: + dma_unmap_single(pdev, aso->dma_addr, sizeof(aso->ctx), + DMA_BIDIRECTIONAL); +err_dma: + kfree(aso); + return err; +} + +void mlx5e_ipsec_aso_cleanup(struct mlx5e_ipsec *ipsec) +{ + struct mlx5_core_dev *mdev = ipsec->mdev; + struct mlx5e_ipsec_aso *aso; + struct device *pdev; + + aso = ipsec->aso; + pdev = mlx5_core_dma_dev(mdev); + + mlx5_aso_destroy(aso->aso); + dma_unmap_single(pdev, aso->dma_addr, sizeof(aso->ctx), + DMA_BIDIRECTIONAL); + kfree(aso); +} From 8d15f364d5d39956fd6bb848ad7732e077616c9a Mon Sep 17 00:00:00 2001 From: Leon Romanovsky Date: Fri, 2 Dec 2022 22:10:31 +0200 Subject: [PATCH 1612/2002] net/mlx5e: Create hardware IPsec packet offload objects Create initial hardware IPsec packet offload object and connect it to advanced steering operation (ASO) context and queue, so the data path can communicate with the stack. Reviewed-by: Raed Salem Reviewed-by: Saeed Mahameed Signed-off-by: Leon Romanovsky Signed-off-by: Steffen Klassert --- .../mellanox/mlx5/core/en_accel/ipsec.c | 1 + .../mellanox/mlx5/core/en_accel/ipsec.h | 3 +- .../mlx5/core/en_accel/ipsec_offload.c | 37 +++++++++++++++++++ 3 files changed, 39 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.c b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.c index d2c814e7af97..c5bccc0df60d 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.c @@ -176,6 +176,7 @@ mlx5e_ipsec_build_accel_xfrm_attrs(struct mlx5e_ipsec_sa_entry *sa_entry, memcpy(&attrs->saddr, x->props.saddr.a6, sizeof(attrs->saddr)); memcpy(&attrs->daddr, x->id.daddr.a6, sizeof(attrs->daddr)); attrs->family = x->props.family; + attrs->type = x->xso.type; } static inline int mlx5e_xfrm_validate_state(struct xfrm_state *x) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.h b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.h index 8e2f88f269ac..2c9aedf6b0ef 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.h @@ -73,6 +73,7 @@ struct mlx5_accel_esp_xfrm_attrs { u8 dir : 2; u8 esn_overlap : 1; u8 esn_trigger : 1; + u8 type : 2; u8 family; u32 replay_window; }; @@ -102,8 +103,6 @@ struct mlx5e_ipsec_aso { u8 ctx[MLX5_ST_SZ_BYTES(ipsec_aso)]; dma_addr_t dma_addr; struct mlx5_aso *aso; - u32 pdn; - u32 mkey; }; struct mlx5e_ipsec { diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_offload.c b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_offload.c index 7fef5de55229..fc88454aaf8d 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_offload.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_offload.c @@ -53,6 +53,38 @@ u32 mlx5_ipsec_device_caps(struct mlx5_core_dev *mdev) } EXPORT_SYMBOL_GPL(mlx5_ipsec_device_caps); +static void mlx5e_ipsec_packet_setup(void *obj, u32 pdn, + struct mlx5_accel_esp_xfrm_attrs *attrs) +{ + void *aso_ctx; + + aso_ctx = MLX5_ADDR_OF(ipsec_obj, obj, ipsec_aso); + if (attrs->esn_trigger) { + MLX5_SET(ipsec_aso, aso_ctx, esn_event_arm, 1); + + if (attrs->dir == XFRM_DEV_OFFLOAD_IN) { + MLX5_SET(ipsec_aso, aso_ctx, window_sz, + attrs->replay_window / 64); + MLX5_SET(ipsec_aso, aso_ctx, mode, + MLX5_IPSEC_ASO_REPLAY_PROTECTION); + } + } + + /* ASO context */ + MLX5_SET(ipsec_obj, obj, ipsec_aso_access_pd, pdn); + MLX5_SET(ipsec_obj, obj, full_offload, 1); + MLX5_SET(ipsec_aso, aso_ctx, valid, 1); + /* MLX5_IPSEC_ASO_REG_C_4_5 is type C register that is used + * in flow steering to perform matching against. Please be + * aware that this register was chosen arbitrary and can't + * be used in other places as long as IPsec packet offload + * active. + */ + MLX5_SET(ipsec_obj, obj, aso_return_reg, MLX5_IPSEC_ASO_REG_C_4_5); + if (attrs->dir == XFRM_DEV_OFFLOAD_OUT) + MLX5_SET(ipsec_aso, aso_ctx, mode, MLX5_IPSEC_ASO_INC_SN); +} + static int mlx5_create_ipsec_obj(struct mlx5e_ipsec_sa_entry *sa_entry) { struct mlx5_accel_esp_xfrm_attrs *attrs = &sa_entry->attrs; @@ -61,6 +93,7 @@ static int mlx5_create_ipsec_obj(struct mlx5e_ipsec_sa_entry *sa_entry) u32 out[MLX5_ST_SZ_DW(general_obj_out_cmd_hdr)]; u32 in[MLX5_ST_SZ_DW(create_ipsec_obj_in)] = {}; void *obj, *salt_p, *salt_iv_p; + struct mlx5e_hw_objs *res; int err; obj = MLX5_ADDR_OF(create_ipsec_obj_in, in, ipsec_object); @@ -87,6 +120,10 @@ static int mlx5_create_ipsec_obj(struct mlx5e_ipsec_sa_entry *sa_entry) MLX5_SET(general_obj_in_cmd_hdr, in, obj_type, MLX5_GENERAL_OBJECT_TYPES_IPSEC); + res = &mdev->mlx5e_res.hw_objs; + if (attrs->type == XFRM_DEV_OFFLOAD_PACKET) + mlx5e_ipsec_packet_setup(obj, res->pdn, attrs); + err = mlx5_cmd_exec(mdev, in, sizeof(in), out, sizeof(out)); if (!err) sa_entry->ipsec_obj_id = From 42ba0f9d4b3972bd9ca9c86f89d5ccc8dc5c58b6 Mon Sep 17 00:00:00 2001 From: Leon Romanovsky Date: Fri, 2 Dec 2022 22:10:32 +0200 Subject: [PATCH 1613/2002] net/mlx5e: Move IPsec flow table creation to separate function Even now, to support IPsec crypto, the RX and TX paths use same logic to create flow tables. In the following patches, we will add more tables to support IPsec packet offload. So reuse existing code and rewrite it to support IPsec packet offload from the beginning. Reviewed-by: Raed Salem Reviewed-by: Saeed Mahameed Signed-off-by: Leon Romanovsky Signed-off-by: Steffen Klassert --- .../mellanox/mlx5/core/en_accel/ipsec_fs.c | 45 ++++++++++--------- 1 file changed, 23 insertions(+), 22 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_fs.c b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_fs.c index 8e87d8d02511..f65a74e3d648 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_fs.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_fs.c @@ -42,6 +42,21 @@ static enum mlx5_traffic_types family2tt(u32 family) return MLX5_TT_IPV6_IPSEC_ESP; } +static struct mlx5_flow_table *ipsec_ft_create(struct mlx5_flow_namespace *ns, + int level, int prio, + int max_num_groups) +{ + struct mlx5_flow_table_attr ft_attr = {}; + + ft_attr.autogroup.num_reserved_entries = 1; + ft_attr.autogroup.max_num_groups = max_num_groups; + ft_attr.max_fte = NUM_IPSEC_FTE; + ft_attr.level = level; + ft_attr.prio = prio; + + return mlx5_create_auto_grouped_flow_table(ns, &ft_attr); +} + static int rx_err_add_rule(struct mlx5_core_dev *mdev, struct mlx5e_ipsec_rx *rx, struct mlx5e_ipsec_rx_err *rx_err) @@ -160,17 +175,13 @@ static int rx_create(struct mlx5_core_dev *mdev, struct mlx5e_ipsec *ipsec, { struct mlx5_flow_namespace *ns = mlx5e_fs_get_ns(ipsec->fs, false); struct mlx5_ttc_table *ttc = mlx5e_fs_get_ttc(ipsec->fs, false); - struct mlx5_flow_table_attr ft_attr = {}; struct mlx5_flow_table *ft; int err; rx->default_dest = mlx5_ttc_get_default_dest(ttc, family2tt(family)); - ft_attr.max_fte = 1; - ft_attr.autogroup.max_num_groups = 1; - ft_attr.level = MLX5E_ACCEL_FS_ESP_FT_ERR_LEVEL; - ft_attr.prio = MLX5E_NIC_PRIO; - ft = mlx5_create_auto_grouped_flow_table(ns, &ft_attr); + ft = ipsec_ft_create(ns, MLX5E_ACCEL_FS_ESP_FT_ERR_LEVEL, + MLX5E_NIC_PRIO, 1); if (IS_ERR(ft)) return PTR_ERR(ft); @@ -180,12 +191,8 @@ static int rx_create(struct mlx5_core_dev *mdev, struct mlx5e_ipsec *ipsec, goto err_add; /* Create FT */ - ft_attr.max_fte = NUM_IPSEC_FTE; - ft_attr.level = MLX5E_ACCEL_FS_ESP_FT_LEVEL; - ft_attr.prio = MLX5E_NIC_PRIO; - ft_attr.autogroup.num_reserved_entries = 1; - ft_attr.autogroup.max_num_groups = 1; - ft = mlx5_create_auto_grouped_flow_table(ns, &ft_attr); + ft = ipsec_ft_create(ns, MLX5E_ACCEL_FS_ESP_FT_LEVEL, MLX5E_NIC_PRIO, + 1); if (IS_ERR(ft)) { err = PTR_ERR(ft); goto err_fs_ft; @@ -273,18 +280,12 @@ out: /* IPsec TX flow steering */ static int tx_create(struct mlx5_core_dev *mdev, struct mlx5e_ipsec_tx *tx) { - struct mlx5_flow_table_attr ft_attr = {}; struct mlx5_flow_table *ft; - int err; - ft_attr.max_fte = NUM_IPSEC_FTE; - ft_attr.autogroup.max_num_groups = 1; - ft = mlx5_create_auto_grouped_flow_table(tx->ns, &ft_attr); - if (IS_ERR(ft)) { - err = PTR_ERR(ft); - mlx5_core_err(mdev, "fail to create ipsec tx ft err=%d\n", err); - return err; - } + ft = ipsec_ft_create(tx->ns, 0, 0, 1); + if (IS_ERR(ft)) + return PTR_ERR(ft); + tx->ft.sa = ft; return 0; } From 35324bbb962e984fe58e783ccad20bcfccc65cba Mon Sep 17 00:00:00 2001 From: Leon Romanovsky Date: Fri, 2 Dec 2022 22:10:33 +0200 Subject: [PATCH 1614/2002] net/mlx5e: Refactor FTE setup code to be more clear The policy offload logic needs to set flow steering rule that match on saddr and daddr too, so factor out this code to separate functions, together with code alignment to netdev coding pattern of relying on family type. As part of this change, let's separate more logic from setup_fte_common to make sure that the function names describe that is done in the function better than general *common* name. Reviewed-by: Raed Salem Reviewed-by: Saeed Mahameed Signed-off-by: Leon Romanovsky Signed-off-by: Steffen Klassert --- .../mellanox/mlx5/core/en_accel/ipsec_fs.c | 139 +++++++++++------- 1 file changed, 85 insertions(+), 54 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_fs.c b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_fs.c index f65a74e3d648..4c5904544bda 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_fs.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_fs.c @@ -326,61 +326,78 @@ out: mutex_unlock(&tx->ft.mutex); } -static void setup_fte_common(struct mlx5_accel_esp_xfrm_attrs *attrs, - u32 ipsec_obj_id, - struct mlx5_flow_spec *spec, - struct mlx5_flow_act *flow_act) +static void setup_fte_addr4(struct mlx5_flow_spec *spec, __be32 *saddr, + __be32 *daddr) { - u8 ip_version = (attrs->family == AF_INET) ? 4 : 6; + spec->match_criteria_enable |= MLX5_MATCH_OUTER_HEADERS; - spec->match_criteria_enable = MLX5_MATCH_OUTER_HEADERS | MLX5_MATCH_MISC_PARAMETERS; - - /* ip_version */ MLX5_SET_TO_ONES(fte_match_param, spec->match_criteria, outer_headers.ip_version); - MLX5_SET(fte_match_param, spec->match_value, outer_headers.ip_version, ip_version); + MLX5_SET(fte_match_param, spec->match_value, outer_headers.ip_version, 4); - /* Non fragmented */ - MLX5_SET_TO_ONES(fte_match_param, spec->match_criteria, outer_headers.frag); - MLX5_SET(fte_match_param, spec->match_value, outer_headers.frag, 0); + memcpy(MLX5_ADDR_OF(fte_match_param, spec->match_value, + outer_headers.src_ipv4_src_ipv6.ipv4_layout.ipv4), saddr, 4); + memcpy(MLX5_ADDR_OF(fte_match_param, spec->match_value, + outer_headers.dst_ipv4_dst_ipv6.ipv4_layout.ipv4), daddr, 4); + MLX5_SET_TO_ONES(fte_match_param, spec->match_criteria, + outer_headers.src_ipv4_src_ipv6.ipv4_layout.ipv4); + MLX5_SET_TO_ONES(fte_match_param, spec->match_criteria, + outer_headers.dst_ipv4_dst_ipv6.ipv4_layout.ipv4); +} +static void setup_fte_addr6(struct mlx5_flow_spec *spec, __be32 *saddr, + __be32 *daddr) +{ + spec->match_criteria_enable |= MLX5_MATCH_OUTER_HEADERS; + + MLX5_SET_TO_ONES(fte_match_param, spec->match_criteria, outer_headers.ip_version); + MLX5_SET(fte_match_param, spec->match_value, outer_headers.ip_version, 6); + + memcpy(MLX5_ADDR_OF(fte_match_param, spec->match_value, + outer_headers.src_ipv4_src_ipv6.ipv6_layout.ipv6), saddr, 16); + memcpy(MLX5_ADDR_OF(fte_match_param, spec->match_value, + outer_headers.dst_ipv4_dst_ipv6.ipv6_layout.ipv6), daddr, 16); + memset(MLX5_ADDR_OF(fte_match_param, spec->match_criteria, + outer_headers.src_ipv4_src_ipv6.ipv6_layout.ipv6), 0xff, 16); + memset(MLX5_ADDR_OF(fte_match_param, spec->match_criteria, + outer_headers.dst_ipv4_dst_ipv6.ipv6_layout.ipv6), 0xff, 16); +} + +static void setup_fte_esp(struct mlx5_flow_spec *spec) +{ /* ESP header */ + spec->match_criteria_enable |= MLX5_MATCH_MISC_PARAMETERS; + MLX5_SET_TO_ONES(fte_match_param, spec->match_criteria, outer_headers.ip_protocol); MLX5_SET(fte_match_param, spec->match_value, outer_headers.ip_protocol, IPPROTO_ESP); +} +static void setup_fte_spi(struct mlx5_flow_spec *spec, u32 spi) +{ /* SPI number */ + spec->match_criteria_enable |= MLX5_MATCH_MISC_PARAMETERS; + MLX5_SET_TO_ONES(fte_match_param, spec->match_criteria, misc_parameters.outer_esp_spi); + MLX5_SET(fte_match_param, spec->match_value, misc_parameters.outer_esp_spi, spi); +} + +static void setup_fte_no_frags(struct mlx5_flow_spec *spec) +{ + /* Non fragmented */ + spec->match_criteria_enable |= MLX5_MATCH_OUTER_HEADERS; + + MLX5_SET_TO_ONES(fte_match_param, spec->match_criteria, outer_headers.frag); + MLX5_SET(fte_match_param, spec->match_value, outer_headers.frag, 0); +} + +static void setup_fte_reg_a(struct mlx5_flow_spec *spec) +{ + /* Add IPsec indicator in metadata_reg_a */ + spec->match_criteria_enable |= MLX5_MATCH_MISC_PARAMETERS_2; + + MLX5_SET(fte_match_param, spec->match_criteria, + misc_parameters_2.metadata_reg_a, MLX5_ETH_WQE_FT_META_IPSEC); MLX5_SET(fte_match_param, spec->match_value, - misc_parameters.outer_esp_spi, attrs->spi); - - if (ip_version == 4) { - memcpy(MLX5_ADDR_OF(fte_match_param, spec->match_value, - outer_headers.src_ipv4_src_ipv6.ipv4_layout.ipv4), - &attrs->saddr.a4, 4); - memcpy(MLX5_ADDR_OF(fte_match_param, spec->match_value, - outer_headers.dst_ipv4_dst_ipv6.ipv4_layout.ipv4), - &attrs->daddr.a4, 4); - MLX5_SET_TO_ONES(fte_match_param, spec->match_criteria, - outer_headers.src_ipv4_src_ipv6.ipv4_layout.ipv4); - MLX5_SET_TO_ONES(fte_match_param, spec->match_criteria, - outer_headers.dst_ipv4_dst_ipv6.ipv4_layout.ipv4); - } else { - memcpy(MLX5_ADDR_OF(fte_match_param, spec->match_value, - outer_headers.src_ipv4_src_ipv6.ipv6_layout.ipv6), - &attrs->saddr.a6, 16); - memcpy(MLX5_ADDR_OF(fte_match_param, spec->match_value, - outer_headers.dst_ipv4_dst_ipv6.ipv6_layout.ipv6), - &attrs->daddr.a6, 16); - memset(MLX5_ADDR_OF(fte_match_param, spec->match_criteria, - outer_headers.src_ipv4_src_ipv6.ipv6_layout.ipv6), - 0xff, 16); - memset(MLX5_ADDR_OF(fte_match_param, spec->match_criteria, - outer_headers.dst_ipv4_dst_ipv6.ipv6_layout.ipv6), - 0xff, 16); - } - - flow_act->crypto.type = MLX5_FLOW_CONTEXT_ENCRYPT_DECRYPT_TYPE_IPSEC; - flow_act->crypto.obj_id = ipsec_obj_id; - flow_act->flags |= FLOW_ACT_NO_APPEND; + misc_parameters_2.metadata_reg_a, MLX5_ETH_WQE_FT_META_IPSEC); } static int rx_add_rule(struct mlx5e_ipsec_sa_entry *sa_entry) @@ -390,7 +407,6 @@ static int rx_add_rule(struct mlx5e_ipsec_sa_entry *sa_entry) struct mlx5_accel_esp_xfrm_attrs *attrs = &sa_entry->attrs; struct mlx5_core_dev *mdev = mlx5e_ipsec_sa2dev(sa_entry); struct mlx5e_ipsec *ipsec = sa_entry->ipsec; - u32 ipsec_obj_id = sa_entry->ipsec_obj_id; struct mlx5_modify_hdr *modify_hdr = NULL; struct mlx5_flow_destination dest = {}; struct mlx5_flow_act flow_act = {}; @@ -409,13 +425,21 @@ static int rx_add_rule(struct mlx5e_ipsec_sa_entry *sa_entry) goto out_err; } - setup_fte_common(attrs, ipsec_obj_id, spec, &flow_act); + if (attrs->family == AF_INET) + setup_fte_addr4(spec, &attrs->saddr.a4, &attrs->daddr.a4); + else + setup_fte_addr6(spec, attrs->saddr.a6, attrs->daddr.a6); + + setup_fte_spi(spec, attrs->spi); + setup_fte_esp(spec); + setup_fte_no_frags(spec); /* Set bit[31] ipsec marker */ /* Set bit[23-0] ipsec_obj_id */ MLX5_SET(set_action_in, action, action_type, MLX5_ACTION_TYPE_SET); MLX5_SET(set_action_in, action, field, MLX5_ACTION_IN_FIELD_METADATA_REG_B); - MLX5_SET(set_action_in, action, data, (ipsec_obj_id | BIT(31))); + MLX5_SET(set_action_in, action, data, + (sa_entry->ipsec_obj_id | BIT(31))); MLX5_SET(set_action_in, action, offset, 0); MLX5_SET(set_action_in, action, length, 32); @@ -429,6 +453,9 @@ static int rx_add_rule(struct mlx5e_ipsec_sa_entry *sa_entry) goto out_err; } + flow_act.crypto.type = MLX5_FLOW_CONTEXT_ENCRYPT_DECRYPT_TYPE_IPSEC; + flow_act.crypto.obj_id = sa_entry->ipsec_obj_id; + flow_act.flags |= FLOW_ACT_NO_APPEND; flow_act.action = MLX5_FLOW_CONTEXT_ACTION_FWD_DEST | MLX5_FLOW_CONTEXT_ACTION_CRYPTO_DECRYPT | MLX5_FLOW_CONTEXT_ACTION_MOD_HDR; @@ -458,6 +485,7 @@ out: static int tx_add_rule(struct mlx5e_ipsec_sa_entry *sa_entry) { + struct mlx5_accel_esp_xfrm_attrs *attrs = &sa_entry->attrs; struct mlx5_core_dev *mdev = mlx5e_ipsec_sa2dev(sa_entry); struct mlx5e_ipsec *ipsec = sa_entry->ipsec; struct mlx5_flow_act flow_act = {}; @@ -476,16 +504,19 @@ static int tx_add_rule(struct mlx5e_ipsec_sa_entry *sa_entry) goto out; } - setup_fte_common(&sa_entry->attrs, sa_entry->ipsec_obj_id, spec, - &flow_act); + if (attrs->family == AF_INET) + setup_fte_addr4(spec, &attrs->saddr.a4, &attrs->daddr.a4); + else + setup_fte_addr6(spec, attrs->saddr.a6, attrs->daddr.a6); - /* Add IPsec indicator in metadata_reg_a */ - spec->match_criteria_enable |= MLX5_MATCH_MISC_PARAMETERS_2; - MLX5_SET(fte_match_param, spec->match_criteria, misc_parameters_2.metadata_reg_a, - MLX5_ETH_WQE_FT_META_IPSEC); - MLX5_SET(fte_match_param, spec->match_value, misc_parameters_2.metadata_reg_a, - MLX5_ETH_WQE_FT_META_IPSEC); + setup_fte_spi(spec, attrs->spi); + setup_fte_esp(spec); + setup_fte_no_frags(spec); + setup_fte_reg_a(spec); + flow_act.crypto.type = MLX5_FLOW_CONTEXT_ENCRYPT_DECRYPT_TYPE_IPSEC; + flow_act.crypto.obj_id = sa_entry->ipsec_obj_id; + flow_act.flags |= FLOW_ACT_NO_APPEND; flow_act.action = MLX5_FLOW_CONTEXT_ACTION_ALLOW | MLX5_FLOW_CONTEXT_ACTION_CRYPTO_ENCRYPT; rule = mlx5_add_flow_rules(tx->ft.sa, spec, &flow_act, NULL, 0); From 384298c28aac42fcc93e3ff83fe627c900821804 Mon Sep 17 00:00:00 2001 From: Leon Romanovsky Date: Fri, 2 Dec 2022 22:10:34 +0200 Subject: [PATCH 1615/2002] net/mlx5e: Flatten the IPsec RX add rule path Rewrote the IPsec RX add rule path to be less convoluted and don't rely on pre-initialized variables. The code now has clean linear flow with clean separation between error and success paths. Reviewed-by: Raed Salem Reviewed-by: Saeed Mahameed Signed-off-by: Leon Romanovsky Signed-off-by: Steffen Klassert --- .../mellanox/mlx5/core/en_accel/ipsec.h | 2 +- .../mellanox/mlx5/core/en_accel/ipsec_fs.c | 88 +++++++++++-------- 2 files changed, 53 insertions(+), 37 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.h b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.h index 2c9aedf6b0ef..990378d52fd4 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.h @@ -126,7 +126,7 @@ struct mlx5e_ipsec_esn_state { struct mlx5e_ipsec_rule { struct mlx5_flow_handle *rule; - struct mlx5_modify_hdr *set_modify_hdr; + struct mlx5_modify_hdr *modify_hdr; }; struct mlx5e_ipsec_modify_state_work { diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_fs.c b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_fs.c index 4c5904544bda..b81046c71e6c 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_fs.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_fs.c @@ -400,20 +400,52 @@ static void setup_fte_reg_a(struct mlx5_flow_spec *spec) misc_parameters_2.metadata_reg_a, MLX5_ETH_WQE_FT_META_IPSEC); } -static int rx_add_rule(struct mlx5e_ipsec_sa_entry *sa_entry) +static int setup_modify_header(struct mlx5_core_dev *mdev, u32 val, u8 dir, + struct mlx5_flow_act *flow_act) { u8 action[MLX5_UN_SZ_BYTES(set_add_copy_action_in_auto)] = {}; + enum mlx5_flow_namespace_type ns_type; + struct mlx5_modify_hdr *modify_hdr; + + MLX5_SET(set_action_in, action, action_type, MLX5_ACTION_TYPE_SET); + switch (dir) { + case XFRM_DEV_OFFLOAD_IN: + MLX5_SET(set_action_in, action, field, + MLX5_ACTION_IN_FIELD_METADATA_REG_B); + ns_type = MLX5_FLOW_NAMESPACE_KERNEL; + break; + default: + return -EINVAL; + } + + MLX5_SET(set_action_in, action, data, val); + MLX5_SET(set_action_in, action, offset, 0); + MLX5_SET(set_action_in, action, length, 32); + + modify_hdr = mlx5_modify_header_alloc(mdev, ns_type, 1, action); + if (IS_ERR(modify_hdr)) { + mlx5_core_err(mdev, "Failed to allocate modify_header %ld\n", + PTR_ERR(modify_hdr)); + return PTR_ERR(modify_hdr); + } + + flow_act->modify_hdr = modify_hdr; + flow_act->action |= MLX5_FLOW_CONTEXT_ACTION_MOD_HDR; + return 0; +} + +static int rx_add_rule(struct mlx5e_ipsec_sa_entry *sa_entry) +{ struct mlx5e_ipsec_rule *ipsec_rule = &sa_entry->ipsec_rule; struct mlx5_accel_esp_xfrm_attrs *attrs = &sa_entry->attrs; struct mlx5_core_dev *mdev = mlx5e_ipsec_sa2dev(sa_entry); struct mlx5e_ipsec *ipsec = sa_entry->ipsec; - struct mlx5_modify_hdr *modify_hdr = NULL; struct mlx5_flow_destination dest = {}; struct mlx5_flow_act flow_act = {}; struct mlx5_flow_handle *rule; struct mlx5_flow_spec *spec; struct mlx5e_ipsec_rx *rx; - int err = 0; + int err; rx = rx_ft_get(mdev, ipsec, attrs->family); if (IS_ERR(rx)) @@ -422,7 +454,7 @@ static int rx_add_rule(struct mlx5e_ipsec_sa_entry *sa_entry) spec = kvzalloc(sizeof(*spec), GFP_KERNEL); if (!spec) { err = -ENOMEM; - goto out_err; + goto err_alloc; } if (attrs->family == AF_INET) @@ -434,52 +466,36 @@ static int rx_add_rule(struct mlx5e_ipsec_sa_entry *sa_entry) setup_fte_esp(spec); setup_fte_no_frags(spec); - /* Set bit[31] ipsec marker */ - /* Set bit[23-0] ipsec_obj_id */ - MLX5_SET(set_action_in, action, action_type, MLX5_ACTION_TYPE_SET); - MLX5_SET(set_action_in, action, field, MLX5_ACTION_IN_FIELD_METADATA_REG_B); - MLX5_SET(set_action_in, action, data, - (sa_entry->ipsec_obj_id | BIT(31))); - MLX5_SET(set_action_in, action, offset, 0); - MLX5_SET(set_action_in, action, length, 32); - - modify_hdr = mlx5_modify_header_alloc(mdev, MLX5_FLOW_NAMESPACE_KERNEL, - 1, action); - if (IS_ERR(modify_hdr)) { - err = PTR_ERR(modify_hdr); - mlx5_core_err(mdev, - "fail to alloc ipsec set modify_header_id err=%d\n", err); - modify_hdr = NULL; - goto out_err; - } + err = setup_modify_header(mdev, sa_entry->ipsec_obj_id | BIT(31), + XFRM_DEV_OFFLOAD_IN, &flow_act); + if (err) + goto err_mod_header; flow_act.crypto.type = MLX5_FLOW_CONTEXT_ENCRYPT_DECRYPT_TYPE_IPSEC; flow_act.crypto.obj_id = sa_entry->ipsec_obj_id; flow_act.flags |= FLOW_ACT_NO_APPEND; - flow_act.action = MLX5_FLOW_CONTEXT_ACTION_FWD_DEST | - MLX5_FLOW_CONTEXT_ACTION_CRYPTO_DECRYPT | - MLX5_FLOW_CONTEXT_ACTION_MOD_HDR; + flow_act.action |= MLX5_FLOW_CONTEXT_ACTION_FWD_DEST | + MLX5_FLOW_CONTEXT_ACTION_CRYPTO_DECRYPT; dest.type = MLX5_FLOW_DESTINATION_TYPE_FLOW_TABLE; - flow_act.modify_hdr = modify_hdr; dest.ft = rx->rx_err.ft; rule = mlx5_add_flow_rules(rx->ft.sa, spec, &flow_act, &dest, 1); if (IS_ERR(rule)) { err = PTR_ERR(rule); mlx5_core_err(mdev, "fail to add RX ipsec rule err=%d\n", err); - goto out_err; + goto err_add_flow; } + kvfree(spec); ipsec_rule->rule = rule; - ipsec_rule->set_modify_hdr = modify_hdr; - goto out; + ipsec_rule->modify_hdr = flow_act.modify_hdr; + return 0; -out_err: - if (modify_hdr) - mlx5_modify_header_dealloc(mdev, modify_hdr); - rx_ft_put(mdev, ipsec, attrs->family); - -out: +err_add_flow: + mlx5_modify_header_dealloc(mdev, flow_act.modify_hdr); +err_mod_header: kvfree(spec); +err_alloc: + rx_ft_put(mdev, ipsec, attrs->family); return err; } @@ -555,7 +571,7 @@ void mlx5e_accel_ipsec_fs_del_rule(struct mlx5e_ipsec_sa_entry *sa_entry) return; } - mlx5_modify_header_dealloc(mdev, ipsec_rule->set_modify_hdr); + mlx5_modify_header_dealloc(mdev, ipsec_rule->modify_hdr); rx_ft_put(mdev, sa_entry->ipsec, sa_entry->attrs.family); } From 27ebe531d2d99886a189ce3d48b9872c3216bb9e Mon Sep 17 00:00:00 2001 From: Leon Romanovsky Date: Fri, 2 Dec 2022 22:10:35 +0200 Subject: [PATCH 1616/2002] net/mlx5e: Make clear what IPsec rx_err does Reuse existing struct what holds all information about modify header pointer and rule. This helps to reduce ambiguity from the name _err_ that doesn't describe the real purpose of that flow table, rule and function - to copy status result from HW to the stack. Reviewed-by: Saeed Mahameed Signed-off-by: Leon Romanovsky Signed-off-by: Steffen Klassert --- .../mellanox/mlx5/core/en_accel/ipsec_fs.c | 38 ++++++++----------- 1 file changed, 16 insertions(+), 22 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_fs.c b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_fs.c index b81046c71e6c..b89001538abd 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_fs.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_fs.c @@ -9,15 +9,10 @@ #define NUM_IPSEC_FTE BIT(15) -struct mlx5e_ipsec_rx_err { - struct mlx5_flow_table *ft; - struct mlx5_flow_handle *rule; - struct mlx5_modify_hdr *copy_modify_hdr; -}; - struct mlx5e_ipsec_ft { struct mutex mutex; /* Protect changes to this struct */ struct mlx5_flow_table *sa; + struct mlx5_flow_table *status; u32 refcnt; }; @@ -26,7 +21,7 @@ struct mlx5e_ipsec_rx { struct mlx5_flow_group *miss_group; struct mlx5_flow_handle *miss_rule; struct mlx5_flow_destination default_dest; - struct mlx5e_ipsec_rx_err rx_err; + struct mlx5e_ipsec_rule status; }; struct mlx5e_ipsec_tx { @@ -57,9 +52,8 @@ static struct mlx5_flow_table *ipsec_ft_create(struct mlx5_flow_namespace *ns, return mlx5_create_auto_grouped_flow_table(ns, &ft_attr); } -static int rx_err_add_rule(struct mlx5_core_dev *mdev, - struct mlx5e_ipsec_rx *rx, - struct mlx5e_ipsec_rx_err *rx_err) +static int ipsec_status_rule(struct mlx5_core_dev *mdev, + struct mlx5e_ipsec_rx *rx) { u8 action[MLX5_UN_SZ_BYTES(set_add_copy_action_in_auto)] = {}; struct mlx5_flow_act flow_act = {}; @@ -94,7 +88,7 @@ static int rx_err_add_rule(struct mlx5_core_dev *mdev, flow_act.action = MLX5_FLOW_CONTEXT_ACTION_MOD_HDR | MLX5_FLOW_CONTEXT_ACTION_FWD_DEST; flow_act.modify_hdr = modify_hdr; - fte = mlx5_add_flow_rules(rx_err->ft, spec, &flow_act, + fte = mlx5_add_flow_rules(rx->ft.status, spec, &flow_act, &rx->default_dest, 1); if (IS_ERR(fte)) { err = PTR_ERR(fte); @@ -103,8 +97,8 @@ static int rx_err_add_rule(struct mlx5_core_dev *mdev, } kvfree(spec); - rx_err->rule = fte; - rx_err->copy_modify_hdr = modify_hdr; + rx->status.rule = fte; + rx->status.modify_hdr = modify_hdr; return 0; out: @@ -165,9 +159,9 @@ static void rx_destroy(struct mlx5_core_dev *mdev, struct mlx5e_ipsec_rx *rx) mlx5_destroy_flow_group(rx->miss_group); mlx5_destroy_flow_table(rx->ft.sa); - mlx5_del_flow_rules(rx->rx_err.rule); - mlx5_modify_header_dealloc(mdev, rx->rx_err.copy_modify_hdr); - mlx5_destroy_flow_table(rx->rx_err.ft); + mlx5_del_flow_rules(rx->status.rule); + mlx5_modify_header_dealloc(mdev, rx->status.modify_hdr); + mlx5_destroy_flow_table(rx->ft.status); } static int rx_create(struct mlx5_core_dev *mdev, struct mlx5e_ipsec *ipsec, @@ -185,8 +179,8 @@ static int rx_create(struct mlx5_core_dev *mdev, struct mlx5e_ipsec *ipsec, if (IS_ERR(ft)) return PTR_ERR(ft); - rx->rx_err.ft = ft; - err = rx_err_add_rule(mdev, rx, &rx->rx_err); + rx->ft.status = ft; + err = ipsec_status_rule(mdev, rx); if (err) goto err_add; @@ -208,10 +202,10 @@ static int rx_create(struct mlx5_core_dev *mdev, struct mlx5e_ipsec *ipsec, err_fs: mlx5_destroy_flow_table(rx->ft.sa); err_fs_ft: - mlx5_del_flow_rules(rx->rx_err.rule); - mlx5_modify_header_dealloc(mdev, rx->rx_err.copy_modify_hdr); + mlx5_del_flow_rules(rx->status.rule); + mlx5_modify_header_dealloc(mdev, rx->status.modify_hdr); err_add: - mlx5_destroy_flow_table(rx->rx_err.ft); + mlx5_destroy_flow_table(rx->ft.status); return err; } @@ -477,7 +471,7 @@ static int rx_add_rule(struct mlx5e_ipsec_sa_entry *sa_entry) flow_act.action |= MLX5_FLOW_CONTEXT_ACTION_FWD_DEST | MLX5_FLOW_CONTEXT_ACTION_CRYPTO_DECRYPT; dest.type = MLX5_FLOW_DESTINATION_TYPE_FLOW_TABLE; - dest.ft = rx->rx_err.ft; + dest.ft = rx->ft.status; rule = mlx5_add_flow_rules(rx->ft.sa, spec, &flow_act, &dest, 1); if (IS_ERR(rule)) { err = PTR_ERR(rule); From d7ec2b76026bcd72dec47dfeeb79c1a542868204 Mon Sep 17 00:00:00 2001 From: Leon Romanovsky Date: Fri, 2 Dec 2022 22:10:36 +0200 Subject: [PATCH 1617/2002] net/mlx5e: Group IPsec miss handles into separate struct Move miss handles into dedicated struct, so we can reuse it in next patch when creating IPsec policy flow table. Reviewed-by: Raed Salem Reviewed-by: Saeed Mahameed Signed-off-by: Leon Romanovsky Signed-off-by: Steffen Klassert --- .../mellanox/mlx5/core/en_accel/ipsec_fs.c | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_fs.c b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_fs.c index b89001538abd..dfdda5ae2245 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_fs.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_fs.c @@ -16,10 +16,14 @@ struct mlx5e_ipsec_ft { u32 refcnt; }; +struct mlx5e_ipsec_miss { + struct mlx5_flow_group *group; + struct mlx5_flow_handle *rule; +}; + struct mlx5e_ipsec_rx { struct mlx5e_ipsec_ft ft; - struct mlx5_flow_group *miss_group; - struct mlx5_flow_handle *miss_rule; + struct mlx5e_ipsec_miss sa; struct mlx5_flow_destination default_dest; struct mlx5e_ipsec_rule status; }; @@ -135,18 +139,18 @@ static int rx_fs_create(struct mlx5_core_dev *mdev, struct mlx5e_ipsec_rx *rx) mlx5_core_err(mdev, "fail to create ipsec rx miss_group err=%d\n", err); goto out; } - rx->miss_group = miss_group; + rx->sa.group = miss_group; /* Create miss rule */ miss_rule = mlx5_add_flow_rules(ft, spec, &flow_act, &rx->default_dest, 1); if (IS_ERR(miss_rule)) { - mlx5_destroy_flow_group(rx->miss_group); + mlx5_destroy_flow_group(rx->sa.group); err = PTR_ERR(miss_rule); mlx5_core_err(mdev, "fail to create ipsec rx miss_rule err=%d\n", err); goto out; } - rx->miss_rule = miss_rule; + rx->sa.rule = miss_rule; out: kvfree(flow_group_in); kvfree(spec); @@ -155,8 +159,8 @@ out: static void rx_destroy(struct mlx5_core_dev *mdev, struct mlx5e_ipsec_rx *rx) { - mlx5_del_flow_rules(rx->miss_rule); - mlx5_destroy_flow_group(rx->miss_group); + mlx5_del_flow_rules(rx->sa.rule); + mlx5_destroy_flow_group(rx->sa.group); mlx5_destroy_flow_table(rx->ft.sa); mlx5_del_flow_rules(rx->status.rule); From a8e052932a2aaeccb979fddbca6f018ef8b458eb Mon Sep 17 00:00:00 2001 From: Leon Romanovsky Date: Fri, 2 Dec 2022 22:10:37 +0200 Subject: [PATCH 1618/2002] net/mlx5e: Generalize creation of default IPsec miss group and rule Create general function that sets miss group and rule to forward all not-matched traffic to the next table. Reviewed-by: Raed Salem Reviewed-by: Saeed Mahameed Signed-off-by: Leon Romanovsky Signed-off-by: Steffen Klassert --- .../mellanox/mlx5/core/en_accel/ipsec_fs.c | 47 +++++++++---------- 1 file changed, 23 insertions(+), 24 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_fs.c b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_fs.c index dfdda5ae2245..5bc6f9d1f5a6 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_fs.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_fs.c @@ -24,7 +24,6 @@ struct mlx5e_ipsec_miss { struct mlx5e_ipsec_rx { struct mlx5e_ipsec_ft ft; struct mlx5e_ipsec_miss sa; - struct mlx5_flow_destination default_dest; struct mlx5e_ipsec_rule status; }; @@ -57,7 +56,8 @@ static struct mlx5_flow_table *ipsec_ft_create(struct mlx5_flow_namespace *ns, } static int ipsec_status_rule(struct mlx5_core_dev *mdev, - struct mlx5e_ipsec_rx *rx) + struct mlx5e_ipsec_rx *rx, + struct mlx5_flow_destination *dest) { u8 action[MLX5_UN_SZ_BYTES(set_add_copy_action_in_auto)] = {}; struct mlx5_flow_act flow_act = {}; @@ -92,8 +92,7 @@ static int ipsec_status_rule(struct mlx5_core_dev *mdev, flow_act.action = MLX5_FLOW_CONTEXT_ACTION_MOD_HDR | MLX5_FLOW_CONTEXT_ACTION_FWD_DEST; flow_act.modify_hdr = modify_hdr; - fte = mlx5_add_flow_rules(rx->ft.status, spec, &flow_act, - &rx->default_dest, 1); + fte = mlx5_add_flow_rules(rx->ft.status, spec, &flow_act, dest, 1); if (IS_ERR(fte)) { err = PTR_ERR(fte); mlx5_core_err(mdev, "fail to add ipsec rx err copy rule err=%d\n", err); @@ -112,12 +111,12 @@ out_spec: return err; } -static int rx_fs_create(struct mlx5_core_dev *mdev, struct mlx5e_ipsec_rx *rx) +static int ipsec_miss_create(struct mlx5_core_dev *mdev, + struct mlx5_flow_table *ft, + struct mlx5e_ipsec_miss *miss, + struct mlx5_flow_destination *dest) { int inlen = MLX5_ST_SZ_BYTES(create_flow_group_in); - struct mlx5_flow_table *ft = rx->ft.sa; - struct mlx5_flow_group *miss_group; - struct mlx5_flow_handle *miss_rule; MLX5_DECLARE_FLOW_ACT(flow_act); struct mlx5_flow_spec *spec; u32 *flow_group_in; @@ -133,24 +132,23 @@ static int rx_fs_create(struct mlx5_core_dev *mdev, struct mlx5e_ipsec_rx *rx) /* Create miss_group */ MLX5_SET(create_flow_group_in, flow_group_in, start_flow_index, ft->max_fte - 1); MLX5_SET(create_flow_group_in, flow_group_in, end_flow_index, ft->max_fte - 1); - miss_group = mlx5_create_flow_group(ft, flow_group_in); - if (IS_ERR(miss_group)) { - err = PTR_ERR(miss_group); - mlx5_core_err(mdev, "fail to create ipsec rx miss_group err=%d\n", err); + miss->group = mlx5_create_flow_group(ft, flow_group_in); + if (IS_ERR(miss->group)) { + err = PTR_ERR(miss->group); + mlx5_core_err(mdev, "fail to create IPsec miss_group err=%d\n", + err); goto out; } - rx->sa.group = miss_group; /* Create miss rule */ - miss_rule = - mlx5_add_flow_rules(ft, spec, &flow_act, &rx->default_dest, 1); - if (IS_ERR(miss_rule)) { - mlx5_destroy_flow_group(rx->sa.group); - err = PTR_ERR(miss_rule); - mlx5_core_err(mdev, "fail to create ipsec rx miss_rule err=%d\n", err); + miss->rule = mlx5_add_flow_rules(ft, spec, &flow_act, dest, 1); + if (IS_ERR(miss->rule)) { + mlx5_destroy_flow_group(miss->group); + err = PTR_ERR(miss->rule); + mlx5_core_err(mdev, "fail to create IPsec miss_rule err=%d\n", + err); goto out; } - rx->sa.rule = miss_rule; out: kvfree(flow_group_in); kvfree(spec); @@ -173,18 +171,19 @@ static int rx_create(struct mlx5_core_dev *mdev, struct mlx5e_ipsec *ipsec, { struct mlx5_flow_namespace *ns = mlx5e_fs_get_ns(ipsec->fs, false); struct mlx5_ttc_table *ttc = mlx5e_fs_get_ttc(ipsec->fs, false); + struct mlx5_flow_destination dest; struct mlx5_flow_table *ft; int err; - rx->default_dest = mlx5_ttc_get_default_dest(ttc, family2tt(family)); - ft = ipsec_ft_create(ns, MLX5E_ACCEL_FS_ESP_FT_ERR_LEVEL, MLX5E_NIC_PRIO, 1); if (IS_ERR(ft)) return PTR_ERR(ft); rx->ft.status = ft; - err = ipsec_status_rule(mdev, rx); + + dest = mlx5_ttc_get_default_dest(ttc, family2tt(family)); + err = ipsec_status_rule(mdev, rx, &dest); if (err) goto err_add; @@ -197,7 +196,7 @@ static int rx_create(struct mlx5_core_dev *mdev, struct mlx5e_ipsec *ipsec, } rx->ft.sa = ft; - err = rx_fs_create(mdev, rx); + err = ipsec_miss_create(mdev, rx->ft.sa, &rx->sa, &dest); if (err) goto err_fs; From b54b6003612a376e7be32cbc5c1af3754bbbbb3d Mon Sep 17 00:00:00 2001 From: Pu Lehui Date: Tue, 6 Dec 2022 17:14:10 +0800 Subject: [PATCH 1619/2002] riscv, bpf: Emit fixed-length instructions for BPF_PSEUDO_FUNC MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For BPF_PSEUDO_FUNC instruction, verifier will refill imm with correct addresses of bpf_calls and then run last pass of JIT. Since the emit_imm of RV64 is variable-length, which will emit appropriate length instructions accorroding to the imm, it may broke ctx->offset, and lead to unpredictable problem, such as inaccurate jump. So let's fix it with fixed-length instructions. Fixes: 69c087ba6225 ("bpf: Add bpf_for_each_map_elem() helper") Suggested-by: Björn Töpel Signed-off-by: Pu Lehui Signed-off-by: Daniel Borkmann Reviewed-by: Björn Töpel Acked-by: Björn Töpel Link: https://lore.kernel.org/bpf/20221206091410.1584784-1-pulehui@huaweicloud.com --- arch/riscv/net/bpf_jit_comp64.c | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/arch/riscv/net/bpf_jit_comp64.c b/arch/riscv/net/bpf_jit_comp64.c index 00df3a8f92ac..f2417ac54edd 100644 --- a/arch/riscv/net/bpf_jit_comp64.c +++ b/arch/riscv/net/bpf_jit_comp64.c @@ -136,6 +136,25 @@ static bool in_auipc_jalr_range(s64 val) val < ((1L << 31) - (1L << 11)); } +/* Emit fixed-length instructions for address */ +static int emit_addr(u8 rd, u64 addr, bool extra_pass, struct rv_jit_context *ctx) +{ + u64 ip = (u64)(ctx->insns + ctx->ninsns); + s64 off = addr - ip; + s64 upper = (off + (1 << 11)) >> 12; + s64 lower = off & 0xfff; + + if (extra_pass && !in_auipc_jalr_range(off)) { + pr_err("bpf-jit: target offset 0x%llx is out of range\n", off); + return -ERANGE; + } + + emit(rv_auipc(rd, upper), ctx); + emit(rv_addi(rd, rd, lower), ctx); + return 0; +} + +/* Emit variable-length instructions for 32-bit and 64-bit imm */ static void emit_imm(u8 rd, s64 val, struct rv_jit_context *ctx) { /* Note that the immediate from the add is sign-extended, @@ -1050,7 +1069,15 @@ out_be: u64 imm64; imm64 = (u64)insn1.imm << 32 | (u32)imm; - emit_imm(rd, imm64, ctx); + if (bpf_pseudo_func(insn)) { + /* fixed-length insns for extra jit pass */ + ret = emit_addr(rd, imm64, extra_pass, ctx); + if (ret) + return ret; + } else { + emit_imm(rd, imm64, ctx); + } + return 1; } From fa55ef14ef4fe06198c0ce811b603aec24134bc2 Mon Sep 17 00:00:00 2001 From: Miaoqian Lin Date: Tue, 6 Dec 2022 11:19:06 +0400 Subject: [PATCH 1620/2002] bpftool: Fix memory leak in do_build_table_cb strdup() allocates memory for path. We need to release the memory in the following error path. Add free() to avoid memory leak. Fixes: 8f184732b60b ("bpftool: Switch to libbpf's hashmap for pinned paths of BPF objects") Signed-off-by: Miaoqian Lin Signed-off-by: Daniel Borkmann Link: https://lore.kernel.org/bpf/20221206071906.806384-1-linmq006@gmail.com --- tools/bpf/bpftool/common.c | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/bpf/bpftool/common.c b/tools/bpf/bpftool/common.c index c90b756945e3..620032042576 100644 --- a/tools/bpf/bpftool/common.c +++ b/tools/bpf/bpftool/common.c @@ -501,6 +501,7 @@ static int do_build_table_cb(const char *fpath, const struct stat *sb, if (err) { p_err("failed to append entry to hashmap for ID %u, path '%s': %s", pinned_info.id, path, strerror(errno)); + free(path); goto out_close; } From aa67961f3243dfff26c47769f87b4d94b07ec71f Mon Sep 17 00:00:00 2001 From: Martin KaFai Lau Date: Tue, 6 Dec 2022 11:35:54 -0800 Subject: [PATCH 1621/2002] selftests/bpf: Allow building bpf tests with CONFIG_XFRM_INTERFACE=[m|n] It is useful to use vmlinux.h in the xfrm_info test like other kfunc tests do. In particular, it is common for kfunc bpf prog that requires to use other core kernel structures in vmlinux.h Although vmlinux.h is preferred, it needs a ___local flavor of struct bpf_xfrm_info in order to build the bpf selftests when CONFIG_XFRM_INTERFACE=[m|n]. Cc: Eyal Birger Fixes: 90a3a05eb33f ("selftests/bpf: add xfrm_info tests") Signed-off-by: Martin KaFai Lau Link: https://lore.kernel.org/r/20221206193554.1059757-1-martin.lau@linux.dev Signed-off-by: Alexei Starovoitov --- tools/testing/selftests/bpf/progs/xfrm_info.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/tools/testing/selftests/bpf/progs/xfrm_info.c b/tools/testing/selftests/bpf/progs/xfrm_info.c index 3acedcdd962d..f6a501fbba2b 100644 --- a/tools/testing/selftests/bpf/progs/xfrm_info.c +++ b/tools/testing/selftests/bpf/progs/xfrm_info.c @@ -3,18 +3,23 @@ #include "bpf_tracing_net.h" #include +struct bpf_xfrm_info___local { + u32 if_id; + int link; +} __attribute__((preserve_access_index)); + __u32 req_if_id; __u32 resp_if_id; int bpf_skb_set_xfrm_info(struct __sk_buff *skb_ctx, - const struct bpf_xfrm_info *from) __ksym; + const struct bpf_xfrm_info___local *from) __ksym; int bpf_skb_get_xfrm_info(struct __sk_buff *skb_ctx, - struct bpf_xfrm_info *to) __ksym; + struct bpf_xfrm_info___local *to) __ksym; SEC("tc") int set_xfrm_info(struct __sk_buff *skb) { - struct bpf_xfrm_info info = { .if_id = req_if_id }; + struct bpf_xfrm_info___local info = { .if_id = req_if_id }; return bpf_skb_set_xfrm_info(skb, &info) ? TC_ACT_SHOT : TC_ACT_UNSPEC; } @@ -22,7 +27,7 @@ int set_xfrm_info(struct __sk_buff *skb) SEC("tc") int get_xfrm_info(struct __sk_buff *skb) { - struct bpf_xfrm_info info = {}; + struct bpf_xfrm_info___local info = {}; if (bpf_skb_get_xfrm_info(skb, &info) < 0) return TC_ACT_SHOT; From c21dc529baba16d6698a396eb997fee318300ee1 Mon Sep 17 00:00:00 2001 From: Timo Hunziker Date: Sat, 3 Dec 2022 12:37:46 +0000 Subject: [PATCH 1622/2002] libbpf: Parse usdt args without offset on x86 (e.g. 8@(%rsp)) Parse USDT arguments like "8@(%rsp)" on x86. These are emmited by SystemTap. The argument syntax is similar to the existing "memory dereference case" but the offset left out as it's zero (i.e. read the value from the address in the register). We treat it the same as the the "memory dereference case", but set the offset to 0. I've tested that this fixes the "unrecognized arg #N spec: 8@(%rsp).." error I've run into when attaching to a probe with such an argument. Attaching and reading the correct argument values works. Something similar might be needed for the other supported architectures. [0] Closes: https://github.com/libbpf/libbpf/issues/559 Signed-off-by: Timo Hunziker Signed-off-by: Andrii Nakryiko Link: https://lore.kernel.org/bpf/20221203123746.2160-1-timo.hunziker@eclipso.ch --- tools/lib/bpf/usdt.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tools/lib/bpf/usdt.c b/tools/lib/bpf/usdt.c index b8daae265f99..75b411fc2c77 100644 --- a/tools/lib/bpf/usdt.c +++ b/tools/lib/bpf/usdt.c @@ -1233,6 +1233,14 @@ static int parse_usdt_arg(const char *arg_str, int arg_num, struct usdt_arg_spec if (reg_off < 0) return reg_off; arg->reg_off = reg_off; + } else if (sscanf(arg_str, " %d @ ( %%%15[^)] ) %n", &arg_sz, reg_name, &len) == 2) { + /* Memory dereference case without offset, e.g., 8@(%rsp) */ + arg->arg_type = USDT_ARG_REG_DEREF; + arg->val_off = 0; + reg_off = calc_pt_regs_off(reg_name); + if (reg_off < 0) + return reg_off; + arg->reg_off = reg_off; } else if (sscanf(arg_str, " %d @ %%%15s %n", &arg_sz, reg_name, &len) == 2) { /* Register read case, e.g., -4@%eax */ arg->arg_type = USDT_ARG_REG; From d68ae4982cb773f3d738b5dc25f77f5c7550548a Mon Sep 17 00:00:00 2001 From: Daan De Meyer Date: Mon, 5 Dec 2022 14:16:16 +0100 Subject: [PATCH 1623/2002] selftests/bpf: Install all required files to run selftests When installing the selftests using "make -C tools/testing/selftests install", we need to make sure all the required files to run the selftests are installed. Let's make sure this is the case. Signed-off-by: Daan De Meyer Signed-off-by: Andrii Nakryiko Link: https://lore.kernel.org/bpf/20221205131618.1524337-2-daan.j.demeyer@gmail.com --- tools/testing/selftests/bpf/Makefile | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tools/testing/selftests/bpf/Makefile b/tools/testing/selftests/bpf/Makefile index 6a0f043dc410..997b3bd10cbf 100644 --- a/tools/testing/selftests/bpf/Makefile +++ b/tools/testing/selftests/bpf/Makefile @@ -532,8 +532,10 @@ TRUNNER_EXTRA_FILES := $(OUTPUT)/urandom_read $(OUTPUT)/bpf_testmod.ko \ $(OUTPUT)/liburandom_read.so \ $(OUTPUT)/xdp_synproxy \ $(OUTPUT)/sign-file \ - ima_setup.sh verify_sig_setup.sh \ - $(wildcard progs/btf_dump_test_case_*.c) + ima_setup.sh \ + verify_sig_setup.sh \ + $(wildcard progs/btf_dump_test_case_*.c) \ + $(wildcard progs/*.bpf.o) TRUNNER_BPF_BUILD_RULE := CLANG_BPF_BUILD_RULE TRUNNER_BPF_CFLAGS := $(BPF_CFLAGS) $(CLANG_CFLAGS) -DENABLE_ATOMICS_TESTS $(eval $(call DEFINE_TEST_RUNNER,test_progs)) From efe7fadbd59ec4513272c722403226e47321311b Mon Sep 17 00:00:00 2001 From: Daan De Meyer Date: Mon, 5 Dec 2022 14:16:17 +0100 Subject: [PATCH 1624/2002] selftests/bpf: Use "is not set" instead of "=n" "=n" is not valid kconfig syntax. Use "is not set" instead to indicate the option should be disabled. Signed-off-by: Daan De Meyer Signed-off-by: Andrii Nakryiko Link: https://lore.kernel.org/bpf/20221205131618.1524337-3-daan.j.demeyer@gmail.com --- tools/testing/selftests/bpf/config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/testing/selftests/bpf/config b/tools/testing/selftests/bpf/config index 3543c76cef56..be892ea76c32 100644 --- a/tools/testing/selftests/bpf/config +++ b/tools/testing/selftests/bpf/config @@ -8,7 +8,7 @@ CONFIG_BPF_LIRC_MODE2=y CONFIG_BPF_LSM=y CONFIG_BPF_STREAM_PARSER=y CONFIG_BPF_SYSCALL=y -CONFIG_BPF_UNPRIV_DEFAULT_OFF=n +# CONFIG_BPF_UNPRIV_DEFAULT_OFF is not set CONFIG_CGROUP_BPF=y CONFIG_CRYPTO_HMAC=y CONFIG_CRYPTO_SHA256=y From d0c0b48c87274b7735f8c930a7c0d783fb46cfe9 Mon Sep 17 00:00:00 2001 From: Daan De Meyer Date: Mon, 5 Dec 2022 14:16:18 +0100 Subject: [PATCH 1625/2002] selftests/bpf: Use CONFIG_TEST_BPF=m instead of CONFIG_TEST_BPF=y CONFIG_TEST_BPF can only be a module, so let's indicate it as such in the selftests config. Signed-off-by: Daan De Meyer Signed-off-by: Andrii Nakryiko Link: https://lore.kernel.org/bpf/20221205131618.1524337-4-daan.j.demeyer@gmail.com --- tools/testing/selftests/bpf/config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/testing/selftests/bpf/config b/tools/testing/selftests/bpf/config index be892ea76c32..612f699dc4f7 100644 --- a/tools/testing/selftests/bpf/config +++ b/tools/testing/selftests/bpf/config @@ -71,7 +71,7 @@ CONFIG_NF_NAT=y CONFIG_RC_CORE=y CONFIG_SECURITY=y CONFIG_SECURITYFS=y -CONFIG_TEST_BPF=y +CONFIG_TEST_BPF=m CONFIG_USERFAULTFD=y CONFIG_VXLAN=y CONFIG_XDP_SOCKETS=y From 156ed20d22ee68d470232d26ae6df2cefacac4a0 Mon Sep 17 00:00:00 2001 From: David Vernet Date: Tue, 6 Dec 2022 15:05:38 -0600 Subject: [PATCH 1626/2002] bpf: Don't use rcu_users to refcount in task kfuncs A series of prior patches added some kfuncs that allow struct task_struct * objects to be used as kptrs. These kfuncs leveraged the 'refcount_t rcu_users' field of the task for performing refcounting. This field was used instead of 'refcount_t usage', as we wanted to leverage the safety provided by RCU for ensuring a task's lifetime. A struct task_struct is refcounted by two different refcount_t fields: 1. p->usage: The "true" refcount field which task lifetime. The task is freed as soon as this refcount drops to 0. 2. p->rcu_users: An "RCU users" refcount field which is statically initialized to 2, and is co-located in a union with a struct rcu_head field (p->rcu). p->rcu_users essentially encapsulates a single p->usage refcount, and when p->rcu_users goes to 0, an RCU callback is scheduled on the struct rcu_head which decrements the p->usage refcount. Our logic was that by using p->rcu_users, we would be able to use RCU to safely issue refcount_inc_not_zero() a task's rcu_users field to determine if a task could still be acquired, or was exiting. Unfortunately, this does not work due to p->rcu_users and p->rcu sharing a union. When p->rcu_users goes to 0, an RCU callback is scheduled to drop a single p->usage refcount, and because the fields share a union, the refcount immediately becomes nonzero again after the callback is scheduled. If we were to split the fields out of the union, this wouldn't be a problem. Doing so should also be rather non-controversial, as there are a number of places in struct task_struct that have padding which we could use to avoid growing the structure by splitting up the fields. For now, so as to fix the kfuncs to be correct, this patch instead updates bpf_task_acquire() and bpf_task_release() to use the p->usage field for refcounting via the get_task_struct() and put_task_struct() functions. Because we can no longer rely on RCU, the change also guts the bpf_task_acquire_not_zero() and bpf_task_kptr_get() functions pending a resolution on the above problem. In addition, the task fixes the kfunc and rcu_read_lock selftests to expect this new behavior. Fixes: 90660309b0c7 ("bpf: Add kfuncs for storing struct task_struct * as a kptr") Fixes: fca1aa75518c ("bpf: Handle MEM_RCU type properly") Reported-by: Matus Jokay Signed-off-by: David Vernet Link: https://lore.kernel.org/r/20221206210538.597606-1-void@manifault.com Signed-off-by: Alexei Starovoitov --- kernel/bpf/helpers.c | 76 ++++++++++++------- .../selftests/bpf/progs/rcu_read_lock.c | 5 ++ .../selftests/bpf/progs/task_kfunc_success.c | 9 ++- 3 files changed, 60 insertions(+), 30 deletions(-) diff --git a/kernel/bpf/helpers.c b/kernel/bpf/helpers.c index cca642358e80..284b3ffdbe48 100644 --- a/kernel/bpf/helpers.c +++ b/kernel/bpf/helpers.c @@ -1833,8 +1833,7 @@ struct bpf_list_node *bpf_list_pop_back(struct bpf_list_head *head) */ struct task_struct *bpf_task_acquire(struct task_struct *p) { - refcount_inc(&p->rcu_users); - return p; + return get_task_struct(p); } /** @@ -1845,9 +1844,48 @@ struct task_struct *bpf_task_acquire(struct task_struct *p) */ struct task_struct *bpf_task_acquire_not_zero(struct task_struct *p) { - if (!refcount_inc_not_zero(&p->rcu_users)) - return NULL; - return p; + /* For the time being this function returns NULL, as it's not currently + * possible to safely acquire a reference to a task with RCU protection + * using get_task_struct() and put_task_struct(). This is due to the + * slightly odd mechanics of p->rcu_users, and how task RCU protection + * works. + * + * A struct task_struct is refcounted by two different refcount_t + * fields: + * + * 1. p->usage: The "true" refcount field which tracks a task's + * lifetime. The task is freed as soon as this + * refcount drops to 0. + * + * 2. p->rcu_users: An "RCU users" refcount field which is statically + * initialized to 2, and is co-located in a union with + * a struct rcu_head field (p->rcu). p->rcu_users + * essentially encapsulates a single p->usage + * refcount, and when p->rcu_users goes to 0, an RCU + * callback is scheduled on the struct rcu_head which + * decrements the p->usage refcount. + * + * There are two important implications to this task refcounting logic + * described above. The first is that + * refcount_inc_not_zero(&p->rcu_users) cannot be used anywhere, as + * after the refcount goes to 0, the RCU callback being scheduled will + * cause the memory backing the refcount to again be nonzero due to the + * fields sharing a union. The other is that we can't rely on RCU to + * guarantee that a task is valid in a BPF program. This is because a + * task could have already transitioned to being in the TASK_DEAD + * state, had its rcu_users refcount go to 0, and its rcu callback + * invoked in which it drops its single p->usage reference. At this + * point the task will be freed as soon as the last p->usage reference + * goes to 0, without waiting for another RCU gp to elapse. The only + * way that a BPF program can guarantee that a task is valid is in this + * scenario is to hold a p->usage refcount itself. + * + * Until we're able to resolve this issue, either by pulling + * p->rcu_users and p->rcu out of the union, or by getting rid of + * p->usage and just using p->rcu_users for refcounting, we'll just + * return NULL here. + */ + return NULL; } /** @@ -1858,33 +1896,15 @@ struct task_struct *bpf_task_acquire_not_zero(struct task_struct *p) */ struct task_struct *bpf_task_kptr_get(struct task_struct **pp) { - struct task_struct *p; - - rcu_read_lock(); - p = READ_ONCE(*pp); - - /* Another context could remove the task from the map and release it at - * any time, including after we've done the lookup above. This is safe - * because we're in an RCU read region, so the task is guaranteed to - * remain valid until at least the rcu_read_unlock() below. + /* We must return NULL here until we have clarity on how to properly + * leverage RCU for ensuring a task's lifetime. See the comment above + * in bpf_task_acquire_not_zero() for more details. */ - if (p && !refcount_inc_not_zero(&p->rcu_users)) - /* If the task had been removed from the map and freed as - * described above, refcount_inc_not_zero() will return false. - * The task will be freed at some point after the current RCU - * gp has ended, so just return NULL to the user. - */ - p = NULL; - rcu_read_unlock(); - - return p; + return NULL; } /** * bpf_task_release - Release the reference acquired on a struct task_struct *. - * If this kfunc is invoked in an RCU read region, the task_struct is - * guaranteed to not be freed until the current grace period has ended, even if - * its refcount drops to 0. * @p: The task on which a reference is being released. */ void bpf_task_release(struct task_struct *p) @@ -1892,7 +1912,7 @@ void bpf_task_release(struct task_struct *p) if (!p) return; - put_task_struct_rcu_user(p); + put_task_struct(p); } #ifdef CONFIG_CGROUPS diff --git a/tools/testing/selftests/bpf/progs/rcu_read_lock.c b/tools/testing/selftests/bpf/progs/rcu_read_lock.c index cf06a34fcb02..125f908024d3 100644 --- a/tools/testing/selftests/bpf/progs/rcu_read_lock.c +++ b/tools/testing/selftests/bpf/progs/rcu_read_lock.c @@ -161,6 +161,11 @@ int task_acquire(void *ctx) /* acquire a reference which can be used outside rcu read lock region */ gparent = bpf_task_acquire_not_zero(gparent); if (!gparent) + /* Until we resolve the issues with using task->rcu_users, we + * expect bpf_task_acquire_not_zero() to return a NULL task. + * See the comment at the definition of + * bpf_task_acquire_not_zero() for more details. + */ goto out; (void)bpf_task_storage_get(&map_a, gparent, 0, 0); diff --git a/tools/testing/selftests/bpf/progs/task_kfunc_success.c b/tools/testing/selftests/bpf/progs/task_kfunc_success.c index 60c7ead41cfc..9f359cfd29e7 100644 --- a/tools/testing/selftests/bpf/progs/task_kfunc_success.c +++ b/tools/testing/selftests/bpf/progs/task_kfunc_success.c @@ -123,12 +123,17 @@ int BPF_PROG(test_task_get_release, struct task_struct *task, u64 clone_flags) } kptr = bpf_task_kptr_get(&v->task); - if (!kptr) { + if (kptr) { + /* Until we resolve the issues with using task->rcu_users, we + * expect bpf_task_kptr_get() to return a NULL task. See the + * comment at the definition of bpf_task_acquire_not_zero() for + * more details. + */ + bpf_task_release(kptr); err = 3; return 0; } - bpf_task_release(kptr); return 0; } From b93884eea26f97a3dc4c1df8c64389cbb0673001 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Fri, 2 Dec 2022 13:24:22 -0800 Subject: [PATCH 1627/2002] net/ncsi: Silence runtime memcpy() false positive warning The memcpy() in ncsi_cmd_handler_oem deserializes nca->data into a flexible array structure that overlapping with non-flex-array members (mfr_id) intentionally. Since the mem_to_flex() API is not finished, temporarily silence this warning, since it is a false positive, using unsafe_memcpy(). Reported-by: Joel Stanley Link: https://lore.kernel.org/netdev/CACPK8Xdfi=OJKP0x0D1w87fQeFZ4A2DP2qzGCRcuVbpU-9=4sQ@mail.gmail.com/ Cc: Samuel Mendoza-Jonas Signed-off-by: Kees Cook Link: https://lore.kernel.org/r/20221202212418.never.837-kees@kernel.org Signed-off-by: Jakub Kicinski --- net/ncsi/ncsi-cmd.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/net/ncsi/ncsi-cmd.c b/net/ncsi/ncsi-cmd.c index dda8b76b7798..fd2236ee9a79 100644 --- a/net/ncsi/ncsi-cmd.c +++ b/net/ncsi/ncsi-cmd.c @@ -228,7 +228,8 @@ static int ncsi_cmd_handler_oem(struct sk_buff *skb, len += max(payload, padding_bytes); cmd = skb_put_zero(skb, len); - memcpy(&cmd->mfr_id, nca->data, nca->payload); + unsafe_memcpy(&cmd->mfr_id, nca->data, nca->payload, + /* skb allocated with enough to load the payload */); ncsi_cmd_build_header(&cmd->cmd.common, nca); return 0; From d8939cb0a03ce7e4e69f65bbd31b79fe42f7d5e6 Mon Sep 17 00:00:00 2001 From: Dave Marchevsky Date: Tue, 6 Dec 2022 15:09:48 -0800 Subject: [PATCH 1628/2002] bpf: Loosen alloc obj test in verifier's reg_btf_record btf->struct_meta_tab is populated by btf_parse_struct_metas in btf.c. There, a BTF record is created for any type containing a spin_lock or any next-gen datastructure node/head. Currently, for non-MAP_VALUE types, reg_btf_record will only search for a record using struct_meta_tab if the reg->type exactly matches (PTR_TO_BTF_ID | MEM_ALLOC). This exact match is too strict: an "allocated obj" type - returned from bpf_obj_new - might pick up other flags while working its way through the program. Loosen the check to be exact for base_type and just use MEM_ALLOC mask for type_flag. This patch is marked Fixes as the original intent of reg_btf_record was unlikely to have been to fail finding btf_record for valid alloc obj types with additional flags, some of which (e.g. PTR_UNTRUSTED) are valid register type states for alloc obj independent of this series. However, I didn't find a specific broken repro case outside of this series' added functionality, so it's possible that nothing was triggering this logic error before. Signed-off-by: Dave Marchevsky cc: Kumar Kartikeya Dwivedi Fixes: 4e814da0d599 ("bpf: Allow locking bpf_spin_lock in allocated objects") Link: https://lore.kernel.org/r/20221206231000.3180914-2-davemarchevsky@fb.com Signed-off-by: Alexei Starovoitov --- kernel/bpf/verifier.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 1d51bd9596da..67a13110bc22 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -451,6 +451,11 @@ static bool reg_type_not_null(enum bpf_reg_type type) type == PTR_TO_SOCK_COMMON; } +static bool type_is_ptr_alloc_obj(u32 type) +{ + return base_type(type) == PTR_TO_BTF_ID && type_flag(type) & MEM_ALLOC; +} + static struct btf_record *reg_btf_record(const struct bpf_reg_state *reg) { struct btf_record *rec = NULL; @@ -458,7 +463,7 @@ static struct btf_record *reg_btf_record(const struct bpf_reg_state *reg) if (reg->type == PTR_TO_MAP_VALUE) { rec = reg->map_ptr->record; - } else if (reg->type == (PTR_TO_BTF_ID | MEM_ALLOC)) { + } else if (type_is_ptr_alloc_obj(reg->type)) { meta = btf_find_struct_meta(reg->btf, reg->btf_id); if (meta) rec = meta->record; From 15309fb26b87767b4c8b017a628424e3c3b2f69e Mon Sep 17 00:00:00 2001 From: "Russell King (Oracle)" Date: Sat, 3 Dec 2022 17:25:15 +0000 Subject: [PATCH 1629/2002] net: sfp: clean up i2c-bus property parsing We currently have some complicated code in sfp_probe() which gets the I2C bus depending on whether the sfp node is DT or ACPI, and we use completely separate lookup functions. This could do with being in a separate function to make the code more readable, so move it to a new function, sfp_i2c_get(). We can also use fwnode_find_reference() to lookup the I2C bus fwnode before then decending into fwnode-type specific parsing. A future cleanup would be to move the fwnode-type specific parsing into the i2c layer, which is where it really should be. Signed-off-by: Russell King (Oracle) Link: https://lore.kernel.org/r/E1p1WGJ-0098wS-4w@rmk-PC.armlinux.org.uk Signed-off-by: Jakub Kicinski --- drivers/net/phy/sfp.c | 77 +++++++++++++++++++++++-------------------- 1 file changed, 41 insertions(+), 36 deletions(-) diff --git a/drivers/net/phy/sfp.c b/drivers/net/phy/sfp.c index 39fd1811375c..83b99d95b278 100644 --- a/drivers/net/phy/sfp.c +++ b/drivers/net/phy/sfp.c @@ -2642,10 +2642,46 @@ static void sfp_cleanup(void *data) kfree(sfp); } +static int sfp_i2c_get(struct sfp *sfp) +{ + struct acpi_handle *acpi_handle; + struct fwnode_handle *h; + struct i2c_adapter *i2c; + struct device_node *np; + int err; + + h = fwnode_find_reference(dev_fwnode(sfp->dev), "i2c-bus", 0); + if (IS_ERR(h)) { + dev_err(sfp->dev, "missing 'i2c-bus' property\n"); + return -ENODEV; + } + + if (is_acpi_device_node(h)) { + acpi_handle = ACPI_HANDLE_FWNODE(h); + i2c = i2c_acpi_find_adapter_by_handle(acpi_handle); + } else if ((np = to_of_node(h)) != NULL) { + i2c = of_find_i2c_adapter_by_node(np); + } else { + err = -EINVAL; + goto put; + } + + if (!i2c) { + err = -EPROBE_DEFER; + goto put; + } + + err = sfp_i2c_configure(sfp, i2c); + if (err) + i2c_put_adapter(i2c); +put: + fwnode_handle_put(h); + return err; +} + static int sfp_probe(struct platform_device *pdev) { const struct sff_data *sff; - struct i2c_adapter *i2c; char *sfp_irq_name; struct sfp *sfp; int err, i; @@ -2663,51 +2699,20 @@ static int sfp_probe(struct platform_device *pdev) sff = sfp->type = &sfp_data; if (pdev->dev.of_node) { - struct device_node *node = pdev->dev.of_node; const struct of_device_id *id; - struct device_node *np; - id = of_match_node(sfp_of_match, node); + id = of_match_node(sfp_of_match, pdev->dev.of_node); if (WARN_ON(!id)) return -EINVAL; sff = sfp->type = id->data; - - np = of_parse_phandle(node, "i2c-bus", 0); - if (!np) { - dev_err(sfp->dev, "missing 'i2c-bus' property\n"); - return -ENODEV; - } - - i2c = of_find_i2c_adapter_by_node(np); - of_node_put(np); - } else if (has_acpi_companion(&pdev->dev)) { - struct acpi_device *adev = ACPI_COMPANION(&pdev->dev); - struct fwnode_handle *fw = acpi_fwnode_handle(adev); - struct fwnode_reference_args args; - struct acpi_handle *acpi_handle; - int ret; - - ret = acpi_node_get_property_reference(fw, "i2c-bus", 0, &args); - if (ret || !is_acpi_device_node(args.fwnode)) { - dev_err(&pdev->dev, "missing 'i2c-bus' property\n"); - return -ENODEV; - } - - acpi_handle = ACPI_HANDLE_FWNODE(args.fwnode); - i2c = i2c_acpi_find_adapter_by_handle(acpi_handle); - } else { + } else if (!has_acpi_companion(&pdev->dev)) { return -EINVAL; } - if (!i2c) - return -EPROBE_DEFER; - - err = sfp_i2c_configure(sfp, i2c); - if (err < 0) { - i2c_put_adapter(i2c); + err = sfp_i2c_get(sfp); + if (err) return err; - } for (i = 0; i < GPIO_MAX; i++) if (sff->gpios & BIT(i)) { From bffdeaa8a5af7200b0e74c9d5a41167f86626a36 Mon Sep 17 00:00:00 2001 From: Andrii Nakryiko Date: Tue, 6 Dec 2022 15:33:43 -0800 Subject: [PATCH 1630/2002] bpf: decouple prune and jump points BPF verifier marks some instructions as prune points. Currently these prune points serve two purposes. It's a point where verifier tries to find previously verified state and check current state's equivalence to short circuit verification for current code path. But also currently it's a point where jump history, used for precision backtracking, is updated. This is done so that non-linear flow of execution could be properly backtracked. Such coupling is coincidental and unnecessary. Some prune points are not part of some non-linear jump path, so don't need update of jump history. On the other hand, not all instructions which have to be recorded in jump history necessarily are good prune points. This patch splits prune and jump points into independent flags. Currently all prune points are marked as jump points to minimize amount of changes in this patch, but next patch will perform some optimization of prune vs jmp point placement. No functional changes are intended. Acked-by: John Fastabend Signed-off-by: Andrii Nakryiko Link: https://lore.kernel.org/r/20221206233345.438540-2-andrii@kernel.org Signed-off-by: Alexei Starovoitov --- include/linux/bpf_verifier.h | 1 + kernel/bpf/verifier.c | 57 +++++++++++++++++++++++++++--------- 2 files changed, 44 insertions(+), 14 deletions(-) diff --git a/include/linux/bpf_verifier.h b/include/linux/bpf_verifier.h index c07b351a5bc7..70d06a99f0b8 100644 --- a/include/linux/bpf_verifier.h +++ b/include/linux/bpf_verifier.h @@ -452,6 +452,7 @@ struct bpf_insn_aux_data { /* below fields are initialized once */ unsigned int orig_idx; /* original instruction index */ bool prune_point; + bool jmp_point; }; #define MAX_USED_MAPS 64 /* max number of maps accessed by one eBPF program */ diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 67a13110bc22..cf580e4c00f5 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -2530,6 +2530,16 @@ static int check_reg_arg(struct bpf_verifier_env *env, u32 regno, return 0; } +static void mark_jmp_point(struct bpf_verifier_env *env, int idx) +{ + env->insn_aux_data[idx].jmp_point = true; +} + +static bool is_jmp_point(struct bpf_verifier_env *env, int insn_idx) +{ + return env->insn_aux_data[insn_idx].jmp_point; +} + /* for any branch, call, exit record the history of jmps in the given state */ static int push_jmp_history(struct bpf_verifier_env *env, struct bpf_verifier_state *cur) @@ -2538,6 +2548,9 @@ static int push_jmp_history(struct bpf_verifier_env *env, struct bpf_idx_pair *p; size_t alloc_size; + if (!is_jmp_point(env, env->insn_idx)) + return 0; + cnt++; alloc_size = kmalloc_size_roundup(size_mul(cnt, sizeof(*p))); p = krealloc(cur->jmp_history, alloc_size, GFP_USER); @@ -12140,11 +12153,16 @@ static struct bpf_verifier_state_list **explored_state( return &env->explored_states[(idx ^ state->callsite) % state_htab_size(env)]; } -static void init_explored_state(struct bpf_verifier_env *env, int idx) +static void mark_prune_point(struct bpf_verifier_env *env, int idx) { env->insn_aux_data[idx].prune_point = true; } +static bool is_prune_point(struct bpf_verifier_env *env, int insn_idx) +{ + return env->insn_aux_data[insn_idx].prune_point; +} + enum { DONE_EXPLORING = 0, KEEP_EXPLORING = 1, @@ -12173,9 +12191,11 @@ static int push_insn(int t, int w, int e, struct bpf_verifier_env *env, return -EINVAL; } - if (e == BRANCH) + if (e == BRANCH) { /* mark branch target for state pruning */ - init_explored_state(env, w); + mark_prune_point(env, w); + mark_jmp_point(env, w); + } if (insn_state[w] == 0) { /* tree-edge */ @@ -12213,10 +12233,13 @@ static int visit_func_call_insn(int t, int insn_cnt, if (ret) return ret; - if (t + 1 < insn_cnt) - init_explored_state(env, t + 1); + if (t + 1 < insn_cnt) { + mark_prune_point(env, t + 1); + mark_jmp_point(env, t + 1); + } if (visit_callee) { - init_explored_state(env, t); + mark_prune_point(env, t); + mark_jmp_point(env, t); ret = push_insn(t, t + insns[t].imm + 1, BRANCH, env, /* It's ok to allow recursion from CFG point of * view. __check_func_call() will do the actual @@ -12250,13 +12273,15 @@ static int visit_insn(int t, int insn_cnt, struct bpf_verifier_env *env) return DONE_EXPLORING; case BPF_CALL: - if (insns[t].imm == BPF_FUNC_timer_set_callback) + if (insns[t].imm == BPF_FUNC_timer_set_callback) { /* Mark this call insn to trigger is_state_visited() check * before call itself is processed by __check_func_call(). * Otherwise new async state will be pushed for further * exploration. */ - init_explored_state(env, t); + mark_prune_point(env, t); + mark_jmp_point(env, t); + } return visit_func_call_insn(t, insn_cnt, insns, env, insns[t].src_reg == BPF_PSEUDO_CALL); @@ -12274,18 +12299,22 @@ static int visit_insn(int t, int insn_cnt, struct bpf_verifier_env *env) * but it's marked, since backtracking needs * to record jmp history in is_state_visited(). */ - init_explored_state(env, t + insns[t].off + 1); + mark_prune_point(env, t + insns[t].off + 1); + mark_jmp_point(env, t + insns[t].off + 1); /* tell verifier to check for equivalent states * after every call and jump */ - if (t + 1 < insn_cnt) - init_explored_state(env, t + 1); + if (t + 1 < insn_cnt) { + mark_prune_point(env, t + 1); + mark_jmp_point(env, t + 1); + } return ret; default: /* conditional jump with two edges */ - init_explored_state(env, t); + mark_prune_point(env, t); + mark_jmp_point(env, t); ret = push_insn(t, t + 1, FALLTHROUGH, env, true); if (ret) return ret; @@ -13320,11 +13349,11 @@ static int is_state_visited(struct bpf_verifier_env *env, int insn_idx) bool add_new_state = env->test_state_freq ? true : false; cur->last_insn_idx = env->prev_insn_idx; - if (!env->insn_aux_data[insn_idx].prune_point) + if (!is_prune_point(env, insn_idx)) /* this 'insn_idx' instruction wasn't marked, so we will not * be doing state search here */ - return 0; + return push_jmp_history(env, cur); /* bpf progs typically have pruning point every 4 instructions * http://vger.kernel.org/bpfconf2019.html#session-1 From a095f421057e22853022cb644b1589d0dd0e756e Mon Sep 17 00:00:00 2001 From: Andrii Nakryiko Date: Tue, 6 Dec 2022 15:33:44 -0800 Subject: [PATCH 1631/2002] bpf: mostly decouple jump history management from is_state_visited() Jump history updating and state equivalence checks are conceptually independent, so move push_jmp_history() out of is_state_visited(). Also make a decision whether to perform state equivalence checks or not one layer higher in do_check(), keeping is_state_visited() unconditionally performing state checks. push_jmp_history() should be performed after state checks. There is just one small non-uniformity. When is_state_visited() finds already validated equivalent state, it propagates precision marks to current state's parent chain. For this to work correctly, jump history has to be updated, so is_state_visited() is doing that internally. But if no equivalent verified state is found, jump history has to be updated in a newly cloned child state, so is_jmp_point() + push_jmp_history() is performed after is_state_visited() exited with zero result, which means "proceed with validation". This change has no functional changes. It's not strictly necessary, but feels right to decouple these two processes. Acked-by: John Fastabend Signed-off-by: Andrii Nakryiko Link: https://lore.kernel.org/r/20221206233345.438540-3-andrii@kernel.org Signed-off-by: Alexei Starovoitov --- kernel/bpf/verifier.c | 49 +++++++++++++++++++++++-------------------- 1 file changed, 26 insertions(+), 23 deletions(-) diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index cf580e4c00f5..8d8315d9b18b 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -13348,13 +13348,6 @@ static int is_state_visited(struct bpf_verifier_env *env, int insn_idx) int i, j, err, states_cnt = 0; bool add_new_state = env->test_state_freq ? true : false; - cur->last_insn_idx = env->prev_insn_idx; - if (!is_prune_point(env, insn_idx)) - /* this 'insn_idx' instruction wasn't marked, so we will not - * be doing state search here - */ - return push_jmp_history(env, cur); - /* bpf progs typically have pruning point every 4 instructions * http://vger.kernel.org/bpfconf2019.html#session-1 * Do not add new state for future pruning if the verifier hasn't seen @@ -13489,10 +13482,10 @@ next: env->max_states_per_insn = states_cnt; if (!env->bpf_capable && states_cnt > BPF_COMPLEXITY_LIMIT_STATES) - return push_jmp_history(env, cur); + return 0; if (!add_new_state) - return push_jmp_history(env, cur); + return 0; /* There were no equivalent states, remember the current one. * Technically the current state is not proven to be safe yet, @@ -13632,21 +13625,31 @@ static int do_check(struct bpf_verifier_env *env) return -E2BIG; } - err = is_state_visited(env, env->insn_idx); - if (err < 0) - return err; - if (err == 1) { - /* found equivalent state, can prune the search */ - if (env->log.level & BPF_LOG_LEVEL) { - if (do_print_state) - verbose(env, "\nfrom %d to %d%s: safe\n", - env->prev_insn_idx, env->insn_idx, - env->cur_state->speculative ? - " (speculative execution)" : ""); - else - verbose(env, "%d: safe\n", env->insn_idx); + state->last_insn_idx = env->prev_insn_idx; + + if (is_prune_point(env, env->insn_idx)) { + err = is_state_visited(env, env->insn_idx); + if (err < 0) + return err; + if (err == 1) { + /* found equivalent state, can prune the search */ + if (env->log.level & BPF_LOG_LEVEL) { + if (do_print_state) + verbose(env, "\nfrom %d to %d%s: safe\n", + env->prev_insn_idx, env->insn_idx, + env->cur_state->speculative ? + " (speculative execution)" : ""); + else + verbose(env, "%d: safe\n", env->insn_idx); + } + goto process_bpf_exit; } - goto process_bpf_exit; + } + + if (is_jmp_point(env, env->insn_idx)) { + err = push_jmp_history(env, state); + if (err) + return err; } if (signal_pending(current)) From 618945fbed501b6e5865042068a51edfb2dda948 Mon Sep 17 00:00:00 2001 From: Andrii Nakryiko Date: Tue, 6 Dec 2022 15:33:45 -0800 Subject: [PATCH 1632/2002] bpf: remove unnecessary prune and jump points Don't mark some instructions as jump points when there are actually no jumps and instructions are just processed sequentially. Such case is handled naturally by precision backtracking logic without the need to update jump history. See get_prev_insn_idx(). It goes back linearly by one instruction, unless current top of jmp_history is pointing to current instruction. In such case we use `st->jmp_history[cnt - 1].prev_idx` to find instruction from which we jumped to the current instruction non-linearly. Also remove both jump and prune point marking for instruction right after unconditional jumps, as program flow can get to the instruction right after unconditional jump instruction only if there is a jump to that instruction from somewhere else in the program. In such case we'll mark such instruction as prune/jump point because it's a destination of a jump. This change has no changes in terms of number of instructions or states processes across Cilium and selftests programs. Signed-off-by: Andrii Nakryiko Acked-by: John Fastabend Link: https://lore.kernel.org/r/20221206233345.438540-4-andrii@kernel.org Signed-off-by: Alexei Starovoitov --- kernel/bpf/verifier.c | 34 ++++++++++------------------------ 1 file changed, 10 insertions(+), 24 deletions(-) diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 8d8315d9b18b..8c5f0adbbde3 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -12233,13 +12233,12 @@ static int visit_func_call_insn(int t, int insn_cnt, if (ret) return ret; - if (t + 1 < insn_cnt) { - mark_prune_point(env, t + 1); - mark_jmp_point(env, t + 1); - } + mark_prune_point(env, t + 1); + /* when we exit from subprog, we need to record non-linear history */ + mark_jmp_point(env, t + 1); + if (visit_callee) { mark_prune_point(env, t); - mark_jmp_point(env, t); ret = push_insn(t, t + insns[t].imm + 1, BRANCH, env, /* It's ok to allow recursion from CFG point of * view. __check_func_call() will do the actual @@ -12273,15 +12272,13 @@ static int visit_insn(int t, int insn_cnt, struct bpf_verifier_env *env) return DONE_EXPLORING; case BPF_CALL: - if (insns[t].imm == BPF_FUNC_timer_set_callback) { - /* Mark this call insn to trigger is_state_visited() check - * before call itself is processed by __check_func_call(). - * Otherwise new async state will be pushed for further - * exploration. + if (insns[t].imm == BPF_FUNC_timer_set_callback) + /* Mark this call insn as a prune point to trigger + * is_state_visited() check before call itself is + * processed by __check_func_call(). Otherwise new + * async state will be pushed for further exploration. */ mark_prune_point(env, t); - mark_jmp_point(env, t); - } return visit_func_call_insn(t, insn_cnt, insns, env, insns[t].src_reg == BPF_PSEUDO_CALL); @@ -12295,26 +12292,15 @@ static int visit_insn(int t, int insn_cnt, struct bpf_verifier_env *env) if (ret) return ret; - /* unconditional jmp is not a good pruning point, - * but it's marked, since backtracking needs - * to record jmp history in is_state_visited(). - */ mark_prune_point(env, t + insns[t].off + 1); mark_jmp_point(env, t + insns[t].off + 1); - /* tell verifier to check for equivalent states - * after every call and jump - */ - if (t + 1 < insn_cnt) { - mark_prune_point(env, t + 1); - mark_jmp_point(env, t + 1); - } return ret; default: /* conditional jump with two edges */ mark_prune_point(env, t); - mark_jmp_point(env, t); + ret = push_insn(t, t + 1, FALLTHROUGH, env, true); if (ret) return ret; From e9b4aeed56699b469206d05e706ddf2db95700a9 Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Sat, 3 Dec 2022 17:51:04 +0100 Subject: [PATCH 1633/2002] net: xsk: Don't include There is no need to include here. Prefer the less invasive which is needed for 'hlist_head'. Signed-off-by: Christophe JAILLET Acked-by: John Fastabend Link: https://lore.kernel.org/r/88d6a1d88764cca328610854f890a9ca1f4b029e.1670086246.git.christophe.jaillet@wanadoo.fr Signed-off-by: Alexei Starovoitov --- include/net/netns/xdp.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/net/netns/xdp.h b/include/net/netns/xdp.h index e5734261ba0a..21a4f25a187a 100644 --- a/include/net/netns/xdp.h +++ b/include/net/netns/xdp.h @@ -2,8 +2,8 @@ #ifndef __NETNS_XDP_H__ #define __NETNS_XDP_H__ -#include #include +#include struct netns_xdp { struct mutex lock; From 1ab586f5177b3b2805a05aa926a5b46d3d7f112c Mon Sep 17 00:00:00 2001 From: ye xingchen Date: Mon, 5 Dec 2022 10:21:45 +0800 Subject: [PATCH 1634/2002] sfc: use sysfs_emit() to instead of scnprintf() Follow the advice of the Documentation/filesystems/sysfs.rst and show() should only use sysfs_emit() or sysfs_emit_at() when formatting the value to be returned to user space. Signed-off-by: ye xingchen Reviewed-by: Leon Romanovsky Acked-by: Martin Habets Link: https://lore.kernel.org/r/202212051021451139126@zte.com.cn Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/sfc/efx_common.c | 2 +- drivers/net/ethernet/sfc/siena/efx_common.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/sfc/efx_common.c b/drivers/net/ethernet/sfc/efx_common.c index c2224e41a694..cc30524c2fe4 100644 --- a/drivers/net/ethernet/sfc/efx_common.c +++ b/drivers/net/ethernet/sfc/efx_common.c @@ -1164,7 +1164,7 @@ static ssize_t mcdi_logging_show(struct device *dev, struct efx_nic *efx = dev_get_drvdata(dev); struct efx_mcdi_iface *mcdi = efx_mcdi(efx); - return scnprintf(buf, PAGE_SIZE, "%d\n", mcdi->logging_enabled); + return sysfs_emit(buf, "%d\n", mcdi->logging_enabled); } static ssize_t mcdi_logging_store(struct device *dev, diff --git a/drivers/net/ethernet/sfc/siena/efx_common.c b/drivers/net/ethernet/sfc/siena/efx_common.c index 1fd396b00bfb..e4b294b8e9ac 100644 --- a/drivers/net/ethernet/sfc/siena/efx_common.c +++ b/drivers/net/ethernet/sfc/siena/efx_common.c @@ -1178,7 +1178,7 @@ static ssize_t mcdi_logging_show(struct device *dev, struct efx_nic *efx = dev_get_drvdata(dev); struct efx_mcdi_iface *mcdi = efx_mcdi(efx); - return scnprintf(buf, PAGE_SIZE, "%d\n", mcdi->logging_enabled); + return sysfs_emit(buf, "%d\n", mcdi->logging_enabled); } static ssize_t mcdi_logging_store(struct device *dev, From e3bd74c3d1907360a37f49a0cdf02daa2983ff53 Mon Sep 17 00:00:00 2001 From: zhang songyi Date: Mon, 5 Dec 2022 14:22:15 +0800 Subject: [PATCH 1635/2002] net: microchip: vcap: Remove unneeded semicolons Semicolons after "}" are not needed. Signed-off-by: zhang songyi Reviewed-by: Jiri Pirko Link: https://lore.kernel.org/r/202212051422158113766@zte.com.cn Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/microchip/vcap/vcap_api.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/microchip/vcap/vcap_api.c b/drivers/net/ethernet/microchip/vcap/vcap_api.c index 05e915ea858d..664aae3e2acd 100644 --- a/drivers/net/ethernet/microchip/vcap/vcap_api.c +++ b/drivers/net/ethernet/microchip/vcap/vcap_api.c @@ -2197,7 +2197,7 @@ static void vcap_copy_from_client_keyfield(struct vcap_rule *rule, vcap_copy_to_w32be(field->data.u128.value, data->u128.value, size); vcap_copy_to_w32be(field->data.u128.mask, data->u128.mask, size); break; - }; + } } /* Check if the keyfield is already in the rule */ @@ -2403,7 +2403,7 @@ static void vcap_copy_from_client_actionfield(struct vcap_rule *rule, case VCAP_FIELD_U128: vcap_copy_to_w32be(field->data.u128.value, data->u128.value, size); break; - }; + } } /* Check if the actionfield is already in the rule */ From ed883bec679b027b198d57a336715f8298fb88b4 Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Mon, 5 Dec 2022 12:34:42 +0100 Subject: [PATCH 1636/2002] net: ethernet: mtk_wed: add reset to rx_ring_setup callback This patch adds reset parameter to mtk_wed_rx_ring_setup signature in order to align rx_ring_setup callback to tx_ring_setup one introduced in 'commit 23dca7a90017 ("net: ethernet: mtk_wed: add reset to tx_ring_setup callback")' Co-developed-by: Sujuan Chen Signed-off-by: Sujuan Chen Signed-off-by: Lorenzo Bianconi Reviewed-by: Leon Romanovsky Link: https://lore.kernel.org/r/29c6e7a5469e784406cf3e2920351d1207713d05.1670239984.git.lorenzo@kernel.org Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/mediatek/mtk_wed.c | 20 +++++++++++++------- drivers/net/wireless/mediatek/mt76/dma.c | 2 +- include/linux/soc/mediatek/mtk_wed.h | 8 ++++---- 3 files changed, 18 insertions(+), 12 deletions(-) diff --git a/drivers/net/ethernet/mediatek/mtk_wed.c b/drivers/net/ethernet/mediatek/mtk_wed.c index d041615b2bac..06b6cc53fa02 100644 --- a/drivers/net/ethernet/mediatek/mtk_wed.c +++ b/drivers/net/ethernet/mediatek/mtk_wed.c @@ -1210,7 +1210,8 @@ mtk_wed_wdma_rx_ring_setup(struct mtk_wed_device *dev, int idx, int size, } static int -mtk_wed_wdma_tx_ring_setup(struct mtk_wed_device *dev, int idx, int size) +mtk_wed_wdma_tx_ring_setup(struct mtk_wed_device *dev, int idx, int size, + bool reset) { u32 desc_size = sizeof(struct mtk_wdma_desc) * dev->hw->version; struct mtk_wed_ring *wdma; @@ -1219,8 +1220,8 @@ mtk_wed_wdma_tx_ring_setup(struct mtk_wed_device *dev, int idx, int size) return -EINVAL; wdma = &dev->tx_wdma[idx]; - if (mtk_wed_ring_alloc(dev, wdma, MTK_WED_WDMA_RING_SIZE, desc_size, - true)) + if (!reset && mtk_wed_ring_alloc(dev, wdma, MTK_WED_WDMA_RING_SIZE, + desc_size, true)) return -ENOMEM; wdma_w32(dev, MTK_WDMA_RING_TX(idx) + MTK_WED_RING_OFS_BASE, @@ -1230,6 +1231,9 @@ mtk_wed_wdma_tx_ring_setup(struct mtk_wed_device *dev, int idx, int size) wdma_w32(dev, MTK_WDMA_RING_TX(idx) + MTK_WED_RING_OFS_CPU_IDX, 0); wdma_w32(dev, MTK_WDMA_RING_TX(idx) + MTK_WED_RING_OFS_DMA_IDX, 0); + if (reset) + mtk_wed_ring_reset(wdma, MTK_WED_WDMA_RING_SIZE, true); + if (!idx) { wed_w32(dev, MTK_WED_WDMA_RING_TX + MTK_WED_RING_OFS_BASE, wdma->desc_phys); @@ -1569,18 +1573,20 @@ mtk_wed_txfree_ring_setup(struct mtk_wed_device *dev, void __iomem *regs) } static int -mtk_wed_rx_ring_setup(struct mtk_wed_device *dev, int idx, void __iomem *regs) +mtk_wed_rx_ring_setup(struct mtk_wed_device *dev, int idx, void __iomem *regs, + bool reset) { struct mtk_wed_ring *ring = &dev->rx_ring[idx]; if (WARN_ON(idx >= ARRAY_SIZE(dev->rx_ring))) return -EINVAL; - if (mtk_wed_ring_alloc(dev, ring, MTK_WED_RX_RING_SIZE, - sizeof(*ring->desc), false)) + if (!reset && mtk_wed_ring_alloc(dev, ring, MTK_WED_RX_RING_SIZE, + sizeof(*ring->desc), false)) return -ENOMEM; - if (mtk_wed_wdma_tx_ring_setup(dev, idx, MTK_WED_WDMA_RING_SIZE)) + if (mtk_wed_wdma_tx_ring_setup(dev, idx, MTK_WED_WDMA_RING_SIZE, + reset)) return -ENOMEM; ring->reg_base = MTK_WED_RING_RX_DATA(idx); diff --git a/drivers/net/wireless/mediatek/mt76/dma.c b/drivers/net/wireless/mediatek/mt76/dma.c index 3f8c0845fcca..f795548562f5 100644 --- a/drivers/net/wireless/mediatek/mt76/dma.c +++ b/drivers/net/wireless/mediatek/mt76/dma.c @@ -648,7 +648,7 @@ mt76_dma_wed_setup(struct mt76_dev *dev, struct mt76_queue *q) q->wed_regs = wed->txfree_ring.reg_base; break; case MT76_WED_Q_RX: - ret = mtk_wed_device_rx_ring_setup(wed, ring, q->regs); + ret = mtk_wed_device_rx_ring_setup(wed, ring, q->regs, false); if (!ret) q->wed_regs = wed->rx_ring[ring].reg_base; break; diff --git a/include/linux/soc/mediatek/mtk_wed.h b/include/linux/soc/mediatek/mtk_wed.h index beb190449704..a0746d4aec20 100644 --- a/include/linux/soc/mediatek/mtk_wed.h +++ b/include/linux/soc/mediatek/mtk_wed.h @@ -160,7 +160,7 @@ struct mtk_wed_ops { int (*tx_ring_setup)(struct mtk_wed_device *dev, int ring, void __iomem *regs, bool reset); int (*rx_ring_setup)(struct mtk_wed_device *dev, int ring, - void __iomem *regs); + void __iomem *regs, bool reset); int (*txfree_ring_setup)(struct mtk_wed_device *dev, void __iomem *regs); int (*msg_update)(struct mtk_wed_device *dev, int cmd_id, @@ -228,8 +228,8 @@ mtk_wed_get_rx_capa(struct mtk_wed_device *dev) (_dev)->ops->irq_get(_dev, _mask) #define mtk_wed_device_irq_set_mask(_dev, _mask) \ (_dev)->ops->irq_set_mask(_dev, _mask) -#define mtk_wed_device_rx_ring_setup(_dev, _ring, _regs) \ - (_dev)->ops->rx_ring_setup(_dev, _ring, _regs) +#define mtk_wed_device_rx_ring_setup(_dev, _ring, _regs, _reset) \ + (_dev)->ops->rx_ring_setup(_dev, _ring, _regs, _reset) #define mtk_wed_device_ppe_check(_dev, _skb, _reason, _hash) \ (_dev)->ops->ppe_check(_dev, _skb, _reason, _hash) #define mtk_wed_device_update_msg(_dev, _id, _msg, _len) \ @@ -249,7 +249,7 @@ static inline bool mtk_wed_device_active(struct mtk_wed_device *dev) #define mtk_wed_device_reg_write(_dev, _reg, _val) do {} while (0) #define mtk_wed_device_irq_get(_dev, _mask) 0 #define mtk_wed_device_irq_set_mask(_dev, _mask) do {} while (0) -#define mtk_wed_device_rx_ring_setup(_dev, _ring, _regs) -ENODEV +#define mtk_wed_device_rx_ring_setup(_dev, _ring, _regs, _reset) -ENODEV #define mtk_wed_device_ppe_check(_dev, _skb, _reason, _hash) do {} while (0) #define mtk_wed_device_update_msg(_dev, _id, _msg, _len) -ENODEV #define mtk_wed_device_stop(_dev) do {} while (0) From e22dcbc9aa329e7902a8c9f8d11aba74f5d7c97d Mon Sep 17 00:00:00 2001 From: Yuan Can Date: Mon, 5 Dec 2022 03:43:39 +0000 Subject: [PATCH 1637/2002] net: ethernet: mtk_wed: Fix missing of_node_put() in mtk_wed_wo_hardware_init() The np needs to be released through of_node_put() in the error handling path of mtk_wed_wo_hardware_init(). Fixes: 799684448e3e ("net: ethernet: mtk_wed: introduce wed wo support") Signed-off-by: Yuan Can Reviewed-by: Leon Romanovsky Link: https://lore.kernel.org/r/20221205034339.112163-1-yuancan@huawei.com Signed-off-by: Paolo Abeni --- drivers/net/ethernet/mediatek/mtk_wed_wo.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/mediatek/mtk_wed_wo.c b/drivers/net/ethernet/mediatek/mtk_wed_wo.c index a219da85f4db..a0a39643caf7 100644 --- a/drivers/net/ethernet/mediatek/mtk_wed_wo.c +++ b/drivers/net/ethernet/mediatek/mtk_wed_wo.c @@ -408,8 +408,10 @@ mtk_wed_wo_hardware_init(struct mtk_wed_wo *wo) return -ENODEV; wo->mmio.regs = syscon_regmap_lookup_by_phandle(np, NULL); - if (IS_ERR_OR_NULL(wo->mmio.regs)) - return PTR_ERR(wo->mmio.regs); + if (IS_ERR(wo->mmio.regs)) { + ret = PTR_ERR(wo->mmio.regs); + goto error_put; + } wo->mmio.irq = irq_of_parse_and_map(np, 0); wo->mmio.irq_mask = MTK_WED_WO_ALL_INT_MASK; @@ -457,7 +459,8 @@ mtk_wed_wo_hardware_init(struct mtk_wed_wo *wo) error: devm_free_irq(wo->hw->dev, wo->mmio.irq, wo); - +error_put: + of_node_put(np); return ret; } From 838c19f89454cab177ed6f74ab61ad32db1764c5 Mon Sep 17 00:00:00 2001 From: Oleksij Rempel Date: Mon, 5 Dec 2022 06:22:27 +0100 Subject: [PATCH 1638/2002] net: dsa: microchip: move max mtu to one location There are no HW specific registers, so we can process all of them in one location. Signed-off-by: Oleksij Rempel Tested-by: Arun Ramadoss (KSZ9893 and LAN937x) Signed-off-by: Paolo Abeni --- drivers/net/dsa/microchip/ksz9477.c | 5 ----- drivers/net/dsa/microchip/ksz9477.h | 1 - drivers/net/dsa/microchip/ksz9477_reg.h | 2 -- drivers/net/dsa/microchip/ksz_common.c | 22 +++++++++++++++++----- drivers/net/dsa/microchip/ksz_common.h | 3 ++- 5 files changed, 19 insertions(+), 14 deletions(-) diff --git a/drivers/net/dsa/microchip/ksz9477.c b/drivers/net/dsa/microchip/ksz9477.c index 0d6b40968657..602d00671bef 100644 --- a/drivers/net/dsa/microchip/ksz9477.c +++ b/drivers/net/dsa/microchip/ksz9477.c @@ -60,11 +60,6 @@ int ksz9477_change_mtu(struct ksz_device *dev, int port, int mtu) REG_SW_MTU_MASK, max_frame); } -int ksz9477_max_mtu(struct ksz_device *dev, int port) -{ - return KSZ9477_MAX_FRAME_SIZE - VLAN_ETH_HLEN - ETH_FCS_LEN; -} - static int ksz9477_wait_vlan_ctrl_ready(struct ksz_device *dev) { unsigned int val; diff --git a/drivers/net/dsa/microchip/ksz9477.h b/drivers/net/dsa/microchip/ksz9477.h index 00862c4cfb7f..7c5bb3032772 100644 --- a/drivers/net/dsa/microchip/ksz9477.h +++ b/drivers/net/dsa/microchip/ksz9477.h @@ -50,7 +50,6 @@ int ksz9477_mdb_add(struct ksz_device *dev, int port, int ksz9477_mdb_del(struct ksz_device *dev, int port, const struct switchdev_obj_port_mdb *mdb, struct dsa_db db); int ksz9477_change_mtu(struct ksz_device *dev, int port, int mtu); -int ksz9477_max_mtu(struct ksz_device *dev, int port); void ksz9477_config_cpu_port(struct dsa_switch *ds); int ksz9477_enable_stp_addr(struct ksz_device *dev); int ksz9477_reset_switch(struct ksz_device *dev); diff --git a/drivers/net/dsa/microchip/ksz9477_reg.h b/drivers/net/dsa/microchip/ksz9477_reg.h index 53c68d286dd3..cc457fa64939 100644 --- a/drivers/net/dsa/microchip/ksz9477_reg.h +++ b/drivers/net/dsa/microchip/ksz9477_reg.h @@ -1615,6 +1615,4 @@ #define PTP_TRIG_UNIT_M (BIT(MAX_TRIG_UNIT) - 1) #define PTP_TS_UNIT_M (BIT(MAX_TIMESTAMP_UNIT) - 1) -#define KSZ9477_MAX_FRAME_SIZE 9000 - #endif /* KSZ9477_REGS_H */ diff --git a/drivers/net/dsa/microchip/ksz_common.c b/drivers/net/dsa/microchip/ksz_common.c index 8c8db315317d..49a5a236d958 100644 --- a/drivers/net/dsa/microchip/ksz_common.c +++ b/drivers/net/dsa/microchip/ksz_common.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -206,7 +207,6 @@ static const struct ksz_dev_ops ksz9477_dev_ops = { .mdb_add = ksz9477_mdb_add, .mdb_del = ksz9477_mdb_del, .change_mtu = ksz9477_change_mtu, - .max_mtu = ksz9477_max_mtu, .phylink_mac_link_up = ksz9477_phylink_mac_link_up, .config_cpu_port = ksz9477_config_cpu_port, .enable_stp_addr = ksz9477_enable_stp_addr, @@ -243,7 +243,6 @@ static const struct ksz_dev_ops lan937x_dev_ops = { .mdb_add = ksz9477_mdb_add, .mdb_del = ksz9477_mdb_del, .change_mtu = lan937x_change_mtu, - .max_mtu = ksz9477_max_mtu, .phylink_mac_link_up = ksz9477_phylink_mac_link_up, .config_cpu_port = lan937x_config_cpu_port, .enable_stp_addr = ksz9477_enable_stp_addr, @@ -2500,10 +2499,23 @@ static int ksz_max_mtu(struct dsa_switch *ds, int port) { struct ksz_device *dev = ds->priv; - if (!dev->dev_ops->max_mtu) - return -EOPNOTSUPP; + switch (dev->chip_id) { + case KSZ8563_CHIP_ID: + case KSZ9477_CHIP_ID: + case KSZ9563_CHIP_ID: + case KSZ9567_CHIP_ID: + case KSZ9893_CHIP_ID: + case KSZ9896_CHIP_ID: + case KSZ9897_CHIP_ID: + case LAN9370_CHIP_ID: + case LAN9371_CHIP_ID: + case LAN9372_CHIP_ID: + case LAN9373_CHIP_ID: + case LAN9374_CHIP_ID: + return KSZ9477_MAX_FRAME_SIZE - VLAN_ETH_HLEN - ETH_FCS_LEN; + } - return dev->dev_ops->max_mtu(dev, port); + return -EOPNOTSUPP; } static void ksz_set_xmii(struct ksz_device *dev, int port, diff --git a/drivers/net/dsa/microchip/ksz_common.h b/drivers/net/dsa/microchip/ksz_common.h index c6726cbd5465..27c26ee15af4 100644 --- a/drivers/net/dsa/microchip/ksz_common.h +++ b/drivers/net/dsa/microchip/ksz_common.h @@ -322,7 +322,6 @@ struct ksz_dev_ops { void (*get_caps)(struct ksz_device *dev, int port, struct phylink_config *config); int (*change_mtu)(struct ksz_device *dev, int port, int mtu); - int (*max_mtu)(struct ksz_device *dev, int port); void (*freeze_mib)(struct ksz_device *dev, int port, bool freeze); void (*port_init_cnt)(struct ksz_device *dev, int port); void (*phylink_mac_config)(struct ksz_device *dev, int port, @@ -588,6 +587,8 @@ static inline int is_lan937x(struct ksz_device *dev) #define PORT_SRC_PHY_INT 1 +#define KSZ9477_MAX_FRAME_SIZE 9000 + /* Regmap tables generation */ #define KSZ_SPI_OP_RD 3 #define KSZ_SPI_OP_WR 2 From 1d0a1a6d0d63db5bba73abe862180c947861f4d4 Mon Sep 17 00:00:00 2001 From: Oleksij Rempel Date: Mon, 5 Dec 2022 06:22:28 +0100 Subject: [PATCH 1639/2002] net: dsa: microchip: do not store max MTU for all ports If we have global MTU configuration, it is enough to configure it on CPU port only. Signed-off-by: Oleksij Rempel Reviewed-by: Vladimir Oltean Reviewed-by: Florian Fainelli Signed-off-by: Paolo Abeni --- drivers/net/dsa/microchip/ksz9477.c | 14 +++++--------- drivers/net/dsa/microchip/ksz_common.h | 1 - 2 files changed, 5 insertions(+), 10 deletions(-) diff --git a/drivers/net/dsa/microchip/ksz9477.c b/drivers/net/dsa/microchip/ksz9477.c index 602d00671bef..f6e7968ab105 100644 --- a/drivers/net/dsa/microchip/ksz9477.c +++ b/drivers/net/dsa/microchip/ksz9477.c @@ -45,19 +45,15 @@ static void ksz9477_port_cfg32(struct ksz_device *dev, int port, int offset, int ksz9477_change_mtu(struct ksz_device *dev, int port, int mtu) { - u16 frame_size, max_frame = 0; - int i; + u16 frame_size; + + if (!dsa_is_cpu_port(dev->ds, port)) + return 0; frame_size = mtu + VLAN_ETH_HLEN + ETH_FCS_LEN; - /* Cache the per-port MTU setting */ - dev->ports[port].max_frame = frame_size; - - for (i = 0; i < dev->info->port_cnt; i++) - max_frame = max(max_frame, dev->ports[i].max_frame); - return regmap_update_bits(dev->regmap[1], REG_SW_MTU__2, - REG_SW_MTU_MASK, max_frame); + REG_SW_MTU_MASK, frame_size); } static int ksz9477_wait_vlan_ctrl_ready(struct ksz_device *dev) diff --git a/drivers/net/dsa/microchip/ksz_common.h b/drivers/net/dsa/microchip/ksz_common.h index 27c26ee15af4..61228be299f9 100644 --- a/drivers/net/dsa/microchip/ksz_common.h +++ b/drivers/net/dsa/microchip/ksz_common.h @@ -95,7 +95,6 @@ struct ksz_port { struct ksz_port_mib mib; phy_interface_t interface; - u16 max_frame; u32 rgmii_tx_val; u32 rgmii_rx_val; struct ksz_device *ksz_dev; From 6f1b986a43ce9aa67b11a7e54ac75530705d04e7 Mon Sep 17 00:00:00 2001 From: Oleksij Rempel Date: Mon, 5 Dec 2022 06:22:29 +0100 Subject: [PATCH 1640/2002] net: dsa: microchip: add ksz_rmw8() function Add ksz_rmw8(), it will be used in the next patch. Signed-off-by: Oleksij Rempel Acked-by: Arun Ramadoss Reviewed-by: Florian Fainelli Signed-off-by: Paolo Abeni --- drivers/net/dsa/microchip/ksz_common.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/net/dsa/microchip/ksz_common.h b/drivers/net/dsa/microchip/ksz_common.h index 61228be299f9..5f404a444ce1 100644 --- a/drivers/net/dsa/microchip/ksz_common.h +++ b/drivers/net/dsa/microchip/ksz_common.h @@ -454,6 +454,11 @@ static inline int ksz_write64(struct ksz_device *dev, u32 reg, u64 value) return regmap_bulk_write(dev->regmap[2], reg, val, 2); } +static inline int ksz_rmw8(struct ksz_device *dev, int offset, u8 mask, u8 val) +{ + return regmap_update_bits(dev->regmap[0], offset, mask, val); +} + static inline int ksz_pread8(struct ksz_device *dev, int port, int offset, u8 *data) { From 29d1e85f45e03c9fd1b8985f983f33c021e130c8 Mon Sep 17 00:00:00 2001 From: Oleksij Rempel Date: Mon, 5 Dec 2022 06:22:30 +0100 Subject: [PATCH 1641/2002] net: dsa: microchip: ksz8: add MTU configuration support Make MTU configurable on KSZ87xx and KSZ88xx series of switches. Before this patch, pre-configured behavior was different on different switch series, due to opposite meaning of the same bit: - KSZ87xx: Reg 4, Bit 1 - if 1, max frame size is 1532; if 0 - 1514 - KSZ88xx: Reg 4, Bit 1 - if 1, max frame size is 1514; if 0 - 1532 Since the code was telling "... SW_LEGAL_PACKET_DISABLE, true)", I assume, the idea was to set max frame size to 1532. With this patch, by setting MTU size 1500, both switch series will be configured to the 1532 frame limit. This patch was tested on KSZ8873. Signed-off-by: Oleksij Rempel Acked-by: Arun Ramadoss Reviewed-by: Vladimir Oltean Reviewed-by: Florian Fainelli Signed-off-by: Paolo Abeni --- drivers/net/dsa/microchip/ksz8.h | 1 + drivers/net/dsa/microchip/ksz8795.c | 53 ++++++++++++++++++++++++- drivers/net/dsa/microchip/ksz8795_reg.h | 3 ++ drivers/net/dsa/microchip/ksz_common.c | 7 ++++ drivers/net/dsa/microchip/ksz_common.h | 4 ++ 5 files changed, 66 insertions(+), 2 deletions(-) diff --git a/drivers/net/dsa/microchip/ksz8.h b/drivers/net/dsa/microchip/ksz8.h index 8582b4b67d98..ea05abfbd51d 100644 --- a/drivers/net/dsa/microchip/ksz8.h +++ b/drivers/net/dsa/microchip/ksz8.h @@ -57,5 +57,6 @@ int ksz8_reset_switch(struct ksz_device *dev); int ksz8_switch_detect(struct ksz_device *dev); int ksz8_switch_init(struct ksz_device *dev); void ksz8_switch_exit(struct ksz_device *dev); +int ksz8_change_mtu(struct ksz_device *dev, int port, int mtu); #endif diff --git a/drivers/net/dsa/microchip/ksz8795.c b/drivers/net/dsa/microchip/ksz8795.c index bd3b133e7085..d01bfd609130 100644 --- a/drivers/net/dsa/microchip/ksz8795.c +++ b/drivers/net/dsa/microchip/ksz8795.c @@ -76,6 +76,57 @@ int ksz8_reset_switch(struct ksz_device *dev) return 0; } +static int ksz8863_change_mtu(struct ksz_device *dev, int frame_size) +{ + u8 ctrl2 = 0; + + if (frame_size <= KSZ8_LEGAL_PACKET_SIZE) + ctrl2 |= KSZ8863_LEGAL_PACKET_ENABLE; + else if (frame_size > KSZ8863_NORMAL_PACKET_SIZE) + ctrl2 |= KSZ8863_HUGE_PACKET_ENABLE; + + return ksz_rmw8(dev, REG_SW_CTRL_2, KSZ8863_LEGAL_PACKET_ENABLE | + KSZ8863_HUGE_PACKET_ENABLE, ctrl2); +} + +static int ksz8795_change_mtu(struct ksz_device *dev, int frame_size) +{ + u8 ctrl1 = 0, ctrl2 = 0; + int ret; + + if (frame_size > KSZ8_LEGAL_PACKET_SIZE) + ctrl2 |= SW_LEGAL_PACKET_DISABLE; + else if (frame_size > KSZ8863_NORMAL_PACKET_SIZE) + ctrl1 |= SW_HUGE_PACKET; + + ret = ksz_rmw8(dev, REG_SW_CTRL_1, SW_HUGE_PACKET, ctrl1); + if (ret) + return ret; + + return ksz_rmw8(dev, REG_SW_CTRL_2, SW_LEGAL_PACKET_DISABLE, ctrl2); +} + +int ksz8_change_mtu(struct ksz_device *dev, int port, int mtu) +{ + u16 frame_size; + + if (!dsa_is_cpu_port(dev->ds, port)) + return 0; + + frame_size = mtu + VLAN_ETH_HLEN + ETH_FCS_LEN; + + switch (dev->chip_id) { + case KSZ8795_CHIP_ID: + case KSZ8794_CHIP_ID: + case KSZ8765_CHIP_ID: + return ksz8795_change_mtu(dev, frame_size); + case KSZ8830_CHIP_ID: + return ksz8863_change_mtu(dev, frame_size); + } + + return -EOPNOTSUPP; +} + static void ksz8795_set_prio_queue(struct ksz_device *dev, int port, int queue) { u8 hi, lo; @@ -1233,8 +1284,6 @@ void ksz8_config_cpu_port(struct dsa_switch *ds) masks = dev->info->masks; regs = dev->info->regs; - /* Switch marks the maximum frame with extra byte as oversize. */ - ksz_cfg(dev, REG_SW_CTRL_2, SW_LEGAL_PACKET_DISABLE, true); ksz_cfg(dev, regs[S_TAIL_TAG_CTRL], masks[SW_TAIL_TAG_ENABLE], true); p = &dev->ports[dev->cpu_port]; diff --git a/drivers/net/dsa/microchip/ksz8795_reg.h b/drivers/net/dsa/microchip/ksz8795_reg.h index 77487d611824..7a57c6088f80 100644 --- a/drivers/net/dsa/microchip/ksz8795_reg.h +++ b/drivers/net/dsa/microchip/ksz8795_reg.h @@ -48,6 +48,9 @@ #define NO_EXC_COLLISION_DROP BIT(3) #define SW_LEGAL_PACKET_DISABLE BIT(1) +#define KSZ8863_HUGE_PACKET_ENABLE BIT(2) +#define KSZ8863_LEGAL_PACKET_ENABLE BIT(1) + #define REG_SW_CTRL_3 0x05 #define WEIGHTED_FAIR_QUEUE_ENABLE BIT(3) diff --git a/drivers/net/dsa/microchip/ksz_common.c b/drivers/net/dsa/microchip/ksz_common.c index 49a5a236d958..f39b041765fb 100644 --- a/drivers/net/dsa/microchip/ksz_common.c +++ b/drivers/net/dsa/microchip/ksz_common.c @@ -172,6 +172,7 @@ static const struct ksz_dev_ops ksz8_dev_ops = { .reset = ksz8_reset_switch, .init = ksz8_switch_init, .exit = ksz8_switch_exit, + .change_mtu = ksz8_change_mtu, }; static void ksz9477_phylink_mac_link_up(struct ksz_device *dev, int port, @@ -2500,6 +2501,12 @@ static int ksz_max_mtu(struct dsa_switch *ds, int port) struct ksz_device *dev = ds->priv; switch (dev->chip_id) { + case KSZ8795_CHIP_ID: + case KSZ8794_CHIP_ID: + case KSZ8765_CHIP_ID: + return KSZ8795_HUGE_PACKET_SIZE - VLAN_ETH_HLEN - ETH_FCS_LEN; + case KSZ8830_CHIP_ID: + return KSZ8863_HUGE_PACKET_SIZE - VLAN_ETH_HLEN - ETH_FCS_LEN; case KSZ8563_CHIP_ID: case KSZ9477_CHIP_ID: case KSZ9563_CHIP_ID: diff --git a/drivers/net/dsa/microchip/ksz_common.h b/drivers/net/dsa/microchip/ksz_common.h index 5f404a444ce1..cb27f5a180c7 100644 --- a/drivers/net/dsa/microchip/ksz_common.h +++ b/drivers/net/dsa/microchip/ksz_common.h @@ -591,6 +591,10 @@ static inline int is_lan937x(struct ksz_device *dev) #define PORT_SRC_PHY_INT 1 +#define KSZ8795_HUGE_PACKET_SIZE 2000 +#define KSZ8863_HUGE_PACKET_SIZE 1916 +#define KSZ8863_NORMAL_PACKET_SIZE 1536 +#define KSZ8_LEGAL_PACKET_SIZE 1518 #define KSZ9477_MAX_FRAME_SIZE 9000 /* Regmap tables generation */ From 6b30cfa86ee703dc75743d56b078661753a18da3 Mon Sep 17 00:00:00 2001 From: Oleksij Rempel Date: Mon, 5 Dec 2022 06:22:31 +0100 Subject: [PATCH 1642/2002] net: dsa: microchip: enable MTU normalization for KSZ8795 and KSZ9477 compatible switches KSZ8795 and KSZ9477 compatible series of switches use global max frame size configuration register. So, enable MTU normalization for this reason. Signed-off-by: Oleksij Rempel Acked-by: Arun Ramadoss Reviewed-by: Vladimir Oltean Signed-off-by: Paolo Abeni --- drivers/net/dsa/microchip/ksz8795.c | 2 ++ drivers/net/dsa/microchip/ksz9477.c | 2 ++ 2 files changed, 4 insertions(+) diff --git a/drivers/net/dsa/microchip/ksz8795.c b/drivers/net/dsa/microchip/ksz8795.c index d01bfd609130..060e41b9b6ef 100644 --- a/drivers/net/dsa/microchip/ksz8795.c +++ b/drivers/net/dsa/microchip/ksz8795.c @@ -1357,6 +1357,8 @@ int ksz8_setup(struct dsa_switch *ds) struct ksz_device *dev = ds->priv; int i; + ds->mtu_enforcement_ingress = true; + ksz_cfg(dev, S_REPLACE_VID_CTRL, SW_FLOW_CTRL, true); /* Enable automatic fast aging when link changed detected. */ diff --git a/drivers/net/dsa/microchip/ksz9477.c b/drivers/net/dsa/microchip/ksz9477.c index f6e7968ab105..47b54ecf2c6f 100644 --- a/drivers/net/dsa/microchip/ksz9477.c +++ b/drivers/net/dsa/microchip/ksz9477.c @@ -1134,6 +1134,8 @@ int ksz9477_setup(struct dsa_switch *ds) struct ksz_device *dev = ds->priv; int ret = 0; + ds->mtu_enforcement_ingress = true; + /* Required for port partitioning. */ ksz9477_cfg32(dev, REG_SW_QM_CTRL__4, UNICAST_VLAN_BOUNDARY, true); From 55a952eef70a51340afdf080ecf81dd2b7bbf952 Mon Sep 17 00:00:00 2001 From: Oleksij Rempel Date: Mon, 5 Dec 2022 06:22:32 +0100 Subject: [PATCH 1643/2002] net: dsa: microchip: ksz8: move all DSA configurations to one location To make the code more comparable to KSZ9477 code, move DSA configurations to the same location. Signed-off-by: Oleksij Rempel Acked-by: Arun Ramadoss Reviewed-by: Vladimir Oltean Signed-off-by: Paolo Abeni --- drivers/net/dsa/microchip/ksz8795.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/drivers/net/dsa/microchip/ksz8795.c b/drivers/net/dsa/microchip/ksz8795.c index 060e41b9b6ef..003b0ac2854c 100644 --- a/drivers/net/dsa/microchip/ksz8795.c +++ b/drivers/net/dsa/microchip/ksz8795.c @@ -1359,6 +1359,16 @@ int ksz8_setup(struct dsa_switch *ds) ds->mtu_enforcement_ingress = true; + /* We rely on software untagging on the CPU port, so that we + * can support both tagged and untagged VLANs + */ + ds->untag_bridge_pvid = true; + + /* VLAN filtering is partly controlled by the global VLAN + * Enable flag + */ + ds->vlan_filtering_is_global = true; + ksz_cfg(dev, S_REPLACE_VID_CTRL, SW_FLOW_CTRL, true); /* Enable automatic fast aging when link changed detected. */ @@ -1418,16 +1428,6 @@ int ksz8_switch_init(struct ksz_device *dev) dev->phy_port_cnt = dev->info->port_cnt - 1; dev->port_mask = (BIT(dev->phy_port_cnt) - 1) | dev->info->cpu_ports; - /* We rely on software untagging on the CPU port, so that we - * can support both tagged and untagged VLANs - */ - dev->ds->untag_bridge_pvid = true; - - /* VLAN filtering is partly controlled by the global VLAN - * Enable flag - */ - dev->ds->vlan_filtering_is_global = true; - return 0; } From f2e664ad503d4e5ce7c42a0862ab164331a0ef37 Mon Sep 17 00:00:00 2001 From: Rakesh Babu Saladi Date: Mon, 5 Dec 2022 12:35:18 +0530 Subject: [PATCH 1644/2002] octeontx2-af: Support variable number of lmacs Most of the code in CGX/RPM driver assumes that max lmacs per given MAC as always, 4 and the number of MAC blocks also as 4. With this assumption, the max number of interfaces supported is hardcoded to 16. This creates a problem as next gen CN10KB silicon MAC supports 8 lmacs per MAC block. This patch solves the problem by using "max lmac per MAC block" value from constant csrs and uses cgx_cnt_max value which is populated based number of MAC blocks supported by silicon. Signed-off-by: Rakesh Babu Saladi Signed-off-by: Hariprasad Kelam Signed-off-by: Sunil Kovvuri Goutham Signed-off-by: Paolo Abeni --- .../net/ethernet/marvell/octeontx2/af/cgx.c | 35 ++++++++----------- .../net/ethernet/marvell/octeontx2/af/cgx.h | 6 ++-- .../marvell/octeontx2/af/lmac_common.h | 5 ++- .../net/ethernet/marvell/octeontx2/af/rvu.h | 2 +- .../ethernet/marvell/octeontx2/af/rvu_cgx.c | 26 ++++++++------ .../marvell/octeontx2/af/rvu_debugfs.c | 2 +- .../ethernet/marvell/octeontx2/af/rvu_nix.c | 2 +- .../marvell/octeontx2/af/rvu_npc_hash.c | 4 ++- 8 files changed, 42 insertions(+), 40 deletions(-) diff --git a/drivers/net/ethernet/marvell/octeontx2/af/cgx.c b/drivers/net/ethernet/marvell/octeontx2/af/cgx.c index c8724bfa86b0..fa5a1e88cb84 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/cgx.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/cgx.c @@ -78,7 +78,7 @@ static bool is_dev_rpm(void *cgxd) bool is_lmac_valid(struct cgx *cgx, int lmac_id) { - if (!cgx || lmac_id < 0 || lmac_id >= MAX_LMAC_PER_CGX) + if (!cgx || lmac_id < 0 || lmac_id >= cgx->max_lmac_per_mac) return false; return test_bit(lmac_id, &cgx->lmac_bmap); } @@ -90,7 +90,7 @@ static int get_sequence_id_of_lmac(struct cgx *cgx, int lmac_id) { int tmp, id = 0; - for_each_set_bit(tmp, &cgx->lmac_bmap, MAX_LMAC_PER_CGX) { + for_each_set_bit(tmp, &cgx->lmac_bmap, cgx->max_lmac_per_mac) { if (tmp == lmac_id) break; id++; @@ -121,7 +121,7 @@ u64 cgx_read(struct cgx *cgx, u64 lmac, u64 offset) struct lmac *lmac_pdata(u8 lmac_id, struct cgx *cgx) { - if (!cgx || lmac_id >= MAX_LMAC_PER_CGX) + if (!cgx || lmac_id >= cgx->max_lmac_per_mac) return NULL; return cgx->lmac_idmap[lmac_id]; @@ -1395,7 +1395,7 @@ int cgx_get_fwdata_base(u64 *base) if (!cgx) return -ENXIO; - first_lmac = find_first_bit(&cgx->lmac_bmap, MAX_LMAC_PER_CGX); + first_lmac = find_first_bit(&cgx->lmac_bmap, cgx->max_lmac_per_mac); req = FIELD_SET(CMDREG_ID, CGX_CMD_GET_FWD_BASE, req); err = cgx_fwi_cmd_generic(req, &resp, cgx, first_lmac); if (!err) @@ -1484,7 +1484,7 @@ static int cgx_fwi_link_change(struct cgx *cgx, int lmac_id, bool enable) static inline int cgx_fwi_read_version(u64 *resp, struct cgx *cgx) { - int first_lmac = find_first_bit(&cgx->lmac_bmap, MAX_LMAC_PER_CGX); + int first_lmac = find_first_bit(&cgx->lmac_bmap, cgx->max_lmac_per_mac); u64 req = 0; req = FIELD_SET(CMDREG_ID, CGX_CMD_GET_FW_VER, req); @@ -1522,7 +1522,7 @@ static void cgx_lmac_linkup_work(struct work_struct *work) int i, err; /* Do Link up for all the enabled lmacs */ - for_each_set_bit(i, &cgx->lmac_bmap, MAX_LMAC_PER_CGX) { + for_each_set_bit(i, &cgx->lmac_bmap, cgx->max_lmac_per_mac) { err = cgx_fwi_link_change(cgx, i, true); if (err) dev_info(dev, "cgx port %d:%d Link up command failed\n", @@ -1542,14 +1542,6 @@ int cgx_lmac_linkup_start(void *cgxd) return 0; } -static void cgx_lmac_get_fifolen(struct cgx *cgx) -{ - u64 cfg; - - cfg = cgx_read(cgx, 0, CGX_CONST); - cgx->mac_ops->fifo_len = FIELD_GET(CGX_CONST_RXFIFO_SIZE, cfg); -} - static int cgx_configure_interrupt(struct cgx *cgx, struct lmac *lmac, int cnt, bool req_free) { @@ -1604,17 +1596,14 @@ static int cgx_lmac_init(struct cgx *cgx) u64 lmac_list; int i, err; - cgx_lmac_get_fifolen(cgx); - - cgx->lmac_count = cgx->mac_ops->get_nr_lmacs(cgx); /* lmac_list specifies which lmacs are enabled * when bit n is set to 1, LMAC[n] is enabled */ if (cgx->mac_ops->non_contiguous_serdes_lane) lmac_list = cgx_read(cgx, 0, CGXX_CMRX_RX_LMACS) & 0xFULL; - if (cgx->lmac_count > MAX_LMAC_PER_CGX) - cgx->lmac_count = MAX_LMAC_PER_CGX; + if (cgx->lmac_count > cgx->max_lmac_per_mac) + cgx->lmac_count = cgx->max_lmac_per_mac; for (i = 0; i < cgx->lmac_count; i++) { lmac = kzalloc(sizeof(struct lmac), GFP_KERNEL); @@ -1692,7 +1681,7 @@ static int cgx_lmac_exit(struct cgx *cgx) } /* Free all lmac related resources */ - for_each_set_bit(i, &cgx->lmac_bmap, MAX_LMAC_PER_CGX) { + for_each_set_bit(i, &cgx->lmac_bmap, cgx->max_lmac_per_mac) { lmac = cgx->lmac_idmap[i]; if (!lmac) continue; @@ -1708,6 +1697,12 @@ static int cgx_lmac_exit(struct cgx *cgx) static void cgx_populate_features(struct cgx *cgx) { + u64 cfg; + + cfg = cgx_read(cgx, 0, CGX_CONST); + cgx->mac_ops->fifo_len = FIELD_GET(CGX_CONST_RXFIFO_SIZE, cfg); + cgx->max_lmac_per_mac = FIELD_GET(CGX_CONST_MAX_LMACS, cfg); + if (is_dev_rpm(cgx)) cgx->hw_features = (RVU_LMAC_FEAT_DMACF | RVU_MAC_RPM | RVU_LMAC_FEAT_FC | RVU_LMAC_FEAT_PTP); diff --git a/drivers/net/ethernet/marvell/octeontx2/af/cgx.h b/drivers/net/ethernet/marvell/octeontx2/af/cgx.h index 0b06788b8d80..ce66c7271e3a 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/cgx.h +++ b/drivers/net/ethernet/marvell/octeontx2/af/cgx.h @@ -18,11 +18,8 @@ /* PCI BAR nos */ #define PCI_CFG_REG_BAR_NUM 0 -#define CGX_ID_MASK 0x7 -#define MAX_LMAC_PER_CGX 4 +#define CGX_ID_MASK 0xF #define MAX_DMAC_ENTRIES_PER_CGX 32 -#define CGX_FIFO_LEN 65536 /* 64K for both Rx & Tx */ -#define CGX_OFFSET(x) ((x) * MAX_LMAC_PER_CGX) /* Registers */ #define CGXX_CMRX_CFG 0x00 @@ -57,6 +54,7 @@ #define CGXX_SCRATCH1_REG 0x1058 #define CGX_CONST 0x2000 #define CGX_CONST_RXFIFO_SIZE GENMASK_ULL(23, 0) +#define CGX_CONST_MAX_LMACS GENMASK_ULL(31, 24) #define CGXX_SPUX_CONTROL1 0x10000 #define CGXX_SPUX_LNX_FEC_CORR_BLOCKS 0x10700 #define CGXX_SPUX_LNX_FEC_UNCORR_BLOCKS 0x10800 diff --git a/drivers/net/ethernet/marvell/octeontx2/af/lmac_common.h b/drivers/net/ethernet/marvell/octeontx2/af/lmac_common.h index 52b6016789fa..697cfec74aa1 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/lmac_common.h +++ b/drivers/net/ethernet/marvell/octeontx2/af/lmac_common.h @@ -128,7 +128,10 @@ struct cgx { struct pci_dev *pdev; u8 cgx_id; u8 lmac_count; - struct lmac *lmac_idmap[MAX_LMAC_PER_CGX]; + /* number of LMACs per MAC could be 4 or 8 */ + u8 max_lmac_per_mac; +#define MAX_LMAC_COUNT 8 + struct lmac *lmac_idmap[MAX_LMAC_COUNT]; struct work_struct cgx_cmd_work; struct workqueue_struct *cgx_cmd_workq; struct list_head cgx_list; diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu.h b/drivers/net/ethernet/marvell/octeontx2/af/rvu.h index f718cbd32a94..04333f127282 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu.h +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu.h @@ -478,7 +478,7 @@ struct rvu { u8 cgx_mapped_pfs; u8 cgx_cnt_max; /* CGX port count max */ u8 *pf2cgxlmac_map; /* pf to cgx_lmac map */ - u16 *cgxlmac2pf_map; /* bitmap of mapped pfs for + u64 *cgxlmac2pf_map; /* bitmap of mapped pfs for * every cgx lmac port */ unsigned long pf_notify_bmap; /* Flags for PF notification */ diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_cgx.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_cgx.c index addc69f4b65c..8d9f9bbc262b 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_cgx.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_cgx.c @@ -55,8 +55,9 @@ bool is_mac_feature_supported(struct rvu *rvu, int pf, int feature) return (cgx_features_get(cgxd) & feature); } +#define CGX_OFFSET(x) ((x) * rvu->hw->lmac_per_cgx) /* Returns bitmap of mapped PFs */ -static u16 cgxlmac_to_pfmap(struct rvu *rvu, u8 cgx_id, u8 lmac_id) +static u64 cgxlmac_to_pfmap(struct rvu *rvu, u8 cgx_id, u8 lmac_id) { return rvu->cgxlmac2pf_map[CGX_OFFSET(cgx_id) + lmac_id]; } @@ -71,7 +72,8 @@ int cgxlmac_to_pf(struct rvu *rvu, int cgx_id, int lmac_id) if (!pfmap) return -ENODEV; else - return find_first_bit(&pfmap, 16); + return find_first_bit(&pfmap, + rvu->cgx_cnt_max * rvu->hw->lmac_per_cgx); } static u8 cgxlmac_id_to_bmap(u8 cgx_id, u8 lmac_id) @@ -129,14 +131,14 @@ static int rvu_map_cgx_lmac_pf(struct rvu *rvu) if (!cgx_cnt_max) return 0; - if (cgx_cnt_max > 0xF || MAX_LMAC_PER_CGX > 0xF) + if (cgx_cnt_max > 0xF || rvu->hw->lmac_per_cgx > 0xF) return -EINVAL; /* Alloc map table * An additional entry is required since PF id starts from 1 and * hence entry at offset 0 is invalid. */ - size = (cgx_cnt_max * MAX_LMAC_PER_CGX + 1) * sizeof(u8); + size = (cgx_cnt_max * rvu->hw->lmac_per_cgx + 1) * sizeof(u8); rvu->pf2cgxlmac_map = devm_kmalloc(rvu->dev, size, GFP_KERNEL); if (!rvu->pf2cgxlmac_map) return -ENOMEM; @@ -145,9 +147,10 @@ static int rvu_map_cgx_lmac_pf(struct rvu *rvu) memset(rvu->pf2cgxlmac_map, 0xFF, size); /* Reverse map table */ - rvu->cgxlmac2pf_map = devm_kzalloc(rvu->dev, - cgx_cnt_max * MAX_LMAC_PER_CGX * sizeof(u16), - GFP_KERNEL); + rvu->cgxlmac2pf_map = + devm_kzalloc(rvu->dev, + cgx_cnt_max * rvu->hw->lmac_per_cgx * sizeof(u64), + GFP_KERNEL); if (!rvu->cgxlmac2pf_map) return -ENOMEM; @@ -156,7 +159,7 @@ static int rvu_map_cgx_lmac_pf(struct rvu *rvu) if (!rvu_cgx_pdata(cgx, rvu)) continue; lmac_bmap = cgx_get_lmac_bmap(rvu_cgx_pdata(cgx, rvu)); - for_each_set_bit(iter, &lmac_bmap, MAX_LMAC_PER_CGX) { + for_each_set_bit(iter, &lmac_bmap, rvu->hw->lmac_per_cgx) { lmac = cgx_get_lmacid(rvu_cgx_pdata(cgx, rvu), iter); rvu->pf2cgxlmac_map[pf] = cgxlmac_id_to_bmap(cgx, lmac); @@ -235,7 +238,8 @@ static void cgx_notify_pfs(struct cgx_link_event *event, struct rvu *rvu) pfmap = cgxlmac_to_pfmap(rvu, event->cgx_id, event->lmac_id); do { - pfid = find_first_bit(&pfmap, 16); + pfid = find_first_bit(&pfmap, + rvu->cgx_cnt_max * rvu->hw->lmac_per_cgx); clear_bit(pfid, &pfmap); /* check if notification is enabled */ @@ -310,7 +314,7 @@ static int cgx_lmac_event_handler_init(struct rvu *rvu) if (!cgxd) continue; lmac_bmap = cgx_get_lmac_bmap(cgxd); - for_each_set_bit(lmac, &lmac_bmap, MAX_LMAC_PER_CGX) { + for_each_set_bit(lmac, &lmac_bmap, rvu->hw->lmac_per_cgx) { err = cgx_lmac_evh_register(&cb, cgxd, lmac); if (err) dev_err(rvu->dev, @@ -396,7 +400,7 @@ int rvu_cgx_exit(struct rvu *rvu) if (!cgxd) continue; lmac_bmap = cgx_get_lmac_bmap(cgxd); - for_each_set_bit(lmac, &lmac_bmap, MAX_LMAC_PER_CGX) + for_each_set_bit(lmac, &lmac_bmap, rvu->hw->lmac_per_cgx) cgx_lmac_evh_unregister(cgxd, lmac); } diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_debugfs.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_debugfs.c index 0eb3085c4c21..fa280ebd3052 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_debugfs.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_debugfs.c @@ -2613,7 +2613,7 @@ static void rvu_dbg_cgx_init(struct rvu *rvu) rvu->rvu_dbg.cgx = debugfs_create_dir(dname, rvu->rvu_dbg.cgx_root); - for_each_set_bit(lmac_id, &lmac_bmap, MAX_LMAC_PER_CGX) { + for_each_set_bit(lmac_id, &lmac_bmap, rvu->hw->lmac_per_cgx) { /* lmac debugfs dir */ sprintf(dname, "lmac%d", lmac_id); rvu->rvu_dbg.lmac = diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c index a62c1b322012..de489e7366da 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c @@ -4109,7 +4109,7 @@ static void nix_link_config(struct rvu *rvu, int blkaddr, /* Get LMAC id's from bitmap */ lmac_bmap = cgx_get_lmac_bmap(rvu_cgx_pdata(cgx, rvu)); - for_each_set_bit(iter, &lmac_bmap, MAX_LMAC_PER_CGX) { + for_each_set_bit(iter, &lmac_bmap, rvu->hw->lmac_per_cgx) { lmac_fifo_len = rvu_cgx_get_lmac_fifolen(rvu, cgx, iter); if (!lmac_fifo_len) { dev_err(rvu->dev, diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_hash.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_hash.c index 00aef8f5ac29..f69102d20c90 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_hash.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_hash.c @@ -1956,7 +1956,9 @@ int rvu_npc_exact_init(struct rvu *rvu) /* Install SDP drop rule */ drop_mcam_idx = &table->num_drop_rules; - max_lmac_cnt = rvu->cgx_cnt_max * MAX_LMAC_PER_CGX + PF_CGXMAP_BASE; + max_lmac_cnt = rvu->cgx_cnt_max * rvu->hw->lmac_per_cgx + + PF_CGXMAP_BASE; + for (i = PF_CGXMAP_BASE; i < max_lmac_cnt; i++) { if (rvu->pf2cgxlmac_map[i] == 0xFF) continue; From b9d0fedc6234011d67f6e2ba0ae1a8de6beca010 Mon Sep 17 00:00:00 2001 From: Hariprasad Kelam Date: Mon, 5 Dec 2022 12:35:19 +0530 Subject: [PATCH 1645/2002] octeontx2-af: cn10kb: Add RPM_USX MAC support OcteonTx2's next gen platform the CN10KB has RPM_USX MAC which has a different serdes when compared to RPM MAC. Though the underlying HW is different, the CSR interface has been designed largely inline with RPM MAC, with few exceptions though. So we are using the same CGX driver for RPM_USX MAC as well and will have a different set of APIs for RPM_USX where ever necessary. The RPM and RPM_USX blocks support a different number of LMACS. RPM_USX support 8 LMACS per MAC block whereas legacy RPM supports only 4 LMACS per MAC. with this RPM_USX support double the number of DMAC filters and fifo size. This patch adds initial support for CN10KB's RPM_USX MAC i.e registering the driver and defining MAC operations (mac_ops). Adds the logic to configure internal loopback and pause frames and assign FIFO length to LMACS. Kernel reads lmac features like lmac type, autoneg, etc from shared firmware data this structure only supports 4 lmacs per MAC, this patch extends this structure to accommodate 8 lmacs. Signed-off-by: Hariprasad Kelam Signed-off-by: Sunil Kovvuri Goutham Signed-off-by: Paolo Abeni --- .../net/ethernet/marvell/octeontx2/af/cgx.c | 38 +++- .../net/ethernet/marvell/octeontx2/af/cgx.h | 3 +- .../marvell/octeontx2/af/lmac_common.h | 7 +- .../net/ethernet/marvell/octeontx2/af/rpm.c | 204 ++++++++++++++++-- .../net/ethernet/marvell/octeontx2/af/rpm.h | 22 ++ .../net/ethernet/marvell/octeontx2/af/rvu.h | 10 +- .../ethernet/marvell/octeontx2/af/rvu_cgx.c | 19 +- .../ethernet/marvell/octeontx2/af/rvu_nix.c | 8 +- 8 files changed, 270 insertions(+), 41 deletions(-) diff --git a/drivers/net/ethernet/marvell/octeontx2/af/cgx.c b/drivers/net/ethernet/marvell/octeontx2/af/cgx.c index fa5a1e88cb84..242c5b0eb15d 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/cgx.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/cgx.c @@ -64,6 +64,7 @@ static int cgx_fwi_link_change(struct cgx *cgx, int lmac_id, bool en); static const struct pci_device_id cgx_id_table[] = { { PCI_DEVICE(PCI_VENDOR_ID_CAVIUM, PCI_DEVID_OCTEONTX2_CGX) }, { PCI_DEVICE(PCI_VENDOR_ID_CAVIUM, PCI_DEVID_CN10K_RPM) }, + { PCI_DEVICE(PCI_VENDOR_ID_CAVIUM, PCI_DEVID_CN10KB_RPM) }, { 0, } /* end of table */ }; @@ -73,7 +74,8 @@ static bool is_dev_rpm(void *cgxd) { struct cgx *cgx = cgxd; - return (cgx->pdev->device == PCI_DEVID_CN10K_RPM); + return (cgx->pdev->device == PCI_DEVID_CN10K_RPM) || + (cgx->pdev->device == PCI_DEVID_CN10KB_RPM); } bool is_lmac_valid(struct cgx *cgx, int lmac_id) @@ -485,7 +487,7 @@ int cgx_set_pkind(void *cgxd, u8 lmac_id, int pkind) if (!is_lmac_valid(cgx, lmac_id)) return -ENODEV; - cgx_write(cgx, lmac_id, CGXX_CMRX_RX_ID_MAP, (pkind & 0x3F)); + cgx_write(cgx, lmac_id, cgx->mac_ops->rxid_map_offset, (pkind & 0x3F)); return 0; } @@ -1224,7 +1226,7 @@ static inline void link_status_user_format(u64 lstat, linfo->speed = cgx_speed_mbps[FIELD_GET(RESP_LINKSTAT_SPEED, lstat)]; linfo->an = FIELD_GET(RESP_LINKSTAT_AN, lstat); linfo->fec = FIELD_GET(RESP_LINKSTAT_FEC, lstat); - linfo->lmac_type_id = cgx_get_lmac_type(cgx, lmac_id); + linfo->lmac_type_id = FIELD_GET(RESP_LINKSTAT_LMAC_TYPE, lstat); lmac_string = cgx_lmactype_string[linfo->lmac_type_id]; strncpy(linfo->lmac_type, lmac_string, LMACTYPE_STR_LEN - 1); } @@ -1599,8 +1601,14 @@ static int cgx_lmac_init(struct cgx *cgx) /* lmac_list specifies which lmacs are enabled * when bit n is set to 1, LMAC[n] is enabled */ - if (cgx->mac_ops->non_contiguous_serdes_lane) - lmac_list = cgx_read(cgx, 0, CGXX_CMRX_RX_LMACS) & 0xFULL; + if (cgx->mac_ops->non_contiguous_serdes_lane) { + if (is_dev_rpm2(cgx)) + lmac_list = + cgx_read(cgx, 0, RPM2_CMRX_RX_LMACS) & 0xFFULL; + else + lmac_list = + cgx_read(cgx, 0, CGXX_CMRX_RX_LMACS) & 0xFULL; + } if (cgx->lmac_count > cgx->max_lmac_per_mac) cgx->lmac_count = cgx->max_lmac_per_mac; @@ -1624,7 +1632,9 @@ static int cgx_lmac_init(struct cgx *cgx) lmac->cgx = cgx; lmac->mac_to_index_bmap.max = - MAX_DMAC_ENTRIES_PER_CGX / cgx->lmac_count; + cgx->mac_ops->dmac_filter_count / + cgx->lmac_count; + err = rvu_alloc_bitmap(&lmac->mac_to_index_bmap); if (err) goto err_name_free; @@ -1711,6 +1721,15 @@ static void cgx_populate_features(struct cgx *cgx) RVU_LMAC_FEAT_PTP | RVU_LMAC_FEAT_DMACF); } +static u8 cgx_get_rxid_mapoffset(struct cgx *cgx) +{ + if (cgx->pdev->subsystem_device == PCI_SUBSYS_DEVID_CNF10KB_RPM || + is_dev_rpm2(cgx)) + return 0x80; + else + return 0x60; +} + static struct mac_ops cgx_mac_ops = { .name = "cgx", .csr_offset = 0, @@ -1723,6 +1742,7 @@ static struct mac_ops cgx_mac_ops = { .non_contiguous_serdes_lane = false, .rx_stats_cnt = 9, .tx_stats_cnt = 18, + .dmac_filter_count = 32, .get_nr_lmacs = cgx_get_nr_lmacs, .get_lmac_type = cgx_get_lmac_type, .lmac_fifo_len = cgx_get_lmac_fifo_len, @@ -1754,11 +1774,13 @@ static int cgx_probe(struct pci_dev *pdev, const struct pci_device_id *id) pci_set_drvdata(pdev, cgx); /* Use mac_ops to get MAC specific features */ - if (pdev->device == PCI_DEVID_CN10K_RPM) - cgx->mac_ops = rpm_get_mac_ops(); + if (is_dev_rpm(cgx)) + cgx->mac_ops = rpm_get_mac_ops(cgx); else cgx->mac_ops = &cgx_mac_ops; + cgx->mac_ops->rxid_map_offset = cgx_get_rxid_mapoffset(cgx); + err = pci_enable_device(pdev); if (err) { dev_err(dev, "Failed to enable PCI device\n"); diff --git a/drivers/net/ethernet/marvell/octeontx2/af/cgx.h b/drivers/net/ethernet/marvell/octeontx2/af/cgx.h index ce66c7271e3a..fb2d37676d84 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/cgx.h +++ b/drivers/net/ethernet/marvell/octeontx2/af/cgx.h @@ -19,7 +19,6 @@ #define PCI_CFG_REG_BAR_NUM 0 #define CGX_ID_MASK 0xF -#define MAX_DMAC_ENTRIES_PER_CGX 32 /* Registers */ #define CGXX_CMRX_CFG 0x00 @@ -53,7 +52,7 @@ #define CGXX_SCRATCH0_REG 0x1050 #define CGXX_SCRATCH1_REG 0x1058 #define CGX_CONST 0x2000 -#define CGX_CONST_RXFIFO_SIZE GENMASK_ULL(23, 0) +#define CGX_CONST_RXFIFO_SIZE GENMASK_ULL(55, 32) #define CGX_CONST_MAX_LMACS GENMASK_ULL(31, 24) #define CGXX_SPUX_CONTROL1 0x10000 #define CGXX_SPUX_LNX_FEC_CORR_BLOCKS 0x10700 diff --git a/drivers/net/ethernet/marvell/octeontx2/af/lmac_common.h b/drivers/net/ethernet/marvell/octeontx2/af/lmac_common.h index 697cfec74aa1..386fb73ad366 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/lmac_common.h +++ b/drivers/net/ethernet/marvell/octeontx2/af/lmac_common.h @@ -75,6 +75,11 @@ struct mac_ops { /* RPM & CGX differs in number of Receive/transmit stats */ u8 rx_stats_cnt; u8 tx_stats_cnt; + /* Unlike CN10K which shares same CSR offset with CGX + * CNF10KB has different csr offset + */ + u64 rxid_map_offset; + u8 dmac_filter_count; /* Incase of RPM get number of lmacs from RPMX_CMR_RX_LMACS[LMAC_EXIST] * number of setbits in lmac_exist tells number of lmacs */ @@ -153,6 +158,6 @@ struct lmac *lmac_pdata(u8 lmac_id, struct cgx *cgx); int cgx_fwi_cmd_send(u64 req, u64 *resp, struct lmac *lmac); int cgx_fwi_cmd_generic(u64 req, u64 *resp, struct cgx *cgx, int lmac_id); bool is_lmac_valid(struct cgx *cgx, int lmac_id); -struct mac_ops *rpm_get_mac_ops(void); +struct mac_ops *rpm_get_mac_ops(struct cgx *cgx); #endif /* LMAC_COMMON_H */ diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rpm.c b/drivers/net/ethernet/marvell/octeontx2/af/rpm.c index a70e1153fa04..a15a59d5dff8 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rpm.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/rpm.c @@ -8,7 +8,7 @@ #include "cgx.h" #include "lmac_common.h" -static struct mac_ops rpm_mac_ops = { +static struct mac_ops rpm_mac_ops = { .name = "rpm", .csr_offset = 0x4e00, .lmac_offset = 20, @@ -20,6 +20,7 @@ static struct mac_ops rpm_mac_ops = { .non_contiguous_serdes_lane = true, .rx_stats_cnt = 43, .tx_stats_cnt = 34, + .dmac_filter_count = 32, .get_nr_lmacs = rpm_get_nr_lmacs, .get_lmac_type = rpm_get_lmac_type, .lmac_fifo_len = rpm_get_lmac_fifo_len, @@ -37,9 +38,49 @@ static struct mac_ops rpm_mac_ops = { .mac_get_pfc_frm_cfg = rpm_lmac_get_pfc_frm_cfg, }; -struct mac_ops *rpm_get_mac_ops(void) +static struct mac_ops rpm2_mac_ops = { + .name = "rpm", + .csr_offset = RPM2_CSR_OFFSET, + .lmac_offset = 20, + .int_register = RPM2_CMRX_SW_INT, + .int_set_reg = RPM2_CMRX_SW_INT_ENA_W1S, + .irq_offset = 1, + .int_ena_bit = BIT_ULL(0), + .lmac_fwi = RPM_LMAC_FWI, + .non_contiguous_serdes_lane = true, + .rx_stats_cnt = 43, + .tx_stats_cnt = 34, + .dmac_filter_count = 64, + .get_nr_lmacs = rpm2_get_nr_lmacs, + .get_lmac_type = rpm_get_lmac_type, + .lmac_fifo_len = rpm2_get_lmac_fifo_len, + .mac_lmac_intl_lbk = rpm_lmac_internal_loopback, + .mac_get_rx_stats = rpm_get_rx_stats, + .mac_get_tx_stats = rpm_get_tx_stats, + .mac_enadis_rx_pause_fwding = rpm_lmac_enadis_rx_pause_fwding, + .mac_get_pause_frm_status = rpm_lmac_get_pause_frm_status, + .mac_enadis_pause_frm = rpm_lmac_enadis_pause_frm, + .mac_pause_frm_config = rpm_lmac_pause_frm_config, + .mac_enadis_ptp_config = rpm_lmac_ptp_config, + .mac_rx_tx_enable = rpm_lmac_rx_tx_enable, + .mac_tx_enable = rpm_lmac_tx_enable, + .pfc_config = rpm_lmac_pfc_config, + .mac_get_pfc_frm_cfg = rpm_lmac_get_pfc_frm_cfg, +}; + +bool is_dev_rpm2(void *rpmd) { - return &rpm_mac_ops; + rpm_t *rpm = rpmd; + + return (rpm->pdev->device == PCI_DEVID_CN10KB_RPM); +} + +struct mac_ops *rpm_get_mac_ops(rpm_t *rpm) +{ + if (is_dev_rpm2(rpm)) + return &rpm2_mac_ops; + else + return &rpm_mac_ops; } static void rpm_write(rpm_t *rpm, u64 lmac, u64 offset, u64 val) @@ -52,6 +93,16 @@ static u64 rpm_read(rpm_t *rpm, u64 lmac, u64 offset) return cgx_read(rpm, lmac, offset); } +/* Read HW major version to determine RPM + * MAC type 100/USX + */ +static bool is_mac_rpmusx(void *rpmd) +{ + rpm_t *rpm = rpmd; + + return rpm_read(rpm, 0, RPMX_CONST1) & 0x700ULL; +} + int rpm_get_nr_lmacs(void *rpmd) { rpm_t *rpm = rpmd; @@ -59,6 +110,13 @@ int rpm_get_nr_lmacs(void *rpmd) return hweight8(rpm_read(rpm, 0, CGXX_CMRX_RX_LMACS) & 0xFULL); } +int rpm2_get_nr_lmacs(void *rpmd) +{ + rpm_t *rpm = rpmd; + + return hweight8(rpm_read(rpm, 0, RPM2_CMRX_RX_LMACS) & 0xFFULL); +} + int rpm_lmac_tx_enable(void *rpmd, int lmac_id, bool enable) { rpm_t *rpm = rpmd; @@ -222,6 +280,46 @@ static void rpm_cfg_pfc_quanta_thresh(rpm_t *rpm, int lmac_id, } } +static void rpm2_lmac_cfg_bp(rpm_t *rpm, int lmac_id, u8 tx_pause, u8 rx_pause) +{ + u64 cfg; + + cfg = rpm_read(rpm, lmac_id, RPM2_CMR_RX_OVR_BP); + if (tx_pause) { + /* Configure CL0 Pause Quanta & threshold + * for 802.3X frames + */ + rpm_cfg_pfc_quanta_thresh(rpm, lmac_id, 1, true); + cfg &= ~RPM2_CMR_RX_OVR_BP_EN; + } else { + /* Disable all Pause Quanta & threshold values */ + rpm_cfg_pfc_quanta_thresh(rpm, lmac_id, 0xffff, false); + cfg |= RPM2_CMR_RX_OVR_BP_EN; + cfg &= ~RPM2_CMR_RX_OVR_BP_BP; + } + rpm_write(rpm, lmac_id, RPM2_CMR_RX_OVR_BP, cfg); +} + +static void rpm_lmac_cfg_bp(rpm_t *rpm, int lmac_id, u8 tx_pause, u8 rx_pause) +{ + u64 cfg; + + cfg = rpm_read(rpm, 0, RPMX_CMR_RX_OVR_BP); + if (tx_pause) { + /* Configure CL0 Pause Quanta & threshold for + * 802.3X frames + */ + rpm_cfg_pfc_quanta_thresh(rpm, lmac_id, 1, true); + cfg &= ~RPMX_CMR_RX_OVR_BP_EN(lmac_id); + } else { + /* Disable all Pause Quanta & threshold values */ + rpm_cfg_pfc_quanta_thresh(rpm, lmac_id, 0xffff, false); + cfg |= RPMX_CMR_RX_OVR_BP_EN(lmac_id); + cfg &= ~RPMX_CMR_RX_OVR_BP_BP(lmac_id); + } + rpm_write(rpm, 0, RPMX_CMR_RX_OVR_BP, cfg); +} + int rpm_lmac_enadis_pause_frm(void *rpmd, int lmac_id, u8 tx_pause, u8 rx_pause) { @@ -243,18 +341,11 @@ int rpm_lmac_enadis_pause_frm(void *rpmd, int lmac_id, u8 tx_pause, cfg |= tx_pause ? 0x0 : RPMX_MTI_MAC100X_COMMAND_CONFIG_TX_P_DISABLE; rpm_write(rpm, lmac_id, RPMX_MTI_MAC100X_COMMAND_CONFIG, cfg); - cfg = rpm_read(rpm, 0, RPMX_CMR_RX_OVR_BP); - if (tx_pause) { - /* Configure CL0 Pause Quanta & threshold for 802.3X frames */ - rpm_cfg_pfc_quanta_thresh(rpm, lmac_id, 1, true); - cfg &= ~RPMX_CMR_RX_OVR_BP_EN(lmac_id); - } else { - /* Disable all Pause Quanta & threshold values */ - rpm_cfg_pfc_quanta_thresh(rpm, lmac_id, 0xffff, false); - cfg |= RPMX_CMR_RX_OVR_BP_EN(lmac_id); - cfg &= ~RPMX_CMR_RX_OVR_BP_BP(lmac_id); - } - rpm_write(rpm, 0, RPMX_CMR_RX_OVR_BP, cfg); + if (is_dev_rpm2(rpm)) + rpm2_lmac_cfg_bp(rpm, lmac_id, tx_pause, rx_pause); + else + rpm_lmac_cfg_bp(rpm, lmac_id, tx_pause, rx_pause); + return 0; } @@ -278,13 +369,16 @@ void rpm_lmac_pause_frm_config(void *rpmd, int lmac_id, bool enable) cfg |= RPMX_MTI_MAC100X_COMMAND_CONFIG_TX_P_DISABLE; rpm_write(rpm, lmac_id, RPMX_MTI_MAC100X_COMMAND_CONFIG, cfg); + /* Enable channel mask for all LMACS */ + if (is_dev_rpm2(rpm)) + rpm_write(rpm, lmac_id, RPM2_CMR_CHAN_MSK_OR, 0xffff); + else + rpm_write(rpm, 0, RPMX_CMR_CHAN_MSK_OR, ~0ULL); + /* Disable all PFC classes */ cfg = rpm_read(rpm, lmac_id, RPMX_CMRX_PRT_CBFC_CTL); cfg = FIELD_SET(RPM_PFC_CLASS_MASK, 0, cfg); rpm_write(rpm, lmac_id, RPMX_CMRX_PRT_CBFC_CTL, cfg); - - /* Enable channel mask for all LMACS */ - rpm_write(rpm, 0, RPMX_CMR_CHAN_MSK_OR, ~0ULL); } int rpm_get_rx_stats(void *rpmd, int lmac_id, int idx, u64 *rx_stat) @@ -292,7 +386,7 @@ int rpm_get_rx_stats(void *rpmd, int lmac_id, int idx, u64 *rx_stat) rpm_t *rpm = rpmd; u64 val_lo, val_hi; - if (!rpm || lmac_id >= rpm->lmac_count) + if (!is_lmac_valid(rpm, lmac_id)) return -ENODEV; mutex_lock(&rpm->lock); @@ -320,7 +414,7 @@ int rpm_get_tx_stats(void *rpmd, int lmac_id, int idx, u64 *tx_stat) rpm_t *rpm = rpmd; u64 val_lo, val_hi; - if (!rpm || lmac_id >= rpm->lmac_count) + if (!is_lmac_valid(rpm, lmac_id)) return -ENODEV; mutex_lock(&rpm->lock); @@ -380,13 +474,71 @@ u32 rpm_get_lmac_fifo_len(void *rpmd, int lmac_id) return 0; } +static int rpmusx_lmac_internal_loopback(rpm_t *rpm, int lmac_id, bool enable) +{ + u64 cfg; + + cfg = rpm_read(rpm, lmac_id, RPM2_USX_PCSX_CONTROL1); + + if (enable) + cfg |= RPM2_USX_PCS_LBK; + else + cfg &= ~RPM2_USX_PCS_LBK; + rpm_write(rpm, lmac_id, RPM2_USX_PCSX_CONTROL1, cfg); + + return 0; +} + +u32 rpm2_get_lmac_fifo_len(void *rpmd, int lmac_id) +{ + u64 hi_perf_lmac, lmac_info; + rpm_t *rpm = rpmd; + u8 num_lmacs; + u32 fifo_len; + + lmac_info = rpm_read(rpm, 0, RPM2_CMRX_RX_LMACS); + /* LMACs are divided into two groups and each group + * gets half of the FIFO + * Group0 lmac_id range {0..3} + * Group1 lmac_id range {4..7} + */ + fifo_len = rpm->mac_ops->fifo_len / 2; + + if (lmac_id < 4) { + num_lmacs = hweight8(lmac_info & 0xF); + hi_perf_lmac = (lmac_info >> 8) & 0x3ULL; + } else { + num_lmacs = hweight8(lmac_info & 0xF0); + hi_perf_lmac = (lmac_info >> 10) & 0x3ULL; + hi_perf_lmac += 4; + } + + switch (num_lmacs) { + case 1: + return fifo_len; + case 2: + return fifo_len / 2; + case 3: + /* LMAC marked as hi_perf gets half of the FIFO + * and rest 1/4th + */ + if (lmac_id == hi_perf_lmac) + return fifo_len / 2; + return fifo_len / 4; + case 4: + default: + return fifo_len / 4; + } + return 0; +} + int rpm_lmac_internal_loopback(void *rpmd, int lmac_id, bool enable) { rpm_t *rpm = rpmd; u8 lmac_type; u64 cfg; - if (!rpm || lmac_id >= rpm->lmac_count) + if (!is_lmac_valid(rpm, lmac_id)) return -ENODEV; lmac_type = rpm->mac_ops->get_lmac_type(rpm, lmac_id); @@ -395,6 +547,9 @@ int rpm_lmac_internal_loopback(void *rpmd, int lmac_id, bool enable) return 0; } + if (is_dev_rpm2(rpm) && is_mac_rpmusx(rpm)) + return rpmusx_lmac_internal_loopback(rpm, lmac_id, enable); + cfg = rpm_read(rpm, lmac_id, RPMX_MTI_PCS100X_CONTROL1); if (enable) @@ -439,8 +594,8 @@ void rpm_lmac_ptp_config(void *rpmd, int lmac_id, bool enable) int rpm_lmac_pfc_config(void *rpmd, int lmac_id, u8 tx_pause, u8 rx_pause, u16 pfc_en) { + u64 cfg, class_en, pfc_class_mask_cfg; rpm_t *rpm = rpmd; - u64 cfg, class_en; if (!is_lmac_valid(rpm, lmac_id)) return -ENODEV; @@ -476,7 +631,10 @@ int rpm_lmac_pfc_config(void *rpmd, int lmac_id, u8 tx_pause, u8 rx_pause, u16 p rpm_write(rpm, lmac_id, RPMX_MTI_MAC100X_COMMAND_CONFIG, cfg); - rpm_write(rpm, lmac_id, RPMX_CMRX_PRT_CBFC_CTL, class_en); + pfc_class_mask_cfg = is_dev_rpm2(rpm) ? RPM2_CMRX_PRT_CBFC_CTL : + RPMX_CMRX_PRT_CBFC_CTL; + + rpm_write(rpm, lmac_id, pfc_class_mask_cfg, class_en); return 0; } diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rpm.h b/drivers/net/ethernet/marvell/octeontx2/af/rpm.h index 77f2ef9e1425..fc20a35bd8f9 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rpm.h +++ b/drivers/net/ethernet/marvell/octeontx2/af/rpm.h @@ -12,11 +12,14 @@ /* PCI device IDs */ #define PCI_DEVID_CN10K_RPM 0xA060 +#define PCI_SUBSYS_DEVID_CNF10KB_RPM 0xBC00 +#define PCI_DEVID_CN10KB_RPM 0xA09F /* Registers */ #define RPMX_CMRX_CFG 0x00 #define RPMX_RX_TS_PREPEND BIT_ULL(22) #define RPMX_TX_PTP_1S_SUPPORT BIT_ULL(17) +#define RPMX_CMRX_RX_ID_MAP 0x80 #define RPMX_CMRX_SW_INT 0x180 #define RPMX_CMRX_SW_INT_W1S 0x188 #define RPMX_CMRX_SW_INT_ENA_W1S 0x198 @@ -76,11 +79,28 @@ #define RPMX_MTI_MAC100X_XIF_MODE 0x8100 #define RPMX_ONESTEP_ENABLE BIT_ULL(5) #define RPMX_TS_BINARY_MODE BIT_ULL(11) +#define RPMX_CONST1 0x2008 + +/* CN10KB CSR Declaration */ +#define RPM2_CMRX_SW_INT 0x1b0 +#define RPM2_CMRX_SW_INT_ENA_W1S 0x1b8 +#define RPM2_CMR_CHAN_MSK_OR 0x3120 +#define RPM2_CMR_RX_OVR_BP_EN BIT_ULL(2) +#define RPM2_CMR_RX_OVR_BP_BP BIT_ULL(1) +#define RPM2_CMR_RX_OVR_BP 0x3130 +#define RPM2_CSR_OFFSET 0x3e00 +#define RPM2_CMRX_PRT_CBFC_CTL 0x6510 +#define RPM2_CMRX_RX_LMACS 0x100 +#define RPM2_CMRX_RX_LOGL_XON 0x3100 +#define RPM2_CMRX_RX_STAT2 0x3010 +#define RPM2_USX_PCSX_CONTROL1 0x80000 +#define RPM2_USX_PCS_LBK BIT_ULL(14) /* Function Declarations */ int rpm_get_nr_lmacs(void *rpmd); u8 rpm_get_lmac_type(void *rpmd, int lmac_id); u32 rpm_get_lmac_fifo_len(void *rpmd, int lmac_id); +u32 rpm2_get_lmac_fifo_len(void *rpmd, int lmac_id); int rpm_lmac_internal_loopback(void *rpmd, int lmac_id, bool enable); void rpm_lmac_enadis_rx_pause_fwding(void *rpmd, int lmac_id, bool enable); int rpm_lmac_get_pause_frm_status(void *cgxd, int lmac_id, u8 *tx_pause, @@ -97,4 +117,6 @@ int rpm_lmac_pfc_config(void *rpmd, int lmac_id, u8 tx_pause, u8 rx_pause, u16 pfc_en); int rpm_lmac_get_pfc_frm_cfg(void *rpmd, int lmac_id, u8 *tx_pause, u8 *rx_pause); +int rpm2_get_nr_lmacs(void *rpmd); +bool is_dev_rpm2(void *rpmd); #endif /* RPM_H */ diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu.h b/drivers/net/ethernet/marvell/octeontx2/af/rvu.h index 04333f127282..7f0a64731c67 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu.h +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu.h @@ -410,9 +410,15 @@ struct rvu_fwdata { u32 ptp_ext_tstamp; #define FWDATA_RESERVED_MEM 1022 u64 reserved[FWDATA_RESERVED_MEM]; -#define CGX_MAX 5 +#define CGX_MAX 9 #define CGX_LMACS_MAX 4 - struct cgx_lmac_fwdata_s cgx_fw_data[CGX_MAX][CGX_LMACS_MAX]; +#define CGX_LMACS_USX 8 + union { + struct cgx_lmac_fwdata_s + cgx_fw_data[CGX_MAX][CGX_LMACS_MAX]; + struct cgx_lmac_fwdata_s + cgx_fw_data_usx[CGX_MAX][CGX_LMACS_USX]; + }; /* Do not add new fields below this line */ }; diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_cgx.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_cgx.c index 8d9f9bbc262b..53580e0381c9 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_cgx.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_cgx.c @@ -472,6 +472,7 @@ void rvu_cgx_disable_dmac_entries(struct rvu *rvu, u16 pcifunc) { int pf = rvu_get_pf(pcifunc); int i = 0, lmac_count = 0; + struct mac_ops *mac_ops; u8 max_dmac_filters; u8 cgx_id, lmac_id; void *cgx_dev; @@ -487,7 +488,12 @@ void rvu_cgx_disable_dmac_entries(struct rvu *rvu, u16 pcifunc) rvu_get_cgx_lmac_id(rvu->pf2cgxlmac_map[pf], &cgx_id, &lmac_id); cgx_dev = cgx_get_pdata(cgx_id); lmac_count = cgx_get_lmac_cnt(cgx_dev); - max_dmac_filters = MAX_DMAC_ENTRIES_PER_CGX / lmac_count; + + mac_ops = get_mac_ops(cgx_dev); + if (!mac_ops) + return; + + max_dmac_filters = mac_ops->dmac_filter_count / lmac_count; for (i = 0; i < max_dmac_filters; i++) cgx_lmac_addr_del(cgx_id, lmac_id, i); @@ -1114,8 +1120,15 @@ int rvu_mbox_handler_cgx_get_aux_link_info(struct rvu *rvu, struct msg_req *req, rvu_get_cgx_lmac_id(rvu->pf2cgxlmac_map[pf], &cgx_id, &lmac_id); - memcpy(&rsp->fwdata, &rvu->fwdata->cgx_fw_data[cgx_id][lmac_id], - sizeof(struct cgx_lmac_fwdata_s)); + if (rvu->hw->lmac_per_cgx == CGX_LMACS_USX) + memcpy(&rsp->fwdata, + &rvu->fwdata->cgx_fw_data_usx[cgx_id][lmac_id], + sizeof(struct cgx_lmac_fwdata_s)); + else + memcpy(&rsp->fwdata, + &rvu->fwdata->cgx_fw_data[cgx_id][lmac_id], + sizeof(struct cgx_lmac_fwdata_s)); + return 0; } diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c index de489e7366da..6b8747ebc08c 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c @@ -3197,8 +3197,12 @@ static void rvu_get_lbk_link_max_frs(struct rvu *rvu, u16 *max_mtu) static void rvu_get_lmac_link_max_frs(struct rvu *rvu, u16 *max_mtu) { - /* RPM supports FIFO len 128 KB */ - if (rvu_cgx_get_fifolen(rvu) == 0x20000) + int fifo_size = rvu_cgx_get_fifolen(rvu); + + /* RPM supports FIFO len 128 KB and RPM2 supports double the + * FIFO len to accommodate 8 LMACS + */ + if (fifo_size == 0x20000 || fifo_size == 0x40000) *max_mtu = CN10K_LMAC_LINK_MAX_FRS; else *max_mtu = NIC_HW_MAX_FRS; From b441c4ac5ea37251d6acabe679215a828025004b Mon Sep 17 00:00:00 2001 From: Hariprasad Kelam Date: Mon, 5 Dec 2022 12:35:20 +0530 Subject: [PATCH 1646/2002] octeontx2-pf: ethtool: Implement get_fec_stats This patch registers a callback for get_fec_stats such that FEC stats can be queried from the below command "ethtool -I --show-fec eth0" Signed-off-by: Hariprasad Kelam Signed-off-by: Sunil Kovvuri Goutham Signed-off-by: Paolo Abeni --- .../marvell/octeontx2/nic/otx2_ethtool.c | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_ethtool.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_ethtool.c index 0eb74e8c553d..0f8d1a69139f 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_ethtool.c +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_ethtool.c @@ -1268,6 +1268,39 @@ end: return err; } +static void otx2_get_fec_stats(struct net_device *netdev, + struct ethtool_fec_stats *fec_stats) +{ + struct otx2_nic *pfvf = netdev_priv(netdev); + struct cgx_fw_data *rsp; + + otx2_update_lmac_fec_stats(pfvf); + + /* Report MAC FEC stats */ + fec_stats->corrected_blocks.total = pfvf->hw.cgx_fec_corr_blks; + fec_stats->uncorrectable_blocks.total = pfvf->hw.cgx_fec_uncorr_blks; + + rsp = otx2_get_fwdata(pfvf); + if (!IS_ERR(rsp) && rsp->fwdata.phy.misc.has_fec_stats && + !otx2_get_phy_fec_stats(pfvf)) { + /* Fetch fwdata again because it's been recently populated with + * latest PHY FEC stats. + */ + rsp = otx2_get_fwdata(pfvf); + if (!IS_ERR(rsp)) { + struct fec_stats_s *p = &rsp->fwdata.phy.fec_stats; + + if (pfvf->linfo.fec == OTX2_FEC_BASER) { + fec_stats->corrected_blocks.total = p->brfec_corr_blks; + fec_stats->uncorrectable_blocks.total = p->brfec_uncorr_blks; + } else { + fec_stats->corrected_blocks.total = p->rsfec_corr_cws; + fec_stats->uncorrectable_blocks.total = p->rsfec_uncorr_cws; + } + } + } +} + static const struct ethtool_ops otx2_ethtool_ops = { .supported_coalesce_params = ETHTOOL_COALESCE_USECS | ETHTOOL_COALESCE_MAX_FRAMES | @@ -1298,6 +1331,7 @@ static const struct ethtool_ops otx2_ethtool_ops = { .get_pauseparam = otx2_get_pauseparam, .set_pauseparam = otx2_set_pauseparam, .get_ts_info = otx2_get_ts_info, + .get_fec_stats = otx2_get_fec_stats, .get_fecparam = otx2_get_fecparam, .set_fecparam = otx2_set_fecparam, .get_link_ksettings = otx2_get_link_ksettings, From 84ad3642115dfc2f17b6bd98e0470f799b8411e3 Mon Sep 17 00:00:00 2001 From: Hariprasad Kelam Date: Mon, 5 Dec 2022 12:35:21 +0530 Subject: [PATCH 1647/2002] octeontx2-af: Add FEC stats for RPM/RPM_USX block CN10K silicon MAC block RPM and CN10KB silicon MAC block RPM_USX both support BASER and RSFEC modes. Also MAC (CGX) on OcteonTx2 silicon variants and MAC (RPM) on OcteonTx3 CN10K are different and FEC stats need to be read differently. CN10KB MAC block (RPM_USX) fec csr offsets are same as CN10K MAC block (RPM) mac_ops points to same fn(). Upper layer interface between RVU AF and PF netdev is kept same. Based on silicon variant appropriate fn() pointer is called to read FEC stats Signed-off-by: Hariprasad Kelam Signed-off-by: Sunil Kovvuri Goutham Signed-off-by: Paolo Abeni --- .../net/ethernet/marvell/octeontx2/af/cgx.c | 5 ++ .../marvell/octeontx2/af/lmac_common.h | 3 + .../net/ethernet/marvell/octeontx2/af/rpm.c | 58 +++++++++++++++++++ .../net/ethernet/marvell/octeontx2/af/rpm.h | 14 ++++- .../ethernet/marvell/octeontx2/af/rvu_cgx.c | 4 +- 5 files changed, 82 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/marvell/octeontx2/af/cgx.c b/drivers/net/ethernet/marvell/octeontx2/af/cgx.c index 242c5b0eb15d..b2b71fe80d61 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/cgx.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/cgx.c @@ -742,6 +742,10 @@ int cgx_get_fec_stats(void *cgxd, int lmac_id, struct cgx_fec_stats_rsp *rsp) if (!cgx || lmac_id >= cgx->lmac_count) return -ENODEV; + + if (cgx->lmac_idmap[lmac_id]->link_info.fec == OTX2_FEC_NONE) + return 0; + fec_stats_count = cgx_set_fec_stats_count(&cgx->lmac_idmap[lmac_id]->link_info); if (cgx->lmac_idmap[lmac_id]->link_info.fec == OTX2_FEC_BASER) { @@ -1749,6 +1753,7 @@ static struct mac_ops cgx_mac_ops = { .mac_lmac_intl_lbk = cgx_lmac_internal_loopback, .mac_get_rx_stats = cgx_get_rx_stats, .mac_get_tx_stats = cgx_get_tx_stats, + .get_fec_stats = cgx_get_fec_stats, .mac_enadis_rx_pause_fwding = cgx_lmac_enadis_rx_pause_fwding, .mac_get_pause_frm_status = cgx_lmac_get_pause_frm_status, .mac_enadis_pause_frm = cgx_lmac_enadis_pause_frm, diff --git a/drivers/net/ethernet/marvell/octeontx2/af/lmac_common.h b/drivers/net/ethernet/marvell/octeontx2/af/lmac_common.h index 386fb73ad366..39aaf0e4467d 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/lmac_common.h +++ b/drivers/net/ethernet/marvell/octeontx2/af/lmac_common.h @@ -126,6 +126,9 @@ struct mac_ops { int (*mac_get_pfc_frm_cfg)(void *cgxd, int lmac_id, u8 *tx_pause, u8 *rx_pause); + /* FEC stats */ + int (*get_fec_stats)(void *cgxd, int lmac_id, + struct cgx_fec_stats_rsp *rsp); }; struct cgx { diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rpm.c b/drivers/net/ethernet/marvell/octeontx2/af/rpm.c index a15a59d5dff8..de0d88dd10d6 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rpm.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/rpm.c @@ -27,6 +27,7 @@ static struct mac_ops rpm_mac_ops = { .mac_lmac_intl_lbk = rpm_lmac_internal_loopback, .mac_get_rx_stats = rpm_get_rx_stats, .mac_get_tx_stats = rpm_get_tx_stats, + .get_fec_stats = rpm_get_fec_stats, .mac_enadis_rx_pause_fwding = rpm_lmac_enadis_rx_pause_fwding, .mac_get_pause_frm_status = rpm_lmac_get_pause_frm_status, .mac_enadis_pause_frm = rpm_lmac_enadis_pause_frm, @@ -57,6 +58,7 @@ static struct mac_ops rpm2_mac_ops = { .mac_lmac_intl_lbk = rpm_lmac_internal_loopback, .mac_get_rx_stats = rpm_get_rx_stats, .mac_get_tx_stats = rpm_get_tx_stats, + .get_fec_stats = rpm_get_fec_stats, .mac_enadis_rx_pause_fwding = rpm_lmac_enadis_rx_pause_fwding, .mac_get_pause_frm_status = rpm_lmac_get_pause_frm_status, .mac_enadis_pause_frm = rpm_lmac_enadis_pause_frm, @@ -655,3 +657,59 @@ int rpm_lmac_get_pfc_frm_cfg(void *rpmd, int lmac_id, u8 *tx_pause, u8 *rx_paus return 0; } + +int rpm_get_fec_stats(void *rpmd, int lmac_id, struct cgx_fec_stats_rsp *rsp) +{ + u64 val_lo, val_hi; + rpm_t *rpm = rpmd; + u64 cfg; + + if (!is_lmac_valid(rpm, lmac_id)) + return -ENODEV; + + if (rpm->lmac_idmap[lmac_id]->link_info.fec == OTX2_FEC_NONE) + return 0; + + if (rpm->lmac_idmap[lmac_id]->link_info.fec == OTX2_FEC_BASER) { + val_lo = rpm_read(rpm, lmac_id, RPMX_MTI_FCFECX_VL0_CCW_LO); + val_hi = rpm_read(rpm, lmac_id, RPMX_MTI_FCFECX_CW_HI); + rsp->fec_corr_blks = (val_hi << 16 | val_lo); + + val_lo = rpm_read(rpm, lmac_id, RPMX_MTI_FCFECX_VL0_NCCW_LO); + val_hi = rpm_read(rpm, lmac_id, RPMX_MTI_FCFECX_CW_HI); + rsp->fec_uncorr_blks = (val_hi << 16 | val_lo); + + /* 50G uses 2 Physical serdes lines */ + if (rpm->lmac_idmap[lmac_id]->link_info.lmac_type_id == + LMAC_MODE_50G_R) { + val_lo = rpm_read(rpm, lmac_id, + RPMX_MTI_FCFECX_VL1_CCW_LO); + val_hi = rpm_read(rpm, lmac_id, + RPMX_MTI_FCFECX_CW_HI); + rsp->fec_corr_blks += (val_hi << 16 | val_lo); + + val_lo = rpm_read(rpm, lmac_id, + RPMX_MTI_FCFECX_VL1_NCCW_LO); + val_hi = rpm_read(rpm, lmac_id, + RPMX_MTI_FCFECX_CW_HI); + rsp->fec_uncorr_blks += (val_hi << 16 | val_lo); + } + } else { + /* enable RS-FEC capture */ + cfg = rpm_read(rpm, 0, RPMX_MTI_STAT_STATN_CONTROL); + cfg |= RPMX_RSFEC_RX_CAPTURE | BIT(lmac_id); + rpm_write(rpm, 0, RPMX_MTI_STAT_STATN_CONTROL, cfg); + + val_lo = rpm_read(rpm, 0, + RPMX_MTI_RSFEC_STAT_COUNTER_CAPTURE_2); + val_hi = rpm_read(rpm, 0, RPMX_MTI_STAT_DATA_HI_CDC); + rsp->fec_corr_blks = (val_hi << 32 | val_lo); + + val_lo = rpm_read(rpm, 0, + RPMX_MTI_RSFEC_STAT_COUNTER_CAPTURE_3); + val_hi = rpm_read(rpm, 0, RPMX_MTI_STAT_DATA_HI_CDC); + rsp->fec_uncorr_blks = (val_hi << 32 | val_lo); + } + + return 0; +} diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rpm.h b/drivers/net/ethernet/marvell/octeontx2/af/rpm.h index fc20a35bd8f9..22147b4c2137 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rpm.h +++ b/drivers/net/ethernet/marvell/octeontx2/af/rpm.h @@ -25,7 +25,6 @@ #define RPMX_CMRX_SW_INT_ENA_W1S 0x198 #define RPMX_CMRX_LINK_CFG 0x1070 #define RPMX_MTI_PCS100X_CONTROL1 0x20000 -#define RPMX_MTI_LPCSX_CONTROL1 0x30000 #define RPMX_MTI_PCS_LBK BIT_ULL(14) #define RPMX_MTI_LPCSX_CONTROL(id) (0x30000 | ((id) * 0x100)) @@ -81,6 +80,18 @@ #define RPMX_TS_BINARY_MODE BIT_ULL(11) #define RPMX_CONST1 0x2008 +/* FEC stats */ +#define RPMX_MTI_STAT_STATN_CONTROL 0x10018 +#define RPMX_MTI_STAT_DATA_HI_CDC 0x10038 +#define RPMX_RSFEC_RX_CAPTURE BIT_ULL(27) +#define RPMX_MTI_RSFEC_STAT_COUNTER_CAPTURE_2 0x40050 +#define RPMX_MTI_RSFEC_STAT_COUNTER_CAPTURE_3 0x40058 +#define RPMX_MTI_FCFECX_VL0_CCW_LO 0x38618 +#define RPMX_MTI_FCFECX_VL0_NCCW_LO 0x38620 +#define RPMX_MTI_FCFECX_VL1_CCW_LO 0x38628 +#define RPMX_MTI_FCFECX_VL1_NCCW_LO 0x38630 +#define RPMX_MTI_FCFECX_CW_HI 0x38638 + /* CN10KB CSR Declaration */ #define RPM2_CMRX_SW_INT 0x1b0 #define RPM2_CMRX_SW_INT_ENA_W1S 0x1b8 @@ -119,4 +130,5 @@ int rpm_lmac_get_pfc_frm_cfg(void *rpmd, int lmac_id, u8 *tx_pause, u8 *rx_pause); int rpm2_get_nr_lmacs(void *rpmd); bool is_dev_rpm2(void *rpmd); +int rpm_get_fec_stats(void *cgxd, int lmac_id, struct cgx_fec_stats_rsp *rsp); #endif /* RPM_H */ diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_cgx.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_cgx.c index 53580e0381c9..438b212fb54a 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_cgx.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_cgx.c @@ -579,6 +579,7 @@ int rvu_mbox_handler_cgx_fec_stats(struct rvu *rvu, struct cgx_fec_stats_rsp *rsp) { int pf = rvu_get_pf(req->hdr.pcifunc); + struct mac_ops *mac_ops; u8 cgx_idx, lmac; void *cgxd; @@ -587,7 +588,8 @@ int rvu_mbox_handler_cgx_fec_stats(struct rvu *rvu, rvu_get_cgx_lmac_id(rvu->pf2cgxlmac_map[pf], &cgx_idx, &lmac); cgxd = rvu_cgx_pdata(cgx_idx, rvu); - return cgx_get_fec_stats(cgxd, lmac, rsp); + mac_ops = get_mac_ops(cgxd); + return mac_ops->get_fec_stats(cgxd, lmac, rsp); } int rvu_mbox_handler_cgx_mac_addr_set(struct rvu *rvu, From 5b481acab4ce017fda8166fa9428511da41109e5 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Tue, 6 Dec 2022 15:59:32 +0100 Subject: [PATCH 1648/2002] bpf: do not rely on ALLOW_ERROR_INJECTION for fmod_ret The current way of expressing that a non-bpf kernel component is willing to accept that bpf programs can be attached to it and that they can change the return value is to abuse ALLOW_ERROR_INJECTION. This is debated in the link below, and the result is that it is not a reasonable thing to do. Reuse the kfunc declaration structure to also tag the kernel functions we want to be fmodret. This way we can control from any subsystem which functions are being modified by bpf without touching the verifier. Link: https://lore.kernel.org/all/20221121104403.1545f9b5@gandalf.local.home/ Suggested-by: Alexei Starovoitov Signed-off-by: Benjamin Tissoires Acked-by: Alexei Starovoitov Link: https://lore.kernel.org/r/20221206145936.922196-2-benjamin.tissoires@redhat.com --- include/linux/btf.h | 2 ++ kernel/bpf/btf.c | 30 +++++++++++++++++++++++++----- kernel/bpf/verifier.c | 17 +++++++++++++++-- net/bpf/test_run.c | 14 +++++++++++--- 4 files changed, 53 insertions(+), 10 deletions(-) diff --git a/include/linux/btf.h b/include/linux/btf.h index f9aababc5d78..6a0808e7845c 100644 --- a/include/linux/btf.h +++ b/include/linux/btf.h @@ -412,8 +412,10 @@ struct btf *bpf_prog_get_target_btf(const struct bpf_prog *prog); u32 *btf_kfunc_id_set_contains(const struct btf *btf, enum bpf_prog_type prog_type, u32 kfunc_btf_id); +u32 *btf_kfunc_is_modify_return(const struct btf *btf, u32 kfunc_btf_id); int register_btf_kfunc_id_set(enum bpf_prog_type prog_type, const struct btf_kfunc_id_set *s); +int register_btf_fmodret_id_set(const struct btf_kfunc_id_set *kset); s32 btf_find_dtor_kfunc(struct btf *btf, u32 btf_id); int register_btf_id_dtor_kfuncs(const struct btf_id_dtor_kfunc *dtors, u32 add_cnt, struct module *owner); diff --git a/kernel/bpf/btf.c b/kernel/bpf/btf.c index 35c07afac924..c1df506293e4 100644 --- a/kernel/bpf/btf.c +++ b/kernel/bpf/btf.c @@ -204,6 +204,7 @@ enum btf_kfunc_hook { BTF_KFUNC_HOOK_STRUCT_OPS, BTF_KFUNC_HOOK_TRACING, BTF_KFUNC_HOOK_SYSCALL, + BTF_KFUNC_HOOK_FMODRET, BTF_KFUNC_HOOK_MAX, }; @@ -7448,11 +7449,14 @@ u32 *btf_kfunc_id_set_contains(const struct btf *btf, return __btf_kfunc_id_set_contains(btf, hook, kfunc_btf_id); } -/* This function must be invoked only from initcalls/module init functions */ -int register_btf_kfunc_id_set(enum bpf_prog_type prog_type, - const struct btf_kfunc_id_set *kset) +u32 *btf_kfunc_is_modify_return(const struct btf *btf, u32 kfunc_btf_id) +{ + return __btf_kfunc_id_set_contains(btf, BTF_KFUNC_HOOK_FMODRET, kfunc_btf_id); +} + +static int __register_btf_kfunc_id_set(enum btf_kfunc_hook hook, + const struct btf_kfunc_id_set *kset) { - enum btf_kfunc_hook hook; struct btf *btf; int ret; @@ -7471,13 +7475,29 @@ int register_btf_kfunc_id_set(enum bpf_prog_type prog_type, if (IS_ERR(btf)) return PTR_ERR(btf); - hook = bpf_prog_type_to_kfunc_hook(prog_type); ret = btf_populate_kfunc_set(btf, hook, kset->set); btf_put(btf); return ret; } + +/* This function must be invoked only from initcalls/module init functions */ +int register_btf_kfunc_id_set(enum bpf_prog_type prog_type, + const struct btf_kfunc_id_set *kset) +{ + enum btf_kfunc_hook hook; + + hook = bpf_prog_type_to_kfunc_hook(prog_type); + return __register_btf_kfunc_id_set(hook, kset); +} EXPORT_SYMBOL_GPL(register_btf_kfunc_id_set); +/* This function must be invoked only from initcalls/module init functions */ +int register_btf_fmodret_id_set(const struct btf_kfunc_id_set *kset) +{ + return __register_btf_kfunc_id_set(BTF_KFUNC_HOOK_FMODRET, kset); +} +EXPORT_SYMBOL_GPL(register_btf_fmodret_id_set); + s32 btf_find_dtor_kfunc(struct btf *btf, u32 btf_id) { struct btf_id_dtor_kfunc_tab *tab = btf->dtor_kfunc_tab; diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 225666307bba..ec2e6ec7309e 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -15021,12 +15021,22 @@ int bpf_check_attach_target(struct bpf_verifier_log *log, ret = -EINVAL; switch (prog->type) { case BPF_PROG_TYPE_TRACING: - /* fentry/fexit/fmod_ret progs can be sleepable only if they are + + /* fentry/fexit/fmod_ret progs can be sleepable if they are * attached to ALLOW_ERROR_INJECTION and are not in denylist. */ if (!check_non_sleepable_error_inject(btf_id) && within_error_injection_list(addr)) ret = 0; + /* fentry/fexit/fmod_ret progs can also be sleepable if they are + * in the fmodret id set with the KF_SLEEPABLE flag. + */ + else { + u32 *flags = btf_kfunc_is_modify_return(btf, btf_id); + + if (flags && (*flags & KF_SLEEPABLE)) + ret = 0; + } break; case BPF_PROG_TYPE_LSM: /* LSM progs check that they are attached to bpf_lsm_*() funcs. @@ -15047,7 +15057,10 @@ int bpf_check_attach_target(struct bpf_verifier_log *log, bpf_log(log, "can't modify return codes of BPF programs\n"); return -EINVAL; } - ret = check_attach_modify_return(addr, tname); + ret = -EINVAL; + if (btf_kfunc_is_modify_return(btf, btf_id) || + !check_attach_modify_return(addr, tname)) + ret = 0; if (ret) { bpf_log(log, "%s() is not modifiable\n", tname); return ret; diff --git a/net/bpf/test_run.c b/net/bpf/test_run.c index 13d578ce2a09..5079fb089b5f 100644 --- a/net/bpf/test_run.c +++ b/net/bpf/test_run.c @@ -489,7 +489,6 @@ int noinline bpf_fentry_test1(int a) return a + 1; } EXPORT_SYMBOL_GPL(bpf_fentry_test1); -ALLOW_ERROR_INJECTION(bpf_fentry_test1, ERRNO); int noinline bpf_fentry_test2(int a, u64 b) { @@ -733,7 +732,15 @@ noinline void bpf_kfunc_call_test_destructive(void) __diag_pop(); -ALLOW_ERROR_INJECTION(bpf_modify_return_test, ERRNO); +BTF_SET8_START(bpf_test_modify_return_ids) +BTF_ID_FLAGS(func, bpf_modify_return_test) +BTF_ID_FLAGS(func, bpf_fentry_test1, KF_SLEEPABLE) +BTF_SET8_END(bpf_test_modify_return_ids) + +static const struct btf_kfunc_id_set bpf_test_modify_return_set = { + .owner = THIS_MODULE, + .set = &bpf_test_modify_return_ids, +}; BTF_SET8_START(test_sk_check_kfunc_ids) BTF_ID_FLAGS(func, bpf_kfunc_call_test1) @@ -1668,7 +1675,8 @@ static int __init bpf_prog_test_run_init(void) }; int ret; - ret = register_btf_kfunc_id_set(BPF_PROG_TYPE_SCHED_CLS, &bpf_prog_test_kfunc_set); + ret = register_btf_fmodret_id_set(&bpf_test_modify_return_set); + ret = ret ?: register_btf_kfunc_id_set(BPF_PROG_TYPE_SCHED_CLS, &bpf_prog_test_kfunc_set); ret = ret ?: register_btf_kfunc_id_set(BPF_PROG_TYPE_TRACING, &bpf_prog_test_kfunc_set); ret = ret ?: register_btf_kfunc_id_set(BPF_PROG_TYPE_SYSCALL, &bpf_prog_test_kfunc_set); return ret ?: register_btf_id_dtor_kfuncs(bpf_prog_test_dtor_kfunc, From 8ca67e3d8bfe8d1cf9e68328c585562ab5e63309 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 5 Dec 2022 10:35:38 +0200 Subject: [PATCH 1649/2002] wifi: iwlwifi: nvm-parse: enable WiFi7 for Fm radio for now We're still working out how the hardware/firmware (will) advertise support for this - for now, assume that Fm radio supports it. Signed-off-by: Johannes Berg Signed-off-by: Gregory Greenman Link: https://lore.kernel.org/r/20221205102808.f73face1a674.Ic62ee5c61c0ef2a5b5e9f89782b7bc82b0caa408@changeid --- drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c index 476b90f32626..aa8e08487b52 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c @@ -1964,6 +1964,8 @@ struct iwl_nvm_data *iwl_get_nvm(struct iwl_trans *trans, !!(mac_flags & NVM_MAC_SKU_FLAGS_BAND_5_2_ENABLED); nvm->sku_cap_mimo_disabled = !!(mac_flags & NVM_MAC_SKU_FLAGS_MIMO_DISABLED); + if (CSR_HW_RFID_TYPE(trans->hw_rf_id) == IWL_CFG_RF_TYPE_FM) + nvm->sku_cap_11be_enable = true; /* Initialize PHY sku data */ nvm->valid_tx_ant = (u8)le32_to_cpu(rsp->phy_sku.tx_chains); From c5a976cf6a756efeee190f05c875b732bd10b4df Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 5 Dec 2022 10:35:39 +0200 Subject: [PATCH 1650/2002] wifi: iwlwifi: modify new queue allocation command Follow a new firmware API changes and update the queue allocation command in the remove/modify cases to take the station mask and TID instead of the queue ID. Signed-off-by: Johannes Berg Signed-off-by: Gregory Greenman Link: https://lore.kernel.org/r/20221205102808.2925b38d3929.Ib8467711590c1969817c3321509eb131f4230e15@changeid --- .../wireless/intel/iwlwifi/fw/api/datapath.h | 16 +++++++----- drivers/net/wireless/intel/iwlwifi/mvm/sta.c | 26 ++++++++++++------- 2 files changed, 26 insertions(+), 16 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/datapath.h b/drivers/net/wireless/intel/iwlwifi/fw/api/datapath.h index d07982d8c897..9263413ee06f 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/datapath.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/datapath.h @@ -382,9 +382,11 @@ enum iwl_scd_queue_cfg_operation { * @u.add.cb_size: size code * @u.add.bc_dram_addr: byte-count table IOVA * @u.add.tfdq_dram_addr: TFD queue IOVA - * @u.remove.queue: queue ID for removal - * @u.modify.sta_mask: new station mask for modify - * @u.modify.queue: queue ID to modify + * @u.remove.sta_mask: station mask of queue to remove + * @u.remove.tid: TID of queue to remove + * @u.modify.old_sta_mask: old station mask for modify + * @u.modify.tid: TID of queue to modify + * @u.modify.new_sta_mask: new station mask for modify */ struct iwl_scd_queue_cfg_cmd { __le32 operation; @@ -399,11 +401,13 @@ struct iwl_scd_queue_cfg_cmd { __le64 tfdq_dram_addr; } __packed add; /* TX_QUEUE_CFG_CMD_ADD_API_S_VER_1 */ struct { - __le32 queue; + __le32 sta_mask; + __le32 tid; } __packed remove; /* TX_QUEUE_CFG_CMD_REMOVE_API_S_VER_1 */ struct { - __le32 sta_mask; - __le32 queue; + __le32 old_sta_mask; + __le32 tid; + __le32 new_sta_mask; } __packed modify; /* TX_QUEUE_CFG_CMD_MODIFY_API_S_VER_1 */ } __packed u; /* TX_QUEUE_CFG_CMD_OPERATION_API_U_VER_1 */ } __packed; /* TX_QUEUE_CFG_CMD_API_S_VER_3 */ diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/sta.c b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c index 515dd3e0730d..6c9c5d6e7783 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/sta.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c @@ -316,7 +316,7 @@ static int iwl_mvm_invalidate_sta_queue(struct iwl_mvm *mvm, int queue, } static int iwl_mvm_disable_txq(struct iwl_mvm *mvm, struct ieee80211_sta *sta, - u16 *queueptr, u8 tid) + int sta_id, u16 *queueptr, u8 tid) { int queue = *queueptr; struct iwl_scd_txq_cfg_cmd cmd = { @@ -333,7 +333,8 @@ static int iwl_mvm_disable_txq(struct iwl_mvm *mvm, struct ieee80211_sta *sta, SCD_QUEUE_CONFIG_CMD); struct iwl_scd_queue_cfg_cmd remove_cmd = { .operation = cpu_to_le32(IWL_SCD_QUEUE_REMOVE), - .u.remove.queue = cpu_to_le32(queue), + .u.remove.tid = cpu_to_le32(tid), + .u.remove.sta_mask = cpu_to_le32(BIT(sta_id)), }; ret = iwl_mvm_send_cmd_pdu(mvm, cmd_id, 0, @@ -531,7 +532,7 @@ static int iwl_mvm_free_inactive_queue(struct iwl_mvm *mvm, int queue, iwl_mvm_invalidate_sta_queue(mvm, queue, disable_agg_tids, false); - ret = iwl_mvm_disable_txq(mvm, old_sta, &queue_tmp, tid); + ret = iwl_mvm_disable_txq(mvm, old_sta, sta_id, &queue_tmp, tid); if (ret) { IWL_ERR(mvm, "Failed to free inactive queue %d (ret=%d)\n", @@ -1408,7 +1409,7 @@ static int iwl_mvm_sta_alloc_queue(struct iwl_mvm *mvm, out_err: queue_tmp = queue; - iwl_mvm_disable_txq(mvm, sta, &queue_tmp, tid); + iwl_mvm_disable_txq(mvm, sta, mvmsta->sta_id, &queue_tmp, tid); return ret; } @@ -1854,7 +1855,8 @@ static void iwl_mvm_disable_sta_queues(struct iwl_mvm *mvm, if (mvm_sta->tid_data[i].txq_id == IWL_MVM_INVALID_QUEUE) continue; - iwl_mvm_disable_txq(mvm, sta, &mvm_sta->tid_data[i].txq_id, i); + iwl_mvm_disable_txq(mvm, sta, mvm_sta->sta_id, + &mvm_sta->tid_data[i].txq_id, i); mvm_sta->tid_data[i].txq_id = IWL_MVM_INVALID_QUEUE; } @@ -2065,7 +2067,7 @@ static int iwl_mvm_add_int_sta_with_queue(struct iwl_mvm *mvm, int macidx, ret = iwl_mvm_add_int_sta_common(mvm, sta, addr, macidx, maccolor); if (ret) { if (!iwl_mvm_has_new_tx_api(mvm)) - iwl_mvm_disable_txq(mvm, NULL, queue, + iwl_mvm_disable_txq(mvm, NULL, sta->sta_id, queue, IWL_MAX_TID_COUNT); return ret; } @@ -2138,7 +2140,8 @@ int iwl_mvm_rm_snif_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif) if (WARN_ON_ONCE(mvm->snif_sta.sta_id == IWL_MVM_INVALID_STA)) return -EINVAL; - iwl_mvm_disable_txq(mvm, NULL, &mvm->snif_queue, IWL_MAX_TID_COUNT); + iwl_mvm_disable_txq(mvm, NULL, mvm->snif_sta.sta_id, + &mvm->snif_queue, IWL_MAX_TID_COUNT); ret = iwl_mvm_rm_sta_common(mvm, mvm->snif_sta.sta_id); if (ret) IWL_WARN(mvm, "Failed sending remove station\n"); @@ -2155,7 +2158,8 @@ int iwl_mvm_rm_aux_sta(struct iwl_mvm *mvm) if (WARN_ON_ONCE(mvm->aux_sta.sta_id == IWL_MVM_INVALID_STA)) return -EINVAL; - iwl_mvm_disable_txq(mvm, NULL, &mvm->aux_queue, IWL_MAX_TID_COUNT); + iwl_mvm_disable_txq(mvm, NULL, mvm->aux_sta.sta_id, + &mvm->aux_queue, IWL_MAX_TID_COUNT); ret = iwl_mvm_rm_sta_common(mvm, mvm->aux_sta.sta_id); if (ret) IWL_WARN(mvm, "Failed sending remove station\n"); @@ -2272,7 +2276,8 @@ static void iwl_mvm_free_bcast_sta_queues(struct iwl_mvm *mvm, } queue = *queueptr; - iwl_mvm_disable_txq(mvm, NULL, queueptr, IWL_MAX_TID_COUNT); + iwl_mvm_disable_txq(mvm, NULL, mvmvif->bcast_sta.sta_id, + queueptr, IWL_MAX_TID_COUNT); if (iwl_mvm_has_new_tx_api(mvm)) return; @@ -2507,7 +2512,8 @@ int iwl_mvm_rm_mcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif) iwl_mvm_flush_sta(mvm, &mvmvif->mcast_sta, true); - iwl_mvm_disable_txq(mvm, NULL, &mvmvif->cab_queue, 0); + iwl_mvm_disable_txq(mvm, NULL, mvmvif->mcast_sta.sta_id, + &mvmvif->cab_queue, 0); ret = iwl_mvm_rm_sta_common(mvm, mvmvif->mcast_sta.sta_id); if (ret) From 1174e8fcbba89593f67240c3395554b3812a4981 Mon Sep 17 00:00:00 2001 From: Mordechay Goodstein Date: Mon, 5 Dec 2022 10:35:40 +0200 Subject: [PATCH 1651/2002] wifi: iwlwifi: mvm: don't access packet before checking len Currently in sniffer mode we access pkt fields before checking that the frame has the length to access it. Fix this by moving the check to before the access. Signed-off-by: Mordechay Goodstein Signed-off-by: Gregory Greenman Link: https://lore.kernel.org/r/20221205102808.934da230c698.Ib56f11bbc8978e15d38394336a929cb4996ba39e@changeid --- drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c | 38 ++++++++++--------- 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c b/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c index 5f782ca1e254..97b67270f384 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c @@ -2066,22 +2066,30 @@ void iwl_mvm_rx_monitor_no_data(struct iwl_mvm *mvm, struct napi_struct *napi, struct ieee80211_rx_status *rx_status; struct iwl_rx_packet *pkt = rxb_addr(rxb); struct iwl_rx_no_data *desc = (void *)pkt->data; - u32 rssi = le32_to_cpu(desc->rssi); - u32 info_type = le32_to_cpu(desc->info) & RX_NO_DATA_INFO_TYPE_MSK; + u32 rssi; + u32 info_type; struct ieee80211_sta *sta = NULL; struct sk_buff *skb; - struct iwl_mvm_rx_phy_data phy_data = { - .d0 = desc->phy_info[0], - .d1 = desc->phy_info[1], - .phy_info = IWL_RX_MPDU_PHY_TSF_OVERLOAD, - .gp2_on_air_rise = le32_to_cpu(desc->on_air_rise_time), - .rate_n_flags = le32_to_cpu(desc->rate), - .energy_a = u32_get_bits(rssi, RX_NO_DATA_CHAIN_A_MSK), - .energy_b = u32_get_bits(rssi, RX_NO_DATA_CHAIN_B_MSK), - .channel = u32_get_bits(rssi, RX_NO_DATA_CHANNEL_MSK), - }; + struct iwl_mvm_rx_phy_data phy_data; u32 format; + if (unlikely(test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status))) + return; + + if (unlikely(iwl_rx_packet_payload_len(pkt) < sizeof(struct iwl_rx_no_data))) + return; + + rssi = le32_to_cpu(desc->rssi); + info_type = le32_to_cpu(desc->info) & RX_NO_DATA_INFO_TYPE_MSK; + phy_data.d0 = desc->phy_info[0]; + phy_data.d1 = desc->phy_info[1]; + phy_data.phy_info = IWL_RX_MPDU_PHY_TSF_OVERLOAD; + phy_data.gp2_on_air_rise = le32_to_cpu(desc->on_air_rise_time); + phy_data.rate_n_flags = le32_to_cpu(desc->rate); + phy_data.energy_a = u32_get_bits(rssi, RX_NO_DATA_CHAIN_A_MSK); + phy_data.energy_b = u32_get_bits(rssi, RX_NO_DATA_CHAIN_B_MSK); + phy_data.channel = u32_get_bits(rssi, RX_NO_DATA_CHANNEL_MSK); + if (iwl_fw_lookup_notif_ver(mvm->fw, DATA_PATH_GROUP, RX_NO_DATA_NOTIF, 0) < 2) { IWL_DEBUG_DROP(mvm, "Got an old rate format. Old rate: 0x%x\n", @@ -2093,12 +2101,6 @@ void iwl_mvm_rx_monitor_no_data(struct iwl_mvm *mvm, struct napi_struct *napi, format = phy_data.rate_n_flags & RATE_MCS_MOD_TYPE_MSK; - if (unlikely(iwl_rx_packet_payload_len(pkt) < sizeof(*desc))) - return; - - if (unlikely(test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status))) - return; - /* Dont use dev_alloc_skb(), we'll have enough headroom once * ieee80211_hdr pulled. */ From 838a0c7d144111df7f1fd426d099ab68e81bb24c Mon Sep 17 00:00:00 2001 From: Mukesh Sisodiya Date: Mon, 5 Dec 2022 10:35:41 +0200 Subject: [PATCH 1652/2002] wifi: iwlwifi: dump: Update check for valid FW address The error dump base address need an update based on current hardware. Signed-off-by: Mukesh Sisodiya Signed-off-by: Gregory Greenman Link: https://lore.kernel.org/r/20221205102808.a521691632e9.Ic15fa65107c0d4d0b555a1fb3a2a20ab2725b683@changeid --- drivers/net/wireless/intel/iwlwifi/fw/dump.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/fw/dump.c b/drivers/net/wireless/intel/iwlwifi/fw/dump.c index b90f1e9ce691..03b1b2428b5e 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/dump.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/dump.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2012-2014, 2018-2021 Intel Corporation + * Copyright (C) 2012-2014, 2018-2022 Intel Corporation * Copyright (C) 2013-2014 Intel Mobile Communications GmbH * Copyright (C) 2015-2017 Intel Deutschland GmbH */ @@ -157,7 +157,8 @@ static void iwl_fwrt_dump_lmac_error_log(struct iwl_fw_runtime *fwrt, u8 lmac_nu base = fwrt->fw->inst_errlog_ptr; } - if (base < 0x400000) { + if ((fwrt->trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_BZ && !base) || + (fwrt->trans->trans_cfg->device_family < IWL_DEVICE_FAMILY_BZ && base < 0x400000)) { IWL_ERR(fwrt, "Not valid error log pointer 0x%08X for %s uCode\n", base, From 6d7cb4a63db8716d23036323ff1a639cbade52ac Mon Sep 17 00:00:00 2001 From: Jakob Koschel Date: Mon, 5 Dec 2022 10:35:42 +0200 Subject: [PATCH 1653/2002] wifi: iwlwifi: mvm: replace usage of found with dedicated list iterator variable To move the list iterator variable into the list_for_each_entry_*() macro in the future it should be avoided to use the list iterator variable after the loop body. To *never* use the list iterator variable after the loop it was concluded to use a separate iterator variable instead of a found boolean [1]. This removes the need to use a found variable and simply checking if the variable was set, can determine if the break/goto was hit. While at it, stop using the unnecessary _safe() variant. [1] https://lore.kernel.org/all/CAHk-=wgRr_D8CB-D9Kg-c=EHreAsk5SqXPwr9Y7k9sA6cWXJ6w@mail.gmail.com/ Signed-off-by: Jakob Koschel [change to not use _safe variant] Signed-off-by: Johannes Berg Signed-off-by: Gregory Greenman Link: https://lore.kernel.org/r/20221205102808.e4882dc35543.I32b2b945ba234de72ee119fc20f5b8be02b6a3f2@changeid --- .../net/wireless/intel/iwlwifi/mvm/ftm-initiator.c | 12 +++++------- drivers/net/wireless/intel/iwlwifi/mvm/time-event.c | 7 +++---- 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/ftm-initiator.c b/drivers/net/wireless/intel/iwlwifi/mvm/ftm-initiator.c index 8c5b97fb1941..6eee3d0b2157 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/ftm-initiator.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/ftm-initiator.c @@ -1010,11 +1010,10 @@ static int iwl_mvm_ftm_range_resp_valid(struct iwl_mvm *mvm, u8 request_id, static void iwl_mvm_ftm_rtt_smoothing(struct iwl_mvm *mvm, struct cfg80211_pmsr_result *res) { - struct iwl_mvm_smooth_entry *resp; + struct iwl_mvm_smooth_entry *resp = NULL, *iter; s64 rtt_avg, rtt = res->ftm.rtt_avg; u32 undershoot, overshoot; u8 alpha; - bool found; if (!IWL_MVM_FTM_INITIATOR_ENABLE_SMOOTH) return; @@ -1028,15 +1027,14 @@ static void iwl_mvm_ftm_rtt_smoothing(struct iwl_mvm *mvm, return; } - found = false; - list_for_each_entry(resp, &mvm->ftm_initiator.smooth.resp, list) { - if (!memcmp(res->addr, resp->addr, ETH_ALEN)) { - found = true; + list_for_each_entry(iter, &mvm->ftm_initiator.smooth.resp, list) { + if (!memcmp(res->addr, iter->addr, ETH_ALEN)) { + resp = iter; break; } } - if (!found) { + if (!resp) { resp = kzalloc(sizeof(*resp), GFP_KERNEL); if (!resp) return; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/time-event.c b/drivers/net/wireless/intel/iwlwifi/mvm/time-event.c index ed8ba81a6043..e403a240a82f 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/time-event.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/time-event.c @@ -376,12 +376,11 @@ static void iwl_mvm_te_handle_notif(struct iwl_mvm *mvm, static int iwl_mvm_aux_roc_te_handle_notif(struct iwl_mvm *mvm, struct iwl_time_event_notif *notif) { - struct iwl_mvm_time_event_data *te_data, *tmp; - bool aux_roc_te = false; + struct iwl_mvm_time_event_data *aux_roc_te = NULL, *te_data; - list_for_each_entry_safe(te_data, tmp, &mvm->aux_roc_te_list, list) { + list_for_each_entry(te_data, &mvm->aux_roc_te_list, list) { if (le32_to_cpu(notif->unique_id) == te_data->uid) { - aux_roc_te = true; + aux_roc_te = te_data; break; } } From 3a27a1a158bd1811db7f9b82a72303414fa270e1 Mon Sep 17 00:00:00 2001 From: Mukesh Sisodiya Date: Mon, 5 Dec 2022 10:35:43 +0200 Subject: [PATCH 1654/2002] wifi: iwlwifi: pcie: Add reading and storing of crf and cdb id. Read and store crf and cdb id details to make it available during driver's lifetime Signed-off-by: Mukesh Sisodiya Signed-off-by: Gregory Greenman Link: https://lore.kernel.org/r/20221205102808.888b30780128.Ic8c4c088a75a0d7092d6301ef3760a118a06d819@changeid --- .../net/wireless/intel/iwlwifi/iwl-trans.h | 4 +++ drivers/net/wireless/intel/iwlwifi/pcie/drv.c | 33 +++++++++++++------ 2 files changed, 27 insertions(+), 10 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-trans.h b/drivers/net/wireless/intel/iwlwifi/iwl-trans.h index 32bd7f19f1d5..479a518c89a1 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-trans.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-trans.h @@ -976,6 +976,8 @@ struct iwl_trans_txqs { * @max_skb_frags: maximum number of fragments an SKB can have when transmitted. * 0 indicates that frag SKBs (NETIF_F_SG) aren't supported. * @hw_rf_id a u32 with the device RF ID + * @hw_crf_id a u32 with the device CRF ID + * @hw_cdb_id a u32 with the device CDB ID * @hw_id: a u32 with the ID of the device / sub-device. * Set during transport allocation. * @hw_id_str: a string with info about HW ID. Set during transport allocation. @@ -1015,6 +1017,8 @@ struct iwl_trans { u32 hw_rev; u32 hw_rev_step; u32 hw_rf_id; + u32 hw_crf_id; + u32 hw_cdb_id; u32 hw_id; char hw_id_str[52]; u32 sku_id[3]; diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/drv.c b/drivers/net/wireless/intel/iwlwifi/pcie/drv.c index 4f699862e7f7..99768d6a6032 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/drv.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/drv.c @@ -1350,15 +1350,13 @@ static const struct iwl_dev_info iwl_dev_info_table[] = { }; /* - * In case that there is no OTP on the NIC, get the rf id and cdb info - * from the prph registers. + * Read rf id and cdb info from prph register and store it */ static int get_crf_id(struct iwl_trans *iwl_trans) { int ret = 0; u32 sd_reg_ver_addr; - u32 cdb = 0; - u32 val; + u32 val = 0; if (iwl_trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_AX210) sd_reg_ver_addr = SD_REG_VER_GEN2; @@ -1377,10 +1375,26 @@ static int get_crf_id(struct iwl_trans *iwl_trans) iwl_write_umac_prph_no_grab(iwl_trans, WFPM_CTRL_REG, val); /* Read crf info */ - val = iwl_read_prph_no_grab(iwl_trans, sd_reg_ver_addr); + iwl_trans->hw_crf_id = iwl_read_prph_no_grab(iwl_trans, sd_reg_ver_addr); /* Read cdb info (also contains the jacket info if needed in the future */ - cdb = iwl_read_umac_prph_no_grab(iwl_trans, WFPM_OTP_CFG1_ADDR); + iwl_trans->hw_cdb_id = iwl_read_umac_prph_no_grab(iwl_trans, WFPM_OTP_CFG1_ADDR); + + iwl_trans_release_nic_access(iwl_trans); + +out: + return ret; +} + +/* + * In case that there is no OTP on the NIC, map the rf id and cdb info + * from the prph registers. + */ +static int map_crf_id(struct iwl_trans *iwl_trans) +{ + int ret = 0; + u32 val = iwl_trans->hw_crf_id; + u32 cdb = iwl_trans->hw_cdb_id; /* Map between crf id to rf id */ switch (REG_CRF_ID_TYPE(val)) { @@ -1410,7 +1424,7 @@ static int get_crf_id(struct iwl_trans *iwl_trans) IWL_ERR(iwl_trans, "Can find a correct rfid for crf id 0x%x\n", REG_CRF_ID_TYPE(val)); - goto out_release; + goto out; } @@ -1423,8 +1437,6 @@ static int get_crf_id(struct iwl_trans *iwl_trans) IWL_INFO(iwl_trans, "Detected RF 0x%x from crf id 0x%x\n", iwl_trans->hw_rf_id, REG_CRF_ID_TYPE(val)); -out_release: - iwl_trans_release_nic_access(iwl_trans); out: return ret; @@ -1544,6 +1556,7 @@ static int iwl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) } iwl_trans->hw_rf_id = iwl_read32(iwl_trans, CSR_HW_RF_ID); + get_crf_id(iwl_trans); /* * The RF_ID is set to zero in blank OTP so read version to @@ -1552,7 +1565,7 @@ static int iwl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) */ if (iwl_trans->trans_cfg->rf_id && iwl_trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_9000 && - !CSR_HW_RFID_TYPE(iwl_trans->hw_rf_id) && get_crf_id(iwl_trans)) { + !CSR_HW_RFID_TYPE(iwl_trans->hw_rf_id) && map_crf_id(iwl_trans)) { ret = -EINVAL; goto out_free_trans; } From b201944af11f7b2c860bef5cb562eb7163427724 Mon Sep 17 00:00:00 2001 From: Miri Korenblit Date: Mon, 5 Dec 2022 10:35:44 +0200 Subject: [PATCH 1655/2002] wifi: iwlwifi: mvm: Don't use deprecated register The CSR_HW_IF_CONFIG_REG register is no longer in use from IWL_DEVICE_FAMILY_AX210 and on. This register uses now for CSR_CTXT_INFO_BOOT_CTRL which has a different meaning and a different format. Currently we're writing to the register according to CSR_HW_IF_CONFIG_REGs format, regardless to the device family. This causes to miss-interpretation of the register value, as it is parsed according to CSR_CTXT_INFO_BOOT_CTRLs format for devices families >= IWL_DEVICE_FAMILY_AX210. Fix this by writing to the register according to the old format only for the relevant hardware. Signed-off-by: Miri Korenblit Signed-off-by: Gregory Greenman Link: https://lore.kernel.org/r/20221205102808.8e91eb10dc7d.I3882a2452785ef8b455d99af235e4477917bf46d@changeid --- drivers/net/wireless/intel/iwlwifi/mvm/ops.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c index e78f5beaa2d0..ebe6d9c4ccaf 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c @@ -92,6 +92,12 @@ static void iwl_mvm_nic_config(struct iwl_op_mode *op_mode) radio_cfg_dash = (phy_config & FW_PHY_CFG_RADIO_DASH) >> FW_PHY_CFG_RADIO_DASH_POS; + IWL_DEBUG_INFO(mvm, "Radio type=0x%x-0x%x-0x%x\n", radio_cfg_type, + radio_cfg_step, radio_cfg_dash); + + if (mvm->trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_AX210) + return; + /* SKU control */ reg_val = CSR_HW_REV_STEP_DASH(mvm->trans->hw_rev); @@ -127,9 +133,6 @@ static void iwl_mvm_nic_config(struct iwl_op_mode *op_mode) CSR_HW_IF_CONFIG_REG_D3_DEBUG, reg_val); - IWL_DEBUG_INFO(mvm, "Radio type=0x%x-0x%x-0x%x\n", radio_cfg_type, - radio_cfg_step, radio_cfg_dash); - /* * W/A : NIC is stuck in a reset state after Early PCIe power off * (PCIe power is lost before PERST# is asserted), causing ME FW From 9ad28ba1c6f054ef801bcd63c5b73792b4b1d9a4 Mon Sep 17 00:00:00 2001 From: Tom Rix Date: Mon, 5 Dec 2022 10:35:45 +0200 Subject: [PATCH 1656/2002] wifi: iwlwifi: mei: clean up comments SPDX *.h use /* */ style comments Spelling replacements commnunication to communication adsress to address procotol to protocol addtional to additional kown to know negotiaion to negotiation mssage to message Signed-off-by: Tom Rix Signed-off-by: Johannes Berg Signed-off-by: Gregory Greenman Link: https://lore.kernel.org/r/20221205102808.f1ff5aff270b.Ie4ba3f980a35455ed95e7c0cae854231fccc89e2@changeid --- drivers/net/wireless/intel/iwlwifi/mei/iwl-mei.h | 6 +++--- drivers/net/wireless/intel/iwlwifi/mei/main.c | 2 +- drivers/net/wireless/intel/iwlwifi/mei/sap.h | 10 +++++----- drivers/net/wireless/intel/iwlwifi/mei/trace-data.h | 2 +- drivers/net/wireless/intel/iwlwifi/mei/trace.h | 2 +- 5 files changed, 11 insertions(+), 11 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mei/iwl-mei.h b/drivers/net/wireless/intel/iwlwifi/mei/iwl-mei.h index 2b639eef595d..ae66192feefe 100644 --- a/drivers/net/wireless/intel/iwlwifi/mei/iwl-mei.h +++ b/drivers/net/wireless/intel/iwlwifi/mei/iwl-mei.h @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0-only +/* SPDX-License-Identifier: GPL-2.0-only */ /* * Copyright (C) 2021 Intel Corporation */ @@ -13,7 +13,7 @@ /** * DOC: Introduction * - * iwlmei is the kernel module that is in charge of the commnunication between + * iwlmei is the kernel module that is in charge of the communication between * the iwlwifi driver and the CSME firmware's WLAN driver. This communication * uses the SAP protocol defined in another file. * iwlwifi can request or release ownership on the WiFi device through iwlmei. @@ -348,7 +348,7 @@ void iwl_mei_set_rfkill_state(bool hw_rfkill, bool sw_rfkill); /** * iwl_mei_set_nic_info() - set mac address * @mac_address: mac address to set - * @nvm_address: NVM mac adsress to set + * @nvm_address: NVM mac address to set * * This function must be called upon mac address change. */ diff --git a/drivers/net/wireless/intel/iwlwifi/mei/main.c b/drivers/net/wireless/intel/iwlwifi/mei/main.c index b89989b6399a..f9d11935ed97 100644 --- a/drivers/net/wireless/intel/iwlwifi/mei/main.c +++ b/drivers/net/wireless/intel/iwlwifi/mei/main.c @@ -1963,7 +1963,7 @@ static int iwl_mei_probe(struct mei_cl_device *cldev, iwl_mei_dbgfs_register(mei); /* - * We now have a Rx function in place, start the SAP procotol + * We now have a Rx function in place, start the SAP protocol * we expect to get the SAP_ME_MSG_START_OK response later on. */ mutex_lock(&iwl_mei_mutex); diff --git a/drivers/net/wireless/intel/iwlwifi/mei/sap.h b/drivers/net/wireless/intel/iwlwifi/mei/sap.h index 6c0ad4adbf32..3b56637b9697 100644 --- a/drivers/net/wireless/intel/iwlwifi/mei/sap.h +++ b/drivers/net/wireless/intel/iwlwifi/mei/sap.h @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0-only +/* SPDX-License-Identifier: GPL-2.0-only */ /* * Copyright (C) 2021 - 2022 Intel Corporation */ @@ -25,7 +25,7 @@ * * Since this messaging system cannot support high amounts of * traffic, iwlwifi and the CSME firmware's WLAN driver have an - * addtional communication pipe to exchange information. The body + * additional communication pipe to exchange information. The body * of the message is copied to a shared area and the message that * goes over the ME interface just signals the other side * that a new message is waiting in the shared area. The ME @@ -55,7 +55,7 @@ /** * DOC: Host and driver state messages * - * In order to let CSME konw about the host state and the host driver state, + * In order to let CSME know about the host state and the host driver state, * the host sends messages that let CSME know about the host's state. * When the host driver is loaded, the host sends %SAP_MSG_NOTIF_WIFIDR_UP. * When the host driver is unloaded, the host sends %SAP_MSG_NOTIF_WIFIDR_DOWN. @@ -76,7 +76,7 @@ * DOC: Ownership * * The device can be controlled either by the CSME firmware or - * by the host driver: iwlwifi. There is a negotiaion between + * by the host driver: iwlwifi. There is a negotiation between * those two entities to determine who controls (or owns) the * device. Since the CSME can control the device even when the * OS is not working or even missing, the CSME can request the @@ -136,7 +136,7 @@ enum iwl_sap_me_msg_id { * struct iwl_sap_me_msg_hdr - the header of the ME message * @type: the type of the message, see &enum iwl_sap_me_msg_id. * @seq_num: a sequence number used for debug only. - * @len: the length of the mssage. + * @len: the length of the message. */ struct iwl_sap_me_msg_hdr { __le32 type; diff --git a/drivers/net/wireless/intel/iwlwifi/mei/trace-data.h b/drivers/net/wireless/intel/iwlwifi/mei/trace-data.h index 83639c6225ca..15cb0bb4e9dc 100644 --- a/drivers/net/wireless/intel/iwlwifi/mei/trace-data.h +++ b/drivers/net/wireless/intel/iwlwifi/mei/trace-data.h @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0-only +/* SPDX-License-Identifier: GPL-2.0-only */ /* * Copyright(c) 2021 Intel Corporation */ diff --git a/drivers/net/wireless/intel/iwlwifi/mei/trace.h b/drivers/net/wireless/intel/iwlwifi/mei/trace.h index 45ecb22ec84a..20ff836733bb 100644 --- a/drivers/net/wireless/intel/iwlwifi/mei/trace.h +++ b/drivers/net/wireless/intel/iwlwifi/mei/trace.h @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0-only +/* SPDX-License-Identifier: GPL-2.0-only */ /* * Copyright(c) 2021 Intel Corporation */ From 5933b8620a454894c4fdb7c123ddc6048c67e473 Mon Sep 17 00:00:00 2001 From: Naftali Goldstein Date: Mon, 5 Dec 2022 10:35:46 +0200 Subject: [PATCH 1657/2002] wifi: iwlwifi: mvm: d3: add TKIP to the GTK iterator This was missed when this function was refactored out of iwl_mvm_wowlan_program_keys. Signed-off-by: Naftali Goldstein Signed-off-by: Gregory Greenman Link: https://lore.kernel.org/r/20221205102808.746f40250502.Ieef45ffc617726cb47b3a4ec625d513d16082f4b@changeid --- drivers/net/wireless/intel/iwlwifi/mvm/d3.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c index 919b1f478b4c..c5ad34b063df 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c @@ -599,6 +599,11 @@ static void iwl_mvm_wowlan_gtk_type_iter(struct ieee80211_hw *hw, switch (key->cipher) { default: return; + case WLAN_CIPHER_SUITE_TKIP: + if (!sta) + data->kek_kck_cmd->gtk_cipher = + cpu_to_le32(STA_KEY_FLG_TKIP); + return; case WLAN_CIPHER_SUITE_BIP_GMAC_256: case WLAN_CIPHER_SUITE_BIP_GMAC_128: data->kek_kck_cmd->igtk_cipher = cpu_to_le32(STA_KEY_FLG_GCMP); @@ -610,13 +615,13 @@ static void iwl_mvm_wowlan_gtk_type_iter(struct ieee80211_hw *hw, if (!sta) data->kek_kck_cmd->gtk_cipher = cpu_to_le32(STA_KEY_FLG_CCM); - break; + return; case WLAN_CIPHER_SUITE_GCMP: case WLAN_CIPHER_SUITE_GCMP_256: if (!sta) data->kek_kck_cmd->gtk_cipher = cpu_to_le32(STA_KEY_FLG_GCMP); - break; + return; } } From b2f20cf2899fdce94e01101dee08ddbd2e32ea2f Mon Sep 17 00:00:00 2001 From: Mukesh Sisodiya Date: Mon, 5 Dec 2022 10:35:47 +0200 Subject: [PATCH 1658/2002] wifi: iwlwifi: dump: Update check for UMAC valid FW address The error dump base address check for UMAC need an update based on current hardware. Signed-off-by: Mukesh Sisodiya Signed-off-by: Gregory Greenman Link: https://lore.kernel.org/r/20221205102808.e4b7fc650144.I46103baf400bb4ae69d82738e402a3903b17c030@changeid --- drivers/net/wireless/intel/iwlwifi/mvm/fw.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c index 0a41cd0ab243..0c6b49fcb00d 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c @@ -122,6 +122,9 @@ static bool iwl_alive_fn(struct iwl_notif_wait_data *notif_wait, u32 version = iwl_fw_lookup_notif_ver(mvm->fw, LEGACY_GROUP, UCODE_ALIVE_NTFY, 0); u32 i; + struct iwl_trans *trans = mvm->trans; + enum iwl_device_family device_family = trans->trans_cfg->device_family; + if (version == 6) { struct iwl_alive_ntf_v6 *palive; @@ -230,7 +233,8 @@ static bool iwl_alive_fn(struct iwl_notif_wait_data *notif_wait, if (umac_error_table) { if (umac_error_table >= - mvm->trans->cfg->min_umac_error_event_table) { + mvm->trans->cfg->min_umac_error_event_table || + device_family >= IWL_DEVICE_FAMILY_BZ) { iwl_fw_umac_set_alive_err_table(mvm->trans, umac_error_table); } else { From a6365c2da9faf7c7bcb81fbc4b422e9f319c9f0c Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 5 Dec 2022 10:35:48 +0200 Subject: [PATCH 1659/2002] wifi: iwlwifi: fw: use correct IML/ROM status register Different registers should be used, but they code wasn't adjusted for the 'error' register, only for the 'data1'. Fix that. Coverity CID: 1487176 Coverity CID: 1487230 Signed-off-by: Johannes Berg Signed-off-by: Gregory Greenman Link: https://lore.kernel.org/r/20221205102808.28de1a5b2493.I356c9404623fd5ef793d47481fe37a95be355c6b@changeid --- drivers/net/wireless/intel/iwlwifi/fw/dump.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/intel/iwlwifi/fw/dump.c b/drivers/net/wireless/intel/iwlwifi/fw/dump.c index 03b1b2428b5e..792f7fee1840 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/dump.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/dump.c @@ -377,7 +377,7 @@ static void iwl_fwrt_dump_iml_error_log(struct iwl_fw_runtime *fwrt) return; } - error = iwl_read_umac_prph(trans, UMAG_SB_CPU_2_STATUS); + error = iwl_read_umac_prph(trans, error); IWL_ERR(trans, "IML/ROM dump:\n"); From dcb2288b1fd9a8cdf2f3b8c0c7b3763346ef515f Mon Sep 17 00:00:00 2001 From: Andrii Nakryiko Date: Wed, 7 Dec 2022 11:55:34 -0800 Subject: [PATCH 1660/2002] bpf: Remove unused insn_cnt argument from visit_[func_call_]insn() Number of total instructions in BPF program (including subprogs) can and is accessed from env->prog->len. visit_func_call_insn() doesn't do any checks against insn_cnt anymore, relying on push_insn() to do this check internally. So remove unnecessary insn_cnt input argument from visit_func_call_insn() and visit_insn() functions. Suggested-by: Alexei Starovoitov Signed-off-by: Andrii Nakryiko Signed-off-by: Daniel Borkmann Link: https://lore.kernel.org/bpf/20221207195534.2866030-1-andrii@kernel.org --- kernel/bpf/verifier.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 1f151f996203..29e09ef15188 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -12222,8 +12222,7 @@ static int push_insn(int t, int w, int e, struct bpf_verifier_env *env, return DONE_EXPLORING; } -static int visit_func_call_insn(int t, int insn_cnt, - struct bpf_insn *insns, +static int visit_func_call_insn(int t, struct bpf_insn *insns, struct bpf_verifier_env *env, bool visit_callee) { @@ -12254,13 +12253,13 @@ static int visit_func_call_insn(int t, int insn_cnt, * DONE_EXPLORING - the instruction was fully explored * KEEP_EXPLORING - there is still work to be done before it is fully explored */ -static int visit_insn(int t, int insn_cnt, struct bpf_verifier_env *env) +static int visit_insn(int t, struct bpf_verifier_env *env) { struct bpf_insn *insns = env->prog->insnsi; int ret; if (bpf_pseudo_func(insns + t)) - return visit_func_call_insn(t, insn_cnt, insns, env, true); + return visit_func_call_insn(t, insns, env, true); /* All non-branch instructions have a single fall-through edge. */ if (BPF_CLASS(insns[t].code) != BPF_JMP && @@ -12279,7 +12278,7 @@ static int visit_insn(int t, int insn_cnt, struct bpf_verifier_env *env) * async state will be pushed for further exploration. */ mark_prune_point(env, t); - return visit_func_call_insn(t, insn_cnt, insns, env, + return visit_func_call_insn(t, insns, env, insns[t].src_reg == BPF_PSEUDO_CALL); case BPF_JA: @@ -12336,7 +12335,7 @@ static int check_cfg(struct bpf_verifier_env *env) while (env->cfg.cur_stack > 0) { int t = insn_stack[env->cfg.cur_stack - 1]; - ret = visit_insn(t, insn_cnt, env); + ret = visit_insn(t, env); switch (ret) { case DONE_EXPLORING: insn_state[t] = EXPLORED; From 537c3f66eac137a02ec50a40219d2da6597e5dc9 Mon Sep 17 00:00:00 2001 From: Andrii Nakryiko Date: Wed, 7 Dec 2022 12:16:47 -0800 Subject: [PATCH 1661/2002] selftests/bpf: add generic BPF program tester-loader It's become a common pattern to have a collection of small BPF programs in one BPF object file, each representing one test case. On user-space side of such tests we maintain a table of program names and expected failure or success, along with optional expected verifier log message. This works, but each set of tests reimplement this mundane code over and over again, which is a waste of time for anyone trying to add a new set of tests. Furthermore, it's quite error prone as it's way too easy to miss some entries in these manually maintained test tables (as evidences by dynptr_fail tests, in which ringbuf_release_uninit_dynptr subtest was accidentally missed; this is fixed in next patch). So this patch implements generic test_loader, which accepts skeleton name and handles the rest of details: opens and loads BPF object file, making sure each program is tested in isolation. Optionally each test case can specify expected BPF verifier log message. In case of failure, tester makes sure to report verifier log, but it also reports verifier log in verbose mode unconditionally. Now, the interesting deviation from existing custom implementations is the use of btf_decl_tag attribute to specify expected-to-fail vs expected-to-succeed markers and, optionally, expected log message directly next to BPF program source code, eliminating the need to manually create and update table of tests. We define few macros wrapping btf_decl_tag with a convention that all values of btf_decl_tag start with "comment:" prefix, and then utilizing a very simple "just_some_text_tag" or "some_key_name=" pattern to define things like expected success/failure, expected verifier message, extra verifier log level (if necessary). This approach is demonstrated by next patch in which two existing sets of failure tests are converted. Tester supports both expected-to-fail and expected-to-succeed programs, though this patch set didn't convert any existing expected-to-succeed programs yet, as existing tests couple BPF program loading with their further execution through attach or test_prog_run. One way to allow testing scenarios like this would be ability to specify custom callback, executed for each successfully loaded BPF program. This is left for follow up patches, after some more analysis of existing test cases. This test_loader is, hopefully, a start of a test_verifier-like runner, but integrated into test_progs infrastructure. It will allow much better "user experience" of defining low-level verification tests that can take advantage of all the libbpf-provided nicety features on BPF side: global variables, declarative maps, etc. All while having a choice of defining it in C or as BPF assembly (through __attribute__((naked)) functions and using embedded asm), depending on what makes most sense in each particular case. This will be explored in follow up patches as well. Acked-by: John Fastabend Signed-off-by: Andrii Nakryiko Link: https://lore.kernel.org/r/20221207201648.2990661-1-andrii@kernel.org Signed-off-by: Alexei Starovoitov --- tools/testing/selftests/bpf/Makefile | 2 +- tools/testing/selftests/bpf/progs/bpf_misc.h | 5 + tools/testing/selftests/bpf/test_loader.c | 233 +++++++++++++++++++ tools/testing/selftests/bpf/test_progs.h | 33 +++ 4 files changed, 272 insertions(+), 1 deletion(-) create mode 100644 tools/testing/selftests/bpf/test_loader.c diff --git a/tools/testing/selftests/bpf/Makefile b/tools/testing/selftests/bpf/Makefile index 997b3bd10cbf..c22c43bbee19 100644 --- a/tools/testing/selftests/bpf/Makefile +++ b/tools/testing/selftests/bpf/Makefile @@ -527,7 +527,7 @@ TRUNNER_BPF_PROGS_DIR := progs TRUNNER_EXTRA_SOURCES := test_progs.c cgroup_helpers.c trace_helpers.c \ network_helpers.c testing_helpers.c \ btf_helpers.c flow_dissector_load.h \ - cap_helpers.c + cap_helpers.c test_loader.c TRUNNER_EXTRA_FILES := $(OUTPUT)/urandom_read $(OUTPUT)/bpf_testmod.ko \ $(OUTPUT)/liburandom_read.so \ $(OUTPUT)/xdp_synproxy \ diff --git a/tools/testing/selftests/bpf/progs/bpf_misc.h b/tools/testing/selftests/bpf/progs/bpf_misc.h index 5bb11fe595a4..4a01ea9113bf 100644 --- a/tools/testing/selftests/bpf/progs/bpf_misc.h +++ b/tools/testing/selftests/bpf/progs/bpf_misc.h @@ -2,6 +2,11 @@ #ifndef __BPF_MISC_H__ #define __BPF_MISC_H__ +#define __msg(msg) __attribute__((btf_decl_tag("comment:test_expect_msg=" msg))) +#define __failure __attribute__((btf_decl_tag("comment:test_expect_failure"))) +#define __success __attribute__((btf_decl_tag("comment:test_expect_success"))) +#define __log_level(lvl) __attribute__((btf_decl_tag("comment:test_log_level="#lvl))) + #if defined(__TARGET_ARCH_x86) #define SYSCALL_WRAPPER 1 #define SYS_PREFIX "__x64_" diff --git a/tools/testing/selftests/bpf/test_loader.c b/tools/testing/selftests/bpf/test_loader.c new file mode 100644 index 000000000000..679efb3aa785 --- /dev/null +++ b/tools/testing/selftests/bpf/test_loader.c @@ -0,0 +1,233 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* Copyright (c) 2022 Meta Platforms, Inc. and affiliates. */ +#include +#include +#include + +#define str_has_pfx(str, pfx) \ + (strncmp(str, pfx, __builtin_constant_p(pfx) ? sizeof(pfx) - 1 : strlen(pfx)) == 0) + +#define TEST_LOADER_LOG_BUF_SZ 1048576 + +#define TEST_TAG_EXPECT_FAILURE "comment:test_expect_failure" +#define TEST_TAG_EXPECT_SUCCESS "comment:test_expect_success" +#define TEST_TAG_EXPECT_MSG_PFX "comment:test_expect_msg=" +#define TEST_TAG_LOG_LEVEL_PFX "comment:test_log_level=" + +struct test_spec { + const char *name; + bool expect_failure; + const char *expect_msg; + int log_level; +}; + +static int tester_init(struct test_loader *tester) +{ + if (!tester->log_buf) { + tester->log_buf_sz = TEST_LOADER_LOG_BUF_SZ; + tester->log_buf = malloc(tester->log_buf_sz); + if (!ASSERT_OK_PTR(tester->log_buf, "tester_log_buf")) + return -ENOMEM; + } + + return 0; +} + +void test_loader_fini(struct test_loader *tester) +{ + if (!tester) + return; + + free(tester->log_buf); +} + +static int parse_test_spec(struct test_loader *tester, + struct bpf_object *obj, + struct bpf_program *prog, + struct test_spec *spec) +{ + struct btf *btf; + int func_id, i; + + memset(spec, 0, sizeof(*spec)); + + spec->name = bpf_program__name(prog); + + btf = bpf_object__btf(obj); + if (!btf) { + ASSERT_FAIL("BPF object has no BTF"); + return -EINVAL; + } + + func_id = btf__find_by_name_kind(btf, spec->name, BTF_KIND_FUNC); + if (func_id < 0) { + ASSERT_FAIL("failed to find FUNC BTF type for '%s'", spec->name); + return -EINVAL; + } + + for (i = 1; i < btf__type_cnt(btf); i++) { + const struct btf_type *t; + const char *s; + + t = btf__type_by_id(btf, i); + if (!btf_is_decl_tag(t)) + continue; + + if (t->type != func_id || btf_decl_tag(t)->component_idx != -1) + continue; + + s = btf__str_by_offset(btf, t->name_off); + if (strcmp(s, TEST_TAG_EXPECT_FAILURE) == 0) { + spec->expect_failure = true; + } else if (strcmp(s, TEST_TAG_EXPECT_SUCCESS) == 0) { + spec->expect_failure = false; + } else if (str_has_pfx(s, TEST_TAG_EXPECT_MSG_PFX)) { + spec->expect_msg = s + sizeof(TEST_TAG_EXPECT_MSG_PFX) - 1; + } else if (str_has_pfx(s, TEST_TAG_LOG_LEVEL_PFX)) { + errno = 0; + spec->log_level = strtol(s + sizeof(TEST_TAG_LOG_LEVEL_PFX) - 1, NULL, 0); + if (errno) { + ASSERT_FAIL("failed to parse test log level from '%s'", s); + return -EINVAL; + } + } + } + + return 0; +} + +static void prepare_case(struct test_loader *tester, + struct test_spec *spec, + struct bpf_object *obj, + struct bpf_program *prog) +{ + int min_log_level = 0; + + if (env.verbosity > VERBOSE_NONE) + min_log_level = 1; + if (env.verbosity > VERBOSE_VERY) + min_log_level = 2; + + bpf_program__set_log_buf(prog, tester->log_buf, tester->log_buf_sz); + + /* Make sure we set at least minimal log level, unless test requirest + * even higher level already. Make sure to preserve independent log + * level 4 (verifier stats), though. + */ + if ((spec->log_level & 3) < min_log_level) + bpf_program__set_log_level(prog, (spec->log_level & 4) | min_log_level); + else + bpf_program__set_log_level(prog, spec->log_level); + + tester->log_buf[0] = '\0'; +} + +static void emit_verifier_log(const char *log_buf, bool force) +{ + if (!force && env.verbosity == VERBOSE_NONE) + return; + fprintf(stdout, "VERIFIER LOG:\n=============\n%s=============\n", log_buf); +} + +static void validate_case(struct test_loader *tester, + struct test_spec *spec, + struct bpf_object *obj, + struct bpf_program *prog, + int load_err) +{ + if (spec->expect_msg) { + char *match; + + match = strstr(tester->log_buf, spec->expect_msg); + if (!ASSERT_OK_PTR(match, "expect_msg")) { + /* if we are in verbose mode, we've already emitted log */ + if (env.verbosity == VERBOSE_NONE) + emit_verifier_log(tester->log_buf, true /*force*/); + fprintf(stderr, "EXPECTED MSG: '%s'\n", spec->expect_msg); + return; + } + } +} + +/* this function is forced noinline and has short generic name to look better + * in test_progs output (in case of a failure) + */ +static noinline +void run_subtest(struct test_loader *tester, + const char *skel_name, + skel_elf_bytes_fn elf_bytes_factory) +{ + LIBBPF_OPTS(bpf_object_open_opts, open_opts, .object_name = skel_name); + struct bpf_object *obj = NULL, *tobj; + struct bpf_program *prog, *tprog; + const void *obj_bytes; + size_t obj_byte_cnt; + int err; + + if (tester_init(tester) < 0) + return; /* failed to initialize tester */ + + obj_bytes = elf_bytes_factory(&obj_byte_cnt); + obj = bpf_object__open_mem(obj_bytes, obj_byte_cnt, &open_opts); + if (!ASSERT_OK_PTR(obj, "obj_open_mem")) + return; + + bpf_object__for_each_program(prog, obj) { + const char *prog_name = bpf_program__name(prog); + struct test_spec spec; + + if (!test__start_subtest(prog_name)) + continue; + + /* if we can't derive test specification, go to the next test */ + err = parse_test_spec(tester, obj, prog, &spec); + if (!ASSERT_OK(err, "parse_test_spec")) + continue; + + tobj = bpf_object__open_mem(obj_bytes, obj_byte_cnt, &open_opts); + if (!ASSERT_OK_PTR(tobj, "obj_open_mem")) /* shouldn't happen */ + continue; + + bpf_object__for_each_program(tprog, tobj) + bpf_program__set_autoload(tprog, false); + + bpf_object__for_each_program(tprog, tobj) { + /* only load specified program */ + if (strcmp(bpf_program__name(tprog), prog_name) == 0) { + bpf_program__set_autoload(tprog, true); + break; + } + } + + prepare_case(tester, &spec, tobj, tprog); + + err = bpf_object__load(tobj); + if (spec.expect_failure) { + if (!ASSERT_ERR(err, "unexpected_load_success")) { + emit_verifier_log(tester->log_buf, false /*force*/); + goto tobj_cleanup; + } + } else { + if (!ASSERT_OK(err, "unexpected_load_failure")) { + emit_verifier_log(tester->log_buf, true /*force*/); + goto tobj_cleanup; + } + } + + emit_verifier_log(tester->log_buf, false /*force*/); + validate_case(tester, &spec, tobj, tprog, err); + +tobj_cleanup: + bpf_object__close(tobj); + } + + bpf_object__close(obj); +} + +void test_loader__run_subtests(struct test_loader *tester, + const char *skel_name, + skel_elf_bytes_fn elf_bytes_factory) +{ + /* see comment in run_subtest() for why we do this function nesting */ + run_subtest(tester, skel_name, elf_bytes_factory); +} diff --git a/tools/testing/selftests/bpf/test_progs.h b/tools/testing/selftests/bpf/test_progs.h index b090996daee5..3f058dfadbaf 100644 --- a/tools/testing/selftests/bpf/test_progs.h +++ b/tools/testing/selftests/bpf/test_progs.h @@ -1,4 +1,7 @@ /* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __TEST_PROGS_H +#define __TEST_PROGS_H + #include #include #include @@ -210,6 +213,12 @@ int test__join_cgroup(const char *path); #define CHECK_ATTR(condition, tag, format...) \ _CHECK(condition, tag, tattr.duration, format) +#define ASSERT_FAIL(fmt, args...) ({ \ + static int duration = 0; \ + CHECK(false, "", fmt"\n", ##args); \ + false; \ +}) + #define ASSERT_TRUE(actual, name) ({ \ static int duration = 0; \ bool ___ok = (actual); \ @@ -397,3 +406,27 @@ int write_sysctl(const char *sysctl, const char *value); #endif #define BPF_TESTMOD_TEST_FILE "/sys/kernel/bpf_testmod" + +struct test_loader { + char *log_buf; + size_t log_buf_sz; + + struct bpf_object *obj; +}; + +typedef const void *(*skel_elf_bytes_fn)(size_t *sz); + +extern void test_loader__run_subtests(struct test_loader *tester, + const char *skel_name, + skel_elf_bytes_fn elf_bytes_factory); + +extern void test_loader_fini(struct test_loader *tester); + +#define RUN_TESTS(skel) ({ \ + struct test_loader tester = {}; \ + \ + test_loader__run_subtests(&tester, #skel, skel##__elf_bytes); \ + test_loader_fini(&tester); \ +}) + +#endif /* __TEST_PROGS_H */ From 26c386ecf0212affb50f02dabcb0152995b99b07 Mon Sep 17 00:00:00 2001 From: Andrii Nakryiko Date: Wed, 7 Dec 2022 12:16:48 -0800 Subject: [PATCH 1662/2002] selftests/bpf: convert dynptr_fail and map_kptr_fail subtests to generic tester Convert big chunks of dynptr and map_kptr subtests to use generic verification_tester. They are switched from using manually maintained tables of test cases, specifying program name and expected error verifier message, to btf_decl_tag-based annotations directly on corresponding BPF programs: __failure to specify that BPF program is expected to fail verification, and __msg() to specify expected log message. Acked-by: John Fastabend Signed-off-by: Andrii Nakryiko Acked-by: Kumar Kartikeya Dwivedi Link: https://lore.kernel.org/r/20221207201648.2990661-2-andrii@kernel.org Signed-off-by: Alexei Starovoitov --- .../testing/selftests/bpf/prog_tests/dynptr.c | 80 +------------------ .../selftests/bpf/prog_tests/map_kptr.c | 80 +------------------ .../testing/selftests/bpf/progs/dynptr_fail.c | 31 +++++++ .../selftests/bpf/progs/dynptr_success.c | 1 + .../selftests/bpf/progs/map_kptr_fail.c | 27 +++++++ 5 files changed, 64 insertions(+), 155 deletions(-) diff --git a/tools/testing/selftests/bpf/prog_tests/dynptr.c b/tools/testing/selftests/bpf/prog_tests/dynptr.c index b0c06f821cb8..7faaf6d9e0d4 100644 --- a/tools/testing/selftests/bpf/prog_tests/dynptr.c +++ b/tools/testing/selftests/bpf/prog_tests/dynptr.c @@ -5,86 +5,16 @@ #include "dynptr_fail.skel.h" #include "dynptr_success.skel.h" -static size_t log_buf_sz = 1048576; /* 1 MB */ -static char obj_log_buf[1048576]; - static struct { const char *prog_name; const char *expected_err_msg; } dynptr_tests[] = { - /* failure cases */ - {"ringbuf_missing_release1", "Unreleased reference id=1"}, - {"ringbuf_missing_release2", "Unreleased reference id=2"}, - {"ringbuf_missing_release_callback", "Unreleased reference id"}, - {"use_after_invalid", "Expected an initialized dynptr as arg #3"}, - {"ringbuf_invalid_api", "type=mem expected=ringbuf_mem"}, - {"add_dynptr_to_map1", "invalid indirect read from stack"}, - {"add_dynptr_to_map2", "invalid indirect read from stack"}, - {"data_slice_out_of_bounds_ringbuf", "value is outside of the allowed memory range"}, - {"data_slice_out_of_bounds_map_value", "value is outside of the allowed memory range"}, - {"data_slice_use_after_release1", "invalid mem access 'scalar'"}, - {"data_slice_use_after_release2", "invalid mem access 'scalar'"}, - {"data_slice_missing_null_check1", "invalid mem access 'mem_or_null'"}, - {"data_slice_missing_null_check2", "invalid mem access 'mem_or_null'"}, - {"invalid_helper1", "invalid indirect read from stack"}, - {"invalid_helper2", "Expected an initialized dynptr as arg #3"}, - {"invalid_write1", "Expected an initialized dynptr as arg #1"}, - {"invalid_write2", "Expected an initialized dynptr as arg #3"}, - {"invalid_write3", "Expected an initialized dynptr as arg #1"}, - {"invalid_write4", "arg 1 is an unacquired reference"}, - {"invalid_read1", "invalid read from stack"}, - {"invalid_read2", "cannot pass in dynptr at an offset"}, - {"invalid_read3", "invalid read from stack"}, - {"invalid_read4", "invalid read from stack"}, - {"invalid_offset", "invalid write to stack"}, - {"global", "type=map_value expected=fp"}, - {"release_twice", "arg 1 is an unacquired reference"}, - {"release_twice_callback", "arg 1 is an unacquired reference"}, - {"dynptr_from_mem_invalid_api", - "Unsupported reg type fp for bpf_dynptr_from_mem data"}, - /* success cases */ {"test_read_write", NULL}, {"test_data_slice", NULL}, {"test_ringbuf", NULL}, }; -static void verify_fail(const char *prog_name, const char *expected_err_msg) -{ - LIBBPF_OPTS(bpf_object_open_opts, opts); - struct bpf_program *prog; - struct dynptr_fail *skel; - int err; - - opts.kernel_log_buf = obj_log_buf; - opts.kernel_log_size = log_buf_sz; - opts.kernel_log_level = 1; - - skel = dynptr_fail__open_opts(&opts); - if (!ASSERT_OK_PTR(skel, "dynptr_fail__open_opts")) - goto cleanup; - - prog = bpf_object__find_program_by_name(skel->obj, prog_name); - if (!ASSERT_OK_PTR(prog, "bpf_object__find_program_by_name")) - goto cleanup; - - bpf_program__set_autoload(prog, true); - - bpf_map__set_max_entries(skel->maps.ringbuf, getpagesize()); - - err = dynptr_fail__load(skel); - if (!ASSERT_ERR(err, "unexpected load success")) - goto cleanup; - - if (!ASSERT_OK_PTR(strstr(obj_log_buf, expected_err_msg), "expected_err_msg")) { - fprintf(stderr, "Expected err_msg: %s\n", expected_err_msg); - fprintf(stderr, "Verifier output: %s\n", obj_log_buf); - } - -cleanup: - dynptr_fail__destroy(skel); -} - static void verify_success(const char *prog_name) { struct dynptr_success *skel; @@ -97,8 +27,6 @@ static void verify_success(const char *prog_name) skel->bss->pid = getpid(); - bpf_map__set_max_entries(skel->maps.ringbuf, getpagesize()); - dynptr_success__load(skel); if (!ASSERT_OK_PTR(skel, "dynptr_success__load")) goto cleanup; @@ -129,10 +57,8 @@ void test_dynptr(void) if (!test__start_subtest(dynptr_tests[i].prog_name)) continue; - if (dynptr_tests[i].expected_err_msg) - verify_fail(dynptr_tests[i].prog_name, - dynptr_tests[i].expected_err_msg); - else - verify_success(dynptr_tests[i].prog_name); + verify_success(dynptr_tests[i].prog_name); } + + RUN_TESTS(dynptr_fail); } diff --git a/tools/testing/selftests/bpf/prog_tests/map_kptr.c b/tools/testing/selftests/bpf/prog_tests/map_kptr.c index 0d66b1524208..3533a4ecad01 100644 --- a/tools/testing/selftests/bpf/prog_tests/map_kptr.c +++ b/tools/testing/selftests/bpf/prog_tests/map_kptr.c @@ -5,83 +5,6 @@ #include "map_kptr.skel.h" #include "map_kptr_fail.skel.h" -static char log_buf[1024 * 1024]; - -struct { - const char *prog_name; - const char *err_msg; -} map_kptr_fail_tests[] = { - { "size_not_bpf_dw", "kptr access size must be BPF_DW" }, - { "non_const_var_off", "kptr access cannot have variable offset" }, - { "non_const_var_off_kptr_xchg", "R1 doesn't have constant offset. kptr has to be" }, - { "misaligned_access_write", "kptr access misaligned expected=8 off=7" }, - { "misaligned_access_read", "kptr access misaligned expected=8 off=1" }, - { "reject_var_off_store", "variable untrusted_ptr_ access var_off=(0x0; 0x1e0)" }, - { "reject_bad_type_match", "invalid kptr access, R1 type=untrusted_ptr_prog_test_ref_kfunc" }, - { "marked_as_untrusted_or_null", "R1 type=untrusted_ptr_or_null_ expected=percpu_ptr_" }, - { "correct_btf_id_check_size", "access beyond struct prog_test_ref_kfunc at off 32 size 4" }, - { "inherit_untrusted_on_walk", "R1 type=untrusted_ptr_ expected=percpu_ptr_" }, - { "reject_kptr_xchg_on_unref", "off=8 kptr isn't referenced kptr" }, - { "reject_kptr_get_no_map_val", "arg#0 expected pointer to map value" }, - { "reject_kptr_get_no_null_map_val", "arg#0 expected pointer to map value" }, - { "reject_kptr_get_no_kptr", "arg#0 no referenced kptr at map value offset=0" }, - { "reject_kptr_get_on_unref", "arg#0 no referenced kptr at map value offset=8" }, - { "reject_kptr_get_bad_type_match", "kernel function bpf_kfunc_call_test_kptr_get args#0" }, - { "mark_ref_as_untrusted_or_null", "R1 type=untrusted_ptr_or_null_ expected=percpu_ptr_" }, - { "reject_untrusted_store_to_ref", "store to referenced kptr disallowed" }, - { "reject_bad_type_xchg", "invalid kptr access, R2 type=ptr_prog_test_ref_kfunc expected=ptr_prog_test_member" }, - { "reject_untrusted_xchg", "R2 type=untrusted_ptr_ expected=ptr_" }, - { "reject_member_of_ref_xchg", "invalid kptr access, R2 type=ptr_prog_test_ref_kfunc" }, - { "reject_indirect_helper_access", "kptr cannot be accessed indirectly by helper" }, - { "reject_indirect_global_func_access", "kptr cannot be accessed indirectly by helper" }, - { "kptr_xchg_ref_state", "Unreleased reference id=5 alloc_insn=" }, - { "kptr_get_ref_state", "Unreleased reference id=3 alloc_insn=" }, -}; - -static void test_map_kptr_fail_prog(const char *prog_name, const char *err_msg) -{ - LIBBPF_OPTS(bpf_object_open_opts, opts, .kernel_log_buf = log_buf, - .kernel_log_size = sizeof(log_buf), - .kernel_log_level = 1); - struct map_kptr_fail *skel; - struct bpf_program *prog; - int ret; - - skel = map_kptr_fail__open_opts(&opts); - if (!ASSERT_OK_PTR(skel, "map_kptr_fail__open_opts")) - return; - - prog = bpf_object__find_program_by_name(skel->obj, prog_name); - if (!ASSERT_OK_PTR(prog, "bpf_object__find_program_by_name")) - goto end; - - bpf_program__set_autoload(prog, true); - - ret = map_kptr_fail__load(skel); - if (!ASSERT_ERR(ret, "map_kptr__load must fail")) - goto end; - - if (!ASSERT_OK_PTR(strstr(log_buf, err_msg), "expected error message")) { - fprintf(stderr, "Expected: %s\n", err_msg); - fprintf(stderr, "Verifier: %s\n", log_buf); - } - -end: - map_kptr_fail__destroy(skel); -} - -static void test_map_kptr_fail(void) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(map_kptr_fail_tests); i++) { - if (!test__start_subtest(map_kptr_fail_tests[i].prog_name)) - continue; - test_map_kptr_fail_prog(map_kptr_fail_tests[i].prog_name, - map_kptr_fail_tests[i].err_msg); - } -} - static void test_map_kptr_success(bool test_run) { LIBBPF_OPTS(bpf_test_run_opts, opts, @@ -145,5 +68,6 @@ void test_map_kptr(void) */ test_map_kptr_success(true); } - test_map_kptr_fail(); + + RUN_TESTS(map_kptr_fail); } diff --git a/tools/testing/selftests/bpf/progs/dynptr_fail.c b/tools/testing/selftests/bpf/progs/dynptr_fail.c index b0f08ff024fb..78debc1b3820 100644 --- a/tools/testing/selftests/bpf/progs/dynptr_fail.c +++ b/tools/testing/selftests/bpf/progs/dynptr_fail.c @@ -43,6 +43,7 @@ struct sample { struct { __uint(type, BPF_MAP_TYPE_RINGBUF); + __uint(max_entries, 4096); } ringbuf SEC(".maps"); int err, val; @@ -66,6 +67,7 @@ static int get_map_val_dynptr(struct bpf_dynptr *ptr) * bpf_ringbuf_submit/discard_dynptr call */ SEC("?raw_tp") +__failure __msg("Unreleased reference id=1") int ringbuf_missing_release1(void *ctx) { struct bpf_dynptr ptr; @@ -78,6 +80,7 @@ int ringbuf_missing_release1(void *ctx) } SEC("?raw_tp") +__failure __msg("Unreleased reference id=2") int ringbuf_missing_release2(void *ctx) { struct bpf_dynptr ptr1, ptr2; @@ -113,6 +116,7 @@ static int missing_release_callback_fn(__u32 index, void *data) /* Any dynptr initialized within a callback must have bpf_dynptr_put called */ SEC("?raw_tp") +__failure __msg("Unreleased reference id") int ringbuf_missing_release_callback(void *ctx) { bpf_loop(10, missing_release_callback_fn, NULL, 0); @@ -121,6 +125,7 @@ int ringbuf_missing_release_callback(void *ctx) /* Can't call bpf_ringbuf_submit/discard_dynptr on a non-initialized dynptr */ SEC("?raw_tp") +__failure __msg("arg 1 is an unacquired reference") int ringbuf_release_uninit_dynptr(void *ctx) { struct bpf_dynptr ptr; @@ -133,6 +138,7 @@ int ringbuf_release_uninit_dynptr(void *ctx) /* A dynptr can't be used after it has been invalidated */ SEC("?raw_tp") +__failure __msg("Expected an initialized dynptr as arg #3") int use_after_invalid(void *ctx) { struct bpf_dynptr ptr; @@ -152,6 +158,7 @@ int use_after_invalid(void *ctx) /* Can't call non-dynptr ringbuf APIs on a dynptr ringbuf sample */ SEC("?raw_tp") +__failure __msg("type=mem expected=ringbuf_mem") int ringbuf_invalid_api(void *ctx) { struct bpf_dynptr ptr; @@ -174,6 +181,7 @@ done: /* Can't add a dynptr to a map */ SEC("?raw_tp") +__failure __msg("invalid indirect read from stack") int add_dynptr_to_map1(void *ctx) { struct bpf_dynptr ptr; @@ -191,6 +199,7 @@ int add_dynptr_to_map1(void *ctx) /* Can't add a struct with an embedded dynptr to a map */ SEC("?raw_tp") +__failure __msg("invalid indirect read from stack") int add_dynptr_to_map2(void *ctx) { struct test_info x; @@ -208,6 +217,7 @@ int add_dynptr_to_map2(void *ctx) /* A data slice can't be accessed out of bounds */ SEC("?raw_tp") +__failure __msg("value is outside of the allowed memory range") int data_slice_out_of_bounds_ringbuf(void *ctx) { struct bpf_dynptr ptr; @@ -228,6 +238,7 @@ done: } SEC("?raw_tp") +__failure __msg("value is outside of the allowed memory range") int data_slice_out_of_bounds_map_value(void *ctx) { __u32 key = 0, map_val; @@ -248,6 +259,7 @@ int data_slice_out_of_bounds_map_value(void *ctx) /* A data slice can't be used after it has been released */ SEC("?raw_tp") +__failure __msg("invalid mem access 'scalar'") int data_slice_use_after_release1(void *ctx) { struct bpf_dynptr ptr; @@ -279,6 +291,7 @@ done: * ptr2 is at fp - 16). */ SEC("?raw_tp") +__failure __msg("invalid mem access 'scalar'") int data_slice_use_after_release2(void *ctx) { struct bpf_dynptr ptr1, ptr2; @@ -310,6 +323,7 @@ done: /* A data slice must be first checked for NULL */ SEC("?raw_tp") +__failure __msg("invalid mem access 'mem_or_null'") int data_slice_missing_null_check1(void *ctx) { struct bpf_dynptr ptr; @@ -330,6 +344,7 @@ int data_slice_missing_null_check1(void *ctx) /* A data slice can't be dereferenced if it wasn't checked for null */ SEC("?raw_tp") +__failure __msg("invalid mem access 'mem_or_null'") int data_slice_missing_null_check2(void *ctx) { struct bpf_dynptr ptr; @@ -352,6 +367,7 @@ done: * dynptr argument */ SEC("?raw_tp") +__failure __msg("invalid indirect read from stack") int invalid_helper1(void *ctx) { struct bpf_dynptr ptr; @@ -366,6 +382,7 @@ int invalid_helper1(void *ctx) /* A dynptr can't be passed into a helper function at a non-zero offset */ SEC("?raw_tp") +__failure __msg("Expected an initialized dynptr as arg #3") int invalid_helper2(void *ctx) { struct bpf_dynptr ptr; @@ -381,6 +398,7 @@ int invalid_helper2(void *ctx) /* A bpf_dynptr is invalidated if it's been written into */ SEC("?raw_tp") +__failure __msg("Expected an initialized dynptr as arg #1") int invalid_write1(void *ctx) { struct bpf_dynptr ptr; @@ -402,6 +420,7 @@ int invalid_write1(void *ctx) * offset */ SEC("?raw_tp") +__failure __msg("Expected an initialized dynptr as arg #3") int invalid_write2(void *ctx) { struct bpf_dynptr ptr; @@ -425,6 +444,7 @@ int invalid_write2(void *ctx) * non-const offset */ SEC("?raw_tp") +__failure __msg("Expected an initialized dynptr as arg #1") int invalid_write3(void *ctx) { struct bpf_dynptr ptr; @@ -456,6 +476,7 @@ static int invalid_write4_callback(__u32 index, void *data) * be invalidated as a dynptr */ SEC("?raw_tp") +__failure __msg("arg 1 is an unacquired reference") int invalid_write4(void *ctx) { struct bpf_dynptr ptr; @@ -472,7 +493,9 @@ int invalid_write4(void *ctx) /* A globally-defined bpf_dynptr can't be used (it must reside as a stack frame) */ struct bpf_dynptr global_dynptr; + SEC("?raw_tp") +__failure __msg("type=map_value expected=fp") int global(void *ctx) { /* this should fail */ @@ -485,6 +508,7 @@ int global(void *ctx) /* A direct read should fail */ SEC("?raw_tp") +__failure __msg("invalid read from stack") int invalid_read1(void *ctx) { struct bpf_dynptr ptr; @@ -501,6 +525,7 @@ int invalid_read1(void *ctx) /* A direct read at an offset should fail */ SEC("?raw_tp") +__failure __msg("cannot pass in dynptr at an offset") int invalid_read2(void *ctx) { struct bpf_dynptr ptr; @@ -516,6 +541,7 @@ int invalid_read2(void *ctx) /* A direct read at an offset into the lower stack slot should fail */ SEC("?raw_tp") +__failure __msg("invalid read from stack") int invalid_read3(void *ctx) { struct bpf_dynptr ptr1, ptr2; @@ -542,6 +568,7 @@ static int invalid_read4_callback(__u32 index, void *data) /* A direct read within a callback function should fail */ SEC("?raw_tp") +__failure __msg("invalid read from stack") int invalid_read4(void *ctx) { struct bpf_dynptr ptr; @@ -557,6 +584,7 @@ int invalid_read4(void *ctx) /* Initializing a dynptr on an offset should fail */ SEC("?raw_tp") +__failure __msg("invalid write to stack") int invalid_offset(void *ctx) { struct bpf_dynptr ptr; @@ -571,6 +599,7 @@ int invalid_offset(void *ctx) /* Can't release a dynptr twice */ SEC("?raw_tp") +__failure __msg("arg 1 is an unacquired reference") int release_twice(void *ctx) { struct bpf_dynptr ptr; @@ -597,6 +626,7 @@ static int release_twice_callback_fn(__u32 index, void *data) * within a calback function, fails */ SEC("?raw_tp") +__failure __msg("arg 1 is an unacquired reference") int release_twice_callback(void *ctx) { struct bpf_dynptr ptr; @@ -612,6 +642,7 @@ int release_twice_callback(void *ctx) /* Reject unsupported local mem types for dynptr_from_mem API */ SEC("?raw_tp") +__failure __msg("Unsupported reg type fp for bpf_dynptr_from_mem data") int dynptr_from_mem_invalid_api(void *ctx) { struct bpf_dynptr ptr; diff --git a/tools/testing/selftests/bpf/progs/dynptr_success.c b/tools/testing/selftests/bpf/progs/dynptr_success.c index a3a6103c8569..35db7c6c1fc7 100644 --- a/tools/testing/selftests/bpf/progs/dynptr_success.c +++ b/tools/testing/selftests/bpf/progs/dynptr_success.c @@ -20,6 +20,7 @@ struct sample { struct { __uint(type, BPF_MAP_TYPE_RINGBUF); + __uint(max_entries, 4096); } ringbuf SEC(".maps"); struct { diff --git a/tools/testing/selftests/bpf/progs/map_kptr_fail.c b/tools/testing/selftests/bpf/progs/map_kptr_fail.c index 05e209b1b12a..760e41e1a632 100644 --- a/tools/testing/selftests/bpf/progs/map_kptr_fail.c +++ b/tools/testing/selftests/bpf/progs/map_kptr_fail.c @@ -3,6 +3,7 @@ #include #include #include +#include "bpf_misc.h" struct map_value { char buf[8]; @@ -23,6 +24,7 @@ extern struct prog_test_ref_kfunc * bpf_kfunc_call_test_kptr_get(struct prog_test_ref_kfunc **p, int a, int b) __ksym; SEC("?tc") +__failure __msg("kptr access size must be BPF_DW") int size_not_bpf_dw(struct __sk_buff *ctx) { struct map_value *v; @@ -37,6 +39,7 @@ int size_not_bpf_dw(struct __sk_buff *ctx) } SEC("?tc") +__failure __msg("kptr access cannot have variable offset") int non_const_var_off(struct __sk_buff *ctx) { struct map_value *v; @@ -55,6 +58,7 @@ int non_const_var_off(struct __sk_buff *ctx) } SEC("?tc") +__failure __msg("R1 doesn't have constant offset. kptr has to be") int non_const_var_off_kptr_xchg(struct __sk_buff *ctx) { struct map_value *v; @@ -73,6 +77,7 @@ int non_const_var_off_kptr_xchg(struct __sk_buff *ctx) } SEC("?tc") +__failure __msg("kptr access misaligned expected=8 off=7") int misaligned_access_write(struct __sk_buff *ctx) { struct map_value *v; @@ -88,6 +93,7 @@ int misaligned_access_write(struct __sk_buff *ctx) } SEC("?tc") +__failure __msg("kptr access misaligned expected=8 off=1") int misaligned_access_read(struct __sk_buff *ctx) { struct map_value *v; @@ -101,6 +107,7 @@ int misaligned_access_read(struct __sk_buff *ctx) } SEC("?tc") +__failure __msg("variable untrusted_ptr_ access var_off=(0x0; 0x1e0)") int reject_var_off_store(struct __sk_buff *ctx) { struct prog_test_ref_kfunc *unref_ptr; @@ -124,6 +131,7 @@ int reject_var_off_store(struct __sk_buff *ctx) } SEC("?tc") +__failure __msg("invalid kptr access, R1 type=untrusted_ptr_prog_test_ref_kfunc") int reject_bad_type_match(struct __sk_buff *ctx) { struct prog_test_ref_kfunc *unref_ptr; @@ -144,6 +152,7 @@ int reject_bad_type_match(struct __sk_buff *ctx) } SEC("?tc") +__failure __msg("R1 type=untrusted_ptr_or_null_ expected=percpu_ptr_") int marked_as_untrusted_or_null(struct __sk_buff *ctx) { struct map_value *v; @@ -158,6 +167,7 @@ int marked_as_untrusted_or_null(struct __sk_buff *ctx) } SEC("?tc") +__failure __msg("access beyond struct prog_test_ref_kfunc at off 32 size 4") int correct_btf_id_check_size(struct __sk_buff *ctx) { struct prog_test_ref_kfunc *p; @@ -175,6 +185,7 @@ int correct_btf_id_check_size(struct __sk_buff *ctx) } SEC("?tc") +__failure __msg("R1 type=untrusted_ptr_ expected=percpu_ptr_") int inherit_untrusted_on_walk(struct __sk_buff *ctx) { struct prog_test_ref_kfunc *unref_ptr; @@ -194,6 +205,7 @@ int inherit_untrusted_on_walk(struct __sk_buff *ctx) } SEC("?tc") +__failure __msg("off=8 kptr isn't referenced kptr") int reject_kptr_xchg_on_unref(struct __sk_buff *ctx) { struct map_value *v; @@ -208,6 +220,7 @@ int reject_kptr_xchg_on_unref(struct __sk_buff *ctx) } SEC("?tc") +__failure __msg("arg#0 expected pointer to map value") int reject_kptr_get_no_map_val(struct __sk_buff *ctx) { bpf_kfunc_call_test_kptr_get((void *)&ctx, 0, 0); @@ -215,6 +228,7 @@ int reject_kptr_get_no_map_val(struct __sk_buff *ctx) } SEC("?tc") +__failure __msg("arg#0 expected pointer to map value") int reject_kptr_get_no_null_map_val(struct __sk_buff *ctx) { bpf_kfunc_call_test_kptr_get(bpf_map_lookup_elem(&array_map, &(int){0}), 0, 0); @@ -222,6 +236,7 @@ int reject_kptr_get_no_null_map_val(struct __sk_buff *ctx) } SEC("?tc") +__failure __msg("arg#0 no referenced kptr at map value offset=0") int reject_kptr_get_no_kptr(struct __sk_buff *ctx) { struct map_value *v; @@ -236,6 +251,7 @@ int reject_kptr_get_no_kptr(struct __sk_buff *ctx) } SEC("?tc") +__failure __msg("arg#0 no referenced kptr at map value offset=8") int reject_kptr_get_on_unref(struct __sk_buff *ctx) { struct map_value *v; @@ -250,6 +266,7 @@ int reject_kptr_get_on_unref(struct __sk_buff *ctx) } SEC("?tc") +__failure __msg("kernel function bpf_kfunc_call_test_kptr_get args#0") int reject_kptr_get_bad_type_match(struct __sk_buff *ctx) { struct map_value *v; @@ -264,6 +281,7 @@ int reject_kptr_get_bad_type_match(struct __sk_buff *ctx) } SEC("?tc") +__failure __msg("R1 type=untrusted_ptr_or_null_ expected=percpu_ptr_") int mark_ref_as_untrusted_or_null(struct __sk_buff *ctx) { struct map_value *v; @@ -278,6 +296,7 @@ int mark_ref_as_untrusted_or_null(struct __sk_buff *ctx) } SEC("?tc") +__failure __msg("store to referenced kptr disallowed") int reject_untrusted_store_to_ref(struct __sk_buff *ctx) { struct prog_test_ref_kfunc *p; @@ -297,6 +316,7 @@ int reject_untrusted_store_to_ref(struct __sk_buff *ctx) } SEC("?tc") +__failure __msg("R2 type=untrusted_ptr_ expected=ptr_") int reject_untrusted_xchg(struct __sk_buff *ctx) { struct prog_test_ref_kfunc *p; @@ -315,6 +335,8 @@ int reject_untrusted_xchg(struct __sk_buff *ctx) } SEC("?tc") +__failure +__msg("invalid kptr access, R2 type=ptr_prog_test_ref_kfunc expected=ptr_prog_test_member") int reject_bad_type_xchg(struct __sk_buff *ctx) { struct prog_test_ref_kfunc *ref_ptr; @@ -333,6 +355,7 @@ int reject_bad_type_xchg(struct __sk_buff *ctx) } SEC("?tc") +__failure __msg("invalid kptr access, R2 type=ptr_prog_test_ref_kfunc") int reject_member_of_ref_xchg(struct __sk_buff *ctx) { struct prog_test_ref_kfunc *ref_ptr; @@ -351,6 +374,7 @@ int reject_member_of_ref_xchg(struct __sk_buff *ctx) } SEC("?syscall") +__failure __msg("kptr cannot be accessed indirectly by helper") int reject_indirect_helper_access(struct __sk_buff *ctx) { struct map_value *v; @@ -371,6 +395,7 @@ int write_func(int *p) } SEC("?tc") +__failure __msg("kptr cannot be accessed indirectly by helper") int reject_indirect_global_func_access(struct __sk_buff *ctx) { struct map_value *v; @@ -384,6 +409,7 @@ int reject_indirect_global_func_access(struct __sk_buff *ctx) } SEC("?tc") +__failure __msg("Unreleased reference id=5 alloc_insn=") int kptr_xchg_ref_state(struct __sk_buff *ctx) { struct prog_test_ref_kfunc *p; @@ -402,6 +428,7 @@ int kptr_xchg_ref_state(struct __sk_buff *ctx) } SEC("?tc") +__failure __msg("Unreleased reference id=3 alloc_insn=") int kptr_get_ref_state(struct __sk_buff *ctx) { struct map_value *v; From 25c5e92d197bd721e706444c5910fd386c330456 Mon Sep 17 00:00:00 2001 From: David Vernet Date: Wed, 7 Dec 2022 14:49:10 -0600 Subject: [PATCH 1663/2002] bpf/docs: Document struct task_struct * kfuncs bpf_task_acquire(), bpf_task_release(), and bpf_task_from_pid() are kfuncs that were recently added to kernel/bpf/helpers.c. These are "core" kfuncs in that they're available for use for any tracepoint or struct_ops BPF program. Though they have no ABI stability guarantees, we should still document them. This patch adds a new Core kfuncs section to the BPF kfuncs doc, and adds entries for all of these task kfuncs. Note that bpf_task_kptr_get() is not documented, as it still returns NULL while we're working to resolve how it can use RCU to ensure struct task_struct * lifetime. Signed-off-by: David Vernet Link: https://lore.kernel.org/r/20221207204911.873646-2-void@manifault.com Signed-off-by: Alexei Starovoitov --- Documentation/bpf/kfuncs.rst | 83 ++++++++++++++++++++++++++++++++++++ kernel/bpf/helpers.c | 2 +- 2 files changed, 84 insertions(+), 1 deletion(-) diff --git a/Documentation/bpf/kfuncs.rst b/Documentation/bpf/kfuncs.rst index b027fe16ee66..24ed109afc98 100644 --- a/Documentation/bpf/kfuncs.rst +++ b/Documentation/bpf/kfuncs.rst @@ -222,3 +222,86 @@ type. An example is shown below:: return register_btf_kfunc_id_set(BPF_PROG_TYPE_TRACING, &bpf_task_kfunc_set); } late_initcall(init_subsystem); + +3. Core kfuncs +============== + +The BPF subsystem provides a number of "core" kfuncs that are potentially +applicable to a wide variety of different possible use cases and programs. +Those kfuncs are documented here. + +3.1 struct task_struct * kfuncs +------------------------------- + +There are a number of kfuncs that allow ``struct task_struct *`` objects to be +used as kptrs: + +.. kernel-doc:: kernel/bpf/helpers.c + :identifiers: bpf_task_acquire bpf_task_release + +These kfuncs are useful when you want to acquire or release a reference to a +``struct task_struct *`` that was passed as e.g. a tracepoint arg, or a +struct_ops callback arg. For example: + +.. code-block:: c + + /** + * A trivial example tracepoint program that shows how to + * acquire and release a struct task_struct * pointer. + */ + SEC("tp_btf/task_newtask") + int BPF_PROG(task_acquire_release_example, struct task_struct *task, u64 clone_flags) + { + struct task_struct *acquired; + + acquired = bpf_task_acquire(task); + + /* + * In a typical program you'd do something like store + * the task in a map, and the map will automatically + * release it later. Here, we release it manually. + */ + bpf_task_release(acquired); + return 0; + } + +---- + +A BPF program can also look up a task from a pid. This can be useful if the +caller doesn't have a trusted pointer to a ``struct task_struct *`` object that +it can acquire a reference on with bpf_task_acquire(). + +.. kernel-doc:: kernel/bpf/helpers.c + :identifiers: bpf_task_from_pid + +Here is an example of it being used: + +.. code-block:: c + + SEC("tp_btf/task_newtask") + int BPF_PROG(task_get_pid_example, struct task_struct *task, u64 clone_flags) + { + struct task_struct *lookup; + + lookup = bpf_task_from_pid(task->pid); + if (!lookup) + /* A task should always be found, as %task is a tracepoint arg. */ + return -ENOENT; + + if (lookup->pid != task->pid) { + /* bpf_task_from_pid() looks up the task via its + * globally-unique pid from the init_pid_ns. Thus, + * the pid of the lookup task should always be the + * same as the input task. + */ + bpf_task_release(lookup); + return -EINVAL; + } + + /* bpf_task_from_pid() returns an acquired reference, + * so it must be dropped before returning from the + * tracepoint handler. + */ + bpf_task_release(lookup); + return 0; + } diff --git a/kernel/bpf/helpers.c b/kernel/bpf/helpers.c index 284b3ffdbe48..1e4bf466b08f 100644 --- a/kernel/bpf/helpers.c +++ b/kernel/bpf/helpers.c @@ -1904,7 +1904,7 @@ struct task_struct *bpf_task_kptr_get(struct task_struct **pp) } /** - * bpf_task_release - Release the reference acquired on a struct task_struct *. + * bpf_task_release - Release the reference acquired on a task. * @p: The task on which a reference is being released. */ void bpf_task_release(struct task_struct *p) From 36aa10ffd6480b93e32611411be4a8fc49804aba Mon Sep 17 00:00:00 2001 From: David Vernet Date: Wed, 7 Dec 2022 14:49:11 -0600 Subject: [PATCH 1664/2002] bpf/docs: Document struct cgroup * kfuncs bpf_cgroup_acquire(), bpf_cgroup_release(), bpf_cgroup_kptr_get(), and bpf_cgroup_ancestor(), are kfuncs that were recently added to kernel/bpf/helpers.c. These are "core" kfuncs in that they're available for use in any tracepoint or struct_ops BPF program. Though they have no ABI stability guarantees, we should still document them. This patch adds a struct cgroup * subsection to the Core kfuncs section which describes each of these kfuncs. Signed-off-by: David Vernet Link: https://lore.kernel.org/r/20221207204911.873646-3-void@manifault.com Signed-off-by: Alexei Starovoitov --- Documentation/bpf/kfuncs.rst | 115 +++++++++++++++++++++++++++++++++++ kernel/bpf/helpers.c | 2 +- 2 files changed, 116 insertions(+), 1 deletion(-) diff --git a/Documentation/bpf/kfuncs.rst b/Documentation/bpf/kfuncs.rst index 24ed109afc98..9fd7fb539f85 100644 --- a/Documentation/bpf/kfuncs.rst +++ b/Documentation/bpf/kfuncs.rst @@ -305,3 +305,118 @@ Here is an example of it being used: bpf_task_release(lookup); return 0; } + +3.2 struct cgroup * kfuncs +-------------------------- + +``struct cgroup *`` objects also have acquire and release functions: + +.. kernel-doc:: kernel/bpf/helpers.c + :identifiers: bpf_cgroup_acquire bpf_cgroup_release + +These kfuncs are used in exactly the same manner as bpf_task_acquire() and +bpf_task_release() respectively, so we won't provide examples for them. + +---- + +You may also acquire a reference to a ``struct cgroup`` kptr that's already +stored in a map using bpf_cgroup_kptr_get(): + +.. kernel-doc:: kernel/bpf/helpers.c + :identifiers: bpf_cgroup_kptr_get + +Here's an example of how it can be used: + +.. code-block:: c + + /* struct containing the struct task_struct kptr which is actually stored in the map. */ + struct __cgroups_kfunc_map_value { + struct cgroup __kptr_ref * cgroup; + }; + + /* The map containing struct __cgroups_kfunc_map_value entries. */ + struct { + __uint(type, BPF_MAP_TYPE_HASH); + __type(key, int); + __type(value, struct __cgroups_kfunc_map_value); + __uint(max_entries, 1); + } __cgroups_kfunc_map SEC(".maps"); + + /* ... */ + + /** + * A simple example tracepoint program showing how a + * struct cgroup kptr that is stored in a map can + * be acquired using the bpf_cgroup_kptr_get() kfunc. + */ + SEC("tp_btf/cgroup_mkdir") + int BPF_PROG(cgroup_kptr_get_example, struct cgroup *cgrp, const char *path) + { + struct cgroup *kptr; + struct __cgroups_kfunc_map_value *v; + s32 id = cgrp->self.id; + + /* Assume a cgroup kptr was previously stored in the map. */ + v = bpf_map_lookup_elem(&__cgroups_kfunc_map, &id); + if (!v) + return -ENOENT; + + /* Acquire a reference to the cgroup kptr that's already stored in the map. */ + kptr = bpf_cgroup_kptr_get(&v->cgroup); + if (!kptr) + /* If no cgroup was present in the map, it's because + * we're racing with another CPU that removed it with + * bpf_kptr_xchg() between the bpf_map_lookup_elem() + * above, and our call to bpf_cgroup_kptr_get(). + * bpf_cgroup_kptr_get() internally safely handles this + * race, and will return NULL if the task is no longer + * present in the map by the time we invoke the kfunc. + */ + return -EBUSY; + + /* Free the reference we just took above. Note that the + * original struct cgroup kptr is still in the map. It will + * be freed either at a later time if another context deletes + * it from the map, or automatically by the BPF subsystem if + * it's still present when the map is destroyed. + */ + bpf_cgroup_release(kptr); + + return 0; + } + +---- + +Another kfunc available for interacting with ``struct cgroup *`` objects is +bpf_cgroup_ancestor(). This allows callers to access the ancestor of a cgroup, +and return it as a cgroup kptr. + +.. kernel-doc:: kernel/bpf/helpers.c + :identifiers: bpf_cgroup_ancestor + +Eventually, BPF should be updated to allow this to happen with a normal memory +load in the program itself. This is currently not possible without more work in +the verifier. bpf_cgroup_ancestor() can be used as follows: + +.. code-block:: c + + /** + * Simple tracepoint example that illustrates how a cgroup's + * ancestor can be accessed using bpf_cgroup_ancestor(). + */ + SEC("tp_btf/cgroup_mkdir") + int BPF_PROG(cgrp_ancestor_example, struct cgroup *cgrp, const char *path) + { + struct cgroup *parent; + + /* The parent cgroup resides at the level before the current cgroup's level. */ + parent = bpf_cgroup_ancestor(cgrp, cgrp->level - 1); + if (!parent) + return -ENOENT; + + bpf_printk("Parent id is %d", parent->self.id); + + /* Return the parent cgroup that was acquired above. */ + bpf_cgroup_release(parent); + return 0; + } diff --git a/kernel/bpf/helpers.c b/kernel/bpf/helpers.c index 1e4bf466b08f..6ed5875b97a3 100644 --- a/kernel/bpf/helpers.c +++ b/kernel/bpf/helpers.c @@ -1960,7 +1960,7 @@ struct cgroup *bpf_cgroup_kptr_get(struct cgroup **cgrpp) } /** - * bpf_cgroup_release - Release the reference acquired on a struct cgroup *. + * bpf_cgroup_release - Release the reference acquired on a cgroup. * If this kfunc is invoked in an RCU read region, the cgroup is guaranteed to * not be freed until the current grace period has ended, even if its refcount * drops to 0. From 5608e0a817ceb84afca8a8d738c29ffefbe4a398 Mon Sep 17 00:00:00 2001 From: Oleksij Rempel Date: Mon, 5 Dec 2022 14:21:02 +0100 Subject: [PATCH 1665/2002] net: asix: add support for the Linux Automation GmbH USB 10Base-T1L Add ASIX based USB 10Base-T1L adapter support: https://linux-automation.com/en/products/usb-t1l.html Signed-off-by: Oleksij Rempel Link: https://lore.kernel.org/r/20221205132102.2941732-1-o.rempel@pengutronix.de Signed-off-by: Jakub Kicinski --- drivers/net/usb/asix_devices.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/drivers/net/usb/asix_devices.c b/drivers/net/usb/asix_devices.c index 0fe3773c5bca..743cbf5d662c 100644 --- a/drivers/net/usb/asix_devices.c +++ b/drivers/net/usb/asix_devices.c @@ -1350,6 +1350,20 @@ static const struct driver_info ax88772b_info = { .data = FLAG_EEPROM_MAC, }; +static const struct driver_info lxausb_t1l_info = { + .description = "Linux Automation GmbH USB 10Base-T1L", + .bind = ax88772_bind, + .unbind = ax88772_unbind, + .status = asix_status, + .reset = ax88772_reset, + .stop = ax88772_stop, + .flags = FLAG_ETHER | FLAG_FRAMING_AX | FLAG_LINK_INTR | + FLAG_MULTI_PACKET, + .rx_fixup = asix_rx_fixup_common, + .tx_fixup = asix_tx_fixup, + .data = FLAG_EEPROM_MAC, +}; + static const struct driver_info ax88178_info = { .description = "ASIX AX88178 USB 2.0 Ethernet", .bind = ax88178_bind, @@ -1538,6 +1552,10 @@ static const struct usb_device_id products [] = { */ USB_DEVICE(0x066b, 0x20f9), .driver_info = (unsigned long) &hg20f9_info, +}, { + // Linux Automation GmbH USB 10Base-T1L + USB_DEVICE(0x33f7, 0x0004), + .driver_info = (unsigned long) &lxausb_t1l_info, }, { }, // END }; From 16dc16d9f058dce7031ee8b850f10622b8b5fb14 Mon Sep 17 00:00:00 2001 From: ye xingchen Date: Mon, 5 Dec 2022 19:18:56 +0800 Subject: [PATCH 1666/2002] net: ethernet: use sysfs_emit() to instead of scnprintf() Follow the advice of the Documentation/filesystems/sysfs.rst and show() should only use sysfs_emit() or sysfs_emit_at() when formatting the value to be returned to user space. Signed-off-by: ye xingchen Reviewed-by: Leon Romanovsky Link: https://lore.kernel.org/r/202212051918564721658@zte.com.cn Signed-off-by: Jakub Kicinski --- net/ethernet/eth.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/ethernet/eth.c b/net/ethernet/eth.c index e02daa74e833..2edc8b796a4e 100644 --- a/net/ethernet/eth.c +++ b/net/ethernet/eth.c @@ -398,7 +398,7 @@ EXPORT_SYMBOL(alloc_etherdev_mqs); ssize_t sysfs_format_mac(char *buf, const unsigned char *addr, int len) { - return scnprintf(buf, PAGE_SIZE, "%*phC\n", len, addr); + return sysfs_emit(buf, "%*phC\n", len, addr); } EXPORT_SYMBOL(sysfs_format_mac); From cb453926865ea0dddda898c8184eebb595f3c53d Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Tue, 6 Dec 2022 12:58:01 +0200 Subject: [PATCH 1667/2002] bridge: mcast: Centralize netlink attribute parsing Netlink attributes are currently passed deep in the MDB creation call chain, making it difficult to add new attributes. In addition, some validity checks are performed under the multicast lock although they can be performed before it is ever acquired. As a first step towards solving these issues, parse the RTM_{NEW,DEL}MDB messages into a configuration structure, relieving other functions from the need to handle raw netlink attributes. Subsequent patches will convert the MDB code to use this configuration structure. This is consistent with how other rtnetlink objects are handled, such as routes and nexthops. Signed-off-by: Ido Schimmel Acked-by: Nikolay Aleksandrov Signed-off-by: Jakub Kicinski --- net/bridge/br_mdb.c | 120 ++++++++++++++++++++++++++++++++++++++++ net/bridge/br_private.h | 7 +++ 2 files changed, 127 insertions(+) diff --git a/net/bridge/br_mdb.c b/net/bridge/br_mdb.c index 321be94c445a..bd3a7d881d52 100644 --- a/net/bridge/br_mdb.c +++ b/net/bridge/br_mdb.c @@ -974,6 +974,116 @@ static int __br_mdb_add(struct net *net, struct net_bridge *br, return ret; } +static int br_mdb_config_attrs_init(struct nlattr *set_attrs, + struct br_mdb_config *cfg, + struct netlink_ext_ack *extack) +{ + struct nlattr *mdb_attrs[MDBE_ATTR_MAX + 1]; + int err; + + err = nla_parse_nested(mdb_attrs, MDBE_ATTR_MAX, set_attrs, + br_mdbe_attrs_pol, extack); + if (err) + return err; + + if (mdb_attrs[MDBE_ATTR_SOURCE] && + !is_valid_mdb_source(mdb_attrs[MDBE_ATTR_SOURCE], + cfg->entry->addr.proto, extack)) + return -EINVAL; + + __mdb_entry_to_br_ip(cfg->entry, &cfg->group, mdb_attrs); + + return 0; +} + +static int br_mdb_config_init(struct net *net, const struct nlmsghdr *nlh, + struct br_mdb_config *cfg, + struct netlink_ext_ack *extack) +{ + struct nlattr *tb[MDBA_SET_ENTRY_MAX + 1]; + struct br_port_msg *bpm; + struct net_device *dev; + int err; + + err = nlmsg_parse_deprecated(nlh, sizeof(*bpm), tb, + MDBA_SET_ENTRY_MAX, NULL, extack); + if (err) + return err; + + memset(cfg, 0, sizeof(*cfg)); + + bpm = nlmsg_data(nlh); + if (!bpm->ifindex) { + NL_SET_ERR_MSG_MOD(extack, "Invalid bridge ifindex"); + return -EINVAL; + } + + dev = __dev_get_by_index(net, bpm->ifindex); + if (!dev) { + NL_SET_ERR_MSG_MOD(extack, "Bridge device doesn't exist"); + return -ENODEV; + } + + if (!netif_is_bridge_master(dev)) { + NL_SET_ERR_MSG_MOD(extack, "Device is not a bridge"); + return -EOPNOTSUPP; + } + + cfg->br = netdev_priv(dev); + + if (!netif_running(cfg->br->dev)) { + NL_SET_ERR_MSG_MOD(extack, "Bridge device is not running"); + return -EINVAL; + } + + if (!br_opt_get(cfg->br, BROPT_MULTICAST_ENABLED)) { + NL_SET_ERR_MSG_MOD(extack, "Bridge's multicast processing is disabled"); + return -EINVAL; + } + + if (NL_REQ_ATTR_CHECK(extack, NULL, tb, MDBA_SET_ENTRY)) { + NL_SET_ERR_MSG_MOD(extack, "Missing MDBA_SET_ENTRY attribute"); + return -EINVAL; + } + if (nla_len(tb[MDBA_SET_ENTRY]) != sizeof(struct br_mdb_entry)) { + NL_SET_ERR_MSG_MOD(extack, "Invalid MDBA_SET_ENTRY attribute length"); + return -EINVAL; + } + + cfg->entry = nla_data(tb[MDBA_SET_ENTRY]); + if (!is_valid_mdb_entry(cfg->entry, extack)) + return -EINVAL; + + if (cfg->entry->ifindex != cfg->br->dev->ifindex) { + struct net_device *pdev; + + pdev = __dev_get_by_index(net, cfg->entry->ifindex); + if (!pdev) { + NL_SET_ERR_MSG_MOD(extack, "Port net device doesn't exist"); + return -ENODEV; + } + + cfg->p = br_port_get_rtnl(pdev); + if (!cfg->p) { + NL_SET_ERR_MSG_MOD(extack, "Net device is not a bridge port"); + return -EINVAL; + } + + if (cfg->p->br != cfg->br) { + NL_SET_ERR_MSG_MOD(extack, "Port belongs to a different bridge device"); + return -EINVAL; + } + } + + if (tb[MDBA_SET_ENTRY_ATTRS]) + return br_mdb_config_attrs_init(tb[MDBA_SET_ENTRY_ATTRS], cfg, + extack); + else + __mdb_entry_to_br_ip(cfg->entry, &cfg->group, NULL); + + return 0; +} + static int br_mdb_add(struct sk_buff *skb, struct nlmsghdr *nlh, struct netlink_ext_ack *extack) { @@ -984,9 +1094,14 @@ static int br_mdb_add(struct sk_buff *skb, struct nlmsghdr *nlh, struct net_device *dev, *pdev; struct br_mdb_entry *entry; struct net_bridge_vlan *v; + struct br_mdb_config cfg; struct net_bridge *br; int err; + err = br_mdb_config_init(net, nlh, &cfg, extack); + if (err) + return err; + err = br_mdb_parse(skb, nlh, &dev, &entry, mdb_attrs, extack); if (err < 0) return err; @@ -1101,9 +1216,14 @@ static int br_mdb_del(struct sk_buff *skb, struct nlmsghdr *nlh, struct net_device *dev, *pdev; struct br_mdb_entry *entry; struct net_bridge_vlan *v; + struct br_mdb_config cfg; struct net_bridge *br; int err; + err = br_mdb_config_init(net, nlh, &cfg, extack); + if (err) + return err; + err = br_mdb_parse(skb, nlh, &dev, &entry, mdb_attrs, extack); if (err < 0) return err; diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h index 4c4fda930068..0a09f10966dc 100644 --- a/net/bridge/br_private.h +++ b/net/bridge/br_private.h @@ -92,6 +92,13 @@ struct bridge_mcast_stats { struct br_mcast_stats mstats; struct u64_stats_sync syncp; }; + +struct br_mdb_config { + struct net_bridge *br; + struct net_bridge_port *p; + struct br_mdb_entry *entry; + struct br_ip group; +}; #endif /* net_bridge_mcast_port must be always defined due to forwarding stubs */ From 3866116815249be8a5ebfa37ddea9f1032c976a9 Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Tue, 6 Dec 2022 12:58:02 +0200 Subject: [PATCH 1668/2002] bridge: mcast: Remove redundant checks These checks are now redundant as they are performed by br_mdb_config_init() while parsing the RTM_{NEW,DEL}MDB messages. Remove them. Signed-off-by: Ido Schimmel Acked-by: Nikolay Aleksandrov Signed-off-by: Jakub Kicinski --- net/bridge/br_mdb.c | 63 +++++++-------------------------------------- 1 file changed, 9 insertions(+), 54 deletions(-) diff --git a/net/bridge/br_mdb.c b/net/bridge/br_mdb.c index bd3a7d881d52..c8d78e4ec94e 100644 --- a/net/bridge/br_mdb.c +++ b/net/bridge/br_mdb.c @@ -1090,11 +1090,10 @@ static int br_mdb_add(struct sk_buff *skb, struct nlmsghdr *nlh, struct nlattr *mdb_attrs[MDBE_ATTR_MAX + 1]; struct net *net = sock_net(skb->sk); struct net_bridge_vlan_group *vg; - struct net_bridge_port *p = NULL; - struct net_device *dev, *pdev; struct br_mdb_entry *entry; struct net_bridge_vlan *v; struct br_mdb_config cfg; + struct net_device *dev; struct net_bridge *br; int err; @@ -1108,38 +1107,12 @@ static int br_mdb_add(struct sk_buff *skb, struct nlmsghdr *nlh, br = netdev_priv(dev); - if (!netif_running(br->dev)) { - NL_SET_ERR_MSG_MOD(extack, "Bridge device is not running"); - return -EINVAL; - } - - if (!br_opt_get(br, BROPT_MULTICAST_ENABLED)) { - NL_SET_ERR_MSG_MOD(extack, "Bridge's multicast processing is disabled"); - return -EINVAL; - } - if (entry->ifindex != br->dev->ifindex) { - pdev = __dev_get_by_index(net, entry->ifindex); - if (!pdev) { - NL_SET_ERR_MSG_MOD(extack, "Port net device doesn't exist"); - return -ENODEV; - } - - p = br_port_get_rtnl(pdev); - if (!p) { - NL_SET_ERR_MSG_MOD(extack, "Net device is not a bridge port"); - return -EINVAL; - } - - if (p->br != br) { - NL_SET_ERR_MSG_MOD(extack, "Port belongs to a different bridge device"); - return -EINVAL; - } - if (p->state == BR_STATE_DISABLED && entry->state != MDB_PERMANENT) { + if (cfg.p->state == BR_STATE_DISABLED && entry->state != MDB_PERMANENT) { NL_SET_ERR_MSG_MOD(extack, "Port is in disabled state and entry is not permanent"); return -EINVAL; } - vg = nbp_vlan_group(p); + vg = nbp_vlan_group(cfg.p); } else { vg = br_vlan_group(br); } @@ -1150,12 +1123,12 @@ static int br_mdb_add(struct sk_buff *skb, struct nlmsghdr *nlh, if (br_vlan_enabled(br->dev) && vg && entry->vid == 0) { list_for_each_entry(v, &vg->vlan_list, vlist) { entry->vid = v->vid; - err = __br_mdb_add(net, br, p, entry, mdb_attrs, extack); + err = __br_mdb_add(net, br, cfg.p, entry, mdb_attrs, extack); if (err) break; } } else { - err = __br_mdb_add(net, br, p, entry, mdb_attrs, extack); + err = __br_mdb_add(net, br, cfg.p, entry, mdb_attrs, extack); } return err; @@ -1170,9 +1143,6 @@ static int __br_mdb_del(struct net_bridge *br, struct br_mdb_entry *entry, struct br_ip ip; int err = -EINVAL; - if (!netif_running(br->dev) || !br_opt_get(br, BROPT_MULTICAST_ENABLED)) - return -EINVAL; - __mdb_entry_to_br_ip(entry, &ip, mdb_attrs); spin_lock_bh(&br->multicast_lock); @@ -1212,11 +1182,10 @@ static int br_mdb_del(struct sk_buff *skb, struct nlmsghdr *nlh, struct nlattr *mdb_attrs[MDBE_ATTR_MAX + 1]; struct net *net = sock_net(skb->sk); struct net_bridge_vlan_group *vg; - struct net_bridge_port *p = NULL; - struct net_device *dev, *pdev; struct br_mdb_entry *entry; struct net_bridge_vlan *v; struct br_mdb_config cfg; + struct net_device *dev; struct net_bridge *br; int err; @@ -1230,24 +1199,10 @@ static int br_mdb_del(struct sk_buff *skb, struct nlmsghdr *nlh, br = netdev_priv(dev); - if (entry->ifindex != br->dev->ifindex) { - pdev = __dev_get_by_index(net, entry->ifindex); - if (!pdev) - return -ENODEV; - - p = br_port_get_rtnl(pdev); - if (!p) { - NL_SET_ERR_MSG_MOD(extack, "Net device is not a bridge port"); - return -EINVAL; - } - if (p->br != br) { - NL_SET_ERR_MSG_MOD(extack, "Port belongs to a different bridge device"); - return -EINVAL; - } - vg = nbp_vlan_group(p); - } else { + if (entry->ifindex != br->dev->ifindex) + vg = nbp_vlan_group(cfg.p); + else vg = br_vlan_group(br); - } /* If vlan filtering is enabled and VLAN is not specified * delete mdb entry on all vlans configured on the port. From f2b5aac68117909c3ac9e660c3182fd59524242f Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Tue, 6 Dec 2022 12:58:03 +0200 Subject: [PATCH 1669/2002] bridge: mcast: Use MDB configuration structure where possible The MDB configuration structure (i.e., struct br_mdb_config) now includes all the necessary information from the parsed RTM_{NEW,DEL}MDB netlink messages, so use it. This will later allow us to delete the calls to br_mdb_parse() from br_mdb_add() and br_mdb_del(). No functional changes intended. Signed-off-by: Ido Schimmel Acked-by: Nikolay Aleksandrov Signed-off-by: Jakub Kicinski --- net/bridge/br_mdb.c | 34 +++++++++++++++------------------- 1 file changed, 15 insertions(+), 19 deletions(-) diff --git a/net/bridge/br_mdb.c b/net/bridge/br_mdb.c index c8d78e4ec94e..080516a3ee9c 100644 --- a/net/bridge/br_mdb.c +++ b/net/bridge/br_mdb.c @@ -1094,7 +1094,6 @@ static int br_mdb_add(struct sk_buff *skb, struct nlmsghdr *nlh, struct net_bridge_vlan *v; struct br_mdb_config cfg; struct net_device *dev; - struct net_bridge *br; int err; err = br_mdb_config_init(net, nlh, &cfg, extack); @@ -1105,30 +1104,30 @@ static int br_mdb_add(struct sk_buff *skb, struct nlmsghdr *nlh, if (err < 0) return err; - br = netdev_priv(dev); - - if (entry->ifindex != br->dev->ifindex) { - if (cfg.p->state == BR_STATE_DISABLED && entry->state != MDB_PERMANENT) { + if (cfg.p) { + if (cfg.p->state == BR_STATE_DISABLED && cfg.entry->state != MDB_PERMANENT) { NL_SET_ERR_MSG_MOD(extack, "Port is in disabled state and entry is not permanent"); return -EINVAL; } vg = nbp_vlan_group(cfg.p); } else { - vg = br_vlan_group(br); + vg = br_vlan_group(cfg.br); } /* If vlan filtering is enabled and VLAN is not specified * install mdb entry on all vlans configured on the port. */ - if (br_vlan_enabled(br->dev) && vg && entry->vid == 0) { + if (br_vlan_enabled(cfg.br->dev) && vg && cfg.entry->vid == 0) { list_for_each_entry(v, &vg->vlan_list, vlist) { - entry->vid = v->vid; - err = __br_mdb_add(net, br, cfg.p, entry, mdb_attrs, extack); + cfg.entry->vid = v->vid; + err = __br_mdb_add(net, cfg.br, cfg.p, cfg.entry, + mdb_attrs, extack); if (err) break; } } else { - err = __br_mdb_add(net, br, cfg.p, entry, mdb_attrs, extack); + err = __br_mdb_add(net, cfg.br, cfg.p, cfg.entry, mdb_attrs, + extack); } return err; @@ -1186,7 +1185,6 @@ static int br_mdb_del(struct sk_buff *skb, struct nlmsghdr *nlh, struct net_bridge_vlan *v; struct br_mdb_config cfg; struct net_device *dev; - struct net_bridge *br; int err; err = br_mdb_config_init(net, nlh, &cfg, extack); @@ -1197,23 +1195,21 @@ static int br_mdb_del(struct sk_buff *skb, struct nlmsghdr *nlh, if (err < 0) return err; - br = netdev_priv(dev); - - if (entry->ifindex != br->dev->ifindex) + if (cfg.p) vg = nbp_vlan_group(cfg.p); else - vg = br_vlan_group(br); + vg = br_vlan_group(cfg.br); /* If vlan filtering is enabled and VLAN is not specified * delete mdb entry on all vlans configured on the port. */ - if (br_vlan_enabled(br->dev) && vg && entry->vid == 0) { + if (br_vlan_enabled(cfg.br->dev) && vg && cfg.entry->vid == 0) { list_for_each_entry(v, &vg->vlan_list, vlist) { - entry->vid = v->vid; - err = __br_mdb_del(br, entry, mdb_attrs); + cfg.entry->vid = v->vid; + err = __br_mdb_del(cfg.br, cfg.entry, mdb_attrs); } } else { - err = __br_mdb_del(br, entry, mdb_attrs); + err = __br_mdb_del(cfg.br, cfg.entry, mdb_attrs); } return err; From 8bd9c08e324175051d6d8ad5b2acfeaccaa1047b Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Tue, 6 Dec 2022 12:58:04 +0200 Subject: [PATCH 1670/2002] bridge: mcast: Propagate MDB configuration structure further As an intermediate step towards only using the new MDB configuration structure, pass it further in the control path instead of passing individual attributes. No functional changes intended. Signed-off-by: Ido Schimmel Acked-by: Nikolay Aleksandrov Signed-off-by: Jakub Kicinski --- net/bridge/br_mdb.c | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/net/bridge/br_mdb.c b/net/bridge/br_mdb.c index 080516a3ee9c..6017bff8316a 100644 --- a/net/bridge/br_mdb.c +++ b/net/bridge/br_mdb.c @@ -959,17 +959,15 @@ static int br_mdb_add_group(struct net_bridge *br, struct net_bridge_port *port, return 0; } -static int __br_mdb_add(struct net *net, struct net_bridge *br, - struct net_bridge_port *p, - struct br_mdb_entry *entry, +static int __br_mdb_add(const struct br_mdb_config *cfg, struct nlattr **mdb_attrs, struct netlink_ext_ack *extack) { int ret; - spin_lock_bh(&br->multicast_lock); - ret = br_mdb_add_group(br, p, entry, mdb_attrs, extack); - spin_unlock_bh(&br->multicast_lock); + spin_lock_bh(&cfg->br->multicast_lock); + ret = br_mdb_add_group(cfg->br, cfg->p, cfg->entry, mdb_attrs, extack); + spin_unlock_bh(&cfg->br->multicast_lock); return ret; } @@ -1120,22 +1118,22 @@ static int br_mdb_add(struct sk_buff *skb, struct nlmsghdr *nlh, if (br_vlan_enabled(cfg.br->dev) && vg && cfg.entry->vid == 0) { list_for_each_entry(v, &vg->vlan_list, vlist) { cfg.entry->vid = v->vid; - err = __br_mdb_add(net, cfg.br, cfg.p, cfg.entry, - mdb_attrs, extack); + err = __br_mdb_add(&cfg, mdb_attrs, extack); if (err) break; } } else { - err = __br_mdb_add(net, cfg.br, cfg.p, cfg.entry, mdb_attrs, - extack); + err = __br_mdb_add(&cfg, mdb_attrs, extack); } return err; } -static int __br_mdb_del(struct net_bridge *br, struct br_mdb_entry *entry, +static int __br_mdb_del(const struct br_mdb_config *cfg, struct nlattr **mdb_attrs) { + struct br_mdb_entry *entry = cfg->entry; + struct net_bridge *br = cfg->br; struct net_bridge_mdb_entry *mp; struct net_bridge_port_group *p; struct net_bridge_port_group __rcu **pp; @@ -1206,10 +1204,10 @@ static int br_mdb_del(struct sk_buff *skb, struct nlmsghdr *nlh, if (br_vlan_enabled(cfg.br->dev) && vg && cfg.entry->vid == 0) { list_for_each_entry(v, &vg->vlan_list, vlist) { cfg.entry->vid = v->vid; - err = __br_mdb_del(cfg.br, cfg.entry, mdb_attrs); + err = __br_mdb_del(&cfg, mdb_attrs); } } else { - err = __br_mdb_del(cfg.br, cfg.entry, mdb_attrs); + err = __br_mdb_del(&cfg, mdb_attrs); } return err; From 9f52a51429791737eaf35c6fb5273d178ec005d9 Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Tue, 6 Dec 2022 12:58:05 +0200 Subject: [PATCH 1671/2002] bridge: mcast: Use MDB group key from configuration structure The MDB group key (i.e., {source, destination, protocol, VID}) is currently determined under the multicast lock from the netlink attributes. Instead, use the group key from the MDB configuration structure that was prepared before acquiring the lock. No functional changes intended. Signed-off-by: Ido Schimmel Acked-by: Nikolay Aleksandrov Signed-off-by: Jakub Kicinski --- net/bridge/br_mdb.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/net/bridge/br_mdb.c b/net/bridge/br_mdb.c index 6017bff8316a..b459886af675 100644 --- a/net/bridge/br_mdb.c +++ b/net/bridge/br_mdb.c @@ -855,20 +855,19 @@ out: static int br_mdb_add_group(struct net_bridge *br, struct net_bridge_port *port, struct br_mdb_entry *entry, - struct nlattr **mdb_attrs, + const struct br_mdb_config *cfg, struct netlink_ext_ack *extack) { struct net_bridge_mdb_entry *mp, *star_mp; struct net_bridge_port_group __rcu **pp; struct net_bridge_port_group *p; struct net_bridge_mcast *brmctx; - struct br_ip group, star_group; + struct br_ip group = cfg->group; unsigned long now = jiffies; unsigned char flags = 0; + struct br_ip star_group; u8 filter_mode; - __mdb_entry_to_br_ip(entry, &group, mdb_attrs); - brmctx = __br_mdb_choose_context(br, entry, extack); if (!brmctx) return -EINVAL; @@ -966,7 +965,7 @@ static int __br_mdb_add(const struct br_mdb_config *cfg, int ret; spin_lock_bh(&cfg->br->multicast_lock); - ret = br_mdb_add_group(cfg->br, cfg->p, cfg->entry, mdb_attrs, extack); + ret = br_mdb_add_group(cfg->br, cfg->p, cfg->entry, cfg, extack); spin_unlock_bh(&cfg->br->multicast_lock); return ret; @@ -1118,6 +1117,7 @@ static int br_mdb_add(struct sk_buff *skb, struct nlmsghdr *nlh, if (br_vlan_enabled(cfg.br->dev) && vg && cfg.entry->vid == 0) { list_for_each_entry(v, &vg->vlan_list, vlist) { cfg.entry->vid = v->vid; + cfg.group.vid = v->vid; err = __br_mdb_add(&cfg, mdb_attrs, extack); if (err) break; @@ -1137,11 +1137,9 @@ static int __br_mdb_del(const struct br_mdb_config *cfg, struct net_bridge_mdb_entry *mp; struct net_bridge_port_group *p; struct net_bridge_port_group __rcu **pp; - struct br_ip ip; + struct br_ip ip = cfg->group; int err = -EINVAL; - __mdb_entry_to_br_ip(entry, &ip, mdb_attrs); - spin_lock_bh(&br->multicast_lock); mp = br_mdb_ip_get(br, &ip); if (!mp) @@ -1204,6 +1202,7 @@ static int br_mdb_del(struct sk_buff *skb, struct nlmsghdr *nlh, if (br_vlan_enabled(cfg.br->dev) && vg && cfg.entry->vid == 0) { list_for_each_entry(v, &vg->vlan_list, vlist) { cfg.entry->vid = v->vid; + cfg.group.vid = v->vid; err = __br_mdb_del(&cfg, mdb_attrs); } } else { From 3ee5662345f26c3d694e25f3e3b577deaaf31c0f Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Tue, 6 Dec 2022 12:58:06 +0200 Subject: [PATCH 1672/2002] bridge: mcast: Remove br_mdb_parse() The parsing of the netlink messages and the validity checks are now performed in br_mdb_config_init() so we can remove br_mdb_parse(). This finally allows us to stop passing netlink attributes deep in the MDB control path and only use the MDB configuration structure. Signed-off-by: Ido Schimmel Acked-by: Nikolay Aleksandrov Signed-off-by: Jakub Kicinski --- net/bridge/br_mdb.c | 93 +++------------------------------------------ 1 file changed, 5 insertions(+), 88 deletions(-) diff --git a/net/bridge/br_mdb.c b/net/bridge/br_mdb.c index b459886af675..d0e018628f5d 100644 --- a/net/bridge/br_mdb.c +++ b/net/bridge/br_mdb.c @@ -754,73 +754,6 @@ static const struct nla_policy br_mdbe_attrs_pol[MDBE_ATTR_MAX + 1] = { sizeof(struct in6_addr)), }; -static int br_mdb_parse(struct sk_buff *skb, struct nlmsghdr *nlh, - struct net_device **pdev, struct br_mdb_entry **pentry, - struct nlattr **mdb_attrs, struct netlink_ext_ack *extack) -{ - struct net *net = sock_net(skb->sk); - struct br_mdb_entry *entry; - struct br_port_msg *bpm; - struct nlattr *tb[MDBA_SET_ENTRY_MAX+1]; - struct net_device *dev; - int err; - - err = nlmsg_parse_deprecated(nlh, sizeof(*bpm), tb, - MDBA_SET_ENTRY_MAX, NULL, NULL); - if (err < 0) - return err; - - bpm = nlmsg_data(nlh); - if (bpm->ifindex == 0) { - NL_SET_ERR_MSG_MOD(extack, "Invalid bridge ifindex"); - return -EINVAL; - } - - dev = __dev_get_by_index(net, bpm->ifindex); - if (dev == NULL) { - NL_SET_ERR_MSG_MOD(extack, "Bridge device doesn't exist"); - return -ENODEV; - } - - if (!netif_is_bridge_master(dev)) { - NL_SET_ERR_MSG_MOD(extack, "Device is not a bridge"); - return -EOPNOTSUPP; - } - - *pdev = dev; - - if (!tb[MDBA_SET_ENTRY]) { - NL_SET_ERR_MSG_MOD(extack, "Missing MDBA_SET_ENTRY attribute"); - return -EINVAL; - } - if (nla_len(tb[MDBA_SET_ENTRY]) != sizeof(struct br_mdb_entry)) { - NL_SET_ERR_MSG_MOD(extack, "Invalid MDBA_SET_ENTRY attribute length"); - return -EINVAL; - } - - entry = nla_data(tb[MDBA_SET_ENTRY]); - if (!is_valid_mdb_entry(entry, extack)) - return -EINVAL; - *pentry = entry; - - if (tb[MDBA_SET_ENTRY_ATTRS]) { - err = nla_parse_nested(mdb_attrs, MDBE_ATTR_MAX, - tb[MDBA_SET_ENTRY_ATTRS], - br_mdbe_attrs_pol, extack); - if (err) - return err; - if (mdb_attrs[MDBE_ATTR_SOURCE] && - !is_valid_mdb_source(mdb_attrs[MDBE_ATTR_SOURCE], - entry->addr.proto, extack)) - return -EINVAL; - } else { - memset(mdb_attrs, 0, - sizeof(struct nlattr *) * (MDBE_ATTR_MAX + 1)); - } - - return 0; -} - static struct net_bridge_mcast * __br_mdb_choose_context(struct net_bridge *br, const struct br_mdb_entry *entry, @@ -959,7 +892,6 @@ static int br_mdb_add_group(struct net_bridge *br, struct net_bridge_port *port, } static int __br_mdb_add(const struct br_mdb_config *cfg, - struct nlattr **mdb_attrs, struct netlink_ext_ack *extack) { int ret; @@ -1084,23 +1016,16 @@ static int br_mdb_config_init(struct net *net, const struct nlmsghdr *nlh, static int br_mdb_add(struct sk_buff *skb, struct nlmsghdr *nlh, struct netlink_ext_ack *extack) { - struct nlattr *mdb_attrs[MDBE_ATTR_MAX + 1]; struct net *net = sock_net(skb->sk); struct net_bridge_vlan_group *vg; - struct br_mdb_entry *entry; struct net_bridge_vlan *v; struct br_mdb_config cfg; - struct net_device *dev; int err; err = br_mdb_config_init(net, nlh, &cfg, extack); if (err) return err; - err = br_mdb_parse(skb, nlh, &dev, &entry, mdb_attrs, extack); - if (err < 0) - return err; - if (cfg.p) { if (cfg.p->state == BR_STATE_DISABLED && cfg.entry->state != MDB_PERMANENT) { NL_SET_ERR_MSG_MOD(extack, "Port is in disabled state and entry is not permanent"); @@ -1118,19 +1043,18 @@ static int br_mdb_add(struct sk_buff *skb, struct nlmsghdr *nlh, list_for_each_entry(v, &vg->vlan_list, vlist) { cfg.entry->vid = v->vid; cfg.group.vid = v->vid; - err = __br_mdb_add(&cfg, mdb_attrs, extack); + err = __br_mdb_add(&cfg, extack); if (err) break; } } else { - err = __br_mdb_add(&cfg, mdb_attrs, extack); + err = __br_mdb_add(&cfg, extack); } return err; } -static int __br_mdb_del(const struct br_mdb_config *cfg, - struct nlattr **mdb_attrs) +static int __br_mdb_del(const struct br_mdb_config *cfg) { struct br_mdb_entry *entry = cfg->entry; struct net_bridge *br = cfg->br; @@ -1174,23 +1098,16 @@ unlock: static int br_mdb_del(struct sk_buff *skb, struct nlmsghdr *nlh, struct netlink_ext_ack *extack) { - struct nlattr *mdb_attrs[MDBE_ATTR_MAX + 1]; struct net *net = sock_net(skb->sk); struct net_bridge_vlan_group *vg; - struct br_mdb_entry *entry; struct net_bridge_vlan *v; struct br_mdb_config cfg; - struct net_device *dev; int err; err = br_mdb_config_init(net, nlh, &cfg, extack); if (err) return err; - err = br_mdb_parse(skb, nlh, &dev, &entry, mdb_attrs, extack); - if (err < 0) - return err; - if (cfg.p) vg = nbp_vlan_group(cfg.p); else @@ -1203,10 +1120,10 @@ static int br_mdb_del(struct sk_buff *skb, struct nlmsghdr *nlh, list_for_each_entry(v, &vg->vlan_list, vlist) { cfg.entry->vid = v->vid; cfg.group.vid = v->vid; - err = __br_mdb_del(&cfg, mdb_attrs); + err = __br_mdb_del(&cfg); } } else { - err = __br_mdb_del(&cfg, mdb_attrs); + err = __br_mdb_del(&cfg); } return err; From 4c1ebc6c1f2196c11cff78832c2604bbcec7d088 Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Tue, 6 Dec 2022 12:58:07 +0200 Subject: [PATCH 1673/2002] bridge: mcast: Move checks out of critical section The checks only require information parsed from the RTM_NEWMDB netlink message and do not rely on any state stored in the bridge driver. Therefore, there is no need to perform the checks in the critical section under the multicast lock. Move the checks out of the critical section. Signed-off-by: Ido Schimmel Acked-by: Nikolay Aleksandrov Signed-off-by: Jakub Kicinski --- net/bridge/br_mdb.c | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/net/bridge/br_mdb.c b/net/bridge/br_mdb.c index d0e018628f5d..d954d8f7cb0a 100644 --- a/net/bridge/br_mdb.c +++ b/net/bridge/br_mdb.c @@ -805,24 +805,6 @@ static int br_mdb_add_group(struct net_bridge *br, struct net_bridge_port *port, if (!brmctx) return -EINVAL; - /* host join errors which can happen before creating the group */ - if (!port && !br_group_is_l2(&group)) { - /* don't allow any flags for host-joined IP groups */ - if (entry->state) { - NL_SET_ERR_MSG_MOD(extack, "Flags are not allowed for host groups"); - return -EINVAL; - } - if (!br_multicast_is_star_g(&group)) { - NL_SET_ERR_MSG_MOD(extack, "Groups with sources cannot be manually host joined"); - return -EINVAL; - } - } - - if (br_group_is_l2(&group) && entry->state != MDB_PERMANENT) { - NL_SET_ERR_MSG_MOD(extack, "Only permanent L2 entries allowed"); - return -EINVAL; - } - mp = br_multicast_new_group(br, &group); if (IS_ERR(mp)) return PTR_ERR(mp); @@ -1026,6 +1008,24 @@ static int br_mdb_add(struct sk_buff *skb, struct nlmsghdr *nlh, if (err) return err; + /* host join errors which can happen before creating the group */ + if (!cfg.p && !br_group_is_l2(&cfg.group)) { + /* don't allow any flags for host-joined IP groups */ + if (cfg.entry->state) { + NL_SET_ERR_MSG_MOD(extack, "Flags are not allowed for host groups"); + return -EINVAL; + } + if (!br_multicast_is_star_g(&cfg.group)) { + NL_SET_ERR_MSG_MOD(extack, "Groups with sources cannot be manually host joined"); + return -EINVAL; + } + } + + if (br_group_is_l2(&cfg.group) && cfg.entry->state != MDB_PERMANENT) { + NL_SET_ERR_MSG_MOD(extack, "Only permanent L2 entries allowed"); + return -EINVAL; + } + if (cfg.p) { if (cfg.p->state == BR_STATE_DISABLED && cfg.entry->state != MDB_PERMANENT) { NL_SET_ERR_MSG_MOD(extack, "Port is in disabled state and entry is not permanent"); From 090149eaf3911ce588775107b25c725d287752ef Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Tue, 6 Dec 2022 12:58:08 +0200 Subject: [PATCH 1674/2002] bridge: mcast: Remove redundant function arguments Drop the first three arguments and instead extract them from the MDB configuration structure. Signed-off-by: Ido Schimmel Acked-by: Nikolay Aleksandrov Signed-off-by: Jakub Kicinski --- net/bridge/br_mdb.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/net/bridge/br_mdb.c b/net/bridge/br_mdb.c index d954d8f7cb0a..ae7d93c08880 100644 --- a/net/bridge/br_mdb.c +++ b/net/bridge/br_mdb.c @@ -786,13 +786,14 @@ out: return brmctx; } -static int br_mdb_add_group(struct net_bridge *br, struct net_bridge_port *port, - struct br_mdb_entry *entry, - const struct br_mdb_config *cfg, +static int br_mdb_add_group(const struct br_mdb_config *cfg, struct netlink_ext_ack *extack) { struct net_bridge_mdb_entry *mp, *star_mp; struct net_bridge_port_group __rcu **pp; + struct br_mdb_entry *entry = cfg->entry; + struct net_bridge_port *port = cfg->p; + struct net_bridge *br = cfg->br; struct net_bridge_port_group *p; struct net_bridge_mcast *brmctx; struct br_ip group = cfg->group; @@ -879,7 +880,7 @@ static int __br_mdb_add(const struct br_mdb_config *cfg, int ret; spin_lock_bh(&cfg->br->multicast_lock); - ret = br_mdb_add_group(cfg->br, cfg->p, cfg->entry, cfg, extack); + ret = br_mdb_add_group(cfg, extack); spin_unlock_bh(&cfg->br->multicast_lock); return ret; From f86c3e2c1b5ea5c959ef176541c2f831231fa631 Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Tue, 6 Dec 2022 12:58:09 +0200 Subject: [PATCH 1675/2002] bridge: mcast: Constify 'group' argument in br_multicast_new_port_group() The 'group' argument is not modified, so mark it as 'const'. It will allow us to constify arguments of the callers of this function in future patches. Signed-off-by: Ido Schimmel Acked-by: Nikolay Aleksandrov Signed-off-by: Jakub Kicinski --- net/bridge/br_multicast.c | 2 +- net/bridge/br_private.h | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/net/bridge/br_multicast.c b/net/bridge/br_multicast.c index 5e988f0ed2c0..db4c3900ae95 100644 --- a/net/bridge/br_multicast.c +++ b/net/bridge/br_multicast.c @@ -1273,7 +1273,7 @@ br_multicast_new_group_src(struct net_bridge_port_group *pg, struct br_ip *src_i struct net_bridge_port_group *br_multicast_new_port_group( struct net_bridge_port *port, - struct br_ip *group, + const struct br_ip *group, struct net_bridge_port_group __rcu *next, unsigned char flags, const unsigned char *src, diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h index 0a09f10966dc..3997e16c15fc 100644 --- a/net/bridge/br_private.h +++ b/net/bridge/br_private.h @@ -941,7 +941,8 @@ br_mdb_ip_get(struct net_bridge *br, struct br_ip *dst); struct net_bridge_mdb_entry * br_multicast_new_group(struct net_bridge *br, struct br_ip *group); struct net_bridge_port_group * -br_multicast_new_port_group(struct net_bridge_port *port, struct br_ip *group, +br_multicast_new_port_group(struct net_bridge_port *port, + const struct br_ip *group, struct net_bridge_port_group __rcu *next, unsigned char flags, const unsigned char *src, u8 filter_mode, u8 rt_protocol); From df268f6ca7da1088d7ecff4250d1478d3fa2406b Mon Sep 17 00:00:00 2001 From: Yishai Hadas Date: Tue, 6 Dec 2022 20:51:12 +0200 Subject: [PATCH 1676/2002] net/mlx5: Introduce IFC bits for migratable Introduce IFC related capabilities to enable setting VF to be able to perform live migration. e.g.: to be migratable. Signed-off-by: Yishai Hadas Reviewed-by: Mark Bloch Acked-by: Saeed Mahameed Signed-off-by: Jakub Kicinski --- include/linux/mlx5/mlx5_ifc.h | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/include/linux/mlx5/mlx5_ifc.h b/include/linux/mlx5/mlx5_ifc.h index 5a4e914e2a6f..2093131483c7 100644 --- a/include/linux/mlx5/mlx5_ifc.h +++ b/include/linux/mlx5/mlx5_ifc.h @@ -68,6 +68,7 @@ enum { MLX5_SET_HCA_CAP_OP_MOD_ODP = 0x2, MLX5_SET_HCA_CAP_OP_MOD_ATOMIC = 0x3, MLX5_SET_HCA_CAP_OP_MOD_ROCE = 0x4, + MLX5_SET_HCA_CAP_OP_MOD_GENERAL_DEVICE2 = 0x20, MLX5_SET_HCA_CAP_OP_MODE_PORT_SELECTION = 0x25, }; @@ -1875,7 +1876,10 @@ struct mlx5_ifc_cmd_hca_cap_bits { }; struct mlx5_ifc_cmd_hca_cap_2_bits { - u8 reserved_at_0[0xa0]; + u8 reserved_at_0[0x80]; + + u8 migratable[0x1]; + u8 reserved_at_81[0x1f]; u8 max_reformat_insert_size[0x8]; u8 max_reformat_insert_offset[0x8]; From c0bea69d1ca7974a6a387dbc8d9ca15345e2779f Mon Sep 17 00:00:00 2001 From: Shay Drory Date: Tue, 6 Dec 2022 20:51:13 +0200 Subject: [PATCH 1677/2002] devlink: Validate port function request In order to avoid partial request processing, validate the request before processing it. Signed-off-by: Shay Drory Reviewed-by: Jiri Pirko Signed-off-by: Jakub Kicinski --- net/core/devlink.c | 32 +++++++++++++++++++++++--------- 1 file changed, 23 insertions(+), 9 deletions(-) diff --git a/net/core/devlink.c b/net/core/devlink.c index 907df7124157..035249c5dd17 100644 --- a/net/core/devlink.c +++ b/net/core/devlink.c @@ -1632,11 +1632,6 @@ static int devlink_port_function_hw_addr_set(struct devlink_port *port, } } - if (!ops->port_function_hw_addr_set) { - NL_SET_ERR_MSG_MOD(extack, "Port doesn't support function attributes"); - return -EOPNOTSUPP; - } - return ops->port_function_hw_addr_set(port, hw_addr, hw_addr_len, extack); } @@ -1650,12 +1645,27 @@ static int devlink_port_fn_state_set(struct devlink_port *port, state = nla_get_u8(attr); ops = port->devlink->ops; - if (!ops->port_fn_state_set) { - NL_SET_ERR_MSG_MOD(extack, - "Function does not support state setting"); + return ops->port_fn_state_set(port, state, extack); +} + +static int devlink_port_function_validate(struct devlink_port *devlink_port, + struct nlattr **tb, + struct netlink_ext_ack *extack) +{ + const struct devlink_ops *ops = devlink_port->devlink->ops; + + if (tb[DEVLINK_PORT_FUNCTION_ATTR_HW_ADDR] && + !ops->port_function_hw_addr_set) { + NL_SET_ERR_MSG_ATTR(extack, tb[DEVLINK_PORT_FUNCTION_ATTR_HW_ADDR], + "Port doesn't support function attributes"); return -EOPNOTSUPP; } - return ops->port_fn_state_set(port, state, extack); + if (tb[DEVLINK_PORT_FN_ATTR_STATE] && !ops->port_fn_state_set) { + NL_SET_ERR_MSG_ATTR(extack, tb[DEVLINK_PORT_FUNCTION_ATTR_HW_ADDR], + "Function does not support state setting"); + return -EOPNOTSUPP; + } + return 0; } static int devlink_port_function_set(struct devlink_port *port, @@ -1672,6 +1682,10 @@ static int devlink_port_function_set(struct devlink_port *port, return err; } + err = devlink_port_function_validate(port, tb, extack); + if (err) + return err; + attr = tb[DEVLINK_PORT_FUNCTION_ATTR_HW_ADDR]; if (attr) { err = devlink_port_function_hw_addr_set(port, attr, extack); From 875cd5eeba96091d97ce27e6e9b855fba9d2e0fc Mon Sep 17 00:00:00 2001 From: Shay Drory Date: Tue, 6 Dec 2022 20:51:14 +0200 Subject: [PATCH 1678/2002] devlink: Move devlink port function hw_addr attr documentation devlink port function hw_addr attr documentation is in mlx5 specific file while there is nothing mlx5 specific about it. Move it to devlink-port.rst. Signed-off-by: Shay Drory Reviewed-by: Jiri Pirko Signed-off-by: Jakub Kicinski --- .../device_drivers/ethernet/mellanox/mlx5.rst | 38 +---------------- .../networking/devlink/devlink-port.rst | 42 ++++++++++++++++++- 2 files changed, 43 insertions(+), 37 deletions(-) diff --git a/Documentation/networking/device_drivers/ethernet/mellanox/mlx5.rst b/Documentation/networking/device_drivers/ethernet/mellanox/mlx5.rst index e8fa7ac9e6b1..07cfc1b07db3 100644 --- a/Documentation/networking/device_drivers/ethernet/mellanox/mlx5.rst +++ b/Documentation/networking/device_drivers/ethernet/mellanox/mlx5.rst @@ -351,42 +351,8 @@ driver. MAC address setup ----------------- -mlx5 driver provides mechanism to setup the MAC address of the PCI VF/SF. - -The configured MAC address of the PCI VF/SF will be used by netdevice and rdma -device created for the PCI VF/SF. - -- Get the MAC address of the VF identified by its unique devlink port index:: - - $ devlink port show pci/0000:06:00.0/2 - pci/0000:06:00.0/2: type eth netdev enp6s0pf0vf1 flavour pcivf pfnum 0 vfnum 1 - function: - hw_addr 00:00:00:00:00:00 - -- Set the MAC address of the VF identified by its unique devlink port index:: - - $ devlink port function set pci/0000:06:00.0/2 hw_addr 00:11:22:33:44:55 - - $ devlink port show pci/0000:06:00.0/2 - pci/0000:06:00.0/2: type eth netdev enp6s0pf0vf1 flavour pcivf pfnum 0 vfnum 1 - function: - hw_addr 00:11:22:33:44:55 - -- Get the MAC address of the SF identified by its unique devlink port index:: - - $ devlink port show pci/0000:06:00.0/32768 - pci/0000:06:00.0/32768: type eth netdev enp6s0pf0sf88 flavour pcisf pfnum 0 sfnum 88 - function: - hw_addr 00:00:00:00:00:00 - -- Set the MAC address of the SF identified by its unique devlink port index:: - - $ devlink port function set pci/0000:06:00.0/32768 hw_addr 00:00:00:00:88:88 - - $ devlink port show pci/0000:06:00.0/32768 - pci/0000:06:00.0/32768: type eth netdev enp6s0pf0sf88 flavour pcisf pfnum 0 sfnum 88 - function: - hw_addr 00:00:00:00:88:88 +mlx5 driver support devlink port function attr mechanism to setup MAC +address. (refer to Documentation/networking/devlink/devlink-port.rst) SF state setup -------------- diff --git a/Documentation/networking/devlink/devlink-port.rst b/Documentation/networking/devlink/devlink-port.rst index 98557c2ab1c1..2c637f4aae8e 100644 --- a/Documentation/networking/devlink/devlink-port.rst +++ b/Documentation/networking/devlink/devlink-port.rst @@ -119,9 +119,49 @@ function device to the driver. For subfunctions, this means user should configure port function attribute before activating the port function. A user may set the hardware address of the function using -'devlink port function set hw_addr' command. For Ethernet port function +`devlink port function set hw_addr` command. For Ethernet port function this means a MAC address. +Function attributes +=================== + +MAC address setup +----------------- +The configured MAC address of the PCI VF/SF will be used by netdevice and rdma +device created for the PCI VF/SF. + +- Get the MAC address of the VF identified by its unique devlink port index:: + + $ devlink port show pci/0000:06:00.0/2 + pci/0000:06:00.0/2: type eth netdev enp6s0pf0vf1 flavour pcivf pfnum 0 vfnum 1 + function: + hw_addr 00:00:00:00:00:00 + +- Set the MAC address of the VF identified by its unique devlink port index:: + + $ devlink port function set pci/0000:06:00.0/2 hw_addr 00:11:22:33:44:55 + + $ devlink port show pci/0000:06:00.0/2 + pci/0000:06:00.0/2: type eth netdev enp6s0pf0vf1 flavour pcivf pfnum 0 vfnum 1 + function: + hw_addr 00:11:22:33:44:55 + +- Get the MAC address of the SF identified by its unique devlink port index:: + + $ devlink port show pci/0000:06:00.0/32768 + pci/0000:06:00.0/32768: type eth netdev enp6s0pf0sf88 flavour pcisf pfnum 0 sfnum 88 + function: + hw_addr 00:00:00:00:00:00 + +- Set the MAC address of the SF identified by its unique devlink port index:: + + $ devlink port function set pci/0000:06:00.0/32768 hw_addr 00:00:00:00:88:88 + + $ devlink port show pci/0000:06:00.0/32768 + pci/0000:06:00.0/32768: type eth netdev enp6s0pf0sf88 flavour pcisf pfnum 0 sfnum 88 + function: + hw_addr 00:00:00:00:88:88 + Subfunction ============ From da65e9ff3bf614d2836e38e1d405c7073e6ba3b7 Mon Sep 17 00:00:00 2001 From: Shay Drory Date: Tue, 6 Dec 2022 20:51:15 +0200 Subject: [PATCH 1679/2002] devlink: Expose port function commands to control RoCE Expose port function commands to enable / disable RoCE, this is used to control the port RoCE device capabilities. When RoCE is disabled for a function of the port, function cannot create any RoCE specific resources (e.g GID table). It also saves system memory utilization. For example disabling RoCE enable a VF/SF saves 1 Mbytes of system memory per function. Example of a PCI VF port which supports function configuration: Set RoCE of the VF's port function. $ devlink port show pci/0000:06:00.0/2 pci/0000:06:00.0/2: type eth netdev enp6s0pf0vf1 flavour pcivf pfnum 0 vfnum 1 function: hw_addr 00:00:00:00:00:00 roce enable $ devlink port function set pci/0000:06:00.0/2 roce disable $ devlink port show pci/0000:06:00.0/2 pci/0000:06:00.0/2: type eth netdev enp6s0pf0vf1 flavour pcivf pfnum 0 vfnum 1 function: hw_addr 00:00:00:00:00:00 roce disable Signed-off-by: Shay Drory Reviewed-by: Jiri Pirko Signed-off-by: Jakub Kicinski --- .../networking/devlink/devlink-port.rst | 34 +++++- include/net/devlink.h | 18 +++ include/uapi/linux/devlink.h | 10 ++ net/core/devlink.c | 113 ++++++++++++++++++ 4 files changed, 174 insertions(+), 1 deletion(-) diff --git a/Documentation/networking/devlink/devlink-port.rst b/Documentation/networking/devlink/devlink-port.rst index 2c637f4aae8e..c3302d23e480 100644 --- a/Documentation/networking/devlink/devlink-port.rst +++ b/Documentation/networking/devlink/devlink-port.rst @@ -110,7 +110,7 @@ devlink ports for both the controllers. Function configuration ====================== -A user can configure the function attribute before enumerating the PCI +Users can configure one or more function attributes before enumerating the PCI function. Usually it means, user should configure function attribute before a bus specific device for the function is created. However, when SRIOV is enabled, virtual function devices are created on the PCI bus. @@ -122,6 +122,9 @@ A user may set the hardware address of the function using `devlink port function set hw_addr` command. For Ethernet port function this means a MAC address. +Users may also set the RoCE capability of the function using +`devlink port function set roce` command. + Function attributes =================== @@ -162,6 +165,35 @@ device created for the PCI VF/SF. function: hw_addr 00:00:00:00:88:88 +RoCE capability setup +--------------------- +Not all PCI VFs/SFs require RoCE capability. + +When RoCE capability is disabled, it saves system memory per PCI VF/SF. + +When user disables RoCE capability for a VF/SF, user application cannot send or +receive any RoCE packets through this VF/SF and RoCE GID table for this PCI +will be empty. + +When RoCE capability is disabled in the device using port function attribute, +VF/SF driver cannot override it. + +- Get RoCE capability of the VF device:: + + $ devlink port show pci/0000:06:00.0/2 + pci/0000:06:00.0/2: type eth netdev enp6s0pf0vf1 flavour pcivf pfnum 0 vfnum 1 + function: + hw_addr 00:00:00:00:00:00 roce enable + +- Set RoCE capability of the VF device:: + + $ devlink port function set pci/0000:06:00.0/2 roce disable + + $ devlink port show pci/0000:06:00.0/2 + pci/0000:06:00.0/2: type eth netdev enp6s0pf0vf1 flavour pcivf pfnum 0 vfnum 1 + function: + hw_addr 00:00:00:00:00:00 roce disable + Subfunction ============ diff --git a/include/net/devlink.h b/include/net/devlink.h index 5f6eca5e4a40..ce4c65d2f2e7 100644 --- a/include/net/devlink.h +++ b/include/net/devlink.h @@ -1451,6 +1451,24 @@ struct devlink_ops { int (*port_function_hw_addr_set)(struct devlink_port *port, const u8 *hw_addr, int hw_addr_len, struct netlink_ext_ack *extack); + /** + * @port_fn_roce_get: Port function's roce get function. + * + * Query RoCE state of a function managed by the devlink port. + * Return -EOPNOTSUPP if port function RoCE handling is not supported. + */ + int (*port_fn_roce_get)(struct devlink_port *devlink_port, + bool *is_enable, + struct netlink_ext_ack *extack); + /** + * @port_fn_roce_set: Port function's roce set function. + * + * Enable/Disable the RoCE state of a function managed by the devlink + * port. + * Return -EOPNOTSUPP if port function RoCE handling is not supported. + */ + int (*port_fn_roce_set)(struct devlink_port *devlink_port, + bool enable, struct netlink_ext_ack *extack); /** * port_new() - Add a new port function of a specified flavor * @devlink: Devlink instance diff --git a/include/uapi/linux/devlink.h b/include/uapi/linux/devlink.h index 70191d96af89..6cc2925bd478 100644 --- a/include/uapi/linux/devlink.h +++ b/include/uapi/linux/devlink.h @@ -658,11 +658,21 @@ enum devlink_resource_unit { DEVLINK_RESOURCE_UNIT_ENTRY, }; +enum devlink_port_fn_attr_cap { + DEVLINK_PORT_FN_ATTR_CAP_ROCE_BIT, + + /* Add new caps above */ + __DEVLINK_PORT_FN_ATTR_CAPS_MAX, +}; + +#define DEVLINK_PORT_FN_CAP_ROCE _BITUL(DEVLINK_PORT_FN_ATTR_CAP_ROCE_BIT) + enum devlink_port_function_attr { DEVLINK_PORT_FUNCTION_ATTR_UNSPEC, DEVLINK_PORT_FUNCTION_ATTR_HW_ADDR, /* binary */ DEVLINK_PORT_FN_ATTR_STATE, /* u8 */ DEVLINK_PORT_FN_ATTR_OPSTATE, /* u8 */ + DEVLINK_PORT_FN_ATTR_CAPS, /* bitfield32 */ __DEVLINK_PORT_FUNCTION_ATTR_MAX, DEVLINK_PORT_FUNCTION_ATTR_MAX = __DEVLINK_PORT_FUNCTION_ATTR_MAX - 1 diff --git a/net/core/devlink.c b/net/core/devlink.c index 035249c5dd17..8c0ad52431c5 100644 --- a/net/core/devlink.c +++ b/net/core/devlink.c @@ -195,11 +195,16 @@ EXPORT_TRACEPOINT_SYMBOL_GPL(devlink_hwmsg); EXPORT_TRACEPOINT_SYMBOL_GPL(devlink_hwerr); EXPORT_TRACEPOINT_SYMBOL_GPL(devlink_trap_report); +#define DEVLINK_PORT_FN_CAPS_VALID_MASK \ + (_BITUL(__DEVLINK_PORT_FN_ATTR_CAPS_MAX) - 1) + static const struct nla_policy devlink_function_nl_policy[DEVLINK_PORT_FUNCTION_ATTR_MAX + 1] = { [DEVLINK_PORT_FUNCTION_ATTR_HW_ADDR] = { .type = NLA_BINARY }, [DEVLINK_PORT_FN_ATTR_STATE] = NLA_POLICY_RANGE(NLA_U8, DEVLINK_PORT_FN_STATE_INACTIVE, DEVLINK_PORT_FN_STATE_ACTIVE), + [DEVLINK_PORT_FN_ATTR_CAPS] = + NLA_POLICY_BITFIELD32(DEVLINK_PORT_FN_CAPS_VALID_MASK), }; static const struct nla_policy devlink_selftest_nl_policy[DEVLINK_ATTR_SELFTEST_ID_MAX + 1] = { @@ -680,6 +685,60 @@ devlink_sb_tc_index_get_from_attrs(struct devlink_sb *devlink_sb, return 0; } +static void devlink_port_fn_cap_fill(struct nla_bitfield32 *caps, + u32 cap, bool is_enable) +{ + caps->selector |= cap; + if (is_enable) + caps->value |= cap; +} + +static int devlink_port_fn_roce_fill(const struct devlink_ops *ops, + struct devlink_port *devlink_port, + struct nla_bitfield32 *caps, + struct netlink_ext_ack *extack) +{ + bool is_enable; + int err; + + if (!ops->port_fn_roce_get) + return 0; + + err = ops->port_fn_roce_get(devlink_port, &is_enable, extack); + if (err) { + if (err == -EOPNOTSUPP) + return 0; + return err; + } + + devlink_port_fn_cap_fill(caps, DEVLINK_PORT_FN_CAP_ROCE, is_enable); + return 0; +} + +static int devlink_port_fn_caps_fill(const struct devlink_ops *ops, + struct devlink_port *devlink_port, + struct sk_buff *msg, + struct netlink_ext_ack *extack, + bool *msg_updated) +{ + struct nla_bitfield32 caps = {}; + int err; + + err = devlink_port_fn_roce_fill(ops, devlink_port, &caps, extack); + if (err) + return err; + + if (!caps.selector) + return 0; + err = nla_put_bitfield32(msg, DEVLINK_PORT_FN_ATTR_CAPS, caps.value, + caps.selector); + if (err) + return err; + + *msg_updated = true; + return 0; +} + static int devlink_sb_tc_index_get_from_info(struct devlink_sb *devlink_sb, struct genl_info *info, @@ -1263,6 +1322,35 @@ static int devlink_port_fn_state_fill(const struct devlink_ops *ops, return 0; } +static int +devlink_port_fn_roce_set(struct devlink_port *devlink_port, bool enable, + struct netlink_ext_ack *extack) +{ + const struct devlink_ops *ops = devlink_port->devlink->ops; + + return ops->port_fn_roce_set(devlink_port, enable, extack); +} + +static int devlink_port_fn_caps_set(struct devlink_port *devlink_port, + const struct nlattr *attr, + struct netlink_ext_ack *extack) +{ + struct nla_bitfield32 caps; + u32 caps_value; + int err; + + caps = nla_get_bitfield32(attr); + caps_value = caps.value & caps.selector; + if (caps.selector & DEVLINK_PORT_FN_CAP_ROCE) { + err = devlink_port_fn_roce_set(devlink_port, + caps_value & DEVLINK_PORT_FN_CAP_ROCE, + extack); + if (err) + return err; + } + return 0; +} + static int devlink_nl_port_function_attrs_put(struct sk_buff *msg, struct devlink_port *port, struct netlink_ext_ack *extack) @@ -1281,6 +1369,10 @@ devlink_nl_port_function_attrs_put(struct sk_buff *msg, struct devlink_port *por &msg_updated); if (err) goto out; + err = devlink_port_fn_caps_fill(ops, port, msg, extack, + &msg_updated); + if (err) + goto out; err = devlink_port_fn_state_fill(ops, port, msg, extack, &msg_updated); out: if (err || !msg_updated) @@ -1653,6 +1745,7 @@ static int devlink_port_function_validate(struct devlink_port *devlink_port, struct netlink_ext_ack *extack) { const struct devlink_ops *ops = devlink_port->devlink->ops; + struct nlattr *attr; if (tb[DEVLINK_PORT_FUNCTION_ATTR_HW_ADDR] && !ops->port_function_hw_addr_set) { @@ -1665,6 +1758,18 @@ static int devlink_port_function_validate(struct devlink_port *devlink_port, "Function does not support state setting"); return -EOPNOTSUPP; } + attr = tb[DEVLINK_PORT_FN_ATTR_CAPS]; + if (attr) { + struct nla_bitfield32 caps; + + caps = nla_get_bitfield32(attr); + if (caps.selector & DEVLINK_PORT_FN_CAP_ROCE && + !ops->port_fn_roce_set) { + NL_SET_ERR_MSG_ATTR(extack, attr, + "Port doesn't support RoCE function attribute"); + return -EOPNOTSUPP; + } + } return 0; } @@ -1692,6 +1797,14 @@ static int devlink_port_function_set(struct devlink_port *port, if (err) return err; } + + attr = tb[DEVLINK_PORT_FN_ATTR_CAPS]; + if (attr) { + err = devlink_port_fn_caps_set(port, attr, extack); + if (err) + return err; + } + /* Keep this as the last function attribute set, so that when * multiple port function attributes are set along with state, * Those can be applied first before activating the state. From 47d0c500d76c7b1d220c47cd875763fef04eba2e Mon Sep 17 00:00:00 2001 From: Shay Drory Date: Tue, 6 Dec 2022 20:51:16 +0200 Subject: [PATCH 1680/2002] net/mlx5: Add generic getters for other functions caps Downstream patch requires to get other function GENERAL2 caps while mlx5_vport_get_other_func_cap() gets only one type of caps (general). Rename it to represent this and introduce a generic implementation of mlx5_vport_get_other_func_cap(). Signed-off-by: Shay Drory Reviewed-by: Mark Bloch Acked-by: Saeed Mahameed Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c | 2 +- drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h | 3 ++- drivers/net/ethernet/mellanox/mlx5/core/pci_irq.c | 3 ++- drivers/net/ethernet/mellanox/mlx5/core/vport.c | 6 ++++-- include/linux/mlx5/vport.h | 2 ++ 5 files changed, 11 insertions(+), 5 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c b/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c index 9b6fbb19c22a..33dffcb8bdd7 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c @@ -3889,7 +3889,7 @@ static int mlx5_esw_query_vport_vhca_id(struct mlx5_eswitch *esw, u16 vport_num, if (!query_ctx) return -ENOMEM; - err = mlx5_vport_get_other_func_cap(esw->dev, vport_num, query_ctx); + err = mlx5_vport_get_other_func_general_cap(esw->dev, vport_num, query_ctx); if (err) goto out_free; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h b/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h index a806e3de7b7c..09473983778f 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h @@ -324,7 +324,8 @@ void mlx5_unload_one_devl_locked(struct mlx5_core_dev *dev); int mlx5_load_one(struct mlx5_core_dev *dev, bool recovery); int mlx5_load_one_devl_locked(struct mlx5_core_dev *dev, bool recovery); -int mlx5_vport_get_other_func_cap(struct mlx5_core_dev *dev, u16 function_id, void *out); +#define mlx5_vport_get_other_func_general_cap(dev, fid, out) \ + mlx5_vport_get_other_func_cap(dev, fid, out, MLX5_CAP_GENERAL) void mlx5_events_work_enqueue(struct mlx5_core_dev *dev, struct work_struct *work); static inline u32 mlx5_sriov_get_vf_total_msix(struct pci_dev *pdev) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/pci_irq.c b/drivers/net/ethernet/mellanox/mlx5/core/pci_irq.c index 662f1d55e30e..6bde18bcd42f 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/pci_irq.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/pci_irq.c @@ -4,6 +4,7 @@ #include #include #include +#include #include "mlx5_core.h" #include "mlx5_irq.h" #include "pci_irq.h" @@ -101,7 +102,7 @@ int mlx5_set_msix_vec_count(struct mlx5_core_dev *dev, int function_id, goto out; } - ret = mlx5_vport_get_other_func_cap(dev, function_id, query_cap); + ret = mlx5_vport_get_other_func_general_cap(dev, function_id, query_cap); if (ret) goto out; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/vport.c b/drivers/net/ethernet/mellanox/mlx5/core/vport.c index d5c317325030..7eca7582f243 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/vport.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/vport.c @@ -1160,14 +1160,16 @@ u64 mlx5_query_nic_system_image_guid(struct mlx5_core_dev *mdev) } EXPORT_SYMBOL_GPL(mlx5_query_nic_system_image_guid); -int mlx5_vport_get_other_func_cap(struct mlx5_core_dev *dev, u16 function_id, void *out) +int mlx5_vport_get_other_func_cap(struct mlx5_core_dev *dev, u16 function_id, void *out, + u16 opmod) { - u16 opmod = (MLX5_CAP_GENERAL << 1) | (HCA_CAP_OPMOD_GET_MAX & 0x01); u8 in[MLX5_ST_SZ_BYTES(query_hca_cap_in)] = {}; + opmod = (opmod << 1) | (HCA_CAP_OPMOD_GET_MAX & 0x01); MLX5_SET(query_hca_cap_in, in, opcode, MLX5_CMD_OP_QUERY_HCA_CAP); MLX5_SET(query_hca_cap_in, in, op_mod, opmod); MLX5_SET(query_hca_cap_in, in, function_id, function_id); MLX5_SET(query_hca_cap_in, in, other_function, true); return mlx5_cmd_exec_inout(dev, query_hca_cap, in, out); } +EXPORT_SYMBOL_GPL(mlx5_vport_get_other_func_cap); diff --git a/include/linux/mlx5/vport.h b/include/linux/mlx5/vport.h index aad53cb72f17..7f31432f44c2 100644 --- a/include/linux/mlx5/vport.h +++ b/include/linux/mlx5/vport.h @@ -132,4 +132,6 @@ int mlx5_nic_vport_affiliate_multiport(struct mlx5_core_dev *master_mdev, int mlx5_nic_vport_unaffiliate_multiport(struct mlx5_core_dev *port_mdev); u64 mlx5_query_nic_system_image_guid(struct mlx5_core_dev *mdev); +int mlx5_vport_get_other_func_cap(struct mlx5_core_dev *dev, u16 function_id, void *out, + u16 opmod); #endif /* __MLX5_VPORT_H__ */ From 7db98396ef4581e4ca2f7e7beb766f4d19fc856a Mon Sep 17 00:00:00 2001 From: Yishai Hadas Date: Tue, 6 Dec 2022 20:51:17 +0200 Subject: [PATCH 1681/2002] net/mlx5: E-Switch, Implement devlink port function cmds to control RoCE Implement devlink port function commands to enable / disable RoCE. This is used to control the RoCE device capabilities. This patch implement infrastructure which will be used by downstream patches that will add additional capabilities. Signed-off-by: Yishai Hadas Signed-off-by: Daniel Jurgens Signed-off-by: Shay Drory Reviewed-by: Parav Pandit Reviewed-by: Mark Bloch Acked-by: Saeed Mahameed Signed-off-by: Jakub Kicinski --- .../device_drivers/ethernet/mellanox/mlx5.rst | 10 ++ .../net/ethernet/mellanox/mlx5/core/devlink.c | 2 + .../net/ethernet/mellanox/mlx5/core/eswitch.c | 35 ++++++ .../net/ethernet/mellanox/mlx5/core/eswitch.h | 6 +- .../mellanox/mlx5/core/eswitch_offloads.c | 108 ++++++++++++++++++ .../ethernet/mellanox/mlx5/core/mlx5_core.h | 2 + .../net/ethernet/mellanox/mlx5/core/vport.c | 24 ++++ 7 files changed, 186 insertions(+), 1 deletion(-) diff --git a/Documentation/networking/device_drivers/ethernet/mellanox/mlx5.rst b/Documentation/networking/device_drivers/ethernet/mellanox/mlx5.rst index 07cfc1b07db3..8b8f95d1293a 100644 --- a/Documentation/networking/device_drivers/ethernet/mellanox/mlx5.rst +++ b/Documentation/networking/device_drivers/ethernet/mellanox/mlx5.rst @@ -354,6 +354,16 @@ MAC address setup mlx5 driver support devlink port function attr mechanism to setup MAC address. (refer to Documentation/networking/devlink/devlink-port.rst) +RoCE capability setup +--------------------- +Not all mlx5 PCI devices/SFs require RoCE capability. + +When RoCE capability is disabled, it saves 1 Mbytes worth of system memory per +PCI devices/SF. + +mlx5 driver support devlink port function attr mechanism to setup RoCE +capability. (refer to Documentation/networking/devlink/devlink-port.rst) + SF state setup -------------- To use the SF, the user must activate the SF using the SF function state diff --git a/drivers/net/ethernet/mellanox/mlx5/core/devlink.c b/drivers/net/ethernet/mellanox/mlx5/core/devlink.c index 751bc4a9edcf..336c7b7fa494 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/devlink.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/devlink.c @@ -314,6 +314,8 @@ static const struct devlink_ops mlx5_devlink_ops = { .rate_node_new = mlx5_esw_devlink_rate_node_new, .rate_node_del = mlx5_esw_devlink_rate_node_del, .rate_leaf_parent_set = mlx5_esw_devlink_rate_parent_set, + .port_fn_roce_get = mlx5_devlink_port_fn_roce_get, + .port_fn_roce_set = mlx5_devlink_port_fn_roce_set, #endif #ifdef CONFIG_MLX5_SF_MANAGER .port_new = mlx5_devlink_sf_port_new, diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c index 374e3fbdc2cf..001fb1e62135 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c @@ -772,6 +772,33 @@ static void esw_vport_cleanup_acl(struct mlx5_eswitch *esw, esw_vport_destroy_offloads_acl_tables(esw, vport); } +static int mlx5_esw_vport_caps_get(struct mlx5_eswitch *esw, struct mlx5_vport *vport) +{ + int query_out_sz = MLX5_ST_SZ_BYTES(query_hca_cap_out); + void *query_ctx; + void *hca_caps; + int err; + + if (!MLX5_CAP_GEN(esw->dev, vhca_resource_manager)) + return 0; + + query_ctx = kzalloc(query_out_sz, GFP_KERNEL); + if (!query_ctx) + return -ENOMEM; + + err = mlx5_vport_get_other_func_cap(esw->dev, vport->vport, query_ctx, + MLX5_CAP_GENERAL); + if (err) + goto out_free; + + hca_caps = MLX5_ADDR_OF(query_hca_cap_out, query_ctx, capability); + vport->info.roce_enabled = MLX5_GET(cmd_hca_cap, hca_caps, roce); + +out_free: + kfree(query_ctx); + return err; +} + static int esw_vport_setup(struct mlx5_eswitch *esw, struct mlx5_vport *vport) { u16 vport_num = vport->vport; @@ -785,6 +812,10 @@ static int esw_vport_setup(struct mlx5_eswitch *esw, struct mlx5_vport *vport) if (mlx5_esw_is_manager_vport(esw, vport_num)) return 0; + err = mlx5_esw_vport_caps_get(esw, vport); + if (err) + goto err_caps; + mlx5_modify_vport_admin_state(esw->dev, MLX5_VPORT_STATE_OP_MOD_ESW_VPORT, vport_num, 1, @@ -804,6 +835,10 @@ static int esw_vport_setup(struct mlx5_eswitch *esw, struct mlx5_vport *vport) vport->info.qos, flags); return 0; + +err_caps: + esw_vport_cleanup_acl(esw, vport); + return err; } /* Don't cleanup vport->info, it's needed to restore vport configuration */ diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h index 42d9df417e20..eea0521729df 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h @@ -153,6 +153,7 @@ struct mlx5_vport_info { u8 qos; u8 spoofchk: 1; u8 trusted: 1; + u8 roce_enabled: 1; }; /* Vport context events */ @@ -508,7 +509,10 @@ int mlx5_devlink_port_function_hw_addr_get(struct devlink_port *port, int mlx5_devlink_port_function_hw_addr_set(struct devlink_port *port, const u8 *hw_addr, int hw_addr_len, struct netlink_ext_ack *extack); - +int mlx5_devlink_port_fn_roce_get(struct devlink_port *port, bool *is_enabled, + struct netlink_ext_ack *extack); +int mlx5_devlink_port_fn_roce_set(struct devlink_port *port, bool enable, + struct netlink_ext_ack *extack); void *mlx5_eswitch_get_uplink_priv(struct mlx5_eswitch *esw, u8 rep_type); int mlx5_eswitch_add_vlan_action(struct mlx5_eswitch *esw, diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c b/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c index 33dffcb8bdd7..7618c51351ca 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c @@ -4022,3 +4022,111 @@ int mlx5_devlink_port_function_hw_addr_set(struct devlink_port *port, return mlx5_eswitch_set_vport_mac(esw, vport_num, hw_addr); } + +static struct mlx5_vport * +mlx5_devlink_port_fn_get_vport(struct devlink_port *port, struct mlx5_eswitch *esw) +{ + u16 vport_num; + + if (!MLX5_CAP_GEN(esw->dev, vhca_resource_manager)) + return ERR_PTR(-EOPNOTSUPP); + + vport_num = mlx5_esw_devlink_port_index_to_vport_num(port->index); + if (!is_port_function_supported(esw, vport_num)) + return ERR_PTR(-EOPNOTSUPP); + + return mlx5_eswitch_get_vport(esw, vport_num); +} + +int mlx5_devlink_port_fn_roce_get(struct devlink_port *port, bool *is_enabled, + struct netlink_ext_ack *extack) +{ + struct mlx5_eswitch *esw; + struct mlx5_vport *vport; + int err = -EOPNOTSUPP; + + esw = mlx5_devlink_eswitch_get(port->devlink); + if (IS_ERR(esw)) + return PTR_ERR(esw); + + vport = mlx5_devlink_port_fn_get_vport(port, esw); + if (IS_ERR(vport)) { + NL_SET_ERR_MSG_MOD(extack, "Invalid port"); + return PTR_ERR(vport); + } + + mutex_lock(&esw->state_lock); + if (vport->enabled) { + *is_enabled = vport->info.roce_enabled; + err = 0; + } + mutex_unlock(&esw->state_lock); + return err; +} + +int mlx5_devlink_port_fn_roce_set(struct devlink_port *port, bool enable, + struct netlink_ext_ack *extack) +{ + int query_out_sz = MLX5_ST_SZ_BYTES(query_hca_cap_out); + struct mlx5_eswitch *esw; + struct mlx5_vport *vport; + int err = -EOPNOTSUPP; + void *query_ctx; + void *hca_caps; + u16 vport_num; + + esw = mlx5_devlink_eswitch_get(port->devlink); + if (IS_ERR(esw)) + return PTR_ERR(esw); + + vport = mlx5_devlink_port_fn_get_vport(port, esw); + if (IS_ERR(vport)) { + NL_SET_ERR_MSG_MOD(extack, "Invalid port"); + return PTR_ERR(vport); + } + vport_num = vport->vport; + + mutex_lock(&esw->state_lock); + if (!vport->enabled) { + NL_SET_ERR_MSG_MOD(extack, "Eswitch vport is disabled"); + goto out; + } + + if (vport->info.roce_enabled == enable) { + err = 0; + goto out; + } + + query_ctx = kzalloc(query_out_sz, GFP_KERNEL); + if (!query_ctx) { + err = -ENOMEM; + goto out; + } + + err = mlx5_vport_get_other_func_cap(esw->dev, vport_num, query_ctx, + MLX5_CAP_GENERAL); + if (err) { + NL_SET_ERR_MSG_MOD(extack, "Failed getting HCA caps"); + goto out_free; + } + + hca_caps = MLX5_ADDR_OF(query_hca_cap_out, query_ctx, capability); + memcpy(hca_caps, MLX5_ADDR_OF(query_hca_cap_out, query_ctx, capability), + MLX5_UN_SZ_BYTES(hca_cap_union)); + MLX5_SET(cmd_hca_cap, hca_caps, roce, enable); + + err = mlx5_vport_set_other_func_cap(esw->dev, hca_caps, vport_num, + MLX5_SET_HCA_CAP_OP_MOD_GENERAL_DEVICE); + if (err) { + NL_SET_ERR_MSG_MOD(extack, "Failed setting HCA roce cap"); + goto out_free; + } + + vport->info.roce_enabled = enable; + +out_free: + kfree(query_ctx); +out: + mutex_unlock(&esw->state_lock); + return err; +} diff --git a/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h b/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h index 09473983778f..029305a8b80a 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h @@ -324,6 +324,8 @@ void mlx5_unload_one_devl_locked(struct mlx5_core_dev *dev); int mlx5_load_one(struct mlx5_core_dev *dev, bool recovery); int mlx5_load_one_devl_locked(struct mlx5_core_dev *dev, bool recovery); +int mlx5_vport_set_other_func_cap(struct mlx5_core_dev *dev, const void *hca_cap, u16 function_id, + u16 opmod); #define mlx5_vport_get_other_func_general_cap(dev, fid, out) \ mlx5_vport_get_other_func_cap(dev, fid, out, MLX5_CAP_GENERAL) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/vport.c b/drivers/net/ethernet/mellanox/mlx5/core/vport.c index 7eca7582f243..ba7e3df22413 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/vport.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/vport.c @@ -1173,3 +1173,27 @@ int mlx5_vport_get_other_func_cap(struct mlx5_core_dev *dev, u16 function_id, vo return mlx5_cmd_exec_inout(dev, query_hca_cap, in, out); } EXPORT_SYMBOL_GPL(mlx5_vport_get_other_func_cap); + +int mlx5_vport_set_other_func_cap(struct mlx5_core_dev *dev, const void *hca_cap, + u16 function_id, u16 opmod) +{ + int set_sz = MLX5_ST_SZ_BYTES(set_hca_cap_in); + void *set_hca_cap; + void *set_ctx; + int ret; + + set_ctx = kzalloc(set_sz, GFP_KERNEL); + if (!set_ctx) + return -ENOMEM; + + MLX5_SET(set_hca_cap_in, set_ctx, opcode, MLX5_CMD_OP_SET_HCA_CAP); + MLX5_SET(set_hca_cap_in, set_ctx, op_mod, opmod << 1); + set_hca_cap = MLX5_ADDR_OF(set_hca_cap_in, set_ctx, capability); + memcpy(set_hca_cap, hca_cap, MLX5_ST_SZ_BYTES(cmd_hca_cap)); + MLX5_SET(set_hca_cap_in, set_ctx, function_id, function_id); + MLX5_SET(set_hca_cap_in, set_ctx, other_function, true); + ret = mlx5_cmd_exec_in(dev, set_hca_cap, set_ctx); + + kfree(set_ctx); + return ret; +} From a8ce7b26a51efc4d7753b23d639ae092878a6193 Mon Sep 17 00:00:00 2001 From: Shay Drory Date: Tue, 6 Dec 2022 20:51:18 +0200 Subject: [PATCH 1682/2002] devlink: Expose port function commands to control migratable Expose port function commands to enable / disable migratable capability, this is used to set the port function as migratable. Live migration is the process of transferring a live virtual machine from one physical host to another without disrupting its normal operation. In order for a VM to be able to perform LM, all the VM components must be able to perform migration. e.g.: to be migratable. In order for VF to be migratable, VF must be bound to VFIO driver with migration support. When migratable capability is enabled for a function of the port, the device is making the necessary preparations for the function to be migratable, which might include disabling features which cannot be migrated. Example of LM with migratable function configuration: Set migratable of the VF's port function. $ devlink port show pci/0000:06:00.0/2 pci/0000:06:00.0/2: type eth netdev enp6s0pf0vf1 flavour pcivf pfnum 0 vfnum 1 function: hw_addr 00:00:00:00:00:00 migratable disable $ devlink port function set pci/0000:06:00.0/2 migratable enable $ devlink port show pci/0000:06:00.0/2 pci/0000:06:00.0/2: type eth netdev enp6s0pf0vf1 flavour pcivf pfnum 0 vfnum 1 function: hw_addr 00:00:00:00:00:00 migratable enable Bind VF to VFIO driver with migration support: $ echo > /sys/bus/pci/devices/0000:08:00.0/driver/unbind $ echo mlx5_vfio_pci > /sys/bus/pci/devices/0000:08:00.0/driver_override $ echo > /sys/bus/pci/devices/0000:08:00.0/driver/bind Attach VF to the VM. Start the VM. Perform LM. Signed-off-by: Shay Drory Reviewed-by: Jiri Pirko Acked-by: Shannon Nelson Signed-off-by: Jakub Kicinski --- .../networking/devlink/devlink-port.rst | 46 ++++++++++++++++ include/net/devlink.h | 21 +++++++ include/uapi/linux/devlink.h | 3 + net/core/devlink.c | 55 +++++++++++++++++++ 4 files changed, 125 insertions(+) diff --git a/Documentation/networking/devlink/devlink-port.rst b/Documentation/networking/devlink/devlink-port.rst index c3302d23e480..3da590953ce8 100644 --- a/Documentation/networking/devlink/devlink-port.rst +++ b/Documentation/networking/devlink/devlink-port.rst @@ -125,6 +125,9 @@ this means a MAC address. Users may also set the RoCE capability of the function using `devlink port function set roce` command. +Users may also set the function as migratable using +'devlink port function set migratable' command. + Function attributes =================== @@ -194,6 +197,49 @@ VF/SF driver cannot override it. function: hw_addr 00:00:00:00:00:00 roce disable +migratable capability setup +--------------------------- +Live migration is the process of transferring a live virtual machine +from one physical host to another without disrupting its normal +operation. + +User who want PCI VFs to be able to perform live migration need to +explicitly enable the VF migratable capability. + +When user enables migratable capability for a VF, and the HV binds the VF to VFIO driver +with migration support, the user can migrate the VM with this VF from one HV to a +different one. + +However, when migratable capability is enable, device will disable features which cannot +be migrated. Thus migratable cap can impose limitations on a VF so let the user decide. + +Example of LM with migratable function configuration: +- Get migratable capability of the VF device:: + + $ devlink port show pci/0000:06:00.0/2 + pci/0000:06:00.0/2: type eth netdev enp6s0pf0vf1 flavour pcivf pfnum 0 vfnum 1 + function: + hw_addr 00:00:00:00:00:00 migratable disable + +- Set migratable capability of the VF device:: + + $ devlink port function set pci/0000:06:00.0/2 migratable enable + + $ devlink port show pci/0000:06:00.0/2 + pci/0000:06:00.0/2: type eth netdev enp6s0pf0vf1 flavour pcivf pfnum 0 vfnum 1 + function: + hw_addr 00:00:00:00:00:00 migratable enable + +- Bind VF to VFIO driver with migration support:: + + $ echo > /sys/bus/pci/devices/0000:08:00.0/driver/unbind + $ echo mlx5_vfio_pci > /sys/bus/pci/devices/0000:08:00.0/driver_override + $ echo > /sys/bus/pci/devices/0000:08:00.0/driver/bind + +Attach VF to the VM. +Start the VM. +Perform live migration. + Subfunction ============ diff --git a/include/net/devlink.h b/include/net/devlink.h index ce4c65d2f2e7..0f376a28b9c4 100644 --- a/include/net/devlink.h +++ b/include/net/devlink.h @@ -1469,6 +1469,27 @@ struct devlink_ops { */ int (*port_fn_roce_set)(struct devlink_port *devlink_port, bool enable, struct netlink_ext_ack *extack); + /** + * @port_fn_migratable_get: Port function's migratable get function. + * + * Query migratable state of a function managed by the devlink port. + * Return -EOPNOTSUPP if port function migratable handling is not + * supported. + */ + int (*port_fn_migratable_get)(struct devlink_port *devlink_port, + bool *is_enable, + struct netlink_ext_ack *extack); + /** + * @port_fn_migratable_set: Port function's migratable set function. + * + * Enable/Disable migratable state of a function managed by the devlink + * port. + * Return -EOPNOTSUPP if port function migratable handling is not + * supported. + */ + int (*port_fn_migratable_set)(struct devlink_port *devlink_port, + bool enable, + struct netlink_ext_ack *extack); /** * port_new() - Add a new port function of a specified flavor * @devlink: Devlink instance diff --git a/include/uapi/linux/devlink.h b/include/uapi/linux/devlink.h index 6cc2925bd478..3782d4219ac9 100644 --- a/include/uapi/linux/devlink.h +++ b/include/uapi/linux/devlink.h @@ -660,12 +660,15 @@ enum devlink_resource_unit { enum devlink_port_fn_attr_cap { DEVLINK_PORT_FN_ATTR_CAP_ROCE_BIT, + DEVLINK_PORT_FN_ATTR_CAP_MIGRATABLE_BIT, /* Add new caps above */ __DEVLINK_PORT_FN_ATTR_CAPS_MAX, }; #define DEVLINK_PORT_FN_CAP_ROCE _BITUL(DEVLINK_PORT_FN_ATTR_CAP_ROCE_BIT) +#define DEVLINK_PORT_FN_CAP_MIGRATABLE \ + _BITUL(DEVLINK_PORT_FN_ATTR_CAP_MIGRATABLE_BIT) enum devlink_port_function_attr { DEVLINK_PORT_FUNCTION_ATTR_UNSPEC, diff --git a/net/core/devlink.c b/net/core/devlink.c index 8c0ad52431c5..ab40ebcb4aea 100644 --- a/net/core/devlink.c +++ b/net/core/devlink.c @@ -715,6 +715,29 @@ static int devlink_port_fn_roce_fill(const struct devlink_ops *ops, return 0; } +static int devlink_port_fn_migratable_fill(const struct devlink_ops *ops, + struct devlink_port *devlink_port, + struct nla_bitfield32 *caps, + struct netlink_ext_ack *extack) +{ + bool is_enable; + int err; + + if (!ops->port_fn_migratable_get || + devlink_port->attrs.flavour != DEVLINK_PORT_FLAVOUR_PCI_VF) + return 0; + + err = ops->port_fn_migratable_get(devlink_port, &is_enable, extack); + if (err) { + if (err == -EOPNOTSUPP) + return 0; + return err; + } + + devlink_port_fn_cap_fill(caps, DEVLINK_PORT_FN_CAP_MIGRATABLE, is_enable); + return 0; +} + static int devlink_port_fn_caps_fill(const struct devlink_ops *ops, struct devlink_port *devlink_port, struct sk_buff *msg, @@ -728,6 +751,10 @@ static int devlink_port_fn_caps_fill(const struct devlink_ops *ops, if (err) return err; + err = devlink_port_fn_migratable_fill(ops, devlink_port, &caps, extack); + if (err) + return err; + if (!caps.selector) return 0; err = nla_put_bitfield32(msg, DEVLINK_PORT_FN_ATTR_CAPS, caps.value, @@ -1322,6 +1349,15 @@ static int devlink_port_fn_state_fill(const struct devlink_ops *ops, return 0; } +static int +devlink_port_fn_mig_set(struct devlink_port *devlink_port, bool enable, + struct netlink_ext_ack *extack) +{ + const struct devlink_ops *ops = devlink_port->devlink->ops; + + return ops->port_fn_migratable_set(devlink_port, enable, extack); +} + static int devlink_port_fn_roce_set(struct devlink_port *devlink_port, bool enable, struct netlink_ext_ack *extack) @@ -1348,6 +1384,13 @@ static int devlink_port_fn_caps_set(struct devlink_port *devlink_port, if (err) return err; } + if (caps.selector & DEVLINK_PORT_FN_CAP_MIGRATABLE) { + err = devlink_port_fn_mig_set(devlink_port, caps_value & + DEVLINK_PORT_FN_CAP_MIGRATABLE, + extack); + if (err) + return err; + } return 0; } @@ -1769,6 +1812,18 @@ static int devlink_port_function_validate(struct devlink_port *devlink_port, "Port doesn't support RoCE function attribute"); return -EOPNOTSUPP; } + if (caps.selector & DEVLINK_PORT_FN_CAP_MIGRATABLE) { + if (!ops->port_fn_migratable_set) { + NL_SET_ERR_MSG_ATTR(extack, attr, + "Port doesn't support migratable function attribute"); + return -EOPNOTSUPP; + } + if (devlink_port->attrs.flavour != DEVLINK_PORT_FLAVOUR_PCI_VF) { + NL_SET_ERR_MSG_ATTR(extack, attr, + "migratable function attribute supported for VFs only"); + return -EOPNOTSUPP; + } + } } return 0; } From e5b9642a33be6f6a9ec2f035299f4a5c0980d7c0 Mon Sep 17 00:00:00 2001 From: Shay Drory Date: Tue, 6 Dec 2022 20:51:19 +0200 Subject: [PATCH 1683/2002] net/mlx5: E-Switch, Implement devlink port function cmds to control migratable Implement devlink port function commands to enable / disable migratable. This is used to control the migratable capability of the device. Signed-off-by: Shay Drory Reviewed-by: Mark Bloch Acked-by: Saeed Mahameed Signed-off-by: Jakub Kicinski --- .../device_drivers/ethernet/mellanox/mlx5.rst | 8 ++ .../net/ethernet/mellanox/mlx5/core/devlink.c | 2 + .../net/ethernet/mellanox/mlx5/core/eswitch.c | 8 ++ .../net/ethernet/mellanox/mlx5/core/eswitch.h | 5 + .../mellanox/mlx5/core/eswitch_offloads.c | 101 ++++++++++++++++++ 5 files changed, 124 insertions(+) diff --git a/Documentation/networking/device_drivers/ethernet/mellanox/mlx5.rst b/Documentation/networking/device_drivers/ethernet/mellanox/mlx5.rst index 8b8f95d1293a..6969652f593c 100644 --- a/Documentation/networking/device_drivers/ethernet/mellanox/mlx5.rst +++ b/Documentation/networking/device_drivers/ethernet/mellanox/mlx5.rst @@ -364,6 +364,14 @@ PCI devices/SF. mlx5 driver support devlink port function attr mechanism to setup RoCE capability. (refer to Documentation/networking/devlink/devlink-port.rst) +migratable capability setup +--------------------------- +User who wants mlx5 PCI VFs to be able to perform live migration need to +explicitly enable the VF migratable capability. + +mlx5 driver support devlink port function attr mechanism to setup migratable +capability. (refer to Documentation/networking/devlink/devlink-port.rst) + SF state setup -------------- To use the SF, the user must activate the SF using the SF function state diff --git a/drivers/net/ethernet/mellanox/mlx5/core/devlink.c b/drivers/net/ethernet/mellanox/mlx5/core/devlink.c index 336c7b7fa494..ddb197970c22 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/devlink.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/devlink.c @@ -316,6 +316,8 @@ static const struct devlink_ops mlx5_devlink_ops = { .rate_leaf_parent_set = mlx5_esw_devlink_rate_parent_set, .port_fn_roce_get = mlx5_devlink_port_fn_roce_get, .port_fn_roce_set = mlx5_devlink_port_fn_roce_set, + .port_fn_migratable_get = mlx5_devlink_port_fn_migratable_get, + .port_fn_migratable_set = mlx5_devlink_port_fn_migratable_set, #endif #ifdef CONFIG_MLX5_SF_MANAGER .port_new = mlx5_devlink_sf_port_new, diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c index 001fb1e62135..527e4bffda8d 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c @@ -794,6 +794,14 @@ static int mlx5_esw_vport_caps_get(struct mlx5_eswitch *esw, struct mlx5_vport * hca_caps = MLX5_ADDR_OF(query_hca_cap_out, query_ctx, capability); vport->info.roce_enabled = MLX5_GET(cmd_hca_cap, hca_caps, roce); + memset(query_ctx, 0, query_out_sz); + err = mlx5_vport_get_other_func_cap(esw->dev, vport->vport, query_ctx, + MLX5_CAP_GENERAL_2); + if (err) + goto out_free; + + hca_caps = MLX5_ADDR_OF(query_hca_cap_out, query_ctx, capability); + vport->info.mig_enabled = MLX5_GET(cmd_hca_cap_2, hca_caps, migratable); out_free: kfree(query_ctx); return err; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h index eea0521729df..5a85a5d32be7 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h @@ -154,6 +154,7 @@ struct mlx5_vport_info { u8 spoofchk: 1; u8 trusted: 1; u8 roce_enabled: 1; + u8 mig_enabled: 1; }; /* Vport context events */ @@ -513,6 +514,10 @@ int mlx5_devlink_port_fn_roce_get(struct devlink_port *port, bool *is_enabled, struct netlink_ext_ack *extack); int mlx5_devlink_port_fn_roce_set(struct devlink_port *port, bool enable, struct netlink_ext_ack *extack); +int mlx5_devlink_port_fn_migratable_get(struct devlink_port *port, bool *is_enabled, + struct netlink_ext_ack *extack); +int mlx5_devlink_port_fn_migratable_set(struct devlink_port *port, bool enable, + struct netlink_ext_ack *extack); void *mlx5_eswitch_get_uplink_priv(struct mlx5_eswitch *esw, u8 rep_type); int mlx5_eswitch_add_vlan_action(struct mlx5_eswitch *esw, diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c b/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c index 7618c51351ca..c6a14202c62c 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c @@ -4038,6 +4038,107 @@ mlx5_devlink_port_fn_get_vport(struct devlink_port *port, struct mlx5_eswitch *e return mlx5_eswitch_get_vport(esw, vport_num); } +int mlx5_devlink_port_fn_migratable_get(struct devlink_port *port, bool *is_enabled, + struct netlink_ext_ack *extack) +{ + struct mlx5_eswitch *esw; + struct mlx5_vport *vport; + int err = -EOPNOTSUPP; + + esw = mlx5_devlink_eswitch_get(port->devlink); + if (IS_ERR(esw)) + return PTR_ERR(esw); + + if (!MLX5_CAP_GEN(esw->dev, migration)) { + NL_SET_ERR_MSG_MOD(extack, "Device doesn't support migration"); + return err; + } + + vport = mlx5_devlink_port_fn_get_vport(port, esw); + if (IS_ERR(vport)) { + NL_SET_ERR_MSG_MOD(extack, "Invalid port"); + return PTR_ERR(vport); + } + + mutex_lock(&esw->state_lock); + if (vport->enabled) { + *is_enabled = vport->info.mig_enabled; + err = 0; + } + mutex_unlock(&esw->state_lock); + return err; +} + +int mlx5_devlink_port_fn_migratable_set(struct devlink_port *port, bool enable, + struct netlink_ext_ack *extack) +{ + int query_out_sz = MLX5_ST_SZ_BYTES(query_hca_cap_out); + struct mlx5_eswitch *esw; + struct mlx5_vport *vport; + void *query_ctx; + void *hca_caps; + int err = -EOPNOTSUPP; + + esw = mlx5_devlink_eswitch_get(port->devlink); + if (IS_ERR(esw)) + return PTR_ERR(esw); + + if (!MLX5_CAP_GEN(esw->dev, migration)) { + NL_SET_ERR_MSG_MOD(extack, "Device doesn't support migration"); + return err; + } + + vport = mlx5_devlink_port_fn_get_vport(port, esw); + if (IS_ERR(vport)) { + NL_SET_ERR_MSG_MOD(extack, "Invalid port"); + return PTR_ERR(vport); + } + + mutex_lock(&esw->state_lock); + if (!vport->enabled) { + NL_SET_ERR_MSG_MOD(extack, "Eswitch vport is disabled"); + goto out; + } + + if (vport->info.mig_enabled == enable) { + err = 0; + goto out; + } + + query_ctx = kzalloc(query_out_sz, GFP_KERNEL); + if (!query_ctx) { + err = -ENOMEM; + goto out; + } + + err = mlx5_vport_get_other_func_cap(esw->dev, vport->vport, query_ctx, + MLX5_CAP_GENERAL_2); + if (err) { + NL_SET_ERR_MSG_MOD(extack, "Failed getting HCA caps"); + goto out_free; + } + + hca_caps = MLX5_ADDR_OF(query_hca_cap_out, query_ctx, capability); + memcpy(hca_caps, MLX5_ADDR_OF(query_hca_cap_out, query_ctx, capability), + MLX5_UN_SZ_BYTES(hca_cap_union)); + MLX5_SET(cmd_hca_cap_2, hca_caps, migratable, 1); + + err = mlx5_vport_set_other_func_cap(esw->dev, hca_caps, vport->vport, + MLX5_SET_HCA_CAP_OP_MOD_GENERAL_DEVICE2); + if (err) { + NL_SET_ERR_MSG_MOD(extack, "Failed setting HCA migratable cap"); + goto out_free; + } + + vport->info.mig_enabled = enable; + +out_free: + kfree(query_ctx); +out: + mutex_unlock(&esw->state_lock); + return err; +} + int mlx5_devlink_port_fn_roce_get(struct devlink_port *port, bool *is_enabled, struct netlink_ext_ack *extack) { From 1a352596722a129cacc5f601f42ece083d400086 Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Tue, 6 Dec 2022 11:44:14 +0200 Subject: [PATCH 1684/2002] Revert "net: ethernet: ti: am65-cpsw: Fix hardware switch mode on suspend/resume" This reverts commit 1af3cb3702d02167926a2bd18580cecb2d64fd94. This is to make it easier to revert the offending commit fd23df72f2be ("net: ethernet: ti: am65-cpsw: Add suspend/resume support") Signed-off-by: Roger Quadros Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/ti/am65-cpsw-nuss.c | 7 ------- drivers/net/ethernet/ti/am65-cpsw-nuss.h | 2 -- 2 files changed, 9 deletions(-) diff --git a/drivers/net/ethernet/ti/am65-cpsw-nuss.c b/drivers/net/ethernet/ti/am65-cpsw-nuss.c index 51c37e99d086..071cb226315c 100644 --- a/drivers/net/ethernet/ti/am65-cpsw-nuss.c +++ b/drivers/net/ethernet/ti/am65-cpsw-nuss.c @@ -2714,7 +2714,6 @@ static int am65_cpsw_nuss_probe(struct platform_device *pdev) struct clk *clk; u64 id_temp; int ret, i; - int ale_entries; common = devm_kzalloc(dev, sizeof(struct am65_cpsw_common), GFP_KERNEL); if (!common) @@ -2809,10 +2808,6 @@ static int am65_cpsw_nuss_probe(struct platform_device *pdev) goto err_of_clear; } - ale_entries = common->ale->params.ale_entries; - common->ale_context = devm_kzalloc(dev, - ale_entries * ALE_ENTRY_WORDS * sizeof(u32), - GFP_KERNEL); ret = am65_cpsw_init_cpts(common); if (ret) goto err_of_clear; @@ -2883,7 +2878,6 @@ static int am65_cpsw_nuss_suspend(struct device *dev) int i, ret; struct am65_cpsw_host *host_p = am65_common_get_host(common); - cpsw_ale_dump(common->ale, common->ale_context); host_p->vid_context = readl(host_p->port_base + AM65_CPSW_PORT_VLAN_REG_OFFSET); for (i = 0; i < common->port_num; i++) { port = &common->ports[i]; @@ -2942,7 +2936,6 @@ static int am65_cpsw_nuss_resume(struct device *dev) } writel(host_p->vid_context, host_p->port_base + AM65_CPSW_PORT_VLAN_REG_OFFSET); - cpsw_ale_restore(common->ale, common->ale_context); return 0; } diff --git a/drivers/net/ethernet/ti/am65-cpsw-nuss.h b/drivers/net/ethernet/ti/am65-cpsw-nuss.h index 4b75620f8d28..e95cc37a7286 100644 --- a/drivers/net/ethernet/ti/am65-cpsw-nuss.h +++ b/drivers/net/ethernet/ti/am65-cpsw-nuss.h @@ -149,8 +149,6 @@ struct am65_cpsw_common { struct net_device *hw_bridge_dev; struct notifier_block am65_cpsw_netdevice_nb; unsigned char switch_id[MAX_PHYS_ITEM_ID_LEN]; - /* only for suspend/resume context restore */ - u32 *ale_context; }; struct am65_cpsw_ndev_stats { From 1bae8fa8c4f3e3fd447ea2acdad96bd0fd7ff5da Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Tue, 6 Dec 2022 11:44:15 +0200 Subject: [PATCH 1685/2002] Revert "net: ethernet: ti: am65-cpsw: retain PORT_VLAN_REG after suspend/resume" This reverts commit 643cf0e3ab5ccee37b3c53c018bd476c45c4b70e. This is to make it easier to revert the offending commit fd23df72f2be ("net: ethernet: ti: am65-cpsw: Add suspend/resume support") Signed-off-by: Roger Quadros Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/ti/am65-cpsw-nuss.c | 7 ------- drivers/net/ethernet/ti/am65-cpsw-nuss.h | 4 ---- 2 files changed, 11 deletions(-) diff --git a/drivers/net/ethernet/ti/am65-cpsw-nuss.c b/drivers/net/ethernet/ti/am65-cpsw-nuss.c index 071cb226315c..44773a3e930e 100644 --- a/drivers/net/ethernet/ti/am65-cpsw-nuss.c +++ b/drivers/net/ethernet/ti/am65-cpsw-nuss.c @@ -2876,9 +2876,7 @@ static int am65_cpsw_nuss_suspend(struct device *dev) struct am65_cpsw_port *port; struct net_device *ndev; int i, ret; - struct am65_cpsw_host *host_p = am65_common_get_host(common); - host_p->vid_context = readl(host_p->port_base + AM65_CPSW_PORT_VLAN_REG_OFFSET); for (i = 0; i < common->port_num; i++) { port = &common->ports[i]; ndev = port->ndev; @@ -2886,7 +2884,6 @@ static int am65_cpsw_nuss_suspend(struct device *dev) if (!ndev) continue; - port->vid_context = readl(port->port_base + AM65_CPSW_PORT_VLAN_REG_OFFSET); netif_device_detach(ndev); if (netif_running(ndev)) { rtnl_lock(); @@ -2910,7 +2907,6 @@ static int am65_cpsw_nuss_resume(struct device *dev) struct am65_cpsw_port *port; struct net_device *ndev; int i, ret; - struct am65_cpsw_host *host_p = am65_common_get_host(common); am65_cpts_resume(common->cpts); @@ -2932,11 +2928,8 @@ static int am65_cpsw_nuss_resume(struct device *dev) } netif_device_attach(ndev); - writel(port->vid_context, port->port_base + AM65_CPSW_PORT_VLAN_REG_OFFSET); } - writel(host_p->vid_context, host_p->port_base + AM65_CPSW_PORT_VLAN_REG_OFFSET); - return 0; } #endif /* CONFIG_PM_SLEEP */ diff --git a/drivers/net/ethernet/ti/am65-cpsw-nuss.h b/drivers/net/ethernet/ti/am65-cpsw-nuss.h index e95cc37a7286..2c9850fdfcb6 100644 --- a/drivers/net/ethernet/ti/am65-cpsw-nuss.h +++ b/drivers/net/ethernet/ti/am65-cpsw-nuss.h @@ -55,16 +55,12 @@ struct am65_cpsw_port { bool rx_ts_enabled; struct am65_cpsw_qos qos; struct devlink_port devlink_port; - /* Only for suspend resume context */ - u32 vid_context; }; struct am65_cpsw_host { struct am65_cpsw_common *common; void __iomem *port_base; void __iomem *stat_base; - /* Only for suspend resume context */ - u32 vid_context; }; struct am65_cpsw_tx_chn { From 1a014663e7dddb711566a9c9746200b187742092 Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Tue, 6 Dec 2022 11:44:16 +0200 Subject: [PATCH 1686/2002] Revert "net: ethernet: ti: am65-cpsw: Add suspend/resume support" This reverts commit fd23df72f2be317d38d9fde0a8996b8e7454fd2a. This commit broke set channel operation. Revert this and implement it with a different approach in a separate patch. Signed-off-by: Roger Quadros Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/ti/am65-cpsw-nuss.c | 221 ++++++----------------- 1 file changed, 55 insertions(+), 166 deletions(-) diff --git a/drivers/net/ethernet/ti/am65-cpsw-nuss.c b/drivers/net/ethernet/ti/am65-cpsw-nuss.c index 44773a3e930e..c682103ce946 100644 --- a/drivers/net/ethernet/ti/am65-cpsw-nuss.c +++ b/drivers/net/ethernet/ti/am65-cpsw-nuss.c @@ -24,7 +24,6 @@ #include #include #include -#include #include #include #include @@ -133,11 +132,6 @@ NETIF_MSG_IFUP | NETIF_MSG_PROBE | NETIF_MSG_IFDOWN | \ NETIF_MSG_RX_ERR | NETIF_MSG_TX_ERR) -static int am65_cpsw_nuss_init_tx_chns(struct am65_cpsw_common *common); -static int am65_cpsw_nuss_init_rx_chns(struct am65_cpsw_common *common); -static void am65_cpsw_nuss_free_tx_chns(struct am65_cpsw_common *common); -static void am65_cpsw_nuss_free_rx_chns(struct am65_cpsw_common *common); - static void am65_cpsw_port_set_sl_mac(struct am65_cpsw_port *slave, const u8 *dev_addr) { @@ -379,20 +373,6 @@ static int am65_cpsw_nuss_common_open(struct am65_cpsw_common *common) if (common->usage_count) return 0; - /* init tx/rx channels */ - ret = am65_cpsw_nuss_init_tx_chns(common); - if (ret) { - dev_err(common->dev, "init_tx_chns failed\n"); - return ret; - } - - ret = am65_cpsw_nuss_init_rx_chns(common); - if (ret) { - dev_err(common->dev, "init_rx_chns failed\n"); - am65_cpsw_nuss_free_tx_chns(common); - return ret; - } - /* Control register */ writel(AM65_CPSW_CTL_P0_ENABLE | AM65_CPSW_CTL_P0_TX_CRC_REMOVE | AM65_CPSW_CTL_VLAN_AWARE | AM65_CPSW_CTL_P0_RX_PAD, @@ -421,7 +401,6 @@ static int am65_cpsw_nuss_common_open(struct am65_cpsw_common *common) /* disable priority elevation */ writel(0, common->cpsw_base + AM65_CPSW_REG_PTYPE); - cpsw_ale_control_set(common->ale, 0, ALE_CLEAR, 1); cpsw_ale_start(common->ale); /* limit to one RX flow only */ @@ -453,8 +432,7 @@ static int am65_cpsw_nuss_common_open(struct am65_cpsw_common *common) GFP_KERNEL); if (!skb) { dev_err(common->dev, "cannot allocate skb\n"); - ret = -ENOMEM; - goto err; + return -ENOMEM; } ret = am65_cpsw_nuss_rx_push(common, skb); @@ -463,7 +441,7 @@ static int am65_cpsw_nuss_common_open(struct am65_cpsw_common *common) "cannot submit skb to channel rx, error %d\n", ret); kfree_skb(skb); - goto err; + return ret; } kmemleak_not_leak(skb); } @@ -472,7 +450,7 @@ static int am65_cpsw_nuss_common_open(struct am65_cpsw_common *common) for (i = 0; i < common->tx_ch_num; i++) { ret = k3_udma_glue_enable_tx_chn(common->tx_chns[i].tx_chn); if (ret) - goto err; + return ret; napi_enable(&common->tx_chns[i].napi_tx); } @@ -484,12 +462,6 @@ static int am65_cpsw_nuss_common_open(struct am65_cpsw_common *common) dev_dbg(common->dev, "cpsw_nuss started\n"); return 0; - -err: - am65_cpsw_nuss_free_tx_chns(common); - am65_cpsw_nuss_free_rx_chns(common); - - return ret; } static void am65_cpsw_nuss_tx_cleanup(void *data, dma_addr_t desc_dma); @@ -543,9 +515,6 @@ static int am65_cpsw_nuss_common_stop(struct am65_cpsw_common *common) writel(0, common->cpsw_base + AM65_CPSW_REG_CTL); writel(0, common->cpsw_base + AM65_CPSW_REG_STAT_PORT_EN); - am65_cpsw_nuss_free_tx_chns(common); - am65_cpsw_nuss_free_rx_chns(common); - dev_dbg(common->dev, "cpsw_nuss stopped\n"); return 0; } @@ -586,29 +555,11 @@ static int am65_cpsw_nuss_ndo_slave_open(struct net_device *ndev) struct am65_cpsw_common *common = am65_ndev_to_common(ndev); struct am65_cpsw_port *port = am65_ndev_to_port(ndev); int ret, i; - u32 reg; - int tmo; ret = pm_runtime_resume_and_get(common->dev); if (ret < 0) return ret; - /* Idle MAC port */ - cpsw_sl_ctl_set(port->slave.mac_sl, CPSW_SL_CTL_CMD_IDLE); - - tmo = cpsw_sl_wait_for_idle(port->slave.mac_sl, 100); - dev_info(common->dev, "down msc_sl %08x tmo %d\n", - cpsw_sl_reg_read(port->slave.mac_sl, CPSW_SL_MACSTATUS), tmo); - - cpsw_sl_ctl_reset(port->slave.mac_sl); - - /* soft reset MAC */ - cpsw_sl_reg_write(port->slave.mac_sl, CPSW_SL_SOFT_RESET, 1); - mdelay(1); - reg = cpsw_sl_reg_read(port->slave.mac_sl, CPSW_SL_SOFT_RESET); - if (reg) - dev_info(common->dev, "mac reset not yet done\n"); - /* Notify the stack of the actual queue counts. */ ret = netif_set_real_num_tx_queues(ndev, common->tx_ch_num); if (ret) { @@ -1539,9 +1490,9 @@ static void am65_cpsw_nuss_slave_disable_unused(struct am65_cpsw_port *port) cpsw_sl_ctl_reset(port->slave.mac_sl); } -static void am65_cpsw_nuss_free_tx_chns(struct am65_cpsw_common *common) +static void am65_cpsw_nuss_free_tx_chns(void *data) { - struct device *dev = common->dev; + struct am65_cpsw_common *common = data; int i; for (i = 0; i < common->tx_ch_num; i++) { @@ -1553,11 +1504,7 @@ static void am65_cpsw_nuss_free_tx_chns(struct am65_cpsw_common *common) if (!IS_ERR_OR_NULL(tx_chn->tx_chn)) k3_udma_glue_release_tx_chn(tx_chn->tx_chn); - /* Don't clear tx_chn memory as we need to preserve - * data between suspend/resume - */ - if (!(tx_chn->irq < 0)) - devm_free_irq(dev, tx_chn->irq, tx_chn); + memset(tx_chn, 0, sizeof(*tx_chn)); } } @@ -1566,10 +1513,12 @@ void am65_cpsw_nuss_remove_tx_chns(struct am65_cpsw_common *common) struct device *dev = common->dev; int i; + devm_remove_action(dev, am65_cpsw_nuss_free_tx_chns, common); + for (i = 0; i < common->tx_ch_num; i++) { struct am65_cpsw_tx_chn *tx_chn = &common->tx_chns[i]; - if (!(tx_chn->irq < 0)) + if (tx_chn->irq) devm_free_irq(dev, tx_chn->irq, tx_chn); netif_napi_del(&tx_chn->napi_tx); @@ -1639,7 +1588,7 @@ static int am65_cpsw_nuss_init_tx_chns(struct am65_cpsw_common *common) } tx_chn->irq = k3_udma_glue_tx_get_irq(tx_chn->tx_chn); - if (tx_chn->irq < 0) { + if (tx_chn->irq <= 0) { dev_err(dev, "Failed to get tx dma irq %d\n", tx_chn->irq); goto err; @@ -1648,36 +1597,25 @@ static int am65_cpsw_nuss_init_tx_chns(struct am65_cpsw_common *common) snprintf(tx_chn->tx_chn_name, sizeof(tx_chn->tx_chn_name), "%s-tx%d", dev_name(dev), tx_chn->id); - - ret = devm_request_irq(dev, tx_chn->irq, - am65_cpsw_nuss_tx_irq, - IRQF_TRIGGER_HIGH, - tx_chn->tx_chn_name, tx_chn); - if (ret) { - dev_err(dev, "failure requesting tx%u irq %u, %d\n", - tx_chn->id, tx_chn->irq, ret); - tx_chn->irq = -EINVAL; - goto err; - } } - return 0; - err: - am65_cpsw_nuss_free_tx_chns(common); + i = devm_add_action(dev, am65_cpsw_nuss_free_tx_chns, common); + if (i) { + dev_err(dev, "Failed to add free_tx_chns action %d\n", i); + return i; + } return ret; } -static void am65_cpsw_nuss_free_rx_chns(struct am65_cpsw_common *common) +static void am65_cpsw_nuss_free_rx_chns(void *data) { + struct am65_cpsw_common *common = data; struct am65_cpsw_rx_chn *rx_chn; rx_chn = &common->rx_chns; - if (!(rx_chn->irq < 0)) - devm_free_irq(common->dev, rx_chn->irq, common); - if (!IS_ERR_OR_NULL(rx_chn->desc_pool)) k3_cppi_desc_pool_destroy(rx_chn->desc_pool); @@ -1700,7 +1638,7 @@ static int am65_cpsw_nuss_init_rx_chns(struct am65_cpsw_common *common) rx_cfg.swdata_size = AM65_CPSW_NAV_SW_DATA_SIZE; rx_cfg.flow_id_num = AM65_CPSW_MAX_RX_FLOWS; - rx_cfg.flow_id_base = -1; + rx_cfg.flow_id_base = common->rx_flow_id_base; /* init all flows */ rx_chn->dev = dev; @@ -1772,20 +1710,12 @@ static int am65_cpsw_nuss_init_rx_chns(struct am65_cpsw_common *common) } } - ret = devm_request_irq(dev, rx_chn->irq, - am65_cpsw_nuss_rx_irq, - IRQF_TRIGGER_HIGH, dev_name(dev), common); - if (ret) { - dev_err(dev, "failure requesting rx irq %u, %d\n", - rx_chn->irq, ret); - rx_chn->irq = -EINVAL; - goto err; - } - - return 0; - err: - am65_cpsw_nuss_free_rx_chns(common); + i = devm_add_action(dev, am65_cpsw_nuss_free_rx_chns, common); + if (i) { + dev_err(dev, "Failed to add free_rx_chns action %d\n", i); + return i; + } return ret; } @@ -2051,7 +1981,6 @@ am65_cpsw_nuss_init_port_ndev(struct am65_cpsw_common *common, u32 port_idx) port->slave.phylink_config.dev = &port->ndev->dev; port->slave.phylink_config.type = PHYLINK_NETDEV; port->slave.phylink_config.mac_capabilities = MAC_SYM_PAUSE | MAC_10 | MAC_100 | MAC_1000FD; - port->slave.phylink_config.mac_managed_pm = true; /* MAC does PM */ if (phy_interface_mode_is_rgmii(port->slave.phy_if)) { phy_interface_set_rgmii(port->slave.phylink_config.supported_interfaces); @@ -2113,16 +2042,28 @@ static int am65_cpsw_nuss_init_ndevs(struct am65_cpsw_common *common) static int am65_cpsw_nuss_ndev_add_tx_napi(struct am65_cpsw_common *common) { - int i; + struct device *dev = common->dev; + int i, ret = 0; for (i = 0; i < common->tx_ch_num; i++) { struct am65_cpsw_tx_chn *tx_chn = &common->tx_chns[i]; netif_napi_add_tx(common->dma_ndev, &tx_chn->napi_tx, am65_cpsw_nuss_tx_poll); + + ret = devm_request_irq(dev, tx_chn->irq, + am65_cpsw_nuss_tx_irq, + IRQF_TRIGGER_HIGH, + tx_chn->tx_chn_name, tx_chn); + if (ret) { + dev_err(dev, "failure requesting tx%u irq %u, %d\n", + tx_chn->id, tx_chn->irq, ret); + goto err; + } } - return 0; +err: + return ret; } static void am65_cpsw_nuss_cleanup_ndev(struct am65_cpsw_common *common) @@ -2591,6 +2532,15 @@ static int am65_cpsw_nuss_register_ndevs(struct am65_cpsw_common *common) if (ret) return ret; + ret = devm_request_irq(dev, common->rx_chns.irq, + am65_cpsw_nuss_rx_irq, + IRQF_TRIGGER_HIGH, dev_name(dev), common); + if (ret) { + dev_err(dev, "failure requesting rx irq %u, %d\n", + common->rx_chns.irq, ret); + return ret; + } + ret = am65_cpsw_nuss_register_devlink(common); if (ret) return ret; @@ -2744,6 +2694,7 @@ static int am65_cpsw_nuss_probe(struct platform_device *pdev) if (common->port_num < 1 || common->port_num > AM65_CPSW_MAX_PORTS) return -ENOENT; + common->rx_flow_id_base = -1; init_completion(&common->tdown_complete); common->tx_ch_num = 1; common->pf_p0_rx_ptype_rrobin = false; @@ -2785,6 +2736,14 @@ static int am65_cpsw_nuss_probe(struct platform_device *pdev) am65_cpsw_nuss_get_ver(common); + /* init tx channels */ + ret = am65_cpsw_nuss_init_tx_chns(common); + if (ret) + goto err_of_clear; + ret = am65_cpsw_nuss_init_rx_chns(common); + if (ret) + goto err_of_clear; + ret = am65_cpsw_nuss_init_host_p(common); if (ret) goto err_of_clear; @@ -2869,80 +2828,10 @@ static int am65_cpsw_nuss_remove(struct platform_device *pdev) return 0; } -#ifdef CONFIG_PM_SLEEP -static int am65_cpsw_nuss_suspend(struct device *dev) -{ - struct am65_cpsw_common *common = dev_get_drvdata(dev); - struct am65_cpsw_port *port; - struct net_device *ndev; - int i, ret; - - for (i = 0; i < common->port_num; i++) { - port = &common->ports[i]; - ndev = port->ndev; - - if (!ndev) - continue; - - netif_device_detach(ndev); - if (netif_running(ndev)) { - rtnl_lock(); - ret = am65_cpsw_nuss_ndo_slave_stop(ndev); - rtnl_unlock(); - if (ret < 0) { - netdev_err(ndev, "failed to stop: %d", ret); - return ret; - } - } - } - - am65_cpts_suspend(common->cpts); - - return 0; -} - -static int am65_cpsw_nuss_resume(struct device *dev) -{ - struct am65_cpsw_common *common = dev_get_drvdata(dev); - struct am65_cpsw_port *port; - struct net_device *ndev; - int i, ret; - - am65_cpts_resume(common->cpts); - - for (i = 0; i < common->port_num; i++) { - port = &common->ports[i]; - ndev = port->ndev; - - if (!ndev) - continue; - - if (netif_running(ndev)) { - rtnl_lock(); - ret = am65_cpsw_nuss_ndo_slave_open(ndev); - rtnl_unlock(); - if (ret < 0) { - netdev_err(ndev, "failed to start: %d", ret); - return ret; - } - } - - netif_device_attach(ndev); - } - - return 0; -} -#endif /* CONFIG_PM_SLEEP */ - -static const struct dev_pm_ops am65_cpsw_nuss_dev_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(am65_cpsw_nuss_suspend, am65_cpsw_nuss_resume) -}; - static struct platform_driver am65_cpsw_nuss_driver = { .driver = { .name = AM65_CPSW_DRV_NAME, .of_match_table = am65_cpsw_nuss_of_mtable, - .pm = &am65_cpsw_nuss_dev_pm_ops, }, .probe = am65_cpsw_nuss_probe, .remove = am65_cpsw_nuss_remove, From 24bc19b05f1fc29e55ec73df5345fe3571c48b15 Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Tue, 6 Dec 2022 11:44:17 +0200 Subject: [PATCH 1687/2002] net: ethernet: ti: am65-cpsw: Add suspend/resume support Add PM handlers for System suspend/resume. As DMA driver doesn't yet support suspend/resume we free up the DMA channels at suspend and acquire and initialize them at resume. In this revised approach we do not free the TX/RX IRQs at am65_cpsw_nuss_common_stop() as it causes problems. We will now free them only on .suspend() as we need to release the DMA channels (as DMA looses context) and re-acquiring them on .resume() may not necessarily give us the same IRQs. To make this easier: - introduce am65_cpsw_nuss_remove_rx_chns() which is similar to am65_cpsw_nuss_remove_tx_chns(). These will be invoked in pm.suspend() to release the DMA channels and free up the IRQs. - move napi_add() and request_irq() calls to am65_cpsw_nuss_init_rx/tx_chns() so we can invoke them in pm.resume() to acquire the DMA channels and IRQs. As CPTS looses contect during suspend/resume, invoke the necessary CPTS suspend/resume helpers. ALE_CLEAR command is issued in cpsw_ale_start() so no need to issue it before the call to cpsw_ale_start(). Signed-off-by: Roger Quadros Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/ti/am65-cpsw-nuss.c | 222 ++++++++++++++++++----- 1 file changed, 173 insertions(+), 49 deletions(-) diff --git a/drivers/net/ethernet/ti/am65-cpsw-nuss.c b/drivers/net/ethernet/ti/am65-cpsw-nuss.c index c682103ce946..797da6294270 100644 --- a/drivers/net/ethernet/ti/am65-cpsw-nuss.c +++ b/drivers/net/ethernet/ti/am65-cpsw-nuss.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -555,11 +556,26 @@ static int am65_cpsw_nuss_ndo_slave_open(struct net_device *ndev) struct am65_cpsw_common *common = am65_ndev_to_common(ndev); struct am65_cpsw_port *port = am65_ndev_to_port(ndev); int ret, i; + u32 reg; ret = pm_runtime_resume_and_get(common->dev); if (ret < 0) return ret; + /* Idle MAC port */ + cpsw_sl_ctl_set(port->slave.mac_sl, CPSW_SL_CTL_CMD_IDLE); + cpsw_sl_wait_for_idle(port->slave.mac_sl, 100); + cpsw_sl_ctl_reset(port->slave.mac_sl); + + /* soft reset MAC */ + cpsw_sl_reg_write(port->slave.mac_sl, CPSW_SL_SOFT_RESET, 1); + mdelay(1); + reg = cpsw_sl_reg_read(port->slave.mac_sl, CPSW_SL_SOFT_RESET); + if (reg) { + dev_err(common->dev, "soft RESET didn't complete\n"); + return -ETIMEDOUT; + } + /* Notify the stack of the actual queue counts. */ ret = netif_set_real_num_tx_queues(ndev, common->tx_ch_num); if (ret) { @@ -1533,6 +1549,32 @@ void am65_cpsw_nuss_remove_tx_chns(struct am65_cpsw_common *common) } } +static int am65_cpsw_nuss_ndev_add_tx_napi(struct am65_cpsw_common *common) +{ + struct device *dev = common->dev; + int i, ret = 0; + + for (i = 0; i < common->tx_ch_num; i++) { + struct am65_cpsw_tx_chn *tx_chn = &common->tx_chns[i]; + + netif_napi_add_tx(common->dma_ndev, &tx_chn->napi_tx, + am65_cpsw_nuss_tx_poll); + + ret = devm_request_irq(dev, tx_chn->irq, + am65_cpsw_nuss_tx_irq, + IRQF_TRIGGER_HIGH, + tx_chn->tx_chn_name, tx_chn); + if (ret) { + dev_err(dev, "failure requesting tx%u irq %u, %d\n", + tx_chn->id, tx_chn->irq, ret); + goto err; + } + } + +err: + return ret; +} + static int am65_cpsw_nuss_init_tx_chns(struct am65_cpsw_common *common) { u32 max_desc_num = ALIGN(AM65_CPSW_MAX_TX_DESC, MAX_SKB_FRAGS); @@ -1599,6 +1641,12 @@ static int am65_cpsw_nuss_init_tx_chns(struct am65_cpsw_common *common) dev_name(dev), tx_chn->id); } + ret = am65_cpsw_nuss_ndev_add_tx_napi(common); + if (ret) { + dev_err(dev, "Failed to add tx NAPI %d\n", ret); + goto err; + } + err: i = devm_add_action(dev, am65_cpsw_nuss_free_tx_chns, common); if (i) { @@ -1623,6 +1671,29 @@ static void am65_cpsw_nuss_free_rx_chns(void *data) k3_udma_glue_release_rx_chn(rx_chn->rx_chn); } +static void am65_cpsw_nuss_remove_rx_chns(void *data) +{ + struct am65_cpsw_common *common = data; + struct am65_cpsw_rx_chn *rx_chn; + struct device *dev = common->dev; + + rx_chn = &common->rx_chns; + devm_remove_action(dev, am65_cpsw_nuss_free_rx_chns, common); + + if (!(rx_chn->irq < 0)) + devm_free_irq(dev, rx_chn->irq, common); + + netif_napi_del(&common->napi_rx); + + if (!IS_ERR_OR_NULL(rx_chn->desc_pool)) + k3_cppi_desc_pool_destroy(rx_chn->desc_pool); + + if (!IS_ERR_OR_NULL(rx_chn->rx_chn)) + k3_udma_glue_release_rx_chn(rx_chn->rx_chn); + + common->rx_flow_id_base = -1; +} + static int am65_cpsw_nuss_init_rx_chns(struct am65_cpsw_common *common) { struct am65_cpsw_rx_chn *rx_chn = &common->rx_chns; @@ -1710,6 +1781,18 @@ static int am65_cpsw_nuss_init_rx_chns(struct am65_cpsw_common *common) } } + netif_napi_add(common->dma_ndev, &common->napi_rx, + am65_cpsw_nuss_rx_poll); + + ret = devm_request_irq(dev, rx_chn->irq, + am65_cpsw_nuss_rx_irq, + IRQF_TRIGGER_HIGH, dev_name(dev), common); + if (ret) { + dev_err(dev, "failure requesting rx irq %u, %d\n", + rx_chn->irq, ret); + goto err; + } + err: i = devm_add_action(dev, am65_cpsw_nuss_free_rx_chns, common); if (i) { @@ -1981,6 +2064,7 @@ am65_cpsw_nuss_init_port_ndev(struct am65_cpsw_common *common, u32 port_idx) port->slave.phylink_config.dev = &port->ndev->dev; port->slave.phylink_config.type = PHYLINK_NETDEV; port->slave.phylink_config.mac_capabilities = MAC_SYM_PAUSE | MAC_10 | MAC_100 | MAC_1000FD; + port->slave.phylink_config.mac_managed_pm = true; /* MAC does PM */ if (phy_interface_mode_is_rgmii(port->slave.phy_if)) { phy_interface_set_rgmii(port->slave.phylink_config.supported_interfaces); @@ -2034,35 +2118,6 @@ static int am65_cpsw_nuss_init_ndevs(struct am65_cpsw_common *common) return ret; } - netif_napi_add(common->dma_ndev, &common->napi_rx, - am65_cpsw_nuss_rx_poll); - - return ret; -} - -static int am65_cpsw_nuss_ndev_add_tx_napi(struct am65_cpsw_common *common) -{ - struct device *dev = common->dev; - int i, ret = 0; - - for (i = 0; i < common->tx_ch_num; i++) { - struct am65_cpsw_tx_chn *tx_chn = &common->tx_chns[i]; - - netif_napi_add_tx(common->dma_ndev, &tx_chn->napi_tx, - am65_cpsw_nuss_tx_poll); - - ret = devm_request_irq(dev, tx_chn->irq, - am65_cpsw_nuss_tx_irq, - IRQF_TRIGGER_HIGH, - tx_chn->tx_chn_name, tx_chn); - if (ret) { - dev_err(dev, "failure requesting tx%u irq %u, %d\n", - tx_chn->id, tx_chn->irq, ret); - goto err; - } - } - -err: return ret; } @@ -2528,18 +2583,13 @@ static int am65_cpsw_nuss_register_ndevs(struct am65_cpsw_common *common) struct am65_cpsw_port *port; int ret = 0, i; - ret = am65_cpsw_nuss_ndev_add_tx_napi(common); + /* init tx channels */ + ret = am65_cpsw_nuss_init_tx_chns(common); if (ret) return ret; - - ret = devm_request_irq(dev, common->rx_chns.irq, - am65_cpsw_nuss_rx_irq, - IRQF_TRIGGER_HIGH, dev_name(dev), common); - if (ret) { - dev_err(dev, "failure requesting rx irq %u, %d\n", - common->rx_chns.irq, ret); + ret = am65_cpsw_nuss_init_rx_chns(common); + if (ret) return ret; - } ret = am65_cpsw_nuss_register_devlink(common); if (ret) @@ -2584,10 +2634,8 @@ int am65_cpsw_nuss_update_tx_chns(struct am65_cpsw_common *common, int num_tx) common->tx_ch_num = num_tx; ret = am65_cpsw_nuss_init_tx_chns(common); - if (ret) - return ret; - return am65_cpsw_nuss_ndev_add_tx_napi(common); + return ret; } struct am65_cpsw_soc_pdata { @@ -2736,14 +2784,6 @@ static int am65_cpsw_nuss_probe(struct platform_device *pdev) am65_cpsw_nuss_get_ver(common); - /* init tx channels */ - ret = am65_cpsw_nuss_init_tx_chns(common); - if (ret) - goto err_of_clear; - ret = am65_cpsw_nuss_init_rx_chns(common); - if (ret) - goto err_of_clear; - ret = am65_cpsw_nuss_init_host_p(common); if (ret) goto err_of_clear; @@ -2828,10 +2868,94 @@ static int am65_cpsw_nuss_remove(struct platform_device *pdev) return 0; } +#ifdef CONFIG_PM_SLEEP +static int am65_cpsw_nuss_suspend(struct device *dev) +{ + struct am65_cpsw_common *common = dev_get_drvdata(dev); + struct am65_cpsw_port *port; + struct net_device *ndev; + int i, ret; + + for (i = 0; i < common->port_num; i++) { + port = &common->ports[i]; + ndev = port->ndev; + + if (!ndev) + continue; + + netif_device_detach(ndev); + if (netif_running(ndev)) { + rtnl_lock(); + ret = am65_cpsw_nuss_ndo_slave_stop(ndev); + rtnl_unlock(); + if (ret < 0) { + netdev_err(ndev, "failed to stop: %d", ret); + return ret; + } + } + } + + am65_cpts_suspend(common->cpts); + + am65_cpsw_nuss_remove_rx_chns(common); + am65_cpsw_nuss_remove_tx_chns(common); + + return 0; +} + +static int am65_cpsw_nuss_resume(struct device *dev) +{ + struct am65_cpsw_common *common = dev_get_drvdata(dev); + struct am65_cpsw_port *port; + struct net_device *ndev; + int i, ret; + + ret = am65_cpsw_nuss_init_tx_chns(common); + if (ret) + return ret; + ret = am65_cpsw_nuss_init_rx_chns(common); + if (ret) + return ret; + + /* If RX IRQ was disabled before suspend, keep it disabled */ + if (common->rx_irq_disabled) + disable_irq(common->rx_chns.irq); + + am65_cpts_resume(common->cpts); + + for (i = 0; i < common->port_num; i++) { + port = &common->ports[i]; + ndev = port->ndev; + + if (!ndev) + continue; + + if (netif_running(ndev)) { + rtnl_lock(); + ret = am65_cpsw_nuss_ndo_slave_open(ndev); + rtnl_unlock(); + if (ret < 0) { + netdev_err(ndev, "failed to start: %d", ret); + return ret; + } + } + + netif_device_attach(ndev); + } + + return 0; +} +#endif /* CONFIG_PM_SLEEP */ + +static const struct dev_pm_ops am65_cpsw_nuss_dev_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(am65_cpsw_nuss_suspend, am65_cpsw_nuss_resume) +}; + static struct platform_driver am65_cpsw_nuss_driver = { .driver = { .name = AM65_CPSW_DRV_NAME, .of_match_table = am65_cpsw_nuss_of_mtable, + .pm = &am65_cpsw_nuss_dev_pm_ops, }, .probe = am65_cpsw_nuss_probe, .remove = am65_cpsw_nuss_remove, From 1581cd8b11748d9ff1d9deedd9dc5c0c1ce9029b Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Tue, 6 Dec 2022 11:44:18 +0200 Subject: [PATCH 1688/2002] net: ethernet: ti: am65-cpsw: retain PORT_VLAN_REG after suspend/resume During suspend resume the context of PORT_VLAN_REG is lost so save it during suspend and restore it during resume for host port and slave ports. Signed-off-by: Roger Quadros Signed-off-by: David S. Miller Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/ti/am65-cpsw-nuss.c | 7 +++++++ drivers/net/ethernet/ti/am65-cpsw-nuss.h | 4 ++++ 2 files changed, 11 insertions(+) diff --git a/drivers/net/ethernet/ti/am65-cpsw-nuss.c b/drivers/net/ethernet/ti/am65-cpsw-nuss.c index 797da6294270..61a3332f33ad 100644 --- a/drivers/net/ethernet/ti/am65-cpsw-nuss.c +++ b/drivers/net/ethernet/ti/am65-cpsw-nuss.c @@ -2872,10 +2872,12 @@ static int am65_cpsw_nuss_remove(struct platform_device *pdev) static int am65_cpsw_nuss_suspend(struct device *dev) { struct am65_cpsw_common *common = dev_get_drvdata(dev); + struct am65_cpsw_host *host_p = am65_common_get_host(common); struct am65_cpsw_port *port; struct net_device *ndev; int i, ret; + host_p->vid_context = readl(host_p->port_base + AM65_CPSW_PORT_VLAN_REG_OFFSET); for (i = 0; i < common->port_num; i++) { port = &common->ports[i]; ndev = port->ndev; @@ -2883,6 +2885,7 @@ static int am65_cpsw_nuss_suspend(struct device *dev) if (!ndev) continue; + port->vid_context = readl(port->port_base + AM65_CPSW_PORT_VLAN_REG_OFFSET); netif_device_detach(ndev); if (netif_running(ndev)) { rtnl_lock(); @@ -2909,6 +2912,7 @@ static int am65_cpsw_nuss_resume(struct device *dev) struct am65_cpsw_port *port; struct net_device *ndev; int i, ret; + struct am65_cpsw_host *host_p = am65_common_get_host(common); ret = am65_cpsw_nuss_init_tx_chns(common); if (ret) @@ -2941,8 +2945,11 @@ static int am65_cpsw_nuss_resume(struct device *dev) } netif_device_attach(ndev); + writel(port->vid_context, port->port_base + AM65_CPSW_PORT_VLAN_REG_OFFSET); } + writel(host_p->vid_context, host_p->port_base + AM65_CPSW_PORT_VLAN_REG_OFFSET); + return 0; } #endif /* CONFIG_PM_SLEEP */ diff --git a/drivers/net/ethernet/ti/am65-cpsw-nuss.h b/drivers/net/ethernet/ti/am65-cpsw-nuss.h index 2c9850fdfcb6..e95cc37a7286 100644 --- a/drivers/net/ethernet/ti/am65-cpsw-nuss.h +++ b/drivers/net/ethernet/ti/am65-cpsw-nuss.h @@ -55,12 +55,16 @@ struct am65_cpsw_port { bool rx_ts_enabled; struct am65_cpsw_qos qos; struct devlink_port devlink_port; + /* Only for suspend resume context */ + u32 vid_context; }; struct am65_cpsw_host { struct am65_cpsw_common *common; void __iomem *port_base; void __iomem *stat_base; + /* Only for suspend resume context */ + u32 vid_context; }; struct am65_cpsw_tx_chn { From 020b232f79e97ad06d5edc67a2dc34fe5bc271ef Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Tue, 6 Dec 2022 11:44:19 +0200 Subject: [PATCH 1689/2002] net: ethernet: ti: am65-cpsw: Fix hardware switch mode on suspend/resume On low power during system suspend the ALE table context is lost. Save the ALE context before suspend and restore it after resume. Signed-off-by: Roger Quadros Signed-off-by: David S. Miller Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/ti/am65-cpsw-nuss.c | 7 +++++++ drivers/net/ethernet/ti/am65-cpsw-nuss.h | 2 ++ 2 files changed, 9 insertions(+) diff --git a/drivers/net/ethernet/ti/am65-cpsw-nuss.c b/drivers/net/ethernet/ti/am65-cpsw-nuss.c index 61a3332f33ad..a8a6f0b48906 100644 --- a/drivers/net/ethernet/ti/am65-cpsw-nuss.c +++ b/drivers/net/ethernet/ti/am65-cpsw-nuss.c @@ -2712,6 +2712,7 @@ static int am65_cpsw_nuss_probe(struct platform_device *pdev) struct clk *clk; u64 id_temp; int ret, i; + int ale_entries; common = devm_kzalloc(dev, sizeof(struct am65_cpsw_common), GFP_KERNEL); if (!common) @@ -2807,6 +2808,10 @@ static int am65_cpsw_nuss_probe(struct platform_device *pdev) goto err_of_clear; } + ale_entries = common->ale->params.ale_entries; + common->ale_context = devm_kzalloc(dev, + ale_entries * ALE_ENTRY_WORDS * sizeof(u32), + GFP_KERNEL); ret = am65_cpsw_init_cpts(common); if (ret) goto err_of_clear; @@ -2877,6 +2882,7 @@ static int am65_cpsw_nuss_suspend(struct device *dev) struct net_device *ndev; int i, ret; + cpsw_ale_dump(common->ale, common->ale_context); host_p->vid_context = readl(host_p->port_base + AM65_CPSW_PORT_VLAN_REG_OFFSET); for (i = 0; i < common->port_num; i++) { port = &common->ports[i]; @@ -2949,6 +2955,7 @@ static int am65_cpsw_nuss_resume(struct device *dev) } writel(host_p->vid_context, host_p->port_base + AM65_CPSW_PORT_VLAN_REG_OFFSET); + cpsw_ale_restore(common->ale, common->ale_context); return 0; } diff --git a/drivers/net/ethernet/ti/am65-cpsw-nuss.h b/drivers/net/ethernet/ti/am65-cpsw-nuss.h index e95cc37a7286..4b75620f8d28 100644 --- a/drivers/net/ethernet/ti/am65-cpsw-nuss.h +++ b/drivers/net/ethernet/ti/am65-cpsw-nuss.h @@ -149,6 +149,8 @@ struct am65_cpsw_common { struct net_device *hw_bridge_dev; struct notifier_block am65_cpsw_netdevice_nb; unsigned char switch_id[MAX_PHYS_ITEM_ID_LEN]; + /* only for suspend/resume context restore */ + u32 *ale_context; }; struct am65_cpsw_ndev_stats { From 8c17295bd4991b6d275248ff7f987f54dd5eb0e6 Mon Sep 17 00:00:00 2001 From: Leon Romanovsky Date: Fri, 2 Dec 2022 22:14:45 +0200 Subject: [PATCH 1690/2002] net/mlx5e: Create IPsec policy offload tables Add empty table to be used for IPsec policy offload. Signed-off-by: Leon Romanovsky Signed-off-by: Steffen Klassert --- .../net/ethernet/mellanox/mlx5/core/en/fs.h | 3 +- .../mellanox/mlx5/core/en_accel/ipsec_fs.c | 53 ++++++++++++++++++- .../net/ethernet/mellanox/mlx5/core/fs_core.c | 6 +-- 3 files changed, 56 insertions(+), 6 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/fs.h b/drivers/net/ethernet/mellanox/mlx5/core/en/fs.h index bf2741eb7f9b..379c6dc9a3be 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/fs.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/fs.h @@ -84,7 +84,8 @@ enum { MLX5E_ARFS_FT_LEVEL = MLX5E_INNER_TTC_FT_LEVEL + 1, #endif #ifdef CONFIG_MLX5_EN_IPSEC - MLX5E_ACCEL_FS_ESP_FT_LEVEL = MLX5E_INNER_TTC_FT_LEVEL + 1, + MLX5E_ACCEL_FS_POL_FT_LEVEL = MLX5E_INNER_TTC_FT_LEVEL + 1, + MLX5E_ACCEL_FS_ESP_FT_LEVEL, MLX5E_ACCEL_FS_ESP_FT_ERR_LEVEL, #endif }; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_fs.c b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_fs.c index 5bc6f9d1f5a6..a3c7d0f142c0 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_fs.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_fs.c @@ -11,6 +11,7 @@ struct mlx5e_ipsec_ft { struct mutex mutex; /* Protect changes to this struct */ + struct mlx5_flow_table *pol; struct mlx5_flow_table *sa; struct mlx5_flow_table *status; u32 refcnt; @@ -23,12 +24,14 @@ struct mlx5e_ipsec_miss { struct mlx5e_ipsec_rx { struct mlx5e_ipsec_ft ft; + struct mlx5e_ipsec_miss pol; struct mlx5e_ipsec_miss sa; struct mlx5e_ipsec_rule status; }; struct mlx5e_ipsec_tx { struct mlx5e_ipsec_ft ft; + struct mlx5e_ipsec_miss pol; struct mlx5_flow_namespace *ns; }; @@ -157,6 +160,10 @@ out: static void rx_destroy(struct mlx5_core_dev *mdev, struct mlx5e_ipsec_rx *rx) { + mlx5_del_flow_rules(rx->pol.rule); + mlx5_destroy_flow_group(rx->pol.group); + mlx5_destroy_flow_table(rx->ft.pol); + mlx5_del_flow_rules(rx->sa.rule); mlx5_destroy_flow_group(rx->sa.group); mlx5_destroy_flow_table(rx->ft.sa); @@ -200,8 +207,27 @@ static int rx_create(struct mlx5_core_dev *mdev, struct mlx5e_ipsec *ipsec, if (err) goto err_fs; + ft = ipsec_ft_create(ns, MLX5E_ACCEL_FS_POL_FT_LEVEL, MLX5E_NIC_PRIO, + 1); + if (IS_ERR(ft)) { + err = PTR_ERR(ft); + goto err_pol_ft; + } + rx->ft.pol = ft; + memset(&dest, 0x00, sizeof(dest)); + dest.type = MLX5_FLOW_DESTINATION_TYPE_FLOW_TABLE; + dest.ft = rx->ft.sa; + err = ipsec_miss_create(mdev, rx->ft.pol, &rx->pol, &dest); + if (err) + goto err_pol_miss; + return 0; +err_pol_miss: + mlx5_destroy_flow_table(rx->ft.pol); +err_pol_ft: + mlx5_del_flow_rules(rx->sa.rule); + mlx5_destroy_flow_group(rx->sa.group); err_fs: mlx5_destroy_flow_table(rx->ft.sa); err_fs_ft: @@ -236,7 +262,7 @@ static struct mlx5e_ipsec_rx *rx_ft_get(struct mlx5_core_dev *mdev, /* connect */ dest.type = MLX5_FLOW_DESTINATION_TYPE_FLOW_TABLE; - dest.ft = rx->ft.sa; + dest.ft = rx->ft.pol; mlx5_ttc_fwd_dest(ttc, family2tt(family), &dest); skip: @@ -277,14 +303,34 @@ out: /* IPsec TX flow steering */ static int tx_create(struct mlx5_core_dev *mdev, struct mlx5e_ipsec_tx *tx) { + struct mlx5_flow_destination dest = {}; struct mlx5_flow_table *ft; + int err; - ft = ipsec_ft_create(tx->ns, 0, 0, 1); + ft = ipsec_ft_create(tx->ns, 1, 0, 1); if (IS_ERR(ft)) return PTR_ERR(ft); tx->ft.sa = ft; + + ft = ipsec_ft_create(tx->ns, 0, 0, 1); + if (IS_ERR(ft)) { + err = PTR_ERR(ft); + goto err_pol_ft; + } + tx->ft.pol = ft; + dest.type = MLX5_FLOW_DESTINATION_TYPE_FLOW_TABLE; + dest.ft = tx->ft.sa; + err = ipsec_miss_create(mdev, tx->ft.pol, &tx->pol, &dest); + if (err) + goto err_pol_miss; return 0; + +err_pol_miss: + mlx5_destroy_flow_table(tx->ft.pol); +err_pol_ft: + mlx5_destroy_flow_table(tx->ft.sa); + return err; } static struct mlx5e_ipsec_tx *tx_ft_get(struct mlx5_core_dev *mdev, @@ -318,6 +364,9 @@ static void tx_ft_put(struct mlx5e_ipsec *ipsec) if (tx->ft.refcnt) goto out; + mlx5_del_flow_rules(tx->pol.rule); + mlx5_destroy_flow_group(tx->pol.group); + mlx5_destroy_flow_table(tx->ft.pol); mlx5_destroy_flow_table(tx->ft.sa); out: mutex_unlock(&tx->ft.mutex); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c index d53749248fa0..9995307d374b 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c @@ -111,8 +111,8 @@ #define ETHTOOL_PRIO_NUM_LEVELS 1 #define ETHTOOL_NUM_PRIOS 11 #define ETHTOOL_MIN_LEVEL (KERNEL_MIN_LEVEL + ETHTOOL_NUM_PRIOS) -/* Promiscuous, Vlan, mac, ttc, inner ttc, {UDP/ANY/aRFS/accel/{esp, esp_err}} */ -#define KERNEL_NIC_PRIO_NUM_LEVELS 7 +/* Promiscuous, Vlan, mac, ttc, inner ttc, {UDP/ANY/aRFS/accel/{esp, esp_err}}, IPsec policy */ +#define KERNEL_NIC_PRIO_NUM_LEVELS 8 #define KERNEL_NIC_NUM_PRIOS 1 /* One more level for tc */ #define KERNEL_MIN_LEVEL (KERNEL_NIC_PRIO_NUM_LEVELS + 1) @@ -133,7 +133,7 @@ #define LAG_MIN_LEVEL (OFFLOADS_MIN_LEVEL + KERNEL_RX_MACSEC_MIN_LEVEL + 1) #define KERNEL_TX_IPSEC_NUM_PRIOS 1 -#define KERNEL_TX_IPSEC_NUM_LEVELS 1 +#define KERNEL_TX_IPSEC_NUM_LEVELS 2 #define KERNEL_TX_IPSEC_MIN_LEVEL (KERNEL_TX_IPSEC_NUM_LEVELS) #define KERNEL_TX_MACSEC_NUM_PRIOS 1 From a5b8ca9471d394459152a2a7f8f5f8227ae3e3c0 Mon Sep 17 00:00:00 2001 From: Leon Romanovsky Date: Fri, 2 Dec 2022 22:14:46 +0200 Subject: [PATCH 1691/2002] net/mlx5e: Add XFRM policy offload logic Implement mlx5 flow steering logic and mlx5 IPsec code support XFRM policy offload. Reviewed-by: Raed Salem Reviewed-by: Saeed Mahameed Signed-off-by: Leon Romanovsky Signed-off-by: Steffen Klassert --- .../mellanox/mlx5/core/en_accel/ipsec.c | 118 +++++++++++++- .../mellanox/mlx5/core/en_accel/ipsec.h | 32 ++++ .../mellanox/mlx5/core/en_accel/ipsec_fs.c | 146 ++++++++++++++++++ 3 files changed, 295 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.c b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.c index c5bccc0df60d..b22a31e178cf 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.c @@ -45,6 +45,11 @@ static struct mlx5e_ipsec_sa_entry *to_ipsec_sa_entry(struct xfrm_state *x) return (struct mlx5e_ipsec_sa_entry *)x->xso.offload_handle; } +static struct mlx5e_ipsec_pol_entry *to_ipsec_pol_entry(struct xfrm_policy *x) +{ + return (struct mlx5e_ipsec_pol_entry *)x->xdo.offload_handle; +} + struct xfrm_state *mlx5e_ipsec_sadb_rx_lookup(struct mlx5e_ipsec *ipsec, unsigned int handle) { @@ -446,6 +451,101 @@ static void mlx5e_xfrm_advance_esn_state(struct xfrm_state *x) queue_work(sa_entry->ipsec->wq, &modify_work->work); } +static int mlx5e_xfrm_validate_policy(struct xfrm_policy *x) +{ + struct net_device *netdev = x->xdo.real_dev; + + if (x->type != XFRM_POLICY_TYPE_MAIN) { + netdev_info(netdev, "Cannot offload non-main policy types\n"); + return -EINVAL; + } + + /* Please pay attention that we support only one template */ + if (x->xfrm_nr > 1) { + netdev_info(netdev, "Cannot offload more than one template\n"); + return -EINVAL; + } + + if (x->xdo.dir != XFRM_DEV_OFFLOAD_IN && + x->xdo.dir != XFRM_DEV_OFFLOAD_OUT) { + netdev_info(netdev, "Cannot offload forward policy\n"); + return -EINVAL; + } + + if (!x->xfrm_vec[0].reqid) { + netdev_info(netdev, "Cannot offload policy without reqid\n"); + return -EINVAL; + } + + if (x->xdo.type != XFRM_DEV_OFFLOAD_PACKET) { + netdev_info(netdev, "Unsupported xfrm offload type\n"); + return -EINVAL; + } + + return 0; +} + +static void +mlx5e_ipsec_build_accel_pol_attrs(struct mlx5e_ipsec_pol_entry *pol_entry, + struct mlx5_accel_pol_xfrm_attrs *attrs) +{ + struct xfrm_policy *x = pol_entry->x; + struct xfrm_selector *sel; + + sel = &x->selector; + memset(attrs, 0, sizeof(*attrs)); + + memcpy(&attrs->saddr, sel->saddr.a6, sizeof(attrs->saddr)); + memcpy(&attrs->daddr, sel->daddr.a6, sizeof(attrs->daddr)); + attrs->family = sel->family; + attrs->dir = x->xdo.dir; + attrs->action = x->action; + attrs->type = XFRM_DEV_OFFLOAD_PACKET; +} + +static int mlx5e_xfrm_add_policy(struct xfrm_policy *x) +{ + struct net_device *netdev = x->xdo.real_dev; + struct mlx5e_ipsec_pol_entry *pol_entry; + struct mlx5e_priv *priv; + int err; + + priv = netdev_priv(netdev); + if (!priv->ipsec) + return -EOPNOTSUPP; + + err = mlx5e_xfrm_validate_policy(x); + if (err) + return err; + + pol_entry = kzalloc(sizeof(*pol_entry), GFP_KERNEL); + if (!pol_entry) + return -ENOMEM; + + pol_entry->x = x; + pol_entry->ipsec = priv->ipsec; + + mlx5e_ipsec_build_accel_pol_attrs(pol_entry, &pol_entry->attrs); + err = mlx5e_accel_ipsec_fs_add_pol(pol_entry); + if (err) + goto err_fs; + + x->xdo.offload_handle = (unsigned long)pol_entry; + return 0; + +err_fs: + kfree(pol_entry); + return err; +} + +static void mlx5e_xfrm_free_policy(struct xfrm_policy *x) +{ + struct mlx5e_ipsec_pol_entry *pol_entry = to_ipsec_pol_entry(x); + + mlx5e_accel_ipsec_fs_del_pol(pol_entry); + kfree(pol_entry); +} + static const struct xfrmdev_ops mlx5e_ipsec_xfrmdev_ops = { .xdo_dev_state_add = mlx5e_xfrm_add_state, .xdo_dev_state_delete = mlx5e_xfrm_del_state, @@ -454,6 +554,17 @@ static const struct xfrmdev_ops mlx5e_ipsec_xfrmdev_ops = { .xdo_dev_state_advance_esn = mlx5e_xfrm_advance_esn_state, }; +static const struct xfrmdev_ops mlx5e_ipsec_packet_xfrmdev_ops = { + .xdo_dev_state_add = mlx5e_xfrm_add_state, + .xdo_dev_state_delete = mlx5e_xfrm_del_state, + .xdo_dev_state_free = mlx5e_xfrm_free_state, + .xdo_dev_offload_ok = mlx5e_ipsec_offload_ok, + .xdo_dev_state_advance_esn = mlx5e_xfrm_advance_esn_state, + + .xdo_dev_policy_add = mlx5e_xfrm_add_policy, + .xdo_dev_policy_free = mlx5e_xfrm_free_policy, +}; + void mlx5e_ipsec_build_netdev(struct mlx5e_priv *priv) { struct mlx5_core_dev *mdev = priv->mdev; @@ -463,7 +574,12 @@ void mlx5e_ipsec_build_netdev(struct mlx5e_priv *priv) return; mlx5_core_info(mdev, "mlx5e: IPSec ESP acceleration enabled\n"); - netdev->xfrmdev_ops = &mlx5e_ipsec_xfrmdev_ops; + + if (mlx5_ipsec_device_caps(mdev) & MLX5_IPSEC_CAP_PACKET_OFFLOAD) + netdev->xfrmdev_ops = &mlx5e_ipsec_packet_xfrmdev_ops; + else + netdev->xfrmdev_ops = &mlx5e_ipsec_xfrmdev_ops; + netdev->features |= NETIF_F_HW_ESP; netdev->hw_enc_features |= NETIF_F_HW_ESP; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.h b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.h index 990378d52fd4..8d1a0d053eb4 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.h @@ -149,6 +149,30 @@ struct mlx5e_ipsec_sa_entry { struct mlx5e_ipsec_modify_state_work modify_work; }; +struct mlx5_accel_pol_xfrm_attrs { + union { + __be32 a4; + __be32 a6[4]; + } saddr; + + union { + __be32 a4; + __be32 a6[4]; + } daddr; + + u8 family; + u8 action; + u8 type : 2; + u8 dir : 2; +}; + +struct mlx5e_ipsec_pol_entry { + struct xfrm_policy *x; + struct mlx5e_ipsec *ipsec; + struct mlx5_flow_handle *rule; + struct mlx5_accel_pol_xfrm_attrs attrs; +}; + void mlx5e_ipsec_init(struct mlx5e_priv *priv); void mlx5e_ipsec_cleanup(struct mlx5e_priv *priv); void mlx5e_ipsec_build_netdev(struct mlx5e_priv *priv); @@ -160,6 +184,8 @@ void mlx5e_accel_ipsec_fs_cleanup(struct mlx5e_ipsec *ipsec); int mlx5e_accel_ipsec_fs_init(struct mlx5e_ipsec *ipsec); int mlx5e_accel_ipsec_fs_add_rule(struct mlx5e_ipsec_sa_entry *sa_entry); void mlx5e_accel_ipsec_fs_del_rule(struct mlx5e_ipsec_sa_entry *sa_entry); +int mlx5e_accel_ipsec_fs_add_pol(struct mlx5e_ipsec_pol_entry *pol_entry); +void mlx5e_accel_ipsec_fs_del_pol(struct mlx5e_ipsec_pol_entry *pol_entry); int mlx5_ipsec_create_sa_ctx(struct mlx5e_ipsec_sa_entry *sa_entry); void mlx5_ipsec_free_sa_ctx(struct mlx5e_ipsec_sa_entry *sa_entry); @@ -177,6 +203,12 @@ mlx5e_ipsec_sa2dev(struct mlx5e_ipsec_sa_entry *sa_entry) { return sa_entry->ipsec->mdev; } + +static inline struct mlx5_core_dev * +mlx5e_ipsec_pol2dev(struct mlx5e_ipsec_pol_entry *pol_entry) +{ + return pol_entry->ipsec->mdev; +} #else static inline void mlx5e_ipsec_init(struct mlx5e_priv *priv) { diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_fs.c b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_fs.c index a3c7d0f142c0..27f866a158de 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_fs.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_fs.c @@ -597,6 +597,130 @@ out: return err; } +static int tx_add_policy(struct mlx5e_ipsec_pol_entry *pol_entry) +{ + struct mlx5_accel_pol_xfrm_attrs *attrs = &pol_entry->attrs; + struct mlx5_core_dev *mdev = mlx5e_ipsec_pol2dev(pol_entry); + struct mlx5_flow_destination dest = {}; + struct mlx5_flow_act flow_act = {}; + struct mlx5_flow_handle *rule; + struct mlx5_flow_spec *spec; + struct mlx5e_ipsec_tx *tx; + int err; + + tx = tx_ft_get(mdev, pol_entry->ipsec); + if (IS_ERR(tx)) + return PTR_ERR(tx); + + spec = kvzalloc(sizeof(*spec), GFP_KERNEL); + if (!spec) { + err = -ENOMEM; + goto err_alloc; + } + + if (attrs->family == AF_INET) + setup_fte_addr4(spec, &attrs->saddr.a4, &attrs->daddr.a4); + else + setup_fte_addr6(spec, attrs->saddr.a6, attrs->daddr.a6); + + setup_fte_no_frags(spec); + + switch (attrs->action) { + case XFRM_POLICY_ALLOW: + flow_act.action |= MLX5_FLOW_CONTEXT_ACTION_FWD_DEST; + break; + case XFRM_POLICY_BLOCK: + flow_act.action |= MLX5_FLOW_CONTEXT_ACTION_DROP; + break; + default: + WARN_ON(true); + err = -EINVAL; + goto err_action; + } + + flow_act.flags |= FLOW_ACT_NO_APPEND; + dest.ft = tx->ft.sa; + dest.type = MLX5_FLOW_DESTINATION_TYPE_FLOW_TABLE; + rule = mlx5_add_flow_rules(tx->ft.pol, spec, &flow_act, &dest, 1); + if (IS_ERR(rule)) { + err = PTR_ERR(rule); + mlx5_core_err(mdev, "fail to add TX ipsec rule err=%d\n", err); + goto err_action; + } + + kvfree(spec); + pol_entry->rule = rule; + return 0; + +err_action: + kvfree(spec); +err_alloc: + tx_ft_put(pol_entry->ipsec); + return err; +} + +static int rx_add_policy(struct mlx5e_ipsec_pol_entry *pol_entry) +{ + struct mlx5_accel_pol_xfrm_attrs *attrs = &pol_entry->attrs; + struct mlx5_core_dev *mdev = mlx5e_ipsec_pol2dev(pol_entry); + struct mlx5_flow_destination dest = {}; + struct mlx5_flow_act flow_act = {}; + struct mlx5_flow_handle *rule; + struct mlx5_flow_spec *spec; + struct mlx5e_ipsec_rx *rx; + int err; + + rx = rx_ft_get(mdev, pol_entry->ipsec, attrs->family); + if (IS_ERR(rx)) + return PTR_ERR(rx); + + spec = kvzalloc(sizeof(*spec), GFP_KERNEL); + if (!spec) { + err = -ENOMEM; + goto err_alloc; + } + + if (attrs->family == AF_INET) + setup_fte_addr4(spec, &attrs->saddr.a4, &attrs->daddr.a4); + else + setup_fte_addr6(spec, attrs->saddr.a6, attrs->daddr.a6); + + setup_fte_no_frags(spec); + + switch (attrs->action) { + case XFRM_POLICY_ALLOW: + flow_act.action |= MLX5_FLOW_CONTEXT_ACTION_FWD_DEST; + break; + case XFRM_POLICY_BLOCK: + flow_act.action |= MLX5_FLOW_CONTEXT_ACTION_DROP; + break; + default: + WARN_ON(true); + err = -EINVAL; + goto err_action; + } + + flow_act.flags |= FLOW_ACT_NO_APPEND; + dest.type = MLX5_FLOW_DESTINATION_TYPE_FLOW_TABLE; + dest.ft = rx->ft.sa; + rule = mlx5_add_flow_rules(rx->ft.pol, spec, &flow_act, &dest, 1); + if (IS_ERR(rule)) { + err = PTR_ERR(rule); + mlx5_core_err(mdev, "Fail to add RX IPsec policy rule err=%d\n", err); + goto err_action; + } + + kvfree(spec); + pol_entry->rule = rule; + return 0; + +err_action: + kvfree(spec); +err_alloc: + rx_ft_put(mdev, pol_entry->ipsec, attrs->family); + return err; +} + int mlx5e_accel_ipsec_fs_add_rule(struct mlx5e_ipsec_sa_entry *sa_entry) { if (sa_entry->attrs.dir == XFRM_DEV_OFFLOAD_OUT) @@ -621,6 +745,28 @@ void mlx5e_accel_ipsec_fs_del_rule(struct mlx5e_ipsec_sa_entry *sa_entry) rx_ft_put(mdev, sa_entry->ipsec, sa_entry->attrs.family); } +int mlx5e_accel_ipsec_fs_add_pol(struct mlx5e_ipsec_pol_entry *pol_entry) +{ + if (pol_entry->attrs.dir == XFRM_DEV_OFFLOAD_OUT) + return tx_add_policy(pol_entry); + + return rx_add_policy(pol_entry); +} + +void mlx5e_accel_ipsec_fs_del_pol(struct mlx5e_ipsec_pol_entry *pol_entry) +{ + struct mlx5_core_dev *mdev = mlx5e_ipsec_pol2dev(pol_entry); + + mlx5_del_flow_rules(pol_entry->rule); + + if (pol_entry->attrs.dir == XFRM_DEV_OFFLOAD_OUT) { + tx_ft_put(pol_entry->ipsec); + return; + } + + rx_ft_put(mdev, pol_entry->ipsec, pol_entry->attrs.family); +} + void mlx5e_accel_ipsec_fs_cleanup(struct mlx5e_ipsec *ipsec) { if (!ipsec->tx) From 9af594d8a99ef726b5785a309fb230265d16fd8b Mon Sep 17 00:00:00 2001 From: Leon Romanovsky Date: Fri, 2 Dec 2022 22:14:47 +0200 Subject: [PATCH 1692/2002] net/mlx5e: Use same coding pattern for Rx and Tx flows Remove intermediate variable in favor of having similar coding style for Rx and Tx add rule functions. Reviewed-by: Raed Salem Reviewed-by: Saeed Mahameed Signed-off-by: Leon Romanovsky Signed-off-by: Steffen Klassert --- drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_fs.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_fs.c b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_fs.c index 27f866a158de..893c1862e211 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_fs.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_fs.c @@ -482,7 +482,6 @@ static int setup_modify_header(struct mlx5_core_dev *mdev, u32 val, u8 dir, static int rx_add_rule(struct mlx5e_ipsec_sa_entry *sa_entry) { - struct mlx5e_ipsec_rule *ipsec_rule = &sa_entry->ipsec_rule; struct mlx5_accel_esp_xfrm_attrs *attrs = &sa_entry->attrs; struct mlx5_core_dev *mdev = mlx5e_ipsec_sa2dev(sa_entry); struct mlx5e_ipsec *ipsec = sa_entry->ipsec; @@ -532,8 +531,8 @@ static int rx_add_rule(struct mlx5e_ipsec_sa_entry *sa_entry) } kvfree(spec); - ipsec_rule->rule = rule; - ipsec_rule->modify_hdr = flow_act.modify_hdr; + sa_entry->ipsec_rule.rule = rule; + sa_entry->ipsec_rule.modify_hdr = flow_act.modify_hdr; return 0; err_add_flow: From 6b5c45e16e434efc6d323a8bc79e0eba49cd13f2 Mon Sep 17 00:00:00 2001 From: Leon Romanovsky Date: Fri, 2 Dec 2022 22:14:48 +0200 Subject: [PATCH 1693/2002] net/mlx5e: Configure IPsec packet offload flow steering In packet offload mode, the HW is responsible to handle ESP headers, SPI numbers and trailers (ICV) together with different logic for RX and TX paths. In order to support packet offload mode, special logic is added to flow steering rules. Reviewed-by: Raed Salem Reviewed-by: Saeed Mahameed Signed-off-by: Leon Romanovsky Signed-off-by: Steffen Klassert --- .../mellanox/mlx5/core/en_accel/ipsec.c | 2 + .../mellanox/mlx5/core/en_accel/ipsec.h | 2 + .../mellanox/mlx5/core/en_accel/ipsec_fs.c | 101 +++++++++++++++--- 3 files changed, 93 insertions(+), 12 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.c b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.c index b22a31e178cf..65f73a5c29ba 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.c @@ -162,6 +162,8 @@ mlx5e_ipsec_build_accel_xfrm_attrs(struct mlx5e_ipsec_sa_entry *sa_entry, memcpy(&aes_gcm->salt, x->aead->alg_key + key_len, sizeof(aes_gcm->salt)); + attrs->authsize = crypto_aead_authsize(aead) / 4; /* in dwords */ + /* iv len */ aes_gcm->icv_len = x->aead->alg_icv_len; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.h b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.h index 8d1a0d053eb4..25b865590488 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.h @@ -76,6 +76,7 @@ struct mlx5_accel_esp_xfrm_attrs { u8 type : 2; u8 family; u32 replay_window; + u32 authsize; }; enum mlx5_ipsec_cap { @@ -127,6 +128,7 @@ struct mlx5e_ipsec_esn_state { struct mlx5e_ipsec_rule { struct mlx5_flow_handle *rule; struct mlx5_modify_hdr *modify_hdr; + struct mlx5_pkt_reformat *pkt_reformat; }; struct mlx5e_ipsec_modify_state_work { diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_fs.c b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_fs.c index 893c1862e211..dbe35accaebf 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_fs.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_fs.c @@ -480,6 +480,48 @@ static int setup_modify_header(struct mlx5_core_dev *mdev, u32 val, u8 dir, return 0; } +static int setup_pkt_reformat(struct mlx5_core_dev *mdev, + struct mlx5_accel_esp_xfrm_attrs *attrs, + struct mlx5_flow_act *flow_act) +{ + enum mlx5_flow_namespace_type ns_type = MLX5_FLOW_NAMESPACE_EGRESS; + struct mlx5_pkt_reformat_params reformat_params = {}; + struct mlx5_pkt_reformat *pkt_reformat; + u8 reformatbf[16] = {}; + __be32 spi; + + if (attrs->dir == XFRM_DEV_OFFLOAD_IN) { + reformat_params.type = MLX5_REFORMAT_TYPE_DEL_ESP_TRANSPORT; + ns_type = MLX5_FLOW_NAMESPACE_KERNEL; + goto cmd; + } + + if (attrs->family == AF_INET) + reformat_params.type = + MLX5_REFORMAT_TYPE_ADD_ESP_TRANSPORT_OVER_IPV4; + else + reformat_params.type = + MLX5_REFORMAT_TYPE_ADD_ESP_TRANSPORT_OVER_IPV6; + + /* convert to network format */ + spi = htonl(attrs->spi); + memcpy(reformatbf, &spi, 4); + + reformat_params.param_0 = attrs->authsize; + reformat_params.size = sizeof(reformatbf); + reformat_params.data = &reformatbf; + +cmd: + pkt_reformat = + mlx5_packet_reformat_alloc(mdev, &reformat_params, ns_type); + if (IS_ERR(pkt_reformat)) + return PTR_ERR(pkt_reformat); + + flow_act->pkt_reformat = pkt_reformat; + flow_act->action |= MLX5_FLOW_CONTEXT_ACTION_PACKET_REFORMAT; + return 0; +} + static int rx_add_rule(struct mlx5e_ipsec_sa_entry *sa_entry) { struct mlx5_accel_esp_xfrm_attrs *attrs = &sa_entry->attrs; @@ -516,6 +558,16 @@ static int rx_add_rule(struct mlx5e_ipsec_sa_entry *sa_entry) if (err) goto err_mod_header; + switch (attrs->type) { + case XFRM_DEV_OFFLOAD_PACKET: + err = setup_pkt_reformat(mdev, attrs, &flow_act); + if (err) + goto err_pkt_reformat; + break; + default: + break; + } + flow_act.crypto.type = MLX5_FLOW_CONTEXT_ENCRYPT_DECRYPT_TYPE_IPSEC; flow_act.crypto.obj_id = sa_entry->ipsec_obj_id; flow_act.flags |= FLOW_ACT_NO_APPEND; @@ -533,9 +585,13 @@ static int rx_add_rule(struct mlx5e_ipsec_sa_entry *sa_entry) sa_entry->ipsec_rule.rule = rule; sa_entry->ipsec_rule.modify_hdr = flow_act.modify_hdr; + sa_entry->ipsec_rule.pkt_reformat = flow_act.pkt_reformat; return 0; err_add_flow: + if (flow_act.pkt_reformat) + mlx5_packet_reformat_dealloc(mdev, flow_act.pkt_reformat); +err_pkt_reformat: mlx5_modify_header_dealloc(mdev, flow_act.modify_hdr); err_mod_header: kvfree(spec); @@ -562,7 +618,7 @@ static int tx_add_rule(struct mlx5e_ipsec_sa_entry *sa_entry) spec = kvzalloc(sizeof(*spec), GFP_KERNEL); if (!spec) { err = -ENOMEM; - goto out; + goto err_alloc; } if (attrs->family == AF_INET) @@ -570,29 +626,47 @@ static int tx_add_rule(struct mlx5e_ipsec_sa_entry *sa_entry) else setup_fte_addr6(spec, attrs->saddr.a6, attrs->daddr.a6); - setup_fte_spi(spec, attrs->spi); - setup_fte_esp(spec); setup_fte_no_frags(spec); - setup_fte_reg_a(spec); + + switch (attrs->type) { + case XFRM_DEV_OFFLOAD_CRYPTO: + setup_fte_spi(spec, attrs->spi); + setup_fte_esp(spec); + setup_fte_reg_a(spec); + break; + case XFRM_DEV_OFFLOAD_PACKET: + err = setup_pkt_reformat(mdev, attrs, &flow_act); + if (err) + goto err_pkt_reformat; + break; + default: + break; + } flow_act.crypto.type = MLX5_FLOW_CONTEXT_ENCRYPT_DECRYPT_TYPE_IPSEC; flow_act.crypto.obj_id = sa_entry->ipsec_obj_id; flow_act.flags |= FLOW_ACT_NO_APPEND; - flow_act.action = MLX5_FLOW_CONTEXT_ACTION_ALLOW | - MLX5_FLOW_CONTEXT_ACTION_CRYPTO_ENCRYPT; + flow_act.action |= MLX5_FLOW_CONTEXT_ACTION_ALLOW | + MLX5_FLOW_CONTEXT_ACTION_CRYPTO_ENCRYPT; rule = mlx5_add_flow_rules(tx->ft.sa, spec, &flow_act, NULL, 0); if (IS_ERR(rule)) { err = PTR_ERR(rule); mlx5_core_err(mdev, "fail to add TX ipsec rule err=%d\n", err); - goto out; + goto err_add_flow; } - sa_entry->ipsec_rule.rule = rule; - -out: kvfree(spec); - if (err) - tx_ft_put(ipsec); + sa_entry->ipsec_rule.rule = rule; + sa_entry->ipsec_rule.pkt_reformat = flow_act.pkt_reformat; + return 0; + +err_add_flow: + if (flow_act.pkt_reformat) + mlx5_packet_reformat_dealloc(mdev, flow_act.pkt_reformat); +err_pkt_reformat: + kvfree(spec); +err_alloc: + tx_ft_put(ipsec); return err; } @@ -735,6 +809,9 @@ void mlx5e_accel_ipsec_fs_del_rule(struct mlx5e_ipsec_sa_entry *sa_entry) mlx5_del_flow_rules(ipsec_rule->rule); + if (ipsec_rule->pkt_reformat) + mlx5_packet_reformat_dealloc(mdev, ipsec_rule->pkt_reformat); + if (sa_entry->attrs.dir == XFRM_DEV_OFFLOAD_OUT) { tx_ft_put(sa_entry->ipsec); return; From 18f38fd267abf3be8330dd73ddeb838f2dd98f29 Mon Sep 17 00:00:00 2001 From: Leon Romanovsky Date: Fri, 2 Dec 2022 22:14:49 +0200 Subject: [PATCH 1694/2002] net/mlx5e: Improve IPsec flow steering autogroup Flow steering API separates newly created rules based on their match criteria. Right now, all IPsec tables are created with one group and suffers from not-optimal FS performance. Count number of different match criteria for relevant tables, and set proper value at the table creation. Reviewed-by: Raed Salem Signed-off-by: Leon Romanovsky Signed-off-by: Steffen Klassert --- .../net/ethernet/mellanox/mlx5/core/en_accel/ipsec_fs.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_fs.c b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_fs.c index dbe35accaebf..0af831128c4e 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_fs.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_fs.c @@ -196,7 +196,7 @@ static int rx_create(struct mlx5_core_dev *mdev, struct mlx5e_ipsec *ipsec, /* Create FT */ ft = ipsec_ft_create(ns, MLX5E_ACCEL_FS_ESP_FT_LEVEL, MLX5E_NIC_PRIO, - 1); + 2); if (IS_ERR(ft)) { err = PTR_ERR(ft); goto err_fs_ft; @@ -208,7 +208,7 @@ static int rx_create(struct mlx5_core_dev *mdev, struct mlx5e_ipsec *ipsec, goto err_fs; ft = ipsec_ft_create(ns, MLX5E_ACCEL_FS_POL_FT_LEVEL, MLX5E_NIC_PRIO, - 1); + 2); if (IS_ERR(ft)) { err = PTR_ERR(ft); goto err_pol_ft; @@ -307,13 +307,13 @@ static int tx_create(struct mlx5_core_dev *mdev, struct mlx5e_ipsec_tx *tx) struct mlx5_flow_table *ft; int err; - ft = ipsec_ft_create(tx->ns, 1, 0, 1); + ft = ipsec_ft_create(tx->ns, 1, 0, 4); if (IS_ERR(ft)) return PTR_ERR(ft); tx->ft.sa = ft; - ft = ipsec_ft_create(tx->ns, 0, 0, 1); + ft = ipsec_ft_create(tx->ns, 0, 0, 2); if (IS_ERR(ft)) { err = PTR_ERR(ft); goto err_pol_ft; From 81f8fba5ecaad53b6b32a81fe7ef06b772557647 Mon Sep 17 00:00:00 2001 From: Raed Salem Date: Fri, 2 Dec 2022 22:14:50 +0200 Subject: [PATCH 1695/2002] net/mlx5e: Add statistics for Rx/Tx IPsec offloaded flows Add the following statistics: RX successfully IPsec flows: ipsec_rx_pkts : Number of packets passed Rx IPsec flow ipsec_rx_bytes : Number of bytes passed Rx IPsec flow Rx dropped IPsec policy packets: ipsec_rx_drop_pkts: Number of packets dropped in Rx datapath due to IPsec drop policy ipsec_rx_drop_bytes: Number of bytes dropped in Rx datapath due to IPsec drop policy TX successfully encrypted and encapsulated IPsec packets: ipsec_tx_pkts : Number of packets encrypted and encapsulated successfully ipsec_tx_bytes : Number of bytes encrypted and encapsulated successfully Tx dropped IPsec policy packets: ipsec_tx_drop_pkts: Number of packets dropped in Tx datapath due to IPsec drop policy ipsec_tx_drop_bytes: Number of bytes dropped in Tx datapath due to IPsec drop policy The above can be seen using: ethtool -S |grep ipsec Signed-off-by: Raed Salem Signed-off-by: Leon Romanovsky Signed-off-by: Steffen Klassert --- .../mellanox/mlx5/core/en_accel/ipsec.h | 15 ++ .../mellanox/mlx5/core/en_accel/ipsec_fs.c | 188 +++++++++++++++--- .../mellanox/mlx5/core/en_accel/ipsec_stats.c | 52 +++++ .../ethernet/mellanox/mlx5/core/en_stats.c | 1 + .../ethernet/mellanox/mlx5/core/en_stats.h | 1 + 5 files changed, 233 insertions(+), 24 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.h b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.h index 25b865590488..2e7654e90314 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.h @@ -87,6 +87,17 @@ enum mlx5_ipsec_cap { struct mlx5e_priv; +struct mlx5e_ipsec_hw_stats { + u64 ipsec_rx_pkts; + u64 ipsec_rx_bytes; + u64 ipsec_rx_drop_pkts; + u64 ipsec_rx_drop_bytes; + u64 ipsec_tx_pkts; + u64 ipsec_tx_bytes; + u64 ipsec_tx_drop_pkts; + u64 ipsec_tx_drop_bytes; +}; + struct mlx5e_ipsec_sw_stats { atomic64_t ipsec_rx_drop_sp_alloc; atomic64_t ipsec_rx_drop_sadb_miss; @@ -111,6 +122,7 @@ struct mlx5e_ipsec { DECLARE_HASHTABLE(sadb_rx, MLX5E_IPSEC_SADB_RX_BITS); spinlock_t sadb_rx_lock; /* Protects sadb_rx */ struct mlx5e_ipsec_sw_stats sw_stats; + struct mlx5e_ipsec_hw_stats hw_stats; struct workqueue_struct *wq; struct mlx5e_flow_steering *fs; struct mlx5e_ipsec_rx *rx_ipv4; @@ -200,6 +212,9 @@ void mlx5_accel_esp_modify_xfrm(struct mlx5e_ipsec_sa_entry *sa_entry, int mlx5e_ipsec_aso_init(struct mlx5e_ipsec *ipsec); void mlx5e_ipsec_aso_cleanup(struct mlx5e_ipsec *ipsec); +void mlx5e_accel_ipsec_fs_read_stats(struct mlx5e_priv *priv, + void *ipsec_stats); + static inline struct mlx5_core_dev * mlx5e_ipsec_sa2dev(struct mlx5e_ipsec_sa_entry *sa_entry) { diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_fs.c b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_fs.c index 0af831128c4e..8ba92b00afdb 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_fs.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_fs.c @@ -9,6 +9,11 @@ #define NUM_IPSEC_FTE BIT(15) +struct mlx5e_ipsec_fc { + struct mlx5_fc *cnt; + struct mlx5_fc *drop; +}; + struct mlx5e_ipsec_ft { struct mutex mutex; /* Protect changes to this struct */ struct mlx5_flow_table *pol; @@ -27,12 +32,14 @@ struct mlx5e_ipsec_rx { struct mlx5e_ipsec_miss pol; struct mlx5e_ipsec_miss sa; struct mlx5e_ipsec_rule status; + struct mlx5e_ipsec_fc *fc; }; struct mlx5e_ipsec_tx { struct mlx5e_ipsec_ft ft; struct mlx5e_ipsec_miss pol; struct mlx5_flow_namespace *ns; + struct mlx5e_ipsec_fc *fc; }; /* IPsec RX flow steering */ @@ -93,9 +100,10 @@ static int ipsec_status_rule(struct mlx5_core_dev *mdev, /* create fte */ flow_act.action = MLX5_FLOW_CONTEXT_ACTION_MOD_HDR | - MLX5_FLOW_CONTEXT_ACTION_FWD_DEST; + MLX5_FLOW_CONTEXT_ACTION_FWD_DEST | + MLX5_FLOW_CONTEXT_ACTION_COUNT; flow_act.modify_hdr = modify_hdr; - fte = mlx5_add_flow_rules(rx->ft.status, spec, &flow_act, dest, 1); + fte = mlx5_add_flow_rules(rx->ft.status, spec, &flow_act, dest, 2); if (IS_ERR(fte)) { err = PTR_ERR(fte); mlx5_core_err(mdev, "fail to add ipsec rx err copy rule err=%d\n", err); @@ -178,7 +186,7 @@ static int rx_create(struct mlx5_core_dev *mdev, struct mlx5e_ipsec *ipsec, { struct mlx5_flow_namespace *ns = mlx5e_fs_get_ns(ipsec->fs, false); struct mlx5_ttc_table *ttc = mlx5e_fs_get_ttc(ipsec->fs, false); - struct mlx5_flow_destination dest; + struct mlx5_flow_destination dest[2]; struct mlx5_flow_table *ft; int err; @@ -189,8 +197,10 @@ static int rx_create(struct mlx5_core_dev *mdev, struct mlx5e_ipsec *ipsec, rx->ft.status = ft; - dest = mlx5_ttc_get_default_dest(ttc, family2tt(family)); - err = ipsec_status_rule(mdev, rx, &dest); + dest[0] = mlx5_ttc_get_default_dest(ttc, family2tt(family)); + dest[1].type = MLX5_FLOW_DESTINATION_TYPE_COUNTER; + dest[1].counter_id = mlx5_fc_id(rx->fc->cnt); + err = ipsec_status_rule(mdev, rx, dest); if (err) goto err_add; @@ -203,7 +213,7 @@ static int rx_create(struct mlx5_core_dev *mdev, struct mlx5e_ipsec *ipsec, } rx->ft.sa = ft; - err = ipsec_miss_create(mdev, rx->ft.sa, &rx->sa, &dest); + err = ipsec_miss_create(mdev, rx->ft.sa, &rx->sa, dest); if (err) goto err_fs; @@ -214,10 +224,10 @@ static int rx_create(struct mlx5_core_dev *mdev, struct mlx5e_ipsec *ipsec, goto err_pol_ft; } rx->ft.pol = ft; - memset(&dest, 0x00, sizeof(dest)); - dest.type = MLX5_FLOW_DESTINATION_TYPE_FLOW_TABLE; - dest.ft = rx->ft.sa; - err = ipsec_miss_create(mdev, rx->ft.pol, &rx->pol, &dest); + memset(dest, 0x00, 2 * sizeof(*dest)); + dest[0].type = MLX5_FLOW_DESTINATION_TYPE_FLOW_TABLE; + dest[0].ft = rx->ft.sa; + err = ipsec_miss_create(mdev, rx->ft.pol, &rx->pol, dest); if (err) goto err_pol_miss; @@ -605,6 +615,7 @@ static int tx_add_rule(struct mlx5e_ipsec_sa_entry *sa_entry) struct mlx5_accel_esp_xfrm_attrs *attrs = &sa_entry->attrs; struct mlx5_core_dev *mdev = mlx5e_ipsec_sa2dev(sa_entry); struct mlx5e_ipsec *ipsec = sa_entry->ipsec; + struct mlx5_flow_destination dest = {}; struct mlx5_flow_act flow_act = {}; struct mlx5_flow_handle *rule; struct mlx5_flow_spec *spec; @@ -647,8 +658,11 @@ static int tx_add_rule(struct mlx5e_ipsec_sa_entry *sa_entry) flow_act.crypto.obj_id = sa_entry->ipsec_obj_id; flow_act.flags |= FLOW_ACT_NO_APPEND; flow_act.action |= MLX5_FLOW_CONTEXT_ACTION_ALLOW | - MLX5_FLOW_CONTEXT_ACTION_CRYPTO_ENCRYPT; - rule = mlx5_add_flow_rules(tx->ft.sa, spec, &flow_act, NULL, 0); + MLX5_FLOW_CONTEXT_ACTION_CRYPTO_ENCRYPT | + MLX5_FLOW_CONTEXT_ACTION_COUNT; + dest.type = MLX5_FLOW_DESTINATION_TYPE_COUNTER; + dest.counter_id = mlx5_fc_id(tx->fc->cnt); + rule = mlx5_add_flow_rules(tx->ft.sa, spec, &flow_act, &dest, 1); if (IS_ERR(rule)) { err = PTR_ERR(rule); mlx5_core_err(mdev, "fail to add TX ipsec rule err=%d\n", err); @@ -674,12 +688,12 @@ static int tx_add_policy(struct mlx5e_ipsec_pol_entry *pol_entry) { struct mlx5_accel_pol_xfrm_attrs *attrs = &pol_entry->attrs; struct mlx5_core_dev *mdev = mlx5e_ipsec_pol2dev(pol_entry); - struct mlx5_flow_destination dest = {}; + struct mlx5_flow_destination dest[2] = {}; struct mlx5_flow_act flow_act = {}; struct mlx5_flow_handle *rule; struct mlx5_flow_spec *spec; struct mlx5e_ipsec_tx *tx; - int err; + int err, dstn = 0; tx = tx_ft_get(mdev, pol_entry->ipsec); if (IS_ERR(tx)) @@ -703,7 +717,11 @@ static int tx_add_policy(struct mlx5e_ipsec_pol_entry *pol_entry) flow_act.action |= MLX5_FLOW_CONTEXT_ACTION_FWD_DEST; break; case XFRM_POLICY_BLOCK: - flow_act.action |= MLX5_FLOW_CONTEXT_ACTION_DROP; + flow_act.action |= MLX5_FLOW_CONTEXT_ACTION_DROP | + MLX5_FLOW_CONTEXT_ACTION_COUNT; + dest[dstn].type = MLX5_FLOW_DESTINATION_TYPE_COUNTER; + dest[dstn].counter_id = mlx5_fc_id(tx->fc->drop); + dstn++; break; default: WARN_ON(true); @@ -712,9 +730,10 @@ static int tx_add_policy(struct mlx5e_ipsec_pol_entry *pol_entry) } flow_act.flags |= FLOW_ACT_NO_APPEND; - dest.ft = tx->ft.sa; - dest.type = MLX5_FLOW_DESTINATION_TYPE_FLOW_TABLE; - rule = mlx5_add_flow_rules(tx->ft.pol, spec, &flow_act, &dest, 1); + dest[dstn].ft = tx->ft.sa; + dest[dstn].type = MLX5_FLOW_DESTINATION_TYPE_FLOW_TABLE; + dstn++; + rule = mlx5_add_flow_rules(tx->ft.pol, spec, &flow_act, dest, dstn); if (IS_ERR(rule)) { err = PTR_ERR(rule); mlx5_core_err(mdev, "fail to add TX ipsec rule err=%d\n", err); @@ -736,12 +755,12 @@ static int rx_add_policy(struct mlx5e_ipsec_pol_entry *pol_entry) { struct mlx5_accel_pol_xfrm_attrs *attrs = &pol_entry->attrs; struct mlx5_core_dev *mdev = mlx5e_ipsec_pol2dev(pol_entry); - struct mlx5_flow_destination dest = {}; + struct mlx5_flow_destination dest[2]; struct mlx5_flow_act flow_act = {}; struct mlx5_flow_handle *rule; struct mlx5_flow_spec *spec; struct mlx5e_ipsec_rx *rx; - int err; + int err, dstn = 0; rx = rx_ft_get(mdev, pol_entry->ipsec, attrs->family); if (IS_ERR(rx)) @@ -765,7 +784,10 @@ static int rx_add_policy(struct mlx5e_ipsec_pol_entry *pol_entry) flow_act.action |= MLX5_FLOW_CONTEXT_ACTION_FWD_DEST; break; case XFRM_POLICY_BLOCK: - flow_act.action |= MLX5_FLOW_CONTEXT_ACTION_DROP; + flow_act.action |= MLX5_FLOW_CONTEXT_ACTION_DROP | MLX5_FLOW_CONTEXT_ACTION_COUNT; + dest[dstn].type = MLX5_FLOW_DESTINATION_TYPE_COUNTER; + dest[dstn].counter_id = mlx5_fc_id(rx->fc->drop); + dstn++; break; default: WARN_ON(true); @@ -774,9 +796,10 @@ static int rx_add_policy(struct mlx5e_ipsec_pol_entry *pol_entry) } flow_act.flags |= FLOW_ACT_NO_APPEND; - dest.type = MLX5_FLOW_DESTINATION_TYPE_FLOW_TABLE; - dest.ft = rx->ft.sa; - rule = mlx5_add_flow_rules(rx->ft.pol, spec, &flow_act, &dest, 1); + dest[dstn].type = MLX5_FLOW_DESTINATION_TYPE_FLOW_TABLE; + dest[dstn].ft = rx->ft.sa; + dstn++; + rule = mlx5_add_flow_rules(rx->ft.pol, spec, &flow_act, dest, dstn); if (IS_ERR(rule)) { err = PTR_ERR(rule); mlx5_core_err(mdev, "Fail to add RX IPsec policy rule err=%d\n", err); @@ -794,6 +817,116 @@ err_alloc: return err; } +static void ipsec_fs_destroy_counters(struct mlx5e_ipsec *ipsec) +{ + struct mlx5e_ipsec_rx *rx_ipv4 = ipsec->rx_ipv4; + struct mlx5_core_dev *mdev = ipsec->mdev; + struct mlx5e_ipsec_tx *tx = ipsec->tx; + + mlx5_fc_destroy(mdev, tx->fc->drop); + mlx5_fc_destroy(mdev, tx->fc->cnt); + kfree(tx->fc); + mlx5_fc_destroy(mdev, rx_ipv4->fc->drop); + mlx5_fc_destroy(mdev, rx_ipv4->fc->cnt); + kfree(rx_ipv4->fc); +} + +static int ipsec_fs_init_counters(struct mlx5e_ipsec *ipsec) +{ + struct mlx5e_ipsec_rx *rx_ipv4 = ipsec->rx_ipv4; + struct mlx5e_ipsec_rx *rx_ipv6 = ipsec->rx_ipv6; + struct mlx5_core_dev *mdev = ipsec->mdev; + struct mlx5e_ipsec_tx *tx = ipsec->tx; + struct mlx5e_ipsec_fc *fc; + struct mlx5_fc *counter; + int err; + + fc = kzalloc(sizeof(*rx_ipv4->fc), GFP_KERNEL); + if (!fc) + return -ENOMEM; + + /* Both IPv4 and IPv6 point to same flow counters struct. */ + rx_ipv4->fc = fc; + rx_ipv6->fc = fc; + counter = mlx5_fc_create(mdev, false); + if (IS_ERR(counter)) { + err = PTR_ERR(counter); + goto err_rx_cnt; + } + + fc->cnt = counter; + counter = mlx5_fc_create(mdev, false); + if (IS_ERR(counter)) { + err = PTR_ERR(counter); + goto err_rx_drop; + } + + fc->drop = counter; + fc = kzalloc(sizeof(*tx->fc), GFP_KERNEL); + if (!fc) { + err = -ENOMEM; + goto err_tx_fc; + } + + tx->fc = fc; + counter = mlx5_fc_create(mdev, false); + if (IS_ERR(counter)) { + err = PTR_ERR(counter); + goto err_tx_cnt; + } + + fc->cnt = counter; + counter = mlx5_fc_create(mdev, false); + if (IS_ERR(counter)) { + err = PTR_ERR(counter); + goto err_tx_drop; + } + + fc->drop = counter; + return 0; + +err_tx_drop: + mlx5_fc_destroy(mdev, tx->fc->cnt); +err_tx_cnt: + kfree(tx->fc); +err_tx_fc: + mlx5_fc_destroy(mdev, rx_ipv4->fc->drop); +err_rx_drop: + mlx5_fc_destroy(mdev, rx_ipv4->fc->cnt); +err_rx_cnt: + kfree(rx_ipv4->fc); + return err; +} + +void mlx5e_accel_ipsec_fs_read_stats(struct mlx5e_priv *priv, void *ipsec_stats) +{ + struct mlx5_core_dev *mdev = priv->mdev; + struct mlx5e_ipsec *ipsec = priv->ipsec; + struct mlx5e_ipsec_hw_stats *stats; + struct mlx5e_ipsec_fc *fc; + + stats = (struct mlx5e_ipsec_hw_stats *)ipsec_stats; + + stats->ipsec_rx_pkts = 0; + stats->ipsec_rx_bytes = 0; + stats->ipsec_rx_drop_pkts = 0; + stats->ipsec_rx_drop_bytes = 0; + stats->ipsec_tx_pkts = 0; + stats->ipsec_tx_bytes = 0; + stats->ipsec_tx_drop_pkts = 0; + stats->ipsec_tx_drop_bytes = 0; + + fc = ipsec->rx_ipv4->fc; + mlx5_fc_query(mdev, fc->cnt, &stats->ipsec_rx_pkts, &stats->ipsec_rx_bytes); + mlx5_fc_query(mdev, fc->drop, &stats->ipsec_rx_drop_pkts, + &stats->ipsec_rx_drop_bytes); + + fc = ipsec->tx->fc; + mlx5_fc_query(mdev, fc->cnt, &stats->ipsec_tx_pkts, &stats->ipsec_tx_bytes); + mlx5_fc_query(mdev, fc->drop, &stats->ipsec_tx_drop_pkts, + &stats->ipsec_tx_drop_bytes); +} + int mlx5e_accel_ipsec_fs_add_rule(struct mlx5e_ipsec_sa_entry *sa_entry) { if (sa_entry->attrs.dir == XFRM_DEV_OFFLOAD_OUT) @@ -848,6 +981,7 @@ void mlx5e_accel_ipsec_fs_cleanup(struct mlx5e_ipsec *ipsec) if (!ipsec->tx) return; + ipsec_fs_destroy_counters(ipsec); mutex_destroy(&ipsec->tx->ft.mutex); WARN_ON(ipsec->tx->ft.refcnt); kfree(ipsec->tx); @@ -883,6 +1017,10 @@ int mlx5e_accel_ipsec_fs_init(struct mlx5e_ipsec *ipsec) if (!ipsec->rx_ipv6) goto err_rx_ipv6; + err = ipsec_fs_init_counters(ipsec); + if (err) + goto err_counters; + mutex_init(&ipsec->tx->ft.mutex); mutex_init(&ipsec->rx_ipv4->ft.mutex); mutex_init(&ipsec->rx_ipv6->ft.mutex); @@ -890,6 +1028,8 @@ int mlx5e_accel_ipsec_fs_init(struct mlx5e_ipsec *ipsec) return 0; +err_counters: + kfree(ipsec->rx_ipv6); err_rx_ipv6: kfree(ipsec->rx_ipv4); err_rx_ipv4: diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_stats.c b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_stats.c index 9de84821dafb..e0e36a09721c 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_stats.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_stats.c @@ -37,6 +37,17 @@ #include "en.h" #include "ipsec.h" +static const struct counter_desc mlx5e_ipsec_hw_stats_desc[] = { + { MLX5E_DECLARE_STAT(struct mlx5e_ipsec_hw_stats, ipsec_rx_pkts) }, + { MLX5E_DECLARE_STAT(struct mlx5e_ipsec_hw_stats, ipsec_rx_bytes) }, + { MLX5E_DECLARE_STAT(struct mlx5e_ipsec_hw_stats, ipsec_rx_drop_pkts) }, + { MLX5E_DECLARE_STAT(struct mlx5e_ipsec_hw_stats, ipsec_rx_drop_bytes) }, + { MLX5E_DECLARE_STAT(struct mlx5e_ipsec_hw_stats, ipsec_tx_pkts) }, + { MLX5E_DECLARE_STAT(struct mlx5e_ipsec_hw_stats, ipsec_tx_bytes) }, + { MLX5E_DECLARE_STAT(struct mlx5e_ipsec_hw_stats, ipsec_tx_drop_pkts) }, + { MLX5E_DECLARE_STAT(struct mlx5e_ipsec_hw_stats, ipsec_tx_drop_bytes) }, +}; + static const struct counter_desc mlx5e_ipsec_sw_stats_desc[] = { { MLX5E_DECLARE_STAT(struct mlx5e_ipsec_sw_stats, ipsec_rx_drop_sp_alloc) }, { MLX5E_DECLARE_STAT(struct mlx5e_ipsec_sw_stats, ipsec_rx_drop_sadb_miss) }, @@ -50,8 +61,48 @@ static const struct counter_desc mlx5e_ipsec_sw_stats_desc[] = { #define MLX5E_READ_CTR_ATOMIC64(ptr, dsc, i) \ atomic64_read((atomic64_t *)((char *)(ptr) + (dsc)[i].offset)) +#define NUM_IPSEC_HW_COUNTERS ARRAY_SIZE(mlx5e_ipsec_hw_stats_desc) #define NUM_IPSEC_SW_COUNTERS ARRAY_SIZE(mlx5e_ipsec_sw_stats_desc) +static MLX5E_DECLARE_STATS_GRP_OP_NUM_STATS(ipsec_hw) +{ + if (!priv->ipsec) + return 0; + + return NUM_IPSEC_HW_COUNTERS; +} + +static inline MLX5E_DECLARE_STATS_GRP_OP_UPDATE_STATS(ipsec_hw) {} + +static MLX5E_DECLARE_STATS_GRP_OP_FILL_STRS(ipsec_hw) +{ + unsigned int i; + + if (!priv->ipsec) + return idx; + + for (i = 0; i < NUM_IPSEC_HW_COUNTERS; i++) + strcpy(data + (idx++) * ETH_GSTRING_LEN, + mlx5e_ipsec_hw_stats_desc[i].format); + + return idx; +} + +static MLX5E_DECLARE_STATS_GRP_OP_FILL_STATS(ipsec_hw) +{ + int i; + + if (!priv->ipsec) + return idx; + + mlx5e_accel_ipsec_fs_read_stats(priv, &priv->ipsec->hw_stats); + for (i = 0; i < NUM_IPSEC_HW_COUNTERS; i++) + data[idx++] = MLX5E_READ_CTR_ATOMIC64(&priv->ipsec->hw_stats, + mlx5e_ipsec_hw_stats_desc, i); + + return idx; +} + static MLX5E_DECLARE_STATS_GRP_OP_NUM_STATS(ipsec_sw) { return priv->ipsec ? NUM_IPSEC_SW_COUNTERS : 0; @@ -81,4 +132,5 @@ static MLX5E_DECLARE_STATS_GRP_OP_FILL_STATS(ipsec_sw) return idx; } +MLX5E_DEFINE_STATS_GRP(ipsec_hw, 0); MLX5E_DEFINE_STATS_GRP(ipsec_sw, 0); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_stats.c b/drivers/net/ethernet/mellanox/mlx5/core/en_stats.c index 70c4ea3841d7..6687b8136e44 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_stats.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_stats.c @@ -2480,6 +2480,7 @@ mlx5e_stats_grp_t mlx5e_nic_stats_grps[] = { &MLX5E_STATS_GRP(per_prio), &MLX5E_STATS_GRP(pme), #ifdef CONFIG_MLX5_EN_IPSEC + &MLX5E_STATS_GRP(ipsec_hw), &MLX5E_STATS_GRP(ipsec_sw), #endif &MLX5E_STATS_GRP(tls), diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_stats.h b/drivers/net/ethernet/mellanox/mlx5/core/en_stats.h index cbc831ca646b..712cac10ba49 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_stats.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_stats.h @@ -490,6 +490,7 @@ extern MLX5E_DECLARE_STATS_GRP(per_prio); extern MLX5E_DECLARE_STATS_GRP(pme); extern MLX5E_DECLARE_STATS_GRP(channels); extern MLX5E_DECLARE_STATS_GRP(per_port_buff_congest); +extern MLX5E_DECLARE_STATS_GRP(ipsec_hw); extern MLX5E_DECLARE_STATS_GRP(ipsec_sw); extern MLX5E_DECLARE_STATS_GRP(ptp); extern MLX5E_DECLARE_STATS_GRP(macsec_hw); From 6721239672fe38187119afdafe996902ab24fd11 Mon Sep 17 00:00:00 2001 From: Leon Romanovsky Date: Fri, 2 Dec 2022 22:14:51 +0200 Subject: [PATCH 1696/2002] net/mlx5e: Skip IPsec encryption for TX path without matching policy Software implementation of IPsec skips encryption of packets in TX path if no matching policy is found. So align HW implementation to this behavior, by requiring matching reqid for offloaded policy and SA. Reviewed-by: Raed Salem Reviewed-by: Saeed Mahameed Signed-off-by: Leon Romanovsky Signed-off-by: Steffen Klassert --- .../mellanox/mlx5/core/en_accel/ipsec.c | 7 ++++ .../mellanox/mlx5/core/en_accel/ipsec.h | 4 +- .../mellanox/mlx5/core/en_accel/ipsec_fs.c | 39 ++++++++++++++++--- 3 files changed, 43 insertions(+), 7 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.c b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.c index 65f73a5c29ba..8f08dbf2206e 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.c @@ -184,6 +184,7 @@ mlx5e_ipsec_build_accel_xfrm_attrs(struct mlx5e_ipsec_sa_entry *sa_entry, memcpy(&attrs->daddr, x->id.daddr.a6, sizeof(attrs->daddr)); attrs->family = x->props.family; attrs->type = x->xso.type; + attrs->reqid = x->props.reqid; } static inline int mlx5e_xfrm_validate_state(struct xfrm_state *x) @@ -267,6 +268,11 @@ static inline int mlx5e_xfrm_validate_state(struct xfrm_state *x) x->replay_esn->replay_window); return -EINVAL; } + + if (!x->props.reqid) { + netdev_info(netdev, "Cannot offload without reqid\n"); + return -EINVAL; + } } return 0; } @@ -503,6 +509,7 @@ mlx5e_ipsec_build_accel_pol_attrs(struct mlx5e_ipsec_pol_entry *pol_entry, attrs->dir = x->xdo.dir; attrs->action = x->action; attrs->type = XFRM_DEV_OFFLOAD_PACKET; + attrs->reqid = x->xfrm_vec[0].reqid; } static int mlx5e_xfrm_add_policy(struct xfrm_policy *x) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.h b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.h index 2e7654e90314..492be255d267 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.h @@ -77,6 +77,7 @@ struct mlx5_accel_esp_xfrm_attrs { u8 family; u32 replay_window; u32 authsize; + u32 reqid; }; enum mlx5_ipsec_cap { @@ -178,12 +179,13 @@ struct mlx5_accel_pol_xfrm_attrs { u8 action; u8 type : 2; u8 dir : 2; + u32 reqid; }; struct mlx5e_ipsec_pol_entry { struct xfrm_policy *x; struct mlx5e_ipsec *ipsec; - struct mlx5_flow_handle *rule; + struct mlx5e_ipsec_rule ipsec_rule; struct mlx5_accel_pol_xfrm_attrs attrs; }; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_fs.c b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_fs.c index 8ba92b00afdb..9f19f4b59a70 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_fs.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_fs.c @@ -456,6 +456,17 @@ static void setup_fte_reg_a(struct mlx5_flow_spec *spec) misc_parameters_2.metadata_reg_a, MLX5_ETH_WQE_FT_META_IPSEC); } +static void setup_fte_reg_c0(struct mlx5_flow_spec *spec, u32 reqid) +{ + /* Pass policy check before choosing this SA */ + spec->match_criteria_enable |= MLX5_MATCH_MISC_PARAMETERS_2; + + MLX5_SET(fte_match_param, spec->match_criteria, + misc_parameters_2.metadata_reg_c_0, reqid); + MLX5_SET(fte_match_param, spec->match_value, + misc_parameters_2.metadata_reg_c_0, reqid); +} + static int setup_modify_header(struct mlx5_core_dev *mdev, u32 val, u8 dir, struct mlx5_flow_act *flow_act) { @@ -470,6 +481,11 @@ static int setup_modify_header(struct mlx5_core_dev *mdev, u32 val, u8 dir, MLX5_ACTION_IN_FIELD_METADATA_REG_B); ns_type = MLX5_FLOW_NAMESPACE_KERNEL; break; + case XFRM_DEV_OFFLOAD_OUT: + MLX5_SET(set_action_in, action, field, + MLX5_ACTION_IN_FIELD_METADATA_REG_C_0); + ns_type = MLX5_FLOW_NAMESPACE_EGRESS; + break; default: return -EINVAL; } @@ -646,6 +662,7 @@ static int tx_add_rule(struct mlx5e_ipsec_sa_entry *sa_entry) setup_fte_reg_a(spec); break; case XFRM_DEV_OFFLOAD_PACKET: + setup_fte_reg_c0(spec, attrs->reqid); err = setup_pkt_reformat(mdev, attrs, &flow_act); if (err) goto err_pkt_reformat; @@ -712,6 +729,11 @@ static int tx_add_policy(struct mlx5e_ipsec_pol_entry *pol_entry) setup_fte_no_frags(spec); + err = setup_modify_header(mdev, attrs->reqid, XFRM_DEV_OFFLOAD_OUT, + &flow_act); + if (err) + goto err_mod_header; + switch (attrs->action) { case XFRM_POLICY_ALLOW: flow_act.action |= MLX5_FLOW_CONTEXT_ACTION_FWD_DEST; @@ -741,10 +763,13 @@ static int tx_add_policy(struct mlx5e_ipsec_pol_entry *pol_entry) } kvfree(spec); - pol_entry->rule = rule; + pol_entry->ipsec_rule.rule = rule; + pol_entry->ipsec_rule.modify_hdr = flow_act.modify_hdr; return 0; err_action: + mlx5_modify_header_dealloc(mdev, flow_act.modify_hdr); +err_mod_header: kvfree(spec); err_alloc: tx_ft_put(pol_entry->ipsec); @@ -807,7 +832,7 @@ static int rx_add_policy(struct mlx5e_ipsec_pol_entry *pol_entry) } kvfree(spec); - pol_entry->rule = rule; + pol_entry->ipsec_rule.rule = rule; return 0; err_action: @@ -964,16 +989,18 @@ int mlx5e_accel_ipsec_fs_add_pol(struct mlx5e_ipsec_pol_entry *pol_entry) void mlx5e_accel_ipsec_fs_del_pol(struct mlx5e_ipsec_pol_entry *pol_entry) { + struct mlx5e_ipsec_rule *ipsec_rule = &pol_entry->ipsec_rule; struct mlx5_core_dev *mdev = mlx5e_ipsec_pol2dev(pol_entry); - mlx5_del_flow_rules(pol_entry->rule); + mlx5_del_flow_rules(ipsec_rule->rule); - if (pol_entry->attrs.dir == XFRM_DEV_OFFLOAD_OUT) { - tx_ft_put(pol_entry->ipsec); + if (pol_entry->attrs.dir == XFRM_DEV_OFFLOAD_IN) { + rx_ft_put(mdev, pol_entry->ipsec, pol_entry->attrs.family); return; } - rx_ft_put(mdev, pol_entry->ipsec, pol_entry->attrs.family); + mlx5_modify_header_dealloc(mdev, ipsec_rule->modify_hdr); + tx_ft_put(pol_entry->ipsec); } void mlx5e_accel_ipsec_fs_cleanup(struct mlx5e_ipsec *ipsec) From 7bddb659bd0fc4e9022d3a3a2260540c01856e68 Mon Sep 17 00:00:00 2001 From: Leon Romanovsky Date: Fri, 2 Dec 2022 22:14:52 +0200 Subject: [PATCH 1697/2002] net/mlx5e: Provide intermediate pointer to access IPsec struct Improve readability by providing direct pointer to struct mlx5e_ipsec. Reviewed-by: Saeed Mahameed Signed-off-by: Leon Romanovsky Signed-off-by: Steffen Klassert --- .../mellanox/mlx5/core/en_accel/ipsec_rxtx.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_rxtx.c b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_rxtx.c index 6859f1c1a831..9f07e58f7737 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_rxtx.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_rxtx.c @@ -312,23 +312,23 @@ void mlx5e_ipsec_offload_handle_rx_skb(struct net_device *netdev, struct mlx5_cqe64 *cqe) { u32 ipsec_meta_data = be32_to_cpu(cqe->ft_metadata); - struct mlx5e_priv *priv; + struct mlx5e_priv *priv = netdev_priv(netdev); + struct mlx5e_ipsec *ipsec = priv->ipsec; struct xfrm_offload *xo; struct xfrm_state *xs; struct sec_path *sp; u32 sa_handle; sa_handle = MLX5_IPSEC_METADATA_HANDLE(ipsec_meta_data); - priv = netdev_priv(netdev); sp = secpath_set(skb); if (unlikely(!sp)) { - atomic64_inc(&priv->ipsec->sw_stats.ipsec_rx_drop_sp_alloc); + atomic64_inc(&ipsec->sw_stats.ipsec_rx_drop_sp_alloc); return; } - xs = mlx5e_ipsec_sadb_rx_lookup(priv->ipsec, sa_handle); + xs = mlx5e_ipsec_sadb_rx_lookup(ipsec, sa_handle); if (unlikely(!xs)) { - atomic64_inc(&priv->ipsec->sw_stats.ipsec_rx_drop_sadb_miss); + atomic64_inc(&ipsec->sw_stats.ipsec_rx_drop_sadb_miss); return; } @@ -349,6 +349,6 @@ void mlx5e_ipsec_offload_handle_rx_skb(struct net_device *netdev, xo->status = CRYPTO_INVALID_PACKET_SYNTAX; break; default: - atomic64_inc(&priv->ipsec->sw_stats.ipsec_rx_drop_syndrome); + atomic64_inc(&ipsec->sw_stats.ipsec_rx_drop_syndrome); } } From 403b383a3ce3ebe12c26317978c34dd3d653c867 Mon Sep 17 00:00:00 2001 From: Leon Romanovsky Date: Fri, 2 Dec 2022 22:14:53 +0200 Subject: [PATCH 1698/2002] net/mlx5e: Store all XFRM SAs in Xarray Instead of performing custom hash calculations, rely on FW that returns unique identifier to every created SA. That identifier is Xarray ready, which provides better semantic with efficient access. In addition, store both TX and RX SAs to allow correlation between event generated by HW when limits are armed and XFRM states. Signed-off-by: Leon Romanovsky Signed-off-by: Steffen Klassert --- .../mellanox/mlx5/core/en_accel/ipsec.c | 82 +++++-------------- .../mellanox/mlx5/core/en_accel/ipsec.h | 8 +- .../mellanox/mlx5/core/en_accel/ipsec_rxtx.c | 12 ++- 3 files changed, 28 insertions(+), 74 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.c b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.c index 8f08dbf2206e..fe10f1a2a04a 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.c @@ -50,57 +50,6 @@ static struct mlx5e_ipsec_pol_entry *to_ipsec_pol_entry(struct xfrm_policy *x) return (struct mlx5e_ipsec_pol_entry *)x->xdo.offload_handle; } -struct xfrm_state *mlx5e_ipsec_sadb_rx_lookup(struct mlx5e_ipsec *ipsec, - unsigned int handle) -{ - struct mlx5e_ipsec_sa_entry *sa_entry; - struct xfrm_state *ret = NULL; - - rcu_read_lock(); - hash_for_each_possible_rcu(ipsec->sadb_rx, sa_entry, hlist, handle) - if (sa_entry->handle == handle) { - ret = sa_entry->x; - xfrm_state_hold(ret); - break; - } - rcu_read_unlock(); - - return ret; -} - -static int mlx5e_ipsec_sadb_rx_add(struct mlx5e_ipsec_sa_entry *sa_entry) -{ - unsigned int handle = sa_entry->ipsec_obj_id; - struct mlx5e_ipsec *ipsec = sa_entry->ipsec; - struct mlx5e_ipsec_sa_entry *_sa_entry; - unsigned long flags; - - rcu_read_lock(); - hash_for_each_possible_rcu(ipsec->sadb_rx, _sa_entry, hlist, handle) - if (_sa_entry->handle == handle) { - rcu_read_unlock(); - return -EEXIST; - } - rcu_read_unlock(); - - spin_lock_irqsave(&ipsec->sadb_rx_lock, flags); - sa_entry->handle = handle; - hash_add_rcu(ipsec->sadb_rx, &sa_entry->hlist, sa_entry->handle); - spin_unlock_irqrestore(&ipsec->sadb_rx_lock, flags); - - return 0; -} - -static void mlx5e_ipsec_sadb_rx_del(struct mlx5e_ipsec_sa_entry *sa_entry) -{ - struct mlx5e_ipsec *ipsec = sa_entry->ipsec; - unsigned long flags; - - spin_lock_irqsave(&ipsec->sadb_rx_lock, flags); - hash_del_rcu(&sa_entry->hlist); - spin_unlock_irqrestore(&ipsec->sadb_rx_lock, flags); -} - static bool mlx5e_ipsec_update_esn_state(struct mlx5e_ipsec_sa_entry *sa_entry) { struct xfrm_replay_state_esn *replay_esn; @@ -291,6 +240,7 @@ static int mlx5e_xfrm_add_state(struct xfrm_state *x) { struct mlx5e_ipsec_sa_entry *sa_entry = NULL; struct net_device *netdev = x->xso.real_dev; + struct mlx5e_ipsec *ipsec; struct mlx5e_priv *priv; int err; @@ -298,6 +248,7 @@ static int mlx5e_xfrm_add_state(struct xfrm_state *x) if (!priv->ipsec) return -EOPNOTSUPP; + ipsec = priv->ipsec; err = mlx5e_xfrm_validate_state(x); if (err) return err; @@ -309,7 +260,7 @@ static int mlx5e_xfrm_add_state(struct xfrm_state *x) } sa_entry->x = x; - sa_entry->ipsec = priv->ipsec; + sa_entry->ipsec = ipsec; /* check esn */ mlx5e_ipsec_update_esn_state(sa_entry); @@ -324,18 +275,22 @@ static int mlx5e_xfrm_add_state(struct xfrm_state *x) if (err) goto err_hw_ctx; - if (x->xso.dir == XFRM_DEV_OFFLOAD_IN) { - err = mlx5e_ipsec_sadb_rx_add(sa_entry); - if (err) - goto err_add_rule; - } else { + /* We use *_bh() variant because xfrm_timer_handler(), which runs + * in softirq context, can reach our state delete logic and we need + * xa_erase_bh() there. + */ + err = xa_insert_bh(&ipsec->sadb, sa_entry->ipsec_obj_id, sa_entry, + GFP_KERNEL); + if (err) + goto err_add_rule; + + if (x->xso.dir == XFRM_DEV_OFFLOAD_OUT) sa_entry->set_iv_op = (x->props.flags & XFRM_STATE_ESN) ? mlx5e_ipsec_set_iv_esn : mlx5e_ipsec_set_iv; - } INIT_WORK(&sa_entry->modify_work.work, _update_xfrm_state); x->xso.offload_handle = (unsigned long)sa_entry; - goto out; + return 0; err_add_rule: mlx5e_accel_ipsec_fs_del_rule(sa_entry); @@ -350,9 +305,11 @@ out: static void mlx5e_xfrm_del_state(struct xfrm_state *x) { struct mlx5e_ipsec_sa_entry *sa_entry = to_ipsec_sa_entry(x); + struct mlx5e_ipsec *ipsec = sa_entry->ipsec; + struct mlx5e_ipsec_sa_entry *old; - if (x->xso.dir == XFRM_DEV_OFFLOAD_IN) - mlx5e_ipsec_sadb_rx_del(sa_entry); + old = xa_erase_bh(&ipsec->sadb, sa_entry->ipsec_obj_id); + WARN_ON(old != sa_entry); } static void mlx5e_xfrm_free_state(struct xfrm_state *x) @@ -379,8 +336,7 @@ void mlx5e_ipsec_init(struct mlx5e_priv *priv) if (!ipsec) return; - hash_init(ipsec->sadb_rx); - spin_lock_init(&ipsec->sadb_rx_lock); + xa_init_flags(&ipsec->sadb, XA_FLAGS_ALLOC); ipsec->mdev = priv->mdev; ipsec->wq = alloc_ordered_workqueue("mlx5e_ipsec: %s", 0, priv->netdev->name); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.h b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.h index 492be255d267..724f2df14a97 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.h @@ -120,8 +120,7 @@ struct mlx5e_ipsec_aso { struct mlx5e_ipsec { struct mlx5_core_dev *mdev; - DECLARE_HASHTABLE(sadb_rx, MLX5E_IPSEC_SADB_RX_BITS); - spinlock_t sadb_rx_lock; /* Protects sadb_rx */ + struct xarray sadb; struct mlx5e_ipsec_sw_stats sw_stats; struct mlx5e_ipsec_hw_stats hw_stats; struct workqueue_struct *wq; @@ -150,9 +149,7 @@ struct mlx5e_ipsec_modify_state_work { }; struct mlx5e_ipsec_sa_entry { - struct hlist_node hlist; /* Item in SADB_RX hashtable */ struct mlx5e_ipsec_esn_state esn_state; - unsigned int handle; /* Handle in SADB_RX */ struct xfrm_state *x; struct mlx5e_ipsec *ipsec; struct mlx5_accel_esp_xfrm_attrs attrs; @@ -193,9 +190,6 @@ void mlx5e_ipsec_init(struct mlx5e_priv *priv); void mlx5e_ipsec_cleanup(struct mlx5e_priv *priv); void mlx5e_ipsec_build_netdev(struct mlx5e_priv *priv); -struct xfrm_state *mlx5e_ipsec_sadb_rx_lookup(struct mlx5e_ipsec *dev, - unsigned int handle); - void mlx5e_accel_ipsec_fs_cleanup(struct mlx5e_ipsec *ipsec); int mlx5e_accel_ipsec_fs_init(struct mlx5e_ipsec *ipsec); int mlx5e_accel_ipsec_fs_add_rule(struct mlx5e_ipsec_sa_entry *sa_entry); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_rxtx.c b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_rxtx.c index 9f07e58f7737..eab5bc718771 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_rxtx.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_rxtx.c @@ -314,8 +314,8 @@ void mlx5e_ipsec_offload_handle_rx_skb(struct net_device *netdev, u32 ipsec_meta_data = be32_to_cpu(cqe->ft_metadata); struct mlx5e_priv *priv = netdev_priv(netdev); struct mlx5e_ipsec *ipsec = priv->ipsec; + struct mlx5e_ipsec_sa_entry *sa_entry; struct xfrm_offload *xo; - struct xfrm_state *xs; struct sec_path *sp; u32 sa_handle; @@ -326,13 +326,17 @@ void mlx5e_ipsec_offload_handle_rx_skb(struct net_device *netdev, return; } - xs = mlx5e_ipsec_sadb_rx_lookup(ipsec, sa_handle); - if (unlikely(!xs)) { + rcu_read_lock(); + sa_entry = xa_load(&ipsec->sadb, sa_handle); + if (unlikely(!sa_entry)) { + rcu_read_unlock(); atomic64_inc(&ipsec->sw_stats.ipsec_rx_drop_sadb_miss); return; } + xfrm_state_hold(sa_entry->x); + rcu_read_unlock(); - sp->xvec[sp->len++] = xs; + sp->xvec[sp->len++] = sa_entry->x; sp->olen++; xo = xfrm_offload(skb); From 1ed78fc033074c55221a80498204c539a3696877 Mon Sep 17 00:00:00 2001 From: Leon Romanovsky Date: Fri, 2 Dec 2022 22:14:54 +0200 Subject: [PATCH 1699/2002] net/mlx5e: Update IPsec soft and hard limits Implement mlx5 IPsec callback to update current lifetime counters. Signed-off-by: Leon Romanovsky Signed-off-by: Steffen Klassert --- .../mellanox/mlx5/core/en_accel/ipsec.c | 63 +++++++++++++++++++ .../mellanox/mlx5/core/en_accel/ipsec.h | 6 ++ .../mlx5/core/en_accel/ipsec_offload.c | 57 +++++++++++++++++ .../net/ethernet/mellanox/mlx5/core/lib/aso.h | 1 + 4 files changed, 127 insertions(+) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.c b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.c index fe10f1a2a04a..8d0c605d4cdb 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.c @@ -83,6 +83,31 @@ static bool mlx5e_ipsec_update_esn_state(struct mlx5e_ipsec_sa_entry *sa_entry) return false; } +static void mlx5e_ipsec_init_limits(struct mlx5e_ipsec_sa_entry *sa_entry, + struct mlx5_accel_esp_xfrm_attrs *attrs) +{ + struct xfrm_state *x = sa_entry->x; + + attrs->hard_packet_limit = x->lft.hard_packet_limit; + if (x->lft.soft_packet_limit == XFRM_INF) + return; + + /* Hardware decrements hard_packet_limit counter through + * the operation. While fires an event when soft_packet_limit + * is reached. It emans that we need substitute the numbers + * in order to properly count soft limit. + * + * As an example: + * XFRM user sets soft limit is 2 and hard limit is 9 and + * expects to see soft event after 2 packets and hard event + * after 9 packets. In our case, the hard limit will be set + * to 9 and soft limit is comparator to 7 so user gets the + * soft event after 2 packeta + */ + attrs->soft_packet_limit = + x->lft.hard_packet_limit - x->lft.soft_packet_limit; +} + static void mlx5e_ipsec_build_accel_xfrm_attrs(struct mlx5e_ipsec_sa_entry *sa_entry, struct mlx5_accel_esp_xfrm_attrs *attrs) @@ -134,6 +159,8 @@ mlx5e_ipsec_build_accel_xfrm_attrs(struct mlx5e_ipsec_sa_entry *sa_entry, attrs->family = x->props.family; attrs->type = x->xso.type; attrs->reqid = x->props.reqid; + + mlx5e_ipsec_init_limits(sa_entry, attrs); } static inline int mlx5e_xfrm_validate_state(struct xfrm_state *x) @@ -222,6 +249,21 @@ static inline int mlx5e_xfrm_validate_state(struct xfrm_state *x) netdev_info(netdev, "Cannot offload without reqid\n"); return -EINVAL; } + + if (x->lft.hard_byte_limit != XFRM_INF || + x->lft.soft_byte_limit != XFRM_INF) { + netdev_info(netdev, + "Device doesn't support limits in bytes\n"); + return -EINVAL; + } + + if (x->lft.soft_packet_limit >= x->lft.hard_packet_limit && + x->lft.hard_packet_limit != XFRM_INF) { + /* XFRM stack doesn't prevent such configuration :(. */ + netdev_info(netdev, + "Hard packet limit must be greater than soft one\n"); + return -EINVAL; + } } return 0; } @@ -415,6 +457,26 @@ static void mlx5e_xfrm_advance_esn_state(struct xfrm_state *x) queue_work(sa_entry->ipsec->wq, &modify_work->work); } +static void mlx5e_xfrm_update_curlft(struct xfrm_state *x) +{ + struct mlx5e_ipsec_sa_entry *sa_entry = to_ipsec_sa_entry(x); + int err; + + lockdep_assert_held(&x->lock); + + if (sa_entry->attrs.soft_packet_limit == XFRM_INF) + /* Limits are not configured, as soft limit + * must be lowever than hard limit. + */ + return; + + err = mlx5e_ipsec_aso_query(sa_entry); + if (err) + return; + + mlx5e_ipsec_aso_update_curlft(sa_entry, &x->curlft.packets); +} + static int mlx5e_xfrm_validate_policy(struct xfrm_policy *x) { struct net_device *netdev = x->xdo.real_dev; @@ -526,6 +588,7 @@ static const struct xfrmdev_ops mlx5e_ipsec_packet_xfrmdev_ops = { .xdo_dev_offload_ok = mlx5e_ipsec_offload_ok, .xdo_dev_state_advance_esn = mlx5e_xfrm_advance_esn_state, + .xdo_dev_state_update_curlft = mlx5e_xfrm_update_curlft, .xdo_dev_policy_add = mlx5e_xfrm_add_policy, .xdo_dev_policy_free = mlx5e_xfrm_free_policy, }; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.h b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.h index 724f2df14a97..aac1e6a83631 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.h @@ -78,6 +78,8 @@ struct mlx5_accel_esp_xfrm_attrs { u32 replay_window; u32 authsize; u32 reqid; + u64 hard_packet_limit; + u64 soft_packet_limit; }; enum mlx5_ipsec_cap { @@ -208,6 +210,10 @@ void mlx5_accel_esp_modify_xfrm(struct mlx5e_ipsec_sa_entry *sa_entry, int mlx5e_ipsec_aso_init(struct mlx5e_ipsec *ipsec); void mlx5e_ipsec_aso_cleanup(struct mlx5e_ipsec *ipsec); +int mlx5e_ipsec_aso_query(struct mlx5e_ipsec_sa_entry *sa_entry); +void mlx5e_ipsec_aso_update_curlft(struct mlx5e_ipsec_sa_entry *sa_entry, + u64 *packets); + void mlx5e_accel_ipsec_fs_read_stats(struct mlx5e_priv *priv, void *ipsec_stats); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_offload.c b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_offload.c index fc88454aaf8d..8790558ea859 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_offload.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_offload.c @@ -83,6 +83,20 @@ static void mlx5e_ipsec_packet_setup(void *obj, u32 pdn, MLX5_SET(ipsec_obj, obj, aso_return_reg, MLX5_IPSEC_ASO_REG_C_4_5); if (attrs->dir == XFRM_DEV_OFFLOAD_OUT) MLX5_SET(ipsec_aso, aso_ctx, mode, MLX5_IPSEC_ASO_INC_SN); + + if (attrs->hard_packet_limit != XFRM_INF) { + MLX5_SET(ipsec_aso, aso_ctx, remove_flow_pkt_cnt, + lower_32_bits(attrs->hard_packet_limit)); + MLX5_SET(ipsec_aso, aso_ctx, hard_lft_arm, 1); + MLX5_SET(ipsec_aso, aso_ctx, remove_flow_enable, 1); + } + + if (attrs->soft_packet_limit != XFRM_INF) { + MLX5_SET(ipsec_aso, aso_ctx, remove_flow_soft_lft, + lower_32_bits(attrs->soft_packet_limit)); + + MLX5_SET(ipsec_aso, aso_ctx, soft_lft_arm, 1); + } } static int mlx5_create_ipsec_obj(struct mlx5e_ipsec_sa_entry *sa_entry) @@ -298,3 +312,46 @@ void mlx5e_ipsec_aso_cleanup(struct mlx5e_ipsec *ipsec) DMA_BIDIRECTIONAL); kfree(aso); } + +int mlx5e_ipsec_aso_query(struct mlx5e_ipsec_sa_entry *sa_entry) +{ + struct mlx5e_ipsec *ipsec = sa_entry->ipsec; + struct mlx5e_ipsec_aso *aso = ipsec->aso; + struct mlx5_core_dev *mdev = ipsec->mdev; + struct mlx5_wqe_aso_ctrl_seg *ctrl; + struct mlx5e_hw_objs *res; + struct mlx5_aso_wqe *wqe; + u8 ds_cnt; + + res = &mdev->mlx5e_res.hw_objs; + + memset(aso->ctx, 0, sizeof(aso->ctx)); + wqe = mlx5_aso_get_wqe(aso->aso); + ds_cnt = DIV_ROUND_UP(sizeof(*wqe), MLX5_SEND_WQE_DS); + mlx5_aso_build_wqe(aso->aso, ds_cnt, wqe, sa_entry->ipsec_obj_id, + MLX5_ACCESS_ASO_OPC_MOD_IPSEC); + + ctrl = &wqe->aso_ctrl; + ctrl->va_l = + cpu_to_be32(lower_32_bits(aso->dma_addr) | ASO_CTRL_READ_EN); + ctrl->va_h = cpu_to_be32(upper_32_bits(aso->dma_addr)); + ctrl->l_key = cpu_to_be32(res->mkey); + + mlx5_aso_post_wqe(aso->aso, false, &wqe->ctrl); + return mlx5_aso_poll_cq(aso->aso, false); +} + +void mlx5e_ipsec_aso_update_curlft(struct mlx5e_ipsec_sa_entry *sa_entry, + u64 *packets) +{ + struct mlx5e_ipsec *ipsec = sa_entry->ipsec; + struct mlx5e_ipsec_aso *aso = ipsec->aso; + u64 hard_cnt; + + hard_cnt = MLX5_GET(ipsec_aso, aso->ctx, remove_flow_pkt_cnt); + /* HW decresases the limit till it reaches zero to fire an avent. + * We need to fix the calculations, so the returned count is a total + * number of passed packets and not how much left. + */ + *packets = sa_entry->attrs.hard_packet_limit - hard_cnt; +} diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lib/aso.h b/drivers/net/ethernet/mellanox/mlx5/core/lib/aso.h index c8fc3c838642..afb078bbb8ef 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/lib/aso.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/lib/aso.h @@ -15,6 +15,7 @@ #define MLX5_WQE_CTRL_WQE_OPC_MOD_SHIFT 24 #define MLX5_MACSEC_ASO_DS_CNT (DIV_ROUND_UP(sizeof(struct mlx5_aso_wqe), MLX5_SEND_WQE_DS)) +#define ASO_CTRL_READ_EN BIT(0) struct mlx5_wqe_aso_ctrl_seg { __be32 va_h; __be32 va_l; /* include read_enable */ From 8c582ddfbb473c1d799c40b5140aed81278e2837 Mon Sep 17 00:00:00 2001 From: Leon Romanovsky Date: Fri, 2 Dec 2022 22:14:55 +0200 Subject: [PATCH 1700/2002] net/mlx5e: Handle hardware IPsec limits events Enable object changed event to signal IPsec about hitting soft and hard limits. Signed-off-by: Leon Romanovsky Signed-off-by: Steffen Klassert --- .../mellanox/mlx5/core/en_accel/ipsec.c | 2 +- .../mellanox/mlx5/core/en_accel/ipsec.h | 19 +++- .../mlx5/core/en_accel/ipsec_offload.c | 97 ++++++++++++++++++- drivers/net/ethernet/mellanox/mlx5/core/eq.c | 5 + 4 files changed, 118 insertions(+), 5 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.c b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.c index 8d0c605d4cdb..4f176bd8395a 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.c @@ -470,7 +470,7 @@ static void mlx5e_xfrm_update_curlft(struct xfrm_state *x) */ return; - err = mlx5e_ipsec_aso_query(sa_entry); + err = mlx5e_ipsec_aso_query(sa_entry, NULL); if (err) return; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.h b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.h index aac1e6a83631..e7f21e449268 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.h @@ -34,8 +34,6 @@ #ifndef __MLX5E_IPSEC_H__ #define __MLX5E_IPSEC_H__ -#ifdef CONFIG_MLX5_EN_IPSEC - #include #include #include @@ -114,10 +112,21 @@ struct mlx5e_ipsec_sw_stats { struct mlx5e_ipsec_rx; struct mlx5e_ipsec_tx; +struct mlx5e_ipsec_work { + struct work_struct work; + struct mlx5e_ipsec *ipsec; + u32 id; +}; + struct mlx5e_ipsec_aso { u8 ctx[MLX5_ST_SZ_BYTES(ipsec_aso)]; dma_addr_t dma_addr; struct mlx5_aso *aso; + /* IPsec ASO caches data on every query call, + * so in nested calls, we can use this boolean to save + * recursive calls to mlx5e_ipsec_aso_query() + */ + u8 use_cache : 1; }; struct mlx5e_ipsec { @@ -131,6 +140,7 @@ struct mlx5e_ipsec { struct mlx5e_ipsec_rx *rx_ipv6; struct mlx5e_ipsec_tx *tx; struct mlx5e_ipsec_aso *aso; + struct notifier_block nb; }; struct mlx5e_ipsec_esn_state { @@ -188,6 +198,8 @@ struct mlx5e_ipsec_pol_entry { struct mlx5_accel_pol_xfrm_attrs attrs; }; +#ifdef CONFIG_MLX5_EN_IPSEC + void mlx5e_ipsec_init(struct mlx5e_priv *priv); void mlx5e_ipsec_cleanup(struct mlx5e_priv *priv); void mlx5e_ipsec_build_netdev(struct mlx5e_priv *priv); @@ -210,7 +222,8 @@ void mlx5_accel_esp_modify_xfrm(struct mlx5e_ipsec_sa_entry *sa_entry, int mlx5e_ipsec_aso_init(struct mlx5e_ipsec *ipsec); void mlx5e_ipsec_aso_cleanup(struct mlx5e_ipsec *ipsec); -int mlx5e_ipsec_aso_query(struct mlx5e_ipsec_sa_entry *sa_entry); +int mlx5e_ipsec_aso_query(struct mlx5e_ipsec_sa_entry *sa_entry, + struct mlx5_wqe_aso_ctrl_seg *data); void mlx5e_ipsec_aso_update_curlft(struct mlx5e_ipsec_sa_entry *sa_entry, u64 *packets); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_offload.c b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_offload.c index 8790558ea859..1b5014ffa257 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_offload.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_offload.c @@ -260,6 +260,73 @@ void mlx5_accel_esp_modify_xfrm(struct mlx5e_ipsec_sa_entry *sa_entry, memcpy(&sa_entry->attrs, attrs, sizeof(sa_entry->attrs)); } +static void mlx5e_ipsec_handle_event(struct work_struct *_work) +{ + struct mlx5e_ipsec_work *work = + container_of(_work, struct mlx5e_ipsec_work, work); + struct mlx5_accel_esp_xfrm_attrs *attrs; + struct mlx5e_ipsec_sa_entry *sa_entry; + struct mlx5e_ipsec_aso *aso; + struct mlx5e_ipsec *ipsec; + int ret; + + sa_entry = xa_load(&work->ipsec->sadb, work->id); + if (!sa_entry) + goto out; + + ipsec = sa_entry->ipsec; + aso = ipsec->aso; + attrs = &sa_entry->attrs; + + spin_lock(&sa_entry->x->lock); + ret = mlx5e_ipsec_aso_query(sa_entry, NULL); + if (ret) + goto unlock; + + aso->use_cache = true; + if (attrs->soft_packet_limit != XFRM_INF) + if (!MLX5_GET(ipsec_aso, aso->ctx, soft_lft_arm) || + !MLX5_GET(ipsec_aso, aso->ctx, hard_lft_arm) || + !MLX5_GET(ipsec_aso, aso->ctx, remove_flow_enable)) + xfrm_state_check_expire(sa_entry->x); + aso->use_cache = false; + +unlock: + spin_unlock(&sa_entry->x->lock); +out: + kfree(work); +} + +static int mlx5e_ipsec_event(struct notifier_block *nb, unsigned long event, + void *data) +{ + struct mlx5e_ipsec *ipsec = container_of(nb, struct mlx5e_ipsec, nb); + struct mlx5_eqe_obj_change *object; + struct mlx5e_ipsec_work *work; + struct mlx5_eqe *eqe = data; + u16 type; + + if (event != MLX5_EVENT_TYPE_OBJECT_CHANGE) + return NOTIFY_DONE; + + object = &eqe->data.obj_change; + type = be16_to_cpu(object->obj_type); + + if (type != MLX5_GENERAL_OBJECT_TYPES_IPSEC) + return NOTIFY_DONE; + + work = kmalloc(sizeof(*work), GFP_ATOMIC); + if (!work) + return NOTIFY_DONE; + + INIT_WORK(&work->work, mlx5e_ipsec_handle_event); + work->ipsec = ipsec; + work->id = be32_to_cpu(object->obj_id); + + queue_work(ipsec->wq, &work->work); + return NOTIFY_OK; +} + int mlx5e_ipsec_aso_init(struct mlx5e_ipsec *ipsec) { struct mlx5_core_dev *mdev = ipsec->mdev; @@ -287,6 +354,9 @@ int mlx5e_ipsec_aso_init(struct mlx5e_ipsec *ipsec) goto err_aso_create; } + ipsec->nb.notifier_call = mlx5e_ipsec_event; + mlx5_notifier_register(mdev, &ipsec->nb); + ipsec->aso = aso; return 0; @@ -307,13 +377,33 @@ void mlx5e_ipsec_aso_cleanup(struct mlx5e_ipsec *ipsec) aso = ipsec->aso; pdev = mlx5_core_dma_dev(mdev); + mlx5_notifier_unregister(mdev, &ipsec->nb); mlx5_aso_destroy(aso->aso); dma_unmap_single(pdev, aso->dma_addr, sizeof(aso->ctx), DMA_BIDIRECTIONAL); kfree(aso); } -int mlx5e_ipsec_aso_query(struct mlx5e_ipsec_sa_entry *sa_entry) +static void mlx5e_ipsec_aso_copy(struct mlx5_wqe_aso_ctrl_seg *ctrl, + struct mlx5_wqe_aso_ctrl_seg *data) +{ + if (!data) + return; + + ctrl->data_mask_mode = data->data_mask_mode; + ctrl->condition_1_0_operand = data->condition_1_0_operand; + ctrl->condition_1_0_offset = data->condition_1_0_offset; + ctrl->data_offset_condition_operand = data->data_offset_condition_operand; + ctrl->condition_0_data = data->condition_0_data; + ctrl->condition_0_mask = data->condition_0_mask; + ctrl->condition_1_data = data->condition_1_data; + ctrl->condition_1_mask = data->condition_1_mask; + ctrl->bitwise_data = data->bitwise_data; + ctrl->data_mask = data->data_mask; +} + +int mlx5e_ipsec_aso_query(struct mlx5e_ipsec_sa_entry *sa_entry, + struct mlx5_wqe_aso_ctrl_seg *data) { struct mlx5e_ipsec *ipsec = sa_entry->ipsec; struct mlx5e_ipsec_aso *aso = ipsec->aso; @@ -323,6 +413,10 @@ int mlx5e_ipsec_aso_query(struct mlx5e_ipsec_sa_entry *sa_entry) struct mlx5_aso_wqe *wqe; u8 ds_cnt; + lockdep_assert_held(&sa_entry->x->lock); + if (aso->use_cache) + return 0; + res = &mdev->mlx5e_res.hw_objs; memset(aso->ctx, 0, sizeof(aso->ctx)); @@ -336,6 +430,7 @@ int mlx5e_ipsec_aso_query(struct mlx5e_ipsec_sa_entry *sa_entry) cpu_to_be32(lower_32_bits(aso->dma_addr) | ASO_CTRL_READ_EN); ctrl->va_h = cpu_to_be32(upper_32_bits(aso->dma_addr)); ctrl->l_key = cpu_to_be32(res->mkey); + mlx5e_ipsec_aso_copy(ctrl, data); mlx5_aso_post_wqe(aso->aso, false, &wqe->ctrl); return mlx5_aso_poll_cq(aso->aso, false); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eq.c b/drivers/net/ethernet/mellanox/mlx5/core/eq.c index a0242dc15741..8f7580fec193 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/eq.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/eq.c @@ -19,6 +19,7 @@ #include "diag/fw_tracer.h" #include "mlx5_irq.h" #include "devlink.h" +#include "en_accel/ipsec.h" enum { MLX5_EQE_OWNER_INIT_VAL = 0x1, @@ -578,6 +579,10 @@ static void gather_async_events_mask(struct mlx5_core_dev *dev, u64 mask[4]) if (MLX5_CAP_MACSEC(dev, log_max_macsec_offload)) async_event_mask |= (1ull << MLX5_EVENT_TYPE_OBJECT_CHANGE); + if (mlx5_ipsec_device_caps(dev) & MLX5_IPSEC_CAP_PACKET_OFFLOAD) + async_event_mask |= + (1ull << MLX5_EVENT_TYPE_OBJECT_CHANGE); + mask[0] = async_event_mask; if (MLX5_CAP_GEN(dev, event_cap)) From cee137a634318bfbda18ee5af45d300153b57fa5 Mon Sep 17 00:00:00 2001 From: Leon Romanovsky Date: Fri, 2 Dec 2022 22:14:56 +0200 Subject: [PATCH 1701/2002] net/mlx5e: Handle ESN update events Extend event logic to update ESN state (esn_msb, esn_overlap) for an IPsec Offload context. Reviewed-by: Saeed Mahameed Signed-off-by: Leon Romanovsky Signed-off-by: Steffen Klassert --- .../mellanox/mlx5/core/en_accel/ipsec.c | 5 +-- .../mellanox/mlx5/core/en_accel/ipsec.h | 2 + .../mlx5/core/en_accel/ipsec_offload.c | 44 +++++++++++++++++++ 3 files changed, 48 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.c b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.c index 4f176bd8395a..f5f930ea3f0f 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.c @@ -108,9 +108,8 @@ static void mlx5e_ipsec_init_limits(struct mlx5e_ipsec_sa_entry *sa_entry, x->lft.hard_packet_limit - x->lft.soft_packet_limit; } -static void -mlx5e_ipsec_build_accel_xfrm_attrs(struct mlx5e_ipsec_sa_entry *sa_entry, - struct mlx5_accel_esp_xfrm_attrs *attrs) +void mlx5e_ipsec_build_accel_xfrm_attrs(struct mlx5e_ipsec_sa_entry *sa_entry, + struct mlx5_accel_esp_xfrm_attrs *attrs) { struct xfrm_state *x = sa_entry->x; struct aes_gcm_keymat *aes_gcm = &attrs->aes_gcm; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.h b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.h index e7f21e449268..a92e19c4c499 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.h @@ -230,6 +230,8 @@ void mlx5e_ipsec_aso_update_curlft(struct mlx5e_ipsec_sa_entry *sa_entry, void mlx5e_accel_ipsec_fs_read_stats(struct mlx5e_priv *priv, void *ipsec_stats); +void mlx5e_ipsec_build_accel_xfrm_attrs(struct mlx5e_ipsec_sa_entry *sa_entry, + struct mlx5_accel_esp_xfrm_attrs *attrs); static inline struct mlx5_core_dev * mlx5e_ipsec_sa2dev(struct mlx5e_ipsec_sa_entry *sa_entry) { diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_offload.c b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_offload.c index 1b5014ffa257..8e3614218fc4 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_offload.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_offload.c @@ -6,6 +6,10 @@ #include "ipsec.h" #include "lib/mlx5.h" +enum { + MLX5_IPSEC_ASO_REMOVE_FLOW_PKT_CNT_OFFSET, +}; + u32 mlx5_ipsec_device_caps(struct mlx5_core_dev *mdev) { u32 caps = 0; @@ -260,6 +264,39 @@ void mlx5_accel_esp_modify_xfrm(struct mlx5e_ipsec_sa_entry *sa_entry, memcpy(&sa_entry->attrs, attrs, sizeof(sa_entry->attrs)); } +static void +mlx5e_ipsec_aso_update_esn(struct mlx5e_ipsec_sa_entry *sa_entry, + const struct mlx5_accel_esp_xfrm_attrs *attrs) +{ + struct mlx5_wqe_aso_ctrl_seg data = {}; + + data.data_mask_mode = MLX5_ASO_DATA_MASK_MODE_BITWISE_64BIT << 6; + data.condition_1_0_operand = MLX5_ASO_ALWAYS_TRUE | MLX5_ASO_ALWAYS_TRUE + << 4; + data.data_offset_condition_operand = MLX5_IPSEC_ASO_REMOVE_FLOW_PKT_CNT_OFFSET; + data.bitwise_data = cpu_to_be64(BIT_ULL(54)); + data.data_mask = data.bitwise_data; + + mlx5e_ipsec_aso_query(sa_entry, &data); +} + +static void mlx5e_ipsec_update_esn_state(struct mlx5e_ipsec_sa_entry *sa_entry, + u32 mode_param) +{ + struct mlx5_accel_esp_xfrm_attrs attrs = {}; + + if (mode_param < MLX5E_IPSEC_ESN_SCOPE_MID) { + sa_entry->esn_state.esn++; + sa_entry->esn_state.overlap = 0; + } else { + sa_entry->esn_state.overlap = 1; + } + + mlx5e_ipsec_build_accel_xfrm_attrs(sa_entry, &attrs); + mlx5_accel_esp_modify_xfrm(sa_entry, &attrs); + mlx5e_ipsec_aso_update_esn(sa_entry, &attrs); +} + static void mlx5e_ipsec_handle_event(struct work_struct *_work) { struct mlx5e_ipsec_work *work = @@ -284,6 +321,13 @@ static void mlx5e_ipsec_handle_event(struct work_struct *_work) goto unlock; aso->use_cache = true; + if (attrs->esn_trigger && + !MLX5_GET(ipsec_aso, aso->ctx, esn_event_arm)) { + u32 mode_param = MLX5_GET(ipsec_aso, aso->ctx, mode_parameter); + + mlx5e_ipsec_update_esn_state(sa_entry, mode_param); + } + if (attrs->soft_packet_limit != XFRM_INF) if (!MLX5_GET(ipsec_aso, aso->ctx, soft_lft_arm) || !MLX5_GET(ipsec_aso, aso->ctx, hard_lft_arm) || From 37d244ad183f42f91910e47984a7636e970e9d1b Mon Sep 17 00:00:00 2001 From: Leon Romanovsky Date: Fri, 2 Dec 2022 22:14:57 +0200 Subject: [PATCH 1702/2002] net/mlx5e: Open mlx5 driver to accept IPsec packet offload Enable configuration of IPsec packet offload through XFRM state add interface together with moving specific to IPsec packet mode limitations to specific switch-case section. Reviewed-by: Raed Salem Signed-off-by: Leon Romanovsky Signed-off-by: Steffen Klassert --- .../mellanox/mlx5/core/en_accel/ipsec.c | 41 ++++++++++++++----- 1 file changed, 31 insertions(+), 10 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.c b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.c index f5f930ea3f0f..bb9023957f74 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.c @@ -191,11 +191,6 @@ static inline int mlx5e_xfrm_validate_state(struct xfrm_state *x) netdev_info(netdev, "Only IPv4/6 xfrm states may be offloaded\n"); return -EINVAL; } - if (x->props.mode != XFRM_MODE_TRANSPORT && - x->props.mode != XFRM_MODE_TUNNEL) { - dev_info(&netdev->dev, "Only transport and tunnel xfrm states may be offloaded\n"); - return -EINVAL; - } if (x->id.proto != IPPROTO_ESP) { netdev_info(netdev, "Only ESP xfrm state may be offloaded\n"); return -EINVAL; @@ -229,11 +224,32 @@ static inline int mlx5e_xfrm_validate_state(struct xfrm_state *x) netdev_info(netdev, "Cannot offload xfrm states with geniv other than seqiv\n"); return -EINVAL; } - if (x->xso.type != XFRM_DEV_OFFLOAD_CRYPTO) { - netdev_info(netdev, "Unsupported xfrm offload type\n"); - return -EINVAL; - } - if (x->xso.type == XFRM_DEV_OFFLOAD_PACKET) { + switch (x->xso.type) { + case XFRM_DEV_OFFLOAD_CRYPTO: + if (!(mlx5_ipsec_device_caps(priv->mdev) & + MLX5_IPSEC_CAP_CRYPTO)) { + netdev_info(netdev, "Crypto offload is not supported\n"); + return -EINVAL; + } + + if (x->props.mode != XFRM_MODE_TRANSPORT && + x->props.mode != XFRM_MODE_TUNNEL) { + netdev_info(netdev, "Only transport and tunnel xfrm states may be offloaded\n"); + return -EINVAL; + } + break; + case XFRM_DEV_OFFLOAD_PACKET: + if (!(mlx5_ipsec_device_caps(priv->mdev) & + MLX5_IPSEC_CAP_PACKET_OFFLOAD)) { + netdev_info(netdev, "Packet offload is not supported\n"); + return -EINVAL; + } + + if (x->props.mode != XFRM_MODE_TRANSPORT) { + netdev_info(netdev, "Only transport xfrm states may be offloaded in packet mode\n"); + return -EINVAL; + } + if (x->replay_esn && x->replay_esn->replay_window != 32 && x->replay_esn->replay_window != 64 && x->replay_esn->replay_window != 128 && @@ -263,6 +279,11 @@ static inline int mlx5e_xfrm_validate_state(struct xfrm_state *x) "Hard packet limit must be greater than soft one\n"); return -EINVAL; } + break; + default: + netdev_info(netdev, "Unsupported xfrm offload type %d\n", + x->xso.type); + return -EINVAL; } return 0; } From 5107778d0061db58b1683bb412418d5b3cf0b0e0 Mon Sep 17 00:00:00 2001 From: Jiapeng Chong Date: Tue, 29 Nov 2022 14:24:07 +0800 Subject: [PATCH 1703/2002] wifi: ipw2x00: Remove some unused functions Functions write_nic_auto_inc_address() and write_nic_dword_auto_inc() are defined in the ipw2100.c file, but not called elsewhere, so remove these unused functions. drivers/net/wireless/intel/ipw2x00/ipw2100.c:427:20: warning: unused function 'write_nic_dword_auto_inc'. Link: https://bugzilla.openanolis.cn/show_bug.cgi?id=3285 Reported-by: Abaci Robot Signed-off-by: Jiapeng Chong Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20221129062407.83157-1-jiapeng.chong@linux.alibaba.com --- drivers/net/wireless/intel/ipw2x00/ipw2100.c | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/drivers/net/wireless/intel/ipw2x00/ipw2100.c b/drivers/net/wireless/intel/ipw2x00/ipw2100.c index b0f23cf1a621..0812db8936f1 100644 --- a/drivers/net/wireless/intel/ipw2x00/ipw2100.c +++ b/drivers/net/wireless/intel/ipw2x00/ipw2100.c @@ -418,17 +418,6 @@ static inline void write_nic_byte(struct net_device *dev, u32 addr, u8 val) write_register_byte(dev, IPW_REG_INDIRECT_ACCESS_DATA, val); } -static inline void write_nic_auto_inc_address(struct net_device *dev, u32 addr) -{ - write_register(dev, IPW_REG_AUTOINCREMENT_ADDRESS, - addr & IPW_REG_INDIRECT_ADDR_MASK); -} - -static inline void write_nic_dword_auto_inc(struct net_device *dev, u32 val) -{ - write_register(dev, IPW_REG_AUTOINCREMENT_DATA, val); -} - static void write_nic_memory(struct net_device *dev, u32 addr, u32 len, const u8 * buf) { From 76821aad49cebc69eca86800f41e8133104e91d3 Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Tue, 29 Nov 2022 14:54:40 +0100 Subject: [PATCH 1704/2002] wifi: brcmfmac: add function to unbind device to bus layer api Introduce a new bus callback .remove() which will unbind the device from the driver. This allows the common driver layer to stop handling a device. Reviewed-by: Hante Meuleman Reviewed-by: Pieter-Paul Giesberts Reviewed-by: Franky Lin Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20221129135446.151065-2-arend.vanspriel@broadcom.com --- .../net/wireless/broadcom/brcm80211/brcmfmac/bus.h | 13 +++++++++++++ .../net/wireless/broadcom/brcm80211/brcmfmac/sdio.c | 12 +++++++++++- 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bus.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bus.h index 60f5645aead3..256456e38108 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bus.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bus.h @@ -8,6 +8,7 @@ #include #include +#include #include "debug.h" /* IDs of the 6 default common rings of msgbuf protocol */ @@ -74,6 +75,7 @@ struct brcmf_bus_dcmd { * @get_ramsize: obtain size of device memory. * @get_memdump: obtain device memory dump in provided buffer. * @get_blob: obtain a firmware blob. + * @remove: initiate unbind of the device. * * This structure provides an abstract interface towards the * bus specific driver. For control messages to common driver @@ -94,6 +96,7 @@ struct brcmf_bus_ops { enum brcmf_blob_type type); void (*debugfs_create)(struct device *dev); int (*reset)(struct device *dev); + void (*remove)(struct device *dev); }; @@ -257,6 +260,16 @@ int brcmf_bus_reset(struct brcmf_bus *bus) return bus->ops->reset(bus->dev); } +static inline void brcmf_bus_remove(struct brcmf_bus *bus) +{ + if (!bus->ops->remove) { + device_release_driver(bus->dev); + return; + } + + bus->ops->remove(bus->dev); +} + /* * interface functions from common layer */ diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c index 244ba48cc304..c949051c4bc4 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c @@ -4171,6 +4171,15 @@ static int brcmf_sdio_bus_reset(struct device *dev) return 0; } +static void brcmf_sdio_bus_remove(struct device *dev) +{ + struct brcmf_bus *bus_if = dev_get_drvdata(dev); + struct brcmf_sdio_dev *sdiod = bus_if->bus_priv.sdio; + + device_release_driver(&sdiod->func2->dev); + device_release_driver(&sdiod->func1->dev); +} + static const struct brcmf_bus_ops brcmf_sdio_bus_ops = { .stop = brcmf_sdio_bus_stop, .preinit = brcmf_sdio_bus_preinit, @@ -4183,7 +4192,8 @@ static const struct brcmf_bus_ops brcmf_sdio_bus_ops = { .get_memdump = brcmf_sdio_bus_get_memdump, .get_blob = brcmf_sdio_get_blob, .debugfs_create = brcmf_sdio_debugfs_create, - .reset = brcmf_sdio_bus_reset + .reset = brcmf_sdio_bus_reset, + .remove = brcmf_sdio_bus_remove, }; #define BRCMF_SDIO_FW_CODE 0 From da6d9c8ecd00e20218461007948f2b0a8e7fa242 Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Tue, 29 Nov 2022 14:54:41 +0100 Subject: [PATCH 1705/2002] wifi: brcmfmac: add firmware vendor info in driver info In order to determine the vendor that released a firmware image for a specific device, the device table now sets the vendor identifier in driver info and it is stored in struct brcmf_bus::fwvid during probe. Reviewed-by: Hante Meuleman Reviewed-by: Pieter-Paul Giesberts Reviewed-by: Franky Lin Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20221129135446.151065-3-arend.vanspriel@broadcom.com --- .../broadcom/brcm80211/brcmfmac/bcmsdh.c | 6 ++++- .../broadcom/brcm80211/brcmfmac/bus.h | 11 +++++++- .../broadcom/brcm80211/brcmfmac/pcie.c | 20 ++++++++++---- .../broadcom/brcm80211/brcmfmac/usb.c | 27 +++++++++++++------ 4 files changed, 49 insertions(+), 15 deletions(-) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c index d0daef674e72..fa919432b1c6 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c @@ -960,7 +960,10 @@ out: } #define BRCMF_SDIO_DEVICE(dev_id) \ - {SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, dev_id)} + { \ + SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, dev_id), \ + .driver_data = BRCMF_FWVENDOR_WCC \ + } /* devices we support, null terminated */ static const struct sdio_device_id brcmf_sdmmc_ids[] = { @@ -1051,6 +1054,7 @@ static int brcmf_ops_sdio_probe(struct sdio_func *func, sdiodev->bus_if = bus_if; bus_if->bus_priv.sdio = sdiodev; bus_if->proto_type = BRCMF_PROTO_BCDC; + bus_if->fwvid = id->driver_data; dev_set_drvdata(&func->dev, bus_if); dev_set_drvdata(&sdiodev->func1->dev, bus_if); sdiodev->dev = &sdiodev->func1->dev; diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bus.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bus.h index 256456e38108..79fe0a49471c 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bus.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bus.h @@ -31,6 +31,13 @@ /* The maximum console interval value (5 mins) */ #define MAX_CONSOLE_INTERVAL (5 * 60) +enum brcmf_fwvendor { + BRCMF_FWVENDOR_WCC, + /* keep last */ + BRCMF_FWVENDOR_NUM, + BRCMF_FWVENDOR_INVALID +}; + /* The level of bus communication with the dongle */ enum brcmf_bus_state { BRCMF_BUS_DOWN, /* Not ready for frame transfers */ @@ -144,9 +151,10 @@ struct brcmf_bus_stats { * @stats: statistics shared between common and bus layer. * @maxctl: maximum size for rxctl request message. * @chip: device identifier of the dongle chip. + * @chiprev: revision of the dongle chip. + * @fwvid: firmware vendor-support identifier of the device. * @always_use_fws_queue: bus wants use queue also when fwsignal is inactive. * @wowl_supported: is wowl supported by bus driver. - * @chiprev: revision of the dongle chip. * @msgbuf: msgbuf protocol parameters provided by bus layer. */ struct brcmf_bus { @@ -163,6 +171,7 @@ struct brcmf_bus { uint maxctl; u32 chip; u32 chiprev; + enum brcmf_fwvendor fwvid; bool always_use_fws_queue; bool wowl_supported; diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c index cf564adc612a..094bb84ed72f 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c @@ -2389,6 +2389,7 @@ brcmf_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id) bus->bus_priv.pcie = pcie_bus_dev; bus->ops = &brcmf_pcie_bus_ops; bus->proto_type = BRCMF_PROTO_MSGBUF; + bus->fwvid = id->driver_data; bus->chip = devinfo->coreid; bus->wowl_supported = pci_pme_capable(pdev, PCI_D3hot); dev_set_drvdata(&pdev->dev, bus); @@ -2570,11 +2571,20 @@ static const struct dev_pm_ops brcmf_pciedrvr_pm = { #endif /* CONFIG_PM */ -#define BRCMF_PCIE_DEVICE(dev_id) { BRCM_PCIE_VENDOR_ID_BROADCOM, dev_id,\ - PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_NETWORK_OTHER << 8, 0xffff00, 0 } -#define BRCMF_PCIE_DEVICE_SUB(dev_id, subvend, subdev) { \ - BRCM_PCIE_VENDOR_ID_BROADCOM, dev_id,\ - subvend, subdev, PCI_CLASS_NETWORK_OTHER << 8, 0xffff00, 0 } +#define BRCMF_PCIE_DEVICE(dev_id) \ + { \ + BRCM_PCIE_VENDOR_ID_BROADCOM, (dev_id), \ + PCI_ANY_ID, PCI_ANY_ID, \ + PCI_CLASS_NETWORK_OTHER << 8, 0xffff00, \ + BRCMF_FWVENDOR_WCC \ + } +#define BRCMF_PCIE_DEVICE_SUB(dev_id, subvend, subdev) \ + { \ + BRCM_PCIE_VENDOR_ID_BROADCOM, (dev_id), \ + (subvend), (subdev), \ + PCI_CLASS_NETWORK_OTHER << 8, 0xffff00, \ + BRCMF_FWVENDOR_WCC \ + } static const struct pci_device_id brcmf_pcie_devid_table[] = { BRCMF_PCIE_DEVICE(BRCM_PCIE_4350_DEVICE_ID), diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c index 85e18fb9c497..246843aeb696 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c @@ -1240,7 +1240,8 @@ brcmf_usb_prepare_fw_request(struct brcmf_usbdev_info *devinfo) return fwreq; } -static int brcmf_usb_probe_cb(struct brcmf_usbdev_info *devinfo) +static int brcmf_usb_probe_cb(struct brcmf_usbdev_info *devinfo, + enum brcmf_fwvendor fwvid) { struct brcmf_bus *bus = NULL; struct brcmf_usbdev *bus_pub = NULL; @@ -1265,6 +1266,7 @@ static int brcmf_usb_probe_cb(struct brcmf_usbdev_info *devinfo) dev_set_drvdata(dev, bus); bus->ops = &brcmf_usb_bus_ops; bus->proto_type = BRCMF_PROTO_BCDC; + bus->fwvid = fwvid; bus->always_use_fws_queue = true; #ifdef CONFIG_PM bus->wowl_supported = true; @@ -1423,7 +1425,7 @@ brcmf_usb_probe(struct usb_interface *intf, const struct usb_device_id *id) else brcmf_dbg(USB, "Broadcom full speed USB WLAN interface detected\n"); - ret = brcmf_usb_probe_cb(devinfo); + ret = brcmf_usb_probe_cb(devinfo, id->driver_info); if (ret) goto fail; @@ -1511,14 +1513,23 @@ static int brcmf_usb_reset_resume(struct usb_interface *intf) return ret; } -#define BRCMF_USB_DEVICE(dev_id) \ - { USB_DEVICE(BRCM_USB_VENDOR_ID_BROADCOM, dev_id) } +#define BRCMF_USB_DEVICE(dev_id) \ + { \ + USB_DEVICE(BRCM_USB_VENDOR_ID_BROADCOM, dev_id), \ + .driver_info = BRCMF_FWVENDOR_WCC \ + } -#define LINKSYS_USB_DEVICE(dev_id) \ - { USB_DEVICE(BRCM_USB_VENDOR_ID_LINKSYS, dev_id) } +#define LINKSYS_USB_DEVICE(dev_id) \ + { \ + USB_DEVICE(BRCM_USB_VENDOR_ID_LINKSYS, dev_id), \ + .driver_info = BRCMF_FWVENDOR_WCC \ + } -#define CYPRESS_USB_DEVICE(dev_id) \ - { USB_DEVICE(CY_USB_VENDOR_ID_CYPRESS, dev_id) } +#define CYPRESS_USB_DEVICE(dev_id) \ + { \ + USB_DEVICE(CY_USB_VENDOR_ID_CYPRESS, dev_id), \ + .driver_info = BRCMF_FWVENDOR_WCC \ + } static const struct usb_device_id brcmf_usb_devid_table[] = { BRCMF_USB_DEVICE(BRCM_USB_43143_DEVICE_ID), From d6a5c562214f26e442c8ec3ff1e28e16675d1bcf Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Tue, 29 Nov 2022 14:54:42 +0100 Subject: [PATCH 1706/2002] wifi: brcmfmac: add support for vendor-specific firmware api The driver is being used by multiple vendors who develop the firmware api independently. So far the firmware api as used by the driver has not diverged (yet). This change adds framework for supporting multiple firmware apis. The vendor-specific support code has to provide a number of callback operations. Right now it is only attach and detach callbacks so no real functionality as the api is still common. This code only adds WCC variant anyway, which is selected for all devices right now. The vendor-specific part will be built in a separate module when the driver is configured to be built as a module through Kconfig, ie. when CONFIG_BRCMFMAC=m. Reviewed-by: Hante Meuleman Reviewed-by: Pieter-Paul Giesberts Reviewed-by: Franky Lin Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20221129135446.151065-4-arend.vanspriel@broadcom.com --- .../broadcom/brcm80211/brcmfmac/Makefile | 7 + .../broadcom/brcm80211/brcmfmac/bus.h | 4 + .../broadcom/brcm80211/brcmfmac/core.c | 9 + .../broadcom/brcm80211/brcmfmac/core.h | 2 + .../broadcom/brcm80211/brcmfmac/fwvid.c | 190 ++++++++++++++++++ .../broadcom/brcm80211/brcmfmac/fwvid.h | 46 +++++ .../broadcom/brcm80211/brcmfmac/wcc/Makefile | 12 ++ .../broadcom/brcm80211/brcmfmac/wcc/core.c | 27 +++ .../broadcom/brcm80211/brcmfmac/wcc/module.c | 26 +++ .../broadcom/brcm80211/brcmfmac/wcc/vops.h | 11 + 10 files changed, 334 insertions(+) create mode 100644 drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwvid.c create mode 100644 drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwvid.h create mode 100644 drivers/net/wireless/broadcom/brcm80211/brcmfmac/wcc/Makefile create mode 100644 drivers/net/wireless/broadcom/brcm80211/brcmfmac/wcc/core.c create mode 100644 drivers/net/wireless/broadcom/brcm80211/brcmfmac/wcc/module.c create mode 100644 drivers/net/wireless/broadcom/brcm80211/brcmfmac/wcc/vops.h diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/Makefile b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/Makefile index 13c13504a6e8..e7ceea7af13f 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/Makefile +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/Makefile @@ -20,6 +20,7 @@ brcmfmac-objs += \ common.o \ core.o \ firmware.o \ + fwvid.o \ feature.o \ btcoex.o \ vendor.o \ @@ -47,3 +48,9 @@ brcmfmac-$(CONFIG_OF) += \ of.o brcmfmac-$(CONFIG_DMI) += \ dmi.o + +ifeq ($(CONFIG_BRCMFMAC),m) +obj-m += wcc/ +else +brcmfmac-$(CONFIG_BRCMFMAC) += wcc/core.o +endif diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bus.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bus.h index 79fe0a49471c..26be49ee8c90 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bus.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bus.h @@ -155,7 +155,9 @@ struct brcmf_bus_stats { * @fwvid: firmware vendor-support identifier of the device. * @always_use_fws_queue: bus wants use queue also when fwsignal is inactive. * @wowl_supported: is wowl supported by bus driver. + * @ops: callbacks for this bus instance. * @msgbuf: msgbuf protocol parameters provided by bus layer. + * @list: member used to add this bus instance to linked list. */ struct brcmf_bus { union { @@ -177,6 +179,8 @@ struct brcmf_bus { const struct brcmf_bus_ops *ops; struct brcmf_bus_msgbuf *msgbuf; + + struct list_head list; }; /* diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c index d354f79fd0ac..584431150f7c 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c @@ -18,6 +18,7 @@ #include "core.h" #include "bus.h" +#include "fwvid.h" #include "debug.h" #include "fwil_types.h" #include "p2p.h" @@ -1332,6 +1333,12 @@ int brcmf_attach(struct device *dev) /* Link to bus module */ drvr->hdrlen = 0; + ret = brcmf_fwvid_attach(drvr); + if (ret != 0) { + bphy_err(drvr, "brcmf_fwvid_attach failed\n"); + goto fail; + } + /* Attach and link in the protocol */ ret = brcmf_proto_attach(drvr); if (ret != 0) { @@ -1443,6 +1450,8 @@ void brcmf_detach(struct device *dev) brcmf_cfg80211_detach(drvr->config); drvr->config = NULL; } + + brcmf_fwvid_detach(drvr); } void brcmf_free(struct device *dev) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.h index 2e71b5c2a975..a98b86982502 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.h @@ -137,6 +137,8 @@ struct brcmf_pub { u8 clmver[BRCMF_DCMD_SMLEN]; u8 sta_mac_idx; + const struct brcmf_fwvid_ops *vops; + void *vdata; }; /* forward declarations */ diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwvid.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwvid.c new file mode 100644 index 000000000000..f5cbb09b1c83 --- /dev/null +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwvid.c @@ -0,0 +1,190 @@ +// SPDX-License-Identifier: ISC +/* + * Copyright (c) 2022 Broadcom Corporation + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "core.h" +#include "bus.h" +#include "debug.h" +#include "fwvid.h" + +#include "wcc/vops.h" + +struct brcmf_fwvid_entry { + const char *name; + const struct brcmf_fwvid_ops *vops; + struct list_head drvr_list; +#if IS_MODULE(CONFIG_BRCMFMAC) + struct module *vmod; + struct completion reg_done; +#endif +}; + +static DEFINE_MUTEX(fwvid_list_lock); + +#if IS_MODULE(CONFIG_BRCMFMAC) +#define FWVID_ENTRY_INIT(_vid, _name) \ + [BRCMF_FWVENDOR_ ## _vid] = { \ + .name = #_name, \ + .reg_done = COMPLETION_INITIALIZER(fwvid_list[BRCMF_FWVENDOR_ ## _vid].reg_done), \ + .drvr_list = LIST_HEAD_INIT(fwvid_list[BRCMF_FWVENDOR_ ## _vid].drvr_list), \ + } +#else +#define FWVID_ENTRY_INIT(_vid, _name) \ + [BRCMF_FWVENDOR_ ## _vid] = { \ + .name = #_name, \ + .drvr_list = LIST_HEAD_INIT(fwvid_list[BRCMF_FWVENDOR_ ## _vid].drvr_list), \ + .vops = _vid ## _VOPS \ + } +#endif /* IS_MODULE(CONFIG_BRCMFMAC) */ + +static struct brcmf_fwvid_entry fwvid_list[BRCMF_FWVENDOR_NUM] = { + FWVID_ENTRY_INIT(WCC, wcc), +}; + +#if IS_MODULE(CONFIG_BRCMFMAC) +static int brcmf_fwvid_request_module(enum brcmf_fwvendor fwvid) +{ + int ret; + + if (!fwvid_list[fwvid].vmod) { + struct completion *reg_done = &fwvid_list[fwvid].reg_done; + + mutex_unlock(&fwvid_list_lock); + + ret = request_module("brcmfmac-%s", fwvid_list[fwvid].name); + if (ret) + goto fail; + + ret = wait_for_completion_interruptible(reg_done); + if (ret) + goto fail; + + mutex_lock(&fwvid_list_lock); + } + return 0; + +fail: + brcmf_err("mod=%s: failed %d\n", fwvid_list[fwvid].name, ret); + return ret; +} + +int brcmf_fwvid_register_vendor(enum brcmf_fwvendor fwvid, struct module *vmod, + const struct brcmf_fwvid_ops *vops) +{ + if (fwvid >= BRCMF_FWVENDOR_NUM) + return -ERANGE; + + if (WARN_ON(!vmod) || WARN_ON(!vops) || + WARN_ON(!vops->attach) || WARN_ON(!vops->detach)) + return -EINVAL; + + if (WARN_ON(fwvid_list[fwvid].vmod)) + return -EEXIST; + + brcmf_dbg(TRACE, "mod=%s: enter\n", fwvid_list[fwvid].name); + + mutex_lock(&fwvid_list_lock); + + fwvid_list[fwvid].vmod = vmod; + fwvid_list[fwvid].vops = vops; + + mutex_unlock(&fwvid_list_lock); + + complete_all(&fwvid_list[fwvid].reg_done); + + return 0; +} +EXPORT_SYMBOL(brcmf_fwvid_register_vendor); + +int brcmf_fwvid_unregister_vendor(enum brcmf_fwvendor fwvid, struct module *mod) +{ + struct brcmf_bus *bus, *tmp; + + if (fwvid >= BRCMF_FWVENDOR_NUM) + return -ERANGE; + + if (WARN_ON(fwvid_list[fwvid].vmod != mod)) + return -ENOENT; + + mutex_lock(&fwvid_list_lock); + + list_for_each_entry_safe(bus, tmp, &fwvid_list[fwvid].drvr_list, list) { + mutex_unlock(&fwvid_list_lock); + + brcmf_dbg(INFO, "mod=%s: removing %s\n", fwvid_list[fwvid].name, + dev_name(bus->dev)); + brcmf_bus_remove(bus); + + mutex_lock(&fwvid_list_lock); + } + + fwvid_list[fwvid].vmod = NULL; + fwvid_list[fwvid].vops = NULL; + reinit_completion(&fwvid_list[fwvid].reg_done); + + brcmf_dbg(TRACE, "mod=%s: exit\n", fwvid_list[fwvid].name); + mutex_unlock(&fwvid_list_lock); + + return 0; +} +EXPORT_SYMBOL(brcmf_fwvid_unregister_vendor); +#else +static inline int brcmf_fwvid_request_module(enum brcmf_fwvendor fwvid) +{ + return 0; +} +#endif + +int brcmf_fwvid_attach_ops(struct brcmf_pub *drvr) +{ + enum brcmf_fwvendor fwvid = drvr->bus_if->fwvid; + int ret; + + if (fwvid >= ARRAY_SIZE(fwvid_list)) + return -ERANGE; + + brcmf_dbg(TRACE, "mod=%s: enter: dev %s\n", fwvid_list[fwvid].name, + dev_name(drvr->bus_if->dev)); + + mutex_lock(&fwvid_list_lock); + + ret = brcmf_fwvid_request_module(fwvid); + if (ret) + return ret; + + drvr->vops = fwvid_list[fwvid].vops; + list_add(&drvr->bus_if->list, &fwvid_list[fwvid].drvr_list); + + mutex_unlock(&fwvid_list_lock); + + return ret; +} + +void brcmf_fwvid_detach_ops(struct brcmf_pub *drvr) +{ + enum brcmf_fwvendor fwvid = drvr->bus_if->fwvid; + + if (fwvid >= ARRAY_SIZE(fwvid_list)) + return; + + brcmf_dbg(TRACE, "mod=%s: enter: dev %s\n", fwvid_list[fwvid].name, + dev_name(drvr->bus_if->dev)); + + mutex_lock(&fwvid_list_lock); + + drvr->vops = NULL; + list_del(&drvr->bus_if->list); + + mutex_unlock(&fwvid_list_lock); +} diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwvid.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwvid.h new file mode 100644 index 000000000000..6b3aec190023 --- /dev/null +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwvid.h @@ -0,0 +1,46 @@ +/* SPDX-License-Identifier: ISC */ +/* + * Copyright (c) 2022 Broadcom Corporation + */ +#ifndef FWVID_H_ +#define FWVID_H_ + +#include "firmware.h" + +struct brcmf_pub; + +struct brcmf_fwvid_ops { + int (*attach)(struct brcmf_pub *drvr); + void (*detach)(struct brcmf_pub *drvr); +}; + +/* exported functions */ +int brcmf_fwvid_register_vendor(enum brcmf_fwvendor fwvid, struct module *mod, + const struct brcmf_fwvid_ops *ops); +int brcmf_fwvid_unregister_vendor(enum brcmf_fwvendor fwvid, struct module *mod); + +/* core driver functions */ +int brcmf_fwvid_attach_ops(struct brcmf_pub *drvr); +void brcmf_fwvid_detach_ops(struct brcmf_pub *drvr); + +static inline int brcmf_fwvid_attach(struct brcmf_pub *drvr) +{ + int ret; + + ret = brcmf_fwvid_attach_ops(drvr); + if (ret) + return ret; + + return drvr->vops->attach(drvr); +} + +static inline void brcmf_fwvid_detach(struct brcmf_pub *drvr) +{ + if (!drvr->vops) + return; + + drvr->vops->detach(drvr); + brcmf_fwvid_detach_ops(drvr); +} + +#endif /* FWVID_H_ */ diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/wcc/Makefile b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/wcc/Makefile new file mode 100644 index 000000000000..7f455a19a2b1 --- /dev/null +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/wcc/Makefile @@ -0,0 +1,12 @@ +# SPDX-License-Identifier: ISC +# +# Copyright (c) 2022 Broadcom Corporation + +ccflags-y += \ + -I $(srctree)/$(src) \ + -I $(srctree)/$(src)/.. \ + -I $(srctree)/$(src)/../../include + +obj-m += brcmfmac-wcc.o +brcmfmac-wcc-objs += \ + core.o module.o diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/wcc/core.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/wcc/core.c new file mode 100644 index 000000000000..02de99818efa --- /dev/null +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/wcc/core.c @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: ISC +/* + * Copyright (c) 2022 Broadcom Corporation + */ +#include +#include +#include +#include +#include + +#include "vops.h" + +static int brcmf_wcc_attach(struct brcmf_pub *drvr) +{ + pr_err("%s: executing\n", __func__); + return 0; +} + +static void brcmf_wcc_detach(struct brcmf_pub *drvr) +{ + pr_err("%s: executing\n", __func__); +} + +const struct brcmf_fwvid_ops brcmf_wcc_ops = { + .attach = brcmf_wcc_attach, + .detach = brcmf_wcc_detach, +}; diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/wcc/module.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/wcc/module.c new file mode 100644 index 000000000000..23e3a4557880 --- /dev/null +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/wcc/module.c @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: ISC +/* + * Copyright (c) 2022 Broadcom Corporation + */ +#include +#include +#include +#include + +#include "vops.h" + +static int __init brcmf_wcc_init(void) +{ + return brcmf_fwvid_register_vendor(BRCMF_FWVENDOR_WCC, THIS_MODULE, + &brcmf_wcc_ops); +} + +static void __exit brcmf_wcc_exit(void) +{ + brcmf_fwvid_unregister_vendor(BRCMF_FWVENDOR_WCC, THIS_MODULE); +} + +MODULE_LICENSE("Dual BSD/GPL"); + +module_init(brcmf_wcc_init); +module_exit(brcmf_wcc_exit); diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/wcc/vops.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/wcc/vops.h new file mode 100644 index 000000000000..3aec44f80600 --- /dev/null +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/wcc/vops.h @@ -0,0 +1,11 @@ +/* SPDX-License-Identifier: ISC */ +/* + * Copyright (c) 2022 Broadcom Corporation + */ +#ifndef _BRCMFMAC_WCC_VOPS_H +#define _BRCMFMAC_WCC_VOPS_H + +extern const struct brcmf_fwvid_ops brcmf_wcc_ops; +#define WCC_VOPS (&brcmf_wcc_ops) + +#endif /* _BRCMFMAC_WCC_VOPS_H */ From f74f1ec22dc232be0296739148d126e9158eadf9 Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Tue, 29 Nov 2022 14:54:43 +0100 Subject: [PATCH 1707/2002] wifi: brcmfmac: add support for Cypress firmware api Cypress uses the brcmfmac driver and releases firmware which will likely diverge over time (or already has). So adding support for handling that. Reviewed-by: Hante Meuleman Reviewed-by: Pieter-Paul Giesberts Reviewed-by: Franky Lin Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20221129135446.151065-5-arend.vanspriel@broadcom.com --- .../broadcom/brcm80211/brcmfmac/Makefile | 2 + .../broadcom/brcm80211/brcmfmac/bcmsdh.c | 48 +++++++++---------- .../broadcom/brcm80211/brcmfmac/bus.h | 1 + .../broadcom/brcm80211/brcmfmac/cyw/Makefile | 12 +++++ .../broadcom/brcm80211/brcmfmac/cyw/core.c | 27 +++++++++++ .../broadcom/brcm80211/brcmfmac/cyw/module.c | 26 ++++++++++ .../broadcom/brcm80211/brcmfmac/cyw/vops.h | 11 +++++ .../broadcom/brcm80211/brcmfmac/fwvid.c | 2 + 8 files changed, 105 insertions(+), 24 deletions(-) create mode 100644 drivers/net/wireless/broadcom/brcm80211/brcmfmac/cyw/Makefile create mode 100644 drivers/net/wireless/broadcom/brcm80211/brcmfmac/cyw/core.c create mode 100644 drivers/net/wireless/broadcom/brcm80211/brcmfmac/cyw/module.c create mode 100644 drivers/net/wireless/broadcom/brcm80211/brcmfmac/cyw/vops.h diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/Makefile b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/Makefile index e7ceea7af13f..64fd77a378fd 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/Makefile +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/Makefile @@ -51,6 +51,8 @@ brcmfmac-$(CONFIG_DMI) += \ ifeq ($(CONFIG_BRCMFMAC),m) obj-m += wcc/ +obj-m += cyw/ else brcmfmac-$(CONFIG_BRCMFMAC) += wcc/core.o +brcmfmac-$(CONFIG_BRCMFMAC) += cyw/core.o endif diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c index fa919432b1c6..b7c918f241c9 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c @@ -959,36 +959,36 @@ out: return ret; } -#define BRCMF_SDIO_DEVICE(dev_id) \ +#define BRCMF_SDIO_DEVICE(dev_id, fw_vend) \ { \ SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, dev_id), \ - .driver_data = BRCMF_FWVENDOR_WCC \ + .driver_data = BRCMF_FWVENDOR_ ## fw_vend \ } /* devices we support, null terminated */ static const struct sdio_device_id brcmf_sdmmc_ids[] = { - BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_43143), - BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_43241), - BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4329), - BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4330), - BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4334), - BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_43340), - BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_43341), - BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_43362), - BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_43364), - BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4335_4339), - BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4339), - BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_43430), - BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4345), - BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_43455), - BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4354), - BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4356), - BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4359), - BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_CYPRESS_4373), - BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_CYPRESS_43012), - BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_CYPRESS_43439), - BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_CYPRESS_43752), - BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_CYPRESS_89359), + BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_43143, WCC), + BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_43241, WCC), + BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4329, WCC), + BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4330, WCC), + BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4334, WCC), + BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_43340, WCC), + BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_43341, WCC), + BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_43362, WCC), + BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_43364, WCC), + BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4335_4339, WCC), + BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4339, WCC), + BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_43430, WCC), + BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4345, WCC), + BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_43455, WCC), + BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4354, WCC), + BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4356, WCC), + BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4359, WCC), + BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_CYPRESS_4373, CYW), + BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_CYPRESS_43012, CYW), + BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_CYPRESS_43439, CYW), + BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_CYPRESS_43752, CYW), + BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_CYPRESS_89359, CYW), { /* end: all zeroes */ } }; MODULE_DEVICE_TABLE(sdio, brcmf_sdmmc_ids); diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bus.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bus.h index 26be49ee8c90..72036fec9a8e 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bus.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bus.h @@ -33,6 +33,7 @@ enum brcmf_fwvendor { BRCMF_FWVENDOR_WCC, + BRCMF_FWVENDOR_CYW, /* keep last */ BRCMF_FWVENDOR_NUM, BRCMF_FWVENDOR_INVALID diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cyw/Makefile b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cyw/Makefile new file mode 100644 index 000000000000..5e1fddaff79e --- /dev/null +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cyw/Makefile @@ -0,0 +1,12 @@ +# SPDX-License-Identifier: ISC +# +# Copyright (c) 2022 Broadcom Corporation + +ccflags-y += \ + -I $(srctree)/$(src) \ + -I $(srctree)/$(src)/.. \ + -I $(srctree)/$(src)/../../include + +obj-m += brcmfmac-cyw.o +brcmfmac-cyw-objs += \ + core.o module.o diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cyw/core.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cyw/core.c new file mode 100644 index 000000000000..b75652ba9359 --- /dev/null +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cyw/core.c @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: ISC +/* + * Copyright (c) 2022 Broadcom Corporation + */ +#include +#include +#include +#include +#include + +#include "vops.h" + +static int brcmf_cyw_attach(struct brcmf_pub *drvr) +{ + pr_err("%s: executing\n", __func__); + return 0; +} + +static void brcmf_cyw_detach(struct brcmf_pub *drvr) +{ + pr_err("%s: executing\n", __func__); +} + +const struct brcmf_fwvid_ops brcmf_cyw_ops = { + .attach = brcmf_cyw_attach, + .detach = brcmf_cyw_detach, +}; diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cyw/module.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cyw/module.c new file mode 100644 index 000000000000..34294724a1f8 --- /dev/null +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cyw/module.c @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: ISC +/* + * Copyright (c) 2022 Broadcom Corporation + */ +#include +#include +#include +#include + +#include "vops.h" + +static int __init brcmf_cyw_init(void) +{ + return brcmf_fwvid_register_vendor(BRCMF_FWVENDOR_CYW, THIS_MODULE, + &brcmf_cyw_ops); +} + +static void __exit brcmf_cyw_exit(void) +{ + brcmf_fwvid_unregister_vendor(BRCMF_FWVENDOR_CYW, THIS_MODULE); +} + +MODULE_LICENSE("Dual BSD/GPL"); + +module_init(brcmf_cyw_init); +module_exit(brcmf_cyw_exit); diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cyw/vops.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cyw/vops.h new file mode 100644 index 000000000000..870b5bead436 --- /dev/null +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cyw/vops.h @@ -0,0 +1,11 @@ +/* SPDX-License-Identifier: ISC */ +/* + * Copyright (c) 2022 Broadcom Corporation + */ +#ifndef _BRCMFMAC_CYW_VOPS_H +#define _BRCMFMAC_CYW_VOPS_H + +extern const struct brcmf_fwvid_ops brcmf_cyw_ops; +#define CYW_VOPS (&brcmf_cyw_ops) + +#endif /* _BRCMFMAC_CYW_VOPS_H */ diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwvid.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwvid.c index f5cbb09b1c83..ee23eb1809c4 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwvid.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwvid.c @@ -19,6 +19,7 @@ #include "fwvid.h" #include "wcc/vops.h" +#include "cyw/vops.h" struct brcmf_fwvid_entry { const char *name; @@ -50,6 +51,7 @@ static DEFINE_MUTEX(fwvid_list_lock); static struct brcmf_fwvid_entry fwvid_list[BRCMF_FWVENDOR_NUM] = { FWVID_ENTRY_INIT(WCC, wcc), + FWVID_ENTRY_INIT(CYW, cyw), }; #if IS_MODULE(CONFIG_BRCMFMAC) From b1d94be570c28be9bda6931e300e4ff74be82452 Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Tue, 29 Nov 2022 14:54:44 +0100 Subject: [PATCH 1708/2002] wifi: brcmfmac: add support Broadcom BCA firmware api Broadcom BCA division develops its own firmware api and as such will likely diverge over time (or already has). Add support for handling this. Reviewed-by: Hante Meuleman Reviewed-by: Pieter-Paul Giesberts Reviewed-by: Franky Lin Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20221129135446.151065-6-arend.vanspriel@broadcom.com --- .../broadcom/brcm80211/brcmfmac/Makefile | 2 + .../broadcom/brcm80211/brcmfmac/bca/Makefile | 12 ++++ .../broadcom/brcm80211/brcmfmac/bca/core.c | 27 +++++++++ .../broadcom/brcm80211/brcmfmac/bca/module.c | 26 +++++++++ .../broadcom/brcm80211/brcmfmac/bca/vops.h | 11 ++++ .../broadcom/brcm80211/brcmfmac/bus.h | 1 + .../broadcom/brcm80211/brcmfmac/fwvid.c | 2 + .../broadcom/brcm80211/brcmfmac/pcie.c | 58 +++++++++---------- 8 files changed, 110 insertions(+), 29 deletions(-) create mode 100644 drivers/net/wireless/broadcom/brcm80211/brcmfmac/bca/Makefile create mode 100644 drivers/net/wireless/broadcom/brcm80211/brcmfmac/bca/core.c create mode 100644 drivers/net/wireless/broadcom/brcm80211/brcmfmac/bca/module.c create mode 100644 drivers/net/wireless/broadcom/brcm80211/brcmfmac/bca/vops.h diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/Makefile b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/Makefile index 64fd77a378fd..0e996cf24f88 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/Makefile +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/Makefile @@ -52,7 +52,9 @@ brcmfmac-$(CONFIG_DMI) += \ ifeq ($(CONFIG_BRCMFMAC),m) obj-m += wcc/ obj-m += cyw/ +obj-m += bca/ else brcmfmac-$(CONFIG_BRCMFMAC) += wcc/core.o brcmfmac-$(CONFIG_BRCMFMAC) += cyw/core.o +brcmfmac-$(CONFIG_BRCMFMAC) += bca/core.o endif diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bca/Makefile b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bca/Makefile new file mode 100644 index 000000000000..46098705e236 --- /dev/null +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bca/Makefile @@ -0,0 +1,12 @@ +# SPDX-License-Identifier: ISC +# +# Copyright (c) 2022 Broadcom Corporation + +ccflags-y += \ + -I $(srctree)/$(src) \ + -I $(srctree)/$(src)/.. \ + -I $(srctree)/$(src)/../../include + +obj-m += brcmfmac-bca.o +brcmfmac-bca-objs += \ + core.o module.o diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bca/core.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bca/core.c new file mode 100644 index 000000000000..ac3a36fa3640 --- /dev/null +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bca/core.c @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: ISC +/* + * Copyright (c) 2022 Broadcom Corporation + */ +#include +#include +#include +#include +#include + +#include "vops.h" + +static int brcmf_bca_attach(struct brcmf_pub *drvr) +{ + pr_err("%s: executing\n", __func__); + return 0; +} + +static void brcmf_bca_detach(struct brcmf_pub *drvr) +{ + pr_err("%s: executing\n", __func__); +} + +const struct brcmf_fwvid_ops brcmf_bca_ops = { + .attach = brcmf_bca_attach, + .detach = brcmf_bca_detach, +}; diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bca/module.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bca/module.c new file mode 100644 index 000000000000..790116a735c7 --- /dev/null +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bca/module.c @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: ISC +/* + * Copyright (c) 2022 Broadcom Corporation + */ +#include +#include +#include +#include + +#include "vops.h" + +static int __init brcmf_bca_init(void) +{ + return brcmf_fwvid_register_vendor(BRCMF_FWVENDOR_BCA, THIS_MODULE, + &brcmf_bca_ops); +} + +static void __exit brcmf_bca_exit(void) +{ + brcmf_fwvid_unregister_vendor(BRCMF_FWVENDOR_BCA, THIS_MODULE); +} + +MODULE_LICENSE("Dual BSD/GPL"); + +module_init(brcmf_bca_init); +module_exit(brcmf_bca_exit); diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bca/vops.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bca/vops.h new file mode 100644 index 000000000000..7897e6b6eefb --- /dev/null +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bca/vops.h @@ -0,0 +1,11 @@ +/* SPDX-License-Identifier: ISC */ +/* + * Copyright (c) 2022 Broadcom Corporation + */ +#ifndef _BRCMFMAC_BCA_VOPS_H +#define _BRCMFMAC_BCA_VOPS_H + +extern const struct brcmf_fwvid_ops brcmf_bca_ops; +#define BCA_VOPS (&brcmf_bca_ops) + +#endif /* _BRCMFMAC_BCA_VOPS_H */ diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bus.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bus.h index 72036fec9a8e..501136e011b5 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bus.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bus.h @@ -34,6 +34,7 @@ enum brcmf_fwvendor { BRCMF_FWVENDOR_WCC, BRCMF_FWVENDOR_CYW, + BRCMF_FWVENDOR_BCA, /* keep last */ BRCMF_FWVENDOR_NUM, BRCMF_FWVENDOR_INVALID diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwvid.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwvid.c index ee23eb1809c4..274d512cbdad 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwvid.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwvid.c @@ -20,6 +20,7 @@ #include "wcc/vops.h" #include "cyw/vops.h" +#include "bca/vops.h" struct brcmf_fwvid_entry { const char *name; @@ -52,6 +53,7 @@ static DEFINE_MUTEX(fwvid_list_lock); static struct brcmf_fwvid_entry fwvid_list[BRCMF_FWVENDOR_NUM] = { FWVID_ENTRY_INIT(WCC, wcc), FWVID_ENTRY_INIT(CYW, cyw), + FWVID_ENTRY_INIT(BCA, bca), }; #if IS_MODULE(CONFIG_BRCMFMAC) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c index 094bb84ed72f..3e121e56e800 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c @@ -2571,47 +2571,47 @@ static const struct dev_pm_ops brcmf_pciedrvr_pm = { #endif /* CONFIG_PM */ -#define BRCMF_PCIE_DEVICE(dev_id) \ +#define BRCMF_PCIE_DEVICE(dev_id, fw_vend) \ { \ BRCM_PCIE_VENDOR_ID_BROADCOM, (dev_id), \ PCI_ANY_ID, PCI_ANY_ID, \ PCI_CLASS_NETWORK_OTHER << 8, 0xffff00, \ - BRCMF_FWVENDOR_WCC \ + BRCMF_FWVENDOR_ ## fw_vend \ } -#define BRCMF_PCIE_DEVICE_SUB(dev_id, subvend, subdev) \ +#define BRCMF_PCIE_DEVICE_SUB(dev_id, subvend, subdev, fw_vend) \ { \ BRCM_PCIE_VENDOR_ID_BROADCOM, (dev_id), \ (subvend), (subdev), \ PCI_CLASS_NETWORK_OTHER << 8, 0xffff00, \ - BRCMF_FWVENDOR_WCC \ + BRCMF_FWVENDOR_ ## fw_vend \ } static const struct pci_device_id brcmf_pcie_devid_table[] = { - BRCMF_PCIE_DEVICE(BRCM_PCIE_4350_DEVICE_ID), - BRCMF_PCIE_DEVICE_SUB(0x4355, BRCM_PCIE_VENDOR_ID_BROADCOM, 0x4355), - BRCMF_PCIE_DEVICE(BRCM_PCIE_4354_RAW_DEVICE_ID), - BRCMF_PCIE_DEVICE(BRCM_PCIE_4356_DEVICE_ID), - BRCMF_PCIE_DEVICE(BRCM_PCIE_43567_DEVICE_ID), - BRCMF_PCIE_DEVICE(BRCM_PCIE_43570_DEVICE_ID), - BRCMF_PCIE_DEVICE(BRCM_PCIE_43570_RAW_DEVICE_ID), - BRCMF_PCIE_DEVICE(BRCM_PCIE_4358_DEVICE_ID), - BRCMF_PCIE_DEVICE(BRCM_PCIE_4359_DEVICE_ID), - BRCMF_PCIE_DEVICE(BRCM_PCIE_43602_DEVICE_ID), - BRCMF_PCIE_DEVICE(BRCM_PCIE_43602_2G_DEVICE_ID), - BRCMF_PCIE_DEVICE(BRCM_PCIE_43602_5G_DEVICE_ID), - BRCMF_PCIE_DEVICE(BRCM_PCIE_43602_RAW_DEVICE_ID), - BRCMF_PCIE_DEVICE(BRCM_PCIE_4364_DEVICE_ID), - BRCMF_PCIE_DEVICE(BRCM_PCIE_4365_DEVICE_ID), - BRCMF_PCIE_DEVICE(BRCM_PCIE_4365_2G_DEVICE_ID), - BRCMF_PCIE_DEVICE(BRCM_PCIE_4365_5G_DEVICE_ID), - BRCMF_PCIE_DEVICE_SUB(0x4365, BRCM_PCIE_VENDOR_ID_BROADCOM, 0x4365), - BRCMF_PCIE_DEVICE(BRCM_PCIE_4366_DEVICE_ID), - BRCMF_PCIE_DEVICE(BRCM_PCIE_4366_2G_DEVICE_ID), - BRCMF_PCIE_DEVICE(BRCM_PCIE_4366_5G_DEVICE_ID), - BRCMF_PCIE_DEVICE(BRCM_PCIE_4371_DEVICE_ID), - BRCMF_PCIE_DEVICE(BRCM_PCIE_4378_DEVICE_ID), - BRCMF_PCIE_DEVICE(CY_PCIE_89459_DEVICE_ID), - BRCMF_PCIE_DEVICE(CY_PCIE_89459_RAW_DEVICE_ID), + BRCMF_PCIE_DEVICE(BRCM_PCIE_4350_DEVICE_ID, WCC), + BRCMF_PCIE_DEVICE_SUB(0x4355, BRCM_PCIE_VENDOR_ID_BROADCOM, 0x4355, WCC), + BRCMF_PCIE_DEVICE(BRCM_PCIE_4354_RAW_DEVICE_ID, WCC), + BRCMF_PCIE_DEVICE(BRCM_PCIE_4356_DEVICE_ID, WCC), + BRCMF_PCIE_DEVICE(BRCM_PCIE_43567_DEVICE_ID, WCC), + BRCMF_PCIE_DEVICE(BRCM_PCIE_43570_DEVICE_ID, WCC), + BRCMF_PCIE_DEVICE(BRCM_PCIE_43570_RAW_DEVICE_ID, WCC), + BRCMF_PCIE_DEVICE(BRCM_PCIE_4358_DEVICE_ID, WCC), + BRCMF_PCIE_DEVICE(BRCM_PCIE_4359_DEVICE_ID, WCC), + BRCMF_PCIE_DEVICE(BRCM_PCIE_43602_DEVICE_ID, WCC), + BRCMF_PCIE_DEVICE(BRCM_PCIE_43602_2G_DEVICE_ID, WCC), + BRCMF_PCIE_DEVICE(BRCM_PCIE_43602_5G_DEVICE_ID, WCC), + BRCMF_PCIE_DEVICE(BRCM_PCIE_43602_RAW_DEVICE_ID, WCC), + BRCMF_PCIE_DEVICE(BRCM_PCIE_4364_DEVICE_ID, BCA), + BRCMF_PCIE_DEVICE(BRCM_PCIE_4365_DEVICE_ID, BCA), + BRCMF_PCIE_DEVICE(BRCM_PCIE_4365_2G_DEVICE_ID, BCA), + BRCMF_PCIE_DEVICE(BRCM_PCIE_4365_5G_DEVICE_ID, BCA), + BRCMF_PCIE_DEVICE_SUB(0x4365, BRCM_PCIE_VENDOR_ID_BROADCOM, 0x4365, BCA), + BRCMF_PCIE_DEVICE(BRCM_PCIE_4366_DEVICE_ID, BCA), + BRCMF_PCIE_DEVICE(BRCM_PCIE_4366_2G_DEVICE_ID, BCA), + BRCMF_PCIE_DEVICE(BRCM_PCIE_4366_5G_DEVICE_ID, BCA), + BRCMF_PCIE_DEVICE(BRCM_PCIE_4371_DEVICE_ID, WCC), + BRCMF_PCIE_DEVICE(BRCM_PCIE_4378_DEVICE_ID, WCC), + BRCMF_PCIE_DEVICE(CY_PCIE_89459_DEVICE_ID, CYW), + BRCMF_PCIE_DEVICE(CY_PCIE_89459_RAW_DEVICE_ID, CYW), { /* end: all zeroes */ } }; From 7205f9f2fc55be611dbd3364f6b1b361aa21a52d Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Tue, 29 Nov 2022 14:54:45 +0100 Subject: [PATCH 1709/2002] wifi: brcmfmac: add vendor name in revinfo debugfs file Upon probe the driver determines the vendor supporting the device. Expose this information in the revinfo debugfs file. Reviewed-by: Hante Meuleman Reviewed-by: Pieter-Paul Giesberts Reviewed-by: Franky Lin Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20221129135446.151065-7-arend.vanspriel@broadcom.com --- drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c | 3 ++- drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwvid.c | 5 +++++ drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwvid.h | 1 + 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c index 584431150f7c..83ea251cfcec 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c @@ -1136,7 +1136,8 @@ static int brcmf_revinfo_read(struct seq_file *s, void *data) seq_printf(s, "vendorid: 0x%04x\n", ri->vendorid); seq_printf(s, "deviceid: 0x%04x\n", ri->deviceid); seq_printf(s, "radiorev: %s\n", brcmu_dotrev_str(ri->radiorev, drev)); - seq_printf(s, "chip: %s\n", ri->chipname); + seq_printf(s, "chip: %s (%s)\n", ri->chipname, + brcmf_fwvid_vendor_name(bus_if->drvr)); seq_printf(s, "chippkg: %u\n", ri->chippkg); seq_printf(s, "corerev: %u\n", ri->corerev); seq_printf(s, "boardid: 0x%04x\n", ri->boardid); diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwvid.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwvid.c index 274d512cbdad..6f16157a8a4e 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwvid.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwvid.c @@ -192,3 +192,8 @@ void brcmf_fwvid_detach_ops(struct brcmf_pub *drvr) mutex_unlock(&fwvid_list_lock); } + +const char *brcmf_fwvid_vendor_name(struct brcmf_pub *drvr) +{ + return fwvid_list[drvr->bus_if->fwvid].name; +} diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwvid.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwvid.h index 6b3aec190023..43df58bb70ad 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwvid.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwvid.h @@ -22,6 +22,7 @@ int brcmf_fwvid_unregister_vendor(enum brcmf_fwvendor fwvid, struct module *mod) /* core driver functions */ int brcmf_fwvid_attach_ops(struct brcmf_pub *drvr); void brcmf_fwvid_detach_ops(struct brcmf_pub *drvr); +const char *brcmf_fwvid_vendor_name(struct brcmf_pub *drvr); static inline int brcmf_fwvid_attach(struct brcmf_pub *drvr) { From 8041f2bffbf1c0854d9f6675ad01dc6ca72f71dd Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Tue, 29 Nov 2022 14:54:46 +0100 Subject: [PATCH 1710/2002] wifi: brcmfmac: introduce BRCMFMAC exported symbols namespace Using a namespace variant to make clear it is only intended to be used by the vendor-specific modules. The symbol will only truly export the symbols when the driver and consequently the vendor-specific part are built as kernel modules. Reviewed-by: Hante Meuleman Reviewed-by: Pieter-Paul Giesberts Reviewed-by: Franky Lin Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20221129135446.151065-8-arend.vanspriel@broadcom.com --- .../net/wireless/broadcom/brcm80211/brcmfmac/bca/module.c | 1 + drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.h | 6 ++++++ .../net/wireless/broadcom/brcm80211/brcmfmac/cyw/module.c | 1 + drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwvid.c | 4 ++-- .../net/wireless/broadcom/brcm80211/brcmfmac/wcc/module.c | 1 + 5 files changed, 11 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bca/module.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bca/module.c index 790116a735c7..d55f3271d619 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bca/module.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bca/module.c @@ -21,6 +21,7 @@ static void __exit brcmf_bca_exit(void) } MODULE_LICENSE("Dual BSD/GPL"); +MODULE_IMPORT_NS(BRCMFMAC); module_init(brcmf_bca_init); module_exit(brcmf_bca_exit); diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.h index a98b86982502..e4f911dd414b 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.h @@ -13,6 +13,12 @@ #include #include "fweh.h" +#if IS_MODULE(CONFIG_BRCMFMAC) +#define BRCMF_EXPORT_SYMBOL_GPL(__sym) EXPORT_SYMBOL_NS_GPL(__sym, BRCMFMAC) +#else +#define BRCMF_EXPORT_SYMBOL_GPL(__sym) +#endif + #define TOE_TX_CSUM_OL 0x00000001 #define TOE_RX_CSUM_OL 0x00000002 diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cyw/module.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cyw/module.c index 34294724a1f8..f82fbbe3ecef 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cyw/module.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cyw/module.c @@ -21,6 +21,7 @@ static void __exit brcmf_cyw_exit(void) } MODULE_LICENSE("Dual BSD/GPL"); +MODULE_IMPORT_NS(BRCMFMAC); module_init(brcmf_cyw_init); module_exit(brcmf_cyw_exit); diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwvid.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwvid.c index 6f16157a8a4e..86eafdb40541 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwvid.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwvid.c @@ -109,7 +109,7 @@ int brcmf_fwvid_register_vendor(enum brcmf_fwvendor fwvid, struct module *vmod, return 0; } -EXPORT_SYMBOL(brcmf_fwvid_register_vendor); +BRCMF_EXPORT_SYMBOL_GPL(brcmf_fwvid_register_vendor); int brcmf_fwvid_unregister_vendor(enum brcmf_fwvendor fwvid, struct module *mod) { @@ -142,7 +142,7 @@ int brcmf_fwvid_unregister_vendor(enum brcmf_fwvendor fwvid, struct module *mod) return 0; } -EXPORT_SYMBOL(brcmf_fwvid_unregister_vendor); +BRCMF_EXPORT_SYMBOL_GPL(brcmf_fwvid_unregister_vendor); #else static inline int brcmf_fwvid_request_module(enum brcmf_fwvendor fwvid) { diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/wcc/module.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/wcc/module.c index 23e3a4557880..02918d434556 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/wcc/module.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/wcc/module.c @@ -21,6 +21,7 @@ static void __exit brcmf_wcc_exit(void) } MODULE_LICENSE("Dual BSD/GPL"); +MODULE_IMPORT_NS(BRCMFMAC); module_init(brcmf_wcc_init); module_exit(brcmf_wcc_exit); From dd469a754afdb782ba3033cee102147493dc39f4 Mon Sep 17 00:00:00 2001 From: Bitterblue Smith Date: Thu, 1 Dec 2022 16:13:57 +0200 Subject: [PATCH 1711/2002] wifi: rtl8xxxu: Add __packed to struct rtl8723bu_c2h This struct is used to access a sequence of bytes received from the wifi chip. It must not have any padding bytes between the members. This doesn't change anything on my system, possibly because currently none of the members need more than byte alignment. Fixes: b2b43b7837ba ("rtl8xxxu: Initial functionality to handle C2H events for 8723bu") Signed-off-by: Bitterblue Smith Reviewed-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/1a270918-da22-ff5f-29fc-7855f740c5ba@gmail.com --- drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h index 136992f0200c..d26df4095da0 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h +++ b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h @@ -1240,7 +1240,7 @@ struct rtl8723bu_c2h { u8 bw; } __packed ra_report; }; -}; +} __packed; struct rtl8xxxu_fileops; From 76c16af2cb10282274596e21add2c9f0b95c941b Mon Sep 17 00:00:00 2001 From: Bitterblue Smith Date: Thu, 1 Dec 2022 16:15:08 +0200 Subject: [PATCH 1712/2002] wifi: rtl8xxxu: Fix the channel width reporting The gen 2 chips RTL8192EU and RTL8188FU periodically send the driver reports about the TX rate, and the driver passes these reports to sta_statistics. The reports from RTL8192EU may or may not include the channel width. The reports from RTL8188FU do not include it. Only access the c2h->ra_report.bw field if the report (skb) is big enough. The other problem fixed here is that the code was actually never changing the channel width initially reported by rtl8xxxu_bss_info_changed because the value of RATE_INFO_BW_20 is 0. Fixes: 0985d3a410ac ("rtl8xxxu: Feed current txrate information for mac80211") Signed-off-by: Bitterblue Smith Reviewed-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/5b41f1ae-72e7-6b7a-2459-b736399a1c40@gmail.com --- drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c index 28f136064297..7ca46fb77a3b 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c +++ b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c @@ -5569,7 +5569,6 @@ static void rtl8xxxu_c2hcmd_callback(struct work_struct *work) rarpt->txrate.flags = 0; rate = c2h->ra_report.rate; sgi = c2h->ra_report.sgi; - bw = c2h->ra_report.bw; if (rate < DESC_RATE_MCS0) { rarpt->txrate.legacy = @@ -5586,8 +5585,13 @@ static void rtl8xxxu_c2hcmd_callback(struct work_struct *work) RATE_INFO_FLAGS_SHORT_GI; } - if (bw == RATE_INFO_BW_20) - rarpt->txrate.bw |= RATE_INFO_BW_20; + if (skb->len >= offsetofend(typeof(*c2h), ra_report.bw)) { + if (c2h->ra_report.bw == RTL8XXXU_CHANNEL_WIDTH_40) + bw = RATE_INFO_BW_40; + else + bw = RATE_INFO_BW_20; + rarpt->txrate.bw = bw; + } } bit_rate = cfg80211_calculate_bitrate(&rarpt->txrate); rarpt->bit_rate = bit_rate; From 7de16123d9e230d4aff17a466eac820645c8a796 Mon Sep 17 00:00:00 2001 From: Bitterblue Smith Date: Thu, 1 Dec 2022 16:16:46 +0200 Subject: [PATCH 1713/2002] wifi: rtl8xxxu: Introduce rtl8xxxu_update_ra_report The ra_report struct is used for reporting the TX rate via sta_statistics. The code which fills it out is duplicated in two places, and the RTL8188EU will need it in a third place. Move this code into a new function rtl8xxxu_update_ra_report. Signed-off-by: Bitterblue Smith Reviewed-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/0777ad35-fe03-473c-2e02-e3390bef5dd0@gmail.com --- .../wireless/realtek/rtl8xxxu/rtl8xxxu_core.c | 105 ++++++++---------- 1 file changed, 45 insertions(+), 60 deletions(-) diff --git a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c index 7ca46fb77a3b..3ed435401e57 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c +++ b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c @@ -4598,6 +4598,32 @@ static void rtl8xxxu_set_aifs(struct rtl8xxxu_priv *priv, u8 slot_time) } } +static void rtl8xxxu_update_ra_report(struct rtl8xxxu_ra_report *rarpt, + u8 rate, u8 sgi, u8 bw) +{ + u8 mcs, nss; + + rarpt->txrate.flags = 0; + + if (rate <= DESC_RATE_54M) { + rarpt->txrate.legacy = rtl8xxxu_legacy_ratetable[rate].bitrate; + } else { + rtl8xxxu_desc_to_mcsrate(rate, &mcs, &nss); + rarpt->txrate.flags |= RATE_INFO_FLAGS_MCS; + + rarpt->txrate.mcs = mcs; + rarpt->txrate.nss = nss; + + if (sgi) + rarpt->txrate.flags |= RATE_INFO_FLAGS_SHORT_GI; + + rarpt->txrate.bw = bw; + } + + rarpt->bit_rate = cfg80211_calculate_bitrate(&rarpt->txrate); + rarpt->desc_rate = rate; +} + static void rtl8xxxu_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_bss_conf *bss_conf, u64 changed) @@ -4620,9 +4646,7 @@ rtl8xxxu_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif, u32 ramask; int sgi = 0; u8 highest_rate; - u8 mcs = 0, nss = 0; - u32 bit_rate; - + u8 bw; rcu_read_lock(); sta = ieee80211_find_sta(vif, bss_conf->bssid); @@ -4647,37 +4671,19 @@ rtl8xxxu_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif, sgi = 1; highest_rate = fls(ramask) - 1; - if (highest_rate < DESC_RATE_MCS0) { - rarpt->txrate.legacy = - rtl8xxxu_legacy_ratetable[highest_rate].bitrate; - } else { - rtl8xxxu_desc_to_mcsrate(highest_rate, - &mcs, &nss); - rarpt->txrate.flags |= RATE_INFO_FLAGS_MCS; - - rarpt->txrate.mcs = mcs; - rarpt->txrate.nss = nss; - - if (sgi) { - rarpt->txrate.flags |= - RATE_INFO_FLAGS_SHORT_GI; - } - - if (rtl8xxxu_ht40_2g && - (sta->deflink.ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40)) - rarpt->txrate.bw = RATE_INFO_BW_40; - else - rarpt->txrate.bw = RATE_INFO_BW_20; - } + if (rtl8xxxu_ht40_2g && + (sta->deflink.ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40)) + bw = RATE_INFO_BW_40; + else + bw = RATE_INFO_BW_20; rcu_read_unlock(); - bit_rate = cfg80211_calculate_bitrate(&rarpt->txrate); - rarpt->bit_rate = bit_rate; - rarpt->desc_rate = highest_rate; + + rtl8xxxu_update_ra_report(rarpt, highest_rate, sgi, bw); priv->vif = vif; priv->rssi_level = RTL8XXXU_RATR_STA_INIT; - priv->fops->update_rate_mask(priv, ramask, 0, sgi, rarpt->txrate.bw == RATE_INFO_BW_40); + priv->fops->update_rate_mask(priv, ramask, 0, sgi, bw == RATE_INFO_BW_40); rtl8xxxu_write8(priv, REG_BCN_MAX_ERR, 0xff); @@ -5538,9 +5544,7 @@ static void rtl8xxxu_c2hcmd_callback(struct work_struct *work) u8 bt_info = 0; struct rtl8xxxu_btcoex *btcoex; struct rtl8xxxu_ra_report *rarpt; - u8 rate, sgi, bw; - u32 bit_rate; - u8 mcs = 0, nss = 0; + u8 bw; priv = container_of(work, struct rtl8xxxu_priv, c2hcmd_work); btcoex = &priv->bt_coex; @@ -5566,36 +5570,17 @@ static void rtl8xxxu_c2hcmd_callback(struct work_struct *work) rtl8723bu_handle_bt_info(priv); break; case C2H_8723B_RA_REPORT: - rarpt->txrate.flags = 0; - rate = c2h->ra_report.rate; - sgi = c2h->ra_report.sgi; + bw = rarpt->txrate.bw; - if (rate < DESC_RATE_MCS0) { - rarpt->txrate.legacy = - rtl8xxxu_legacy_ratetable[rate].bitrate; - } else { - rtl8xxxu_desc_to_mcsrate(rate, &mcs, &nss); - rarpt->txrate.flags |= RATE_INFO_FLAGS_MCS; - - rarpt->txrate.mcs = mcs; - rarpt->txrate.nss = nss; - - if (sgi) { - rarpt->txrate.flags |= - RATE_INFO_FLAGS_SHORT_GI; - } - - if (skb->len >= offsetofend(typeof(*c2h), ra_report.bw)) { - if (c2h->ra_report.bw == RTL8XXXU_CHANNEL_WIDTH_40) - bw = RATE_INFO_BW_40; - else - bw = RATE_INFO_BW_20; - rarpt->txrate.bw = bw; - } + if (skb->len >= offsetofend(typeof(*c2h), ra_report.bw)) { + if (c2h->ra_report.bw == RTL8XXXU_CHANNEL_WIDTH_40) + bw = RATE_INFO_BW_40; + else + bw = RATE_INFO_BW_20; } - bit_rate = cfg80211_calculate_bitrate(&rarpt->txrate); - rarpt->bit_rate = bit_rate; - rarpt->desc_rate = rate; + + rtl8xxxu_update_ra_report(rarpt, c2h->ra_report.rate, + c2h->ra_report.sgi, bw); break; default: break; From c2f2924bc7f9ea75ef8d95863e710168f8196256 Mon Sep 17 00:00:00 2001 From: Wang Yufen Date: Fri, 2 Dec 2022 13:35:42 +0800 Subject: [PATCH 1714/2002] wifi: brcmfmac: Fix error return code in brcmf_sdio_download_firmware() Fix to return a negative error code instead of 0 when brcmf_chip_set_active() fails. In addition, change the return value for brcmf_pcie_exit_download_state() to keep consistent. Fixes: d380ebc9b6fb ("brcmfmac: rename chip download functions") Signed-off-by: Wang Yufen Reviewed-by: Arend van Spriel Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/1669959342-27144-1-git-send-email-wangyufen@huawei.com --- drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c | 2 +- drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c index 3e121e56e800..ae57a9a3ab05 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c @@ -736,7 +736,7 @@ static int brcmf_pcie_exit_download_state(struct brcmf_pciedev_info *devinfo, } if (!brcmf_chip_set_active(devinfo->ci, resetintr)) - return -EINVAL; + return -EIO; return 0; } diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c index c949051c4bc4..6b38d9de71af 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c @@ -3412,6 +3412,7 @@ static int brcmf_sdio_download_firmware(struct brcmf_sdio *bus, /* Take arm out of reset */ if (!brcmf_chip_set_active(bus->ci, rstvec)) { brcmf_err("error getting out of ARM core reset\n"); + bcmerror = -EIO; goto err; } From 3ddfe3bdd3cf199676737fbd8cb7878b962acd2a Mon Sep 17 00:00:00 2001 From: Zong-Zhe Yang Date: Fri, 2 Dec 2022 14:05:20 +0800 Subject: [PATCH 1715/2002] wifi: rtw89: don't request partial firmware if SECURITY_LOADPIN_ENFORCE Kernel logs on platform enabling SECURITY_LOADPIN_ENFORCE ------ ``` LoadPin: firmware old-api-denied obj= pid=810 cmdline="modprobe -q -- rtw89_8852ce" rtw89_8852ce 0000:01:00.0: loading /lib/firmware/rtw89/rtw8852c_fw.bin failed with error -1 rtw89_8852ce 0000:01:00.0: Direct firmware load for rtw89/rtw8852c_fw.bin failed with error -1 rtw89_8852ce 0000:01:00.0: failed to early request firmware: -1 ``` Trace ------ ``` request_partial_firmware_into_buf() > _request_firmware() >> fw_get_filesystem_firmware() >>> kernel_read_file_from_path_initns() >>>> kernel_read_file() >>>>> security_kernel_read_file() // It will iterate enabled LSMs' hooks for kernel_read_file. // With loadpin, it hooks loadpin_read_file. ``` If SECURITY_LOADPIN_ENFORCE is enabled, doing kernel_read_file() on partial files will be denied and return -EPERM (-1). Then, the outer API based on it, e.g. request_partial_firmware_into_buf(), will get the error. In the case, we cannot get the firmware stuffs right, even though there might be no error other than a permission issue on reading a partial file. So we have to request full firmware if SECURITY_LOADPIN_ENFORCE is enabled. It makes us still have a chance to do early firmware work on this kind of platforms. Signed-off-by: Zong-Zhe Yang Signed-off-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20221202060521.501512-2-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/fw.c | 30 +++++++++++++++++-------- drivers/net/wireless/realtek/rtw89/fw.h | 15 +++++++++++++ 2 files changed, 36 insertions(+), 9 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/fw.c b/drivers/net/wireless/realtek/rtw89/fw.c index 7e682709232d..f5daea0d4f93 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.c +++ b/drivers/net/wireless/realtek/rtw89/fw.c @@ -277,25 +277,37 @@ void rtw89_early_fw_feature_recognize(struct device *device, const struct rtw89_chip_info *chip, u32 *early_feat_map) { - union { - struct rtw89_mfw_hdr mfw_hdr; - u8 fw_hdr[RTW89_FW_HDR_SIZE]; - } buf = {}; + union rtw89_compat_fw_hdr buf = {}; const struct firmware *firmware; + bool full_req = false; u32 ver_code; int ret; int i; - ret = request_partial_firmware_into_buf(&firmware, chip->fw_name, - device, &buf, sizeof(buf), 0); + /* If SECURITY_LOADPIN_ENFORCE is enabled, reading partial files will + * be denied (-EPERM). Then, we don't get right firmware things as + * expected. So, in this case, we have to request full firmware here. + */ + if (IS_ENABLED(CONFIG_SECURITY_LOADPIN_ENFORCE)) + full_req = true; + + if (full_req) + ret = request_firmware(&firmware, chip->fw_name, device); + else + ret = request_partial_firmware_into_buf(&firmware, chip->fw_name, + device, &buf, sizeof(buf), + 0); + if (ret) { dev_err(device, "failed to early request firmware: %d\n", ret); return; } - ver_code = buf.mfw_hdr.sig != RTW89_MFW_SIG ? - RTW89_FW_HDR_VER_CODE(&buf.fw_hdr) : - RTW89_MFW_HDR_VER_CODE(&buf.mfw_hdr); + if (full_req) + ver_code = rtw89_compat_fw_hdr_ver_code(firmware->data); + else + ver_code = rtw89_compat_fw_hdr_ver_code(&buf); + if (!ver_code) goto out; diff --git a/drivers/net/wireless/realtek/rtw89/fw.h b/drivers/net/wireless/realtek/rtw89/fw.h index 46d57414f24e..5c4c7de1b4f5 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.h +++ b/drivers/net/wireless/realtek/rtw89/fw.h @@ -3291,6 +3291,21 @@ struct fwcmd_hdr { __le32 hdr1; }; +union rtw89_compat_fw_hdr { + struct rtw89_mfw_hdr mfw_hdr; + u8 fw_hdr[RTW89_FW_HDR_SIZE]; +}; + +static inline u32 rtw89_compat_fw_hdr_ver_code(const void *fw_buf) +{ + const union rtw89_compat_fw_hdr *compat = (typeof(compat))fw_buf; + + if (compat->mfw_hdr.sig == RTW89_MFW_SIG) + return RTW89_MFW_HDR_VER_CODE(&compat->mfw_hdr); + else + return RTW89_FW_HDR_VER_CODE(&compat->fw_hdr); +} + #define RTW89_H2C_RF_PAGE_SIZE 500 #define RTW89_H2C_RF_PAGE_NUM 3 struct rtw89_fw_h2c_rf_reg_info { From 13eb07e0be1b95f1e1fab721fb0f38117edfe80b Mon Sep 17 00:00:00 2001 From: Zong-Zhe Yang Date: Fri, 2 Dec 2022 14:05:21 +0800 Subject: [PATCH 1716/2002] wifi: rtw89: request full firmware only once if it's early requested Under some condition, we now have to do early request full firmware when rtw89_early_fw_feature_recognize(). In this case, we can avoid requesting full firmware twice during probing driver. So, we pass out full firmware from rtw89_early_fw_feature_recognize() if it's requested successfully. And then, if firmware is settled, we have no need to request full firmware again during normal initizating flow. Setting firmware flow is updated to be as the following. platform | early recognizing | normally initizating ----------------------------------------------------------------------- deny reading | request full FW | (no more FW requesting) partial file | | (obtain FW from early pahse) ----------------------------------------------------------------------- able to read | request partial FW | async request full FW partial file | (quite small chunk) | Signed-off-by: Zong-Zhe Yang Signed-off-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20221202060521.501512-3-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/core.c | 6 ++++- drivers/net/wireless/realtek/rtw89/fw.c | 28 +++++++++++++++++++---- drivers/net/wireless/realtek/rtw89/fw.h | 7 +++--- 3 files changed, 32 insertions(+), 9 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c index 5ab95250755d..931aff8b5dc9 100644 --- a/drivers/net/wireless/realtek/rtw89/core.c +++ b/drivers/net/wireless/realtek/rtw89/core.c @@ -3420,6 +3420,7 @@ struct rtw89_dev *rtw89_alloc_ieee80211_hw(struct device *device, u32 bus_data_size, const struct rtw89_chip_info *chip) { + const struct firmware *firmware; struct ieee80211_hw *hw; struct rtw89_dev *rtwdev; struct ieee80211_ops *ops; @@ -3427,7 +3428,7 @@ struct rtw89_dev *rtw89_alloc_ieee80211_hw(struct device *device, u32 early_feat_map = 0; bool no_chanctx; - rtw89_early_fw_feature_recognize(device, chip, &early_feat_map); + firmware = rtw89_early_fw_feature_recognize(device, chip, &early_feat_map); ops = kmemdup(&rtw89_ops, sizeof(rtw89_ops), GFP_KERNEL); if (!ops) @@ -3454,6 +3455,7 @@ struct rtw89_dev *rtw89_alloc_ieee80211_hw(struct device *device, rtwdev->dev = device; rtwdev->ops = ops; rtwdev->chip = chip; + rtwdev->fw.firmware = firmware; rtw89_debug(rtwdev, RTW89_DBG_FW, "probe driver %s chanctx\n", no_chanctx ? "without" : "with"); @@ -3462,6 +3464,7 @@ struct rtw89_dev *rtw89_alloc_ieee80211_hw(struct device *device, err: kfree(ops); + release_firmware(firmware); return NULL; } EXPORT_SYMBOL(rtw89_alloc_ieee80211_hw); @@ -3469,6 +3472,7 @@ EXPORT_SYMBOL(rtw89_alloc_ieee80211_hw); void rtw89_free_ieee80211_hw(struct rtw89_dev *rtwdev) { kfree(rtwdev->ops); + release_firmware(rtwdev->fw.firmware); ieee80211_free_hw(rtwdev->hw); } EXPORT_SYMBOL(rtw89_free_ieee80211_hw); diff --git a/drivers/net/wireless/realtek/rtw89/fw.c b/drivers/net/wireless/realtek/rtw89/fw.c index f5daea0d4f93..de1f23779fc6 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.c +++ b/drivers/net/wireless/realtek/rtw89/fw.c @@ -273,9 +273,10 @@ static void rtw89_fw_recognize_features(struct rtw89_dev *rtwdev) } } -void rtw89_early_fw_feature_recognize(struct device *device, - const struct rtw89_chip_info *chip, - u32 *early_feat_map) +const struct firmware * +rtw89_early_fw_feature_recognize(struct device *device, + const struct rtw89_chip_info *chip, + u32 *early_feat_map) { union rtw89_compat_fw_hdr buf = {}; const struct firmware *firmware; @@ -300,7 +301,7 @@ void rtw89_early_fw_feature_recognize(struct device *device, if (ret) { dev_err(device, "failed to early request firmware: %d\n", ret); - return; + return NULL; } if (full_req) @@ -322,7 +323,11 @@ void rtw89_early_fw_feature_recognize(struct device *device, } out: + if (full_req) + return firmware; + release_firmware(firmware); + return NULL; } int rtw89_fw_recognize(struct rtw89_dev *rtwdev) @@ -629,6 +634,13 @@ int rtw89_load_firmware(struct rtw89_dev *rtwdev) fw->rtwdev = rtwdev; init_completion(&fw->completion); + if (fw->firmware) { + rtw89_debug(rtwdev, RTW89_DBG_FW, + "full firmware has been early requested\n"); + complete_all(&fw->completion); + return 0; + } + ret = request_firmware_nowait(THIS_MODULE, true, fw_name, rtwdev->dev, GFP_KERNEL, fw, rtw89_load_firmware_cb); if (ret) { @@ -645,8 +657,14 @@ void rtw89_unload_firmware(struct rtw89_dev *rtwdev) rtw89_wait_firmware_completion(rtwdev); - if (fw->firmware) + if (fw->firmware) { release_firmware(fw->firmware); + + /* assign NULL back in case rtw89_free_ieee80211_hw() + * try to release the same one again. + */ + fw->firmware = NULL; + } } #define H2C_CAM_LEN 60 diff --git a/drivers/net/wireless/realtek/rtw89/fw.h b/drivers/net/wireless/realtek/rtw89/fw.h index 5c4c7de1b4f5..4d2f9ea9e002 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.h +++ b/drivers/net/wireless/realtek/rtw89/fw.h @@ -3444,9 +3444,10 @@ struct rtw89_fw_h2c_rf_get_mccch { int rtw89_fw_check_rdy(struct rtw89_dev *rtwdev); int rtw89_fw_recognize(struct rtw89_dev *rtwdev); -void rtw89_early_fw_feature_recognize(struct device *device, - const struct rtw89_chip_info *chip, - u32 *early_feat_map); +const struct firmware * +rtw89_early_fw_feature_recognize(struct device *device, + const struct rtw89_chip_info *chip, + u32 *early_feat_map); int rtw89_fw_download(struct rtw89_dev *rtwdev, enum rtw89_fw_type type); int rtw89_load_firmware(struct rtw89_dev *rtwdev); void rtw89_unload_firmware(struct rtw89_dev *rtwdev); From fb2b8cec81d75b2b20bdbc2fca7a60a68bc78aac Mon Sep 17 00:00:00 2001 From: Po-Hao Huang Date: Fri, 2 Dec 2022 14:15:24 +0800 Subject: [PATCH 1717/2002] wifi: rtw89: add mac TSF sync function If the interface is in AP/P2P GO mode, we adjust the TSF with random offset to avoid TBTT of different vifs to overlap and collide. For every new interface added, we adjust the value and resync for all interfaces. Signed-off-by: Po-Hao Huang Signed-off-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20221202061527.505668-2-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/mac.c | 44 ++++++++++++++++++++++++ drivers/net/wireless/realtek/rtw89/mac.h | 2 ++ drivers/net/wireless/realtek/rtw89/reg.h | 17 +++++++++ 3 files changed, 63 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/mac.c b/drivers/net/wireless/realtek/rtw89/mac.c index d80050c2e9b3..a80690d0bf48 100644 --- a/drivers/net/wireless/realtek/rtw89/mac.c +++ b/drivers/net/wireless/realtek/rtw89/mac.c @@ -3913,6 +3913,49 @@ static void rtw89_mac_port_cfg_tbtt_shift(struct rtw89_dev *rtwdev, B_AX_TBTT_SHIFT_OFST_MASK, val); } +static void rtw89_mac_port_tsf_sync(struct rtw89_dev *rtwdev, + struct rtw89_vif *rtwvif, + struct rtw89_vif *rtwvif_src, u8 offset, + int *n_offset) +{ + u32 val, reg; + + if (rtwvif->net_type != RTW89_NET_TYPE_AP_MODE || rtwvif == rtwvif_src) + return; + + /* adjust offset randomly to avoid beacon conflict */ + offset = offset - offset / 4 + get_random_u32() % (offset / 2); + val = RTW89_PORT_OFFSET_MS_TO_32US((*n_offset)++, offset); + reg = rtw89_mac_reg_by_idx(R_AX_PORT0_TSF_SYNC + rtwvif->port * 4, + rtwvif->mac_idx); + + rtw89_write32_mask(rtwdev, reg, B_AX_SYNC_PORT_SRC, rtwvif_src->port); + rtw89_write32_mask(rtwdev, reg, B_AX_SYNC_PORT_OFFSET_VAL, val); + rtw89_write32_set(rtwdev, reg, B_AX_SYNC_NOW); +} + +static void rtw89_mac_port_tsf_resync_all(struct rtw89_dev *rtwdev) +{ + struct rtw89_vif *src = NULL, *tmp; + u8 offset = 100, vif_aps = 0; + int n_offset = 1; + + rtw89_for_each_rtwvif(rtwdev, tmp) { + if (!src || tmp->net_type == RTW89_NET_TYPE_INFRA) + src = tmp; + if (tmp->net_type == RTW89_NET_TYPE_AP_MODE) + vif_aps++; + } + + if (vif_aps == 0) + return; + + offset /= (vif_aps + 1); + + rtw89_for_each_rtwvif(rtwdev, tmp) + rtw89_mac_port_tsf_sync(rtwdev, tmp, src, offset, &n_offset); +} + int rtw89_mac_vif_init(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif) { int ret; @@ -3991,6 +4034,7 @@ int rtw89_mac_port_update(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif) rtw89_mac_port_cfg_bss_color(rtwdev, rtwvif); rtw89_mac_port_cfg_mbssid(rtwdev, rtwvif); rtw89_mac_port_cfg_func_en(rtwdev, rtwvif); + rtw89_mac_port_tsf_resync_all(rtwdev); fsleep(BCN_ERLY_SET_DLY); rtw89_mac_port_cfg_bcn_early(rtwdev, rtwvif); diff --git a/drivers/net/wireless/realtek/rtw89/mac.h b/drivers/net/wireless/realtek/rtw89/mac.h index adb0c86a98d3..766ca6934d33 100644 --- a/drivers/net/wireless/realtek/rtw89/mac.h +++ b/drivers/net/wireless/realtek/rtw89/mac.h @@ -168,6 +168,8 @@ enum rtw89_mac_ax_l0_to_l1_event { MAC_AX_L0_TO_L1_EVENT_MAX = 15, }; +#define RTW89_PORT_OFFSET_MS_TO_32US(n, shift_ms) ((n) * (shift_ms) * 1000 / 32) + enum rtw89_mac_dbg_port_sel { /* CMAC 0 related */ RTW89_DBG_PORT_SEL_PTCL_C0 = 0, diff --git a/drivers/net/wireless/realtek/rtw89/reg.h b/drivers/net/wireless/realtek/rtw89/reg.h index f2634062f377..5324e645728b 100644 --- a/drivers/net/wireless/realtek/rtw89/reg.h +++ b/drivers/net/wireless/realtek/rtw89/reg.h @@ -2048,6 +2048,23 @@ #define B_AX_PTCL_TOP_ERR_IND BIT(1) #define B_AX_SCHEDULE_TOP_ERR_IND BIT(0) +#define R_AX_PORT0_TSF_SYNC 0xC2A0 +#define R_AX_PORT0_TSF_SYNC_C1 0xE2A0 +#define R_AX_PORT1_TSF_SYNC 0xC2A4 +#define R_AX_PORT1_TSF_SYNC_C1 0xE2A4 +#define R_AX_PORT2_TSF_SYNC 0xC2A8 +#define R_AX_PORT2_TSF_SYNC_C1 0xE2A8 +#define R_AX_PORT3_TSF_SYNC 0xC2AC +#define R_AX_PORT3_TSF_SYNC_C1 0xE2AC +#define R_AX_PORT4_TSF_SYNC 0xC2B0 +#define R_AX_PORT4_TSF_SYNC_C1 0xE2B0 +#define B_AX_SYNC_NOW BIT(30) +#define B_AX_SYNC_ONCE BIT(29) +#define B_AX_SYNC_AUTO BIT(28) +#define B_AX_SYNC_PORT_SRC GENMASK(26, 24) +#define B_AX_SYNC_PORT_OFFSET_SIGN BIT(18) +#define B_AX_SYNC_PORT_OFFSET_VAL GENMASK(17, 0) + #define R_AX_MACID_SLEEP_0 0xC2C0 #define R_AX_MACID_SLEEP_0_C1 0xE2C0 #define B_AX_MACID31_0_SLEEP_SH 0 From d592b9f742643f07db53ac34baf6bbd3ce9478dc Mon Sep 17 00:00:00 2001 From: Po-Hao Huang Date: Fri, 2 Dec 2022 14:15:25 +0800 Subject: [PATCH 1718/2002] wifi: rtw89: stop mac port function when stop_ap() Disable hardware beacon related functions when ap stops. So hardware won't transmit beacons while interface is already removed. Signed-off-by: Po-Hao Huang Signed-off-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20221202061527.505668-3-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/mac.c | 16 +++++++++++++--- drivers/net/wireless/realtek/rtw89/mac.h | 1 + drivers/net/wireless/realtek/rtw89/mac80211.c | 1 + 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/mac.c b/drivers/net/wireless/realtek/rtw89/mac.c index a80690d0bf48..12cbf41590bb 100644 --- a/drivers/net/wireless/realtek/rtw89/mac.c +++ b/drivers/net/wireless/realtek/rtw89/mac.c @@ -3877,11 +3877,16 @@ static void rtw89_mac_port_cfg_hiq_drop(struct rtw89_dev *rtwdev, } static void rtw89_mac_port_cfg_func_en(struct rtw89_dev *rtwdev, - struct rtw89_vif *rtwvif) + struct rtw89_vif *rtwvif, bool enable) { const struct rtw89_port_reg *p = &rtw_port_base; - rtw89_write32_port_set(rtwdev, rtwvif, p->port_cfg, B_AX_PORT_FUNC_EN); + if (enable) + rtw89_write32_port_set(rtwdev, rtwvif, p->port_cfg, + B_AX_PORT_FUNC_EN); + else + rtw89_write32_port_clr(rtwdev, rtwvif, p->port_cfg, + B_AX_PORT_FUNC_EN); } static void rtw89_mac_port_cfg_bcn_early(struct rtw89_dev *rtwdev, @@ -4033,7 +4038,7 @@ int rtw89_mac_port_update(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif) rtw89_mac_port_cfg_tbtt_shift(rtwdev, rtwvif); rtw89_mac_port_cfg_bss_color(rtwdev, rtwvif); rtw89_mac_port_cfg_mbssid(rtwdev, rtwvif); - rtw89_mac_port_cfg_func_en(rtwdev, rtwvif); + rtw89_mac_port_cfg_func_en(rtwdev, rtwvif, true); rtw89_mac_port_tsf_resync_all(rtwdev); fsleep(BCN_ERLY_SET_DLY); rtw89_mac_port_cfg_bcn_early(rtwdev, rtwvif); @@ -4085,6 +4090,11 @@ void rtw89_mac_set_he_obss_narrow_bw_ru(struct rtw89_dev *rtwdev, rtw89_write32_set(rtwdev, reg, B_AX_RXTRIG_RU26_DIS); } +void rtw89_mac_stop_ap(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif) +{ + rtw89_mac_port_cfg_func_en(rtwdev, rtwvif, false); +} + int rtw89_mac_add_vif(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif) { int ret; diff --git a/drivers/net/wireless/realtek/rtw89/mac.h b/drivers/net/wireless/realtek/rtw89/mac.h index 766ca6934d33..f0b684b205f1 100644 --- a/drivers/net/wireless/realtek/rtw89/mac.h +++ b/drivers/net/wireless/realtek/rtw89/mac.h @@ -908,6 +908,7 @@ int rtw89_mac_add_vif(struct rtw89_dev *rtwdev, struct rtw89_vif *vif); int rtw89_mac_port_update(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif); void rtw89_mac_set_he_obss_narrow_bw_ru(struct rtw89_dev *rtwdev, struct ieee80211_vif *vif); +void rtw89_mac_stop_ap(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif); int rtw89_mac_remove_vif(struct rtw89_dev *rtwdev, struct rtw89_vif *vif); void rtw89_mac_disable_cpu(struct rtw89_dev *rtwdev); int rtw89_mac_enable_cpu(struct rtw89_dev *rtwdev, u8 boot_reason, bool dlfw); diff --git a/drivers/net/wireless/realtek/rtw89/mac80211.c b/drivers/net/wireless/realtek/rtw89/mac80211.c index 1a99267d710d..0c86d416b7ad 100644 --- a/drivers/net/wireless/realtek/rtw89/mac80211.c +++ b/drivers/net/wireless/realtek/rtw89/mac80211.c @@ -454,6 +454,7 @@ void rtw89_ops_stop_ap(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct rtw89_vif *rtwvif = (struct rtw89_vif *)vif->drv_priv; mutex_lock(&rtwdev->mutex); + rtw89_mac_stop_ap(rtwdev, rtwvif); rtw89_fw_h2c_assoc_cmac_tbl(rtwdev, vif, NULL); rtw89_fw_h2c_join_info(rtwdev, rtwvif, NULL, true); mutex_unlock(&rtwdev->mutex); From 8fc5d4338620b81b1b265c725b38aced8acf8d72 Mon Sep 17 00:00:00 2001 From: Po-Hao Huang Date: Fri, 2 Dec 2022 14:15:26 +0800 Subject: [PATCH 1719/2002] wifi: rtw89: fix unsuccessful interface_add flow Remove according vifs from list if we couldn't set this interface up. Otherwise the rtwvif_list could contain unreferenced objects. Signed-off-by: Po-Hao Huang Signed-off-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20221202061527.505668-4-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/mac80211.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/mac80211.c b/drivers/net/wireless/realtek/rtw89/mac80211.c index 0c86d416b7ad..f9b95c52916b 100644 --- a/drivers/net/wireless/realtek/rtw89/mac80211.c +++ b/drivers/net/wireless/realtek/rtw89/mac80211.c @@ -125,6 +125,7 @@ static int rtw89_ops_add_interface(struct ieee80211_hw *hw, RTW89_PORT_NUM); if (rtwvif->port == RTW89_PORT_NUM) { ret = -ENOSPC; + list_del_init(&rtwvif->list); goto out; } @@ -138,6 +139,7 @@ static int rtw89_ops_add_interface(struct ieee80211_hw *hw, ret = rtw89_mac_add_vif(rtwdev, rtwvif); if (ret) { rtw89_core_release_bit_map(rtwdev->hw_port, rtwvif->port); + list_del_init(&rtwvif->list); goto out; } From a0e78d5c6082fc953fef5af7293be0145c67dba4 Mon Sep 17 00:00:00 2001 From: Po-Hao Huang Date: Fri, 2 Dec 2022 14:15:27 +0800 Subject: [PATCH 1720/2002] wifi: rtw89: add join info upon create interface To support multiple vifs, fw need more information of each role. Send this info to make things work as expected. Signed-off-by: Po-Hao Huang Signed-off-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20221202061527.505668-5-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/mac.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/mac.c b/drivers/net/wireless/realtek/rtw89/mac.c index 12cbf41590bb..cf9a0a3120a7 100644 --- a/drivers/net/wireless/realtek/rtw89/mac.c +++ b/drivers/net/wireless/realtek/rtw89/mac.c @@ -3980,6 +3980,10 @@ int rtw89_mac_vif_init(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif) if (ret) return ret; + ret = rtw89_fw_h2c_join_info(rtwdev, rtwvif, NULL, true); + if (ret) + return ret; + ret = rtw89_cam_init(rtwdev, rtwvif); if (ret) return ret; From 1d89660494402168565e0637268486ae28bad642 Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Fri, 2 Dec 2022 09:12:14 +0100 Subject: [PATCH 1721/2002] wifi: rtw88: print firmware type in info message It's confusing to read two different firmware versions in the syslog for the same device: rtw_8822cu 2-1:1.2: Firmware version 9.9.4, H2C version 15 rtw_8822cu 2-1:1.2: Firmware version 9.9.11, H2C version 15 Print the firmware type in this message to make clear these are really two different firmwares for different purposes: rtw_8822cu 1-1.4:1.2: WOW Firmware version 9.9.4, H2C version 15 rtw_8822cu 1-1.4:1.2: Firmware version 9.9.11, H2C version 15 Signed-off-by: Sascha Hauer Reviewed-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20221202081224.2779981-2-s.hauer@pengutronix.de --- drivers/net/wireless/realtek/rtw88/main.c | 4 +++- drivers/net/wireless/realtek/rtw88/main.h | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/realtek/rtw88/main.c b/drivers/net/wireless/realtek/rtw88/main.c index 67151dbf8384..a7331872e853 100644 --- a/drivers/net/wireless/realtek/rtw88/main.c +++ b/drivers/net/wireless/realtek/rtw88/main.c @@ -1731,7 +1731,8 @@ static void rtw_load_firmware_cb(const struct firmware *firmware, void *context) update_firmware_info(rtwdev, fw); complete_all(&fw->completion); - rtw_info(rtwdev, "Firmware version %u.%u.%u, H2C version %u\n", + rtw_info(rtwdev, "%sFirmware version %u.%u.%u, H2C version %u\n", + fw->type == RTW_WOWLAN_FW ? "WOW " : "", fw->version, fw->sub_version, fw->sub_index, fw->h2c_version); } @@ -1757,6 +1758,7 @@ static int rtw_load_firmware(struct rtw_dev *rtwdev, enum rtw_fw_type type) return -ENOENT; } + fw->type = type; fw->rtwdev = rtwdev; init_completion(&fw->completion); diff --git a/drivers/net/wireless/realtek/rtw88/main.h b/drivers/net/wireless/realtek/rtw88/main.h index bccd7b28f60c..6e5875f6d07f 100644 --- a/drivers/net/wireless/realtek/rtw88/main.h +++ b/drivers/net/wireless/realtek/rtw88/main.h @@ -1851,6 +1851,7 @@ struct rtw_fw_state { u16 h2c_version; u32 feature; u32 feature_ext; + enum rtw_fw_type type; }; enum rtw_sar_sources { From 69020957bcb783184af1a86c8483139557cec751 Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Fri, 2 Dec 2022 09:12:15 +0100 Subject: [PATCH 1722/2002] wifi: rtw88: Call rtw_fw_beacon_filter_config() with rtwdev->mutex held rtw_fw_beacon_filter_config() is called once with rtwdev->mutex held and once without the mutex held. Call it consistently with rtwdev->mutex held. Signed-off-by: Sascha Hauer Reviewed-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20221202081224.2779981-3-s.hauer@pengutronix.de --- drivers/net/wireless/realtek/rtw88/mac80211.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/realtek/rtw88/mac80211.c b/drivers/net/wireless/realtek/rtw88/mac80211.c index 07578ccc4bab..776a9a9884b5 100644 --- a/drivers/net/wireless/realtek/rtw88/mac80211.c +++ b/drivers/net/wireless/realtek/rtw88/mac80211.c @@ -487,8 +487,8 @@ static int rtw_ops_sta_remove(struct ieee80211_hw *hw, { struct rtw_dev *rtwdev = hw->priv; - rtw_fw_beacon_filter_config(rtwdev, false, vif); mutex_lock(&rtwdev->mutex); + rtw_fw_beacon_filter_config(rtwdev, false, vif); rtw_sta_remove(rtwdev, sta, true); mutex_unlock(&rtwdev->mutex); From d57ca103e54e2b3eea7e2603548c58bcc4155541 Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Fri, 2 Dec 2022 09:12:16 +0100 Subject: [PATCH 1723/2002] wifi: rtw88: Drop rf_lock The rtwdev->rf_lock spinlock protects the rf register accesses in rtw_read_rf() and rtw_write_rf(). Most callers of these functions hold rtwdev->mutex already with the exception of the callsites in the debugfs code. The debugfs code doesn't justify an extra lock, so acquire the mutex there as well before calling rf register accessors and drop the now unnecessary spinlock. Signed-off-by: Sascha Hauer Reviewed-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20221202081224.2779981-4-s.hauer@pengutronix.de --- drivers/net/wireless/realtek/rtw88/debug.c | 11 +++++++++++ drivers/net/wireless/realtek/rtw88/hci.h | 9 +++------ drivers/net/wireless/realtek/rtw88/main.c | 1 - drivers/net/wireless/realtek/rtw88/main.h | 3 --- 4 files changed, 14 insertions(+), 10 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw88/debug.c b/drivers/net/wireless/realtek/rtw88/debug.c index 9ebe544e51d0..70e19f2a1a35 100644 --- a/drivers/net/wireless/realtek/rtw88/debug.c +++ b/drivers/net/wireless/realtek/rtw88/debug.c @@ -144,7 +144,9 @@ static int rtw_debugfs_get_rf_read(struct seq_file *m, void *v) addr = debugfs_priv->rf_addr; mask = debugfs_priv->rf_mask; + mutex_lock(&rtwdev->mutex); val = rtw_read_rf(rtwdev, path, addr, mask); + mutex_unlock(&rtwdev->mutex); seq_printf(m, "rf_read path:%d addr:0x%08x mask:0x%08x val=0x%08x\n", path, addr, mask, val); @@ -414,7 +416,9 @@ static ssize_t rtw_debugfs_set_rf_write(struct file *filp, return count; } + mutex_lock(&rtwdev->mutex); rtw_write_rf(rtwdev, path, addr, mask, val); + mutex_unlock(&rtwdev->mutex); rtw_dbg(rtwdev, RTW_DBG_DEBUGFS, "write_rf path:%d addr:0x%08x mask:0x%08x, val:0x%08x\n", path, addr, mask, val); @@ -519,6 +523,8 @@ static int rtw_debug_get_rf_dump(struct seq_file *m, void *v) u32 addr, offset, data; u8 path; + mutex_lock(&rtwdev->mutex); + for (path = 0; path < rtwdev->hal.rf_path_num; path++) { seq_printf(m, "RF path:%d\n", path); for (addr = 0; addr < 0x100; addr += 4) { @@ -533,6 +539,8 @@ static int rtw_debug_get_rf_dump(struct seq_file *m, void *v) seq_puts(m, "\n"); } + mutex_unlock(&rtwdev->mutex); + return 0; } @@ -1026,6 +1034,8 @@ static void dump_gapk_status(struct rtw_dev *rtwdev, struct seq_file *m) dm_info->dm_flags & BIT(RTW_DM_CAP_TXGAPK) ? '-' : '+', rtw_dm_cap_strs[RTW_DM_CAP_TXGAPK]); + mutex_lock(&rtwdev->mutex); + for (path = 0; path < rtwdev->hal.rf_path_num; path++) { val = rtw_read_rf(rtwdev, path, RF_GAINTX, RFREG_MASK); seq_printf(m, "path %d:\n0x%x = 0x%x\n", path, RF_GAINTX, val); @@ -1035,6 +1045,7 @@ static void dump_gapk_status(struct rtw_dev *rtwdev, struct seq_file *m) txgapk->rf3f_fs[path][i], i); seq_puts(m, "\n"); } + mutex_unlock(&rtwdev->mutex); } static int rtw_debugfs_get_dm_cap(struct seq_file *m, void *v) diff --git a/drivers/net/wireless/realtek/rtw88/hci.h b/drivers/net/wireless/realtek/rtw88/hci.h index 4c6fc6fb3f83..830d7532f2a3 100644 --- a/drivers/net/wireless/realtek/rtw88/hci.h +++ b/drivers/net/wireless/realtek/rtw88/hci.h @@ -166,12 +166,11 @@ static inline u32 rtw_read_rf(struct rtw_dev *rtwdev, enum rtw_rf_path rf_path, u32 addr, u32 mask) { - unsigned long flags; u32 val; - spin_lock_irqsave(&rtwdev->rf_lock, flags); + lockdep_assert_held(&rtwdev->mutex); + val = rtwdev->chip->ops->read_rf(rtwdev, rf_path, addr, mask); - spin_unlock_irqrestore(&rtwdev->rf_lock, flags); return val; } @@ -180,11 +179,9 @@ static inline void rtw_write_rf(struct rtw_dev *rtwdev, enum rtw_rf_path rf_path, u32 addr, u32 mask, u32 data) { - unsigned long flags; + lockdep_assert_held(&rtwdev->mutex); - spin_lock_irqsave(&rtwdev->rf_lock, flags); rtwdev->chip->ops->write_rf(rtwdev, rf_path, addr, mask, data); - spin_unlock_irqrestore(&rtwdev->rf_lock, flags); } static inline u32 diff --git a/drivers/net/wireless/realtek/rtw88/main.c b/drivers/net/wireless/realtek/rtw88/main.c index a7331872e853..710ddb0283c8 100644 --- a/drivers/net/wireless/realtek/rtw88/main.c +++ b/drivers/net/wireless/realtek/rtw88/main.c @@ -2067,7 +2067,6 @@ int rtw_core_init(struct rtw_dev *rtwdev) skb_queue_head_init(&rtwdev->coex.queue); skb_queue_head_init(&rtwdev->tx_report.queue); - spin_lock_init(&rtwdev->rf_lock); spin_lock_init(&rtwdev->h2c.lock); spin_lock_init(&rtwdev->txq_lock); spin_lock_init(&rtwdev->tx_report.q_lock); diff --git a/drivers/net/wireless/realtek/rtw88/main.h b/drivers/net/wireless/realtek/rtw88/main.h index 6e5875f6d07f..f24d17f482aa 100644 --- a/drivers/net/wireless/realtek/rtw88/main.h +++ b/drivers/net/wireless/realtek/rtw88/main.h @@ -1995,9 +1995,6 @@ struct rtw_dev { /* ensures exclusive access from mac80211 callbacks */ struct mutex mutex; - /* read/write rf register */ - spinlock_t rf_lock; - /* watch dog every 2 sec */ struct delayed_work watch_dog_work; u32 watch_dog_cnt; From 1e2701f4079a7906ff3fb43a315925d303e289d8 Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Fri, 2 Dec 2022 09:12:17 +0100 Subject: [PATCH 1724/2002] wifi: rtw88: Drop h2c.lock The h2c.lock spinlock is used in rtw_fw_send_h2c_command() and rtw_fw_send_h2c_packet(). Most callers call this with rtwdev->mutex held, except from one callsite in the debugfs code. The debugfs code alone doesn't justify the extra lock, so acquire rtwdev->mutex in debugfs and drop the now unnecessary spinlock. Signed-off-by: Sascha Hauer Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20221202081224.2779981-5-s.hauer@pengutronix.de --- drivers/net/wireless/realtek/rtw88/debug.c | 2 ++ drivers/net/wireless/realtek/rtw88/fw.c | 13 ++++--------- drivers/net/wireless/realtek/rtw88/main.c | 1 - drivers/net/wireless/realtek/rtw88/main.h | 2 -- 4 files changed, 6 insertions(+), 12 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw88/debug.c b/drivers/net/wireless/realtek/rtw88/debug.c index 70e19f2a1a35..f5b8a77ebc67 100644 --- a/drivers/net/wireless/realtek/rtw88/debug.c +++ b/drivers/net/wireless/realtek/rtw88/debug.c @@ -392,7 +392,9 @@ static ssize_t rtw_debugfs_set_h2c(struct file *filp, return -EINVAL; } + mutex_lock(&rtwdev->mutex); rtw_fw_h2c_cmd_dbg(rtwdev, param); + mutex_unlock(&rtwdev->mutex); return count; } diff --git a/drivers/net/wireless/realtek/rtw88/fw.c b/drivers/net/wireless/realtek/rtw88/fw.c index 5e760c884f89..82295ac6402e 100644 --- a/drivers/net/wireless/realtek/rtw88/fw.c +++ b/drivers/net/wireless/realtek/rtw88/fw.c @@ -322,7 +322,7 @@ static void rtw_fw_send_h2c_command(struct rtw_dev *rtwdev, h2c[3], h2c[2], h2c[1], h2c[0], h2c[7], h2c[6], h2c[5], h2c[4]); - spin_lock(&rtwdev->h2c.lock); + lockdep_assert_held(&rtwdev->mutex); box = rtwdev->h2c.last_box_num; switch (box) { @@ -344,7 +344,7 @@ static void rtw_fw_send_h2c_command(struct rtw_dev *rtwdev, break; default: WARN(1, "invalid h2c mail box number\n"); - goto out; + return; } ret = read_poll_timeout_atomic(rtw_read8, box_state, @@ -353,7 +353,7 @@ static void rtw_fw_send_h2c_command(struct rtw_dev *rtwdev, if (ret) { rtw_err(rtwdev, "failed to send h2c command\n"); - goto out; + return; } rtw_write32(rtwdev, box_ex_reg, le32_to_cpu(h2c_cmd->msg_ext)); @@ -361,9 +361,6 @@ static void rtw_fw_send_h2c_command(struct rtw_dev *rtwdev, if (++rtwdev->h2c.last_box_num >= 4) rtwdev->h2c.last_box_num = 0; - -out: - spin_unlock(&rtwdev->h2c.lock); } void rtw_fw_h2c_cmd_dbg(struct rtw_dev *rtwdev, u8 *h2c) @@ -375,15 +372,13 @@ static void rtw_fw_send_h2c_packet(struct rtw_dev *rtwdev, u8 *h2c_pkt) { int ret; - spin_lock(&rtwdev->h2c.lock); + lockdep_assert_held(&rtwdev->mutex); FW_OFFLOAD_H2C_SET_SEQ_NUM(h2c_pkt, rtwdev->h2c.seq); ret = rtw_hci_write_data_h2c(rtwdev, h2c_pkt, H2C_PKT_SIZE); if (ret) rtw_err(rtwdev, "failed to send h2c packet\n"); rtwdev->h2c.seq++; - - spin_unlock(&rtwdev->h2c.lock); } void diff --git a/drivers/net/wireless/realtek/rtw88/main.c b/drivers/net/wireless/realtek/rtw88/main.c index 710ddb0283c8..c98e56890401 100644 --- a/drivers/net/wireless/realtek/rtw88/main.c +++ b/drivers/net/wireless/realtek/rtw88/main.c @@ -2067,7 +2067,6 @@ int rtw_core_init(struct rtw_dev *rtwdev) skb_queue_head_init(&rtwdev->coex.queue); skb_queue_head_init(&rtwdev->tx_report.queue); - spin_lock_init(&rtwdev->h2c.lock); spin_lock_init(&rtwdev->txq_lock); spin_lock_init(&rtwdev->tx_report.q_lock); diff --git a/drivers/net/wireless/realtek/rtw88/main.h b/drivers/net/wireless/realtek/rtw88/main.h index f24d17f482aa..4b57542bef1e 100644 --- a/drivers/net/wireless/realtek/rtw88/main.h +++ b/drivers/net/wireless/realtek/rtw88/main.h @@ -2020,8 +2020,6 @@ struct rtw_dev { struct { /* incicate the mail box to use with fw */ u8 last_box_num; - /* protect to send h2c to fw */ - spinlock_t lock; u32 seq; } h2c; From 8647f7f0b9080bc2d2f6e02524782f2f02f159bc Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Fri, 2 Dec 2022 09:12:18 +0100 Subject: [PATCH 1725/2002] wifi: rtw88: Drop coex mutex coex->mutex is used in rtw_coex_info_request() only. Most callers of this function hold rtwdev->mutex already, except for one callsite in the debugfs code. The debugfs code alone doesn't justify the extra lock, so acquire rtwdev->mutex there as well and drop the now unnecessary spinlock. Signed-off-by: Sascha Hauer Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20221202081224.2779981-6-s.hauer@pengutronix.de --- drivers/net/wireless/realtek/rtw88/coex.c | 3 +-- drivers/net/wireless/realtek/rtw88/debug.c | 2 ++ drivers/net/wireless/realtek/rtw88/main.c | 2 -- drivers/net/wireless/realtek/rtw88/main.h | 2 -- 4 files changed, 3 insertions(+), 6 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw88/coex.c b/drivers/net/wireless/realtek/rtw88/coex.c index 6276ad624299..38697237ee5f 100644 --- a/drivers/net/wireless/realtek/rtw88/coex.c +++ b/drivers/net/wireless/realtek/rtw88/coex.c @@ -633,7 +633,7 @@ static struct sk_buff *rtw_coex_info_request(struct rtw_dev *rtwdev, struct rtw_coex *coex = &rtwdev->coex; struct sk_buff *skb_resp = NULL; - mutex_lock(&coex->mutex); + lockdep_assert_held(&rtwdev->mutex); rtw_fw_query_bt_mp_info(rtwdev, req); @@ -650,7 +650,6 @@ static struct sk_buff *rtw_coex_info_request(struct rtw_dev *rtwdev, } out: - mutex_unlock(&coex->mutex); return skb_resp; } diff --git a/drivers/net/wireless/realtek/rtw88/debug.c b/drivers/net/wireless/realtek/rtw88/debug.c index f5b8a77ebc67..fa3d73b333ba 100644 --- a/drivers/net/wireless/realtek/rtw88/debug.c +++ b/drivers/net/wireless/realtek/rtw88/debug.c @@ -841,7 +841,9 @@ static int rtw_debugfs_get_coex_info(struct seq_file *m, void *v) struct rtw_debugfs_priv *debugfs_priv = m->private; struct rtw_dev *rtwdev = debugfs_priv->rtwdev; + mutex_lock(&rtwdev->mutex); rtw_coex_display_coex_info(rtwdev, m); + mutex_unlock(&rtwdev->mutex); return 0; } diff --git a/drivers/net/wireless/realtek/rtw88/main.c b/drivers/net/wireless/realtek/rtw88/main.c index c98e56890401..0a2ce7f50f41 100644 --- a/drivers/net/wireless/realtek/rtw88/main.c +++ b/drivers/net/wireless/realtek/rtw88/main.c @@ -2071,7 +2071,6 @@ int rtw_core_init(struct rtw_dev *rtwdev) spin_lock_init(&rtwdev->tx_report.q_lock); mutex_init(&rtwdev->mutex); - mutex_init(&rtwdev->coex.mutex); mutex_init(&rtwdev->hal.tx_power_mutex); init_waitqueue_head(&rtwdev->coex.wait); @@ -2143,7 +2142,6 @@ void rtw_core_deinit(struct rtw_dev *rtwdev) } mutex_destroy(&rtwdev->mutex); - mutex_destroy(&rtwdev->coex.mutex); mutex_destroy(&rtwdev->hal.tx_power_mutex); } EXPORT_SYMBOL(rtw_core_deinit); diff --git a/drivers/net/wireless/realtek/rtw88/main.h b/drivers/net/wireless/realtek/rtw88/main.h index 4b57542bef1e..77fd48b6cc45 100644 --- a/drivers/net/wireless/realtek/rtw88/main.h +++ b/drivers/net/wireless/realtek/rtw88/main.h @@ -1501,8 +1501,6 @@ struct rtw_coex_stat { }; struct rtw_coex { - /* protects coex info request section */ - struct mutex mutex; struct sk_buff_head queue; wait_queue_head_t wait; From 78d5bf925f30bf9f79a69ce77386902672defe68 Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Fri, 2 Dec 2022 09:12:19 +0100 Subject: [PATCH 1726/2002] wifi: rtw88: iterate over vif/sta list non-atomically The driver uses ieee80211_iterate_active_interfaces_atomic() and ieee80211_iterate_stations_atomic() in several places and does register accesses in the iterators. This doesn't cope with upcoming USB support as registers can only be accessed non-atomically. Split these into a two stage process: First use the atomic iterator functions to collect all active interfaces or stations on a list, then iterate over the list non-atomically and call the iterator on each entry. Signed-off-by: Sascha Hauer Suggested-by: Ping-Ke shih Reviewed-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20221202081224.2779981-7-s.hauer@pengutronix.de --- drivers/net/wireless/realtek/rtw88/phy.c | 6 +- drivers/net/wireless/realtek/rtw88/ps.c | 2 +- drivers/net/wireless/realtek/rtw88/util.c | 103 ++++++++++++++++++++++ drivers/net/wireless/realtek/rtw88/util.h | 12 ++- 4 files changed, 116 insertions(+), 7 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw88/phy.c b/drivers/net/wireless/realtek/rtw88/phy.c index bd7d05e08084..128e75a81bf3 100644 --- a/drivers/net/wireless/realtek/rtw88/phy.c +++ b/drivers/net/wireless/realtek/rtw88/phy.c @@ -300,7 +300,7 @@ static void rtw_phy_stat_rssi(struct rtw_dev *rtwdev) data.rtwdev = rtwdev; data.min_rssi = U8_MAX; - rtw_iterate_stas_atomic(rtwdev, rtw_phy_stat_rssi_iter, &data); + rtw_iterate_stas(rtwdev, rtw_phy_stat_rssi_iter, &data); dm_info->pre_min_rssi = dm_info->min_rssi; dm_info->min_rssi = data.min_rssi; @@ -544,7 +544,7 @@ static void rtw_phy_ra_info_update(struct rtw_dev *rtwdev) if (rtwdev->watch_dog_cnt & 0x3) return; - rtw_iterate_stas_atomic(rtwdev, rtw_phy_ra_info_update_iter, rtwdev); + rtw_iterate_stas(rtwdev, rtw_phy_ra_info_update_iter, rtwdev); } static u32 rtw_phy_get_rrsr_mask(struct rtw_dev *rtwdev, u8 rate_idx) @@ -597,7 +597,7 @@ static void rtw_phy_rrsr_update(struct rtw_dev *rtwdev) struct rtw_dm_info *dm_info = &rtwdev->dm_info; dm_info->rrsr_mask_min = RRSR_RATE_ORDER_MAX; - rtw_iterate_stas_atomic(rtwdev, rtw_phy_rrsr_mask_min_iter, rtwdev); + rtw_iterate_stas(rtwdev, rtw_phy_rrsr_mask_min_iter, rtwdev); rtw_write32(rtwdev, REG_RRSR, dm_info->rrsr_val_init & dm_info->rrsr_mask_min); } diff --git a/drivers/net/wireless/realtek/rtw88/ps.c b/drivers/net/wireless/realtek/rtw88/ps.c index c93da743681f..11594940d6b0 100644 --- a/drivers/net/wireless/realtek/rtw88/ps.c +++ b/drivers/net/wireless/realtek/rtw88/ps.c @@ -61,7 +61,7 @@ int rtw_leave_ips(struct rtw_dev *rtwdev) return ret; } - rtw_iterate_vifs_atomic(rtwdev, rtw_restore_port_cfg_iter, rtwdev); + rtw_iterate_vifs(rtwdev, rtw_restore_port_cfg_iter, rtwdev); rtw_coex_ips_notify(rtwdev, COEX_IPS_LEAVE); diff --git a/drivers/net/wireless/realtek/rtw88/util.c b/drivers/net/wireless/realtek/rtw88/util.c index cdfd66a85075..ff3c269fb1a7 100644 --- a/drivers/net/wireless/realtek/rtw88/util.c +++ b/drivers/net/wireless/realtek/rtw88/util.c @@ -105,3 +105,106 @@ void rtw_desc_to_mcsrate(u16 rate, u8 *mcs, u8 *nss) *mcs = rate - DESC_RATEMCS0; } } + +struct rtw_stas_entry { + struct list_head list; + struct ieee80211_sta *sta; +}; + +struct rtw_iter_stas_data { + struct rtw_dev *rtwdev; + struct list_head list; +}; + +static void rtw_collect_sta_iter(void *data, struct ieee80211_sta *sta) +{ + struct rtw_iter_stas_data *iter_stas = data; + struct rtw_stas_entry *stas_entry; + + stas_entry = kmalloc(sizeof(*stas_entry), GFP_ATOMIC); + if (!stas_entry) + return; + + stas_entry->sta = sta; + list_add_tail(&stas_entry->list, &iter_stas->list); +} + +void rtw_iterate_stas(struct rtw_dev *rtwdev, + void (*iterator)(void *data, + struct ieee80211_sta *sta), + void *data) +{ + struct rtw_iter_stas_data iter_data; + struct rtw_stas_entry *sta_entry, *tmp; + + /* &rtwdev->mutex makes sure no stations can be removed between + * collecting the stations and iterating over them. + */ + lockdep_assert_held(&rtwdev->mutex); + + iter_data.rtwdev = rtwdev; + INIT_LIST_HEAD(&iter_data.list); + + ieee80211_iterate_stations_atomic(rtwdev->hw, rtw_collect_sta_iter, + &iter_data); + + list_for_each_entry_safe(sta_entry, tmp, &iter_data.list, + list) { + list_del_init(&sta_entry->list); + iterator(data, sta_entry->sta); + kfree(sta_entry); + } +} + +struct rtw_vifs_entry { + struct list_head list; + struct ieee80211_vif *vif; + u8 mac[ETH_ALEN]; +}; + +struct rtw_iter_vifs_data { + struct rtw_dev *rtwdev; + struct list_head list; +}; + +static void rtw_collect_vif_iter(void *data, u8 *mac, struct ieee80211_vif *vif) +{ + struct rtw_iter_vifs_data *iter_stas = data; + struct rtw_vifs_entry *vifs_entry; + + vifs_entry = kmalloc(sizeof(*vifs_entry), GFP_ATOMIC); + if (!vifs_entry) + return; + + vifs_entry->vif = vif; + ether_addr_copy(vifs_entry->mac, mac); + list_add_tail(&vifs_entry->list, &iter_stas->list); +} + +void rtw_iterate_vifs(struct rtw_dev *rtwdev, + void (*iterator)(void *data, u8 *mac, + struct ieee80211_vif *vif), + void *data) +{ + struct rtw_iter_vifs_data iter_data; + struct rtw_vifs_entry *vif_entry, *tmp; + + /* &rtwdev->mutex makes sure no interfaces can be removed between + * collecting the interfaces and iterating over them. + */ + lockdep_assert_held(&rtwdev->mutex); + + iter_data.rtwdev = rtwdev; + INIT_LIST_HEAD(&iter_data.list); + + ieee80211_iterate_active_interfaces_atomic(rtwdev->hw, + IEEE80211_IFACE_ITER_NORMAL, + rtw_collect_vif_iter, &iter_data); + + list_for_each_entry_safe(vif_entry, tmp, &iter_data.list, + list) { + list_del_init(&vif_entry->list); + iterator(data, vif_entry->mac, vif_entry->vif); + kfree(vif_entry); + } +} diff --git a/drivers/net/wireless/realtek/rtw88/util.h b/drivers/net/wireless/realtek/rtw88/util.h index 0c23b5069be0..dc8965525400 100644 --- a/drivers/net/wireless/realtek/rtw88/util.h +++ b/drivers/net/wireless/realtek/rtw88/util.h @@ -7,9 +7,6 @@ struct rtw_dev; -#define rtw_iterate_vifs(rtwdev, iterator, data) \ - ieee80211_iterate_active_interfaces(rtwdev->hw, \ - IEEE80211_IFACE_ITER_NORMAL, iterator, data) #define rtw_iterate_vifs_atomic(rtwdev, iterator, data) \ ieee80211_iterate_active_interfaces_atomic(rtwdev->hw, \ IEEE80211_IFACE_ITER_NORMAL, iterator, data) @@ -20,6 +17,15 @@ struct rtw_dev; #define rtw_iterate_keys_rcu(rtwdev, vif, iterator, data) \ ieee80211_iter_keys_rcu((rtwdev)->hw, vif, iterator, data) +void rtw_iterate_vifs(struct rtw_dev *rtwdev, + void (*iterator)(void *data, u8 *mac, + struct ieee80211_vif *vif), + void *data); +void rtw_iterate_stas(struct rtw_dev *rtwdev, + void (*iterator)(void *data, + struct ieee80211_sta *sta), + void *data); + static inline u8 *get_hdr_bssid(struct ieee80211_hdr *hdr) { __le16 fc = hdr->frame_control; From a82dfd33d1237f6c0fb8a7077022189d1fc7ec98 Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Fri, 2 Dec 2022 09:12:20 +0100 Subject: [PATCH 1727/2002] wifi: rtw88: Add common USB chip support Add the common bits and pieces to add USB support to the RTW88 driver. This is based on https://github.com/ulli-kroll/rtw88-usb.git which itself is first written by Neo Jou. Signed-off-by: neo_jou Signed-off-by: Hans Ulli Kroll Signed-off-by: Sascha Hauer Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20221202081224.2779981-8-s.hauer@pengutronix.de --- drivers/net/wireless/realtek/rtw88/Kconfig | 3 + drivers/net/wireless/realtek/rtw88/Makefile | 3 + drivers/net/wireless/realtek/rtw88/mac.c | 3 + drivers/net/wireless/realtek/rtw88/main.c | 4 + drivers/net/wireless/realtek/rtw88/main.h | 4 + drivers/net/wireless/realtek/rtw88/reg.h | 1 + drivers/net/wireless/realtek/rtw88/tx.h | 31 + drivers/net/wireless/realtek/rtw88/usb.c | 911 ++++++++++++++++++++ drivers/net/wireless/realtek/rtw88/usb.h | 107 +++ 9 files changed, 1067 insertions(+) create mode 100644 drivers/net/wireless/realtek/rtw88/usb.c create mode 100644 drivers/net/wireless/realtek/rtw88/usb.h diff --git a/drivers/net/wireless/realtek/rtw88/Kconfig b/drivers/net/wireless/realtek/rtw88/Kconfig index e3d7cb6c1290..1624c5db69ba 100644 --- a/drivers/net/wireless/realtek/rtw88/Kconfig +++ b/drivers/net/wireless/realtek/rtw88/Kconfig @@ -16,6 +16,9 @@ config RTW88_CORE config RTW88_PCI tristate +config RTW88_USB + tristate + config RTW88_8822B tristate diff --git a/drivers/net/wireless/realtek/rtw88/Makefile b/drivers/net/wireless/realtek/rtw88/Makefile index 834c66ec0af9..2c2b0e5133cd 100644 --- a/drivers/net/wireless/realtek/rtw88/Makefile +++ b/drivers/net/wireless/realtek/rtw88/Makefile @@ -46,3 +46,6 @@ rtw88_8821ce-objs := rtw8821ce.o obj-$(CONFIG_RTW88_PCI) += rtw88_pci.o rtw88_pci-objs := pci.o + +obj-$(CONFIG_RTW88_USB) += rtw88_usb.o +rtw88_usb-objs := usb.o diff --git a/drivers/net/wireless/realtek/rtw88/mac.c b/drivers/net/wireless/realtek/rtw88/mac.c index c7e64f7036ac..98777f294945 100644 --- a/drivers/net/wireless/realtek/rtw88/mac.c +++ b/drivers/net/wireless/realtek/rtw88/mac.c @@ -1048,6 +1048,9 @@ static int txdma_queue_mapping(struct rtw_dev *rtwdev) if (rtw_chip_wcpu_11ac(rtwdev)) rtw_write32(rtwdev, REG_H2CQ_CSR, BIT_H2CQ_FULL); + if (rtw_hci_type(rtwdev) == RTW_HCI_TYPE_USB) + rtw_write8_set(rtwdev, REG_TXDMA_PQ_MAP, BIT_RXDMA_ARBBW_EN); + return 0; } diff --git a/drivers/net/wireless/realtek/rtw88/main.c b/drivers/net/wireless/realtek/rtw88/main.c index 0a2ce7f50f41..888427cf3bdf 100644 --- a/drivers/net/wireless/realtek/rtw88/main.c +++ b/drivers/net/wireless/realtek/rtw88/main.c @@ -1783,6 +1783,10 @@ static int rtw_chip_parameter_setup(struct rtw_dev *rtwdev) rtwdev->hci.rpwm_addr = 0x03d9; rtwdev->hci.cpwm_addr = 0x03da; break; + case RTW_HCI_TYPE_USB: + rtwdev->hci.rpwm_addr = 0xfe58; + rtwdev->hci.cpwm_addr = 0xfe57; + break; default: rtw_err(rtwdev, "unsupported hci type\n"); return -EINVAL; diff --git a/drivers/net/wireless/realtek/rtw88/main.h b/drivers/net/wireless/realtek/rtw88/main.h index 77fd48b6cc45..165f299e8e1f 100644 --- a/drivers/net/wireless/realtek/rtw88/main.h +++ b/drivers/net/wireless/realtek/rtw88/main.h @@ -871,6 +871,10 @@ struct rtw_chip_ops { bool is_tx2_path); void (*config_txrx_mode)(struct rtw_dev *rtwdev, u8 tx_path, u8 rx_path, bool is_tx2_path); + /* for USB/SDIO only */ + void (*fill_txdesc_checksum)(struct rtw_dev *rtwdev, + struct rtw_tx_pkt_info *pkt_info, + u8 *txdesc); /* for coex */ void (*coex_set_init)(struct rtw_dev *rtwdev); diff --git a/drivers/net/wireless/realtek/rtw88/reg.h b/drivers/net/wireless/realtek/rtw88/reg.h index 03bd8dc53f72..8852b24d6c2a 100644 --- a/drivers/net/wireless/realtek/rtw88/reg.h +++ b/drivers/net/wireless/realtek/rtw88/reg.h @@ -184,6 +184,7 @@ #define BIT_TXDMA_VIQ_MAP(x) \ (((x) & BIT_MASK_TXDMA_VIQ_MAP) << BIT_SHIFT_TXDMA_VIQ_MAP) #define REG_TXDMA_PQ_MAP 0x010C +#define BIT_RXDMA_ARBBW_EN BIT(0) #define BIT_SHIFT_TXDMA_BEQ_MAP 8 #define BIT_MASK_TXDMA_BEQ_MAP 0x3 #define BIT_TXDMA_BEQ_MAP(x) \ diff --git a/drivers/net/wireless/realtek/rtw88/tx.h b/drivers/net/wireless/realtek/rtw88/tx.h index 8419603adce4..a2f3ac326041 100644 --- a/drivers/net/wireless/realtek/rtw88/tx.h +++ b/drivers/net/wireless/realtek/rtw88/tx.h @@ -71,6 +71,14 @@ le32p_replace_bits((__le32 *)(txdesc) + 0x03, value, BIT(15)) #define SET_TX_DESC_BT_NULL(txdesc, value) \ le32p_replace_bits((__le32 *)(txdesc) + 0x02, value, BIT(23)) +#define SET_TX_DESC_TXDESC_CHECKSUM(txdesc, value) \ + le32p_replace_bits((__le32 *)(txdesc) + 0x07, value, GENMASK(15, 0)) +#define SET_TX_DESC_DMA_TXAGG_NUM(txdesc, value) \ + le32p_replace_bits((__le32 *)(txdesc) + 0x07, value, GENMASK(31, 24)) +#define GET_TX_DESC_PKT_OFFSET(txdesc) \ + le32_get_bits(*((__le32 *)(txdesc) + 0x01), GENMASK(28, 24)) +#define GET_TX_DESC_QSEL(txdesc) \ + le32_get_bits(*((__le32 *)(txdesc) + 0x01), GENMASK(12, 8)) enum rtw_tx_desc_queue_select { TX_DESC_QSEL_TID0 = 0, @@ -123,4 +131,27 @@ rtw_tx_write_data_h2c_get(struct rtw_dev *rtwdev, struct rtw_tx_pkt_info *pkt_info, u8 *buf, u32 size); +static inline +void fill_txdesc_checksum_common(u8 *txdesc, size_t words) +{ + __le16 chksum = 0; + __le16 *data = (__le16 *)(txdesc); + + SET_TX_DESC_TXDESC_CHECKSUM(txdesc, 0x0000); + + while (words--) + chksum ^= *data++; + + SET_TX_DESC_TXDESC_CHECKSUM(txdesc, __le16_to_cpu(chksum)); +} + +static inline void rtw_tx_fill_txdesc_checksum(struct rtw_dev *rtwdev, + struct rtw_tx_pkt_info *pkt_info, + u8 *txdesc) +{ + const struct rtw_chip_info *chip = rtwdev->chip; + + chip->ops->fill_txdesc_checksum(rtwdev, pkt_info, txdesc); +} + #endif diff --git a/drivers/net/wireless/realtek/rtw88/usb.c b/drivers/net/wireless/realtek/rtw88/usb.c new file mode 100644 index 000000000000..4ef38279b64c --- /dev/null +++ b/drivers/net/wireless/realtek/rtw88/usb.c @@ -0,0 +1,911 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* Copyright(c) 2018-2019 Realtek Corporation + */ + +#include +#include +#include +#include "main.h" +#include "debug.h" +#include "reg.h" +#include "tx.h" +#include "rx.h" +#include "fw.h" +#include "ps.h" +#include "usb.h" + +#define RTW_USB_MAX_RXQ_LEN 512 + +struct rtw_usb_txcb { + struct rtw_dev *rtwdev; + struct sk_buff_head tx_ack_queue; +}; + +static void rtw_usb_fill_tx_checksum(struct rtw_usb *rtwusb, + struct sk_buff *skb, int agg_num) +{ + struct rtw_dev *rtwdev = rtwusb->rtwdev; + struct rtw_tx_pkt_info pkt_info; + + SET_TX_DESC_DMA_TXAGG_NUM(skb->data, agg_num); + pkt_info.pkt_offset = GET_TX_DESC_PKT_OFFSET(skb->data); + rtw_tx_fill_txdesc_checksum(rtwdev, &pkt_info, skb->data); +} + +static u32 rtw_usb_read(struct rtw_dev *rtwdev, u32 addr, u16 len) +{ + struct rtw_usb *rtwusb = rtw_get_usb_priv(rtwdev); + struct usb_device *udev = rtwusb->udev; + __le32 *data; + unsigned long flags; + int idx, ret; + static int count; + + spin_lock_irqsave(&rtwusb->usb_lock, flags); + + idx = rtwusb->usb_data_index; + rtwusb->usb_data_index = (idx + 1) & (RTW_USB_MAX_RXTX_COUNT - 1); + + spin_unlock_irqrestore(&rtwusb->usb_lock, flags); + + data = &rtwusb->usb_data[idx]; + + ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), + RTW_USB_CMD_REQ, RTW_USB_CMD_READ, addr, + RTW_USB_VENQT_CMD_IDX, data, len, 1000); + if (ret < 0 && ret != -ENODEV && count++ < 4) + rtw_err(rtwdev, "read register 0x%x failed with %d\n", + addr, ret); + + return le32_to_cpu(*data); +} + +static u8 rtw_usb_read8(struct rtw_dev *rtwdev, u32 addr) +{ + return (u8)rtw_usb_read(rtwdev, addr, 1); +} + +static u16 rtw_usb_read16(struct rtw_dev *rtwdev, u32 addr) +{ + return (u16)rtw_usb_read(rtwdev, addr, 2); +} + +static u32 rtw_usb_read32(struct rtw_dev *rtwdev, u32 addr) +{ + return (u32)rtw_usb_read(rtwdev, addr, 4); +} + +static void rtw_usb_write(struct rtw_dev *rtwdev, u32 addr, u32 val, int len) +{ + struct rtw_usb *rtwusb = (struct rtw_usb *)rtwdev->priv; + struct usb_device *udev = rtwusb->udev; + unsigned long flags; + __le32 *data; + int idx, ret; + static int count; + + spin_lock_irqsave(&rtwusb->usb_lock, flags); + + idx = rtwusb->usb_data_index; + rtwusb->usb_data_index = (idx + 1) & (RTW_USB_MAX_RXTX_COUNT - 1); + + spin_unlock_irqrestore(&rtwusb->usb_lock, flags); + + data = &rtwusb->usb_data[idx]; + + *data = cpu_to_le32(val); + + ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), + RTW_USB_CMD_REQ, RTW_USB_CMD_WRITE, + addr, 0, data, len, 30000); + if (ret < 0 && ret != -ENODEV && count++ < 4) + rtw_err(rtwdev, "write register 0x%x failed with %d\n", + addr, ret); +} + +static void rtw_usb_write8(struct rtw_dev *rtwdev, u32 addr, u8 val) +{ + rtw_usb_write(rtwdev, addr, val, 1); +} + +static void rtw_usb_write16(struct rtw_dev *rtwdev, u32 addr, u16 val) +{ + rtw_usb_write(rtwdev, addr, val, 2); +} + +static void rtw_usb_write32(struct rtw_dev *rtwdev, u32 addr, u32 val) +{ + rtw_usb_write(rtwdev, addr, val, 4); +} + +static int rtw_usb_parse(struct rtw_dev *rtwdev, + struct usb_interface *interface) +{ + struct rtw_usb *rtwusb = rtw_get_usb_priv(rtwdev); + struct usb_host_interface *host_interface = &interface->altsetting[0]; + struct usb_interface_descriptor *interface_desc = &host_interface->desc; + struct usb_endpoint_descriptor *endpoint; + struct usb_device *usbd = interface_to_usbdev(interface); + int num_out_pipes = 0; + int i; + u8 num; + + for (i = 0; i < interface_desc->bNumEndpoints; i++) { + endpoint = &host_interface->endpoint[i].desc; + num = usb_endpoint_num(endpoint); + + if (usb_endpoint_dir_in(endpoint) && + usb_endpoint_xfer_bulk(endpoint)) { + if (rtwusb->pipe_in) { + rtw_err(rtwdev, "IN pipes overflow\n"); + return -EINVAL; + } + + rtwusb->pipe_in = num; + } + + if (usb_endpoint_dir_in(endpoint) && + usb_endpoint_xfer_int(endpoint)) { + if (rtwusb->pipe_interrupt) { + rtw_err(rtwdev, "INT pipes overflow\n"); + return -EINVAL; + } + + rtwusb->pipe_interrupt = num; + } + + if (usb_endpoint_dir_out(endpoint) && + usb_endpoint_xfer_bulk(endpoint)) { + if (num_out_pipes >= ARRAY_SIZE(rtwusb->out_ep)) { + rtw_err(rtwdev, "OUT pipes overflow\n"); + return -EINVAL; + } + + rtwusb->out_ep[num_out_pipes++] = num; + } + } + + switch (usbd->speed) { + case USB_SPEED_LOW: + case USB_SPEED_FULL: + rtwusb->bulkout_size = RTW_USB_FULL_SPEED_BULK_SIZE; + break; + case USB_SPEED_HIGH: + rtwusb->bulkout_size = RTW_USB_HIGH_SPEED_BULK_SIZE; + break; + case USB_SPEED_SUPER: + rtwusb->bulkout_size = RTW_USB_SUPER_SPEED_BULK_SIZE; + break; + default: + rtw_err(rtwdev, "failed to detect usb speed\n"); + return -EINVAL; + } + + rtwdev->hci.bulkout_num = num_out_pipes; + + switch (num_out_pipes) { + case 4: + case 3: + rtwusb->qsel_to_ep[TX_DESC_QSEL_TID0] = 2; + rtwusb->qsel_to_ep[TX_DESC_QSEL_TID1] = 2; + rtwusb->qsel_to_ep[TX_DESC_QSEL_TID2] = 2; + rtwusb->qsel_to_ep[TX_DESC_QSEL_TID3] = 2; + rtwusb->qsel_to_ep[TX_DESC_QSEL_TID4] = 1; + rtwusb->qsel_to_ep[TX_DESC_QSEL_TID5] = 1; + rtwusb->qsel_to_ep[TX_DESC_QSEL_TID6] = 0; + rtwusb->qsel_to_ep[TX_DESC_QSEL_TID7] = 0; + break; + case 2: + rtwusb->qsel_to_ep[TX_DESC_QSEL_TID0] = 1; + rtwusb->qsel_to_ep[TX_DESC_QSEL_TID1] = 1; + rtwusb->qsel_to_ep[TX_DESC_QSEL_TID2] = 1; + rtwusb->qsel_to_ep[TX_DESC_QSEL_TID3] = 1; + break; + case 1: + break; + default: + rtw_err(rtwdev, "failed to get out_pipes(%d)\n", num_out_pipes); + return -EINVAL; + } + + return 0; +} + +static void rtw_usb_write_port_tx_complete(struct urb *urb) +{ + struct rtw_usb_txcb *txcb = urb->context; + struct rtw_dev *rtwdev = txcb->rtwdev; + struct ieee80211_hw *hw = rtwdev->hw; + + while (true) { + struct sk_buff *skb = skb_dequeue(&txcb->tx_ack_queue); + struct ieee80211_tx_info *info; + struct rtw_usb_tx_data *tx_data; + + if (!skb) + break; + + info = IEEE80211_SKB_CB(skb); + tx_data = rtw_usb_get_tx_data(skb); + + /* enqueue to wait for tx report */ + if (info->flags & IEEE80211_TX_CTL_REQ_TX_STATUS) { + rtw_tx_report_enqueue(rtwdev, skb, tx_data->sn); + continue; + } + + /* always ACK for others, then they won't be marked as drop */ + ieee80211_tx_info_clear_status(info); + if (info->flags & IEEE80211_TX_CTL_NO_ACK) + info->flags |= IEEE80211_TX_STAT_NOACK_TRANSMITTED; + else + info->flags |= IEEE80211_TX_STAT_ACK; + + ieee80211_tx_status_irqsafe(hw, skb); + } + + kfree(txcb); +} + +static int qsel_to_ep(struct rtw_usb *rtwusb, unsigned int qsel) +{ + if (qsel >= ARRAY_SIZE(rtwusb->qsel_to_ep)) + return 0; + + return rtwusb->qsel_to_ep[qsel]; +} + +static int rtw_usb_write_port(struct rtw_dev *rtwdev, u8 qsel, struct sk_buff *skb, + usb_complete_t cb, void *context) +{ + struct rtw_usb *rtwusb = rtw_get_usb_priv(rtwdev); + struct usb_device *usbd = rtwusb->udev; + struct urb *urb; + unsigned int pipe; + int ret; + int ep = qsel_to_ep(rtwusb, qsel); + + pipe = usb_sndbulkpipe(usbd, rtwusb->out_ep[ep]); + urb = usb_alloc_urb(0, GFP_ATOMIC); + if (!urb) + return -ENOMEM; + + usb_fill_bulk_urb(urb, usbd, pipe, skb->data, skb->len, cb, context); + ret = usb_submit_urb(urb, GFP_ATOMIC); + + usb_free_urb(urb); + + return ret; +} + +static bool rtw_usb_tx_agg_skb(struct rtw_usb *rtwusb, struct sk_buff_head *list) +{ + struct rtw_dev *rtwdev = rtwusb->rtwdev; + struct rtw_usb_txcb *txcb; + struct sk_buff *skb_head; + struct sk_buff *skb_iter; + int agg_num = 0; + unsigned int align_next = 0; + + if (skb_queue_empty(list)) + return false; + + txcb = kmalloc(sizeof(*txcb), GFP_ATOMIC); + if (!txcb) + return false; + + txcb->rtwdev = rtwdev; + skb_queue_head_init(&txcb->tx_ack_queue); + + skb_iter = skb_dequeue(list); + + if (skb_queue_empty(list)) { + skb_head = skb_iter; + goto queue; + } + + skb_head = dev_alloc_skb(RTW_USB_MAX_XMITBUF_SZ); + if (!skb_head) { + skb_head = skb_iter; + goto queue; + } + + while (skb_iter) { + unsigned long flags; + + skb_put(skb_head, align_next); + skb_put_data(skb_head, skb_iter->data, skb_iter->len); + + align_next = ALIGN(skb_iter->len, 8) - skb_iter->len; + + agg_num++; + + skb_queue_tail(&txcb->tx_ack_queue, skb_iter); + + spin_lock_irqsave(&list->lock, flags); + + skb_iter = skb_peek(list); + + if (skb_iter && skb_iter->len + skb_head->len <= RTW_USB_MAX_XMITBUF_SZ) + __skb_unlink(skb_iter, list); + else + skb_iter = NULL; + spin_unlock_irqrestore(&list->lock, flags); + } + + if (agg_num > 1) + rtw_usb_fill_tx_checksum(rtwusb, skb_head, agg_num); + +queue: + skb_queue_tail(&txcb->tx_ack_queue, skb_head); + + rtw_usb_write_port(rtwdev, GET_TX_DESC_QSEL(skb_head->data), skb_head, + rtw_usb_write_port_tx_complete, txcb); + + return true; +} + +static void rtw_usb_tx_handler(struct work_struct *work) +{ + struct rtw_usb *rtwusb = container_of(work, struct rtw_usb, tx_work); + int i, limit; + + for (i = ARRAY_SIZE(rtwusb->tx_queue) - 1; i >= 0; i--) { + for (limit = 0; limit < 200; limit++) { + struct sk_buff_head *list = &rtwusb->tx_queue[i]; + + if (!rtw_usb_tx_agg_skb(rtwusb, list)) + break; + } + } +} + +static void rtw_usb_tx_queue_purge(struct rtw_usb *rtwusb) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(rtwusb->tx_queue); i++) + skb_queue_purge(&rtwusb->tx_queue[i]); +} + +static void rtw_usb_write_port_complete(struct urb *urb) +{ + struct sk_buff *skb = urb->context; + + dev_kfree_skb_any(skb); +} + +static int rtw_usb_write_data(struct rtw_dev *rtwdev, + struct rtw_tx_pkt_info *pkt_info, + u8 *buf) +{ + const struct rtw_chip_info *chip = rtwdev->chip; + struct sk_buff *skb; + unsigned int desclen, headsize, size; + u8 qsel; + int ret = 0; + + size = pkt_info->tx_pkt_size; + qsel = pkt_info->qsel; + desclen = chip->tx_pkt_desc_sz; + headsize = pkt_info->offset ? pkt_info->offset : desclen; + + skb = dev_alloc_skb(headsize + size); + if (unlikely(!skb)) + return -ENOMEM; + + skb_reserve(skb, headsize); + skb_put_data(skb, buf, size); + skb_push(skb, headsize); + memset(skb->data, 0, headsize); + rtw_tx_fill_tx_desc(pkt_info, skb); + rtw_tx_fill_txdesc_checksum(rtwdev, pkt_info, skb->data); + + ret = rtw_usb_write_port(rtwdev, qsel, skb, + rtw_usb_write_port_complete, skb); + if (unlikely(ret)) + rtw_err(rtwdev, "failed to do USB write, ret=%d\n", ret); + + return ret; +} + +static int rtw_usb_write_data_rsvd_page(struct rtw_dev *rtwdev, u8 *buf, + u32 size) +{ + const struct rtw_chip_info *chip = rtwdev->chip; + struct rtw_usb *rtwusb; + struct rtw_tx_pkt_info pkt_info = {0}; + u32 len, desclen; + + rtwusb = rtw_get_usb_priv(rtwdev); + + pkt_info.tx_pkt_size = size; + pkt_info.qsel = TX_DESC_QSEL_BEACON; + + desclen = chip->tx_pkt_desc_sz; + len = desclen + size; + if (len % rtwusb->bulkout_size == 0) { + len += RTW_USB_PACKET_OFFSET_SZ; + pkt_info.offset = desclen + RTW_USB_PACKET_OFFSET_SZ; + pkt_info.pkt_offset = 1; + } else { + pkt_info.offset = desclen; + } + + return rtw_usb_write_data(rtwdev, &pkt_info, buf); +} + +static int rtw_usb_write_data_h2c(struct rtw_dev *rtwdev, u8 *buf, u32 size) +{ + struct rtw_tx_pkt_info pkt_info = {0}; + + pkt_info.tx_pkt_size = size; + pkt_info.qsel = TX_DESC_QSEL_H2C; + + return rtw_usb_write_data(rtwdev, &pkt_info, buf); +} + +static u8 rtw_usb_tx_queue_mapping_to_qsel(struct sk_buff *skb) +{ + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; + __le16 fc = hdr->frame_control; + u8 qsel; + + if (unlikely(ieee80211_is_mgmt(fc) || ieee80211_is_ctl(fc))) + qsel = TX_DESC_QSEL_MGMT; + else if (skb_get_queue_mapping(skb) <= IEEE80211_AC_BK) + qsel = skb->priority; + else + qsel = TX_DESC_QSEL_BEACON; + + return qsel; +} + +static int rtw_usb_tx_write(struct rtw_dev *rtwdev, + struct rtw_tx_pkt_info *pkt_info, + struct sk_buff *skb) +{ + struct rtw_usb *rtwusb = rtw_get_usb_priv(rtwdev); + const struct rtw_chip_info *chip = rtwdev->chip; + struct rtw_usb_tx_data *tx_data; + u8 *pkt_desc; + int ep; + + pkt_desc = skb_push(skb, chip->tx_pkt_desc_sz); + memset(pkt_desc, 0, chip->tx_pkt_desc_sz); + pkt_info->qsel = rtw_usb_tx_queue_mapping_to_qsel(skb); + ep = qsel_to_ep(rtwusb, pkt_info->qsel); + rtw_tx_fill_tx_desc(pkt_info, skb); + rtw_tx_fill_txdesc_checksum(rtwdev, pkt_info, skb->data); + tx_data = rtw_usb_get_tx_data(skb); + tx_data->sn = pkt_info->sn; + + skb_queue_tail(&rtwusb->tx_queue[ep], skb); + + return 0; +} + +static void rtw_usb_tx_kick_off(struct rtw_dev *rtwdev) +{ + struct rtw_usb *rtwusb = rtw_get_usb_priv(rtwdev); + + queue_work(rtwusb->txwq, &rtwusb->tx_work); +} + +static void rtw_usb_rx_handler(struct work_struct *work) +{ + struct rtw_usb *rtwusb = container_of(work, struct rtw_usb, rx_work); + struct rtw_dev *rtwdev = rtwusb->rtwdev; + const struct rtw_chip_info *chip = rtwdev->chip; + struct rtw_rx_pkt_stat pkt_stat; + struct ieee80211_rx_status rx_status; + struct sk_buff *skb; + u32 pkt_desc_sz = chip->rx_pkt_desc_sz; + u32 pkt_offset; + u8 *rx_desc; + int limit; + + for (limit = 0; limit < 200; limit++) { + skb = skb_dequeue(&rtwusb->rx_queue); + if (!skb) + break; + + rx_desc = skb->data; + chip->ops->query_rx_desc(rtwdev, rx_desc, &pkt_stat, + &rx_status); + pkt_offset = pkt_desc_sz + pkt_stat.drv_info_sz + + pkt_stat.shift; + + if (pkt_stat.is_c2h) { + skb_put(skb, pkt_stat.pkt_len + pkt_offset); + rtw_fw_c2h_cmd_rx_irqsafe(rtwdev, pkt_offset, skb); + continue; + } + + if (skb_queue_len(&rtwusb->rx_queue) >= RTW_USB_MAX_RXQ_LEN) { + rtw_err(rtwdev, "failed to get rx_queue, overflow\n"); + dev_kfree_skb_any(skb); + continue; + } + + skb_put(skb, pkt_stat.pkt_len); + skb_reserve(skb, pkt_offset); + memcpy(skb->cb, &rx_status, sizeof(rx_status)); + ieee80211_rx_irqsafe(rtwdev->hw, skb); + } +} + +static void rtw_usb_read_port_complete(struct urb *urb); + +static void rtw_usb_rx_resubmit(struct rtw_usb *rtwusb, struct rx_usb_ctrl_block *rxcb) +{ + struct rtw_dev *rtwdev = rtwusb->rtwdev; + int error; + + rxcb->rx_skb = alloc_skb(RTW_USB_MAX_RECVBUF_SZ, GFP_ATOMIC); + if (!rxcb->rx_skb) + return; + + usb_fill_bulk_urb(rxcb->rx_urb, rtwusb->udev, + usb_rcvbulkpipe(rtwusb->udev, rtwusb->pipe_in), + rxcb->rx_skb->data, RTW_USB_MAX_RECVBUF_SZ, + rtw_usb_read_port_complete, rxcb); + + error = usb_submit_urb(rxcb->rx_urb, GFP_ATOMIC); + if (error) { + kfree_skb(rxcb->rx_skb); + if (error != -ENODEV) + rtw_err(rtwdev, "Err sending rx data urb %d\n", + error); + } +} + +static void rtw_usb_read_port_complete(struct urb *urb) +{ + struct rx_usb_ctrl_block *rxcb = urb->context; + struct rtw_dev *rtwdev = rxcb->rtwdev; + struct rtw_usb *rtwusb = rtw_get_usb_priv(rtwdev); + struct sk_buff *skb = rxcb->rx_skb; + + if (urb->status == 0) { + if (urb->actual_length >= RTW_USB_MAX_RECVBUF_SZ || + urb->actual_length < 24) { + rtw_err(rtwdev, "failed to get urb length:%d\n", + urb->actual_length); + if (skb) + dev_kfree_skb_any(skb); + } else { + skb_queue_tail(&rtwusb->rx_queue, skb); + queue_work(rtwusb->rxwq, &rtwusb->rx_work); + } + rtw_usb_rx_resubmit(rtwusb, rxcb); + } else { + switch (urb->status) { + case -EINVAL: + case -EPIPE: + case -ENODEV: + case -ESHUTDOWN: + case -ENOENT: + case -EPROTO: + case -EILSEQ: + case -ETIME: + case -ECOMM: + case -EOVERFLOW: + case -EINPROGRESS: + break; + default: + rtw_err(rtwdev, "status %d\n", urb->status); + break; + } + if (skb) + dev_kfree_skb_any(skb); + } +} + +static void rtw_usb_cancel_rx_bufs(struct rtw_usb *rtwusb) +{ + struct rx_usb_ctrl_block *rxcb; + int i; + + for (i = 0; i < RTW_USB_RXCB_NUM; i++) { + rxcb = &rtwusb->rx_cb[i]; + if (rxcb->rx_urb) + usb_kill_urb(rxcb->rx_urb); + } +} + +static void rtw_usb_free_rx_bufs(struct rtw_usb *rtwusb) +{ + struct rx_usb_ctrl_block *rxcb; + int i; + + for (i = 0; i < RTW_USB_RXCB_NUM; i++) { + rxcb = &rtwusb->rx_cb[i]; + if (rxcb->rx_urb) { + usb_kill_urb(rxcb->rx_urb); + usb_free_urb(rxcb->rx_urb); + } + } +} + +static int rtw_usb_alloc_rx_bufs(struct rtw_usb *rtwusb) +{ + int i; + + for (i = 0; i < RTW_USB_RXCB_NUM; i++) { + struct rx_usb_ctrl_block *rxcb = &rtwusb->rx_cb[i]; + + rxcb->n = i; + rxcb->rtwdev = rtwusb->rtwdev; + rxcb->rx_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!rxcb->rx_urb) + goto err; + } + + return 0; +err: + rtw_usb_free_rx_bufs(rtwusb); + return -ENOMEM; +} + +static int rtw_usb_setup(struct rtw_dev *rtwdev) +{ + /* empty function for rtw_hci_ops */ + return 0; +} + +static int rtw_usb_start(struct rtw_dev *rtwdev) +{ + return 0; +} + +static void rtw_usb_stop(struct rtw_dev *rtwdev) +{ +} + +static void rtw_usb_deep_ps(struct rtw_dev *rtwdev, bool enter) +{ + /* empty function for rtw_hci_ops */ +} + +static void rtw_usb_link_ps(struct rtw_dev *rtwdev, bool enter) +{ + /* empty function for rtw_hci_ops */ +} + +static void rtw_usb_interface_cfg(struct rtw_dev *rtwdev) +{ + /* empty function for rtw_hci_ops */ +} + +static struct rtw_hci_ops rtw_usb_ops = { + .tx_write = rtw_usb_tx_write, + .tx_kick_off = rtw_usb_tx_kick_off, + .setup = rtw_usb_setup, + .start = rtw_usb_start, + .stop = rtw_usb_stop, + .deep_ps = rtw_usb_deep_ps, + .link_ps = rtw_usb_link_ps, + .interface_cfg = rtw_usb_interface_cfg, + + .write8 = rtw_usb_write8, + .write16 = rtw_usb_write16, + .write32 = rtw_usb_write32, + .read8 = rtw_usb_read8, + .read16 = rtw_usb_read16, + .read32 = rtw_usb_read32, + + .write_data_rsvd_page = rtw_usb_write_data_rsvd_page, + .write_data_h2c = rtw_usb_write_data_h2c, +}; + +static int rtw_usb_init_rx(struct rtw_dev *rtwdev) +{ + struct rtw_usb *rtwusb = rtw_get_usb_priv(rtwdev); + int i; + + rtwusb->rxwq = create_singlethread_workqueue("rtw88_usb: rx wq"); + if (!rtwusb->rxwq) { + rtw_err(rtwdev, "failed to create RX work queue\n"); + return -ENOMEM; + } + + skb_queue_head_init(&rtwusb->rx_queue); + + INIT_WORK(&rtwusb->rx_work, rtw_usb_rx_handler); + + for (i = 0; i < RTW_USB_RXCB_NUM; i++) { + struct rx_usb_ctrl_block *rxcb = &rtwusb->rx_cb[i]; + + rtw_usb_rx_resubmit(rtwusb, rxcb); + } + + return 0; +} + +static void rtw_usb_deinit_rx(struct rtw_dev *rtwdev) +{ + struct rtw_usb *rtwusb = rtw_get_usb_priv(rtwdev); + + skb_queue_purge(&rtwusb->rx_queue); + + flush_workqueue(rtwusb->rxwq); + destroy_workqueue(rtwusb->rxwq); +} + +static int rtw_usb_init_tx(struct rtw_dev *rtwdev) +{ + struct rtw_usb *rtwusb = rtw_get_usb_priv(rtwdev); + int i; + + rtwusb->txwq = create_singlethread_workqueue("rtw88_usb: tx wq"); + if (!rtwusb->txwq) { + rtw_err(rtwdev, "failed to create TX work queue\n"); + return -ENOMEM; + } + + for (i = 0; i < ARRAY_SIZE(rtwusb->tx_queue); i++) + skb_queue_head_init(&rtwusb->tx_queue[i]); + + INIT_WORK(&rtwusb->tx_work, rtw_usb_tx_handler); + + return 0; +} + +static void rtw_usb_deinit_tx(struct rtw_dev *rtwdev) +{ + struct rtw_usb *rtwusb = rtw_get_usb_priv(rtwdev); + + rtw_usb_tx_queue_purge(rtwusb); + flush_workqueue(rtwusb->txwq); + destroy_workqueue(rtwusb->txwq); +} + +static int rtw_usb_intf_init(struct rtw_dev *rtwdev, + struct usb_interface *intf) +{ + struct rtw_usb *rtwusb = rtw_get_usb_priv(rtwdev); + struct usb_device *udev = usb_get_dev(interface_to_usbdev(intf)); + int ret; + + rtwusb->udev = udev; + ret = rtw_usb_parse(rtwdev, intf); + if (ret) + return ret; + + rtwusb->usb_data = kcalloc(RTW_USB_MAX_RXTX_COUNT, sizeof(u32), + GFP_KERNEL); + if (!rtwusb->usb_data) + return -ENOMEM; + + usb_set_intfdata(intf, rtwdev->hw); + + SET_IEEE80211_DEV(rtwdev->hw, &intf->dev); + spin_lock_init(&rtwusb->usb_lock); + + return 0; +} + +static void rtw_usb_intf_deinit(struct rtw_dev *rtwdev, + struct usb_interface *intf) +{ + struct rtw_usb *rtwusb = rtw_get_usb_priv(rtwdev); + + usb_put_dev(rtwusb->udev); + usb_set_intfdata(intf, NULL); +} + +int rtw_usb_probe(struct usb_interface *intf, const struct usb_device_id *id) +{ + struct rtw_dev *rtwdev; + struct ieee80211_hw *hw; + struct rtw_usb *rtwusb; + int drv_data_size; + int ret; + + drv_data_size = sizeof(struct rtw_dev) + sizeof(struct rtw_usb); + hw = ieee80211_alloc_hw(drv_data_size, &rtw_ops); + if (!hw) + return -ENOMEM; + + rtwdev = hw->priv; + rtwdev->hw = hw; + rtwdev->dev = &intf->dev; + rtwdev->chip = (struct rtw_chip_info *)id->driver_info; + rtwdev->hci.ops = &rtw_usb_ops; + rtwdev->hci.type = RTW_HCI_TYPE_USB; + + rtwusb = rtw_get_usb_priv(rtwdev); + rtwusb->rtwdev = rtwdev; + + ret = rtw_usb_alloc_rx_bufs(rtwusb); + if (ret) + return ret; + + ret = rtw_core_init(rtwdev); + if (ret) + goto err_release_hw; + + ret = rtw_usb_intf_init(rtwdev, intf); + if (ret) { + rtw_err(rtwdev, "failed to init USB interface\n"); + goto err_deinit_core; + } + + ret = rtw_usb_init_tx(rtwdev); + if (ret) { + rtw_err(rtwdev, "failed to init USB TX\n"); + goto err_destroy_usb; + } + + ret = rtw_usb_init_rx(rtwdev); + if (ret) { + rtw_err(rtwdev, "failed to init USB RX\n"); + goto err_destroy_txwq; + } + + ret = rtw_chip_info_setup(rtwdev); + if (ret) { + rtw_err(rtwdev, "failed to setup chip information\n"); + goto err_destroy_rxwq; + } + + ret = rtw_register_hw(rtwdev, rtwdev->hw); + if (ret) { + rtw_err(rtwdev, "failed to register hw\n"); + goto err_destroy_rxwq; + } + + return 0; + +err_destroy_rxwq: + rtw_usb_deinit_rx(rtwdev); + +err_destroy_txwq: + rtw_usb_deinit_tx(rtwdev); + +err_destroy_usb: + rtw_usb_intf_deinit(rtwdev, intf); + +err_deinit_core: + rtw_core_deinit(rtwdev); + +err_release_hw: + ieee80211_free_hw(hw); + + return ret; +} +EXPORT_SYMBOL(rtw_usb_probe); + +void rtw_usb_disconnect(struct usb_interface *intf) +{ + struct ieee80211_hw *hw = usb_get_intfdata(intf); + struct rtw_dev *rtwdev; + struct rtw_usb *rtwusb; + + if (!hw) + return; + + rtwdev = hw->priv; + rtwusb = rtw_get_usb_priv(rtwdev); + + rtw_usb_cancel_rx_bufs(rtwusb); + + rtw_unregister_hw(rtwdev, hw); + rtw_usb_deinit_tx(rtwdev); + rtw_usb_deinit_rx(rtwdev); + + if (rtwusb->udev->state != USB_STATE_NOTATTACHED) + usb_reset_device(rtwusb->udev); + + rtw_usb_free_rx_bufs(rtwusb); + + rtw_usb_intf_deinit(rtwdev, intf); + rtw_core_deinit(rtwdev); + ieee80211_free_hw(hw); +} +EXPORT_SYMBOL(rtw_usb_disconnect); + +MODULE_AUTHOR("Realtek Corporation"); +MODULE_DESCRIPTION("Realtek 802.11ac wireless USB driver"); +MODULE_LICENSE("Dual BSD/GPL"); diff --git a/drivers/net/wireless/realtek/rtw88/usb.h b/drivers/net/wireless/realtek/rtw88/usb.h new file mode 100644 index 000000000000..30647f0dd61c --- /dev/null +++ b/drivers/net/wireless/realtek/rtw88/usb.h @@ -0,0 +1,107 @@ +/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ +/* Copyright(c) 2018-2019 Realtek Corporation + */ + +#ifndef __RTW_USB_H_ +#define __RTW_USB_H_ + +#define FW_8192C_START_ADDRESS 0x1000 +#define FW_8192C_END_ADDRESS 0x5fff + +#define RTW_USB_MAX_RXTX_COUNT 128 +#define RTW_USB_VENQT_MAX_BUF_SIZE 254 +#define MAX_USBCTRL_VENDORREQ_TIMES 10 + +#define RTW_USB_CMD_READ 0xc0 +#define RTW_USB_CMD_WRITE 0x40 +#define RTW_USB_CMD_REQ 0x05 + +#define RTW_USB_VENQT_CMD_IDX 0x00 + +#define RTW_USB_SUPER_SPEED_BULK_SIZE 1024 +#define RTW_USB_HIGH_SPEED_BULK_SIZE 512 +#define RTW_USB_FULL_SPEED_BULK_SIZE 64 + +#define RTW_USB_TX_SEL_HQ BIT(0) +#define RTW_USB_TX_SEL_LQ BIT(1) +#define RTW_USB_TX_SEL_NQ BIT(2) +#define RTW_USB_TX_SEL_EQ BIT(3) + +#define RTW_USB_BULK_IN_ADDR 0x80 +#define RTW_USB_INT_IN_ADDR 0x81 + +#define RTW_USB_HW_QUEUE_ENTRY 8 + +#define RTW_USB_PACKET_OFFSET_SZ 8 +#define RTW_USB_MAX_XMITBUF_SZ (1024 * 20) +#define RTW_USB_MAX_RECVBUF_SZ 32768 + +#define RTW_USB_RECVBUFF_ALIGN_SZ 8 + +#define RTW_USB_RXAGG_SIZE 6 +#define RTW_USB_RXAGG_TIMEOUT 10 + +#define RTW_USB_RXCB_NUM 4 + +#define RTW_USB_EP_MAX 4 + +#define TX_DESC_QSEL_MAX 20 + +#define RTW_USB_VENDOR_ID_REALTEK 0x0bda + +static inline struct rtw_usb *rtw_get_usb_priv(struct rtw_dev *rtwdev) +{ + return (struct rtw_usb *)rtwdev->priv; +} + +struct rx_usb_ctrl_block { + struct rtw_dev *rtwdev; + struct urb *rx_urb; + struct sk_buff *rx_skb; + int n; +}; + +struct rtw_usb_tx_data { + u8 sn; +}; + +struct rtw_usb { + struct rtw_dev *rtwdev; + struct usb_device *udev; + + /* protects usb_data_index */ + spinlock_t usb_lock; + __le32 *usb_data; + unsigned int usb_data_index; + + u32 bulkout_size; + u8 pipe_interrupt; + u8 pipe_in; + u8 out_ep[RTW_USB_EP_MAX]; + u8 qsel_to_ep[TX_DESC_QSEL_MAX]; + u8 usb_txagg_num; + + struct workqueue_struct *txwq, *rxwq; + + struct sk_buff_head tx_queue[RTW_USB_EP_MAX]; + struct work_struct tx_work; + + struct rx_usb_ctrl_block rx_cb[RTW_USB_RXCB_NUM]; + struct sk_buff_head rx_queue; + struct work_struct rx_work; +}; + +static inline struct rtw_usb_tx_data *rtw_usb_get_tx_data(struct sk_buff *skb) +{ + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + + BUILD_BUG_ON(sizeof(struct rtw_usb_tx_data) > + sizeof(info->status.status_driver_data)); + + return (struct rtw_usb_tx_data *)info->status.status_driver_data; +} + +int rtw_usb_probe(struct usb_interface *intf, const struct usb_device_id *id); +void rtw_usb_disconnect(struct usb_interface *intf); + +#endif From aff5ffd718de23cb8603f2e229204670e2644334 Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Fri, 2 Dec 2022 09:12:21 +0100 Subject: [PATCH 1728/2002] wifi: rtw88: Add rtw8821cu chipset support Add support for the rtw8821cu chipset based on https://github.com/ulli-kroll/rtw88-usb.git Signed-off-by: Sascha Hauer Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20221202081224.2779981-9-s.hauer@pengutronix.de --- drivers/net/wireless/realtek/rtw88/Kconfig | 11 ++++ drivers/net/wireless/realtek/rtw88/Makefile | 3 ++ drivers/net/wireless/realtek/rtw88/rtw8821c.c | 18 +++++++ drivers/net/wireless/realtek/rtw88/rtw8821c.h | 21 ++++++++ .../net/wireless/realtek/rtw88/rtw8821cu.c | 50 +++++++++++++++++++ 5 files changed, 103 insertions(+) create mode 100644 drivers/net/wireless/realtek/rtw88/rtw8821cu.c diff --git a/drivers/net/wireless/realtek/rtw88/Kconfig b/drivers/net/wireless/realtek/rtw88/Kconfig index 1624c5db69ba..2b500dbefbc2 100644 --- a/drivers/net/wireless/realtek/rtw88/Kconfig +++ b/drivers/net/wireless/realtek/rtw88/Kconfig @@ -75,6 +75,17 @@ config RTW88_8821CE 802.11ac PCIe wireless network adapter +config RTW88_8821CU + tristate "Realtek 8821CU USB wireless network adapter" + depends on USB + select RTW88_CORE + select RTW88_USB + select RTW88_8821C + help + Select this option will enable support for 8821CU chipset + + 802.11ac USB wireless network adapter + config RTW88_DEBUG bool "Realtek rtw88 debug support" depends on RTW88_CORE diff --git a/drivers/net/wireless/realtek/rtw88/Makefile b/drivers/net/wireless/realtek/rtw88/Makefile index 2c2b0e5133cd..552661a638de 100644 --- a/drivers/net/wireless/realtek/rtw88/Makefile +++ b/drivers/net/wireless/realtek/rtw88/Makefile @@ -44,6 +44,9 @@ rtw88_8821c-objs := rtw8821c.o rtw8821c_table.o obj-$(CONFIG_RTW88_8821CE) += rtw88_8821ce.o rtw88_8821ce-objs := rtw8821ce.o +obj-$(CONFIG_RTW88_8821CU) += rtw88_8821cu.o +rtw88_8821cu-objs := rtw8821cu.o + obj-$(CONFIG_RTW88_PCI) += rtw88_pci.o rtw88_pci-objs := pci.o diff --git a/drivers/net/wireless/realtek/rtw88/rtw8821c.c b/drivers/net/wireless/realtek/rtw88/rtw8821c.c index 9afdc5ce86b4..17f800f6efbd 100644 --- a/drivers/net/wireless/realtek/rtw88/rtw8821c.c +++ b/drivers/net/wireless/realtek/rtw88/rtw8821c.c @@ -26,6 +26,12 @@ static void rtw8821ce_efuse_parsing(struct rtw_efuse *efuse, ether_addr_copy(efuse->addr, map->e.mac_addr); } +static void rtw8821cu_efuse_parsing(struct rtw_efuse *efuse, + struct rtw8821c_efuse *map) +{ + ether_addr_copy(efuse->addr, map->u.mac_addr); +} + enum rtw8821ce_rf_set { SWITCH_TO_BTG, SWITCH_TO_WLG, @@ -68,6 +74,9 @@ static int rtw8821c_read_efuse(struct rtw_dev *rtwdev, u8 *log_map) case RTW_HCI_TYPE_PCIE: rtw8821ce_efuse_parsing(efuse, map); break; + case RTW_HCI_TYPE_USB: + rtw8821cu_efuse_parsing(efuse, map); + break; default: /* unsupported now */ return -ENOTSUPP; @@ -1148,6 +1157,13 @@ static void rtw8821c_phy_cck_pd_set(struct rtw_dev *rtwdev, u8 new_lvl) dm_info->cck_pd_default + new_lvl * 2); } +static void rtw8821c_fill_txdesc_checksum(struct rtw_dev *rtwdev, + struct rtw_tx_pkt_info *pkt_info, + u8 *txdesc) +{ + fill_txdesc_checksum_common(txdesc, 16); +} + static struct rtw_pwr_seq_cmd trans_carddis_to_cardemu_8821c[] = { {0x0086, RTW_PWR_CUT_ALL_MSK, @@ -1521,6 +1537,7 @@ static const struct rtw_rfe_def rtw8821c_rfe_defs[] = { [2] = RTW_DEF_RFE_EXT(8821c, 0, 0, 2), [4] = RTW_DEF_RFE_EXT(8821c, 0, 0, 2), [6] = RTW_DEF_RFE(8821c, 0, 0), + [34] = RTW_DEF_RFE(8821c, 0, 0), }; static struct rtw_hw_reg rtw8821c_dig[] = { @@ -1595,6 +1612,7 @@ static struct rtw_chip_ops rtw8821c_ops = { .config_bfee = rtw8821c_bf_config_bfee, .set_gid_table = rtw_bf_set_gid_table, .cfg_csi_rate = rtw_bf_cfg_csi_rate, + .fill_txdesc_checksum = rtw8821c_fill_txdesc_checksum, .coex_set_init = rtw8821c_coex_cfg_init, .coex_set_ant_switch = rtw8821c_coex_cfg_ant_switch, diff --git a/drivers/net/wireless/realtek/rtw88/rtw8821c.h b/drivers/net/wireless/realtek/rtw88/rtw8821c.h index 2698801fc35d..1c81260f3a54 100644 --- a/drivers/net/wireless/realtek/rtw88/rtw8821c.h +++ b/drivers/net/wireless/realtek/rtw88/rtw8821c.h @@ -9,6 +9,26 @@ #define RCR_VHT_ACK BIT(26) +struct rtw8821cu_efuse { + u8 res4[4]; /* 0xd0 */ + u8 usb_optional_function; + u8 res5[0x1e]; + u8 res6[2]; + u8 serial[0x0b]; /* 0xf5 */ + u8 vid; /* 0x100 */ + u8 res7; + u8 pid; + u8 res8[4]; + u8 mac_addr[ETH_ALEN]; /* 0x107 */ + u8 res9[2]; + u8 vendor_name[0x07]; + u8 res10[2]; + u8 device_name[0x14]; + u8 res11[0xcf]; + u8 package_type; /* 0x1fb */ + u8 res12[0x4]; +}; + struct rtw8821ce_efuse { u8 mac_addr[ETH_ALEN]; /* 0xd0 */ u8 vender_id[2]; @@ -73,6 +93,7 @@ struct rtw8821c_efuse { u8 res[3]; union { struct rtw8821ce_efuse e; + struct rtw8821cu_efuse u; }; }; diff --git a/drivers/net/wireless/realtek/rtw88/rtw8821cu.c b/drivers/net/wireless/realtek/rtw88/rtw8821cu.c new file mode 100644 index 000000000000..7a5cbdc31ef7 --- /dev/null +++ b/drivers/net/wireless/realtek/rtw88/rtw8821cu.c @@ -0,0 +1,50 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* Copyright(c) 2018-2019 Realtek Corporation + */ + +#include +#include +#include "main.h" +#include "rtw8821c.h" +#include "usb.h" + +static const struct usb_device_id rtw_8821cu_id_table[] = { + { USB_DEVICE_AND_INTERFACE_INFO(RTW_USB_VENDOR_ID_REALTEK, 0xb82b, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&(rtw8821c_hw_spec) }, /* 8821CU */ + { USB_DEVICE_AND_INTERFACE_INFO(RTW_USB_VENDOR_ID_REALTEK, 0xb820, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&(rtw8821c_hw_spec) }, /* 8821CU */ + { USB_DEVICE_AND_INTERFACE_INFO(RTW_USB_VENDOR_ID_REALTEK, 0xc821, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&(rtw8821c_hw_spec) }, /* 8821CU */ + { USB_DEVICE_AND_INTERFACE_INFO(RTW_USB_VENDOR_ID_REALTEK, 0xc820, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&(rtw8821c_hw_spec) }, /* 8821CU */ + { USB_DEVICE_AND_INTERFACE_INFO(RTW_USB_VENDOR_ID_REALTEK, 0xc82a, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&(rtw8821c_hw_spec) }, /* 8821CU */ + { USB_DEVICE_AND_INTERFACE_INFO(RTW_USB_VENDOR_ID_REALTEK, 0xc82b, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&(rtw8821c_hw_spec) }, /* 8821CU */ + { USB_DEVICE_AND_INTERFACE_INFO(RTW_USB_VENDOR_ID_REALTEK, 0xc811, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&(rtw8821c_hw_spec) }, /* 8811CU */ + { USB_DEVICE_AND_INTERFACE_INFO(RTW_USB_VENDOR_ID_REALTEK, 0x8811, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&(rtw8821c_hw_spec) }, /* 8811CU */ + { USB_DEVICE_AND_INTERFACE_INFO(RTW_USB_VENDOR_ID_REALTEK, 0x2006, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&(rtw8821c_hw_spec) }, /* TOTOLINK A650UA v3 */ + {}, +}; +MODULE_DEVICE_TABLE(usb, rtw_8821cu_id_table); + +static int rtw_8821cu_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + return rtw_usb_probe(intf, id); +} + +static struct usb_driver rtw_8821cu_driver = { + .name = "rtw_8821cu", + .id_table = rtw_8821cu_id_table, + .probe = rtw_8821cu_probe, + .disconnect = rtw_usb_disconnect, +}; +module_usb_driver(rtw_8821cu_driver); + +MODULE_AUTHOR("Hans Ulli Kroll "); +MODULE_DESCRIPTION("Realtek 802.11ac wireless 8821cu driver"); +MODULE_LICENSE("Dual BSD/GPL"); From 45794099f5e1d7abc5eb07e6eec7e1e5c6cb540d Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Fri, 2 Dec 2022 09:12:22 +0100 Subject: [PATCH 1729/2002] wifi: rtw88: Add rtw8822bu chipset support Add support for the rtw8822bu chipset based on https://github.com/ulli-kroll/rtw88-usb.git Signed-off-by: Sascha Hauer Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20221202081224.2779981-10-s.hauer@pengutronix.de --- drivers/net/wireless/realtek/rtw88/Kconfig | 11 +++ drivers/net/wireless/realtek/rtw88/Makefile | 3 + drivers/net/wireless/realtek/rtw88/rtw8822b.c | 19 ++++ .../net/wireless/realtek/rtw88/rtw8822bu.c | 90 +++++++++++++++++++ 4 files changed, 123 insertions(+) create mode 100644 drivers/net/wireless/realtek/rtw88/rtw8822bu.c diff --git a/drivers/net/wireless/realtek/rtw88/Kconfig b/drivers/net/wireless/realtek/rtw88/Kconfig index 2b500dbefbc2..10f4e7f88b85 100644 --- a/drivers/net/wireless/realtek/rtw88/Kconfig +++ b/drivers/net/wireless/realtek/rtw88/Kconfig @@ -42,6 +42,17 @@ config RTW88_8822BE 802.11ac PCIe wireless network adapter +config RTW88_8822BU + tristate "Realtek 8822BU USB wireless network adapter" + depends on USB + select RTW88_CORE + select RTW88_USB + select RTW88_8822B + help + Select this option will enable support for 8822BU chipset + + 802.11ac USB wireless network adapter + config RTW88_8822CE tristate "Realtek 8822CE PCI wireless network adapter" depends on PCI diff --git a/drivers/net/wireless/realtek/rtw88/Makefile b/drivers/net/wireless/realtek/rtw88/Makefile index 552661a638de..6984bfb7a317 100644 --- a/drivers/net/wireless/realtek/rtw88/Makefile +++ b/drivers/net/wireless/realtek/rtw88/Makefile @@ -26,6 +26,9 @@ rtw88_8822b-objs := rtw8822b.o rtw8822b_table.o obj-$(CONFIG_RTW88_8822BE) += rtw88_8822be.o rtw88_8822be-objs := rtw8822be.o +obj-$(CONFIG_RTW88_8822BU) += rtw88_8822bu.o +rtw88_8822bu-objs := rtw8822bu.o + obj-$(CONFIG_RTW88_8822C) += rtw88_8822c.o rtw88_8822c-objs := rtw8822c.o rtw8822c_table.o diff --git a/drivers/net/wireless/realtek/rtw88/rtw8822b.c b/drivers/net/wireless/realtek/rtw88/rtw8822b.c index 690e35c98f6e..74dfb89b2c94 100644 --- a/drivers/net/wireless/realtek/rtw88/rtw8822b.c +++ b/drivers/net/wireless/realtek/rtw88/rtw8822b.c @@ -26,6 +26,12 @@ static void rtw8822be_efuse_parsing(struct rtw_efuse *efuse, ether_addr_copy(efuse->addr, map->e.mac_addr); } +static void rtw8822bu_efuse_parsing(struct rtw_efuse *efuse, + struct rtw8822b_efuse *map) +{ + ether_addr_copy(efuse->addr, map->u.mac_addr); +} + static int rtw8822b_read_efuse(struct rtw_dev *rtwdev, u8 *log_map) { struct rtw_efuse *efuse = &rtwdev->efuse; @@ -56,6 +62,9 @@ static int rtw8822b_read_efuse(struct rtw_dev *rtwdev, u8 *log_map) case RTW_HCI_TYPE_PCIE: rtw8822be_efuse_parsing(efuse, map); break; + case RTW_HCI_TYPE_USB: + rtw8822bu_efuse_parsing(efuse, map); + break; default: /* unsupported now */ return -ENOTSUPP; @@ -1588,6 +1597,15 @@ static void rtw8822b_adaptivity(struct rtw_dev *rtwdev) rtw_phy_set_edcca_th(rtwdev, l2h, h2l); } +static void rtw8822b_fill_txdesc_checksum(struct rtw_dev *rtwdev, + struct rtw_tx_pkt_info *pkt_info, + u8 *txdesc) +{ + size_t words = 32 / 2; /* calculate the first 32 bytes (16 words) */ + + fill_txdesc_checksum_common(txdesc, words); +} + static const struct rtw_pwr_seq_cmd trans_carddis_to_cardemu_8822b[] = { {0x0086, RTW_PWR_CUT_ALL_MSK, @@ -2163,6 +2181,7 @@ static struct rtw_chip_ops rtw8822b_ops = { .cfg_csi_rate = rtw_bf_cfg_csi_rate, .adaptivity_init = rtw8822b_adaptivity_init, .adaptivity = rtw8822b_adaptivity, + .fill_txdesc_checksum = rtw8822b_fill_txdesc_checksum, .coex_set_init = rtw8822b_coex_cfg_init, .coex_set_ant_switch = rtw8822b_coex_cfg_ant_switch, diff --git a/drivers/net/wireless/realtek/rtw88/rtw8822bu.c b/drivers/net/wireless/realtek/rtw88/rtw8822bu.c new file mode 100644 index 000000000000..ab620a0b1dfc --- /dev/null +++ b/drivers/net/wireless/realtek/rtw88/rtw8822bu.c @@ -0,0 +1,90 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* Copyright(c) 2018-2019 Realtek Corporation + */ + +#include +#include +#include "main.h" +#include "rtw8822b.h" +#include "usb.h" + +static const struct usb_device_id rtw_8822bu_id_table[] = { + { USB_DEVICE_AND_INTERFACE_INFO(RTW_USB_VENDOR_ID_REALTEK, 0xb812, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&(rtw8822b_hw_spec) }, + { USB_DEVICE_AND_INTERFACE_INFO(RTW_USB_VENDOR_ID_REALTEK, 0xb82c, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&(rtw8822b_hw_spec) }, + { USB_DEVICE_AND_INTERFACE_INFO(RTW_USB_VENDOR_ID_REALTEK, 0x2102, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&(rtw8822b_hw_spec) }, /* CCNC */ + { USB_DEVICE_AND_INTERFACE_INFO(0x7392, 0xb822, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&(rtw8822b_hw_spec) }, /* Edimax EW-7822ULC */ + { USB_DEVICE_AND_INTERFACE_INFO(0x7392, 0xc822, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&(rtw8822b_hw_spec) }, /* Edimax EW-7822UTC */ + { USB_DEVICE_AND_INTERFACE_INFO(0x7392, 0xd822, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&(rtw8822b_hw_spec) }, /* Edimax */ + { USB_DEVICE_AND_INTERFACE_INFO(0x7392, 0xe822, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&(rtw8822b_hw_spec) }, /* Edimax */ + { USB_DEVICE_AND_INTERFACE_INFO(0x7392, 0xf822, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&(rtw8822b_hw_spec) }, /* Edimax EW-7822UAD */ + { USB_DEVICE_AND_INTERFACE_INFO(RTW_USB_VENDOR_ID_REALTEK, 0xb81a, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&(rtw8822b_hw_spec) }, /* Default ID */ + { USB_DEVICE_AND_INTERFACE_INFO(0x0b05, 0x1841, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&(rtw8822b_hw_spec) }, /* ASUS AC1300 USB-AC55 B1 */ + { USB_DEVICE_AND_INTERFACE_INFO(0x0b05, 0x184c, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&(rtw8822b_hw_spec) }, /* ASUS U2 */ + { USB_DEVICE_AND_INTERFACE_INFO(0x0B05, 0x19aa, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&(rtw8822b_hw_spec) }, /* ASUS - USB-AC58 rev A1 */ + { USB_DEVICE_AND_INTERFACE_INFO(0x0B05, 0x1870, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&(rtw8822b_hw_spec) }, /* ASUS */ + { USB_DEVICE_AND_INTERFACE_INFO(0x0B05, 0x1874, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&(rtw8822b_hw_spec) }, /* ASUS */ + { USB_DEVICE_AND_INTERFACE_INFO(0x2001, 0x331e, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&(rtw8822b_hw_spec) }, /* Dlink - DWA-181 */ + { USB_DEVICE_AND_INTERFACE_INFO(0x2001, 0x331c, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&(rtw8822b_hw_spec) }, /* Dlink - DWA-182 - D1 */ + {USB_DEVICE_AND_INTERFACE_INFO(0x2001, 0x331f, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&(rtw8822b_hw_spec)}, /* Dlink - DWA-183 D Ver */ + { USB_DEVICE_AND_INTERFACE_INFO(0x13b1, 0x0043, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&(rtw8822b_hw_spec) }, /* Linksys WUSB6400M */ + { USB_DEVICE_AND_INTERFACE_INFO(0x13b1, 0x0045, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&(rtw8822b_hw_spec) }, /* Linksys WUSB3600 v2 */ + { USB_DEVICE_AND_INTERFACE_INFO(0x2357, 0x012d, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&(rtw8822b_hw_spec) }, /* TP-Link Archer T3U v1 */ + { USB_DEVICE_AND_INTERFACE_INFO(0x2357, 0x0138, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&(rtw8822b_hw_spec) }, /* TP-Link Archer T3U Plus v1 */ + { USB_DEVICE_AND_INTERFACE_INFO(0x2357, 0x0115, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&(rtw8822b_hw_spec) }, /* TP-Link Archer T4U V3 */ + { USB_DEVICE_AND_INTERFACE_INFO(0x2357, 0x012e, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&(rtw8822b_hw_spec) }, /* TP-LINK */ + { USB_DEVICE_AND_INTERFACE_INFO(0x2357, 0x0116, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&(rtw8822b_hw_spec) }, /* TP-LINK */ + { USB_DEVICE_AND_INTERFACE_INFO(0x2357, 0x0117, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&(rtw8822b_hw_spec) }, /* TP-LINK */ + { USB_DEVICE_AND_INTERFACE_INFO(0x0846, 0x9055, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&(rtw8822b_hw_spec) }, /* Netgear A6150 */ + { USB_DEVICE_AND_INTERFACE_INFO(0x0e66, 0x0025, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&(rtw8822b_hw_spec) }, /* Hawking HW12ACU */ + { USB_DEVICE_AND_INTERFACE_INFO(0x04ca, 0x8602, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&(rtw8822b_hw_spec) }, /* LiteOn */ + { USB_DEVICE_AND_INTERFACE_INFO(0x20f4, 0x808a, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&(rtw8822b_hw_spec) }, /* TRENDnet TEW-808UBM */ + {}, +}; +MODULE_DEVICE_TABLE(usb, rtw_8822bu_id_table); + +static int rtw8822bu_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + return rtw_usb_probe(intf, id); +} + +static struct usb_driver rtw_8822bu_driver = { + .name = "rtw_8822bu", + .id_table = rtw_8822bu_id_table, + .probe = rtw8822bu_probe, + .disconnect = rtw_usb_disconnect, +}; +module_usb_driver(rtw_8822bu_driver); + +MODULE_AUTHOR("Realtek Corporation"); +MODULE_DESCRIPTION("Realtek 802.11ac wireless 8822bu driver"); +MODULE_LICENSE("Dual BSD/GPL"); From 07cef03b8d44dee7488de3d1585387e603c78676 Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Fri, 2 Dec 2022 09:12:23 +0100 Subject: [PATCH 1730/2002] wifi: rtw88: Add rtw8822cu chipset support Add support for the rtw8822cu chipset based on https://github.com/ulli-kroll/rtw88-usb.git Signed-off-by: Sascha Hauer Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20221202081224.2779981-11-s.hauer@pengutronix.de --- drivers/net/wireless/realtek/rtw88/Kconfig | 11 +++++ drivers/net/wireless/realtek/rtw88/Makefile | 3 ++ drivers/net/wireless/realtek/rtw88/rtw8822c.c | 24 ++++++++++ .../net/wireless/realtek/rtw88/rtw8822cu.c | 44 +++++++++++++++++++ 4 files changed, 82 insertions(+) create mode 100644 drivers/net/wireless/realtek/rtw88/rtw8822cu.c diff --git a/drivers/net/wireless/realtek/rtw88/Kconfig b/drivers/net/wireless/realtek/rtw88/Kconfig index 10f4e7f88b85..138289bc5ad0 100644 --- a/drivers/net/wireless/realtek/rtw88/Kconfig +++ b/drivers/net/wireless/realtek/rtw88/Kconfig @@ -64,6 +64,17 @@ config RTW88_8822CE 802.11ac PCIe wireless network adapter +config RTW88_8822CU + tristate "Realtek 8822CU USB wireless network adapter" + depends on USB + select RTW88_CORE + select RTW88_USB + select RTW88_8822C + help + Select this option will enable support for 8822CU chipset + + 802.11ac USB wireless network adapter + config RTW88_8723DE tristate "Realtek 8723DE PCI wireless network adapter" depends on PCI diff --git a/drivers/net/wireless/realtek/rtw88/Makefile b/drivers/net/wireless/realtek/rtw88/Makefile index 6984bfb7a317..fe2dd90204a7 100644 --- a/drivers/net/wireless/realtek/rtw88/Makefile +++ b/drivers/net/wireless/realtek/rtw88/Makefile @@ -35,6 +35,9 @@ rtw88_8822c-objs := rtw8822c.o rtw8822c_table.o obj-$(CONFIG_RTW88_8822CE) += rtw88_8822ce.o rtw88_8822ce-objs := rtw8822ce.o +obj-$(CONFIG_RTW88_8822CU) += rtw88_8822cu.o +rtw88_8822cu-objs := rtw8822cu.o + obj-$(CONFIG_RTW88_8723D) += rtw88_8723d.o rtw88_8723d-objs := rtw8723d.o rtw8723d_table.o diff --git a/drivers/net/wireless/realtek/rtw88/rtw8822c.c b/drivers/net/wireless/realtek/rtw88/rtw8822c.c index fccb15dfb959..964e27887fe2 100644 --- a/drivers/net/wireless/realtek/rtw88/rtw8822c.c +++ b/drivers/net/wireless/realtek/rtw88/rtw8822c.c @@ -29,6 +29,12 @@ static void rtw8822ce_efuse_parsing(struct rtw_efuse *efuse, ether_addr_copy(efuse->addr, map->e.mac_addr); } +static void rtw8822cu_efuse_parsing(struct rtw_efuse *efuse, + struct rtw8822c_efuse *map) +{ + ether_addr_copy(efuse->addr, map->u.mac_addr); +} + static int rtw8822c_read_efuse(struct rtw_dev *rtwdev, u8 *log_map) { struct rtw_efuse *efuse = &rtwdev->efuse; @@ -58,6 +64,9 @@ static int rtw8822c_read_efuse(struct rtw_dev *rtwdev, u8 *log_map) case RTW_HCI_TYPE_PCIE: rtw8822ce_efuse_parsing(efuse, map); break; + case RTW_HCI_TYPE_USB: + rtw8822cu_efuse_parsing(efuse, map); + break; default: /* unsupported now */ return -ENOTSUPP; @@ -4557,6 +4566,18 @@ static void rtw8822c_adaptivity(struct rtw_dev *rtwdev) rtw_phy_set_edcca_th(rtwdev, l2h, h2l); } +static void rtw8822c_fill_txdesc_checksum(struct rtw_dev *rtwdev, + struct rtw_tx_pkt_info *pkt_info, + u8 *txdesc) +{ + const struct rtw_chip_info *chip = rtwdev->chip; + size_t words; + + words = (pkt_info->pkt_offset * 8 + chip->tx_pkt_desc_sz) / 2; + + fill_txdesc_checksum_common(txdesc, words); +} + static const struct rtw_pwr_seq_cmd trans_carddis_to_cardemu_8822c[] = { {0x0086, RTW_PWR_CUT_ALL_MSK, @@ -4895,6 +4916,8 @@ static const struct rtw_rfe_def rtw8822c_rfe_defs[] = { [0] = RTW_DEF_RFE(8822c, 0, 0), [1] = RTW_DEF_RFE(8822c, 0, 0), [2] = RTW_DEF_RFE(8822c, 0, 0), + [3] = RTW_DEF_RFE(8822c, 0, 0), + [4] = RTW_DEF_RFE(8822c, 0, 0), [5] = RTW_DEF_RFE(8822c, 0, 5), [6] = RTW_DEF_RFE(8822c, 0, 0), }; @@ -4978,6 +5001,7 @@ static struct rtw_chip_ops rtw8822c_ops = { .cfo_track = rtw8822c_cfo_track, .config_tx_path = rtw8822c_config_tx_path, .config_txrx_mode = rtw8822c_config_trx_mode, + .fill_txdesc_checksum = rtw8822c_fill_txdesc_checksum, .coex_set_init = rtw8822c_coex_cfg_init, .coex_set_ant_switch = NULL, diff --git a/drivers/net/wireless/realtek/rtw88/rtw8822cu.c b/drivers/net/wireless/realtek/rtw88/rtw8822cu.c new file mode 100644 index 000000000000..af28ca09d41f --- /dev/null +++ b/drivers/net/wireless/realtek/rtw88/rtw8822cu.c @@ -0,0 +1,44 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* Copyright(c) 2018-2019 Realtek Corporation + */ + +#include +#include +#include "main.h" +#include "rtw8822c.h" +#include "usb.h" + +static const struct usb_device_id rtw_8822cu_id_table[] = { + { USB_DEVICE_AND_INTERFACE_INFO(RTW_USB_VENDOR_ID_REALTEK, 0xc82c, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&(rtw8822c_hw_spec) }, + { USB_DEVICE_AND_INTERFACE_INFO(RTW_USB_VENDOR_ID_REALTEK, 0xc812, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&(rtw8822c_hw_spec) }, + { USB_DEVICE_AND_INTERFACE_INFO(RTW_USB_VENDOR_ID_REALTEK, 0xc82e, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&(rtw8822c_hw_spec) }, + { USB_DEVICE_AND_INTERFACE_INFO(RTW_USB_VENDOR_ID_REALTEK, 0xd820, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&(rtw8822c_hw_spec) }, + { USB_DEVICE_AND_INTERFACE_INFO(RTW_USB_VENDOR_ID_REALTEK, 0xd82b, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&(rtw8822c_hw_spec) }, + { USB_DEVICE_AND_INTERFACE_INFO(0x13b1, 0x0043, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&(rtw8822c_hw_spec) }, /* Alpha - Alpha */ + {}, +}; +MODULE_DEVICE_TABLE(usb, rtw_8822cu_id_table); + +static int rtw8822bu_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + return rtw_usb_probe(intf, id); +} + +static struct usb_driver rtw_8822cu_driver = { + .name = "rtw_8822cu", + .id_table = rtw_8822cu_id_table, + .probe = rtw8822bu_probe, + .disconnect = rtw_usb_disconnect, +}; +module_usb_driver(rtw_8822cu_driver); + +MODULE_AUTHOR("Realtek Corporation"); +MODULE_DESCRIPTION("Realtek 802.11ac wireless 8822cu driver"); +MODULE_LICENSE("Dual BSD/GPL"); From 87caeef032fc3921bc866ad7becb6ed51aa8b27b Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Fri, 2 Dec 2022 09:12:24 +0100 Subject: [PATCH 1731/2002] wifi: rtw88: Add rtw8723du chipset support Add support for the rtw8723du chipset based on https://github.com/ulli-kroll/rtw88-usb.git Signed-off-by: Sascha Hauer Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20221202081224.2779981-12-s.hauer@pengutronix.de --- drivers/net/wireless/realtek/rtw88/Kconfig | 11 ++++++ drivers/net/wireless/realtek/rtw88/Makefile | 3 ++ drivers/net/wireless/realtek/rtw88/rtw8723d.c | 28 +++++++++++++++ drivers/net/wireless/realtek/rtw88/rtw8723d.h | 13 ++++++- .../net/wireless/realtek/rtw88/rtw8723du.c | 36 +++++++++++++++++++ 5 files changed, 90 insertions(+), 1 deletion(-) create mode 100644 drivers/net/wireless/realtek/rtw88/rtw8723du.c diff --git a/drivers/net/wireless/realtek/rtw88/Kconfig b/drivers/net/wireless/realtek/rtw88/Kconfig index 138289bc5ad0..651ab56d9c6b 100644 --- a/drivers/net/wireless/realtek/rtw88/Kconfig +++ b/drivers/net/wireless/realtek/rtw88/Kconfig @@ -86,6 +86,17 @@ config RTW88_8723DE 802.11n PCIe wireless network adapter +config RTW88_8723DU + tristate "Realtek 8723DU USB wireless network adapter" + depends on USB + select RTW88_CORE + select RTW88_USB + select RTW88_8723D + help + Select this option will enable support for 8723DU chipset + + 802.11n USB wireless network adapter + config RTW88_8821CE tristate "Realtek 8821CE PCI wireless network adapter" depends on PCI diff --git a/drivers/net/wireless/realtek/rtw88/Makefile b/drivers/net/wireless/realtek/rtw88/Makefile index fe2dd90204a7..fe7293ee87b4 100644 --- a/drivers/net/wireless/realtek/rtw88/Makefile +++ b/drivers/net/wireless/realtek/rtw88/Makefile @@ -44,6 +44,9 @@ rtw88_8723d-objs := rtw8723d.o rtw8723d_table.o obj-$(CONFIG_RTW88_8723DE) += rtw88_8723de.o rtw88_8723de-objs := rtw8723de.o +obj-$(CONFIG_RTW88_8723DU) += rtw88_8723du.o +rtw88_8723du-objs := rtw8723du.o + obj-$(CONFIG_RTW88_8821C) += rtw88_8821c.o rtw88_8821c-objs := rtw8821c.o rtw8821c_table.o diff --git a/drivers/net/wireless/realtek/rtw88/rtw8723d.c b/drivers/net/wireless/realtek/rtw88/rtw8723d.c index 0a4f770fcbb7..2d2f768bae2e 100644 --- a/drivers/net/wireless/realtek/rtw88/rtw8723d.c +++ b/drivers/net/wireless/realtek/rtw88/rtw8723d.c @@ -210,6 +210,12 @@ static void rtw8723de_efuse_parsing(struct rtw_efuse *efuse, ether_addr_copy(efuse->addr, map->e.mac_addr); } +static void rtw8723du_efuse_parsing(struct rtw_efuse *efuse, + struct rtw8723d_efuse *map) +{ + ether_addr_copy(efuse->addr, map->u.mac_addr); +} + static int rtw8723d_read_efuse(struct rtw_dev *rtwdev, u8 *log_map) { struct rtw_efuse *efuse = &rtwdev->efuse; @@ -239,6 +245,9 @@ static int rtw8723d_read_efuse(struct rtw_dev *rtwdev, u8 *log_map) case RTW_HCI_TYPE_PCIE: rtw8723de_efuse_parsing(efuse, map); break; + case RTW_HCI_TYPE_USB: + rtw8723du_efuse_parsing(efuse, map); + break; default: /* unsupported now */ return -ENOTSUPP; @@ -1945,6 +1954,24 @@ static void rtw8723d_pwr_track(struct rtw_dev *rtwdev) dm_info->pwr_trk_triggered = false; } +static void rtw8723d_fill_txdesc_checksum(struct rtw_dev *rtwdev, + struct rtw_tx_pkt_info *pkt_info, + u8 *txdesc) +{ + size_t words = 32 / 2; /* calculate the first 32 bytes (16 words) */ + __le16 chksum = 0; + __le16 *data = (__le16 *)(txdesc); + + SET_TX_DESC_TXDESC_CHECKSUM(txdesc, 0x0000); + + while (words--) + chksum ^= *data++; + + chksum = ~chksum; + + SET_TX_DESC_TXDESC_CHECKSUM(txdesc, __le16_to_cpu(chksum)); +} + static struct rtw_chip_ops rtw8723d_ops = { .phy_set_param = rtw8723d_phy_set_param, .read_efuse = rtw8723d_read_efuse, @@ -1965,6 +1992,7 @@ static struct rtw_chip_ops rtw8723d_ops = { .config_bfee = NULL, .set_gid_table = NULL, .cfg_csi_rate = NULL, + .fill_txdesc_checksum = rtw8723d_fill_txdesc_checksum, .coex_set_init = rtw8723d_coex_cfg_init, .coex_set_ant_switch = NULL, diff --git a/drivers/net/wireless/realtek/rtw88/rtw8723d.h b/drivers/net/wireless/realtek/rtw88/rtw8723d.h index 4641f6e047b4..a356318a5c15 100644 --- a/drivers/net/wireless/realtek/rtw88/rtw8723d.h +++ b/drivers/net/wireless/realtek/rtw88/rtw8723d.h @@ -41,6 +41,14 @@ struct rtw8723de_efuse { u8 sub_device_id[2]; }; +struct rtw8723du_efuse { + u8 res4[48]; /* 0xd0 */ + u8 vender_id[2]; /* 0x100 */ + u8 product_id[2]; /* 0x102 */ + u8 usb_option; /* 0x104 */ + u8 mac_addr[ETH_ALEN]; /* 0x107 */ +}; + struct rtw8723d_efuse { __le16 rtl_id; u8 rsvd[2]; @@ -69,7 +77,10 @@ struct rtw8723d_efuse { u8 rfe_option; u8 country_code[2]; u8 res[3]; - struct rtw8723de_efuse e; + union { + struct rtw8723de_efuse e; + struct rtw8723du_efuse u; + }; }; extern const struct rtw_chip_info rtw8723d_hw_spec; diff --git a/drivers/net/wireless/realtek/rtw88/rtw8723du.c b/drivers/net/wireless/realtek/rtw88/rtw8723du.c new file mode 100644 index 000000000000..322a805da76b --- /dev/null +++ b/drivers/net/wireless/realtek/rtw88/rtw8723du.c @@ -0,0 +1,36 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* Copyright(c) 2018-2019 Realtek Corporation + */ + +#include +#include +#include "main.h" +#include "rtw8723d.h" +#include "usb.h" + +static const struct usb_device_id rtw_8723du_id_table[] = { + { USB_DEVICE_AND_INTERFACE_INFO(RTW_USB_VENDOR_ID_REALTEK, 0xd723, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&(rtw8723d_hw_spec) }, /* 8723DU 1*1 */ + { USB_DEVICE_AND_INTERFACE_INFO(0x7392, 0xd611, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&(rtw8723d_hw_spec) }, /* Edimax EW-7611ULB V2 */ + { }, +}; +MODULE_DEVICE_TABLE(usb, rtw_8723du_id_table); + +static int rtw8723du_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + return rtw_usb_probe(intf, id); +} + +static struct usb_driver rtw_8723du_driver = { + .name = "rtw_8723du", + .id_table = rtw_8723du_id_table, + .probe = rtw8723du_probe, + .disconnect = rtw_usb_disconnect, +}; +module_usb_driver(rtw_8723du_driver); + +MODULE_AUTHOR("Hans Ulli Kroll "); +MODULE_DESCRIPTION("Realtek 802.11n wireless 8723du driver"); +MODULE_LICENSE("Dual BSD/GPL"); From 49ebca0d9018ff3517b24972f6f2a9f3d0dc956f Mon Sep 17 00:00:00 2001 From: Peter Kosyh Date: Mon, 5 Dec 2022 11:53:42 +0300 Subject: [PATCH 1732/2002] wifi: rtlwifi: rtl8192se: remove redundant rtl_get_bbreg() call Extra rtl_get_bbreg() call looks like redundant reading. The read has already been done in the "else" branch. Compile test only. Found by Linux Verification Center (linuxtesting.org) with SVACE. Signed-off-by: Peter Kosyh Acked-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20221205085342.677329-1-pkosyh@yandex.ru --- drivers/net/wireless/realtek/rtlwifi/rtl8192se/phy.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192se/phy.c b/drivers/net/wireless/realtek/rtlwifi/rtl8192se/phy.c index aaa004d4d6d0..09591a0b5a81 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8192se/phy.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192se/phy.c @@ -115,9 +115,6 @@ static u32 _rtl92s_phy_rf_serial_read(struct ieee80211_hw *hw, retvalue = rtl_get_bbreg(hw, pphyreg->rf_rb, BLSSI_READBACK_DATA); - retvalue = rtl_get_bbreg(hw, pphyreg->rf_rb, - BLSSI_READBACK_DATA); - rtl_dbg(rtlpriv, COMP_RF, DBG_TRACE, "RFR-%d Addr[0x%x]=0x%x\n", rfpath, pphyreg->rf_rb, retvalue); From e48c45318d3d38460c585dce6379ef77d65a069e Mon Sep 17 00:00:00 2001 From: Peter Kosyh Date: Tue, 6 Dec 2022 13:49:19 +0300 Subject: [PATCH 1733/2002] wifi: rtlwifi: btcoexist: fix conditions branches that are never executed Commit 40ca18823515 ("rtlwifi: btcoex: 23b 1ant: fine tune for wifi not connected") introduced never executed branches. Compile test only. Found by Linux Verification Center (linuxtesting.org) with SVACE. Signed-off-by: Peter Kosyh Acked-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20221206104919.739746-1-pkosyh@yandex.ru --- .../net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b1ant.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b1ant.c b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b1ant.c index 70492929d7e4..039bbedb41c2 100644 --- a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b1ant.c +++ b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b1ant.c @@ -1903,7 +1903,7 @@ btc8723b1ant_action_wifi_not_conn_scan(struct btc_coexist *btcoexist) true, 32); halbtc8723b1ant_coex_table_with_type(btcoexist, NORMAL_EXEC, 4); - } else if (bt_link_info->a2dp_exist) { + } else if (bt_link_info->pan_exist) { halbtc8723b1ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 22); halbtc8723b1ant_coex_table_with_type(btcoexist, @@ -1964,8 +1964,7 @@ static void btc8723b1ant_action_wifi_conn_scan(struct btc_coexist *btcoexist) true, 32); halbtc8723b1ant_coex_table_with_type(btcoexist, NORMAL_EXEC, 4); - } else if (bt_link_info->a2dp_exist && - bt_link_info->pan_exist) { + } else if (bt_link_info->pan_exist) { halbtc8723b1ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 22); halbtc8723b1ant_coex_table_with_type(btcoexist, From 695c5d3a8055dcb97966496ade51968c9a75f5c6 Mon Sep 17 00:00:00 2001 From: Jun ASAKA Date: Wed, 7 Dec 2022 11:39:26 +0800 Subject: [PATCH 1734/2002] wifi: rtl8xxxu: fixing IQK failures for rtl8192eu Fixing "Path A RX IQK failed" and "Path B RX IQK failed" issues for rtl8192eu chips by replacing the arguments with the ones in the updated official driver as shown below. 1. https://github.com/Mange/rtl8192eu-linux-driver 2. vendor driver version: 5.6.4 Signed-off-by: Jun ASAKA Reviewed-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20221207033926.11777-1-JunASAKA@zzy040330.moe --- .../realtek/rtl8xxxu/rtl8xxxu_8192e.c | 73 +++++++++++++------ 1 file changed, 51 insertions(+), 22 deletions(-) diff --git a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8192e.c b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8192e.c index 6dc1e5858e77..a7d76693c02d 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8192e.c +++ b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8192e.c @@ -780,6 +780,12 @@ static int rtl8192eu_iqk_path_a(struct rtl8xxxu_priv *priv) */ rtl8xxxu_write32(priv, REG_FPGA0_IQK, 0x00000000); rtl8xxxu_write_rfreg(priv, RF_A, RF6052_REG_UNKNOWN_DF, 0x00180); + + rtl8xxxu_write_rfreg(priv, RF_A, RF6052_REG_WE_LUT, 0x800a0); + rtl8xxxu_write_rfreg(priv, RF_A, RF6052_REG_RCK_OS, 0x20000); + rtl8xxxu_write_rfreg(priv, RF_A, RF6052_REG_TXPA_G1, 0x0000f); + rtl8xxxu_write_rfreg(priv, RF_A, RF6052_REG_TXPA_G2, 0x07f77); + rtl8xxxu_write32(priv, REG_FPGA0_IQK, 0x80800000); /* Path A IQK setting */ @@ -825,11 +831,16 @@ static int rtl8192eu_rx_iqk_path_a(struct rtl8xxxu_priv *priv) rtl8xxxu_write_rfreg(priv, RF_A, RF6052_REG_WE_LUT, 0x800a0); rtl8xxxu_write_rfreg(priv, RF_A, RF6052_REG_RCK_OS, 0x30000); rtl8xxxu_write_rfreg(priv, RF_A, RF6052_REG_TXPA_G1, 0x0000f); - rtl8xxxu_write_rfreg(priv, RF_A, RF6052_REG_TXPA_G2, 0xf117b); + rtl8xxxu_write_rfreg(priv, RF_A, RF6052_REG_TXPA_G2, 0xf1173); + + rtl8xxxu_write_rfreg(priv, RF_B, RF6052_REG_WE_LUT, 0x800a0); + rtl8xxxu_write_rfreg(priv, RF_B, RF6052_REG_RCK_OS, 0x30000); + rtl8xxxu_write_rfreg(priv, RF_B, RF6052_REG_TXPA_G1, 0x0000f); + rtl8xxxu_write_rfreg(priv, RF_B, RF6052_REG_TXPA_G2, 0xf1173); /* PA/PAD control by 0x56, and set = 0x0 */ rtl8xxxu_write_rfreg(priv, RF_A, RF6052_REG_UNKNOWN_DF, 0x00980); - rtl8xxxu_write_rfreg(priv, RF_A, RF6052_REG_UNKNOWN_56, 0x51000); + rtl8xxxu_write_rfreg(priv, RF_A, RF6052_REG_UNKNOWN_56, 0x511e0); /* Enter IQK mode */ rtl8xxxu_write32(priv, REG_FPGA0_IQK, 0x80800000); @@ -844,14 +855,14 @@ static int rtl8192eu_rx_iqk_path_a(struct rtl8xxxu_priv *priv) rtl8xxxu_write32(priv, REG_TX_IQK_TONE_B, 0x38008c1c); rtl8xxxu_write32(priv, REG_RX_IQK_TONE_B, 0x38008c1c); - rtl8xxxu_write32(priv, REG_TX_IQK_PI_A, 0x82160c1f); - rtl8xxxu_write32(priv, REG_RX_IQK_PI_A, 0x68160c1f); + rtl8xxxu_write32(priv, REG_TX_IQK_PI_A, 0x8216031f); + rtl8xxxu_write32(priv, REG_RX_IQK_PI_A, 0x6816031f); /* LO calibration setting */ rtl8xxxu_write32(priv, REG_IQK_AGC_RSP, 0x0046a911); /* One shot, path A LOK & IQK */ - rtl8xxxu_write32(priv, REG_IQK_AGC_PTS, 0xfa000000); + rtl8xxxu_write32(priv, REG_IQK_AGC_PTS, 0xf9000000); rtl8xxxu_write32(priv, REG_IQK_AGC_PTS, 0xf8000000); mdelay(10); @@ -882,11 +893,16 @@ static int rtl8192eu_rx_iqk_path_a(struct rtl8xxxu_priv *priv) rtl8xxxu_write_rfreg(priv, RF_A, RF6052_REG_WE_LUT, 0x800a0); rtl8xxxu_write_rfreg(priv, RF_A, RF6052_REG_RCK_OS, 0x30000); rtl8xxxu_write_rfreg(priv, RF_A, RF6052_REG_TXPA_G1, 0x0000f); - rtl8xxxu_write_rfreg(priv, RF_A, RF6052_REG_TXPA_G2, 0xf7ffa); + rtl8xxxu_write_rfreg(priv, RF_A, RF6052_REG_TXPA_G2, 0xf7ff2); + + rtl8xxxu_write_rfreg(priv, RF_B, RF6052_REG_WE_LUT, 0x800a0); + rtl8xxxu_write_rfreg(priv, RF_B, RF6052_REG_RCK_OS, 0x30000); + rtl8xxxu_write_rfreg(priv, RF_B, RF6052_REG_TXPA_G1, 0x0000f); + rtl8xxxu_write_rfreg(priv, RF_B, RF6052_REG_TXPA_G2, 0xf7ff2); /* PA/PAD control by 0x56, and set = 0x0 */ rtl8xxxu_write_rfreg(priv, RF_A, RF6052_REG_UNKNOWN_DF, 0x00980); - rtl8xxxu_write_rfreg(priv, RF_A, RF6052_REG_UNKNOWN_56, 0x51000); + rtl8xxxu_write_rfreg(priv, RF_A, RF6052_REG_UNKNOWN_56, 0x510e0); /* Enter IQK mode */ rtl8xxxu_write32(priv, REG_FPGA0_IQK, 0x80800000); @@ -900,14 +916,14 @@ static int rtl8192eu_rx_iqk_path_a(struct rtl8xxxu_priv *priv) rtl8xxxu_write32(priv, REG_TX_IQK_TONE_B, 0x38008c1c); rtl8xxxu_write32(priv, REG_RX_IQK_TONE_B, 0x38008c1c); - rtl8xxxu_write32(priv, REG_TX_IQK_PI_A, 0x82160c1f); - rtl8xxxu_write32(priv, REG_RX_IQK_PI_A, 0x28160c1f); + rtl8xxxu_write32(priv, REG_TX_IQK_PI_A, 0x821608ff); + rtl8xxxu_write32(priv, REG_RX_IQK_PI_A, 0x281608ff); /* LO calibration setting */ rtl8xxxu_write32(priv, REG_IQK_AGC_RSP, 0x0046a891); /* One shot, path A LOK & IQK */ - rtl8xxxu_write32(priv, REG_IQK_AGC_PTS, 0xfa000000); + rtl8xxxu_write32(priv, REG_IQK_AGC_PTS, 0xf9000000); rtl8xxxu_write32(priv, REG_IQK_AGC_PTS, 0xf8000000); mdelay(10); @@ -937,9 +953,12 @@ static int rtl8192eu_iqk_path_b(struct rtl8xxxu_priv *priv) rtl8xxxu_write32(priv, REG_FPGA0_IQK, 0x00000000); rtl8xxxu_write_rfreg(priv, RF_B, RF6052_REG_UNKNOWN_DF, 0x00180); - rtl8xxxu_write32(priv, REG_FPGA0_IQK, 0x80800000); - rtl8xxxu_write32(priv, REG_FPGA0_IQK, 0x00000000); + rtl8xxxu_write_rfreg(priv, RF_B, RF6052_REG_WE_LUT, 0x800a0); + rtl8xxxu_write_rfreg(priv, RF_B, RF6052_REG_RCK_OS, 0x20000); + rtl8xxxu_write_rfreg(priv, RF_B, RF6052_REG_TXPA_G1, 0x0000f); + rtl8xxxu_write_rfreg(priv, RF_B, RF6052_REG_TXPA_G2, 0x07f77); + rtl8xxxu_write32(priv, REG_FPGA0_IQK, 0x80800000); /* Path B IQK setting */ @@ -948,11 +967,11 @@ static int rtl8192eu_iqk_path_b(struct rtl8xxxu_priv *priv) rtl8xxxu_write32(priv, REG_TX_IQK_TONE_B, 0x18008c1c); rtl8xxxu_write32(priv, REG_RX_IQK_TONE_B, 0x38008c1c); - rtl8xxxu_write32(priv, REG_TX_IQK_PI_B, 0x821403e2); + rtl8xxxu_write32(priv, REG_TX_IQK_PI_B, 0x82140303); rtl8xxxu_write32(priv, REG_RX_IQK_PI_B, 0x68160000); /* LO calibration setting */ - rtl8xxxu_write32(priv, REG_IQK_AGC_RSP, 0x00492911); + rtl8xxxu_write32(priv, REG_IQK_AGC_RSP, 0x00462911); /* One shot, path A LOK & IQK */ rtl8xxxu_write32(priv, REG_IQK_AGC_PTS, 0xfa000000); @@ -988,11 +1007,16 @@ static int rtl8192eu_rx_iqk_path_b(struct rtl8xxxu_priv *priv) rtl8xxxu_write_rfreg(priv, RF_B, RF6052_REG_WE_LUT, 0x800a0); rtl8xxxu_write_rfreg(priv, RF_B, RF6052_REG_RCK_OS, 0x30000); rtl8xxxu_write_rfreg(priv, RF_B, RF6052_REG_TXPA_G1, 0x0000f); - rtl8xxxu_write_rfreg(priv, RF_B, RF6052_REG_TXPA_G2, 0xf117b); + rtl8xxxu_write_rfreg(priv, RF_B, RF6052_REG_TXPA_G2, 0xf1173); + + rtl8xxxu_write_rfreg(priv, RF_A, RF6052_REG_WE_LUT, 0x800a0); + rtl8xxxu_write_rfreg(priv, RF_A, RF6052_REG_RCK_OS, 0x30000); + rtl8xxxu_write_rfreg(priv, RF_A, RF6052_REG_TXPA_G1, 0x0000f); + rtl8xxxu_write_rfreg(priv, RF_A, RF6052_REG_TXPA_G2, 0xf1173); /* PA/PAD control by 0x56, and set = 0x0 */ rtl8xxxu_write_rfreg(priv, RF_B, RF6052_REG_UNKNOWN_DF, 0x00980); - rtl8xxxu_write_rfreg(priv, RF_B, RF6052_REG_UNKNOWN_56, 0x51000); + rtl8xxxu_write_rfreg(priv, RF_B, RF6052_REG_UNKNOWN_56, 0x511e0); /* Enter IQK mode */ rtl8xxxu_write32(priv, REG_FPGA0_IQK, 0x80800000); @@ -1007,8 +1031,8 @@ static int rtl8192eu_rx_iqk_path_b(struct rtl8xxxu_priv *priv) rtl8xxxu_write32(priv, REG_TX_IQK_TONE_B, 0x18008c1c); rtl8xxxu_write32(priv, REG_RX_IQK_TONE_B, 0x38008c1c); - rtl8xxxu_write32(priv, REG_TX_IQK_PI_B, 0x82160c1f); - rtl8xxxu_write32(priv, REG_RX_IQK_PI_B, 0x68160c1f); + rtl8xxxu_write32(priv, REG_TX_IQK_PI_B, 0x8216031f); + rtl8xxxu_write32(priv, REG_RX_IQK_PI_B, 0x6816031f); /* LO calibration setting */ rtl8xxxu_write32(priv, REG_IQK_AGC_RSP, 0x0046a911); @@ -1048,11 +1072,16 @@ static int rtl8192eu_rx_iqk_path_b(struct rtl8xxxu_priv *priv) rtl8xxxu_write_rfreg(priv, RF_B, RF6052_REG_WE_LUT, 0x800a0); rtl8xxxu_write_rfreg(priv, RF_B, RF6052_REG_RCK_OS, 0x30000); rtl8xxxu_write_rfreg(priv, RF_B, RF6052_REG_TXPA_G1, 0x0000f); - rtl8xxxu_write_rfreg(priv, RF_B, RF6052_REG_TXPA_G2, 0xf7ffa); + rtl8xxxu_write_rfreg(priv, RF_B, RF6052_REG_TXPA_G2, 0xf7ff2); + + rtl8xxxu_write_rfreg(priv, RF_A, RF6052_REG_WE_LUT, 0x800a0); + rtl8xxxu_write_rfreg(priv, RF_A, RF6052_REG_RCK_OS, 0x30000); + rtl8xxxu_write_rfreg(priv, RF_A, RF6052_REG_TXPA_G1, 0x0000f); + rtl8xxxu_write_rfreg(priv, RF_A, RF6052_REG_TXPA_G2, 0xf7ff2); /* PA/PAD control by 0x56, and set = 0x0 */ rtl8xxxu_write_rfreg(priv, RF_B, RF6052_REG_UNKNOWN_DF, 0x00980); - rtl8xxxu_write_rfreg(priv, RF_B, RF6052_REG_UNKNOWN_56, 0x51000); + rtl8xxxu_write_rfreg(priv, RF_B, RF6052_REG_UNKNOWN_56, 0x510e0); /* Enter IQK mode */ rtl8xxxu_write32(priv, REG_FPGA0_IQK, 0x80800000); @@ -1066,8 +1095,8 @@ static int rtl8192eu_rx_iqk_path_b(struct rtl8xxxu_priv *priv) rtl8xxxu_write32(priv, REG_TX_IQK_TONE_B, 0x38008c1c); rtl8xxxu_write32(priv, REG_RX_IQK_TONE_B, 0x18008c1c); - rtl8xxxu_write32(priv, REG_TX_IQK_PI_A, 0x82160c1f); - rtl8xxxu_write32(priv, REG_RX_IQK_PI_A, 0x28160c1f); + rtl8xxxu_write32(priv, REG_TX_IQK_PI_A, 0x821608ff); + rtl8xxxu_write32(priv, REG_RX_IQK_PI_A, 0x281608ff); /* LO calibration setting */ rtl8xxxu_write32(priv, REG_IQK_AGC_RSP, 0x0046a891); From bde55dd9ccda7a3f5f735d89e855691362745248 Mon Sep 17 00:00:00 2001 From: Oleksij Rempel Date: Mon, 5 Dec 2022 06:29:04 +0100 Subject: [PATCH 1735/2002] net: dsa: microchip: add stats64 support for ksz8 series of switches Add stats64 support for ksz8xxx series of switches. Signed-off-by: Oleksij Rempel Link: https://lore.kernel.org/r/20221205052904.2834962-1-o.rempel@pengutronix.de Signed-off-by: Jakub Kicinski --- drivers/net/dsa/microchip/ksz_common.c | 87 ++++++++++++++++++++++++++ drivers/net/dsa/microchip/ksz_common.h | 1 + 2 files changed, 88 insertions(+) diff --git a/drivers/net/dsa/microchip/ksz_common.c b/drivers/net/dsa/microchip/ksz_common.c index f39b041765fb..423f944cc34c 100644 --- a/drivers/net/dsa/microchip/ksz_common.c +++ b/drivers/net/dsa/microchip/ksz_common.c @@ -70,6 +70,43 @@ struct ksz_stats_raw { u64 tx_discards; }; +struct ksz88xx_stats_raw { + u64 rx; + u64 rx_hi; + u64 rx_undersize; + u64 rx_fragments; + u64 rx_oversize; + u64 rx_jabbers; + u64 rx_symbol_err; + u64 rx_crc_err; + u64 rx_align_err; + u64 rx_mac_ctrl; + u64 rx_pause; + u64 rx_bcast; + u64 rx_mcast; + u64 rx_ucast; + u64 rx_64_or_less; + u64 rx_65_127; + u64 rx_128_255; + u64 rx_256_511; + u64 rx_512_1023; + u64 rx_1024_1522; + u64 tx; + u64 tx_hi; + u64 tx_late_col; + u64 tx_pause; + u64 tx_bcast; + u64 tx_mcast; + u64 tx_ucast; + u64 tx_deferred; + u64 tx_total_col; + u64 tx_exc_col; + u64 tx_single_col; + u64 tx_mult_col; + u64 rx_discards; + u64 tx_discards; +}; + static const struct ksz_mib_names ksz88xx_mib_names[] = { { 0x00, "rx" }, { 0x01, "rx_hi" }, @@ -156,6 +193,7 @@ static const struct ksz_dev_ops ksz8_dev_ops = { .w_phy = ksz8_w_phy, .r_mib_cnt = ksz8_r_mib_cnt, .r_mib_pkt = ksz8_r_mib_pkt, + .r_mib_stat64 = ksz88xx_r_mib_stats64, .freeze_mib = ksz8_freeze_mib, .port_init_cnt = ksz8_port_init_cnt, .fdb_dump = ksz8_fdb_dump, @@ -1583,6 +1621,55 @@ void ksz_r_mib_stats64(struct ksz_device *dev, int port) spin_unlock(&mib->stats64_lock); } +void ksz88xx_r_mib_stats64(struct ksz_device *dev, int port) +{ + struct ethtool_pause_stats *pstats; + struct rtnl_link_stats64 *stats; + struct ksz88xx_stats_raw *raw; + struct ksz_port_mib *mib; + + mib = &dev->ports[port].mib; + stats = &mib->stats64; + pstats = &mib->pause_stats; + raw = (struct ksz88xx_stats_raw *)mib->counters; + + spin_lock(&mib->stats64_lock); + + stats->rx_packets = raw->rx_bcast + raw->rx_mcast + raw->rx_ucast + + raw->rx_pause; + stats->tx_packets = raw->tx_bcast + raw->tx_mcast + raw->tx_ucast + + raw->tx_pause; + + /* HW counters are counting bytes + FCS which is not acceptable + * for rtnl_link_stats64 interface + */ + stats->rx_bytes = raw->rx + raw->rx_hi - stats->rx_packets * ETH_FCS_LEN; + stats->tx_bytes = raw->tx + raw->tx_hi - stats->tx_packets * ETH_FCS_LEN; + + stats->rx_length_errors = raw->rx_undersize + raw->rx_fragments + + raw->rx_oversize; + + stats->rx_crc_errors = raw->rx_crc_err; + stats->rx_frame_errors = raw->rx_align_err; + stats->rx_dropped = raw->rx_discards; + stats->rx_errors = stats->rx_length_errors + stats->rx_crc_errors + + stats->rx_frame_errors + stats->rx_dropped; + + stats->tx_window_errors = raw->tx_late_col; + stats->tx_fifo_errors = raw->tx_discards; + stats->tx_aborted_errors = raw->tx_exc_col; + stats->tx_errors = stats->tx_window_errors + stats->tx_fifo_errors + + stats->tx_aborted_errors; + + stats->multicast = raw->rx_mcast; + stats->collisions = raw->tx_total_col; + + pstats->tx_pause_frames = raw->tx_pause; + pstats->rx_pause_frames = raw->rx_pause; + + spin_unlock(&mib->stats64_lock); +} + static void ksz_get_stats64(struct dsa_switch *ds, int port, struct rtnl_link_stats64 *s) { diff --git a/drivers/net/dsa/microchip/ksz_common.h b/drivers/net/dsa/microchip/ksz_common.h index cb27f5a180c7..055d61ff3fb8 100644 --- a/drivers/net/dsa/microchip/ksz_common.h +++ b/drivers/net/dsa/microchip/ksz_common.h @@ -345,6 +345,7 @@ void ksz_switch_remove(struct ksz_device *dev); void ksz_init_mib_timer(struct ksz_device *dev); void ksz_r_mib_stats64(struct ksz_device *dev, int port); +void ksz88xx_r_mib_stats64(struct ksz_device *dev, int port); void ksz_port_stp_state_set(struct dsa_switch *ds, int port, u8 state); bool ksz_get_gbit(struct ksz_device *dev, int port); phy_interface_t ksz_get_xmii(struct ksz_device *dev, int port, bool gbit); From d35af0a7feb077c43ff0233bba5a8c6e75b73e35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20T=C3=B6pel?= Date: Wed, 7 Dec 2022 11:35:40 +0100 Subject: [PATCH 1736/2002] bpf: Do not zero-extend kfunc return values MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In BPF all global functions, and BPF helpers return a 64-bit value. For kfunc calls, this is not the case, and they can return e.g. 32-bit values. The return register R0 for kfuncs calls can therefore be marked as subreg_def != DEF_NOT_SUBREG. In general, if a register is marked with subreg_def != DEF_NOT_SUBREG, some archs (where bpf_jit_needs_zext() returns true) require the verifier to insert explicit zero-extension instructions. For kfuncs calls, however, the caller should do sign/zero extension for return values. In other words, the compiler is responsible to insert proper instructions, not the verifier. An example, provided by Yonghong Song: $ cat t.c extern unsigned foo(void); unsigned bar1(void) { return foo(); } unsigned bar2(void) { if (foo()) return 10; else return 20; } $ clang -target bpf -mcpu=v3 -O2 -c t.c && llvm-objdump -d t.o t.o: file format elf64-bpf Disassembly of section .text: 0000000000000000 : 0: 85 10 00 00 ff ff ff ff call -0x1 1: 95 00 00 00 00 00 00 00 exit 0000000000000010 : 2: 85 10 00 00 ff ff ff ff call -0x1 3: bc 01 00 00 00 00 00 00 w1 = w0 4: b4 00 00 00 14 00 00 00 w0 = 0x14 5: 16 01 01 00 00 00 00 00 if w1 == 0x0 goto +0x1 6: b4 00 00 00 0a 00 00 00 w0 = 0xa 0000000000000038 : 7: 95 00 00 00 00 00 00 00 exit If the return value of 'foo()' is used in the BPF program, the proper zero-extension will be done. Currently, the verifier correctly marks, say, a 32-bit return value as subreg_def != DEF_NOT_SUBREG, but will fail performing the actual zero-extension, due to a verifier bug in opt_subreg_zext_lo32_rnd_hi32(). load_reg is not properly set to R0, and the following path will be taken: if (WARN_ON(load_reg == -1)) { verbose(env, "verifier bug. zext_dst is set, but no reg is defined\n"); return -EFAULT; } A longer discussion from v1 can be found in the link below. Correct the verifier by avoiding doing explicit zero-extension of R0 for kfunc calls. Note that R0 will still be marked as a sub-register for return values smaller than 64-bit. Fixes: 83a2881903f3 ("bpf: Account for BPF_FETCH in insn_has_def32()") Link: https://lore.kernel.org/bpf/20221202103620.1915679-1-bjorn@kernel.org/ Suggested-by: Yonghong Song Signed-off-by: Björn Töpel Acked-by: Yonghong Song Link: https://lore.kernel.org/r/20221207103540.396496-1-bjorn@kernel.org Signed-off-by: Alexei Starovoitov --- kernel/bpf/verifier.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 29e09ef15188..1085dd185c95 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -14836,6 +14836,10 @@ static int opt_subreg_zext_lo32_rnd_hi32(struct bpf_verifier_env *env, if (!bpf_jit_needs_zext() && !is_cmpxchg_insn(&insn)) continue; + /* Zero-extension is done by the caller. */ + if (bpf_pseudo_kfunc_call(&insn)) + continue; + if (WARN_ON(load_reg == -1)) { verbose(env, "verifier bug. zext_dst is set, but no reg is defined\n"); return -EFAULT; From c2cc0ce72a5ed3e01705e14221d97e96ed7a37b8 Mon Sep 17 00:00:00 2001 From: Yang Jihong Date: Thu, 8 Dec 2022 09:37:24 +0800 Subject: [PATCH 1737/2002] bpf: Fix comment error in fixup_kfunc_call function insn->imm for kfunc is the relative address of __bpf_call_base, instead of __bpf_base_call, Fix the comment error. Signed-off-by: Yang Jihong Link: https://lore.kernel.org/r/20221208013724.257848-1-yangjihong1@huawei.com Signed-off-by: Alexei Starovoitov --- kernel/bpf/verifier.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 1085dd185c95..3194e9d9e4e4 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -15350,7 +15350,7 @@ static int fixup_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn, } /* insn->imm has the btf func_id. Replace it with - * an address (relative to __bpf_base_call). + * an address (relative to __bpf_call_base). */ desc = find_kfunc_desc(env->prog, insn->imm, insn->off); if (!desc) { From 6b5cbc8c4ec71e49dc8d2c393b105e8007a02b41 Mon Sep 17 00:00:00 2001 From: Sergey Temerkhanov Date: Mon, 5 Dec 2022 11:52:39 -0800 Subject: [PATCH 1738/2002] ice: Use more generic names for ice_ptp_tx fields Some supported devices have per-port timestamp memory blocks while others have shared ones within quads. Rename the struct ice_ptp_tx fields to reflect the block entities it works with Signed-off-by: Sergey Temerkhanov Tested-by: Gurucharan G (A Contingent worker at Intel) Signed-off-by: Tony Nguyen --- drivers/net/ethernet/intel/ice/ice_ptp.c | 26 ++++++++++++------------ drivers/net/ethernet/intel/ice/ice_ptp.h | 11 +++++----- 2 files changed, 19 insertions(+), 18 deletions(-) diff --git a/drivers/net/ethernet/intel/ice/ice_ptp.c b/drivers/net/ethernet/intel/ice/ice_ptp.c index 13e75279e71c..f41f4674cadd 100644 --- a/drivers/net/ethernet/intel/ice/ice_ptp.c +++ b/drivers/net/ethernet/intel/ice/ice_ptp.c @@ -651,14 +651,14 @@ static bool ice_ptp_tx_tstamp(struct ice_ptp_tx *tx) for_each_set_bit(idx, tx->in_use, tx->len) { struct skb_shared_hwtstamps shhwtstamps = {}; - u8 phy_idx = idx + tx->quad_offset; + u8 phy_idx = idx + tx->offset; u64 raw_tstamp, tstamp; struct sk_buff *skb; int err; ice_trace(tx_tstamp_fw_req, tx->tstamps[idx].skb, idx); - err = ice_read_phy_tstamp(&pf->hw, tx->quad, phy_idx, + err = ice_read_phy_tstamp(&pf->hw, tx->block, phy_idx, &raw_tstamp); if (err) continue; @@ -713,7 +713,7 @@ static bool ice_ptp_tx_tstamp(struct ice_ptp_tx *tx) * @tx: Tx tracking structure to initialize * * Assumes that the length has already been initialized. Do not call directly, - * use the ice_ptp_init_tx_e822 or ice_ptp_init_tx_e810 instead. + * use the ice_ptp_init_tx_* instead. */ static int ice_ptp_alloc_tx_tracker(struct ice_ptp_tx *tx) @@ -747,7 +747,7 @@ ice_ptp_flush_tx_tracker(struct ice_pf *pf, struct ice_ptp_tx *tx) u8 idx; for (idx = 0; idx < tx->len; idx++) { - u8 phy_idx = idx + tx->quad_offset; + u8 phy_idx = idx + tx->offset; spin_lock(&tx->lock); if (tx->tstamps[idx].skb) { @@ -760,7 +760,7 @@ ice_ptp_flush_tx_tracker(struct ice_pf *pf, struct ice_ptp_tx *tx) /* Clear any potential residual timestamp in the PHY block */ if (!pf->hw.reset_ongoing) - ice_clear_phy_tstamp(&pf->hw, tx->quad, phy_idx); + ice_clear_phy_tstamp(&pf->hw, tx->block, phy_idx); } } @@ -801,9 +801,9 @@ ice_ptp_release_tx_tracker(struct ice_pf *pf, struct ice_ptp_tx *tx) static int ice_ptp_init_tx_e822(struct ice_pf *pf, struct ice_ptp_tx *tx, u8 port) { - tx->quad = port / ICE_PORTS_PER_QUAD; - tx->quad_offset = (port % ICE_PORTS_PER_QUAD) * INDEX_PER_PORT; - tx->len = INDEX_PER_PORT; + tx->block = port / ICE_PORTS_PER_QUAD; + tx->offset = (port % ICE_PORTS_PER_QUAD) * INDEX_PER_PORT_E822; + tx->len = INDEX_PER_PORT_E822; return ice_ptp_alloc_tx_tracker(tx); } @@ -819,9 +819,9 @@ ice_ptp_init_tx_e822(struct ice_pf *pf, struct ice_ptp_tx *tx, u8 port) static int ice_ptp_init_tx_e810(struct ice_pf *pf, struct ice_ptp_tx *tx) { - tx->quad = pf->hw.port_info->lport; - tx->quad_offset = 0; - tx->len = INDEX_PER_QUAD; + tx->block = pf->hw.port_info->lport; + tx->offset = 0; + tx->len = INDEX_PER_PORT_E810; return ice_ptp_alloc_tx_tracker(tx); } @@ -854,7 +854,7 @@ static void ice_ptp_tx_tstamp_cleanup(struct ice_pf *pf, struct ice_ptp_tx *tx) continue; /* Read tstamp to be able to use this register again */ - ice_read_phy_tstamp(hw, tx->quad, idx + tx->quad_offset, + ice_read_phy_tstamp(hw, tx->block, idx + tx->offset, &raw_tstamp); spin_lock(&tx->lock); @@ -2359,7 +2359,7 @@ s8 ice_ptp_request_ts(struct ice_ptp_tx *tx, struct sk_buff *skb) if (idx >= tx->len) return -1; else - return idx + tx->quad_offset; + return idx + tx->offset; } /** diff --git a/drivers/net/ethernet/intel/ice/ice_ptp.h b/drivers/net/ethernet/intel/ice/ice_ptp.h index 028349295b71..ca0fbfd71ed2 100644 --- a/drivers/net/ethernet/intel/ice/ice_ptp.h +++ b/drivers/net/ethernet/intel/ice/ice_ptp.h @@ -108,8 +108,8 @@ struct ice_tx_tstamp { * @lock: lock to prevent concurrent write to in_use bitmap * @tstamps: array of len to store outstanding requests * @in_use: bitmap of len to indicate which slots are in use - * @quad: which quad the timestamps are captured in - * @quad_offset: offset into timestamp block of the quad to get the real index + * @block: which memory block (quad or port) the timestamps are captured in + * @offset: offset into timestamp block to get the real index * @len: length of the tstamps and in_use fields. * @init: if true, the tracker is initialized; * @calibrating: if true, the PHY is calibrating the Tx offset. During this @@ -119,8 +119,8 @@ struct ice_ptp_tx { spinlock_t lock; /* lock protecting in_use bitmap */ struct ice_tx_tstamp *tstamps; unsigned long *in_use; - u8 quad; - u8 quad_offset; + u8 block; + u8 offset; u8 len; u8 init; u8 calibrating; @@ -128,7 +128,8 @@ struct ice_ptp_tx { /* Quad and port information for initializing timestamp blocks */ #define INDEX_PER_QUAD 64 -#define INDEX_PER_PORT (INDEX_PER_QUAD / ICE_PORTS_PER_QUAD) +#define INDEX_PER_PORT_E822 16 +#define INDEX_PER_PORT_E810 64 /** * struct ice_ptp_port - data used to initialize an external port for PTP From 0357d5cab8e497517d19b745d386573b97e2bc4b Mon Sep 17 00:00:00 2001 From: Milena Olech Date: Mon, 5 Dec 2022 11:52:40 -0800 Subject: [PATCH 1739/2002] ice: Remove the E822 vernier "bypass" logic The E822 devices support an extended "vernier" calibration which enables higher precision timestamps by accounting for delays in the PHY, and compensating for them. These delays are measured by hardware as part of its vernier calibration logic. The driver currently starts the PHY in "bypass" mode which skips the compensation. Then it later attempts to switch from bypass to vernier. This unfortunately does not work as expected. Instead of properly compensating for the delays, the hardware continues operating in bypass without the improved precision expected. Because we cannot dynamically switch between bypass and vernier mode, refactor the driver to always operate in vernier mode. This has a slight downside: Tx timestamp and Rx timestamp requests that occur as the very first packet set after link up will not complete properly and may be reported to applications as missing timestamps. This occurs frequently in test environments where traffic is light or targeted specifically at testing PTP. However, in practice most environments will have transmitted or received some data over the network before such initial requests are made. Signed-off-by: Milena Olech Signed-off-by: Jacob Keller Tested-by: Gurucharan G (A Contingent worker at Intel) Signed-off-by: Tony Nguyen --- drivers/net/ethernet/intel/ice/ice_ptp.c | 10 +- drivers/net/ethernet/intel/ice/ice_ptp_hw.c | 145 +------------------- drivers/net/ethernet/intel/ice/ice_ptp_hw.h | 4 +- 3 files changed, 14 insertions(+), 145 deletions(-) diff --git a/drivers/net/ethernet/intel/ice/ice_ptp.c b/drivers/net/ethernet/intel/ice/ice_ptp.c index f41f4674cadd..9539d2d37c5b 100644 --- a/drivers/net/ethernet/intel/ice/ice_ptp.c +++ b/drivers/net/ethernet/intel/ice/ice_ptp.c @@ -1252,10 +1252,10 @@ static void ice_ptp_wait_for_offset_valid(struct kthread_work *work) return; } - /* Offsets are valid, so it is safe to exit bypass mode */ - err = ice_phy_exit_bypass_e822(hw, port->port_num); + /* Offsets are valid, so Vernier mode calculations are started */ + err = ice_phy_calc_vernier_e822(hw, port->port_num); if (err) { - dev_warn(dev, "Failed to exit bypass mode for PHY port %u, err %d\n", + dev_warn(dev, "Failed to prepare Vernier mode for PHY port %u, err %d\n", port->port_num, err); return; } @@ -1320,8 +1320,8 @@ ice_ptp_port_phy_restart(struct ice_ptp_port *ptp_port) ptp_port->tx.calibrating = true; ptp_port->tx_fifo_busy_cnt = 0; - /* Start the PHY timer in bypass mode */ - err = ice_start_phy_timer_e822(hw, port, true); + /* Start the PHY timer in Vernier mode */ + err = ice_start_phy_timer_e822(hw, port); if (err) goto out_unlock; diff --git a/drivers/net/ethernet/intel/ice/ice_ptp_hw.c b/drivers/net/ethernet/intel/ice/ice_ptp_hw.c index 1f8dd50db524..242c4db65171 100644 --- a/drivers/net/ethernet/intel/ice/ice_ptp_hw.c +++ b/drivers/net/ethernet/intel/ice/ice_ptp_hw.c @@ -1786,47 +1786,6 @@ static int ice_phy_cfg_tx_offset_e822(struct ice_hw *hw, u8 port) return 0; } -/** - * ice_phy_cfg_fixed_tx_offset_e822 - Configure Tx offset for bypass mode - * @hw: pointer to the HW struct - * @port: the PHY port to configure - * - * Calculate and program the fixed Tx offset, and indicate that the offset is - * ready. This can be used when operating in bypass mode. - */ -static int -ice_phy_cfg_fixed_tx_offset_e822(struct ice_hw *hw, u8 port) -{ - enum ice_ptp_link_spd link_spd; - enum ice_ptp_fec_mode fec_mode; - u64 total_offset; - int err; - - err = ice_phy_get_speed_and_fec_e822(hw, port, &link_spd, &fec_mode); - if (err) - return err; - - total_offset = ice_calc_fixed_tx_offset_e822(hw, link_spd); - - /* Program the fixed Tx offset into the P_REG_TOTAL_TX_OFFSET_L - * register, then indicate that the Tx offset is ready. After this, - * timestamps will be enabled. - * - * Note that this skips including the more precise offsets generated - * by the Vernier calibration. - */ - err = ice_write_64b_phy_reg_e822(hw, port, P_REG_TOTAL_TX_OFFSET_L, - total_offset); - if (err) - return err; - - err = ice_write_phy_reg_e822(hw, port, P_REG_TX_OR, 1); - if (err) - return err; - - return 0; -} - /** * ice_phy_calc_pmd_adj_e822 - Calculate PMD adjustment for Rx * @hw: pointer to the HW struct @@ -2104,47 +2063,6 @@ static int ice_phy_cfg_rx_offset_e822(struct ice_hw *hw, u8 port) return 0; } -/** - * ice_phy_cfg_fixed_rx_offset_e822 - Configure fixed Rx offset for bypass mode - * @hw: pointer to the HW struct - * @port: the PHY port to configure - * - * Calculate and program the fixed Rx offset, and indicate that the offset is - * ready. This can be used when operating in bypass mode. - */ -static int -ice_phy_cfg_fixed_rx_offset_e822(struct ice_hw *hw, u8 port) -{ - enum ice_ptp_link_spd link_spd; - enum ice_ptp_fec_mode fec_mode; - u64 total_offset; - int err; - - err = ice_phy_get_speed_and_fec_e822(hw, port, &link_spd, &fec_mode); - if (err) - return err; - - total_offset = ice_calc_fixed_rx_offset_e822(hw, link_spd); - - /* Program the fixed Rx offset into the P_REG_TOTAL_RX_OFFSET_L - * register, then indicate that the Rx offset is ready. After this, - * timestamps will be enabled. - * - * Note that this skips including the more precise offsets generated - * by Vernier calibration. - */ - err = ice_write_64b_phy_reg_e822(hw, port, P_REG_TOTAL_RX_OFFSET_L, - total_offset); - if (err) - return err; - - err = ice_write_phy_reg_e822(hw, port, P_REG_RX_OR, 1); - if (err) - return err; - - return 0; -} - /** * ice_read_phy_and_phc_time_e822 - Simultaneously capture PHC and PHY time * @hw: pointer to the HW struct @@ -2323,20 +2241,14 @@ ice_stop_phy_timer_e822(struct ice_hw *hw, u8 port, bool soft_reset) * ice_start_phy_timer_e822 - Start the PHY clock timer * @hw: pointer to the HW struct * @port: the PHY port to start - * @bypass: if true, start the PHY in bypass mode * * Start the clock of a PHY port. This must be done as part of the flow to * re-calibrate Tx and Rx timestamping offsets whenever the clock time is * initialized or when link speed changes. * - * Bypass mode enables timestamps immediately without waiting for Vernier - * calibration to complete. Hardware will still continue taking Vernier - * measurements on Tx or Rx of packets, but they will not be applied to - * timestamps. Use ice_phy_exit_bypass_e822 to exit bypass mode once hardware - * has completed offset calculation. + * Hardware will take Vernier measurements on Tx or Rx of packets. */ -int -ice_start_phy_timer_e822(struct ice_hw *hw, u8 port, bool bypass) +int ice_start_phy_timer_e822(struct ice_hw *hw, u8 port) { u32 lo, hi, val; u64 incval; @@ -2414,44 +2326,24 @@ ice_start_phy_timer_e822(struct ice_hw *hw, u8 port, bool bypass) if (err) return err; - if (bypass) { - val |= P_REG_PS_BYPASS_MODE_M; - /* Enter BYPASS mode, enabling timestamps immediately. */ - err = ice_write_phy_reg_e822(hw, port, P_REG_PS, val); - if (err) - return err; - - /* Program the fixed Tx offset */ - err = ice_phy_cfg_fixed_tx_offset_e822(hw, port); - if (err) - return err; - - /* Program the fixed Rx offset */ - err = ice_phy_cfg_fixed_rx_offset_e822(hw, port); - if (err) - return err; - } - ice_debug(hw, ICE_DBG_PTP, "Enabled clock on PHY port %u\n", port); return 0; } /** - * ice_phy_exit_bypass_e822 - Exit bypass mode, after vernier calculations + * ice_phy_calc_vernier_e822 - Perform vernier calculations * @hw: pointer to the HW struct * @port: the PHY port to configure * - * After hardware finishes vernier calculations for the Tx and Rx offset, this - * function can be used to exit bypass mode by updating the total Tx and Rx - * offsets, and then disabling bypass. This will enable hardware to include - * the more precise offset calibrations, increasing precision of the generated - * timestamps. + * Perform vernier calculations for the Tx and Rx offset. This will enable + * hardware to include the more precise offset calibrations, + * increasing precision of the generated timestamps. * * This cannot be done until hardware has measured the offsets, which requires * waiting until at least one packet has been sent and received by the device. */ -int ice_phy_exit_bypass_e822(struct ice_hw *hw, u8 port) +int ice_phy_calc_vernier_e822(struct ice_hw *hw, u8 port) { int err; u32 val; @@ -2496,29 +2388,6 @@ int ice_phy_exit_bypass_e822(struct ice_hw *hw, u8 port) return err; } - /* Exit bypass mode now that the offset has been updated */ - err = ice_read_phy_reg_e822(hw, port, P_REG_PS, &val); - if (err) { - ice_debug(hw, ICE_DBG_PTP, "Failed to read P_REG_PS for port %u, err %d\n", - port, err); - return err; - } - - if (!(val & P_REG_PS_BYPASS_MODE_M)) - ice_debug(hw, ICE_DBG_PTP, "Port %u not in bypass mode\n", - port); - - val &= ~P_REG_PS_BYPASS_MODE_M; - err = ice_write_phy_reg_e822(hw, port, P_REG_PS, val); - if (err) { - ice_debug(hw, ICE_DBG_PTP, "Failed to disable bypass for port %u, err %d\n", - port, err); - return err; - } - - dev_info(ice_hw_to_dev(hw), "Exiting bypass mode on PHY port %u\n", - port); - return 0; } diff --git a/drivers/net/ethernet/intel/ice/ice_ptp_hw.h b/drivers/net/ethernet/intel/ice/ice_ptp_hw.h index 2bda64c76abc..db4f57cb9ec9 100644 --- a/drivers/net/ethernet/intel/ice/ice_ptp_hw.h +++ b/drivers/net/ethernet/intel/ice/ice_ptp_hw.h @@ -184,8 +184,8 @@ static inline u64 ice_e822_pps_delay(enum ice_time_ref_freq time_ref) /* E822 Vernier calibration functions */ int ice_stop_phy_timer_e822(struct ice_hw *hw, u8 port, bool soft_reset); -int ice_start_phy_timer_e822(struct ice_hw *hw, u8 port, bool bypass); -int ice_phy_exit_bypass_e822(struct ice_hw *hw, u8 port); +int ice_start_phy_timer_e822(struct ice_hw *hw, u8 port); +int ice_phy_calc_vernier_e822(struct ice_hw *hw, u8 port); /* E810 family functions */ int ice_ptp_init_phy_e810(struct ice_hw *hw); From 407b66c07e9837cd11564746faaac4f32a0c5c5b Mon Sep 17 00:00:00 2001 From: Karol Kolacinski Date: Mon, 5 Dec 2022 11:52:41 -0800 Subject: [PATCH 1740/2002] ice: Reset TS memory for all quads In E822 products, the owner PF should reset memory for all quads, not only for the one where assigned lport is. Signed-off-by: Karol Kolacinski Tested-by: Gurucharan G (A Contingent worker at Intel) Signed-off-by: Tony Nguyen --- drivers/net/ethernet/intel/ice/ice_ptp.c | 29 ++-------------- drivers/net/ethernet/intel/ice/ice_ptp_hw.c | 38 +++++++++++++++++++++ drivers/net/ethernet/intel/ice/ice_ptp_hw.h | 2 ++ 3 files changed, 42 insertions(+), 27 deletions(-) diff --git a/drivers/net/ethernet/intel/ice/ice_ptp.c b/drivers/net/ethernet/intel/ice/ice_ptp.c index 9539d2d37c5b..f93fa0273252 100644 --- a/drivers/net/ethernet/intel/ice/ice_ptp.c +++ b/drivers/net/ethernet/intel/ice/ice_ptp.c @@ -1059,19 +1059,6 @@ static u64 ice_base_incval(struct ice_pf *pf) return incval; } -/** - * ice_ptp_reset_ts_memory_quad - Reset timestamp memory for one quad - * @pf: The PF private data structure - * @quad: The quad (0-4) - */ -static void ice_ptp_reset_ts_memory_quad(struct ice_pf *pf, int quad) -{ - struct ice_hw *hw = &pf->hw; - - ice_write_quad_reg_e822(hw, quad, Q_REG_TS_CTRL, Q_REG_TS_CTRL_M); - ice_write_quad_reg_e822(hw, quad, Q_REG_TS_CTRL, ~(u32)Q_REG_TS_CTRL_M); -} - /** * ice_ptp_check_tx_fifo - Check whether Tx FIFO is in an OK state * @port: PTP port for which Tx FIFO is checked @@ -1124,7 +1111,7 @@ static int ice_ptp_check_tx_fifo(struct ice_ptp_port *port) dev_dbg(ice_pf_to_dev(pf), "Port %d Tx FIFO still not empty; resetting quad %d\n", port->port_num, quad); - ice_ptp_reset_ts_memory_quad(pf, quad); + ice_ptp_reset_ts_memory_quad_e822(hw, quad); port->tx_fifo_busy_cnt = FIFO_OK; return 0; } @@ -1370,18 +1357,6 @@ int ice_ptp_link_change(struct ice_pf *pf, u8 port, bool linkup) return ice_ptp_port_phy_restart(ptp_port); } -/** - * ice_ptp_reset_ts_memory - Reset timestamp memory for all quads - * @pf: The PF private data structure - */ -static void ice_ptp_reset_ts_memory(struct ice_pf *pf) -{ - int quad; - - quad = pf->hw.port_info->lport / ICE_PORTS_PER_QUAD; - ice_ptp_reset_ts_memory_quad(pf, quad); -} - /** * ice_ptp_tx_ena_intr - Enable or disable the Tx timestamp interrupt * @pf: PF private structure @@ -1397,7 +1372,7 @@ static int ice_ptp_tx_ena_intr(struct ice_pf *pf, bool ena, u32 threshold) int quad; u32 val; - ice_ptp_reset_ts_memory(pf); + ice_ptp_reset_ts_memory(hw); for (quad = 0; quad < ICE_MAX_QUAD; quad++) { err = ice_read_quad_reg_e822(hw, quad, Q_REG_TX_MEM_GBL_CFG, diff --git a/drivers/net/ethernet/intel/ice/ice_ptp_hw.c b/drivers/net/ethernet/intel/ice/ice_ptp_hw.c index 242c4db65171..6c149b88c235 100644 --- a/drivers/net/ethernet/intel/ice/ice_ptp_hw.c +++ b/drivers/net/ethernet/intel/ice/ice_ptp_hw.c @@ -655,6 +655,32 @@ ice_clear_phy_tstamp_e822(struct ice_hw *hw, u8 quad, u8 idx) return 0; } +/** + * ice_ptp_reset_ts_memory_quad_e822 - Clear all timestamps from the quad block + * @hw: pointer to the HW struct + * @quad: the quad to read from + * + * Clear all timestamps from the PHY quad block that is shared between the + * internal PHYs on the E822 devices. + */ +void ice_ptp_reset_ts_memory_quad_e822(struct ice_hw *hw, u8 quad) +{ + ice_write_quad_reg_e822(hw, quad, Q_REG_TS_CTRL, Q_REG_TS_CTRL_M); + ice_write_quad_reg_e822(hw, quad, Q_REG_TS_CTRL, ~(u32)Q_REG_TS_CTRL_M); +} + +/** + * ice_ptp_reset_ts_memory_e822 - Clear all timestamps from all quad blocks + * @hw: pointer to the HW struct + */ +static void ice_ptp_reset_ts_memory_e822(struct ice_hw *hw) +{ + unsigned int quad; + + for (quad = 0; quad < ICE_MAX_QUAD; quad++) + ice_ptp_reset_ts_memory_quad_e822(hw, quad); +} + /** * ice_read_cgu_reg_e822 - Read a CGU register * @hw: pointer to the HW struct @@ -3247,6 +3273,18 @@ bool ice_is_pca9575_present(struct ice_hw *hw) return !status && handle; } +/** + * ice_ptp_reset_ts_memory - Reset timestamp memory for all blocks + * @hw: pointer to the HW struct + */ +void ice_ptp_reset_ts_memory(struct ice_hw *hw) +{ + if (ice_is_e810(hw)) + return; + + ice_ptp_reset_ts_memory_e822(hw); +} + /** * ice_ptp_init_phc - Initialize PTP hardware clock * @hw: pointer to the HW struct diff --git a/drivers/net/ethernet/intel/ice/ice_ptp_hw.h b/drivers/net/ethernet/intel/ice/ice_ptp_hw.h index db4f57cb9ec9..b0cd73aaac6b 100644 --- a/drivers/net/ethernet/intel/ice/ice_ptp_hw.h +++ b/drivers/net/ethernet/intel/ice/ice_ptp_hw.h @@ -133,6 +133,7 @@ int ice_ptp_write_incval_locked(struct ice_hw *hw, u64 incval); int ice_ptp_adj_clock(struct ice_hw *hw, s32 adj); int ice_read_phy_tstamp(struct ice_hw *hw, u8 block, u8 idx, u64 *tstamp); int ice_clear_phy_tstamp(struct ice_hw *hw, u8 block, u8 idx); +void ice_ptp_reset_ts_memory(struct ice_hw *hw); int ice_ptp_init_phc(struct ice_hw *hw); /* E822 family functions */ @@ -141,6 +142,7 @@ int ice_write_phy_reg_e822(struct ice_hw *hw, u8 port, u16 offset, u32 val); int ice_read_quad_reg_e822(struct ice_hw *hw, u8 quad, u16 offset, u32 *val); int ice_write_quad_reg_e822(struct ice_hw *hw, u8 quad, u16 offset, u32 val); int ice_ptp_prep_port_adj_e822(struct ice_hw *hw, u8 port, s64 time); +void ice_ptp_reset_ts_memory_quad_e822(struct ice_hw *hw, u8 quad); /** * ice_e822_time_ref - Get the current TIME_REF from capabilities From 11722c39c8d91cfb97197f8f5e2d45c9ebf27bb6 Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Mon, 5 Dec 2022 11:52:42 -0800 Subject: [PATCH 1741/2002] ice: fix misuse of "link err" with "link status" The ice_ptp_link_change function has a comment which mentions "link err" when referring to the current link status. We are storing the status of whether link is up or down, which is not an error. It is appears that this use of err accidentally got included due to an overzealous search and replace when removing the ice_status enum and local status variable. Fix the wording to use the correct term. Signed-off-by: Jacob Keller Tested-by: Gurucharan G (A Contingent worker at Intel) Signed-off-by: Tony Nguyen --- drivers/net/ethernet/intel/ice/ice_ptp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/intel/ice/ice_ptp.c b/drivers/net/ethernet/intel/ice/ice_ptp.c index f93fa0273252..5607ec578499 100644 --- a/drivers/net/ethernet/intel/ice/ice_ptp.c +++ b/drivers/net/ethernet/intel/ice/ice_ptp.c @@ -1347,7 +1347,7 @@ int ice_ptp_link_change(struct ice_pf *pf, u8 port, bool linkup) if (ptp_port->port_num != port) return -EINVAL; - /* Update cached link err for this port immediately */ + /* Update cached link status for this port immediately */ ptp_port->link_up = linkup; if (!test_bit(ICE_FLAG_PTP, pf->flags)) From 6b1ff5d392283b737abc038f7ab1509d9b8311c7 Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Mon, 5 Dec 2022 11:52:43 -0800 Subject: [PATCH 1742/2002] ice: always call ice_ptp_link_change and make it void The ice_ptp_link_change function is currently only called for E822 based hardware. Future changes are going to extend this function to perform additional tasks on link change. Always call this function, moving the E810 check from the callers down to just before we call the E822-specific function required to restart the PHY. This function also returns an error value, but none of the callers actually check it. In general, the errors it produces are more likely systemic problems such as invalid or corrupt port numbers. No caller checks these, and so no warning is logged. Re-order the flag checks so that ICE_FLAG_PTP is checked first. Drop the unnecessary check for ICE_FLAG_PTP_SUPPORTED, as ICE_FLAG_PTP will not be set except when ICE_FLAG_PTP_SUPPORTED is set. Convert the port checks to WARN_ON_ONCE, in order to generate a kernel stack trace when they are hit. Convert the function to void since no caller actually checks these return values. Co-developed-by: Dave Ertman Signed-off-by: Dave Ertman Signed-off-by: Jacob Keller Tested-by: Gurucharan G (A Contingent worker at Intel) Signed-off-by: Tony Nguyen --- drivers/net/ethernet/intel/ice/ice_main.c | 9 +++------ drivers/net/ethernet/intel/ice/ice_ptp.c | 24 +++++++++++------------ drivers/net/ethernet/intel/ice/ice_ptp.h | 7 ++++--- 3 files changed, 19 insertions(+), 21 deletions(-) diff --git a/drivers/net/ethernet/intel/ice/ice_main.c b/drivers/net/ethernet/intel/ice/ice_main.c index 2b23b4714a26..a9a7f8b52140 100644 --- a/drivers/net/ethernet/intel/ice/ice_main.c +++ b/drivers/net/ethernet/intel/ice/ice_main.c @@ -1111,8 +1111,7 @@ ice_link_event(struct ice_pf *pf, struct ice_port_info *pi, bool link_up, if (link_up == old_link && link_speed == old_link_speed) return 0; - if (!ice_is_e810(&pf->hw)) - ice_ptp_link_change(pf, pf->hw.pf_id, link_up); + ice_ptp_link_change(pf, pf->hw.pf_id, link_up); if (ice_is_dcb_active(pf)) { if (test_bit(ICE_FLAG_DCB_ENA, pf->flags)) @@ -6340,8 +6339,7 @@ static int ice_up_complete(struct ice_vsi *vsi) ice_print_link_msg(vsi, true); netif_tx_start_all_queues(vsi->netdev); netif_carrier_on(vsi->netdev); - if (!ice_is_e810(&pf->hw)) - ice_ptp_link_change(pf, pf->hw.pf_id, true); + ice_ptp_link_change(pf, pf->hw.pf_id, true); } /* Perform an initial read of the statistics registers now to @@ -6773,8 +6771,7 @@ int ice_down(struct ice_vsi *vsi) if (vsi->netdev && vsi->type == ICE_VSI_PF) { vlan_err = ice_vsi_del_vlan_zero(vsi); - if (!ice_is_e810(&vsi->back->hw)) - ice_ptp_link_change(vsi->back, vsi->back->hw.pf_id, false); + ice_ptp_link_change(vsi->back, vsi->back->hw.pf_id, false); netif_carrier_off(vsi->netdev); netif_tx_disable(vsi->netdev); } else if (vsi->type == ICE_VSI_SWITCHDEV_CTRL) { diff --git a/drivers/net/ethernet/intel/ice/ice_ptp.c b/drivers/net/ethernet/intel/ice/ice_ptp.c index 5607ec578499..1564c72189bf 100644 --- a/drivers/net/ethernet/intel/ice/ice_ptp.c +++ b/drivers/net/ethernet/intel/ice/ice_ptp.c @@ -1328,33 +1328,33 @@ out_unlock: } /** - * ice_ptp_link_change - Set or clear port registers for timestamping + * ice_ptp_link_change - Reconfigure PTP after link status change * @pf: Board private structure * @port: Port for which the PHY start is set * @linkup: Link is up or down */ -int ice_ptp_link_change(struct ice_pf *pf, u8 port, bool linkup) +void ice_ptp_link_change(struct ice_pf *pf, u8 port, bool linkup) { struct ice_ptp_port *ptp_port; - if (!test_bit(ICE_FLAG_PTP_SUPPORTED, pf->flags)) - return 0; + if (!test_bit(ICE_FLAG_PTP, pf->flags)) + return; - if (port >= ICE_NUM_EXTERNAL_PORTS) - return -EINVAL; + if (WARN_ON_ONCE(port >= ICE_NUM_EXTERNAL_PORTS)) + return; ptp_port = &pf->ptp.port; - if (ptp_port->port_num != port) - return -EINVAL; + if (WARN_ON_ONCE(ptp_port->port_num != port)) + return; /* Update cached link status for this port immediately */ ptp_port->link_up = linkup; - if (!test_bit(ICE_FLAG_PTP, pf->flags)) - /* PTP is not setup */ - return -EAGAIN; + /* E810 devices do not need to reconfigure the PHY */ + if (ice_is_e810(&pf->hw)) + return; - return ice_ptp_port_phy_restart(ptp_port); + ice_ptp_port_phy_restart(ptp_port); } /** diff --git a/drivers/net/ethernet/intel/ice/ice_ptp.h b/drivers/net/ethernet/intel/ice/ice_ptp.h index ca0fbfd71ed2..39cab020f1af 100644 --- a/drivers/net/ethernet/intel/ice/ice_ptp.h +++ b/drivers/net/ethernet/intel/ice/ice_ptp.h @@ -257,7 +257,7 @@ void ice_ptp_reset(struct ice_pf *pf); void ice_ptp_prepare_for_reset(struct ice_pf *pf); void ice_ptp_init(struct ice_pf *pf); void ice_ptp_release(struct ice_pf *pf); -int ice_ptp_link_change(struct ice_pf *pf, u8 port, bool linkup); +void ice_ptp_link_change(struct ice_pf *pf, u8 port, bool linkup); #else /* IS_ENABLED(CONFIG_PTP_1588_CLOCK) */ static inline int ice_ptp_set_ts_config(struct ice_pf *pf, struct ifreq *ifr) { @@ -292,7 +292,8 @@ static inline void ice_ptp_reset(struct ice_pf *pf) { } static inline void ice_ptp_prepare_for_reset(struct ice_pf *pf) { } static inline void ice_ptp_init(struct ice_pf *pf) { } static inline void ice_ptp_release(struct ice_pf *pf) { } -static inline int ice_ptp_link_change(struct ice_pf *pf, u8 port, bool linkup) -{ return 0; } +static inline void ice_ptp_link_change(struct ice_pf *pf, u8 port, bool linkup) +{ +} #endif /* IS_ENABLED(CONFIG_PTP_1588_CLOCK) */ #endif /* _ICE_PTP_H_ */ From 0dd9286263923860a1829778e232a501b49c485f Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Mon, 5 Dec 2022 11:52:44 -0800 Subject: [PATCH 1743/2002] ice: handle discarding old Tx requests in ice_ptp_tx_tstamp Currently the driver uses the PTP kthread to process handling and discarding of stale Tx timestamp requests. The function ice_ptp_tx_tstamp_cleanup is used for this. A separate thread creates complications for the driver as we now have both the main Tx timestamp processing IRQ checking timestamps as well as the kthread. Rather than using the kthread to handle this, simply check for stale timestamps within the ice_ptp_tx_tstamp function. This function must already process the timestamps anyways. If a Tx timestamp has been waiting for 2 seconds we simply clear the bit and discard the SKB. This avoids the complication of having separate threads polling, reducing overall CPU work. Signed-off-by: Jacob Keller Tested-by: Gurucharan G (A Contingent worker at Intel) Signed-off-by: Tony Nguyen --- drivers/net/ethernet/intel/ice/ice_ptp.c | 111 ++++++++++------------- 1 file changed, 48 insertions(+), 63 deletions(-) diff --git a/drivers/net/ethernet/intel/ice/ice_ptp.c b/drivers/net/ethernet/intel/ice/ice_ptp.c index 1564c72189bf..726579c0098c 100644 --- a/drivers/net/ethernet/intel/ice/ice_ptp.c +++ b/drivers/net/ethernet/intel/ice/ice_ptp.c @@ -626,15 +626,32 @@ static u64 ice_ptp_extend_40b_ts(struct ice_pf *pf, u64 in_tstamp) * Note that we only take the tracking lock when clearing the bit and when * checking if we need to re-queue this task. The only place where bits can be * set is the hard xmit routine where an SKB has a request flag set. The only - * places where we clear bits are this work function, or the periodic cleanup - * thread. If the cleanup thread clears a bit we're processing we catch it - * when we lock to clear the bit and then grab the SKB pointer. If a Tx thread - * starts a new timestamp, we might not begin processing it right away but we - * will notice it at the end when we re-queue the task. If a Tx thread starts - * a new timestamp just after this function exits without re-queuing, - * the interrupt when the timestamp finishes should trigger. Avoiding holding - * the lock for the entire function is important in order to ensure that Tx - * threads do not get blocked while waiting for the lock. + * places where we clear bits are this work function, or when flushing the Tx + * timestamp tracker. + * + * If the Tx tracker gets flushed while we're processing a packet, we catch + * this because we grab the SKB pointer under lock. If the SKB is NULL we know + * that another thread already discarded the SKB and we can avoid passing it + * up to the stack. + * + * If a Tx thread starts a new timestamp, we might not begin processing it + * right away but we will notice it at the end when we re-queue the task. + * + * If a Tx thread starts a new timestamp just after this function exits, the + * interrupt for that timestamp should re-trigger this function once + * a timestamp is ready. + * + * Note that minimizing the time we hold the lock is important. If we held the + * lock for the entire function we would unnecessarily block the Tx hot path + * which needs to set the timestamp index. Limiting how long we hold the lock + * ensures we do not block Tx threads. + * + * If a Tx packet has been waiting for more than 2 seconds, it is not possible + * to correctly extend the timestamp using the cached PHC time. It is + * extremely unlikely that a packet will ever take this long to timestamp. If + * we detect a Tx timestamp request that has waited for this long we assume + * the packet will never be sent by hardware and discard it without reading + * the timestamp register. */ static bool ice_ptp_tx_tstamp(struct ice_ptp_tx *tx) { @@ -652,10 +669,21 @@ static bool ice_ptp_tx_tstamp(struct ice_ptp_tx *tx) for_each_set_bit(idx, tx->in_use, tx->len) { struct skb_shared_hwtstamps shhwtstamps = {}; u8 phy_idx = idx + tx->offset; - u64 raw_tstamp, tstamp; + u64 raw_tstamp = 0, tstamp; + bool drop_ts = false; struct sk_buff *skb; int err; + /* Drop packets which have waited for more than 2 seconds */ + if (time_is_before_jiffies(tx->tstamps[idx].start + 2 * HZ)) { + drop_ts = true; + + /* Count the number of Tx timestamps that timed out */ + pf->ptp.tx_hwtstamp_timeouts++; + + goto skip_ts_read; + } + ice_trace(tx_tstamp_fw_req, tx->tstamps[idx].skb, idx); err = ice_read_phy_tstamp(&pf->hw, tx->block, phy_idx, @@ -670,22 +698,26 @@ static bool ice_ptp_tx_tstamp(struct ice_ptp_tx *tx) raw_tstamp == tx->tstamps[idx].cached_tstamp) continue; - /* The timestamp is valid, so we'll go ahead and clear this - * index and then send the timestamp up to the stack. - */ +skip_ts_read: spin_lock(&tx->lock); - tx->tstamps[idx].cached_tstamp = raw_tstamp; + if (raw_tstamp) + tx->tstamps[idx].cached_tstamp = raw_tstamp; clear_bit(idx, tx->in_use); skb = tx->tstamps[idx].skb; tx->tstamps[idx].skb = NULL; spin_unlock(&tx->lock); - /* it's (unlikely but) possible we raced with the cleanup - * thread for discarding old timestamp requests. + /* It is unlikely but possible that the SKB will have been + * flushed at this point due to link change or teardown. */ if (!skb) continue; + if (drop_ts) { + dev_kfree_skb_any(skb); + continue; + } + /* Extend the timestamp using cached PHC time */ tstamp = ice_ptp_extend_40b_ts(pf, raw_tstamp); if (tstamp) { @@ -826,51 +858,6 @@ ice_ptp_init_tx_e810(struct ice_pf *pf, struct ice_ptp_tx *tx) return ice_ptp_alloc_tx_tracker(tx); } -/** - * ice_ptp_tx_tstamp_cleanup - Cleanup old timestamp requests that got dropped - * @pf: pointer to the PF struct - * @tx: PTP Tx tracker to clean up - * - * Loop through the Tx timestamp requests and see if any of them have been - * waiting for a long time. Discard any SKBs that have been waiting for more - * than 2 seconds. This is long enough to be reasonably sure that the - * timestamp will never be captured. This might happen if the packet gets - * discarded before it reaches the PHY timestamping block. - */ -static void ice_ptp_tx_tstamp_cleanup(struct ice_pf *pf, struct ice_ptp_tx *tx) -{ - struct ice_hw *hw = &pf->hw; - u8 idx; - - if (!tx->init) - return; - - for_each_set_bit(idx, tx->in_use, tx->len) { - struct sk_buff *skb; - u64 raw_tstamp; - - /* Check if this SKB has been waiting for too long */ - if (time_is_after_jiffies(tx->tstamps[idx].start + 2 * HZ)) - continue; - - /* Read tstamp to be able to use this register again */ - ice_read_phy_tstamp(hw, tx->block, idx + tx->offset, - &raw_tstamp); - - spin_lock(&tx->lock); - skb = tx->tstamps[idx].skb; - tx->tstamps[idx].skb = NULL; - clear_bit(idx, tx->in_use); - spin_unlock(&tx->lock); - - /* Count the number of Tx timestamps which have timed out */ - pf->ptp.tx_hwtstamp_timeouts++; - - /* Free the SKB after we've cleared the bit */ - dev_kfree_skb_any(skb); - } -} - /** * ice_ptp_update_cached_phctime - Update the cached PHC time values * @pf: Board specific private structure @@ -2359,8 +2346,6 @@ static void ice_ptp_periodic_work(struct kthread_work *work) err = ice_ptp_update_cached_phctime(pf); - ice_ptp_tx_tstamp_cleanup(pf, &pf->ptp.port.tx); - /* Run twice a second or reschedule if phc update failed */ kthread_queue_delayed_work(ptp->kworker, &ptp->work, msecs_to_jiffies(err ? 10 : 500)); From 10e4b4a3a3e1b7007a72988bb0a7c741b19d90d7 Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Mon, 5 Dec 2022 11:52:45 -0800 Subject: [PATCH 1744/2002] ice: check Tx timestamp memory register for ready timestamps The PHY for E822 based hardware has a register which indicates which timestamps are valid in the PHY timestamp memory block. Each bit in the register indicates whether the associated index in the timestamp memory is valid. Hardware sets this bit when the timestamp is captured, and clears the bit when the timestamp is read. Use of this register is important as reading timestamp registers can impact the way that hardware generates timestamp interrupts. This occurs because the PHY has an internal value which is incremented when hardware captures a timestamp and decremented when software reads a timestamp. Reading timestamps which are not marked as valid still decrement the internal value and can result in the Tx timestamp interrupt not triggering in the future. To prevent this, use the timestamp memory value to determine which timestamps are ready to be read. The ice_get_phy_tx_tstamp_ready function reads this value. For E810 devices, this just always returns with all bits set. Skip any timestamp which is not set in this bitmap, avoiding reading extra timestamps on E822 devices. The stale check against a cached timestamp value is no longer necessary for PHYs which support the timestamp ready bitmap properly. E810 devices still need this. Introduce a new verify_cached flag to the ice_ptp_tx structure. Use this to determine if we need to perform the verification against the cached timestamp value. Set this to 1 for the E810 Tx tracker init function. Notice that many of the fields in ice_ptp_tx are simple 1 bit flags. Save some structure space by using bitfields of length 1 for these values. Modify the ICE_PTP_TS_VALID check to simply drop the timestamp immediately so that in an event of getting such an invalid timestamp the driver does not attempt to re-read the timestamp again in a future poll of the register. With these changes, the driver now reads each timestamp register exactly once, and does not attempt any re-reads. This ensures the interrupt tracking logic in the PHY will not get stuck. Signed-off-by: Jacob Keller Tested-by: Gurucharan G (A Contingent worker at Intel) Signed-off-by: Tony Nguyen --- drivers/net/ethernet/intel/ice/ice_ptp.c | 48 ++++++++++++-- drivers/net/ethernet/intel/ice/ice_ptp.h | 17 +++-- drivers/net/ethernet/intel/ice/ice_ptp_hw.c | 72 +++++++++++++++++++++ drivers/net/ethernet/intel/ice/ice_ptp_hw.h | 1 + 4 files changed, 126 insertions(+), 12 deletions(-) diff --git a/drivers/net/ethernet/intel/ice/ice_ptp.c b/drivers/net/ethernet/intel/ice/ice_ptp.c index 726579c0098c..30061598912b 100644 --- a/drivers/net/ethernet/intel/ice/ice_ptp.c +++ b/drivers/net/ethernet/intel/ice/ice_ptp.c @@ -658,6 +658,9 @@ static bool ice_ptp_tx_tstamp(struct ice_ptp_tx *tx) struct ice_ptp_port *ptp_port; bool ts_handled = true; struct ice_pf *pf; + struct ice_hw *hw; + u64 tstamp_ready; + int err; u8 idx; if (!tx->init) @@ -665,6 +668,12 @@ static bool ice_ptp_tx_tstamp(struct ice_ptp_tx *tx) ptp_port = container_of(tx, struct ice_ptp_port, tx); pf = ptp_port_to_pf(ptp_port); + hw = &pf->hw; + + /* Read the Tx ready status first */ + err = ice_get_phy_tx_tstamp_ready(hw, tx->block, &tstamp_ready); + if (err) + return false; for_each_set_bit(idx, tx->in_use, tx->len) { struct skb_shared_hwtstamps shhwtstamps = {}; @@ -672,7 +681,6 @@ static bool ice_ptp_tx_tstamp(struct ice_ptp_tx *tx) u64 raw_tstamp = 0, tstamp; bool drop_ts = false; struct sk_buff *skb; - int err; /* Drop packets which have waited for more than 2 seconds */ if (time_is_before_jiffies(tx->tstamps[idx].start + 2 * HZ)) { @@ -680,27 +688,47 @@ static bool ice_ptp_tx_tstamp(struct ice_ptp_tx *tx) /* Count the number of Tx timestamps that timed out */ pf->ptp.tx_hwtstamp_timeouts++; + } - goto skip_ts_read; + /* Only read a timestamp from the PHY if its marked as ready + * by the tstamp_ready register. This avoids unnecessary + * reading of timestamps which are not yet valid. This is + * important as we must read all timestamps which are valid + * and only timestamps which are valid during each interrupt. + * If we do not, the hardware logic for generating a new + * interrupt can get stuck on some devices. + */ + if (!(tstamp_ready & BIT_ULL(phy_idx))) { + if (drop_ts) + goto skip_ts_read; + + continue; } ice_trace(tx_tstamp_fw_req, tx->tstamps[idx].skb, idx); - err = ice_read_phy_tstamp(&pf->hw, tx->block, phy_idx, - &raw_tstamp); + err = ice_read_phy_tstamp(hw, tx->block, phy_idx, &raw_tstamp); if (err) continue; ice_trace(tx_tstamp_fw_done, tx->tstamps[idx].skb, idx); - /* Check if the timestamp is invalid or stale */ - if (!(raw_tstamp & ICE_PTP_TS_VALID) || + /* For PHYs which don't implement a proper timestamp ready + * bitmap, verify that the timestamp value is different + * from the last cached timestamp. If it is not, skip this for + * now assuming it hasn't yet been captured by hardware. + */ + if (!drop_ts && tx->verify_cached && raw_tstamp == tx->tstamps[idx].cached_tstamp) continue; + /* Discard any timestamp value without the valid bit set */ + if (!(raw_tstamp & ICE_PTP_TS_VALID)) + drop_ts = true; + skip_ts_read: spin_lock(&tx->lock); - if (raw_tstamp) + if (tx->verify_cached && raw_tstamp) tx->tstamps[idx].cached_tstamp = raw_tstamp; clear_bit(idx, tx->in_use); skb = tx->tstamps[idx].skb; @@ -836,6 +864,7 @@ ice_ptp_init_tx_e822(struct ice_pf *pf, struct ice_ptp_tx *tx, u8 port) tx->block = port / ICE_PORTS_PER_QUAD; tx->offset = (port % ICE_PORTS_PER_QUAD) * INDEX_PER_PORT_E822; tx->len = INDEX_PER_PORT_E822; + tx->verify_cached = 0; return ice_ptp_alloc_tx_tracker(tx); } @@ -854,6 +883,11 @@ ice_ptp_init_tx_e810(struct ice_pf *pf, struct ice_ptp_tx *tx) tx->block = pf->hw.port_info->lport; tx->offset = 0; tx->len = INDEX_PER_PORT_E810; + /* The E810 PHY does not provide a timestamp ready bitmap. Instead, + * verify new timestamps against cached copy of the last read + * timestamp. + */ + tx->verify_cached = 1; return ice_ptp_alloc_tx_tracker(tx); } diff --git a/drivers/net/ethernet/intel/ice/ice_ptp.h b/drivers/net/ethernet/intel/ice/ice_ptp.h index 39cab020f1af..5052fc41bed3 100644 --- a/drivers/net/ethernet/intel/ice/ice_ptp.h +++ b/drivers/net/ethernet/intel/ice/ice_ptp.h @@ -93,9 +93,14 @@ struct ice_perout_channel { * we discard old requests that were not fulfilled within a 2 second time * window. * Timestamp values in the PHY are read only and do not get cleared except at - * hardware reset or when a new timestamp value is captured. The cached_tstamp - * field is used to detect the case where a new timestamp has not yet been - * captured, ensuring that we avoid sending stale timestamp data to the stack. + * hardware reset or when a new timestamp value is captured. + * + * Some PHY types do not provide a "ready" bitmap indicating which timestamp + * indexes are valid. In these cases, we use a cached_tstamp to keep track of + * the last timestamp we read for a given index. If the current timestamp + * value is the same as the cached value, we assume a new timestamp hasn't + * been captured. This avoids reporting stale timestamps to the stack. This is + * only done if the verify_cached flag is set in ice_ptp_tx structure. */ struct ice_tx_tstamp { struct sk_buff *skb; @@ -114,6 +119,7 @@ struct ice_tx_tstamp { * @init: if true, the tracker is initialized; * @calibrating: if true, the PHY is calibrating the Tx offset. During this * window, timestamps are temporarily disabled. + * @verify_cached: if true, verify new timestamp differs from last read value */ struct ice_ptp_tx { spinlock_t lock; /* lock protecting in_use bitmap */ @@ -122,8 +128,9 @@ struct ice_ptp_tx { u8 block; u8 offset; u8 len; - u8 init; - u8 calibrating; + u8 init : 1; + u8 calibrating : 1; + u8 verify_cached : 1; }; /* Quad and port information for initializing timestamp blocks */ diff --git a/drivers/net/ethernet/intel/ice/ice_ptp_hw.c b/drivers/net/ethernet/intel/ice/ice_ptp_hw.c index 6c149b88c235..d5d51427580a 100644 --- a/drivers/net/ethernet/intel/ice/ice_ptp_hw.c +++ b/drivers/net/ethernet/intel/ice/ice_ptp_hw.c @@ -2417,6 +2417,41 @@ int ice_phy_calc_vernier_e822(struct ice_hw *hw, u8 port) return 0; } +/** + * ice_get_phy_tx_tstamp_ready_e822 - Read Tx memory status register + * @hw: pointer to the HW struct + * @quad: the timestamp quad to read from + * @tstamp_ready: contents of the Tx memory status register + * + * Read the Q_REG_TX_MEMORY_STATUS register indicating which timestamps in + * the PHY are ready. A set bit means the corresponding timestamp is valid and + * ready to be captured from the PHY timestamp block. + */ +static int +ice_get_phy_tx_tstamp_ready_e822(struct ice_hw *hw, u8 quad, u64 *tstamp_ready) +{ + u32 hi, lo; + int err; + + err = ice_read_quad_reg_e822(hw, quad, Q_REG_TX_MEMORY_STATUS_U, &hi); + if (err) { + ice_debug(hw, ICE_DBG_PTP, "Failed to read TX_MEMORY_STATUS_U for quad %u, err %d\n", + quad, err); + return err; + } + + err = ice_read_quad_reg_e822(hw, quad, Q_REG_TX_MEMORY_STATUS_L, &lo); + if (err) { + ice_debug(hw, ICE_DBG_PTP, "Failed to read TX_MEMORY_STATUS_L for quad %u, err %d\n", + quad, err); + return err; + } + + *tstamp_ready = (u64)hi << 32 | (u64)lo; + + return 0; +} + /* E810 functions * * The following functions operate on the E810 series devices which use @@ -3091,6 +3126,22 @@ int ice_clear_phy_tstamp(struct ice_hw *hw, u8 block, u8 idx) return ice_clear_phy_tstamp_e822(hw, block, idx); } +/** + * ice_get_phy_tx_tstamp_ready_e810 - Read Tx memory status register + * @hw: pointer to the HW struct + * @port: the PHY port to read + * @tstamp_ready: contents of the Tx memory status register + * + * E810 devices do not use a Tx memory status register. Instead simply + * indicate that all timestamps are currently ready. + */ +static int +ice_get_phy_tx_tstamp_ready_e810(struct ice_hw *hw, u8 port, u64 *tstamp_ready) +{ + *tstamp_ready = 0xFFFFFFFFFFFFFFFF; + return 0; +} + /* E810T SMA functions * * The following functions operate specifically on E810T hardware and are used @@ -3306,3 +3357,24 @@ int ice_ptp_init_phc(struct ice_hw *hw) else return ice_ptp_init_phc_e822(hw); } + +/** + * ice_get_phy_tx_tstamp_ready - Read PHY Tx memory status indication + * @hw: pointer to the HW struct + * @block: the timestamp block to check + * @tstamp_ready: storage for the PHY Tx memory status information + * + * Check the PHY for Tx timestamp memory status. This reports a 64 bit value + * which indicates which timestamps in the block may be captured. A set bit + * means the timestamp can be read. An unset bit means the timestamp is not + * ready and software should avoid reading the register. + */ +int ice_get_phy_tx_tstamp_ready(struct ice_hw *hw, u8 block, u64 *tstamp_ready) +{ + if (ice_is_e810(hw)) + return ice_get_phy_tx_tstamp_ready_e810(hw, block, + tstamp_ready); + else + return ice_get_phy_tx_tstamp_ready_e822(hw, block, + tstamp_ready); +} diff --git a/drivers/net/ethernet/intel/ice/ice_ptp_hw.h b/drivers/net/ethernet/intel/ice/ice_ptp_hw.h index b0cd73aaac6b..b781dadf5a39 100644 --- a/drivers/net/ethernet/intel/ice/ice_ptp_hw.h +++ b/drivers/net/ethernet/intel/ice/ice_ptp_hw.h @@ -135,6 +135,7 @@ int ice_read_phy_tstamp(struct ice_hw *hw, u8 block, u8 idx, u64 *tstamp); int ice_clear_phy_tstamp(struct ice_hw *hw, u8 block, u8 idx); void ice_ptp_reset_ts_memory(struct ice_hw *hw); int ice_ptp_init_phc(struct ice_hw *hw); +int ice_get_phy_tx_tstamp_ready(struct ice_hw *hw, u8 block, u64 *tstamp_ready); /* E822 family functions */ int ice_read_phy_reg_e822(struct ice_hw *hw, u8 port, u16 offset, u32 *val); From f0ae124019faaa03f8b4c3fbe52ae35ab3a8dbda Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Mon, 5 Dec 2022 11:52:46 -0800 Subject: [PATCH 1745/2002] ice: synchronize the misc IRQ when tearing down Tx tracker Since commit 1229b33973c7 ("ice: Add low latency Tx timestamp read") the ice driver has used a threaded IRQ for handling Tx timestamps. This change did not add a call to synchronize_irq during ice_ptp_release_tx_tracker. Thus it is possible that an interrupt could occur just as the tracker is being removed. This could lead to a use-after-free of the Tx tracker structure data. Fix this by calling sychronize_irq in ice_ptp_release_tx_tracker after we've cleared the init flag. In addition, make sure that we re-check the init flag at the end of ice_ptp_tx_tstamp before we exit ensuring that we will stop polling for new timestamps once the tracker de-initialization has begun. Refactor the ts_handled variable into "more_timestamps" so that we can simply directly assign this boolean instead of relying on an initialized value of true. This makes the new combined check easier to read. With this change, the ice_ptp_release_tx_tracker function will now wait for the threaded interrupt to complete if it was executing while the init flag was cleared. Signed-off-by: Jacob Keller Tested-by: Gurucharan G (A Contingent worker at Intel) Signed-off-by: Tony Nguyen --- drivers/net/ethernet/intel/ice/ice_ptp.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/net/ethernet/intel/ice/ice_ptp.c b/drivers/net/ethernet/intel/ice/ice_ptp.c index 30061598912b..0282ccc55819 100644 --- a/drivers/net/ethernet/intel/ice/ice_ptp.c +++ b/drivers/net/ethernet/intel/ice/ice_ptp.c @@ -656,7 +656,7 @@ static u64 ice_ptp_extend_40b_ts(struct ice_pf *pf, u64 in_tstamp) static bool ice_ptp_tx_tstamp(struct ice_ptp_tx *tx) { struct ice_ptp_port *ptp_port; - bool ts_handled = true; + bool more_timestamps; struct ice_pf *pf; struct ice_hw *hw; u64 tstamp_ready; @@ -761,11 +761,10 @@ skip_ts_read: * poll for remaining timestamps. */ spin_lock(&tx->lock); - if (!bitmap_empty(tx->in_use, tx->len)) - ts_handled = false; + more_timestamps = tx->init && !bitmap_empty(tx->in_use, tx->len); spin_unlock(&tx->lock); - return ts_handled; + return !more_timestamps; } /** @@ -836,6 +835,9 @@ ice_ptp_release_tx_tracker(struct ice_pf *pf, struct ice_ptp_tx *tx) { tx->init = 0; + /* wait for potentially outstanding interrupt to complete */ + synchronize_irq(pf->msix_entries[pf->oicr_idx].vector); + ice_ptp_flush_tx_tracker(pf, tx); kfree(tx->tstamps); From 5a5624d1edf44e808481dedaf59eae61fd66e052 Mon Sep 17 00:00:00 2001 From: Oz Shlomo Date: Sat, 3 Dec 2022 14:13:23 -0800 Subject: [PATCH 1746/2002] net/mlx5e: E-Switch, handle flow attribute with no destinations Rules with drop action are not required to have a destination. Currently the destination list is allocated with the maximum number of destinations and passed to the fs_core layer along with the actual number of destinations. Remove redundant passing of dest pointer when count of dest is 0. Signed-off-by: Oz Shlomo Reviewed-by: Roi Dayan Signed-off-by: Saeed Mahameed Link: https://lore.kernel.org/r/20221203221337.29267-2-saeed@kernel.org Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c b/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c index c6a14202c62c..1987a9d9d40c 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c @@ -640,6 +640,11 @@ mlx5_eswitch_add_offloaded_rule(struct mlx5_eswitch *esw, goto err_esw_get; } + if (!i) { + kfree(dest); + dest = NULL; + } + if (mlx5_eswitch_termtbl_required(esw, attr, &flow_act, spec)) rule = mlx5_eswitch_add_termtbl_rule(esw, fdb, spec, esw_attr, &flow_act, dest, i); From f07d8afb1ceae57767cf80c134b7a03e9c589276 Mon Sep 17 00:00:00 2001 From: Oz Shlomo Date: Sat, 3 Dec 2022 14:13:24 -0800 Subject: [PATCH 1747/2002] net/mlx5: fs, assert null dest pointer when dest_num is 0 Currently create_flow_handle() assumes a null dest pointer when there are no destinations. This might not be the case as the caller may pass an allocated dest array while setting the dest_num parameter to 0. Assert null dest array for flow rules that have no destinations (e.g. drop rule). Signed-off-by: Oz Shlomo Reviewed-by: Roi Dayan Reviewed-by: Mark Bloch Signed-off-by: Saeed Mahameed Link: https://lore.kernel.org/r/20221203221337.29267-3-saeed@kernel.org Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/mellanox/mlx5/core/fs_core.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c index d53749248fa0..d53190f22871 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c @@ -1962,6 +1962,9 @@ _mlx5_add_flow_rules(struct mlx5_flow_table *ft, if (flow_act->fg && ft->autogroup.active) return ERR_PTR(-EINVAL); + if (dest && dest_num <= 0) + return ERR_PTR(-EINVAL); + for (i = 0; i < dest_num; i++) { if (!dest_is_valid(&dest[i], flow_act, ft)) return ERR_PTR(-EINVAL); From 8facc02f22f17879351419df9b253e06dc1f618b Mon Sep 17 00:00:00 2001 From: Oz Shlomo Date: Sat, 3 Dec 2022 14:13:25 -0800 Subject: [PATCH 1748/2002] net/mlx5e: TC, reuse flow attribute post parser processing After the tc action parsing phase the flow attribute is initialized with relevant eswitch offload objects such as tunnel, vlan, header modify and counter attributes. The post processing is done both for fdb and post-action attributes. Reuse the flow attribute post parsing logic by both fdb and post-action offloads. Signed-off-by: Oz Shlomo Reviewed-by: Roi Dayan Signed-off-by: Saeed Mahameed Link: https://lore.kernel.org/r/20221203221337.29267-4-saeed@kernel.org Signed-off-by: Jakub Kicinski --- .../net/ethernet/mellanox/mlx5/core/en_tc.c | 98 ++++++++++--------- 1 file changed, 52 insertions(+), 46 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c index 10d1609ece58..46222541e435 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c @@ -606,6 +606,12 @@ int mlx5e_get_flow_namespace(struct mlx5e_tc_flow *flow) MLX5_FLOW_NAMESPACE_FDB : MLX5_FLOW_NAMESPACE_KERNEL; } +static struct mlx5_core_dev * +get_flow_counter_dev(struct mlx5e_tc_flow *flow) +{ + return mlx5e_is_eswitch_flow(flow) ? flow->attr->esw_attr->counter_dev : flow->priv->mdev; +} + static struct mod_hdr_tbl * get_mod_hdr_table(struct mlx5e_priv *priv, struct mlx5e_tc_flow *flow) { @@ -1718,6 +1724,48 @@ clean_encap_dests(struct mlx5e_priv *priv, } } +static int +post_process_attr(struct mlx5e_tc_flow *flow, + struct mlx5_flow_attr *attr, + bool is_post_act_attr, + struct netlink_ext_ack *extack) +{ + struct mlx5_eswitch *esw = flow->priv->mdev->priv.eswitch; + bool vf_tun; + int err = 0; + + err = set_encap_dests(flow->priv, flow, attr, extack, &vf_tun); + if (err) + goto err_out; + + if (mlx5e_is_eswitch_flow(flow)) { + err = mlx5_eswitch_add_vlan_action(esw, attr); + if (err) + goto err_out; + } + + if (attr->action & MLX5_FLOW_CONTEXT_ACTION_MOD_HDR) { + if (vf_tun || is_post_act_attr) { + err = mlx5e_tc_add_flow_mod_hdr(flow->priv, flow, attr); + if (err) + goto err_out; + } else { + err = mlx5e_attach_mod_hdr(flow->priv, flow, attr->parse_attr); + if (err) + goto err_out; + } + } + + if (attr->action & MLX5_FLOW_CONTEXT_ACTION_COUNT) { + err = alloc_flow_attr_counter(get_flow_counter_dev(flow), attr); + if (err) + goto err_out; + } + +err_out: + return err; +} + static int mlx5e_tc_add_fdb_flow(struct mlx5e_priv *priv, struct mlx5e_tc_flow *flow, @@ -1728,7 +1776,6 @@ mlx5e_tc_add_fdb_flow(struct mlx5e_priv *priv, struct mlx5_flow_attr *attr = flow->attr; struct mlx5_esw_flow_attr *esw_attr; u32 max_prio, max_chain; - bool vf_tun; int err = 0; parse_attr = attr->parse_attr; @@ -1818,32 +1865,10 @@ mlx5e_tc_add_fdb_flow(struct mlx5e_priv *priv, esw_attr->int_port = int_port; } - err = set_encap_dests(priv, flow, attr, extack, &vf_tun); + err = post_process_attr(flow, attr, false, extack); if (err) goto err_out; - err = mlx5_eswitch_add_vlan_action(esw, attr); - if (err) - goto err_out; - - if (attr->action & MLX5_FLOW_CONTEXT_ACTION_MOD_HDR) { - if (vf_tun) { - err = mlx5e_tc_add_flow_mod_hdr(priv, flow, attr); - if (err) - goto err_out; - } else { - err = mlx5e_attach_mod_hdr(priv, flow, parse_attr); - if (err) - goto err_out; - } - } - - if (attr->action & MLX5_FLOW_CONTEXT_ACTION_COUNT) { - err = alloc_flow_attr_counter(esw_attr->counter_dev, attr); - if (err) - goto err_out; - } - /* we get here if one of the following takes place: * (1) there's no error * (2) there's an encap action and we don't have valid neigh @@ -3639,12 +3664,6 @@ mlx5e_clone_flow_attr_for_post_act(struct mlx5_flow_attr *attr, return attr2; } -static struct mlx5_core_dev * -get_flow_counter_dev(struct mlx5e_tc_flow *flow) -{ - return mlx5e_is_eswitch_flow(flow) ? flow->attr->esw_attr->counter_dev : flow->priv->mdev; -} - struct mlx5_flow_attr * mlx5e_tc_get_encap_attr(struct mlx5e_tc_flow *flow) { @@ -3754,7 +3773,6 @@ alloc_flow_post_acts(struct mlx5e_tc_flow *flow, struct netlink_ext_ack *extack) struct mlx5e_post_act *post_act = get_post_action(flow->priv); struct mlx5_flow_attr *attr, *next_attr = NULL; struct mlx5e_post_act_handle *handle; - bool vf_tun; int err; /* This is going in reverse order as needed. @@ -3776,25 +3794,13 @@ alloc_flow_post_acts(struct mlx5e_tc_flow *flow, struct netlink_ext_ack *extack) if (list_is_last(&attr->list, &flow->attrs)) break; - err = set_encap_dests(flow->priv, flow, attr, extack, &vf_tun); - if (err) - goto out_free; - err = actions_prepare_mod_hdr_actions(flow->priv, flow, attr, extack); if (err) goto out_free; - if (attr->action & MLX5_FLOW_CONTEXT_ACTION_MOD_HDR) { - err = mlx5e_tc_add_flow_mod_hdr(flow->priv, flow, attr); - if (err) - goto out_free; - } - - if (attr->action & MLX5_FLOW_CONTEXT_ACTION_COUNT) { - err = alloc_flow_attr_counter(get_flow_counter_dev(flow), attr); - if (err) - goto out_free; - } + err = post_process_attr(flow, attr, true, extack); + if (err) + goto out_free; handle = mlx5e_tc_post_act_add(post_act, attr); if (IS_ERR(handle)) { From d3f6b0df9128c00c54e11702f62b967fc63c85f3 Mon Sep 17 00:00:00 2001 From: Oz Shlomo Date: Sat, 3 Dec 2022 14:13:26 -0800 Subject: [PATCH 1749/2002] net/mlx5e: TC, add terminating actions Extend act api to identify actions that terminate action list. Pre-step for terminating branching actions. Signed-off-by: Oz Shlomo Reviewed-by: Roi Dayan Signed-off-by: Saeed Mahameed Link: https://lore.kernel.org/r/20221203221337.29267-5-saeed@kernel.org Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/accept.c | 1 + drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/act.c | 2 +- drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/act.h | 3 +++ drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/drop.c | 1 + drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/goto.c | 1 + drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/mirred.c | 7 +++++++ .../net/ethernet/mellanox/mlx5/core/en/tc/act/mirred_nic.c | 1 + 7 files changed, 15 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/accept.c b/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/accept.c index 21aab96357b5..a278f52d52b0 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/accept.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/accept.c @@ -28,4 +28,5 @@ tc_act_parse_accept(struct mlx5e_tc_act_parse_state *parse_state, struct mlx5e_tc_act mlx5e_tc_act_accept = { .can_offload = tc_act_can_offload_accept, .parse_action = tc_act_parse_accept, + .is_terminating_action = true, }; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/act.c b/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/act.c index 3337241cfd84..eba0c8698926 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/act.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/act.c @@ -11,7 +11,7 @@ static struct mlx5e_tc_act *tc_acts_fdb[NUM_FLOW_ACTIONS] = { [FLOW_ACTION_DROP] = &mlx5e_tc_act_drop, [FLOW_ACTION_TRAP] = &mlx5e_tc_act_trap, [FLOW_ACTION_GOTO] = &mlx5e_tc_act_goto, - [FLOW_ACTION_REDIRECT] = &mlx5e_tc_act_mirred, + [FLOW_ACTION_REDIRECT] = &mlx5e_tc_act_redirect, [FLOW_ACTION_MIRRED] = &mlx5e_tc_act_mirred, [FLOW_ACTION_REDIRECT_INGRESS] = &mlx5e_tc_act_redirect_ingress, [FLOW_ACTION_VLAN_PUSH] = &mlx5e_tc_act_vlan, diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/act.h b/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/act.h index e1570ff056ae..8ede44902284 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/act.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/act.h @@ -60,6 +60,8 @@ struct mlx5e_tc_act { int (*stats_action)(struct mlx5e_priv *priv, struct flow_offload_action *fl_act); + + bool is_terminating_action; }; struct mlx5e_tc_flow_action { @@ -81,6 +83,7 @@ extern struct mlx5e_tc_act mlx5e_tc_act_vlan_mangle; extern struct mlx5e_tc_act mlx5e_tc_act_mpls_push; extern struct mlx5e_tc_act mlx5e_tc_act_mpls_pop; extern struct mlx5e_tc_act mlx5e_tc_act_mirred; +extern struct mlx5e_tc_act mlx5e_tc_act_redirect; extern struct mlx5e_tc_act mlx5e_tc_act_mirred_nic; extern struct mlx5e_tc_act mlx5e_tc_act_ct; extern struct mlx5e_tc_act mlx5e_tc_act_sample; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/drop.c b/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/drop.c index dd025a95c439..7d16aeabb119 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/drop.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/drop.c @@ -27,4 +27,5 @@ tc_act_parse_drop(struct mlx5e_tc_act_parse_state *parse_state, struct mlx5e_tc_act mlx5e_tc_act_drop = { .can_offload = tc_act_can_offload_drop, .parse_action = tc_act_parse_drop, + .is_terminating_action = true, }; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/goto.c b/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/goto.c index 25174f68613e..0923e6db2d0a 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/goto.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/goto.c @@ -121,4 +121,5 @@ struct mlx5e_tc_act mlx5e_tc_act_goto = { .can_offload = tc_act_can_offload_goto, .parse_action = tc_act_parse_goto, .post_parse = tc_act_post_parse_goto, + .is_terminating_action = true, }; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/mirred.c b/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/mirred.c index 4ac7de3f6afa..78c427b38048 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/mirred.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/mirred.c @@ -334,4 +334,11 @@ tc_act_parse_mirred(struct mlx5e_tc_act_parse_state *parse_state, struct mlx5e_tc_act mlx5e_tc_act_mirred = { .can_offload = tc_act_can_offload_mirred, .parse_action = tc_act_parse_mirred, + .is_terminating_action = false, +}; + +struct mlx5e_tc_act mlx5e_tc_act_redirect = { + .can_offload = tc_act_can_offload_mirred, + .parse_action = tc_act_parse_mirred, + .is_terminating_action = true, }; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/mirred_nic.c b/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/mirred_nic.c index 90b4c1b34776..7f409692b18f 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/mirred_nic.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/mirred_nic.c @@ -48,4 +48,5 @@ tc_act_parse_mirred_nic(struct mlx5e_tc_act_parse_state *parse_state, struct mlx5e_tc_act mlx5e_tc_act_mirred_nic = { .can_offload = tc_act_can_offload_mirred_nic, .parse_action = tc_act_parse_mirred_nic, + .is_terminating_action = true, }; From 6442638251f3679a05a75e69d5096680c14a210e Mon Sep 17 00:00:00 2001 From: Oz Shlomo Date: Sat, 3 Dec 2022 14:13:27 -0800 Subject: [PATCH 1750/2002] net/mlx5e: TC, validate action list per attribute Currently the entire flow action list is validate for offload limitations. For example, flow with both forward and drop actions are declared invalid due to hardware restrictions. However, a multi-table hardware model changes the limitations from a flow scope to a single flow attribute scope. Apply offload limitations to flow attributes instead of the entire flow. Signed-off-by: Oz Shlomo Reviewed-by: Roi Dayan Signed-off-by: Saeed Mahameed Link: https://lore.kernel.org/r/20221203221337.29267-6-saeed@kernel.org Signed-off-by: Jakub Kicinski --- .../net/ethernet/mellanox/mlx5/core/en_tc.c | 62 ++++++++++--------- 1 file changed, 32 insertions(+), 30 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c index 46222541e435..7eaf6c73b091 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c @@ -1724,6 +1724,30 @@ clean_encap_dests(struct mlx5e_priv *priv, } } +static int +verify_attr_actions(u32 actions, struct netlink_ext_ack *extack) +{ + if (!(actions & + (MLX5_FLOW_CONTEXT_ACTION_FWD_DEST | MLX5_FLOW_CONTEXT_ACTION_DROP))) { + NL_SET_ERR_MSG_MOD(extack, "Rule must have at least one forward/drop action"); + return -EOPNOTSUPP; + } + + if (!(~actions & + (MLX5_FLOW_CONTEXT_ACTION_FWD_DEST | MLX5_FLOW_CONTEXT_ACTION_DROP))) { + NL_SET_ERR_MSG_MOD(extack, "Rule cannot support forward+drop action"); + return -EOPNOTSUPP; + } + + if (actions & MLX5_FLOW_CONTEXT_ACTION_MOD_HDR && + actions & MLX5_FLOW_CONTEXT_ACTION_DROP) { + NL_SET_ERR_MSG_MOD(extack, "Drop with modify header action is not supported"); + return -EOPNOTSUPP; + } + + return 0; +} + static int post_process_attr(struct mlx5e_tc_flow *flow, struct mlx5_flow_attr *attr, @@ -1734,6 +1758,10 @@ post_process_attr(struct mlx5e_tc_flow *flow, bool vf_tun; int err = 0; + err = verify_attr_actions(attr->action, extack); + if (err) + goto err_out; + err = set_encap_dests(flow->priv, flow, attr, extack, &vf_tun); if (err) goto err_out; @@ -3532,36 +3560,6 @@ actions_match_supported(struct mlx5e_priv *priv, ct_clear = flow->attr->ct_attr.ct_action & TCA_CT_ACT_CLEAR; ct_flow = flow_flag_test(flow, CT) && !ct_clear; - if (!(actions & - (MLX5_FLOW_CONTEXT_ACTION_FWD_DEST | MLX5_FLOW_CONTEXT_ACTION_DROP))) { - NL_SET_ERR_MSG_MOD(extack, "Rule must have at least one forward/drop action"); - return false; - } - - if (!(~actions & - (MLX5_FLOW_CONTEXT_ACTION_FWD_DEST | MLX5_FLOW_CONTEXT_ACTION_DROP))) { - NL_SET_ERR_MSG_MOD(extack, "Rule cannot support forward+drop action"); - return false; - } - - if (actions & MLX5_FLOW_CONTEXT_ACTION_MOD_HDR && - actions & MLX5_FLOW_CONTEXT_ACTION_DROP) { - NL_SET_ERR_MSG_MOD(extack, "Drop with modify header action is not supported"); - return false; - } - - if (!(~actions & - (MLX5_FLOW_CONTEXT_ACTION_FWD_DEST | MLX5_FLOW_CONTEXT_ACTION_DROP))) { - NL_SET_ERR_MSG_MOD(extack, "Rule cannot support forward+drop action"); - return false; - } - - if (actions & MLX5_FLOW_CONTEXT_ACTION_MOD_HDR && - actions & MLX5_FLOW_CONTEXT_ACTION_DROP) { - NL_SET_ERR_MSG_MOD(extack, "Drop with modify header action is not supported"); - return false; - } - if (actions & MLX5_FLOW_CONTEXT_ACTION_MOD_HDR && !modify_header_match_supported(priv, &parse_attr->spec, flow_action, actions, ct_flow, ct_clear, extack)) @@ -3957,6 +3955,10 @@ parse_tc_nic_actions(struct mlx5e_priv *priv, if (err) return err; + err = verify_attr_actions(attr->action, extack); + if (err) + return err; + if (!actions_match_supported(priv, flow_action, parse_state->actions, parse_attr, flow, extack)) return -EOPNOTSUPP; From ec5878552b73cbb7e7d72f9a1a3d8a23a43a0bb2 Mon Sep 17 00:00:00 2001 From: Oz Shlomo Date: Sat, 3 Dec 2022 14:13:28 -0800 Subject: [PATCH 1751/2002] net/mlx5e: TC, set control params for branching actions Extend the act tc api to set the branch control params aligning with the police conform/exceed use case. Signed-off-by: Oz Shlomo Reviewed-by: Roi Dayan Signed-off-by: Saeed Mahameed Link: https://lore.kernel.org/r/20221203221337.29267-7-saeed@kernel.org Signed-off-by: Jakub Kicinski --- .../ethernet/mellanox/mlx5/core/en/tc/act/act.h | 9 +++++++++ .../ethernet/mellanox/mlx5/core/en/tc/act/police.c | 14 ++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/act.h b/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/act.h index 8ede44902284..8346557eeaf6 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/act.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/act.h @@ -32,6 +32,11 @@ struct mlx5e_tc_act_parse_state { struct mlx5_tc_ct_priv *ct_priv; }; +struct mlx5e_tc_act_branch_ctrl { + enum flow_action_id act_id; + u32 extval; +}; + struct mlx5e_tc_act { bool (*can_offload)(struct mlx5e_tc_act_parse_state *parse_state, const struct flow_action_entry *act, @@ -61,6 +66,10 @@ struct mlx5e_tc_act { int (*stats_action)(struct mlx5e_priv *priv, struct flow_offload_action *fl_act); + bool (*get_branch_ctrl)(const struct flow_action_entry *act, + struct mlx5e_tc_act_branch_ctrl *cond_true, + struct mlx5e_tc_act_branch_ctrl *cond_false); + bool is_terminating_action; }; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/police.c b/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/police.c index c8e5ca65bb6e..81aa4185c64a 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/police.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/police.c @@ -147,6 +147,19 @@ tc_act_police_stats(struct mlx5e_priv *priv, return 0; } +static bool +tc_act_police_get_branch_ctrl(const struct flow_action_entry *act, + struct mlx5e_tc_act_branch_ctrl *cond_true, + struct mlx5e_tc_act_branch_ctrl *cond_false) +{ + cond_true->act_id = act->police.notexceed.act_id; + cond_true->extval = act->police.notexceed.extval; + + cond_false->act_id = act->police.exceed.act_id; + cond_false->extval = act->police.exceed.extval; + return true; +} + struct mlx5e_tc_act mlx5e_tc_act_police = { .can_offload = tc_act_can_offload_police, .parse_action = tc_act_parse_police, @@ -154,4 +167,5 @@ struct mlx5e_tc_act mlx5e_tc_act_police = { .offload_action = tc_act_police_offload, .destroy_action = tc_act_police_destroy, .stats_action = tc_act_police_stats, + .get_branch_ctrl = tc_act_police_get_branch_ctrl, }; From f86488cb4685bbb885057afa0b61bb8afc1d6bb1 Mon Sep 17 00:00:00 2001 From: Oz Shlomo Date: Sat, 3 Dec 2022 14:13:29 -0800 Subject: [PATCH 1752/2002] net/mlx5e: TC, initialize branch flow attributes Initialize flow attribute for drop, accept, pipe and jump branching actions. Instantiate a flow attribute instance according to the specified branch control action. Store the branching attributes on the branching action flow attribute during the parsing phase. Then, during the offload phase, allocate the relevant mod header objects to the branching actions. Signed-off-by: Oz Shlomo Reviewed-by: Roi Dayan Signed-off-by: Saeed Mahameed Link: https://lore.kernel.org/r/20221203221337.29267-8-saeed@kernel.org Signed-off-by: Jakub Kicinski --- .../net/ethernet/mellanox/mlx5/core/en_tc.c | 156 ++++++++++++++++-- .../net/ethernet/mellanox/mlx5/core/en_tc.h | 2 + 2 files changed, 142 insertions(+), 16 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c index 7eaf6c73b091..6bd71a85d56d 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c @@ -160,6 +160,7 @@ static struct lock_class_key tc_ht_lock_key; static void mlx5e_put_flow_tunnel_id(struct mlx5e_tc_flow *flow); static void free_flow_post_acts(struct mlx5e_tc_flow *flow); +static void mlx5_free_flow_attr(struct mlx5e_tc_flow *flow, struct mlx5_flow_attr *attr); void mlx5e_tc_match_to_reg_match(struct mlx5_flow_spec *spec, @@ -1784,6 +1785,20 @@ post_process_attr(struct mlx5e_tc_flow *flow, } } + if (attr->branch_true && + attr->branch_true->action & MLX5_FLOW_CONTEXT_ACTION_MOD_HDR) { + err = mlx5e_tc_add_flow_mod_hdr(flow->priv, flow, attr->branch_true); + if (err) + goto err_out; + } + + if (attr->branch_false && + attr->branch_false->action & MLX5_FLOW_CONTEXT_ACTION_MOD_HDR) { + err = mlx5e_tc_add_flow_mod_hdr(flow->priv, flow, attr->branch_false); + if (err) + goto err_out; + } + if (attr->action & MLX5_FLOW_CONTEXT_ACTION_COUNT) { err = alloc_flow_attr_counter(get_flow_counter_dev(flow), attr); if (err) @@ -1932,6 +1947,16 @@ static bool mlx5_flow_has_geneve_opt(struct mlx5e_tc_flow *flow) return !!geneve_tlv_opt_0_data; } +static void free_branch_attr(struct mlx5e_tc_flow *flow, struct mlx5_flow_attr *attr) +{ + if (!attr) + return; + + mlx5_free_flow_attr(flow, attr); + kvfree(attr->parse_attr); + kfree(attr); +} + static void mlx5e_tc_del_fdb_flow(struct mlx5e_priv *priv, struct mlx5e_tc_flow *flow) { @@ -1987,6 +2012,8 @@ static void mlx5e_tc_del_fdb_flow(struct mlx5e_priv *priv, mlx5e_detach_decap(priv, flow); free_flow_post_acts(flow); + free_branch_attr(flow, attr->branch_true); + free_branch_attr(flow, attr->branch_false); if (flow->attr->lag.count) mlx5_lag_del_mpesw_rule(esw->dev); @@ -3659,6 +3686,8 @@ mlx5e_clone_flow_attr_for_post_act(struct mlx5_flow_attr *attr, attr2->esw_attr->split_count = 0; } + attr2->branch_true = NULL; + attr2->branch_false = NULL; return attr2; } @@ -3697,28 +3726,15 @@ mlx5e_tc_unoffload_flow_post_acts(struct mlx5e_tc_flow *flow) static void free_flow_post_acts(struct mlx5e_tc_flow *flow) { - struct mlx5_core_dev *counter_dev = get_flow_counter_dev(flow); - struct mlx5e_post_act *post_act = get_post_action(flow->priv); struct mlx5_flow_attr *attr, *tmp; - bool vf_tun; list_for_each_entry_safe(attr, tmp, &flow->attrs, list) { if (list_is_last(&attr->list, &flow->attrs)) break; - if (attr->post_act_handle) - mlx5e_tc_post_act_del(post_act, attr->post_act_handle); - - clean_encap_dests(flow->priv, flow, attr, &vf_tun); - - if (attr->action & MLX5_FLOW_CONTEXT_ACTION_COUNT) - mlx5_fc_destroy(counter_dev, attr->counter); - - if (attr->action & MLX5_FLOW_CONTEXT_ACTION_MOD_HDR) { - mlx5e_mod_hdr_dealloc(&attr->parse_attr->mod_hdr_acts); - if (attr->modify_hdr) - mlx5_modify_header_dealloc(flow->priv->mdev, attr->modify_hdr); - } + mlx5_free_flow_attr(flow, attr); + free_branch_attr(flow, attr->branch_true); + free_branch_attr(flow, attr->branch_false); list_del(&attr->list); kvfree(attr->parse_attr); @@ -3825,6 +3841,86 @@ out_free: return err; } +static int +alloc_branch_attr(struct mlx5e_tc_flow *flow, + struct mlx5e_tc_act_branch_ctrl *cond, + struct mlx5_flow_attr **cond_attr, + u32 *jump_count, + struct netlink_ext_ack *extack) +{ + struct mlx5_flow_attr *attr; + int err = 0; + + *cond_attr = mlx5e_clone_flow_attr_for_post_act(flow->attr, + mlx5e_get_flow_namespace(flow)); + if (!(*cond_attr)) + return -ENOMEM; + + attr = *cond_attr; + + switch (cond->act_id) { + case FLOW_ACTION_DROP: + attr->action |= MLX5_FLOW_CONTEXT_ACTION_DROP; + break; + case FLOW_ACTION_ACCEPT: + case FLOW_ACTION_PIPE: + attr->action |= MLX5_FLOW_CONTEXT_ACTION_FWD_DEST; + attr->dest_ft = mlx5e_tc_post_act_get_ft(get_post_action(flow->priv)); + break; + case FLOW_ACTION_JUMP: + if (*jump_count) { + NL_SET_ERR_MSG_MOD(extack, "Cannot offload flows with nested jumps"); + err = -EOPNOTSUPP; + goto out_err; + } + *jump_count = cond->extval; + attr->action |= MLX5_FLOW_CONTEXT_ACTION_FWD_DEST; + attr->dest_ft = mlx5e_tc_post_act_get_ft(get_post_action(flow->priv)); + break; + default: + err = -EOPNOTSUPP; + goto out_err; + } + + return err; +out_err: + kfree(*cond_attr); + *cond_attr = NULL; + return err; +} + +static int +parse_branch_ctrl(struct flow_action_entry *act, struct mlx5e_tc_act *tc_act, + struct mlx5e_tc_flow *flow, struct mlx5_flow_attr *attr, + struct netlink_ext_ack *extack) +{ + struct mlx5e_tc_act_branch_ctrl cond_true, cond_false; + u32 jump_count; + int err; + + if (!tc_act->get_branch_ctrl) + return 0; + + tc_act->get_branch_ctrl(act, &cond_true, &cond_false); + + err = alloc_branch_attr(flow, &cond_true, + &attr->branch_true, &jump_count, extack); + if (err) + goto out_err; + + err = alloc_branch_attr(flow, &cond_false, + &attr->branch_false, &jump_count, extack); + if (err) + goto err_branch_false; + + return 0; + +err_branch_false: + free_branch_attr(flow, attr->branch_true); +out_err: + return err; +} + static int parse_tc_actions(struct mlx5e_tc_act_parse_state *parse_state, struct flow_action *flow_action) @@ -3868,6 +3964,10 @@ parse_tc_actions(struct mlx5e_tc_act_parse_state *parse_state, if (err) goto out_free; + err = parse_branch_ctrl(act, tc_act, flow, attr, extack); + if (err) + goto out_free; + parse_state->actions |= attr->action; /* Split attr for multi table act if not the last act. */ @@ -4196,6 +4296,30 @@ mlx5_alloc_flow_attr(enum mlx5_flow_namespace_type type) return attr; } +static void +mlx5_free_flow_attr(struct mlx5e_tc_flow *flow, struct mlx5_flow_attr *attr) +{ + struct mlx5_core_dev *counter_dev = get_flow_counter_dev(flow); + bool vf_tun; + + if (!attr) + return; + + if (attr->post_act_handle) + mlx5e_tc_post_act_del(get_post_action(flow->priv), attr->post_act_handle); + + clean_encap_dests(flow->priv, flow, attr, &vf_tun); + + if (attr->action & MLX5_FLOW_CONTEXT_ACTION_COUNT) + mlx5_fc_destroy(counter_dev, attr->counter); + + if (attr->action & MLX5_FLOW_CONTEXT_ACTION_MOD_HDR) { + mlx5e_mod_hdr_dealloc(&attr->parse_attr->mod_hdr_acts); + if (attr->modify_hdr) + mlx5_modify_header_dealloc(flow->priv->mdev, attr->modify_hdr); + } +} + static int mlx5e_alloc_flow(struct mlx5e_priv *priv, int attr_size, struct flow_cls_offload *f, unsigned long flow_flags, diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.h b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.h index 0db41fa4a9a6..cee88f7dd50f 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.h @@ -95,6 +95,8 @@ struct mlx5_flow_attr { */ bool count; } lag; + struct mlx5_flow_attr *branch_true; + struct mlx5_flow_attr *branch_false; /* keep this union last */ union { DECLARE_FLEX_ARRAY(struct mlx5_esw_flow_attr, esw_attr); From c84fa1ab94a77c069503fb816c7aa310a6d10e31 Mon Sep 17 00:00:00 2001 From: Oz Shlomo Date: Sat, 3 Dec 2022 14:13:30 -0800 Subject: [PATCH 1753/2002] net/mlx5e: TC, initialize branching action with target attr Identify the jump target action when iterating the action list. Initialize the jump target attr with the jumping attribute during the parsing phase. Initialize the jumping attr post action with the target during the offload phase. Signed-off-by: Oz Shlomo Reviewed-by: Roi Dayan Signed-off-by: Saeed Mahameed Link: https://lore.kernel.org/r/20221203221337.29267-9-saeed@kernel.org Signed-off-by: Jakub Kicinski --- .../net/ethernet/mellanox/mlx5/core/en_tc.c | 86 +++++++++++++++++-- .../net/ethernet/mellanox/mlx5/core/en_tc.h | 2 + 2 files changed, 83 insertions(+), 5 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c index 6bd71a85d56d..d52f8601fef4 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c @@ -132,6 +132,15 @@ struct mlx5e_tc_attr_to_reg_mapping mlx5e_tc_attr_to_reg_mappings[] = { [PACKET_COLOR_TO_REG] = packet_color_to_reg, }; +struct mlx5e_tc_jump_state { + u32 jump_count; + bool jump_target; + struct mlx5_flow_attr *jumping_attr; + + enum flow_action_id last_id; + u32 last_index; +}; + struct mlx5e_tc_table *mlx5e_tc_table_alloc(void) { struct mlx5e_tc_table *tc; @@ -3688,6 +3697,7 @@ mlx5e_clone_flow_attr_for_post_act(struct mlx5_flow_attr *attr, attr2->branch_true = NULL; attr2->branch_false = NULL; + attr2->jumping_attr = NULL; return attr2; } @@ -3796,7 +3806,9 @@ alloc_flow_post_acts(struct mlx5e_tc_flow *flow, struct netlink_ext_ack *extack) if (!next_attr) { /* Set counter action on last post act rule. */ attr->action |= MLX5_FLOW_CONTEXT_ACTION_COUNT; - } else { + } + + if (next_attr && !(attr->flags & MLX5_ATTR_FLAG_TERMINATING)) { err = mlx5e_tc_act_set_next_post_act(flow, attr, next_attr); if (err) goto out_free; @@ -3823,6 +3835,13 @@ alloc_flow_post_acts(struct mlx5e_tc_flow *flow, struct netlink_ext_ack *extack) } attr->post_act_handle = handle; + + if (attr->jumping_attr) { + err = mlx5e_tc_act_set_next_post_act(flow, attr->jumping_attr, attr); + if (err) + goto out_free; + } + next_attr = attr; } @@ -3889,13 +3908,58 @@ out_err: return err; } +static void +dec_jump_count(struct flow_action_entry *act, struct mlx5e_tc_act *tc_act, + struct mlx5_flow_attr *attr, struct mlx5e_priv *priv, + struct mlx5e_tc_jump_state *jump_state) +{ + if (!jump_state->jump_count) + return; + + /* Single tc action can instantiate multiple offload actions (e.g. pedit) + * Jump only over a tc action + */ + if (act->id == jump_state->last_id && act->hw_index == jump_state->last_index) + return; + + jump_state->last_id = act->id; + jump_state->last_index = act->hw_index; + + /* nothing to do for intermediate actions */ + if (--jump_state->jump_count > 1) + return; + + if (jump_state->jump_count == 1) { /* last action in the jump action list */ + + /* create a new attribute after this action */ + jump_state->jump_target = true; + + if (tc_act->is_terminating_action) { /* the branch ends here */ + attr->flags |= MLX5_ATTR_FLAG_TERMINATING; + attr->action |= MLX5_FLOW_CONTEXT_ACTION_COUNT; + } else { /* the branch continues executing the rest of the actions */ + struct mlx5e_post_act *post_act; + + attr->action |= MLX5_FLOW_CONTEXT_ACTION_FWD_DEST; + post_act = get_post_action(priv); + attr->dest_ft = mlx5e_tc_post_act_get_ft(post_act); + } + } else if (jump_state->jump_count == 0) { /* first attr after the jump action list */ + /* This is the post action for the jumping attribute (either red or green) + * Use the stored jumping_attr to set the post act id on the jumping attribute + */ + attr->jumping_attr = jump_state->jumping_attr; + } +} + static int parse_branch_ctrl(struct flow_action_entry *act, struct mlx5e_tc_act *tc_act, struct mlx5e_tc_flow *flow, struct mlx5_flow_attr *attr, + struct mlx5e_tc_jump_state *jump_state, struct netlink_ext_ack *extack) { struct mlx5e_tc_act_branch_ctrl cond_true, cond_false; - u32 jump_count; + u32 jump_count = jump_state->jump_count; int err; if (!tc_act->get_branch_ctrl) @@ -3908,11 +3972,18 @@ parse_branch_ctrl(struct flow_action_entry *act, struct mlx5e_tc_act *tc_act, if (err) goto out_err; + if (jump_count) + jump_state->jumping_attr = attr->branch_true; + err = alloc_branch_attr(flow, &cond_false, &attr->branch_false, &jump_count, extack); if (err) goto err_branch_false; + if (jump_count && !jump_state->jumping_attr) + jump_state->jumping_attr = attr->branch_false; + + jump_state->jump_count = jump_count; return 0; err_branch_false: @@ -3928,6 +3999,7 @@ parse_tc_actions(struct mlx5e_tc_act_parse_state *parse_state, struct netlink_ext_ack *extack = parse_state->extack; struct mlx5e_tc_flow_action flow_action_reorder; struct mlx5e_tc_flow *flow = parse_state->flow; + struct mlx5e_tc_jump_state jump_state = {}; struct mlx5_flow_attr *attr = flow->attr; enum mlx5_flow_namespace_type ns_type; struct mlx5e_priv *priv = flow->priv; @@ -3947,6 +4019,7 @@ parse_tc_actions(struct mlx5e_tc_act_parse_state *parse_state, list_add(&attr->list, &flow->attrs); flow_action_for_each(i, _act, &flow_action_reorder) { + jump_state.jump_target = false; act = *_act; tc_act = mlx5e_tc_act_get(act->id, ns_type); if (!tc_act) { @@ -3964,16 +4037,19 @@ parse_tc_actions(struct mlx5e_tc_act_parse_state *parse_state, if (err) goto out_free; - err = parse_branch_ctrl(act, tc_act, flow, attr, extack); + dec_jump_count(act, tc_act, attr, priv, &jump_state); + + err = parse_branch_ctrl(act, tc_act, flow, attr, &jump_state, extack); if (err) goto out_free; parse_state->actions |= attr->action; /* Split attr for multi table act if not the last act. */ - if (tc_act->is_multi_table_act && + if (jump_state.jump_target || + (tc_act->is_multi_table_act && tc_act->is_multi_table_act(priv, act, attr) && - i < flow_action_reorder.num_entries - 1) { + i < flow_action_reorder.num_entries - 1)) { err = mlx5e_tc_act_post_parse(parse_state, flow_action, attr, ns_type); if (err) goto out_free; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.h b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.h index cee88f7dd50f..f2677d9ca0b4 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.h @@ -97,6 +97,7 @@ struct mlx5_flow_attr { } lag; struct mlx5_flow_attr *branch_true; struct mlx5_flow_attr *branch_false; + struct mlx5_flow_attr *jumping_attr; /* keep this union last */ union { DECLARE_FLEX_ARRAY(struct mlx5_esw_flow_attr, esw_attr); @@ -112,6 +113,7 @@ enum { MLX5_ATTR_FLAG_SAMPLE = BIT(4), MLX5_ATTR_FLAG_ACCEPT = BIT(5), MLX5_ATTR_FLAG_CT = BIT(6), + MLX5_ATTR_FLAG_TERMINATING = BIT(7), }; /* Returns true if any of the flags that require skipping further TC/NF processing are set. */ From 3fcb94e393576aa64315964b42eeb1db580957d3 Mon Sep 17 00:00:00 2001 From: Oz Shlomo Date: Sat, 3 Dec 2022 14:13:31 -0800 Subject: [PATCH 1754/2002] net/mlx5e: TC, rename post_meter actions Currently post meter supports only the pipe/drop conform-exceed policy. This assumption is reflected in several variable names. Rename the following variables as a pre-step for using the generalized branching action platform. Rename fwd_green_rule/drop_red_rule to green_rule/red_rule respectively. Repurpose red_counter/green_counter to act_counter/drop_counter to allow police conform-exceed configurations that do not drop. Signed-off-by: Oz Shlomo Reviewed-by: Roi Dayan Signed-off-by: Saeed Mahameed Link: https://lore.kernel.org/r/20221203221337.29267-10-saeed@kernel.org Signed-off-by: Jakub Kicinski --- .../ethernet/mellanox/mlx5/core/en/tc/meter.c | 24 +++++++-------- .../ethernet/mellanox/mlx5/core/en/tc/meter.h | 4 +-- .../mellanox/mlx5/core/en/tc/post_meter.c | 30 +++++++++---------- .../mellanox/mlx5/core/en/tc/post_meter.h | 4 +-- .../net/ethernet/mellanox/mlx5/core/en_tc.c | 4 +-- 5 files changed, 33 insertions(+), 33 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/tc/meter.c b/drivers/net/ethernet/mellanox/mlx5/core/en/tc/meter.c index be74e1403328..4e5f4aa44724 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/tc/meter.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/tc/meter.c @@ -257,16 +257,16 @@ __mlx5e_flow_meter_alloc(struct mlx5e_flow_meters *flow_meters) counter = mlx5_fc_create(mdev, true); if (IS_ERR(counter)) { err = PTR_ERR(counter); - goto err_red_counter; + goto err_drop_counter; } - meter->red_counter = counter; + meter->drop_counter = counter; counter = mlx5_fc_create(mdev, true); if (IS_ERR(counter)) { err = PTR_ERR(counter); - goto err_green_counter; + goto err_act_counter; } - meter->green_counter = counter; + meter->act_counter = counter; meters_obj = list_first_entry_or_null(&flow_meters->partial_list, struct mlx5e_flow_meter_aso_obj, @@ -313,10 +313,10 @@ __mlx5e_flow_meter_alloc(struct mlx5e_flow_meters *flow_meters) err_mem: mlx5e_flow_meter_destroy_aso_obj(mdev, id); err_create: - mlx5_fc_destroy(mdev, meter->green_counter); -err_green_counter: - mlx5_fc_destroy(mdev, meter->red_counter); -err_red_counter: + mlx5_fc_destroy(mdev, meter->act_counter); +err_act_counter: + mlx5_fc_destroy(mdev, meter->drop_counter); +err_drop_counter: kfree(meter); return ERR_PTR(err); } @@ -329,8 +329,8 @@ __mlx5e_flow_meter_free(struct mlx5e_flow_meter_handle *meter) struct mlx5e_flow_meter_aso_obj *meters_obj; int n, pos; - mlx5_fc_destroy(mdev, meter->green_counter); - mlx5_fc_destroy(mdev, meter->red_counter); + mlx5_fc_destroy(mdev, meter->act_counter); + mlx5_fc_destroy(mdev, meter->drop_counter); meters_obj = meter->meters_obj; pos = (meter->obj_id - meters_obj->base_id) * 2 + meter->idx; @@ -575,8 +575,8 @@ mlx5e_tc_meter_get_stats(struct mlx5e_flow_meter_handle *meter, u64 bytes1, packets1, lastuse1; u64 bytes2, packets2, lastuse2; - mlx5_fc_query_cached(meter->green_counter, &bytes1, &packets1, &lastuse1); - mlx5_fc_query_cached(meter->red_counter, &bytes2, &packets2, &lastuse2); + mlx5_fc_query_cached(meter->act_counter, &bytes1, &packets1, &lastuse1); + mlx5_fc_query_cached(meter->drop_counter, &bytes2, &packets2, &lastuse2); *bytes = bytes1 + bytes2; *packets = packets1 + packets2; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/tc/meter.h b/drivers/net/ethernet/mellanox/mlx5/core/en/tc/meter.h index 6de6e8a16327..f16abf33bb51 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/tc/meter.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/tc/meter.h @@ -32,8 +32,8 @@ struct mlx5e_flow_meter_handle { struct hlist_node hlist; struct mlx5e_flow_meter_params params; - struct mlx5_fc *green_counter; - struct mlx5_fc *red_counter; + struct mlx5_fc *act_counter; + struct mlx5_fc *drop_counter; }; struct mlx5e_meter_attr { diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/tc/post_meter.c b/drivers/net/ethernet/mellanox/mlx5/core/en/tc/post_meter.c index 8b77e822810e..60209205f683 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/tc/post_meter.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/tc/post_meter.c @@ -11,8 +11,8 @@ struct mlx5e_post_meter_priv { struct mlx5_flow_table *ft; struct mlx5_flow_group *fg; - struct mlx5_flow_handle *fwd_green_rule; - struct mlx5_flow_handle *drop_red_rule; + struct mlx5_flow_handle *green_rule; + struct mlx5_flow_handle *red_rule; }; struct mlx5_flow_table * @@ -85,8 +85,8 @@ static int mlx5e_post_meter_rules_create(struct mlx5e_priv *priv, struct mlx5e_post_meter_priv *post_meter, struct mlx5e_post_act *post_act, - struct mlx5_fc *green_counter, - struct mlx5_fc *red_counter) + struct mlx5_fc *act_counter, + struct mlx5_fc *drop_counter) { struct mlx5_flow_destination dest[2] = {}; struct mlx5_flow_act flow_act = {}; @@ -104,7 +104,7 @@ mlx5e_post_meter_rules_create(struct mlx5e_priv *priv, MLX5_FLOW_CONTEXT_ACTION_COUNT; flow_act.flags |= FLOW_ACT_IGNORE_FLOW_LEVEL; dest[0].type = MLX5_FLOW_DESTINATION_TYPE_COUNTER; - dest[0].counter_id = mlx5_fc_id(red_counter); + dest[0].counter_id = mlx5_fc_id(drop_counter); rule = mlx5_add_flow_rules(post_meter->ft, spec, &flow_act, dest, 1); if (IS_ERR(rule)) { @@ -112,7 +112,7 @@ mlx5e_post_meter_rules_create(struct mlx5e_priv *priv, err = PTR_ERR(rule); goto err_red; } - post_meter->drop_red_rule = rule; + post_meter->red_rule = rule; mlx5e_tc_match_to_reg_match(spec, PACKET_COLOR_TO_REG, MLX5_FLOW_METER_COLOR_GREEN, MLX5_PACKET_COLOR_MASK); @@ -121,7 +121,7 @@ mlx5e_post_meter_rules_create(struct mlx5e_priv *priv, dest[0].type = MLX5_FLOW_DESTINATION_TYPE_FLOW_TABLE; dest[0].ft = mlx5e_tc_post_act_get_ft(post_act); dest[1].type = MLX5_FLOW_DESTINATION_TYPE_COUNTER; - dest[1].counter_id = mlx5_fc_id(green_counter); + dest[1].counter_id = mlx5_fc_id(act_counter); rule = mlx5_add_flow_rules(post_meter->ft, spec, &flow_act, dest, 2); if (IS_ERR(rule)) { @@ -129,13 +129,13 @@ mlx5e_post_meter_rules_create(struct mlx5e_priv *priv, err = PTR_ERR(rule); goto err_green; } - post_meter->fwd_green_rule = rule; + post_meter->green_rule = rule; kvfree(spec); return 0; err_green: - mlx5_del_flow_rules(post_meter->drop_red_rule); + mlx5_del_flow_rules(post_meter->red_rule); err_red: kvfree(spec); return err; @@ -144,8 +144,8 @@ err_red: static void mlx5e_post_meter_rules_destroy(struct mlx5e_post_meter_priv *post_meter) { - mlx5_del_flow_rules(post_meter->drop_red_rule); - mlx5_del_flow_rules(post_meter->fwd_green_rule); + mlx5_del_flow_rules(post_meter->red_rule); + mlx5_del_flow_rules(post_meter->green_rule); } static void @@ -164,8 +164,8 @@ struct mlx5e_post_meter_priv * mlx5e_post_meter_init(struct mlx5e_priv *priv, enum mlx5_flow_namespace_type ns_type, struct mlx5e_post_act *post_act, - struct mlx5_fc *green_counter, - struct mlx5_fc *red_counter) + struct mlx5_fc *act_counter, + struct mlx5_fc *drop_counter) { struct mlx5e_post_meter_priv *post_meter; int err; @@ -182,8 +182,8 @@ mlx5e_post_meter_init(struct mlx5e_priv *priv, if (err) goto err_fg; - err = mlx5e_post_meter_rules_create(priv, post_meter, post_act, green_counter, - red_counter); + err = mlx5e_post_meter_rules_create(priv, post_meter, post_act, + act_counter, drop_counter); if (err) goto err_rules; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/tc/post_meter.h b/drivers/net/ethernet/mellanox/mlx5/core/en/tc/post_meter.h index 34d0e4b9fc7a..37c74e7bfb6a 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/tc/post_meter.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/tc/post_meter.h @@ -21,8 +21,8 @@ struct mlx5e_post_meter_priv * mlx5e_post_meter_init(struct mlx5e_priv *priv, enum mlx5_flow_namespace_type ns_type, struct mlx5e_post_act *post_act, - struct mlx5_fc *green_counter, - struct mlx5_fc *red_counter); + struct mlx5_fc *act_counter, + struct mlx5_fc *drop_counter); void mlx5e_post_meter_cleanup(struct mlx5e_post_meter_priv *post_meter); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c index d52f8601fef4..ce449fa4f36e 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c @@ -422,8 +422,8 @@ mlx5e_tc_add_flow_meter(struct mlx5e_priv *priv, } ns_type = mlx5e_tc_meter_get_namespace(meter->flow_meters); - post_meter = mlx5e_post_meter_init(priv, ns_type, post_act, meter->green_counter, - meter->red_counter); + post_meter = mlx5e_post_meter_init(priv, ns_type, post_act, meter->act_counter, + meter->drop_counter); if (IS_ERR(post_meter)) { mlx5_core_err(priv->mdev, "Failed to init post meter\n"); goto err_meter_init; From 0d8c38d44f338bbe5772532127fdbacc9f608ac8 Mon Sep 17 00:00:00 2001 From: Oz Shlomo Date: Sat, 3 Dec 2022 14:13:32 -0800 Subject: [PATCH 1755/2002] net/mlx5e: TC, init post meter rules with branching attributes Instantiate the post meter actions with the platform initialized branching action attributes. Signed-off-by: Oz Shlomo Reviewed-by: Roi Dayan Signed-off-by: Saeed Mahameed Link: https://lore.kernel.org/r/20221203221337.29267-11-saeed@kernel.org Signed-off-by: Jakub Kicinski --- .../mellanox/mlx5/core/en/tc/post_meter.c | 84 +++++++++++++------ .../mellanox/mlx5/core/en/tc/post_meter.h | 6 +- .../net/ethernet/mellanox/mlx5/core/en_tc.c | 11 +-- 3 files changed, 67 insertions(+), 34 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/tc/post_meter.c b/drivers/net/ethernet/mellanox/mlx5/core/en/tc/post_meter.c index 60209205f683..c38211097746 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/tc/post_meter.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/tc/post_meter.c @@ -12,7 +12,9 @@ struct mlx5e_post_meter_priv { struct mlx5_flow_table *ft; struct mlx5_flow_group *fg; struct mlx5_flow_handle *green_rule; + struct mlx5_flow_attr *green_attr; struct mlx5_flow_handle *red_rule; + struct mlx5_flow_attr *red_attr; }; struct mlx5_flow_table * @@ -81,15 +83,48 @@ mlx5e_post_meter_fg_create(struct mlx5e_priv *priv, return err; } +static struct mlx5_flow_handle * +mlx5e_post_meter_add_rule(struct mlx5e_priv *priv, + struct mlx5e_post_meter_priv *post_meter, + struct mlx5_flow_spec *spec, + struct mlx5_flow_attr *attr, + struct mlx5_fc *act_counter, + struct mlx5_fc *drop_counter) +{ + struct mlx5_eswitch *esw = priv->mdev->priv.eswitch; + struct mlx5_flow_handle *ret; + + attr->action |= MLX5_FLOW_CONTEXT_ACTION_COUNT; + if (attr->action & MLX5_FLOW_CONTEXT_ACTION_DROP) + attr->counter = drop_counter; + else + attr->counter = act_counter; + + attr->ft = post_meter->ft; + attr->flags |= MLX5_ATTR_FLAG_NO_IN_PORT; + attr->outer_match_level = MLX5_MATCH_NONE; + attr->chain = 0; + attr->prio = 0; + + ret = mlx5_eswitch_add_offloaded_rule(esw, spec, attr); + + /* We did not create the counter, so we can't delete it. + * Avoid freeing the counter when the attr is deleted in free_branching_attr + */ + attr->action &= ~MLX5_FLOW_CONTEXT_ACTION_COUNT; + + return ret; +} + static int mlx5e_post_meter_rules_create(struct mlx5e_priv *priv, struct mlx5e_post_meter_priv *post_meter, struct mlx5e_post_act *post_act, struct mlx5_fc *act_counter, - struct mlx5_fc *drop_counter) + struct mlx5_fc *drop_counter, + struct mlx5_flow_attr *green_attr, + struct mlx5_flow_attr *red_attr) { - struct mlx5_flow_destination dest[2] = {}; - struct mlx5_flow_act flow_act = {}; struct mlx5_flow_handle *rule; struct mlx5_flow_spec *spec; int err; @@ -100,36 +135,28 @@ mlx5e_post_meter_rules_create(struct mlx5e_priv *priv, mlx5e_tc_match_to_reg_match(spec, PACKET_COLOR_TO_REG, MLX5_FLOW_METER_COLOR_RED, MLX5_PACKET_COLOR_MASK); - flow_act.action = MLX5_FLOW_CONTEXT_ACTION_DROP | - MLX5_FLOW_CONTEXT_ACTION_COUNT; - flow_act.flags |= FLOW_ACT_IGNORE_FLOW_LEVEL; - dest[0].type = MLX5_FLOW_DESTINATION_TYPE_COUNTER; - dest[0].counter_id = mlx5_fc_id(drop_counter); - rule = mlx5_add_flow_rules(post_meter->ft, spec, &flow_act, dest, 1); + rule = mlx5e_post_meter_add_rule(priv, post_meter, spec, red_attr, + act_counter, drop_counter); if (IS_ERR(rule)) { - mlx5_core_warn(priv->mdev, "Failed to create post_meter flow drop rule\n"); + mlx5_core_warn(priv->mdev, "Failed to create post_meter exceed rule\n"); err = PTR_ERR(rule); goto err_red; } post_meter->red_rule = rule; + post_meter->red_attr = red_attr; mlx5e_tc_match_to_reg_match(spec, PACKET_COLOR_TO_REG, MLX5_FLOW_METER_COLOR_GREEN, MLX5_PACKET_COLOR_MASK); - flow_act.action = MLX5_FLOW_CONTEXT_ACTION_FWD_DEST | - MLX5_FLOW_CONTEXT_ACTION_COUNT; - dest[0].type = MLX5_FLOW_DESTINATION_TYPE_FLOW_TABLE; - dest[0].ft = mlx5e_tc_post_act_get_ft(post_act); - dest[1].type = MLX5_FLOW_DESTINATION_TYPE_COUNTER; - dest[1].counter_id = mlx5_fc_id(act_counter); - - rule = mlx5_add_flow_rules(post_meter->ft, spec, &flow_act, dest, 2); + rule = mlx5e_post_meter_add_rule(priv, post_meter, spec, green_attr, + act_counter, drop_counter); if (IS_ERR(rule)) { - mlx5_core_warn(priv->mdev, "Failed to create post_meter flow fwd rule\n"); + mlx5_core_warn(priv->mdev, "Failed to create post_meter notexceed rule\n"); err = PTR_ERR(rule); goto err_green; } post_meter->green_rule = rule; + post_meter->green_attr = green_attr; kvfree(spec); return 0; @@ -142,10 +169,11 @@ err_red: } static void -mlx5e_post_meter_rules_destroy(struct mlx5e_post_meter_priv *post_meter) +mlx5e_post_meter_rules_destroy(struct mlx5_eswitch *esw, + struct mlx5e_post_meter_priv *post_meter) { - mlx5_del_flow_rules(post_meter->red_rule); - mlx5_del_flow_rules(post_meter->green_rule); + mlx5_eswitch_del_offloaded_rule(esw, post_meter->red_rule, post_meter->red_attr); + mlx5_eswitch_del_offloaded_rule(esw, post_meter->green_rule, post_meter->green_attr); } static void @@ -165,7 +193,9 @@ mlx5e_post_meter_init(struct mlx5e_priv *priv, enum mlx5_flow_namespace_type ns_type, struct mlx5e_post_act *post_act, struct mlx5_fc *act_counter, - struct mlx5_fc *drop_counter) + struct mlx5_fc *drop_counter, + struct mlx5_flow_attr *branch_true, + struct mlx5_flow_attr *branch_false) { struct mlx5e_post_meter_priv *post_meter; int err; @@ -182,8 +212,8 @@ mlx5e_post_meter_init(struct mlx5e_priv *priv, if (err) goto err_fg; - err = mlx5e_post_meter_rules_create(priv, post_meter, post_act, - act_counter, drop_counter); + err = mlx5e_post_meter_rules_create(priv, post_meter, post_act, act_counter, + drop_counter, branch_true, branch_false); if (err) goto err_rules; @@ -199,9 +229,9 @@ err_ft: } void -mlx5e_post_meter_cleanup(struct mlx5e_post_meter_priv *post_meter) +mlx5e_post_meter_cleanup(struct mlx5_eswitch *esw, struct mlx5e_post_meter_priv *post_meter) { - mlx5e_post_meter_rules_destroy(post_meter); + mlx5e_post_meter_rules_destroy(esw, post_meter); mlx5e_post_meter_fg_destroy(post_meter); mlx5e_post_meter_table_destroy(post_meter); kfree(post_meter); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/tc/post_meter.h b/drivers/net/ethernet/mellanox/mlx5/core/en/tc/post_meter.h index 37c74e7bfb6a..a4075d33fde2 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/tc/post_meter.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/tc/post_meter.h @@ -22,8 +22,10 @@ mlx5e_post_meter_init(struct mlx5e_priv *priv, enum mlx5_flow_namespace_type ns_type, struct mlx5e_post_act *post_act, struct mlx5_fc *act_counter, - struct mlx5_fc *drop_counter); + struct mlx5_fc *drop_counter, + struct mlx5_flow_attr *branch_true, + struct mlx5_flow_attr *branch_false); void -mlx5e_post_meter_cleanup(struct mlx5e_post_meter_priv *post_meter); +mlx5e_post_meter_cleanup(struct mlx5_eswitch *esw, struct mlx5e_post_meter_priv *post_meter); #endif /* __MLX5_EN_POST_METER_H__ */ diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c index ce449fa4f36e..338e0b21d0b3 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c @@ -422,8 +422,9 @@ mlx5e_tc_add_flow_meter(struct mlx5e_priv *priv, } ns_type = mlx5e_tc_meter_get_namespace(meter->flow_meters); - post_meter = mlx5e_post_meter_init(priv, ns_type, post_act, meter->act_counter, - meter->drop_counter); + post_meter = mlx5e_post_meter_init(priv, ns_type, post_act, + meter->act_counter, meter->drop_counter, + attr->branch_true, attr->branch_false); if (IS_ERR(post_meter)) { mlx5_core_err(priv->mdev, "Failed to init post meter\n"); goto err_meter_init; @@ -442,9 +443,9 @@ err_meter_init: } static void -mlx5e_tc_del_flow_meter(struct mlx5_flow_attr *attr) +mlx5e_tc_del_flow_meter(struct mlx5_eswitch *esw, struct mlx5_flow_attr *attr) { - mlx5e_post_meter_cleanup(attr->meter_attr.post_meter); + mlx5e_post_meter_cleanup(esw, attr->meter_attr.post_meter); mlx5e_tc_meter_put(attr->meter_attr.meter); } @@ -505,7 +506,7 @@ mlx5e_tc_rule_unoffload(struct mlx5e_priv *priv, mlx5_eswitch_del_offloaded_rule(esw, rule, attr); if (attr->meter_attr.meter) - mlx5e_tc_del_flow_meter(attr); + mlx5e_tc_del_flow_meter(esw, attr); } int From 3603f26633e7624cb33f47fc92843db55c798097 Mon Sep 17 00:00:00 2001 From: Oz Shlomo Date: Sat, 3 Dec 2022 14:13:33 -0800 Subject: [PATCH 1756/2002] net/mlx5e: TC, allow meter jump control action Separate the matchall police action validation from flower validation. Isolate the action validation logic in the police action parser. Signed-off-by: Oz Shlomo Reviewed-by: Roi Dayan Signed-off-by: Saeed Mahameed Link: https://lore.kernel.org/r/20221203221337.29267-12-saeed@kernel.org Signed-off-by: Jakub Kicinski --- .../mellanox/mlx5/core/en/tc/act/police.c | 52 +++++++++++++++---- .../ethernet/mellanox/mlx5/core/en/tc_priv.h | 4 -- .../net/ethernet/mellanox/mlx5/core/en_tc.c | 21 ++++---- 3 files changed, 54 insertions(+), 23 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/police.c b/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/police.c index 81aa4185c64a..898fe16a4384 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/police.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/police.c @@ -4,20 +4,54 @@ #include "act.h" #include "en/tc_priv.h" +static bool police_act_validate_control(enum flow_action_id act_id, + struct netlink_ext_ack *extack) +{ + if (act_id != FLOW_ACTION_PIPE && + act_id != FLOW_ACTION_ACCEPT && + act_id != FLOW_ACTION_JUMP && + act_id != FLOW_ACTION_DROP) { + NL_SET_ERR_MSG_MOD(extack, + "Offload not supported when conform-exceed action is not pipe, ok, jump or drop"); + return false; + } + + return true; +} + +static int police_act_validate(const struct flow_action_entry *act, + struct netlink_ext_ack *extack) +{ + if (!police_act_validate_control(act->police.exceed.act_id, extack) || + !police_act_validate_control(act->police.notexceed.act_id, extack)) + return -EOPNOTSUPP; + + if (act->police.peakrate_bytes_ps || + act->police.avrate || act->police.overhead) { + NL_SET_ERR_MSG_MOD(extack, + "Offload not supported when peakrate/avrate/overhead is configured"); + return -EOPNOTSUPP; + } + + if (act->police.rate_pkt_ps) { + NL_SET_ERR_MSG_MOD(extack, + "QoS offload not support packets per second"); + return -EOPNOTSUPP; + } + + return 0; +} + static bool tc_act_can_offload_police(struct mlx5e_tc_act_parse_state *parse_state, const struct flow_action_entry *act, int act_index, struct mlx5_flow_attr *attr) { - if (act->police.notexceed.act_id != FLOW_ACTION_PIPE && - act->police.notexceed.act_id != FLOW_ACTION_ACCEPT) { - NL_SET_ERR_MSG_MOD(parse_state->extack, - "Offload not supported when conform action is not pipe or ok"); - return false; - } - if (mlx5e_policer_validate(parse_state->flow_action, act, - parse_state->extack)) + int err; + + err = police_act_validate(act, parse_state->extack); + if (err) return false; return !!mlx5e_get_flow_meters(parse_state->flow->priv->mdev); @@ -79,7 +113,7 @@ tc_act_police_offload(struct mlx5e_priv *priv, struct mlx5e_flow_meter_handle *meter; int err = 0; - err = mlx5e_policer_validate(&fl_act->action, act, fl_act->extack); + err = police_act_validate(act, fl_act->extack); if (err) return err; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/tc_priv.h b/drivers/net/ethernet/mellanox/mlx5/core/en/tc_priv.h index 2e42d7c5451e..2b7fd1c0e643 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/tc_priv.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/tc_priv.h @@ -211,8 +211,4 @@ struct mlx5e_flow_meters *mlx5e_get_flow_meters(struct mlx5_core_dev *dev); void *mlx5e_get_match_headers_value(u32 flags, struct mlx5_flow_spec *spec); void *mlx5e_get_match_headers_criteria(u32 flags, struct mlx5_flow_spec *spec); -int mlx5e_policer_validate(const struct flow_action *action, - const struct flow_action_entry *act, - struct netlink_ext_ack *extack); - #endif /* __MLX5_EN_TC_PRIV_H__ */ diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c index 338e0b21d0b3..227fa6ef9e41 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c @@ -4939,10 +4939,17 @@ static int apply_police_params(struct mlx5e_priv *priv, u64 rate, return err; } -int mlx5e_policer_validate(const struct flow_action *action, - const struct flow_action_entry *act, - struct netlink_ext_ack *extack) +static int +tc_matchall_police_validate(const struct flow_action *action, + const struct flow_action_entry *act, + struct netlink_ext_ack *extack) { + if (act->police.notexceed.act_id != FLOW_ACTION_CONTINUE) { + NL_SET_ERR_MSG_MOD(extack, + "Offload not supported when conform action is not continue"); + return -EOPNOTSUPP; + } + if (act->police.exceed.act_id != FLOW_ACTION_DROP) { NL_SET_ERR_MSG_MOD(extack, "Offload not supported when exceed action is not drop"); @@ -4993,13 +5000,7 @@ static int scan_tc_matchall_fdb_actions(struct mlx5e_priv *priv, flow_action_for_each(i, act, flow_action) { switch (act->id) { case FLOW_ACTION_POLICE: - if (act->police.notexceed.act_id != FLOW_ACTION_CONTINUE) { - NL_SET_ERR_MSG_MOD(extack, - "Offload not supported when conform action is not continue"); - return -EOPNOTSUPP; - } - - err = mlx5e_policer_validate(flow_action, act, extack); + err = tc_matchall_police_validate(flow_action, act, extack); if (err) return err; From 895fa59647cd64da99eebd1199cf27ecce08c17c Mon Sep 17 00:00:00 2001 From: Li Qiong Date: Tue, 6 Dec 2022 15:44:14 +0800 Subject: [PATCH 1757/2002] netfilter: flowtable: add a 'default' case to flowtable datapath Add a 'default' case in case return a uninitialized value of ret, this should not ever happen since the follow transmit path types: - FLOW_OFFLOAD_XMIT_UNSPEC - FLOW_OFFLOAD_XMIT_TC are never observed from this path. Add this check for safety reasons. Signed-off-by: Li Qiong Signed-off-by: Pablo Neira Ayuso --- net/netfilter/nf_flow_table_ip.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/net/netfilter/nf_flow_table_ip.c b/net/netfilter/nf_flow_table_ip.c index b350fe9d00b0..19efba1e51ef 100644 --- a/net/netfilter/nf_flow_table_ip.c +++ b/net/netfilter/nf_flow_table_ip.c @@ -421,6 +421,10 @@ nf_flow_offload_ip_hook(void *priv, struct sk_buff *skb, if (ret == NF_DROP) flow_offload_teardown(flow); break; + default: + WARN_ON_ONCE(1); + ret = NF_DROP; + break; } return ret; @@ -682,6 +686,10 @@ nf_flow_offload_ipv6_hook(void *priv, struct sk_buff *skb, if (ret == NF_DROP) flow_offload_teardown(flow); break; + default: + WARN_ON_ONCE(1); + ret = NF_DROP; + break; } return ret; From 3ad5c10bf21d1d69ac1117a89b3ce2271a832b34 Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Mon, 5 Dec 2022 11:52:47 -0800 Subject: [PATCH 1758/2002] ice: protect init and calibrating check in ice_ptp_request_ts When requesting a new timestamp, the ice_ptp_request_ts function does not hold the Tx tracker lock while checking init and calibrating. This means that we might issue a new timestamp request just after the Tx timestamp tracker starts being deinitialized. This could lead to incorrect access of the timestamp structures. Correct this by moving the init and calibrating checks under the lock, and updating the flows which modify these fields to use the lock. Note that we do not need to hold the lock while checking for tx->init in ice_ptp_tx_tstamp. This is because the teardown function will use synchronize_irq after clearing the flag to ensure that the threaded interrupt completes. Either a) the tx->init flag will be cleared before the ice_ptp_tx_tstamp function starts, thus it will exit immediately, or b) the threaded interrupt will be executing and the synchronize_irq will wait until the threaded interrupt has completed at which point we know the init field has definitely been set and new interrupts will not execute the Tx timestamp thread function. Signed-off-by: Jacob Keller Tested-by: Gurucharan G (A Contingent worker at Intel) Signed-off-by: Tony Nguyen --- drivers/net/ethernet/intel/ice/ice_ptp.c | 38 ++++++++++++++++++++---- drivers/net/ethernet/intel/ice/ice_ptp.h | 2 +- 2 files changed, 33 insertions(+), 7 deletions(-) diff --git a/drivers/net/ethernet/intel/ice/ice_ptp.c b/drivers/net/ethernet/intel/ice/ice_ptp.c index 0282ccc55819..481492d84e0e 100644 --- a/drivers/net/ethernet/intel/ice/ice_ptp.c +++ b/drivers/net/ethernet/intel/ice/ice_ptp.c @@ -599,6 +599,23 @@ static u64 ice_ptp_extend_40b_ts(struct ice_pf *pf, u64 in_tstamp) (in_tstamp >> 8) & mask); } +/** + * ice_ptp_is_tx_tracker_up - Check if Tx tracker is ready for new timestamps + * @tx: the PTP Tx timestamp tracker to check + * + * Check that a given PTP Tx timestamp tracker is up, i.e. that it is ready + * to accept new timestamp requests. + * + * Assumes the tx->lock spinlock is already held. + */ +static bool +ice_ptp_is_tx_tracker_up(struct ice_ptp_tx *tx) +{ + lockdep_assert_held(&tx->lock); + + return tx->init && !tx->calibrating; +} + /** * ice_ptp_tx_tstamp - Process Tx timestamps for a port * @tx: the PTP Tx timestamp tracker @@ -788,10 +805,10 @@ ice_ptp_alloc_tx_tracker(struct ice_ptp_tx *tx) return -ENOMEM; } - spin_lock_init(&tx->lock); - tx->init = 1; + spin_lock_init(&tx->lock); + return 0; } @@ -833,7 +850,9 @@ ice_ptp_flush_tx_tracker(struct ice_pf *pf, struct ice_ptp_tx *tx) static void ice_ptp_release_tx_tracker(struct ice_pf *pf, struct ice_ptp_tx *tx) { + spin_lock(&tx->lock); tx->init = 0; + spin_unlock(&tx->lock); /* wait for potentially outstanding interrupt to complete */ synchronize_irq(pf->msix_entries[pf->oicr_idx].vector); @@ -1327,7 +1346,9 @@ ice_ptp_port_phy_restart(struct ice_ptp_port *ptp_port) kthread_cancel_delayed_work_sync(&ptp_port->ov_work); /* temporarily disable Tx timestamps while calibrating PHY offset */ + spin_lock(&ptp_port->tx.lock); ptp_port->tx.calibrating = true; + spin_unlock(&ptp_port->tx.lock); ptp_port->tx_fifo_busy_cnt = 0; /* Start the PHY timer in Vernier mode */ @@ -1336,7 +1357,9 @@ ice_ptp_port_phy_restart(struct ice_ptp_port *ptp_port) goto out_unlock; /* Enable Tx timestamps right away */ + spin_lock(&ptp_port->tx.lock); ptp_port->tx.calibrating = false; + spin_unlock(&ptp_port->tx.lock); kthread_queue_delayed_work(pf->ptp.kworker, &ptp_port->ov_work, 0); @@ -2330,11 +2353,14 @@ s8 ice_ptp_request_ts(struct ice_ptp_tx *tx, struct sk_buff *skb) { u8 idx; - /* Check if this tracker is initialized */ - if (!tx->init || tx->calibrating) - return -1; - spin_lock(&tx->lock); + + /* Check that this tracker is accepting new timestamp requests */ + if (!ice_ptp_is_tx_tracker_up(tx)) { + spin_unlock(&tx->lock); + return -1; + } + /* Find and set the first available index */ idx = find_first_zero_bit(tx->in_use, tx->len); if (idx < tx->len) { diff --git a/drivers/net/ethernet/intel/ice/ice_ptp.h b/drivers/net/ethernet/intel/ice/ice_ptp.h index 5052fc41bed3..0bfafaaab6c7 100644 --- a/drivers/net/ethernet/intel/ice/ice_ptp.h +++ b/drivers/net/ethernet/intel/ice/ice_ptp.h @@ -110,7 +110,7 @@ struct ice_tx_tstamp { /** * struct ice_ptp_tx - Tracking structure for all Tx timestamp requests on a port - * @lock: lock to prevent concurrent write to in_use bitmap + * @lock: lock to prevent concurrent access to fields of this struct * @tstamps: array of len to store outstanding requests * @in_use: bitmap of len to indicate which slots are in use * @block: which memory block (quad or port) the timestamps are captured in From c1f3414df2e86e63d603712f81443af6cf07f8a3 Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Mon, 5 Dec 2022 11:52:49 -0800 Subject: [PATCH 1759/2002] ice: cleanup allocations in ice_ptp_alloc_tx_tracker The ice_ptp_alloc_tx_tracker function must allocate the timestamp array and the bitmap for tracking the currently in use indexes. A future change is going to add yet another allocation to this function. If these allocations fail we need to ensure that we properly cleanup and ensure that the pointers in the ice_ptp_tx structure are NULL. Simplify this logic by allocating to local variables first. If any allocation fails, then free everything and exit. Only update the ice_ptp_tx structure if all allocations succeed. This ensures that we have no side effects on the Tx structure unless all allocations have succeeded. Thus, no code will see an invalid pointer and we don't need to re-assign NULL on cleanup. This is safe because kernel "free" functions are designed to be NULL safe and perform no action if passed a NULL pointer. Thus its safe to simply always call kfree or bitmap_free even if one of those pointers was NULL. Signed-off-by: Jacob Keller Tested-by: Gurucharan G (A Contingent worker at Intel) Signed-off-by: Tony Nguyen --- drivers/net/ethernet/intel/ice/ice_ptp.c | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/drivers/net/ethernet/intel/ice/ice_ptp.c b/drivers/net/ethernet/intel/ice/ice_ptp.c index 481492d84e0e..6b49d9a5f538 100644 --- a/drivers/net/ethernet/intel/ice/ice_ptp.c +++ b/drivers/net/ethernet/intel/ice/ice_ptp.c @@ -794,17 +794,21 @@ skip_ts_read: static int ice_ptp_alloc_tx_tracker(struct ice_ptp_tx *tx) { - tx->tstamps = kcalloc(tx->len, sizeof(*tx->tstamps), GFP_KERNEL); - if (!tx->tstamps) - return -ENOMEM; + struct ice_tx_tstamp *tstamps; + unsigned long *in_use; + + tstamps = kcalloc(tx->len, sizeof(*tstamps), GFP_KERNEL); + in_use = bitmap_zalloc(tx->len, GFP_KERNEL); + + if (!tstamps || !in_use) { + kfree(tstamps); + bitmap_free(in_use); - tx->in_use = bitmap_zalloc(tx->len, GFP_KERNEL); - if (!tx->in_use) { - kfree(tx->tstamps); - tx->tstamps = NULL; return -ENOMEM; } + tx->tstamps = tstamps; + tx->in_use = in_use; tx->init = 1; spin_lock_init(&tx->lock); From d40fd60093325cd7b77bc8622cb9593cd27fdaa6 Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Mon, 5 Dec 2022 11:52:50 -0800 Subject: [PATCH 1760/2002] ice: handle flushing stale Tx timestamps in ice_ptp_tx_tstamp In the event of a PTP clock time change due to .adjtime or .settime, the ice driver needs to update the cached copy of the PHC time and also discard any outstanding Tx timestamps. This is required because otherwise the wrong copy of the PHC time will be used when extending the Tx timestamp. This could result in reporting incorrect timestamps to the stack. The current approach taken to handle this is to call ice_ptp_flush_tx_tracker, which will discard any timestamps which are not yet complete. This is problematic for two reasons: 1) it could lead to a potential race condition where the wrong timestamp is associated with a future packet. This can occur with the following flow: 1. Thread A gets request to transmit a timestamped packet, and picks an index and transmits the packet 2. Thread B calls ice_ptp_flush_tx_tracker and sees the index in use, marking is as disarded. No timestamp read occurs because the status bit is not set, but the index is released for re-use 3. Thread A gets a new request to transmit another timestamped packet, picks the same (now unused) index and transmits that packet. 4. The PHY transmits the first packet and updates the timestamp slot and generates an interrupt. 5. The ice_ptp_tx_tstamp thread executes and sees the interrupt and a valid timestamp but associates it with the new Tx SKB and not the one that actual timestamp for the packet as expected. This could result in the previous timestamp being assigned to a new packet producing incorrect timestamps and leading to incorrect behavior in PTP applications. This is most likely to occur when the packet rate for Tx timestamp requests is very high. 2) on E822 hardware, we must avoid reading a timestamp index more than once each time its status bit is set and an interrupt is generated by hardware. We do have some extensive checks for the unread flag to ensure that only one of either the ice_ptp_flush_tx_tracker or ice_ptp_tx_tstamp threads read the timestamp. However, even with this we can still have cases where we "flush" a timestamp that was actually completed in hardware. This can lead to cases where we don't read the timestamp index as appropriate. To fix both of these issues, we must avoid calling ice_ptp_flush_tx_tracker outside of the teardown path. Rather than using ice_ptp_flush_tx_tracker, introduce a new state bitmap, the stale bitmap. Start this as cleared when we begin a new timestamp request. When we're about to extend a timestamp and send it up to the stack, first check to see if that stale bit was set. If so, drop the timestamp without sending it to the stack. When we need to update the cached PHC timestamp out of band, just mark all currently outstanding timestamps as stale. This will ensure that once hardware completes the timestamp we'll ignore it correctly and avoid reporting bogus timestamps to userspace. With this change, we fix potential issues caused by calling ice_ptp_flush_tx_tracker during normal operation. Signed-off-by: Jacob Keller Tested-by: Gurucharan G (A Contingent worker at Intel) Signed-off-by: Tony Nguyen --- drivers/net/ethernet/intel/ice/ice_ptp.c | 99 +++++++++++++++--------- drivers/net/ethernet/intel/ice/ice_ptp.h | 2 + 2 files changed, 66 insertions(+), 35 deletions(-) diff --git a/drivers/net/ethernet/intel/ice/ice_ptp.c b/drivers/net/ethernet/intel/ice/ice_ptp.c index 6b49d9a5f538..f9cf9df334e8 100644 --- a/drivers/net/ethernet/intel/ice/ice_ptp.c +++ b/drivers/net/ethernet/intel/ice/ice_ptp.c @@ -625,11 +625,13 @@ ice_ptp_is_tx_tracker_up(struct ice_ptp_tx *tx) * * If a given index has a valid timestamp, perform the following steps: * - * 1) copy the timestamp out of the PHY register - * 4) clear the timestamp valid bit in the PHY register - * 5) unlock the index by clearing the associated in_use bit. - * 2) extend the 40b timestamp value to get a 64bit timestamp - * 3) send that timestamp to the stack + * 1) check that the timestamp request is not stale + * 2) check that a timestamp is ready and available in the PHY memory bank + * 3) read and copy the timestamp out of the PHY register + * 4) unlock the index by clearing the associated in_use bit + * 5) check if the timestamp is stale, and discard if so + * 6) extend the 40 bit timestamp value to get a 64 bit timestamp value + * 7) send this 64 bit timestamp to the stack * * Returns true if all timestamps were handled, and false if any slots remain * without a timestamp. @@ -640,16 +642,16 @@ ice_ptp_is_tx_tracker_up(struct ice_ptp_tx *tx) * interrupt. In some cases hardware might not interrupt us again when the * timestamp is captured. * - * Note that we only take the tracking lock when clearing the bit and when - * checking if we need to re-queue this task. The only place where bits can be - * set is the hard xmit routine where an SKB has a request flag set. The only - * places where we clear bits are this work function, or when flushing the Tx - * timestamp tracker. + * Note that we do not hold the tracking lock while reading the Tx timestamp. + * This is because reading the timestamp requires taking a mutex that might + * sleep. * - * If the Tx tracker gets flushed while we're processing a packet, we catch - * this because we grab the SKB pointer under lock. If the SKB is NULL we know - * that another thread already discarded the SKB and we can avoid passing it - * up to the stack. + * The only place where we set in_use is when a new timestamp is initiated + * with a slot index. This is only called in the hard xmit routine where an + * SKB has a request flag set. The only places where we clear this bit is this + * function, or during teardown when the Tx timestamp tracker is being + * removed. A timestamp index will never be re-used until the in_use bit for + * that index is cleared. * * If a Tx thread starts a new timestamp, we might not begin processing it * right away but we will notice it at the end when we re-queue the task. @@ -658,10 +660,11 @@ ice_ptp_is_tx_tracker_up(struct ice_ptp_tx *tx) * interrupt for that timestamp should re-trigger this function once * a timestamp is ready. * - * Note that minimizing the time we hold the lock is important. If we held the - * lock for the entire function we would unnecessarily block the Tx hot path - * which needs to set the timestamp index. Limiting how long we hold the lock - * ensures we do not block Tx threads. + * In cases where the PTP hardware clock was directly adjusted, some + * timestamps may not be able to safely use the timestamp extension math. In + * this case, software will set the stale bit for any outstanding Tx + * timestamps when the clock is adjusted. Then this function will discard + * those captured timestamps instead of sending them to the stack. * * If a Tx packet has been waiting for more than 2 seconds, it is not possible * to correctly extend the timestamp using the cached PHC time. It is @@ -750,6 +753,8 @@ skip_ts_read: clear_bit(idx, tx->in_use); skb = tx->tstamps[idx].skb; tx->tstamps[idx].skb = NULL; + if (test_and_clear_bit(idx, tx->stale)) + drop_ts = true; spin_unlock(&tx->lock); /* It is unlikely but possible that the SKB will have been @@ -794,21 +799,24 @@ skip_ts_read: static int ice_ptp_alloc_tx_tracker(struct ice_ptp_tx *tx) { + unsigned long *in_use, *stale; struct ice_tx_tstamp *tstamps; - unsigned long *in_use; tstamps = kcalloc(tx->len, sizeof(*tstamps), GFP_KERNEL); in_use = bitmap_zalloc(tx->len, GFP_KERNEL); + stale = bitmap_zalloc(tx->len, GFP_KERNEL); - if (!tstamps || !in_use) { + if (!tstamps || !in_use || !stale) { kfree(tstamps); bitmap_free(in_use); + bitmap_free(stale); return -ENOMEM; } tx->tstamps = tstamps; tx->in_use = in_use; + tx->stale = stale; tx->init = 1; spin_lock_init(&tx->lock); @@ -836,6 +844,7 @@ ice_ptp_flush_tx_tracker(struct ice_pf *pf, struct ice_ptp_tx *tx) pf->ptp.tx_hwtstamp_flushed++; } clear_bit(idx, tx->in_use); + clear_bit(idx, tx->stale); spin_unlock(&tx->lock); /* Clear any potential residual timestamp in the PHY block */ @@ -844,6 +853,25 @@ ice_ptp_flush_tx_tracker(struct ice_pf *pf, struct ice_ptp_tx *tx) } } +/** + * ice_ptp_mark_tx_tracker_stale - Mark unfinished timestamps as stale + * @tx: the tracker to mark + * + * Mark currently outstanding Tx timestamps as stale. This prevents sending + * their timestamp value to the stack. This is required to prevent extending + * the 40bit hardware timestamp incorrectly. + * + * This should be called when the PTP clock is modified such as after a set + * time request. + */ +static void +ice_ptp_mark_tx_tracker_stale(struct ice_ptp_tx *tx) +{ + spin_lock(&tx->lock); + bitmap_or(tx->stale, tx->stale, tx->in_use, tx->len); + spin_unlock(&tx->lock); +} + /** * ice_ptp_release_tx_tracker - Release allocated memory for Tx tracker * @pf: Board private structure @@ -869,6 +897,9 @@ ice_ptp_release_tx_tracker(struct ice_pf *pf, struct ice_ptp_tx *tx) bitmap_free(tx->in_use); tx->in_use = NULL; + bitmap_free(tx->stale); + tx->stale = NULL; + tx->len = 0; } @@ -987,20 +1018,13 @@ static int ice_ptp_update_cached_phctime(struct ice_pf *pf) * @pf: Board specific private structure * * This function must be called when the cached PHC time is no longer valid, - * such as after a time adjustment. It discards any outstanding Tx timestamps, - * and updates the cached PHC time for both the PF and Rx rings. If updating - * the PHC time cannot be done immediately, a warning message is logged and - * the work item is scheduled. + * such as after a time adjustment. It marks any currently outstanding Tx + * timestamps as stale and updates the cached PHC time for both the PF and Rx + * rings. * - * These steps are required in order to ensure that we do not accidentally - * report a timestamp extended by the wrong PHC cached copy. Note that we - * do not directly update the cached timestamp here because it is possible - * this might produce an error when ICE_CFG_BUSY is set. If this occurred, we - * would have to try again. During that time window, timestamps might be - * requested and returned with an invalid extension. Thus, on failure to - * immediately update the cached PHC time we would need to zero the value - * anyways. For this reason, we just zero the value immediately and queue the - * update work item. + * If updating the PHC time cannot be done immediately, a warning message is + * logged and the work item is scheduled immediately to minimize the window + * with a wrong cached timestamp. */ static void ice_ptp_reset_cached_phctime(struct ice_pf *pf) { @@ -1024,8 +1048,12 @@ static void ice_ptp_reset_cached_phctime(struct ice_pf *pf) msecs_to_jiffies(10)); } - /* Flush any outstanding Tx timestamps */ - ice_ptp_flush_tx_tracker(pf, &pf->ptp.port.tx); + /* Mark any outstanding timestamps as stale, since they might have + * been captured in hardware before the time update. This could lead + * to us extending them with the wrong cached value resulting in + * incorrect timestamp values. + */ + ice_ptp_mark_tx_tracker_stale(&pf->ptp.port.tx); } /** @@ -2373,6 +2401,7 @@ s8 ice_ptp_request_ts(struct ice_ptp_tx *tx, struct sk_buff *skb) * requests. */ set_bit(idx, tx->in_use); + clear_bit(idx, tx->stale); tx->tstamps[idx].start = jiffies; tx->tstamps[idx].skb = skb_get(skb); skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS; diff --git a/drivers/net/ethernet/intel/ice/ice_ptp.h b/drivers/net/ethernet/intel/ice/ice_ptp.h index 0bfafaaab6c7..9cda2f43e0e5 100644 --- a/drivers/net/ethernet/intel/ice/ice_ptp.h +++ b/drivers/net/ethernet/intel/ice/ice_ptp.h @@ -113,6 +113,7 @@ struct ice_tx_tstamp { * @lock: lock to prevent concurrent access to fields of this struct * @tstamps: array of len to store outstanding requests * @in_use: bitmap of len to indicate which slots are in use + * @stale: bitmap of len to indicate slots which have stale timestamps * @block: which memory block (quad or port) the timestamps are captured in * @offset: offset into timestamp block to get the real index * @len: length of the tstamps and in_use fields. @@ -125,6 +126,7 @@ struct ice_ptp_tx { spinlock_t lock; /* lock protecting in_use bitmap */ struct ice_tx_tstamp *tstamps; unsigned long *in_use; + unsigned long *stale; u8 block; u8 offset; u8 len; From e3ba5248669310537cac55d63dd3c4059b862e00 Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Mon, 5 Dec 2022 11:52:51 -0800 Subject: [PATCH 1761/2002] ice: only check set bits in ice_ptp_flush_tx_tracker The ice_ptp_flush_tx_tracker function is called to clear all outstanding Tx timestamp requests when the port is being brought down. This function iterates over the entire list, but this is unnecessary. We only need to check the bits which are actually set in the ready bitmap. Replace this logic with for_each_set_bit, and follow a similar flow as in ice_ptp_tx_tstamp_cleanup. Note that it is safe to call dev_kfree_skb_any on a NULL pointer as it will perform a no-op so we do not need to verify that the skb is actually NULL. The new implementation also avoids clearing (and thus reading!) the PHY timestamp unless the index is marked as having a valid timestamp in the timestamp status bitmap. This ensures that we properly clear the status registers as appropriate. Signed-off-by: Jacob Keller Tested-by: Gurucharan G (A Contingent worker at Intel) Signed-off-by: Tony Nguyen --- drivers/net/ethernet/intel/ice/ice_ptp.c | 38 ++++++++++++++++++------ 1 file changed, 29 insertions(+), 9 deletions(-) diff --git a/drivers/net/ethernet/intel/ice/ice_ptp.c b/drivers/net/ethernet/intel/ice/ice_ptp.c index f9cf9df334e8..c5de14abe7f4 100644 --- a/drivers/net/ethernet/intel/ice/ice_ptp.c +++ b/drivers/net/ethernet/intel/ice/ice_ptp.c @@ -828,28 +828,48 @@ ice_ptp_alloc_tx_tracker(struct ice_ptp_tx *tx) * ice_ptp_flush_tx_tracker - Flush any remaining timestamps from the tracker * @pf: Board private structure * @tx: the tracker to flush + * + * Called during teardown when a Tx tracker is being removed. */ static void ice_ptp_flush_tx_tracker(struct ice_pf *pf, struct ice_ptp_tx *tx) { + struct ice_hw *hw = &pf->hw; + u64 tstamp_ready; + int err; u8 idx; - for (idx = 0; idx < tx->len; idx++) { + err = ice_get_phy_tx_tstamp_ready(hw, tx->block, &tstamp_ready); + if (err) { + dev_dbg(ice_pf_to_dev(pf), "Failed to get the Tx tstamp ready bitmap for block %u, err %d\n", + tx->block, err); + + /* If we fail to read the Tx timestamp ready bitmap just + * skip clearing the PHY timestamps. + */ + tstamp_ready = 0; + } + + for_each_set_bit(idx, tx->in_use, tx->len) { u8 phy_idx = idx + tx->offset; + struct sk_buff *skb; + + /* In case this timestamp is ready, we need to clear it. */ + if (!hw->reset_ongoing && (tstamp_ready & BIT_ULL(phy_idx))) + ice_clear_phy_tstamp(hw, tx->block, phy_idx); spin_lock(&tx->lock); - if (tx->tstamps[idx].skb) { - dev_kfree_skb_any(tx->tstamps[idx].skb); - tx->tstamps[idx].skb = NULL; - pf->ptp.tx_hwtstamp_flushed++; - } + skb = tx->tstamps[idx].skb; + tx->tstamps[idx].skb = NULL; clear_bit(idx, tx->in_use); clear_bit(idx, tx->stale); spin_unlock(&tx->lock); - /* Clear any potential residual timestamp in the PHY block */ - if (!pf->hw.reset_ongoing) - ice_clear_phy_tstamp(&pf->hw, tx->block, phy_idx); + /* Count the number of Tx timestamps flushed */ + pf->ptp.tx_hwtstamp_flushed++; + + /* Free the SKB after we've cleared the bit */ + dev_kfree_skb_any(skb); } } From f029a34394e7239f4111507b402ab234f990ec94 Mon Sep 17 00:00:00 2001 From: Siddaraju DH Date: Mon, 5 Dec 2022 11:52:52 -0800 Subject: [PATCH 1762/2002] ice: make Tx and Rx vernier offset calibration independent The Tx and Rx calibration and timestamp generation blocks are independent. However, the ice driver waits until both blocks are ready before configuring either block. This can result in delay of configuring one block because we have not yet received a packet in the other block. There is no reason to wait to finish programming Tx just because we haven't received a packet. Similarly there is no reason to wait to program Rx just because we haven't transmitted a packet. Instead of checking both offset status before programming either block, refactor the ice_phy_cfg_tx_offset_e822 and ice_phy_cfg_rx_offset_e822 functions so that they perform their own offset status checks. Additionally, make them also check the offset ready bit to determine if the offset values have already been programmed. Call the individual configure functions directly in ice_ptp_wait_for_offset_valid. The functions will now correctly check status, and program the offsets if ready. Once the offset is programmed, the functions will exit quickly after just checking the offset ready register. Remove the ice_phy_calc_vernier_e822 in ice_ptp_hw.c, as well as the offset valid check functions in ice_ptp.c entirely as they are no longer necessary. With this change, the Tx and Rx blocks will each be enabled as soon as possible without waiting for the other block to complete calibration. This can enable timestamps faster in setups which have a low rate of transmitted or received packets. In particular, it can stop a situation where one port never receives traffic, and thus never finishes calibration of the Tx block, resulting in continuous faults reported by the ptp4l daemon application. Signed-off-by: Siddaraju DH Signed-off-by: Jacob Keller Tested-by: Gurucharan G (A Contingent worker at Intel) Signed-off-by: Tony Nguyen --- drivers/net/ethernet/intel/ice/ice_ptp.c | 128 ++++--------------- drivers/net/ethernet/intel/ice/ice_ptp_hw.c | 133 ++++++++++---------- drivers/net/ethernet/intel/ice/ice_ptp_hw.h | 3 +- 3 files changed, 91 insertions(+), 173 deletions(-) diff --git a/drivers/net/ethernet/intel/ice/ice_ptp.c b/drivers/net/ethernet/intel/ice/ice_ptp.c index c5de14abe7f4..7b5120906d3f 100644 --- a/drivers/net/ethernet/intel/ice/ice_ptp.c +++ b/drivers/net/ethernet/intel/ice/ice_ptp.c @@ -1214,132 +1214,46 @@ static int ice_ptp_check_tx_fifo(struct ice_ptp_port *port) } /** - * ice_ptp_check_tx_offset_valid - Check if the Tx PHY offset is valid - * @port: the PTP port to check - * - * Checks whether the Tx offset for the PHY associated with this port is - * valid. Returns 0 if the offset is valid, and a non-zero error code if it is - * not. - */ -static int ice_ptp_check_tx_offset_valid(struct ice_ptp_port *port) -{ - struct ice_pf *pf = ptp_port_to_pf(port); - struct device *dev = ice_pf_to_dev(pf); - struct ice_hw *hw = &pf->hw; - u32 val; - int err; - - err = ice_ptp_check_tx_fifo(port); - if (err) - return err; - - err = ice_read_phy_reg_e822(hw, port->port_num, P_REG_TX_OV_STATUS, - &val); - if (err) { - dev_err(dev, "Failed to read TX_OV_STATUS for port %d, err %d\n", - port->port_num, err); - return -EAGAIN; - } - - if (!(val & P_REG_TX_OV_STATUS_OV_M)) - return -EAGAIN; - - return 0; -} - -/** - * ice_ptp_check_rx_offset_valid - Check if the Rx PHY offset is valid - * @port: the PTP port to check - * - * Checks whether the Rx offset for the PHY associated with this port is - * valid. Returns 0 if the offset is valid, and a non-zero error code if it is - * not. - */ -static int ice_ptp_check_rx_offset_valid(struct ice_ptp_port *port) -{ - struct ice_pf *pf = ptp_port_to_pf(port); - struct device *dev = ice_pf_to_dev(pf); - struct ice_hw *hw = &pf->hw; - int err; - u32 val; - - err = ice_read_phy_reg_e822(hw, port->port_num, P_REG_RX_OV_STATUS, - &val); - if (err) { - dev_err(dev, "Failed to read RX_OV_STATUS for port %d, err %d\n", - port->port_num, err); - return err; - } - - if (!(val & P_REG_RX_OV_STATUS_OV_M)) - return -EAGAIN; - - return 0; -} - -/** - * ice_ptp_check_offset_valid - Check port offset valid bit - * @port: Port for which offset valid bit is checked - * - * Returns 0 if both Tx and Rx offset are valid, and -EAGAIN if one of the - * offset is not ready. - */ -static int ice_ptp_check_offset_valid(struct ice_ptp_port *port) -{ - int tx_err, rx_err; - - /* always check both Tx and Rx offset validity */ - tx_err = ice_ptp_check_tx_offset_valid(port); - rx_err = ice_ptp_check_rx_offset_valid(port); - - if (tx_err || rx_err) - return -EAGAIN; - - return 0; -} - -/** - * ice_ptp_wait_for_offset_valid - Check for valid Tx and Rx offsets + * ice_ptp_wait_for_offsets - Check for valid Tx and Rx offsets * @work: Pointer to the kthread_work structure for this task * - * Check whether both the Tx and Rx offsets are valid for enabling the vernier - * calibration. + * Check whether hardware has completed measuring the Tx and Rx offset values + * used to configure and enable vernier timestamp calibration. * - * Once we have valid offsets from hardware, update the total Tx and Rx - * offsets, and exit bypass mode. This enables more precise timestamps using - * the extra data measured during the vernier calibration process. + * Once the offset in either direction is measured, configure the associated + * registers with the calibrated offset values and enable timestamping. The Tx + * and Rx directions are configured independently as soon as their associated + * offsets are known. + * + * This function reschedules itself until both Tx and Rx calibration have + * completed. */ -static void ice_ptp_wait_for_offset_valid(struct kthread_work *work) +static void ice_ptp_wait_for_offsets(struct kthread_work *work) { struct ice_ptp_port *port; - int err; - struct device *dev; struct ice_pf *pf; struct ice_hw *hw; + int tx_err; + int rx_err; port = container_of(work, struct ice_ptp_port, ov_work.work); pf = ptp_port_to_pf(port); hw = &pf->hw; - dev = ice_pf_to_dev(pf); if (ice_is_reset_in_progress(pf->state)) return; - if (ice_ptp_check_offset_valid(port)) { - /* Offsets not ready yet, try again later */ + tx_err = ice_ptp_check_tx_fifo(port); + if (!tx_err) + tx_err = ice_phy_cfg_tx_offset_e822(hw, port->port_num); + rx_err = ice_phy_cfg_rx_offset_e822(hw, port->port_num); + if (tx_err || rx_err) { + /* Tx and/or Rx offset not yet configured, try again later */ kthread_queue_delayed_work(pf->ptp.kworker, &port->ov_work, msecs_to_jiffies(100)); return; } - - /* Offsets are valid, so Vernier mode calculations are started */ - err = ice_phy_calc_vernier_e822(hw, port->port_num); - if (err) { - dev_warn(dev, "Failed to prepare Vernier mode for PHY port %u, err %d\n", - port->port_num, err); - return; - } } /** @@ -2537,7 +2451,7 @@ pfr: err = ice_ptp_init_tx_e810(pf, &ptp->port.tx); } else { kthread_init_delayed_work(&ptp->port.ov_work, - ice_ptp_wait_for_offset_valid); + ice_ptp_wait_for_offsets); err = ice_ptp_init_tx_e822(pf, &ptp->port.tx, ptp->port.port_num); } @@ -2700,7 +2614,7 @@ static int ice_ptp_init_port(struct ice_pf *pf, struct ice_ptp_port *ptp_port) return ice_ptp_init_tx_e810(pf, &ptp_port->tx); kthread_init_delayed_work(&ptp_port->ov_work, - ice_ptp_wait_for_offset_valid); + ice_ptp_wait_for_offsets); return ice_ptp_init_tx_e822(pf, &ptp_port->tx, ptp_port->port_num); } diff --git a/drivers/net/ethernet/intel/ice/ice_ptp_hw.c b/drivers/net/ethernet/intel/ice/ice_ptp_hw.c index d5d51427580a..a38614d21ea8 100644 --- a/drivers/net/ethernet/intel/ice/ice_ptp_hw.c +++ b/drivers/net/ethernet/intel/ice/ice_ptp_hw.c @@ -1741,21 +1741,48 @@ ice_calc_fixed_tx_offset_e822(struct ice_hw *hw, enum ice_ptp_link_spd link_spd) * adjust Tx timestamps by. This is calculated by combining some known static * latency along with the Vernier offset computations done by hardware. * - * This function must be called only after the offset registers are valid, - * i.e. after the Vernier calibration wait has passed, to ensure that the PHY - * has measured the offset. + * This function will not return successfully until the Tx offset calculations + * have been completed, which requires waiting until at least one packet has + * been transmitted by the device. It is safe to call this function + * periodically until calibration succeeds, as it will only program the offset + * once. * * To avoid overflow, when calculating the offset based on the known static * latency values, we use measurements in 1/100th of a nanosecond, and divide * the TUs per second up front. This avoids overflow while allowing * calculation of the adjustment using integer arithmetic. + * + * Returns zero on success, -EBUSY if the hardware vernier offset + * calibration has not completed, or another error code on failure. */ -static int ice_phy_cfg_tx_offset_e822(struct ice_hw *hw, u8 port) +int ice_phy_cfg_tx_offset_e822(struct ice_hw *hw, u8 port) { enum ice_ptp_link_spd link_spd; enum ice_ptp_fec_mode fec_mode; u64 total_offset, val; int err; + u32 reg; + + /* Nothing to do if we've already programmed the offset */ + err = ice_read_phy_reg_e822(hw, port, P_REG_TX_OR, ®); + if (err) { + ice_debug(hw, ICE_DBG_PTP, "Failed to read TX_OR for port %u, err %d\n", + port, err); + return err; + } + + if (reg) + return 0; + + err = ice_read_phy_reg_e822(hw, port, P_REG_TX_OV_STATUS, ®); + if (err) { + ice_debug(hw, ICE_DBG_PTP, "Failed to read TX_OV_STATUS for port %u, err %d\n", + port, err); + return err; + } + + if (!(reg & P_REG_TX_OV_STATUS_OV_M)) + return -EBUSY; err = ice_phy_get_speed_and_fec_e822(hw, port, &link_spd, &fec_mode); if (err) @@ -1809,6 +1836,9 @@ static int ice_phy_cfg_tx_offset_e822(struct ice_hw *hw, u8 port) if (err) return err; + dev_info(ice_hw_to_dev(hw), "Port=%d Tx vernier offset calibration complete\n", + port); + return 0; } @@ -2011,6 +2041,11 @@ ice_calc_fixed_rx_offset_e822(struct ice_hw *hw, enum ice_ptp_link_spd link_spd) * measurements taken in hardware with some data about known fixed delay as * well as adjusting for multi-lane alignment delay. * + * This function will not return successfully until the Rx offset calculations + * have been completed, which requires waiting until at least one packet has + * been received by the device. It is safe to call this function periodically + * until calibration succeeds, as it will only program the offset once. + * * This function must be called only after the offset registers are valid, * i.e. after the Vernier calibration wait has passed, to ensure that the PHY * has measured the offset. @@ -2019,13 +2054,38 @@ ice_calc_fixed_rx_offset_e822(struct ice_hw *hw, enum ice_ptp_link_spd link_spd) * latency values, we use measurements in 1/100th of a nanosecond, and divide * the TUs per second up front. This avoids overflow while allowing * calculation of the adjustment using integer arithmetic. + * + * Returns zero on success, -EBUSY if the hardware vernier offset + * calibration has not completed, or another error code on failure. */ -static int ice_phy_cfg_rx_offset_e822(struct ice_hw *hw, u8 port) +int ice_phy_cfg_rx_offset_e822(struct ice_hw *hw, u8 port) { enum ice_ptp_link_spd link_spd; enum ice_ptp_fec_mode fec_mode; u64 total_offset, pmd, val; int err; + u32 reg; + + /* Nothing to do if we've already programmed the offset */ + err = ice_read_phy_reg_e822(hw, port, P_REG_RX_OR, ®); + if (err) { + ice_debug(hw, ICE_DBG_PTP, "Failed to read RX_OR for port %u, err %d\n", + port, err); + return err; + } + + if (reg) + return 0; + + err = ice_read_phy_reg_e822(hw, port, P_REG_RX_OV_STATUS, ®); + if (err) { + ice_debug(hw, ICE_DBG_PTP, "Failed to read RX_OV_STATUS for port %u, err %d\n", + port, err); + return err; + } + + if (!(reg & P_REG_RX_OV_STATUS_OV_M)) + return -EBUSY; err = ice_phy_get_speed_and_fec_e822(hw, port, &link_spd, &fec_mode); if (err) @@ -2086,6 +2146,9 @@ static int ice_phy_cfg_rx_offset_e822(struct ice_hw *hw, u8 port) if (err) return err; + dev_info(ice_hw_to_dev(hw), "Port=%d Rx vernier offset calibration complete\n", + port); + return 0; } @@ -2357,66 +2420,6 @@ int ice_start_phy_timer_e822(struct ice_hw *hw, u8 port) return 0; } -/** - * ice_phy_calc_vernier_e822 - Perform vernier calculations - * @hw: pointer to the HW struct - * @port: the PHY port to configure - * - * Perform vernier calculations for the Tx and Rx offset. This will enable - * hardware to include the more precise offset calibrations, - * increasing precision of the generated timestamps. - * - * This cannot be done until hardware has measured the offsets, which requires - * waiting until at least one packet has been sent and received by the device. - */ -int ice_phy_calc_vernier_e822(struct ice_hw *hw, u8 port) -{ - int err; - u32 val; - - err = ice_read_phy_reg_e822(hw, port, P_REG_TX_OV_STATUS, &val); - if (err) { - ice_debug(hw, ICE_DBG_PTP, "Failed to read TX_OV_STATUS for port %u, err %d\n", - port, err); - return err; - } - - if (!(val & P_REG_TX_OV_STATUS_OV_M)) { - ice_debug(hw, ICE_DBG_PTP, "Tx offset is not yet valid for port %u\n", - port); - return -EBUSY; - } - - err = ice_read_phy_reg_e822(hw, port, P_REG_RX_OV_STATUS, &val); - if (err) { - ice_debug(hw, ICE_DBG_PTP, "Failed to read RX_OV_STATUS for port %u, err %d\n", - port, err); - return err; - } - - if (!(val & P_REG_TX_OV_STATUS_OV_M)) { - ice_debug(hw, ICE_DBG_PTP, "Rx offset is not yet valid for port %u\n", - port); - return -EBUSY; - } - - err = ice_phy_cfg_tx_offset_e822(hw, port); - if (err) { - ice_debug(hw, ICE_DBG_PTP, "Failed to program total Tx offset for port %u, err %d\n", - port, err); - return err; - } - - err = ice_phy_cfg_rx_offset_e822(hw, port); - if (err) { - ice_debug(hw, ICE_DBG_PTP, "Failed to program total Rx offset for port %u, err %d\n", - port, err); - return err; - } - - return 0; -} - /** * ice_get_phy_tx_tstamp_ready_e822 - Read Tx memory status register * @hw: pointer to the HW struct diff --git a/drivers/net/ethernet/intel/ice/ice_ptp_hw.h b/drivers/net/ethernet/intel/ice/ice_ptp_hw.h index b781dadf5a39..3b68cb91bd81 100644 --- a/drivers/net/ethernet/intel/ice/ice_ptp_hw.h +++ b/drivers/net/ethernet/intel/ice/ice_ptp_hw.h @@ -188,7 +188,8 @@ static inline u64 ice_e822_pps_delay(enum ice_time_ref_freq time_ref) /* E822 Vernier calibration functions */ int ice_stop_phy_timer_e822(struct ice_hw *hw, u8 port, bool soft_reset); int ice_start_phy_timer_e822(struct ice_hw *hw, u8 port); -int ice_phy_calc_vernier_e822(struct ice_hw *hw, u8 port); +int ice_phy_cfg_tx_offset_e822(struct ice_hw *hw, u8 port); +int ice_phy_cfg_rx_offset_e822(struct ice_hw *hw, u8 port); /* E810 family functions */ int ice_ptp_init_phy_e810(struct ice_hw *hw); From 95af1f1c4c9f9f08bbdb40812248b7bc5868a1da Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Mon, 5 Dec 2022 11:52:53 -0800 Subject: [PATCH 1763/2002] ice: reschedule ice_ptp_wait_for_offset_valid during reset If the ice_ptp_wait_for_offest_valid function is scheduled to run while the driver is resetting, it will exit without completing calibration. The work function gets scheduled by ice_ptp_port_phy_restart which will be called as part of the reset recovery process. It is possible for the first execution to occur before the driver has completely cleared its resetting flags. Ensure calibration completes by rescheduling the task until reset is fully completed. Reported-by: Siddaraju DH Signed-off-by: Jacob Keller Tested-by: Gurucharan G (A Contingent worker at Intel) Signed-off-by: Tony Nguyen --- drivers/net/ethernet/intel/ice/ice_ptp.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/intel/ice/ice_ptp.c b/drivers/net/ethernet/intel/ice/ice_ptp.c index 7b5120906d3f..d63161d73eb1 100644 --- a/drivers/net/ethernet/intel/ice/ice_ptp.c +++ b/drivers/net/ethernet/intel/ice/ice_ptp.c @@ -1240,8 +1240,13 @@ static void ice_ptp_wait_for_offsets(struct kthread_work *work) pf = ptp_port_to_pf(port); hw = &pf->hw; - if (ice_is_reset_in_progress(pf->state)) + if (ice_is_reset_in_progress(pf->state)) { + /* wait for device driver to complete reset */ + kthread_queue_delayed_work(pf->ptp.kworker, + &port->ov_work, + msecs_to_jiffies(100)); return; + } tx_err = ice_ptp_check_tx_fifo(port); if (!tx_err) From 35f31ff0c0b62b9e864fdb92bc1af9212818d624 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Wed, 7 Dec 2022 14:12:35 +0000 Subject: [PATCH 1764/2002] net/mlx4: rename two constants MAX_DESC_SIZE is really the size of the bounce buffer used when reaching the right side of TX ring buffer. MAX_DESC_TXBBS get a MLX4_ prefix. Signed-off-by: Eric Dumazet Reviewed-by: Tariq Toukan Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/mellanox/mlx4/en_tx.c | 10 ++++++---- drivers/net/ethernet/mellanox/mlx4/mlx4_en.h | 4 ++-- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx4/en_tx.c b/drivers/net/ethernet/mellanox/mlx4/en_tx.c index 43a4102e9c09..8372aeb392a2 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_tx.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_tx.c @@ -65,7 +65,7 @@ int mlx4_en_create_tx_ring(struct mlx4_en_priv *priv, ring->size = size; ring->size_mask = size - 1; ring->sp_stride = stride; - ring->full_size = ring->size - HEADROOM - MAX_DESC_TXBBS; + ring->full_size = ring->size - HEADROOM - MLX4_MAX_DESC_TXBBS; tmp = size * sizeof(struct mlx4_en_tx_info); ring->tx_info = kvmalloc_node(tmp, GFP_KERNEL, node); @@ -77,9 +77,11 @@ int mlx4_en_create_tx_ring(struct mlx4_en_priv *priv, en_dbg(DRV, priv, "Allocated tx_info ring at addr:%p size:%d\n", ring->tx_info, tmp); - ring->bounce_buf = kmalloc_node(MAX_DESC_SIZE, GFP_KERNEL, node); + ring->bounce_buf = kmalloc_node(MLX4_TX_BOUNCE_BUFFER_SIZE, + GFP_KERNEL, node); if (!ring->bounce_buf) { - ring->bounce_buf = kmalloc(MAX_DESC_SIZE, GFP_KERNEL); + ring->bounce_buf = kmalloc(MLX4_TX_BOUNCE_BUFFER_SIZE, + GFP_KERNEL); if (!ring->bounce_buf) { err = -ENOMEM; goto err_info; @@ -909,7 +911,7 @@ netdev_tx_t mlx4_en_xmit(struct sk_buff *skb, struct net_device *dev) /* Align descriptor to TXBB size */ desc_size = ALIGN(real_size, TXBB_SIZE); nr_txbb = desc_size >> LOG_TXBB_SIZE; - if (unlikely(nr_txbb > MAX_DESC_TXBBS)) { + if (unlikely(nr_txbb > MLX4_MAX_DESC_TXBBS)) { if (netif_msg_tx_err(priv)) en_warn(priv, "Oversized header or SG list\n"); goto tx_drop_count; diff --git a/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h b/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h index e132ff4c82f2..7cc288db2a64 100644 --- a/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h +++ b/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h @@ -90,8 +90,8 @@ #define MLX4_EN_FILTER_EXPIRY_QUOTA 60 /* Typical TSO descriptor with 16 gather entries is 352 bytes... */ -#define MAX_DESC_SIZE 512 -#define MAX_DESC_TXBBS (MAX_DESC_SIZE / TXBB_SIZE) +#define MLX4_TX_BOUNCE_BUFFER_SIZE 512 +#define MLX4_MAX_DESC_TXBBS (MLX4_TX_BOUNCE_BUFFER_SIZE / TXBB_SIZE) /* * OS related constants and tunables From 26782aad00ccb8f8e5ae6221358fa79cdafc8548 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Wed, 7 Dec 2022 14:12:36 +0000 Subject: [PATCH 1765/2002] net/mlx4: MLX4_TX_BOUNCE_BUFFER_SIZE depends on MAX_SKB_FRAGS Google production kernel has increased MAX_SKB_FRAGS to 45 for BIG-TCP rollout. Unfortunately mlx4 TX bounce buffer is not big enough whenever an skb has up to 45 page fragments. This can happen often with TCP TX zero copy, as one frag usually holds 4096 bytes of payload (order-0 page). Tested: Kernel built with MAX_SKB_FRAGS=45 ip link set dev eth0 gso_max_size 185000 netperf -t TCP_SENDFILE I made sure that "ethtool -G eth0 tx 64" was properly working, ring->full_size being set to 15. Signed-off-by: Eric Dumazet Reported-by: Wei Wang Reviewed-by: Tariq Toukan Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/mellanox/mlx4/mlx4_en.h | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h b/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h index 7cc288db2a64..3d4226ddba5e 100644 --- a/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h +++ b/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h @@ -89,8 +89,18 @@ #define MLX4_EN_FILTER_HASH_SHIFT 4 #define MLX4_EN_FILTER_EXPIRY_QUOTA 60 -/* Typical TSO descriptor with 16 gather entries is 352 bytes... */ -#define MLX4_TX_BOUNCE_BUFFER_SIZE 512 +#define CTRL_SIZE sizeof(struct mlx4_wqe_ctrl_seg) +#define DS_SIZE sizeof(struct mlx4_wqe_data_seg) + +/* Maximal size of the bounce buffer: + * 256 bytes for LSO headers. + * CTRL_SIZE for control desc. + * DS_SIZE if skb->head contains some payload. + * MAX_SKB_FRAGS frags. + */ +#define MLX4_TX_BOUNCE_BUFFER_SIZE \ + ALIGN(256 + CTRL_SIZE + DS_SIZE + MAX_SKB_FRAGS * DS_SIZE, TXBB_SIZE) + #define MLX4_MAX_DESC_TXBBS (MLX4_TX_BOUNCE_BUFFER_SIZE / TXBB_SIZE) /* @@ -217,9 +227,7 @@ struct mlx4_en_tx_info { #define MLX4_EN_BIT_DESC_OWN 0x80000000 -#define CTRL_SIZE sizeof(struct mlx4_wqe_ctrl_seg) #define MLX4_EN_MEMTYPE_PAD 0x100 -#define DS_SIZE sizeof(struct mlx4_wqe_data_seg) struct mlx4_en_tx_desc { From 0e706f7961a44f4be13cf9b5eedb70f0ab2724b8 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Wed, 7 Dec 2022 14:12:37 +0000 Subject: [PATCH 1766/2002] net/mlx4: small optimization in mlx4_en_xmit() Test against MLX4_MAX_DESC_TXBBS only matters if the TX bounce buffer is going to be used. Signed-off-by: Eric Dumazet Cc: Wei Wang Reviewed-by: Tariq Toukan Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/mellanox/mlx4/en_tx.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx4/en_tx.c b/drivers/net/ethernet/mellanox/mlx4/en_tx.c index 8372aeb392a2..c5758637b7be 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_tx.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_tx.c @@ -911,11 +911,6 @@ netdev_tx_t mlx4_en_xmit(struct sk_buff *skb, struct net_device *dev) /* Align descriptor to TXBB size */ desc_size = ALIGN(real_size, TXBB_SIZE); nr_txbb = desc_size >> LOG_TXBB_SIZE; - if (unlikely(nr_txbb > MLX4_MAX_DESC_TXBBS)) { - if (netif_msg_tx_err(priv)) - en_warn(priv, "Oversized header or SG list\n"); - goto tx_drop_count; - } bf_ok = ring->bf_enabled; if (skb_vlan_tag_present(skb)) { @@ -943,6 +938,11 @@ netdev_tx_t mlx4_en_xmit(struct sk_buff *skb, struct net_device *dev) if (likely(index + nr_txbb <= ring->size)) tx_desc = ring->buf + (index << LOG_TXBB_SIZE); else { + if (unlikely(nr_txbb > MLX4_MAX_DESC_TXBBS)) { + if (netif_msg_tx_err(priv)) + en_warn(priv, "Oversized header or SG list\n"); + goto tx_drop_count; + } tx_desc = (struct mlx4_en_tx_desc *) ring->bounce_buf; bounce = true; bf_ok = false; From e60db051a4a70bff151eb59774c64af3a0266794 Mon Sep 17 00:00:00 2001 From: Stanislav Fomichev Date: Tue, 6 Dec 2022 15:27:39 -0800 Subject: [PATCH 1767/2002] selftests/bpf: Bring test_offload.py back to life Bpftool has new extra libbpf_det_bind probing map we need to exclude. Also skip trying to load netdevsim modules if it's already loaded (builtin). v2: - drop iproute2->bpftool changes (Toke) Signed-off-by: Stanislav Fomichev Signed-off-by: Andrii Nakryiko Link: https://lore.kernel.org/bpf/20221206232739.2504890-1-sdf@google.com --- tools/testing/selftests/bpf/test_offload.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/tools/testing/selftests/bpf/test_offload.py b/tools/testing/selftests/bpf/test_offload.py index 7fc15e0d24a9..7cb1bc05e5cf 100755 --- a/tools/testing/selftests/bpf/test_offload.py +++ b/tools/testing/selftests/bpf/test_offload.py @@ -769,12 +769,14 @@ skip(ret != 0, "bpftool not installed") base_progs = progs _, base_maps = bpftool("map") base_map_names = [ - 'pid_iter.rodata' # created on each bpftool invocation + 'pid_iter.rodata', # created on each bpftool invocation + 'libbpf_det_bind', # created on each bpftool invocation ] # Check netdevsim -ret, out = cmd("modprobe netdevsim", fail=False) -skip(ret != 0, "netdevsim module could not be loaded") +if not os.path.isdir("/sys/bus/netdevsim/"): + ret, out = cmd("modprobe netdevsim", fail=False) + skip(ret != 0, "netdevsim module could not be loaded") # Check debugfs _, out = cmd("mount") From f1543c7abab25d93bc8e9fae79b4cb3153ed6669 Mon Sep 17 00:00:00 2001 From: Yevgeny Kliteynik Date: Tue, 31 May 2022 13:55:28 +0300 Subject: [PATCH 1768/2002] net/mlx5: mlx5_ifc updates for MATCH_DEFINER general object Update full structure of match definer and add an ID of the SELECT match definer type. Signed-off-by: Yevgeny Kliteynik Reviewed-by: Alex Vesker Signed-off-by: Saeed Mahameed --- include/linux/mlx5/mlx5_ifc.h | 68 +++++++++++++++++++++++++++++++++-- 1 file changed, 66 insertions(+), 2 deletions(-) diff --git a/include/linux/mlx5/mlx5_ifc.h b/include/linux/mlx5/mlx5_ifc.h index 2093131483c7..294cfe175c4b 100644 --- a/include/linux/mlx5/mlx5_ifc.h +++ b/include/linux/mlx5/mlx5_ifc.h @@ -6108,6 +6108,38 @@ struct mlx5_ifc_match_definer_format_32_bits { u8 inner_dmac_15_0[0x10]; }; +enum { + MLX5_IFC_DEFINER_FORMAT_ID_SELECT = 61, +}; + +#define MLX5_IFC_DEFINER_FORMAT_OFFSET_UNUSED 0x0 +#define MLX5_IFC_DEFINER_FORMAT_OFFSET_OUTER_ETH_PKT_LEN 0x48 +#define MLX5_IFC_DEFINER_DW_SELECTORS_NUM 9 +#define MLX5_IFC_DEFINER_BYTE_SELECTORS_NUM 8 + +struct mlx5_ifc_match_definer_match_mask_bits { + u8 reserved_at_1c0[5][0x20]; + u8 match_dw_8[0x20]; + u8 match_dw_7[0x20]; + u8 match_dw_6[0x20]; + u8 match_dw_5[0x20]; + u8 match_dw_4[0x20]; + u8 match_dw_3[0x20]; + u8 match_dw_2[0x20]; + u8 match_dw_1[0x20]; + u8 match_dw_0[0x20]; + + u8 match_byte_7[0x8]; + u8 match_byte_6[0x8]; + u8 match_byte_5[0x8]; + u8 match_byte_4[0x8]; + + u8 match_byte_3[0x8]; + u8 match_byte_2[0x8]; + u8 match_byte_1[0x8]; + u8 match_byte_0[0x8]; +}; + struct mlx5_ifc_match_definer_bits { u8 modify_field_select[0x40]; @@ -6116,9 +6148,41 @@ struct mlx5_ifc_match_definer_bits { u8 reserved_at_80[0x10]; u8 format_id[0x10]; - u8 reserved_at_a0[0x160]; + u8 reserved_at_a0[0x60]; - u8 match_mask[16][0x20]; + u8 format_select_dw3[0x8]; + u8 format_select_dw2[0x8]; + u8 format_select_dw1[0x8]; + u8 format_select_dw0[0x8]; + + u8 format_select_dw7[0x8]; + u8 format_select_dw6[0x8]; + u8 format_select_dw5[0x8]; + u8 format_select_dw4[0x8]; + + u8 reserved_at_100[0x18]; + u8 format_select_dw8[0x8]; + + u8 reserved_at_120[0x20]; + + u8 format_select_byte3[0x8]; + u8 format_select_byte2[0x8]; + u8 format_select_byte1[0x8]; + u8 format_select_byte0[0x8]; + + u8 format_select_byte7[0x8]; + u8 format_select_byte6[0x8]; + u8 format_select_byte5[0x8]; + u8 format_select_byte4[0x8]; + + u8 reserved_at_180[0x40]; + + union { + struct { + u8 match_mask[16][0x20]; + }; + struct mlx5_ifc_match_definer_match_mask_bits match_mask_format; + }; }; struct mlx5_ifc_general_obj_in_cmd_hdr_bits { From 38bf24c38d19dc4ba5ec684ac2afa2f8e256db1e Mon Sep 17 00:00:00 2001 From: Yevgeny Kliteynik Date: Mon, 15 Aug 2022 12:45:28 +0300 Subject: [PATCH 1769/2002] net/mlx5: fs, add match on ranges API Range is a new flow destination type which allows matching on a range of values instead of matching on a specific value. Range flow destination has the following fields: - hit_ft: flow table to forward the traffic in case of hit - miss_ft: flow table to forward the traffic in case of miss - field: which packet characteristic to match on - min: minimal value for the selected field - max: maximal value for the selected field Note: - In order to match, the value in the packet should meet the following criteria: min <= value < max - Currently, the only supported field type is L2 packet length Signed-off-by: Yevgeny Kliteynik Reviewed-by: Alex Vesker Reviewed-by: Mark Bloch Signed-off-by: Saeed Mahameed --- .../mellanox/mlx5/core/diag/fs_tracepoint.c | 16 ++++++++++++++++ .../net/ethernet/mellanox/mlx5/core/fs_core.c | 11 +++++++++-- .../net/ethernet/mellanox/mlx5/core/fs_core.h | 1 + include/linux/mlx5/fs.h | 12 ++++++++++++ 4 files changed, 38 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/diag/fs_tracepoint.c b/drivers/net/ethernet/mellanox/mlx5/core/diag/fs_tracepoint.c index c5bb79a4fa57..2732128e7a6e 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/diag/fs_tracepoint.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/diag/fs_tracepoint.c @@ -228,6 +228,17 @@ const char *parse_fs_hdrs(struct trace_seq *p, return ret; } +static const char +*fs_dest_range_field_to_str(enum mlx5_flow_dest_range_field field) +{ + switch (field) { + case MLX5_FLOW_DEST_RANGE_FIELD_PKT_LEN: + return "packet len"; + default: + return "unknown dest range field"; + } +} + const char *parse_fs_dst(struct trace_seq *p, const struct mlx5_flow_destination *dst, u32 counter_id) @@ -259,6 +270,11 @@ const char *parse_fs_dst(struct trace_seq *p, case MLX5_FLOW_DESTINATION_TYPE_PORT: trace_seq_printf(p, "port\n"); break; + case MLX5_FLOW_DESTINATION_TYPE_RANGE: + trace_seq_printf(p, "field=%s min=%d max=%d\n", + fs_dest_range_field_to_str(dst->range.field), + dst->range.min, dst->range.max); + break; case MLX5_FLOW_DESTINATION_TYPE_NONE: trace_seq_printf(p, "none\n"); break; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c index d53190f22871..4dcd26b86662 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c @@ -448,7 +448,8 @@ static bool is_fwd_dest_type(enum mlx5_flow_destination_type type) type == MLX5_FLOW_DESTINATION_TYPE_UPLINK || type == MLX5_FLOW_DESTINATION_TYPE_VPORT || type == MLX5_FLOW_DESTINATION_TYPE_FLOW_SAMPLER || - type == MLX5_FLOW_DESTINATION_TYPE_TIR; + type == MLX5_FLOW_DESTINATION_TYPE_TIR || + type == MLX5_FLOW_DESTINATION_TYPE_RANGE; } static bool check_valid_spec(const struct mlx5_flow_spec *spec) @@ -1578,7 +1579,13 @@ static bool mlx5_flow_dests_cmp(struct mlx5_flow_destination *d1, (d1->type == MLX5_FLOW_DESTINATION_TYPE_FLOW_TABLE_NUM && d1->ft_num == d2->ft_num) || (d1->type == MLX5_FLOW_DESTINATION_TYPE_FLOW_SAMPLER && - d1->sampler_id == d2->sampler_id)) + d1->sampler_id == d2->sampler_id) || + (d1->type == MLX5_FLOW_DESTINATION_TYPE_RANGE && + d1->range.field == d2->range.field && + d1->range.hit_ft == d2->range.hit_ft && + d1->range.miss_ft == d2->range.miss_ft && + d1->range.min == d2->range.min && + d1->range.max == d2->range.max)) return true; } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.h b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.h index 3af50fd04d28..f137a0611b77 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.h @@ -123,6 +123,7 @@ enum mlx5_flow_steering_mode { enum mlx5_flow_steering_capabilty { MLX5_FLOW_STEERING_CAP_VLAN_PUSH_ON_RX = 1UL << 0, MLX5_FLOW_STEERING_CAP_VLAN_POP_ON_TX = 1UL << 1, + MLX5_FLOW_STEERING_CAP_MATCH_RANGES = 1UL << 2, }; struct mlx5_flow_steering { diff --git a/include/linux/mlx5/fs.h b/include/linux/mlx5/fs.h index c7a91981cd5a..ba6958b49a8e 100644 --- a/include/linux/mlx5/fs.h +++ b/include/linux/mlx5/fs.h @@ -50,6 +50,7 @@ enum mlx5_flow_destination_type { MLX5_FLOW_DESTINATION_TYPE_PORT, MLX5_FLOW_DESTINATION_TYPE_COUNTER, MLX5_FLOW_DESTINATION_TYPE_FLOW_TABLE_NUM, + MLX5_FLOW_DESTINATION_TYPE_RANGE, }; enum { @@ -143,6 +144,10 @@ enum { MLX5_FLOW_DEST_VPORT_REFORMAT_ID = BIT(1), }; +enum mlx5_flow_dest_range_field { + MLX5_FLOW_DEST_RANGE_FIELD_PKT_LEN = 0, +}; + struct mlx5_flow_destination { enum mlx5_flow_destination_type type; union { @@ -156,6 +161,13 @@ struct mlx5_flow_destination { struct mlx5_pkt_reformat *pkt_reformat; u8 flags; } vport; + struct { + struct mlx5_flow_table *hit_ft; + struct mlx5_flow_table *miss_ft; + enum mlx5_flow_dest_range_field field; + u32 min; + u32 max; + } range; u32 sampler_id; }; }; From e046b86e2900005726dd8f10217f778dff1b5929 Mon Sep 17 00:00:00 2001 From: Yevgeny Kliteynik Date: Tue, 31 May 2022 16:25:17 +0300 Subject: [PATCH 1770/2002] net/mlx5: DR, Add functions to create/destroy MATCH_DEFINER general object SW steering is able to match only on the exact values of the packet fields, as requested by the user: the user provides mask for the fields that are of interest, and the exact values to be matched on when the traffic is handled. Match Definer is a general FW object that defines which fields in the packet will be referenced by the mask and tag of each STE. Match definer ID is part of STE fields, and it defines how the HW needs to interpret the STE's mask/tag values. Till now SW steering used the definers that were managed by FW and implemented the STE layout as described by the HW spec. Now that we're adding a new type of STE, SW steering needs to define for the HW how it should interpret this new STE's layout. This is done with a programmable match definer. The programmable definer allows to selects which fields will be included in the definer, and their layout: it has up to 9 DW selectors 8 Byte selectors. Each selector indicates a DW/Byte worth of fields out of the table that is defined by HW spec by referencing the offset of the required DW/Byte. This patch adds dr_cmd function to create and destroy MATCH_DEFINER general object. Signed-off-by: Yevgeny Kliteynik Reviewed-by: Alex Vesker Signed-off-by: Saeed Mahameed --- .../mellanox/mlx5/core/steering/dr_cmd.c | 77 +++++++++++++++++++ .../mellanox/mlx5/core/steering/dr_types.h | 9 +++ 2 files changed, 86 insertions(+) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_cmd.c b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_cmd.c index b4739eafc180..07b6a6dcb92f 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_cmd.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_cmd.c @@ -564,6 +564,83 @@ void mlx5dr_cmd_destroy_reformat_ctx(struct mlx5_core_dev *mdev, mlx5_cmd_exec_in(mdev, dealloc_packet_reformat_context, in); } +static void dr_cmd_set_definer_format(void *ptr, u16 format_id, + u8 *dw_selectors, + u8 *byte_selectors) +{ + if (format_id != MLX5_IFC_DEFINER_FORMAT_ID_SELECT) + return; + + MLX5_SET(match_definer, ptr, format_select_dw0, dw_selectors[0]); + MLX5_SET(match_definer, ptr, format_select_dw1, dw_selectors[1]); + MLX5_SET(match_definer, ptr, format_select_dw2, dw_selectors[2]); + MLX5_SET(match_definer, ptr, format_select_dw3, dw_selectors[3]); + MLX5_SET(match_definer, ptr, format_select_dw4, dw_selectors[4]); + MLX5_SET(match_definer, ptr, format_select_dw5, dw_selectors[5]); + MLX5_SET(match_definer, ptr, format_select_dw6, dw_selectors[6]); + MLX5_SET(match_definer, ptr, format_select_dw7, dw_selectors[7]); + MLX5_SET(match_definer, ptr, format_select_dw8, dw_selectors[8]); + + MLX5_SET(match_definer, ptr, format_select_byte0, byte_selectors[0]); + MLX5_SET(match_definer, ptr, format_select_byte1, byte_selectors[1]); + MLX5_SET(match_definer, ptr, format_select_byte2, byte_selectors[2]); + MLX5_SET(match_definer, ptr, format_select_byte3, byte_selectors[3]); + MLX5_SET(match_definer, ptr, format_select_byte4, byte_selectors[4]); + MLX5_SET(match_definer, ptr, format_select_byte5, byte_selectors[5]); + MLX5_SET(match_definer, ptr, format_select_byte6, byte_selectors[6]); + MLX5_SET(match_definer, ptr, format_select_byte7, byte_selectors[7]); +} + +int mlx5dr_cmd_create_definer(struct mlx5_core_dev *mdev, + u16 format_id, + u8 *dw_selectors, + u8 *byte_selectors, + u8 *match_mask, + u32 *definer_id) +{ + u32 out[MLX5_ST_SZ_DW(general_obj_out_cmd_hdr)] = {}; + u32 in[MLX5_ST_SZ_DW(create_match_definer_in)] = {}; + void *ptr; + int err; + + ptr = MLX5_ADDR_OF(create_match_definer_in, in, + general_obj_in_cmd_hdr); + MLX5_SET(general_obj_in_cmd_hdr, ptr, opcode, + MLX5_CMD_OP_CREATE_GENERAL_OBJECT); + MLX5_SET(general_obj_in_cmd_hdr, ptr, obj_type, + MLX5_OBJ_TYPE_MATCH_DEFINER); + + ptr = MLX5_ADDR_OF(create_match_definer_in, in, obj_context); + MLX5_SET(match_definer, ptr, format_id, format_id); + + dr_cmd_set_definer_format(ptr, format_id, + dw_selectors, byte_selectors); + + ptr = MLX5_ADDR_OF(match_definer, ptr, match_mask); + memcpy(ptr, match_mask, MLX5_FLD_SZ_BYTES(match_definer, match_mask)); + + err = mlx5_cmd_exec(mdev, in, sizeof(in), out, sizeof(out)); + if (err) + return err; + + *definer_id = MLX5_GET(general_obj_out_cmd_hdr, out, obj_id); + + return 0; +} + +void +mlx5dr_cmd_destroy_definer(struct mlx5_core_dev *mdev, u32 definer_id) +{ + u32 in[MLX5_ST_SZ_DW(general_obj_in_cmd_hdr)] = {}; + u32 out[MLX5_ST_SZ_DW(general_obj_out_cmd_hdr)]; + + MLX5_SET(general_obj_in_cmd_hdr, in, opcode, MLX5_CMD_OP_DESTROY_GENERAL_OBJECT); + MLX5_SET(general_obj_in_cmd_hdr, in, obj_type, MLX5_OBJ_TYPE_MATCH_DEFINER); + MLX5_SET(general_obj_in_cmd_hdr, in, obj_id, definer_id); + + mlx5_cmd_exec(mdev, in, sizeof(in), out, sizeof(out)); +} + int mlx5dr_cmd_query_gid(struct mlx5_core_dev *mdev, u8 vhca_port_num, u16 index, struct mlx5dr_cmd_gid_attr *attr) { diff --git a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_types.h b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_types.h index 41a37b9ac98b..772ee747511f 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_types.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_types.h @@ -81,6 +81,7 @@ mlx5dr_icm_next_higher_chunk(enum mlx5dr_icm_chunk_size chunk) enum { DR_STE_SIZE = 64, DR_STE_SIZE_CTRL = 32, + DR_STE_SIZE_MATCH_TAG = 32, DR_STE_SIZE_TAG = 16, DR_STE_SIZE_MASK = 16, DR_STE_SIZE_REDUCED = DR_STE_SIZE - DR_STE_SIZE_MASK, @@ -1295,6 +1296,14 @@ int mlx5dr_cmd_create_reformat_ctx(struct mlx5_core_dev *mdev, u32 *reformat_id); void mlx5dr_cmd_destroy_reformat_ctx(struct mlx5_core_dev *mdev, u32 reformat_id); +int mlx5dr_cmd_create_definer(struct mlx5_core_dev *mdev, + u16 format_id, + u8 *dw_selectors, + u8 *byte_selectors, + u8 *match_mask, + u32 *definer_id); +void mlx5dr_cmd_destroy_definer(struct mlx5_core_dev *mdev, + u32 definer_id); struct mlx5dr_cmd_gid_attr { u8 gid[16]; From 0a8c20e23ff2d042295a20d09b0dde1492554ef7 Mon Sep 17 00:00:00 2001 From: Yevgeny Kliteynik Date: Fri, 5 Aug 2022 13:42:24 +0300 Subject: [PATCH 1771/2002] net/mlx5: DR, Rework is_fw_table function This patch handles the following two changes w.r.t. is_fw_table function: 1. When SW steering is asked to create/destroy FW table, we allow for creation/destruction of only termination tables. Rename mlx5_dr_is_fw_table both to comply with the static function naming and to reflect that we're actually checking for FW termination table. 2. When the action 'go to flow table' is created, the destination flow table can be any FW table, not only termination table. Adding function to check if the dest table is FW table. This function will also be used by the later creation of range match action, so putting it the header file. Signed-off-by: Yevgeny Kliteynik Reviewed-by: Alex Vesker Reviewed-by: Mark Bloch Signed-off-by: Saeed Mahameed --- .../mellanox/mlx5/core/steering/dr_types.h | 6 +++++ .../mellanox/mlx5/core/steering/fs_dr.c | 23 ++++++++++--------- 2 files changed, 18 insertions(+), 11 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_types.h b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_types.h index 772ee747511f..804268b487d8 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_types.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_types.h @@ -1492,4 +1492,10 @@ int mlx5dr_fw_create_md_tbl(struct mlx5dr_domain *dmn, u32 flow_source); void mlx5dr_fw_destroy_md_tbl(struct mlx5dr_domain *dmn, u32 tbl_id, u32 group_id); + +static inline bool mlx5dr_is_fw_table(struct mlx5_flow_table *ft) +{ + return !ft->fs_dr_table.dr_table; +} + #endif /* _DR_TYPES_H_ */ diff --git a/drivers/net/ethernet/mellanox/mlx5/core/steering/fs_dr.c b/drivers/net/ethernet/mellanox/mlx5/core/steering/fs_dr.c index 13b6d4721e17..c78fb016c245 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/steering/fs_dr.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/steering/fs_dr.c @@ -7,10 +7,11 @@ #include "fs_cmd.h" #include "mlx5dr.h" #include "fs_dr.h" +#include "dr_types.h" -static bool mlx5_dr_is_fw_table(u32 flags) +static bool dr_is_fw_term_table(struct mlx5_flow_table *ft) { - if (flags & MLX5_FLOW_TABLE_TERMINATION) + if (ft->flags & MLX5_FLOW_TABLE_TERMINATION) return true; return false; @@ -69,7 +70,7 @@ static int mlx5_cmd_dr_create_flow_table(struct mlx5_flow_root_namespace *ns, u32 flags; int err; - if (mlx5_dr_is_fw_table(ft->flags)) + if (dr_is_fw_term_table(ft)) return mlx5_fs_cmd_get_fw_cmds()->create_flow_table(ns, ft, ft_attr, next_ft); @@ -109,7 +110,7 @@ static int mlx5_cmd_dr_destroy_flow_table(struct mlx5_flow_root_namespace *ns, struct mlx5dr_action *action = ft->fs_dr_table.miss_action; int err; - if (mlx5_dr_is_fw_table(ft->flags)) + if (dr_is_fw_term_table(ft)) return mlx5_fs_cmd_get_fw_cmds()->destroy_flow_table(ns, ft); err = mlx5dr_table_destroy(ft->fs_dr_table.dr_table); @@ -134,7 +135,7 @@ static int mlx5_cmd_dr_modify_flow_table(struct mlx5_flow_root_namespace *ns, struct mlx5_flow_table *ft, struct mlx5_flow_table *next_ft) { - if (mlx5_dr_is_fw_table(ft->flags)) + if (dr_is_fw_term_table(ft)) return mlx5_fs_cmd_get_fw_cmds()->modify_flow_table(ns, ft, next_ft); return set_miss_action(ns, ft, next_ft); @@ -153,7 +154,7 @@ static int mlx5_cmd_dr_create_flow_group(struct mlx5_flow_root_namespace *ns, match_criteria_enable); struct mlx5dr_match_parameters mask; - if (mlx5_dr_is_fw_table(ft->flags)) + if (dr_is_fw_term_table(ft)) return mlx5_fs_cmd_get_fw_cmds()->create_flow_group(ns, ft, in, fg); @@ -178,7 +179,7 @@ static int mlx5_cmd_dr_destroy_flow_group(struct mlx5_flow_root_namespace *ns, struct mlx5_flow_table *ft, struct mlx5_flow_group *fg) { - if (mlx5_dr_is_fw_table(ft->flags)) + if (dr_is_fw_term_table(ft)) return mlx5_fs_cmd_get_fw_cmds()->destroy_flow_group(ns, ft, fg); return mlx5dr_matcher_destroy(fg->fs_dr_matcher.dr_matcher); @@ -209,7 +210,7 @@ static struct mlx5dr_action *create_ft_action(struct mlx5dr_domain *domain, { struct mlx5_flow_table *dest_ft = dst->dest_attr.ft; - if (mlx5_dr_is_fw_table(dest_ft->flags)) + if (mlx5dr_is_fw_table(dest_ft)) return mlx5dr_action_create_dest_flow_fw_table(domain, dest_ft); return mlx5dr_action_create_dest_table(dest_ft->fs_dr_table.dr_table); } @@ -260,7 +261,7 @@ static int mlx5_cmd_dr_create_fte(struct mlx5_flow_root_namespace *ns, int err = 0; int i; - if (mlx5_dr_is_fw_table(ft->flags)) + if (dr_is_fw_term_table(ft)) return mlx5_fs_cmd_get_fw_cmds()->create_fte(ns, ft, group, fte); actions = kcalloc(MLX5_FLOW_CONTEXT_ACTION_MAX, sizeof(*actions), @@ -702,7 +703,7 @@ static int mlx5_cmd_dr_delete_fte(struct mlx5_flow_root_namespace *ns, int err; int i; - if (mlx5_dr_is_fw_table(ft->flags)) + if (dr_is_fw_term_table(ft)) return mlx5_fs_cmd_get_fw_cmds()->delete_fte(ns, ft, fte); err = mlx5dr_rule_destroy(rule->dr_rule); @@ -727,7 +728,7 @@ static int mlx5_cmd_dr_update_fte(struct mlx5_flow_root_namespace *ns, struct fs_fte fte_tmp = {}; int ret; - if (mlx5_dr_is_fw_table(ft->flags)) + if (dr_is_fw_term_table(ft)) return mlx5_fs_cmd_get_fw_cmds()->update_fte(ns, ft, group, modify_mask, fte); /* Backup current dr rule details */ From c72a57ad6e9183eeb6739ae688b6417df4a512ac Mon Sep 17 00:00:00 2001 From: Yevgeny Kliteynik Date: Sat, 6 Aug 2022 02:36:54 +0300 Subject: [PATCH 1772/2002] net/mlx5: DR, Handle FT action in a separate function As preparation for range action support, moving the handling of final ICM address for flow table action to a separate function. Signed-off-by: Yevgeny Kliteynik Reviewed-by: Alex Vesker Signed-off-by: Saeed Mahameed --- .../mellanox/mlx5/core/steering/dr_action.c | 127 +++++++++++------- 1 file changed, 81 insertions(+), 46 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_action.c b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_action.c index b1dfad274a39..fc44fee2f9c2 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_action.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_action.c @@ -634,6 +634,83 @@ static void dr_action_print_sequence(struct mlx5dr_domain *dmn, actions[i]->action_type); } +static int dr_action_get_dest_fw_tbl_addr(struct mlx5dr_matcher *matcher, + struct mlx5dr_action_dest_tbl *dest_tbl, + bool is_rx_rule, + u64 *final_icm_addr) +{ + struct mlx5dr_cmd_query_flow_table_details output; + struct mlx5dr_domain *dmn = matcher->tbl->dmn; + int ret; + + if (!dest_tbl->fw_tbl.rx_icm_addr) { + ret = mlx5dr_cmd_query_flow_table(dmn->mdev, + dest_tbl->fw_tbl.type, + dest_tbl->fw_tbl.id, + &output); + if (ret) { + mlx5dr_err(dmn, + "Failed mlx5_cmd_query_flow_table ret: %d\n", + ret); + return ret; + } + + dest_tbl->fw_tbl.tx_icm_addr = output.sw_owner_icm_root_1; + dest_tbl->fw_tbl.rx_icm_addr = output.sw_owner_icm_root_0; + } + + *final_icm_addr = is_rx_rule ? dest_tbl->fw_tbl.rx_icm_addr : + dest_tbl->fw_tbl.tx_icm_addr; + return 0; +} + +static int dr_action_get_dest_sw_tbl_addr(struct mlx5dr_matcher *matcher, + struct mlx5dr_action_dest_tbl *dest_tbl, + bool is_rx_rule, + u64 *final_icm_addr) +{ + struct mlx5dr_domain *dmn = matcher->tbl->dmn; + struct mlx5dr_icm_chunk *chunk; + + if (dest_tbl->tbl->dmn != dmn) { + mlx5dr_err(dmn, + "Destination table belongs to a different domain\n"); + return -EINVAL; + } + + if (dest_tbl->tbl->level <= matcher->tbl->level) { + mlx5_core_dbg_once(dmn->mdev, + "Connecting table to a lower/same level destination table\n"); + mlx5dr_dbg(dmn, + "Connecting table at level %d to a destination table at level %d\n", + matcher->tbl->level, + dest_tbl->tbl->level); + } + + chunk = is_rx_rule ? dest_tbl->tbl->rx.s_anchor->chunk : + dest_tbl->tbl->tx.s_anchor->chunk; + + *final_icm_addr = mlx5dr_icm_pool_get_chunk_icm_addr(chunk); + return 0; +} + +static int dr_action_get_dest_tbl_addr(struct mlx5dr_matcher *matcher, + struct mlx5dr_action_dest_tbl *dest_tbl, + bool is_rx_rule, + u64 *final_icm_addr) +{ + if (dest_tbl->is_fw_tbl) + return dr_action_get_dest_fw_tbl_addr(matcher, + dest_tbl, + is_rx_rule, + final_icm_addr); + + return dr_action_get_dest_sw_tbl_addr(matcher, + dest_tbl, + is_rx_rule, + final_icm_addr); +} + #define WITH_VLAN_NUM_HW_ACTIONS 6 int mlx5dr_actions_build_ste_arr(struct mlx5dr_matcher *matcher, @@ -661,8 +738,6 @@ int mlx5dr_actions_build_ste_arr(struct mlx5dr_matcher *matcher, action_domain = dr_action_get_action_domain(dmn->type, nic_dmn->type); for (i = 0; i < num_actions; i++) { - struct mlx5dr_action_dest_tbl *dest_tbl; - struct mlx5dr_icm_chunk *chunk; struct mlx5dr_action *action; int max_actions_type = 1; u32 action_type; @@ -676,50 +751,10 @@ int mlx5dr_actions_build_ste_arr(struct mlx5dr_matcher *matcher, break; case DR_ACTION_TYP_FT: dest_action = action; - dest_tbl = action->dest_tbl; - if (!dest_tbl->is_fw_tbl) { - if (dest_tbl->tbl->dmn != dmn) { - mlx5dr_err(dmn, - "Destination table belongs to a different domain\n"); - return -EINVAL; - } - if (dest_tbl->tbl->level <= matcher->tbl->level) { - mlx5_core_dbg_once(dmn->mdev, - "Connecting table to a lower/same level destination table\n"); - mlx5dr_dbg(dmn, - "Connecting table at level %d to a destination table at level %d\n", - matcher->tbl->level, - dest_tbl->tbl->level); - } - chunk = rx_rule ? dest_tbl->tbl->rx.s_anchor->chunk : - dest_tbl->tbl->tx.s_anchor->chunk; - attr.final_icm_addr = mlx5dr_icm_pool_get_chunk_icm_addr(chunk); - } else { - struct mlx5dr_cmd_query_flow_table_details output; - int ret; - - /* get the relevant addresses */ - if (!action->dest_tbl->fw_tbl.rx_icm_addr) { - ret = mlx5dr_cmd_query_flow_table(dmn->mdev, - dest_tbl->fw_tbl.type, - dest_tbl->fw_tbl.id, - &output); - if (!ret) { - dest_tbl->fw_tbl.tx_icm_addr = - output.sw_owner_icm_root_1; - dest_tbl->fw_tbl.rx_icm_addr = - output.sw_owner_icm_root_0; - } else { - mlx5dr_err(dmn, - "Failed mlx5_cmd_query_flow_table ret: %d\n", - ret); - return ret; - } - } - attr.final_icm_addr = rx_rule ? - dest_tbl->fw_tbl.rx_icm_addr : - dest_tbl->fw_tbl.tx_icm_addr; - } + ret = dr_action_get_dest_tbl_addr(matcher, action->dest_tbl, + rx_rule, &attr.final_icm_addr); + if (ret) + return ret; break; case DR_ACTION_TYP_QP: mlx5dr_info(dmn, "Domain doesn't support QP\n"); From 1339678fdde1c84ea8b4d6e3627f7742c87f95fa Mon Sep 17 00:00:00 2001 From: Yevgeny Kliteynik Date: Thu, 25 Aug 2022 15:35:59 +0300 Subject: [PATCH 1773/2002] net/mlx5: DR, Manage definers with refcounts In many cases different actions will ask for the same definer format. Instead of allocating new definer general object and running out of definers, have an xarray of allocated definers and keep track of their usage with refcounts: allocate a new definer only when there isn't one with the same format already created, and destroy definer only when its refcount runs down to zero. Signed-off-by: Yevgeny Kliteynik Reviewed-by: Alex Vesker Signed-off-by: Saeed Mahameed --- .../net/ethernet/mellanox/mlx5/core/Makefile | 1 + .../mellanox/mlx5/core/steering/dr_definer.c | 151 ++++++++++++++++++ .../mellanox/mlx5/core/steering/dr_domain.c | 7 +- .../mellanox/mlx5/core/steering/dr_types.h | 1 + .../mellanox/mlx5/core/steering/mlx5dr.h | 5 + 5 files changed, 163 insertions(+), 2 deletions(-) create mode 100644 drivers/net/ethernet/mellanox/mlx5/core/steering/dr_definer.c diff --git a/drivers/net/ethernet/mellanox/mlx5/core/Makefile b/drivers/net/ethernet/mellanox/mlx5/core/Makefile index a22c32aabf11..cd4a1ab0ea78 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/Makefile +++ b/drivers/net/ethernet/mellanox/mlx5/core/Makefile @@ -111,6 +111,7 @@ mlx5_core-$(CONFIG_MLX5_SW_STEERING) += steering/dr_domain.o steering/dr_table.o steering/dr_ste_v2.o \ steering/dr_cmd.o steering/dr_fw.o \ steering/dr_action.o steering/fs_dr.o \ + steering/dr_definer.o \ steering/dr_dbg.o lib/smfs.o # # SF device diff --git a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_definer.c b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_definer.c new file mode 100644 index 000000000000..d5ea97751945 --- /dev/null +++ b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_definer.c @@ -0,0 +1,151 @@ +// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB +// Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + +#include "dr_types.h" +#include "dr_ste.h" + +struct dr_definer_object { + u32 id; + u16 format_id; + u8 dw_selectors[MLX5_IFC_DEFINER_DW_SELECTORS_NUM]; + u8 byte_selectors[MLX5_IFC_DEFINER_BYTE_SELECTORS_NUM]; + u8 match_mask[DR_STE_SIZE_MATCH_TAG]; + refcount_t refcount; +}; + +static bool dr_definer_compare(struct dr_definer_object *definer, + u16 format_id, u8 *dw_selectors, + u8 *byte_selectors, u8 *match_mask) +{ + int i; + + if (definer->format_id != format_id) + return false; + + for (i = 0; i < MLX5_IFC_DEFINER_DW_SELECTORS_NUM; i++) + if (definer->dw_selectors[i] != dw_selectors[i]) + return false; + + for (i = 0; i < MLX5_IFC_DEFINER_BYTE_SELECTORS_NUM; i++) + if (definer->byte_selectors[i] != byte_selectors[i]) + return false; + + if (memcmp(definer->match_mask, match_mask, DR_STE_SIZE_MATCH_TAG)) + return false; + + return true; +} + +static struct dr_definer_object * +dr_definer_find_obj(struct mlx5dr_domain *dmn, u16 format_id, + u8 *dw_selectors, u8 *byte_selectors, u8 *match_mask) +{ + struct dr_definer_object *definer_obj; + unsigned long id; + + xa_for_each(&dmn->definers_xa, id, definer_obj) { + if (dr_definer_compare(definer_obj, format_id, + dw_selectors, byte_selectors, + match_mask)) + return definer_obj; + } + + return NULL; +} + +static struct dr_definer_object * +dr_definer_create_obj(struct mlx5dr_domain *dmn, u16 format_id, + u8 *dw_selectors, u8 *byte_selectors, u8 *match_mask) +{ + struct dr_definer_object *definer_obj; + int ret = 0; + + definer_obj = kzalloc(sizeof(*definer_obj), GFP_KERNEL); + if (!definer_obj) + return NULL; + + ret = mlx5dr_cmd_create_definer(dmn->mdev, + format_id, + dw_selectors, + byte_selectors, + match_mask, + &definer_obj->id); + if (ret) + goto err_free_definer_obj; + + /* Definer ID can have 32 bits, but STE format + * supports only definers with 8 bit IDs. + */ + if (definer_obj->id > 0xff) { + mlx5dr_err(dmn, "Unsupported definer ID (%d)\n", definer_obj->id); + goto err_destroy_definer; + } + + definer_obj->format_id = format_id; + memcpy(definer_obj->dw_selectors, dw_selectors, sizeof(definer_obj->dw_selectors)); + memcpy(definer_obj->byte_selectors, byte_selectors, sizeof(definer_obj->byte_selectors)); + memcpy(definer_obj->match_mask, match_mask, sizeof(definer_obj->match_mask)); + + refcount_set(&definer_obj->refcount, 1); + + ret = xa_insert(&dmn->definers_xa, definer_obj->id, definer_obj, GFP_KERNEL); + if (ret) { + mlx5dr_dbg(dmn, "Couldn't insert new definer into xarray (%d)\n", ret); + goto err_destroy_definer; + } + + return definer_obj; + +err_destroy_definer: + mlx5dr_cmd_destroy_definer(dmn->mdev, definer_obj->id); +err_free_definer_obj: + kfree(definer_obj); + + return NULL; +} + +static void dr_definer_destroy_obj(struct mlx5dr_domain *dmn, + struct dr_definer_object *definer_obj) +{ + mlx5dr_cmd_destroy_definer(dmn->mdev, definer_obj->id); + xa_erase(&dmn->definers_xa, definer_obj->id); + kfree(definer_obj); +} + +int mlx5dr_definer_get(struct mlx5dr_domain *dmn, u16 format_id, + u8 *dw_selectors, u8 *byte_selectors, + u8 *match_mask, u32 *definer_id) +{ + struct dr_definer_object *definer_obj; + int ret = 0; + + definer_obj = dr_definer_find_obj(dmn, format_id, dw_selectors, + byte_selectors, match_mask); + if (!definer_obj) { + definer_obj = dr_definer_create_obj(dmn, format_id, + dw_selectors, byte_selectors, + match_mask); + if (!definer_obj) + return -ENOMEM; + } else { + refcount_inc(&definer_obj->refcount); + } + + *definer_id = definer_obj->id; + + return ret; +} + +void mlx5dr_definer_put(struct mlx5dr_domain *dmn, u32 definer_id) +{ + struct dr_definer_object *definer_obj; + + definer_obj = xa_load(&dmn->definers_xa, definer_id); + if (!definer_obj) { + mlx5dr_err(dmn, "Definer ID %d not found\n", definer_id); + return; + } + + if (refcount_dec_and_test(&definer_obj->refcount)) + dr_definer_destroy_obj(dmn, definer_obj); +} diff --git a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_domain.c b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_domain.c index 9a9836218c8e..5b8bb2ca31e6 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_domain.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_domain.c @@ -425,10 +425,11 @@ mlx5dr_domain_create(struct mlx5_core_dev *mdev, enum mlx5dr_domain_type type) refcount_set(&dmn->refcount, 1); mutex_init(&dmn->info.rx.mutex); mutex_init(&dmn->info.tx.mutex); + xa_init(&dmn->definers_xa); if (dr_domain_caps_init(mdev, dmn)) { mlx5dr_err(dmn, "Failed init domain, no caps\n"); - goto free_domain; + goto def_xa_destroy; } dmn->info.max_log_action_icm_sz = DR_CHUNK_SIZE_4K; @@ -453,7 +454,8 @@ mlx5dr_domain_create(struct mlx5_core_dev *mdev, enum mlx5dr_domain_type type) uninit_caps: dr_domain_caps_uninit(dmn); -free_domain: +def_xa_destroy: + xa_destroy(&dmn->definers_xa); kfree(dmn); return NULL; } @@ -493,6 +495,7 @@ int mlx5dr_domain_destroy(struct mlx5dr_domain *dmn) dr_domain_uninit_csum_recalc_fts(dmn); dr_domain_uninit_resources(dmn); dr_domain_caps_uninit(dmn); + xa_destroy(&dmn->definers_xa); mutex_destroy(&dmn->info.tx.mutex); mutex_destroy(&dmn->info.rx.mutex); kfree(dmn); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_types.h b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_types.h index 804268b487d8..94093c3b55a5 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_types.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_types.h @@ -925,6 +925,7 @@ struct mlx5dr_domain { struct mlx5dr_ste_ctx *ste_ctx; struct list_head dbg_tbl_list; struct mlx5dr_dbg_dump_info dump_info; + struct xarray definers_xa; }; struct mlx5dr_table_rx_tx { diff --git a/drivers/net/ethernet/mellanox/mlx5/core/steering/mlx5dr.h b/drivers/net/ethernet/mellanox/mlx5/core/steering/mlx5dr.h index 84ed77763b21..6ea50436ea61 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/steering/mlx5dr.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/steering/mlx5dr.h @@ -142,6 +142,11 @@ mlx5dr_action_create_aso(struct mlx5dr_domain *dmn, int mlx5dr_action_destroy(struct mlx5dr_action *action); +int mlx5dr_definer_get(struct mlx5dr_domain *dmn, u16 format_id, + u8 *dw_selectors, u8 *byte_selectors, + u8 *match_mask, u32 *definer_id); +void mlx5dr_definer_put(struct mlx5dr_domain *dmn, u32 definer_id); + static inline bool mlx5dr_is_supported(struct mlx5_core_dev *dev) { From f31bda789f1d7216c0e8d24c55d35dc4e97dc4ab Mon Sep 17 00:00:00 2001 From: Yevgeny Kliteynik Date: Wed, 30 Nov 2022 00:26:05 +0200 Subject: [PATCH 1774/2002] net/mlx5: DR, Some refactoring of miss address handling In preparation for MATCH RANGE STE support, create a function to set the miss address of an STE. Signed-off-by: Yevgeny Kliteynik Reviewed-by: Erez Shitrit Reviewed-by: Mark Bloch Signed-off-by: Saeed Mahameed --- .../mellanox/mlx5/core/steering/dr_rule.c | 24 +++++++++++-------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_rule.c b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_rule.c index 7879991048ce..3351b2a1ba18 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_rule.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_rule.c @@ -35,16 +35,25 @@ static int dr_rule_append_to_miss_list(struct mlx5dr_domain *dmn, return 0; } +static void dr_rule_set_last_ste_miss_addr(struct mlx5dr_matcher *matcher, + struct mlx5dr_matcher_rx_tx *nic_matcher, + u8 *hw_ste) +{ + struct mlx5dr_ste_ctx *ste_ctx = matcher->tbl->dmn->ste_ctx; + u64 icm_addr; + + icm_addr = mlx5dr_icm_pool_get_chunk_icm_addr(nic_matcher->e_anchor->chunk); + mlx5dr_ste_set_miss_addr(ste_ctx, hw_ste, icm_addr); +} + static struct mlx5dr_ste * dr_rule_create_collision_htbl(struct mlx5dr_matcher *matcher, struct mlx5dr_matcher_rx_tx *nic_matcher, u8 *hw_ste) { struct mlx5dr_domain *dmn = matcher->tbl->dmn; - struct mlx5dr_ste_ctx *ste_ctx = dmn->ste_ctx; struct mlx5dr_ste_htbl *new_htbl; struct mlx5dr_ste *ste; - u64 icm_addr; /* Create new table for miss entry */ new_htbl = mlx5dr_ste_htbl_alloc(dmn->ste_icm_pool, @@ -58,8 +67,7 @@ dr_rule_create_collision_htbl(struct mlx5dr_matcher *matcher, /* One and only entry, never grows */ ste = new_htbl->chunk->ste_arr; - icm_addr = mlx5dr_icm_pool_get_chunk_icm_addr(nic_matcher->e_anchor->chunk); - mlx5dr_ste_set_miss_addr(ste_ctx, hw_ste, icm_addr); + dr_rule_set_last_ste_miss_addr(matcher, nic_matcher, hw_ste); mlx5dr_htbl_get(new_htbl); return ste; @@ -241,7 +249,6 @@ dr_rule_rehash_copy_ste(struct mlx5dr_matcher *matcher, bool use_update_list = false; u8 hw_ste[DR_STE_SIZE] = {}; struct mlx5dr_ste *new_ste; - u64 icm_addr; int new_idx; u8 sb_idx; @@ -250,9 +257,8 @@ dr_rule_rehash_copy_ste(struct mlx5dr_matcher *matcher, mlx5dr_ste_set_bit_mask(hw_ste, nic_matcher->ste_builder[sb_idx].bit_mask); /* Copy STE control and tag */ - icm_addr = mlx5dr_icm_pool_get_chunk_icm_addr(nic_matcher->e_anchor->chunk); memcpy(hw_ste, mlx5dr_ste_get_hw_ste(cur_ste), DR_STE_SIZE_REDUCED); - mlx5dr_ste_set_miss_addr(dmn->ste_ctx, hw_ste, icm_addr); + dr_rule_set_last_ste_miss_addr(matcher, nic_matcher, hw_ste); new_idx = mlx5dr_ste_calc_hash_index(hw_ste, new_htbl); new_ste = &new_htbl->chunk->ste_arr[new_idx]; @@ -773,7 +779,6 @@ static int dr_rule_handle_empty_entry(struct mlx5dr_matcher *matcher, { struct mlx5dr_domain *dmn = matcher->tbl->dmn; struct mlx5dr_ste_send_info *ste_info; - u64 icm_addr; /* Take ref on table, only on first time this ste is used */ mlx5dr_htbl_get(cur_htbl); @@ -781,8 +786,7 @@ static int dr_rule_handle_empty_entry(struct mlx5dr_matcher *matcher, /* new entry -> new branch */ list_add_tail(&ste->miss_list_node, miss_list); - icm_addr = mlx5dr_icm_pool_get_chunk_icm_addr(nic_matcher->e_anchor->chunk); - mlx5dr_ste_set_miss_addr(dmn->ste_ctx, hw_ste, icm_addr); + dr_rule_set_last_ste_miss_addr(matcher, nic_matcher, hw_ste); ste->ste_chain_location = ste_location; From 1207a772c09d43f2720ed4296b8a5a096d62365a Mon Sep 17 00:00:00 2001 From: Yevgeny Kliteynik Date: Tue, 29 Nov 2022 11:01:23 +0200 Subject: [PATCH 1775/2002] net/mlx5: DR, Add function that tells if STE miss addr has been initialized Up until now miss address in all the STEs was used to connect miss lists and to link the last STE in the list to end anchor. Match range STE will require special handling because its miss address is part of the 'action'. That is, range action has hit and miss addresses. Since the range action is always the last action, need to make sure that its miss address isn't overwritten by the end anchor. Adding new function mlx5dr_ste_is_miss_addr_set() to answer the question whether the STE's miss address has already been set as part of STE initialization. Use a callback that always returns false right now. Once match range is added, a different callback will be used for that STE type. Signed-off-by: Yevgeny Kliteynik Reviewed-by: Erez Shitrit Reviewed-by: Mark Bloch Signed-off-by: Saeed Mahameed --- .../net/ethernet/mellanox/mlx5/core/steering/dr_rule.c | 3 +++ .../net/ethernet/mellanox/mlx5/core/steering/dr_ste.c | 10 ++++++++++ .../net/ethernet/mellanox/mlx5/core/steering/dr_ste.h | 1 + .../ethernet/mellanox/mlx5/core/steering/dr_ste_v1.c | 6 ++++++ .../ethernet/mellanox/mlx5/core/steering/dr_ste_v1.h | 1 + .../ethernet/mellanox/mlx5/core/steering/dr_ste_v2.c | 1 + .../ethernet/mellanox/mlx5/core/steering/dr_types.h | 1 + 7 files changed, 23 insertions(+) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_rule.c b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_rule.c index 3351b2a1ba18..74cbe53ee9db 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_rule.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_rule.c @@ -42,6 +42,9 @@ static void dr_rule_set_last_ste_miss_addr(struct mlx5dr_matcher *matcher, struct mlx5dr_ste_ctx *ste_ctx = matcher->tbl->dmn->ste_ctx; u64 icm_addr; + if (mlx5dr_ste_is_miss_addr_set(ste_ctx, hw_ste)) + return; + icm_addr = mlx5dr_icm_pool_get_chunk_icm_addr(nic_matcher->e_anchor->chunk); mlx5dr_ste_set_miss_addr(ste_ctx, hw_ste, icm_addr); } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_ste.c b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_ste.c index 9e19a8dc9022..1e15f605df6e 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_ste.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_ste.c @@ -90,6 +90,16 @@ static void dr_ste_set_always_miss(struct dr_hw_ste_format *hw_ste) hw_ste->mask[0] = 0; } +bool mlx5dr_ste_is_miss_addr_set(struct mlx5dr_ste_ctx *ste_ctx, + u8 *hw_ste_p) +{ + if (!ste_ctx->is_miss_addr_set) + return false; + + /* check if miss address is already set for this type of STE */ + return ste_ctx->is_miss_addr_set(hw_ste_p); +} + void mlx5dr_ste_set_miss_addr(struct mlx5dr_ste_ctx *ste_ctx, u8 *hw_ste_p, u64 miss_addr) { diff --git a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_ste.h b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_ste.h index 17513baff9b0..7075142bcfb6 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_ste.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_ste.h @@ -151,6 +151,7 @@ struct mlx5dr_ste_ctx { bool is_rx, u16 gvmi); void (*set_next_lu_type)(u8 *hw_ste_p, u16 lu_type); u16 (*get_next_lu_type)(u8 *hw_ste_p); + bool (*is_miss_addr_set)(u8 *hw_ste_p); void (*set_miss_addr)(u8 *hw_ste_p, u64 miss_addr); u64 (*get_miss_addr)(u8 *hw_ste_p); void (*set_hit_addr)(u8 *hw_ste_p, u64 icm_addr, u32 ht_size); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_ste_v1.c b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_ste_v1.c index ee677a5c76be..87c29118c51b 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_ste_v1.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_ste_v1.c @@ -267,6 +267,11 @@ static void dr_ste_v1_set_entry_type(u8 *hw_ste_p, u8 entry_type) MLX5_SET(ste_match_bwc_v1, hw_ste_p, entry_format, entry_type); } +bool dr_ste_v1_is_miss_addr_set(u8 *hw_ste_p) +{ + return false; +} + void dr_ste_v1_set_miss_addr(u8 *hw_ste_p, u64 miss_addr) { u64 index = miss_addr >> 6; @@ -2144,6 +2149,7 @@ static struct mlx5dr_ste_ctx ste_ctx_v1 = { .ste_init = &dr_ste_v1_init, .set_next_lu_type = &dr_ste_v1_set_next_lu_type, .get_next_lu_type = &dr_ste_v1_get_next_lu_type, + .is_miss_addr_set = &dr_ste_v1_is_miss_addr_set, .set_miss_addr = &dr_ste_v1_set_miss_addr, .get_miss_addr = &dr_ste_v1_get_miss_addr, .set_hit_addr = &dr_ste_v1_set_hit_addr, diff --git a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_ste_v1.h b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_ste_v1.h index 8a1d49790c6e..b5c0f0f8392f 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_ste_v1.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_ste_v1.h @@ -7,6 +7,7 @@ #include "dr_types.h" #include "dr_ste.h" +bool dr_ste_v1_is_miss_addr_set(u8 *hw_ste_p); void dr_ste_v1_set_miss_addr(u8 *hw_ste_p, u64 miss_addr); u64 dr_ste_v1_get_miss_addr(u8 *hw_ste_p); void dr_ste_v1_set_byte_mask(u8 *hw_ste_p, u16 byte_mask); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_ste_v2.c b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_ste_v2.c index c60fddd125d2..cf1a3c9a1cf4 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_ste_v2.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_ste_v2.c @@ -202,6 +202,7 @@ static struct mlx5dr_ste_ctx ste_ctx_v2 = { .ste_init = &dr_ste_v1_init, .set_next_lu_type = &dr_ste_v1_set_next_lu_type, .get_next_lu_type = &dr_ste_v1_get_next_lu_type, + .is_miss_addr_set = &dr_ste_v1_is_miss_addr_set, .set_miss_addr = &dr_ste_v1_set_miss_addr, .get_miss_addr = &dr_ste_v1_get_miss_addr, .set_hit_addr = &dr_ste_v1_set_hit_addr, diff --git a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_types.h b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_types.h index 94093c3b55a5..a3e8ce415563 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_types.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_types.h @@ -238,6 +238,7 @@ static inline void mlx5dr_htbl_get(struct mlx5dr_ste_htbl *htbl) /* STE utils */ u32 mlx5dr_ste_calc_hash_index(u8 *hw_ste_p, struct mlx5dr_ste_htbl *htbl); +bool mlx5dr_ste_is_miss_addr_set(struct mlx5dr_ste_ctx *ste_ctx, u8 *hw_ste_p); void mlx5dr_ste_set_miss_addr(struct mlx5dr_ste_ctx *ste_ctx, u8 *hw_ste, u64 miss_addr); void mlx5dr_ste_set_hit_addr(struct mlx5dr_ste_ctx *ste_ctx, From be6d5daeaa3bcd6ce414c20c61bd8ae8d38da49a Mon Sep 17 00:00:00 2001 From: Yevgeny Kliteynik Date: Tue, 29 Nov 2022 11:21:38 +0200 Subject: [PATCH 1776/2002] net/mlx5: DR, Add support for range match action Add support for matching on range. The supported type of range is L2 frame size. Signed-off-by: Yevgeny Kliteynik Reviewed-by: Erez Shitrit Signed-off-by: Saeed Mahameed --- .../mellanox/mlx5/core/steering/dr_action.c | 173 ++++++++++++++++++ .../mellanox/mlx5/core/steering/dr_dbg.c | 29 ++- .../mellanox/mlx5/core/steering/dr_ste_v1.c | 65 ++++++- .../mellanox/mlx5/core/steering/dr_types.h | 26 +++ .../mellanox/mlx5/core/steering/fs_dr.c | 30 ++- .../mlx5/core/steering/mlx5_ifc_dr_ste_v1.h | 35 ++++ .../mellanox/mlx5/core/steering/mlx5dr.h | 8 + 7 files changed, 363 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_action.c b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_action.c index fc44fee2f9c2..ee104cf04392 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_action.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_action.c @@ -44,6 +44,7 @@ static const char * const action_type_to_str[] = { [DR_ACTION_TYP_INSERT_HDR] = "DR_ACTION_TYP_INSERT_HDR", [DR_ACTION_TYP_REMOVE_HDR] = "DR_ACTION_TYP_REMOVE_HDR", [DR_ACTION_TYP_ASO_FLOW_METER] = "DR_ACTION_TYP_ASO_FLOW_METER", + [DR_ACTION_TYP_RANGE] = "DR_ACTION_TYP_RANGE", [DR_ACTION_TYP_MAX] = "DR_ACTION_UNKNOWN", }; @@ -61,6 +62,7 @@ next_action_state[DR_ACTION_DOMAIN_MAX][DR_ACTION_STATE_MAX][DR_ACTION_TYP_MAX] [DR_ACTION_TYP_DROP] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_QP] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_FT] = DR_ACTION_STATE_TERM, + [DR_ACTION_TYP_RANGE] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_SAMPLER] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_TAG] = DR_ACTION_STATE_NON_TERM, [DR_ACTION_TYP_CTR] = DR_ACTION_STATE_NON_TERM, @@ -79,6 +81,7 @@ next_action_state[DR_ACTION_DOMAIN_MAX][DR_ACTION_STATE_MAX][DR_ACTION_TYP_MAX] [DR_ACTION_TYP_DROP] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_QP] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_FT] = DR_ACTION_STATE_TERM, + [DR_ACTION_TYP_RANGE] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_SAMPLER] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_TAG] = DR_ACTION_STATE_DECAP, [DR_ACTION_TYP_CTR] = DR_ACTION_STATE_DECAP, @@ -94,6 +97,7 @@ next_action_state[DR_ACTION_DOMAIN_MAX][DR_ACTION_STATE_MAX][DR_ACTION_TYP_MAX] [DR_ACTION_TYP_DROP] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_QP] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_FT] = DR_ACTION_STATE_TERM, + [DR_ACTION_TYP_RANGE] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_SAMPLER] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_TAG] = DR_ACTION_STATE_ENCAP, [DR_ACTION_TYP_CTR] = DR_ACTION_STATE_ENCAP, @@ -103,6 +107,7 @@ next_action_state[DR_ACTION_DOMAIN_MAX][DR_ACTION_STATE_MAX][DR_ACTION_TYP_MAX] [DR_ACTION_TYP_DROP] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_QP] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_FT] = DR_ACTION_STATE_TERM, + [DR_ACTION_TYP_RANGE] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_SAMPLER] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_TAG] = DR_ACTION_STATE_MODIFY_HDR, [DR_ACTION_TYP_CTR] = DR_ACTION_STATE_MODIFY_HDR, @@ -116,6 +121,7 @@ next_action_state[DR_ACTION_DOMAIN_MAX][DR_ACTION_STATE_MAX][DR_ACTION_TYP_MAX] [DR_ACTION_TYP_DROP] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_QP] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_FT] = DR_ACTION_STATE_TERM, + [DR_ACTION_TYP_RANGE] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_SAMPLER] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_TAG] = DR_ACTION_STATE_POP_VLAN, [DR_ACTION_TYP_CTR] = DR_ACTION_STATE_POP_VLAN, @@ -129,6 +135,7 @@ next_action_state[DR_ACTION_DOMAIN_MAX][DR_ACTION_STATE_MAX][DR_ACTION_TYP_MAX] [DR_ACTION_STATE_PUSH_VLAN] = { [DR_ACTION_TYP_QP] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_FT] = DR_ACTION_STATE_TERM, + [DR_ACTION_TYP_RANGE] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_SAMPLER] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_TAG] = DR_ACTION_STATE_PUSH_VLAN, [DR_ACTION_TYP_CTR] = DR_ACTION_STATE_PUSH_VLAN, @@ -141,6 +148,7 @@ next_action_state[DR_ACTION_DOMAIN_MAX][DR_ACTION_STATE_MAX][DR_ACTION_TYP_MAX] [DR_ACTION_TYP_DROP] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_QP] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_FT] = DR_ACTION_STATE_TERM, + [DR_ACTION_TYP_RANGE] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_SAMPLER] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_TAG] = DR_ACTION_STATE_NON_TERM, [DR_ACTION_TYP_CTR] = DR_ACTION_STATE_NON_TERM, @@ -159,6 +167,7 @@ next_action_state[DR_ACTION_DOMAIN_MAX][DR_ACTION_STATE_MAX][DR_ACTION_TYP_MAX] [DR_ACTION_TYP_DROP] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_QP] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_FT] = DR_ACTION_STATE_TERM, + [DR_ACTION_TYP_RANGE] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_CTR] = DR_ACTION_STATE_ASO, }, [DR_ACTION_STATE_TERM] = { @@ -169,6 +178,7 @@ next_action_state[DR_ACTION_DOMAIN_MAX][DR_ACTION_STATE_MAX][DR_ACTION_TYP_MAX] [DR_ACTION_STATE_NO_ACTION] = { [DR_ACTION_TYP_DROP] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_FT] = DR_ACTION_STATE_TERM, + [DR_ACTION_TYP_RANGE] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_SAMPLER] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_CTR] = DR_ACTION_STATE_NON_TERM, [DR_ACTION_TYP_L2_TO_TNL_L2] = DR_ACTION_STATE_ENCAP, @@ -183,6 +193,7 @@ next_action_state[DR_ACTION_DOMAIN_MAX][DR_ACTION_STATE_MAX][DR_ACTION_TYP_MAX] [DR_ACTION_STATE_DECAP] = { [DR_ACTION_TYP_DROP] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_FT] = DR_ACTION_STATE_TERM, + [DR_ACTION_TYP_RANGE] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_SAMPLER] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_CTR] = DR_ACTION_STATE_DECAP, [DR_ACTION_TYP_ASO_FLOW_METER] = DR_ACTION_STATE_ASO, @@ -190,6 +201,7 @@ next_action_state[DR_ACTION_DOMAIN_MAX][DR_ACTION_STATE_MAX][DR_ACTION_TYP_MAX] [DR_ACTION_STATE_ENCAP] = { [DR_ACTION_TYP_DROP] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_FT] = DR_ACTION_STATE_TERM, + [DR_ACTION_TYP_RANGE] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_SAMPLER] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_CTR] = DR_ACTION_STATE_ENCAP, [DR_ACTION_TYP_ASO_FLOW_METER] = DR_ACTION_STATE_ASO, @@ -197,6 +209,7 @@ next_action_state[DR_ACTION_DOMAIN_MAX][DR_ACTION_STATE_MAX][DR_ACTION_TYP_MAX] [DR_ACTION_STATE_MODIFY_HDR] = { [DR_ACTION_TYP_DROP] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_FT] = DR_ACTION_STATE_TERM, + [DR_ACTION_TYP_RANGE] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_SAMPLER] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_CTR] = DR_ACTION_STATE_MODIFY_HDR, [DR_ACTION_TYP_L2_TO_TNL_L2] = DR_ACTION_STATE_ENCAP, @@ -207,6 +220,7 @@ next_action_state[DR_ACTION_DOMAIN_MAX][DR_ACTION_STATE_MAX][DR_ACTION_TYP_MAX] }, [DR_ACTION_STATE_POP_VLAN] = { [DR_ACTION_TYP_FT] = DR_ACTION_STATE_TERM, + [DR_ACTION_TYP_RANGE] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_SAMPLER] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_CTR] = DR_ACTION_STATE_POP_VLAN, [DR_ACTION_TYP_POP_VLAN] = DR_ACTION_STATE_POP_VLAN, @@ -220,6 +234,7 @@ next_action_state[DR_ACTION_DOMAIN_MAX][DR_ACTION_STATE_MAX][DR_ACTION_TYP_MAX] [DR_ACTION_STATE_PUSH_VLAN] = { [DR_ACTION_TYP_DROP] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_FT] = DR_ACTION_STATE_TERM, + [DR_ACTION_TYP_RANGE] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_SAMPLER] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_CTR] = DR_ACTION_STATE_PUSH_VLAN, [DR_ACTION_TYP_PUSH_VLAN] = DR_ACTION_STATE_PUSH_VLAN, @@ -231,6 +246,7 @@ next_action_state[DR_ACTION_DOMAIN_MAX][DR_ACTION_STATE_MAX][DR_ACTION_TYP_MAX] [DR_ACTION_STATE_NON_TERM] = { [DR_ACTION_TYP_DROP] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_FT] = DR_ACTION_STATE_TERM, + [DR_ACTION_TYP_RANGE] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_SAMPLER] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_CTR] = DR_ACTION_STATE_NON_TERM, [DR_ACTION_TYP_L2_TO_TNL_L2] = DR_ACTION_STATE_ENCAP, @@ -250,6 +266,7 @@ next_action_state[DR_ACTION_DOMAIN_MAX][DR_ACTION_STATE_MAX][DR_ACTION_TYP_MAX] [DR_ACTION_TYP_CTR] = DR_ACTION_STATE_ASO, [DR_ACTION_TYP_DROP] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_FT] = DR_ACTION_STATE_TERM, + [DR_ACTION_TYP_RANGE] = DR_ACTION_STATE_TERM, }, [DR_ACTION_STATE_TERM] = { [DR_ACTION_TYP_CTR] = DR_ACTION_STATE_TERM, @@ -259,6 +276,7 @@ next_action_state[DR_ACTION_DOMAIN_MAX][DR_ACTION_STATE_MAX][DR_ACTION_TYP_MAX] [DR_ACTION_STATE_NO_ACTION] = { [DR_ACTION_TYP_DROP] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_FT] = DR_ACTION_STATE_TERM, + [DR_ACTION_TYP_RANGE] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_SAMPLER] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_CTR] = DR_ACTION_STATE_NON_TERM, [DR_ACTION_TYP_TNL_L2_TO_L2] = DR_ACTION_STATE_DECAP, @@ -276,6 +294,7 @@ next_action_state[DR_ACTION_DOMAIN_MAX][DR_ACTION_STATE_MAX][DR_ACTION_TYP_MAX] [DR_ACTION_STATE_DECAP] = { [DR_ACTION_TYP_DROP] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_FT] = DR_ACTION_STATE_TERM, + [DR_ACTION_TYP_RANGE] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_CTR] = DR_ACTION_STATE_DECAP, [DR_ACTION_TYP_SAMPLER] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_MODIFY_HDR] = DR_ACTION_STATE_MODIFY_HDR, @@ -291,6 +310,7 @@ next_action_state[DR_ACTION_DOMAIN_MAX][DR_ACTION_STATE_MAX][DR_ACTION_TYP_MAX] [DR_ACTION_TYP_DROP] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_QP] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_FT] = DR_ACTION_STATE_TERM, + [DR_ACTION_TYP_RANGE] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_VPORT] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_SAMPLER] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_CTR] = DR_ACTION_STATE_ENCAP, @@ -299,6 +319,7 @@ next_action_state[DR_ACTION_DOMAIN_MAX][DR_ACTION_STATE_MAX][DR_ACTION_TYP_MAX] [DR_ACTION_STATE_MODIFY_HDR] = { [DR_ACTION_TYP_DROP] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_FT] = DR_ACTION_STATE_TERM, + [DR_ACTION_TYP_RANGE] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_SAMPLER] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_CTR] = DR_ACTION_STATE_MODIFY_HDR, [DR_ACTION_TYP_VPORT] = DR_ACTION_STATE_TERM, @@ -311,6 +332,7 @@ next_action_state[DR_ACTION_DOMAIN_MAX][DR_ACTION_STATE_MAX][DR_ACTION_TYP_MAX] [DR_ACTION_STATE_POP_VLAN] = { [DR_ACTION_TYP_DROP] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_FT] = DR_ACTION_STATE_TERM, + [DR_ACTION_TYP_RANGE] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_SAMPLER] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_POP_VLAN] = DR_ACTION_STATE_POP_VLAN, [DR_ACTION_TYP_CTR] = DR_ACTION_STATE_POP_VLAN, @@ -324,6 +346,7 @@ next_action_state[DR_ACTION_DOMAIN_MAX][DR_ACTION_STATE_MAX][DR_ACTION_TYP_MAX] [DR_ACTION_STATE_PUSH_VLAN] = { [DR_ACTION_TYP_DROP] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_FT] = DR_ACTION_STATE_TERM, + [DR_ACTION_TYP_RANGE] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_SAMPLER] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_PUSH_VLAN] = DR_ACTION_STATE_PUSH_VLAN, [DR_ACTION_TYP_CTR] = DR_ACTION_STATE_PUSH_VLAN, @@ -337,6 +360,7 @@ next_action_state[DR_ACTION_DOMAIN_MAX][DR_ACTION_STATE_MAX][DR_ACTION_TYP_MAX] [DR_ACTION_STATE_NON_TERM] = { [DR_ACTION_TYP_DROP] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_FT] = DR_ACTION_STATE_TERM, + [DR_ACTION_TYP_RANGE] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_SAMPLER] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_CTR] = DR_ACTION_STATE_NON_TERM, [DR_ACTION_TYP_TNL_L2_TO_L2] = DR_ACTION_STATE_DECAP, @@ -354,6 +378,7 @@ next_action_state[DR_ACTION_DOMAIN_MAX][DR_ACTION_STATE_MAX][DR_ACTION_TYP_MAX] [DR_ACTION_STATE_ASO] = { [DR_ACTION_TYP_DROP] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_FT] = DR_ACTION_STATE_TERM, + [DR_ACTION_TYP_RANGE] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_VPORT] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_CTR] = DR_ACTION_STATE_ASO, }, @@ -365,6 +390,7 @@ next_action_state[DR_ACTION_DOMAIN_MAX][DR_ACTION_STATE_MAX][DR_ACTION_TYP_MAX] [DR_ACTION_STATE_NO_ACTION] = { [DR_ACTION_TYP_DROP] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_FT] = DR_ACTION_STATE_TERM, + [DR_ACTION_TYP_RANGE] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_SAMPLER] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_CTR] = DR_ACTION_STATE_NON_TERM, [DR_ACTION_TYP_MODIFY_HDR] = DR_ACTION_STATE_MODIFY_HDR, @@ -380,6 +406,7 @@ next_action_state[DR_ACTION_DOMAIN_MAX][DR_ACTION_STATE_MAX][DR_ACTION_TYP_MAX] [DR_ACTION_STATE_DECAP] = { [DR_ACTION_TYP_DROP] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_FT] = DR_ACTION_STATE_TERM, + [DR_ACTION_TYP_RANGE] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_CTR] = DR_ACTION_STATE_DECAP, [DR_ACTION_TYP_VPORT] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_SAMPLER] = DR_ACTION_STATE_TERM, @@ -388,6 +415,7 @@ next_action_state[DR_ACTION_DOMAIN_MAX][DR_ACTION_STATE_MAX][DR_ACTION_TYP_MAX] [DR_ACTION_STATE_ENCAP] = { [DR_ACTION_TYP_DROP] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_FT] = DR_ACTION_STATE_TERM, + [DR_ACTION_TYP_RANGE] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_CTR] = DR_ACTION_STATE_ENCAP, [DR_ACTION_TYP_SAMPLER] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_VPORT] = DR_ACTION_STATE_TERM, @@ -396,6 +424,7 @@ next_action_state[DR_ACTION_DOMAIN_MAX][DR_ACTION_STATE_MAX][DR_ACTION_TYP_MAX] [DR_ACTION_STATE_MODIFY_HDR] = { [DR_ACTION_TYP_DROP] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_FT] = DR_ACTION_STATE_TERM, + [DR_ACTION_TYP_RANGE] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_SAMPLER] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_CTR] = DR_ACTION_STATE_MODIFY_HDR, [DR_ACTION_TYP_L2_TO_TNL_L2] = DR_ACTION_STATE_ENCAP, @@ -407,6 +436,7 @@ next_action_state[DR_ACTION_DOMAIN_MAX][DR_ACTION_STATE_MAX][DR_ACTION_TYP_MAX] }, [DR_ACTION_STATE_POP_VLAN] = { [DR_ACTION_TYP_FT] = DR_ACTION_STATE_TERM, + [DR_ACTION_TYP_RANGE] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_SAMPLER] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_CTR] = DR_ACTION_STATE_POP_VLAN, [DR_ACTION_TYP_POP_VLAN] = DR_ACTION_STATE_POP_VLAN, @@ -421,6 +451,7 @@ next_action_state[DR_ACTION_DOMAIN_MAX][DR_ACTION_STATE_MAX][DR_ACTION_TYP_MAX] [DR_ACTION_STATE_PUSH_VLAN] = { [DR_ACTION_TYP_DROP] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_FT] = DR_ACTION_STATE_TERM, + [DR_ACTION_TYP_RANGE] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_SAMPLER] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_PUSH_VLAN] = DR_ACTION_STATE_PUSH_VLAN, [DR_ACTION_TYP_CTR] = DR_ACTION_STATE_PUSH_VLAN, @@ -433,6 +464,7 @@ next_action_state[DR_ACTION_DOMAIN_MAX][DR_ACTION_STATE_MAX][DR_ACTION_TYP_MAX] [DR_ACTION_STATE_NON_TERM] = { [DR_ACTION_TYP_DROP] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_FT] = DR_ACTION_STATE_TERM, + [DR_ACTION_TYP_RANGE] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_SAMPLER] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_CTR] = DR_ACTION_STATE_NON_TERM, [DR_ACTION_TYP_MODIFY_HDR] = DR_ACTION_STATE_MODIFY_HDR, @@ -452,6 +484,7 @@ next_action_state[DR_ACTION_DOMAIN_MAX][DR_ACTION_STATE_MAX][DR_ACTION_TYP_MAX] [DR_ACTION_TYP_PUSH_VLAN] = DR_ACTION_STATE_PUSH_VLAN, [DR_ACTION_TYP_DROP] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_FT] = DR_ACTION_STATE_TERM, + [DR_ACTION_TYP_RANGE] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_VPORT] = DR_ACTION_STATE_TERM, [DR_ACTION_TYP_CTR] = DR_ACTION_STATE_ASO, }, @@ -756,6 +789,23 @@ int mlx5dr_actions_build_ste_arr(struct mlx5dr_matcher *matcher, if (ret) return ret; break; + case DR_ACTION_TYP_RANGE: + ret = dr_action_get_dest_tbl_addr(matcher, + action->range->hit_tbl_action->dest_tbl, + rx_rule, &attr.final_icm_addr); + if (ret) + return ret; + + ret = dr_action_get_dest_tbl_addr(matcher, + action->range->miss_tbl_action->dest_tbl, + rx_rule, &attr.range.miss_icm_addr); + if (ret) + return ret; + + attr.range.definer_id = action->range->definer_id; + attr.range.min = action->range->min; + attr.range.max = action->range->max; + break; case DR_ACTION_TYP_QP: mlx5dr_info(dmn, "Domain doesn't support QP\n"); return -EOPNOTSUPP; @@ -901,6 +951,7 @@ static unsigned int action_size[DR_ACTION_TYP_MAX] = { [DR_ACTION_TYP_REMOVE_HDR] = sizeof(struct mlx5dr_action_reformat), [DR_ACTION_TYP_SAMPLER] = sizeof(struct mlx5dr_action_sampler), [DR_ACTION_TYP_ASO_FLOW_METER] = sizeof(struct mlx5dr_action_aso_flow_meter), + [DR_ACTION_TYP_RANGE] = sizeof(struct mlx5dr_action_range), }; static struct mlx5dr_action * @@ -968,6 +1019,123 @@ dec_ref: return NULL; } +static void dr_action_range_definer_fill(u16 *format_id, + u8 *dw_selectors, + u8 *byte_selectors, + u8 *match_mask) +{ + int i; + + *format_id = MLX5_IFC_DEFINER_FORMAT_ID_SELECT; + + dw_selectors[0] = MLX5_IFC_DEFINER_FORMAT_OFFSET_OUTER_ETH_PKT_LEN / 4; + + for (i = 1; i < MLX5_IFC_DEFINER_DW_SELECTORS_NUM; i++) + dw_selectors[i] = MLX5_IFC_DEFINER_FORMAT_OFFSET_UNUSED; + + for (i = 0; i < MLX5_IFC_DEFINER_BYTE_SELECTORS_NUM; i++) + byte_selectors[i] = MLX5_IFC_DEFINER_FORMAT_OFFSET_UNUSED; + + MLX5_SET(match_definer_match_mask, match_mask, + match_dw_0, 0xffffUL << 16); +} + +static int dr_action_create_range_definer(struct mlx5dr_action *action) +{ + u8 match_mask[MLX5_FLD_SZ_BYTES(match_definer, match_mask)] = {}; + u8 byte_selectors[MLX5_IFC_DEFINER_BYTE_SELECTORS_NUM] = {}; + u8 dw_selectors[MLX5_IFC_DEFINER_DW_SELECTORS_NUM] = {}; + struct mlx5dr_domain *dmn = action->range->dmn; + u32 definer_id; + u16 format_id; + int ret; + + dr_action_range_definer_fill(&format_id, + dw_selectors, + byte_selectors, + match_mask); + + ret = mlx5dr_definer_get(dmn, format_id, + dw_selectors, byte_selectors, + match_mask, &definer_id); + if (ret) + return ret; + + action->range->definer_id = definer_id; + return 0; +} + +static void dr_action_destroy_range_definer(struct mlx5dr_action *action) +{ + mlx5dr_definer_put(action->range->dmn, action->range->definer_id); +} + +struct mlx5dr_action * +mlx5dr_action_create_dest_match_range(struct mlx5dr_domain *dmn, + u32 field, + struct mlx5_flow_table *hit_ft, + struct mlx5_flow_table *miss_ft, + u32 min, + u32 max) +{ + struct mlx5dr_action *action; + int ret; + + if (!mlx5dr_supp_match_ranges(dmn->mdev)) { + mlx5dr_dbg(dmn, "SELECT definer support is needed for match range\n"); + return NULL; + } + + if (field != MLX5_FLOW_DEST_RANGE_FIELD_PKT_LEN || + min > 0xffff || max > 0xffff) { + mlx5dr_err(dmn, "Invalid match range parameters\n"); + return NULL; + } + + action = dr_action_create_generic(DR_ACTION_TYP_RANGE); + if (!action) + return NULL; + + action->range->hit_tbl_action = + mlx5dr_is_fw_table(hit_ft) ? + mlx5dr_action_create_dest_flow_fw_table(dmn, hit_ft) : + mlx5dr_action_create_dest_table(hit_ft->fs_dr_table.dr_table); + + if (!action->range->hit_tbl_action) + goto free_action; + + action->range->miss_tbl_action = + mlx5dr_is_fw_table(miss_ft) ? + mlx5dr_action_create_dest_flow_fw_table(dmn, miss_ft) : + mlx5dr_action_create_dest_table(miss_ft->fs_dr_table.dr_table); + + if (!action->range->miss_tbl_action) + goto free_hit_tbl_action; + + action->range->min = min; + action->range->max = max; + action->range->dmn = dmn; + + ret = dr_action_create_range_definer(action); + if (ret) + goto free_miss_tbl_action; + + /* No need to increase refcount on domain for this action, + * the hit/miss table actions will do it internally. + */ + + return action; + +free_miss_tbl_action: + mlx5dr_action_destroy(action->range->miss_tbl_action); +free_hit_tbl_action: + mlx5dr_action_destroy(action->range->hit_tbl_action); +free_action: + kfree(action); + + return NULL; +} + struct mlx5dr_action * mlx5dr_action_create_mult_dest_tbl(struct mlx5dr_domain *dmn, struct mlx5dr_action_dest *dests, @@ -2015,6 +2183,11 @@ int mlx5dr_action_destroy(struct mlx5dr_action *action) case DR_ACTION_TYP_ASO_FLOW_METER: refcount_dec(&action->aso->dmn->refcount); break; + case DR_ACTION_TYP_RANGE: + dr_action_destroy_range_definer(action); + mlx5dr_action_destroy(action->range->miss_tbl_action); + mlx5dr_action_destroy(action->range->hit_tbl_action); + break; default: break; } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_dbg.c b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_dbg.c index 7adcf0eec13b..db81d881d38e 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_dbg.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_dbg.c @@ -49,7 +49,8 @@ enum dr_dump_rec_type { DR_DUMP_REC_TYPE_ACTION_POP_VLAN = 3413, DR_DUMP_REC_TYPE_ACTION_SAMPLER = 3415, DR_DUMP_REC_TYPE_ACTION_INSERT_HDR = 3420, - DR_DUMP_REC_TYPE_ACTION_REMOVE_HDR = 3421 + DR_DUMP_REC_TYPE_ACTION_REMOVE_HDR = 3421, + DR_DUMP_REC_TYPE_ACTION_MATCH_RANGE = 3425, }; void mlx5dr_dbg_tbl_add(struct mlx5dr_table *tbl) @@ -107,6 +108,8 @@ dr_dump_rule_action_mem(struct seq_file *file, const u64 rule_id, { struct mlx5dr_action *action = action_mem->action; const u64 action_id = DR_DBG_PTR_TO_ID(action); + u64 hit_tbl_ptr, miss_tbl_ptr; + u32 hit_tbl_id, miss_tbl_id; switch (action->action_type) { case DR_ACTION_TYP_DROP: @@ -198,6 +201,30 @@ dr_dump_rule_action_mem(struct seq_file *file, const u64 rule_id, action->sampler->rx_icm_addr, action->sampler->tx_icm_addr); break; + case DR_ACTION_TYP_RANGE: + if (action->range->hit_tbl_action->dest_tbl->is_fw_tbl) { + hit_tbl_id = action->range->hit_tbl_action->dest_tbl->fw_tbl.id; + hit_tbl_ptr = 0; + } else { + hit_tbl_id = action->range->hit_tbl_action->dest_tbl->tbl->table_id; + hit_tbl_ptr = + DR_DBG_PTR_TO_ID(action->range->hit_tbl_action->dest_tbl->tbl); + } + + if (action->range->miss_tbl_action->dest_tbl->is_fw_tbl) { + miss_tbl_id = action->range->miss_tbl_action->dest_tbl->fw_tbl.id; + miss_tbl_ptr = 0; + } else { + miss_tbl_id = action->range->miss_tbl_action->dest_tbl->tbl->table_id; + miss_tbl_ptr = + DR_DBG_PTR_TO_ID(action->range->miss_tbl_action->dest_tbl->tbl); + } + + seq_printf(file, "%d,0x%llx,0x%llx,0x%x,0x%llx,0x%x,0x%llx,0x%x\n", + DR_DUMP_REC_TYPE_ACTION_MATCH_RANGE, action_id, rule_id, + hit_tbl_id, hit_tbl_ptr, miss_tbl_id, miss_tbl_ptr, + action->range->definer_id); + break; default: return 0; } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_ste_v1.c b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_ste_v1.c index 87c29118c51b..084145f18084 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_ste_v1.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_ste_v1.c @@ -13,6 +13,7 @@ enum dr_ste_v1_entry_format { DR_STE_V1_TYPE_BWC_BYTE = 0x0, DR_STE_V1_TYPE_BWC_DW = 0x1, DR_STE_V1_TYPE_MATCH = 0x2, + DR_STE_V1_TYPE_MATCH_RANGES = 0x7, }; /* Lookup type is built from 2B: [ Definer mode 1B ][ Definer index 1B ] */ @@ -269,7 +270,12 @@ static void dr_ste_v1_set_entry_type(u8 *hw_ste_p, u8 entry_type) bool dr_ste_v1_is_miss_addr_set(u8 *hw_ste_p) { - return false; + u8 entry_type = MLX5_GET(ste_match_bwc_v1, hw_ste_p, entry_format); + + /* unlike MATCH STE, for MATCH_RANGES STE both hit and miss addresses + * are part of the action, so they both set as part of STE init + */ + return entry_type == DR_STE_V1_TYPE_MATCH_RANGES; } void dr_ste_v1_set_miss_addr(u8 *hw_ste_p, u64 miss_addr) @@ -525,6 +531,27 @@ static void dr_ste_v1_set_aso_flow_meter(u8 *d_action, init_color); } +static void dr_ste_v1_set_match_range_pkt_len(u8 *hw_ste_p, u32 definer_id, + u32 min, u32 max) +{ + MLX5_SET(ste_match_ranges_v1, hw_ste_p, match_definer_ctx_idx, definer_id); + + /* When the STE will be sent, its mask and tags will be swapped in + * dr_ste_v1_prepare_for_postsend(). This, however, is match range STE + * which doesn't have mask, and shouldn't have mask/tag swapped. + * We're using the common utilities functions to send this STE, so need + * to allow for this swapping - place the values in the corresponding + * locations to allow flipping them when writing to ICM. + * + * min/max_value_2 corresponds to match_dw_0 in its definer. + * To allow mask/tag swapping, writing the min/max_2 to min/max_0. + * + * Pkt len is 2 bytes that are stored in the higher section of the DW. + */ + MLX5_SET(ste_match_ranges_v1, hw_ste_p, min_value_0, min << 16); + MLX5_SET(ste_match_ranges_v1, hw_ste_p, max_value_0, max << 16); +} + static void dr_ste_v1_arr_init_next_match(u8 **last_ste, u32 *added_stes, u16 gvmi) @@ -540,6 +567,14 @@ static void dr_ste_v1_arr_init_next_match(u8 **last_ste, memset(action, 0, MLX5_FLD_SZ_BYTES(ste_mask_and_match_v1, action)); } +static void dr_ste_v1_arr_init_next_match_range(u8 **last_ste, + u32 *added_stes, + u16 gvmi) +{ + dr_ste_v1_arr_init_next_match(last_ste, added_stes, gvmi); + dr_ste_v1_set_entry_type(*last_ste, DR_STE_V1_TYPE_MATCH_RANGES); +} + void dr_ste_v1_set_actions_tx(struct mlx5dr_domain *dmn, u8 *action_type_set, u32 actions_caps, @@ -675,6 +710,20 @@ void dr_ste_v1_set_actions_tx(struct mlx5dr_domain *dmn, action += DR_STE_ACTION_DOUBLE_SZ; } + if (action_type_set[DR_ACTION_TYP_RANGE]) { + /* match ranges requires a new STE of its own type */ + dr_ste_v1_arr_init_next_match_range(&last_ste, added_stes, attr->gvmi); + dr_ste_v1_set_miss_addr(last_ste, attr->range.miss_icm_addr); + + /* we do not support setting any action on the match ranges STE */ + action_sz = 0; + + dr_ste_v1_set_match_range_pkt_len(last_ste, + attr->range.definer_id, + attr->range.min, + attr->range.max); + } + dr_ste_v1_set_hit_gvmi(last_ste, attr->hit_gvmi); dr_ste_v1_set_hit_addr(last_ste, attr->final_icm_addr, 1); } @@ -863,6 +912,20 @@ void dr_ste_v1_set_actions_rx(struct mlx5dr_domain *dmn, action += DR_STE_ACTION_DOUBLE_SZ; } + if (action_type_set[DR_ACTION_TYP_RANGE]) { + /* match ranges requires a new STE of its own type */ + dr_ste_v1_arr_init_next_match_range(&last_ste, added_stes, attr->gvmi); + dr_ste_v1_set_miss_addr(last_ste, attr->range.miss_icm_addr); + + /* we do not support setting any action on the match ranges STE */ + action_sz = 0; + + dr_ste_v1_set_match_range_pkt_len(last_ste, + attr->range.definer_id, + attr->range.min, + attr->range.max); + } + dr_ste_v1_set_hit_gvmi(last_ste, attr->hit_gvmi); dr_ste_v1_set_hit_addr(last_ste, attr->final_icm_addr, 1); } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_types.h b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_types.h index a3e8ce415563..2b769dcbd453 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_types.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_types.h @@ -129,6 +129,7 @@ enum mlx5dr_action_type { DR_ACTION_TYP_REMOVE_HDR, DR_ACTION_TYP_SAMPLER, DR_ACTION_TYP_ASO_FLOW_METER, + DR_ACTION_TYP_RANGE, DR_ACTION_TYP_MAX, }; @@ -283,6 +284,13 @@ struct mlx5dr_ste_actions_attr { u8 dest_reg_id; u8 init_color; } aso_flow_meter; + + struct { + u64 miss_icm_addr; + u32 definer_id; + u32 min; + u32 max; + } range; }; void mlx5dr_ste_set_actions_rx(struct mlx5dr_ste_ctx *ste_ctx, @@ -1029,6 +1037,15 @@ struct mlx5dr_action_dest_tbl { }; }; +struct mlx5dr_action_range { + struct mlx5dr_domain *dmn; + struct mlx5dr_action *hit_tbl_action; + struct mlx5dr_action *miss_tbl_action; + u32 definer_id; + u32 min; + u32 max; +}; + struct mlx5dr_action_ctr { u32 ctr_id; u32 offset; @@ -1075,6 +1092,7 @@ struct mlx5dr_action { struct mlx5dr_action_push_vlan *push_vlan; struct mlx5dr_action_flow_tag *flow_tag; struct mlx5dr_action_aso_flow_meter *aso; + struct mlx5dr_action_range *range; }; }; @@ -1500,4 +1518,12 @@ static inline bool mlx5dr_is_fw_table(struct mlx5_flow_table *ft) return !ft->fs_dr_table.dr_table; } +static inline bool mlx5dr_supp_match_ranges(struct mlx5_core_dev *dev) +{ + return (MLX5_CAP_GEN(dev, steering_format_version) >= + MLX5_STEERING_FORMAT_CONNECTX_6DX) && + (MLX5_CAP_GEN_64(dev, match_definer_format_supported) & + (1ULL << MLX5_IFC_DEFINER_FORMAT_ID_SELECT)); +} + #endif /* _DR_TYPES_H_ */ diff --git a/drivers/net/ethernet/mellanox/mlx5/core/steering/fs_dr.c b/drivers/net/ethernet/mellanox/mlx5/core/steering/fs_dr.c index c78fb016c245..984653756779 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/steering/fs_dr.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/steering/fs_dr.c @@ -215,6 +215,17 @@ static struct mlx5dr_action *create_ft_action(struct mlx5dr_domain *domain, return mlx5dr_action_create_dest_table(dest_ft->fs_dr_table.dr_table); } +static struct mlx5dr_action *create_range_action(struct mlx5dr_domain *domain, + struct mlx5_flow_rule *dst) +{ + return mlx5dr_action_create_dest_match_range(domain, + dst->dest_attr.range.field, + dst->dest_attr.range.hit_ft, + dst->dest_attr.range.miss_ft, + dst->dest_attr.range.min, + dst->dest_attr.range.max); +} + static struct mlx5dr_action *create_action_push_vlan(struct mlx5dr_domain *domain, struct mlx5_fs_vlan *vlan) { @@ -468,6 +479,15 @@ static int mlx5_cmd_dr_create_fte(struct mlx5_flow_root_namespace *ns, fs_dr_actions[fs_dr_num_actions++] = tmp_action; term_actions[num_term_actions++].dest = tmp_action; break; + case MLX5_FLOW_DESTINATION_TYPE_RANGE: + tmp_action = create_range_action(domain, dst); + if (!tmp_action) { + err = -ENOMEM; + goto free_actions; + } + fs_dr_actions[fs_dr_num_actions++] = tmp_action; + term_actions[num_term_actions++].dest = tmp_action; + break; default: err = -EOPNOTSUPP; goto free_actions; @@ -781,11 +801,19 @@ static int mlx5_cmd_dr_destroy_ns(struct mlx5_flow_root_namespace *ns) static u32 mlx5_cmd_dr_get_capabilities(struct mlx5_flow_root_namespace *ns, enum fs_flow_table_type ft_type) { + u32 steering_caps = 0; + if (ft_type != FS_FT_FDB || MLX5_CAP_GEN(ns->dev, steering_format_version) == MLX5_STEERING_FORMAT_CONNECTX_5) return 0; - return MLX5_FLOW_STEERING_CAP_VLAN_PUSH_ON_RX | MLX5_FLOW_STEERING_CAP_VLAN_POP_ON_TX; + steering_caps |= MLX5_FLOW_STEERING_CAP_VLAN_PUSH_ON_RX; + steering_caps |= MLX5_FLOW_STEERING_CAP_VLAN_POP_ON_TX; + + if (mlx5dr_supp_match_ranges(ns->dev)) + steering_caps |= MLX5_FLOW_STEERING_CAP_MATCH_RANGES; + + return steering_caps; } bool mlx5_fs_dr_is_supported(struct mlx5_core_dev *dev) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/steering/mlx5_ifc_dr_ste_v1.h b/drivers/net/ethernet/mellanox/mlx5/core/steering/mlx5_ifc_dr_ste_v1.h index 34c2bd17a8b4..790a17d6207f 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/steering/mlx5_ifc_dr_ste_v1.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/steering/mlx5_ifc_dr_ste_v1.h @@ -165,6 +165,41 @@ struct mlx5_ifc_ste_mask_and_match_v1_bits { u8 action[0x60]; }; +struct mlx5_ifc_ste_match_ranges_v1_bits { + u8 entry_format[0x8]; + u8 counter_id[0x18]; + + u8 miss_address_63_48[0x10]; + u8 match_definer_ctx_idx[0x8]; + u8 miss_address_39_32[0x8]; + + u8 miss_address_31_6[0x1a]; + u8 reserved_at_5a[0x1]; + u8 match_polarity[0x1]; + u8 reparse[0x1]; + u8 reserved_at_5d[0x3]; + + u8 next_table_base_63_48[0x10]; + u8 hash_definer_ctx_idx[0x8]; + u8 next_table_base_39_32_size[0x8]; + + u8 next_table_base_31_5_size[0x1b]; + u8 hash_type[0x2]; + u8 hash_after_actions[0x1]; + u8 reserved_at_9e[0x2]; + + u8 action[0x60]; + + u8 max_value_0[0x20]; + u8 min_value_0[0x20]; + u8 max_value_1[0x20]; + u8 min_value_1[0x20]; + u8 max_value_2[0x20]; + u8 min_value_2[0x20]; + u8 max_value_3[0x20]; + u8 min_value_3[0x20]; +}; + struct mlx5_ifc_ste_eth_l2_src_v1_bits { u8 reserved_at_0[0x1]; u8 sx_sniffer[0x1]; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/steering/mlx5dr.h b/drivers/net/ethernet/mellanox/mlx5/core/steering/mlx5dr.h index 6ea50436ea61..9afd268a2573 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/steering/mlx5dr.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/steering/mlx5dr.h @@ -140,6 +140,14 @@ mlx5dr_action_create_aso(struct mlx5dr_domain *dmn, u8 init_color, u8 meter_id); +struct mlx5dr_action * +mlx5dr_action_create_dest_match_range(struct mlx5dr_domain *dmn, + u32 field, + struct mlx5_flow_table *hit_ft, + struct mlx5_flow_table *miss_ft, + u32 min, + u32 max); + int mlx5dr_action_destroy(struct mlx5dr_action *action); int mlx5dr_definer_get(struct mlx5dr_domain *dmn, u16 format_id, From fd6fa761466cef5279a399ca6f21d8f84d5b2332 Mon Sep 17 00:00:00 2001 From: Oz Shlomo Date: Thu, 1 Dec 2022 12:35:36 +0000 Subject: [PATCH 1777/2002] net/mlx5e: meter, refactor to allow multiple post meter tables TC police action may configure the maximum packet size to be handled by the policer, in addition to byte/packet rate. Currently the post meter table steers the packet according to the meter aso output. Refactor the code to allow both metering and range post actions as a pre-step for adding police mtu offload support. Signed-off-by: Oz Shlomo Reviewed-by: Roi Dayan Signed-off-by: Saeed Mahameed --- .../mellanox/mlx5/core/en/tc/post_meter.c | 154 +++++++++++------- 1 file changed, 96 insertions(+), 58 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/tc/post_meter.c b/drivers/net/ethernet/mellanox/mlx5/core/en/tc/post_meter.c index c38211097746..7b2387eef34c 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/tc/post_meter.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/tc/post_meter.c @@ -8,7 +8,7 @@ #define MLX5_PACKET_COLOR_BITS MLX5_REG_MAPPING_MBITS(PACKET_COLOR_TO_REG) #define MLX5_PACKET_COLOR_MASK MLX5_REG_MAPPING_MASK(PACKET_COLOR_TO_REG) -struct mlx5e_post_meter_priv { +struct mlx5e_post_meter_rate_table { struct mlx5_flow_table *ft; struct mlx5_flow_group *fg; struct mlx5_flow_handle *green_rule; @@ -17,16 +17,19 @@ struct mlx5e_post_meter_priv { struct mlx5_flow_attr *red_attr; }; +struct mlx5e_post_meter_priv { + struct mlx5e_post_meter_rate_table rate_steering_table; +}; + struct mlx5_flow_table * mlx5e_post_meter_get_ft(struct mlx5e_post_meter_priv *post_meter) { - return post_meter->ft; + return post_meter->rate_steering_table.ft; } -static int +static struct mlx5_flow_table * mlx5e_post_meter_table_create(struct mlx5e_priv *priv, - enum mlx5_flow_namespace_type ns_type, - struct mlx5e_post_meter_priv *post_meter) + enum mlx5_flow_namespace_type ns_type) { struct mlx5_flow_table_attr ft_attr = {}; struct mlx5_flow_namespace *root_ns; @@ -34,7 +37,7 @@ mlx5e_post_meter_table_create(struct mlx5e_priv *priv, root_ns = mlx5_get_flow_namespace(priv->mdev, ns_type); if (!root_ns) { mlx5_core_warn(priv->mdev, "Failed to get namespace for flow meter\n"); - return -EOPNOTSUPP; + return ERR_PTR(-EOPNOTSUPP); } ft_attr.flags = MLX5_FLOW_TABLE_UNMANAGED; @@ -42,19 +45,14 @@ mlx5e_post_meter_table_create(struct mlx5e_priv *priv, ft_attr.max_fte = 2; ft_attr.level = 1; - post_meter->ft = mlx5_create_flow_table(root_ns, &ft_attr); - if (IS_ERR(post_meter->ft)) { - mlx5_core_warn(priv->mdev, "Failed to create post_meter table\n"); - return PTR_ERR(post_meter->ft); - } - - return 0; + return mlx5_create_flow_table(root_ns, &ft_attr); } static int -mlx5e_post_meter_fg_create(struct mlx5e_priv *priv, - struct mlx5e_post_meter_priv *post_meter) +mlx5e_post_meter_rate_fg_create(struct mlx5e_priv *priv, + struct mlx5e_post_meter_priv *post_meter) { + struct mlx5e_post_meter_rate_table *table = &post_meter->rate_steering_table; int inlen = MLX5_ST_SZ_BYTES(create_flow_group_in); void *misc2, *match_criteria; u32 *flow_group_in; @@ -73,10 +71,10 @@ mlx5e_post_meter_fg_create(struct mlx5e_priv *priv, MLX5_SET(create_flow_group_in, flow_group_in, start_flow_index, 0); MLX5_SET(create_flow_group_in, flow_group_in, end_flow_index, 1); - post_meter->fg = mlx5_create_flow_group(post_meter->ft, flow_group_in); - if (IS_ERR(post_meter->fg)) { + table->fg = mlx5_create_flow_group(table->ft, flow_group_in); + if (IS_ERR(table->fg)) { mlx5_core_warn(priv->mdev, "Failed to create post_meter flow group\n"); - err = PTR_ERR(post_meter->fg); + err = PTR_ERR(table->fg); } kvfree(flow_group_in); @@ -100,7 +98,7 @@ mlx5e_post_meter_add_rule(struct mlx5e_priv *priv, else attr->counter = act_counter; - attr->ft = post_meter->ft; + attr->ft = post_meter->rate_steering_table.ft; attr->flags |= MLX5_ATTR_FLAG_NO_IN_PORT; attr->outer_match_level = MLX5_MATCH_NONE; attr->chain = 0; @@ -117,14 +115,15 @@ mlx5e_post_meter_add_rule(struct mlx5e_priv *priv, } static int -mlx5e_post_meter_rules_create(struct mlx5e_priv *priv, - struct mlx5e_post_meter_priv *post_meter, - struct mlx5e_post_act *post_act, - struct mlx5_fc *act_counter, - struct mlx5_fc *drop_counter, - struct mlx5_flow_attr *green_attr, - struct mlx5_flow_attr *red_attr) +mlx5e_post_meter_rate_rules_create(struct mlx5e_priv *priv, + struct mlx5e_post_meter_priv *post_meter, + struct mlx5e_post_act *post_act, + struct mlx5_fc *act_counter, + struct mlx5_fc *drop_counter, + struct mlx5_flow_attr *green_attr, + struct mlx5_flow_attr *red_attr) { + struct mlx5e_post_meter_rate_table *table = &post_meter->rate_steering_table; struct mlx5_flow_handle *rule; struct mlx5_flow_spec *spec; int err; @@ -143,8 +142,8 @@ mlx5e_post_meter_rules_create(struct mlx5e_priv *priv, err = PTR_ERR(rule); goto err_red; } - post_meter->red_rule = rule; - post_meter->red_attr = red_attr; + table->red_rule = rule; + table->red_attr = red_attr; mlx5e_tc_match_to_reg_match(spec, PACKET_COLOR_TO_REG, MLX5_FLOW_METER_COLOR_GREEN, MLX5_PACKET_COLOR_MASK); @@ -155,37 +154,81 @@ mlx5e_post_meter_rules_create(struct mlx5e_priv *priv, err = PTR_ERR(rule); goto err_green; } - post_meter->green_rule = rule; - post_meter->green_attr = green_attr; + table->green_rule = rule; + table->green_attr = green_attr; kvfree(spec); return 0; err_green: - mlx5_del_flow_rules(post_meter->red_rule); + mlx5_del_flow_rules(table->red_rule); err_red: kvfree(spec); return err; } static void -mlx5e_post_meter_rules_destroy(struct mlx5_eswitch *esw, - struct mlx5e_post_meter_priv *post_meter) +mlx5e_post_meter_rate_rules_destroy(struct mlx5_eswitch *esw, + struct mlx5e_post_meter_priv *post_meter) { - mlx5_eswitch_del_offloaded_rule(esw, post_meter->red_rule, post_meter->red_attr); - mlx5_eswitch_del_offloaded_rule(esw, post_meter->green_rule, post_meter->green_attr); + struct mlx5e_post_meter_rate_table *rate_table = &post_meter->rate_steering_table; + + mlx5_eswitch_del_offloaded_rule(esw, rate_table->red_rule, rate_table->red_attr); + mlx5_eswitch_del_offloaded_rule(esw, rate_table->green_rule, rate_table->green_attr); } static void -mlx5e_post_meter_fg_destroy(struct mlx5e_post_meter_priv *post_meter) +mlx5e_post_meter_rate_fg_destroy(struct mlx5e_post_meter_priv *post_meter) { - mlx5_destroy_flow_group(post_meter->fg); + mlx5_destroy_flow_group(post_meter->rate_steering_table.fg); } static void -mlx5e_post_meter_table_destroy(struct mlx5e_post_meter_priv *post_meter) +mlx5e_post_meter_rate_table_destroy(struct mlx5e_post_meter_priv *post_meter) { - mlx5_destroy_flow_table(post_meter->ft); + mlx5_destroy_flow_table(post_meter->rate_steering_table.ft); +} + +static int +mlx5e_post_meter_rate_create(struct mlx5e_priv *priv, + enum mlx5_flow_namespace_type ns_type, + struct mlx5e_post_act *post_act, + struct mlx5_fc *act_counter, + struct mlx5_fc *drop_counter, + struct mlx5e_post_meter_priv *post_meter, + struct mlx5_flow_attr *green_attr, + struct mlx5_flow_attr *red_attr) +{ + struct mlx5_flow_table *ft; + int err; + + ft = mlx5e_post_meter_table_create(priv, ns_type); + if (IS_ERR(ft)) { + err = PTR_ERR(ft); + mlx5_core_warn(priv->mdev, "Failed to create post_meter table\n"); + goto err_ft; + } + + post_meter->rate_steering_table.ft = ft; + + err = mlx5e_post_meter_rate_fg_create(priv, post_meter); + if (err) + goto err_fg; + + err = mlx5e_post_meter_rate_rules_create(priv, post_meter, post_act, + act_counter, drop_counter, + green_attr, red_attr); + if (err) + goto err_rules; + + return 0; + +err_rules: + mlx5e_post_meter_rate_fg_destroy(post_meter); +err_fg: + mlx5e_post_meter_rate_table_destroy(post_meter); +err_ft: + return err; } struct mlx5e_post_meter_priv * @@ -204,36 +247,31 @@ mlx5e_post_meter_init(struct mlx5e_priv *priv, if (!post_meter) return ERR_PTR(-ENOMEM); - err = mlx5e_post_meter_table_create(priv, ns_type, post_meter); + err = mlx5e_post_meter_rate_create(priv, ns_type, post_act, + act_counter, drop_counter, post_meter, + branch_true, branch_false); if (err) - goto err_ft; - - err = mlx5e_post_meter_fg_create(priv, post_meter); - if (err) - goto err_fg; - - err = mlx5e_post_meter_rules_create(priv, post_meter, post_act, act_counter, - drop_counter, branch_true, branch_false); - if (err) - goto err_rules; + goto err; return post_meter; -err_rules: - mlx5e_post_meter_fg_destroy(post_meter); -err_fg: - mlx5e_post_meter_table_destroy(post_meter); -err_ft: +err: kfree(post_meter); return ERR_PTR(err); } +static void +mlx5e_post_meter_rate_destroy(struct mlx5_eswitch *esw, struct mlx5e_post_meter_priv *post_meter) +{ + mlx5e_post_meter_rate_rules_destroy(esw, post_meter); + mlx5e_post_meter_rate_fg_destroy(post_meter); + mlx5e_post_meter_rate_table_destroy(post_meter); +} + void mlx5e_post_meter_cleanup(struct mlx5_eswitch *esw, struct mlx5e_post_meter_priv *post_meter) { - mlx5e_post_meter_rules_destroy(esw, post_meter); - mlx5e_post_meter_fg_destroy(post_meter); - mlx5e_post_meter_table_destroy(post_meter); + mlx5e_post_meter_rate_destroy(esw, post_meter); kfree(post_meter); } From d56713250a59aba48428345f80e1bff9dde897e1 Mon Sep 17 00:00:00 2001 From: Oz Shlomo Date: Wed, 24 Aug 2022 12:45:43 +0000 Subject: [PATCH 1778/2002] net/mlx5e: meter, add mtu post meter tables TC police action may configure the maximum packet size to be handled by the policer, in addition to byte/packet rate. MTU check is realized in hardware using the range destination, specifying a hit ft, if packet len is in the range, or miss ft otherwise. Instantiate mtu green/red flow tables with a single match-all rule. Add the green/red actions to the hit/miss table accordingly. Signed-off-by: Oz Shlomo Reviewed-by: Roi Dayan Signed-off-by: Saeed Mahameed --- .../mellanox/mlx5/core/en/tc/post_meter.c | 184 +++++++++++++++++- .../mellanox/mlx5/core/en/tc/post_meter.h | 7 + .../net/ethernet/mellanox/mlx5/core/en_tc.c | 1 + 3 files changed, 185 insertions(+), 7 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/tc/post_meter.c b/drivers/net/ethernet/mellanox/mlx5/core/en/tc/post_meter.c index 7b2387eef34c..ffed3af7d01e 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/tc/post_meter.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/tc/post_meter.c @@ -17,8 +17,24 @@ struct mlx5e_post_meter_rate_table { struct mlx5_flow_attr *red_attr; }; +struct mlx5e_post_meter_mtu_table { + struct mlx5_flow_table *ft; + struct mlx5_flow_group *fg; + struct mlx5_flow_handle *rule; + struct mlx5_flow_attr *attr; +}; + +struct mlx5e_post_meter_mtu_tables { + struct mlx5e_post_meter_mtu_table green_table; + struct mlx5e_post_meter_mtu_table red_table; +}; + struct mlx5e_post_meter_priv { - struct mlx5e_post_meter_rate_table rate_steering_table; + enum mlx5e_post_meter_type type; + union { + struct mlx5e_post_meter_rate_table rate_steering_table; + struct mlx5e_post_meter_mtu_tables mtu_tables; + }; }; struct mlx5_flow_table * @@ -98,7 +114,6 @@ mlx5e_post_meter_add_rule(struct mlx5e_priv *priv, else attr->counter = act_counter; - attr->ft = post_meter->rate_steering_table.ft; attr->flags |= MLX5_ATTR_FLAG_NO_IN_PORT; attr->outer_match_level = MLX5_MATCH_NONE; attr->chain = 0; @@ -134,7 +149,7 @@ mlx5e_post_meter_rate_rules_create(struct mlx5e_priv *priv, mlx5e_tc_match_to_reg_match(spec, PACKET_COLOR_TO_REG, MLX5_FLOW_METER_COLOR_RED, MLX5_PACKET_COLOR_MASK); - + red_attr->ft = post_meter->rate_steering_table.ft; rule = mlx5e_post_meter_add_rule(priv, post_meter, spec, red_attr, act_counter, drop_counter); if (IS_ERR(rule)) { @@ -147,6 +162,7 @@ mlx5e_post_meter_rate_rules_create(struct mlx5e_priv *priv, mlx5e_tc_match_to_reg_match(spec, PACKET_COLOR_TO_REG, MLX5_FLOW_METER_COLOR_GREEN, MLX5_PACKET_COLOR_MASK); + green_attr->ft = post_meter->rate_steering_table.ft; rule = mlx5e_post_meter_add_rule(priv, post_meter, spec, green_attr, act_counter, drop_counter); if (IS_ERR(rule)) { @@ -189,6 +205,33 @@ mlx5e_post_meter_rate_table_destroy(struct mlx5e_post_meter_priv *post_meter) mlx5_destroy_flow_table(post_meter->rate_steering_table.ft); } +static void +mlx5e_post_meter_mtu_rules_destroy(struct mlx5e_post_meter_priv *post_meter) +{ + struct mlx5e_post_meter_mtu_tables *mtu_tables = &post_meter->mtu_tables; + + mlx5_del_flow_rules(mtu_tables->green_table.rule); + mlx5_del_flow_rules(mtu_tables->red_table.rule); +} + +static void +mlx5e_post_meter_mtu_fg_destroy(struct mlx5e_post_meter_priv *post_meter) +{ + struct mlx5e_post_meter_mtu_tables *mtu_tables = &post_meter->mtu_tables; + + mlx5_destroy_flow_group(mtu_tables->green_table.fg); + mlx5_destroy_flow_group(mtu_tables->red_table.fg); +} + +static void +mlx5e_post_meter_mtu_table_destroy(struct mlx5e_post_meter_priv *post_meter) +{ + struct mlx5e_post_meter_mtu_tables *mtu_tables = &post_meter->mtu_tables; + + mlx5_destroy_flow_table(mtu_tables->green_table.ft); + mlx5_destroy_flow_table(mtu_tables->red_table.ft); +} + static int mlx5e_post_meter_rate_create(struct mlx5e_priv *priv, enum mlx5_flow_namespace_type ns_type, @@ -202,6 +245,8 @@ mlx5e_post_meter_rate_create(struct mlx5e_priv *priv, struct mlx5_flow_table *ft; int err; + post_meter->type = MLX5E_POST_METER_RATE; + ft = mlx5e_post_meter_table_create(priv, ns_type); if (IS_ERR(ft)) { err = PTR_ERR(ft); @@ -231,10 +276,111 @@ err_ft: return err; } +static int +mlx5e_post_meter_create_mtu_table(struct mlx5e_priv *priv, + enum mlx5_flow_namespace_type ns_type, + struct mlx5e_post_meter_mtu_table *table) +{ + int inlen = MLX5_ST_SZ_BYTES(create_flow_group_in); + struct mlx5_flow_group *fg; + u32 *flow_group_in; + int err; + + flow_group_in = kvzalloc(inlen, GFP_KERNEL); + if (!flow_group_in) + return -ENOMEM; + + table->ft = mlx5e_post_meter_table_create(priv, ns_type); + if (IS_ERR(table->ft)) { + err = PTR_ERR(table->ft); + goto err_ft; + } + + /* create miss group */ + memset(flow_group_in, 0, inlen); + MLX5_SET(create_flow_group_in, flow_group_in, start_flow_index, 1); + MLX5_SET(create_flow_group_in, flow_group_in, end_flow_index, 1); + fg = mlx5_create_flow_group(table->ft, flow_group_in); + if (IS_ERR(fg)) { + err = PTR_ERR(fg); + goto err_miss_grp; + } + table->fg = fg; + + kvfree(flow_group_in); + return 0; + +err_miss_grp: + mlx5_destroy_flow_table(table->ft); +err_ft: + kvfree(flow_group_in); + return err; +} + +static int +mlx5e_post_meter_mtu_create(struct mlx5e_priv *priv, + enum mlx5_flow_namespace_type ns_type, + struct mlx5e_post_act *post_act, + struct mlx5_fc *act_counter, + struct mlx5_fc *drop_counter, + struct mlx5e_post_meter_priv *post_meter, + struct mlx5_flow_attr *green_attr, + struct mlx5_flow_attr *red_attr) +{ + struct mlx5e_post_meter_mtu_tables *mtu_tables = &post_meter->mtu_tables; + static struct mlx5_flow_spec zero_spec = {}; + struct mlx5_flow_handle *rule; + int err; + + post_meter->type = MLX5E_POST_METER_MTU; + + err = mlx5e_post_meter_create_mtu_table(priv, ns_type, &mtu_tables->green_table); + if (err) + goto err_green_ft; + + green_attr->ft = mtu_tables->green_table.ft; + rule = mlx5e_post_meter_add_rule(priv, post_meter, &zero_spec, green_attr, + act_counter, drop_counter); + if (IS_ERR(rule)) { + mlx5_core_warn(priv->mdev, "Failed to create post_meter conform rule\n"); + err = PTR_ERR(rule); + goto err_green_rule; + } + mtu_tables->green_table.rule = rule; + mtu_tables->green_table.attr = green_attr; + + err = mlx5e_post_meter_create_mtu_table(priv, ns_type, &mtu_tables->red_table); + if (err) + goto err_red_ft; + + red_attr->ft = mtu_tables->red_table.ft; + rule = mlx5e_post_meter_add_rule(priv, post_meter, &zero_spec, red_attr, + act_counter, drop_counter); + if (IS_ERR(rule)) { + mlx5_core_warn(priv->mdev, "Failed to create post_meter exceed rule\n"); + err = PTR_ERR(rule); + goto err_red_rule; + } + mtu_tables->red_table.rule = rule; + mtu_tables->red_table.attr = red_attr; + + return 0; + +err_red_rule: + mlx5_destroy_flow_table(mtu_tables->red_table.ft); +err_red_ft: + mlx5_del_flow_rules(mtu_tables->green_table.rule); +err_green_rule: + mlx5_destroy_flow_table(mtu_tables->green_table.ft); +err_green_ft: + return err; +} + struct mlx5e_post_meter_priv * mlx5e_post_meter_init(struct mlx5e_priv *priv, enum mlx5_flow_namespace_type ns_type, struct mlx5e_post_act *post_act, + enum mlx5e_post_meter_type type, struct mlx5_fc *act_counter, struct mlx5_fc *drop_counter, struct mlx5_flow_attr *branch_true, @@ -247,9 +393,21 @@ mlx5e_post_meter_init(struct mlx5e_priv *priv, if (!post_meter) return ERR_PTR(-ENOMEM); - err = mlx5e_post_meter_rate_create(priv, ns_type, post_act, - act_counter, drop_counter, post_meter, - branch_true, branch_false); + switch (type) { + case MLX5E_POST_METER_MTU: + err = mlx5e_post_meter_mtu_create(priv, ns_type, post_act, + act_counter, drop_counter, post_meter, + branch_true, branch_false); + break; + case MLX5E_POST_METER_RATE: + err = mlx5e_post_meter_rate_create(priv, ns_type, post_act, + act_counter, drop_counter, post_meter, + branch_true, branch_false); + break; + default: + err = -EOPNOTSUPP; + } + if (err) goto err; @@ -268,10 +426,22 @@ mlx5e_post_meter_rate_destroy(struct mlx5_eswitch *esw, struct mlx5e_post_meter_ mlx5e_post_meter_rate_table_destroy(post_meter); } +static void +mlx5e_post_meter_mtu_destroy(struct mlx5e_post_meter_priv *post_meter) +{ + mlx5e_post_meter_mtu_rules_destroy(post_meter); + mlx5e_post_meter_mtu_fg_destroy(post_meter); + mlx5e_post_meter_mtu_table_destroy(post_meter); +} + void mlx5e_post_meter_cleanup(struct mlx5_eswitch *esw, struct mlx5e_post_meter_priv *post_meter) { - mlx5e_post_meter_rate_destroy(esw, post_meter); + if (post_meter->type == MLX5E_POST_METER_RATE) + mlx5e_post_meter_rate_destroy(esw, post_meter); + else + mlx5e_post_meter_mtu_destroy(post_meter); + kfree(post_meter); } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/tc/post_meter.h b/drivers/net/ethernet/mellanox/mlx5/core/en/tc/post_meter.h index a4075d33fde2..0a3dbf5ed86d 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/tc/post_meter.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/tc/post_meter.h @@ -14,6 +14,11 @@ struct mlx5e_post_meter_priv; +enum mlx5e_post_meter_type { + MLX5E_POST_METER_RATE = 0, + MLX5E_POST_METER_MTU +}; + struct mlx5_flow_table * mlx5e_post_meter_get_ft(struct mlx5e_post_meter_priv *post_meter); @@ -21,10 +26,12 @@ struct mlx5e_post_meter_priv * mlx5e_post_meter_init(struct mlx5e_priv *priv, enum mlx5_flow_namespace_type ns_type, struct mlx5e_post_act *post_act, + enum mlx5e_post_meter_type type, struct mlx5_fc *act_counter, struct mlx5_fc *drop_counter, struct mlx5_flow_attr *branch_true, struct mlx5_flow_attr *branch_false); + void mlx5e_post_meter_cleanup(struct mlx5_eswitch *esw, struct mlx5e_post_meter_priv *post_meter); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c index 227fa6ef9e41..f66a2546003e 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c @@ -423,6 +423,7 @@ mlx5e_tc_add_flow_meter(struct mlx5e_priv *priv, ns_type = mlx5e_tc_meter_get_namespace(meter->flow_meters); post_meter = mlx5e_post_meter_init(priv, ns_type, post_act, + MLX5E_POST_METER_RATE, meter->act_counter, meter->drop_counter, attr->branch_true, attr->branch_false); if (IS_ERR(post_meter)) { From 6fda078d5f7521cf02f493b62d9d87abda3cb890 Mon Sep 17 00:00:00 2001 From: Oz Shlomo Date: Wed, 2 Nov 2022 14:36:51 +0000 Subject: [PATCH 1779/2002] net/mlx5e: TC, add support for meter mtu offload Initialize the meter object with the TC police mtu parameter. Use the hardware range destination to compare the pkt len to the mtu setting. Assign the range destination hit/miss ft to the police conform/exceed attributes. Signed-off-by: Oz Shlomo Reviewed-by: Roi Dayan Signed-off-by: Saeed Mahameed --- .../mellanox/mlx5/core/en/tc/act/police.c | 20 +++++++++++++--- .../ethernet/mellanox/mlx5/core/en/tc/meter.c | 15 +++++++++--- .../ethernet/mellanox/mlx5/core/en/tc/meter.h | 1 + .../mellanox/mlx5/core/en/tc/post_meter.c | 12 ++++++++++ .../mellanox/mlx5/core/en/tc/post_meter.h | 24 +++++++++++++++++++ .../net/ethernet/mellanox/mlx5/core/en_tc.c | 9 ++++--- .../net/ethernet/mellanox/mlx5/core/en_tc.h | 1 + .../mellanox/mlx5/core/eswitch_offloads.c | 19 +++++++++++++++ 8 files changed, 92 insertions(+), 9 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/police.c b/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/police.c index 898fe16a4384..512d43148922 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/police.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/police.c @@ -3,6 +3,7 @@ #include "act.h" #include "en/tc_priv.h" +#include "fs_core.h" static bool police_act_validate_control(enum flow_action_id act_id, struct netlink_ext_ack *extack) @@ -71,6 +72,8 @@ fill_meter_params_from_act(const struct flow_action_entry *act, params->mode = MLX5_RATE_LIMIT_PPS; params->rate = act->police.rate_pkt_ps; params->burst = act->police.burst_pkt; + } else if (act->police.mtu) { + params->mtu = act->police.mtu; } else { return -EOPNOTSUPP; } @@ -84,14 +87,25 @@ tc_act_parse_police(struct mlx5e_tc_act_parse_state *parse_state, struct mlx5e_priv *priv, struct mlx5_flow_attr *attr) { + enum mlx5_flow_namespace_type ns = mlx5e_get_flow_namespace(parse_state->flow); + struct mlx5e_flow_meter_params *params = &attr->meter_attr.params; int err; - err = fill_meter_params_from_act(act, &attr->meter_attr.params); + err = fill_meter_params_from_act(act, params); if (err) return err; - attr->action |= MLX5_FLOW_CONTEXT_ACTION_EXECUTE_ASO; - attr->exe_aso_type = MLX5_EXE_ASO_FLOW_METER; + if (params->mtu) { + if (!(mlx5_fs_get_capabilities(priv->mdev, ns) & + MLX5_FLOW_STEERING_CAP_MATCH_RANGES)) + return -EOPNOTSUPP; + + attr->action |= MLX5_FLOW_CONTEXT_ACTION_FWD_DEST; + attr->flags |= MLX5_ATTR_FLAG_MTU; + } else { + attr->action |= MLX5_FLOW_CONTEXT_ACTION_EXECUTE_ASO; + attr->exe_aso_type = MLX5_EXE_ASO_FLOW_METER; + } return 0; } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/tc/meter.c b/drivers/net/ethernet/mellanox/mlx5/core/en/tc/meter.c index 4e5f4aa44724..9c1c24da9453 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/tc/meter.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/tc/meter.c @@ -241,7 +241,7 @@ mlx5e_flow_meter_destroy_aso_obj(struct mlx5_core_dev *mdev, u32 obj_id) } static struct mlx5e_flow_meter_handle * -__mlx5e_flow_meter_alloc(struct mlx5e_flow_meters *flow_meters) +__mlx5e_flow_meter_alloc(struct mlx5e_flow_meters *flow_meters, bool alloc_aso) { struct mlx5_core_dev *mdev = flow_meters->mdev; struct mlx5e_flow_meter_aso_obj *meters_obj; @@ -268,6 +268,9 @@ __mlx5e_flow_meter_alloc(struct mlx5e_flow_meters *flow_meters) } meter->act_counter = counter; + if (!alloc_aso) + goto no_aso; + meters_obj = list_first_entry_or_null(&flow_meters->partial_list, struct mlx5e_flow_meter_aso_obj, entry); @@ -300,11 +303,12 @@ __mlx5e_flow_meter_alloc(struct mlx5e_flow_meters *flow_meters) } bitmap_set(meters_obj->meters_map, pos, 1); - meter->flow_meters = flow_meters; meter->meters_obj = meters_obj; meter->obj_id = meters_obj->base_id + pos / 2; meter->idx = pos % 2; +no_aso: + meter->flow_meters = flow_meters; mlx5_core_dbg(mdev, "flow meter allocated, obj_id=0x%x, index=%d\n", meter->obj_id, meter->idx); @@ -332,6 +336,9 @@ __mlx5e_flow_meter_free(struct mlx5e_flow_meter_handle *meter) mlx5_fc_destroy(mdev, meter->act_counter); mlx5_fc_destroy(mdev, meter->drop_counter); + if (meter->params.mtu) + goto out_no_aso; + meters_obj = meter->meters_obj; pos = (meter->obj_id - meters_obj->base_id) * 2 + meter->idx; bitmap_clear(meters_obj->meters_map, pos, 1); @@ -345,6 +352,7 @@ __mlx5e_flow_meter_free(struct mlx5e_flow_meter_handle *meter) list_add(&meters_obj->entry, &flow_meters->partial_list); } +out_no_aso: mlx5_core_dbg(mdev, "flow meter freed, obj_id=0x%x, index=%d\n", meter->obj_id, meter->idx); kfree(meter); @@ -409,12 +417,13 @@ mlx5e_tc_meter_alloc(struct mlx5e_flow_meters *flow_meters, { struct mlx5e_flow_meter_handle *meter; - meter = __mlx5e_flow_meter_alloc(flow_meters); + meter = __mlx5e_flow_meter_alloc(flow_meters, !params->mtu); if (IS_ERR(meter)) return meter; hash_add(flow_meters->hashtbl, &meter->hlist, params->index); meter->params.index = params->index; + meter->params.mtu = params->mtu; meter->refcnt++; return meter; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/tc/meter.h b/drivers/net/ethernet/mellanox/mlx5/core/en/tc/meter.h index f16abf33bb51..9b795cd106bb 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/tc/meter.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/tc/meter.h @@ -20,6 +20,7 @@ struct mlx5e_flow_meter_params { u32 index; u64 rate; u64 burst; + u32 mtu; }; struct mlx5e_flow_meter_handle { diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/tc/post_meter.c b/drivers/net/ethernet/mellanox/mlx5/core/en/tc/post_meter.c index ffed3af7d01e..8d7d761482d2 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/tc/post_meter.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/tc/post_meter.c @@ -43,6 +43,18 @@ mlx5e_post_meter_get_ft(struct mlx5e_post_meter_priv *post_meter) return post_meter->rate_steering_table.ft; } +struct mlx5_flow_table * +mlx5e_post_meter_get_mtu_true_ft(struct mlx5e_post_meter_priv *post_meter) +{ + return post_meter->mtu_tables.green_table.ft; +} + +struct mlx5_flow_table * +mlx5e_post_meter_get_mtu_false_ft(struct mlx5e_post_meter_priv *post_meter) +{ + return post_meter->mtu_tables.red_table.ft; +} + static struct mlx5_flow_table * mlx5e_post_meter_table_create(struct mlx5e_priv *priv, enum mlx5_flow_namespace_type ns_type) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/tc/post_meter.h b/drivers/net/ethernet/mellanox/mlx5/core/en/tc/post_meter.h index 0a3dbf5ed86d..e013b77186b2 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/tc/post_meter.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/tc/post_meter.h @@ -19,9 +19,17 @@ enum mlx5e_post_meter_type { MLX5E_POST_METER_MTU }; +#if IS_ENABLED(CONFIG_MLX5_CLS_ACT) + struct mlx5_flow_table * mlx5e_post_meter_get_ft(struct mlx5e_post_meter_priv *post_meter); +struct mlx5_flow_table * +mlx5e_post_meter_get_mtu_true_ft(struct mlx5e_post_meter_priv *post_meter); + +struct mlx5_flow_table * +mlx5e_post_meter_get_mtu_false_ft(struct mlx5e_post_meter_priv *post_meter); + struct mlx5e_post_meter_priv * mlx5e_post_meter_init(struct mlx5e_priv *priv, enum mlx5_flow_namespace_type ns_type, @@ -35,4 +43,20 @@ mlx5e_post_meter_init(struct mlx5e_priv *priv, void mlx5e_post_meter_cleanup(struct mlx5_eswitch *esw, struct mlx5e_post_meter_priv *post_meter); +#else /* CONFIG_MLX5_CLS_ACT */ + +static inline struct mlx5_flow_table * +mlx5e_post_meter_get_mtu_true_ft(struct mlx5e_post_meter_priv *post_meter) +{ + return NULL; +} + +static inline struct mlx5_flow_table * +mlx5e_post_meter_get_mtu_false_ft(struct mlx5e_post_meter_priv *post_meter) +{ + return NULL; +} + +#endif + #endif /* __MLX5_EN_POST_METER_H__ */ diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c index f66a2546003e..9af2aa2922f5 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c @@ -402,8 +402,9 @@ mlx5_tc_rule_delete(struct mlx5e_priv *priv, static bool is_flow_meter_action(struct mlx5_flow_attr *attr) { - return ((attr->action & MLX5_FLOW_CONTEXT_ACTION_EXECUTE_ASO) && - (attr->exe_aso_type == MLX5_EXE_ASO_FLOW_METER)); + return (((attr->action & MLX5_FLOW_CONTEXT_ACTION_EXECUTE_ASO) && + (attr->exe_aso_type == MLX5_EXE_ASO_FLOW_METER)) || + attr->flags & MLX5_ATTR_FLAG_MTU); } static int @@ -414,6 +415,7 @@ mlx5e_tc_add_flow_meter(struct mlx5e_priv *priv, struct mlx5e_post_meter_priv *post_meter; enum mlx5_flow_namespace_type ns_type; struct mlx5e_flow_meter_handle *meter; + enum mlx5e_post_meter_type type; meter = mlx5e_tc_meter_replace(priv->mdev, &attr->meter_attr.params); if (IS_ERR(meter)) { @@ -422,8 +424,9 @@ mlx5e_tc_add_flow_meter(struct mlx5e_priv *priv, } ns_type = mlx5e_tc_meter_get_namespace(meter->flow_meters); + type = meter->params.mtu ? MLX5E_POST_METER_MTU : MLX5E_POST_METER_RATE; post_meter = mlx5e_post_meter_init(priv, ns_type, post_act, - MLX5E_POST_METER_RATE, + type, meter->act_counter, meter->drop_counter, attr->branch_true, attr->branch_false); if (IS_ERR(post_meter)) { diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.h b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.h index f2677d9ca0b4..50af70ef22f3 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.h @@ -114,6 +114,7 @@ enum { MLX5_ATTR_FLAG_ACCEPT = BIT(5), MLX5_ATTR_FLAG_CT = BIT(6), MLX5_ATTR_FLAG_TERMINATING = BIT(7), + MLX5_ATTR_FLAG_MTU = BIT(8), }; /* Returns true if any of the flags that require skipping further TC/NF processing are set. */ diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c b/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c index 1987a9d9d40c..e455b215c708 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c @@ -50,6 +50,7 @@ #include "en/mapping.h" #include "devlink.h" #include "lag/lag.h" +#include "en/tc/post_meter.h" #define mlx5_esw_for_each_rep(esw, i, rep) \ xa_for_each(&((esw)->offloads.vport_reps), i, rep) @@ -201,6 +202,21 @@ esw_cleanup_decap_indir(struct mlx5_eswitch *esw, true); } +static int +esw_setup_mtu_dest(struct mlx5_flow_destination *dest, + struct mlx5e_meter_attr *meter, + int i) +{ + dest[i].type = MLX5_FLOW_DESTINATION_TYPE_RANGE; + dest[i].range.field = MLX5_FLOW_DEST_RANGE_FIELD_PKT_LEN; + dest[i].range.min = 0; + dest[i].range.max = meter->params.mtu; + dest[i].range.hit_ft = mlx5e_post_meter_get_mtu_true_ft(meter->post_meter); + dest[i].range.miss_ft = mlx5e_post_meter_get_mtu_false_ft(meter->post_meter); + + return 0; +} + static int esw_setup_sampler_dest(struct mlx5_flow_destination *dest, struct mlx5_flow_act *flow_act, @@ -491,6 +507,9 @@ esw_setup_dests(struct mlx5_flow_destination *dest, } else if (attr->flags & MLX5_ATTR_FLAG_ACCEPT) { esw_setup_accept_dest(dest, flow_act, chains, *i); (*i)++; + } else if (attr->flags & MLX5_ATTR_FLAG_MTU) { + err = esw_setup_mtu_dest(dest, &attr->meter_attr, *i); + (*i)++; } else if (esw_is_indir_table(esw, attr)) { err = esw_setup_indir_table(dest, flow_act, esw, attr, spec, true, i); } else if (esw_is_chain_src_port_rewrite(esw, esw_attr)) { From 7c33e73995e933475985ee0ca3cfa5b335da447e Mon Sep 17 00:00:00 2001 From: Maor Dickman Date: Sun, 9 Oct 2022 09:53:22 +0300 Subject: [PATCH 1780/2002] net/mlx5e: multipath, support routes with more than 2 nexthops Today multipath offload is only supported when the number of nexthops is 2 which block the use of it in case of system with 2 NICs. This patch solve it by enabling multipath offload per NIC if 2 nexthops of the route are its uplinks. Signed-off-by: Maor Dickman Reviewed-by: Roi Dayan Signed-off-by: Saeed Mahameed --- .../net/ethernet/mellanox/mlx5/core/lag/mp.c | 81 +++++++++++-------- 1 file changed, 49 insertions(+), 32 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lag/mp.c b/drivers/net/ethernet/mellanox/mlx5/core/lag/mp.c index 0259a149a64c..d9fcb9ed726f 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/lag/mp.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/lag/mp.c @@ -118,13 +118,41 @@ struct mlx5_fib_event_work { }; }; +static struct net_device* +mlx5_lag_get_next_fib_dev(struct mlx5_lag *ldev, + struct fib_info *fi, + struct net_device *current_dev) +{ + struct net_device *fib_dev; + int i, ldev_idx, nhs; + + nhs = fib_info_num_path(fi); + i = 0; + if (current_dev) { + for (; i < nhs; i++) { + fib_dev = fib_info_nh(fi, i)->fib_nh_dev; + if (fib_dev == current_dev) { + i++; + break; + } + } + } + for (; i < nhs; i++) { + fib_dev = fib_info_nh(fi, i)->fib_nh_dev; + ldev_idx = mlx5_lag_dev_get_netdev_idx(ldev, fib_dev); + if (ldev_idx >= 0) + return ldev->pf[ldev_idx].netdev; + } + + return NULL; +} + static void mlx5_lag_fib_route_event(struct mlx5_lag *ldev, unsigned long event, struct fib_entry_notifier_info *fen_info) { + struct net_device *nh_dev0, *nh_dev1; struct fib_info *fi = fen_info->fi; struct lag_mp *mp = &ldev->lag_mp; - struct fib_nh *fib_nh0, *fib_nh1; - unsigned int nhs; /* Handle delete event */ if (event == FIB_EVENT_ENTRY_DEL) { @@ -140,16 +168,25 @@ static void mlx5_lag_fib_route_event(struct mlx5_lag *ldev, unsigned long event, fi->fib_priority >= mp->fib.priority) return; - /* Handle add/replace event */ - nhs = fib_info_num_path(fi); - if (nhs == 1) { - if (__mlx5_lag_is_active(ldev)) { - struct fib_nh *nh = fib_info_nh(fi, 0); - struct net_device *nh_dev = nh->fib_nh_dev; - int i = mlx5_lag_dev_get_netdev_idx(ldev, nh_dev); + nh_dev0 = mlx5_lag_get_next_fib_dev(ldev, fi, NULL); + nh_dev1 = mlx5_lag_get_next_fib_dev(ldev, fi, nh_dev0); - if (i < 0) - return; + /* Handle add/replace event */ + if (!nh_dev0) { + if (mp->fib.dst == fen_info->dst && mp->fib.dst_len == fen_info->dst_len) + mp->fib.mfi = NULL; + return; + } + + if (nh_dev0 == nh_dev1) { + mlx5_core_warn(ldev->pf[MLX5_LAG_P1].dev, + "Multipath offload doesn't support routes with multiple nexthops of the same device"); + return; + } + + if (!nh_dev1) { + if (__mlx5_lag_is_active(ldev)) { + int i = mlx5_lag_dev_get_netdev_idx(ldev, nh_dev0); i++; mlx5_lag_set_port_affinity(ldev, i); @@ -159,21 +196,6 @@ static void mlx5_lag_fib_route_event(struct mlx5_lag *ldev, unsigned long event, return; } - if (nhs != 2) - return; - - /* Verify next hops are ports of the same hca */ - fib_nh0 = fib_info_nh(fi, 0); - fib_nh1 = fib_info_nh(fi, 1); - if (!(fib_nh0->fib_nh_dev == ldev->pf[MLX5_LAG_P1].netdev && - fib_nh1->fib_nh_dev == ldev->pf[MLX5_LAG_P2].netdev) && - !(fib_nh0->fib_nh_dev == ldev->pf[MLX5_LAG_P2].netdev && - fib_nh1->fib_nh_dev == ldev->pf[MLX5_LAG_P1].netdev)) { - mlx5_core_warn(ldev->pf[MLX5_LAG_P1].dev, - "Multipath offload require two ports of the same HCA\n"); - return; - } - /* First time we see multipath route */ if (!mp->fib.mfi && !__mlx5_lag_is_active(ldev)) { struct lag_tracker tracker; @@ -268,7 +290,6 @@ static int mlx5_lag_fib_event(struct notifier_block *nb, struct mlx5_fib_event_work *fib_work; struct fib_entry_notifier_info *fen_info; struct fib_nh_notifier_info *fnh_info; - struct net_device *fib_dev; struct fib_info *fi; if (info->family != AF_INET) @@ -285,11 +306,7 @@ static int mlx5_lag_fib_event(struct notifier_block *nb, fi = fen_info->fi; if (fi->nh) return NOTIFY_DONE; - fib_dev = fib_info_nh(fen_info->fi, 0)->fib_nh_dev; - if (fib_dev != ldev->pf[MLX5_LAG_P1].netdev && - fib_dev != ldev->pf[MLX5_LAG_P2].netdev) { - return NOTIFY_DONE; - } + fib_work = mlx5_lag_init_fib_work(ldev, event); if (!fib_work) return NOTIFY_DONE; From 64b68e36964997e6a00297daf0fd6eab12e9fb82 Mon Sep 17 00:00:00 2001 From: Or Har-Toov Date: Tue, 22 Nov 2022 12:42:18 +0200 Subject: [PATCH 1781/2002] net/mlx5: Refactor and expand rep vport stat group Expand representor vport stat group to support all counters from the vport stat group, to count all the traffic passing through the vport. Fix current implementation where fill_stats and update_stats use different structs. Signed-off-by: Or Har-Toov Reviewed-by: Maor Gottlieb Signed-off-by: Saeed Mahameed --- .../net/ethernet/mellanox/mlx5/core/en_rep.c | 94 +++++++++++++++---- .../ethernet/mellanox/mlx5/core/en_stats.h | 16 ++++ 2 files changed, 90 insertions(+), 20 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c b/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c index 623886462c10..75b9e1528fd2 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c @@ -85,18 +85,25 @@ static const struct counter_desc sw_rep_stats_desc[] = { { MLX5E_DECLARE_STAT(struct mlx5e_sw_stats, tx_bytes) }, }; -struct vport_stats { - u64 vport_rx_packets; - u64 vport_tx_packets; - u64 vport_rx_bytes; - u64 vport_tx_bytes; -}; - static const struct counter_desc vport_rep_stats_desc[] = { - { MLX5E_DECLARE_STAT(struct vport_stats, vport_rx_packets) }, - { MLX5E_DECLARE_STAT(struct vport_stats, vport_rx_bytes) }, - { MLX5E_DECLARE_STAT(struct vport_stats, vport_tx_packets) }, - { MLX5E_DECLARE_STAT(struct vport_stats, vport_tx_bytes) }, + { MLX5E_DECLARE_STAT(struct mlx5e_rep_stats, vport_rx_packets) }, + { MLX5E_DECLARE_STAT(struct mlx5e_rep_stats, vport_rx_bytes) }, + { MLX5E_DECLARE_STAT(struct mlx5e_rep_stats, vport_tx_packets) }, + { MLX5E_DECLARE_STAT(struct mlx5e_rep_stats, vport_tx_bytes) }, + { MLX5E_DECLARE_STAT(struct mlx5e_rep_stats, + rx_vport_rdma_unicast_packets) }, + { MLX5E_DECLARE_STAT(struct mlx5e_rep_stats, rx_vport_rdma_unicast_bytes) }, + { MLX5E_DECLARE_STAT(struct mlx5e_rep_stats, + tx_vport_rdma_unicast_packets) }, + { MLX5E_DECLARE_STAT(struct mlx5e_rep_stats, tx_vport_rdma_unicast_bytes) }, + { MLX5E_DECLARE_STAT(struct mlx5e_rep_stats, + rx_vport_rdma_multicast_packets) }, + { MLX5E_DECLARE_STAT(struct mlx5e_rep_stats, + rx_vport_rdma_multicast_bytes) }, + { MLX5E_DECLARE_STAT(struct mlx5e_rep_stats, + tx_vport_rdma_multicast_packets) }, + { MLX5E_DECLARE_STAT(struct mlx5e_rep_stats, + tx_vport_rdma_multicast_bytes) }, }; #define NUM_VPORT_REP_SW_COUNTERS ARRAY_SIZE(sw_rep_stats_desc) @@ -161,33 +168,80 @@ static MLX5E_DECLARE_STATS_GRP_OP_FILL_STATS(vport_rep) int i; for (i = 0; i < NUM_VPORT_REP_HW_COUNTERS; i++) - data[idx++] = MLX5E_READ_CTR64_CPU(&priv->stats.vf_vport, + data[idx++] = MLX5E_READ_CTR64_CPU(&priv->stats.rep_stats, vport_rep_stats_desc, i); return idx; } static MLX5E_DECLARE_STATS_GRP_OP_UPDATE_STATS(vport_rep) { + struct mlx5e_rep_stats *rep_stats = &priv->stats.rep_stats; + int outlen = MLX5_ST_SZ_BYTES(query_vport_counter_out); struct mlx5_eswitch *esw = priv->mdev->priv.eswitch; struct mlx5e_rep_priv *rpriv = priv->ppriv; struct mlx5_eswitch_rep *rep = rpriv->rep; - struct rtnl_link_stats64 *vport_stats; - struct ifla_vf_stats vf_stats; + u32 *out; int err; - err = mlx5_eswitch_get_vport_stats(esw, rep->vport, &vf_stats); + out = kvzalloc(outlen, GFP_KERNEL); + if (!out) + return; + + err = mlx5_core_query_vport_counter(esw->dev, 1, rep->vport - 1, 0, out); if (err) { netdev_warn(priv->netdev, "vport %d error %d reading stats\n", rep->vport, err); return; } - vport_stats = &priv->stats.vf_vport; + #define MLX5_GET_CTR(p, x) \ + MLX5_GET64(query_vport_counter_out, p, x) /* flip tx/rx as we are reporting the counters for the switch vport */ - vport_stats->rx_packets = vf_stats.tx_packets; - vport_stats->rx_bytes = vf_stats.tx_bytes; - vport_stats->tx_packets = vf_stats.rx_packets; - vport_stats->tx_bytes = vf_stats.rx_bytes; + rep_stats->vport_rx_packets = + MLX5_GET_CTR(out, transmitted_ib_unicast.packets) + + MLX5_GET_CTR(out, transmitted_eth_unicast.packets) + + MLX5_GET_CTR(out, transmitted_ib_multicast.packets) + + MLX5_GET_CTR(out, transmitted_eth_multicast.packets) + + MLX5_GET_CTR(out, transmitted_eth_broadcast.packets); + + rep_stats->vport_tx_packets = + MLX5_GET_CTR(out, received_ib_unicast.packets) + + MLX5_GET_CTR(out, received_eth_unicast.packets) + + MLX5_GET_CTR(out, received_ib_multicast.packets) + + MLX5_GET_CTR(out, received_eth_multicast.packets) + + MLX5_GET_CTR(out, received_eth_broadcast.packets); + + rep_stats->vport_rx_bytes = + MLX5_GET_CTR(out, transmitted_ib_unicast.octets) + + MLX5_GET_CTR(out, transmitted_eth_unicast.octets) + + MLX5_GET_CTR(out, transmitted_ib_multicast.octets) + + MLX5_GET_CTR(out, transmitted_eth_broadcast.octets); + + rep_stats->vport_tx_bytes = + MLX5_GET_CTR(out, received_ib_unicast.octets) + + MLX5_GET_CTR(out, received_eth_unicast.octets) + + MLX5_GET_CTR(out, received_ib_multicast.octets) + + MLX5_GET_CTR(out, received_eth_multicast.octets) + + MLX5_GET_CTR(out, received_eth_broadcast.octets); + + rep_stats->rx_vport_rdma_unicast_packets = + MLX5_GET_CTR(out, transmitted_ib_unicast.packets); + rep_stats->tx_vport_rdma_unicast_packets = + MLX5_GET_CTR(out, received_ib_unicast.packets); + rep_stats->rx_vport_rdma_unicast_bytes = + MLX5_GET_CTR(out, transmitted_ib_unicast.octets); + rep_stats->tx_vport_rdma_unicast_bytes = + MLX5_GET_CTR(out, received_ib_unicast.octets); + rep_stats->rx_vport_rdma_multicast_packets = + MLX5_GET_CTR(out, transmitted_ib_multicast.packets); + rep_stats->tx_vport_rdma_multicast_packets = + MLX5_GET_CTR(out, received_ib_multicast.packets); + rep_stats->rx_vport_rdma_multicast_bytes = + MLX5_GET_CTR(out, transmitted_ib_multicast.octets); + rep_stats->tx_vport_rdma_multicast_bytes = + MLX5_GET_CTR(out, received_ib_multicast.octets); + + kvfree(out); } static void mlx5e_rep_get_strings(struct net_device *dev, diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_stats.h b/drivers/net/ethernet/mellanox/mlx5/core/en_stats.h index cbc831ca646b..37df58ba958c 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_stats.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_stats.h @@ -463,6 +463,21 @@ struct mlx5e_ptp_cq_stats { u64 resync_event; }; +struct mlx5e_rep_stats { + u64 vport_rx_packets; + u64 vport_tx_packets; + u64 vport_rx_bytes; + u64 vport_tx_bytes; + u64 rx_vport_rdma_unicast_packets; + u64 tx_vport_rdma_unicast_packets; + u64 rx_vport_rdma_unicast_bytes; + u64 tx_vport_rdma_unicast_bytes; + u64 rx_vport_rdma_multicast_packets; + u64 tx_vport_rdma_multicast_packets; + u64 rx_vport_rdma_multicast_bytes; + u64 tx_vport_rdma_multicast_bytes; +}; + struct mlx5e_stats { struct mlx5e_sw_stats sw; struct mlx5e_qcounter_stats qcnt; @@ -471,6 +486,7 @@ struct mlx5e_stats { struct mlx5e_pport_stats pport; struct rtnl_link_stats64 vf_vport; struct mlx5e_pcie_stats pcie; + struct mlx5e_rep_stats rep_stats; }; extern mlx5e_stats_grp_t mlx5e_nic_stats_grps[]; From 4fe1b3a5f8fe2fdcedcaba9561e5b0ae5cb1d15b Mon Sep 17 00:00:00 2001 From: Michael Guralnik Date: Thu, 8 Dec 2022 06:23:46 +0200 Subject: [PATCH 1782/2002] net/mlx5: Expose steering dropped packets counter Add rx steering discarded packets counter to the vnic_diag debugfs. Signed-off-by: Michael Guralnik Reviewed-by: Maor Gottlieb Signed-off-by: Saeed Mahameed --- .../ethernet/mellanox/mlx5/core/esw/debugfs.c | 22 ++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/esw/debugfs.c b/drivers/net/ethernet/mellanox/mlx5/core/esw/debugfs.c index 2db13c71e88c..3d0bbcca1cb9 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/esw/debugfs.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/esw/debugfs.c @@ -12,10 +12,11 @@ enum vnic_diag_counter { MLX5_VNIC_DIAG_CQ_OVERRUN, MLX5_VNIC_DIAG_INVALID_COMMAND, MLX5_VNIC_DIAG_QOUTA_EXCEEDED_COMMAND, + MLX5_VNIC_DIAG_RX_STEERING_DISCARD, }; static int mlx5_esw_query_vnic_diag(struct mlx5_vport *vport, enum vnic_diag_counter counter, - u32 *val) + u64 *val) { u32 out[MLX5_ST_SZ_DW(query_vnic_env_out)] = {}; u32 in[MLX5_ST_SZ_DW(query_vnic_env_in)] = {}; @@ -57,6 +58,10 @@ static int mlx5_esw_query_vnic_diag(struct mlx5_vport *vport, enum vnic_diag_cou case MLX5_VNIC_DIAG_QOUTA_EXCEEDED_COMMAND: *val = MLX5_GET(vnic_diagnostic_statistics, vnic_diag_out, quota_exceeded_command); break; + case MLX5_VNIC_DIAG_RX_STEERING_DISCARD: + *val = MLX5_GET64(vnic_diagnostic_statistics, vnic_diag_out, + nic_receive_steering_discard); + break; } return 0; @@ -65,14 +70,14 @@ static int mlx5_esw_query_vnic_diag(struct mlx5_vport *vport, enum vnic_diag_cou static int __show_vnic_diag(struct seq_file *file, struct mlx5_vport *vport, enum vnic_diag_counter type) { - u32 val = 0; + u64 val = 0; int ret; ret = mlx5_esw_query_vnic_diag(vport, type, &val); if (ret) return ret; - seq_printf(file, "%d\n", val); + seq_printf(file, "%llu\n", val); return 0; } @@ -112,6 +117,11 @@ static int quota_exceeded_command_show(struct seq_file *file, void *priv) return __show_vnic_diag(file, file->private, MLX5_VNIC_DIAG_QOUTA_EXCEEDED_COMMAND); } +static int rx_steering_discard_show(struct seq_file *file, void *priv) +{ + return __show_vnic_diag(file, file->private, MLX5_VNIC_DIAG_RX_STEERING_DISCARD); +} + DEFINE_SHOW_ATTRIBUTE(total_q_under_processor_handle); DEFINE_SHOW_ATTRIBUTE(send_queue_priority_update_flow); DEFINE_SHOW_ATTRIBUTE(comp_eq_overrun); @@ -119,6 +129,7 @@ DEFINE_SHOW_ATTRIBUTE(async_eq_overrun); DEFINE_SHOW_ATTRIBUTE(cq_overrun); DEFINE_SHOW_ATTRIBUTE(invalid_command); DEFINE_SHOW_ATTRIBUTE(quota_exceeded_command); +DEFINE_SHOW_ATTRIBUTE(rx_steering_discard); void mlx5_esw_vport_debugfs_destroy(struct mlx5_eswitch *esw, u16 vport_num) { @@ -179,4 +190,9 @@ void mlx5_esw_vport_debugfs_create(struct mlx5_eswitch *esw, u16 vport_num, bool if (MLX5_CAP_GEN(esw->dev, quota_exceeded_count)) debugfs_create_file("quota_exceeded_command", 0444, vnic_diag, vport, "a_exceeded_command_fops); + + if (MLX5_CAP_GEN(esw->dev, nic_receive_steering_discard)) + debugfs_create_file("rx_steering_discard", 0444, vnic_diag, vport, + &rx_steering_discard_fops); + } From 0893d6007db5cf397f3fc92b2a6935c3ed0c6f00 Mon Sep 17 00:00:00 2001 From: Hou Tao Date: Fri, 9 Dec 2022 09:09:46 +0800 Subject: [PATCH 1783/2002] bpf: Reuse freed element in free_by_rcu during allocation When there are batched freeing operations on a specific CPU, part of the freed elements ((high_watermark - lower_watermark) / 2 + 1) will be indirectly moved into waiting_for_gp list through free_by_rcu list. After call_rcu_in_progress becomes false again, the remaining elements in free_by_rcu list will be moved to waiting_for_gp list by the next invocation of free_bulk(). However if the expiration of RCU tasks trace grace period is relatively slow, none element in free_by_rcu list will be moved. So instead of invoking __alloc_percpu_gfp() or kmalloc_node() to allocate a new object, in alloc_bulk() just check whether or not there is freed element in free_by_rcu list and reuse it if available. Acked-by: Yonghong Song Signed-off-by: Hou Tao Link: https://lore.kernel.org/r/20221209010947.3130477-2-houtao@huaweicloud.com Signed-off-by: Alexei Starovoitov --- kernel/bpf/memalloc.c | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/kernel/bpf/memalloc.c b/kernel/bpf/memalloc.c index 8f0d65f2474a..04d96d1b98a3 100644 --- a/kernel/bpf/memalloc.c +++ b/kernel/bpf/memalloc.c @@ -171,9 +171,24 @@ static void alloc_bulk(struct bpf_mem_cache *c, int cnt, int node) memcg = get_memcg(c); old_memcg = set_active_memcg(memcg); for (i = 0; i < cnt; i++) { - obj = __alloc(c, node); - if (!obj) - break; + /* + * free_by_rcu is only manipulated by irq work refill_work(). + * IRQ works on the same CPU are called sequentially, so it is + * safe to use __llist_del_first() here. If alloc_bulk() is + * invoked by the initial prefill, there will be no running + * refill_work(), so __llist_del_first() is fine as well. + * + * In most cases, objects on free_by_rcu are from the same CPU. + * If some objects come from other CPUs, it doesn't incur any + * harm because NUMA_NO_NODE means the preference for current + * numa node and it is not a guarantee. + */ + obj = __llist_del_first(&c->free_by_rcu); + if (!obj) { + obj = __alloc(c, node); + if (!obj) + break; + } if (IS_ENABLED(CONFIG_PREEMPT_RT)) /* In RT irq_work runs in per-cpu kthread, so disable * interrupts to avoid preemption and interrupts and From 822ed78fab13d5a54f8b8c030e8c5dc0fcd2cdae Mon Sep 17 00:00:00 2001 From: Hou Tao Date: Fri, 9 Dec 2022 09:09:47 +0800 Subject: [PATCH 1784/2002] bpf: Skip rcu_barrier() if rcu_trace_implies_rcu_gp() is true If there are pending rcu callback, free_mem_alloc() will use rcu_barrier_tasks_trace() and rcu_barrier() to wait for the pending __free_rcu_tasks_trace() and __free_rcu() callback. If rcu_trace_implies_rcu_gp() is true, there will be no pending __free_rcu(), so it will be OK to skip rcu_barrier() as well. Acked-by: Yonghong Song Acked-by: Paul E. McKenney Signed-off-by: Hou Tao Link: https://lore.kernel.org/r/20221209010947.3130477-3-houtao@huaweicloud.com Signed-off-by: Alexei Starovoitov --- kernel/bpf/memalloc.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/kernel/bpf/memalloc.c b/kernel/bpf/memalloc.c index 04d96d1b98a3..ebcc3dd0fa19 100644 --- a/kernel/bpf/memalloc.c +++ b/kernel/bpf/memalloc.c @@ -464,9 +464,17 @@ static void free_mem_alloc(struct bpf_mem_alloc *ma) { /* waiting_for_gp lists was drained, but __free_rcu might * still execute. Wait for it now before we freeing percpu caches. + * + * rcu_barrier_tasks_trace() doesn't imply synchronize_rcu_tasks_trace(), + * but rcu_barrier_tasks_trace() and rcu_barrier() below are only used + * to wait for the pending __free_rcu_tasks_trace() and __free_rcu(), + * so if call_rcu(head, __free_rcu) is skipped due to + * rcu_trace_implies_rcu_gp(), it will be OK to skip rcu_barrier() by + * using rcu_trace_implies_rcu_gp() as well. */ rcu_barrier_tasks_trace(); - rcu_barrier(); + if (!rcu_trace_implies_rcu_gp()) + rcu_barrier(); free_mem_alloc_no_barrier(ma); } From 6b75bd3d036745b9be30917909f03602099adbdb Mon Sep 17 00:00:00 2001 From: Kumar Kartikeya Dwivedi Date: Thu, 8 Dec 2022 02:11:35 +0530 Subject: [PATCH 1785/2002] bpf: Refactor ARG_PTR_TO_DYNPTR checks into process_dynptr_func ARG_PTR_TO_DYNPTR is akin to ARG_PTR_TO_TIMER, ARG_PTR_TO_KPTR, where the underlying register type is subjected to more special checks to determine the type of object represented by the pointer and its state consistency. Move dynptr checks to their own 'process_dynptr_func' function so that is consistent and in-line with existing code. This also makes it easier to reuse this code for kfunc handling. Then, reuse this consolidated function in kfunc dynptr handling too. Note that for kfuncs, the arg_type constraint of DYNPTR_TYPE_LOCAL has been lifted. Acked-by: David Vernet Acked-by: Joanne Koong Signed-off-by: Kumar Kartikeya Dwivedi Link: https://lore.kernel.org/r/20221207204141.308952-2-memxor@gmail.com Signed-off-by: Alexei Starovoitov --- include/linux/bpf_verifier.h | 8 +- kernel/bpf/verifier.c | 134 +++++++++--------- .../bpf/prog_tests/kfunc_dynptr_param.c | 7 +- .../bpf/progs/test_kfunc_dynptr_param.c | 12 -- 4 files changed, 75 insertions(+), 86 deletions(-) diff --git a/include/linux/bpf_verifier.h b/include/linux/bpf_verifier.h index 70d06a99f0b8..df0cb825e0e3 100644 --- a/include/linux/bpf_verifier.h +++ b/include/linux/bpf_verifier.h @@ -615,11 +615,9 @@ int check_func_arg_reg_off(struct bpf_verifier_env *env, enum bpf_arg_type arg_type); int check_mem_reg(struct bpf_verifier_env *env, struct bpf_reg_state *reg, u32 regno, u32 mem_size); -bool is_dynptr_reg_valid_init(struct bpf_verifier_env *env, - struct bpf_reg_state *reg); -bool is_dynptr_type_expected(struct bpf_verifier_env *env, - struct bpf_reg_state *reg, - enum bpf_arg_type arg_type); +struct bpf_call_arg_meta; +int process_dynptr_func(struct bpf_verifier_env *env, int regno, + enum bpf_arg_type arg_type, struct bpf_call_arg_meta *meta); /* this lives here instead of in bpf.h because it needs to dereference tgt_prog */ static inline u64 bpf_trampoline_compute_key(const struct bpf_prog *tgt_prog, diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 3194e9d9e4e4..fcd8a71035aa 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -810,8 +810,7 @@ static bool is_dynptr_reg_valid_uninit(struct bpf_verifier_env *env, struct bpf_ return true; } -bool is_dynptr_reg_valid_init(struct bpf_verifier_env *env, - struct bpf_reg_state *reg) +static bool is_dynptr_reg_valid_init(struct bpf_verifier_env *env, struct bpf_reg_state *reg) { struct bpf_func_state *state = func(env, reg); int spi = get_spi(reg->off); @@ -830,9 +829,8 @@ bool is_dynptr_reg_valid_init(struct bpf_verifier_env *env, return true; } -bool is_dynptr_type_expected(struct bpf_verifier_env *env, - struct bpf_reg_state *reg, - enum bpf_arg_type arg_type) +static bool is_dynptr_type_expected(struct bpf_verifier_env *env, struct bpf_reg_state *reg, + enum bpf_arg_type arg_type) { struct bpf_func_state *state = func(env, reg); enum bpf_dynptr_type dynptr_type; @@ -5859,6 +5857,65 @@ static int process_kptr_func(struct bpf_verifier_env *env, int regno, return 0; } +int process_dynptr_func(struct bpf_verifier_env *env, int regno, + enum bpf_arg_type arg_type, + struct bpf_call_arg_meta *meta) +{ + struct bpf_reg_state *regs = cur_regs(env), *reg = ®s[regno]; + + /* We only need to check for initialized / uninitialized helper + * dynptr args if the dynptr is not PTR_TO_DYNPTR, as the + * assumption is that if it is, that a helper function + * initialized the dynptr on behalf of the BPF program. + */ + if (base_type(reg->type) == PTR_TO_DYNPTR) + return 0; + if (arg_type & MEM_UNINIT) { + if (!is_dynptr_reg_valid_uninit(env, reg)) { + verbose(env, "Dynptr has to be an uninitialized dynptr\n"); + return -EINVAL; + } + + /* We only support one dynptr being uninitialized at the moment, + * which is sufficient for the helper functions we have right now. + */ + if (meta->uninit_dynptr_regno) { + verbose(env, "verifier internal error: multiple uninitialized dynptr args\n"); + return -EFAULT; + } + + meta->uninit_dynptr_regno = regno; + } else { + if (!is_dynptr_reg_valid_init(env, reg)) { + verbose(env, + "Expected an initialized dynptr as arg #%d\n", + regno); + return -EINVAL; + } + + if (!is_dynptr_type_expected(env, reg, arg_type)) { + const char *err_extra = ""; + + switch (arg_type & DYNPTR_TYPE_FLAG_MASK) { + case DYNPTR_TYPE_LOCAL: + err_extra = "local"; + break; + case DYNPTR_TYPE_RINGBUF: + err_extra = "ringbuf"; + break; + default: + err_extra = ""; + break; + } + verbose(env, + "Expected a dynptr of type %s as arg #%d\n", + err_extra, regno); + return -EINVAL; + } + } + return 0; +} + static bool arg_type_is_mem_size(enum bpf_arg_type type) { return type == ARG_CONST_SIZE || @@ -6390,52 +6447,8 @@ skip_type_check: err = check_mem_size_reg(env, reg, regno, true, meta); break; case ARG_PTR_TO_DYNPTR: - /* We only need to check for initialized / uninitialized helper - * dynptr args if the dynptr is not PTR_TO_DYNPTR, as the - * assumption is that if it is, that a helper function - * initialized the dynptr on behalf of the BPF program. - */ - if (base_type(reg->type) == PTR_TO_DYNPTR) - break; - if (arg_type & MEM_UNINIT) { - if (!is_dynptr_reg_valid_uninit(env, reg)) { - verbose(env, "Dynptr has to be an uninitialized dynptr\n"); - return -EINVAL; - } - - /* We only support one dynptr being uninitialized at the moment, - * which is sufficient for the helper functions we have right now. - */ - if (meta->uninit_dynptr_regno) { - verbose(env, "verifier internal error: multiple uninitialized dynptr args\n"); - return -EFAULT; - } - - meta->uninit_dynptr_regno = regno; - } else if (!is_dynptr_reg_valid_init(env, reg)) { - verbose(env, - "Expected an initialized dynptr as arg #%d\n", - arg + 1); - return -EINVAL; - } else if (!is_dynptr_type_expected(env, reg, arg_type)) { - const char *err_extra = ""; - - switch (arg_type & DYNPTR_TYPE_FLAG_MASK) { - case DYNPTR_TYPE_LOCAL: - err_extra = "local"; - break; - case DYNPTR_TYPE_RINGBUF: - err_extra = "ringbuf"; - break; - default: - err_extra = ""; - break; - } - verbose(env, - "Expected a dynptr of type %s as arg #%d\n", - err_extra, arg + 1); - return -EINVAL; - } + if (process_dynptr_func(env, regno, arg_type, meta)) + return -EACCES; break; case ARG_CONST_ALLOC_SIZE_OR_ZERO: if (!tnum_is_const(reg->var_off)) { @@ -8829,22 +8842,15 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_kfunc_call_ return ret; break; case KF_ARG_PTR_TO_DYNPTR: - if (reg->type != PTR_TO_STACK) { - verbose(env, "arg#%d expected pointer to stack\n", i); + if (reg->type != PTR_TO_STACK && + reg->type != PTR_TO_DYNPTR) { + verbose(env, "arg#%d expected pointer to stack or dynptr_ptr\n", i); return -EINVAL; } - if (!is_dynptr_reg_valid_init(env, reg)) { - verbose(env, "arg#%d pointer type %s %s must be valid and initialized\n", - i, btf_type_str(ref_t), ref_tname); - return -EINVAL; - } - - if (!is_dynptr_type_expected(env, reg, ARG_PTR_TO_DYNPTR | DYNPTR_TYPE_LOCAL)) { - verbose(env, "arg#%d pointer type %s %s points to unsupported dynamic pointer type\n", - i, btf_type_str(ref_t), ref_tname); - return -EINVAL; - } + ret = process_dynptr_func(env, regno, ARG_PTR_TO_DYNPTR, NULL); + if (ret < 0) + return ret; break; case KF_ARG_PTR_TO_LIST_HEAD: if (reg->type != PTR_TO_MAP_VALUE && diff --git a/tools/testing/selftests/bpf/prog_tests/kfunc_dynptr_param.c b/tools/testing/selftests/bpf/prog_tests/kfunc_dynptr_param.c index 55d641c1f126..a9229260a6ce 100644 --- a/tools/testing/selftests/bpf/prog_tests/kfunc_dynptr_param.c +++ b/tools/testing/selftests/bpf/prog_tests/kfunc_dynptr_param.c @@ -18,11 +18,8 @@ static struct { const char *expected_verifier_err_msg; int expected_runtime_err; } kfunc_dynptr_tests[] = { - {"dynptr_type_not_supp", - "arg#0 pointer type STRUCT bpf_dynptr_kern points to unsupported dynamic pointer type", 0}, - {"not_valid_dynptr", - "arg#0 pointer type STRUCT bpf_dynptr_kern must be valid and initialized", 0}, - {"not_ptr_to_stack", "arg#0 expected pointer to stack", 0}, + {"not_valid_dynptr", "Expected an initialized dynptr as arg #1", 0}, + {"not_ptr_to_stack", "arg#0 expected pointer to stack or dynptr_ptr", 0}, {"dynptr_data_null", NULL, -EBADMSG}, }; diff --git a/tools/testing/selftests/bpf/progs/test_kfunc_dynptr_param.c b/tools/testing/selftests/bpf/progs/test_kfunc_dynptr_param.c index ce39d096bba3..f4a8250329b2 100644 --- a/tools/testing/selftests/bpf/progs/test_kfunc_dynptr_param.c +++ b/tools/testing/selftests/bpf/progs/test_kfunc_dynptr_param.c @@ -32,18 +32,6 @@ int err, pid; char _license[] SEC("license") = "GPL"; -SEC("?lsm.s/bpf") -int BPF_PROG(dynptr_type_not_supp, int cmd, union bpf_attr *attr, - unsigned int size) -{ - char write_data[64] = "hello there, world!!"; - struct bpf_dynptr ptr; - - bpf_ringbuf_reserve_dynptr(&ringbuf, sizeof(write_data), 0, &ptr); - - return bpf_verify_pkcs7_signature(&ptr, &ptr, NULL); -} - SEC("?lsm.s/bpf") int BPF_PROG(not_valid_dynptr, int cmd, union bpf_attr *attr, unsigned int size) { From ac50fe51ce873f4299928e312ce2042e35ab5c08 Mon Sep 17 00:00:00 2001 From: Kumar Kartikeya Dwivedi Date: Thu, 8 Dec 2022 02:11:36 +0530 Subject: [PATCH 1786/2002] bpf: Propagate errors from process_* checks in check_func_arg Currently, we simply ignore the errors in process_spin_lock, process_timer_func, process_kptr_func, process_dynptr_func. Instead, bubble up the error by storing and checking err variable. Acked-by: Joanne Koong Signed-off-by: Kumar Kartikeya Dwivedi Link: https://lore.kernel.org/r/20221207204141.308952-3-memxor@gmail.com Signed-off-by: Alexei Starovoitov --- kernel/bpf/verifier.c | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index fcd8a71035aa..eb742ac75844 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -6412,19 +6412,22 @@ skip_type_check: break; case ARG_PTR_TO_SPIN_LOCK: if (meta->func_id == BPF_FUNC_spin_lock) { - if (process_spin_lock(env, regno, true)) - return -EACCES; + err = process_spin_lock(env, regno, true); + if (err) + return err; } else if (meta->func_id == BPF_FUNC_spin_unlock) { - if (process_spin_lock(env, regno, false)) - return -EACCES; + err = process_spin_lock(env, regno, false); + if (err) + return err; } else { verbose(env, "verifier internal error\n"); return -EFAULT; } break; case ARG_PTR_TO_TIMER: - if (process_timer_func(env, regno, meta)) - return -EACCES; + err = process_timer_func(env, regno, meta); + if (err) + return err; break; case ARG_PTR_TO_FUNC: meta->subprogno = reg->subprogno; @@ -6447,8 +6450,9 @@ skip_type_check: err = check_mem_size_reg(env, reg, regno, true, meta); break; case ARG_PTR_TO_DYNPTR: - if (process_dynptr_func(env, regno, arg_type, meta)) - return -EACCES; + err = process_dynptr_func(env, regno, arg_type, meta); + if (err) + return err; break; case ARG_CONST_ALLOC_SIZE_OR_ZERO: if (!tnum_is_const(reg->var_off)) { @@ -6515,8 +6519,9 @@ skip_type_check: break; } case ARG_PTR_TO_KPTR: - if (process_kptr_func(env, regno, meta)) - return -EACCES; + err = process_kptr_func(env, regno, meta); + if (err) + return err; break; } From 270605317366e4535d8d9fc3d9da1ad0fb3c9d45 Mon Sep 17 00:00:00 2001 From: Kumar Kartikeya Dwivedi Date: Thu, 8 Dec 2022 02:11:37 +0530 Subject: [PATCH 1787/2002] bpf: Rework process_dynptr_func Recently, user ringbuf support introduced a PTR_TO_DYNPTR register type for use in callback state, because in case of user ringbuf helpers, there is no dynptr on the stack that is passed into the callback. To reflect such a state, a special register type was created. However, some checks have been bypassed incorrectly during the addition of this feature. First, for arg_type with MEM_UNINIT flag which initialize a dynptr, they must be rejected for such register type. Secondly, in the future, there are plans to add dynptr helpers that operate on the dynptr itself and may change its offset and other properties. In all of these cases, PTR_TO_DYNPTR shouldn't be allowed to be passed to such helpers, however the current code simply returns 0. The rejection for helpers that release the dynptr is already handled. For fixing this, we take a step back and rework existing code in a way that will allow fitting in all classes of helpers and have a coherent model for dealing with the variety of use cases in which dynptr is used. First, for ARG_PTR_TO_DYNPTR, it can either be set alone or together with a DYNPTR_TYPE_* constant that denotes the only type it accepts. Next, helpers which initialize a dynptr use MEM_UNINIT to indicate this fact. To make the distinction clear, use MEM_RDONLY flag to indicate that the helper only operates on the memory pointed to by the dynptr, not the dynptr itself. In C parlance, it would be equivalent to taking the dynptr as a point to const argument. When either of these flags are not present, the helper is allowed to mutate both the dynptr itself and also the memory it points to. Currently, the read only status of the memory is not tracked in the dynptr, but it would be trivial to add this support inside dynptr state of the register. With these changes and renaming PTR_TO_DYNPTR to CONST_PTR_TO_DYNPTR to better reflect its usage, it can no longer be passed to helpers that initialize a dynptr, i.e. bpf_dynptr_from_mem, bpf_ringbuf_reserve_dynptr. A note to reviewers is that in code that does mark_stack_slots_dynptr, and unmark_stack_slots_dynptr, we implicitly rely on the fact that PTR_TO_STACK reg is the only case that can reach that code path, as one cannot pass CONST_PTR_TO_DYNPTR to helpers that don't set MEM_RDONLY. In both cases such helpers won't be setting that flag. The next patch will add a couple of selftest cases to make sure this doesn't break. Fixes: 205715673844 ("bpf: Add bpf_user_ringbuf_drain() helper") Acked-by: Joanne Koong Signed-off-by: Kumar Kartikeya Dwivedi Link: https://lore.kernel.org/r/20221207204141.308952-4-memxor@gmail.com Signed-off-by: Alexei Starovoitov --- include/linux/bpf.h | 4 +- include/uapi/linux/bpf.h | 8 +- kernel/bpf/helpers.c | 18 +- kernel/bpf/verifier.c | 229 +++++++++++++----- scripts/bpf_doc.py | 1 + tools/include/uapi/linux/bpf.h | 8 +- .../selftests/bpf/prog_tests/user_ringbuf.c | 4 +- 7 files changed, 192 insertions(+), 80 deletions(-) diff --git a/include/linux/bpf.h b/include/linux/bpf.h index 4920ac252754..3de24cfb7a3d 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -775,7 +775,7 @@ enum bpf_reg_type { PTR_TO_MEM, /* reg points to valid memory region */ PTR_TO_BUF, /* reg points to a read/write buffer */ PTR_TO_FUNC, /* reg points to a bpf program function */ - PTR_TO_DYNPTR, /* reg points to a dynptr */ + CONST_PTR_TO_DYNPTR, /* reg points to a const struct bpf_dynptr */ __BPF_REG_TYPE_MAX, /* Extended reg_types. */ @@ -2828,7 +2828,7 @@ void bpf_dynptr_init(struct bpf_dynptr_kern *ptr, void *data, enum bpf_dynptr_type type, u32 offset, u32 size); void bpf_dynptr_set_null(struct bpf_dynptr_kern *ptr); int bpf_dynptr_check_size(u32 size); -u32 bpf_dynptr_get_size(struct bpf_dynptr_kern *ptr); +u32 bpf_dynptr_get_size(const struct bpf_dynptr_kern *ptr); #ifdef CONFIG_BPF_LSM void bpf_cgroup_atype_get(u32 attach_btf_id, int cgroup_atype); diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h index f89de51a45db..464ca3f01fe7 100644 --- a/include/uapi/linux/bpf.h +++ b/include/uapi/linux/bpf.h @@ -5293,7 +5293,7 @@ union bpf_attr { * Return * Nothing. Always succeeds. * - * long bpf_dynptr_read(void *dst, u32 len, struct bpf_dynptr *src, u32 offset, u64 flags) + * long bpf_dynptr_read(void *dst, u32 len, const struct bpf_dynptr *src, u32 offset, u64 flags) * Description * Read *len* bytes from *src* into *dst*, starting from *offset* * into *src*. @@ -5303,7 +5303,7 @@ union bpf_attr { * of *src*'s data, -EINVAL if *src* is an invalid dynptr or if * *flags* is not 0. * - * long bpf_dynptr_write(struct bpf_dynptr *dst, u32 offset, void *src, u32 len, u64 flags) + * long bpf_dynptr_write(const struct bpf_dynptr *dst, u32 offset, void *src, u32 len, u64 flags) * Description * Write *len* bytes from *src* into *dst*, starting from *offset* * into *dst*. @@ -5313,7 +5313,7 @@ union bpf_attr { * of *dst*'s data, -EINVAL if *dst* is an invalid dynptr or if *dst* * is a read-only dynptr or if *flags* is not 0. * - * void *bpf_dynptr_data(struct bpf_dynptr *ptr, u32 offset, u32 len) + * void *bpf_dynptr_data(const struct bpf_dynptr *ptr, u32 offset, u32 len) * Description * Get a pointer to the underlying dynptr data. * @@ -5414,7 +5414,7 @@ union bpf_attr { * Drain samples from the specified user ring buffer, and invoke * the provided callback for each such sample: * - * long (\*callback_fn)(struct bpf_dynptr \*dynptr, void \*ctx); + * long (\*callback_fn)(const struct bpf_dynptr \*dynptr, void \*ctx); * * If **callback_fn** returns 0, the helper will continue to try * and drain the next sample, up to a maximum of diff --git a/kernel/bpf/helpers.c b/kernel/bpf/helpers.c index 6ed5875b97a3..fa1093d4ad6b 100644 --- a/kernel/bpf/helpers.c +++ b/kernel/bpf/helpers.c @@ -1404,7 +1404,7 @@ static const struct bpf_func_proto bpf_kptr_xchg_proto = { #define DYNPTR_SIZE_MASK 0xFFFFFF #define DYNPTR_RDONLY_BIT BIT(31) -static bool bpf_dynptr_is_rdonly(struct bpf_dynptr_kern *ptr) +static bool bpf_dynptr_is_rdonly(const struct bpf_dynptr_kern *ptr) { return ptr->size & DYNPTR_RDONLY_BIT; } @@ -1414,7 +1414,7 @@ static void bpf_dynptr_set_type(struct bpf_dynptr_kern *ptr, enum bpf_dynptr_typ ptr->size |= type << DYNPTR_TYPE_SHIFT; } -u32 bpf_dynptr_get_size(struct bpf_dynptr_kern *ptr) +u32 bpf_dynptr_get_size(const struct bpf_dynptr_kern *ptr) { return ptr->size & DYNPTR_SIZE_MASK; } @@ -1438,7 +1438,7 @@ void bpf_dynptr_set_null(struct bpf_dynptr_kern *ptr) memset(ptr, 0, sizeof(*ptr)); } -static int bpf_dynptr_check_off_len(struct bpf_dynptr_kern *ptr, u32 offset, u32 len) +static int bpf_dynptr_check_off_len(const struct bpf_dynptr_kern *ptr, u32 offset, u32 len) { u32 size = bpf_dynptr_get_size(ptr); @@ -1483,7 +1483,7 @@ static const struct bpf_func_proto bpf_dynptr_from_mem_proto = { .arg4_type = ARG_PTR_TO_DYNPTR | DYNPTR_TYPE_LOCAL | MEM_UNINIT, }; -BPF_CALL_5(bpf_dynptr_read, void *, dst, u32, len, struct bpf_dynptr_kern *, src, +BPF_CALL_5(bpf_dynptr_read, void *, dst, u32, len, const struct bpf_dynptr_kern *, src, u32, offset, u64, flags) { int err; @@ -1506,12 +1506,12 @@ static const struct bpf_func_proto bpf_dynptr_read_proto = { .ret_type = RET_INTEGER, .arg1_type = ARG_PTR_TO_UNINIT_MEM, .arg2_type = ARG_CONST_SIZE_OR_ZERO, - .arg3_type = ARG_PTR_TO_DYNPTR, + .arg3_type = ARG_PTR_TO_DYNPTR | MEM_RDONLY, .arg4_type = ARG_ANYTHING, .arg5_type = ARG_ANYTHING, }; -BPF_CALL_5(bpf_dynptr_write, struct bpf_dynptr_kern *, dst, u32, offset, void *, src, +BPF_CALL_5(bpf_dynptr_write, const struct bpf_dynptr_kern *, dst, u32, offset, void *, src, u32, len, u64, flags) { int err; @@ -1532,14 +1532,14 @@ static const struct bpf_func_proto bpf_dynptr_write_proto = { .func = bpf_dynptr_write, .gpl_only = false, .ret_type = RET_INTEGER, - .arg1_type = ARG_PTR_TO_DYNPTR, + .arg1_type = ARG_PTR_TO_DYNPTR | MEM_RDONLY, .arg2_type = ARG_ANYTHING, .arg3_type = ARG_PTR_TO_MEM | MEM_RDONLY, .arg4_type = ARG_CONST_SIZE_OR_ZERO, .arg5_type = ARG_ANYTHING, }; -BPF_CALL_3(bpf_dynptr_data, struct bpf_dynptr_kern *, ptr, u32, offset, u32, len) +BPF_CALL_3(bpf_dynptr_data, const struct bpf_dynptr_kern *, ptr, u32, offset, u32, len) { int err; @@ -1560,7 +1560,7 @@ static const struct bpf_func_proto bpf_dynptr_data_proto = { .func = bpf_dynptr_data, .gpl_only = false, .ret_type = RET_PTR_TO_DYNPTR_MEM_OR_NULL, - .arg1_type = ARG_PTR_TO_DYNPTR, + .arg1_type = ARG_PTR_TO_DYNPTR | MEM_RDONLY, .arg2_type = ARG_ANYTHING, .arg3_type = ARG_CONST_ALLOC_SIZE_OR_ZERO, }; diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index eb742ac75844..a880776bd999 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -592,7 +592,7 @@ static const char *reg_type_str(struct bpf_verifier_env *env, [PTR_TO_BUF] = "buf", [PTR_TO_FUNC] = "func", [PTR_TO_MAP_KEY] = "map_key", - [PTR_TO_DYNPTR] = "dynptr_ptr", + [CONST_PTR_TO_DYNPTR] = "dynptr_ptr", }; if (type & PTR_MAYBE_NULL) { @@ -725,6 +725,28 @@ static bool dynptr_type_refcounted(enum bpf_dynptr_type type) return type == BPF_DYNPTR_TYPE_RINGBUF; } +static void __mark_dynptr_reg(struct bpf_reg_state *reg, + enum bpf_dynptr_type type, + bool first_slot); + +static void __mark_reg_not_init(const struct bpf_verifier_env *env, + struct bpf_reg_state *reg); + +static void mark_dynptr_stack_regs(struct bpf_reg_state *sreg1, + struct bpf_reg_state *sreg2, + enum bpf_dynptr_type type) +{ + __mark_dynptr_reg(sreg1, type, true); + __mark_dynptr_reg(sreg2, type, false); +} + +static void mark_dynptr_cb_reg(struct bpf_reg_state *reg, + enum bpf_dynptr_type type) +{ + __mark_dynptr_reg(reg, type, true); +} + + static int mark_stack_slots_dynptr(struct bpf_verifier_env *env, struct bpf_reg_state *reg, enum bpf_arg_type arg_type, int insn_idx) { @@ -746,9 +768,8 @@ static int mark_stack_slots_dynptr(struct bpf_verifier_env *env, struct bpf_reg_ if (type == BPF_DYNPTR_TYPE_INVALID) return -EINVAL; - state->stack[spi].spilled_ptr.dynptr.first_slot = true; - state->stack[spi].spilled_ptr.dynptr.type = type; - state->stack[spi - 1].spilled_ptr.dynptr.type = type; + mark_dynptr_stack_regs(&state->stack[spi].spilled_ptr, + &state->stack[spi - 1].spilled_ptr, type); if (dynptr_type_refcounted(type)) { /* The id is used to track proper releasing */ @@ -756,8 +777,8 @@ static int mark_stack_slots_dynptr(struct bpf_verifier_env *env, struct bpf_reg_ if (id < 0) return id; - state->stack[spi].spilled_ptr.id = id; - state->stack[spi - 1].spilled_ptr.id = id; + state->stack[spi].spilled_ptr.ref_obj_id = id; + state->stack[spi - 1].spilled_ptr.ref_obj_id = id; } return 0; @@ -779,25 +800,23 @@ static int unmark_stack_slots_dynptr(struct bpf_verifier_env *env, struct bpf_re } /* Invalidate any slices associated with this dynptr */ - if (dynptr_type_refcounted(state->stack[spi].spilled_ptr.dynptr.type)) { - release_reference(env, state->stack[spi].spilled_ptr.id); - state->stack[spi].spilled_ptr.id = 0; - state->stack[spi - 1].spilled_ptr.id = 0; - } - - state->stack[spi].spilled_ptr.dynptr.first_slot = false; - state->stack[spi].spilled_ptr.dynptr.type = 0; - state->stack[spi - 1].spilled_ptr.dynptr.type = 0; + if (dynptr_type_refcounted(state->stack[spi].spilled_ptr.dynptr.type)) + WARN_ON_ONCE(release_reference(env, state->stack[spi].spilled_ptr.ref_obj_id)); + __mark_reg_not_init(env, &state->stack[spi].spilled_ptr); + __mark_reg_not_init(env, &state->stack[spi - 1].spilled_ptr); return 0; } static bool is_dynptr_reg_valid_uninit(struct bpf_verifier_env *env, struct bpf_reg_state *reg) { struct bpf_func_state *state = func(env, reg); - int spi = get_spi(reg->off); - int i; + int spi, i; + if (reg->type == CONST_PTR_TO_DYNPTR) + return false; + + spi = get_spi(reg->off); if (!is_spi_bounds_valid(state, spi, BPF_DYNPTR_NR_SLOTS)) return true; @@ -813,9 +832,14 @@ static bool is_dynptr_reg_valid_uninit(struct bpf_verifier_env *env, struct bpf_ static bool is_dynptr_reg_valid_init(struct bpf_verifier_env *env, struct bpf_reg_state *reg) { struct bpf_func_state *state = func(env, reg); - int spi = get_spi(reg->off); + int spi; int i; + /* This already represents first slot of initialized bpf_dynptr */ + if (reg->type == CONST_PTR_TO_DYNPTR) + return true; + + spi = get_spi(reg->off); if (!is_spi_bounds_valid(state, spi, BPF_DYNPTR_NR_SLOTS) || !state->stack[spi].spilled_ptr.dynptr.first_slot) return false; @@ -834,15 +858,19 @@ static bool is_dynptr_type_expected(struct bpf_verifier_env *env, struct bpf_reg { struct bpf_func_state *state = func(env, reg); enum bpf_dynptr_type dynptr_type; - int spi = get_spi(reg->off); + int spi; /* ARG_PTR_TO_DYNPTR takes any type of dynptr */ if (arg_type == ARG_PTR_TO_DYNPTR) return true; dynptr_type = arg_to_dynptr_type(arg_type); - - return state->stack[spi].spilled_ptr.dynptr.type == dynptr_type; + if (reg->type == CONST_PTR_TO_DYNPTR) { + return reg->dynptr.type == dynptr_type; + } else { + spi = get_spi(reg->off); + return state->stack[spi].spilled_ptr.dynptr.type == dynptr_type; + } } /* The reg state of a pointer or a bounded scalar was saved when @@ -1354,9 +1382,6 @@ static const int caller_saved[CALLER_SAVED_REGS] = { BPF_REG_0, BPF_REG_1, BPF_REG_2, BPF_REG_3, BPF_REG_4, BPF_REG_5 }; -static void __mark_reg_not_init(const struct bpf_verifier_env *env, - struct bpf_reg_state *reg); - /* This helper doesn't clear reg->id */ static void ___mark_reg_known(struct bpf_reg_state *reg, u64 imm) { @@ -1419,6 +1444,19 @@ static void mark_reg_known_zero(struct bpf_verifier_env *env, __mark_reg_known_zero(regs + regno); } +static void __mark_dynptr_reg(struct bpf_reg_state *reg, enum bpf_dynptr_type type, + bool first_slot) +{ + /* reg->type has no meaning for STACK_DYNPTR, but when we set reg for + * callback arguments, it does need to be CONST_PTR_TO_DYNPTR, so simply + * set it unconditionally as it is ignored for STACK_DYNPTR anyway. + */ + __mark_reg_known_zero(reg); + reg->type = CONST_PTR_TO_DYNPTR; + reg->dynptr.type = type; + reg->dynptr.first_slot = first_slot; +} + static void mark_ptr_not_null_reg(struct bpf_reg_state *reg) { if (base_type(reg->type) == PTR_TO_MAP_VALUE) { @@ -5857,19 +5895,58 @@ static int process_kptr_func(struct bpf_verifier_env *env, int regno, return 0; } +/* There are two register types representing a bpf_dynptr, one is PTR_TO_STACK + * which points to a stack slot, and the other is CONST_PTR_TO_DYNPTR. + * + * In both cases we deal with the first 8 bytes, but need to mark the next 8 + * bytes as STACK_DYNPTR in case of PTR_TO_STACK. In case of + * CONST_PTR_TO_DYNPTR, we are guaranteed to get the beginning of the object. + * + * Mutability of bpf_dynptr is at two levels, one is at the level of struct + * bpf_dynptr itself, i.e. whether the helper is receiving a pointer to struct + * bpf_dynptr or pointer to const struct bpf_dynptr. In the former case, it can + * mutate the view of the dynptr and also possibly destroy it. In the latter + * case, it cannot mutate the bpf_dynptr itself but it can still mutate the + * memory that dynptr points to. + * + * The verifier will keep track both levels of mutation (bpf_dynptr's in + * reg->type and the memory's in reg->dynptr.type), but there is no support for + * readonly dynptr view yet, hence only the first case is tracked and checked. + * + * This is consistent with how C applies the const modifier to a struct object, + * where the pointer itself inside bpf_dynptr becomes const but not what it + * points to. + * + * Helpers which do not mutate the bpf_dynptr set MEM_RDONLY in their argument + * type, and declare it as 'const struct bpf_dynptr *' in their prototype. + */ int process_dynptr_func(struct bpf_verifier_env *env, int regno, - enum bpf_arg_type arg_type, - struct bpf_call_arg_meta *meta) + enum bpf_arg_type arg_type, struct bpf_call_arg_meta *meta) { struct bpf_reg_state *regs = cur_regs(env), *reg = ®s[regno]; - /* We only need to check for initialized / uninitialized helper - * dynptr args if the dynptr is not PTR_TO_DYNPTR, as the - * assumption is that if it is, that a helper function - * initialized the dynptr on behalf of the BPF program. + /* MEM_UNINIT and MEM_RDONLY are exclusive, when applied to an + * ARG_PTR_TO_DYNPTR (or ARG_PTR_TO_DYNPTR | DYNPTR_TYPE_*): + */ + if ((arg_type & (MEM_UNINIT | MEM_RDONLY)) == (MEM_UNINIT | MEM_RDONLY)) { + verbose(env, "verifier internal error: misconfigured dynptr helper type flags\n"); + return -EFAULT; + } + /* MEM_UNINIT - Points to memory that is an appropriate candidate for + * constructing a mutable bpf_dynptr object. + * + * Currently, this is only possible with PTR_TO_STACK + * pointing to a region of at least 16 bytes which doesn't + * contain an existing bpf_dynptr. + * + * MEM_RDONLY - Points to a initialized bpf_dynptr that will not be + * mutated or destroyed. However, the memory it points to + * may be mutated. + * + * None - Points to a initialized dynptr that can be mutated and + * destroyed, including mutation of the memory it points + * to. */ - if (base_type(reg->type) == PTR_TO_DYNPTR) - return 0; if (arg_type & MEM_UNINIT) { if (!is_dynptr_reg_valid_uninit(env, reg)) { verbose(env, "Dynptr has to be an uninitialized dynptr\n"); @@ -5885,7 +5962,13 @@ int process_dynptr_func(struct bpf_verifier_env *env, int regno, } meta->uninit_dynptr_regno = regno; - } else { + } else /* MEM_RDONLY and None case from above */ { + /* For the reg->type == PTR_TO_STACK case, bpf_dynptr is never const */ + if (reg->type == CONST_PTR_TO_DYNPTR && !(arg_type & MEM_RDONLY)) { + verbose(env, "cannot pass pointer to const bpf_dynptr, the helper mutates it\n"); + return -EINVAL; + } + if (!is_dynptr_reg_valid_init(env, reg)) { verbose(env, "Expected an initialized dynptr as arg #%d\n", @@ -5893,7 +5976,8 @@ int process_dynptr_func(struct bpf_verifier_env *env, int regno, return -EINVAL; } - if (!is_dynptr_type_expected(env, reg, arg_type)) { + /* Fold modifiers (in this case, MEM_RDONLY) when checking expected type */ + if (!is_dynptr_type_expected(env, reg, arg_type & ~MEM_RDONLY)) { const char *err_extra = ""; switch (arg_type & DYNPTR_TYPE_FLAG_MASK) { @@ -6056,7 +6140,7 @@ static const struct bpf_reg_types kptr_types = { .types = { PTR_TO_MAP_VALUE } } static const struct bpf_reg_types dynptr_types = { .types = { PTR_TO_STACK, - PTR_TO_DYNPTR | DYNPTR_TYPE_LOCAL, + CONST_PTR_TO_DYNPTR, } }; @@ -6241,12 +6325,16 @@ int check_func_arg_reg_off(struct bpf_verifier_env *env, return __check_ptr_off_reg(env, reg, regno, fixed_off_ok); } -static u32 stack_slot_get_id(struct bpf_verifier_env *env, struct bpf_reg_state *reg) +static u32 dynptr_ref_obj_id(struct bpf_verifier_env *env, struct bpf_reg_state *reg) { struct bpf_func_state *state = func(env, reg); - int spi = get_spi(reg->off); + int spi; - return state->stack[spi].spilled_ptr.id; + if (reg->type == CONST_PTR_TO_DYNPTR) + return reg->ref_obj_id; + + spi = get_spi(reg->off); + return state->stack[spi].spilled_ptr.ref_obj_id; } static int check_func_arg(struct bpf_verifier_env *env, u32 arg, @@ -6311,11 +6399,22 @@ skip_type_check: if (arg_type_is_release(arg_type)) { if (arg_type_is_dynptr(arg_type)) { struct bpf_func_state *state = func(env, reg); - int spi = get_spi(reg->off); + int spi; - if (!is_spi_bounds_valid(state, spi, BPF_DYNPTR_NR_SLOTS) || - !state->stack[spi].spilled_ptr.id) { - verbose(env, "arg %d is an unacquired reference\n", regno); + /* Only dynptr created on stack can be released, thus + * the get_spi and stack state checks for spilled_ptr + * should only be done before process_dynptr_func for + * PTR_TO_STACK. + */ + if (reg->type == PTR_TO_STACK) { + spi = get_spi(reg->off); + if (!is_spi_bounds_valid(state, spi, BPF_DYNPTR_NR_SLOTS) || + !state->stack[spi].spilled_ptr.ref_obj_id) { + verbose(env, "arg %d is an unacquired reference\n", regno); + return -EINVAL; + } + } else { + verbose(env, "cannot release unowned const bpf_dynptr\n"); return -EINVAL; } } else if (!reg->ref_obj_id && !register_is_null(reg)) { @@ -7289,11 +7388,10 @@ static int set_user_ringbuf_callback_state(struct bpf_verifier_env *env, { /* bpf_user_ringbuf_drain(struct bpf_map *map, void *callback_fn, void * callback_ctx, u64 flags); - * callback_fn(struct bpf_dynptr_t* dynptr, void *callback_ctx); + * callback_fn(const struct bpf_dynptr_t* dynptr, void *callback_ctx); */ __mark_reg_not_init(env, &callee->regs[BPF_REG_0]); - callee->regs[BPF_REG_1].type = PTR_TO_DYNPTR | DYNPTR_TYPE_LOCAL; - __mark_reg_known_zero(&callee->regs[BPF_REG_1]); + mark_dynptr_cb_reg(&callee->regs[BPF_REG_1], BPF_DYNPTR_TYPE_LOCAL); callee->regs[BPF_REG_2] = caller->regs[BPF_REG_3]; /* unused */ @@ -7687,7 +7785,15 @@ static int check_helper_call(struct bpf_verifier_env *env, struct bpf_insn *insn regs = cur_regs(env); + /* This can only be set for PTR_TO_STACK, as CONST_PTR_TO_DYNPTR cannot + * be reinitialized by any dynptr helper. Hence, mark_stack_slots_dynptr + * is safe to do directly. + */ if (meta.uninit_dynptr_regno) { + if (regs[meta.uninit_dynptr_regno].type == CONST_PTR_TO_DYNPTR) { + verbose(env, "verifier internal error: CONST_PTR_TO_DYNPTR cannot be initialized\n"); + return -EFAULT; + } /* we write BPF_DW bits (8 bytes) at a time */ for (i = 0; i < BPF_DYNPTR_SIZE; i += 8) { err = check_mem_access(env, insn_idx, meta.uninit_dynptr_regno, @@ -7705,15 +7811,24 @@ static int check_helper_call(struct bpf_verifier_env *env, struct bpf_insn *insn if (meta.release_regno) { err = -EINVAL; - if (arg_type_is_dynptr(fn->arg_type[meta.release_regno - BPF_REG_1])) - err = unmark_stack_slots_dynptr(env, ®s[meta.release_regno]); - else if (meta.ref_obj_id) - err = release_reference(env, meta.ref_obj_id); - /* meta.ref_obj_id can only be 0 if register that is meant to be - * released is NULL, which must be > R0. + /* This can only be set for PTR_TO_STACK, as CONST_PTR_TO_DYNPTR cannot + * be released by any dynptr helper. Hence, unmark_stack_slots_dynptr + * is safe to do directly. */ - else if (register_is_null(®s[meta.release_regno])) + if (arg_type_is_dynptr(fn->arg_type[meta.release_regno - BPF_REG_1])) { + if (regs[meta.release_regno].type == CONST_PTR_TO_DYNPTR) { + verbose(env, "verifier internal error: CONST_PTR_TO_DYNPTR cannot be released\n"); + return -EFAULT; + } + err = unmark_stack_slots_dynptr(env, ®s[meta.release_regno]); + } else if (meta.ref_obj_id) { + err = release_reference(env, meta.ref_obj_id); + } else if (register_is_null(®s[meta.release_regno])) { + /* meta.ref_obj_id can only be 0 if register that is meant to be + * released is NULL, which must be > R0. + */ err = 0; + } if (err) { verbose(env, "func %s#%d reference has not been acquired before\n", func_id_name(func_id), func_id); @@ -7787,11 +7902,7 @@ static int check_helper_call(struct bpf_verifier_env *env, struct bpf_insn *insn return -EFAULT; } - if (base_type(reg->type) != PTR_TO_DYNPTR) - /* Find the id of the dynptr we're - * tracking the reference of - */ - meta.ref_obj_id = stack_slot_get_id(env, reg); + meta.ref_obj_id = dynptr_ref_obj_id(env, reg); break; } } @@ -8848,12 +8959,12 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_kfunc_call_ break; case KF_ARG_PTR_TO_DYNPTR: if (reg->type != PTR_TO_STACK && - reg->type != PTR_TO_DYNPTR) { + reg->type != CONST_PTR_TO_DYNPTR) { verbose(env, "arg#%d expected pointer to stack or dynptr_ptr\n", i); return -EINVAL; } - ret = process_dynptr_func(env, regno, ARG_PTR_TO_DYNPTR, NULL); + ret = process_dynptr_func(env, regno, ARG_PTR_TO_DYNPTR | MEM_RDONLY, NULL); if (ret < 0) return ret; break; diff --git a/scripts/bpf_doc.py b/scripts/bpf_doc.py index fdb0aff8cb5a..e8d90829f23e 100755 --- a/scripts/bpf_doc.py +++ b/scripts/bpf_doc.py @@ -752,6 +752,7 @@ class PrinterHelpers(Printer): 'struct bpf_timer', 'struct mptcp_sock', 'struct bpf_dynptr', + 'const struct bpf_dynptr', 'struct iphdr', 'struct ipv6hdr', } diff --git a/tools/include/uapi/linux/bpf.h b/tools/include/uapi/linux/bpf.h index f89de51a45db..464ca3f01fe7 100644 --- a/tools/include/uapi/linux/bpf.h +++ b/tools/include/uapi/linux/bpf.h @@ -5293,7 +5293,7 @@ union bpf_attr { * Return * Nothing. Always succeeds. * - * long bpf_dynptr_read(void *dst, u32 len, struct bpf_dynptr *src, u32 offset, u64 flags) + * long bpf_dynptr_read(void *dst, u32 len, const struct bpf_dynptr *src, u32 offset, u64 flags) * Description * Read *len* bytes from *src* into *dst*, starting from *offset* * into *src*. @@ -5303,7 +5303,7 @@ union bpf_attr { * of *src*'s data, -EINVAL if *src* is an invalid dynptr or if * *flags* is not 0. * - * long bpf_dynptr_write(struct bpf_dynptr *dst, u32 offset, void *src, u32 len, u64 flags) + * long bpf_dynptr_write(const struct bpf_dynptr *dst, u32 offset, void *src, u32 len, u64 flags) * Description * Write *len* bytes from *src* into *dst*, starting from *offset* * into *dst*. @@ -5313,7 +5313,7 @@ union bpf_attr { * of *dst*'s data, -EINVAL if *dst* is an invalid dynptr or if *dst* * is a read-only dynptr or if *flags* is not 0. * - * void *bpf_dynptr_data(struct bpf_dynptr *ptr, u32 offset, u32 len) + * void *bpf_dynptr_data(const struct bpf_dynptr *ptr, u32 offset, u32 len) * Description * Get a pointer to the underlying dynptr data. * @@ -5414,7 +5414,7 @@ union bpf_attr { * Drain samples from the specified user ring buffer, and invoke * the provided callback for each such sample: * - * long (\*callback_fn)(struct bpf_dynptr \*dynptr, void \*ctx); + * long (\*callback_fn)(const struct bpf_dynptr \*dynptr, void \*ctx); * * If **callback_fn** returns 0, the helper will continue to try * and drain the next sample, up to a maximum of diff --git a/tools/testing/selftests/bpf/prog_tests/user_ringbuf.c b/tools/testing/selftests/bpf/prog_tests/user_ringbuf.c index 02b18d018b36..aefa0a474e58 100644 --- a/tools/testing/selftests/bpf/prog_tests/user_ringbuf.c +++ b/tools/testing/selftests/bpf/prog_tests/user_ringbuf.c @@ -673,8 +673,8 @@ static struct { {"user_ringbuf_callback_write_forbidden", "invalid mem access 'dynptr_ptr'"}, {"user_ringbuf_callback_null_context_write", "invalid mem access 'scalar'"}, {"user_ringbuf_callback_null_context_read", "invalid mem access 'scalar'"}, - {"user_ringbuf_callback_discard_dynptr", "arg 1 is an unacquired reference"}, - {"user_ringbuf_callback_submit_dynptr", "arg 1 is an unacquired reference"}, + {"user_ringbuf_callback_discard_dynptr", "cannot release unowned const bpf_dynptr"}, + {"user_ringbuf_callback_submit_dynptr", "cannot release unowned const bpf_dynptr"}, {"user_ringbuf_callback_invalid_return", "At callback return the register R0 has value"}, }; From 184c9bdb8f65d9f909b3a089a83bc0b0f1e1ea4c Mon Sep 17 00:00:00 2001 From: Kumar Kartikeya Dwivedi Date: Thu, 8 Dec 2022 02:11:38 +0530 Subject: [PATCH 1788/2002] bpf: Rework check_func_arg_reg_off While check_func_arg_reg_off is the place which performs generic checks needed by various candidates of reg->type, there is some handling for special cases, like ARG_PTR_TO_DYNPTR, OBJ_RELEASE, and ARG_PTR_TO_RINGBUF_MEM. This commit aims to streamline these special cases and instead leave other things up to argument type specific code to handle. The function will be restrictive by default, and cover all possible cases when OBJ_RELEASE is set, without having to update the function again (and missing to do that being a bug). This is done primarily for two reasons: associating back reg->type to its argument leaves room for the list getting out of sync when a new reg->type is supported by an arg_type. The other case is ARG_PTR_TO_RINGBUF_MEM. The problem there is something we already handle, whenever a release argument is expected, it should be passed as the pointer that was received from the acquire function. Hence zero fixed and variable offset. There is nothing special about ARG_PTR_TO_RINGBUF_MEM, where technically its target register type PTR_TO_MEM | MEM_RINGBUF can already be passed with non-zero offset to other helper functions, which makes sense. Hence, lift the arg_type_is_release check for reg->off and cover all possible register types, instead of duplicating the same kind of check twice for current OBJ_RELEASE arg_types (alloc_mem and ptr_to_btf_id). For the release argument, arg_type_is_dynptr is the special case, where we go to actual object being freed through the dynptr, so the offset of the pointer still needs to allow fixed and variable offset and process_dynptr_func will verify them later for the release argument case as well. This is not specific to ARG_PTR_TO_DYNPTR though, we will need to make this exception for any future object on the stack that needs to be released. In this sense, PTR_TO_STACK as a candidate for object on stack argument is a special case for release offset checks, and they need to be done by the helper releasing the object on stack. Since the check has been lifted above all register type checks, remove the duplicated check that is being done for PTR_TO_BTF_ID. Acked-by: Joanne Koong Acked-by: David Vernet Signed-off-by: Kumar Kartikeya Dwivedi Link: https://lore.kernel.org/r/20221207204141.308952-5-memxor@gmail.com Signed-off-by: Alexei Starovoitov --- kernel/bpf/verifier.c | 63 +++++++++++-------- tools/testing/selftests/bpf/verifier/calls.c | 2 +- .../testing/selftests/bpf/verifier/ringbuf.c | 2 +- 3 files changed, 40 insertions(+), 27 deletions(-) diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index a880776bd999..8a7e964bcc31 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -6269,11 +6269,37 @@ int check_func_arg_reg_off(struct bpf_verifier_env *env, const struct bpf_reg_state *reg, int regno, enum bpf_arg_type arg_type) { - enum bpf_reg_type type = reg->type; - bool fixed_off_ok = false; + u32 type = reg->type; - switch ((u32)type) { - /* Pointer types where reg offset is explicitly allowed: */ + /* When referenced register is passed to release function, its fixed + * offset must be 0. + * + * We will check arg_type_is_release reg has ref_obj_id when storing + * meta->release_regno. + */ + if (arg_type_is_release(arg_type)) { + /* ARG_PTR_TO_DYNPTR with OBJ_RELEASE is a bit special, as it + * may not directly point to the object being released, but to + * dynptr pointing to such object, which might be at some offset + * on the stack. In that case, we simply to fallback to the + * default handling. + */ + if (arg_type_is_dynptr(arg_type) && type == PTR_TO_STACK) + return 0; + /* Doing check_ptr_off_reg check for the offset will catch this + * because fixed_off_ok is false, but checking here allows us + * to give the user a better error message. + */ + if (reg->off) { + verbose(env, "R%d must have zero offset when passed to release func or trusted arg to kfunc\n", + regno); + return -EINVAL; + } + return __check_ptr_off_reg(env, reg, regno, false); + } + + switch (type) { + /* Pointer types where both fixed and variable offset is explicitly allowed: */ case PTR_TO_STACK: if (arg_type_is_dynptr(arg_type) && reg->off % BPF_REG_SIZE) { verbose(env, "cannot pass in dynptr at an offset\n"); @@ -6290,12 +6316,7 @@ int check_func_arg_reg_off(struct bpf_verifier_env *env, case PTR_TO_BUF: case PTR_TO_BUF | MEM_RDONLY: case SCALAR_VALUE: - /* Some of the argument types nevertheless require a - * zero register offset. - */ - if (base_type(arg_type) != ARG_PTR_TO_RINGBUF_MEM) - return 0; - break; + return 0; /* All the rest must be rejected, except PTR_TO_BTF_ID which allows * fixed offset. */ @@ -6305,24 +6326,16 @@ int check_func_arg_reg_off(struct bpf_verifier_env *env, case PTR_TO_BTF_ID | MEM_RCU: case PTR_TO_BTF_ID | MEM_ALLOC | PTR_TRUSTED: /* When referenced PTR_TO_BTF_ID is passed to release function, - * it's fixed offset must be 0. In the other cases, fixed offset - * can be non-zero. + * its fixed offset must be 0. In the other cases, fixed offset + * can be non-zero. This was already checked above. So pass + * fixed_off_ok as true to allow fixed offset for all other + * cases. var_off always must be 0 for PTR_TO_BTF_ID, hence we + * still need to do checks instead of returning. */ - if (arg_type_is_release(arg_type) && reg->off) { - verbose(env, "R%d must have zero offset when passed to release func\n", - regno); - return -EINVAL; - } - /* For arg is release pointer, fixed_off_ok must be false, but - * we already checked and rejected reg->off != 0 above, so set - * to true to allow fixed offset for all other cases. - */ - fixed_off_ok = true; - break; + return __check_ptr_off_reg(env, reg, regno, true); default: - break; + return __check_ptr_off_reg(env, reg, regno, false); } - return __check_ptr_off_reg(env, reg, regno, fixed_off_ok); } static u32 dynptr_ref_obj_id(struct bpf_verifier_env *env, struct bpf_reg_state *reg) diff --git a/tools/testing/selftests/bpf/verifier/calls.c b/tools/testing/selftests/bpf/verifier/calls.c index 3193915c5ee6..babcec123251 100644 --- a/tools/testing/selftests/bpf/verifier/calls.c +++ b/tools/testing/selftests/bpf/verifier/calls.c @@ -76,7 +76,7 @@ }, .prog_type = BPF_PROG_TYPE_SCHED_CLS, .result = REJECT, - .errstr = "arg#0 expected pointer to ctx, but got PTR", + .errstr = "R1 must have zero offset when passed to release func or trusted arg to kfunc", .fixup_kfunc_btf_id = { { "bpf_kfunc_call_test_pass_ctx", 2 }, }, diff --git a/tools/testing/selftests/bpf/verifier/ringbuf.c b/tools/testing/selftests/bpf/verifier/ringbuf.c index 84838feba47f..92e3f6a61a79 100644 --- a/tools/testing/selftests/bpf/verifier/ringbuf.c +++ b/tools/testing/selftests/bpf/verifier/ringbuf.c @@ -28,7 +28,7 @@ }, .fixup_map_ringbuf = { 1 }, .result = REJECT, - .errstr = "dereference of modified ringbuf_mem ptr R1", + .errstr = "R1 must have zero offset when passed to release func", }, { "ringbuf: invalid reservation offset 2", From f6ee298fa140f6b5bae3acf6d6108741278bcced Mon Sep 17 00:00:00 2001 From: Kumar Kartikeya Dwivedi Date: Thu, 8 Dec 2022 02:11:39 +0530 Subject: [PATCH 1789/2002] bpf: Move PTR_TO_STACK alignment check to process_dynptr_func After previous commit, we are minimizing helper specific assumptions from check_func_arg_reg_off, making it generic, and offloading checks for a specific argument type to their respective functions called after check_func_arg_reg_off has been called. This allows relying on a consistent set of guarantees after that call and then relying on them in code that deals with registers for each argument type later. This is in line with how process_spin_lock, process_timer_func, process_kptr_func check reg->var_off to be constant. The same reasoning is used here to move the alignment check into process_dynptr_func. Note that it also needs to check for constant var_off, and accumulate the constant var_off when computing the spi in get_spi, but that fix will come in later changes. Acked-by: Joanne Koong Signed-off-by: Kumar Kartikeya Dwivedi Link: https://lore.kernel.org/r/20221207204141.308952-6-memxor@gmail.com Signed-off-by: Alexei Starovoitov --- kernel/bpf/verifier.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 8a7e964bcc31..9791788071d5 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -5932,6 +5932,14 @@ int process_dynptr_func(struct bpf_verifier_env *env, int regno, verbose(env, "verifier internal error: misconfigured dynptr helper type flags\n"); return -EFAULT; } + /* CONST_PTR_TO_DYNPTR already has fixed and var_off as 0 due to + * check_func_arg_reg_off's logic. We only need to check offset + * alignment for PTR_TO_STACK. + */ + if (reg->type == PTR_TO_STACK && (reg->off % BPF_REG_SIZE)) { + verbose(env, "cannot pass in dynptr at an offset=%d\n", reg->off); + return -EINVAL; + } /* MEM_UNINIT - Points to memory that is an appropriate candidate for * constructing a mutable bpf_dynptr object. * @@ -6301,11 +6309,6 @@ int check_func_arg_reg_off(struct bpf_verifier_env *env, switch (type) { /* Pointer types where both fixed and variable offset is explicitly allowed: */ case PTR_TO_STACK: - if (arg_type_is_dynptr(arg_type) && reg->off % BPF_REG_SIZE) { - verbose(env, "cannot pass in dynptr at an offset\n"); - return -EINVAL; - } - fallthrough; case PTR_TO_PACKET: case PTR_TO_PACKET_META: case PTR_TO_MAP_KEY: From 76d16077bef0954528ec3896710f9eda8b2b4db1 Mon Sep 17 00:00:00 2001 From: Kumar Kartikeya Dwivedi Date: Thu, 8 Dec 2022 02:11:40 +0530 Subject: [PATCH 1790/2002] bpf: Use memmove for bpf_dynptr_{read,write} It may happen that destination buffer memory overlaps with memory dynptr points to. Hence, we must use memmove to correctly copy from dynptr to destination buffer, or source buffer to dynptr. This actually isn't a problem right now, as memcpy implementation falls back to memmove on detecting overlap and warns about it, but we shouldn't be relying on that. Acked-by: Joanne Koong Acked-by: David Vernet Signed-off-by: Kumar Kartikeya Dwivedi Link: https://lore.kernel.org/r/20221207204141.308952-7-memxor@gmail.com Signed-off-by: Alexei Starovoitov --- kernel/bpf/helpers.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/kernel/bpf/helpers.c b/kernel/bpf/helpers.c index fa1093d4ad6b..af30c6cbd65d 100644 --- a/kernel/bpf/helpers.c +++ b/kernel/bpf/helpers.c @@ -1495,7 +1495,11 @@ BPF_CALL_5(bpf_dynptr_read, void *, dst, u32, len, const struct bpf_dynptr_kern if (err) return err; - memcpy(dst, src->data + src->offset + offset, len); + /* Source and destination may possibly overlap, hence use memmove to + * copy the data. E.g. bpf_dynptr_from_mem may create two dynptr + * pointing to overlapping PTR_TO_MAP_VALUE regions. + */ + memmove(dst, src->data + src->offset + offset, len); return 0; } @@ -1523,7 +1527,11 @@ BPF_CALL_5(bpf_dynptr_write, const struct bpf_dynptr_kern *, dst, u32, offset, v if (err) return err; - memcpy(dst->data + dst->offset + offset, src, len); + /* Source and destination may possibly overlap, hence use memmove to + * copy the data. E.g. bpf_dynptr_from_mem may create two dynptr + * pointing to overlapping PTR_TO_MAP_VALUE regions. + */ + memmove(dst->data + dst->offset + offset, src, len); return 0; } From 292064cce7969787f13ed9988733aadc12fe0ca2 Mon Sep 17 00:00:00 2001 From: Kumar Kartikeya Dwivedi Date: Thu, 8 Dec 2022 02:11:41 +0530 Subject: [PATCH 1791/2002] selftests/bpf: Add test for dynptr reinit in user_ringbuf callback The original support for bpf_user_ringbuf_drain callbacks simply short-circuited checks for the dynptr state, allowing users to pass PTR_TO_DYNPTR (now CONST_PTR_TO_DYNPTR) to helpers that initialize a dynptr. This bug would have also surfaced with other dynptr helpers in the future that changed dynptr view or modified it in some way. Include test cases for all cases, i.e. both bpf_dynptr_from_mem and bpf_ringbuf_reserve_dynptr, and ensure verifier rejects both of them. Without the fix, both of these programs load and pass verification. While at it, remove sys_nanosleep target from failure cases' SEC definition, as there is no such tracepoint. Acked-by: David Vernet Acked-by: Joanne Koong Signed-off-by: Kumar Kartikeya Dwivedi Link: https://lore.kernel.org/r/20221207204141.308952-8-memxor@gmail.com Signed-off-by: Alexei Starovoitov --- .../selftests/bpf/prog_tests/user_ringbuf.c | 2 + .../selftests/bpf/progs/user_ringbuf_fail.c | 51 ++++++++++++++++--- 2 files changed, 45 insertions(+), 8 deletions(-) diff --git a/tools/testing/selftests/bpf/prog_tests/user_ringbuf.c b/tools/testing/selftests/bpf/prog_tests/user_ringbuf.c index aefa0a474e58..dae68de285b9 100644 --- a/tools/testing/selftests/bpf/prog_tests/user_ringbuf.c +++ b/tools/testing/selftests/bpf/prog_tests/user_ringbuf.c @@ -676,6 +676,8 @@ static struct { {"user_ringbuf_callback_discard_dynptr", "cannot release unowned const bpf_dynptr"}, {"user_ringbuf_callback_submit_dynptr", "cannot release unowned const bpf_dynptr"}, {"user_ringbuf_callback_invalid_return", "At callback return the register R0 has value"}, + {"user_ringbuf_callback_reinit_dynptr_mem", "Dynptr has to be an uninitialized dynptr"}, + {"user_ringbuf_callback_reinit_dynptr_ringbuf", "Dynptr has to be an uninitialized dynptr"}, }; #define SUCCESS_TEST(_func) { _func, #_func } diff --git a/tools/testing/selftests/bpf/progs/user_ringbuf_fail.c b/tools/testing/selftests/bpf/progs/user_ringbuf_fail.c index 82aba4529aa9..f3201dc69a60 100644 --- a/tools/testing/selftests/bpf/progs/user_ringbuf_fail.c +++ b/tools/testing/selftests/bpf/progs/user_ringbuf_fail.c @@ -18,6 +18,13 @@ struct { __uint(type, BPF_MAP_TYPE_USER_RINGBUF); } user_ringbuf SEC(".maps"); +struct { + __uint(type, BPF_MAP_TYPE_RINGBUF); + __uint(max_entries, 2); +} ringbuf SEC(".maps"); + +static int map_value; + static long bad_access1(struct bpf_dynptr *dynptr, void *context) { @@ -32,7 +39,7 @@ bad_access1(struct bpf_dynptr *dynptr, void *context) /* A callback that accesses a dynptr in a bpf_user_ringbuf_drain callback should * not be able to read before the pointer. */ -SEC("?raw_tp/sys_nanosleep") +SEC("?raw_tp/") int user_ringbuf_callback_bad_access1(void *ctx) { bpf_user_ringbuf_drain(&user_ringbuf, bad_access1, NULL, 0); @@ -54,7 +61,7 @@ bad_access2(struct bpf_dynptr *dynptr, void *context) /* A callback that accesses a dynptr in a bpf_user_ringbuf_drain callback should * not be able to read past the end of the pointer. */ -SEC("?raw_tp/sys_nanosleep") +SEC("?raw_tp/") int user_ringbuf_callback_bad_access2(void *ctx) { bpf_user_ringbuf_drain(&user_ringbuf, bad_access2, NULL, 0); @@ -73,7 +80,7 @@ write_forbidden(struct bpf_dynptr *dynptr, void *context) /* A callback that accesses a dynptr in a bpf_user_ringbuf_drain callback should * not be able to write to that pointer. */ -SEC("?raw_tp/sys_nanosleep") +SEC("?raw_tp/") int user_ringbuf_callback_write_forbidden(void *ctx) { bpf_user_ringbuf_drain(&user_ringbuf, write_forbidden, NULL, 0); @@ -92,7 +99,7 @@ null_context_write(struct bpf_dynptr *dynptr, void *context) /* A callback that accesses a dynptr in a bpf_user_ringbuf_drain callback should * not be able to write to that pointer. */ -SEC("?raw_tp/sys_nanosleep") +SEC("?raw_tp/") int user_ringbuf_callback_null_context_write(void *ctx) { bpf_user_ringbuf_drain(&user_ringbuf, null_context_write, NULL, 0); @@ -113,7 +120,7 @@ null_context_read(struct bpf_dynptr *dynptr, void *context) /* A callback that accesses a dynptr in a bpf_user_ringbuf_drain callback should * not be able to write to that pointer. */ -SEC("?raw_tp/sys_nanosleep") +SEC("?raw_tp/") int user_ringbuf_callback_null_context_read(void *ctx) { bpf_user_ringbuf_drain(&user_ringbuf, null_context_read, NULL, 0); @@ -132,7 +139,7 @@ try_discard_dynptr(struct bpf_dynptr *dynptr, void *context) /* A callback that accesses a dynptr in a bpf_user_ringbuf_drain callback should * not be able to read past the end of the pointer. */ -SEC("?raw_tp/sys_nanosleep") +SEC("?raw_tp/") int user_ringbuf_callback_discard_dynptr(void *ctx) { bpf_user_ringbuf_drain(&user_ringbuf, try_discard_dynptr, NULL, 0); @@ -151,7 +158,7 @@ try_submit_dynptr(struct bpf_dynptr *dynptr, void *context) /* A callback that accesses a dynptr in a bpf_user_ringbuf_drain callback should * not be able to read past the end of the pointer. */ -SEC("?raw_tp/sys_nanosleep") +SEC("?raw_tp/") int user_ringbuf_callback_submit_dynptr(void *ctx) { bpf_user_ringbuf_drain(&user_ringbuf, try_submit_dynptr, NULL, 0); @@ -168,10 +175,38 @@ invalid_drain_callback_return(struct bpf_dynptr *dynptr, void *context) /* A callback that accesses a dynptr in a bpf_user_ringbuf_drain callback should * not be able to write to that pointer. */ -SEC("?raw_tp/sys_nanosleep") +SEC("?raw_tp/") int user_ringbuf_callback_invalid_return(void *ctx) { bpf_user_ringbuf_drain(&user_ringbuf, invalid_drain_callback_return, NULL, 0); return 0; } + +static long +try_reinit_dynptr_mem(struct bpf_dynptr *dynptr, void *context) +{ + bpf_dynptr_from_mem(&map_value, 4, 0, dynptr); + return 0; +} + +static long +try_reinit_dynptr_ringbuf(struct bpf_dynptr *dynptr, void *context) +{ + bpf_ringbuf_reserve_dynptr(&ringbuf, 8, 0, dynptr); + return 0; +} + +SEC("?raw_tp/") +int user_ringbuf_callback_reinit_dynptr_mem(void *ctx) +{ + bpf_user_ringbuf_drain(&user_ringbuf, try_reinit_dynptr_mem, NULL, 0); + return 0; +} + +SEC("?raw_tp/") +int user_ringbuf_callback_reinit_dynptr_ringbuf(void *ctx) +{ + bpf_user_ringbuf_drain(&user_ringbuf, try_reinit_dynptr_ringbuf, NULL, 0); + return 0; +} From 40ef76de8a7fabb30dafebae4035d3e7eebb47a9 Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Wed, 7 Dec 2022 13:36:42 +0100 Subject: [PATCH 1792/2002] mlxsw: spectrum_router: Use gen_pool for RIF index allocation Currently, each router interface (RIF) consumes one entry in the RIFs table and there are no alignment constraints. This is going to change in subsequent patches where some RIFs will consume two table entries and their indexes will need to be aligned to the allocation size (even). Prepare for this change by converting the RIF index allocation to use gen_pool with the 'gen_pool_first_fit_order_align' algorithm. No Kconfig changes necessary as mlxsw already selects 'GENERIC_ALLOCATOR'. Signed-off-by: Ido Schimmel Reviewed-by: Amit Cohen Signed-off-by: Petr Machata Signed-off-by: Jakub Kicinski --- .../ethernet/mellanox/mlxsw/spectrum_router.c | 73 ++++++++++++++++--- .../ethernet/mellanox/mlxsw/spectrum_router.h | 4 + 2 files changed, 67 insertions(+), 10 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c index 48f1fa62a4fd..47d8c1635897 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -7828,16 +7829,18 @@ mlxsw_sp_dev_rif_type(const struct mlxsw_sp *mlxsw_sp, static int mlxsw_sp_rif_index_alloc(struct mlxsw_sp *mlxsw_sp, u16 *p_rif_index) { - int i; + *p_rif_index = gen_pool_alloc(mlxsw_sp->router->rifs_table, 1); + if (*p_rif_index == 0) + return -ENOBUFS; + *p_rif_index -= MLXSW_SP_ROUTER_GENALLOC_OFFSET; - for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS); i++) { - if (!mlxsw_sp->router->rifs[i]) { - *p_rif_index = i; - return 0; - } - } + return 0; +} - return -ENOBUFS; +static void mlxsw_sp_rif_index_free(struct mlxsw_sp *mlxsw_sp, u16 rif_index) +{ + gen_pool_free(mlxsw_sp->router->rifs_table, + MLXSW_SP_ROUTER_GENALLOC_OFFSET + rif_index, 1); } static struct mlxsw_sp_rif *mlxsw_sp_rif_alloc(size_t rif_size, u16 rif_index, @@ -8162,6 +8165,7 @@ err_fid_get: dev_put(rif->dev); kfree(rif); err_rif_alloc: + mlxsw_sp_rif_index_free(mlxsw_sp, rif_index); err_rif_index_alloc: vr->rif_count--; mlxsw_sp_vr_put(mlxsw_sp, vr); @@ -8173,6 +8177,7 @@ static void mlxsw_sp_rif_destroy(struct mlxsw_sp_rif *rif) const struct mlxsw_sp_rif_ops *ops = rif->ops; struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp; struct mlxsw_sp_fid *fid = rif->fid; + u16 rif_index = rif->rif_index; struct mlxsw_sp_vr *vr; int i; @@ -8198,6 +8203,7 @@ static void mlxsw_sp_rif_destroy(struct mlxsw_sp_rif *rif) mlxsw_sp->router->rifs[rif->rif_index] = NULL; dev_put(rif->dev); kfree(rif); + mlxsw_sp_rif_index_free(mlxsw_sp, rif_index); vr->rif_count--; mlxsw_sp_vr_put(mlxsw_sp, vr); } @@ -9781,8 +9787,10 @@ mlxsw_sp_ul_rif_create(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_vr *vr, } ul_rif = mlxsw_sp_rif_alloc(sizeof(*ul_rif), rif_index, vr->id, NULL); - if (!ul_rif) - return ERR_PTR(-ENOMEM); + if (!ul_rif) { + err = -ENOMEM; + goto err_rif_alloc; + } mlxsw_sp->router->rifs[rif_index] = ul_rif; ul_rif->mlxsw_sp = mlxsw_sp; @@ -9796,17 +9804,21 @@ mlxsw_sp_ul_rif_create(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_vr *vr, ul_rif_op_err: mlxsw_sp->router->rifs[rif_index] = NULL; kfree(ul_rif); +err_rif_alloc: + mlxsw_sp_rif_index_free(mlxsw_sp, rif_index); return ERR_PTR(err); } static void mlxsw_sp_ul_rif_destroy(struct mlxsw_sp_rif *ul_rif) { struct mlxsw_sp *mlxsw_sp = ul_rif->mlxsw_sp; + u16 rif_index = ul_rif->rif_index; atomic_dec(&mlxsw_sp->router->rifs_count); mlxsw_sp_rif_ipip_lb_ul_rif_op(ul_rif, false); mlxsw_sp->router->rifs[ul_rif->rif_index] = NULL; kfree(ul_rif); + mlxsw_sp_rif_index_free(mlxsw_sp, rif_index); } static struct mlxsw_sp_rif * @@ -9940,11 +9952,43 @@ static const struct mlxsw_sp_rif_ops *mlxsw_sp2_rif_ops_arr[] = { [MLXSW_SP_RIF_TYPE_IPIP_LB] = &mlxsw_sp2_rif_ipip_lb_ops, }; +static int mlxsw_sp_rifs_table_init(struct mlxsw_sp *mlxsw_sp) +{ + struct gen_pool *rifs_table; + int err; + + rifs_table = gen_pool_create(0, -1); + if (!rifs_table) + return -ENOMEM; + + gen_pool_set_algo(rifs_table, gen_pool_first_fit_order_align, + NULL); + + err = gen_pool_add(rifs_table, MLXSW_SP_ROUTER_GENALLOC_OFFSET, + MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS), -1); + if (err) + goto err_gen_pool_add; + + mlxsw_sp->router->rifs_table = rifs_table; + + return 0; + +err_gen_pool_add: + gen_pool_destroy(rifs_table); + return err; +} + +static void mlxsw_sp_rifs_table_fini(struct mlxsw_sp *mlxsw_sp) +{ + gen_pool_destroy(mlxsw_sp->router->rifs_table); +} + static int mlxsw_sp_rifs_init(struct mlxsw_sp *mlxsw_sp) { u64 max_rifs = MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS); struct devlink *devlink = priv_to_devlink(mlxsw_sp->core); struct mlxsw_core *core = mlxsw_sp->core; + int err; if (!MLXSW_CORE_RES_VALID(core, MAX_RIF_MAC_PROFILES)) return -EIO; @@ -9957,6 +10001,10 @@ static int mlxsw_sp_rifs_init(struct mlxsw_sp *mlxsw_sp) if (!mlxsw_sp->router->rifs) return -ENOMEM; + err = mlxsw_sp_rifs_table_init(mlxsw_sp); + if (err) + goto err_rifs_table_init; + idr_init(&mlxsw_sp->router->rif_mac_profiles_idr); atomic_set(&mlxsw_sp->router->rif_mac_profiles_count, 0); atomic_set(&mlxsw_sp->router->rifs_count, 0); @@ -9970,6 +10018,10 @@ static int mlxsw_sp_rifs_init(struct mlxsw_sp *mlxsw_sp) mlxsw_sp); return 0; + +err_rifs_table_init: + kfree(mlxsw_sp->router->rifs); + return err; } static void mlxsw_sp_rifs_fini(struct mlxsw_sp *mlxsw_sp) @@ -9986,6 +10038,7 @@ static void mlxsw_sp_rifs_fini(struct mlxsw_sp *mlxsw_sp) MLXSW_SP_RESOURCE_RIF_MAC_PROFILES); WARN_ON(!idr_is_empty(&mlxsw_sp->router->rif_mac_profiles_idr)); idr_destroy(&mlxsw_sp->router->rif_mac_profiles_idr); + mlxsw_sp_rifs_table_fini(mlxsw_sp); kfree(mlxsw_sp->router->rifs); } diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.h b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.h index c5dfb972b433..37d6e4c80e6a 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.h +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.h @@ -15,8 +15,12 @@ struct mlxsw_sp_router_nve_decap { u8 valid:1; }; +/* gen_pool_alloc() returns 0 when allocation fails, so use an offset */ +#define MLXSW_SP_ROUTER_GENALLOC_OFFSET 0x100 + struct mlxsw_sp_router { struct mlxsw_sp *mlxsw_sp; + struct gen_pool *rifs_table; struct mlxsw_sp_rif **rifs; struct idr rif_mac_profiles_idr; atomic_t rif_mac_profiles_count; From 1a2f65b4a277fca10bb24d5615e512971a87587a Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Wed, 7 Dec 2022 13:36:43 +0100 Subject: [PATCH 1793/2002] mlxsw: spectrum_router: Parametrize RIF allocation size Currently, each router interface (RIF) consumes one entry in the RIFs table. This is going to change in subsequent patches where some RIFs will consume two table entries. Prepare for this change by parametrizing the RIF allocation size. For now, always pass '1'. Signed-off-by: Ido Schimmel Reviewed-by: Amit Cohen Signed-off-by: Petr Machata Signed-off-by: Jakub Kicinski --- .../ethernet/mellanox/mlxsw/spectrum_router.c | 41 ++++++++++++------- 1 file changed, 27 insertions(+), 14 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c index 47d8c1635897..f3ace20dca8b 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c @@ -60,6 +60,7 @@ struct mlxsw_sp_rif { int mtu; u16 rif_index; u8 mac_profile_id; + u8 rif_entries; u16 vr_id; const struct mlxsw_sp_rif_ops *ops; struct mlxsw_sp *mlxsw_sp; @@ -7827,20 +7828,26 @@ mlxsw_sp_dev_rif_type(const struct mlxsw_sp *mlxsw_sp, return mlxsw_sp_fid_type_rif_type(mlxsw_sp, type); } -static int mlxsw_sp_rif_index_alloc(struct mlxsw_sp *mlxsw_sp, u16 *p_rif_index) +static int mlxsw_sp_rif_index_alloc(struct mlxsw_sp *mlxsw_sp, u16 *p_rif_index, + u8 rif_entries) { - *p_rif_index = gen_pool_alloc(mlxsw_sp->router->rifs_table, 1); + *p_rif_index = gen_pool_alloc(mlxsw_sp->router->rifs_table, + rif_entries); if (*p_rif_index == 0) return -ENOBUFS; *p_rif_index -= MLXSW_SP_ROUTER_GENALLOC_OFFSET; + /* RIF indexes must be aligned to the allocation size. */ + WARN_ON_ONCE(*p_rif_index % rif_entries); + return 0; } -static void mlxsw_sp_rif_index_free(struct mlxsw_sp *mlxsw_sp, u16 rif_index) +static void mlxsw_sp_rif_index_free(struct mlxsw_sp *mlxsw_sp, u16 rif_index, + u8 rif_entries) { gen_pool_free(mlxsw_sp->router->rifs_table, - MLXSW_SP_ROUTER_GENALLOC_OFFSET + rif_index, 1); + MLXSW_SP_ROUTER_GENALLOC_OFFSET + rif_index, rif_entries); } static struct mlxsw_sp_rif *mlxsw_sp_rif_alloc(size_t rif_size, u16 rif_index, @@ -8090,6 +8097,7 @@ mlxsw_sp_rif_create(struct mlxsw_sp *mlxsw_sp, enum mlxsw_sp_rif_type type; struct mlxsw_sp_rif *rif; struct mlxsw_sp_vr *vr; + u8 rif_entries = 1; u16 rif_index; int i, err; @@ -8101,7 +8109,7 @@ mlxsw_sp_rif_create(struct mlxsw_sp *mlxsw_sp, return ERR_CAST(vr); vr->rif_count++; - err = mlxsw_sp_rif_index_alloc(mlxsw_sp, &rif_index); + err = mlxsw_sp_rif_index_alloc(mlxsw_sp, &rif_index, rif_entries); if (err) { NL_SET_ERR_MSG_MOD(extack, "Exceeded number of supported router interfaces"); goto err_rif_index_alloc; @@ -8116,6 +8124,7 @@ mlxsw_sp_rif_create(struct mlxsw_sp *mlxsw_sp, mlxsw_sp->router->rifs[rif_index] = rif; rif->mlxsw_sp = mlxsw_sp; rif->ops = ops; + rif->rif_entries = rif_entries; if (ops->fid_get) { fid = ops->fid_get(rif, extack); @@ -8149,7 +8158,7 @@ mlxsw_sp_rif_create(struct mlxsw_sp *mlxsw_sp, mlxsw_sp_rif_counters_alloc(rif); } - atomic_inc(&mlxsw_sp->router->rifs_count); + atomic_add(rif_entries, &mlxsw_sp->router->rifs_count); return rif; err_stats_enable: @@ -8165,7 +8174,7 @@ err_fid_get: dev_put(rif->dev); kfree(rif); err_rif_alloc: - mlxsw_sp_rif_index_free(mlxsw_sp, rif_index); + mlxsw_sp_rif_index_free(mlxsw_sp, rif_index, rif_entries); err_rif_index_alloc: vr->rif_count--; mlxsw_sp_vr_put(mlxsw_sp, vr); @@ -8177,11 +8186,12 @@ static void mlxsw_sp_rif_destroy(struct mlxsw_sp_rif *rif) const struct mlxsw_sp_rif_ops *ops = rif->ops; struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp; struct mlxsw_sp_fid *fid = rif->fid; + u8 rif_entries = rif->rif_entries; u16 rif_index = rif->rif_index; struct mlxsw_sp_vr *vr; int i; - atomic_dec(&mlxsw_sp->router->rifs_count); + atomic_sub(rif_entries, &mlxsw_sp->router->rifs_count); mlxsw_sp_router_rif_gone_sync(mlxsw_sp, rif); vr = &mlxsw_sp->router->vrs[rif->vr_id]; @@ -8203,7 +8213,7 @@ static void mlxsw_sp_rif_destroy(struct mlxsw_sp_rif *rif) mlxsw_sp->router->rifs[rif->rif_index] = NULL; dev_put(rif->dev); kfree(rif); - mlxsw_sp_rif_index_free(mlxsw_sp, rif_index); + mlxsw_sp_rif_index_free(mlxsw_sp, rif_index, rif_entries); vr->rif_count--; mlxsw_sp_vr_put(mlxsw_sp, vr); } @@ -9777,10 +9787,11 @@ mlxsw_sp_ul_rif_create(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_vr *vr, struct netlink_ext_ack *extack) { struct mlxsw_sp_rif *ul_rif; + u8 rif_entries = 1; u16 rif_index; int err; - err = mlxsw_sp_rif_index_alloc(mlxsw_sp, &rif_index); + err = mlxsw_sp_rif_index_alloc(mlxsw_sp, &rif_index, rif_entries); if (err) { NL_SET_ERR_MSG_MOD(extack, "Exceeded number of supported router interfaces"); return ERR_PTR(err); @@ -9794,31 +9805,33 @@ mlxsw_sp_ul_rif_create(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_vr *vr, mlxsw_sp->router->rifs[rif_index] = ul_rif; ul_rif->mlxsw_sp = mlxsw_sp; + ul_rif->rif_entries = rif_entries; err = mlxsw_sp_rif_ipip_lb_ul_rif_op(ul_rif, true); if (err) goto ul_rif_op_err; - atomic_inc(&mlxsw_sp->router->rifs_count); + atomic_add(rif_entries, &mlxsw_sp->router->rifs_count); return ul_rif; ul_rif_op_err: mlxsw_sp->router->rifs[rif_index] = NULL; kfree(ul_rif); err_rif_alloc: - mlxsw_sp_rif_index_free(mlxsw_sp, rif_index); + mlxsw_sp_rif_index_free(mlxsw_sp, rif_index, rif_entries); return ERR_PTR(err); } static void mlxsw_sp_ul_rif_destroy(struct mlxsw_sp_rif *ul_rif) { struct mlxsw_sp *mlxsw_sp = ul_rif->mlxsw_sp; + u8 rif_entries = ul_rif->rif_entries; u16 rif_index = ul_rif->rif_index; - atomic_dec(&mlxsw_sp->router->rifs_count); + atomic_sub(rif_entries, &mlxsw_sp->router->rifs_count); mlxsw_sp_rif_ipip_lb_ul_rif_op(ul_rif, false); mlxsw_sp->router->rifs[ul_rif->rif_index] = NULL; kfree(ul_rif); - mlxsw_sp_rif_index_free(mlxsw_sp, rif_index); + mlxsw_sp_rif_index_free(mlxsw_sp, rif_index, rif_entries); } static struct mlxsw_sp_rif * From 5ca1b208c5d107fd4b9e7801200dea18ab1af8e7 Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Wed, 7 Dec 2022 13:36:44 +0100 Subject: [PATCH 1794/2002] mlxsw: spectrum_router: Add support for double entry RIFs In Spectrum-1, loopback router interfaces (RIFs) used for IP-in-IP encapsulation with an IPv6 underlay require two RIF entries and the RIF index must be even. Prepare for this change by extending the RIF parameters structure with a 'double_entry' field that indicates if the RIF being created requires two RIF entries or not. Only set it for RIFs representing ip6gre tunnels in Spectrum-1. Signed-off-by: Ido Schimmel Reviewed-by: Amit Cohen Signed-off-by: Petr Machata Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/mellanox/mlxsw/spectrum_ipip.c | 1 + drivers/net/ethernet/mellanox/mlxsw/spectrum_ipip.h | 1 + drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c | 4 +++- 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_ipip.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_ipip.c index a2ee695a3f17..7ed4b64fecc7 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_ipip.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_ipip.c @@ -433,6 +433,7 @@ static const struct mlxsw_sp_ipip_ops mlxsw_sp1_ipip_gre6_ops = { .dev_type = ARPHRD_IP6GRE, .ul_proto = MLXSW_SP_L3_PROTO_IPV6, .inc_parsing_depth = true, + .double_rif_entry = true, .parms_init = mlxsw_sp1_ipip_netdev_parms_init_gre6, .nexthop_update = mlxsw_sp1_ipip_nexthop_update_gre6, .decap_config = mlxsw_sp1_ipip_decap_config_gre6, diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_ipip.h b/drivers/net/ethernet/mellanox/mlxsw/spectrum_ipip.h index 8cc259dcc8d0..a35f009da561 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_ipip.h +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_ipip.h @@ -49,6 +49,7 @@ struct mlxsw_sp_ipip_ops { int dev_type; enum mlxsw_sp_l3proto ul_proto; /* Underlay. */ bool inc_parsing_depth; + bool double_rif_entry; struct mlxsw_sp_ipip_parms (*parms_init)(const struct net_device *ol_dev); diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c index f3ace20dca8b..c22c3ac4e2a1 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c @@ -79,6 +79,7 @@ struct mlxsw_sp_rif_params { }; u16 vid; bool lag; + bool double_entry; }; struct mlxsw_sp_rif_subport { @@ -1070,6 +1071,7 @@ mlxsw_sp_ipip_ol_ipip_lb_create(struct mlxsw_sp *mlxsw_sp, lb_params = (struct mlxsw_sp_rif_params_ipip_lb) { .common.dev = ol_dev, .common.lag = false, + .common.double_entry = ipip_ops->double_rif_entry, .lb_config = ipip_ops->ol_loopback_config(mlxsw_sp, ol_dev), }; @@ -8091,13 +8093,13 @@ mlxsw_sp_rif_create(struct mlxsw_sp *mlxsw_sp, const struct mlxsw_sp_rif_params *params, struct netlink_ext_ack *extack) { + u8 rif_entries = params->double_entry ? 2 : 1; u32 tb_id = l3mdev_fib_table(params->dev); const struct mlxsw_sp_rif_ops *ops; struct mlxsw_sp_fid *fid = NULL; enum mlxsw_sp_rif_type type; struct mlxsw_sp_rif *rif; struct mlxsw_sp_vr *vr; - u8 rif_entries = 1; u16 rif_index; int i, err; From ab30e4d4b29ba530c65406e8a146630d0663c570 Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Wed, 7 Dec 2022 13:36:45 +0100 Subject: [PATCH 1795/2002] mlxsw: spectrum_ipip: Rename Spectrum-2 ip6gre operations There are two main differences between Spectrum-1 and newer ASICs in terms of IP-in-IP support: 1. In Spectrum-1, RIFs representing ip6gre tunnels require two entries in the RIF table. 2. In Spectrum-2 and newer ASICs, packets ingress the underlay (during encapsulation) and egress the underlay (during decapsulation) via a special generic loopback RIF. The first difference was handled in previous patches by adding the 'double_rif_entry' field to the Spectrum-1 operations structure of ip6gre RIFs. The second difference is handled during RIF creation, by only creating a generic loopback RIF in Spectrum-2 and newer ASICs. Therefore, the ip6gre operations can be shared between Spectrum-1 and newer ASIC in a similar fashion to how the ipgre operations are shared. Rename the operations to not be Spectrum-2 specific and move them earlier in the file so that they could later be used for Spectrum-1. Signed-off-by: Ido Schimmel Reviewed-by: Amit Cohen Signed-off-by: Petr Machata Signed-off-by: Jakub Kicinski --- .../ethernet/mellanox/mlxsw/spectrum_ipip.c | 94 +++++++++---------- 1 file changed, 47 insertions(+), 47 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_ipip.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_ipip.c index 7ed4b64fecc7..fd421fbfc71b 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_ipip.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_ipip.c @@ -429,28 +429,8 @@ mlxsw_sp1_ipip_rem_addr_unset_gre6(struct mlxsw_sp *mlxsw_sp, WARN_ON_ONCE(1); } -static const struct mlxsw_sp_ipip_ops mlxsw_sp1_ipip_gre6_ops = { - .dev_type = ARPHRD_IP6GRE, - .ul_proto = MLXSW_SP_L3_PROTO_IPV6, - .inc_parsing_depth = true, - .double_rif_entry = true, - .parms_init = mlxsw_sp1_ipip_netdev_parms_init_gre6, - .nexthop_update = mlxsw_sp1_ipip_nexthop_update_gre6, - .decap_config = mlxsw_sp1_ipip_decap_config_gre6, - .can_offload = mlxsw_sp1_ipip_can_offload_gre6, - .ol_loopback_config = mlxsw_sp1_ipip_ol_loopback_config_gre6, - .ol_netdev_change = mlxsw_sp1_ipip_ol_netdev_change_gre6, - .rem_ip_addr_set = mlxsw_sp1_ipip_rem_addr_set_gre6, - .rem_ip_addr_unset = mlxsw_sp1_ipip_rem_addr_unset_gre6, -}; - -const struct mlxsw_sp_ipip_ops *mlxsw_sp1_ipip_ops_arr[] = { - [MLXSW_SP_IPIP_TYPE_GRE4] = &mlxsw_sp_ipip_gre4_ops, - [MLXSW_SP_IPIP_TYPE_GRE6] = &mlxsw_sp1_ipip_gre6_ops, -}; - static struct mlxsw_sp_ipip_parms -mlxsw_sp2_ipip_netdev_parms_init_gre6(const struct net_device *ol_dev) +mlxsw_sp_ipip_netdev_parms_init_gre6(const struct net_device *ol_dev) { struct __ip6_tnl_parm parms = mlxsw_sp_ipip_netdev_parms6(ol_dev); @@ -465,9 +445,9 @@ mlxsw_sp2_ipip_netdev_parms_init_gre6(const struct net_device *ol_dev) } static int -mlxsw_sp2_ipip_nexthop_update_gre6(struct mlxsw_sp *mlxsw_sp, u32 adj_index, - struct mlxsw_sp_ipip_entry *ipip_entry, - bool force, char *ratr_pl) +mlxsw_sp_ipip_nexthop_update_gre6(struct mlxsw_sp *mlxsw_sp, u32 adj_index, + struct mlxsw_sp_ipip_entry *ipip_entry, + bool force, char *ratr_pl) { u16 rif_index = mlxsw_sp_ipip_lb_rif_index(ipip_entry->ol_lb); enum mlxsw_reg_ratr_op op; @@ -483,9 +463,9 @@ mlxsw_sp2_ipip_nexthop_update_gre6(struct mlxsw_sp *mlxsw_sp, u32 adj_index, } static int -mlxsw_sp2_ipip_decap_config_gre6(struct mlxsw_sp *mlxsw_sp, - struct mlxsw_sp_ipip_entry *ipip_entry, - u32 tunnel_index) +mlxsw_sp_ipip_decap_config_gre6(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_ipip_entry *ipip_entry, + u32 tunnel_index) { u16 rif_index = mlxsw_sp_ipip_lb_rif_index(ipip_entry->ol_lb); u16 ul_rif_id = mlxsw_sp_ipip_lb_ul_rif_id(ipip_entry->ol_lb); @@ -520,8 +500,8 @@ mlxsw_sp2_ipip_decap_config_gre6(struct mlxsw_sp *mlxsw_sp, return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rtdp), rtdp_pl); } -static bool mlxsw_sp2_ipip_can_offload_gre6(const struct mlxsw_sp *mlxsw_sp, - const struct net_device *ol_dev) +static bool mlxsw_sp_ipip_can_offload_gre6(const struct mlxsw_sp *mlxsw_sp, + const struct net_device *ol_dev) { struct __ip6_tnl_parm tparm = mlxsw_sp_ipip_netdev_parms6(ol_dev); bool inherit_tos = tparm.flags & IP6_TNL_F_USE_ORIG_TCLASS; @@ -535,8 +515,8 @@ static bool mlxsw_sp2_ipip_can_offload_gre6(const struct mlxsw_sp *mlxsw_sp, } static struct mlxsw_sp_rif_ipip_lb_config -mlxsw_sp2_ipip_ol_loopback_config_gre6(struct mlxsw_sp *mlxsw_sp, - const struct net_device *ol_dev) +mlxsw_sp_ipip_ol_loopback_config_gre6(struct mlxsw_sp *mlxsw_sp, + const struct net_device *ol_dev) { struct __ip6_tnl_parm parms = mlxsw_sp_ipip_netdev_parms6(ol_dev); enum mlxsw_reg_ritr_loopback_ipip_type lb_ipipt; @@ -554,20 +534,20 @@ mlxsw_sp2_ipip_ol_loopback_config_gre6(struct mlxsw_sp *mlxsw_sp, } static int -mlxsw_sp2_ipip_ol_netdev_change_gre6(struct mlxsw_sp *mlxsw_sp, - struct mlxsw_sp_ipip_entry *ipip_entry, - struct netlink_ext_ack *extack) +mlxsw_sp_ipip_ol_netdev_change_gre6(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_ipip_entry *ipip_entry, + struct netlink_ext_ack *extack) { struct mlxsw_sp_ipip_parms new_parms; - new_parms = mlxsw_sp2_ipip_netdev_parms_init_gre6(ipip_entry->ol_dev); + new_parms = mlxsw_sp_ipip_netdev_parms_init_gre6(ipip_entry->ol_dev); return mlxsw_sp_ipip_ol_netdev_change_gre(mlxsw_sp, ipip_entry, &new_parms, extack); } static int -mlxsw_sp2_ipip_rem_addr_set_gre6(struct mlxsw_sp *mlxsw_sp, - struct mlxsw_sp_ipip_entry *ipip_entry) +mlxsw_sp_ipip_rem_addr_set_gre6(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_ipip_entry *ipip_entry) { return mlxsw_sp_ipv6_addr_kvdl_index_get(mlxsw_sp, &ipip_entry->parms.daddr.addr6, @@ -575,24 +555,44 @@ mlxsw_sp2_ipip_rem_addr_set_gre6(struct mlxsw_sp *mlxsw_sp, } static void -mlxsw_sp2_ipip_rem_addr_unset_gre6(struct mlxsw_sp *mlxsw_sp, - const struct mlxsw_sp_ipip_entry *ipip_entry) +mlxsw_sp_ipip_rem_addr_unset_gre6(struct mlxsw_sp *mlxsw_sp, + const struct mlxsw_sp_ipip_entry *ipip_entry) { mlxsw_sp_ipv6_addr_put(mlxsw_sp, &ipip_entry->parms.daddr.addr6); } +static const struct mlxsw_sp_ipip_ops mlxsw_sp1_ipip_gre6_ops = { + .dev_type = ARPHRD_IP6GRE, + .ul_proto = MLXSW_SP_L3_PROTO_IPV6, + .inc_parsing_depth = true, + .double_rif_entry = true, + .parms_init = mlxsw_sp1_ipip_netdev_parms_init_gre6, + .nexthop_update = mlxsw_sp1_ipip_nexthop_update_gre6, + .decap_config = mlxsw_sp1_ipip_decap_config_gre6, + .can_offload = mlxsw_sp1_ipip_can_offload_gre6, + .ol_loopback_config = mlxsw_sp1_ipip_ol_loopback_config_gre6, + .ol_netdev_change = mlxsw_sp1_ipip_ol_netdev_change_gre6, + .rem_ip_addr_set = mlxsw_sp1_ipip_rem_addr_set_gre6, + .rem_ip_addr_unset = mlxsw_sp1_ipip_rem_addr_unset_gre6, +}; + +const struct mlxsw_sp_ipip_ops *mlxsw_sp1_ipip_ops_arr[] = { + [MLXSW_SP_IPIP_TYPE_GRE4] = &mlxsw_sp_ipip_gre4_ops, + [MLXSW_SP_IPIP_TYPE_GRE6] = &mlxsw_sp1_ipip_gre6_ops, +}; + static const struct mlxsw_sp_ipip_ops mlxsw_sp2_ipip_gre6_ops = { .dev_type = ARPHRD_IP6GRE, .ul_proto = MLXSW_SP_L3_PROTO_IPV6, .inc_parsing_depth = true, - .parms_init = mlxsw_sp2_ipip_netdev_parms_init_gre6, - .nexthop_update = mlxsw_sp2_ipip_nexthop_update_gre6, - .decap_config = mlxsw_sp2_ipip_decap_config_gre6, - .can_offload = mlxsw_sp2_ipip_can_offload_gre6, - .ol_loopback_config = mlxsw_sp2_ipip_ol_loopback_config_gre6, - .ol_netdev_change = mlxsw_sp2_ipip_ol_netdev_change_gre6, - .rem_ip_addr_set = mlxsw_sp2_ipip_rem_addr_set_gre6, - .rem_ip_addr_unset = mlxsw_sp2_ipip_rem_addr_unset_gre6, + .parms_init = mlxsw_sp_ipip_netdev_parms_init_gre6, + .nexthop_update = mlxsw_sp_ipip_nexthop_update_gre6, + .decap_config = mlxsw_sp_ipip_decap_config_gre6, + .can_offload = mlxsw_sp_ipip_can_offload_gre6, + .ol_loopback_config = mlxsw_sp_ipip_ol_loopback_config_gre6, + .ol_netdev_change = mlxsw_sp_ipip_ol_netdev_change_gre6, + .rem_ip_addr_set = mlxsw_sp_ipip_rem_addr_set_gre6, + .rem_ip_addr_unset = mlxsw_sp_ipip_rem_addr_unset_gre6, }; const struct mlxsw_sp_ipip_ops *mlxsw_sp2_ipip_ops_arr[] = { From 7ec5364351ed841cde48ef1e68259b8738d858aa Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Wed, 7 Dec 2022 13:36:46 +0100 Subject: [PATCH 1796/2002] mlxsw: spectrum_ipip: Add Spectrum-1 ip6gre support As explained in the previous patch, the existing Spectrum-2 ip6gre implementation can be reused for Spectrum-1. Change the Spectrum-1 ip6gre operations structure to use the common operations. Signed-off-by: Ido Schimmel Reviewed-by: Amit Cohen Signed-off-by: Petr Machata Signed-off-by: Jakub Kicinski --- .../ethernet/mellanox/mlxsw/spectrum_ipip.c | 83 ++----------------- 1 file changed, 8 insertions(+), 75 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_ipip.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_ipip.c index fd421fbfc71b..3340b4a694c3 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_ipip.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_ipip.c @@ -362,73 +362,6 @@ static const struct mlxsw_sp_ipip_ops mlxsw_sp_ipip_gre4_ops = { .rem_ip_addr_unset = mlxsw_sp_ipip_rem_addr_unset_gre4, }; -static struct mlxsw_sp_ipip_parms -mlxsw_sp1_ipip_netdev_parms_init_gre6(const struct net_device *ol_dev) -{ - struct mlxsw_sp_ipip_parms parms = {0}; - - WARN_ON_ONCE(1); - return parms; -} - -static int -mlxsw_sp1_ipip_nexthop_update_gre6(struct mlxsw_sp *mlxsw_sp, u32 adj_index, - struct mlxsw_sp_ipip_entry *ipip_entry, - bool force, char *ratr_pl) -{ - WARN_ON_ONCE(1); - return -EINVAL; -} - -static int -mlxsw_sp1_ipip_decap_config_gre6(struct mlxsw_sp *mlxsw_sp, - struct mlxsw_sp_ipip_entry *ipip_entry, - u32 tunnel_index) -{ - WARN_ON_ONCE(1); - return -EINVAL; -} - -static bool mlxsw_sp1_ipip_can_offload_gre6(const struct mlxsw_sp *mlxsw_sp, - const struct net_device *ol_dev) -{ - return false; -} - -static struct mlxsw_sp_rif_ipip_lb_config -mlxsw_sp1_ipip_ol_loopback_config_gre6(struct mlxsw_sp *mlxsw_sp, - const struct net_device *ol_dev) -{ - struct mlxsw_sp_rif_ipip_lb_config config = {0}; - - WARN_ON_ONCE(1); - return config; -} - -static int -mlxsw_sp1_ipip_ol_netdev_change_gre6(struct mlxsw_sp *mlxsw_sp, - struct mlxsw_sp_ipip_entry *ipip_entry, - struct netlink_ext_ack *extack) -{ - WARN_ON_ONCE(1); - return -EINVAL; -} - -static int -mlxsw_sp1_ipip_rem_addr_set_gre6(struct mlxsw_sp *mlxsw_sp, - struct mlxsw_sp_ipip_entry *ipip_entry) -{ - WARN_ON_ONCE(1); - return -EINVAL; -} - -static void -mlxsw_sp1_ipip_rem_addr_unset_gre6(struct mlxsw_sp *mlxsw_sp, - const struct mlxsw_sp_ipip_entry *ipip_entry) -{ - WARN_ON_ONCE(1); -} - static struct mlxsw_sp_ipip_parms mlxsw_sp_ipip_netdev_parms_init_gre6(const struct net_device *ol_dev) { @@ -566,14 +499,14 @@ static const struct mlxsw_sp_ipip_ops mlxsw_sp1_ipip_gre6_ops = { .ul_proto = MLXSW_SP_L3_PROTO_IPV6, .inc_parsing_depth = true, .double_rif_entry = true, - .parms_init = mlxsw_sp1_ipip_netdev_parms_init_gre6, - .nexthop_update = mlxsw_sp1_ipip_nexthop_update_gre6, - .decap_config = mlxsw_sp1_ipip_decap_config_gre6, - .can_offload = mlxsw_sp1_ipip_can_offload_gre6, - .ol_loopback_config = mlxsw_sp1_ipip_ol_loopback_config_gre6, - .ol_netdev_change = mlxsw_sp1_ipip_ol_netdev_change_gre6, - .rem_ip_addr_set = mlxsw_sp1_ipip_rem_addr_set_gre6, - .rem_ip_addr_unset = mlxsw_sp1_ipip_rem_addr_unset_gre6, + .parms_init = mlxsw_sp_ipip_netdev_parms_init_gre6, + .nexthop_update = mlxsw_sp_ipip_nexthop_update_gre6, + .decap_config = mlxsw_sp_ipip_decap_config_gre6, + .can_offload = mlxsw_sp_ipip_can_offload_gre6, + .ol_loopback_config = mlxsw_sp_ipip_ol_loopback_config_gre6, + .ol_netdev_change = mlxsw_sp_ipip_ol_netdev_change_gre6, + .rem_ip_addr_set = mlxsw_sp_ipip_rem_addr_set_gre6, + .rem_ip_addr_unset = mlxsw_sp_ipip_rem_addr_unset_gre6, }; const struct mlxsw_sp_ipip_ops *mlxsw_sp1_ipip_ops_arr[] = { From db401875f438168c5804b295b93a28c7730bb57a Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Wed, 7 Dec 2022 13:36:47 +0100 Subject: [PATCH 1797/2002] selftests: mlxsw: Move IPv6 decap_error test to shared directory Now that Spectrum-1 gained ip6gre support we can move the test out of the Spectrum-2 directory. Signed-off-by: Ido Schimmel Reviewed-by: Amit Cohen Signed-off-by: Petr Machata Signed-off-by: Jakub Kicinski --- .../net/mlxsw/{spectrum-2 => }/devlink_trap_tunnel_ipip6.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename tools/testing/selftests/drivers/net/mlxsw/{spectrum-2 => }/devlink_trap_tunnel_ipip6.sh (99%) diff --git a/tools/testing/selftests/drivers/net/mlxsw/spectrum-2/devlink_trap_tunnel_ipip6.sh b/tools/testing/selftests/drivers/net/mlxsw/devlink_trap_tunnel_ipip6.sh similarity index 99% rename from tools/testing/selftests/drivers/net/mlxsw/spectrum-2/devlink_trap_tunnel_ipip6.sh rename to tools/testing/selftests/drivers/net/mlxsw/devlink_trap_tunnel_ipip6.sh index f62ce479c266..878125041fc3 100755 --- a/tools/testing/selftests/drivers/net/mlxsw/spectrum-2/devlink_trap_tunnel_ipip6.sh +++ b/tools/testing/selftests/drivers/net/mlxsw/devlink_trap_tunnel_ipip6.sh @@ -31,7 +31,7 @@ # | 2001:db8:10::2/64 | # +-------------------------+ -lib_dir=$(dirname $0)/../../../../net/forwarding +lib_dir=$(dirname $0)/../../../net/forwarding ALL_TESTS=" decap_error_test From 17961a37ce4077ae43128ba5ce0b1dfd8f02fdb9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20T=C3=B6pel?= Date: Tue, 6 Dec 2022 11:28:38 +0100 Subject: [PATCH 1798/2002] selftests: net: Fix O=dir builds MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The BPF Makefile in net/bpf did incorrect path substitution for O=dir builds, e.g. make O=/tmp/kselftest headers make O=/tmp/kselftest -C tools/testing/selftests would fail in selftest builds [1] net/ with clang-16: error: no such file or directory: 'kselftest/net/bpf/nat6to4.c' clang-16: error: no input files Add a pattern prerequisite and an order-only-prerequisite (for creating the directory), to resolve the issue. [1] https://lore.kernel.org/all/202212060009.34CkQmCN-lkp@intel.com/ Reported-by: kernel test robot Fixes: 837a3d66d698 ("selftests: net: Add cross-compilation support for BPF programs") Signed-off-by: Björn Töpel Link: https://lore.kernel.org/r/20221206102838.272584-1-bjorn@kernel.org Signed-off-by: Jakub Kicinski --- tools/testing/selftests/net/bpf/Makefile | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/testing/selftests/net/bpf/Makefile b/tools/testing/selftests/net/bpf/Makefile index a26cb94354f6..4abaf16d2077 100644 --- a/tools/testing/selftests/net/bpf/Makefile +++ b/tools/testing/selftests/net/bpf/Makefile @@ -12,7 +12,7 @@ CCINCLUDE += -I$(SCRATCH_DIR)/include BPFOBJ := $(BUILD_DIR)/libbpf/libbpf.a -MAKE_DIRS := $(BUILD_DIR)/libbpf +MAKE_DIRS := $(BUILD_DIR)/libbpf $(OUTPUT)/bpf $(MAKE_DIRS): mkdir -p $@ @@ -37,8 +37,8 @@ endif CLANG_SYS_INCLUDES = $(call get_sys_includes,$(CLANG),$(CLANG_TARGET_ARCH)) -$(TEST_CUSTOM_PROGS): $(BPFOBJ) - $(CLANG) -O2 -target bpf -c $(@:.o=.c) $(CCINCLUDE) $(CLANG_SYS_INCLUDES) -o $@ +$(TEST_CUSTOM_PROGS): $(OUTPUT)/%.o: %.c $(BPFOBJ) | $(MAKE_DIRS) + $(CLANG) -O2 -target bpf -c $< $(CCINCLUDE) $(CLANG_SYS_INCLUDES) -o $@ $(BPFOBJ): $(wildcard $(BPFDIR)/*.[ch] $(BPFDIR)/Makefile) \ $(APIDIR)/linux/bpf.h \ From 3df96774a4224fd96a54eff7446425f1fbd76006 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Wed, 7 Dec 2022 09:43:12 +0000 Subject: [PATCH 1799/2002] nfp: Fix spelling mistake "tha" -> "the" There is a spelling mistake in a nn_dp_warn message. Fix it. Signed-off-by: Colin Ian King Reviewed-by: Simon Horman Link: https://lore.kernel.org/r/20221207094312.2281493-1-colin.i.king@gmail.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/netronome/nfp/ccm_mbox.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/netronome/nfp/ccm_mbox.c b/drivers/net/ethernet/netronome/nfp/ccm_mbox.c index 4247bca09807..aa8aba4ff7aa 100644 --- a/drivers/net/ethernet/netronome/nfp/ccm_mbox.c +++ b/drivers/net/ethernet/netronome/nfp/ccm_mbox.c @@ -503,7 +503,7 @@ nfp_ccm_mbox_msg_prepare(struct nfp_net *nn, struct sk_buff *skb, max_len = max(max_reply_size, round_up(skb->len, 4)); if (max_len > mbox_max) { nn_dp_warn(&nn->dp, - "message too big for tha mailbox: %u/%u vs %u\n", + "message too big for the mailbox: %u/%u vs %u\n", skb->len, max_reply_size, mbox_max); return -EMSGSIZE; } From c79e0af5ae5e4de98595983fc7e883e2ca54db11 Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Wed, 7 Dec 2022 15:04:54 +0100 Subject: [PATCH 1800/2002] net: ethernet: mtk_wed: fix some possible NULL pointer dereferences Fix possible NULL pointer dereference in mtk_wed_detach routine checking wo pointer is properly allocated before running mtk_wed_wo_reset() and mtk_wed_wo_deinit(). Even if it is just a theoretical issue at the moment check wo pointer is not NULL in mtk_wed_mcu_msg_update. Moreover, honor mtk_wed_mcu_send_msg return value in mtk_wed_wo_reset() Fixes: 799684448e3e ("net: ethernet: mtk_wed: introduce wed wo support") Fixes: 4c5de09eb0d0 ("net: ethernet: mtk_wed: add configure wed wo support") Signed-off-by: Lorenzo Bianconi Reviewed-by: Leon Romanovsky Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/mediatek/mtk_wed.c | 13 ++++++++----- drivers/net/ethernet/mediatek/mtk_wed_mcu.c | 3 +++ 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/drivers/net/ethernet/mediatek/mtk_wed.c b/drivers/net/ethernet/mediatek/mtk_wed.c index 06b6cc53fa02..4ef23eadd69e 100644 --- a/drivers/net/ethernet/mediatek/mtk_wed.c +++ b/drivers/net/ethernet/mediatek/mtk_wed.c @@ -174,9 +174,10 @@ mtk_wed_wo_reset(struct mtk_wed_device *dev) mtk_wdma_tx_reset(dev); mtk_wed_reset(dev, MTK_WED_RESET_WED); - mtk_wed_mcu_send_msg(wo, MTK_WED_MODULE_ID_WO, - MTK_WED_WO_CMD_CHANGE_STATE, &state, - sizeof(state), false); + if (mtk_wed_mcu_send_msg(wo, MTK_WED_MODULE_ID_WO, + MTK_WED_WO_CMD_CHANGE_STATE, &state, + sizeof(state), false)) + return; if (readx_poll_timeout(mtk_wed_wo_read_status, dev, val, val == MTK_WED_WOIF_DISABLE_DONE, @@ -590,9 +591,11 @@ mtk_wed_detach(struct mtk_wed_device *dev) mtk_wed_free_tx_rings(dev); if (mtk_wed_get_rx_capa(dev)) { - mtk_wed_wo_reset(dev); + if (hw->wed_wo) + mtk_wed_wo_reset(dev); mtk_wed_free_rx_rings(dev); - mtk_wed_wo_deinit(hw); + if (hw->wed_wo) + mtk_wed_wo_deinit(hw); } if (dev->wlan.bus_type == MTK_WED_BUS_PCIE) { diff --git a/drivers/net/ethernet/mediatek/mtk_wed_mcu.c b/drivers/net/ethernet/mediatek/mtk_wed_mcu.c index f9539e6233c9..6bad0d262f28 100644 --- a/drivers/net/ethernet/mediatek/mtk_wed_mcu.c +++ b/drivers/net/ethernet/mediatek/mtk_wed_mcu.c @@ -207,6 +207,9 @@ int mtk_wed_mcu_msg_update(struct mtk_wed_device *dev, int id, void *data, if (dev->hw->version == 1) return 0; + if (WARN_ON(!wo)) + return -ENODEV; + return mtk_wed_mcu_send_msg(wo, MTK_WED_MODULE_ID_WO, id, data, len, true); } From 587585e1bbebc250d46ebb0eeaf986d9c3c01c69 Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Wed, 7 Dec 2022 15:04:55 +0100 Subject: [PATCH 1801/2002] net: ethernet: mtk_wed: fix possible deadlock if mtk_wed_wo_init fails Introduce __mtk_wed_detach() in order to avoid a deadlock in mtk_wed_attach routine if mtk_wed_wo_init fails since both mtk_wed_attach and mtk_wed_detach run holding hw_lock mutex. Fixes: 4c5de09eb0d0 ("net: ethernet: mtk_wed: add configure wed wo support") Signed-off-by: Lorenzo Bianconi Reviewed-by: Leon Romanovsky Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/mediatek/mtk_wed.c | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/drivers/net/ethernet/mediatek/mtk_wed.c b/drivers/net/ethernet/mediatek/mtk_wed.c index 4ef23eadd69e..a6271449617f 100644 --- a/drivers/net/ethernet/mediatek/mtk_wed.c +++ b/drivers/net/ethernet/mediatek/mtk_wed.c @@ -577,12 +577,10 @@ mtk_wed_deinit(struct mtk_wed_device *dev) } static void -mtk_wed_detach(struct mtk_wed_device *dev) +__mtk_wed_detach(struct mtk_wed_device *dev) { struct mtk_wed_hw *hw = dev->hw; - mutex_lock(&hw_lock); - mtk_wed_deinit(dev); mtk_wdma_rx_reset(dev); @@ -615,6 +613,13 @@ mtk_wed_detach(struct mtk_wed_device *dev) module_put(THIS_MODULE); hw->wed_dev = NULL; +} + +static void +mtk_wed_detach(struct mtk_wed_device *dev) +{ + mutex_lock(&hw_lock); + __mtk_wed_detach(dev); mutex_unlock(&hw_lock); } @@ -1497,8 +1502,10 @@ mtk_wed_attach(struct mtk_wed_device *dev) ret = mtk_wed_wo_init(hw); } out: - if (ret) - mtk_wed_detach(dev); + if (ret) { + dev_err(dev->hw->dev, "failed to attach wed device\n"); + __mtk_wed_detach(dev); + } unlock: mutex_unlock(&hw_lock); From b534dc46c8ae0165b1b2509be24dbea4fa9c4011 Mon Sep 17 00:00:00 2001 From: Willem de Bruijn Date: Wed, 7 Dec 2022 09:37:01 -0500 Subject: [PATCH 1802/2002] net_tstamp: add SOF_TIMESTAMPING_OPT_ID_TCP Add an option to initialize SOF_TIMESTAMPING_OPT_ID for TCP from write_seq sockets instead of snd_una. This should have been the behavior from the start. Because processes may now exist that rely on the established behavior, do not change behavior of the existing option, but add the right behavior with a new flag. It is encouraged to always set SOF_TIMESTAMPING_OPT_ID_TCP on stream sockets along with the existing SOF_TIMESTAMPING_OPT_ID. Intuitively the contract is that the counter is zero after the setsockopt, so that the next write N results in a notification for the last byte N - 1. On idle sockets snd_una == write_seq and this holds for both. But on sockets with data in transmission, snd_una records the unacked offset in the stream. This depends on the ACK response from the peer. A process cannot learn this in a race free manner (ioctl SIOCOUTQ is one racy approach). write_seq records the offset at the last byte written by the process. This is a better starting point. It matches the intuitive contract in all circumstances, unaffected by external behavior. The new timestamp flag necessitates increasing sk_tsflags to 32 bits. Move the field in struct sock to avoid growing the socket (for some common CONFIG variants). The UAPI interface so_timestamping.flags is already int, so 32 bits wide. Reported-by: Sotirios Delimanolis Signed-off-by: Willem de Bruijn Link: https://lore.kernel.org/r/20221207143701.29861-1-willemdebruijn.kernel@gmail.com Signed-off-by: Jakub Kicinski --- Documentation/networking/timestamping.rst | 32 ++++++++++++++++++++++- include/net/sock.h | 6 ++--- include/uapi/linux/net_tstamp.h | 3 ++- net/core/sock.c | 9 ++++++- net/ethtool/common.c | 1 + 5 files changed, 45 insertions(+), 6 deletions(-) diff --git a/Documentation/networking/timestamping.rst b/Documentation/networking/timestamping.rst index be4eb1242057..f17c01834a12 100644 --- a/Documentation/networking/timestamping.rst +++ b/Documentation/networking/timestamping.rst @@ -179,7 +179,8 @@ SOF_TIMESTAMPING_OPT_ID: identifier and returns that along with the timestamp. The identifier is derived from a per-socket u32 counter (that wraps). For datagram sockets, the counter increments with each sent packet. For stream - sockets, it increments with every byte. + sockets, it increments with every byte. For stream sockets, also set + SOF_TIMESTAMPING_OPT_ID_TCP, see the section below. The counter starts at zero. It is initialized the first time that the socket option is enabled. It is reset each time the option is @@ -192,6 +193,35 @@ SOF_TIMESTAMPING_OPT_ID: among all possibly concurrently outstanding timestamp requests for that socket. +SOF_TIMESTAMPING_OPT_ID_TCP: + Pass this modifier along with SOF_TIMESTAMPING_OPT_ID for new TCP + timestamping applications. SOF_TIMESTAMPING_OPT_ID defines how the + counter increments for stream sockets, but its starting point is + not entirely trivial. This option fixes that. + + For stream sockets, if SOF_TIMESTAMPING_OPT_ID is set, this should + always be set too. On datagram sockets the option has no effect. + + A reasonable expectation is that the counter is reset to zero with + the system call, so that a subsequent write() of N bytes generates + a timestamp with counter N-1. SOF_TIMESTAMPING_OPT_ID_TCP + implements this behavior under all conditions. + + SOF_TIMESTAMPING_OPT_ID without modifier often reports the same, + especially when the socket option is set when no data is in + transmission. If data is being transmitted, it may be off by the + length of the output queue (SIOCOUTQ). + + The difference is due to being based on snd_una versus write_seq. + snd_una is the offset in the stream acknowledged by the peer. This + depends on factors outside of process control, such as network RTT. + write_seq is the last byte written by the process. This offset is + not affected by external inputs. + + The difference is subtle and unlikely to be noticed when configured + at initial socket creation, when no data is queued or sent. But + SOF_TIMESTAMPING_OPT_ID_TCP behavior is more robust regardless of + when the socket option is set. SOF_TIMESTAMPING_OPT_CMSG: Support recv() cmsg for all timestamped packets. Control messages diff --git a/include/net/sock.h b/include/net/sock.h index 6d207e7c4ad0..ecea3dcc2217 100644 --- a/include/net/sock.h +++ b/include/net/sock.h @@ -503,10 +503,10 @@ struct sock { #if BITS_PER_LONG==32 seqlock_t sk_stamp_seq; #endif - u16 sk_tsflags; - u8 sk_shutdown; atomic_t sk_tskey; atomic_t sk_zckey; + u32 sk_tsflags; + u8 sk_shutdown; u8 sk_clockid; u8 sk_txtime_deadline_mode : 1, @@ -1899,7 +1899,7 @@ static inline void sock_replace_proto(struct sock *sk, struct proto *proto) struct sockcm_cookie { u64 transmit_time; u32 mark; - u16 tsflags; + u32 tsflags; }; static inline void sockcm_init(struct sockcm_cookie *sockc, diff --git a/include/uapi/linux/net_tstamp.h b/include/uapi/linux/net_tstamp.h index 55501e5e7ac8..a2c66b3d7f0f 100644 --- a/include/uapi/linux/net_tstamp.h +++ b/include/uapi/linux/net_tstamp.h @@ -31,8 +31,9 @@ enum { SOF_TIMESTAMPING_OPT_PKTINFO = (1<<13), SOF_TIMESTAMPING_OPT_TX_SWHW = (1<<14), SOF_TIMESTAMPING_BIND_PHC = (1 << 15), + SOF_TIMESTAMPING_OPT_ID_TCP = (1 << 16), - SOF_TIMESTAMPING_LAST = SOF_TIMESTAMPING_BIND_PHC, + SOF_TIMESTAMPING_LAST = SOF_TIMESTAMPING_OPT_ID_TCP, SOF_TIMESTAMPING_MASK = (SOF_TIMESTAMPING_LAST - 1) | SOF_TIMESTAMPING_LAST }; diff --git a/net/core/sock.c b/net/core/sock.c index 4571914a4aa8..b0ab841e0aed 100644 --- a/net/core/sock.c +++ b/net/core/sock.c @@ -901,13 +901,20 @@ int sock_set_timestamping(struct sock *sk, int optname, if (val & ~SOF_TIMESTAMPING_MASK) return -EINVAL; + if (val & SOF_TIMESTAMPING_OPT_ID_TCP && + !(val & SOF_TIMESTAMPING_OPT_ID)) + return -EINVAL; + if (val & SOF_TIMESTAMPING_OPT_ID && !(sk->sk_tsflags & SOF_TIMESTAMPING_OPT_ID)) { if (sk_is_tcp(sk)) { if ((1 << sk->sk_state) & (TCPF_CLOSE | TCPF_LISTEN)) return -EINVAL; - atomic_set(&sk->sk_tskey, tcp_sk(sk)->snd_una); + if (val & SOF_TIMESTAMPING_OPT_ID_TCP) + atomic_set(&sk->sk_tskey, tcp_sk(sk)->write_seq); + else + atomic_set(&sk->sk_tskey, tcp_sk(sk)->snd_una); } else { atomic_set(&sk->sk_tskey, 0); } diff --git a/net/ethtool/common.c b/net/ethtool/common.c index 21cfe8557205..6f399afc2ff2 100644 --- a/net/ethtool/common.c +++ b/net/ethtool/common.c @@ -417,6 +417,7 @@ const char sof_timestamping_names[][ETH_GSTRING_LEN] = { [const_ilog2(SOF_TIMESTAMPING_OPT_PKTINFO)] = "option-pktinfo", [const_ilog2(SOF_TIMESTAMPING_OPT_TX_SWHW)] = "option-tx-swhw", [const_ilog2(SOF_TIMESTAMPING_BIND_PHC)] = "bind-phc", + [const_ilog2(SOF_TIMESTAMPING_OPT_ID_TCP)] = "option-id-tcp", }; static_assert(ARRAY_SIZE(sof_timestamping_names) == __SOF_TIMESTAMPING_CNT); From 0bdff1152c2496acf29930ec9b3c3cd7790b3f68 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Tue, 6 Dec 2022 20:42:57 -0800 Subject: [PATCH 1803/2002] net: phy: remove redundant "depends on" lines Delete a few lines of "depends on PHYLIB" since they are inside an "if PHYLIB / endif # PHYLIB" block, i.e., they are redundant and the other 50+ drivers there don't use "depends on PHYLIB" since it is not needed. Signed-off-by: Randy Dunlap Cc: Andrew Lunn Cc: Heiner Kallweit Cc: Russell King Link: https://lore.kernel.org/r/20221207044257.30036-1-rdunlap@infradead.org Signed-off-by: Jakub Kicinski --- drivers/net/phy/Kconfig | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig index af00cf44cd97..1327290decab 100644 --- a/drivers/net/phy/Kconfig +++ b/drivers/net/phy/Kconfig @@ -47,7 +47,6 @@ config LED_TRIGGER_PHY config FIXED_PHY tristate "MDIO Bus/PHY emulation with fixed speed/link PHYs" - depends on PHYLIB select SWPHY help Adds the platform "fixed" MDIO Bus to cover the boards that use @@ -112,7 +111,6 @@ config BROADCOM_PHY config BCM54140_PHY tristate "Broadcom BCM54140 PHY" - depends on PHYLIB depends on HWMON || HWMON=n select BCM_NET_PHYLIB help @@ -137,7 +135,6 @@ config BCM7XXX_PHY config BCM84881_PHY tristate "Broadcom BCM84881 PHY" - depends on PHYLIB help Support the Broadcom BCM84881 PHY. From abe2343d37c2b4361547d5d31e17340ff9ec7356 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Wed, 7 Dec 2022 09:23:14 +0000 Subject: [PATCH 1804/2002] xfrm: Fix spelling mistake "oflload" -> "offload" There is a spelling mistake in a NL_SET_ERR_MSG message. Fix it. Signed-off-by: Colin Ian King Signed-off-by: Steffen Klassert --- net/xfrm/xfrm_device.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/xfrm/xfrm_device.c b/net/xfrm/xfrm_device.c index 3e9e874522a8..4aff76c6f12e 100644 --- a/net/xfrm/xfrm_device.c +++ b/net/xfrm/xfrm_device.c @@ -379,7 +379,7 @@ int xfrm_dev_policy_add(struct net *net, struct xfrm_policy *xp, default: xdo->dev = NULL; dev_put(dev); - NL_SET_ERR_MSG(extack, "Unrecognized oflload direction"); + NL_SET_ERR_MSG(extack, "Unrecognized offload direction"); return -EINVAL; } From 44aa5a6dba8283bfda28b1517af4de711c5652a4 Mon Sep 17 00:00:00 2001 From: Artem Chernyshev Date: Tue, 6 Dec 2022 09:58:34 +0300 Subject: [PATCH 1805/2002] net: vmw_vsock: vmci: Check memcpy_from_msg() vmci_transport_dgram_enqueue() does not check the return value of memcpy_from_msg(). If memcpy_from_msg() fails, it is possible that uninitialized memory contents are sent unintentionally instead of user's message in the datagram to the destination. Return with an error if memcpy_from_msg() fails. Found by Linux Verification Center (linuxtesting.org) with SVACE. Fixes: 0f7db23a07af ("vmci_transport: switch ->enqeue_dgram, ->enqueue_stream and ->dequeue_stream to msghdr") Signed-off-by: Artem Chernyshev Reviewed-by: Stefano Garzarella Reviewed-by: Vishnu Dasa Signed-off-by: David S. Miller --- net/vmw_vsock/vmci_transport.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/net/vmw_vsock/vmci_transport.c b/net/vmw_vsock/vmci_transport.c index 842c94286d31..36eb16a40745 100644 --- a/net/vmw_vsock/vmci_transport.c +++ b/net/vmw_vsock/vmci_transport.c @@ -1711,7 +1711,11 @@ static int vmci_transport_dgram_enqueue( if (!dg) return -ENOMEM; - memcpy_from_msg(VMCI_DG_PAYLOAD(dg), msg, len); + err = memcpy_from_msg(VMCI_DG_PAYLOAD(dg), msg, len); + if (err) { + kfree(dg); + return err; + } dg->dst = vmci_make_handle(remote_addr->svm_cid, remote_addr->svm_port); From 2a7d228f1ae78b6eabef5f18bd1a8d2280555628 Mon Sep 17 00:00:00 2001 From: Pedro Tammela Date: Tue, 6 Dec 2022 10:55:10 -0300 Subject: [PATCH 1806/2002] net/sched: move struct action_ops definition out of ifdef The type definition should be visible even in configurations not using CONFIG_NET_CLS_ACT. Signed-off-by: Pedro Tammela Reviewed-by: Jamal Hadi Salim Reviewed-by: Victor Nogueira Signed-off-by: David S. Miller --- include/net/act_api.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/include/net/act_api.h b/include/net/act_api.h index c94ea1a306e0..2a6f443f0ef6 100644 --- a/include/net/act_api.h +++ b/include/net/act_api.h @@ -101,11 +101,6 @@ static inline enum flow_action_hw_stats tc_act_hw_stats(u8 hw_stats) return hw_stats; } -#ifdef CONFIG_NET_CLS_ACT - -#define ACT_P_CREATED 1 -#define ACT_P_DELETED 1 - typedef void (*tc_action_priv_destructor)(void *priv); struct tc_action_ops { @@ -140,6 +135,11 @@ struct tc_action_ops { struct netlink_ext_ack *extack); }; +#ifdef CONFIG_NET_CLS_ACT + +#define ACT_P_CREATED 1 +#define ACT_P_DELETED 1 + struct tc_action_net { struct tcf_idrinfo *idrinfo; const struct tc_action_ops *ops; From 7f0e810220e2d985338ecdd907c1598404db251d Mon Sep 17 00:00:00 2001 From: Pedro Tammela Date: Tue, 6 Dec 2022 10:55:11 -0300 Subject: [PATCH 1807/2002] net/sched: add retpoline wrapper for tc On kernels using retpoline as a spectrev2 mitigation, optimize actions and filters that are compiled as built-ins into a direct call. On subsequent patches we expose the classifiers and actions functions and wire up the wrapper into tc. Signed-off-by: Pedro Tammela Reviewed-by: Jamal Hadi Salim Reviewed-by: Victor Nogueira Signed-off-by: David S. Miller --- include/net/tc_wrapper.h | 251 +++++++++++++++++++++++++++++++++++++++ net/sched/sch_api.c | 5 + 2 files changed, 256 insertions(+) create mode 100644 include/net/tc_wrapper.h diff --git a/include/net/tc_wrapper.h b/include/net/tc_wrapper.h new file mode 100644 index 000000000000..ceed2fc089ff --- /dev/null +++ b/include/net/tc_wrapper.h @@ -0,0 +1,251 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __NET_TC_WRAPPER_H +#define __NET_TC_WRAPPER_H + +#include + +#if IS_ENABLED(CONFIG_RETPOLINE) + +#include +#include +#include + +#define TC_INDIRECT_SCOPE + +extern struct static_key_false tc_skip_wrapper; + +/* TC Actions */ +#ifdef CONFIG_NET_CLS_ACT + +#define TC_INDIRECT_ACTION_DECLARE(fname) \ + INDIRECT_CALLABLE_DECLARE(int fname(struct sk_buff *skb, \ + const struct tc_action *a, \ + struct tcf_result *res)) + +TC_INDIRECT_ACTION_DECLARE(tcf_bpf_act); +TC_INDIRECT_ACTION_DECLARE(tcf_connmark_act); +TC_INDIRECT_ACTION_DECLARE(tcf_csum_act); +TC_INDIRECT_ACTION_DECLARE(tcf_ct_act); +TC_INDIRECT_ACTION_DECLARE(tcf_ctinfo_act); +TC_INDIRECT_ACTION_DECLARE(tcf_gact_act); +TC_INDIRECT_ACTION_DECLARE(tcf_gate_act); +TC_INDIRECT_ACTION_DECLARE(tcf_ife_act); +TC_INDIRECT_ACTION_DECLARE(tcf_ipt_act); +TC_INDIRECT_ACTION_DECLARE(tcf_mirred_act); +TC_INDIRECT_ACTION_DECLARE(tcf_mpls_act); +TC_INDIRECT_ACTION_DECLARE(tcf_nat_act); +TC_INDIRECT_ACTION_DECLARE(tcf_pedit_act); +TC_INDIRECT_ACTION_DECLARE(tcf_police_act); +TC_INDIRECT_ACTION_DECLARE(tcf_sample_act); +TC_INDIRECT_ACTION_DECLARE(tcf_simp_act); +TC_INDIRECT_ACTION_DECLARE(tcf_skbedit_act); +TC_INDIRECT_ACTION_DECLARE(tcf_skbmod_act); +TC_INDIRECT_ACTION_DECLARE(tcf_vlan_act); +TC_INDIRECT_ACTION_DECLARE(tunnel_key_act); + +static inline int tc_act(struct sk_buff *skb, const struct tc_action *a, + struct tcf_result *res) +{ + if (static_branch_likely(&tc_skip_wrapper)) + goto skip; + +#if IS_BUILTIN(CONFIG_NET_ACT_GACT) + if (a->ops->act == tcf_gact_act) + return tcf_gact_act(skb, a, res); +#endif +#if IS_BUILTIN(CONFIG_NET_ACT_MIRRED) + if (a->ops->act == tcf_mirred_act) + return tcf_mirred_act(skb, a, res); +#endif +#if IS_BUILTIN(CONFIG_NET_ACT_PEDIT) + if (a->ops->act == tcf_pedit_act) + return tcf_pedit_act(skb, a, res); +#endif +#if IS_BUILTIN(CONFIG_NET_ACT_SKBEDIT) + if (a->ops->act == tcf_skbedit_act) + return tcf_skbedit_act(skb, a, res); +#endif +#if IS_BUILTIN(CONFIG_NET_ACT_SKBMOD) + if (a->ops->act == tcf_skbmod_act) + return tcf_skbmod_act(skb, a, res); +#endif +#if IS_BUILTIN(CONFIG_NET_ACT_POLICE) + if (a->ops->act == tcf_police_act) + return tcf_police_act(skb, a, res); +#endif +#if IS_BUILTIN(CONFIG_NET_ACT_BPF) + if (a->ops->act == tcf_bpf_act) + return tcf_bpf_act(skb, a, res); +#endif +#if IS_BUILTIN(CONFIG_NET_ACT_CONNMARK) + if (a->ops->act == tcf_connmark_act) + return tcf_connmark_act(skb, a, res); +#endif +#if IS_BUILTIN(CONFIG_NET_ACT_CSUM) + if (a->ops->act == tcf_csum_act) + return tcf_csum_act(skb, a, res); +#endif +#if IS_BUILTIN(CONFIG_NET_ACT_CT) + if (a->ops->act == tcf_ct_act) + return tcf_ct_act(skb, a, res); +#endif +#if IS_BUILTIN(CONFIG_NET_ACT_CTINFO) + if (a->ops->act == tcf_ctinfo_act) + return tcf_ctinfo_act(skb, a, res); +#endif +#if IS_BUILTIN(CONFIG_NET_ACT_GATE) + if (a->ops->act == tcf_gate_act) + return tcf_gate_act(skb, a, res); +#endif +#if IS_BUILTIN(CONFIG_NET_ACT_MPLS) + if (a->ops->act == tcf_mpls_act) + return tcf_mpls_act(skb, a, res); +#endif +#if IS_BUILTIN(CONFIG_NET_ACT_NAT) + if (a->ops->act == tcf_nat_act) + return tcf_nat_act(skb, a, res); +#endif +#if IS_BUILTIN(CONFIG_NET_ACT_TUNNEL_KEY) + if (a->ops->act == tunnel_key_act) + return tunnel_key_act(skb, a, res); +#endif +#if IS_BUILTIN(CONFIG_NET_ACT_VLAN) + if (a->ops->act == tcf_vlan_act) + return tcf_vlan_act(skb, a, res); +#endif +#if IS_BUILTIN(CONFIG_NET_ACT_IFE) + if (a->ops->act == tcf_ife_act) + return tcf_ife_act(skb, a, res); +#endif +#if IS_BUILTIN(CONFIG_NET_ACT_IPT) + if (a->ops->act == tcf_ipt_act) + return tcf_ipt_act(skb, a, res); +#endif +#if IS_BUILTIN(CONFIG_NET_ACT_SIMP) + if (a->ops->act == tcf_simp_act) + return tcf_simp_act(skb, a, res); +#endif +#if IS_BUILTIN(CONFIG_NET_ACT_SAMPLE) + if (a->ops->act == tcf_sample_act) + return tcf_sample_act(skb, a, res); +#endif + +skip: + return a->ops->act(skb, a, res); +} + +#endif /* CONFIG_NET_CLS_ACT */ + +/* TC Filters */ +#ifdef CONFIG_NET_CLS + +#define TC_INDIRECT_FILTER_DECLARE(fname) \ + INDIRECT_CALLABLE_DECLARE(int fname(struct sk_buff *skb, \ + const struct tcf_proto *tp, \ + struct tcf_result *res)) + +TC_INDIRECT_FILTER_DECLARE(basic_classify); +TC_INDIRECT_FILTER_DECLARE(cls_bpf_classify); +TC_INDIRECT_FILTER_DECLARE(cls_cgroup_classify); +TC_INDIRECT_FILTER_DECLARE(fl_classify); +TC_INDIRECT_FILTER_DECLARE(flow_classify); +TC_INDIRECT_FILTER_DECLARE(fw_classify); +TC_INDIRECT_FILTER_DECLARE(mall_classify); +TC_INDIRECT_FILTER_DECLARE(route4_classify); +TC_INDIRECT_FILTER_DECLARE(rsvp_classify); +TC_INDIRECT_FILTER_DECLARE(rsvp6_classify); +TC_INDIRECT_FILTER_DECLARE(tcindex_classify); +TC_INDIRECT_FILTER_DECLARE(u32_classify); + +static inline int tc_classify(struct sk_buff *skb, const struct tcf_proto *tp, + struct tcf_result *res) +{ + if (static_branch_likely(&tc_skip_wrapper)) + goto skip; + +#if IS_BUILTIN(CONFIG_NET_CLS_BPF) + if (tp->classify == cls_bpf_classify) + return cls_bpf_classify(skb, tp, res); +#endif +#if IS_BUILTIN(CONFIG_NET_CLS_U32) + if (tp->classify == u32_classify) + return u32_classify(skb, tp, res); +#endif +#if IS_BUILTIN(CONFIG_NET_CLS_FLOWER) + if (tp->classify == fl_classify) + return fl_classify(skb, tp, res); +#endif +#if IS_BUILTIN(CONFIG_NET_CLS_FW) + if (tp->classify == fw_classify) + return fw_classify(skb, tp, res); +#endif +#if IS_BUILTIN(CONFIG_NET_CLS_MATCHALL) + if (tp->classify == mall_classify) + return mall_classify(skb, tp, res); +#endif +#if IS_BUILTIN(CONFIG_NET_CLS_BASIC) + if (tp->classify == basic_classify) + return basic_classify(skb, tp, res); +#endif +#if IS_BUILTIN(CONFIG_NET_CLS_CGROUP) + if (tp->classify == cls_cgroup_classify) + return cls_cgroup_classify(skb, tp, res); +#endif +#if IS_BUILTIN(CONFIG_NET_CLS_FLOW) + if (tp->classify == flow_classify) + return flow_classify(skb, tp, res); +#endif +#if IS_BUILTIN(CONFIG_NET_CLS_ROUTE4) + if (tp->classify == route4_classify) + return route4_classify(skb, tp, res); +#endif +#if IS_BUILTIN(CONFIG_NET_CLS_RSVP) + if (tp->classify == rsvp_classify) + return rsvp_classify(skb, tp, res); +#endif +#if IS_BUILTIN(CONFIG_NET_CLS_RSVP6) + if (tp->classify == rsvp6_classify) + return rsvp6_classify(skb, tp, res); +#endif +#if IS_BUILTIN(CONFIG_NET_CLS_TCINDEX) + if (tp->classify == tcindex_classify) + return tcindex_classify(skb, tp, res); +#endif + +skip: + return tp->classify(skb, tp, res); +} + +static inline void tc_wrapper_init(void) +{ +#ifdef CONFIG_X86 + if (!cpu_feature_enabled(X86_FEATURE_RETPOLINE)) + static_branch_enable(&tc_skip_wrapper); +#endif +} + +#endif /* CONFIG_NET_CLS */ + +#else + +#define TC_INDIRECT_SCOPE static + +static inline int tc_act(struct sk_buff *skb, const struct tc_action *a, + struct tcf_result *res) +{ + return a->ops->act(skb, a, res); +} + +static inline int tc_classify(struct sk_buff *skb, const struct tcf_proto *tp, + struct tcf_result *res) +{ + return tp->classify(skb, tp, res); +} + +static inline void tc_wrapper_init(void) +{ +} + +#endif + +#endif /* __NET_TC_WRAPPER_H */ diff --git a/net/sched/sch_api.c b/net/sched/sch_api.c index 4a27dfb1ba0f..2317db02c764 100644 --- a/net/sched/sch_api.c +++ b/net/sched/sch_api.c @@ -31,6 +31,7 @@ #include #include #include +#include #include @@ -2273,6 +2274,8 @@ static struct pernet_operations psched_net_ops = { .exit = psched_net_exit, }; +DEFINE_STATIC_KEY_FALSE(tc_skip_wrapper); + static int __init pktsched_init(void) { int err; @@ -2300,6 +2303,8 @@ static int __init pktsched_init(void) rtnl_register(PF_UNSPEC, RTM_GETTCLASS, tc_ctl_tclass, tc_dump_tclass, 0); + tc_wrapper_init(); + return 0; } From 871cf386dd16705b1e08942efd02c58801293d01 Mon Sep 17 00:00:00 2001 From: Pedro Tammela Date: Tue, 6 Dec 2022 10:55:12 -0300 Subject: [PATCH 1808/2002] net/sched: avoid indirect act functions on retpoline kernels Expose the necessary tc act functions and wire up act_api to use direct calls in retpoline kernels. Signed-off-by: Pedro Tammela Reviewed-by: Jamal Hadi Salim Reviewed-by: Victor Nogueira Signed-off-by: David S. Miller --- net/sched/act_api.c | 3 ++- net/sched/act_bpf.c | 6 ++++-- net/sched/act_connmark.c | 6 ++++-- net/sched/act_csum.c | 6 ++++-- net/sched/act_ct.c | 5 +++-- net/sched/act_ctinfo.c | 6 ++++-- net/sched/act_gact.c | 6 ++++-- net/sched/act_gate.c | 6 ++++-- net/sched/act_ife.c | 6 ++++-- net/sched/act_ipt.c | 6 ++++-- net/sched/act_mirred.c | 6 ++++-- net/sched/act_mpls.c | 6 ++++-- net/sched/act_nat.c | 7 ++++--- net/sched/act_pedit.c | 6 ++++-- net/sched/act_police.c | 6 ++++-- net/sched/act_sample.c | 6 ++++-- net/sched/act_simple.c | 6 ++++-- net/sched/act_skbedit.c | 6 ++++-- net/sched/act_skbmod.c | 6 ++++-- net/sched/act_tunnel_key.c | 6 ++++-- net/sched/act_vlan.c | 6 ++++-- 21 files changed, 81 insertions(+), 42 deletions(-) diff --git a/net/sched/act_api.c b/net/sched/act_api.c index 9b31a10cc639..5b3c0ac495be 100644 --- a/net/sched/act_api.c +++ b/net/sched/act_api.c @@ -23,6 +23,7 @@ #include #include #include +#include #ifdef CONFIG_INET DEFINE_STATIC_KEY_FALSE(tcf_frag_xmit_count); @@ -1080,7 +1081,7 @@ restart_act_graph: repeat_ttl = 32; repeat: - ret = a->ops->act(skb, a, res); + ret = tc_act(skb, a, res); if (unlikely(ret == TC_ACT_REPEAT)) { if (--repeat_ttl != 0) goto repeat; diff --git a/net/sched/act_bpf.c b/net/sched/act_bpf.c index b79eee44e24e..b0455fda7d0b 100644 --- a/net/sched/act_bpf.c +++ b/net/sched/act_bpf.c @@ -18,6 +18,7 @@ #include #include +#include #define ACT_BPF_NAME_LEN 256 @@ -31,8 +32,9 @@ struct tcf_bpf_cfg { static struct tc_action_ops act_bpf_ops; -static int tcf_bpf_act(struct sk_buff *skb, const struct tc_action *act, - struct tcf_result *res) +TC_INDIRECT_SCOPE int tcf_bpf_act(struct sk_buff *skb, + const struct tc_action *act, + struct tcf_result *res) { bool at_ingress = skb_at_tc_ingress(skb); struct tcf_bpf *prog = to_bpf(act); diff --git a/net/sched/act_connmark.c b/net/sched/act_connmark.c index d41002e4613f..7e63ff7e3ed7 100644 --- a/net/sched/act_connmark.c +++ b/net/sched/act_connmark.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include @@ -27,8 +28,9 @@ static struct tc_action_ops act_connmark_ops; -static int tcf_connmark_act(struct sk_buff *skb, const struct tc_action *a, - struct tcf_result *res) +TC_INDIRECT_SCOPE int tcf_connmark_act(struct sk_buff *skb, + const struct tc_action *a, + struct tcf_result *res) { const struct nf_conntrack_tuple_hash *thash; struct nf_conntrack_tuple tuple; diff --git a/net/sched/act_csum.c b/net/sched/act_csum.c index 1366adf9b909..95e9304024b7 100644 --- a/net/sched/act_csum.c +++ b/net/sched/act_csum.c @@ -32,6 +32,7 @@ #include #include +#include static const struct nla_policy csum_policy[TCA_CSUM_MAX + 1] = { [TCA_CSUM_PARMS] = { .len = sizeof(struct tc_csum), }, @@ -563,8 +564,9 @@ fail: return 0; } -static int tcf_csum_act(struct sk_buff *skb, const struct tc_action *a, - struct tcf_result *res) +TC_INDIRECT_SCOPE int tcf_csum_act(struct sk_buff *skb, + const struct tc_action *a, + struct tcf_result *res) { struct tcf_csum *p = to_tcf_csum(a); bool orig_vlan_tag_present = false; diff --git a/net/sched/act_ct.c b/net/sched/act_ct.c index dd5ae7551956..f6df0168c91f 100644 --- a/net/sched/act_ct.c +++ b/net/sched/act_ct.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include @@ -1038,8 +1039,8 @@ static int tcf_ct_act_nat(struct sk_buff *skb, #endif } -static int tcf_ct_act(struct sk_buff *skb, const struct tc_action *a, - struct tcf_result *res) +TC_INDIRECT_SCOPE int tcf_ct_act(struct sk_buff *skb, const struct tc_action *a, + struct tcf_result *res) { struct net *net = dev_net(skb->dev); enum ip_conntrack_info ctinfo; diff --git a/net/sched/act_ctinfo.c b/net/sched/act_ctinfo.c index eaa02f098d1c..4b1b59da5c0b 100644 --- a/net/sched/act_ctinfo.c +++ b/net/sched/act_ctinfo.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include @@ -75,8 +76,9 @@ static void tcf_ctinfo_cpmark_set(struct nf_conn *ct, struct tcf_ctinfo *ca, skb->mark = READ_ONCE(ct->mark) & cp->cpmarkmask; } -static int tcf_ctinfo_act(struct sk_buff *skb, const struct tc_action *a, - struct tcf_result *res) +TC_INDIRECT_SCOPE int tcf_ctinfo_act(struct sk_buff *skb, + const struct tc_action *a, + struct tcf_result *res) { const struct nf_conntrack_tuple_hash *thash = NULL; struct tcf_ctinfo *ca = to_ctinfo(a); diff --git a/net/sched/act_gact.c b/net/sched/act_gact.c index 62d682b96b88..54f1b13b2360 100644 --- a/net/sched/act_gact.c +++ b/net/sched/act_gact.c @@ -18,6 +18,7 @@ #include #include #include +#include static struct tc_action_ops act_gact_ops; @@ -145,8 +146,9 @@ release_idr: return err; } -static int tcf_gact_act(struct sk_buff *skb, const struct tc_action *a, - struct tcf_result *res) +TC_INDIRECT_SCOPE int tcf_gact_act(struct sk_buff *skb, + const struct tc_action *a, + struct tcf_result *res) { struct tcf_gact *gact = to_gact(a); int action = READ_ONCE(gact->tcf_action); diff --git a/net/sched/act_gate.c b/net/sched/act_gate.c index 3049878e7315..9b8def0be41e 100644 --- a/net/sched/act_gate.c +++ b/net/sched/act_gate.c @@ -14,6 +14,7 @@ #include #include #include +#include static struct tc_action_ops act_gate_ops; @@ -113,8 +114,9 @@ static enum hrtimer_restart gate_timer_func(struct hrtimer *timer) return HRTIMER_RESTART; } -static int tcf_gate_act(struct sk_buff *skb, const struct tc_action *a, - struct tcf_result *res) +TC_INDIRECT_SCOPE int tcf_gate_act(struct sk_buff *skb, + const struct tc_action *a, + struct tcf_result *res) { struct tcf_gate *gact = to_gate(a); diff --git a/net/sched/act_ife.c b/net/sched/act_ife.c index 41d63b33461d..bc7611b0744c 100644 --- a/net/sched/act_ife.c +++ b/net/sched/act_ife.c @@ -29,6 +29,7 @@ #include #include #include +#include static int max_metacnt = IFE_META_MAX + 1; static struct tc_action_ops act_ife_ops; @@ -861,8 +862,9 @@ static int tcf_ife_encode(struct sk_buff *skb, const struct tc_action *a, return action; } -static int tcf_ife_act(struct sk_buff *skb, const struct tc_action *a, - struct tcf_result *res) +TC_INDIRECT_SCOPE int tcf_ife_act(struct sk_buff *skb, + const struct tc_action *a, + struct tcf_result *res) { struct tcf_ife_info *ife = to_ife(a); struct tcf_ife_params *p; diff --git a/net/sched/act_ipt.c b/net/sched/act_ipt.c index 1625e1037416..5d96ffebd40f 100644 --- a/net/sched/act_ipt.c +++ b/net/sched/act_ipt.c @@ -20,6 +20,7 @@ #include #include #include +#include #include @@ -216,8 +217,9 @@ static int tcf_xt_init(struct net *net, struct nlattr *nla, a, &act_xt_ops, tp, flags); } -static int tcf_ipt_act(struct sk_buff *skb, const struct tc_action *a, - struct tcf_result *res) +TC_INDIRECT_SCOPE int tcf_ipt_act(struct sk_buff *skb, + const struct tc_action *a, + struct tcf_result *res) { int ret = 0, result = 0; struct tcf_ipt *ipt = to_ipt(a); diff --git a/net/sched/act_mirred.c b/net/sched/act_mirred.c index b8ad6ae282c0..7284bcea7b0b 100644 --- a/net/sched/act_mirred.c +++ b/net/sched/act_mirred.c @@ -24,6 +24,7 @@ #include #include #include +#include static LIST_HEAD(mirred_list); static DEFINE_SPINLOCK(mirred_list_lock); @@ -217,8 +218,9 @@ static int tcf_mirred_forward(bool want_ingress, struct sk_buff *skb) return err; } -static int tcf_mirred_act(struct sk_buff *skb, const struct tc_action *a, - struct tcf_result *res) +TC_INDIRECT_SCOPE int tcf_mirred_act(struct sk_buff *skb, + const struct tc_action *a, + struct tcf_result *res) { struct tcf_mirred *m = to_mirred(a); struct sk_buff *skb2 = skb; diff --git a/net/sched/act_mpls.c b/net/sched/act_mpls.c index 8ad25cc8ccd5..ff47ce4d3968 100644 --- a/net/sched/act_mpls.c +++ b/net/sched/act_mpls.c @@ -14,6 +14,7 @@ #include #include #include +#include static struct tc_action_ops act_mpls_ops; @@ -49,8 +50,9 @@ static __be32 tcf_mpls_get_lse(struct mpls_shim_hdr *lse, return cpu_to_be32(new_lse); } -static int tcf_mpls_act(struct sk_buff *skb, const struct tc_action *a, - struct tcf_result *res) +TC_INDIRECT_SCOPE int tcf_mpls_act(struct sk_buff *skb, + const struct tc_action *a, + struct tcf_result *res) { struct tcf_mpls *m = to_mpls(a); struct tcf_mpls_params *p; diff --git a/net/sched/act_nat.c b/net/sched/act_nat.c index 9265145f1040..74c74be33048 100644 --- a/net/sched/act_nat.c +++ b/net/sched/act_nat.c @@ -24,7 +24,7 @@ #include #include #include - +#include static struct tc_action_ops act_nat_ops; @@ -98,8 +98,9 @@ release_idr: return err; } -static int tcf_nat_act(struct sk_buff *skb, const struct tc_action *a, - struct tcf_result *res) +TC_INDIRECT_SCOPE int tcf_nat_act(struct sk_buff *skb, + const struct tc_action *a, + struct tcf_result *res) { struct tcf_nat *p = to_tcf_nat(a); struct iphdr *iph; diff --git a/net/sched/act_pedit.c b/net/sched/act_pedit.c index 94ed5857ce67..a0378e9f0121 100644 --- a/net/sched/act_pedit.c +++ b/net/sched/act_pedit.c @@ -20,6 +20,7 @@ #include #include #include +#include static struct tc_action_ops act_pedit_ops; @@ -319,8 +320,9 @@ static int pedit_skb_hdr_offset(struct sk_buff *skb, return ret; } -static int tcf_pedit_act(struct sk_buff *skb, const struct tc_action *a, - struct tcf_result *res) +TC_INDIRECT_SCOPE int tcf_pedit_act(struct sk_buff *skb, + const struct tc_action *a, + struct tcf_result *res) { struct tcf_pedit *p = to_pedit(a); u32 max_offset; diff --git a/net/sched/act_police.c b/net/sched/act_police.c index 0adb26e366a7..227cba58ce9f 100644 --- a/net/sched/act_police.c +++ b/net/sched/act_police.c @@ -19,6 +19,7 @@ #include #include #include +#include /* Each policer is serialized by its individual spinlock */ @@ -242,8 +243,9 @@ static bool tcf_police_mtu_check(struct sk_buff *skb, u32 limit) return len <= limit; } -static int tcf_police_act(struct sk_buff *skb, const struct tc_action *a, - struct tcf_result *res) +TC_INDIRECT_SCOPE int tcf_police_act(struct sk_buff *skb, + const struct tc_action *a, + struct tcf_result *res) { struct tcf_police *police = to_police(a); s64 now, toks, ppstoks = 0, ptoks = 0; diff --git a/net/sched/act_sample.c b/net/sched/act_sample.c index 7a25477f5d99..98dea08c1764 100644 --- a/net/sched/act_sample.c +++ b/net/sched/act_sample.c @@ -20,6 +20,7 @@ #include #include #include +#include #include @@ -153,8 +154,9 @@ static bool tcf_sample_dev_ok_push(struct net_device *dev) } } -static int tcf_sample_act(struct sk_buff *skb, const struct tc_action *a, - struct tcf_result *res) +TC_INDIRECT_SCOPE int tcf_sample_act(struct sk_buff *skb, + const struct tc_action *a, + struct tcf_result *res) { struct tcf_sample *s = to_sample(a); struct psample_group *psample_group; diff --git a/net/sched/act_simple.c b/net/sched/act_simple.c index 18d376135461..4b84514534f3 100644 --- a/net/sched/act_simple.c +++ b/net/sched/act_simple.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include @@ -21,8 +22,9 @@ static struct tc_action_ops act_simp_ops; #define SIMP_MAX_DATA 32 -static int tcf_simp_act(struct sk_buff *skb, const struct tc_action *a, - struct tcf_result *res) +TC_INDIRECT_SCOPE int tcf_simp_act(struct sk_buff *skb, + const struct tc_action *a, + struct tcf_result *res) { struct tcf_defact *d = to_defact(a); diff --git a/net/sched/act_skbedit.c b/net/sched/act_skbedit.c index 1710780c908a..ce7008cf291c 100644 --- a/net/sched/act_skbedit.c +++ b/net/sched/act_skbedit.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include @@ -36,8 +37,9 @@ static u16 tcf_skbedit_hash(struct tcf_skbedit_params *params, return netdev_cap_txqueue(skb->dev, queue_mapping); } -static int tcf_skbedit_act(struct sk_buff *skb, const struct tc_action *a, - struct tcf_result *res) +TC_INDIRECT_SCOPE int tcf_skbedit_act(struct sk_buff *skb, + const struct tc_action *a, + struct tcf_result *res) { struct tcf_skbedit *d = to_skbedit(a); struct tcf_skbedit_params *params; diff --git a/net/sched/act_skbmod.c b/net/sched/act_skbmod.c index d98758a63934..dffa990a9629 100644 --- a/net/sched/act_skbmod.c +++ b/net/sched/act_skbmod.c @@ -15,14 +15,16 @@ #include #include #include +#include #include #include static struct tc_action_ops act_skbmod_ops; -static int tcf_skbmod_act(struct sk_buff *skb, const struct tc_action *a, - struct tcf_result *res) +TC_INDIRECT_SCOPE int tcf_skbmod_act(struct sk_buff *skb, + const struct tc_action *a, + struct tcf_result *res) { struct tcf_skbmod *d = to_skbmod(a); int action, max_edit_len, err; diff --git a/net/sched/act_tunnel_key.c b/net/sched/act_tunnel_key.c index 2691a3d8e451..2d12d2626415 100644 --- a/net/sched/act_tunnel_key.c +++ b/net/sched/act_tunnel_key.c @@ -16,14 +16,16 @@ #include #include #include +#include #include #include static struct tc_action_ops act_tunnel_key_ops; -static int tunnel_key_act(struct sk_buff *skb, const struct tc_action *a, - struct tcf_result *res) +TC_INDIRECT_SCOPE int tunnel_key_act(struct sk_buff *skb, + const struct tc_action *a, + struct tcf_result *res) { struct tcf_tunnel_key *t = to_tunnel_key(a); struct tcf_tunnel_key_params *params; diff --git a/net/sched/act_vlan.c b/net/sched/act_vlan.c index 7b24e898a3e6..0251442f5f29 100644 --- a/net/sched/act_vlan.c +++ b/net/sched/act_vlan.c @@ -12,14 +12,16 @@ #include #include #include +#include #include #include static struct tc_action_ops act_vlan_ops; -static int tcf_vlan_act(struct sk_buff *skb, const struct tc_action *a, - struct tcf_result *res) +TC_INDIRECT_SCOPE int tcf_vlan_act(struct sk_buff *skb, + const struct tc_action *a, + struct tcf_result *res) { struct tcf_vlan *v = to_vlan(a); struct tcf_vlan_params *p; From 9f3101dca3a7c69027c65770ac28803768efefa5 Mon Sep 17 00:00:00 2001 From: Pedro Tammela Date: Tue, 6 Dec 2022 10:55:13 -0300 Subject: [PATCH 1809/2002] net/sched: avoid indirect classify functions on retpoline kernels Expose the necessary tc classifier functions and wire up cls_api to use direct calls in retpoline kernels. Signed-off-by: Pedro Tammela Reviewed-by: Jamal Hadi Salim Reviewed-by: Victor Nogueira Signed-off-by: David S. Miller --- net/sched/cls_api.c | 3 ++- net/sched/cls_basic.c | 6 ++++-- net/sched/cls_bpf.c | 6 ++++-- net/sched/cls_cgroup.c | 6 ++++-- net/sched/cls_flow.c | 6 ++++-- net/sched/cls_flower.c | 6 ++++-- net/sched/cls_fw.c | 6 ++++-- net/sched/cls_matchall.c | 6 ++++-- net/sched/cls_route.c | 6 ++++-- net/sched/cls_rsvp.c | 2 ++ net/sched/cls_rsvp.h | 6 +++--- net/sched/cls_rsvp6.c | 2 ++ net/sched/cls_tcindex.c | 7 ++++--- net/sched/cls_u32.c | 6 ++++-- 14 files changed, 49 insertions(+), 25 deletions(-) diff --git a/net/sched/cls_api.c b/net/sched/cls_api.c index 23d1cfa4f58c..668130f08903 100644 --- a/net/sched/cls_api.c +++ b/net/sched/cls_api.c @@ -40,6 +40,7 @@ #include #include #include +#include extern const struct nla_policy rtm_tca_policy[TCA_MAX + 1]; @@ -1564,7 +1565,7 @@ reclassify: tp->protocol != htons(ETH_P_ALL)) continue; - err = tp->classify(skb, tp, res); + err = tc_classify(skb, tp, res); #ifdef CONFIG_NET_CLS_ACT if (unlikely(err == TC_ACT_RECLASSIFY && !compat_mode)) { first_tp = orig_tp; diff --git a/net/sched/cls_basic.c b/net/sched/cls_basic.c index d229ce99e554..1b92c33b5f81 100644 --- a/net/sched/cls_basic.c +++ b/net/sched/cls_basic.c @@ -18,6 +18,7 @@ #include #include #include +#include struct basic_head { struct list_head flist; @@ -36,8 +37,9 @@ struct basic_filter { struct rcu_work rwork; }; -static int basic_classify(struct sk_buff *skb, const struct tcf_proto *tp, - struct tcf_result *res) +TC_INDIRECT_SCOPE int basic_classify(struct sk_buff *skb, + const struct tcf_proto *tp, + struct tcf_result *res) { int r; struct basic_head *head = rcu_dereference_bh(tp->root); diff --git a/net/sched/cls_bpf.c b/net/sched/cls_bpf.c index bc317b3eac12..466c26df853a 100644 --- a/net/sched/cls_bpf.c +++ b/net/sched/cls_bpf.c @@ -19,6 +19,7 @@ #include #include #include +#include MODULE_LICENSE("GPL"); MODULE_AUTHOR("Daniel Borkmann "); @@ -77,8 +78,9 @@ static int cls_bpf_exec_opcode(int code) } } -static int cls_bpf_classify(struct sk_buff *skb, const struct tcf_proto *tp, - struct tcf_result *res) +TC_INDIRECT_SCOPE int cls_bpf_classify(struct sk_buff *skb, + const struct tcf_proto *tp, + struct tcf_result *res) { struct cls_bpf_head *head = rcu_dereference_bh(tp->root); bool at_ingress = skb_at_tc_ingress(skb); diff --git a/net/sched/cls_cgroup.c b/net/sched/cls_cgroup.c index ed00001b528a..bd9322d71910 100644 --- a/net/sched/cls_cgroup.c +++ b/net/sched/cls_cgroup.c @@ -13,6 +13,7 @@ #include #include #include +#include struct cls_cgroup_head { u32 handle; @@ -22,8 +23,9 @@ struct cls_cgroup_head { struct rcu_work rwork; }; -static int cls_cgroup_classify(struct sk_buff *skb, const struct tcf_proto *tp, - struct tcf_result *res) +TC_INDIRECT_SCOPE int cls_cgroup_classify(struct sk_buff *skb, + const struct tcf_proto *tp, + struct tcf_result *res) { struct cls_cgroup_head *head = rcu_dereference_bh(tp->root); u32 classid = task_get_classid(skb); diff --git a/net/sched/cls_flow.c b/net/sched/cls_flow.c index 014cd3de7b5d..535668e1f748 100644 --- a/net/sched/cls_flow.c +++ b/net/sched/cls_flow.c @@ -24,6 +24,7 @@ #include #include #include +#include #if IS_ENABLED(CONFIG_NF_CONNTRACK) #include @@ -292,8 +293,9 @@ static u32 flow_key_get(struct sk_buff *skb, int key, struct flow_keys *flow) (1 << FLOW_KEY_NFCT_PROTO_SRC) | \ (1 << FLOW_KEY_NFCT_PROTO_DST)) -static int flow_classify(struct sk_buff *skb, const struct tcf_proto *tp, - struct tcf_result *res) +TC_INDIRECT_SCOPE int flow_classify(struct sk_buff *skb, + const struct tcf_proto *tp, + struct tcf_result *res) { struct flow_head *head = rcu_dereference_bh(tp->root); struct flow_filter *f; diff --git a/net/sched/cls_flower.c b/net/sched/cls_flower.c index 25bc57ee6ea1..0b15698b3531 100644 --- a/net/sched/cls_flower.c +++ b/net/sched/cls_flower.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #include @@ -305,8 +306,9 @@ static u16 fl_ct_info_to_flower_map[] = { TCA_FLOWER_KEY_CT_FLAGS_NEW, }; -static int fl_classify(struct sk_buff *skb, const struct tcf_proto *tp, - struct tcf_result *res) +TC_INDIRECT_SCOPE int fl_classify(struct sk_buff *skb, + const struct tcf_proto *tp, + struct tcf_result *res) { struct cls_fl_head *head = rcu_dereference_bh(tp->root); bool post_ct = tc_skb_cb(skb)->post_ct; diff --git a/net/sched/cls_fw.c b/net/sched/cls_fw.c index a32351da968c..ae9439a6c56c 100644 --- a/net/sched/cls_fw.c +++ b/net/sched/cls_fw.c @@ -21,6 +21,7 @@ #include #include #include +#include #define HTSIZE 256 @@ -47,8 +48,9 @@ static u32 fw_hash(u32 handle) return handle % HTSIZE; } -static int fw_classify(struct sk_buff *skb, const struct tcf_proto *tp, - struct tcf_result *res) +TC_INDIRECT_SCOPE int fw_classify(struct sk_buff *skb, + const struct tcf_proto *tp, + struct tcf_result *res) { struct fw_head *head = rcu_dereference_bh(tp->root); struct fw_filter *f; diff --git a/net/sched/cls_matchall.c b/net/sched/cls_matchall.c index 39a5d9c170de..705f63da2c21 100644 --- a/net/sched/cls_matchall.c +++ b/net/sched/cls_matchall.c @@ -12,6 +12,7 @@ #include #include +#include struct cls_mall_head { struct tcf_exts exts; @@ -24,8 +25,9 @@ struct cls_mall_head { bool deleting; }; -static int mall_classify(struct sk_buff *skb, const struct tcf_proto *tp, - struct tcf_result *res) +TC_INDIRECT_SCOPE int mall_classify(struct sk_buff *skb, + const struct tcf_proto *tp, + struct tcf_result *res) { struct cls_mall_head *head = rcu_dereference_bh(tp->root); diff --git a/net/sched/cls_route.c b/net/sched/cls_route.c index 9e43b929d4ca..d0c53724d3e8 100644 --- a/net/sched/cls_route.c +++ b/net/sched/cls_route.c @@ -17,6 +17,7 @@ #include #include #include +#include /* * 1. For now we assume that route tags < 256. @@ -121,8 +122,9 @@ static inline int route4_hash_wild(void) return 0; \ } -static int route4_classify(struct sk_buff *skb, const struct tcf_proto *tp, - struct tcf_result *res) +TC_INDIRECT_SCOPE int route4_classify(struct sk_buff *skb, + const struct tcf_proto *tp, + struct tcf_result *res) { struct route4_head *head = rcu_dereference_bh(tp->root); struct dst_entry *dst; diff --git a/net/sched/cls_rsvp.c b/net/sched/cls_rsvp.c index de1c1d4da597..03d8619bd9c6 100644 --- a/net/sched/cls_rsvp.c +++ b/net/sched/cls_rsvp.c @@ -15,10 +15,12 @@ #include #include #include +#include #define RSVP_DST_LEN 1 #define RSVP_ID "rsvp" #define RSVP_OPS cls_rsvp_ops +#define RSVP_CLS rsvp_classify #include "cls_rsvp.h" MODULE_LICENSE("GPL"); diff --git a/net/sched/cls_rsvp.h b/net/sched/cls_rsvp.h index b00a7dbd0587..869efba9f834 100644 --- a/net/sched/cls_rsvp.h +++ b/net/sched/cls_rsvp.h @@ -124,8 +124,8 @@ static inline unsigned int hash_src(__be32 *src) return r; \ } -static int rsvp_classify(struct sk_buff *skb, const struct tcf_proto *tp, - struct tcf_result *res) +TC_INDIRECT_SCOPE int RSVP_CLS(struct sk_buff *skb, const struct tcf_proto *tp, + struct tcf_result *res) { struct rsvp_head *head = rcu_dereference_bh(tp->root); struct rsvp_session *s; @@ -738,7 +738,7 @@ static void rsvp_bind_class(void *fh, u32 classid, unsigned long cl, void *q, static struct tcf_proto_ops RSVP_OPS __read_mostly = { .kind = RSVP_ID, - .classify = rsvp_classify, + .classify = RSVP_CLS, .init = rsvp_init, .destroy = rsvp_destroy, .get = rsvp_get, diff --git a/net/sched/cls_rsvp6.c b/net/sched/cls_rsvp6.c index 64078846000e..e627cc32d633 100644 --- a/net/sched/cls_rsvp6.c +++ b/net/sched/cls_rsvp6.c @@ -15,10 +15,12 @@ #include #include #include +#include #define RSVP_DST_LEN 4 #define RSVP_ID "rsvp6" #define RSVP_OPS cls_rsvp6_ops +#define RSVP_CLS rsvp6_classify #include "cls_rsvp.h" MODULE_LICENSE("GPL"); diff --git a/net/sched/cls_tcindex.c b/net/sched/cls_tcindex.c index 1c9eeb98d826..eb0e9458e722 100644 --- a/net/sched/cls_tcindex.c +++ b/net/sched/cls_tcindex.c @@ -16,6 +16,7 @@ #include #include #include +#include /* * Passing parameters to the root seems to be done more awkwardly than really @@ -98,9 +99,9 @@ static struct tcindex_filter_result *tcindex_lookup(struct tcindex_data *p, return NULL; } - -static int tcindex_classify(struct sk_buff *skb, const struct tcf_proto *tp, - struct tcf_result *res) +TC_INDIRECT_SCOPE int tcindex_classify(struct sk_buff *skb, + const struct tcf_proto *tp, + struct tcf_result *res) { struct tcindex_data *p = rcu_dereference_bh(tp->root); struct tcindex_filter_result *f; diff --git a/net/sched/cls_u32.c b/net/sched/cls_u32.c index 34d25f7a0687..4e2e269f121f 100644 --- a/net/sched/cls_u32.c +++ b/net/sched/cls_u32.c @@ -39,6 +39,7 @@ #include #include #include +#include struct tc_u_knode { struct tc_u_knode __rcu *next; @@ -100,8 +101,9 @@ static inline unsigned int u32_hash_fold(__be32 key, return h; } -static int u32_classify(struct sk_buff *skb, const struct tcf_proto *tp, - struct tcf_result *res) +TC_INDIRECT_SCOPE int u32_classify(struct sk_buff *skb, + const struct tcf_proto *tp, + struct tcf_result *res) { struct { struct tc_u_knode *knode; From e47877c7aa821c413b45e05f804819579bdfa1a3 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Tue, 6 Dec 2022 11:36:32 -1000 Subject: [PATCH 1810/2002] rhashtable: Allow rhashtable to be used from irq-safe contexts rhashtable currently only does bh-safe synchronization making it impossible to use from irq-safe contexts. Switch it to use irq-safe synchronization to remove the restriction. v2: Update the lock functions to return the ulong flags value and unlock functions to take the value directly instead of passing around the pointer. Suggested by Linus. Signed-off-by: Tejun Heo Reviewed-by: David Vernet Acked-by: Josh Don Acked-by: Hao Luo Acked-by: Barret Rhoden Cc: Linus Torvalds Signed-off-by: David S. Miller --- include/linux/rhashtable.h | 61 ++++++++++++++++++++++---------------- lib/rhashtable.c | 16 ++++++---- 2 files changed, 46 insertions(+), 31 deletions(-) diff --git a/include/linux/rhashtable.h b/include/linux/rhashtable.h index 68dab3e08aad..5b5357c0bd8c 100644 --- a/include/linux/rhashtable.h +++ b/include/linux/rhashtable.h @@ -323,29 +323,36 @@ static inline struct rhash_lock_head __rcu **rht_bucket_insert( * When we write to a bucket without unlocking, we use rht_assign_locked(). */ -static inline void rht_lock(struct bucket_table *tbl, - struct rhash_lock_head __rcu **bkt) +static inline unsigned long rht_lock(struct bucket_table *tbl, + struct rhash_lock_head __rcu **bkt) { - local_bh_disable(); + unsigned long flags; + + local_irq_save(flags); bit_spin_lock(0, (unsigned long *)bkt); lock_map_acquire(&tbl->dep_map); + return flags; } -static inline void rht_lock_nested(struct bucket_table *tbl, - struct rhash_lock_head __rcu **bucket, - unsigned int subclass) +static inline unsigned long rht_lock_nested(struct bucket_table *tbl, + struct rhash_lock_head __rcu **bucket, + unsigned int subclass) { - local_bh_disable(); + unsigned long flags; + + local_irq_save(flags); bit_spin_lock(0, (unsigned long *)bucket); lock_acquire_exclusive(&tbl->dep_map, subclass, 0, NULL, _THIS_IP_); + return flags; } static inline void rht_unlock(struct bucket_table *tbl, - struct rhash_lock_head __rcu **bkt) + struct rhash_lock_head __rcu **bkt, + unsigned long flags) { lock_map_release(&tbl->dep_map); bit_spin_unlock(0, (unsigned long *)bkt); - local_bh_enable(); + local_irq_restore(flags); } static inline struct rhash_head *__rht_ptr( @@ -393,7 +400,8 @@ static inline void rht_assign_locked(struct rhash_lock_head __rcu **bkt, static inline void rht_assign_unlock(struct bucket_table *tbl, struct rhash_lock_head __rcu **bkt, - struct rhash_head *obj) + struct rhash_head *obj, + unsigned long flags) { if (rht_is_a_nulls(obj)) obj = NULL; @@ -401,7 +409,7 @@ static inline void rht_assign_unlock(struct bucket_table *tbl, rcu_assign_pointer(*bkt, (void *)obj); preempt_enable(); __release(bitlock); - local_bh_enable(); + local_irq_restore(flags); } /** @@ -706,6 +714,7 @@ static inline void *__rhashtable_insert_fast( struct rhash_head __rcu **pprev; struct bucket_table *tbl; struct rhash_head *head; + unsigned long flags; unsigned int hash; int elasticity; void *data; @@ -720,11 +729,11 @@ static inline void *__rhashtable_insert_fast( if (!bkt) goto out; pprev = NULL; - rht_lock(tbl, bkt); + flags = rht_lock(tbl, bkt); if (unlikely(rcu_access_pointer(tbl->future_tbl))) { slow_path: - rht_unlock(tbl, bkt); + rht_unlock(tbl, bkt, flags); rcu_read_unlock(); return rhashtable_insert_slow(ht, key, obj); } @@ -756,9 +765,9 @@ slow_path: RCU_INIT_POINTER(list->rhead.next, head); if (pprev) { rcu_assign_pointer(*pprev, obj); - rht_unlock(tbl, bkt); + rht_unlock(tbl, bkt, flags); } else - rht_assign_unlock(tbl, bkt, obj); + rht_assign_unlock(tbl, bkt, obj, flags); data = NULL; goto out; } @@ -785,7 +794,7 @@ slow_path: } atomic_inc(&ht->nelems); - rht_assign_unlock(tbl, bkt, obj); + rht_assign_unlock(tbl, bkt, obj, flags); if (rht_grow_above_75(ht, tbl)) schedule_work(&ht->run_work); @@ -797,7 +806,7 @@ out: return data; out_unlock: - rht_unlock(tbl, bkt); + rht_unlock(tbl, bkt, flags); goto out; } @@ -991,6 +1000,7 @@ static inline int __rhashtable_remove_fast_one( struct rhash_lock_head __rcu **bkt; struct rhash_head __rcu **pprev; struct rhash_head *he; + unsigned long flags; unsigned int hash; int err = -ENOENT; @@ -999,7 +1009,7 @@ static inline int __rhashtable_remove_fast_one( if (!bkt) return -ENOENT; pprev = NULL; - rht_lock(tbl, bkt); + flags = rht_lock(tbl, bkt); rht_for_each_from(he, rht_ptr(bkt, tbl, hash), tbl, hash) { struct rhlist_head *list; @@ -1043,14 +1053,14 @@ static inline int __rhashtable_remove_fast_one( if (pprev) { rcu_assign_pointer(*pprev, obj); - rht_unlock(tbl, bkt); + rht_unlock(tbl, bkt, flags); } else { - rht_assign_unlock(tbl, bkt, obj); + rht_assign_unlock(tbl, bkt, obj, flags); } goto unlocked; } - rht_unlock(tbl, bkt); + rht_unlock(tbl, bkt, flags); unlocked: if (err > 0) { atomic_dec(&ht->nelems); @@ -1143,6 +1153,7 @@ static inline int __rhashtable_replace_fast( struct rhash_lock_head __rcu **bkt; struct rhash_head __rcu **pprev; struct rhash_head *he; + unsigned long flags; unsigned int hash; int err = -ENOENT; @@ -1158,7 +1169,7 @@ static inline int __rhashtable_replace_fast( return -ENOENT; pprev = NULL; - rht_lock(tbl, bkt); + flags = rht_lock(tbl, bkt); rht_for_each_from(he, rht_ptr(bkt, tbl, hash), tbl, hash) { if (he != obj_old) { @@ -1169,15 +1180,15 @@ static inline int __rhashtable_replace_fast( rcu_assign_pointer(obj_new->next, obj_old->next); if (pprev) { rcu_assign_pointer(*pprev, obj_new); - rht_unlock(tbl, bkt); + rht_unlock(tbl, bkt, flags); } else { - rht_assign_unlock(tbl, bkt, obj_new); + rht_assign_unlock(tbl, bkt, obj_new, flags); } err = 0; goto unlocked; } - rht_unlock(tbl, bkt); + rht_unlock(tbl, bkt, flags); unlocked: return err; diff --git a/lib/rhashtable.c b/lib/rhashtable.c index e12bbfb240b8..6ae2ba8e06a2 100644 --- a/lib/rhashtable.c +++ b/lib/rhashtable.c @@ -231,6 +231,7 @@ static int rhashtable_rehash_one(struct rhashtable *ht, struct rhash_head *head, *next, *entry; struct rhash_head __rcu **pprev = NULL; unsigned int new_hash; + unsigned long flags; if (new_tbl->nest) goto out; @@ -253,13 +254,14 @@ static int rhashtable_rehash_one(struct rhashtable *ht, new_hash = head_hashfn(ht, new_tbl, entry); - rht_lock_nested(new_tbl, &new_tbl->buckets[new_hash], SINGLE_DEPTH_NESTING); + flags = rht_lock_nested(new_tbl, &new_tbl->buckets[new_hash], + SINGLE_DEPTH_NESTING); head = rht_ptr(new_tbl->buckets + new_hash, new_tbl, new_hash); RCU_INIT_POINTER(entry->next, head); - rht_assign_unlock(new_tbl, &new_tbl->buckets[new_hash], entry); + rht_assign_unlock(new_tbl, &new_tbl->buckets[new_hash], entry, flags); if (pprev) rcu_assign_pointer(*pprev, next); @@ -276,18 +278,19 @@ static int rhashtable_rehash_chain(struct rhashtable *ht, { struct bucket_table *old_tbl = rht_dereference(ht->tbl, ht); struct rhash_lock_head __rcu **bkt = rht_bucket_var(old_tbl, old_hash); + unsigned long flags; int err; if (!bkt) return 0; - rht_lock(old_tbl, bkt); + flags = rht_lock(old_tbl, bkt); while (!(err = rhashtable_rehash_one(ht, bkt, old_hash))) ; if (err == -ENOENT) err = 0; - rht_unlock(old_tbl, bkt); + rht_unlock(old_tbl, bkt, flags); return err; } @@ -590,6 +593,7 @@ static void *rhashtable_try_insert(struct rhashtable *ht, const void *key, struct bucket_table *new_tbl; struct bucket_table *tbl; struct rhash_lock_head __rcu **bkt; + unsigned long flags; unsigned int hash; void *data; @@ -607,7 +611,7 @@ static void *rhashtable_try_insert(struct rhashtable *ht, const void *key, new_tbl = rht_dereference_rcu(tbl->future_tbl, ht); data = ERR_PTR(-EAGAIN); } else { - rht_lock(tbl, bkt); + flags = rht_lock(tbl, bkt); data = rhashtable_lookup_one(ht, bkt, tbl, hash, key, obj); new_tbl = rhashtable_insert_one(ht, bkt, tbl, @@ -615,7 +619,7 @@ static void *rhashtable_try_insert(struct rhashtable *ht, const void *key, if (PTR_ERR(new_tbl) != -EEXIST) data = ERR_CAST(new_tbl); - rht_unlock(tbl, bkt); + rht_unlock(tbl, bkt, flags); } } while (!IS_ERR_OR_NULL(new_tbl)); From 1933ea365aa7a48ce26bea2ea09c9f7cc48cc668 Mon Sep 17 00:00:00 2001 From: wangchuanlei Date: Tue, 6 Dec 2022 20:38:57 -0500 Subject: [PATCH 1811/2002] net: openvswitch: Add support to count upcall packets Add support to count upall packets, when kmod of openvswitch upcall to count the number of packets for upcall succeed and failed, which is a better way to see how many packets upcalled on every interfaces. Signed-off-by: wangchuanlei Acked-by: Eelco Chaudron Signed-off-by: David S. Miller --- include/uapi/linux/openvswitch.h | 14 +++++++++ net/openvswitch/datapath.c | 41 ++++++++++++++++++++++++++ net/openvswitch/vport.c | 50 ++++++++++++++++++++++++++++++++ net/openvswitch/vport.h | 16 ++++++++++ 4 files changed, 121 insertions(+) diff --git a/include/uapi/linux/openvswitch.h b/include/uapi/linux/openvswitch.h index 94066f87e9ee..c5d62ee82567 100644 --- a/include/uapi/linux/openvswitch.h +++ b/include/uapi/linux/openvswitch.h @@ -277,11 +277,25 @@ enum ovs_vport_attr { OVS_VPORT_ATTR_PAD, OVS_VPORT_ATTR_IFINDEX, OVS_VPORT_ATTR_NETNSID, + OVS_VPORT_ATTR_UPCALL_STATS, __OVS_VPORT_ATTR_MAX }; #define OVS_VPORT_ATTR_MAX (__OVS_VPORT_ATTR_MAX - 1) +/** + * enum ovs_vport_upcall_attr - attributes for %OVS_VPORT_UPCALL* commands + * @OVS_VPORT_UPCALL_SUCCESS: 64-bit upcall success packets. + * @OVS_VPORT_UPCALL_FAIL: 64-bit upcall fail packets. + */ +enum ovs_vport_upcall_attr { + OVS_VPORT_UPCALL_ATTR_SUCCESS, + OVS_VPORT_UPCALL_ATTR_FAIL, + __OVS_VPORT_UPCALL_ATTR_MAX +}; + +#define OVS_VPORT_UPCALL_ATTR_MAX (__OVS_VPORT_UPCALL_ATTR_MAX - 1) + enum { OVS_VXLAN_EXT_UNSPEC, OVS_VXLAN_EXT_GBP, /* Flag or __u32 */ diff --git a/net/openvswitch/datapath.c b/net/openvswitch/datapath.c index 861dfb8daf4a..932bcf766d63 100644 --- a/net/openvswitch/datapath.c +++ b/net/openvswitch/datapath.c @@ -209,6 +209,26 @@ static struct vport *new_vport(const struct vport_parms *parms) return vport; } +static void ovs_vport_update_upcall_stats(struct sk_buff *skb, + const struct dp_upcall_info *upcall_info, + bool upcall_result) +{ + struct vport *p = OVS_CB(skb)->input_vport; + struct vport_upcall_stats_percpu *stats; + + if (upcall_info->cmd != OVS_PACKET_CMD_MISS && + upcall_info->cmd != OVS_PACKET_CMD_ACTION) + return; + + stats = this_cpu_ptr(p->upcall_stats); + u64_stats_update_begin(&stats->syncp); + if (upcall_result) + u64_stats_inc(&stats->n_success); + else + u64_stats_inc(&stats->n_fail); + u64_stats_update_end(&stats->syncp); +} + void ovs_dp_detach_port(struct vport *p) { ASSERT_OVSL(); @@ -216,6 +236,9 @@ void ovs_dp_detach_port(struct vport *p) /* First drop references to device. */ hlist_del_rcu(&p->dp_hash_node); + /* Free percpu memory */ + free_percpu(p->upcall_stats); + /* Then destroy it. */ ovs_vport_del(p); } @@ -305,6 +328,8 @@ int ovs_dp_upcall(struct datapath *dp, struct sk_buff *skb, err = queue_userspace_packet(dp, skb, key, upcall_info, cutlen); else err = queue_gso_packets(dp, skb, key, upcall_info, cutlen); + + ovs_vport_update_upcall_stats(skb, upcall_info, !err); if (err) goto err; @@ -1826,6 +1851,12 @@ static int ovs_dp_cmd_new(struct sk_buff *skb, struct genl_info *info) goto err_destroy_portids; } + vport->upcall_stats = netdev_alloc_pcpu_stats(struct vport_upcall_stats_percpu); + if (!vport->upcall_stats) { + err = -ENOMEM; + goto err_destroy_portids; + } + err = ovs_dp_cmd_fill_info(dp, reply, info->snd_portid, info->snd_seq, 0, OVS_DP_CMD_NEW); BUG_ON(err < 0); @@ -2098,6 +2129,9 @@ static int ovs_vport_cmd_fill_info(struct vport *vport, struct sk_buff *skb, OVS_VPORT_ATTR_PAD)) goto nla_put_failure; + if (ovs_vport_get_upcall_stats(vport, skb)) + goto nla_put_failure; + if (ovs_vport_get_upcall_portids(vport, skb)) goto nla_put_failure; @@ -2279,6 +2313,12 @@ restart: goto exit_unlock_free; } + vport->upcall_stats = netdev_alloc_pcpu_stats(struct vport_upcall_stats_percpu); + if (!vport->upcall_stats) { + err = -ENOMEM; + goto exit_unlock_free; + } + err = ovs_vport_cmd_fill_info(vport, reply, genl_info_net(info), info->snd_portid, info->snd_seq, 0, OVS_VPORT_CMD_NEW, GFP_KERNEL); @@ -2508,6 +2548,7 @@ static const struct nla_policy vport_policy[OVS_VPORT_ATTR_MAX + 1] = { [OVS_VPORT_ATTR_OPTIONS] = { .type = NLA_NESTED }, [OVS_VPORT_ATTR_IFINDEX] = { .type = NLA_U32 }, [OVS_VPORT_ATTR_NETNSID] = { .type = NLA_S32 }, + [OVS_VPORT_ATTR_UPCALL_STATS] = { .type = NLA_NESTED }, }; static const struct genl_small_ops dp_vport_genl_ops[] = { diff --git a/net/openvswitch/vport.c b/net/openvswitch/vport.c index 82a74f998966..7e0f5c45b512 100644 --- a/net/openvswitch/vport.c +++ b/net/openvswitch/vport.c @@ -284,6 +284,56 @@ void ovs_vport_get_stats(struct vport *vport, struct ovs_vport_stats *stats) stats->tx_packets = dev_stats->tx_packets; } +/** + * ovs_vport_get_upcall_stats - retrieve upcall stats + * + * @vport: vport from which to retrieve the stats. + * @skb: sk_buff where upcall stats should be appended. + * + * Retrieves upcall stats for the given device. + * + * Must be called with ovs_mutex or rcu_read_lock. + */ +int ovs_vport_get_upcall_stats(struct vport *vport, struct sk_buff *skb) +{ + struct nlattr *nla; + int i; + + __u64 tx_success = 0; + __u64 tx_fail = 0; + + for_each_possible_cpu(i) { + const struct vport_upcall_stats_percpu *stats; + unsigned int start; + + stats = per_cpu_ptr(vport->upcall_stats, i); + do { + start = u64_stats_fetch_begin(&stats->syncp); + tx_success += u64_stats_read(&stats->n_success); + tx_fail += u64_stats_read(&stats->n_fail); + } while (u64_stats_fetch_retry(&stats->syncp, start)); + } + + nla = nla_nest_start_noflag(skb, OVS_VPORT_ATTR_UPCALL_STATS); + if (!nla) + return -EMSGSIZE; + + if (nla_put_u64_64bit(skb, OVS_VPORT_UPCALL_ATTR_SUCCESS, tx_success, + OVS_VPORT_ATTR_PAD)) { + nla_nest_cancel(skb, nla); + return -EMSGSIZE; + } + + if (nla_put_u64_64bit(skb, OVS_VPORT_UPCALL_ATTR_FAIL, tx_fail, + OVS_VPORT_ATTR_PAD)) { + nla_nest_cancel(skb, nla); + return -EMSGSIZE; + } + nla_nest_end(skb, nla); + + return 0; +} + /** * ovs_vport_get_options - retrieve device options * diff --git a/net/openvswitch/vport.h b/net/openvswitch/vport.h index 6ff45e8a0868..3e71ca8ad8a7 100644 --- a/net/openvswitch/vport.h +++ b/net/openvswitch/vport.h @@ -32,6 +32,8 @@ struct vport *ovs_vport_locate(const struct net *net, const char *name); void ovs_vport_get_stats(struct vport *, struct ovs_vport_stats *); +int ovs_vport_get_upcall_stats(struct vport *vport, struct sk_buff *skb); + int ovs_vport_set_options(struct vport *, struct nlattr *options); int ovs_vport_get_options(const struct vport *, struct sk_buff *); @@ -65,6 +67,7 @@ struct vport_portids { * @hash_node: Element in @dev_table hash table in vport.c. * @dp_hash_node: Element in @datapath->ports hash table in datapath.c. * @ops: Class structure. + * @upcall_stats: Upcall stats of every ports. * @detach_list: list used for detaching vport in net-exit call. * @rcu: RCU callback head for deferred destruction. */ @@ -78,6 +81,7 @@ struct vport { struct hlist_node hash_node; struct hlist_node dp_hash_node; const struct vport_ops *ops; + struct vport_upcall_stats_percpu __percpu *upcall_stats; struct list_head detach_list; struct rcu_head rcu; @@ -137,6 +141,18 @@ struct vport_ops { struct list_head list; }; +/** + * struct vport_upcall_stats_percpu - per-cpu packet upcall statistics for + * a given vport. + * @n_success: Number of packets that upcall to userspace succeed. + * @n_fail: Number of packets that upcall to userspace failed. + */ +struct vport_upcall_stats_percpu { + struct u64_stats_sync syncp; + u64_stats_t n_success; + u64_stats_t n_fail; +}; + struct vport *ovs_vport_alloc(int priv_size, const struct vport_ops *, const struct vport_parms *); void ovs_vport_free(struct vport *); From ae18dcdff0f8d7e84cd3fd9f496518b5e72d185d Mon Sep 17 00:00:00 2001 From: Yongqiang Liu Date: Wed, 7 Dec 2022 07:20:45 +0000 Subject: [PATCH 1812/2002] net: defxx: Fix missing err handling in dfx_init() When eisa_driver_register() or tc_register_driver() failed, the modprobe defxx would fail with some err log as follows: Error: Driver 'defxx' is already registered, aborting... Fix this issue by adding err hanling in dfx_init(). Fixes: e89a2cfb7d7b5 ("[TC] defxx: TURBOchannel support") Signed-off-by: Yongqiang Liu Reviewed-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/fddi/defxx.c | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/drivers/net/fddi/defxx.c b/drivers/net/fddi/defxx.c index b584ffe38ad6..1fef8a9b1a0f 100644 --- a/drivers/net/fddi/defxx.c +++ b/drivers/net/fddi/defxx.c @@ -3831,10 +3831,24 @@ static int dfx_init(void) int status; status = pci_register_driver(&dfx_pci_driver); - if (!status) - status = eisa_driver_register(&dfx_eisa_driver); - if (!status) - status = tc_register_driver(&dfx_tc_driver); + if (status) + goto err_pci_register; + + status = eisa_driver_register(&dfx_eisa_driver); + if (status) + goto err_eisa_register; + + status = tc_register_driver(&dfx_tc_driver); + if (status) + goto err_tc_register; + + return 0; + +err_tc_register: + eisa_driver_unregister(&dfx_eisa_driver); +err_eisa_register: + pci_unregister_driver(&dfx_pci_driver); +err_pci_register: return status; } From f150b63f3fa5fdd81e0dd6151e8850268e29438c Mon Sep 17 00:00:00 2001 From: Zhang Changzhong Date: Wed, 7 Dec 2022 16:31:59 +0800 Subject: [PATCH 1813/2002] net: stmmac: selftests: fix potential memleak in stmmac_test_arpoffload() The skb allocated by stmmac_test_get_arp_skb() hasn't been released in some error handling case, which will lead to a memory leak. Fix this up by adding kfree_skb() to release skb. Compile tested only. Fixes: 5e3fb0a6e2b3 ("net: stmmac: selftests: Implement the ARP Offload test") Signed-off-by: Zhang Changzhong Signed-off-by: David S. Miller --- drivers/net/ethernet/stmicro/stmmac/stmmac_selftests.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_selftests.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_selftests.c index 49af7e78b7f5..687f43cd466c 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_selftests.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_selftests.c @@ -1654,12 +1654,16 @@ static int stmmac_test_arpoffload(struct stmmac_priv *priv) } ret = stmmac_set_arp_offload(priv, priv->hw, true, ip_addr); - if (ret) + if (ret) { + kfree_skb(skb); goto cleanup; + } ret = dev_set_promiscuity(priv->dev, 1); - if (ret) + if (ret) { + kfree_skb(skb); goto cleanup; + } ret = dev_direct_xmit(skb, 0); if (ret) From a137f3f27f9290933fe7e40e6dc8a445781c31a2 Mon Sep 17 00:00:00 2001 From: Gaosheng Cui Date: Wed, 7 Dec 2022 16:34:13 +0800 Subject: [PATCH 1814/2002] net: stmmac: fix possible memory leak in stmmac_dvr_probe() The bitmap_free() should be called to free priv->af_xdp_zc_qps when create_singlethread_workqueue() fails, otherwise there will be a memory leak, so we add the err path error_wq_init to fix it. Fixes: bba2556efad6 ("net: stmmac: Enable RX via AF_XDP zero-copy") Signed-off-by: Gaosheng Cui Reviewed-by: Leon Romanovsky Signed-off-by: David S. Miller --- drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index 23ec0a9e396c..b978d057c5f4 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -7097,7 +7097,7 @@ int stmmac_dvr_probe(struct device *device, priv->wq = create_singlethread_workqueue("stmmac_wq"); if (!priv->wq) { dev_err(priv->device, "failed to create workqueue\n"); - return -ENOMEM; + goto error_wq_init; } INIT_WORK(&priv->service_task, stmmac_service_task); @@ -7325,6 +7325,7 @@ error_mdio_register: stmmac_napi_del(ndev); error_hw_init: destroy_workqueue(priv->wq); +error_wq_init: bitmap_free(priv->af_xdp_zc_qps); return ret; From 01de1123322e4fe1bbd0fcdf0982511b55519c03 Mon Sep 17 00:00:00 2001 From: Yuan Can Date: Wed, 7 Dec 2022 08:54:10 +0000 Subject: [PATCH 1815/2002] drivers: net: qlcnic: Fix potential memory leak in qlcnic_sriov_init() If vp alloc failed in qlcnic_sriov_init(), all previously allocated vp needs to be freed. Fixes: f197a7aa6288 ("qlcnic: VF-PF communication channel implementation") Signed-off-by: Yuan Can Reviewed-by: Leon Romanovsky Signed-off-by: David S. Miller --- drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_common.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_common.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_common.c index 9282321c2e7f..f9dd50152b1e 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_common.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_common.c @@ -221,6 +221,8 @@ int qlcnic_sriov_init(struct qlcnic_adapter *adapter, int num_vfs) return 0; qlcnic_destroy_async_wq: + while (i--) + kfree(sriov->vf_info[i].vp); destroy_workqueue(bc->bc_async_wq); qlcnic_destroy_trans_wq: From f3212ad5b7e93c002bd2dbe552c2b0b0033317ff Mon Sep 17 00:00:00 2001 From: Donald Hunter Date: Fri, 9 Dec 2022 11:24:01 +0000 Subject: [PATCH 1816/2002] docs/bpf: Add documentation for BPF_MAP_TYPE_SK_STORAGE Add documentation for the BPF_MAP_TYPE_SK_STORAGE including kernel version introduced, usage and examples. Signed-off-by: Donald Hunter Acked-by: David Vernet Link: https://lore.kernel.org/r/20221209112401.69319-1-donald.hunter@gmail.com Signed-off-by: Alexei Starovoitov --- Documentation/bpf/map_sk_storage.rst | 155 +++++++++++++++++++++++++++ 1 file changed, 155 insertions(+) create mode 100644 Documentation/bpf/map_sk_storage.rst diff --git a/Documentation/bpf/map_sk_storage.rst b/Documentation/bpf/map_sk_storage.rst new file mode 100644 index 000000000000..047e16c8aaa8 --- /dev/null +++ b/Documentation/bpf/map_sk_storage.rst @@ -0,0 +1,155 @@ +.. SPDX-License-Identifier: GPL-2.0-only +.. Copyright (C) 2022 Red Hat, Inc. + +======================= +BPF_MAP_TYPE_SK_STORAGE +======================= + +.. note:: + - ``BPF_MAP_TYPE_SK_STORAGE`` was introduced in kernel version 5.2 + +``BPF_MAP_TYPE_SK_STORAGE`` is used to provide socket-local storage for BPF +programs. A map of type ``BPF_MAP_TYPE_SK_STORAGE`` declares the type of storage +to be provided and acts as the handle for accessing the socket-local +storage. The values for maps of type ``BPF_MAP_TYPE_SK_STORAGE`` are stored +locally with each socket instead of with the map. The kernel is responsible for +allocating storage for a socket when requested and for freeing the storage when +either the map or the socket is deleted. + +.. note:: + - The key type must be ``int`` and ``max_entries`` must be set to ``0``. + - The ``BPF_F_NO_PREALLOC`` flag must be used when creating a map for + socket-local storage. + +Usage +===== + +Kernel BPF +---------- + +bpf_sk_storage_get() +~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: c + + void *bpf_sk_storage_get(struct bpf_map *map, void *sk, void *value, u64 flags) + +Socket-local storage can be retrieved using the ``bpf_sk_storage_get()`` +helper. The helper gets the storage from ``sk`` that is associated with ``map``. +If the ``BPF_LOCAL_STORAGE_GET_F_CREATE`` flag is used then +``bpf_sk_storage_get()`` will create the storage for ``sk`` if it does not +already exist. ``value`` can be used together with +``BPF_LOCAL_STORAGE_GET_F_CREATE`` to initialize the storage value, otherwise it +will be zero initialized. Returns a pointer to the storage on success, or +``NULL`` in case of failure. + +.. note:: + - ``sk`` is a kernel ``struct sock`` pointer for LSM or tracing programs. + - ``sk`` is a ``struct bpf_sock`` pointer for other program types. + +bpf_sk_storage_delete() +~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: c + + long bpf_sk_storage_delete(struct bpf_map *map, void *sk) + +Socket-local storage can be deleted using the ``bpf_sk_storage_delete()`` +helper. The helper deletes the storage from ``sk`` that is identified by +``map``. Returns ``0`` on success, or negative error in case of failure. + +User space +---------- + +bpf_map_update_elem() +~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: c + + int bpf_map_update_elem(int map_fd, const void *key, const void *value, __u64 flags) + +Socket-local storage for the socket identified by ``key`` belonging to +``map_fd`` can be added or updated using the ``bpf_map_update_elem()`` libbpf +function. ``key`` must be a pointer to a valid ``fd`` in the user space +program. The ``flags`` parameter can be used to control the update behaviour: + +- ``BPF_ANY`` will create storage for ``fd`` or update existing storage. +- ``BPF_NOEXIST`` will create storage for ``fd`` only if it did not already + exist, otherwise the call will fail with ``-EEXIST``. +- ``BPF_EXIST`` will update existing storage for ``fd`` if it already exists, + otherwise the call will fail with ``-ENOENT``. + +Returns ``0`` on success, or negative error in case of failure. + +bpf_map_lookup_elem() +~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: c + + int bpf_map_lookup_elem(int map_fd, const void *key, void *value) + +Socket-local storage for the socket identified by ``key`` belonging to +``map_fd`` can be retrieved using the ``bpf_map_lookup_elem()`` libbpf +function. ``key`` must be a pointer to a valid ``fd`` in the user space +program. Returns ``0`` on success, or negative error in case of failure. + +bpf_map_delete_elem() +~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: c + + int bpf_map_delete_elem(int map_fd, const void *key) + +Socket-local storage for the socket identified by ``key`` belonging to +``map_fd`` can be deleted using the ``bpf_map_delete_elem()`` libbpf +function. Returns ``0`` on success, or negative error in case of failure. + +Examples +======== + +Kernel BPF +---------- + +This snippet shows how to declare socket-local storage in a BPF program: + +.. code-block:: c + + struct { + __uint(type, BPF_MAP_TYPE_SK_STORAGE); + __uint(map_flags, BPF_F_NO_PREALLOC); + __type(key, int); + __type(value, struct my_storage); + } socket_storage SEC(".maps"); + +This snippet shows how to retrieve socket-local storage in a BPF program: + +.. code-block:: c + + SEC("sockops") + int _sockops(struct bpf_sock_ops *ctx) + { + struct my_storage *storage; + struct bpf_sock *sk; + + sk = ctx->sk; + if (!sk) + return 1; + + storage = bpf_sk_storage_get(&socket_storage, sk, 0, + BPF_LOCAL_STORAGE_GET_F_CREATE); + if (!storage) + return 1; + + /* Use 'storage' here */ + + return 1; + } + + +Please see the ``tools/testing/selftests/bpf`` directory for functional +examples. + +References +========== + +https://lwn.net/ml/netdev/20190426171103.61892-1-kafai@fb.com/ From 8b34b52c1794932a16308631121b758830349ae9 Mon Sep 17 00:00:00 2001 From: Geliang Tang Date: Thu, 8 Dec 2022 16:44:30 -0800 Subject: [PATCH 1817/2002] mptcp: use nlmsg_free instead of kfree_skb Use nlmsg_free() instead of kfree_skb() in pm_netlink.c. The SKB's have been created by nlmsg_new(). The proper cleaning way should then be done with nlmsg_free(). For the moment, nlmsg_free() is simply calling kfree_skb() so we don't change the behaviour here. Suggested-by: Jakub Kicinski Reviewed-by: Matthieu Baerts Signed-off-by: Geliang Tang Signed-off-by: Mat Martineau Signed-off-by: Jakub Kicinski --- net/mptcp/pm_netlink.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/net/mptcp/pm_netlink.c b/net/mptcp/pm_netlink.c index eef69d0e44ec..08c65f3e70a3 100644 --- a/net/mptcp/pm_netlink.c +++ b/net/mptcp/pm_netlink.c @@ -2094,7 +2094,7 @@ void mptcp_event_addr_removed(const struct mptcp_sock *msk, uint8_t id) return; nla_put_failure: - kfree_skb(skb); + nlmsg_free(skb); } void mptcp_event_addr_announced(const struct sock *ssk, @@ -2151,7 +2151,7 @@ void mptcp_event_addr_announced(const struct sock *ssk, return; nla_put_failure: - kfree_skb(skb); + nlmsg_free(skb); } void mptcp_event_pm_listener(const struct sock *ssk, @@ -2203,7 +2203,7 @@ void mptcp_event_pm_listener(const struct sock *ssk, return; nla_put_failure: - kfree_skb(skb); + nlmsg_free(skb); } void mptcp_event(enum mptcp_event_type type, const struct mptcp_sock *msk, @@ -2261,7 +2261,7 @@ void mptcp_event(enum mptcp_event_type type, const struct mptcp_sock *msk, return; nla_put_failure: - kfree_skb(skb); + nlmsg_free(skb); } static const struct genl_small_ops mptcp_pm_ops[] = { From 03e7d28cd25ee6ff731386fb154d03f76dede1cc Mon Sep 17 00:00:00 2001 From: Matthieu Baerts Date: Thu, 8 Dec 2022 16:44:31 -0800 Subject: [PATCH 1818/2002] mptcp: return 0 instead of 'err' var When 'err' is 0, it looks clearer to return '0' instead of the variable called 'err'. The behaviour is then not modified, just a clearer code. By doing this, we can also avoid false positive smatch warnings like this one: net/mptcp/pm_netlink.c:1169 mptcp_pm_parse_pm_addr_attr() warn: missing error code? 'err' Reported-by: kernel test robot Reported-by: Dan Carpenter Suggested-by: Mat Martineau Reviewed-by: Mat Martineau Signed-off-by: Matthieu Baerts Signed-off-by: Mat Martineau Signed-off-by: Jakub Kicinski --- net/mptcp/pm_netlink.c | 4 ++-- net/mptcp/sockopt.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/net/mptcp/pm_netlink.c b/net/mptcp/pm_netlink.c index 08c65f3e70a3..2ea7eae43bdb 100644 --- a/net/mptcp/pm_netlink.c +++ b/net/mptcp/pm_netlink.c @@ -1190,7 +1190,7 @@ static int mptcp_pm_parse_pm_addr_attr(struct nlattr *tb[], if (!tb[MPTCP_PM_ADDR_ATTR_FAMILY]) { if (!require_family) - return err; + return 0; NL_SET_ERR_MSG_ATTR(info->extack, attr, "missing family"); @@ -1224,7 +1224,7 @@ static int mptcp_pm_parse_pm_addr_attr(struct nlattr *tb[], if (tb[MPTCP_PM_ADDR_ATTR_PORT]) addr->port = htons(nla_get_u16(tb[MPTCP_PM_ADDR_ATTR_PORT])); - return err; + return 0; } int mptcp_pm_parse_addr(struct nlattr *attr, struct genl_info *info, diff --git a/net/mptcp/sockopt.c b/net/mptcp/sockopt.c index a47423ebb33a..d4b1e6ec1b36 100644 --- a/net/mptcp/sockopt.c +++ b/net/mptcp/sockopt.c @@ -740,7 +740,7 @@ static int mptcp_setsockopt_v4_set_tos(struct mptcp_sock *msk, int optname, } release_sock(sk); - return err; + return 0; } static int mptcp_setsockopt_v4(struct mptcp_sock *msk, int optname, From 28d39503e4e06c2caf09a89865c81cfd9e4eae7c Mon Sep 17 00:00:00 2001 From: Jiapeng Chong Date: Fri, 9 Dec 2022 11:37:23 +0800 Subject: [PATCH 1819/2002] net: bcmgenet: Remove the unused function The function dmadesc_get_addr() is defined in the bcmgenet.c file, but not called elsewhere, so remove this unused function. drivers/net/ethernet/broadcom/genet/bcmgenet.c:120:26: warning: unused function 'dmadesc_get_addr'. Link: https://bugzilla.openanolis.cn/show_bug.cgi?id=3401 Reported-by: Abaci Robot Signed-off-by: Jiapeng Chong Link: https://lore.kernel.org/r/20221209033723.32452-1-jiapeng.chong@linux.alibaba.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/broadcom/genet/bcmgenet.c | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/drivers/net/ethernet/broadcom/genet/bcmgenet.c b/drivers/net/ethernet/broadcom/genet/bcmgenet.c index a8ce8d0cf9c4..21973046b12b 100644 --- a/drivers/net/ethernet/broadcom/genet/bcmgenet.c +++ b/drivers/net/ethernet/broadcom/genet/bcmgenet.c @@ -117,24 +117,6 @@ static inline void dmadesc_set(struct bcmgenet_priv *priv, dmadesc_set_length_status(priv, d, val); } -static inline dma_addr_t dmadesc_get_addr(struct bcmgenet_priv *priv, - void __iomem *d) -{ - dma_addr_t addr; - - addr = bcmgenet_readl(d + DMA_DESC_ADDRESS_LO); - - /* Register writes to GISB bus can take couple hundred nanoseconds - * and are done for each packet, save these expensive writes unless - * the platform is explicitly configured for 64-bits/LPAE. - */ -#ifdef CONFIG_PHYS_ADDR_T_64BIT - if (priv->hw_params->flags & GENET_HAS_40BITS) - addr |= (u64)bcmgenet_readl(d + DMA_DESC_ADDRESS_HI) << 32; -#endif - return addr; -} - #define GENET_VER_FMT "%1d.%1d EPHY: 0x%04x" #define GENET_MSG_DEFAULT (NETIF_MSG_DRV | NETIF_MSG_PROBE | \ From ce098da1497c6dee9589fce2c61d1910f4fcf0e7 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Wed, 7 Dec 2022 22:02:59 -0800 Subject: [PATCH 1820/2002] skbuff: Introduce slab_build_skb() syzkaller reported: BUG: KASAN: slab-out-of-bounds in __build_skb_around+0x235/0x340 net/core/skbuff.c:294 Write of size 32 at addr ffff88802aa172c0 by task syz-executor413/5295 For bpf_prog_test_run_skb(), which uses a kmalloc()ed buffer passed to build_skb(). When build_skb() is passed a frag_size of 0, it means the buffer came from kmalloc. In these cases, ksize() is used to find its actual size, but since the allocation may not have been made to that size, actually perform the krealloc() call so that all the associated buffer size checking will be correctly notified (and use the "new" pointer so that compiler hinting works correctly). Split this logic out into a new interface, slab_build_skb(), but leave the original 0 checking for now to catch any stragglers. Reported-by: syzbot+fda18eaa8c12534ccb3b@syzkaller.appspotmail.com Link: https://groups.google.com/g/syzkaller-bugs/c/UnIKxTtU5-0/m/-wbXinkgAQAJ Fixes: 38931d8989b5 ("mm: Make ksize() a reporting-only function") Cc: Pavel Begunkov Cc: pepsipu Cc: syzbot+fda18eaa8c12534ccb3b@syzkaller.appspotmail.com Cc: Vlastimil Babka Cc: kasan-dev Cc: Andrii Nakryiko Cc: ast@kernel.org Cc: Daniel Borkmann Cc: Hao Luo Cc: Jesper Dangaard Brouer Cc: John Fastabend Cc: jolsa@kernel.org Cc: KP Singh Cc: martin.lau@linux.dev Cc: Stanislav Fomichev Cc: song@kernel.org Cc: Yonghong Song Signed-off-by: Kees Cook Link: https://lore.kernel.org/r/20221208060256.give.994-kees@kernel.org Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/broadcom/bnx2.c | 2 +- drivers/net/ethernet/qlogic/qed/qed_ll2.c | 2 +- include/linux/skbuff.h | 1 + net/bpf/test_run.c | 2 +- net/core/skbuff.c | 70 ++++++++++++++++++++--- 5 files changed, 66 insertions(+), 11 deletions(-) diff --git a/drivers/net/ethernet/broadcom/bnx2.c b/drivers/net/ethernet/broadcom/bnx2.c index dbe310144780..9f473854b0f4 100644 --- a/drivers/net/ethernet/broadcom/bnx2.c +++ b/drivers/net/ethernet/broadcom/bnx2.c @@ -3045,7 +3045,7 @@ error: dma_unmap_single(&bp->pdev->dev, dma_addr, bp->rx_buf_use_size, DMA_FROM_DEVICE); - skb = build_skb(data, 0); + skb = slab_build_skb(data); if (!skb) { kfree(data); goto error; diff --git a/drivers/net/ethernet/qlogic/qed/qed_ll2.c b/drivers/net/ethernet/qlogic/qed/qed_ll2.c index ed274f033626..e5116a86cfbc 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_ll2.c +++ b/drivers/net/ethernet/qlogic/qed/qed_ll2.c @@ -200,7 +200,7 @@ static void qed_ll2b_complete_rx_packet(void *cxt, dma_unmap_single(&cdev->pdev->dev, buffer->phys_addr, cdev->ll2->rx_size, DMA_FROM_DEVICE); - skb = build_skb(buffer->data, 0); + skb = slab_build_skb(buffer->data); if (!skb) { DP_INFO(cdev, "Failed to build SKB\n"); kfree(buffer->data); diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index 4e464a27adaf..4c8492401a10 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -1255,6 +1255,7 @@ struct sk_buff *build_skb_around(struct sk_buff *skb, void skb_attempt_defer_free(struct sk_buff *skb); struct sk_buff *napi_build_skb(void *data, unsigned int frag_size); +struct sk_buff *slab_build_skb(void *data); /** * alloc_skb - allocate a network buffer diff --git a/net/bpf/test_run.c b/net/bpf/test_run.c index 6094ef7cffcd..c9bfd263dcef 100644 --- a/net/bpf/test_run.c +++ b/net/bpf/test_run.c @@ -1128,7 +1128,7 @@ int bpf_prog_test_run_skb(struct bpf_prog *prog, const union bpf_attr *kattr, } sock_init_data(NULL, sk); - skb = build_skb(data, 0); + skb = slab_build_skb(data); if (!skb) { kfree(data); kfree(ctx); diff --git a/net/core/skbuff.c b/net/core/skbuff.c index 4bf95e36ed16..3cbba7099c0f 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -270,12 +270,10 @@ static struct sk_buff *napi_skb_cache_get(void) return skb; } -/* Caller must provide SKB that is memset cleared */ -static void __build_skb_around(struct sk_buff *skb, void *data, - unsigned int frag_size) +static inline void __finalize_skb_around(struct sk_buff *skb, void *data, + unsigned int size) { struct skb_shared_info *shinfo; - unsigned int size = frag_size ? : ksize(data); size -= SKB_DATA_ALIGN(sizeof(struct skb_shared_info)); @@ -297,15 +295,71 @@ static void __build_skb_around(struct sk_buff *skb, void *data, skb_set_kcov_handle(skb, kcov_common_handle()); } +static inline void *__slab_build_skb(struct sk_buff *skb, void *data, + unsigned int *size) +{ + void *resized; + + /* Must find the allocation size (and grow it to match). */ + *size = ksize(data); + /* krealloc() will immediately return "data" when + * "ksize(data)" is requested: it is the existing upper + * bounds. As a result, GFP_ATOMIC will be ignored. Note + * that this "new" pointer needs to be passed back to the + * caller for use so the __alloc_size hinting will be + * tracked correctly. + */ + resized = krealloc(data, *size, GFP_ATOMIC); + WARN_ON_ONCE(resized != data); + return resized; +} + +/* build_skb() variant which can operate on slab buffers. + * Note that this should be used sparingly as slab buffers + * cannot be combined efficiently by GRO! + */ +struct sk_buff *slab_build_skb(void *data) +{ + struct sk_buff *skb; + unsigned int size; + + skb = kmem_cache_alloc(skbuff_head_cache, GFP_ATOMIC); + if (unlikely(!skb)) + return NULL; + + memset(skb, 0, offsetof(struct sk_buff, tail)); + data = __slab_build_skb(skb, data, &size); + __finalize_skb_around(skb, data, size); + + return skb; +} +EXPORT_SYMBOL(slab_build_skb); + +/* Caller must provide SKB that is memset cleared */ +static void __build_skb_around(struct sk_buff *skb, void *data, + unsigned int frag_size) +{ + unsigned int size = frag_size; + + /* frag_size == 0 is considered deprecated now. Callers + * using slab buffer should use slab_build_skb() instead. + */ + if (WARN_ONCE(size == 0, "Use slab_build_skb() instead")) + data = __slab_build_skb(skb, data, &size); + + __finalize_skb_around(skb, data, size); +} + /** * __build_skb - build a network buffer * @data: data buffer provided by caller - * @frag_size: size of data, or 0 if head was kmalloced + * @frag_size: size of data (must not be 0) * * Allocate a new &sk_buff. Caller provides space holding head and - * skb_shared_info. @data must have been allocated by kmalloc() only if - * @frag_size is 0, otherwise data should come from the page allocator - * or vmalloc() + * skb_shared_info. @data must have been allocated from the page + * allocator or vmalloc(). (A @frag_size of 0 to indicate a kmalloc() + * allocation is deprecated, and callers should use slab_build_skb() + * instead.) * The return is the new skb buffer. * On a failure the return is %NULL, and @data is not freed. * Notes : From 5fc11a401a8dc491b326d2c916b07d22e7ac8833 Mon Sep 17 00:00:00 2001 From: Gavrilov Ilia Date: Thu, 8 Dec 2022 08:31:39 +0000 Subject: [PATCH 1821/2002] net: devlink: Add missing error check to devlink_resource_put() When the resource size changes, the return value of the 'nla_put_u64_64bit' function is not checked. That has been fixed to avoid rechecking at the next step. Found by InfoTeCS on behalf of Linux Verification Center (linuxtesting.org) with SVACE. Note that this is harmless, we'd error out at the next put(). Signed-off-by: Ilia.Gavrilov Link: https://lore.kernel.org/r/20221208082821.3927937-1-Ilia.Gavrilov@infotecs.ru Signed-off-by: Jakub Kicinski --- net/core/devlink.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/net/core/devlink.c b/net/core/devlink.c index ab40ebcb4aea..6004bd0ccee4 100644 --- a/net/core/devlink.c +++ b/net/core/devlink.c @@ -4441,9 +4441,10 @@ static int devlink_resource_put(struct devlink *devlink, struct sk_buff *skb, nla_put_u64_64bit(skb, DEVLINK_ATTR_RESOURCE_ID, resource->id, DEVLINK_ATTR_PAD)) goto nla_put_failure; - if (resource->size != resource->size_new) - nla_put_u64_64bit(skb, DEVLINK_ATTR_RESOURCE_SIZE_NEW, - resource->size_new, DEVLINK_ATTR_PAD); + if (resource->size != resource->size_new && + nla_put_u64_64bit(skb, DEVLINK_ATTR_RESOURCE_SIZE_NEW, + resource->size_new, DEVLINK_ATTR_PAD)) + goto nla_put_failure; if (devlink_resource_occ_put(resource, skb)) goto nla_put_failure; if (devlink_resource_size_params_put(resource, skb)) From cce2d7d2abcc8d083fbb24e795089542f1e742c8 Mon Sep 17 00:00:00 2001 From: Marc Kleine-Budde Date: Mon, 31 Oct 2022 12:26:24 +0100 Subject: [PATCH 1822/2002] can: kvaser_usb: kvaser_usb_set_bittiming(): fix redundant initialization warning for err The variable err is initialized, but the initialized value is Overwritten before it is read. Fix the warning by not initializing the variable err at all. Fixes: 39d3df6b0ea8 ("can: kvaser_usb: Compare requested bittiming parameters with actual parameters in do_set_{,data}_bittiming") Cc: Jimmy Assarsson Cc: Anssi Hannula Link: https://lore.kernel.org/all/20221031114513.81214-1-mkl@pengutronix.de Signed-off-by: Marc Kleine-Budde --- drivers/net/can/usb/kvaser_usb/kvaser_usb_core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/can/usb/kvaser_usb/kvaser_usb_core.c b/drivers/net/can/usb/kvaser_usb/kvaser_usb_core.c index 3a2bfaad1406..b17c4d8c6cf9 100644 --- a/drivers/net/can/usb/kvaser_usb/kvaser_usb_core.c +++ b/drivers/net/can/usb/kvaser_usb/kvaser_usb_core.c @@ -541,7 +541,7 @@ static int kvaser_usb_set_bittiming(struct net_device *netdev) int tseg1 = bt->prop_seg + bt->phase_seg1; int tseg2 = bt->phase_seg2; int sjw = bt->sjw; - int err = -EOPNOTSUPP; + int err; busparams.bitrate = cpu_to_le32(bt->bitrate); busparams.sjw = (u8)sjw; From 0bf582fc51684dc4380b0b9248112cf9f837a585 Mon Sep 17 00:00:00 2001 From: Marc Kleine-Budde Date: Mon, 31 Oct 2022 12:28:08 +0100 Subject: [PATCH 1823/2002] can: kvaser_usb: kvaser_usb_set_{,data}bittiming(): remove empty lines in variable declaration Fix coding style by removing empty lines in variable declaration. Fixes: 39d3df6b0ea8 ("can: kvaser_usb: Compare requested bittiming parameters with actual parameters in do_set_{,data}_bittiming") Cc: Jimmy Assarsson Cc: Anssi Hannula Link: https://lore.kernel.org/all/20221031114513.81214-2-mkl@pengutronix.de Signed-off-by: Marc Kleine-Budde --- drivers/net/can/usb/kvaser_usb/kvaser_usb_core.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/net/can/usb/kvaser_usb/kvaser_usb_core.c b/drivers/net/can/usb/kvaser_usb/kvaser_usb_core.c index b17c4d8c6cf9..d4c5356d5884 100644 --- a/drivers/net/can/usb/kvaser_usb/kvaser_usb_core.c +++ b/drivers/net/can/usb/kvaser_usb/kvaser_usb_core.c @@ -536,7 +536,6 @@ static int kvaser_usb_set_bittiming(struct net_device *netdev) struct kvaser_usb *dev = priv->dev; const struct kvaser_usb_dev_ops *ops = dev->driver_info->ops; struct can_bittiming *bt = &priv->can.bittiming; - struct kvaser_usb_busparams busparams; int tseg1 = bt->prop_seg + bt->phase_seg1; int tseg2 = bt->phase_seg2; @@ -581,7 +580,6 @@ static int kvaser_usb_set_data_bittiming(struct net_device *netdev) struct kvaser_usb *dev = priv->dev; const struct kvaser_usb_dev_ops *ops = dev->driver_info->ops; struct can_bittiming *dbt = &priv->can.data_bittiming; - struct kvaser_usb_busparams busparams; int tseg1 = dbt->prop_seg + dbt->phase_seg1; int tseg2 = dbt->phase_seg2; From ce7c5382758b13dd4dfa2eb2f4989f9a27c58ffe Mon Sep 17 00:00:00 2001 From: Biju Das Date: Thu, 27 Oct 2022 09:21:53 +0100 Subject: [PATCH 1824/2002] can: rcar_canfd: rcar_canfd_probe: Add struct rcar_canfd_hw_info to driver data The CAN FD IP found on RZ/G2L SoC has some HW features different to that of R-Car. For example, it has multiple resets and multiple IRQs for global and channel interrupts. Also, it does not have ECC error flag registers and clk post divider present on R-Car. Similarly, R-Car V3U has 8 channels whereas other SoCs has only 2 channels. This patch adds the struct rcar_canfd_hw_info to take care of the HW feature differences and driver data present on both IPs. It also replaces the driver data chip type with struct rcar_canfd_hw_info by moving chip type to it. Whilst started using driver data instead of chip_id for detecting R-Car V3U SoCs. Signed-off-by: Biju Das Reviewed-by: Geert Uytterhoeven Link: https://lore.kernel.org/all/20221027082158.95895-2-biju.das.jz@bp.renesas.com Signed-off-by: Marc Kleine-Budde --- drivers/net/can/rcar/rcar_canfd.c | 43 +++++++++++++++++++++---------- 1 file changed, 30 insertions(+), 13 deletions(-) diff --git a/drivers/net/can/rcar/rcar_canfd.c b/drivers/net/can/rcar/rcar_canfd.c index 0a59eab35da7..1a5a013ac5bc 100644 --- a/drivers/net/can/rcar/rcar_canfd.c +++ b/drivers/net/can/rcar/rcar_canfd.c @@ -522,6 +522,10 @@ enum rcar_canfd_fcanclk { struct rcar_canfd_global; +struct rcar_canfd_hw_info { + enum rcanfd_chip_id chip_id; +}; + /* Channel priv data */ struct rcar_canfd_channel { struct can_priv can; /* Must be the first member */ @@ -547,7 +551,7 @@ struct rcar_canfd_global { bool fdmode; /* CAN FD or Classical CAN only mode */ struct reset_control *rstc1; struct reset_control *rstc2; - enum rcanfd_chip_id chip_id; + const struct rcar_canfd_hw_info *info; u32 max_channels; }; @@ -590,10 +594,22 @@ static const struct can_bittiming_const rcar_canfd_bittiming_const = { .brp_inc = 1, }; +static const struct rcar_canfd_hw_info rcar_gen3_hw_info = { + .chip_id = RENESAS_RCAR_GEN3, +}; + +static const struct rcar_canfd_hw_info rzg2l_hw_info = { + .chip_id = RENESAS_RZG2L, +}; + +static const struct rcar_canfd_hw_info r8a779a0_hw_info = { + .chip_id = RENESAS_R8A779A0, +}; + /* Helper functions */ static inline bool is_v3u(struct rcar_canfd_global *gpriv) { - return gpriv->chip_id == RENESAS_R8A779A0; + return gpriv->info == &r8a779a0_hw_info; } static inline u32 reg_v3u(struct rcar_canfd_global *gpriv, @@ -1696,6 +1712,7 @@ static const struct ethtool_ops rcar_canfd_ethtool_ops = { static int rcar_canfd_channel_probe(struct rcar_canfd_global *gpriv, u32 ch, u32 fcan_freq) { + const struct rcar_canfd_hw_info *info = gpriv->info; struct platform_device *pdev = gpriv->pdev; struct rcar_canfd_channel *priv; struct net_device *ndev; @@ -1718,7 +1735,7 @@ static int rcar_canfd_channel_probe(struct rcar_canfd_global *gpriv, u32 ch, priv->can.clock.freq = fcan_freq; dev_info(&pdev->dev, "can_clk rate is %u\n", priv->can.clock.freq); - if (gpriv->chip_id == RENESAS_RZG2L) { + if (info->chip_id == RENESAS_RZG2L) { char *irq_name; int err_irq; int tx_irq; @@ -1818,6 +1835,7 @@ static void rcar_canfd_channel_remove(struct rcar_canfd_global *gpriv, u32 ch) static int rcar_canfd_probe(struct platform_device *pdev) { + const struct rcar_canfd_hw_info *info; void __iomem *addr; u32 sts, ch, fcan_freq; struct rcar_canfd_global *gpriv; @@ -1826,13 +1844,12 @@ static int rcar_canfd_probe(struct platform_device *pdev) int err, ch_irq, g_irq; int g_err_irq, g_recc_irq; bool fdmode = true; /* CAN FD only mode - default */ - enum rcanfd_chip_id chip_id; int max_channels; char name[9] = "channelX"; int i; - chip_id = (uintptr_t)of_device_get_match_data(&pdev->dev); - max_channels = chip_id == RENESAS_R8A779A0 ? 8 : 2; + info = of_device_get_match_data(&pdev->dev); + max_channels = info->chip_id == RENESAS_R8A779A0 ? 8 : 2; if (of_property_read_bool(pdev->dev.of_node, "renesas,no-can-fd")) fdmode = false; /* Classical CAN only mode */ @@ -1845,7 +1862,7 @@ static int rcar_canfd_probe(struct platform_device *pdev) of_node_put(of_child); } - if (chip_id != RENESAS_RZG2L) { + if (info->chip_id != RENESAS_RZG2L) { ch_irq = platform_get_irq_byname_optional(pdev, "ch_int"); if (ch_irq < 0) { /* For backward compatibility get irq by index */ @@ -1879,7 +1896,7 @@ static int rcar_canfd_probe(struct platform_device *pdev) gpriv->pdev = pdev; gpriv->channels_mask = channels_mask; gpriv->fdmode = fdmode; - gpriv->chip_id = chip_id; + gpriv->info = info; gpriv->max_channels = max_channels; gpriv->rstc1 = devm_reset_control_get_optional_exclusive(&pdev->dev, @@ -1917,7 +1934,7 @@ static int rcar_canfd_probe(struct platform_device *pdev) } fcan_freq = clk_get_rate(gpriv->can_clk); - if (gpriv->fcan == RCANFD_CANFDCLK && gpriv->chip_id != RENESAS_RZG2L) + if (gpriv->fcan == RCANFD_CANFDCLK && info->chip_id != RENESAS_RZG2L) /* CANFD clock is further divided by (1/2) within the IP */ fcan_freq /= 2; @@ -1929,7 +1946,7 @@ static int rcar_canfd_probe(struct platform_device *pdev) gpriv->base = addr; /* Request IRQ that's common for both channels */ - if (gpriv->chip_id != RENESAS_RZG2L) { + if (info->chip_id != RENESAS_RZG2L) { err = devm_request_irq(&pdev->dev, ch_irq, rcar_canfd_channel_interrupt, 0, "canfd.ch_int", gpriv); @@ -2082,9 +2099,9 @@ static SIMPLE_DEV_PM_OPS(rcar_canfd_pm_ops, rcar_canfd_suspend, rcar_canfd_resume); static const __maybe_unused struct of_device_id rcar_canfd_of_table[] = { - { .compatible = "renesas,rcar-gen3-canfd", .data = (void *)RENESAS_RCAR_GEN3 }, - { .compatible = "renesas,rzg2l-canfd", .data = (void *)RENESAS_RZG2L }, - { .compatible = "renesas,r8a779a0-canfd", .data = (void *)RENESAS_R8A779A0 }, + { .compatible = "renesas,rcar-gen3-canfd", .data = &rcar_gen3_hw_info }, + { .compatible = "renesas,rzg2l-canfd", .data = &rzg2l_hw_info }, + { .compatible = "renesas,r8a779a0-canfd", .data = &r8a779a0_hw_info }, { } }; From 09451f244eabb7ba197abdd92a5ed5e0a8387200 Mon Sep 17 00:00:00 2001 From: Vivek Yadav Date: Fri, 4 Nov 2022 10:46:17 +0530 Subject: [PATCH 1825/2002] can: m_can: sort header inclusion alphabetically Sort header inclusion alphabetically. Suggested-by: Marc Kleine-Budde Signed-off-by: Vivek Yadav Link: https://lore.kernel.org/all/20221104051617.21173-1-vivek.2311@samsung.com Signed-off-by: Marc Kleine-Budde --- drivers/net/can/m_can/m_can.c | 8 ++++---- drivers/net/can/m_can/m_can.h | 16 ++++++++-------- drivers/net/can/m_can/m_can_platform.c | 2 +- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/drivers/net/can/m_can/m_can.c b/drivers/net/can/m_can/m_can.c index 0bdec28e7c85..b1893bb27d59 100644 --- a/drivers/net/can/m_can/m_can.c +++ b/drivers/net/can/m_can/m_can.c @@ -9,20 +9,20 @@ */ #include +#include #include #include #include +#include #include #include #include #include #include +#include +#include #include #include -#include -#include -#include -#include #include "m_can.h" diff --git a/drivers/net/can/m_can/m_can.h b/drivers/net/can/m_can/m_can.h index 52563c048732..a839dc71dc9b 100644 --- a/drivers/net/can/m_can/m_can.h +++ b/drivers/net/can/m_can/m_can.h @@ -7,27 +7,27 @@ #define _CAN_M_CAN_H_ #include +#include #include +#include #include +#include #include #include #include -#include -#include -#include -#include #include #include +#include #include #include #include #include #include -#include -#include -#include -#include #include +#include +#include +#include +#include /* m_can lec values */ enum m_can_lec_type { diff --git a/drivers/net/can/m_can/m_can_platform.c b/drivers/net/can/m_can/m_can_platform.c index eee47bad0592..b5a5bedb3116 100644 --- a/drivers/net/can/m_can/m_can_platform.c +++ b/drivers/net/can/m_can/m_can_platform.c @@ -5,8 +5,8 @@ // // Copyright (C) 2018-19 Texas Instruments Incorporated - http://www.ti.com/ -#include #include +#include #include "m_can.h" From 34f9e9852b90d7be6eef95874500d1fb1b768e7f Mon Sep 17 00:00:00 2001 From: Biju Das Date: Thu, 27 Oct 2022 09:21:54 +0100 Subject: [PATCH 1826/2002] can: rcar_canfd: Add max_channels to struct rcar_canfd_hw_info R-Car V3U supports a maximum of 8 channels whereas rest of the SoCs support 2 channels. Add max_channels variable to struct rcar_canfd_hw_info to handle this difference. Signed-off-by: Biju Das Reviewed-by: Geert Uytterhoeven Link: https://lore.kernel.org/all/20221027082158.95895-3-biju.das.jz@bp.renesas.com Signed-off-by: Marc Kleine-Budde --- drivers/net/can/rcar/rcar_canfd.c | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/drivers/net/can/rcar/rcar_canfd.c b/drivers/net/can/rcar/rcar_canfd.c index 1a5a013ac5bc..6a9f750ac9c1 100644 --- a/drivers/net/can/rcar/rcar_canfd.c +++ b/drivers/net/can/rcar/rcar_canfd.c @@ -524,6 +524,7 @@ struct rcar_canfd_global; struct rcar_canfd_hw_info { enum rcanfd_chip_id chip_id; + u8 max_channels; }; /* Channel priv data */ @@ -552,7 +553,6 @@ struct rcar_canfd_global { struct reset_control *rstc1; struct reset_control *rstc2; const struct rcar_canfd_hw_info *info; - u32 max_channels; }; /* CAN FD mode nominal rate constants */ @@ -596,14 +596,17 @@ static const struct can_bittiming_const rcar_canfd_bittiming_const = { static const struct rcar_canfd_hw_info rcar_gen3_hw_info = { .chip_id = RENESAS_RCAR_GEN3, + .max_channels = 2, }; static const struct rcar_canfd_hw_info rzg2l_hw_info = { .chip_id = RENESAS_RZG2L, + .max_channels = 2, }; static const struct rcar_canfd_hw_info r8a779a0_hw_info = { .chip_id = RENESAS_R8A779A0, + .max_channels = 8, }; /* Helper functions */ @@ -737,7 +740,7 @@ static int rcar_canfd_reset_controller(struct rcar_canfd_global *gpriv) rcar_canfd_set_mode(gpriv); /* Transition all Channels to reset mode */ - for_each_set_bit(ch, &gpriv->channels_mask, gpriv->max_channels) { + for_each_set_bit(ch, &gpriv->channels_mask, gpriv->info->max_channels) { rcar_canfd_clear_bit(gpriv->base, RCANFD_CCTR(ch), RCANFD_CCTR_CSLPR); @@ -778,7 +781,7 @@ static void rcar_canfd_configure_controller(struct rcar_canfd_global *gpriv) rcar_canfd_set_bit(gpriv->base, RCANFD_GCFG, cfg); /* Channel configuration settings */ - for_each_set_bit(ch, &gpriv->channels_mask, gpriv->max_channels) { + for_each_set_bit(ch, &gpriv->channels_mask, gpriv->info->max_channels) { rcar_canfd_set_bit(gpriv->base, RCANFD_CCTR(ch), RCANFD_CCTR_ERRD); rcar_canfd_update_bit(gpriv->base, RCANFD_CCTR(ch), @@ -1158,7 +1161,7 @@ static irqreturn_t rcar_canfd_global_err_interrupt(int irq, void *dev_id) struct rcar_canfd_global *gpriv = dev_id; u32 ch; - for_each_set_bit(ch, &gpriv->channels_mask, gpriv->max_channels) + for_each_set_bit(ch, &gpriv->channels_mask, gpriv->info->max_channels) rcar_canfd_handle_global_err(gpriv, ch); return IRQ_HANDLED; @@ -1190,7 +1193,7 @@ static irqreturn_t rcar_canfd_global_receive_fifo_interrupt(int irq, void *dev_i struct rcar_canfd_global *gpriv = dev_id; u32 ch; - for_each_set_bit(ch, &gpriv->channels_mask, gpriv->max_channels) + for_each_set_bit(ch, &gpriv->channels_mask, gpriv->info->max_channels) rcar_canfd_handle_global_receive(gpriv, ch); return IRQ_HANDLED; @@ -1204,7 +1207,7 @@ static irqreturn_t rcar_canfd_global_interrupt(int irq, void *dev_id) /* Global error interrupts still indicate a condition specific * to a channel. RxFIFO interrupt is a global interrupt. */ - for_each_set_bit(ch, &gpriv->channels_mask, gpriv->max_channels) { + for_each_set_bit(ch, &gpriv->channels_mask, gpriv->info->max_channels) { rcar_canfd_handle_global_err(gpriv, ch); rcar_canfd_handle_global_receive(gpriv, ch); } @@ -1300,7 +1303,7 @@ static irqreturn_t rcar_canfd_channel_interrupt(int irq, void *dev_id) u32 ch; /* Common FIFO is a per channel resource */ - for_each_set_bit(ch, &gpriv->channels_mask, gpriv->max_channels) { + for_each_set_bit(ch, &gpriv->channels_mask, gpriv->info->max_channels) { rcar_canfd_handle_channel_err(gpriv, ch); rcar_canfd_handle_channel_tx(gpriv, ch); } @@ -1844,17 +1847,15 @@ static int rcar_canfd_probe(struct platform_device *pdev) int err, ch_irq, g_irq; int g_err_irq, g_recc_irq; bool fdmode = true; /* CAN FD only mode - default */ - int max_channels; char name[9] = "channelX"; int i; info = of_device_get_match_data(&pdev->dev); - max_channels = info->chip_id == RENESAS_R8A779A0 ? 8 : 2; if (of_property_read_bool(pdev->dev.of_node, "renesas,no-can-fd")) fdmode = false; /* Classical CAN only mode */ - for (i = 0; i < max_channels; ++i) { + for (i = 0; i < info->max_channels; ++i) { name[7] = '0' + i; of_child = of_get_child_by_name(pdev->dev.of_node, name); if (of_child && of_device_is_available(of_child)) @@ -1897,7 +1898,6 @@ static int rcar_canfd_probe(struct platform_device *pdev) gpriv->channels_mask = channels_mask; gpriv->fdmode = fdmode; gpriv->info = info; - gpriv->max_channels = max_channels; gpriv->rstc1 = devm_reset_control_get_optional_exclusive(&pdev->dev, "rstp_n"); @@ -2012,7 +2012,7 @@ static int rcar_canfd_probe(struct platform_device *pdev) rcar_canfd_configure_controller(gpriv); /* Configure per channel attributes */ - for_each_set_bit(ch, &gpriv->channels_mask, max_channels) { + for_each_set_bit(ch, &gpriv->channels_mask, info->max_channels) { /* Configure Channel's Rx fifo */ rcar_canfd_configure_rx(gpriv, ch); @@ -2038,7 +2038,7 @@ static int rcar_canfd_probe(struct platform_device *pdev) goto fail_mode; } - for_each_set_bit(ch, &gpriv->channels_mask, max_channels) { + for_each_set_bit(ch, &gpriv->channels_mask, info->max_channels) { err = rcar_canfd_channel_probe(gpriv, ch, fcan_freq); if (err) goto fail_channel; @@ -2050,7 +2050,7 @@ static int rcar_canfd_probe(struct platform_device *pdev) return 0; fail_channel: - for_each_set_bit(ch, &gpriv->channels_mask, max_channels) + for_each_set_bit(ch, &gpriv->channels_mask, info->max_channels) rcar_canfd_channel_remove(gpriv, ch); fail_mode: rcar_canfd_disable_global_interrupts(gpriv); @@ -2071,7 +2071,7 @@ static int rcar_canfd_remove(struct platform_device *pdev) rcar_canfd_reset_controller(gpriv); rcar_canfd_disable_global_interrupts(gpriv); - for_each_set_bit(ch, &gpriv->channels_mask, gpriv->max_channels) { + for_each_set_bit(ch, &gpriv->channels_mask, gpriv->info->max_channels) { rcar_canfd_disable_channel_interrupts(gpriv->ch[ch]); rcar_canfd_channel_remove(gpriv, ch); } From 841645cfc773cdd0a21c67357c53845f2e972f91 Mon Sep 17 00:00:00 2001 From: Biju Das Date: Thu, 27 Oct 2022 09:21:55 +0100 Subject: [PATCH 1827/2002] can: rcar_canfd: Add shared_global_irqs to struct rcar_canfd_hw_info RZ/G2L has separate IRQ lines for receive FIFO and global error interrupt whereas R-Car has shared IRQ line. Add shared_global_irqs to struct rcar_canfd_hw_info to select the driver to choose between shared and separate irq registration for global interrupts. Signed-off-by: Biju Das Reviewed-by: Geert Uytterhoeven Link: https://lore.kernel.org/all/20221027082158.95895-4-biju.das.jz@bp.renesas.com Signed-off-by: Marc Kleine-Budde --- drivers/net/can/rcar/rcar_canfd.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/net/can/rcar/rcar_canfd.c b/drivers/net/can/rcar/rcar_canfd.c index 6a9f750ac9c1..8a0d2c64dd3e 100644 --- a/drivers/net/can/rcar/rcar_canfd.c +++ b/drivers/net/can/rcar/rcar_canfd.c @@ -525,6 +525,8 @@ struct rcar_canfd_global; struct rcar_canfd_hw_info { enum rcanfd_chip_id chip_id; u8 max_channels; + /* hardware features */ + unsigned shared_global_irqs:1; /* Has shared global irqs */ }; /* Channel priv data */ @@ -597,6 +599,7 @@ static const struct can_bittiming_const rcar_canfd_bittiming_const = { static const struct rcar_canfd_hw_info rcar_gen3_hw_info = { .chip_id = RENESAS_RCAR_GEN3, .max_channels = 2, + .shared_global_irqs = 1, }; static const struct rcar_canfd_hw_info rzg2l_hw_info = { @@ -607,6 +610,7 @@ static const struct rcar_canfd_hw_info rzg2l_hw_info = { static const struct rcar_canfd_hw_info r8a779a0_hw_info = { .chip_id = RENESAS_R8A779A0, .max_channels = 8, + .shared_global_irqs = 1, }; /* Helper functions */ @@ -1863,7 +1867,7 @@ static int rcar_canfd_probe(struct platform_device *pdev) of_node_put(of_child); } - if (info->chip_id != RENESAS_RZG2L) { + if (info->shared_global_irqs) { ch_irq = platform_get_irq_byname_optional(pdev, "ch_int"); if (ch_irq < 0) { /* For backward compatibility get irq by index */ @@ -1946,7 +1950,7 @@ static int rcar_canfd_probe(struct platform_device *pdev) gpriv->base = addr; /* Request IRQ that's common for both channels */ - if (info->chip_id != RENESAS_RZG2L) { + if (info->shared_global_irqs) { err = devm_request_irq(&pdev->dev, ch_irq, rcar_canfd_channel_interrupt, 0, "canfd.ch_int", gpriv); From a1dcfbdfd1d0f7843f381f7c58cc87a0f8c11f6c Mon Sep 17 00:00:00 2001 From: Biju Das Date: Thu, 27 Oct 2022 09:21:56 +0100 Subject: [PATCH 1828/2002] can: rcar_canfd: Add postdiv to struct rcar_canfd_hw_info R-Car has a clock divider for CAN FD clock within the IP, whereas it is not available on RZ/G2L. Add postdiv variable to struct rcar_canfd_hw_info to take care of this difference. Signed-off-by: Biju Das Reviewed-by: Geert Uytterhoeven Link: https://lore.kernel.org/all/20221027082158.95895-5-biju.das.jz@bp.renesas.com Signed-off-by: Marc Kleine-Budde --- drivers/net/can/rcar/rcar_canfd.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/net/can/rcar/rcar_canfd.c b/drivers/net/can/rcar/rcar_canfd.c index 8a0d2c64dd3e..16294a78320e 100644 --- a/drivers/net/can/rcar/rcar_canfd.c +++ b/drivers/net/can/rcar/rcar_canfd.c @@ -525,6 +525,7 @@ struct rcar_canfd_global; struct rcar_canfd_hw_info { enum rcanfd_chip_id chip_id; u8 max_channels; + u8 postdiv; /* hardware features */ unsigned shared_global_irqs:1; /* Has shared global irqs */ }; @@ -599,17 +600,20 @@ static const struct can_bittiming_const rcar_canfd_bittiming_const = { static const struct rcar_canfd_hw_info rcar_gen3_hw_info = { .chip_id = RENESAS_RCAR_GEN3, .max_channels = 2, + .postdiv = 2, .shared_global_irqs = 1, }; static const struct rcar_canfd_hw_info rzg2l_hw_info = { .chip_id = RENESAS_RZG2L, + .postdiv = 1, .max_channels = 2, }; static const struct rcar_canfd_hw_info r8a779a0_hw_info = { .chip_id = RENESAS_R8A779A0, .max_channels = 8, + .postdiv = 2, .shared_global_irqs = 1, }; @@ -1938,9 +1942,9 @@ static int rcar_canfd_probe(struct platform_device *pdev) } fcan_freq = clk_get_rate(gpriv->can_clk); - if (gpriv->fcan == RCANFD_CANFDCLK && info->chip_id != RENESAS_RZG2L) + if (gpriv->fcan == RCANFD_CANFDCLK) /* CANFD clock is further divided by (1/2) within the IP */ - fcan_freq /= 2; + fcan_freq /= info->postdiv; addr = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(addr)) { From ea6ff7792203ad6786bda75d1dabf33311c8bef4 Mon Sep 17 00:00:00 2001 From: Biju Das Date: Thu, 27 Oct 2022 09:21:57 +0100 Subject: [PATCH 1829/2002] can: rcar_canfd: Add multi_channel_irqs to struct rcar_canfd_hw_info RZ/G2L has separate IRQ lines for tx and error interrupt for each channel whereas R-Car has a combined IRQ line for all the channel specific tx and error interrupts. Add multi_channel_irqs to struct rcar_canfd_hw_info to select the driver to choose between combined and separate irq registration for channel interrupts. This patch also removes enum rcanfd_chip_id and chip_id from both struct rcar_canfd_hw_info, as it is unused. Signed-off-by: Biju Das Reviewed-by: Geert Uytterhoeven Link: https://lore.kernel.org/all/20221027082158.95895-6-biju.das.jz@bp.renesas.com Signed-off-by: Marc Kleine-Budde --- drivers/net/can/rcar/rcar_canfd.c | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/drivers/net/can/rcar/rcar_canfd.c b/drivers/net/can/rcar/rcar_canfd.c index 16294a78320e..f6fa7157b99b 100644 --- a/drivers/net/can/rcar/rcar_canfd.c +++ b/drivers/net/can/rcar/rcar_canfd.c @@ -41,12 +41,6 @@ #define RCANFD_DRV_NAME "rcar_canfd" -enum rcanfd_chip_id { - RENESAS_RCAR_GEN3 = 0, - RENESAS_RZG2L, - RENESAS_R8A779A0, -}; - /* Global register bits */ /* RSCFDnCFDGRMCFG */ @@ -523,11 +517,11 @@ enum rcar_canfd_fcanclk { struct rcar_canfd_global; struct rcar_canfd_hw_info { - enum rcanfd_chip_id chip_id; u8 max_channels; u8 postdiv; /* hardware features */ unsigned shared_global_irqs:1; /* Has shared global irqs */ + unsigned multi_channel_irqs:1; /* Has multiple channel irqs */ }; /* Channel priv data */ @@ -598,20 +592,18 @@ static const struct can_bittiming_const rcar_canfd_bittiming_const = { }; static const struct rcar_canfd_hw_info rcar_gen3_hw_info = { - .chip_id = RENESAS_RCAR_GEN3, .max_channels = 2, .postdiv = 2, .shared_global_irqs = 1, }; static const struct rcar_canfd_hw_info rzg2l_hw_info = { - .chip_id = RENESAS_RZG2L, - .postdiv = 1, .max_channels = 2, + .postdiv = 1, + .multi_channel_irqs = 1, }; static const struct rcar_canfd_hw_info r8a779a0_hw_info = { - .chip_id = RENESAS_R8A779A0, .max_channels = 8, .postdiv = 2, .shared_global_irqs = 1, @@ -1746,7 +1738,7 @@ static int rcar_canfd_channel_probe(struct rcar_canfd_global *gpriv, u32 ch, priv->can.clock.freq = fcan_freq; dev_info(&pdev->dev, "can_clk rate is %u\n", priv->can.clock.freq); - if (info->chip_id == RENESAS_RZG2L) { + if (info->multi_channel_irqs) { char *irq_name; int err_irq; int tx_irq; From 7c884339bbff80250bfc11d56b5cf48640e6ebdb Mon Sep 17 00:00:00 2001 From: Eduard Zingerman Date: Fri, 9 Dec 2022 15:57:27 +0200 Subject: [PATCH 1830/2002] bpf: regsafe() must not skip check_ids() The verifier.c:regsafe() has the following shortcut: equal = memcmp(rold, rcur, offsetof(struct bpf_reg_state, parent)) == 0; ... if (equal) return true; Which is executed regardless old register type. This is incorrect for register types that might have an ID checked by check_ids(), namely: - PTR_TO_MAP_KEY - PTR_TO_MAP_VALUE - PTR_TO_PACKET_META - PTR_TO_PACKET The following pattern could be used to exploit this: 0: r9 = map_lookup_elem(...) ; Returns PTR_TO_MAP_VALUE_OR_NULL id=1. 1: r8 = map_lookup_elem(...) ; Returns PTR_TO_MAP_VALUE_OR_NULL id=2. 2: r7 = ktime_get_ns() ; Unbound SCALAR_VALUE. 3: r6 = ktime_get_ns() ; Unbound SCALAR_VALUE. 4: if r6 > r7 goto +1 ; No new information about the state ; is derived from this check, thus ; produced verifier states differ only ; in 'insn_idx'. 5: r9 = r8 ; Optionally make r9.id == r8.id. --- checkpoint --- ; Assume is_state_visisted() creates a ; checkpoint here. 6: if r9 == 0 goto ; Nullness info is propagated to all ; registers with matching ID. 7: r1 = *(u64 *) r8 ; Not always safe. Verifier first visits path 1-7 where r8 is verified to be not null at (6). Later the jump from 4 to 6 is examined. The checkpoint for (6) looks as follows: R8_rD=map_value_or_null(id=2,off=0,ks=4,vs=8,imm=0) R9_rwD=map_value_or_null(id=2,off=0,ks=4,vs=8,imm=0) R10=fp0 The current state is: R0=... R6=... R7=... fp-8=... R8=map_value_or_null(id=2,off=0,ks=4,vs=8,imm=0) R9=map_value_or_null(id=1,off=0,ks=4,vs=8,imm=0) R10=fp0 Note that R8 states are byte-to-byte identical, so regsafe() would exit early and skip call to check_ids(), thus ID mapping 2->2 will not be added to 'idmap'. Next, states for R9 are compared: these are not identical and check_ids() is executed, but 'idmap' is empty, so check_ids() adds mapping 2->1 to 'idmap' and returns success. This commit pushes the 'equal' down to register types that don't need check_ids(). Signed-off-by: Eduard Zingerman Link: https://lore.kernel.org/r/20221209135733.28851-2-eddyz87@gmail.com Signed-off-by: Alexei Starovoitov --- kernel/bpf/verifier.c | 29 ++++++++--------------------- 1 file changed, 8 insertions(+), 21 deletions(-) diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 9791788071d5..97645f767354 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -13064,15 +13064,6 @@ static bool regsafe(struct bpf_verifier_env *env, struct bpf_reg_state *rold, equal = memcmp(rold, rcur, offsetof(struct bpf_reg_state, parent)) == 0; - if (rold->type == PTR_TO_STACK) - /* two stack pointers are equal only if they're pointing to - * the same stack frame, since fp-8 in foo != fp-8 in bar - */ - return equal && rold->frameno == rcur->frameno; - - if (equal) - return true; - if (rold->type == NOT_INIT) /* explored state can't have used this */ return true; @@ -13080,6 +13071,8 @@ static bool regsafe(struct bpf_verifier_env *env, struct bpf_reg_state *rold, return false; switch (base_type(rold->type)) { case SCALAR_VALUE: + if (equal) + return true; if (env->explore_alu_limits) return false; if (rcur->type == SCALAR_VALUE) { @@ -13150,20 +13143,14 @@ static bool regsafe(struct bpf_verifier_env *env, struct bpf_reg_state *rold, /* new val must satisfy old val knowledge */ return range_within(rold, rcur) && tnum_in(rold->var_off, rcur->var_off); - case PTR_TO_CTX: - case CONST_PTR_TO_MAP: - case PTR_TO_PACKET_END: - case PTR_TO_FLOW_KEYS: - case PTR_TO_SOCKET: - case PTR_TO_SOCK_COMMON: - case PTR_TO_TCP_SOCK: - case PTR_TO_XDP_SOCK: - /* Only valid matches are exact, which memcmp() above - * would have accepted + case PTR_TO_STACK: + /* two stack pointers are equal only if they're pointing to + * the same stack frame, since fp-8 in foo != fp-8 in bar */ + return equal && rold->frameno == rcur->frameno; default: - /* Don't know what's going on, just say it's not safe */ - return false; + /* Only valid matches are exact, which memcmp() */ + return equal; } /* Shouldn't get here; if we do, say it's not safe */ From cb578c1c9cf60275f258b5af7929c2a54a188f66 Mon Sep 17 00:00:00 2001 From: Eduard Zingerman Date: Fri, 9 Dec 2022 15:57:28 +0200 Subject: [PATCH 1831/2002] selftests/bpf: test cases for regsafe() bug skipping check_id() Under certain conditions it was possible for verifier.c:regsafe() to skip check_id() call. This commit adds negative test cases previously errorneously accepted as safe. Signed-off-by: Eduard Zingerman Link: https://lore.kernel.org/r/20221209135733.28851-3-eddyz87@gmail.com Signed-off-by: Alexei Starovoitov --- .../bpf/verifier/direct_packet_access.c | 54 +++++++++++++++++++ .../selftests/bpf/verifier/value_or_null.c | 49 +++++++++++++++++ 2 files changed, 103 insertions(+) diff --git a/tools/testing/selftests/bpf/verifier/direct_packet_access.c b/tools/testing/selftests/bpf/verifier/direct_packet_access.c index 11acd1855acf..dce2e28aeb43 100644 --- a/tools/testing/selftests/bpf/verifier/direct_packet_access.c +++ b/tools/testing/selftests/bpf/verifier/direct_packet_access.c @@ -654,3 +654,57 @@ .result = ACCEPT, .prog_type = BPF_PROG_TYPE_SCHED_CLS, }, +{ + "direct packet access: test30 (check_id() in regsafe(), bad access)", + .insns = { + /* r9 = ctx */ + BPF_MOV64_REG(BPF_REG_9, BPF_REG_1), + /* r7 = ktime_get_ns() */ + BPF_EMIT_CALL(BPF_FUNC_ktime_get_ns), + BPF_MOV64_REG(BPF_REG_7, BPF_REG_0), + /* r6 = ktime_get_ns() */ + BPF_EMIT_CALL(BPF_FUNC_ktime_get_ns), + BPF_MOV64_REG(BPF_REG_6, BPF_REG_0), + /* r2 = ctx->data + * r3 = ctx->data + * r4 = ctx->data_end + */ + BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_9, offsetof(struct __sk_buff, data)), + BPF_LDX_MEM(BPF_W, BPF_REG_3, BPF_REG_9, offsetof(struct __sk_buff, data)), + BPF_LDX_MEM(BPF_W, BPF_REG_4, BPF_REG_9, offsetof(struct __sk_buff, data_end)), + /* if r6 > 100 goto exit + * if r7 > 100 goto exit + */ + BPF_JMP_IMM(BPF_JGT, BPF_REG_6, 100, 9), + BPF_JMP_IMM(BPF_JGT, BPF_REG_7, 100, 8), + /* r2 += r6 ; this forces assignment of ID to r2 + * r2 += 1 ; get some fixed off for r2 + * r3 += r7 ; this forces assignment of ID to r3 + * r3 += 1 ; get some fixed off for r3 + */ + BPF_ALU64_REG(BPF_ADD, BPF_REG_2, BPF_REG_6), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, 1), + BPF_ALU64_REG(BPF_ADD, BPF_REG_3, BPF_REG_7), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_3, 1), + /* if r6 > r7 goto +1 ; no new information about the state is derived from + * ; this check, thus produced verifier states differ + * ; only in 'insn_idx' + * r2 = r3 ; optionally share ID between r2 and r3 + */ + BPF_JMP_REG(BPF_JNE, BPF_REG_6, BPF_REG_7, 1), + BPF_MOV64_REG(BPF_REG_2, BPF_REG_3), + /* if r3 > ctx->data_end goto exit */ + BPF_JMP_REG(BPF_JGT, BPF_REG_3, BPF_REG_4, 1), + /* r5 = *(u8 *) (r2 - 1) ; access packet memory using r2, + * ; this is not always safe + */ + BPF_LDX_MEM(BPF_B, BPF_REG_5, BPF_REG_2, -1), + /* exit(0) */ + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + }, + .flags = BPF_F_TEST_STATE_FREQ, + .result = REJECT, + .errstr = "invalid access to packet, off=0 size=1, R2", + .prog_type = BPF_PROG_TYPE_SCHED_CLS, +}, diff --git a/tools/testing/selftests/bpf/verifier/value_or_null.c b/tools/testing/selftests/bpf/verifier/value_or_null.c index 3ecb70a3d939..52a8bca14f03 100644 --- a/tools/testing/selftests/bpf/verifier/value_or_null.c +++ b/tools/testing/selftests/bpf/verifier/value_or_null.c @@ -169,3 +169,52 @@ .prog_type = BPF_PROG_TYPE_SCHED_CLS, .result = ACCEPT, }, +{ + "MAP_VALUE_OR_NULL check_ids() in regsafe()", + .insns = { + BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0), + /* r9 = map_lookup_elem(...) */ + BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), + BPF_LD_MAP_FD(BPF_REG_1, + 0), + BPF_EMIT_CALL(BPF_FUNC_map_lookup_elem), + BPF_MOV64_REG(BPF_REG_9, BPF_REG_0), + /* r8 = map_lookup_elem(...) */ + BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), + BPF_LD_MAP_FD(BPF_REG_1, + 0), + BPF_EMIT_CALL(BPF_FUNC_map_lookup_elem), + BPF_MOV64_REG(BPF_REG_8, BPF_REG_0), + /* r7 = ktime_get_ns() */ + BPF_EMIT_CALL(BPF_FUNC_ktime_get_ns), + BPF_MOV64_REG(BPF_REG_7, BPF_REG_0), + /* r6 = ktime_get_ns() */ + BPF_EMIT_CALL(BPF_FUNC_ktime_get_ns), + BPF_MOV64_REG(BPF_REG_6, BPF_REG_0), + /* if r6 > r7 goto +1 ; no new information about the state is derived from + * ; this check, thus produced verifier states differ + * ; only in 'insn_idx' + * r9 = r8 ; optionally share ID between r9 and r8 + */ + BPF_JMP_REG(BPF_JGT, BPF_REG_6, BPF_REG_7, 1), + BPF_MOV64_REG(BPF_REG_9, BPF_REG_8), + /* if r9 == 0 goto */ + BPF_JMP_IMM(BPF_JEQ, BPF_REG_9, 0, 1), + /* read map value via r8, this is not always + * safe because r8 might be not equal to r9. + */ + BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_8, 0), + /* exit 0 */ + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + }, + .flags = BPF_F_TEST_STATE_FREQ, + .fixup_map_hash_8b = { 3, 9 }, + .result = REJECT, + .errstr = "R8 invalid mem access 'map_value_or_null'", + .result_unpriv = REJECT, + .errstr_unpriv = "", + .prog_type = BPF_PROG_TYPE_CGROUP_SKB, +}, From 5dd9cdbc9dec3e99b19e483767e247d15ca8cc0d Mon Sep 17 00:00:00 2001 From: Eduard Zingerman Date: Fri, 9 Dec 2022 15:57:29 +0200 Subject: [PATCH 1832/2002] bpf: states_equal() must build idmap for all function frames verifier.c:states_equal() must maintain register ID mapping across all function frames. Otherwise the following example might be erroneously marked as safe: main: fp[-24] = map_lookup_elem(...) ; frame[0].fp[-24].id == 1 fp[-32] = map_lookup_elem(...) ; frame[0].fp[-32].id == 2 r1 = &fp[-24] r2 = &fp[-32] call foo() r0 = 0 exit foo: 0: r9 = r1 1: r8 = r2 2: r7 = ktime_get_ns() 3: r6 = ktime_get_ns() 4: if (r6 > r7) goto skip_assign 5: r9 = r8 skip_assign: ; <--- checkpoint 6: r9 = *r9 ; (a) frame[1].r9.id == 2 ; (b) frame[1].r9.id == 1 7: if r9 == 0 goto exit: ; mark_ptr_or_null_regs() transfers != 0 info ; for all regs sharing ID: ; (a) r9 != 0 => &frame[0].fp[-32] != 0 ; (b) r9 != 0 => &frame[0].fp[-24] != 0 8: r8 = *r8 ; (a) r8 == &frame[0].fp[-32] ; (b) r8 == &frame[0].fp[-32] 9: r0 = *r8 ; (a) safe ; (b) unsafe exit: 10: exit While processing call to foo() verifier considers the following execution paths: (a) 0-10 (b) 0-4,6-10 (There is also path 0-7,10 but it is not interesting for the issue at hand. (a) is verified first.) Suppose that checkpoint is created at (6) when path (a) is verified, next path (b) is verified and (6) is reached. If states_equal() maintains separate 'idmap' for each frame the mapping at (6) for frame[1] would be empty and regsafe(r9)::check_ids() would add a pair 2->1 and return true, which is an error. If states_equal() maintains single 'idmap' for all frames the mapping at (6) would be { 1->1, 2->2 } and regsafe(r9)::check_ids() would return false when trying to add a pair 2->1. This issue was suggested in the following discussion: https://lore.kernel.org/bpf/CAEf4BzbFB5g4oUfyxk9rHy-PJSLQ3h8q9mV=rVoXfr_JVm8+1Q@mail.gmail.com/ Suggested-by: Andrii Nakryiko Signed-off-by: Eduard Zingerman Link: https://lore.kernel.org/r/20221209135733.28851-4-eddyz87@gmail.com Signed-off-by: Alexei Starovoitov --- include/linux/bpf_verifier.h | 4 ++-- kernel/bpf/verifier.c | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/include/linux/bpf_verifier.h b/include/linux/bpf_verifier.h index df0cb825e0e3..53d175cbaa02 100644 --- a/include/linux/bpf_verifier.h +++ b/include/linux/bpf_verifier.h @@ -273,9 +273,9 @@ struct bpf_id_pair { u32 cur; }; -/* Maximum number of register states that can exist at once */ -#define BPF_ID_MAP_SIZE (MAX_BPF_REG + MAX_BPF_STACK / BPF_REG_SIZE) #define MAX_CALL_FRAMES 8 +/* Maximum number of register states that can exist at once */ +#define BPF_ID_MAP_SIZE ((MAX_BPF_REG + MAX_BPF_STACK / BPF_REG_SIZE) * MAX_CALL_FRAMES) struct bpf_verifier_state { /* call stack tracking */ struct bpf_func_state *frame[MAX_CALL_FRAMES]; diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 97645f767354..00c8e0a28203 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -13260,7 +13260,6 @@ static bool func_states_equal(struct bpf_verifier_env *env, struct bpf_func_stat { int i; - memset(env->idmap_scratch, 0, sizeof(env->idmap_scratch)); for (i = 0; i < MAX_BPF_REG; i++) if (!regsafe(env, &old->regs[i], &cur->regs[i], env->idmap_scratch)) @@ -13284,6 +13283,8 @@ static bool states_equal(struct bpf_verifier_env *env, if (old->curframe != cur->curframe) return false; + memset(env->idmap_scratch, 0, sizeof(env->idmap_scratch)); + /* Verification state from speculative execution simulation * must never prune a non-speculative execution one. */ From 7d05794330877986f605c1618534d7478030f5b8 Mon Sep 17 00:00:00 2001 From: Eduard Zingerman Date: Fri, 9 Dec 2022 15:57:30 +0200 Subject: [PATCH 1833/2002] selftests/bpf: verify states_equal() maintains idmap across all frames A test case that would erroneously pass verification if verifier.c:states_equal() maintains separate register ID mappings for call frames. Signed-off-by: Eduard Zingerman Link: https://lore.kernel.org/r/20221209135733.28851-5-eddyz87@gmail.com Signed-off-by: Alexei Starovoitov --- tools/testing/selftests/bpf/verifier/calls.c | 82 ++++++++++++++++++++ 1 file changed, 82 insertions(+) diff --git a/tools/testing/selftests/bpf/verifier/calls.c b/tools/testing/selftests/bpf/verifier/calls.c index babcec123251..9d993926bf0e 100644 --- a/tools/testing/selftests/bpf/verifier/calls.c +++ b/tools/testing/selftests/bpf/verifier/calls.c @@ -2305,3 +2305,85 @@ .errstr = "!read_ok", .result = REJECT, }, +/* Make sure that verifier.c:states_equal() considers IDs from all + * frames when building 'idmap' for check_ids(). + */ +{ + "calls: check_ids() across call boundary", + .insns = { + /* Function main() */ + BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0), + /* fp[-24] = map_lookup_elem(...) ; get a MAP_VALUE_PTR_OR_NULL with some ID */ + BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), + BPF_LD_MAP_FD(BPF_REG_1, + 0), + BPF_EMIT_CALL(BPF_FUNC_map_lookup_elem), + BPF_STX_MEM(BPF_DW, BPF_REG_FP, BPF_REG_0, -24), + /* fp[-32] = map_lookup_elem(...) ; get a MAP_VALUE_PTR_OR_NULL with some ID */ + BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), + BPF_LD_MAP_FD(BPF_REG_1, + 0), + BPF_EMIT_CALL(BPF_FUNC_map_lookup_elem), + BPF_STX_MEM(BPF_DW, BPF_REG_FP, BPF_REG_0, -32), + /* call foo(&fp[-24], &fp[-32]) ; both arguments have IDs in the current + * ; stack frame + */ + BPF_MOV64_REG(BPF_REG_1, BPF_REG_FP), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, -24), + BPF_MOV64_REG(BPF_REG_2, BPF_REG_FP), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -32), + BPF_CALL_REL(2), + /* exit 0 */ + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + /* Function foo() + * + * r9 = &frame[0].fp[-24] ; save arguments in the callee saved registers, + * r8 = &frame[0].fp[-32] ; arguments are pointers to pointers to map value + */ + BPF_MOV64_REG(BPF_REG_9, BPF_REG_1), + BPF_MOV64_REG(BPF_REG_8, BPF_REG_2), + /* r7 = ktime_get_ns() */ + BPF_EMIT_CALL(BPF_FUNC_ktime_get_ns), + BPF_MOV64_REG(BPF_REG_7, BPF_REG_0), + /* r6 = ktime_get_ns() */ + BPF_EMIT_CALL(BPF_FUNC_ktime_get_ns), + BPF_MOV64_REG(BPF_REG_6, BPF_REG_0), + /* if r6 > r7 goto +1 ; no new information about the state is derived from + * ; this check, thus produced verifier states differ + * ; only in 'insn_idx' + * r9 = r8 + */ + BPF_JMP_REG(BPF_JGT, BPF_REG_6, BPF_REG_7, 1), + BPF_MOV64_REG(BPF_REG_9, BPF_REG_8), + /* r9 = *r9 ; verifier get's to this point via two paths: + * ; (I) one including r9 = r8, verified first; + * ; (II) one excluding r9 = r8, verified next. + * ; After load of *r9 to r9 the frame[0].fp[-24].id == r9.id. + * ; Suppose that checkpoint is created here via path (I). + * ; When verifying via (II) the r9.id must be compared against + * ; frame[0].fp[-24].id, otherwise (I) and (II) would be + * ; incorrectly deemed equivalent. + * if r9 == 0 goto + */ + BPF_LDX_MEM(BPF_DW, BPF_REG_9, BPF_REG_9, 0), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_9, 0, 1), + /* r8 = *r8 ; read map value via r8, this is not safe + * r0 = *r8 ; because r8 might be not equal to r9. + */ + BPF_LDX_MEM(BPF_DW, BPF_REG_8, BPF_REG_8, 0), + BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_8, 0), + /* exit 0 */ + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + }, + .flags = BPF_F_TEST_STATE_FREQ, + .fixup_map_hash_8b = { 3, 9 }, + .result = REJECT, + .errstr = "R8 invalid mem access 'map_value_or_null'", + .result_unpriv = REJECT, + .errstr_unpriv = "", + .prog_type = BPF_PROG_TYPE_CGROUP_SKB, +}, From 4ea2bb158bec2fe171e7e07c033dcf208d86e274 Mon Sep 17 00:00:00 2001 From: Eduard Zingerman Date: Fri, 9 Dec 2022 15:57:31 +0200 Subject: [PATCH 1834/2002] bpf: use check_ids() for active_lock comparison An update for verifier.c:states_equal()/regsafe() to use check_ids() for active spin lock comparisons. This fixes the issue reported by Kumar Kartikeya Dwivedi in [1] using technique suggested by Edward Cree. W/o this commit the verifier might be tricked to accept the following program working with a map containing spin locks: 0: r9 = map_lookup_elem(...) ; Returns PTR_TO_MAP_VALUE_OR_NULL id=1. 1: r8 = map_lookup_elem(...) ; Returns PTR_TO_MAP_VALUE_OR_NULL id=2. 2: if r9 == 0 goto exit ; r9 -> PTR_TO_MAP_VALUE. 3: if r8 == 0 goto exit ; r8 -> PTR_TO_MAP_VALUE. 4: r7 = ktime_get_ns() ; Unbound SCALAR_VALUE. 5: r6 = ktime_get_ns() ; Unbound SCALAR_VALUE. 6: bpf_spin_lock(r8) ; active_lock.id == 2. 7: if r6 > r7 goto +1 ; No new information about the state ; is derived from this check, thus ; produced verifier states differ only ; in 'insn_idx'. 8: r9 = r8 ; Optionally make r9.id == r8.id. --- checkpoint --- ; Assume is_state_visisted() creates a ; checkpoint here. 9: bpf_spin_unlock(r9) ; (a,b) active_lock.id == 2. ; (a) r9.id == 2, (b) r9.id == 1. 10: exit(0) Consider two verification paths: (a) 0-10 (b) 0-7,9-10 The path (a) is verified first. If checkpoint is created at (8) the (b) would assume that (8) is safe because regsafe() does not compare register ids for registers of type PTR_TO_MAP_VALUE. [1] https://lore.kernel.org/bpf/20221111202719.982118-1-memxor@gmail.com/ Reported-by: Kumar Kartikeya Dwivedi Suggested-by: Edward Cree Signed-off-by: Eduard Zingerman Link: https://lore.kernel.org/r/20221209135733.28851-6-eddyz87@gmail.com Signed-off-by: Alexei Starovoitov --- kernel/bpf/verifier.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 00c8e0a28203..a5255a0dcbb6 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -13119,7 +13119,8 @@ static bool regsafe(struct bpf_verifier_env *env, struct bpf_reg_state *rold, */ return memcmp(rold, rcur, offsetof(struct bpf_reg_state, id)) == 0 && range_within(rold, rcur) && - tnum_in(rold->var_off, rcur->var_off); + tnum_in(rold->var_off, rcur->var_off) && + check_ids(rold->id, rcur->id, idmap); case PTR_TO_PACKET_META: case PTR_TO_PACKET: if (rcur->type != rold->type) @@ -13291,8 +13292,17 @@ static bool states_equal(struct bpf_verifier_env *env, if (old->speculative && !cur->speculative) return false; - if (old->active_lock.ptr != cur->active_lock.ptr || - old->active_lock.id != cur->active_lock.id) + if (old->active_lock.ptr != cur->active_lock.ptr) + return false; + + /* Old and cur active_lock's have to be either both present + * or both absent. + */ + if (!!old->active_lock.id != !!cur->active_lock.id) + return false; + + if (old->active_lock.id && + !check_ids(old->active_lock.id, cur->active_lock.id, env->idmap_scratch)) return false; if (old->active_rcu_lock != cur->active_rcu_lock) From 2026f2062df860e5d282ffd4962ea5d5ed53dc51 Mon Sep 17 00:00:00 2001 From: Kumar Kartikeya Dwivedi Date: Fri, 9 Dec 2022 15:57:32 +0200 Subject: [PATCH 1835/2002] selftests/bpf: Add pruning test case for bpf_spin_lock Test that when reg->id is not same for the same register of type PTR_TO_MAP_VALUE between current and old explored state, we currently return false from regsafe and continue exploring. Without the fix in prior commit, the test case fails. Signed-off-by: Kumar Kartikeya Dwivedi Signed-off-by: Eduard Zingerman Link: https://lore.kernel.org/r/20221209135733.28851-7-eddyz87@gmail.com Signed-off-by: Alexei Starovoitov --- .../selftests/bpf/verifier/spin_lock.c | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/tools/testing/selftests/bpf/verifier/spin_lock.c b/tools/testing/selftests/bpf/verifier/spin_lock.c index 781621facae4..0a8dcfc37fc6 100644 --- a/tools/testing/selftests/bpf/verifier/spin_lock.c +++ b/tools/testing/selftests/bpf/verifier/spin_lock.c @@ -331,3 +331,42 @@ .errstr = "inside bpf_spin_lock", .prog_type = BPF_PROG_TYPE_SCHED_CLS, }, +{ + "spin_lock: regsafe compare reg->id for map value", + .insns = { + BPF_MOV64_REG(BPF_REG_6, BPF_REG_1), + BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_6, offsetof(struct __sk_buff, mark)), + BPF_LD_MAP_FD(BPF_REG_1, 0), + BPF_MOV64_REG(BPF_REG_9, BPF_REG_1), + BPF_ST_MEM(BPF_W, BPF_REG_10, -4, 0), + BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -4), + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem), + BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 1), + BPF_EXIT_INSN(), + BPF_MOV64_REG(BPF_REG_7, BPF_REG_0), + BPF_MOV64_REG(BPF_REG_1, BPF_REG_9), + BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -4), + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem), + BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 1), + BPF_EXIT_INSN(), + BPF_MOV64_REG(BPF_REG_8, BPF_REG_0), + BPF_MOV64_REG(BPF_REG_1, BPF_REG_7), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, 4), + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_spin_lock), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_6, 0, 1), + BPF_JMP_IMM(BPF_JA, 0, 0, 1), + BPF_MOV64_REG(BPF_REG_7, BPF_REG_8), + BPF_MOV64_REG(BPF_REG_1, BPF_REG_7), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, 4), + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_spin_unlock), + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + }, + .fixup_map_spin_lock = { 2 }, + .result = REJECT, + .errstr = "bpf_spin_unlock of different lock", + .prog_type = BPF_PROG_TYPE_SCHED_CLS, + .flags = BPF_F_TEST_STATE_FREQ, +}, From efd6286ff74a2fa2b45ed070d344cc0822b8ea6e Mon Sep 17 00:00:00 2001 From: Eduard Zingerman Date: Fri, 9 Dec 2022 15:57:33 +0200 Subject: [PATCH 1836/2002] selftests/bpf: test case for relaxed prunning of active_lock.id Check that verifier.c:states_equal() uses check_ids() to match consistent active_lock/map_value configurations. This allows to prune states with active spin locks even if numerical values of active_lock ids do not match across compared states. Signed-off-by: Eduard Zingerman Link: https://lore.kernel.org/r/20221209135733.28851-8-eddyz87@gmail.com Signed-off-by: Alexei Starovoitov --- .../selftests/bpf/verifier/spin_lock.c | 75 +++++++++++++++++++ 1 file changed, 75 insertions(+) diff --git a/tools/testing/selftests/bpf/verifier/spin_lock.c b/tools/testing/selftests/bpf/verifier/spin_lock.c index 0a8dcfc37fc6..eaf114f07e2e 100644 --- a/tools/testing/selftests/bpf/verifier/spin_lock.c +++ b/tools/testing/selftests/bpf/verifier/spin_lock.c @@ -370,3 +370,78 @@ .prog_type = BPF_PROG_TYPE_SCHED_CLS, .flags = BPF_F_TEST_STATE_FREQ, }, +/* Make sure that regsafe() compares ids for spin lock records using + * check_ids(): + * 1: r9 = map_lookup_elem(...) ; r9.id == 1 + * 2: r8 = map_lookup_elem(...) ; r8.id == 2 + * 3: r7 = ktime_get_ns() + * 4: r6 = ktime_get_ns() + * 5: if r6 > r7 goto <9> + * 6: spin_lock(r8) + * 7: r9 = r8 + * 8: goto <10> + * 9: spin_lock(r9) + * 10: spin_unlock(r9) ; r9.id == 1 || r9.id == 2 and lock is active, + * ; second visit to (10) should be considered safe + * ; if check_ids() is used. + * 11: exit(0) + */ +{ + "spin_lock: regsafe() check_ids() similar id mappings", + .insns = { + BPF_ST_MEM(BPF_W, BPF_REG_10, -4, 0), + /* r9 = map_lookup_elem(...) */ + BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -4), + BPF_LD_MAP_FD(BPF_REG_1, + 0), + BPF_EMIT_CALL(BPF_FUNC_map_lookup_elem), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 24), + BPF_MOV64_REG(BPF_REG_9, BPF_REG_0), + /* r8 = map_lookup_elem(...) */ + BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -4), + BPF_LD_MAP_FD(BPF_REG_1, + 0), + BPF_EMIT_CALL(BPF_FUNC_map_lookup_elem), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 18), + BPF_MOV64_REG(BPF_REG_8, BPF_REG_0), + /* r7 = ktime_get_ns() */ + BPF_EMIT_CALL(BPF_FUNC_ktime_get_ns), + BPF_MOV64_REG(BPF_REG_7, BPF_REG_0), + /* r6 = ktime_get_ns() */ + BPF_EMIT_CALL(BPF_FUNC_ktime_get_ns), + BPF_MOV64_REG(BPF_REG_6, BPF_REG_0), + /* if r6 > r7 goto +5 ; no new information about the state is derived from + * ; this check, thus produced verifier states differ + * ; only in 'insn_idx' + * spin_lock(r8) + * r9 = r8 + * goto unlock + */ + BPF_JMP_REG(BPF_JGT, BPF_REG_6, BPF_REG_7, 5), + BPF_MOV64_REG(BPF_REG_1, BPF_REG_8), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, 4), + BPF_EMIT_CALL(BPF_FUNC_spin_lock), + BPF_MOV64_REG(BPF_REG_9, BPF_REG_8), + BPF_JMP_A(3), + /* spin_lock(r9) */ + BPF_MOV64_REG(BPF_REG_1, BPF_REG_9), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, 4), + BPF_EMIT_CALL(BPF_FUNC_spin_lock), + /* spin_unlock(r9) */ + BPF_MOV64_REG(BPF_REG_1, BPF_REG_9), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, 4), + BPF_EMIT_CALL(BPF_FUNC_spin_unlock), + /* exit(0) */ + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + }, + .fixup_map_spin_lock = { 3, 10 }, + .result = VERBOSE_ACCEPT, + .errstr = "28: safe", + .result_unpriv = REJECT, + .errstr_unpriv = "", + .prog_type = BPF_PROG_TYPE_CGROUP_SKB, + .flags = BPF_F_TEST_STATE_FREQ, +}, From 5df7d714d8cbcce7642936cc0f6532f0c4c3d197 Mon Sep 17 00:00:00 2001 From: Julian Anastasov Date: Tue, 22 Nov 2022 18:45:59 +0200 Subject: [PATCH 1837/2002] ipvs: add rcu protection to stats In preparation to using RCU locking for the list with estimators, make sure the struct ip_vs_stats are released after RCU grace period by using RCU callbacks. This affects ipvs->tot_stats where we can not use RCU callbacks for ipvs, so we use allocated struct ip_vs_stats_rcu. For services and dests we force RCU callbacks for all cases. Signed-off-by: Julian Anastasov Cc: yunhong-cgl jiang Cc: "dust.li" Reviewed-by: Jiri Wiesner Signed-off-by: Pablo Neira Ayuso --- include/net/ip_vs.h | 8 ++++- net/netfilter/ipvs/ip_vs_core.c | 10 ++++-- net/netfilter/ipvs/ip_vs_ctl.c | 64 ++++++++++++++++++++++----------- 3 files changed, 57 insertions(+), 25 deletions(-) diff --git a/include/net/ip_vs.h b/include/net/ip_vs.h index ff1804a0c469..bd8ae137e43b 100644 --- a/include/net/ip_vs.h +++ b/include/net/ip_vs.h @@ -405,6 +405,11 @@ struct ip_vs_stats { struct ip_vs_kstats kstats0; /* reset values */ }; +struct ip_vs_stats_rcu { + struct ip_vs_stats s; + struct rcu_head rcu_head; +}; + struct dst_entry; struct iphdr; struct ip_vs_conn; @@ -688,6 +693,7 @@ struct ip_vs_dest { union nf_inet_addr vaddr; /* virtual IP address */ __u32 vfwmark; /* firewall mark of service */ + struct rcu_head rcu_head; struct list_head t_list; /* in dest_trash */ unsigned int in_rs_table:1; /* we are in rs_table */ }; @@ -869,7 +875,7 @@ struct netns_ipvs { atomic_t conn_count; /* connection counter */ /* ip_vs_ctl */ - struct ip_vs_stats tot_stats; /* Statistics & est. */ + struct ip_vs_stats_rcu *tot_stats; /* Statistics & est. */ int num_services; /* no of virtual services */ int num_services6; /* IPv6 virtual services */ diff --git a/net/netfilter/ipvs/ip_vs_core.c b/net/netfilter/ipvs/ip_vs_core.c index 51ad557a525b..fcdaef1fcccf 100644 --- a/net/netfilter/ipvs/ip_vs_core.c +++ b/net/netfilter/ipvs/ip_vs_core.c @@ -143,7 +143,7 @@ ip_vs_in_stats(struct ip_vs_conn *cp, struct sk_buff *skb) s->cnt.inbytes += skb->len; u64_stats_update_end(&s->syncp); - s = this_cpu_ptr(ipvs->tot_stats.cpustats); + s = this_cpu_ptr(ipvs->tot_stats->s.cpustats); u64_stats_update_begin(&s->syncp); s->cnt.inpkts++; s->cnt.inbytes += skb->len; @@ -179,7 +179,7 @@ ip_vs_out_stats(struct ip_vs_conn *cp, struct sk_buff *skb) s->cnt.outbytes += skb->len; u64_stats_update_end(&s->syncp); - s = this_cpu_ptr(ipvs->tot_stats.cpustats); + s = this_cpu_ptr(ipvs->tot_stats->s.cpustats); u64_stats_update_begin(&s->syncp); s->cnt.outpkts++; s->cnt.outbytes += skb->len; @@ -208,7 +208,7 @@ ip_vs_conn_stats(struct ip_vs_conn *cp, struct ip_vs_service *svc) s->cnt.conns++; u64_stats_update_end(&s->syncp); - s = this_cpu_ptr(ipvs->tot_stats.cpustats); + s = this_cpu_ptr(ipvs->tot_stats->s.cpustats); u64_stats_update_begin(&s->syncp); s->cnt.conns++; u64_stats_update_end(&s->syncp); @@ -2448,6 +2448,10 @@ static void __exit ip_vs_cleanup(void) ip_vs_conn_cleanup(); ip_vs_protocol_cleanup(); ip_vs_control_cleanup(); + /* common rcu_barrier() used by: + * - ip_vs_control_cleanup() + */ + rcu_barrier(); pr_info("ipvs unloaded.\n"); } diff --git a/net/netfilter/ipvs/ip_vs_ctl.c b/net/netfilter/ipvs/ip_vs_ctl.c index 4d62059a6021..9016b641ae52 100644 --- a/net/netfilter/ipvs/ip_vs_ctl.c +++ b/net/netfilter/ipvs/ip_vs_ctl.c @@ -483,17 +483,14 @@ static void ip_vs_service_rcu_free(struct rcu_head *head) ip_vs_service_free(svc); } -static void __ip_vs_svc_put(struct ip_vs_service *svc, bool do_delay) +static void __ip_vs_svc_put(struct ip_vs_service *svc) { if (atomic_dec_and_test(&svc->refcnt)) { IP_VS_DBG_BUF(3, "Removing service %u/%s:%u\n", svc->fwmark, IP_VS_DBG_ADDR(svc->af, &svc->addr), ntohs(svc->port)); - if (do_delay) - call_rcu(&svc->rcu_head, ip_vs_service_rcu_free); - else - ip_vs_service_free(svc); + call_rcu(&svc->rcu_head, ip_vs_service_rcu_free); } } @@ -780,14 +777,22 @@ out: return dest; } +static void ip_vs_dest_rcu_free(struct rcu_head *head) +{ + struct ip_vs_dest *dest; + + dest = container_of(head, struct ip_vs_dest, rcu_head); + free_percpu(dest->stats.cpustats); + ip_vs_dest_put_and_free(dest); +} + static void ip_vs_dest_free(struct ip_vs_dest *dest) { struct ip_vs_service *svc = rcu_dereference_protected(dest->svc, 1); __ip_vs_dst_cache_reset(dest); - __ip_vs_svc_put(svc, false); - free_percpu(dest->stats.cpustats); - ip_vs_dest_put_and_free(dest); + __ip_vs_svc_put(svc); + call_rcu(&dest->rcu_head, ip_vs_dest_rcu_free); } /* @@ -811,6 +816,16 @@ static void ip_vs_trash_cleanup(struct netns_ipvs *ipvs) } } +static void ip_vs_stats_rcu_free(struct rcu_head *head) +{ + struct ip_vs_stats_rcu *rs = container_of(head, + struct ip_vs_stats_rcu, + rcu_head); + + free_percpu(rs->s.cpustats); + kfree(rs); +} + static void ip_vs_copy_stats(struct ip_vs_kstats *dst, struct ip_vs_stats *src) { @@ -923,7 +938,7 @@ __ip_vs_update_dest(struct ip_vs_service *svc, struct ip_vs_dest *dest, if (old_svc != svc) { ip_vs_zero_stats(&dest->stats); __ip_vs_bind_svc(dest, svc); - __ip_vs_svc_put(old_svc, true); + __ip_vs_svc_put(old_svc); } } @@ -1571,7 +1586,7 @@ static void __ip_vs_del_service(struct ip_vs_service *svc, bool cleanup) /* * Free the service if nobody refers to it */ - __ip_vs_svc_put(svc, true); + __ip_vs_svc_put(svc); /* decrease the module use count */ ip_vs_use_count_dec(); @@ -1761,7 +1776,7 @@ static int ip_vs_zero_all(struct netns_ipvs *ipvs) } } - ip_vs_zero_stats(&ipvs->tot_stats); + ip_vs_zero_stats(&ipvs->tot_stats->s); return 0; } @@ -2255,7 +2270,7 @@ static int ip_vs_stats_show(struct seq_file *seq, void *v) seq_puts(seq, " Conns Packets Packets Bytes Bytes\n"); - ip_vs_copy_stats(&show, &net_ipvs(net)->tot_stats); + ip_vs_copy_stats(&show, &net_ipvs(net)->tot_stats->s); seq_printf(seq, "%8LX %8LX %8LX %16LX %16LX\n\n", (unsigned long long)show.conns, (unsigned long long)show.inpkts, @@ -2279,7 +2294,7 @@ static int ip_vs_stats_show(struct seq_file *seq, void *v) static int ip_vs_stats_percpu_show(struct seq_file *seq, void *v) { struct net *net = seq_file_single_net(seq); - struct ip_vs_stats *tot_stats = &net_ipvs(net)->tot_stats; + struct ip_vs_stats *tot_stats = &net_ipvs(net)->tot_stats->s; struct ip_vs_cpu_stats __percpu *cpustats = tot_stats->cpustats; struct ip_vs_kstats kstats; int i; @@ -4107,7 +4122,6 @@ static int __net_init ip_vs_control_net_init_sysctl(struct netns_ipvs *ipvs) kfree(tbl); return -ENOMEM; } - ip_vs_start_estimator(ipvs, &ipvs->tot_stats); ipvs->sysctl_tbl = tbl; /* Schedule defense work */ INIT_DELAYED_WORK(&ipvs->defense_work, defense_work_handler); @@ -4118,6 +4132,7 @@ static int __net_init ip_vs_control_net_init_sysctl(struct netns_ipvs *ipvs) INIT_DELAYED_WORK(&ipvs->expire_nodest_conn_work, expire_nodest_conn_handler); + ip_vs_start_estimator(ipvs, &ipvs->tot_stats->s); return 0; } @@ -4129,7 +4144,7 @@ static void __net_exit ip_vs_control_net_cleanup_sysctl(struct netns_ipvs *ipvs) cancel_delayed_work_sync(&ipvs->defense_work); cancel_work_sync(&ipvs->defense_work.work); unregister_net_sysctl_table(ipvs->sysctl_hdr); - ip_vs_stop_estimator(ipvs, &ipvs->tot_stats); + ip_vs_stop_estimator(ipvs, &ipvs->tot_stats->s); if (!net_eq(net, &init_net)) kfree(ipvs->sysctl_tbl); @@ -4165,17 +4180,20 @@ int __net_init ip_vs_control_net_init(struct netns_ipvs *ipvs) atomic_set(&ipvs->conn_out_counter, 0); /* procfs stats */ - ipvs->tot_stats.cpustats = alloc_percpu(struct ip_vs_cpu_stats); - if (!ipvs->tot_stats.cpustats) + ipvs->tot_stats = kzalloc(sizeof(*ipvs->tot_stats), GFP_KERNEL); + if (!ipvs->tot_stats) return -ENOMEM; + ipvs->tot_stats->s.cpustats = alloc_percpu(struct ip_vs_cpu_stats); + if (!ipvs->tot_stats->s.cpustats) + goto err_tot_stats; for_each_possible_cpu(i) { struct ip_vs_cpu_stats *ipvs_tot_stats; - ipvs_tot_stats = per_cpu_ptr(ipvs->tot_stats.cpustats, i); + ipvs_tot_stats = per_cpu_ptr(ipvs->tot_stats->s.cpustats, i); u64_stats_init(&ipvs_tot_stats->syncp); } - spin_lock_init(&ipvs->tot_stats.lock); + spin_lock_init(&ipvs->tot_stats->s.lock); #ifdef CONFIG_PROC_FS if (!proc_create_net("ip_vs", 0, ipvs->net->proc_net, @@ -4207,7 +4225,10 @@ err_stats: err_vs: #endif - free_percpu(ipvs->tot_stats.cpustats); + free_percpu(ipvs->tot_stats->s.cpustats); + +err_tot_stats: + kfree(ipvs->tot_stats); return -ENOMEM; } @@ -4220,7 +4241,7 @@ void __net_exit ip_vs_control_net_cleanup(struct netns_ipvs *ipvs) remove_proc_entry("ip_vs_stats", ipvs->net->proc_net); remove_proc_entry("ip_vs", ipvs->net->proc_net); #endif - free_percpu(ipvs->tot_stats.cpustats); + call_rcu(&ipvs->tot_stats->rcu_head, ip_vs_stats_rcu_free); } int __init ip_vs_register_nl_ioctl(void) @@ -4280,5 +4301,6 @@ void ip_vs_control_cleanup(void) { EnterFunction(2); unregister_netdevice_notifier(&ip_vs_dst_notifier); + /* relying on common rcu_barrier() in ip_vs_cleanup() */ LeaveFunction(2); } From de39afb3d811ba2c028de8662adafedb4899327b Mon Sep 17 00:00:00 2001 From: Julian Anastasov Date: Tue, 22 Nov 2022 18:46:00 +0200 Subject: [PATCH 1838/2002] ipvs: use common functions for stats allocation Move alloc_percpu/free_percpu logic in new functions Signed-off-by: Julian Anastasov Cc: yunhong-cgl jiang Cc: "dust.li" Reviewed-by: Jiri Wiesner Signed-off-by: Pablo Neira Ayuso --- include/net/ip_vs.h | 5 ++ net/netfilter/ipvs/ip_vs_ctl.c | 96 +++++++++++++++++++--------------- 2 files changed, 60 insertions(+), 41 deletions(-) diff --git a/include/net/ip_vs.h b/include/net/ip_vs.h index bd8ae137e43b..e5582c01a4a3 100644 --- a/include/net/ip_vs.h +++ b/include/net/ip_vs.h @@ -410,6 +410,11 @@ struct ip_vs_stats_rcu { struct rcu_head rcu_head; }; +int ip_vs_stats_init_alloc(struct ip_vs_stats *s); +struct ip_vs_stats *ip_vs_stats_alloc(void); +void ip_vs_stats_release(struct ip_vs_stats *stats); +void ip_vs_stats_free(struct ip_vs_stats *stats); + struct dst_entry; struct iphdr; struct ip_vs_conn; diff --git a/net/netfilter/ipvs/ip_vs_ctl.c b/net/netfilter/ipvs/ip_vs_ctl.c index 9016b641ae52..ec6db864ac36 100644 --- a/net/netfilter/ipvs/ip_vs_ctl.c +++ b/net/netfilter/ipvs/ip_vs_ctl.c @@ -471,7 +471,7 @@ __ip_vs_bind_svc(struct ip_vs_dest *dest, struct ip_vs_service *svc) static void ip_vs_service_free(struct ip_vs_service *svc) { - free_percpu(svc->stats.cpustats); + ip_vs_stats_release(&svc->stats); kfree(svc); } @@ -782,7 +782,7 @@ static void ip_vs_dest_rcu_free(struct rcu_head *head) struct ip_vs_dest *dest; dest = container_of(head, struct ip_vs_dest, rcu_head); - free_percpu(dest->stats.cpustats); + ip_vs_stats_release(&dest->stats); ip_vs_dest_put_and_free(dest); } @@ -822,7 +822,7 @@ static void ip_vs_stats_rcu_free(struct rcu_head *head) struct ip_vs_stats_rcu, rcu_head); - free_percpu(rs->s.cpustats); + ip_vs_stats_release(&rs->s); kfree(rs); } @@ -879,6 +879,47 @@ ip_vs_zero_stats(struct ip_vs_stats *stats) spin_unlock_bh(&stats->lock); } +/* Allocate fields after kzalloc */ +int ip_vs_stats_init_alloc(struct ip_vs_stats *s) +{ + int i; + + spin_lock_init(&s->lock); + s->cpustats = alloc_percpu(struct ip_vs_cpu_stats); + if (!s->cpustats) + return -ENOMEM; + + for_each_possible_cpu(i) { + struct ip_vs_cpu_stats *cs = per_cpu_ptr(s->cpustats, i); + + u64_stats_init(&cs->syncp); + } + return 0; +} + +struct ip_vs_stats *ip_vs_stats_alloc(void) +{ + struct ip_vs_stats *s = kzalloc(sizeof(*s), GFP_KERNEL); + + if (s && ip_vs_stats_init_alloc(s) >= 0) + return s; + kfree(s); + return NULL; +} + +void ip_vs_stats_release(struct ip_vs_stats *stats) +{ + free_percpu(stats->cpustats); +} + +void ip_vs_stats_free(struct ip_vs_stats *stats) +{ + if (stats) { + ip_vs_stats_release(stats); + kfree(stats); + } +} + /* * Update a destination in the given service */ @@ -978,14 +1019,13 @@ static int ip_vs_new_dest(struct ip_vs_service *svc, struct ip_vs_dest_user_kern *udest) { struct ip_vs_dest *dest; - unsigned int atype, i; + unsigned int atype; + int ret; EnterFunction(2); #ifdef CONFIG_IP_VS_IPV6 if (udest->af == AF_INET6) { - int ret; - atype = ipv6_addr_type(&udest->addr.in6); if ((!(atype & IPV6_ADDR_UNICAST) || atype & IPV6_ADDR_LINKLOCAL) && @@ -1007,16 +1047,10 @@ ip_vs_new_dest(struct ip_vs_service *svc, struct ip_vs_dest_user_kern *udest) if (dest == NULL) return -ENOMEM; - dest->stats.cpustats = alloc_percpu(struct ip_vs_cpu_stats); - if (!dest->stats.cpustats) + ret = ip_vs_stats_init_alloc(&dest->stats); + if (ret < 0) goto err_alloc; - for_each_possible_cpu(i) { - struct ip_vs_cpu_stats *ip_vs_dest_stats; - ip_vs_dest_stats = per_cpu_ptr(dest->stats.cpustats, i); - u64_stats_init(&ip_vs_dest_stats->syncp); - } - dest->af = udest->af; dest->protocol = svc->protocol; dest->vaddr = svc->addr; @@ -1032,7 +1066,6 @@ ip_vs_new_dest(struct ip_vs_service *svc, struct ip_vs_dest_user_kern *udest) INIT_HLIST_NODE(&dest->d_list); spin_lock_init(&dest->dst_lock); - spin_lock_init(&dest->stats.lock); __ip_vs_update_dest(svc, dest, udest, 1); LeaveFunction(2); @@ -1040,7 +1073,7 @@ ip_vs_new_dest(struct ip_vs_service *svc, struct ip_vs_dest_user_kern *udest) err_alloc: kfree(dest); - return -ENOMEM; + return ret; } @@ -1299,7 +1332,7 @@ static int ip_vs_add_service(struct netns_ipvs *ipvs, struct ip_vs_service_user_kern *u, struct ip_vs_service **svc_p) { - int ret = 0, i; + int ret = 0; struct ip_vs_scheduler *sched = NULL; struct ip_vs_pe *pe = NULL; struct ip_vs_service *svc = NULL; @@ -1359,18 +1392,9 @@ ip_vs_add_service(struct netns_ipvs *ipvs, struct ip_vs_service_user_kern *u, ret = -ENOMEM; goto out_err; } - svc->stats.cpustats = alloc_percpu(struct ip_vs_cpu_stats); - if (!svc->stats.cpustats) { - ret = -ENOMEM; + ret = ip_vs_stats_init_alloc(&svc->stats); + if (ret < 0) goto out_err; - } - - for_each_possible_cpu(i) { - struct ip_vs_cpu_stats *ip_vs_stats; - ip_vs_stats = per_cpu_ptr(svc->stats.cpustats, i); - u64_stats_init(&ip_vs_stats->syncp); - } - /* I'm the first user of the service */ atomic_set(&svc->refcnt, 0); @@ -1387,7 +1411,6 @@ ip_vs_add_service(struct netns_ipvs *ipvs, struct ip_vs_service_user_kern *u, INIT_LIST_HEAD(&svc->destinations); spin_lock_init(&svc->sched_lock); - spin_lock_init(&svc->stats.lock); /* Bind the scheduler */ if (sched) { @@ -4166,7 +4189,7 @@ static struct notifier_block ip_vs_dst_notifier = { int __net_init ip_vs_control_net_init(struct netns_ipvs *ipvs) { - int i, idx; + int idx; /* Initialize rs_table */ for (idx = 0; idx < IP_VS_RTAB_SIZE; idx++) @@ -4183,18 +4206,9 @@ int __net_init ip_vs_control_net_init(struct netns_ipvs *ipvs) ipvs->tot_stats = kzalloc(sizeof(*ipvs->tot_stats), GFP_KERNEL); if (!ipvs->tot_stats) return -ENOMEM; - ipvs->tot_stats->s.cpustats = alloc_percpu(struct ip_vs_cpu_stats); - if (!ipvs->tot_stats->s.cpustats) + if (ip_vs_stats_init_alloc(&ipvs->tot_stats->s) < 0) goto err_tot_stats; - for_each_possible_cpu(i) { - struct ip_vs_cpu_stats *ipvs_tot_stats; - ipvs_tot_stats = per_cpu_ptr(ipvs->tot_stats->s.cpustats, i); - u64_stats_init(&ipvs_tot_stats->syncp); - } - - spin_lock_init(&ipvs->tot_stats->s.lock); - #ifdef CONFIG_PROC_FS if (!proc_create_net("ip_vs", 0, ipvs->net->proc_net, &ip_vs_info_seq_ops, sizeof(struct ip_vs_iter))) @@ -4225,7 +4239,7 @@ err_stats: err_vs: #endif - free_percpu(ipvs->tot_stats->s.cpustats); + ip_vs_stats_release(&ipvs->tot_stats->s); err_tot_stats: kfree(ipvs->tot_stats); From 1dbd8d9a82e3f26b9d063292d47ece673f48fce2 Mon Sep 17 00:00:00 2001 From: Julian Anastasov Date: Tue, 22 Nov 2022 18:46:01 +0200 Subject: [PATCH 1839/2002] ipvs: use u64_stats_t for the per-cpu counters Use the provided u64_stats_t type to avoid load/store tearing. Fixes: 316580b69d0a ("u64_stats: provide u64_stats_t type") Signed-off-by: Julian Anastasov Cc: yunhong-cgl jiang Cc: "dust.li" Reviewed-by: Jiri Wiesner Tested-by: Jiri Wiesner Signed-off-by: Pablo Neira Ayuso --- include/net/ip_vs.h | 10 +++++----- net/netfilter/ipvs/ip_vs_core.c | 30 +++++++++++++++--------------- net/netfilter/ipvs/ip_vs_ctl.c | 10 +++++----- net/netfilter/ipvs/ip_vs_est.c | 20 ++++++++++---------- 4 files changed, 35 insertions(+), 35 deletions(-) diff --git a/include/net/ip_vs.h b/include/net/ip_vs.h index e5582c01a4a3..a4d44138c2a8 100644 --- a/include/net/ip_vs.h +++ b/include/net/ip_vs.h @@ -351,11 +351,11 @@ struct ip_vs_seq { /* counters per cpu */ struct ip_vs_counters { - __u64 conns; /* connections scheduled */ - __u64 inpkts; /* incoming packets */ - __u64 outpkts; /* outgoing packets */ - __u64 inbytes; /* incoming bytes */ - __u64 outbytes; /* outgoing bytes */ + u64_stats_t conns; /* connections scheduled */ + u64_stats_t inpkts; /* incoming packets */ + u64_stats_t outpkts; /* outgoing packets */ + u64_stats_t inbytes; /* incoming bytes */ + u64_stats_t outbytes; /* outgoing bytes */ }; /* Stats per cpu */ struct ip_vs_cpu_stats { diff --git a/net/netfilter/ipvs/ip_vs_core.c b/net/netfilter/ipvs/ip_vs_core.c index fcdaef1fcccf..2fcc26507d69 100644 --- a/net/netfilter/ipvs/ip_vs_core.c +++ b/net/netfilter/ipvs/ip_vs_core.c @@ -132,21 +132,21 @@ ip_vs_in_stats(struct ip_vs_conn *cp, struct sk_buff *skb) s = this_cpu_ptr(dest->stats.cpustats); u64_stats_update_begin(&s->syncp); - s->cnt.inpkts++; - s->cnt.inbytes += skb->len; + u64_stats_inc(&s->cnt.inpkts); + u64_stats_add(&s->cnt.inbytes, skb->len); u64_stats_update_end(&s->syncp); svc = rcu_dereference(dest->svc); s = this_cpu_ptr(svc->stats.cpustats); u64_stats_update_begin(&s->syncp); - s->cnt.inpkts++; - s->cnt.inbytes += skb->len; + u64_stats_inc(&s->cnt.inpkts); + u64_stats_add(&s->cnt.inbytes, skb->len); u64_stats_update_end(&s->syncp); s = this_cpu_ptr(ipvs->tot_stats->s.cpustats); u64_stats_update_begin(&s->syncp); - s->cnt.inpkts++; - s->cnt.inbytes += skb->len; + u64_stats_inc(&s->cnt.inpkts); + u64_stats_add(&s->cnt.inbytes, skb->len); u64_stats_update_end(&s->syncp); local_bh_enable(); @@ -168,21 +168,21 @@ ip_vs_out_stats(struct ip_vs_conn *cp, struct sk_buff *skb) s = this_cpu_ptr(dest->stats.cpustats); u64_stats_update_begin(&s->syncp); - s->cnt.outpkts++; - s->cnt.outbytes += skb->len; + u64_stats_inc(&s->cnt.outpkts); + u64_stats_add(&s->cnt.outbytes, skb->len); u64_stats_update_end(&s->syncp); svc = rcu_dereference(dest->svc); s = this_cpu_ptr(svc->stats.cpustats); u64_stats_update_begin(&s->syncp); - s->cnt.outpkts++; - s->cnt.outbytes += skb->len; + u64_stats_inc(&s->cnt.outpkts); + u64_stats_add(&s->cnt.outbytes, skb->len); u64_stats_update_end(&s->syncp); s = this_cpu_ptr(ipvs->tot_stats->s.cpustats); u64_stats_update_begin(&s->syncp); - s->cnt.outpkts++; - s->cnt.outbytes += skb->len; + u64_stats_inc(&s->cnt.outpkts); + u64_stats_add(&s->cnt.outbytes, skb->len); u64_stats_update_end(&s->syncp); local_bh_enable(); @@ -200,17 +200,17 @@ ip_vs_conn_stats(struct ip_vs_conn *cp, struct ip_vs_service *svc) s = this_cpu_ptr(cp->dest->stats.cpustats); u64_stats_update_begin(&s->syncp); - s->cnt.conns++; + u64_stats_inc(&s->cnt.conns); u64_stats_update_end(&s->syncp); s = this_cpu_ptr(svc->stats.cpustats); u64_stats_update_begin(&s->syncp); - s->cnt.conns++; + u64_stats_inc(&s->cnt.conns); u64_stats_update_end(&s->syncp); s = this_cpu_ptr(ipvs->tot_stats->s.cpustats); u64_stats_update_begin(&s->syncp); - s->cnt.conns++; + u64_stats_inc(&s->cnt.conns); u64_stats_update_end(&s->syncp); local_bh_enable(); diff --git a/net/netfilter/ipvs/ip_vs_ctl.c b/net/netfilter/ipvs/ip_vs_ctl.c index ec6db864ac36..5f9cc2e7ba71 100644 --- a/net/netfilter/ipvs/ip_vs_ctl.c +++ b/net/netfilter/ipvs/ip_vs_ctl.c @@ -2335,11 +2335,11 @@ static int ip_vs_stats_percpu_show(struct seq_file *seq, void *v) do { start = u64_stats_fetch_begin(&u->syncp); - conns = u->cnt.conns; - inpkts = u->cnt.inpkts; - outpkts = u->cnt.outpkts; - inbytes = u->cnt.inbytes; - outbytes = u->cnt.outbytes; + conns = u64_stats_read(&u->cnt.conns); + inpkts = u64_stats_read(&u->cnt.inpkts); + outpkts = u64_stats_read(&u->cnt.outpkts); + inbytes = u64_stats_read(&u->cnt.inbytes); + outbytes = u64_stats_read(&u->cnt.outbytes); } while (u64_stats_fetch_retry(&u->syncp, start)); seq_printf(seq, "%3X %8LX %8LX %8LX %16LX %16LX\n", diff --git a/net/netfilter/ipvs/ip_vs_est.c b/net/netfilter/ipvs/ip_vs_est.c index 9a1a7af6a186..f53150d82a92 100644 --- a/net/netfilter/ipvs/ip_vs_est.c +++ b/net/netfilter/ipvs/ip_vs_est.c @@ -67,11 +67,11 @@ static void ip_vs_read_cpu_stats(struct ip_vs_kstats *sum, if (add) { do { start = u64_stats_fetch_begin(&s->syncp); - conns = s->cnt.conns; - inpkts = s->cnt.inpkts; - outpkts = s->cnt.outpkts; - inbytes = s->cnt.inbytes; - outbytes = s->cnt.outbytes; + conns = u64_stats_read(&s->cnt.conns); + inpkts = u64_stats_read(&s->cnt.inpkts); + outpkts = u64_stats_read(&s->cnt.outpkts); + inbytes = u64_stats_read(&s->cnt.inbytes); + outbytes = u64_stats_read(&s->cnt.outbytes); } while (u64_stats_fetch_retry(&s->syncp, start)); sum->conns += conns; sum->inpkts += inpkts; @@ -82,11 +82,11 @@ static void ip_vs_read_cpu_stats(struct ip_vs_kstats *sum, add = true; do { start = u64_stats_fetch_begin(&s->syncp); - sum->conns = s->cnt.conns; - sum->inpkts = s->cnt.inpkts; - sum->outpkts = s->cnt.outpkts; - sum->inbytes = s->cnt.inbytes; - sum->outbytes = s->cnt.outbytes; + sum->conns = u64_stats_read(&s->cnt.conns); + sum->inpkts = u64_stats_read(&s->cnt.inpkts); + sum->outpkts = u64_stats_read(&s->cnt.outpkts); + sum->inbytes = u64_stats_read(&s->cnt.inbytes); + sum->outbytes = u64_stats_read(&s->cnt.outbytes); } while (u64_stats_fetch_retry(&s->syncp, start)); } } From 705dd34440812735ece298eb5bc153fde9544d42 Mon Sep 17 00:00:00 2001 From: Julian Anastasov Date: Tue, 22 Nov 2022 18:46:02 +0200 Subject: [PATCH 1840/2002] ipvs: use kthreads for stats estimation Estimating all entries in single list in timer context by single CPU causes large latency with multiple IPVS rules as reported in [1], [2], [3]. Spread the estimator structures in multiple chains and use kthread(s) for the estimation. The chains are processed in multiple (50) timer ticks to ensure the 2-second interval between estimations with some accuracy. Every chain is processed under RCU lock. Every kthread works over its own data structure and all such contexts are attached to array. The contexts can be preserved while the kthread tasks are stopped or restarted. When estimators are removed, unused kthread contexts are released and the slots in array are left empty. First kthread determines parameters to use, eg. maximum number of estimators to process per kthread based on chain's length (chain_max), allowing sub-100us cond_resched rate and estimation taking up to 1/8 of the CPU capacity to avoid any problems if chain_max is not correctly calculated. chain_max is calculated taking into account factors such as CPU speed and memory/cache speed where the cache_factor (4) is selected from real tests with current generation of CPU/NUMA configurations to correct the difference in CPU usage between cached (during calc phase) and non-cached (working) state of the estimated per-cpu data. First kthread also plays the role of distributor of added estimators to all kthreads, keeping low the time to add estimators. The optimization is based on the fact that newly added estimator should be estimated after 2 seconds, so we have the time to offload the adding to chain from controlling process to kthread 0. The allocated kthread context may grow from 1 to 50 allocated structures for timer ticks which saves memory for setups with small number of estimators. We also add delayed work est_reload_work that will make sure the kthread tasks are properly started/stopped. ip_vs_start_estimator() is changed to report errors which allows to safely store the estimators in allocated structures. Many thanks to Jiri Wiesner for his valuable comments and for spending a lot of time reviewing and testing the changes on different platforms with 48-256 CPUs and 1-8 NUMA nodes under different cpufreq governors. [1] Report from Yunhong Jiang: https://lore.kernel.org/netdev/D25792C1-1B89-45DE-9F10-EC350DC04ADC@gmail.com/ [2] https://marc.info/?l=linux-virtual-server&m=159679809118027&w=2 [3] Report from Dust: https://archive.linuxvirtualserver.org/html/lvs-devel/2020-12/msg00000.html Signed-off-by: Julian Anastasov Cc: yunhong-cgl jiang Cc: "dust.li" Reviewed-by: Jiri Wiesner Tested-by: Jiri Wiesner Signed-off-by: Pablo Neira Ayuso --- include/net/ip_vs.h | 88 +++- net/netfilter/ipvs/ip_vs_ctl.c | 126 ++++- net/netfilter/ipvs/ip_vs_est.c | 878 ++++++++++++++++++++++++++++++--- 3 files changed, 991 insertions(+), 101 deletions(-) diff --git a/include/net/ip_vs.h b/include/net/ip_vs.h index a4d44138c2a8..04960dc6228f 100644 --- a/include/net/ip_vs.h +++ b/include/net/ip_vs.h @@ -42,6 +42,8 @@ static inline struct netns_ipvs *net_ipvs(struct net* net) /* Connections' size value needed by ip_vs_ctl.c */ extern int ip_vs_conn_tab_size; +extern struct mutex __ip_vs_mutex; + struct ip_vs_iphdr { int hdr_flags; /* ipvs flags */ __u32 off; /* Where IP or IPv4 header starts */ @@ -365,7 +367,7 @@ struct ip_vs_cpu_stats { /* IPVS statistics objects */ struct ip_vs_estimator { - struct list_head list; + struct hlist_node list; u64 last_inbytes; u64 last_outbytes; @@ -378,6 +380,10 @@ struct ip_vs_estimator { u64 outpps; u64 inbps; u64 outbps; + + s32 ktid:16, /* kthread ID, -1=temp list */ + ktrow:8, /* row/tick ID for kthread */ + ktcid:8; /* chain ID for kthread tick */ }; /* @@ -415,6 +421,66 @@ struct ip_vs_stats *ip_vs_stats_alloc(void); void ip_vs_stats_release(struct ip_vs_stats *stats); void ip_vs_stats_free(struct ip_vs_stats *stats); +/* Process estimators in multiple timer ticks (20/50/100, see ktrow) */ +#define IPVS_EST_NTICKS 50 +/* Estimation uses a 2-second period containing ticks (in jiffies) */ +#define IPVS_EST_TICK ((2 * HZ) / IPVS_EST_NTICKS) + +/* Limit of CPU load per kthread (8 for 12.5%), ratio of CPU capacity (1/C). + * Value of 4 and above ensures kthreads will take work without exceeding + * the CPU capacity under different circumstances. + */ +#define IPVS_EST_LOAD_DIVISOR 8 + +/* Kthreads should not have work that exceeds the CPU load above 50% */ +#define IPVS_EST_CPU_KTHREADS (IPVS_EST_LOAD_DIVISOR / 2) + +/* Desired number of chains per timer tick (chain load factor in 100us units), + * 48=4.8ms of 40ms tick (12% CPU usage): + * 2 sec * 1000 ms in sec * 10 (100us in ms) / 8 (12.5%) / 50 + */ +#define IPVS_EST_CHAIN_FACTOR \ + ALIGN_DOWN(2 * 1000 * 10 / IPVS_EST_LOAD_DIVISOR / IPVS_EST_NTICKS, 8) + +/* Compiled number of chains per tick + * The defines should match cond_resched_rcu + */ +#if defined(CONFIG_DEBUG_ATOMIC_SLEEP) || !defined(CONFIG_PREEMPT_RCU) +#define IPVS_EST_TICK_CHAINS IPVS_EST_CHAIN_FACTOR +#else +#define IPVS_EST_TICK_CHAINS 1 +#endif + +#if IPVS_EST_NTICKS > 127 +#error Too many timer ticks for ktrow +#endif + +/* Multiple chains processed in same tick */ +struct ip_vs_est_tick_data { + struct hlist_head chains[IPVS_EST_TICK_CHAINS]; + DECLARE_BITMAP(present, IPVS_EST_TICK_CHAINS); + DECLARE_BITMAP(full, IPVS_EST_TICK_CHAINS); + int chain_len[IPVS_EST_TICK_CHAINS]; +}; + +/* Context for estimation kthread */ +struct ip_vs_est_kt_data { + struct netns_ipvs *ipvs; + struct task_struct *task; /* task if running */ + struct ip_vs_est_tick_data __rcu *ticks[IPVS_EST_NTICKS]; + DECLARE_BITMAP(avail, IPVS_EST_NTICKS); /* tick has space for ests */ + unsigned long est_timer; /* estimation timer (jiffies) */ + struct ip_vs_stats *calc_stats; /* Used for calculation */ + int tick_len[IPVS_EST_NTICKS]; /* est count */ + int id; /* ktid per netns */ + int chain_max; /* max ests per tick chain */ + int tick_max; /* max ests per tick */ + int est_count; /* attached ests to kthread */ + int est_max_count; /* max ests per kthread */ + int add_row; /* row for new ests */ + int est_row; /* estimated row */ +}; + struct dst_entry; struct iphdr; struct ip_vs_conn; @@ -953,9 +1019,17 @@ struct netns_ipvs { struct ctl_table_header *lblcr_ctl_header; struct ctl_table *lblcr_ctl_table; /* ip_vs_est */ - struct list_head est_list; /* estimator list */ - spinlock_t est_lock; - struct timer_list est_timer; /* Estimation timer */ + struct delayed_work est_reload_work;/* Reload kthread tasks */ + struct mutex est_mutex; /* protect kthread tasks */ + struct hlist_head est_temp_list; /* Ests during calc phase */ + struct ip_vs_est_kt_data **est_kt_arr; /* Array of kthread data ptrs */ + unsigned long est_max_threads;/* Hard limit of kthreads */ + int est_calc_phase; /* Calculation phase */ + int est_chain_max; /* Calculated chain_max */ + int est_kt_count; /* Allocated ptrs */ + int est_add_ktid; /* ktid where to add ests */ + atomic_t est_genid; /* kthreads reload genid */ + atomic_t est_genid_done; /* applied genid */ /* ip_vs_sync */ spinlock_t sync_lock; struct ipvs_master_sync_state *ms; @@ -1486,10 +1560,14 @@ int stop_sync_thread(struct netns_ipvs *ipvs, int state); void ip_vs_sync_conn(struct netns_ipvs *ipvs, struct ip_vs_conn *cp, int pkts); /* IPVS rate estimator prototypes (from ip_vs_est.c) */ -void ip_vs_start_estimator(struct netns_ipvs *ipvs, struct ip_vs_stats *stats); +int ip_vs_start_estimator(struct netns_ipvs *ipvs, struct ip_vs_stats *stats); void ip_vs_stop_estimator(struct netns_ipvs *ipvs, struct ip_vs_stats *stats); void ip_vs_zero_estimator(struct ip_vs_stats *stats); void ip_vs_read_estimator(struct ip_vs_kstats *dst, struct ip_vs_stats *stats); +void ip_vs_est_reload_start(struct netns_ipvs *ipvs); +int ip_vs_est_kthread_start(struct netns_ipvs *ipvs, + struct ip_vs_est_kt_data *kd); +void ip_vs_est_kthread_stop(struct ip_vs_est_kt_data *kd); /* Various IPVS packet transmitters (from ip_vs_xmit.c) */ int ip_vs_null_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, diff --git a/net/netfilter/ipvs/ip_vs_ctl.c b/net/netfilter/ipvs/ip_vs_ctl.c index 5f9cc2e7ba71..c41a5392edc9 100644 --- a/net/netfilter/ipvs/ip_vs_ctl.c +++ b/net/netfilter/ipvs/ip_vs_ctl.c @@ -49,8 +49,7 @@ MODULE_ALIAS_GENL_FAMILY(IPVS_GENL_NAME); -/* semaphore for IPVS sockopts. And, [gs]etsockopt may sleep. */ -static DEFINE_MUTEX(__ip_vs_mutex); +DEFINE_MUTEX(__ip_vs_mutex); /* Serialize configuration with sockopt/netlink */ /* sysctl variables */ @@ -241,6 +240,47 @@ static void defense_work_handler(struct work_struct *work) } #endif +static void est_reload_work_handler(struct work_struct *work) +{ + struct netns_ipvs *ipvs = + container_of(work, struct netns_ipvs, est_reload_work.work); + int genid_done = atomic_read(&ipvs->est_genid_done); + unsigned long delay = HZ / 10; /* repeat startups after failure */ + bool repeat = false; + int genid; + int id; + + mutex_lock(&ipvs->est_mutex); + genid = atomic_read(&ipvs->est_genid); + for (id = 0; id < ipvs->est_kt_count; id++) { + struct ip_vs_est_kt_data *kd = ipvs->est_kt_arr[id]; + + /* netns clean up started, abort delayed work */ + if (!ipvs->enable) + goto unlock; + if (!kd) + continue; + /* New config ? Stop kthread tasks */ + if (genid != genid_done) + ip_vs_est_kthread_stop(kd); + if (!kd->task) { + /* Do not start kthreads above 0 in calc phase */ + if ((!id || !ipvs->est_calc_phase) && + ip_vs_est_kthread_start(ipvs, kd) < 0) + repeat = true; + } + } + + atomic_set(&ipvs->est_genid_done, genid); + + if (repeat) + queue_delayed_work(system_long_wq, &ipvs->est_reload_work, + delay); + +unlock: + mutex_unlock(&ipvs->est_mutex); +} + int ip_vs_use_count_inc(void) { @@ -831,7 +871,7 @@ ip_vs_copy_stats(struct ip_vs_kstats *dst, struct ip_vs_stats *src) { #define IP_VS_SHOW_STATS_COUNTER(c) dst->c = src->kstats.c - src->kstats0.c - spin_lock_bh(&src->lock); + spin_lock(&src->lock); IP_VS_SHOW_STATS_COUNTER(conns); IP_VS_SHOW_STATS_COUNTER(inpkts); @@ -841,7 +881,7 @@ ip_vs_copy_stats(struct ip_vs_kstats *dst, struct ip_vs_stats *src) ip_vs_read_estimator(dst, src); - spin_unlock_bh(&src->lock); + spin_unlock(&src->lock); } static void @@ -862,7 +902,7 @@ ip_vs_export_stats_user(struct ip_vs_stats_user *dst, struct ip_vs_kstats *src) static void ip_vs_zero_stats(struct ip_vs_stats *stats) { - spin_lock_bh(&stats->lock); + spin_lock(&stats->lock); /* get current counters as zero point, rates are zeroed */ @@ -876,7 +916,7 @@ ip_vs_zero_stats(struct ip_vs_stats *stats) ip_vs_zero_estimator(stats); - spin_unlock_bh(&stats->lock); + spin_unlock(&stats->lock); } /* Allocate fields after kzalloc */ @@ -998,7 +1038,6 @@ __ip_vs_update_dest(struct ip_vs_service *svc, struct ip_vs_dest *dest, spin_unlock_bh(&dest->dst_lock); if (add) { - ip_vs_start_estimator(svc->ipvs, &dest->stats); list_add_rcu(&dest->n_list, &svc->destinations); svc->num_dests++; sched = rcu_dereference_protected(svc->scheduler, 1); @@ -1051,6 +1090,10 @@ ip_vs_new_dest(struct ip_vs_service *svc, struct ip_vs_dest_user_kern *udest) if (ret < 0) goto err_alloc; + ret = ip_vs_start_estimator(svc->ipvs, &dest->stats); + if (ret < 0) + goto err_stats; + dest->af = udest->af; dest->protocol = svc->protocol; dest->vaddr = svc->addr; @@ -1071,6 +1114,9 @@ ip_vs_new_dest(struct ip_vs_service *svc, struct ip_vs_dest_user_kern *udest) LeaveFunction(2); return 0; +err_stats: + ip_vs_stats_release(&dest->stats); + err_alloc: kfree(dest); return ret; @@ -1135,14 +1181,18 @@ ip_vs_add_dest(struct ip_vs_service *svc, struct ip_vs_dest_user_kern *udest) IP_VS_DBG_ADDR(svc->af, &dest->vaddr), ntohs(dest->vport)); + ret = ip_vs_start_estimator(svc->ipvs, &dest->stats); + if (ret < 0) + goto err; __ip_vs_update_dest(svc, dest, udest, 1); - ret = 0; } else { /* * Allocate and initialize the dest structure */ ret = ip_vs_new_dest(svc, udest); } + +err: LeaveFunction(2); return ret; @@ -1420,6 +1470,10 @@ ip_vs_add_service(struct netns_ipvs *ipvs, struct ip_vs_service_user_kern *u, sched = NULL; } + ret = ip_vs_start_estimator(ipvs, &svc->stats); + if (ret < 0) + goto out_err; + /* Bind the ct retriever */ RCU_INIT_POINTER(svc->pe, pe); pe = NULL; @@ -1432,8 +1486,6 @@ ip_vs_add_service(struct netns_ipvs *ipvs, struct ip_vs_service_user_kern *u, if (svc->pe && svc->pe->conn_out) atomic_inc(&ipvs->conn_out_counter); - ip_vs_start_estimator(ipvs, &svc->stats); - /* Count only IPv4 services for old get/setsockopt interface */ if (svc->af == AF_INET) ipvs->num_services++; @@ -1444,8 +1496,15 @@ ip_vs_add_service(struct netns_ipvs *ipvs, struct ip_vs_service_user_kern *u, ip_vs_svc_hash(svc); *svc_p = svc; - /* Now there is a service - full throttle */ - ipvs->enable = 1; + + if (!ipvs->enable) { + /* Now there is a service - full throttle */ + ipvs->enable = 1; + + /* Start estimation for first time */ + ip_vs_est_reload_start(ipvs); + } + return 0; @@ -4065,13 +4124,16 @@ static void ip_vs_genl_unregister(void) static int __net_init ip_vs_control_net_init_sysctl(struct netns_ipvs *ipvs) { struct net *net = ipvs->net; - int idx; struct ctl_table *tbl; + int idx, ret; atomic_set(&ipvs->dropentry, 0); spin_lock_init(&ipvs->dropentry_lock); spin_lock_init(&ipvs->droppacket_lock); spin_lock_init(&ipvs->securetcp_lock); + INIT_DELAYED_WORK(&ipvs->defense_work, defense_work_handler); + INIT_DELAYED_WORK(&ipvs->expire_nodest_conn_work, + expire_nodest_conn_handler); if (!net_eq(net, &init_net)) { tbl = kmemdup(vs_vars, sizeof(vs_vars), GFP_KERNEL); @@ -4139,24 +4201,27 @@ static int __net_init ip_vs_control_net_init_sysctl(struct netns_ipvs *ipvs) tbl[idx++].mode = 0444; #endif + ret = -ENOMEM; ipvs->sysctl_hdr = register_net_sysctl(net, "net/ipv4/vs", tbl); - if (ipvs->sysctl_hdr == NULL) { - if (!net_eq(net, &init_net)) - kfree(tbl); - return -ENOMEM; - } + if (!ipvs->sysctl_hdr) + goto err; ipvs->sysctl_tbl = tbl; + + ret = ip_vs_start_estimator(ipvs, &ipvs->tot_stats->s); + if (ret < 0) + goto err; + /* Schedule defense work */ - INIT_DELAYED_WORK(&ipvs->defense_work, defense_work_handler); queue_delayed_work(system_long_wq, &ipvs->defense_work, DEFENSE_TIMER_PERIOD); - /* Init delayed work for expiring no dest conn */ - INIT_DELAYED_WORK(&ipvs->expire_nodest_conn_work, - expire_nodest_conn_handler); - - ip_vs_start_estimator(ipvs, &ipvs->tot_stats->s); return 0; + +err: + unregister_net_sysctl_table(ipvs->sysctl_hdr); + if (!net_eq(net, &init_net)) + kfree(tbl); + return ret; } static void __net_exit ip_vs_control_net_cleanup_sysctl(struct netns_ipvs *ipvs) @@ -4189,6 +4254,7 @@ static struct notifier_block ip_vs_dst_notifier = { int __net_init ip_vs_control_net_init(struct netns_ipvs *ipvs) { + int ret = -ENOMEM; int idx; /* Initialize rs_table */ @@ -4202,10 +4268,12 @@ int __net_init ip_vs_control_net_init(struct netns_ipvs *ipvs) atomic_set(&ipvs->nullsvc_counter, 0); atomic_set(&ipvs->conn_out_counter, 0); + INIT_DELAYED_WORK(&ipvs->est_reload_work, est_reload_work_handler); + /* procfs stats */ ipvs->tot_stats = kzalloc(sizeof(*ipvs->tot_stats), GFP_KERNEL); if (!ipvs->tot_stats) - return -ENOMEM; + goto out; if (ip_vs_stats_init_alloc(&ipvs->tot_stats->s) < 0) goto err_tot_stats; @@ -4222,7 +4290,8 @@ int __net_init ip_vs_control_net_init(struct netns_ipvs *ipvs) goto err_percpu; #endif - if (ip_vs_control_net_init_sysctl(ipvs)) + ret = ip_vs_control_net_init_sysctl(ipvs); + if (ret < 0) goto err; return 0; @@ -4243,13 +4312,16 @@ err_vs: err_tot_stats: kfree(ipvs->tot_stats); - return -ENOMEM; + +out: + return ret; } void __net_exit ip_vs_control_net_cleanup(struct netns_ipvs *ipvs) { ip_vs_trash_cleanup(ipvs); ip_vs_control_net_cleanup_sysctl(ipvs); + cancel_delayed_work_sync(&ipvs->est_reload_work); #ifdef CONFIG_PROC_FS remove_proc_entry("ip_vs_stats_percpu", ipvs->net->proc_net); remove_proc_entry("ip_vs_stats", ipvs->net->proc_net); diff --git a/net/netfilter/ipvs/ip_vs_est.c b/net/netfilter/ipvs/ip_vs_est.c index f53150d82a92..2fb6c097437c 100644 --- a/net/netfilter/ipvs/ip_vs_est.c +++ b/net/netfilter/ipvs/ip_vs_est.c @@ -30,9 +30,6 @@ long interval, it is easy to implement a user level daemon which periodically reads those statistical counters and measure rate. - Currently, the measurement is activated by slow timer handler. Hope - this measurement will not introduce too much load. - We measure rate during the last 8 seconds every 2 seconds: avgrate = avgrate*(1-W) + rate*W @@ -47,68 +44,76 @@ to 32-bit values for conns, packets, bps, cps and pps. * A lot of code is taken from net/core/gen_estimator.c + + KEY POINTS: + - cpustats counters are updated per-cpu in SoftIRQ context with BH disabled + - kthreads read the cpustats to update the estimators (svcs, dests, total) + - the states of estimators can be read (get stats) or modified (zero stats) + from processes + + KTHREADS: + - estimators are added initially to est_temp_list and later kthread 0 + distributes them to one or many kthreads for estimation + - kthread contexts are created and attached to array + - the kthread tasks are started when first service is added, before that + the total stats are not estimated + - the kthread context holds lists with estimators (chains) which are + processed every 2 seconds + - as estimators can be added dynamically and in bursts, we try to spread + them to multiple chains which are estimated at different time + - on start, kthread 0 enters calculation phase to determine the chain limits + and the limit of estimators per kthread + - est_add_ktid: ktid where to add new ests, can point to empty slot where + we should add kt data */ +static struct lock_class_key __ipvs_est_key; -/* - * Make a summary from each cpu - */ -static void ip_vs_read_cpu_stats(struct ip_vs_kstats *sum, - struct ip_vs_cpu_stats __percpu *stats) -{ - int i; - bool add = false; +static void ip_vs_est_calc_phase(struct netns_ipvs *ipvs); +static void ip_vs_est_drain_temp_list(struct netns_ipvs *ipvs); - for_each_possible_cpu(i) { - struct ip_vs_cpu_stats *s = per_cpu_ptr(stats, i); - unsigned int start; - u64 conns, inpkts, outpkts, inbytes, outbytes; - - if (add) { - do { - start = u64_stats_fetch_begin(&s->syncp); - conns = u64_stats_read(&s->cnt.conns); - inpkts = u64_stats_read(&s->cnt.inpkts); - outpkts = u64_stats_read(&s->cnt.outpkts); - inbytes = u64_stats_read(&s->cnt.inbytes); - outbytes = u64_stats_read(&s->cnt.outbytes); - } while (u64_stats_fetch_retry(&s->syncp, start)); - sum->conns += conns; - sum->inpkts += inpkts; - sum->outpkts += outpkts; - sum->inbytes += inbytes; - sum->outbytes += outbytes; - } else { - add = true; - do { - start = u64_stats_fetch_begin(&s->syncp); - sum->conns = u64_stats_read(&s->cnt.conns); - sum->inpkts = u64_stats_read(&s->cnt.inpkts); - sum->outpkts = u64_stats_read(&s->cnt.outpkts); - sum->inbytes = u64_stats_read(&s->cnt.inbytes); - sum->outbytes = u64_stats_read(&s->cnt.outbytes); - } while (u64_stats_fetch_retry(&s->syncp, start)); - } - } -} - - -static void estimation_timer(struct timer_list *t) +static void ip_vs_chain_estimation(struct hlist_head *chain) { struct ip_vs_estimator *e; + struct ip_vs_cpu_stats *c; struct ip_vs_stats *s; u64 rate; - struct netns_ipvs *ipvs = from_timer(ipvs, t, est_timer); - if (!sysctl_run_estimation(ipvs)) - goto skip; + hlist_for_each_entry_rcu(e, chain, list) { + u64 conns, inpkts, outpkts, inbytes, outbytes; + u64 kconns = 0, kinpkts = 0, koutpkts = 0; + u64 kinbytes = 0, koutbytes = 0; + unsigned int start; + int i; + + if (kthread_should_stop()) + break; - spin_lock(&ipvs->est_lock); - list_for_each_entry(e, &ipvs->est_list, list) { s = container_of(e, struct ip_vs_stats, est); + for_each_possible_cpu(i) { + c = per_cpu_ptr(s->cpustats, i); + do { + start = u64_stats_fetch_begin(&c->syncp); + conns = u64_stats_read(&c->cnt.conns); + inpkts = u64_stats_read(&c->cnt.inpkts); + outpkts = u64_stats_read(&c->cnt.outpkts); + inbytes = u64_stats_read(&c->cnt.inbytes); + outbytes = u64_stats_read(&c->cnt.outbytes); + } while (u64_stats_fetch_retry(&c->syncp, start)); + kconns += conns; + kinpkts += inpkts; + koutpkts += outpkts; + kinbytes += inbytes; + koutbytes += outbytes; + } spin_lock(&s->lock); - ip_vs_read_cpu_stats(&s->kstats, s->cpustats); + + s->kstats.conns = kconns; + s->kstats.inpkts = kinpkts; + s->kstats.outpkts = koutpkts; + s->kstats.inbytes = kinbytes; + s->kstats.outbytes = koutbytes; /* scaled by 2^10, but divided 2 seconds */ rate = (s->kstats.conns - e->last_conns) << 9; @@ -133,30 +138,754 @@ static void estimation_timer(struct timer_list *t) e->outbps += ((s64)rate - (s64)e->outbps) >> 2; spin_unlock(&s->lock); } - spin_unlock(&ipvs->est_lock); - -skip: - mod_timer(&ipvs->est_timer, jiffies + 2*HZ); } -void ip_vs_start_estimator(struct netns_ipvs *ipvs, struct ip_vs_stats *stats) +static void ip_vs_tick_estimation(struct ip_vs_est_kt_data *kd, int row) +{ + struct ip_vs_est_tick_data *td; + int cid; + + rcu_read_lock(); + td = rcu_dereference(kd->ticks[row]); + if (!td) + goto out; + for_each_set_bit(cid, td->present, IPVS_EST_TICK_CHAINS) { + if (kthread_should_stop()) + break; + ip_vs_chain_estimation(&td->chains[cid]); + cond_resched_rcu(); + td = rcu_dereference(kd->ticks[row]); + if (!td) + break; + } + +out: + rcu_read_unlock(); +} + +static int ip_vs_estimation_kthread(void *data) +{ + struct ip_vs_est_kt_data *kd = data; + struct netns_ipvs *ipvs = kd->ipvs; + int row = kd->est_row; + unsigned long now; + int id = kd->id; + long gap; + + if (id > 0) { + if (!ipvs->est_chain_max) + return 0; + } else { + if (!ipvs->est_chain_max) { + ipvs->est_calc_phase = 1; + /* commit est_calc_phase before reading est_genid */ + smp_mb(); + } + + /* kthread 0 will handle the calc phase */ + if (ipvs->est_calc_phase) + ip_vs_est_calc_phase(ipvs); + } + + while (1) { + if (!id && !hlist_empty(&ipvs->est_temp_list)) + ip_vs_est_drain_temp_list(ipvs); + set_current_state(TASK_IDLE); + if (kthread_should_stop()) + break; + + /* before estimation, check if we should sleep */ + now = jiffies; + gap = kd->est_timer - now; + if (gap > 0) { + if (gap > IPVS_EST_TICK) { + kd->est_timer = now - IPVS_EST_TICK; + gap = IPVS_EST_TICK; + } + schedule_timeout(gap); + } else { + __set_current_state(TASK_RUNNING); + if (gap < -8 * IPVS_EST_TICK) + kd->est_timer = now; + } + + if (sysctl_run_estimation(ipvs) && kd->tick_len[row]) + ip_vs_tick_estimation(kd, row); + + row++; + if (row >= IPVS_EST_NTICKS) + row = 0; + WRITE_ONCE(kd->est_row, row); + kd->est_timer += IPVS_EST_TICK; + } + __set_current_state(TASK_RUNNING); + + return 0; +} + +/* Schedule stop/start for kthread tasks */ +void ip_vs_est_reload_start(struct netns_ipvs *ipvs) +{ + /* Ignore reloads before first service is added */ + if (!ipvs->enable) + return; + /* Bump the kthread configuration genid */ + atomic_inc(&ipvs->est_genid); + queue_delayed_work(system_long_wq, &ipvs->est_reload_work, 0); +} + +/* Start kthread task with current configuration */ +int ip_vs_est_kthread_start(struct netns_ipvs *ipvs, + struct ip_vs_est_kt_data *kd) +{ + unsigned long now; + int ret = 0; + long gap; + + lockdep_assert_held(&ipvs->est_mutex); + + if (kd->task) + goto out; + now = jiffies; + gap = kd->est_timer - now; + /* Sync est_timer if task is starting later */ + if (abs(gap) > 4 * IPVS_EST_TICK) + kd->est_timer = now; + kd->task = kthread_create(ip_vs_estimation_kthread, kd, "ipvs-e:%d:%d", + ipvs->gen, kd->id); + if (IS_ERR(kd->task)) { + ret = PTR_ERR(kd->task); + kd->task = NULL; + goto out; + } + + pr_info("starting estimator thread %d...\n", kd->id); + wake_up_process(kd->task); + +out: + return ret; +} + +void ip_vs_est_kthread_stop(struct ip_vs_est_kt_data *kd) +{ + if (kd->task) { + pr_info("stopping estimator thread %d...\n", kd->id); + kthread_stop(kd->task); + kd->task = NULL; + } +} + +/* Apply parameters to kthread */ +static void ip_vs_est_set_params(struct netns_ipvs *ipvs, + struct ip_vs_est_kt_data *kd) +{ + kd->chain_max = ipvs->est_chain_max; + /* We are using single chain on RCU preemption */ + if (IPVS_EST_TICK_CHAINS == 1) + kd->chain_max *= IPVS_EST_CHAIN_FACTOR; + kd->tick_max = IPVS_EST_TICK_CHAINS * kd->chain_max; + kd->est_max_count = IPVS_EST_NTICKS * kd->tick_max; +} + +/* Create and start estimation kthread in a free or new array slot */ +static int ip_vs_est_add_kthread(struct netns_ipvs *ipvs) +{ + struct ip_vs_est_kt_data *kd = NULL; + int id = ipvs->est_kt_count; + int ret = -ENOMEM; + void *arr = NULL; + int i; + + if ((unsigned long)ipvs->est_kt_count >= ipvs->est_max_threads && + ipvs->enable && ipvs->est_max_threads) + return -EINVAL; + + mutex_lock(&ipvs->est_mutex); + + for (i = 0; i < id; i++) { + if (!ipvs->est_kt_arr[i]) + break; + } + if (i >= id) { + arr = krealloc_array(ipvs->est_kt_arr, id + 1, + sizeof(struct ip_vs_est_kt_data *), + GFP_KERNEL); + if (!arr) + goto out; + ipvs->est_kt_arr = arr; + } else { + id = i; + } + + kd = kzalloc(sizeof(*kd), GFP_KERNEL); + if (!kd) + goto out; + kd->ipvs = ipvs; + bitmap_fill(kd->avail, IPVS_EST_NTICKS); + kd->est_timer = jiffies; + kd->id = id; + ip_vs_est_set_params(ipvs, kd); + + /* Pre-allocate stats used in calc phase */ + if (!id && !kd->calc_stats) { + kd->calc_stats = ip_vs_stats_alloc(); + if (!kd->calc_stats) + goto out; + } + + /* Start kthread tasks only when services are present */ + if (ipvs->enable) { + ret = ip_vs_est_kthread_start(ipvs, kd); + if (ret < 0) + goto out; + } + + if (arr) + ipvs->est_kt_count++; + ipvs->est_kt_arr[id] = kd; + kd = NULL; + /* Use most recent kthread for new ests */ + ipvs->est_add_ktid = id; + ret = 0; + +out: + mutex_unlock(&ipvs->est_mutex); + if (kd) { + ip_vs_stats_free(kd->calc_stats); + kfree(kd); + } + + return ret; +} + +/* Select ktid where to add new ests: available, unused or new slot */ +static void ip_vs_est_update_ktid(struct netns_ipvs *ipvs) +{ + int ktid, best = ipvs->est_kt_count; + struct ip_vs_est_kt_data *kd; + + for (ktid = 0; ktid < ipvs->est_kt_count; ktid++) { + kd = ipvs->est_kt_arr[ktid]; + if (kd) { + if (kd->est_count < kd->est_max_count) { + best = ktid; + break; + } + } else if (ktid < best) { + best = ktid; + } + } + ipvs->est_add_ktid = best; +} + +/* Add estimator to current kthread (est_add_ktid) */ +static int ip_vs_enqueue_estimator(struct netns_ipvs *ipvs, + struct ip_vs_estimator *est) +{ + struct ip_vs_est_kt_data *kd = NULL; + struct ip_vs_est_tick_data *td; + int ktid, row, crow, cid, ret; + int delay = est->ktrow; + + BUILD_BUG_ON_MSG(IPVS_EST_TICK_CHAINS > 127, + "Too many chains for ktcid"); + + if (ipvs->est_add_ktid < ipvs->est_kt_count) { + kd = ipvs->est_kt_arr[ipvs->est_add_ktid]; + if (kd) + goto add_est; + } + + ret = ip_vs_est_add_kthread(ipvs); + if (ret < 0) + goto out; + kd = ipvs->est_kt_arr[ipvs->est_add_ktid]; + +add_est: + ktid = kd->id; + /* For small number of estimators prefer to use few ticks, + * otherwise try to add into the last estimated row. + * est_row and add_row point after the row we should use + */ + if (kd->est_count >= 2 * kd->tick_max || delay < IPVS_EST_NTICKS - 1) + crow = READ_ONCE(kd->est_row); + else + crow = kd->add_row; + crow += delay; + if (crow >= IPVS_EST_NTICKS) + crow -= IPVS_EST_NTICKS; + /* Assume initial delay ? */ + if (delay >= IPVS_EST_NTICKS - 1) { + /* Preserve initial delay or decrease it if no space in tick */ + row = crow; + if (crow < IPVS_EST_NTICKS - 1) { + crow++; + row = find_last_bit(kd->avail, crow); + } + if (row >= crow) + row = find_last_bit(kd->avail, IPVS_EST_NTICKS); + } else { + /* Preserve delay or increase it if no space in tick */ + row = IPVS_EST_NTICKS; + if (crow > 0) + row = find_next_bit(kd->avail, IPVS_EST_NTICKS, crow); + if (row >= IPVS_EST_NTICKS) + row = find_first_bit(kd->avail, IPVS_EST_NTICKS); + } + + td = rcu_dereference_protected(kd->ticks[row], 1); + if (!td) { + td = kzalloc(sizeof(*td), GFP_KERNEL); + if (!td) { + ret = -ENOMEM; + goto out; + } + rcu_assign_pointer(kd->ticks[row], td); + } + + cid = find_first_zero_bit(td->full, IPVS_EST_TICK_CHAINS); + + kd->est_count++; + kd->tick_len[row]++; + if (!td->chain_len[cid]) + __set_bit(cid, td->present); + td->chain_len[cid]++; + est->ktid = ktid; + est->ktrow = row; + est->ktcid = cid; + hlist_add_head_rcu(&est->list, &td->chains[cid]); + + if (td->chain_len[cid] >= kd->chain_max) { + __set_bit(cid, td->full); + if (kd->tick_len[row] >= kd->tick_max) + __clear_bit(row, kd->avail); + } + + /* Update est_add_ktid to point to first available/empty kt slot */ + if (kd->est_count == kd->est_max_count) + ip_vs_est_update_ktid(ipvs); + + ret = 0; + +out: + return ret; +} + +/* Start estimation for stats */ +int ip_vs_start_estimator(struct netns_ipvs *ipvs, struct ip_vs_stats *stats) { struct ip_vs_estimator *est = &stats->est; + int ret; - INIT_LIST_HEAD(&est->list); + if (!ipvs->est_max_threads && ipvs->enable) + ipvs->est_max_threads = IPVS_EST_CPU_KTHREADS * + num_possible_cpus(); - spin_lock_bh(&ipvs->est_lock); - list_add(&est->list, &ipvs->est_list); - spin_unlock_bh(&ipvs->est_lock); + est->ktid = -1; + est->ktrow = IPVS_EST_NTICKS - 1; /* Initial delay */ + + /* We prefer this code to be short, kthread 0 will requeue the + * estimator to available chain. If tasks are disabled, we + * will not allocate much memory, just for kt 0. + */ + ret = 0; + if (!ipvs->est_kt_count || !ipvs->est_kt_arr[0]) + ret = ip_vs_est_add_kthread(ipvs); + if (ret >= 0) + hlist_add_head(&est->list, &ipvs->est_temp_list); + else + INIT_HLIST_NODE(&est->list); + return ret; } +static void ip_vs_est_kthread_destroy(struct ip_vs_est_kt_data *kd) +{ + if (kd) { + if (kd->task) { + pr_info("stop unused estimator thread %d...\n", kd->id); + kthread_stop(kd->task); + } + ip_vs_stats_free(kd->calc_stats); + kfree(kd); + } +} + +/* Unlink estimator from chain */ void ip_vs_stop_estimator(struct netns_ipvs *ipvs, struct ip_vs_stats *stats) { struct ip_vs_estimator *est = &stats->est; + struct ip_vs_est_tick_data *td; + struct ip_vs_est_kt_data *kd; + int ktid = est->ktid; + int row = est->ktrow; + int cid = est->ktcid; - spin_lock_bh(&ipvs->est_lock); - list_del(&est->list); - spin_unlock_bh(&ipvs->est_lock); + /* Failed to add to chain ? */ + if (hlist_unhashed(&est->list)) + return; + + /* On return, estimator can be freed, dequeue it now */ + + /* In est_temp_list ? */ + if (ktid < 0) { + hlist_del(&est->list); + goto end_kt0; + } + + hlist_del_rcu(&est->list); + kd = ipvs->est_kt_arr[ktid]; + td = rcu_dereference_protected(kd->ticks[row], 1); + __clear_bit(cid, td->full); + td->chain_len[cid]--; + if (!td->chain_len[cid]) + __clear_bit(cid, td->present); + kd->tick_len[row]--; + __set_bit(row, kd->avail); + if (!kd->tick_len[row]) { + RCU_INIT_POINTER(kd->ticks[row], NULL); + kfree_rcu(td); + } + kd->est_count--; + if (kd->est_count) { + /* This kt slot can become available just now, prefer it */ + if (ktid < ipvs->est_add_ktid) + ipvs->est_add_ktid = ktid; + return; + } + + if (ktid > 0) { + mutex_lock(&ipvs->est_mutex); + ip_vs_est_kthread_destroy(kd); + ipvs->est_kt_arr[ktid] = NULL; + if (ktid == ipvs->est_kt_count - 1) { + ipvs->est_kt_count--; + while (ipvs->est_kt_count > 1 && + !ipvs->est_kt_arr[ipvs->est_kt_count - 1]) + ipvs->est_kt_count--; + } + mutex_unlock(&ipvs->est_mutex); + + /* This slot is now empty, prefer another available kt slot */ + if (ktid == ipvs->est_add_ktid) + ip_vs_est_update_ktid(ipvs); + } + +end_kt0: + /* kt 0 is freed after all other kthreads and chains are empty */ + if (ipvs->est_kt_count == 1 && hlist_empty(&ipvs->est_temp_list)) { + kd = ipvs->est_kt_arr[0]; + if (!kd || !kd->est_count) { + mutex_lock(&ipvs->est_mutex); + if (kd) { + ip_vs_est_kthread_destroy(kd); + ipvs->est_kt_arr[0] = NULL; + } + ipvs->est_kt_count--; + mutex_unlock(&ipvs->est_mutex); + ipvs->est_add_ktid = 0; + } + } +} + +/* Register all ests from est_temp_list to kthreads */ +static void ip_vs_est_drain_temp_list(struct netns_ipvs *ipvs) +{ + struct ip_vs_estimator *est; + + while (1) { + int max = 16; + + mutex_lock(&__ip_vs_mutex); + + while (max-- > 0) { + est = hlist_entry_safe(ipvs->est_temp_list.first, + struct ip_vs_estimator, list); + if (est) { + if (kthread_should_stop()) + goto unlock; + hlist_del_init(&est->list); + if (ip_vs_enqueue_estimator(ipvs, est) >= 0) + continue; + est->ktid = -1; + hlist_add_head(&est->list, + &ipvs->est_temp_list); + /* Abort, some entries will not be estimated + * until next attempt + */ + } + goto unlock; + } + mutex_unlock(&__ip_vs_mutex); + cond_resched(); + } + +unlock: + mutex_unlock(&__ip_vs_mutex); +} + +/* Calculate limits for all kthreads */ +static int ip_vs_est_calc_limits(struct netns_ipvs *ipvs, int *chain_max) +{ + DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wq); + struct ip_vs_est_kt_data *kd; + struct hlist_head chain; + struct ip_vs_stats *s; + int cache_factor = 4; + int i, loops, ntest; + s32 min_est = 0; + ktime_t t1, t2; + s64 diff, val; + int max = 8; + int ret = 1; + + INIT_HLIST_HEAD(&chain); + mutex_lock(&__ip_vs_mutex); + kd = ipvs->est_kt_arr[0]; + mutex_unlock(&__ip_vs_mutex); + s = kd ? kd->calc_stats : NULL; + if (!s) + goto out; + hlist_add_head(&s->est.list, &chain); + + loops = 1; + /* Get best result from many tests */ + for (ntest = 0; ntest < 12; ntest++) { + if (!(ntest & 3)) { + /* Wait for cpufreq frequency transition */ + wait_event_idle_timeout(wq, kthread_should_stop(), + HZ / 50); + if (!ipvs->enable || kthread_should_stop()) + goto stop; + } + + local_bh_disable(); + rcu_read_lock(); + + /* Put stats in cache */ + ip_vs_chain_estimation(&chain); + + t1 = ktime_get(); + for (i = loops * cache_factor; i > 0; i--) + ip_vs_chain_estimation(&chain); + t2 = ktime_get(); + + rcu_read_unlock(); + local_bh_enable(); + + if (!ipvs->enable || kthread_should_stop()) + goto stop; + cond_resched(); + + diff = ktime_to_ns(ktime_sub(t2, t1)); + if (diff <= 1 * NSEC_PER_USEC) { + /* Do more loops on low time resolution */ + loops *= 2; + continue; + } + if (diff >= NSEC_PER_SEC) + continue; + val = diff; + do_div(val, loops); + if (!min_est || val < min_est) { + min_est = val; + /* goal: 95usec per chain */ + val = 95 * NSEC_PER_USEC; + if (val >= min_est) { + do_div(val, min_est); + max = (int)val; + } else { + max = 1; + } + } + } + +out: + if (s) + hlist_del_init(&s->est.list); + *chain_max = max; + return ret; + +stop: + ret = 0; + goto out; +} + +/* Calculate the parameters and apply them in context of kt #0 + * ECP: est_calc_phase + * ECM: est_chain_max + * ECP ECM Insert Chain enable Description + * --------------------------------------------------------------------------- + * 0 0 est_temp_list 0 create kt #0 context + * 0 0 est_temp_list 0->1 service added, start kthread #0 task + * 0->1 0 est_temp_list 1 kt task #0 started, enters calc phase + * 1 0 est_temp_list 1 kt #0: determine est_chain_max, + * stop tasks, move ests to est_temp_list + * and free kd for kthreads 1..last + * 1->0 0->N kt chains 1 ests can go to kthreads + * 0 N kt chains 1 drain est_temp_list, create new kthread + * contexts, start tasks, estimate + */ +static void ip_vs_est_calc_phase(struct netns_ipvs *ipvs) +{ + int genid = atomic_read(&ipvs->est_genid); + struct ip_vs_est_tick_data *td; + struct ip_vs_est_kt_data *kd; + struct ip_vs_estimator *est; + struct ip_vs_stats *stats; + int id, row, cid, delay; + bool last, last_td; + int chain_max; + int step; + + if (!ip_vs_est_calc_limits(ipvs, &chain_max)) + return; + + mutex_lock(&__ip_vs_mutex); + + /* Stop all other tasks, so that we can immediately move the + * estimators to est_temp_list without RCU grace period + */ + mutex_lock(&ipvs->est_mutex); + for (id = 1; id < ipvs->est_kt_count; id++) { + /* netns clean up started, abort */ + if (!ipvs->enable) + goto unlock2; + kd = ipvs->est_kt_arr[id]; + if (!kd) + continue; + ip_vs_est_kthread_stop(kd); + } + mutex_unlock(&ipvs->est_mutex); + + /* Move all estimators to est_temp_list but carefully, + * all estimators and kthread data can be released while + * we reschedule. Even for kthread 0. + */ + step = 0; + + /* Order entries in est_temp_list in ascending delay, so now + * walk delay(desc), id(desc), cid(asc) + */ + delay = IPVS_EST_NTICKS; + +next_delay: + delay--; + if (delay < 0) + goto end_dequeue; + +last_kt: + /* Destroy contexts backwards */ + id = ipvs->est_kt_count; + +next_kt: + if (!ipvs->enable || kthread_should_stop()) + goto unlock; + id--; + if (id < 0) + goto next_delay; + kd = ipvs->est_kt_arr[id]; + if (!kd) + goto next_kt; + /* kt 0 can exist with empty chains */ + if (!id && kd->est_count <= 1) + goto next_delay; + + row = kd->est_row + delay; + if (row >= IPVS_EST_NTICKS) + row -= IPVS_EST_NTICKS; + td = rcu_dereference_protected(kd->ticks[row], 1); + if (!td) + goto next_kt; + + cid = 0; + +walk_chain: + if (kthread_should_stop()) + goto unlock; + step++; + if (!(step & 63)) { + /* Give chance estimators to be added (to est_temp_list) + * and deleted (releasing kthread contexts) + */ + mutex_unlock(&__ip_vs_mutex); + cond_resched(); + mutex_lock(&__ip_vs_mutex); + + /* Current kt released ? */ + if (id >= ipvs->est_kt_count) + goto last_kt; + if (kd != ipvs->est_kt_arr[id]) + goto next_kt; + /* Current td released ? */ + if (td != rcu_dereference_protected(kd->ticks[row], 1)) + goto next_kt; + /* No fatal changes on the current kd and td */ + } + est = hlist_entry_safe(td->chains[cid].first, struct ip_vs_estimator, + list); + if (!est) { + cid++; + if (cid >= IPVS_EST_TICK_CHAINS) + goto next_kt; + goto walk_chain; + } + /* We can cheat and increase est_count to protect kt 0 context + * from release but we prefer to keep the last estimator + */ + last = kd->est_count <= 1; + /* Do not free kt #0 data */ + if (!id && last) + goto next_delay; + last_td = kd->tick_len[row] <= 1; + stats = container_of(est, struct ip_vs_stats, est); + ip_vs_stop_estimator(ipvs, stats); + /* Tasks are stopped, move without RCU grace period */ + est->ktid = -1; + est->ktrow = row - kd->est_row; + if (est->ktrow < 0) + est->ktrow += IPVS_EST_NTICKS; + hlist_add_head(&est->list, &ipvs->est_temp_list); + /* kd freed ? */ + if (last) + goto next_kt; + /* td freed ? */ + if (last_td) + goto next_kt; + goto walk_chain; + +end_dequeue: + /* All estimators removed while calculating ? */ + if (!ipvs->est_kt_count) + goto unlock; + kd = ipvs->est_kt_arr[0]; + if (!kd) + goto unlock; + kd->add_row = kd->est_row; + ipvs->est_chain_max = chain_max; + ip_vs_est_set_params(ipvs, kd); + + pr_info("using max %d ests per chain, %d per kthread\n", + kd->chain_max, kd->est_max_count); + + /* Try to keep tot_stats in kt0, enqueue it early */ + if (ipvs->tot_stats && !hlist_unhashed(&ipvs->tot_stats->s.est.list) && + ipvs->tot_stats->s.est.ktid == -1) { + hlist_del(&ipvs->tot_stats->s.est.list); + hlist_add_head(&ipvs->tot_stats->s.est.list, + &ipvs->est_temp_list); + } + + mutex_lock(&ipvs->est_mutex); + + /* We completed the calc phase, new calc phase not requested */ + if (genid == atomic_read(&ipvs->est_genid)) + ipvs->est_calc_phase = 0; + +unlock2: + mutex_unlock(&ipvs->est_mutex); + +unlock: + mutex_unlock(&__ip_vs_mutex); } void ip_vs_zero_estimator(struct ip_vs_stats *stats) @@ -191,14 +920,25 @@ void ip_vs_read_estimator(struct ip_vs_kstats *dst, struct ip_vs_stats *stats) int __net_init ip_vs_estimator_net_init(struct netns_ipvs *ipvs) { - INIT_LIST_HEAD(&ipvs->est_list); - spin_lock_init(&ipvs->est_lock); - timer_setup(&ipvs->est_timer, estimation_timer, 0); - mod_timer(&ipvs->est_timer, jiffies + 2 * HZ); + INIT_HLIST_HEAD(&ipvs->est_temp_list); + ipvs->est_kt_arr = NULL; + ipvs->est_max_threads = 0; + ipvs->est_calc_phase = 0; + ipvs->est_chain_max = 0; + ipvs->est_kt_count = 0; + ipvs->est_add_ktid = 0; + atomic_set(&ipvs->est_genid, 0); + atomic_set(&ipvs->est_genid_done, 0); + __mutex_init(&ipvs->est_mutex, "ipvs->est_mutex", &__ipvs_est_key); return 0; } void __net_exit ip_vs_estimator_net_cleanup(struct netns_ipvs *ipvs) { - del_timer_sync(&ipvs->est_timer); + int i; + + for (i = 0; i < ipvs->est_kt_count; i++) + ip_vs_est_kthread_destroy(ipvs->est_kt_arr[i]); + kfree(ipvs->est_kt_arr); + mutex_destroy(&ipvs->est_mutex); } From f0be83d5421718ead31707b6ece34cf77d411c00 Mon Sep 17 00:00:00 2001 From: Julian Anastasov Date: Tue, 22 Nov 2022 18:46:03 +0200 Subject: [PATCH 1841/2002] ipvs: add est_cpulist and est_nice sysctl vars Allow the kthreads for stats to be configured for specific cpulist (isolation) and niceness (scheduling priority). Signed-off-by: Julian Anastasov Cc: yunhong-cgl jiang Cc: "dust.li" Reviewed-by: Jiri Wiesner Signed-off-by: Pablo Neira Ayuso --- Documentation/networking/ipvs-sysctl.rst | 20 ++++ include/net/ip_vs.h | 58 +++++++++ net/netfilter/ipvs/ip_vs_ctl.c | 143 ++++++++++++++++++++++- net/netfilter/ipvs/ip_vs_est.c | 12 +- 4 files changed, 229 insertions(+), 4 deletions(-) diff --git a/Documentation/networking/ipvs-sysctl.rst b/Documentation/networking/ipvs-sysctl.rst index 387fda80f05f..1b778705d706 100644 --- a/Documentation/networking/ipvs-sysctl.rst +++ b/Documentation/networking/ipvs-sysctl.rst @@ -129,6 +129,26 @@ drop_packet - INTEGER threshold. When the mode 3 is set, the always mode drop rate is controlled by the /proc/sys/net/ipv4/vs/am_droprate. +est_cpulist - CPULIST + Allowed CPUs for estimation kthreads + + Syntax: standard cpulist format + empty list - stop kthread tasks and estimation + default - the system's housekeeping CPUs for kthreads + + Example: + "all": all possible CPUs + "0-N": all possible CPUs, N denotes last CPU number + "0,1-N:1/2": first and all CPUs with odd number + "": empty list + +est_nice - INTEGER + default 0 + Valid range: -20 (more favorable) .. 19 (less favorable) + + Niceness value to use for the estimation kthreads (scheduling + priority) + expire_nodest_conn - BOOLEAN - 0 - disabled (default) - not 0 - enabled diff --git a/include/net/ip_vs.h b/include/net/ip_vs.h index 04960dc6228f..dc51b5497cf7 100644 --- a/include/net/ip_vs.h +++ b/include/net/ip_vs.h @@ -29,6 +29,7 @@ #include #endif #include /* Netw namespace */ +#include #define IP_VS_HDR_INVERSE 1 #define IP_VS_HDR_ICMP 2 @@ -365,6 +366,9 @@ struct ip_vs_cpu_stats { struct u64_stats_sync syncp; }; +/* Default nice for estimator kthreads */ +#define IPVS_EST_NICE 0 + /* IPVS statistics objects */ struct ip_vs_estimator { struct hlist_node list; @@ -1009,6 +1013,12 @@ struct netns_ipvs { int sysctl_schedule_icmp; int sysctl_ignore_tunneled; int sysctl_run_estimation; +#ifdef CONFIG_SYSCTL + cpumask_var_t sysctl_est_cpulist; /* kthread cpumask */ + int est_cpulist_valid; /* cpulist set */ + int sysctl_est_nice; /* kthread nice */ + int est_stopped; /* stop tasks */ +#endif /* ip_vs_lblc */ int sysctl_lblc_expiration; @@ -1162,6 +1172,19 @@ static inline int sysctl_run_estimation(struct netns_ipvs *ipvs) return ipvs->sysctl_run_estimation; } +static inline const struct cpumask *sysctl_est_cpulist(struct netns_ipvs *ipvs) +{ + if (ipvs->est_cpulist_valid) + return ipvs->sysctl_est_cpulist; + else + return housekeeping_cpumask(HK_TYPE_KTHREAD); +} + +static inline int sysctl_est_nice(struct netns_ipvs *ipvs) +{ + return ipvs->sysctl_est_nice; +} + #else static inline int sysctl_sync_threshold(struct netns_ipvs *ipvs) @@ -1259,6 +1282,16 @@ static inline int sysctl_run_estimation(struct netns_ipvs *ipvs) return 1; } +static inline const struct cpumask *sysctl_est_cpulist(struct netns_ipvs *ipvs) +{ + return housekeeping_cpumask(HK_TYPE_KTHREAD); +} + +static inline int sysctl_est_nice(struct netns_ipvs *ipvs) +{ + return IPVS_EST_NICE; +} + #endif /* IPVS core functions @@ -1569,6 +1602,31 @@ int ip_vs_est_kthread_start(struct netns_ipvs *ipvs, struct ip_vs_est_kt_data *kd); void ip_vs_est_kthread_stop(struct ip_vs_est_kt_data *kd); +static inline void ip_vs_est_stopped_recalc(struct netns_ipvs *ipvs) +{ +#ifdef CONFIG_SYSCTL + ipvs->est_stopped = ipvs->est_cpulist_valid && + cpumask_empty(sysctl_est_cpulist(ipvs)); +#endif +} + +static inline bool ip_vs_est_stopped(struct netns_ipvs *ipvs) +{ +#ifdef CONFIG_SYSCTL + return ipvs->est_stopped; +#else + return false; +#endif +} + +static inline int ip_vs_est_max_threads(struct netns_ipvs *ipvs) +{ + unsigned int limit = IPVS_EST_CPU_KTHREADS * + cpumask_weight(sysctl_est_cpulist(ipvs)); + + return max(1U, limit); +} + /* Various IPVS packet transmitters (from ip_vs_xmit.c) */ int ip_vs_null_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, struct ip_vs_protocol *pp, struct ip_vs_iphdr *iph); diff --git a/net/netfilter/ipvs/ip_vs_ctl.c b/net/netfilter/ipvs/ip_vs_ctl.c index c41a5392edc9..38df3ee655ed 100644 --- a/net/netfilter/ipvs/ip_vs_ctl.c +++ b/net/netfilter/ipvs/ip_vs_ctl.c @@ -263,7 +263,7 @@ static void est_reload_work_handler(struct work_struct *work) /* New config ? Stop kthread tasks */ if (genid != genid_done) ip_vs_est_kthread_stop(kd); - if (!kd->task) { + if (!kd->task && !ip_vs_est_stopped(ipvs)) { /* Do not start kthreads above 0 in calc phase */ if ((!id || !ipvs->est_calc_phase) && ip_vs_est_kthread_start(ipvs, kd) < 0) @@ -1940,6 +1940,122 @@ proc_do_sync_ports(struct ctl_table *table, int write, return rc; } +static int ipvs_proc_est_cpumask_set(struct ctl_table *table, void *buffer) +{ + struct netns_ipvs *ipvs = table->extra2; + cpumask_var_t *valp = table->data; + cpumask_var_t newmask; + int ret; + + if (!zalloc_cpumask_var(&newmask, GFP_KERNEL)) + return -ENOMEM; + + ret = cpulist_parse(buffer, newmask); + if (ret) + goto out; + + mutex_lock(&ipvs->est_mutex); + + if (!ipvs->est_cpulist_valid) { + if (!zalloc_cpumask_var(valp, GFP_KERNEL)) { + ret = -ENOMEM; + goto unlock; + } + ipvs->est_cpulist_valid = 1; + } + cpumask_and(newmask, newmask, ¤t->cpus_mask); + cpumask_copy(*valp, newmask); + /* est_max_threads may depend on cpulist size */ + ipvs->est_max_threads = ip_vs_est_max_threads(ipvs); + ipvs->est_calc_phase = 1; + ip_vs_est_reload_start(ipvs); + +unlock: + mutex_unlock(&ipvs->est_mutex); + +out: + free_cpumask_var(newmask); + return ret; +} + +static int ipvs_proc_est_cpumask_get(struct ctl_table *table, void *buffer, + size_t size) +{ + struct netns_ipvs *ipvs = table->extra2; + cpumask_var_t *valp = table->data; + struct cpumask *mask; + int ret; + + mutex_lock(&ipvs->est_mutex); + + if (ipvs->est_cpulist_valid) + mask = *valp; + else + mask = (struct cpumask *)housekeeping_cpumask(HK_TYPE_KTHREAD); + ret = scnprintf(buffer, size, "%*pbl\n", cpumask_pr_args(mask)); + + mutex_unlock(&ipvs->est_mutex); + + return ret; +} + +static int ipvs_proc_est_cpulist(struct ctl_table *table, int write, + void *buffer, size_t *lenp, loff_t *ppos) +{ + int ret; + + /* Ignore both read and write(append) if *ppos not 0 */ + if (*ppos || !*lenp) { + *lenp = 0; + return 0; + } + if (write) { + /* proc_sys_call_handler() appends terminator */ + ret = ipvs_proc_est_cpumask_set(table, buffer); + if (ret >= 0) + *ppos += *lenp; + } else { + /* proc_sys_call_handler() allocates 1 byte for terminator */ + ret = ipvs_proc_est_cpumask_get(table, buffer, *lenp + 1); + if (ret >= 0) { + *lenp = ret; + *ppos += *lenp; + ret = 0; + } + } + return ret; +} + +static int ipvs_proc_est_nice(struct ctl_table *table, int write, + void *buffer, size_t *lenp, loff_t *ppos) +{ + struct netns_ipvs *ipvs = table->extra2; + int *valp = table->data; + int val = *valp; + int ret; + + struct ctl_table tmp_table = { + .data = &val, + .maxlen = sizeof(int), + .mode = table->mode, + }; + + ret = proc_dointvec(&tmp_table, write, buffer, lenp, ppos); + if (write && ret >= 0) { + if (val < MIN_NICE || val > MAX_NICE) { + ret = -EINVAL; + } else { + mutex_lock(&ipvs->est_mutex); + if (*valp != val) { + *valp = val; + ip_vs_est_reload_start(ipvs); + } + mutex_unlock(&ipvs->est_mutex); + } + } + return ret; +} + /* * IPVS sysctl table (under the /proc/sys/net/ipv4/vs/) * Do not change order or insert new entries without @@ -2116,6 +2232,18 @@ static struct ctl_table vs_vars[] = { .mode = 0644, .proc_handler = proc_dointvec, }, + { + .procname = "est_cpulist", + .maxlen = NR_CPUS, /* unused */ + .mode = 0644, + .proc_handler = ipvs_proc_est_cpulist, + }, + { + .procname = "est_nice", + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = ipvs_proc_est_nice, + }, #ifdef CONFIG_IP_VS_DEBUG { .procname = "debug_level", @@ -4134,6 +4262,7 @@ static int __net_init ip_vs_control_net_init_sysctl(struct netns_ipvs *ipvs) INIT_DELAYED_WORK(&ipvs->defense_work, defense_work_handler); INIT_DELAYED_WORK(&ipvs->expire_nodest_conn_work, expire_nodest_conn_handler); + ipvs->est_stopped = 0; if (!net_eq(net, &init_net)) { tbl = kmemdup(vs_vars, sizeof(vs_vars), GFP_KERNEL); @@ -4195,6 +4324,15 @@ static int __net_init ip_vs_control_net_init_sysctl(struct netns_ipvs *ipvs) tbl[idx++].data = &ipvs->sysctl_ignore_tunneled; ipvs->sysctl_run_estimation = 1; tbl[idx++].data = &ipvs->sysctl_run_estimation; + + ipvs->est_cpulist_valid = 0; + tbl[idx].extra2 = ipvs; + tbl[idx++].data = &ipvs->sysctl_est_cpulist; + + ipvs->sysctl_est_nice = IPVS_EST_NICE; + tbl[idx].extra2 = ipvs; + tbl[idx++].data = &ipvs->sysctl_est_nice; + #ifdef CONFIG_IP_VS_DEBUG /* Global sysctls must be ro in non-init netns */ if (!net_eq(net, &init_net)) @@ -4234,6 +4372,9 @@ static void __net_exit ip_vs_control_net_cleanup_sysctl(struct netns_ipvs *ipvs) unregister_net_sysctl_table(ipvs->sysctl_hdr); ip_vs_stop_estimator(ipvs, &ipvs->tot_stats->s); + if (ipvs->est_cpulist_valid) + free_cpumask_var(ipvs->sysctl_est_cpulist); + if (!net_eq(net, &init_net)) kfree(ipvs->sysctl_tbl); } diff --git a/net/netfilter/ipvs/ip_vs_est.c b/net/netfilter/ipvs/ip_vs_est.c index 2fb6c097437c..e0f5f5da5b6d 100644 --- a/net/netfilter/ipvs/ip_vs_est.c +++ b/net/netfilter/ipvs/ip_vs_est.c @@ -57,6 +57,9 @@ - kthread contexts are created and attached to array - the kthread tasks are started when first service is added, before that the total stats are not estimated + - when configuration (cpulist/nice) is changed, the tasks are restarted + by work (est_reload_work) + - kthread tasks are stopped while the cpulist is empty - the kthread context holds lists with estimators (chains) which are processed every 2 seconds - as estimators can be added dynamically and in bursts, we try to spread @@ -229,6 +232,7 @@ void ip_vs_est_reload_start(struct netns_ipvs *ipvs) /* Ignore reloads before first service is added */ if (!ipvs->enable) return; + ip_vs_est_stopped_recalc(ipvs); /* Bump the kthread configuration genid */ atomic_inc(&ipvs->est_genid); queue_delayed_work(system_long_wq, &ipvs->est_reload_work, 0); @@ -259,6 +263,9 @@ int ip_vs_est_kthread_start(struct netns_ipvs *ipvs, goto out; } + set_user_nice(kd->task, sysctl_est_nice(ipvs)); + set_cpus_allowed_ptr(kd->task, sysctl_est_cpulist(ipvs)); + pr_info("starting estimator thread %d...\n", kd->id); wake_up_process(kd->task); @@ -334,7 +341,7 @@ static int ip_vs_est_add_kthread(struct netns_ipvs *ipvs) } /* Start kthread tasks only when services are present */ - if (ipvs->enable) { + if (ipvs->enable && !ip_vs_est_stopped(ipvs)) { ret = ip_vs_est_kthread_start(ipvs, kd); if (ret < 0) goto out; @@ -478,8 +485,7 @@ int ip_vs_start_estimator(struct netns_ipvs *ipvs, struct ip_vs_stats *stats) int ret; if (!ipvs->est_max_threads && ipvs->enable) - ipvs->est_max_threads = IPVS_EST_CPU_KTHREADS * - num_possible_cpus(); + ipvs->est_max_threads = ip_vs_est_max_threads(ipvs); est->ktid = -1; est->ktrow = IPVS_EST_NTICKS - 1; /* Initial delay */ From 144361c1949f227df9244302da02c258a363b674 Mon Sep 17 00:00:00 2001 From: Julian Anastasov Date: Tue, 22 Nov 2022 18:46:04 +0200 Subject: [PATCH 1842/2002] ipvs: run_estimation should control the kthread tasks Change the run_estimation flag to start/stop the kthread tasks. Signed-off-by: Julian Anastasov Cc: yunhong-cgl jiang Cc: "dust.li" Reviewed-by: Jiri Wiesner Signed-off-by: Pablo Neira Ayuso --- Documentation/networking/ipvs-sysctl.rst | 4 ++-- include/net/ip_vs.h | 6 +++-- net/netfilter/ipvs/ip_vs_ctl.c | 29 +++++++++++++++++++++++- net/netfilter/ipvs/ip_vs_est.c | 2 +- 4 files changed, 35 insertions(+), 6 deletions(-) diff --git a/Documentation/networking/ipvs-sysctl.rst b/Documentation/networking/ipvs-sysctl.rst index 1b778705d706..3fb5fa142eef 100644 --- a/Documentation/networking/ipvs-sysctl.rst +++ b/Documentation/networking/ipvs-sysctl.rst @@ -324,8 +324,8 @@ run_estimation - BOOLEAN 0 - disabled not 0 - enabled (default) - If disabled, the estimation will be stop, and you can't see - any update on speed estimation data. + If disabled, the estimation will be suspended and kthread tasks + stopped. You can always re-enable estimation by setting this value to 1. But be careful, the first estimation after re-enable is not diff --git a/include/net/ip_vs.h b/include/net/ip_vs.h index dc51b5497cf7..c6c61100d244 100644 --- a/include/net/ip_vs.h +++ b/include/net/ip_vs.h @@ -1605,8 +1605,10 @@ void ip_vs_est_kthread_stop(struct ip_vs_est_kt_data *kd); static inline void ip_vs_est_stopped_recalc(struct netns_ipvs *ipvs) { #ifdef CONFIG_SYSCTL - ipvs->est_stopped = ipvs->est_cpulist_valid && - cpumask_empty(sysctl_est_cpulist(ipvs)); + /* Stop tasks while cpulist is empty or if disabled with flag */ + ipvs->est_stopped = !sysctl_run_estimation(ipvs) || + (ipvs->est_cpulist_valid && + cpumask_empty(sysctl_est_cpulist(ipvs))); #endif } diff --git a/net/netfilter/ipvs/ip_vs_ctl.c b/net/netfilter/ipvs/ip_vs_ctl.c index 38df3ee655ed..c9f598505642 100644 --- a/net/netfilter/ipvs/ip_vs_ctl.c +++ b/net/netfilter/ipvs/ip_vs_ctl.c @@ -2056,6 +2056,32 @@ static int ipvs_proc_est_nice(struct ctl_table *table, int write, return ret; } +static int ipvs_proc_run_estimation(struct ctl_table *table, int write, + void *buffer, size_t *lenp, loff_t *ppos) +{ + struct netns_ipvs *ipvs = table->extra2; + int *valp = table->data; + int val = *valp; + int ret; + + struct ctl_table tmp_table = { + .data = &val, + .maxlen = sizeof(int), + .mode = table->mode, + }; + + ret = proc_dointvec(&tmp_table, write, buffer, lenp, ppos); + if (write && ret >= 0) { + mutex_lock(&ipvs->est_mutex); + if (*valp != val) { + *valp = val; + ip_vs_est_reload_start(ipvs); + } + mutex_unlock(&ipvs->est_mutex); + } + return ret; +} + /* * IPVS sysctl table (under the /proc/sys/net/ipv4/vs/) * Do not change order or insert new entries without @@ -2230,7 +2256,7 @@ static struct ctl_table vs_vars[] = { .procname = "run_estimation", .maxlen = sizeof(int), .mode = 0644, - .proc_handler = proc_dointvec, + .proc_handler = ipvs_proc_run_estimation, }, { .procname = "est_cpulist", @@ -4323,6 +4349,7 @@ static int __net_init ip_vs_control_net_init_sysctl(struct netns_ipvs *ipvs) tbl[idx++].data = &ipvs->sysctl_schedule_icmp; tbl[idx++].data = &ipvs->sysctl_ignore_tunneled; ipvs->sysctl_run_estimation = 1; + tbl[idx].extra2 = ipvs; tbl[idx++].data = &ipvs->sysctl_run_estimation; ipvs->est_cpulist_valid = 0; diff --git a/net/netfilter/ipvs/ip_vs_est.c b/net/netfilter/ipvs/ip_vs_est.c index e0f5f5da5b6d..df56073bb282 100644 --- a/net/netfilter/ipvs/ip_vs_est.c +++ b/net/netfilter/ipvs/ip_vs_est.c @@ -212,7 +212,7 @@ static int ip_vs_estimation_kthread(void *data) kd->est_timer = now; } - if (sysctl_run_estimation(ipvs) && kd->tick_len[row]) + if (kd->tick_len[row]) ip_vs_tick_estimation(kd, row); row++; From 1fd54773c26787b9aea80e2f62c7d0780ea444d0 Mon Sep 17 00:00:00 2001 From: Andrew Melnychenko Date: Wed, 7 Dec 2022 13:35:53 +0200 Subject: [PATCH 1843/2002] udp: allow header check for dodgy GSO_UDP_L4 packets. Allow UDP_L4 for robust packets. Signed-off-by: Jason Wang Signed-off-by: Andrew Melnychenko Signed-off-by: David S. Miller --- net/ipv4/udp_offload.c | 3 ++- net/ipv6/udp_offload.c | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/net/ipv4/udp_offload.c b/net/ipv4/udp_offload.c index aedde65e2268..1f01e15ca24f 100644 --- a/net/ipv4/udp_offload.c +++ b/net/ipv4/udp_offload.c @@ -387,7 +387,8 @@ static struct sk_buff *udp4_ufo_fragment(struct sk_buff *skb, if (!pskb_may_pull(skb, sizeof(struct udphdr))) goto out; - if (skb_shinfo(skb)->gso_type & SKB_GSO_UDP_L4) + if (skb_shinfo(skb)->gso_type & SKB_GSO_UDP_L4 && + !skb_gso_ok(skb, features | NETIF_F_GSO_ROBUST)) return __udp_gso_segment(skb, features, false); mss = skb_shinfo(skb)->gso_size; diff --git a/net/ipv6/udp_offload.c b/net/ipv6/udp_offload.c index e0e10f6bcdc1..c39c1e32f980 100644 --- a/net/ipv6/udp_offload.c +++ b/net/ipv6/udp_offload.c @@ -42,7 +42,8 @@ static struct sk_buff *udp6_ufo_fragment(struct sk_buff *skb, if (!pskb_may_pull(skb, sizeof(struct udphdr))) goto out; - if (skb_shinfo(skb)->gso_type & SKB_GSO_UDP_L4) + if (skb_shinfo(skb)->gso_type & SKB_GSO_UDP_L4 && + !skb_gso_ok(skb, features | NETIF_F_GSO_ROBUST)) return __udp_gso_segment(skb, features, true); mss = skb_shinfo(skb)->gso_size; From b22bbdd17a5af2cc99cf42048970b9355dcbec8b Mon Sep 17 00:00:00 2001 From: Andrew Melnychenko Date: Wed, 7 Dec 2022 13:35:54 +0200 Subject: [PATCH 1844/2002] uapi/linux/if_tun.h: Added new offload types for USO4/6. Added 2 additional offlloads for USO(IPv4 & IPv6). Separate offloads are required for Windows VM guests, g.e. Windows may set USO rx only for IPv4. Signed-off-by: Andrew Melnychenko Signed-off-by: David S. Miller --- include/uapi/linux/if_tun.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/uapi/linux/if_tun.h b/include/uapi/linux/if_tun.h index b6d7b868f290..287cdc81c939 100644 --- a/include/uapi/linux/if_tun.h +++ b/include/uapi/linux/if_tun.h @@ -90,6 +90,8 @@ #define TUN_F_TSO6 0x04 /* I can handle TSO for IPv6 packets */ #define TUN_F_TSO_ECN 0x08 /* I can handle TSO with ECN bits. */ #define TUN_F_UFO 0x10 /* I can handle UFO packets */ +#define TUN_F_USO4 0x20 /* I can handle USO for IPv4 packets */ +#define TUN_F_USO6 0x40 /* I can handle USO for IPv6 packets */ /* Protocol info prepended to the packets (when IFF_NO_PI is not set) */ #define TUN_PKT_STRIP 0x0001 From 399e0827642f6a8bcae24277fe08e80e7e4bb891 Mon Sep 17 00:00:00 2001 From: Andrew Melnychenko Date: Wed, 7 Dec 2022 13:35:55 +0200 Subject: [PATCH 1845/2002] driver/net/tun: Added features for USO. Added support for USO4 and USO6. For now, to "enable" USO, it's required to set both USO4 and USO6 simultaneously. USO enables NETIF_F_GSO_UDP_L4. Signed-off-by: Andrew Melnychenko Signed-off-by: David S. Miller --- drivers/net/tap.c | 10 ++++++++-- drivers/net/tun.c | 8 +++++++- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/drivers/net/tap.c b/drivers/net/tap.c index 9e75ed3f08ce..a2be1994b389 100644 --- a/drivers/net/tap.c +++ b/drivers/net/tap.c @@ -957,6 +957,10 @@ static int set_offload(struct tap_queue *q, unsigned long arg) if (arg & TUN_F_TSO6) feature_mask |= NETIF_F_TSO6; } + + /* TODO: for now USO4 and USO6 should work simultaneously */ + if ((arg & (TUN_F_USO4 | TUN_F_USO6)) == (TUN_F_USO4 | TUN_F_USO6)) + features |= NETIF_F_GSO_UDP_L4; } /* tun/tap driver inverts the usage for TSO offloads, where @@ -967,7 +971,8 @@ static int set_offload(struct tap_queue *q, unsigned long arg) * When user space turns off TSO, we turn off GSO/LRO so that * user-space will not receive TSO frames. */ - if (feature_mask & (NETIF_F_TSO | NETIF_F_TSO6)) + if (feature_mask & (NETIF_F_TSO | NETIF_F_TSO6) || + (feature_mask & (TUN_F_USO4 | TUN_F_USO6)) == (TUN_F_USO4 | TUN_F_USO6)) features |= RX_OFFLOADS; else features &= ~RX_OFFLOADS; @@ -1091,7 +1096,8 @@ static long tap_ioctl(struct file *file, unsigned int cmd, case TUNSETOFFLOAD: /* let the user check for future flags */ if (arg & ~(TUN_F_CSUM | TUN_F_TSO4 | TUN_F_TSO6 | - TUN_F_TSO_ECN | TUN_F_UFO)) + TUN_F_TSO_ECN | TUN_F_UFO | + TUN_F_USO4 | TUN_F_USO6)) return -EINVAL; rtnl_lock(); diff --git a/drivers/net/tun.c b/drivers/net/tun.c index b4baa2001a63..a7d17c680f4a 100644 --- a/drivers/net/tun.c +++ b/drivers/net/tun.c @@ -185,7 +185,7 @@ struct tun_struct { struct net_device *dev; netdev_features_t set_features; #define TUN_USER_FEATURES (NETIF_F_HW_CSUM|NETIF_F_TSO_ECN|NETIF_F_TSO| \ - NETIF_F_TSO6) + NETIF_F_TSO6 | NETIF_F_GSO_UDP_L4) int align; int vnet_hdr_sz; @@ -2878,6 +2878,12 @@ static int set_offload(struct tun_struct *tun, unsigned long arg) } arg &= ~TUN_F_UFO; + + /* TODO: for now USO4 and USO6 should work simultaneously */ + if (arg & TUN_F_USO4 && arg & TUN_F_USO6) { + features |= NETIF_F_GSO_UDP_L4; + arg &= ~(TUN_F_USO4 | TUN_F_USO6); + } } /* This gives the user a way to test for new features in future by From 34061b348ae912b9783fae7fabae69d46977a036 Mon Sep 17 00:00:00 2001 From: Andrew Melnychenko Date: Wed, 7 Dec 2022 13:35:56 +0200 Subject: [PATCH 1846/2002] uapi/linux/virtio_net.h: Added USO types. Added new GSO type for USO: VIRTIO_NET_HDR_GSO_UDP_L4. Feature VIRTIO_NET_F_HOST_USO allows to enable NETIF_F_GSO_UDP_L4. Separated VIRTIO_NET_F_GUEST_USO4 & VIRTIO_NET_F_GUEST_USO6 features required for Windows guests. Signed-off-by: Andrew Melnychenko Signed-off-by: David S. Miller --- include/uapi/linux/virtio_net.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/include/uapi/linux/virtio_net.h b/include/uapi/linux/virtio_net.h index 6cb842ea8979..b4062bed186a 100644 --- a/include/uapi/linux/virtio_net.h +++ b/include/uapi/linux/virtio_net.h @@ -57,6 +57,9 @@ * Steering */ #define VIRTIO_NET_F_CTRL_MAC_ADDR 23 /* Set MAC address */ #define VIRTIO_NET_F_NOTF_COAL 53 /* Device supports notifications coalescing */ +#define VIRTIO_NET_F_GUEST_USO4 54 /* Guest can handle USOv4 in. */ +#define VIRTIO_NET_F_GUEST_USO6 55 /* Guest can handle USOv6 in. */ +#define VIRTIO_NET_F_HOST_USO 56 /* Host can handle USO in. */ #define VIRTIO_NET_F_HASH_REPORT 57 /* Supports hash report */ #define VIRTIO_NET_F_RSS 60 /* Supports RSS RX steering */ #define VIRTIO_NET_F_RSC_EXT 61 /* extended coalescing info */ @@ -130,6 +133,7 @@ struct virtio_net_hdr_v1 { #define VIRTIO_NET_HDR_GSO_TCPV4 1 /* GSO frame, IPv4 TCP (TSO) */ #define VIRTIO_NET_HDR_GSO_UDP 3 /* GSO frame, IPv4 UDP (UFO) */ #define VIRTIO_NET_HDR_GSO_TCPV6 4 /* GSO frame, IPv6 TCP */ +#define VIRTIO_NET_HDR_GSO_UDP_L4 5 /* GSO frame, IPv4& IPv6 UDP (USO) */ #define VIRTIO_NET_HDR_GSO_ECN 0x80 /* TCP has ECN set */ __u8 gso_type; __virtio16 hdr_len; /* Ethernet + IP + tcp/udp hdrs */ From 860b7f27b8f78564ca5a2f607e0820b2d352a562 Mon Sep 17 00:00:00 2001 From: Andrew Melnychenko Date: Wed, 7 Dec 2022 13:35:57 +0200 Subject: [PATCH 1847/2002] linux/virtio_net.h: Support USO offload in vnet header. Now, it's possible to convert USO vnet packets from/to skb. Added support for GSO_UDP_L4 offload. Signed-off-by: Andrew Melnychenko Signed-off-by: David S. Miller --- include/linux/virtio_net.h | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/include/linux/virtio_net.h b/include/linux/virtio_net.h index a960de68ac69..bdf8de2cdd93 100644 --- a/include/linux/virtio_net.h +++ b/include/linux/virtio_net.h @@ -15,6 +15,7 @@ static inline bool virtio_net_hdr_match_proto(__be16 protocol, __u8 gso_type) case VIRTIO_NET_HDR_GSO_TCPV6: return protocol == cpu_to_be16(ETH_P_IPV6); case VIRTIO_NET_HDR_GSO_UDP: + case VIRTIO_NET_HDR_GSO_UDP_L4: return protocol == cpu_to_be16(ETH_P_IP) || protocol == cpu_to_be16(ETH_P_IPV6); default: @@ -31,6 +32,7 @@ static inline int virtio_net_hdr_set_proto(struct sk_buff *skb, switch (hdr->gso_type & ~VIRTIO_NET_HDR_GSO_ECN) { case VIRTIO_NET_HDR_GSO_TCPV4: case VIRTIO_NET_HDR_GSO_UDP: + case VIRTIO_NET_HDR_GSO_UDP_L4: skb->protocol = cpu_to_be16(ETH_P_IP); break; case VIRTIO_NET_HDR_GSO_TCPV6: @@ -69,6 +71,11 @@ static inline int virtio_net_hdr_to_skb(struct sk_buff *skb, ip_proto = IPPROTO_UDP; thlen = sizeof(struct udphdr); break; + case VIRTIO_NET_HDR_GSO_UDP_L4: + gso_type = SKB_GSO_UDP_L4; + ip_proto = IPPROTO_UDP; + thlen = sizeof(struct udphdr); + break; default: return -EINVAL; } @@ -182,6 +189,8 @@ static inline int virtio_net_hdr_from_skb(const struct sk_buff *skb, hdr->gso_type = VIRTIO_NET_HDR_GSO_TCPV4; else if (sinfo->gso_type & SKB_GSO_TCPV6) hdr->gso_type = VIRTIO_NET_HDR_GSO_TCPV6; + else if (sinfo->gso_type & SKB_GSO_UDP_L4) + hdr->gso_type = VIRTIO_NET_HDR_GSO_UDP_L4; else return -EINVAL; if (sinfo->gso_type & SKB_GSO_TCP_ECN) From 418044e1de3063a6c50df964bdfca7eecd7ec6e5 Mon Sep 17 00:00:00 2001 From: Andrew Melnychenko Date: Wed, 7 Dec 2022 13:35:58 +0200 Subject: [PATCH 1848/2002] drivers/net/virtio_net.c: Added USO support. Now, it possible to enable GSO_UDP_L4("tx-udp-segmentation") for VirtioNet. Signed-off-by: Andrew Melnychenko Signed-off-by: David S. Miller --- drivers/net/virtio_net.c | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c index 19eee0655b99..7723b2a49d8e 100644 --- a/drivers/net/virtio_net.c +++ b/drivers/net/virtio_net.c @@ -60,13 +60,17 @@ static const unsigned long guest_offloads[] = { VIRTIO_NET_F_GUEST_TSO6, VIRTIO_NET_F_GUEST_ECN, VIRTIO_NET_F_GUEST_UFO, - VIRTIO_NET_F_GUEST_CSUM + VIRTIO_NET_F_GUEST_CSUM, + VIRTIO_NET_F_GUEST_USO4, + VIRTIO_NET_F_GUEST_USO6 }; #define GUEST_OFFLOAD_GRO_HW_MASK ((1ULL << VIRTIO_NET_F_GUEST_TSO4) | \ (1ULL << VIRTIO_NET_F_GUEST_TSO6) | \ (1ULL << VIRTIO_NET_F_GUEST_ECN) | \ - (1ULL << VIRTIO_NET_F_GUEST_UFO)) + (1ULL << VIRTIO_NET_F_GUEST_UFO) | \ + (1ULL << VIRTIO_NET_F_GUEST_USO4) | \ + (1ULL << VIRTIO_NET_F_GUEST_USO6)) struct virtnet_stat_desc { char desc[ETH_GSTRING_LEN]; @@ -3085,7 +3089,9 @@ static int virtnet_xdp_set(struct net_device *dev, struct bpf_prog *prog, virtio_has_feature(vi->vdev, VIRTIO_NET_F_GUEST_TSO6) || virtio_has_feature(vi->vdev, VIRTIO_NET_F_GUEST_ECN) || virtio_has_feature(vi->vdev, VIRTIO_NET_F_GUEST_UFO) || - virtio_has_feature(vi->vdev, VIRTIO_NET_F_GUEST_CSUM))) { + virtio_has_feature(vi->vdev, VIRTIO_NET_F_GUEST_CSUM) || + virtio_has_feature(vi->vdev, VIRTIO_NET_F_GUEST_USO4) || + virtio_has_feature(vi->vdev, VIRTIO_NET_F_GUEST_USO6))) { NL_SET_ERR_MSG_MOD(extack, "Can't set XDP while host is implementing GRO_HW/CSUM, disable GRO_HW/CSUM first"); return -EOPNOTSUPP; } @@ -3690,7 +3696,9 @@ static bool virtnet_check_guest_gso(const struct virtnet_info *vi) return virtio_has_feature(vi->vdev, VIRTIO_NET_F_GUEST_TSO4) || virtio_has_feature(vi->vdev, VIRTIO_NET_F_GUEST_TSO6) || virtio_has_feature(vi->vdev, VIRTIO_NET_F_GUEST_ECN) || - virtio_has_feature(vi->vdev, VIRTIO_NET_F_GUEST_UFO); + virtio_has_feature(vi->vdev, VIRTIO_NET_F_GUEST_UFO) || + (virtio_has_feature(vi->vdev, VIRTIO_NET_F_GUEST_USO4) && + virtio_has_feature(vi->vdev, VIRTIO_NET_F_GUEST_USO6)); } static void virtnet_set_big_packets(struct virtnet_info *vi, const int mtu) @@ -3759,6 +3767,8 @@ static int virtnet_probe(struct virtio_device *vdev) dev->hw_features |= NETIF_F_TSO6; if (virtio_has_feature(vdev, VIRTIO_NET_F_HOST_ECN)) dev->hw_features |= NETIF_F_TSO_ECN; + if (virtio_has_feature(vdev, VIRTIO_NET_F_HOST_USO)) + dev->hw_features |= NETIF_F_GSO_UDP_L4; dev->features |= NETIF_F_GSO_ROBUST; @@ -4036,6 +4046,7 @@ static struct virtio_device_id id_table[] = { VIRTIO_NET_F_HOST_TSO4, VIRTIO_NET_F_HOST_UFO, VIRTIO_NET_F_HOST_TSO6, \ VIRTIO_NET_F_HOST_ECN, VIRTIO_NET_F_GUEST_TSO4, VIRTIO_NET_F_GUEST_TSO6, \ VIRTIO_NET_F_GUEST_ECN, VIRTIO_NET_F_GUEST_UFO, \ + VIRTIO_NET_F_HOST_USO, VIRTIO_NET_F_GUEST_USO4, VIRTIO_NET_F_GUEST_USO6, \ VIRTIO_NET_F_MRG_RXBUF, VIRTIO_NET_F_STATUS, VIRTIO_NET_F_CTRL_VQ, \ VIRTIO_NET_F_CTRL_RX, VIRTIO_NET_F_CTRL_VLAN, \ VIRTIO_NET_F_GUEST_ANNOUNCE, VIRTIO_NET_F_MQ, \ From 6d534ee057b62ca9332b988619323ee99c7847c1 Mon Sep 17 00:00:00 2001 From: Michael Sit Wei Hong Date: Thu, 8 Dec 2022 17:03:15 +0800 Subject: [PATCH 1849/2002] net: stmmac: Add check for taprio basetime configuration Adds a boundary check to prevent negative basetime input from user while configuring taprio. Signed-off-by: Michael Sit Wei Hong Signed-off-by: Lai Peter Jun Ann Signed-off-by: David S. Miller --- drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c index 773e415cc2de..2cfb18cef1d4 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c @@ -926,6 +926,9 @@ static int tc_setup_taprio(struct stmmac_priv *priv, int i, ret = 0; u64 ctr; + if (qopt->base_time < 0) + return -ERANGE; + if (!priv->dma_cap.estsel) return -EOPNOTSUPP; From 6cee96e09df54ae17784c0f38a49e0ed8229b825 Mon Sep 17 00:00:00 2001 From: Yang Yingliang Date: Thu, 8 Dec 2022 20:01:21 +0800 Subject: [PATCH 1850/2002] ethernet: s2io: don't call dev_kfree_skb() under spin_lock_irqsave() It is not allowed to call kfree_skb() or consume_skb() from hardware interrupt context or with hardware interrupts being disabled. It should use dev_kfree_skb_irq() or dev_consume_skb_irq() instead. The difference between them is free reason, dev_kfree_skb_irq() means the SKB is dropped in error and dev_consume_skb_irq() means the SKB is consumed in normal. In this case, dev_kfree_skb() is called in free_tx_buffers() to drop the SKBs in tx buffers, when the card is down, so replace it with dev_kfree_skb_irq() here. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Signed-off-by: Yang Yingliang Signed-off-by: David S. Miller --- drivers/net/ethernet/neterion/s2io.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/neterion/s2io.c b/drivers/net/ethernet/neterion/s2io.c index 1d3c4474b7cb..700c05fb05b9 100644 --- a/drivers/net/ethernet/neterion/s2io.c +++ b/drivers/net/ethernet/neterion/s2io.c @@ -2386,7 +2386,7 @@ static void free_tx_buffers(struct s2io_nic *nic) skb = s2io_txdl_getskb(&mac_control->fifos[i], txdp, j); if (skb) { swstats->mem_freed += skb->truesize; - dev_kfree_skb(skb); + dev_kfree_skb_irq(skb); cnt++; } } From 2f623aaf9f31de968dea6169849706a2f9be444c Mon Sep 17 00:00:00 2001 From: Li Zetao Date: Thu, 8 Dec 2022 20:05:40 +0800 Subject: [PATCH 1851/2002] net: farsync: Fix kmemleak when rmmods farsync There are two memory leaks reported by kmemleak: unreferenced object 0xffff888114b20200 (size 128): comm "modprobe", pid 4846, jiffies 4295146524 (age 401.345s) hex dump (first 32 bytes): e0 62 57 09 81 88 ff ff e0 62 57 09 81 88 ff ff .bW......bW..... 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ backtrace: [] kmalloc_trace+0x22/0x60 [] __hw_addr_add_ex+0x198/0x6c0 [] dev_addr_init+0x13d/0x230 [] alloc_netdev_mqs+0x10d/0xe50 [] alloc_hdlcdev+0x2e/0x80 [] fst_add_one+0x601/0x10e0 [farsync] ... unreferenced object 0xffff88810b85b000 (size 1024): comm "modprobe", pid 4846, jiffies 4295146523 (age 401.346s) hex dump (first 32 bytes): 00 00 b0 02 00 c9 ff ff 00 70 0a 00 00 c9 ff ff .........p...... 00 00 00 f2 00 00 00 f3 0a 00 00 00 02 00 00 00 ................ backtrace: [] kmalloc_trace+0x22/0x60 [] fst_add_one+0x154/0x10e0 [farsync] [] local_pci_probe+0xd3/0x170 ... The root cause is traced to the netdev and fst_card_info are not freed when removes one fst in fst_remove_one(), which may trigger oom if repeated insmod and rmmod module. Fix it by adding free_netdev() and kfree() in fst_remove_one(), just as the operations on the error handling path in fst_add_one(). Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Signed-off-by: Li Zetao Reviewed-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/wan/farsync.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/wan/farsync.c b/drivers/net/wan/farsync.c index 6a212c085435..5b01642ca44e 100644 --- a/drivers/net/wan/farsync.c +++ b/drivers/net/wan/farsync.c @@ -2545,6 +2545,7 @@ fst_remove_one(struct pci_dev *pdev) struct net_device *dev = port_to_dev(&card->ports[i]); unregister_hdlc_device(dev); + free_netdev(dev); } fst_disable_intr(card); @@ -2564,6 +2565,7 @@ fst_remove_one(struct pci_dev *pdev) card->tx_dma_handle_card); } fst_card_array[card->card_no] = NULL; + kfree(card); } static struct pci_driver fst_driver = { From 3cf7203ca620682165706f70a1b12b5194607dce Mon Sep 17 00:00:00 2001 From: Hangbin Liu Date: Thu, 8 Dec 2022 20:04:52 +0800 Subject: [PATCH 1852/2002] net/tunnel: wait until all sk_user_data reader finish before releasing the sock There is a race condition in vxlan that when deleting a vxlan device during receiving packets, there is a possibility that the sock is released after getting vxlan_sock vs from sk_user_data. Then in later vxlan_ecn_decapsulate(), vxlan_get_sk_family() we will got NULL pointer dereference. e.g. #0 [ffffa25ec6978a38] machine_kexec at ffffffff8c669757 #1 [ffffa25ec6978a90] __crash_kexec at ffffffff8c7c0a4d #2 [ffffa25ec6978b58] crash_kexec at ffffffff8c7c1c48 #3 [ffffa25ec6978b60] oops_end at ffffffff8c627f2b #4 [ffffa25ec6978b80] page_fault_oops at ffffffff8c678fcb #5 [ffffa25ec6978bd8] exc_page_fault at ffffffff8d109542 #6 [ffffa25ec6978c00] asm_exc_page_fault at ffffffff8d200b62 [exception RIP: vxlan_ecn_decapsulate+0x3b] RIP: ffffffffc1014e7b RSP: ffffa25ec6978cb0 RFLAGS: 00010246 RAX: 0000000000000008 RBX: ffff8aa000888000 RCX: 0000000000000000 RDX: 000000000000000e RSI: ffff8a9fc7ab803e RDI: ffff8a9fd1168700 RBP: ffff8a9fc7ab803e R8: 0000000000700000 R9: 00000000000010ae R10: ffff8a9fcb748980 R11: 0000000000000000 R12: ffff8a9fd1168700 R13: ffff8aa000888000 R14: 00000000002a0000 R15: 00000000000010ae ORIG_RAX: ffffffffffffffff CS: 0010 SS: 0018 #7 [ffffa25ec6978ce8] vxlan_rcv at ffffffffc10189cd [vxlan] #8 [ffffa25ec6978d90] udp_queue_rcv_one_skb at ffffffff8cfb6507 #9 [ffffa25ec6978dc0] udp_unicast_rcv_skb at ffffffff8cfb6e45 #10 [ffffa25ec6978dc8] __udp4_lib_rcv at ffffffff8cfb8807 #11 [ffffa25ec6978e20] ip_protocol_deliver_rcu at ffffffff8cf76951 #12 [ffffa25ec6978e48] ip_local_deliver at ffffffff8cf76bde #13 [ffffa25ec6978ea0] __netif_receive_skb_one_core at ffffffff8cecde9b #14 [ffffa25ec6978ec8] process_backlog at ffffffff8cece139 #15 [ffffa25ec6978f00] __napi_poll at ffffffff8ceced1a #16 [ffffa25ec6978f28] net_rx_action at ffffffff8cecf1f3 #17 [ffffa25ec6978fa0] __softirqentry_text_start at ffffffff8d4000ca #18 [ffffa25ec6978ff0] do_softirq at ffffffff8c6fbdc3 Reproducer: https://github.com/Mellanox/ovs-tests/blob/master/test-ovs-vxlan-remove-tunnel-during-traffic.sh Fix this by waiting for all sk_user_data reader to finish before releasing the sock. Reported-by: Jianlin Shi Suggested-by: Jakub Sitnicki Fixes: 6a93cc905274 ("udp-tunnel: Add a few more UDP tunnel APIs") Signed-off-by: Hangbin Liu Reviewed-by: Jiri Pirko Signed-off-by: David S. Miller --- net/ipv4/udp_tunnel_core.c | 1 + 1 file changed, 1 insertion(+) diff --git a/net/ipv4/udp_tunnel_core.c b/net/ipv4/udp_tunnel_core.c index 8242c8947340..5f8104cf082d 100644 --- a/net/ipv4/udp_tunnel_core.c +++ b/net/ipv4/udp_tunnel_core.c @@ -176,6 +176,7 @@ EXPORT_SYMBOL_GPL(udp_tunnel_xmit_skb); void udp_tunnel_sock_release(struct socket *sock) { rcu_assign_sk_user_data(sock->sk, NULL); + synchronize_rcu(); kernel_sock_shutdown(sock, SHUT_RDWR); sock_release(sock); } From 3dfe3486c1cd4f82b466b7d307f23777137b8acc Mon Sep 17 00:00:00 2001 From: Yang Yingliang Date: Thu, 8 Dec 2022 21:37:34 +0800 Subject: [PATCH 1853/2002] net: apple: mace: don't call dev_kfree_skb() under spin_lock_irqsave() It is not allowed to call kfree_skb() or consume_skb() from hardware interrupt context or with hardware interrupts being disabled. It should use dev_kfree_skb_irq() or dev_consume_skb_irq() instead. The difference between them is free reason, dev_kfree_skb_irq() means the SKB is dropped in error and dev_consume_skb_irq() means the SKB is consumed in normal. In this case, dev_kfree_skb() is called in mace_tx_timeout() to drop the SKB, when tx timeout, so replace it with dev_kfree_skb_irq(). Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Signed-off-by: Yang Yingliang Signed-off-by: David S. Miller --- drivers/net/ethernet/apple/mace.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/apple/mace.c b/drivers/net/ethernet/apple/mace.c index d0a771b65e88..fd1b008b7208 100644 --- a/drivers/net/ethernet/apple/mace.c +++ b/drivers/net/ethernet/apple/mace.c @@ -846,7 +846,7 @@ static void mace_tx_timeout(struct timer_list *t) if (mp->tx_bad_runt) { mp->tx_bad_runt = 0; } else if (i != mp->tx_fill) { - dev_kfree_skb(mp->tx_bufs[i]); + dev_kfree_skb_irq(mp->tx_bufs[i]); if (++i >= N_TX_RING) i = 0; mp->tx_empty = i; From 5fe02e046e6422c4adfdbc50206ec7186077da24 Mon Sep 17 00:00:00 2001 From: Yang Yingliang Date: Thu, 8 Dec 2022 21:37:35 +0800 Subject: [PATCH 1854/2002] net: apple: bmac: don't call dev_kfree_skb() under spin_lock_irqsave() It is not allowed to call kfree_skb() or consume_skb() from hardware interrupt context or with hardware interrupts being disabled. It should use dev_kfree_skb_irq() or dev_consume_skb_irq() instead. The difference between them is free reason, dev_kfree_skb_irq() means the SKB is dropped in error and dev_consume_skb_irq() means the SKB is consumed in normal. In this case, dev_kfree_skb() is called in bmac_tx_timeout() to drop the SKB, when tx timeout, so replace it with dev_kfree_skb_irq(). Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Signed-off-by: Yang Yingliang Signed-off-by: David S. Miller --- drivers/net/ethernet/apple/bmac.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/apple/bmac.c b/drivers/net/ethernet/apple/bmac.c index 334de0d93c89..9e653e2925f7 100644 --- a/drivers/net/ethernet/apple/bmac.c +++ b/drivers/net/ethernet/apple/bmac.c @@ -1510,7 +1510,7 @@ static void bmac_tx_timeout(struct timer_list *t) i = bp->tx_empty; ++dev->stats.tx_errors; if (i != bp->tx_fill) { - dev_kfree_skb(bp->tx_bufs[i]); + dev_kfree_skb_irq(bp->tx_bufs[i]); bp->tx_bufs[i] = NULL; if (++i >= N_TX_RING) i = 0; bp->tx_empty = i; From d1678bf45f21fa5ae4a456f821858679556ea5f8 Mon Sep 17 00:00:00 2001 From: Yang Yingliang Date: Thu, 8 Dec 2022 22:21:44 +0800 Subject: [PATCH 1855/2002] net: emaclite: don't call dev_kfree_skb() under spin_lock_irqsave() It is not allowed to call kfree_skb() or consume_skb() from hardware interrupt context or with hardware interrupts being disabled. It should use dev_kfree_skb_irq() or dev_consume_skb_irq() instead. The difference between them is free reason, dev_kfree_skb_irq() means the SKB is dropped in error and dev_consume_skb_irq() means the SKB is consumed in normal. In this case, dev_kfree_skb() is called in xemaclite_tx_timeout() to drop the SKB, when tx timeout, so replace it with dev_kfree_skb_irq(). Fixes: bb81b2ddfa19 ("net: add Xilinx emac lite device driver") Signed-off-by: Yang Yingliang Signed-off-by: David S. Miller --- drivers/net/ethernet/xilinx/xilinx_emaclite.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/xilinx/xilinx_emaclite.c b/drivers/net/ethernet/xilinx/xilinx_emaclite.c index a3967f8de417..ad2c30d9a482 100644 --- a/drivers/net/ethernet/xilinx/xilinx_emaclite.c +++ b/drivers/net/ethernet/xilinx/xilinx_emaclite.c @@ -536,7 +536,7 @@ static void xemaclite_tx_timeout(struct net_device *dev, unsigned int txqueue) xemaclite_enable_interrupts(lp); if (lp->deferred_skb) { - dev_kfree_skb(lp->deferred_skb); + dev_kfree_skb_irq(lp->deferred_skb); lp->deferred_skb = NULL; dev->stats.tx_errors++; } From f07fadcbee2a5e84caa67c7c445424200bffb60b Mon Sep 17 00:00:00 2001 From: Yang Yingliang Date: Thu, 8 Dec 2022 22:21:45 +0800 Subject: [PATCH 1856/2002] net: ethernet: dnet: don't call dev_kfree_skb() under spin_lock_irqsave() It is not allowed to call kfree_skb() or consume_skb() from hardware interrupt context or with hardware interrupts being disabled. In this case, the lock is used to protected 'bp', so we can move dev_kfree_skb() after the spin_unlock_irqrestore(). Fixes: 4796417417a6 ("dnet: Dave DNET ethernet controller driver (updated)") Signed-off-by: Yang Yingliang Signed-off-by: David S. Miller --- drivers/net/ethernet/dnet.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/dnet.c b/drivers/net/ethernet/dnet.c index 08184f20f510..151ca9573be9 100644 --- a/drivers/net/ethernet/dnet.c +++ b/drivers/net/ethernet/dnet.c @@ -550,11 +550,11 @@ static netdev_tx_t dnet_start_xmit(struct sk_buff *skb, struct net_device *dev) skb_tx_timestamp(skb); + spin_unlock_irqrestore(&bp->lock, flags); + /* free the buffer */ dev_kfree_skb(skb); - spin_unlock_irqrestore(&bp->lock, flags); - return NETDEV_TX_OK; } From 3727f742915f04f6fc550b80cf406999bd4e90d0 Mon Sep 17 00:00:00 2001 From: Yang Yingliang Date: Thu, 8 Dec 2022 22:21:46 +0800 Subject: [PATCH 1857/2002] hamradio: don't call dev_kfree_skb() under spin_lock_irqsave() It is not allowed to call kfree_skb() or consume_skb() from hardware interrupt context or with hardware interrupts being disabled. It should use dev_kfree_skb_irq() or dev_consume_skb_irq() instead. The difference between them is free reason, dev_kfree_skb_irq() means the SKB is dropped in error and dev_consume_skb_irq() means the SKB is consumed in normal. In scc_discard_buffers(), dev_kfree_skb() is called to discard the SKBs, so replace it with dev_kfree_skb_irq(). In scc_net_tx(), dev_kfree_skb() is called to drop the SKB that exceed queue length, so replace it with dev_kfree_skb_irq(). Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Signed-off-by: Yang Yingliang Signed-off-by: David S. Miller --- drivers/net/hamradio/scc.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/net/hamradio/scc.c b/drivers/net/hamradio/scc.c index f90830d3dfa6..a9184a78650b 100644 --- a/drivers/net/hamradio/scc.c +++ b/drivers/net/hamradio/scc.c @@ -302,12 +302,12 @@ static inline void scc_discard_buffers(struct scc_channel *scc) spin_lock_irqsave(&scc->lock, flags); if (scc->tx_buff != NULL) { - dev_kfree_skb(scc->tx_buff); + dev_kfree_skb_irq(scc->tx_buff); scc->tx_buff = NULL; } while (!skb_queue_empty(&scc->tx_queue)) - dev_kfree_skb(skb_dequeue(&scc->tx_queue)); + dev_kfree_skb_irq(skb_dequeue(&scc->tx_queue)); spin_unlock_irqrestore(&scc->lock, flags); } @@ -1668,7 +1668,7 @@ static netdev_tx_t scc_net_tx(struct sk_buff *skb, struct net_device *dev) if (skb_queue_len(&scc->tx_queue) > scc->dev->tx_queue_len) { struct sk_buff *skb_del; skb_del = skb_dequeue(&scc->tx_queue); - dev_kfree_skb(skb_del); + dev_kfree_skb_irq(skb_del); } skb_queue_tail(&scc->tx_queue, skb); netif_trans_update(dev); From 6151d105dfce8c23edf30eed35e97f3d9b96a35c Mon Sep 17 00:00:00 2001 From: Yang Yingliang Date: Thu, 8 Dec 2022 22:21:47 +0800 Subject: [PATCH 1858/2002] net: amd: lance: don't call dev_kfree_skb() under spin_lock_irqsave() It is not allowed to call kfree_skb() or consume_skb() from hardware interrupt context or with hardware interrupts being disabled. It should use dev_kfree_skb_irq() or dev_consume_skb_irq() instead. The difference between them is free reason, dev_kfree_skb_irq() means the SKB is dropped in error and dev_consume_skb_irq() means the SKB is consumed in normal. In these two cases, dev_kfree_skb() is called consume the xmited SKB, so replace it with dev_consume_skb_irq(). Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Signed-off-by: Yang Yingliang Signed-off-by: David S. Miller --- drivers/net/ethernet/amd/atarilance.c | 2 +- drivers/net/ethernet/amd/lance.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/amd/atarilance.c b/drivers/net/ethernet/amd/atarilance.c index 3222c48ce6ae..ec704222925d 100644 --- a/drivers/net/ethernet/amd/atarilance.c +++ b/drivers/net/ethernet/amd/atarilance.c @@ -824,7 +824,7 @@ lance_start_xmit(struct sk_buff *skb, struct net_device *dev) lp->memcpy_f( PKTBUF_ADDR(head), (void *)skb->data, skb->len ); head->flag = TMD1_OWN_CHIP | TMD1_ENP | TMD1_STP; dev->stats.tx_bytes += skb->len; - dev_kfree_skb( skb ); + dev_consume_skb_irq(skb); lp->cur_tx++; while( lp->cur_tx >= TX_RING_SIZE && lp->dirty_tx >= TX_RING_SIZE ) { lp->cur_tx -= TX_RING_SIZE; diff --git a/drivers/net/ethernet/amd/lance.c b/drivers/net/ethernet/amd/lance.c index fb8686214a32..8971665a4b2a 100644 --- a/drivers/net/ethernet/amd/lance.c +++ b/drivers/net/ethernet/amd/lance.c @@ -1001,7 +1001,7 @@ static netdev_tx_t lance_start_xmit(struct sk_buff *skb, skb_copy_from_linear_data(skb, &lp->tx_bounce_buffs[entry], skb->len); lp->tx_ring[entry].base = ((u32)isa_virt_to_bus((lp->tx_bounce_buffs + entry)) & 0xffffff) | 0x83000000; - dev_kfree_skb(skb); + dev_consume_skb_irq(skb); } else { lp->tx_skbuff[entry] = skb; lp->tx_ring[entry].base = ((u32)isa_virt_to_bus(skb->data) & 0xffffff) | 0x83000000; From 94d30e8933382184b257e94ac0d020d3f56ed614 Mon Sep 17 00:00:00 2001 From: Xu Panda Date: Fri, 9 Dec 2022 15:33:25 +0800 Subject: [PATCH 1859/2002] hns: use strscpy() to instead of strncpy() The implementation of strscpy() is more robust and safer. That's now the recommended way to copy NUL terminated strings. Signed-off-by: Xu Panda Signed-off-by: Yang Yang Reviewed-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/hisilicon/hns/hns_ethtool.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/drivers/net/ethernet/hisilicon/hns/hns_ethtool.c b/drivers/net/ethernet/hisilicon/hns/hns_ethtool.c index 54faf0f2d1d8..b54f3706fb97 100644 --- a/drivers/net/ethernet/hisilicon/hns/hns_ethtool.c +++ b/drivers/net/ethernet/hisilicon/hns/hns_ethtool.c @@ -644,18 +644,15 @@ static void hns_nic_get_drvinfo(struct net_device *net_dev, { struct hns_nic_priv *priv = netdev_priv(net_dev); - strncpy(drvinfo->version, HNAE_DRIVER_VERSION, + strscpy(drvinfo->version, HNAE_DRIVER_VERSION, sizeof(drvinfo->version)); - drvinfo->version[sizeof(drvinfo->version) - 1] = '\0'; - strncpy(drvinfo->driver, HNAE_DRIVER_NAME, sizeof(drvinfo->driver)); - drvinfo->driver[sizeof(drvinfo->driver) - 1] = '\0'; + strscpy(drvinfo->driver, HNAE_DRIVER_NAME, sizeof(drvinfo->driver)); - strncpy(drvinfo->bus_info, priv->dev->bus->name, + strscpy(drvinfo->bus_info, priv->dev->bus->name, sizeof(drvinfo->bus_info)); - drvinfo->bus_info[ETHTOOL_BUSINFO_LEN - 1] = '\0'; - strncpy(drvinfo->fw_version, "N/A", ETHTOOL_FWVERS_LEN); + strscpy(drvinfo->fw_version, "N/A", ETHTOOL_FWVERS_LEN); drvinfo->eedump_len = 0; } From f6b759f56d1e2e3333a14936830e2377cbc9539e Mon Sep 17 00:00:00 2001 From: Xu Panda Date: Fri, 9 Dec 2022 15:34:49 +0800 Subject: [PATCH 1860/2002] liquidio: use strscpy() to instead of strncpy() The implementation of strscpy() is more robust and safer. That's now the recommended way to copy NUL terminated strings. Signed-off-by: Xu Panda Signed-off-by: Yang Yang Reviewed-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/cavium/liquidio/octeon_console.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/net/ethernet/cavium/liquidio/octeon_console.c b/drivers/net/ethernet/cavium/liquidio/octeon_console.c index 28feabec8fbb..67c3570f875f 100644 --- a/drivers/net/ethernet/cavium/liquidio/octeon_console.c +++ b/drivers/net/ethernet/cavium/liquidio/octeon_console.c @@ -247,8 +247,7 @@ static const struct cvmx_bootmem_named_block_desc struct cvmx_bootmem_named_block_desc, size)); - strncpy(desc->name, name, sizeof(desc->name)); - desc->name[sizeof(desc->name) - 1] = 0; + strscpy(desc->name, name, sizeof(desc->name)); return &oct->bootmem_named_block_desc; } else { return NULL; @@ -471,8 +470,8 @@ static void output_console_line(struct octeon_device *oct, if (line != &console_buffer[bytes_read]) { console_buffer[bytes_read] = '\0'; len = strlen(console->leftover); - strncpy(&console->leftover[len], line, - sizeof(console->leftover) - len); + strscpy(&console->leftover[len], line, + sizeof(console->leftover) - len + 1); } } From a2d40ce7acdbf4641235ee8be4b4d338746c03b5 Mon Sep 17 00:00:00 2001 From: Xu Panda Date: Fri, 9 Dec 2022 15:37:29 +0800 Subject: [PATCH 1861/2002] myri10ge: use strscpy() to instead of strncpy() The implementation of strscpy() is more robust and safer. That's now the recommended way to copy NUL terminated strings. Signed-off-by: Xu Panda Signed-off-by: Yang Yang Reviewed-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/myricom/myri10ge/myri10ge.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/net/ethernet/myricom/myri10ge/myri10ge.c b/drivers/net/ethernet/myricom/myri10ge/myri10ge.c index 9063e2e22cd5..8073d7a90a26 100644 --- a/drivers/net/ethernet/myricom/myri10ge/myri10ge.c +++ b/drivers/net/ethernet/myricom/myri10ge/myri10ge.c @@ -552,8 +552,7 @@ myri10ge_validate_firmware(struct myri10ge_priv *mgp, } /* save firmware version for ethtool */ - strncpy(mgp->fw_version, hdr->version, sizeof(mgp->fw_version)); - mgp->fw_version[sizeof(mgp->fw_version) - 1] = '\0'; + strscpy(mgp->fw_version, hdr->version, sizeof(mgp->fw_version)); sscanf(mgp->fw_version, "%d.%d.%d", &mgp->fw_ver_major, &mgp->fw_ver_minor, &mgp->fw_ver_tiny); From 526682b458b1b56d2e0db027df535cb5cdcfde59 Mon Sep 17 00:00:00 2001 From: Richard Gobert Date: Thu, 8 Dec 2022 15:54:46 +0100 Subject: [PATCH 1862/2002] net: setsockopt: fix IPV6_UNICAST_IF option for connected sockets Change the behaviour of ip6_datagram_connect to consider the interface set by the IPV6_UNICAST_IF socket option, similarly to udpv6_sendmsg. This change is the IPv6 counterpart of the fix for IP_UNICAST_IF. The tests introduced by that patch showed that the incorrect behavior is present in IPv6 as well. This patch fixes the broken test. Reported-by: kernel test robot Link: https://lore.kernel.org/r/202210062117.c7eef1a3-oliver.sang@intel.com Fixes: 0e4d354762ce ("net-next: Fix IP_UNICAST_IF option behavior for connected sockets") Signed-off-by: Richard Gobert Reviewed-by: David Ahern Signed-off-by: David S. Miller --- net/ipv6/datagram.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/net/ipv6/datagram.c b/net/ipv6/datagram.c index 5ecb56522f9d..ba28aeb7cade 100644 --- a/net/ipv6/datagram.c +++ b/net/ipv6/datagram.c @@ -42,24 +42,29 @@ static void ip6_datagram_flow_key_init(struct flowi6 *fl6, struct sock *sk) { struct inet_sock *inet = inet_sk(sk); struct ipv6_pinfo *np = inet6_sk(sk); + int oif = sk->sk_bound_dev_if; memset(fl6, 0, sizeof(*fl6)); fl6->flowi6_proto = sk->sk_protocol; fl6->daddr = sk->sk_v6_daddr; fl6->saddr = np->saddr; - fl6->flowi6_oif = sk->sk_bound_dev_if; fl6->flowi6_mark = sk->sk_mark; fl6->fl6_dport = inet->inet_dport; fl6->fl6_sport = inet->inet_sport; fl6->flowlabel = np->flow_label; fl6->flowi6_uid = sk->sk_uid; - if (!fl6->flowi6_oif) - fl6->flowi6_oif = np->sticky_pktinfo.ipi6_ifindex; + if (!oif) + oif = np->sticky_pktinfo.ipi6_ifindex; - if (!fl6->flowi6_oif && ipv6_addr_is_multicast(&fl6->daddr)) - fl6->flowi6_oif = np->mcast_oif; + if (!oif) { + if (ipv6_addr_is_multicast(&fl6->daddr)) + oif = np->mcast_oif; + else + oif = np->ucast_oif; + } + fl6->flowi6_oif = oif; security_sk_classify_flow(sk, flowi6_to_flowi_common(fl6)); } From 73e341e0281a35274629e9be27eae2f9b1b492bf Mon Sep 17 00:00:00 2001 From: Yang Yingliang Date: Thu, 8 Dec 2022 23:01:58 +0800 Subject: [PATCH 1863/2002] af_unix: call proto_unregister() in the error path in af_unix_init() If register unix_stream_proto returns error, unix_dgram_proto needs be unregistered. Fixes: 94531cfcbe79 ("af_unix: Add unix_stream_proto for sockmap") Signed-off-by: Yang Yingliang Reviewed-by: Simon Horman Reviewed-by: Kuniyuki Iwashima Signed-off-by: David S. Miller --- net/unix/af_unix.c | 1 + 1 file changed, 1 insertion(+) diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c index b3545fc68097..ede2b2a140a4 100644 --- a/net/unix/af_unix.c +++ b/net/unix/af_unix.c @@ -3738,6 +3738,7 @@ static int __init af_unix_init(void) rc = proto_register(&unix_stream_proto, 1); if (rc != 0) { pr_crit("%s: Cannot create unix_sock SLAB cache!\n", __func__); + proto_unregister(&unix_dgram_proto); goto out; } From 4998006c73afe44e2f639d55bd331c6c26eb039f Mon Sep 17 00:00:00 2001 From: Tom Lendacky Date: Thu, 8 Dec 2022 10:22:24 -0600 Subject: [PATCH 1864/2002] net: amd-xgbe: Fix logic around active and passive cables SFP+ active and passive cables are copper cables with fixed SFP+ end connectors. Due to a misinterpretation of this, SFP+ active cables could end up not being recognized, causing the driver to fail to establish a connection. Introduce a new enum in SFP+ cable types, XGBE_SFP_CABLE_FIBER, that is the default cable type, and handle active and passive cables when they are specifically detected. Fixes: abf0a1c2b26a ("amd-xgbe: Add support for SFP+ modules") Signed-off-by: Tom Lendacky Signed-off-by: David S. Miller --- drivers/net/ethernet/amd/xgbe/xgbe-phy-v2.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-phy-v2.c b/drivers/net/ethernet/amd/xgbe/xgbe-phy-v2.c index 4064c3e3dd49..868a768f424c 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-phy-v2.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-phy-v2.c @@ -189,6 +189,7 @@ enum xgbe_sfp_cable { XGBE_SFP_CABLE_UNKNOWN = 0, XGBE_SFP_CABLE_ACTIVE, XGBE_SFP_CABLE_PASSIVE, + XGBE_SFP_CABLE_FIBER, }; enum xgbe_sfp_base { @@ -1149,16 +1150,18 @@ static void xgbe_phy_sfp_parse_eeprom(struct xgbe_prv_data *pdata) phy_data->sfp_tx_fault = xgbe_phy_check_sfp_tx_fault(phy_data); phy_data->sfp_rx_los = xgbe_phy_check_sfp_rx_los(phy_data); - /* Assume ACTIVE cable unless told it is PASSIVE */ + /* Assume FIBER cable unless told otherwise */ if (sfp_base[XGBE_SFP_BASE_CABLE] & XGBE_SFP_BASE_CABLE_PASSIVE) { phy_data->sfp_cable = XGBE_SFP_CABLE_PASSIVE; phy_data->sfp_cable_len = sfp_base[XGBE_SFP_BASE_CU_CABLE_LEN]; - } else { + } else if (sfp_base[XGBE_SFP_BASE_CABLE] & XGBE_SFP_BASE_CABLE_ACTIVE) { phy_data->sfp_cable = XGBE_SFP_CABLE_ACTIVE; + } else { + phy_data->sfp_cable = XGBE_SFP_CABLE_FIBER; } /* Determine the type of SFP */ - if (phy_data->sfp_cable == XGBE_SFP_CABLE_PASSIVE && + if (phy_data->sfp_cable != XGBE_SFP_CABLE_FIBER && xgbe_phy_sfp_bit_rate(sfp_eeprom, XGBE_SFP_SPEED_10000)) phy_data->sfp_base = XGBE_SFP_BASE_10000_CR; else if (sfp_base[XGBE_SFP_BASE_10GBE_CC] & XGBE_SFP_BASE_10GBE_CC_SR) From f8ab263d4d48e6dab752029bf562f20a2ee630ed Mon Sep 17 00:00:00 2001 From: Tom Lendacky Date: Thu, 8 Dec 2022 10:22:25 -0600 Subject: [PATCH 1865/2002] net: amd-xgbe: Check only the minimum speed for active/passive cables There are cables that exist that can support speeds in excess of 10GbE. The driver, however, restricts the EEPROM advertised nominal bitrate to a specific range, which can prevent usage of cables that can support, for example, up to 25GbE. Rather than checking that an active or passive cable supports a specific range, only check for a minimum supported speed. Fixes: abf0a1c2b26a ("amd-xgbe: Add support for SFP+ modules") Signed-off-by: Tom Lendacky Signed-off-by: David S. Miller --- drivers/net/ethernet/amd/xgbe/xgbe-phy-v2.c | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-phy-v2.c b/drivers/net/ethernet/amd/xgbe/xgbe-phy-v2.c index 868a768f424c..c731a04731f8 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-phy-v2.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-phy-v2.c @@ -237,10 +237,7 @@ enum xgbe_sfp_speed { #define XGBE_SFP_BASE_BR 12 #define XGBE_SFP_BASE_BR_1GBE_MIN 0x0a -#define XGBE_SFP_BASE_BR_1GBE_MAX 0x0d #define XGBE_SFP_BASE_BR_10GBE_MIN 0x64 -#define XGBE_SFP_BASE_BR_10GBE_MAX 0x68 -#define XGBE_MOLEX_SFP_BASE_BR_10GBE_MAX 0x78 #define XGBE_SFP_BASE_CU_CABLE_LEN 18 @@ -827,29 +824,22 @@ static void xgbe_phy_sfp_phy_settings(struct xgbe_prv_data *pdata) static bool xgbe_phy_sfp_bit_rate(struct xgbe_sfp_eeprom *sfp_eeprom, enum xgbe_sfp_speed sfp_speed) { - u8 *sfp_base, min, max; + u8 *sfp_base, min; sfp_base = sfp_eeprom->base; switch (sfp_speed) { case XGBE_SFP_SPEED_1000: min = XGBE_SFP_BASE_BR_1GBE_MIN; - max = XGBE_SFP_BASE_BR_1GBE_MAX; break; case XGBE_SFP_SPEED_10000: min = XGBE_SFP_BASE_BR_10GBE_MIN; - if (memcmp(&sfp_eeprom->base[XGBE_SFP_BASE_VENDOR_NAME], - XGBE_MOLEX_VENDOR, XGBE_SFP_BASE_VENDOR_NAME_LEN) == 0) - max = XGBE_MOLEX_SFP_BASE_BR_10GBE_MAX; - else - max = XGBE_SFP_BASE_BR_10GBE_MAX; break; default: return false; } - return ((sfp_base[XGBE_SFP_BASE_BR] >= min) && - (sfp_base[XGBE_SFP_BASE_BR] <= max)); + return sfp_base[XGBE_SFP_BASE_BR] >= min; } static void xgbe_phy_free_phy_device(struct xgbe_prv_data *pdata) From bf14f4923d516d77320500461c0692c9d4480c30 Mon Sep 17 00:00:00 2001 From: Xin Long Date: Thu, 8 Dec 2022 11:56:08 -0500 Subject: [PATCH 1866/2002] openvswitch: delete the unncessary skb_pull_rcsum call in ovs_ct_nat_execute The calls to ovs_ct_nat_execute() are as below: ovs_ct_execute() ovs_ct_lookup() __ovs_ct_lookup() ovs_ct_nat() ovs_ct_nat_execute() ovs_ct_commit() __ovs_ct_lookup() ovs_ct_nat() ovs_ct_nat_execute() and since skb_pull_rcsum() and skb_push_rcsum() are already called in ovs_ct_execute(), there's no need to do it again in ovs_ct_nat_execute(). Reviewed-by: Saeed Mahameed Acked-by: Aaron Conole Signed-off-by: Xin Long Signed-off-by: David S. Miller --- net/openvswitch/conntrack.c | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/net/openvswitch/conntrack.c b/net/openvswitch/conntrack.c index d78f0fc4337d..dff093a10d6d 100644 --- a/net/openvswitch/conntrack.c +++ b/net/openvswitch/conntrack.c @@ -735,10 +735,7 @@ static int ovs_ct_nat_execute(struct sk_buff *skb, struct nf_conn *ct, const struct nf_nat_range2 *range, enum nf_nat_manip_type maniptype, struct sw_flow_key *key) { - int hooknum, nh_off, err = NF_ACCEPT; - - nh_off = skb_network_offset(skb); - skb_pull_rcsum(skb, nh_off); + int hooknum, err = NF_ACCEPT; /* See HOOK2MANIP(). */ if (maniptype == NF_NAT_MANIP_SRC) @@ -755,7 +752,7 @@ static int ovs_ct_nat_execute(struct sk_buff *skb, struct nf_conn *ct, if (!nf_nat_icmp_reply_translation(skb, ct, ctinfo, hooknum)) err = NF_DROP; - goto push; + goto out; } else if (IS_ENABLED(CONFIG_IPV6) && skb->protocol == htons(ETH_P_IPV6)) { __be16 frag_off; @@ -770,7 +767,7 @@ static int ovs_ct_nat_execute(struct sk_buff *skb, struct nf_conn *ct, hooknum, hdrlen)) err = NF_DROP; - goto push; + goto out; } } /* Non-ICMP, fall thru to initialize if needed. */ @@ -788,7 +785,7 @@ static int ovs_ct_nat_execute(struct sk_buff *skb, struct nf_conn *ct, ? nf_nat_setup_info(ct, range, maniptype) : nf_nat_alloc_null_binding(ct, hooknum); if (err != NF_ACCEPT) - goto push; + goto out; } break; @@ -798,13 +795,11 @@ static int ovs_ct_nat_execute(struct sk_buff *skb, struct nf_conn *ct, default: err = NF_DROP; - goto push; + goto out; } err = nf_nat_packet(ct, ctinfo, hooknum, skb); -push: - skb_push_rcsum(skb, nh_off); - +out: /* Update the flow key if NAT successful. */ if (err == NF_ACCEPT) ovs_nat_update_key(key, skb, maniptype); From 7795928921332fdd52c33eab73f1280d5e58678a Mon Sep 17 00:00:00 2001 From: Xin Long Date: Thu, 8 Dec 2022 11:56:09 -0500 Subject: [PATCH 1867/2002] openvswitch: return NF_ACCEPT when OVS_CT_NAT is not set in info nat Either OVS_CT_SRC_NAT or OVS_CT_DST_NAT is set, OVS_CT_NAT must be set in info->nat. Thus, if OVS_CT_NAT is not set in info->nat, it will definitely not do NAT but returns NF_ACCEPT in ovs_ct_nat(). This patch changes nothing funcational but only makes this return earlier in ovs_ct_nat() to keep consistent with TC's processing in tcf_ct_act_nat(). Reviewed-by: Saeed Mahameed Acked-by: Aaron Conole Signed-off-by: Xin Long Signed-off-by: David S. Miller --- net/openvswitch/conntrack.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/net/openvswitch/conntrack.c b/net/openvswitch/conntrack.c index dff093a10d6d..5ea74270da46 100644 --- a/net/openvswitch/conntrack.c +++ b/net/openvswitch/conntrack.c @@ -816,6 +816,9 @@ static int ovs_ct_nat(struct net *net, struct sw_flow_key *key, enum nf_nat_manip_type maniptype; int err; + if (!(info->nat & OVS_CT_NAT)) + return NF_ACCEPT; + /* Add NAT extension if not confirmed yet. */ if (!nf_ct_is_confirmed(ct) && !nf_ct_nat_ext_add(ct)) return NF_ACCEPT; /* Can't NAT. */ @@ -825,8 +828,7 @@ static int ovs_ct_nat(struct net *net, struct sw_flow_key *key, * Make sure new expected connections (IP_CT_RELATED) are NATted only * when committing. */ - if (info->nat & OVS_CT_NAT && ctinfo != IP_CT_NEW && - ct->status & IPS_NAT_MASK && + if (ctinfo != IP_CT_NEW && ct->status & IPS_NAT_MASK && (ctinfo != IP_CT_RELATED || info->commit)) { /* NAT an established or related connection like before. */ if (CTINFO2DIR(ctinfo) == IP_CT_DIR_REPLY) From 2b85144ab36e0e870f59b5ae55e299179eb8cdb8 Mon Sep 17 00:00:00 2001 From: Xin Long Date: Thu, 8 Dec 2022 11:56:10 -0500 Subject: [PATCH 1868/2002] openvswitch: return NF_DROP when fails to add nat ext in ovs_ct_nat When it fails to allocate nat ext, the packet should be dropped, like the memory allocation failures in other places in ovs_ct_nat(). This patch changes to return NF_DROP when fails to add nat ext before doing NAT in ovs_ct_nat(), also it would keep consistent with tc action ct' processing in tcf_ct_act_nat(). Signed-off-by: Xin Long Acked-by: Aaron Conole Signed-off-by: David S. Miller --- net/openvswitch/conntrack.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/openvswitch/conntrack.c b/net/openvswitch/conntrack.c index 5ea74270da46..58c9f0edc3c4 100644 --- a/net/openvswitch/conntrack.c +++ b/net/openvswitch/conntrack.c @@ -821,7 +821,7 @@ static int ovs_ct_nat(struct net *net, struct sw_flow_key *key, /* Add NAT extension if not confirmed yet. */ if (!nf_ct_is_confirmed(ct) && !nf_ct_nat_ext_add(ct)) - return NF_ACCEPT; /* Can't NAT. */ + return NF_DROP; /* Can't NAT. */ /* Determine NAT type. * Check if the NAT type can be deduced from the tracked connection. From 0564c3e51bc7bb200e76d0cad2d7067cc77cb83e Mon Sep 17 00:00:00 2001 From: Xin Long Date: Thu, 8 Dec 2022 11:56:11 -0500 Subject: [PATCH 1869/2002] net: sched: update the nat flag for icmp error packets in ct_nat_execute In ovs_ct_nat_execute(), the packet flow key nat flags are updated when it processes ICMP(v6) error packets translation successfully. In ct_nat_execute() when processing ICMP(v6) error packets translation successfully, it should have done the same in ct_nat_execute() to set post_ct_s/dnat flag, which will be used to update flow key nat flags in OVS module later. Reviewed-by: Saeed Mahameed Signed-off-by: Xin Long Signed-off-by: David S. Miller --- net/sched/act_ct.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/sched/act_ct.c b/net/sched/act_ct.c index f6df0168c91f..eac4e07eb56b 100644 --- a/net/sched/act_ct.c +++ b/net/sched/act_ct.c @@ -937,13 +937,13 @@ static int ct_nat_execute(struct sk_buff *skb, struct nf_conn *ct, } err = nf_nat_packet(ct, ctinfo, hooknum, skb); +out: if (err == NF_ACCEPT) { if (maniptype == NF_NAT_MANIP_SRC) tc_skb_cb(skb)->post_ct_snat = 1; if (maniptype == NF_NAT_MANIP_DST) tc_skb_cb(skb)->post_ct_dnat = 1; } -out: return err; } #endif /* CONFIG_NF_NAT */ From ebddb1404900657b7f03a56ee4c34a9d218c4030 Mon Sep 17 00:00:00 2001 From: Xin Long Date: Thu, 8 Dec 2022 11:56:12 -0500 Subject: [PATCH 1870/2002] net: move the nat function to nf_nat_ovs for ovs and tc There are two nat functions are nearly the same in both OVS and TC code, (ovs_)ct_nat_execute() and ovs_ct_nat/tcf_ct_act_nat(). This patch creates nf_nat_ovs.c under netfilter and moves them there then exports nf_ct_nat() so that it can be shared by both OVS and TC, and keeps the nat (type) check and nat flag update in OVS and TC's own place, as these parts are different between OVS and TC. Note that in OVS nat function it was using skb->protocol to get the proto as it already skips vlans in key_extract(), while it doesn't in TC, and TC has to call skb_protocol() to get proto. So in nf_ct_nat_execute(), we keep using skb_protocol() which works for both OVS and TC contrack. Signed-off-by: Xin Long Acked-by: Aaron Conole Acked-by: Pablo Neira Ayuso Signed-off-by: David S. Miller --- include/net/netfilter/nf_nat.h | 4 + net/netfilter/Kconfig | 3 + net/netfilter/Makefile | 1 + net/netfilter/nf_nat_ovs.c | 135 ++++++++++++++++++++++++++++++++ net/openvswitch/Kconfig | 1 + net/openvswitch/conntrack.c | 137 +++------------------------------ net/sched/Kconfig | 1 + net/sched/act_ct.c | 134 +++----------------------------- 8 files changed, 165 insertions(+), 251 deletions(-) create mode 100644 net/netfilter/nf_nat_ovs.c diff --git a/include/net/netfilter/nf_nat.h b/include/net/netfilter/nf_nat.h index e9eb01e99d2f..9877f064548a 100644 --- a/include/net/netfilter/nf_nat.h +++ b/include/net/netfilter/nf_nat.h @@ -104,6 +104,10 @@ unsigned int nf_nat_inet_fn(void *priv, struct sk_buff *skb, const struct nf_hook_state *state); +int nf_ct_nat(struct sk_buff *skb, struct nf_conn *ct, + enum ip_conntrack_info ctinfo, int *action, + const struct nf_nat_range2 *range, bool commit); + static inline int nf_nat_initialized(const struct nf_conn *ct, enum nf_nat_manip_type manip) { diff --git a/net/netfilter/Kconfig b/net/netfilter/Kconfig index 0846bd75b1da..f71b41c7ce2f 100644 --- a/net/netfilter/Kconfig +++ b/net/netfilter/Kconfig @@ -459,6 +459,9 @@ config NF_NAT_REDIRECT config NF_NAT_MASQUERADE bool +config NF_NAT_OVS + bool + config NETFILTER_SYNPROXY tristate diff --git a/net/netfilter/Makefile b/net/netfilter/Makefile index 1d4db1943936..3754eb06fb41 100644 --- a/net/netfilter/Makefile +++ b/net/netfilter/Makefile @@ -59,6 +59,7 @@ obj-$(CONFIG_NF_LOG_SYSLOG) += nf_log_syslog.o obj-$(CONFIG_NF_NAT) += nf_nat.o nf_nat-$(CONFIG_NF_NAT_REDIRECT) += nf_nat_redirect.o nf_nat-$(CONFIG_NF_NAT_MASQUERADE) += nf_nat_masquerade.o +nf_nat-$(CONFIG_NF_NAT_OVS) += nf_nat_ovs.o ifeq ($(CONFIG_NF_NAT),m) nf_nat-$(CONFIG_DEBUG_INFO_BTF_MODULES) += nf_nat_bpf.o diff --git a/net/netfilter/nf_nat_ovs.c b/net/netfilter/nf_nat_ovs.c new file mode 100644 index 000000000000..551abd2da614 --- /dev/null +++ b/net/netfilter/nf_nat_ovs.c @@ -0,0 +1,135 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* Support nat functions for openvswitch and used by OVS and TC conntrack. */ + +#include + +/* Modelled after nf_nat_ipv[46]_fn(). + * range is only used for new, uninitialized NAT state. + * Returns either NF_ACCEPT or NF_DROP. + */ +static int nf_ct_nat_execute(struct sk_buff *skb, struct nf_conn *ct, + enum ip_conntrack_info ctinfo, int *action, + const struct nf_nat_range2 *range, + enum nf_nat_manip_type maniptype) +{ + __be16 proto = skb_protocol(skb, true); + int hooknum, err = NF_ACCEPT; + + /* See HOOK2MANIP(). */ + if (maniptype == NF_NAT_MANIP_SRC) + hooknum = NF_INET_LOCAL_IN; /* Source NAT */ + else + hooknum = NF_INET_LOCAL_OUT; /* Destination NAT */ + + switch (ctinfo) { + case IP_CT_RELATED: + case IP_CT_RELATED_REPLY: + if (proto == htons(ETH_P_IP) && + ip_hdr(skb)->protocol == IPPROTO_ICMP) { + if (!nf_nat_icmp_reply_translation(skb, ct, ctinfo, + hooknum)) + err = NF_DROP; + goto out; + } else if (IS_ENABLED(CONFIG_IPV6) && proto == htons(ETH_P_IPV6)) { + __be16 frag_off; + u8 nexthdr = ipv6_hdr(skb)->nexthdr; + int hdrlen = ipv6_skip_exthdr(skb, + sizeof(struct ipv6hdr), + &nexthdr, &frag_off); + + if (hdrlen >= 0 && nexthdr == IPPROTO_ICMPV6) { + if (!nf_nat_icmpv6_reply_translation(skb, ct, + ctinfo, + hooknum, + hdrlen)) + err = NF_DROP; + goto out; + } + } + /* Non-ICMP, fall thru to initialize if needed. */ + fallthrough; + case IP_CT_NEW: + /* Seen it before? This can happen for loopback, retrans, + * or local packets. + */ + if (!nf_nat_initialized(ct, maniptype)) { + /* Initialize according to the NAT action. */ + err = (range && range->flags & NF_NAT_RANGE_MAP_IPS) + /* Action is set up to establish a new + * mapping. + */ + ? nf_nat_setup_info(ct, range, maniptype) + : nf_nat_alloc_null_binding(ct, hooknum); + if (err != NF_ACCEPT) + goto out; + } + break; + + case IP_CT_ESTABLISHED: + case IP_CT_ESTABLISHED_REPLY: + break; + + default: + err = NF_DROP; + goto out; + } + + err = nf_nat_packet(ct, ctinfo, hooknum, skb); + if (err == NF_ACCEPT) + *action |= BIT(maniptype); +out: + return err; +} + +int nf_ct_nat(struct sk_buff *skb, struct nf_conn *ct, + enum ip_conntrack_info ctinfo, int *action, + const struct nf_nat_range2 *range, bool commit) +{ + enum nf_nat_manip_type maniptype; + int err, ct_action = *action; + + *action = 0; + + /* Add NAT extension if not confirmed yet. */ + if (!nf_ct_is_confirmed(ct) && !nf_ct_nat_ext_add(ct)) + return NF_DROP; /* Can't NAT. */ + + if (ctinfo != IP_CT_NEW && (ct->status & IPS_NAT_MASK) && + (ctinfo != IP_CT_RELATED || commit)) { + /* NAT an established or related connection like before. */ + if (CTINFO2DIR(ctinfo) == IP_CT_DIR_REPLY) + /* This is the REPLY direction for a connection + * for which NAT was applied in the forward + * direction. Do the reverse NAT. + */ + maniptype = ct->status & IPS_SRC_NAT + ? NF_NAT_MANIP_DST : NF_NAT_MANIP_SRC; + else + maniptype = ct->status & IPS_SRC_NAT + ? NF_NAT_MANIP_SRC : NF_NAT_MANIP_DST; + } else if (ct_action & BIT(NF_NAT_MANIP_SRC)) { + maniptype = NF_NAT_MANIP_SRC; + } else if (ct_action & BIT(NF_NAT_MANIP_DST)) { + maniptype = NF_NAT_MANIP_DST; + } else { + return NF_ACCEPT; + } + + err = nf_ct_nat_execute(skb, ct, ctinfo, action, range, maniptype); + if (err == NF_ACCEPT && ct->status & IPS_DST_NAT) { + if (ct->status & IPS_SRC_NAT) { + if (maniptype == NF_NAT_MANIP_SRC) + maniptype = NF_NAT_MANIP_DST; + else + maniptype = NF_NAT_MANIP_SRC; + + err = nf_ct_nat_execute(skb, ct, ctinfo, action, range, + maniptype); + } else if (CTINFO2DIR(ctinfo) == IP_CT_DIR_ORIGINAL) { + err = nf_ct_nat_execute(skb, ct, ctinfo, action, NULL, + NF_NAT_MANIP_SRC); + } + } + return err; +} +EXPORT_SYMBOL_GPL(nf_ct_nat); diff --git a/net/openvswitch/Kconfig b/net/openvswitch/Kconfig index 15bd287f5cbd..747d537a3f06 100644 --- a/net/openvswitch/Kconfig +++ b/net/openvswitch/Kconfig @@ -15,6 +15,7 @@ config OPENVSWITCH select NET_MPLS_GSO select DST_CACHE select NET_NSH + select NF_NAT_OVS if NF_NAT help Open vSwitch is a multilayer Ethernet switch targeted at virtualized environments. In addition to supporting a variety of features diff --git a/net/openvswitch/conntrack.c b/net/openvswitch/conntrack.c index 58c9f0edc3c4..c8b137649ca4 100644 --- a/net/openvswitch/conntrack.c +++ b/net/openvswitch/conntrack.c @@ -726,144 +726,27 @@ static void ovs_nat_update_key(struct sw_flow_key *key, } } -/* Modelled after nf_nat_ipv[46]_fn(). - * range is only used for new, uninitialized NAT state. - * Returns either NF_ACCEPT or NF_DROP. - */ -static int ovs_ct_nat_execute(struct sk_buff *skb, struct nf_conn *ct, - enum ip_conntrack_info ctinfo, - const struct nf_nat_range2 *range, - enum nf_nat_manip_type maniptype, struct sw_flow_key *key) -{ - int hooknum, err = NF_ACCEPT; - - /* See HOOK2MANIP(). */ - if (maniptype == NF_NAT_MANIP_SRC) - hooknum = NF_INET_LOCAL_IN; /* Source NAT */ - else - hooknum = NF_INET_LOCAL_OUT; /* Destination NAT */ - - switch (ctinfo) { - case IP_CT_RELATED: - case IP_CT_RELATED_REPLY: - if (IS_ENABLED(CONFIG_NF_NAT) && - skb->protocol == htons(ETH_P_IP) && - ip_hdr(skb)->protocol == IPPROTO_ICMP) { - if (!nf_nat_icmp_reply_translation(skb, ct, ctinfo, - hooknum)) - err = NF_DROP; - goto out; - } else if (IS_ENABLED(CONFIG_IPV6) && - skb->protocol == htons(ETH_P_IPV6)) { - __be16 frag_off; - u8 nexthdr = ipv6_hdr(skb)->nexthdr; - int hdrlen = ipv6_skip_exthdr(skb, - sizeof(struct ipv6hdr), - &nexthdr, &frag_off); - - if (hdrlen >= 0 && nexthdr == IPPROTO_ICMPV6) { - if (!nf_nat_icmpv6_reply_translation(skb, ct, - ctinfo, - hooknum, - hdrlen)) - err = NF_DROP; - goto out; - } - } - /* Non-ICMP, fall thru to initialize if needed. */ - fallthrough; - case IP_CT_NEW: - /* Seen it before? This can happen for loopback, retrans, - * or local packets. - */ - if (!nf_nat_initialized(ct, maniptype)) { - /* Initialize according to the NAT action. */ - err = (range && range->flags & NF_NAT_RANGE_MAP_IPS) - /* Action is set up to establish a new - * mapping. - */ - ? nf_nat_setup_info(ct, range, maniptype) - : nf_nat_alloc_null_binding(ct, hooknum); - if (err != NF_ACCEPT) - goto out; - } - break; - - case IP_CT_ESTABLISHED: - case IP_CT_ESTABLISHED_REPLY: - break; - - default: - err = NF_DROP; - goto out; - } - - err = nf_nat_packet(ct, ctinfo, hooknum, skb); -out: - /* Update the flow key if NAT successful. */ - if (err == NF_ACCEPT) - ovs_nat_update_key(key, skb, maniptype); - - return err; -} - /* Returns NF_DROP if the packet should be dropped, NF_ACCEPT otherwise. */ static int ovs_ct_nat(struct net *net, struct sw_flow_key *key, const struct ovs_conntrack_info *info, struct sk_buff *skb, struct nf_conn *ct, enum ip_conntrack_info ctinfo) { - enum nf_nat_manip_type maniptype; - int err; + int err, action = 0; if (!(info->nat & OVS_CT_NAT)) return NF_ACCEPT; + if (info->nat & OVS_CT_SRC_NAT) + action |= BIT(NF_NAT_MANIP_SRC); + if (info->nat & OVS_CT_DST_NAT) + action |= BIT(NF_NAT_MANIP_DST); - /* Add NAT extension if not confirmed yet. */ - if (!nf_ct_is_confirmed(ct) && !nf_ct_nat_ext_add(ct)) - return NF_DROP; /* Can't NAT. */ + err = nf_ct_nat(skb, ct, ctinfo, &action, &info->range, info->commit); - /* Determine NAT type. - * Check if the NAT type can be deduced from the tracked connection. - * Make sure new expected connections (IP_CT_RELATED) are NATted only - * when committing. - */ - if (ctinfo != IP_CT_NEW && ct->status & IPS_NAT_MASK && - (ctinfo != IP_CT_RELATED || info->commit)) { - /* NAT an established or related connection like before. */ - if (CTINFO2DIR(ctinfo) == IP_CT_DIR_REPLY) - /* This is the REPLY direction for a connection - * for which NAT was applied in the forward - * direction. Do the reverse NAT. - */ - maniptype = ct->status & IPS_SRC_NAT - ? NF_NAT_MANIP_DST : NF_NAT_MANIP_SRC; - else - maniptype = ct->status & IPS_SRC_NAT - ? NF_NAT_MANIP_SRC : NF_NAT_MANIP_DST; - } else if (info->nat & OVS_CT_SRC_NAT) { - maniptype = NF_NAT_MANIP_SRC; - } else if (info->nat & OVS_CT_DST_NAT) { - maniptype = NF_NAT_MANIP_DST; - } else { - return NF_ACCEPT; /* Connection is not NATed. */ - } - err = ovs_ct_nat_execute(skb, ct, ctinfo, &info->range, maniptype, key); - - if (err == NF_ACCEPT && ct->status & IPS_DST_NAT) { - if (ct->status & IPS_SRC_NAT) { - if (maniptype == NF_NAT_MANIP_SRC) - maniptype = NF_NAT_MANIP_DST; - else - maniptype = NF_NAT_MANIP_SRC; - - err = ovs_ct_nat_execute(skb, ct, ctinfo, &info->range, - maniptype, key); - } else if (CTINFO2DIR(ctinfo) == IP_CT_DIR_ORIGINAL) { - err = ovs_ct_nat_execute(skb, ct, ctinfo, NULL, - NF_NAT_MANIP_SRC, key); - } - } + if (action & BIT(NF_NAT_MANIP_SRC)) + ovs_nat_update_key(key, skb, NF_NAT_MANIP_SRC); + if (action & BIT(NF_NAT_MANIP_DST)) + ovs_nat_update_key(key, skb, NF_NAT_MANIP_DST); return err; } diff --git a/net/sched/Kconfig b/net/sched/Kconfig index 4662a6ce8a7e..777d6b50505c 100644 --- a/net/sched/Kconfig +++ b/net/sched/Kconfig @@ -977,6 +977,7 @@ config NET_ACT_TUNNEL_KEY config NET_ACT_CT tristate "connection tracking tc action" depends on NET_CLS_ACT && NF_CONNTRACK && (!NF_NAT || NF_NAT) && NF_FLOW_TABLE + select NF_NAT_OVS if NF_NAT help Say Y here to allow sending the packets to conntrack module. diff --git a/net/sched/act_ct.c b/net/sched/act_ct.c index eac4e07eb56b..0ca2bb8ed026 100644 --- a/net/sched/act_ct.c +++ b/net/sched/act_ct.c @@ -864,90 +864,6 @@ static void tcf_ct_params_free_rcu(struct rcu_head *head) tcf_ct_params_free(params); } -#if IS_ENABLED(CONFIG_NF_NAT) -/* Modelled after nf_nat_ipv[46]_fn(). - * range is only used for new, uninitialized NAT state. - * Returns either NF_ACCEPT or NF_DROP. - */ -static int ct_nat_execute(struct sk_buff *skb, struct nf_conn *ct, - enum ip_conntrack_info ctinfo, - const struct nf_nat_range2 *range, - enum nf_nat_manip_type maniptype) -{ - __be16 proto = skb_protocol(skb, true); - int hooknum, err = NF_ACCEPT; - - /* See HOOK2MANIP(). */ - if (maniptype == NF_NAT_MANIP_SRC) - hooknum = NF_INET_LOCAL_IN; /* Source NAT */ - else - hooknum = NF_INET_LOCAL_OUT; /* Destination NAT */ - - switch (ctinfo) { - case IP_CT_RELATED: - case IP_CT_RELATED_REPLY: - if (proto == htons(ETH_P_IP) && - ip_hdr(skb)->protocol == IPPROTO_ICMP) { - if (!nf_nat_icmp_reply_translation(skb, ct, ctinfo, - hooknum)) - err = NF_DROP; - goto out; - } else if (IS_ENABLED(CONFIG_IPV6) && proto == htons(ETH_P_IPV6)) { - __be16 frag_off; - u8 nexthdr = ipv6_hdr(skb)->nexthdr; - int hdrlen = ipv6_skip_exthdr(skb, - sizeof(struct ipv6hdr), - &nexthdr, &frag_off); - - if (hdrlen >= 0 && nexthdr == IPPROTO_ICMPV6) { - if (!nf_nat_icmpv6_reply_translation(skb, ct, - ctinfo, - hooknum, - hdrlen)) - err = NF_DROP; - goto out; - } - } - /* Non-ICMP, fall thru to initialize if needed. */ - fallthrough; - case IP_CT_NEW: - /* Seen it before? This can happen for loopback, retrans, - * or local packets. - */ - if (!nf_nat_initialized(ct, maniptype)) { - /* Initialize according to the NAT action. */ - err = (range && range->flags & NF_NAT_RANGE_MAP_IPS) - /* Action is set up to establish a new - * mapping. - */ - ? nf_nat_setup_info(ct, range, maniptype) - : nf_nat_alloc_null_binding(ct, hooknum); - if (err != NF_ACCEPT) - goto out; - } - break; - - case IP_CT_ESTABLISHED: - case IP_CT_ESTABLISHED_REPLY: - break; - - default: - err = NF_DROP; - goto out; - } - - err = nf_nat_packet(ct, ctinfo, hooknum, skb); -out: - if (err == NF_ACCEPT) { - if (maniptype == NF_NAT_MANIP_SRC) - tc_skb_cb(skb)->post_ct_snat = 1; - if (maniptype == NF_NAT_MANIP_DST) - tc_skb_cb(skb)->post_ct_dnat = 1; - } - return err; -} -#endif /* CONFIG_NF_NAT */ - static void tcf_ct_act_set_mark(struct nf_conn *ct, u32 mark, u32 mask) { #if IS_ENABLED(CONFIG_NF_CONNTRACK_MARK) @@ -987,52 +903,22 @@ static int tcf_ct_act_nat(struct sk_buff *skb, bool commit) { #if IS_ENABLED(CONFIG_NF_NAT) - int err; - enum nf_nat_manip_type maniptype; + int err, action = 0; if (!(ct_action & TCA_CT_ACT_NAT)) return NF_ACCEPT; + if (ct_action & TCA_CT_ACT_NAT_SRC) + action |= BIT(NF_NAT_MANIP_SRC); + if (ct_action & TCA_CT_ACT_NAT_DST) + action |= BIT(NF_NAT_MANIP_DST); - /* Add NAT extension if not confirmed yet. */ - if (!nf_ct_is_confirmed(ct) && !nf_ct_nat_ext_add(ct)) - return NF_DROP; /* Can't NAT. */ + err = nf_ct_nat(skb, ct, ctinfo, &action, range, commit); - if (ctinfo != IP_CT_NEW && (ct->status & IPS_NAT_MASK) && - (ctinfo != IP_CT_RELATED || commit)) { - /* NAT an established or related connection like before. */ - if (CTINFO2DIR(ctinfo) == IP_CT_DIR_REPLY) - /* This is the REPLY direction for a connection - * for which NAT was applied in the forward - * direction. Do the reverse NAT. - */ - maniptype = ct->status & IPS_SRC_NAT - ? NF_NAT_MANIP_DST : NF_NAT_MANIP_SRC; - else - maniptype = ct->status & IPS_SRC_NAT - ? NF_NAT_MANIP_SRC : NF_NAT_MANIP_DST; - } else if (ct_action & TCA_CT_ACT_NAT_SRC) { - maniptype = NF_NAT_MANIP_SRC; - } else if (ct_action & TCA_CT_ACT_NAT_DST) { - maniptype = NF_NAT_MANIP_DST; - } else { - return NF_ACCEPT; - } + if (action & BIT(NF_NAT_MANIP_SRC)) + tc_skb_cb(skb)->post_ct_snat = 1; + if (action & BIT(NF_NAT_MANIP_DST)) + tc_skb_cb(skb)->post_ct_dnat = 1; - err = ct_nat_execute(skb, ct, ctinfo, range, maniptype); - if (err == NF_ACCEPT && ct->status & IPS_DST_NAT) { - if (ct->status & IPS_SRC_NAT) { - if (maniptype == NF_NAT_MANIP_SRC) - maniptype = NF_NAT_MANIP_DST; - else - maniptype = NF_NAT_MANIP_SRC; - - err = ct_nat_execute(skb, ct, ctinfo, range, - maniptype); - } else if (CTINFO2DIR(ctinfo) == IP_CT_DIR_ORIGINAL) { - err = ct_nat_execute(skb, ct, ctinfo, NULL, - NF_NAT_MANIP_SRC); - } - } return err; #else return NF_ACCEPT; From 005c54278b3dd38f6045a2450a8c988cc7d3def2 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Thu, 24 Nov 2022 14:16:04 +0100 Subject: [PATCH 1871/2002] can: ctucanfd: Drop obsolete dependency on COMPILE_TEST Since commit 0166dc11be91 ("of: make CONFIG_OF user selectable"), it is possible to test-build any driver which depends on OF on any architecture by explicitly selecting OF. Therefore depending on COMPILE_TEST as an alternative is no longer needed. It is actually better to always build such drivers with OF enabled, so that the test builds are closer to how each driver will actually be built on its intended target. Building them without OF may not test much as the compiler will optimize out potentially large parts of the code. In the worst case, this could even pop false positive warnings. Dropping COMPILE_TEST here improves the quality of our testing and avoids wasting time on non-existent issues. Signed-off-by: Jean Delvare Cc: Pavel Pisa Cc: Ondrej Ille Acked-by: Pavel Pisa Link: https://lore.kernel.org/all/20221124141604.4265225f@endymion.delvare Signed-off-by: Marc Kleine-Budde --- drivers/net/can/ctucanfd/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/can/ctucanfd/Kconfig b/drivers/net/can/ctucanfd/Kconfig index 6e2073351a8f..f52407f5c5d8 100644 --- a/drivers/net/can/ctucanfd/Kconfig +++ b/drivers/net/can/ctucanfd/Kconfig @@ -23,7 +23,7 @@ config CAN_CTUCANFD_PCI config CAN_CTUCANFD_PLATFORM tristate "CTU CAN-FD IP core platform (FPGA, SoC) driver" - depends on HAS_IOMEM && (OF || COMPILE_TEST) + depends on HAS_IOMEM && OF select CAN_CTUCANFD help The core has been tested together with OpenCores SJA1000 From 8fd9323ef7210b90d1d209dd4f0d65a8411b60e1 Mon Sep 17 00:00:00 2001 From: Vincent Mailhol Date: Sun, 27 Nov 2022 01:05:25 +0900 Subject: [PATCH 1872/2002] can: etas_es58x: sort the includes by alphabetic order Follow the best practices, reorder the includes. While doing so, bump up copyright year of each modified files. Signed-off-by: Vincent Mailhol Link: https://lore.kernel.org/all/20221126160525.87036-1-mailhol.vincent@wanadoo.fr Signed-off-by: Marc Kleine-Budde --- drivers/net/can/usb/etas_es58x/es581_4.c | 4 ++-- drivers/net/can/usb/etas_es58x/es58x_core.c | 6 +++--- drivers/net/can/usb/etas_es58x/es58x_core.h | 8 ++++---- drivers/net/can/usb/etas_es58x/es58x_fd.c | 4 ++-- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/drivers/net/can/usb/etas_es58x/es581_4.c b/drivers/net/can/usb/etas_es58x/es581_4.c index 1bcdcece5ec7..4151b18fd045 100644 --- a/drivers/net/can/usb/etas_es58x/es581_4.c +++ b/drivers/net/can/usb/etas_es58x/es581_4.c @@ -6,12 +6,12 @@ * * Copyright (c) 2019 Robert Bosch Engineering and Business Solutions. All rights reserved. * Copyright (c) 2020 ETAS K.K.. All rights reserved. - * Copyright (c) 2020, 2021 Vincent Mailhol + * Copyright (c) 2020-2022 Vincent Mailhol */ +#include #include #include -#include #include "es58x_core.h" #include "es581_4.h" diff --git a/drivers/net/can/usb/etas_es58x/es58x_core.c b/drivers/net/can/usb/etas_es58x/es58x_core.c index ddb7c5735c9a..5aba16849603 100644 --- a/drivers/net/can/usb/etas_es58x/es58x_core.c +++ b/drivers/net/can/usb/etas_es58x/es58x_core.c @@ -7,15 +7,15 @@ * * Copyright (c) 2019 Robert Bosch Engineering and Business Solutions. All rights reserved. * Copyright (c) 2020 ETAS K.K.. All rights reserved. - * Copyright (c) 2020, 2021 Vincent Mailhol + * Copyright (c) 2020-2022 Vincent Mailhol */ +#include +#include #include #include #include #include -#include -#include #include "es58x_core.h" diff --git a/drivers/net/can/usb/etas_es58x/es58x_core.h b/drivers/net/can/usb/etas_es58x/es58x_core.h index 640fe0a1df63..4a082fd69e6f 100644 --- a/drivers/net/can/usb/etas_es58x/es58x_core.h +++ b/drivers/net/can/usb/etas_es58x/es58x_core.h @@ -6,17 +6,17 @@ * * Copyright (c) 2019 Robert Bosch Engineering and Business Solutions. All rights reserved. * Copyright (c) 2020 ETAS K.K.. All rights reserved. - * Copyright (c) 2020, 2021 Vincent Mailhol + * Copyright (c) 2020-2022 Vincent Mailhol */ #ifndef __ES58X_COMMON_H__ #define __ES58X_COMMON_H__ -#include -#include -#include #include #include +#include +#include +#include #include "es581_4.h" #include "es58x_fd.h" diff --git a/drivers/net/can/usb/etas_es58x/es58x_fd.c b/drivers/net/can/usb/etas_es58x/es58x_fd.c index c97ffa71fd75..fa87b0b78e3e 100644 --- a/drivers/net/can/usb/etas_es58x/es58x_fd.c +++ b/drivers/net/can/usb/etas_es58x/es58x_fd.c @@ -8,12 +8,12 @@ * * Copyright (c) 2019 Robert Bosch Engineering and Business Solutions. All rights reserved. * Copyright (c) 2020 ETAS K.K.. All rights reserved. - * Copyright (c) 2020, 2021 Vincent Mailhol + * Copyright (c) 2020-2022 Vincent Mailhol */ +#include #include #include -#include #include "es58x_core.h" #include "es58x_fd.h" From 8cb53b485f18732c1e0671a0e826063de47650a6 Mon Sep 17 00:00:00 2001 From: Haibo Chen Date: Tue, 22 Nov 2022 19:32:30 +0800 Subject: [PATCH 1873/2002] can: flexcan: add auto stop mode for IMX93 to support wakeup IMX93 do not contain a GPR to config the stop mode, it will set the flexcan into stop mode automatically once the ARM core go into low power mode (WFI instruct) and gate off the flexcan related clock automatically. But to let these logic work as expect, before ARM core go into low power mode, need to make sure the flexcan related clock keep on. To support stop mode and wakeup feature on imx93, this patch add a new fsl_imx93_devtype_data to separate from imx8mp. Signed-off-by: Haibo Chen Link: https://lore.kernel.org/all/1669116752-4260-1-git-send-email-haibo.chen@nxp.com Signed-off-by: Marc Kleine-Budde --- drivers/net/can/flexcan/flexcan-core.c | 37 +++++++++++++++++++++++--- drivers/net/can/flexcan/flexcan.h | 2 ++ 2 files changed, 36 insertions(+), 3 deletions(-) diff --git a/drivers/net/can/flexcan/flexcan-core.c b/drivers/net/can/flexcan/flexcan-core.c index 9bdadd716f4e..0aeff34e5ae1 100644 --- a/drivers/net/can/flexcan/flexcan-core.c +++ b/drivers/net/can/flexcan/flexcan-core.c @@ -345,6 +345,15 @@ static struct flexcan_devtype_data fsl_imx8mp_devtype_data = { FLEXCAN_QUIRK_SUPPORT_RX_MAILBOX_RTR, }; +static struct flexcan_devtype_data fsl_imx93_devtype_data = { + .quirks = FLEXCAN_QUIRK_DISABLE_RXFG | FLEXCAN_QUIRK_ENABLE_EACEN_RRS | + FLEXCAN_QUIRK_DISABLE_MECR | FLEXCAN_QUIRK_USE_RX_MAILBOX | + FLEXCAN_QUIRK_BROKEN_PERR_STATE | FLEXCAN_QUIRK_AUTO_STOP_MODE | + FLEXCAN_QUIRK_SUPPORT_FD | FLEXCAN_QUIRK_SUPPORT_ECC | + FLEXCAN_QUIRK_SUPPORT_RX_MAILBOX | + FLEXCAN_QUIRK_SUPPORT_RX_MAILBOX_RTR, +}; + static const struct flexcan_devtype_data fsl_vf610_devtype_data = { .quirks = FLEXCAN_QUIRK_DISABLE_RXFG | FLEXCAN_QUIRK_ENABLE_EACEN_RRS | FLEXCAN_QUIRK_DISABLE_MECR | FLEXCAN_QUIRK_USE_RX_MAILBOX | @@ -532,9 +541,14 @@ static inline int flexcan_enter_stop_mode(struct flexcan_priv *priv) ret = flexcan_stop_mode_enable_scfw(priv, true); if (ret < 0) return ret; - } else { + } else if (priv->devtype_data.quirks & FLEXCAN_QUIRK_SETUP_STOP_MODE_GPR) { regmap_update_bits(priv->stm.gpr, priv->stm.req_gpr, 1 << priv->stm.req_bit, 1 << priv->stm.req_bit); + } else if (priv->devtype_data.quirks & FLEXCAN_QUIRK_AUTO_STOP_MODE) { + /* For the auto stop mode, software do nothing, hardware will cover + * all the operation automatically after system go into low power mode. + */ + return 0; } return flexcan_low_power_enter_ack(priv); @@ -551,7 +565,7 @@ static inline int flexcan_exit_stop_mode(struct flexcan_priv *priv) ret = flexcan_stop_mode_enable_scfw(priv, false); if (ret < 0) return ret; - } else { + } else if (priv->devtype_data.quirks & FLEXCAN_QUIRK_SETUP_STOP_MODE_GPR) { regmap_update_bits(priv->stm.gpr, priv->stm.req_gpr, 1 << priv->stm.req_bit, 0); } @@ -560,6 +574,12 @@ static inline int flexcan_exit_stop_mode(struct flexcan_priv *priv) reg_mcr &= ~FLEXCAN_MCR_SLF_WAK; priv->write(reg_mcr, ®s->mcr); + /* For the auto stop mode, hardware will exist stop mode + * automatically after system go out of low power mode. + */ + if (priv->devtype_data.quirks & FLEXCAN_QUIRK_AUTO_STOP_MODE) + return 0; + return flexcan_low_power_exit_ack(priv); } @@ -1974,6 +1994,8 @@ static int flexcan_setup_stop_mode(struct platform_device *pdev) ret = flexcan_setup_stop_mode_scfw(pdev); else if (priv->devtype_data.quirks & FLEXCAN_QUIRK_SETUP_STOP_MODE_GPR) ret = flexcan_setup_stop_mode_gpr(pdev); + else if (priv->devtype_data.quirks & FLEXCAN_QUIRK_AUTO_STOP_MODE) + ret = 0; else /* return 0 directly if doesn't support stop mode feature */ return 0; @@ -1992,6 +2014,7 @@ static int flexcan_setup_stop_mode(struct platform_device *pdev) static const struct of_device_id flexcan_of_match[] = { { .compatible = "fsl,imx8qm-flexcan", .data = &fsl_imx8qm_devtype_data, }, { .compatible = "fsl,imx8mp-flexcan", .data = &fsl_imx8mp_devtype_data, }, + { .compatible = "fsl,imx93-flexcan", .data = &fsl_imx93_devtype_data, }, { .compatible = "fsl,imx6q-flexcan", .data = &fsl_imx6q_devtype_data, }, { .compatible = "fsl,imx28-flexcan", .data = &fsl_imx28_devtype_data, }, { .compatible = "fsl,imx53-flexcan", .data = &fsl_imx25_devtype_data, }, @@ -2299,8 +2322,16 @@ static int __maybe_unused flexcan_noirq_suspend(struct device *device) if (netif_running(dev)) { int err; - if (device_may_wakeup(device)) + if (device_may_wakeup(device)) { flexcan_enable_wakeup_irq(priv, true); + /* For auto stop mode, need to keep the clock on before + * system go into low power mode. After system go into + * low power mode, hardware will config the flexcan into + * stop mode, and gate off the clock automatically. + */ + if (priv->devtype_data.quirks & FLEXCAN_QUIRK_AUTO_STOP_MODE) + return 0; + } err = pm_runtime_force_suspend(device); if (err) diff --git a/drivers/net/can/flexcan/flexcan.h b/drivers/net/can/flexcan/flexcan.h index 025c3417031f..91402977780b 100644 --- a/drivers/net/can/flexcan/flexcan.h +++ b/drivers/net/can/flexcan/flexcan.h @@ -68,6 +68,8 @@ #define FLEXCAN_QUIRK_SUPPORT_RX_MAILBOX_RTR BIT(15) /* Device supports RX via FIFO */ #define FLEXCAN_QUIRK_SUPPORT_RX_FIFO BIT(16) +/* auto enter stop mode to support wakeup */ +#define FLEXCAN_QUIRK_AUTO_STOP_MODE BIT(17) struct flexcan_devtype_data { u32 quirks; /* quirks needed for different IP cores */ From 2c4a1efcf6ab37abef8b593a4c0dda43557504c8 Mon Sep 17 00:00:00 2001 From: Vincent Mailhol Date: Thu, 1 Dec 2022 02:46:52 +0900 Subject: [PATCH 1874/2002] can: etas_es58x: add devlink support Add basic support for devlink at the device level. The callbacks of struct devlink_ops will be implemented next. Signed-off-by: Vincent Mailhol Link: https://lore.kernel.org/all/20221130174658.29282-2-mailhol.vincent@wanadoo.fr Signed-off-by: Marc Kleine-Budde --- drivers/net/can/usb/Kconfig | 1 + drivers/net/can/usb/etas_es58x/Makefile | 2 +- drivers/net/can/usb/etas_es58x/es58x_core.c | 13 ++++++++++--- drivers/net/can/usb/etas_es58x/es58x_core.h | 6 ++++++ drivers/net/can/usb/etas_es58x/es58x_devlink.c | 13 +++++++++++++ 5 files changed, 31 insertions(+), 4 deletions(-) create mode 100644 drivers/net/can/usb/etas_es58x/es58x_devlink.c diff --git a/drivers/net/can/usb/Kconfig b/drivers/net/can/usb/Kconfig index 8c6fea661530..445504ababce 100644 --- a/drivers/net/can/usb/Kconfig +++ b/drivers/net/can/usb/Kconfig @@ -30,6 +30,7 @@ config CAN_ESD_USB config CAN_ETAS_ES58X tristate "ETAS ES58X CAN/USB interfaces" select CRC16 + select NET_DEVLINK help This driver supports the ES581.4, ES582.1 and ES584.1 interfaces from ETAS GmbH (https://www.etas.com/en/products/es58x.php). diff --git a/drivers/net/can/usb/etas_es58x/Makefile b/drivers/net/can/usb/etas_es58x/Makefile index a129b4aa0215..d6667ebe259f 100644 --- a/drivers/net/can/usb/etas_es58x/Makefile +++ b/drivers/net/can/usb/etas_es58x/Makefile @@ -1,3 +1,3 @@ # SPDX-License-Identifier: GPL-2.0 obj-$(CONFIG_CAN_ETAS_ES58X) += etas_es58x.o -etas_es58x-y = es58x_core.o es581_4.o es58x_fd.o +etas_es58x-y = es58x_core.o es58x_devlink.o es581_4.o es58x_fd.o diff --git a/drivers/net/can/usb/etas_es58x/es58x_core.c b/drivers/net/can/usb/etas_es58x/es58x_core.c index 5aba16849603..aeffe61faed8 100644 --- a/drivers/net/can/usb/etas_es58x/es58x_core.c +++ b/drivers/net/can/usb/etas_es58x/es58x_core.c @@ -16,6 +16,7 @@ #include #include #include +#include #include "es58x_core.h" @@ -2177,6 +2178,7 @@ static struct es58x_device *es58x_init_es58x_dev(struct usb_interface *intf, { struct device *dev = &intf->dev; struct es58x_device *es58x_dev; + struct devlink *devlink; const struct es58x_parameters *param; const struct es58x_operators *ops; struct usb_device *udev = interface_to_usbdev(intf); @@ -2199,11 +2201,12 @@ static struct es58x_device *es58x_init_es58x_dev(struct usb_interface *intf, ops = &es581_4_ops; } - es58x_dev = devm_kzalloc(dev, es58x_sizeof_es58x_device(param), - GFP_KERNEL); - if (!es58x_dev) + devlink = devlink_alloc(&es58x_dl_ops, es58x_sizeof_es58x_device(param), + dev); + if (!devlink) return ERR_PTR(-ENOMEM); + es58x_dev = devlink_priv(devlink); es58x_dev->param = param; es58x_dev->ops = ops; es58x_dev->dev = dev; @@ -2250,6 +2253,8 @@ static int es58x_probe(struct usb_interface *intf, if (ret) return ret; + devlink_register(priv_to_devlink(es58x_dev)); + for (ch_idx = 0; ch_idx < es58x_dev->num_can_ch; ch_idx++) { ret = es58x_init_netdev(es58x_dev, ch_idx); if (ret) { @@ -2275,8 +2280,10 @@ static void es58x_disconnect(struct usb_interface *intf) dev_info(&intf->dev, "Disconnecting %s %s\n", es58x_dev->udev->manufacturer, es58x_dev->udev->product); + devlink_unregister(priv_to_devlink(es58x_dev)); es58x_free_netdevs(es58x_dev); es58x_free_urbs(es58x_dev); + devlink_free(priv_to_devlink(es58x_dev)); usb_set_intfdata(intf, NULL); } diff --git a/drivers/net/can/usb/etas_es58x/es58x_core.h b/drivers/net/can/usb/etas_es58x/es58x_core.h index 4a082fd69e6f..bf24375580e5 100644 --- a/drivers/net/can/usb/etas_es58x/es58x_core.h +++ b/drivers/net/can/usb/etas_es58x/es58x_core.h @@ -674,6 +674,7 @@ static inline enum es58x_flag es58x_get_flags(const struct sk_buff *skb) return es58x_flags; } +/* es58x_core.c. */ int es58x_can_get_echo_skb(struct net_device *netdev, u32 packet_idx, u64 *tstamps, unsigned int pkts); int es58x_tx_ack_msg(struct net_device *netdev, u16 tx_free_entries, @@ -691,9 +692,14 @@ int es58x_rx_cmd_ret_u32(struct net_device *netdev, int es58x_send_msg(struct es58x_device *es58x_dev, u8 cmd_type, u8 cmd_id, const void *msg, u16 cmd_len, int channel_idx); +/* es58x_devlink.c. */ +extern const struct devlink_ops es58x_dl_ops; + +/* es581_4.c. */ extern const struct es58x_parameters es581_4_param; extern const struct es58x_operators es581_4_ops; +/* es58x_fd.c. */ extern const struct es58x_parameters es58x_fd_param; extern const struct es58x_operators es58x_fd_ops; diff --git a/drivers/net/can/usb/etas_es58x/es58x_devlink.c b/drivers/net/can/usb/etas_es58x/es58x_devlink.c new file mode 100644 index 000000000000..af6ca7ada23f --- /dev/null +++ b/drivers/net/can/usb/etas_es58x/es58x_devlink.c @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: GPL-2.0 + +/* Driver for ETAS GmbH ES58X USB CAN(-FD) Bus Interfaces. + * + * File es58x_devlink.c: report the product information using devlink. + * + * Copyright (c) 2022 Vincent Mailhol + */ + +#include + +const struct devlink_ops es58x_dl_ops = { +}; From a21cee59b41695957f2eeb3943e345e8a68732c4 Mon Sep 17 00:00:00 2001 From: Haibo Chen Date: Tue, 22 Nov 2022 19:32:31 +0800 Subject: [PATCH 1875/2002] dt-bindings: can: fsl,flexcan: add imx93 compatible Add a new compatible string for imx93. Signed-off-by: Haibo Chen Acked-by: Krzysztof Kozlowski Link: https://lore.kernel.org/all/1669116752-4260-2-git-send-email-haibo.chen@nxp.com Signed-off-by: Marc Kleine-Budde --- Documentation/devicetree/bindings/net/can/fsl,flexcan.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/net/can/fsl,flexcan.yaml b/Documentation/devicetree/bindings/net/can/fsl,flexcan.yaml index e52db841bb8c..6e59bd2a6094 100644 --- a/Documentation/devicetree/bindings/net/can/fsl,flexcan.yaml +++ b/Documentation/devicetree/bindings/net/can/fsl,flexcan.yaml @@ -17,6 +17,7 @@ properties: compatible: oneOf: - enum: + - fsl,imx93-flexcan - fsl,imx8qm-flexcan - fsl,imx8mp-flexcan - fsl,imx6q-flexcan From 594a25e1ffc59fd1ef9a6760a1eb2ef6bbd4cfc1 Mon Sep 17 00:00:00 2001 From: Vincent Mailhol Date: Thu, 1 Dec 2022 02:46:53 +0900 Subject: [PATCH 1876/2002] can: etas_es58x: add devlink port support Add support for devlink port which extends the devlink support to the network interface level. For now, the etas_es58x driver will only rely on the default features that devlink port has to offer and not implement additional feature ones. Signed-off-by: Vincent Mailhol Link: https://lore.kernel.org/all/20221130174658.29282-3-mailhol.vincent@wanadoo.fr Signed-off-by: Marc Kleine-Budde --- drivers/net/can/usb/etas_es58x/es58x_core.c | 34 ++++++++++++++++----- drivers/net/can/usb/etas_es58x/es58x_core.h | 3 ++ 2 files changed, 29 insertions(+), 8 deletions(-) diff --git a/drivers/net/can/usb/etas_es58x/es58x_core.c b/drivers/net/can/usb/etas_es58x/es58x_core.c index aeffe61faed8..de884de9fe57 100644 --- a/drivers/net/can/usb/etas_es58x/es58x_core.c +++ b/drivers/net/can/usb/etas_es58x/es58x_core.c @@ -2039,10 +2039,16 @@ static int es58x_set_mode(struct net_device *netdev, enum can_mode mode) * @es58x_dev: ES58X device. * @priv: ES58X private parameters related to the network device. * @channel_idx: Index of the network device. + * + * Return: zero on success, errno if devlink port could not be + * properly registered. */ -static void es58x_init_priv(struct es58x_device *es58x_dev, - struct es58x_priv *priv, int channel_idx) +static int es58x_init_priv(struct es58x_device *es58x_dev, + struct es58x_priv *priv, int channel_idx) { + struct devlink_port_attrs attrs = { + .flavour = DEVLINK_PORT_FLAVOUR_PHYSICAL, + }; const struct es58x_parameters *param = es58x_dev->param; struct can_priv *can = &priv->can; @@ -2061,6 +2067,10 @@ static void es58x_init_priv(struct es58x_device *es58x_dev, can->state = CAN_STATE_STOPPED; can->ctrlmode_supported = param->ctrlmode_supported; can->do_set_mode = es58x_set_mode; + + devlink_port_attrs_set(&priv->devlink_port, &attrs); + return devlink_port_register(priv_to_devlink(es58x_dev), + &priv->devlink_port, channel_idx); } /** @@ -2084,7 +2094,10 @@ static int es58x_init_netdev(struct es58x_device *es58x_dev, int channel_idx) } SET_NETDEV_DEV(netdev, dev); es58x_dev->netdev[channel_idx] = netdev; - es58x_init_priv(es58x_dev, es58x_priv(netdev), channel_idx); + ret = es58x_init_priv(es58x_dev, es58x_priv(netdev), channel_idx); + if (ret) + goto free_candev; + SET_NETDEV_DEVLINK_PORT(netdev, &es58x_priv(netdev)->devlink_port); netdev->netdev_ops = &es58x_netdev_ops; netdev->ethtool_ops = &es58x_ethtool_ops; @@ -2092,16 +2105,20 @@ static int es58x_init_netdev(struct es58x_device *es58x_dev, int channel_idx) netdev->dev_port = channel_idx; ret = register_candev(netdev); - if (ret) { - es58x_dev->netdev[channel_idx] = NULL; - free_candev(netdev); - return ret; - } + if (ret) + goto devlink_port_unregister; netdev_queue_set_dql_min_limit(netdev_get_tx_queue(netdev, 0), es58x_dev->param->dql_min_limit); return ret; + + devlink_port_unregister: + devlink_port_unregister(&es58x_priv(netdev)->devlink_port); + free_candev: + es58x_dev->netdev[channel_idx] = NULL; + free_candev(netdev); + return ret; } /** @@ -2118,6 +2135,7 @@ static void es58x_free_netdevs(struct es58x_device *es58x_dev) if (!netdev) continue; unregister_candev(netdev); + devlink_port_unregister(&es58x_priv(netdev)->devlink_port); es58x_dev->netdev[i] = NULL; free_candev(netdev); } diff --git a/drivers/net/can/usb/etas_es58x/es58x_core.h b/drivers/net/can/usb/etas_es58x/es58x_core.h index bf24375580e5..a76789119229 100644 --- a/drivers/net/can/usb/etas_es58x/es58x_core.h +++ b/drivers/net/can/usb/etas_es58x/es58x_core.h @@ -17,6 +17,7 @@ #include #include #include +#include #include "es581_4.h" #include "es58x_fd.h" @@ -230,6 +231,7 @@ union es58x_urb_cmd { * @can: struct can_priv must be the first member (Socket CAN relies * on the fact that function netdev_priv() returns a pointer to * a struct can_priv). + * @devlink_port: devlink instance for the network interface. * @es58x_dev: pointer to the corresponding ES58X device. * @tx_urb: Used as a buffer to concatenate the TX messages and to do * a bulk send. Please refer to es58x_start_xmit() for more @@ -255,6 +257,7 @@ union es58x_urb_cmd { */ struct es58x_priv { struct can_priv can; + struct devlink_port devlink_port; struct es58x_device *es58x_dev; struct urb *tx_urb; From 5237ff4e7d3d60e19c0625169361eb40cd435f6d Mon Sep 17 00:00:00 2001 From: Lad Prabhakar Date: Tue, 15 Nov 2022 12:38:11 +0000 Subject: [PATCH 1877/2002] dt-bindings: can: renesas,rcar-canfd: Document RZ/Five SoC The CANFD block on the RZ/Five SoC is identical to one found on the RZ/G2UL SoC. "renesas,r9a07g043-canfd" compatible string will be used on the RZ/Five SoC so to make this clear, update the comment to include RZ/Five SoC. No driver changes are required as generic compatible string "renesas,rzg2l-canfd" will be used as a fallback on RZ/Five SoC. Signed-off-by: Lad Prabhakar Acked-by: Rob Herring Reviewed-by: Geert Uytterhoeven Link: https://lore.kernel.org/all/20221115123811.1182922-1-prabhakar.mahadev-lad.rj@bp.renesas.com Signed-off-by: Marc Kleine-Budde --- .../devicetree/bindings/net/can/renesas,rcar-canfd.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/net/can/renesas,rcar-canfd.yaml b/Documentation/devicetree/bindings/net/can/renesas,rcar-canfd.yaml index 6f71fc96bc4e..8347053a96dc 100644 --- a/Documentation/devicetree/bindings/net/can/renesas,rcar-canfd.yaml +++ b/Documentation/devicetree/bindings/net/can/renesas,rcar-canfd.yaml @@ -33,7 +33,7 @@ properties: - items: - enum: - - renesas,r9a07g043-canfd # RZ/G2UL + - renesas,r9a07g043-canfd # RZ/G2UL and RZ/Five - renesas,r9a07g044-canfd # RZ/G2{L,LC} - renesas,r9a07g054-canfd # RZ/V2L - const: renesas,rzg2l-canfd # RZ/G2L family From 983055bf839745ec2812fc55cacd96888aa0d7a6 Mon Sep 17 00:00:00 2001 From: Vincent Mailhol Date: Thu, 1 Dec 2022 02:46:54 +0900 Subject: [PATCH 1878/2002] USB: core: export usb_cache_string() usb_cache_string() can also be useful for the drivers so export it. Signed-off-by: Vincent Mailhol Acked-by: Greg Kroah-Hartman Link: https://lore.kernel.org/all/20221130174658.29282-4-mailhol.vincent@wanadoo.fr Signed-off-by: Marc Kleine-Budde --- drivers/usb/core/message.c | 1 + drivers/usb/core/usb.h | 1 - include/linux/usb.h | 1 + 3 files changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c index 4d59d927ae3e..127fac1af676 100644 --- a/drivers/usb/core/message.c +++ b/drivers/usb/core/message.c @@ -1037,6 +1037,7 @@ char *usb_cache_string(struct usb_device *udev, int index) } return smallbuf; } +EXPORT_SYMBOL_GPL(usb_cache_string); /* * usb_get_device_descriptor - (re)reads the device descriptor (usbcore) diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h index 82538daac8b8..0eac7d4285d1 100644 --- a/drivers/usb/core/usb.h +++ b/drivers/usb/core/usb.h @@ -47,7 +47,6 @@ extern int usb_get_device_descriptor(struct usb_device *dev, extern int usb_set_isoch_delay(struct usb_device *dev); extern int usb_get_bos_descriptor(struct usb_device *dev); extern void usb_release_bos_descriptor(struct usb_device *dev); -extern char *usb_cache_string(struct usb_device *udev, int index); extern int usb_set_configuration(struct usb_device *dev, int configuration); extern int usb_choose_configuration(struct usb_device *udev); extern int usb_generic_driver_probe(struct usb_device *udev); diff --git a/include/linux/usb.h b/include/linux/usb.h index 9ff1ad4dfad1..d2d2f41052c0 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -1829,6 +1829,7 @@ static inline int usb_get_ptm_status(struct usb_device *dev, void *data) extern int usb_string(struct usb_device *dev, int index, char *buf, size_t size); +extern char *usb_cache_string(struct usb_device *udev, int index); /* wrappers that also update important state inside usbcore */ extern int usb_clear_halt(struct usb_device *dev, int pipe); From 74d95352bdfc4ed1eb72ef9d69e06ed2d6891ff7 Mon Sep 17 00:00:00 2001 From: Minghao Chi Date: Fri, 11 Nov 2022 14:43:00 +0800 Subject: [PATCH 1879/2002] can: c_can: use devm_platform_get_and_ioremap_resource() Convert platform_get_resource(), devm_ioremap_resource() to a single call to devm_platform_get_and_ioremap_resource(), as this is exactly what this function does. Reported-by: Zeal Robot Signed-off-by: Minghao Chi Link: https://lore.kernel.org/all/202211111443005202576@zte.com.cn Signed-off-by: Marc Kleine-Budde --- drivers/net/can/c_can/c_can_platform.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/net/can/c_can/c_can_platform.c b/drivers/net/can/c_can/c_can_platform.c index 86e95e9d6533..03ccb7cfacaf 100644 --- a/drivers/net/can/c_can/c_can_platform.c +++ b/drivers/net/can/c_can/c_can_platform.c @@ -290,8 +290,7 @@ static int c_can_plat_probe(struct platform_device *pdev) goto exit; } - mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); - addr = devm_ioremap_resource(&pdev->dev, mem); + addr = devm_platform_get_and_ioremap_resource(pdev, 0, &mem); if (IS_ERR(addr)) { ret = PTR_ERR(addr); goto exit; From 01d80532295cd359dc43e6bd71860d5515f84372 Mon Sep 17 00:00:00 2001 From: Vincent Mailhol Date: Thu, 1 Dec 2022 02:46:55 +0900 Subject: [PATCH 1880/2002] net: devlink: add DEVLINK_INFO_VERSION_GENERIC_FW_BOOTLOADER As discussed in [1], abbreviating the bootloader to "bl" might not be well understood. Instead, a bootloader technically being a firmware, name it "fw.bootloader". Add a new macro to devlink.h to formalize this new info attribute name and update the documentation. [1] https://lore.kernel.org/netdev/20221128142723.2f826d20@kernel.org/ Suggested-by: Jakub Kicinski Signed-off-by: Vincent Mailhol Link: https://lore.kernel.org/all/20221130174658.29282-5-mailhol.vincent@wanadoo.fr Signed-off-by: Marc Kleine-Budde --- Documentation/networking/devlink/devlink-info.rst | 5 +++++ include/net/devlink.h | 2 ++ 2 files changed, 7 insertions(+) diff --git a/Documentation/networking/devlink/devlink-info.rst b/Documentation/networking/devlink/devlink-info.rst index 7572bf6de5c1..1242b0e6826b 100644 --- a/Documentation/networking/devlink/devlink-info.rst +++ b/Documentation/networking/devlink/devlink-info.rst @@ -198,6 +198,11 @@ fw.bundle_id Unique identifier of the entire firmware bundle. +fw.bootloader +------------- + +Version of the bootloader. + Future work =========== diff --git a/include/net/devlink.h b/include/net/devlink.h index 0f376a28b9c4..6a2e4f21779f 100644 --- a/include/net/devlink.h +++ b/include/net/devlink.h @@ -621,6 +621,8 @@ enum devlink_param_generic_id { #define DEVLINK_INFO_VERSION_GENERIC_FW_ROCE "fw.roce" /* Firmware bundle identifier */ #define DEVLINK_INFO_VERSION_GENERIC_FW_BUNDLE_ID "fw.bundle_id" +/* Bootloader */ +#define DEVLINK_INFO_VERSION_GENERIC_FW_BOOTLOADER "fw.bootloader" /** * struct devlink_flash_update_params - Flash Update parameters From 9f06631c3f1f0f298536443df85a6837ba4c5f5c Mon Sep 17 00:00:00 2001 From: Vincent Mailhol Date: Thu, 1 Dec 2022 02:46:56 +0900 Subject: [PATCH 1881/2002] can: etas_es58x: export product information through devlink_ops::info_get() ES58x devices report below product information through a custom usb string: * the firmware version * the bootloader version * the hardware revision Parse this string, store the results in struct es58x_dev, export: * the firmware version through devlink's "fw" name * the bootloader version through devlink's "fw.bootloader" name * the hardware revisionthrough devlink's "board.rev" name Those devlink entries are not critical to use the device, if parsing fails, print an informative log message and continue to probe the device. In addition to that, use usb_device::serial to report the device serial number. Signed-off-by: Vincent Mailhol Reviewed-by: Andrew Lunn Link: https://lore.kernel.org/all/20221130174658.29282-6-mailhol.vincent@wanadoo.fr Signed-off-by: Marc Kleine-Budde --- drivers/net/can/usb/etas_es58x/es58x_core.c | 1 + drivers/net/can/usb/etas_es58x/es58x_core.h | 41 ++++ .../net/can/usb/etas_es58x/es58x_devlink.c | 222 ++++++++++++++++++ 3 files changed, 264 insertions(+) diff --git a/drivers/net/can/usb/etas_es58x/es58x_core.c b/drivers/net/can/usb/etas_es58x/es58x_core.c index de884de9fe57..4d6d5a4ac06e 100644 --- a/drivers/net/can/usb/etas_es58x/es58x_core.c +++ b/drivers/net/can/usb/etas_es58x/es58x_core.c @@ -2271,6 +2271,7 @@ static int es58x_probe(struct usb_interface *intf, if (ret) return ret; + es58x_parse_product_info(es58x_dev); devlink_register(priv_to_devlink(es58x_dev)); for (ch_idx = 0; ch_idx < es58x_dev->num_can_ch; ch_idx++) { diff --git a/drivers/net/can/usb/etas_es58x/es58x_core.h b/drivers/net/can/usb/etas_es58x/es58x_core.h index a76789119229..c1ba1a4e8857 100644 --- a/drivers/net/can/usb/etas_es58x/es58x_core.h +++ b/drivers/net/can/usb/etas_es58x/es58x_core.h @@ -359,6 +359,39 @@ struct es58x_operators { int (*get_timestamp)(struct es58x_device *es58x_dev); }; +/** + * struct es58x_sw_version - Version number of the firmware or the + * bootloader. + * @major: Version major number, represented on two digits. + * @minor: Version minor number, represented on two digits. + * @revision: Version revision number, represented on two digits. + * + * The firmware and the bootloader share the same format: "xx.xx.xx" + * where 'x' is a digit. Both can be retrieved from the product + * information string. + */ +struct es58x_sw_version { + u8 major; + u8 minor; + u8 revision; +}; + +/** + * struct es58x_hw_revision - Hardware revision number. + * @letter: Revision letter. + * @major: Version major number, represented on three digits. + * @minor: Version minor number, represented on three digits. + * + * The hardware revision uses its own format: "axxx/xxx" where 'a' is + * a letter and 'x' a digit. It can be retrieved from the product + * information string. + */ +struct es58x_hw_revision { + char letter; + u16 major; + u16 minor; +}; + /** * struct es58x_device - All information specific to an ES58X device. * @dev: Device information. @@ -376,6 +409,9 @@ struct es58x_operators { * queue wake/stop logic should prevent this URB from getting * empty. Please refer to es58x_get_tx_urb() for more details. * @tx_urbs_idle_cnt: number of urbs in @tx_urbs_idle. + * @firmware_version: The firmware version number. + * @bootloader_version: The bootloader version number. + * @hardware_revision: The hardware revision number. * @ktime_req_ns: kernel timestamp when es58x_set_realtime_diff_ns() * was called. * @realtime_diff_ns: difference in nanoseconds between the clocks of @@ -411,6 +447,10 @@ struct es58x_device { struct usb_anchor tx_urbs_idle; atomic_t tx_urbs_idle_cnt; + struct es58x_sw_version firmware_version; + struct es58x_sw_version bootloader_version; + struct es58x_hw_revision hardware_revision; + u64 ktime_req_ns; s64 realtime_diff_ns; @@ -696,6 +736,7 @@ int es58x_send_msg(struct es58x_device *es58x_dev, u8 cmd_type, u8 cmd_id, const void *msg, u16 cmd_len, int channel_idx); /* es58x_devlink.c. */ +void es58x_parse_product_info(struct es58x_device *es58x_dev); extern const struct devlink_ops es58x_dl_ops; /* es581_4.c. */ diff --git a/drivers/net/can/usb/etas_es58x/es58x_devlink.c b/drivers/net/can/usb/etas_es58x/es58x_devlink.c index af6ca7ada23f..9fba29e2f57c 100644 --- a/drivers/net/can/usb/etas_es58x/es58x_devlink.c +++ b/drivers/net/can/usb/etas_es58x/es58x_devlink.c @@ -7,7 +7,229 @@ * Copyright (c) 2022 Vincent Mailhol */ +#include +#include +#include #include +#include "es58x_core.h" + +/* USB descriptor index containing the product information string. */ +#define ES58X_PROD_INFO_IDX 6 + +/** + * es58x_parse_sw_version() - Extract boot loader or firmware version. + * @es58x_dev: ES58X device. + * @prod_info: USB custom string returned by the device. + * @prefix: Select which information should be parsed. Set it to "FW" + * to parse the firmware version or to "BL" to parse the + * bootloader version. + * + * The @prod_info string contains the firmware and the bootloader + * version number all prefixed by a magic string and concatenated with + * other numbers. Depending on the device, the firmware (bootloader) + * format is either "FW_Vxx.xx.xx" ("BL_Vxx.xx.xx") or "FW:xx.xx.xx" + * ("BL:xx.xx.xx") where 'x' represents a digit. @prod_info must + * contains the common part of those prefixes: "FW" or "BL". + * + * Parse @prod_info and store the version number in + * &es58x_dev.firmware_version or &es58x_dev.bootloader_version + * according to @prefix value. + * + * Return: zero on success, -EINVAL if @prefix contains an invalid + * value and -EBADMSG if @prod_info could not be parsed. + */ +static int es58x_parse_sw_version(struct es58x_device *es58x_dev, + const char *prod_info, const char *prefix) +{ + struct es58x_sw_version *version; + int major, minor, revision; + + if (!strcmp(prefix, "FW")) + version = &es58x_dev->firmware_version; + else if (!strcmp(prefix, "BL")) + version = &es58x_dev->bootloader_version; + else + return -EINVAL; + + /* Go to prefix */ + prod_info = strstr(prod_info, prefix); + if (!prod_info) + return -EBADMSG; + /* Go to beginning of the version number */ + while (!isdigit(*prod_info)) { + prod_info++; + if (!*prod_info) + return -EBADMSG; + } + + if (sscanf(prod_info, "%2u.%2u.%2u", &major, &minor, &revision) != 3) + return -EBADMSG; + + version->major = major; + version->minor = minor; + version->revision = revision; + + return 0; +} + +/** + * es58x_parse_hw_rev() - Extract hardware revision number. + * @es58x_dev: ES58X device. + * @prod_info: USB custom string returned by the device. + * + * @prod_info contains the hardware revision prefixed by a magic + * string and conquenated together with other numbers. Depending on + * the device, the hardware revision format is either + * "HW_VER:axxx/xxx" or "HR:axxx/xxx" where 'a' represents a letter + * and 'x' a digit. + * + * Parse @prod_info and store the hardware revision number in + * &es58x_dev.hardware_revision. + * + * Return: zero on success, -EBADMSG if @prod_info could not be + * parsed. + */ +static int es58x_parse_hw_rev(struct es58x_device *es58x_dev, + const char *prod_info) +{ + char letter; + int major, minor; + + /* The only occurrence of 'H' is in the hardware revision prefix. */ + prod_info = strchr(prod_info, 'H'); + if (!prod_info) + return -EBADMSG; + /* Go to beginning of the hardware revision */ + prod_info = strchr(prod_info, ':'); + if (!prod_info) + return -EBADMSG; + prod_info++; + + if (sscanf(prod_info, "%c%3u/%3u", &letter, &major, &minor) != 3) + return -EBADMSG; + + es58x_dev->hardware_revision.letter = letter; + es58x_dev->hardware_revision.major = major; + es58x_dev->hardware_revision.minor = minor; + + return 0; +} + +/** + * es58x_parse_product_info() - Parse the ES58x product information + * string. + * @es58x_dev: ES58X device. + * + * Retrieve the product information string and parse it to extract the + * firmware version, the bootloader version and the hardware + * revision. + * + * If the function fails, simply emit a log message and continue + * because product information is not critical for the driver to + * operate. + */ +void es58x_parse_product_info(struct es58x_device *es58x_dev) +{ + char *prod_info; + + prod_info = usb_cache_string(es58x_dev->udev, ES58X_PROD_INFO_IDX); + if (!prod_info) { + dev_warn(es58x_dev->dev, + "could not retrieve the product info string\n"); + return; + } + + if (es58x_parse_sw_version(es58x_dev, prod_info, "FW") || + es58x_parse_sw_version(es58x_dev, prod_info, "BL") || + es58x_parse_hw_rev(es58x_dev, prod_info)) + dev_info(es58x_dev->dev, + "could not parse product info: '%s'\n", prod_info); + + kfree(prod_info); +} + +/** + * es58x_sw_version_is_set() - Check if the version is a valid number. + * @sw_ver: Version number of either the firmware or the bootloader. + * + * If &es58x_sw_version.major, &es58x_sw_version.minor and + * &es58x_sw_version.revision are all zero, the product string could + * not be parsed and the version number is invalid. + */ +static inline bool es58x_sw_version_is_set(struct es58x_sw_version *sw_ver) +{ + return sw_ver->major || sw_ver->minor || sw_ver->revision; +} + +/** + * es58x_hw_revision_is_set() - Check if the revision is a valid number. + * @hw_rev: Revision number of the hardware. + * + * If &es58x_hw_revision.letter is the null character, the product + * string could not be parsed and the hardware revision number is + * invalid. + */ +static inline bool es58x_hw_revision_is_set(struct es58x_hw_revision *hw_rev) +{ + return hw_rev->letter != '\0'; +} + +/** + * es58x_devlink_info_get() - Report the product information. + * @devlink: Devlink. + * @req: skb wrapper where to put requested information. + * @extack: Unused. + * + * Report the firmware version, the bootloader version, the hardware + * revision and the serial number through netlink. + * + * Return: zero on success, errno when any error occurs. + */ +static int es58x_devlink_info_get(struct devlink *devlink, + struct devlink_info_req *req, + struct netlink_ext_ack *extack) +{ + struct es58x_device *es58x_dev = devlink_priv(devlink); + struct es58x_sw_version *fw_ver = &es58x_dev->firmware_version; + struct es58x_sw_version *bl_ver = &es58x_dev->bootloader_version; + struct es58x_hw_revision *hw_rev = &es58x_dev->hardware_revision; + char buf[max(sizeof("xx.xx.xx"), sizeof("axxx/xxx"))]; + int ret = 0; + + if (es58x_sw_version_is_set(fw_ver)) { + snprintf(buf, sizeof(buf), "%02u.%02u.%02u", + fw_ver->major, fw_ver->minor, fw_ver->revision); + ret = devlink_info_version_running_put(req, + DEVLINK_INFO_VERSION_GENERIC_FW, + buf); + if (ret) + return ret; + } + + if (es58x_sw_version_is_set(bl_ver)) { + snprintf(buf, sizeof(buf), "%02u.%02u.%02u", + bl_ver->major, bl_ver->minor, bl_ver->revision); + ret = devlink_info_version_running_put(req, + DEVLINK_INFO_VERSION_GENERIC_FW_BOOTLOADER, + buf); + if (ret) + return ret; + } + + if (es58x_hw_revision_is_set(hw_rev)) { + snprintf(buf, sizeof(buf), "%c%03u/%03u", + hw_rev->letter, hw_rev->major, hw_rev->minor); + ret = devlink_info_version_fixed_put(req, + DEVLINK_INFO_VERSION_GENERIC_BOARD_REV, + buf); + if (ret) + return ret; + } + + return devlink_info_serial_number_put(req, es58x_dev->udev->serial); +} + const struct devlink_ops es58x_dl_ops = { + .info_get = es58x_devlink_info_get, }; From d8f26fd689dd81da8f4e72f17d1554c4c5b018c2 Mon Sep 17 00:00:00 2001 From: Vincent Mailhol Date: Thu, 1 Dec 2022 02:46:57 +0900 Subject: [PATCH 1882/2002] can: etas_es58x: remove es58x_get_product_info() Now that the product information are available under devlink, no more need to print them in the kernel log. Remove es58x_get_product_info(). Signed-off-by: Vincent Mailhol Reviewed-by: Andrew Lunn Link: https://lore.kernel.org/all/20221130174658.29282-7-mailhol.vincent@wanadoo.fr Signed-off-by: Marc Kleine-Budde --- drivers/net/can/usb/etas_es58x/es58x_core.c | 52 ++------------------- 1 file changed, 3 insertions(+), 49 deletions(-) diff --git a/drivers/net/can/usb/etas_es58x/es58x_core.c b/drivers/net/can/usb/etas_es58x/es58x_core.c index 4d6d5a4ac06e..0c7f7505632c 100644 --- a/drivers/net/can/usb/etas_es58x/es58x_core.c +++ b/drivers/net/can/usb/etas_es58x/es58x_core.c @@ -2141,48 +2141,6 @@ static void es58x_free_netdevs(struct es58x_device *es58x_dev) } } -/** - * es58x_get_product_info() - Get the product information and print them. - * @es58x_dev: ES58X device. - * - * Do a synchronous call to get the product information. - * - * Return: zero on success, errno when any error occurs. - */ -static int es58x_get_product_info(struct es58x_device *es58x_dev) -{ - struct usb_device *udev = es58x_dev->udev; - const int es58x_prod_info_idx = 6; - /* Empirical tests show a prod_info length of maximum 83, - * below should be more than enough. - */ - const size_t prod_info_len = 127; - char *prod_info; - int ret; - - prod_info = kmalloc(prod_info_len, GFP_KERNEL); - if (!prod_info) - return -ENOMEM; - - ret = usb_string(udev, es58x_prod_info_idx, prod_info, prod_info_len); - if (ret < 0) { - dev_err(es58x_dev->dev, - "%s: Could not read the product info: %pe\n", - __func__, ERR_PTR(ret)); - goto out_free; - } - if (ret >= prod_info_len - 1) { - dev_warn(es58x_dev->dev, - "%s: Buffer is too small, result might be truncated\n", - __func__); - } - dev_info(es58x_dev->dev, "Product info: %s\n", prod_info); - - out_free: - kfree(prod_info); - return ret < 0 ? ret : 0; -} - /** * es58x_init_es58x_dev() - Initialize the ES58X device. * @intf: USB interface. @@ -2261,28 +2219,24 @@ static int es58x_probe(struct usb_interface *intf, const struct usb_device_id *id) { struct es58x_device *es58x_dev; - int ch_idx, ret; + int ch_idx; es58x_dev = es58x_init_es58x_dev(intf, id->driver_info); if (IS_ERR(es58x_dev)) return PTR_ERR(es58x_dev); - ret = es58x_get_product_info(es58x_dev); - if (ret) - return ret; - es58x_parse_product_info(es58x_dev); devlink_register(priv_to_devlink(es58x_dev)); for (ch_idx = 0; ch_idx < es58x_dev->num_can_ch; ch_idx++) { - ret = es58x_init_netdev(es58x_dev, ch_idx); + int ret = es58x_init_netdev(es58x_dev, ch_idx); if (ret) { es58x_free_netdevs(es58x_dev); return ret; } } - return ret; + return 0; } /** From 9f63f96aac92751e85f8313f59f2c867b88ea453 Mon Sep 17 00:00:00 2001 From: Vincent Mailhol Date: Thu, 1 Dec 2022 02:46:58 +0900 Subject: [PATCH 1883/2002] Documentation: devlink: add devlink documentation for the etas_es58x driver List all the version information reported by the etas_es58x driver through devlink. Also, update MAINTAINERS with the newly created file. Signed-off-by: Vincent Mailhol Link: https://lore.kernel.org/all/20221130174658.29282-8-mailhol.vincent@wanadoo.fr [mkl: fixed version information table: "bl" -> "fw.bootloader" Signed-off-by: Marc Kleine-Budde --- .../networking/devlink/etas_es58x.rst | 36 +++++++++++++++++++ MAINTAINERS | 1 + 2 files changed, 37 insertions(+) create mode 100644 Documentation/networking/devlink/etas_es58x.rst diff --git a/Documentation/networking/devlink/etas_es58x.rst b/Documentation/networking/devlink/etas_es58x.rst new file mode 100644 index 000000000000..3b857d82a44c --- /dev/null +++ b/Documentation/networking/devlink/etas_es58x.rst @@ -0,0 +1,36 @@ +.. SPDX-License-Identifier: GPL-2.0 + +========================== +etas_es58x devlink support +========================== + +This document describes the devlink features implemented by the +``etas_es58x`` device driver. + +Info versions +============= + +The ``etas_es58x`` driver reports the following versions + +.. list-table:: devlink info versions implemented + :widths: 5 5 90 + + * - Name + - Type + - Description + * - ``fw`` + - running + - Version of the firmware running on the device. Also available + through ``ethtool -i`` as the first member of the + ``firmware-version``. + * - ``fw.bootloader`` + - running + - Version of the bootloader running on the device. Also available + through ``ethtool -i`` as the second member of the + ``firmware-version``. + * - ``board.rev`` + - fixed + - The hardware revision of the device. + * - ``serial_number`` + - fixed + - The USB serial number. Also available through ``lsusb -v``. diff --git a/MAINTAINERS b/MAINTAINERS index 955c1be1efb2..71f4f8776779 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -7682,6 +7682,7 @@ ETAS ES58X CAN/USB DRIVER M: Vincent Mailhol L: linux-can@vger.kernel.org S: Maintained +F: Documentation/networking/devlink/etas_es58x.rst F: drivers/net/can/usb/etas_es58x/ ETHERNET BRIDGE From 7fdaf8966aae476deafe11f9a0067ff588615444 Mon Sep 17 00:00:00 2001 From: Xu Panda Date: Wed, 7 Dec 2022 09:09:09 +0800 Subject: [PATCH 1884/2002] can: ucan: use strscpy() to instead of strncpy() The implementation of strscpy() is more robust and safer. That's now the recommended way to copy NUL terminated strings. Signed-off-by: Xu Panda Signed-off-by: Yang Yang Link: https://lore.kernel.org/all/202212070909095189693@zte.com.cn Signed-off-by: Marc Kleine-Budde --- drivers/net/can/usb/ucan.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/net/can/usb/ucan.c b/drivers/net/can/usb/ucan.c index ffa38f533c35..159e25ffa337 100644 --- a/drivers/net/can/usb/ucan.c +++ b/drivers/net/can/usb/ucan.c @@ -1534,9 +1534,8 @@ static int ucan_probe(struct usb_interface *intf, sizeof(union ucan_ctl_payload)); if (ret > 0) { /* copy string while ensuring zero termination */ - strncpy(firmware_str, up->ctl_msg_buffer->raw, - sizeof(union ucan_ctl_payload)); - firmware_str[sizeof(union ucan_ctl_payload)] = '\0'; + strscpy(firmware_str, up->ctl_msg_buffer->raw, + sizeof(union ucan_ctl_payload) + 1); } else { strcpy(firmware_str, "unknown"); } From f793458bba544e29657efbfa53b62dc511c6f22c Mon Sep 17 00:00:00 2001 From: Ye Bin Date: Thu, 8 Dec 2022 17:09:40 +0800 Subject: [PATCH 1885/2002] net: af_can: remove useless parameter 'err' in 'can_rx_register()' Since commit bdfb5765e45b remove NULL-ptr checks from users of can_dev_rcv_lists_find(). 'err' parameter is useless, so remove it. Signed-off-by: Ye Bin Link: https://lore.kernel.org/all/20221208090940.3695670-1-yebin@huaweicloud.com Signed-off-by: Marc Kleine-Budde --- net/can/af_can.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/net/can/af_can.c b/net/can/af_can.c index c69168f11e44..7343fd487dbe 100644 --- a/net/can/af_can.c +++ b/net/can/af_can.c @@ -446,7 +446,6 @@ int can_rx_register(struct net *net, struct net_device *dev, canid_t can_id, struct hlist_head *rcv_list; struct can_dev_rcv_lists *dev_rcv_lists; struct can_rcv_lists_stats *rcv_lists_stats = net->can.rcv_lists_stats; - int err = 0; /* insert new receiver (dev,canid,mask) -> (func,data) */ @@ -481,7 +480,7 @@ int can_rx_register(struct net *net, struct net_device *dev, canid_t can_id, rcv_lists_stats->rcv_entries); spin_unlock_bh(&net->can.rcvlists_lock); - return err; + return 0; } EXPORT_SYMBOL(can_rx_register); From f54b101ddeaea68fe7e52e842deb8b7a081bf156 Mon Sep 17 00:00:00 2001 From: Vincent Mailhol Date: Thu, 8 Dec 2022 17:11:41 +0900 Subject: [PATCH 1886/2002] can: ucan: remove unused ucan_priv::intf Field intf of struct ucan_priv is set but never used. Remove it. Signed-off-by: Vincent Mailhol Link: https://lore.kernel.org/all/20221208081142.16936-2-mailhol.vincent@wanadoo.fr Signed-off-by: Marc Kleine-Budde --- drivers/net/can/usb/ucan.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/net/can/usb/ucan.c b/drivers/net/can/usb/ucan.c index 159e25ffa337..a0f7bcec719c 100644 --- a/drivers/net/can/usb/ucan.c +++ b/drivers/net/can/usb/ucan.c @@ -277,7 +277,6 @@ struct ucan_priv { /* linux USB device structures */ struct usb_device *udev; - struct usb_interface *intf; struct net_device *netdev; /* lock for can->echo_skb (used around @@ -1501,7 +1500,6 @@ static int ucan_probe(struct usb_interface *intf, /* initialize data */ up->udev = udev; - up->intf = intf; up->netdev = netdev; up->intf_index = iface_desc->desc.bInterfaceNumber; up->in_ep_addr = in_ep_addr; From 56c56a309e7959c319d3cbd6bd16e598ecd5831c Mon Sep 17 00:00:00 2001 From: Vincent Mailhol Date: Thu, 8 Dec 2022 17:11:42 +0900 Subject: [PATCH 1887/2002] can: gs_usb: remove gs_can::iface The iface field of struct gs_can is only used to retrieve the usb_device which is already available in gs_can::udev. Replace each occurrence of interface_to_usbdev(dev->iface) with dev->udev. This done, remove gs_can::iface. Signed-off-by: Vincent Mailhol Link: https://lore.kernel.org/all/20221208081142.16936-3-mailhol.vincent@wanadoo.fr Signed-off-by: Marc Kleine-Budde --- drivers/net/can/usb/gs_usb.c | 29 +++++++++-------------------- 1 file changed, 9 insertions(+), 20 deletions(-) diff --git a/drivers/net/can/usb/gs_usb.c b/drivers/net/can/usb/gs_usb.c index 838744d2ce34..d476c2884008 100644 --- a/drivers/net/can/usb/gs_usb.c +++ b/drivers/net/can/usb/gs_usb.c @@ -299,7 +299,6 @@ struct gs_can { struct net_device *netdev; struct usb_device *udev; - struct usb_interface *iface; struct can_bittiming_const bt_const, data_bt_const; unsigned int channel; /* channel number */ @@ -383,8 +382,7 @@ static int gs_cmd_reset(struct gs_can *dev) .mode = GS_CAN_MODE_RESET, }; - return usb_control_msg_send(interface_to_usbdev(dev->iface), 0, - GS_USB_BREQ_MODE, + return usb_control_msg_send(dev->udev, 0, GS_USB_BREQ_MODE, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, dev->channel, 0, &dm, sizeof(dm), 1000, GFP_KERNEL); @@ -396,8 +394,7 @@ static inline int gs_usb_get_timestamp(const struct gs_can *dev, __le32 timestamp; int rc; - rc = usb_control_msg_recv(interface_to_usbdev(dev->iface), 0, - GS_USB_BREQ_TIMESTAMP, + rc = usb_control_msg_recv(dev->udev, 0, GS_USB_BREQ_TIMESTAMP, USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, dev->channel, 0, ×tamp, sizeof(timestamp), @@ -674,8 +671,7 @@ static int gs_usb_set_bittiming(struct net_device *netdev) }; /* request bit timings */ - return usb_control_msg_send(interface_to_usbdev(dev->iface), 0, - GS_USB_BREQ_BITTIMING, + return usb_control_msg_send(dev->udev, 0, GS_USB_BREQ_BITTIMING, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, dev->channel, 0, &dbt, sizeof(dbt), 1000, GFP_KERNEL); @@ -698,8 +694,7 @@ static int gs_usb_set_data_bittiming(struct net_device *netdev) request = GS_USB_BREQ_QUIRK_CANTACT_PRO_DATA_BITTIMING; /* request data bit timings */ - return usb_control_msg_send(interface_to_usbdev(dev->iface), 0, - request, + return usb_control_msg_send(dev->udev, 0, request, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, dev->channel, 0, &dbt, sizeof(dbt), 1000, GFP_KERNEL); @@ -941,8 +936,7 @@ static int gs_can_open(struct net_device *netdev) /* finally start device */ dev->can.state = CAN_STATE_ERROR_ACTIVE; dm.flags = cpu_to_le32(flags); - rc = usb_control_msg_send(interface_to_usbdev(dev->iface), 0, - GS_USB_BREQ_MODE, + rc = usb_control_msg_send(dev->udev, 0, GS_USB_BREQ_MODE, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, dev->channel, 0, &dm, sizeof(dm), 1000, GFP_KERNEL); @@ -969,8 +963,7 @@ static int gs_usb_get_state(const struct net_device *netdev, struct gs_device_state ds; int rc; - rc = usb_control_msg_recv(interface_to_usbdev(dev->iface), 0, - GS_USB_BREQ_GET_STATE, + rc = usb_control_msg_recv(dev->udev, 0, GS_USB_BREQ_GET_STATE, USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, dev->channel, 0, &ds, sizeof(ds), @@ -1064,8 +1057,7 @@ static int gs_usb_set_identify(struct net_device *netdev, bool do_identify) else imode.mode = cpu_to_le32(GS_CAN_IDENTIFY_OFF); - return usb_control_msg_send(interface_to_usbdev(dev->iface), 0, - GS_USB_BREQ_IDENTIFY, + return usb_control_msg_send(dev->udev, 0, GS_USB_BREQ_IDENTIFY, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, dev->channel, 0, &imode, sizeof(imode), 100, GFP_KERNEL); @@ -1118,8 +1110,7 @@ static int gs_usb_get_termination(struct net_device *netdev, u16 *term) struct gs_device_termination_state term_state; int rc; - rc = usb_control_msg_recv(interface_to_usbdev(dev->iface), 0, - GS_USB_BREQ_GET_TERMINATION, + rc = usb_control_msg_recv(dev->udev, 0, GS_USB_BREQ_GET_TERMINATION, USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, dev->channel, 0, &term_state, sizeof(term_state), 1000, @@ -1145,8 +1136,7 @@ static int gs_usb_set_termination(struct net_device *netdev, u16 term) else term_state.state = cpu_to_le32(GS_CAN_TERMINATION_STATE_OFF); - return usb_control_msg_send(interface_to_usbdev(dev->iface), 0, - GS_USB_BREQ_SET_TERMINATION, + return usb_control_msg_send(dev->udev, 0, GS_USB_BREQ_SET_TERMINATION, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, dev->channel, 0, &term_state, sizeof(term_state), 1000, @@ -1210,7 +1200,6 @@ static struct gs_can *gs_make_candev(unsigned int channel, dev->bt_const.brp_inc = le32_to_cpu(bt_const.brp_inc); dev->udev = interface_to_usbdev(intf); - dev->iface = intf; dev->netdev = netdev; dev->channel = channel; From eaacfeaca7ad0804b9a6eff7afeba93a87db7638 Mon Sep 17 00:00:00 2001 From: Vivek Yadav Date: Wed, 7 Dec 2022 15:36:31 +0530 Subject: [PATCH 1888/2002] can: m_can: Call the RAM init directly from m_can_chip_config When we try to access the mcan message ram addresses during the probe, hclk is gated by any other drivers or disabled, because of that probe gets failed. Move the mram init functionality to mcan chip config called by m_can_start from mcan open function, by that time clocks are enabled. Suggested-by: Marc Kleine-Budde Signed-off-by: Vivek Yadav Link: https://lore.kernel.org/all/20221207100632.96200-2-vivek.2311@samsung.com Signed-off-by: Marc Kleine-Budde --- drivers/net/can/m_can/m_can.c | 32 +++++++++++++++++++++----- drivers/net/can/m_can/m_can_platform.c | 4 ---- drivers/net/can/m_can/tcan4x5x-core.c | 5 ---- 3 files changed, 26 insertions(+), 15 deletions(-) diff --git a/drivers/net/can/m_can/m_can.c b/drivers/net/can/m_can/m_can.c index b1893bb27d59..be8f4b662f95 100644 --- a/drivers/net/can/m_can/m_can.c +++ b/drivers/net/can/m_can/m_can.c @@ -1243,10 +1243,17 @@ static int m_can_set_bittiming(struct net_device *dev) * - setup bittiming * - configure timestamp generation */ -static void m_can_chip_config(struct net_device *dev) +static int m_can_chip_config(struct net_device *dev) { struct m_can_classdev *cdev = netdev_priv(dev); u32 cccr, test; + int err; + + err = m_can_init_ram(cdev); + if (err) { + dev_err(cdev->dev, "Message RAM configuration failed\n"); + return err; + } m_can_config_endisable(cdev, true); @@ -1370,18 +1377,25 @@ static void m_can_chip_config(struct net_device *dev) if (cdev->ops->init) cdev->ops->init(cdev); + + return 0; } -static void m_can_start(struct net_device *dev) +static int m_can_start(struct net_device *dev) { struct m_can_classdev *cdev = netdev_priv(dev); + int ret; /* basic m_can configuration */ - m_can_chip_config(dev); + ret = m_can_chip_config(dev); + if (ret) + return ret; cdev->can.state = CAN_STATE_ERROR_ACTIVE; m_can_enable_all_interrupts(cdev); + + return 0; } static int m_can_set_mode(struct net_device *dev, enum can_mode mode) @@ -1809,7 +1823,9 @@ static int m_can_open(struct net_device *dev) } /* start the m_can controller */ - m_can_start(dev); + err = m_can_start(dev); + if (err) + goto exit_irq_fail; if (!cdev->is_peripheral) napi_enable(&cdev->napi); @@ -2068,9 +2084,13 @@ int m_can_class_resume(struct device *dev) ret = m_can_clk_start(cdev); if (ret) return ret; + ret = m_can_start(ndev); + if (ret) { + m_can_clk_stop(cdev); + + return ret; + } - m_can_init_ram(cdev); - m_can_start(ndev); netif_device_attach(ndev); netif_start_queue(ndev); } diff --git a/drivers/net/can/m_can/m_can_platform.c b/drivers/net/can/m_can/m_can_platform.c index b5a5bedb3116..9c1dcf838006 100644 --- a/drivers/net/can/m_can/m_can_platform.c +++ b/drivers/net/can/m_can/m_can_platform.c @@ -140,10 +140,6 @@ static int m_can_plat_probe(struct platform_device *pdev) platform_set_drvdata(pdev, mcan_class); - ret = m_can_init_ram(mcan_class); - if (ret) - goto probe_fail; - pm_runtime_enable(mcan_class->dev); ret = m_can_class_register(mcan_class); if (ret) diff --git a/drivers/net/can/m_can/tcan4x5x-core.c b/drivers/net/can/m_can/tcan4x5x-core.c index 41645a24384c..a3aeb83de152 100644 --- a/drivers/net/can/m_can/tcan4x5x-core.c +++ b/drivers/net/can/m_can/tcan4x5x-core.c @@ -234,11 +234,6 @@ static int tcan4x5x_init(struct m_can_classdev *cdev) if (ret) return ret; - /* Zero out the MCAN buffers */ - ret = m_can_init_ram(cdev); - if (ret) - return ret; - ret = regmap_update_bits(tcan4x5x->regmap, TCAN4X5X_CONFIG, TCAN4X5X_MODE_SEL_MASK, TCAN4X5X_MODE_NORMAL); if (ret) From 0826e82b8a32e646b7b32ba8b68ba30812028e47 Mon Sep 17 00:00:00 2001 From: Marc Kleine-Budde Date: Fri, 9 Dec 2022 10:10:08 +0100 Subject: [PATCH 1889/2002] can: raw: add support for SO_MARK Add support for SO_MARK to the CAN_RAW protocol. This makes it possible to add traffic control filters based on the fwmark. Link: https://lore.kernel.org/all/20221210113653.170346-1-mkl@pengutronix.de Acked-by: Oliver Hartkopp Signed-off-by: Marc Kleine-Budde --- net/can/raw.c | 1 + 1 file changed, 1 insertion(+) diff --git a/net/can/raw.c b/net/can/raw.c index 3eb7d3e2b541..81071cdb0301 100644 --- a/net/can/raw.c +++ b/net/can/raw.c @@ -857,6 +857,7 @@ static int raw_sendmsg(struct socket *sock, struct msghdr *msg, size_t size) skb->dev = dev; skb->priority = sk->sk_priority; + skb->mark = sk->sk_mark; skb->tstamp = sockc.transmit_time; skb_setup_tx_timestamp(skb, sockc.tsflags); From 3abcc01c38bc5d424635df96e0677e2fbec021de Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Wed, 7 Dec 2022 17:50:21 +0100 Subject: [PATCH 1890/2002] dt-bindings: can: renesas,rcar-canfd: Fix number of channels for R-Car V3U According to the bindings, only two channels are supported. However, R-Car V3U supports eight, leading to "make dtbs" failures: arch/arm64/boot/dts/renesas/r8a779a0-falcon.dtb: can@e6660000: Unevaluated properties are not allowed ('channel2', 'channel3', 'channel4', 'channel5', 'channel6', 'channel7' were unexpected) Update the number of channels to 8 on R-Car V3U. While at it, prevent adding more properties to the channel nodes, as they must contain no other properties than a status property. Fixes: d6254d52d70de530 ("dt-bindings: can: renesas,rcar-canfd: Document r8a779a0 support") Signed-off-by: Geert Uytterhoeven Reviewed-by: Krzysztof Kozlowski Link: https://lore.kernel.org/all/7d41d72cd7db2e90bae069ce57dbb672f17500ae.1670431681.git.geert+renesas@glider.be Signed-off-by: Marc Kleine-Budde --- .../bindings/net/can/renesas,rcar-canfd.yaml | 121 ++++++++++-------- 1 file changed, 66 insertions(+), 55 deletions(-) diff --git a/Documentation/devicetree/bindings/net/can/renesas,rcar-canfd.yaml b/Documentation/devicetree/bindings/net/can/renesas,rcar-canfd.yaml index 8347053a96dc..1eb98c9a1a26 100644 --- a/Documentation/devicetree/bindings/net/can/renesas,rcar-canfd.yaml +++ b/Documentation/devicetree/bindings/net/can/renesas,rcar-canfd.yaml @@ -9,9 +9,6 @@ title: Renesas R-Car CAN FD Controller maintainers: - Fabrizio Castro -allOf: - - $ref: can-controller.yaml# - properties: compatible: oneOf: @@ -77,12 +74,13 @@ properties: description: Maximum frequency of the CANFD clock. patternProperties: - "^channel[01]$": + "^channel[0-7]$": type: object description: - The controller supports two channels and each is represented as a child - node. Each child node supports the "status" property only, which - is used to enable/disable the respective channel. + The controller supports multiple channels and each is represented as a + child node. Each channel can be enabled/disabled individually. + + additionalProperties: false required: - compatible @@ -98,60 +96,73 @@ required: - channel0 - channel1 -if: - properties: - compatible: - contains: - enum: - - renesas,rzg2l-canfd -then: - properties: - interrupts: - items: - - description: CAN global error interrupt - - description: CAN receive FIFO interrupt - - description: CAN0 error interrupt - - description: CAN0 transmit interrupt - - description: CAN0 transmit/receive FIFO receive completion interrupt - - description: CAN1 error interrupt - - description: CAN1 transmit interrupt - - description: CAN1 transmit/receive FIFO receive completion interrupt +allOf: + - $ref: can-controller.yaml# - interrupt-names: - items: - - const: g_err - - const: g_recc - - const: ch0_err - - const: ch0_rec - - const: ch0_trx - - const: ch1_err - - const: ch1_rec - - const: ch1_trx + - if: + properties: + compatible: + contains: + enum: + - renesas,rzg2l-canfd + then: + properties: + interrupts: + items: + - description: CAN global error interrupt + - description: CAN receive FIFO interrupt + - description: CAN0 error interrupt + - description: CAN0 transmit interrupt + - description: CAN0 transmit/receive FIFO receive completion interrupt + - description: CAN1 error interrupt + - description: CAN1 transmit interrupt + - description: CAN1 transmit/receive FIFO receive completion interrupt - resets: - maxItems: 2 + interrupt-names: + items: + - const: g_err + - const: g_recc + - const: ch0_err + - const: ch0_rec + - const: ch0_trx + - const: ch1_err + - const: ch1_rec + - const: ch1_trx - reset-names: - items: - - const: rstp_n - - const: rstc_n + resets: + maxItems: 2 - required: - - reset-names -else: - properties: - interrupts: - items: - - description: Channel interrupt - - description: Global interrupt + reset-names: + items: + - const: rstp_n + - const: rstc_n - interrupt-names: - items: - - const: ch_int - - const: g_int + required: + - reset-names + else: + properties: + interrupts: + items: + - description: Channel interrupt + - description: Global interrupt - resets: - maxItems: 1 + interrupt-names: + items: + - const: ch_int + - const: g_int + + resets: + maxItems: 1 + + - if: + not: + properties: + compatible: + contains: + const: renesas,r8a779a0-canfd + then: + patternProperties: + "^channel[2-7]$": false unevaluatedProperties: false From c1eaf8b9bd3145d029446c927e1c9ce925dfe6a7 Mon Sep 17 00:00:00 2001 From: Markus Schneider-Pargmann Date: Tue, 6 Dec 2022 12:57:18 +0100 Subject: [PATCH 1891/2002] can: m_can: Eliminate double read of TXFQS in tx_handler The TXFQS register is read first to check if the fifo is full and then immediately again to get the putidx. This is unnecessary and adds significant overhead if read requests are done over a slow bus, for example SPI with tcan4x5x. Add a variable to store the value of the register. Split the m_can_tx_fifo_full function into two to avoid the hidden m_can_read call if not needed. Signed-off-by: Markus Schneider-Pargmann Link: https://lore.kernel.org/all/20221206115728.1056014-2-msp@baylibre.com Signed-off-by: Marc Kleine-Budde --- drivers/net/can/m_can/m_can.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/drivers/net/can/m_can/m_can.c b/drivers/net/can/m_can/m_can.c index be8f4b662f95..61531daf66b8 100644 --- a/drivers/net/can/m_can/m_can.c +++ b/drivers/net/can/m_can/m_can.c @@ -369,9 +369,14 @@ m_can_txe_fifo_read(struct m_can_classdev *cdev, u32 fgi, u32 offset, u32 *val) return cdev->ops->read_fifo(cdev, addr_offset, val, 1); } +static inline bool _m_can_tx_fifo_full(u32 txfqs) +{ + return !!(txfqs & TXFQS_TFQF); +} + static inline bool m_can_tx_fifo_full(struct m_can_classdev *cdev) { - return !!(m_can_read(cdev, M_CAN_TXFQS) & TXFQS_TFQF); + return _m_can_tx_fifo_full(m_can_read(cdev, M_CAN_TXFQS)); } static void m_can_config_endisable(struct m_can_classdev *cdev, bool enable) @@ -1609,6 +1614,7 @@ static netdev_tx_t m_can_tx_handler(struct m_can_classdev *cdev) struct sk_buff *skb = cdev->tx_skb; struct id_and_dlc fifo_header; u32 cccr, fdflags; + u32 txfqs; int err; int putidx; @@ -1665,8 +1671,10 @@ static netdev_tx_t m_can_tx_handler(struct m_can_classdev *cdev) } else { /* Transmit routine for version >= v3.1.x */ + txfqs = m_can_read(cdev, M_CAN_TXFQS); + /* Check if FIFO full */ - if (m_can_tx_fifo_full(cdev)) { + if (_m_can_tx_fifo_full(txfqs)) { /* This shouldn't happen */ netif_stop_queue(dev); netdev_warn(dev, @@ -1682,8 +1690,7 @@ static netdev_tx_t m_can_tx_handler(struct m_can_classdev *cdev) } /* get put index for frame */ - putidx = FIELD_GET(TXFQS_TFQPI_MASK, - m_can_read(cdev, M_CAN_TXFQS)); + putidx = FIELD_GET(TXFQS_TFQPI_MASK, txfqs); /* Construct DLC Field, with CAN-FD configuration. * Use the put index of the fifo as the message marker, From 57757937974988dc8feb7cf66b17dca72bca813b Mon Sep 17 00:00:00 2001 From: Markus Schneider-Pargmann Date: Tue, 6 Dec 2022 12:57:19 +0100 Subject: [PATCH 1892/2002] can: m_can: Avoid reading irqstatus twice For peripheral devices the m_can_rx_handler is called directly after setting cdev->irqstatus. This means we don't have to read the irqstatus again in m_can_rx_handler. Avoid this by adding a parameter that is false for direct calls. Signed-off-by: Markus Schneider-Pargmann Link: https://lore.kernel.org/all/20221206115728.1056014-3-msp@baylibre.com Signed-off-by: Marc Kleine-Budde --- drivers/net/can/m_can/m_can.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/drivers/net/can/m_can/m_can.c b/drivers/net/can/m_can/m_can.c index 61531daf66b8..8399e55a4ea6 100644 --- a/drivers/net/can/m_can/m_can.c +++ b/drivers/net/can/m_can/m_can.c @@ -905,14 +905,13 @@ static int m_can_handle_bus_errors(struct net_device *dev, u32 irqstatus, return work_done; } -static int m_can_rx_handler(struct net_device *dev, int quota) +static int m_can_rx_handler(struct net_device *dev, int quota, u32 irqstatus) { struct m_can_classdev *cdev = netdev_priv(dev); int rx_work_or_err; int work_done = 0; - u32 irqstatus, psr; + u32 psr; - irqstatus = cdev->irqstatus | m_can_read(cdev, M_CAN_IR); if (!irqstatus) goto end; @@ -956,12 +955,12 @@ end: return work_done; } -static int m_can_rx_peripheral(struct net_device *dev) +static int m_can_rx_peripheral(struct net_device *dev, u32 irqstatus) { struct m_can_classdev *cdev = netdev_priv(dev); int work_done; - work_done = m_can_rx_handler(dev, NAPI_POLL_WEIGHT); + work_done = m_can_rx_handler(dev, NAPI_POLL_WEIGHT, irqstatus); /* Don't re-enable interrupts if the driver had a fatal error * (e.g., FIFO read failure). @@ -977,8 +976,11 @@ static int m_can_poll(struct napi_struct *napi, int quota) struct net_device *dev = napi->dev; struct m_can_classdev *cdev = netdev_priv(dev); int work_done; + u32 irqstatus; - work_done = m_can_rx_handler(dev, quota); + irqstatus = cdev->irqstatus | m_can_read(cdev, M_CAN_IR); + + work_done = m_can_rx_handler(dev, quota, irqstatus); /* Don't re-enable interrupts if the driver had a fatal error * (e.g., FIFO read failure). @@ -1088,7 +1090,7 @@ static irqreturn_t m_can_isr(int irq, void *dev_id) m_can_disable_all_interrupts(cdev); if (!cdev->is_peripheral) napi_schedule(&cdev->napi); - else if (m_can_rx_peripheral(dev) < 0) + else if (m_can_rx_peripheral(dev, ir) < 0) goto out_fail; } From fac52bf786e5c302990fa79f2fdde24e84f810b0 Mon Sep 17 00:00:00 2001 From: Markus Schneider-Pargmann Date: Tue, 6 Dec 2022 12:57:20 +0100 Subject: [PATCH 1893/2002] can: m_can: Read register PSR only on error Only read register PSR if there is an error indicated in irqstatus. Signed-off-by: Markus Schneider-Pargmann Link: https://lore.kernel.org/all/20221206115728.1056014-4-msp@baylibre.com Signed-off-by: Marc Kleine-Budde --- drivers/net/can/m_can/m_can.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/drivers/net/can/m_can/m_can.c b/drivers/net/can/m_can/m_can.c index 8399e55a4ea6..045679af76a0 100644 --- a/drivers/net/can/m_can/m_can.c +++ b/drivers/net/can/m_can/m_can.c @@ -910,7 +910,6 @@ static int m_can_rx_handler(struct net_device *dev, int quota, u32 irqstatus) struct m_can_classdev *cdev = netdev_priv(dev); int rx_work_or_err; int work_done = 0; - u32 psr; if (!irqstatus) goto end; @@ -936,13 +935,13 @@ static int m_can_rx_handler(struct net_device *dev, int quota, u32 irqstatus) } } - psr = m_can_read(cdev, M_CAN_PSR); - if (irqstatus & IR_ERR_STATE) - work_done += m_can_handle_state_errors(dev, psr); + work_done += m_can_handle_state_errors(dev, + m_can_read(cdev, M_CAN_PSR)); if (irqstatus & IR_ERR_BUS_30X) - work_done += m_can_handle_bus_errors(dev, irqstatus, psr); + work_done += m_can_handle_bus_errors(dev, irqstatus, + m_can_read(cdev, M_CAN_PSR)); if (irqstatus & IR_RF0N) { rx_work_or_err = m_can_do_rx_poll(dev, (quota - work_done)); From d4535b90a76af49d1dd7bc70ad0311a88ac5313d Mon Sep 17 00:00:00 2001 From: Markus Schneider-Pargmann Date: Tue, 6 Dec 2022 12:57:21 +0100 Subject: [PATCH 1894/2002] can: m_can: Count TXE FIFO getidx in the driver The getindex simply increases by one for every iteration. There is no need to get the current getidx every time from a register. Instead we can just count and wrap if necessary. Signed-off-by: Markus Schneider-Pargmann Link: https://lore.kernel.org/all/20221206115728.1056014-5-msp@baylibre.com Signed-off-by: Marc Kleine-Budde --- drivers/net/can/m_can/m_can.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/net/can/m_can/m_can.c b/drivers/net/can/m_can/m_can.c index 045679af76a0..83d3a7f191bf 100644 --- a/drivers/net/can/m_can/m_can.c +++ b/drivers/net/can/m_can/m_can.c @@ -1030,15 +1030,13 @@ static int m_can_echo_tx_event(struct net_device *dev) /* Get Tx Event fifo element count */ txe_count = FIELD_GET(TXEFS_EFFL_MASK, m_can_txefs); + fgi = FIELD_GET(TXEFS_EFGI_MASK, m_can_txefs); /* Get and process all sent elements */ for (i = 0; i < txe_count; i++) { u32 txe, timestamp = 0; int err; - /* retrieve get index */ - fgi = FIELD_GET(TXEFS_EFGI_MASK, m_can_read(cdev, M_CAN_TXEFS)); - /* get message marker, timestamp */ err = m_can_txe_fifo_read(cdev, fgi, 4, &txe); if (err) { @@ -1052,6 +1050,7 @@ static int m_can_echo_tx_event(struct net_device *dev) /* ack txe element */ m_can_write(cdev, M_CAN_TXEFA, FIELD_PREP(TXEFA_EFAI_MASK, fgi)); + fgi = (++fgi >= cdev->mcfg[MRAM_TXE].num ? 0 : fgi); /* update stats */ m_can_tx_update_stats(cdev, msg_mark, timestamp); From 6355a3c983e6d6fc998f4223bd2c7ef047e4572b Mon Sep 17 00:00:00 2001 From: Markus Schneider-Pargmann Date: Tue, 6 Dec 2022 12:57:22 +0100 Subject: [PATCH 1895/2002] can: m_can: Count read getindex in the driver The getindex gets increased by one every time. We can calculate the correct getindex in the driver and avoid the additional reads of rxfs. Signed-off-by: Markus Schneider-Pargmann Link: https://lore.kernel.org/all/20221206115728.1056014-6-msp@baylibre.com Signed-off-by: Marc Kleine-Budde --- drivers/net/can/m_can/m_can.c | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/drivers/net/can/m_can/m_can.c b/drivers/net/can/m_can/m_can.c index 83d3a7f191bf..9476d96013fa 100644 --- a/drivers/net/can/m_can/m_can.c +++ b/drivers/net/can/m_can/m_can.c @@ -477,19 +477,16 @@ static void m_can_receive_skb(struct m_can_classdev *cdev, } } -static int m_can_read_fifo(struct net_device *dev, u32 rxfs) +static int m_can_read_fifo(struct net_device *dev, u32 fgi) { struct net_device_stats *stats = &dev->stats; struct m_can_classdev *cdev = netdev_priv(dev); struct canfd_frame *cf; struct sk_buff *skb; struct id_and_dlc fifo_header; - u32 fgi; u32 timestamp = 0; int err; - /* calculate the fifo get index for where to read data */ - fgi = FIELD_GET(RXFS_FGI_MASK, rxfs); err = m_can_fifo_read(cdev, fgi, M_CAN_FIFO_ID, &fifo_header, 2); if (err) goto out_fail; @@ -554,6 +551,9 @@ static int m_can_do_rx_poll(struct net_device *dev, int quota) struct m_can_classdev *cdev = netdev_priv(dev); u32 pkts = 0; u32 rxfs; + u32 rx_count; + u32 fgi; + int i; int err; rxfs = m_can_read(cdev, M_CAN_RXF0S); @@ -562,14 +562,17 @@ static int m_can_do_rx_poll(struct net_device *dev, int quota) return 0; } - while ((rxfs & RXFS_FFL_MASK) && (quota > 0)) { - err = m_can_read_fifo(dev, rxfs); + rx_count = FIELD_GET(RXFS_FFL_MASK, rxfs); + fgi = FIELD_GET(RXFS_FGI_MASK, rxfs); + + for (i = 0; i < rx_count && quota > 0; ++i) { + err = m_can_read_fifo(dev, fgi); if (err) return err; quota--; pkts++; - rxfs = m_can_read(cdev, M_CAN_RXF0S); + fgi = (++fgi >= cdev->mcfg[MRAM_RXF0].num ? 0 : fgi); } return pkts; From e3bff5256a0f12a1edc8d79bdef53eb83c2f1fbf Mon Sep 17 00:00:00 2001 From: Markus Schneider-Pargmann Date: Tue, 6 Dec 2022 12:57:23 +0100 Subject: [PATCH 1896/2002] can: m_can: Batch acknowledge transmit events Transmit events from the txe fifo can be batch acknowledged by acknowledging the last read txe fifo item. This will save txe_count writes which is important for peripheral chips. Signed-off-by: Markus Schneider-Pargmann Link: https://lore.kernel.org/all/20221206115728.1056014-7-msp@baylibre.com Signed-off-by: Marc Kleine-Budde --- drivers/net/can/m_can/m_can.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/drivers/net/can/m_can/m_can.c b/drivers/net/can/m_can/m_can.c index 9476d96013fa..656d2daafad1 100644 --- a/drivers/net/can/m_can/m_can.c +++ b/drivers/net/can/m_can/m_can.c @@ -1023,7 +1023,9 @@ static int m_can_echo_tx_event(struct net_device *dev) u32 txe_count = 0; u32 m_can_txefs; u32 fgi = 0; + int ack_fgi = -1; int i = 0; + int err = 0; unsigned int msg_mark; struct m_can_classdev *cdev = netdev_priv(dev); @@ -1038,28 +1040,29 @@ static int m_can_echo_tx_event(struct net_device *dev) /* Get and process all sent elements */ for (i = 0; i < txe_count; i++) { u32 txe, timestamp = 0; - int err; /* get message marker, timestamp */ err = m_can_txe_fifo_read(cdev, fgi, 4, &txe); if (err) { netdev_err(dev, "TXE FIFO read returned %d\n", err); - return err; + break; } msg_mark = FIELD_GET(TX_EVENT_MM_MASK, txe); timestamp = FIELD_GET(TX_EVENT_TXTS_MASK, txe) << 16; - /* ack txe element */ - m_can_write(cdev, M_CAN_TXEFA, FIELD_PREP(TXEFA_EFAI_MASK, - fgi)); + ack_fgi = fgi; fgi = (++fgi >= cdev->mcfg[MRAM_TXE].num ? 0 : fgi); /* update stats */ m_can_tx_update_stats(cdev, msg_mark, timestamp); } - return 0; + if (ack_fgi != -1) + m_can_write(cdev, M_CAN_TXEFA, FIELD_PREP(TXEFA_EFAI_MASK, + ack_fgi)); + + return err; } static irqreturn_t m_can_isr(int irq, void *dev_id) From e2f1c8cb020296ddfa255e06b15f071807c2ed73 Mon Sep 17 00:00:00 2001 From: Markus Schneider-Pargmann Date: Tue, 6 Dec 2022 12:57:24 +0100 Subject: [PATCH 1897/2002] can: m_can: Batch acknowledge rx fifo Instead of acknowledging every item of the fifo, only acknowledge the last item read. This behavior is documented in the datasheet. The new getindex will be the acknowledged item + 1. Signed-off-by: Markus Schneider-Pargmann Link: https://lore.kernel.org/all/20221206115728.1056014-8-msp@baylibre.com Signed-off-by: Marc Kleine-Budde --- drivers/net/can/m_can/m_can.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/drivers/net/can/m_can/m_can.c b/drivers/net/can/m_can/m_can.c index 656d2daafad1..8e83d6963d85 100644 --- a/drivers/net/can/m_can/m_can.c +++ b/drivers/net/can/m_can/m_can.c @@ -530,9 +530,6 @@ static int m_can_read_fifo(struct net_device *dev, u32 fgi) } stats->rx_packets++; - /* acknowledge rx fifo 0 */ - m_can_write(cdev, M_CAN_RXF0A, fgi); - timestamp = FIELD_GET(RX_BUF_RXTS_MASK, fifo_header.dlc) << 16; m_can_receive_skb(cdev, skb, timestamp); @@ -553,8 +550,9 @@ static int m_can_do_rx_poll(struct net_device *dev, int quota) u32 rxfs; u32 rx_count; u32 fgi; + int ack_fgi = -1; int i; - int err; + int err = 0; rxfs = m_can_read(cdev, M_CAN_RXF0S); if (!(rxfs & RXFS_FFL_MASK)) { @@ -568,13 +566,20 @@ static int m_can_do_rx_poll(struct net_device *dev, int quota) for (i = 0; i < rx_count && quota > 0; ++i) { err = m_can_read_fifo(dev, fgi); if (err) - return err; + break; quota--; pkts++; + ack_fgi = fgi; fgi = (++fgi >= cdev->mcfg[MRAM_RXF0].num ? 0 : fgi); } + if (ack_fgi != -1) + m_can_write(cdev, M_CAN_RXF0A, ack_fgi); + + if (err) + return err; + return pkts; } From 40c9e4f676abbe194541d88e796341c92d5a13c0 Mon Sep 17 00:00:00 2001 From: Markus Schneider-Pargmann Date: Tue, 6 Dec 2022 12:57:25 +0100 Subject: [PATCH 1898/2002] can: tcan4x5x: Remove invalid write in clear_interrupts Register 0x824 TCAN4X5X_MCAN_INT_REG is a read-only register. Any writes to this register do not have any effect. Remove this write. The m_can driver aldready clears the interrupts in m_can_isr() by writing to M_CAN_IR which is translated to register 0x1050 which is a writable version of this register. Fixes: 5443c226ba91 ("can: tcan4x5x: Add tcan4x5x driver to the kernel") Signed-off-by: Markus Schneider-Pargmann Link: https://lore.kernel.org/all/20221206115728.1056014-9-msp@baylibre.com Signed-off-by: Marc Kleine-Budde --- drivers/net/can/m_can/tcan4x5x-core.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/drivers/net/can/m_can/tcan4x5x-core.c b/drivers/net/can/m_can/tcan4x5x-core.c index a3aeb83de152..a77f4d4f6299 100644 --- a/drivers/net/can/m_can/tcan4x5x-core.c +++ b/drivers/net/can/m_can/tcan4x5x-core.c @@ -204,11 +204,6 @@ static int tcan4x5x_clear_interrupts(struct m_can_classdev *cdev) if (ret) return ret; - ret = tcan4x5x_write_tcan_reg(cdev, TCAN4X5X_MCAN_INT_REG, - TCAN4X5X_ENABLE_MCAN_INT); - if (ret) - return ret; - ret = tcan4x5x_write_tcan_reg(cdev, TCAN4X5X_INT_FLAGS, TCAN4X5X_CLEAR_ALL_INT); if (ret) From 67727a17a6b375d68fe569b77e6516b034b834c0 Mon Sep 17 00:00:00 2001 From: Markus Schneider-Pargmann Date: Tue, 6 Dec 2022 12:57:26 +0100 Subject: [PATCH 1899/2002] can: tcan4x5x: Fix use of register error status mask TCAN4X5X_ERROR_STATUS is not a status register that needs clearing during interrupt handling. Instead this is a masking register that masks error interrupts. Writing TCAN4X5X_CLEAR_ALL_INT to this register effectively masks everything. Rename the register and mask all error interrupts only once by writing to the register in tcan4x5x_init. Fixes: 5443c226ba91 ("can: tcan4x5x: Add tcan4x5x driver to the kernel") Signed-off-by: Markus Schneider-Pargmann Link: https://lore.kernel.org/all/20221206115728.1056014-10-msp@baylibre.com Signed-off-by: Marc Kleine-Budde --- drivers/net/can/m_can/tcan4x5x-core.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/net/can/m_can/tcan4x5x-core.c b/drivers/net/can/m_can/tcan4x5x-core.c index a77f4d4f6299..2342aa011647 100644 --- a/drivers/net/can/m_can/tcan4x5x-core.c +++ b/drivers/net/can/m_can/tcan4x5x-core.c @@ -10,7 +10,7 @@ #define TCAN4X5X_DEV_ID1 0x04 #define TCAN4X5X_REV 0x08 #define TCAN4X5X_STATUS 0x0C -#define TCAN4X5X_ERROR_STATUS 0x10 +#define TCAN4X5X_ERROR_STATUS_MASK 0x10 #define TCAN4X5X_CONTROL 0x14 #define TCAN4X5X_CONFIG 0x800 @@ -204,12 +204,7 @@ static int tcan4x5x_clear_interrupts(struct m_can_classdev *cdev) if (ret) return ret; - ret = tcan4x5x_write_tcan_reg(cdev, TCAN4X5X_INT_FLAGS, - TCAN4X5X_CLEAR_ALL_INT); - if (ret) - return ret; - - return tcan4x5x_write_tcan_reg(cdev, TCAN4X5X_ERROR_STATUS, + return tcan4x5x_write_tcan_reg(cdev, TCAN4X5X_INT_FLAGS, TCAN4X5X_CLEAR_ALL_INT); } @@ -229,6 +224,11 @@ static int tcan4x5x_init(struct m_can_classdev *cdev) if (ret) return ret; + ret = tcan4x5x_write_tcan_reg(cdev, TCAN4X5X_ERROR_STATUS_MASK, + TCAN4X5X_CLEAR_ALL_INT); + if (ret) + return ret; + ret = regmap_update_bits(tcan4x5x->regmap, TCAN4X5X_CONFIG, TCAN4X5X_MODE_SEL_MASK, TCAN4X5X_MODE_NORMAL); if (ret) From ef5778f7084196997e00095c43b12693ef5111d0 Mon Sep 17 00:00:00 2001 From: Markus Schneider-Pargmann Date: Tue, 6 Dec 2022 12:57:27 +0100 Subject: [PATCH 1900/2002] can: tcan4x5x: Fix register range of first two blocks According to the datasheet 0x10 is the last register in the first block, not register 0x2c. The datasheet lists the last register of the second block as 0x830, not 0x83c. Signed-off-by: Markus Schneider-Pargmann Link: https://lore.kernel.org/all/20221206115728.1056014-11-msp@baylibre.com Signed-off-by: Marc Kleine-Budde --- drivers/net/can/m_can/tcan4x5x-regmap.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/can/m_can/tcan4x5x-regmap.c b/drivers/net/can/m_can/tcan4x5x-regmap.c index 26e212b8ca7a..33aed989e42a 100644 --- a/drivers/net/can/m_can/tcan4x5x-regmap.c +++ b/drivers/net/can/m_can/tcan4x5x-regmap.c @@ -91,8 +91,8 @@ static int tcan4x5x_regmap_read(void *context, } static const struct regmap_range tcan4x5x_reg_table_yes_range[] = { - regmap_reg_range(0x0000, 0x002c), /* Device ID and SPI Registers */ - regmap_reg_range(0x0800, 0x083c), /* Device configuration registers and Interrupt Flags*/ + regmap_reg_range(0x0000, 0x0010), /* Device ID and SPI Registers */ + regmap_reg_range(0x0800, 0x0830), /* Device configuration registers and Interrupt Flags*/ regmap_reg_range(0x1000, 0x10fc), /* M_CAN */ regmap_reg_range(0x8000, 0x87fc), /* MRAM */ }; From 39dbb21b6a2951a2ce311c93eb09f9ff9b96e7b8 Mon Sep 17 00:00:00 2001 From: Markus Schneider-Pargmann Date: Tue, 6 Dec 2022 12:57:28 +0100 Subject: [PATCH 1901/2002] can: tcan4x5x: Specify separate read/write ranges Specify exactly which registers are read/writeable in the chip. This is supposed to help detect any violations in the future. Signed-off-by: Markus Schneider-Pargmann Link: https://lore.kernel.org/all/20221206115728.1056014-12-msp@baylibre.com Signed-off-by: Marc Kleine-Budde --- drivers/net/can/m_can/tcan4x5x-regmap.c | 43 +++++++++++++++++++++---- 1 file changed, 37 insertions(+), 6 deletions(-) diff --git a/drivers/net/can/m_can/tcan4x5x-regmap.c b/drivers/net/can/m_can/tcan4x5x-regmap.c index 33aed989e42a..2b218ce04e9f 100644 --- a/drivers/net/can/m_can/tcan4x5x-regmap.c +++ b/drivers/net/can/m_can/tcan4x5x-regmap.c @@ -90,16 +90,47 @@ static int tcan4x5x_regmap_read(void *context, return 0; } -static const struct regmap_range tcan4x5x_reg_table_yes_range[] = { +static const struct regmap_range tcan4x5x_reg_table_wr_range[] = { + /* Device ID and SPI Registers */ + regmap_reg_range(0x000c, 0x0010), + /* Device configuration registers and Interrupt Flags*/ + regmap_reg_range(0x0800, 0x080c), + regmap_reg_range(0x0814, 0x0814), + regmap_reg_range(0x0820, 0x0820), + regmap_reg_range(0x0830, 0x0830), + /* M_CAN */ + regmap_reg_range(0x100c, 0x102c), + regmap_reg_range(0x1048, 0x1048), + regmap_reg_range(0x1050, 0x105c), + regmap_reg_range(0x1080, 0x1088), + regmap_reg_range(0x1090, 0x1090), + regmap_reg_range(0x1098, 0x10a0), + regmap_reg_range(0x10a8, 0x10b0), + regmap_reg_range(0x10b8, 0x10c0), + regmap_reg_range(0x10c8, 0x10c8), + regmap_reg_range(0x10d0, 0x10d4), + regmap_reg_range(0x10e0, 0x10e4), + regmap_reg_range(0x10f0, 0x10f0), + regmap_reg_range(0x10f8, 0x10f8), + /* MRAM */ + regmap_reg_range(0x8000, 0x87fc), +}; + +static const struct regmap_range tcan4x5x_reg_table_rd_range[] = { regmap_reg_range(0x0000, 0x0010), /* Device ID and SPI Registers */ regmap_reg_range(0x0800, 0x0830), /* Device configuration registers and Interrupt Flags*/ regmap_reg_range(0x1000, 0x10fc), /* M_CAN */ regmap_reg_range(0x8000, 0x87fc), /* MRAM */ }; -static const struct regmap_access_table tcan4x5x_reg_table = { - .yes_ranges = tcan4x5x_reg_table_yes_range, - .n_yes_ranges = ARRAY_SIZE(tcan4x5x_reg_table_yes_range), +static const struct regmap_access_table tcan4x5x_reg_table_wr = { + .yes_ranges = tcan4x5x_reg_table_wr_range, + .n_yes_ranges = ARRAY_SIZE(tcan4x5x_reg_table_wr_range), +}; + +static const struct regmap_access_table tcan4x5x_reg_table_rd = { + .yes_ranges = tcan4x5x_reg_table_rd_range, + .n_yes_ranges = ARRAY_SIZE(tcan4x5x_reg_table_rd_range), }; static const struct regmap_config tcan4x5x_regmap = { @@ -107,8 +138,8 @@ static const struct regmap_config tcan4x5x_regmap = { .reg_stride = 4, .pad_bits = 8, .val_bits = 32, - .wr_table = &tcan4x5x_reg_table, - .rd_table = &tcan4x5x_reg_table, + .wr_table = &tcan4x5x_reg_table_wr, + .rd_table = &tcan4x5x_reg_table_rd, .max_register = TCAN4X5X_MAX_REGISTER, .cache_type = REGCACHE_NONE, .read_flag_mask = (__force unsigned long) From 5821504f5073983733465b8bc430049c4343bbd7 Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Thu, 8 Dec 2022 12:55:34 +0200 Subject: [PATCH 1902/2002] net: ethernet: ti: am65-cpsw: Fix PM runtime leakage in am65_cpsw_nuss_ndo_slave_open() Ensure pm_runtime_put() is issued in error path. Reported-by: Jakub Kicinski Fixes: 93a76530316a ("net: ethernet: ti: introduce am65x/j721e gigabit eth subsystem driver") Signed-off-by: Roger Quadros Reviewed-by: Saeed Mahameed Link: https://lore.kernel.org/r/20221208105534.63709-1-rogerq@kernel.org Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/ti/am65-cpsw-nuss.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/drivers/net/ethernet/ti/am65-cpsw-nuss.c b/drivers/net/ethernet/ti/am65-cpsw-nuss.c index de112ab3195c..9decb0c7961b 100644 --- a/drivers/net/ethernet/ti/am65-cpsw-nuss.c +++ b/drivers/net/ethernet/ti/am65-cpsw-nuss.c @@ -573,20 +573,21 @@ static int am65_cpsw_nuss_ndo_slave_open(struct net_device *ndev) reg = cpsw_sl_reg_read(port->slave.mac_sl, CPSW_SL_SOFT_RESET); if (reg) { dev_err(common->dev, "soft RESET didn't complete\n"); - return -ETIMEDOUT; + ret = -ETIMEDOUT; + goto runtime_put; } /* Notify the stack of the actual queue counts. */ ret = netif_set_real_num_tx_queues(ndev, common->tx_ch_num); if (ret) { dev_err(common->dev, "cannot set real number of tx queues\n"); - return ret; + goto runtime_put; } ret = netif_set_real_num_rx_queues(ndev, AM65_CPSW_MAX_RX_QUEUES); if (ret) { dev_err(common->dev, "cannot set real number of rx queues\n"); - return ret; + goto runtime_put; } for (i = 0; i < common->tx_ch_num; i++) @@ -594,7 +595,7 @@ static int am65_cpsw_nuss_ndo_slave_open(struct net_device *ndev) ret = am65_cpsw_nuss_common_open(common); if (ret) - return ret; + goto runtime_put; common->usage_count++; @@ -622,6 +623,10 @@ static int am65_cpsw_nuss_ndo_slave_open(struct net_device *ndev) error_cleanup: am65_cpsw_nuss_ndo_slave_stop(ndev); return ret; + +runtime_put: + pm_runtime_put(common->dev); + return ret; } static void am65_cpsw_nuss_rx_cleanup(void *data, dma_addr_t desc_dma) From 8964916d206071b058c6351f88b1966bd58cbde0 Mon Sep 17 00:00:00 2001 From: Jerry Ray Date: Fri, 9 Dec 2022 09:35:02 -0600 Subject: [PATCH 1903/2002] net: lan9303: Fix read error execution path This patch fixes an issue where a read failure of a port statistic counter will return unknown results. While it is highly unlikely the read will ever fail, it is much cleaner to return a zero for the stat count. Fixes: a1292595e006 ("net: dsa: add new DSA switch driver for the SMSC-LAN9303") Signed-off-by: Jerry Ray Reviewed-by: Vladimir Oltean Reviewed-by: Florian Fainelli Link: https://lore.kernel.org/r/20221209153502.7429-1-jerry.ray@microchip.com Signed-off-by: Jakub Kicinski --- drivers/net/dsa/lan9303-core.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/net/dsa/lan9303-core.c b/drivers/net/dsa/lan9303-core.c index 80f07bd20593..2e270b479143 100644 --- a/drivers/net/dsa/lan9303-core.c +++ b/drivers/net/dsa/lan9303-core.c @@ -1005,9 +1005,11 @@ static void lan9303_get_ethtool_stats(struct dsa_switch *ds, int port, ret = lan9303_read_switch_port( chip, port, lan9303_mib[u].offset, ®); - if (ret) + if (ret) { dev_warn(chip->dev, "Reading status port %d reg %u failed\n", port, lan9303_mib[u].offset); + reg = 0; + } data[u] = reg; } } From 5f7d78b2b12a9d561f48fa00bab29b40f4616dad Mon Sep 17 00:00:00 2001 From: Eric Pilmore Date: Thu, 8 Dec 2022 16:06:59 -0800 Subject: [PATCH 1904/2002] ntb_netdev: Use dev_kfree_skb_any() in interrupt context TX/RX callback handlers (ntb_netdev_tx_handler(), ntb_netdev_rx_handler()) can be called in interrupt context via the DMA framework when the respective DMA operations have completed. As such, any calls by these routines to free skb's, should use the interrupt context safe dev_kfree_skb_any() function. Previously, these callback handlers would call the interrupt unsafe version of dev_kfree_skb(). This has not presented an issue on Intel IOAT DMA engines as that driver utilizes tasklets rather than a hard interrupt handler, like the AMD PTDMA DMA driver. On AMD systems, a kernel WARNING message is encountered, which is being issued from skb_release_head_state() due to in_hardirq() being true. Besides the user visible WARNING from the kernel, the other symptom of this bug was that TCP/IP performance across the ntb_netdev interface was very poor, i.e. approximately an order of magnitude below what was expected. With the repair to use dev_kfree_skb_any(), kernel WARNINGs from skb_release_head_state() ceased and TCP/IP performance, as measured by iperf, was on par with expected results, approximately 20 Gb/s on AMD Milan based server. Note that this performance is comparable with Intel based servers. Fixes: 765ccc7bc3d91 ("ntb_netdev: correct skb leak") Fixes: 548c237c0a997 ("net: Add support for NTB virtual ethernet device") Signed-off-by: Eric Pilmore Reviewed-by: Dave Jiang Link: https://lore.kernel.org/r/20221209000659.8318-1-epilmore@gigaio.com Signed-off-by: Jakub Kicinski --- drivers/net/ntb_netdev.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/ntb_netdev.c b/drivers/net/ntb_netdev.c index a4abea921046..85dbe7f73e31 100644 --- a/drivers/net/ntb_netdev.c +++ b/drivers/net/ntb_netdev.c @@ -137,7 +137,7 @@ static void ntb_netdev_rx_handler(struct ntb_transport_qp *qp, void *qp_data, enqueue_again: rc = ntb_transport_rx_enqueue(qp, skb, skb->data, ndev->mtu + ETH_HLEN); if (rc) { - dev_kfree_skb(skb); + dev_kfree_skb_any(skb); ndev->stats.rx_errors++; ndev->stats.rx_fifo_errors++; } @@ -192,7 +192,7 @@ static void ntb_netdev_tx_handler(struct ntb_transport_qp *qp, void *qp_data, ndev->stats.tx_aborted_errors++; } - dev_kfree_skb(skb); + dev_kfree_skb_any(skb); if (ntb_transport_tx_free_entry(dev->qp) >= tx_start) { /* Make sure anybody stopping the queue after this sees the new From da05cecc4939c0410d56c29e252998b192756318 Mon Sep 17 00:00:00 2001 From: Firo Yang Date: Fri, 9 Dec 2022 13:48:54 +0800 Subject: [PATCH 1905/2002] sctp: sysctl: make extra pointers netns aware Recently, a customer reported that from their container whose net namespace is different to the host's init_net, they can't set the container's net.sctp.rto_max to any value smaller than init_net.sctp.rto_min. For instance, Host: sudo sysctl net.sctp.rto_min net.sctp.rto_min = 1000 Container: echo 100 > /mnt/proc-net/sctp/rto_min echo 400 > /mnt/proc-net/sctp/rto_max echo: write error: Invalid argument This is caused by the check made from this'commit 4f3fdf3bc59c ("sctp: add check rto_min and rto_max in sysctl")' When validating the input value, it's always referring the boundary value set for the init_net namespace. Having container's rto_max smaller than host's init_net.sctp.rto_min does make sense. Consider that the rto between two containers on the same host is very likely smaller than it for two hosts. So to fix this problem, as suggested by Marcelo, this patch makes the extra pointers of rto_min, rto_max, pf_retrans, and ps_retrans point to the corresponding variables from the newly created net namespace while the new net namespace is being registered in sctp_sysctl_net_register. Fixes: 4f3fdf3bc59c ("sctp: add check rto_min and rto_max in sysctl") Reviewed-by: Marcelo Ricardo Leitner Reviewed-by: Jakub Kicinski Acked-by: Marcelo Ricardo Leitner Signed-off-by: Firo Yang Link: https://lore.kernel.org/r/20221209054854.23889-1-firo.yang@suse.com Signed-off-by: Jakub Kicinski --- net/sctp/sysctl.c | 73 ++++++++++++++++++++++++++++------------------- 1 file changed, 44 insertions(+), 29 deletions(-) diff --git a/net/sctp/sysctl.c b/net/sctp/sysctl.c index 7f40ed117fc7..a7a9136198fd 100644 --- a/net/sctp/sysctl.c +++ b/net/sctp/sysctl.c @@ -84,17 +84,18 @@ static struct ctl_table sctp_table[] = { { /* sentinel */ } }; +/* The following index defines are used in sctp_sysctl_net_register(). + * If you add new items to the sctp_net_table, please ensure that + * the index values of these defines hold the same meaning indicated by + * their macro names when they appear in sctp_net_table. + */ +#define SCTP_RTO_MIN_IDX 0 +#define SCTP_RTO_MAX_IDX 1 +#define SCTP_PF_RETRANS_IDX 2 +#define SCTP_PS_RETRANS_IDX 3 + static struct ctl_table sctp_net_table[] = { - { - .procname = "rto_initial", - .data = &init_net.sctp.rto_initial, - .maxlen = sizeof(unsigned int), - .mode = 0644, - .proc_handler = proc_dointvec_minmax, - .extra1 = SYSCTL_ONE, - .extra2 = &timer_max - }, - { + [SCTP_RTO_MIN_IDX] = { .procname = "rto_min", .data = &init_net.sctp.rto_min, .maxlen = sizeof(unsigned int), @@ -103,7 +104,7 @@ static struct ctl_table sctp_net_table[] = { .extra1 = SYSCTL_ONE, .extra2 = &init_net.sctp.rto_max }, - { + [SCTP_RTO_MAX_IDX] = { .procname = "rto_max", .data = &init_net.sctp.rto_max, .maxlen = sizeof(unsigned int), @@ -112,6 +113,33 @@ static struct ctl_table sctp_net_table[] = { .extra1 = &init_net.sctp.rto_min, .extra2 = &timer_max }, + [SCTP_PF_RETRANS_IDX] = { + .procname = "pf_retrans", + .data = &init_net.sctp.pf_retrans, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec_minmax, + .extra1 = SYSCTL_ZERO, + .extra2 = &init_net.sctp.ps_retrans, + }, + [SCTP_PS_RETRANS_IDX] = { + .procname = "ps_retrans", + .data = &init_net.sctp.ps_retrans, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec_minmax, + .extra1 = &init_net.sctp.pf_retrans, + .extra2 = &ps_retrans_max, + }, + { + .procname = "rto_initial", + .data = &init_net.sctp.rto_initial, + .maxlen = sizeof(unsigned int), + .mode = 0644, + .proc_handler = proc_dointvec_minmax, + .extra1 = SYSCTL_ONE, + .extra2 = &timer_max + }, { .procname = "rto_alpha_exp_divisor", .data = &init_net.sctp.rto_alpha, @@ -207,24 +235,6 @@ static struct ctl_table sctp_net_table[] = { .extra1 = SYSCTL_ONE, .extra2 = SYSCTL_INT_MAX, }, - { - .procname = "pf_retrans", - .data = &init_net.sctp.pf_retrans, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec_minmax, - .extra1 = SYSCTL_ZERO, - .extra2 = &init_net.sctp.ps_retrans, - }, - { - .procname = "ps_retrans", - .data = &init_net.sctp.ps_retrans, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec_minmax, - .extra1 = &init_net.sctp.pf_retrans, - .extra2 = &ps_retrans_max, - }, { .procname = "sndbuf_policy", .data = &init_net.sctp.sndbuf_policy, @@ -597,6 +607,11 @@ int sctp_sysctl_net_register(struct net *net) for (i = 0; table[i].data; i++) table[i].data += (char *)(&net->sctp) - (char *)&init_net.sctp; + table[SCTP_RTO_MIN_IDX].extra2 = &net->sctp.rto_max; + table[SCTP_RTO_MAX_IDX].extra1 = &net->sctp.rto_min; + table[SCTP_PF_RETRANS_IDX].extra2 = &net->sctp.ps_retrans; + table[SCTP_PS_RETRANS_IDX].extra1 = &net->sctp.pf_retrans; + net->sctp.sysctl_header = register_net_sysctl(net, "net/sctp", table); if (net->sctp.sysctl_header == NULL) { kfree(table); From 80a464d83f08cac5052c7f857fc067b3fe88071d Mon Sep 17 00:00:00 2001 From: Xu Panda Date: Fri, 9 Dec 2022 15:38:59 +0800 Subject: [PATCH 1906/2002] net: hns3: use strscpy() to instead of strncpy() The implementation of strscpy() is more robust and safer. That's now the recommended way to copy NUL terminated strings. Signed-off-by: Xu Panda Signed-off-by: Yang Yang Reviewed-by: Jiri Pirko Link: https://lore.kernel.org/r/202212091538591375035@zte.com.cn Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/hisilicon/hns3/hns3_ethtool.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3_ethtool.c b/drivers/net/ethernet/hisilicon/hns3/hns3_ethtool.c index cdf76fb58d45..55306fe8a540 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3_ethtool.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3_ethtool.c @@ -639,13 +639,11 @@ static void hns3_get_drvinfo(struct net_device *netdev, return; } - strncpy(drvinfo->driver, dev_driver_string(&h->pdev->dev), + strscpy(drvinfo->driver, dev_driver_string(&h->pdev->dev), sizeof(drvinfo->driver)); - drvinfo->driver[sizeof(drvinfo->driver) - 1] = '\0'; - strncpy(drvinfo->bus_info, pci_name(h->pdev), + strscpy(drvinfo->bus_info, pci_name(h->pdev), sizeof(drvinfo->bus_info)); - drvinfo->bus_info[ETHTOOL_BUSINFO_LEN - 1] = '\0'; fw_version = priv->ae_handle->ae_algo->ops->get_fw_version(h); From 4e426e2534ce2811380ef09ee2a050779d1b5a29 Mon Sep 17 00:00:00 2001 From: Taras Chornyi Date: Fri, 9 Dec 2022 17:45:21 +0200 Subject: [PATCH 1907/2002] MAINTAINERS: Update email address for Marvell Prestera Ethernet Switch driver Taras's Marvell email account will be shut down soon so change it to Plvision. Signed-off-by: Taras Chornyi Signed-off-by: Vadym Kochan Link: https://lore.kernel.org/r/20221209154521.1246881-1-vadym.kochan@plvision.eu Signed-off-by: Jakub Kicinski --- MAINTAINERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MAINTAINERS b/MAINTAINERS index 71f4f8776779..574519bc3036 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -12366,7 +12366,7 @@ F: Documentation/networking/device_drivers/ethernet/marvell/octeontx2.rst F: drivers/net/ethernet/marvell/octeontx2/af/ MARVELL PRESTERA ETHERNET SWITCH DRIVER -M: Taras Chornyi +M: Taras Chornyi S: Supported W: https://github.com/Marvell-switching/switchdev-prestera F: drivers/net/ethernet/marvell/prestera/ From 15eb1621762134bd3a0f81020359b0c7745d1080 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Fri, 9 Dec 2022 11:15:52 -0600 Subject: [PATCH 1908/2002] dt-bindings: net: Convert Socionext NetSec Ethernet to DT schema Convert the Socionext NetSec Ethernet binding to DT schema format. Signed-off-by: Rob Herring Acked-by: Jassi Brar Link: https://lore.kernel.org/r/20221209171553.3350583-1-robh@kernel.org Signed-off-by: Jakub Kicinski --- .../net/socionext,synquacer-netsec.yaml | 73 +++++++++++++++++++ .../bindings/net/socionext-netsec.txt | 56 -------------- MAINTAINERS | 2 +- 3 files changed, 74 insertions(+), 57 deletions(-) create mode 100644 Documentation/devicetree/bindings/net/socionext,synquacer-netsec.yaml delete mode 100644 Documentation/devicetree/bindings/net/socionext-netsec.txt diff --git a/Documentation/devicetree/bindings/net/socionext,synquacer-netsec.yaml b/Documentation/devicetree/bindings/net/socionext,synquacer-netsec.yaml new file mode 100644 index 000000000000..a65e6aa215a7 --- /dev/null +++ b/Documentation/devicetree/bindings/net/socionext,synquacer-netsec.yaml @@ -0,0 +1,73 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/net/socionext,synquacer-netsec.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Socionext NetSec Ethernet Controller IP + +maintainers: + - Jassi Brar + - Ilias Apalodimas + +allOf: + - $ref: ethernet-controller.yaml# + +properties: + compatible: + const: socionext,synquacer-netsec + + reg: + items: + - description: control register area + - description: EEPROM holding the MAC address and microengine firmware + + clocks: + maxItems: 1 + + clock-names: + const: phy_ref_clk + + dma-coherent: true + + interrupts: + maxItems: 1 + + mdio: + $ref: mdio.yaml# + +required: + - compatible + - reg + - clocks + - clock-names + - interrupts + - mdio + +unevaluatedProperties: false + +examples: + - | + #include + + ethernet@522d0000 { + compatible = "socionext,synquacer-netsec"; + reg = <0x522d0000 0x10000>, <0x10000000 0x10000>; + interrupts = ; + clocks = <&clk_netsec>; + clock-names = "phy_ref_clk"; + phy-mode = "rgmii"; + max-speed = <1000>; + max-frame-size = <9000>; + phy-handle = <&phy1>; + + mdio { + #address-cells = <1>; + #size-cells = <0>; + phy1: ethernet-phy@1 { + compatible = "ethernet-phy-ieee802.3-c22"; + reg = <1>; + }; + }; + }; +... diff --git a/Documentation/devicetree/bindings/net/socionext-netsec.txt b/Documentation/devicetree/bindings/net/socionext-netsec.txt deleted file mode 100644 index a3c1dffaa4bb..000000000000 --- a/Documentation/devicetree/bindings/net/socionext-netsec.txt +++ /dev/null @@ -1,56 +0,0 @@ -* Socionext NetSec Ethernet Controller IP - -Required properties: -- compatible: Should be "socionext,synquacer-netsec" -- reg: Address and length of the control register area, followed by the - address and length of the EEPROM holding the MAC address and - microengine firmware -- interrupts: Should contain ethernet controller interrupt -- clocks: phandle to the PHY reference clock -- clock-names: Should be "phy_ref_clk" -- phy-mode: See ethernet.txt file in the same directory -- phy-handle: See ethernet.txt in the same directory. - -- mdio device tree subnode: When the Netsec has a phy connected to its local - mdio, there must be device tree subnode with the following - required properties: - - - #address-cells: Must be <1>. - - #size-cells: Must be <0>. - - For each phy on the mdio bus, there must be a node with the following - fields: - - compatible: Refer to phy.txt - - reg: phy id used to communicate to phy. - -Optional properties: (See ethernet.txt file in the same directory) -- dma-coherent: Boolean property, must only be present if memory - accesses performed by the device are cache coherent. -- max-speed: See ethernet.txt in the same directory. -- max-frame-size: See ethernet.txt in the same directory. - -The MAC address will be determined using the optional properties -defined in ethernet.txt. The 'phy-mode' property is required, but may -be set to the empty string if the PHY configuration is programmed by -the firmware or set by hardware straps, and needs to be preserved. - -Example: - eth0: ethernet@522d0000 { - compatible = "socionext,synquacer-netsec"; - reg = <0 0x522d0000 0x0 0x10000>, <0 0x10000000 0x0 0x10000>; - interrupts = ; - clocks = <&clk_netsec>; - clock-names = "phy_ref_clk"; - phy-mode = "rgmii"; - max-speed = <1000>; - max-frame-size = <9000>; - phy-handle = <&phy1>; - - mdio { - #address-cells = <1>; - #size-cells = <0>; - phy1: ethernet-phy@1 { - compatible = "ethernet-phy-ieee802.3-c22"; - reg = <1>; - }; - }; diff --git a/MAINTAINERS b/MAINTAINERS index 574519bc3036..0fbe5135490d 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -19066,7 +19066,7 @@ M: Jassi Brar M: Ilias Apalodimas L: netdev@vger.kernel.org S: Maintained -F: Documentation/devicetree/bindings/net/socionext-netsec.txt +F: Documentation/devicetree/bindings/net/socionext,synquacer-netsec.yaml F: drivers/net/ethernet/socionext/netsec.c SOCIONEXT (SNI) Synquacer SPI DRIVER From 4e81462a45ce7d758eb29ec60126f5793593c6f7 Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Fri, 9 Dec 2022 14:05:18 -0800 Subject: [PATCH 1909/2002] MAINTAINERS: Update NXP FEC maintainer Emails to Joakim Zhang bounce, update the list of maintainers per feedback from Clark Wang and designate Wei Fang as the primary maintainer. Signed-off-by: Florian Fainelli Signed-off-by: Jakub Kicinski --- MAINTAINERS | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/MAINTAINERS b/MAINTAINERS index 1daadaa4d48b..78d8928741ed 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -8183,7 +8183,10 @@ S: Maintained F: drivers/i2c/busses/i2c-cpm.c FREESCALE IMX / MXC FEC DRIVER -M: Joakim Zhang +M: Wei Fang +R: Shenwei Wang +R: Clark Wang +R: NXP Linux Team L: netdev@vger.kernel.org S: Maintained F: Documentation/devicetree/bindings/net/fsl,fec.yaml From fb21cad28489137a384c25b045d1c5e178f6610b Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Fri, 9 Dec 2022 14:05:19 -0800 Subject: [PATCH 1910/2002] dt-bindings: FEC/i.MX DWMAC and INTMUX maintainer Emails to Joakim Zhang bounce, add Shawn Guo (i.MX architecture maintainer) and the NXP Linux Team exploder email as well as well Wei Wang for FEC and Clark Wang for DWMAC. Acked-by: Krzysztof Kozlowski Signed-off-by: Florian Fainelli Signed-off-by: Jakub Kicinski --- .../devicetree/bindings/interrupt-controller/fsl,intmux.yaml | 3 ++- Documentation/devicetree/bindings/net/fsl,fec.yaml | 4 +++- Documentation/devicetree/bindings/net/nxp,dwmac-imx.yaml | 4 +++- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/Documentation/devicetree/bindings/interrupt-controller/fsl,intmux.yaml b/Documentation/devicetree/bindings/interrupt-controller/fsl,intmux.yaml index 1d6e0f64a807..985bfa4f6fda 100644 --- a/Documentation/devicetree/bindings/interrupt-controller/fsl,intmux.yaml +++ b/Documentation/devicetree/bindings/interrupt-controller/fsl,intmux.yaml @@ -7,7 +7,8 @@ $schema: http://devicetree.org/meta-schemas/core.yaml# title: Freescale INTMUX interrupt multiplexer maintainers: - - Joakim Zhang + - Shawn Guo + - NXP Linux Team properties: compatible: diff --git a/Documentation/devicetree/bindings/net/fsl,fec.yaml b/Documentation/devicetree/bindings/net/fsl,fec.yaml index e0f376f7e274..77e5f32cb62f 100644 --- a/Documentation/devicetree/bindings/net/fsl,fec.yaml +++ b/Documentation/devicetree/bindings/net/fsl,fec.yaml @@ -7,7 +7,9 @@ $schema: http://devicetree.org/meta-schemas/core.yaml# title: Freescale Fast Ethernet Controller (FEC) maintainers: - - Joakim Zhang + - Shawn Guo + - Wei Fang + - NXP Linux Team allOf: - $ref: ethernet-controller.yaml# diff --git a/Documentation/devicetree/bindings/net/nxp,dwmac-imx.yaml b/Documentation/devicetree/bindings/net/nxp,dwmac-imx.yaml index 4c155441acbf..1857cb4b7139 100644 --- a/Documentation/devicetree/bindings/net/nxp,dwmac-imx.yaml +++ b/Documentation/devicetree/bindings/net/nxp,dwmac-imx.yaml @@ -7,7 +7,9 @@ $schema: http://devicetree.org/meta-schemas/core.yaml# title: NXP i.MX8 DWMAC glue layer maintainers: - - Joakim Zhang + - Clark Wang + - Shawn Guo + - NXP Linux Team # We need a select here so we don't match all nodes with 'snps,dwmac' select: From e0fe1123ab2b07d2cd5475660bd0b4e6993ffaa7 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Fri, 9 Dec 2022 16:28:07 -0800 Subject: [PATCH 1911/2002] mptcp: netlink: fix some error return code Fix to return negative error code -EINVAL from some error handling case instead of 0, as done elsewhere in those functions. Fixes: 9ab4807c84a4 ("mptcp: netlink: Add MPTCP_PM_CMD_ANNOUNCE") Fixes: 702c2f646d42 ("mptcp: netlink: allow userspace-driven subflow establishment") Cc: stable@vger.kernel.org Reviewed-by: Matthieu Baerts Signed-off-by: Wei Yongjun Signed-off-by: Mat Martineau Signed-off-by: Jakub Kicinski --- net/mptcp/pm_userspace.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/net/mptcp/pm_userspace.c b/net/mptcp/pm_userspace.c index 9e82250cbb70..0430415357ba 100644 --- a/net/mptcp/pm_userspace.c +++ b/net/mptcp/pm_userspace.c @@ -156,6 +156,7 @@ int mptcp_nl_cmd_announce(struct sk_buff *skb, struct genl_info *info) if (addr_val.addr.id == 0 || !(addr_val.flags & MPTCP_PM_ADDR_FLAG_SIGNAL)) { GENL_SET_ERR_MSG(info, "invalid addr id or flags"); + err = -EINVAL; goto announce_err; } @@ -282,6 +283,7 @@ int mptcp_nl_cmd_sf_create(struct sk_buff *skb, struct genl_info *info) if (addr_l.id == 0) { NL_SET_ERR_MSG_ATTR(info->extack, laddr, "missing local addr id"); + err = -EINVAL; goto create_err; } @@ -395,11 +397,13 @@ int mptcp_nl_cmd_sf_destroy(struct sk_buff *skb, struct genl_info *info) if (addr_l.family != addr_r.family) { GENL_SET_ERR_MSG(info, "address families do not match"); + err = -EINVAL; goto destroy_err; } if (!addr_l.port || !addr_r.port) { GENL_SET_ERR_MSG(info, "missing local or remote port"); + err = -EINVAL; goto destroy_err; } From 3fff88186f047627bb128d65155f42517f8e448f Mon Sep 17 00:00:00 2001 From: Matthieu Baerts Date: Fri, 9 Dec 2022 16:28:08 -0800 Subject: [PATCH 1912/2002] mptcp: remove MPTCP 'ifdef' in TCP SYN cookies To ease the maintenance, it is often recommended to avoid having #ifdef preprocessor conditions. Here the section related to CONFIG_MPTCP was quite short but the next commit needs to add more code around. It is then cleaner to move specific MPTCP code to functions located in net/mptcp directory. Now that mptcp_subflow_request_sock_ops structure can be static, it can also be marked as "read only after init". Suggested-by: Paolo Abeni Reviewed-by: Mat Martineau Cc: stable@vger.kernel.org Signed-off-by: Matthieu Baerts Signed-off-by: Mat Martineau Signed-off-by: Jakub Kicinski --- include/net/mptcp.h | 12 ++++++++++-- net/ipv4/syncookies.c | 7 +++---- net/mptcp/subflow.c | 12 +++++++++++- 3 files changed, 24 insertions(+), 7 deletions(-) diff --git a/include/net/mptcp.h b/include/net/mptcp.h index 412479ebf5ad..3c5c68618fcc 100644 --- a/include/net/mptcp.h +++ b/include/net/mptcp.h @@ -97,8 +97,6 @@ struct mptcp_out_options { }; #ifdef CONFIG_MPTCP -extern struct request_sock_ops mptcp_subflow_request_sock_ops; - void mptcp_init(void); static inline bool sk_is_mptcp(const struct sock *sk) @@ -188,6 +186,9 @@ void mptcp_seq_show(struct seq_file *seq); int mptcp_subflow_init_cookie_req(struct request_sock *req, const struct sock *sk_listener, struct sk_buff *skb); +struct request_sock *mptcp_subflow_reqsk_alloc(const struct request_sock_ops *ops, + struct sock *sk_listener, + bool attach_listener); __be32 mptcp_get_reset_option(const struct sk_buff *skb); @@ -274,6 +275,13 @@ static inline int mptcp_subflow_init_cookie_req(struct request_sock *req, return 0; /* TCP fallback */ } +static inline struct request_sock *mptcp_subflow_reqsk_alloc(const struct request_sock_ops *ops, + struct sock *sk_listener, + bool attach_listener) +{ + return NULL; +} + static inline __be32 mptcp_reset_option(const struct sk_buff *skb) { return htonl(0u); } #endif /* CONFIG_MPTCP */ diff --git a/net/ipv4/syncookies.c b/net/ipv4/syncookies.c index 942d2dfa1115..26fb97d1d4d9 100644 --- a/net/ipv4/syncookies.c +++ b/net/ipv4/syncookies.c @@ -288,12 +288,11 @@ struct request_sock *cookie_tcp_reqsk_alloc(const struct request_sock_ops *ops, struct tcp_request_sock *treq; struct request_sock *req; -#ifdef CONFIG_MPTCP if (sk_is_mptcp(sk)) - ops = &mptcp_subflow_request_sock_ops; -#endif + req = mptcp_subflow_reqsk_alloc(ops, sk, false); + else + req = inet_reqsk_alloc(ops, sk, false); - req = inet_reqsk_alloc(ops, sk, false); if (!req) return NULL; diff --git a/net/mptcp/subflow.c b/net/mptcp/subflow.c index 2159b5f9988f..3f670f2d5c5c 100644 --- a/net/mptcp/subflow.c +++ b/net/mptcp/subflow.c @@ -529,7 +529,7 @@ static int subflow_v6_rebuild_header(struct sock *sk) } #endif -struct request_sock_ops mptcp_subflow_request_sock_ops; +static struct request_sock_ops mptcp_subflow_request_sock_ops __ro_after_init; static struct tcp_request_sock_ops subflow_request_sock_ipv4_ops __ro_after_init; static int subflow_v4_conn_request(struct sock *sk, struct sk_buff *skb) @@ -582,6 +582,16 @@ drop: } #endif +struct request_sock *mptcp_subflow_reqsk_alloc(const struct request_sock_ops *ops, + struct sock *sk_listener, + bool attach_listener) +{ + ops = &mptcp_subflow_request_sock_ops; + + return inet_reqsk_alloc(ops, sk_listener, attach_listener); +} +EXPORT_SYMBOL(mptcp_subflow_reqsk_alloc); + /* validate hmac received in third ACK */ static bool subflow_hmac_valid(const struct request_sock *req, const struct mptcp_options_received *mp_opt) From 34b21d1ddc8ace77a8fa35c1b1e06377209e0dae Mon Sep 17 00:00:00 2001 From: Matthieu Baerts Date: Fri, 9 Dec 2022 16:28:09 -0800 Subject: [PATCH 1913/2002] mptcp: dedicated request sock for subflow in v6 tcp_request_sock_ops structure is specific to IPv4. It should then not be used with MPTCP subflows on top of IPv6. For example, it contains the 'family' field, initialised to AF_INET. This 'family' field is used by TCP FastOpen code to generate the cookie but also by TCP Metrics, SELinux and SYN Cookies. Using the wrong family will not lead to crashes but displaying/using/checking wrong things. Note that 'send_reset' callback from request_sock_ops structure is used in some error paths. It is then also important to use the correct one for IPv4 or IPv6. The slab name can also be different in IPv4 and IPv6, it will be used when printing some log messages. The slab pointer will anyway be the same because the object size is the same for both v4 and v6. A BUILD_BUG_ON() has also been added to make sure this size is the same. Fixes: cec37a6e41aa ("mptcp: Handle MP_CAPABLE options for outgoing connections") Reviewed-by: Mat Martineau Cc: stable@vger.kernel.org Signed-off-by: Matthieu Baerts Signed-off-by: Mat Martineau Signed-off-by: Jakub Kicinski --- net/mptcp/subflow.c | 34 ++++++++++++++++++++++++++-------- 1 file changed, 26 insertions(+), 8 deletions(-) diff --git a/net/mptcp/subflow.c b/net/mptcp/subflow.c index 3f670f2d5c5c..30524dd7d0ec 100644 --- a/net/mptcp/subflow.c +++ b/net/mptcp/subflow.c @@ -529,7 +529,7 @@ static int subflow_v6_rebuild_header(struct sock *sk) } #endif -static struct request_sock_ops mptcp_subflow_request_sock_ops __ro_after_init; +static struct request_sock_ops mptcp_subflow_v4_request_sock_ops __ro_after_init; static struct tcp_request_sock_ops subflow_request_sock_ipv4_ops __ro_after_init; static int subflow_v4_conn_request(struct sock *sk, struct sk_buff *skb) @@ -542,7 +542,7 @@ static int subflow_v4_conn_request(struct sock *sk, struct sk_buff *skb) if (skb_rtable(skb)->rt_flags & (RTCF_BROADCAST | RTCF_MULTICAST)) goto drop; - return tcp_conn_request(&mptcp_subflow_request_sock_ops, + return tcp_conn_request(&mptcp_subflow_v4_request_sock_ops, &subflow_request_sock_ipv4_ops, sk, skb); drop: @@ -551,6 +551,7 @@ drop: } #if IS_ENABLED(CONFIG_MPTCP_IPV6) +static struct request_sock_ops mptcp_subflow_v6_request_sock_ops __ro_after_init; static struct tcp_request_sock_ops subflow_request_sock_ipv6_ops __ro_after_init; static struct inet_connection_sock_af_ops subflow_v6_specific __ro_after_init; static struct inet_connection_sock_af_ops subflow_v6m_specific __ro_after_init; @@ -573,7 +574,7 @@ static int subflow_v6_conn_request(struct sock *sk, struct sk_buff *skb) return 0; } - return tcp_conn_request(&mptcp_subflow_request_sock_ops, + return tcp_conn_request(&mptcp_subflow_v6_request_sock_ops, &subflow_request_sock_ipv6_ops, sk, skb); drop: @@ -586,7 +587,12 @@ struct request_sock *mptcp_subflow_reqsk_alloc(const struct request_sock_ops *op struct sock *sk_listener, bool attach_listener) { - ops = &mptcp_subflow_request_sock_ops; + if (ops->family == AF_INET) + ops = &mptcp_subflow_v4_request_sock_ops; +#if IS_ENABLED(CONFIG_MPTCP_IPV6) + else if (ops->family == AF_INET6) + ops = &mptcp_subflow_v6_request_sock_ops; +#endif return inet_reqsk_alloc(ops, sk_listener, attach_listener); } @@ -1914,7 +1920,6 @@ static struct tcp_ulp_ops subflow_ulp_ops __read_mostly = { static int subflow_ops_init(struct request_sock_ops *subflow_ops) { subflow_ops->obj_size = sizeof(struct mptcp_subflow_request_sock); - subflow_ops->slab_name = "request_sock_subflow"; subflow_ops->slab = kmem_cache_create(subflow_ops->slab_name, subflow_ops->obj_size, 0, @@ -1931,9 +1936,10 @@ static int subflow_ops_init(struct request_sock_ops *subflow_ops) void __init mptcp_subflow_init(void) { - mptcp_subflow_request_sock_ops = tcp_request_sock_ops; - if (subflow_ops_init(&mptcp_subflow_request_sock_ops) != 0) - panic("MPTCP: failed to init subflow request sock ops\n"); + mptcp_subflow_v4_request_sock_ops = tcp_request_sock_ops; + mptcp_subflow_v4_request_sock_ops.slab_name = "request_sock_subflow_v4"; + if (subflow_ops_init(&mptcp_subflow_v4_request_sock_ops) != 0) + panic("MPTCP: failed to init subflow v4 request sock ops\n"); subflow_request_sock_ipv4_ops = tcp_request_sock_ipv4_ops; subflow_request_sock_ipv4_ops.route_req = subflow_v4_route_req; @@ -1948,6 +1954,18 @@ void __init mptcp_subflow_init(void) tcp_prot_override.release_cb = tcp_release_cb_override; #if IS_ENABLED(CONFIG_MPTCP_IPV6) + /* In struct mptcp_subflow_request_sock, we assume the TCP request sock + * structures for v4 and v6 have the same size. It should not changed in + * the future but better to make sure to be warned if it is no longer + * the case. + */ + BUILD_BUG_ON(sizeof(struct tcp_request_sock) != sizeof(struct tcp6_request_sock)); + + mptcp_subflow_v6_request_sock_ops = tcp6_request_sock_ops; + mptcp_subflow_v6_request_sock_ops.slab_name = "request_sock_subflow_v6"; + if (subflow_ops_init(&mptcp_subflow_v6_request_sock_ops) != 0) + panic("MPTCP: failed to init subflow v6 request sock ops\n"); + subflow_request_sock_ipv6_ops = tcp_request_sock_ipv6_ops; subflow_request_sock_ipv6_ops.route_req = subflow_v6_route_req; From d3295fee3c756ece33ac0d935e172e68c0a4161b Mon Sep 17 00:00:00 2001 From: Matthieu Baerts Date: Fri, 9 Dec 2022 16:28:10 -0800 Subject: [PATCH 1914/2002] mptcp: use proper req destructor for IPv6 Before, only the destructor from TCP request sock in IPv4 was called even if the subflow was IPv6. It is important to use the right destructor to avoid memory leaks with some advanced IPv6 features, e.g. when the request socks contain specific IPv6 options. Fixes: 79c0949e9a09 ("mptcp: Add key generation and token tree") Reviewed-by: Mat Martineau Cc: stable@vger.kernel.org Signed-off-by: Matthieu Baerts Signed-off-by: Mat Martineau Signed-off-by: Jakub Kicinski --- net/mptcp/subflow.c | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/net/mptcp/subflow.c b/net/mptcp/subflow.c index 30524dd7d0ec..613f515fedf0 100644 --- a/net/mptcp/subflow.c +++ b/net/mptcp/subflow.c @@ -45,7 +45,6 @@ static void subflow_req_destructor(struct request_sock *req) sock_put((struct sock *)subflow_req->msk); mptcp_token_destroy_request(req); - tcp_request_sock_ops.destructor(req); } static void subflow_generate_hmac(u64 key1, u64 key2, u32 nonce1, u32 nonce2, @@ -550,6 +549,12 @@ drop: return 0; } +static void subflow_v4_req_destructor(struct request_sock *req) +{ + subflow_req_destructor(req); + tcp_request_sock_ops.destructor(req); +} + #if IS_ENABLED(CONFIG_MPTCP_IPV6) static struct request_sock_ops mptcp_subflow_v6_request_sock_ops __ro_after_init; static struct tcp_request_sock_ops subflow_request_sock_ipv6_ops __ro_after_init; @@ -581,6 +586,12 @@ drop: tcp_listendrop(sk); return 0; /* don't send reset */ } + +static void subflow_v6_req_destructor(struct request_sock *req) +{ + subflow_req_destructor(req); + tcp6_request_sock_ops.destructor(req); +} #endif struct request_sock *mptcp_subflow_reqsk_alloc(const struct request_sock_ops *ops, @@ -1929,8 +1940,6 @@ static int subflow_ops_init(struct request_sock_ops *subflow_ops) if (!subflow_ops->slab) return -ENOMEM; - subflow_ops->destructor = subflow_req_destructor; - return 0; } @@ -1938,6 +1947,8 @@ void __init mptcp_subflow_init(void) { mptcp_subflow_v4_request_sock_ops = tcp_request_sock_ops; mptcp_subflow_v4_request_sock_ops.slab_name = "request_sock_subflow_v4"; + mptcp_subflow_v4_request_sock_ops.destructor = subflow_v4_req_destructor; + if (subflow_ops_init(&mptcp_subflow_v4_request_sock_ops) != 0) panic("MPTCP: failed to init subflow v4 request sock ops\n"); @@ -1963,6 +1974,8 @@ void __init mptcp_subflow_init(void) mptcp_subflow_v6_request_sock_ops = tcp6_request_sock_ops; mptcp_subflow_v6_request_sock_ops.slab_name = "request_sock_subflow_v6"; + mptcp_subflow_v6_request_sock_ops.destructor = subflow_v6_req_destructor; + if (subflow_ops_init(&mptcp_subflow_v6_request_sock_ops) != 0) panic("MPTCP: failed to init subflow v6 request sock ops\n"); From f9c5cbd52c62f2fa9d2dfbe1af8fb00e5056aefe Mon Sep 17 00:00:00 2001 From: Chethan Tumkur Narayan Date: Mon, 3 Oct 2022 12:18:36 +0530 Subject: [PATCH 1915/2002] btusb: Avoid reset of ISOC endpoint alt settings to zero In case of suspend/resume and HCI_RESET (BT On and Off), ISOC endpoint set to alt setting 0 when no SCO connection exists. This patch shall avoid resetting of ISOC endpoint to alt setting to 0. Signed-off-by: Chethan Tumkur Narayan Signed-off-by: Kiran K Signed-off-by: Luiz Augusto von Dentz --- drivers/bluetooth/btusb.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index f05018988a17..3b4ae7905fd4 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -1981,10 +1981,11 @@ static void btusb_work(struct work_struct *work) if (btusb_switch_alt_setting(hdev, new_alts) < 0) bt_dev_err(hdev, "set USB alt:(%d) failed!", new_alts); } else { - clear_bit(BTUSB_ISOC_RUNNING, &data->flags); usb_kill_anchored_urbs(&data->isoc_anchor); - __set_isoc_interface(hdev, 0); + if (test_and_clear_bit(BTUSB_ISOC_RUNNING, &data->flags)) + __set_isoc_interface(hdev, 0); + if (test_and_clear_bit(BTUSB_DID_ISO_RESUME, &data->flags)) usb_autopm_put_interface(data->isoc ? data->isoc : data->intf); } From c9209b269afd29c55b8be49916725ed469f8c5d9 Mon Sep 17 00:00:00 2001 From: Archie Pusaka Date: Thu, 6 Oct 2022 17:09:31 +0800 Subject: [PATCH 1916/2002] Bluetooth: btusb: Introduce generic USB reset On cmd_timeout with no reset_gpio, reset the USB port as a last resort. This patch changes the behavior of btusb_intel_cmd_timeout and btusb_rtl_cmd_timeout. Signed-off-by: Archie Pusaka Reviewed-by: Abhishek Pandit-Subedi Reviewed-by: Ying Hsu Signed-off-by: Luiz Augusto von Dentz --- drivers/bluetooth/btusb.c | 35 +++++++++++++++++++++++--------- include/net/bluetooth/hci_core.h | 1 + 2 files changed, 26 insertions(+), 10 deletions(-) diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index 3b4ae7905fd4..a9e43425c35f 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -696,6 +696,28 @@ struct btusb_data { unsigned cmd_timeout_cnt; }; +static void btusb_reset(struct hci_dev *hdev) +{ + struct btusb_data *data; + int err; + + if (hdev->reset) { + hdev->reset(hdev); + return; + } + + data = hci_get_drvdata(hdev); + /* This is not an unbalanced PM reference since the device will reset */ + err = usb_autopm_get_interface(data->intf); + if (err) { + bt_dev_err(hdev, "Failed usb_autopm_get_interface: %d", err); + return; + } + + bt_dev_err(hdev, "Resetting usb device."); + usb_queue_reset_device(data->intf); +} + static void btusb_intel_cmd_timeout(struct hci_dev *hdev) { struct btusb_data *data = hci_get_drvdata(hdev); @@ -705,7 +727,7 @@ static void btusb_intel_cmd_timeout(struct hci_dev *hdev) return; if (!reset_gpio) { - bt_dev_err(hdev, "No way to reset. Ignoring and continuing"); + btusb_reset(hdev); return; } @@ -736,7 +758,7 @@ static void btusb_rtl_cmd_timeout(struct hci_dev *hdev) return; if (!reset_gpio) { - bt_dev_err(hdev, "No gpio to reset Realtek device, ignoring"); + btusb_reset(hdev); return; } @@ -761,7 +783,6 @@ static void btusb_qca_cmd_timeout(struct hci_dev *hdev) { struct btusb_data *data = hci_get_drvdata(hdev); struct gpio_desc *reset_gpio = data->reset_gpio; - int err; if (++data->cmd_timeout_cnt < 5) return; @@ -787,13 +808,7 @@ static void btusb_qca_cmd_timeout(struct hci_dev *hdev) return; } - bt_dev_err(hdev, "Multiple cmd timeouts seen. Resetting usb device."); - /* This is not an unbalanced PM reference since the device will reset */ - err = usb_autopm_get_interface(data->intf); - if (!err) - usb_queue_reset_device(data->intf); - else - bt_dev_err(hdev, "Failed usb_autopm_get_interface with %d", err); + btusb_reset(hdev); } static inline void btusb_free_frags(struct btusb_data *data) diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index c54bc71254af..55a40f5606c3 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -659,6 +659,7 @@ struct hci_dev { int (*set_diag)(struct hci_dev *hdev, bool enable); int (*set_bdaddr)(struct hci_dev *hdev, const bdaddr_t *bdaddr); void (*cmd_timeout)(struct hci_dev *hdev); + void (*reset)(struct hci_dev *hdev); bool (*wakeup)(struct hci_dev *hdev); int (*set_quality_report)(struct hci_dev *hdev, bool enable); int (*get_data_path_id)(struct hci_dev *hdev, __u8 *data_path); From b3fdb8c9789dcb888986c75ef6677d41d40ec83e Mon Sep 17 00:00:00 2001 From: Nicolas Cavallari Date: Wed, 5 Oct 2022 17:06:21 +0200 Subject: [PATCH 1917/2002] Bluetooth: Work around SCO over USB HCI design defect The USB interface between the host and the bluetooth adapter used for SCO packets uses an USB isochronous endpoint with a fragmentation scheme that does not tolerate errors. Except USB isochronous transfers do not provide a reliable stream with guaranteed delivery. (There is no retry on error, see USB spec v2.0 5.6 and 8.5.5.) To fragment a packet, the bluetooth HCI simply splits it in parts and transfer them as-is. The receiver is expected to reconstruct the packet by assuming the first fragment contains the header and parsing its size field. There is no error detection either. If a fragment is lost, the end result is that the kernel is no longer synchronized and will pass malformed data to the upper layers, since it has no way to tell if the first fragment is an actual first fragment or a continuation fragment. Resynchronization can only happen by luck and requires an unbounded amount of time. The typical symptom for a HSP/HFP bluetooth headset is that the microphone stops working and dmesg contains piles of rate-limited "Bluetooth: hci0: SCO packet for unknown connection handle XXXX" errors for an indeterminate amount of time, until the kernel accidentally resynchronize. A workaround is to ask the upper layer to prevalidate the first fragment header. This is not possible with user channels so this workaround is disabled in this case. This problem is the most severe when using an ath3k adapter on an i.MX 6 board, where packet loss occur regularly, possibly because it is an USB1 device connected on an USB2 hub and this is a special case requiring split transactions. Signed-off-by: Nicolas Cavallari Signed-off-by: Luiz Augusto von Dentz --- drivers/bluetooth/btusb.c | 35 +++++++++++++++++++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index a9e43425c35f..80f38991ad6f 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -977,6 +977,34 @@ static int btusb_recv_bulk(struct btusb_data *data, void *buffer, int count) return err; } +static bool btusb_validate_sco_handle(struct hci_dev *hdev, + struct hci_sco_hdr *hdr) +{ + __u16 handle; + + if (hci_dev_test_flag(hdev, HCI_USER_CHANNEL)) + // Can't validate, userspace controls everything. + return true; + + /* + * USB isochronous transfers are not designed to be reliable and may + * lose fragments. When this happens, the next first fragment + * encountered might actually be a continuation fragment. + * Validate the handle to detect it and drop it, or else the upper + * layer will get garbage for a while. + */ + + handle = hci_handle(__le16_to_cpu(hdr->handle)); + + switch (hci_conn_lookup_type(hdev, handle)) { + case SCO_LINK: + case ESCO_LINK: + return true; + default: + return false; + } +} + static int btusb_recv_isoc(struct btusb_data *data, void *buffer, int count) { struct sk_buff *skb; @@ -1009,9 +1037,12 @@ static int btusb_recv_isoc(struct btusb_data *data, void *buffer, int count) if (skb->len == HCI_SCO_HDR_SIZE) { /* Complete SCO header */ - hci_skb_expect(skb) = hci_sco_hdr(skb)->dlen; + struct hci_sco_hdr *hdr = hci_sco_hdr(skb); - if (skb_tailroom(skb) < hci_skb_expect(skb)) { + hci_skb_expect(skb) = hdr->dlen; + + if (skb_tailroom(skb) < hci_skb_expect(skb) || + !btusb_validate_sco_handle(data->hdev, hdr)) { kfree_skb(skb); skb = NULL; From e411443c32554b4db157eb49f199d9cfebda2817 Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Fri, 7 Oct 2022 13:25:38 -0700 Subject: [PATCH 1918/2002] Bluetooth: hci_sync: Fix not setting static address This attempts to program the address stored in hdev->static_addr after the init sequence has been complete: @ MGMT Command: Set Static A.. (0x002b) plen 6 Address: C0:55:44:33:22:11 (Static) @ MGMT Event: Command Complete (0x0001) plen 7 Set Static Address (0x002b) plen 4 Status: Success (0x00) Current settings: 0x00008200 Low Energy Static Address @ MGMT Event: New Settings (0x0006) plen 4 Current settings: 0x00008200 Low Energy Static Address < HCI Command: LE Set Random.. (0x08|0x0005) plen 6 Address: C0:55:44:33:22:11 (Static) > HCI Event: Command Complete (0x0e) plen 4 LE Set Random Address (0x08|0x0005) ncmd 1 Status: Success (0x00) @ MGMT Event: Command Complete (0x0001) plen 7 Set Powered (0x0005) plen 4 Status: Success (0x00) Current settings: 0x00008201 Powered Low Energy Static Address @ MGMT Event: New Settings (0x0006) plen 4 Current settings: 0x00008201 Powered Low Energy Static Address Signed-off-by: Luiz Augusto von Dentz Tested-by: Brian Gix --- net/bluetooth/hci_sync.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c index 1fc693122a47..1cd05072dbb7 100644 --- a/net/bluetooth/hci_sync.c +++ b/net/bluetooth/hci_sync.c @@ -3055,6 +3055,7 @@ int hci_update_name_sync(struct hci_dev *hdev) * Enable Authentication * lmp_bredr_capable(Set Fast Connectable -> Set Scan Type -> Set Class -> * Set Name -> Set EIR) + * HCI_FORCE_STATIC_ADDR | BDADDR_ANY && !HCI_BREDR_ENABLED (Set Static Address) */ int hci_powered_update_sync(struct hci_dev *hdev) { @@ -3094,6 +3095,23 @@ int hci_powered_update_sync(struct hci_dev *hdev) hci_update_eir_sync(hdev); } + /* If forcing static address is in use or there is no public + * address use the static address as random address (but skip + * the HCI command if the current random address is already the + * static one. + * + * In case BR/EDR has been disabled on a dual-mode controller + * and a static address has been configured, then use that + * address instead of the public BR/EDR address. + */ + if (hci_dev_test_flag(hdev, HCI_FORCE_STATIC_ADDR) || + (!bacmp(&hdev->bdaddr, BDADDR_ANY) && + !hci_dev_test_flag(hdev, HCI_BREDR_ENABLED))) { + if (bacmp(&hdev->static_addr, BDADDR_ANY)) + return hci_set_random_addr_sync(hdev, + &hdev->static_addr); + } + return 0; } From eeb1aafe97fa6da558157d2eb18cce25878b8656 Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Fri, 7 Oct 2022 18:08:43 -0700 Subject: [PATCH 1919/2002] Bluetooth: hci_sync: Fix not able to set force_static_address force_static_address shall be writable while hdev is initing but is not considered powered yet since the static address is written only when powered. Signed-off-by: Luiz Augusto von Dentz Tested-by: Brian Gix --- net/bluetooth/hci_debugfs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/bluetooth/hci_debugfs.c b/net/bluetooth/hci_debugfs.c index 3f401ec5bb0c..b7f682922a16 100644 --- a/net/bluetooth/hci_debugfs.c +++ b/net/bluetooth/hci_debugfs.c @@ -757,7 +757,7 @@ static ssize_t force_static_address_write(struct file *file, bool enable; int err; - if (test_bit(HCI_UP, &hdev->flags)) + if (hdev_is_powered(hdev)) return -EBUSY; err = kstrtobool_from_user(user_buf, count, &enable); From 97dfaf073f5881c624856ef293be307b6166115c Mon Sep 17 00:00:00 2001 From: Archie Pusaka Date: Wed, 5 Oct 2022 15:09:47 +0800 Subject: [PATCH 1920/2002] Bluetooth: hci_sync: cancel cmd_timer if hci_open failed If a command is already sent, we take care of freeing it, but we also need to cancel the timeout as well. Signed-off-by: Archie Pusaka Reviewed-by: Abhishek Pandit-Subedi Signed-off-by: Luiz Augusto von Dentz --- net/bluetooth/hci_sync.c | 1 + 1 file changed, 1 insertion(+) diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c index 1cd05072dbb7..4debe205d4af 100644 --- a/net/bluetooth/hci_sync.c +++ b/net/bluetooth/hci_sync.c @@ -4721,6 +4721,7 @@ int hci_dev_open_sync(struct hci_dev *hdev) hdev->flush(hdev); if (hdev->sent_cmd) { + cancel_delayed_work_sync(&hdev->cmd_timer); kfree_skb(hdev->sent_cmd); hdev->sent_cmd = NULL; } From dc65b4b0f90a2c5eb37b47cd3a82fcd857a69264 Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Mon, 10 Oct 2022 13:14:37 -0400 Subject: [PATCH 1921/2002] Bluetooth: virtio_bt: fix device removal MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Device removal is clearly out of virtio spec: it attempts to remove unused buffers from a VQ before invoking device reset. To fix, make open/close NOPs and do all cleanup/setup in probe/remove. NB: This is a hacky way to handle this - virtbt_{open,close} as NOP is not really what a driver is supposed to be doing. These are transport enable/disable callbacks from the BT core towards the driver. It maps to a device being enabled/disabled by something like bluetoothd for example. So if disabled, users expect that no resources/queues are in use. It does work with all other transports like USB, SDIO, UART etc. There should be no buffer used if the device is powered off. We also don’t have any USB URBs in-flight if the transport is not active. The way to implement a proper fix would be using vq reset if supported, or even using a full device reset. The cost of the hack is a single skb wasted on an unused bt device. NB2: with this fix in place driver still suffers from a race condition if an interrupt triggers while device is being reset. To fix, in the virtbt_close() callback we should deactivate all interrupts. To be fixed. squashed fixup: bluetooth: virtio_bt: fix an error code in probe() Signed-off-by: Dan Carpenter Reported-by: Hulk Robot Signed-off-by: Yang Yingliang Signed-off-by: Michael S. Tsirkin Message-Id: <20220811080943.198245-1-mst@redhat.com> Signed-off-by: Luiz Augusto von Dentz Tested-by: Igor Skalkin --- drivers/bluetooth/virtio_bt.c | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/drivers/bluetooth/virtio_bt.c b/drivers/bluetooth/virtio_bt.c index fd281d439505..2bbeb65b1cd3 100644 --- a/drivers/bluetooth/virtio_bt.c +++ b/drivers/bluetooth/virtio_bt.c @@ -50,8 +50,11 @@ static int virtbt_add_inbuf(struct virtio_bluetooth *vbt) static int virtbt_open(struct hci_dev *hdev) { - struct virtio_bluetooth *vbt = hci_get_drvdata(hdev); + return 0; +} +static int virtbt_open_vdev(struct virtio_bluetooth *vbt) +{ if (virtbt_add_inbuf(vbt) < 0) return -EIO; @@ -61,7 +64,11 @@ static int virtbt_open(struct hci_dev *hdev) static int virtbt_close(struct hci_dev *hdev) { - struct virtio_bluetooth *vbt = hci_get_drvdata(hdev); + return 0; +} + +static int virtbt_close_vdev(struct virtio_bluetooth *vbt) +{ int i; cancel_work_sync(&vbt->rx); @@ -354,8 +361,15 @@ static int virtbt_probe(struct virtio_device *vdev) goto failed; } + virtio_device_ready(vdev); + err = virtbt_open_vdev(vbt); + if (err) + goto open_failed; + return 0; +open_failed: + hci_free_dev(hdev); failed: vdev->config->del_vqs(vdev); return err; @@ -368,6 +382,7 @@ static void virtbt_remove(struct virtio_device *vdev) hci_unregister_dev(hdev); virtio_reset_device(vdev); + virtbt_close_vdev(vbt); hci_free_dev(hdev); vbt->hdev = NULL; From 5b355944b19011011dd3fd4187444c5ff1d76ad2 Mon Sep 17 00:00:00 2001 From: Hilda Wu Date: Wed, 5 Oct 2022 16:43:30 +0800 Subject: [PATCH 1922/2002] Bluetooth: btrtl: Add btrealtek data struct This patch adds a data structure for btrealtek object, and the definition of vendor behavior flags. It also adds macros to set/test/get the flags. Signed-off-by: Hilda Wu Signed-off-by: Luiz Augusto von Dentz --- drivers/bluetooth/btrtl.h | 21 +++++++++++++++++++++ drivers/bluetooth/btusb.c | 3 +++ 2 files changed, 24 insertions(+) diff --git a/drivers/bluetooth/btrtl.h b/drivers/bluetooth/btrtl.h index 2c441bda390a..ebf0101c959b 100644 --- a/drivers/bluetooth/btrtl.h +++ b/drivers/bluetooth/btrtl.h @@ -47,6 +47,27 @@ struct rtl_vendor_config { struct rtl_vendor_config_entry entry[]; } __packed; +enum { + REALTEK_ALT6_CONTINUOUS_TX_CHIP, + + __REALTEK_NUM_FLAGS, +}; + +struct btrealtek_data { + DECLARE_BITMAP(flags, __REALTEK_NUM_FLAGS); +}; + +#define btrealtek_set_flag(hdev, nr) \ + do { \ + struct btrealtek_data *realtek = hci_get_priv((hdev)); \ + set_bit((nr), realtek->flags); \ + } while (0) + +#define btrealtek_get_flag(hdev) \ + (((struct btrealtek_data *)hci_get_priv(hdev))->flags) + +#define btrealtek_test_flag(hdev, nr) test_bit((nr), btrealtek_get_flag(hdev)) + #if IS_ENABLED(CONFIG_BT_RTL) struct btrtl_device_info *btrtl_initialize(struct hci_dev *hdev, diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index 80f38991ad6f..39a30c36586a 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -3808,6 +3808,9 @@ static int btusb_probe(struct usb_interface *intf, /* Override the rx handlers */ data->recv_event = btusb_recv_event_intel; data->recv_bulk = btusb_recv_bulk_intel; + } else if (id->driver_info & BTUSB_REALTEK) { + /* Allocate extra space for Realtek device */ + priv_size += sizeof(struct btrealtek_data); } data->recv_acl = hci_recv_frame; From a479e71322ced3e663b0baf17a6d250c973454c8 Mon Sep 17 00:00:00 2001 From: Hilda Wu Date: Wed, 5 Oct 2022 16:43:31 +0800 Subject: [PATCH 1923/2002] Bluetooth: btusb: Ignore zero length of USB packets on ALT 6 for specific chip For USB ALT 6 settings some Realtek chips need to transmit mSBC data continuously without the zero length of USB packets. In this commit, create BTUSB_ALT6_CONTINUOUS_TX to manage the behavior. Therefore, create REALTEK_ALT6_CONTINUOUS_TX_CHIP to manage the specific chip model for the behavior. Signed-off-by: Max Chou Signed-off-by: Hilda Wu Signed-off-by: Luiz Augusto von Dentz --- drivers/bluetooth/btrtl.c | 7 +++++++ drivers/bluetooth/btusb.c | 25 +++++++++++++++++++++++-- 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/drivers/bluetooth/btrtl.c b/drivers/bluetooth/btrtl.c index fb52313a1d45..69c3fe649ca7 100644 --- a/drivers/bluetooth/btrtl.c +++ b/drivers/bluetooth/btrtl.c @@ -781,6 +781,13 @@ void btrtl_set_quirks(struct hci_dev *hdev, struct btrtl_device_info *btrtl_dev) case CHIP_ID_8852C: set_bit(HCI_QUIRK_VALID_LE_STATES, &hdev->quirks); set_bit(HCI_QUIRK_WIDEBAND_SPEECH_SUPPORTED, &hdev->quirks); + + /* RTL8852C needs to transmit mSBC data continuously without + * the zero length of USB packets for the ALT 6 supported chips + */ + if (btrtl_dev->project_id == CHIP_ID_8852C) + btrealtek_set_flag(hdev, REALTEK_ALT6_CONTINUOUS_TX_CHIP); + hci_set_aosp_capable(hdev); break; default: diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index 39a30c36586a..1a14447fafa8 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -632,6 +632,7 @@ static const struct dmi_system_id btusb_needs_reset_resume_table[] = { #define BTUSB_TX_WAIT_VND_EVT 13 #define BTUSB_WAKEUP_AUTOSUSPEND 14 #define BTUSB_USE_ALT3_FOR_WBS 15 +#define BTUSB_ALT6_CONTINUOUS_TX 16 struct btusb_data { struct hci_dev *hdev; @@ -1322,11 +1323,17 @@ static void btusb_isoc_complete(struct urb *urb) static inline void __fill_isoc_descriptor_msbc(struct urb *urb, int len, int mtu, struct btusb_data *data) { - int i, offset = 0; + int i = 0, offset = 0; unsigned int interval; BT_DBG("len %d mtu %d", len, mtu); + /* For mSBC ALT 6 settings some chips need to transmit the data + * continuously without the zero length of USB packets. + */ + if (test_bit(BTUSB_ALT6_CONTINUOUS_TX, &data->flags)) + goto ignore_usb_alt6_packet_flow; + /* For mSBC ALT 6 setting the host will send the packet at continuous * flow. As per core spec 5, vol 4, part B, table 2.1. For ALT setting * 6 the HCI PACKET INTERVAL should be 7.5ms for every usb packets. @@ -1346,6 +1353,7 @@ static inline void __fill_isoc_descriptor_msbc(struct urb *urb, int len, urb->iso_frame_desc[i].length = offset; } +ignore_usb_alt6_packet_flow: if (len && i < BTUSB_MAX_ISOC_FRAMES) { urb->iso_frame_desc[i].offset = offset; urb->iso_frame_desc[i].length = len; @@ -2368,6 +2376,19 @@ static int btusb_send_frame_intel(struct hci_dev *hdev, struct sk_buff *skb) return -EILSEQ; } +static int btusb_setup_realtek(struct hci_dev *hdev) +{ + struct btusb_data *data = hci_get_drvdata(hdev); + int ret; + + ret = btrtl_setup_realtek(hdev); + + if (btrealtek_test_flag(data->hdev, REALTEK_ALT6_CONTINUOUS_TX_CHIP)) + set_bit(BTUSB_ALT6_CONTINUOUS_TX, &data->flags); + + return ret; +} + /* UHW CR mapping */ #define MTK_BT_MISC 0x70002510 #define MTK_BT_SUBSYS_RST 0x70002610 @@ -3969,7 +3990,7 @@ static int btusb_probe(struct usb_interface *intf, if (IS_ENABLED(CONFIG_BT_HCIBTUSB_RTL) && (id->driver_info & BTUSB_REALTEK)) { - hdev->setup = btrtl_setup_realtek; + hdev->setup = btusb_setup_realtek; hdev->shutdown = btrtl_shutdown_realtek; hdev->cmd_timeout = btusb_rtl_cmd_timeout; From d11ab690c300becca17223531f9ca4ae29284c52 Mon Sep 17 00:00:00 2001 From: Pauli Virtanen Date: Tue, 11 Oct 2022 22:25:34 +0300 Subject: [PATCH 1924/2002] Bluetooth: hci_conn: use HCI dst_type values also for BIS For ISO BIS related functions in hci_conn.c, make dst_type values be HCI address type values, not ISO socket address type values. This makes it consistent with CIS functions. Signed-off-by: Pauli Virtanen Signed-off-by: Luiz Augusto von Dentz --- net/bluetooth/hci_conn.c | 6 ------ net/bluetooth/iso.c | 6 ++++-- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index a6c12863a253..df914f521c9d 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -2052,12 +2052,6 @@ int hci_pa_create_sync(struct hci_dev *hdev, bdaddr_t *dst, __u8 dst_type, return -ENOMEM; } - /* Convert from ISO socket address type to HCI address type */ - if (dst_type == BDADDR_LE_PUBLIC) - dst_type = ADDR_LE_DEV_PUBLIC; - else - dst_type = ADDR_LE_DEV_RANDOM; - memset(cp, 0, sizeof(*cp)); cp->sid = sid; cp->addr_type = dst_type; diff --git a/net/bluetooth/iso.c b/net/bluetooth/iso.c index 26db929b97c4..e23aabf4e0cf 100644 --- a/net/bluetooth/iso.c +++ b/net/bluetooth/iso.c @@ -270,7 +270,8 @@ static int iso_connect_bis(struct sock *sk) goto done; } - hcon = hci_connect_bis(hdev, &iso_pi(sk)->dst, iso_pi(sk)->dst_type, + hcon = hci_connect_bis(hdev, &iso_pi(sk)->dst, + le_addr_type(iso_pi(sk)->dst_type), &iso_pi(sk)->qos, iso_pi(sk)->base_len, iso_pi(sk)->base); if (IS_ERR(hcon)) { @@ -875,7 +876,8 @@ static int iso_listen_bis(struct sock *sk) hci_dev_lock(hdev); - err = hci_pa_create_sync(hdev, &iso_pi(sk)->dst, iso_pi(sk)->dst_type, + err = hci_pa_create_sync(hdev, &iso_pi(sk)->dst, + le_addr_type(iso_pi(sk)->dst_type), iso_pi(sk)->bc_sid); hci_dev_unlock(hdev); From e9b3e5b8c65733f626a7ee919c4bc895b51d7bb2 Mon Sep 17 00:00:00 2001 From: Zhengping Jiang Date: Thu, 13 Oct 2022 14:45:08 -0700 Subject: [PATCH 1925/2002] Bluetooth: hci_qca: only assign wakeup with serial port support Only assign hdev->wakeup if the serial port supports wakeup. Otherwise it will fall back to the hci_uart_wakeup or the behavior that can be overridden before calling the hci_uart_register_device(). Signed-off-by: Zhengping Jiang Signed-off-by: Luiz Augusto von Dentz --- drivers/bluetooth/hci_qca.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/bluetooth/hci_qca.c b/drivers/bluetooth/hci_qca.c index 8df11016fd51..dacb6f5efd29 100644 --- a/drivers/bluetooth/hci_qca.c +++ b/drivers/bluetooth/hci_qca.c @@ -1765,7 +1765,8 @@ retry: qca_debugfs_init(hdev); hu->hdev->hw_error = qca_hw_error; hu->hdev->cmd_timeout = qca_cmd_timeout; - hu->hdev->wakeup = qca_wakeup; + if (device_can_wakeup(hu->serdev->ctrl->dev.parent)) + hu->hdev->wakeup = qca_wakeup; } else if (ret == -ENOENT) { /* No patch/nvm-config found, run with original fw/config */ set_bit(QCA_ROM_FW, &qca->flags); From 37224a290853a69f7e8e8c2269c9caea6e2bcb4b Mon Sep 17 00:00:00 2001 From: Jiapeng Chong Date: Mon, 17 Oct 2022 13:47:13 +0800 Subject: [PATCH 1926/2002] Bluetooth: Use kzalloc instead of kmalloc/memset Use kzalloc rather than duplicating its implementation, which makes code simple and easy to understand. ./net/bluetooth/hci_conn.c:2038:6-13: WARNING: kzalloc should be used for cp, instead of kmalloc/memset. Link: https://bugzilla.openanolis.cn/show_bug.cgi?id=2406 Reported-by: Abaci Robot Signed-off-by: Jiapeng Chong Signed-off-by: Luiz Augusto von Dentz --- net/bluetooth/hci_conn.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index df914f521c9d..07ddc1ef1aff 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -2046,13 +2046,12 @@ int hci_pa_create_sync(struct hci_dev *hdev, bdaddr_t *dst, __u8 dst_type, if (hci_dev_test_and_set_flag(hdev, HCI_PA_SYNC)) return -EBUSY; - cp = kmalloc(sizeof(*cp), GFP_KERNEL); + cp = kzalloc(sizeof(*cp), GFP_KERNEL); if (!cp) { hci_dev_clear_flag(hdev, HCI_PA_SYNC); return -ENOMEM; } - memset(cp, 0, sizeof(*cp)); cp->sid = sid; cp->addr_type = dst_type; bacpy(&cp->addr, dst); From 0d75da38e060d21f948b3df5f5e349c962cf1ed2 Mon Sep 17 00:00:00 2001 From: Yang Yingliang Date: Thu, 20 Oct 2022 10:16:56 +0800 Subject: [PATCH 1927/2002] Bluetooth: hci_core: fix error handling in hci_register_dev() If hci_register_suspend_notifier() returns error, the hdev and rfkill are leaked. We could disregard the error and print a warning message instead to avoid leaks, as it just means we won't be handing suspend requests. Fixes: 9952d90ea288 ("Bluetooth: Handle PM_SUSPEND_PREPARE and PM_POST_SUSPEND") Signed-off-by: Yang Yingliang Signed-off-by: Luiz Augusto von Dentz --- net/bluetooth/hci_core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index d97fac4f7130..56f8569ace86 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -2660,7 +2660,7 @@ int hci_register_dev(struct hci_dev *hdev) error = hci_register_suspend_notifier(hdev); if (error) - goto err_wqueue; + BT_WARN("register suspend notifier failed error:%d\n", error); queue_work(hdev->req_workqueue, &hdev->power_on); From 3b1c7c00b8c22b3cb79532252c59eb0b287bb86d Mon Sep 17 00:00:00 2001 From: Inga Stotland Date: Fri, 21 Oct 2022 17:48:56 -0700 Subject: [PATCH 1928/2002] Bluetooth: MGMT: Fix error report for ADD_EXT_ADV_PARAMS When validating the parameter length for MGMT_OP_ADD_EXT_ADV_PARAMS command, use the correct op code in error status report: was MGMT_OP_ADD_ADVERTISING, changed to MGMT_OP_ADD_EXT_ADV_PARAMS. Fixes: 12410572833a2 ("Bluetooth: Break add adv into two mgmt commands") Signed-off-by: Inga Stotland Signed-off-by: Luiz Augusto von Dentz --- net/bluetooth/mgmt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index a92e7e485feb..0dd30a3beb77 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -8859,7 +8859,7 @@ static int add_ext_adv_params(struct sock *sk, struct hci_dev *hdev, * extra parameters we don't know about will be ignored in this request. */ if (data_len < MGMT_ADD_EXT_ADV_PARAMS_MIN_SIZE) - return mgmt_cmd_status(sk, hdev->id, MGMT_OP_ADD_ADVERTISING, + return mgmt_cmd_status(sk, hdev->id, MGMT_OP_ADD_EXT_ADV_PARAMS, MGMT_STATUS_INVALID_PARAMS); flags = __le32_to_cpu(cp->flags); From 47c50853bb9c5c35bf5c3eb00b712310ec2969e2 Mon Sep 17 00:00:00 2001 From: Igor Skalkin Date: Mon, 24 Oct 2022 15:40:33 +0200 Subject: [PATCH 1929/2002] virtio_bt: Fix alignment in configuration struct The current version of the configuration structure has unaligned 16-bit fields, but according to the specification [1], access to the configuration space must be aligned. Add a second, aligned version of the configuration structure and a new feature bit indicating that this version is being used. [1] https://docs.oasis-open.org/virtio/virtio/v1.1/virtio-v1.1.pdf Signed-off-by: Igor Skalkin Signed-off-by: Luiz Augusto von Dentz --- drivers/bluetooth/virtio_bt.c | 16 +++++++++++++--- include/uapi/linux/virtio_bt.h | 8 ++++++++ 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/drivers/bluetooth/virtio_bt.c b/drivers/bluetooth/virtio_bt.c index 2bbeb65b1cd3..c570c45d1480 100644 --- a/drivers/bluetooth/virtio_bt.c +++ b/drivers/bluetooth/virtio_bt.c @@ -313,7 +313,12 @@ static int virtbt_probe(struct virtio_device *vdev) if (virtio_has_feature(vdev, VIRTIO_BT_F_VND_HCI)) { __u16 vendor; - virtio_cread(vdev, struct virtio_bt_config, vendor, &vendor); + if (virtio_has_feature(vdev, VIRTIO_BT_F_CONFIG_V2)) + virtio_cread(vdev, struct virtio_bt_config_v2, + vendor, &vendor); + else + virtio_cread(vdev, struct virtio_bt_config, + vendor, &vendor); switch (vendor) { case VIRTIO_BT_CONFIG_VENDOR_ZEPHYR: @@ -346,8 +351,12 @@ static int virtbt_probe(struct virtio_device *vdev) if (virtio_has_feature(vdev, VIRTIO_BT_F_MSFT_EXT)) { __u16 msft_opcode; - virtio_cread(vdev, struct virtio_bt_config, - msft_opcode, &msft_opcode); + if (virtio_has_feature(vdev, VIRTIO_BT_F_CONFIG_V2)) + virtio_cread(vdev, struct virtio_bt_config_v2, + msft_opcode, &msft_opcode); + else + virtio_cread(vdev, struct virtio_bt_config, + msft_opcode, &msft_opcode); hci_set_msft_opcode(hdev, msft_opcode); } @@ -402,6 +411,7 @@ static const unsigned int virtbt_features[] = { VIRTIO_BT_F_VND_HCI, VIRTIO_BT_F_MSFT_EXT, VIRTIO_BT_F_AOSP_EXT, + VIRTIO_BT_F_CONFIG_V2, }; static struct virtio_driver virtbt_driver = { diff --git a/include/uapi/linux/virtio_bt.h b/include/uapi/linux/virtio_bt.h index a7bd48daa9a9..af798f4c9680 100644 --- a/include/uapi/linux/virtio_bt.h +++ b/include/uapi/linux/virtio_bt.h @@ -9,6 +9,7 @@ #define VIRTIO_BT_F_VND_HCI 0 /* Indicates vendor command support */ #define VIRTIO_BT_F_MSFT_EXT 1 /* Indicates MSFT vendor support */ #define VIRTIO_BT_F_AOSP_EXT 2 /* Indicates AOSP vendor support */ +#define VIRTIO_BT_F_CONFIG_V2 3 /* Use second version configuration */ enum virtio_bt_config_type { VIRTIO_BT_CONFIG_TYPE_PRIMARY = 0, @@ -28,4 +29,11 @@ struct virtio_bt_config { __u16 msft_opcode; } __attribute__((packed)); +struct virtio_bt_config_v2 { + __u8 type; + __u8 alignment; + __u16 vendor; + __u16 msft_opcode; +}; + #endif /* _UAPI_LINUX_VIRTIO_BT_H */ From bb1afb2955212d30177b122b13ae636a69abf883 Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Tue, 25 Oct 2022 14:12:57 -0700 Subject: [PATCH 1930/2002] Bluetooth: btusb: Add CONFIG_BT_HCIBTUSB_POLL_SYNC This adds CONFIG_BT_HCIBTUSB_POLL_SYNC which can be used to set the default behavior of Bluetooth USB controller with respect to poll synchronization of its endpoits. Signed-off-by: Luiz Augusto von Dentz Tested-by: Tedd Ho-Jeong An --- drivers/bluetooth/Kconfig | 10 ++++++++++ drivers/bluetooth/btusb.c | 3 +++ 2 files changed, 13 insertions(+) diff --git a/drivers/bluetooth/Kconfig b/drivers/bluetooth/Kconfig index e30707405455..fc36057d546d 100644 --- a/drivers/bluetooth/Kconfig +++ b/drivers/bluetooth/Kconfig @@ -45,6 +45,16 @@ config BT_HCIBTUSB_AUTOSUSPEND This can be overridden by passing btusb.enable_autosuspend=[y|n] on the kernel commandline. +config BT_HCIBTUSB_POLL_SYNC + bool "Enable USB poll_sync for Bluetooth USB devices by default" + depends on BT_HCIBTUSB + help + poll_sync synchronizes the USB data and event endpoints by + prioritizing the later. + + Say Y here to enable USB poll_sync for Bluetooth USB devices by + default. + config BT_HCIBTUSB_BCM bool "Broadcom protocol support" depends on BT_HCIBTUSB diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index 1a14447fafa8..8f34bf195bae 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -32,6 +32,7 @@ static bool disable_scofix; static bool force_scofix; static bool enable_autosuspend = IS_ENABLED(CONFIG_BT_HCIBTUSB_AUTOSUSPEND); +static bool enable_poll_sync = IS_ENABLED(CONFIG_BT_HCIBTUSB_POLL_SYNC); static bool reset = true; static struct usb_driver btusb_driver; @@ -4075,6 +4076,8 @@ static int btusb_probe(struct usb_interface *intf, if (enable_autosuspend) usb_enable_autosuspend(data->udev); + data->poll_sync = enable_poll_sync; + err = hci_register_dev(hdev); if (err < 0) goto out_free_dev; From db11223571d489d1aab575a4ac4b7352d2d54e2f Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Tue, 25 Oct 2022 14:12:58 -0700 Subject: [PATCH 1931/2002] Bluetooth: btusb: Default CONFIG_BT_HCIBTUSB_POLL_SYNC=y poll_sync has been proven to fix races of USB data and event endpoints so this enables it by default. Signed-off-by: Luiz Augusto von Dentz Tested-by: Tedd Ho-Jeong An --- drivers/bluetooth/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/bluetooth/Kconfig b/drivers/bluetooth/Kconfig index fc36057d546d..89b4d47b7c79 100644 --- a/drivers/bluetooth/Kconfig +++ b/drivers/bluetooth/Kconfig @@ -48,6 +48,7 @@ config BT_HCIBTUSB_AUTOSUSPEND config BT_HCIBTUSB_POLL_SYNC bool "Enable USB poll_sync for Bluetooth USB devices by default" depends on BT_HCIBTUSB + default y help poll_sync synchronizes the USB data and event endpoints by prioritizing the later. From 462fcd53924ccc21596d30edd0673fa7c939b392 Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Thu, 27 Oct 2022 16:18:04 -0700 Subject: [PATCH 1932/2002] Bluetooth: Add CONFIG_BT_LE_L2CAP_ECRED This adds CONFIG_BT_LE_L2CAP_ECRED which can be used to enable L2CAP Enhanced Credit Flow Control Mode by default, previously it was only possible to set it via module parameter (e.g. bluetooth.enable_ecred=1). Since L2CAP ECRED mode is required by the likes of EATT which is recommended for LE Audio this enables it by default. Signed-off-by: Luiz Augusto von Dentz Tested-By: Tedd Ho-Jeong An --- net/bluetooth/Kconfig | 11 +++++++++++ net/bluetooth/l2cap_core.c | 2 +- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/net/bluetooth/Kconfig b/net/bluetooth/Kconfig index ae3bdc6dfc92..da7cac0a1b71 100644 --- a/net/bluetooth/Kconfig +++ b/net/bluetooth/Kconfig @@ -78,6 +78,17 @@ config BT_LE Bluetooth Low Energy includes support low-energy physical layer available with Bluetooth version 4.0 or later. +config BT_LE_L2CAP_ECRED + bool "Bluetooth L2CAP Enhanced Credit Flow Control" + depends on BT_LE + default y + help + Bluetooth Low Energy L2CAP Enhanced Credit Flow Control available with + Bluetooth version 5.2 or later. + + This can be overridden by passing bluetooth.enable_ecred=[1|0] + on the kernel commandline. + config BT_6LOWPAN tristate "Bluetooth 6LoWPAN support" depends on BT_LE && 6LOWPAN diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 9fdede5fe71c..a3e0dc6a6e73 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -45,7 +45,7 @@ #define LE_FLOWCTL_MAX_CREDITS 65535 bool disable_ertm; -bool enable_ecred; +bool enable_ecred = IS_ENABLED(CONFIG_BT_LE_L2CAP_ECRED); static u32 l2cap_feat_mask = L2CAP_FEAT_FIXED_CHAN | L2CAP_FEAT_UCD; From 63db780a93eb802ece1bbf61ab5894ad8827b56e Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Sun, 30 Oct 2022 08:00:03 +0100 Subject: [PATCH 1933/2002] Bluetooth: Fix EALREADY and ELOOP cases in bt_status() 'err' is known to be <0 at this point. So, some cases can not be reached because of a missing "-". Add it. Fixes: ca2045e059c3 ("Bluetooth: Add bt_status") Signed-off-by: Christophe JAILLET Signed-off-by: Luiz Augusto von Dentz --- net/bluetooth/lib.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/bluetooth/lib.c b/net/bluetooth/lib.c index 469a0c95b6e8..53a796ac078c 100644 --- a/net/bluetooth/lib.c +++ b/net/bluetooth/lib.c @@ -170,7 +170,7 @@ __u8 bt_status(int err) case -EMLINK: return 0x09; - case EALREADY: + case -EALREADY: return 0x0b; case -EBUSY: @@ -191,7 +191,7 @@ __u8 bt_status(int err) case -ECONNABORTED: return 0x16; - case ELOOP: + case -ELOOP: return 0x17; case -EPROTONOSUPPORT: From ca2a99447e17acd67258aa1d54d7ea3c404a779c Mon Sep 17 00:00:00 2001 From: Shengyu Qu Date: Sun, 30 Oct 2022 01:22:54 +0800 Subject: [PATCH 1934/2002] Bluetooth: btusb: Add more device IDs for WCN6855 Add IDs to usb_device_id table for WCN6855. IDs are extracted from Windows driver of Lenovo Thinkpad T14 Gen 2(Driver version 1.0.0.1205 Windows 10) Windows driver download address: https://pcsupport.lenovo.com/us/en/products/laptops-and-netbooks/ thinkpad-t-series-laptops/thinkpad-t14-gen-2-type-20xk-20xl/downloads /driver-list/ Signed-off-by: Shengyu Qu Signed-off-by: Luiz Augusto von Dentz --- drivers/bluetooth/btusb.c | 84 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 84 insertions(+) diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index 8f34bf195bae..64a3febb9dec 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -317,6 +317,90 @@ static const struct usb_device_id blacklist_table[] = { { USB_DEVICE(0x0489, 0xe0d0), .driver_info = BTUSB_QCA_WCN6855 | BTUSB_WIDEBAND_SPEECH | BTUSB_VALID_LE_STATES }, + { USB_DEVICE(0x10ab, 0x9108), .driver_info = BTUSB_QCA_WCN6855 | + BTUSB_WIDEBAND_SPEECH | + BTUSB_VALID_LE_STATES }, + { USB_DEVICE(0x10ab, 0x9109), .driver_info = BTUSB_QCA_WCN6855 | + BTUSB_WIDEBAND_SPEECH | + BTUSB_VALID_LE_STATES }, + { USB_DEVICE(0x10ab, 0x9208), .driver_info = BTUSB_QCA_WCN6855 | + BTUSB_WIDEBAND_SPEECH | + BTUSB_VALID_LE_STATES }, + { USB_DEVICE(0x10ab, 0x9209), .driver_info = BTUSB_QCA_WCN6855 | + BTUSB_WIDEBAND_SPEECH | + BTUSB_VALID_LE_STATES }, + { USB_DEVICE(0x10ab, 0x9308), .driver_info = BTUSB_QCA_WCN6855 | + BTUSB_WIDEBAND_SPEECH | + BTUSB_VALID_LE_STATES }, + { USB_DEVICE(0x10ab, 0x9408), .driver_info = BTUSB_QCA_WCN6855 | + BTUSB_WIDEBAND_SPEECH | + BTUSB_VALID_LE_STATES }, + { USB_DEVICE(0x10ab, 0x9508), .driver_info = BTUSB_QCA_WCN6855 | + BTUSB_WIDEBAND_SPEECH | + BTUSB_VALID_LE_STATES }, + { USB_DEVICE(0x10ab, 0x9509), .driver_info = BTUSB_QCA_WCN6855 | + BTUSB_WIDEBAND_SPEECH | + BTUSB_VALID_LE_STATES }, + { USB_DEVICE(0x10ab, 0x9608), .driver_info = BTUSB_QCA_WCN6855 | + BTUSB_WIDEBAND_SPEECH | + BTUSB_VALID_LE_STATES }, + { USB_DEVICE(0x10ab, 0x9609), .driver_info = BTUSB_QCA_WCN6855 | + BTUSB_WIDEBAND_SPEECH | + BTUSB_VALID_LE_STATES }, + { USB_DEVICE(0x10ab, 0x9f09), .driver_info = BTUSB_QCA_WCN6855 | + BTUSB_WIDEBAND_SPEECH | + BTUSB_VALID_LE_STATES }, + { USB_DEVICE(0x04ca, 0x3022), .driver_info = BTUSB_QCA_WCN6855 | + BTUSB_WIDEBAND_SPEECH | + BTUSB_VALID_LE_STATES }, + { USB_DEVICE(0x0489, 0xe0c7), .driver_info = BTUSB_QCA_WCN6855 | + BTUSB_WIDEBAND_SPEECH | + BTUSB_VALID_LE_STATES }, + { USB_DEVICE(0x0489, 0xe0c9), .driver_info = BTUSB_QCA_WCN6855 | + BTUSB_WIDEBAND_SPEECH | + BTUSB_VALID_LE_STATES }, + { USB_DEVICE(0x0489, 0xe0ca), .driver_info = BTUSB_QCA_WCN6855 | + BTUSB_WIDEBAND_SPEECH | + BTUSB_VALID_LE_STATES }, + { USB_DEVICE(0x0489, 0xe0cb), .driver_info = BTUSB_QCA_WCN6855 | + BTUSB_WIDEBAND_SPEECH | + BTUSB_VALID_LE_STATES }, + { USB_DEVICE(0x0489, 0xe0ce), .driver_info = BTUSB_QCA_WCN6855 | + BTUSB_WIDEBAND_SPEECH | + BTUSB_VALID_LE_STATES }, + { USB_DEVICE(0x0489, 0xe0de), .driver_info = BTUSB_QCA_WCN6855 | + BTUSB_WIDEBAND_SPEECH | + BTUSB_VALID_LE_STATES }, + { USB_DEVICE(0x0489, 0xe0df), .driver_info = BTUSB_QCA_WCN6855 | + BTUSB_WIDEBAND_SPEECH | + BTUSB_VALID_LE_STATES }, + { USB_DEVICE(0x0489, 0xe0e1), .driver_info = BTUSB_QCA_WCN6855 | + BTUSB_WIDEBAND_SPEECH | + BTUSB_VALID_LE_STATES }, + { USB_DEVICE(0x0489, 0xe0ea), .driver_info = BTUSB_QCA_WCN6855 | + BTUSB_WIDEBAND_SPEECH | + BTUSB_VALID_LE_STATES }, + { USB_DEVICE(0x0489, 0xe0ec), .driver_info = BTUSB_QCA_WCN6855 | + BTUSB_WIDEBAND_SPEECH | + BTUSB_VALID_LE_STATES }, + { USB_DEVICE(0x04ca, 0x3023), .driver_info = BTUSB_QCA_WCN6855 | + BTUSB_WIDEBAND_SPEECH | + BTUSB_VALID_LE_STATES }, + { USB_DEVICE(0x04ca, 0x3024), .driver_info = BTUSB_QCA_WCN6855 | + BTUSB_WIDEBAND_SPEECH | + BTUSB_VALID_LE_STATES }, + { USB_DEVICE(0x04ca, 0x3a22), .driver_info = BTUSB_QCA_WCN6855 | + BTUSB_WIDEBAND_SPEECH | + BTUSB_VALID_LE_STATES }, + { USB_DEVICE(0x04ca, 0x3a24), .driver_info = BTUSB_QCA_WCN6855 | + BTUSB_WIDEBAND_SPEECH | + BTUSB_VALID_LE_STATES }, + { USB_DEVICE(0x04ca, 0x3a26), .driver_info = BTUSB_QCA_WCN6855 | + BTUSB_WIDEBAND_SPEECH | + BTUSB_VALID_LE_STATES }, + { USB_DEVICE(0x04ca, 0x3a27), .driver_info = BTUSB_QCA_WCN6855 | + BTUSB_WIDEBAND_SPEECH | + BTUSB_VALID_LE_STATES }, /* QCA WCN785x chipset */ { USB_DEVICE(0x0cf3, 0xe700), .driver_info = BTUSB_QCA_WCN6855 | From 3958e87783e746a2ce78fbb30f24231569f03543 Mon Sep 17 00:00:00 2001 From: Kang Minchul Date: Mon, 31 Oct 2022 03:17:22 +0900 Subject: [PATCH 1935/2002] Bluetooth: Use kzalloc instead of kmalloc/memset Replace kmalloc+memset by kzalloc for better readability and simplicity. This addresses the cocci warning below: WARNING: kzalloc should be used for d, instead of kmalloc/memset Signed-off-by: Kang Minchul Signed-off-by: Luiz Augusto von Dentz --- net/bluetooth/hci_conn.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index 07ddc1ef1aff..3287b2ca789e 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -824,11 +824,10 @@ static int hci_le_terminate_big(struct hci_dev *hdev, u8 big, u8 bis) bt_dev_dbg(hdev, "big 0x%2.2x bis 0x%2.2x", big, bis); - d = kmalloc(sizeof(*d), GFP_KERNEL); + d = kzalloc(sizeof(*d), GFP_KERNEL); if (!d) return -ENOMEM; - memset(d, 0, sizeof(*d)); d->big = big; d->bis = bis; @@ -861,11 +860,10 @@ static int hci_le_big_terminate(struct hci_dev *hdev, u8 big, u16 sync_handle) bt_dev_dbg(hdev, "big 0x%2.2x sync_handle 0x%4.4x", big, sync_handle); - d = kmalloc(sizeof(*d), GFP_KERNEL); + d = kzalloc(sizeof(*d), GFP_KERNEL); if (!d) return -ENOMEM; - memset(d, 0, sizeof(*d)); d->big = big; d->sync_handle = sync_handle; From 892913f0f2fdf57cd7271a59bf489ca0c60eded5 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Wed, 2 Nov 2022 17:47:04 +0100 Subject: [PATCH 1936/2002] dt-bindings: net: broadcom-bluetooth: Add CYW4373A0 DT binding CYW4373A0 is a Wi-Fi + Bluetooth combo device from Cypress. This chip is present e.g. on muRata 2AE module. Extend the binding with its DT compatible. Acked-by: Rob Herring Reviewed-by: Linus Walleij Signed-off-by: Marek Vasut Signed-off-by: Luiz Augusto von Dentz --- Documentation/devicetree/bindings/net/broadcom-bluetooth.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/net/broadcom-bluetooth.yaml b/Documentation/devicetree/bindings/net/broadcom-bluetooth.yaml index 445b2a553625..e0c8abc27413 100644 --- a/Documentation/devicetree/bindings/net/broadcom-bluetooth.yaml +++ b/Documentation/devicetree/bindings/net/broadcom-bluetooth.yaml @@ -24,6 +24,7 @@ properties: - brcm,bcm43540-bt - brcm,bcm4335a0 - brcm,bcm4349-bt + - cypress,cyw4373a0-bt - infineon,cyw55572-bt shutdown-gpios: From 02d056a3404e20245a69dcb4022a0930085fc5ec Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Wed, 2 Nov 2022 17:47:05 +0100 Subject: [PATCH 1937/2002] Bluetooth: hci_bcm: Add CYW4373A0 support CYW4373A0 is a Wi-Fi + Bluetooth combo device from Cypress. This chip is present e.g. on muRata 2AE module. This chip has additional quirk where the HCI command 0xfc45, used on older chips to switch UART clock from 24 MHz to 48 MHz, to support baudrates over 3 Mbdps, is no longer recognized by this newer chip. This newer chip can configure the 4 Mbdps baudrate without the need to issue HCI command 0xfc45, so add flag to indicate this and do not issue the command on this chip to avoid failure to set 4 Mbdps baud rate. It is not clear whether there is a way to determine which chip does and which chip does not support the HCI command 0xfc45, other than trial and error. Reviewed-by: Linus Walleij Signed-off-by: Marek Vasut Signed-off-by: Luiz Augusto von Dentz --- drivers/bluetooth/hci_bcm.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/drivers/bluetooth/hci_bcm.c b/drivers/bluetooth/hci_bcm.c index d7e0b75db8a6..2b6c0e1922cb 100644 --- a/drivers/bluetooth/hci_bcm.c +++ b/drivers/bluetooth/hci_bcm.c @@ -53,11 +53,13 @@ * struct bcm_device_data - device specific data * @no_early_set_baudrate: Disallow set baudrate before driver setup() * @drive_rts_on_open: drive RTS signal on ->open() when platform requires it + * @no_uart_clock_set: UART clock set command for >3Mbps mode is unavailable * @max_autobaud_speed: max baudrate supported by device in autobaud mode */ struct bcm_device_data { bool no_early_set_baudrate; bool drive_rts_on_open; + bool no_uart_clock_set; u32 max_autobaud_speed; }; @@ -100,6 +102,7 @@ struct bcm_device_data { * @is_suspended: whether flow control is currently disabled * @no_early_set_baudrate: don't set_baudrate before setup() * @drive_rts_on_open: drive RTS signal on ->open() when platform requires it + * @no_uart_clock_set: UART clock set command for >3Mbps mode is unavailable * @pcm_int_params: keep the initial PCM configuration * @use_autobaud_mode: start Bluetooth device in autobaud mode * @max_autobaud_speed: max baudrate supported by device in autobaud mode @@ -140,6 +143,7 @@ struct bcm_device { #endif bool no_early_set_baudrate; bool drive_rts_on_open; + bool no_uart_clock_set; bool use_autobaud_mode; u8 pcm_int_params[5]; u32 max_autobaud_speed; @@ -172,10 +176,11 @@ static inline void host_set_baudrate(struct hci_uart *hu, unsigned int speed) static int bcm_set_baudrate(struct hci_uart *hu, unsigned int speed) { struct hci_dev *hdev = hu->hdev; + struct bcm_data *bcm = hu->priv; struct sk_buff *skb; struct bcm_update_uart_baud_rate param; - if (speed > 3000000) { + if (speed > 3000000 && !bcm->dev->no_uart_clock_set) { struct bcm_write_uart_clock_setting clock; clock.type = BCM_UART_CLOCK_48MHZ; @@ -1529,6 +1534,7 @@ static int bcm_serdev_probe(struct serdev_device *serdev) bcmdev->max_autobaud_speed = data->max_autobaud_speed; bcmdev->no_early_set_baudrate = data->no_early_set_baudrate; bcmdev->drive_rts_on_open = data->drive_rts_on_open; + bcmdev->no_uart_clock_set = data->no_uart_clock_set; } return hci_uart_register_device(&bcmdev->serdev_hu, &bcm_proto); @@ -1550,6 +1556,10 @@ static struct bcm_device_data bcm43438_device_data = { .drive_rts_on_open = true, }; +static struct bcm_device_data cyw4373a0_device_data = { + .no_uart_clock_set = true, +}; + static struct bcm_device_data cyw55572_device_data = { .max_autobaud_speed = 921600, }; @@ -1566,6 +1576,7 @@ static const struct of_device_id bcm_bluetooth_of_match[] = { { .compatible = "brcm,bcm4349-bt", .data = &bcm43438_device_data }, { .compatible = "brcm,bcm43540-bt", .data = &bcm4354_device_data }, { .compatible = "brcm,bcm4335a0" }, + { .compatible = "cypress,cyw4373a0-bt", .data = &cyw4373a0_device_data }, { .compatible = "infineon,cyw55572-bt", .data = &cyw55572_device_data }, { }, }; From 45564c4ef607a4410f333a3a5e881bab90c761cd Mon Sep 17 00:00:00 2001 From: Sven Peter Date: Fri, 4 Nov 2022 22:12:57 +0100 Subject: [PATCH 1938/2002] dt-bindings: net: Add generic Bluetooth controller Bluetooth controllers share the common local-bd-address property. Add a generic YAML schema to replace bluetooth.txt for those. Signed-off-by: Sven Peter Reviewed-by: Rob Herring Signed-off-by: Luiz Augusto von Dentz --- .../devicetree/bindings/net/bluetooth.txt | 5 ---- .../net/bluetooth/bluetooth-controller.yaml | 29 +++++++++++++++++++ .../{ => bluetooth}/qualcomm-bluetooth.yaml | 6 ++-- .../bindings/soc/qcom/qcom,wcnss.yaml | 8 ++--- 4 files changed, 35 insertions(+), 13 deletions(-) delete mode 100644 Documentation/devicetree/bindings/net/bluetooth.txt create mode 100644 Documentation/devicetree/bindings/net/bluetooth/bluetooth-controller.yaml rename Documentation/devicetree/bindings/net/{ => bluetooth}/qualcomm-bluetooth.yaml (96%) diff --git a/Documentation/devicetree/bindings/net/bluetooth.txt b/Documentation/devicetree/bindings/net/bluetooth.txt deleted file mode 100644 index 94797df751b8..000000000000 --- a/Documentation/devicetree/bindings/net/bluetooth.txt +++ /dev/null @@ -1,5 +0,0 @@ -The following properties are common to the Bluetooth controllers: - -- local-bd-address: array of 6 bytes, specifies the BD address that was - uniquely assigned to the Bluetooth device, formatted with least significant - byte first (little-endian). diff --git a/Documentation/devicetree/bindings/net/bluetooth/bluetooth-controller.yaml b/Documentation/devicetree/bindings/net/bluetooth/bluetooth-controller.yaml new file mode 100644 index 000000000000..9309dc40f54f --- /dev/null +++ b/Documentation/devicetree/bindings/net/bluetooth/bluetooth-controller.yaml @@ -0,0 +1,29 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/net/bluetooth/bluetooth-controller.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Bluetooth Controller Generic Binding + +maintainers: + - Marcel Holtmann + - Johan Hedberg + - Luiz Augusto von Dentz + +properties: + $nodename: + pattern: "^bluetooth(@.*)?$" + + local-bd-address: + $ref: /schemas/types.yaml#/definitions/uint8-array + maxItems: 6 + description: + Specifies the BD address that was uniquely assigned to the Bluetooth + device. Formatted with least significant byte first (little-endian), e.g. + in order to assign the address 00:11:22:33:44:55 this property must have + the value [55 44 33 22 11 00]. + +additionalProperties: true + +... diff --git a/Documentation/devicetree/bindings/net/qualcomm-bluetooth.yaml b/Documentation/devicetree/bindings/net/bluetooth/qualcomm-bluetooth.yaml similarity index 96% rename from Documentation/devicetree/bindings/net/qualcomm-bluetooth.yaml rename to Documentation/devicetree/bindings/net/bluetooth/qualcomm-bluetooth.yaml index f93c6e7a1b59..a6a6b0e4df7a 100644 --- a/Documentation/devicetree/bindings/net/qualcomm-bluetooth.yaml +++ b/Documentation/devicetree/bindings/net/bluetooth/qualcomm-bluetooth.yaml @@ -1,7 +1,7 @@ # SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) %YAML 1.2 --- -$id: http://devicetree.org/schemas/net/qualcomm-bluetooth.yaml# +$id: http://devicetree.org/schemas/net/bluetooth/qualcomm-bluetooth.yaml# $schema: http://devicetree.org/meta-schemas/core.yaml# title: Qualcomm Bluetooth Chips @@ -79,8 +79,7 @@ properties: firmware-name: description: specify the name of nvm firmware to load - local-bd-address: - description: see Documentation/devicetree/bindings/net/bluetooth.txt + local-bd-address: true required: @@ -89,6 +88,7 @@ required: additionalProperties: false allOf: + - $ref: bluetooth-controller.yaml# - if: properties: compatible: diff --git a/Documentation/devicetree/bindings/soc/qcom/qcom,wcnss.yaml b/Documentation/devicetree/bindings/soc/qcom/qcom,wcnss.yaml index 5320504bb5e0..0e6fd57d658d 100644 --- a/Documentation/devicetree/bindings/soc/qcom/qcom,wcnss.yaml +++ b/Documentation/devicetree/bindings/soc/qcom/qcom,wcnss.yaml @@ -42,15 +42,13 @@ properties: bluetooth: type: object additionalProperties: false + allOf: + - $ref: /schemas/net/bluetooth/bluetooth-controller.yaml# properties: compatible: const: qcom,wcnss-bt - local-bd-address: - $ref: /schemas/types.yaml#/definitions/uint8-array - maxItems: 6 - description: - See Documentation/devicetree/bindings/net/bluetooth.txt + local-bd-address: true required: - compatible From c9176e10b2b68dfa4b94dce11ad99214ceecf714 Mon Sep 17 00:00:00 2001 From: Sven Peter Date: Fri, 4 Nov 2022 22:12:58 +0100 Subject: [PATCH 1939/2002] dt-bindings: net: Add Broadcom BCM4377 family PCIe Bluetooth These chips are combined Wi-Fi/Bluetooth radios which expose a PCI subfunction for the Bluetooth part. They are found in Apple machines such as the x86 models with the T2 chip or the arm64 models with the M1 or M2 chips. Signed-off-by: Sven Peter Reviewed-by: Rob Herring Signed-off-by: Luiz Augusto von Dentz --- .../net/bluetooth/brcm,bcm4377-bluetooth.yaml | 81 +++++++++++++++++++ MAINTAINERS | 1 + 2 files changed, 82 insertions(+) create mode 100644 Documentation/devicetree/bindings/net/bluetooth/brcm,bcm4377-bluetooth.yaml diff --git a/Documentation/devicetree/bindings/net/bluetooth/brcm,bcm4377-bluetooth.yaml b/Documentation/devicetree/bindings/net/bluetooth/brcm,bcm4377-bluetooth.yaml new file mode 100644 index 000000000000..37cb39a3a62e --- /dev/null +++ b/Documentation/devicetree/bindings/net/bluetooth/brcm,bcm4377-bluetooth.yaml @@ -0,0 +1,81 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/net/bluetooth/brcm,bcm4377-bluetooth.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Broadcom BCM4377 family PCIe Bluetooth Chips + +maintainers: + - Sven Peter + +description: + This binding describes Broadcom BCM4377 family PCIe-attached bluetooth chips + usually found in Apple machines. The Wi-Fi part of the chip is described in + bindings/net/wireless/brcm,bcm4329-fmac.yaml. + +allOf: + - $ref: bluetooth-controller.yaml# + +properties: + compatible: + enum: + - pci14e4,5fa0 # BCM4377 + - pci14e4,5f69 # BCM4378 + - pci14e4,5f71 # BCM4387 + + reg: + maxItems: 1 + + brcm,board-type: + $ref: /schemas/types.yaml#/definitions/string + description: Board type of the Bluetooth chip. This is used to decouple + the overall system board from the Bluetooth module and used to construct + firmware and calibration data filenames. + On Apple platforms, this should be the Apple module-instance codename + prefixed by "apple,", e.g. "apple,atlantisb". + pattern: '^apple,.*' + + brcm,taurus-cal-blob: + $ref: /schemas/types.yaml#/definitions/uint8-array + description: A per-device calibration blob for the Bluetooth radio. This + should be filled in by the bootloader from platform configuration + data, if necessary, and will be uploaded to the device. + This blob is used if the chip stepping of the Bluetooth module does not + support beamforming. + + brcm,taurus-bf-cal-blob: + $ref: /schemas/types.yaml#/definitions/uint8-array + description: A per-device calibration blob for the Bluetooth radio. This + should be filled in by the bootloader from platform configuration + data, if necessary, and will be uploaded to the device. + This blob is used if the chip stepping of the Bluetooth module supports + beamforming. + + local-bd-address: true + +required: + - compatible + - reg + - local-bd-address + - brcm,board-type + +additionalProperties: false + +examples: + - | + pcie@a0000000 { + #address-cells = <3>; + #size-cells = <2>; + reg = <0xa0000000 0x1000000>; + device_type = "pci"; + ranges = <0x43000000 0x6 0xa0000000 0xa0000000 0x0 0x20000000>; + + bluetooth@0,1 { + compatible = "pci14e4,5f69"; + reg = <0x100 0x0 0x0 0x0 0x0>; + brcm,board-type = "apple,honshu"; + /* To be filled by the bootloader */ + local-bd-address = [00 00 00 00 00 00]; + }; + }; diff --git a/MAINTAINERS b/MAINTAINERS index 0fbe5135490d..45c443aa7c60 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1903,6 +1903,7 @@ F: Documentation/devicetree/bindings/interrupt-controller/apple,* F: Documentation/devicetree/bindings/iommu/apple,dart.yaml F: Documentation/devicetree/bindings/iommu/apple,sart.yaml F: Documentation/devicetree/bindings/mailbox/apple,mailbox.yaml +F: Documentation/devicetree/bindings/net/bluetooth/brcm,bcm4377-bluetooth.yaml F: Documentation/devicetree/bindings/nvme/apple,nvme-ans.yaml F: Documentation/devicetree/bindings/nvmem/apple,efuses.yaml F: Documentation/devicetree/bindings/pci/apple,pcie.yaml From 7a73b976eda916a2ed703e4b996c92c871715d9b Mon Sep 17 00:00:00 2001 From: Sven Peter Date: Fri, 4 Nov 2022 22:12:59 +0100 Subject: [PATCH 1940/2002] arm64: dts: apple: t8103: Add Bluetooth controller Add bluetooth controller nodes and the required brcm,board-type properties to be able to select the correct firmware to all board device trees. Signed-off-by: Sven Peter Signed-off-by: Luiz Augusto von Dentz --- arch/arm64/boot/dts/apple/t8103-j274.dts | 4 ++++ arch/arm64/boot/dts/apple/t8103-j293.dts | 4 ++++ arch/arm64/boot/dts/apple/t8103-j313.dts | 4 ++++ arch/arm64/boot/dts/apple/t8103-j456.dts | 4 ++++ arch/arm64/boot/dts/apple/t8103-j457.dts | 4 ++++ arch/arm64/boot/dts/apple/t8103-jxxx.dtsi | 8 ++++++++ 6 files changed, 28 insertions(+) diff --git a/arch/arm64/boot/dts/apple/t8103-j274.dts b/arch/arm64/boot/dts/apple/t8103-j274.dts index c1f3ba9c39f6..b52ddc409893 100644 --- a/arch/arm64/boot/dts/apple/t8103-j274.dts +++ b/arch/arm64/boot/dts/apple/t8103-j274.dts @@ -21,6 +21,10 @@ }; }; +&bluetooth0 { + brcm,board-type = "apple,atlantisb"; +}; + &wifi0 { brcm,board-type = "apple,atlantisb"; }; diff --git a/arch/arm64/boot/dts/apple/t8103-j293.dts b/arch/arm64/boot/dts/apple/t8103-j293.dts index ecb10d237a05..151074109a11 100644 --- a/arch/arm64/boot/dts/apple/t8103-j293.dts +++ b/arch/arm64/boot/dts/apple/t8103-j293.dts @@ -17,6 +17,10 @@ model = "Apple MacBook Pro (13-inch, M1, 2020)"; }; +&bluetooth0 { + brcm,board-type = "apple,honshu"; +}; + &wifi0 { brcm,board-type = "apple,honshu"; }; diff --git a/arch/arm64/boot/dts/apple/t8103-j313.dts b/arch/arm64/boot/dts/apple/t8103-j313.dts index df741737b8e6..bc1f865aa790 100644 --- a/arch/arm64/boot/dts/apple/t8103-j313.dts +++ b/arch/arm64/boot/dts/apple/t8103-j313.dts @@ -17,6 +17,10 @@ model = "Apple MacBook Air (M1, 2020)"; }; +&bluetooth0 { + brcm,board-type = "apple,shikoku"; +}; + &wifi0 { brcm,board-type = "apple,shikoku"; }; diff --git a/arch/arm64/boot/dts/apple/t8103-j456.dts b/arch/arm64/boot/dts/apple/t8103-j456.dts index 8c6bf9592510..7ea27456f33c 100644 --- a/arch/arm64/boot/dts/apple/t8103-j456.dts +++ b/arch/arm64/boot/dts/apple/t8103-j456.dts @@ -21,6 +21,10 @@ }; }; +&bluetooth0 { + brcm,board-type = "apple,capri"; +}; + &wifi0 { brcm,board-type = "apple,capri"; }; diff --git a/arch/arm64/boot/dts/apple/t8103-j457.dts b/arch/arm64/boot/dts/apple/t8103-j457.dts index fe7c0aaf7d62..8ee0ac871426 100644 --- a/arch/arm64/boot/dts/apple/t8103-j457.dts +++ b/arch/arm64/boot/dts/apple/t8103-j457.dts @@ -21,6 +21,10 @@ }; }; +&bluetooth0 { + brcm,board-type = "apple,santorini"; +}; + &wifi0 { brcm,board-type = "apple,santorini"; }; diff --git a/arch/arm64/boot/dts/apple/t8103-jxxx.dtsi b/arch/arm64/boot/dts/apple/t8103-jxxx.dtsi index 3d15b8e2a6c1..cc2e04035763 100644 --- a/arch/arm64/boot/dts/apple/t8103-jxxx.dtsi +++ b/arch/arm64/boot/dts/apple/t8103-jxxx.dtsi @@ -11,6 +11,7 @@ / { aliases { + bluetooth0 = &bluetooth0; serial0 = &serial0; serial2 = &serial2; wifi0 = &wifi0; @@ -77,4 +78,11 @@ local-mac-address = [00 00 00 00 00 00]; apple,antenna-sku = "XX"; }; + + bluetooth0: bluetooth@0,1 { + compatible = "pci14e4,5f69"; + reg = <0x10100 0x0 0x0 0x0 0x0>; + /* To be filled by the loader */ + local-bd-address = [00 00 00 00 00 00]; + }; }; From ad38e55e1c89384aecee1bb0425bf1bf21ec86fd Mon Sep 17 00:00:00 2001 From: Sven Peter Date: Fri, 4 Nov 2022 22:13:00 +0100 Subject: [PATCH 1941/2002] Bluetooth: hci_event: Ignore reserved bits in LE Extended Adv Report Broadcom controllers present on Apple Silicon devices use the upper 8 bits of the event type in the LE Extended Advertising Report for the channel on which the frame has been received. These bits are reserved according to the Bluetooth spec anyway such that we can just drop them to ensure that the advertising results are parsed correctly. The following excerpt from a btmon trace shows a report received on channel 37 by these controllers: > HCI Event: LE Meta Event (0x3e) plen 55 LE Extended Advertising Report (0x0d) Num reports: 1 Entry 0 Event type: 0x2513 Props: 0x0013 Connectable Scannable Use legacy advertising PDUs Data status: Complete Reserved (0x2500) Legacy PDU Type: Reserved (0x2513) Address type: Public (0x00) Address: XX:XX:XX:XX:XX:XX (Shenzhen Jingxun Software [...]) Primary PHY: LE 1M Secondary PHY: No packets SID: no ADI field (0xff) TX power: 127 dBm RSSI: -76 dBm (0xb4) Periodic advertising interval: 0.00 msec (0x0000) Direct address type: Public (0x00) Direct address: 00:00:00:00:00:00 (OUI 00-00-00) Data length: 0x1d [...] Flags: 0x18 Simultaneous LE and BR/EDR (Controller) Simultaneous LE and BR/EDR (Host) Company: Harman International Industries, Inc. (87) Data: [...] Service Data (UUID 0xfddf): Name (complete): JBL Flip 5 Signed-off-by: Sven Peter Signed-off-by: Luiz Augusto von Dentz --- include/net/bluetooth/hci.h | 1 + net/bluetooth/hci_event.c | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 684f1cd28730..a035ff6055da 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -2590,6 +2590,7 @@ struct hci_ev_le_conn_complete { #define LE_EXT_ADV_DIRECT_IND 0x0004 #define LE_EXT_ADV_SCAN_RSP 0x0008 #define LE_EXT_ADV_LEGACY_PDU 0x0010 +#define LE_EXT_ADV_EVT_TYPE_MASK 0x007f #define ADDR_LE_DEV_PUBLIC 0x00 #define ADDR_LE_DEV_RANDOM 0x01 diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index faca701bce2a..ade2628aae0d 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -6494,7 +6494,7 @@ static void hci_le_ext_adv_report_evt(struct hci_dev *hdev, void *data, info->length)) break; - evt_type = __le16_to_cpu(info->type); + evt_type = __le16_to_cpu(info->type) & LE_EXT_ADV_EVT_TYPE_MASK; legacy_evt_type = ext_evt_type_to_legacy(hdev, evt_type); if (legacy_evt_type != LE_ADV_INVALID) { process_adv_report(hdev, legacy_evt_type, &info->bdaddr, From 392fca352c7a95e2828d49e7500e26d0c87ca265 Mon Sep 17 00:00:00 2001 From: Sven Peter Date: Fri, 4 Nov 2022 22:13:01 +0100 Subject: [PATCH 1942/2002] Bluetooth: Add quirk to disable extended scanning Broadcom 4377 controllers found in Apple x86 Macs with the T2 chip claim to support extended scanning when querying supported states, < HCI Command: LE Read Supported St.. (0x08|0x001c) plen 0 > HCI Event: Command Complete (0x0e) plen 12 LE Read Supported States (0x08|0x001c) ncmd 1 Status: Success (0x00) States: 0x000003ffffffffff [...] LE Set Extended Scan Parameters (Octet 37 - Bit 5) LE Set Extended Scan Enable (Octet 37 - Bit 6) [...] , but then fail to actually implement the extended scanning: < HCI Command: LE Set Extended Sca.. (0x08|0x0041) plen 8 Own address type: Random (0x01) Filter policy: Accept all advertisement (0x00) PHYs: 0x01 Entry 0: LE 1M Type: Active (0x01) Interval: 11.250 msec (0x0012) Window: 11.250 msec (0x0012) > HCI Event: Command Complete (0x0e) plen 4 LE Set Extended Scan Parameters (0x08|0x0041) ncmd 1 Status: Unknown HCI Command (0x01) Signed-off-by: Sven Peter Signed-off-by: Luiz Augusto von Dentz --- include/net/bluetooth/hci.h | 10 ++++++++++ include/net/bluetooth/hci_core.h | 4 +++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index a035ff6055da..4cf6bc363e4f 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -274,6 +274,16 @@ enum { * during the hdev->setup vendor callback. */ HCI_QUIRK_BROKEN_ENHANCED_SETUP_SYNC_CONN, + + /* + * When this quirk is set, the HCI_OP_LE_SET_EXT_SCAN_ENABLE command is + * disabled. This is required for some Broadcom controllers which + * erroneously claim to support extended scanning. + * + * This quirk can be set before hci_register_dev is called or + * during the hdev->setup vendor callback. + */ + HCI_QUIRK_BROKEN_EXT_SCAN, }; /* HCI device flags */ diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 55a40f5606c3..1113d74e1f9f 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -1690,7 +1690,9 @@ void hci_conn_del_sysfs(struct hci_conn *conn); /* Use ext scanning if set ext scan param and ext scan enable is supported */ #define use_ext_scan(dev) (((dev)->commands[37] & 0x20) && \ - ((dev)->commands[37] & 0x40)) + ((dev)->commands[37] & 0x40) && \ + !test_bit(HCI_QUIRK_BROKEN_EXT_SCAN, &(dev)->quirks)) + /* Use ext create connection if command is supported */ #define use_ext_conn(dev) ((dev)->commands[37] & 0x80) From ffcb0a445ec2d5753751437706aa0a7ea8351099 Mon Sep 17 00:00:00 2001 From: Sven Peter Date: Fri, 4 Nov 2022 22:13:02 +0100 Subject: [PATCH 1943/2002] Bluetooth: Add quirk to disable MWS Transport Configuration Broadcom 4378/4387 controllers found in Apple Silicon Macs claim to support getting MWS Transport Layer Configuration, < HCI Command: Read Local Supported... (0x04|0x0002) plen 0 > HCI Event: Command Complete (0x0e) plen 68 Read Local Supported Commands (0x04|0x0002) ncmd 1 Status: Success (0x00) [...] Get MWS Transport Layer Configuration (Octet 30 - Bit 3)] [...] , but then don't actually allow the required command: > HCI Event: Command Complete (0x0e) plen 15 Get MWS Transport Layer Configuration (0x05|0x000c) ncmd 1 Status: Command Disallowed (0x0c) Number of transports: 0 Baud rate list: 0 entries 00 00 00 00 00 00 00 00 00 00 Signed-off-by: Sven Peter Signed-off-by: Luiz Augusto von Dentz --- include/net/bluetooth/hci.h | 10 ++++++++++ include/net/bluetooth/hci_core.h | 3 +++ net/bluetooth/hci_sync.c | 2 +- 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 4cf6bc363e4f..8d773b042c85 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -284,6 +284,16 @@ enum { * during the hdev->setup vendor callback. */ HCI_QUIRK_BROKEN_EXT_SCAN, + + /* + * When this quirk is set, the HCI_OP_GET_MWS_TRANSPORT_CONFIG command is + * disabled. This is required for some Broadcom controllers which + * erroneously claim to support MWS Transport Layer Configuration. + * + * This quirk can be set before hci_register_dev is called or + * during the hdev->setup vendor callback. + */ + HCI_QUIRK_BROKEN_MWS_TRANSPORT_CONFIG, }; /* HCI device flags */ diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 1113d74e1f9f..7254edfba4c9 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -1720,6 +1720,9 @@ void hci_conn_del_sysfs(struct hci_conn *conn); ((dev)->le_features[3] & HCI_LE_CIS_PERIPHERAL) #define bis_capable(dev) ((dev)->le_features[3] & HCI_LE_ISO_BROADCASTER) +#define mws_transport_config_capable(dev) (((dev)->commands[30] & 0x08) && \ + (!test_bit(HCI_QUIRK_BROKEN_MWS_TRANSPORT_CONFIG, &(dev)->quirks))) + /* ----- HCI protocols ----- */ #define HCI_PROTO_DEFER 0x01 diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c index 4debe205d4af..9e2d7e4b850c 100644 --- a/net/bluetooth/hci_sync.c +++ b/net/bluetooth/hci_sync.c @@ -4279,7 +4279,7 @@ static int hci_read_local_pairing_opts_sync(struct hci_dev *hdev) /* Get MWS transport configuration if the HCI command is supported */ static int hci_get_mws_transport_config_sync(struct hci_dev *hdev) { - if (!(hdev->commands[30] & 0x08)) + if (!mws_transport_config_capable(hdev)) return 0; return __hci_cmd_sync_status(hdev, HCI_OP_GET_MWS_TRANSPORT_CONFIG, From 8a06127602de70541e751a8c64a06995ee281f22 Mon Sep 17 00:00:00 2001 From: Sven Peter Date: Fri, 4 Nov 2022 22:13:03 +0100 Subject: [PATCH 1944/2002] Bluetooth: hci_bcm4377: Add new driver for BCM4377 PCIe boards Broadcom BCM4377/4378/4387 are dual WiFi/Bluetooth boards found in Apple machines. This driver adds support for the Bluetooth function which exposes a shared memory IPC protocol over PCIe to tunnel HCI traffic. Signed-off-by: Sven Peter Signed-off-by: Luiz Augusto von Dentz --- MAINTAINERS | 1 + drivers/bluetooth/Kconfig | 12 + drivers/bluetooth/Makefile | 1 + drivers/bluetooth/hci_bcm4377.c | 2514 +++++++++++++++++++++++++++++++ 4 files changed, 2528 insertions(+) create mode 100644 drivers/bluetooth/hci_bcm4377.c diff --git a/MAINTAINERS b/MAINTAINERS index 45c443aa7c60..32dd41574930 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1911,6 +1911,7 @@ F: Documentation/devicetree/bindings/pinctrl/apple,pinctrl.yaml F: Documentation/devicetree/bindings/power/apple* F: Documentation/devicetree/bindings/watchdog/apple,wdt.yaml F: arch/arm64/boot/dts/apple/ +F: drivers/bluetooth/hci_bcm4377.c F: drivers/clk/clk-apple-nco.c F: drivers/dma/apple-admac.c F: drivers/i2c/busses/i2c-pasemi-core.c diff --git a/drivers/bluetooth/Kconfig b/drivers/bluetooth/Kconfig index 89b4d47b7c79..5a1a7bec3c42 100644 --- a/drivers/bluetooth/Kconfig +++ b/drivers/bluetooth/Kconfig @@ -285,6 +285,18 @@ config BT_HCIBCM203X Say Y here to compile support for HCI BCM203x devices into the kernel or say M to compile it as module (bcm203x). + +config BT_HCIBCM4377 + tristate "HCI BCM4377/4378/4387 PCIe driver" + depends on PCI + select FW_LOADER + help + Support for Broadcom BCM4377/4378/4387 Bluetooth chipsets attached via + PCIe. These are usually found in Apple machines. + + Say Y here to compile support for HCI BCM4377 family devices into the + kernel or say M to compile it as module (hci_bcm4377). + config BT_HCIBPA10X tristate "HCI BPA10x USB driver" depends on USB diff --git a/drivers/bluetooth/Makefile b/drivers/bluetooth/Makefile index 3321a8aea4a0..e0b261f24fc9 100644 --- a/drivers/bluetooth/Makefile +++ b/drivers/bluetooth/Makefile @@ -6,6 +6,7 @@ obj-$(CONFIG_BT_HCIVHCI) += hci_vhci.o obj-$(CONFIG_BT_HCIUART) += hci_uart.o obj-$(CONFIG_BT_HCIBCM203X) += bcm203x.o +obj-$(CONFIG_BT_HCIBCM4377) += hci_bcm4377.o obj-$(CONFIG_BT_HCIBPA10X) += bpa10x.o obj-$(CONFIG_BT_HCIBFUSB) += bfusb.o obj-$(CONFIG_BT_HCIDTL1) += dtl1_cs.o diff --git a/drivers/bluetooth/hci_bcm4377.c b/drivers/bluetooth/hci_bcm4377.c new file mode 100644 index 000000000000..74f44562ac33 --- /dev/null +++ b/drivers/bluetooth/hci_bcm4377.c @@ -0,0 +1,2514 @@ +// SPDX-License-Identifier: GPL-2.0-only OR MIT +/* + * Bluetooth HCI driver for Broadcom 4377/4378/4387 devices attached via PCIe + * + * Copyright (C) The Asahi Linux Contributors + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +enum bcm4377_chip { + BCM4377 = 0, + BCM4378, + BCM4387, +}; + +#define BCM4377_DEVICE_ID 0x5fa0 +#define BCM4378_DEVICE_ID 0x5f69 +#define BCM4387_DEVICE_ID 0x5f71 + +#define BCM4377_TIMEOUT 1000 + +/* + * These devices only support DMA transactions inside a 32bit window + * (possibly to avoid 64 bit arithmetic). The window size cannot exceed + * 0xffffffff but is always aligned down to the previous 0x200 byte boundary + * which effectively limits the window to [start, start+0xfffffe00]. + * We just limit the DMA window to [0, 0xfffffe00] to make sure we don't + * run into this limitation. + */ +#define BCM4377_DMA_MASK 0xfffffe00 + +#define BCM4377_PCIECFG_BAR0_WINDOW1 0x80 +#define BCM4377_PCIECFG_BAR0_WINDOW2 0x70 +#define BCM4377_PCIECFG_BAR0_CORE2_WINDOW1 0x74 +#define BCM4377_PCIECFG_BAR0_CORE2_WINDOW2 0x78 +#define BCM4377_PCIECFG_BAR2_WINDOW 0x84 + +#define BCM4377_PCIECFG_BAR0_CORE2_WINDOW1_DEFAULT 0x18011000 +#define BCM4377_PCIECFG_BAR2_WINDOW_DEFAULT 0x19000000 + +#define BCM4377_PCIECFG_SUBSYSTEM_CTRL 0x88 + +#define BCM4377_BAR0_FW_DOORBELL 0x140 +#define BCM4377_BAR0_RTI_CONTROL 0x144 + +#define BCM4377_BAR0_SLEEP_CONTROL 0x150 +#define BCM4377_BAR0_SLEEP_CONTROL_UNQUIESCE 0 +#define BCM4377_BAR0_SLEEP_CONTROL_AWAKE 2 +#define BCM4377_BAR0_SLEEP_CONTROL_QUIESCE 3 + +#define BCM4377_BAR0_DOORBELL 0x174 +#define BCM4377_BAR0_DOORBELL_VALUE GENMASK(31, 16) +#define BCM4377_BAR0_DOORBELL_IDX GENMASK(15, 8) +#define BCM4377_BAR0_DOORBELL_RING BIT(5) + +#define BCM4377_BAR0_HOST_WINDOW_LO 0x590 +#define BCM4377_BAR0_HOST_WINDOW_HI 0x594 +#define BCM4377_BAR0_HOST_WINDOW_SIZE 0x598 + +#define BCM4377_BAR2_BOOTSTAGE 0x200454 + +#define BCM4377_BAR2_FW_LO 0x200478 +#define BCM4377_BAR2_FW_HI 0x20047c +#define BCM4377_BAR2_FW_SIZE 0x200480 + +#define BCM4377_BAR2_CONTEXT_ADDR_LO 0x20048c +#define BCM4377_BAR2_CONTEXT_ADDR_HI 0x200450 + +#define BCM4377_BAR2_RTI_STATUS 0x20045c +#define BCM4377_BAR2_RTI_WINDOW_LO 0x200494 +#define BCM4377_BAR2_RTI_WINDOW_HI 0x200498 +#define BCM4377_BAR2_RTI_WINDOW_SIZE 0x20049c + +#define BCM4377_OTP_SIZE 0xe0 +#define BCM4377_OTP_SYS_VENDOR 0x15 +#define BCM4377_OTP_CIS 0x80 +#define BCM4377_OTP_VENDOR_HDR 0x00000008 +#define BCM4377_OTP_MAX_PARAM_LEN 16 + +#define BCM4377_N_TRANSFER_RINGS 9 +#define BCM4377_N_COMPLETION_RINGS 6 + +#define BCM4377_MAX_RING_SIZE 256 + +#define BCM4377_MSGID_GENERATION GENMASK(15, 8) +#define BCM4377_MSGID_ID GENMASK(7, 0) + +#define BCM4377_RING_N_ENTRIES 128 + +#define BCM4377_CONTROL_MSG_SIZE 0x34 +#define BCM4377_XFER_RING_MAX_INPLACE_PAYLOAD_SIZE (4 * 0xff) + +#define MAX_ACL_PAYLOAD_SIZE (HCI_MAX_FRAME_SIZE + HCI_ACL_HDR_SIZE) +#define MAX_SCO_PAYLOAD_SIZE (HCI_MAX_SCO_SIZE + HCI_SCO_HDR_SIZE) +#define MAX_EVENT_PAYLOAD_SIZE (HCI_MAX_EVENT_SIZE + HCI_EVENT_HDR_SIZE) + +enum bcm4377_otp_params_type { + BCM4377_OTP_BOARD_PARAMS, + BCM4377_OTP_CHIP_PARAMS +}; + +enum bcm4377_transfer_ring_id { + BCM4377_XFER_RING_CONTROL = 0, + BCM4377_XFER_RING_HCI_H2D = 1, + BCM4377_XFER_RING_HCI_D2H = 2, + BCM4377_XFER_RING_SCO_H2D = 3, + BCM4377_XFER_RING_SCO_D2H = 4, + BCM4377_XFER_RING_ACL_H2D = 5, + BCM4377_XFER_RING_ACL_D2H = 6, +}; + +enum bcm4377_completion_ring_id { + BCM4377_ACK_RING_CONTROL = 0, + BCM4377_ACK_RING_HCI_ACL = 1, + BCM4377_EVENT_RING_HCI_ACL = 2, + BCM4377_ACK_RING_SCO = 3, + BCM4377_EVENT_RING_SCO = 4, +}; + +enum bcm4377_doorbell { + BCM4377_DOORBELL_CONTROL = 0, + BCM4377_DOORBELL_HCI_H2D = 1, + BCM4377_DOORBELL_HCI_D2H = 2, + BCM4377_DOORBELL_ACL_H2D = 3, + BCM4377_DOORBELL_ACL_D2H = 4, + BCM4377_DOORBELL_SCO = 6, +}; + +/* + * Transfer ring entry + * + * flags: Flags to indicate if the payload is appended or mapped + * len: Payload length + * payload: Optional payload DMA address + * id: Message id to recognize the answer in the completion ring entry + */ +struct bcm4377_xfer_ring_entry { +#define BCM4377_XFER_RING_FLAG_PAYLOAD_MAPPED BIT(0) +#define BCM4377_XFER_RING_FLAG_PAYLOAD_IN_FOOTER BIT(1) + u8 flags; + __le16 len; + u8 _unk0; + __le64 payload; + __le16 id; + u8 _unk1[2]; +} __packed; +static_assert(sizeof(struct bcm4377_xfer_ring_entry) == 0x10); + +/* + * Completion ring entry + * + * flags: Flags to indicate if the payload is appended or mapped. If the payload + * is mapped it can be found in the buffer of the corresponding transfer + * ring message. + * ring_id: Transfer ring ID which required this message + * msg_id: Message ID specified in transfer ring entry + * len: Payload length + */ +struct bcm4377_completion_ring_entry { + u8 flags; + u8 _unk0; + __le16 ring_id; + __le16 msg_id; + __le32 len; + u8 _unk1[6]; +} __packed; +static_assert(sizeof(struct bcm4377_completion_ring_entry) == 0x10); + +enum bcm4377_control_message_type { + BCM4377_CONTROL_MSG_CREATE_XFER_RING = 1, + BCM4377_CONTROL_MSG_CREATE_COMPLETION_RING = 2, + BCM4377_CONTROL_MSG_DESTROY_XFER_RING = 3, + BCM4377_CONTROL_MSG_DESTROY_COMPLETION_RING = 4, +}; + +/* + * Control message used to create a completion ring + * + * msg_type: Must be BCM4377_CONTROL_MSG_CREATE_COMPLETION_RING + * header_size: Unknown, but probably reserved space in front of the entry + * footer_size: Number of 32 bit words reserved for payloads after the entry + * id/id_again: Completion ring index + * ring_iova: DMA address of the ring buffer + * n_elements: Number of elements inside the ring buffer + * msi: MSI index, doesn't work for all rings though and should be zero + * intmod_delay: Unknown delay + * intmod_bytes: Unknown + */ +struct bcm4377_create_completion_ring_msg { + u8 msg_type; + u8 header_size; + u8 footer_size; + u8 _unk0; + __le16 id; + __le16 id_again; + __le64 ring_iova; + __le16 n_elements; + __le32 unk; + u8 _unk1[6]; + __le16 msi; + __le16 intmod_delay; + __le32 intmod_bytes; + __le16 _unk2; + __le32 _unk3; + u8 _unk4[10]; +} __packed; +static_assert(sizeof(struct bcm4377_create_completion_ring_msg) == + BCM4377_CONTROL_MSG_SIZE); + +/* + * Control ring message used to destroy a completion ring + * + * msg_type: Must be BCM4377_CONTROL_MSG_DESTROY_COMPLETION_RING + * ring_id: Completion ring to be destroyed + */ +struct bcm4377_destroy_completion_ring_msg { + u8 msg_type; + u8 _pad0; + __le16 ring_id; + u8 _pad1[48]; +} __packed; +static_assert(sizeof(struct bcm4377_destroy_completion_ring_msg) == + BCM4377_CONTROL_MSG_SIZE); + +/* + * Control message used to create a transfer ring + * + * msg_type: Must be BCM4377_CONTROL_MSG_CREATE_XFER_RING + * header_size: Number of 32 bit words reserved for unknown content before the + * entry + * footer_size: Number of 32 bit words reserved for payloads after the entry + * ring_id/ring_id_again: Transfer ring index + * ring_iova: DMA address of the ring buffer + * n_elements: Number of elements inside the ring buffer + * completion_ring_id: Completion ring index for acknowledgements and events + * doorbell: Doorbell index used to notify device of new entries + * flags: Transfer ring flags + * - virtual: set if there is no associated shared memory and only the + * corresponding completion ring is used + * - sync: only set for the SCO rings + */ +struct bcm4377_create_transfer_ring_msg { + u8 msg_type; + u8 header_size; + u8 footer_size; + u8 _unk0; + __le16 ring_id; + __le16 ring_id_again; + __le64 ring_iova; + u8 _unk1[8]; + __le16 n_elements; + __le16 completion_ring_id; + __le16 doorbell; +#define BCM4377_XFER_RING_FLAG_VIRTUAL BIT(7) +#define BCM4377_XFER_RING_FLAG_SYNC BIT(8) + __le16 flags; + u8 _unk2[20]; +} __packed; +static_assert(sizeof(struct bcm4377_create_transfer_ring_msg) == + BCM4377_CONTROL_MSG_SIZE); + +/* + * Control ring message used to destroy a transfer ring + * + * msg_type: Must be BCM4377_CONTROL_MSG_DESTROY_XFER_RING + * ring_id: Transfer ring to be destroyed + */ +struct bcm4377_destroy_transfer_ring_msg { + u8 msg_type; + u8 _pad0; + __le16 ring_id; + u8 _pad1[48]; +} __packed; +static_assert(sizeof(struct bcm4377_destroy_transfer_ring_msg) == + BCM4377_CONTROL_MSG_SIZE); + +/* + * "Converged IPC" context struct used to make the device aware of all other + * shared memory structures. A pointer to this structure is configured inside a + * MMIO register. + * + * version: Protocol version, must be 2. + * size: Size of this structure, must be 0x68. + * enabled_caps: Enabled capabilities. Unknown bitfield but should be 2. + * peripheral_info_addr: DMA address for a 0x20 buffer to which the device will + * write unknown contents + * {completion,xfer}_ring_{tails,heads}_addr: DMA pointers to ring heads/tails + * n_completion_rings: Number of completion rings, the firmware only works if + * this is set to BCM4377_N_COMPLETION_RINGS. + * n_xfer_rings: Number of transfer rings, the firmware only works if + * this is set to BCM4377_N_TRANSFER_RINGS. + * control_completion_ring_addr: Control completion ring buffer DMA address + * control_xfer_ring_addr: Control transfer ring buffer DMA address + * control_xfer_ring_n_entries: Number of control transfer ring entries + * control_completion_ring_n_entries: Number of control completion ring entries + * control_xfer_ring_doorbell: Control transfer ring doorbell + * control_completion_ring_doorbell: Control completion ring doorbell, + * must be set to 0xffff + * control_xfer_ring_msi: Control completion ring MSI index, must be 0 + * control_completion_ring_msi: Control completion ring MSI index, must be 0. + * control_xfer_ring_header_size: Number of 32 bit words reserved in front of + * every control transfer ring entry + * control_xfer_ring_footer_size: Number of 32 bit words reserved after every + * control transfer ring entry + * control_completion_ring_header_size: Number of 32 bit words reserved in front + * of every control completion ring entry + * control_completion_ring_footer_size: Number of 32 bit words reserved after + * every control completion ring entry + * scratch_pad: Optional scratch pad DMA address + * scratch_pad_size: Scratch pad size + */ +struct bcm4377_context { + __le16 version; + __le16 size; + __le32 enabled_caps; + + __le64 peripheral_info_addr; + + /* ring heads and tails */ + __le64 completion_ring_heads_addr; + __le64 xfer_ring_tails_addr; + __le64 completion_ring_tails_addr; + __le64 xfer_ring_heads_addr; + __le16 n_completion_rings; + __le16 n_xfer_rings; + + /* control ring configuration */ + __le64 control_completion_ring_addr; + __le64 control_xfer_ring_addr; + __le16 control_xfer_ring_n_entries; + __le16 control_completion_ring_n_entries; + __le16 control_xfer_ring_doorbell; + __le16 control_completion_ring_doorbell; + __le16 control_xfer_ring_msi; + __le16 control_completion_ring_msi; + u8 control_xfer_ring_header_size; + u8 control_xfer_ring_footer_size; + u8 control_completion_ring_header_size; + u8 control_completion_ring_footer_size; + + __le16 _unk0; + __le16 _unk1; + + __le64 scratch_pad; + __le32 scratch_pad_size; + + __le32 _unk3; +} __packed; +static_assert(sizeof(struct bcm4377_context) == 0x68); + +#define BCM4378_CALIBRATION_CHUNK_SIZE 0xe6 +struct bcm4378_hci_send_calibration_cmd { + u8 unk; + __le16 blocks_left; + u8 data[BCM4378_CALIBRATION_CHUNK_SIZE]; +} __packed; + +#define BCM4378_PTB_CHUNK_SIZE 0xcf +struct bcm4378_hci_send_ptb_cmd { + __le16 blocks_left; + u8 data[BCM4378_PTB_CHUNK_SIZE]; +} __packed; + +/* + * Shared memory structure used to store the ring head and tail pointers. + */ +struct bcm4377_ring_state { + __le16 completion_ring_head[BCM4377_N_COMPLETION_RINGS]; + __le16 completion_ring_tail[BCM4377_N_COMPLETION_RINGS]; + __le16 xfer_ring_head[BCM4377_N_TRANSFER_RINGS]; + __le16 xfer_ring_tail[BCM4377_N_TRANSFER_RINGS]; +}; + +/* + * A transfer ring can be used in two configurations: + * 1) Send control or HCI messages to the device which are then acknowledged + * in the corresponding completion ring + * 2) Receiving HCI frames from the devices. In this case the transfer ring + * itself contains empty messages that are acknowledged once data is + * available from the device. If the payloads fit inside the footers + * of the completion ring the transfer ring can be configured to be + * virtual such that it has no ring buffer. + * + * ring_id: ring index hardcoded in the firmware + * doorbell: doorbell index to notify device of new entries + * payload_size: optional in-place payload size + * mapped_payload_size: optional out-of-place payload size + * completion_ring: index of corresponding completion ring + * n_entries: number of entries inside this ring + * generation: ring generation; incremented on hci_open to detect stale messages + * sync: set to true for SCO rings + * virtual: set to true if this ring has no entries and is just required to + * setup a corresponding completion ring for device->host messages + * d2h_buffers_only: set to true if this ring is only used to provide large + * buffers used by device->host messages in the completion + * ring + * allow_wait: allow to wait for messages to be acknowledged + * enabled: true once the ring has been created and can be used + * ring: ring buffer for entries (struct bcm4377_xfer_ring_entry) + * ring_dma: DMA address for ring entry buffer + * payloads: payload buffer for mapped_payload_size payloads + * payloads_dma:DMA address for payload buffer + * events: pointer to array of completions if waiting is allowed + * msgids: bitmap to keep track of used message ids + * lock: Spinlock to protect access to ring structurs used in the irq handler + */ +struct bcm4377_transfer_ring { + enum bcm4377_transfer_ring_id ring_id; + enum bcm4377_doorbell doorbell; + size_t payload_size; + size_t mapped_payload_size; + u8 completion_ring; + u16 n_entries; + u8 generation; + + bool sync; + bool virtual; + bool d2h_buffers_only; + bool allow_wait; + bool enabled; + + void *ring; + dma_addr_t ring_dma; + + void *payloads; + dma_addr_t payloads_dma; + + struct completion **events; + DECLARE_BITMAP(msgids, BCM4377_MAX_RING_SIZE); + spinlock_t lock; +}; + +/* + * A completion ring can be either used to either acknowledge messages sent in + * the corresponding transfer ring or to receive messages associated with the + * transfer ring. When used to receive messages the transfer ring either + * has no ring buffer and is only advanced ("virtual transfer ring") or it + * only contains empty DMA buffers to be used for the payloads. + * + * ring_id: completion ring id, hardcoded in firmware + * payload_size: optional payload size after each entry + * delay: unknown delay + * n_entries: number of entries in this ring + * enabled: true once the ring has been created and can be used + * ring: ring buffer for entries (struct bcm4377_completion_ring_entry) + * ring_dma: DMA address of ring buffer + * transfer_rings: bitmap of corresponding transfer ring ids + */ +struct bcm4377_completion_ring { + enum bcm4377_completion_ring_id ring_id; + u16 payload_size; + u16 delay; + u16 n_entries; + bool enabled; + + void *ring; + dma_addr_t ring_dma; + + unsigned long transfer_rings; +}; + +struct bcm4377_data; + +/* + * Chip-specific configuration struct + * + * id: Chip id (e.g. 0x4377 for BCM4377) + * otp_offset: Offset to the start of the OTP inside BAR0 + * bar0_window1: Backplane address mapped to the first window in BAR0 + * bar0_window2: Backplane address mapped to the second window in BAR0 + * bar0_core2_window2: Optional backplane address mapped to the second core's + * second window in BAR0 + * has_bar0_core2_window2: Set to true if this chip requires the second core's + * second window to be configured + * clear_pciecfg_subsystem_ctrl_bit19: Set to true if bit 19 in the + * vendor-specific subsystem control + * register has to be cleared + * disable_aspm: Set to true if ASPM must be disabled due to hardware errata + * broken_ext_scan: Set to true if the chip erroneously claims to support + * extended scanning + * broken_mws_transport_config: Set to true if the chip erroneously claims to + * support MWS Transport Configuration + * send_calibration: Optional callback to send calibration data + * send_ptb: Callback to send "PTB" regulatory/calibration data + */ +struct bcm4377_hw { + unsigned int id; + + u32 otp_offset; + + u32 bar0_window1; + u32 bar0_window2; + u32 bar0_core2_window2; + + unsigned long has_bar0_core2_window2 : 1; + unsigned long clear_pciecfg_subsystem_ctrl_bit19 : 1; + unsigned long disable_aspm : 1; + unsigned long broken_ext_scan : 1; + unsigned long broken_mws_transport_config : 1; + + int (*send_calibration)(struct bcm4377_data *bcm4377); + int (*send_ptb)(struct bcm4377_data *bcm4377, + const struct firmware *fw); +}; + +static const struct bcm4377_hw bcm4377_hw_variants[]; +static const struct dmi_system_id bcm4377_dmi_board_table[]; + +/* + * Private struct associated with each device containing global state + * + * pdev: Pointer to associated struct pci_dev + * hdev: Pointer to associated strucy hci_dev + * bar0: iomem pointing to BAR0 + * bar1: iomem pointing to BAR2 + * bootstage: Current value of the bootstage + * rti_status: Current "RTI" status value + * hw: Pointer to chip-specific struct bcm4377_hw + * taurus_cal_blob: "Taurus" calibration blob used for some chips + * taurus_cal_size: "Taurus" calibration blob size + * taurus_beamforming_cal_blob: "Taurus" beamforming calibration blob used for + * some chips + * taurus_beamforming_cal_size: "Taurus" beamforming calibration blob size + * stepping: Chip stepping read from OTP; used for firmware selection + * vendor: Antenna vendor read from OTP; used for firmware selection + * board_type: Board type from FDT or DMI match; used for firmware selection + * event: Event for changed bootstage or rti_status; used for booting firmware + * ctx: "Converged IPC" context + * ctx_dma: "Converged IPC" context DMA address + * ring_state: Shared memory buffer containing ring head and tail indexes + * ring_state_dma: DMA address for ring_state + * {control,hci_acl,sco}_ack_ring: Completion rings used to acknowledge messages + * {hci_acl,sco}_event_ring: Completion rings used for device->host messages + * control_h2d_ring: Transfer ring used for control messages + * {hci,sco,acl}_h2d_ring: Transfer ring used to transfer HCI frames + * {hci,sco,acl}_d2h_ring: Transfer ring used to receive HCI frames in the + * corresponding completion ring + */ +struct bcm4377_data { + struct pci_dev *pdev; + struct hci_dev *hdev; + + void __iomem *bar0; + void __iomem *bar2; + + u32 bootstage; + u32 rti_status; + + const struct bcm4377_hw *hw; + + const void *taurus_cal_blob; + int taurus_cal_size; + const void *taurus_beamforming_cal_blob; + int taurus_beamforming_cal_size; + + char stepping[BCM4377_OTP_MAX_PARAM_LEN]; + char vendor[BCM4377_OTP_MAX_PARAM_LEN]; + const char *board_type; + + struct completion event; + + struct bcm4377_context *ctx; + dma_addr_t ctx_dma; + + struct bcm4377_ring_state *ring_state; + dma_addr_t ring_state_dma; + + /* + * The HCI and ACL rings have to be merged because this structure is + * hardcoded in the firmware. + */ + struct bcm4377_completion_ring control_ack_ring; + struct bcm4377_completion_ring hci_acl_ack_ring; + struct bcm4377_completion_ring hci_acl_event_ring; + struct bcm4377_completion_ring sco_ack_ring; + struct bcm4377_completion_ring sco_event_ring; + + struct bcm4377_transfer_ring control_h2d_ring; + struct bcm4377_transfer_ring hci_h2d_ring; + struct bcm4377_transfer_ring hci_d2h_ring; + struct bcm4377_transfer_ring sco_h2d_ring; + struct bcm4377_transfer_ring sco_d2h_ring; + struct bcm4377_transfer_ring acl_h2d_ring; + struct bcm4377_transfer_ring acl_d2h_ring; +}; + +static void bcm4377_ring_doorbell(struct bcm4377_data *bcm4377, u8 doorbell, + u16 val) +{ + u32 db = 0; + + db |= FIELD_PREP(BCM4377_BAR0_DOORBELL_VALUE, val); + db |= FIELD_PREP(BCM4377_BAR0_DOORBELL_IDX, doorbell); + db |= BCM4377_BAR0_DOORBELL_RING; + + dev_dbg(&bcm4377->pdev->dev, "write %d to doorbell #%d (0x%x)\n", val, + doorbell, db); + iowrite32(db, bcm4377->bar0 + BCM4377_BAR0_DOORBELL); +} + +static int bcm4377_extract_msgid(struct bcm4377_data *bcm4377, + struct bcm4377_transfer_ring *ring, + u16 raw_msgid, u8 *msgid) +{ + u8 generation = FIELD_GET(BCM4377_MSGID_GENERATION, raw_msgid); + *msgid = FIELD_GET(BCM4377_MSGID_ID, raw_msgid); + + if (generation != ring->generation) { + dev_warn( + &bcm4377->pdev->dev, + "invalid message generation %d should be %d in entry for ring %d\n", + generation, ring->generation, ring->ring_id); + return -EINVAL; + } + + if (*msgid >= ring->n_entries) { + dev_warn(&bcm4377->pdev->dev, + "invalid message id in entry for ring %d: %d > %d\n", + ring->ring_id, *msgid, ring->n_entries); + return -EINVAL; + } + + return 0; +} + +static void bcm4377_handle_event(struct bcm4377_data *bcm4377, + struct bcm4377_transfer_ring *ring, + u16 raw_msgid, u8 entry_flags, u8 type, + void *payload, size_t len) +{ + struct sk_buff *skb; + u16 head; + u8 msgid; + unsigned long flags; + + spin_lock_irqsave(&ring->lock, flags); + if (!ring->enabled) { + dev_warn(&bcm4377->pdev->dev, + "event for disabled transfer ring %d\n", + ring->ring_id); + goto out; + } + + if (ring->d2h_buffers_only && + entry_flags & BCM4377_XFER_RING_FLAG_PAYLOAD_MAPPED) { + if (bcm4377_extract_msgid(bcm4377, ring, raw_msgid, &msgid)) + goto out; + + if (len > ring->mapped_payload_size) { + dev_warn( + &bcm4377->pdev->dev, + "invalid payload len in event for ring %d: %zu > %zu\n", + ring->ring_id, len, ring->mapped_payload_size); + goto out; + } + + payload = ring->payloads + msgid * ring->mapped_payload_size; + } + + skb = bt_skb_alloc(len, GFP_ATOMIC); + if (!skb) + goto out; + + memcpy(skb_put(skb, len), payload, len); + hci_skb_pkt_type(skb) = type; + hci_recv_frame(bcm4377->hdev, skb); + +out: + head = le16_to_cpu(bcm4377->ring_state->xfer_ring_head[ring->ring_id]); + head = (head + 1) % ring->n_entries; + bcm4377->ring_state->xfer_ring_head[ring->ring_id] = cpu_to_le16(head); + + bcm4377_ring_doorbell(bcm4377, ring->doorbell, head); + + spin_unlock_irqrestore(&ring->lock, flags); +} + +static void bcm4377_handle_ack(struct bcm4377_data *bcm4377, + struct bcm4377_transfer_ring *ring, + u16 raw_msgid) +{ + unsigned long flags; + u8 msgid; + + spin_lock_irqsave(&ring->lock, flags); + + if (bcm4377_extract_msgid(bcm4377, ring, raw_msgid, &msgid)) + goto unlock; + + if (!test_bit(msgid, ring->msgids)) { + dev_warn( + &bcm4377->pdev->dev, + "invalid message id in ack for ring %d: %d is not used\n", + ring->ring_id, msgid); + goto unlock; + } + + if (ring->allow_wait && ring->events[msgid]) { + complete(ring->events[msgid]); + ring->events[msgid] = NULL; + } + + bitmap_release_region(ring->msgids, msgid, ring->n_entries); + +unlock: + spin_unlock_irqrestore(&ring->lock, flags); +} + +static void bcm4377_handle_completion(struct bcm4377_data *bcm4377, + struct bcm4377_completion_ring *ring, + u16 pos) +{ + struct bcm4377_completion_ring_entry *entry; + u16 msg_id, transfer_ring; + size_t entry_size, data_len; + void *data; + + if (pos >= ring->n_entries) { + dev_warn(&bcm4377->pdev->dev, + "invalid offset %d for completion ring %d\n", pos, + ring->ring_id); + return; + } + + entry_size = sizeof(*entry) + ring->payload_size; + entry = ring->ring + pos * entry_size; + data = ring->ring + pos * entry_size + sizeof(*entry); + data_len = le32_to_cpu(entry->len); + msg_id = le16_to_cpu(entry->msg_id); + transfer_ring = le16_to_cpu(entry->ring_id); + + if ((ring->transfer_rings & BIT(transfer_ring)) == 0) { + dev_warn( + &bcm4377->pdev->dev, + "invalid entry at offset %d for transfer ring %d in completion ring %d\n", + pos, transfer_ring, ring->ring_id); + return; + } + + dev_dbg(&bcm4377->pdev->dev, + "entry in completion ring %d for transfer ring %d with msg_id %d\n", + ring->ring_id, transfer_ring, msg_id); + + switch (transfer_ring) { + case BCM4377_XFER_RING_CONTROL: + bcm4377_handle_ack(bcm4377, &bcm4377->control_h2d_ring, msg_id); + break; + case BCM4377_XFER_RING_HCI_H2D: + bcm4377_handle_ack(bcm4377, &bcm4377->hci_h2d_ring, msg_id); + break; + case BCM4377_XFER_RING_SCO_H2D: + bcm4377_handle_ack(bcm4377, &bcm4377->sco_h2d_ring, msg_id); + break; + case BCM4377_XFER_RING_ACL_H2D: + bcm4377_handle_ack(bcm4377, &bcm4377->acl_h2d_ring, msg_id); + break; + + case BCM4377_XFER_RING_HCI_D2H: + bcm4377_handle_event(bcm4377, &bcm4377->hci_d2h_ring, msg_id, + entry->flags, HCI_EVENT_PKT, data, + data_len); + break; + case BCM4377_XFER_RING_SCO_D2H: + bcm4377_handle_event(bcm4377, &bcm4377->sco_d2h_ring, msg_id, + entry->flags, HCI_SCODATA_PKT, data, + data_len); + break; + case BCM4377_XFER_RING_ACL_D2H: + bcm4377_handle_event(bcm4377, &bcm4377->acl_d2h_ring, msg_id, + entry->flags, HCI_ACLDATA_PKT, data, + data_len); + break; + + default: + dev_warn( + &bcm4377->pdev->dev, + "entry in completion ring %d for unknown transfer ring %d with msg_id %d\n", + ring->ring_id, transfer_ring, msg_id); + } +} + +static void bcm4377_poll_completion_ring(struct bcm4377_data *bcm4377, + struct bcm4377_completion_ring *ring) +{ + u16 tail; + __le16 *heads = bcm4377->ring_state->completion_ring_head; + __le16 *tails = bcm4377->ring_state->completion_ring_tail; + + if (!ring->enabled) + return; + + tail = le16_to_cpu(tails[ring->ring_id]); + dev_dbg(&bcm4377->pdev->dev, + "completion ring #%d: head: %d, tail: %d\n", ring->ring_id, + le16_to_cpu(heads[ring->ring_id]), tail); + + while (tail != le16_to_cpu(READ_ONCE(heads[ring->ring_id]))) { + /* + * ensure the CPU doesn't speculate through the comparison. + * otherwise it might already read the (empty) queue entry + * before the updated head has been loaded and checked. + */ + dma_rmb(); + + bcm4377_handle_completion(bcm4377, ring, tail); + + tail = (tail + 1) % ring->n_entries; + tails[ring->ring_id] = cpu_to_le16(tail); + } +} + +static irqreturn_t bcm4377_irq(int irq, void *data) +{ + struct bcm4377_data *bcm4377 = data; + u32 bootstage, rti_status; + + bootstage = ioread32(bcm4377->bar2 + BCM4377_BAR2_BOOTSTAGE); + rti_status = ioread32(bcm4377->bar2 + BCM4377_BAR2_RTI_STATUS); + + if (bootstage != bcm4377->bootstage || + rti_status != bcm4377->rti_status) { + dev_dbg(&bcm4377->pdev->dev, + "bootstage = %d -> %d, rti state = %d -> %d\n", + bcm4377->bootstage, bootstage, bcm4377->rti_status, + rti_status); + complete(&bcm4377->event); + bcm4377->bootstage = bootstage; + bcm4377->rti_status = rti_status; + } + + if (rti_status > 2) + dev_err(&bcm4377->pdev->dev, "RTI status is %d\n", rti_status); + + bcm4377_poll_completion_ring(bcm4377, &bcm4377->control_ack_ring); + bcm4377_poll_completion_ring(bcm4377, &bcm4377->hci_acl_event_ring); + bcm4377_poll_completion_ring(bcm4377, &bcm4377->hci_acl_ack_ring); + bcm4377_poll_completion_ring(bcm4377, &bcm4377->sco_ack_ring); + bcm4377_poll_completion_ring(bcm4377, &bcm4377->sco_event_ring); + + return IRQ_HANDLED; +} + +static int bcm4377_enqueue(struct bcm4377_data *bcm4377, + struct bcm4377_transfer_ring *ring, void *data, + size_t len, bool wait) +{ + unsigned long flags; + struct bcm4377_xfer_ring_entry *entry; + void *payload; + size_t offset; + u16 head, tail, new_head; + u16 raw_msgid; + int ret, msgid; + DECLARE_COMPLETION_ONSTACK(event); + + if (len > ring->payload_size && len > ring->mapped_payload_size) { + dev_warn( + &bcm4377->pdev->dev, + "payload len %zu is too large for ring %d (max is %zu or %zu)\n", + len, ring->ring_id, ring->payload_size, + ring->mapped_payload_size); + return -EINVAL; + } + if (wait && !ring->allow_wait) + return -EINVAL; + if (ring->virtual) + return -EINVAL; + + spin_lock_irqsave(&ring->lock, flags); + + head = le16_to_cpu(bcm4377->ring_state->xfer_ring_head[ring->ring_id]); + tail = le16_to_cpu(bcm4377->ring_state->xfer_ring_tail[ring->ring_id]); + + new_head = (head + 1) % ring->n_entries; + + if (new_head == tail) { + dev_warn(&bcm4377->pdev->dev, + "can't send message because ring %d is full\n", + ring->ring_id); + ret = -EINVAL; + goto out; + } + + msgid = bitmap_find_free_region(ring->msgids, ring->n_entries, 0); + if (msgid < 0) { + dev_warn(&bcm4377->pdev->dev, + "can't find message id for ring %d\n", ring->ring_id); + ret = -EINVAL; + goto out; + } + + raw_msgid = FIELD_PREP(BCM4377_MSGID_GENERATION, ring->generation); + raw_msgid |= FIELD_PREP(BCM4377_MSGID_ID, msgid); + + offset = head * (sizeof(*entry) + ring->payload_size); + entry = ring->ring + offset; + + memset(entry, 0, sizeof(*entry)); + entry->id = cpu_to_le16(raw_msgid); + entry->len = cpu_to_le16(len); + + if (len <= ring->payload_size) { + entry->flags = BCM4377_XFER_RING_FLAG_PAYLOAD_IN_FOOTER; + payload = ring->ring + offset + sizeof(*entry); + } else { + entry->flags = BCM4377_XFER_RING_FLAG_PAYLOAD_MAPPED; + entry->payload = cpu_to_le64(ring->payloads_dma + + msgid * ring->mapped_payload_size); + payload = ring->payloads + msgid * ring->mapped_payload_size; + } + + memcpy(payload, data, len); + + if (wait) + ring->events[msgid] = &event; + + /* + * The 4377 chips stop responding to any commands as soon as they + * have been idle for a while. Poking the sleep control register here + * makes them come alive again. + */ + iowrite32(BCM4377_BAR0_SLEEP_CONTROL_AWAKE, + bcm4377->bar0 + BCM4377_BAR0_SLEEP_CONTROL); + + dev_dbg(&bcm4377->pdev->dev, + "updating head for transfer queue #%d to %d\n", ring->ring_id, + new_head); + bcm4377->ring_state->xfer_ring_head[ring->ring_id] = + cpu_to_le16(new_head); + + if (!ring->sync) + bcm4377_ring_doorbell(bcm4377, ring->doorbell, new_head); + ret = 0; + +out: + spin_unlock_irqrestore(&ring->lock, flags); + + if (ret == 0 && wait) { + ret = wait_for_completion_interruptible_timeout( + &event, BCM4377_TIMEOUT); + if (ret == 0) + ret = -ETIMEDOUT; + else if (ret > 0) + ret = 0; + + spin_lock_irqsave(&ring->lock, flags); + ring->events[msgid] = NULL; + spin_unlock_irqrestore(&ring->lock, flags); + } + + return ret; +} + +static int bcm4377_create_completion_ring(struct bcm4377_data *bcm4377, + struct bcm4377_completion_ring *ring) +{ + struct bcm4377_create_completion_ring_msg msg; + int ret; + + if (ring->enabled) { + dev_warn(&bcm4377->pdev->dev, + "completion ring %d already enabled\n", ring->ring_id); + return 0; + } + + memset(ring->ring, 0, + ring->n_entries * (sizeof(struct bcm4377_completion_ring_entry) + + ring->payload_size)); + memset(&msg, 0, sizeof(msg)); + msg.msg_type = BCM4377_CONTROL_MSG_CREATE_COMPLETION_RING; + msg.id = cpu_to_le16(ring->ring_id); + msg.id_again = cpu_to_le16(ring->ring_id); + msg.ring_iova = cpu_to_le64(ring->ring_dma); + msg.n_elements = cpu_to_le16(ring->n_entries); + msg.intmod_bytes = cpu_to_le32(0xffffffff); + msg.unk = cpu_to_le32(0xffffffff); + msg.intmod_delay = cpu_to_le16(ring->delay); + msg.footer_size = ring->payload_size / 4; + + ret = bcm4377_enqueue(bcm4377, &bcm4377->control_h2d_ring, &msg, + sizeof(msg), true); + if (!ret) + ring->enabled = true; + + return ret; +} + +static int bcm4377_destroy_completion_ring(struct bcm4377_data *bcm4377, + struct bcm4377_completion_ring *ring) +{ + struct bcm4377_destroy_completion_ring_msg msg; + int ret; + + memset(&msg, 0, sizeof(msg)); + msg.msg_type = BCM4377_CONTROL_MSG_DESTROY_COMPLETION_RING; + msg.ring_id = cpu_to_le16(ring->ring_id); + + ret = bcm4377_enqueue(bcm4377, &bcm4377->control_h2d_ring, &msg, + sizeof(msg), true); + if (ret) + dev_warn(&bcm4377->pdev->dev, + "failed to destroy completion ring %d\n", + ring->ring_id); + + ring->enabled = false; + return ret; +} + +static int bcm4377_create_transfer_ring(struct bcm4377_data *bcm4377, + struct bcm4377_transfer_ring *ring) +{ + struct bcm4377_create_transfer_ring_msg msg; + u16 flags = 0; + int ret, i; + unsigned long spinlock_flags; + + if (ring->virtual) + flags |= BCM4377_XFER_RING_FLAG_VIRTUAL; + if (ring->sync) + flags |= BCM4377_XFER_RING_FLAG_SYNC; + + spin_lock_irqsave(&ring->lock, spinlock_flags); + memset(&msg, 0, sizeof(msg)); + msg.msg_type = BCM4377_CONTROL_MSG_CREATE_XFER_RING; + msg.ring_id = cpu_to_le16(ring->ring_id); + msg.ring_id_again = cpu_to_le16(ring->ring_id); + msg.ring_iova = cpu_to_le64(ring->ring_dma); + msg.n_elements = cpu_to_le16(ring->n_entries); + msg.completion_ring_id = cpu_to_le16(ring->completion_ring); + msg.doorbell = cpu_to_le16(ring->doorbell); + msg.flags = cpu_to_le16(flags); + msg.footer_size = ring->payload_size / 4; + + bcm4377->ring_state->xfer_ring_head[ring->ring_id] = 0; + bcm4377->ring_state->xfer_ring_tail[ring->ring_id] = 0; + ring->generation++; + spin_unlock_irqrestore(&ring->lock, spinlock_flags); + + ret = bcm4377_enqueue(bcm4377, &bcm4377->control_h2d_ring, &msg, + sizeof(msg), true); + + spin_lock_irqsave(&ring->lock, spinlock_flags); + + if (ring->d2h_buffers_only) { + for (i = 0; i < ring->n_entries; ++i) { + struct bcm4377_xfer_ring_entry *entry = + ring->ring + i * sizeof(*entry); + u16 raw_msgid = FIELD_PREP(BCM4377_MSGID_GENERATION, + ring->generation); + raw_msgid |= FIELD_PREP(BCM4377_MSGID_ID, i); + + memset(entry, 0, sizeof(*entry)); + entry->id = cpu_to_le16(raw_msgid); + entry->len = cpu_to_le16(ring->mapped_payload_size); + entry->flags = BCM4377_XFER_RING_FLAG_PAYLOAD_MAPPED; + entry->payload = + cpu_to_le64(ring->payloads_dma + + i * ring->mapped_payload_size); + } + } + + /* + * send some messages if this is a device->host ring to allow the device + * to reply by acknowledging them in the completion ring + */ + if (ring->virtual || ring->d2h_buffers_only) { + bcm4377->ring_state->xfer_ring_head[ring->ring_id] = + cpu_to_le16(0xf); + bcm4377_ring_doorbell(bcm4377, ring->doorbell, 0xf); + } + + ring->enabled = true; + spin_unlock_irqrestore(&ring->lock, spinlock_flags); + + return ret; +} + +static int bcm4377_destroy_transfer_ring(struct bcm4377_data *bcm4377, + struct bcm4377_transfer_ring *ring) +{ + struct bcm4377_destroy_transfer_ring_msg msg; + int ret; + + memset(&msg, 0, sizeof(msg)); + msg.msg_type = BCM4377_CONTROL_MSG_DESTROY_XFER_RING; + msg.ring_id = cpu_to_le16(ring->ring_id); + + ret = bcm4377_enqueue(bcm4377, &bcm4377->control_h2d_ring, &msg, + sizeof(msg), true); + if (ret) + dev_warn(&bcm4377->pdev->dev, + "failed to destroy transfer ring %d\n", ring->ring_id); + + ring->enabled = false; + return ret; +} + +static int __bcm4378_send_calibration_chunk(struct bcm4377_data *bcm4377, + const void *data, size_t data_len, + u16 blocks_left) +{ + struct bcm4378_hci_send_calibration_cmd cmd; + struct sk_buff *skb; + + if (data_len > sizeof(cmd.data)) + return -EINVAL; + + memset(&cmd, 0, sizeof(cmd)); + cmd.unk = 0x03; + cmd.blocks_left = cpu_to_le16(blocks_left); + memcpy(cmd.data, data, data_len); + + skb = __hci_cmd_sync(bcm4377->hdev, 0xfd97, sizeof(cmd), &cmd, + HCI_INIT_TIMEOUT); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + kfree_skb(skb); + return 0; +} + +static int __bcm4378_send_calibration(struct bcm4377_data *bcm4377, + const void *data, size_t data_size) +{ + int ret; + size_t i, left, transfer_len; + size_t blocks = + DIV_ROUND_UP(data_size, (size_t)BCM4378_CALIBRATION_CHUNK_SIZE); + + if (!data) { + dev_err(&bcm4377->pdev->dev, + "no calibration data available.\n"); + return -ENOENT; + } + + for (i = 0, left = data_size; i < blocks; ++i, left -= transfer_len) { + transfer_len = + min_t(size_t, left, BCM4378_CALIBRATION_CHUNK_SIZE); + + ret = __bcm4378_send_calibration_chunk( + bcm4377, data + i * BCM4378_CALIBRATION_CHUNK_SIZE, + transfer_len, blocks - i - 1); + if (ret) { + dev_err(&bcm4377->pdev->dev, + "send calibration chunk failed with %d\n", ret); + return ret; + } + } + + return 0; +} + +static int bcm4378_send_calibration(struct bcm4377_data *bcm4377) +{ + if ((strcmp(bcm4377->stepping, "b1") == 0) || + strcmp(bcm4377->stepping, "b3") == 0) + return __bcm4378_send_calibration( + bcm4377, bcm4377->taurus_beamforming_cal_blob, + bcm4377->taurus_beamforming_cal_size); + else + return __bcm4378_send_calibration(bcm4377, + bcm4377->taurus_cal_blob, + bcm4377->taurus_cal_size); +} + +static int bcm4387_send_calibration(struct bcm4377_data *bcm4377) +{ + if (strcmp(bcm4377->stepping, "c2") == 0) + return __bcm4378_send_calibration( + bcm4377, bcm4377->taurus_beamforming_cal_blob, + bcm4377->taurus_beamforming_cal_size); + else + return __bcm4378_send_calibration(bcm4377, + bcm4377->taurus_cal_blob, + bcm4377->taurus_cal_size); +} + +static const struct firmware *bcm4377_request_blob(struct bcm4377_data *bcm4377, + const char *suffix) +{ + const struct firmware *fw; + char name0[64], name1[64]; + int ret; + + snprintf(name0, sizeof(name0), "brcm/brcmbt%04x%s-%s-%s.%s", + bcm4377->hw->id, bcm4377->stepping, bcm4377->board_type, + bcm4377->vendor, suffix); + snprintf(name1, sizeof(name1), "brcm/brcmbt%04x%s-%s.%s", + bcm4377->hw->id, bcm4377->stepping, bcm4377->board_type, + suffix); + dev_dbg(&bcm4377->pdev->dev, "Trying to load firmware: '%s' or '%s'\n", + name0, name1); + + ret = firmware_request_nowarn(&fw, name0, &bcm4377->pdev->dev); + if (!ret) + return fw; + ret = firmware_request_nowarn(&fw, name1, &bcm4377->pdev->dev); + if (!ret) + return fw; + + dev_err(&bcm4377->pdev->dev, + "Unable to load firmware; tried '%s' and '%s'\n", name0, name1); + return NULL; +} + +static int bcm4377_send_ptb(struct bcm4377_data *bcm4377, + const struct firmware *fw) +{ + struct sk_buff *skb; + + skb = __hci_cmd_sync(bcm4377->hdev, 0xfd98, fw->size, fw->data, + HCI_INIT_TIMEOUT); + /* + * This command seems to always fail on more recent firmware versions + * (even in traces taken from the macOS driver). It's unclear why this + * happens but because the PTB file contains calibration and/or + * regulatory data and may be required on older firmware we still try to + * send it here just in case and just ignore if it fails. + */ + if (!IS_ERR(skb)) + kfree_skb(skb); + return 0; +} + +static int bcm4378_send_ptb_chunk(struct bcm4377_data *bcm4377, + const void *data, size_t data_len, + u16 blocks_left) +{ + struct bcm4378_hci_send_ptb_cmd cmd; + struct sk_buff *skb; + + if (data_len > BCM4378_PTB_CHUNK_SIZE) + return -EINVAL; + + memset(&cmd, 0, sizeof(cmd)); + cmd.blocks_left = cpu_to_le16(blocks_left); + memcpy(cmd.data, data, data_len); + + skb = __hci_cmd_sync(bcm4377->hdev, 0xfe0d, sizeof(cmd), &cmd, + HCI_INIT_TIMEOUT); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + kfree_skb(skb); + return 0; +} + +static int bcm4378_send_ptb(struct bcm4377_data *bcm4377, + const struct firmware *fw) +{ + size_t chunks = DIV_ROUND_UP(fw->size, (size_t)BCM4378_PTB_CHUNK_SIZE); + size_t i, left, transfer_len; + int ret; + + for (i = 0, left = fw->size; i < chunks; ++i, left -= transfer_len) { + transfer_len = min_t(size_t, left, BCM4378_PTB_CHUNK_SIZE); + + dev_dbg(&bcm4377->pdev->dev, "sending ptb chunk %zu/%zu\n", + i + 1, chunks); + ret = bcm4378_send_ptb_chunk( + bcm4377, fw->data + i * BCM4378_PTB_CHUNK_SIZE, + transfer_len, chunks - i - 1); + if (ret) { + dev_err(&bcm4377->pdev->dev, + "sending ptb chunk %zu failed (%d)", i, ret); + return ret; + } + } + + return 0; +} + +static int bcm4377_hci_open(struct hci_dev *hdev) +{ + struct bcm4377_data *bcm4377 = hci_get_drvdata(hdev); + int ret; + + dev_dbg(&bcm4377->pdev->dev, "creating rings\n"); + + ret = bcm4377_create_completion_ring(bcm4377, + &bcm4377->hci_acl_ack_ring); + if (ret) + return ret; + ret = bcm4377_create_completion_ring(bcm4377, + &bcm4377->hci_acl_event_ring); + if (ret) + goto destroy_hci_acl_ack; + ret = bcm4377_create_completion_ring(bcm4377, &bcm4377->sco_ack_ring); + if (ret) + goto destroy_hci_acl_event; + ret = bcm4377_create_completion_ring(bcm4377, &bcm4377->sco_event_ring); + if (ret) + goto destroy_sco_ack; + dev_dbg(&bcm4377->pdev->dev, + "all completion rings successfully created!\n"); + + ret = bcm4377_create_transfer_ring(bcm4377, &bcm4377->hci_h2d_ring); + if (ret) + goto destroy_sco_event; + ret = bcm4377_create_transfer_ring(bcm4377, &bcm4377->hci_d2h_ring); + if (ret) + goto destroy_hci_h2d; + ret = bcm4377_create_transfer_ring(bcm4377, &bcm4377->sco_h2d_ring); + if (ret) + goto destroy_hci_d2h; + ret = bcm4377_create_transfer_ring(bcm4377, &bcm4377->sco_d2h_ring); + if (ret) + goto destroy_sco_h2d; + ret = bcm4377_create_transfer_ring(bcm4377, &bcm4377->acl_h2d_ring); + if (ret) + goto destroy_sco_d2h; + ret = bcm4377_create_transfer_ring(bcm4377, &bcm4377->acl_d2h_ring); + if (ret) + goto destroy_acl_h2d; + dev_dbg(&bcm4377->pdev->dev, + "all transfer rings successfully created!\n"); + + return 0; + +destroy_acl_h2d: + bcm4377_destroy_transfer_ring(bcm4377, &bcm4377->acl_h2d_ring); +destroy_sco_d2h: + bcm4377_destroy_transfer_ring(bcm4377, &bcm4377->sco_d2h_ring); +destroy_sco_h2d: + bcm4377_destroy_transfer_ring(bcm4377, &bcm4377->sco_h2d_ring); +destroy_hci_d2h: + bcm4377_destroy_transfer_ring(bcm4377, &bcm4377->hci_h2d_ring); +destroy_hci_h2d: + bcm4377_destroy_transfer_ring(bcm4377, &bcm4377->hci_d2h_ring); +destroy_sco_event: + bcm4377_destroy_completion_ring(bcm4377, &bcm4377->sco_event_ring); +destroy_sco_ack: + bcm4377_destroy_completion_ring(bcm4377, &bcm4377->sco_ack_ring); +destroy_hci_acl_event: + bcm4377_destroy_completion_ring(bcm4377, &bcm4377->hci_acl_event_ring); +destroy_hci_acl_ack: + bcm4377_destroy_completion_ring(bcm4377, &bcm4377->hci_acl_ack_ring); + + dev_err(&bcm4377->pdev->dev, "Creating rings failed with %d\n", ret); + return ret; +} + +static int bcm4377_hci_close(struct hci_dev *hdev) +{ + struct bcm4377_data *bcm4377 = hci_get_drvdata(hdev); + + dev_dbg(&bcm4377->pdev->dev, "destroying rings in hci_close\n"); + + bcm4377_destroy_transfer_ring(bcm4377, &bcm4377->acl_d2h_ring); + bcm4377_destroy_transfer_ring(bcm4377, &bcm4377->acl_h2d_ring); + bcm4377_destroy_transfer_ring(bcm4377, &bcm4377->sco_d2h_ring); + bcm4377_destroy_transfer_ring(bcm4377, &bcm4377->sco_h2d_ring); + bcm4377_destroy_transfer_ring(bcm4377, &bcm4377->hci_d2h_ring); + bcm4377_destroy_transfer_ring(bcm4377, &bcm4377->hci_h2d_ring); + + bcm4377_destroy_completion_ring(bcm4377, &bcm4377->sco_event_ring); + bcm4377_destroy_completion_ring(bcm4377, &bcm4377->sco_ack_ring); + bcm4377_destroy_completion_ring(bcm4377, &bcm4377->hci_acl_event_ring); + bcm4377_destroy_completion_ring(bcm4377, &bcm4377->hci_acl_ack_ring); + + return 0; +} + +static bool bcm4377_is_valid_bdaddr(struct bcm4377_data *bcm4377, + bdaddr_t *addr) +{ + if (addr->b[0] != 0x93) + return true; + if (addr->b[1] != 0x76) + return true; + if (addr->b[2] != 0x00) + return true; + if (addr->b[4] != (bcm4377->hw->id & 0xff)) + return true; + if (addr->b[5] != (bcm4377->hw->id >> 8)) + return true; + return false; +} + +static int bcm4377_check_bdaddr(struct bcm4377_data *bcm4377) +{ + struct hci_rp_read_bd_addr *bda; + struct sk_buff *skb; + + skb = __hci_cmd_sync(bcm4377->hdev, HCI_OP_READ_BD_ADDR, 0, NULL, + HCI_INIT_TIMEOUT); + if (IS_ERR(skb)) { + int err = PTR_ERR(skb); + + dev_err(&bcm4377->pdev->dev, "HCI_OP_READ_BD_ADDR failed (%d)", + err); + return err; + } + + if (skb->len != sizeof(*bda)) { + dev_err(&bcm4377->pdev->dev, + "HCI_OP_READ_BD_ADDR reply length invalid"); + kfree_skb(skb); + return -EIO; + } + + bda = (struct hci_rp_read_bd_addr *)skb->data; + if (!bcm4377_is_valid_bdaddr(bcm4377, &bda->bdaddr)) + set_bit(HCI_QUIRK_INVALID_BDADDR, &bcm4377->hdev->quirks); + + kfree_skb(skb); + return 0; +} + +static int bcm4377_hci_setup(struct hci_dev *hdev) +{ + struct bcm4377_data *bcm4377 = hci_get_drvdata(hdev); + const struct firmware *fw; + int ret; + + if (bcm4377->hw->send_calibration) { + ret = bcm4377->hw->send_calibration(bcm4377); + if (ret) + return ret; + } + + fw = bcm4377_request_blob(bcm4377, "ptb"); + if (!fw) { + dev_err(&bcm4377->pdev->dev, "failed to load PTB data"); + return -ENOENT; + } + + ret = bcm4377->hw->send_ptb(bcm4377, fw); + release_firmware(fw); + if (ret) + return ret; + + return bcm4377_check_bdaddr(bcm4377); +} + +static int bcm4377_hci_send_frame(struct hci_dev *hdev, struct sk_buff *skb) +{ + struct bcm4377_data *bcm4377 = hci_get_drvdata(hdev); + struct bcm4377_transfer_ring *ring; + int ret; + + switch (hci_skb_pkt_type(skb)) { + case HCI_COMMAND_PKT: + hdev->stat.cmd_tx++; + ring = &bcm4377->hci_h2d_ring; + break; + + case HCI_ACLDATA_PKT: + hdev->stat.acl_tx++; + ring = &bcm4377->acl_h2d_ring; + break; + + case HCI_SCODATA_PKT: + hdev->stat.sco_tx++; + ring = &bcm4377->sco_h2d_ring; + break; + + default: + return -EILSEQ; + } + + ret = bcm4377_enqueue(bcm4377, ring, skb->data, skb->len, false); + if (ret < 0) { + hdev->stat.err_tx++; + return ret; + } + + hdev->stat.byte_tx += skb->len; + kfree_skb(skb); + return ret; +} + +static int bcm4377_hci_set_bdaddr(struct hci_dev *hdev, const bdaddr_t *bdaddr) +{ + struct bcm4377_data *bcm4377 = hci_get_drvdata(hdev); + struct sk_buff *skb; + int err; + + skb = __hci_cmd_sync(hdev, 0xfc01, 6, bdaddr, HCI_INIT_TIMEOUT); + if (IS_ERR(skb)) { + err = PTR_ERR(skb); + dev_err(&bcm4377->pdev->dev, + "Change address command failed (%d)", err); + return err; + } + kfree_skb(skb); + + return 0; +} + +static int bcm4377_alloc_transfer_ring(struct bcm4377_data *bcm4377, + struct bcm4377_transfer_ring *ring) +{ + size_t entry_size; + + spin_lock_init(&ring->lock); + ring->payload_size = ALIGN(ring->payload_size, 4); + ring->mapped_payload_size = ALIGN(ring->mapped_payload_size, 4); + + if (ring->payload_size > BCM4377_XFER_RING_MAX_INPLACE_PAYLOAD_SIZE) + return -EINVAL; + if (ring->n_entries > BCM4377_MAX_RING_SIZE) + return -EINVAL; + if (ring->virtual && ring->allow_wait) + return -EINVAL; + + if (ring->d2h_buffers_only) { + if (ring->virtual) + return -EINVAL; + if (ring->payload_size) + return -EINVAL; + if (!ring->mapped_payload_size) + return -EINVAL; + } + if (ring->virtual) + return 0; + + entry_size = + ring->payload_size + sizeof(struct bcm4377_xfer_ring_entry); + ring->ring = dmam_alloc_coherent(&bcm4377->pdev->dev, + ring->n_entries * entry_size, + &ring->ring_dma, GFP_KERNEL); + if (!ring->ring) + return -ENOMEM; + + if (ring->allow_wait) { + ring->events = devm_kcalloc(&bcm4377->pdev->dev, + ring->n_entries, + sizeof(*ring->events), GFP_KERNEL); + if (!ring->events) + return -ENOMEM; + } + + if (ring->mapped_payload_size) { + ring->payloads = dmam_alloc_coherent( + &bcm4377->pdev->dev, + ring->n_entries * ring->mapped_payload_size, + &ring->payloads_dma, GFP_KERNEL); + if (!ring->payloads) + return -ENOMEM; + } + + return 0; +} + +static int bcm4377_alloc_completion_ring(struct bcm4377_data *bcm4377, + struct bcm4377_completion_ring *ring) +{ + size_t entry_size; + + ring->payload_size = ALIGN(ring->payload_size, 4); + if (ring->payload_size > BCM4377_XFER_RING_MAX_INPLACE_PAYLOAD_SIZE) + return -EINVAL; + if (ring->n_entries > BCM4377_MAX_RING_SIZE) + return -EINVAL; + + entry_size = ring->payload_size + + sizeof(struct bcm4377_completion_ring_entry); + + ring->ring = dmam_alloc_coherent(&bcm4377->pdev->dev, + ring->n_entries * entry_size, + &ring->ring_dma, GFP_KERNEL); + if (!ring->ring) + return -ENOMEM; + return 0; +} + +static int bcm4377_init_context(struct bcm4377_data *bcm4377) +{ + struct device *dev = &bcm4377->pdev->dev; + dma_addr_t peripheral_info_dma; + + bcm4377->ctx = dmam_alloc_coherent(dev, sizeof(*bcm4377->ctx), + &bcm4377->ctx_dma, GFP_KERNEL); + if (!bcm4377->ctx) + return -ENOMEM; + memset(bcm4377->ctx, 0, sizeof(*bcm4377->ctx)); + + bcm4377->ring_state = + dmam_alloc_coherent(dev, sizeof(*bcm4377->ring_state), + &bcm4377->ring_state_dma, GFP_KERNEL); + if (!bcm4377->ring_state) + return -ENOMEM; + memset(bcm4377->ring_state, 0, sizeof(*bcm4377->ring_state)); + + bcm4377->ctx->version = cpu_to_le16(1); + bcm4377->ctx->size = cpu_to_le16(sizeof(*bcm4377->ctx)); + bcm4377->ctx->enabled_caps = cpu_to_le32(2); + + /* + * The BT device will write 0x20 bytes of data to this buffer but + * the exact contents are unknown. It only needs to exist for BT + * to work such that we can just allocate and then ignore it. + */ + if (!dmam_alloc_coherent(&bcm4377->pdev->dev, 0x20, + &peripheral_info_dma, GFP_KERNEL)) + return -ENOMEM; + bcm4377->ctx->peripheral_info_addr = cpu_to_le64(peripheral_info_dma); + + bcm4377->ctx->xfer_ring_heads_addr = cpu_to_le64( + bcm4377->ring_state_dma + + offsetof(struct bcm4377_ring_state, xfer_ring_head)); + bcm4377->ctx->xfer_ring_tails_addr = cpu_to_le64( + bcm4377->ring_state_dma + + offsetof(struct bcm4377_ring_state, xfer_ring_tail)); + bcm4377->ctx->completion_ring_heads_addr = cpu_to_le64( + bcm4377->ring_state_dma + + offsetof(struct bcm4377_ring_state, completion_ring_head)); + bcm4377->ctx->completion_ring_tails_addr = cpu_to_le64( + bcm4377->ring_state_dma + + offsetof(struct bcm4377_ring_state, completion_ring_tail)); + + bcm4377->ctx->n_completion_rings = + cpu_to_le16(BCM4377_N_COMPLETION_RINGS); + bcm4377->ctx->n_xfer_rings = cpu_to_le16(BCM4377_N_TRANSFER_RINGS); + + bcm4377->ctx->control_completion_ring_addr = + cpu_to_le64(bcm4377->control_ack_ring.ring_dma); + bcm4377->ctx->control_completion_ring_n_entries = + cpu_to_le16(bcm4377->control_ack_ring.n_entries); + bcm4377->ctx->control_completion_ring_doorbell = cpu_to_le16(0xffff); + bcm4377->ctx->control_completion_ring_msi = 0; + bcm4377->ctx->control_completion_ring_header_size = 0; + bcm4377->ctx->control_completion_ring_footer_size = 0; + + bcm4377->ctx->control_xfer_ring_addr = + cpu_to_le64(bcm4377->control_h2d_ring.ring_dma); + bcm4377->ctx->control_xfer_ring_n_entries = + cpu_to_le16(bcm4377->control_h2d_ring.n_entries); + bcm4377->ctx->control_xfer_ring_doorbell = + cpu_to_le16(bcm4377->control_h2d_ring.doorbell); + bcm4377->ctx->control_xfer_ring_msi = 0; + bcm4377->ctx->control_xfer_ring_header_size = 0; + bcm4377->ctx->control_xfer_ring_footer_size = + bcm4377->control_h2d_ring.payload_size / 4; + + dev_dbg(&bcm4377->pdev->dev, "context initialized at IOVA %pad", + &bcm4377->ctx_dma); + + return 0; +} + +static int bcm4377_prepare_rings(struct bcm4377_data *bcm4377) +{ + int ret; + + /* + * Even though many of these settings appear to be configurable + * when sending the "create ring" messages most of these are + * actually hardcoded in some (and quite possibly all) firmware versions + * and changing them on the host has no effect. + * Specifically, this applies to at least the doorbells, the transfer + * and completion ring ids and their mapping (e.g. both HCI and ACL + * entries will always be queued in completion rings 1 and 2 no matter + * what we configure here). + */ + bcm4377->control_ack_ring.ring_id = BCM4377_ACK_RING_CONTROL; + bcm4377->control_ack_ring.n_entries = 32; + bcm4377->control_ack_ring.transfer_rings = + BIT(BCM4377_XFER_RING_CONTROL); + + bcm4377->hci_acl_ack_ring.ring_id = BCM4377_ACK_RING_HCI_ACL; + bcm4377->hci_acl_ack_ring.n_entries = 2 * BCM4377_RING_N_ENTRIES; + bcm4377->hci_acl_ack_ring.transfer_rings = + BIT(BCM4377_XFER_RING_HCI_H2D) | BIT(BCM4377_XFER_RING_ACL_H2D); + bcm4377->hci_acl_ack_ring.delay = 1000; + + /* + * A payload size of MAX_EVENT_PAYLOAD_SIZE is enough here since large + * ACL packets will be transmitted inside buffers mapped via + * acl_d2h_ring anyway. + */ + bcm4377->hci_acl_event_ring.ring_id = BCM4377_EVENT_RING_HCI_ACL; + bcm4377->hci_acl_event_ring.payload_size = MAX_EVENT_PAYLOAD_SIZE; + bcm4377->hci_acl_event_ring.n_entries = 2 * BCM4377_RING_N_ENTRIES; + bcm4377->hci_acl_event_ring.transfer_rings = + BIT(BCM4377_XFER_RING_HCI_D2H) | BIT(BCM4377_XFER_RING_ACL_D2H); + bcm4377->hci_acl_event_ring.delay = 1000; + + bcm4377->sco_ack_ring.ring_id = BCM4377_ACK_RING_SCO; + bcm4377->sco_ack_ring.n_entries = BCM4377_RING_N_ENTRIES; + bcm4377->sco_ack_ring.transfer_rings = BIT(BCM4377_XFER_RING_SCO_H2D); + + bcm4377->sco_event_ring.ring_id = BCM4377_EVENT_RING_SCO; + bcm4377->sco_event_ring.payload_size = MAX_SCO_PAYLOAD_SIZE; + bcm4377->sco_event_ring.n_entries = BCM4377_RING_N_ENTRIES; + bcm4377->sco_event_ring.transfer_rings = BIT(BCM4377_XFER_RING_SCO_D2H); + + bcm4377->control_h2d_ring.ring_id = BCM4377_XFER_RING_CONTROL; + bcm4377->control_h2d_ring.doorbell = BCM4377_DOORBELL_CONTROL; + bcm4377->control_h2d_ring.payload_size = BCM4377_CONTROL_MSG_SIZE; + bcm4377->control_h2d_ring.completion_ring = BCM4377_ACK_RING_CONTROL; + bcm4377->control_h2d_ring.allow_wait = true; + bcm4377->control_h2d_ring.n_entries = BCM4377_RING_N_ENTRIES; + + bcm4377->hci_h2d_ring.ring_id = BCM4377_XFER_RING_HCI_H2D; + bcm4377->hci_h2d_ring.doorbell = BCM4377_DOORBELL_HCI_H2D; + bcm4377->hci_h2d_ring.payload_size = MAX_EVENT_PAYLOAD_SIZE; + bcm4377->hci_h2d_ring.completion_ring = BCM4377_ACK_RING_HCI_ACL; + bcm4377->hci_h2d_ring.n_entries = BCM4377_RING_N_ENTRIES; + + bcm4377->hci_d2h_ring.ring_id = BCM4377_XFER_RING_HCI_D2H; + bcm4377->hci_d2h_ring.doorbell = BCM4377_DOORBELL_HCI_D2H; + bcm4377->hci_d2h_ring.completion_ring = BCM4377_EVENT_RING_HCI_ACL; + bcm4377->hci_d2h_ring.virtual = true; + bcm4377->hci_d2h_ring.n_entries = BCM4377_RING_N_ENTRIES; + + bcm4377->sco_h2d_ring.ring_id = BCM4377_XFER_RING_SCO_H2D; + bcm4377->sco_h2d_ring.doorbell = BCM4377_DOORBELL_SCO; + bcm4377->sco_h2d_ring.payload_size = MAX_SCO_PAYLOAD_SIZE; + bcm4377->sco_h2d_ring.completion_ring = BCM4377_ACK_RING_SCO; + bcm4377->sco_h2d_ring.sync = true; + bcm4377->sco_h2d_ring.n_entries = BCM4377_RING_N_ENTRIES; + + bcm4377->sco_d2h_ring.ring_id = BCM4377_XFER_RING_SCO_D2H; + bcm4377->sco_d2h_ring.doorbell = BCM4377_DOORBELL_SCO; + bcm4377->sco_d2h_ring.completion_ring = BCM4377_EVENT_RING_SCO; + bcm4377->sco_d2h_ring.virtual = true; + bcm4377->sco_d2h_ring.sync = true; + bcm4377->sco_d2h_ring.n_entries = BCM4377_RING_N_ENTRIES; + + /* + * This ring has to use mapped_payload_size because the largest ACL + * packet doesn't fit inside the largest possible footer + */ + bcm4377->acl_h2d_ring.ring_id = BCM4377_XFER_RING_ACL_H2D; + bcm4377->acl_h2d_ring.doorbell = BCM4377_DOORBELL_ACL_H2D; + bcm4377->acl_h2d_ring.mapped_payload_size = MAX_ACL_PAYLOAD_SIZE; + bcm4377->acl_h2d_ring.completion_ring = BCM4377_ACK_RING_HCI_ACL; + bcm4377->acl_h2d_ring.n_entries = BCM4377_RING_N_ENTRIES; + + /* + * This ring only contains empty buffers to be used by incoming + * ACL packets that do not fit inside the footer of hci_acl_event_ring + */ + bcm4377->acl_d2h_ring.ring_id = BCM4377_XFER_RING_ACL_D2H; + bcm4377->acl_d2h_ring.doorbell = BCM4377_DOORBELL_ACL_D2H; + bcm4377->acl_d2h_ring.completion_ring = BCM4377_EVENT_RING_HCI_ACL; + bcm4377->acl_d2h_ring.d2h_buffers_only = true; + bcm4377->acl_d2h_ring.mapped_payload_size = MAX_ACL_PAYLOAD_SIZE; + bcm4377->acl_d2h_ring.n_entries = BCM4377_RING_N_ENTRIES; + + /* + * no need for any cleanup since this is only called from _probe + * and only devres-managed allocations are used + */ + ret = bcm4377_alloc_transfer_ring(bcm4377, &bcm4377->control_h2d_ring); + if (ret) + return ret; + ret = bcm4377_alloc_transfer_ring(bcm4377, &bcm4377->hci_h2d_ring); + if (ret) + return ret; + ret = bcm4377_alloc_transfer_ring(bcm4377, &bcm4377->hci_d2h_ring); + if (ret) + return ret; + ret = bcm4377_alloc_transfer_ring(bcm4377, &bcm4377->sco_h2d_ring); + if (ret) + return ret; + ret = bcm4377_alloc_transfer_ring(bcm4377, &bcm4377->sco_d2h_ring); + if (ret) + return ret; + ret = bcm4377_alloc_transfer_ring(bcm4377, &bcm4377->acl_h2d_ring); + if (ret) + return ret; + ret = bcm4377_alloc_transfer_ring(bcm4377, &bcm4377->acl_d2h_ring); + if (ret) + return ret; + + ret = bcm4377_alloc_completion_ring(bcm4377, + &bcm4377->control_ack_ring); + if (ret) + return ret; + ret = bcm4377_alloc_completion_ring(bcm4377, + &bcm4377->hci_acl_ack_ring); + if (ret) + return ret; + ret = bcm4377_alloc_completion_ring(bcm4377, + &bcm4377->hci_acl_event_ring); + if (ret) + return ret; + ret = bcm4377_alloc_completion_ring(bcm4377, &bcm4377->sco_ack_ring); + if (ret) + return ret; + ret = bcm4377_alloc_completion_ring(bcm4377, &bcm4377->sco_event_ring); + if (ret) + return ret; + + dev_dbg(&bcm4377->pdev->dev, "all rings allocated and prepared\n"); + + return 0; +} + +static int bcm4377_boot(struct bcm4377_data *bcm4377) +{ + const struct firmware *fw; + void *bfr; + dma_addr_t fw_dma; + int ret = 0; + u32 bootstage, rti_status; + + bootstage = ioread32(bcm4377->bar2 + BCM4377_BAR2_BOOTSTAGE); + rti_status = ioread32(bcm4377->bar2 + BCM4377_BAR2_RTI_STATUS); + + if (bootstage != 0) { + dev_err(&bcm4377->pdev->dev, "bootstage is %d and not 0\n", + bootstage); + return -EINVAL; + } + + if (rti_status != 0) { + dev_err(&bcm4377->pdev->dev, "RTI status is %d and not 0\n", + rti_status); + return -EINVAL; + } + + fw = bcm4377_request_blob(bcm4377, "bin"); + if (!fw) { + dev_err(&bcm4377->pdev->dev, "Failed to load firmware\n"); + return -ENOENT; + } + + bfr = dma_alloc_coherent(&bcm4377->pdev->dev, fw->size, &fw_dma, + GFP_KERNEL); + if (!bfr) { + ret = -ENOMEM; + goto out_release_fw; + } + + memcpy(bfr, fw->data, fw->size); + + iowrite32(0, bcm4377->bar0 + BCM4377_BAR0_HOST_WINDOW_LO); + iowrite32(0, bcm4377->bar0 + BCM4377_BAR0_HOST_WINDOW_HI); + iowrite32(BCM4377_DMA_MASK, + bcm4377->bar0 + BCM4377_BAR0_HOST_WINDOW_SIZE); + + iowrite32(lower_32_bits(fw_dma), bcm4377->bar2 + BCM4377_BAR2_FW_LO); + iowrite32(upper_32_bits(fw_dma), bcm4377->bar2 + BCM4377_BAR2_FW_HI); + iowrite32(fw->size, bcm4377->bar2 + BCM4377_BAR2_FW_SIZE); + iowrite32(0, bcm4377->bar0 + BCM4377_BAR0_FW_DOORBELL); + + dev_dbg(&bcm4377->pdev->dev, "waiting for firmware to boot\n"); + + ret = wait_for_completion_interruptible_timeout(&bcm4377->event, + BCM4377_TIMEOUT); + if (ret == 0) { + ret = -ETIMEDOUT; + goto out_dma_free; + } else if (ret < 0) { + goto out_dma_free; + } + + if (bcm4377->bootstage != 2) { + dev_err(&bcm4377->pdev->dev, "boostage %d != 2\n", + bcm4377->bootstage); + ret = -ENXIO; + goto out_dma_free; + } + + dev_dbg(&bcm4377->pdev->dev, "firmware has booted (stage = %x)\n", + bcm4377->bootstage); + ret = 0; + +out_dma_free: + dma_free_coherent(&bcm4377->pdev->dev, fw->size, bfr, fw_dma); +out_release_fw: + release_firmware(fw); + return ret; +} + +static int bcm4377_setup_rti(struct bcm4377_data *bcm4377) +{ + int ret; + + dev_dbg(&bcm4377->pdev->dev, "starting RTI\n"); + iowrite32(1, bcm4377->bar0 + BCM4377_BAR0_RTI_CONTROL); + + ret = wait_for_completion_interruptible_timeout(&bcm4377->event, + BCM4377_TIMEOUT); + if (ret == 0) { + dev_err(&bcm4377->pdev->dev, + "timed out while waiting for RTI to transition to state 1"); + return -ETIMEDOUT; + } else if (ret < 0) { + return ret; + } + + if (bcm4377->rti_status != 1) { + dev_err(&bcm4377->pdev->dev, "RTI did not ack state 1 (%d)\n", + bcm4377->rti_status); + return -ENODEV; + } + dev_dbg(&bcm4377->pdev->dev, "RTI is in state 1\n"); + + /* allow access to the entire IOVA space again */ + iowrite32(0, bcm4377->bar2 + BCM4377_BAR2_RTI_WINDOW_LO); + iowrite32(0, bcm4377->bar2 + BCM4377_BAR2_RTI_WINDOW_HI); + iowrite32(BCM4377_DMA_MASK, + bcm4377->bar2 + BCM4377_BAR2_RTI_WINDOW_SIZE); + + /* setup "Converged IPC" context */ + iowrite32(lower_32_bits(bcm4377->ctx_dma), + bcm4377->bar2 + BCM4377_BAR2_CONTEXT_ADDR_LO); + iowrite32(upper_32_bits(bcm4377->ctx_dma), + bcm4377->bar2 + BCM4377_BAR2_CONTEXT_ADDR_HI); + iowrite32(2, bcm4377->bar0 + BCM4377_BAR0_RTI_CONTROL); + + ret = wait_for_completion_interruptible_timeout(&bcm4377->event, + BCM4377_TIMEOUT); + if (ret == 0) { + dev_err(&bcm4377->pdev->dev, + "timed out while waiting for RTI to transition to state 2"); + return -ETIMEDOUT; + } else if (ret < 0) { + return ret; + } + + if (bcm4377->rti_status != 2) { + dev_err(&bcm4377->pdev->dev, "RTI did not ack state 2 (%d)\n", + bcm4377->rti_status); + return -ENODEV; + } + + dev_dbg(&bcm4377->pdev->dev, + "RTI is in state 2; control ring is ready\n"); + bcm4377->control_ack_ring.enabled = true; + + return 0; +} + +static int bcm4377_parse_otp_board_params(struct bcm4377_data *bcm4377, + char tag, const char *val, size_t len) +{ + if (tag != 'V') + return 0; + if (len >= sizeof(bcm4377->vendor)) + return -EINVAL; + + strscpy(bcm4377->vendor, val, len + 1); + return 0; +} + +static int bcm4377_parse_otp_chip_params(struct bcm4377_data *bcm4377, char tag, + const char *val, size_t len) +{ + size_t idx = 0; + + if (tag != 's') + return 0; + if (len >= sizeof(bcm4377->stepping)) + return -EINVAL; + + while (len != 0) { + bcm4377->stepping[idx] = tolower(val[idx]); + if (val[idx] == '\0') + return 0; + + idx++; + len--; + } + + bcm4377->stepping[idx] = '\0'; + return 0; +} + +static int bcm4377_parse_otp_str(struct bcm4377_data *bcm4377, const u8 *str, + enum bcm4377_otp_params_type type) +{ + const char *p; + int ret; + + p = skip_spaces(str); + while (*p) { + char tag = *p++; + const char *end; + size_t len; + + if (*p++ != '=') /* implicit NUL check */ + return -EINVAL; + + /* *p might be NUL here, if so end == p and len == 0 */ + end = strchrnul(p, ' '); + len = end - p; + + /* leave 1 byte for NUL in destination string */ + if (len > (BCM4377_OTP_MAX_PARAM_LEN - 1)) + return -EINVAL; + + switch (type) { + case BCM4377_OTP_BOARD_PARAMS: + ret = bcm4377_parse_otp_board_params(bcm4377, tag, p, + len); + break; + case BCM4377_OTP_CHIP_PARAMS: + ret = bcm4377_parse_otp_chip_params(bcm4377, tag, p, + len); + break; + default: + ret = -EINVAL; + break; + } + + if (ret) + return ret; + + /* Skip to next arg, if any */ + p = skip_spaces(end); + } + + return 0; +} + +static int bcm4377_parse_otp_sys_vendor(struct bcm4377_data *bcm4377, u8 *otp, + size_t size) +{ + int idx = 4; + const char *chip_params; + const char *board_params; + int ret; + + /* 4-byte header and two empty strings */ + if (size < 6) + return -EINVAL; + + if (get_unaligned_le32(otp) != BCM4377_OTP_VENDOR_HDR) + return -EINVAL; + + chip_params = &otp[idx]; + + /* Skip first string, including terminator */ + idx += strnlen(chip_params, size - idx) + 1; + if (idx >= size) + return -EINVAL; + + board_params = &otp[idx]; + + /* Skip to terminator of second string */ + idx += strnlen(board_params, size - idx); + if (idx >= size) + return -EINVAL; + + /* At this point both strings are guaranteed NUL-terminated */ + dev_dbg(&bcm4377->pdev->dev, + "OTP: chip_params='%s' board_params='%s'\n", chip_params, + board_params); + + ret = bcm4377_parse_otp_str(bcm4377, chip_params, + BCM4377_OTP_CHIP_PARAMS); + if (ret) + return ret; + + ret = bcm4377_parse_otp_str(bcm4377, board_params, + BCM4377_OTP_BOARD_PARAMS); + if (ret) + return ret; + + if (!bcm4377->stepping[0] || !bcm4377->vendor[0]) + return -EINVAL; + + dev_dbg(&bcm4377->pdev->dev, "OTP: stepping=%s, vendor=%s\n", + bcm4377->stepping, bcm4377->vendor); + return 0; +} + +static int bcm4377_parse_otp(struct bcm4377_data *bcm4377) +{ + u8 *otp; + int i; + int ret = -ENOENT; + + otp = kzalloc(BCM4377_OTP_SIZE, GFP_KERNEL); + if (!otp) + return -ENOMEM; + + for (i = 0; i < BCM4377_OTP_SIZE; ++i) + otp[i] = ioread8(bcm4377->bar0 + bcm4377->hw->otp_offset + i); + + i = 0; + while (i < (BCM4377_OTP_SIZE - 1)) { + u8 type = otp[i]; + u8 length = otp[i + 1]; + + if (type == 0) + break; + + if ((i + 2 + length) > BCM4377_OTP_SIZE) + break; + + switch (type) { + case BCM4377_OTP_SYS_VENDOR: + dev_dbg(&bcm4377->pdev->dev, + "OTP @ 0x%x (%d): SYS_VENDOR", i, length); + ret = bcm4377_parse_otp_sys_vendor(bcm4377, &otp[i + 2], + length); + break; + case BCM4377_OTP_CIS: + dev_dbg(&bcm4377->pdev->dev, "OTP @ 0x%x (%d): CIS", i, + length); + break; + default: + dev_dbg(&bcm4377->pdev->dev, "OTP @ 0x%x (%d): unknown", + i, length); + break; + } + + i += 2 + length; + } + + kfree(otp); + return ret; +} + +static int bcm4377_init_cfg(struct bcm4377_data *bcm4377) +{ + int ret; + u32 ctrl; + + ret = pci_write_config_dword(bcm4377->pdev, + BCM4377_PCIECFG_BAR0_WINDOW1, + bcm4377->hw->bar0_window1); + if (ret) + return ret; + + ret = pci_write_config_dword(bcm4377->pdev, + BCM4377_PCIECFG_BAR0_WINDOW2, + bcm4377->hw->bar0_window2); + if (ret) + return ret; + + ret = pci_write_config_dword( + bcm4377->pdev, BCM4377_PCIECFG_BAR0_CORE2_WINDOW1, + BCM4377_PCIECFG_BAR0_CORE2_WINDOW1_DEFAULT); + if (ret) + return ret; + + if (bcm4377->hw->has_bar0_core2_window2) { + ret = pci_write_config_dword(bcm4377->pdev, + BCM4377_PCIECFG_BAR0_CORE2_WINDOW2, + bcm4377->hw->bar0_core2_window2); + if (ret) + return ret; + } + + ret = pci_write_config_dword(bcm4377->pdev, BCM4377_PCIECFG_BAR2_WINDOW, + BCM4377_PCIECFG_BAR2_WINDOW_DEFAULT); + if (ret) + return ret; + + ret = pci_read_config_dword(bcm4377->pdev, + BCM4377_PCIECFG_SUBSYSTEM_CTRL, &ctrl); + if (ret) + return ret; + + if (bcm4377->hw->clear_pciecfg_subsystem_ctrl_bit19) + ctrl &= ~BIT(19); + ctrl |= BIT(16); + + return pci_write_config_dword(bcm4377->pdev, + BCM4377_PCIECFG_SUBSYSTEM_CTRL, ctrl); +} + +static int bcm4377_probe_dmi(struct bcm4377_data *bcm4377) +{ + const struct dmi_system_id *board_type_dmi_id; + + board_type_dmi_id = dmi_first_match(bcm4377_dmi_board_table); + if (board_type_dmi_id && board_type_dmi_id->driver_data) { + bcm4377->board_type = board_type_dmi_id->driver_data; + dev_dbg(&bcm4377->pdev->dev, + "found board type via DMI match: %s\n", + bcm4377->board_type); + } + + return 0; +} + +static int bcm4377_probe_of(struct bcm4377_data *bcm4377) +{ + struct device_node *np = bcm4377->pdev->dev.of_node; + int ret; + + if (!np) + return 0; + + ret = of_property_read_string(np, "brcm,board-type", + &bcm4377->board_type); + if (ret) { + dev_err(&bcm4377->pdev->dev, "no brcm,board-type property\n"); + return ret; + } + + bcm4377->taurus_beamforming_cal_blob = + of_get_property(np, "brcm,taurus-bf-cal-blob", + &bcm4377->taurus_beamforming_cal_size); + if (!bcm4377->taurus_beamforming_cal_blob) { + dev_err(&bcm4377->pdev->dev, + "no brcm,taurus-bf-cal-blob property\n"); + return -ENOENT; + } + bcm4377->taurus_cal_blob = of_get_property(np, "brcm,taurus-cal-blob", + &bcm4377->taurus_cal_size); + if (!bcm4377->taurus_cal_blob) { + dev_err(&bcm4377->pdev->dev, + "no brcm,taurus-cal-blob property\n"); + return -ENOENT; + } + + return 0; +} + +static void bcm4377_disable_aspm(struct bcm4377_data *bcm4377) +{ + pci_disable_link_state(bcm4377->pdev, + PCIE_LINK_STATE_L0S | PCIE_LINK_STATE_L1); + + /* + * pci_disable_link_state can fail if either CONFIG_PCIEASPM is disabled + * or if the BIOS hasn't handed over control to us. We must *always* + * disable ASPM for this device due to hardware errata though. + */ + pcie_capability_clear_word(bcm4377->pdev, PCI_EXP_LNKCTL, + PCI_EXP_LNKCTL_ASPMC); +} + +static void bcm4377_pci_free_irq_vectors(void *data) +{ + pci_free_irq_vectors(data); +} + +static void bcm4377_hci_free_dev(void *data) +{ + hci_free_dev(data); +} + +static void bcm4377_hci_unregister_dev(void *data) +{ + hci_unregister_dev(data); +} + +static int bcm4377_probe(struct pci_dev *pdev, const struct pci_device_id *id) +{ + struct bcm4377_data *bcm4377; + struct hci_dev *hdev; + int ret, irq; + + ret = dma_set_mask_and_coherent(&pdev->dev, BCM4377_DMA_MASK); + if (ret) + return ret; + + bcm4377 = devm_kzalloc(&pdev->dev, sizeof(*bcm4377), GFP_KERNEL); + if (!bcm4377) + return -ENOMEM; + + bcm4377->pdev = pdev; + bcm4377->hw = &bcm4377_hw_variants[id->driver_data]; + init_completion(&bcm4377->event); + + ret = bcm4377_prepare_rings(bcm4377); + if (ret) + return ret; + + ret = bcm4377_init_context(bcm4377); + if (ret) + return ret; + + ret = bcm4377_probe_dmi(bcm4377); + if (ret) + return ret; + ret = bcm4377_probe_of(bcm4377); + if (ret) + return ret; + if (!bcm4377->board_type) { + dev_err(&pdev->dev, "unable to determine board type\n"); + return -ENODEV; + } + + if (bcm4377->hw->disable_aspm) + bcm4377_disable_aspm(bcm4377); + + ret = pci_reset_function_locked(pdev); + if (ret) + dev_warn( + &pdev->dev, + "function level reset failed with %d; trying to continue anyway\n", + ret); + + /* + * If this number is too low and we try to access any BAR too + * early the device will crash. Experiments have shown that + * approximately 50 msec is the minimum amount we have to wait. + * Let's double that to be safe. + */ + msleep(100); + + ret = pci_enable_device(pdev); + if (ret) + return ret; + pci_set_master(pdev); + + ret = bcm4377_init_cfg(bcm4377); + if (ret) + return ret; + + bcm4377->bar0 = pcim_iomap(pdev, 0, 0); + if (!bcm4377->bar0) + return -EBUSY; + bcm4377->bar2 = pcim_iomap(pdev, 2, 0); + if (!bcm4377->bar2) + return -EBUSY; + + ret = bcm4377_parse_otp(bcm4377); + if (ret) { + dev_err(&pdev->dev, "Reading OTP failed with %d\n", ret); + return ret; + } + + /* + * Legacy interrupts result in an IRQ storm because we don't know where + * the interrupt mask and status registers for these chips are. + * MSIs are acked automatically instead. + */ + ret = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_MSI); + if (ret < 0) + return -ENODEV; + ret = devm_add_action_or_reset(&pdev->dev, bcm4377_pci_free_irq_vectors, + pdev); + if (ret) + return ret; + + irq = pci_irq_vector(pdev, 0); + if (irq <= 0) + return -ENODEV; + + ret = devm_request_irq(&pdev->dev, irq, bcm4377_irq, 0, "bcm4377", + bcm4377); + if (ret) + return ret; + + hdev = hci_alloc_dev(); + if (!hdev) + return -ENOMEM; + ret = devm_add_action_or_reset(&pdev->dev, bcm4377_hci_free_dev, hdev); + if (ret) + return ret; + + bcm4377->hdev = hdev; + + hdev->bus = HCI_PCI; + hdev->dev_type = HCI_PRIMARY; + hdev->open = bcm4377_hci_open; + hdev->close = bcm4377_hci_close; + hdev->send = bcm4377_hci_send_frame; + hdev->set_bdaddr = bcm4377_hci_set_bdaddr; + hdev->setup = bcm4377_hci_setup; + + set_bit(HCI_QUIRK_USE_BDADDR_PROPERTY, &hdev->quirks); + if (bcm4377->hw->broken_mws_transport_config) + set_bit(HCI_QUIRK_BROKEN_MWS_TRANSPORT_CONFIG, &hdev->quirks); + if (bcm4377->hw->broken_ext_scan) + set_bit(HCI_QUIRK_BROKEN_EXT_SCAN, &hdev->quirks); + + pci_set_drvdata(pdev, bcm4377); + hci_set_drvdata(hdev, bcm4377); + SET_HCIDEV_DEV(hdev, &pdev->dev); + + ret = bcm4377_boot(bcm4377); + if (ret) + return ret; + + ret = bcm4377_setup_rti(bcm4377); + if (ret) + return ret; + + ret = hci_register_dev(hdev); + if (ret) + return ret; + return devm_add_action_or_reset(&pdev->dev, bcm4377_hci_unregister_dev, + hdev); +} + +static int bcm4377_suspend(struct pci_dev *pdev, pm_message_t state) +{ + struct bcm4377_data *bcm4377 = pci_get_drvdata(pdev); + int ret; + + ret = hci_suspend_dev(bcm4377->hdev); + if (ret) + return ret; + + iowrite32(BCM4377_BAR0_SLEEP_CONTROL_QUIESCE, + bcm4377->bar0 + BCM4377_BAR0_SLEEP_CONTROL); + + return 0; +} + +static int bcm4377_resume(struct pci_dev *pdev) +{ + struct bcm4377_data *bcm4377 = pci_get_drvdata(pdev); + + iowrite32(BCM4377_BAR0_SLEEP_CONTROL_UNQUIESCE, + bcm4377->bar0 + BCM4377_BAR0_SLEEP_CONTROL); + + return hci_resume_dev(bcm4377->hdev); +} + +static const struct dmi_system_id bcm4377_dmi_board_table[] = { + { + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "Apple Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "MacBookAir9,1"), + }, + .driver_data = "apple,formosa", + }, + { + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "Apple Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro15,4"), + }, + .driver_data = "apple,formosa", + }, + { + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "Apple Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro16,3"), + }, + .driver_data = "apple,formosa", + }, + {} +}; + +static const struct bcm4377_hw bcm4377_hw_variants[] = { + [BCM4377] = { + .id = 0x4377, + .otp_offset = 0x4120, + .bar0_window1 = 0x1800b000, + .bar0_window2 = 0x1810c000, + .disable_aspm = true, + .broken_ext_scan = true, + .send_ptb = bcm4377_send_ptb, + }, + + [BCM4378] = { + .id = 0x4378, + .otp_offset = 0x4120, + .bar0_window1 = 0x18002000, + .bar0_window2 = 0x1810a000, + .bar0_core2_window2 = 0x18107000, + .has_bar0_core2_window2 = true, + .broken_mws_transport_config = true, + .send_calibration = bcm4378_send_calibration, + .send_ptb = bcm4378_send_ptb, + }, + + [BCM4387] = { + .id = 0x4387, + .otp_offset = 0x413c, + .bar0_window1 = 0x18002000, + .bar0_window2 = 0x18109000, + .bar0_core2_window2 = 0x18106000, + .has_bar0_core2_window2 = true, + .clear_pciecfg_subsystem_ctrl_bit19 = true, + .broken_mws_transport_config = true, + .send_calibration = bcm4387_send_calibration, + .send_ptb = bcm4378_send_ptb, + }, +}; + +#define BCM4377_DEVID_ENTRY(id) \ + { \ + PCI_VENDOR_ID_BROADCOM, BCM##id##_DEVICE_ID, PCI_ANY_ID, \ + PCI_ANY_ID, PCI_CLASS_NETWORK_OTHER << 8, 0xffff00, \ + BCM##id \ + } + +static const struct pci_device_id bcm4377_devid_table[] = { + BCM4377_DEVID_ENTRY(4377), + BCM4377_DEVID_ENTRY(4378), + BCM4377_DEVID_ENTRY(4387), + {}, +}; +MODULE_DEVICE_TABLE(pci, bcm4377_devid_table); + +static struct pci_driver bcm4377_pci_driver = { + .name = "hci_bcm4377", + .id_table = bcm4377_devid_table, + .probe = bcm4377_probe, + .suspend = bcm4377_suspend, + .resume = bcm4377_resume, +}; +module_pci_driver(bcm4377_pci_driver); + +MODULE_AUTHOR("Sven Peter "); +MODULE_DESCRIPTION("Bluetooth support for Broadcom 4377/4378/4387 devices"); +MODULE_LICENSE("Dual MIT/GPL"); +MODULE_FIRMWARE("brcm/brcmbt4377*.bin"); +MODULE_FIRMWARE("brcm/brcmbt4377*.ptb"); +MODULE_FIRMWARE("brcm/brcmbt4378*.bin"); +MODULE_FIRMWARE("brcm/brcmbt4378*.ptb"); +MODULE_FIRMWARE("brcm/brcmbt4387*.bin"); +MODULE_FIRMWARE("brcm/brcmbt4387*.ptb"); From ac09bb3f87a776f8c7cabc746066a9aca8f507b6 Mon Sep 17 00:00:00 2001 From: Raman Varabets Date: Tue, 8 Nov 2022 20:54:13 +0800 Subject: [PATCH 1945/2002] Bluetooth: btusb: Add Realtek 8761BUV support ID 0x2B89:0x8761 Identifies as "Realtek Bluetooth Radio"; used in UGREEN CM390 (P/N 80889). Device description at /sys/kernel/debug/usb/devices: T: Bus=01 Lev=02 Prnt=02 Port=01 Cnt=01 Dev#= 7 Spd=12 MxCh= 0 D: Ver= 1.10 Cls=e0(wlcon) Sub=01 Prot=01 MxPS=64 #Cfgs= 1 P: Vendor=2b89 ProdID=8761 Rev= 2.00 S: Manufacturer=Realtek S: Product=Bluetooth Radio S: SerialNumber=00E04C239987 C:* #Ifs= 2 Cfg#= 1 Atr=e0 MxPwr=500mA I:* If#= 0 Alt= 0 #EPs= 3 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb E: Ad=81(I) Atr=03(Int.) MxPS= 16 Ivl=1ms E: Ad=02(O) Atr=02(Bulk) MxPS= 64 Ivl=0ms E: Ad=82(I) Atr=02(Bulk) MxPS= 64 Ivl=0ms I:* If#= 1 Alt= 0 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb E: Ad=03(O) Atr=01(Isoc) MxPS= 0 Ivl=1ms E: Ad=83(I) Atr=01(Isoc) MxPS= 0 Ivl=1ms I: If#= 1 Alt= 1 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb E: Ad=03(O) Atr=01(Isoc) MxPS= 9 Ivl=1ms E: Ad=83(I) Atr=01(Isoc) MxPS= 9 Ivl=1ms I: If#= 1 Alt= 2 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb E: Ad=03(O) Atr=01(Isoc) MxPS= 17 Ivl=1ms E: Ad=83(I) Atr=01(Isoc) MxPS= 17 Ivl=1ms I: If#= 1 Alt= 3 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb E: Ad=03(O) Atr=01(Isoc) MxPS= 25 Ivl=1ms E: Ad=83(I) Atr=01(Isoc) MxPS= 25 Ivl=1ms I: If#= 1 Alt= 4 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb E: Ad=03(O) Atr=01(Isoc) MxPS= 33 Ivl=1ms E: Ad=83(I) Atr=01(Isoc) MxPS= 33 Ivl=1ms I: If#= 1 Alt= 5 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb E: Ad=03(O) Atr=01(Isoc) MxPS= 49 Ivl=1ms E: Ad=83(I) Atr=01(Isoc) MxPS= 49 Ivl=1ms Signed-off-by: Raman Varabets Signed-off-by: Luiz Augusto von Dentz --- drivers/bluetooth/btusb.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index 64a3febb9dec..e7914783d29e 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -628,6 +628,8 @@ static const struct usb_device_id blacklist_table[] = { BTUSB_WIDEBAND_SPEECH }, { USB_DEVICE(0x7392, 0xc611), .driver_info = BTUSB_REALTEK | BTUSB_WIDEBAND_SPEECH }, + { USB_DEVICE(0x2b89, 0x8761), .driver_info = BTUSB_REALTEK | + BTUSB_WIDEBAND_SPEECH }, /* Additional Realtek 8821AE Bluetooth devices */ { USB_DEVICE(0x0b05, 0x17dc), .driver_info = BTUSB_REALTEK }, From b1e05cfb646cda7c53b5a34ea946c9e506db3c1c Mon Sep 17 00:00:00 2001 From: Yang Yingliang Date: Sat, 12 Nov 2022 17:04:37 +0800 Subject: [PATCH 1946/2002] Bluetooth: hci_bcm4377: Fix missing pci_disable_device() on error in bcm4377_probe() pci_disable_device() need be called while module exiting, switch to use pcim_enable(), pci_disable_device() will be called in pcim_release() after probe() failure. Fixes: ab80b2cec05f ("Bluetooth: hci_bcm4377: Add new driver for BCM4377 PCIe boards") Signed-off-by: Yang Yingliang Reviewed-by: Sven Peter Signed-off-by: Luiz Augusto von Dentz --- drivers/bluetooth/hci_bcm4377.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/bluetooth/hci_bcm4377.c b/drivers/bluetooth/hci_bcm4377.c index 74f44562ac33..19ad0e788646 100644 --- a/drivers/bluetooth/hci_bcm4377.c +++ b/drivers/bluetooth/hci_bcm4377.c @@ -2306,7 +2306,7 @@ static int bcm4377_probe(struct pci_dev *pdev, const struct pci_device_id *id) */ msleep(100); - ret = pci_enable_device(pdev); + ret = pcim_enable_device(pdev); if (ret) return ret; pci_set_master(pdev); From d4e9b8b802525f12375a858a6d3711f9ef4ce779 Mon Sep 17 00:00:00 2001 From: Luca Weiss Date: Sat, 24 Sep 2022 16:21:55 +0200 Subject: [PATCH 1947/2002] dt-bindings: bluetooth: broadcom: add BCM43430A0 & BCM43430A1 Document the compatible string for BCM43430A0 bluetooth used in lg-lenok and BCM43430A1 used in asus-sparrow. Signed-off-by: Luca Weiss Reviewed-by: Linus Walleij Acked-by: Rob Herring Signed-off-by: Luiz Augusto von Dentz --- Documentation/devicetree/bindings/net/broadcom-bluetooth.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Documentation/devicetree/bindings/net/broadcom-bluetooth.yaml b/Documentation/devicetree/bindings/net/broadcom-bluetooth.yaml index e0c8abc27413..b964c7dcec15 100644 --- a/Documentation/devicetree/bindings/net/broadcom-bluetooth.yaml +++ b/Documentation/devicetree/bindings/net/broadcom-bluetooth.yaml @@ -19,6 +19,8 @@ properties: - brcm,bcm4329-bt - brcm,bcm4330-bt - brcm,bcm4334-bt + - brcm,bcm43430a0-bt + - brcm,bcm43430a1-bt - brcm,bcm43438-bt - brcm,bcm4345c5 - brcm,bcm43540-bt From 13fcc94d1bbcbf48ca771d124c0c536e8e414229 Mon Sep 17 00:00:00 2001 From: Andy Chi Date: Mon, 28 Nov 2022 15:49:57 +0800 Subject: [PATCH 1948/2002] Bluetooth: btusb: Add a new VID/PID 0489/e0f2 for MT7922 Add VID/PID 0489/e0f2 for MediaTek MT7922 Bluetooth chip. Found and tested with HP ProBook. From /sys/kernel/debug/usb/devices: T: Bus=03 Lev=01 Prnt=01 Port=02 Cnt=01 Dev#= 2 Spd=480 MxCh= 0 D: Ver= 2.10 Cls=ef(misc ) Sub=02 Prot=01 MxPS=64 #Cfgs= 1 P: Vendor=0489 ProdID=e0f2 Rev= 1.00 S: Manufacturer=MediaTek Inc. S: Product=Wireless_Device S: SerialNumber=000000000 C:* #Ifs= 3 Cfg#= 1 Atr=e0 MxPwr=100mA A: FirstIf#= 0 IfCount= 3 Cls=e0(wlcon) Sub=01 Prot=01 I:* If#= 0 Alt= 0 #EPs= 3 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb E: Ad=81(I) Atr=03(Int.) MxPS= 16 Ivl=125us E: Ad=82(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=02(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms I:* If#= 1 Alt= 0 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb E: Ad=83(I) Atr=01(Isoc) MxPS= 0 Ivl=1ms E: Ad=03(O) Atr=01(Isoc) MxPS= 0 Ivl=1ms I: If#= 1 Alt= 1 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb E: Ad=83(I) Atr=01(Isoc) MxPS= 9 Ivl=1ms E: Ad=03(O) Atr=01(Isoc) MxPS= 9 Ivl=1ms I: If#= 1 Alt= 2 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb E: Ad=83(I) Atr=01(Isoc) MxPS= 17 Ivl=1ms E: Ad=03(O) Atr=01(Isoc) MxPS= 17 Ivl=1ms I: If#= 1 Alt= 3 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb E: Ad=83(I) Atr=01(Isoc) MxPS= 25 Ivl=1ms E: Ad=03(O) Atr=01(Isoc) MxPS= 25 Ivl=1ms I: If#= 1 Alt= 4 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb E: Ad=83(I) Atr=01(Isoc) MxPS= 33 Ivl=1ms E: Ad=03(O) Atr=01(Isoc) MxPS= 33 Ivl=1ms I: If#= 1 Alt= 5 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb E: Ad=83(I) Atr=01(Isoc) MxPS= 49 Ivl=1ms E: Ad=03(O) Atr=01(Isoc) MxPS= 49 Ivl=1ms I: If#= 1 Alt= 6 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb E: Ad=83(I) Atr=01(Isoc) MxPS= 63 Ivl=1ms E: Ad=03(O) Atr=01(Isoc) MxPS= 63 Ivl=1ms I:* If#= 2 Alt= 0 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=(none) E: Ad=8a(I) Atr=03(Int.) MxPS= 64 Ivl=125us E: Ad=0a(O) Atr=03(Int.) MxPS= 64 Ivl=125us I: If#= 2 Alt= 1 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=(none) E: Ad=8a(I) Atr=03(Int.) MxPS= 512 Ivl=125us E: Ad=0a(O) Atr=03(Int.) MxPS= 512 Ivl=125us Signed-off-by: Andy Chi Signed-off-by: Luiz Augusto von Dentz --- drivers/bluetooth/btusb.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index e7914783d29e..0ce209948621 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -596,6 +596,9 @@ static const struct usb_device_id blacklist_table[] = { { USB_DEVICE(0x0489, 0xe0e2), .driver_info = BTUSB_MEDIATEK | BTUSB_WIDEBAND_SPEECH | BTUSB_VALID_LE_STATES }, + { USB_DEVICE(0x0489, 0xe0f2), .driver_info = BTUSB_MEDIATEK | + BTUSB_WIDEBAND_SPEECH | + BTUSB_VALID_LE_STATES }, /* Additional Realtek 8723AE Bluetooth devices */ { USB_DEVICE(0x0930, 0x021d), .driver_info = BTUSB_REALTEK }, From ba6ae1fb680331a8db05063e3fd42615d634277a Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Thu, 24 Nov 2022 22:09:56 -0600 Subject: [PATCH 1949/2002] dt-bindings: net: realtek-bluetooth: Add RTL8723DS RTL8723DS is another variant of the RTL8723 WiFi + Bluetooth chip. It is already supported by the hci_uart/btrtl driver. Document the compatible. Acked-by: Rob Herring Reviewed-by: Alistair Francis Signed-off-by: Samuel Holland Signed-off-by: Luiz Augusto von Dentz --- Documentation/devicetree/bindings/net/realtek-bluetooth.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/net/realtek-bluetooth.yaml b/Documentation/devicetree/bindings/net/realtek-bluetooth.yaml index e329ef06e10f..143b5667abad 100644 --- a/Documentation/devicetree/bindings/net/realtek-bluetooth.yaml +++ b/Documentation/devicetree/bindings/net/realtek-bluetooth.yaml @@ -20,6 +20,7 @@ properties: enum: - realtek,rtl8723bs-bt - realtek,rtl8723cs-bt + - realtek,rtl8723ds-bt - realtek,rtl8822cs-bt device-wake-gpios: From 393b4916b7b5b94faf5c6a7c68df1c62d17e4f38 Mon Sep 17 00:00:00 2001 From: Artem Lukyanov Date: Wed, 23 Nov 2022 11:10:05 +0300 Subject: [PATCH 1950/2002] Bluetooth: btusb: Add Realtek RTL8852BE support ID 0x0cb8:0xc559 Add the support ID(0x0cb8, 0xc559) to usb_device_id table for Realtek RTL8852BE. The device info from /sys/kernel/debug/usb/devices as below. T: Bus=03 Lev=01 Prnt=01 Port=02 Cnt=01 Dev#= 2 Spd=12 MxCh= 0 D: Ver= 1.00 Cls=e0(wlcon) Sub=01 Prot=01 MxPS=64 #Cfgs= 1 P: Vendor=0cb8 ProdID=c559 Rev= 0.00 S: Manufacturer=Realtek S: Product=Bluetooth Radio S: SerialNumber=00e04c000001 C:* #Ifs= 2 Cfg#= 1 Atr=e0 MxPwr=500mA I:* If#= 0 Alt= 0 #EPs= 3 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb E: Ad=81(I) Atr=03(Int.) MxPS= 16 Ivl=1ms E: Ad=02(O) Atr=02(Bulk) MxPS= 64 Ivl=0ms E: Ad=82(I) Atr=02(Bulk) MxPS= 64 Ivl=0ms I:* If#= 1 Alt= 0 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb E: Ad=03(O) Atr=01(Isoc) MxPS= 0 Ivl=1ms E: Ad=83(I) Atr=01(Isoc) MxPS= 0 Ivl=1ms I: If#= 1 Alt= 1 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb E: Ad=03(O) Atr=01(Isoc) MxPS= 9 Ivl=1ms E: Ad=83(I) Atr=01(Isoc) MxPS= 9 Ivl=1ms I: If#= 1 Alt= 2 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb E: Ad=03(O) Atr=01(Isoc) MxPS= 17 Ivl=1ms E: Ad=83(I) Atr=01(Isoc) MxPS= 17 Ivl=1ms I: If#= 1 Alt= 3 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb E: Ad=03(O) Atr=01(Isoc) MxPS= 25 Ivl=1ms E: Ad=83(I) Atr=01(Isoc) MxPS= 25 Ivl=1ms I: If#= 1 Alt= 4 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb E: Ad=03(O) Atr=01(Isoc) MxPS= 33 Ivl=1ms E: Ad=83(I) Atr=01(Isoc) MxPS= 33 Ivl=1ms I: If#= 1 Alt= 5 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb E: Ad=03(O) Atr=01(Isoc) MxPS= 49 Ivl=1ms E: Ad=83(I) Atr=01(Isoc) MxPS= 49 Ivl=1ms Signed-off-by: Artem Lukyanov Signed-off-by: Luiz Augusto von Dentz --- drivers/bluetooth/btusb.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index 0ce209948621..fccc0bf9a109 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -528,6 +528,10 @@ static const struct usb_device_id blacklist_table[] = { { USB_DEVICE(0x13d3, 0x3592), .driver_info = BTUSB_REALTEK | BTUSB_WIDEBAND_SPEECH }, + /* Realtek 8852BE Bluetooth devices */ + { USB_DEVICE(0x0cb8, 0xc559), .driver_info = BTUSB_REALTEK | + BTUSB_WIDEBAND_SPEECH }, + /* Realtek Bluetooth devices */ { USB_VENDOR_AND_INTERFACE_INFO(0x0bda, 0xe0, 0x01, 0x01), .driver_info = BTUSB_REALTEK }, From 6d0a4fe28ac21610c968f9f1ad47e6b705affb5c Mon Sep 17 00:00:00 2001 From: Gongwei Li Date: Thu, 1 Dec 2022 17:43:53 +0800 Subject: [PATCH 1951/2002] Bluetooth: btusb: Add a new PID/VID 13d3/3549 for RTL8822CU * /sys/kernel/debug/usb/devices T: Bus=03 Lev=02 Prnt=02 Port=02 Cnt=03 Dev#= 5 Spd=12 MxCh= 0 D: Ver= 1.00 Cls=e0(wlcon) Sub=01 Prot=01 MxPS=64 #Cfgs= 1 P: Vendor=0bda ProdID=b85b Rev= 0.00 S: Manufacturer=Realtek S: Product=Bluetooth Radio S: SerialNumber=00e04c000001 C:* #Ifs= 2 Cfg#= 1 Atr=e0 MxPwr=500mA I:* If#= 0 Alt= 0 #EPs= 3 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb E: Ad=81(I) Atr=03(Int.) MxPS= 16 Ivl=1ms E: Ad=02(O) Atr=02(Bulk) MxPS= 64 Ivl=0ms E: Ad=82(I) Atr=02(Bulk) MxPS= 64 Ivl=0ms I:* If#= 1 Alt= 0 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb E: Ad=03(O) Atr=01(Isoc) MxPS= 0 Ivl=1ms E: Ad=83(I) Atr=01(Isoc) MxPS= 0 Ivl=1ms I: If#= 1 Alt= 1 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb E: Ad=03(O) Atr=01(Isoc) MxPS= 9 Ivl=1ms E: Ad=83(I) Atr=01(Isoc) MxPS= 9 Ivl=1ms I: If#= 1 Alt= 2 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb E: Ad=03(O) Atr=01(Isoc) MxPS= 17 Ivl=1ms E: Ad=83(I) Atr=01(Isoc) MxPS= 17 Ivl=1ms I: If#= 1 Alt= 3 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb E: Ad=03(O) Atr=01(Isoc) MxPS= 25 Ivl=1ms E: Ad=83(I) Atr=01(Isoc) MxPS= 25 Ivl=1ms I: If#= 1 Alt= 4 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb E: Ad=03(O) Atr=01(Isoc) MxPS= 33 Ivl=1ms E: Ad=83(I) Atr=01(Isoc) MxPS= 33 Ivl=1ms I: If#= 1 Alt= 5 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb E: Ad=03(O) Atr=01(Isoc) MxPS= 49 Ivl=1ms E: Ad=83(I) Atr=01(Isoc) MxPS= 49 Ivl=1ms Signed-off-by: Gongwei Li Signed-off-by: Luiz Augusto von Dentz --- drivers/bluetooth/btusb.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index fccc0bf9a109..d57571cc82d4 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -498,6 +498,10 @@ static const struct usb_device_id blacklist_table[] = { { USB_DEVICE(0x0bda, 0xc822), .driver_info = BTUSB_REALTEK | BTUSB_WIDEBAND_SPEECH }, + /* Realtek 8822CU Bluetooth devices */ + { USB_DEVICE(0x13d3, 0x3549), .driver_info = BTUSB_REALTEK | + BTUSB_WIDEBAND_SPEECH }, + /* Realtek 8852AE Bluetooth devices */ { USB_DEVICE(0x0bda, 0x2852), .driver_info = BTUSB_REALTEK | BTUSB_WIDEBAND_SPEECH }, From cb3648a78e51c90fc95b99677450145119a9675c Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Mon, 5 Dec 2022 09:49:15 -0800 Subject: [PATCH 1952/2002] Bluetooth: btusb: Fix new sparce warnings This fix the following warnings detect with make W=1 C=1: drivers/bluetooth/btusb.c:2212:9: warning: cast to restricted __le16 drivers/bluetooth/btusb.c:2212:9: warning: cast to restricted __le16 drivers/bluetooth/btusb.c:2245:18: warning: cast to restricted __le16 drivers/bluetooth/btusb.c:2249:18: warning: cast to restricted __le16 drivers/bluetooth/btusb.c:2253:18: warning: cast to restricted __le16 drivers/bluetooth/btusb.c:2257:18: warning: cast to restricted __le16 drivers/bluetooth/btusb.c:2261:18: warning: cast to restricted __le16 drivers/bluetooth/btusb.c:2267:18: warning: cast to restricted __le16 Signed-off-by: Luiz Augusto von Dentz --- drivers/bluetooth/btusb.c | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index d57571cc82d4..422c878a9500 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -2201,18 +2201,19 @@ static int btusb_setup_csr(struct hci_dev *hdev) return err; } - if (skb->len != sizeof(struct hci_rp_read_local_version)) { + rp = skb_pull_data(skb, sizeof(*rp)); + if (!rp) { bt_dev_err(hdev, "CSR: Local version length mismatch"); kfree_skb(skb); return -EIO; } - rp = (struct hci_rp_read_local_version *)skb->data; + bt_dev_info(hdev, "CSR: Setting up dongle with HCI ver=%u rev=%04x", + rp->hci_ver, le16_to_cpu(rp->hci_rev)); - bt_dev_info(hdev, "CSR: Setting up dongle with HCI ver=%u rev=%04x; LMP ver=%u subver=%04x; manufacturer=%u", - le16_to_cpu(rp->hci_ver), le16_to_cpu(rp->hci_rev), - le16_to_cpu(rp->lmp_ver), le16_to_cpu(rp->lmp_subver), - le16_to_cpu(rp->manufacturer)); + bt_dev_info(hdev, "LMP ver=%u subver=%04x; manufacturer=%u", + rp->lmp_ver, le16_to_cpu(rp->lmp_subver), + le16_to_cpu(rp->manufacturer)); /* Detect a wide host of Chinese controllers that aren't CSR. * @@ -2242,29 +2243,29 @@ static int btusb_setup_csr(struct hci_dev *hdev) * third-party BT 4.0 dongle reuses it. */ else if (le16_to_cpu(rp->lmp_subver) <= 0x034e && - le16_to_cpu(rp->hci_ver) > BLUETOOTH_VER_1_1) + rp->hci_ver > BLUETOOTH_VER_1_1) is_fake = true; else if (le16_to_cpu(rp->lmp_subver) <= 0x0529 && - le16_to_cpu(rp->hci_ver) > BLUETOOTH_VER_1_2) + rp->hci_ver > BLUETOOTH_VER_1_2) is_fake = true; else if (le16_to_cpu(rp->lmp_subver) <= 0x0c5c && - le16_to_cpu(rp->hci_ver) > BLUETOOTH_VER_2_0) + rp->hci_ver > BLUETOOTH_VER_2_0) is_fake = true; else if (le16_to_cpu(rp->lmp_subver) <= 0x1899 && - le16_to_cpu(rp->hci_ver) > BLUETOOTH_VER_2_1) + rp->hci_ver > BLUETOOTH_VER_2_1) is_fake = true; else if (le16_to_cpu(rp->lmp_subver) <= 0x22bb && - le16_to_cpu(rp->hci_ver) > BLUETOOTH_VER_4_0) + rp->hci_ver > BLUETOOTH_VER_4_0) is_fake = true; /* Other clones which beat all the above checks */ else if (bcdDevice == 0x0134 && le16_to_cpu(rp->lmp_subver) == 0x0c5c && - le16_to_cpu(rp->hci_ver) == BLUETOOTH_VER_2_0) + rp->hci_ver == BLUETOOTH_VER_2_0) is_fake = true; if (is_fake) { From 42d3b43e3f64ba4fec6c849980b88f47ae2e3318 Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Mon, 5 Dec 2022 09:58:43 -0800 Subject: [PATCH 1953/2002] Bluetooth: btusb: Fix existing sparce warning This fix the following warnings detect with make W=1 C=1: drivers/bluetooth/btusb.c:3426:28: warning: cast to restricted __le32 Signed-off-by: Luiz Augusto von Dentz --- drivers/bluetooth/btusb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index 422c878a9500..16e043166055 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -3423,7 +3423,7 @@ static int btusb_setup_qca_load_rampatch(struct hci_dev *hdev, if (ver_rom & ~0xffffU) { rver_rom_high = le16_to_cpu(rver->rom_version_high); - rver_rom = le32_to_cpu(rver_rom_high << 16 | rver_rom_low); + rver_rom = rver_rom_high << 16 | rver_rom_low; } else { rver_rom = rver_rom_low; } From 069ab3f94cfe266bd2810564bf287e765034b4f8 Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Mon, 5 Dec 2022 10:25:52 -0800 Subject: [PATCH 1954/2002] Bluetooth: btintel: Fix existing sparce warnings This fix the following warnings detect with make W=1 C=1: drivers/bluetooth/btintel.c:1041:38: warning: cast to restricted __le32 drivers/bluetooth/btintel.c:1786:25: warning: cast to restricted __le16 drivers/bluetooth/btintel.c:1795:25: warning: cast to restricted __le16 drivers/bluetooth/btintel.c:1796:25: warning: cast to restricted __le16 drivers/bluetooth/btintel.c:1797:25: warning: cast to restricted __le16 Signed-off-by: Luiz Augusto von Dentz --- drivers/bluetooth/btintel.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/bluetooth/btintel.c b/drivers/bluetooth/btintel.c index a657e9a3e96a..7e9a0e52949f 100644 --- a/drivers/bluetooth/btintel.c +++ b/drivers/bluetooth/btintel.c @@ -26,7 +26,7 @@ #define CMD_WRITE_BOOT_PARAMS 0xfc0e struct cmd_write_boot_params { - u32 boot_addr; + __le32 boot_addr; u8 fw_build_num; u8 fw_build_ww; u8 fw_build_yy; @@ -1783,19 +1783,19 @@ static int btintel_get_fw_name(struct intel_version *ver, case 0x0b: /* SfP */ case 0x0c: /* WsP */ snprintf(fw_name, len, "intel/ibt-%u-%u.%s", - le16_to_cpu(ver->hw_variant), - le16_to_cpu(params->dev_revid), - suffix); + ver->hw_variant, + le16_to_cpu(params->dev_revid), + suffix); break; case 0x11: /* JfP */ case 0x12: /* ThP */ case 0x13: /* HrP */ case 0x14: /* CcP */ snprintf(fw_name, len, "intel/ibt-%u-%u-%u.%s", - le16_to_cpu(ver->hw_variant), - le16_to_cpu(ver->hw_revision), - le16_to_cpu(ver->fw_revision), - suffix); + ver->hw_variant, + ver->hw_revision, + ver->fw_revision, + suffix); break; default: return -EINVAL; From 50757a259ba78c4e938b5735e76ffec6cd0c942e Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Mon, 5 Dec 2022 17:11:57 -0800 Subject: [PATCH 1955/2002] Bluetooth: hci_conn: Fix crash on hci_create_cis_sync When attempting to connect multiple ISO sockets without using DEFER_SETUP may result in the following crash: BUG: KASAN: null-ptr-deref in hci_create_cis_sync+0x18b/0x2b0 Read of size 2 at addr 0000000000000036 by task kworker/u3:1/50 CPU: 0 PID: 50 Comm: kworker/u3:1 Not tainted 6.0.0-rc7-02243-gb84a13ff4eda #4373 Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.16.0-1.fc36 04/01/2014 Workqueue: hci0 hci_cmd_sync_work Call Trace: dump_stack_lvl+0x19/0x27 kasan_report+0xbc/0xf0 ? hci_create_cis_sync+0x18b/0x2b0 hci_create_cis_sync+0x18b/0x2b0 ? get_link_mode+0xd0/0xd0 ? __ww_mutex_lock_slowpath+0x10/0x10 ? mutex_lock+0xe0/0xe0 ? get_link_mode+0xd0/0xd0 hci_cmd_sync_work+0x111/0x190 process_one_work+0x427/0x650 worker_thread+0x87/0x750 ? process_one_work+0x650/0x650 kthread+0x14e/0x180 ? kthread_exit+0x50/0x50 ret_from_fork+0x22/0x30 Fixes: 26afbd826ee3 ("Bluetooth: Add initial implementation of CIS connections") Signed-off-by: Luiz Augusto von Dentz --- net/bluetooth/hci_conn.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index 3287b2ca789e..d3e542c2fc3e 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -1879,7 +1879,7 @@ static int hci_create_cis_sync(struct hci_dev *hdev, void *data) continue; /* Check if all CIS(s) belonging to a CIG are ready */ - if (conn->link->state != BT_CONNECTED || + if (!conn->link || conn->link->state != BT_CONNECTED || conn->state != BT_CONNECT) { cmd.cp.num_cis = 0; break; From cee50ce899de415baf4da3ed38b7d4f13c3170d1 Mon Sep 17 00:00:00 2001 From: Wang ShaoBo Date: Mon, 5 Dec 2022 21:53:57 +0800 Subject: [PATCH 1956/2002] Bluetooth: btintel: Fix missing free skb in btintel_setup_combined() skb allocated by __hci_cmd_sync would not be used whether in checking for supported iBT hardware variants or after, we should free it in all error branches, this patch makes the case read version failed or default error case free skb before return. Fixes: c86c7285bb08 ("Bluetooth: btintel: Fix the legacy bootloader returns tlv based version") Fixes: 019a1caa7fd2 ("Bluetooth: btintel: Refactoring setup routine for bootloader devices") Signed-off-by: Wang ShaoBo Reviewed-by: Tedd Ho-Jeong An Signed-off-by: Luiz Augusto von Dentz --- drivers/bluetooth/btintel.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/bluetooth/btintel.c b/drivers/bluetooth/btintel.c index 7e9a0e52949f..d4e2cb9a4eb4 100644 --- a/drivers/bluetooth/btintel.c +++ b/drivers/bluetooth/btintel.c @@ -2524,7 +2524,7 @@ static int btintel_setup_combined(struct hci_dev *hdev) */ err = btintel_read_version(hdev, &ver); if (err) - return err; + break; /* Apply the device specific HCI quirks * @@ -2566,7 +2566,8 @@ static int btintel_setup_combined(struct hci_dev *hdev) default: bt_dev_err(hdev, "Unsupported Intel hw variant (%u)", INTEL_HW_VARIANT(ver_tlv.cnvi_bt)); - return -EINVAL; + err = -EINVAL; + break; } exit_error: From b15a6bd3c80c77faec8317319b97f976b1a08332 Mon Sep 17 00:00:00 2001 From: Yang Yingliang Date: Tue, 6 Dec 2022 20:59:10 +0800 Subject: [PATCH 1957/2002] Bluetooth: btusb: don't call kfree_skb() under spin_lock_irqsave() It is not allowed to call kfree_skb() from hardware interrupt context or with interrupts being disabled. So replace kfree_skb() with dev_kfree_skb_irq() under spin_lock_irqsave(). Fixes: 803b58367ffb ("Bluetooth: btusb: Implement driver internal packet reassembly") Signed-off-by: Yang Yingliang Signed-off-by: Luiz Augusto von Dentz --- drivers/bluetooth/btusb.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index 16e043166055..2ad4efdd9e40 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -916,13 +916,13 @@ static inline void btusb_free_frags(struct btusb_data *data) spin_lock_irqsave(&data->rxlock, flags); - kfree_skb(data->evt_skb); + dev_kfree_skb_irq(data->evt_skb); data->evt_skb = NULL; - kfree_skb(data->acl_skb); + dev_kfree_skb_irq(data->acl_skb); data->acl_skb = NULL; - kfree_skb(data->sco_skb); + dev_kfree_skb_irq(data->sco_skb); data->sco_skb = NULL; spin_unlock_irqrestore(&data->rxlock, flags); From df4cfc91208e0a98f078223793f5871b1a82cc54 Mon Sep 17 00:00:00 2001 From: Yang Yingliang Date: Wed, 7 Dec 2022 10:18:30 +0800 Subject: [PATCH 1958/2002] Bluetooth: hci_qca: don't call kfree_skb() under spin_lock_irqsave() It is not allowed to call kfree_skb() from hardware interrupt context or with interrupts being disabled. So replace kfree_skb() with dev_kfree_skb_irq() under spin_lock_irqsave(). Fixes: 0ff252c1976d ("Bluetooth: hciuart: Add support QCA chipset for UART") Signed-off-by: Yang Yingliang Signed-off-by: Luiz Augusto von Dentz --- drivers/bluetooth/hci_qca.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/bluetooth/hci_qca.c b/drivers/bluetooth/hci_qca.c index dacb6f5efd29..3df9e692756a 100644 --- a/drivers/bluetooth/hci_qca.c +++ b/drivers/bluetooth/hci_qca.c @@ -912,7 +912,7 @@ static int qca_enqueue(struct hci_uart *hu, struct sk_buff *skb) default: BT_ERR("Illegal tx state: %d (losing packet)", qca->tx_ibs_state); - kfree_skb(skb); + dev_kfree_skb_irq(skb); break; } From 8f458f783dfbb19c1f1cb58ed06eeb701f52091b Mon Sep 17 00:00:00 2001 From: Yang Yingliang Date: Wed, 7 Dec 2022 10:18:31 +0800 Subject: [PATCH 1959/2002] Bluetooth: hci_ll: don't call kfree_skb() under spin_lock_irqsave() It is not allowed to call kfree_skb() from hardware interrupt context or with interrupts being disabled. So replace kfree_skb() with dev_kfree_skb_irq() under spin_lock_irqsave(). Fixes: 166d2f6a4332 ("[Bluetooth] Add UART driver for Texas Instruments' BRF63xx chips") Signed-off-by: Yang Yingliang Signed-off-by: Luiz Augusto von Dentz --- drivers/bluetooth/hci_ll.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/bluetooth/hci_ll.c b/drivers/bluetooth/hci_ll.c index 4eb420a9ed04..5abc01a2acf7 100644 --- a/drivers/bluetooth/hci_ll.c +++ b/drivers/bluetooth/hci_ll.c @@ -345,7 +345,7 @@ static int ll_enqueue(struct hci_uart *hu, struct sk_buff *skb) default: BT_ERR("illegal hcill state: %ld (losing packet)", ll->hcill_state); - kfree_skb(skb); + dev_kfree_skb_irq(skb); break; } From 383630cc6758d619874c2e8bb2f68a61f3f9ef6e Mon Sep 17 00:00:00 2001 From: Yang Yingliang Date: Wed, 7 Dec 2022 10:18:32 +0800 Subject: [PATCH 1960/2002] Bluetooth: hci_h5: don't call kfree_skb() under spin_lock_irqsave() It is not allowed to call kfree_skb() from hardware interrupt context or with interrupts being disabled. So replace kfree_skb() with dev_kfree_skb_irq() under spin_lock_irqsave(). Fixes: 43eb12d78960 ("Bluetooth: Fix/implement Three-wire reliable packet sending") Signed-off-by: Yang Yingliang Signed-off-by: Luiz Augusto von Dentz --- drivers/bluetooth/hci_h5.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/bluetooth/hci_h5.c b/drivers/bluetooth/hci_h5.c index c5a0409ef84f..6455bc4fb5bb 100644 --- a/drivers/bluetooth/hci_h5.c +++ b/drivers/bluetooth/hci_h5.c @@ -313,7 +313,7 @@ static void h5_pkt_cull(struct h5 *h5) break; __skb_unlink(skb, &h5->unack); - kfree_skb(skb); + dev_kfree_skb_irq(skb); } if (skb_queue_empty(&h5->unack)) From 7b503e339c1a80bf0051ec2d19c3bc777014ac61 Mon Sep 17 00:00:00 2001 From: Yang Yingliang Date: Wed, 7 Dec 2022 10:18:33 +0800 Subject: [PATCH 1961/2002] Bluetooth: hci_bcsp: don't call kfree_skb() under spin_lock_irqsave() It is not allowed to call kfree_skb() from hardware interrupt context or with interrupts being disabled. So replace kfree_skb() with dev_kfree_skb_irq() under spin_lock_irqsave(). Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Signed-off-by: Yang Yingliang Signed-off-by: Luiz Augusto von Dentz --- drivers/bluetooth/hci_bcsp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/bluetooth/hci_bcsp.c b/drivers/bluetooth/hci_bcsp.c index cf4a56095817..8055f63603f4 100644 --- a/drivers/bluetooth/hci_bcsp.c +++ b/drivers/bluetooth/hci_bcsp.c @@ -378,7 +378,7 @@ static void bcsp_pkt_cull(struct bcsp_struct *bcsp) i++; __skb_unlink(skb, &bcsp->unack); - kfree_skb(skb); + dev_kfree_skb_irq(skb); } if (skb_queue_empty(&bcsp->unack)) From 39c1eb6fcbae8ce9bb71b2ac5cb609355a2b181b Mon Sep 17 00:00:00 2001 From: Yang Yingliang Date: Wed, 7 Dec 2022 10:18:34 +0800 Subject: [PATCH 1962/2002] Bluetooth: hci_core: don't call kfree_skb() under spin_lock_irqsave() It is not allowed to call kfree_skb() from hardware interrupt context or with interrupts being disabled. So replace kfree_skb() with dev_kfree_skb_irq() under spin_lock_irqsave(). Fixes: 9238f36a5a50 ("Bluetooth: Add request cmd_complete and cmd_status functions") Signed-off-by: Yang Yingliang Signed-off-by: Luiz Augusto von Dentz --- net/bluetooth/hci_core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 56f8569ace86..b65c3aabcd53 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -3985,7 +3985,7 @@ void hci_req_cmd_complete(struct hci_dev *hdev, u16 opcode, u8 status, *req_complete_skb = bt_cb(skb)->hci.req_complete_skb; else *req_complete = bt_cb(skb)->hci.req_complete; - kfree_skb(skb); + dev_kfree_skb_irq(skb); } spin_unlock_irqrestore(&hdev->cmd_q.lock, flags); } From 0ba18967d4544955b2eff2fbc4f2a8750c4df90a Mon Sep 17 00:00:00 2001 From: Yang Yingliang Date: Wed, 7 Dec 2022 10:18:35 +0800 Subject: [PATCH 1963/2002] Bluetooth: RFCOMM: don't call kfree_skb() under spin_lock_irqsave() It is not allowed to call kfree_skb() from hardware interrupt context or with interrupts being disabled. So replace kfree_skb() with dev_kfree_skb_irq() under spin_lock_irqsave(). Fixes: 81be03e026dc ("Bluetooth: RFCOMM: Replace use of memcpy_from_msg with bt_skb_sendmmsg") Signed-off-by: Yang Yingliang Signed-off-by: Luiz Augusto von Dentz --- net/bluetooth/rfcomm/core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/bluetooth/rfcomm/core.c b/net/bluetooth/rfcomm/core.c index 7324764384b6..8d6fce9005bd 100644 --- a/net/bluetooth/rfcomm/core.c +++ b/net/bluetooth/rfcomm/core.c @@ -590,7 +590,7 @@ int rfcomm_dlc_send(struct rfcomm_dlc *d, struct sk_buff *skb) ret = rfcomm_dlc_send_frag(d, frag); if (ret < 0) { - kfree_skb(frag); + dev_kfree_skb_irq(frag); goto unlock; } From 241f51931c35085449502c10f64fb3ecd6e02171 Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Tue, 6 Dec 2022 16:34:42 -0800 Subject: [PATCH 1964/2002] Bluetooth: ISO: Avoid circular locking dependency This attempts to avoid circular locking dependency between sock_lock and hdev_lock: WARNING: possible circular locking dependency detected 6.0.0-rc7-03728-g18dd8ab0a783 #3 Not tainted ------------------------------------------------------ kworker/u3:2/53 is trying to acquire lock: ffff888000254130 (sk_lock-AF_BLUETOOTH-BTPROTO_ISO){+.+.}-{0:0}, at: iso_conn_del+0xbd/0x1d0 but task is already holding lock: ffffffff9f39a080 (hci_cb_list_lock){+.+.}-{3:3}, at: hci_le_cis_estabilished_evt+0x1b5/0x500 which lock already depends on the new lock. the existing dependency chain (in reverse order) is: -> #2 (hci_cb_list_lock){+.+.}-{3:3}: __mutex_lock+0x10e/0xfe0 hci_le_remote_feat_complete_evt+0x17f/0x320 hci_event_packet+0x39c/0x7d0 hci_rx_work+0x2bf/0x950 process_one_work+0x569/0x980 worker_thread+0x2a3/0x6f0 kthread+0x153/0x180 ret_from_fork+0x22/0x30 -> #1 (&hdev->lock){+.+.}-{3:3}: __mutex_lock+0x10e/0xfe0 iso_connect_cis+0x6f/0x5a0 iso_sock_connect+0x1af/0x710 __sys_connect+0x17e/0x1b0 __x64_sys_connect+0x37/0x50 do_syscall_64+0x43/0x90 entry_SYSCALL_64_after_hwframe+0x62/0xcc -> #0 (sk_lock-AF_BLUETOOTH-BTPROTO_ISO){+.+.}-{0:0}: __lock_acquire+0x1b51/0x33d0 lock_acquire+0x16f/0x3b0 lock_sock_nested+0x32/0x80 iso_conn_del+0xbd/0x1d0 iso_connect_cfm+0x226/0x680 hci_le_cis_estabilished_evt+0x1ed/0x500 hci_event_packet+0x39c/0x7d0 hci_rx_work+0x2bf/0x950 process_one_work+0x569/0x980 worker_thread+0x2a3/0x6f0 kthread+0x153/0x180 ret_from_fork+0x22/0x30 other info that might help us debug this: Chain exists of: sk_lock-AF_BLUETOOTH-BTPROTO_ISO --> &hdev->lock --> hci_cb_list_lock Possible unsafe locking scenario: CPU0 CPU1 ---- ---- lock(hci_cb_list_lock); lock(&hdev->lock); lock(hci_cb_list_lock); lock(sk_lock-AF_BLUETOOTH-BTPROTO_ISO); *** DEADLOCK *** 4 locks held by kworker/u3:2/53: #0: ffff8880021d9130 ((wq_completion)hci0#2){+.+.}-{0:0}, at: process_one_work+0x4ad/0x980 #1: ffff888002387de0 ((work_completion)(&hdev->rx_work)){+.+.}-{0:0}, at: process_one_work+0x4ad/0x980 #2: ffff888001ac0070 (&hdev->lock){+.+.}-{3:3}, at: hci_le_cis_estabilished_evt+0xc3/0x500 #3: ffffffff9f39a080 (hci_cb_list_lock){+.+.}-{3:3}, at: hci_le_cis_estabilished_evt+0x1b5/0x500 Signed-off-by: Luiz Augusto von Dentz --- net/bluetooth/iso.c | 61 ++++++++++++++++++++++++++++----------------- 1 file changed, 38 insertions(+), 23 deletions(-) diff --git a/net/bluetooth/iso.c b/net/bluetooth/iso.c index e23aabf4e0cf..035bb5d25f85 100644 --- a/net/bluetooth/iso.c +++ b/net/bluetooth/iso.c @@ -261,13 +261,13 @@ static int iso_connect_bis(struct sock *sk) if (!bis_capable(hdev)) { err = -EOPNOTSUPP; - goto done; + goto unlock; } /* Fail if out PHYs are marked as disabled */ if (!iso_pi(sk)->qos.out.phy) { err = -EINVAL; - goto done; + goto unlock; } hcon = hci_connect_bis(hdev, &iso_pi(sk)->dst, @@ -276,22 +276,27 @@ static int iso_connect_bis(struct sock *sk) iso_pi(sk)->base); if (IS_ERR(hcon)) { err = PTR_ERR(hcon); - goto done; + goto unlock; } conn = iso_conn_add(hcon); if (!conn) { hci_conn_drop(hcon); err = -ENOMEM; - goto done; + goto unlock; } + hci_dev_unlock(hdev); + hci_dev_put(hdev); + + lock_sock(sk); + /* Update source addr of the socket */ bacpy(&iso_pi(sk)->src, &hcon->src); err = iso_chan_add(conn, sk, NULL); if (err) - goto done; + goto release; if (hcon->state == BT_CONNECTED) { iso_sock_clear_timer(sk); @@ -301,7 +306,11 @@ static int iso_connect_bis(struct sock *sk) iso_sock_set_timer(sk, sk->sk_sndtimeo); } -done: +release: + release_sock(sk); + return err; + +unlock: hci_dev_unlock(hdev); hci_dev_put(hdev); return err; @@ -325,13 +334,13 @@ static int iso_connect_cis(struct sock *sk) if (!cis_central_capable(hdev)) { err = -EOPNOTSUPP; - goto done; + goto unlock; } /* Fail if either PHYs are marked as disabled */ if (!iso_pi(sk)->qos.in.phy && !iso_pi(sk)->qos.out.phy) { err = -EINVAL; - goto done; + goto unlock; } /* Just bind if DEFER_SETUP has been set */ @@ -341,7 +350,7 @@ static int iso_connect_cis(struct sock *sk) &iso_pi(sk)->qos); if (IS_ERR(hcon)) { err = PTR_ERR(hcon); - goto done; + goto unlock; } } else { hcon = hci_connect_cis(hdev, &iso_pi(sk)->dst, @@ -349,7 +358,7 @@ static int iso_connect_cis(struct sock *sk) &iso_pi(sk)->qos); if (IS_ERR(hcon)) { err = PTR_ERR(hcon); - goto done; + goto unlock; } } @@ -357,15 +366,20 @@ static int iso_connect_cis(struct sock *sk) if (!conn) { hci_conn_drop(hcon); err = -ENOMEM; - goto done; + goto unlock; } + hci_dev_unlock(hdev); + hci_dev_put(hdev); + + lock_sock(sk); + /* Update source addr of the socket */ bacpy(&iso_pi(sk)->src, &hcon->src); err = iso_chan_add(conn, sk, NULL); if (err) - goto done; + goto release; if (hcon->state == BT_CONNECTED) { iso_sock_clear_timer(sk); @@ -378,7 +392,11 @@ static int iso_connect_cis(struct sock *sk) iso_sock_set_timer(sk, sk->sk_sndtimeo); } -done: +release: + release_sock(sk); + return err; + +unlock: hci_dev_unlock(hdev); hci_dev_put(hdev); return err; @@ -832,20 +850,23 @@ static int iso_sock_connect(struct socket *sock, struct sockaddr *addr, bacpy(&iso_pi(sk)->dst, &sa->iso_bdaddr); iso_pi(sk)->dst_type = sa->iso_bdaddr_type; + release_sock(sk); + if (bacmp(&iso_pi(sk)->dst, BDADDR_ANY)) err = iso_connect_cis(sk); else err = iso_connect_bis(sk); if (err) - goto done; + return err; + + lock_sock(sk); if (!test_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags)) { err = bt_sock_wait_state(sk, BT_CONNECTED, sock_sndtimeo(sk, flags & O_NONBLOCK)); } -done: release_sock(sk); return err; } @@ -1101,28 +1122,22 @@ static int iso_sock_recvmsg(struct socket *sock, struct msghdr *msg, { struct sock *sk = sock->sk; struct iso_pinfo *pi = iso_pi(sk); - int err; BT_DBG("sk %p", sk); - lock_sock(sk); - if (test_and_clear_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags)) { switch (sk->sk_state) { case BT_CONNECT2: + lock_sock(sk); iso_conn_defer_accept(pi->conn->hcon); sk->sk_state = BT_CONFIG; release_sock(sk); return 0; case BT_CONNECT: - err = iso_connect_cis(sk); - release_sock(sk); - return err; + return iso_connect_cis(sk); } } - release_sock(sk); - return bt_sock_recvmsg(sock, msg, len, flags); } From 7aca0ac4792e6cb0f35ef97bfcb39b1663a92fb7 Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Wed, 7 Dec 2022 11:56:57 -0800 Subject: [PATCH 1965/2002] Bluetooth: Wait for HCI_OP_WRITE_AUTH_PAYLOAD_TO to complete This make sure HCI_OP_WRITE_AUTH_PAYLOAD_TO completes before notifying the encryption change just as is done with HCI_OP_READ_ENC_KEY_SIZE. Signed-off-by: Luiz Augusto von Dentz --- net/bluetooth/hci_event.c | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index ade2628aae0d..0594af4e37ca 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -801,9 +801,6 @@ static u8 hci_cc_write_auth_payload_timeout(struct hci_dev *hdev, void *data, bt_dev_dbg(hdev, "status 0x%2.2x", rp->status); - if (rp->status) - return rp->status; - sent = hci_sent_cmd_data(hdev, HCI_OP_WRITE_AUTH_PAYLOAD_TO); if (!sent) return rp->status; @@ -811,9 +808,17 @@ static u8 hci_cc_write_auth_payload_timeout(struct hci_dev *hdev, void *data, hci_dev_lock(hdev); conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(rp->handle)); - if (conn) + if (!conn) { + rp->status = 0xff; + goto unlock; + } + + if (!rp->status) conn->auth_payload_timeout = get_unaligned_le16(sent + 2); + hci_encrypt_cfm(conn, 0); + +unlock: hci_dev_unlock(hdev); return rp->status; @@ -3680,8 +3685,13 @@ static void hci_encrypt_change_evt(struct hci_dev *hdev, void *data, cp.handle = cpu_to_le16(conn->handle); cp.timeout = cpu_to_le16(hdev->auth_payload_timeout); - hci_send_cmd(conn->hdev, HCI_OP_WRITE_AUTH_PAYLOAD_TO, - sizeof(cp), &cp); + if (hci_send_cmd(conn->hdev, HCI_OP_WRITE_AUTH_PAYLOAD_TO, + sizeof(cp), &cp)) { + bt_dev_err(hdev, "write auth payload timeout failed"); + goto notify; + } + + goto unlock; } notify: From 8a1786b7d44180ad8316d280d99524db6272198f Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Fri, 9 Dec 2022 19:28:14 +0200 Subject: [PATCH 1966/2002] net: dsa: mv88e6xxx: remove ATU age out violation print Currently, the MV88E6XXX_PORT_ASSOC_VECTOR_INT_AGE_OUT bit (interrupt on age out) is not enabled by the driver, and as a result, the print for age out violations is dead code. Remove it until there is some way for this to be triggered. Signed-off-by: Vladimir Oltean Reviewed-by: Florian Fainelli Signed-off-by: Jakub Kicinski --- drivers/net/dsa/mv88e6xxx/global1_atu.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/drivers/net/dsa/mv88e6xxx/global1_atu.c b/drivers/net/dsa/mv88e6xxx/global1_atu.c index 40bd67a5c8e9..7a1c4b917339 100644 --- a/drivers/net/dsa/mv88e6xxx/global1_atu.c +++ b/drivers/net/dsa/mv88e6xxx/global1_atu.c @@ -378,12 +378,6 @@ static irqreturn_t mv88e6xxx_g1_atu_prob_irq_thread_fn(int irq, void *dev_id) spid = entry.state; - if (val & MV88E6XXX_G1_ATU_OP_AGE_OUT_VIOLATION) { - dev_err_ratelimited(chip->dev, - "ATU age out violation for %pM\n", - entry.mac); - } - if (val & MV88E6XXX_G1_ATU_OP_MEMBER_VIOLATION) { dev_err_ratelimited(chip->dev, "ATU member violation for %pM portvec %x spid %d\n", From 4bf24ad09bc0b05e97fb48b962b2c9246fc76727 Mon Sep 17 00:00:00 2001 From: "Hans J. Schultz" Date: Fri, 9 Dec 2022 19:28:15 +0200 Subject: [PATCH 1967/2002] net: dsa: mv88e6xxx: read FID when handling ATU violations When an ATU violation occurs, the switch uses the ATU FID register to report the FID of the MAC address that incurred the violation. It would be good for the driver to know the FID value for purposes such as logging and CPU-based authentication. Up until now, the driver has been calling the mv88e6xxx_g1_atu_op() function to read ATU violations, but that doesn't do exactly what we want, namely it calls mv88e6xxx_g1_atu_fid_write() with FID 0. (side note, the documentation for the ATU Get/Clear Violation command says that writes to the ATU FID register have no effect before the operation starts, it's only that we disregard the value that this register provides once the operation completes) So mv88e6xxx_g1_atu_fid_write() is not what we want, but rather mv88e6xxx_g1_atu_fid_read(). However, the latter doesn't exist, we need to write it. The remainder of mv88e6xxx_g1_atu_op() except for mv88e6xxx_g1_atu_fid_write() is still needed, namely to send a GET_CLR_VIOLATION command to the ATU. In principle we could have still kept calling mv88e6xxx_g1_atu_op(), but the MDIO writes to the ATU FID register are pointless, but in the interest of doing less CPU work per interrupt, write a new function called mv88e6xxx_g1_read_atu_violation() and call it. The FID will be the port default FID as set by mv88e6xxx_port_set_fid() if the VID from the packet cannot be found in the VTU. Otherwise it is the FID derived from the VTU entry associated with that VID. Signed-off-by: Hans J. Schultz Signed-off-by: Vladimir Oltean Reviewed-by: Florian Fainelli Signed-off-by: Jakub Kicinski --- drivers/net/dsa/mv88e6xxx/global1_atu.c | 72 +++++++++++++++++++++---- 1 file changed, 61 insertions(+), 11 deletions(-) diff --git a/drivers/net/dsa/mv88e6xxx/global1_atu.c b/drivers/net/dsa/mv88e6xxx/global1_atu.c index 7a1c4b917339..b7e62ff4b599 100644 --- a/drivers/net/dsa/mv88e6xxx/global1_atu.c +++ b/drivers/net/dsa/mv88e6xxx/global1_atu.c @@ -114,6 +114,19 @@ static int mv88e6xxx_g1_atu_op_wait(struct mv88e6xxx_chip *chip) return mv88e6xxx_g1_wait_bit(chip, MV88E6XXX_G1_ATU_OP, bit, 0); } +static int mv88e6xxx_g1_read_atu_violation(struct mv88e6xxx_chip *chip) +{ + int err; + + err = mv88e6xxx_g1_write(chip, MV88E6XXX_G1_ATU_OP, + MV88E6XXX_G1_ATU_OP_BUSY | + MV88E6XXX_G1_ATU_OP_GET_CLR_VIOLATION); + if (err) + return err; + + return mv88e6xxx_g1_atu_op_wait(chip); +} + static int mv88e6xxx_g1_atu_op(struct mv88e6xxx_chip *chip, u16 fid, u16 op) { u16 val; @@ -159,6 +172,41 @@ int mv88e6xxx_g1_atu_get_next(struct mv88e6xxx_chip *chip, u16 fid) return mv88e6xxx_g1_atu_op(chip, fid, MV88E6XXX_G1_ATU_OP_GET_NEXT_DB); } +static int mv88e6xxx_g1_atu_fid_read(struct mv88e6xxx_chip *chip, u16 *fid) +{ + u16 val = 0, upper = 0, op = 0; + int err = -EOPNOTSUPP; + + if (mv88e6xxx_num_databases(chip) > 256) { + err = mv88e6xxx_g1_read(chip, MV88E6352_G1_ATU_FID, &val); + val &= 0xfff; + if (err) + return err; + } else { + err = mv88e6xxx_g1_read(chip, MV88E6XXX_G1_ATU_OP, &op); + if (err) + return err; + if (mv88e6xxx_num_databases(chip) > 64) { + /* ATU DBNum[7:4] are located in ATU Control 15:12 */ + err = mv88e6xxx_g1_read(chip, MV88E6XXX_G1_ATU_CTL, + &upper); + if (err) + return err; + + upper = (upper >> 8) & 0x00f0; + } else if (mv88e6xxx_num_databases(chip) > 16) { + /* ATU DBNum[5:4] are located in ATU Operation 9:8 */ + upper = (op >> 4) & 0x30; + } + + /* ATU DBNum[3:0] are located in ATU Operation 3:0 */ + val = (op & 0xf) | upper; + } + *fid = val; + + return err; +} + /* Offset 0x0C: ATU Data Register */ static int mv88e6xxx_g1_atu_data_read(struct mv88e6xxx_chip *chip, @@ -353,14 +401,12 @@ static irqreturn_t mv88e6xxx_g1_atu_prob_irq_thread_fn(int irq, void *dev_id) { struct mv88e6xxx_chip *chip = dev_id; struct mv88e6xxx_atu_entry entry; - int spid; - int err; - u16 val; + int err, spid; + u16 val, fid; mv88e6xxx_reg_lock(chip); - err = mv88e6xxx_g1_atu_op(chip, 0, - MV88E6XXX_G1_ATU_OP_GET_CLR_VIOLATION); + err = mv88e6xxx_g1_read_atu_violation(chip); if (err) goto out; @@ -368,6 +414,10 @@ static irqreturn_t mv88e6xxx_g1_atu_prob_irq_thread_fn(int irq, void *dev_id) if (err) goto out; + err = mv88e6xxx_g1_atu_fid_read(chip, &fid); + if (err) + goto out; + err = mv88e6xxx_g1_atu_data_read(chip, &entry); if (err) goto out; @@ -380,22 +430,22 @@ static irqreturn_t mv88e6xxx_g1_atu_prob_irq_thread_fn(int irq, void *dev_id) if (val & MV88E6XXX_G1_ATU_OP_MEMBER_VIOLATION) { dev_err_ratelimited(chip->dev, - "ATU member violation for %pM portvec %x spid %d\n", - entry.mac, entry.portvec, spid); + "ATU member violation for %pM fid %u portvec %x spid %d\n", + entry.mac, fid, entry.portvec, spid); chip->ports[spid].atu_member_violation++; } if (val & MV88E6XXX_G1_ATU_OP_MISS_VIOLATION) { dev_err_ratelimited(chip->dev, - "ATU miss violation for %pM portvec %x spid %d\n", - entry.mac, entry.portvec, spid); + "ATU miss violation for %pM fid %u portvec %x spid %d\n", + entry.mac, fid, entry.portvec, spid); chip->ports[spid].atu_miss_violation++; } if (val & MV88E6XXX_G1_ATU_OP_FULL_VIOLATION) { dev_err_ratelimited(chip->dev, - "ATU full violation for %pM portvec %x spid %d\n", - entry.mac, entry.portvec, spid); + "ATU full violation for %pM fid %u portvec %x spid %d\n", + entry.mac, fid, entry.portvec, spid); chip->ports[spid].atu_full_violation++; } mv88e6xxx_reg_unlock(chip); From 8646384d80f3d3b4a66b3284dbbd8232d1b8799e Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Fri, 9 Dec 2022 19:28:16 +0200 Subject: [PATCH 1968/2002] net: dsa: mv88e6xxx: replace ATU violation prints with trace points In applications where the switch ports must perform 802.1X based authentication and are therefore locked, ATU violation interrupts are quite to be expected as part of normal operation. The problem is that they currently spam the kernel log, even if rate limited. Create a series of trace points, all derived from the same event class, which log these violations to the kernel's trace buffer, which is both much faster and much easier to ignore than printing to a serial console. New usage model: $ trace-cmd list | grep mv88e6xxx mv88e6xxx mv88e6xxx:mv88e6xxx_atu_full_violation mv88e6xxx:mv88e6xxx_atu_miss_violation mv88e6xxx:mv88e6xxx_atu_member_violation $ trace-cmd record -e mv88e6xxx sleep 10 Signed-off-by: Vladimir Oltean Reviewed-by: Saeed Mahameed Reviewed-by: Florian Fainelli Signed-off-by: Jakub Kicinski --- drivers/net/dsa/mv88e6xxx/Makefile | 4 ++ drivers/net/dsa/mv88e6xxx/global1_atu.c | 19 +++---- drivers/net/dsa/mv88e6xxx/trace.c | 6 +++ drivers/net/dsa/mv88e6xxx/trace.h | 66 +++++++++++++++++++++++++ 4 files changed, 86 insertions(+), 9 deletions(-) create mode 100644 drivers/net/dsa/mv88e6xxx/trace.c create mode 100644 drivers/net/dsa/mv88e6xxx/trace.h diff --git a/drivers/net/dsa/mv88e6xxx/Makefile b/drivers/net/dsa/mv88e6xxx/Makefile index c8eca2b6f959..49bf358b9c4f 100644 --- a/drivers/net/dsa/mv88e6xxx/Makefile +++ b/drivers/net/dsa/mv88e6xxx/Makefile @@ -15,3 +15,7 @@ mv88e6xxx-objs += port_hidden.o mv88e6xxx-$(CONFIG_NET_DSA_MV88E6XXX_PTP) += ptp.o mv88e6xxx-objs += serdes.o mv88e6xxx-objs += smi.o +mv88e6xxx-objs += trace.o + +# for tracing framework to find trace.h +CFLAGS_trace.o := -I$(src) diff --git a/drivers/net/dsa/mv88e6xxx/global1_atu.c b/drivers/net/dsa/mv88e6xxx/global1_atu.c index b7e62ff4b599..61ae2d61e25c 100644 --- a/drivers/net/dsa/mv88e6xxx/global1_atu.c +++ b/drivers/net/dsa/mv88e6xxx/global1_atu.c @@ -12,6 +12,7 @@ #include "chip.h" #include "global1.h" +#include "trace.h" /* Offset 0x01: ATU FID Register */ @@ -429,23 +430,23 @@ static irqreturn_t mv88e6xxx_g1_atu_prob_irq_thread_fn(int irq, void *dev_id) spid = entry.state; if (val & MV88E6XXX_G1_ATU_OP_MEMBER_VIOLATION) { - dev_err_ratelimited(chip->dev, - "ATU member violation for %pM fid %u portvec %x spid %d\n", - entry.mac, fid, entry.portvec, spid); + trace_mv88e6xxx_atu_member_violation(chip->dev, spid, + entry.portvec, entry.mac, + fid); chip->ports[spid].atu_member_violation++; } if (val & MV88E6XXX_G1_ATU_OP_MISS_VIOLATION) { - dev_err_ratelimited(chip->dev, - "ATU miss violation for %pM fid %u portvec %x spid %d\n", - entry.mac, fid, entry.portvec, spid); + trace_mv88e6xxx_atu_miss_violation(chip->dev, spid, + entry.portvec, entry.mac, + fid); chip->ports[spid].atu_miss_violation++; } if (val & MV88E6XXX_G1_ATU_OP_FULL_VIOLATION) { - dev_err_ratelimited(chip->dev, - "ATU full violation for %pM fid %u portvec %x spid %d\n", - entry.mac, fid, entry.portvec, spid); + trace_mv88e6xxx_atu_full_violation(chip->dev, spid, + entry.portvec, entry.mac, + fid); chip->ports[spid].atu_full_violation++; } mv88e6xxx_reg_unlock(chip); diff --git a/drivers/net/dsa/mv88e6xxx/trace.c b/drivers/net/dsa/mv88e6xxx/trace.c new file mode 100644 index 000000000000..7833cb50ca5d --- /dev/null +++ b/drivers/net/dsa/mv88e6xxx/trace.c @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* Copyright 2022 NXP + */ + +#define CREATE_TRACE_POINTS +#include "trace.h" diff --git a/drivers/net/dsa/mv88e6xxx/trace.h b/drivers/net/dsa/mv88e6xxx/trace.h new file mode 100644 index 000000000000..d9ab5c8dee55 --- /dev/null +++ b/drivers/net/dsa/mv88e6xxx/trace.h @@ -0,0 +1,66 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* Copyright 2022 NXP + */ + +#undef TRACE_SYSTEM +#define TRACE_SYSTEM mv88e6xxx + +#if !defined(_MV88E6XXX_TRACE_H) || defined(TRACE_HEADER_MULTI_READ) +#define _MV88E6XXX_TRACE_H + +#include +#include +#include + +DECLARE_EVENT_CLASS(mv88e6xxx_atu_violation, + + TP_PROTO(const struct device *dev, int spid, u16 portvec, + const unsigned char *addr, u16 fid), + + TP_ARGS(dev, spid, portvec, addr, fid), + + TP_STRUCT__entry( + __string(name, dev_name(dev)) + __field(int, spid) + __field(u16, portvec) + __array(unsigned char, addr, ETH_ALEN) + __field(u16, fid) + ), + + TP_fast_assign( + __assign_str(name, dev_name(dev)); + __entry->spid = spid; + __entry->portvec = portvec; + memcpy(__entry->addr, addr, ETH_ALEN); + __entry->fid = fid; + ), + + TP_printk("dev %s spid %d portvec 0x%x addr %pM fid %u", + __get_str(name), __entry->spid, __entry->portvec, + __entry->addr, __entry->fid) +); + +DEFINE_EVENT(mv88e6xxx_atu_violation, mv88e6xxx_atu_member_violation, + TP_PROTO(const struct device *dev, int spid, u16 portvec, + const unsigned char *addr, u16 fid), + TP_ARGS(dev, spid, portvec, addr, fid)); + +DEFINE_EVENT(mv88e6xxx_atu_violation, mv88e6xxx_atu_miss_violation, + TP_PROTO(const struct device *dev, int spid, u16 portvec, + const unsigned char *addr, u16 fid), + TP_ARGS(dev, spid, portvec, addr, fid)); + +DEFINE_EVENT(mv88e6xxx_atu_violation, mv88e6xxx_atu_full_violation, + TP_PROTO(const struct device *dev, int spid, u16 portvec, + const unsigned char *addr, u16 fid), + TP_ARGS(dev, spid, portvec, addr, fid)); + +#endif /* _MV88E6XXX_TRACE_H */ + +/* We don't want to use include/trace/events */ +#undef TRACE_INCLUDE_PATH +#define TRACE_INCLUDE_PATH . +#undef TRACE_INCLUDE_FILE +#define TRACE_INCLUDE_FILE trace +/* This part must be outside protection */ +#include From 9e3d9ae52b5657399a7b61258cc7482434a911bb Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Fri, 9 Dec 2022 19:28:17 +0200 Subject: [PATCH 1969/2002] net: dsa: mv88e6xxx: replace VTU violation prints with trace points It is possible to trigger these VTU violation messages very easily, it's only necessary to send packets with an unknown VLAN ID to a port that belongs to a VLAN-aware bridge. Do a similar thing as for ATU violation messages, and hide them in the kernel's trace buffer. New usage model: $ trace-cmd list | grep mv88e6xxx mv88e6xxx mv88e6xxx:mv88e6xxx_vtu_miss_violation mv88e6xxx:mv88e6xxx_vtu_member_violation $ trace-cmd report Signed-off-by: Vladimir Oltean Reviewed-by: Saeed Mahameed Reviewed-by: Florian Fainelli Signed-off-by: Jakub Kicinski --- drivers/net/dsa/mv88e6xxx/global1_vtu.c | 7 +++--- drivers/net/dsa/mv88e6xxx/trace.h | 30 +++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 4 deletions(-) diff --git a/drivers/net/dsa/mv88e6xxx/global1_vtu.c b/drivers/net/dsa/mv88e6xxx/global1_vtu.c index 38e18f5811bf..bcfb4a812055 100644 --- a/drivers/net/dsa/mv88e6xxx/global1_vtu.c +++ b/drivers/net/dsa/mv88e6xxx/global1_vtu.c @@ -13,6 +13,7 @@ #include "chip.h" #include "global1.h" +#include "trace.h" /* Offset 0x02: VTU FID Register */ @@ -628,14 +629,12 @@ static irqreturn_t mv88e6xxx_g1_vtu_prob_irq_thread_fn(int irq, void *dev_id) spid = val & MV88E6XXX_G1_VTU_OP_SPID_MASK; if (val & MV88E6XXX_G1_VTU_OP_MEMBER_VIOLATION) { - dev_err_ratelimited(chip->dev, "VTU member violation for vid %d, source port %d\n", - vid, spid); + trace_mv88e6xxx_vtu_member_violation(chip->dev, spid, vid); chip->ports[spid].vtu_member_violation++; } if (val & MV88E6XXX_G1_VTU_OP_MISS_VIOLATION) { - dev_dbg_ratelimited(chip->dev, "VTU miss violation for vid %d, source port %d\n", - vid, spid); + trace_mv88e6xxx_vtu_miss_violation(chip->dev, spid, vid); chip->ports[spid].vtu_miss_violation++; } diff --git a/drivers/net/dsa/mv88e6xxx/trace.h b/drivers/net/dsa/mv88e6xxx/trace.h index d9ab5c8dee55..f59ca04768e7 100644 --- a/drivers/net/dsa/mv88e6xxx/trace.h +++ b/drivers/net/dsa/mv88e6xxx/trace.h @@ -55,6 +55,36 @@ DEFINE_EVENT(mv88e6xxx_atu_violation, mv88e6xxx_atu_full_violation, const unsigned char *addr, u16 fid), TP_ARGS(dev, spid, portvec, addr, fid)); +DECLARE_EVENT_CLASS(mv88e6xxx_vtu_violation, + + TP_PROTO(const struct device *dev, int spid, u16 vid), + + TP_ARGS(dev, spid, vid), + + TP_STRUCT__entry( + __string(name, dev_name(dev)) + __field(int, spid) + __field(u16, vid) + ), + + TP_fast_assign( + __assign_str(name, dev_name(dev)); + __entry->spid = spid; + __entry->vid = vid; + ), + + TP_printk("dev %s spid %d vid %u", + __get_str(name), __entry->spid, __entry->vid) +); + +DEFINE_EVENT(mv88e6xxx_vtu_violation, mv88e6xxx_vtu_member_violation, + TP_PROTO(const struct device *dev, int spid, u16 vid), + TP_ARGS(dev, spid, vid)); + +DEFINE_EVENT(mv88e6xxx_vtu_violation, mv88e6xxx_vtu_miss_violation, + TP_PROTO(const struct device *dev, int spid, u16 vid), + TP_ARGS(dev, spid, vid)); + #endif /* _MV88E6XXX_TRACE_H */ /* We don't want to use include/trace/events */ From 8f18655c49eb6abfe7fc3711d32d23b311fbc6a6 Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Fri, 9 Dec 2022 19:58:40 +0200 Subject: [PATCH 1970/2002] net: dsa: don't call ptp_classify_raw() if switch doesn't provide RX timestamping ptp_classify_raw() is not exactly cheap, since it invokes a BPF program for every skb in the receive path. For switches which do not provide ds->ops->port_rxtstamp(), running ptp_classify_raw() provides precisely nothing, so check for the presence of the function pointer first, since that is much cheaper. Signed-off-by: Vladimir Oltean Reviewed-by: Florian Fainelli Reviewed-by: Kurt Kanzenbach Link: https://lore.kernel.org/r/20221209175840.390707-1-vladimir.oltean@nxp.com Signed-off-by: Jakub Kicinski --- net/dsa/tag.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/net/dsa/tag.c b/net/dsa/tag.c index 383721e167d6..b2fba1a003ce 100644 --- a/net/dsa/tag.c +++ b/net/dsa/tag.c @@ -33,6 +33,9 @@ static bool dsa_skb_defer_rx_timestamp(struct dsa_slave_priv *p, struct dsa_switch *ds = p->dp->ds; unsigned int type; + if (!ds->ops->port_rxtstamp) + return false; + if (skb_headroom(skb) < ETH_HLEN) return false; @@ -45,10 +48,7 @@ static bool dsa_skb_defer_rx_timestamp(struct dsa_slave_priv *p, if (type == PTP_CLASS_NONE) return false; - if (likely(ds->ops->port_rxtstamp)) - return ds->ops->port_rxtstamp(ds, p->dp->index, skb, type); - - return false; + return ds->ops->port_rxtstamp(ds, p->dp->index, skb, type); } static int dsa_switch_rcv(struct sk_buff *skb, struct net_device *dev, From d7b061b80ee6f91aa0b89daa3069802d7ea4c57f Mon Sep 17 00:00:00 2001 From: Yunsheng Lin Date: Mon, 12 Dec 2022 11:24:26 +0800 Subject: [PATCH 1971/2002] net: tso: inline tso_count_descs() tso_count_descs() is a small function doing simple calculation, and tso_count_descs() is used in fast path, so inline it to reduce the overhead of calls. Signed-off-by: Yunsheng Lin Link: https://lore.kernel.org/r/20221212032426.16050-1-linyunsheng@huawei.com Signed-off-by: Jakub Kicinski --- include/net/tso.h | 8 +++++++- net/core/tso.c | 8 -------- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/include/net/tso.h b/include/net/tso.h index 62c98a9c60f1..e7e157ae0526 100644 --- a/include/net/tso.h +++ b/include/net/tso.h @@ -2,6 +2,7 @@ #ifndef _TSO_H #define _TSO_H +#include #include #define TSO_HEADER_SIZE 256 @@ -16,7 +17,12 @@ struct tso_t { u32 tcp_seq; }; -int tso_count_descs(const struct sk_buff *skb); +/* Calculate the worst case buffer count */ +static inline int tso_count_descs(const struct sk_buff *skb) +{ + return skb_shinfo(skb)->gso_segs * 2 + skb_shinfo(skb)->nr_frags; +} + void tso_build_hdr(const struct sk_buff *skb, char *hdr, struct tso_t *tso, int size, bool is_last); void tso_build_data(const struct sk_buff *skb, struct tso_t *tso, int size); diff --git a/net/core/tso.c b/net/core/tso.c index 4148f6d48953..e00796e3b146 100644 --- a/net/core/tso.c +++ b/net/core/tso.c @@ -5,14 +5,6 @@ #include #include -/* Calculate expected number of TX descriptors */ -int tso_count_descs(const struct sk_buff *skb) -{ - /* The Marvell Way */ - return skb_shinfo(skb)->gso_segs * 2 + skb_shinfo(skb)->nr_frags; -} -EXPORT_SYMBOL(tso_count_descs); - void tso_build_hdr(const struct sk_buff *skb, char *hdr, struct tso_t *tso, int size, bool is_last) { From 93e637a37b2872c2fce6ce4df6e59cdc2fe8ca5e Mon Sep 17 00:00:00 2001 From: Kurt Kanzenbach Date: Mon, 12 Dec 2022 09:15:46 +0100 Subject: [PATCH 1972/2002] dt-bindings: net: dsa: hellcreek: Sync DSA maintainers The current DSA maintainers are Florian Fainelli, Andrew Lunn and Vladimir Oltean. Update the hellcreek binding accordingly. Signed-off-by: Kurt Kanzenbach Reviewed-by: Vladimir Oltean Acked-by: Rob Herring Acked-by: Florian Fainelli Link: https://lore.kernel.org/r/20221212081546.6916-1-kurt@linutronix.de Signed-off-by: Jakub Kicinski --- .../devicetree/bindings/net/dsa/hirschmann,hellcreek.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/net/dsa/hirschmann,hellcreek.yaml b/Documentation/devicetree/bindings/net/dsa/hirschmann,hellcreek.yaml index 73b774eadd0b..1d7dab31457d 100644 --- a/Documentation/devicetree/bindings/net/dsa/hirschmann,hellcreek.yaml +++ b/Documentation/devicetree/bindings/net/dsa/hirschmann,hellcreek.yaml @@ -12,7 +12,7 @@ allOf: maintainers: - Andrew Lunn - Florian Fainelli - - Vivien Didelot + - Vladimir Oltean - Kurt Kanzenbach description: From 1280d4b76f3402645aa7075a53f49a3a14be07a8 Mon Sep 17 00:00:00 2001 From: Uladzislau Koshchanka Date: Sat, 10 Dec 2022 03:44:23 +0300 Subject: [PATCH 1973/2002] lib: packing: replace bit_reverse() with bitrev8() Remove bit_reverse() function. Instead use bitrev8() from linux/bitrev.h + bitshift. Reduces code-repetition. Signed-off-by: Uladzislau Koshchanka Link: https://lore.kernel.org/r/20221210004423.32332-1-koshchanka@gmail.com Signed-off-by: Jakub Kicinski --- lib/Kconfig | 1 + lib/packing.c | 16 ++-------------- 2 files changed, 3 insertions(+), 14 deletions(-) diff --git a/lib/Kconfig b/lib/Kconfig index 9bbf8a4b2108..cc969ef58a2a 100644 --- a/lib/Kconfig +++ b/lib/Kconfig @@ -24,6 +24,7 @@ config LINEAR_RANGES config PACKING bool "Generic bitfield packing and unpacking" + select BITREVERSE default n help This option provides the packing() helper function, which permits diff --git a/lib/packing.c b/lib/packing.c index 9a72f4bbf0e2..a96169237ae6 100644 --- a/lib/packing.c +++ b/lib/packing.c @@ -7,6 +7,7 @@ #include #include #include +#include static int get_le_offset(int offset) { @@ -29,19 +30,6 @@ static int get_reverse_lsw32_offset(int offset, size_t len) return word_index * 4 + offset; } -static u64 bit_reverse(u64 val, unsigned int width) -{ - u64 new_val = 0; - unsigned int bit; - unsigned int i; - - for (i = 0; i < width; i++) { - bit = (val & (1 << i)) != 0; - new_val |= (bit << (width - i - 1)); - } - return new_val; -} - static void adjust_for_msb_right_quirk(u64 *to_write, int *box_start_bit, int *box_end_bit, u8 *box_mask) { @@ -49,7 +37,7 @@ static void adjust_for_msb_right_quirk(u64 *to_write, int *box_start_bit, int new_box_start_bit, new_box_end_bit; *to_write >>= *box_end_bit; - *to_write = bit_reverse(*to_write, box_bit_width); + *to_write = bitrev8(*to_write) >> (8 - box_bit_width); *to_write <<= *box_end_bit; new_box_end_bit = box_bit_width - *box_start_bit - 1; From 87c978123ef1f346d7385eaccc141022d368166f Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Sat, 10 Dec 2022 18:35:00 +0100 Subject: [PATCH 1974/2002] octeontx2-af: cn10k: mcs: Fix a resource leak in the probe and remove functions In mcs_register_interrupts(), a call to request_irq() is not balanced by a corresponding free_irq(), neither in the error handling path, nor in the remove function. Add the missing calls. Fixes: 6c635f78c474 ("octeontx2-af: cn10k: mcs: Handle MCS block interrupts") Signed-off-by: Christophe JAILLET Link: https://lore.kernel.org/r/69f153db5152a141069f990206e7389f961d41ec.1670693669.git.christophe.jaillet@wanadoo.fr Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/marvell/octeontx2/af/mcs.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/marvell/octeontx2/af/mcs.c b/drivers/net/ethernet/marvell/octeontx2/af/mcs.c index c0bedf402da9..f68a6a0e3aa4 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/mcs.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/mcs.c @@ -1184,10 +1184,13 @@ static int mcs_register_interrupts(struct mcs *mcs) mcs->tx_sa_active = alloc_mem(mcs, mcs->hw->sc_entries); if (!mcs->tx_sa_active) { ret = -ENOMEM; - goto exit; + goto free_irq; } return ret; + +free_irq: + free_irq(pci_irq_vector(mcs->pdev, MCS_INT_VEC_IP), mcs); exit: pci_free_irq_vectors(mcs->pdev); mcs->num_vec = 0; @@ -1589,6 +1592,7 @@ static void mcs_remove(struct pci_dev *pdev) /* Set MCS to external bypass */ mcs_set_external_bypass(mcs, true); + free_irq(pci_irq_vector(pdev, MCS_INT_VEC_IP), mcs); pci_free_irq_vectors(pdev); pci_release_regions(pdev); pci_disable_device(pdev); From ede5a389852d3640a28e7187fb32b7f204380901 Mon Sep 17 00:00:00 2001 From: Piergiorgio Beruto Date: Sat, 10 Dec 2022 23:37:22 +0100 Subject: [PATCH 1975/2002] stmmac: fix potential division by 0 When the MAC is connected to a 10 Mb/s PHY and the PTP clock is derived from the MAC reference clock (default), the clk_ptp_rate becomes too small and the calculated sub second increment becomes 0 when computed by the stmmac_config_sub_second_increment() function within stmmac_init_tstamp_counter(). Therefore, the subsequent div_u64 in stmmac_init_tstamp_counter() operation triggers a divide by 0 exception as shown below. [ 95.062067] socfpga-dwmac ff700000.ethernet eth0: Register MEM_TYPE_PAGE_POOL RxQ-0 [ 95.076440] socfpga-dwmac ff700000.ethernet eth0: PHY [stmmac-0:08] driver [NCN26000] (irq=49) [ 95.095964] dwmac1000: Master AXI performs any burst length [ 95.101588] socfpga-dwmac ff700000.ethernet eth0: No Safety Features support found [ 95.109428] Division by zero in kernel. [ 95.113447] CPU: 0 PID: 239 Comm: ifconfig Not tainted 6.1.0-rc7-centurion3-1.0.3.0-01574-gb624218205b7-dirty #77 [ 95.123686] Hardware name: Altera SOCFPGA [ 95.127695] unwind_backtrace from show_stack+0x10/0x14 [ 95.132938] show_stack from dump_stack_lvl+0x40/0x4c [ 95.137992] dump_stack_lvl from Ldiv0+0x8/0x10 [ 95.142527] Ldiv0 from __aeabi_uidivmod+0x8/0x18 [ 95.147232] __aeabi_uidivmod from div_u64_rem+0x1c/0x40 [ 95.152552] div_u64_rem from stmmac_init_tstamp_counter+0xd0/0x164 [ 95.158826] stmmac_init_tstamp_counter from stmmac_hw_setup+0x430/0xf00 [ 95.165533] stmmac_hw_setup from __stmmac_open+0x214/0x2d4 [ 95.171117] __stmmac_open from stmmac_open+0x30/0x44 [ 95.176182] stmmac_open from __dev_open+0x11c/0x134 [ 95.181172] __dev_open from __dev_change_flags+0x168/0x17c [ 95.186750] __dev_change_flags from dev_change_flags+0x14/0x50 [ 95.192662] dev_change_flags from devinet_ioctl+0x2b4/0x604 [ 95.198321] devinet_ioctl from inet_ioctl+0x1ec/0x214 [ 95.203462] inet_ioctl from sock_ioctl+0x14c/0x3c4 [ 95.208354] sock_ioctl from vfs_ioctl+0x20/0x38 [ 95.212984] vfs_ioctl from sys_ioctl+0x250/0x844 [ 95.217691] sys_ioctl from ret_fast_syscall+0x0/0x4c [ 95.222743] Exception stack(0xd0ee1fa8 to 0xd0ee1ff0) [ 95.227790] 1fa0: 00574c4f be9aeca4 00000003 00008914 be9aeca4 be9aec50 [ 95.235945] 1fc0: 00574c4f be9aeca4 0059f078 00000036 be9aee8c be9aef7a 00000015 00000000 [ 95.244096] 1fe0: 005a01f0 be9aec38 004d7484 b6e67d74 Signed-off-by: Piergiorgio Beruto Fixes: 91a2559c1dc5 ("net: stmmac: Fix sub-second increment") Reviewed-by: Andrew Lunn Link: https://lore.kernel.org/r/de4c64ccac9084952c56a06a8171d738604c4770.1670678513.git.piergiorgio.beruto@gmail.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/stmicro/stmmac/stmmac_hwtstamp.c | 3 ++- drivers/net/ethernet/stmicro/stmmac/stmmac_ptp.h | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_hwtstamp.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_hwtstamp.c index 764832f4dae1..8b50f03056b7 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_hwtstamp.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_hwtstamp.c @@ -47,7 +47,8 @@ static void config_sub_second_increment(void __iomem *ioaddr, if (!(value & PTP_TCR_TSCTRLSSR)) data = (data * 1000) / 465; - data &= PTP_SSIR_SSINC_MASK; + if (data > PTP_SSIR_SSINC_MAX) + data = PTP_SSIR_SSINC_MAX; reg_value = data; if (gmac4) diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_ptp.h b/drivers/net/ethernet/stmicro/stmmac/stmmac_ptp.h index 53172a439810..bf619295d079 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_ptp.h +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_ptp.h @@ -64,7 +64,7 @@ #define PTP_TCR_TSENMACADDR BIT(18) /* SSIR defines */ -#define PTP_SSIR_SSINC_MASK 0xff +#define PTP_SSIR_SSINC_MAX 0xff #define GMAC4_PTP_SSIR_SSINC_SHIFT 16 /* Auxiliary Control defines */ From 8a321cf7becc6c065ae595b837b826a2a81036b9 Mon Sep 17 00:00:00 2001 From: Xin Long Date: Fri, 9 Dec 2022 10:21:38 -0500 Subject: [PATCH 1976/2002] net: add IFF_NO_ADDRCONF and use it in bonding to prevent ipv6 addrconf Currently, in bonding it reused the IFF_SLAVE flag and checked it in ipv6 addrconf to prevent ipv6 addrconf. However, it is not a proper flag to use for no ipv6 addrconf, for bonding it has to move IFF_SLAVE flag setting ahead of dev_open() in bond_enslave(). Also, IFF_MASTER/SLAVE are historical flags used in bonding and eql, as Jiri mentioned, the new devices like Team, Failover do not use this flag. So as Jiri suggested, this patch adds IFF_NO_ADDRCONF in priv_flags of the device to indicate no ipv6 addconf, and uses it in bonding and moves IFF_SLAVE flag setting back to its original place. Signed-off-by: Xin Long Signed-off-by: Jakub Kicinski --- drivers/net/bonding/bond_main.c | 18 +++++++++++++----- include/linux/netdevice.h | 3 ++- net/ipv6/addrconf.c | 4 ++-- 3 files changed, 17 insertions(+), 8 deletions(-) diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index 4048876f842c..f7767afe116b 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -1632,13 +1632,19 @@ static int bond_master_upper_dev_link(struct bonding *bond, struct slave *slave, { struct netdev_lag_upper_info lag_upper_info; enum netdev_lag_tx_type type; + int err; type = bond_lag_tx_type(bond); lag_upper_info.tx_type = type; lag_upper_info.hash_type = bond_lag_hash_type(bond, type); - return netdev_master_upper_dev_link(slave->dev, bond->dev, slave, - &lag_upper_info, extack); + err = netdev_master_upper_dev_link(slave->dev, bond->dev, slave, + &lag_upper_info, extack); + if (err) + return err; + + slave->dev->flags |= IFF_SLAVE; + return 0; } static void bond_upper_dev_unlink(struct bonding *bond, struct slave *slave) @@ -1950,8 +1956,8 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev, } } - /* set slave flag before open to prevent IPv6 addrconf */ - slave_dev->flags |= IFF_SLAVE; + /* set no_addrconf flag before open to prevent IPv6 addrconf */ + slave_dev->priv_flags |= IFF_NO_ADDRCONF; /* open the slave since the application closed it */ res = dev_open(slave_dev, extack); @@ -2254,7 +2260,7 @@ err_close: dev_close(slave_dev); err_restore_mac: - slave_dev->flags &= ~IFF_SLAVE; + slave_dev->priv_flags &= ~IFF_NO_ADDRCONF; if (!bond->params.fail_over_mac || BOND_MODE(bond) != BOND_MODE_ACTIVEBACKUP) { /* XXX TODO - fom follow mode needs to change master's @@ -2446,6 +2452,8 @@ static int __bond_release_one(struct net_device *bond_dev, /* close slave before restoring its mac address */ dev_close(slave_dev); + slave_dev->priv_flags &= ~IFF_NO_ADDRCONF; + if (bond->params.fail_over_mac != BOND_FOM_ACTIVE || BOND_MODE(bond) != BOND_MODE_ACTIVEBACKUP) { /* restore original ("permanent") mac address */ diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 2287cb8eb9e4..aad12a179e54 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -1662,6 +1662,7 @@ struct net_device_ops { * @IFF_FAILOVER: device is a failover master device * @IFF_FAILOVER_SLAVE: device is lower dev of a failover master device * @IFF_L3MDEV_RX_HANDLER: only invoke the rx handler of L3 master device + * @IFF_NO_ADDRCONF: prevent ipv6 addrconf * @IFF_TX_SKB_NO_LINEAR: device/driver is capable of xmitting frames with * skb_headlen(skb) == 0 (data starts from frag0) * @IFF_CHANGE_PROTO_DOWN: device supports setting carrier via IFLA_PROTO_DOWN @@ -1697,7 +1698,7 @@ enum netdev_priv_flags { IFF_FAILOVER = 1<<27, IFF_FAILOVER_SLAVE = 1<<28, IFF_L3MDEV_RX_HANDLER = 1<<29, - /* was IFF_LIVE_RENAME_OK */ + IFF_NO_ADDRCONF = BIT_ULL(30), IFF_TX_SKB_NO_LINEAR = BIT_ULL(31), IFF_CHANGE_PROTO_DOWN = BIT_ULL(32), }; diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index 9c3f5202a97b..c338dfb05d2c 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -3320,7 +3320,7 @@ static void addrconf_addr_gen(struct inet6_dev *idev, bool prefix_route) return; /* no link local addresses on devices flagged as slaves */ - if (idev->dev->flags & IFF_SLAVE) + if (idev->dev->priv_flags & IFF_NO_ADDRCONF) return; ipv6_addr_set(&addr, htonl(0xFE800000), 0, 0, 0); @@ -3560,7 +3560,7 @@ static int addrconf_notify(struct notifier_block *this, unsigned long event, if (idev && idev->cnf.disable_ipv6) break; - if (dev->flags & IFF_SLAVE) { + if (dev->priv_flags & IFF_NO_ADDRCONF) { if (event == NETDEV_UP && !IS_ERR_OR_NULL(idev) && dev->flags & IFF_UP && dev->flags & IFF_MULTICAST) ipv6_mc_up(idev); From 0aa64df30b382fc71d4fb1827d528e0eb3eff854 Mon Sep 17 00:00:00 2001 From: Xin Long Date: Fri, 9 Dec 2022 10:21:39 -0500 Subject: [PATCH 1977/2002] net: team: use IFF_NO_ADDRCONF flag to prevent ipv6 addrconf This patch is to use IFF_NO_ADDRCONF flag to prevent ipv6 addrconf for Team port. This flag will be set in team_port_enter(), which is called before dev_open(), and cleared in team_port_leave(), called after dev_close() and the err path in team_port_add(). Signed-off-by: Xin Long Signed-off-by: Jakub Kicinski --- drivers/net/team/team.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c index d10606f257c4..fcd43d62d86b 100644 --- a/drivers/net/team/team.c +++ b/drivers/net/team/team.c @@ -1044,6 +1044,7 @@ static int team_port_enter(struct team *team, struct team_port *port) goto err_port_enter; } } + port->dev->priv_flags |= IFF_NO_ADDRCONF; return 0; @@ -1057,6 +1058,7 @@ static void team_port_leave(struct team *team, struct team_port *port) { if (team->ops.port_leave) team->ops.port_leave(team, port); + port->dev->priv_flags &= ~IFF_NO_ADDRCONF; dev_put(team->dev); } From cb54d392279dd450e65f6fa3c3f66db8cbdbcc0e Mon Sep 17 00:00:00 2001 From: Xin Long Date: Fri, 9 Dec 2022 10:21:40 -0500 Subject: [PATCH 1978/2002] net: failover: use IFF_NO_ADDRCONF flag to prevent ipv6 addrconf Similar to Bonding and Team, to prevent ipv6 addrconf with IFF_NO_ADDRCONF in slave_dev->priv_flags for slave ports is also needed in net failover. Note that dev_open(slave_dev) is called in .slave_register, which is called after the IFF_NO_ADDRCONF flag is set in failover_slave_register(). Signed-off-by: Xin Long Signed-off-by: Jakub Kicinski --- net/core/failover.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/net/core/failover.c b/net/core/failover.c index 655411c4ca51..2a140b3ea669 100644 --- a/net/core/failover.c +++ b/net/core/failover.c @@ -80,14 +80,14 @@ static int failover_slave_register(struct net_device *slave_dev) goto err_upper_link; } - slave_dev->priv_flags |= IFF_FAILOVER_SLAVE; + slave_dev->priv_flags |= (IFF_FAILOVER_SLAVE | IFF_NO_ADDRCONF); if (fops && fops->slave_register && !fops->slave_register(slave_dev, failover_dev)) return NOTIFY_OK; netdev_upper_dev_unlink(slave_dev, failover_dev); - slave_dev->priv_flags &= ~IFF_FAILOVER_SLAVE; + slave_dev->priv_flags &= ~(IFF_FAILOVER_SLAVE | IFF_NO_ADDRCONF); err_upper_link: netdev_rx_handler_unregister(slave_dev); done: @@ -121,7 +121,7 @@ int failover_slave_unregister(struct net_device *slave_dev) netdev_rx_handler_unregister(slave_dev); netdev_upper_dev_unlink(slave_dev, failover_dev); - slave_dev->priv_flags &= ~IFF_FAILOVER_SLAVE; + slave_dev->priv_flags &= ~(IFF_FAILOVER_SLAVE | IFF_NO_ADDRCONF); if (fops && fops->slave_unregister && !fops->slave_unregister(slave_dev, failover_dev)) From 0c87b545a2ed5cd8a6318011f1c92b188c2d74bc Mon Sep 17 00:00:00 2001 From: Bartosz Staszewski Date: Fri, 9 Dec 2022 10:54:11 -0800 Subject: [PATCH 1979/2002] i40e: Fix the inability to attach XDP program on downed interface Whenever trying to load XDP prog on downed interface, function i40e_xdp was passing vsi->rx_buf_len field to i40e_xdp_setup() which was equal 0. i40e_open() calls i40e_vsi_configure_rx() which configures that field, but that only happens when interface is up. When it is down, i40e_open() is not being called, thus vsi->rx_buf_len is not set. Solution for this is calculate buffer length in newly created function - i40e_calculate_vsi_rx_buf_len() that return actual buffer length. Buffer length is being calculated based on the same rules applied previously in i40e_vsi_configure_rx() function. Fixes: 613142b0bb88 ("i40e: Log error for oversized MTU on device") Fixes: 0c8493d90b6b ("i40e: add XDP support for pass and drop actions") Signed-off-by: Bartosz Staszewski Signed-off-by: Mateusz Palczewski Tested-by: Shwetha Nagaraju Reviewed-by: Maciej Fijalkowski Signed-off-by: Tony Nguyen Reviewed-by: Saeed Mahameed Link: https://lore.kernel.org/r/20221209185411.2519898-1-anthony.l.nguyen@intel.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/intel/i40e/i40e_main.c | 36 ++++++++++++++------- 1 file changed, 24 insertions(+), 12 deletions(-) diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index 6416322d7c18..e6e349f0c945 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -3693,6 +3693,24 @@ static int i40e_vsi_configure_tx(struct i40e_vsi *vsi) return err; } +/** + * i40e_calculate_vsi_rx_buf_len - Calculates buffer length + * + * @vsi: VSI to calculate rx_buf_len from + */ +static u16 i40e_calculate_vsi_rx_buf_len(struct i40e_vsi *vsi) +{ + if (!vsi->netdev || (vsi->back->flags & I40E_FLAG_LEGACY_RX)) + return I40E_RXBUFFER_2048; + +#if (PAGE_SIZE < 8192) + if (!I40E_2K_TOO_SMALL_WITH_PADDING && vsi->netdev->mtu <= ETH_DATA_LEN) + return I40E_RXBUFFER_1536 - NET_IP_ALIGN; +#endif + + return PAGE_SIZE < 8192 ? I40E_RXBUFFER_3072 : I40E_RXBUFFER_2048; +} + /** * i40e_vsi_configure_rx - Configure the VSI for Rx * @vsi: the VSI being configured @@ -3704,20 +3722,14 @@ static int i40e_vsi_configure_rx(struct i40e_vsi *vsi) int err = 0; u16 i; - if (!vsi->netdev || (vsi->back->flags & I40E_FLAG_LEGACY_RX)) { - vsi->max_frame = I40E_MAX_RXBUFFER; - vsi->rx_buf_len = I40E_RXBUFFER_2048; + vsi->max_frame = I40E_MAX_RXBUFFER; + vsi->rx_buf_len = i40e_calculate_vsi_rx_buf_len(vsi); + #if (PAGE_SIZE < 8192) - } else if (!I40E_2K_TOO_SMALL_WITH_PADDING && - (vsi->netdev->mtu <= ETH_DATA_LEN)) { + if (vsi->netdev && !I40E_2K_TOO_SMALL_WITH_PADDING && + vsi->netdev->mtu <= ETH_DATA_LEN) vsi->max_frame = I40E_RXBUFFER_1536 - NET_IP_ALIGN; - vsi->rx_buf_len = I40E_RXBUFFER_1536 - NET_IP_ALIGN; #endif - } else { - vsi->max_frame = I40E_MAX_RXBUFFER; - vsi->rx_buf_len = (PAGE_SIZE < 8192) ? I40E_RXBUFFER_3072 : - I40E_RXBUFFER_2048; - } /* set up individual rings */ for (i = 0; i < vsi->num_queue_pairs && !err; i++) @@ -13282,7 +13294,7 @@ static int i40e_xdp_setup(struct i40e_vsi *vsi, struct bpf_prog *prog, int i; /* Don't allow frames that span over multiple buffers */ - if (frame_size > vsi->rx_buf_len) { + if (frame_size > i40e_calculate_vsi_rx_buf_len(vsi)) { NL_SET_ERR_MSG_MOD(extack, "MTU too large to enable XDP"); return -EINVAL; } From b1746fbab15a1d6d5e61846f260f8fece1fca576 Mon Sep 17 00:00:00 2001 From: Tirthendu Sarkar Date: Fri, 9 Dec 2022 10:55:53 -0800 Subject: [PATCH 1980/2002] i40e: allow toggling loopback mode via ndo_set_features callback Add support for NETIF_F_LOOPBACK. This feature can be set via: $ ethtool -K eth0 loopback This sets the MAC Tx->Rx loopback. This feature is used for the xsk selftests, and might have other uses too. Signed-off-by: Tirthendu Sarkar Reviewed-by: Alexander Lobakin Reviewed-by: Leon Romanovsky Tested-by: Magnus Karlsson Signed-off-by: Tony Nguyen Link: https://lore.kernel.org/r/20221209185553.2520088-1-anthony.l.nguyen@intel.com Signed-off-by: Jakub Kicinski --- .../net/ethernet/intel/i40e/i40e_adminq_cmd.h | 8 ++++-- drivers/net/ethernet/intel/i40e/i40e_common.c | 26 +++++++++++++++++ drivers/net/ethernet/intel/i40e/i40e_main.c | 28 ++++++++++++++++++- .../net/ethernet/intel/i40e/i40e_prototype.h | 3 ++ 4 files changed, 61 insertions(+), 4 deletions(-) diff --git a/drivers/net/ethernet/intel/i40e/i40e_adminq_cmd.h b/drivers/net/ethernet/intel/i40e/i40e_adminq_cmd.h index 60f9e0a6aaca..3357d65a906b 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_adminq_cmd.h +++ b/drivers/net/ethernet/intel/i40e/i40e_adminq_cmd.h @@ -1795,9 +1795,11 @@ I40E_CHECK_CMD_LENGTH(i40e_aqc_an_advt_reg); /* Set Loopback mode (0x0618) */ struct i40e_aqc_set_lb_mode { __le16 lb_mode; -#define I40E_AQ_LB_PHY_LOCAL 0x01 -#define I40E_AQ_LB_PHY_REMOTE 0x02 -#define I40E_AQ_LB_MAC_LOCAL 0x04 +#define I40E_LEGACY_LOOPBACK_NVM_VER 0x6000 +#define I40E_AQ_LB_MAC_LOCAL 0x01 +#define I40E_AQ_LB_PHY_LOCAL 0x05 +#define I40E_AQ_LB_PHY_REMOTE 0x06 +#define I40E_AQ_LB_MAC_LOCAL_LEGACY 0x04 u8 reserved[14]; }; diff --git a/drivers/net/ethernet/intel/i40e/i40e_common.c b/drivers/net/ethernet/intel/i40e/i40e_common.c index 4f01e2a6b6bb..8f764ff5c990 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_common.c +++ b/drivers/net/ethernet/intel/i40e/i40e_common.c @@ -1830,6 +1830,32 @@ i40e_status i40e_aq_set_phy_int_mask(struct i40e_hw *hw, return status; } +/** + * i40e_aq_set_mac_loopback + * @hw: pointer to the HW struct + * @ena_lpbk: Enable or Disable loopback + * @cmd_details: pointer to command details structure or NULL + * + * Enable/disable loopback on a given port + */ +i40e_status i40e_aq_set_mac_loopback(struct i40e_hw *hw, bool ena_lpbk, + struct i40e_asq_cmd_details *cmd_details) +{ + struct i40e_aq_desc desc; + struct i40e_aqc_set_lb_mode *cmd = + (struct i40e_aqc_set_lb_mode *)&desc.params.raw; + + i40e_fill_default_direct_cmd_desc(&desc, i40e_aqc_opc_set_lb_modes); + if (ena_lpbk) { + if (hw->nvm.version <= I40E_LEGACY_LOOPBACK_NVM_VER) + cmd->lb_mode = cpu_to_le16(I40E_AQ_LB_MAC_LOCAL_LEGACY); + else + cmd->lb_mode = cpu_to_le16(I40E_AQ_LB_MAC_LOCAL); + } + + return i40e_asq_send_command(hw, &desc, NULL, 0, cmd_details); +} + /** * i40e_aq_set_phy_debug * @hw: pointer to the hw struct diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index 95485b56d6c3..6d2e3025c231 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -12937,6 +12937,29 @@ static void i40e_clear_rss_lut(struct i40e_vsi *vsi) } } +/** + * i40e_set_loopback - turn on/off loopback mode on underlying PF + * @vsi: ptr to VSI + * @ena: flag to indicate the on/off setting + */ +static int i40e_set_loopback(struct i40e_vsi *vsi, bool ena) +{ + bool if_running = netif_running(vsi->netdev) && + !test_and_set_bit(__I40E_VSI_DOWN, vsi->state); + int ret; + + if (if_running) + i40e_down(vsi); + + ret = i40e_aq_set_mac_loopback(&vsi->back->hw, ena, NULL); + if (ret) + netdev_err(vsi->netdev, "Failed to toggle loopback state\n"); + if (if_running) + i40e_up(vsi); + + return ret; +} + /** * i40e_set_features - set the netdev feature flags * @netdev: ptr to the netdev being adjusted @@ -12977,6 +13000,9 @@ static int i40e_set_features(struct net_device *netdev, if (need_reset) i40e_do_reset(pf, I40E_PF_RESET_FLAG, true); + if ((features ^ netdev->features) & NETIF_F_LOOPBACK) + return i40e_set_loopback(vsi, !!(features & NETIF_F_LOOPBACK)); + return 0; } @@ -13739,7 +13765,7 @@ static int i40e_config_netdev(struct i40e_vsi *vsi) if (!(pf->flags & I40E_FLAG_MFP_ENABLED)) hw_features |= NETIF_F_NTUPLE | NETIF_F_HW_TC; - netdev->hw_features |= hw_features; + netdev->hw_features |= hw_features | NETIF_F_LOOPBACK; netdev->features |= hw_features | NETIF_F_HW_VLAN_CTAG_FILTER; netdev->hw_enc_features |= NETIF_F_TSO_MANGLEID; diff --git a/drivers/net/ethernet/intel/i40e/i40e_prototype.h b/drivers/net/ethernet/intel/i40e/i40e_prototype.h index ebdcde6f1aeb..9a71121420c3 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_prototype.h +++ b/drivers/net/ethernet/intel/i40e/i40e_prototype.h @@ -105,6 +105,9 @@ enum i40e_status_code i40e_aq_set_phy_config(struct i40e_hw *hw, struct i40e_asq_cmd_details *cmd_details); enum i40e_status_code i40e_set_fc(struct i40e_hw *hw, u8 *aq_failures, bool atomic_reset); +i40e_status i40e_aq_set_mac_loopback(struct i40e_hw *hw, + bool ena_lpbk, + struct i40e_asq_cmd_details *cmd_details); i40e_status i40e_aq_set_phy_int_mask(struct i40e_hw *hw, u16 mask, struct i40e_asq_cmd_details *cmd_details); i40e_status i40e_aq_clear_pxe_mode(struct i40e_hw *hw, From e095493091e850d5292ad01d8fbf5cde1d89ac53 Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Sat, 10 Dec 2022 01:52:42 +0200 Subject: [PATCH 1981/2002] net: dsa: tag_8021q: avoid leaking ctx on dsa_tag_8021q_register() error path If dsa_tag_8021q_setup() fails, for example due to the inability of the device to install a VLAN, the tag_8021q context of the switch will leak. Make sure it is freed on the error path. Fixes: 328621f6131f ("net: dsa: tag_8021q: absorb dsa_8021q_setup into dsa_tag_8021q_{,un}register") Signed-off-by: Vladimir Oltean Link: https://lore.kernel.org/r/20221209235242.480344-1-vladimir.oltean@nxp.com Signed-off-by: Jakub Kicinski --- net/dsa/tag_8021q.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/net/dsa/tag_8021q.c b/net/dsa/tag_8021q.c index 34e5ec5d3e23..89371b16416e 100644 --- a/net/dsa/tag_8021q.c +++ b/net/dsa/tag_8021q.c @@ -398,6 +398,7 @@ static void dsa_tag_8021q_teardown(struct dsa_switch *ds) int dsa_tag_8021q_register(struct dsa_switch *ds, __be16 proto) { struct dsa_8021q_context *ctx; + int err; ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); if (!ctx) @@ -410,7 +411,15 @@ int dsa_tag_8021q_register(struct dsa_switch *ds, __be16 proto) ds->tag_8021q_ctx = ctx; - return dsa_tag_8021q_setup(ds); + err = dsa_tag_8021q_setup(ds); + if (err) + goto err_free; + + return 0; + +err_free: + kfree(ctx); + return err; } EXPORT_SYMBOL_GPL(dsa_tag_8021q_register); From 02abf84aa52da86586ec6323969afa158ec6e4aa Mon Sep 17 00:00:00 2001 From: Jiapeng Chong Date: Mon, 12 Dec 2022 13:58:13 +0800 Subject: [PATCH 1982/2002] qlcnic: Clean up some inconsistent indenting No functional modification involved. drivers/net/ethernet/qlogic/qlcnic/qlcnic_ethtool.c:714 qlcnic_validate_ring_count() warn: inconsistent indenting. Link: https://bugzilla.openanolis.cn/show_bug.cgi?id=3419 Reported-by: Abaci Robot Signed-off-by: Jiapeng Chong Link: https://lore.kernel.org/r/20221212055813.91154-1-jiapeng.chong@linux.alibaba.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/qlogic/qlcnic/qlcnic_ethtool.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ethtool.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ethtool.c index 1ee491f78c6b..c1436e1554de 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ethtool.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ethtool.c @@ -711,7 +711,7 @@ static int qlcnic_validate_ring_count(struct qlcnic_adapter *adapter, } } - if (tx_ring != 0) { + if (tx_ring != 0) { if (tx_ring > adapter->max_tx_rings) { netdev_err(adapter->netdev, "Invalid ring count, Tx ring count %d should not be greater than max %d driver Tx rings.\n", From b63e30651c59bdef89ec158879d146e8d89cd5e1 Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Sat, 10 Dec 2022 16:56:20 +0200 Subject: [PATCH 1983/2002] bridge: mcast: Do not derive entry type from its filter mode Currently, the filter mode (i.e., INCLUDE / EXCLUDE) of MDB entries cannot be set from user space. Instead, it is set by the kernel according to the entry type: (*, G) entries are treated as EXCLUDE and (S, G) entries are treated as INCLUDE. This allows the kernel to derive the entry type from its filter mode. Subsequent patches will allow user space to set the filter mode of (*, G) entries, making the current assumption incorrect. As a preparation, remove the current assumption and instead determine the entry type from its key, which is a more direct way. Signed-off-by: Ido Schimmel Acked-by: Nikolay Aleksandrov Signed-off-by: Jakub Kicinski --- net/bridge/br_mdb.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/net/bridge/br_mdb.c b/net/bridge/br_mdb.c index ae7d93c08880..2b6921dbdc02 100644 --- a/net/bridge/br_mdb.c +++ b/net/bridge/br_mdb.c @@ -857,17 +857,14 @@ static int br_mdb_add_group(const struct br_mdb_config *cfg, * added to it for proper replication */ if (br_multicast_should_handle_mode(brmctx, group.proto)) { - switch (filter_mode) { - case MCAST_EXCLUDE: - br_multicast_star_g_handle_mode(p, MCAST_EXCLUDE); - break; - case MCAST_INCLUDE: + if (br_multicast_is_star_g(&group)) { + br_multicast_star_g_handle_mode(p, filter_mode); + } else { star_group = p->key.addr; memset(&star_group.src, 0, sizeof(star_group.src)); star_mp = br_mdb_ip_get(br, &star_group); if (star_mp) br_multicast_sg_add_exclude_ports(star_mp, p); - break; } } From 6ff1e68eb21501042ebf8226d500398fd07350f3 Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Sat, 10 Dec 2022 16:56:21 +0200 Subject: [PATCH 1984/2002] bridge: mcast: Split (*, G) and (S, G) addition into different functions When the bridge is using IGMP version 3 or MLD version 2, it handles the addition of (*, G) and (S, G) entries differently. When a new (S, G) port group entry is added, all the (*, G) EXCLUDE ports need to be added to the port group of the new entry. Similarly, when a new (*, G) EXCLUDE port group entry is added, the port needs to be added to the port group of all the matching (S, G) entries. Subsequent patches will create more differences between both entry types. Namely, filter mode and source list can only be specified for (*, G) entries. Given the current and future differences between both entry types, handle the addition of each entry type in a different function, thereby avoiding the creation of one complex function. Signed-off-by: Ido Schimmel Acked-by: Nikolay Aleksandrov Signed-off-by: Jakub Kicinski --- net/bridge/br_mdb.c | 145 +++++++++++++++++++++++++++++--------------- 1 file changed, 96 insertions(+), 49 deletions(-) diff --git a/net/bridge/br_mdb.c b/net/bridge/br_mdb.c index 2b6921dbdc02..e3bd2122d559 100644 --- a/net/bridge/br_mdb.c +++ b/net/bridge/br_mdb.c @@ -786,21 +786,107 @@ out: return brmctx; } +static int br_mdb_add_group_sg(const struct br_mdb_config *cfg, + struct net_bridge_mdb_entry *mp, + struct net_bridge_mcast *brmctx, + unsigned char flags, + struct netlink_ext_ack *extack) +{ + struct net_bridge_port_group __rcu **pp; + struct net_bridge_port_group *p; + unsigned long now = jiffies; + + for (pp = &mp->ports; + (p = mlock_dereference(*pp, cfg->br)) != NULL; + pp = &p->next) { + if (p->key.port == cfg->p) { + NL_SET_ERR_MSG_MOD(extack, "(S, G) group is already joined by port"); + return -EEXIST; + } + if ((unsigned long)p->key.port < (unsigned long)cfg->p) + break; + } + + p = br_multicast_new_port_group(cfg->p, &cfg->group, *pp, flags, NULL, + MCAST_INCLUDE, RTPROT_STATIC); + if (unlikely(!p)) { + NL_SET_ERR_MSG_MOD(extack, "Couldn't allocate new (S, G) port group"); + return -ENOMEM; + } + rcu_assign_pointer(*pp, p); + if (!(flags & MDB_PG_FLAGS_PERMANENT)) + mod_timer(&p->timer, + now + brmctx->multicast_membership_interval); + br_mdb_notify(cfg->br->dev, mp, p, RTM_NEWMDB); + + /* All of (*, G) EXCLUDE ports need to be added to the new (S, G) for + * proper replication. + */ + if (br_multicast_should_handle_mode(brmctx, cfg->group.proto)) { + struct net_bridge_mdb_entry *star_mp; + struct br_ip star_group; + + star_group = p->key.addr; + memset(&star_group.src, 0, sizeof(star_group.src)); + star_mp = br_mdb_ip_get(cfg->br, &star_group); + if (star_mp) + br_multicast_sg_add_exclude_ports(star_mp, p); + } + + return 0; +} + +static int br_mdb_add_group_star_g(const struct br_mdb_config *cfg, + struct net_bridge_mdb_entry *mp, + struct net_bridge_mcast *brmctx, + unsigned char flags, + struct netlink_ext_ack *extack) +{ + struct net_bridge_port_group __rcu **pp; + struct net_bridge_port_group *p; + unsigned long now = jiffies; + + for (pp = &mp->ports; + (p = mlock_dereference(*pp, cfg->br)) != NULL; + pp = &p->next) { + if (p->key.port == cfg->p) { + NL_SET_ERR_MSG_MOD(extack, "(*, G) group is already joined by port"); + return -EEXIST; + } + if ((unsigned long)p->key.port < (unsigned long)cfg->p) + break; + } + + p = br_multicast_new_port_group(cfg->p, &cfg->group, *pp, flags, NULL, + MCAST_EXCLUDE, RTPROT_STATIC); + if (unlikely(!p)) { + NL_SET_ERR_MSG_MOD(extack, "Couldn't allocate new (*, G) port group"); + return -ENOMEM; + } + rcu_assign_pointer(*pp, p); + if (!(flags & MDB_PG_FLAGS_PERMANENT)) + mod_timer(&p->timer, + now + brmctx->multicast_membership_interval); + br_mdb_notify(cfg->br->dev, mp, p, RTM_NEWMDB); + /* If we are adding a new EXCLUDE port group (*, G), it needs to be + * also added to all (S, G) entries for proper replication. + */ + if (br_multicast_should_handle_mode(brmctx, cfg->group.proto)) + br_multicast_star_g_handle_mode(p, MCAST_EXCLUDE); + + return 0; +} + static int br_mdb_add_group(const struct br_mdb_config *cfg, struct netlink_ext_ack *extack) { - struct net_bridge_mdb_entry *mp, *star_mp; - struct net_bridge_port_group __rcu **pp; struct br_mdb_entry *entry = cfg->entry; struct net_bridge_port *port = cfg->p; + struct net_bridge_mdb_entry *mp; struct net_bridge *br = cfg->br; - struct net_bridge_port_group *p; struct net_bridge_mcast *brmctx; struct br_ip group = cfg->group; - unsigned long now = jiffies; unsigned char flags = 0; - struct br_ip star_group; - u8 filter_mode; brmctx = __br_mdb_choose_context(br, entry, extack); if (!brmctx) @@ -823,52 +909,13 @@ static int br_mdb_add_group(const struct br_mdb_config *cfg, return 0; } - for (pp = &mp->ports; - (p = mlock_dereference(*pp, br)) != NULL; - pp = &p->next) { - if (p->key.port == port) { - NL_SET_ERR_MSG_MOD(extack, "Group is already joined by port"); - return -EEXIST; - } - if ((unsigned long)p->key.port < (unsigned long)port) - break; - } - - filter_mode = br_multicast_is_star_g(&group) ? MCAST_EXCLUDE : - MCAST_INCLUDE; - if (entry->state == MDB_PERMANENT) flags |= MDB_PG_FLAGS_PERMANENT; - p = br_multicast_new_port_group(port, &group, *pp, flags, NULL, - filter_mode, RTPROT_STATIC); - if (unlikely(!p)) { - NL_SET_ERR_MSG_MOD(extack, "Couldn't allocate new port group"); - return -ENOMEM; - } - rcu_assign_pointer(*pp, p); - if (entry->state == MDB_TEMPORARY) - mod_timer(&p->timer, - now + brmctx->multicast_membership_interval); - br_mdb_notify(br->dev, mp, p, RTM_NEWMDB); - /* if we are adding a new EXCLUDE port group (*,G) it needs to be also - * added to all S,G entries for proper replication, if we are adding - * a new INCLUDE port (S,G) then all of *,G EXCLUDE ports need to be - * added to it for proper replication - */ - if (br_multicast_should_handle_mode(brmctx, group.proto)) { - if (br_multicast_is_star_g(&group)) { - br_multicast_star_g_handle_mode(p, filter_mode); - } else { - star_group = p->key.addr; - memset(&star_group.src, 0, sizeof(star_group.src)); - star_mp = br_mdb_ip_get(br, &star_group); - if (star_mp) - br_multicast_sg_add_exclude_ports(star_mp, p); - } - } - - return 0; + if (br_multicast_is_star_g(&group)) + return br_mdb_add_group_star_g(cfg, mp, brmctx, flags, extack); + else + return br_mdb_add_group_sg(cfg, mp, brmctx, flags, extack); } static int __br_mdb_add(const struct br_mdb_config *cfg, From 1870a2d35abb6f4d8ff2213d50bbd082b1f8cde5 Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Sat, 10 Dec 2022 16:56:22 +0200 Subject: [PATCH 1985/2002] bridge: mcast: Place netlink policy before validation functions Subsequent patches are going to add additional validation functions and netlink policies. Some of these functions will need to perform parsing using nla_parse_nested() and the new policies. In order to keep all the policies next to each other, move the current policy to before the validation functions. Signed-off-by: Ido Schimmel Acked-by: Nikolay Aleksandrov Signed-off-by: Jakub Kicinski --- net/bridge/br_mdb.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/net/bridge/br_mdb.c b/net/bridge/br_mdb.c index e3bd2122d559..fcdd464cf997 100644 --- a/net/bridge/br_mdb.c +++ b/net/bridge/br_mdb.c @@ -663,6 +663,12 @@ errout: rtnl_set_sk_err(net, RTNLGRP_MDB, err); } +static const struct nla_policy br_mdbe_attrs_pol[MDBE_ATTR_MAX + 1] = { + [MDBE_ATTR_SOURCE] = NLA_POLICY_RANGE(NLA_BINARY, + sizeof(struct in_addr), + sizeof(struct in6_addr)), +}; + static bool is_valid_mdb_entry(struct br_mdb_entry *entry, struct netlink_ext_ack *extack) { @@ -748,12 +754,6 @@ static bool is_valid_mdb_source(struct nlattr *attr, __be16 proto, return true; } -static const struct nla_policy br_mdbe_attrs_pol[MDBE_ATTR_MAX + 1] = { - [MDBE_ATTR_SOURCE] = NLA_POLICY_RANGE(NLA_BINARY, - sizeof(struct in_addr), - sizeof(struct in6_addr)), -}; - static struct net_bridge_mcast * __br_mdb_choose_context(struct net_bridge *br, const struct br_mdb_entry *entry, From 160dd93114ddd31ed2b6290a7495d53717b79cf8 Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Sat, 10 Dec 2022 16:56:23 +0200 Subject: [PATCH 1986/2002] bridge: mcast: Add a centralized error path Subsequent patches will add memory allocations in br_mdb_config_init() as the MDB configuration structure will include a linked list of source entries. This memory will need to be freed regardless if br_mdb_add() succeeded or failed. As a preparation for this change, add a centralized error path where the memory will be freed. Note that br_mdb_del() already has one error path and therefore does not require any changes. Signed-off-by: Ido Schimmel Acked-by: Nikolay Aleksandrov Signed-off-by: Jakub Kicinski --- net/bridge/br_mdb.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/net/bridge/br_mdb.c b/net/bridge/br_mdb.c index fcdd464cf997..95780652cdbf 100644 --- a/net/bridge/br_mdb.c +++ b/net/bridge/br_mdb.c @@ -1053,28 +1053,29 @@ static int br_mdb_add(struct sk_buff *skb, struct nlmsghdr *nlh, if (err) return err; + err = -EINVAL; /* host join errors which can happen before creating the group */ if (!cfg.p && !br_group_is_l2(&cfg.group)) { /* don't allow any flags for host-joined IP groups */ if (cfg.entry->state) { NL_SET_ERR_MSG_MOD(extack, "Flags are not allowed for host groups"); - return -EINVAL; + goto out; } if (!br_multicast_is_star_g(&cfg.group)) { NL_SET_ERR_MSG_MOD(extack, "Groups with sources cannot be manually host joined"); - return -EINVAL; + goto out; } } if (br_group_is_l2(&cfg.group) && cfg.entry->state != MDB_PERMANENT) { NL_SET_ERR_MSG_MOD(extack, "Only permanent L2 entries allowed"); - return -EINVAL; + goto out; } if (cfg.p) { if (cfg.p->state == BR_STATE_DISABLED && cfg.entry->state != MDB_PERMANENT) { NL_SET_ERR_MSG_MOD(extack, "Port is in disabled state and entry is not permanent"); - return -EINVAL; + goto out; } vg = nbp_vlan_group(cfg.p); } else { @@ -1096,6 +1097,7 @@ static int br_mdb_add(struct sk_buff *skb, struct nlmsghdr *nlh, err = __br_mdb_add(&cfg, extack); } +out: return err; } From fd0c696164cf13ae0128f14209e2dbfcd86584b8 Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Sat, 10 Dec 2022 16:56:24 +0200 Subject: [PATCH 1987/2002] bridge: mcast: Expose br_multicast_new_group_src() Currently, new group source entries are only created in response to received Membership Reports. Subsequent patches are going to allow user space to install (*, G) entries with a source list. As a preparatory step, expose br_multicast_new_group_src() so that it could later be invoked from the MDB code (i.e., br_mdb.c) that handles RTM_NEWMDB messages. Signed-off-by: Ido Schimmel Acked-by: Nikolay Aleksandrov Signed-off-by: Jakub Kicinski --- net/bridge/br_multicast.c | 2 +- net/bridge/br_private.h | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/net/bridge/br_multicast.c b/net/bridge/br_multicast.c index db4c3900ae95..b2bc23fdcee5 100644 --- a/net/bridge/br_multicast.c +++ b/net/bridge/br_multicast.c @@ -1232,7 +1232,7 @@ br_multicast_find_group_src(struct net_bridge_port_group *pg, struct br_ip *ip) return NULL; } -static struct net_bridge_group_src * +struct net_bridge_group_src * br_multicast_new_group_src(struct net_bridge_port_group *pg, struct br_ip *src_ip) { struct net_bridge_group_src *grp_src; diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h index 3997e16c15fc..183de6c57d72 100644 --- a/net/bridge/br_private.h +++ b/net/bridge/br_private.h @@ -974,6 +974,9 @@ void br_multicast_sg_add_exclude_ports(struct net_bridge_mdb_entry *star_mp, struct net_bridge_port_group *sg); struct net_bridge_group_src * br_multicast_find_group_src(struct net_bridge_port_group *pg, struct br_ip *ip); +struct net_bridge_group_src * +br_multicast_new_group_src(struct net_bridge_port_group *pg, + struct br_ip *src_ip); void br_multicast_del_group_src(struct net_bridge_group_src *src, bool fastleave); void br_multicast_ctx_init(struct net_bridge *br, From 083e353482b4c9b727846643ad6ca7b784dd486b Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Sat, 10 Dec 2022 16:56:25 +0200 Subject: [PATCH 1988/2002] bridge: mcast: Expose __br_multicast_del_group_src() Expose __br_multicast_del_group_src() which is symmetric to br_multicast_new_group_src() and does not remove the installed {S, G} forwarding entry, unlike br_multicast_del_group_src(). The function will be used in the error path when user space was able to add a new source entry, but failed to install a corresponding forwarding entry. Signed-off-by: Ido Schimmel Acked-by: Nikolay Aleksandrov Signed-off-by: Jakub Kicinski --- net/bridge/br_multicast.c | 11 ++++++++--- net/bridge/br_private.h | 1 + 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/net/bridge/br_multicast.c b/net/bridge/br_multicast.c index b2bc23fdcee5..8432b4ea7f28 100644 --- a/net/bridge/br_multicast.c +++ b/net/bridge/br_multicast.c @@ -650,18 +650,23 @@ static void br_multicast_destroy_group_src(struct net_bridge_mcast_gc *gc) kfree_rcu(src, rcu); } -void br_multicast_del_group_src(struct net_bridge_group_src *src, - bool fastleave) +void __br_multicast_del_group_src(struct net_bridge_group_src *src) { struct net_bridge *br = src->pg->key.port->br; - br_multicast_fwd_src_remove(src, fastleave); hlist_del_init_rcu(&src->node); src->pg->src_ents--; hlist_add_head(&src->mcast_gc.gc_node, &br->mcast_gc_list); queue_work(system_long_wq, &br->mcast_gc_work); } +void br_multicast_del_group_src(struct net_bridge_group_src *src, + bool fastleave) +{ + br_multicast_fwd_src_remove(src, fastleave); + __br_multicast_del_group_src(src); +} + static void br_multicast_destroy_port_group(struct net_bridge_mcast_gc *gc) { struct net_bridge_port_group *pg; diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h index 183de6c57d72..a3db99d79a3d 100644 --- a/net/bridge/br_private.h +++ b/net/bridge/br_private.h @@ -977,6 +977,7 @@ br_multicast_find_group_src(struct net_bridge_port_group *pg, struct br_ip *ip); struct net_bridge_group_src * br_multicast_new_group_src(struct net_bridge_port_group *pg, struct br_ip *src_ip); +void __br_multicast_del_group_src(struct net_bridge_group_src *src); void br_multicast_del_group_src(struct net_bridge_group_src *src, bool fastleave); void br_multicast_ctx_init(struct net_bridge *br, From a01ecb1712ddbcd41360ad0c554b460adbac0528 Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Sat, 10 Dec 2022 16:56:26 +0200 Subject: [PATCH 1989/2002] bridge: mcast: Add a flag for user installed source entries There are a few places where the bridge driver differentiates between (S, G) entries installed by the kernel (in response to Membership Reports) and those installed by user space. One of them is when deleting an (S, G) entry corresponding to a source entry that is being deleted. While user space cannot currently add a source entry to a (*, G), it can add an (S, G) entry that later corresponds to a source entry created by the reception of a Membership Report. If this source entry is later deleted because its source timer expired or because the (*, G) entry is being deleted, the bridge driver will not delete the corresponding (S, G) entry if it was added by user space as permanent. This is going to be a problem when the ability to install a (*, G) with a source list is exposed to user space. In this case, when user space installs the (*, G) as permanent, then all the (S, G) entries corresponding to its source list will also be installed as permanent. When user space deletes the (*, G), all the source entries will be deleted and the expectation is that the corresponding (S, G) entries will be deleted as well. Solve this by introducing a new source entry flag denoting that the entry was installed by user space. When the entry is deleted, delete the corresponding (S, G) entry even if it was installed by user space as permanent, as the flag tells us that it was installed in response to the source entry being created. The flag will be set in a subsequent patch where source entries are created in response to user requests. Signed-off-by: Ido Schimmel Acked-by: Nikolay Aleksandrov Signed-off-by: Jakub Kicinski --- net/bridge/br_multicast.c | 3 ++- net/bridge/br_private.h | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/net/bridge/br_multicast.c b/net/bridge/br_multicast.c index 8432b4ea7f28..48170bd3785e 100644 --- a/net/bridge/br_multicast.c +++ b/net/bridge/br_multicast.c @@ -552,7 +552,8 @@ static void br_multicast_fwd_src_remove(struct net_bridge_group_src *src, continue; if (p->rt_protocol != RTPROT_KERNEL && - (p->flags & MDB_PG_FLAGS_PERMANENT)) + (p->flags & MDB_PG_FLAGS_PERMANENT) && + !(src->flags & BR_SGRP_F_USER_ADDED)) break; if (fastleave) diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h index a3db99d79a3d..74f17b56c9eb 100644 --- a/net/bridge/br_private.h +++ b/net/bridge/br_private.h @@ -300,6 +300,7 @@ struct net_bridge_fdb_flush_desc { #define BR_SGRP_F_DELETE BIT(0) #define BR_SGRP_F_SEND BIT(1) #define BR_SGRP_F_INSTALLED BIT(2) +#define BR_SGRP_F_USER_ADDED BIT(3) struct net_bridge_mcast_gc { struct hlist_node gc_node; From 079afd66161bbbde14ed9f207f7fe6170f5b37b3 Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Sat, 10 Dec 2022 16:56:27 +0200 Subject: [PATCH 1990/2002] bridge: mcast: Avoid arming group timer when (S, G) corresponds to a source User space will soon be able to install a (*, G) with a source list, prompting the creation of a (S, G) entry for each source. In this case, the group timer of the (S, G) entry should never be set. Solve this by adding a new field to the MDB configuration structure that denotes whether the (S, G) corresponds to a source or not. The field will be set in a subsequent patch where br_mdb_add_group_sg() is called in order to create a (S, G) entry for each user provided source. Signed-off-by: Ido Schimmel Acked-by: Nikolay Aleksandrov Signed-off-by: Jakub Kicinski --- net/bridge/br_mdb.c | 2 +- net/bridge/br_private.h | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/net/bridge/br_mdb.c b/net/bridge/br_mdb.c index 95780652cdbf..7cda9d1c5c93 100644 --- a/net/bridge/br_mdb.c +++ b/net/bridge/br_mdb.c @@ -814,7 +814,7 @@ static int br_mdb_add_group_sg(const struct br_mdb_config *cfg, return -ENOMEM; } rcu_assign_pointer(*pp, p); - if (!(flags & MDB_PG_FLAGS_PERMANENT)) + if (!(flags & MDB_PG_FLAGS_PERMANENT) && !cfg->src_entry) mod_timer(&p->timer, now + brmctx->multicast_membership_interval); br_mdb_notify(cfg->br->dev, mp, p, RTM_NEWMDB); diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h index 74f17b56c9eb..e98bfe3c02e1 100644 --- a/net/bridge/br_private.h +++ b/net/bridge/br_private.h @@ -98,6 +98,7 @@ struct br_mdb_config { struct net_bridge_port *p; struct br_mdb_entry *entry; struct br_ip group; + bool src_entry; }; #endif From b1c8fec8d459fb49b2033c014256477e51913e2e Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Sat, 10 Dec 2022 16:56:28 +0200 Subject: [PATCH 1991/2002] bridge: mcast: Add support for (*, G) with a source list and filter mode In preparation for allowing user space to add (*, G) entries with a source list and associated filter mode, add the necessary plumbing to handle such requests. Extend the MDB configuration structure with a currently empty source array and filter mode that is currently hard coded to EXCLUDE. Add the source entries and the corresponding (S, G) entries before making the new (*, G) port group entry visible to the data path. Handle the creation of each source entry in a similar fashion to how it is created from the data path in response to received Membership Reports: Create the source entry, arm the source timer (if needed), add a corresponding (S, G) forwarding entry and finally mark the source entry as installed (by user space). Add the (S, G) entry by populating an MDB configuration structure and calling br_mdb_add_group_sg() as if a new entry is created by user space, with the sole difference that the 'src_entry' field is set to make sure that the group timer of such entries is never armed. Note that it is not currently possible to add more than 32 source entries to a port group entry. If this proves to be a problem we can either increase 'PG_SRC_ENT_LIMIT' or avoid forcing a limit on entries created by user space. Signed-off-by: Ido Schimmel Acked-by: Nikolay Aleksandrov Signed-off-by: Jakub Kicinski --- net/bridge/br_mdb.c | 128 +++++++++++++++++++++++++++++++++++++++- net/bridge/br_private.h | 7 +++ 2 files changed, 132 insertions(+), 3 deletions(-) diff --git a/net/bridge/br_mdb.c b/net/bridge/br_mdb.c index 7cda9d1c5c93..e9a4b7e247e7 100644 --- a/net/bridge/br_mdb.c +++ b/net/bridge/br_mdb.c @@ -836,6 +836,114 @@ static int br_mdb_add_group_sg(const struct br_mdb_config *cfg, return 0; } +static int br_mdb_add_group_src_fwd(const struct br_mdb_config *cfg, + struct br_ip *src_ip, + struct net_bridge_mcast *brmctx, + struct netlink_ext_ack *extack) +{ + struct net_bridge_mdb_entry *sgmp; + struct br_mdb_config sg_cfg; + struct br_ip sg_ip; + u8 flags = 0; + + sg_ip = cfg->group; + sg_ip.src = src_ip->src; + sgmp = br_multicast_new_group(cfg->br, &sg_ip); + if (IS_ERR(sgmp)) { + NL_SET_ERR_MSG_MOD(extack, "Failed to add (S, G) MDB entry"); + return PTR_ERR(sgmp); + } + + if (cfg->entry->state == MDB_PERMANENT) + flags |= MDB_PG_FLAGS_PERMANENT; + if (cfg->filter_mode == MCAST_EXCLUDE) + flags |= MDB_PG_FLAGS_BLOCKED; + + memset(&sg_cfg, 0, sizeof(sg_cfg)); + sg_cfg.br = cfg->br; + sg_cfg.p = cfg->p; + sg_cfg.entry = cfg->entry; + sg_cfg.group = sg_ip; + sg_cfg.src_entry = true; + sg_cfg.filter_mode = MCAST_INCLUDE; + return br_mdb_add_group_sg(&sg_cfg, sgmp, brmctx, flags, extack); +} + +static int br_mdb_add_group_src(const struct br_mdb_config *cfg, + struct net_bridge_port_group *pg, + struct net_bridge_mcast *brmctx, + struct br_mdb_src_entry *src, + struct netlink_ext_ack *extack) +{ + struct net_bridge_group_src *ent; + unsigned long now = jiffies; + int err; + + ent = br_multicast_find_group_src(pg, &src->addr); + if (!ent) { + ent = br_multicast_new_group_src(pg, &src->addr); + if (!ent) { + NL_SET_ERR_MSG_MOD(extack, "Failed to add new source entry"); + return -ENOSPC; + } + } else { + NL_SET_ERR_MSG_MOD(extack, "Source entry already exists"); + return -EEXIST; + } + + if (cfg->filter_mode == MCAST_INCLUDE && + cfg->entry->state == MDB_TEMPORARY) + mod_timer(&ent->timer, now + br_multicast_gmi(brmctx)); + else + del_timer(&ent->timer); + + /* Install a (S, G) forwarding entry for the source. */ + err = br_mdb_add_group_src_fwd(cfg, &src->addr, brmctx, extack); + if (err) + goto err_del_sg; + + ent->flags = BR_SGRP_F_INSTALLED | BR_SGRP_F_USER_ADDED; + + return 0; + +err_del_sg: + __br_multicast_del_group_src(ent); + return err; +} + +static void br_mdb_del_group_src(struct net_bridge_port_group *pg, + struct br_mdb_src_entry *src) +{ + struct net_bridge_group_src *ent; + + ent = br_multicast_find_group_src(pg, &src->addr); + if (WARN_ON_ONCE(!ent)) + return; + br_multicast_del_group_src(ent, false); +} + +static int br_mdb_add_group_srcs(const struct br_mdb_config *cfg, + struct net_bridge_port_group *pg, + struct net_bridge_mcast *brmctx, + struct netlink_ext_ack *extack) +{ + int i, err; + + for (i = 0; i < cfg->num_src_entries; i++) { + err = br_mdb_add_group_src(cfg, pg, brmctx, + &cfg->src_entries[i], extack); + if (err) + goto err_del_group_srcs; + } + + return 0; + +err_del_group_srcs: + for (i--; i >= 0; i--) + br_mdb_del_group_src(pg, &cfg->src_entries[i]); + return err; +} + static int br_mdb_add_group_star_g(const struct br_mdb_config *cfg, struct net_bridge_mdb_entry *mp, struct net_bridge_mcast *brmctx, @@ -845,6 +953,7 @@ static int br_mdb_add_group_star_g(const struct br_mdb_config *cfg, struct net_bridge_port_group __rcu **pp; struct net_bridge_port_group *p; unsigned long now = jiffies; + int err; for (pp = &mp->ports; (p = mlock_dereference(*pp, cfg->br)) != NULL; @@ -858,23 +967,35 @@ static int br_mdb_add_group_star_g(const struct br_mdb_config *cfg, } p = br_multicast_new_port_group(cfg->p, &cfg->group, *pp, flags, NULL, - MCAST_EXCLUDE, RTPROT_STATIC); + cfg->filter_mode, RTPROT_STATIC); if (unlikely(!p)) { NL_SET_ERR_MSG_MOD(extack, "Couldn't allocate new (*, G) port group"); return -ENOMEM; } + + err = br_mdb_add_group_srcs(cfg, p, brmctx, extack); + if (err) + goto err_del_port_group; + rcu_assign_pointer(*pp, p); - if (!(flags & MDB_PG_FLAGS_PERMANENT)) + if (!(flags & MDB_PG_FLAGS_PERMANENT) && + cfg->filter_mode == MCAST_EXCLUDE) mod_timer(&p->timer, now + brmctx->multicast_membership_interval); br_mdb_notify(cfg->br->dev, mp, p, RTM_NEWMDB); /* If we are adding a new EXCLUDE port group (*, G), it needs to be * also added to all (S, G) entries for proper replication. */ - if (br_multicast_should_handle_mode(brmctx, cfg->group.proto)) + if (br_multicast_should_handle_mode(brmctx, cfg->group.proto) && + cfg->filter_mode == MCAST_EXCLUDE) br_multicast_star_g_handle_mode(p, MCAST_EXCLUDE); return 0; + +err_del_port_group: + hlist_del_init(&p->mglist); + kfree(p); + return err; } static int br_mdb_add_group(const struct br_mdb_config *cfg, @@ -967,6 +1088,7 @@ static int br_mdb_config_init(struct net *net, const struct nlmsghdr *nlh, return err; memset(cfg, 0, sizeof(*cfg)); + cfg->filter_mode = MCAST_EXCLUDE; bpm = nlmsg_data(nlh); if (!bpm->ifindex) { diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h index e98bfe3c02e1..368f5f6fa42b 100644 --- a/net/bridge/br_private.h +++ b/net/bridge/br_private.h @@ -93,12 +93,19 @@ struct bridge_mcast_stats { struct u64_stats_sync syncp; }; +struct br_mdb_src_entry { + struct br_ip addr; +}; + struct br_mdb_config { struct net_bridge *br; struct net_bridge_port *p; struct br_mdb_entry *entry; struct br_ip group; bool src_entry; + u8 filter_mode; + struct br_mdb_src_entry *src_entries; + int num_src_entries; }; #endif From 6afaae6d12f54accf194e73ece617d1f456e438d Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Sat, 10 Dec 2022 16:56:29 +0200 Subject: [PATCH 1992/2002] bridge: mcast: Allow user space to add (*, G) with a source list and filter mode Add new netlink attributes to the RTM_NEWMDB request that allow user space to add (*, G) with a source list and filter mode. The RTM_NEWMDB message can already dump such entries (created by the kernel) so there is no need to add dump support. However, the message contains a different set of attributes depending if it is a request or a response. The naming and structure of the new attributes try to follow the existing ones used in the response. Request: [ struct nlmsghdr ] [ struct br_port_msg ] [ MDBA_SET_ENTRY ] struct br_mdb_entry [ MDBA_SET_ENTRY_ATTRS ] [ MDBE_ATTR_SOURCE ] struct in_addr / struct in6_addr [ MDBE_ATTR_SRC_LIST ] // new [ MDBE_SRC_LIST_ENTRY ] [ MDBE_SRCATTR_ADDRESS ] struct in_addr / struct in6_addr [ ...] [ MDBE_ATTR_GROUP_MODE ] // new u8 Response: [ struct nlmsghdr ] [ struct br_port_msg ] [ MDBA_MDB ] [ MDBA_MDB_ENTRY ] [ MDBA_MDB_ENTRY_INFO ] struct br_mdb_entry [ MDBA_MDB_EATTR_TIMER ] u32 [ MDBA_MDB_EATTR_SOURCE ] struct in_addr / struct in6_addr [ MDBA_MDB_EATTR_RTPROT ] u8 [ MDBA_MDB_EATTR_SRC_LIST ] [ MDBA_MDB_SRCLIST_ENTRY ] [ MDBA_MDB_SRCATTR_ADDRESS ] struct in_addr / struct in6_addr [ MDBA_MDB_SRCATTR_TIMER ] u8 [...] [ MDBA_MDB_EATTR_GROUP_MODE ] u8 Signed-off-by: Ido Schimmel Acked-by: Nikolay Aleksandrov Signed-off-by: Jakub Kicinski --- include/uapi/linux/if_bridge.h | 20 +++++ net/bridge/br_mdb.c | 130 +++++++++++++++++++++++++++++++++ 2 files changed, 150 insertions(+) diff --git a/include/uapi/linux/if_bridge.h b/include/uapi/linux/if_bridge.h index a86a7e7b811f..0d9fe73fc48c 100644 --- a/include/uapi/linux/if_bridge.h +++ b/include/uapi/linux/if_bridge.h @@ -723,10 +723,30 @@ enum { enum { MDBE_ATTR_UNSPEC, MDBE_ATTR_SOURCE, + MDBE_ATTR_SRC_LIST, + MDBE_ATTR_GROUP_MODE, __MDBE_ATTR_MAX, }; #define MDBE_ATTR_MAX (__MDBE_ATTR_MAX - 1) +/* per mdb entry source */ +enum { + MDBE_SRC_LIST_UNSPEC, + MDBE_SRC_LIST_ENTRY, + __MDBE_SRC_LIST_MAX, +}; +#define MDBE_SRC_LIST_MAX (__MDBE_SRC_LIST_MAX - 1) + +/* per mdb entry per source attributes + * these are embedded in MDBE_SRC_LIST_ENTRY + */ +enum { + MDBE_SRCATTR_UNSPEC, + MDBE_SRCATTR_ADDRESS, + __MDBE_SRCATTR_MAX, +}; +#define MDBE_SRCATTR_MAX (__MDBE_SRCATTR_MAX - 1) + /* Embedded inside LINK_XSTATS_TYPE_BRIDGE */ enum { BRIDGE_XSTATS_UNSPEC, diff --git a/net/bridge/br_mdb.c b/net/bridge/br_mdb.c index e9a4b7e247e7..61d46b0a31b6 100644 --- a/net/bridge/br_mdb.c +++ b/net/bridge/br_mdb.c @@ -663,10 +663,25 @@ errout: rtnl_set_sk_err(net, RTNLGRP_MDB, err); } +static const struct nla_policy +br_mdbe_src_list_entry_pol[MDBE_SRCATTR_MAX + 1] = { + [MDBE_SRCATTR_ADDRESS] = NLA_POLICY_RANGE(NLA_BINARY, + sizeof(struct in_addr), + sizeof(struct in6_addr)), +}; + +static const struct nla_policy +br_mdbe_src_list_pol[MDBE_SRC_LIST_MAX + 1] = { + [MDBE_SRC_LIST_ENTRY] = NLA_POLICY_NESTED(br_mdbe_src_list_entry_pol), +}; + static const struct nla_policy br_mdbe_attrs_pol[MDBE_ATTR_MAX + 1] = { [MDBE_ATTR_SOURCE] = NLA_POLICY_RANGE(NLA_BINARY, sizeof(struct in_addr), sizeof(struct in6_addr)), + [MDBE_ATTR_GROUP_MODE] = NLA_POLICY_RANGE(NLA_U8, MCAST_EXCLUDE, + MCAST_INCLUDE), + [MDBE_ATTR_SRC_LIST] = NLA_POLICY_NESTED(br_mdbe_src_list_pol), }; static bool is_valid_mdb_entry(struct br_mdb_entry *entry, @@ -1051,6 +1066,76 @@ static int __br_mdb_add(const struct br_mdb_config *cfg, return ret; } +static int br_mdb_config_src_entry_init(struct nlattr *src_entry, + struct br_mdb_src_entry *src, + __be16 proto, + struct netlink_ext_ack *extack) +{ + struct nlattr *tb[MDBE_SRCATTR_MAX + 1]; + int err; + + err = nla_parse_nested(tb, MDBE_SRCATTR_MAX, src_entry, + br_mdbe_src_list_entry_pol, extack); + if (err) + return err; + + if (NL_REQ_ATTR_CHECK(extack, src_entry, tb, MDBE_SRCATTR_ADDRESS)) + return -EINVAL; + + if (!is_valid_mdb_source(tb[MDBE_SRCATTR_ADDRESS], proto, extack)) + return -EINVAL; + + src->addr.proto = proto; + nla_memcpy(&src->addr.src, tb[MDBE_SRCATTR_ADDRESS], + nla_len(tb[MDBE_SRCATTR_ADDRESS])); + + return 0; +} + +static int br_mdb_config_src_list_init(struct nlattr *src_list, + struct br_mdb_config *cfg, + struct netlink_ext_ack *extack) +{ + struct nlattr *src_entry; + int rem, err; + int i = 0; + + nla_for_each_nested(src_entry, src_list, rem) + cfg->num_src_entries++; + + if (cfg->num_src_entries >= PG_SRC_ENT_LIMIT) { + NL_SET_ERR_MSG_FMT_MOD(extack, "Exceeded maximum number of source entries (%u)", + PG_SRC_ENT_LIMIT - 1); + return -EINVAL; + } + + cfg->src_entries = kcalloc(cfg->num_src_entries, + sizeof(struct br_mdb_src_entry), GFP_KERNEL); + if (!cfg->src_entries) + return -ENOMEM; + + nla_for_each_nested(src_entry, src_list, rem) { + err = br_mdb_config_src_entry_init(src_entry, + &cfg->src_entries[i], + cfg->entry->addr.proto, + extack); + if (err) + goto err_src_entry_init; + i++; + } + + return 0; + +err_src_entry_init: + kfree(cfg->src_entries); + return err; +} + +static void br_mdb_config_src_list_fini(struct br_mdb_config *cfg) +{ + kfree(cfg->src_entries); +} + static int br_mdb_config_attrs_init(struct nlattr *set_attrs, struct br_mdb_config *cfg, struct netlink_ext_ack *extack) @@ -1070,6 +1155,44 @@ static int br_mdb_config_attrs_init(struct nlattr *set_attrs, __mdb_entry_to_br_ip(cfg->entry, &cfg->group, mdb_attrs); + if (mdb_attrs[MDBE_ATTR_GROUP_MODE]) { + if (!cfg->p) { + NL_SET_ERR_MSG_MOD(extack, "Filter mode cannot be set for host groups"); + return -EINVAL; + } + if (!br_multicast_is_star_g(&cfg->group)) { + NL_SET_ERR_MSG_MOD(extack, "Filter mode can only be set for (*, G) entries"); + return -EINVAL; + } + cfg->filter_mode = nla_get_u8(mdb_attrs[MDBE_ATTR_GROUP_MODE]); + } else { + cfg->filter_mode = MCAST_EXCLUDE; + } + + if (mdb_attrs[MDBE_ATTR_SRC_LIST]) { + if (!cfg->p) { + NL_SET_ERR_MSG_MOD(extack, "Source list cannot be set for host groups"); + return -EINVAL; + } + if (!br_multicast_is_star_g(&cfg->group)) { + NL_SET_ERR_MSG_MOD(extack, "Source list can only be set for (*, G) entries"); + return -EINVAL; + } + if (!mdb_attrs[MDBE_ATTR_GROUP_MODE]) { + NL_SET_ERR_MSG_MOD(extack, "Source list cannot be set without filter mode"); + return -EINVAL; + } + err = br_mdb_config_src_list_init(mdb_attrs[MDBE_ATTR_SRC_LIST], + cfg, extack); + if (err) + return err; + } + + if (!cfg->num_src_entries && cfg->filter_mode == MCAST_INCLUDE) { + NL_SET_ERR_MSG_MOD(extack, "Cannot add (*, G) INCLUDE with an empty source list"); + return -EINVAL; + } + return 0; } @@ -1162,6 +1285,11 @@ static int br_mdb_config_init(struct net *net, const struct nlmsghdr *nlh, return 0; } +static void br_mdb_config_fini(struct br_mdb_config *cfg) +{ + br_mdb_config_src_list_fini(cfg); +} + static int br_mdb_add(struct sk_buff *skb, struct nlmsghdr *nlh, struct netlink_ext_ack *extack) { @@ -1220,6 +1348,7 @@ static int br_mdb_add(struct sk_buff *skb, struct nlmsghdr *nlh, } out: + br_mdb_config_fini(&cfg); return err; } @@ -1295,6 +1424,7 @@ static int br_mdb_del(struct sk_buff *skb, struct nlmsghdr *nlh, err = __br_mdb_del(&cfg); } + br_mdb_config_fini(&cfg); return err; } From 1d7b66a7d9754c229d12c53ad6a1effe88c16ca4 Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Sat, 10 Dec 2022 16:56:30 +0200 Subject: [PATCH 1993/2002] bridge: mcast: Allow user space to specify MDB entry routing protocol Add the 'MDBE_ATTR_RTPORT' attribute to allow user space to specify the routing protocol of the MDB port group entry. Enforce a minimum value of 'RTPROT_STATIC' to prevent user space from using protocol values that should only be set by the kernel (e.g., 'RTPROT_KERNEL'). Maintain backward compatibility by defaulting to 'RTPROT_STATIC'. The protocol is already visible to user space in RTM_NEWMDB responses and notifications via the 'MDBA_MDB_EATTR_RTPROT' attribute. The routing protocol allows a routing daemon to distinguish between entries configured by it and those configured by the administrator. Once MDB flush is supported, the protocol can be used as a criterion according to which the flush is performed. Examples: # bridge mdb add dev br0 port dummy10 grp 239.1.1.1 permanent proto kernel Error: integer out of range. # bridge mdb add dev br0 port dummy10 grp 239.1.1.1 permanent proto static # bridge mdb add dev br0 port dummy10 grp 239.1.1.1 src 192.0.2.1 permanent proto zebra # bridge mdb add dev br0 port dummy10 grp 239.1.1.2 permanent source_list 198.51.100.1,198.51.100.2 filter_mode include proto 250 # bridge -d mdb show dev br0 port dummy10 grp 239.1.1.2 src 198.51.100.2 permanent filter_mode include proto 250 dev br0 port dummy10 grp 239.1.1.2 src 198.51.100.1 permanent filter_mode include proto 250 dev br0 port dummy10 grp 239.1.1.2 permanent filter_mode include source_list 198.51.100.2/0.00,198.51.100.1/0.00 proto 250 dev br0 port dummy10 grp 239.1.1.1 src 192.0.2.1 permanent filter_mode include proto zebra dev br0 port dummy10 grp 239.1.1.1 permanent filter_mode exclude proto static Signed-off-by: Ido Schimmel Acked-by: Nikolay Aleksandrov Signed-off-by: Jakub Kicinski --- include/uapi/linux/if_bridge.h | 1 + net/bridge/br_mdb.c | 15 +++++++++++++-- net/bridge/br_private.h | 1 + 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/include/uapi/linux/if_bridge.h b/include/uapi/linux/if_bridge.h index 0d9fe73fc48c..d9de241d90f9 100644 --- a/include/uapi/linux/if_bridge.h +++ b/include/uapi/linux/if_bridge.h @@ -725,6 +725,7 @@ enum { MDBE_ATTR_SOURCE, MDBE_ATTR_SRC_LIST, MDBE_ATTR_GROUP_MODE, + MDBE_ATTR_RTPROT, __MDBE_ATTR_MAX, }; #define MDBE_ATTR_MAX (__MDBE_ATTR_MAX - 1) diff --git a/net/bridge/br_mdb.c b/net/bridge/br_mdb.c index 61d46b0a31b6..72d4e53193e5 100644 --- a/net/bridge/br_mdb.c +++ b/net/bridge/br_mdb.c @@ -682,6 +682,7 @@ static const struct nla_policy br_mdbe_attrs_pol[MDBE_ATTR_MAX + 1] = { [MDBE_ATTR_GROUP_MODE] = NLA_POLICY_RANGE(NLA_U8, MCAST_EXCLUDE, MCAST_INCLUDE), [MDBE_ATTR_SRC_LIST] = NLA_POLICY_NESTED(br_mdbe_src_list_pol), + [MDBE_ATTR_RTPROT] = NLA_POLICY_MIN(NLA_U8, RTPROT_STATIC), }; static bool is_valid_mdb_entry(struct br_mdb_entry *entry, @@ -823,7 +824,7 @@ static int br_mdb_add_group_sg(const struct br_mdb_config *cfg, } p = br_multicast_new_port_group(cfg->p, &cfg->group, *pp, flags, NULL, - MCAST_INCLUDE, RTPROT_STATIC); + MCAST_INCLUDE, cfg->rt_protocol); if (unlikely(!p)) { NL_SET_ERR_MSG_MOD(extack, "Couldn't allocate new (S, G) port group"); return -ENOMEM; @@ -881,6 +882,7 @@ static int br_mdb_add_group_src_fwd(const struct br_mdb_config *cfg, sg_cfg.group = sg_ip; sg_cfg.src_entry = true; sg_cfg.filter_mode = MCAST_INCLUDE; + sg_cfg.rt_protocol = cfg->rt_protocol; return br_mdb_add_group_sg(&sg_cfg, sgmp, brmctx, flags, extack); } @@ -982,7 +984,7 @@ static int br_mdb_add_group_star_g(const struct br_mdb_config *cfg, } p = br_multicast_new_port_group(cfg->p, &cfg->group, *pp, flags, NULL, - cfg->filter_mode, RTPROT_STATIC); + cfg->filter_mode, cfg->rt_protocol); if (unlikely(!p)) { NL_SET_ERR_MSG_MOD(extack, "Couldn't allocate new (*, G) port group"); return -ENOMEM; @@ -1193,6 +1195,14 @@ static int br_mdb_config_attrs_init(struct nlattr *set_attrs, return -EINVAL; } + if (mdb_attrs[MDBE_ATTR_RTPROT]) { + if (!cfg->p) { + NL_SET_ERR_MSG_MOD(extack, "Protocol cannot be set for host groups"); + return -EINVAL; + } + cfg->rt_protocol = nla_get_u8(mdb_attrs[MDBE_ATTR_RTPROT]); + } + return 0; } @@ -1212,6 +1222,7 @@ static int br_mdb_config_init(struct net *net, const struct nlmsghdr *nlh, memset(cfg, 0, sizeof(*cfg)); cfg->filter_mode = MCAST_EXCLUDE; + cfg->rt_protocol = RTPROT_STATIC; bpm = nlmsg_data(nlh); if (!bpm->ifindex) { diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h index 368f5f6fa42b..cdc9e040f1f6 100644 --- a/net/bridge/br_private.h +++ b/net/bridge/br_private.h @@ -106,6 +106,7 @@ struct br_mdb_config { u8 filter_mode; struct br_mdb_src_entry *src_entries; int num_src_entries; + u8 rt_protocol; }; #endif From 61f2183512a72c28674717b1ba706ed2749938d1 Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Sat, 10 Dec 2022 16:56:31 +0200 Subject: [PATCH 1994/2002] bridge: mcast: Support replacement of MDB port group entries Now that user space can specify additional attributes of port group entries such as filter mode and source list, it makes sense to allow user space to atomically modify these attributes by replacing entries instead of forcing user space to delete the entries and add them back. Replace MDB port group entries when the 'NLM_F_REPLACE' flag is specified in the netlink message header. When a (*, G) entry is replaced, update the following attributes: Source list, state, filter mode, protocol and flags. If the entry is temporary and in EXCLUDE mode, reset the group timer to the group membership interval. If the entry is temporary and in INCLUDE mode, reset the source timers of associated sources to the group membership interval. Examples: # bridge mdb replace dev br0 port dummy10 grp 239.1.1.1 permanent source_list 192.0.2.1,192.0.2.2 filter_mode include # bridge -d -s mdb show dev br0 port dummy10 grp 239.1.1.1 src 192.0.2.2 permanent filter_mode include proto static 0.00 dev br0 port dummy10 grp 239.1.1.1 src 192.0.2.1 permanent filter_mode include proto static 0.00 dev br0 port dummy10 grp 239.1.1.1 permanent filter_mode include source_list 192.0.2.2/0.00,192.0.2.1/0.00 proto static 0.00 # bridge mdb replace dev br0 port dummy10 grp 239.1.1.1 permanent source_list 192.0.2.1,192.0.2.3 filter_mode exclude proto zebra # bridge -d -s mdb show dev br0 port dummy10 grp 239.1.1.1 src 192.0.2.3 permanent filter_mode include proto zebra blocked 0.00 dev br0 port dummy10 grp 239.1.1.1 src 192.0.2.1 permanent filter_mode include proto zebra blocked 0.00 dev br0 port dummy10 grp 239.1.1.1 permanent filter_mode exclude source_list 192.0.2.3/0.00,192.0.2.1/0.00 proto zebra 0.00 # bridge mdb replace dev br0 port dummy10 grp 239.1.1.1 temp source_list 192.0.2.4,192.0.2.3 filter_mode include proto bgp # bridge -d -s mdb show dev br0 port dummy10 grp 239.1.1.1 src 192.0.2.4 temp filter_mode include proto bgp 0.00 dev br0 port dummy10 grp 239.1.1.1 src 192.0.2.3 temp filter_mode include proto bgp 0.00 dev br0 port dummy10 grp 239.1.1.1 temp filter_mode include source_list 192.0.2.4/259.44,192.0.2.3/259.44 proto bgp 0.00 Signed-off-by: Ido Schimmel Acked-by: Nikolay Aleksandrov Signed-off-by: Jakub Kicinski --- net/bridge/br_mdb.c | 102 ++++++++++++++++++++++++++++++++++++++-- net/bridge/br_private.h | 1 + 2 files changed, 98 insertions(+), 5 deletions(-) diff --git a/net/bridge/br_mdb.c b/net/bridge/br_mdb.c index 72d4e53193e5..00e5743647b0 100644 --- a/net/bridge/br_mdb.c +++ b/net/bridge/br_mdb.c @@ -802,6 +802,27 @@ out: return brmctx; } +static int br_mdb_replace_group_sg(const struct br_mdb_config *cfg, + struct net_bridge_mdb_entry *mp, + struct net_bridge_port_group *pg, + struct net_bridge_mcast *brmctx, + unsigned char flags) +{ + unsigned long now = jiffies; + + pg->flags = flags; + pg->rt_protocol = cfg->rt_protocol; + if (!(flags & MDB_PG_FLAGS_PERMANENT) && !cfg->src_entry) + mod_timer(&pg->timer, + now + brmctx->multicast_membership_interval); + else + del_timer(&pg->timer); + + br_mdb_notify(cfg->br->dev, mp, pg, RTM_NEWMDB); + + return 0; +} + static int br_mdb_add_group_sg(const struct br_mdb_config *cfg, struct net_bridge_mdb_entry *mp, struct net_bridge_mcast *brmctx, @@ -816,8 +837,12 @@ static int br_mdb_add_group_sg(const struct br_mdb_config *cfg, (p = mlock_dereference(*pp, cfg->br)) != NULL; pp = &p->next) { if (p->key.port == cfg->p) { - NL_SET_ERR_MSG_MOD(extack, "(S, G) group is already joined by port"); - return -EEXIST; + if (!(cfg->nlflags & NLM_F_REPLACE)) { + NL_SET_ERR_MSG_MOD(extack, "(S, G) group is already joined by port"); + return -EEXIST; + } + return br_mdb_replace_group_sg(cfg, mp, p, brmctx, + flags); } if ((unsigned long)p->key.port < (unsigned long)cfg->p) break; @@ -883,6 +908,7 @@ static int br_mdb_add_group_src_fwd(const struct br_mdb_config *cfg, sg_cfg.src_entry = true; sg_cfg.filter_mode = MCAST_INCLUDE; sg_cfg.rt_protocol = cfg->rt_protocol; + sg_cfg.nlflags = cfg->nlflags; return br_mdb_add_group_sg(&sg_cfg, sgmp, brmctx, flags, extack); } @@ -903,7 +929,7 @@ static int br_mdb_add_group_src(const struct br_mdb_config *cfg, NL_SET_ERR_MSG_MOD(extack, "Failed to add new source entry"); return -ENOSPC; } - } else { + } else if (!(cfg->nlflags & NLM_F_REPLACE)) { NL_SET_ERR_MSG_MOD(extack, "Source entry already exists"); return -EEXIST; } @@ -961,6 +987,67 @@ err_del_group_srcs: return err; } +static int br_mdb_replace_group_srcs(const struct br_mdb_config *cfg, + struct net_bridge_port_group *pg, + struct net_bridge_mcast *brmctx, + struct netlink_ext_ack *extack) +{ + struct net_bridge_group_src *ent; + struct hlist_node *tmp; + int err; + + hlist_for_each_entry(ent, &pg->src_list, node) + ent->flags |= BR_SGRP_F_DELETE; + + err = br_mdb_add_group_srcs(cfg, pg, brmctx, extack); + if (err) + goto err_clear_delete; + + hlist_for_each_entry_safe(ent, tmp, &pg->src_list, node) { + if (ent->flags & BR_SGRP_F_DELETE) + br_multicast_del_group_src(ent, false); + } + + return 0; + +err_clear_delete: + hlist_for_each_entry(ent, &pg->src_list, node) + ent->flags &= ~BR_SGRP_F_DELETE; + return err; +} + +static int br_mdb_replace_group_star_g(const struct br_mdb_config *cfg, + struct net_bridge_mdb_entry *mp, + struct net_bridge_port_group *pg, + struct net_bridge_mcast *brmctx, + unsigned char flags, + struct netlink_ext_ack *extack) +{ + unsigned long now = jiffies; + int err; + + err = br_mdb_replace_group_srcs(cfg, pg, brmctx, extack); + if (err) + return err; + + pg->flags = flags; + pg->filter_mode = cfg->filter_mode; + pg->rt_protocol = cfg->rt_protocol; + if (!(flags & MDB_PG_FLAGS_PERMANENT) && + cfg->filter_mode == MCAST_EXCLUDE) + mod_timer(&pg->timer, + now + brmctx->multicast_membership_interval); + else + del_timer(&pg->timer); + + br_mdb_notify(cfg->br->dev, mp, pg, RTM_NEWMDB); + + if (br_multicast_should_handle_mode(brmctx, cfg->group.proto)) + br_multicast_star_g_handle_mode(pg, cfg->filter_mode); + + return 0; +} + static int br_mdb_add_group_star_g(const struct br_mdb_config *cfg, struct net_bridge_mdb_entry *mp, struct net_bridge_mcast *brmctx, @@ -976,8 +1063,12 @@ static int br_mdb_add_group_star_g(const struct br_mdb_config *cfg, (p = mlock_dereference(*pp, cfg->br)) != NULL; pp = &p->next) { if (p->key.port == cfg->p) { - NL_SET_ERR_MSG_MOD(extack, "(*, G) group is already joined by port"); - return -EEXIST; + if (!(cfg->nlflags & NLM_F_REPLACE)) { + NL_SET_ERR_MSG_MOD(extack, "(*, G) group is already joined by port"); + return -EEXIST; + } + return br_mdb_replace_group_star_g(cfg, mp, p, brmctx, + flags, extack); } if ((unsigned long)p->key.port < (unsigned long)cfg->p) break; @@ -1223,6 +1314,7 @@ static int br_mdb_config_init(struct net *net, const struct nlmsghdr *nlh, memset(cfg, 0, sizeof(*cfg)); cfg->filter_mode = MCAST_EXCLUDE; cfg->rt_protocol = RTPROT_STATIC; + cfg->nlflags = nlh->nlmsg_flags; bpm = nlmsg_data(nlh); if (!bpm->ifindex) { diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h index cdc9e040f1f6..15ef7fd508ee 100644 --- a/net/bridge/br_private.h +++ b/net/bridge/br_private.h @@ -104,6 +104,7 @@ struct br_mdb_config { struct br_ip group; bool src_entry; u8 filter_mode; + u16 nlflags; struct br_mdb_src_entry *src_entries; int num_src_entries; u8 rt_protocol; From f9923a67ab623723663e8ba3eadf00a5bac82d6e Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Sat, 10 Dec 2022 16:56:32 +0200 Subject: [PATCH 1995/2002] selftests: forwarding: Rename bridge_mdb test The test is only concerned with host MDB entries and not with MDB entries as a whole. Rename the test to reflect that. Subsequent patches will add a more general test that will contain the test cases for host MDB entries and remove the current test. Signed-off-by: Ido Schimmel Acked-by: Nikolay Aleksandrov Signed-off-by: Jakub Kicinski --- tools/testing/selftests/net/forwarding/Makefile | 2 +- .../net/forwarding/{bridge_mdb.sh => bridge_mdb_host.sh} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename tools/testing/selftests/net/forwarding/{bridge_mdb.sh => bridge_mdb_host.sh} (100%) diff --git a/tools/testing/selftests/net/forwarding/Makefile b/tools/testing/selftests/net/forwarding/Makefile index a9c5c1be5088..f2df81ca3179 100644 --- a/tools/testing/selftests/net/forwarding/Makefile +++ b/tools/testing/selftests/net/forwarding/Makefile @@ -2,7 +2,7 @@ TEST_PROGS = bridge_igmp.sh \ bridge_locked_port.sh \ - bridge_mdb.sh \ + bridge_mdb_host.sh \ bridge_mdb_port_down.sh \ bridge_mld.sh \ bridge_port_isolation.sh \ diff --git a/tools/testing/selftests/net/forwarding/bridge_mdb.sh b/tools/testing/selftests/net/forwarding/bridge_mdb_host.sh similarity index 100% rename from tools/testing/selftests/net/forwarding/bridge_mdb.sh rename to tools/testing/selftests/net/forwarding/bridge_mdb_host.sh From b6d00da08610e2b17412330d7cfc7c04a3b6570d Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Sat, 10 Dec 2022 16:56:33 +0200 Subject: [PATCH 1996/2002] selftests: forwarding: Add bridge MDB test Add a selftests that includes the following test cases: 1. Configuration tests. Both valid and invalid configurations are tested across all entry types (e.g., L2, IPv4). 2. Forwarding tests. Both host and port group entries are tested across all entry types. 3. Interaction between user installed MDB entries and IGMP / MLD control packets. Example output: INFO: # Host entries configuration tests TEST: Common host entries configuration tests (IPv4) [ OK ] TEST: Common host entries configuration tests (IPv6) [ OK ] TEST: Common host entries configuration tests (L2) [ OK ] INFO: # Port group entries configuration tests - (*, G) TEST: Common port group entries configuration tests (IPv4 (*, G)) [ OK ] TEST: Common port group entries configuration tests (IPv6 (*, G)) [ OK ] TEST: IPv4 (*, G) port group entries configuration tests [ OK ] TEST: IPv6 (*, G) port group entries configuration tests [ OK ] INFO: # Port group entries configuration tests - (S, G) TEST: Common port group entries configuration tests (IPv4 (S, G)) [ OK ] TEST: Common port group entries configuration tests (IPv6 (S, G)) [ OK ] TEST: IPv4 (S, G) port group entries configuration tests [ OK ] TEST: IPv6 (S, G) port group entries configuration tests [ OK ] INFO: # Port group entries configuration tests - L2 TEST: Common port group entries configuration tests (L2 (*, G)) [ OK ] TEST: L2 (*, G) port group entries configuration tests [ OK ] INFO: # Forwarding tests TEST: IPv4 host entries forwarding tests [ OK ] TEST: IPv6 host entries forwarding tests [ OK ] TEST: L2 host entries forwarding tests [ OK ] TEST: IPv4 port group "exclude" entries forwarding tests [ OK ] TEST: IPv6 port group "exclude" entries forwarding tests [ OK ] TEST: IPv4 port group "include" entries forwarding tests [ OK ] TEST: IPv6 port group "include" entries forwarding tests [ OK ] TEST: L2 port entries forwarding tests [ OK ] INFO: # Control packets tests TEST: IGMPv3 MODE_IS_INCLUE tests [ OK ] TEST: MLDv2 MODE_IS_INCLUDE tests [ OK ] Signed-off-by: Ido Schimmel Acked-by: Nikolay Aleksandrov Signed-off-by: Jakub Kicinski --- .../testing/selftests/net/forwarding/Makefile | 1 + .../selftests/net/forwarding/bridge_mdb.sh | 1164 +++++++++++++++++ 2 files changed, 1165 insertions(+) create mode 100755 tools/testing/selftests/net/forwarding/bridge_mdb.sh diff --git a/tools/testing/selftests/net/forwarding/Makefile b/tools/testing/selftests/net/forwarding/Makefile index f2df81ca3179..453ae006fbcf 100644 --- a/tools/testing/selftests/net/forwarding/Makefile +++ b/tools/testing/selftests/net/forwarding/Makefile @@ -2,6 +2,7 @@ TEST_PROGS = bridge_igmp.sh \ bridge_locked_port.sh \ + bridge_mdb.sh \ bridge_mdb_host.sh \ bridge_mdb_port_down.sh \ bridge_mld.sh \ diff --git a/tools/testing/selftests/net/forwarding/bridge_mdb.sh b/tools/testing/selftests/net/forwarding/bridge_mdb.sh new file mode 100755 index 000000000000..2fa5973c0c28 --- /dev/null +++ b/tools/testing/selftests/net/forwarding/bridge_mdb.sh @@ -0,0 +1,1164 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0 + +# +-----------------------+ +------------------------+ +# | H1 (vrf) | | H2 (vrf) | +# | + $h1.10 | | + $h2.10 | +# | | 192.0.2.1/28 | | | 192.0.2.2/28 | +# | | 2001:db8:1::1/64 | | | 2001:db8:1::2/64 | +# | | | | | | +# | | + $h1.20 | | | + $h2.20 | +# | \ | 198.51.100.1/24 | | \ | 198.51.100.2/24 | +# | \ | 2001:db8:2::1/64 | | \ | 2001:db8:2::2/64 | +# | \| | | \| | +# | + $h1 | | + $h2 | +# +----|------------------+ +----|-------------------+ +# | | +# +----|--------------------------------------------------|-------------------+ +# | SW | | | +# | +--|--------------------------------------------------|-----------------+ | +# | | + $swp1 BR0 (802.1q) + $swp2 | | +# | | vid 10 vid 10 | | +# | | vid 20 vid 20 | | +# | | | | +# | +-----------------------------------------------------------------------+ | +# +---------------------------------------------------------------------------+ + +ALL_TESTS=" + cfg_test + fwd_test + ctrl_test +" + +NUM_NETIFS=4 +source lib.sh +source tc_common.sh + +h1_create() +{ + simple_if_init $h1 + vlan_create $h1 10 v$h1 192.0.2.1/28 2001:db8:1::1/64 + vlan_create $h1 20 v$h1 198.51.100.1/24 2001:db8:2::1/64 +} + +h1_destroy() +{ + vlan_destroy $h1 20 + vlan_destroy $h1 10 + simple_if_fini $h1 +} + +h2_create() +{ + simple_if_init $h2 + vlan_create $h2 10 v$h2 192.0.2.2/28 + vlan_create $h2 20 v$h2 198.51.100.2/24 +} + +h2_destroy() +{ + vlan_destroy $h2 20 + vlan_destroy $h2 10 + simple_if_fini $h2 +} + +switch_create() +{ + ip link add name br0 type bridge vlan_filtering 1 vlan_default_pvid 0 \ + mcast_snooping 1 mcast_igmp_version 3 mcast_mld_version 2 + bridge vlan add vid 10 dev br0 self + bridge vlan add vid 20 dev br0 self + ip link set dev br0 up + + ip link set dev $swp1 master br0 + ip link set dev $swp1 up + bridge vlan add vid 10 dev $swp1 + bridge vlan add vid 20 dev $swp1 + + ip link set dev $swp2 master br0 + ip link set dev $swp2 up + bridge vlan add vid 10 dev $swp2 + bridge vlan add vid 20 dev $swp2 + + tc qdisc add dev br0 clsact + tc qdisc add dev $h2 clsact +} + +switch_destroy() +{ + tc qdisc del dev $h2 clsact + tc qdisc del dev br0 clsact + + bridge vlan del vid 20 dev $swp2 + bridge vlan del vid 10 dev $swp2 + ip link set dev $swp2 down + ip link set dev $swp2 nomaster + + bridge vlan del vid 20 dev $swp1 + bridge vlan del vid 10 dev $swp1 + ip link set dev $swp1 down + ip link set dev $swp1 nomaster + + ip link set dev br0 down + bridge vlan del vid 20 dev br0 self + bridge vlan del vid 10 dev br0 self + ip link del dev br0 +} + +setup_prepare() +{ + h1=${NETIFS[p1]} + swp1=${NETIFS[p2]} + + swp2=${NETIFS[p3]} + h2=${NETIFS[p4]} + + vrf_prepare + forwarding_enable + + h1_create + h2_create + switch_create +} + +cleanup() +{ + pre_cleanup + + switch_destroy + h2_destroy + h1_destroy + + forwarding_restore + vrf_cleanup +} + +cfg_test_host_common() +{ + local name=$1; shift + local grp=$1; shift + local src=$1; shift + local state=$1; shift + local invalid_state=$1; shift + + RET=0 + + # Check basic add, replace and delete behavior. + bridge mdb add dev br0 port br0 grp $grp $state vid 10 + bridge mdb show dev br0 vid 10 | grep -q "$grp" + check_err $? "Failed to add $name host entry" + + bridge mdb replace dev br0 port br0 grp $grp $state vid 10 &> /dev/null + check_fail $? "Managed to replace $name host entry" + + bridge mdb del dev br0 port br0 grp $grp $state vid 10 + bridge mdb show dev br0 vid 10 | grep -q "$grp" + check_fail $? "Failed to delete $name host entry" + + # Check error cases. + bridge mdb add dev br0 port br0 grp $grp $invalid_state vid 10 \ + &> /dev/null + check_fail $? "Managed to add $name host entry with a $invalid_state state" + + bridge mdb add dev br0 port br0 grp $grp src $src $state vid 10 \ + &> /dev/null + check_fail $? "Managed to add $name host entry with a source" + + bridge mdb add dev br0 port br0 grp $grp $state vid 10 \ + filter_mode exclude &> /dev/null + check_fail $? "Managed to add $name host entry with a filter mode" + + bridge mdb add dev br0 port br0 grp $grp $state vid 10 \ + source_list $src &> /dev/null + check_fail $? "Managed to add $name host entry with a source list" + + bridge mdb add dev br0 port br0 grp $grp $state vid 10 \ + proto 123 &> /dev/null + check_fail $? "Managed to add $name host entry with a protocol" + + log_test "Common host entries configuration tests ($name)" +} + +# Check configuration of host entries from all types. +cfg_test_host() +{ + echo + log_info "# Host entries configuration tests" + + cfg_test_host_common "IPv4" "239.1.1.1" "192.0.2.1" "temp" "permanent" + cfg_test_host_common "IPv6" "ff0e::1" "2001:db8:1::1" "temp" "permanent" + cfg_test_host_common "L2" "01:02:03:04:05:06" "00:00:00:00:00:01" \ + "permanent" "temp" +} + +cfg_test_port_common() +{ + local name=$1;shift + local grp_key=$1; shift + + RET=0 + + # Check basic add, replace and delete behavior. + bridge mdb add dev br0 port $swp1 $grp_key permanent vid 10 + bridge mdb show dev br0 vid 10 | grep -q "$grp_key" + check_err $? "Failed to add $name entry" + + bridge mdb replace dev br0 port $swp1 $grp_key permanent vid 10 \ + &> /dev/null + check_err $? "Failed to replace $name entry" + + bridge mdb del dev br0 port $swp1 $grp_key permanent vid 10 + bridge mdb show dev br0 vid 10 | grep -q "$grp_key" + check_fail $? "Failed to delete $name entry" + + # Check default protocol and replacement. + bridge mdb add dev br0 port $swp1 $grp_key permanent vid 10 + bridge -d mdb show dev br0 vid 10 | grep "$grp_key" | grep -q "static" + check_err $? "$name entry not added with default \"static\" protocol" + + bridge mdb replace dev br0 port $swp1 $grp_key permanent vid 10 \ + proto 123 + bridge -d mdb show dev br0 vid 10 | grep "$grp_key" | grep -q "123" + check_err $? "Failed to replace protocol of $name entry" + bridge mdb del dev br0 port $swp1 $grp_key permanent vid 10 + + # Check behavior when VLAN is not specified. + bridge mdb add dev br0 port $swp1 $grp_key permanent + bridge mdb show dev br0 vid 10 | grep -q "$grp_key" + check_err $? "$name entry with VLAN 10 not added when VLAN was not specified" + bridge mdb show dev br0 vid 20 | grep -q "$grp_key" + check_err $? "$name entry with VLAN 20 not added when VLAN was not specified" + + bridge mdb del dev br0 port $swp1 $grp_key permanent + bridge mdb show dev br0 vid 10 | grep -q "$grp_key" + check_fail $? "$name entry with VLAN 10 not deleted when VLAN was not specified" + bridge mdb show dev br0 vid 20 | grep -q "$grp_key" + check_fail $? "$name entry with VLAN 20 not deleted when VLAN was not specified" + + # Check behavior when bridge port is down. + ip link set dev $swp1 down + + bridge mdb add dev br0 port $swp1 $grp_key permanent vid 10 + check_err $? "Failed to add $name permanent entry when bridge port is down" + + bridge mdb del dev br0 port $swp1 $grp_key permanent vid 10 + + bridge mdb add dev br0 port $swp1 $grp_key temp vid 10 &> /dev/null + check_fail $? "Managed to add $name temporary entry when bridge port is down" + + ip link set dev $swp1 up + setup_wait_dev $swp1 + + # Check error cases. + ip link set dev br0 down + bridge mdb add dev br0 port $swp1 $grp_key permanent vid 10 \ + &> /dev/null + check_fail $? "Managed to add $name entry when bridge is down" + ip link set dev br0 up + + ip link set dev br0 type bridge mcast_snooping 0 + bridge mdb add dev br0 port $swp1 $grp_key permanent vid \ + 10 &> /dev/null + check_fail $? "Managed to add $name entry when multicast snooping is disabled" + ip link set dev br0 type bridge mcast_snooping 1 + + bridge mdb add dev br0 port $swp1 $grp_key permanent vid 5000 \ + &> /dev/null + check_fail $? "Managed to add $name entry with an invalid VLAN" + + log_test "Common port group entries configuration tests ($name)" +} + +src_list_create() +{ + local src_prefix=$1; shift + local num_srcs=$1; shift + local src_list + local i + + for i in $(seq 1 $num_srcs); do + src_list=${src_list},${src_prefix}${i} + done + + echo $src_list | cut -c 2- +} + +__cfg_test_port_ip_star_g() +{ + local name=$1; shift + local grp=$1; shift + local invalid_grp=$1; shift + local src_prefix=$1; shift + local src1=${src_prefix}1 + local src2=${src_prefix}2 + local src3=${src_prefix}3 + local max_srcs=31 + local num_srcs + + RET=0 + + bridge mdb add dev br0 port $swp1 grp $grp vid 10 + bridge -d mdb show dev br0 vid 10 | grep "$grp" | grep -q "exclude" + check_err $? "Default filter mode is not \"exclude\"" + bridge mdb del dev br0 port $swp1 grp $grp vid 10 + + # Check basic add and delete behavior. + bridge mdb add dev br0 port $swp1 grp $grp vid 10 filter_mode exclude \ + source_list $src1 + bridge -d mdb show dev br0 vid 10 | grep "$grp" | grep -q -v "src" + check_err $? "(*, G) entry not created" + bridge -d mdb show dev br0 vid 10 | grep "$grp" | grep -q "src $src1" + check_err $? "(S, G) entry not created" + bridge mdb del dev br0 port $swp1 grp $grp vid 10 + bridge -d mdb show dev br0 vid 10 | grep "$grp" | grep -q -v "src" + check_fail $? "(*, G) entry not deleted" + bridge -d mdb show dev br0 vid 10 | grep "$grp" | grep -q "src $src1" + check_fail $? "(S, G) entry not deleted" + + ## State (permanent / temp) tests. + + # Check that group and source timer are not set for permanent entries. + bridge mdb add dev br0 port $swp1 grp $grp permanent vid 10 \ + filter_mode exclude source_list $src1 + + bridge -d mdb show dev br0 vid 10 | grep "$grp" | grep -v "src" | \ + grep -q "permanent" + check_err $? "(*, G) entry not added as \"permanent\" when should" + bridge -d mdb show dev br0 vid 10 | grep "$grp" | grep "src" | \ + grep -q "permanent" + check_err $? "(S, G) entry not added as \"permanent\" when should" + + bridge -d -s mdb show dev br0 vid 10 | grep "$grp" | grep -v "src" | \ + grep -q " 0.00" + check_err $? "(*, G) \"permanent\" entry has a pending group timer" + bridge -d -s mdb show dev br0 vid 10 | grep "$grp" | grep -v "src" | \ + grep -q "\/0.00" + check_err $? "\"permanent\" source entry has a pending source timer" + + bridge mdb del dev br0 port $swp1 grp $grp vid 10 + + # Check that group timer is set for temporary (*, G) EXCLUDE, but not + # the source timer. + bridge mdb add dev br0 port $swp1 grp $grp temp vid 10 \ + filter_mode exclude source_list $src1 + + bridge -d mdb show dev br0 vid 10 | grep "$grp" | grep -v "src" | \ + grep -q "temp" + check_err $? "(*, G) EXCLUDE entry not added as \"temp\" when should" + bridge -d mdb show dev br0 vid 10 | grep "$grp" | grep "src" | \ + grep -q "temp" + check_err $? "(S, G) \"blocked\" entry not added as \"temp\" when should" + + bridge -d -s mdb show dev br0 vid 10 | grep "$grp" | grep -v "src" | \ + grep -q " 0.00" + check_fail $? "(*, G) EXCLUDE entry does not have a pending group timer" + bridge -d -s mdb show dev br0 vid 10 | grep "$grp" | grep -v "src" | \ + grep -q "\/0.00" + check_err $? "\"blocked\" source entry has a pending source timer" + + bridge mdb del dev br0 port $swp1 grp $grp vid 10 + + # Check that group timer is not set for temporary (*, G) INCLUDE, but + # that the source timer is set. + bridge mdb add dev br0 port $swp1 grp $grp temp vid 10 \ + filter_mode include source_list $src1 + + bridge -d mdb show dev br0 vid 10 | grep "$grp" | grep -v "src" | \ + grep -q "temp" + check_err $? "(*, G) INCLUDE entry not added as \"temp\" when should" + bridge -d mdb show dev br0 vid 10 | grep "$grp" | grep "src" | \ + grep -q "temp" + check_err $? "(S, G) entry not added as \"temp\" when should" + + bridge -d -s mdb show dev br0 vid 10 | grep "$grp" | grep -v "src" | \ + grep -q " 0.00" + check_err $? "(*, G) INCLUDE entry has a pending group timer" + bridge -d -s mdb show dev br0 vid 10 | grep "$grp" | grep -v "src" | \ + grep -q "\/0.00" + check_fail $? "Source entry does not have a pending source timer" + + bridge mdb del dev br0 port $swp1 grp $grp vid 10 + + # Check that group timer is never set for (S, G) entries. + bridge mdb add dev br0 port $swp1 grp $grp temp vid 10 \ + filter_mode include source_list $src1 + + bridge -d -s mdb show dev br0 vid 10 | grep "$grp" | grep "src" | \ + grep -q " 0.00" + check_err $? "(S, G) entry has a pending group timer" + + bridge mdb del dev br0 port $swp1 grp $grp vid 10 + + ## Filter mode (include / exclude) tests. + + # Check that (*, G) INCLUDE entries are added with correct filter mode + # and that (S, G) entries are not marked as "blocked". + bridge mdb add dev br0 port $swp1 grp $grp vid 10 \ + filter_mode include source_list $src1 + + bridge -d mdb show dev br0 vid 10 | grep "$grp" | grep -v "src" | \ + grep -q "include" + check_err $? "(*, G) INCLUDE not added with \"include\" filter mode" + bridge -d mdb show dev br0 vid 10 | grep "$grp" | grep "src" | \ + grep -q "blocked" + check_fail $? "(S, G) entry marked as \"blocked\" when should not" + + bridge mdb del dev br0 port $swp1 grp $grp vid 10 + + # Check that (*, G) EXCLUDE entries are added with correct filter mode + # and that (S, G) entries are marked as "blocked". + bridge mdb add dev br0 port $swp1 grp $grp vid 10 \ + filter_mode exclude source_list $src1 + + bridge -d mdb show dev br0 vid 10 | grep "$grp" | grep -v "src" | \ + grep -q "exclude" + check_err $? "(*, G) EXCLUDE not added with \"exclude\" filter mode" + bridge -d mdb show dev br0 vid 10 | grep "$grp" | grep "src" | \ + grep -q "blocked" + check_err $? "(S, G) entry not marked as \"blocked\" when should" + + bridge mdb del dev br0 port $swp1 grp $grp vid 10 + + ## Protocol tests. + + # Check that (*, G) and (S, G) entries are added with the specified + # protocol. + bridge mdb add dev br0 port $swp1 grp $grp vid 10 \ + filter_mode exclude source_list $src1 proto zebra + + bridge -d mdb show dev br0 vid 10 | grep "$grp" | grep -v "src" | \ + grep -q "zebra" + check_err $? "(*, G) entry not added with \"zebra\" protocol" + bridge -d mdb show dev br0 vid 10 | grep "$grp" | grep "src" | \ + grep -q "zebra" + check_err $? "(S, G) entry not marked added with \"zebra\" protocol" + + bridge mdb del dev br0 port $swp1 grp $grp vid 10 + + ## Replace tests. + + # Check that state can be modified. + bridge mdb add dev br0 port $swp1 grp $grp temp vid 10 \ + filter_mode exclude source_list $src1 + + bridge mdb replace dev br0 port $swp1 grp $grp permanent vid 10 \ + filter_mode exclude source_list $src1 + bridge -d mdb show dev br0 vid 10 | grep "$grp" | grep -v "src" | \ + grep -q "permanent" + check_err $? "(*, G) entry not marked as \"permanent\" after replace" + bridge -d mdb show dev br0 vid 10 | grep "$grp" | grep "src" | \ + grep -q "permanent" + check_err $? "(S, G) entry not marked as \"permanent\" after replace" + + bridge mdb replace dev br0 port $swp1 grp $grp temp vid 10 \ + filter_mode exclude source_list $src1 + bridge -d mdb show dev br0 vid 10 | grep "$grp" | grep -v "src" | \ + grep -q "temp" + check_err $? "(*, G) entry not marked as \"temp\" after replace" + bridge -d mdb show dev br0 vid 10 | grep "$grp" | grep "src" | \ + grep -q "temp" + check_err $? "(S, G) entry not marked as \"temp\" after replace" + + bridge mdb del dev br0 port $swp1 grp $grp vid 10 + + # Check that filter mode can be modified. + bridge mdb add dev br0 port $swp1 grp $grp temp vid 10 \ + filter_mode exclude source_list $src1 + + bridge mdb replace dev br0 port $swp1 grp $grp temp vid 10 \ + filter_mode include source_list $src1 + bridge -d mdb show dev br0 vid 10 | grep "$grp" | grep -v "src" | \ + grep -q "include" + check_err $? "(*, G) not marked with \"include\" filter mode after replace" + bridge -d mdb show dev br0 vid 10 | grep "$grp" | grep "src" | \ + grep -q "blocked" + check_fail $? "(S, G) marked as \"blocked\" after replace" + + bridge mdb replace dev br0 port $swp1 grp $grp temp vid 10 \ + filter_mode exclude source_list $src1 + bridge -d mdb show dev br0 vid 10 | grep "$grp" | grep -v "src" | \ + grep -q "exclude" + check_err $? "(*, G) not marked with \"exclude\" filter mode after replace" + bridge -d mdb show dev br0 vid 10 | grep "$grp" | grep "src" | \ + grep -q "blocked" + check_err $? "(S, G) not marked as \"blocked\" after replace" + + bridge mdb del dev br0 port $swp1 grp $grp vid 10 + + # Check that sources can be added to and removed from the source list. + bridge mdb add dev br0 port $swp1 grp $grp temp vid 10 \ + filter_mode exclude source_list $src1 + + bridge mdb replace dev br0 port $swp1 grp $grp temp vid 10 \ + filter_mode exclude source_list $src1,$src2,$src3 + bridge -d mdb show dev br0 vid 10 | grep "$grp" | grep -q "src $src1" + check_err $? "(S, G) entry for source $src1 not created after replace" + bridge -d mdb show dev br0 vid 10 | grep "$grp" | grep -q "src $src2" + check_err $? "(S, G) entry for source $src2 not created after replace" + bridge -d mdb show dev br0 vid 10 | grep "$grp" | grep -q "src $src3" + check_err $? "(S, G) entry for source $src3 not created after replace" + + bridge mdb replace dev br0 port $swp1 grp $grp temp vid 10 \ + filter_mode exclude source_list $src1,$src3 + bridge -d mdb show dev br0 vid 10 | grep "$grp" | grep -q "src $src1" + check_err $? "(S, G) entry for source $src1 not created after second replace" + bridge -d mdb show dev br0 vid 10 | grep "$grp" | grep -q "src $src2" + check_fail $? "(S, G) entry for source $src2 created after second replace" + bridge -d mdb show dev br0 vid 10 | grep "$grp" | grep -q "src $src3" + check_err $? "(S, G) entry for source $src3 not created after second replace" + + bridge mdb del dev br0 port $swp1 grp $grp vid 10 + + # Check that protocol can be modified. + bridge mdb add dev br0 port $swp1 grp $grp temp vid 10 \ + filter_mode exclude source_list $src1 proto zebra + + bridge mdb replace dev br0 port $swp1 grp $grp temp vid 10 \ + filter_mode exclude source_list $src1 proto bgp + bridge -d mdb show dev br0 vid 10 | grep "$grp" | grep -v "src" | \ + grep -q "bgp" + check_err $? "(*, G) protocol not changed to \"bgp\" after replace" + bridge -d mdb show dev br0 vid 10 | grep "$grp" | grep "src" | \ + grep -q "bgp" + check_err $? "(S, G) protocol not changed to \"bgp\" after replace" + + bridge mdb del dev br0 port $swp1 grp $grp vid 10 + + ## Star exclude tests. + + # Check star exclude functionality. When adding a new EXCLUDE (*, G), + # it needs to be also added to all (S, G) entries for proper + # replication. + bridge mdb add dev br0 port $swp2 grp $grp vid 10 \ + filter_mode include source_list $src1 + bridge mdb add dev br0 port $swp1 grp $grp vid 10 + bridge -d mdb show dev br0 vid 10 | grep "$swp1" | grep "$grp" | \ + grep "$src1" | grep -q "added_by_star_ex" + check_err $? "\"added_by_star_ex\" entry not created after adding (*, G) entry" + bridge mdb del dev br0 port $swp1 grp $grp vid 10 + bridge mdb del dev br0 port $swp2 grp $grp src $src1 vid 10 + + ## Error cases tests. + + bridge mdb add dev br0 port $swp1 grp $invalid_grp vid 10 &> /dev/null + check_fail $? "Managed to add an entry with an invalid group" + + bridge mdb add dev br0 port $swp1 grp $grp vid 10 filter_mode include \ + &> /dev/null + check_fail $? "Managed to add an INCLUDE entry with an empty source list" + + bridge mdb add dev br0 port $swp1 grp $grp vid 10 filter_mode include \ + source_list $grp &> /dev/null + check_fail $? "Managed to add an entry with an invalid source in source list" + + bridge mdb add dev br0 port $swp1 grp $grp vid 10 \ + source_list $src &> /dev/null + check_fail $? "Managed to add an entry with a source list and no filter mode" + + bridge mdb add dev br0 port $swp1 grp $grp vid 10 filter_mode include \ + source_list $src1 + bridge mdb add dev br0 port $swp1 grp $grp vid 10 filter_mode exclude \ + source_list $src1 &> /dev/null + check_fail $? "Managed to replace an entry without using replace" + bridge mdb del dev br0 port $swp1 grp $grp vid 10 + + bridge mdb add dev br0 port $swp1 grp $grp src $src2 vid 10 + bridge mdb add dev br0 port $swp1 grp $grp vid 10 filter_mode include \ + source_list $src1,$src2,$src3 &> /dev/null + check_fail $? "Managed to add a source that already has a forwarding entry" + bridge mdb del dev br0 port $swp1 grp $grp src $src2 vid 10 + + # Check maximum number of sources. + bridge mdb add dev br0 port $swp1 grp $grp vid 10 filter_mode exclude \ + source_list $(src_list_create $src_prefix $max_srcs) + num_srcs=$(bridge -d mdb show dev br0 vid 10 | grep "$grp" | \ + grep "src" | wc -l) + [[ $num_srcs -eq $max_srcs ]] + check_err $? "Failed to configure maximum number of sources ($max_srcs)" + bridge mdb del dev br0 port $swp1 grp $grp vid 10 + + bridge mdb add dev br0 port $swp1 grp $grp vid 10 filter_mode exclude \ + source_list $(src_list_create $src_prefix $((max_srcs + 1))) \ + &> /dev/null + check_fail $? "Managed to exceed maximum number of sources ($max_srcs)" + + log_test "$name (*, G) port group entries configuration tests" +} + +cfg_test_port_ip_star_g() +{ + echo + log_info "# Port group entries configuration tests - (*, G)" + + cfg_test_port_common "IPv4 (*, G)" "grp 239.1.1.1" + cfg_test_port_common "IPv6 (*, G)" "grp ff0e::1" + __cfg_test_port_ip_star_g "IPv4" "239.1.1.1" "224.0.0.1" "192.0.2." + __cfg_test_port_ip_star_g "IPv6" "ff0e::1" "ff02::1" "2001:db8:1::" +} + +__cfg_test_port_ip_sg() +{ + local name=$1; shift + local grp=$1; shift + local src=$1; shift + local grp_key="grp $grp src $src" + + RET=0 + + bridge mdb add dev br0 port $swp1 $grp_key vid 10 + bridge -d mdb show dev br0 vid 10 | grep "$grp_key" | grep -q "include" + check_err $? "Default filter mode is not \"include\"" + bridge mdb del dev br0 port $swp1 $grp_key vid 10 + + # Check that entries can be added as both permanent and temp and that + # group timer is set correctly. + bridge mdb add dev br0 port $swp1 $grp_key permanent vid 10 + bridge -d mdb show dev br0 vid 10 | grep "$grp_key" | \ + grep -q "permanent" + check_err $? "Entry not added as \"permanent\" when should" + bridge -d -s mdb show dev br0 vid 10 | grep "$grp_key" | \ + grep -q "0.00" + check_err $? "\"permanent\" entry has a pending group timer" + bridge mdb del dev br0 port $swp1 $grp_key vid 10 + + bridge mdb add dev br0 port $swp1 $grp_key temp vid 10 + bridge -d mdb show dev br0 vid 10 | grep "$grp_key" | \ + grep -q "temp" + check_err $? "Entry not added as \"temp\" when should" + bridge -d -s mdb show dev br0 vid 10 | grep "$grp_key" | \ + grep -q "0.00" + check_fail $? "\"temp\" entry has an unpending group timer" + bridge mdb del dev br0 port $swp1 $grp_key vid 10 + + # Check error cases. + bridge mdb add dev br0 port $swp1 $grp_key vid 10 \ + filter_mode include &> /dev/null + check_fail $? "Managed to add an entry with a filter mode" + + bridge mdb add dev br0 port $swp1 $grp_key vid 10 \ + filter_mode include source_list $src &> /dev/null + check_fail $? "Managed to add an entry with a source list" + + bridge mdb add dev br0 port $swp1 grp $grp src $grp vid 10 &> /dev/null + check_fail $? "Managed to add an entry with an invalid source" + + bridge mdb add dev br0 port $swp1 $grp_key vid 10 temp + bridge mdb add dev br0 port $swp1 $grp_key vid 10 permanent &> /dev/null + check_fail $? "Managed to replace an entry without using replace" + bridge mdb del dev br0 port $swp1 $grp_key vid 10 + + # Check that we can replace available attributes. + bridge mdb add dev br0 port $swp1 $grp_key vid 10 proto 123 + bridge mdb replace dev br0 port $swp1 $grp_key vid 10 proto 111 + bridge -d mdb show dev br0 vid 10 | grep "$grp_key" | \ + grep -q "111" + check_err $? "Failed to replace protocol" + + bridge mdb replace dev br0 port $swp1 $grp_key vid 10 permanent + bridge -d mdb show dev br0 vid 10 | grep "$grp_key" | \ + grep -q "permanent" + check_err $? "Entry not marked as \"permanent\" after replace" + bridge -d -s mdb show dev br0 vid 10 | grep "$grp_key" | \ + grep -q "0.00" + check_err $? "Entry has a pending group timer after replace" + + bridge mdb replace dev br0 port $swp1 $grp_key vid 10 temp + bridge -d mdb show dev br0 vid 10 | grep "$grp_key" | \ + grep -q "temp" + check_err $? "Entry not marked as \"temp\" after replace" + bridge -d -s mdb show dev br0 vid 10 | grep "$grp_key" | \ + grep -q "0.00" + check_fail $? "Entry has an unpending group timer after replace" + bridge mdb del dev br0 port $swp1 $grp_key vid 10 + + # Check star exclude functionality. When adding a (S, G), all matching + # (*, G) ports need to be added to it. + bridge mdb add dev br0 port $swp2 grp $grp vid 10 + bridge mdb add dev br0 port $swp1 $grp_key vid 10 + bridge mdb show dev br0 vid 10 | grep "$grp_key" | grep $swp2 | \ + grep -q "added_by_star_ex" + check_err $? "\"added_by_star_ex\" entry not created after adding (S, G) entry" + bridge mdb del dev br0 port $swp1 $grp_key vid 10 + bridge mdb del dev br0 port $swp2 grp $grp vid 10 + + log_test "$name (S, G) port group entries configuration tests" +} + +cfg_test_port_ip_sg() +{ + echo + log_info "# Port group entries configuration tests - (S, G)" + + cfg_test_port_common "IPv4 (S, G)" "grp 239.1.1.1 src 192.0.2.1" + cfg_test_port_common "IPv6 (S, G)" "grp ff0e::1 src 2001:db8:1::1" + __cfg_test_port_ip_sg "IPv4" "239.1.1.1" "192.0.2.1" + __cfg_test_port_ip_sg "IPv6" "ff0e::1" "2001:db8:1::1" +} + +cfg_test_port_ip() +{ + cfg_test_port_ip_star_g + cfg_test_port_ip_sg +} + +__cfg_test_port_l2() +{ + local grp="01:02:03:04:05:06" + + RET=0 + + bridge meb add dev br0 port $swp grp 00:01:02:03:04:05 \ + permanent vid 10 &> /dev/null + check_fail $? "Managed to add an entry with unicast MAC" + + bridge mdb add dev br0 port $swp grp $grp src 00:01:02:03:04:05 \ + permanent vid 10 &> /dev/null + check_fail $? "Managed to add an entry with a source" + + bridge mdb add dev br0 port $swp1 grp $grp permanent vid 10 \ + filter_mode include &> /dev/null + check_fail $? "Managed to add an entry with a filter mode" + + bridge mdb add dev br0 port $swp1 grp $grp permanent vid 10 \ + source_list 00:01:02:03:04:05 &> /dev/null + check_fail $? "Managed to add an entry with a source list" + + log_test "L2 (*, G) port group entries configuration tests" +} + +cfg_test_port_l2() +{ + echo + log_info "# Port group entries configuration tests - L2" + + cfg_test_port_common "L2 (*, G)" "grp 01:02:03:04:05:06" + __cfg_test_port_l2 +} + +# Check configuration of regular (port) entries of all types. +cfg_test_port() +{ + cfg_test_port_ip + cfg_test_port_l2 +} + +cfg_test() +{ + cfg_test_host + cfg_test_port +} + +__fwd_test_host_ip() +{ + local grp=$1; shift + local src=$1; shift + local mode=$1; shift + local name + local eth_type + + RET=0 + + if [[ $mode == "-4" ]]; then + name="IPv4" + eth_type="ipv4" + else + name="IPv6" + eth_type="ipv6" + fi + + tc filter add dev br0 ingress protocol 802.1q pref 1 handle 1 flower \ + vlan_ethtype $eth_type vlan_id 10 dst_ip $grp src_ip $src \ + action drop + + # Packet should only be flooded to multicast router ports when there is + # no matching MDB entry. The bridge is not configured as a multicast + # router port. + $MZ $mode $h1.10 -c 1 -p 128 -A $src -B $grp -t udp -q + tc_check_packets "dev br0 ingress" 1 0 + check_err $? "Packet locally received after flood" + + # Install a regular port group entry and expect the packet to not be + # locally received. + bridge mdb add dev br0 port $swp2 grp $grp temp vid 10 + $MZ $mode $h1.10 -c 1 -p 128 -A $src -B $grp -t udp -q + tc_check_packets "dev br0 ingress" 1 0 + check_err $? "Packet locally received after installing a regular entry" + + # Add a host entry and expect the packet to be locally received. + bridge mdb add dev br0 port br0 grp $grp temp vid 10 + $MZ $mode $h1.10 -c 1 -p 128 -A $src -B $grp -t udp -q + tc_check_packets "dev br0 ingress" 1 1 + check_err $? "Packet not locally received after adding a host entry" + + # Remove the host entry and expect the packet to not be locally + # received. + bridge mdb del dev br0 port br0 grp $grp vid 10 + $MZ $mode $h1.10 -c 1 -p 128 -A $src -B $grp -t udp -q + tc_check_packets "dev br0 ingress" 1 1 + check_err $? "Packet locally received after removing a host entry" + + bridge mdb del dev br0 port $swp2 grp $grp vid 10 + + tc filter del dev br0 ingress protocol 802.1q pref 1 handle 1 flower + + log_test "$name host entries forwarding tests" +} + +fwd_test_host_ip() +{ + __fwd_test_host_ip "239.1.1.1" "192.0.2.1" "-4" + __fwd_test_host_ip "ff0e::1" "2001:db8:1::1" "-6" +} + +fwd_test_host_l2() +{ + local dmac=01:02:03:04:05:06 + + RET=0 + + tc filter add dev br0 ingress protocol all pref 1 handle 1 flower \ + dst_mac $dmac action drop + + # Packet should be flooded and locally received when there is no + # matching MDB entry. + $MZ $h1.10 -c 1 -p 128 -a own -b $dmac -q + tc_check_packets "dev br0 ingress" 1 1 + check_err $? "Packet not locally received after flood" + + # Install a regular port group entry and expect the packet to not be + # locally received. + bridge mdb add dev br0 port $swp2 grp $dmac permanent vid 10 + $MZ $h1.10 -c 1 -p 128 -a own -b $dmac -q + tc_check_packets "dev br0 ingress" 1 1 + check_err $? "Packet locally received after installing a regular entry" + + # Add a host entry and expect the packet to be locally received. + bridge mdb add dev br0 port br0 grp $dmac permanent vid 10 + $MZ $h1.10 -c 1 -p 128 -a own -b $dmac -q + tc_check_packets "dev br0 ingress" 1 2 + check_err $? "Packet not locally received after adding a host entry" + + # Remove the host entry and expect the packet to not be locally + # received. + bridge mdb del dev br0 port br0 grp $dmac permanent vid 10 + $MZ $h1.10 -c 1 -p 128 -a own -b $dmac -q + tc_check_packets "dev br0 ingress" 1 2 + check_err $? "Packet locally received after removing a host entry" + + bridge mdb del dev br0 port $swp2 grp $dmac permanent vid 10 + + tc filter del dev br0 ingress protocol all pref 1 handle 1 flower + + log_test "L2 host entries forwarding tests" +} + +fwd_test_host() +{ + # Disable multicast router on the bridge to ensure that packets are + # only locally received when a matching host entry is present. + ip link set dev br0 type bridge mcast_router 0 + + fwd_test_host_ip + fwd_test_host_l2 + + ip link set dev br0 type bridge mcast_router 1 +} + +__fwd_test_port_ip() +{ + local grp=$1; shift + local valid_src=$1; shift + local invalid_src=$1; shift + local mode=$1; shift + local filter_mode=$1; shift + local name + local eth_type + local src_list + + RET=0 + + if [[ $mode == "-4" ]]; then + name="IPv4" + eth_type="ipv4" + else + name="IPv6" + eth_type="ipv6" + fi + + # The valid source is the one we expect to get packets from after + # adding the entry. + if [[ $filter_mode == "include" ]]; then + src_list=$valid_src + else + src_list=$invalid_src + fi + + tc filter add dev $h2 ingress protocol 802.1q pref 1 handle 1 flower \ + vlan_ethtype $eth_type vlan_id 10 dst_ip $grp \ + src_ip $valid_src action drop + tc filter add dev $h2 ingress protocol 802.1q pref 1 handle 2 flower \ + vlan_ethtype $eth_type vlan_id 10 dst_ip $grp \ + src_ip $invalid_src action drop + + $MZ $mode $h1.10 -c 1 -p 128 -A $valid_src -B $grp -t udp -q + tc_check_packets "dev $h2 ingress" 1 0 + check_err $? "Packet from valid source received on H2 before adding entry" + + $MZ $mode $h1.10 -c 1 -p 128 -A $invalid_src -B $grp -t udp -q + tc_check_packets "dev $h2 ingress" 2 0 + check_err $? "Packet from invalid source received on H2 before adding entry" + + bridge mdb add dev br0 port $swp2 grp $grp vid 10 \ + filter_mode $filter_mode source_list $src_list + + $MZ $mode $h1.10 -c 1 -p 128 -A $valid_src -B $grp -t udp -q + tc_check_packets "dev $h2 ingress" 1 1 + check_err $? "Packet from valid source not received on H2 after adding entry" + + $MZ $mode $h1.10 -c 1 -p 128 -A $invalid_src -B $grp -t udp -q + tc_check_packets "dev $h2 ingress" 2 0 + check_err $? "Packet from invalid source received on H2 after adding entry" + + bridge mdb replace dev br0 port $swp2 grp $grp vid 10 \ + filter_mode exclude + + $MZ $mode $h1.10 -c 1 -p 128 -A $valid_src -B $grp -t udp -q + tc_check_packets "dev $h2 ingress" 1 2 + check_err $? "Packet from valid source not received on H2 after allowing all sources" + + $MZ $mode $h1.10 -c 1 -p 128 -A $invalid_src -B $grp -t udp -q + tc_check_packets "dev $h2 ingress" 2 1 + check_err $? "Packet from invalid source not received on H2 after allowing all sources" + + bridge mdb del dev br0 port $swp2 grp $grp vid 10 + + $MZ $mode $h1.10 -c 1 -p 128 -A $valid_src -B $grp -t udp -q + tc_check_packets "dev $h2 ingress" 1 2 + check_err $? "Packet from valid source received on H2 after deleting entry" + + $MZ $mode $h1.10 -c 1 -p 128 -A $invalid_src -B $grp -t udp -q + tc_check_packets "dev $h2 ingress" 2 1 + check_err $? "Packet from invalid source received on H2 after deleting entry" + + tc filter del dev $h2 ingress protocol 802.1q pref 1 handle 2 flower + tc filter del dev $h2 ingress protocol 802.1q pref 1 handle 1 flower + + log_test "$name port group \"$filter_mode\" entries forwarding tests" +} + +fwd_test_port_ip() +{ + __fwd_test_port_ip "239.1.1.1" "192.0.2.1" "192.0.2.2" "-4" "exclude" + __fwd_test_port_ip "ff0e::1" "2001:db8:1::1" "2001:db8:1::2" "-6" \ + "exclude" + __fwd_test_port_ip "239.1.1.1" "192.0.2.1" "192.0.2.2" "-4" "include" + __fwd_test_port_ip "ff0e::1" "2001:db8:1::1" "2001:db8:1::2" "-6" \ + "include" +} + +fwd_test_port_l2() +{ + local dmac=01:02:03:04:05:06 + + RET=0 + + tc filter add dev $h2 ingress protocol all pref 1 handle 1 flower \ + dst_mac $dmac action drop + + $MZ $h1.10 -c 1 -p 128 -a own -b $dmac -q + tc_check_packets "dev $h2 ingress" 1 0 + check_err $? "Packet received on H2 before adding entry" + + bridge mdb add dev br0 port $swp2 grp $dmac permanent vid 10 + $MZ $h1.10 -c 1 -p 128 -a own -b $dmac -q + tc_check_packets "dev $h2 ingress" 1 1 + check_err $? "Packet not received on H2 after adding entry" + + bridge mdb del dev br0 port $swp2 grp $dmac permanent vid 10 + $MZ $h1.10 -c 1 -p 128 -a own -b $dmac -q + tc_check_packets "dev $h2 ingress" 1 1 + check_err $? "Packet received on H2 after deleting entry" + + tc filter del dev $h2 ingress protocol all pref 1 handle 1 flower + + log_test "L2 port entries forwarding tests" +} + +fwd_test_port() +{ + # Disable multicast flooding to ensure that packets are only forwarded + # out of a port when a matching port group entry is present. + bridge link set dev $swp2 mcast_flood off + + fwd_test_port_ip + fwd_test_port_l2 + + bridge link set dev $swp2 mcast_flood on +} + +fwd_test() +{ + echo + log_info "# Forwarding tests" + + # Forwarding according to MDB entries only takes place when the bridge + # detects that there is a valid querier in the network. Set the bridge + # as the querier and assign it a valid IPv6 link-local address to be + # used as the source address for MLD queries. + ip -6 address add fe80::1/64 nodad dev br0 + ip link set dev br0 type bridge mcast_querier 1 + # Wait the default Query Response Interval (10 seconds) for the bridge + # to determine that there are no other queriers in the network. + sleep 10 + + fwd_test_host + fwd_test_port + + ip link set dev br0 type bridge mcast_querier 0 + ip -6 address del fe80::1/64 dev br0 +} + +igmpv3_is_in_get() +{ + local igmpv3 + + igmpv3=$(: + )"22:"$( : Type - Membership Report + )"00:"$( : Reserved + )"2a:f8:"$( : Checksum + )"00:00:"$( : Reserved + )"00:01:"$( : Number of Group Records + )"01:"$( : Record Type - IS_IN + )"00:"$( : Aux Data Len + )"00:01:"$( : Number of Sources + )"ef:01:01:01:"$( : Multicast Address - 239.1.1.1 + )"c0:00:02:02"$( : Source Address - 192.0.2.2 + ) + + echo $igmpv3 +} + +ctrl_igmpv3_is_in_test() +{ + RET=0 + + # Add a permanent entry and check that it is not affected by the + # received IGMP packet. + bridge mdb add dev br0 port $swp1 grp 239.1.1.1 permanent vid 10 \ + filter_mode include source_list 192.0.2.1 + + # IS_IN ( 192.0.2.2 ) + $MZ $h1.10 -c 1 -A 192.0.2.1 -B 239.1.1.1 \ + -t ip proto=2,p=$(igmpv3_is_in_get) -q + + bridge -d mdb show dev br0 vid 10 | grep 239.1.1.1 | grep -q 192.0.2.2 + check_fail $? "Permanent entry affected by IGMP packet" + + # Replace the permanent entry with a temporary one and check that after + # processing the IGMP packet, a new source is added to the list along + # with a new forwarding entry. + bridge mdb replace dev br0 port $swp1 grp 239.1.1.1 temp vid 10 \ + filter_mode include source_list 192.0.2.1 + + # IS_IN ( 192.0.2.2 ) + $MZ $h1.10 -c 1 -A 192.0.2.1 -B 239.1.1.1 \ + -t ip proto=2,p=$(igmpv3_is_in_get) -q + + bridge -d mdb show dev br0 vid 10 | grep 239.1.1.1 | grep -v "src" | \ + grep -q 192.0.2.2 + check_err $? "Source not add to source list" + + bridge -d mdb show dev br0 vid 10 | grep 239.1.1.1 | \ + grep -q "src 192.0.2.2" + check_err $? "(S, G) entry not created for new source" + + bridge mdb del dev br0 port $swp1 grp 239.1.1.1 vid 10 + + log_test "IGMPv3 MODE_IS_INCLUE tests" +} + +mldv2_is_in_get() +{ + local hbh + local icmpv6 + + hbh=$(: + )"3a:"$( : Next Header - ICMPv6 + )"00:"$( : Hdr Ext Len + )"00:00:00:00:00:00:"$( : Options and Padding + ) + + icmpv6=$(: + )"8f:"$( : Type - MLDv2 Report + )"00:"$( : Code + )"45:39:"$( : Checksum + )"00:00:"$( : Reserved + )"00:01:"$( : Number of Group Records + )"01:"$( : Record Type - IS_IN + )"00:"$( : Aux Data Len + )"00:01:"$( : Number of Sources + )"ff:0e:00:00:00:00:00:00:"$( : Multicast address - ff0e::1 + )"00:00:00:00:00:00:00:01:"$( : + )"20:01:0d:b8:00:01:00:00:"$( : Source Address - 2001:db8:1::2 + )"00:00:00:00:00:00:00:02:"$( : + ) + + echo ${hbh}${icmpv6} +} + +ctrl_mldv2_is_in_test() +{ + RET=0 + + # Add a permanent entry and check that it is not affected by the + # received MLD packet. + bridge mdb add dev br0 port $swp1 grp ff0e::1 permanent vid 10 \ + filter_mode include source_list 2001:db8:1::1 + + # IS_IN ( 2001:db8:1::2 ) + $MZ -6 $h1.10 -c 1 -A fe80::1 -B ff0e::1 \ + -t ip hop=1,next=0,p=$(mldv2_is_in_get) -q + + bridge -d mdb show dev br0 vid 10 | grep ff0e::1 | \ + grep -q 2001:db8:1::2 + check_fail $? "Permanent entry affected by MLD packet" + + # Replace the permanent entry with a temporary one and check that after + # processing the MLD packet, a new source is added to the list along + # with a new forwarding entry. + bridge mdb replace dev br0 port $swp1 grp ff0e::1 temp vid 10 \ + filter_mode include source_list 2001:db8:1::1 + + # IS_IN ( 2001:db8:1::2 ) + $MZ -6 $h1.10 -c 1 -A fe80::1 -B ff0e::1 \ + -t ip hop=1,next=0,p=$(mldv2_is_in_get) -q + + bridge -d mdb show dev br0 vid 10 | grep ff0e::1 | grep -v "src" | \ + grep -q 2001:db8:1::2 + check_err $? "Source not add to source list" + + bridge -d mdb show dev br0 vid 10 | grep ff0e::1 | \ + grep -q "src 2001:db8:1::2" + check_err $? "(S, G) entry not created for new source" + + bridge mdb del dev br0 port $swp1 grp ff0e::1 vid 10 + + log_test "MLDv2 MODE_IS_INCLUDE tests" +} + +ctrl_test() +{ + echo + log_info "# Control packets tests" + + ctrl_igmpv3_is_in_test + ctrl_mldv2_is_in_test +} + +trap cleanup EXIT + +setup_prepare +setup_wait +tests_run + +exit $EXIT_STATUS From 89300468e2b2ec216c7827ba04ac45c129794403 Mon Sep 17 00:00:00 2001 From: Coco Li Date: Sat, 10 Dec 2022 04:16:45 +0000 Subject: [PATCH 1997/2002] IPv6/GRO: generic helper to remove temporary HBH/jumbo header in driver IPv6/TCP and GRO stacks can build big TCP packets with an added temporary Hop By Hop header. Is GSO is not involved, then the temporary header needs to be removed in the driver. This patch provides a generic helper for drivers that need to modify their headers in place. Tested: Compiled and ran with ethtool -K eth1 tso off Could send Big TCP packets Signed-off-by: Coco Li Link: https://lore.kernel.org/r/20221210041646.3587757-1-lixiaoyan@google.com Signed-off-by: Jakub Kicinski --- include/net/ipv6.h | 33 +++++++++++++++++++++++++++++++++ net/ipv6/ip6_offload.c | 27 ++++----------------------- 2 files changed, 37 insertions(+), 23 deletions(-) diff --git a/include/net/ipv6.h b/include/net/ipv6.h index d383c895592a..03f3af02a9a6 100644 --- a/include/net/ipv6.h +++ b/include/net/ipv6.h @@ -500,6 +500,39 @@ static inline int ipv6_has_hopopt_jumbo(const struct sk_buff *skb) return jhdr->nexthdr; } +/* Return 0 if HBH header is successfully removed + * Or if HBH removal is unnecessary (packet is not big TCP) + * Return error to indicate dropping the packet + */ +static inline int ipv6_hopopt_jumbo_remove(struct sk_buff *skb) +{ + const int hophdr_len = sizeof(struct hop_jumbo_hdr); + int nexthdr = ipv6_has_hopopt_jumbo(skb); + struct ipv6hdr *h6; + + if (!nexthdr) + return 0; + + if (skb_cow_head(skb, 0)) + return -1; + + /* Remove the HBH header. + * Layout: [Ethernet header][IPv6 header][HBH][L4 Header] + */ + memmove(skb_mac_header(skb) + hophdr_len, skb_mac_header(skb), + skb_network_header(skb) - skb_mac_header(skb) + + sizeof(struct ipv6hdr)); + + __skb_pull(skb, hophdr_len); + skb->network_header += hophdr_len; + skb->mac_header += hophdr_len; + + h6 = ipv6_hdr(skb); + h6->nexthdr = nexthdr; + + return 0; +} + static inline bool ipv6_accept_ra(struct inet6_dev *idev) { /* If forwarding is enabled, RA are not accepted unless the special diff --git a/net/ipv6/ip6_offload.c b/net/ipv6/ip6_offload.c index 3ee345672849..00dc2e3b0184 100644 --- a/net/ipv6/ip6_offload.c +++ b/net/ipv6/ip6_offload.c @@ -77,7 +77,7 @@ static struct sk_buff *ipv6_gso_segment(struct sk_buff *skb, struct sk_buff *segs = ERR_PTR(-EINVAL); struct ipv6hdr *ipv6h; const struct net_offload *ops; - int proto, nexthdr; + int proto, err; struct frag_hdr *fptr; unsigned int payload_len; u8 *prevhdr; @@ -87,28 +87,9 @@ static struct sk_buff *ipv6_gso_segment(struct sk_buff *skb, bool gso_partial; skb_reset_network_header(skb); - nexthdr = ipv6_has_hopopt_jumbo(skb); - if (nexthdr) { - const int hophdr_len = sizeof(struct hop_jumbo_hdr); - int err; - - err = skb_cow_head(skb, 0); - if (err < 0) - return ERR_PTR(err); - - /* remove the HBH header. - * Layout: [Ethernet header][IPv6 header][HBH][TCP header] - */ - memmove(skb_mac_header(skb) + hophdr_len, - skb_mac_header(skb), - ETH_HLEN + sizeof(struct ipv6hdr)); - skb->data += hophdr_len; - skb->len -= hophdr_len; - skb->network_header += hophdr_len; - skb->mac_header += hophdr_len; - ipv6h = (struct ipv6hdr *)skb->data; - ipv6h->nexthdr = nexthdr; - } + err = ipv6_hopopt_jumbo_remove(skb); + if (err) + return ERR_PTR(err); nhoff = skb_network_header(skb) - skb_mac_header(skb); if (unlikely(!pskb_may_pull(skb, sizeof(*ipv6h)))) goto out; From b6488b161ab2972a0b4f45490ea3aecef2b23256 Mon Sep 17 00:00:00 2001 From: Coco Li Date: Sat, 10 Dec 2022 04:16:46 +0000 Subject: [PATCH 1998/2002] bnxt: Use generic HBH removal helper in tx path Eric Dumazet implemented Big TCP that allowed bigger TSO/GRO packet sizes for IPv6 traffic. See patch series: 'commit 89527be8d8d6 ("net: add IFLA_TSO_{MAX_SIZE|SEGS} attributes")' This reduces the number of packets traversing the networking stack and should usually improves performance. However, it also inserts a temporary Hop-by-hop IPv6 extension header. Using the HBH header removal method in the previous patch, the extra header be removed in bnxt drivers to allow it to send big TCP packets (bigger TSO packets) as well. Tested: Compiled locally To further test functional correctness, update the GSO/GRO limit on the physical NIC: ip link set eth0 gso_max_size 181000 ip link set eth0 gro_max_size 181000 Note that if there are bonding or ipvan devices on top of the physical NIC, their GSO sizes need to be updated as well. Then, IPv6/TCP packets with sizes larger than 64k can be observed. Signed-off-by: Coco Li Reviewed-by: Michael Chan Tested-by: Michael Chan Link: https://lore.kernel.org/r/20221210041646.3587757-2-lixiaoyan@google.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/broadcom/bnxt/bnxt.c | 26 ++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c index 0fe164b42c5d..4c7d07c684c4 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c @@ -389,6 +389,9 @@ static netdev_tx_t bnxt_start_xmit(struct sk_buff *skb, struct net_device *dev) return NETDEV_TX_BUSY; } + if (unlikely(ipv6_hopopt_jumbo_remove(skb))) + goto tx_free; + length = skb->len; len = skb_headlen(skb); last_frag = skb_shinfo(skb)->nr_frags; @@ -11315,6 +11318,7 @@ static bool bnxt_exthdr_check(struct bnxt *bp, struct sk_buff *skb, int nw_off, u8 **nextp) { struct ipv6hdr *ip6h = (struct ipv6hdr *)(skb->data + nw_off); + struct hop_jumbo_hdr *jhdr; int hdr_count = 0; u8 *nexthdr; int start; @@ -11342,9 +11346,27 @@ static bool bnxt_exthdr_check(struct bnxt *bp, struct sk_buff *skb, int nw_off, if (hdrlen > 64) return false; + + /* The ext header may be a hop-by-hop header inserted for + * big TCP purposes. This will be removed before sending + * from NIC, so do not count it. + */ + if (*nexthdr == NEXTHDR_HOP) { + if (likely(skb->len <= GRO_LEGACY_MAX_SIZE)) + goto increment_hdr; + + jhdr = (struct hop_jumbo_hdr *)hp; + if (jhdr->tlv_type != IPV6_TLV_JUMBO || jhdr->hdrlen != 0 || + jhdr->nexthdr != IPPROTO_TCP) + goto increment_hdr; + + goto next_hdr; + } +increment_hdr: + hdr_count++; +next_hdr: nexthdr = &hp->nexthdr; start += hdrlen; - hdr_count++; } if (nextp) { /* Caller will check inner protocol */ @@ -13657,6 +13679,8 @@ static int bnxt_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) dev->features &= ~NETIF_F_LRO; dev->priv_flags |= IFF_UNICAST_FLT; + netif_set_tso_max_size(dev, GSO_MAX_SIZE); + #ifdef CONFIG_BNXT_SRIOV init_waitqueue_head(&bp->sriov_cfg_wait); #endif From 5071429f828140e96d9973991e024dfa97ed42f5 Mon Sep 17 00:00:00 2001 From: Luca Weiss Date: Thu, 8 Dec 2022 15:15:28 -0600 Subject: [PATCH 1999/2002] dt-bindings: net: qcom,ipa: Add SM6350 compatible Add support for SM6350, which uses IPA v4.7. Signed-off-by: Luca Weiss Signed-off-by: Alex Elder Acked-by: Krzysztof Kozlowski Signed-off-by: Jakub Kicinski --- Documentation/devicetree/bindings/net/qcom,ipa.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/net/qcom,ipa.yaml b/Documentation/devicetree/bindings/net/qcom,ipa.yaml index 9e81b9ec7cfd..4aeda379726f 100644 --- a/Documentation/devicetree/bindings/net/qcom,ipa.yaml +++ b/Documentation/devicetree/bindings/net/qcom,ipa.yaml @@ -49,6 +49,7 @@ properties: - qcom,sc7280-ipa - qcom,sdm845-ipa - qcom,sdx55-ipa + - qcom,sm6350-ipa - qcom,sm8350-ipa reg: From b310de784bacd20f5217a81b725de979ef24848e Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Thu, 8 Dec 2022 15:15:29 -0600 Subject: [PATCH 2000/2002] net: ipa: add IPA v4.7 support Add the necessary register and data definitions needed for IPA v4.7, which is found on the SM6350 SoC. Co-developed-by: Luca Weiss Signed-off-by: Luca Weiss Signed-off-by: Alex Elder Signed-off-by: Jakub Kicinski --- drivers/net/ipa/Makefile | 2 +- drivers/net/ipa/data/ipa_data-v4.7.c | 405 +++++++++++++++++++++ drivers/net/ipa/ipa_data.h | 1 + drivers/net/ipa/ipa_main.c | 4 + drivers/net/ipa/ipa_reg.c | 2 + drivers/net/ipa/ipa_reg.h | 1 + drivers/net/ipa/ipa_version.h | 1 + drivers/net/ipa/reg/ipa_reg-v4.7.c | 507 +++++++++++++++++++++++++++ 8 files changed, 922 insertions(+), 1 deletion(-) create mode 100644 drivers/net/ipa/data/ipa_data-v4.7.c create mode 100644 drivers/net/ipa/reg/ipa_reg-v4.7.c diff --git a/drivers/net/ipa/Makefile b/drivers/net/ipa/Makefile index 48255fc4b25c..8cdcaaf58ae3 100644 --- a/drivers/net/ipa/Makefile +++ b/drivers/net/ipa/Makefile @@ -2,7 +2,7 @@ # # Makefile for the Qualcomm IPA driver. -IPA_VERSIONS := 3.1 3.5.1 4.2 4.5 4.9 4.11 +IPA_VERSIONS := 3.1 3.5.1 4.2 4.5 4.7 4.9 4.11 obj-$(CONFIG_QCOM_IPA) += ipa.o diff --git a/drivers/net/ipa/data/ipa_data-v4.7.c b/drivers/net/ipa/data/ipa_data-v4.7.c new file mode 100644 index 000000000000..7552c400961e --- /dev/null +++ b/drivers/net/ipa/data/ipa_data-v4.7.c @@ -0,0 +1,405 @@ +// SPDX-License-Identifier: GPL-2.0 + +/* Copyright (C) 2022 Linaro Ltd. */ + +#include + +#include "../gsi.h" +#include "../ipa_data.h" +#include "../ipa_endpoint.h" +#include "../ipa_mem.h" + +/** enum ipa_resource_type - IPA resource types for an SoC having IPA v4.7 */ +enum ipa_resource_type { + /* Source resource types; first must have value 0 */ + IPA_RESOURCE_TYPE_SRC_PKT_CONTEXTS = 0, + IPA_RESOURCE_TYPE_SRC_DESCRIPTOR_LISTS, + IPA_RESOURCE_TYPE_SRC_DESCRIPTOR_BUFF, + IPA_RESOURCE_TYPE_SRC_HPS_DMARS, + IPA_RESOURCE_TYPE_SRC_ACK_ENTRIES, + + /* Destination resource types; first must have value 0 */ + IPA_RESOURCE_TYPE_DST_DATA_SECTORS = 0, + IPA_RESOURCE_TYPE_DST_DPS_DMARS, +}; + +/* Resource groups used for an SoC having IPA v4.7 */ +enum ipa_rsrc_group_id { + /* Source resource group identifiers */ + IPA_RSRC_GROUP_SRC_UL_DL = 0, + IPA_RSRC_GROUP_SRC_UC_RX_Q, + IPA_RSRC_GROUP_SRC_COUNT, /* Last in set; not a source group */ + + /* Destination resource group identifiers */ + IPA_RSRC_GROUP_DST_UL_DL_DPL = 0, + IPA_RSRC_GROUP_DST_UNUSED_1, + IPA_RSRC_GROUP_DST_COUNT, /* Last; not a destination group */ +}; + +/* QSB configuration data for an SoC having IPA v4.7 */ +static const struct ipa_qsb_data ipa_qsb_data[] = { + [IPA_QSB_MASTER_DDR] = { + .max_writes = 8, + .max_reads = 0, /* no limit (hardware max) */ + .max_reads_beats = 120, + }, +}; + +/* Endpoint configuration data for an SoC having IPA v4.7 */ +static const struct ipa_gsi_endpoint_data ipa_gsi_endpoint_data[] = { + [IPA_ENDPOINT_AP_COMMAND_TX] = { + .ee_id = GSI_EE_AP, + .channel_id = 5, + .endpoint_id = 7, + .toward_ipa = true, + .channel = { + .tre_count = 256, + .event_count = 256, + .tlv_count = 20, + }, + .endpoint = { + .config = { + .resource_group = IPA_RSRC_GROUP_SRC_UL_DL, + .dma_mode = true, + .dma_endpoint = IPA_ENDPOINT_AP_LAN_RX, + .tx = { + .seq_type = IPA_SEQ_DMA, + }, + }, + }, + }, + [IPA_ENDPOINT_AP_LAN_RX] = { + .ee_id = GSI_EE_AP, + .channel_id = 14, + .endpoint_id = 9, + .toward_ipa = false, + .channel = { + .tre_count = 256, + .event_count = 256, + .tlv_count = 9, + }, + .endpoint = { + .config = { + .resource_group = IPA_RSRC_GROUP_DST_UL_DL_DPL, + .aggregation = true, + .status_enable = true, + .rx = { + .buffer_size = 8192, + .pad_align = ilog2(sizeof(u32)), + .aggr_time_limit = 500, + }, + }, + }, + }, + [IPA_ENDPOINT_AP_MODEM_TX] = { + .ee_id = GSI_EE_AP, + .channel_id = 2, + .endpoint_id = 2, + .toward_ipa = true, + .channel = { + .tre_count = 512, + .event_count = 512, + .tlv_count = 16, + }, + .endpoint = { + .filter_support = true, + .config = { + .resource_group = IPA_RSRC_GROUP_SRC_UL_DL, + .qmap = true, + .status_enable = true, + .tx = { + .seq_type = IPA_SEQ_2_PASS_SKIP_LAST_UC, + .status_endpoint = + IPA_ENDPOINT_MODEM_AP_RX, + }, + }, + }, + }, + [IPA_ENDPOINT_AP_MODEM_RX] = { + .ee_id = GSI_EE_AP, + .channel_id = 7, + .endpoint_id = 16, + .toward_ipa = false, + .channel = { + .tre_count = 256, + .event_count = 256, + .tlv_count = 9, + }, + .endpoint = { + .config = { + .resource_group = IPA_RSRC_GROUP_DST_UL_DL_DPL, + .qmap = true, + .aggregation = true, + .rx = { + .buffer_size = 8192, + .aggr_time_limit = 500, + .aggr_close_eof = true, + }, + }, + }, + }, + [IPA_ENDPOINT_MODEM_AP_TX] = { + .ee_id = GSI_EE_MODEM, + .channel_id = 0, + .endpoint_id = 5, + .toward_ipa = true, + .endpoint = { + .filter_support = true, + }, + }, + [IPA_ENDPOINT_MODEM_AP_RX] = { + .ee_id = GSI_EE_MODEM, + .channel_id = 7, + .endpoint_id = 14, + .toward_ipa = false, + }, + [IPA_ENDPOINT_MODEM_DL_NLO_TX] = { + .ee_id = GSI_EE_MODEM, + .channel_id = 2, + .endpoint_id = 8, + .toward_ipa = true, + .endpoint = { + .filter_support = true, + }, + }, +}; + +/* Source resource configuration data for an SoC having IPA v4.7 */ +static const struct ipa_resource ipa_resource_src[] = { + [IPA_RESOURCE_TYPE_SRC_PKT_CONTEXTS] = { + .limits[IPA_RSRC_GROUP_SRC_UL_DL] = { + .min = 8, .max = 8, + }, + }, + [IPA_RESOURCE_TYPE_SRC_DESCRIPTOR_LISTS] = { + .limits[IPA_RSRC_GROUP_SRC_UL_DL] = { + .min = 8, .max = 8, + }, + }, + [IPA_RESOURCE_TYPE_SRC_DESCRIPTOR_BUFF] = { + .limits[IPA_RSRC_GROUP_SRC_UL_DL] = { + .min = 18, .max = 18, + }, + }, + [IPA_RESOURCE_TYPE_SRC_HPS_DMARS] = { + .limits[IPA_RSRC_GROUP_SRC_UL_DL] = { + .min = 2, .max = 2, + }, + }, + [IPA_RESOURCE_TYPE_SRC_ACK_ENTRIES] = { + .limits[IPA_RSRC_GROUP_SRC_UL_DL] = { + .min = 15, .max = 15, + }, + }, +}; + +/* Destination resource configuration data for an SoC having IPA v4.7 */ +static const struct ipa_resource ipa_resource_dst[] = { + [IPA_RESOURCE_TYPE_DST_DATA_SECTORS] = { + .limits[IPA_RSRC_GROUP_DST_UL_DL_DPL] = { + .min = 7, .max = 7, + }, + }, + [IPA_RESOURCE_TYPE_DST_DPS_DMARS] = { + .limits[IPA_RSRC_GROUP_DST_UL_DL_DPL] = { + .min = 2, .max = 2, + }, + }, +}; + +/* Resource configuration data for an SoC having IPA v4.7 */ +static const struct ipa_resource_data ipa_resource_data = { + .rsrc_group_dst_count = IPA_RSRC_GROUP_DST_COUNT, + .rsrc_group_src_count = IPA_RSRC_GROUP_SRC_COUNT, + .resource_src_count = ARRAY_SIZE(ipa_resource_src), + .resource_src = ipa_resource_src, + .resource_dst_count = ARRAY_SIZE(ipa_resource_dst), + .resource_dst = ipa_resource_dst, +}; + +/* IPA-resident memory region data for an SoC having IPA v4.7 */ +static const struct ipa_mem ipa_mem_local_data[] = { + { + .id = IPA_MEM_UC_SHARED, + .offset = 0x0000, + .size = 0x0080, + .canary_count = 0, + }, + { + .id = IPA_MEM_UC_INFO, + .offset = 0x0080, + .size = 0x0200, + .canary_count = 0, + }, + { + .id = IPA_MEM_V4_FILTER_HASHED, + .offset = 0x0288, + .size = 0x0078, + .canary_count = 2, + }, + { + .id = IPA_MEM_V4_FILTER, + .offset = 0x0308, + .size = 0x0078, + .canary_count = 2, + }, + { + .id = IPA_MEM_V6_FILTER_HASHED, + .offset = 0x0388, + .size = 0x0078, + .canary_count = 2, + }, + { + .id = IPA_MEM_V6_FILTER, + .offset = 0x0408, + .size = 0x0078, + .canary_count = 2, + }, + { + .id = IPA_MEM_V4_ROUTE_HASHED, + .offset = 0x0488, + .size = 0x0078, + .canary_count = 2, + }, + { + .id = IPA_MEM_V4_ROUTE, + .offset = 0x0508, + .size = 0x0078, + .canary_count = 2, + }, + { + .id = IPA_MEM_V6_ROUTE_HASHED, + .offset = 0x0588, + .size = 0x0078, + .canary_count = 2, + }, + { + .id = IPA_MEM_V6_ROUTE, + .offset = 0x0608, + .size = 0x0078, + .canary_count = 2, + }, + { + .id = IPA_MEM_MODEM_HEADER, + .offset = 0x0688, + .size = 0x0240, + .canary_count = 2, + }, + { + .id = IPA_MEM_AP_HEADER, + .offset = 0x08c8, + .size = 0x0200, + .canary_count = 0, + }, + { + .id = IPA_MEM_MODEM_PROC_CTX, + .offset = 0x0ad0, + .size = 0x0200, + .canary_count = 2, + }, + { + .id = IPA_MEM_AP_PROC_CTX, + .offset = 0x0cd0, + .size = 0x0200, + .canary_count = 0, + }, + { + .id = IPA_MEM_NAT_TABLE, + .offset = 0x0ee0, + .size = 0x0d00, + .canary_count = 4, + }, + { + .id = IPA_MEM_PDN_CONFIG, + .offset = 0x1be8, + .size = 0x0050, + .canary_count = 0, + }, + { + .id = IPA_MEM_STATS_QUOTA_MODEM, + .offset = 0x1c40, + .size = 0x0030, + .canary_count = 4, + }, + { + .id = IPA_MEM_STATS_QUOTA_AP, + .offset = 0x1c70, + .size = 0x0048, + .canary_count = 0, + }, + { + .id = IPA_MEM_STATS_TETHERING, + .offset = 0x1cb8, + .size = 0x0238, + .canary_count = 0, + }, + { + .id = IPA_MEM_STATS_DROP, + .offset = 0x1ef0, + .size = 0x0020, + .canary_count = 0, + }, + { + .id = IPA_MEM_MODEM, + .offset = 0x1f18, + .size = 0x100c, + .canary_count = 2, + }, + { + .id = IPA_MEM_END_MARKER, + .offset = 0x3000, + .size = 0x0000, + .canary_count = 1, + }, +}; + +/* Memory configuration data for an SoC having IPA v4.7 */ +static const struct ipa_mem_data ipa_mem_data = { + .local_count = ARRAY_SIZE(ipa_mem_local_data), + .local = ipa_mem_local_data, + .imem_addr = 0x146a9000, + .imem_size = 0x00002000, + .smem_id = 497, + .smem_size = 0x00009000, +}; + +/* Interconnect rates are in 1000 byte/second units */ +static const struct ipa_interconnect_data ipa_interconnect_data[] = { + { + .name = "memory", + .peak_bandwidth = 600000, /* 600 MBps */ + .average_bandwidth = 150000, /* 150 MBps */ + }, + /* Average rate is unused for the next two interconnects */ + { + .name = "imem", + .peak_bandwidth = 450000, /* 450 MBps */ + .average_bandwidth = 75000, /* 75 MBps (unused?) */ + }, + { + .name = "config", + .peak_bandwidth = 171400, /* 171.4 MBps */ + .average_bandwidth = 0, /* unused */ + }, +}; + +/* Clock and interconnect configuration data for an SoC having IPA v4.7 */ +static const struct ipa_power_data ipa_power_data = { + /* XXX Downstream code says 150 MHz (DT SVS2), 60 MHz (code) */ + .core_clock_rate = 100 * 1000 * 1000, /* Hz (150? 60?) */ + .interconnect_count = ARRAY_SIZE(ipa_interconnect_data), + .interconnect_data = ipa_interconnect_data, +}; + +/* Configuration data for an SoC having IPA v4.7 */ +const struct ipa_data ipa_data_v4_7 = { + .version = IPA_VERSION_4_7, + .qsb_count = ARRAY_SIZE(ipa_qsb_data), + .qsb_data = ipa_qsb_data, + .modem_route_count = 8, + .endpoint_count = ARRAY_SIZE(ipa_gsi_endpoint_data), + .endpoint_data = ipa_gsi_endpoint_data, + .resource_data = &ipa_resource_data, + .mem_data = &ipa_mem_data, + .power_data = &ipa_power_data, +}; diff --git a/drivers/net/ipa/ipa_data.h b/drivers/net/ipa/ipa_data.h index 412edbfac786..818e64114ed5 100644 --- a/drivers/net/ipa/ipa_data.h +++ b/drivers/net/ipa/ipa_data.h @@ -246,6 +246,7 @@ extern const struct ipa_data ipa_data_v3_1; extern const struct ipa_data ipa_data_v3_5_1; extern const struct ipa_data ipa_data_v4_2; extern const struct ipa_data ipa_data_v4_5; +extern const struct ipa_data ipa_data_v4_7; extern const struct ipa_data ipa_data_v4_9; extern const struct ipa_data ipa_data_v4_11; diff --git a/drivers/net/ipa/ipa_main.c b/drivers/net/ipa/ipa_main.c index 8f20825675a1..4fb92f771974 100644 --- a/drivers/net/ipa/ipa_main.c +++ b/drivers/net/ipa/ipa_main.c @@ -662,6 +662,10 @@ static const struct of_device_id ipa_match[] = { .compatible = "qcom,sdx55-ipa", .data = &ipa_data_v4_5, }, + { + .compatible = "qcom,sm6350-ipa", + .data = &ipa_data_v4_7, + }, { .compatible = "qcom,sm8350-ipa", .data = &ipa_data_v4_9, diff --git a/drivers/net/ipa/ipa_reg.c b/drivers/net/ipa/ipa_reg.c index 22f067741d9b..ddd529153e15 100644 --- a/drivers/net/ipa/ipa_reg.c +++ b/drivers/net/ipa/ipa_reg.c @@ -86,6 +86,8 @@ static const struct ipa_regs *ipa_regs(enum ipa_version version) return &ipa_regs_v4_2; case IPA_VERSION_4_5: return &ipa_regs_v4_5; + case IPA_VERSION_4_7: + return &ipa_regs_v4_7; case IPA_VERSION_4_9: return &ipa_regs_v4_9; case IPA_VERSION_4_11: diff --git a/drivers/net/ipa/ipa_reg.h b/drivers/net/ipa/ipa_reg.h index 7bf70f70f63f..ff64b19a4df8 100644 --- a/drivers/net/ipa/ipa_reg.h +++ b/drivers/net/ipa/ipa_reg.h @@ -658,6 +658,7 @@ extern const struct ipa_regs ipa_regs_v3_1; extern const struct ipa_regs ipa_regs_v3_5_1; extern const struct ipa_regs ipa_regs_v4_2; extern const struct ipa_regs ipa_regs_v4_5; +extern const struct ipa_regs ipa_regs_v4_7; extern const struct ipa_regs ipa_regs_v4_9; extern const struct ipa_regs ipa_regs_v4_11; diff --git a/drivers/net/ipa/ipa_version.h b/drivers/net/ipa/ipa_version.h index 7889c310e943..d15821467743 100644 --- a/drivers/net/ipa/ipa_version.h +++ b/drivers/net/ipa/ipa_version.h @@ -48,6 +48,7 @@ static inline bool ipa_version_supported(enum ipa_version version) case IPA_VERSION_3_5_1: case IPA_VERSION_4_2: case IPA_VERSION_4_5: + case IPA_VERSION_4_7: case IPA_VERSION_4_9: case IPA_VERSION_4_11: case IPA_VERSION_5_0: diff --git a/drivers/net/ipa/reg/ipa_reg-v4.7.c b/drivers/net/ipa/reg/ipa_reg-v4.7.c new file mode 100644 index 000000000000..21f8a58e59a0 --- /dev/null +++ b/drivers/net/ipa/reg/ipa_reg-v4.7.c @@ -0,0 +1,507 @@ +// SPDX-License-Identifier: GPL-2.0 + +/* Copyright (C) 2022 Linaro Ltd. */ + +#include + +#include "../ipa.h" +#include "../ipa_reg.h" + +static const u32 ipa_reg_comp_cfg_fmask[] = { + [RAM_ARB_PRI_CLIENT_SAMP_FIX_DIS] = BIT(0), + [GSI_SNOC_BYPASS_DIS] = BIT(1), + [GEN_QMB_0_SNOC_BYPASS_DIS] = BIT(2), + [GEN_QMB_1_SNOC_BYPASS_DIS] = BIT(3), + /* Bit 4 reserved */ + [IPA_QMB_SELECT_CONS_EN] = BIT(5), + [IPA_QMB_SELECT_PROD_EN] = BIT(6), + [GSI_MULTI_INORDER_RD_DIS] = BIT(7), + [GSI_MULTI_INORDER_WR_DIS] = BIT(8), + [GEN_QMB_0_MULTI_INORDER_RD_DIS] = BIT(9), + [GEN_QMB_1_MULTI_INORDER_RD_DIS] = BIT(10), + [GEN_QMB_0_MULTI_INORDER_WR_DIS] = BIT(11), + [GEN_QMB_1_MULTI_INORDER_WR_DIS] = BIT(12), + [GEN_QMB_0_SNOC_CNOC_LOOP_PROT_DIS] = BIT(13), + [GSI_SNOC_CNOC_LOOP_PROT_DISABLE] = BIT(14), + [GSI_MULTI_AXI_MASTERS_DIS] = BIT(15), + [IPA_QMB_SELECT_GLOBAL_EN] = BIT(16), + [ATOMIC_FETCHER_ARB_LOCK_DIS] = GENMASK(20, 17), + [FULL_FLUSH_WAIT_RS_CLOSURE_EN] = BIT(21), + /* Bits 22-31 reserved */ +}; + +IPA_REG_FIELDS(COMP_CFG, comp_cfg, 0x0000003c); + +static const u32 ipa_reg_clkon_cfg_fmask[] = { + [CLKON_RX] = BIT(0), + [CLKON_PROC] = BIT(1), + [TX_WRAPPER] = BIT(2), + [CLKON_MISC] = BIT(3), + [RAM_ARB] = BIT(4), + [FTCH_HPS] = BIT(5), + [FTCH_DPS] = BIT(6), + [CLKON_HPS] = BIT(7), + [CLKON_DPS] = BIT(8), + [RX_HPS_CMDQS] = BIT(9), + [HPS_DPS_CMDQS] = BIT(10), + [DPS_TX_CMDQS] = BIT(11), + [RSRC_MNGR] = BIT(12), + [CTX_HANDLER] = BIT(13), + [ACK_MNGR] = BIT(14), + [D_DCPH] = BIT(15), + [H_DCPH] = BIT(16), + [CLKON_DCMP] = BIT(17), + [NTF_TX_CMDQS] = BIT(18), + [CLKON_TX_0] = BIT(19), + [CLKON_TX_1] = BIT(20), + [CLKON_FNR] = BIT(21), + [QSB2AXI_CMDQ_L] = BIT(22), + [AGGR_WRAPPER] = BIT(23), + [RAM_SLAVEWAY] = BIT(24), + [CLKON_QMB] = BIT(25), + [WEIGHT_ARB] = BIT(26), + [GSI_IF] = BIT(27), + [CLKON_GLOBAL] = BIT(28), + [GLOBAL_2X_CLK] = BIT(29), + [DPL_FIFO] = BIT(30), + [DRBIP] = BIT(31), +}; + +IPA_REG_FIELDS(CLKON_CFG, clkon_cfg, 0x00000044); + +static const u32 ipa_reg_route_fmask[] = { + [ROUTE_DIS] = BIT(0), + [ROUTE_DEF_PIPE] = GENMASK(5, 1), + [ROUTE_DEF_HDR_TABLE] = BIT(6), + [ROUTE_DEF_HDR_OFST] = GENMASK(16, 7), + [ROUTE_FRAG_DEF_PIPE] = GENMASK(21, 17), + /* Bits 22-23 reserved */ + [ROUTE_DEF_RETAIN_HDR] = BIT(24), + /* Bits 25-31 reserved */ +}; + +IPA_REG_FIELDS(ROUTE, route, 0x00000048); + +static const u32 ipa_reg_shared_mem_size_fmask[] = { + [MEM_SIZE] = GENMASK(15, 0), + [MEM_BADDR] = GENMASK(31, 16), +}; + +IPA_REG_FIELDS(SHARED_MEM_SIZE, shared_mem_size, 0x00000054); + +static const u32 ipa_reg_qsb_max_writes_fmask[] = { + [GEN_QMB_0_MAX_WRITES] = GENMASK(3, 0), + [GEN_QMB_1_MAX_WRITES] = GENMASK(7, 4), + /* Bits 8-31 reserved */ +}; + +IPA_REG_FIELDS(QSB_MAX_WRITES, qsb_max_writes, 0x00000074); + +static const u32 ipa_reg_qsb_max_reads_fmask[] = { + [GEN_QMB_0_MAX_READS] = GENMASK(3, 0), + [GEN_QMB_1_MAX_READS] = GENMASK(7, 4), + /* Bits 8-15 reserved */ + [GEN_QMB_0_MAX_READS_BEATS] = GENMASK(23, 16), + [GEN_QMB_1_MAX_READS_BEATS] = GENMASK(31, 24), +}; + +IPA_REG_FIELDS(QSB_MAX_READS, qsb_max_reads, 0x00000078); + +static const u32 ipa_reg_filt_rout_hash_en_fmask[] = { + [IPV6_ROUTER_HASH] = BIT(0), + /* Bits 1-3 reserved */ + [IPV6_FILTER_HASH] = BIT(4), + /* Bits 5-7 reserved */ + [IPV4_ROUTER_HASH] = BIT(8), + /* Bits 9-11 reserved */ + [IPV4_FILTER_HASH] = BIT(12), + /* Bits 13-31 reserved */ +}; + +IPA_REG_FIELDS(FILT_ROUT_HASH_EN, filt_rout_hash_en, 0x0000148); + +static const u32 ipa_reg_filt_rout_hash_flush_fmask[] = { + [IPV6_ROUTER_HASH] = BIT(0), + /* Bits 1-3 reserved */ + [IPV6_FILTER_HASH] = BIT(4), + /* Bits 5-7 reserved */ + [IPV4_ROUTER_HASH] = BIT(8), + /* Bits 9-11 reserved */ + [IPV4_FILTER_HASH] = BIT(12), + /* Bits 13-31 reserved */ +}; + +IPA_REG_FIELDS(FILT_ROUT_HASH_FLUSH, filt_rout_hash_flush, 0x000014c); + +/* Valid bits defined by ipa->available */ +IPA_REG_STRIDE(STATE_AGGR_ACTIVE, state_aggr_active, 0x000000b4, 0x0004); + +static const u32 ipa_reg_local_pkt_proc_cntxt_fmask[] = { + [IPA_BASE_ADDR] = GENMASK(17, 0), + /* Bits 18-31 reserved */ +}; + +/* Offset must be a multiple of 8 */ +IPA_REG_FIELDS(LOCAL_PKT_PROC_CNTXT, local_pkt_proc_cntxt, 0x000001e8); + +/* Valid bits defined by ipa->available */ +IPA_REG_STRIDE(AGGR_FORCE_CLOSE, aggr_force_close, 0x000001ec, 0x0004); + +static const u32 ipa_reg_ipa_tx_cfg_fmask[] = { + /* Bits 0-1 reserved */ + [PREFETCH_ALMOST_EMPTY_SIZE_TX0] = GENMASK(5, 2), + [DMAW_SCND_OUTSD_PRED_THRESHOLD] = GENMASK(9, 6), + [DMAW_SCND_OUTSD_PRED_EN] = BIT(10), + [DMAW_MAX_BEATS_256_DIS] = BIT(11), + [PA_MASK_EN] = BIT(12), + [PREFETCH_ALMOST_EMPTY_SIZE_TX1] = GENMASK(16, 13), + [DUAL_TX_ENABLE] = BIT(17), + [SSPND_PA_NO_START_STATE] = BIT(18), + /* Bits 19-31 reserved */ +}; + +IPA_REG_FIELDS(IPA_TX_CFG, ipa_tx_cfg, 0x000001fc); + +static const u32 ipa_reg_flavor_0_fmask[] = { + [MAX_PIPES] = GENMASK(3, 0), + /* Bits 4-7 reserved */ + [MAX_CONS_PIPES] = GENMASK(12, 8), + /* Bits 13-15 reserved */ + [MAX_PROD_PIPES] = GENMASK(20, 16), + /* Bits 21-23 reserved */ + [PROD_LOWEST] = GENMASK(27, 24), + /* Bits 28-31 reserved */ +}; + +IPA_REG_FIELDS(FLAVOR_0, flavor_0, 0x00000210); + +static const u32 ipa_reg_idle_indication_cfg_fmask[] = { + [ENTER_IDLE_DEBOUNCE_THRESH] = GENMASK(15, 0), + [CONST_NON_IDLE_ENABLE] = BIT(16), + /* Bits 17-31 reserved */ +}; + +IPA_REG_FIELDS(IDLE_INDICATION_CFG, idle_indication_cfg, 0x00000240); + +static const u32 ipa_reg_qtime_timestamp_cfg_fmask[] = { + [DPL_TIMESTAMP_LSB] = GENMASK(4, 0), + /* Bits 5-6 reserved */ + [DPL_TIMESTAMP_SEL] = BIT(7), + [TAG_TIMESTAMP_LSB] = GENMASK(12, 8), + /* Bits 13-15 reserved */ + [NAT_TIMESTAMP_LSB] = GENMASK(20, 16), + /* Bits 21-31 reserved */ +}; + +IPA_REG_FIELDS(QTIME_TIMESTAMP_CFG, qtime_timestamp_cfg, 0x0000024c); + +static const u32 ipa_reg_timers_xo_clk_div_cfg_fmask[] = { + [DIV_VALUE] = GENMASK(8, 0), + /* Bits 9-30 reserved */ + [DIV_ENABLE] = BIT(31), +}; + +IPA_REG_FIELDS(TIMERS_XO_CLK_DIV_CFG, timers_xo_clk_div_cfg, 0x00000250); + +static const u32 ipa_reg_timers_pulse_gran_cfg_fmask[] = { + [PULSE_GRAN_0] = GENMASK(2, 0), + [PULSE_GRAN_1] = GENMASK(5, 3), + [PULSE_GRAN_2] = GENMASK(8, 6), +}; + +IPA_REG_FIELDS(TIMERS_PULSE_GRAN_CFG, timers_pulse_gran_cfg, 0x00000254); + +static const u32 ipa_reg_src_rsrc_grp_01_rsrc_type_fmask[] = { + [X_MIN_LIM] = GENMASK(5, 0), + /* Bits 6-7 reserved */ + [X_MAX_LIM] = GENMASK(13, 8), + /* Bits 14-15 reserved */ + [Y_MIN_LIM] = GENMASK(21, 16), + /* Bits 22-23 reserved */ + [Y_MAX_LIM] = GENMASK(29, 24), + /* Bits 30-31 reserved */ +}; + +IPA_REG_STRIDE_FIELDS(SRC_RSRC_GRP_01_RSRC_TYPE, src_rsrc_grp_01_rsrc_type, + 0x00000400, 0x0020); + +static const u32 ipa_reg_src_rsrc_grp_23_rsrc_type_fmask[] = { + [X_MIN_LIM] = GENMASK(5, 0), + /* Bits 6-7 reserved */ + [X_MAX_LIM] = GENMASK(13, 8), + /* Bits 14-15 reserved */ + [Y_MIN_LIM] = GENMASK(21, 16), + /* Bits 22-23 reserved */ + [Y_MAX_LIM] = GENMASK(29, 24), + /* Bits 30-31 reserved */ +}; + +IPA_REG_STRIDE_FIELDS(SRC_RSRC_GRP_23_RSRC_TYPE, src_rsrc_grp_23_rsrc_type, + 0x00000404, 0x0020); + +static const u32 ipa_reg_dst_rsrc_grp_01_rsrc_type_fmask[] = { + [X_MIN_LIM] = GENMASK(5, 0), + /* Bits 6-7 reserved */ + [X_MAX_LIM] = GENMASK(13, 8), + /* Bits 14-15 reserved */ + [Y_MIN_LIM] = GENMASK(21, 16), + /* Bits 22-23 reserved */ + [Y_MAX_LIM] = GENMASK(29, 24), + /* Bits 30-31 reserved */ +}; + +IPA_REG_STRIDE_FIELDS(DST_RSRC_GRP_01_RSRC_TYPE, dst_rsrc_grp_01_rsrc_type, + 0x00000500, 0x0020); + +static const u32 ipa_reg_dst_rsrc_grp_23_rsrc_type_fmask[] = { + [X_MIN_LIM] = GENMASK(5, 0), + /* Bits 6-7 reserved */ + [X_MAX_LIM] = GENMASK(13, 8), + /* Bits 14-15 reserved */ + [Y_MIN_LIM] = GENMASK(21, 16), + /* Bits 22-23 reserved */ + [Y_MAX_LIM] = GENMASK(29, 24), + /* Bits 30-31 reserved */ +}; + +IPA_REG_STRIDE_FIELDS(DST_RSRC_GRP_23_RSRC_TYPE, dst_rsrc_grp_23_rsrc_type, + 0x00000504, 0x0020); + +static const u32 ipa_reg_endp_init_cfg_fmask[] = { + [FRAG_OFFLOAD_EN] = BIT(0), + [CS_OFFLOAD_EN] = GENMASK(2, 1), + [CS_METADATA_HDR_OFFSET] = GENMASK(6, 3), + /* Bit 7 reserved */ + [CS_GEN_QMB_MASTER_SEL] = BIT(8), + /* Bits 9-31 reserved */ +}; + +IPA_REG_STRIDE_FIELDS(ENDP_INIT_CFG, endp_init_cfg, 0x00000808, 0x0070); + +static const u32 ipa_reg_endp_init_nat_fmask[] = { + [NAT_EN] = GENMASK(1, 0), + /* Bits 2-31 reserved */ +}; + +IPA_REG_STRIDE_FIELDS(ENDP_INIT_NAT, endp_init_nat, 0x0000080c, 0x0070); + +static const u32 ipa_reg_endp_init_hdr_fmask[] = { + [HDR_LEN] = GENMASK(5, 0), + [HDR_OFST_METADATA_VALID] = BIT(6), + [HDR_OFST_METADATA] = GENMASK(12, 7), + [HDR_ADDITIONAL_CONST_LEN] = GENMASK(18, 13), + [HDR_OFST_PKT_SIZE_VALID] = BIT(19), + [HDR_OFST_PKT_SIZE] = GENMASK(25, 20), + [HDR_A5_MUX] = BIT(26), + [HDR_LEN_INC_DEAGG_HDR] = BIT(27), + [HDR_LEN_MSB] = GENMASK(29, 28), + [HDR_OFST_METADATA_MSB] = GENMASK(31, 30), +}; + +IPA_REG_STRIDE_FIELDS(ENDP_INIT_HDR, endp_init_hdr, 0x00000810, 0x0070); + +static const u32 ipa_reg_endp_init_hdr_ext_fmask[] = { + [HDR_ENDIANNESS] = BIT(0), + [HDR_TOTAL_LEN_OR_PAD_VALID] = BIT(1), + [HDR_TOTAL_LEN_OR_PAD] = BIT(2), + [HDR_PAYLOAD_LEN_INC_PADDING] = BIT(3), + [HDR_TOTAL_LEN_OR_PAD_OFFSET] = GENMASK(9, 4), + [HDR_PAD_TO_ALIGNMENT] = GENMASK(13, 10), + /* Bits 14-15 reserved */ + [HDR_TOTAL_LEN_OR_PAD_OFFSET_MSB] = GENMASK(17, 16), + [HDR_OFST_PKT_SIZE_MSB] = GENMASK(19, 18), + [HDR_ADDITIONAL_CONST_LEN_MSB] = GENMASK(21, 20), + /* Bits 22-31 reserved */ +}; + +IPA_REG_STRIDE_FIELDS(ENDP_INIT_HDR_EXT, endp_init_hdr_ext, 0x00000814, 0x0070); + +IPA_REG_STRIDE(ENDP_INIT_HDR_METADATA_MASK, endp_init_hdr_metadata_mask, + 0x00000818, 0x0070); + +static const u32 ipa_reg_endp_init_mode_fmask[] = { + [ENDP_MODE] = GENMASK(2, 0), + [DCPH_ENABLE] = BIT(3), + [DEST_PIPE_INDEX] = GENMASK(8, 4), + /* Bits 9-11 reserved */ + [BYTE_THRESHOLD] = GENMASK(27, 12), + [PIPE_REPLICATION_EN] = BIT(28), + [PAD_EN] = BIT(29), + /* Bits 30-31 reserved */ +}; + +IPA_REG_STRIDE_FIELDS(ENDP_INIT_MODE, endp_init_mode, 0x00000820, 0x0070); + +static const u32 ipa_reg_endp_init_aggr_fmask[] = { + [AGGR_EN] = GENMASK(1, 0), + [AGGR_TYPE] = GENMASK(4, 2), + [BYTE_LIMIT] = GENMASK(10, 5), + /* Bit 11 reserved */ + [TIME_LIMIT] = GENMASK(16, 12), + [PKT_LIMIT] = GENMASK(22, 17), + [SW_EOF_ACTIVE] = BIT(23), + [FORCE_CLOSE] = BIT(24), + /* Bit 25 reserved */ + [HARD_BYTE_LIMIT_EN] = BIT(26), + [AGGR_GRAN_SEL] = BIT(27), + /* Bits 28-31 reserved */ +}; + +IPA_REG_STRIDE_FIELDS(ENDP_INIT_AGGR, endp_init_aggr, 0x00000824, 0x0070); + +static const u32 ipa_reg_endp_init_hol_block_en_fmask[] = { + [HOL_BLOCK_EN] = BIT(0), + /* Bits 1-31 reserved */ +}; + +IPA_REG_STRIDE_FIELDS(ENDP_INIT_HOL_BLOCK_EN, endp_init_hol_block_en, + 0x0000082c, 0x0070); + +static const u32 ipa_reg_endp_init_hol_block_timer_fmask[] = { + [TIMER_LIMIT] = GENMASK(4, 0), + /* Bits 5-7 reserved */ + [TIMER_GRAN_SEL] = BIT(8), + /* Bits 9-31 reserved */ +}; + +IPA_REG_STRIDE_FIELDS(ENDP_INIT_HOL_BLOCK_TIMER, endp_init_hol_block_timer, + 0x00000830, 0x0070); + +static const u32 ipa_reg_endp_init_deaggr_fmask[] = { + [DEAGGR_HDR_LEN] = GENMASK(5, 0), + [SYSPIPE_ERR_DETECTION] = BIT(6), + [PACKET_OFFSET_VALID] = BIT(7), + [PACKET_OFFSET_LOCATION] = GENMASK(13, 8), + [IGNORE_MIN_PKT_ERR] = BIT(14), + /* Bit 15 reserved */ + [MAX_PACKET_LEN] = GENMASK(31, 16), +}; + +IPA_REG_STRIDE_FIELDS(ENDP_INIT_DEAGGR, endp_init_deaggr, 0x00000834, 0x0070); + +static const u32 ipa_reg_endp_init_rsrc_grp_fmask[] = { + [ENDP_RSRC_GRP] = BIT(0), + /* Bits 1-31 reserved */ +}; + +IPA_REG_STRIDE_FIELDS(ENDP_INIT_RSRC_GRP, endp_init_rsrc_grp, + 0x00000838, 0x0070); + +static const u32 ipa_reg_endp_init_seq_fmask[] = { + [SEQ_TYPE] = GENMASK(7, 0), + /* Bits 8-31 reserved */ +}; + +IPA_REG_STRIDE_FIELDS(ENDP_INIT_SEQ, endp_init_seq, 0x0000083c, 0x0070); + +static const u32 ipa_reg_endp_status_fmask[] = { + [STATUS_EN] = BIT(0), + [STATUS_ENDP] = GENMASK(5, 1), + /* Bits 6-8 reserved */ + [STATUS_PKT_SUPPRESS] = BIT(9), + /* Bits 10-31 reserved */ +}; + +IPA_REG_STRIDE_FIELDS(ENDP_STATUS, endp_status, 0x00000840, 0x0070); + +static const u32 ipa_reg_endp_filter_router_hsh_cfg_fmask[] = { + [FILTER_HASH_MSK_SRC_ID] = BIT(0), + [FILTER_HASH_MSK_SRC_IP] = BIT(1), + [FILTER_HASH_MSK_DST_IP] = BIT(2), + [FILTER_HASH_MSK_SRC_PORT] = BIT(3), + [FILTER_HASH_MSK_DST_PORT] = BIT(4), + [FILTER_HASH_MSK_PROTOCOL] = BIT(5), + [FILTER_HASH_MSK_METADATA] = BIT(6), + [FILTER_HASH_MSK_ALL] = GENMASK(6, 0), + /* Bits 7-15 reserved */ + [ROUTER_HASH_MSK_SRC_ID] = BIT(16), + [ROUTER_HASH_MSK_SRC_IP] = BIT(17), + [ROUTER_HASH_MSK_DST_IP] = BIT(18), + [ROUTER_HASH_MSK_SRC_PORT] = BIT(19), + [ROUTER_HASH_MSK_DST_PORT] = BIT(20), + [ROUTER_HASH_MSK_PROTOCOL] = BIT(21), + [ROUTER_HASH_MSK_METADATA] = BIT(22), + [ROUTER_HASH_MSK_ALL] = GENMASK(22, 16), + /* Bits 23-31 reserved */ +}; + +IPA_REG_STRIDE_FIELDS(ENDP_FILTER_ROUTER_HSH_CFG, endp_filter_router_hsh_cfg, + 0x0000085c, 0x0070); + +/* Valid bits defined by enum ipa_irq_id; only used for GSI_EE_AP */ +IPA_REG(IPA_IRQ_STTS, ipa_irq_stts, 0x00003008 + 0x1000 * GSI_EE_AP); + +/* Valid bits defined by enum ipa_irq_id; only used for GSI_EE_AP */ +IPA_REG(IPA_IRQ_EN, ipa_irq_en, 0x0000300c + 0x1000 * GSI_EE_AP); + +/* Valid bits defined by enum ipa_irq_id; only used for GSI_EE_AP */ +IPA_REG(IPA_IRQ_CLR, ipa_irq_clr, 0x00003010 + 0x1000 * GSI_EE_AP); + +static const u32 ipa_reg_ipa_irq_uc_fmask[] = { + [UC_INTR] = BIT(0), + /* Bits 1-31 reserved */ +}; + +IPA_REG_FIELDS(IPA_IRQ_UC, ipa_irq_uc, 0x0000301c + 0x1000 * GSI_EE_AP); + +/* Valid bits defined by ipa->available */ +IPA_REG_STRIDE(IRQ_SUSPEND_INFO, irq_suspend_info, + 0x00003030 + 0x1000 * GSI_EE_AP, 0x0004); + +/* Valid bits defined by ipa->available */ +IPA_REG_STRIDE(IRQ_SUSPEND_EN, irq_suspend_en, + 0x00003034 + 0x1000 * GSI_EE_AP, 0x0004); + +/* Valid bits defined by ipa->available */ +IPA_REG_STRIDE(IRQ_SUSPEND_CLR, irq_suspend_clr, + 0x00003038 + 0x1000 * GSI_EE_AP, 0x0004); + +static const struct ipa_reg *ipa_reg_array[] = { + [COMP_CFG] = &ipa_reg_comp_cfg, + [CLKON_CFG] = &ipa_reg_clkon_cfg, + [ROUTE] = &ipa_reg_route, + [SHARED_MEM_SIZE] = &ipa_reg_shared_mem_size, + [QSB_MAX_WRITES] = &ipa_reg_qsb_max_writes, + [QSB_MAX_READS] = &ipa_reg_qsb_max_reads, + [FILT_ROUT_HASH_EN] = &ipa_reg_filt_rout_hash_en, + [FILT_ROUT_HASH_FLUSH] = &ipa_reg_filt_rout_hash_flush, + [STATE_AGGR_ACTIVE] = &ipa_reg_state_aggr_active, + [LOCAL_PKT_PROC_CNTXT] = &ipa_reg_local_pkt_proc_cntxt, + [AGGR_FORCE_CLOSE] = &ipa_reg_aggr_force_close, + [IPA_TX_CFG] = &ipa_reg_ipa_tx_cfg, + [FLAVOR_0] = &ipa_reg_flavor_0, + [IDLE_INDICATION_CFG] = &ipa_reg_idle_indication_cfg, + [QTIME_TIMESTAMP_CFG] = &ipa_reg_qtime_timestamp_cfg, + [TIMERS_XO_CLK_DIV_CFG] = &ipa_reg_timers_xo_clk_div_cfg, + [TIMERS_PULSE_GRAN_CFG] = &ipa_reg_timers_pulse_gran_cfg, + [SRC_RSRC_GRP_01_RSRC_TYPE] = &ipa_reg_src_rsrc_grp_01_rsrc_type, + [SRC_RSRC_GRP_23_RSRC_TYPE] = &ipa_reg_src_rsrc_grp_23_rsrc_type, + [DST_RSRC_GRP_01_RSRC_TYPE] = &ipa_reg_dst_rsrc_grp_01_rsrc_type, + [DST_RSRC_GRP_23_RSRC_TYPE] = &ipa_reg_dst_rsrc_grp_23_rsrc_type, + [ENDP_INIT_CFG] = &ipa_reg_endp_init_cfg, + [ENDP_INIT_NAT] = &ipa_reg_endp_init_nat, + [ENDP_INIT_HDR] = &ipa_reg_endp_init_hdr, + [ENDP_INIT_HDR_EXT] = &ipa_reg_endp_init_hdr_ext, + [ENDP_INIT_HDR_METADATA_MASK] = &ipa_reg_endp_init_hdr_metadata_mask, + [ENDP_INIT_MODE] = &ipa_reg_endp_init_mode, + [ENDP_INIT_AGGR] = &ipa_reg_endp_init_aggr, + [ENDP_INIT_HOL_BLOCK_EN] = &ipa_reg_endp_init_hol_block_en, + [ENDP_INIT_HOL_BLOCK_TIMER] = &ipa_reg_endp_init_hol_block_timer, + [ENDP_INIT_DEAGGR] = &ipa_reg_endp_init_deaggr, + [ENDP_INIT_RSRC_GRP] = &ipa_reg_endp_init_rsrc_grp, + [ENDP_INIT_SEQ] = &ipa_reg_endp_init_seq, + [ENDP_STATUS] = &ipa_reg_endp_status, + [ENDP_FILTER_ROUTER_HSH_CFG] = &ipa_reg_endp_filter_router_hsh_cfg, + [IPA_IRQ_STTS] = &ipa_reg_ipa_irq_stts, + [IPA_IRQ_EN] = &ipa_reg_ipa_irq_en, + [IPA_IRQ_CLR] = &ipa_reg_ipa_irq_clr, + [IPA_IRQ_UC] = &ipa_reg_ipa_irq_uc, + [IRQ_SUSPEND_INFO] = &ipa_reg_irq_suspend_info, + [IRQ_SUSPEND_EN] = &ipa_reg_irq_suspend_en, + [IRQ_SUSPEND_CLR] = &ipa_reg_irq_suspend_clr, +}; + +const struct ipa_regs ipa_regs_v4_7 = { + .reg_count = ARRAY_SIZE(ipa_reg_array), + .reg = ipa_reg_array, +}; From d1c722867f8022a27182b9a1d84e9bca75486c9a Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Mon, 12 Dec 2022 20:37:16 +0100 Subject: [PATCH 2001/2002] net: lan966x: Remove a useless test in lan966x_ptp_add_trap() vcap_alloc_rule() can't return NULL. So remove some dead-code Signed-off-by: Christophe JAILLET Link: https://lore.kernel.org/r/27992ffcee47fc865ce87274d6dfcffe7a1e69e0.1670873784.git.christophe.jaillet@wanadoo.fr Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/microchip/lan966x/lan966x_ptp.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_ptp.c b/drivers/net/ethernet/microchip/lan966x/lan966x_ptp.c index 300fe4005919..f9ebfaafbebc 100644 --- a/drivers/net/ethernet/microchip/lan966x/lan966x_ptp.c +++ b/drivers/net/ethernet/microchip/lan966x/lan966x_ptp.c @@ -76,8 +76,6 @@ static int lan966x_ptp_add_trap(struct lan966x_port *port, vrule = vcap_alloc_rule(lan966x->vcap_ctrl, port->dev, LAN966X_VCAP_CID_IS2_L0, VCAP_USER_PTP, 0, rule_id); - if (!vrule) - return -ENOMEM; if (IS_ERR(vrule)) return PTR_ERR(vrule); From 7c4a6309e27f411743817fe74a832ec2d2798a4b Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Mon, 12 Dec 2022 19:20:37 -0800 Subject: [PATCH 2002/2002] ipvs: fix type warning in do_div() on 32 bit 32 bit platforms without 64bit div generate the following warning: net/netfilter/ipvs/ip_vs_est.c: In function 'ip_vs_est_calc_limits': include/asm-generic/div64.h:222:35: warning: comparison of distinct pointer types lacks a cast 222 | (void)(((typeof((n)) *)0) == ((uint64_t *)0)); \ | ^~ net/netfilter/ipvs/ip_vs_est.c:694:17: note: in expansion of macro 'do_div' 694 | do_div(val, loops); | ^~~~~~ include/asm-generic/div64.h:222:35: warning: comparison of distinct pointer types lacks a cast 222 | (void)(((typeof((n)) *)0) == ((uint64_t *)0)); \ | ^~ net/netfilter/ipvs/ip_vs_est.c:700:33: note: in expansion of macro 'do_div' 700 | do_div(val, min_est); | ^~~~~~ first argument of do_div() should be unsigned. We can't just cast as do_div() updates it as well, so we need an lval. Make val unsigned in the first place, all paths check that the value they assign to this variables are non-negative already. Fixes: 705dd3444081 ("ipvs: use kthreads for stats estimation") Signed-off-by: Jakub Kicinski Link: https://lore.kernel.org/r/20221213032037.844517-1-kuba@kernel.org Signed-off-by: Paolo Abeni --- net/netfilter/ipvs/ip_vs_est.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/net/netfilter/ipvs/ip_vs_est.c b/net/netfilter/ipvs/ip_vs_est.c index df56073bb282..ce2a1549b304 100644 --- a/net/netfilter/ipvs/ip_vs_est.c +++ b/net/netfilter/ipvs/ip_vs_est.c @@ -640,9 +640,10 @@ static int ip_vs_est_calc_limits(struct netns_ipvs *ipvs, int *chain_max) int i, loops, ntest; s32 min_est = 0; ktime_t t1, t2; - s64 diff, val; int max = 8; int ret = 1; + s64 diff; + u64 val; INIT_HLIST_HEAD(&chain); mutex_lock(&__ip_vs_mutex);